Back to Homework

Mobius strip

HW15 TSR Help

Overview

You are writing a TSR. Normally, when a program finishes executing, the OS reclaims ("cleans up") the memory it was using. With a TSR, when you quit the program, you leave part (or all) of the code in memory, usually so it can listen for certain events.

You are also changing the vector table. The vector table is basically an index of pointers to software functions that handle certain events. So, for example, every time the clock timer ticks, it looks up 1Ch in the vector table to find a segment:offset address. The computer goes to that address in memory and runs whatever code it finds there each time the timer ticks.

You are going to change the 09h vector to point to the TSR code you left in memory. Everytime there is a keypress, the computer will look up 09h in the vector table, go the address it finds there, and execute that code--which will be your resident TSR code. In that resident TSR code, you will check to see if it was a function key was pressed and respond accordingly.

To find out more about TSRs, you can have a look in your book--Section 15.4, I think.

Some of the features you need are documented in the Notes ("Additional Assembler instructions involved" -- addtn Assembler.doc) and HW ("Hmw" -- hotkey.doc) files on Pager's site. (Bold words are how I refer to these files below.)

The Code

Your code will have two parts/sections--the Setup code and the Resident code. You can follow the book's example and make these two PROCs, or you can follow the greet.asm example and just use labels for the two sections.

You will also have to calculate how many paragraphs (16 byte chunks) of code to leave resident. If you want to get fancy, like the book, you can leave only the necessary code (the Resident section) in memory. Or you can follow greet.asm's example and leave all the code in memory (Resident and Setup).

Part 1: The Setup code

First, you need to save the original 09h vector. You need this later so you can call it from your Resident code. [See HW for how to setup the variables to store the saved segment and offset and later make the call. See Notes for int 21h, fn. 35h].

Then, you need to replace the 09h vector with the segment and offset of your Resident code/function. [See greet.asm. (Notes for int 21h, fn. 25h are currently incorrect--seg:offset should be in ds:dx, not "ex":bx.)]

Finally, you quit the program, leaving behind the Resident code and data (or, if you follow the greet.asm example, leaving behind all the code and data). [See Notes for int 21h, fn. 31h]

Part 2: The Resident code

If your Setup code worked properly, now everytime there is a keypress, your Resident code will be executed.

First thing you need to do is call the saved vector. This original 09h vector actually processes keypresses and puts the contents into the buffer. You can't read any scan codes (or ASCII codes) until this has been done! If you try it, int 16h fn. 01h will simply return with the zero flag--no keys found.

Then check the scan code. [See Notes for int 16h, fn. 1. You cannot use fn. 2 to do this. And the function number goes in ah, as with any int call. See HW for scan codes.]

If it is one of the 3 function keys you're interested in, print the appropriate message or disable/enable printing. (I recommend using a simple variable that's either 1 or 0. If it's 0, F1 and F2 print nothing. When you press F3, change this variable to 0 if it is 1, or to 1 if it is 0.) Print with int 10h, fn. 13h. [See Notes or greet.asm]

A Code Skeleton

This follows the book's example: using PROCs and leaving only the necessary code resident. To do this, all the Setup code must come at the end. Feel free to change any of the names.

	.MODEL SMALL
	.586
	.STACK 100

	.CODE			;No separate DATA seg for TSR

;*** BEGIN Resident code (including data)***
RES_BEGIN 	EQU	$

;put your data here: message contents, etc.

KbdHandler	LABEL 	DWORD	;where to save the old keyboard handler
kbdh_offset	DW	?
kbdh_seg	DW	?

Resident	PROC
	;call the old handler first!
	pushf
	call 	KbdHandler

	sti				;allow other interrupts
	pushad				;save state

;use int 16h, fn. 01h to look at the inputted key
;match the scan codes to find out if it was a function key
;use int 10h, fn. 13h to print out the message (see greet.asm)

	popad				;restore state
	iret

Resident	ENDP

RES_SIZE	EQU	$ - RES_BEGIN
;*** END Resident code ***

Setup	PROC
	push	cs			;no .DATA, so setting ds to cs
	pop	ds

;use int21h, fn. 35h to save the 09h vector in KbdHandler
;use int21h, fn. 25h to replace the 09h vector with Resident
;use int21h, fn. 31h to terminate but leave the Resident code behind

;here's how to calculate the needed number of paragraphs
;(see the book for the details)
	mov	dx, (RES_SIZE + 15)/16 + 16	;# of paragraphs, +16 for PSP

Setup	ENDP

	END	Setup

Filling in the Code

I recommend the following order of things:

  1. Write the Setup code.
  2. Comment out the vector call in Resident. Just print "HI!". If this works, you'll see your message on the first keypress after running your program. If you do, you know your Setup's fn.s 25h and 31h worked--it can find and run your Resident code.

    If it doesn't work: Try your printing code in a normal (not TSR) program to make sure it works. You will probably need to pause for a keypress so it shows up before your program quits. Try other operations to find out if your TSR code is resident and responding.

  3. Uncomment the vector call and comment out everything else.

    Now the command line should work normally. This means you saved the old vector correctly during Setup and are calling it successfully.

  4. Now you can work on getting your Resident code to process the function keys.


~ztomasze Index : TA Details : ICS312 : TSR Help
http://www2.hawaii.edu/~ztomasze
Last Edited: 06 Mar 2006
©2006 by Z. Tomaszewski.