.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 ;; @ref kb::MOD "status of the modifier keys" modifier: .byte .bss ;; previous scancode is used to determine if a key was released (BREAK sent previously) ;; will be $ff if the previous scancode was K_BREAK previous_scancode: .byte ;; 0 when no key or non-character key was pressed, otherwise character of the key (with modifiers applied) char: .byte ;; @ref kb::K "keycode" of the last key that was pressed or 0 if none was pressed keycode: .byte .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 @ref kb::keycode ;; If the key is an ascii character, the corresponding ascii character is stored in @ref 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 cpx #$60 ; codes >= 60 in separate table to reduce memory consumption bcs x_ge_60 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 modralt: lda CHARS_MODRALT,x bra check_char modshift: lda CHARS_MODSHIFT,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 x_ge_60: ; x >= $60 cpx #$90 ; only BREAK is higher than 8F, (also code table only has 8F bytes) bcs check_release ; CHARS_NUMLOCK_OFF - CHARS_NOMOD = 60 -> no need to subtract $60 first ; CHARS_NUMLOCK_ON - CHARS_MODSHIFT = 60 -> no need to subtract $60 first bbs kb::MOD::NUMLOCK, modifier, modshift ; if numlock active lda CHARS_NOMOD,x bra check_char check_release: ; x >= $90 cpx #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" ;; numlock range characters where numlock is off 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_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_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" 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"