.TITLE CCSUBS Calendar clock routines .IDENT /3.2/ .GLOBL $YEAR,$INCY,$CLOK,$WDOG,$PAGE ; ; V3.1 Modify address definitions to allow for Micro/RSX ; V3.2 Replace WHICH's algorithm with one more likely to work. ; ; ; GENERAL ; ------- ; This file, CCSUBS.MAC, is part of the standard calendar clock Support Kit, ; and contains the source code for three Fortran callable subroutines, RTIME, ; RTIMW and WTIME, which respectively read, read and write the date and time ; information in any of the following CTI calendar clock boards: ; ; Clock Year maintained in: ; Model Name Chip CMOS RAM Clock Chip ; --- ---- --- --- --- ; 101 Q-Timer I 58174 X ; 102 Q-Timer II 58274 X ; 120 Calendar Clock 58274 X ; 150 C-Timer I 58174 X ; 151 C-Timer II 58274 X ; ; The Model 101 and Model 150 use the National Semiconductor NS58174 CMOS ; clock chip; the 58174 has no provision for storing the year, which must ; maintained by these routines in CMOS RAM. All other models use the NS58274, ; which does maintain the year; the 58274 is pin compatible, but not software ; compatible, with the 58174. These routines determine which clock chip is ; present and then access it appropriately. ; ; The Model 101 and Model 102 have the RTIME, RTIMW and WTIME subroutines in ; on-board ROM; this Support Kit does not use the ROM-resident routines in ; order to be compatible with those boards which do not have ROM. If you ; have a Model 101 or 102, information on calling ROM-resident routines is ; available in your board's manual. ; ; Input to, and output from, these three routines is in the form a 9-word ; integer array, the first 8 words of which are identical to the RSX date and ; time array (detailed below) used in GTIM$ and STIM$ directives. The 9th word ; contains the day-of-week parameter. By convention, the values of this ; parameter are [1 ... 7], which correspond to [Sunday ... Saturday], ; respectively. ; ; For the Model 101 and Model 150, two parameters needed to maintain the year ; are stored in the CMOS RAM. These are "$YEAR", which is the year of the ; century, and "$INCY", which indicates whether or not the year must be ; incremented (i.e. if the date has rolled over from 31-DEC to 1-JAN). ; Both parameters are stored in page 7 of CMOS RAM (for compatibility with ; the Model 101's ROM-resident subroutines) $INCY in location 772, $YEAR in ; location 776. These two symbols must be defined either in the text of the ; main program, or by the linker's GBLDEF option. ; ; For all other boards, the CMOS RAM (if present) is not used for date ; and time maintainence. ; ; ; RSX Compatible Date and Time Format ; ----------------------------------- ; ; The 9-word date and time array is formatted as follows: ; ; 1st word year (0-99) ; 2nd " month (1-12) ; 3rd " day (1-31) ; 4th " hour (0-23) ; 5th " minute (0-59) ; 6th " second (0-59) ; 7th " tick (0-9) (clock rate is 10 Hz) ; 8th " rate (10) (this is the clock rate; RTIME returns ; a value of 10, and WTIME ignores ; this parameter.) ; ; 9th word day-of-week (1-7 for Sunday-Saturday) ; ; ; Calendar Clock Behavior ; ----------------------- ; The calendar clock used on all boards other than the Model 101 and Model 150 ; is the NS58274. The behavior of this chip is straightforward, and requires ; no special mention here; further information on the clock chip registers and ; bit assignments is available in the manual for your board. ; ; The calendar clock used on Model 101 and 150 boards, the NS58174, returns a ; value of "all 1s" in any of the data registers if an internal 10 Hz update ; is in progress at the instant that a register is read. The timing and ; propagation of the all 1s pattern within the chip are such that it is not ; possible to guarantee that valid data will be returned, even if the all 1s ; condition is absent (this is a bug of which neither we nor the chip ; manufacturer were aware at the time, which could not be remedied, but which ; was designed out of the NS58274). ; ; Reading invalid data can, however, be minimized (to once in several million ; times) by employing the fairly elaborate algorithm implemented in RTIME, or ; avoided entirely by using the algorithm implemented in RTIMW. ; ; The RTIME algorithm involves reading the calendar clock's .1 second register ; 16 times (these are called "guard reads"), then reading all date and time ; information, then performing 16 more guard reads. Success is defined as an ; execution of this sequence of reads which returns no instance of all ones. ; The sequence will be repeated until success is achieved, or until a loop ; limit is reached. The loop limit is imposed to avoid an infinite loop in ; the event that the clock chip is stopped or defective. ; ; The RTIMW algorithm avoids the small chance of returning erroneous date and ; time by forcing a wait (50 msec average, 100 msec maximum) until an update ; occurs before reading the clock and returning the data. ; ; ; RTIMW ; ----- ; If the clock chip is the 58174, RTIMW waits until a 10 Hz update occurs ; (i.e. a "1111" is returned), then waits until the update is complete (i.e. a ; non-1111 is returned), and then calls RTIME. ; ; If the clock chip is the 58274, RTIMW is identical to RTIME. ; ; Calling sequence is as shown below for RTIME. ; RTIMW:: JSR PC,WHICH ; 58174 or -274? JMP RT274 ; Return here if -274 CLR R0 ; Loop not more than 64K times 10$: MOV @#$CLOK+2,-(SP) ; Read the .1 second register BIC #177760,(SP) ; Keep only <3:0> CMP (SP)+,#17 ; Is it 1111? BEQ 20$ ; If EQ yes SOB R0,10$ ; No -- loop until we get one, BR RTIME ; or until we give up 20$: MOV @#$CLOK+2,-(SP) ; BIC #177760,(SP) ; CMP (SP)+,#17 ; Is it 1111? BNE RTIME ; If NE no SOB R0,20$ ; Yes -- loop until it goes away, ; then fall into RTIME ; ; RTIME ; ----- ; RTIME reads the calendar clock and returns date and time information to the ; caller in the RSX 8-word format, plus a 9th word which contains the ; day-of-week parameter. ; ; ; Macro: ARGS: .BYTE 1,0 ; Establish Fortran-style ; .WORD DATIME ; argument list, ; DATIME: .BLKW 9. ; and 9 word array ; . ; ; . ; ; . ; ; ; ; MOV #ARGS,R5 ; Point to the array ; JSR PC,RTIME ; Call the subroutine ; ; (contents of R0 - R4 lost, ; ; R5 --> DATIME on return) ; ; Fortran: INTEGER*2 DATIME(9) ! Establish the 9 word array ; . ! ; . ! ; . ! ; ! ; CALL RTIME(DATIME) ! Call the subroutine ; RTIME:: JSR PC,WHICH ; 58174 or -274? JMP RT274 ; Return here if -274 MOV 2(R5),R5 ; R5 points to arg list MOV #7000,@#$PAGE ; Select CMOS RAM page 7 MOV R5,-(SP) ; Save initial pointer to time array MOV #177760,R4 ; Keep this handy for BICs MOV #1000.,R3 ; R3 limits the number of retries 200$: MOV #44.,R1 ; Init "1111 detector counter" JSR PC,GUARD ; See if any 1111s occur MOV (SP),R5 ; Retrieve original time array pointer MOV #12.,R2 ; Read 12 clock registers: 210$: MOV (R0)+,-(SP) ; get next clock register BIC R4,(SP) ; clear all but <3:0> (it's BCD) CMP (SP),#17 ; if 1111 then "CLC", else "SEC" SBC R1 ; if 1111 then "NOP", else "DEC R1" 213$: SOB R2,210$ ; JSR PC,GUARD ; TST R1 ; Any 1111s? BEQ 217$ ; If EQ no ADD #2*12.,SP ; Yes -- discard these parameters TST @#$WDOG ; reset watchdog timer (in case it's enabled) SOB R3,200$ ; and try again SUB #2*12.,SP ; (unless this is the last try) 217$: MOV @#$YEAR,(R5)+ ; Move year from CMOS RAM to output array JSR R0,GPARAM ; Month MOV #6,-(SP) ; SUB R0,(SP) ; Which half of the year are we in? BMI 220$ ; If MI 2nd TST @#$INCY ; 1st -- has year been incremented yet? BPL 220$ ; If PL yes INC @#$YEAR ; No -- increment non-volatile copy INC -4(R5) ; and the caller's copy 220$: MOV (SP)+,@#$INCY ; Set flag as appropriate BIC #177770,(SP) ; MOV (SP)+,14(R5) ; Day of week MOV #4,R2 ; 4 more items: RTX: JSR R0,GPARAM ; Day, hour, minute, second SOB R2,RTX ; MOV (SP)+,(R5)+ ; Tenths of second MOV #10.,(R5) ; Clock rate is always 10 Hz MOV (SP)+,R5 ; Clean stack RTS PC ; ; To read 58274 date and time: ; ; 1) read the control register to clear "Data Changed" (177300<3>), then ; 2) read any or all of the 14 data registers (177302 through 177334), then ; 3) test "Data Changed" to see if the clock incremented while you were ; reading it. If so, go to step 1. ; ; Standard Bits Read/ Valid ; Address Used Write Description Values Notes ; +--------+-------+-----+--------------------------+---------+ ; | 177300 | <3:0> | R/W | Control register | n/a | 1 ; | 177302 | <3:0> | R | Tenths of seconds | 0-9 | 2,3 ; | 177304 | <3:0> | R/W | Unit seconds | 0-9 | 2 ; | 177306 | <3:0> | R/W | Tens of seconds | 0-5 | 2 ; | 177310 | <3:0> | R/W | Unit minutes | 0-9 | 2 ; | 177312 | <3:0> | R/W | Tens of minutes | 0-5 | 2 ; | 177314 | <3:0> | R/W | Unit hours | 0-9 | 2 ; | 177316 | <3:0> | R/W | Tens of hours | 0-2 | 2 ; | 177320 | <3:0> | R/W | Unit days | 0-9 | 2 ; | 177322 | <3:0> | R/W | Tens of days | 0-3 | 2 ; | 177324 | <3:0> | R/W | Unit months | 0-9 | 2 ; | 177326 | <3:0> | R/W | Tens of months | 0-1 | 2 ; | 177330 | <3:0> | R/W | Unit years | 0-9 | 2 ; | 177332 | <3:0> | R/W | Tens of years | 0-9 | 2 ; | 177334 | <2:0> | R/W | Day of week | 1-7 | 4 ; | 177336 | <3:0> | R/W | Clock setting/interrupt | n/a | 5,6 ; +--------+-------+-----+--------------------------+---------+ ; ; NOTES ; ----- ; 1) When read: ; <3> = 1 ==> clock data have changed since last ; time control register was read ; <0> = 1 ==> internal clock interrupt has occurred ; When written: ; <3> = 0 selects normal mode. ; = 1 selects test mode; gates the clock ; oscillator to pin 13 and TP2 ; <2> = 0 starts clock ; = 1 stops clock ; <1> = 0 selects clock setting register at 177336 ; = 1 selects interrupt control register at 177336 ; <0> = 0 starts internal interrupt ; = 1 stops internal interrupt ; 2) Register contains a BCD value -- decimal values of ; 10, 11, 12, 13, 14 and 15 should never occur ; 3) This register is always cleared when the clock is restarted ; 4) Day of week register "wraps around" to 1, not 0. By convention, ; 1 ... 7 correspond to Sunday ... Saturday, respectively on PDP11s ; 1 ... 7 correspond to Monday ... Sunday, respectively on VAXs ; ; 5) This location is actually two read/write registers -- either ; the clock setting register or the interrupt control register, ; depending upon the condition of 177300<1> (see Note 1 above) ; Clock setting register: ; <3:2> = 0 for leap year, 1 for leap year plus 1, etc. ; <1> = 0 AM \ ; = 1 PM > if clock is in 12-hour mode ; <0> = 0 selects 12-hour mode ; = 1 selects 24-hour mode ; Interrupt control register: ; <3> = 0 selects single interrupt operation ; = 1 selects repeated interrupt operation ; <2:0> = 0 interrupt output cleared, interrupt start/stop ; set to 1 (stop), interrupts disabled ; = 1 interrupt in 0.1 second (+/- 1 ms, non-accumulating) ; = 2 interrupt in 0.5 second (+/- 1 ms, non-accumulating) ; = 3 interrupt in 1 second (+/- 1 ms, non-accumulating) ; = 4 interrupt in 5 seconds (+/- 1 ms, non-accumulating) ; = 5 interrupt in 10 seconds (+/- 1 ms, non-accumulating) ; = 6 interrupt in 30 seconds (+/- 1 ms, non-accumulating) ; = 7 interrupt in 60 seconds (+/- 1 ms, non-accumulating) ; 6) Clock chip interrupts are not wired, but can be polled. ; RT274: MOV 2(R5),R5 ; R5 points to arg list MOV R5,-(SP) ; MOV #177760,R4 ; Keep this handy for BICs MOV #1000.,R3 ; R3 limits the number of retries 200$: MOV #$CLOK,R0 ; R0 --> clock registers TST (R0)+ ; Clear clock's "data changed" flag MOV #14.,R2 ; Read 14 clock registers 210$: MOV (R0)+,-(SP) ; BIC R4,(SP) ; SOB R2,210$ ; BIT #10,@#$CLOK ; Data changed? BEQ 217$ ; If EQ no ADD #2*14.,SP ; Yes -- discard these parameters TST @#$WDOG ; Reset watchdog timer (in case it's enabled) SOB R3,200$ ; and try again SUB #2*14.,SP ; (unless this is the last try) 217$: BIC #10,(SP) ; Day of week (0-7, 0 will print as "***") MOV (SP)+,20(R5) ; MOV #6,R2 ; 6 items: BR RTX ; ; WTIME sets the calendar clock, using information provided by the caller ; in an RSX compatible 8-word array, plus a 9th word which contains the ; day-of-week parameter. ; ; Macro: ARGS: .BYTE 1,0 ; Establish Fortran-style ; .WORD DATIME ; argument list, ; DATIME: .BLKW 9. ; and the 9 word array ; . ; Define the ; . ; date and time ; . ; parameters in DATIME ; ; ; MOV #ARGS,R5 ; ; JSR PC,WTIME ; Call the subroutine ; ; (contents of all ; ; general registers lost) ; ; Fortran: INTEGER*2 DATIME ! Establish the 9 word array ; . ! Define the ; . ! date and time ; . ! parameters in DATIME ; ! ; CALL WTIME(DATIME) ! Call the subroutine ; WTIME:: MOV 2(R5),R5 ; MOV #$CLOK+36,R2 ; JSR PC,WHICH ; JMP WT274 ; Return here if -274 MOV #7000,@#$PAGE ; Select CMOS RAM page 7 CLR -(R2) ; Stop clock MOV (R5)+,R1 ; R1 := year of century MOV R1,@#$YEAR ; Record it in CMOS RAM MOV #6,-(SP) ; Set "inc year" flag SUB (R5),(SP) ; MOV (SP)+,@#$INCY ; BIC #177774,R1 ; R1 := year modulo 4 MOV #^B10000,R0 ; Load leap year shift register: INC R1 ; 8 for leap year 300$: ASR R0 ; 4 for leap year + 1 SOB R1,300$ ; 2 for leap year + 2 MOV R0,-(R2) ; 1 for leap year + 3 JSR PC,PPARAM ; Set month MOV 14(R5),-(R2) ; Set day of week JSR PC,PPARAM ; Set day of month JSR PC,PPARAM ; Set hour JSR PC,PPARAM ; Set minute MOV #1,@#$CLOK+34 ; Start clock, and clear seconds RTS PC WT274: MOV R5,-(SP) ; MOV #4,@#$CLOK ; Stop clock MOV (R5),-(SP) ; Get year BIC #177774,(SP) ; modulo 4 for leap years ASL (SP) ; (aligned for clock ASL (SP) ; setting register), and BIS #1,(SP) ; force 24-hour mode MOV (SP)+,(R2) ; MOV 20(R5),-(R2) ; Set day of week MOV #6,R4 ; Set 6 parameters (12 clock registers): 10$: JSR PC,PPARAM ; Year, month, day, hour, minute, second SOB R4,10$ ; CLR -4(R2) ; Start clock, clear tenths MOV (SP)+,R5 ; RTS PC ; ; Support routines used by RTIME and WTIME ; ; PPARAM converts the next parameter (pointed to by R5) to two BCD ; digits, and loads them into the calendar clock registers (pointed ; to by R2). ; PPARAM: MOV (R5)+,R0 ; Get next parameter MOV #10.,R1 ; JSR PC,$DIV ; (R0,R1) := (quotient,remainder) MOV R0,-(R2) ; 10s digit to CMOS clock MOV R1,-(R2) ; 1s digit RTS PC ; ; Inputs: Dividend in R0, divisor in R1 ; Outputs: Quotient in R0, remainder in R1 ; $DIV: MOV #16.,-(SP) ; Number of bits MOV R1,-(SP) ; Save divisor CLR R1 ; Remainder := 0 5$: ASL R0 ; Shift dividend out, quotient in ROL R1 ; Remainder := remainder * 2 CMP R1,(SP) ; (Remainder * 2) > than divisor? BLO 10$ ; If LO no SUB (SP),R1 ; Remainder := remainder - divisor INC R0 ; Quotient := quotient + 1 10$: DEC 2(SP) ; Count bits BGT 5$ ; CMP (SP)+,(SP)+ ; Clean stack RTS PC ; ; GUARD will read the tenths of seconds register 16 times, repeatedly ; until it gets 16 values, none of which is a value of all ones. ; GUARD: MOV #$CLOK,R0 ; MOV #16.,R2 ; R2 := 16 CLR (R0)+ ; Ensure clock is in normal mode TST (R0) ; Read once and discard 20$: MOV (R0),-(SP) ; Read tenths of seconds BIC R4,(SP) ; Make sure that no 1111s occur CMP (SP)+,#17 ; SBC R1 ; If not 1111, "DEC R1" 30$: SOB R2,20$ ; RTS PC ; ; ; GPARAM gets the next two BCD clock parameters from the stack ; (a one's digit and a ten's digit), and converts them to a single ; value, which is returned to RTIME in R0. ; GPARAM: MOV R3,(SP)+ ; Discard old R0 and test R3 BEQ 40$ ; If EQ clock was stopped BIC R4,(SP) ; Clear extraneous bits BIC R4,2(SP) ; CMP (SP),#9. ; Is tens digit a valid BCD value? BGT 40$ ; If GT no CMP 2(SP),#9. ; Valid units digit? BGT 40$ ; ASL (SP) ; (SP) := 2x MOV (SP),-(SP) ; 2x ASL (SP) ; 4x ASL (SP) ; 8x ADD (SP)+,(SP) ; (SP) := 8x + 2x = 10x ADD (SP)+,(SP) ; (SP) := units + tens BR 50$ ; 40$: TST (SP)+ ; Discard tens parameter MOV #-1,(SP) ; Stuff bad parameter flag 50$: MOV (SP),(R5)+ ; Store value in output array RTS R0 ; ; Determine which clock chip is present. ; Returns to JSR+2 if 58274, JSR+6 if 58174. ; WHICH: MOV #3,@#$CLOK ; For 174s: normal|NOP|NOP|NOP ; For 274s: normal|clock-on|sel-int-reg|int-off CLR @#$CLOK+36 ; For either: interrupts off, output cleared TST @#$CLOK+36 ; For 174s: recommended for initialization TST @#$CLOK+36 ; For 274s: NOPs (hopefully) TST @#$CLOK+36 ; MOV #2,@#$CLOK+36 ; For either: write the interrupt register CLR @#$CLOK ; For 174s: normal|NOP|NOP|NOP ; For 274s: normal|clock-on|sel-clk-reg|int-on MOV @#$CLOK+36,-(SP) ; Read register "F"; <1> of 274's clock set reg BIC #177770,(SP) ; is AM/PM bit (always 0 in 24-hr mode) CMP (SP)+,#2 ; Now, what was it we read back? BNE 274$ ; If NE it must be the 274's clock setting reg ADD #4,(SP) ; Still the interrupt reg -- it's a 174 274$: RTS PC .END