'=========================================================================== ' Subject: KEYBOARD ISR Date: 06-22-96 (22:14) ' Author: Steven Sensarn Code: QB, QBasic, PDS ' Origin: comp.lang.basic.misc Packet: KEYBOARD.ABC '=========================================================================== 'Here is a nice program that uses its own ISR to handle keyboard I/O. 'Normally, BIOS handles interrupt calls from the keyboard. Unfortunately, 'BIOS is relatively slow and often cannot catch up with the user pressing the 'keys on the keyboard. This results in a keyboard buffer overflow and the PC 'starts beeping (it's happend to all of us). To correct this error, the 'programmer writes his own ISR (interrupt service routine) and replaces the 'old ISR with it. The keyboard is connected to IRQ1 (or it should be--if 'you're having keyboard problems, that might be the reason). Therefore, the 'ISR must be connected to interrupt 9h (IRQ1's alias). Enjoy! DECLARE SUB SETVECT (S AS INTEGER, O AS INTEGER, I AS INTEGER) DECLARE SUB GETVECT (S AS INTEGER, O AS INTEGER, I AS INTEGER) DECLARE SUB KEYBOARD.IN (OLDSEG AS INTEGER, OLDOFF AS INTEGER) DECLARE SUB KEYBOARD.OUT (OLDSEG AS INTEGER, OLDOFF AS INTEGER) '$STATIC DIM RAWKEY AS INTEGER DIM OLD.ISR.SEG AS INTEGER, OLD.ISR.OFF AS INTEGER CALL KEYBOARD.IN(OLD.ISR.SEG, OLD.ISR.OFF) DO PRINT RAWKEY 'DISPLAY SCANCODE OF LAST KEY PRESSED 'NOTICE THAT EACH KEY HAS A PRESS AND RELEASE CODE LOOP UNTIL RAWKEY = 1 CALL KEYBOARD.OUT(OLD.ISR.SEG, OLD.ISR.OFF) SUB GETVECT (S AS INTEGER, O AS INTEGER, I AS INTEGER) 'GETVECT RETURNS THE ADDRESS OF A FUNCTION POINTED TO IN THE 'INTERRUPT VECTOR TABLE (STARTS AT 0000:0000H) STATIC ASM AS STRING 'THE CODE FOR GETVECT STATIC INI AS INTEGER 'USED TO DETECT WHETHER GETVECT HAS PREVIOUSLY 'BEEN CALLED IF INI = 0 THEN 'CREATE ML FUNCTION IF NOT ALREADY CREATED ASM = ASM + CHR$(&H55) 'PUSH BP ASM = ASM + CHR$(&H89) + CHR$(&HE5) 'MOV BP,SP ASM = ASM + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H6) 'MOV BX,[BP+06] ASM = ASM + CHR$(&H8A) + CHR$(&H7) 'MOV AL,[BX] ASM = ASM + CHR$(&HB4) + CHR$(&H35) 'MOV AH,35 ASM = ASM + CHR$(&HCD) + CHR$(&H21) 'INT 21 ASM = ASM + CHR$(&H53) 'PUSH BX ASM = ASM + CHR$(&H8B) + CHR$(&H5E) + CHR$(&HA) 'MOV BX,[BP+0A] ASM = ASM + CHR$(&H8C) + CHR$(&H7) 'MOV [BX],ES ASM = ASM + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H8) 'MOV BX,[BP+08] ASM = ASM + CHR$(&H58) 'POP AX ASM = ASM + CHR$(&H89) + CHR$(&H7) 'MOV [BX],AX ASM = ASM + CHR$(&H5D) 'POP BP ASM = ASM + CHR$(&HCA) + CHR$(&H6) + CHR$(&H0) 'RETF 0006 INI = 1 'FLAG CREATION END IF DEF SEG = VARSEG(ASM) CALL ABSOLUTE(S, O, I, SADD(ASM)) 'RUN FUNCTION END SUB SUB KEYBOARD.IN (OLDSEG AS INTEGER, OLDOFF AS INTEGER) SHARED RAWKEY AS INTEGER 'GLOBAL VARIABLE HOLDS SCANCODE DIM SGL AS INTEGER, SGH AS INTEGER 'SEGMENT OF RAWKEY DIM OFL AS INTEGER, OFH AS INTEGER 'OFFSET OF RAWKEY DIM BYTE AS STRING * 1 'USED TO ACTIVATE IRQ 1 IN PIC STATIC ASM AS STRING 'HOLDS ISR SGL = VARSEG(RAWKEY) AND &HFF 'LOAD LOW "BYTE" SEGMENT SGH = INT(VARSEG(RAWKEY) / 256) AND &HFF 'LOAD HIGH "BYTE" SEGMENT OFL = VARPTR(RAWKEY) AND &HFF 'LOAD LOW "BYTE" OFFSET OFH = INT(VARPTR(RAWKEY) / 256) AND &HFF 'LOAD HIGH "BYTE" OFFSET 'THIS IS THE ISR. IT READS A SCANCODE FROM THE KEYBOARD BUFFER 'AND RESETS IT. THE BEST PART IS, BIOS CAN'T TOUCH IT! ASM = "" ASM = ASM + CHR$(&H52) 'PUSH DX ASM = ASM + CHR$(&H51) 'PUSH CX ASM = ASM + CHR$(&H53) 'PUSH BX ASM = ASM + CHR$(&H50) 'PUSH AX ASM = ASM + CHR$(&H6) 'PUSH ES ASM = ASM + CHR$(&H57) 'PUSH DI ASM = ASM + CHR$(&H1E) 'PUSH DS ASM = ASM + CHR$(&H56) 'PUSH SI ASM = ASM + CHR$(&HFB) 'STI ASM = ASM + CHR$(&HBA) + CHR$(&H60) + CHR$(&H0) 'MOV DX,0060 ASM = ASM + CHR$(&HEC) 'IN AL,DX ASM = ASM + CHR$(&H30) + CHR$(&HE4) 'XOR AH,AH ASM = ASM + CHR$(&HBA) + CHR$(SGL) + CHR$(SGH) 'MOV DX,SEG RAWKEY ASM = ASM + CHR$(&H8E) + CHR$(&HDA) 'MOV DS,DX ASM = ASM + CHR$(&HBE) + CHR$(OFL) + CHR$(OFH) 'MOV SI,OFFSET RAWKEY ASM = ASM + CHR$(&H88) + CHR$(&H4) 'MOV [SI],AL ASM = ASM + CHR$(&HBA) + CHR$(&H61) + CHR$(&H0) 'MOV DX,0061 ASM = ASM + CHR$(&HEC) 'IN AL,DX ASM = ASM + CHR$(&HC) + CHR$(&H82) 'OR AL,82 ASM = ASM + CHR$(&HEE) 'OUT DX,AL ASM = ASM + CHR$(&H24) + CHR$(&H7F) 'AND AL,7F ASM = ASM + CHR$(&HEE) 'OUT DX,AL ASM = ASM + CHR$(&HB0) + CHR$(&H20) 'MOV AL,20 ASM = ASM + CHR$(&HBA) + CHR$(&H20) + CHR$(&H0) 'MOV DX,0020 ASM = ASM + CHR$(&HEE) 'OUT DX,AL ASM = ASM + CHR$(&H5E) 'POP SI ASM = ASM + CHR$(&H1F) 'POP DS ASM = ASM + CHR$(&H5F) 'POP DI ASM = ASM + CHR$(&H7) 'POP ES ASM = ASM + CHR$(&H58) 'POP AX ASM = ASM + CHR$(&H5B) 'POP BX ASM = ASM + CHR$(&H59) 'POP CX ASM = ASM + CHR$(&H5A) 'POP DX ASM = ASM + CHR$(&HCF) 'IRET BYTE = CHR$(INP(&H21)) 'LOAD IRQ ENABLE REGISTER IN PIC OUT &H21, (ASC(BYTE) AND (255 XOR 2)) 'CLEAR BIT 2 (IRQ 1) CALL GETVECT(OLDSEG, OLDOFF, &H9) 'LOAD OLD ISR CALL SETVECT(VARSEG(ASM), SADD(ASM), &H9) 'STORE NEW ISR END SUB SUB KEYBOARD.OUT (OLDSEG AS INTEGER, OLDOFF AS INTEGER) CALL SETVECT(OLDSEG, OLDOFF, &H9) 'RESTORE OLD ISR END SUB SUB SETVECT (S AS INTEGER, O AS INTEGER, I AS INTEGER) 'SETVECT CHANGES THE ADDRESSES IN THE INTERRUPT VECTOR TABLE 'TO POINT TO NEW FUNCTIONS STATIC ASM AS STRING 'HOLDS THE SETVECT FUNCTION STATIC INI AS INTEGER 'USED TO TEST WHETHER OR NOT FUNCTION HAS PREVOUSLY 'BEEN CALLED IF INI = 0 THEN 'CREATE FUNCTION IF NOT ALREADY CREATED ASM = "" ASM = ASM + CHR$(&H55) 'PUSH BP ASM = ASM + CHR$(&H89) + CHR$(&HE5) 'MOV BP,SP ASM = ASM + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H8) 'MOV BX,[BP+08] ASM = ASM + CHR$(&H8B) + CHR$(&H17) 'MOV DX,[BX] ASM = ASM + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H6) 'MOV BX,[BP+06] ASM = ASM + CHR$(&H8A) + CHR$(&H7) 'MOV AL,[BX] ASM = ASM + CHR$(&H8B) + CHR$(&H5E) + CHR$(&HA) 'MOV BX,[BP+0A] ASM = ASM + CHR$(&H1E) 'PUSH DS ASM = ASM + CHR$(&H8E) + CHR$(&H1F) 'MOV DS,[BX] ASM = ASM + CHR$(&HB4) + CHR$(&H25) 'MOV AH,25 ASM = ASM + CHR$(&HCD) + CHR$(&H21) 'INT 21 ASM = ASM + CHR$(&H1F) 'POP DS ASM = ASM + CHR$(&H5D) 'POP BP ASM = ASM + CHR$(&HCA) + CHR$(&H6) + CHR$(&H0) 'RETF 0006 INI = 1 'FLAG CREATION END IF DEF SEG = VARSEG(ASM) CALL ABSOLUTE(S, O, I, SADD(ASM)) 'RUN SETVECT END SUB