diff --git a/programs/ps2_keyboard_text_handler.h65 b/programs/ps2_keyboard_text_handler.h65 new file mode 100644 index 0000000..1d3fc22 --- /dev/null +++ b/programs/ps2_keyboard_text_handler.h65 @@ -0,0 +1,189 @@ +;;******************************************************************************** +;; @module ps2_keyboard_text_handler +;; @type system +;; @details: +;; This module processes keyboard scancodes from the ps2_keyboard driver. +;; It requires a PS/2 keyboard that supports scancode set 3. +;; +;; @section What it does +;; - keeps track of modifier keys: shift, left/right alt, left right meta (super), +;; control, capslock, numlock, scrollock +;; - convert valid keycodes to chars, dependening on the state of shift and right alt +;; - update the keyboard LEDs (numlock, capslock,, scrolllock) + +;; This handler is designed for efficient text input. +;; It does not track the status of the keys (except modifiers) and is therefore not suitable for games. +;; +;; @section How it works +;; In the init function, the module disables the typematic behaviour for the modifier keys by +;; sending commands to the keyboard. The 3 lock keys will be set to make only mode and +;; the others to make release. +;; This makes tracking the modifier keys much easier and is the reason why scancode set 3 +;; is required, as these commands are only available with that. +;; +;; Using scancode set 3 also adds the benefit that all scancodes are only 1 byte large, +;; making the recognition of scancodes trivial. +;; +;; Scancodes from the keyboard are processed immediately, as the handling functions is called +;; from the keyboard's interrupt handler. +;; +;; @subsection Mapping keycodes to chars +;; The mapping of keycodes to chars is done using an array of chars for each state: +;; default, shift and right alt. The tables contains the ascii characters, 0 where the keycode does not +;; generate a character and 1 where the keycode is modifier key. +;; This approach consumes a relatively large amount of ROM (3 * 144 bytes), but is super fast, +;; as loading a character is merely one indexed load instruction. +;; Checking for mod keys is now also fast, beceause we only have to do 1 comparison to see IF it is a modifier key. +;; +;; @section How to use it +;; Call `kb::init` and check if it was successful (Z = 1). +;; In your program loop, check if kb::keycode is 0. If it is not, a key has been pressed. +;; +;; If the keypress generated a character `kb::char` will be non-zero and contain the character. +;; +;; Note that if you will probably want to write a 0 to `kb::keycode` and `kb::char` +;; after processing the keycode. This will not be done by the handler. +;; +;; Conditional branches based on the status of a modifier (SHIFT, RALT, LALT, CTRL, META, NUMLOCK, SCROLLLOCK) can be done +;; using the `bbs` and `bbr` instructions, as each modifier is represented by a bit of the `kb::modifier` variable +;; in the zeropage. +;; +;; @subsection Changing the keyboard behaviour +;; You can change the typematic rate and delay (see PS/2 keyboard command 0xF3) +;; and the typematic behavior (make, make/release, typematic) for all keys except the modifier keys. +;; +;; You may also send the echo (0xEE) and identify (0xF2) commands to the keyboard. +;;******************************************************************************** +.ifndef INCLUDE_KEYBOARD_TEXT +INCLUDE_KEYBOARD_TEXT = 1 + +.include "ps2_keyboard.h65" + +.scope kb +Import kb, init, char, keycode +Import kb, CHARS_NOMOD, CHARS_MODRALT, CHARS_MODSHIFT, CHARS_NUMLOCK_OFF, CHARS_NUMLOCK_ON +ImportZp kb, modifier + +.enum MOD + SHIFT = %10000000 ; set if either capslock is on or shift is pressed + RALT = %01000000 ; set while rightalt is pressed + LALT = %00100000 ; set while leftalt is pressed + META = %00010000 ; set while left or right meta is pressed + CTRL = %00001000 ; set while left or right ctrl is pressed + CAPSLOCK = %00000100 ; ps/2 LED + NUMLOCK = %00000010 ; ps/2 LED + SCROLLLOCK = %00000001 ; ps/2 LED +.endenum + +K_BREAK = $F0 + +.enum K + F1 = $07 + ESCAPE = $08 + TAB = $0D + GRAVE = $0E + F2 = $0F + LEFTCTRL = $11 + LEFTSHIFT = $12 + CAPSLOCK = $14 + K_Q = $15 + K_1 = $16 + F3 = $17 + LEFTALT = $19 + K_Y = $1A + K_S = $1B + K_A = $1C + K_W = $1D + K_2 = $1E + F4 = $1F + K_C = $21 + K_X = $22 + K_D = $23 + K_E = $24 + K_4 = $25 + K_3 = $26 + F5 = $27 + SPACE = $29 + K_V = $2A + K_F = $2B + K_T = $2C + K_R = $2D + K_5 = $2E + F6 = $2F + K_N = $31 + K_B = $32 + K_H = $33 + K_G = $34 + K_Z = $35 + K_6 = $36 + F7 = $37 + RIGHTALT = $39 + K_M = $3A + K_J = $3B + K_U = $3C + K_7 = $3D + K_8 = $3E + F8 = $3F + COMMA = $41 + K_K = $42 + K_I = $43 + K_O = $44 + K_0 = $45 + K_9 = $46 + F9 = $47 + DOT = $49 + DASH = $4A + KPDIVIDE = $4A + K_L = $4B + OUML = $4C + K_P = $4D + SSHARP = $4E + KPMINUS = $4E + F10 = $4F + AUML = $52 + UUML = $54 + BACKTICK = $55 + F11 = $56 + PRINT = $57 + RIGHTCTRL = $58 + RIGHTSHIFT = $59 + ENTER = $5A + PLUS = $5B + LESSTHAN = $5C + HASH = $5D + F12 = $5E + SCROLLLOCK = $5F + RIGHT = $60 + LEFT = $61 + PAUSE = $62 + UP = $63 + DELETE = $64 + END = $65 + BACKSPACE = $66 + INSERT = $67 + KP1 = $69 + DOWN = $6A + KP4 = $6B + KP7 = $6C + PAGEDOWN = $6D + POS1 = $6E + PAGEUP = $6F + KP0 = $70 + KPDOT = $71 + KP2 = $72 + KP5 = $73 + KP6 = $74 + KP8 = $75 + NUMLOCK = $76 + KPENTER = $79 + KP3 = $7A + KPPLUS = $7C + KP9 = $7D + KPASTERISK = $7E + LEFTMETA = $8B + RIGHTMETA = $8C + MENU = $8D +.endenum + +.endscope +.endif ; guard diff --git a/programs/ps2_keyboard_text_handler.s65 b/programs/ps2_keyboard_text_handler.s65 new file mode 100644 index 0000000..78bf796 --- /dev/null +++ b/programs/ps2_keyboard_text_handler.s65 @@ -0,0 +1,306 @@ +.include "ps2_keyboard_text_handler.h65" +Export kb, init, char, keycode +Export kb, CHARS_NOMOD, CHARS_MODRALT, CHARS_MODSHIFT, CHARS_NUMLOCK_OFF, CHARS_NUMLOCK_ON +ExportZp kb, modifier + +.zeropage +modifier: .res 1 +.bss +previous_scancode: .res 1 ; all 1 if previous_scancode is break +char: .res 1 +keycode: .res 1 + +.code +;;******************************************************************************** +;; @function Initialize the keyboard handler +;; @returns Z: Z = 1 => success, Z = 0 => failure +;; @details: +;; Initializes the PS/2 keyboard driver and the handler. +;; If any PS/2 command fails, init returns with Z = 0. The failed command (and parameter) can be loaded from `ps2kb::send_cmd` and `ps2kb::send_data` +;; After init, the keyboard will be able to send scancodes. +;; @modifies A, X, Y +;;******************************************************************************** +.proc init + stz modifier + stz char + stz keycode + stz previous_scancode + + jsr ps2kb::init + + ; init & reset + ps2kb_CmdSelfTest + ps2kb_WaitFinishCmd + lda ps2kb::cmd_response+1 + cmp #$aa + bne fail + ; set set 3 + ps2kb_CmdSetScancodeSet 3 + ps2kb_WaitFinishCmd + lda ps2kb::cmd_response+1 + cmp #ps2kb::ACK + bne fail + + ; toggle-behaving keys + ldx #kb::K::CAPSLOCK + jsr set_make + ldx #kb::K::NUMLOCK + jsr set_make + ldx #kb::K::SCROLLLOCK + jsr set_make + + ; modifiers + ldx #kb::K::LEFTSHIFT + jsr set_make_release + ldx #kb::K::RIGHTSHIFT + jsr set_make_release + ldx #kb::K::LEFTCTRL + jsr set_make_release + ldx #kb::K::RIGHTCTRL + jsr set_make_release + ldx #kb::K::LEFTALT + jsr set_make_release + ldx #kb::K::RIGHTALT + jsr set_make_release + ldx #kb::K::LEFTMETA + jsr set_make_release + ldx #kb::K::RIGHTMETA + jsr set_make_release + + ps2kb_CmdEnable + ps2kb_WaitFinishCmd + lda ps2kb::cmd_response + cmp #ps2kb::ACK + bne fail + + StoreDByte process_scancode, ps2kb::scancode_handler + jsr ps2kb::begin_receive + lda #0 ; set Z = success +fail: + rts + +set_make: + ; x must be set to scancode + lda #$fd + bra set +set_make_release: + ; x must be set to scancode + lda #$fc +set: + ldy #0 + jsr ps2kb::send_command + ps2kb_WaitFinishCmd + lda ps2kb::cmd_response+1 + cmp #ps2kb::ACK + bne fail + rts +.endproc + + +;;******************************************************************************** +;; @function Process a scancode +;; @details +;; Update modifier bits if a mod key is pressed. +;; If the key is not a mod key, the keycode is stored in kb::keycode +;; If the key is an ascii character, the corresponding ascii character is stored in kb::char +;; @modifies A, X +;;******************************************************************************** +.proc process_scancode + ; check how this scancode needs to be interpreted + ldx ps2kb::scancode + bit previous_scancode + jmi break_key ; bit 7 set = BREAK + + cmp #$90 ; only BREAK is higher than 8F, (also code table only has 8F bytes) + bcs check_release + cmp #$60 ; codes >= 60 in separate table to reduce memory consumption + bcs upper_range + bbs kb::MOD::SHIFT, modifier, modshift ; if shift active + bbs kb::MOD::RALT, modifier, modralt ; if ralt active + +; load from the correct table, if 0 => non-char, if 1 => non-char but mod, else char +nomod: + lda CHARS_NOMOD,x + bra check_char +upper_range: + bbs kb::MOD::NUMLOCK, modifier, modnumlock ; if numlock active + ; numlock off + lda CHARS_NUMLOCK_OFF,x + bra check_char +modnumlock: + lda CHARS_NUMLOCK_ON,x + bra check_char +modshift: + lda CHARS_MODSHIFT,x + bra check_char +modralt: + lda CHARS_MODRALT,x +check_char: + beq not_mod ; 0 => not mod and not char + cmp #1 ; 1 => mod + beq is_mod +@char: ; store char + sta char +not_mod: ; store keycode if not mod + stx keycode +_rts: + rts + +check_release: + cmp #kb::K_BREAK + bne _rts + lda #$ff + sta previous_scancode + rts + +is_mod: + ; check which mod + cpx #kb::K::LEFTSHIFT + beq set_shift + cpx #kb::K::RIGHTSHIFT + beq set_shift + cpx #kb::K::RIGHTALT + beq set_rightalt + cpx #kb::K::LEFTALT + beq set_leftalt + cpx #kb::K::RIGHTMETA + beq set_meta + cpx #kb::K::LEFTMETA + beq set_meta + cpx #kb::K::RIGHTCTRL + beq set_ctrl + cpx #kb::K::LEFTCTRL + beq set_ctrl + cpx #kb::K::CAPSLOCK + beq toggle_capslock + cpx #kb::K::NUMLOCK + beq toggle_numlock + cpx #kb::K::SCROLLLOCK + beq toggle_scrolllock + + ; a real keycode and not a released keycode, twobyte or release scancode + +set_ctrl: + smb kb::MOD::CTRL, modifier + rts +set_meta: + smb kb::MOD::META, modifier + rts +set_leftalt: + smb kb::MOD::LALT, modifier + rts +set_rightalt: + smb kb::MOD::RALT, modifier + rts +set_shift: ; process shift + bbs kb::MOD::CAPSLOCK, modifier, @unset_shift ; CAPSLOCK unset shift when capslock is on +@set_shift: + smb kb::MOD::SHIFT, modifier + rts +@unset_shift: + rmb kb::MOD::SHIFT, modifier + rts + +toggle_numlock: ; toggle numlock + lda modifier + eor #kb::MOD::NUMLOCK + sta modifier + bra update_leds +toggle_scrolllock: ; toggle numlock + lda modifier + eor #kb::MOD::SCROLLLOCK + sta modifier + bra update_leds +toggle_capslock: ; toggle capslock & shift + lda modifier + eor #(kb::MOD::CAPSLOCK | kb::MOD::SHIFT) + sta modifier + ; fallthrough +load_update_leds: + lda modifier +update_leds: + and #%00000111 ; only bottom 3 bits allowed - otherwise command fails + tax + ps2kb_CmdSetLeds + rts +.endproc + +;;******************************************************************************** +;; @details +;; Unset modifier bits if a mod key is released. +;; @param X: The scancode +;; @modifies: +;;******************************************************************************** +.proc break_key + ; rmb kb::PREVIOUS::BREAK, kb::previous_scancode + stz previous_scancode + + ; check mod keys + cpx #kb::K::LEFTSHIFT + beq unset_shift + cpx #kb::K::RIGHTSHIFT + beq unset_shift + cpx #kb::K::RIGHTALT + beq unset_rightalt + cpx #kb::K::LEFTALT + beq unset_leftalt + cpx #kb::K::RIGHTMETA + beq unset_meta + cpx #kb::K::LEFTMETA + beq unset_meta + cpx #kb::K::RIGHTCTRL + beq unset_ctrl + cpx #kb::K::LEFTCTRL + rts +unset_shift: ; process shift + bbs kb::MOD::CAPSLOCK, modifier, @set_shift ; set shift when capslock is on + rmb kb::MOD::SHIFT, modifier ; unset if capslock is off + rts +@set_shift: + smb kb::MOD::SHIFT, modifier + rts +unset_ctrl: + rmb kb::MOD::CTRL, modifier + rts +unset_meta: + rmb kb::MOD::META, modifier + rts +unset_leftalt: + rmb kb::MOD::LALT, modifier + rts +unset_rightalt: + rmb kb::MOD::RALT, modifier + rts +.endproc + +.rodata +.align 256 +CHARS_NOMOD: + .byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00^\x00" + .byte "\x00\x01\x01<\x01q1\x00\x00\x01ysaw2\x00" + .byte "\x00cxde43\x00\x00 vftr5\x00" + .byte "\x00nbhgz6\x00\x00\x01mju78\x00" + .byte "\x00,kio09\x00\x00.-l\xEFp\xE2\x00" + .byte "\x00\x00\xE1#\xF5`\x00\x00\x01\x01\x00\x00\x00\x00\x00\x01" +CHARS_MODSHIFT: + .byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xDF\x00" + .byte "\x00\x01\x01>\x01Q!\x00\x00\x01YSAW\"\x00" + .byte "\x00CXDE$\xED\x00\x00 VFTR%\x00" + .byte "\x00NBHGZ&\x00\x00\x01MJU/(\x00" + .byte "\x00;KIO=)\x00\x00:_L\xEFP\xE2\x00" + .byte "\x00\x00\xE1'\xF5`\x00\x00\x01\x01\x00\x00\x00\x00\x00\x01" +CHARS_MODRALT: + .byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\x00" + .byte "\x00\x01\x01|\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00" + .byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + .byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00{[\x00" + .byte "\x00\x00\x00\x00\x00}]\x00\x00\xA5\xB0\x00\x00\x00\x00\x00" + .byte "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x01" +CHARS_NUMLOCK_OFF: + .byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + .byte "\x00\x00\x00\x00\x00\x00\x01/\x00\x00\x00\x00+\x00*\x00" + .byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00-\x00" +CHARS_NUMLOCK_ON: + .byte "\x00\x00\x00\x00\x00\x00\x00\x00\x001\x0047\x00\x00\x00" + .byte "0.2568\x01\x00\x00\x003\x00\x009\x00\x00" + .byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00" diff --git a/system/keyboard_handler.h65 b/system/keyboard_handler.h65 deleted file mode 100644 index 117238e..0000000 --- a/system/keyboard_handler.h65 +++ /dev/null @@ -1,141 +0,0 @@ -;;******************************************************************************** -;; @module keyboard_handler -;; @type system -;; @details: -;; This module processes keyboard scancodes from the ps2_keyboard module. -;;******************************************************************************** -.ifndef INCLUDE_KEYBOARD -INCLUDE_KEYBOARD = 1 - -.include "ps2_keyboard.h65" - -.scope kb -Import kb, CHARS_NOMOD, CHARS_MODSHIFT -ImportZp kb,status - -.enum STATUS - RELEASE = %10000000 - TWOBYTE = %01000000 - SHIFT = %00100000 - CAPSLOCK = %00010000 - CAPSLOCK_HELD = %00000010 - RIGHTALT = %00001000 - NUMLOCK = %00000100 -.endenum - -K_TWOBYTE = $E0 -K_RELEASE = $F0 - -.enum K - F9 = $01 - F5 = $03 - F3 = $04 - F1 = $05 - F2 = $06 - F12 = $07 - F10 = $09 - F8 = $0A - F6 = $0B - F4 = $0C - TAB = $0D - GRAVE = $0E - LEFTALT = $11 - LEFTSHIFT = $12 - LEFTCTRL = $14 - K_Q = $15 - K_1 = $16 - K_Y = $1A - K_S = $1B - K_A = $1C - K_W = $1D - K_2 = $1E - K_C = $21 - K_X = $22 - K_D = $23 - K_E = $24 - K_4 = $25 - K_3 = $26 - SPACE = $29 - K_V = $2A - K_F = $2B - K_T = $2C - K_R = $2D - K_5 = $2E - K_N = $31 - K_B = $32 - K_H = $33 - K_G = $34 - K_Z = $35 - K_6 = $36 - K_M = $3A - K_J = $3B - K_U = $3C - K_7 = $3D - K_8 = $3E - COMMA = $41 - K_K = $42 - K_I = $43 - K_O = $44 - K_0 = $45 - K_9 = $46 - DOT = $49 - DASH = $4A - K_L = $4B - OUML = $4C - K_P = $4D - SSHARP = $4E - AUML = $52 - UUML = $54 - BACKTICK = $55 - CAPSLOCK = $58 - RIGHTSHIFT = $59 - ENTER = $5A - PLUS = $5B - HASH = $5D - LESSTHAN = $61 - BACKSPACE = $66 - KP1 = $69 - KP4 = $6B - KP7 = $6C - KP0 = $70 - KPDOT = $71 - KP2 = $72 - KP5 = $73 - KP6 = $74 - KP8 = $75 - ESCAPE = $76 - NUMLOCK = $77 - F11 = $78 - KPPLUS = $79 - KP3 = $7A - KPMINUS = $7B - KPASTERISK = $7C - KP9 = $7D - SCROLLLOCK = $7E - F7 = $83 -.endenum - -.enum K2 - RIGHTALT = $11 - RIGHTCTRL = $14 - LEFTMETA = $1F - RIGHTMETA = $27 - MENU = $2F - BREAK = $37 - SYSRQ = $3F - KPDIVIDE = $4A - KPENTER = $5A - END = $69 - LEFT = $6B - POS1 = $6C - INSERT = $70 - DELETE = $71 - DOWN = $72 - RIGHT = $74 - UP = $75 - PAGEDOWN = $7A - PAGEUP = $7D -.endenum - -.endscope -.endif ; guard diff --git a/system/keyboard_handler.s65 b/system/keyboard_handler.s65 deleted file mode 100644 index 9ce816b..0000000 --- a/system/keyboard_handler.s65 +++ /dev/null @@ -1,28 +0,0 @@ -.include "keyboard_handler.h65" -Export kb, CHARS_NOMOD, CHARS_MODSHIFT -ExportZp kb,status -.zeropage -status: .res 1 - -.rodata -.align 256 -CHARS_NOMOD: - .byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00^\x00" - .byte "\x00\x00\x00\x00\x00q1\x00\x00\x00ysaw2\x00" - .byte "\x00cxde43\x00\x00 vftr5\x00" - .byte "\x00nbhgz6\x00\x00\x00mju78\x00" - .byte "\x00,kio09\x00\x00.-l\xEFp\xE2\x00" - .byte "\x00\x00\xE1\x00\xF5`\x00\x00\x00\x00\x00\x00\x00#\x00\x00" - .byte "\x00<\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - .byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - -.align 256 -CHARS_MODSHIFT: - .byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xDF\x00" - .byte "\x00\x00\x00\x00\x00Q!\x00\x00\x00YSAW\"\x00" - .byte "\x00CXDE$\xED\x00\x00 VFTR%\x00" - .byte "\x00NBHGZ&\x00\x00\x00MJU/(\x00" - .byte "\x00;KIO=)\x00\x00:_L\xEFP\xE2\x00" - .byte "\x00\x00\xE1\x00\xF5`\x00\x00\x00\x00\x00\x00\x00'\x00\x00" - .byte "\x00>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - .byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"