;------------------------------------------------------------------------------;
;  KBD_FIX.COM  from Socha's Toolbox by John Socha                             ;
;    Ref: Softalk PC Nov 83 pg 117                                             ;
;         Softalk PC Jan 84 pg 107  (corrections, incorporated below)          ;
;                                                                              ;
; Note that type ahead buffer can be cleared by pressing Alt and Ctrl together.;
;------------------------------------------------------------------------------;
VECTORS                 SEGMENT         AT 0H
        ORG             9H*4
KEYBOARD_INT_VECTOR                     LABEL           DWORD
        ORG             16H*4
KEYBOARD_IO_VECTOR                      LABEL           DWORD
VECTORS                 ENDS

ROM_BIOS_DATA           SEGMENT         AT 40H
        ORG             17H
KBD_FLG                 DB              ?
        ORG             1AH
ROM_BUFFER_HEAD         DW              ?
ROM_BUFFER_TAIL         DW              ?
KB_BUFFER               DW              16 DUP          (?)
KB_BUFFER_END           LABEL           WORD
ROM_BIOS_DATA           ENDS

CODE_SEG                SEGMENT
        ASSUME          CS:CODE_SEG
        ORG             100H
BEGIN:  JMP             INIT_VECTORS    ;Initialize vectors and attach to DOS

ROM_KEYBOARD_INT        DD              ;Address for ROM routine
ROM_KEYBOARD_IO         DD
BUFFER_HEAD             DW              OFFSET KEYBOARD_BUFFER
BUFFER_TAIL             DW              OFFSET KEYBOARD_BUFFER
KEYBOARD_BUFFER         DW              160 DUP (0)   ;159 character buffer
KEYBOARD_BUFFER_END     LABEL           WORD

;-----------------------------------------------------------------;
; This procedure sends a short beep when the buffer fills.        ;
;-----------------------------------------------------------------;

KB_CONTROL      EQU     61H     ;Control bits for keyboard (and speaker)
ERROR_BEEP      PROC    NEAR
                PUSH    AX
                PUSH    BX
                PUSH    CX
                PUSHF                   ;Save the old interrupt enable flag
                CLI                     ;Turn off beep during interrupt
                MOV     BX,30           ;Number of cycles for 1/8 second tone
                IN      AL,KB_CONTROL
                PUSH    AX
START_OF_ONE_CYCLE:
                AND     AL,0FCH
                OUT     KB_CONTROL,AL
                MOV     CX,60           ;Delay for one half cycle
OFF_LOOP:
                LOOP    OFF_LOOP
                OR      AL,2            ;Turn on the speaker
                OUT     KB_CONTROL,AL
                MOV     CX,60           ;Delay for second half cycle
ON_LOOP:
                LOOP    ON_LOOP
                DEC     BX              ;200 cycles yet
                JNZ     START_OF_ONE_CYCLE
                POP     AX              ;Recover old keyboard information
                OUT     KB_CONTROL,AL
                POPF                    ;Restore interrupt flag
                POP     CX
                POP     BX
                POP     AX
                RET
ERROR_BEEP      ENDP

;---------------------------------------------------------------------;
;This procedure checks the ROM keyboard buffer to see if some program ;
;tried to clear this buffer.  We know it's been cleared when the ROM  ;
;tail and header overlap.  Normally, the new procedure below keep the ;
;dummy character, word 0, in the buffer.                              ;
;                                                                     ;
;Uses BX,DS                                                           ;
;Writes:        BUFFER_HEAD, BUFFER_TAIL, ROM_BUFFER_HEAD             ;
;               ROM_BUFFER_TAIL                                       ;
;Reads:         KEYBOARD_BUFFER, KB_BUFFER                            ;
;---------------------------------------------------------------------;
CHECK_CLEAR_BUFFER    PROC     NEAR
                ASSUME  DS:ROM_BIOS_DATA
                MOV     BX,ROM_BIOS_DATA    ;Establish pointer to BIOS data
                MOV     DS,BX
                CLI                         ;Turn off interrupts during this check
                MOV     BX,ROM_BUFFER_HEAD  ;Check to see if buffer is cleared
                CMP     BX,ROM_BUFFER_TAIL  ;Is the buffer empty?
                JNE     BUFFER_OK           ;No, then everything is alright
                                            ;Yes, then clear the internal buffer
                MOV     BX,OFFSET KB_BUFFER ;Reset the buffer with word 0 in buffer
                MOV     ROM_BUFFER_HEAD,BX
                ADD     BX,2
                MOV     ROM_BUFFER_TAIL,BX
                ASSUME  DS:CODE_SEG
                MOV     BX,CS
                MOV     DS,BX
                MOV     BX,OFFSET KEYBOARD_BUFFER  ;Reset internal buffer
                MOV     BUFFER_HEAD,BX
                MOV     BUFFER_TAIL,BX
