;CLOCKS2/ASM - LDOS 6.2, Model II - 12/29/83
;
*MOD
;
ALIVO	EQU	08H		;alive ON char
ALIVF	EQU	0AH		;alive OFF char
;
;	maximum time count table
;
TIMETBL	DB	30,60,60,24	;tics,secs,mins,hours
;
UPDTIME	LD	HL,TIMETBL	;time counter
	DEC	(HL)		;Count down 'ticks'
	RET	NZ		;Back if not one second
;
	IF	@HZ50
	LD	(HL),25		;Set for 50 hertz
HERTZ$	EQU	$-1
	ELSE			; else use 60 hertz
	LD	(HL),30		;Reset for one second
HERTZ$	EQU	$-1
	ENDIF
;
	LD	B,3
	EX	DE,HL		;DE => time table
	INC	DE		;max seconds
	LD	HL,TIME$	;time storage
;
TIMER1	INC	(HL)		;Bump time parm
	LD	A,(DE)		;fetch max count
	SUB	(HL)		;at maximum?
	RET	NZ		;Ret if not max
	LD	(HL),A		;  else set to 0
	INC	HL		;Pt to next parm
	INC	DE		;next max entry
	DJNZ	TIMER1		;Loop thru 3 parms
;*=*=*
;	Update date at midnight
;*=*=*
	LD	L,DATE$+1&0FFH	;Point to day of month
	LD	DE,MAXDAY$+1	;Point to test table
	INC	(HL)		;Bump the day
	INC	HL		;Point to month
	LD	A,(HL)		;Get the month
	DEC	HL
	DEC	A		;Index into table
	ADD	A,E
	LD	E,A
	JR	NC,$+3		;go if no page cross
	INC	D		;else bump page
	LD	A,(DE)		;P/u max days
	CP	(HL)		;Is day in range?
	RET	NC		;Return if it is
	LD	(HL),1		;  else reset day to 1
	INC	HL		;  & bump the month
	INC	(HL)
	LD	A,(HL)		;If went past Dec,
	SUB	12+1		;  then need to fix
	RET	C
	LD	(HL),1		;Correct to Jan
	DEC	HL		;Backup to year
	DEC	HL
	INC	(HL)
RETINST	RET
;*=*=*
;	Clock display processor
;*=*=*
DO_CLOCK
	DW	CLOKTSK		;task address
CLOKTSK	LD	HL,TIMBFF	;dummy time buffer
	PUSH	HL		;save pointer
	CALL	@TIME		;load string
	POP	HL		;restore
	LD	E,69-80		;display offset
	LD	C,8		;length of text
;
;	move interrupt data to video
;
TO_VIDEO
	LD	A,(LCKFLG$)	;video lockout flag
	ADD	A,A		;7 set = no video enable!
	RET	C		;cannot go!
;
;	select video memory
;
	LD	A,(MODOUT$)	;get port image
	PUSH	AF		;save image
	CALL	VIDON		;enable video
	LD	A,(CRTD1)	;get # video columns
	ADD	A,E		;add offset to video pos
	LD	E,A		;update!
	LD	D,CRTBGN$<-8	;msb video
	LD	B,0		;init MSB
	LDIR			;data to video
	POP	AF		;restore image
SET_MOD	LD	(MODOUT$),A	;update image
	OUT	($BSEL),A	;re-set
	RET			;done
;
TIMBFF	DB	'..:..:..'	;dummy clock buffer
;
;	load user buffer with current date
;
@DATE	LD	DE,DATE$+2	;end date$
	LD	C,'/'		;separator
	JR	TIME1		;go common
;
;	load user buffer with current time
;
@TIME	LD	DE,TIME$+2	;Point to time$
	LD	C,':'		;Set the separator
TIME1	LD	B,3		;Init for three fields
TIME2	LD	A,(DE)		;Get a field item
	LD	(HL),'0'-1	;setup for convert
