; 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.
;
; > $.Source.PMF.realtime

; *****************************************************************************
;
;       SWI OS_ResyncTime
;
; in:   r0  = 0 - Real time clock soft copy only
;       r0 <> 0   reserved for future expansion
; out:  registers preserved
;    or VS and R0 -> error
;
ResyncTimeSWI ROUT
        Push    "R1, R12, LR"
        BYTEWS  WsPtr
        BL      RTCToRealTime
        MOVVC   R1, #Service_RTCSynchronised
        BLVC    Issue_Service
        Pull    "R1, R12, LR"
        B       SLVK_TestV

; *****************************************************************************
;
;       SetTime - Write the CMOS clock time and update 5-byte RealTime
;
; in:   UTC time:
;       R0 = hours
;       R1 = minutes
;       R2 = day of month (1-based)
;       R3 = month (1-based)
;       R5 = year (lo)
;       R6 = year (hi)
;       R7 = seconds
;       R8 = centiseconds
; out:  registers preserved
;    or VS and R0 -> error
;
;       If R0,R2,R5 or R6 is -1 then the time,date,year,century (respectively) will not be written.
;       However if R2=-1, R5 & R6 will also be -1, so only R0 (for time) and R2 (for D/M/Y) need checking.
;

SetTime ROUT
        Push    "R4, R9, LR"

        ; Prepare BCD data on stack
        ASSERT  (RTCTimeStruct_Size :AND: 3)=0
        SUB     R13, R13, #RTCTimeStruct_Size
        MOV     R9, R13
        MOV     R4, R0

        ; Ensure struct matches our setting order
        ASSERT  RTCTimeStruct_Centiseconds = 0
        ASSERT  RTCTimeStruct_Seconds = 1
        ASSERT  RTCTimeStruct_Minutes = 2
        ASSERT  RTCTimeStruct_Hours = 3
        ASSERT  RTCTimeStruct_DayOfMonth = 4
        ASSERT  RTCTimeStruct_Month = 5
        ASSERT  RTCTimeStruct_YearLO = 6
        ASSERT  RTCTimeStruct_YearHI = 7

        ; Are we setting the time?
        CMP     R4, #-1
        STREQ   R4, [R9], #4 ; Conveniently, 4 bytes of time info
        BEQ     %FT11
        MOV     R0, R8
        BL      HTBS9
        MOV     R0, R7
        BL      HTBS9
        MOV     R0, R1
        BL      HTBS9
        MOV     R0, R4
        BL      HTBS9
11
        ; Are we setting the date?
        CMP     R2, #-1
        STREQ   R2, [R9], #4 ; Conveniently, 4 bytes of date info
        BEQ     %FT12
        MOV     R0, R2
        BL      HTBS9
        MOV     R0, R3
        BL      HTBS9
        MOV     R0, R5
        BL      HTBS9
        MOV     R0, R6
        BL      HTBS9
12
        MOV     R0, SP
        SWI     XRTC_Write              ; Try update hardware

        MOV     R0, R4
        BL      RegToRealTime           ; Update soft copy regardless

        CLRV
        ADD     SP, SP, #RTCTimeStruct_Size
        Pull    "R4, R9, PC"

; *****************************************************************************
;
;       ReadTime - Read the CMOS clock time
;
; in:   -
; out:  R0 = hours
;       R1 = minutes
;       R2 = day of month (1-based)
;       R3 = month (1-based)
;       R5 = year (lo)
;       R6 = year (hi)
;       R7 = seconds
;       R8 = centiseconds
;    or VS and R0 -> error

