;:::::::::::::::::::::::::::::::::::::::*************************** ; Time-handling Routines ** Machine-Dependant ** ; D-X Designs Pty Ltd, P112 *************************** ; ; This module incorporates provisions for an interrupt-driven clock, or ; the Dallas DS-1202 Real Time Clock for obtaining Time and Date Info. ; ; 1.0 - 18 Jul 96 - Initial Release. HFB ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; This code module should handle all Time-related segments, to include ; Interrupt handlers for Real Time update, motor timeouts, user down- ; counter and any necessary time format conversion routines. ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; TIMER ; This routine keeps the Real Time Clock, Diskette Time Out ; counter, and General-Purpose down-counters. An interrupt is ; generated every 50 mS by the Z-182 Programmable Interrupt Timer ; and used to update parameters. Every other interrupt (100 mS ; intervals) is used to update the 100 mS counters and Time string ; if using Interrupt-driven Time and Date. ; Enter: No parameters needed (Interrupt) ; Exit : None ; Uses : None. All registers preserved. Decrements MTM, User and BIOS ; general-purpose counter bytes every 100 mS. ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: CSEG ; Come here every 50 milliseconds from Programmable Reload Timer 0 TIMER: DI ; Don't Interrupt from here LD (TIMSTK),SP ; Save entry Stack Pointer LD SP,TIMSTK ; ..set up Local Interrupt Stack PUSH HL PUSH AF ; Save the registers IN0 A,(TCR) IN0 A,(TMDR0L) IN0 A,(TMDR0H) ; Clear the interrupt LD HL,FLAG50 ; Point to 50 mS Flag LD A,(HL) ; fetch XOR 01H ; and toggle LSB LD (HL),A ; Re-save JR NZ,TDONE ; ..exit if Odd 50 mS Tick LD HL,DCNTR ; Adjust User GP Down-Counter DEC (HL) LD HL,MTM ; Adjust Bios GP Down-Counter DEC (HL) INC HL ; Point to Motor On-timer (MOTIM) LD A,(HL) OR A ; Already Timed Out? JR Z,TIMER0 ; ..jump if So DEC (HL) ; Else count down CALL Z,MOTOFF ; turning Motors Off if timed out TIMER0: ; Adjust the Interrupt-driven Real Clock (if no Real Time Clock exists). ; This "Clock" maintains Date and Time in a DateStamper(C) compatible ; string per ZSDOS specifications. IF CLOCK AND [NOT DS1202] LD HL,TENTHS ; 100ms counter INC (HL) ; Bump Tenths-of-Seconds LD A,(HL) ; get the value SUB 10 ; Subtract limit value (in decimal times 2) JR NZ,TDONE ; jump to exit if no rollover LD (HL),A ; Else save a zero DEC HL ; back down time string LD A,(HL) ; Get Seconds INC A ; bump DAA ; in BCD LD (HL),A ; and save SUB 60H ; Subtract limit value JR NZ,TDONE ; ..jump to exit if no rollover LD (HL),A ; Else save a zero DEC HL ; back down time string LD A,(HL) ; Get Minutes INC A ; bump DAA ; in BCD LD (HL),A ; and save SUB 60H ; Subtract limit value JR NZ,TDONE ; ..jump to exit if no rollover LD (HL),A ; Else save a zero DEC HL ; back down time string LD A,(HL) ; Get Hours INC A ; bump DAA ; in BCD LD (HL),A ; and save SUB 24H ; Subtract limit value JR NZ,TDONE ; ..jump to exit if no rollover LD (HL),A ; Else save a zero DEC HL ; back down time string LD A,(HL) ; Get Day INC A ; bump DAA ; in BCD LD (HL),A ; and save PUSH DE ; Save regs PUSH AF EX DE,HL LD HL,DPM-1 ; Point to days per month table indexed base 1 DEC DE ; back up to Months byte LD A,(DE) INC DE ; move ptr back CP 10 ; >Sept? JR C,TIMER1 ; ..jump if Not SUB 6 ; Else convert BCD to binary TIMER1: CALL ADDAHL ; Offset into table based on Month POP AF ; Get current day count CP (HL) ; Time for new month? EX DE,HL ; (put regs back) POP DE JR C,TDONE ; ..exit here if not time LD (HL),1 ; Else start out on first of month DEC HL ; back down to month LD A,(HL) ; Get month INC A ; Bump DAA ; in BCD LD (HL),A ; and save SUB 13H ; Time for new year? JR C,TDONE ; ..exit if not LD (HL),1 ; Else start at month 1 (Jan) DEC HL ; back up to Years byte LD A,(HL) ; Get Year INC A ; bump DAA ; in BCD LD (HL),A ; and save SUB 99H ; Time for next century? JR NZ,TDONE ; ..exit if not LD (HL),A ; Else save 0 year ENDIF ;Clock & Not DS1202 TDONE: POP AF POP HL ; Restore registers LD SP,(TIMSTK) ; and Stack Pointer EI ; allow Ints again RET IF CLOCK AND [NOT DS1202] ; Table of Days-per-Month DPM: DEFB 31H+1, 28H+1, 31H+1 ; January, February, March DEFB 30H+1, 31H+1, 30H+1 ; April, May, June DEFB 31H+1, 31H+1, 30H+1 ; July, August, September DEFB 31H+1, 30H+1, 31H+1 ; October, November, December ENDIF ;Clock & Not Ds1202 ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; TIME - Set or Return the time string as defined for ZSDOS. If Reading, ; The Six digit BCD ZSDOS Clock string is copied to the location ; addressed by Register pair DE. As an enhancement, the tenths-of-seconds ; value is returned in Reg D. If Setting the Clock, the RTC clock string ; will be set from the 6 bytes addressed by DE. ; ; ENTER: C - 0 to Read the Clock, Non-0 (1 recommended) to Set the Clock ; DE = Pointer to receive 6-byte Time/Date on Read, Source for Set ; ; EXIT : E = Original contents of Target Seconds field ; D = Tenths of Seconds field ; HL = Pointer to Target Seconds field ; A = 1 for success, 0 if Unable to Set or Read ; BC = Address of User General-Purpose Down-Counter ; ; NOTE: The Wall Clock string is arranged as BCD digits with Tenths- ; of-Seconds byte appended. The entire string is: ; ; YR MO DA HH MM SS TT ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: IF CLOCK AND DS1202 ; Dallas DS-1202 Clock Interface on the D-X Designs Pty Ltd P112. ; Read the Clock to a buffer area in Memory. Seven bytes (Six used) are ; read in burst mode from the clock chip, one bit at a time via three bits ; from one of the Z-182 parallel ports. When Setting the clock from this ; code fragment, the DOW is forced to 01H, which will probably be invalid. ; The Clock is accessed serially (LSB first) one byte at a time with a ; command byte being written to begin the Read/Write. Burst Mode is used ; with a 0BFH byte for Reading, 0BEH for Writing as the Command. Clock ; Setting clears the Write Protect bit before setting, and resets the chip ; to Read-Only when finished. The Entire Date/Time String is eight bytes ; read as: ; ; Sec Min Hour Day Mon DOW Year WProt ; (12/24) (MSB) ; ; In this implementation, the DOW field is not used, and the 12/24 hour bit ; is always set to 24-hour mode by setting the MSB to 1. RTCRst EQU 2 ; DS-1202 Reset\ Bit RTCClk EQU 1 ; DS-1202 Clock Bit RTCDat EQU 0 ; DS-1202 Data IO Bit TIME: DI ; NO INTERRUPTS in This Routine LD (TIMSTK),SP ; Save Entry Stack Ptr LD SP,TIMSTK ; and Set Local Stack LD A,C ; Get Clock Operation Flag OR A ; Is it a Write? PUSH AF ; (save) CALL CSETUP ; Set Clock to Read, returning BC->DRA Port LD DE,5 ; (above also moved ptr to HL) ADD HL,DE ; offset to Seconds Field POP AF ; (restore Write flag) IF CLKSET JR NZ,TIMSET ; ..jump if Write and Set allowed ELSE LD A,00 ; (preset Error Condition) JR NZ,NOCLOK ; ..jump to Error if Set Not allowed ENDIF LD E,(HL) ; Else Read, so fetch orig contents PUSH DE ; (Save for Exit) PUSH HL ; (and preserve ptr) ; Command the DS-1202 for Burst Read of Clock LD A,0BFH ; Load the Burst Clock Read Command CALL Wr1202 ; and Send it ; Read the Clock Data. D still 0 from above Load, use as Byte Index RdTim0: PUSH HL ; Save Ptr LD E,8 ; Gather 8 bit for a byte RdTim1: IN A,(C) ; Read Port RES RTCClk,A ; Clock LO OUT (C),A ; Set NOP ; (settle) IN A,(C) ; Read Bit to LSB RRCA ; Data Bit to Carry RR L ; to MSB of Accum RLCA ; restore Parallel Port bits SET RTCClk,A ; Clock HI OUT (C),A ; Set DEC E ; Byte Done? JR NZ,RdTim1 ; ..jump if Not LD E,L ; Else Get Byte POP HL ; Restore Ptr to Dest LD A,D ; Get Index CP 5 ; Day-Of-Week? JR Z,RdTim2 ; ..jump if So, bypassing save LD (HL),E ; Save value in output string DEC HL ; back down to previous byte in output RdTim2: INC D ; increment index CP 6 ; Did we just save Year? JR NZ,RdTim0 ; ..get another byte if So CALL CClose ; Else Deselect Clock POP HL ; Restore Ptr POP DE ; Orig Seconds content (and 0 Tenths) ClkExt: LD A,01H ; Set Good Exit NOCLOK: LD SP,(TIMSTK) ; Restore entry Stack LD BC,DCNTR ; Get Address of User Down-Counter EI ; Ints Ok now RET ; and Quit ;..... ; Set the Clock if code assembly option set ; Enter: BC = DRA Address ; D = 0 (used as count of bytes written) ; HL -> Seconds of ZSTime string IF CLKSET WrTim: LD A,8EH CALL Wr1202 ; Turn Write Protect OFF XOR A CALL Wr1202 ; by clearing MSB of Time+6 CALL CClose ; then terminate Clock Access CALL COpen ; Start Clock access again WrTim0: LD A,D ; Get Index 0..7 of Write String CP 8 ; At End? JR Z,WrTimX ; ..quit if So CP 6 ; Year or WrProtect? JR C,WrTim1 ; .jump if Not JR Z,WrTim2 ; ..jump if Year LD A,80H ; Else Write Protect On since last byte JR WrTim3 ; ..do It! WrTim1: CP 5 ; DOW? LD A,01H ; (set default if So) JR Z,WrTim3 ; ..set if So WrTim2: LD A,(HL) ; Else Get Byte from ZSTime String DEC HL ; back up for Next Byte WrTim3: CALL Wr1202 ; Write Byte to Clock INC D ; bump index of bytes written JR WrTim0 ; ..continue til Done WrTimX: CALL CClose ; Terminate Clock Access JR ClkExt ; ..and Exit ENDIF ;..... ; Set up DS-1202 interface from Z80182 Parallel port ; Entry: None ; Exit : BC -> Data Port w/Clk at bits 0-2 ; Uses : AF,BC,DE CSETUP: EX DE,HL ; Move Pointer to HL COpen: LD BC,DDRA ; Address Parallel Port A Control IN A,(C) ; Read Settings SET RTCDat,A ; Data Line to Input OUT (C),A ; and Set INC BC ; Address Parallel Port A (DRA) IN A,(C) ; Fetch settings RES RTCClk,A ; Clk LO to Start OUT (C),A ; (set) SET RTCRst,A ; Clear Reset to HI OUT (C),A ; (set) RET ;..... ; Write the Byte in A to the clock (used for Command) ; Entry: BC -> Data Port (DRA) ; Exit : None ; Uses : AF,E Wr1202: PUSH HL ; Save Regs LD L,A ; Store byte LD E,8 ; set bit count IN0 A,(DDRA) ; Fetch Def'n Reg contents RES RTCDat,A ; Data Line to Output OUT0 (DDRA),A ; (set) Wr120L: IN A,(C) ; Read Parallel Port RES RTCClk,A ; Clk LO OUT (C),A ; (set) SRL A ; (clear LSB) RR L ; Data Byte LSB to Carry RLA ; then to Parallel Port LSB OUT (C),A ; (set) SET RTCClk,A ; Clk HI OUT (C),A ; (set) DEC E ; Eight Bits Sent? JR NZ,Wr120L ; ..loop if Not IN0 A,(DDRA) SET RTCDat,A ; Set Port to Data IN OUT0 (DDRA),A POP HL ; Restore Regs RET ;..... ; Deselect the Clock for Exit ; Enter: BC -> Z80182 Parallel Port A ; Exit : None ; Uses : AF CClose: IN A,(C) ; Fetch Parallel Port SET RTCClk,A ; Clk to HI OUT (C),A RES RTCRst,A ; Reset Active LO OUT (C),A RET ENDIF ;Clock & Ds1202 ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: IF CLOCK AND [NOT DS1202] ; Not Dallas, make Intrpt Code TIME: LD A,C ; Get Clock Operation Flag LD BC,5 ; and set up for ZSDOS Read/Write LD HL,YEAR ; Pointing to Clock String OR A ; Is it a Read? JR Z,TIMES ; ..jump it so EX DE,HL ; Else swap pointers for write TIMES: DI ; disable Interrupts LDIR ; and move 5 bytes of 6 bytes (.1 Sec = 7) LD C,(HL) ; Get the Seconds byte INC HL ; bump to 1/10th Seconds LD H,(HL) ; and Get EI ; Re-enable Interrupts EX DE,HL ; Swap pointers LD E,(HL) ; Preserve original Sec field contents in E LD (HL),C ; and store the Seconds byte (.1 Sec in D) LD BC,DCNTR ; Get Address of User Down-Counter LD A,01 ; Set Ok exit value of 1 RET ENDIF ;Clock & Not Ds1202 IF NOT CLOCK ; If No Clock.. XOR A ; Set Error Return RET ; and exit ELSE DSEG ; Real Time Clock Buffer IF NOT DS1202 ; Don't need this if D-X Designs P112 YEAR: DEFS 1 ; Year - packed BCD DEFS 1 ; Month - packed BCD DEFS 1 ; Day - packed BCD DEFS 1 ; Hour - packed BCD DEFS 1 ; Minute - packed BCD DEFS 1 ; Seconds - packed BCD TENTHS: DEFS 1 ; Tenths of Seconds - Binary (Int, else..) ENDIF ENDIF ;Clock ;..... ; Save some space in the Common RAM Area for a local stack DSEG DCNTR: DEFS 1 ; User 100 mS General-Purpose Down-Counter FLAG50: DEFS 1 ; Flag for dividing 50mS to 100mS ticks DEFS 12 ; We use 6 levels of stack (worst case) TIMSTK: DEFS 2 ; Store the entry Stack pointer here CSEG ; End up by restoring CSEG ;=========================== END of TIM-DX ==================================