Assistance in writing TSR program (s) in building NASM for DOS

I am trying to write TSR (Terminate-Stay-Resident) (in general) programs in Assembly (16-bit) for MS-DOS. I read the WSR page on TSR, as well as the page about using it specifically in DOS (but it seems to teach it in C, and not in the assembly directly). I went through a website with tons of documentation on DOS interrupts and found this , this , and the other most relevant to TSR programs. I can’t post all the links, because as a new user I can have up to 2 hyperlinks in the message.

So, I tried writing (apparently) a very simple TSR program in real mode (.COM file format) in NASM. Here is the code:

[BITS 16] [ORG 0x0100] [SECTION .text] Start: ; Get current interrupt handler for INT 21h mov AX,3521h ; DOS function 35h GET INTERRUPT VECTOR for interrupt 21h int 21h ; Call DOS (Current interrupt handler returned in ES:BX) mov WORD [v21HandlerSegment],ES ; Store the current INT 21h handler segment mov WORD [v21HandlerOffset],BX ; Store the current INT 21h handler offset ; Write new interrupt handler for INT 21h mov AX,2521h ; DOS function 25h SET INTERRUPT VECTOR for interrupt 21h mov DX,TSRStart ; Load DX with the offset address of the start of this TSR program ; DS already contains the segment address, it is the same as CS in this .COM file int 21h ; Override the INT 21h handler with this TSR program ; The TSR program will be called even when this portion uses INT 21h to terminate and stay resident mov AX,3100h ; DOS function TSR, return code 00h mov DX,00FFh ; I don't know how many paragraphs to keep resident, so keep a bunch int 21h ; Call our own TSR program first, then call DOS TSRStart: push WORD [v21HandlerSegment] ; Push the far address of the original push WORD [v21HandlerOffset] ; INT 21h handler onto the stack retf ; Jump to it! [SECTION .data] v21HandlerSegment dw 0000h v21HandlerOffset dw 0000h 

When I collect this and execute it inside DOS, instead of returning back to the DOS prompt, it hangs up by the system (no action happens, except that the hardware cursor simply blinks below the last prompt). I think memory junk can execute, but you understand the point.

Can someone help or figure out what the problem is with this code, and / or offer general guidelines for coding TSR in DOS? Thanks in advance, any help is greatly appreciated!

+9
source share
2 answers

I get it. Looking through a couple more sources, I found that this code:

 push WORD [v21HandlerSegment] ; Push the far address of the original push WORD [v21HandlerOffset] ; INT 21h handler onto the stack 

there should be something like this:

 push WORD [CS:v21HandlerSegment] ; Push the far address of the original push WORD [CS:v21HandlerOffset] ; INT 21h handler onto the stack 

because these memory references refer to a data segment that is not configured from the TSR caller. So basically I was referring to data from something else data block ...

This can also be done by putting CS in DS (and then returning the original DS value) as follows:

 push DS push CS pop DS ; Memory references.... pop DS 
+6
source
  1. You need to use the cs: segment redefinition to access the TSR data from the general-purpose interrupt handler, because then the ds value is an arbitrary user register.

  2. You do not need to push the next handler address retf stack and then jump with retf . It is easier to make jmp far [cs:...] (and this has a shorter encoding). But your method works well too.

  3. You can put your initialization processing (not required in the resident installed handler) at the end of your program image. This is a trivial TSR size optimization.

  4. To calculate the size of your resident process, use NASM tags. To allow the shift (or division) operations necessary to determine the length in paragraphs, use only deltas of labels. The delta (difference) is a scalar value for NASM, so it can be used in calculations. One (unstructured) label in itself is not a scalar.

Here is an example of using all of these:

  cpu 8086 bits 16 org 256 start: jmp init align 4 int21old: dd 0 int21handler: jmp far [cs:int21old] end_of_resident: init: mov ax, 3521h int 21h mov word [int21old + 2], es mov word [int21old], bx mov ax, 2521h mov dx, int21handler int 21h mov ax, 3100h mov dx, (end_of_resident - start + 256 + 15) >> 4 int 21h 

Size calculation calculates the delta of two labels, adds 256 for the PSP process (same as org 256 ), adds 15 to round up the division of the offset up, and then shift it down to the number of paragraphs.

+1
source

All Articles