; 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