From bcb499b5e697b09cdfcb435ed2edaa47199a293b Mon Sep 17 00:00:00 2001
From: Jeffrey Lee <jlee@gitlab.riscosopen.org>
Date: Wed, 5 Dec 2012 01:16:22 +0000
Subject: [PATCH] Add support for new extended internal key codes, low level
 key codes, and key handler format

Detail:
  s/Middle - Added OS_ReadSysInfo 13 to allow the kernel to validate a key handler before the owner attempts to install it
  Resources/UK/Messages - Text for new "Bad key handler" error
  s/GetAll, s/PMF/Def - Get rid of now obsolete s/PMF/Def file. It only contained definitions for pre-HAL hardware, and for the key handler layout (now in Hdr:Keyboard)
  hdr/KeyWS - Increased size of KeysDown array so it can hold 768 keys instead of 160. Trim a couple of obsolete variables, and increase CurrKey/OldKey from 1 byte to 4 bytes.
  s/PMF/key, s/PMF/osbyte - Main bulk of the changes for the new key handling. All the important interfaces are now able to deal with extended (i.e. > 8 bit) internal key numbers, and the kernel is able to cope with key handlers which use 16 bit internal/low level key numbers instead of 8 bit.
Admin:
  Tested on Pandora & BB-xM
  Requires HdrSrc-2_20


Version 5.35, 4.79.2.178. Tagged as 'Kernel-5_35-4_79_2_178'
---
 Resources/UK/Messages |   1 +
 VersionASM            |  10 +-
 VersionNum            |  14 +-
 hdr/KeyWS             |  10 +-
 s/GetAll              |   3 +-
 s/Middle              |  36 +++-
 s/PMF/Def             | 148 -----------------
 s/PMF/key             | 376 +++++++++++++++++++++++++++++++++++-------
 s/PMF/osbyte          |   4 +-
 9 files changed, 368 insertions(+), 234 deletions(-)
 delete mode 100644 s/PMF/Def

diff --git a/Resources/UK/Messages b/Resources/UK/Messages
index 2d204f6..d1f84d0 100644
--- a/Resources/UK/Messages
+++ b/Resources/UK/Messages
@@ -163,6 +163,7 @@ SNoMask:Mask or Palette operations not supported in this display depth
 BadVIDCDiv:Bad VIDC divider value
 BadPlatReas:Unknown OS_PlatformFeatures reason code
 UnConv:Unsupported conversion
+BadKeyHandler:Bad key handler
 
 600:ARM 600 Processor
 610:ARM 610 Processor
diff --git a/VersionASM b/VersionASM
index 78c9911..e196b8f 100644
--- a/VersionASM
+++ b/VersionASM
@@ -13,11 +13,11 @@
                         GBLS    Module_ComponentPath
 Module_MajorVersion     SETS    "5.35"
 Module_Version          SETA    535
-Module_MinorVersion     SETS    "4.79.2.177"
-Module_Date             SETS    "21 Nov 2012"
-Module_ApplicationDate  SETS    "21-Nov-12"
+Module_MinorVersion     SETS    "4.79.2.178"
+Module_Date             SETS    "05 Dec 2012"
+Module_ApplicationDate  SETS    "05-Dec-12"
 Module_ComponentName    SETS    "Kernel"
 Module_ComponentPath    SETS    "castle/RiscOS/Sources/Kernel"
-Module_FullVersion      SETS    "5.35 (4.79.2.177)"
-Module_HelpVersion      SETS    "5.35 (21 Nov 2012) 4.79.2.177"
+Module_FullVersion      SETS    "5.35 (4.79.2.178)"
+Module_HelpVersion      SETS    "5.35 (05 Dec 2012) 4.79.2.178"
                         END
diff --git a/VersionNum b/VersionNum
index 2a0428c..15cc993 100644
--- a/VersionNum
+++ b/VersionNum
@@ -5,19 +5,19 @@
  *
  */
 #define Module_MajorVersion_CMHG        5.35
-#define Module_MinorVersion_CMHG        4.79.2.177
-#define Module_Date_CMHG                21 Nov 2012
+#define Module_MinorVersion_CMHG        4.79.2.178
+#define Module_Date_CMHG                05 Dec 2012
 
 #define Module_MajorVersion             "5.35"
 #define Module_Version                  535
-#define Module_MinorVersion             "4.79.2.177"
-#define Module_Date                     "21 Nov 2012"
+#define Module_MinorVersion             "4.79.2.178"
+#define Module_Date                     "05 Dec 2012"
 
-#define Module_ApplicationDate          "21-Nov-12"
+#define Module_ApplicationDate          "05-Dec-12"
 
 #define Module_ComponentName            "Kernel"
 #define Module_ComponentPath            "castle/RiscOS/Sources/Kernel"
 
