]> pd.if.org Git - mmurtl/commitdiff
autocommit for file dated 1995-01-21 08:01:08
authorRichard Burgess <>
Sat, 21 Jan 1995 08:01:08 +0000 (08:01 +0000)
committerNathan Wagner <nw@hydaspes.if.org>
Mon, 17 Oct 2016 14:03:47 +0000 (14:03 +0000)
ossource/jobc.c [new file with mode: 0644]

diff --git a/ossource/jobc.c b/ossource/jobc.c
new file mode 100644 (file)
index 0000000..0b80eeb
--- /dev/null
@@ -0,0 +1,1209 @@
+/*\r
+  MMURTL Operating System Source Code\r
+  Copyright 1991,1992,1993,1994 Richard A. Burgess\r
+  ALL RIGHTS RESERVED\r
+  Version 1.0\r
+*/\r
+\r
+/* This file contains functions and data used to support\r
+   loading or terminating jobs (or services).\r
+   It contains the public functions:\r
+\r
+   Chain()                      Loads new job run file in current JCB & PD\r
+   LoadNewJob()                 Loads a new job into a new JCB & PD\r
+   ExitJob()            Exits current job, loads ExitJob if specified\r
+\r
+   GetExitJob()                 Gets run file name that will be loaded upon ExitJob\r
+   SetExitJob()                 Sets run file name to load upon ExitJob\r
+\r
+   SetCmdLine()                 Sets the command line for next job\r
+   GetCmdLine()                 Gets the command line for the current job\r
+\r
+   GetPath()            Gets the path prefix for the current job\r
+   Setpath()            Sets the path prefix for the current job\r
+\r
+   SetUserName()        Sets the Username for the current job\r
+   GetUserName()        Gets the Username for the current job\r
+\r
+*/\r
+\r
+\r
+#define U32 unsigned long\r
+#define S32 long\r
+#define U16 unsigned int\r
+#define S16 int\r
+#define U8 unsigned char\r
+#define S8 char\r
+#define TRUE 1\r
+#define FALSE 1\r
+\r
+#include "MKernel.h"\r
+#include "MMemory.h"\r
+#include "MData.h"\r
+#include "MTimer.h"\r
+#include "MVid.h"\r
+#include "MKbd.h"\r
+#include "MJob.h"\r
+#include "MFiles.h"\r
+\r
+#include "runfile.h"\r
+\r
+#define MEMUSERD 7                     /* User Writable (Data) */\r
+\r
+#define ErcOpCancel     4      /* Operator cancel */\r
+#define ErcOutOfRange  10      /* Bad Exchange specified in OS call */\r
+#define ErcBadJobNum   70      /* A Bad job number was specified */\r
+#define ErcNoExitJob   76      /* No ExitJob specified on ExitJob(n)*/\r
+#define ErcBadRunFile  74      /* Couldn't load specified ExitRunFile */\r
+\r
+/* Near Support Calls from JobCode.ASM */\r
+\r
+extern long AllocJCB(long *pdJobNumRet, char *ppJCBRet);\r
+extern long RemoveRdyJob(char *pJCB);\r
+extern long    GetExchOwner(long Exch, char *pJCBRet);\r
+extern long    SetExchOwner(long Exch, char *pNewJCB);\r
+extern long    SendAbort(long JobNum, long Exch);\r
+\r
+/* Temporary NEAR externals from the monitor program for debugging */\r
+\r
+extern long xprintf(char *fmt, ...);\r
+extern U32 Dump(unsigned char *pb, long cb);\r
+\r
+/* We switch to this stack when we clean out a user\r
+   PD before we rebuild it.\r
+*/\r
+\r
+static long TmpStack[128];             /* 512 byte temporary stack */\r
+\r
+/* Used for allocating/filling in new JCB */\r
+\r
+static struct JCBRec *pNewJCB;         /* Used to access a JCB */\r
+static struct JCBRec *pTmpJCB;\r
+static struct JCBRec *pCrntJCB;\r
+\r
+static long JobNum;\r
+\r
+/* For ExitJob and Chain cause They can't have local vars! */\r
+\r
+static long JobNumE, job_fhE;\r
+static long ExchE, ercE, iE;\r
+static long BogusMsg[2];\r
+static long *pPDE;\r
+static char *pExchJCBE\r
+static long KeyCodeE;\r
+\r
+static char aFileE[80];\r
+static long cbFileE;\r
+\r
+extern unsigned long KillExch; /* From the Monitor */\r
+\r
+/* Run file data */\r
+\r
+static char *pCode, *pData, *pStack;   /* Ptrs in User mem to load to */\r
+static long sCode,   sData,  sStack;   /* Size of segments */\r
+static unsigned long oCode, oData;             /* Offset in file to Code & Data */\r
+\r
+static long  offCode = 0,                              /* Virtual Offset for Code & Data Segs */\r
+         offData = 0;\r
+\r
+static unsigned long   nCDFIX = 0,\r
+                               oCDFIX = 0,\r
+                               nCCFIX = 0,\r
+                               oCCFIX = 0,\r
+                               nDDFIX = 0,\r
+                               oDDFIX = 0,\r
+                               nDCFIX = 0,\r
+                               oDCFIX = 0;\r
+\r
+static char *pStart, filetype;\r
+\r
+static struct tagtype tag;\r
+\r
+\r
+/************* INTERNAL SUPPORT CALLS ******************/\r
+\r
+/******************************************************\r
+ This deallocates all user memory in a PD.  This is\r
+ used when we ExitJob to free up all the memory.\r
+ We get the pointer to the PD and "walk" through all\r
+ the User PTs deallocating linear memory. When all\r
+ PTs have been cleaned, we eliminate the user PDEs.\r
+ This leaves a blank PD for reuse if needed.\r
+ NOTE: This must be called from a task that is\r
+ running in the JCB that owns the memory!\r
+******************************************************/\r
+\r
+static void CleanUserPD(long *pPD)\r
+{\r
+long i, j, k, erc;\r
+unsigned long *pPT;\r
+char *pMem;\r
+\r
+       for (i=768; i<1024; i++) {                      /* Look at each shadow PDE */\r
+               if (pPD[i]) {                                   /* If it's a linear address (non 0)*/\r
+                       pPT = pPD[i] & 0xFFFFF000;      /* Point to Page Table */\r
+                       for (j=0; j<1024; j++) {\r
+\r
+                       /* Get Beginning address for each run to deallocate */\r
+\r
+                               k = 0;  /* nPages to deallocate */\r
+                               pMem = ((i-512) * 0x400000) + (j * 4096);\r
+                               while ((pPT[j++]) && (j<1024))\r
+                                       k++;                    /* one more page */\r
+                               if (k)\r
+                               {                       /* we have pages (one or more) */\r
+                                       erc =  DeAllocPage(pMem, k);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+/*********************************************************\r
+  This opens, reads and validates the run file.\r
+  If all is well, it leaves it open and returns the\r
+  file handle, else it closes the run file and returns\r
+  the error to the caller.\r
+  The caller is responsible for closing the file!!!\r
+*********************************************************/\r
+\r
+static long GetRunFile(char *pFileName, long cbFileName, long *pfhRet)\r
+{\r
+long erc, i, fh, dret;\r
+char fDone, junk;\r
+\r
+       offCode = 0;\r
+       offData = 0;\r
+       nCDFIX = 0;\r
+       oCDFIX = 0;\r
+       nCCFIX = 0;\r
+       oCCFIX = 0;\r
+       nDDFIX = 0;\r
+       oDDFIX = 0;\r
+       nDCFIX = 0;\r
+       oDCFIX = 0;\r
+       fh = 0;\r
+\r
+       *pfhRet = 0;    /* default to 0 */\r
+\r
+                       /* Mode Read, Stream type */\r
+       erc = OpenFile(pFileName, cbFileName, 0, 1, &fh);\r
+\r
+       if (!erc) {     /* File opened OK */\r
+\r
+               fDone = 0;\r
+               while ((!erc) && (!fDone)) {\r
+                       tag.id = 0;\r
+                       erc = ReadBytes (fh, &tag, 5, &dret);\r
+\r
+                       switch (tag.id) {\r
+                               case IDTAG:\r
+                                       erc = ReadBytes (fh, &filetype, 1, &dret);\r
+                                       if ((filetype < 1) || (filetype > 3))\r
+                                               erc = ErcBadRunFile + 30000;\r
+                                       break;\r
+                               case SEGTAG:\r
+                                       erc = ReadBytes (fh, &sStack, 4, &dret);\r
+                                       if (!erc) erc = ReadBytes (fh, &sCode, 4, &dret);\r
+                                       if (!erc) erc = ReadBytes (fh, &sData, 4, &dret);\r
+                                       break;\r
+                               case DOFFTAG:\r
+                                       erc = ReadBytes (fh, &offData, 4, &dret);\r
+                                       break;\r
+                               case COFFTAG:\r
+                                       erc = ReadBytes (fh, &offCode, 4, &dret);\r
+                                       break;\r
+                               case STRTTAG:\r
+                                       erc = ReadBytes (fh, &pStart, 4, &dret);\r
+                                       break;\r
+                               case CODETAG:\r
+                                       erc = GetFileLFA(fh, &oCode);\r
+                                       if (!erc)\r
+                                               erc = SetFileLFA(fh, oCode+tag.len);    /* skip it */\r
+                                       break;\r
+                               case DATATAG:\r
+                                       erc = GetFileLFA(fh, &oData);\r
+                                       if (!erc)\r
+                                               erc = SetFileLFA(fh, oData+tag.len);    /* skip it */\r
+                                       break;\r
+                               case CDFIXTAG:\r
+                                       erc = GetFileLFA(fh, &oCDFIX);\r
+                                       nCDFIX = tag.len/4;\r
+                                       if (!erc)\r
+                                               erc = SetFileLFA(fh, oCDFIX+tag.len);   /* skip it */\r
+                                       break;\r
+                               case CCFIXTAG:\r
+                                       erc = GetFileLFA(fh, &oCCFIX);\r
+                                       nCCFIX = tag.len/4;\r
+                                       if (!erc)\r
+                                               erc = SetFileLFA(fh, oCCFIX+tag.len);   /* skip it */\r
+                                       break;\r
+                               case DDFIXTAG:\r
+                                       erc = GetFileLFA(fh, &oDDFIX);\r
+                                       nDDFIX = tag.len/4;\r
+                                       if (!erc)\r
+                                               erc = SetFileLFA(fh, oDDFIX+tag.len);   /* skip it */\r
+                                       break;\r
+                               case DCFIXTAG:\r
+                                       erc = GetFileLFA(fh, &oDCFIX);\r
+                                       nDCFIX = tag.len/4;\r
+                                       if (!erc)\r
+                                               erc = SetFileLFA(fh, oDCFIX+tag.len);   /* skip it */\r
+                                       break;\r
+                               case ENDTAG:\r
+                                       fDone = TRUE;\r
+                                       break;\r
+                               default:\r
+                                       erc = GetFileLFA(fh, &i);\r
+                                       if (tag.len > 1024)\r
+                                               erc = ErcBadRunFile;\r
+                                       if (!erc)\r
+                                               erc = SetFileLFA(fh, i+tag.len);        /* skip it */\r
+                                       if (erc)\r
+                                               erc = ErcBadRunFile;\r
+                               break;\r
+                       }\r
+\r
+               }\r
+               if (erc)\r
+                       CloseFile(fh);\r
+       }\r
+       if (!erc)\r
+               *pfhRet = fh;\r
+\r
+       return (erc);\r
+}\r
+\r
+/********************************************************/\r
+/*********** PUBLIC CALLS FOR JOB MANAGEMENT ************/\r
+/********************************************************/\r
+\r
+/**************************************************/\r
+long far _SetExitJob(char *pRunFile, long dcbRunFile)\r
+{\r
+long JobNum;\r
+\r
+       GetJobNum(&JobNum);\r
+       GetpJCB(JobNum, &pTmpJCB);              /* Get pJCB to current Job */\r
+       if (dcbRunFile > 79)\r
+               return (ErcBadJobParam);\r
+       else if (!dcbRunFile)\r
+               pTmpJCB->JcbExitRF[0] = 0;\r
+       else {\r
+               CopyData(pRunFile, &pTmpJCB->JcbExitRF[1], dcbRunFile);\r
+               pTmpJCB->JcbExitRF[0] = dcbRunFile;\r
+       }\r
+       return(0);\r
+}\r
+\r
+/**************************************************/\r
+long far _GetExitJob(char *pRunRet, long *pdcbRunRet)\r
+{\r
+long JobNum, i;\r
+\r
+       GetJobNum(&JobNum);\r
+       GetpJCB(JobNum, &pTmpJCB);              /* Get pJCB to current Job */\r
+       i =     pTmpJCB->JcbExitRF[0];\r
+       if (i)\r
+               CopyData(&pTmpJCB->JcbExitRF[1], pRunRet, i);\r
+       *pdcbRunRet = i;\r
+       return(0);\r
+}\r
+\r
+/**************************************************/\r
+long far _SetPath(char *pPath, long dcbPath)\r
+{\r
+long JobNum;\r
+\r
+       GetJobNum(&JobNum);\r
+       GetpJCB(JobNum, &pTmpJCB);              /* Get pJCB to current Job */\r
+       if (dcbPath > 69)\r
+               return (ErcBadJobParam);\r
+       else if (!dcbPath)\r
+               pTmpJCB->sbPath[0] = 0;\r
+       else {\r
+               CopyData(pPath, &pTmpJCB->sbPath[1], dcbPath);\r
+               pTmpJCB->sbPath[0] = dcbPath;\r
+       }\r
+       return(0);\r
+}\r
+\r
+/**************************************************/\r
+long far _GetPath(long JobNum, char *pPathRet, long *pdcbPathRet)\r
+{\r
+long i, erc;\r
+\r
+       erc = GetpJCB(JobNum, &pTmpJCB);  /* Get pJCB to JobNum */\r
+       if (!erc) {\r
+               i =     pTmpJCB->sbPath[0];\r
+               if (i)\r
+                       CopyData(&pTmpJCB->sbPath[1], pPathRet, i);\r
+               *pdcbPathRet = i;\r
+       }\r
+       return(erc);\r
+}\r
+/**************************************************/\r
+long far _SetCmdLine(char *pCmd, long dcbCmd)\r
+{\r
+long JobNum;\r
+\r
+       GetJobNum(&JobNum);\r
+       GetpJCB(JobNum, &pTmpJCB);              /* Get pJCB to current Job */\r
+       if (dcbCmd > 79)\r
+               return (ErcBadJobParam);\r
+       else if (!dcbCmd)\r
+               pTmpJCB->JcbCmdLine[0] = 0;\r
+       else {\r
+               CopyData(pCmd, &pTmpJCB->JcbCmdLine[1], dcbCmd);\r
+               pTmpJCB->JcbCmdLine[0] = dcbCmd;\r
+       }\r
+       return(0);\r
+}\r
+\r
+/**************************************************/\r
+long far _GetCmdLine(char *pCmdRet, long *pdcbCmdRet)\r
+{\r
+long JobNum, i;\r
+\r
+       GetJobNum(&JobNum);\r
+       GetpJCB(JobNum, &pTmpJCB);              /* Get pJCB to current Job */\r
+       i =     pTmpJCB->JcbCmdLine[0];\r
+       if (i)\r
+               CopyData(&pTmpJCB->JcbCmdLine[1], pCmdRet, i);\r
+       *pdcbCmdRet = i;\r
+       return(0);\r
+}\r
+\r
+\r
+/**************************************************/\r
+long far _SetUserName(char *pUser, long dcbUser)\r
+{\r
+long JobNum;\r
+\r
+       GetJobNum(&JobNum);\r
+       GetpJCB(JobNum, &pTmpJCB);              /* Get pJCB to current Job */\r
+       if (dcbUser > 29)\r
+               return (ErcBadJobParam);\r
+       else if (!dcbUser)\r
+               pTmpJCB->sbUserName[0] = 0;\r
+       else {\r
+               CopyData(pUser, &pTmpJCB->sbUserName[1], dcbUser);\r
+               pTmpJCB->sbUserName[0] = dcbUser;\r
+       }\r
+       return(0);\r
+}\r
+\r
+/**************************************************/\r
+long far _GetUserName(char *pUserRet, long *pdcbUserRet)\r
+{\r
+long JobNum, i;\r
+\r
+       GetJobNum(&JobNum);\r
+       GetpJCB(JobNum, &pTmpJCB);              /* Get pJCB to current Job */\r
+       i =     pTmpJCB->sbUserName[0];\r
+       if (i)\r
+               CopyData(&pTmpJCB->sbUserName[1], pUserRet, i);\r
+       *pdcbUserRet = i;\r
+       return(0);\r
+}\r
+\r
+/**************************************************/\r
+long far _SetSysIn(char *pName, long dcbName)\r
+{\r
+long JobNum;\r
+\r
+       GetJobNum(&JobNum);\r
+       GetpJCB(JobNum, &pTmpJCB);              /* Get pJCB to current Job */\r
+       if ((dcbName > 49) || (!dcbName))\r
+               return (ErcBadJobParam);\r
+       else {\r
+               CopyData(pName, &pTmpJCB->JcbSysIn[1], dcbName);\r
+               pTmpJCB->JcbSysIn[0] = dcbName;\r
+       }\r
+       return(0);\r
+}\r
+\r
+/**************************************************/\r
+long far _GetSysIn(char *pFileRet, long *pdcbFileRet)\r
+{\r
+long JobNum, i;\r
+\r
+       GetJobNum(&JobNum);\r
+       GetpJCB(JobNum, &pTmpJCB);              /* Get pJCB to current Job */\r
+       i =     pTmpJCB->JcbSysIn[0];\r
+       if (i)\r
+               CopyData(&pTmpJCB->JcbSysIn[1], pFileRet, i);\r
+       *pdcbFileRet = i;\r
+       return(0);\r
+}\r
+\r
+/**************************************************/\r
+long far _SetSysOut(char *pName, long dcbName)\r
+{\r
+long JobNum;\r
+\r
+       GetJobNum(&JobNum);\r
+       GetpJCB(JobNum, &pTmpJCB);              /* Get pJCB to current Job */\r
+       if ((dcbName > 49) || (!dcbName))\r
+               return (ErcBadJobParam);\r
+       else {\r
+               CopyData(pName, &pTmpJCB->JcbSysOut[1], dcbName);\r
+               pTmpJCB->JcbSysOut[0] = dcbName;\r
+       }\r
+       return(0);\r
+}\r
+\r
+/**************************************************/\r
+long far _GetSysOut(char *pFileRet, long *pdcbFileRet)\r
+{\r
+long JobNum, i;\r
+\r
+       GetJobNum(&JobNum);\r
+       GetpJCB(JobNum, &pTmpJCB);              /* Get pJCB to current Job */\r
+       i =     pTmpJCB->JcbSysOut[0];\r
+       if (i)\r
+               CopyData(&pTmpJCB->JcbSysOut[1], pFileRet, i);\r
+       *pdcbFileRet = i;\r
+       return(0);\r
+}\r
+\r
+/**************************************************/\r
+long far _SetJobName(char *pName, long dcbName)\r
+{\r
+long JobNum;\r
+       GetJobNum(&JobNum);\r
+       GetpJCB(JobNum, &pTmpJCB);              /* Get pJCB to current Job */\r
+       if (dcbName > 13)\r
+               dcbName = 13;\r
+       if (dcbName)\r
+               CopyData(pName, &pTmpJCB->sbJobName[1], dcbName);\r
+       pTmpJCB->sbJobName[0] = dcbName;\r
+       return(0);\r
+}\r
+\r
+/*********************************************************\r
+  This creates and loads a new Job from a RUN file.\r
+  This returns ErcOK and new job number if loaded OK\r
+  else an error is returned.\r
+*********************************************************/\r
+\r
+long far _LoadNewJob(char *pFileName, long cbFileName, long *pJobNumRet)\r
+{\r
+long erc, i, fh, dret, nPages;\r
+unsigned long *pPD, *pPT, *pVid, *pOSPD;\r
+long *pFix;\r
+U32 PhyAdd;\r
+\r
+\r
+       erc =  GetRunFile(pFileName, cbFileName, &fh);\r
+\r
+       if (!erc) {\r
+\r
+               /* We set these to zero so we can tell if they have\r
+               been allocated in case we fail so we know what to deallocate\r
+               */\r
+\r
+               JobNum = 0;\r
+               pNewJCB = 0;\r
+               pPD = 0;\r
+               pPT = 0;\r
+               pVid = 0;\r
+\r
+               erc = AllocJCB(&JobNum, &pNewJCB);\r
+\r
+               /* Alloc OS memory pages required for new job */\r
+\r
+               if (!erc)\r
+                       erc = AllocOSPage(3, &pPD);             /* Job's PD, PT * pVirtVid */\r
+\r
+               pPT = pPD + 4096;                       /* 1 page later */\r
+               pVid = pPD + 8192;                      /* 2 pages later */\r
+\r
+               if (!erc) {\r
+                       FillData(pPT, 4096, 0);         /* Zero user PT */\r
+                       FillData(pVid, 4096, 0);        /* Zero user video */\r
+\r
+                       GetpJCB(1, &pTmpJCB);           /* Get OS pJCB */\r
+                       pOSPD = pTmpJCB->pJcbPD;        /* Pointer to OS PD */\r
+\r
+                       GetPhyAdd(1, pPT, &PhyAdd);     /* Get Phy Add for new PT */\r
+\r
+                       PhyAdd |= MEMUSERD;                     /* Set user code bits in PDE */\r
+\r
+                       pOSPD[256] = PhyAdd;            /* Make User PDE in OS PD */\r
+                       pOSPD[768] = pPT;                       /* Shadow Linear Address of PT */\r
+\r
+                       /* for serious troubleshooting...\r
+                       xprintf("pOSPD  : %08x\r\n", pOSPD);\r
+                       xprintf("pUserPD: %08x\r\n", pPD);\r
+                       xprintf("pUserPT: %08x\r\n", pPT);\r
+                       xprintf("PhyAdd : %08x\r\n", PhyAdd);\r
+                       ReadKbd(&KeyCodeE, 1);\r
+                       */\r
+\r
+                       /* Now we can allocate User Job Memory */\r
+                       /* Allocate user memory for Stack Code and Data */\r
+                       /* This is STILL done in the OS PD */\r
+\r
+                       nPages = sStack/4096;\r
+                       if (sStack%4096) nPages++;\r
+                       erc = AllocPage(nPages, &pStack);\r
+                       sStack = nPages * 4096;                         /* set to whole pages */\r
+\r
+                       nPages = sCode/4096;\r
+                       if (sCode%4096) nPages++;\r
+                       if (!erc)\r
+                               erc = AllocPage(nPages, &pCode);\r
+\r
+                       nPages = sData/4096;\r
+                       if (sData%4096) nPages++;\r
+                       if (!erc)\r
+                               erc = AllocPage(nPages, &pData);\r
+\r
+                       /* Right now, the OS PD looks exacly like we want\r
+                       the User PD to look.  We will now copy the entire\r
+                       OS PD into the User's New PD.\r
+                       */\r
+\r
+                       CopyData(pOSPD, pPD, 4096);             /* Copy OS PD to User PD */\r
+\r
+                       /* All Job memory is now allocated, so let's LOAD IT! */\r
+\r
+                       if (!erc)\r
+                               erc = SetFileLFA(fh, oCode);\r
+                       if (!erc)\r
+                               erc = ReadBytes (fh, pCode, sCode, &dret);\r
+\r
+                       if (!erc)\r
+                               erc = SetFileLFA(fh, oData);\r
+                       if (!erc)\r
+                               erc = ReadBytes (fh, pData, sData, &dret);\r
+\r
+                       /* Now that we have read in the code and data we\r
+                       apply fixups to these segments from the runfile\r
+                       */\r
+\r
+                       if (!erc) {\r
+\r
+                               if (nCDFIX) {\r
+                                       erc = SetFileLFA(fh, oCDFIX);   /* back to fixups */\r
+                                       while ((nCDFIX--) && (!erc)) {\r
+                                               erc = ReadBytes (fh, &i, 4, &dret);\r
+                                               pFix = pCode + i;               /* Where in CSeg */\r
+                                               *pFix = *pFix - offData + pData;\r
+                                       }\r
+                               }\r
+\r
+                               if (nCCFIX) {\r
+                                       erc = SetFileLFA(fh, oCCFIX);   /* back to fixups */\r
+                                       while ((nCCFIX--) && (!erc)) {\r
+                                               erc = ReadBytes (fh, &i, 4, &dret);\r
+                                               pFix = pCode + i;               /* Where in CSeg */\r
+                                               *pFix = *pFix - offCode + pCode;\r
+                                       }\r
+                               }\r
+\r
+                               if (nDCFIX) {\r
+                                       erc = SetFileLFA(fh, oDCFIX);   /* back to fixups */\r
+                                       while ((nDCFIX--) && (!erc)) {\r
+                                               erc = ReadBytes (fh, &i, 4, &dret);\r
+                                               pFix = pData + i;               /* Where in DSeg */\r
+                                               *pFix = *pFix - offCode + pCode;\r
+                                       }\r
+                               }\r
+\r
+                               if (nDDFIX) {\r
+                                       erc = SetFileLFA(fh, oDDFIX);   /* back to fixups */\r
+                                       while ((nDDFIX--) && (!erc)) {\r
+                                               erc = ReadBytes (fh, &i, 4, &dret);\r
+                                               pFix = pData + i;               /* Where in DSeg */\r
+                                               *pFix = *pFix - offData + pData;\r
+                                       }\r
+                               }\r
+\r
+                       }\r
+\r
+                       /* Clean the OS PD of User memory */\r
+\r
+                       FillData(&pOSPD[256], 1024, 0); /* Clean OS PD of User PDEs */\r
+                       FillData(&pOSPD[768], 1024, 0); /* Clean OS PD of User Shadow */\r
+\r
+                       /* Now we fill in the rest of the User's JCB */\r
+\r
+                       pNewJCB->pJcbPD = pPD;                  /* Lin Add of PD */\r
+                       pNewJCB->pJcbStack = pStack;    /* Add of Stack */\r
+                       pNewJCB->sJcbStack = sStack;    /* Size of Code */\r
+                       pNewJCB->pJcbCode  = pCode;             /* Add of Code */\r
+                       pNewJCB->sJcbCode  = sCode;     /* Size of Code */\r
+                       pNewJCB->pJcbData  = pData;             /* Add of Code */\r
+                       pNewJCB->sJcbData  = sData;     /* Size of Code */\r
+\r
+                       pNewJCB->sbUserName[0] = 0;     /* Zero UserName */\r
+                       pNewJCB->sbPath[0] = 0;                 /* No Default path */\r
+                       pNewJCB->JcbExitRF[0] = 0;              /* No Exit Run File */\r
+                       pNewJCB->JcbCmdLine[0] = 0;             /* No Cmd Line */\r
+\r
+                       CopyData("KBD", &pNewJCB->JcbSysIn[1], 3);\r
+                       pNewJCB->JcbSysIn[0] = 3;               /* Size */\r
+\r
+                       CopyData("VID", &pNewJCB->JcbSysOut[1], 3);\r
+                       pNewJCB->JcbSysOut[0] = 3;              /* Size */\r
+\r
+                       pNewJCB->pVidMem = pVid;                /* Default to Virt Vid */\r
+                       pNewJCB->pVirtVid = pVid;               /* Virtual Video memory */\r
+                       pNewJCB->CrntX = 0;                             /* Vid X Position */\r
+                       pNewJCB->CrntY = 0;                             /* Vid Y Position */\r
+                       pNewJCB->nCols = 80;                    /* Columns */\r
+                       pNewJCB->nLines = 25;                   /* Lines */\r
+\r
+                       pNewJCB->VidMode = 0;                   /* 80x25 VGA Color Text */\r
+                       pNewJCB->fCursOn = 1;                   /* Cursor On */\r
+                       pNewJCB->fCursType = 0;                 /* UnderLine */\r
+\r
+\r
+                       /* Finally, we crank up the new task and schedule it for\r
+                       execution!\r
+                       */\r
+                       if (!erc)\r
+                               erc = AllocExch(&i);\r
+\r
+                       if (!erc)\r
+                               erc = NewTask(JobNum, 0x18, 25, 0, i,\r
+                                                         pStack+sStack-4,\r
+                                                         pStart+pCode-offCode);\r
+                       if (!erc)\r
+                               SetExchOwner(i, pNewJCB);       /* Exch now belongs to new JCB */\r
+               }\r
+\r
+               CloseFile(fh);\r
+       }       /* read run file OK */\r
+\r
+       if (!erc)\r
+               *pJobNumRet = JobNum;\r
+\r
+       return(erc);\r
+}\r
+\r
+/*********************************************************\r
+  This loads a job into an existing PD and JCB.  This is\r
+  called by Chain() and may also be called by ExitJob()\r
+  if an ExitJob was specified in the current JCB.\r
+  The PD, pVid & first PT still exist in OS memory.\r
+  (CleanPD left the first PT for us). ExitJob and Chain\r
+  are responsible for opening and validating the runfile\r
+  and setting up the run file variables.\r
+*********************************************************/\r
+\r
+static long LoadJob(char *pJCB, long fh)\r
+{\r
+long erc, i, dret, nPages;\r
+long *pFix;\r
+\r
+       pNewJCB = pJCB;\r
+\r
+       /* Allocate user memory for Stack, Code and Data */\r
+       /* This is done in the context of the USER PD */\r
+\r
+       nPages = sStack/4096;\r
+       if (sStack%4096) nPages++;\r
+       erc = AllocPage(nPages, &pStack);\r
+       sStack = nPages * 4096;                         /* set to whole pages */\r
+\r
+       nPages = sCode/4096;\r
+       if (sCode%4096) nPages++;\r
+       if (!erc)\r
+               erc = AllocPage(nPages, &pCode);\r
+\r
+       nPages = sData/4096;\r
+       if (sData%4096) nPages++;\r
+\r
+       erc = AllocPage(nPages, &pData);\r
+\r
+       /* All Job memory is now allocated, so let's LOAD IT! */\r
+\r
+       if (!erc)\r
+               erc = SetFileLFA(fh, oCode);\r
+       if (!erc)\r
+               erc = ReadBytes (fh, pCode, sCode, &dret);\r
+\r
+       if (!erc)\r
+               erc = SetFileLFA(fh, oData);\r
+       if (!erc)\r
+               erc = ReadBytes (fh, pData, sData, &dret);\r
+\r
+       /* Now that we have read in the code and data we\r
+       apply fixups to these segments from the runfile\r
+       */\r
+\r
+       if (!erc) {\r
+               if (nCDFIX) {\r
+                       erc = SetFileLFA(fh, oCDFIX);   /* back to fixups */\r
+                       while ((nCDFIX--) && (!erc)) {\r
+                               erc = ReadBytes (fh, &i, 4, &dret);\r
+                               pFix = pCode + i;               /* Where in CSeg */\r
+                               *pFix = *pFix - offData + pData;\r
+                       }\r
+               }\r
+\r
+               if (nCCFIX) {\r
+                       erc = SetFileLFA(fh, oCCFIX);   /* back to fixups */\r
+                       while ((nCCFIX--) && (!erc)) {\r
+                               erc = ReadBytes (fh, &i, 4, &dret);\r
+                               pFix = pCode + i;               /* Where in CSeg */\r
+                               *pFix = *pFix - offCode + pCode;\r
+                       }\r
+               }\r
+\r
+               if (nDCFIX) {\r
+                       erc = SetFileLFA(fh, oDCFIX);   /* back to fixups */\r
+                       while ((nDCFIX--) && (!erc)) {\r
+                               erc = ReadBytes (fh, &i, 4, &dret);\r
+                               pFix = pData + i;               /* Where in DSeg */\r
+                               *pFix = *pFix - offCode + pCode;\r
+                       }\r
+               }\r
+\r
+               if (nDDFIX) {\r
+                       erc = SetFileLFA(fh, oDDFIX);   /* back to fixups */\r
+                       while ((nDDFIX--) && (!erc)) {\r
+                               erc = ReadBytes (fh, &i, 4, &dret);\r
+                               pFix = pData + i;               /* Where in DSeg */\r
+                               *pFix = *pFix - offData + pData;\r
+                       }\r
+               }\r
+\r
+               /* Now we fill in the rest of the User's JCB */\r
+               pNewJCB->pJcbStack = pStack;    /* Add of Stack */\r
+               pNewJCB->sJcbStack = sStack;    /* Size of Code */\r
+               pNewJCB->pJcbCode  = pCode;             /* Add of Code */\r
+               pNewJCB->sJcbCode  = sCode;     /* Size of Code */\r
+               pNewJCB->pJcbData  = pData;             /* Add of Code */\r
+               pNewJCB->sJcbData  = sData;     /* Size of Code */\r
+\r
+       }\r
+       CloseFile(fh);\r
+       return(erc);\r
+}\r
+\r
+\r
+/******************************************************\r
+ This is started as new task in the context of a\r
+ job that is being killed off. This is done to allow\r
+ memory access and also reuse the code for ExitJob\r
+ in the monitor. This task, it's exchange, and TSS\r
+ will be reclaimed by the monitor along with the\r
+ JCB and all OS memory pages for the PD,PT and video.\r
+******************************************************/\r
+\r
+void _KillTask(void)\r
+{\r
+\r
+       GetJobNum(&JobNumE);\r
+       GetpJCB(JobNumE, &pTmpJCB);             /* Get pJCB to this Job */\r
+\r
+       /* Clean the PD of all user memory leaving OS memory to be\r
+          deallocated by the caller (monitor or whoever).\r
+       */\r
+\r
+       pPDE = pTmpJCB->pJcbPD;\r
+       CleanUserPD(pPDE);\r
+\r
+       GetTSSExch(&ExchE);\r
+\r
+    ercE = 0;\r
+       while(!ercE)            /* clear the exchange */\r
+               ercE = CheckMsg(ExchE, BogusMsg);\r
+\r
+       ISendMsg(KillExch, ExchE, ErcOpCancel);\r
+       SetPriority(31);\r
+       WaitMsg(ExchE, BogusMsg);\r
+\r
+       while(1); /* in case we get scheduled again RAB */\r
+\r
+       /* He's History! */\r
+}\r
+\r
+\r
+/******************************************************\r
+ This called from one job to kill another job or\r
+ service. This cleans up ALL resources that the\r
+ job had allocated.\r
+ This is used to kill run-away jobs, or terminate a\r
+ job just for the fun ot it.\r
+ It results in a violent death for the job specified.\r
+ This must never be called from a task within the job\r
+ to be killed.  A job may terminate itself with ExitJob().\r
+******************************************************/\r
+\r
+long far _KillJob(long JobNum)\r
+{\r
+long erc;\r
+       /* Make sure it's not the Monitor, Debugger or the current job. */\r
+\r
+       GetJobNum(&JobNumE);\r
+       if ((JobNum == JobNumE) ||\r
+           (JobNum == 1) ||\r
+           (JobNum == 2))\r
+\r
+               return(ErcBadJobNum);\r
+\r
+       erc = GetpJCB(JobNum, &pTmpJCB);                        /* Get pJCB to the Job */\r
+       if (erc)\r
+               return(erc);\r
+\r
+       pTmpJCB->ExitError = ErcOpCancel; /* Operator said DIE! */\r
+\r
+       /* Remove ALL tasks for this job that are at the ReadyQue.\r
+          The task we are in does not belong to the job we are\r
+          killing so we can remove them all.\r
+       */\r
+\r
+       RemoveRdyJob(pTmpJCB);  /* It always returns ErcOk */\r
+\r
+       /* Deallocate ALL exchanges for this job */\r
+\r
+        ercE = 0;\r
+        iE = 0;\r
+        while (ercE != ErcOutOfRange)\r
+        {\r
+               ercE = GetExchOwner(iE, &pExchJCBE);\r
+               if ((!ercE) && (pExchJCBE == pTmpJCB))\r
+                       DeAllocExch(iE);\r
+               iE++;\r
+        }\r
+        ercE = 0;              /* Clear the error */\r
+\r
+       /* Now that the user can't make anymore requests,\r
+          We send "Abort" messages to all services.\r
+          This closes all files that were opened by the Job\r
+       and frees up any other resources held for this\r
+       job by any service.\r
+\r
+          We will allocate one exchange for the job\r
+          that is being killed so we can use it for SendAbort\r
+          and also as the exchange number we send to the\r
+          KillExch in the monitor which will kill of the JCB\r
+          completely (he also switches video and keyboard\r
+          if needed).\r
+        */\r
+\r
+       erc = AllocExch(&ExchE);                /* Get an Exch */\r
+       SetExchOwner(ExchE, pTmpJCB);   /* make him the owner */\r
+       SendAbort(JobNum, ExchE);       /* Notify all services */\r
+\r
+               /*JobNum, CodeSeg, Priority, fDebug, Exch, ESP, EIP */\r
+       erc = NewTask(JobNum, 0x08, 3, 0, ExchE, &TmpStack[127], &_KillTask);\r
+       return(erc);\r
+\r
+       /* He's History! */\r
+}\r
+\r
+/******************************************************\r
+ This called from Exit() in C or directly from a user\r
+ job or service. This cleans up ALL resources that the\r
+ job had allocated.\r
+ This also checks for an exit run file to load if\r
+ one is specified. If no exit run file is specified\r
+ we just kill the JCB entirely and if video and\r
+ keyboard are assigned we assign them to the Monitor.\r
+******************************************************/\r
+\r
+void far _ExitJob(long dError)\r
+{\r
+/* NO LOCAL VARIABLES BECAUSE WE SWITCH STACKS!! */\r
+\r
+       GetJobNum(&JobNumE);\r
+       GetpJCB(JobNumE, &pCrntJCB);            /* Get pJCB to current Job */\r
+       pCrntJCB->ExitError = dError;\r
+\r
+       /* Remove ALL tasks for this job that are at the ReadyQue.\r
+          The task we are in won't be removed because its RUNNING!\r
+       */\r
+\r
+       RemoveRdyJob(pCrntJCB); /* It always returns ErcOk */\r
+\r
+       /* Deallocate all exchanges for this job except the one belonging\r
+          to current TSS!  The Dealloc Exchange call will invalidate\r
+          all TSSs found at exchanges belonging to this user, and\r
+          will also free up RQBs and Link Blocks.  The job will not be\r
+          able to initiate requests or send messages after this unless\r
+          it is done with the TSSExchange because it will get a kernel\r
+          error (invalid exchange).\r
+       */\r
+\r
+        /* Find out what our TSS exchange is so\r
+        we don't deallocate it to! We need it. */\r
+\r
+        GetTSSExch(&ExchE);\r
+\r
+        ercE = 0;\r
+        iE = 0;\r
+        while (ercE != ErcOutOfRange)\r
+        {\r
+               ercE = GetExchOwner(iE, &pExchJCBE);\r
+               if ((!ercE) && (iE != ExchE) && (pExchJCBE == pCrntJCB))\r
+                       DeAllocExch(iE);\r
+               iE++;\r
+        }\r
+\r
+       /* Now that the user can't make anymore requests,\r
+          Send Abort messages to all services.\r
+          This closes all files that were opened by the Job\r
+       and frees up any other resources held for this\r
+       job by any service.\r
+       */\r
+\r
+       SendAbort(JobNumE, ExchE);\r
+\r
+       ercE = 0;                               /* Clear the error */\r
+       while(!ercE)            /* clear the exchange */\r
+               ercE = CheckMsg(ExchE, BogusMsg);\r
+       ercE = 0;                               /* Clear the error */\r
+\r
+       /* We must now switch to a temporary stack so we can\r
+       clean out the user PD (we are on his stack right now!).\r
+       */\r
+\r
+#asm\r
+       MOV EAX, OFFSET _TmpStack\r
+       ADD EAX, 508\r
+       MOV ESP, EAX\r
+       MOV EBP, EAX\r
+#endasm\r
+\r
+       /* Clean the PD of all user memory leaving OS memory for next\r
+       job if there is one.\r
+       */\r
+\r
+       pPDE = pCrntJCB->pJcbPD;\r
+       CleanUserPD(pPDE);\r
+\r
+       /* Look for Exit Run file to load if any exists.  If no exit run\r
+       file, we deallocate the PD and JCB then return to JOB 1. */\r
+\r
+       GetExitJob(aFileE, &cbFileE);           /* Exit Run File!! */\r
+\r
+       if (!cbFileE)\r
+               ercE = ErcNoExitJob;\r
+\r
+       if (!ercE)\r
+               ercE =  GetRunFile(aFileE, cbFileE, &job_fhE);\r
+\r
+       if (!ercE)\r
+               ercE = LoadJob(pCrntJCB, job_fhE);\r
+\r
+       if (!ercE) {\r
+\r
+                       pStart = pStart+pCode-offCode;\r
+\r
+                       /* Now we RETURN to new job's address after we put him\r
+                       on his new stack. */\r
+\r
+#asm\r
+                       MOV EAX, _pStack\r
+                       MOV EBX, _sStack\r
+                       ADD EAX, EBX\r
+                       SUB EAX, 4\r
+                       MOV ESP, EAX\r
+                       MOV EBP, EAX\r
+                       PUSH 18h\r
+                       MOV EAX, _pStart\r
+                       PUSH EAX\r
+                       RETF                            ;We are history!\r
+#endasm\r
+\r
+       }\r
+\r
+       if (ercE) {             /* something failed or we don't have an ExitRF */\r
+\r
+               /* In case there is no job to run or a fatal error has happened\r
+               we send a message (ISendMsg) to the monitor status\r
+               task with our TSSExch and the Error. Then he will WIPE US OUT!\r
+               We use ISend (vice send) so he can't run before we get to\r
+               the exchange otherwise we will be placed back on the readyQueue!\r
+           */\r
+\r
+               ISendMsg(KillExch, ExchE, ercE);\r
+               SetPriority(31);\r
+               WaitMsg(ExchE, BogusMsg);\r
+\r
+               while(1); /* in case we get scheduled again RAB */\r
+\r
+               /* We are NO MORE */\r
+       }\r
+}\r
+\r
+/******************************************************\r
+ This is called to execute a program without changing\r
+ the ExitJob. This is so you can run program B from\r
+ program A and return to program A when program B is\r
+ done.  This runs Job B in the "context" of Job A\r
+ which means Job B inherits the JCB and PD of job A\r
+ so it can use things like the command line and\r
+ path that were set up by A.\r
+ Information can be passed to Job B by calling\r
+ SetCmdLine (if Job B reads it), and also by\r
+ setting the ExitError value in the parameter.\r
+ Chain will only return to you if there was an\r
+ error loading the Job.  In other words, if Chain\r
+ fails in a critial section we try to load the\r
+ ExitJob and pass it the error.\r
+******************************************************/\r
+\r
+long far _Chain(char *pFileName, long cbFileName, long dExitError)\r
+{\r
+/* NO LOCAL VARIABLES BECAUSE WE SWITCH STACKS!! */\r
+\r
+       CopyData(pFileName, aFileE, cbFileName);\r
+       cbFileE = cbFileName;\r
+\r
+       ercE =  GetRunFile(pFileName, cbFileName, &job_fhE);\r
+       if (ercE)\r
+       {\r
+               CloseFile(job_fhE);     /* if it had a handle at all */\r
+               return(ercE);\r
+       }\r
+\r
+       CloseFile(job_fhE);             /* we will open it again after SendAbort */\r
+\r
+       GetJobNum(&JobNumE);\r
+       GetpJCB(JobNumE, &pCrntJCB);            /* Get pJCB to current Job */\r
+       pCrntJCB->ExitError = dExitError;\r
+\r
+       /* Remove ALL tasks for this job that are at the ReadyQue.\r
+          The task we are in won't be removed because its RUNNING!\r
+       */\r
+\r
+       RemoveRdyJob(pCrntJCB); /* It always returns ErcOk */\r
+\r
+       /* Deallocate all exchanges for this job except the one belonging\r
+          to current TSS!  The Dealloc Exchange call will invalidate\r
+          all TSSs found at exchanges belonging to this user, and\r
+          will also free up RQBs and Link Blocks.  The job will not be\r
+          able to initiate requests or send messages after this unless\r
+          it is done with the TSSExchange because it will get a kernel\r
+          error (invalid exchange).\r
+       */\r
+\r
+        /* Find out what our TSS exchange is so\r
+        we don't deallocate it to! */\r
+\r
+        GetTSSExch(&ExchE);\r
+\r
+        ercE = 0;\r
+        iE = 0;\r
+        while (ercE != ErcOutOfRange) \r
+        {\r
+               ercE = GetExchOwner(iE, &pExchJCBE);\r
+               if ((!ercE) && (iE != ExchE) && (pExchJCBE == pCrntJCB))\r
+                       DeAllocExch(iE);\r
+               iE++;\r
+        }\r
+\r
+       /* Now that the user can't make anymore requests,\r
+          Send Abort messages to all services.\r
+          This closes all files that were opened by the Job\r
+       and frees up any other resources held for this\r
+       job by any services.\r
+       */\r
+\r
+       SendAbort(JobNumE, ExchE);\r
+\r
+       ercE = 0;                               /* Clear the error */\r
+       while(!ercE)            /* clear the exchange of abort responses*/\r
+               ercE = CheckMsg(ExchE, BogusMsg);\r
+       ercE = 0;                               /* Clear the error */\r
+\r
+       /* We must now switch to a temporary stack so we can\r
+       clean out the user PD (we are on his stack right now!).\r
+       */\r
+\r
+#asm\r
+       MOV EAX, OFFSET _TmpStack\r
+       ADD EAX, 508\r
+       MOV ESP, EAX\r
+       MOV EBP, EAX\r
+#endasm\r
+\r
+       /* Clean the PD of all user memory leaving OS memory for next\r
+       job if there is one.\r
+       */\r
+\r
+       pPDE = pCrntJCB->pJcbPD;\r
+       CleanUserPD(pPDE);\r
+\r
+       /* Try to load the Chain file. Don't bother checking error\r
+          cause it was valid if we got here!\r
+       */\r
+\r
+       GetRunFile(aFileE, cbFileE, &job_fhE); /* it was valid before!*/\r
+       ercE = LoadJob(pCrntJCB, job_fhE);\r
+\r
+       if (ercE) \r
+       {\r
+               /* We have errored in a critical part of Chain (LoadJob).\r
+               The original user's job is destroyed, and we can't run the\r
+               chain file (bummer).\r
+               The only thing left to do is Look for Exit Run file to load\r
+               if any exists.  If no exit run file, we kill this guy\r
+               and return to the monitor if he had the screen. */\r
+\r
+               GetExitJob(aFileE, &cbFileE);           /* Exit Run File!! */\r
+\r
+               if (!cbFileE)\r
+                       ercE = ErcNoExitJob;\r
+\r
+               if (!ercE)\r
+                       ercE =  GetRunFile(aFileE, cbFileE, &job_fhE);\r
+\r
+               if (!ercE)\r
+                       ercE = LoadJob(pCrntJCB, job_fhE);\r
+       }\r
+\r
+       if (!ercE) \r
+       {               /* No error */\r
+\r
+               pStart = pStart+pCode-offCode;\r
+\r
+               /* Now we RETURN to new job's address after we put him\r
+               on his new stack. */\r
+\r
+#asm\r
+               MOV EAX, _pStack\r
+               MOV EBX, _sStack\r
+               ADD EAX, EBX\r
+               SUB EAX, 4\r
+               MOV ESP, EAX\r
+               MOV EBP, EAX\r
+               PUSH 18h\r
+               MOV EAX, _pStart\r
+               PUSH EAX\r
+               RETF                            ;We are history!\r
+#endasm\r
+       }\r
+\r
+       if (ercE) \r
+       {               /* Something failed loading the job (Chain or Exit) */\r
+\r
+               /* In case there is no job to run or a fatal error has happened\r
+               we send a message (ISendMsg) to the monitor status\r
+               task with our TSSExch and the Error. Then he will WIPE US OUT!\r
+               We use ISend (vice send) so he can't run before we get to\r
+               the exchange otherwise we will be placed back on the readyQueue!\r
+           */\r
+\r
+               ISendMsg(KillExch, ExchE, ercE);  /* ISend clears ints! */\r
+#asm\r
+               STI\r
+#endasm\r
+               SetPriority(31);\r
+               WaitMsg(ExchE, BogusMsg);\r
+\r
+               while(1); /* in case we get scheduled again RAB */\r
+\r
+               /* We are NO MORE */\r
+\r
+       }\r
+}\r
+\r
+/*********************** End of Module *****************/\r