; 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.osword

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

        MACRO
        WordReturnV $cond

        ASSERT  "$cond"="" :LOR: "$cond"="VS"

        [ "$cond"=""
        LDRVC   R0,[R13]
        ]
        LDM$cond.IB R13,{R1-R4,R11,WsPtr}
        ADD$cond    R13,R13,#8*4 ; R0-R4,R11,WsPtr,R14
        LDR$cond    PC,[R13],#4

        MEND

; Main OsWord entry point
; R0,R1,R2 are parameters


OsWord
        Push    "R0-R4, R11, R12, R14"
        CMP     R0, #(WordTableEnd - WordTableStart) :SHR: 2
        BLLS    OsWordGo                        ; Call the subsid entry pt.
        LDMIA   R13, {R2-R4}                    ; R2=A, R3=X, R4=Y
        MOV     R1, #Service_UKWord             ; osword service reason
        CLRPSR  V_bit, R0                       ; in case there's no service
        BL      Issue_Service
        TEQ     R1, #0
        STMEQIA R13, {R2-R4}                    ; if claimed, then update
                                                ; returned R0-R2
        Pull    "R0-R4, R11, R12, PC"           ; pass V back from service

GoMyOsword
        CLRPSR  V_bit, R4
        Pull    "R0-R4, R11, R12"
        ADD     R13, R13, #4
        Pull    PC

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

OsWordGo ROUT
        BYTEWS  WsPtr
10                                              ; Point to despatch table
        ADD     PC, PC, R0, LSL #2              ; add in the action*4 and go
        &       0
        ASSERT  WordTableStart - %BT10 = 8

WordTableStart

        BAL     OsWord00
        BAL     OsWord01
        BAL     OsWord02
        BAL     OsWord03
        BAL     OsWord04
        BAL     OsWord05
        BAL     OsWord06
        BAL     OsWord07

        BAL     OsWord08
        BAL     OsWord09
        BAL     OsWord0A
        BAL     OsWord0B
        BAL     OsWord0C
        BAL     OsWord0D
        BAL     OsWord0E
        BAL     OsWord0F

        BAL     OsWord10
        BAL     OsWord11
        BAL     OsWord12
        BAL     OsWord13
        BAL     OsWord14
        BAL     OsWord15
        BAL     OsWord16

WordTableEnd

; *****************************************************************************
; That's All Folks
; *****************************************************************************

; *****************************************************************************
; The OsWord routines themselves
; *****************************************************************************

; Osword Zero : Input a line