-#define Module_FullVersion              "5.35 (4.79.2.177)"
-#define Module_HelpVersion              "5.35 (21 Nov 2012) 4.79.2.177"
+#define Module_FullVersion              "5.35 (4.79.2.178)"
+#define Module_HelpVersion              "5.35 (05 Dec 2012) 4.79.2.178"
 #define Module_LibraryVersionInfo       "5:35"
diff --git a/hdr/KeyWS b/hdr/KeyWS
index ab62d64..5b42ab4 100644
--- a/hdr/KeyWS
+++ b/hdr/KeyWS
@@ -32,19 +32,19 @@ keyprefix SETS  "Key$"
 
         ^       0, R11
 
-CurrKey         #       1               ; current key in two key rollover
-OldKey          #       1               ; old key in two key rollover
+CurrKey         #       4               ; current key in two key rollover
+OldKey          #       4               ; old key in two key rollover
 KbId            #       1
 LastKbId        #       1
 AutoRepeatCount #       1
 Debouncing      #       1               ; NZ => do delay next, Z => do repeat
 MouseButtons    #       1               ; bit0=R, bit1=C, bit2=L
 PendingAltType  #       1               ; 1 => A, 2 => SA, 3 => CA, 4 => CSA
-ModulesOK       #       1               ; bit0=1 => modules are initialised
-                                        ; bit1=1 => we have offered service
 LastLED         #       1               ; last request for LED change, so we don't send repeated ones
 MouseType       #       1               ; current pointer device type
+ [ STB
 MousePresent    #       1               ; mouse detected
+ ]
 MouseReporting  #       1               ; mouse is sending reports itself
 NoDebounce      #       1               ; NZ => no kernel key debounce
                                         ; set if R2="NoKd" on KeyV 0 entry
@@ -66,7 +66,7 @@ MouseBoundBRow  *       MouseBounds+4
 MouseBoundRCol  *       MouseBounds+8
 MouseBoundTRow  *       MouseBounds+12
 
-KeysDown        #       20              ; bitmap of all down keys
+KeysDown        #       &300/8          ; bitmap of all down keys
 
 SoftKeyName     #       3 + :LEN:(keyprefix)    ; up to 2 digits + terminator
 
diff --git a/s/GetAll b/s/GetAll
index beb86e2..ece8e17 100644
--- a/s/GetAll
+++ b/s/GetAll
@@ -75,8 +75,7 @@
         GET     Hdr:nvram
         GET     Hdr:PortMan
         GET     Hdr:SerialOp
-        GET     s.PMF.DEF          ; Common with 6502 code in the keyboard
-        Protocol
+        GET     Hdr:Keyboard
 
 ; now the main parts of the MOS
 
diff --git a/s/Middle b/s/Middle
index 9996afc..35f630d 100644
--- a/s/Middle
+++ b/s/Middle
@@ -1291,7 +1291,7 @@ dhte
 ; Out   r0 = sysinfo for r0in
 
 ReadSysInfo_Code ROUT
-        CMP     r0,#13 ;R0 > 12, so illegal value
+        CMP     r0,#14 ;R0 > 13, so illegal value
         ADDLO   PC, PC, R0,LSL #2
         B       ReadSysInfo_InvalidReason
 
@@ -1308,6 +1308,7 @@ ReadSysInfo_Code ROUT
         B       ReadSysInfo_InvalidReason ; ROL's "read OS version" call
         B       %FT110
         B       %FT120
+        B       %FT130
 
 ReadSysInfo_InvalidReason
         ADR     r0, ErrorBlock_BadReadSysInfo
@@ -2111,6 +2112,39 @@ RSI_DebugRX
         Pull    "r1-r3,r9,r14"
         ExitSWIHandler
 
