;
code_base:
;
init_entry:
	LJMP	init_driver
;
;	call vector 0x003H:	ALLOCATE_CELL
;
ac_entry:			; ALLOCATE CELL to be called from BASIC
	LJMP	cl_ac
;
;	call vector 0x006H:	TEST_QUEUE <TOS>
;
tq_entry:			; TEST QUEUE to be called from BASIC
	LJMP	cl_tq
;
;	call vector 0x009H:	PUT_QUEUE
;
pq_entry:			; PUT QUEUE to be called from BASIC
	LJMP	cl_pq
;
;	call vector 0x00CH:	GET_QUEUE
;
gq_entry:			; GET QUEUE to be called from BASIC
	LJMP	cl_gq
;
;	call vector 0x00FH:	the TASKER
;
tk_entry:
	LJMP	cl_tasker	; TASKER to be called from BASIC
;
; ---------------------------------------------------------------
;
	ORG	code_base+030H
;
; ---------------------------------------------------------------
;
cl_tq:
	anl	psw,#11100111B
	mov	a,#1
	lcall	030h		; pop queue number parameter
	mov	a,r1
	lcall	setq
	lcall	tstq		; check if queue contains data
	mov	r0,a
	mov	r2,#0
	mov	a,#154
	lcall	030h		; push result on argument stack
	ret
;
; ---------------------------------------------------------------
;
cl_gq:
	anl	psw,#11100111B
	mov	a,#1
	lcall	030h		; pop queue number
	mov	a,r1
	lcall	setq
	lcall	remqh
	mov	r0,dpl
	mov	r2,dph
	mov	a,#154
	lcall	030H		; push cell pointer
	ret
;
; ---------------------------------------------------------------
;
cl_ac:
	anl	psw,#11100111B
	lcall	alloc_cell
	mov	r0,dpl
	mov	r2,dph
	mov	a,#154
	lcall	030H		; push cell pointer
	ret	
;
; ---------------------------------------------------------------
;
	using	0
;    
cl_pq:
	anl	psw,#11100111B
	mov	a,#1
	lcall	030h		; pop queue number parameter
	mov	a,r1
	lcall	setq
	push	ar1
	push	ar3		; save QCB pointer
	mov	a,#1
	lcall	030h		; pop cell pointer
	mov	dpl,r1
	mov	dph,r3
	pop	ar3
	pop	ar1
	lcall	insqt		; insert cell in queue
	ret	
;
; ---------------------------------------------------------------
;
; FUNCTION:	LDIR
; INPUT:	R0/R2 - destination pointer
;		R1/R3 - source pointer
;		R4    - byte count 
; DESTORYS:	R1,R3,R4 of bank 0
; ACTION:	copy <R4> bytes from <R1/R3> to <R0/2>
;
	USING	0
ldir:
	push	ACC
	push	AR0
	push	AR2
ldirl:
	mov	dpl,r1
	mov	dph,r3
	movx	a,@dptr
	inc	dptr
	mov	r1,dpl
	mov	r3,dph
	mov	dpl,r0
	mov	dph,r2
	movx	@dptr,a
	inc	dptr
	mov	r0,dpl
	mov	r2,dph
	djnz	r4,ldirl
	pop	AR2
	pop	AR0
	pop	ACC
	ret
;
; ---------------------------------------------------------------
;
;  task control table
;  
;  
;  key/sensor assignment:
;  
;  0/0 = 040H ... Vorzimmer9 ... EssZiTür ... links ........ A0101
;  0/1 = 041H ... Vorzimmer9 ... EssZiTür ... rechts ....... A0102
;  0/2 = 042H ... Vorzimmer9 ... WCTür ...... links ........ A0201
;  0/3 = 043H ... Vorzimmer9 ... WCTür ...... rechts ....... A0202
;  0/4 = 044H ... Vorzimmer9 ... BadTür ..... links ........ A0301
;  0/5 = 045H ... Vorzimmer9 ... BadTür ..... rechts ....... A0302
;  0/6 = 046H ... Vorzimmer9 ... EingTür .... links ........ A0401
;  0/7 = 047H ... Vorzimmer9 ... EingTür .... rechts ....... A0402
;  
;  1/0 = 048H/008H
;  1/1 = 049H/009H
;  1/2 = 04AH/00AH ... BI-relay-return 0 ... Vorzimmer9 ... Spiegellampe
;  1/3 = 04BH/00BH ... BI-relay-return 1 ... Bad9 ......... Waschtischlampe
;  1/4 = 04CH/00CH ... BI-relay-return 2 ... Bad9 ......... Deckenlampe
;  1/5 = 04DH/00DH ... BI-relay-return 3 ... Bad9 ......... Lüfter
;  1/6 = 04EH/00EH ... BI-relay-return 4 ... Vorzimmer9 ... Deckenlampe
;  1/7 = 04FH/00FH ... BI-relay-return 5 ... WC9 .......... Deckenlampe
;  
;  4/0 = 060H ... Esszimmer9 ... VorZiTür ... linksOben .... B0101 
;  4/1 = 061H ... Esszimmer9 ... VorZiTür ... rechtsOben ... B0102
;  4/2 = 062H ... Esszimmer9 ... VorZiTür ... linksUnten ... B0103
;  4/3 = 063H ... Esszimmer9 ... VorZiTür ... rechtsUnten .. B0104
;  4/4 = 064H/024H ... BI-relay-return 08 ... Esszimmer	.... Kartentisch
;  4/5 = 065H/025H ... BI-relay-return 09 ... Esszimmer	.... Esstisch
;  4/6 = 066H/026H ... BI-relay-return 10 ... Esszimmer	.... Bar
;  4/7 = 067H/027H ... BI-relay-return 11 ... Esszimmer	.... Lichtsäule
;  5/0 = 068H ... Esszimmer9 ... ArbZiTür ... linksOben .... B0201
;  5/1 = 069H ... Esszimmer9 ... ArbZiTür ... rechtsOben ... B0202
;  5/2 = 06AH ... Esszimmer9 ... ArbZiTür ... linksUnten ... B0203
;  5/3 = 06BH ... Esszimmer9 ... ArbZiTür ... rechtsUnten .. B0204
;
;  6/0 = 070H ... Esszimmer9 ... InBar ...... links ........ B0301
;  6/1 = 071H ... Esszimmer9 ... InBar ...... linksMitte ... B0302
;  6/2 = 072H ... Esszimmer9 ... InBar ...... rechtsMitte .. B0303
;  6/3 = 073H ... Esszimmer9 ... InBar ...... rechts ....... B0304
;
;  7/0 = 078H ... Esszimmer9 ... LoggiaTür .. linksOben .... B0401
;  7/1 = 079H ... Esszimmer9 ... LoggiaTür .. rechtsOben ... B0402
;  7/2 = 07AH ... Esszimmer9 ... LoggiaTür .. linksUnten ... B0403
;  7/3 = 07BH ... Esszimmer9 ... LoggiaTür .. rechtsUnten .. B0404
;  
;  
;  relay/driver assignment
;  
;  MO-relay 00 +
;  MO-relay 01 ... BI-relay AKE  memorizes BI-relay 3 as ON
        DB	1,00DH,pev_mboff,3  ; key 1/2 B00 ... Vorzimmer9 ... Spiegellampe
