;__________________________________________________________________________________________________ ; ; 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