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