; 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.Key ; ARTHUR keyboard code ; Authors Tim Dobson, Jon Thackray ; Started 13-Oct-86 ; ************************************************************ ; *** C h a n g e L i s t (better late than never!) *** ; ************************************************************ ; Date Who Description ; ---- --- ----------- ; 17-Feb-88 Added Sam's code to call the callback vector in RDCH/INKEY ; idle loop ; 02-Mar-88 Initialise KeyVec to NewKeyStruct before the keyboard has told ; us its ID, so Sam can call INKEY(-ve) with no keyboard ; 13-Apr-88 Fixed RDCH from RS423 if treating as keyboard, getting NUL+char ; (didn't try to reenable RTS on 2nd char) ; 11-Jun-88 Put input redirection where it was needed really. SKS ; 12-Aug-88 Read mouse position from buffer forces inside bounding box ; 02-Sep-88 Buffered mouse coords stored in absolute terms, not relative to ; origin at time of click. Made relative after reading out and ; clipping to bounding box. Mouse event coords are still relative ; to origin at time of click. ; 06-Feb-91 LastLED added to stop unnecessary LED transmissions ; ; 24-Feb-93 SMC Split off Archimedes keyboard driver into file KbdDrA1. ; Split off mouse stuff into file mouse. ; Added new interfaces for generic keyboard driver. ; 23-Aug-93 SMC GotKbId now sets KbId but remembers the last in LastKbId. ; 24-Aug-93 SMC Key handler can now get keyboard stuff going. ; 18-Nov-93 SMC Fixed bug in key handler stuff (no handler => KeyVec=-1). ; 25-Nov-93 SMC Key handler and keyboard driver now trigger each other correctly. ; 25-Apr-94 RCM ReadCh modified for Stork's power saving scheme. ; ; 28-Apr-04 JWB Added magic switch to turn off kernel kbd debounce ; r2 on KEYV vector 0 = 'NoKd' to disable debounce GBLL MouseBufferFix MouseBufferFix SETL {TRUE} ; ***************************************************************************** ; ; Entry point for keyboard code - Initialisation ; KeyInit ROUT LDR R11, =ZeroPage+KeyWorkSpace Push R14 WritePSRc I_bit :OR: SVC_mode, R14 [ Keyboard_Type = "A1A500" BL A1KeyInit ] MOV R0, #-1 ; no default key handler STR R0, KeyVec MOV R0, #&FF ; indicate no previous keyboard id STRB R0, LastKbId STRB R0, KbId BL ClearKbd [ Keyboard_Type = "A1A500" BL A1Reset ] Pull PC ; go back to user ; ***************************************************************************** ; ; KeyPostInit - Called after modules have initialised ; KeyPostInit ROUT Push R14 LDR R11, =ZeroPage+KeyWorkSpace PHPSEI ; disable interrupts round this bit Push R14 ; save I_bit indication LDRB R1, LastKbId TEQ R1, #&FF ; if no keyboard initialised yet then LDREQB R1, KbId ; try now BLEQ GotKbId Pull R14 ; restore I_bit indication PLP ; set I_bit from this LDROSB R1, LastBREAK ; is it a soft reset ? TEQ R1, #0 Pull PC, EQ ; if so, then exit MOV R0, #OsbyteSetCountry LDROSB R1, Country SWI XOS_Byte Pull PC ; ***************************************************************************** ClearKbd ROUT Push R14 MOV R0, #&FF STRB R0, CurrKey ; no current key STRB R0, OldKey STRB R0, LastLED ; Set up keyboard table MOV R0, #0 ; All keys up STR R0, KeysDown ; zero 160 bits = 5 words STR R0, KeysDown +4 STR R0, KeysDown +8 STR R0, KeysDown +12 STR R0, KeysDown +16 Pull PC ; ***************************************************************************** ; ; UpdateLEDs - Update the LED(s) from the keyboard status byte ; ; in: R11 -> keyboard workspace ; R12 -> IOC ; ; out: R0, R1 corrupted ; UpdateLEDs ROUT LDRB r0, KbId ; get keyboard id TEQ r0, #&FF ; if not found yet MOVEQ pc, lr ; then exit LDROSB r0, KeyBdStatus ; Build current LED state byte. TST r0, #KBStat_NoCapsLock MOVEQ r1, #1 MOVNE r1, #0 TST r0, #KBStat_NoNumLock ORREQ r1, r1, #2 TST r0, #KBStat_ScrollLock ORRNE r1, r1, #4 ; fall through ASSERT . = SetLEDs ; ***************************************************************************** ; ; SetLEDs - Set the LED(s) to a specific value ; ; in: R1 = desired LED state (bit 0 = caps lock, 1 = num lock, 2 = scroll lock) ; R11 -> keyboard workspace ; R12 -> IOC ; ; out: R0, R1 corrupted ; SetLEDs LDRB r0, LastLED ; Only update if different. TEQ r0, r1 MOVEQ pc,lr STRB r1, LastLED MOV r0, #3 [ AssembleKEYV Push "r10-r12,lr" MRS r11, CPSR ; Save current PSR. ORR r10, r11, #SVC_mode + I32_bit MSR CPSR_c, r10 ; Call KEYV in SVC mode, no IRQs. MOV r10, #KEYV Push "lr" ; Save SVC lr. BL CallVector Pull "lr" ; Restore SVC lr. MSR CPSR_cf, r11 ; Go back to old mode. NOP Pull "r10-r12,pc" | [ Keyboard_Type = "A1A500" B A1KeyVec | MOV pc, lr ] ] ; ***************************************************************************** ; ; LEDsOn - Turn on all the LEDs ; ; out: all registers preserved ; LEDsOn Push "r0,r1,r11,lr" MOV r1, #7 LDR r11, =ZeroPage+KeyWorkSpace BL SetLEDs Pull "r0,r1,r11,pc" ; ***************************************************************************** ; ; LEDsOff - Restore the LEDs to keyboard status ; ; out: all registers preserved ; LEDsOff Push "r0,r1,r11,lr" LDR r11, =ZeroPage+KeyWorkSpace BL UpdateLEDs Pull "r0,r1,r11,pc" ; ***************************************************************************** ; ; Handle new keyboard id. ; In: r1 = keyboard id ; r2 = magic 'NoKd' to disable kernel debouncing ; r11 = KeyWorkSpace ; Out: preserve flags ; GotKbId EntryS STRB r1, KbId ; Store the new keyboard id. LDR r0, KeyVec Push r0 ; Save old key handler so we know if it's changed. LDR lr, NoKbMagic TEQ r2, lr MOVNE lr, #0 STRB lr, NoDebounce ; remember kernel debounce switch LDRB r8, LastKbId TEQ r8, r1 ; If we have a different keyboard id then BLNE IssueKeyboardService ; issue service. LDR lr, KeyVec ; Get possibly new key handler. Pull r0 ; And old handler. TEQ r0, lr ; If key handler has not changed then BLEQ KeyboardEnable ; handler has not initialised with OS_InstallKeyHandler so we try. EXITS NoKbMagic DCD &4e6f4b64 ; Magic of NoKd to signal no kernel ; debounce ; ***************************************************************************** ; Initialise keyboard handler and enable keyboard driver. ; ; In: r1 = keyboard id ; KeyboardEnable Push lr BL ClearKbd LDR r0, KeyVec CMP r0, #-1 ; If no key handler LDRNEB r1, KbId ; or no keyboard yet then TEQNE r1, #&FF Pull pc, EQ ; can't initialise. STRB r1, LastKbId ; Remember last keyboard id that initialised. LDR r8, [r0, #KVInit] ; Initialise key handler. ADD r8, r8, r0 BL CallUserKeyCode MOV r0, #4 ; Initialise keyboard driver. [ AssembleKEYV MOV r10, #KEYV BL CallVector | [ Keyboard_Type = "A1A500" BL A1KeyVec ] ] Pull pc [ AssembleKEYV ; ***************************************************************************** ; ; Default KEYV handler (deal with keyboard id, keys up/down). ; ; In: r0 = reason code 0 ; r1 = keyboard id ; r2 = magic 'NoKd' if kernel debouncing to be disabled ; or ; r0 = reason code 1 or 2 ; r1 = key code ; KeyVector ROUT CMP r0, #3 ; If not id/key up/key down then Pull pc, CS ; just claim call. Push "r0-r12" LDR r11, =ZeroPage+KeyWorkSpace TEQ r0, #0 ; If keyboard id then BLEQ GotKbId ; handle it Pull "r0-r12,pc",EQ ; and claim call. | GotKey ROUT Push lr ] MOV r2, r1 SUB r1, r0, #1 CMP R2, #&A0 BCS %FT05 ADR R0, KeysDown MOV lr, R2, LSR #5 LDR lr, [R0, lr, LSL #2]! ; load appropriate word MOV R3, #&80000000 ; index 0 is in top bit TEQ r1, #0 BNE %FT03 TST lr, R3, ROR R2 ; if going up and key already up (keyboard reinitialised) then BEQ %FT50 ; nothing to do B %FT04 ; else clear flag, generate event etc. 03 TST lr, R3, ROR R2 ; if going down and key already down (weird) then BNE %FT50 ; nothing to do else... 04 EOR lr, lr, R3, ROR R2 ; switch state of flag STR lr, [R0] ; store back 05 BL KeyboardEvent ; generate key up/down event BL CheckForShiftingKey BCC %FT10 ; [not shifting key] BL CallSpecialReturnNChars B %FT50 10 TEQ r1, #0 ; if key up then BEQ %FT30 ; go and deal with it LDRB R0, CurrKey TEQ R0, #&FF ; have we got a current key ? BEQ %FT20 LDRB R1, OldKey TEQ R1, #&FF ; have we got an old key ? BNE %FT50 ; ignore new - we've got 2 down already STRB R0, OldKey ; make current key old 20 STRB R2, CurrKey ; update current LDRB r1, NoDebounce ; check debouncing? TEQ r1, #0 MOVEQ R0, #2 ; Eq.. normal kernel debounce MOVNE R0, #0 ; NE.. no kernel debounce STRB R0, Debouncing LDROSB R0, KeyRepDelay, NE ; and load delay STRB R0, AutoRepeatCount ; generate char after 2 100Hz ticks MOVNE r1,#2 ; mark as first key BLNE GenerateChar ; R2 = key number B %FT50 30 LDRB R0, OldKey TEQ R0, R2 ; is it old key going up ? BNE %FT40 ; Old key going up LDRB R0, CurrKey ; current key is one to ignore in scan BL ScanKeys STRPLB R0, OldKey ; found key, so current -> old BPL %BT20 ; and R2 -> current MOV R0, #&FF ; else mark old key invalid STRB R0, OldKey B %FT50 ; and return 40 LDRB R1, CurrKey TEQ R1, R2 ; is it current key going up ? BNE %FT50 ; not interested if not BL ScanKeys ; R0 was OldKey BPL %BT20 ; was a key so make that current STRB R2, CurrKey ; mark current key up (R2 = -1) 50 [ AssembleKEYV Pull "r0-r12,pc" | Pull pc ] ; ***************************************************************************** ; ; KeyboardEvent - Generate key up/down event ; ; in: R1 = 0 for up, 1 for down ; R2 = key index ; KeyboardEvent ROUT LDRB R3, KbId ; tell event user the keyboard id MOV R0, #Event_Keyboard B OSEVEN ; ***************************************************************************** ; ; Scan keyboard for keys down, ignoring key number R0 and shifting keys ; ; in: R0 = key number to ignore ; ; out: N=0 => R2 = key number found ; N=1 => no key found; R2 = -1 ; R0 preserved ; ScanKeys ROUT Push "R0, R14" ADR R1, KeysDown MOV R2, #4 10 LDR R3, [R1, R2, LSL #2] ; get the word TEQ R3, #0 ; if any keys in this down, skip BNE %FT20 15 SUBS R2, R2, #1 ; N=1 last time round BPL %BT10 Pull "R0, PC" 20 MOV R2, R2, LSL #5 ; multiply by 32 ADD R2, R2, #32 ; and add 32 30 TEQ R3, #0 ; no more bits ? MOVEQ R2, R2, LSR #5 ; then reset R2 to word offset BEQ %BT15 ; and continue word loop SUB R2, R2, #1 ; decrement key number MOVS R3, R3, LSR #1 ; shift out bit BCC %BT30 CMP R2, R0 ; is it old key (C=1 if it is) BLNE CheckForShiftingKeyR0R3 ; check that it's not shifting key BCS %BT30 ; C=1 => invalid, so loop TEQ R2, #0 ; N := 0 Pull "R0, PC" ; ***************************************************************************** ; ; CheckForShiftingKey - either going down or going up ; ; in: R2 = key number ; ; out: C=1 <=> is shifting key, so don't set current key etc ; R0 -> key structure ; R4 = shifting key index, or 0 if not shifting key ; R3,R5 undefined ; R1,R2,R6-R12 preserved ; CheckForShiftingKeyR0R3 ROUT ; version that saves R0, for ScanKeys Push "R0,R3,R14" BL CheckForShiftingKey Pull "R0,R3,PC" CheckForShiftingKey ROUT LDR R0, KeyVec CMP R0, #-1 MOVEQ PC, R14 LDR R3, [R0, #KVKeyTranSize] ; maximum internal key number +1 CMP R2, R3 ; is it outside table ? LDRCC R3, [R0, #KVKeyTran] ; no, R3 := offset to keytran ADDCC R3, R3, R0 ; R3 -> keytran LDRCC R3, [R3, R2, LSL #2] ; R3 = table word for this key CMNCC R3, #1 ; C=1 <=> outside table or is special MOVCC PC, R14 ; can't be shifting key LDR R3, [R0, #KVShiftingList] ; R3 = offset to shifting key list LDRB R4, [R3, R0]! ; R4 = length of shifting key list TEQ R4, #0 10 LDRNEB R5, [R3, R4] TEQNE R5, R2 SUBNES R4, R4, #1 BNE %BT10 CMP R4, #1 ; C=1 <=> shifting key MOV PC, R14 ; not one of the shifting keys ; ***************************************************************************** ; ; CallSpecialCode - Call code for a special key ; ; in: R0 -> Key structure ; R1 = 0 for up, 1 for down (shifting keys); 2 for first, 3 for repeat ; R2 = key number ; CallSpecialCode ROUT ADR R6, NullCharList LDR R3, [R0, #KVSpecialList] ; R3 = offset to special list LDRB R4, [R3, R0]! ; R4 = length of special list TEQ R4, #0 MOVEQ PC, R14 ; no special keys, so can't be one 10 LDRB R5, [R3, R4] TEQ R5, R2 BEQ %FT20 SUBS R4, R4, #1 BNE %BT10 MOV PC, R14 20 LDR R3, [R0, #KVSpecialCodeTable] ; R3 = offset to special table ADD R3, R3, R0 ; R3 -> special code table SUB R5, R3, #4 ; 0th entry is for 1st special LDR R8, [R5, R4, LSL #2] ; R8 = offset to code for this special ADD R8, R8, R3 ; R8 = address of code for this special ADR R3, ReturnVector ; and drop thru to ... CallUserKeyCode ROUT Push R14 LDROSB R5, KeyBdStatus LDRB R7, PendingAltType Push R5 BL %FT10 Pull R12 STRB R7, PendingAltType TEQ R5, R12 BLNE OfferKeyStatusUpCall STROSB R5, KeyBdStatus, R12 Pull R14 MOV R12, #IOC B UpdateLEDs 10 ADRL R12, UserKeyWorkSpace MOV PC, R8 NullCharList = 0 ALIGN ReturnVector B MouseButtonChange B DoBreakKey ; On entry: R5 = new status, R12 = old ; On exit: R5 = new new status, R12 corrupt, all other registers preserved OfferKeyStatusUpCall Entry "R0-R3,R10,R11" MRS R11, CPSR ORR R10, R11, #SVC_mode + I32_bit MSR CPSR_c, R10 Push "R14" MOV R10, #UpCallV MOV R3, R5 ; new value MOV R2, R12 ; old value MOV R1, #0 ; pre-change MOV R0, #UpCall_KeyboardStatus BL CallVector MOV R5, R3 ; R5 = value, after interference 10 TEQ R5, R2 MOVNE R10, #UpCallV MOVNE R1, #1 ; post-change MOVNE R0, #UpCall_KeyboardStatus BLNE CallVector Pull "R14" MSR CPSR_cf, R11 NOP EXIT OfferPostKeyStatusUpCall ALTENTRY MRS R11, CPSR ORR R10, R11, #SVC_mode + I32_bit MSR CPSR_c, R10 Push "R14" MOV R3, R5 MOV R2, R12 B %BT10 ; ***************************************************************************** ; ; Centisecond tick routine ; ; out: R12 corrupted CentiSecondTick ROUT Push "R11, R14" LDR R11, =ZeroPage+KeyWorkSpace MOV R12, #IOC [ PollMouse MOV R0, #K1rqmp STRB R0, RequestMouse TXon R0 ] LDR R0, InkeyCounter SUBS R0, R0, #1 ; decrement STRCS R0, InkeyCounter ; store back unless was frozen at 0 LDRB R2, CurrKey TEQ R2, #&FF Pull "R11,PC", EQ ; no current key, so no auto-repeat BL UpdateLEDs ; update LEDs from keyboard status LDRB R0, AutoRepeatCount SUBS R0, R0, #1 ; decrement count (if frozen then C:=0) STRHIB R0, AutoRepeatCount ; store back if now non-zero and not frozen Pull "R11,PC", NE ; return if non-zero or was frozen LDRB R1, Debouncing ; get debounce flag TEQ R1, #0 STRNEB R0, Debouncing ; if not zero, zero it LDROSB R0, KeyRepDelay, NE ; and load delay MOVNE R1, #2 ; indicate first time LDROSB R0, KeyRepRate, EQ ; if zero, then load repeat MOVEQ R1, #3 ; indicate subsequent time STRB R0, AutoRepeatCount ; in any case, store back Push "R4-R10" ; save registers BL GenerateChar ; R2 = key number Pull "R4-R11,PC" ; ***************************************************************************** ; ; DoBreakKey - Called by key handler when break key up or down ; ; in: R0 -> key structure ; R1 = 0 for up, 1 for down (shouldn't be 2 or 3) ; R2 = ARM internal key number ; R3 = address of ReturnVector ; R4 = special number 1..n ; R5 = keyboard status ; ; out: R6 -> list of chars to return ; DoBreakKey ROUT TST R5, #KBStat_ShiftEngaged ; shift down ? MOVEQ R3, #31 MOVNE R3, #29 TST R5, #KBStat_CtrlEngaged ; ctrl down ? BICNE R3, R3, #4 LDROSB R2, BREAKvector MOVS R2, R2, LSL R3 ; put relevant bits in C,N MOVCS PC, R14 ; 2 or 3 => ignore BPL %FT10 ; 0 => do a reset TEQ R1, #1 ; is it key down ? ADREQ R6, EscList ; yes, return ESCAPE MOV PC, R14 ; else just return 10 [ AssemblingArthur TEQ R1, #0 ; is it key up ? MOVNE PC, R14 ; no, then return ; This entry point is used by new SWI OS_Reset (TMD 06-Jan-94) ; If it's break on the keyboard R0 <> the magic 'power off' word,when '&OFF' is ; passed in R0 the power will be turned off assuming the hardware supports it PerformReset Push R0 WritePSRc F_bit+I_bit+SVC_mode, R14 BL IICAbort ; Ensure any CMOS operation aborted MOV R1, #Service_PreReset ; offer the pre-reset service IssueService WritePSRc F_bit+I_bit+SVC_mode, R14 ; just in case! Pull R0 LDR R1, PowerDownMagic TEQ R0, R1 BNE %FT15 [ :LNOT: HAL LDR r0, =&83900000 ; Address in IOMD to force power off LDR r0, [r0] | AddressHAL MOV a1, #0 CallHAL HAL_Reset ] 15 B CONT_Break ; If we can't turn the power off,we may end up back here anyway | MOV PC, R14 ; do nowt ] EscList = 1, &1B ALIGN PowerDownMagic DCD &46464F26 ; ***************************************************************************** ; ; Generate a character in keyboard buffer, if necessary ; ; in: R1 = 2 if first press; 3 if repetition ; R2 = key number ; R12 -> IOC ; GenerateChar ROUT Push R14 LDR R0, KeyVec CMP R0, #-1 Pull PC,EQ LDR R3, [R0, #KVKeyTranSize] ; get size CMP R2, R3 ; if outside table BCS %FT04 ; then assume special LDR R3, [R0, #KVKeyTran] ; R3 = offset to KeyTran ADD R3, R3, R0 ; R3 -> KeyTran ; now modify for CTRL and SHIFT LDROSB R5, KeyBdStatus TST R5, #KBStat_CtrlEngaged ADDNE R3, R3, #2 TST R5, #KBStat_ShiftEngaged ADDNE R3, R3, #1 LDRB R3, [R3, R2, LSL #2] ; get real code ; apply CAPS lock modifying BIC R6, R3, #&20 ; get upper-case code CMP R6, #"A" RSBCSS R6, R6, #"Z" ; is it alphabetic ? BCC %FT20 TST R5, #KBStat_ShiftEnable ; if SHCAPS EORNE R3, R3, #&20 ; then swap case TSTEQ R5, #KBStat_NoCapsLock ; else if CAPS BICEQ R3, R3, #&20 ; force upper case 20 TEQ R3, #&FF ; is it a special ? BEQ %FT04 ; [yes, so skip] LDROSB R6, ESCch ; if ESCAPE character TEQ R3, R6 LDROSB R6, ESCaction, EQ ; and normal ESCAPE action TEQEQ R6, #0 LDROSB R6, ESCBREAK, EQ ; and ESCAPE not disabled TSTEQ R6, #1 BNE %FT21 TST R5, #KBStat_PendingAlt BEQ %FT21 MOV R5, R12 BIC R5, R5, #KBStat_PendingAlt ; then cancel pending alt BL OfferPostKeyStatusUpCall ; don't let them interfere STROSB R5, KeyBdStatus, R6 ; and store back 21 TST R5, #KBStat_PendingAlt ; is there a pending Alt ? BNE ProcessPendingAlt TEQ R3, #0 ; is it NUL ? BNE %FT10 ; no, so skip ADR R6, NULNULList ; then insert NUL NUL B ReturnNChars CallSpecialReturnNChars Push R14 CMP R0, #-1 Pull PC,EQ 04 BL CallSpecialCode ReturnNChars LDRB R3, [R6], #1 ; R1 = count of characters ; TMD 25-Sep-89: Fix bug which resulted in Break key (acting as Escape) not ; working if buffer was full - only count spaces if more than 1 character going ; into buffer [ {TRUE} CMP R3, #1 Pull PC, CC ; no chars, so exit now BEQ %FT05 ; only 1 char, don't count | TEQ R3, #0 ; no chars? Pull PC, EQ ; then exit now ] MOV R1, #Buff_Key CMP PC, #0 ; C=1, V=0 so count spaces BL CnpEntry ORR R1, R1, R2, LSL #8 ; R1 = number of spaces CMP R3, R1 ; are there enough ? Pull PC, HI ; no, then forget them 05 LDRB R2, [R6], #1 ; send chars BL InsertKeyZCOE ; one at a time SUBS R3, R3, #1 BNE %BT05 Pull PC 10 Pull R14 ; restore stacked R14 MOV R2, R3 ; and drop thru to ... ; ***************************************************************************** ; ; InsertKeyZCOE - Insert key zeroing count on escape ; ; in: R2 = character ; InsertKeyZCOE LDROSB R0, KeyBdDisable ; disable insertion of codes ? TEQ R0, #0 MOVNE PC, R14 ; [disabled] LDROSB R0, ESCch ; escape character TEQ R0, R2 ; if is esc char LDROSB R0, ESCaction, EQ TEQEQ R0, #0 ; and FX229,0 STREQB R0, AutoRepeatCount ; then zero repeat counter ; and drop thru to ... ; ***************************************************************************** ; ; RDCHS - Insert character into keyboard buffer ; ; in: R2 = character ; RDCHS ROUT MOV R1, #Buff_Key ; keyboard buffer id ; Insert character R2 into buffer R1, checking for escape character B DoInsertESC ; ***************************************************************************** NULNULList ; list for returning NUL NUL = 2, 0, 0 ALIGN ; ***************************************************************************** ProcessPendingAlt ADR R6, NullCharList LDR R8, [R0, #KVPendingAltCode] ADD R8, R8, R0 BL CallUserKeyCode B ReturnNChars ; ***************************************************************************** ; ; Read character entry point ; ; in: - ; out: R0 = character ; C=1 => ESCAPE ; R1-R13 preserved ; NewRdch Push "R1-R4,R11" LDR R11, =ZeroPage+KeyWorkSpace MOV R4, #1 ; indicate RDCH not INKEY BL RdchInkey Pull "R1-R4,R11,PC" ; ***************************************************************************** ; ; RDCH/INKEY ; ; in: R4 = 0 => INKEY ; R4 <> 0 => RDCH ; *** TMD This changed 25-Apr-91 *** ; ; out: V=1 => error (and possibly R0 -> error block if you're lucky!) ; RdchInkey Entry ; Enable interrupts so that keyboard can work properly WritePSRc SVC_mode, r1 LDR r1, =ZeroPage LDRB r1, [r1, #RedirectInHandle] TEQ r1, #0 BEQ %FT10 ; Tutu doesn't believe that an escape condition should break redirection ; - similar to exec if you turn off escape ack side-effects SWI XOS_BGet ; get byte from redirection handle BVS RedirectBadExit BCC ReturnChar ; (C=0) ; EOF, so close redirect file and read from exec file or keyboard ; stop redirecting, BEFORE closing file, in case the CLOSE gets an error LDR r0, =ZeroPage ASSERT (ZeroPage :AND: 255) = 0 STRB r0, [r0, #RedirectInHandle] ; Convenient, huh ? SWI XOS_Find ; close file (R0=0, R1=handle) EXIT VS 10 ; First check for EXEC file LDROSB R1, ExecFileH ; read EXEC handle TEQ R1, #0 BEQ %FT20 ; no exec file SWI XOS_BGet ; get byte from exec handle BVS ExecBadExit BCC ReturnChar ; (C=0) ; EOF, so close exec file and read from keyboard ; stop EXECing, BEFORE closing file, in case the CLOSE gets an error ASSERT (ZeroPage :AND: 255) = 0 STROSB R0, ExecFileH, R0 ; (STROSB sets temp reg to 0) [ ZeroPage <> 0 MOV R0, #0 ] SWI XOS_Find ; close file (R0=0, R1=handle) EXIT VS 20 Push "R5,R6" LDR R5, =ZeroPage [ StorkPowerSave LDRB R5, [R5, #PortableFlags] ; 0 if not a portable, else Portable_Features result | LDRB R5, [R5, #PortableFlag] ; 0 if want to try issuing SWI, 1 if know it's hopeless ] RdchLoop LDR R0, =ZeroPage LDRB R0, [R0, #ESC_Status] MOVS R0, R0, LSL #(32-6) ; shift relevant bit into carry MOVCS R0, #27 ; escape detected BCS ReturnChar2 LDROSB R1, InputStream ; 0 => keyboard, 1 => RS423 BL RDCHG BCC ReturnChar2 ; Sam's hack to call the callback vector if appropriate [ AssemblingArthur LDR R0, =ZeroPage LDRB R14, [R0, #CallBack_Flag] TST R14, #CBack_VectorReq BLNE process_callback_chain ] ; here endeth the hack TEQ R4, #0 ; EQ => inkey, NE => rdch LDREQ R0, InkeyCounter ; if inkey TEQEQ R0, #0 ; and count expired BEQ InkeyTimeout [ StorkPowerSave TST R5, #PortableFeature_Idle SWINE XPortable_Idle TST R5, #PowerSave BNE RdchLoop ; if we've gone slow already, then loop TST R5, #PortableFeature_Speed BEQ RdchLoop ; if speed change doesn't work, then loop MOV R0, #1 ; go slow MOV R1, #0 SWI XPortable_Speed ; out: R0 = old speed, R1 = new speed ORRVC R5, R5, #PowerSave ; if OK, indicate power save mode MOVVC R6, R0 ; and remember old speed LDRVS R5, =ZeroPage ; if got error, then indicate we don't want to try again ASSERT (ZeroPage :AND: 255) = 0 STRVSB R5, [R5, #PortableFlags] ; and store this back for future RDCHs | CMP R5, #1 ; if we've gone slow already (2), or there's no portable module (1), then loop BCS RdchLoop MOV R0, #1 ; go slow MOV R1, #0 ; ignore old speed SWI XPortable_Speed ; out: R0 = old speed, R1 = new speed MOVVC R5, #2 ; if OK, indicate we've successfully gone slow MOVVC R6, R0 ; and remember old speed LDRVS R5, =ZeroPage+1 ; if got error, then indicate we don't want to try again ASSERT (ZeroPage :AND: 255) = 0 STRVSB R5, [R5, #PortableFlag-1] ; and store this back for future RDCHs ] B RdchLoop InkeyTimeout MOV R0, #&FF ; indicate timeout SEC ; and set carry ReturnChar2 [ StorkPowerSave TST R5, #PowerSave ; NB preserves carry BLNE RestoreSpeed | TEQ R5, #2 ; NB preserves carry BLEQ RestoreSpeed ] Pull "R5,R6" ReturnChar CLRPSR V_bit, R14 EXIT RestoreSpeed EntryS "R0" MOV R0, R6 ; restore old speed MOV R1, #0 ; AND mask of 0 SWI XPortable_Speed EXITS ; restore R0 and carry ExecBadExit ; got an error from BGET Push R0 ; save error pointer ASSERT (ZeroPage :AND: 255) = 0 STROSB R0, ExecFileH, R0 ; (STROSB sets temp reg to 0) [ ZeroPage <> 0 MOV R0, #0 ] SWI XOS_Find ; close file (R0=0, R1=handle) Pull "R1, R14" ; pull registers MOVVC R0, R1 ; if closed OK, then restore old error SETV ; still indicate error MOV PC, R14 RedirectBadExit ; got an error from BGET BL RemoveOscliCharJobs ; preserves r0 SETV ; still indicate error Pull "PC" ; pull register ; ***************************************************************************** ; ; RDCHG - Fetch character from input buffer ; Expand soft keys as necessary ; Pass cursor control keys to VDU driver ; Return carry set if character not available ; ; in: R1 = input buffer id (0 => keyboard, 1 => RS423) RDCHG ROUT [ {FALSE} STMFD sp!,{R1-R12,R14} MOV a1, #0 AddressHAL a1 SUB sp,sp,#4 MOV a2,sp CallHAL HAL_UARTReceiveByte LDR a2,[sp],#4 TST a2,#1 BNE %FT00 SEC LDMFD sp!,{R1-R12,PC} 00 CLC LDMFD sp!,{R1-R12,PC} ] Push R14 ; insert check here for ECONET interception of RDCH RDCHNM LDROSB R0, SoftKeyLen ; are we expanding a soft key TEQ R0, #0 BEQ RDCHG1 ; not expanding LDROSB R2, RS423mode TST R1, R2 ; if RS423 and 8 bit data BNE RDCHG1 ; ignore soft keys LDR R2, SoftKeyPtr LDRB R2, [R2, -R0] ; get character out of buffer SUB R0, R0, #1 ; decrement character count STROSB R0, SoftKeyLen, R3 ; store back MOV R0, R2 ; put character in R0 CLC ; and exit with carry clear Pull PC RDCHG1 BL KeyREMOVECheckRS423 ; remove character, if none, exit CS LDROSB R2, RS423mode ; 0 => treat RS423 as keyboard TST R1, R2 ; NZ => let RS423 deliver 8-bit codes BNE RDCHGCLC TEQ R0, #0 ; is it NUL ? BNE %FT10 BL KeyREMOVECheckRS423 ; get another char, if none then ; spurious, so ignore TEQ R0, #0 ; is it NUL NUL ? BNE RDCHGCLC ; no, then return this character [ ZeroPage <> 0 LDR R0, =ZeroPage ] LDRB R2, [R0, #OsbyteVars + :INDEX: IPbufferCh]! ; R0 was 0, so now -> 1st of 8 keybases ADD R3, R0, #8 05 TEQ R2, #2 ; is this key base = 2 ? MOVEQ R0, #0 ; if so then return NUL NUL BEQ ReturnNULR0 LDRB R2, [R0, #1]! ; load next key base TEQ R0, R3 ; if not tried all of them BNE %BT05 ; then loop MOV R0, #0 ; no special key bases, ; so just return NUL 10 TST R0, #&80 BEQ RDCHGCLC ; now check for cursor key movement AND R3, R0, #&0F ; save bottom nybble CMP R3, #&0B ; is it a cursor key ? BCC NotCursorKey TST R0, #&40 ; don't let Cx-Fx be cursor keys BNE NotCursorKey LDROSB R2, CurEdit ; FX 4 state CMP R2, #1 ADDLS R0, R3, #&87-&0B ; 0 or 1 => force in range &87-&8B BCC ItsCursorEdit ; 0 => cursor edit BEQ RDCHGCLC ; 1 => return these codes NotCursorKey MOV R0, R0, LSR #4 EOR R0, R0, #&0C ; 4..7, 0..3 [ ZeroPage = 0 LDRB R2, [R0, #OsbyteVars+IPbufferCh-OSBYTEFirstVar] | LDR R2, =ZeroPage+OsbyteVars+IPbufferCh-OSBYTEFirstVar LDRB R2, [R0, R2] ] ; get key variable CMP R2, #1 ; is it 0 (ignore) or 1 (softkey) BCC RDCHG1 ; get another char if 0 BEQ ExpandSoftKey ; expand soft key if 1 TEQ R2, #2 ; is it special Compact option ? EOREQ R0, R0, #&0C ; undo that mangling ! ORREQ R0, R3, R0, LSL #4 ; if so, then return NUL <code> BEQ ReturnNULR0 ADD R0, R2, R3 ; add offset to base AND R0, R0, #&FF ; make it wrap RDCHGCLC CLC Pull PC ItsCursorEdit LDROSB R2, WrchDest TST R2, #2 ; if wrch not to VDU BNE RDCHG1 ; then ignore character Push "R1,R4-R12" [ AssemblingArthur VDWS WsPtr BL DoCursorEdit | BL DCE10 ] Pull "R1,R4-R12" BCS RDCHG1 ; no character yet, so loop Pull PC ; NB carry clear - no ESCAPE ! [ :LNOT: AssemblingArthur DCE10 MOV R1, #VduDriver ADD PC, R1, #CursorEdit ] ; ***************************************************************************** ; ; ReturnNULR0 - Return NUL followed by R0 from RDCH ; ReturnNULR0 ROUT ADR R2, SoftKeyExpand ; store code in SoftKeyExpand +0 STRB R0, [R2], #1 ; and set ptr to SoftKeyExpand +1 STR R2, SoftKeyPtr MOV R2, #1 ; set key length to 1 STROSB R2, SoftKeyLen, R0 ; (sets R0 to 0!) [ ZeroPage <> 0 MOV R0, #0 ] B RDCHGCLC ; return NUL as first character ; ***************************************************************************** KeyREMOVECheckRS423 ROUT Push R14 BL KeyREMOVE Pull "R14, PC", CS ; pull stacked R14 if CS Pull PC ; ***************************************************************************** KeyREMOVE Push "R10,R12,R14" CLRV ; do remove not examine MOV R10, #REMV B GoVec ; expand a soft key as a variable (R3 = key number) ExpandSoftKey ROUT Push "R1,R4" BL SetupKeyName ADR R1, SoftKeyExpand MOV R2, #255 ; max length of string MOV R3, #0 ; no name pointer MOV R4, #VarType_Expanded SWI XOS_ReadVarVal Pull "R1,R4", VS BVS RDCHG1 ; no string or bad STROSB R2, SoftKeyLen, R0 ; store length (may be zero) ADD R1, R1, R2 ; R1 -> last char+1 STR R1, SoftKeyPtr Pull "R1,R4" B RDCHNM ; try to expand it KeyName = keyprefix,0 ALIGN ; ***************************************************************************** ; ; SetupKeyName - Set up the name <keyprefix><n><0> in SoftKeyName ; ; in: R11 -> KeyWS ; R3 = key number ; ; out: R0 -> SoftKeyName, which contains <keyprefix><n><0> ; R2-R4 corrupted ; SetupKeyName ROUT ADR R2, KeyName ADR R0, SoftKeyName 10 LDRB R4, [R2], #1 ; copy keyprefix in TEQ R4, #0 STRNEB R4, [R0], #1 BNE %BT10 ; now put digits at R0 ORR R3, R3, #"0" CMP R3, #"9"+1 MOVCS R2, #"1" ; if >=10 then put in "1" STRCSB R2, [R0], #1 SUBCS R3, R3, #10 ; and subtract 10 STRB R3, [R0], #1 STRB R4, [R0] ; (R4=0) terminate ADR R0, SoftKeyName MOV PC, R14 ; ***************************************************************************** ; ; DoInkeyOp - Perform INKEY DoInkeyOp TST R2, #&80 ; INKEY(+ve) ? BNE NewInkeyNeg NewInkeyPos Push R4 LDR R11, =ZeroPage+KeyWorkSpace AND R1, R1, #&FF ; no funny business AND R2, R2, #&FF ; ditto ORR R1, R1, R2, LSL #8 ; get combined count STR R1, InkeyCounter MOV R4, #0 ; indicate inkey not rdch BL RdchInkey MOV R1, R0 ; make X the character MOVCC R2, #0 ; Y := 0 if normal exit MOVCS R2, R0 ; Y := &1B or &FF for ESC or timeout Pull "R4,PC" ; return preserving V and R0 NewInkeyNeg EOR R1, R1, #&7F ; invert bits for scan call BL BBCScanKeys Pull PC ; ***************************************************************************** ; ; BBCScanKeys - Test individual key or scan for key depression ; ; in: R1 = 0..&7F => scan keyboard from BBC internal key R1 ; out: C=0 => R1 = BBC internal key found ; C=1 => R1 = &FF (no key found) ; ; in: R1 = &80..&FF => test if BBC internal key (R1 EOR &80) is down ; out: C=0, R1=R2=&00 => key is up ; C=1, R1=R2=&FF => key is down ; BBCScanKeys ROUT Push R11 LDR R11, =ZeroPage+KeyWorkSpace AND R1, R1, #&FF ; trap wallies LDR R2, KeyVec TST R1, #&80 ; >=&80 => test single key ; < &80 => scan for key BEQ DoBBCScan ; [is scanning not testing] ADD R0, R2, #1 CMP R0, #1 ; if no key handler then MOVCC R1, #0 ; return key up (C=0) BCC ExitBBCScan LDR R0, [R2, #KVInkeyTran] ADD R0, R2, R0 ; R0 -> InkeyTran or InkeyTran2 ADD R0, R0, #4 * &FF ; R0 -> InkeyTran+4*&FF LDR R0, [R0, -R1, LSL #2] ; get word of indexes into KeysDown MOV R2, #&FF000000 02 CMP R0, #-1 ; is it all FF's MOVEQ R1, #0 ; if so then none of keys down BEQ %FT04 AND R1, R0, #&FF ; just get bottom byte ADR R3, KeysDown ; look up in KeysDown MOV R1, R1, LSR #5 LDR R3, [R3, R1, LSL #2] ; get word of 32 bits AND R1, R0, #31 MOV R3, R3, LSL R1 ; put relevant bit into top bit MOVS R1, R3, LSR #31 ; R1 = 0 if up, 1 if down ORREQ R0, R2, R0, LSR #8 ; shift down, putting FF in top byte BEQ %BT02 04 CMP R1, #1 ; C=1 <=> at least one of keys down MOVCC R1, #0 MOVCS R1, #&FF MOV R2, R1 ExitBBCScan Pull R11 MOV PC, R14 DoBBCScan CMP R2, #-1 ; if no key handler then MOVEQ r1, #&FF ; return all keys up (C=1) BEQ ExitBBCScan LDR R0, [R2, #KVInkeyTran] ADD R0, R2, R0 ; R0 -> InkeyTran or InkeyTran2 Push "R4, R5" ADD R0, R0, #4 * &7F ; R0 -> InkeyTran+4*&7F MOV R4, #&FF000000 10 LDR R3, [R0, -R1, LSL #2] ; get word of indexes into KeysDown 15 CMP R3, #-1 ; all FFs ? BEQ %FT18 ; then not one of these keys AND R5, R3, #&FF ADR R2, KeysDown MOV R5, R5, LSR #5 LDR R2, [R2, R5, LSL #2] ; get word of bits AND R5, R3, #31 MOV R2, R2, LSL R5 ; put relevant bit into top bit MOVS R5, R2, LSR #31 ; R5 = 0 for up, 1 for down BNE %FT20 ; [down, so stop] ORR R3, R4, R3, LSR #8 ; up -> shift down putting FF in top B %BT15 18 ADD R1, R1, #1 ; go to next key TEQ R1, #&80 ; if not run out of keys BNE %BT10 ; then loop MOV R1, #&FF ; indicate no key 20 CMP R1, #&FF ; C=0 <=> found key Pull "R4,R5,R11" MOV PC, R14 ; ***************************************************************************** ; ; Write keys down information ; ; in: R1 = Current key (in BBC internal key format) ; R2 = Old key (------------""------------) ; ; out: R1, R2 preserved ; WriteKeysDown ROUT Push R14 LDR R11, =ZeroPage+KeyWorkSpace MOV R0, R1 BL ConvertInternalKey STRB R0, CurrKey MOV R0, R2 BL ConvertInternalKey STRB R0, OldKey Pull PC ConvertInternalKey TST R0, #&80 ; if not in range &80..&FF MOVEQ R0, #&FF ; return value &FF (key not valid) MOVEQ PC, R14 EOR R0, R0, #&7F ; else convert to inkey value Push R4 LDR R3, KeyVec CMP R3, #-1 ; if no key handler then MOVEQ R0, #&FF ; return no key MOVEQ PC, R14 LDR R4, [R3, #KVInkeyTran] ADD R3, R3, R4 ; R3 -> InkeyTran or InkeyTran2 Pull R4 SUB R3, R3, #&80*4 ; R3 -> InkeyTran-4*&80 LDRB R0, [R3, R0, LSL #2] ; convert to ARM internal key ; (just get 1st key for this key) MOV PC, R14 ; ***************************************************************************** ; ; InstallKeyHandler - Install user key handler ; ; in: R0 = new key handler ; 0 => just read old key handler ; 1 => just read keyboard id ; ; out: R0 = old key handler, or ; R0 = keyboard id if R0 was 1 on entry (&FF => no keyboard id yet) ; InstallKeyHandler ROUT MRS R11, CPSR ORR R11, R11, #I32_bit MSR CPSR_c, R11 ; disable IRQs LDR R11, =ZeroPage+KeyWorkSpace TEQ R0, #1 ; asking for keyboard id ? LDREQB R0, KbId ; then load it ExitSWIHandler EQ ; and exit LDR R10, KeyVec ; R10 -> old key handler TEQ R0, #0 ; if not just reading it STRNE R0, KeyVec ; then store new one MOV R0, R10 ; R0 -> old key handler ExitSWIHandler EQ ; exit if just reading Push "R0-R12,LR" BL KeyboardEnable Pull "R0-R12,LR" ExitSWIHandler ; ***************************************************************************** ; ; IssueKeyboardService - Issue keyboard handler service ; ; in: R11 -> KeyWorkSpace ; ; out: R0 preserved ; IssueKeyboardService Push "R0,R14" MOV R1, #Service_KeyHandler LDRB R2, KbId IssueService Pull "R0,PC" LTORG END