; rtc/asm
*TITLE	'<DOS+II - REAL TIME CLOCK SERVICE>'
;
NMICODE PUSH	AF		;SAVE REGISTERS
	PUSH	BC
	PUSH	DE
	PUSH	HL
	PUSH	IX
;
	IN	A,(RDRTC)	;CLEAR RTC
	XOR	A		;INIT
;
NMILOOP PUSH	AF		;SAVE COUNT
	CALL	NMIEXEC 	;GO ROUTINE!
	POP	AF		;GET COUNT
	INC	A		;NEXT
	AND	7		;DONE?
	JR	NZ,NMILOOP	;TIL DONE
;
	LD	HL,DATAK0	;DRIVE TIME OUT
	OR	(HL)		;TIMED OUT?
	JR	Z,NMIXIT	;IF YES
	DEC	(HL)		;COUNT
	JR	NZ,NMIXIT
	LD	A,4FH		;SIDE 0 NO SEL
	OUT	(DRSL),A	;DE-SEL DRIVE
;
NMIXIT	POP	IX		;RESTORE PARTIAL
	POP	HL
	POP	DE
	POP	BC
	POP	AF
	RETN			;GO DEBUG!
;
;	EXECUTE INTERRUPT TASK
;
NMIEXEC LD	HL,RTCTBL	;HL => INT TABLE
	ADD	A,A		;* 2
	ADD	A,L		;+ TABLE
	LD	L,A		;HL => TABLE ENTRY
	LD	E,(HL)
	INC	HL
	LD	D,(HL)		;DE => INDIRECT ADD
	PUSH	DE
	POP	IX		;IX => INDIRECT ADD
	EX	DE,HL		;HL => INDIRECT ADD
	LD	E,(HL)
	INC	HL
	LD	D,(HL)		;DE => INT TASK
	EX	DE,HL		;HL => INT TASK
	JP	(HL)		;GO!
;
;	NILL INTERRUPT TASK
;
NILL	DEFW	NULL		;ENTRY ADD
NULL	RET
;
*EJECT
;	$NMICTL - ADD/REMOVE INT TASKS
;
;	ENT	 B =	SET/RESET FLAAG
;		 C =	SLOT NUMBER 0-15
;		DE =>	USER ROUTINE
;
NMICTL	CALL	REGSAV		;SAVE REGISTERS
	INC	B
	DEC	B		;REMOVE?
	JR	NZ,SETNMI	;IF NOT
;
RESNMI	LD	DE,NILL 	;DE => NILL ROUTINE
;
SETNMI	LD	A,C		;GET SLOT
	CP	8		;VALID?
	JR	NC,NMIBAD	;invalid, go!
	LD	HL,RTCTBL	;HL => INT TABLE
	ADD	A,A
	ADD	A,L
	LD	L,A		;HL => TABLE ENTRY
	CALL	VIDOFF		;DISABLE RTC
	LD	(HL),E
	INC	HL
	LD	(HL),D		;SET VECTOR
	CALL	NMION		;ENABLE RTC
	XOR	A		;NO ERRORS
	RET
;
NMIBAD	LD	A,3		;PARAMETER ERROR
	OR	A		;NZ STATUS
	RET
;
*EJECT
;	real time clock advance TASK
;
TIMTASK DEFW	CLOCK0		;driver address
;
CLOCK0	LD	HL,TIME$	;point to current time
	LD	DE,TIMETBL	;max values
	LD	B,4		;4 bytes for time
CLOCK1	INC	(HL)		;bump the ticks
	LD	A,(DE)		;get max
	SUB	(HL)		;test it
	RET	NZ		;not time to change yet
	LD	(HL),A		;reset the value to 0
	INC	L		;bump time pointer
	INC	E		;bump max pointer
	DJNZ	CLOCK1		;do all 4 time values
;
; optional date adjust for each 24 hour period
; leap years are automatically adjusted
;
	PUSH	HL		;save day pointer
	INC	L		;point to month
	LD	A,(HL)		;get it
	DEC	A		;make 1-12 into 0-11
	ADD	A,E
	LD	E,A		;(DE)=max days this month
	INC	L		;point to year
	LD	A,(HL)		;get the year
	AND	3		;check for leap year
	LD	A,28		;28 days hath february
	JR	NZ,CLOCK3	;go, not leap year
	INC	A		;else 29 days this year