TIME3	INC	(HL)		;Bump until proper digit
	SUB	10
	JR	NC,TIME3
	ADD	A,'0'+10	;last sub + ascii
	INC	HL		;Bump to next display
	LD	(HL),A		;  & stuff the digit
	INC	HL
	DEC	B		;less count
	RET	Z		;go if done
TIME4	LD	(HL),C		;  else stuff separator
	INC	HL
	DEC	DE		;Point to next field
	JR	TIME2		;  & loop
;*=*=*
;	Dynamic Trace routine
;*=*=*
DO_TRACE
	DW	TRACTSK		;task address
TRACTSK	LD	DE,0		;get PC
PCADDR	EQU	$-2
	LD	HL,TIMBFF	;temp buffer
	PUSH	HL		;save start
	CALL	@HEX16		;convert to hex on disply
	POP	HL		;HL => trace address hex
	LD	E,63-80		;video offset
	LD	C,4		;data length
	JR	TO_VIDEO	;move to video if poss.
;*=*=*
;	Hexadecimal display routine
;*=*=*
@HEX16	LD	A,D		;get MSB
	CALL	@HEX8		;convert & load
	LD	A,E		;get LSB
;
@HEX8	PUSH	AF		;save digit
	RRCA			;align to low nibble
	RRCA
	RRCA
	RRCA
	CALL	HEX1		;convert nibble
	POP	AF		;restore digit
;
HEX1	AND	0FH		;low 4 bits only
	ADD	A,90H		;align for adjust
	DAA			;adjust decimal
	ADC	A,40H		;align for adjust
	DAA			;adjust hex/decimal
	LD	(HL),A		;to buffer
	INC	HL		;bump pointer
	RET			;done
;
;	alive feature
;
DO_ALIVE
	DW	ALIVTSK		;task start
	DB	ALIVO		;alive character
ALIVTSK	LD	A,ALIVO.XOR.ALIVF
	XOR	(IX+2)		;reverse mask
	LD	(IX+2),A	;update next char
	DEC	HL		;HL => alive char
	LD	E,79-80		;video offset
	LD	C,1		;char count
	JP	TO_VIDEO	;move to video if poss.
;*=*=*
;	Routine to enable video RAM & change stack
;*=*=*
;
*MOD
;
ENADIS_DO_RAM
;*=*=*
;	check if switch to system stack
;*=*=*
	LD	(HLSAV),HL	;save HL
	PUSH	AF		;pass flags to HL
	POP	HL		;HL = flags
	LD	(AFSAV),HL	;save AF
ENADISW	LD	A,(LCKFLG$)	;video lockout flag
	ADD	A,A		;check bit 7
	JR	C,ENADISW	;wait if video locked!
	LD	A,(MODOUT$)	;get memory image
;
	LD	HL,0C03H	;can't exceed F3FC
	ADD	HL,SP		;check if in range
	JR	NC,$J?1		;go if OK
;
;	switch to system stack
;
	POP	HL		;transfer RET address
	LD	(SPSAV),SP	;save stack
	LD	SP,STACK$-20H	;keep room at top
	PUSH	HL		;put RET back
$J?1	LD	HL,DIS_DO_RAM	;disable return address
	EX	(SP),HL		;leave vector
	PUSH	HL		;return vector back
	LD	HL,OPREG_SV_AREA
OPREG_SV_PTR	EQU	$-2
	SET	6,A		;set stack OK
	JR	NC,$J?2		;go if no stack change
	RES	6,A		;else set stack change
$J?2	LD	(HL),A		;save flag
	INC	HL		;bump to next free
	SET	7,A		;enable video
	INC	A		;must set NZ flag
	DEC	A		;A = NZ
	JR	DOOPREG		;continue
