.Z80 ;TIME.COM 0000..FFFF ; ; Disassembly: John Elliott, 26-1-1999 ; ; Storage: This program uses at least four different storage formats for ; the year: ; ; * The RTC it talks to appears to store the year as BCD, 0-99, with 0 ; meaning 1984. ; * The program stores the year as BCD, 84-99, with 84 meaning 1984; ; * and as binary, 84-99, with 84 meaning 1984; ; * and as the CP/M 3 day count. ; ; For year 2000 fixes, I will assume: ; * The RTC works correctly past 2000, so it returns 16 for 2000. ; * The internal year variables are 84-99 for 20th century, 0-83 for 21st. ; ; As provided, the source will assemble to the original TIME.COM. ;Set Y2000 to YES for my tentative Year 2000 fixes. ; ; NO EQU 0 YES EQU NOT NO Y2000 EQU NO FDOS EQU 5 B0010 EQU 0010h B0011 EQU 0011h B0012 EQU 0012h B0013 EQU 0013h B0014 EQU 0014h FCB EQU 005Ch IF Y2000 ;[JCE] Might as well add in protection ;from DOS, while we're here DEFB 0EBh,04h ;DOS protection... JMPS LABE EX DE,HL JP MAIN DEFB 0B4h,09h ;DOS protection... MOV AH,9 DEFB 0BAh DEFW BVMES ;DOS protection... MOV DX,OFFSET BVMES DEFB 0CDh,021h ;DOS protection... INT 21h. DEFB 0CDh,020h ;DOS protection... INT 20h. DB 0Dh, ' TIME (Year 2000 vers.) by RSDA/PJM/JCE-',13,10 DB ' to provide CP/M+ with SCA MK2 date & time.' T013F: DB 1Ah BVMES: DB 'This is a CP/M program.',13,10,'$' ELSE ;Y2000 JP MAIN DB ' TIME by RSDA/PJM- to provide CPM+' DB ' with SCA MK2 date & time.' T013F: DB 1Ah ENDIF ;Y2000 MAIN: LD SP,T013F XOR A OUT (0E1h),A LD A,18h OUT (0E1h),A ;Reset the Z80-DART (Channel A) XOR A OUT (0E3h),A LD A,18h OUT (0E3h),A ;Reset the Z80-DART (Channel B) CALL UPAUSE LD A,(FCB+1) ;Get or set? CP ' ' ; 20h JP Z,GETTM SETTM: CALL INPDATE CALL SENDTIME JP L018C GETTM: LD A,3 ;Getting time? LD (RETRIES),A CALL ZAPTIME L016D: CALL RDTIME JP NZ,L017F ;Time has changed. Try again until it CALL YRPLUS84 ;stabilises. CALL BUF2BIN CALL ISVALID ;Time is valid? JP Z,L0189 ;Valid. Check for 29 Feb. L017F: LD HL,RETRIES ;Not got a valid time DEC (HL) JP NZ,L016D JP SETTM ;Clock is not set. Complain bitterly. L0189: CALL ROLL29 ;Special check for 29 Feb L018C: CALL SENDDATE CALL SETTM3 CALL SETTM2 CALL PRDATE JP 0 RDTIME: LD HL,CHANGED LD (HL),0 ;No change detected CALL STARTCMD LD A,0D0h CALL SENDBYT XOR A ;Send D0, 0 CALL SENDBYT CALL ENDCMD CALL STARTCMD LD A,0D1h ;Send D1 CALL SENDBYT INC HL CALL RDCK1 ;Hours INC HL CALL RDCK1 ;Mins INC HL CALL RDCK1 ;Days INC HL CALL RDCHEK ;Months CALL ENDCMD CALL STARTCMD LD A,0D0h ;Send D0, 5 CALL SENDBYT LD A,5 CALL SENDBYT CALL ENDCMD CALL STARTCMD LD A,0D1h CALL SENDBYT INC HL CALL RDCK1 ;Years INC HL CALL RDCK1 ;? INC HL CALL RDCHEK ;? CALL ENDCMD LD A,(CHANGED) OR A RET ZAPTIME: LD HL,CHANGED LD A,8 L01FB: LD (HL),0 INC HL DEC A JP NZ,L01FB RET RDCK1: CALL RECV1 ;Receive byte, acknowledge it, write with check JP L020C RDCHEK: CALL RECVBYTE ;Receive byte, write with check L020C: LD B,(HL) CP B RET Z LD (HL),A OR 1 LD (CHANGED),A RET YRPLUS84: ;Year += 84 LD A,(BCDYR) CALL BCD2BIN ;Convert packed BCD to binary ;; ;;Year 2000 << ;; IF Y2000 CP 16 JP C,G0001 ;Map 21st century into 1900-1983 ADD A,-100 G0001: ENDIF ;; ;;Year 2000 >> ;; ADD A,84 CALL BIN2BCD ;Convert binary to packed BCD LD (BCDYR),A RET SENDTIME: CALL STARTCMD LD A,0D0h CALL SENDBYT LD A,20h CALL SENDBYT CALL ENDCMD C0235: CALL STARTCMD LD A,0D0h CALL SENDBYT LD A,0 CALL SENDBYT LD HL,BCDHR ;Hours LD A,(HL) CALL SENDBYT INC HL LD A,(HL) ;Mins CALL SENDBYT INC HL LD A,(HL) ;Days CALL SENDBYT INC HL LD A,(HL) ;Months CALL SENDBYT CALL ENDCMD RET SENDDATE: CALL STARTCMD ;Send date. LD A,0D0h CALL SENDBYT LD A,5 CALL SENDBYT LD HL,BCDYR ;Send Y, D, M of all the bizarre orderings LD A,(HL) CALL BCD2BIN ;Convert packed BCD to binary ;; ;;Year 2000 << ;; IF Y2000 CP 84 ;Assume 1900-1983 are in 21st century JP NC,G0002 ADD A,100 ;Bring A back into the 21st century G0002: ENDIF ;; ;;Year 2000 >> ;; SUB 84 CALL BIN2BCD ;Convert binary to packed BCD CALL SENDBYT LD HL,BCDDY LD A,(HL) CALL SENDBYT INC HL LD A,(HL) CALL SENDBYT CALL ENDCMD RET DROP_DTR: LD A,5 OUT (0E1h),A LD A,68h OUT (0E1h),A JP UPAUSE RAISE_DTR: LD A,5 OUT (0E1h),A LD A,0E8h OUT (0E1h),A JP UPAUSE RESET_B: LD A,18h OUT (0E3h),A JP UPAUSE RAISE_RTS: LD A,5 OUT (0E3h),A LD A,6Ah OUT (0E3h),A UPAUSE: LD A,40h L02AF: DEC A JP NZ,L02AF RET GETBIT: LD A,10h OUT (0E3h),A CALL UPAUSE IN A,(0E3h) PUSH AF CALL UPAUSE POP AF XOR 0FFh AND 10h RRCA RRCA RRCA RRCA RET STARTCMD: CALL DROP_DTR CALL RESET_B CALL RAISE_RTS CALL RAISE_DTR RET ENDCMD: CALL RAISE_RTS CALL RAISE_DTR CALL DROP_DTR CALL RESET_B RET SENDBYT: LD C,8 ;8 bits LD B,A ;in B CALL RAISE_DTR L02EB: LD A,B RLCA LD B,A JP NC,L02F7 CALL RESET_B JP L02FA L02F7: CALL RAISE_RTS L02FA: CALL DROP_DTR CALL RAISE_DTR DEC C JP NZ,L02EB CALL DROP_DTR CALL RESET_B CALL GETBIT CALL RAISE_DTR RET RECVBYTE: LD C,8 LD B,0 CALL RAISE_DTR CALL RESET_B L031B: CALL DROP_DTR CALL GETBIT ADD A,B ADD A,B LD B,A CALL RAISE_DTR DEC C JP NZ,L031B LD A,B RET RECV1: CALL RECVBYTE PUSH AF CALL RESET_B CALL RAISE_RTS CALL DROP_DTR CALL RAISE_DTR CALL RESET_B POP AF RET T0342: DW 0,0,31,59,90,120,151,181,212,243,273,304,334 CPM3DY: DW 0 CPM3HR: DW 0 SETTM3: XOR A LD (CPM3DY+1),A LD A,(BINDY) LD (CPM3DY),A LD HL,T0342 LD A,(BINM) LD C,A LD B,0 ADD HL,BC ADD HL,BC LD C,(HL) ;Days from year start to start of month INC HL LD B,(HL) LD HL,(CPM3DY) ADD HL,BC ;Days from year start to today LD A,(BINYR) ;; ;;Year 2000 << ;; IF Y2000 CP 84 JP NC,G0003 ;Bring A back into the 21st century ADD A,100 G0003: ENDIF ;; ;;Year 2000 >> ;; LD B,A LD C,77 ;1977 (CP/M clock starts on 1-1-1978) LD DE,365 L0385: LD A,B ;Increase year until it matches INC C CP C JP Z,L0396 ADD HL,DE LD A,C ;Leap year fix AND 3 JP NZ,L0385 INC HL JP L0385 L0396: AND 3 ;Current year is leap year JP NZ,L03A4 LD A,(BINM) CP 3 JP M,L03A4 INC HL L03A4: LD (CPM3DY),HL LD HL,(BCDHR) LD (CPM3HR),HL LD C,68h ;CP/M set-time call LD DE,CPM3DY CALL FDOS RET SETTM2: LD A,(BCDM) LD (B0010),A LD A,(BCDDY) LD (B0011),A LD A,(BCDYR) LD (B0012),A LD A,(BCDHR) LD (B0013),A LD A,(BCDMN) LD (B0014),A RET INPDATE: LD C,9 LD DE,ENTDT$ CALL FDOS CALL INPLN LD DE,BCDDY LD HL,INPBUF+2 LD C,3 L03E8: CALL DEC2BCD INC DE DEC C JP NZ,L03E8 LD C,9 LD DE,ENTTM$ CALL FDOS CALL INPLN LD DE,BCDHR LD HL,INPBUF+2 LD C,2 L0403: CALL DEC2BCD INC DE DEC C JP NZ,L0403 CALL BUF2BIN CALL ISVALID RET Z LD C,9 LD DE,BADTM$ CALL FDOS JP INPDATE BADTM$: DB 0Dh,0Ah,'Date or Time not valid!',0Dh,0Ah,'$' DEC2BCD: LD B,0 L043B: LD A,B RLCA RLCA RLCA RLCA AND 0F0h LD B,A LD A,(HL) INC HL CP '0' ; 30h RET M CP ':' ; 3Ah RET P AND 0Fh OR B LD B,A EX DE,HL LD (HL),B EX DE,HL JP L043B INPLN: LD A,'*' LD HL,INPBUF LD B,(HL) ;Max len INC HL L045C: INC HL ;Fill buffer with *s LD (HL),A DEC B JP NZ,L045C LD C,0Ah LD DE,INPBUF CALL FDOS RET ENTDT$: DB 0Ah,0Dh,'Please enter date (DD/MM/YY format) $' ENTTM$: DB 0Ah,0Dh,'Please enter time (HH:MM) $' INPBUF: DB 8,0,'*********' ISVALID: LD HL,BINHR LD A,23 ;Validate hours CP (HL) JP M,L04EF INC HL LD A,59 ;Validate minutes CP (HL) JP M,L04EF INC HL XOR A CP (HL) JP Z,L04EF LD A,31 ;Validate days CP (HL) JP M,L04EF INC HL XOR A CP (HL) JP Z,L04EF LD A,12 ;Validate months CP (HL) JP M,L04EF INC HL LD A,99 ;Validate years CP (HL) JP M,L04EF XOR A RET L04EF: OR 1 RET ROLL29: LD HL,BINM LD A,(CANDIDATE) CP (HL) INC HL ;HL->year JP Z,L0504 JP M,L0504 INC (HL) ;Next year JP L0526 L0504: LD A,(HL) ;HL->year AND 3 RET NZ ;Leap year? DEC HL ;HL->month LD A,(HL) CP 3 RET NZ LD HL,CANDIDATE CP (HL) RET M RET Z DEC HL ;HL->day LD A,(HL) CP 29 RET Z LD HL,BINDY DEC (HL) LD A,(HL) OR (HL) JP NZ,L0526 LD (HL),29 ;Convert 0 March to 29 Feb INC HL LD (HL),2 L0526: LD DE,3 ; L0529: LD HL,BINMN ADD HL,DE LD A,(HL) CALL BIN2BCD ;Convert binary to packed BCD LD HL,BCDMN ADD HL,DE LD (HL),A DEC E JP NZ,L0529 CALL C0235 RET BUF2BIN: LD HL,CHANGED LD DE,BINBUF LD B,8 L0546: LD A,(HL) CALL BCD2BIN ;Convert packed BCD to binary LD (DE),A INC DE INC HL DEC B JP NZ,L0546 RET PRDATE: LD A,(BCDDY) LD HL,DECDAYS CALL BCD2ASC LD A,(BCDM) LD HL,DECMONTH CALL BCD2ASC LD A,(BCDYR) ;Does not need fixing for 2000 as BCDYR LD HL,DECYEAR ;is still 2 digits. CALL BCD2ASC LD A,(BCDHR) LD HL,DECHOUR CALL BCD2ASC LD A,(BCDMN) LD HL,DECMIN CALL BCD2ASC LD C,9 LD DE,DECDATE CALL FDOS RET BCD2BIN: PUSH AF RRCA RRCA RRCA RRCA AND 0Fh ;Low nibble ADD A,A LD C,A ADD A,A ADD A,A ADD A,C LD C,A ;C := A * 10 POP AF AND 0Fh ADD A,C RET BIN2BCD: LD C,0FFh L059C: INC C SUB 0Ah JP NC,L059C ;C := A / 10 ADD A,0Ah LD B,A ;B := A mod 10 LD A,C RLCA RLCA RLCA RLCA ADD A,B RET ;A := ((A / 10) << 4) | (A % 10) BCD2ASC: LD B,A ;Simplistic date writer, good for 0-99 RRCA RRCA RRCA RRCA AND 0Fh ADD A,'0' ; 30h LD (HL),A INC HL LD A,B AND 0Fh ADD A,'0' ; 30h LD (HL),A RET RETRIES: DB 0 CHANGED: DB 0 ;Buffer changed? BCDHR: DB 0 ;Time stored as BCD BCDMN: DB 0 BCDDY: DB 0 BCDM: DB 0 BCDYR: DB 0 DB 0 DB 0 BINBUF: DB 0 ;Time stored as binary BINHR: DB 0 BINMN: DB 0 BINDY: DB 0 BINM: DB 0 BINYR: DB 0 DB 0 CANDIDATE: DB 0 DECDATE: DB 0Dh,0Ah,0Ah,'Date ' DECDAYS: DB ' /' DECMONTH: DB ' /' DECYEAR: DB ' Time ' DECHOUR: DB ' :' DECMIN: DB ' ',0Dh,0Ah,'$' END