; kbd - implement GetC for PS/2 keyboard ; ; It reads raw key codes from a FetchKC routine and returns, if appropriate, ; a proper ASCII char to type. See recipes rc2014/ps2 and sms/kbd. ; ; *** Defines *** ; Pointer to a routine that fetches the last typed keyword in A. Should return ; 0 when nothing was typed. ; KBD_FETCHKC ; *** Consts *** .equ KBD_KC_BREAK 0xf0 .equ KBD_KC_EXT 0xe0 .equ KBD_KC_LSHIFT 0x12 .equ KBD_KC_RSHIFT 0x59 ; *** Variables *** ; Set to previously received scan code .equ KBD_PREV_KC KBD_RAMSTART ; Whether Shift key is pressed .equ KBD_SHIFT_ON KBD_PREV_KC+1 .equ KBD_RAMEND KBD_SHIFT_ON+1 kbdInit: xor a ld (KBD_PREV_KC), a ld (KBD_SHIFT_ON), a ret kbdGetC: call KBD_FETCHKC or a jr z, .nothing ; scan code not zero, maybe we have something. ; Do we need to skip it? ex af, af' ; save fetched KC ld a, (KBD_PREV_KC) ; Whatever the KC, the new A becomes our prev. The easiest way to do ; this is to do it now. ex af, af' ; restore KC ld (KBD_PREV_KC), a ex af, af' ; restore prev KC ; If F0 (break code) or E0 (extended code), we skip this code cp KBD_KC_BREAK jr z, .break cp KBD_KC_EXT jr z, .ignore ex af, af' ; restore saved KC cp 0x80 jr nc, .ignore ; No need to skip, code within bounds, we have something! call .isShift jr z, .shiftPressed ; Let's see if there's a ASCII code associated to it. push hl ; --> lvl 1 ld hl, KBD_SHIFT_ON bit 0, (hl) ld hl, kbdScanCodes ; no flag changed jr z, .shiftNotPressed ; Shift is being pressed. Use Shifted table. ld hl, kbdScanCodesS .shiftNotPressed: call addHL ld a, (hl) pop hl ; <-- lvl 1 or a jp z, unsetZ ; no code. Keep A at 0, but unset Z ; We have something! cp a ; ensure Z ret .shiftPressed: ld a, 1 ld (KBD_SHIFT_ON), a jr .ignore ; to actual char to return .break: ex af, af' ; restore saved KC call .isShift jr nz, .ignore ; We had a shift break, update status xor a ld (KBD_SHIFT_ON), a ; continue to .ignore .ignore: ; A scan code over 0x80 is out of bounds or prev KC tell us we should ; skip. Ignore. xor a jp unsetZ .nothing: ; We have nothing. Before we go further, we'll wait a bit to give our ; device the time to "breathe". When we're in a "nothing" loop, the z80 ; hammers the device really fast and continuously generates interrupts ; on it and it interferes with its other task of reading the keyboard. push bc ld b, 0 .wait: nop djnz .wait pop bc jp unsetZ ; Whether KC in A is L or R shift .isShift: cp KBD_KC_LSHIFT ret z cp KBD_KC_RSHIFT ret ; A list of the values associated with the 0x80 possible scan codes of the set ; 2 of the PS/2 keyboard specs. 0 means no value. That value is a character than ; can be read in a GetC routine. No make code in the PS/2 set 2 reaches 0x80. kbdScanCodes: ; 0x00 1 2 3 4 5 6 7 8 9 a b c d e f .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,'`', 0 ; 0x10 9 = TAB .db 0, 0, 0, 0, 0,'q','1', 0, 0, 0,'z','s','a','w','2', 0 ; 0x20 32 = SPACE .db 0,'c','x','d','e','4','3', 0, 0, 32,'v','f','t','r','5', 0 ; 0x30 .db 0,'n','b','h','g','y','6', 0, 0, 0,'m','j','u','7','8', 0 ; 0x40 59 = ; .db 0,',','k','i','o','0','9', 0, 0,'.','/','l', 59,'p','-', 0 ; 0x50 13 = RETURN 39 = ' .db 0, 0, 39, 0,'[','=', 0, 0, 0, 0, 13,']', 0,'\', 0, 0 ; 0x60 8 = BKSP .db 0, 0, 0, 0, 0, 0, 8, 0, 0,'1', 0,'4','7', 0, 0, 0 ; 0x70 27 = ESC .db '0','.','2','5','6','8', 27, 0, 0, 0,'3', 0, 0,'9', 0, 0 ; Same values, but shifted kbdScanCodesS: ; 0x00 1 2 3 4 5 6 7 8 9 a b c d e f .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,'~', 0 ; 0x10 9 = TAB .db 0, 0, 0, 0, 0,'Q','!', 0, 0, 0,'Z','S','A','W','@', 0 ; 0x20 32 = SPACE .db 0,'C','X','D','E','$','#', 0, 0, 32,'V','F','T','R','%', 0 ; 0x30 .db 0,'N','B','H','G','Y','^', 0, 0, 0,'M','J','U','&','*', 0 ; 0x40 59 = ; .db 0,'<','K','I','O',')','(', 0, 0,'>','?','L',':','P','_', 0 ; 0x50 13 = RETURN .db 0, 0,'"', 0,'{','+', 0, 0, 0, 0, 13,'}', 0,'|', 0, 0 ; 0x60 8 = BKSP .db 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 0x70 27 = ESC .db 0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0