I've been puzzled by this for a while now. The (very old) game Phantasie comes with three small TSRs that are run prior to running the main game executable. This is the content of the file PH.BAT, used to start the game:
@echo off r32768.com m1.com m2.com phantasi.exe
If you try running phantasi.exe directly, the game refuses to run, and you immediately go right back to the command prompt. These programs that run first are extremely tiny; M1.COM and M2.COM are both 44 bytes, and R32768.COM is 86 bytes. I did a quick and dirty disassembly with DEBUG.EXE. Below is the source code for each, along with my interpretations, which could be incorrect (I'm not an 8086 assembly expert).
CLI SUB AX,AX MOV ES,AX ES: MOV AX, CMP AX,49A7 JZ lbl_011A ES: MOV AX,[018A] CMP AX,49A7 JZ lbl_011A STI INT 20 lbl_011A: ES: MOV WORD PTR ,0000 ES: MOV [018A],CS STI MOV DX,84D8 INT 27
This seems to disable interrupts, check if 0000:0188 = 49A7h or 0000:018A = 49A7h, and if neither one matches, it enables interrupts and quits back to DOS. Then, it stores 0 into 0000:0188, stores the current CS register value into 0000:018A, enables interrupts, and tells DOS it wants to terminate and stay resident, occupying memory through CS:84D8.
CLI SUB AX,AX MOV ES,AX ES: MOV AX,[018C] CMP AX,49A8 JZ lbl_011A ES: MOV AX,[018E] CMP AX,49A8 JZ lbl_011A STI INT 20 lbl_011A: ES: MOV WORD PTR [018C],0000 ES: MOV [018E],CS STI MOV DX,84D8 INT 27
Basically the same as M1.COM, but with different addresses/values. It checks if 0000:018C = 49A8h or 0000:018E = 49A8h, stores 0 in 0000:018C, copies CS to 0000:018E, and TSRs, also asking for memory through CS:84D8.
CLI SUB AX,AX MOV ES,AX ES: MOV AX, CMP AX,49A6 JNZ lbl_011A ES: MOV AX, CMP AX,49A6 JNZ lbl_011A STI INT 20 lbl_011A: ES: MOV WORD PTR ,49A6 ES: MOV WORD PTR ,49A6 ES: MOV WORD PTR ,49A7 ES: MOV WORD PTR [018A],49A7 ES: MOV WORD PTR [018C],49A8 ES: MOV WORD PTR [018E],49A8 ES: MOV WORD PTR ,0000 ES: MOV ,CS STI MOV DX,8000 INT 27
Disables interrupts, then checks if 0000:0180 and 0000:0182 both equal 49A6. If they do, it enables interrupts and exits (contrary to the previous two programs, which quit if neither one matches the test values). Then it writes 16 bytes starting at the locations 0000:0180 (notice that 0000:0184-0187 are written out of order, and 0000:0186 gets a copy of CS rather than the previous constant). Once finished, it TSRs, and reserves memory up through CS:8000.
It seems like they're rewriting the interrupt vector table starting at around int 60h, but I can't really figure out what they're meant to accomplish.