OsWord00 ROUT
      [ NoARMv6 :LOR: NoUnaligned
        LDRB    R0, [R1, #0]            ; lo-byte of address
        LDRB    R2, [R1, #1]            ; hi-byte of address
        ORR     R0, R0, R2, LSL #8      ; R0 := address
      |
        ; Use unaligned load from ARMv6
        LDRH    R0, [R1]                ; 16-bit address(!)
      ]
        LDRB    R2, [R1, #3]            ; lo limit
        LDRB    R3, [R1, #4]            ; hi limit
        LDRB    R1, [R1, #2]            ; length of buffer
        MOV     R4, #0                  ; flags
        SWI     XOS_ReadLine32
        WordReturnV VS

        MOV     R2, R1                  ; put line length into R2
        Pull    "R0,R1,R3"              ; don't overwrite R2
        Pull    "R3, R4, R11, R12"
        ADD     R13, R13, #4
        Pull    PC

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

; Read/Write System Clock
; entered with IRQs off
; in:   R0 = 1 => read clock
;       R0 = 2 => write clock

OsWord01 ROUT
OsWord02 ROUT
        CMP     R0, #2                  ; C=1 <=> write clock
        LDRB    R0, TimerState
        EORCS   R0, R0, #&0F            ; if writing, then write to other state
                                        ; in case user resets in middle
        TEQ     R0, #5                  ; 5 => alpha, 10 => beta (C preserved)
        ADREQ   R2, TimerAlpha
        ADRNE   R2, TimerBeta
        Swap    R1, R2, CS              ; if writing then R2 is destination
      [ NoARMv6 :LOR: NoUnaligned
        MOV     R3, #5
10
        LDRB    R4, [R2], #1
        STRB    R4, [R1], #1
        SUBS    R3, R3, #1
        BNE     %BT10
      |
        ; Use unaligned load/store from ARMv6
        LDR     R3, [R2], #4
        LDRB    R4, [R2]
        STR     R3, [R1], #4
        STRB    R4, [R1]
      ]
        STRB    R0, TimerState          ; if writing, switch state
                                        ; (if reading, write current state)
        MyOsWord

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

; Read/Write Interval Timer
; entered with IRQs off
; in:   R0 = 3 => read interval timer
;       R0 = 4 => write interval timer

OsWord03 ROUT
OsWord04 ROUT
        CMP     R0, #4                  ; C=1 => write timer
        MOVCS   R2, R1                  ; if writing then R1 is source
        ADRCS   R1, IntervalTimer
        ADRCC   R2, IntervalTimer       ; else R2 is source
      [ NoARMv6 :LOR: NoUnaligned
        MOV     R0, #5
10
        LDRB    R3, [R2], #1
        STRB    R3, [R1], #1
        SUBS    R0, R0, #1
        BNE     %BT10
      |
        ; Use unaligned load/store from ARMv6
        LDR     R0, [R2], #4
        LDRB    R3, [R2]
        STR     R0, [R1], #4
        STRB    R3, [R1]
      ]
        MyOsWord

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

; Perform a SOUND command
OsWord07 ROUT
        TST     R1, #3
        BNE     %FT05
        LDMIA   R1, {R0,R1}
        SWI     XSound_ControlPacked
        MyOsWord
05

      [ NoARMv6 :LOR: NoUnaligned
; Block not word aligned, so push it on the stack

        SUB     R13, R13, #8            ; create stack frame of 8 bytes
        MOV     R0, #7
10
        LDRB    R2, [R1, R0]            ; copy block into stack frame
        STRB    R2, [R13, R0]
        SUBS    R0, R0, #1
        BCS     %BT10

        Pull    "R0, R1"                ; then pull stack frame into R0 and R1
      |
        ; Use unaligned load from ARMv6
        LDR     R0, [R1], #4
        LDR     R1, [R1]
      ]
        SWI     XSound_ControlPacked
        MyOsWord

; *****************************************************************************
; Read the logical colour of a Pixel ( BASIC's POINT function)
; Uses SWI ReadPoint

OsWord09 ROUT
        Push    R1                      ; save pointer

      [ NoARMv6 :LOR: NoUnaligned
        LDRB    R2, [R1, #0]            ; X lo-byte
        LDRB    R0, [R1, #1]            ; X hi-byte
        ORR     R0, R2, R0, LSL #8

        MOV     R0, R0, LSL #16         ; sign extend X
        MOV     R0, R0, ASR #16

        LDRB    R2, [R1, #2]            ; Y lo-byte
        LDRB    R1, [R1, #3]            ; Y hi-byte
        ORR     R1, R2, R1, LSL #8

        MOV     R1, R1, LSL #16         ; sign extend Y
        MOV     R1, R1, ASR #16
      |
        ; Use unaligned load from ARMv6
        LDRSH   R0, [R1], #2
        LDRSH   R1, [R1]
      ]

        SWI     XOS_ReadPoint   ; in: R0=X, R1=Y
                                ; out: R2=colour, R3=tint, R4=0/-1 (on/off)
        Pull    R1
        STRB    R2, [R1, #4]
        WordReturnV

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

; Read a character definition

OsWord0A ROUT
        ByteToNosbod DoReadFont
        MyOsWord

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

; Read the palette setting (VDU19,L,P,R,G,B)

OsWord0B ROUT
        ByteToNosbod DoReadPalette
        MyOsWord

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

; Write the palette setting (see VDU19)

OsWord0C ROUT
        ByteToNosbod DoSetPalette
        MyOsWord

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

; Read the last two graphics cursor positions

OsWord0D ROUT
        ByteToNosbod DoOsWord13
        MyOsWord

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

; Read CMOS clock

OsWord0E ROUT
        Push    "R5-R8, R14"            ; R0-R4 saved by Osword

        MOV     R4, R1                  ; pointer to the Osword Block

        LDRB    R0, [R4, #0]
        CMP     R0, #1
        BCC     OsWord0EAlpha
        BEQ     OsWord0EBeta
        CMP     R0, #3
        BCC     OsWord0EGamma
        BEQ     OsWord0EDelta

        Pull    "R5-R8, PC"             ; unknown option

; *****************************************************************************
;
;       OsWord0EAlpha - Read time as a string in the form
;       eg Wed,01 Jan 1986.12:34:56
;
; in:   R1 -> buffer for string
;

OsWord0EAlpha ROUT

        ADR     R0, RealTime            ; load snapshot of 5 bytes of real time
        LDMIA   R0, {R0, R2}            ; while IRQs are still off
        Push    "R0, R2"                ; save on stack
        CLRPSR  I_bit, R0               ; enable IRQs now

OSWord0EReturnString
        MOV     R0, #-1                 ; This territory
        MOV     R2, R1
        MOV     R1, R13                 ; point to stacked copy
; KJB 07-Sep-98: No-one is guaranteeing anywhere the length of %w3 or %m3 -
; see PRM 1-402 and 1-415. So give an indefinite buffer length here (making
; overflow the caller's problem - this call is obsolete anyway). Problem
; first noted with territory Japan, for which %m3 is potentially 5 bytes long.
        MOV     R3, #&10000000
        ADR     R4, TimeFormat
        SWI     XTerritory_ConvertDateAndTime
        ADD     R13, R13, #8            ; junk stack frame
        MOVVC   R0, #13                 ; if no error
        STRVCB  R0, [R1]                ; overwrite terminating 0 with CR

        Pull    "R5-R8,R14"
        WordReturnV

TimeFormat
        =       "%w3,%dy %m3 %ce%yr.%24:%mi:%se", 0
        ALIGN


; *****************************************************************************
;
;       OsWord0EBeta - Read time in BCD format
;
; in:   R4 -> parameter block
;
; out:  [R4, #0] = year         (00-99)
;       [R4, #1] = month        (01-12)
;       [R4, #2] = day of month (01-31)
;       [R4, #3] = day of week  (01-07) Sun=01
;       [R4, #4] = hours        (00-23)
;       [R4, #5] = minutes      (00-59)
;       [R4, #6] = seconds      (00-59)
;

OsWord0EBeta ROUT

        ADR     R0, RealTime            ; load snapshot of 5 bytes of real time
        LDMIA   R0, {R0, R2}            ; while IRQs are still off
        Push    "R0, R2"                ; save on stack
        CLRPSR  I_bit, R0               ; this may take some time

        MOV     R0,#-1
        MOV     R1,SP
        SUB     SP,SP,#36               ; Space for ordinals.
        MOV     R2,SP
        SWI     XTerritory_ConvertTimeToOrdinals
        ADDVS   SP,SP,#36+(2*4)
        BVS     OSWord0Eerror

;   [R2]    = CS.                     ; all values are for LOCAL time
;   [R2+4]  = Second
;   [R2+8]  = Minute
;   [R2+12] = Hour (out of 24)
;   [R2+16] = Day number in month.
;   [R2+20] = Month number in year.
;   [R2+24] = Year number.
;   [R2+28] = Day of week.
;   [R2+32] = Day of year

        LDR     R0,[R2,#24]             ; Get year
        LDR     R1,=1900
        SUB     R0,R0,R1
01
        CMP     R0,#100
        SUBGT   R0,R0,#100
        BGT     %BT01                   ; Get year MOD 100.

        STRB    R0,[R4,#0]              ; Store it.
        LDR     R0,[R2,#20]
        STRB    R0,[R4,#1]
        LDR     R0,[R2,#16]
        STRB    R0,[R4,#2]
        LDR     R0,[R2,#28]
        STRB    R0,[R4,#3]
        LDR     R0,[R2,#12]
        STRB    R0,[R4,#4]
        LDR     R0,[R2,#8]
        STRB    R0,[R4,#5]
        LDR     R0,[R2,#4]
        STRB    R0,[R4,#6]

        ADD     SP,SP,#36+(2*4)        ; junk stack frame and 5 byte time.


; now we have the time in hex in the parameter block
; so convert each item into BCD

        MOV     R1, #6                  ; seven bytes to convert

10
        LDRB    R0, [R4, R1]
        BL      HexToBCD
        STRB    R0, [R4, R1]
        SUBS    R1, R1, #1
        BPL     %BT10

        B       OsWord0Eend

; *****************************************************************************
;
;       OsWord0EGamma - Convert time in BCD format (at offsets 1..7)
;       into string format at offsets (0..24)
;
; in:   R4 -> BCD time
;

OsWord0EGamma ROUT

;build a block for Territory_ConvertOrdinalsToTime
;   [R2]    = CS.                     ; all values are for LOCAL time
;   [R2+4]  = Second
;   [R2+8]  = Minute
;   [R2+12] = Hour (out of 24)
;   [R2+16] = Day number in month.
;   [R2+20] = Month number in year.
;   [R2+24] = Year number.

        SUB     SP,SP,#28
        MOV     R2,SP
        MOV     R0,#0
        STR     R0,[R2]

        LDRB    R0,[R4,#7]              ; Seconds
        BL      BCDToHex
        STR     R0,[R2,#4]

        LDRB    R0,[R4,#6]              ; Minutes
        BL      BCDToHex
        STR     R0,[R2,#8]

        LDRB    R0,[R4,#5]              ; Hours
        BL      BCDToHex
        STR     R0,[R2,#12]

        LDRB    R0,[R4,#3]              ; Day of month
        BL      BCDToHex
        STR     R0,[R2,#16]

        LDRB    R0,[R4,#2]              ; Month
        BL      BCDToHex
        STR     R0,[R2,#20]


        LDRB    R0,[R4,#1]              ; Year
        BL      BCDToHex
        LDR     R1,=1900
        ADD     R0,R0,R1
        STR     R0,[R2,#24]

        MOV     R0,#-1
        ADD     R1,SP,#20               ; Put value on satck
        SWI     XTerritory_ConvertOrdinalsToTime
        ADDVS   SP,SP,#28
        BVS     OSWord0Eerror

        ADD     SP,SP,#20
        MOV     R1,R4
        B       OSWord0EReturnString    ; Now we have 5 byte value on stack,
                                        ; use same code as OSWord0EAlpha

; *****************************************************************************
;
;       OsWord0EDelta - Read 5-byte RealTime
;
; in:   R4 -> block
;
; out:  [R4, #0..4] = RealTime
;

OsWord0EDelta ROUT
        LDR     R1, RealTime +0
      [ NoARMv6 :LOR: NoUnaligned
        STRB    R1, [R4, #0]
        MOV     R1, R1, LSR #8
        STRB    R1, [R4, #1]
        MOV     R1, R1, LSR #8
        STRB    R1, [R4, #2]
        MOV     R1, R1, LSR #8
        STRB    R1, [R4, #3]
      |
        ; Use unaligned store from ARMv6
        STR     R1, [R4, #0]
      ]
        LDRB    R1, RealTime +4
        STRB    R1, [R4, #4]

; and drop thru to ...

OsWord0Eend
        Pull    "R5-R8, R14"
        MyOsWord

OSWord0Eerror
        Pull    "R5-R8, R14"
        WordReturnV

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

; Osword 15 (&0F) Write the Real Time Clock.
; Four different calls

OsWord0F ROUT
        CLRPSR  I_bit, R0               ; this may take some time

        Push    "R5-R10, R14"
        MOV     R4, R1                  ; Copy the parameter block pointer
        LDRB    R0, [R1]
        MOV     R9, #0

        TEQ     R0, #5                  ; write all of time (5-byte UTC)
        BEQ     OsWord0F_5byte

        TEQ     R0, #8                  ; write hours, minutes, seconds
        MOVEQ   R9, #1

        TEQ     R0, #15                 ; write day, date, month, year
        MOVEQ   R9, #2

        TEQ     R0, #24                 ; write all of time
        MOVEQ   R9, #3

        TEQ     R9, #0
        Pull    "R5-R10, PC", EQ        ; unknown call, pass it on

        TST     R9, #2
        BEQ     %FT01                   ; no date parsing

; KJB 980908 - can't assume length of date, as %w3, %dy and %m3 may be any length. Unfortunately,
; the string may not be terminated. Best we can do is plop a terminator at the maximum length position,
; which will at least cope with territories with fixed-length strings. Territories with variable length
; strings may work anyway, as territory modules are quite good at ignoring trailing junk. I've
; fixed things I've spotted that didn't terminate, such as BASIC.

        SUB     SP, SP, #48
        MOV     R0, #-1
        MOV     R2, SP                  ; R1 points to real memory - contents don't matter to us
        SWI     XTerritory_ReadCalendarInformation
        ADDVS   SP, SP, #48
        BVS     Bad0F

; need to work out maximum length of "%w3,%dy %m3 %ce%yr[.%24:%mi:%se]"
        TST     R9, #1
        MOVEQ   R0, #7                  ; length of ",  %ce%yr"
        MOVNE   R0, #16                 ; length of ",  %ce%yr.%24:%mi:%se"
        LDR     R10, [R2, #24]
        LDR     R14, [R2, #28]
        ADD     R0, R0, R10             ; + max length of %w3
        LDR     R10, [R2, #40]
        ADD     R0, R0, R14             ; + max length of %dy
        ADD     R0, R0, R10             ; + max length of %m3
        ADD     SP, SP, #48

01      ADD     r10, r0, #3+1           ; round up number of bytes in block to word boundary, including null terminator
        BIC     r10, r10, #3
        SUB     sp, sp, r10

        ADD     r2, r1, #1              ; point at actual string
        MOV     r1, #0
02
        LDRB    r14, [r2, r1]           ; copy string (not terminated) on stack
        STRB    r14, [sp, r1]
        ADD     r1, r1, #1
        TEQ     r1, r0                  ; have we copied all bytes of string?
        BNE     %BT02                   ; loop if not

        MOV     r14, #0                 ; null terminator
        STRB    r14, [sp, r0]

        MOV     r0,#-1                  ; set things up for territory SWI - r0 = -1 for current territory
        MOV     r1, r9                  ; r1 = reason code (1, 2 or 3)
        MOV     r2, sp                  ; r2 -> terminated string on stack
        SUB     sp, sp, #36             ; get space for result.
        MOV     r3, sp

        SWI     XTerritory_ConvertTimeStringToOrdinals
        ADDVS   sp, sp, #36             ; if error then junk return block
        ADDVS   sp, sp, r10             ; and junk variable length string on stack
        BVS     Bad0F

        CMP     r9, #2                  ; if writing everything just go to UTC conversion step
        BHI     %FT05

; We have the time but no date, or have the date but no time. Get the missing fields
; because changing the one can roll the other across a timezone.

        ADDLO   r7, sp, #0*4
        ADDEQ   r7, sp, #4*4
        LDMIA   r7, {r3-r6}             ; preserve the values we have converted

        ADR     r0, RealTime
        LDMIA   r0, {r0,r1}             ; LDM is atomic wrt interrupts

        Push    "r0,r1"                 ; put value on stack
        MOV     r0,#-1                  ; use configured territory.
        MOV     r1, sp
        ADD     r2, sp, #8
        SWI     XTerritory_ConvertTimeToOrdinals        ; get ordinals for current time
        ADDVS   sp, sp, #36+8           ; 36 from above + 8 for stacked 5 byte time
        ADDVS   sp, sp, r10             ; and junk string as well
        BVS     Bad0F
        ADD     sp, sp, #8              ; dump 5 byte time on TOS

        STMIA   r7, {r3-r6}             ; restore the values we have converted

05
; Now [SP] -> ordinals in local time, but we want time in UTC
; First convert the ordinals to 5 byte UTC time

        MOV     r0, #-1                 ; use configured territory.
        MOV     r2, sp                  ; r2 -> ordinals block
        SUB     sp, sp, #8              ; two more words to contain 5 byte time
        MOV     r1, sp
        SWI     XTerritory_ConvertOrdinalsToTime
        ADDVS   sp, sp, #36+8           ; 36 from above + 8 for 5 byte time
        ADDVS   sp, sp, r10             ; and junk string as well
        BVS     Bad0F

; Now we have a 5 byte UTC time, convert it to UTC ordinals

        MOV     r1, sp                  ; our 5 byte time
        ADD     r2, sp, #8              ; place to put ordinals
        SWI     XTerritory_ConvertTimeToUTCOrdinals
        ADDVS   sp, sp, #36+8           ; 36 bytes ordinals + 8 for 5 byte time
        ADDVS   sp, sp, r10             ; and junk string as well
        BVS     Bad0F

        ADD     sp, sp, #8              ; discard 5 byte time

10
; Load the registers. (SP->Ordinals)

        LDR     r8, [sp], #4            ; centiseconds
        LDR     r7, [sp], #4            ; seconds
        LDR     r1, [sp], #4            ; minutes
        Pull    "r0,r2,r3,r5"           ; hours, day of month, month, year
        ADD     sp, sp, #8              ; junk day of week, day of year
        ADD     sp, sp, r10             ; and string on stack
        DivRem  r6, r5, #100, r14       ; r5 = Year (lo), r6 = Year (hi)

        BL      SetTime                 ; also updates 5-byte RealTime

Bad0F                                   ; come here if setting invalid
        Pull    "R5-R10, R14"
        MyOsWord

OsWord0F_5byte
        SUB     SP, SP, #36
        ADD     R1, R1, #1              ; process the user's 5-byte block
        MOV     R2, SP
        SWI     XTerritory_ConvertTimeToUTCOrdinals
        ADDVS   SP, SP, #36
        BVS     Bad0F
        MOV     R10, #0                 ; no string on stack
        B       %BT10                   ; nip back in to the main handler

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

; Define hardware cursor size, shape and active point

OsWord15
        ByteToNosbod DoPointerStuff
        MyOsWord

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

; Set start of screen address (for VDU drivers and display)
; [R1, #0] = bit mask (bit0 set => change drivers; bit1 set => change display)
; [R1, #1..4] = byte offset from start of screen memory

OsWord16
        ByteToNosbod DoSetScreenStart
        MyOsWord

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

; All the unused OS_Word calls

; Read Byte of I/O proc memory
OsWord05 ROUT
; Write byte of I/O proc memory
OsWord06 ROUT
; Define an ENVELOPE
OsWord08 ROUT
; Allocated to the net
OsWord10
OsWord11
OsWord12
OsWord13
OsWord14
        Unused

        LTORG

        END