The Art of
ASSEMBLY LANGUAGE PROGRAMMING

Chapter Twenty (Part 3)

Table of Content

Chapter Twenty (Part 5) 

CHAPTER TWENTY:
THE PC KEYBOARD (Part 4)
20.5 - The Keyboard Interrupt Service Routine
20.5 The Keyboard Interrupt Service Routine

The int 16h ISR is the interface between application programs and the keyboard. In a similar vein, the int 9 ISR is the interface between the keyboard hardware and the int 16h ISR. It is the job of the int 9 ISR to process keyboard hardware interrupts, convert incoming scan codes to scan/ASCII code combinations and place them in the typeahead buffer, and process other messages the keyboard generates.

To convert keyboard scan codes to scan/ASCII codes, the int 9 ISR must keep track of the current state of the modifier keys. When a scan code comes along, the int 9 ISR can use the xlat instruction to translate the scan code to an ASCII code using a table int 9 selects on the basis of the modifier flags. Another important issue is that the int 9 handler must handle special key sequences like ctrl-alt-del (reset) and PrtSc. The following assembly code provides a simple int 9 handler for the keyboard. It does not support alt-Keypad ASCII code entry or a few other minor features, but it does support almost everything you need for a keyboard interrupt service routine. Certainly it demonstrates all the techniques you need to know when programming the keyboard.

; INT9.ASM
;
; A short TSR to provide a driver for the keyboard hardware interrupt.
;
; Note that this code does not patch into int 2Fh (multiplex interrupt)
; nor can you remove this code from memory except by rebooting.
; If you want to be able to do these two things (as well as check for
; a previous installation), see the chapter on resident programs. Such
; code was omitted from this program because of length constraints.
;
;
; cseg and EndResident must occur before the standard library segments!

cseg            segment para public 'code'
OldInt9         dword   ?
cseg            ends

; Marker segment, to find the end of the resident section.

EndResident     segment para public 'Resident'
EndResident     ends

                .xlist
                include         stdlib.a
                includelib      stdlib.lib
                .list


NumLockScan     equ     45h
ScrlLockScan    equ     46h
CapsLockScan    equ     3ah
CtrlScan        equ     1dh
AltScan         equ     38h
RShiftScan      equ     36h
LShiftScan      equ     2ah
InsScanCode     equ     52h
DelScanCode     equ     53h

; Bits for the various modifier keys

RShfBit         equ     1
LShfBit         equ     2
CtrlBit         equ     4
AltBit          equ     8
SLBit           equ     10h
NLBit           equ     20h
CLBit           equ     40h
InsBit          equ     80h


KbdFlags        equ     <byte ptr ds:[17h]>
KbdFlags2       equ     <byte ptr ds:[18h]>
KbdFlags3       equ     <byte ptr ds:[96h]>
KbdFlags4       equ     <byte ptr ds:[97h]>

byp             equ     <byte ptr>


cseg            segment para public 'code'
                assume  ds:nothing

; Scan code translation table.
; The incoming scan code from the keyboard selects a row.
; The modifier status selects the column.
; The word at the intersection of the two is the scan/ASCII code to
; put into the PC's type ahead buffer.
; If the value fetched from the table is zero, then we do not put the
; character into the type ahead buffer.
;
;               norm   shft   ctrl   alt    num   caps   shcap  shnum

