; 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	;Cen
	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		;5300h
		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	dd	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 mode.
		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 all 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			;Check for illegal 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		db	1024 dup ("stack   ")
sseg		ends

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



