; MXC-ZS10.Z80
;
; MEX-Plus Clock Overlay for ZSDOS
;
; A hardware independent clock module, obtaining the time and date
; from ZSDOS.
;
; This module passes the current ZSDOS time and date to MEX for
; display. It also implements the CSET command to set the ZSDOS
; clock. There are two separate commands:
;
; CSET DATE {mm}/{dd}/{yy}
;
; and
;
; CSET TIME {hh}:{mm}:{ss}
;
; Entering all elements of a date or time specification is not
; necessary. Missing elements will be filled in from the current
; setting of the clock. In other words, if you just want to change
; the hour--perhaps to daylight saving time--enter:
; CSET TIME hh
; To adjust the minutes, use:
; CSET TIME :mm
; Or to tweek the seconds:
; CSET TIME ::ss
; The date parameter works the same way. As the examples indicate,
; leading delimiters are required, but trailing delimiters are not.
;
; If desired, date entry can be configured to use European format:
; CSET DATE dd.mm.yy
; See the EURDAT equate below.
;
; If ZSDOS returns no date and time, this clock module returns
; 12:00 a.m., January 1, 1978. Relative time is not supported.
;
; Please report any bugs.
; Gene Pizzetta
; 481 Revere St.
; Revere, MA 02151
;
; Voice: (617) 284-0891
; Newton Centre Z-Node: (617) 965-7259
; Ladera Z-Node Central: (213) 670-9465
;
; Version 1.0 -- June 16, 1991 -- Gene Pizzetta
; Based on MXC-DS10 overlay for DateStamper by Jim Lill, ZSLIB
; routines by Carson Wilson, and SYSLIB routines by Richard
; Conn.
;
; System addresses
;
Bdos equ 0005h
TPA equ 100h
;
; BDOS functions
;
ZSGTime equ 98 ; ZSDOS get time function
ZSSTime equ 99 ; ZSDOS set time function
;
; MEX functions
;
MEX equ 0D00h ; MEX function entry
LOOKUP equ 247 ; table search: see CMDTBL comments for info
SBLANK equ 244 ; scan input stream to next non-blank
GNC equ 241 ; get char from input, cy=1 if none
ILP equ 240 ; inline print
PRINT equ 9 ; simulated BDOS function 9: print string
;
; ASCII
;
BELL equ 07h
LF equ 0Ah
CR equ 0Dh
;
FALSE equ 0
TRUE equ not FALSE
;
; Date format -- Select here either American (mm/dd/yy) or European
; (dd.mm.yy) date format for entry of date specification to CSET
; processor. MEX always displays the date in American format.
;
EURDAT equ FALSE ; FALSE=American, TRUE=European
;
org TPA ; we begin
;
db 0C3h ; JP required by load
;
; Jump table for clock overlay
;
org 0E00h ; start of clock overlay
Start: jp GetTim ; get time
jp GetDat ; get date
jp CstCmd ; cset processor
ret ; clock overlay init
db 0,0
ret ; clock overlay de-init
db 0,0
;
; GetTim -- gets BCD time into DatTbl and then loads registers with
; binary equivalents for MEX:
; B = hours (0-23)
; C = minutes (0-59)
; D = seconds (0-59)
; E = hundredths of seconds (0-99)
;
GetTim: call GetZST ; get time from ZSDOS
jr z,GetTm1 ; (okay, continue)
ld bc,0 ; no time, return midnight
ld de,0
ret
;
GetTm1: ld a,(hours) ; BCD hours to A
call BcdBin
ld b,a ; binary hours to B
ld a,(minute) ; BCD minutes to A
call BcdBin
ld c,a ; binary minutes to C
ld a,(second) ; BCD seconds to A
call BcdBin
ld d,a ; binary seconds to D
ld e,0 ; no 100th's of second
ret
;
; GetDat -- gets BCD date into DatTbl and then loads registers with
; binary equivalents for MEX:
; BC = year (1978-2077)
; D = month (1=Jan, 2=Feb, etc.)
; E = day (1-31)
;
GetDat: call GetZST ; get date from ZSDOS
jr z,GetDt1 ; (okay, continue)
ld e,1 ; no date, return Jan. 1, 1978
ld d,e
ld bc,1978
ret
;
GetDt1: ld a,(day) ; BCD day to A
call BcdBin
ld e,a ; binary day to E
ld a,(month) ; BCD month to A
call BcdBin
ld d,a ; binary month to D
ld a,(year) ; BCD year (00-99) to A
call BcdBin
ld c,a ; binary year in C
ld b,0
ld hl,1900 ; add century
cp 78
jr nc,GetDt2
ld hl,2000
GetDt2: add hl,bc
ld c,l ; put year in BC
ld b,h
ret
;
; CSET Processor for ZSDOS
;
CstCmd: ld c,SBLANK ; any arguments?
call MEX
jr c,CstErr ; if not, display defaults
ld de,CstTbl
ld c,LOOKUP
call MEX ; parse the argument
push hl ; save any parsed arguments
ret nc ; ..and return to it
pop hl ; not found
CstErr: ld de,CstEms
ld c,PRINT
call MEX
ret
;
CstEms: db BELL,' CSET syntax error -- "CSET ?" for help',CR,LF,'$'
;
CstTbl: dc '?' ; help
dw CstHlp
dc 'DATE' ; set date
dw CsDate
dc 'TIME' ; set time
dw CsTime
db 0 ; end of table
;
; CSET with "?" prints help message
;
CstHlp: call SILP
db ' CSET Options:',CR,LF
db ' DATE '
IF EURDAT
db '{dd}.{mm}.{yy}'
ELSE
db '{mm}/{dd}/{yy}'
ENDIF ; EURDAT
db ' (sets ZSDOS date)',CR,LF
db ' TIME {hh}:{mm}:{ss} (sets ZSDOS time)',CR,LF
db 0
ret
;
; CsDate -- sets ZSDOS system date
;
CsDate: call MvDat ; move date to storage
jp c,CstErr ; (none given)
call GetZST ; fill DatTbl
jr nz,ClkErr ; (clock error)
call PrsDat ; parse date and make BCD
jp nz,CstErr ; (invalid date)
call SetZST ; set date
jr nz,ClkErr ; (clock error)
call SILP
db 'ZSDOS Date Set',0
ret
;
; CsTime -- sets ZSDOS system time
;
CsTime: call MvDat ; move time to storage
jp c,CstErr ; (none given)
call GetZST ; fill DatTbl
jr nz,ClkErr ; (clock error)
call PrsTim ; parse time and make BCD
jp nz,CstErr ; (invalid time)
call SetZST ; set time
jr nz,ClkErr ; (clock error)
call SILP
db 'ZSDOS Time Set',0
ret
;
ClkErr: call SILP
db BELL,'ZSDOS clock error',0
ret
;
; PrsDat -- parses date specification string. (Modified from ZSLIB's
; ZSPARSDS module by Carson Wilson.)
;
PrsDat: ld de,DatTbl ; point to BCD date
ld hl,DatStr-1 ; point to command line date
push de
ld b,d
ld c,e
inc bc ; BC --> storage + 1 (month)
IF EURDAT
inc bc ; BC --> storage + 2 (day)
ENDIF ; EURDAT
;
; Test month (or day if EurDat)
;
call GetNxt ; get next datespec character or abort
cp '.' ; got character, use default month?
jr z,TestDy ; (yes)
cp '/'
jr z,TestDy
call IsDgt ; digit?
jp nz,ErExit ; (no)
call eval16 ; must be day spec. SYSLIB evaluates ASCII
; ..hex to binary and points HL to next
TstMon: ld (bc),a ; save value
ld a,(hl) ; get next
cp '.' ; day spec?
jr z,TestDy
cp '/'
jp nz,PsExit ; no, done
;
TestDy:
IF EURDAT
dec bc ; BC --> storage + 1 (month)
ELSE
inc bc ; BC --> storage + 2 (day)
ENDIF ; EURDAT
call GetNxt ; get/abort
cp '.' ; got character, use default day?
jr z,TestYr ; (yes)
cp '/'
jr z,TestYr
call IsDgt ; digit?
jp nz,ErExit ; (no)
call eval16 ; evaluate day
TestD1: ld (bc),a ; save value
ld a,(hl)
cp ' '
jr z,PsExit
cp '.' ; got year?
jr z,TestYr
cp '/'
jr nz,PsExit ; (no)
;
TestYr: dec bc ; point to year
IF NOT EURDAT
dec bc
ENDIF ; NOT EURDAT
call GetNxt ; get/abort
cp ' ' ; use default year?
jr z,PsExit ; (yes)
call IsDgt ; digit?
jr nz,ErExit ; (no)
call eval16 ; evaluate year
TestY1: ld (bc),a ; save value
jr PsExit ; (no, done)
;
; PrsTim -- parses time specification string
;
PrsTim: ld bc,Hours
ld de,DatTbl
ld hl,DatStr-1
push de
TestHr: call GetNxt ; get next command character
cp ':' ; use default hour?
jr z,TestMn ; (yes)
call IsDgt ; digit?
jr nz,ErExit ; (no, error)
call eval16 ; get hour
ld (bc),a ; save value
ld a,(hl)
cp ':' ; got minute?
jr nz,PsExit ; (no, done)
;
TestMn: inc bc ; point to minute
call GetNxt ; minute or wildcard
cp ':' ; use default minute?
jr z,TestSc ; (yes)
cp ' '
jr z,PsExit
call IsDgt ; digit?
jr nz,ErExit ; (no)
call eval16 ; evaluate minute
ld (bc),a ; save value
ld a,(hl)
cp ':' ; got second?
jr nz,PsExit ; (no, done)
;
TestSc: inc bc ; point to second
call GetNxt ; second
cp ' '
jr z,PsExit
call IsDgt ; digit?
jr nz,ErExit ; (no)
call eval16 ; evaluate second
ld (bc),a ; save value
;
PsExit: pop de ; point to stored date
ex de,hl ; check value at HL
call IsBcdd
ex de,hl ; restore DE
ret ; return (Z) if date OK.
;
ErExit: pop de
ret
;
; GetNxt -- get next date/time spec character for PrsDat and PrsTim
; On entry HL = address of next datespec position minus 1. On exit
; HL incremented by 1 and A = character pointed to by HL.
;
GetNxt: inc hl ; next input
ld a,(hl)
or a ; done?
ret nz ; (no)
pop de ; yes, remove return address
jr PsExit ; ..and exit parse
;
; IsBcdD -- Test BCD date and time at HL for validity. Returns NZ
; on error. (Modified from ZSLIB's ZSISBCDD module by Carson Wilson.)
;
IsBcdD: push hl
ld a,(hl) ; BCD year
call IsBcd
jr nc,NotBcd
inc hl ; month
ld a,(hl)
or a
jr z,NotBcd
cp 13h
call c,IsBcd
jr nc,NotBcd
inc hl ; day
ld a,(hl)
or a
jr z,NotBcd
cp 32h
call c,IsBcd
jr nc,NotBcd
inc hl ; hour
ld a,(hl)
cp 24h
call c,IsBcd
jr nc,NotBcd
inc hl ; min
ld a,(hl)
cp 60h
call c,IsBcd
jr nc,NotBcd
inc hl ; sec
ld a,(hl)
cp 60h
call c,IsBcd
jr nc,NotBcd
pop hl
xor a ; return Z no error
ret
NotBcd: ld a,(hl) ; return NZ for error
pop hl
or 0FFh
ret
;
; IsBcd -- Test if byte in A is BCD. Carry set (C) if byte is BCD.
;
IsBcd: cp 09Ah ; see if nibbles in 0..9
ret nc ; not BCD if > 99
and 00001111b ; test right nibble
cp 00Ah
ret
;
; SetZST -- Set current date and time from DatTbl. Returns A=0 and
; zero flag set (Z) if clock set, zero flag reset (NZ) on error.
; (Modified from ZSLIB's ZSGSTIME module by Carson Wilson.)
;
SetZST: ld c,ZSSTime
jr GetSet
;
; GetZST -- Get current date and time to DatTbl. Returns A=0 and zero
; flag set (Z) if buffer filled, zero flag reset (NZ) on error.
;
GetZST: ld c,ZSGTime
;
GetSet: ld de,DatTbl ; point DE to buffer
call Bdos
dec a ; ZSDOS returns 1 if okay
ret
;
; IsDgt -- returns Zero Flag Set if char in A is numeric (0-9).
; Returns NZ if not. Char in A is unaffected. (Modified from
; SYSLIB 3.6 SISDIGIT module by Richard Conn.)
;
IsDgt: push bc ; save BC
ld c,a ; save character in C
and 7Fh ; mask out MSB
cp '0' ; less than 0?
jr c,NoDgt
cp '9'+1 ; less than or equal to 9?
jr nc,NoDgt
xor a ; set flag
ld a,c ; get character
pop bc ; restore BC
ret
;
NoDgt: ld a,0FFh ; set flag
or a
ld a,c ; get character
pop bc ; restore BC
ret
;
; Eval16 -- Convert the string of ASCII hexadecimal digits pointed to
; by HL into a binary value; string is converted until invalid digit is
; encountered. On return, HL points to error character, DE=value,
; A=E (low order 8 bits of value). BC not affected. (SYSLIB 3.6
; SEVAL2 module by Richard Conn.)
;
Eval16: push bc ; save BC
ld de,0 ; set DE = 0 initially
; Get next digit and check for '0' - '9'
E16L: ld a,(hl) ; get byte
call Caps ; capitalize
cp '0' ; check for range
jr c,Done
cp 'F'+1 ; check for range
jr nc,Done
cp '9'+1 ; check for 0-9
jr c,ProDec
cp 'A' ; check for out of range
jr c,Done
ProDec: sub '0' ; convert to binary
cp 10
jr c,Proc
sub 7 ; adjust for 'A'-'F'
; Proceed with processing
Proc: push af ; save value
; Multiply DE by 16
Mul16: push hl ; save HL
ld hl,0 ; Acc=0
ld b,16 ; 16 loops
Mul16L: add hl,de ; HL = HL + DE
dec b ; count down
jr nz,Mul16L
ld d,h ; new DE
ld e,l
pop hl ; restore HL
; Add in A
pop af ; get latest digit
add a,e ; A = A + E
ld e,a
ld a,d ; add to D if necessary
adc 0
; Continue
inc hl ; point to next character
jr E16L
; Done -- result already in DE; set A = E
Done: ld a,e ; A = E
pop bc ; restore BC
ret
;
; BcdBin -- packed BCD in A converted to binary in A.
;
BcdBin: push bc
ld c,a ; move value to C
and 0F0h ; mask lower nibble
rra ; move upper nibble into lower
rra
rra
rra
ld b,a ; times 1
add a
add a ; times 4
add b ; times 5
add a ; times 10
ld b,a ; 10's digit to B
ld a,c ; lower digit to A
and 0Fh
add b ; combine digits
pop bc
ret
;
; Caps -- Capitalize ASCII Character in A. (SYSLIB 3.6 SCAPS module
; by Richard Conn.)
;
Caps: and 7Fh ; mask out MSB
cp 61h ; less than lower-case a?
ret c
cp 7Ah+1 ; between lower-case a and z?
ret nc
and 5Fh ; reset bit 5 to capitalize
ret
;
; MvDat -- moves date or time from command line to storage
;
MvDat: ld c,SBLANK ; any arguments?
call MEX
ret c ; (if not, error)
ld hl,DatStr ; point to string storage
ld b,8 ; eight characters maximum
ld c,GNC
MvDat1: push hl
push bc
call MEX
pop bc
pop hl
jr c,MvDat2
ld (hl),a
inc hl
djnz MvDat1
MvDat2: xor a ; zero A and clear carry
ld (hl),a ; insert a final null
ret
;
; SILP -- In-line print routine (calls MEX)
;
SILP: ld c,ILP
jp MEX
;
; DatTbl -- BCD time and date storage
;
DatTbl:
Year: db 0 ; 00 - 99
Month: db 0 ; 1 - 12
Day: db 0 ; 1 - 31
Hours: db 0 ; 00 - 23
Minute: db 0 ; 00 - 59
Second: db 0 ; 00 - 59
;
DatStr: ds 9 ; command line date/time storage
;
end