⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 keyeval.asm

📁 汇编编程艺术
💻 ASM
📖 第 1 页 / 共 2 页
字号:
; This is an example of an active TSR that counts keyboard interrupts
; once activated.  Every minute it writes the number of keyboard
; interrupts that occurred in the previous minute to an output file.
; This continues until the user removes the program from memory.
;
;
; Usage:
;	KEYEVAL  filename	- Begins logging keystroke data  to
;				  this file.
;
;	KEYEVAL  REMOVE		- Removes the resident program from
;				  memory.
;
;
; This TSR checks to make sure there isn't a copy already active in
; memory.  When doing disk I/O from the interrupts, it checks to make
; sure DOS isn't busy and it preserves application globals (PSP, DTA,
; and extended error info).  When removing itself from memory, it
; makes sure there are no other interrupts chained into any of its
; interrupts before doing the remove.
;
; The resident segment definitions must come before everything else.

ResidentSeg	segment	para public 'Resident'
ResidentSeg	ends

EndResident	segment	para public 'EndRes'
EndResident	ends

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


; Resident segment that holds the TSR code:

ResidentSeg	segment	para public 'Resident'
		assume	cs:ResidentSeg, ds:nothing

; Int 2Fh ID number for this TSR:

MyTSRID		byte	0

; The following variable counts the number of keyboard interrupts

KeyIntCnt	word	0

; Counter counts off the number of milliseconds that pass, SecCounter
; counts off the number of seconds (up to 60).

Counter		word	0
SecCounter	word	0

; FileHandle is the handle for the log file:

FileHandle	word	0

; NeedIO determines if we have a pending I/O opearation.

NeedIO		word	0

; PSP is the psp address for this program.

PSP		word	0

; Variables to tell us if DOS, INT 13h, or INT 16h are busy:

InInt13		byte	0
InInt16		byte	0
InDOSFlag	dword	?

; These variables contain the original values in the interrupt vectors
; we've patched.

OldInt9		dword	?
OldInt13	dword	?
OldInt16	dword	?
OldInt1C	dword	?
OldInt28	dword	?
OldInt2F	dword	?


; DOS data structures:

ExtErr		struct
eeAX		word	?
eeBX		word	?
eeCX		word	?
eeDX		word	?
eeSI		word	?
eeDI		word	?
eeDS		word	?
eeES		word	?
		word	3 dup (0)
ExtErr		ends



XErr		ExtErr	{}		;Extended Error Status.
AppPSP		word	?		;Application PSP value.
AppDTA		dword	?		;Application DTA address.


; The following data is the output record.  After storing this data
; to these variables, the TSR writes this data to disk.

month		byte	0
day		byte	0
year		word	0
hour		byte	0
minute		byte	0
second		byte	0
Keystrokes	word	0
RecSize		=	$-month







; MyInt9-	The system calls this routine every time a keyboard
;		interrupt occus.  This routine increments the
;		KeyIntCnt variable and then passes control on to the
;		original Int9 handler.

MyInt9		proc	far
		inc	ResidentSeg:KeyIntCnt
		jmp	ResidentSeg:OldInt9
MyInt9		endp





; MyInt1C-	Timer interrupt.  This guy counts off 60 seconds and then
;		attempts to write a record to the output file.  Of course,
;		this call has to jump through all sorts of hoops to keep
;		from reentering DOS and other problematic code.

MyInt1C		proc	far
		assume	ds:ResidentSeg

		push	ds
		push	es
		pusha				;Save all the registers.
		mov	ax, ResidentSeg
		mov	ds, ax

		pushf
		call	OldInt1C

; First things first, let's bump our interrupt counter so we can count
; off a minute.  Since we're getting interrupted about every 54.92549
; milliseconds, let's shoot for a little more accuracy than 18 times
; per second so the timings don't drift too much.

		add	Counter, 549		;54.9 msec per int 1C.
		cmp	Counter, 10000		;1 second.
		jb	NotSecYet
		sub	Counter, 10000
		inc	SecCounter
NotSecYet:


; If NEEDIO is not zero, then there is an I/O operation in progress.
; Do not disturb the output values if this is the case.

		cli				;This is a critical region.
		cmp	NeedIO, 0
		jne	SkipSetNIO

; Okay, no I/O in progress, see if a minute has passed since the last
; time we logged the keystrokes to the file.  If so, it's time to start
; another I/O operation.

		cmp	SecCounter, 60		;One minute passed yet?
		jb	Int1CDone
		mov	NeedIO, 1		;Flag need for I/O.
		mov	ax, KeyIntCnt		;Copy this to the output
		shr	ax, 1			; buffer after computing
		mov     KeyStrokes, ax		; # of keystrokes.
		mov	KeyIntCnt, 0		;Reset for next minute.
		mov	SecCounter, 0

SkipSetNIO:     cmp	NeedIO, 1		;Is the I/O already in
		jne	Int1CDone		; progress?  Or done?

		call	ChkDOSStatus		;See if DOS/BIOS are free.
		jnc	Int1CDone		;Branch if busy.

		call	DoIO			;Do I/O if DOS is free.

Int1CDone:	popa
		pop	es
		pop	ds
		iret
MyInt1C		endp
		assume	ds:nothing


; MyInt28-	Idle interrupt.  If DOS is in a busy-wait loop waiting for
;		I/O to complete, it executes an int 28h instruction each
;		time through the loop.  We can ignore the InDOS and CritErr
;		flags at that time, and do the I/O if the other interrupts
;		are free.

