;**************************************************************************** ; PC keyboard Interface for the 6502 Microprocessor utilizing a 6522 VIA ; (or suitable substitute) ; ; Designed and Written by Daryl Rictor (c) 2001 65c02@altavista.com ; Offered as freeware. No warranty is given. Use at your own risk. ; Modified 09/08/2022 by Paul Bartlett for use with HobbyPCBs SBC65C02 ; ; ; Compile with 64tass --m65c02 pckybd.asm -o pckybd.hex ; ; ; Software communicates to/from the keyboard and converts the received scan-codes ; into usable ASCII code. ASCII codes 01-7F are decoded as well as extra ; pseudo-codes in order to acess all the extra keys including cursor, num pad, function, ; and 3 windows 98 keys. It was tested on two inexpensive keyboards with no errors. ; Just in case, though, I've coded the - key combination to perform ; a keyboard re-initialization just in case it goes south during data entry. ; ; Recommended Routines callable from external programs ; ; KBINPUT - wait for a key press and return with its assigned ASCII code in A. ; KBGET - wait for a key press and return with its unprocessed scancode in A. ; KBSCAN - Scan the keyboard for 105uS, returns 0 in A if no key pressed. ; Return ambiguous data in A if key is pressed. Use KBINPUT OR KBGET ; to get the key information. You can modify the code to automatically ; jump to either routine if your application needs it. ; KBINIT - Initialize the keyboard and associated variables and set the LEDs ; ;**************************************************************************** ; ; All standard keys and control keys are decoded to 7 bit (bit 7=0) standard ASCII. ; Control key note: It is being assumed that if you hold down the ctrl key, ; you are going to press an alpha key (A-Z) with it (except break key defined below.) ; If you press another key, its ascii code's lower 5 bits will be send as a control ; code. For example, Ctrl-1 sends $11, Ctrl-; sends $2B (Esc), Ctrl-F1 sends $01. ; ; The following no-standard keys are decoded with bit 7=1, bit 6=0 if not shifted, ; bit 6=1 if shifted, and bits 0-5 identify the key. ; ; Function key translation: ; ASCII / Shifted ASCII ; F1 - 81 / C1 ; F2 - 82 / C2 ; F3 - 83 / C3 ; F4 - 84 / C4 ; F5 - 85 / C5 ; F6 - 86 / C6 ; F7 - 87 / C7 ; F8 - 88 / C8 ; F9 - 89 / C9 ; F10 - 8A / CA ; F11 - 8B / CB ; F12 - 8C / CC ; ; The Print screen and Pause/Break keys are decoded as: ; ASCII Shifted ASCII ; PrtScn - 8F CF ; Ctrl-PrtScn - performs keyboard reinitialization in case of errors ; (haven't had any yet) (can be removed or changed by user) ; Pause/Brk - 03 03 (Ctrl-C) (can change to 8E/CE)(non-repeating key) ; Ctrl-Break - 02 02 (Ctrl-B) (can be changed to AE/EE)(non-repeating key) ; Scrl Lck - 8D CD ; ; The Alt key is decoded as a hold down (like shift and ctrl) but does not ; alter the ASCII code of the key(s) that follow. Rather, it sends ; a Alt key-down code and a seperate Alt key-up code. The user program ; will have to keep track of it if they want to use Alt keys. ; ; Alt down - A0 ; Alt up - E0 ; ; Example byte stream of the Alt-F1 sequence: A0 81 E0. If Alt is held down longer ; than the repeat delay, a series of A0's will preceeed the 81 E0. ; i.e. A0 A0 A0 A0 A0 A0 81 E0. ; ; The three windows 98 keys are decoded as follows: ; ASCII Shifted ASCII ; Left Menu Key - A1 E1 ; Right Menu Key - A2 E2 ; Right option Key - A3 E3 ; ; The following "special" keys ignore the shift key and return their special key code ; when numlock is off or their direct labeled key is pressed. When numlock is on, the digits ; are returned reguardless of shift key state. ; keypad(NumLck off) or Direct - ASCII Keypad(NumLck on) ASCII ; Keypad 0 Ins - 90 30 ; Keypad . Del - 7F 2E ; Keypad 7 Home - 97 37 ; Keypad 1 End - 91 31 ; Keypad 9 PgUp - 99 39 ; Keypad 3 PgDn - 93 33 ; Keypad 8 UpArrow - 98 38 ; Keypad 2 DnArrow - 92 32 ; Keypad 4 LfArrow - 94 34 ; Keypad 6 RtArrow - 96 36 ; Keypad 5 (blank) - 95 35 ; ;**************************************************************************** ; ; I/O Port definitions kbportreg = $9000 ; 6522 IO port register B kbportddr = $9002 ; 6522 IO data direction register B clk = $20 ; 6522 IO port clock bit mask (PB5) XX1XXXXX data = $10 ; 6522 IO port data bit mask (PB4) XXX1XXXX MASK45 = $CF MASK4 = $DF MASK5 = $EF MASK0 = $30 ; NOTE: some locations use the inverse of the bit masks to change the state of ; bit. You will have to find them and change them in the code acordingly. ; To make this easier, I've placed this text in the comment of each such statement: ; "(change if port bits change)" ; ; ; temporary storage locations (zero page can be used but not necessary) byte = $F0 ; byte send/received parity = $F1 ; parity holder for rx special = $F2 ; ctrl, shift, caps and kb LED holder lastbyte = $F3 ; last byte received ; bit definitions for the special variable ; (1 is active, 0 inactive) ; special = 01 - Scroll Lock ; 02 - Num Lock ; 04 - Caps lock ; 08 - control (either left or right) ; 10 - shift (either left or right) ; ; Scroll Lock LED is used to tell when ready for input ; Scroll Lock LED on = Not ready for input ; Scroll Lock LED off = Waiting (ready) for input ; ; Num Lock and Caps Lock LED's are used normally to ; indicate their respective states. ; ;*************************************************************************************** ; ; test program - reads input, prints the ascii code to the terminal and loops until the ; target keyboard key is pressed. ; ; external routine "output" prints character in A to the terminal ; external routine "print1byte" prints A register as two hexidecimal characters ; external routine "print_cr" prints characters $0D & $0A to the terminal ; (substitute your own routines as needed) ; output = $FF9B print1byte = $FECA print_cr = $FBF5 ; *= $F000 ; locate program beginning at $1000 jsr KBINIT ; init the keyboard, LEDs, and flags lp0 jsr print_cr ; prints 0D 0A (CR LF) to the terminal lp1 jsr kbinput ; wait for a keypress, return decoded ASCII code in A cmp #$0d ; if CR, then print CR LF to terminal beq lp0 ; cmp #$1B ; esc ascii code beq lp2 ; cmp #$20 ; bcc lp3 ; control key, print as except $0d (CR) & $2B (Esc) cmp #$80 ; bcs lp3 ; extended key, just print the hex ascii code as jsr output ; prints contents of A reg to the Terminal, ascii 20-7F JMP lp1 ; lp2 rts ; done lp3 pha ; lda #$3C ; < jsr output ; PHA AND #$F0 LSR A LSR A LSR A LSR A JSR print1byte PLA AND #$0F JSR print1byte lda #$3E ; > jsr output ; JMP lp1 ; ; ;************************************************************************************** ; ; Decoding routines ; ; KBINPUT is the main routine to call to get an ascii char from the keyboard ; (waits for a non-zero ascii code) ; kbreinit jsr kbinit ; KBINPUT jsr kbtscrl ; turn off scroll lock (ready to input) bne kbinput ; ensure its off kbinput1 jsr kbget ; get a code (wait for a key to be pressed) jsr kbcsrch ; scan for 14 special case codes kbcnvt beq kbinput1 ; 0=complete, get next scancode tax ; set up scancode as table pointer cmp #$78 ; see if its the F11 beq kbcnvt1 ; it is, skip keypad test cmp #$69 ; test for keypad codes 69 bmi kbcnvt1 ; thru cmp #$7E ; 7D (except 78 tested above) bpl kbcnvt1 ; skip if not a keypad code lda special ; test numlock bit #$02 ; numlock on? beq kbcnvt2 ; no, set shifted table for special keys txa ; yes, set unshifted table for number keys and #$7F ; tax ; JMP kbcnvt3 ; skip shift test kbcnvt1 lda special ; bit #$10 ; shift enabled? beq kbcnvt3 ; no kbcnvt2 txa ; yes ora #$80 ; set shifted table tax ; kbcnvt3 lda special ; bit #$08 ; control? beq kbcnvt4 ; no lda asciitbl,x ; get ascii code cmp #$8F ; {ctrl-Printscrn - do re-init or user can remove this code } beq kbreinit ; {do kb reinit } and #$1F ; mask control code (assumes A-Z is pressed) beq kbinput1 ; ensure mask didn't leave 0 tax ; JMP kbdone ; kbcnvt4 lda asciitbl,x ; get ascii code beq kbinput1 ; if ascii code is 0, invalid scancode, get another tax ; save ascii code in x reg lda special ; bit #$04 ; test caps lock beq kbdone ; caps lock off txa ; caps lock on - get ascii code cmp #$61 ; test for lower case a bcc kbdone ; if less than, skip down cmp #$7B ; test for lower case z bcs kbdone ; if greater than, skip down sec ; alpha chr found, make it uppercase sbc #$20 ; if caps on and lowercase, change to upper tax ; put new ascii to x reg kbdone phx ; save ascii to stack kbdone1 jsr kbtscrl ; turn on scroll lock (not ready to receive) beq kbdone1 ; ensure scroll lock is on pla ; get ASCII code rts ; return to calling program ; ;****************************************************************************** ; ; scan code processing routines ; ; kbtrap83 lda #$02 ; traps the F7 code of $83 and chang rts ; ; kbsshift lda #$10 ; *** neat trick to tuck code inside harmless cmd .byte $2c ; *** use BIT Absolute to skip lda #$02 below kbsctrl lda #$08 ; *** disassembles as LDA #$01 ora special ; BIT $A902 sta special ; ORA $02D3 JMP kbnull ; return with 0 in A ; kbtnum lda special ; toggle numlock bit in special eor #$02 ; sta special ; jsr kbsled ; update keyboard leds JMP kbnull ; return with 0 in A ; kbresend lda lastbyte ; jsr kbsend ; JMP kbnull ; return with 0 in A ; kbtcaps lda special ; toggle caps bit in special eor #$04 ; sta special ; jsr kbsled ; set new status leds kbnull lda #$00 ; set caps, get next code rts ; ; kbExt jsr kbget ; get next code cmp #$F0 ; is it an extended key release? beq kbexrls ; test for shift, ctrl, caps cmp #$14 ; right control? beq kbsctrl ; set control and get next scancode ldx #$03 ; test for 4 scancode to be relocated kbext1 cmp kbextlst,x ; scan list beq kbext3 ; get data if match found dex ; get next item bpl kbext1 ; cmp #$3F ; not in list, test range 00-3f or 40-7f bmi kbExt2 ; its a windows/alt key, just return unshifted ora #$80 ; return scancode and point to shifted table kbExt2 rts ; kbext3 lda kbextdat,x ; get new scancode rts ; ; kbextlst .byte $7E ; E07E ctrl-break scancode .byte $4A ; E04A kp/ .byte $12 ; E012 scancode .byte $7C ; E07C prt scrn ; kbextdat .byte $20 ; new ctrl-brk scancode .byte $6A ; new kp/ scancode .byte $00 ; do nothing (return and get next scancode) .byte $0F ; new prt scrn scancode ; kbexrls jsr kbget ; cmp #$12 ; is it a release of the E012 code? bne kbrlse1 ; no - process normal release JMP kbnull ; return with 0 in A ; kbrlse jsr kbget ; test for shift & ctrl cmp #$12 ; beq kbrshift ; reset shift bit cmp #$59 ; beq kbrshift ; kbrlse1 cmp #$14 ; beq kbrctrl ; cmp #$11 ; alt key release bne kbnull ; return with 0 in A kbralt lda #$13 ; new alt release scancode rts ; kbrctrl lda #$F7 ; reset ctrl bit in special .byte $2c ; use (BIT Absolute) to skip lda #$EF if passing down kbrshift lda #$EF ; reset shift bit in special and special ; sta special ; JMP kbnull ; return with 0 in A ; kbtscrl lda special ; toggle scroll lock bit in special eor #$01 ; sta special ; jsr kbsled ; update keyboard leds lda special ; bit #$01 ; check scroll lock status bit rts ; return ; kbBrk ldx #$07 ; ignore next 7 scancodes then kbBrk1 jsr kbget ; get scancode dex ; bne kbBrk1 ; lda #$10 ; new scan code rts ; ; kbcsrch ldx #$0E ; 14 codes to check kbcsrch1 cmp kbclst,x ; search scancode table for special processing beq kbcsrch2 ; if found run the routine dex ; bpl kbcsrch1 ; rts ; no match, return from here for further processing kbcsrch2 txa ; code found - get index asl ; mult by two tax ; save back to x lda byte ; load scancode back into A jmp (kbccmd,x) ; execute scancode routine, return 0 if done ; nonzero scancode if ready for ascii conversion ; ;keyboard command/scancode test list ; db=define byte, stores one byte of data ; kbclst .byte $83 ; F7 - move to scancode 02 .byte $58 ; caps .byte $12 ; Lshift .byte $59 ; Rshift .byte $14 ; ctrl .byte $77 ; num lock .byte $E1 ; Extended pause break .byte $E0 ; Extended key handler .byte $F0 ; Release 1 byte key code .byte $FA ; Ack .byte $AA ; POST passed .byte $EE ; Echo .byte $FE ; resend .byte $FF ; overflow/error .byte $00 ; underflow/error ; ; command/scancode jump table ; kbccmd .word kbtrap83 ; .word kbtcaps ; .word kbsshift ; .word kbsshift ; .word kbsctrl ; .word kbtnum ; .word kbBrk ; .word kbExt ; .word kbRlse ; .word kbnull ; .word kbnull ; .word kbnull ; .word kbresend ; .word kbflush ; .word kbflush ; ; ;************************************************************** ; ; Keyboard I/O suport ; ; ; KBSCAN will scan the keyboard for incoming data for about ; 105uS and returns with A=0 if no data was received. ; It does not decode anything, the non-zero value in A if data ; is ready is ambiguous. You must call KBGET or KBINPUT to ; get the keyboard data. ; KBSCAN ldx #$05 ; timer: x = (cycles - 40)/13 (105-40)/13=5 lda kbportddr ; and #MASK45 ; set clk(PB4) to input (change if port bits change) 11001111 sta kbportddr ; kbscan1 lda #clk ; bit kbportreg ; beq kbscan2 ; if clk goes low, data ready dex ; reduce timer bne kbscan1 ; wait while clk is high jsr kbdis ; timed out, no data, disable receiver lda #$00 ; set data not ready flag rts ; return kbscan2 jsr kbdis ; disable the receiver so other routines get it ; Three alternative exits if data is ready to be received: Either return or jmp to handler rts ; return (A<>0, A=clk bit mask value from kbdis) ; jmp KBINPUT ; if key pressed, decode it with KBINPUT ; jmp KBGET ; if key pressed, decode it with KBGET ; ; kbflush lda #$f4 ; flush buffer ; ; send a byte to the keyboard ; kbsend sta byte ; save byte to send phx ; save registers phy ; sta lastbyte ; keep just in case the send fails lda kbportreg ; and #MASK4 ; clk low, data high (change if port bits change) ora #data ; sta kbportreg ; lda kbportddr ; ora #MASK0 ; bit bits high (change if port bits change) sta kbportddr ; set outputs, clk=0, data=1 lda #$10 ; 1Mhz cpu clock delay (delay = cpuclk/62500) kbsendw dea ; bne kbsendw ; 64uS delay ldy #$00 ; parity counter ldx #$08 ; bit counter lda kbportreg ; and #MASK45 ; clk low, data low (change if port bits change) sta kbportreg ; lda kbportddr ; and #MASK4 ; set clk as input (change if port bits change) sta kbportddr ; set outputs jsr kbhighlow ; kbsend1 ror byte ; get lsb first bcs kbmark ; lda kbportreg ; and #MASK5 ; turn off data bit (change if port bits change) sta kbportreg ; JMP kbnext ; kbmark lda kbportreg ; ora #data ; sta kbportreg ; iny ; inc parity counter kbnext jsr kbhighlow ; dex ; bne kbsend1 ; send 8 data bits tya ; get parity count and #$01 ; get odd or even bne kbpclr ; if odd, send 0 lda kbportreg ; ora #data ; if even, send 1 sta kbportreg ; JMP kback ; kbpclr lda kbportreg ; and #MASK5 ; send data=0 (change if port bits change) sta kbportreg ; kback jsr kbhighlow ; lda kbportddr ; and #MASK45 ; set clk & data to input (change if port bits change) sta kbportddr ; ply ; restore saved registers plx ; jsr kbhighlow ; wait for ack from keyboard bne kbinit ; VERY RUDE error handler - re-init the keyboard kbsend2 lda kbportreg ; and #clk ; beq kbsend2 ; wait while clk low JMP kbdis ; diable kb sending ; ; KBGET waits for one scancode from the keyboard ; kberror lda #$FE ; resend cmd jsr kbsend ; KBGET phx ; phy ; lda #$00 ; sta byte ; clear scankey holder sta parity ; clear parity holder ldy #$00 ; clear parity counter ldx #$08 ; bit counter lda kbportddr ; and #MASK45 ; set clk to input (change if port bits change) sta kbportddr ; kbget1 lda #clk ; bit kbportreg ; bne kbget1 ; wait while clk is high lda kbportreg ; and #data ; get start bit bne kbget1 ; if 1, false start bit, do again kbget2 jsr kbhighlow ; wait for clk to return high then go low again cmp #$01 ; set c if data bit=1, clr if data bit=0 ; (change if port bits change) ok unless data=01 or 80 ; in that case, use ASL or LSR to set carry bit ror byte ; save bit to byte holder bpl kbget3 ; iny ; add 1 to parity counter kbget3 dex ; dec bit counter bne kbget2 ; get next bit if bit count > 0 jsr kbhighlow ; wait for parity bit beq kbget4 ; if parity bit 0 do nothing inc parity ; if 1, set parity to 1 kbget4 tya ; get parity count ply ; plx ; eor parity ; compare with parity bit and #$01 ; mask bit 1 only beq kberror ; bad parity jsr kbhighlow ; wait for stop bit beq kberror ; 0=bad stop bit lda byte ; if byte & parity 0, beq kbget ; no data, do again jsr kbdis ; lda byte ; rts ; ; kbdis lda kbportreg ; disable kb from sending more data and #MASK4 ; clk = 0 (change if port bits change) sta kbportreg ; lda kbportddr ; set clk to ouput low and #MASK45 ; (stop more data until ready) (change if port bits change) ora #clk ; sta kbportddr ; rts ; ; KBINIT lda #$02 ; init - num lock on, all other off sta special ; kbinit1 lda #$ff ; keybrd reset jsr kbsend ; reset keyboard jsr kbget ; cmp #$FA ; ack? bne kbinit1 ; resend reset cmd jsr kbget ; cmp #$AA ; reset ok bne kbinit1 ; resend reset cmd ; fall into to set the leds kbsled lda #$ED ; Set the keybrd LED's from kbleds variable jsr kbsend ; jsr kbget ; cmp #$FA ; ack? bne kbsled ; resend led cmd lda special ; and #$07 ; ensure bits 3-7 are 0 jsr kbsend ; rts ; ; kbhighlow lda #clk ; wait for a low to high to low transition bit kbportreg ; beq kbhighlow ; wait while clk low kbhl1 bit kbportreg ; bne kbhl1 ; wait while clk is high lda kbportreg ; and #data ; get data line state rts ; ;************************************************************* ; ; Unshifted table for scancodes to ascii conversion ; Scan|Keyboard ; Code|Key ; ----|---------- ASCIITBL .byte $00 ; 00 no key pressed .byte $89 ; 01 F9 .byte $87 ; 02 relocated F7 .byte $85 ; 03 F5 .byte $83 ; 04 F3 .byte $81 ; 05 F1 .byte $82 ; 06 F2 .byte $8C ; 07 F12 .byte $00 ; 08 .byte $8A ; 09 F10 .byte $88 ; 0A F8 .byte $86 ; 0B F6 .byte $84 ; 0C F4 .byte $09 ; 0D tab .byte $60 ; 0E `~ .byte $8F ; 0F relocated Print Screen key .byte $03 ; 10 relocated Pause/Break key .byte $A0 ; 11 left alt (right alt too) .byte $00 ; 12 left shift .byte $E0 ; 13 relocated Alt release code .byte $00 ; 14 left ctrl (right ctrl too) .byte $71 ; 15 qQ .byte $31 ; 16 1! .byte $00 ; 17 .byte $00 ; 18 .byte $00 ; 19 .byte $7A ; 1A zZ .byte $73 ; 1B sS .byte $61 ; 1C aA .byte $77 ; 1D wW .byte $32 ; 1E 2@ .byte $A1 ; 1F Windows 98 menu key (left side) .byte $02 ; 20 relocated ctrl-break key .byte $63 ; 21 cC .byte $78 ; 22 xX .byte $64 ; 23 dD .byte $65 ; 24 eE .byte $34 ; 25 4$ .byte $33 ; 26 3# .byte $A2 ; 27 Windows 98 menu key (right side) .byte $00 ; 28 .byte $20 ; 29 space .byte $76 ; 2A vV .byte $66 ; 2B fF .byte $74 ; 2C tT .byte $72 ; 2D rR .byte $35 ; 2E 5% .byte $A3 ; 2F Windows 98 option key (right click, right side) .byte $00 ; 30 .byte $6E ; 31 nN .byte $62 ; 32 bB .byte $68 ; 33 hH .byte $67 ; 34 gG .byte $79 ; 35 yY .byte $36 ; 36 6^ .byte $00 ; 37 .byte $00 ; 38 .byte $00 ; 39 .byte $6D ; 3A mM .byte $6A ; 3B jJ .byte $75 ; 3C uU .byte $37 ; 3D 7& .byte $38 ; 3E 8* .byte $00 ; 3F .byte $00 ; 40 .byte $2C ; 41 ,< .byte $6B ; 42 kK .byte $69 ; 43 iI .byte $6F ; 44 oO .byte $30 ; 45 0) .byte $39 ; 46 9( .byte $00 ; 47 .byte $00 ; 48 .byte $2E ; 49 .> .byte $2F ; 4A /? .byte $6C ; 4B lL .byte $3B ; 4C ;: .byte $70 ; 4D pP .byte $2D ; 4E -_ .byte $00 ; 4F .byte $00 ; 50 .byte $00 ; 51 .byte $27 ; 52 '" .byte $00 ; 53 .byte $5B ; 54 [{ .byte $3D ; 55 =+ .byte $00 ; 56 .byte $00 ; 57 .byte $00 ; 58 caps .byte $00 ; 59 r shift .byte $0D ; 5A .byte $5D ; 5B ]} .byte $00 ; 5C .byte $5C ; 5D \| .byte $00 ; 5E .byte $00 ; 5F .byte $00 ; 60 .byte $00 ; 61 .byte $00 ; 62 .byte $00 ; 63 .byte $00 ; 64 .byte $00 ; 65 .byte $08 ; 66 bkspace .byte $00 ; 67 .byte $00 ; 68 .byte $31 ; 69 kp 1 .byte $2f ; 6A kp / converted from E04A in code .byte $34 ; 6B kp 4 .byte $37 ; 6C kp 7 .byte $00 ; 6D .byte $00 ; 6E .byte $00 ; 6F .byte $30 ; 70 kp 0 .byte $2E ; 71 kp . .byte $32 ; 72 kp 2 .byte $35 ; 73 kp 5 .byte $36 ; 74 kp 6 .byte $38 ; 75 kp 8 .byte $1B ; 76 esc .byte $00 ; 77 num lock .byte $8B ; 78 F11 .byte $2B ; 79 kp + .byte $33 ; 7A kp 3 .byte $2D ; 7B kp - .byte $2A ; 7C kp * .byte $39 ; 7D kp 9 .byte $8D ; 7E scroll lock .byte $00 ; 7F ; ; Table for shifted scancodes ; .byte $00 ; 80 .byte $C9 ; 81 F9 .byte $C7 ; 82 relocated F7 .byte $C5 ; 83 F5 (F7 actual scancode=83) .byte $C3 ; 84 F3 .byte $C1 ; 85 F1 .byte $C2 ; 86 F2 .byte $CC ; 87 F12 .byte $00 ; 88 .byte $CA ; 89 F10 .byte $C8 ; 8A F8 .byte $C6 ; 8B F6 .byte $C4 ; 8C F4 .byte $09 ; 8D tab .byte $7E ; 8E `~ .byte $CF ; 8F relocated Print Screen key .byte $03 ; 90 relocated Pause/Break key .byte $A0 ; 91 left alt (right alt) .byte $00 ; 92 left shift .byte $E0 ; 93 relocated Alt release code .byte $00 ; 94 left ctrl (and right ctrl) .byte $51 ; 95 qQ .byte $21 ; 96 1! .byte $00 ; 97 .byte $00 ; 98 .byte $00 ; 99 .byte $5A ; 9A zZ .byte $53 ; 9B sS .byte $41 ; 9C aA .byte $57 ; 9D wW .byte $40 ; 9E 2@ .byte $E1 ; 9F Windows 98 menu key (left side) .byte $02 ; A0 relocated ctrl-break key .byte $43 ; A1 cC .byte $58 ; A2 xX .byte $44 ; A3 dD .byte $45 ; A4 eE .byte $24 ; A5 4$ .byte $23 ; A6 3# .byte $E2 ; A7 Windows 98 menu key (right side) .byte $00 ; A8 .byte $20 ; A9 space .byte $56 ; AA vV .byte $46 ; AB fF .byte $54 ; AC tT .byte $52 ; AD rR .byte $25 ; AE 5% .byte $E3 ; AF Windows 98 option key (right click, right side) .byte $00 ; B0 .byte $4E ; B1 nN .byte $42 ; B2 bB .byte $48 ; B3 hH .byte $47 ; B4 gG .byte $59 ; B5 yY .byte $5E ; B6 6^ .byte $00 ; B7 .byte $00 ; B8 .byte $00 ; B9 .byte $4D ; BA mM .byte $4A ; BB jJ .byte $55 ; BC uU .byte $26 ; BD 7& .byte $2A ; BE 8* .byte $00 ; BF .byte $00 ; C0 .byte $3C ; C1 ,< .byte $4B ; C2 kK .byte $49 ; C3 iI .byte $4F ; C4 oO .byte $29 ; C5 0) .byte $28 ; C6 9( .byte $00 ; C7 .byte $00 ; C8 .byte $3E ; C9 .> .byte $3F ; CA /? .byte $4C ; CB lL .byte $3A ; CC ;: .byte $50 ; CD pP .byte $5F ; CE -_ .byte $00 ; CF .byte $00 ; D0 .byte $00 ; D1 .byte $22 ; D2 '" .byte $00 ; D3 .byte $7B ; D4 [{ .byte $2B ; D5 =+ .byte $00 ; D6 .byte $00 ; D7 .byte $00 ; D8 caps .byte $00 ; D9 r shift .byte $0D ; DA .byte $7D ; DB ]} .byte $00 ; DC .byte $7C ; DD \| .byte $00 ; DE .byte $00 ; DF .byte $00 ; E0 .byte $00 ; E1 .byte $00 ; E2 .byte $00 ; E3 .byte $00 ; E4 .byte $00 ; E5 .byte $08 ; E6 bkspace .byte $00 ; E7 .byte $00 ; E8 .byte $91 ; E9 kp 1 .byte $2f ; EA kp / converted from E04A in code .byte $94 ; EB kp 4 .byte $97 ; EC kp 7 .byte $00 ; ED .byte $00 ; EE .byte $00 ; EF .byte $90 ; F0 kp 0 .byte $7F ; F1 kp . .byte $92 ; F2 kp 2 .byte $95 ; F3 kp 5 .byte $96 ; F4 kp 6 .byte $98 ; F5 kp 8 .byte $1B ; F6 esc .byte $00 ; F7 num lock .byte $CB ; F8 F11 .byte $2B ; F9 kp + .byte $93 ; FA kp 3 .byte $2D ; FB kp - .byte $2A ; FC kp * .byte $99 ; FD kp 9 .byte $CD ; FE scroll lock ; NOT USED .byte $00 ; FF ; end