; Copyright 1996 Acorn Computers Ltd ; ; Licensed under the Apache License, Version 2.0 (the "License"); ; you may not use this file except in compliance with the License. ; You may obtain a copy of the License at ; ; http://www.apache.org/licenses/LICENSE-2.0 ; ; Unless required by applicable law or agreed to in writing, software ; distributed under the License is distributed on an "AS IS" BASIS, ; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ; See the License for the specific language governing permissions and ; limitations under the License. ; ; > RTC ; ********************************* ; *** C h a n g e L i s t *** ; ********************************* ; Date Description ; ---- ----------- ; 28-Sep-89 Started ; 04-Oct-89 First working version ; 05-Oct-89 Added trap for OS_Word 15 ; 27-Nov-89 Added GET <Hdr>.File, to make it assemble again ; (no change to object) ; 27-Mar-91 ECN Internationalised ; 21-May-93 TMD Applied "-1" correction to timer latch programming ; 31-Jan-97 MJS Fix the bug that means CallEvery is requested twice ; (see SingleCallEveryFix) GET Hdr:ListOpts GET Hdr:Macros GET Hdr:System GET Hdr:CMOS GET Hdr:ModHand GET Hdr:Services GET Hdr:Proc GET Hdr:FSNumbers GET Hdr:NewErrors GET Hdr:IIC GET Hdr:ExtraLong GET Hdr:MsgTrans LEADR Module_LoadAddr ;Fix bug such that CallEvery is requested twice during start-up - once ;on module init, once when ServiceReset is seen. This is a latent bug ;since RO 3.1. It means that corrections are done on the very short ;period between the double CallEvery's, and may inject very large time ;rate variations (which then are not recorrected until the next main ;period, currently 1 hour). GBLL SingleCallEveryFix SingleCallEveryFix SETL {TRUE} TAB * 9 LF * 10 FF * 12 CR * 13 OsbyteReadCMOS * &A1 OsbyteWriteCMOS * &A2 OswordSetTime * &0F ; Failure modes Failure_ReadCMOS * 1 Failure_WriteIIC * 2 Failure_ReadIIC * 3 Failure_Conversion * 4 Failure_ReadIOCTime * 5 Failure_ReadMonoTime * 6 Failure_ErrorTooBig * 7 Failure_NewLatchTooBig * 8 ; Module workspace allocation ^ 0, R12 FailureMode # 4 LastError # 4 LastMonoTime # 4 LastLatchValue # 4 CallBackPending # 4 RTCCentiLo # 4 RTCCentiHi # 4 IOCCentiLo # 4 IOCCentiHi # 4 [ SingleCallEveryFix NCorrections # 4 ;no. of corrections made - just to aid test monitoring ] MessageFile_Block # 16 MessageFile_Open # 4 RTC_WorkspaceSize * :INDEX: @ ; Default value to stick in IOC latch DefaultLatchValue * 20000 MinimumLatchValue * 18000 MaximumLatchValue * 22000 ; Time in centiseconds between recalibrations Period * 100*60*60 ; one hour ; Macro to convert from a BCD value to a binary one ; reg = 16*hi + lo ; we want reg = 10*hi + lo = reg-6*hi MACRO BCDToBinary $reg, $temp MOV $temp, $reg, LSR #4 ; get hi nybble ADD $temp, $temp, $temp, LSL #1 ; hi * 3 SUB $reg, $reg, $temp, LSL #1 MEND ; **************** Module code starts here ********************** Module_BaseAddr DCD 0 DCD RTC_Init -Module_BaseAddr DCD RTC_Die -Module_BaseAddr DCD RTC_Service -Module_BaseAddr DCD RTC_Title -Module_BaseAddr DCD RTC_HelpStr -Module_BaseAddr DCD RTC_HC_Table-Module_BaseAddr DCD 0 ; RTCSWI * Module_SWIChunkSize DCD 0 ; RTC_SWIHandler-Module_BaseAddr DCD 0 ; RTC_SWINameTable-Module_BaseAddr DCD 0 ; Code to manually decode swi name (not needed) RTC_Title = "RTCAdjust", 0 RTC_HelpStr = "RTCAdjust" = TAB = "0.06 (20 Apr 1998)", 0 ALIGN ; ************************************************************************** RTC_HC_Table * Module_BaseAddr ; ************************************************************************** ; ; RTC_Init - Initialisation entry ; RTC_Init ENTRY LDR R2, [R12] ; have we got workspace yet ? TEQ R2, #0 BNE %FT05 MOV R0, #ModHandReason_Claim MOV R3, #RTC_WorkspaceSize SWI XOS_Module EXIT VS ; R2 -> workspace STR R2, [R12] ; save address in my workspace pointer, ; so Tutu can free it for me when I die 05 MOV R12, R2 MOV r0, #0 STR r0, MessageFile_Open BL DoInit ; can exit with VS EXIT ; ************************************************************************** ; ; RTC_Service - Service entry ; ;Ursula format ; UServTab DCD 0 DCD UService - Module_BaseAddr DCD Service_Reset DCD 0 DCD UServTab - Module_BaseAddr RTC_Service ROUT MOV r0,r0 TEQ R1, #Service_Reset MOVNE PC, R14 UService LDR R12, [R12] Push "R0-R6,R14" BL DoInit Pull "R0-R6,PC" ; ************************************************************************** ; ; RTC_Die - Die entry ; RTC_Die ENTRY LDR R12, [R12] MOV R6, PC ORR R0, R6, #I_bit TEQP R0, #0 ; disable IRQs round this bit LDR R0, CallBackPending TEQ R0, #0 BNE %FT10 LDR r0, MessageFile_Open CMP r0, #0 ADRNE r0, MessageFile_Block SWINE XMessageTrans_CloseFile ADR R0, MyCallEvery ; get me off the CallEvery MOV R1, R12 SWI XOS_RemoveTickerEvent TEQP R6, #0 ; restore IRQ status - it's safe! BL GetOffWordV LDR R3, =DefaultLatchValue ; put back normal latch value BL ProgramLatch CLRV EXIT 10 ADR R0, NotNowImBusy BL CopyError EXIT NotNowImBusy & 1 = "M00", 0 ALIGN ; ************************************************************************** ; ; DoInit - Set up variables on init/reset ; DoInit ENTRY MOV R0, #0 STR R0, CallBackPending STR R0, FailureMode [ SingleCallEveryFix STR R0, NCorrections ] BL InitVars EXIT VS LDR R0, =DefaultLatchValue STR R0, LastLatchValue MOV R0, #WordV ADR R1, MyWordV MOV R2, R12 SWI XOS_Claim EXIT VS [ SingleCallEveryFix ;remove previous request if any, in case this is the second ;request on start-up (one from module init, one from Service_Reset) ADR R0, MyCallEvery MOV R1, R12 SWI XOS_RemoveTickerEvent ] LDR R0, =Period-1 ; adjust for bug ADR R1, MyCallEvery MOV R2, R12 SWI XOS_CallEvery BLVS GetOffWordV EXIT ; ************************************************************************** ; ; InitVars - Init called on init, reset or set time ; InitVars ENTRY MOV R0, #0 STR R0, LastError SWI XOS_ReadMonotonicTime STRVC R0, LastMonoTime EXIT ; ************************************************************************** ; ; GetOffWordV - Get off WordV ; ; out: PSR preserved ; GetOffWordV ENTRY MOV R0, #WordV ADR R1, MyWordV MOV R2, R12 SWI XOS_Release EXITS ; ************************************************************************** ; ; MyWordV - Routine on WordV ; MyWordV ROUT TEQ R0, #OswordSetTime MOVNE PC, R14 Push "R0,R14" BL InitVars Pull "R0,PC" ; ************************************************************************** ; ; MyCallEvery - CallEvery routine ; MyCallEvery ENTRY "R0,R1,R8,R9" MOV R9, PC ; save old processor mode ORR R8, R9, #SVC_mode ; make SVC mode TEQP R8, #0 MOVNV R0, R0 Push R14 ADR R0, Recalibrate MOV R1, R12 SWI XOS_AddCallBack Pull R14 TEQP R9, #0 ; restore old processor mode MOVNV R0, R0 LDR R0, CallBackPending ADD R0, R0, #1 ; indicate another pending CallBack STR R0, CallBackPending EXIT ; ************************************************************************** ; ; Recalibrate - Recalibration routine ; ; in: SVC mode ; R12 -> my workspace ; ; (P+C) P' ; new latch := old latch * ------ * -------- ; P (P'-C+W) ; ; where P = Period, the requested number of cs ticks between calls ; ; P' = the actual number of cs ticks since last called (different from ; P because of the time taken to grant the callback) ; ; C = the number of cs we are ahead of the RTC this call ; ; W = the number of cs we were ahead of the RTC last call ; Recalibrate ENTRY "R0-R9" LDR R0, CallBackPending SUBS R0, R0, #1 ; one fewer pending callbacks STRCS R0, CallBackPending ; don't go negative MOV R0, #OsbyteReadCMOS ; Read year MOD 100 MOV R1, #YearCMOS SWI XOS_Byte MOVVC R4, R2 ; and put it into R4 MOVVC R1, #YearCMOS +1 ; Read year DIV 100 SWIVC XOS_Byte MOVVS R0, #Failure_ReadCMOS BVS %FT99 MOV R5, R2 ; and put it into R5 ; now read the RTC values SUB R13, R13, #2*4 ; need 6 bytes rounded up MOV R1, R13 MOV R2, #&01 ; CMOS address to start reading from STRB R2, [R1,#0] ; and also length of block MOV R0, #&A0 ; write CMOS address SWI XIIC_Control MOVVS R0, #Failure_WriteIIC BVS %FT98 MOV R0, #&A1 ; read MOV R2, #6 ; 6 bytes SWI XIIC_Control MOVVS R0, #Failure_ReadIIC BVS %FT98 LDRB R2, [R1, #5-1] ; register 5 (top 2 bits = year) AND R0, R4, #3 ; CMOS RAM year AND 3 RSBS R0, R0, R2, LSR #6 ; RTC year AND3 - CMOS RAM year AND3 ADDCC R0, R0, #4 ; if -ve then RTC year has wrapped ADD R4, R4, R0 ; add onto CMOS RAM year CMP R4, #100 ; if year lo >= 100 SUBCS R4, R4, #100 ; then wrap it ADDCS R5, R5, #1 ; and increment year hi CMPCS R5, #100 ; if year hi >= 100 SUBCS R5, R5, #100 ; then wrap it AND R2, R2, #&3F ; knock out year bits from d-o-m BCDToBinary R2, R14 ; and convert to binary LDRB R6, [R1, #2-1] ; register 2 (seconds) BCDToBinary R6, R14 LDRB R7, [R1, #1-1] ; register 1 (centiseconds) BCDToBinary R7, R14 LDRB R14, [R1, #6-1] ; register 6 (weekday/month) AND R3, R14, #&0F ; extract lo nybble TST R14, #&10 ; only one bit in hi nybble ADDNE R3, R3, #10 ; which adds 10 LDRB R0, [R1, #4-1] ; register 4 (hours/am-pm/12-24) AND R0, R0, #&3F ; extract hours BCDToBinary R0, R14 LDRB R1, [R1, #3-1] ; register 3 (minutes) BCDToBinary R1, R14 ADD R13, R13, #2*4 BL ConvertTo5Byte CMP R8, #-1 MOVEQ R0, #Failure_Conversion BEQ %FT99 STR R7, RTCCentiLo STR R8, RTCCentiHi ADR R1, IOCCentiLo MOV R0, #3 ; read 5-byte IOC realtime STRB R0, [R1, #0] MOV R0, #&0E SWI XOS_Word MOVVS R0, #Failure_ReadIOCTime BVS %FT99 LDR R5, IOCCentiLo LDRB R6, IOCCentiHi SUBS R5, R5, R7 ; R5 = C SBCS R6, R6, R8 CMP R6, R5, ASR #31 ; if hiword not all bit 31 of loword MOVNE R0, #Failure_ErrorTooBig ; then error BNE %FT99 SWI XOS_ReadMonotonicTime MOVVS R0, #Failure_ReadMonoTime BVS %FT99 LDR R1, LastMonoTime STR R0, LastMonoTime SUB R9, R0, R1 ; R9 = P' LDR R0, LastLatchValue ; R0 = L MOV R1, #0 ; R1 = L(hi) = 0 LDR R2, =Period ; R2 = P ADDS R3, R2, R5 ; R3 = P + C MOVLT R3, #0 ; if <0 then make 0 MOV R4, #0 ; R4 = (P + C)(hi) = 0 mextralong_multiply R6, R7, R0, R1, R3, R4 ; R6,R7 = L*(P+C) mextralong_multiply R3, R4, R6, R7, R9, R1 ; R3,R4 = L*(P+C)*P' LDR R8, LastError ; R8 = W STR R5, LastError SUB R5, R5, R8 ; R5 = C-W SUBS R9, R9, R5 ; R9 = P'-(C-W) MOVLE R9, #1 ; if <=0 then make 1 mextralong_divide R6, R7, R3, R4, R2, R1, R0, R5, R8 ; R6,R7 = L*(P+C)*P'/P mextralong_divide R3, R4, R6, R7, R9, R1, R0, R5, R8 ; R3,R4 = L*(P+C)*P'/P/(P'-(C-W)) LDR R1, =MaximumLatchValue CMP R3, R1 SBCS R0, R4, #0 MOVGE R3, R1 MOVGE R4, #0 LDR R1, =MinimumLatchValue CMP R3, R1 SBCS R0, R4, #0 MOVLT R3, R1 MOVLT R4, #0 CMP R3, #&10000 CMPCC R4, #1 MOVCS R0, #Failure_NewLatchTooBig BCS %FT99 STR R3, LastLatchValue BL ProgramLatch MOV R0, #0 B %FT99 98 ADD R13, R13, #2*4 99 STR R0, FailureMode [ SingleCallEveryFix LDR R0, NCorrections ADD R0, R0, #1 STR R0, NCorrections ] EXIT ; ************************************************************************** ; ; ProgramLatch - Program IOC Timer 0 latch with R3 value ; ProgramLatch ENTRY "R0,R3" SUB R3, R3, #1 ; TMD 21-May-93: Apply correction - IOC latch should be programmed with n-1 MOV R0, #IOC STRB R3, [R0, #Timer0LL] MOV R3, R3, LSR #8 STRB R3, [R0, #Timer0LH] EXIT ; ************************************************************************** ; ; ConvertTo5Byte - Convert real time value (separate registers) into ; 5-byte centisecond format ; ; in: R0 = hours ; R1 = minutes ; R2 = days ; R3 = months ; R4 = year MOD 100 ; R5 = year DIV 100 ; R6 = seconds ; R7 = centiseconds ; ; out: R7 = centiseconds (lo) ; R8 = centiseconds (hi) ; R0-R6, R9 corrupted ; ConvertTo5Byte ENTRY MOV R9, #24 SUB R2, R2, #1 ; decrement day (day=1 => nowt to add) MLA R0, R9, R2, R0 ; R0 = hours + day*24 MOV R9, #60 MLA R1, R0, R9, R1 ; R1 = mins + hours*60 MLA R6, R1, R9, R6 ; R6 = secs + mins*60 MOV R9, #100 MLA R7, R6, R9, R7 ; R7 = centisecs + secs*100 ADR R0, STMonths-4 ; Point to table (month = 1..12) LDR R1, [R0, R3, LSL #2] ; get word of offset ADD R7, R7, R1 ; add to total ; if not had leap day in this year yet, then exclude this year from the ; leap day calculations CMP R3, #3 ; if month >= 3 SBCS R0, R4, #0 ; then R0,R1 = R4,R5 MOVCC R0, #99 ; else R0,R1 = R4,R5 -1 SBC R1, R5, #0 ; want (yl+100*yh) DIV 4 - (yl+100*yh) DIV 100 + (yl+100*yh) DIV 400 ; = (yl DIV 4)+ (25*yh) - yh + (yh DIV 4) ; = (yl >> 2) + 24*yh + (yh >> 2) MOV R0, R0, LSR #2 ; yl >> 2 ADD R0, R0, R1, LSR #2 ; + yh >> 2 ADD R0, R0, R1, LSL #4 ; + yh * 16 ADD R0, R0, R1, LSL #3 ; + yh * 8 ; now subtract off the number of leap days in first 1900 years = 460 SUBS R0, R0, #460 BCC BadYear ; before 1900, so bad CMP R0, #86 ; if more than 86 days, then it's BCS BadYear ; after 2248, so bad LDR R9, =ticksperday ; multiply by ticksperday and add to MLA R7, R9, R0, R7 ; total (no overflow possible as this ; can never be more than 85+31 days) ; now add on (year-1900)*ticksperyear SUBS R5, R5, #19 ; subtract off 1900 BCC BadYear MOV R9, #100 MLA R4, R9, R5, R4 ; R4 = year-1900 LDR R0, =ticksperyear ; lo word of amount to add on MOV R1, #0 ; hi word of amount to add on MOV R8, #0 ; hi word of result 10 MOVS R4, R4, LSR #1 BCC %FT15 ADDS R7, R7, R0 ; if bit set then add on amount ADCS R8, R8, R1 BCS BadYear ; overflow => bad time value 15 ADDS R0, R0, R0 ; shift up amount ADCS R1, R1, R1 TEQ R4, #0 ; if still bits to add in BNE %BT10 ; then loop CMP R8, #&100 ; R8 must only be a byte EXIT CC BadYear MOV R7, #-1 MOV R8, #-1 EXIT tickspersecond * 100 ticksperminute * tickspersecond * 60 ticksperhour * ticksperminute * 60 ticksperday * ticksperhour * 24 ticksperyear * ticksperday * 365 ; &BBF81E00 STMonths & &00000000 ; Jan & &0FF6EA00 ; Feb & &1E625200 ; Mar & &2E593C00 ; Apr & &3DCC5000 ; May & &4DC33A00 ; Jun & &5D364E00 ; Jul & &6D2D3800 ; Aug & &7D242200 ; Sep & &8C973600 ; Oct & &9C8E2000 ; Nov & &AC013400 ; Dec & &F0000000 ; terminator, must be less than this (+1) CopyError ENTRY r1-r7 BL open_messagefile EXIT VS ADR R1, MessageFile_Block MOV R2, #0 MOV R4, #0 MOV R5, #0 MOV R6, #0 MOV R7, #0 SWI XMessageTrans_ErrorLookup EXIT message_filename DCB "Resources:$.Resources.RTCAdjust.Messages", 0 ALIGN open_messagefile ENTRY r0-r2 LDR r0, MessageFile_Open CMP r0, #0 EXIT NE ADR R0, MessageFile_Block ADR R1, message_filename MOV r2, #0 SWI XMessageTrans_OpenFile EXIT VS MOV r0, #1 STR r0, MessageFile_Open EXIT END