; -=[ SETCLOCK - Sets Date & Time on Real-Time Clock/Calendar ]=- ; ; Hardware specific for Oki MSM5832 real-time clock/calendar accessed ; via port B of Z80 PIO (port 79h = data, port 7Bh = control) or via ; 8255 parallel interface (port 30h = clock data, port 31h = clock address, ; port 32h = clock control, port 33h = 8255 control) ; ; Written in 8080 mnemonics for assembly with ASM (or equivalent) by John ; D. Osnes, 1084 6th Street #41, Albany, CA (9/30/86). The author grate- ; fully acknowledges Mike Allen for his contribution of a clock design for ; Morrow Designs Rev.2 computers accessed via an 8255 parallel interface ; and for the corresponding READTIME and SETTIME routines which the 8255 ; option herein is based on. ; ; USER BE AWARE: the author has a Legacy clock accessed via a Z80 PIO, so ; the 8255 option in SETCLOCK is UNTESTED. What should work may not and no ; fault should be attributed to Mike Allen's fine work. For that matter, ; the author specifically disclaims any warranties, implied or otherwise, ; as to the performance and suitability of SETCLOCK. ; ; ADDITIONAL NOTES BY MIKE ALLEN: I am quite indebted to John for not only ; this exceptional program, but for the matching CLOCKOUT.ASM program as well ; as his additions to the code in CADPRINT.ASM for the OKI 92 and 93 printers. ; The above 'USER BE AWARE' notice is no longer valid. I have checked this and ; the CLOCKOUT routines with the 8255 option on my home-brew 8255 clock ; installed in my MD3 and they function as advertised. Please note that the ; 'HIGHLIGHT' code I have included is for reverse video on the Morrow MDT60/70 ; series of terminals which may not be applicable to your particular terminal. ; Mike Allen 11/16/86 ; ; FALSE EQU 0 TRUE EQU NOT FALSE ORG 0100H JMP CLOCK VERSN: DB VERSN2,CR,LF DB ' SETCLOCK v1.0 for Oki MSM5832 Clock/Calendar',CR,LF DB '(SPACE increments value, CR enters it, ^C aborts)',CR,LF DB LF VERSN2 EQU $-VERSN-1 ; *********************** USER SPECIFICATIONS ************************** ; If terminal has highlighting feature, specify start/stop sequence in ; form: #bytes, byte1, byte2, ... (simply zero the first byte of each ; sequence if terminal does not have highlighting feature). HION: DB 3 ; Sequence to start-highlighting DB ESC,'G','4',0,0 ; MDT60/70 Reverse Video. DB 0,0,0,0,0 HIOFF: DB 3 ; Sequence to stop-highlighting DB ESC,'G','0',0,0 ; MDT60/70 Normal Video DB 0,0,0,0,0 HIPAR: DB 0 ; If parity bit of highlighted chars ; must be set, make HIPAR 80h; otherwise, ; HIPAR must be zero! ; ********************* END OF USER SPECIFICATIONS ************************ ; ************************ ASSEMBLY-TIME OPTIONS ************************** SPEED EQU 40 ; 10 times speed of CPU in MHz (40=4 MHz) Z80PIO EQU FALSE ; If true, clock accessed via Z80 PIO ; If false, clock accessed via 8255 IF Z80PIO ; Z80 PIO ports (defaults based on Legacy clock) CLKDAT EQU 79H ; Data port for clock (Z80 PIO port B) CLKCTL EQU 7BH ; Control port for clock (Z80 PIO port B) ENDIF ; Z80PIO IF NOT Z80PIO ; 8255 ports (defaults based on Mike Allen's ; design for Morrow Designs Rev.2 clock) CLKDAT EQU 30H ; Data port for clock (8255 port A) CLKADR EQU 31H ; Address port for clock (8255 port B) CLKCTL EQU 32H ; Control port for clock (8255 port C) PRTCTL EQU 33H ; 8255 control port ENDIF ; NOT Z80PIO (8255) ; *************************** END OF OPTIONS ****************************** BDOS EQU 0005H ; BDOS entry point DCONIO EQU 06H ; BDOS "Direct Console I/O" function CNTLC EQU 'C'-'@' ; ^C=abort BELL EQU 'G'-'@' ; ^G=bell BS EQU 'H'-'@' ; ^H=backspace LF EQU 'J'-'@' ; ^J=linefeed CR EQU 'M'-'@' ; ^M=carriage return ESC EQU '['-'@' ; ^[=escape CLOCK: LXI H,0 ; Set up local stack DAD SP SHLD LSTACK LXI SP,LSTACK LXI H,VERSN ; Display signon msg CALL DSTRNG CALL SWPCLK ; Sweep thru 11 addresses of clock ; and store in memory (sec's not read) LXI H,MIN ; Convert BCD digits to binary values LXI D,NUMIN CALL BCDBIN ; Minutes MOV B,M ; Hours INX H MOV C,M MVI A,8 ANA C STAX D ; 12/24-hour clock INX D MVI A,0 JNZ CLOCK2 MVI A,4 CLOCK2: ANA C STAX D ; a.m./p.m. if 12-hour clock INX D MVI A,3 ANA C MOV C,A CALL BCDBI2 ; Hours INX H ; Skip day-of-week INX D MOV B,M INX H MOV A,M ANI 3 MOV C,A CALL BCDBI2 ; Day-of-month CALL BCDBIN ; Month CALL BCDBIN ; Year LDA FG1224 ; Get 12/24-hour selection ORA A CLOCK4: MVI A,12 JZ CLOCK6 MVI A,24 CLOCK6: CALL HIDECI PUSH PSW LXI H,HRCLK CALL DSTRNG POP PSW CALL INPUT JNZ CLOCK8 CPI 24 JMP CLOCK4 CLOCK8: RRC ; A=12 or 24 selection ANI 8 LXI H,FG1224 CMP M ; 12/24-hour selection changed? JZ CLCK11 ; NO MOV M,A ; YES, store selection INX H JC CLCK10 ; Carry set if 24-hour -> 12-hour MOV A,M ; 12-hour -> 24-hour ORA A ; Is current time a.m.? JZ CLCK11 ; YES MVI M,0 ; NO, so clear p.m. flag INX H MOV A,M ; Add 12 to current hour ADI 12 MOV M,A JMP CLCK11 CLCK10: INX H ; 24-hour -> 12-hour MOV A,M ; Current hour > 11? SUI 12 JC CLCK11 ; NO MOV M,A ; YES, so hour-12 p.m. DCX H MVI M,4 CLCK11: MVI D,HRCLK2+1 ; Clear line of display CLCK12: MVI E,' ' CALL REPEAT MVI E,CR CALL DCHAR LDA NUMNTH ; Display current date CALL DECIML MVI E,'/' CALL DCHAR LDA NUDAY CALL DECIML MVI E,'/' CALL DCHAR LDA NUYEAR CALL DECIML MVI E,CR CALL DCHAR LDA NUMNTH ; Get month MVI B,1 MVI C,13 CALL GETVAL STA NUMNTH CALL DECIML MVI E,'/' CALL DCHAR DCR A ; Get day-of-month LXI H,DOM ADD L MOV L,A MVI A,0 ADC H MOV H,A MOV C,M INR C LDA NUDAY CALL GETVAL STA NUDAY CALL DECIML MVI E,'/' CALL DCHAR LDA NUYEAR ; Get year LXI B,100 CALL GETVAL STA NUYEAR CALL DECIML ORA A ; Leap year if year is multiple of 4 JZ CLCK14 ; but not zero (for 20th century) ANI 3 JNZ CLCK14 MVI A,4 ; Leap year STA LYFLAG JMP CLCK16 CLCK14: LXI H,NUDAY ; Not leap year, so can't be Feb 29 MOV A,M CPI 29 JNZ CLCK16 INX H MOV A,M CPI 2 JNZ CLCK16 LXI H,FEB29 ; Feb 29 in non-leap year, so display CALL DSTRNG ; error msg and wait for user to read LDA NUYEAR ; it CALL DECIML LXI H,WAIT CALL DSTRNG CALL INPUT MVI E,CR ; Clear error msg CALL DCHAR MVI D,FEB292+WAIT2+9 JMP CLCK12 ; Get date again CLCK16: LXI H,NUYEAR ; Calculate day-of-week (algorithm for MVI D,0 ; 20th century courtesy DDJ, Jun'83) MOV E,M ; DE=year DCX H MOV A,M ; A=month MOV H,D SUI 3 ; Jan or Feb? JNC CLCK18 ; NO ADI 12 ; YES, so add 12 to month DCX D ; and substract 1 from year CLCK18: MOV L,A ; HL=month-3 MOV A,D ; Date before Mar 1, 1900? ORA A JZ CLCK20 ; NO MOV D,H ; YES, so calculate day in 20th century MOV A,L ; A=month (10=Jan, 11=Feb) MOV L,H CPI 10 ; Jan, 1900? JZ CLCK22 ; YES, so day in 20th century=day of month MVI L,31 ; NO, so day in 20th century=day of month+31 JMP CLCK22 CLCK20: XCHG ; DE=month, HL=year PUSH D ; Save month LXI D,1461 ; Multiply year by 1461 CALL MULWW LXI B,4 ; Divide product by 4 CALL DIVLW POP H ; Restore HL=month PUSH D ; Save INT((1461*year)/4) LXI D,153 ; Multiply month by 153 CALL MULWW INX H ; Add 2 to product INX H LXI B,5 ; Divide sum by 5 CALL DIVLW ; DE=INT((153*month+2)/5) POP H ; Restore HL=INT((1461*year)/4) DAD D ; HL=days from 3/1/1900 to month/1/19yr LXI D,3 ; Add offset of 3 to days to make 3/1/1900 DAD D ; be day-of-week 4 (Thursday) CLCK22: LDA NUDAY MOV E,A ; DE=day of month DAD D ; HL=days from 3/1/1900 to date + 4 MOV E,D LXI B,7 ; Divide days by 7 CALL DIVLW ; L=day-of-week (0=Sunday) MOV A,L STA NUDOW MVI D,2 ; Skip a couple spaces MVI E,' ' CALL REPEAT LDA FG1224 ; 12-hour clock? ORA A JNZ CLCK24 ; NO, 24-hour clock MVI A,STC ; YES, so modify GETVAL routine to display STA GETVA6 ; a.m./p.m. when hour increments 11 -> 0 LXI B,12 LDA AMPMFG ; a.m.? ORA A JZ CLCK26 ; YES, msg for a.m./p.m. display OK MVI A,'p' ; NO, change msg for display to p.m. STA AMPM JMP CLCK26 CLCK24: MVI A,AMPM-TIMSG-2 ; 24-hour clock, so don't display a.m./p.m. STA TIMSG LXI B,24 CLCK26: CALL DTIME4 ; Display time LDA NUHOUR ; Get hour CALL GETVAL STA NUHOUR CALL DECIML MVI E,':' CALL DCHAR MVI A,0B7H ; Restore GETVAL routine with op code STA GETVA6 ; B7h = ORA A (if 12-hour clock) LDA NUMIN ; Get minute LXI B,60 CALL GETVAL STA NUMIN CALL DECIML LXI H,TIMSG CALL DSTRNG LXI D,NUYEAR ; Convert date/time to BCD digits LXI H,YEAR+1 CALL BINBCD ; Year CALL BINBCD ; Month CALL BINBCD ; Day-of-month LDAX D ; Day-of-week (no conversion) DCX D MOV M,A DCX H CALL BINBCD ; Hour LDAX D ; a.m./p.m. flag MOV B,A DCX D LDAX D ; 12/24-hour flag ORA B PUSH PSW ; Save flags DCX D CALL BINBCD ; Minute MOV A,M ; Leap-year flag MVI M,0 ; Zero 10's digit of seconds LXI H,DAY+1 ; Add leap-year flag to 10's digit of ORA M ; day-of-month MOV M,A POP PSW ; Restore a.m./p.m. & 12/24-hour flags LXI H,HOUR+1 ; and add them to 10's digit of hour ORA M MOV M,A LXI H,START ; Display prompt to set clock on minute CALL DSTRNG ; and wait for user's response CALL INPUT LXI H,YEAR+1 ; Set 13 addr of clock with date/time CALL SETCLK EXIT: LHLD LSTACK ; Restore user's stack SPHL RET ; Return to CCP ; **************************** FUNCTIONS ******************************* ; Sweep through 11 addresses of clock calendar and store BCD contents ; (don't bother reading seconds, which are always zeroed) SWPCLK: MVI A,11 MVI E,2 LXI H,MIN SWPCL2: PUSH PSW CALL RDCLOK MOV M,A INR E INX H POP PSW DCR A JNZ SWPCL2 RET ; Convert pair of BCD digits pointed to by HL to binary value and ; store at addr in DE BCDBIN: MOV B,M INX H MOV C,M BCDBI2: INX H MOV A,C ADD A ADD A ADD C ADD A ADD B STAX D INX D RET ; Convert binary value pointed to by DE to pair of BCD digits and store ; 10's digit at addr given in HL and 1's digit at addr in HL minus 1 BINBCD: LDAX D DCX D MVI C,0FFH BINBC2: INR C MOV B,A SUI 10 JNC BINBC2 MOV M,C DCX H MOV M,B DCX H RET ; Display A in decimal with highlighting if possible HIDECI: PUSH PSW LXI H,HION CALL DSTRNG LDA HIPAR STA PARITY POP PSW CALL DECIML PUSH PSW LXI H,HIOFF CALL DSTRNG STA PARITY POP PSW RET ; Toggle a.m./p.m. and display mm:00 x.m. DTIME: PUSH PSW ; Toggle a.m./p.m. flag and display MVI A,4 LXI H,AMPMFG XRA M MOV M,A MVI A,'p' LXI H,AMPM CMP M MOV M,A JNZ DTIME2 MVI M,'a' DTIME2: POP PSW DTIME4: MVI D,2 ; Skip a couple spaces (hours) MVI E,' ' CALL REPEAT MVI E,':' ; Colon separator CALL DCHAR PUSH PSW LDA NUMIN ; Minutes CALL DECIML LXI H,TIMSG ; :00 x.m. if 12-hour clock CALL DSTRNG ; :00 if 24-hour clock LDA TIMSG ; Backspace to beginning of time ADI 5 MOV D,A MVI E,BS POP PSW ; Fall thru REPEAT ; Display char in E, D times REPEAT: PUSH D CALL DCHAR POP D DCR D JNZ REPEAT RET ; Display string pointed to by HL, where first byte is number ; of bytes in string DSTRNG: MOV A,M ORA A RZ DSTRN2: INX H MOV E,M CALL DCHAR DCR A JNZ DSTRN2 RET ; Display A in decimal (2 digits with leading zeroes) DECIML: PUSH PSW MVI E,0FFH DECIM2: INR E MOV D,A SUI 10 JNC DECIM2 MOV A,E CALL DECIM3 CALL DECIM3 POP PSW RET DECIM3: ADI '0' PARITY EQU $+1 ORI 0 ; Set parity bit if needed for highlighting MOV E,A MOV A,D ; Fall thru DCHAR ; Display char in E using Direct Console Output DCHAR: PUSH PSW PUSH B PUSH H MVI C,DCONIO CALL BDOS POP H POP B POP PSW RET ; Get input using Direct Console Input (only SPACE, CR, and ^C recognized, ; where ^C aborts, zero flag set if SPACE, and zero flag clear if CR) INPUT: PUSH PSW PUSH B INPUT2: MVI C,DCONIO MVI E,0FFH CALL BDOS ORA A JZ INPUT2 CPI CNTLC JZ EXIT CPI CR JZ INPUT4 SUI ' ' JNZ INPUT2 DCR A INPUT4: MOV E,A POP B POP PSW INR E RET ; Display value in A, increment it if user enters SPACE, and repeat ; until user enters CR (lower and upper bounds on A are B and C-1, ; respectively). GETVAL: CMP B JC GETVA4 GETVA2: CMP C JC GETVA8 GETVA4: MOV A,B GETVA6: ORA A ; ORA A will clear carry flag, so DTIME CC DTIME ; routine will never be called unless ORA A ; is replaced by STC when hours are being ; entered on 12-hour clock GETVA8: CALL HIDECI MVI D,2 MVI E,BS CALL REPEAT CALL INPUT RNZ INR A JMP GETVA2 ; Multiply HL*DE and return 32-bit product in (DE,HL) MULWW: MOV B,H MOV C,L LXI H,0 MVI A,16 MULWW1: PUSH PSW DAD H XCHG MOV A,L ADC L MOV L,A MOV A,H ADC H MOV H,A XCHG JNC MULWW2 DAD B JNC MULWW2 INX D MULWW2: POP PSW DCR A JNZ MULWW1 RET ; Divide (DE,HL) by BC and return quotient to DE and ; remainder (modulo) to HL DIVLW: XCHG MVI A,16 DIVLW1: PUSH PSW XCHG DAD H XCHG MOV A,L ADC L MOV L,A MOV A,H ADC H MOV H,A MOV A,L SBB C MOV L,A MOV A,H SBB B MOV H,A JNC DIVLW3 DAD B DIVLW2: POP PSW DCR A JNZ DIVLW1 RET DIVLW3: INX D JMP DIVLW2 IF Z80PIO ; ************************************************************************* ; * Routine reads E=addr from clock interfaced by Z80 PIO and returns * ; * 1 BCD digit in A * ; ************************************************************************* HOLD EQU 10H ; Mask to halt clock READ EQU 20H ; Mask to read clock WRITE EQU 40H ; Mask to set clock ADDRIN EQU 80H ; Mask to enter addr MODE3 EQU 0CFH ; Byte to set clock port (Z80 PIO) to mode 3 RDCLOK: MVI A,MODE3 ; Set clock port to mode 3 OUT CLKCTL ; With all bits for output XRA A OUT CLKCTL MVI A,ADDRIN ; Send addr to clock ORA E OUT CLKDAT MOV A,E ; Latch addr OUT CLKDAT MVI A,MODE3 ; Set lower 4 bits of clock port OUT CLKCTL ; To input MVI A,0FH OUT CLKCTL MVI A,HOLD ; Halt clock OUT CLKDAT RDCLK1 EQU (150*SPEED)/170 MVI A,RDCLK1 ; Wait 150 microseconds RDCLK2: SUI 1 JNC RDCLK2 MVI A,READ+HOLD ; Send read request OUT CLKDAT RDCLK3 EQU (6*SPEED)/170 MVI A,RDCLK3 ; Wait 6 microseconds RDCLK4: SUI 1 JNC RDCLK4 IN CLKDAT ; Get BCD digit (in lower 4 bits) ANI 0FH ; Strip 4 high bits MOV B,A ; Save digit XRA A ; Clear read request & restart clock OUT CLKDAT MOV A,B ; Return BCD digit in A RET ; ************************************************************************* ; * Routine sets 13 addresses of clock interfaced by Z80 PIO with BCD * ; * digits, beginning at 10's digit of years (addr 12) pointed to by * ; * HL and working down to 1's digit of seconds (addr 0) * ; ************************************************************************* SETCLK: MVI D,12 ; Set addr 12->0 of clock MVI A,MODE3 ; Set clock port to mode 3 OUT CLKCTL ; with all bits for output XRA A OUT CLKCTL MVI A,HOLD ; Halt clock OUT CLKDAT SETCL1 EQU (150*SPEED)/170 MVI A,SETCL1 ; Wait 150 microseconds SETCL2: SUI 1 JNC SETCL2 SETCL3: MVI A,ADDRIN+HOLD ; Send addr to clock ORA D OUT CLKDAT MVI A,HOLD ; Latch addr ORA D OUT CLKDAT MVI A,HOLD ; Send value to clock ORA M OUT CLKDAT MOV E,A MVI A,WRITE ; Strobe write on ORA E OUT CLKDAT DCX H ; Decrement digit pointer MOV A,E ; Strobe write off OUT CLKDAT MOV A,D ; Decrement clock addr SUI 1 MOV D,A JNC SETCL3 ; Set next addr if non-negative XRA A ; Restart clock OUT CLKDAT RET ENDIF ; Z80PIO IF NOT Z80PIO ; (8255) ; ************************************************************************* ; * Routine reads E=addr from clock interfaced by 8255 and returns * ; * 1 BCD digit in A (based on Mike Allen's READTIME in MDCLCK11.LBR) * ; ************************************************************************* RDCLOK: MVI A,90H ; Set up 8255 OUT PRTCTL MVI A,20H ; Send read request to clock OUT CLKCTL MOV A,E ; Send address to clock OUT CLKADR RDCLK1 EQU (6*SPEED)/170 MVI A,RDCLK1 ; Wait 6 microseconds RDCLK2: SUI 1 JNC RDCLK2 IN CLKDAT ; Get BCD digit (in lower 4 bits) ANI 0FH ; Strip 4 high bits MOV B,A ; Save digit XRA A ; Clear read request OUT CLKCTL MOV A,B ; Return BCD digit in A RET ; ************************************************************************* ; * Routine sets 13 addresses of clock interfaced by 8255 with BCD * ; * digits, beginning at 10's digit of years (addr 12) pointed to by * ; * HL and working down to 1's digit of seconds (addr 0) (based on * ; * Mike Allen's SETTIME in MDCLCK11.LBR) * ; ************************************************************************* SETCLK: MVI D,12 ; Set addr 12->0 of clock MVI A,80H ; Set up 8255 OUT PRTCTL MVI A,10H ; Halt clock OUT CLKCTL SETCL1 EQU (150*SPEED)/170 MVI A,SETCL1 ; Wait 150 microseconds SETCL2: SUI 1 JNC SETCL2 SETCL3: MOV A,D ; Send addr to clock OUT CLKADR MOV A,M ; Send value to clock OUT CLKDAT MVI A,50H ; Strobe write on OUT CLKCTL DCX H ; Decrement digit pointer MVI A,10H ; Strobe write off OUT CLKCTL MOV A,D ; Decrement clock addr SUI 1 MOV D,A JNC SETCL3 ; Set next addr if non-negative XRA A ; Restart clock OUT CLKCTL MVI A,90H ; Restore 8255 OUT PRTCTL RET ENDIF ; NOT Z80PIO (8255) ; ***************************** DATA AREAS ******************************** HRCLK: DB HRCLK2 DB '-Hour Clock',CR HRCLK2 EQU $-HRCLK-1 FEB29: DB FEB292 DB BELL,' No Feb 29 in 19' FEB292 EQU $-FEB29-1 WAIT: DB WAIT2 DB '! CR to continue: ' WAIT2 EQU $-WAIT-1 TIMSG: DB TIMSG2 DB ':00 ' AMPM: DB 'a.m.' TIMSG2 EQU $-TIMSG-1 START: DB START2 DB ' CR on minute to set clock: ' START2 EQU $-START-1 DOM: DB 31,29,31,30 ; Number of days in each DB 31,30,31,31 ; month (in leap years) DB 30,31,30,31 DB 0 LYFLAG: DB 0 ; Leap year mask MIN EQU $ HOUR EQU MIN+2 DOW EQU HOUR+2 DAY EQU DOW+1 MNTH EQU DAY+2 YEAR EQU MNTH+2 NUMIN EQU YEAR+2 FG1224 EQU NUMIN+1 AMPMFG EQU FG1224+1 NUHOUR EQU AMPMFG+1 NUDOW EQU NUHOUR+1 NUDAY EQU NUDOW+1 NUMNTH EQU NUDAY+1 NUYEAR EQU NUMNTH+1 LSTACK EQU NUMNTH+41 ; Local stack (40 bytes = 20 entries max) ; User's stack pointer stored on top END