BUFFER_OK:
                ASSUME  DS:CODE_SEG
                STI                     ;Interrupts back on
                RET
CHECK_CLEAR_BUFFER   ENDP

;------------------------------------------------------------------------------;
;This procedure intercepts the keyboard interrupt and moves any new            ;
;charcters to the internal, 80 character, buffer.                              ;
;------------------------------------------------------------------------------;

INTERCEPT_KEYBOARD_INT PROC     NEAR
                ASSUME  DS:NOTHING
                PUSH    DS
                PUSH    SI
                PUSH    BX
                PUSH    AX
                CALL    CHECK_CLEAR_BUFFER   ;Check for buffer cleared
                PUSHF
                CALL    ROM_KEYBOARD_INT     ;Read scan code with BIOS routines.
;------         Transfer any charcters to internal buffer

                ASSUME  DS:ROM_BIOS_DATA
                MOV     BX,ROM_BIOS_DATA
                MOV     DS,BX
                MOV     SI,BUFFER_TAIL
                MOV     BX,ROM_BUFFER_HEAD    ;Check if real character in buffer
                ADD     BX,2                  ;Skip over dummy character
                CMP     BX,OFFSET KB_BUFFER_END
                JB      DONT_WRAP             ;No need to wrap the pointer
                MOV     BX,OFFSET KB_BUFFER   ;Wrap the pointer
DONT_WRAP:
                CMP     BX,ROM_BUFFER_TAIL    ;Is there a real character?
                JE      NO_NEW_CHARACTERS     ;No, then return to caller
                MOV     AX,[BX]          ;Yes, move character to internal buffer
                MOV     CS:[SI],AX
                ADD     SI,2                  ;Move to next position
                CMP     SI,OFFSET KEYBOARD_BUFFER_END
                JB      NOT_AT_END
                MOV     SI,OFFSET KEYBOARD_BUFFER
NOT_AT_END:
                CMP     SI,BUFFER_HEAD      ;Buffer overrun?
                JNE     WRITE_TO_BUFFER     ;Yes, beef and throw out character
                CALL    ERROR_BEEP
                JMP     SHORT NOT_AT_KB_END
WRITE_TO_BUFFER:
                MOV     BUFFER_TAIL,SI
NOT_AT_KB_END:
                MOV     ROM_BUFFER_HEAD,BX

NO_NEW_CHARACTERS:
;---------      See if CTRL + ALT pushed and clear buffer if so

                MOV     AL,KBD_FLG         ;Get status of shift keys into AL
                AND     AL,0CH             ;Isolate Alt and Ctrl shift flags
                CMP     AL,0CH             ;Are both the Ctrl and Alt keys down?
                JNE     DONT_CLEAR_BUFFER  ;No, then don't clear the buffer
                MOV     AX,BUFFER_TAIL     ;Yes, then clear the buffer
                MOV     BUFFER_HEAD,AX

DONT_CLEAR_BUFFER:
                POP     AX
                POP     BX
                POP     SI
                POP     DS
                IRET
INTERCEPT_KEYBOARD_INT  ENDP

;------------------------------------------------------------------------------;
;This procedure replaces the ROM BIOS routines for reading a character         ;
;------------------------------------------------------------------------------;

INTERCEPT_KEYBOARD_IO   PROC   FAR
        STI                          ;Interrupts back on
        PUSH    DS                   ;Save current DS
        PUSH    BX                   ;Save BX temporarily
        CALL    CHECK_CLEAR_BUFFER   ;Check for buffer cleared
        MOV     BX,CS                ;Establish pointer to data area
        MOV     DS,BX
        OR      AH,AH                ;AH=0?
        JZ      READ_CHARACTER       ;Yes, read a character
        CMP     AH,1                 ;AH=1?      **************
        JZ      READ_STATUS          ;Yes, return the status
        POP     BX                   ;Let the ROM BIOS handle other functions
        POP     DS
        ASSUME  DS:NOTHING
        JMP     ROM_KEYBOARD_IO      ;Call ROM BIOS for other functions
                                     ;Read the key
        ASSUME  DS:CODE_SEG
