Продолжаю клавиатурную тему Есть прога, которая показывает нажатые клавиши (т.е. жмём все 4 стрелки – видим, что все 4 нажаты, отпускаем по одной, видим 3, 2, 1, 0). Так вот, если быстро стукнуть по нескольким клавишам (по тем же стрелкам, например), получаем генерацию IRQ 1 с нажатием 4-х клавиш и следом IRQ 1 с отпусканием... не всегда 4-х, а чаще 3-х или 2-х. Соответственно, 1 или 2 клавиши продолжают отображаться как нажатые. Причём, при запуске из-под винды (DOSBox, VMware+FreeDOS) такого нет, только в чистом DOS'е. Но тут есть один интересный момент. При таком же быстром ударе по Ctrl, Alt и Shift происходит то же самое (что неудивительно), при этом если я делаю это в каком-нибудь Volcov Commander, то вижу, что строка с помощью по функциональным клавишам остаётся в состоянии Shift или Alt, к примеру. Но! Если я нажму на букву, то эта буква будет воспроизведена как обычная буква, без Shift или Alt. Значит, BIOS каким-то образом понимает (в момент нажатия на букву, судя по всему), что Shift или Alt всё же отпущен. Как он это понимает?
Вот так: Код (ASM): TITLE 01/04/84 KEYBOARD BIOS .XLIST INCLUDE DSEG.SRC INCLUDE POSTEQU.SRC .LIST PUBLIC KEYBOARD_IO_1 PUBLIC KB_INT_1 PUBLIC K16 INCLUDE SEGMENT.SRC EXTRN DDS:NEAR EXTRN START_1:NEAR EXTRN K6:BYTE EXTRN K6L:ABS EXTRN K7:BYTE EXTRN K8:BYTE EXTRN K9:BYTE EXTRN K10:BYTE EXTRN K11:BYTE EXTRN K12:BYTE EXTRN K13:BYTE EXTRN K14:BYTE EXTRN K15:BYTE ;---- INT 16 --------------------------------------------------------- ; KEYBOARD I/O ; THESE ROUTINES PROVIDE KEYBOARD SUPPORT ; INPUT ; (AH)=0 READ THE NEXT ASCII CHARACTER STRUCK FROM THE KEYBOARD ; RETURN THE RESULT IN (AL), SCAN CODE IN (AH) ; (AH)=1 SET THE Z FLAG TO INDICATE IF AN ASCII CHARACTER IS AVAILABLE ; TO BE READ. ; (ZF)=1 -- NO CODE AVAILABLE ; (ZF)=0 -- CODE IS AVAILABLE ; IF ZF = 0, THE NEXT CHARACTER IN THE BUFFER TO BE READ IS ; IN AX, AND THE ENTRY REMAINS IN THE BUFFER ; (AH)=2 RETURN THE CURRENT SHIFT STATUS IN AL REGISTER ; THE BIT SETTINGS FOR THIS CODE ARE INDICATED IN THE ; EQUATES FOR KB_FLAG ; OUTPUT ; AS NOTED ABOVE, ONLY AX AND FLAGS CHANGED ; ALL REGISTERS RETAINED ;---------------------------------------- ASSUME CS:CODE,DS:DATA KEYBOARD_IO_1 PROC FAR ;>>> ENTRY POINT FOR ORG 0E82EH STI ; INTERRUPTS BACK ON PUSH DS ; SAVE CURRENT DS PUSH BX ; SAVE BX TEMPORARILY CALL DDS ; ESTABLISH POINTER TO DATA REGION OR AH,AH ; AH=0 JZ K1B ; ASCII_READ DEC AH ; AH=1 JZ K2 ; ASCII_STATUS DEC AH ; AH=2 JZ K3 ; SHIFT_STATUS POP BX ; RECOVER REGISTER POP DS IRET ; INVALID COMMAND ;------ READ THE KEY TO FIGURE OUT WHAT TO DO K1B: MOV BX,BUFFER_HEAD ; GET POINTER TO HEAD OF BUFFER CMP BX,BUFFER_TAIL ; TEST END OF BUFFER JNE K1C ; IF ANYTHING IN BUFFER DONT DO INTERRUPT ; MOV AX,09002H ; MOVE IN WAIT CODE & TYPE INT 15H ; PERFORM OTHER FUNCTION K1: ; ASCII READ STI ; INTERRUPTS BACK ON DURING LOOP NOP ; ALLOW AN INTERRUPT TO OCCUR K1C: CLI ; INTERRUPTS BACK OFF MOV BX,BUFFER_HEAD ; GET POINTER TO HEAD OF BUFFER CMP BX,BUFFER_TAIL ; TEST END OF BUFFER PUSH BX ; SAVE ADDRESS PUSHF ; SAVE FLAG CALL MAKE_LED ; GO GET MODE INDICATOR DATA BYTE MOV BL,KB_FLAG_2 ; GET PREVIOUS BITS XOR BL,AL ; SEE IF ANY DIFFERENT AND BL,07H ; ISOLATE INDICATOR BITS JZ K1A ; IF NO CHANGE BYPASS UPDATE ; CALL SND_LED1 ; GO TURN ON MODE INDICATORS CLI ; DISABLE INTERRUPTS K1A: POPF ; RESTORE FLAGS POP BX ; RESTORE ADDRESS JZ K1 ; LOOP UNTIL SOMETHING IN BUFFER ; MOV AX,[BX] ; GET SCAN CODE AND ASCII CODE CALL K4 ; MOVE POINTER TO NEXT POSITION MOV BUFFER_HEAD,BX ; STORE VALUE IN VARIABLE POP BX ; RECOVER REGISTER POP DS ; RECOVER SEGMENT IRET ; RETURN TO CALLER ;------ ASCII STATUS K2: CLI ; INTERRUPTS OFF MOV BX,BUFFER_HEAD ; GET HEAD POINTER CMP BX,BUFFER_TAIL ; IF EQUAL (Z=1) THEN NOTHING THERE MOV AX,[BX] PUSHF ; SAVE FLAGS ; PUSH AX ; SAVE CODE CALL MAKE_LED ; GO GET MODE INDICATOR DATA BYTE MOV BL,KB_FLAG_2 ; GET PREVIOUS BITS XOR BL,AL ; SEE IF ANY DIFFERENT AND BL,07H ; ISOLATE INDICATOR BITS JZ SK2 ; IF NO CHANGE BYPASS UPDATE ; CALL SND_LED1 ; GO TURN ON MODE INDICATORS SK2: POP AX ; RESTORE CODE POPF ; RESTORE FLAGS STI ; INTERRUPTS BACK ON POP BX ; RECOVER REGISTER POP DS ; RECOVER SEGMENT RET 2 ; THROW AWAY FLAGS ;------ SHIFT STATUS K3: MOV AL,KB_FLAG ; GET THE SHIFT STATUS FLAGS POP BX ; RECOVER REGISTER POP DS ; RECOVER REGISTERS IRET ; RETURN TO CALLER KEYBOARD_IO_1 ENDP ;------ INCREMENT A BUFFER POINTER K4 PROC NEAR INC BX ; MOVE TO NEXT WORD IN LIST INC BX CMP BX,BUFFER_END ; AT END OF BUFFER? JNE K5 ; NO, CONTINUE MOV BX,BUFFER_START ; YES, RESET TO BUFFER BEGINNING K5: RET K4 ENDP ;------ KEYBOARD INTERRUPT ROUTINE KB_INT_1 PROC FAR STI ; ENABLE INTERRUPTS PUSH BP PUSH AX PUSH BX PUSH CX PUSH DX PUSH SI PUSH DI PUSH DS PUSH ES CLD ; FORWARD DIRECTION CALL DDS ; SET UP ADDRESSING MOV AL,DIS_KBD ; DISABLE THE KEYBOARD CALL SHIP_IT ; EXECUTE DISABLE ;------- WAIT FOR COMMAND TO ACCEPTED CLI ; DISABLE INTERRUPTS SUB CX,CX ; KB_INT_01: IN AL,STATUS_PORT ; TEST AL,INPT_BUF_FULL LOOPNZ KB_INT_01 ; WAIT FOR COMMAND TO BE ACCEPTED IN AL,PORT_A ; READ IN THE CHARACTER STI ; ENABLE INTERRUPTS AGAIN ;--------CHECK FOR A RESEND COMMAND TO KEYBOARD CMP AL,KB_RESEND ; IS THE INPUT A RESEND JE KB_INT_4 ; GO IF RESEND ;------- CHECK FOR RESPONSE TO A COMMAND TO KEYBOARD CMP AL,KB_ACK ; IS THE INPUT AN ACKNOWLEDGE JNZ KB_INT_2 ; GO IF NOT ;------- A COMMAND TO THE KEYBOARD WAS ISSUED CLI ; DISABLE INTERRUPTS OR KB_FLAG_2,KB_FA ; INDICATE ACK RECEIVED JMP K26 ; RETURN IF NOT (THIS ACK RETURNED FOR DATA) ;-------- RESEND THE LAST BYTE KB_INT_4: CLI ; DISABLE INTERRUPTS OR KB_FLAG_2,KB_FE ; INDICATE RESEND RECEIVED JMP K26 ; RETURN IF NOT (THIS ACK RETURNED FOR DATA) KB_INT_2: ;--------UPDATE MODE INDICATORS IF CHANGE IN STATE PUSH AX ; SAVE DATA IN CALL MAKE_LED ; GO GET MODE INDICATOR DATA BYTE MOV BL,KB_FLAG_2 ; GET PREVIOUS BITS XOR BL,AL ; SEE IF ANY DIFFERENT AND BL,07H ; ISOLATE INDICATOR BITS JZ UP0 ; IF NO CHANGE BYPASS UPDATE ; CALL SND_LED ; GO TURN ON MODE INDICATORS UP0: POP AX ; RESTORE DATA IN MOV AH,AL ; SAVE SCAN CODE IN AH ALSO ;------ TEST FOR OVERRUN SCAN CODE FROM KEYBOARD CMP AL,KB_OVER_RUN ; IS THIS AN OVERRUN CHAR JNZ K16 ; NO, TEST FOR SHIFT KEY JMP K62 ; BUFFER_FULL_BEEP ;------ TEST FOR SHIFT KEYS K16: ; TEST_SHIFT AND AL,07FH ; TURN OFF THE BREAK BIT PUSH CS POP ES ; ESTABLISH ADDRESS OF SHIFT TABLE ;------- TEST FOR SYSTEM KEY CMP AL,SYS_KEY ; IS IT THE SYSTEM KEY? JNZ K16A ; CONTINUE IF NOT ; TEST AH,080H ; CHECK IF THIS A BREAK CODE JNZ K16C ; DONT TOUCH SYSTEM INDICATOR IF TRUE ; TEST KB_FLAG_1,SYS_SHIFT ; SEE IF IN SYSTEM KEY HELD DOWN JNZ K16B ; IF YES, DONT PROCESS SYSTEM INDICATOR ; OR KB_FLAG_1,SYS_SHIFT ; INDICATE SYSTEM KEY DEPRESSED MOV AL,EOI ; END OF INTERRUPT COMMAND OUT 020H,AL ; SEND COMMAND TO INTERRUPT CONTROL PORT ; INTERRUPT-RETURN-NO-EOI MOV AL,ENA_KBD ; INSURE KEYBOARD IS ENABLED CALL SHIP_IT ; EXECUTE ENABLE MOV AX,08500H ; FUNCTION VALUE FOR MAKE OF SYSTEM KEY STI ; MAKE SURE INTERRUPTS ENABLED INT 15H ; USER INTERRUPT JMP K27A ; END PROCESSING K16B: JMP K26 ; IGNORE SYSTEM KEY ; K16C: AND KB_FLAG_1,NOT SYS_SHIFT ; TURN OFF SHIFT KEY HELD DOWN MOV AL,EOI ; END OF INTERRUPT COMMAND OUT 020H,AL ; SEND COMMAND TO INTERRUPT CONTROL PORT ; INTERRUPT-RETURN-NO-EOI MOV AL,ENA_KBD ; INSURE KEYBOARD IS ENABLED CALL SHIP_IT ; EXECUTE ENABLE MOV AX,08501H ; FUNCTION VALUE FOR BREAK OF SYSTEM KEY STI ; MAKE SURE INTERRUPTS ENABLED INT 15H ; USER INTERRUPT JMP K27A ; IGNORE SYSTEM KEY ; K16A: MOV DI,OFFSET K6 ; SHIFT KEY TABLE MOV CX,OFFSET K6L ; LENGTH REPNE SCASB ; LOOK THROUGH THE TABLE FOR A MATCH MOV AL,AH ; RECOVER SCAN CODE JE K17 ; JUMP IF MATCH FOUND JMP K25 ; IF NO MATCH, THEN SHIFT NOT FOUND ;------ SHIFT KEY FOUND K17: SUB DI,OFFSET K6+1 ; ADJUST PTR TO SCAN CODE MATCH MOV AH,CS:K7[DI] ; GET MASK INTO AH TEST AL,80H ; TEST FOR BREAK KEY JZ K17C ; BREAK_SHIFT_FOUND JMP SHORT K23 ; CONTINUE ;-------- DETERMINE SET OR TOGGLE K17C: CMP AH,SCROLL_SHIFT JAE K18 ; IF SCROLL SHIFT OR ABOVE, TOGGLE KEY ;------ PLAIN SHIFT KEY, SET SHIFT ON OR KB_FLAG,AH ; TURN ON SHIFT BIT JMP K26 ; INTERRUPT_RETURN ;------ TOGGLED SHIFT KEY, TEST FOR 1ST MAKE OR NOT K18: ; SHIFT-TOGGLE TEST KB_FLAG, CTL_SHIFT ; CHECK CTL SHIFT STATE JZ K18A ; JUMP IF NOT CTL STATE JMP K25 K18A: CMP AL,INS_KEY ; CHECK FOR INSERT KEY JNZ K22 ; JUMP IF NOT INSERT KEY TEST KB_FLAG, ALT_SHIFT ; CHECK FOR ALTERNATE SHIFT JZ K19 ; JUMP IF NOT ALTERNATE SHIFT JMP K25 ; JUMP IF ALTERNATE SHIFT K19: TEST KB_FLAG, NUM_STATE ; CHECK FOR BASE STATE JNZ K21 ; JUMP IF NUM LOCK IS ON TEST KB_FLAG, LEFT_SHIFT+RIGHT_SHIFT ; JZ K22 ; JUMP IF BASE STATE K20: ; NUMERIC ZERO, NOT INSERT KEY MOV AX, 5230H ; PUT OUT AN ASCII ZERO JMP K57 ; BUFFER_FILL K21: ; MIGHT BE NUMERIC TEST KB_FLAG, LEFT_SHIFT+RIGHT_SHIFT ; JZ K20 ; JUMP NUMERIC, NOT INSERT K22: ; SHIFT TOGGLE KEY HIT; PROCESS IT TEST AH,KB_FLAG_1 ; IS KEY ALREADY DEPRESSED JZ K22A0 ; GO IF NOT JMP SHORT K26 ; JUMP IF KEY ALREADY DEPRESSED K22A0: OR KB_FLAG_1,AH ; INDICATE THAT THE KEY IS DEPRESSED XOR KB_FLAG,AH ; TOGGLE THE SHIFT STATE ;------- TOGGLE LED IF CAPS OR NUM KEY DEPRESSED TEST AH,CAPS_SHIFT+NUM_SHIFT+SCROLL_SHIFT ; SHIFT TOGGLE? JZ K22B ; GO IF NOT PUSH AX ; SAVE SCAN CODE AND SHIFT MASK CALL SND_LED ; GO TURN MODE INDICATORS ON POP AX ; RESTORE SCAN CODE K22B: CMP AL,INS_KEY ; TEST FOR 1ST MAKE OF INSERT KEY JNE K26 ; JUMP IF NOT INSERT KEY MOV AX,INS_KEY*256 ; SET SCAN CODE INTO AH, 0 INTO AL JMP K57 ; PUT INTO OUTPUT BUFFER ;------ BREAK SHIFT FOUND K23: ; BREAK-SHIFT-FOUND CMP AH,SCROLL_SHIFT ; IS THIS A TOGGLE KEY JAE K24 ; YES, HANDLE BREAK TOGGLE NOT AH ; INVERT MASK AND KB_FLAG,AH ; TURN OFF SHIFT BIT CMP AL,ALT_KEY+80H ; IS THIS ALTERNATE SHIFT RELEASE JNE K26 ; INTERRUPT_RETURN ;------ ALTERNATE SHIFT KEY RELEASED, GET THE VALUE INTO BUFFER MOV AL,ALT_INPUT MOV AH,0 ; SCAN CODE OF 0 MOV ALT_INPUT,AH ; ZERO OUT THE FIELD CMP AL,0 ; WAS THE INPUT=0 JE K26 ; INTERRUPT_RETURN JMP K58 ; IT WASN'T, SO PUT IN BUFFER K24: ; BREAK-TOGGLE NOT AH ; INVERT MASK AND KB_FLAG_1,AH ; INDICATE NO LONGER DEPRESSED JMP SHORT K26 ; INTERRUPT_RETURN ;------ TEST FOR HOLD STATE K25: ; NO-SHIFT-FOUND CMP AL,80H ; TEST FOR BREAK KEY JAE K26 ; NOTHING FOR BREAK CHARS FROM HERE ON TEST KB_FLAG_1,HOLD_STATE ; ARE WE IN HOLD STATE JZ K28 ; BRANCH AROUND TEST IF NOT CMP AL,NUM_KEY JE K26 ; CAN'T END HOLD ON NUM_LOCK AND KB_FLAG_1,NOT HOLD_STATE ; TURN OFF THE HOLD STATE BIT K26: ; INTERRUPT-RETURN CLI ; TURN OFF INTERRUPTS MOV AL,EOI ; END OF INTERRUPT COMMAND OUT 020H,AL ; SEND COMMAND TO INTERRUPT CONTROL PORT K27: ; INTERRUPT-RETURN-NO-EOI MOV AL,ENA_KBD ; INSURE KEYBOARD IS ENABLED CALL SHIP_IT ; EXECUTE ENABLE K27A: CLI ; DISABLE INTERRUPTS POP ES ; RESTORE REGISTERS POP DS ; " POP DI ; " POP SI ; " POP DX ; " POP CX ; " POP BX ; " POP AX ; " POP BP ; " IRET ; RETURN, INTERRUPTS ON WITH FLAG CHANGE ;------ NOT IN HOLD STATE K28: ; NO-HOLD-STATE TEST KB_FLAG,ALT_SHIFT ; ARE WE IN ALTERNATE SHIFT JNZ K29 ; JUMP IF ALTERNATE SHIFT JMP K38 ; JUMP IF NOT ALTERNATE ;------ TEST FOR RESET KEY SEQUENCE (CTL ALT DEL) K29: ; TEST-RESET TEST KB_FLAG,CTL_SHIFT ; ARE WE IN CONTROL SHIFT ALSO JZ K31 ; NO RESET CMP AL,DEL_KEY ; SHIFT STATE IS THERE, TEST KEY JNE K31 ; NO-RESET ;------ CTL-ALT-DEL HAS BEEN FOUND, DO I/O CLEANUP MOV RESET_FLAG, 1234H ; SET FLAG FOR RESET FUNCTION JMP START_1 ; JUMP TO POWER ON DIAGNOSTICS ;------ ALT-INPUT-TABLE K30 LABEL BYTE DB 82,79,80,81,75,76,77 DB 71,72,73 ; 10 NUMBERS ON KEYPAD ;------ SUPER-SHIFT-TABLE DB 16,17,18,19,20,21,22,23 ; A-Z TYPEWRITER CHARS DB 24,25,30,31,32,33,34,35 DB 36,37,38,44,45,46,47,48 DB 49,50 ;------ IN ALTERNATE SHIFT, RESET NOT FOUND K31: ; NO-RESET CMP AL,57 ; TEST FOR SPACE KEY JNE K32 ; NOT THERE MOV AL,' ' ; SET SPACE CHAR JMP K57 ; BUFFER_FILL ;------ LOOK FOR KEY PAD ENTRY K32: ; ALT-KEY-PAD MOV DI,OFFSET K30 ; ALT-INPUT-TABLE MOV CX,10 ; LOOK FOR ENTRY USING KEYPAD REPNE SCASB ; LOOK FOR MATCH JNE K33 ; NO_ALT_KEYPAD SUB DI,OFFSET K30+1 ; DI NOW HAS ENTRY VALUE MOV AL,ALT_INPUT ; GET THE CURRENT BYTE MOV AH,10 ; MULTIPLY BY 10 MUL AH ADD AX,DI ; ADD IN THE LATEST ENTRY MOV ALT_INPUT,AL ; STORE IT AWAY JMP K26 ; THROW AWAY THAT KEYSTROKE ;------ LOOK FOR SUPERSHIFT ENTRY K33: ; NO-ALT-KEYPAD MOV ALT_INPUT,0 ; ZERO ANY PREVIOUS ENTRY INTO INPUT MOV CX,26 ; DI,ES ALREADY POINTING REPNE SCASB ; LOOK FOR MATCH IN ALPHABET JNE K34 ; NOT FOUND, FUNCTION KEY OR OTHER MOV AL,0 ; ASCII CODE OF ZERO JMP K57 ; PUT IT IN THE BUFFER ;------ LOOK FOR TOP ROW OF ALTERNATE SHIFT K34: ; ALT-TOP-ROW CMP AL,2 ; KEY WITH '1' ON IT JB K35 ; NOT ONE OF INTERESTING KEYS CMP AL,14 ; IS IT IN THE REGION JAE K35 ; ALT-FUNCTION ADD AH,118 ; CONVERT PSEUDO SCAN CODE TO RANGE MOV AL,0 ; INDICATE AS SUCH JMP K57 ; BUFFER_FILL ;------ TRANSLATE ALTERNATE SHIFT PSEUDO SCAN CODES K35: ; ALT-FUNCTION CMP AL,59 ; TEST FOR IN TABLE JAE K37 ; ALT-CONTINUE K36: ; CLOSE-RETURN JMP K26 ; IGNORE THE KEY K37: ; ALT-CONTINUE CMP AL,71 ; IN KEYPAD REGION JAE K36 ; IF SO, IGNORE MOV BX,OFFSET K13 ; ALT SHIFT PSEUDO SCAN TABLE JMP K63 ; TRANSLATE THAT ;------ NOT IN ALTERNATE SHIFT K38: ; NOT-ALT-SHIFT TEST KB_FLAG,CTL_SHIFT ; ARE WE IN CONTROL SHIFT JZ K44 ; NOT-CTL-SHIFT ;------ CONTROL SHIFT, TEST SPECIAL CHARACTERS ;------ TEST FOR BREAK AND PAUSE KEYS CMP AL,SCROLL_KEY ; TEST FOR BREAK JNE K39 ; NO-BREAK MOV BX,BUFFER_START ; RESET BUFFER TO EMPTY MOV BUFFER_HEAD,BX MOV BUFFER_TAIL,BX MOV BIOS_BREAK,80H ; TURN ON BIOS_BREAK BIT ;-------- ENABLE KEYBOARD MOV AL,ENA_KBD ; ENABLE KEYBOARD CALL SHIP_IT ; EXECUTE ENABLE INT 1BH ; BREAK INTERRUPT VECTOR SUB AX,AX ; PUT OUT DUMMY CHARACTER JMP K57 ; BUFFER_FILL K39: ; NO_BREAK CMP AL,NUM_KEY ; LOOK FOR PAUSE KEY JNE K41 ; NO-PAUSE OR KB_FLAG_1,HOLD_STATE ; TURN ON THE HOLD FLAG ;----- ENABLE KEYBOARD MOV AL,ENA_KBD ; ENABLE KEYBOARD CALL SHIP_IT ; EXECUTE ENABLE MOV AL,EOI ; END OF INTERRUPT TO CONTROL PORT OUT 020H,AL ; ALLOW FURTHER KEYSTROKE INTS ;------ DURING PAUSE INTERVAL, TURN CRT BACK ON CMP CRT_MODE,7 ; IS THIS THE BLACK AND WHITE CARD JE K40 ; YES, NOTHING TO DO MOV DX,03D8H ; PORT FOR COLOR CARD MOV AL,CRT_MODE_SET ; GET THE VALUE OF THE CURRENT MODE OUT DX,AL ; SET THE CRT MODE, SO THAT CRT IS ON K40: ; PAUSE-LOOP K40A: TEST KB_FLAG_1,HOLD_STATE JNZ K40 ; LOOP UNTIL FLAG TURNED OFF JMP K27A ; INTERRUPT_RETURN_NO_EOI K41: ; NO-PAUSE ;------ TEST SPECIAL CASE KEY 55 CMP AL,55 JNE K42 ; NOT-KEY-55 MOV AX,114*256 ; START/STOP PRINTING SWITCH JMP K57 ; BUFFER_FILL ;------ SET UP TO TRANSLATE CONTROL SHIFT K42: ; NOT-KEY-55 MOV BX,OFFSET K8 ; SET UP TO TRANSLATE CTL CMP AL,59 ; IS IT IN TABLE JB K56 ; YES, GO TRANSLATE CHAR ; CTL-TABLE-TRANSLATE MOV BX,OFFSET K9 ; CTL TABLE SCAN JMP K63 ; TRANSLATE_SCAN ;------ NOT IN CONTROL SHIFT K44: ; NOT-CTL-SHIFT CMP AL,71 ; TEST FOR KEYPAD REGION JAE K48 ; HANDLE KEYPAD REGION TEST KB_FLAG,LEFT_SHIFT+RIGHT_SHIFT JZ K54 ; TEST FOR SHIFT STATE ;------ UPPER CASE, HANDLE SPECIAL CASES CMP AL,15 ; BACK TAB KEY JNE K45 ; NOT-BACK-TAB MOV AX,15*256 ; SET PSEUDO SCAN CODE JMP SHORT K57 ; BUFFER_FILL K45: ; NOT-BACK-TAB CMP AL,55 ; PRINT SCREEN KEY JNE K46 ; NOT-PRINT-SCREEN ;------ ISSUE INTERRUPT TO INDICATE PRINT SCREEN FUNCTION MOV AL,ENA_KBD ; INSURE KEYBOARD IS ENABLED CALL SHIP_IT ; EXECUTE ENABLE MOV AL,EOI ; END OF CURRENT INTERRUPT OUT 020H,AL ; SO FURTHER THINGS CAN HAPPEN PUSH BP ; SAVE POINTER INT 5H ; ISSUE PRINT SCREEN INTERRUPT POP BP ; RESTORE POINTER JMP K27 ; GO BACK WITHOUT EOI OCCURRING K46: ; NOT-PRINT-SCREEN CMP AL,59 ; FUNCTION KEYS JB K47 ; NOT-UPPER-FUNCTION MOV BX,OFFSET K12 ; UPPER CASE PSEUDO SCAN CODES JMP K63 ; TRANSLATE_SCAN K47: ; NOT-UPPER-FUNCTION MOV BX,OFFSET K11 ; POINT TO UPPER CASE TABLE JMP SHORT K56 ; OK, TRANSLATE THE CHAR ;------ KEYPAD KEYS, MUST TEST NUM LOCK FOR DETERMINATION K48: ; KEYPAD-REGION TEST KB_FLAG,NUM_STATE ; ARE WE IN NUM LOCK JNZ K52 ; TEST FOR SURE TEST KB_FLAG,LEFT_SHIFT+RIGHT_SHIFT ; ARE WE IN SHIFT STATE JNZ K53 ; IF SHIFTED, REALLY NUM STATE ;------ BASE CASE FOR KEYPAD K49: ; BASE-CASE ; CMP AL,74 ; SPECIAL CASE FOR A COUPLE OF KEYS JE K50 ; MINUS CMP AL,78 JE K51 SUB AL,71 ; CONVERT ORIGIN MOV BX,OFFSET K15 ; BASE CASE TABLE JMP K64 ; CONVERT TO PSEUDO SCAN K50: MOV AX,74*256+'-' ; MINUS JMP SHORT K57 ; BUFFER_FILL K51: MOV AX,78*256+'+' ; PLUS JMP SHORT K57 ; BUFFER_FILL ;------ MIGHT BE NUM LOCK, TEST SHIFT STATUS K52: ; ALMOST-NUM-STATE TEST KB_FLAG,LEFT_SHIFT+RIGHT_SHIFT JNZ K49 ; SHIFTED TEMP OUT OF NUM STATE K53: ; REALLY NUM STATE SUB AL,70 ; CONVERT ORIGIN MOV BX,OFFSET K14 ; NUM STATE TABLE JMP SHORT K56 ; TRANSLATE_CHAR ;------ PLAIN OLD LOWER CASE K54: ; NOT-SHIFT CMP AL,59 ; TEST FOR FUNCTION KEYS JB K55 ; NOT-LOWER-FUNCTION MOV AL,0 ; SCAN CODE IN AH ALREADY JMP SHORT K57 ; BUFFER_FILL K55: ; NOT-LOWER-FUNCTION MOV BX,OFFSET K10 ; LC TABLE ;------ TRANSLATE THE CHARACTER K56: ; TRANSLATE-CHAR DEC AL ; CONVERT ORIGIN XLAT CS:K11 ; CONVERT THE SCAN CODE TO ASCII ;------ PUT CHARACTER INTO BUFFER K57: ; BUFFER_FILL CMP AL,-1 ; IS THIS AN IGNORE CHAR JE K59 ; YES, DO NOTHING WITH IT CMP AH,-1 ; LOOK FOR -1 PSEUDO SCAN JE K59 ; NEAR_INTERRUPT_RETURN ;------ HANDLE THE CAPS LOCK PROBLEM K58: ; BUFFER_FILL-NOTEST TEST KB_FLAG,CAPS_STATE ; ARE WE IN CAPS LOCK STATE JZ K61 ; SKIP IF NOT ;------ IN CAPS LOCK STATE TEST KB_FLAG,LEFT_SHIFT+RIGHT_SHIFT ; TEST FOR SHIFT STATE JZ K60 ; IF NOT SHIFT, CONVERT LOWER TO UPPER ;------ CONVERT ANY UPPER CASE TO LOWER CASE CMP AL,'A' ; FIND OUT IF ALPHABETIC JB K61 ; NOT-CAPS-STATE CMP AL,'Z' JA K61 ; NOT_CAPS STATE ADD AL,'a'-'A' ; CONVERT TO LOWER CASE JMP SHORT K61 ; NOT_CAPS_STATE K59: ; NEAR-INTERRUPT-RETURN JMP K26 ; INTERRUPT_RETURN ;------ CONVERT ANY LOWER CASE TO UPPER CASE K60: ; LOWER-TO-UPPER CMP AL,'a' ; FIND OUT IF ALPHABETIC JB K61 ; NOT_CAPS_STATE CMP AL,'z' JA K61 ; NOT CAPS STATE SUB AL,'a'-'A' ; CONVERT TO UPPER CASE K61: ; NOT-CAPS-STATE MOV BX,BUFFER_TAIL ; GET THE END POINTER TO THE BUFFER MOV SI,BX ; SAVE THE VALUE CALL K4 ; ADVANCE THE TAIL CMP BX,BUFFER_HEAD ; HAS THE BUFFER WRAPPED AROUND JE K62 ; BUFFER_FULL_BEEP MOV [SI],AX ; STORE THE VALUE MOV BUFFER_TAIL,BX ; MOVE THE POINTER UP CLI ; TURN OFF INTERRUPTS MOV AL,EOI ; END OF INTERRUPT COMMAND OUT 020H,AL ; SEND COMMAND TO INTERRUPT CONTROL PORT MOV AL,ENA_KBD ; INSURE KEYBOARD IS ENABLED CALL SHIP_IT ; EXECUTE ENABLE MOV AX,09102H ; MOVE IN POST CODE & TYPE INT 15H ; PERFORM OTHER FUNCTION JMP K27A ; INTERRUPT_RETURN ;------ TRANSLATE SCAN FOR PSEUDO SCAN CODES K63: ; TRANSLATE-SCAN SUB AL,59 ; CONVERT ORIGIN TO FUNCTION KEYS K64: ; TRANSLATE-SCAN-ORGD XLAT CS:K9 ; CTL TABLE SCAN MOV AH,AL ; PUT VALUE INTO AH MOV AL,0 ; ZERO ASCII CODE JMP K57 ; PUT IT INTO THE BUFFER KB_INT_1 ENDP K62: MOV AL,EOI ; ENABLE INTR. CTL. CHIP OUT INTA00,AL ; MOV BX,82H ; NUMBER OF CYCLES FOR 1/8 SECOND TONE IN AL,KB_CTL ; GET CONTROL INFORMATION PUSH AX ; SAVE K65: ; BEEP-CYCLE AND AL,0FCH ; TURN OFF TIMER GATE AND SPEAKER DATA JMP SHORT $+2 ; IO DELAY OUT KB_CTL,AL ; OUTPUT TO CONTROL MOV CX,0CEH ; HALF CYCLE TIME FOR TONE K66: LOOP K66 ; SPEAKER OFF OR AL,2 ; TURN ON SPEAKER BIT OUT KB_CTL,AL ; OUTPUT TO CONTROL MOV CX,0E5H ; SET UP COUNT K67: LOOP K67 ; ANOTHER HALF CYCLE DEC BX ; TOTAL TIME COUNT JNZ K65 ; DO ANOTHER CYCLE POP AX ; RECOVER CONTROL OUT KB_CTL,AL ; OUTPUT THE CONTROL JMP K27 ; EXIT ;------------------------------------------------------------------------------ ; ; SND_DATA ; ; THIS ROUTINES HANDLES TRANSMISSION OF COMMAND AND DATA BYTES ; TO THE KEYBOARD AND RECEIPT OF ACKNOWLEDGEMENTS. IT ALSO ; HANDLES ANY RETRIES IF REQUIRED ; ;------------------------------------------------------------------------------ SND_DATA PROC NEAR PUSH AX ; SAVE REGISTERS PUSH BX ; " PUSH CX MOV BH,AL ; SAVE TRANSMITTED BYTE FOR RETRIES MOV BL,3 ; LOAD RETRY COUNT SD0: CLI ; DISABLE INTERRUPTS AND KB_FLAG_2,NOT (KB_FE+KB_FA) ; CLEAR ACK AND RESEND FLAGS ;------- WAIT FOR COMMAND TO ACCEPTED SUB CX,CX ; SD5: IN AL,STATUS_PORT ; TEST AL,INPT_BUF_FULL LOOPNZ SD5 ; WAIT FOR COMMAND TO BE ACCEPTED ; MOV AL,BH ; REESTABLISH BYTE TO TRANSMIT OUT PORT_A,AL ; SEND BYTE STI ; ENABLE INTERRUPTS MOV CX,01A00H ; LOAD COUNT FOR 10 ms+ SD1: TEST KB_FLAG_2,KB_FE+KB_FA ; SEE IF EITHER BIT SET JNZ SD3 ; IF SET, SOMETHING RECEIVED GO PROCESS ; LOOP SD1 ; OTHERWISE WAIT ; SD2: DEC BL ; DECREMENT RETRY COUNT JNZ SD0 ; RETRY TRANSMISSION ; OR KB_FLAG_2,KB_ERR ; TURN ON TRANSMIT ERROR FLAG JMP SHORT SD4 ; RETRIES EXHAUSTED FORGET TRANSMISSION ; SD3: TEST KB_FLAG_2,KB_FA ; SEE IF THIS IS AN ACKNOWLEDGE JZ SD2 ; IF NOT, GO RESEND ; SD4: POP CX ; RESTORE REGISTERS POP BX ; POP AX ; * RET ; RETURN, GOOD TRANSMISSION SND_DATA ENDP ;------------------------------------------------------------------------------ ; SND_LED ; ; THIS ROUTINES TURNS ON THE MODE INDICATORS. ; ;------------------------------------------------------------------------------ SND_LED PROC NEAR CLI ; TURN OFF INTERRUPTS TEST KB_FLAG_2,KB_PR_LED ; CHECK FOR MODE INDICATOR UPDATE JNZ SL1 ; DONT UPDATE AGAIN IF UPDATE UNDERWAY ; OR KB_FLAG_2,KB_PR_LED ; TURN ON UPDATE IN PROCESS MOV AL,EOI ; END OF INTERRUPT COMMAND OUT 020H,AL ; SEND COMMAND TO INTERRUPT CONTROL PORT JMP SHORT SL0 ; GO SEND MODE INDICATOR COMMAND ; SND_LED1: CLI ; TURN OFF INTERRUPTS TEST KB_FLAG_2,KB_PR_LED ; CHECK FOR MODE INDICATOR UPDATE JNZ SL1 ; DONT UPDATE AGAIN IF UPDATE UNDERWAY ; OR KB_FLAG_2,KB_PR_LED ; TURN ON UPDATE IN PROCESS SL0: MOV AL,LED_CMD ; LED CMD BYTE CALL SND_DATA ; SEND DATA TO KEYBOARD CLI ; CALL MAKE_LED ; GO FORM INDICATOR DATA BYTE AND KB_FLAG_2,0F8H ; CLEAR MODE INDICATOR BITS OR KB_FLAG_2,AL ; SAVE PRESENT INDICATORS STATES FOR NEXT TIME TEST KB_FLAG_2,KB_ERR ; TRANSMIT ERROR DETECTED JNZ SL2 ; IF YES, BYPASS SECOND BYTE TRANSMISSION ; CALL SND_DATA ; SEND DATA TO KEYBOARD CLI ; TURN OFF INTERRUPTS TEST KB_FLAG_2,KB_ERR ; TRANSMIT ERROR DETECTED JZ SL3 ; IF NOT, DONT SEND AN ENABLE COMMAND ; SL2: MOV AL,KB_ENABLE ; GET KEYBOARD CSA ENABLE COMMAND CALL SND_DATA ; SEND DATA TO KEYBOARD CLI ; TURN OFF INTERRUPTS SL3: AND KB_FLAG_2,NOT(KB_PR_LED+KB_ERR) ; TURN OFF MODE INDICATOR ; UPDATE AND TRANSMIT ERROR FLAG SL1: STI ; ENABLE INTERRUPTS RET ; RETURN TO CALLER SND_LED ENDP ;------------------------------------------------------------------------------ ; MAKE_LED ; ; THIS ROUTINES FORMS THE DATA BYTE NECESSARY TO TURN ON/OFF ; THE MODE INDICATORS ; ;------------------------------------------------------------------------------ MAKE_LED PROC NEAR PUSH CX ; SAVE CX MOV AL,KB_FLAG ; GET CAPS & NUM LOCK INDICATORS AND AL,CAPS_STATE+NUM_STATE+SCROLL_STATE ; ISOLATE INDICATORS MOV CL,4 ; SHIFT COUNT ROL AL,CL ; SHIFT BITS OVER TO TURN ON INDICATORS AND AL,07H ; MAKE SURE ONLY MODE BITS ON POP CX ; RET ; RETURN TO CALLER MAKE_LED ENDP ;------------------------------------------------------------------------------ ; SHIP_IT ; ; THIS ROUTINES HANDLES TRANSMISSION OF COMMAND AND DATA BYTES ; TO THE KEYBOARD CONTROLLER. ; ;------------------------------------------------------------------------------ SHIP_IT PROC NEAR PUSH AX ; SAVE DATA TO SEND ;------- WAIT FOR COMMAND TO ACCEPTED CLI ; DISABLE INTERRUPTS SUB CX,CX ; CLEAR COUNTER S10: IN AL,STATUS_PORT ; TEST AL,INPT_BUF_FULL LOOPNZ S10 ; WAIT FOR COMMAND TO BE ACCEPTED ; POP AX ; GET DATA TO SEND OUT STATUS_PORT,AL ; SEND TO KEYBOARD CONTROLLER STI ; ENABLE INTERRUPTS AGAIN RET ; RETURN TO CALLER SHIP_IT ENDP CODE ENDS END