;LBDATE/ASM - Date/Time Commands
	TITLE	<DATE/TIME - LS-DOS 6.3>
;
*GET	BUILDVER/ASM:3
*GET	SVCMAC:3		;SVC Macro equivalents
*LIST OFF			;Get LDOS60/EQU
*GET	LDOS60/EQU:2		;Xref of lowcore
*LIST ON
;
CR	EQU	13
@ADTSK	EQU	29		;Add Task SVC #
@RMTSK	EQU	30		;Remove Task SVC #
CLK_SLT	EQU	5		;Clock Task Slot #
;
;
;	DATE$ Storage
;
; DATE$+0   Year (80-87)
; DATE$+1   Day of the month (1-31)
; DATE$+2   Month (1-12)
; DATE$+3   Bits 0-7 of Day of Year
; Date$+4   Bit 0 = bit 8 of Day of Year
;	    Bits 1-3 contain Day of Week
;	    Bit 7 set if a leap year
;
;	TIME$ Storage
;
; TIME$+0   Seconds (0-59)
; TIME$+1   Minutes (0-59)
; TIME$+2   Hours (0-23)
;
;
;
	ORG	2400H
;
;	Branch to TIME entry point
;
	JP	TIME		;Time entry point
;
	SUBTTL	'<LBDATE - DATE Code>'
;
DATE	@@CKBRKC		;Break key down?
	JR	Z,BEGINA	;Ok if not
	LD	HL,-1		;  else abort
	RET
;
;	DATE - Pick up DATE$+0 pointer & stuff in IY
;
BEGINA	PUSH	HL		;Save command ptr
	LD	HL,DUMBUF	;HL => Dummy Buffer
	@@DATE			;DE <= DATE$+0
	POP	HL		;Recover command ptr
	PUSH	DE		;Xfer ptr to IY
	POP	IY
;
;	Was a Date entered on the command line ?
;
	LD	A,(HL)		;P/u character
	CP	CR+1		;Date entry ?
	JP	C,DSPDATE	;No - display date
;
;	Date entered - check if legal format
;
	LD	C,'0'		;Init separator
	CALL	PARSDAT		;Parse entry
	JP	NZ,BADFMT	;Bad entry - abort
;
;	Legal Date - If Intl date - swap DTBUF+1 & 2
;
	IF	@INTL
	INC	DE		;DE => DTBUF+1
	LD	H,D		;HL => DTBUF+1
	LD	L,E
	LD	C,(HL)		;C = Intl MONTH
	INC	HL		;HL => DTBUF+2
	LD	A,(HL)		;A = Intl DAY
	LD	(DE),A		;Set DTBUF+1 = DAY
	LD	(HL),C		;Set DTBUF+2 = MONTH
	DEC	DE		;DE => DTBUF+0
	ELSE
	DC	9,0		;Pad US ver to match Intl
	ENDIF
;
;	Is the year legal ?
;
	LD	A,(DE)		;P/u year entry
	IF	@BLD631
	CP	0CH		;<631>
	JR	NC,L2436	;<631>
	ADD	A,64H		;<631>
	LD	(DE),A		;<631>
L2436:
	ENDIF
	SUB	80		;Accept only 80-87
	JP	C,BADFMT	;Less than 80 - bad
	IF	@BLD631
	CP	20H		;<631>
	ELSE
	CP	20		;Greater than 99 ?
	ENDIF
	JR	NC,BADFMT2	;Yes - bad
;
;	If Year is 1980 or 84 then set FEB = 29 days
;
	AND	3		;0 or 4 ?
	LD	HL,MAXDAYS+2	;Set Feb to have 29 days
	JR	NZ,NOTLEAP	;No - don't inc
	INC	(HL)		;Leap year - inc max days
;
;	Check Range of month - must be 1 - 12
;
NOTLEAP	LD	A,(DTBUF+2)	;P/u month
	DEC	A		;Set month = 0-11
	CP	12		;Valid Month ?
	JR	NC,BADFMT2	;Abort if 0 or >12
;
;	Valid month - point HL to max days/month
;
	DEC	HL		;HL => Max day table
	ADD	A,L		;Add month # to start
	LD	L,A		;HL => Max days for month
