ATTENUATOR Controller

Control unit - software v.1.5.1 description

Control unit is a simple microprocessor system based on ATMEL 89C51 chip (Intel 8051 family). There is small LCD display (4 lines) and keyboard (12 keys). Unfortunately, software for these things is no as simple as it seems to be.

Program is divided into three independently co-operating sections:

Program consists of 9 sections, which are concatenated creating one source code: Below is source code (8051 compatible). You can also get binary version of that data in downloads' section.

Notice: However we publish full source code, it is copyrighted by:
© High Pressure Research Centre, Polish Academy of Sciences, Warsaw, Poland, http://www.unipress.waw.pl/
© roman pielaszek, pielasze@unipress.waw.pl, http://www.unipress.waw.pl/~pielasze

You can freely include part of or entire code without permission for non-commercial use, referencing to above copyrights.

;///////////////////////////////////////////////////////////
;       att_brd.h       -       attenuator board layout
;       ---------               -----------------------
;
;       This file defines labels corresponding to physical
;       circuit wirings.
;
;       15:18 20.11.1999 Hamburg, roman pielaszek
;
;
;LCD conn.
;-----------
;pin#   name
;1      +5V
;2      contrast
;3      E                               5       P1.4
;4      nc
;5      RS                              7       P1.6
;6      GND
;7      D7                              32      P0.7
;8      D6                              33      P0.6
;9      D5                              34      P0.5
;10     D4                              35      P0.4
;11     D3                              36      P0.3
;12     D2                              37      P0.2
;13     D1                              38      P0.1
;14     D0                              39      P0.0
;15     nc
;16     R/W                             6       P1.5
;
;///////////////////////////////////////////////////////////
;       LCD labels      should be already defined in hardware board section (e.g. att_brd.h or 80c535.h) !
;       ----------
;
LCD_E           EQU     P1.4
LCD_RW          EQU     P1.5
LCD_RS          EQU     P1.6
LCD_DATA        EQU     P0
;
;
;
;
;
;KBD conn.      GAL16V8                 8951            meaning (active: 0VDC)
;-----------    ------------            ------------    ---------------
;pin#   name    pin#    name            pin#    name
;               1       I0              8       P1.7    M1: reset switch signal (???)
;11             2       I1              25      P2.4    M1: direction: left
;2              3       I2              26      P2.5    M1: direction: right
;6              4       I3              27      P2.6    M1: motor on/off
;               5       I4              28      P2.7    M1: limit switch signal
;13             6       I5              21      P2.0    M0: direction: left
;5              7       I6              22      P2.1    M0: direction: right
;9              8       I7              23      P2.2    M0: motor on/off
;               9       I8              24      P2.3    M0: limit switch signal
;
;               11      I9              4       P1.3    M0: reset switch signal (???)
;
;KBD connector          GAL16V8
;-------------          ------------
;pin#   name            pin#    name
;1      GND             
;2                      3
;3
;4
;5                      7
;6                      4
;7
;8      GND
;9                      8
;10
;11                     2
;12     GND
;13                     6
;
;


MOTOR_REG       EQU     P2
M0_LEFT         EQU     P2.0
M0_RIGHT        EQU     P2.1
M0_ON           EQU     P2.2
M0_LIMIT_SW     EQU     P2.3

M1_LEFT         EQU     P2.4
M1_RIGHT        EQU     P2.5
M1_ON           EQU     P2.6
M1_LIMIT_SW     EQU     P2.7

M0_RESET_SW     EQU     P1.3
M1_RESET_SW     EQU     P1.7




;KBD_H1         EQU     P5.5
;KBD_H4         EQU     P5.3
;KBD_V1         EQU     P5.7
;KBD_V2         EQU     P5.4
;KBD_V3         EQU     P5.2


KEY_M0_LEFT     EQU     P3.6
KEY_M0_RES      EQU     P1.1
KEY_M0_RIGHT    EQU     P3.7
KEY_M1_LEFT     EQU     P1.0
KEY_M1_RES      EQU     KEY_M0_RES
KEY_M1_RIGHT    EQU     P1.2

;///////////////////////////////////////////////////////////
;       att_main.asm    -       main attenuator program
;       ------------            -----------------------
;
;
;       Motor registers occupy bank#1
;
M0_CURR_POS     EQU     8       ;0-9
M0_DEST_POS     EQU     9       ;0-9
M0_STAT         EQU     10      ;b7=phase #, b1=reset bit
M0_PHASE_AGE    EQU     11
M1_CURR_POS     EQU     12      ;0-9
M1_DEST_POS     EQU     13      ;0-9
M1_STAT         EQU     14      ;b7=phase #, b1=reset bit
M1_PHASE_AGE    EQU     15

;
;shadow registers - used for comparisons (when parameters on the display
;                                               should be updated)
M0_CURR_POS_SH  EQU     16      ; 0-9
M0_DEST_POS_SH  EQU     17      ; 0-9
M0_STAT_SH      EQU     18
TR1_DELAY       EQU     19      ; counter used for delay int_tr1
M1_CURR_POS_SH  EQU     20      ; 0-9
M1_DEST_POS_SH  EQU     21      ; 0-9
M1_STAT_SH      EQU     22
MOTOR_REG_SH    EQU     23      ;shadows hardware registers MOTOR_REG, and so on


PHASE0_LENGTH   EQU     10      ;starting (1->0 on limit switch) history
PHASE1_LENGTH   EQU     3       ;stopping (0->1 on limit switch) history
;
;
;///////////////////////////////////////////////////////////
;       Interrupt routines
;       ------------------
;
ORG     0000H
                LJMP    BEGIN           ;begin of the programm
