;__________________________________________________________________________________________________ ; ; 8242 BASED PS/2 KEYBOARD DRIVER FOR SBC ; ; ORIGINAL CODE BY DR JAMES MOXHAM ; ROMWBW ADAPTATION BY WAYNE WARTHEN ;__________________________________________________________________________________________________ ; ; TODO: ; CONSIDER DETECTING ERRORS IN STATUS BYTE (PERR, TO) ;__________________________________________________________________________________________________ ; DATA CONSTANTS ;__________________________________________________________________________________________________ ; ; DRIVER DATA OFFSETS (FROM IY) ; KBD_MODE .EQU 0 ; BYTE, KEYBOARD MODE (STD OR VRC) KBD_ST .EQU 1 ; BYTE, STATUS PORT NUM (R) KBD_CMD .EQU KBD_ST ; BYTE, CMD PORT NUM (W) KBD_DAT .EQU 2 ; BYTE, DATA PORT NUM (R/W) ; ; TIMING CONSTANTS ; KBD_WAITTO .EQU 0 ; 0 IS MAX WAIT (256) ; ; 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: CALL NEWLINE ; FORMATTING PRTS("KBD: IO=0x$") LD A,(IY+KBD_DAT) CALL PRTHEXBYTE ; 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,(IY+KBD_MODE) ; GET KBD MODE BYTE CP KBDMODE_VRC ; VRC? JR Z,KBD_INIT1 ; IF SO, MUST ASSUME PRESENT ; LD A,$AA ; CONTROLLER SELF TEST CALL KBD_PUTCMD ; SEND IT CALL KBD_GETDATA ; CONTROLLER SHOULD RESPOND WITH $55 (ACK) ; CP $55 ; IS IT THERE? JR Z,KBD_INIT1 ; IF SO, CONTINUE PRTS(" NOT PRESENT$") ; DIAGNOSE PROBLEM RET ; BAIL OUT ; KBD_INIT1: PRTS(" MODE=$") ; TAG LD A,(IY+KBD_MODE) ; GET MODE VALUE LD DE,KBD_STR_MODEPS2 CP KBDMODE_PS2 JR Z,KBD_INIT2 LD DE,KBD_STR_MODEVRC CP KBDMODE_VRC JR Z,KBD_INIT2 LD DE,KBD_STR_MODEUNK KBD_INIT2: CALL WRITESTR ; LD A,$60 ; SET COMMAND REGISTER CALL KBD_PUTCMD ; SEND IT ; LD A,$60 ; XLAT ENABLED, MOUSE DISABLED, NO INTS LD A,$20 ; XLAT DISABLED, MOUSE DISABLED, NO INTS CALL KBD_PUTDATA ; SEND IT CALL KBD_GETDATA ; GOBBLE UP $AA FROM POWER UP, AS NEEDED 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: CALL KBD_DECODE ; CHECK THE KEYBOARD JP Z,CIO_IDLE ; RET VIA IDLE PROCESSING IF NO KEY 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 ; KEY READY? JR Z,KBD_READ ; NOT READY, KEEP TRYING ; LD A,(KBD_STATE) ; GET STATE AND $01 ; ISOLATE EXTENDED SCANCODE BIT RRCA ; ROTATE IT TO HIGH ORDER BIT LD E,A ; SAVE IT IN E FOR NOW LD A,(KBD_SCANCODE) ; GET SCANCODE OR E ; COMBINE WITH EXTENDED BIT LD C,A ; STORE IT IN C FOR RETURN 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 ; LD C,(IY+KBD_ST) ; STATUS PORT IN A,(C) ; 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 ; LD C,(IY+KBD_ST) ; STATUS PORT IN A,(C) ; 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 A,(IY+KBD_MODE) ; GET MODE BYTE CP KBDMODE_VRC ; VGARC KEYBOARD? JR Z,KBD_PUTCMD2 ; BAIL OUT 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 #IF (KBDTRACE >= 2) CALL PC_SPACE CALL PC_GT CALL PC_GT CALL PRTHEXBYTE #ENDIF LD C,(IY+KBD_CMD) ; COMMAND PORT OUT (C),A ; WRITE IT KBD_PUTCMD2: 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 A,(IY+KBD_MODE) ; GET MODE BYTE CP KBDMODE_VRC ; VGARC KEYBOARD? JR Z,KBD_PUTDATA2 ; BAIL OUT 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 #IF (KBDTRACE >= 2) CALL PC_SPACE CALL PC_GT CALL PRTHEXBYTE #ENDIF LD C,(IY+KBD_DAT) ; DATA PORT OUT (C),A ; WRITE IT KBD_PUTDATA2: 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: LD C,(IY+KBD_DAT) ; DATA PORT IN A,(C) ; GET THE DATA VALUE #IF (KBDTRACE >= 2) PUSH AF CALL PC_SPACE CALL PC_LT CALL PRTHEXBYTE POP AF #ENDIF OR A ; SET FLAGS RET ; ;__________________________________________________________________________________________________ KBD_GETDATAX: ; ; GET A RAW DATA BYTE FROM KEYBOARD INTERFACE INTO A WITH NOTIMEOUT ; CALL KBD_IST ; GET INPUT REGISTER STATUS RET Z ; NOTHING THERE, DONE JR KBD_GETDATA1 ; GO GET IT ; ;__________________________________________________________________________________________________ ; 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 KBD_RESET0: PUSH BC ; PRESERVE COUNTER CALL KBD_GETDATA ; TRY TO GET THE RESPONSE POP BC ; RECOVER COUNTER JR NZ,KBD_RESET1 ; GOT A BYTE? IF SO, GET OUT OF LOOP DJNZ KBD_RESET0 ; LOOP TILL COUNTER EXHAUSTED KBD_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_GETDATAX ; GET THE SCANCODE RET Z ; NO KEY READY, 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 LDELAY ; WAIT A BIT 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 7 SCANCODES LD B,7 ; 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 CP $F0 ; BREAK (KEY UP) PREFIX? JR NZ,KBD_DEC5 ; NOPE MOVE ON LD A,(KBD_STATUS) ; GET STATUS OR KBD_BREAK ; SET BREAK BIT LD (KBD_STATUS),A ; SAVE STATUS JR KBD_DEC1 ; LOOP TO DO NEXT SCANCODE 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 ; ; DRIVER DATA ; KBD_STR_MODEPS2 .TEXT "PS2$" KBD_STR_MODEVRC .TEXT "VRC$" KBD_STR_MODEUNK .TEXT "???$" ; ; ; #IF (KBDKBLOUT == KBD_US) ; ;__________________________________________________________________________________________________ ; ; MAPPING TABLES US/ENGLISH ;__________________________________________________________________________________________________ ; KBD_MAPSTD: ; SCANCODE IS INDEX INTO TABLE TO RESULTANT LOOKUP KEYCODE .DB $FF,$E8,$FF,$E4,$E2,$E0,$E1,$EB,$FF,$E9,$E7,$E5,$E3,$09,'`',$FF .DB $FF,$B4,$B0,$FF,$B2,'q','1',$FF,$FF,$FF,'z','s','a','w','2',$FF .DB $FF,'c','x','d','e','4','3',$FF,$FF,' ','v','f','t','r','5',$FF .DB $FF,'n','b','h','g','y','6',$FF,$FF,$FF,'m','j','u','7','8',$FF .DB $FF,',','k','i','o','0','9',$FF,$FF,'.','/','l',';','p','-',$FF .DB $FF,$FF,$27,$FF,'[','=',$FF,$FF,$BC,$B1,$0D,']',$FF,'\',$FF,$FF .DB $FF,$FF,$FF,$FF,$FF,$FF,$08,$FF,$FF,$C0,$FF,$C3,$C6,$FF,$FF,$FF .DB $C9,$CA,$C1,$C4,$C5,$C7,$1B,$BD,$FA,$CE,$C2,$CD,$CC,$C8,$BE,$FF .DB $FF,$FF,$FF,$E6,$EC ; KBD_MAPSIZ .EQU ($ - KBD_MAPSTD) ; KBD_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,$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 .DB $FF,$FF,$FF,$E6,$EC ; KBD_MAPEXT: ; PAIRS ARE [SCANCODE,KEYCODE] FOR EXTENDED SCANCODES .DB $11,$B5, $14,$B3, $1F,$B6, $27,$B7 .DB $2F,$EF, $37,$FA, $3F,$FB, $4A,$CB .DB $5A,$CF, $5E,$FC, $69,$F3, $6B,$F8 .DB $6C,$F2, $70,$F0, $71,$F1, $72,$F7 .DB $74,$F9, $75,$F6, $7A,$F5, $7C,$ED .DB $7D,$F4, $7E,$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 #ENDIF ; #IF (KBDKBLOUT == KBD_DE) ; ;__________________________________________________________________________________________________ ; ; MAPPING TABLES GERMAN ;__________________________________________________________________________________________________ ; KBD_MAPSTD: ; SCANCODE IS INDEX INTO TABLE TO RESULTANT LOOKUP KEYCODE ROW ; Column 0 1 2 3 4 5 6 7 8 9 A B C D E F ; Special adjustments listed below .DB $FF,$E8,$FF,$E4,$E2,$E0,$E1,$EB,$FF,$E9,$E7,$E5,$E3,$09,'^',$FF ;0 for German keyboard keys that give .DB $FF,$B4,$B0,$FF,$B2,'q','1',$FF,$FF,$FF,'y','s','a','w','2',$FF ;1 different characters than are printed .DB $FF,'c','x','d','e','4','3',$FF,$FF,' ','v','f','t','r','5',$FF ;2 on the keys. .DB $FF,'n','b','h','g','z','6',$FF,$FF,$FF,'m','j','u','7','8',$FF ;3 'german key' --> 'new occupied with' .DB $FF,',','k','i','o','0','9',$FF,$FF,'.','-','l','[','p',$5C,$FF ;4 Assembler ERROR: '\'-->$5C ; 'ö'-->'[' .DB $FF,$FF,'@',$FF,']','|',$FF,$FF,$BC,$B1,$0D,'+',$FF,'#',$FF,$FF ;5 'ä'-->'@' ; 'ü'-->']' .DB $FF,'<',$FF,$FF,$FF,$FF,$08,$FF,$FF,$C0,$FF,$C3,$C6,'<',$FF,$FF ;6 .DB $C9,$CA,$C1,$C4,$C5,$C7,$1B,$BD,$FA,$CE,$C2,$CD,$CC,$C8,$BE,$FF ;7 .DB $FF,$FF,$FF,$E6,$EC ;8 KBD_MAPSIZ .EQU ($ - KBD_MAPSTD) ; KBD_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,'Y','S','A','W',$22,$FF .DB $FF,'C','X','D','E','$',$20,$FF,$FF,' ','V','F','T','R','%',$FF ; '§'-->$20; '§'=Paragraph not used in CP/M .DB $FF,'N','B','H','G','Z','&',$FF,$FF,$FF,'M','J','U','/','(',$FF .DB $FF,';','K','I','O','=',')',$FF,$FF,':','_','L','{','P','?',$FF ; 'Ö'-->'{' .DB $FF,$FF,'@',$FF,'}','`',$FF,$FF,$BC,$B1,$0D,'*',$FF,$27,$FF,$FF ; 'Ä'-->'@' ; 'Ü'-->'}' .DB $FF,'>',$FF,$FF,$FF,$FF,$08,$FF,$FF,$D0,$FF,$D3,$D6,'>',$FF,$FF .DB $D9,$DA,$D1,$D4,$D5,$D7,$1B,$BD,$FA,$DE,$D2,$DD,$DC,$D8,$BE,$FF .DB $FF,$FF,$FF,$E6,$EC KBD_MAPEXT: ; PAIRS ARE [SCANCODE,KEYCODE] FOR EXTENDED SCANCODES .DB $11,$B5, $14,$B3, $1F,$B6, $27,$B7 .DB $2F,$EF, $37,$FA, $3F,$FB, $4A,$CB ; All keys listed below are customized for Wordstar. .DB $5A,$CF, $5E,$FC, $69,$06, $6B,$13 ; n.a , n.a , word right , n.a. .DB $6C,$01, $70,$16, $71,$07, $72,$18 ; Word left , Toggle Insert/Overwrite , Del Char , Cursor down .DB $74,$04, $75,$05, $7A,$1A, $7C,$ED ; Cursor right , Cursor up , Page down .DB $7D,$17, $7E,$FD, $00,$00 ; Page up , n.a. , END KBD_MAPEXT (Pairs end) ; 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 ; #ENDIF ; ;__________________________________________________________________________________________________ ; 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