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).
M1.COM
CLI
SUB AX,AX
MOV ES,AX
ES:
MOV AX,[0188]
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 [0188],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.
M2.COM
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.
R32768.COM
CLI
SUB AX,AX
MOV ES,AX
ES:
MOV AX,[0180]
CMP AX,49A6
JNZ lbl_011A
ES:
MOV AX,[0182]
CMP AX,49A6
JNZ lbl_011A
STI
INT 20
lbl_011A:
ES:
MOV WORD PTR [0180],49A6
ES:
MOV WORD PTR [0182],49A6
ES:
MOV WORD PTR [0188],49A7
ES:
MOV WORD PTR [018A],49A7
ES:
MOV WORD PTR [018C],49A8
ES:
MOV WORD PTR [018E],49A8
ES:
MOV WORD PTR [0184],0000
ES:
MOV [0186],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.