ScanXlat word   0000h, 0000h, 0000h, 0000h, 0000h, 0000h, 0000h, 0000h
        word    011bh, 011bh, 011bh, 011bh, 011bh, 011bh, 011bh, 011bh ;ESC
        word    0231h, 0231h, 0000h, 7800h, 0231h, 0231h, 0231h, 0321h ;1 !
        word    0332h, 0340h, 0300h, 7900h, 0332h, 0332h, 0332h, 0332h ;2 @
        word    0433h, 0423h, 0000h, 7a00h, 0433h, 0433h, 0423h, 0423h ;3 #
        word    0534h, 0524h, 0000h, 7b00h, 0534h, 0534h, 0524h, 0524h ;4 $
        word    0635h, 0625h, 0000h, 7c00h, 0635h, 0635h, 0625h, 0625h ;5 %
        word    0736h, 075eh, 071eh, 7d00h, 0736h, 0736h, 075eh, 075eh ;6 ^

        word    0837h, 0826h, 0000h, 7e00h, 0837h, 0837h, 0826h, 0826h ;7 &
        word    0938h, 092ah, 0000h, 7f00h, 0938h, 0938h, 092ah, 092ah ;8 *
        word    0a39h, 0a28h, 0000h, 8000h, 0a39h, 0a39h, 0a28h, 0a28h ;9 (
        word    0b30h, 0b29h, 0000h, 8100h, 0b30h, 0b30h, 0b29h, 0b29h ;0 )
        word    0c2dh, 0c5fh, 0000h, 8200h, 0c2dh, 0c2dh, 0c5fh, 0c5fh ;- _
        word    0d3dh, 0d2bh, 0000h, 8300h, 0d3dh, 0d3dh, 0d2bh, 0d2bh ;= +
        word    0e08h, 0e08h, 0e7fh, 0000h, 0e08h, 0e08h, 0e08h, 0e08h ;bksp
        word    0f09h, 0f00h, 0000h, 0000h, 0f09h, 0f09h, 0f00h, 0f00h ;Tab

;               norm   shft   ctrl   alt    num   caps   shcap  shnum
        word    1071h, 1051h, 1011h, 1000h, 1071h, 1051h, 1051h, 1071h ;Q
        word    1177h, 1057h, 1017h, 1100h, 1077h, 1057h, 1057h, 1077h ;W
        word    1265h, 1245h, 1205h, 1200h, 1265h, 1245h, 1245h, 1265h ;E
        word    1372h, 1352h, 1312h, 1300h, 1272h, 1252h, 1252h, 1272h ;R
        word    1474h, 1454h, 1414h, 1400h, 1474h, 1454h, 1454h, 1474h ;T
        word    1579h, 1559h, 1519h, 1500h, 1579h, 1559h, 1579h, 1559h ;Y
        word    1675h, 1655h, 1615h, 1600h, 1675h, 1655h, 1675h, 1655h ;U
        word    1769h, 1749h, 1709h, 1700h, 1769h, 1749h, 1769h, 1749h ;I

        word    186fh, 184fh, 180fh, 1800h, 186fh, 184fh, 186fh, 184fh ;O
        word    1970h, 1950h, 1910h, 1900h, 1970h, 1950h, 1970h, 1950h ;P
        word    1a5bh, 1a7bh, 1a1bh, 0000h, 1a5bh, 1a5bh, 1a7bh, 1a7bh ;[ {
        word    1b5dh, 1b7dh, 1b1dh, 0000h, 1b5dh, 1b5dh, 1b7dh, 1b7dh ;] }
        word    1c0dh, 1c0dh, 1c0ah, 0000h, 1c0dh, 1c0dh, 1c0ah, 1c0ah ;enter
        word    1d00h, 1d00h, 1d00h, 1d00h, 1d00h, 1d00h, 1d00h, 1d00h ;ctrl
        word    1e61h, 1e41h, 1e01h, 1e00h, 1e61h, 1e41h, 1e61h, 1e41h ;A
        word    1f73h, 1f5eh, 1f13h, 1f00h, 1f73h, 1f53h, 1f73h, 1f53h ;S