ORG     0003H
;               LJMP    INT_GPIB        ;PRZERWANIE OD NEC'A
org     000Bh
                ljmp    INT_TR0         ;TR0 interrupt (motors' movements)
org     0013h
                reti                    ;EX1 interrupt
org     001Bh
                ljmp    INT_TR1         ;TR1 interrupt (display update)
org     0023H
                LJMP    INT_RS          ;przerwanie od wewnetrznego RS232
org     002BH
                reti                    ;przerwanie od Timer 2
org     0043H
                reti                    ;przerwanie od IADC
org     004BH
                reti                    ;przerwanie od IEX2
org     0053H
                reti                    ;przerwanie od IEX3
org     005BH
                reti                    ;przerwanie od IEX4
org     0063H
                reti                    ;przerwanie od IEX5
org     006BH
                reti                    ;przerwanie od IEX6
;
;
;///////////////////////////////////////////////////////////
;       Initializations of interrupts, timers and external hardware
;       -----------------------------------------------------------
;
;

org     073h

;       Konfiguracja wyswietlacza LCD dla 8-bitowego dwu(cztero!)linijkowca
;       wykonanie komend F, C, A i D tak, aby ustawic:
;       - transmisja 8 bitow, ilosc linii = 2, czcionka 5x7
;       - inkrementacja kursora przy przesuwie, brak przewijania
;       - czyszczenie ekranu i ustawienie kursora w polozenie 0
;       - wlaczenie ekranu, kursora, brak migania
BEGIN:

        ; stack pointer goes higher (default is 8)

        mov     SP,#48


        ; LCD requires some initialization

        lcall   LCD_INIT


        ; RS-232 will setup itself to UART 9600,8N1
        ; and will run Timer 1 as a clock

        mov     a, #-3  ;set speed to 9600
        lcall   RS_INIT


        ; Timer 0 we use as a source of interrupts
        ; used to motor maintenance

        mov     a, #0   ;set frequency to 112Hz
        lcall   TR0_INIT


        ; As mentioned, RS_INIT setups Timer 1 (TR1),
        ; butt RS-232 doesn't need any interrupt
        ; from TR1 - requires just a clock.
        ; We use TR1 also as a source of interrupts
        ; which will maintain display update
        ; So, we have to enable TR1 int-s.

        setb    ET1     ;enable TR1 interrupt


        ; Please notice, that interrupts are still
        ; switched off: flag EA is cleared
;
;
;///////////////////////////////////////////////////////////
;
;       Initialization of Variables
;       ---------------------------
;
;
        mov     MOTOR_REG,      #0FFh
        mov     MOTOR_REG_SH,   #0

        mov     M0_CURR_POS,    #0
        mov     M0_CURR_POS_SH, #1
        mov     M0_DEST_POS,    #0
        mov     M0_DEST_POS_SH, #1
        mov     M0_PHASE_AGE,   #0
        mov     M0_STAT,        #0
        mov     M0_STAT_SH,     #0

        mov     M1_CURR_POS,    #0
        mov     M1_CURR_POS_SH, #1
        mov     M1_DEST_POS,    #0
        mov     M1_DEST_POS_SH, #1
        mov     M1_PHASE_AGE,   #0
        mov     M1_STAT,        #0
        mov     M1_STAT_SH,     #0


        ; Time for an advertisement

        mov     DPTR, #TITLE_LINE1_3
        lcall   LCD_PRINT_LINE_1
        mov     DPTR, #TITLE_LINE2_4
        lcall   LCD_PRINT_LINE_2

        mov     r0, #100
j:      lcall   LCD_WAIT_FIXED_TIME
        djnz    r0, j


        ; Turn on interrupts (means: start maintain motors,
        ; update display, read keyboard and so on)!


        clr     ES      ;disable irq from serial port
;       clr     ET1     ;disable irq from timer1 (display)
;       clr     ET0     ;disable irq from timer0 (motors)
        setb    EA      ;enable interrupts !!


        ; Central point is Command Interpreter.
        ; He and interrupts do all the work.
        ;
        ; Interrupts keep relations:
        ;
        ; TR0: Variables <-> motors (run motors according to var. values)
        ; TR1: Variables <-> display (update display when necessary)
        ; TR1: Variables <-> keyboard (read keyboard and modify vars.)
        ;
        ; Command Interpreter keeps relation:
        ;
        ; Variables <-> RS-232 port


k:      mov     DPTR, #att_cmdtable
        mov     a, #32                  ; first analyzed char (spaces are ignored)
        lcall   cmdinterp
        sjmp    k
;
;
;///////////////////////////////////////////////////////////
;       main    -       data definitions
;       ----            ----------------
;
TITLE_LINE1_3   DB      'Attenuator Ctrl.'
                DB      'UNIPRESS (c)1999',0
TITLE_LINE2_4   DB      '   ver. 1.5.1   '
                DB      'RS-232: 9600,8N1',0
COPYRIGHT       DB      'Copyright by High Pressure Research Center,',0
                DB      'roman pielaszek, pielasze@unipress.waw.pl',0
                DB      'Warsaw, Poland, 1996-1999',0


;
;///////////////////////////////////////////////////////////
;       cmdlib data definitions
;       -----------------------
;
;

att_cmdtable:
        DB      9                                               ;row width
        DB      2,HIGH(att)     ,LOW(att),      '*ATT  '
        DB      2,HIGH(attq)    ,LOW(attq),     '*ATT? '
        DB      2,HIGH(attql)   ,LOW(attql),    '*ATTL?'
        DB      2,HIGH(help)    ,LOW(help),     '*HELP '
        DB      2,HIGH(help)    ,LOW(help),     '*HELP?'
        DB      2,HIGH(idnq)    ,LOW(idnq),     '*IDN? '
        DB      2,HIGH(print)   ,LOW(print),    '*PRINT'
        DB      2,HIGH(res)     ,LOW(res),      '*RES  '
        DB      2,HIGH(resq)    ,LOW(resq),     '*RES? '
        DB      2,HIGH(help)    ,LOW(help),     '?     '
        DB      2,HIGH(help)    ,LOW(help),     'HELP  '
        DB      2,HIGH(help)    ,LOW(help),     'HELP? '
cmdnf:  DB      2,HIGH(cmdnfv)  ,LOW(cmdnfv),   '      '

;
;
;///////////////////////////////////////////////////////////
;       *HELP, *HELP?, HELP, HELP, ?    commands
;
;
help:   
mov     a,b
        jz      help1
        lcall   cmd_vacuum_till_eol
help1:  mov     DPTR, #help_msg
        ljmp    cmd_print_msg
help_msg:
        DB      'Attenuator Controller v.1.5.1 Help',13,10
        DB      '----------------------------------',13,10
        DB      'Keyboard: [4] left  [5] reset  [6] right - 1st wheel (MS)',13,10
        DB      '          [7] left  [8] reset  [9] right - 2nd wheel (LS)',13,10
        DB      'Connection: RS-232 9600,8N1 <-fixed setting. Commands:',13,10
        DB      '?, HELP, *HELP, HELP?, *HELP? - display this message',13,10
        DB      '*ATT ij - sets attenuation foils to pos. ij, where i and j are',13,10
        DB      '          integers 0-9 (single chars!) ',13,10
        DB      '      i - wheel#1 (MSign.) position (0,1,2,..9)',13,10
        DB      '      j - wheel#0 (LSign.) position (0,1,2,..9)',13,10
        DB      '   e.g. "*ATT 13", "*ATT 00"',13,10
        DB      '*ATT?  - currnet pos. of wheels, e.g. "00", "52"',13,10
        DB      '*ATTL? - current pos. in long format, e.g. "0:none\n 1:0.05mmAl"',13,10
        DB      '*IDN?  - SCAPII string of manufacturer',13,10
        DB      '*RES i - reset i (0 or 1) wheel or both if no "i" is given',13,10
        DB      '*RES?  - status of resetting: "00"=completed "01","10" or "11"=not yet',13,10
        DB      '*PRINT - display string',13,10
        DB      '-----------',13,10
        DB      'More help: pielasze@unipress.waw.pl, +48 22 364433,',13,10
        DB      'Ref.:http://www.unipress.waw.pl/xray/desy/attenuator/',13,10
        DB      '(c)1999, High Pressure Research Center "UNIPRESS", Warsaw',13,10
        DB      '(c)1999, roman pielaszek, http://www.unipress.waw.pl/~pielasze',13,10
        DB      '',0

;
;
;///////////////////////////////////////////////////////////
;       *ATT
;
;
att:    mov     a, b            ; we expect some data
        jz      att1

        lcall   cmd_vacuum_whitespaces
        jz      att1
        clr     C
        subb    a, #'0'         ; check if a>'0'
        jc      att2            ; if not -> exit
        push    ACC
        subb    a, #10          ; check if a <= 9
        jnc     att2            ; if not -> exit
        pop     ACC             ; now a is indeed in range 0-9
        mov     M1_DEST_POS, a  ; run motor 1

        lcall   cmd_getchar
        jz      att1
        clr     C
        subb    a, #'0'         ; check if a>'0'
        jc      att2            ; if not -> exit
        push    ACC
        subb    a, #10          ; check if a <= 9
        jnc     att2            ; if not -> exit
        pop     ACC             ; now a is indeed in range 0-9
        mov     M0_DEST_POS, a  ; run motor 1

att2:   lcall   cmd_vacuum_till_eol
att1:   ret

;
;
;///////////////////////////////////////////////////////////
;       *ATT?
;
;
attq:   mov     a, b
        jz      attq1
        lcall   cmd_vacuum_till_eol

attq1:  mov     a, M1_CURR_POS
        add     a, #'0'
        lcall   cmd_putchar

        mov     a, M0_CURR_POS
        add     a, #'0'
        lcall   cmd_putchar

        mov     a, #0
        ljmp    cmd_putchar

;
;
;///////////////////////////////////////////////////////////
;       *ATTL?
;
;
attql:  mov     a, b
        jz      attql1
        lcall   cmd_vacuum_till_eol

attql1: mov     a, M1_CURR_POS          ; for definition of these symbols
        mov     b, #LABEL_SIZE          ; look in att_tr1.asm
        mul     ab
        add     a, #LOW(M1_LABELS)
        mov     dpl,a
        mov     a,b
        addc    a, #HIGH(M1_LABELS)
        mov     dph,a
        lcall   cmd_print_msg

        mov     a, M0_CURR_POS
        mov     b, #LABEL_SIZE
        mul     ab
        add     a, #LOW(M0_LABELS)
        mov     dpl,a
        mov     a,b
        addc    a, #HIGH(M0_LABELS)
        mov     dph,a
        ljmp    cmd_print_msg

;
;
;///////////////////////////////////////////////////////////
;       *IDN?
;
;
idnq:   mov     a,b
        jz      idnq1
        lcall   cmd_vacuum_till_eol
idnq1:  mov     DPTR, #idnq_msg
        ljmp    cmd_print_msg
idnq_msg:
        DB      'UNIPRESS (c)1999,X-Ray Attenuator Controller,S00001,V1.5.1',0

;
;
;///////////////////////////////////////////////////////////
;       *RES
;
;
res:    mov     a, b            ; we don't expect any data
        jz      res1            ; if no parameters -> reset both

        lcall   cmd_vacuum_whitespaces
        jz      res1

        cjne    a, #'0', res2
        mov     M0_STAT, #00000001b     ; reset motor 0
        ret

res2:   cjne    a, #'1', res3
        mov     M1_STAT, #00000001b     ; reset motor 1
        ret

res1:   mov     M0_STAT, #00000001b     ; reset motor 0
        mov     M1_STAT, #00000001b     ; reset motor 1
        ret

res3:   lcall   cmd_vacuum_till_eol
        ret

;
;
;///////////////////////////////////////////////////////////
;       *RES?
;
;
resq:   mov     a,b
        jz      resq1
        lcall   cmd_vacuum_till_eol

resq1:  mov     a, M1_STAT
        anl     a, #00000001b
        add     a, #'0'
        lcall   cmd_putchar

        mov     a, M0_STAT
        anl     a, #00000001b
        add     a, #'0'
        lcall   cmd_putchar

        mov     a, #0
        ljmp    cmd_putchar     


;
;
;///////////////////////////////////////////////////////////
;       *PRINT
;
;

print:  mov     a, b
        jz      pr4

        mov     r0, #0

        lcall   cmd_vacuum_whitespaces
        jz      pr4
        sjmp    pr3

pr2:    lcall   cmd_getchar
        jz      pr1
pr3:    mov     b, a

        mov     a, #50h         ; 3rd line on the display
        add     a, r0
        clr     EA              ; turn off interrupts
        lcall   LCD_ADDRESS
        mov     a, b
        lcall   LCD_OUT
        setb    EA              ; turn on interrupts
        inc     r0
        cjne    r0, #16, pr2

        lcall   cmd_vacuum_till_eol
pr1:    ret

pr4:    mov     DPTR, #LCD_CLEAR16
        clr     ET1
        lcall   LCD_PRINT_LINE_4
        setb    ET1
        ret




;
;
;///////////////////////////////////////////////////////////
;       Command Not Yey Interpreted
;
;
cnyi:   mov     a,b
        jz      cnyi1
        lcall   cmd_vacuum_till_eol
cnyi1:  mov     DPTR, #cnyi_msg
        ljmp    cmd_print_msg

cnyi_msg:       
        DB      'COMMAND NOT YET INTERPRETED',0

;
;
;///////////////////////////////////////////////////////////
;       Command Not Found       -       'BAD COMMAND' message
;
;
cmdnfv: mov     a,b
        jz      cnfv1
        lcall   cmd_vacuum_till_eol
cnfv1:  mov     DPTR, #cmdnf_msg
        ljmp    cmd_print_msg

cmdnf_msg:      
        DB      'BAD COMMAND',0

;
;
;
;       END OF att_main.asm
;///////////////////////////////////////////////////////////


;///////////////////////////////////////////////////////////
;       att_tr0.asm     -       TR0 interrupt routines
;       -----------             ----------------------
;
TR0_INIT:       ;give speed in ACC (see below)
;
;       Inicjowanie pracy Timera 0
;       Tryb 16-bit licznika z automatycznym przeladowaniem
;       podzial 32x1200Hz
;       th0= - (28800 / nasza_czestotliwosc)
        
        mov     TH0, A          ;speed of com, e.g. -3=9600; -2=14400; -1=28800

        mov     A, TMOD         ;try to change lower 4 bits without touching upper
        anl     A, #0F0h
        orl     A, #00000010b   ;C/T (Counter/Timer) --> Timer
        mov     TMOD,A          ;tryb 2: 8-bit timer z automatycznym
                                ;               przeladowaniem

        setb    IT0             ;active for slope (doesn't matter for timer)

        setb    TR0             ;start timera
        setb    ET0             ;enable TR0 interrupts
        ret
;
;
;///////////////////////////////////////////////////////////
;       Interrupt TR1 service routine
;       -----------------------------
;
INT_TR0:
;
        push    PSW
        push    ACC
        push    DPL
        push    DPH

        jb      M0_ON, ITR0M0_CMP       ; when motor if off don't check dynamics

        mov     A, M0_STAT
        anl     A,#10000000b            ;get phase of the movement
        cjne    A, #0, phase1

phase0: 
;phase zero of movemment means climbing from the gap in atten. wheel
;we want to reach stable 0 on M0_LIMIT_SW and leave switch noise behind
;
        inc     M0_PHASE_AGE    ;make the phase older, but...
        jnb     M0_LIMIT_SW, phase0_1   ;while still in previous position...

        mov     M0_PHASE_AGE, #0        ;reset age, we'll count again

phase0_1:
        mov     A, M0_PHASE_AGE
        cjne    A, #PHASE0_LENGTH, ITR0M0_CMP

        mov     A, M0_STAT
        anl     A, #01111111b
        orl     A, #10000000b   ;set phase=1 without affecting other bits
        mov     M0_STAT, A
        mov     M0_PHASE_AGE, #0        ;phase 1 just begun

phase1:
;phase one of movement means descending to destination gap in atten. wheel
;we want to reach stable 1 on M0_LIMIT_SW and leave switch noise behind
;
        inc     M0_PHASE_AGE            ;make the phase older, but...
        jb      M0_LIMIT_SW, phase1_1   ;while still in previous position...

        mov     M0_PHASE_AGE, #0        ;reset age, we'll count again

phase1_1:
        mov     A, M0_PHASE_AGE
        cjne    A, #PHASE1_LENGTH, ITR0M0_CMP

;
;well, we just arrived to next gap
;let's set phase again to zero and modify M0_CURR_POS
;
        mov     M0_PHASE_AGE, #0
        mov     a, M0_STAT
        anl     a, #01111111b
        mov     M0_STAT, a              ;set phase=0

        jb      M0_RIGHT, M0_minus
        inc     M0_CURR_POS
        mov     A, M0_CURR_POS
        cjne    A, #10, M0_DEFINED
        mov     M0_CURR_POS, #0
        sjmp    M0_DEFINED

M0_minus:
        jb      M0_LEFT, M0_DEFINED
        dec     M0_CURR_POS
        mov     A, M0_CURR_POS
        cjne    A, #-1, M0_DEFINED
        mov     M0_CURR_POS, #9

M0_DEFINED:
;       setb    M0_ON                   ;stop motor
;       setb    M0_LEFT
;       setb    M0_RIGHT
        sjmp    ITR0M0_CMP


ITR0M0_CHK_RESET:
        jnb     M0_RESET_SW, ITR0M0_MOVE_RIGHT
        jnb     M0_LIMIT_SW, ITR0M0_MOVE_RIGHT
        mov     M0_CURR_POS, #0         ;set origin right here
        mov     M0_DEST_POS, #0

        mov     a, M0_STAT
        anl     a, #01111110b;          ;clear reset bit and set phase=0
        mov     M0_STAT, a 
        mov     M0_PHASE_AGE, #0


ITR0M0_CMP:
        mov     a, M0_STAT
        anl     a, #00000001b;          ;get reset bit
        jnz     ITR0M0_CHK_RESET        ;in reset mode - go right

itr0m0_cmp2:
        mov     A, M0_CURR_POS
        cjne    A, M0_DEST_POS, ITR0M0_MOVE

        setb    M0_ON                   ;OK, we're on place. Stop motors.
        setb    M0_LEFT
        setb    M0_RIGHT
        sjmp    INT_TR0_M1              ;go to motor 1
        
ITR0M0_MOVE:
        mov     A, M0_CURR_POS
        setb    C
        subb    A, M0_DEST_POS
        jnb     ACC.7,ITR0M0_MOVE_LEFT  ;jump if result=dest-curr is negative

ITR0M0_MOVE_RIGHT:
        jb      M0_LEFT, it0m0mrr2      ;if turns left and we want to change direction...
        mov     a,M0_STAT
        anl     a,#10000000b;           ;get phase bit
        jz      it0m0mrr2               ;if phase==0 we can leave

        dec     M0_CURR_POS             ;in case we change direction in the
        mov     A, M0_CURR_POS          ;middle of movement, we have to assume
        cjne    A, #-1, it0m0mrr2       ;that we have reached destination
        mov     M0_CURR_POS, #9         ;already (-1), because in close future
                                        ;programm will increase (+1) mo_curr_pos
                                        ;being at the same position as before

it0m0mrr2:
        setb    M0_LEFT
        clr     M0_RIGHT                ;begin right turn
        clr     M0_ON
        sjmp    INT_TR0_M1

ITR0M0_MOVE_LEFT:
        jb      M0_RIGHT, it0m0mlr2     ;if turns left and we want to change diirection...
        mov     a, M0_STAT
        anl     a, #10000000b;          ;get phase bit
        jz      it0m0mlr2               ;if phase==0 we can leave

        inc     M0_CURR_POS             ;in case we change direction in the
        mov     A, M0_CURR_POS          ;middle of movement, we have to assume
        cjne    A, #10, it0m0mlr2       ;that we have reached destination
        mov     M0_CURR_POS, #0         ;already (+1), because in close future
                                        ;programm will decrease (-1) mo_curr_pos
                                        ;being at the same position as before
it0m0mlr2:
        setb    M0_RIGHT
        clr     M0_LEFT
        clr     M0_ON
;
;///////////////////////////////////////////////////////////
;       Routines for motor 1
;       --------------------
;

INT_TR0_M1:
        jb      M1_ON, ITR0M1_CMP       ; when motor if off don't check dynamics

        mov     A, M1_STAT
        anl     A,#10000000b            ;get phase of the movement
        cjne    A, #0, m1phase1

m1phase0:       
;phase zero of movemment means climbing from the gap in atten. wheel
;we want to reach stable 0 on M0_LIMIT_SW and leave switch noise behind
;
        inc     M1_PHASE_AGE    ;make the phase older, but...
        jnb     M1_LIMIT_SW, m1phase0_1 ;while still in previous position...

        mov     M1_PHASE_AGE, #0        ;reset age, we'll count again

m1phase0_1:
        mov     A, M1_PHASE_AGE
        cjne    A, #PHASE0_LENGTH, ITR0M1_CMP

        mov     A, M1_STAT
        anl     A, #01111111b
        orl     A, #10000000b   ;set phase=1 without affecting other bits
        mov     M1_STAT, A
        mov     M1_PHASE_AGE, #0        ;phase 1 just begun

m1phase1:
;phase zero of movement means descending to destination gap in atten. wheel
;we want to reach stable 1 on M0_LIMIT_SW and leave switch noise behind
;
        inc     M1_PHASE_AGE            ;make the phase older, but...
        jb      M1_LIMIT_SW, m1phase1_1 ;while still in previous position...

        mov     M1_PHASE_AGE, #0        ;reset age, we'll count again

m1phase1_1:
        mov     A, M1_PHASE_AGE
        cjne    A, #PHASE1_LENGTH, ITR0M1_CMP

;
;well, we just arrived to next gap
;let's set phase again to zero and modify M0_CURR_POS
;
        mov     M1_PHASE_AGE, #0
        mov     a, M1_STAT
        anl     a, #01111111b
        mov     M1_STAT, a              ;set phase=0

        jb      M1_RIGHT, M1_minus      
        inc     M1_CURR_POS
        mov     A, M1_CURR_POS
        cjne    A, #10, M1_DEFINED
        mov     M1_CURR_POS, #0
        sjmp    M1_DEFINED

M1_minus:
        jb      M1_LEFT, M1_DEFINED
        dec     M1_CURR_POS
        mov     A, M1_CURR_POS
        cjne    A, #-1, M1_DEFINED
        mov     M1_CURR_POS, #9

M1_DEFINED:
;       setb    M1_ON                   ;stop motor
;       setb    M1_LEFT
;       setb    M1_RIGHT
        sjmp    ITR0M1_CMP


ITR0M1_CHK_RESET:
        jnb     M1_RESET_SW, ITR0M1_MOVE_RIGHT
        jnb     M1_LIMIT_SW, ITR0M1_MOVE_RIGHT  
        mov     M1_CURR_POS, #0         ;set origin right here
        mov     M1_DEST_POS, #0

        mov     a, M1_STAT
        anl     a, #01111110b;          ;clear reset bit and set phase=0
        mov     M1_STAT, a 
        mov     M0_PHASE_AGE, #0


ITR0M1_CMP:
        mov     a, M1_STAT
        anl     a, #00000001b;          ;get reset bit
        jnz     ITR0M1_CHK_RESET        ;if in reset mode - go right

itr0m1_cmp2:
        mov     A, M1_CURR_POS
        cjne    A, M1_DEST_POS, ITR0M1_MOVE

        setb    M1_ON                   ;OK, we're on place. Stop motors.
        setb    M1_LEFT
        setb    M1_RIGHT
        sjmp    INT_TR0_M2              ;go to motor 2 (exit)
        
ITR0M1_MOVE:
        mov     A, M1_CURR_POS
        setb    C
        subb    A, M1_DEST_POS
        jnb     ACC.7,ITR0M1_MOVE_LEFT  ;jump if result=dest-curr is negative

ITR0M1_MOVE_RIGHT:
        jb      M1_LEFT, it0m1mrr2      ;if turns left and we want to change direction...
        mov     a,M1_STAT
        anl     a,#10000000b;           ;get phase bit
        jz      it0m1mrr2               ;if phase==0 we can leave

        dec     M1_CURR_POS             ;in case we change direction in the
        mov     A, M1_CURR_POS          ;middle of movement, we have to assume
        cjne    A, #-1, it0m1mrr2       ;that we have reached destination
        mov     M1_CURR_POS, #9         ;already (-1), because in close future
                                        ;programm will increase (+1) mo_curr_pos
                                        ;being at the same position as before
it0m1mrr2:
        setb    M1_LEFT
        clr     M1_RIGHT                ;begin right turn
        clr     M1_ON
        sjmp    INT_TR0_M2


ITR0M1_MOVE_LEFT:
        jb      M1_RIGHT, it0m1mlr2     ;if turns left and we want to change diirection...
        mov     a,M1_STAT
        anl     a,#10000000b;           ;get phase bit
        jz      it0m1mlr2               ;if phase==0 we can leave

        inc     M1_CURR_POS             ;in case we change direction in the
        mov     A, M1_CURR_POS          ;middle of movement, we have to assume
        cjne    A, #10, it0m1mlr2       ;that we have reached destination
        mov     M1_CURR_POS, #0         ;already (+1), because in close future
                                        ;programm will decrease (-1) mo_curr_pos
                                        ;being at the same position as before
it0m1mlr2:
        setb    M1_RIGHT
        clr     M1_LEFT
        clr     M1_ON
        

INT_TR0_M2:

INT_TR0_EXIT:
        pop     DPH
        pop     DPL
        pop     ACC
        pop     PSW
        reti
;
;
;
;
;       END OF att_tr0.asm
;///////////////////////////////////////////////////////////


;///////////////////////////////////////////////////////////
;       att_kbd.asm     -       keyboard routines
;       -----------             -----------------
;
;
;       [1] --- [2] --- [3]     --- KBD_H1 (horizontal line #1)
;
;       |       |       |
;       |       |       |
;
;       [4] --- [5] --- [6]     --- KBD_H2 (horizontal line #2)
;
;       |       |       |
;       |       |       |
;
;       [7] --- [8] --- [9]     --- KBD_H3 (horizontal line #3)
;
;       |       |       |
;       |       |       |
;
;       [*] --- [0] --- [#]     --- KBD_H4 (horizontal line #4)
;
;
;       |       |       |
;       |       |       |
;
;       KBD_V1  KBD_V2  KBD_V3  <--- vertical lines
;
;

ATT_KBD:
        ; keyboard behaves differently depending on reset status:
        ; -while in 'reset query' state will wait for reset
        ;  confirmation or cancel
        ; -while in 'normal' operating mode, will wait for:
        ;  * turning into 'reset query' mode
        ;  * attenuator wheel(s) movement(s)

        ; check for 'reset query' mode

                                        ; check if reset pressed
        jnb     KEY_M0_RES, kbd_reset

        mov     a, M0_STAT
        anl     a, #11111101b           ; clear 'reset query' bit
        mov     M0_STAT, a


        ;
        ;motor movements
        ;

        ;
        ;motor 0
        ;

        mov     a, M0_CURR_POS          ; is it moving already?
        cjne    a, M0_DEST_POS, kbd1    ; if so -> even don't check keys

        jb      KEY_M0_LEFT, kbd2       ; '-1' key pressed?

        dec     M0_DEST_POS             ; decrement dest.pos. of motor 0
        mov     a, M0_DEST_POS
        cjne    a, #-1, kbd1
        mov     M0_DEST_POS, #9
        sjmp    kbd1

kbd2:   jb      KEY_M0_RIGHT, kbd1      ; motor 0; '+1' key pressed

        inc     M0_DEST_POS
        mov     a, M0_DEST_POS
        cjne    a, #10, kbd1
        mov     M0_DEST_POS, #0

        ;
        ;motor 1
        ;


kbd1:   mov     a, M1_CURR_POS          ; is it moving already?
        cjne    a, M1_DEST_POS, kbd3    ; if so -> even don't check keys

        jb      KEY_M1_LEFT, kbd4       ; motor 0; key '-1' pressed?

        dec     M1_DEST_POS             ; decrement dest.pos. of motor 0
        mov     a, M1_DEST_POS
        cjne    a, #-1, kbd3
        mov     M1_DEST_POS, #9
        sjmp    kbd3

kbd4:   jb      KEY_M1_RIGHT, kbd3      ; motor 0; key '+1' pressed?

        inc     M1_DEST_POS
        mov     a, M1_DEST_POS
        cjne    a, #10, kbd3
        mov     M1_DEST_POS, #0

kbd3:   sjmp    kbd_exit                ; 'normal' movement section completed





kbd_reset:
        mov     a, M0_STAT
        orl     a, #00000010b           ; set 'reset query' bit
        mov     M0_STAT, a

        jb      KEY_M0_RIGHT, kbd5      ; '+1' pressed? (reset confirmation)

        mov     a, M0_STAT
        orl     a, #00000001b           ; give him reset
        mov     M0_STAT, a

kbd5:   jb      KEY_M0_LEFT, kbd6

        mov     a, M0_STAT
        anl     a, #11111110b           ; take him out from reset mode
        mov     M0_STAT, a

kbd6:   jb      KEY_M1_RIGHT, kbd7      ; '+1' pressed? (reset confirmation)

        mov     a, M1_STAT
        orl     a, #00000001b           ; give him reset
        mov     M1_STAT, a

kbd7:   jb      KEY_M1_LEFT, kbd8

        mov     a, M1_STAT
        anl     a, #11111110b           ; take him out from reset mode
        mov     M1_STAT, a

kbd8:   
kbd_exit:
        ret
;
;
;
;
;       END OF att_kbd.asm
;///////////////////////////////////////////////////////////


;///////////////////////////////////////////////////////////
;       att_tr1.asm     -       TR1 interrupt routines
;       -----------             ----------------------
;
;

TR1_INIT:       ;give speed in ACC (see below)
;
;       Inicjowanie pracy Timera 1 pelniacego role zegara
;       transmisyjnego
;       Tryb 8-bit licznika z automatycznym przeladowaniem
;       podzial 32x1200Hz
;       th1= - (28800 / nasza_czestotliwosc)
        
        mov     TH1, A          ;speed of com, e.g. -3=9600; -2=14400; -1=28800

        mov     A, TMOD         ;try to change upper 4 bits without touching lower;
        anl     A, #0Fh
        orl     A, #00100000b   ;C/T (Counter/Timer) --> Timer
        mov     TMOD,A          ;tryb 2: 8-bit timer z automatycznym
                                ;               przeladowaniem

        setb    IT1             ;active for slope (doesn't matter for timer)

        setb    TR1             ;start timera
        setb    ET1             ;enable interrupt
        ret
;
;
;///////////////////////////////////////////////////////////
;       Interrupt TR0 serviice routine
;       ------------------------------
;
INT_TR1:
;
        push    PSW
        push    ACC
        push    DPL
        push    DPH

        inc     TR1_DELAY
        mov     a, TR1_DELAY
        jz      it1m0
        ljmp    INT_TR1_EXIT
;
;call keyboard routines being in separate file "att_kbd.asm"
;
;
it1m0:

        lcall   ATT_KBD


;
;calculate differences between registers and their shadows
;while the difference appears - update display
;
        mov     a, M0_CURR_POS          ;check curr_pos
        clr     c
        subb    a, M0_CURR_POS_SH
        jz      it1m0_dest
        mov     M0_CURR_POS_SH, M0_CURR_POS
        lcall   M0_PRINT_NEW_CURR_POS

it1m0_dest:                             ;check dest_pos
        mov     a, M0_DEST_POS
        clr     c
        subb    a, M0_DEST_POS_SH
        jz      it1m0_resq
        mov     M0_DEST_POS_SH, M0_DEST_POS
        lcall   M0_PRINT_NEW_DEST_POS
        
it1m0_resq:                             ;check if 'reset query' bit changed
        mov     a, M0_STAT
        xrl     a, M0_STAT_SH
        anl     a, #00000010b           ;'reset query' bit
        jz      it1m0_res
        mov     a, M0_STAT              ;changed: now check the value
        anl     a, #00000010b           ;if present -> print it
        jz      it1m0_resq_clear

        mov     DPTR, #M0RSTQ
        lcall   LCD_PRINT_LINE_3
        sjmp    it1m0_res

it1m0_resq_clear:
        mov     DPTR, #LCD_CLEAR16
        lcall   LCD_PRINT_LINE_3


it1m0_res:                              ;check if reset bit changed
        mov     a, M0_STAT
        xrl     a, M0_STAT_SH
        anl     a, #00000001b           ;reset bit
        jz      it1m0_mov
        mov     a, M0_STAT              ;changed: now check the value
        anl     a, #00000001b           ;if present -> print it
        jz      it1m0_res_clear

        mov     DPTR, #M0RST
        lcall   M0_PRINT_NEW_STAT
        sjmp    it1m1
        
it1m0_res_clear:
        mov     DPTR, #M0STOP
        lcall   M0_PRINT_NEW_STAT


it1m0_mov:                              ;check reset bit
        mov     a, M0_STAT
        anl     a, #00000001b
        jnz     it1m1                   ;don't change status while resetting

        mov     a, MOTOR_REG
        xrl     a, MOTOR_REG_SH
        anl     a, #01110111b           ;ignore change of limit switches
        anl     a, #00001111b           ;interested only in motor 0
        jz      it1m1

        mov     a, MOTOR_REG            
        anl     a, #00001111b
        lcall   CALC_STAT_LABEL
        lcall   M0_PRINT_NEW_STAT
;
;///////////////////////////////////////////////////////////
;       Routines for MOTOR1
;       -------------------
;
;
it1m1:
        mov     a, M1_CURR_POS          ;check curr_pos
        clr     c
        subb    a, M1_CURR_POS_SH
        jz      it1m1_dest
        mov     M1_CURR_POS_SH, M1_CURR_POS
        lcall   M1_PRINT_NEW_CURR_POS

it1m1_dest:                             ;check dest_pos
        mov     a, M1_DEST_POS
        clr     c
        subb    a, M1_DEST_POS_SH
        jz      it1m1_resq
        mov     M1_DEST_POS_SH, M1_DEST_POS
        lcall   M1_PRINT_NEW_DEST_POS
        

it1m1_resq:                             ;check if 'reset query' bit changed
        mov     a, M1_STAT
        xrl     a, M1_STAT_SH
        anl     a, #00000010b           ;'reset query' bit
        jz      it1m1_res
        mov     a, M1_STAT              ;changed: now check the value
        anl     a, #00000010b           ;if present -> print it
        jz      it1m1_resq_clear

        mov     DPTR, #M0RSTQ
        lcall   LCD_PRINT_LINE_3
        sjmp    it1m1_res

it1m1_resq_clear:
        mov     DPTR, #LCD_CLEAR16
        lcall   LCD_PRINT_LINE_3


it1m1_res:                              ;check if reset bit changed
        mov     a, M1_STAT
        xrl     a, M1_STAT_SH
        anl     a, #00000001b
        jz      it1m1_mov
        mov     a, M1_STAT              ;changed: now check the value
        anl     a, #00000001b           ;if present -> print it
        jz      it1m1_res_clear

        mov     DPTR, #M0RST
        lcall   M1_PRINT_NEW_STAT
        sjmp    it1_exit
        
it1m1_res_clear:
        mov     DPTR, #M0STOP
        lcall   M1_PRINT_NEW_STAT
        sjmp    it1_exit



it1m1_mov:                              ;check reset bit
        mov     a, M1_STAT
        anl     a, #00000001b
        jnz     it1_exit                ;don't change status while resetting

        mov     a, MOTOR_REG
        xrl     a, MOTOR_REG_SH
        anl     a, #01110111b           ;ignore change of limit switches

        swap    a                       ;interrested only in motor 1
        anl     a, #00001111b
        jz      it1_exit

        mov     a, MOTOR_REG
        swap    a
        anl     a, #00001111b
        lcall   CALC_STAT_LABEL
        lcall   M1_PRINT_NEW_STAT


it1_exit:       
        mov     MOTOR_REG_SH,   MOTOR_REG       
        mov     M0_STAT_SH,     M0_STAT
        mov     M1_STAT_SH,     M1_STAT

INT_TR1_EXIT:
        pop     DPH
        pop     DPL
        pop     ACC
        pop     PSW
        reti
;
;
;///////////////////////////////////////////////////////////
;       display routines related to motor positions
;       -------------------------------------------
;
;
LABEL_SIZE      EQU     12

M0_PRINT_NEW_CURR_POS:
        mov     a, M0_CURR_POS
        mov     b, #LABEL_SIZE
        mul     ab
        add     a, #LOW(M0_LABELS)
        mov     dpl,a
        mov     a,b
        addc    a, #HIGH(M0_LABELS)
        mov     dph,a

        ljmp    LCD_PRINT_LINE_2

M0_LABELS:
        DB      '0:none     ',0
        DB      '1:0.05mmAl ',0
        DB      '2:0.10mmAl ',0
        DB      '3:0.15mmAl ',0
        DB      '4:0.20mmAl ',0
        DB      '5:0.25mmAl ',0
        DB      '6:0.30mmAl ',0
        DB      '7:0.35mmAl ',0
        DB      '8:0.40mmAl ',0
        DB      '9:0.45mmAl ',0


M1_PRINT_NEW_CURR_POS:
        mov     a, M1_CURR_POS
        mov     b, #LABEL_SIZE
        mul     ab
        add     a, #LOW(M1_LABELS)
        mov     dpl,a
        mov     a,b
        addc    a, #HIGH(M1_LABELS)
        mov     dph,a

        ljmp    LCD_PRINT_LINE_1

M1_LABELS:
        DB      '0:none     ',0
        DB      '1:0.50mmAl ',0
        DB      '2:1.00mmAl ',0
        DB      '3:1.50mmAl ',0
        DB      '4:2.00mmAl ',0
        DB      '5:2.50mmAl ',0
        DB      '6:3.00mmAl ',0
        DB      '7:2.00mmPb ',0
        DB      '8:3.00mmPb ',0
        DB      '9:User`s   ',0



M0_PRINT_NEW_DEST_POS:
        mov     a,#4Fh
        lcall   LCD_ADDRESS
        mov     a, M0_DEST_POS
        lcall   LCD_PRINT_DIGIT_9
        ret


M1_PRINT_NEW_DEST_POS:
        mov     a,#0Fh
        lcall   LCD_ADDRESS
        mov     a, M1_DEST_POS
        lcall   LCD_PRINT_DIGIT_9
        ret



CALC_STAT_LABEL:
        jnb     ACC.2, csl_turns
        mov     DPTR, #M0STOP
        ret

csl_turns:
        jb      ACC.0, csl_right
        mov     DPTR, #M0LEFT
        ret

csl_right:
        jb      ACC.1, csl_exit
        mov     DPTR, #M0RGHT

csl_exit:
        ret

M0LEFT: DB      '<---',0
M0RGHT: DB      '--->',0
M0STOP: DB      ' OK ',0
M0RST:  DB      'RES.',0
M0RSTQ:         DB      '-no  RESET? yes+',0


M0_PRINT_NEW_STAT:
        mov     a,#4Bh
        lcall   LCD_ADDRESS
        lcall   LCD_PRINTF
        ret


M1_PRINT_NEW_STAT:
        mov     a,#0Bh
        lcall   LCD_ADDRESS
        lcall   LCD_PRINTF
        ret
;
;
;
;
;       END OF att_tr1.asm
;///////////////////////////////////////////////////////////


;///////////////////////////////////////////////////////////
;       cmdlib.asm      -       Command Interpreter
;       ----------              -------------------
;
;
;       This routine will read char-by-char from an input (e.g. serial port),
;       look in appended commands' table (look at the end of this section for
;       structure of the table) and jump to respective user-defined procedures.
;
;
EOL     EQU     13

;
;       User has to change routines cmd_getchar and cmd_putchar
;       according to his needs. Now, they read a byte from serial
;       port and send output to serial too.
;       You have also to adjust EOL sign to be agreed with your
;       system.
;
cmd_getchar:
        lcall   SP_IN
        cjne    a, #EOL, cmdgc
        mov     a, #0           ; replace EOL with 0
cmdgc:  jnb     ACC.6, cmdgc2
        clr     ACC.5           ; lower-case to upper-case
cmdgc2: ret
;
;
;
;
;
cmd_putchar:
        push    ACC
        jnz     cmdpc
        mov     a, #10
        lcall   SP_OUT
        mov     a, #EOL
cmdpc:  lcall   SP_OUT
        pop     ACC
        ret

;
;
;///////////////////////////////////////////////////////////
;       cmdinterp       -       main interpreting routine
;       ---------               -------------------------
;
;Input:         DPTR    -       Address of table of commands (table starts
;                               with CMD_ROW_LGTH constant)
;               ACC     -       A first char from input stream. Sometimes
;                               happens, that your reading routine read
;                               one char too much. Put it to ACC. If
;                               whitespace (e.g. space) will be ignored.
;
;Output:        dependent on command routines (cmdinterp jumps to them)
;               Last charater read is ported to called routine in B reg.
;Changes:       ACC,B,DPTR,r0,r1,r2,r3,r4,r5,r6
;
;Notice:        You have to use bank#0 of registers (absolute addresses
;               are used)
;

LAST_CHAR       EQU     5       ; means cell of register R5
CMD_ROW_LGTH    EQU     6       ; means cell of register R6

cmdinterp:
        push    ACC             ; in ACC we have first char read already by
                                ; calling procedure. If space -> ignore him!
        mov     r1, #0          ; row number in commands table
        mov     r0, #3          ; columne number in command table (+2  -> routine address)

        mov     a, #0           ; initialize constant CMD_ROW_LGTH, which
        movc    a,@A+DPTR       ; comes as first byte, before right comd. table
        mov     CMD_ROW_LGTH,a  ; begining

        mov     a, #1           ; add to DPTR this one byte of offset to the
        add     a, dpl          ; begining of data in commands' table
        mov     dpl, a
        mov     a, #0
        addc    a, dph
        mov     dph, a

        mov     r2, dpl
        mov     r3, dph

        pop     ACC
        lcall   cmd_is_whitespace
        jnc     cmd11

cmd1:
        ; First of all pull out all the whitespaces which may appear befor
        ; a command.

        lcall   cmd_vacuum_whitespaces
        jnz     cmd11           ; ignore empty strings
        ret

        ; now, in ACC, there is first character of the command.
        ; Calculate begining of the commands' table.

cmd11:  mov     LAST_CHAR, a
        lcall   cmd_curr_row_DPTR

        mov     a, r0
        movc    a,@a+DPTR       ;get value in current row
        mov     b, a
        mov     a, LAST_CHAR
        sjmp    cmd20

        ; we have to jump-in-to central loop which will read all chars
        ; of the command

cmd2:     inc   r0              ;scan current line
          mov   a, r0
          movc  a,@a+DPTR       ;get value in current row
          mov   b, a
          lcall cmd_getchar     ;load new char to ACC

cmd20:    mov   LAST_CHAR, a
          mov   a, r0
          clr   C
          subb  a, CMD_ROW_LGTH
          jz    cmd_found       ;we are after last char in row
                                ;all row matches -> call service routine
                                ;for further investigations
          mov   a, LAST_CHAR

cmd201: 
;       lcall   cmd_putchar
;       push    ACC
;       mov     a,b
;       lcall   cmd_putchar
;       mov     b,a
;       pop     ACC 
          cjne  a, b, cmd21     ; perform comparison!
          lcall cmd_is_whitespace
          jnc   cmd2            ; no, it's regular char -> do another loop
          mov   LAST_CHAR, a
          sjmp  cmd_found

          ; the characters don't fit. We have two options:
          ; 1) EOL appeared at getchar, and so:
          ;    - if in table we have whitespace here (end of word) -> ok
          ;    - if we have a regular letter - they do not fit indeed
          ; 2) There is maybe a wildcard char in the table here? -> continue
          ; 3) they really do not fit -> try in another row of table

cmd21:    ; First, clearify question of EOL - not EOL

          jnz   cmd22                   ; Try if we have EOL at getchar
          mov   LAST_CHAR, a            ; yes, save EOL char for further routines
          mov   a, b                    ; restore value read from table
          lcall cmd_is_whitespace       ; was a whitespace in table at this pos?
          jc    cmd_found               ; Yes! -> we won!
          sjmp  cmd_not_found           ; word in table was too log - aparently
                                        ; was nothing better befor

cmd22:    ; What we know here is:
          ; 1) There was a misfit getchar<->table
          ; 2) There is no EOL at getchar
          ; 3) This is not end of table

          ; Check for wildcard in the table. If so -> continue search.

          mov   LAST_CHAR, a            ; store getchar
          mov   a, b                    ; restore that from table
          cjne  a, #'~', cmd3           ; was it wildcard '~' ?
          sjmp  cmd2                    ; yes, make loop again