+; OS_ReadSysInfo 13 - Validate key handler
+;
+; On entry:
+;    r0 = 13 (reason code 13)
+;    r1 = key handler address (0 to just return valid flags)
+;
+; On exit:
+;    r0 = Mask of supported key handler flags
+;         Or pointer to error block
+;
+
+130
+        LDR     R0, =KeyHandler_Flag_Wide
+        CMP     R1, #0
+        ExitSWIHandler EQ
+        Push    "R2"
+        LDR     R2, [R1, #KeyHandler_KeyTranSize]
+        TST     R2, #KeyHandler_HasFlags
+        LDRNE   R2, [R1, #KeyHandler_Flags]
+        BICNES  R2, R2, R0
+        Pull    "R2"
+        ExitSWIHandler EQ
+        ADR     R0, ErrorBlock_BadKeyHandler
+      [ International
+        Push    "lr"
+        BL      TranslateError
+        Pull    "lr"
+      ]
+        ORR     lr, lr, #V_bit
+        ExitSWIHandler
+
+        MakeErrorBlock BadKeyHandler
+
 ;
 ; Extended ROM footer functions
 ;
diff --git a/s/PMF/Def b/s/PMF/Def
deleted file mode 100644
index 049ca85..0000000
--- a/s/PMF/Def
+++ /dev/null
@@ -1,148 +0,0 @@
-; 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.Def
- MACRO
- Protocol
-;
-; Protocol constants
-;
-; 4 bit codes for input commands
-;
-LEDON * &00
-LEDOFF * &10
-REQUEST * &20
-ACK * &30
-SPDDATA * &40 ;Bottom four bits are data to convert
-RSTREQ * &80 ;Request for reset
-;
-; 4 bit input data types
-;
-; Requests
-;
-KBID * 0
-SPDRESET * 1
-MDATA * 2 ;New mouse position, even if it hasn't moved
-;
-; Acks
-;
-BYTE * 0
-SCAN * 1
-MOUSE * 2
-ALL * 3
-;
-; data output
-;
-; Type         d7 d6 d5 d4 d3 d2 d1 d0 number of bytes
-; Reset        1  x  x  x  x  x  x  x  1
-; Key up       0  0  1  1  x  x  x  x  2 (row then column)
-; Key down     0  0  1  0  x  x  x  x  2 (row then column)
-; Mouse change 0  1  x  x  x  x  x  x  2 (X, Y)
-; SPD data     0  0  0  0  x  x  x  x  8
-; KB Ids       0  0  0  1  x  x  x  x  2 (low nibble then high nibble)
-;
-; The keyboard type
-;
-IDTYPE * &10
-KBTYPE * 0 ;This is keyboard 0
-;
-; Key transitions
-;
-KEYDOWN * &20
-KEYUP * &30
-;
-; Mouse transitions
-;
-MMOVED * &40
-;
-; SPD converted data
-;
-SPDDONE * 0
-;
-; Reset types
-;
-HRDRESET * &FF
-RST1ACK * &FE
-RST2ACK * &FD
-
-; New keyboard protocols
-
-; Keyboard -> ARM
-
-K1mdat  * &00           ; 0xxx xxxx     Mouse data from keyboard
-K1kbid  * &80           ; 10xx xxxx     Keyboard ID from keyboard
-K1kdda  * &C0           ; 1100 xxxx     Key down data
-K1kuda  * &D0           ; 1101 xxxx     Key up data
-K1pdat  * &E0           ; 1110 xxxx     SPD data from keyboard (won't happen)
-K1rak2  * &FD           ; 1111 1101     Reset acknowledge 2
-K1rak1  * &FE           ; 1111 1110     Reset acknowledge 1
-K1hrst  * &FF           ; 1111 1111     Hard reset
-
-K1kbidmask * &3F        ; 0011 1111     Valid bits in keyboard id
-
-K1notmousedata * &80
-
-; ARM -> Keyboard
-
-;
-; The IOC registers
-;
-        ^       &04, R12
-KARTTx  #       0
-KARTRx  #       0
-
-        ^       &20, R12
-IRQStatusB #    4
-IRQReqB    #    4
-IRQMaskB   #    4
-
-        ^       &70, R12
-Timer3Low   #   4
-Timer3High  #   4
-Timer3Go    #   4
-Timer3Latch #   4
-
-; Register bits
-
-KARTRxBit   *   &80
-KARTTxBit   *   &40
-KARTIRQBits *   KARTTxBit :OR: KARTRxBit
-
-K1leds  * &00           ; 0000 0xxx     Set LED states
-K1rqid  * &20           ; 0010 0000     Request keyboard id
-K1prst  * &21           ; 0010 0001     SPD reset
-K1rqmp  * &22           ; 0010 0010     Request mouse position
-K1nack  * &30           ; 0011 0000     Acknowledge (keys- mouse-)
-K1sack  * &31           ; 0011 0001     Acknowledge (keys+ mouse-)
-K1mack  * &32           ; 0011 0010     Acknowledge (keys- mouse+)
-K1smak  * &33           ; 0011 0011     Acknowledge (keys+ mouse+)
-K1back  * &3F           ; 0011 1111     Byte acknowledge (between 2 data bytes)
-K1rqpd  * &40           ; 0100 xxxx     Request SPD data conversion
-
-; Keyboard vector offsets
-
-        ^ 0
-
-KVKeyTran          # 4
-KVKeyTranSize      # 4
-KVInkeyTran        # 4
-KVShiftingList     # 4
-KVSpecialList      # 4
-KVSpecialCodeTable # 4
-KVInit             # 4
-KVPendingAltCode   # 4
-KVPendingAltSpecial # 4             ; Used only internally
-
- MEND
- END
diff --git a/s/PMF/key b/s/PMF/key
index 5dd27f2..1bdc965 100644
--- a/s/PMF/key
+++ b/s/PMF/key
@@ -114,30 +114,30 @@ KeyPostInit ROUT
 ; *****************************************************************************
 
 ClearKbd ROUT
-        Push    R14
+        Entry   "R1-R2"
 
         MOV     R0, #&FF
-        STRB    R0, CurrKey             ; no current key
-        STRB    R0, OldKey
+        STR     R0, CurrKey             ; no current key
+        STR     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
+        MOV     R1, #?KeysDown
+        ADR     R2, KeysDown
+10
+        SUBS    R1, R1, #4
+        STR     R0, [R2], #4
+        BNE     %BT10
 
-        Pull    PC
+        EXIT
 
 ; *****************************************************************************
 ;
 ;       UpdateLEDs - Update the LED(s) from the keyboard status byte
 ;
 ; in:   R11 -> keyboard workspace
-;       R12 -> IOC
 ;
 ; out:  R0, R1 corrupted
 ;
@@ -165,7 +165,6 @@ UpdateLEDs ROUT
 ;
 ; in:   R1 = desired LED state (bit 0 = caps lock, 1 = num lock, 2 = scroll lock)
 ;       R11 -> keyboard workspace
-;       R12 -> IOC
 ;
 ; out:  R0, R1 corrupted
 ;
@@ -279,7 +278,7 @@ KeyboardEnable
 
         STRB    r1, LastKbId            ; Remember last keyboard id that initialised.
 
-        LDR     r8, [r0, #KVInit]       ; Initialise key handler.
+        LDR     r8, [r0, #KeyHandler_Init] ; Initialise key handler.
         ADD     r8, r8, r0
         BL      CallUserKeyCode
 
@@ -325,7 +324,7 @@ GotKey ROUT
         MOV     r2, r1
         SUB     r1, r0, #1
 
-        CMP     R2, #&A0
+        CMP     R2, #?KeysDown*8
         BCS     %FT05
         ADR     R0, KeysDown
         MOV     lr, R2, LSR #5
@@ -356,17 +355,17 @@ GotKey ROUT
         TEQ     r1, #0                  ; if key up then
         BEQ     %FT30                   ;   go and deal with it
 
-        LDRB    R0, CurrKey
+        LDR     R0, CurrKey
         TEQ     R0, #&FF                ; have we got a current key ?
         BEQ     %FT20
 
-        LDRB    R1, OldKey
+        LDR     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
+        STR     R0, OldKey              ; make current key old
 20
-        STRB    R2, CurrKey             ; update current
+        STR     R2, CurrKey             ; update current
         LDRB    r1, NoDebounce          ; check debouncing?
         TEQ     r1, #0
         MOVEQ   R0, #2                  ; Eq.. normal kernel debounce
@@ -381,31 +380,32 @@ GotKey ROUT
         B       %FT50
 
 30
-        LDRB    R0, OldKey
+        LDR     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
+        LDR     R0, CurrKey             ; current key is one to ignore in scan
         BL      ScanKeys
 
-        STRPLB  R0, OldKey              ; found key, so current -> old
+        STRPL   R0, OldKey              ; found key, so current -> old
         BPL     %BT20                   ; and R2 -> current
 
         MOV     R0, #&FF                ; else mark old key invalid
-        STRB    R0, OldKey
+        STR     R0, OldKey
         B       %FT50                   ; and return
 
 40
-        LDRB    R1, CurrKey
+        LDR     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)
+        MOV     R2, #&FF
+        STR     R2, CurrKey             ; mark current key up
 
 50
  [ AssembleKEYV
@@ -441,8 +441,10 @@ KeyboardEvent ROUT
 ScanKeys ROUT
         Push    "R0, R14"
         ADR     R1, KeysDown
-        MOV     R2, #4
+        MOV     R2, #(?KeysDown/4)-1
 10
+        TEQ     R2, #&200/32
+        MOVEQ   R2, #4                  ; skip extension key range &100
         LDR     R3, [R1, R2, LSL #2]    ; get the word
         TEQ     R3, #0                  ; if any keys in this down, skip
         BNE     %FT20
@@ -491,15 +493,21 @@ CheckForShiftingKey ROUT
         LDR     R0, KeyVec
         CMP     R0, #-1
         MOVEQ   PC, R14
-        LDR     R3, [R0, #KVKeyTranSize] ; maximum internal key number +1
+        LDR     R3, [R0, #KeyHandler_KeyTranSize] ; maximum internal key number +1
+        TST     R3, #KeyHandler_HasFlags
+        BIC     R3, R3, #KeyHandler_HasFlags
+        LDRNE   R5, [R0, #KeyHandler_Flags]
+        TSTNE   R5, #KeyHandler_Flag_Wide
+        BNE     %FT20                   ; Use wide version of routine
+
         CMP     R2, R3                  ; is it outside table ?
-        LDRCC   R3, [R0, #KVKeyTran]    ; no, R3 := offset to keytran
+        LDRCC   R3, [R0, #KeyHandler_KeyTran] ; 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
+        LDR     R3, [R0, #KeyHandler_ShiftingList] ; R3 = offset to shifting key list
         LDRB    R4, [R3, R0]!           ; R4 = length of shifting key list
         TEQ     R4, #0
 10
@@ -511,6 +519,45 @@ CheckForShiftingKey ROUT
         CMP     R4, #1                  ; C=1 <=> shifting key
         MOV     PC, R14                 ; not one of the shifting keys
 
+20
+        CMP     R2, R3                  ; is it outside table ?
+        LDRCC   R3, [R0, #KeyHandler_KeyTran] ; no, R3 := offset to keytran
+        ADDCC   R3, R3, R0              ; R3 -> keytran
+        LDRCC   R4, =&FF00FF00
+        ADDCC   R3, R3, R2, LSL #3      ; R3 = table address for this key
+        LDMCCIA R3, {R3, R5}            ; R3, R5 = table entry for this key
+        EORCC   R3, R3, R4
+        EORCC   R5, R5, R4
+        ANDCC   R3, R3, R5
+        CMNCC   R3, #1                  ; C=1 <=> outside table or is special
+        MOVCC   PC, R14                 ; can't be shifting key
+
+        LDR     R3, [R0, #KeyHandler_ShiftingList] ; R3 = offset to shifting key list
+      [ NoARMv4
+        LDRB    R4, [R3, R0]!
+        LDRB    R5, [R3, #1]
+        ORRS    R4, R4, R5, LSL #8      ; R4 = length of shifting key list
+      |
+        LDRH    R4, [R3, R0]!
+        TEQ     R4, #0                  ; R4 = length of shifting key list
+      ]
+30
+      [ NoARMv4
+        ASSERT  :LNOT: NoUnaligned
+        LDRNE   R5, [R3, R4, LSL #1]
+        MOVNE   R5, R5, LSL #16
+        TEQNE   R5, R2, LSL #16
+      |
+        ADDNE   R5, R3, R4, LSL #1
+        LDRNEH  R5, [R5]
+        TEQNE   R5, R2
+      ]
+        SUBNES  R4, R4, #1
+        BNE     %BT30
+
+        CMP     R4, #1                  ; C=1 <=> shifting key
+        MOV     PC, R14                 ; not one of the shifting keys
+
 ; *****************************************************************************
 ;
 ;       CallSpecialCode - Call code for a special key
@@ -521,8 +568,13 @@ CheckForShiftingKey ROUT
 ;
 
 CallSpecialCode ROUT
+        LDR     R3, [R0, #KeyHandler_KeyTranSize]
         ADR     R6, NullCharList
-        LDR     R3, [R0, #KVSpecialList] ; R3 = offset to special list
+        TST     R3, #KeyHandler_HasFlags
+        LDRNE   R3, [R0, #KeyHandler_Flags]
+        TSTNE   R3, #KeyHandler_Flag_Wide
+        LDR     R3, [R0, #KeyHandler_SpecialList] ; R3 = offset to special list
+        BNE     CallSpecialCodeWide
         LDRB    R4, [R3, R0]!           ; R4 = length of special list
         TEQ     R4, #0
         MOVEQ   PC, R14                 ; no special keys, so can't be one
@@ -534,8 +586,26 @@ CallSpecialCode ROUT
         BNE     %BT10
         MOV     PC, R14
 
+CallSpecialCodeWide
+      [ NoARMv4
+        LDRB    R4, [R3, R0]!
+        LDRB    R5, [R3], #1
+        ORR     R4, R4, R5, LSL #8      ; R4 = length of special list
+      |
+        LDRH    R4, [R3, R0]!           ; R4 = length of special list
+      ]
+        TEQ     R4, #0
+        MOVEQ   PC, R14                 ; no special keys, so can't be one
+15
+        LDHA    R5, R3, R4, R8
+        TEQ     R5, R2
+        BEQ     %FT20
+        SUBS    R4, R4, #1
+        BNE     %BT15
+        MOV     PC, R14
+
 20
-        LDR     R3, [R0, #KVSpecialCodeTable] ; R3 = offset to special table
+        LDR     R3, [R0, #KeyHandler_SpecialCodeTable] ; R3 = offset to special table
         ADD     R3, R3, R0              ; R3 -> special code table
         SUB     R5, R3, #4              ; 0th entry is for 1st special
 
@@ -557,7 +627,6 @@ CallUserKeyCode ROUT
         BLNE    OfferKeyStatusUpCall
         STROSB  R5, KeyBdStatus, R12
         Pull    R14
-        MOV     R12, #IOC
         B       UpdateLEDs
 
 10
@@ -629,7 +698,7 @@ CentiSecondTick ROUT
         SUBS    R0, R0, #1              ; decrement
         STRCS   R0, InkeyCounter        ; store back unless was frozen at 0
 
-        LDRB    R2, CurrKey
+        LDR     R2, CurrKey
         TEQ     R2, #&FF
         Pull    "R11,PC", EQ            ; no current key, so no auto-repeat
 
@@ -747,24 +816,43 @@ GenerateChar ROUT
         LDR     R0, KeyVec
         CMP     R0, #-1
         Pull    PC,EQ
-        LDR     R3, [R0, #KVKeyTranSize]        ; get size
+        LDR     R5, [R0, #KeyHandler_KeyTranSize] ; get size
+        BIC     R3, R5, #KeyHandler_HasFlags
         CMP     R2, R3                          ; if outside table
         BCS     %FT04                           ; then assume special
 
-        LDR     R3, [R0, #KVKeyTran]            ; R3 = offset to KeyTran
+        LDR     R3, [R0, #KeyHandler_KeyTran]   ; R3 = offset to KeyTran
         ADD     R3, R3, R0                      ; R3 -> KeyTran
 
+        TST     R5, #KeyHandler_HasFlags
+        LDRNE   R6, [R0, #KeyHandler_Flags]
+        TSTNE   R6, #KeyHandler_Flag_Wide
+        MOVEQ   R6, #1
+        MOVNE   R6, #2
+        ADDNE   R3, R3, R2, LSL #2
+
 ; now modify for CTRL and SHIFT
 
         LDROSB  R5, KeyBdStatus
 
         TST     R5, #KBStat_CtrlEngaged
-        ADDNE   R3, R3, #2
+        ADDNE   R3, R3, R6, LSL #1
 
         TST     R5, #KBStat_ShiftEngaged
-        ADDNE   R3, R3, #1
-
-        LDRB    R3, [R3, R2, LSL #2]            ; get real code
+        ADDNE   R3, R3, R6
+
+        TEQ     R6, #1
+      [ NoARMv4
+        ASSERT  :LNOT: NoUnaligned
+        LDR     R3, [R3, R2, LSL #2]            ; get real code
+        MOVNE   R3, R3, LSL #16
+        ANDEQ   R3, R3, #255
+        MOVNE   R3, R3, LSR #16
+      |
+        ADD     R3, R3, R2, LSL #2
+        LDREQB  R3, [R3]
+        LDRNEH  R3, [R3]
+      ]
 
 ; apply CAPS lock modifying
 
@@ -893,7 +981,7 @@ NULNULList                                      ; list for returning NUL NUL
 
 ProcessPendingAlt
         ADR     R6, NullCharList
-        LDR     R8, [R0, #KVPendingAltCode]
+        LDR     R8, [R0, #KeyHandler_PendingAltCode]
         ADD     R8, R8, R0
         BL      CallUserKeyCode
         B       ReturnNChars
@@ -1332,6 +1420,7 @@ NewInkeyPos
 
 NewInkeyNeg
         EOR     R1, R1, #&7F            ; invert bits for scan call
+        EOR     R2, R2, #&7F
         BL      BBCScanKeys
         Pull    PC
 
@@ -1339,20 +1428,22 @@ NewInkeyNeg
 ;
 ;       BBCScanKeys - Test individual key or scan for key depression
 ;
-; in:   R1 = 0..&7F => scan keyboard from BBC internal key R1
+; in:   R1 = 0..&7F => scan keyboard from BBC internal key R1+(R2<<8)
 ; 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
+; in:   R1 = &80..&FF => test if BBC internal key ((R1+(R2<<8)) 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
+        Push    "R10, R11, LR"
         LDR     R11, =ZeroPage+KeyWorkSpace
         AND     R1, R1, #&FF            ; trap wallies
 
+        AND     R10, R2, #&7F
+
         LDR     R2, KeyVec
 
         TST     R1, #&80                ; >=&80 => test single key
@@ -1362,13 +1453,25 @@ BBCScanKeys ROUT
         ADD     R0, R2, #1
         CMP     R0, #1                  ; if no key handler then
         MOVCC   R1, #0                  ;   return key up (C=0)
-        BCC     ExitBBCScan
+        BCC     ExitBBCTest
 
-        LDR     R0, [R2, #KVInkeyTran]
+        TEQ     R1, #&80+13             ; extension range?
+        TEQNE   R1, #&80+15
+        BEQ     DoBBCTestExtension
+
+        LDR     R0, [R2, #KeyHandler_InkeyTran]
         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
+
+        LDR     R10, [R2, #KeyHandler_KeyTranSize]
+        TST     R10, #KeyHandler_HasFlags
+        LDRNE   R10, [R2, #KeyHandler_Flags]
+        TSTNE   R10, #KeyHandler_Flag_Wide
+        MOVNE   R2, #&FF0000
+        BNE     DoBBCTestWide
+
         MOV     R2, #&FF000000
 02
         CMP     R0, #-1                 ; is it all FF's
@@ -1384,26 +1487,71 @@ BBCScanKeys ROUT
         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
+ExitBBCTest
+        Pull    "R10, R11, PC"
+
+DoBBCTestWide
+        MOV     R1, R0, LSL #16         ; get bottom entry
+        CMP     R1, #&FF0000            ; is it FF?
+        MOVEQ   R1, #0                  ; if so then none of keys down
+        BEQ     %BT04
 
-DoBBCScan
+        ADR     R3, KeysDown            ; look up in KeysDown
+        MOV     R1, R1, LSR #5+16
+        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 #16     ; shift down, putting FF in top entry
+        BEQ     DoBBCTestWide
+        B       %BT04
+
+DoBBCTestExtension
+        TST     R1, #2
+        ADDEQ   R10, R10, #&100 ; EQ -> (R1 AND &7F) = 13
+        ADDNE   R10, R10, #&200 ; NE -> (R1 AND &7F) = 15
+        ADR     R3, KeysDown
+        MOV     R1, R10, LSR #5
+        LDR     R3, [R3, R1, LSL #2]
+        AND     R1, R10, #31
+        MOV     R3, R3, LSL R1
+        MOV     R1, R3, LSR #31
+        B       %BT04
+
+DoBBCScan       ROUT
         CMP     R2, #-1                 ; if no key handler then
         MOVEQ   r1, #&FF                ;   return all keys up (C=1)
-        BEQ     ExitBBCScan
-        LDR     R0, [R2, #KVInkeyTran]
+        BEQ     ExitBBCTest
+        Push    "R4, R5"
+
+        ; Check for extension key range and include R10 if necessary
+        TEQ     R1, #13
+        TEQNE   R1, #15
+        ORREQ   R1, R1, R10, LSL #8
+
+        LDR     R0, [R2, #KeyHandler_InkeyTran]
         ADD     R0, R2, R0              ; R0 -> InkeyTran or InkeyTran2
 
-        Push    "R4, R5"
         ADD     R0, R0, #4 * &7F        ; R0 -> InkeyTran+4*&7F
+
+        LDR     R4, [R2, #KeyHandler_KeyTranSize]
+        TST     R4, #KeyHandler_HasFlags
+        LDRNE   R4, [R2, #KeyHandler_Flags]
+        TSTNE   R4, #KeyHandler_Flag_Wide
+        MOVNE   R4, #&FF0000
+        BNE     DoBBCScanWide
+
         MOV     R4, #&FF000000
 10
+        AND     R3, R1, #&7F-2
+        TEQ     R3, #13                 ; 13 or 15?
+        BLEQ    DoBBCScanExtension      ; handle extension range scan
         LDR     R3, [R0, -R1, LSL #2]   ; get word of indexes into KeysDown
 15
         CMP     R3, #-1                 ; all FFs ?
@@ -1416,7 +1564,7 @@ DoBBCScan
         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]
+        BNE     ExitBBCScan             ; [down, so stop]
         ORR     R3, R4, R3, LSR #8      ; up -> shift down putting FF in top
         B       %BT15
 18
@@ -1424,10 +1572,90 @@ DoBBCScan
         TEQ     R1, #&80                ; if not run out of keys
         BNE     %BT10                   ; then loop
         MOV     R1, #&FF                ; indicate no key
+ExitBBCScan
+        MOV     R2, #0
+        TEQ     R1, #&FF
+        MOVEQ   R2, #C_bit
+        MSR     CPSR_f, R2              ; C=0 <=> found key
+        Pull    "R4,R5,R10,R11,PC"
+
+DoBBCScanWide   ROUT
+        AND     R3, R1, #&7F-2
+        TEQ     R3, #13                 ; 13 or 15?
+        BLEQ    DoBBCScanExtension      ; handle extension range scan
+        LDR     R3, [R0, -R1, LSL #2]   ; get word of indexes into KeysDown
+15
+        MOV     R5, R3, LSL #16         ; get bottom entry
+        CMP     R5, #&FF0000            ; is it FF?
+        BEQ     %FT18                   ; then not one of these keys
+
+        ADR     R2, KeysDown
+        MOV     R5, R5, LSR #5+16
+        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     ExitBBCScan             ; [down, so stop]
+        ORR     R3, R4, R3, LSR #16     ; 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     DoBBCScanWide           ; then loop
+        MOV     R1, #&FF                ; indicate no key
+        B       ExitBBCScan
+
+DoBBCScanExtension ROUT
+        ; R2, R3, R5, R10 corruptible
+        ; Return to LR if no key found
+        MOV     R10, R1, LSR #8
+        TST     R1, #2
+        ADDEQ   R10, R10, #&100 ; EQ -> (R1 AND &7F) = 13
+        ADDNE   R10, R10, #&200 ; NE -> (R1 AND &7F) = 15
+        ADR     R2, KeysDown
+10
+        MOV     R3, R10, LSR #5
+        LDR     R3, [R2, R3, LSL #2]
+        AND     R5, R10, #31
+        MOVS    R3, R3, LSL R5
+        BNE     %FT20                   ; found one
+
+        BIC     R10, R10, #31           ; skip to next word
+        ADD     R10, R10, #32
+        TEQ     R10, #&300
+        BNE     %BT10
+        ; Ran out of extension keys
+        MOV     R1, #16
+        MOV     PC, LR
+
 20
-        CMP     R1, #&FF                ; C=0 <=> found key
-        Pull    "R4,R5,R11"
-        MOV     PC, R14
+      [ NoARMv5
+        CMP     R3, #1<<16
+        ADDLO   R10, R10, #16
+        MOVLO   R3, R3, LSL #16
+        CMP     R3, #1<<24
+        ADDLO   R10, R10, #8
+        MOVLO   R3, R3, LSL #8
+        CMP     R3, #1<<28
+        ADDLO   R10, R10, #4
+        MOVLO   R3, R3, LSL #4
+        CMP     R3, #1<<30
+        ADDLO   R10, R10, #2
+        MOVLO   R3, R3, LSL #2
+        TST     R3, #1<<31
+        ADDEQ   R10, R10, #1
+      |
+        CLZ     R3, R3
+        ADD     R10, R10, R3
+      ]
+        ; Convert back to inkey key
+        TST     R10, #&100
+        MOVNE   R1, #13
+        MOVEQ   R1, #15
+        ORR     R1, R1, R10, LSL #8
+        BIC     R1, R1, #&FF0000
+        B       ExitBBCScan
+
 
 ; *****************************************************************************
 ;
@@ -1437,38 +1665,58 @@ DoBBCScan
 ;       R2 = Old key     (------------""------------)
 ;
 ; out:  R1, R2 preserved
+;       R3 corrupt
+;
+; Note: Doesn't deal with extended key numbers
 ;
 
 WriteKeysDown ROUT
         Push    R14
         LDR     R11, =ZeroPage+KeyWorkSpace
-        MOV     R0, R1
+        AND     R0, R1, #&FF            ; mask off any extension bits
         BL      ConvertInternalKey
-        STRB    R0, CurrKey
-        MOV     R0, R2
+        STR     R0, CurrKey
+        AND     R0, R2, #&FF
         BL      ConvertInternalKey
-        STRB    R0, OldKey
+        STR     R0, OldKey
         Pull    PC
 
-ConvertInternalKey
+; in:   R0 = INKEY code EOR &FF
+; out:  R0 = internal low level key number
+;       R3 corrupt
+ConvertInternalKey ROUT
         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
+
+        Push    R4
+        LDR     R4, [R3, #KeyHandler_KeyTranSize]
+        TST     R4, #KeyHandler_HasFlags
+        LDRNE   R4, [R3, #KeyHandler_Flags]
+        TSTNE   R4, #KeyHandler_Flag_Wide
+
+        LDR     R4, [R3, #KeyHandler_InkeyTran]
+        ADD     R3, R3, R0, LSL #2
 
         SUB     R3, R3, #&80*4          ; R3 -> InkeyTran-4*&80
 
-        LDRB    R0, [R3, R0, LSL #2]    ; convert to ARM internal key
+      [ NoARMv4
+        LDRB    R0, [R3, R4]!           ; convert to ARM internal key
                                         ; (just get 1st key for this key)
+        LDRNEB  R4, [R3, #1]
+        ORRNE   R0, R0, R4, LSL #8
+      |
+        LDREQB  R0, [R3, R4]
+        LDRNEH  R0, [R3, R4]
+      ]
+        Pull    R4
         MOV     PC, R14
 
 
diff --git a/s/PMF/osbyte b/s/PMF/osbyte
index 420ea37..3c4d3c5 100644
--- a/s/PMF/osbyte
+++ b/s/PMF/osbyte
@@ -815,9 +815,9 @@ Osbyte78
         BL      WriteKeysDown
         MyOsbyte
 
-; Perform Keyboard Scan from 16
+; Perform Keyboard Scan from 15
 Osbyte7A
-        MOV     R1, #&10
+        MOV     R1, #&0F
 ; and drop thru to ...
 
 ; Perform Keyboard Scan
-- 
GitLab