;
;	disable video ram
;
DIS_DO_RAM
	LD	(HLSAV),HL	;save HL
	PUSH	AF		;pass flags
	POP	HL		;to HL
	LD	(AFSAV),HL	;and save
	LD	HL,(OPREG_SV_PTR)	;get pointer
	DEC	HL		;move back to data
	LD	A,(HL)		;get port image
	BIT	6,A		;stack changed?
	RES	7,A		;force video OFF!
;
DOOPREG	LD	(OPREG_SV_PTR),HL	;resave pointer
	RES	6,A		;enable video
	CALL	SET_MOD		;restore image/port
	JR	NZ,$J?3		;go if not stack change
;
;	switch back to previous stack
;
	LD	SP,$		;get old stack back
SPSAV	EQU	$-2
$J?3	LD	HL,$		;get saved AF
AFSAV	EQU	$-2
	PUSH	HL		;pass back
	POP	AF		;flags restored
	LD	HL,$		;get saved HL
HLSAV	EQU	$-2
	RET			;done
;*=*=*
;	Bank selection SVC handler
;	HL=> transfer address for function B=0
;	C => Bank request <0-7>; Set bit 7 to transfer
;	B => Request function
;		0 => Select bank C
;		1 => Reset in-use bit of bank C
;		2 => Test in-use bit of bank C
;		3 => Set in-use bit of bank C
;
;*=*=*
*MOD
@BANK
	PUSH	HL		;save HL
	LD	A,B		;get command
	LD	HL,BANKTBL	;lookup table
	CALL	@LOOKUP		;search entry
	EX	(SP),HL		;get HL, leave vector
	RET	Z		;go vector if found
	POP	AF		;remove vector
	JP	PERR		;go param error!
;
BANKTBL	DB	0		;select bank
	DW	BANK0
	DB	1		;unmark bank
	DW	BANK1
	DB	2		;test bank
	DW	BANK2
	DB	3		;mark bank
	DW	BANK3
	DB	4		;select bank
	DW	BANK4
	DB	-1		;terminator
;
BANK4	LD	A,(LBANK$)	;P/u current bank
	CP	A
	RET
;
;	set bank in use
;
BANK3	CALL	BANK2		;valid and available?
	RET	NZ		;nope, go!
	JR	BMARK		;mark and return
;
;	reset bank
;
BANK1	CALL	VALBANK		;valid bank#?
	RET	NZ		;nope, go!
	JR	UMARK		;unmark and return
;
;	test bank in use
;
BANK2	CALL	VALBANK		;valid bank#?
	RET	NZ		;nope, go!
	JR	MTEST		;test bank marked
;
;	select bank
;
BANK0	PUSH	HL		;Ck if stack is in upper
	LD	HL,8005H	;  bank area
	ADD	HL,SP
	POP	HL
	JP	C,PERR		;Error if > X'7FFE'
	CALL	VALBANK		;valid bank?
	RET	NZ		;go if not
	LD	A,(MODOUT$)	;P/u current memory
	AND	0F0H		;remove bank address
	LD	B,A		;save temp
	LD	A,C		;get bank # needed
	AND	7FH		;remove jump bit
	INC	A		;bump to correct bank
	OR	B		;combine
	CALL	SET_MOD		;set new memory image
	LD	A,(LBANK$)	;Get old bank #
	LD	B,A		;  & save it
	LD	A,C		;P/u new bank #
	AND	7FH		;Strip any bit-7
	LD	(LBANK$),A	;  & save new bank #
	XOR	C		;keep bit 7
	OR	B		;merge in new bank
	LD	C,A		;replace for return
	BIT	7,C		;Transfer to new bank?
	LD	B,0		;Init for invoke later
	RET	Z		;No if bit-7 = 0
	EX	(SP),HL		;Exchange RET with new
	CP	A		;  transfer & go to it
	RET
;
VALBANK	LD	A,C		;get bank #
	AND	7FH		;remove jump bit
	CP	8		;0-7?
	JR	C,ATEST		;yes, check if exists
