In my previous efforts to help you, I've manage to confuse you more than help
you. I tried to give you some clues on a smaller TSR.
What is a TSR ( Terminate and Stay Resident ) ?
A tsr is a program that goes memory resident. Control of this program is
freed from the user and the system ( DOS/WINDOWS ) takes control.
What is a ISR ?
A ISR (Interrupt Service Routine) is a program that goes resident but as
soon as the program is terminated it DOES NOT stay resident.
TSR vs ISR
So whats the deal here? A TSR always starts out as an ISR and only if a
special set of interrups are called will it Terminate and go memory resident.
So what does the TSR do?
Most times you program will chain itself to an interrupt like 0x9 which is
the keyboard interrupt. Everytime you press a key interrupt 0x9 get polled
( called). Our TSR will attach itself on 0x9 so that our program also gets
called. We can find out where the Adress of the program is that gets called
by default. We then change the 0x9 vector to point to our program instead
and when our program is done we call the old routine that would have been
called. This is refered to as chaining the interrupt.
So how do we write a TSR ?
Here is the procedure you should follow
1. find out what Interrupt we want to use.
The logical ones to use most of the time is
0x1C , this interrupt gets called on every system tick
0x9 , this interrupt gets called on every key press
0x8 , this is the realtime clock interrupt it timing can be
reprogrammed to make it fire at a desired rate.
For our TSR problem int 0x8 is just fine. Beware of int 0x1c if you don't
give controll back critical system functions could be lost.
2. find the location of the program that gets called by default by the
interrupt hander.
mov AH, 35h ; get interrupt vector info
mov AL, 01Ch ; AL = the interrupt number
int 21h ; do it.
the results as as follow
ES = Interrupt Handler Segment
BX = Interrupt Handler Offset
Now we know where the default program (handler) resides ES:BX
3. Change the interrupt vector table to point to our hander and not the
default one.
mov AH, 25h ; set the interrupt vector
mov AL, 01Ch ; AL = the interrupt number
mov DX, OFFSET ; our new handler
int 21h ; do it
REMEMBER at the end of your program to jump back to the old Interrupt
hander. If you don't you may get strange results and a blank hard drive.
In essence up to now our program is a ISR if we terminate it now with the
TSR interrupt it will go memory resident and become a TSR.
4. Terminate and stay resident
mov AL,31h ; terminate and stay resident
mov DX, size ; the size of the program in paragraphs
int 21h ; do it
DX must contain the number of paragraphs of memory required
by the program (one paragraph = 16 bytes). AL contains an
exit code.
Be careful when using this function with .EXE programs. The
value in DX must be the total size to remain resident, not
just the size of the code segment which is to remain
resident. A typical error is to forget about the 100H byte
program header prefix and give a value in DX which is 10H
too small.
There is another function you can use to make your program resident.
It used in COM files mostly and is much simpler!
mov DX, size
int 27h
5. All done!
Example of a ISR going TSR. TASM example.asm ; TLINK /t example.obj
----------------------------------8----------------------------------------
CODE SEGMENT ; Code segment start here
.286 ; 286 instruction
ASSUME CS:CODE,DS:CODE ; The code+data segments point to CODE segement
ORG 100H ; Com files always start at 100h
@@MAIN:
; Get the Interrupt vector info
mov AH,35h ; Get interrupt vector info
mov AL,09h ; we want 9h's info
int 21h
mov WORD PTR [oldh+3],es ; Save the adress of the old handler
mov WORD PTR [oldh+1],bx ; which resides in ES:BX
; Set our Handler active
mov AH,25h ; Set the interrupt vector
mov AL,9h ; we want to set 9h
mov DX,OFFSET @@ISR ; DX is where our ISR is
int 21h
; Go TSR
mov dx,offset oldh ; dx is the offset of the last byte of code + 1
add dx, 5
int 27h ; do it
@@ISR: ; The ISR
pusha ; Save all the registers
cli ; clear maskable interrups so that the
; interrupt does not get called when we're
; in it;
sti ; enable the interrups again
popa ; Restore them
oldh DB 0EAh ; Chain our old hander
; WHAT DOES THIS LAST LINE DO ???
; We place a BYTE into the code segment EAh
; EAh = jmp , What we then do is to place the
; adress it should jump to behind it.
; This translates into JMP ES:BX
CODE ENDS
END @@MAIN