cmd_found:      ;we found matching string!
        mov     a, #0
        mov     b, LAST_CHAR    ;send last char to the procedure
        jmp     @A+DPTR         ;jump at the address from begining of the row



cmd_not_found:  ;there is no such a command
        mov     a, LAST_CHAR            ; vacuum input till EOL (wait for EOL) 
;       jz      cmdnf1                  ; if last char was EOL,
;       lcall   cmd_vacuum_till_eol     ; don't try to getchar again!
;
;cmdnf1:        mov     DPTR, #cmdnf_msg                ; now send a message to output
;       ljmp    cmd_print_msg

; There is another epiloque below, you can call routine as other items iin table
        mov     DPTR, #cmdnf
        mov     a, #0
        mov     b, LAST_CHAR
        jmp     @A+DPTR

;
;
; section 2: try to move system to next row if they fit at least till
;               the position we stopped last scan, e.g.:
;
;if text received so far is: "ala_ma_p", and in table we have:
;               ala_ma_kota
;               ala_ma_pchly
;they fit till--------^ this position (7th char == char of index #6)
;and index r0 is here--^ right now (r0 == 7+2 == 9 )
;It means: index r0 is now on the first misfitting position
;
;
cmd3:                   ; check if received value is lt or gt that in table
        mov     a, r0
        movc    a,@A+DPTR
        clr     C
        subb    a, LAST_CHAR    ; if char in table is gt LAST_CHAR...
        jnb     ACC.7, cmd_not_found    ; there will be nothing better below
                                ; since table is in alphabetical order

        ; we're about to compare two rows from the begining (3rd char)
        ; till the position we stopped previous loop.
        
cmd4:
        mov     r4,#2           ; r4 will be loop index, 
                                ; start at val=3 (2+inc r4 below)

cmd41:    inc   r4              ; scan current line

          mov   a, r4           ; check counters: we don't want to exceed
          clr   C               ; value of old index (r0)
          subb  a, r0
          jz    cmd421          ; ok, till index==r0 strings are identical

cmd42:    mov   a, r4
          movc  a,@a+DPTR       ; get value in current row...
          mov   b, a            ; ...and store in B

          mov   a, r4
          add   a, CMD_ROW_LGTH
          movc  a,@a+DPTR       ; get value in next row...

;       lcall   cmd_putchar
;       push    ACC
;       mov     a,b
;       lcall   cmd_putchar
;       mov     b,a
;       pop     ACC 

          cjne  a, b, cmd43
          sjmp  cmd41

        ; ok, string being below is identical till the position we suspended
        ; last scan. Thus we can increment row counter (r1) and continue from
        ; the point we stopped

cmd421: inc     r1
        mov     a, LAST_CHAR
        ljmp    cmd11


cmd43:  cjne    a, #'~', cmd44  ;wildcards '~' are also welcome -> continue
        sjmp    cmd41

                                ; here, there is no successor: last line
cmd44:  sjmp    cmd_not_found   ; was bad, we can't continue with this one
                                ; as well








cmd_curr_row_DPTR:
        push    ACC
        push    B

        mov     a, r1           ;calculate offset to begining of current row
        mov     b, CMD_ROW_LGTH
        mul     ab

        add     a, r2           ;add the offset to begining of the table
        mov     dpl, a
        mov     a, b
        addc    a, r3           ;original DPTR is stored in r2,r3
        mov     dph, a

        ;now we have in DPTR address of begining of current row
        pop     B
        pop     ACC
        ret
;
;
;///////////////////////////////////////////////////////////
;       Supporting cmd_ routines
;       ------------------------
;
;
cmd_is_whitespace:
        clr     C
        cjne    a, #EOL, cmdiw1
        setb    C
        ret
cmdiw1: cjne    a, #32, cmdiw2
        setb    C
        ret
cmdiw2: push    ACC     
        anl     a, #11100000b
        jnz     cmdiw3
        setb    C
cmdiw3: pop     ACC
        ret



cmd_vacuum_whitespaces:
        lcall   cmd_getchar
        lcall   cmd_is_whitespace
        jc      cmd_vacuum_whitespaces
        ret



cmd_vacuum_till_eol:
        lcall   cmd_getchar
        jnz     cmd_vacuum_till_eol
        ret



cmd_print_msg:
        mov     a, r0
        push    ACC
        mov     r0, #-1

cmdpm1: inc     r0
        mov     a,r0
        movc    a,@A+DPTR
        lcall   cmd_putchar
        jz      cmdpm2
        cjne    r0, #255, cmdpm1
        inc     dph
        sjmp    cmdpm1

cmdpm2: pop     ACC
        mov     r0, a
        ret

;
;///////////////////////////////////////////////////////////
;       cmdlib data definitions
;       -----------------------
;
;WARNING! Phrases in the table MUST be in alphabetical order !!!
;
;
;cmdtable:
;       DB      16                                              ;row width
;       DB      2,HIGH(cyc)     ,LOW(cyc),      'cyc          '
;       DB      2,HIGH(dupa)    ,LOW(dupa),     'dupa         '
;       DB      2,HIGH(idn)     ,LOW(idn),      'idn          '
;cmdnf: DB      2,HIGH(cmdnfv)  ,LOW(cmdnfv),   '             '
;
;
;dupa:  ;lcall  cmd_vacuum_till_eol
;       mov     DPTR, #dupa_msg
;       ljmp    cmd_print_msg
;dupa_msg:
;       DB      'DUPA jest OK!',0
;
;
;cyc:   ;lcall  cmd_vacuum_till_eol
;       mov     DPTR, #cyc_msg
;       ljmp    cmd_print_msg
;cyc_msg:
;       DB      'CYC jest OK!',0
;
;
;idn:   mov     DPTR, #cmdidn_msg
;       ljmp    cmd_print_msg
;cmdidn_msg:
;       DB      'IDN jest OK!',0
;
;
;cmdnfv:        mov     DPTR, #cmdnf_msg
;       ljmp    cmd_print_msg
;
;cmdnf_msg:     
;       DB      'LAST LINE - BAD COMMAND',0
;
;
;
;       END OF cmdlib.asm
;///////////////////////////////////////////////////////////




;
;RS Interrupt routine
;
INT_RS:
        push    PSW
        push    ACC
        push    DPL
        push    DPH
                                ;przerwanie z powodu nowo odebranego bajtu
                                ;czy prosba o nadanie kolejnego?
        jb      TI,RS_DATA_SENT
        jb      RI,RS_DATA_RECEIVED

RS_IRQ_EXIT:
        pop     DPH
        pop     DPL
        pop     ACC
        pop     PSW
        reti

RS_DATA_SENT:
        clr     TI
        sjmp    RS_IRQ_EXIT

RS_DATA_RECEIVED:
        mov     A,SBUF  ;odczytanie danej z bufora odbiornika
        lcall   LCD_OUT
        clr     RI      ;przygotowanie do odbioru nastepnej danej
        sjmp    RS_IRQ_EXIT




;*************************************************
;*          INICJACJA PORTU SZEREGOWEGO          *
;*************************************************

;       Inicjowanie pracy portu szeregowego
;       Tryb 8-bit UART,
;       Flaga gotowosci nadawania ustawiona
RS_INIT:
        mov     SCON, #01010010b
        setb    ES
;       setb    PS      ;sets higher irq priority

;       Inicjowanie pracy Timera 1 pelniacego role zegara
;       transmisyjnego
;       Tryb 16-bit licznika z automatycznym przeladowaniem
;       podzial 32x1200Hz
;       th1= - (28800 / nasza_czestotliwosc)
T1_INIT:
;       mov     A,#-3           ; 9600bps
;get speed parameter from calling programm
        sjmp    RS_TR1_INIT     ;call initialisation of timer1 and exit routine




;SP_OUT Wysyla znak przez port szeregowy, jesli jest on gotowy
;
;
SP_OUT: jnb     TI,$    ;oczekiwanie na zwolnienie nadajnika
        clr     TI
        mov     SBUF,A  ;wyslanie danej
        ret


;SP_IN  Odbiera znak z portu szeregowego
;
;
SP_IN:  jnb     RI,$    ;oczekiwanie na pojawienie sie danej
        clr     RI      ;przygotowanie do odbioru nastepnej danej
        mov     A,SBUF  ;odczytanie danej z bufora odbiornika
        ret


                

RS_TR1_INIT:    ;give speed in ACC (see below)
;
;       Inicjowanie pracy Timera 1 pelniacego role zegara
;       transmisyjnego
;       Tryb 16-bit licznika z automatycznym przeladowaniem
;       podzial 32x1200Hz
;       th1= - (28800 / nasza_czestotliwosc)

        
        mov     TH1, A          ;speed of com, e.g. -3=9600; -2=14400; -1=28800

        mov     A, TMOD         ;try to change upper 4 bits without touching lower
        anl     A, #0Fh
        orl     A, #00100000b   ;C/T (Counter/Timer) --> Timer
        mov     TMOD,A          ;tryb 2: 8-bit timer z automatycznym
                                ;               przeladowaniem
;       mov     TMOD, #00100000b
;       setb    IT1             ;active for slope (doesn't matter for timer)

        setb    TR1             ;start timera
        ret

;///////////////////////////////////////////////////////////
;       lcdlib.asm      -       LCD routines
;       ----------              ------------
;
;
;///////////////////////////////////////////////////////////
;       LCD labels      should be already defined in hardware board section (e.g. att_brd.h or 80c535.h) !
;       ----------
;
;LCD_E           EQU     P3.5
;LCD_RW          EQU     P3.4
;LCD_RS          EQU     P3.3
;LCD_DATA        EQU     P4
;
;
;User should    lcall   LCD_INIT routine then the other
;
;       LCD Init
;       --------
;       Konfiguracja wyswietlacza LCD dla 8-bitowego dwu(cztero!)linijkowca
;       wykonanie komend F, C, A i D tak, aby ustawic:
;       - transmisja 8 bitow, ilosc linii = 2, czcionka 5x7
;       - inkrementacja kursora przy przesuwie, brak przewijania
;       - czyszczenie ekranu i ustawienie kursora w polozenie 0
;       - wlaczenie ekranu, kursora, brak migania
;

LCD_INIT:
;       wykonujemy F, C, A i D

;       F: ustawienie na 8bit, 1 linia, czcionka 5x7
        mov a, #00111000b
        lcall LCD_COMMAND


;       C: kierunek przesuwu kursora: inkrementacja, nieprzewijanie
        mov a, #00000110b
        lcall LCD_COMMAND


;       A: czysci DRAM i ustawia kursor w polozenie 0
        mov a, #00000001b
        lcall LCD_COMMAND


;       D: wlaczony ekran, kursor i wylaczone miganie
        mov a, #00001110b
        lcall LCD_COMMAND

        ret


LCD_LINE_1:
        mov     a, #10000000b   ; 128 + adres 0
        lcall   LCD_COMMAND
        ret

LCD_LINE_2:
        mov     a, #11000000b   ; 128 + adres 64
        lcall   LCD_COMMAND
        ret

LCD_LINE_3:
        mov     a, #10010000b   ; 128 + adres 16
        lcall   LCD_COMMAND
        ret

LCD_LINE_4:
        mov     a, #11010000b   ; 128 + adres 64 + 16
        lcall   LCD_COMMAND
        ret

LCD_PRINT_LINE_1:
        mov     a, #10000000b   ; 128 + adres 0
        lcall   LCD_COMMAND
        lcall   LCD_PRINTF
        ret

LCD_PRINT_LINE_2:
        mov     a, #11000000b   ; 128 + adres 64
        lcall   LCD_COMMAND
        lcall   LCD_PRINTF
        ret

LCD_PRINT_LINE_3:
        mov     a, #10010000b   ; 128 + adres 16
        lcall   LCD_COMMAND
        lcall   LCD_PRINTF
        ret

LCD_PRINT_LINE_4:
        mov     a, #11010000b   ; 128 + adres 64 + 16
        lcall   LCD_COMMAND
        lcall   LCD_PRINTF
        ret

LCD_ADDRESS:
        setb    ACC.7
        lcall   LCD_COMMAND
        ret



REFRESH_DISPLAY:
        ; Odswieza wyswietlacz
        mov     a, #00000001b
        lcall   LCD_COMMAND
        ret


LCD_PRINT_HEX:
        push    DPL
        push    DPH

        mov     DPTR, #HEX_TABLE

        mov     B, A
        swap    A
        anl     A, #0Fh
        movc    A, @A+DPTR
        lcall   LCD_OUT

        mov     A, B
        anl     A, #0Fh
        movc    A, @A+DPTR
        lcall   LCD_OUT

        pop     DPH
        pop     DPL
        ret



LCD_PRINT_SINT:
                JNB     ACC.7,LCD_PRINT_USINT
                CPL     A
                INC     A
LCD_PRINT_USINT:
                push    dpl
                push    dph
                MOV     B,#100
                DIV     AB
                ADD     A,#'0'
                PUSH    B
                PUSH    ACC
                LCALL   LCD_OUT
                POP     ACC
                POP     B
                MOV     A,B
                MOV     B,#10
                DIV     AB
                ADD     A,#'0'
                PUSH    ACC
                PUSH    B
                LCALL   LCD_OUT
                POP     B
                POP     ACC
                MOV     A,B
                ADD     A,#'0'
                LCALL   LCD_OUT
                pop     dph
                pop     dpl
                RET






LCD_PRINT_DIGIT:
        mov     B, #100
        div     AB
        add     A, #48
        lcall   LCD_OUT

        mov     A, B
        mov     B, #10
        div     AB
        add     A, #48
        lcall   LCD_OUT

        mov     A, B
        add     A, #48
        lcall   LCD_OUT
        ret




LCD_PRINT_DIGIT_99:
        mov     B, #10
        div     AB
        add     A, #48
        lcall   LCD_OUT

        mov     A, B
        add     A, #48
        lcall   LCD_OUT
        ret




LCD_PRINT_DIGIT_9:
        add     A, #48
        lcall   LCD_OUT
        ret



LCD_PRINTF:
        push    ACC
        mov     a, r1
        push    ACC

        mov     R1, #0
        
lpf1    mov     a, R1
        movc    a, @A+DPTR      

        jz      lpfexit

        lcall   LCD_OUT
        inc     R1
        sjmp    lpf1

lpfexit pop     ACC
        mov     r1, a
        pop     ACC
        ret



LCD_WAIT_FIXED_TIME:
        push    ACC
        mov     a, r7
        push    ACC
        mov     r7,#255
empty:
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        djnz r7, empty
        pop     ACC
        mov     r7, a
        pop     ACC
        ret
        



LCD_WAIT:
        push    ACC
        clr     LCD_RS
        setb    LCD_RW
lwib1:  setb    LCD_E
        mov     a, LCD_DATA  ; czytaj Busy Flag
        clr     LCD_E

        jb      ACC.7, lwib1

        clr     LCD_RW
        pop     ACC
        ret




STROB_E:
        setb    LCD_E
        lcall   LCD_WAIT_FIXED_TIME
        clr     LCD_E
        ret





LCD_COMMAND:
        push    ACC
        
        lcall   LCD_WAIT

        mov     LCD_DATA, a     ; wystaw dane na magistrale wyswietlacza
        clr     LCD_RS          ; ustaw sygnaly do zapisu komendy
        clr     LCD_RW
        lcall   STROB_E         ; E=1

        pop     ACC
        ret     



LCD_OUT:
        push    ACC

        lcall   LCD_WAIT

        mov     LCD_DATA, a     ; dane na magistrale
        clr     LCD_RW          ; ustaw zapis
        setb    LCD_RS          ; RS=1, czyli dana do wyslania
        lcall   STROB_E
        clr     LCD_RS

        pop     ACC
        ret





                
;**************************************
;**                                  **
;**      LCD DATA DEFINITIONS        **
;**                                  **
;**************************************

LCD_CLEAR16     DB      '                ',0
ala             DB      'ALA MA KOTA',  0,0
HEX_TABLE       DB      '0123456789ABCDEF'
;
;
;
;
;       END OF LCD routines (lcdlib.asm)
;///////////////////////////////////////////////////////////


END