;
;	Check for Day entry is valid
;
	LD	A,(DTBUF+1)	;P/u day entry
	DEC	A		;Reduce for test (0->FF)
	CP	(HL)		;More than max days ?
BADFMT2	JP	NC,BADFMT	;Go if too large (or 0)
;
;	Transfer Date into buffer
;
	EX	DE,HL		;Point HL to DTBUF
	PUSH	IY		;Point DE = DATE$
	POP	DE
	LD	C,3		;BC = 3 chars to xfer
	LDIR			;Xfer 3 chars
;
;	Display "No Date in System" if illegal Date
;
DSPDATE	LD	A,(IY+2)	;P/u month
	LD	HL,NODATE$	;"No Date in system"
	OR	A		;Better not be zero
	JP	Z,LOGABRT	;Log & abort
GOTDATE	LD	B,A		;Xfer month to B
	LD	HL,MAXDAYS+2	;Adjust February if
	LD	A,(HL)		;  year is leap year
	SUB	29		;  & not already adjusted
	JR	Z,PUDAY		;  in parsing date entry
;
;	Pick up Year & increment max days if leap yr
;
	LD	A,(IY)		;P/u year
	AND	3		;1980 and 1984 are lp yrs
	JR	NZ,PUDAY	;Not leap year - fine
	INC	(HL)		;Bump to 29
;
;	Set HL = day # this month, DE => Max table
;
PUDAY	LD	L,(IY+1)	;P/u day # this month
	LD	H,0		;  in HL
	LD	DE,MAXDAYS	;DE => Max day table
;
;	Loop to Count up total # of days up to now
;
DAYLP	LD	A,(DE)		;P/u max day
	ADD	A,L		;Add to HL
	LD	L,A
	ADC	A,H
	SUB	L
	LD	H,A
	INC	DE		;Bump days ptr
	DJNZ	DAYLP		;B months of max days
;
;	Stuff days (9 bits) into DATE$
;
	LD	(IY+3),L	;Stuff in lsb
	LD	A,H		;Get bit "8"
	OR	(IY+4)		;  and OR it in
	LD	(IY+4),A	;Then put it back
;
;	Pick up year in E (0-7)
;
	LD	A,(IY)		;P/u year
	SUB	80		;Offset from 80
	LD	E,A		;Put 0-7 in E
	ADD	A,3		;Ck for year >= 84
	RRCA
	RRCA
	IF	@BLD631
	AND	0FH		;<631>Keep bits 3-0
	ELSE
	AND	7		;Keep bits 0,1,2
	ENDIF
	ADD	A,E		;Add back to year
	LD	E,A		;  & save in DE
	LD	D,0
	ADD	HL,DE		;Add to days in year
	INC	HL		;To start in right place
;
;	HL = desired number to divide by seven
;
	LD	BC,7		;Now divide by 7
	XOR	A
DIV7	SBC	HL,BC		;Subtract weeks (7-days)
	JR	NC,DIV7		;  until under flow
;
;	Correct # for division, & put in bits 1-3
;
	LD	A,L
	ADD	A,8		;Add back to get 1-7
	LD	B,A		;Save day of week
	RLCA			;Shift to bits 1-3
	LD	C,A		;  to store in DATE$
;
;	Merge day of week with bit 9 of day of year
;
	LD	A,(IY+4)	;P/u DATE$ + 4
	AND	0F1H		;Keep lp yr bit & bit 0
	OR	C		;Merge day of week
	LD	(IY+4),A	;Stuff back in
;
;	Transfer Day string into display buffer
;
	LD	HL,DAYTBL	;HL => Day string table
	LD	DE,DATEBUF	;Date display buffer
	PUSH	DE		;Save start
	CALL	DSPMDY		;Write out the day
;
;	Position DE to month destination in buffer
;
	INC	DE		;Bump
	INC	DE
;
;	Pick up month, & stuff string into buffer
;
	LD	A,(IY+2)	;P/u month number
	LD	B,A		;Stuff in B
	LD	HL,MONTBL	;HL => Month string table
	CALL	DSPMDY		;Write out the month name
