;GT_OSD Identifies Operating system and Date/time protocol ;Returns a pointer to a data structure which ;contains DOS identity, CPM compatibility, DOS version, and ;data for system clock access. A separate entry (RDCLK) provides ;for reading the clock. ;CALL WITH: (no calling parameters) ;RETURN WITH: ; CY set = unknown operating system ; Cy clear = CP/M, CPM+, ZRDOS, ZSDOS, or ZDDOS ; and System Clock set up for date/time access. ; HL -> OS data structure public gt_osd bdos equ 5 cpmvsn equ 12 ;return CP/M version number zrdvsn equ 48 ;return ZS/ZD/ZRDOS version number GT_OSD: ld a,-1 ;show function already performed ld (gt_osf),a ;call BDOS function 12 (0ch) to return CP/M version ld c,cpmvsn ld e,'D' ;for datestamper call bdos ld (cpm_eq),hl ;(ds_flg)<-h ld a,l or a ;return Z if no CP/M compatible OS scf ;indicate non-compatible OS jr z,exit ;return CY if not cpm2.2 compatible ld (os_vsn),a ;actual version if CP/M ld a,'C' ;assume CP/M ld (os_idc),a ;if Datestamper is present with CP/M or ;CP/M compatible OS, then make it the default ;clock method, to be superseded by ZS/ZD if ;they are implemented for the RTC or DS. ck_4ds: ld a,h cp 'D' ;datestamper installed? jr nz,notds ;skip if not ;Datestamper is installed, so use it for Date/Time ld (clkadr),de ld (os_dtc),a ;make non-zero for rdclk notds: ld a,l ;recover CP/M version cp 30h ;CPM+?? jr c,..zsdr ;jump if not ld hl,os_idc ld (hl),'3' ;identify CPM+ inc hl ;->os_dtc ld (hl),105 ;set time function call jr bdclok ;set bdos clock call and exit ;Call BDOS function 48 (30h) to return zrdos/zsdos/zddos version ..zsdr: ld c,zrdvsn call bdos ;version in reg L and A or a ;is it ZS/ZD/ZRDOS? jr z,gt_rtc ;done if not ZS/ZD/ZRDOS ld (os_vsn),hl ;else update actual version #, ID CHAR ld a,h or a ;if z, then it's ZRDOS jr nz,gt_rtc ld a,'R' ld (os_idc),a jr exit ;use Datestamper if present, else ;no clock or user will implement ;ZS/ZDDOS has been identified. Check for RTC, and ;install parameters in the data block if a clock ;is present. Return with (clkadr) & (dt_buf) ;unchanged if RTC is unimplemented in DOS. gt_rtc: ld a,(os_idc) ;if ZDDOS, kill DateStamper default cp 'D' jr nz,gtrtc1 ;skip if not ZDDOS ld hl,0 ld (clkadr),hl ;remove clock call address ld a,l ld (ds_flg),a ;kill possible DateStamper flag gtrtc1: ld c,98 ;ZS/ZD clock read function ld de,dtbuf ;place for Date, Time call bdos inc a ;ret a=-1 if unimplemented clock jr z,exit ;..skip bdos call setup if a=-1 ;RTC is implemented in ZS/ZD, so install function number ;and bdos address as clock calling parameters in the ;OS data structure ld c,98 ;ZS/ZD clock read function ld (os_dtc),a ;CPM+, ZSDOS, and ZDDOS use dos call for RTC access bdclok: ld hl,bdos ;place to call for date/time ld (clkadr),hl ;make it public exit: ld hl,cpm_eq ;return pointer to data structure ret ;=========================================================== ;data structure for OS ;DOS identifying characters ; 0 = Unknown Dos 3 = CP/M 3.0 ; C = CP/M M = MP/M O = ZOS ; R = ZRDOS S = ZSDOS D = ZDDOS cpm_eq: db 0 ;Compatible CP/M version # ds_flg: db 0 ;D = DateStamper, else 0 os_vsn: db 0 ;Actual DOS version # os_idc: db 0 ;single character identifies DOS os_dtc: db 0 ;value in C during dos call for DT clkadr: dw 0 ;address of clock routine - fills DT buffer dt_buf: dw dtbuf ;address of buffer for DT ;end structure for OS ;=========================================================== gt_osf: db 0 ;nz=gt_osd has been called ;buffer for date/time for read/write system clock dtbuf: ds 6,0 ;=========================================================== ;RDCLK - routine to read the system clock ;CALL WITH: (no parameters) ;RETURN WITH: ; NC = NO CLOCK ; CY set = clock has been read ; HL -> date/time buffer, 6 bytes ;For CPM+, the first byte is undefined. Bytes 1,2 contain ;the number of days since 1 Jan 1978. Bytes 3 - 5 contain ;bcd hours, min, sec. (same as ZSDOS). For ZSDOS, ZDDOS, ;and DATESTAMPER, the first three bytes are Year, Month, ;and Day. All are in BCD. Year is last two digits. public rdclk rdclk: ld a,(gt_osf) ;valid data structure flag or a ;has data structure been filled? call z,gt_osd ;fill it if not. ld hl,(clkadr) ld a,h or l ;test for Z=no clock ld a,-1 ;0ffh = ZS/ZDdos standard/no clock jr z,clkret ;..and do nothing if so. ld a,(os_dtc) ;BDOS function, or argument for ld c,a ;user implemented clock driver ld hl,(dt_buf) ;->clock buffer ld a,(os_idc) cp '3' ;CPM+?? jr nz,rdclk1 ;nz=no inc hl ;use last 5 bytes of the buffer rdclk1: ld e,l ;D/T buffer in HL for Datestamper, ld d,h ;..and in DE (for DOS) push iy ld iy,(clkadr) call regind ;register indirect call to RTC ld a,(os_idc) ;dos ID flag cp '3' ;CP/M+?? call z,time30 ;convert to Universal Time format if so pop iy scf ;like DateStamper, show good read ld a,1 ;ZS/ZDdos return for good read clkret: ld hl,(dt_buf) ;ret HL -> DT buffer ret regind: jp (iy) ;call here for reg.ind. call ;=========================================================== ;TIME - Read the system clock, return ASCII date/time ;CALL WITH: (no calling parameters) ;RETURN WITH: ; HL -> the ASCII date/time string ; FLAGS: C = valid null terminated D/T string ; NC = no clock, string starts with null entry time time: call rdclk ;reads CPM+, DS, ZD/ZSDOS clocks ld hl,(dt_buf) ;source of BCD ZSDOS/DS time data call c,ascidt ;fill buffer if clock is present ld hl,ascdat ;->ascii date/time ret ;=========================================================== ;ASCIDT - Convert BCD standard date/time to formatted ASCII ;Standard packed BCD input string is yy mm dd hh mm ss format. ;Produces an ASCII string like yy/mm/dd hh:mm which is null ;terminated. ;CALL WITH: ; HL -> BCD date/time in standard 5-byte form ;RETURN WITH: ; HL -> Formatted ASCII Date/Time string ;the string is null terminated entry ascidt ext ma2hc ascidt: ;transform the ymdhms form to mdyhms ;which people are more used to. ld a,(hl) ;year or a ret z inc hl ;->month ld de,hdtdat ;local copy in new form push de ldi ;copy month,day ldi ld (de),a ;insert the year inc de ldi ;copy time bytes ldi pop hl ;->reformatted date/time data ;convert to ascii in fixed format ld de,ascdat ;destination for date/time ld b,5 ;number of bytes in Date/Time data stloop: ld a,(hl) inc hl call ma2hc ;HEX->ASCII (same as BCD->ASCII) inc de ;skip a destination separator djnz stloop ld hl,ascdat ;->ascii date/time scf ;cy set shows valid time ret hdtdat: ds 5 ;for m y d h m data (packed bcd) ascdat: db 0,' / / ' ;initial 0 replaced on valid read ;..in formatted date string db ' ' ;format space or early terminator db ' : ' ;formatted time string db 0 ;string terminator ;=========================================================== ;TIME30 - Translate Julian Date to ZS/ZDdos & DS format ;translate the julian date at dtbuf+1 to packed BCD ;yy,mm,dd in the three bytes at dtbuf. ;CALL WITH: (no calling parameters) ;RETURN WITH: ; HL -> Day field of the Universal Date buffer ; BC, DE destroyed entry time30 time30: ld hl,(dt_buf) ;->date/time buffer push hl inc hl ;second byte, where CPM+ stores ld a,(hl) ;..the julian date inc hl ld h,(hl) ld l,a ;julian date in HL call jul2bin ;to bin in c,b,l=a for y,m,d ld d,b ;..in de, 'cause b2bcd munches bc ld e,c ; ex af,af' ;save day for later pop hl ;-> loc for packed BCD date string push af ;save day for later ld a,c ;year call b2bcd ;to packed bcd ld (hl),a inc hl ld a,b ;month call b2bcd ;to packed bcd ld (hl),a inc hl ; ex af,af' ;recover day pop af ;recover day call b2bcd ;to packed bcd ld (hl),a ret ;======================================================== ;JUL2BIN - Julian to binary date routine. ; author: Bridger Mitchell & Howard Goldstein ; date: 4/16/1988 ;converts the 16 bit Julian date provided by CPM 3.0 ;to 3 binary bytes representing year, month, day of ;the Gregorian calander. Only the last 2 digits of ;the year are returned; years 1900 to 1999 are assumed. ;CALL WITH: ; hl = days since 77/12/31 (Julian date) ;RETURN WITH: ; b = month (1=jan...) ; c = year (78...) ; l = day (1...31) ; a = day (copy of l) ; entry jul2bin ; convert hl = days to yr,mo,da ; jul2bin: PUSH DE ld c,78 ;set yr = 78 yrlp: ld b,1 ;set mo = 1 ld de,dpermo ;point at table molp: ld a,(de) ;get # days in mo push de ld e,a ld d,0 or a sbc hl,de ;subtract days in mo jr c,j2bin9 ;done if <= 0 jr z,j2bin9 ld a,b ;if mo == Feb cp 2 jr nz,j2bin3 ld a,c ;..and yr%4 == 0 and 00000011b jr nz,j2bin3 dec hl ;have feb. of leap year ld a,h ;if days left was 1 (Mar 1) or l jr nz,j2bin3 ld l,29 ;..it's Feb. 29th jr j2bin8 j2bin3: pop de ;mo tbl inc de ;point at next month inc b ;mo++ ld a,b cp 13 jr c,molp ;do next month inc c ;yr++ jr yrlp ;do next year ; ; done -- j2bin9: add hl,de ;add back subtracted size of next month j2bin8: pop de ;clear stack ld a,l ; # days in cur. month POP DE ret ; ; ; ; table of days per month (non-leap year) ; ; entry dpermo dpermo: db 31 ;jan db 28 ;feb db 31 ;mar db 30 ;apr db 31 ;may db 30 ;jun db 31 ;jul db 31 ;aug db 30 ;sep db 31 ;oct db 30 ;nov db 31 ;dec ;======================================================== ;B2BCD - Convert binary in A to BCD in A ;convert binary byte to packed BCD, with units ;and 10's in A and 100's in C on return. ;CALL WITH: ; A = binary value to convert ;RETURN WITH: ; A = bcd tens,ones ; B = tens in high nibl ; C = hundreds ;FLAGS: Z = number <100, NZ = number >99 public b2bcd b2bcd: ld bc,0ffffh b2bcd1: inc c sub 100 jr nc,b2bcd1 add a,100 b2bcd0: inc b sub 10 jr nc,b2bcd0 add a,10 rlc b rlc b rlc b rlc b or b inc c dec c ret ;======================================================== end