;  MO-relay 02 +
;  MO-relay 03 ... BI-relay 01 ... Bad9 ......... Waschtischlampe
;  MO-relay 04 +
;  MO-relay 05 ... BI-relay 02 ... Bad9 ......... Deckenlampe
;  MO-relay 06 +
;  MO-relay 07 ... BI-relay 03 ... Bad9 ......... Lüfter
;  MO-relay 08 +
;  MO-relay 09 ... BI-relay 04 ... Vorzimmer9 ... Deckenlampen
;  MO-relay 10 +
;  MO-relay 11 ... BI-relay 05 ... WC9 .......... Deckenlampe
;  MO-relay 12 +
;  MO-relay 13 ... BI-relay 06 ...
;  MO-relay 14 +
;  MO-relay 15 ... BI-relay 07 ...
;  MO-relay 16 +
;  MO-relay 17 ... BI-relay 08 ... Esszimmer .... Kartentisch
;  MO-relay 18 + 
;  MO-relay 19 ... BI-relay 09 ... Esszimmer .... Esstisch
;  MO-relay 20 +
;  MO-relay 21 ... BI-relay 10 ... Esszimmer .... Bar
;  MO-relay 22 +
;  MO-relay 23 ... BI-relay 11 ... Esszimmer .... Lichtsäule
;  MO-relay 24 +
;  MO-relay 25 ... BI-relay 12 ...
;  MO-relay 26 +
;  MO-relay 27 ... BI-relay 13 ...
;  MO-relay 28 ...................
;  MO-relay 29 ...................
;  MO-relay 30 ................... Bad9 ......... Bodenheizung
;  MO-relay 31 ................... WC9 .......... Lüfter
;
;
def_task_table:
	DB	1,040H,3,4	; key 0/0 MAKE	toggles Vorzimmer9/Deckenlampe
        DB	1,041H,3,4	; key 0/1 MAKE	toggles Vorzimmer9/Deckenlampe
	DB	1,042H,3,5	; key 0/2 MAKE	toggles WC9/Deckenlampen
	DB	1,043H,3,5      ; key 0/3 MAKE	toggles WC9/Deckenlampen
	DB	1,044H,3,0	; key 0/4 MAKE	toggles Vorzimmer9/Spiegellampe
	DB	1,045H,c_act_seq,0	; key 0/5 MAKE	toggles Bad9/all
	DB	1,046H,3,4      ; key 0/6 MAKE	toggles Vorzimmer9/Deckenlampe
	DB	1,047H,3,4      ; key 0/7 MAKE	toggles Vorzimmer9/Deckenlampe

	DB	1,04AH,pev_mbon,0   ; key 1/2 MAKE  memorizes BI-relay 0 as ON
        DB	1,00AH,pev_mboff,0  ; key 1/2 BREAK memorizes BI-relay 0 as OFF
	DB	1,04BH,pev_mbon,1   ; key 1/2 MAKE  memorizes BI-relay 1 as ON
        DB	1,00BH,pev_mboff,1  ; key 1/2 BREAK memorizes BI-relay 1 as OFF
	DB	1,04CH,pev_mbon,2   ; key 1/2 MAKE  memorizes BI-relay 2 as ON
        DB	1,00CH,pev_mboff,2  ; key 1/2 BREAK memorizes BI-relay 2 as OFF
	DB	1,04DH,pev_mbon,3   ; key 1/2 MAKE  memorizes BI-relay 3 as ON
        DB	1,00DH,pev_mboff,3  ; key 1/2 BREAK memorizes BI-relay 3 as OFF

	DB	1,060H,pev_brtog,8     ; key 4/0 toggles Esszimmer/Kartentisch 
	DB	1,061H,pev_brtog,9     ; key 4/1 toggles Esszimmer/Esstisch    
	DB	1,062H,pev_brtog,10    ; key 4/2 toggles Esszimmer/Bar         
	DB	1,063H,pev_brtog,11    ; key 4/3 toggles Esszimmer/Lichtsäule  

	DB	1,064H,pev_mbon,08  ; key 4/4 MAKE  memorizes BI-relay 8  as ON
        DB	1,024H,pev_mboff,08 ; key 4/4 BREAK memorizes BI-relay 8  as OFF
	DB	1,065H,pev_mbon,09  ; key 4/5 MAKE  memorizes BI-relay 9  as ON
        DB	1,025H,pev_mboff,09 ; key 4/5 BREAK memorizes BI-relay 9  as OFF
	DB	1,066H,pev_mbon,10  ; key 4/6 MAKE  memorizes BI-relay 10 as ON
        DB	1,026H,pev_mboff,10 ; key 4/6 BREAK memorizes BI-relay 10 as OFF
	DB	1,067H,pev_mbon,11  ; key 4/7 MAKE  memorizes BI-relay 11 as ON
        DB	1,027H,pev_mboff,11 ; key 4/7 BREAK memorizes BI-relay 11 as OFF

	DB	1,068H,pev_brtog,8     ; key 5/0 toggles Esszimmer/Kartentisch 
	DB	1,069H,pev_brtog,9     ; key 5/1 toggles Esszimmer/Esstisch 
	DB	1,06AH,pev_brtog,10    ; key 5/2 toggles Esszimmer/Bar    
	DB	1,06BH,pev_brtog,11    ; key 5/3 toggles Esszimmer/Lichtsäule  
	DB	1,070H,pev_brtog,8     ; key 6/0 toggles Esszimmer/Kartentisch 
	DB	1,071H,pev_brtog,9     ; key 6/1 toggles Esszimmer/Esstisch    
	DB	1,072H,pev_brtog,10    ; key 6/2 toggles Esszimmer/Bar         
	DB	1,073H,pev_brtog,11    ; key 6/3 toggles Esszimmer/Lichtsäule  
	DB	1,078H,pev_brtog,8     ; key 7/0 toggles Esszimmer/Kartentisch 
	DB	1,079H,pev_brtog,9     ; key 7/1 toggles Esszimmer/Esstisch    
	DB	1,07AH,pev_brtog,10    ; key 7/2 toggles Esszimmer/Bar         
	DB	1,07BH,pev_brtog,11    ; key 7/3 toggles Esszimmer/Lichtsäule
        ;
	DB	c_evt_to,32,c_act_seq,1
	DB	c_evt_to,33,c_act_seq,2
	;
	; ---- END of TASKER control table
	;  
	DB	0,0,0,0
;
; ---------------------------------------------------------------
;
; FUNCTION:	CL_TASKER
; INPUT:	-
; OUTPUT:	-
; RESULT:	-
; CALLS:	CTSK_PGET - process GET (console character) input
;               SETQ      - select queue to be worked with
;               TSTQ      - test if selected queue has entries in it
;               REMQH     - get entry from head of queue
;               CTSK_GACT - get action for event
;               CTSK_DISP - dispatch action
; DESTROYS:	Register Bank 0
; ACTION:	CL_TASKER loops until a special console character
;               sequence terminates it.
;               While active, TASKER waits for an event on event queue 0
;               finds an appropriate action for it and performs this action
;
	USING	0
;
cl_tasker:
ctskloop:
	jnb	RI,ctsktq
	mov	a,SBUF
	clr	RI
	clr	acc.7		; reset MSB
	lcall	ctsk_pget
	jz	ctskloop	;=end not requested
	mov	r2,#083H
	sjmp	ctskret2
ctskret:
	mov	r2,#0
ctskret2:
	mov	r0,a
	mov	a,#09AH
	lcall	030H	; push code onto ARG stack
	ret		; return to BASIC
ctsktq:
	mov	a,#0
	lcall	setq
	lcall	tstq	; event in queue 0 ?
	jz	ctskloop
	lcall	remqh	; get event cell
	jz	ctskloop
	inc	dptr
	inc	dptr
	inc	dptr
	inc	dptr	; point to CELL.USE
	push	dpl
	push	dph
	inc	dptr	; point to CELL.CODE
	mov	r1,dpl
	mov	r3,dph
	mov	r0,#LOW(task_evt)
	mov	r2,#HIGH(task_evt)
	mov	r4,#(cell_datalen+3)
	lcall	ldir	; copy data from cell
	pop	dph
	pop	dpl
	clr	a
	movx	@dptr,a	; mark cell as unused again
	lcall	ctsk_gact	; get action for event
	mov	r2,#082H
	jz	ctskret2
	lcall	ctsk_disp	; dispatch the action
	mov	r2,#081H
	jnz	ctskret2	; ACC<>0 => return to basic
	ljmp	ctskloop
;
; ---------------------------------------------------------------
;
ctsk_pget:
	ret	
;
; ---------------------------------------------------------------
;
	USING	0
