Metropoli BBS
VIEWER: task.c MODE: TEXT (ASCII)
/****************************************************************/
/*								*/
/*			     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 "../../hdr/portab.h"
#include "globals.h"

/* $Logfile:   C:/dos-c/src/kernel/task.c_v  $ */
#ifndef IPL
static BYTE *RcsId = "$Header:   C:/dos-c/src/kernel/task.c_v   1.2   01 Sep 1995 17:54:22   patv  $";
#endif

/*
 * $Log:   C:/dos-c/src/kernel/task.c_v  $
 * 
 *    Rev 1.2   01 Sep 1995 17:54:22   patv
 * First GPL release.
 * 
 *    Rev 1.1   30 Jul 1995 20:51:58   patv
 * Eliminated version strings in ipl
 * 
 *    Rev 1.0   02 Jul 1995  8:34:06   patv
 * Initial revision.
 */

#define LOADNGO	0
#define LOAD	1
#define	OVERLAY	3

static exe_header header;

#define	CHUNK 16384
#define MAXENV 32768

#ifndef PROTO
COUNT ChildEnv(exec_blk FAR *, UWORD *);
#else
COUNT ChildEnv();
#endif


#ifndef IPL
COUNT 
ChildEnv (exec_blk FAR *exp, UWORD *pChildEnvSeg)
{
	BYTE FAR *pSrc;
	BYTE FAR *pDest;
	UWORD nEnvSize;
	COUNT RetCode;
	UWORD MaxEnvSize;

	/* create a new environment for the process		*/
	if(exp -> exec.env_seg != 0)
	{
		for(nEnvSize = 0, pSrc = MK_FP(exp -> exec.env_seg,0); *pSrc != '\0'; )
		{
			while(*pSrc != '\0')
			{
				++pSrc;
				++nEnvSize;
			}
			/* account for terminating null		*/
			++nEnvSize;
			++pSrc;
		}
	}
	else
	{
/**		nEnvSize = 0;	*/
		pChildEnvSeg = 0;
		return SUCCESS;
	}

	/* Test env size and abort if greater than max		*/
	if(nEnvSize >= MAXENV)
		return DE_INVLDENV;

	/* allocate enough space for env + path			*/
	if((RetCode = DosMemAlloc(long2para(nEnvSize + 65), FIRST_FIT, (seg FAR *)pChildEnvSeg, (UWORD FAR *)MaxEnvSize)) < 0)
		return RetCode;
	else
		pDest = MK_FP(*pChildEnvSeg + 1, 0);

	/* fill the new env and inform the process of its	*/
	/* location throught the psp				*/
	for(pSrc = MK_FP(exp -> exec.env_seg,0); *pSrc != '\0'; )
	{
		fstrncpy(pDest, pSrc, BUFFERSIZE);
		while(*pSrc != '\0')
			++pSrc;
		while(*pDest != '\0')
			++pDest;
		++pSrc;
		++pDest;
	}
	*pDest = '\0';
	return SUCCESS;
}
#endif