;
;	P/u day of the month & convert to ASCII
;
	INC	DE		;DE => Day destination
	LD	A,(IY+1)	;P/u day
	LD	B,-1		;Init # of tens to -1
;
;	Divide day of the month by 10
;
DIV10	INC	B		;Divide by 10
	SUB	10		;  with quotient in B
	JR	NC,DIV10	;Subtract until carry
;
;	Convert to tens digit to ASCII
;
	PUSH	AF		;Save (10-remainder)
	LD	A,B		;P/u quotient
	ADD	A,'0'		;Change to ASCII
;
;	Change to a space if it's a leading zero
;
	CP	'0'		;Zero?
	JR	NZ,NOTLD0	;No - use it
	LD	A,' '		;Change leading 0 to ' '
NOTLD0	LD	(DE),A		;Stuff in buffer
;
;	Convert remainder to ASCII & stuff in buffer
;
	INC	DE		;DE => ones destination
	POP	AF		;Get back remainder
	ADD	A,3AH		;Change to ASCII
	LD	(DE),A		;Stuff in buffer
;
;	P/u year & stuff lower digit + "0" in buffer
;
	LD	A,(IY)		;Form last year digit
	IF	@BLD631
	LD	HL,1900		;<631>
	ADD	A,L		;<631>
	LD	L,A		;<631>
	ADC	A,H		;<631>
	SUB	L		;<631>
	LD	H,A		;<631>
	LD	DE,YRBUF	;<631>
	@@HEXDEC		;<631>
	ELSE
	SUB	80		;A = 0-19
	CP	10		;In 1980's?
	JR	C,WAS80
	LD	B,A
	LD	A,'9'		;Nope, 1990's
	LD	(DATEBUF+15),A
	LD	A,B		;Get back year offset
	SUB	10		;Sub off decade
WAS80	ADD	A,'0'		;Make ascii
	LD	(DATEBUF+16),A	;Stuff year
	ENDIF
;
;	Set B = 0 (Normal Exit)
;
LOGDT	LD	B,0		;B = 0 (normal exit)
	POP	HL		;HL => Date/Time string
;
;	Display Date or Time String
;
LOGMSG	PUSH	BC		;Save Error #, B (exit)
	@@LOGOT			;Log message
	POP	BC		;B = exit condition
;
;	If B = 0 then exit HL = 0, otherwise HL = -1
;
	LD	H,B		;Set HL = -1 or 0
	LD	L,B		;
	@@CKBRKC		;Clear any break
	RET			;RETurn with condition
;
;	Bad Format - display error & abort
;
BADFMT	LD	HL,BADDAT$	;Illegal Date/Time
LOGABRT	LD	B,-1		;Abort Condition
	JR	LOGMSG		;Log Message
;
	SUBTTL	'<LBDATE - TIME code>'
	PAGE
;
TIME	@@CKBRKC		;Break key down?
	JR	Z,BEGINB	;Ok if not
	LD	HL,-1		;  else abort
	RET
;
;	TIME entry point - Any Parms entered ?
;
BEGINB	PUSH	HL		;Save pointer
TLOOP	LD	A,(HL)		;P/u character
	CP	'('		;Any parameters ?
	JR	Z,GETPRMS	;Yes - get 'em
	CP	CR		;End of line ?
	JR	Z,CLRSTK	;Yes - go check time
	INC	HL		;Bump ptr
	JR	TLOOP		;Do til terminator
;
;	Process any Parameters
;
GETPRMS	LD	DE,PRMTBL$	;DE => Parameter Table
	@@PARAM			;Get parameters
;
;	Stuff "Illegal Time" Mess in error routine
;
CLRSTK	LD	HL,BADTIM$	;Chg "Bad date format"
	LD	(BADFMT+1),HL	;  to "Bad time...
	POP	HL		;Recover command ptr
	JR	Z,GDPARMS	;Z - ok to continue
;
;	Parameter Error - Display & abort
;
IOERR	LD	L,A		;Xfer errcod to HL
	LD	H,0
	OR	0C0H		;Short error
	LD	C,A		;Xfer to C
	@@ERROR			;Log error
	RET
