diff --git a/Source/bnk1.asm b/Source/bnk1.asm index 5df8bf75..2bee8d21 100644 --- a/Source/bnk1.asm +++ b/Source/bnk1.asm @@ -77,6 +77,12 @@ INITSYS1: #IF (HDSKENABLE) CALL HDSK_INIT #ENDIF +#IF (PPKENABLE) + CALL PPK_INIT +#ENDIF +#IF (KBDENABLE) + CALL KBD_INIT +#ENDIF #IF (TTYENABLE) CALL TTY_INIT #ENDIF @@ -586,6 +592,24 @@ SIZ_HDSK .EQU $ - ORG_HDSK .ECHO " bytes.\n" #ENDIF +#IF (PPKENABLE) +ORG_PPK .EQU $ + #INCLUDE "ppk.asm" +SIZ_PPK .EQU $ - ORG_PPK + .ECHO "PPK occupies " + .ECHO SIZ_PPK + .ECHO " bytes.\n" +#ENDIF + +#IF (KBDENABLE) +ORG_KBD .EQU $ + #INCLUDE "kbd.asm" +SIZ_KBD .EQU $ - ORG_KBD + .ECHO "KBD occupies " + .ECHO SIZ_KBD + .ECHO " bytes.\n" +#ENDIF + #IF (TTYENABLE) ORG_TTY .EQU $ #INCLUDE "tty.asm" diff --git a/Source/config_n8_2312.asm b/Source/config_n8_2312.asm index 93753aa7..d7b21bf8 100644 --- a/Source/config_n8_2312.asm +++ b/Source/config_n8_2312.asm @@ -78,6 +78,9 @@ HDSKENABLE .EQU FALSE ; TRUE FOR SIMH HDSK SUPPORT HDSKTRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF IDEENABLE = TRUE) HDSKCAPACITY .EQU 64 ; CAPACITY OF DEVICE (IN MB) ; +PPKENABLE .EQU TRUE ; TRUE FOR PARALLEL PORT KEYBOARD +KBDENABLE .EQU FALSE ; TRUE FOR PS/2 KEYBOARD ON I8242 +; TTYENABLE .EQU TRUE ; INCLUDE TTY EMULATION SUPPORT ANSIENABLE .EQU TRUE ; INCLUDE ANSI EMULATION SUPPORT ; diff --git a/Source/config_n8_2511.asm b/Source/config_n8_2511.asm index 6913ce84..bbb738b8 100644 --- a/Source/config_n8_2511.asm +++ b/Source/config_n8_2511.asm @@ -78,6 +78,9 @@ HDSKENABLE .EQU FALSE ; TRUE FOR SIMH HDSK SUPPORT HDSKTRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF IDEENABLE = TRUE) HDSKCAPACITY .EQU 64 ; CAPACITY OF DEVICE (IN MB) ; +PPKENABLE .EQU TRUE ; TRUE FOR PARALLEL PORT KEYBOARD +KBDENABLE .EQU FALSE ; TRUE FOR PS/2 KEYBOARD ON I8242 +; TTYENABLE .EQU TRUE ; INCLUDE TTY EMULATION SUPPORT ANSIENABLE .EQU TRUE ; INCLUDE ANSI EMULATION SUPPORT ; diff --git a/Source/config_n8vem.asm b/Source/config_n8vem.asm index cda6a22f..3f5d1fef 100644 --- a/Source/config_n8vem.asm +++ b/Source/config_n8vem.asm @@ -78,6 +78,9 @@ HDSKENABLE .EQU FALSE ; TRUE FOR SIMH HDSK SUPPORT HDSKTRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF IDEENABLE = TRUE) HDSKCAPACITY .EQU 64 ; CAPACITY OF DEVICE (IN MB) ; +PPKENABLE .EQU FALSE ; TRUE FOR PARALLEL PORT KEYBOARD +KBDENABLE .EQU FALSE ; TRUE FOR PS/2 KEYBOARD ON I8242 +; TTYENABLE .EQU FALSE ; INCLUDE TTY EMULATION SUPPORT ANSIENABLE .EQU FALSE ; INCLUDE ANSI EMULATION SUPPORT ; diff --git a/Source/config_n8vem_dide.asm b/Source/config_n8vem_dide.asm index 51ab5cdb..dec3b14b 100644 --- a/Source/config_n8vem_dide.asm +++ b/Source/config_n8vem_dide.asm @@ -78,6 +78,9 @@ HDSKENABLE .EQU FALSE ; TRUE FOR SIMH HDSK SUPPORT HDSKTRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF IDEENABLE = TRUE) HDSKCAPACITY .EQU 64 ; CAPACITY OF DEVICE (IN MB) ; +PPKENABLE .EQU FALSE ; TRUE FOR PARALLEL PORT KEYBOARD +KBDENABLE .EQU FALSE ; TRUE FOR PS/2 KEYBOARD ON I8242 +; TTYENABLE .EQU FALSE ; INCLUDE TTY EMULATION SUPPORT ANSIENABLE .EQU FALSE ; INCLUDE ANSI EMULATION SUPPORT ; diff --git a/Source/config_n8vem_diskio.asm b/Source/config_n8vem_diskio.asm index d44ddb18..3be93632 100644 --- a/Source/config_n8vem_diskio.asm +++ b/Source/config_n8vem_diskio.asm @@ -78,6 +78,9 @@ HDSKENABLE .EQU FALSE ; TRUE FOR SIMH HDSK SUPPORT HDSKTRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF IDEENABLE = TRUE) HDSKCAPACITY .EQU 64 ; CAPACITY OF DEVICE (IN MB) ; +PPKENABLE .EQU FALSE ; TRUE FOR PARALLEL PORT KEYBOARD +KBDENABLE .EQU FALSE ; TRUE FOR PS/2 KEYBOARD ON I8242 +; TTYENABLE .EQU FALSE ; INCLUDE TTY EMULATION SUPPORT ANSIENABLE .EQU FALSE ; INCLUDE ANSI EMULATION SUPPORT ; diff --git a/Source/config_n8vem_diskio3.asm b/Source/config_n8vem_diskio3.asm index 7e666284..a06e44ce 100644 --- a/Source/config_n8vem_diskio3.asm +++ b/Source/config_n8vem_diskio3.asm @@ -78,6 +78,9 @@ HDSKENABLE .EQU FALSE ; TRUE FOR SIMH HDSK SUPPORT HDSKTRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF IDEENABLE = TRUE) HDSKCAPACITY .EQU 64 ; CAPACITY OF DEVICE (IN MB) ; +PPKENABLE .EQU FALSE ; TRUE FOR PARALLEL PORT KEYBOARD +KBDENABLE .EQU FALSE ; TRUE FOR PS/2 KEYBOARD ON I8242 +; TTYENABLE .EQU FALSE ; INCLUDE TTY EMULATION SUPPORT ANSIENABLE .EQU FALSE ; INCLUDE ANSI EMULATION SUPPORT ; diff --git a/Source/config_n8vem_ppide.asm b/Source/config_n8vem_ppide.asm index fea97c9e..77a51f2a 100644 --- a/Source/config_n8vem_ppide.asm +++ b/Source/config_n8vem_ppide.asm @@ -78,6 +78,9 @@ HDSKENABLE .EQU FALSE ; TRUE FOR SIMH HDSK SUPPORT HDSKTRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF IDEENABLE = TRUE) HDSKCAPACITY .EQU 64 ; CAPACITY OF DEVICE (IN MB) ; +PPKENABLE .EQU FALSE ; TRUE FOR PARALLEL PORT KEYBOARD +KBDENABLE .EQU FALSE ; TRUE FOR PS/2 KEYBOARD ON I8242 +; TTYENABLE .EQU FALSE ; INCLUDE TTY EMULATION SUPPORT ANSIENABLE .EQU FALSE ; INCLUDE ANSI EMULATION SUPPORT ; diff --git a/Source/config_n8vem_ppisd.asm b/Source/config_n8vem_ppisd.asm index 9d5c5b02..8131c327 100644 --- a/Source/config_n8vem_ppisd.asm +++ b/Source/config_n8vem_ppisd.asm @@ -78,6 +78,9 @@ HDSKENABLE .EQU FALSE ; TRUE FOR SIMH HDSK SUPPORT HDSKTRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF IDEENABLE = TRUE) HDSKCAPACITY .EQU 64 ; CAPACITY OF DEVICE (IN MB) ; +PPKENABLE .EQU FALSE ; TRUE FOR PARALLEL PORT KEYBOARD +KBDENABLE .EQU FALSE ; TRUE FOR PS/2 KEYBOARD ON I8242 +; TTYENABLE .EQU FALSE ; INCLUDE TTY EMULATION SUPPORT ANSIENABLE .EQU FALSE ; INCLUDE ANSI EMULATION SUPPORT ; diff --git a/Source/config_n8vem_propio.asm b/Source/config_n8vem_propio.asm index f61efdc5..dc32007e 100644 --- a/Source/config_n8vem_propio.asm +++ b/Source/config_n8vem_propio.asm @@ -78,6 +78,9 @@ HDSKENABLE .EQU FALSE ; TRUE FOR SIMH HDSK SUPPORT HDSKTRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF IDEENABLE = TRUE) HDSKCAPACITY .EQU 64 ; CAPACITY OF DEVICE (IN MB) ; +PPKENABLE .EQU FALSE ; TRUE FOR PARALLEL PORT KEYBOARD +KBDENABLE .EQU FALSE ; TRUE FOR PS/2 KEYBOARD ON I8242 +; TTYENABLE .EQU FALSE ; INCLUDE TTY EMULATION SUPPORT ANSIENABLE .EQU FALSE ; INCLUDE ANSI EMULATION SUPPORT ; diff --git a/Source/config_n8vem_vdu.asm b/Source/config_n8vem_vdu.asm index fe4f3e3a..bc94f341 100644 --- a/Source/config_n8vem_vdu.asm +++ b/Source/config_n8vem_vdu.asm @@ -78,6 +78,9 @@ HDSKENABLE .EQU FALSE ; TRUE FOR SIMH HDSK SUPPORT HDSKTRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF IDEENABLE = TRUE) HDSKCAPACITY .EQU 64 ; CAPACITY OF DEVICE (IN MB) ; +PPKENABLE .EQU TRUE ; TRUE FOR PARALLEL PORT KEYBOARD +KBDENABLE .EQU FALSE ; TRUE FOR PS/2 KEYBOARD ON I8242 +; TTYENABLE .EQU TRUE ; INCLUDE TTY EMULATION SUPPORT ANSIENABLE .EQU TRUE ; INCLUDE ANSI EMULATION SUPPORT ; diff --git a/Source/config_simh.asm b/Source/config_simh.asm index 115821db..6aeefb25 100644 --- a/Source/config_simh.asm +++ b/Source/config_simh.asm @@ -78,6 +78,9 @@ HDSKENABLE .EQU TRUE ; TRUE FOR HDSK SUPPORT HDSKTRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF IDEENABLE = TRUE) HDSKCAPACITY .EQU 64 ; CAPACITY OF DEVICE (IN MB) ; +PPKENABLE .EQU FALSE ; TRUE FOR PARALLEL PORT KEYBOARD +KBDENABLE .EQU FALSE ; TRUE FOR PS/2 KEYBOARD ON I8242 +; TTYENABLE .EQU FALSE ; INCLUDE TTY EMULATION SUPPORT ANSIENABLE .EQU FALSE ; INCLUDE ANSI EMULATION SUPPORT ; diff --git a/Source/config_zeta.asm b/Source/config_zeta.asm index 8c4c2657..1c775ae3 100644 --- a/Source/config_zeta.asm +++ b/Source/config_zeta.asm @@ -78,6 +78,9 @@ HDSKENABLE .EQU FALSE ; TRUE FOR SIMH HDSK SUPPORT HDSKTRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF IDEENABLE = TRUE) HDSKCAPACITY .EQU 64 ; CAPACITY OF DEVICE (IN MB) ; +PPKENABLE .EQU FALSE ; TRUE FOR PARALLEL PORT KEYBOARD +KBDENABLE .EQU FALSE ; TRUE FOR PS/2 KEYBOARD ON I8242 +; TTYENABLE .EQU FALSE ; INCLUDE TTY EMULATION SUPPORT ANSIENABLE .EQU FALSE ; INCLUDE ANSI EMULATION SUPPORT ; diff --git a/Source/config_zeta_ppp.asm b/Source/config_zeta_ppp.asm index f704dfb4..e5967e62 100644 --- a/Source/config_zeta_ppp.asm +++ b/Source/config_zeta_ppp.asm @@ -78,6 +78,9 @@ HDSKENABLE .EQU FALSE ; TRUE FOR SIMH HDSK SUPPORT HDSKTRACE .EQU 1 ; 0=SILENT, 1=ERRORS, 2=EVERYTHING (ONLY RELEVANT IF IDEENABLE = TRUE) HDSKCAPACITY .EQU 64 ; CAPACITY OF DEVICE (IN MB) ; +PPKENABLE .EQU FALSE ; TRUE FOR PARALLEL PORT KEYBOARD +KBDENABLE .EQU FALSE ; TRUE FOR PS/2 KEYBOARD ON I8242 +; TTYENABLE .EQU FALSE ; INCLUDE TTY EMULATION SUPPORT ANSIENABLE .EQU FALSE ; INCLUDE ANSI EMULATION SUPPORT ; diff --git a/Source/kbd.asm b/Source/kbd.asm new file mode 100644 index 00000000..6b50fbdc --- /dev/null +++ b/Source/kbd.asm @@ -0,0 +1,743 @@ +;__________________________________________________________________________________________________ +; +; 8242 BASED PS/2 KEYBOARD DRIVER FOR N8VEM +; +; ORIGINAL CODE BY DR JAMES MOXHAM +; ROMWBW ADAPTATION BY WAYNE WARTHEN +;__________________________________________________________________________________________________ +; +; TODO: +; CONSIDER DETECTING ERRORS IN STATUS BYTE (PERR, TO) +;__________________________________________________________________________________________________ +; DATA CONSTANTS +;__________________________________________________________________________________________________ +; +; IO PORTS +; +KBD_IOBASE .EQU $E0 +; +KBD_ST .EQU KBD_IOBASE + $0A +KBD_CMD .EQU KBD_IOBASE + $0A +KBD_DATA .EQU KBD_IOBASE + $02 +; +; TIMING CONSTANTS +; +KBD_WAITTO .EQU 150 +; +; STATUS BITS (FOR KBD_STATUS) +; +KBD_EXT .EQU 01H ; BIT 0, EXTENDED SCANCODE ACTIVE +KBD_BREAK .EQU 02H ; BIT 1, THIS IS A KEY UP (BREAK) EVENT +KBD_KEYRDY .EQU 80H ; BIT 7, INDICATES A DECODED KEYCODE IS READY +; +; STATE BITS (FOR KBD_STATE, KBD_LSTATE, KBD_RSTATE) +; +KBD_SHIFT .EQU 01H ; BIT 0, SHIFT ACTIVE (PRESSED) +KBD_CTRL .EQU 02H ; BIT 1, CONTROL ACTIVE (PRESSED) +KBD_ALT .EQU 04H ; BIT 2, ALT ACTIVE (PRESSED) +KBD_WIN .EQU 08H ; BIT 3, WIN ACTIVE (PRESSED) +KBD_SCRLCK .EQU 10H ; BIT 4, CAPS LOCK ACTIVE (TOGGLED ON) +KBD_NUMLCK .EQU 20H ; BIT 5, NUM LOCK ACTIVE (TOGGLED ON) +KBD_CAPSLCK .EQU 40H ; BIT 6, SCROLL LOCK ACTIVE (TOGGLED ON) +KBD_NUMPAD .EQU 80H ; BIT 7, NUM PAD KEY (KEY PRESSED IS ON NUM PAD) +; +KBD_DEFRPT .EQU $40 ; DEFAULT REPEAT RATE (.5 SEC DELAY, 30CPS) +KBD_DEFSTATE .EQU KBD_NUMLCK ; DEFAULT STATE (NUM LOCK ON) +; +;__________________________________________________________________________________________________ +; DATA +;__________________________________________________________________________________________________ +; +KBD_SCANCODE .DB 0 ; RAW SCANCODE +KBD_KEYCODE .DB 0 ; RESULTANT KEYCODE AFTER DECODING +KBD_STATE .DB 0 ; STATE BITS (SEE ABOVE) +KBD_LSTATE .DB 0 ; STATE BITS FOR "LEFT" KEYS +KBD_RSTATE .DB 0 ; STATE BITS FOR "RIGHT" KEYS +KBD_STATUS .DB 0 ; CURRENT STATUS BITS (SEE ABOVE) +KBD_REPEAT .DB 0 ; CURRENT REPEAT RATE +KBD_IDLE .DB 0 ; IDLE COUNT +; +;__________________________________________________________________________________________________ +; KEYBOARD INITIALIZATION +;__________________________________________________________________________________________________ +; +KBD_INIT: + LD A,KBD_DEFRPT ; GET DEFAULT REPEAT RATE + LD (KBD_REPEAT),A ; SAVE IT + LD A,KBD_DEFSTATE ; GET DEFAULT STATE + LD (KBD_STATE),A ; SAVE IT + + LD A,$AA ; CONTROLLER SELF TEST + CALL KBD_PUTCMD ; SEND IT + CALL KBD_GETDATA ; CONTROLLER SHOULD RESPOND WITH $55 (ACK) + LD A,$60 ; SET COMMAND REGISTER + CALL KBD_PUTCMD ; SEND IT + LD A,$60 ; XLAT ENABLED, MOUSE DISABLED, NO INTS + CALL KBD_PUTDATA ; SEND IT + + CALL KBD_GETDATA ; GOBBLE UP $AA FROM POWER UP, AS NEEDED + +; LD A,$AE ; COMMAND = ENABLE KEYBOARD +; CALL KBD_PUTCMD ; SEND IT +; LD A,$A7 ; COMMAND = DISABLE MOUSE +; CALL KBD_PUTCMD ; SEND IT + + CALL KBD_RESET ; RESET THE KEYBOARD + CALL KBD_SETLEDS ; UPDATE LEDS BASED ON CURRENT TOGGLE STATE BITS + CALL KBD_SETRPT ; UPDATE REPEAT RATE BASED ON CURRENT SETTING + + XOR A ; SIGNAL SUCCESS + RET +; +;__________________________________________________________________________________________________ +; KEYBOARD STATUS +;__________________________________________________________________________________________________ +; +KBD_STAT: + LD A,(KBD_STATUS) ; GET STATUS + AND KBD_KEYRDY ; ISOLATE READY BIT + JR NZ,KBD_STAT3 ; KEY READY, DONE + +KBD_STAT2: + CALL Z,KBD_DECODE ; NOT READY, RUN THE DECODING ENGINE + LD A,(KBD_STATUS) ; GET STATUS + AND KBD_KEYRDY ; ISOLATE READ BIT + +KBD_STAT3: + RLCA ; ROTATE READY BIT TO LOW ORDER BIT + RET +; +;__________________________________________________________________________________________________ +; KEYBOARD READ +; +; RETURNS ASCII VALUE IN E. SEE END OF FILE FOR VALUES RETURNED FOR SPECIAL KEYS +; LIKE PGUP, ARROWS, FUNCTION KEYS, ETC. +;__________________________________________________________________________________________________ +; +KBD_READ: +; CALL KBD_STAT ; CHECK TO SEE IF KEY READY + LD A,(KBD_STATUS) ; GET STATUS + AND KBD_KEYRDY ; ISOLIATE KEY READY BIT + JR NZ,KBD_READ1 ; READY, GO GET THE KEY AND RETURN + CALL KBD_DECODE ; TRY TO GET A KEY + JR KBD_READ ; AND LOOP +; +KBD_READ1: + LD A,(KBD_KEYCODE) ; GET KEYCODE + LD E,A ; SAVE IT IN E + LD A,(KBD_STATE) ; GET STATE FLAGS + LD D,A ; SAVE THEM IN D + XOR A ; SIGNAL SUCCESS + LD (KBD_STATUS),A ; CLEAR STATUS TO INDICATE BYTE RECEIVED + RET +; +;__________________________________________________________________________________________________ +; KEYBOARD FLUSH +;__________________________________________________________________________________________________ +; +KBD_FLUSH: + XOR A ; A = 0 + LD (KBD_STATUS),A ; CLEAR STATUS + RET +; +;__________________________________________________________________________________________________ +; HARDWARE INTERFACE +;__________________________________________________________________________________________________ +; +;__________________________________________________________________________________________________ +KBD_IST: +; +; KEYBOARD INPUT STATUS +; A=0, Z SET FOR NOTHING PENDING, OTHERWISE DATA PENDING +; + IN A,(KBD_ST) ; GET STATUS + AND $01 ; ISOLATE INPUT PENDING BIT + RET +; +;__________________________________________________________________________________________________ +KBD_OST: +; +; KEYBOARD OUTPUT STATUS +; A=0, Z SET FOR NOT READY, OTHERWISE READY TO WRITE +; + IN A,(KBD_ST) ; GET STATUS + AND $02 ; ISOLATE OUTPUT EMPTY BIT + XOR $02 ; FLIP IT FOR APPROPRIATE RETURN VALUES + RET +; +;__________________________________________________________________________________________________ +KBD_PUTCMD: +; +; PUT A CMD BYTE FROM A TO THE KEYBOARD INTERFACE WITH TIMEOUT +; + LD E,A ; SAVE INCOMING VALUE IN E + LD B,KBD_WAITTO ; SETUP TO LOOP +KBD_PUTCMD0: + CALL KBD_OST ; GET OUTPUT REGISTER STATUS + JR NZ,KBD_PUTCMD1 ; EMPTY, GO TO WRITE + CALL DELAY ; WAIT A BIT + DJNZ KBD_PUTCMD0 ; LOOP UNTIL COUNTER EXHAUSTED + RET +KBD_PUTCMD1: + LD A,E ; RECOVER VALUE TO WRITE +#IFDEF KBD_DEBUG + CALL PC_SPACE + CALL PC_GT + CALL PC_GT + CALL PRTHEXBYTE +#ENDIF + OUT (KBD_CMD),A ; WRITE IT + XOR A ; SIGNAL SUCCESS + RET +; +;__________________________________________________________________________________________________ +KBD_PUTDATA: +; +; PUT A DATA BYTE FROM A TO THE KEYBOARD INTERFACE WITH TIMEOUT +; + LD E,A ; SAVE INCOMING VALUE IN E + LD B,KBD_WAITTO ; SETUP TO LOOP +KBD_PUTDATA0: + CALL KBD_OST ; GET OUTPUT REGISTER STATUS + JR NZ,KBD_PUTDATA1 ; EMPTY, GO TO WRITE + CALL DELAY ; WAIT A BIT + DJNZ KBD_PUTDATA0 ; LOOP UNTIL COUNTER EXHAUSTED + RET +KBD_PUTDATA1: + LD A,E ; RECOVER VALUE TO WRITE +#IFDEF KBD_DEBUG + CALL PC_SPACE + CALL PC_GT + CALL PRTHEXBYTE +#ENDIF + OUT (KBD_DATA),A ; WRITE IT + XOR A ; SIGNAL SUCCESS + RET +; +;__________________________________________________________________________________________________ +KBD_GETDATA: +; +; GET A RAW DATA BYTE FROM KEYBOARD INTERFACE INTO A WITH TIMEOUT +; + LD B,KBD_WAITTO ; SETUP TO LOOP +KBD_GETDATA0: + CALL KBD_IST ; GET INPUT REGISTER STATUS + JR NZ,KBD_GETDATA1 ; BYTE PENDING, GO GET IT + CALL DELAY ; WAIT A BIT + DJNZ KBD_GETDATA0 ; LOOP UNTIL COUNTER EXHAUSTED + XOR A ; NO DATA, RETURN ZERO + RET +KBD_GETDATA1: + IN A,(KBD_DATA) ; GET THE DATA VALUE +#IFDEF KBD_DEBUG + PUSH AF + CALL PC_SPACE + CALL PC_LT + CALL PRTHEXBYTE + POP AF +#ENDIF + OR A ; SET FLAGS + RET +; +;__________________________________________________________________________________________________ +; RESET KEYBOARD +;__________________________________________________________________________________________________ +; +KBD_RESET: + LD A,$FF ; RESET COMMAND + CALL KBD_PUTDATA ; SEND IT + CALL KBD_GETDATA ; GET THE ACK + LD B,0 ; SETUP LOOP COUNTER +PPK_RESET0: + PUSH BC ; PRESERVE COUNTER + CALL KBD_GETDATA ; TRY TO GET THE RESPONSE + POP BC ; RECOVER COUNTER + JR NZ,PPK_RESET1 ; GOT A BYTE? IF SO, GET OUT OF LOOP + DJNZ PPK_RESET0 ; LOOP TILL COUNTER EXHAUSTED +PPK_RESET1: + LD A,B + XOR A ; SIGNAL SUCCESS (RESPONSE IS IGNORED...) + RET ; DONE +; +;__________________________________________________________________________________________________ +; UPDATE KEYBOARD LEDS BASED ON CURRENT TOGGLE FLAGS +;__________________________________________________________________________________________________ +; +KBD_SETLEDS: + LD A,$ED ; SET/RESET LED'S COMMAND + CALL KBD_PUTDATA ; SEND THE COMMAND + CALL KBD_GETDATA ; READ THE RESPONSE + CP $FA ; MAKE SURE WE GET ACK + RET NZ ; ABORT IF NO ACK + LD A,(KBD_STATE) ; LOAD THE STATE BYTE + RRCA ; ROTATE TOGGLE KEY BITS AS NEEDED + RRCA + RRCA + RRCA + AND $07 ; CLEAR THE IRRELEVANT BITS + CALL KBD_PUTDATA ; SEND THE LED DATA + CALL KBD_GETDATA ; READ THE ACK + JP KBD_DECNEW ; RESTART DECODER FOR A NEW KEY + RET ; DONE +; +;__________________________________________________________________________________________________ +; UPDATE KEYBOARD REPEAT RATE BASED ON CURRENT SETTING +;__________________________________________________________________________________________________ +; +KBD_SETRPT: + LD A,$F3 ; COMMAND = SET TYPEMATIC RATE/DELAY + CALL KBD_PUTDATA ; SEND IT + CALL KBD_GETDATA ; GET THE ACK + CP $FA ; MAKE SURE WE GET ACK + RET NZ ; ABORT IF NO ACK + LD A,(KBD_REPEAT) ; LOAD THE CURRENT RATE/DELAY BYTE + CALL KBD_PUTDATA ; SEND IT + CALL KBD_GETDATA ; GET THE ACK + RET +; +;__________________________________________________________________________________________________ +; DECODING ENGINE +;__________________________________________________________________________________________________ +; +;__________________________________________________________________________________________________ +KBD_DECODE: +; +; RUN THE DECODING ENGINE UNTIL EITHER: 1) NO MORE SCANCODES ARE AVAILABLE +; FROM THE KEYBOARD, OR 2) A DECODED KEY VALUE IS AVAILABLE +; +; RETURNS A=0 AND Z SET IF NO KEYCODE READY, OTHERWISE A DECODED KEY VALUE IS AVAILABLE. +; THE DECODED KEY VALUE AND KEY STATE IS STORED IN KBD_KEYCODE AND KBD_STATE. +; +; KBD_STATUS IS NOT CLEARED AT START. IT IS THE CALLER'S RESPONSIBILITY +; TO CLEAR KBD_STATUS WHEN IT HAS RETRIEVED A PENDING VALUE. IF DECODE IS CALLED +; WITH A KEYCODE STILL PENDING, IT WILL JUST RETURN WITHOUT DOING ANYTHING. +; +; Step 0: Check keycode buffer +; if status[keyrdy] +; return +; +; Step 1: Get scancode +; if no scancode ready +; return +; read scancode +; +; Step 2: Detect and handle special keycodes +; if scancode == $AA +; *** handle hot insert somehow *** +; +; Step 3: Detect and handle scancode prefixes +; if scancode == $E0 +; set status[extended] +; goto Step 1 +; +; if scancode == $E1 +; *** handle pause key somehow *** +; +; Step 4: Detect and flag break event +; *** scancode set #1 variation *** +; set status[break] = high bit of scancode +; clear high order bit +; continue to Step 5 +; *** scancode set #2 variation *** +; if scancode == $F0 +; set status[break] +; goto Step 1 +; +; Step 5: Map scancode to keycode +; if status[extended] +; apply extended-map[scancode] -> keycode +; else if state[shifted] +; apply shifted-map[scancode] -> keycode +; else +; apply normal-map[scancode] -> keycode +; +; Step 6: Handle modifier keys +; if keycode is modifier (shift, ctrl, alt, win) +; set (l/r)state[] = not status[break] +; clear modifier bits in state +; set state = (lstate OR rstate OR state) +; goto New Key +; +; Step 7: Complete procesing of key break events +; if status[break] +; goto New Key +; +; Step 8: Handle toggle keys +; if keycode is toggle (capslock, numlock, scrolllock) +; invert (XOR) state[] +; update keyboard LED's +; goto New Key +; +; Step 9: Adjust keycode for control modifier +; if state[ctrl] +; if keycode is 'a'-'z' +; subtract 20 (clear bit 5) from keycode +; if keycode is '@'-'_' +; subtract 40 (clear bit 6) from keycode +; +; Step 10: Adjust keycode for caps lock +; if state[capslock] +; if keycode is 'a'-'z' OR 'A'-'Z' +; toggle (XOR) bit 5 of keycode +; +; Step 11: Handle num pad keys +; clear state[numpad] +; if keycode is numpad +; set state[numpad] +; if state[numlock] +; toggle (XOR) bit 4 of keycode +; apply numpad-map[keycode] -> keycode +; +; Step 12: Detect unknown/invalid keycodes +; if keycode == $FF +; goto New Key +; +; Step 13: Done +; set status[keyrdy] +; return +; +; New Key: +; clear status +; goto Step 1 +; +KBD_DEC0: ; CHECK KEYCODE BUFFER + LD A,(KBD_STATUS) ; GET CURRENT STATUS + AND KBD_KEYRDY ; ISOLATE KEY READY FLAG + RET NZ ; ABORT IF KEY IS ALREADY PENDING + +KBD_DEC1: ; PROCESS NEXT SCANCODE + CALL KBD_IST ; IS A SCANCODE WAITING? + RET Z ; NOPE, ABORT + CALL KBD_GETDATA ; GET THE SCANCODE + RET Z ; TIMEOUT, RETURN WITH A=0, Z SET + LD (KBD_SCANCODE),A ; SAVE SCANCODE + +KBD_DEC2: ; DETECT AND HANDLE SPECIAL KEYCODES + LD A,(KBD_SCANCODE) ; GET THE CURRENT SCANCODE + CP $AA ; KEYBOARD INSERTION? + JR NZ,KBD_DEC3 ; NOPE, BYPASS + CALL KBD_RESET ; RESET KEYBOARD + CALL KBD_SETLEDS ; SET LEDS + CALL KBD_SETRPT ; SET REPEAT RATE + JP KBD_DECNEW ; RESTART THE ENGINE + +KBD_DEC3: ; DETECT AND HANDLE SCANCODE PREFIXES + LD A,(KBD_SCANCODE) ; GET THE CURRENT SCANCODE + +KBD_DEC3A: ; HANDLE SCANCODE PREFIX $E0 (EXTENDED SCANCODE FOLLOWS) + CP $E0 ; EXTENDED KEY PREFIX $E0? + JR NZ,KBD_DEC3B ; NOPE MOVE ON + LD A,(KBD_STATUS) ; GET STATUS + OR KBD_EXT ; SET EXTENDED BIT + LD (KBD_STATUS),A ; SAVE STATUS + JR KBD_DEC1 ; LOOP TO DO NEXT SCANCODE + +KBD_DEC3B: ; HANDLE SCANCODE PREFIX $E1 (PAUSE KEY) + CP $E1 ; EXTENDED KEY PREFIX $E1 + JR NZ,KBD_DEC4 ; NOPE MOVE ON + LD A,$EE ; MAP TO KEYCODE $EE + LD (KBD_KEYCODE),A ; SAVE IT + ; SWALLOW NEXT 5 SCANCODES + LD B,5 ; LOOP 5 TIMES +KBD_DEC3B1: + PUSH BC + CALL KBD_GETDATA ; RETRIEVE NEXT SCANCODE + POP BC + DJNZ KBD_DEC3B1 ; LOOP AS NEEDED + JP KBD_DEC6 ; RESUME AFTER MAPPING + +KBD_DEC4: ; DETECT AND FLAG BREAK EVENT + LD A,(KBD_SCANCODE) + LD E,A ; SAVE SCANCODE IN E + RES 7,A ; CLEAR THE BREAK BIT FROM THE KEYCODE + LD (KBD_SCANCODE),A ; SAVE UPDATED SCANCODE + LD A,E ; RECOVER ORIGINAL SCANCODE + AND $80 ; ISOLATE BREAK BIT + RLCA ; ROTATE BIT TO POSITION... + RLCA ; OF BREAK BIT IN STATUS + LD E,A ; SAVE IT IN E + LD A,(KBD_STATUS) ; GET THE STATUS FLAGS + AND ~KBD_BREAK ; CLEAR THE BREAK BIT + OR E ; OR IN NEW BREAK BIT + LD (KBD_STATUS),A ; SAVE IT + +KBD_DEC5: ; MAP SCANCODE TO KEYCODE + LD A,(KBD_STATUS) ; GET STATUS + AND KBD_EXT ; EXTENDED BIT SET? + JR Z,KBD_DEC5C ; NOPE, MOVE ON + + ; PERFORM EXTENDED KEY MAPPING + LD A,(KBD_SCANCODE) ; GET SCANCODE + LD E,A ; STASH IT IN E + LD HL,KBD_MAPEXT ; POINT TO START OF EXT MAP TABLE +KBD_DEC5A: + LD A,(HL) ; GET FIRST BYTE OF PAIR + CP $00 ; END OF TABLE? + JP Z,KBD_DECNEW ; UNKNOWN OR BOGUS, START OVER + INC HL ; INC HL FOR FUTURE + CP E ; DOES MATCH BYTE EQUAL SCANCODE? + JR Z,KBD_DEC5B ; YES! JUMP OUT + INC HL ; BUMP TO START OF NEXT PAIR + JR KBD_DEC5A ; LOOP TO CHECK NEXT TABLE ENTRY +KBD_DEC5B: + LD A,(HL) ; GET THE KEYCODE VIA MAPPING TABLE + LD (KBD_KEYCODE),A ; SAVE IT + JR KBD_DEC6 + +KBD_DEC5C: ; PERFORM REGULAR KEY (NOT EXTENDED) KEY MAPPING + LD A,(KBD_SCANCODE) ; GET THE SCANCODE + CP KBD_MAPSIZ ; COMPARE TO SIZE OF TABLE + JR NC,KBD_DEC6 ; PAST END, SKIP OVER LOOKUP + + ; SETUP POINTER TO MAPPING TABLE BASED ON SHIFTED OR UNSHIFTED STATE + LD A,(KBD_STATE) ; GET STATE + AND KBD_SHIFT ; SHIFT ACTIVE? + LD HL,KBD_MAPSTD ; LOAD ADDRESS OF NON-SHIFTED MAPPING TABLE + JR Z,KBD_DEC5D ; NON-SHIFTED, MOVE ON + LD HL,KBD_MAPSHIFT ; LOAD ADDRESS OF SHIFTED MAPPING TABLE +KBD_DEC5D: + LD A,(KBD_SCANCODE) ; GET THE SCANCODE + LD E,A ; SCANCODE TO E FOR TABLE OFFSET + LD D,0 ; D -> 0 + ADD HL,DE ; COMMIT THE TABLE OFFSET TO HL + LD A,(HL) ; GET THE KEYCODE VIA MAPPING TABLE + LD (KBD_KEYCODE),A ; SAVE IT + +KBD_DEC6: ; HANDLE MODIFIER KEYS + LD A,(KBD_KEYCODE) ; MAKE SURE WE HAVE KEYCODE + CP $B8 ; END OF MODIFIER KEYS + JR NC,KBD_DEC7 ; BYPASS MODIFIER KEY CHECKING + CP $B0 ; START OF MODIFIER KEYS + JR C,KBD_DEC7 ; BYPASS MODIFIER KEY CHECKING + + LD B,4 ; LOOP COUNTER TO LOOP THRU 4 MODIFIER BITS + LD E,$80 ; SETUP E TO ROATE THROUGH MODIFIER STATE BITS + SUB $B0 - 1 ; SETUP A TO DECREMENT THROUGH MODIFIER VALUES + +KBD_DEC6A: + RLC E ; SHIFT TO NEXT MODIFIER STATE BIT + DEC A ; L-MODIFIER? + JR Z,KBD_DEC6B ; YES, HANDLE L-MODIFIER MAKE/BREAK + DEC A ; R-MODIFIER? + JR Z,KBD_DEC6C ; YES, HANDLE R-MODIFIER MAKE/BREAK + DJNZ KBD_DEC6A ; LOOP THRU 4 MODIFIER BITS + JR KBD_DEC7 ; FAILSAFE, SHOULD NEVER GET HERE! + +KBD_DEC6B: ; LEFT STATE KEY MAKE/BREAK (STATE BIT TO SET/CLEAR IN E) + LD HL,KBD_LSTATE ; POINT TO LEFT STATE BYTE + JR KBD_DEC6D ; CONTINUE + +KBD_DEC6C: ; RIGHT STATE KEY MAKE/BREAK (STATE BIT TO SET/CLEAR IN E) + LD HL,KBD_RSTATE ; POINT TO RIGHT STATE BYTE + JR KBD_DEC6D ; CONTINUE + +KBD_DEC6D: ; BRANCH BASED ON WHETHER THIS IS A MAKE OR BREAK EVENT + LD A,(KBD_STATUS) ; GET STATUS FLAGS + AND KBD_BREAK ; BREAK EVENT? + JR Z,KBD_DEC6E ; NO, HANDLE A MODIFIER KEY MAKE EVENT + JR KBD_DEC6F ; YES, HANDLE A MODIFIER BREAK EVENT + +KBD_DEC6E: ; HANDLE STATE KEY MAKE EVENT + LD A,E ; GET THE BIT TO SET + OR (HL) ; OR IN THE CURRENT BITS + LD (HL),A ; SAVE THE RESULT + JR KBD_DEC6G ; CONTINUE + +KBD_DEC6F: ; HANDLE STATE KEY BREAK EVENT + LD A,E ; GET THE BIT TO CLEAR + XOR $FF ; FLIP ALL BITS TO SETUP FOR A CLEAR OPERATION + AND (HL) ; AND IN THE FLIPPED BITS TO CLEAR DESIRED BIT + LD (HL),A ; SAVE THE RESULT + JR KBD_DEC6G ; CONTINUE + +KBD_DEC6G: ; COALESCE L/R STATE FLAGS + LD A,(KBD_STATE) ; GET EXISTING STATE BITS + AND $F0 ; GET RID OF OLD MODIFIER BITS + LD DE,(KBD_LSTATE) ; LOAD BOTH L/R STATE BYTES IN D/E + OR E ; MERGE IN LEFT STATE BITS + OR D ; MERGE IN RIGHT STATE BITS + LD (KBD_STATE),A ; SAVE IT + JP KBD_DECNEW ; DONE WITH CURRENT KEYSTROKE + +KBD_DEC7: ; COMPLETE PROCESSING OF EXTENDED AND KEY BREAK EVENTS + LD A,(KBD_STATUS) ; GET CURRENT STATUS FLAGS + AND KBD_BREAK ; IS THIS A KEY BREAK EVENT? + JP NZ,KBD_DECNEW ; PROCESS NEXT KEY + +KBD_DEC8: ; HANDLE TOGGLE KEYS + LD A,(KBD_KEYCODE) ; GET THE CURRENT KEYCODE INTO A + LD E,KBD_CAPSLCK ; SETUP E WITH CAPS LOCK STATE BIT + CP $BC ; IS THIS THE CAPS LOCK KEY? + JR Z,KBD_DEC8A ; YES, GO TO BIT SET ROUTINE + LD E,KBD_NUMLCK ; SETUP E WITH NUM LOCK STATE BIT + CP $BD ; IS THIS THE NUM LOCK KEY? + JR Z,KBD_DEC8A ; YES, GO TO BIT SET ROUTINE + LD E,KBD_SCRLCK ; SETUP E WITH SCROLL LOCK STATE BIT + CP $BE ; IS THIS THE SCROLL LOCK KEY? + JR Z,KBD_DEC8A ; YES, GO TO BIT SET ROUTINE + JR KBD_DEC9 ; NOT A TOGGLE KEY, CONTINUE + +KBD_DEC8A: ; RECORD THE TOGGLE + LD A,(KBD_STATE) ; GET THE CURRENT STATE FLAGS + XOR E ; SET THE TOGGLE KEY BIT FROM ABOVE + LD (KBD_STATE),A ; SAVE IT + CALL KBD_SETLEDS ; UPDATE LED LIGHTS ON KBD + JP KBD_DECNEW ; RESTART DECODER FOR A NEW KEY + +KBD_DEC9: ; ADJUST KEYCODE FOR CONTROL MODIFIER + LD A,(KBD_STATE) ; GET THE CURRENT STATE BITS + AND KBD_CTRL ; CHECK THE CONTROL BIT + JR Z,KBD_DEC10 ; CONTROL KEY NOT PRESSED, MOVE ON + LD A,(KBD_KEYCODE) ; GET CURRENT KEYCODE IN A + CP 'a' ; COMPARE TO LOWERCASE A + JR C,KBD_DEC9A ; BELOW IT, BYPASS + CP 'z' + 1 ; COMPARE TO LOWERCASE Z + JR NC,KBD_DEC9A ; ABOVE IT, BYPASS + RES 5,A ; KEYCODE IN LOWERCASE A-Z RANGE CLEAR BIT 5 TO MAKE IT UPPERCASE +KBD_DEC9A: + CP '@' ; COMPARE TO @ + JR C,KBD_DEC10 ; BELOW IT, BYPASS + CP '_' + 1 ; COMPARE TO _ + JR NC,KBD_DEC10 ; ABOVE IT, BYPASS + RES 6,A ; CONVERT TO CONTROL VALUE BY CLEARING BIT 6 + LD (KBD_KEYCODE),A ; UPDATE KEYCODE TO CONTROL VALUE + +KBD_DEC10: ; ADJUST KEYCODE FOR CAPS LOCK + LD A,(KBD_STATE) ; LOAD THE STATE FLAGS + AND KBD_CAPSLCK ; CHECK CAPS LOCK + JR Z,KBD_DEC11 ; CAPS LOCK NOT ACTIVE, MOVE ON + LD A,(KBD_KEYCODE) ; GET THE CURRENT KEYCODE VALUE + CP 'a' ; COMPARE TO LOWERCASE A + JR C,KBD_DEC10A ; BELOW IT, BYPASS + CP 'z' + 1 ; COMPARE TO LOWERCASE Z + JR NC,KBD_DEC10A ; ABOVE IT, BYPASS + JR KBD_DEC10B ; IN RANGE LOWERCASE A-Z, GO TO CASE SWAPPING LOGIC +KBD_DEC10A: + CP 'A' ; COMPARE TO UPPERCASE A + JR C,KBD_DEC11 ; BELOW IT, BYPASS + CP 'Z' + 1 ; COMPARE TO UPPERCASE Z + JR NC,KBD_DEC11 ; ABOVE IT, BYPASS + JR KBD_DEC10B ; IN RANGE UPPERCASE A-Z, GO TO CASE SWAPPING LOGIC +KBD_DEC10B: + LD A,(KBD_KEYCODE) ; GET THE CURRENT KEYCODE + XOR $20 ; FLIP BIT 5 TO SWAP UPPER/LOWER CASE + LD (KBD_KEYCODE),A ; SAVE IT + +KBD_DEC11: ; HANDLE NUM PAD KEYS + LD A,(KBD_STATE) ; GET THE CURRENT STATE FLAGS + AND ~KBD_NUMPAD ; ASSUME NOT A NUMPAD KEY, CLEAR THE NUMPAD BIT + LD (KBD_STATE),A ; SAVE IT + + LD A,(KBD_KEYCODE) ; GET THE CURRENT KEYCODE + AND 11100000B ; ISOLATE TOP 3 BITS + CP 11000000B ; IS IN NUMPAD RANGE? + JR NZ,KBD_DEC12 ; NOPE, GET OUT + + LD A,(KBD_STATE) ; LOAD THE CURRENT STATE FLAGS + OR KBD_NUMPAD ; TURN ON THE NUMPAD BIT + LD (KBD_STATE),A ; SAVE IT + + AND KBD_NUMLCK ; IS NUM LOCK BIT SET? + JR Z,KBD_DEC11A ; NO, SKIP NUMLOCK PROCESSING + LD A,(KBD_KEYCODE) ; GET THE KEYCODE + XOR $10 ; FLIP VALUES FOR NUMLOCK + LD (KBD_KEYCODE),A ; SAVE IT + +KBD_DEC11A: ; APPLY NUMPAD MAPPING + LD A,(KBD_KEYCODE) ; GET THE CURRENT KEYCODE + LD HL,KBD_MAPNUMPAD ; LOAD THE START OF THE MAPPING TABLE + SUB $C0 ; KEYCODES START AT $C0 + LD E,A ; INDEX TO E + LD D,0 ; D IS ZERO + ADD HL,DE ; POINT TO RESULT OF MAPPING + LD A,(HL) ; GET IT IN A + LD (KBD_KEYCODE),A ; SAVE IT + +KBD_DEC12: ; DETECT UNKNOWN/INVALID KEYCODES + LD A,(KBD_KEYCODE) ; GET THE FINAL KEYCODE + CP $FF ; IS IT $FF (UNKNOWN/INVALID) + JP Z,KBD_DECNEW ; IF SO, JUST RESTART THE ENGINE + +KBD_DEC13: ; DONE - RECORD RESULTS + LD A,(KBD_STATUS) ; GET CURRENT STATUS + OR KBD_KEYRDY ; SET KEY READY BIT + LD (KBD_STATUS),A ; SAVE IT + XOR A ; A=0 + INC A ; SIGNAL SUCCESS WITH A=1 + RET + +KBD_DECNEW: ; START NEW KEYPRESS (CLEAR ALL STATUS BITS) + XOR A ; A = 0 + LD (KBD_STATUS),A ; CLEAR STATUS + JP KBD_DEC1 ; RESTART THE ENGINE +; +;__________________________________________________________________________________________________ +; MAPPING TABLES +;__________________________________________________________________________________________________ +; +KBD_MAPSTD: ; SCANCODE IS INDEX INTO TABLE TO RESULTANT LOOKUP KEYCODE + .DB $FF,$1B,'1','2','3','4','5','6','7','8','9','0','-','=',$08,$09 + .DB 'q','w','e','r','t','y','u','i','o','p','[',']',$0D,$B2,'a','s' + .DB 'd','f','g','h','j','k','l',';',$27,'`',$B0,'\','z','x','c','v' + .DB 'b','n','m',',','.','/',$B1,$CC,$B4,' ',$BC,$E0,$E1,$E2,$E3,$E4 + .DB $E5,$E6,$E7,$E8,$E9,$BD,$BE,$C6,$C7,$C8,$CD,$C3,$C4,$C5,$CE,$C0 + .DB $C1,$C2,$C9,$CA,$EC,$FF,$FF,$FA,$EB +; +KBD_MAPSIZ .EQU ($ - KBD_MAPSTD) +; +KBD_MAPSHIFT: ; SCANCODE IS INDEX INTO TABLE TO RESULTANT LOOKUP KEYCODE WHEN SHIFT ACTIVE + .DB $FF,$1B,'!','@','#','$','%','^','&','*','(',')','_','+',$08,$09 + .DB 'Q','W','E','R','T','Y','U','I','O','P','{','}',$0D,$B2,'A','S' + .DB 'D','F','G','H','J','K','L',':',$22,'~',$B0,'|','Z','X','C','V' + .DB 'B','N','M','<','>','?',$B1,$DC,$B4,' ',$BC,$E0,$E1,$E2,$E3,$E4 + .DB $E5,$E6,$E7,$E8,$E9,$BD,$BE,$D6,$D7,$D8,$DD,$D3,$D4,$D5,$DE,$D0 + .DB $D1,$D2,$D9,$DA,$EC,$FF,$FF,$FA,$EB +; +KBD_MAPEXT: ; PAIRS ARE [SCANCODE,KEYCODE] FOR EXTENDED SCANCODES + .DB $38,$B5, $1D,$B3, $5B,$B6, $5C,$B7 + .DB $5D,$EF, $5E,$FA, $5F,$FB, $35,$CB + .DB $1C,$CF, $63,$FC, $4F,$F3, $4B,$F8 + .DB $47,$F2, $52,$F0, $53,$F1, $50,$F7 + .DB $4D,$F9, $48,$F6, $51,$F5, $37,$ED + .DB $49,$F4, $46,$FD, $00,$00 +; +KBD_MAPNUMPAD: ; KEYCODE TRANSLATION FROM NUMPAD RANGE TO STD ASCII/KEYCODES + .DB $F3,$F7,$F5,$F8,$FF,$F9,$F2,$F6,$F4,$F0,$F1,$2F,$2A,$2D,$2B,$0D + .DB $31,$32,$33,$34,$35,$36,$37,$38,$39,$30,$2E,$2F,$2A,$2D,$2B,$0D +; +;__________________________________________________________________________________________________ +; KEYCODE VALUES RETURNED BY THE DECODER +;__________________________________________________________________________________________________ +; +; VALUES 0-127 ARE STANDARD ASCII, SPECIAL KEYS WILL HAVE THE FOLLOWING VALUES: +; +; F1 $E0 +; F2 $E1 +; F3 $E2 +; F4 $E3 +; F5 $E4 +; F6 $E5 +; F7 $E6 +; F8 $E7 +; F9 $E8 +; F10 $E9 +; F11 $EA +; F12 $EB +; SYSRQ $EC +; PRTSC $ED +; PAUSE $EE +; APP $EF +; INS $F0 +; DEL $F1 +; HOME $F2 +; END $F3 +; PGUP $F4 +; PGDN $F5 +; UP $F6 +; DOWN $F7 +; LEFT $F8 +; RIGHT $F9 +; POWER $FA +; SLEEP $FB +; WAKE $FC +; BREAK $FD diff --git a/Source/n8v.asm b/Source/n8v.asm index eea0e951..f81e964a 100644 --- a/Source/n8v.asm +++ b/Source/n8v.asm @@ -386,11 +386,6 @@ RECOVER: POP BC RET -;__________________________________________________________________________________________________ -; IMBED COMMON PRALLEL PORT KEYBOARD DRIVER -;__________________________________________________________________________________________________ -; -#INCLUDE "ppk.asm" ; ;__________________________________________________________________________________________________ ; LOCAL DRIVER DATA diff --git a/Source/ppk.asm b/Source/ppk.asm index fc491b77..6fd6fa2f 100644 --- a/Source/ppk.asm +++ b/Source/ppk.asm @@ -7,10 +7,13 @@ ; ROMWBW ADAPTATION BY WAYNE WARTHEN ;__________________________________________________________________________________________________ ; +; TODO: ;__________________________________________________________________________________________________ ; DATA CONSTANTS ;__________________________________________________________________________________________________ ; +; IO PORTS +; #IF (PLATFORM == PLT_N8) PPK_PPI .EQU 084H ; PPI PORT BASE FOR N8 #ELSE @@ -28,13 +31,55 @@ PPK_CLK .EQU 01111010B ; PPIX MASK TO MANAGE CLOCK LINE (C:5) PPK_WAITTO .EQU 50 * CPUFREQ ; TUNE!!! WANT SMALL AS POSSIBLE W/O ERRORS PPK_WAITRDY .EQU 10 * CPUFREQ ; TUNE!!! 100US LOOP DELAY TO ENSURE DEVICE READY ; +; STATUS BITS (FOR PPK_STATUS) +; +PPK_EXT .EQU 01H ; BIT 0, EXTENDED SCANCODE ACTIVE +PPK_BREAK .EQU 02H ; BIT 1, THIS IS A KEY UP (BREAK) EVENT +PPK_KEYRDY .EQU 80H ; BIT 7, INDICATES A DECODED KEYCODE IS READY +; +; STATE BITS (FOR PPK_STATE, PPK_LSTATE, PPK_RSTATE) +; +PPK_SHIFT .EQU 01H ; BIT 0, SHIFT ACTIVE (PRESSED) +PPK_CTRL .EQU 02H ; BIT 1, CONTROL ACTIVE (PRESSED) +PPK_ALT .EQU 04H ; BIT 2, ALT ACTIVE (PRESSED) +PPK_WIN .EQU 08H ; BIT 3, WIN ACTIVE (PRESSED) +PPK_SCRLCK .EQU 10H ; BIT 4, CAPS LOCK ACTIVE (TOGGLED ON) +PPK_NUMLCK .EQU 20H ; BIT 5, NUM LOCK ACTIVE (TOGGLED ON) +PPK_CAPSLCK .EQU 40H ; BIT 6, SCROLL LOCK ACTIVE (TOGGLED ON) +PPK_NUMPAD .EQU 80H ; BIT 7, NUM PAD KEY (KEY PRESSED IS ON NUM PAD) +; +PPK_DEFRPT .EQU $40 ; DEFAULT REPEAT RATE (.5 SEC DELAY, 30CPS) +PPK_DEFSTATE .EQU PPK_NUMLCK ; DEFAULT STATE (NUM LOCK ON) +; +;__________________________________________________________________________________________________ +; DATA +;__________________________________________________________________________________________________ +; +PPK_SCANCODE .DB 0 ; RAW SCANCODE +PPK_KEYCODE .DB 0 ; RESULTANT KEYCODE AFTER DECODING +PPK_STATE .DB 0 ; STATE BITS (SEE ABOVE) +PPK_LSTATE .DB 0 ; STATE BITS FOR "LEFT" KEYS +PPK_RSTATE .DB 0 ; STATE BITS FOR "RIGHT" KEYS +PPK_STATUS .DB 0 ; CURRENT STATUS BITS (SEE ABOVE) +PPK_REPEAT .DB 0 ; CURRENT REPEAT RATE +PPK_IDLE .DB 0 ; IDLE COUNT +; ;__________________________________________________________________________________________________ ; KEYBOARD INITIALIZATION ;__________________________________________________________________________________________________ ; PPK_INIT: CALL PPK_INITPORT ; SETS PORT C SO CAN INPUT AND OUTPUT - CALL PPK_RESET ; RESET TO THE KEYBOARD + + LD A,PPK_DEFRPT ; GET DEFAULT REPEAT RATE + LD (PPK_REPEAT),A ; SAVE IT + LD A,PPK_DEFSTATE ; GET DEFAULT STATE + LD (PPK_STATE),A ; SAVE IT + + CALL PPK_RESET ; RESET THE KEYBOARD + CALL PPK_SETLEDS ; UPDATE LEDS BASED ON CURRENT TOGGLE STATE BITS + CALL PPK_SETRPT ; UPDATE REPEAT RATE BASED ON CURRENT SETTING + XOR A ; SIGNAL SUCCESS RET ; @@ -48,7 +93,7 @@ PPK_INIT: ; PPK_STAT: LD A,(PPK_STATUS) ; GET STATUS - AND PPK_KEYRDY ; ISOLATE READ BIT + AND PPK_KEYRDY ; ISOLATE READY BIT JR NZ,PPK_STAT3 ; KEY READY, DONE PPK_STAT1: @@ -89,7 +134,7 @@ PPK_READ1: LD A,(PPK_STATE) ; GET STATE FLAGS LD D,A ; SAVE THEM IN D XOR A ; SIGNAL SUCCESS - LD (PPK_STATUS),A ; CLEAR STATE TO INDICATE BYTE RECEIVED + LD (PPK_STATUS),A ; CLEAR STATUS TO INDICATE BYTE RECEIVED RET ; ;__________________________________________________________________________________________________ @@ -98,7 +143,7 @@ PPK_READ1: ; PPK_FLUSH: XOR A ; A = 0 - LD (PPK_STATE),A ; CLEAR STATE + LD (PPK_STATUS),A ; CLEAR STATUS RET ; ;__________________________________________________________________________________________________ @@ -106,26 +151,24 @@ PPK_FLUSH: ;__________________________________________________________________________________________________ ; ;__________________________________________________________________________________________________ -PPK_GETBYTE: +PPK_GETDATA: ; ; GET RAW BYTE FROM KEYBOARD INTERFACE INTO A ; IF TIMEOUT, RETURN WITH A=0 AND Z SET -; -; ALL REGISTERS ARE DESTROYED ; CALL PPK_CLKHI ; ALLOW KEYBOARD TO XMIT CALL PPK_WTCLKLO ; WAIT FOR CLOCK LINE TO GO LOW - JP NZ,PPK_GETBYTE1 ; IF IT WENT LOW, READ THE BYTE + JP NZ,PPK_GETDATA1 ; IF IT WENT LOW, READ THE BYTE CALL PPK_CLKLO ; SUPPRESS KEYBOARD XMIT XOR A ; SIGNAL TIMEOUT RET -PPK_GETBYTE1: +PPK_GETDATA1: CALL PPK_WTCLKHI ; WAIT FOR END OF START BIT LD B,8 ; SAMPLE 8 TIMES LD E,0 ; START WITH E=0 -PPK_GETBYTE2: +PPK_GETDATA2: CALL PPK_WTCLKLO ; WAIT TILL CLOCK GOES LOW IN A,(PPK_PPIB) ; SAMPLE THE DATA LINE RRA ; MOVE THE DATA BIT INTO THE CARRY REGISTER @@ -133,7 +176,7 @@ PPK_GETBYTE2: RRA ; MOVE THE CARRY BIT INTO BIT 7 AND SHIFT RIGHT LD E,A ; STORE IT BACK AFTER 8 CYCLES 1ST BIT READ WILL BE IN B0 CALL PPK_WTCLKHI ; WAIT TILL GOES HIGH - DJNZ PPK_GETBYTE2 ; DO THIS 8 TIMES + DJNZ PPK_GETDATA2 ; DO THIS 8 TIMES CALL PPK_WTCLKLO ; GET THE PARITY BIT CALL PPK_WTCLKHI CALL PPK_WTCLKLO ; GET THE STOP BIT @@ -151,11 +194,9 @@ PPK_GETBYTE2: RET ; ;__________________________________________________________________________________________________ -PPK_PUTBYTE: +PPK_PUTDATA: ; ; PUT A RAW BYTE FROM A TO THE KEYBOARD INTERFACE -; -; ALL REGISTERS ARE DESTROYED ; LD E,A ; STASH INCOMING BYTE VALUE IN E @@ -180,22 +221,22 @@ PPK_PUTBYTE: ; SEND DATA BITS LD B,8 ; 8 DATA BITS -PPK_PUTBYTE1: +PPK_PUTDATA1: RRC E ; ROTATE LOW BIT OF E TO CARRY (NEXT BIT TO SEND) LD A,PPK_DAT >> 1 ; INIT A WITH DATA MASK SHIFTED RIGHT BY ONE BIT RLA ; SHIFT CARRY INTO LOW BIT OF A OUT (PPK_PPIX),A ; SET/RESET DATA LINE FOR NEXT BIT VALUE CALL PPK_WTCLKHI ; WAIT FOR CLOCK TO TRANSTION HI CALL PPK_WTCLKLO ; THEN LO, BIT HAS NOW BEEN RECEIVED BY DEVICE - DJNZ PPK_PUTBYTE1 ; LOOP TO SEND 8 DATA BITS + DJNZ PPK_PUTDATA1 ; LOOP TO SEND 8 DATA BITS ; SEND PARITY BIT XOR A ; CLEAR A OR E ; OR WITH SENT VALUE, SETS PARITY FLAG! LD A,PPK_DAT ; PREPARE A WITH DATA MASK - JP PO,PPK_PUTBYTE2 ; PARITY IS ALREADY ODD, LEAVE A ALONE + JP PO,PPK_PUTDATA2 ; PARITY IS ALREADY ODD, LEAVE A ALONE INC A ; SET PARITY BIT BY INCREMENTING A -PPK_PUTBYTE2: +PPK_PUTDATA2: OUT (PPK_PPIX),A ; SET THE DATA LINE CALL PPK_WTCLKHI ; WAIT FOR CLOCK TO TRANSITION HI CALL PPK_WTCLKLO ; THEN LO, BIT HAS NOW BEEN RECEIVED BY DEVICE @@ -305,83 +346,104 @@ PPK_SETBIT: ; PPK_RESET: LD A,$FF ; RESET COMMAND - CALL PPK_PUTBYTE ; SEND IT - CALL PPK_GETBYTE ; GET THE ACK + CALL PPK_PUTDATA ; SEND IT + CALL PPK_GETDATA ; GET THE ACK LD B,0 ; SETUP LOOP COUNTER PPK_RESET0: - PUSH BC ; PRESERVE LOOP COUNTER + PUSH BC ; PRESERVE COUNTER CALL DELAY ; DELAY 25MS - CALL PPK_GETBYTE ; TRY TO GET $AA - POP BC ; RESTORE LOOP COUNTER - JR NZ,PPK_RESET1 ; GOT A BYTE? IF SO, DONE (WE IGNORE RESPONSE CODE VALUE) - DJNZ PPK_RESET0 ; KEEP TRYING UNTIL COUNTER EXPIRES + CALL PPK_GETDATA ; TRY TO GET THE RESPONSE + POP BC ; RECOVER COUNTER + JR NZ,PPK_RESET1 ; GOT A BYTE? IF SO, GET OUT OF LOOP + DJNZ PPK_RESET0 ; LOOP TILL COUNTER EXHAUSTED PPK_RESET1: - XOR A ; SIGNAL SUCCESS + LD A,B + XOR A ; SIGNAL SUCCESS (RESPONSE IS IGNORED...) RET ; DONE ; ;__________________________________________________________________________________________________ -; DECODING ENGINE +; UPDATE KEYBOARD LEDS BASED ON CURRENT TOGGLE FLAGS ;__________________________________________________________________________________________________ ; -; STATUS BITS (FOR PPK_STATUS) -; -PPK_EXT .EQU 01H ; BIT 0, EXTENDED SCANCODE ACTIVE -PPK_BREAK .EQU 02H ; BIT 1, THIS IS A KEY UP (BREAK) EVENT -PPK_KEYRDY .EQU 80H ; BIT 7, INDICATES A DECODED KEYCODE IS READY +PPK_SETLEDS: + LD A,$ED ; SET/RESET LED'S COMMAND + CALL PPK_PUTDATA ; SEND THE COMMAND + CALL PPK_GETDATA ; READ THE RESPONSE + CP $FA ; MAKE SURE WE GET ACK + RET NZ ; ABORT IF NO ACK + LD A,(PPK_STATE) ; LOAD THE STATE BYTE + RRCA ; ROTATE TOGGLE KEY BITS AS NEEDED + RRCA + RRCA + RRCA + AND $07 ; CLEAR THE IRRELEVANT BITS + CALL PPK_PUTDATA ; SEND THE LED DATA + CALL PPK_GETDATA ; READ THE ACK + JP PPK_DECNEW ; RESTART DECODER FOR A NEW KEY + RET ; DONE ; -; STATE BITS (FOR PPK_STATE, PPK_LSTATE, PPK_RSTATE) +;__________________________________________________________________________________________________ +; UPDATE KEYBOARD REPEAT RATE BASED ON CURRENT SETTING +;__________________________________________________________________________________________________ ; -PPK_SHIFT .EQU 01H ; BIT 0, SHIFT ACTIVE (PRESSED) -PPK_CTRL .EQU 02H ; BIT 1, CONTROL ACTIVE (PRESSED) -PPK_ALT .EQU 04H ; BIT 2, ALT ACTIVE (PRESSED) -PPK_WIN .EQU 08H ; BIT 3, WIN ACTIVE (PRESSED) -PPK_SCRLCK .EQU 10H ; BIT 4, CAPS LOCK ACTIVE (TOGGLED ON) -PPK_NUMLCK .EQU 20H ; BIT 5, NUM LOCK ACTIVE (TOGGLED ON) -PPK_CAPSLCK .EQU 40H ; BIT 6, SCROLL LOCK ACTIVE (TOGGLED ON) -PPK_NUMPAD .EQU 80H ; BIT 7, NUM PAD KEY (KEY PRESSED IS ON NUM PAD) +PPK_SETRPT: + LD A,$F3 ; COMMAND = SET TYPEMATIC RATE/DELAY + CALL PPK_PUTDATA ; SEND IT + CALL PPK_GETDATA ; GET THE ACK + CP $FA ; MAKE SURE WE GET ACK + RET NZ ; ABORT IF NO ACK + LD A,(PPK_REPEAT) ; LOAD THE CURRENT RATE/DELAY BYTE + CALL PPK_PUTDATA ; SEND IT + CALL PPK_GETDATA ; GET THE ACK + RET ; -PPK_SCANCODE .DB 0 ; RAW SCANCODE -PPK_KEYCODE .DB 0 ; RESULTANT KEYCODE AFTER DECODING -PPK_STATE .DB 0 ; STATE BITS (SEE ABOVE) -PPK_LSTATE .DB 0 ; STATE BITS FOR "LEFT" KEYS -PPK_RSTATE .DB 0 ; STATE BITS FOR "RIGHT" KEYS -PPK_STATUS .DB 0 ; CURRENT STATUS BITS (SEE ABOVE) -PPK_IDLE .DB 0 ; IDLE COUNT +;__________________________________________________________________________________________________ +; DECODING ENGINE +;__________________________________________________________________________________________________ ; ;__________________________________________________________________________________________________ PPK_DECODE: ; -; RUN THE DECODING ENGINE UNTIL EITHER: 1) A TIMEOUT OCCURS TRYING TO GET SCANCODES +; RUN THE DECODING ENGINE UNTIL EITHER: 1) NO MORE SCANCODES ARE AVAILABLE ; FROM THE KEYBOARD, OR 2) A DECODED KEY VALUE IS AVAILABLE ; -; RETURNS A=0 AND Z SET IF TIMEOUT, OTHERWISE A DECODED KEY VALUE IS AVAILABLE. +; RETURNS A=0 AND Z SET IF NO KEYCODE READY, OTHERWISE A DECODED KEY VALUE IS AVAILABLE. ; THE DECODED KEY VALUE AND KEY STATE IS STORED IN PPK_KEYCODE AND PPK_STATE. ; -; PPK_KEYCODE AND PPK_STATUS ARE CLEARED AT START. IF IS THE CALLER'S RESPONSIBILITY -; TO RETRIEVE ANY PRIOR VALUE BEFORE CALLING THIS FUNCTION AGAIN. +; PPK_STATUS IS NOT CLEARED AT START. IT IS THE CALLER'S RESPONSIBILITY +; TO CLEAR PPK_STATUS WHEN IT HAS RETRIEVED A PENDING VALUE. IF DECODE IS CALLED +; WITH A KEYCODE STILL PENDING, IT WILL JUST RETURN WITHOUT DOING ANYTHING. ; -; Step 0: Initialize for next keypress -; clear status ($00) -; clear keycode ($FF) +; Step 0: Check keycode buffer +; if status[keyrdy] +; return ; -; Step 1: Get scancode w/timeout +; Step 1: Get scancode +; if no scancode ready +; return ; read scancode -; if timeout -; return "timeout" -; -; Step 2: Detect and flag break prefix -; if scancode == $F0 -; set status[break] -; goto Phase 1 +; +; Step 2: Detect and handle special keycodes +; if scancode == $AA +; *** handle hot insert somehow *** ; -; Step 3: Detect and flag extended prefix +; Step 3: Detect and handle scancode prefixes ; if scancode == $E0 ; set status[extended] -; goto Phase 1 +; goto Step 1 ; -; Step 4: Detect and handle $E1 ; if scancode == $E1 -; ***handle pause key somehow*** +; *** handle pause key somehow *** +; +; Step 4: Detect and flag break event +; *** scancode set #1 variation *** +; set status[break] = high bit of scancode +; clear high order bit +; continue to Step 5 +; *** scancode set #2 variation *** +; if scancode == $F0 +; set status[break] +; goto Step 1 ; ; Step 5: Map scancode to keycode ; if status[extended] @@ -396,76 +458,103 @@ PPK_DECODE: ; set (l/r)state[] = not status[break] ; clear modifier bits in state ; set state = (lstate OR rstate OR state) -; goto Entry +; goto New Key ; ; Step 7: Complete procesing of key break events ; if status[break] -; goto Entry +; goto New Key ; ; Step 8: Handle toggle keys ; if keycode is toggle (capslock, numlock, scrolllock) ; invert (XOR) state[] ; update keyboard LED's -; goto Entry +; goto New Key ; -; Step 9: Handle control keys +; Step 9: Adjust keycode for control modifier ; if state[ctrl] ; if keycode is 'a'-'z' ; subtract 20 (clear bit 5) from keycode ; if keycode is '@'-'_' ; subtract 40 (clear bit 6) from keycode ; -; Step 10: Adjust keycode for caps lock, if needed +; Step 10: Adjust keycode for caps lock ; if state[capslock] ; if keycode is 'a'-'z' OR 'A'-'Z' ; toggle (XOR) bit 5 of keycode ; ; Step 11: Handle num pad keys +; clear state[numpad] ; if keycode is numpad ; set state[numpad] ; if state[numlock] ; toggle (XOR) bit 4 of keycode ; apply numpad-map[keycode] -> keycode -; else -; clear state[numpad] ; ; Step 12: Detect unknown/invalid keycodes ; if keycode == $FF -; goto Entry +; goto New Key ; ; Step 13: Done -; return "ready" +; set status[keyrdy] +; return ; -PPK_DEC0: - XOR A ; A = ZERO - LD (PPK_STATUS),A ; CLEAR STATUS - DEC A ; A = $FF - LD (PPK_KEYCODE),A ; CLEAR KEYCODE +; New Key: +; clear status +; goto Step 1 +; +PPK_DEC0: ; CHECK KEYCODE BUFFER + LD A,(PPK_STATUS) ; GET CURRENT STATUS + AND PPK_KEYRDY ; ISOLATE KEY READY FLAG + RET NZ ; ABORT IF KEY IS ALREADY PENDING -PPK_DEC1: ; GET SCANCODE W/ TIMEOUT - CALL PPK_GETBYTE ; GET A SCANCODE +PPK_DEC1: ; PROCESS NEXT SCANCODE + CALL PPK_GETDATA ; GET THE SCANCODE RET Z ; TIMEOUT, RETURN WITH A=0, Z SET LD (PPK_SCANCODE),A ; SAVE SCANCODE -PPK_DEC2: ; DETECT AND FLAG BREAK PREFIX $F0 - CP $F0 ; BREAK (KEY UP) PREFIX? - JR NZ,PPK_DEC3 ; NOPE MOVE ON +PPK_DEC2: ; DETECT AND HANDLE SPECIAL KEYCODES + LD A,(PPK_SCANCODE) ; GET THE CURRENT SCANCODE + CP $AA ; KEYBOARD INSERTION? + JR NZ,PPK_DEC3 ; NOPE, BYPASS + CALL LDELAY ; WAIT A BIT + CALL PPK_RESET ; RESET KEYBOARD + CALL PPK_SETLEDS ; SET LEDS + CALL PPK_SETRPT ; SET REPEAT RATE + JP PPK_DECNEW ; RESTART THE ENGINE + +PPK_DEC3: ; DETECT AND HANDLE SCANCODE PREFIXES + LD A,(PPK_SCANCODE) ; GET THE CURRENT SCANCODE + +PPK_DEC3A: ; HANDLE SCANCODE PREFIX $E0 (EXTENDED SCANCODE FOLLOWS) + CP $E0 ; EXTENDED KEY PREFIX $E0? + JR NZ,PPK_DEC3B ; NOPE MOVE ON LD A,(PPK_STATUS) ; GET STATUS - OR PPK_BREAK ; SET BREAK BIT + OR PPK_EXT ; SET EXTENDED BIT LD (PPK_STATUS),A ; SAVE STATUS JR PPK_DEC1 ; LOOP TO DO NEXT SCANCODE -PPK_DEC3: ; DETECT AND FLAG EXTENDED PREFIX $E0 - CP $E0 ; EXTENDED KEY PREFIX? +PPK_DEC3B: ; HANDLE SCANCODE PREFIX $E1 (PAUSE KEY) + CP $E1 ; EXTENDED KEY PREFIX $E1 JR NZ,PPK_DEC4 ; NOPE MOVE ON + LD A,$EE ; MAP TO KEYCODE $EE + LD (PPK_KEYCODE),A ; SAVE IT + ; SWALLOW NEXT 7 SCANCODES + LD B,7 ; LOOP 5 TIMES +PPK_DEC3B1: + PUSH BC + CALL PPK_GETDATA ; RETRIEVE NEXT SCANCODE + POP BC + DJNZ PPK_DEC3B1 ; LOOP AS NEEDED + JP PPK_DEC6 ; RESUME AFTER MAPPING + +PPK_DEC4: ; DETECT AND FLAG BREAK EVENT + CP $F0 ; BREAK (KEY UP) PREFIX? + JR NZ,PPK_DEC5 ; NOPE MOVE ON LD A,(PPK_STATUS) ; GET STATUS - OR PPK_EXT ; SET EXTENDED BIT + OR PPK_BREAK ; SET BREAK BIT LD (PPK_STATUS),A ; SAVE STATUS JR PPK_DEC1 ; LOOP TO DO NEXT SCANCODE -PPK_DEC4: ; DETECT AND HANDLE EXTENDED PREFIX $E1 - ; TODO: HANDLE PAUSE KEY HERE... - PPK_DEC5: ; MAP SCANCODE TO KEYCODE LD A,(PPK_STATUS) ; GET STATUS AND PPK_EXT ; EXTENDED BIT SET? @@ -478,7 +567,7 @@ PPK_DEC5: ; MAP SCANCODE TO KEYCODE PPK_DEC5A: LD A,(HL) ; GET FIRST BYTE OF PAIR CP $00 ; END OF TABLE? - JP Z,PPK_DECODE ; UNKNOWN OR BOGUS, START OVER + JP Z,PPK_DECNEW ; UNKNOWN OR BOGUS, START OVER INC HL ; INC HL FOR FUTURE CP E ; DOES MATCH BYTE EQUAL SCANCODE? JR Z,PPK_DEC5B ; YES! JUMP OUT @@ -491,8 +580,8 @@ PPK_DEC5B: PPK_DEC5C: ; PERFORM REGULAR KEY (NOT EXTENDED) KEY MAPPING LD A,(PPK_SCANCODE) ; GET THE SCANCODE - CP $85 ; PAST END OF TABLE? - JR NC,PPK_DEC6 ; YES, SKIP OVER LOOKUP + CP PPK_MAPSIZ ; COMPARE TO SIZE OF TABLE + JR NC,PPK_DEC6 ; PAST END, SKIP OVER LOOKUP ; SETUP POINTER TO MAPPING TABLE BASED ON SHIFTED OR UNSHIFTED STATE LD A,(PPK_STATE) ; GET STATE @@ -515,76 +604,59 @@ PPK_DEC6: ; HANDLE MODIFIER KEYS CP $B0 ; START OF MODIFIER KEYS JR C,PPK_DEC7 ; BYPASS MODIFIER KEY CHECKING - ; TODO: STUFF BELOW COULD BE A LOOP + LD B,4 ; LOOP COUNTER TO LOOP THRU 4 MODIFIER BITS + LD E,$80 ; SETUP E TO ROATE THROUGH MODIFIER STATE BITS + SUB $B0 - 1 ; SETUP A TO DECREMENT THROUGH MODIFIER VALUES - ; HANDLE L/R SHIFT KEYS - LD E,PPK_SHIFT ; SETUP TO SET/CLEAR SHIFT BIT - SUB $B0 ; L-SHIFT? - JR Z,PPK_DEC6A ; YES, HANDLE L-SHIFT MAKE/BREAK - DEC A ; R-SHIFT? - JR Z,PPK_DEC6B ; YES, HANDLE R-SHIFT MAKE/BREAK - - ; HANDLE L/R CONTROL KEYS - LD E,PPK_CTRL ; SETUP TO SET/CLEAR CONTROL BIT - DEC A ; L-CONTROL? - JR Z,PPK_DEC6A ; YES, HANDLE L-CONTROL MAKE/BREAK - DEC A ; R-CONTROL? - JR Z,PPK_DEC6B ; YES, HANDLE R-CONTROL MAKE/BREAK - - ; HANDLE L/R ALT KEYS - LD E,PPK_ALT ; SETUP TO SET/CLEAR ALT BIT - DEC A ; L-ALT? - JR Z,PPK_DEC6A ; YES, HANDLE L-ALT MAKE/BREAK - DEC A ; R-ALT? - JR Z,PPK_DEC6B ; YES, HANDLE R-ALT MAKE/BREAK - - ; HANDLE L/R WIN KEYS - LD E,PPK_WIN ; SETUP TO SET/CLEAR WIN BIT - DEC A ; L-WIN? - JR Z,PPK_DEC6A ; YES, HANDLE L-WIN MAKE/BREAK - DEC A ; R-WIN? - JR Z,PPK_DEC6B ; YES, HANDLE R-WIN MAKE/BREAK - -PPK_DEC6A: ; LEFT STATE KEY MAKE/BREAK (STATE BIT TO SET/CLEAR IN E) +PPK_DEC6A: + RLC E ; SHIFT TO NEXT MODIFIER STATE BIT + DEC A ; L-MODIFIER? + JR Z,PPK_DEC6B ; YES, HANDLE L-MODIFIER MAKE/BREAK + DEC A ; R-MODIFIER? + JR Z,PPK_DEC6C ; YES, HANDLE R-MODIFIER MAKE/BREAK + DJNZ PPK_DEC6A ; LOOP THRU 4 MODIFIER BITS + JR PPK_DEC7 ; FAILSAFE, SHOULD NEVER GET HERE! + +PPK_DEC6B: ; LEFT STATE KEY MAKE/BREAK (STATE BIT TO SET/CLEAR IN E) LD HL,PPK_LSTATE ; POINT TO LEFT STATE BYTE - JR PPK_DEC6C ; CONTINUE + JR PPK_DEC6D ; CONTINUE -PPK_DEC6B: ; RIGHT STATE KEY MAKE/BREAK (STATE BIT TO SET/CLEAR IN E) +PPK_DEC6C: ; RIGHT STATE KEY MAKE/BREAK (STATE BIT TO SET/CLEAR IN E) LD HL,PPK_RSTATE ; POINT TO RIGHT STATE BYTE - JR PPK_DEC6C ; CONTINUE + JR PPK_DEC6D ; CONTINUE -PPK_DEC6C: ; BRANCH BASED ON WHETHER THIS IS A MAKE OR BREAK EVENT +PPK_DEC6D: ; BRANCH BASED ON WHETHER THIS IS A MAKE OR BREAK EVENT LD A,(PPK_STATUS) ; GET STATUS FLAGS AND PPK_BREAK ; BREAK EVENT? - JR Z,PPK_DEC6D ; NO, HANDLE A MODIFIER KEY MAKE EVENT - JR PPK_DEC6E ; YES, HANDLE A MODIFIER BREAK EVENT + JR Z,PPK_DEC6E ; NO, HANDLE A MODIFIER KEY MAKE EVENT + JR PPK_DEC6F ; YES, HANDLE A MODIFIER BREAK EVENT -PPK_DEC6D: ; HANDLE STATE KEY MAKE EVENT +PPK_DEC6E: ; HANDLE STATE KEY MAKE EVENT LD A,E ; GET THE BIT TO SET OR (HL) ; OR IN THE CURRENT BITS LD (HL),A ; SAVE THE RESULT - JR PPK_DEC6F ; CONTINUE + JR PPK_DEC6G ; CONTINUE -PPK_DEC6E: ; HANDLE STATE KEY BREAK EVENT +PPK_DEC6F: ; HANDLE STATE KEY BREAK EVENT LD A,E ; GET THE BIT TO CLEAR XOR $FF ; FLIP ALL BITS TO SETUP FOR A CLEAR OPERATION AND (HL) ; AND IN THE FLIPPED BITS TO CLEAR DESIRED BIT LD (HL),A ; SAVE THE RESULT - JR PPK_DEC6F ; CONTINUE + JR PPK_DEC6G ; CONTINUE -PPK_DEC6F: ; COALESCE L/R STATE FLAGS +PPK_DEC6G: ; COALESCE L/R STATE FLAGS LD A,(PPK_STATE) ; GET EXISTING STATE BITS AND $F0 ; GET RID OF OLD MODIFIER BITS LD DE,(PPK_LSTATE) ; LOAD BOTH L/R STATE BYTES IN D/E OR E ; MERGE IN LEFT STATE BITS OR D ; MERGE IN RIGHT STATE BITS LD (PPK_STATE),A ; SAVE IT - JP PPK_DECODE ; DONE WITH CURRENT KEYSTROKE + JP PPK_DECNEW ; DONE WITH CURRENT KEYSTROKE -PPK_DEC7: ; COMPLETE PROCESSING OF KEY BREAK EVENTS +PPK_DEC7: ; COMPLETE PROCESSING OF EXTENDED AND KEY BREAK EVENTS LD A,(PPK_STATUS) ; GET CURRENT STATUS FLAGS AND PPK_BREAK ; IS THIS A KEY BREAK EVENT? - JP NZ,PPK_DECODE ; YES, RESTART TO PROCESS NEXT KEY + JP NZ,PPK_DECNEW ; PROCESS NEXT KEY PPK_DEC8: ; HANDLE TOGGLE KEYS LD A,(PPK_KEYCODE) ; GET THE CURRENT KEYCODE INTO A @@ -603,24 +675,10 @@ PPK_DEC8A: ; RECORD THE TOGGLE LD A,(PPK_STATE) ; GET THE CURRENT STATE FLAGS XOR E ; SET THE TOGGLE KEY BIT FROM ABOVE LD (PPK_STATE),A ; SAVE IT + CALL PPK_SETLEDS ; UPDATE LED LIGHTS ON KBD + JP PPK_DECNEW ; RESTART DECODER FOR A NEW KEY - ; UPDATE KEYBOARD LED'S - LD A,$ED ; SET/RESET LED'S COMMAND - CALL PPK_PUTBYTE ; SEND THE COMMAND - CALL PPK_GETBYTE ; READ THE RESPONSE - CP $FA ; MAKE SURE WE GET ACK - JP NZ,PPK_DECODE ; ABORT IF NO ACK - LD A,(PPK_STATE) ; LOAD THE STATE BYTE - RRCA ; ROTATE TOGGLE KEY BITS AS NEEDED - RRCA - RRCA - RRCA - AND $07 ; CLEAR THE IRRELEVANT BITS - CALL PPK_PUTBYTE ; SEND THE LED DATA - CALL PPK_GETBYTE ; READ THE ACK - JP PPK_DECODE ; RESTART DECODER FOR A NEW KEY - -PPK_DEC9: ; HANDLE CONTROL KEYS +PPK_DEC9: ; ADJUST KEYCODE FOR CONTROL MODIFIER LD A,(PPK_STATE) ; GET THE CURRENT STATE BITS AND PPK_CTRL ; CHECK THE CONTROL BIT JR Z,PPK_DEC10 ; CONTROL KEY NOT PRESSED, MOVE ON @@ -638,7 +696,7 @@ PPK_DEC9A: RES 6,A ; CONVERT TO CONTROL VALUE BY CLEARING BIT 6 LD (PPK_KEYCODE),A ; UPDATE KEYCODE TO CONTROL VALUE -PPK_DEC10: ; ADJUST KEYCODE FOR CAPS LOCK, IF NEEDED +PPK_DEC10: ; ADJUST KEYCODE FOR CAPS LOCK LD A,(PPK_STATE) ; LOAD THE STATE FLAGS AND PPK_CAPSLCK ; CHECK CAPS LOCK JR Z,PPK_DEC11 ; CAPS LOCK NOT ACTIVE, MOVE ON @@ -692,7 +750,7 @@ PPK_DEC11A: ; APPLY NUMPAD MAPPING PPK_DEC12: ; DETECT UNKNOWN/INVALID KEYCODES LD A,(PPK_KEYCODE) ; GET THE FINAL KEYCODE CP $FF ; IS IT $FF (UNKNOWN/INVALID) - JP Z,PPK_DECODE ; IF SO, JUST RESTART THE ENGINE + JP Z,PPK_DECNEW ; IF SO, JUST RESTART THE ENGINE PPK_DEC13: ; DONE - RECORD RESULTS LD A,(PPK_STATUS) ; GET CURRENT STATUS @@ -701,6 +759,11 @@ PPK_DEC13: ; DONE - RECORD RESULTS XOR A ; A=0 INC A ; SIGNAL SUCCESS WITH A=1 RET + +PPK_DECNEW: ; START NEW KEYPRESS (CLEAR ALL STATUS BITS) + XOR A ; A = 0 + LD (PPK_STATUS),A ; CLEAR STATUS + JP PPK_DEC1 ; RESTART THE ENGINE ; ;__________________________________________________________________________________________________ ; MAPPING TABLES @@ -717,12 +780,14 @@ PPK_MAPSTD: ; SCANCODE IS INDEX INTO TABLE TO RESULTANT LOOKUP KEYCODE .DB $C9,$CA,$C1,$C4,$C5,$C7,$1B,$BD,$FA,$CE,$C2,$CD,$CC,$C8,$BE,$FF .DB $FF,$FF,$FF,$E6,$EC ; +PPK_MAPSIZ .EQU ($ - PPK_MAPSTD) +; PPK_MAPSHIFT: ; SCANCODE IS INDEX INTO TABLE TO RESULTANT LOOKUP KEYCODE WHEN SHIFT ACTIVE .DB $FF,$E8,$FF,$E4,$E2,$E0,$E1,$EB,$FF,$E9,$E7,$E5,$E3,$09,'~',$FF .DB $FF,$B4,$B0,$FF,$B2,'Q','!',$FF,$FF,$FF,'Z','S','A','W','@',$FF .DB $FF,'C','X','D','E','$','#',$FF,$FF,' ','V','F','T','R','%',$FF .DB $FF,'N','B','H','G','Y','^',$FF,$FF,$FF,'M','J','U','&','*',$FF - .DB $FF,'<','K','I','O',')',')',$FF,$FF,'>','?','L',':','P','_',$FF + .DB $FF,'<','K','I','O',')','(',$FF,$FF,'>','?','L',':','P','_',$FF .DB $FF,$FF,$22,$FF,'{','+',$FF,$FF,$BC,$B1,$0D,'}',$FF,'|',$FF,$FF .DB $FF,$FF,$FF,$FF,$FF,$FF,$08,$FF,$FF,$D0,$FF,$D3,$D6,$FF,$FF,$FF .DB $D9,$DA,$D1,$D4,$D5,$D7,$1B,$BD,$FA,$DE,$D2,$DD,$DC,$D8,$BE,$FF diff --git a/Source/vdu.asm b/Source/vdu.asm index 1db1a068..3a575f0f 100644 --- a/Source/vdu.asm +++ b/Source/vdu.asm @@ -22,7 +22,6 @@ VDU_DATA .EQU 0F3h ; VDU DATA REGISTER ; VDU_INIT: CALL INITVDU - CALL PPK_INIT XOR A RET ; @@ -178,12 +177,6 @@ VDU_WAITRDY: JR VDU_WAITRDY ; KEEP CHECKING ; ;__________________________________________________________________________________________________ -; IMBED COMMON PARALLEL PORT KEYBOARD DRIVER -;__________________________________________________________________________________________________ -; -#INCLUDE "ppk.asm" -; -;__________________________________________________________________________________________________ ; INITIALIZATION ;__________________________________________________________________________________________________ INITVDU: