Ralf Brown's Interrupt List 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 clean-upcleanup 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".