ReadTime ROUT
        Push    "LR"

        ; Receive BCD data on stack
        ASSERT  (RTCTimeStruct_Size :AND: 3)=0
        SUB     R13, R13, #RTCTimeStruct_Size
        MOV     R0, R13
        SWI     XRTC_Read
        ADDVS   R13, R13, #RTCTimeStruct_Size
        Pull    "PC",VS

        ; Now convert the BCD ordinals
        LDRB    R0, [R13, #RTCTimeStruct_Centiseconds]
        BL      BCDToHex
        MOV     R8, R0                  ; centiseconds
        LDRB    R0, [R13, #RTCTimeStruct_Seconds]
        BL      BCDToHex
        MOV     R7, R0                  ; seconds
        LDRB    R0, [R13, #RTCTimeStruct_Minutes]
        BL      BCDToHex
        MOV     R1, R0                  ; minutes
        LDRB    R0, [R13, #RTCTimeStruct_DayOfMonth]
        BL      BCDToHex
        MOV     R2, R0                  ; days
        LDRB    R0, [R13, #RTCTimeStruct_Month]
        BL      BCDToHex
        MOV     R3, R0                  ; months
        LDRB    R0, [R13, #RTCTimeStruct_YearLO]
        BL      BCDToHex
        MOV     R5, R0                  ; year lo
        LDRB    R0, [R13, #RTCTimeStruct_YearHI]
        BL      BCDToHex
        MOV     R6, R0                  ; year hi
        LDRB    R0, [R13, #RTCTimeStruct_Hours]
        BL      BCDToHex
                                        ; hours
        CLRV

        ; We're done with our stack data, remove it
        ADD     R13, R13, #RTCTimeStruct_Size
        Pull    "PC"

; *****************************************************************************
;
;       RTCToRealTime - Set RealTime from actual RTC
;
; in:   R12 -> BYTEWS
; out:  all registers preserved
;    or VS and R0 -> error
;

RTCToRealTime ROUT
        Push    "R0-R9, LR"
        BL      ReadTime                ; R0 := hours, R1 := minutes
                                        ; R2 := days, R3 := months
                                        ; R5 := year(lo), R6 := year(hi)
                                        ; R7 := seconds, R8 := centiseconds
        BVS     %FT10
        BL      ConvertTo5Byte
        BL      Store5ByteInRealTime
        CLRV
10
        STRVS   R0, [SP, #0]
        Pull    "R0-R9, PC"

; *****************************************************************************
;
;       RegToRealTime - Set RealTime from a bunch of registers
;
; in:   R12 -> BYTEWS
;       R0-R3, R5-R8 = validated time and date
; out:  all registers preserved
;

RegToRealTime ROUT
        Push    "R0-R9, LR"
        BL      ConvertTo5Byte
        BL      Store5ByteInRealTime
        Pull    "R0-R9, PC"

; *****************************************************************************
;
;       Store5ByteInRealTime - put 5 byte centisecond UTC time into soft copy
;
; in:   R7 = low word of time
;       R8 = high byte of time
;       R12 -> BYTEWS
; out:  all registers preserved
;

Store5ByteInRealTime
        Push    "LR"
        PHPSEI                          ; disable IRQs for this bit
        STR     R7, RealTime +0
        STRB    R8, RealTime +4
        PLP
        Pull    "PC"

; *****************************************************************************
;
;       ConvertTo5Byte - Convert ordinals to 5 byte time
;
; in:   R12 -> BYTEWS
;       R0-R3, R5-R8 = time and date
; out:  R7 = low word
;       R8 = high byte
;    or R7 = R8 = -1 if conversion failed
;

ConvertTo5Byte ROUT
        Push    "LR"

        MOV     R4, R5                  ; R4 := year MOD 100
        MOV     R5, R6                  ; R5 := year DIV 100
        MOV     R6, R7                  ; R6 := seconds
        MOV     R7, R8                  ; R7 := centiseconds

        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
        Pull    "PC",CC

BadYear
        MOV     R7, #-1
        MOV     R8, #-1
        Pull    "PC"

; *****************************************************************************

tickspersecond  * 100
ticksperminute  * tickspersecond * 60
ticksperhour    * ticksperminute * 60
ticksperday     * ticksperhour   * 24
ticksperyear    * ticksperday    * 365  ; &BBF81E00

STMonths
        DCD     &00000000       ; Jan
        DCD     &0FF6EA00       ; Feb
        DCD     &1E625200       ; Mar
        DCD     &2E593C00       ; Apr
        DCD     &3DCC5000       ; May
        DCD     &4DC33A00       ; Jun
        DCD     &5D364E00       ; Jul
        DCD     &6D2D3800       ; Aug
        DCD     &7D242200       ; Sep
        DCD     &8C973600       ; Oct
        DCD     &9C8E2000       ; Nov
        DCD     &AC013400       ; Dec
        DCD     &F0000000       ; terminator, must be less than this (+1)

        LTORG

        END