/****************************************************************/
/* */
/* task.c */
/* */
/* Task Manager for DOS Processes */
/* */
/* Copyright (c) 1995 */
/* Pasquale J. Villani */
/* All Rights Reserved */
/* */
/* This file is part of DOS-C. */
/* */
/* DOS-C is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU General Public License */
/* as published by the Free Software Foundation; either version */
/* 2, or (at your option) any later version. */
/* */
/* DOS-C is distributed in the hope that it will be useful, but */
/* WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */
/* the GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public */
/* License along with DOS-C; see the file COPYING. If not, */
/* write to the Free Software Foundation, 675 Mass Ave, */
/* Cambridge, MA 02139, USA. */
/****************************************************************/
#include "portab.h"
#include "globals.h"
#include "debug.h"
#define toupper(c) ((c) >= 'a' && (c) ps_parent = 0;
#endif
/* default system version for int21/ah=30 */
p->ps_retdosver = (os_setver_minor ps_parent = cu_psp;
/* previous psp pointer */
p->ps_prevpsp = q;
/* Environment and memory useage parameters */
/* memory size in paragraphs */
p->ps_size = psize;
/* File System parameters */
/* maximum open files */
p->ps_maxfiles = 20;
fmemset(p->ps_files, 0xff, 20);
/* open file table pointer */
p->ps_filetab = p->ps_files;
/* clone the file table -- 0xff is unused */
for (i = 0; i < 20; i++)
if (CloneHandle(i) >= 0)
p->ps_files[i] = q->ps_filetab[i];
/* first command line argument */
p->ps_fcb1.fcb_drive = 0;
fmemset(p->ps_fcb1.fcb_fname, ' ', FNAME_SIZE + FEXT_SIZE);
/* second command line argument */
p->ps_fcb2.fcb_drive = 0;
fmemset(p->ps_fcb2.fcb_fname, ' ', FNAME_SIZE + FEXT_SIZE);
/* local command line */
p->ps_cmd.ctCount = 0;
p->ps_cmd.ctBuffer[0] = 0xd; /* command tail */
}
STATIC UWORD patchPSP(UWORD pspseg, UWORD envseg, exec_blk FAR * exb,
BYTE FAR * fnam)
{
psp FAR *psp;
mcb FAR *pspmcb;
int i;
BYTE FAR *np;
UWORD fakever;
pspmcb = MK_FP(pspseg, 0);
++pspseg;
psp = MK_FP(pspseg, 0);
/* complete the psp by adding the command line and FCBs */
fmemcpy(&psp->ps_cmd, exb->exec.cmd_line, sizeof(CommandTail));
if (FP_OFF(exb->exec.fcb_1) != 0xffff)
{
fmemcpy(&psp->ps_fcb1, exb->exec.fcb_1, 16);
fmemcpy(&psp->ps_fcb2, exb->exec.fcb_2, 16);
}
/* identify the mcb as this functions' */
pspmcb->m_psp = pspseg;
/* Patch in environment segment, if present, also adjust its MCB */
if (envseg)
{
((mcb FAR *) MK_FP(envseg, 0))->m_psp = pspseg;
envseg++;
}
psp->ps_environ = envseg;
/* use the file name less extension - left adjusted and */
np = fnam;
for (;;)
{
switch (*fnam++)
{
case '\0':
goto set_name;
case ':':
case '/':
case '\\':
np = fnam;
}
}
set_name:
for (i = 0; i < 8 && np[i] != '.' && np[i] != '\0'; i++)
{
pspmcb->m_name[i] = toupper(np[i]);
}
if (i < 8)
pspmcb->m_name[i] = '\0';
if ((fakever = SetverGetVersion(setverPtr, np)) != 0)
{
psp->ps_retdosver = fakever;
}
/* return value: AX value to be passed based on FCB values */
return (get_cds1(psp->ps_fcb1.fcb_drive) ? 0 : 0xff) |
(get_cds1(psp->ps_fcb2.fcb_drive) ? 0 : 0xff00);
}
STATIC int load_transfer(UWORD ds, exec_blk *exp, UWORD fcbcode, COUNT mode)
{
psp FAR *p = MK_FP(ds, 0);
psp FAR *q = MK_FP(cu_psp, 0);
/* Transfer control to the executable */
p->ps_parent = cu_psp;
p->ps_prevpsp = q;
q->ps_stack = (BYTE FAR *)user_r;
user_r->FLAGS &= ~FLG_CARRY;
cu_psp = ds;
/* process dta */
dta = &p->ps_cmd;
if (mode == LOADNGO)
{
iregs FAR *irp;
/* build the user area on the stack */
irp = (iregs FAR *)(exp->exec.stack - sizeof(iregs));
/* start allocating REGs (as in MS-DOS - some demos expect them so --LG) */
/* see http://www.beroset.com/asm/showregs.asm */
irp->DX = irp->ES = irp->DS = ds;
irp->CS = FP_SEG(exp->exec.start_addr);
irp->SI = irp->IP = FP_OFF(exp->exec.start_addr);
irp->DI = FP_OFF(exp->exec.stack);
irp->BP = 0x91e; /* this is more or less random but some programs
expect 0x9 in the high byte of BP!! */
irp->AX = irp->BX = fcbcode;
irp->CX = 0xFF;
irp->FLAGS = 0x200;
if (InDOS)
--InDOS;
exec_user(irp, 1);
/* We should never be here
fatal("KERNEL RETURNED!!!"); */
}
/* mode == LOAD */
exp->exec.stack -= 2;
*((UWORD FAR *)(exp->exec.stack)) = fcbcode;
return SUCCESS;
}
/* Now find out how many paragraphs are available
considering a threshold, trying HIGH then LOW */
STATIC int ExecMemLargest(UWORD *asize, UWORD threshold)
{
int rc;
if (mem_access_mode & 0x80)
{
mem_access_mode &= ~0x80;
mem_access_mode |= 0x40;
rc = DosMemLargest(asize);
mem_access_mode &= ~0x40;
/* less memory than the .COM/.EXE file has:
try low memory first */
if (rc != SUCCESS || *asize < threshold)
rc = DosMemLargest(asize);
mem_access_mode |= 0x80;
}
else
rc = DosMemLargest(asize);
return (*asize < threshold ? DE_NOMEM : rc);
}
STATIC int ExecMemAlloc(UWORD size, seg *para, UWORD *asize)
{
/* We can still get an error on first fit if the above */
/* returned size was a best fit case */
/* ModeLoadHigh = 80 = try high, then low */
int rc = DosMemAlloc(size, mem_access_mode, para, asize);
if (rc != SUCCESS)
{
if (rc == DE_NOMEM)
{
rc = DosMemAlloc(0, LARGEST, para, asize);
if ((mem_access_mode & 0x80) && (rc != SUCCESS))
{
mem_access_mode &= ~0x80;
rc = DosMemAlloc(0, LARGEST, para, asize);
mem_access_mode |= 0x80;
}
}
}
else
{
/* with no error, we got exactly what we asked for */
*asize = size;
}
/* This should never happen, but ... */
if (rc == SUCCESS && *asize < size)
{
DosMemFree(*para);
return DE_NOMEM;
}
return rc;
}
COUNT DosComLoader(BYTE FAR * namep, exec_blk * exp, COUNT mode, COUNT fd)
{
UWORD mem;
UWORD env, asize = 0;
{
UWORD com_size;
{
ULONG com_size_long = SftGetFsize(fd);
/* maximally 64k - 256 bytes stack -
256 bytes psp */
com_size = ((UWORD)min(com_size_long, 0xfe00u) >> 4) + 0x10;
}
if ((mode & 0x7f) != OVERLAY)
{
COUNT rc;
UBYTE UMBstate = uppermem_link;
UBYTE orig_mem_access = mem_access_mode;
if (mode & 0x80)
{
mem_access_mode |= 0x80;
DosUmbLink(1); /* link in UMBs */
}
rc = ChildEnv(exp, &env, namep);
#if DEBUG
if (rc != SUCCESS) { ProcDbgPrintf(("Failed to create ChildEnv\n")); }
#endif
/* COMFILES will always be loaded in largest area. is that true TE */
/* yes, see RBIL, int21/ah=48 -- Bart */
if (rc == SUCCESS)
rc = ExecMemLargest(&asize, com_size);
if (rc == SUCCESS)
/* Allocate our memory and pass back any errors */
rc = ExecMemAlloc(asize, &mem, &asize);
if (rc != SUCCESS)
DosMemFree(env);
if (mode & 0x80)
{
DosUmbLink(UMBstate); /* restore link state */
mem_access_mode = orig_mem_access;
mode &= 0x7f;
}
if (rc != SUCCESS)
return rc;
++mem;
}
else
mem = exp->load.load_seg;
}
ProcDbgPrintf(("DosComLoader. Loading '%S' at %04x\n", namep, mem));
/* Now load the executable */
{
BYTE FAR *sp;
if (mode == OVERLAY) /* memory already allocated */
sp = MK_FP(mem, 0);
else /* test the filesize against the allocated memory */
sp = MK_FP(mem, sizeof(psp));
/* MS DOS always only loads the very first 64KB - sizeof(psp) bytes.
-- 1999/04/21 ska */
/* rewind to start */
SftSeek(fd, 0, 0);
/* read everything, but at most 64K - sizeof(PSP) */
/* lpproj: some device drivers (not exe) are larger than 0xff00bytes... */
DosRWSft(fd, (mode == OVERLAY) ? 0xfffeU : 0xff00U, sp, XFR_READ);
DosCloseSft(fd, FALSE);
}
if (mode == OVERLAY)
return SUCCESS;
{
UWORD fcbcode;
psp FAR *p;
/* point to the PSP so we can build it */
setvec(0x22, (intvec)MK_FP(user_r->CS, user_r->IP));
child_psp(mem, cu_psp, mem + asize);
fcbcode = patchPSP(mem - 1, env, exp, namep);
/* set asize to end of segment */
if (asize > 0x1000)
asize = 0x1000;
if (asize < 0x11)
return DE_NOMEM;
asize -= 0x11;
/* CP/M compatibility--size of first segment for .COM files
while preserving the far call to 0:00c0 +
copy in HMA at ffff:00d0 */
p = MK_FP(mem, 0);
p->ps_reentry = MK_FP(0xc - asize, asize ps_isv22);
setvec(0x23, p->ps_isv23);
setvec(0x24, p->ps_isv24);
/* And free all process memory if not a TSR return */
network_redirector(REM_PROCESS_END);
/* might be a good idea to do that after closing
but doesn't help NET either TE */
if (!tsr && p->ps_parent != cu_psp)
{
network_redirector(REM_CLOSEALL);
for (i = 0; i < p->ps_maxfiles; i++)
{
DosClose(i);
}
FcbCloseAll();
FreeProcessMem(cu_psp);
}
cu_psp = p->ps_parent;
q = MK_FP(cu_psp, 0);
irp = (iregs FAR *) q->ps_stack;
irp->CS = FP_SEG(p->ps_isv22);
irp->IP = FP_OFF(p->ps_isv22);
irp->FLAGS = 0x200; /* clear trace and carry flags, set interrupt flag */
if (InDOS)
--InDOS;
exec_user((iregs FAR *) q->ps_stack, 0);
}
COUNT DosExeLoader(BYTE FAR * namep, exec_blk * exp, COUNT mode, COUNT fd)
{
UWORD mem, env, start_seg, asize = 0;
UWORD exe_size;
{
UWORD image_size;
/* compute image size by removing the offset from the */
/* number pages scaled to bytes plus the remainder and */
/* the psp */
#if 0
if (ExeHeader.exPages >= 2048)
return DE_INVLDDATA; /* we're not able to get >=1MB in dos memory */
#else
/* TurboC++ 3 BOSS NE stub: image > 1 MB but load only "X mod 1 MB" */
/* ExeHeader.exPages &= 0x7ff; */ /* just let load.load_seg;
}
/* Now load the executable */
/* offset to start of image */
if (SftSeek(fd, ExeHeader.exHeaderSize * 16UL, 0) != SUCCESS)
{
if (mode != OVERLAY)
{
DosMemFree(--mem);
DosMemFree(env);
}
return DE_INVLDDATA;
}
/* create the start seg for later computations */
start_seg = mem;
exe_size = image_size;
if (mode != OVERLAY)
{
exe_size -= sizeof(psp) / 16;
start_seg += sizeof(psp) / 16;
if (exe_size > 0 && (ExeHeader.exMinAlloc | ExeHeader.exMaxAlloc) == 0)
{
mcb FAR *mp = MK_FP(mem - 1, 0);
/* then the image should be placed as high as possible */
start_seg += mp->m_size - image_size;
}
}
}
/* read in the image in 32256 chunks */
{
int nBytesRead, toRead = CHUNK;
seg sp = start_seg;
while (1)
{
if (exe_size < CHUNK/16)
toRead = exe_size*16;
nBytesRead = (int)DosRWSft(fd, toRead, MK_FP(sp, 0), XFR_READ);
if (nBytesRead < toRead || exe_size load.reloc;
}
else
{
/* spot = MK_FP(reloc[1] + mem + 0x10, reloc[0]); */
spot = MK_FP(reloc[1] + start_seg, reloc[0]);
*spot += start_seg;
}
}
}
/* and finally close the file */
DosCloseSft(fd, FALSE);
/* exit here for overlay */
if (mode == OVERLAY)
return SUCCESS;
{
UWORD fcbcode;
/* point to the PSP so we can build it */
setvec(0x22, (intvec)MK_FP(user_r->CS, user_r->IP));
child_psp(mem, cu_psp, mem + asize);
fcbcode = patchPSP(mem - 1, env, exp, namep);
exp->exec.stack =
MK_FP(ExeHeader.exInitSS + start_seg, ExeHeader.exInitSP);
exp->exec.start_addr =
MK_FP(ExeHeader.exInitCS + start_seg, ExeHeader.exInitIP);
/* Transfer control to the executable */
load_transfer(mem, exp, fcbcode, mode);
}
return SUCCESS;
}
/* mode = LOAD or EXECUTE
ep = EXE block
lp = filename to load (string)
leb = local copy of exe block
*/
COUNT DosExec(COUNT mode, exec_blk FAR * ep, BYTE FAR * lp)
{
COUNT rc;
COUNT fd;
if ((mode & 0x7f) > 3 || (mode & 0x7f) == 2)
return DE_INVLDFMT;
fmemcpy(&TempExeBlock, ep, sizeof(exec_blk));
/* If file not found - free ram and return error */
if (IsDevice(lp) || /* we don't want to execute C:>NUL */
(fd = (short)DosOpenSft(lp, O_LEGACY | O_OPEN | O_RDONLY, 0)) < 0)
{
return DE_FILENOTFND;
}
rc = (int)DosRWSft(fd, sizeof(exe_header), (BYTE FAR *)&ExeHeader, XFR_READ);
if (rc == sizeof(exe_header) &&
(ExeHeader.exSignature == MAGIC || ExeHeader.exSignature == OLD_MAGIC))
{
rc = DosExeLoader(lp, &TempExeBlock, mode, fd);
}
else if (rc != 0)
{
rc = DosComLoader(lp, &TempExeBlock, mode, fd);
} else {
rc = DE_INVLDFMT;
}
DosCloseSft(fd, FALSE);
if (mode == LOAD && rc == SUCCESS)
fmemcpy(ep, &TempExeBlock, sizeof(exec_blk));
return rc;
}
#include "config.h" /* config structure definition */
/* start process 0 (the shell) */
VOID ASMCFUNC P_0(struct config FAR *Config)
{
BYTE *tailp, *endp;
exec_blk exb;
UBYTE mode = Config->cfgP_0_startmode;
/* build exec block and save all parameters here as init part will vanish! */
exb.exec.fcb_1 = exb.exec.fcb_2 = (fcb FAR *)-1L;
exb.exec.env_seg = DOS_PSP + 8;
fstrcpy(Shell, MK_FP(FP_SEG(Config), Config->cfgInit));
/* join name and tail */
fstrcpy(Shell + strlen(Shell), MK_FP(FP_SEG(Config), Config->cfgInitTail));
endp = Shell + strlen(Shell);
for ( ; ; ) /* endless shell load loop - reboot or shut down to exit it! */
{
BYTE *p;
/* if there are no parameters, point to end without "\r\n" */
if((tailp = strchr(Shell,'\t')) == NULL &&
(tailp = strchr(Shell, ' ')) == NULL)
tailp = endp - 2;
/* shift tail to right by 2 to make room for '\0', ctCount */
for (p = endp - 1; p >= tailp; p--)
*(p + 2) = *p;
/* terminate name and tail */
*tailp = *(endp + 2) = '\0';
/* ctCount: just past '\0' do not count the "\r\n" */
exb.exec.cmd_line = (CommandTail *)(tailp + 1);
exb.exec.cmd_line->ctCount = endp - tailp - 2;
#ifdef DEBUG
DebugPrintf(("Process 0 starting: %s%s\n\n", Shell, tailp + 2));
#endif
res_DosExec(mode, &exb, Shell);
put_string("Bad or missing Command Interpreter: "); /* failure _or_ exit */
put_string(Shell);
put_string(tailp + 2);
put_string(" Enter the full shell command line: ");
endp = Shell + res_read(STDIN, Shell, NAMEMAX);
*endp = '\0'; /* terminate string for strchr */
}
}