/* This program demonstrates how regular DOS executable files can be run from XLI and represents an alternative to DOS-CALL. It also provides an example that uses string arguments and the "swap" special service call to access them. User documentation is available under XLI\EXEC.DOC. EXEC.EXE is already provided and can be used immediately by inserting its pathname in your .XLI control file. To generate EXEC.EXE yourself, do the following (substituting directory names and setting the path as needed; Lattice C version 3.0 was used): lc exec masm glue_lc; link \lc\s\c+exec+glue_lc,exec,,\lc\s\lc When EXEC.EXE is loaded, it allocates a block of memory from DOS before returning to PCS. Further external programs, and the Scheme heap, are allocated with this block unavailable to them. On the first (XCALL "exec" ...), the block is returned to DOS, and then DOS can use it to run other programs in. In this approach, nothing of Scheme needs to be saved or restored, so running another program is quick. On the other hand, Scheme's heap is that much smaller, meaning a smaller workspace and more garbage collections. When PCS terminates, this program's termination code makes certain that the block gets deallocated (in case it never got called in the first place). */ #include "dos.h" #define F_NEAR 0x0001 #define F_INTEGER 0x0002 #define F_PAD 0x0008 #define RT_INTEGER 0 #define CARRY_BIT 1 typedef unsigned short WORD; /* 16-bit unsigned value */ extern WORD _psp; /* Lattice C variables */ extern WORD _tsize; extern WORD _oserr; /* Note xwait and xbye are the actual addresses we'll jump to when we call XLI from the glue routine. C calls the glue routine at the two entry points xli_wait and xli_bye. These 2 routines set up the stack for calling xwait and xbye. */ WORD xwait[2]; /* XLI entry points */ WORD xbye[2]; struct xli_file_struct { WORD id; WORD flags; WORD table[2]; /* offset in 0, segment in 1 */ WORD parm_block[2]; WORD reserved[8]; } file_block; struct xli_routine_struct { WORD select; WORD special_service; WORD ss_args[8]; WORD reserved[8]; WORD return_type; int return_value; int dummy[3]; char *arg[16]; /* position 0 == filename */ /* positions 1..15 are for args */ } parm_block; char table[] = /* 0 2 4 6 8 10 12 */ "exec//"; void main(argc,argv) int argc; char *argv[]; { int i,flags,allocated; WORD psp; /*WORD memsize; */ WORD buffer[2]; WORD block_ptr; union REGS regs; struct SREGS segregs; int xli_wait(); void xli_bye(); char *getenv(); long atol(); char cmd[128]; char *local_argv[17]; /* use positions 1..16 */ /* -------------------- XLI-specific initialization ----------------------- */ /* Note PSP@ is not necessarily directly accessible in any Lattice C memory model. */ psp = *(&_psp+1); /* get seg addr of PSP */ /* init file block */ file_block.id = 0x4252; file_block.flags = F_NEAR+F_INTEGER; file_block.table[0] = (WORD) table; file_block.parm_block[0] = (WORD) &parm_block; segread(&segregs); file_block.table[1] = segregs.ds; file_block.parm_block[1] = segregs.ds; /* determine link address */ buffer[0] = (WORD) &file_block; buffer[1] = segregs.ds; /* determine size to keep */ /*memsize = _tsize; */ /* done in glue routine for S Lattice */ /* establish the link addresses between C and PCS */ poke((int) psp, 0x5c, (char *) buffer, 4); /* poke file block@ into PSP */ peek((int) psp, 0x0a, (char *) xwait, 4); /* get DOS ret@ */ xbye[0] = xwait[0]; xbye[1] = xwait[1]; xwait[0] += 3; /* incr by 3 for normal call */ xbye[0] += 6; /* incr by 6 for termination */ /* ==================== start program-specific actions ==================== */ /* ----------------------------- initialization --------------------------- */ /* allocate a block of memory */ regs.x.ax = 0x4800; /* alloc mem */ { /* Set size from "XLI" env variable if available; unit size is Kb. If var doesn't exist, use 64 Kb. Convert to paragraphs. */ char *block_reserve; block_reserve = getenv("XLI"); regs.x.bx = (block_reserve ? atol(block_reserve) * 1024 / 16 : 0x1000); } flags = intdos(®s,®s); block_ptr = (flags & CARRY_BIT) ? 0 : regs.x.ax; allocated = 1; /* set all args to -1; since there are a variable # of args, a -1 after them delimits them */ for (i = 0; i < 16; i++) parm_block.arg[i] = (char *) -1; /* ----------------------------- handler loop --------------------------- */ while (xli_wait()) { if (!block_ptr) continue; /* couldn't alloc, just skip */ /* deallocate the block to leave a hole in which we can bid programs */ if (allocated) { regs.x.ax = 0x4900; /* dealloc mem */ segregs.es = block_ptr; /* @block we previously allocated */ flags = intdosx(®s,®s,&segregs); allocated = 0; } /* end if */ switch (parm_block.select) { int i,error; case 0: /* get name of executable file */ parm_block.special_service = 1; parm_block.ss_args[0] = 0; parm_block.ss_args[1] = 128; parm_block.ss_args[2] = (WORD) cmd; (void) xli_wait(); *(cmd + parm_block.ss_args[0]) = '\0'; /* get arguments to executable file */ for (i = 1; i < 17; i++) local_argv[i] = NULL; for (i = 1; i < 16; i++) { if ((int) parm_block.arg[i] == -1) break; local_argv[i] = cmd + parm_block.ss_args[0] + 1; parm_block.special_service = 1; parm_block.ss_args[0] = i; parm_block.ss_args[1] = cmd + 128 - local_argv[i]; parm_block.ss_args[2] = (WORD) local_argv[i]; (void) xli_wait(); *(local_argv[i] + parm_block.ss_args[0]) = '\0'; } /* exec the file and return the termination code */ /* or -1 if the file doesn't exist */ error = forkvp(cmd,local_argv); parm_block.return_value = (error == -1 ? -1 : wait()); break; default: ; } /* end switch */ parm_block.return_type = RT_INTEGER; for (i = 0; i < 16; i++) parm_block.arg[i] = (char *) -1; } /* end while */ /* ----------------------------- termination --------------------------- */ /* in case this program was never called, the block we reserved */ /* is still allocated, so deallocate it */ if (allocated) { regs.x.ax = 0x4900; /* dealloc mem */ segregs.es = block_ptr; /* @block we previously allocated */ flags = intdosx(®s,®s,&segregs); allocated = 0; } /* end if */ xli_bye(); } /* end main */