/* The following code is 8086 dependant				*/
VOID 
new_psp (psp FAR *p, int psize)
{
	REG COUNT i;
	BYTE FAR *lpPspBuffer;

	/* Clear out new psp first				*/
	for(lpPspBuffer = p, i = 0; i < sizeof(psp) ; ++i)
		*lpPspBuffer = 0;
	/* initialize all entries and exits			*/
	/* CP/M-like exit point					*/
	p -> ps_exit = 0x20cb;
	/* CP/M-like entry point - jump to special entry	*/
	p -> ps_farcall= 0xea;
#ifndef IPL
	p -> ps_reentry = int21_entry;
#endif
	/* unix style call - 0xcd 0x21 0xcb (int 21, retf)	*/
	p -> ps_unix[0] = 0xcd;
	p -> ps_unix[1] = 0x21;	
	p -> ps_unix[2] = 0xcb;	

	/* Now for parent-child relationships			*/
	/* parent psp segment					*/
	p -> ps_parent = DOS_PSP;
	/* previous psp pointer					*/
	p -> ps_prevpsp = (BYTE FAR *)-1;

#ifndef IPL
	/* Environment and memory useage parameters		*/
	/* memory size in paragraphs				*/
	p -> ps_size = psize;
	/* environment paragraph				*/
	p -> ps_environ = 0;
	/* process dta						*/
	p -> ps_dta = (BYTE FAR *)(&p -> ps_cmd_count);
	/* terminate address					*/
	p -> ps_isv22 = int22_handler;
	/* break address					*/
	p -> ps_isv23 = int23_handler;
	/* critical error address				*/
	p -> ps_isv24 = int24_handler;
#endif

	/* File System parameters				*/
	/* user stack pointer - int 21				*/
	p -> ps_stack = (BYTE FAR *)-1;
	/* file table - 0xff is unused				*/
	for(i = 0; i < 20; i++)
		p -> ps_files[i] = 0xff;
	/* initialize stdin, stdout, etc			*/
	p -> ps_files[STDIN] = 0;	/* stdin		*/
	p -> ps_files[STDOUT] = 1;	/* stdout		*/
	p -> ps_files[STDERR] = 2;	/* stderr		*/
	p -> ps_files[STDAUX] = 3;	/* stdaux		*/
	p -> ps_files[STDPRN] = 4;	/* stdprn		*/
	/* maximum open files					*/
	p -> ps_maxfiles = 20;
	/* open file table pointer				*/
	p -> ps_filetab = p -> ps_files;
	/* first command line argument				*/
	p -> ps_fcb1.fcb_drive = 1;
	/* second command line argument				*/
	p -> ps_fcb2.fcb_drive = 2;

	/* local command line					*/
	p -> ps_cmd_count = 0;	/* command tail			*/
	p -> ps_cmd[0] = 0;	/* command tail			*/
}


#ifndef IPL
static COUNT 
DosComLoader (BYTE FAR *namep, exec_blk FAR *exp, COUNT mode)
{
	COUNT rc, env_size, nread;
	UWORD mem;
	UWORD env, asize;
	BYTE FAR *sp, FAR *dp;
	psp FAR *p;
	mcb FAR *mp;
	iregs FAR *irp;
	LONG com_size;

	if(mode != OVERLAY)
	{
		if((rc = ChildEnv(exp, &env)) != SUCCESS)
			return rc;
	}
	else
		mem = exp -> load.load_seg;

	/* Allocate our memory and pass back any errors		*/
	if((rc = DosMemAlloc(0, LARGEST, (seg FAR *)&mem, (UWORD FAR *)&asize)) < 0)
	{
		if(env != 0)
			DosMemFree(env);
		return rc;
	}
	else
	{
		mp = MK_FP(mem, 0);
		++mem;
	}

	/* Now load the executable				*/
	/* If file not found - error				*/
	/* NOTE - this is fatal because we lost it in transit	*/
	/* from DosExec!					*/
	if((rc = dos_open(namep, 0)) < 0)
		fatal("(DosComLoader) com file lost in transit");
	/* do it in 32K chunks					*/
	sp = MK_FP(mem, sizeof(psp));
	for(com_size = 65536l, nread = 0; com_size > 0; )
	{
		nread = dos_read(rc, sp, CHUNK);
		sp += nread;
		com_size -= nread;
		if(nread < CHUNK)
			break;
	}
	dos_close(rc);

	if(mode == OVERLAY)
		return SUCCESS;

	/* point to the PSP so we can build it			*/
	p = MK_FP(mem, 0);
	new_psp(p, mem + asize);

	/* clone the file table					*/
	if(InDOS > 0)
	{
		psp FAR *q;
		REG COUNT i;

		q = MK_FP(cu_psp, 0);
		for(i = 0; i < 20; i++)
		{
			REG COUNT ret;

			if(q -> ps_files[i] != 0xff
			 && ((ret = CloneHandle(q -> ps_files[i])) >= 0))
				p -> ps_files[i] = ret;
			else
				p -> ps_files[i] = 0xff;
		}
	}

	p -> ps_environ = env == 0 ? 0 : env + 1;

	/* complete the psp by adding the command line		*/
	p -> ps_cmd_count = exp -> exec.cmd_line -> ctCount;
	fbcopy(exp -> exec.cmd_line -> ctBuffer, p -> ps_cmd,
		p -> ps_cmd_count > 127 ? 127 : p -> ps_cmd_count);

	/* stick a new line on the end for safe measure		*/
	p -> ps_cmd[p -> ps_cmd_count] = '\r';

	/* identify the mcb as this functions'			*/
	/* use the file name less extension - left adjusted and	*/
	/* space filled						*/
	mp -> m_psp = mem + 1;
	for(asize = 0; asize < 8; asize++)
	{
		if(namep[asize] != '.')
			mp -> m_name[asize] = namep[asize];
		else
			break;
	}
	for(; asize < 8; asize++)
			mp -> m_name[asize] = ' ';

	/* build the user area on the stack			*/
	irp = MK_FP(mem, (0xfffe - sizeof(iregs)));

	/* start allocating REGs				*/
	irp -> ES = irp -> DS = mem;
	irp -> CS = mem;
	irp -> IP = 0x100;
	irp -> AX = 0xffff; /* for now, until fcb code is in	*/
	irp -> BX =
	irp -> CX =
	irp -> DX =
	irp -> SI =
	irp -> DI =
	irp -> BP = 0;
	irp -> FLAGS = 0;

	/* Transfer control to the executable			*/
	p -> ps_parent = cu_psp;
	p -> ps_prevpsp = (BYTE FAR *)MK_FP(cu_psp, 0);
	p -> ps_stack = (BYTE FAR *)user_r;
	switch(mode)
	{
	case LOADNGO:
		cu_psp = mem;
		dta = p -> ps_dta;
		exec_user(irp);
		/* We should never be here			*/
		fatal("KERNEL RETURNED!!!");
		break;

	case LOAD:
		exp -> exec.stack = (BYTE FAR *)irp;
		exp -> exec.start_addr = MK_FP(irp -> CS, irp -> IP);
		return SUCCESS;
	}
}