;
;	Was there a TIME string entered ?
;
GDPARMS	LD	A,(HL)		;P/u char
	CP	'('		;Parms only?
	JP	Z,DSPTIME	;Display old time
	CP	CR		;End of line?
	JP	Z,DSPTIME	;Display old time
;
;	Requested time set - Check if legal format
;
	XOR	A
	LD	(ISTIM),A	;Show doint time
	LD	(DTBUF),A	;Init seconds to 0
	LD	C,'0'		;Init separator
	CALL	PARSDAT		;Parse entry
	JR	NZ,BADFMT	;Bad - abort
;
;	Legal Format - Check if Hours are legal
;
	LD	HL,DTBUF+2	;HL => Hours byte
	LD	A,23		;Greater than 23 ?
	CP	(HL)
	JR	C,BADFMT	;Yes - bad format
;
;	Hours legal - Check if minutes legal
;
	DEC	HL		;HL => Minutes
	LD	A,59		;Greater than 59 ?
	CP	(HL)
	JR	C,BADFMT	;Yes - bad format
;
;	Minutes legal - Check if seconds legal
;
	DEC	HL		;HL => Seconds
	CP	(HL)		;Greater than 59 ?
	JR	C,BADFMT	;Yes - bad format
;
;	Legal input - transfer to TIME$ storage area
;
	PUSH	HL		;Save TIME buffer ptr
	LD	HL,DUMBUF	;HL => dummy buffer
	@@TIME			;DE <= TIME$+0
	POP	HL		;Recover TIME buffer ptr
	LD	BC,3		;3 bytes to xfer
	LDIR			;Xfer
;
;	Was the CLOCK (C) parameter entered ?
;
DOCLOCK	LD	HL,0		;HL = 0 (Normal Exit)
	LD	A,(CRESP)	;P/u response
	OR	A
	RET	Z		;RETurn if no response
;
;	CLOCK (C) parameter entered - ON or OFF ?
;
CLOCK	LD	DE,$-$		;P/u parm = FFFF or 0000
	@@FLAGS			;IY => System Flags
;
;	Just Set/Reset CLOCK bit if Model IV version
;
	IF	@MOD4
	SET	4,(IY+'V'-'A')	;Set Clock bit
	INC	E		;Return if CLOCK = YES
	RET	Z		;
	RES	4,(IY+'V'-'A')	;Otherwise Reset bit
	DEC	E		;Set Z flag
	RET			;Done - RETurn
	ENDIF
;
;	Also Add or Remove Task if Model II Version
;
	IF	@MOD2
	RES	4,(IY+'V'-'A')	;Reset clock bit
	LD	C,CLK_SLT	;Set C = Clock Slot #
	LD	A,@RMTSK	;A = Remove Task SVC #
	INC	E		;Clock Off ?
	JR	NZ,CLOFF	;Yes - remove task
CLON	LD	DE,DO_CLOCK	;Clock on - DE => Address
	SET	4,(IY+'V'-'A')	;Set clock bit
	LD	A,@ADTSK	;A = Add Task SVC #
CLOFF	RST	40		;Issue SVC
	XOR	A		;Set Z for no error
	LD	H,A		;Pass to HL
	LD	L,A		;For normal exit
	RET
	ENDIF
;
;	Display the Time
;
DSPTIME	LD	HL,DATEBUF+9	;Pt to space for time str
	PUSH	HL		;Save pointer
	@@TIME			;Xfer time into buffer
	CALL	DOCLOCK		;Set/Reset Clock bit
	JP	LOGDT		;Log it & exit
	SUBTTL	'<LBDATE - DATE/TIME Common Routines>'
	PAGE
;
;	DSPMDY - Xfer 3 char string from table to buffer
;
;	B  => Entry # in table to display
;	HL => Table to fetch data from
;	DE => Buffer to receive string
;
DSPMDY	DEC	B		;B = entry, 0-6
	LD	A,L		;P/u lsb of table start
	ADD	A,B
	ADD	A,B		;Entries 3 bytes long
	ADD	A,B
	LD	L,A		;HL => Table entry
;
;	Transfer string into buffer
;
	LD	B,3		;Three chars to xfer