CLOCK3	LD	(MONTHS+1),A	;set FEB days
	POP	HL		;now pointing to day
	INC	(HL)		;bump the day
	LD	A,(DE)		;get the max this month
	SUB	(HL)		;compare to current #
	RET	NC		;not end of month yet
	LD	(HL),1		;set first day
	INC	L		;point to month
	INC	(HL)		;bump it
	LD	A,(HL)		;get the month
	CP	13		;at end of year yet?
	RET	C		;nope, done
	LD	(HL),1		;set month 1
	INC	L		;point to year
	INC	(HL)		;bump the year
	RET			;completed
;
*EJECT
;	real tile clock to display TASK
;
CLKTASK DEFW	CLK0		;driver address
	DEFB	30		;count
;
CLK0	LD	HL,CLKTASK+2	;HL => data
	DEC	(HL)		;now?
	RET	NZ		;if not
	LD	(HL),30 	;1 per/sec
	LD	HL,DATAK9	;video address
	LD	DE,TIME$+3	;point to hour
	LD	B,3		;3 loops
	JR	CLK2		;go!
;
CLK1	INC	HL		;next video slot
	LD	(HL),':'        ;time separator
	INC	HL		;next video slot
;
CLK2	LD	A,(DE)		;get time byte
	DEC	DE		;point to next
	CALL	CLKASC		;quick ascii
	LD	(HL),C		;put MSB
	INC	HL		;bump pointer
	LD	(HL),A		;put LSB ascii
	DJNZ	CLK1		;more?
	LD	DE,VIDEO+69+7	;video add
	LD	C,8		;char count
	JR	TOVIDEO 	;SKIP
;
CLKASC	LD	C,'0'           ;start for MSB
CLKA1	SUB	10		;less 10
	JR	C,CLKA2 	;go if done
	INC	C		;bump ascii digit
	JR	CLKA1		;go again
CLKA2	ADD	A,3AH		;make it ascii
	RET			;done, CA = ascii
;
*EJECT
;	real time alive to video TASK
;
ALVTASK DEFW	ALV0		;driver address
	DEFB	8		;count
	DEFB	4		;character
;
ALV0	LD	HL,ALVTASK+2	;HL => data
	DEC	(HL)		;now?
	RET	NZ		;if not
	LD	(HL),8		;count
	INC	HL		;HL => code
	LD	A,2		;code
	XOR	(HL)		;flip
	LD	(HL),A		;save
	LD	DE,VIDEO+78	;video loc
	LD	C,1		;char count
	JR	TOVIDEO 	;SKIP
;
*EJECT
;	real time trace to display TASK
;
TRCTASK DEFW	TRC0		;driver address
;
TRC0	LD	HL,14		;SKIP REG
	ADD	HL,SP		;HL => RET ADD
	LD	C,(HL)
	INC	HL
	LD	B,(HL)		;DE => RET ADD
	LD	HL,DATAK9	;temp storage
	LD	A,B		;get MSB
	CALL	B2H		;quick hex convert
	LD	A,C		;get LSB
	CALL	B2H		;put on video
	DEC	HL		;bksp
	LD	DE,VIDEO+64+3	;video address
	LD	C,4		;count
;
TOVIDEO LD	A,(FLAGK1)	;flags
	BIT	4,A		;ok to bank?
	RET	NZ		;if not
	LD	A,(VMASK)	;video mask
	OR	80H		;enable video
	OUT	(LBANK),A	;do it!
	LD	B,0		;BC = count
	LDDR			;to video
;
;	RESTORE RTC/VIDEO
;
NMION	LD	A,(VMASK)	;video mask
	OUT	(LBANK),A	;restore video
	RET
;
;	DISABLE RTC/VIDEO
;
VIDOFF	LD	A,(VMASK)	;get video mask
	AND	5FH		;no VIDEO/RTC
	OUT	(LBANK),A	;do it!
	RET
;
*EJECT
;	real time user timer TASK
;
TMRTASK DEFW	TMR0		;driver address
	DEFB	30		;count
;
TMR0	LD	HL,TMRTASK+2	;HL => data
	DEC	(HL)		;now?
	RET	NZ		;if not
	LD	(HL),30 	;1 per/sec
	LD	HL,(DATAK1)	;get seconds left
	DEC	HL		;less one second
	LD	(DATAK1),HL	;update the counter
	LD	A,H		;see if time
	OR	L
	RET	NZ		;nope, return
	LD	C,7		;C = slot
	CALL	RESNMI		;turn it off
	LD	HL,(DATAK2)	;get timer vector
	JP	(HL)		;go!
