; NORESET.ASM
;
; A short TSR that patches the int 9 interrupt and intercepts the
; ctrl-alt-del keystroke sequence.
;
; 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


DelScanCode	equ	53h

; Bits for the various modifier keys

CtrlBit		equ	4
AltBit		equ	8


KbdFlags	equ	<byte ptr ds:[17h]>



cseg		segment	para public 'code'
		assume	ds:nothing


; 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


; MyInt9-	Interrupt service routine for the keyboard hardware
;		interrupt.  Tests to see if the user has pressed a
;		DEL key.  If not, it passes control on to the original
;		int 9 handler. If so, it first checks to see if the
;		alt and ctrl keys are currently down; if not, it passes
;		control to the original handler.  Otherwise it eats the
;		scan code and doesn't pass the DEL through.

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, DelScanCode		;Is it the delete key?
		jne	OrigInt9
		mov	al, KbdFlags		;Okay, we've got DEL, is
		and	al, AltBit or CtrlBit	; ctrl+alt down too?
		cmp	al, AltBit or CtrlBit
		jne	OrigInt9

; If ctrl+alt+DEL is down, just eat the DEL code and don't pass it through.

		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

; If ctrl and alt aren't both down, pass DEL on to the original INT 9
; handler routine.

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

		pop	cx
		pop	ax
		pop	ds
		jmp	cs:OldInt9
MyInt9		endp



Main		proc
		assume	ds:cseg

		mov	ax, cseg
		mov	ds, ax

		print
		byte	"Ctrl-Alt-Del Filter",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



