10 REM > USBKBD/src v0.42
   20 REM Background keyboard driver
   30 :
   40 REM v0.30 17-Nov-2017:
   50 REM Works with Vinculum v1 with keyboard as device 0 (no hub)
   60 REM Does not work with Vinculum v1 with a hub - DCv1 doesn't like hubs
   70 :
   80 REM v0.40 12-May-2018:
   90 REM Steps through addresses to find HID device, starts at device 1 if hub
  100 REM  present, Vinculum v2 doesn't respond to DRD reading from hub.
  110 REM Vinculum v2 HID responds verrrry slowly, eventually stalls system.
  120 :
  130 REM v0.41 11-Nov-2018:
  140 REM Alt converted to Shift+Ctrl, Alt+fkey same as Shift+Ctrl+fkey
  150 REM Should be tested for explicitly, as Alt+letter functions as Ctrl
  160 REM and Alt+nothing functions as Shift+Ctrl scroll pause.
  170 :
  180 REM v0.42 15-Dec-2018:
  190 REM Optimised modifier testing, added Alt modifier
  200 REM Alt only modifies special keys, doesn't do Shift+Ctrl pause.
  210 :
  220 kbd%=1
  230 OSBYTE=&FFF4:OSARGS=&FFDA
  240 EVNTV=&220:KEYV=&228:INSV=&22A
  250 usb_D=&FCF8:usb_S=&FCF9
  260 :
  270 DIM mcode% &300:load%=&FFFF08BC
  280 FOR P=0 TO 1
  290   P%=load%:O%=mcode%
  300   [OPT P*3+4
  310   .exec%
  320   .usb_Init
  330   LDX #usb_SCS-usb_Cmd:JSR usb_CmdSend6
  340   JSR usb_Flush:CMP #&80  :\ Cy=HUB
  350   AND #8:BEQ usb_NoKbd
  360   BCS usb_InitLp1a        :\ HUB, start at device 1
  370   .usb_InitLp1
  380   LDX #usb_SC0-usb_Cmd:JSR usb_CmdSend6
  390   JSR usb_Flush:CMP #8:BEQ usb_Init2
  400   .usb_InitLp1a
  410   INC usb_SC0+2:LDA usb_SC0+2
  420   CMP #13:BNE usb_InitLp1:BEQ usb_NoKbd
  430   .usb_Init2
  440   PHP:SEI:LDX #1
  450   .usb_InitLp3
  460   LDA KEYV,X:STA oldKEYV+1,X
  470   LDA usb_Vectors+0,X:STA KEYV,X
  480   LDA usb_Vectors+2,X:STA EVNTV,X
  490   DEX:BPL usb_InitLp3
  500   PLP:LDA #14:LDX #4:JMP OSBYTE
  510   .usb_Vectors
  520   EQUW usb_KEY:EQUW usb_EVNT
  530   :
  540   .usb_NoKbd
  550   BRK:EQUB 214:EQUS "No kbd?":\BRK
  560   .kbd_lastK:BRK:.kbd_modify:BRK
  570   :
  580   .usb_CmdSend6
  590   LDY #6
  600   .usb_CmdSend
  610   TXA:PHA:JSR usb_Flush:PLA:TAX
  620   .usb_CmdSendLp1
  630   LDA usb_Cmd,X:JSR usb_Wr
  640   INX:DEY:BEQ usb_Rd
  650   CMP #13:BNE usb_CmdSendLp1
  660   .usb_CmdSendLp2
  670   JSR usb_Rd:CMP #13:BNE usb_CmdSendLp2
  680   BEQ usb_CmdSend
  690   :
  700   .usb_Flush  :BIT usb_D:LDX #64
  710   .usb_FlushLp:DEX:BNE usb_FlushLp
  720   BIT usb_S:BPL usb_Flush:RTS
  730   :
  740   .usb_Rd:BIT &FF:BMI usb_Rd2:BIT usb_S:BMI usb_Rd
  750   .usb_Rd2:LDA usb_D:RTS
  760   .usb_Wr:BIT &FF:BMI usb_Wr2:BIT usb_S:BVS usb_Wr
  770   .usb_Wr2:STA usb_D:.usb_bye:RTS
  780   :
  790   .usb_KEY
  800   BVS oldKEYV:BCS oldKEYV
  810   JSR oldKEYV:BMI usb_bye:BVS usb_bye    :\ oldKEYV Shift or Ctrl pressed
  820   BIT kbd_modify:PHP:PLA:RTS             :\ Set flags and A from Shift/Ctrl
  830   .oldKEYV
  840   JMP 0
  850   :
  860   .usb_EVNT
  870   CMP #4:BNE usb_bye                      :\ Not VSync
  880   :
  890   \ We are running event code, so all IRQs are disabled
  900   \ So we can use IRQ workspace at &FA/&FB
  910   \
  920   LDX #0:LDY #12:JSR usb_CmdSend         :\ Read from keyboard
  930   CLC:BEQ kbd_flush:CMP #9:BCS kbd_flush :\ Null packet or invalid packet
  940   JSR usb_Rd:JSR usb_Rd:TAY              :\ Y=modifiers
  950   JSR usb_Rd:JSR usb_Rd:CMP #&FF         :\ A=keypress, set CC+NE
  960   .kbd_flush
  970   PHP:JSR usb_Flush:PLP:BCS kbd_exit     :\ Not our data packet
  980   \BIT usb_D:\BIT sub_S:\BPL kbd_flush:\BCS kbd_exit:\TAX
  990   :
 1000   \ CS   =null/invalid packet
 1010   \ CC+EQ=no data received, no change to keys pressed
 1020   \ CC+NE=keypress, might be same or different to last key
 1030   BEQ kbd_repeat                         :\ No key change
 1040   PHA:TYA:STA &FA                        :\ A=gascgasc
 1050   ASL A:ASL A:ASL A:ASL A                :\ A=gasc0000
 1060   ORA &FA:AND #&F0                       :\ A=GASC0000
 1070   ASL A:ASL A:ROL A                      :\ A=C000000A, Cy=S
 1080   BCC P%+4:ORA #&40                      :\ A=CS00000A
 1090   STA kbd_modify                         :\ b7=ctrl, b6=shift, b0=alt
 1100   :
 1110   PLA:CMP kbd_lastK:BEQ kbd_repeat       :\ Same key pressed down
 1120   STA kbd_lastK                          :\ New or no keypress
 1130   LDX &254:STX &E7                       :\ Copy KeyDelay to KeyCountdown
 1140   .kbd_keypress
 1150   JSR kbd_convert:BCS kbd_exit           :\ No keypress
 1160   TAY:EOR &26C:BNE kbd_insert            :\ Not Escape
 1170   STA &E7                                :\ Cancel autorepeat
 1180   .kbd_insert
 1190   LDX #3
 1200   .kbd_save
 1210   LDA &EE,X:PHA:DEX:BNE kbd_save         :\ Save OSBYTE parameters
 1220   LDA #153:JSR OSBYTE:LDX #&FD           :\ Insert with Escape processing
 1230   .kbd_restore
 1240   PLA:STA &F2,X:INX:BNE kbd_restore      :\ Restore OSBYTE parameters
 1250   .kbd_exit
 1260   RTS
 1270   :
 1280   .kbd_repeat
 1290   LDA &E7:BEQ kbd_exit                   :\ Repeat disabled
 1300   DEC &E7:BEQ P%+6:DEC &E7:BNE kbd_exit  :\ Countdown not reached
 1310   LDA &255:STA &E7                       :\ Copy KeyRepeat to KeyCountdown
 1320   LDA kbd_lastK:JMP kbd_keypress         :\ Insert another keypress
 1330   :
 1340   .kbd_convert
 1350   CMP #&39:BNE kbd_notcaps
 1360   LDA &25A:EOR #16:STA &25A:RTS          :\ CAPS
 1370   .kbd_notcaps
 1380   SEC:SBC #4:BPL kbd_test:SBC #&1F       :\ Reduce range if &84+
 1390   .kbd_test
 1400   CMP #&69:BCS kbd_exit                  :\ No keypress
 1410   LDX kbd_modify:STX &FA                 :\ Get modifiers
 1420   ADC #ASC"A":CMP #ASC"[":BCC kbd_tab    :\ Letter
 1430   SBC #42:CMP #ASC"9"+1:BCC kbd_tab      :\ Digit
 1440   TAX:LDA kbd_keytable-58,X
 1450   BNE kbd_tab:LDA &26B                   :\ TAB
 1460   .kbd_tab
 1470   CMP #ASC"!":BCC kbd_exit               :\ ctrls+spc
 1480   CMP #127:BEQ kbd_done:BCS kbd_topset   :\ DEL and topbit keys
 1490   CPX #&7B:BEQ kbd_nonalpha              :\ Special JP '\'
 1500   CMP #ASC"_":BCS kbd_nonalpha           :\ &5F and &60
 1510   CMP #ASC"A":BCS kbd_alpha              :\ Letters and [\]^
 1520   .kbd_nonalpha
 1530   BIT &FA:BVC kbd_noshift                :\ No SHIFT
 1540   AND #&1F:TAX:LDA kbd_shift,X           :\ Get shifted character
 1550   .kbd_noshift
 1560   CMP #ASC"@":BCS kbd_ctrl:RTS           :\ If alpha, check for CTRL
 1570   .kbd_topset
 1580   CMP #&E0:BCC kbd_top1                  :\ Not keypad keys
 1590   BNE kbd_keypad:LDA #&CD                :\ Special case for KRet
 1600   .kbd_keypad
 1610   LDX &27E:BNE kbd_keypad1:SBC #&D0:SEC  :\ If base=0, use '0'
 1620   .kbd_keypad1
 1630   SBC #&F0:CLC:ADC &27E:\CLC:\RTS        :\ Translate keypad keys
 1640   BPL kbd_done                           :\ b7=0, return keypress
 1650   .kbd_top1
 1660   LSR &FA                                :\ &FA=0CS00000, Cy=A
 1670   ROR &FA                                :\ &FA=A0CS0000, Cy=0
 1680   BPL P%+4:EOR #&B0                      :\ Apply ALT
 1690   EOR &FA:RTS                            :\ Apply CTRL and SHIFT
 1700   .kbd_alpha
 1710   CMP #ASC"[":BCS tab_alpha2             :\ Skip past if not a letter
 1720   ROR A:EOR &25A:AND #&EF:EOR &25A:ROL A :\ Apply CAPS to letters
 1730   .tab_alpha2
 1740   BIT &FA:BVC kbd_ctrl:EOR #32           :\ Apply SHIFT
 1750   .kbd_ctrl
 1760   BIT &FA:BPL kbd_done:AND #31           :\ Apply CTRL
 1770   .kbd_done
 1780   CLC:RTS
 1790   :
 1800   .usb_Cmd
 1810   .usb_SCS:EQUB &10:EQUB 13
 1820   .usb_IPH:EQUB &91:EQUB 13
 1830   .usb_QP2:EQUB &2C:EQUB 13
 1840   .usb_SC0:EQUB &86:EQUB &20:EQUB &00:EQUB 13
 1850   .usb_DRD:EQUB &84:EQUB 13
 1860   :
 1870   .kbd_shift
 1880   EQUS LEFT$("~!@~$%&""()*+<_>?)!@#$%^&*(*:<+>?",kbd%=0):\ US
 1890   EQUS LEFT$("~!@~$%&@()*+<_>?)!""`$%^&*(*:<+>?",kbd%=1):\ UK
 1900   \EQUSLEFT$("@!@~$%&'()*+<=>?0!""#$%&'()*+<+>`",kbd%=2):\ JP/BBC/ECMA
 1910   EQUS LEFT$("@!@~$%&'()*+<=>?_!""#$%&'()*+_+>`",kbd%=2):\ JP/BBC/ECMA
 1920   \ Shift+     !"#$%&'()*+,-./012 3456789c;<=>?
 1930   \           @ABCDEFGHIJKLMNOPQR STUVWXYZ[\]^_
 1940   \           `abcdefghijklmnopqr stuvwxyz{|}~
 1950   \
 1960   .kbd_keytable
 1970   EQUB 48:EQUB 13:EQUB 27:EQUB 127:EQUB 0:EQUS " -"
 1980   EQUS LEFT$("=[]\#;'`",kbd%=0):\ US
 1990   EQUS LEFT$("=[]\#;'`",kbd%=1):\ UK
 2000   EQUS LEFT$("^@[\];:_",kbd%=2):\ JP/BBC/ECMA
 2010   EQUS ",./":EQUB 1:EQUB &81:EQUB &82:EQUB &83:EQUB &84:EQUB &85:EQUB &86
 2020   EQUB &87:EQUB &88:EQUB &89:EQUB &CA:EQUB &CB:EQUB &CC:EQUB &80:EQUB &C3
 2030   EQUB &8A:EQUB &CD:EQUB &C8:EQUB &9F:EQUB &7F:EQUB &8B:EQUB &9E:EQUB &8D
 2040   EQUB &8C:EQUB &8E:EQUB &8F:EQUB &C2
 2050   EQUB &C0+ASC"/":EQUB &C0+ASC"*":EQUB &C0+ASC"-":EQUB &C0+ASC"+"
 2060   EQUB &E0       :EQUB &C0+ASC"1":EQUB &C0+ASC"2":EQUB &C0+ASC"3"
 2070   EQUB &C0+ASC"4":EQUB &C0+ASC"5":EQUB &C0+ASC"6":EQUB &C0+ASC"7"
 2080   EQUB &C0+ASC"8":EQUB &C0+ASC"9":EQUB &C0+ASC"0":EQUB &C0+ASC"."
 2090   EQUS "\"       :EQUB &C1       :EQUB &C0+ASC",":EQUB &C0+ASC"="
 2100   EQUS "\":EQUB &C7:EQUS "\":EQUB &C6:EQUB &C5
 2110   \ Note, calculating function/keypad key codes takes 10 bytes but only
 2120   \ save 9 bytes in the lookup table
 2130   :
 2140   ]
 2150 NEXT:IF (P%AND&FFFF)>&B00:PRINT"Code overrun":END
 2160 IF (kbd_lastK AND &FFFF)<&900:PRINT "Code start too early":END
 2170 PRINT"*SAVE USBKBD ";~mcode%;" ";~O%;" ";~exec%OR&FFFF0000;" ";~load%