INVBANK	JP	PERR		;param error
;
;	test if bank exists
;
ATEST	PUSH	HL		;save
	LD	HL,BAR$		;bank avail ram
	JR	AMTEST		;continue
;
;	test if bank is marked
;
MTEST	PUSH	HL		;save
	LD	HL,BUR$		;bank used ram
;
AMTEST	PUSH	BC		;save
	CALL	BSETUP		;setup for test
	LD	A,(HL)		;get data
	AND	C		;marked?
	POP	BC		;restore
	POP	HL
	JR	NZ,INVBANK	;go if not
	RET			;else return Z
;
;	set bank # as marked
;
BMARK	CALL	BUSET		;setup
	OR	C		;mark bank
	JR	MUCOMM		;continue
;
;	unmark bank #
;
UMARK	CALL	BUSET		;setup
	AND	B		;unmark!
;
MUCOMM	LD	(HL),A		;update data
	POP	BC		;restore
	POP	HL
	XOR	A		;set NO error
	RET			;done
;
;	setup for bank activity
;
BUSET	POP	AF		;get caller
	PUSH	HL		;save registers
	PUSH	BC
	PUSH	AF		;return back
	LD	HL,BUR$		;bank used ram
	CALL	BSETUP		;setup
	LD	A,(HL)		;get data
	RET			;return with data
;
;	setup for bank activity
;
BSETUP	RES	7,C		;force in range
	INC	C		;init for test
	LD	B,1		;init bit 0
BSETUP1	DEC	C		;found?
	JR	Z,BSETUP2	;go if yes
	SLA	B		;shift test bit
	JR	BSETUP1		;continue
BSETUP2	LD	A,B		;get test bit
	LD	C,A		;C=set bit
	CPL			;reverse bits
	LD	B,A		;B=reset bits
	RET			;done
;
;	ring buffer management
;
;	IX => data block used by device
;
;	+0/1	= buffer address
;	+2	= buffer length
;	+3	= add offset
;	+4	= take offset
;
BUFFTAK	LD	A,(IX+4)	;get take offset
	CP	(IX+3)		;compare to add offset
	RET	Z		;no chars if equal
;
BUFFINC	PUSH	AF		;save offset
	LD	L,(IX+0)	;get buffer start
	LD	H,(IX+1)
	ADD	A,L		;add lsb offset
	LD	L,A		;update lsb
	JR	NC,$+3		;go if no overflow
	INC	H		;else bump msb
	POP	AF		;restore offset
	INC	A		;bump to next slot
	CP	(IX+2)		;beyond ring?
	RET	C		;nope, return
	XOR	A		;reset offset
	SCF			;must set carry
	RET			;return new pointer
;
BUFFADD	LD	A,(IX+3)	;get add offset
	CALL	BUFFINC		;bump to next slot
	CP	(IX+4)		;buffer full?
	RET	Z		;yes, cannot add
	LD	(IX+3),A	;update ring pointer
	SCF			;Cy = room to add
	RET			;done, return
;
OPREG_SV_AREA
	DW	0,0,0,0,0,0,0	;video mask storage
;
LCKFLG$	DB	0		;7=no video, 6=no RTC
;
VIDON	PUSH	AF		;save
	LD	A,(MODOUT$)	;get flag
	OR	10000000B	;enable video
	JR	VIDONF		;continue
;
VIDOFF	PUSH	AF		;save
	LD	A,(MODOUT$)	;get flag
	AND	01111111B	;disable video
	JR	VIDONF		;continue
;
NMION	PUSH	AF		;save
	LD	A,(MODOUT$)	;get image
	OR	00100000B	;enable RTC
	JR	VIDONF		;continue
;
NMIOFF	PUSH	AF		;save
	LD	A,(MODOUT$)	;get image
	AND	11011111B	;disable RTC
;
VIDONF	CALL	SET_MOD		;update flag/port
	POP	AF		;restore
	RET			;done
;