;               norm   shft   ctrl   alt    num   caps   shcap  shnum
        word    2064h, 2044h, 2004h, 2000h, 2064h, 2044h, 2064h, 2044h ;D
        word    2166h, 2146h, 2106h, 2100h, 2166h, 2146h, 2166h, 2146h ;F
        word    2267h, 2247h, 2207h, 2200h, 2267h, 2247h, 2267h, 2247h ;G
        word    2368h, 2348h, 2308h, 2300h, 2368h, 2348h, 2368h, 2348h ;H
        word    246ah, 244ah, 240ah, 2400h, 246ah, 244ah, 246ah, 244ah ;J
        word    256bh, 254bh, 250bh, 2500h, 256bh, 254bh, 256bh, 254bh ;K
        word    266ch, 264ch, 260ch, 2600h, 266ch, 264ch, 266ch, 264ch ;L
        word    273bh, 273ah, 0000h, 0000h, 273bh, 273bh, 273ah, 273ah ;; :

        word    2827h, 2822h, 0000h, 0000h, 2827h, 2827h, 2822h, 2822h ;' "
        word    2960h, 297eh, 0000h, 0000h, 2960h, 2960h, 297eh, 297eh ;` ~
        word    2a00h, 2a00h, 2a00h, 2a00h, 2a00h, 2a00h, 2a00h, 2a00h ;LShf
        word    2b5ch, 2b7ch, 2b1ch, 0000h, 2b5ch, 2b5ch, 2b7ch, 2b7ch ;\ |
        word    2c7ah, 2c5ah, 2c1ah, 2c00h, 2c7ah, 2c5ah, 2c7ah, 2c5ah ;Z
        word    2d78h, 2d58h, 2d18h, 2d00h, 2d78h, 2d58h, 2d78h, 2d58h ;X
        word    2e63h, 2e43h, 2e03h, 2e00h, 2e63h, 2e43h, 2e63h, 2e43h ;C
        word    2f76h, 2f56h, 2f16h, 2f00h, 2f76h, 2f56h, 2f76h, 2f56h ;V

;               norm   shft   ctrl   alt    num   caps   shcap  shnum
        word    3062h, 3042h, 3002h, 3000h, 3062h, 3042h, 3062h, 3042h ;B
        word    316eh, 314eh, 310eh, 3100h, 316eh, 314eh, 316eh, 314eh ;N
        word    326dh, 324dh, 320dh, 3200h, 326dh, 324dh, 326dh, 324dh ;M
        word    332ch, 333ch, 0000h, 0000h, 332ch, 332ch, 333ch, 333ch ;, <
        word    342eh, 343eh, 0000h, 0000h, 342eh, 342eh, 343eh, 343eh ;. >
        word    352fh, 353fh, 0000h, 0000h, 352fh, 352fh, 353fh, 353fh ;/ ?
        word    3600h, 3600h, 3600h, 3600h, 3600h, 3600h, 3600h, 3600h ;rshf
        word    372ah, 0000h, 3710h, 0000h, 372ah, 372ah, 0000h, 0000h ;* PS

        word    3800h, 3800h, 3800h, 3800h, 3800h, 3800h, 3800h, 3800h ;alt
        word    3920h, 3920h, 3920h, 0000h, 3920h, 3920h, 3920h, 3920h ;spc
        word    3a00h, 3a00h, 3a00h, 3a00h, 3a00h, 3a00h, 3a00h, 3a00h ;caps
        word    3b00h, 5400h, 5e00h, 6800h, 3b00h, 3b00h, 5400h, 5400h ;F1
        word    3c00h, 5500h, 5f00h, 6900h, 3c00h, 3c00h, 5500h, 5500h ;F2
        word    3d00h, 5600h, 6000h, 6a00h, 3d00h, 3d00h, 5600h, 5600h ;F3
        word    3e00h, 5700h, 6100h, 6b00h, 3e00h, 3e00h, 5700h, 5700h ;F4
        word    3f00h, 5800h, 6200h, 6c00h, 3f00h, 3f00h, 5800h, 5800h ;F5

;               norm   shft   ctrl   alt    num   caps   shcap  shnum
        word    4000h, 5900h, 6300h, 6d00h, 4000h, 4000h, 5900h, 5900h ;F6
        word    4100h, 5a00h, 6400h, 6e00h, 4100h, 4100h, 5a00h, 5a00h ;F7
        word    4200h, 5b00h, 6500h, 6f00h, 4200h, 4200h, 5b00h, 5b00h ;F8
        word    4300h, 5c00h, 6600h, 7000h, 4300h, 4300h, 5c00h, 5c00h ;F9
        word    4400h, 5d00h, 6700h, 7100h, 4400h, 4400h, 5d00h, 5d00h ;F10
        word    4500h, 4500h, 4500h, 4500h, 4500h, 4500h, 4500h, 4500h ;num
        word    4600h, 4600h, 4600h, 4600h, 4600h, 4600h, 4600h, 4600h ;scrl
        word    4700h, 4737h, 7700h, 0000h, 4737h, 4700h, 4737h, 4700h ;home

        word    4800h, 4838h, 0000h, 0000h, 4838h, 4800h, 4838h, 4800h ;up
        word    4900h, 4939h, 8400h, 0000h, 4939h, 4900h, 4939h, 4900h ;pgup
        word    4a2dh, 4a2dh, 0000h, 0000h, 4a2dh, 4a2dh, 4a2dh, 4a2dh ;-
        word    4b00h, 4b34h, 7300h, 0000h, 4b34h, 4b00h, 4b34h, 4b00h ;left
        word    4c00h, 4c35h, 0000h, 0000h, 4c35h, 4c00h, 4c35h, 4c00h ;Center
        word    4d00h, 4d36h, 7400h, 0000h, 4d36h, 4d00h, 4d36h, 4d00h ;right
        word    4e2bh, 4e2bh, 0000h, 0000h, 4e2bh, 4e2bh, 4e2bh, 4e2bh ;+
        word    4f00h, 4f31h, 7500h, 0000h, 4f31h, 4f00h, 4f31h, 4f00h ;end

;               norm   shft   ctrl   alt    num   caps   shcap  shnum
        word    5000h, 5032h, 0000h, 0000h, 5032h, 5000h, 5032h, 5000h ;down
        word    5100h, 5133h, 7600h, 0000h, 5133h, 5100h, 5133h, 5100h ;pgdn
        word    5200h, 5230h, 0000h, 0000h, 5230h, 5200h, 5230h, 5200h ;ins
        word    5300h, 532eh, 0000h, 0000h, 532eh, 5300h, 532eh, 5300h ;del
        word    0,0,0,0,0,0,0,0 ; --
        word    0,0,0,0,0,0,0,0 ; --
        word    0,0,0,0,0,0,0,0 ; --
        word    5700h, 0000h, 0000h, 0000h, 5700h, 5700h, 0000h, 0000h ;F11

        word    5800h, 0000h, 0000h, 0000h, 5800h, 5800h, 0000h, 0000h ;F12



;****************************************************************************
;
; AL contains keyboard scan code.

PutInBuffer     proc near
                push ds
                push bx

                mov bx, 40h                     ;Point ES at the BIOS
                mov ds, bx                      ; variables.

; If the current scan code is E0 or E1, we need to take note of this fact
; so that we can properly process cursor keys.

                cmp     al, 0e0h
                jne     TryE1
                or      KbdFlags3, 10b          ;Set E0 flag
                and     KbdFlags3, 0FEh         ;Clear E1 flag
                jmp     Done

TryE1:          cmp     al, 0e1h
                jne     DoScan
                or      KbdFlags3, 1            ;Set E1 flag
                and     KbdFlags3, 0FDh         ;Clear E0 Flag
                jmp     Done


; Before doing anything else, see if this is Ctrl-Alt-Del:

DoScan:         cmp     al, DelScanCode
                jnz     TryIns
                mov     bl, KbdFlags
                and     bl, AltBit or CtrlBit   ;Alt = bit 3, ctrl = bit 2
                cmp     bl, AltBit or CtrlBit
                jne     DoPIB
                mov     word ptr ds:[72h], 1234h ;Warm boot flag.
                jmp     dword ptr cs:RebootAdrs  ;REBOOT Computer

RebootAdrs      dword   0ffff0000h              ;Reset address.


; Check for the INS key here. This one needs to toggle the ins bit
; in the keyboard flags variables.

TryIns:         cmp     al, InsScanCode
                jne     TryInsUp
                or      KbdFlags2, InsBit       ;Note INS is down.
                jmp     doPIB                   ;Pass on INS key.

TryInsUp:       cmp     al, InsScanCode+80h     ;INS up scan code.
                jne     TryLShiftDn
                and     KbdFlags2, not InsBit   ;Note INS is up.
                xor     KbdFlags, InsBit        ;Toggle INS bit.
                jmp     QuitPIB

; Handle the left and right shift keys down here.

TryLShiftDn:    cmp     al, LShiftScan
                jne     TryLShiftUp
                or      KbdFlags, LShfBit       ;Note that the left
                jmp     QuitPIB                 ; shift key is down.

TryLShiftUp:    cmp     al, LShiftScan+80h
                jne     TryRShiftDn
                and     KbdFlags, not LShfBit   ;Note that the left
                jmp     QuitPIB                 ; shift key is up.


TryRShiftDn:    cmp     al, RShiftScan
                jne     TryRShiftUp
                or      KbdFlags, RShfBit       ;Right shf is down.
                jmp     QuitPIB

TryRShiftUp:    cmp     al, RShiftScan+80h
                jne     TryAltDn
                and     KbdFlags, not RShfBit   ;Right shf is up.
                jmp     QuitPIB

; Handle the ALT key down here.

TryAltDn:       cmp     al, AltScan
                jne     TryAltUp
                or      KbdFlags, AltBit        ;Alt key is down.
GotoQPIB:       jmp     QuitPIB

TryAltUp:       cmp     al, AltScan+80h
                jne     TryCtrlDn
                and     KbdFlags, not AltBit    ;Alt key is up.
                jmp     DoPIB


; Deal with the control key down here.

TryCtrlDn:      cmp     al, CtrlScan
                jne     TryCtrlUp
                or      KbdFlags, CtrlBit       ;Ctrl key is down.
                jmp     QuitPIB

TryCtrlUp:      cmp     al, CtrlScan+80h
                jne     TryCapsDn
                and     KbdFlags, not CtrlBit   ;Ctrl key is up.
                jmp     QuitPIB

; Deal with the CapsLock key down here.

TryCapsDn:      cmp     al, CapsLockScan
                jne     TryCapsUp
                or      KbdFlags2, CLBit        ;Capslock is down.
                xor     KbdFlags, CLBit         ;Toggle capslock.
                jmp     QuitPIB

TryCapsUp:      cmp     al, CapsLockScan+80h
                jne     TrySLDn
                and     KbdFlags2, not CLBit    ;Capslock is up.
                call    SetLEDs
                jmp     QuitPIB

; Deal with the Scroll Lock key down here.

TrySLDn:        cmp     al, ScrlLockScan
                jne     TrySLUp
                or      KbdFlags2, SLBit        ;Scrl lock is down.
                xor     KbdFlags, SLBit         ;Toggle scrl lock.
                jmp     QuitPIB

TrySLUp:        cmp     al, ScrlLockScan+80h
                jne     TryNLDn
                and     KbdFlags2, not SLBit    ;Scrl lock is up.
                call    SetLEDs
                jmp     QuitPIB

; Handle the NumLock key down here.


TryNLDn:        cmp     al, NumLockScan
                jne     TryNLUp
                or      KbdFlags2, NLBit        ;Numlock is down.
                xor     KbdFlags, NLBit         ;Toggle numlock.
                jmp     QuitPIB

TryNLUp:        cmp     al, NumLockScan+80h
                jne     DoPIB
                and     KbdFlags2, not NLBit    ;Numlock is up.
                call    SetLEDs
                jmp     QuitPIB



; Handle all the other keys here:

DoPIB:          test    al, 80h                 ;Ignore other up keys.
                jnz     QuitPIB

; If the H.O. bit is set at this point, we'd best only have a zero in AL.
; Otherwise, this is an up code which we can safely ignore.

                call    Convert
                test    ax, ax                  ;Chk for bad code.
                je      QuitPIB

PutCharInBuf:           push    cx
                mov     cx, ax
                mov     ah, 5                   ;Store scan code into
                int     16h                     ; type ahead buffer.
                pop     cx

QuitPIB:        and     KbdFlags3, 0FCh         ;E0, E1 not last code.

Done:           pop bx
                pop ds
                ret
PutInBuffer     endp




;****************************************************************************
;
; Convert-      AL contains a PC Scan code. Convert it to an ASCII char/Scan
;               code pair and return the result in AX. This code assumes
;               that DS points at the BIOS variable space (40h).

Convert         proc    near
                push    bx

                test    al, 80h         ;See if up code
                jz      DownScanCode
                mov     ah, al
                mov     al, 0
                jmp     CSDone

; Okay, we've got a down key. But before going on, let's see if we've
; got an ALT-Keypad sequence.

DownScanCode:           mov     bh, 0
                mov     bl, al
                shl     bx, 1           ;Multiply by eight to compute
                shl     bx, 1           ; row index index the scan
                shl     bx, 1           ; code xlat table

; Compute modifier index as follows:
;
;       if alt then modifier = 3

                test    KbdFlags, AltBit
                je      NotAlt
                add     bl, 3
                jmp     DoConvert

;       if ctrl, then modifier = 2

NotAlt:         test    KbdFlags, CtrlBit
                je      NotCtrl
                add     bl, 2
                jmp     DoConvert

; Regardless of the shift setting, we've got to deal with numlock
; and capslock. Numlock is only a concern if the scan code is greater
; than or equal to 47h. Capslock is only a concern if the scan code
; is less than this.

NotCtrl:        cmp     al, 47h
                jb      DoCapsLk
                test    KbdFlags, NLBit         ;Test Numlock bit
                je      NoNumLck
                test    KbdFlags, LShfBit or RShfBit ;Check l/r shift.
                je      NumOnly
                add     bl, 7                   ;Numlock and shift.
                jmp     DoConvert

NumOnly:        add     bl, 4                   ;Numlock only.
                jmp     DoConvert

; If numlock is not active, see if a shift key is:

NoNumLck:       test    KbdFlags, LShfBit or RShfBit ;Check l/r shift.
                je      DoConvert               ;normal if no shift.
                add     bl, 1
                jmp     DoConvert

; If the scan code's value is below 47h, we need to check for capslock.

DoCapsLk:       test    KbdFlags, CLBit         ;Chk capslock bit
                je      DoShift
                test    KbdFlags, LShfBit or RShfBit ;Chk for l/r shift
                je      CapsOnly
                add     bl, 6                   ;Shift and capslock.
                jmp     DoConvert

CapsOnly:       add     bl, 5                   ;Capslock
                jmp     DoConvert

; Well, nothing else is active, check for just a shift key.

DoShift:        test    KbdFlags, LShfBit or RShfBit ;l/r shift.
                je      DoConvert
                add     bl, 1                   ;Shift

DoConvert:      shl     bx, 1                   ;Word array
                mov     ax, ScanXlat[bx]
CSDone:         pop     bx
                ret
Convert         endp




; SetCmd-               Sends the command byte in the AL register to the 8042
;               keyboard microcontroller chip (command register at
;               port 64h).

SetCmd          proc    near
                push    cx
                push    ax              ;Save command value.
                cli                     ;Critical region, no ints now.

; Wait until the 8042 is done processing the current command.

                xor     cx, cx          ;Allow 65,536 times thru loop.
Wait4Empty:     in      al, 64h         ;Read keyboard status register.
                test    al, 10b         ;Input buffer full?
                loopnz  Wait4Empty      ;If so, wait until empty.

; Okay, send the command to the 8042:

                pop     ax              ;Retrieve command.
                out     64h, al
                sti                     ;Okay, ints can happen again.
                pop     cx
                ret
SetCmd          endp



; SendCmd-              The following routine sends a command or data byte to the
;               keyboard data port (port 60h).

SendCmd         proc    near
                push    ds
                push    bx
                push    cx
                mov     cx, 40h
                mov     ds, cx
                mov     bx, ax          ;Save data byte

                mov     bh, 3           ;Retry cnt.
RetryLp:        cli                     ;Disable ints while accessing HW.

; Clear the Error, Acknowledge received, and resend received flags
; in KbdFlags4

                and     byte ptr KbdFlags4, 4fh

; Wait until the 8042 is done processing the current command.

                xor     cx, cx          ;Allow 65,536 times thru loop.
Wait4Empty:     in      al, 64h         ;Read keyboard status register.
                test    al, 10b         ;Input buffer full?
                loopnz  Wait4Empty      ;If so, wait until empty.

; Okay, send the data to port 60h

                mov     al, bl
                out     60h, al
                sti                             ;Allow interrupts now.

; Wait for the arrival of an acknowledgement from the keyboard ISR:

                xor     cx, cx                  ;Wait a long time, if need be.
Wait4Ack:       test    byp KbdFlags4,10h       ;Acknowledge received bit.
                jnz     GotAck
                loop    Wait4Ack
                dec     bh                      ;Do a retry on this guy.
                jne RetryLp

; If the operation failed after 3 retries, set the error bit and quit.

                or      byp KbdFlags4,80h       ;Set error bit.

GotAck:         pop     cx
                pop     bx
                pop     ds
                ret
SendCmd         endp




; SetLEDs-      Updates the KbdFlags4 LED bits from the KbdFlags
;               variable and then transmits new flag settings to
;               the keyboard.

SetLEDs         proc    near
                push    ax
                push    cx
                mov     al, KbdFlags
                mov     cl, 4
                shr     al, cl
                and     al, 111b
                and     KbdFlags4, 0F8h         ;Clear LED bits.
                or      KbdFlags4, al           ;Mask in new bits.
                mov     ah, al                  ;Save LED bits.

                mov     al, 0ADh                ;Disable kbd for now.
                call    SetCmd

                mov     al, 0EDh                ;8042 set LEDs cmd.
                call    SendCmd                 ;Send the command to 8042.
                mov     al, ah                  ;Get parameter byte
                call    SendCmd                 ;Send parameter to the 8042.

                mov     al, 0AEh                ;Reenable keyboard.
                call    SetCmd
                mov     al, 0F4h                ;Restart kbd scanning.
                call    SendCmd
                pop     cx
                pop     ax
                ret
SetLEDs         endp


; MyInt9-       Interrupt service routine for the keyboard hardware
;               interrupt.

MyInt9          proc    far
                push    ds
                push    ax
                push    cx

                mov     ax, 40h
                mov     ds, ax

                mov     al, 0ADh                ;Disable keyboard
                call    SetCmd
                cli                             ;Disable interrupts.
                xor     cx, cx
Wait4Data:      in      al, 64h                 ;Read kbd status port.
                test    al, 10b                 ;Data in buffer?
                loopz   Wait4Data               ;Wait until data available.
                in      al, 60h                 ;Get keyboard data.
                cmp     al, 0EEh                ;Echo response?
                je      QuitInt9
                cmp     al, 0FAh                ;Acknowledge?
                jne     NotAck
                or      KbdFlags4, 10h          ;Set ack bit.
                jmp     QuitInt9

NotAck:         cmp     al, 0FEh                ;Resend command?
                jne     NotResend
                or      KbdFlags4, 20h          ;Set resend bit.
                jmp     QuitInt9

; Note: other keyboard controller commands all have their H.O. bit set
; and the PutInBuffer routine will ignore them.

NotResend:      call    PutInBuffer             ;Put in type ahead buffer.

QuitInt9:       mov     al, 0AEh                ;Reenable the keyboard
                call    SetCmd

                mov     al, 20h                 ;Send EOI (end of interrupt)
                out     20h, al                 ; to the 8259A PIC.
                pop     cx
                pop     ax
                pop     ds
                iret
MyInt9          endp



Main            proc
                assume  ds:cseg

                mov     ax, cseg
                mov     ds, ax

                print
                byte    "INT 9 Replacement",cr,lf
                byte    "Installing....",cr,lf,0

; Patch into the INT 9 interrupt vector. Note that the
; statements above have made cseg the current data segment,
; so we can store the old INT 9 value directly into
; the OldInt9 variable.

                cli                             ;Turn off interrupts!
                mov     ax, 0
                mov     es, ax
                mov     ax, es:[9*4]
                mov     word ptr OldInt9, ax
                mov     ax, es:[9*4 + 2]
                mov     word ptr OldInt9+2, ax
                mov     es:[9*4], offset MyInt9
                mov     es:[9*4+2], cs
                sti                             ;Okay, ints back on.


; We're hooked up, the only thing that remains is to terminate and
; stay resident.

                print
                byte    "Installed.",cr,lf,0

                mov     ah, 62h                 ;Get this program's PSP
                int     21h                     ; value.

                mov     dx, EndResident         ;Compute size of program.
                sub     dx, bx
                mov     ax, 3100h               ;DOS TSR command.
                int     21h
Main            endp
cseg            ends

sseg            segment para stack 'stack'
stk             byte    1024 dup ("stack ")
sseg            ends

zzzzzzseg       segment para public 'zzzzzz'
LastBytes       db      16 dup (?)
zzzzzzseg       ends
                end     Main

Chapter Twenty (Part 3)

Table of Content

Chapter Twenty (Part 5) 

Chapter Twenty: The PC Keyboard (Part 4)
29 SEP 1996