DSPM1	LD	A,(HL)		;P/u char from table
	LD	(DE),A		;Stuff into buffer
	INC	HL		;Bump
	INC	DE
	DJNZ	DSPM1		;Three chars to xfer
	RET			;Done - RETurn
;
;	PARSDAT - Parse TIME/DATE string entry
;
;	HL => Buffer containing string to parse
;	C  => Delimiter (<"0" = DATE, <"0"or=":" = TIME)
;
;	DTBUF-DTBUF+2 <= Data in compressed format
;	Z  - Set if successful
;
PARSDAT	LD	DE,DTBUF+2	;Point to buf end
	LD	B,3		;Process 3 fields
;
;	Parse a field - Return NZ if bad
;
PRS1	PUSH	DE		;Save pointer
	CALL	PRS2		;Get a digit pair
	POP	DE		;Recover pointer
	RET	NZ		;Ret if bad digit pair
;
;	Good field - Stuff in buff, dec ptr, & count
;
	LD	(DE),A		;  else stuff the value
	DEC	B		;Loop countdown
	RET	Z		;Do for 3 fields
	DEC	DE		;Backup the pointer
;
;	Parsed a field - is the separator valid ?
;
	LD	A,(HL)		;P/u separator
	INC	HL		;Bump pointer
	CP	':'		;Check for ':'
	JR	Z,PRS1		;  loop if so
	CP	C		;Correct ?
	JR	NC,PRSRET	;NC = bad
	CP	CR		;If end of line, ck for time
	JR	NZ,PRS1
	LD	A,-1
ISTIM	EQU	$-1		;Set zero by time
	OR	A
	JR	NZ,PRSRET	;Bad if date
	LD	A,B
	DEC	A		;If B was one, done
	RET	Z
;
PRSRET	OR	A		;Set NZ
	RET			;Return bad entry
;
;	PRS2 - Parse a digit pair at HL
;
PRS2	CALL	PRS4		;Get a digit
	JR	NC,PRS3		;Illegal - return
;
;	Legal Digit - Multiply by 10
;
	LD	E,A		;Multiply by ten
	RLCA			;X 2
	RLCA			;X 4
	ADD	A,E		;X 5
	RLCA			;X 10
	LD	E,A		;Stuff in E
;
;	Get another digit
;
	CALL	PRS4		;Get ones digit
	JR	NC,PRS3		;Bad - return NZ
;
;	Legal digit - Add to tens digit & set Z flag
;
	ADD	A,E		;Accumulate new digit
	LD	E,A		;Save 2-digit value
	CP	A		;Clear flags
	RET			;Return Z
;
;	Force NZ & Return
;
PRS3	OR	A		;Set NZ
	RET			;RETurn
;
;	Pick up a digit and convert to binary
;
PRS4	LD	A,(HL)		;P/u a digit &
	INC	HL		;  bump ptr
	SUB	'0'		;Convert to binary
	CP	10		;Legal ?
	RET			;C - legal, NC - illegal
;
;	Parameter table
;
NUM	EQU	80H
FLAG	EQU	40H
STR	EQU	20H
ABB	EQU	10H
;
PRMTBL$	DB	80H		;6.x Parameter Table
;
	DB	FLAG!ABB!5
	DB	'CLOCK'
CRESP	DB	0
	DW	CLOCK+1
	DB	0
;
	ORG	$<-8+1<+8
;
DAYTBL	DB	'SunMonTueWedThuFriSat'
MONTBL	DB	'JanFebMarAprMayJunJulAugSepOctNovDec'
	IF	@BLD631
DATEBUF:DB	'Day, Mon xx,'	;<631>
YRBUF:	DB	' 198x',CR	;<631>
	ELSE
DATEBUF	DB	'Day, Mon xx, 198x',CR
	ENDIF
MAXDAYS	DB	0,31,28,31,30,31,30,31,31,30,31,30,31
NODATE$	DB	'Date not in system',CR
BADDAT$	DB	'Bad Date format',CR
BADTIM$	DB	'Bad Time format',CR
;
;
DTBUF	EQU	$
DUMBUF	EQU	$+3
;
	END	DATE