READ_CHARACTER:                      ;ASCII read
        STI                          ;Interrupts back on during loop
        NOP                          ;Allow an interrupt to occur
        CLI                          ;Interrupts back off
        MOV     BX,BUFFER_HEAD       ;Get pointer to head of buffer
        CMP     BX,BUFFER_TAIL       ;Test end of buffer
        JE      READ_CHARACTER       ;Loop until something in buffer
        MOV     AX,[BX]              ;Get scan code and ASCII code
        ADD     BX,2                 ;Move to next word in buffer
        CMP     BX,OFFSET KEYBOARD_BUFFER_END   ;At end of buffer?
        JNE     SAVE_POINTER         ;No, continue
        MOV     BX,OFFSET KEYBOARD_BUFFER  ;Yes, reset to buffer start
SAVE_POINTER:
        MOV     BUFFER_HEAD,BX       ;Store value in variable
        POP     BX
        POP     DS
        IRET                         ;Return to caller
;------------ASCII status

READ_STATUS:
        CLI                          ;Interrupts off
        MOV     BX,BUFFER_HEAD       ;Get head pointer
        CMP     BX,BUFFER_TAIL       ;If equal (ZF=1) then nothing there
        MOV     AX,[BX]
        STI                          ;Interrupts back on
        POP     BX                   ;Recover registers
        POP     DS
        RET     2                    ;Throw away flags
INTERCEPT_KEYBOARD_IO     ENDP

;------------------------------------------------------------------------------;
;This procedure initializes the interrupt vectors                              ;
;------------------------------------------------------------------------------;
INIT_VECTORS    PROC    NEAR
        ASSUME  DS:VECTORS
        PUSH    DS                      ;Save old Data Segment
        MOV     AX,VECTORS              ;Set up the data segment for vectors
        MOV     DS,AX
        CLI                             ;Don't allow interrupts
        MOV     AX,KEYBOARD_INT_VECTOR  ;Save addresses of BIOS routines
        MOV     ROM_KEYBOARD_INT,AX
        MOV     AX,KEYBOARD_INT_VECTOR[2]
        MOV     ROM_KEYBOARD_INT[2],AX
                                        ;Set up new KEYBOARD_INT vector
        MOV     KEYBOARD_INT_VECTOR,OFFSET INTERCEPT_KEYBOARD_INT
        MOV     KEYBOARD_INT_VECTOR[2],CS
        STI                             ;Allow interrupts again
                                        ;Set up keyboard IO vector
        MOV     AX,KEYBOARD_IO_VECTOR
        MOV     ROM_KEYBOARD_IO,AX
        MOV     AX,KEYBOARD_IO_VECTOR[2]
        MOV     ROM_KEYBOARD_IO[2],AX
        MOV     KEYBOARD_IO_VECTOR,OFFSET INTERCEPT_KEYBOARD_IO
        MOV     KEYBOARD_IO_VECTOR[2],CS
                                        ;Now set up the keyboard buffer, etc.

       ASSUME  DS:ROM_BIOS_DATA
        MOV     AX,ROM_BIOS_DATA
        MOV     DS,AX
        CLI                             ;Don't allow interrupts
        MOV     BX,OFFSET KB_BUFFER
        MOV     ROM_BUFFER_HEAD,BX
        MOV     WORD PTR [BX],0
        ADD     BX,2
        MOV     ROM_BUFFER_TAIL,BX
        STI                             ;Allow interrupts again

        MOV     DX,OFFSET INIT_VECTORS  ;End of resident portion
        INT     27H                     ;Terminate but stay resident
INIT_VECTORS    ENDP

CODE_SEG        ENDS
        END     BEGIN

¸F PšØÈéÐ¸G PšØÈéÄ‹vä¡Ò‰Dn¡Ò‰Dpö-Ót‹Dp÷Ø‰Dpé£‹vä¡Ò‰Dné—‹vä¡Ò‰Dp