VOID 
return_user (void)
{
	psp FAR *p, FAR *q;
	REG COUNT i;

	/* Close all files					*/
	for(i = 0; i < 20; i++)
		DosClose(i);

	/* restore parent					*/
	p = MK_FP(cu_psp, 0);

	/* When process returns - restore the isv		*/
	setvec(0x22, p -> ps_isv22);
	setvec(0x23, p -> ps_isv23);
	setvec(0x24, p -> ps_isv24);

	/* And free all process memory if not a TSR return	*/
	if(!tsr)
	{
		if(p -> ps_environ != (UWORD)0)
			DosMemFree(--p -> ps_environ);
		DosMemFree(--cu_psp);
	}
	cu_psp = p -> ps_parent;
	q = MK_FP(cu_psp,0);
	dta = q -> ps_dta;
	exec_user(p -> ps_stack);
}
#endif

static COUNT 
DosExeLoader (BYTE FAR *namep, exec_blk FAR *exp, COUNT mode)
{
	COUNT rc, env_size, i, nBytesRead;
	UWORD mem, env, asize, start_seg;
	ULONG image_size;
	ULONG image_offset;
	BYTE FAR *sp, FAR * dp;
	psp FAR *p;
	mcb FAR *mp;
	iregs FAR *irp;
	UWORD reloc[2];
	seg FAR *spot;
	LONG exe_size;
#ifdef IPL
	BYTE szNameBuffer[64];
#endif

#ifndef IPL
	/* Clone the environement and create a memory arena	*/
	if(mode != OVERLAY)
	{
		if((rc = ChildEnv(exp, &env)) != SUCCESS)
			return rc;
	}
	else
		mem = exp -> load.load_seg;
#endif

	/* compute image offset from the header			*/
#ifdef IPL
	fscopy(namep, (BYTE FAR *)szNameBuffer);
	printf("\nEXE loader loading: %s", szNameBuffer);
#endif
	image_offset = (ULONG)header.exHeaderSize * 16l;

	/* compute image size by removing the offset from the	*/
	/* number pages scaled to bytes	plus the remainder and	*/
	/* the psp						*/
	/*  First scale the size				*/
	image_size = (ULONG)(header.exPages) * 512l;
#if 0
	/* remove the offset					*/
	image_size -= image_offset;
	/* add in the remainder bytes				*/
	if(header.exExtraBytes != 0)
		image_size -= (ULONG)(512 - header.exExtraBytes);
#endif
	/* and finally add in the psp size			*/
	if(mode != OVERLAY)
		image_size += (ULONG)long2para((LONG)sizeof(psp));

#ifndef IPL
	if(mode != OVERLAY)
	{
		/* Now find out how many paragraphs are available	*/
		if((rc = DosMemLargest((seg FAR *)&asize)) != SUCCESS)
			return rc;
		else
		{
			exe_size = (LONG)long2para(image_size) + header.exMinAlloc + long2para((LONG)sizeof(psp));
			if(exe_size > asize)
				return DE_NOMEM;
			else if(((LONG)long2para(image_size) + header.exMaxAlloc + long2para((LONG)sizeof(psp))) < asize)
				exe_size = (LONG)long2para(image_size) + header.exMaxAlloc + long2para((LONG)sizeof(psp));
			else
				exe_size = asize;
		}

		/* Allocate our memory and pass back any errors		*/
		/* We can still get an error on first fit if the above	*/
		/* returned size was a bet fit case			*/
		if((rc = DosMemAlloc((seg)exe_size, FIRST_FIT, (seg FAR *)&mem, (UWORD FAR *)&asize)) < 0)
		{
			if(rc == DE_NOMEM)
			{
				if((rc = DosMemAlloc(0, LARGEST, (seg FAR *)&mem, (UWORD FAR *)&asize)) < 0)
				{
					if(env != 0)
						DosMemFree(env);
					return rc;
				}
				/* This should never happen, but ...	*/
				if(asize < exe_size)
				{
					if(env != 0)
						DosMemFree(env);
					DosMemFree(mem);
					return rc;
				}
			}
			else
			{
				if(env != 0)
					DosMemFree(env);
				return rc;
			}
		}
		else
		/* with no error, we got exactly what we asked for	*/
			asize = exe_size;
	}
#else
	mem = KERNSEG;
#endif

#ifndef IPL
	if(mode != OVERLAY)
	{
#endif
		/* memory found large enough - continue processing	*/
		mp = MK_FP(mem, 0);
		++mem;
#ifndef IPL
	}
	else
		mem = exp -> load.load_seg;
#endif

#ifdef IPL
	printf(".");
#endif
	/* create the start seg for later computations		*/
	if(mode == OVERLAY)
		start_seg = mem;
	else
		start_seg = mem + long2para((LONG)sizeof(psp));

	/* Now load the executable				*/
	/* If file not found - error				*/
	/* NOTE - this is fatal because we lost it in transit	*/
	/* from DosExec!					*/
	if((rc = dos_open(namep, 0)) < 0)
		fatal("(DosExeLoader) exe file lost in transit");

#ifdef IPL
	printf(".");
#endif
	/* offset to start of image				*/
	if (dos_lseek(rc, image_offset, 0) != image_offset)
	{
#ifndef IPL
		if(mode != OVERLAY)
		{
			DosMemFree(--mem);
			if(env != 0)
				DosMemFree(env);
		}
#endif
		return DE_INVLDDATA;
	}

#ifdef IPL
	printf(".");
#endif

	/* read in the image in 32K chunks			*/
	if(mode != OVERLAY)
		exe_size = image_size - long2para((LONG)sizeof(psp));
	else
		exe_size = image_size;
	sp = MK_FP(start_seg, 0x0);
	while(exe_size > 0)
	{
		nBytesRead = dos_read((COUNT)rc, (VOID FAR *)sp, (COUNT)(exe_size < CHUNK ? exe_size : CHUNK));
		sp = add_far((VOID FAR *)sp, (ULONG)CHUNK);
		exe_size -= nBytesRead;
		if(nBytesRead == 0 || exe_size <= 0)
			break;
#ifdef IPL
		printf(".");
#endif
	}

#if 0
	/* Error if we did not read the entire image		*/
	if(exe_size != 0)
		fatal("Broken exe loader (exe_size != 0)");
#endif

	/* relocate the image for new segment			*/
	dos_lseek(rc, (LONG)header.exRelocTable, 0);
	for (i=0; i < header.exRelocItems; i++)
	{
		if (dos_read(rc, (VOID FAR *)&reloc[0], sizeof(reloc)) != sizeof(reloc))
		{
			return DE_INVLDDATA;
		}
		if(mode == OVERLAY)
		{
			spot = MK_FP(reloc[1] + mem, reloc[0]);
			*spot = *spot + exp -> load.reloc;
		}
		else
		{
		/*	spot = MK_FP(reloc[1] + mem + 0x10, reloc[0]);*/
			spot = MK_FP(reloc[1] + start_seg, reloc[0]);
			*spot = *spot + start_seg;
		}
	}

#ifdef IPL
	printf(".");
#endif
	/* and finally close the file				*/
	dos_close(rc);

	/* exit here for overlay				*/
	if(mode == OVERLAY)
		return SUCCESS;

	/* point to the PSP so we can build it			*/
	p = MK_FP(mem, 0);
	new_psp(p, mem + asize);

#ifndef IPL
	/* clone the file table					*/
	if(InDOS > 0)
	{
		psp FAR *q;

		q = MK_FP(cu_psp, 0);
		for(i = 0; i < 20; i++)
		{
			REG COUNT ret;

			if(q -> ps_files[i] != 0xff
			 && ((ret = CloneHandle(q -> ps_files[i])) >= 0))
				p -> ps_files[i] = ret;
			else
				p -> ps_files[i] = 0xff;
		}
	}

	p -> ps_environ = env == 0 ? 0 : env + 1;
#else
	p -> ps_environ = 0;
#endif

	/* complete the psp by adding the command line		*/
	p -> ps_cmd_count = exp -> exec.cmd_line -> ctCount;
	fbcopy(exp -> exec.cmd_line -> ctBuffer, p -> ps_cmd,
		p -> ps_cmd_count > 127 ? 127 : p -> ps_cmd_count);

	/* stick a new line on the end for safe measure		*/
	p -> ps_cmd[p -> ps_cmd_count] = '\r';

	/* identify the mcb as this functions'			*/
	/* use the file name less extension - left adjusted and	*/
	/* space filled						*/
	mp -> m_psp = mem + 1;
	for(i = 0; i < 8; i++)
	{
		if(namep[i] != '.')
			mp -> m_name[i] = namep[i];
		else
			break;
	}
	for(; i < 8; i++)
			mp -> m_name[i] = ' ';

	/* build the user area on the stack			*/
	irp = MK_FP(header.exInitSS + start_seg, ((header.exInitSP - sizeof(iregs)) & 0xffff));

#ifdef IPL
	printf(".\n");
#endif
	/* start allocating REGs				*/
	/* Note: must match es & ds memory segment		*/
	irp -> ES = irp -> DS = mem;
	irp -> CS = header.exInitCS + start_seg;
	irp -> IP = header.exInitIP;
	irp -> AX = 0xffff; /* for now, until fcb code is in	*/
#ifdef IPL
	irp -> BX = BootDrive;
	irp -> CX = NumFloppies;
#else
	irp -> BX =
	irp -> CX =
#endif
	irp -> DX =
	irp -> SI =
	irp -> DI =
	irp -> BP = 0;
	irp -> FLAGS = 0;

	/* Transfer control to the executable			*/
	p -> ps_parent = cu_psp;
	p -> ps_prevpsp = (BYTE FAR *)MK_FP(cu_psp, 0);
	p -> ps_stack = (BYTE FAR *)user_r;
#ifdef IPL
	printf("Starting kernel ...\n");
#endif
	switch(mode)
	{
	case LOADNGO:
		cu_psp = mem;
		dta = p -> ps_dta;
		exec_user(irp);
		/* We should never be here			*/
		fatal("KERNEL RETURNED!!!");
		break;

#ifndef IPL
	case LOAD:
		exp -> exec.stack = (BYTE FAR *)irp;
		exp -> exec.start_addr = MK_FP(irp -> CS, irp -> IP);
		return SUCCESS;
#endif
	}
}


COUNT 
DosExec (COUNT mode, exec_blk FAR *ep, BYTE FAR *lp)
{
	COUNT rc, nNumRead;

	/* If file not found - free ram and return error	*/
#ifdef IPL
	printf(".");
#endif
	if((rc = dos_open(lp, 0)) < 0)
		return DE_FILENOTFND;
	if((nNumRead = dos_read(rc, (VOID FAR *)&header, sizeof(exe_header)))
	 != sizeof(exe_header))
	{
#ifdef IPL
		printf("Internal IPL error - Read failure (read %d != %d)",
		 nNumRead, sizeof(exe_header));
#endif
		return DE_INVLDDATA;
	}

	dos_close(rc);
#ifdef IPL
	printf(".");
#endif

	if(header.exSignature != MAGIC)
#ifndef IPL
		return DosComLoader(lp, ep, mode);
#else
	{
		char szFileName[32];

		fmemcpy((BYTE FAR *)szFileName, lp);
		printf("\nFile: %s (MAGIC = %04x)", szFileName, header.exSignature);
		fatal("IPL can't load .com files!");
	}
#endif
	else
		return DosExeLoader(lp, ep, mode);
}


[ RETURN TO DIRECTORY ]