;
ctsk_gact:
	mov	dptr,#task_evt
	movx	a,@dptr
	mov	r3,a
	inc	dptr
	movx	a,@dptr
	mov	r1,a
	mov	a,#098H
	lcall	030H
	%B52_PUTCHAR(#'>')
	mov	dptr,#task_evt
	movx	a,@dptr
	mov	r2,a		; event
	inc	dptr
	movx	a,@dptr
	mov	r0,a		; subevent
	mov	dptr,#task_tblptr
	movx	a,@dptr
	mov	r1,a
	inc	dptr
	movx	a,@dptr
	mov	dph,a
	mov	dpl,r1		; point to task control table
cgac_loop:
	movx	a,@dptr
	mov	r4,a		; AENT.EVT
	inc	dptr
	movx	a,@dptr
	mov	r5,a		; AEN.SEV
	inc	dptr
	movx	a,@dptr
	mov	r6,a		; AEN.ACT
	inc	dptr		; point to <AEN>.SAC
	movx	a,@dptr
	mov	r7,a		; AEN.SAC
	inc	dptr		; point to <AEN.NEXT>.EVT
	mov	a,r4
	jz	cgac_done	; AEN.EVT=0 marks end of table
	cjne	a,ar2,cgac_loop	; event does not match => try next
	mov	a,r5
	cjne	a,ar0,cgac_loop	; subevent does not match => try next
cgac_done:
	mov	dptr,#task_act
	mov	a,r6
	movx	@dptr,a		; save ACTION
	inc	dptr
	mov	a,r7
	movx	@dptr,a		; save SUB-ACTION
	mov	a,r6		; return ACTION in ACC
	push	acc
	mov	r1,ar7
	mov	r3,ar6
	mov	a,#098H
	lcall	030H
	mov	a,#07H
	lcall	030H
	pop	acc
	ret
;
; ---------------------------------------------------------------
;
; FUNCTION:	CSTK_DISP
;
ctsk_disp:
	mov	dptr,#task_act
	movx	a,@dptr		; get action
	mov	r0,a		; save in R0
	inc	dptr
	movx	a,@dptr		; get sub-action
	mov	r2,a		; save in R2
	mov	a,r0
	anl	a,#0F0H		; select action class
	cjne	a,#c_act_puls,ctsd_npuls
; action class 0, send direct event to pulser queue 1
	mov	r4,#1
ctsd_send:
	lcall	alloc_cell	; get a cell	
	push	dpl
	push	dph
	inc	dptr		; ->NXT1
	inc	dptr		; ->PRV0
	inc	dptr		; ->PRV1
	inc	dptr		; ->USE
	inc	dptr		; ->CODE
	mov	a,r0
	movx	@dptr,a		; set event
	inc	dptr
	mov	a,r2
	movx	@dptr,a		; set subevent
	mov	a,r4
	lcall	setq
	pop	dph
	pop	dpl
	lcall	insqt		; set event to queue <R4>
ctsd_ret0:
	clr	a
	ret
ctsd_npuls:
	cjne	a,#c_act_seq,ctsd_nseq
;
; action class SEQ: perform sequence at vector <P1>
;
	lcall	sequencer
ctsd_ret1:
	clr	a
	ret
ctsd_nseq:
	clr	a
	ret
;
; ---------------------------------------------------------------
;
; FUNCTION:	SEND_PULSE
; INPUT:	R0   - event code
;               ACC  - parameter to event
; ACTION:	send event to pulser via queue 1
;
send_pulse:
	mov	r4,#1
send_r4:	
	mov	r2,a
	lcall	alloc_cell	; get a cell	
	push	dpl
	push	dph
	inc	dptr		; ->NXT1
	inc	dptr		; ->PRV0
	inc	dptr		; ->PRV1
	inc	dptr		; ->USE
	inc	dptr		; ->CODE
	mov	a,r0
	movx	@dptr,a		; set event
	inc	dptr
	mov	a,r2
	movx	@dptr,a		; set subevent
	mov	a,r4
	lcall	setq
	pop	dph
	pop	dpl
	lcall	insqt		; set event to queue <R4>
	ret
;
; ---------------------------------------------------------------
;
; FUNCTION:	init_cellpool
; ACTION:	initializes cell pool by marking all
;               cells in pool as unused (USE=0)
;
init_cellpool:
	mov	dptr,#cellpool+cell_use
	mov	r3,#cellpoolnum
incploop:
	clr	a
	movx	@dptr,a
	mov	a,dpl
	add	a,#cell_size
	mov	dpl,a
	mov	a,dph
	addc	a,#0
	mov	dph,a
	djnz	r3,incploop
	ret
;
; ---------------------------------------------------------------
;
init_queues:
	mov	dptr,#cellqueues
	mov	r3,#cque_num*cque_cbsize
icq_loop:
	clr	a
	movx	@dptr,a
	inc	dptr
	djnz	r3,icq_loop
	ret
;
; ---------------------------------------------------------------
;
; FUNCTION:	init_driver
;
init_driver:
	push	psw
	orl	psw,#00011000B	; select RB3
;
; place timer interrupt jump to location 0400BH
;
	mov	dptr,#0400BH
	mov	a,#002H		; 'LJMP' instruction
	movx	@dptr,a
	inc	dptr
	mov	a,#HIGH(timer_0_int)	; high address byte of timer interrupt procedure
	movx	@dptr,a
	inc	dptr
	mov	a,#LOW(timer_0_int)	; low address byte of timer interrupt procedure
	movx	@dptr,a
;
; initialize relay drivers and buffers
;
	mov	dptr,#relbase
	mov	r3,#reldrivnum
idloop1:
	clr	a
	movx	@dptr,a		; turn all relays off
	inc	dptr
	djnz	r3,idloop1
;
	mov	dptr,#reldrivbuf
	mov	r3,#reldrivnum
idloop2:
	clr	a
	movx	@dptr,a		; initialize port driver buffer
	inc	dptr
	djnz	r3,idloop2
;
	mov	dptr,#keydbuf
	mov	r3,#2*keyrownum
idloop3:
	clr	a
	movx	@dptr,a
	djnz	r3,idloop3
;
	mov	a,#0
	mov	dptr,#curkeynum
	movx	@dptr,a		; initialize key number
;
	mov	a,#01H
	mov	dptr,#keyscanbuf
	movx	@dptr,a		; initialize key scan buffer
	mov	dptr,#keydrive
	cpl	a		; convert to negative logic
	movx	@dptr,a		; initialize key scan driver
;
	lcall	init_cellpool
	lcall	init_queues
;
; initialize LED scanner phases
;
	mov	dptr,#ledphase
	clr	a
	movx	@dptr,a		; initialize phase to 0
	inc	dptr
	movx	@dptr,a		; initialize phase 0 to OFF,OFF
	inc	dptr
	mov	a,#00000001B
	movx	@dptr,a		; initialize phase 1 to OFF,ON
;
; initialize pulser
;
	mov	dptr,#puls_state
	clr	a
	movx	@dptr,a		; state = 0
	inc	dptr
	movx	@dptr,a		; P1 = 0
	inc	dptr
	movx	@dptr,a		; P2 = 0
	inc	dptr
	movx	@dptr,a		; counter = 0
	inc	dptr
	mov	a,#LOW(puls_cp)
	movx	@dptr,a
	inc	dptr
	mov	a,#HIGH(puls_cp)
	movx	@dptr,a		; set pulser control program to start
;
; initialize TASKER:
;
	mov	dptr,#task_tblptr
	mov	a,#LOW(def_task_table)
	movx	@dptr,a
	inc	dptr
	mov	a,#HIGH(def_task_table)
	movx	@dptr,a
;
; initialize the SEQUENCER
;
	lcall	init_sequencer
;
; initialize timeouts
;
	lcall	init_timeouts
;
; start the basic timer for first slice
;
	mov	dptr,#t0div
	clr	a
	movx	@dptr,a			; reset level 0 divider
;
	mov	a,tmod
	ANL	a,#11110001B 	; leave timer 1 alone and ...
	ORL     a,#00000001B 	; ... SET TIMER 0 MODE 1 16 BIT TIMER
	mov	tmod,a
	CLR     TR0             	; stop timer 0
	CLR     TF0             	; INIT TIMER 0
	MOV     TH0,#HIGH(TIMER_RELOAD) ;TIMER 0 IS system time base
	MOV     TL0,#LOW(TIMER_RELOAD)
	SETB    TR0                     ;START TIMER 0

	setb    et0             ; ENABLE INTERRUPT FOR TIMER 0
	setb    ea              ; .. and interrupts generally
	pop	psw
	ret
;
; ---------------------------------------------------------------
;
TIMER_0_INT:
	;
	; assume PSW already pushed
	;
        PUSH    ACC
        push    dph
        push    dpl
        CLR     TR0                     ;STOP TIMER
        CLR     TF0                     ;CLEAR EXPIRY FLAG

        MOV     TH0,#HIGH(TIMER_RELOAD)	; start basic timer again
        MOV     TL0,#LOW(TIMER_RELOAD)
        SETB    TR0                     ;START TIMER 0
timer0act:
	ORL	PSW,#00011000B		; select register bank 3
scanner:                        	; next task, scanner
        lcall   key_scanner
	lcall	pulser
	lcall	to_scanner
	mov	dptr,#t0div
	movx	a,@dptr
	inc	a
	movx	@dptr,a			; increment t0 divisor
	cjne	a,#t0divisor,noledscan	; have we reached divisor, => no
	clr	a
	movx	@dptr,a			; reset scan divider
	lcall	led_scanner
noledscan:
;
; end of scanner
;
EXIT_TIMER0:
        pop     dpl
        pop     dph
        POP     ACC             ; restore registers
        POP     PSW
        RETI                    ; return to application

;
; ---------------------------------------------------------------
;
; FUNCTION:	Alloc_Cell
; OUTPUT:	DPTR - address of free cell
; DESTROYS:	ACC, DPTR
; ACTION:	Finds the first free cell in cellpool (USE=0)
;
alloc_cell:
	push	IE
	clr	EA		; disable all interrupts
	mov	dptr,#cellpool+cell_use
allc_loop:
	movx	a,@dptr
	jz	allc_fnd
	mov	a,dpl
	add	a,#cell_size
	mov	dpl,a
	mov	a,dph
	addc	a,#0
	mov	dph,a
	jmp	allc_loop
allc_fnd:
	mov	a,dpl
	clr	C
	subb	a,#cell_use
	mov	dpl,a
	mov	a,dph
	subb	a,#0
	mov	dph,a
	push	dpl
	push	dph
	inc	dptr
	inc	dptr
	inc	dptr
	inc	dptr		; point to CELL.USE
	mov	a,#1
	movx	@dptr,a		; mark cell as used
	pop	dph
	pop	dpl
	pop	IE		; restore interrupt state
	ret
;
; ---------------------------------------------------------------
;
; FUNCTION:	Free_Cell
; INPUT:	DPTR - points to cell to be freed
; ACTION:	return cell (DPTR) again to free cell pool
;
free_cell:
	inc	dptr
	inc	dptr
	inc	dptr
	inc	dptr
	clr	a
	movx	@dptr,a
	ret
;
; ---------------------------------------------------------------
;
; FUNCTION:	INSQT
; DESTROYS:	ACC,R0,R2,R4,R6,DPTR
; ACTION:	incert cell <DPTR> at tail of queue <R3,R1>
;
insqt:
	push	IE	; save interrupt state
	clr	EA	; disable all interrupts
	mov	r0,dpl
	mov	r2,dph	; save ptr to new cell in R2/0
	lcall	tstq	; has queue any entries ?
	jz	insqtf	;=yes, special for inserting only cell
	mov	dpl,r1
	mov	dph,r3	; point to QCB.HEAD
	inc	dptr
	inc	dptr	; point to QCB.TAIL
	movx	a,@dptr
	mov	r4,a
	inc	dptr
	movx	a,@dptr
	mov	r6,a	; get ptr to old tail element to R6/4
	mov 	dpl,r0
	mov	dph,r2	; point to new cell again
	mov	a,r4
	movx	@dptr,a
	inc	dptr
	mov	a,r6
	movx	@dptr,a	; let CELL.NEXT point to old tail cell
	inc	dptr
	clr	a
	movx	@dptr,a
	inc	dptr
	movx	@dptr,a	; let CELL.PREV be NULL
	mov	dpl,r4
	mov	dph,r6	; point to old tail element
	inc	dptr
	inc	dptr	; point to OLDTAIL.PREV
	mov	a,r0
	movx	@dptr,a
	inc	dptr
	mov	a,r2
	movx	@dptr,a	; let OLDTAIL.PREV point to new CELL
	mov	dpl,r1
	mov	dph,r3
	inc	dptr
	inc	dptr	; point to QCB.TAIL
	mov	a,r0
	movx	@dptr,a
	inc	dptr
	mov	a,r2
	movx	@dptr,a	; let QCB.TAIL point to new CELL
insqte:
	pop	IE	; restore interrupt state
	ret
insqtf:			; insert CELL as first and only element
	mov	dpl,r0
	mov	dph,r2
	clr	a
	movx	a,@dptr
	inc	dptr
	movx	a,@dptr
	inc	dptr
	movx	a,@dptr
	inc	dptr
	movx	a,@dptr	; set CELL.NEXT and CELL.PREV to NULL
	mov	dpl,r1
	mov	dph,r3	; point to QCB.HEAD
	mov	a,r0
	movx	@dptr,a
	inc	dptr
	mov	a,r2
	movx	@dptr,a	; let QCB.HEAD point to new CELL
	inc	dptr
	mov	a,r0
	movx	@dptr,a
	inc	dptr
	mov	a,r2
	movx	@dptr,a	; let QCB.TAIL point to new CELL
	sjmp	insqte
	
;
; ---------------------------------------------------------------
;
; FUNCTION:	REMQH
; ACTION:	remove cell from queue <R3,R1> to <DPTR>
;
remqh:
	push	IE	; save interrupt state
	clr	EA	; disable all interrupts
	lcall	tstq	; has queue any entries ?
	jz	remqhf	;=no, nothing to remove
	mov	dpl,r1
	mov	dph,r3	; point to QCB.HEAD
	movx	a,@dptr
	mov	r0,a
	inc	dptr
	movx	a,@dptr
	mov	r2,a	; get ptr to removed head element to R2/0
	mov 	dpl,r0
	mov	dph,r2	; point to cell to be removed
	inc	dptr
	inc	dptr
	movx	a,@dptr
	mov	r4,a
	inc	dptr
	movx	a,@dptr
	mov	r6,a	; get CELL.PREV to R6/4
	orl	a,r4
	jz	remqho	;=there was no previous cell
	mov	dpl,r4
	mov	dph,r6	; point to new head cell
	clr	a
	movx	@dptr,a
	inc	dptr
	movx	@dptr,a	; let NEWHEAD.NEXT be NULL
	mov	dpl,r1
	mov	dph,r3	; point to QCB.HEAD
	mov	a,r4
	movx	@dptr,a
	inc	dptr
	mov	a,r6
	movx	@dptr,a	; let QCB.HEAD point to new head cell
remqhc:
	mov	dpl,r0
	mov	dph,r2	; point to CELL again
	clr	a
	movx	@dptr,a
	inc	dptr
	movx	@dptr,a
	inc	dptr
	movx	@dptr,a
	inc	dptr
	movx	@dptr,a	; unlink cell from queue
	mov	dpl,r0
	mov	dph,r2	; return DPTR pointing to removed cell
remqhe:
	mov	a,dpl
	orl	a,dph
	pop	IE	; restore interrupt state
	ret
remqhf:			; nothing to remove, return NULL pointer
	mov	dpl,#0
	mov	dph,#0
	sjmp	remqhe
remqho:			; we removed the only cell, clear QCB
	mov	dpl,r1
	mov	dph,r3
	clr	a
	movx	@dptr,a
	inc	dptr
	movx	@dptr,a
	inc	dptr
	movx	@dptr,a
	inc	dptr
	movx	@dptr,a
	sjmp	remqhc
;
; ---------------------------------------------------------------
;
; FUNCTION:	SETQ
; DESTROYS:	ACC,R1,R3
; ACTION:	sets (R3,R1) to point to queue number (ACC)
;
setq:
	clr	c
	rlc	a
	rlc	a
	add	a,#LOW(cellqueues)
	mov	r1,a
	mov	a,#HIGH(cellqueues)
	addc	a,#0
	mov	r3,a
	ret
;
; ---------------------------------------------------------------
;
; FUNCTION:	TSTQ
; INPUT:	R3,R1 - (H,L) pointer to queue control block
; OUTPUT:	A - not zero if there are entries in the queue
; DESTROYS:	R7,DPTR
; ACTION:	test if queue <R3,R1> contains entries
;
tstq:
	mov	dpl,r1
	mov	dph,r3
	movx	a,@dptr
	mov	r7,a
	inc	dptr
	movx	a,@dptr
	orl	a,r7
	ret
;
; ---------------------------------------------------------------
;
; FUNCTION:	KEY_SCANNER
;
	USING	3
;
key_scanner:
	mov	dptr,#keyreturn
	movx	a,@dptr		; get scan return data
	cpl	a		; convert to positive logic
	mov	r0,a		; save in R0
	mov	dptr,#curkeynum
	movx	a,@dptr
	mov	r1,a		; save scanned row number in R1
	inc	a
	cjne	a,#keyrownum,kesc_nowrap1
	clr	a		; reset to first row
kesc_nowrap1:
	movx	@dptr,a		; save next scanned row number
	inc	dptr		; point to scan image buffer
	movx	a,@dptr		; get last image
	clr	c
	rlc	a		; move to next scan line
	jnz	kesc_nowrap2
	mov	a,#1		; reset to first scan line
kesc_nowrap2:
	movx	@dptr,a		; save scan pattern
	mov	dptr,#keydrive
	cpl	a		; convert to negative logic
	movx	@dptr,a		; issue new scan pattern
;
	mov	a,#LOW(keydbuf)
	add	a,r1		; index by key scan row
	mov	dpl,a
	mov	a,#HIGH(keydbuf)
	addc	a,#0
	mov	dph,a		; point to current row's debounce buffer
	movx	a,@dptr
	mov	r2,a		; save old state in R2
	anl	a,r0		; debounce
	mov	r3,a		; save debounced in R3
	mov	a,r0
	movx	@dptr,a		; save sampled value
;
	mov	a,#LOW(keycbuf)
	add	a,r1
	mov	dpl,a
	mov	a,#HIGH(keycbuf)
	addc	a,#0
	mov	dph,a
	movx	a,@dptr		; get last value to compare
	mov	r4,a		; save last value in R4
	xrl	a,r3
	mov	r5,a		; one bit set for every changed key in this row
	jz	kesc_end
kesc_diff:			; there are changed keys, new in R3
	mov	a,r3		; get new state
	movx	@dptr,a		; save as new "old" value to compare next time
	mov	r6,#0		; bit counter
kesc_dloop:
	mov	a,r3
	rrc	a		; new state to C
	mov	r3,a
	mov	F0,c		; save in F0
	mov	a,r5
	rrc	a		; change flag to C
	mov	r5,a
	jnc	kesc_dnext	;=no change, skip to next column
	push	ar1
	push	ar2
	push	ar3
	push	ar4
	push	ar5
	push	ar6 
	clr	a
	mov	C,F0		; new key state ...
	mov	acc.6,C		; ... to ACC.6
	orl	a,r6		; column to ACC.2..ACC.0
	mov	r4,a
	mov	a,r1
	rl	a
	rl	a
	rl	a		
	orl	a,r4		; row to ACC.5..ACC.3
	push	acc		; save on stack
	lcall	alloc_cell	; allocate a message cell
	pop	acc
	push	dpl
	push	dph
	push	acc
	inc	dptr
	inc	dptr
	inc	dptr
	inc	dptr		; point to CELL.USE
	mov	a,#080H
	movx	@dptr,a		; USE=128 tells KEY message
	inc	dptr		; point to CELL.CODE
	mov	a,#1
	movx	@dptr,a		; msg code 1 = KEY
	inc	dptr
	pop	acc		; restore calculated key code
	movx	@dptr,a		; key action code to CELL_CODE2
	mov	a,#0
	lcall	setq		; set queue 0
	pop	dph
	pop	dpl
	lcall	insqt		; enqueue KEY msg. to queue 0
	pop	ar6
	pop	ar5
	pop	ar4
	pop	ar3
	pop	ar2
	pop	ar1
kesc_dnext:
	inc	r6
	cjne	r6,#8,kesc_dloop
kesc_end:
	ret
;
; ---------------------------------------------------------------
;
; FUNCTION:	PULSER
;
pulser:
	mov	dptr,#puls_state
	movx	a,@dptr
	cjne	a,#psta_wevt,puls_nwevt
;
; pulser state WEVT, wait for event from queue 1
;
	mov	a,#1
	lcall	setq
	lcall	tstq
	jz	puls_end	;=no event pending
	lcall	remqh		; get the event
	inc	dptr
	inc	dptr
	inc	dptr
	inc	dptr		; point to CELL.USE
	clr	a
	movx	@dptr,a		; mark cell as unused
	inc	dptr		; point to CELL.CODE1
	movx	a,@dptr		; get event code
	mov	r0,a
	inc	dptr		; point to CELL.CODE2
	movx	a,@dptr		; get subevent code
	mov	r2,a
	inc	dptr		; point to CODE3
	inc	dptr		; point to CELL.DATA
	mov	r1,dpl
	mov	r3,dph		; save pointer to CELL.DATA
	mov	dptr,#puls_p1	; point to PULS.P1
	mov	a,r0
	movx	@dptr,a		; save event in <P1>
	inc	dptr		; point to PULS.P2
	mov	a,r2
	movx	@dptr,a	        ; save subevent in <P2>
	inc	dptr		; point to PULS.DCNT
	inc	dptr		; point to PULS.CPTR
	inc	dptr
	inc	dptr		; point to PULS.EVTDAT
	mov	r5,dpl
	mov	r7,dph		; save pointer to PULS.EVDAT(0) in R5/R7
	mov	r2,#cell_datalen
puls_wevtl:
	mov	dpl,r1
	mov	dph,r3		; point to CELL.DATA(n)
	movx	a,@dptr
	inc	dptr
	mov	r1,dpl
	mov	r3,dph
	mov	dpl,r5
	mov	dph,r7		; point to PULS.EVTDAT(n)
	movx	@dptr,a
	inc	dptr
	mov	r5,dpl
	mov	r7,dph
	djnz	r2,puls_wevtl	; repeat for all data bytes
	lcall	puls_nxtc
	sjmp	puls_end
puls_nwevt:
	cjne	a,#psta_delay,puls_ndelay
;
; pulser state DELAY, wait until delay counter expired 
;
	mov	dptr,#puls_dcnt
	movx	a,@dptr
	dec	a
	movx	@dptr,a
	jnz	puls_s1end
	lcall	puls_nxtc
puls_s1end:
	sjmp	puls_end
puls_ndelay:
puls_end:
	ret
;
; the PULSER control program
;
puls_cp:
	%P_C1BE(pev_bron,puls_cpbo)
	%P_C1BE(pev_broff,puls_cpbf)
	%P_C1BE(pev_brtog,puls_cpbt)
	%P_C1BE(pev_mron,puls_cpmo)
	%P_C1BE(pev_mroff,puls_cpmf)
	%P_C1BE(pev_mrtog,puls_cpmt)
	%P_C1BE(pev_mbon,puls_cmbon)
	%P_C1BE(pev_mboff,puls_cmboff)
	%P_C1BE(pev_mmon,puls_cmmon)
	%P_C1BE(pev_mmoff,puls_cmmoff)
puls_cpwait:
	%P_WEVT
	%P_BRA(puls_cp)
puls_cpbo:
	%P_MBRO
	%P_CBRO
puls_sbr:
	%P_PLON2
	%P_DELAY(100)
	%P_PLOFF2
	%P_BRA(puls_fdel)
puls_cpbf:
	%P_MBRF
	%P_CBRF
	%P_BRA(puls_sbr)
puls_cpbt:
	%P_CBTL
	%P_BRA(puls_sbr)
puls_cpmo:
	%P_PLON2		; ON     MO-relay <P2>
puls_fdel:
	%P_DELAY(100)
	%P_BRA(puls_cpwait)
puls_cpmf:			; OFF    MO-relay <P2>
	%P_PLOFF2
	%P_BRA(puls_fdel)
puls_cpmt:			; TOGgle MO-relay <P2>
	%P_MTOG
	%P_BRA(puls_fdel)
puls_cmbon:
	%P_MBRO
	%P_BRA(puls_cpwait)
puls_cmboff:
	%P_MBRF
	%P_BRA(puls_cpwait)
puls_cmmon:
	%P_MMRO
	%P_BRA(puls_cpwait)
puls_cmmoff:
	%P_MMRF
	%P_BRA(puls_cpwait)
;
; ------ the PULSER program processor
;
puls_nxtc:
	lcall	puls_cpgb
	cjne	a,#0,pnx_n0
; pulser command 0: end of sequence
	ret
pnx_n0:
	cjne	a,#pc_c1be,pnx_nc1be
; pulser command C1BE: compare P1 to <PB1>, if equal, branch to <PW2>
	lcall	puls_cpg3
	mov	dptr,#puls_p1
	movx	a,@dptr
	cjne	a,ar1,puls_nxtc	;=not equal, skip to next command
	ljmp	pnx_dbra        ;=equal branch to <PW1>
pnx_nc1be:
	cjne	a,#pc_sdc,pnx_nsdc
; pulser command SDC: set delay counter to <PB1>
	lcall	puls_cpgb
	mov	dptr,#puls_dcnt
	movx	@dptr,a
	ljmp	puls_nxtc
pnx_nsdc:
	cjne	a,#pc_nst,pnx_nnst
; pulser command NST: set state to <PB1>
	lcall	puls_cpgb
	mov	dptr,#puls_state
	movx	@dptr,a
	ljmp	puls_nxtc
pnx_nnst:
	cjne	a,#pc_delay,pnx_ndelay
; pulser command DELAY: wait for <PB1>*10 milliseconds
	lcall	puls_cpgb
	mov	dptr,#puls_state	
	mov	a,#psta_delay
	movx	@dptr,a		; state is DELAY
	inc	dptr
	inc	dptr
	inc	dptr		; point to delay counter
	mov	a,r1
	movx	@dptr,a
	ret			; let pulser wait for delay complete
	ljmp	puls_nxtc
pnx_ndelay:
	cjne	a,#pc_wevt,pnx_nwevt
; pulser command WEVT: wait for event in queue 1
	mov	dptr,#puls_state
	mov	a,#psta_wevt
	movx	@dptr,a		; state is WEVT
	ret			; let pulser wait for event
pnx_nwevt:
	cjne	a,#pc_plon,pnx_nplon
; pulser command PLON: switch output line <P1> to ON
	mov	dptr,#puls_p1
	movx	a,@dptr		; get line number from <P2>
	lcall	plon		; switch it ON
	ljmp	puls_nxtc
pnx_nplon:
	cjne	a,#pc_ploff,pnx_nploff
; pulser command PLOFF: switch output line <P1> to OFF
	mov	dptr,#puls_p1
	movx	a,@dptr		; get line number from <P2>
	lcall	ploff		; switch it OFF
	ljmp	puls_nxtc
pnx_nploff:
	cjne	a,#pc_bra,pnx_nbra
; pulser command BRA, branch unconditional to <PW1>
	lcall	puls_cpgw
pnx_dbra:
	mov	dpl,r0
	mov	dph,r2
	lcall	puls_sccp
	ljmp	puls_nxtc
pnx_nbra:
	cjne	a,#pc_cbro,pnx_ncbro
; pulser command CBRO, calc BI-relay ON line number from <P2> to <P2>
	mov	dptr,#puls_p2
	movx	a,@dptr
	anl	a,#0FH	; limit to 16 BI-relay
	clr	c
	rlc	a
	mov	dptr,#puls_p2
	movx	@dptr,a
	ljmp	puls_nxtc
pnx_ncbro:
	cjne	a,#pc_cbrf,pnx_ncbrf
; pulser command CBRF, calc. BI-relay OFF line number from <P2> to <P2>
	mov	dptr,#puls_p2
	movx	a,@dptr
	anl	a,#0FH	; limit to 16 BI-relay
	setb	c
	rlc	a
	mov	dptr,#puls_p2
	movx	@dptr,a
	ljmp	puls_nxtc
pnx_ncbrf:
	cjne	a,#pc_mbro,pnx_nmbro
; pulser command MBRO, memorize BI-relay <P2> as ON
	lcall	cbra
	mov	a,#0FFH
	movx	@dptr,a
	ljmp	puls_nxtc
pnx_nmbro:
	cjne	a,#pc_mbrf,pnx_nmbrf
; pulser command MBRF, memorize BI-relay <P2> as OFF
	lcall	cbra
	mov	a,#000H
 	movx	@dptr,a
	ljmp	puls_nxtc
pnx_nmbrf:
	cjne	a,#pc_mmro,pnx_nmmro
; pulser command MMRO, memorize MO-relay <P2> as ON
	lcall	cmra
	mov	a,#0FFH
 	movx	@dptr,a
	ljmp	puls_nxtc
pnx_nmmro:
	cjne	a,#pc_mmrf,pnx_nmmrf
; pulser command MBRF, memorize MO-relay <P2> as OFF
	lcall	cmra
	mov	a,#000H
 	movx	@dptr,a
	ljmp	puls_nxtc
pnx_nmmrf:
	cjne	a,#pc_cbtl,pnx_ncbtl
; pulser command CBTL, calc BI-relay <P2> toggle line to <P2>
	mov	dptr,#puls_p2
	movx	a,@dptr
	anl	a,#0FH	; limit to 16 BI-relays
	mov	r3,a
	lcall	cbra
	movx	a,@dptr	; get current state
	cpl	a	; reverse
	movx	@dptr,a	; save new state
	rrc	a	; new state flag to C
	mov	a,r3
	rlc	a	; calculate line
	mov	dptr,#puls_p2
	movx	@dptr,a	; save in P2
	ljmp	puls_nxtc 
pnx_ncbtl:
	cjne	a,#pc_plon2,pnx_nplon2
; pulser command PLON2: switch output line <P2> to ON
	mov	dptr,#puls_p2
	movx	a,@dptr		; get line number from <P2>
	lcall	plon		; switch it ON
	ljmp	puls_nxtc
pnx_nplon2:
	cjne	a,#pc_ploff2,pnx_nploff2
; pulser command PLOFF2: switch output line <P2> to OFF
	mov	dptr,#puls_p2
	movx	a,@dptr		; get line number from <P2>
	lcall	ploff		; switch it OFF
	ljmp	puls_nxtc
pnx_nploff2:
	cjne	a,#pc_mtog,pnx_nmtog
; pulser command MTOG, toggle MO-relay <P2>
	mov	dptr,#puls_p2
	movx	a,@dptr		; MO-relay number
	mov	r5,a
	lcall	cmra		; point to this relay's storage
	movx	a,@dptr
	cpl	a		; toggle state
	movx	@dptr,a		; save
	jnz	pnx_mtogon
	mov	a,r5
	lcall	ploff
	ljmp	puls_nxtc
pnx_mtogon:
	mov	a,r5
	lcall	plon
	ljmp	puls_nxtc
pnx_nmtog:
	ljmp	puls_nxtc
	
;
; ------ calculate address of BI-relay state storage <ACC>
;
cbra:
	anl	a,#0FH		; limit to 16 BI-relays
	add	a,#low(bi_rel_buff)
	mov	dpl,a
	mov	a,#high(bi_rel_buff)
	addc	a,#0
	mov	dph,a
	ret
;
; ------ calculate address of MO-relay state storage <ACC>
;
cmra:
	anl	a,#1FH		; limit to 32 MO-relays
	add	a,#low(mo_rel_buff)
	mov	dpl,a
	mov	a,#high(mo_rel_buff)
	addc	a,#0
	mov	dph,a
	ret
;
; ------ bit-mask-table
;
c_tab_bmask:
	DB	00000001B
	DB	00000010B
	DB	00000100B
	DB	00001000B
	DB	00010000B
	DB	00100000B
	DB	01000000B
	DB	10000000B
;
; ------ calc bitmask from ACC to ACC
;
cbmask:
	anl	a,#07H
	mov	dptr,#c_tab_bmask
	movc	a,@a+dptr
	ret
;
; ------ calc driver address and bit mask for output line
;
; OUTPUT:	DPTR  - output buffer address
;		R0,R2 - output driver address
;               R4,R6 - address of MO-relay state
;		ACC   - bit mask 
;
codp:
	mov	r1,a
	rr	a
	rr	a
	rr	a
	anl	a,#03h
	mov	r3,a	; save index in R3
	add	a,#low(relbase)
	mov	r0,a
	mov	a,#high(relbase)
	addc	a,#0
	mov	r2,a
	mov	a,r1
	lcall	cbmask
	mov	r1,a	; save mask in R1
	mov	a,r3
	add	a,#low(reldrivbuf)
	mov	dpl,a
	mov	a,#high(reldrivbuf)
	addc	a,#0
	mov	dph,a
	mov	a,r1
	ret
;
; ------ set output line <ACC> to ON
;
plon:
	mov	r3,a
	lcall	cmra
	mov	a,#0FFH
	movx	@dptr,a
	mov	a,r3
	lcall	codp
	movx	a,@dptr
	orl	a,r1
	movx	@dptr,a
plset:
	mov	dpl,r0
	mov	dph,r2
	movx	@dptr,a
	ret
;
; ------ set output line <ACC> to OFF
;
ploff:
	mov	r3,a
	lcall	cmra
	mov	a,#00H
	movx	@dptr,a
	mov	a,r3
	lcall	codp
	cpl	a
	mov	r1,a
	movx	a,@dptr
	anl	a,r1
	movx	@dptr,a
	ljmp	plset
	
;
; ------ load pulser control program counter to DPTR
;
puls_lcpp:
	mov	dptr,#puls_cptr
	movx	a,@dptr
	mov	r7,a
	inc	dptr
	movx	a,@dptr
	mov	dph,a
	mov	dpl,r7
	ret
;
; ------ save pulser control program counter from DPTR
;
puls_sccp:
	mov	r7,dph
	mov	a,dpl
	mov	dptr,#puls_cptr
	movx	@dptr,a
	inc	dptr
	mov	a,r7
	movx	@dptr,a
	ret
;
; ------ get byte from pulser control program to ACC
;
puls_cpgb:
	lcall	puls_lcpp
	movx	a,@dptr
	inc	dptr
	mov	r1,a
	lcall	puls_sccp
	mov	a,r1
	ret
;
; ------ get word from pulser control program to R0,R2
;
puls_cpgw:
	lcall	puls_lcpp
	movx	a,@dptr
	mov	r2,a
	inc	dptr
	movx	a,@dptr
	mov	r0,a
	inc	dptr
	lcall	puls_sccp
	ret
;
; ------ get 1 byte to R1 and one word to R0,R2 from pulser control program
;
puls_cpg3:
	lcall	puls_lcpp
	movx	a,@dptr
	inc	dptr
	mov	r1,a	
	movx	a,@dptr
	inc	dptr
	mov	r2,a
	movx	a,@dptr
	inc	dptr
	mov	r0,a
	lcall	puls_sccp
	ret	
;
;
; ---------------------------------------------------------------
;
init_timeouts:
	mov	r5,#c_no_timeouts
	mov	dptr,#timeouts
itol1:
	clr	a
	movx	@dptr,a
	inc	dptr
	djnz	ar5,itol1
	mov	r5,#c_to_classes
	mov	dptr,#to_divisors
itol2:
	mov	a,#c_to_divisor
	movx	@dptr,a
	inc	dptr
	djnz	ar5,itol2
	ret	
;
; ---------------------------------------------------------------
;
; FUNCTION:	START_TO
; INPUT:	R6 - number of ticks
;               R7 - timeout number
;
START_TO:
	mov	a,#LOW(timeouts)
	add	a,r7
	mov	dpl,a
	mov	a,#HIGH(timeouts)
	addc	a,#0
	mov	dph,a
	mov	a,r6
	movx	@dptr,a
	ret
;
; ---------------------------------------------------------------
;
; FUNCTION:	STOP_TO
; INPUT:	R7 - timeout number
; ACTION:	stops timeout number <R7>
;
STOP_TO:
	mov	r6,#0
	ljmp	start_to
;
; ---------------------------------------------------------------
;
; FUNCTION:	TO_SCANNER
; ACTION:	the time-out scanner is called by the basic tick
;               timer interrupt
;
	USING	3
;
to_scanner:
	%LDIX(to_divisors,R1,R3)
	%LDIX(timeouts,R0,R2)
	mov	r4,#c_to_classes
	mov	r5,#0		; count absolute timeout number in R5
tosc_cloop:
	%MDPTR(r1,r3)		; point to class divisor
	movx	a,@dptr
	dec	a
	movx	@dptr,a
	jnz	tosc_end	; not done
	mov	a,#c_to_divisor
	movx	@dptr,a		; reset this class
	inc	dptr		; move on to next class divisor
	%PDPTR(r1,r3)		; save class pointer
;
; class tick done, check all timeouts in this class
;
	mov	r6,#c_to_slots	; walk thru all slots
tosc_sloop:
	%MDPTR(r0,r2)
	movx	a,@dptr
	jz	tosc_snext	;=timeout not running
	dec	a
	movx	@dptr,a		; update slot counter
	jnz	tosc_snext	;=timeout not reached
;
; timeout is expired, enqueue timeout message
;
	push 	ar0
	push 	ar1
	push	ar2
	push	ar3
	push	ar4
	push	ar5
	push	ar6
	lcall	alloc_cell	; allocate a message cell
	push	dpl		; save cell pointer on stack
	push	dph
	inc	dptr
	inc	dptr
	inc	dptr
	inc	dptr		; point to CELL.USE
	mov	a,#c_use_to
	movx	@dptr,a		; USE tells TIMEOUT message
	inc	dptr		; point to CELL.CODE
	mov	a,#c_evt_to
	movx	@dptr,a		; msg code tells TIMEOUT
	inc	dptr
	mov	a,r5		; get time slot number
	movx	@dptr,a		; timeout code to CELL_CODE2
	mov	a,#0
	lcall	setq		; set queue 0
	pop	dph		; restore cell pointer
	pop	dpl
	lcall	insqt		; enqueue TIMEOUT msg. to queue 0
	pop	ar6
	pop	ar5
	pop	ar4
	pop	ar3
	pop	ar2
	pop	ar1
	pop	ar0
tosc_snext:
	inc	r5		; increment slot number
	%MDPTR(r0,r2)		; move on to next slot
	inc	dptr
	%PDPTR(r0,r2)
	djnz	ar6,tosc_sloop	; continue with next slot
tosc_cnext:
	djnz	ar4,tosc_cloop	; continue with next class
tosc_end:
	ret
;
; ---------------------------------------------------------------
;
; FUNCTION:	LED_SCANNER
;
led_scanner:
	mov	dptr,#ledphase
	movx	a,@dptr
	inc	a
	anl	a,#1
	movx	@dptr,a
	inc	dptr		; point to phase 0
	jz	lscn_phase	;=phase 0
	inc	dptr		; point to phase 1
lscn_phase:
	movx	a,@dptr		; get LED pattern for this phase
	cpl	a		; invert for 0-active
	anl	a,#03H
	mov	r0,a
	mov	a,p1
	anl	a,#0FCH		; clear LED bits, let rest alone
	orl	a,r0		; merge LED bits into pattern
	mov	p1,a
	ret
;
; ---------------------------------------------------------------
;            The SEQUENCER
; ---------------------------------------------------------------
;
	USING	0
;
; ---- the default sequencer vector table
;
def_seq_vec:
	DW	seq_tog_bad9
	DW	seq_tb9_to32
	DW	seq_tb9_to33
	DW	0			; spare room for RAM mode sequence vectors
	DW	0
	DW	0
	DW	0
	DW	0
	DW	0
	DW	0
	DW	0
	DW	0
	DW	0
	DW	0
	DW	0
	DW	0
;
; ---- the default sequencer control program
;
def_seq_cp:
;
seq_tog_bad9:		; SEQUENCE TOG_BAD9: toggle all light and fan in Bad-9
	%SEQ_CGEQU(1,1,st_b9_ison)
        ;                           0: everything is off
	%SEQ_TOSTART(32,30)       
	%SEQ_PULS(pev_bron,1)
	%SEQ_PULS(pev_bron,2)
	%SEQ_PULS(pev_broff,3)
	%SEQ_SETVAR(1,1)
	%SEQ_END
st_b9_ison:
        ;                           >0: something is on
        %SEQ_TOSTOP(32)
	%SEQ_PULS(pev_broff,1)
	%SEQ_PULS(pev_broff,2)
	%SEQ_CGEQU(1,2,st_b9_fanon)
        ;                           1: only light was on
	%SEQ_SETVAR(1,0)
	%SEQ_END
st_b9_fanon:                     ;  >1: fan is still on
	%SEQ_CGEQU(1,3,st_b9_onlyfan)
	%SEQ_TOSTART(33,60)
	%SEQ_SETVAR(1,3)
	%SEQ_END
st_b9_onlyfan:
	%SEQ_TOSTOP(33)
	%SEQ_PULS(pev_bron,1)
	%SEQ_PULS(pev_bron,2)
	%SEQ_SETVAR(1,2)
	%SEQ_END
;
seq_tb9_to32:                    ; 5 minutes passed, turn fan on
	%SEQ_PULS(pev_bron,3)
	%SEQ_SETVAR(1,2)
	%SEQ_END
;
seq_tb9_to33:                    ; 10 minutes passed, turn fan off
	%SEQ_PULS(pev_broff,3)
	%SEQ_SETVAR(1,0)
	%SEQ_END
;
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	; spare room for RAM mode sequences
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
;
; ---------------------------------------------------------------
;
init_sequencer:
	mov	dptr,#seq_vec_ptr
	mov	a,#HIGH(def_seq_vec)
	movx	@dptr,a
	inc	dptr
	mov	a,#LOW(def_seq_vec)
	movx	@dptr,a
	ret
;
; ---------------------------------------------------------------
;
; FUNCTION:	SEQUENCER
; INPUT:	R2 - number of sequence vector to start with
;
;
; auxiliary entry SEQ_VR2 uses VARiable array as entry vector
; 
seq_vr2:
	mov	r3,#HIGH(seq_nvars)
	mov	r1,#LOW(seq_nvars)
	sjmp	seq_cvec
;
; main entry SEQUENCER uses vector pointed to by SEQ_VEC_PTR
;
sequencer:
	mov	dptr,#seq_vec_ptr
	movx	a,@dptr
	mov	r3,a
	inc	dptr
	movx	a,@dptr
	mov	r1,a
;
; R3/R1 ... pointer H/L to a vector of sequence entries
; R2 ...... index into vector table
;
seq_cvec:
	mov	a,r2
	clr	c
	rlc	a
	add	a,r1
	mov	dpl,a
	mov	a,r3
	addc	a,#0
	mov	dph,a	; point to vector
	movx	a,@dptr
	mov	r2,a
	inc	dptr
	movx	a,@dptr
	mov	r0,a	; get address of instruction
	%SEQU_PUTIP(R0,R2)
seq_cont:
	lcall	seq_getib	; get instruction byte
	clr	c
	rlc	a
	mov	dptr,#seq_xvec
	jmp	@a+dptr
;
; SEQ_END
;
seqxendtxt:
	db	'S','-','E','N','D',13,'"'
seq_x_end:
	%B52_PUTTXTC(seqxendtxt)
	%B52_PUTCRLF
	ret
;
; SEQ_PULS,<evt>,<par>
;
seq_x_puls:
	lcall	seq_getiw	; EVT to R7, PAR to R5
	mov	a,r7
	mov	r0,a		; EVT to R0
	mov	a,r5		; PAR to ACC
  	lcall	send_pulse
	ljmp	seq_cont
;
; SEQ_TOSTART,<num>,<ticks>
;
seq_x_tostart:
	lcall	seq_getiw	; NUM to R7, TICKS to R5
	mov	a,r5
	mov	r6,a		; TICKS to R6
	lcall	start_to
	ljmp	seq_cont
;
; SEQ_TOSTOP,<num>
;
seq_x_tostop:
	lcall	seq_getib
	mov	r7,a
	lcall	stop_to
	ljmp	seq_cont
;
; SEQ_LJMP,<high_target>,<low_target>
;
seq_x_ljmp:
	lcall	seq_getiw
seq_x_j57:
	%SEQU_PUTIP(R5,R7)
	ljmp	seq_cont
;
; SEQ_SETVAR,<varnum>,<varval>
;
seq_x_setvar:
	lcall	seq_geti2	; VARNUM to R6, VARVAL to R4
	mov	a,r6
	lcall	seq_cva
	mov	a,r4
	movx	@dptr,a
	ljmp	seq_cont
;
; SEQ_CGEQU,<varnum>,<cmpval>,<W_target>
;
seq_x_cgequ:
	lcall	seq_geti2	; VARNUM to R6, CMPVAL to R4
	lcall	seq_getiw	; TARGET to R7/R5
	mov	a,r6
	lcall	seq_cva
	movx	a,@dptr
	clr	c
	subb	a,r4
	jc	seqxcgedont	; if <VAR> less than CMPVAL, then do not branch
	ljmp	seq_x_j57
seqxcgedont:
	ljmp	seq_cont
;
; SEQ_DJNZ,<varnum>,<W_target>
;
seq_x_djnz:
	lcall	seq_getib	; VARNUM to ACC
	mov	r6,a		; VARNUM to  R6
	lcall	seq_getiw	; TARGET to R7/R5
	mov	a,r6
	lcall	seq_cva
	movx	a,@dptr
	dec	a
	movx	@dptr,a
	jz	seqxdjndont	; if decremented to zero, do not branch
	ljmp	seq_x_j57
seqxdjndont:
	ljmp	seq_cont
;
; ----------------------------------------------------------------------
;
; --- Sequencer instruction jump vector
;
seq_xvec:
	AJMP	seq_x_end
	AJMP	seq_x_puls
	AJMP	seq_x_tostart
	AJMP	seq_x_tostop
	AJMP	seq_x_ljmp
	AJMP	seq_x_setvar
	AJMP	seq_x_cgequ
	AJMP	seq_x_djnz
;
; ----------------------------------------------------------------------
; +F+
; FUNCTION:	SEQ_GETIB
; ACTION:	get 1 byte value to ACC
; -F-
;
seq_getib:
;+
	%B52_PUTCHAR(#'S')
;-
	%SEQU_GETIP(R0,R2)
;+
	push	ar2
	push	ar0
	mov	ar1,ar0
	mov	ar3,ar2
	mov	a,#098H
	lcall	030H
	%B52_PUTCHAR(#'-')
	pop	ar0
	pop	ar2
;-
	mov	dpl,r0
	mov	dph,r2
	movx	a,@dptr
	inc	dptr
	push	acc
	mov	r0,dpl
	mov	r2,dph
	%SEQU_PUTIP(R0,R2)
;
	pop	ar1
	push	ar1
	mov	r3,#0
	mov	a,#098H
	lcall	030H
	mov	a,#007H
	lcall	030H
;
	pop	acc
	ret
;
; ----------------------------------------------------------------------
; +F+
; FUNCTION:	SEQ_GETIW
; ACTION:	get 1 word value to R5/R7
; -F-
;
seq_getiw:
	lcall	seq_getib
	push	acc
	lcall	seq_getib
	mov	r5,a
	pop	ar7
	ret
;
; ----------------------------------------------------------------------
; +F+
; FUNCTION:	SEQ_GETI2
; ACTION:	get 1 word value to R4/R6
; -F-
;
seq_geti2:
	lcall	seq_getib
	push	acc
	lcall	seq_getib
	mov	r4,a
	pop	acc
	mov	r6,a
	ret
;
; ----------------------------------------------------------------------
; +F+
; FUNCTION:	SEQ_GETIBW
; OUTPUT:	R5/R7 ... word value
;		ACC ..... byte value
; ACTION:	get one byte value to ACC and 1 word value to R5/R7
; -F-
;
seq_getibw:
	lcall	seq_getib
	push	acc
	lcall	seq_getiw
	pop	acc
	ret
;
; ----------------------------------------------------------------------
; +F+
; FUNCTION:	SEQ_CVA
; ACTION:	calculates address for numeric variable <ACC> to DPTR
; -F-
;
seq_cva:
	add	a,#LOW(seq_nvars)
	mov	dpl,a
	mov	a,#0
	addc	a,#HIGH(seq_nvars)
	mov	dph,a
	ret