MyInt28		proc	far
		assume	ds:ResidentSeg

		push	ds
		push	es
		pusha				;Save all the registers.
		mov	ax, ResidentSeg
		mov	ds, ax

		pushf				;Call the next INT 28h
		call	OldInt28		; ISR in the chain.

		cmp	NeedIO, 1		;Do we have a pending I/O?
		jne	Int28Done

		mov	al, InInt13		;See if BIOS is busy.
		or	al, InInt16
		jne	Int28Done

		call	DoIO			;Go do I/O if BIOS is free.

Int28Done:	popa
		pop	es
		pop	ds
		iret
MyInt28		endp
		assume	ds:nothing


; MyInt16-	This is just a wrapper for the INT 16h (keyboard trap)
;		handler.

MyInt16		proc	far
		inc	ResidentSeg:InInt16
		pushf
		call	ResidentSeg:OldInt16	;Call original handler.
		pushf				;Must preserve flags
		dec	ResidentSeg:InInt16	; for caller.
		popf
		retf	2			;Fake IRET to keep flags.
MyInt16		endp


; MyInt13-	This is just a wrapper for the INT 13h (disk I/O trap)
;		handler.

MyInt13		proc	far
		inc	ResidentSeg:InInt13
		pushf
		call	ResidentSeg:OldInt13	;Call original handler.
		pushf				;Must preserve flags
		dec	ResidentSeg:InInt13	; for caller.
		popf
		retf	2			;Fake iret to keep flags.
MyInt13		endp


; ChkDOSStatus-	Returns with the carry clear if DOS or a BIOS routine
;		is busy and we can't interrupt them.

ChkDOSStatus	proc	near
		assume	ds:ResidentSeg
		les	bx, InDOSFlag
		mov	al, es:[bx]		;Get InDOS flag.
		or	al, es:[bx-1]		;OR with CritErr flag.
		or	al, InInt16		;OR with our wrapper
		or	al, InInt13		; values.
		je	Okay2Call
		clc
		ret

Okay2Call:	clc
		ret
ChkDOSStatus	endp
		assume	ds:nothing


; PreserveDOS-	Gets a copy's of DOS' current PSP, DTA, and extended
;		error information and saves this stuff.  Then it sets
;		the PSP to our local PSP and the DTA to PSP:80h.

PreserveDOS	proc	near
		assume	ds:ResidentSeg

		mov	ah, 51h			;Get app's PSP.
		int	21h
		mov	AppPSP, bx		;Save for later

		mov	ah, 2Fh			;Get app's DTA.
		int	21h
		mov	word ptr AppDTA, bx  	;Save for later.
		mov	word ptr AppDTA+2, es

		push	ds
		mov	ah, 59h			;Get extended err info.
		xor	bx, bx
		int	21h

		mov	cs:XErr.eeDS, ds
		pop	ds
		mov	XErr.eeAX, ax
		mov	XErr.eeBX, bx
		mov	XErr.eeCX, cx
		mov	XErr.eeDX, dx
		mov	XErr.eeSI, si
		mov	XErr.eeDI, di
		mov	XErr.eeES, es

; Okay, point DOS's pointers at us:

		mov	bx, PSP
		mov	ah, 50h			;Set PSP.
		int	21h

		push	ds			;Set the DTA to
		mov	ds, PSP			; address PSP:80h
		mov	dx, 80h
		mov	ah, 1Ah			;Set DTA call.
		int	21h
		pop	ds

		ret
PreserveDOS	endp
		assume	ds:nothing



; RestoreDOS-	Restores DOS' important global data values back to the
;		application's values.

RestoreDOS	proc	near
		assume	ds:ResidentSeg

		mov	bx, AppPSP
		mov	ah, 50h			;Set PSP
		int	21h

		push	ds
		lds	dx, AppDTA
		mov	ah, 1Ah			;Set DTA
		int	21h
		pop	ds
		push	ds

		mov	si, offset XErr		;Saved extended error stuff.
		mov	ax, 5D0Ah		;Restore XErr call.
		int	21h
		pop	ds
		ret
RestoreDOS	endp
		assume	ds:nothing


; DoIO-		This routine processes each of the I/O operations
;		required to write data to the file.

DoIO		proc	near
		assume	ds:ResidentSeg

		mov	NeedIO, 0FFh		;A busy flag for us.

; The following Get Date DOS call may take a while, so turn the
; interrupts back on (we're clear of the critical section once we
; write 0FFh to NeedIO).

		sti
		call	PreserveDOS		;Save DOS data.

		mov	ah, 2Ah			;Get Date DOS call
		int	21h
		mov	month, dh
		mov	day, dl
		mov	year, cx

		mov	ah, 2Ch			;Get Time DOS call
		int	21h
		mov	hour, ch
		mov	minute, cl
		mov	second, dh

		mov	ah, 40h			;DOS Write call
		mov	bx, FileHandle		;Write data to this file.
		mov	cx, RecSize		;This many bytes.
		mov	dx, offset month	;Starting at this address.
		int	21h			;Ignore return errors (!).
		mov	ah, 68h			;DOS Commit call
		mov	bx, FileHandle		;Write data to this file.
		int	21h			;Ignore return errors (!).

		mov	NeedIO, 0		;Ready to start over.
		call	RestoreDOS

PhasesDone:	ret
DoIO		endp
		assume	ds:nothing



; MyInt2F-	Provides int 2Fh (multiplex interrupt) support for this
;		TSR.  The multiplex interrupt recognizes the following
;		subfunctions (passed in AL):
;
;		00- Verify presence.  	Returns 0FFh in AL and a pointer
;					to an ID string in es:di if the
;					TSR ID (in AH) matches this
;					particular TSR.
;
;		01- Remove.		Removes the TSR from memory.
;					Returns 0 in AL if successful,
;					1 in AL if failure.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -