[Ralf Brown's Interrupt List][1] describes a subvariant of the DOS int21/4Bh function with AL=01, where the program is loaded into memory and a PSP + stack are allocated for it, but the program is not executed. The CS:IP and SS:IP are placed in the parameter block structure that was provided as input in ES:BX.
This works, but what do I need to do in order to execute the program and make sure it returns to the point where I called it from (I am doing this from C, FWIW)? I imagine I can't just cast CS:IP to a `void (far *func)(void)` and call it a day?
#pragma pack(1)
struct {
uint16 envSegment;
uint16 cmdlineOffset;
uint16 cmdlineSegment;
uint16 fcb1Offset;
uint16 fcb1Segment;
uint16 fcb2Offset;
uint16 fcb2Segment;
uint16 sp;
uint16 ss;
uint16 ip;
uint16 cs;
} exeLoadParams;
static int loadprog(const char* file, const char far* cmdline) {
union REGS rin, rout;
rin.h.ah = 0x4B;
rin.h.al = 0x1;
// DS will be data segment of current executable, no need to provide with intdosx()?
rin.x.dx = (unsigned int)file;
exeLoadParams.envSegment = 0;
exeLoadParams.cmdlineOffset = FP_OFF(cmdline);
exeLoadParams.cmdlineSegment = FP_SEG(cmdline);
// the two FCBs are located at offsets 0x5c and 0x6c in this program's PSP
exeLoadParams.fcb1Offset = 0x5c;
exeLoadParams.fcb1Segment = _psp;
exeLoadParams.fcb2Offset = 0x6c;
exeLoadParams.fcb2Segment = _psp;
// ES == DS guaranteed? it seems to work
rin.x.bx = (unsigned int)&exeLoadParams;
intdos(&rin, &rout);
// exeLoadParams now contains CS:IP and SS:SP, but what next?
}
Also, what kind of cleanup do I need to do once the program returns? I tried freeing the block whose address I calculated as `CS - PSP_SIZE` and it seems to have freed most of the memory, but not all of it - in this particular case, 9 paragraphs worth of it were "lost".
[1]: https://fd.lod.bz/rbil/interrup/dos_kernel/214b.html