;
*EJECT
;	real time printer spooler TASK
;
PRTTASK DW	PRTASK		;for vector
;
PRTASK	LD	A,PDCB@ 	;printer DCB number
	CALL	LOCDCB		;find the DCB
	BIT	5,(IX+5)	;OK?
	RET	NZ		;do notin
;
SPOOL	LD	C,20		;max chars/interrupt
SPOOL1	CALL	BUFTAKE 	;get character
	RET	NC		;empty!
	LD	B,16		;wait
SPOOL3	CALL	PRIN		;get status byte
	JR	Z,SPOOL2	;ready to go
	DJNZ	SPOOL3		;try again
	RET
;
SPOOL2	CALL	VIDOFF		;no video ram
	LD	L,(HL)		;get char
	CALL	NMION		;restore video
	LD	A,L		;A=char
	CALL	PROUT		;send the character
	LD	(IX+15),E	;update
	LD	(IX+16),D	;ring
	DEC	C		;next
	JR	NZ,SPOOL1	;til done
	RET			;else return
;
; routine to send a char to printer
; ENTRY:  IX => DCB
;	  A  = character
; EXIT:   character sent directly to the device
;
PROUT	BIT	7,(IX+5)	;serial?
	JR	NZ,PROUTS	;go if yes
	OUT	(PIOBD),A	;send parallel
	RET			;done
;
PROUTS	OUT	(SIOBD),A	;send character
	RET
;
; routine to fetch printer status
; ENTRY:  IX => DCB
; EXIT:   Z  = printer can accept char
;	  NZ = not ready
;
PRIN	BIT	7,(IX+5)	;serial?
	JR	NZ,PRINS	;go if yes
	IN	A,(PIOAD)	;get parallel status
	AND	0F0H		;top 4 bits only
	RET			;Z = ready
;
PRINS	LD	A,10H		;reset
	OUT	(SIOBC),A	;channel B command
	IN	A,(SIOBC)	;get status
	CPL			;reverse bits
	AND	4		;TX buffer empty?
	RET			;Z = yes
;
*EJECT
;
;	BUFTAKE - attempt to take char from buffer
;
;	ENT	IX =>	DCB
;
;	EXT	 A =	character
;		HL =>	character buffer
;		DE =	ring location
;		C set if character avail
;
BUFTAKE LD	L,(IX+13)	;P ring
	LD	H,(IX+14)
	LD	E,(IX+15)
	LD	D,(IX+16)	;T ring
	OR	A		;clr cry
	SBC	HL,DE		;T = P?
	RET	Z		;NC = nil
;
	LD	L,(IX+09)
	LD	H,(IX+10)	;HL => buffer
	CALL	INCBUFF 	;bump ring
	ADD	HL,DE		;HL => char
	LD	A,(HL)		;get char
	SCF			;have character!
	RET
;
*EJECT
;
;	BUFFADD - attempt to add char to buffer
;
;	ENT	 A =	character
;		IX =>	DCB
;
;	EXT	HL =>	add location
;		DE =	ring location
;		C set if character put
;
BUFFADD LD	L,(IX+15)	;T ring
	LD	H,(IX+16)
	LD	E,(IX+13)	;P ring
	LD	D,(IX+14)
	CALL	INCBUFF 	;bump ring
	OR	A		;clr cry
	SBC	HL,DE		;P = T?
	RET	Z		;NC = no room!
;
	LD	L,(IX+09)
	LD	H,(IX+10)	;HL => buffer
	ADD	HL,DE		;HL => char
	LD	(HL),A		;put char
	SCF			;char in!
	RET
;
;	INCBUFF - increment ring buffer pointer
;
;	ENT	IX =>	DCB
;		DE =	ring location
;
;	EXT	DE =	new ring location
;
INCBUFF PUSH	HL		;save
	INC	DE		;bump pointer
	LD	L,(IX+11)	;get max length
	LD	H,(IX+12)
	SCF			;for one adjust
	SBC	HL,DE		;see if at end
	POP	HL		;restore pointer
	RET	NC		;not at end
	LD	DE,0		;else start over
	RET
 