]> pd.if.org Git - mmurtl/blob - ossource/jobc.c
autocommit for file dated 1995-01-30 22:58:12
[mmurtl] / ossource / jobc.c
1 /*\r
2   MMURTL Operating System Source Code\r
3   Copyright 1991,1992,1993,1994 Richard A. Burgess\r
4   ALL RIGHTS RESERVED\r
5   Version 1.0\r
6 */\r
7 \r
8 /* This file contains functions and data used to support\r
9    loading or terminating jobs (or services).\r
10    It contains the public functions:\r
11 \r
12    Chain()                       Loads new job run file in current JCB & PD\r
13    LoadNewJob()          Loads a new job into a new JCB & PD\r
14    ExitJob()             Exits current job, loads ExitJob if specified\r
15 \r
16    GetExitJob()          Gets run file name that will be loaded upon ExitJob\r
17    SetExitJob()          Sets run file name to load upon ExitJob\r
18 \r
19    SetCmdLine()          Sets the command line for next job\r
20    GetCmdLine()          Gets the command line for the current job\r
21 \r
22    GetPath()             Gets the path prefix for the current job\r
23    Setpath()             Sets the path prefix for the current job\r
24 \r
25    SetUserName()         Sets the Username for the current job\r
26    GetUserName()         Gets the Username for the current job\r
27 \r
28 */\r
29 \r
30 \r
31 #define U32 unsigned long\r
32 #define S32 long\r
33 #define U16 unsigned int\r
34 #define S16 int\r
35 #define U8 unsigned char\r
36 #define S8 char\r
37 #define TRUE 1\r
38 #define FALSE 1\r
39 \r
40 #include "MKernel.h"\r
41 #include "MMemory.h"\r
42 #include "MData.h"\r
43 #include "MTimer.h"\r
44 #include "MVid.h"\r
45 #include "MKbd.h"\r
46 #include "MJob.h"\r
47 #include "MFiles.h"\r
48 \r
49 #include "runfile.h"\r
50 \r
51 #define MEMUSERD 7                      /* User Writable (Data) */\r
52 \r
53 #define ErcOpCancel     4       /* Operator cancel */\r
54 #define ErcOutOfRange  10       /* Bad Exchange specified in OS call */\r
55 #define ErcBadJobNum   70       /* A Bad job number was specified */\r
56 #define ErcNoExitJob   76       /* No ExitJob specified on ExitJob(n)*/\r
57 #define ErcBadRunFile  74       /* Couldn't load specified ExitRunFile */\r
58 \r
59 /* Near Support Calls from JobCode.ASM */\r
60 \r
61 extern long AllocJCB(long *pdJobNumRet, char *ppJCBRet);\r
62 extern long RemoveRdyJob(char *pJCB);\r
63 extern long     GetExchOwner(long Exch, char *pJCBRet);\r
64 extern long     SetExchOwner(long Exch, char *pNewJCB);\r
65 extern long     SendAbort(long JobNum, long Exch);\r
66 \r
67 /* Temporary NEAR externals from the monitor program for debugging */\r
68 \r
69 extern long xprintf(char *fmt, ...);\r
70 extern U32 Dump(unsigned char *pb, long cb);\r
71 \r
72 /* We switch to this stack when we clean out a user\r
73    PD before we rebuild it.\r
74 */\r
75 \r
76 static long TmpStack[128];              /* 512 byte temporary stack */\r
77 \r
78 /* Used for allocating/filling in new JCB */\r
79 \r
80 static struct JCBRec *pNewJCB;          /* Used to access a JCB */\r
81 static struct JCBRec *pTmpJCB;\r
82 static struct JCBRec *pCrntJCB;\r
83 \r
84 static long JobNum;\r
85 \r
86 /* For ExitJob and Chain cause They can't have local vars! */\r
87 \r
88 static long JobNumE, job_fhE;\r
89 static long ExchE, ercE, iE;\r
90 static long BogusMsg[2];\r
91 static long *pPDE;\r
92 static char *pExchJCBE\r
93 static long KeyCodeE;\r
94 \r
95 static char aFileE[80];\r
96 static long cbFileE;\r
97 \r
98 extern unsigned long KillExch;  /* From the Monitor */\r
99 \r
100 /* Run file data */\r
101 \r
102 static char *pCode, *pData, *pStack;    /* Ptrs in User mem to load to */\r
103 static long sCode,   sData,  sStack;    /* Size of segments */\r
104 static unsigned long oCode, oData;              /* Offset in file to Code & Data */\r
105 \r
106 static long  offCode = 0,                               /* Virtual Offset for Code & Data Segs */\r
107           offData = 0;\r
108 \r
109 static unsigned long    nCDFIX = 0,\r
110                                 oCDFIX = 0,\r
111                                 nCCFIX = 0,\r
112                                 oCCFIX = 0,\r
113                                 nDDFIX = 0,\r
114                                 oDDFIX = 0,\r
115                                 nDCFIX = 0,\r
116                                 oDCFIX = 0;\r
117 \r
118 static char *pStart, filetype;\r
119 \r
120 static struct tagtype tag;\r
121 \r
122 \r
123 /************* INTERNAL SUPPORT CALLS ******************/\r
124 \r
125 /******************************************************\r
126  This deallocates all user memory in a PD.  This is\r
127  used when we ExitJob to free up all the memory.\r
128  We get the pointer to the PD and "walk" through all\r
129  the User PTs deallocating linear memory. When all\r
130  PTs have been cleaned, we eliminate the user PDEs.\r
131  This leaves a blank PD for reuse if needed.\r
132  NOTE: This must be called from a task that is\r
133  running in the JCB that owns the memory!\r
134 ******************************************************/\r
135 \r
136 static void CleanUserPD(long *pPD)\r
137 {\r
138 long i, j, k, erc;\r
139 unsigned long *pPT;\r
140 char *pMem;\r
141 \r
142         for (i=768; i<1024; i++) {                      /* Look at each shadow PDE */\r
143                 if (pPD[i]) {                                   /* If it's a linear address (non 0)*/\r
144                         pPT = pPD[i] & 0xFFFFF000;      /* Point to Page Table */\r
145                         for (j=0; j<1024; j++) {\r
146 \r
147                         /* Get Beginning address for each run to deallocate */\r
148 \r
149                                 k = 0;  /* nPages to deallocate */\r
150                                 pMem = ((i-512) * 0x400000) + (j * 4096);\r
151                                 while ((pPT[j++]) && (j<1024))\r
152                                         k++;                    /* one more page */\r
153                                 if (k)\r
154                                 {                       /* we have pages (one or more) */\r
155                                         erc =  DeAllocPage(pMem, k);\r
156                                 }\r
157                         }\r
158                 }\r
159         }\r
160 }\r
161 \r
162 /*********************************************************\r
163   This opens, reads and validates the run file.\r
164   If all is well, it leaves it open and returns the\r
165   file handle, else it closes the run file and returns\r
166   the error to the caller.\r
167   The caller is responsible for closing the file!!!\r
168 *********************************************************/\r
169 \r
170 static long GetRunFile(char *pFileName, long cbFileName, long *pfhRet)\r
171 {\r
172 long erc, i, fh, dret;\r
173 char fDone, junk;\r
174 \r
175         offCode = 0;\r
176         offData = 0;\r
177         nCDFIX = 0;\r
178         oCDFIX = 0;\r
179         nCCFIX = 0;\r
180         oCCFIX = 0;\r
181         nDDFIX = 0;\r
182         oDDFIX = 0;\r
183         nDCFIX = 0;\r
184         oDCFIX = 0;\r
185         fh = 0;\r
186 \r
187         *pfhRet = 0;    /* default to 0 */\r
188 \r
189                         /* Mode Read, Stream type */\r
190         erc = OpenFile(pFileName, cbFileName, 0, 1, &fh);\r
191 \r
192         if (!erc) {     /* File opened OK */\r
193 \r
194                 fDone = 0;\r
195                 while ((!erc) && (!fDone)) {\r
196                         tag.id = 0;\r
197                         erc = ReadBytes (fh, &tag, 5, &dret);\r
198 \r
199                         switch (tag.id) {\r
200                                 case IDTAG:\r
201                                         erc = ReadBytes (fh, &filetype, 1, &dret);\r
202                                         if ((filetype < 1) || (filetype > 3))\r
203                                                 erc = ErcBadRunFile + 30000;\r
204                                         break;\r
205                                 case SEGTAG:\r
206                                         erc = ReadBytes (fh, &sStack, 4, &dret);\r
207                                         if (!erc) erc = ReadBytes (fh, &sCode, 4, &dret);\r
208                                         if (!erc) erc = ReadBytes (fh, &sData, 4, &dret);\r
209                                         break;\r
210                                 case DOFFTAG:\r
211                                         erc = ReadBytes (fh, &offData, 4, &dret);\r
212                                         break;\r
213                                 case COFFTAG:\r
214                                         erc = ReadBytes (fh, &offCode, 4, &dret);\r
215                                         break;\r
216                                 case STRTTAG:\r
217                                         erc = ReadBytes (fh, &pStart, 4, &dret);\r
218                                         break;\r
219                                 case CODETAG:\r
220                                         erc = GetFileLFA(fh, &oCode);\r
221                                         if (!erc)\r
222                                                 erc = SetFileLFA(fh, oCode+tag.len);    /* skip it */\r
223                                         break;\r
224                                 case DATATAG:\r
225                                         erc = GetFileLFA(fh, &oData);\r
226                                         if (!erc)\r
227                                                 erc = SetFileLFA(fh, oData+tag.len);    /* skip it */\r
228                                         break;\r
229                                 case CDFIXTAG:\r
230                                         erc = GetFileLFA(fh, &oCDFIX);\r
231                                         nCDFIX = tag.len/4;\r
232                                         if (!erc)\r
233                                                 erc = SetFileLFA(fh, oCDFIX+tag.len);   /* skip it */\r
234                                         break;\r
235                                 case CCFIXTAG:\r
236                                         erc = GetFileLFA(fh, &oCCFIX);\r
237                                         nCCFIX = tag.len/4;\r
238                                         if (!erc)\r
239                                                 erc = SetFileLFA(fh, oCCFIX+tag.len);   /* skip it */\r
240                                         break;\r
241                                 case DDFIXTAG:\r
242                                         erc = GetFileLFA(fh, &oDDFIX);\r
243                                         nDDFIX = tag.len/4;\r
244                                         if (!erc)\r
245                                                 erc = SetFileLFA(fh, oDDFIX+tag.len);   /* skip it */\r
246                                         break;\r
247                                 case DCFIXTAG:\r
248                                         erc = GetFileLFA(fh, &oDCFIX);\r
249                                         nDCFIX = tag.len/4;\r
250                                         if (!erc)\r
251                                                 erc = SetFileLFA(fh, oDCFIX+tag.len);   /* skip it */\r
252                                         break;\r
253                                 case ENDTAG:\r
254                                         fDone = TRUE;\r
255                                         break;\r
256                                 default:\r
257                                         erc = GetFileLFA(fh, &i);\r
258                                         if (tag.len > 1024)\r
259                                                 erc = ErcBadRunFile;\r
260                                         if (!erc)\r
261                                                 erc = SetFileLFA(fh, i+tag.len);        /* skip it */\r
262                                         if (erc)\r
263                                                 erc = ErcBadRunFile;\r
264                                 break;\r
265                         }\r
266 \r
267                 }\r
268                 if (erc)\r
269                         CloseFile(fh);\r
270         }\r
271         if (!erc)\r
272                 *pfhRet = fh;\r
273 \r
274         return (erc);\r
275 }\r
276 \r
277 /********************************************************/\r
278 /*********** PUBLIC CALLS FOR JOB MANAGEMENT ************/\r
279 /********************************************************/\r
280 \r
281 /**************************************************/\r
282 long far _SetExitJob(char *pRunFile, long dcbRunFile)\r
283 {\r
284 long JobNum;\r
285 \r
286         GetJobNum(&JobNum);\r
287         GetpJCB(JobNum, &pTmpJCB);              /* Get pJCB to current Job */\r
288         if (dcbRunFile > 79)\r
289                 return (ErcBadJobParam);\r
290         else if (!dcbRunFile)\r
291                 pTmpJCB->JcbExitRF[0] = 0;\r
292         else {\r
293                 CopyData(pRunFile, &pTmpJCB->JcbExitRF[1], dcbRunFile);\r
294                 pTmpJCB->JcbExitRF[0] = dcbRunFile;\r
295         }\r
296         return(0);\r
297 }\r
298 \r
299 /**************************************************/\r
300 long far _GetExitJob(char *pRunRet, long *pdcbRunRet)\r
301 {\r
302 long JobNum, i;\r
303 \r
304         GetJobNum(&JobNum);\r
305         GetpJCB(JobNum, &pTmpJCB);              /* Get pJCB to current Job */\r
306         i =     pTmpJCB->JcbExitRF[0];\r
307         if (i)\r
308                 CopyData(&pTmpJCB->JcbExitRF[1], pRunRet, i);\r
309         *pdcbRunRet = i;\r
310         return(0);\r
311 }\r
312 \r
313 /**************************************************/\r
314 long far _SetPath(char *pPath, long dcbPath)\r
315 {\r
316 long JobNum;\r
317 \r
318         GetJobNum(&JobNum);\r
319         GetpJCB(JobNum, &pTmpJCB);              /* Get pJCB to current Job */\r
320         if (dcbPath > 69)\r
321                 return (ErcBadJobParam);\r
322         else if (!dcbPath)\r
323                 pTmpJCB->sbPath[0] = 0;\r
324         else {\r
325                 CopyData(pPath, &pTmpJCB->sbPath[1], dcbPath);\r
326                 pTmpJCB->sbPath[0] = dcbPath;\r
327         }\r
328         return(0);\r
329 }\r
330 \r
331 /**************************************************/\r
332 long far _GetPath(long JobNum, char *pPathRet, long *pdcbPathRet)\r
333 {\r
334 long i, erc;\r
335 \r
336         erc = GetpJCB(JobNum, &pTmpJCB);  /* Get pJCB to JobNum */\r
337         if (!erc) {\r
338                 i =     pTmpJCB->sbPath[0];\r
339                 if (i)\r
340                         CopyData(&pTmpJCB->sbPath[1], pPathRet, i);\r
341                 *pdcbPathRet = i;\r
342         }\r
343         return(erc);\r
344 }\r
345 /**************************************************/\r
346 long far _SetCmdLine(char *pCmd, long dcbCmd)\r
347 {\r
348 long JobNum;\r
349 \r
350         GetJobNum(&JobNum);\r
351         GetpJCB(JobNum, &pTmpJCB);              /* Get pJCB to current Job */\r
352         if (dcbCmd > 79)\r
353                 return (ErcBadJobParam);\r
354         else if (!dcbCmd)\r
355                 pTmpJCB->JcbCmdLine[0] = 0;\r
356         else {\r
357                 CopyData(pCmd, &pTmpJCB->JcbCmdLine[1], dcbCmd);\r
358                 pTmpJCB->JcbCmdLine[0] = dcbCmd;\r
359         }\r
360         return(0);\r
361 }\r
362 \r
363 /**************************************************/\r
364 long far _GetCmdLine(char *pCmdRet, long *pdcbCmdRet)\r
365 {\r
366 long JobNum, i;\r
367 \r
368         GetJobNum(&JobNum);\r
369         GetpJCB(JobNum, &pTmpJCB);              /* Get pJCB to current Job */\r
370         i =     pTmpJCB->JcbCmdLine[0];\r
371         if (i)\r
372                 CopyData(&pTmpJCB->JcbCmdLine[1], pCmdRet, i);\r
373         *pdcbCmdRet = i;\r
374         return(0);\r
375 }\r
376 \r
377 \r
378 /**************************************************/\r
379 long far _SetUserName(char *pUser, long dcbUser)\r
380 {\r
381 long JobNum;\r
382 \r
383         GetJobNum(&JobNum);\r
384         GetpJCB(JobNum, &pTmpJCB);              /* Get pJCB to current Job */\r
385         if (dcbUser > 29)\r
386                 return (ErcBadJobParam);\r
387         else if (!dcbUser)\r
388                 pTmpJCB->sbUserName[0] = 0;\r
389         else {\r
390                 CopyData(pUser, &pTmpJCB->sbUserName[1], dcbUser);\r
391                 pTmpJCB->sbUserName[0] = dcbUser;\r
392         }\r
393         return(0);\r
394 }\r
395 \r
396 /**************************************************/\r
397 long far _GetUserName(char *pUserRet, long *pdcbUserRet)\r
398 {\r
399 long JobNum, i;\r
400 \r
401         GetJobNum(&JobNum);\r
402         GetpJCB(JobNum, &pTmpJCB);              /* Get pJCB to current Job */\r
403         i =     pTmpJCB->sbUserName[0];\r
404         if (i)\r
405                 CopyData(&pTmpJCB->sbUserName[1], pUserRet, i);\r
406         *pdcbUserRet = i;\r
407         return(0);\r
408 }\r
409 \r
410 /**************************************************/\r
411 long far _SetSysIn(char *pName, long dcbName)\r
412 {\r
413 long JobNum;\r
414 \r
415         GetJobNum(&JobNum);\r
416         GetpJCB(JobNum, &pTmpJCB);              /* Get pJCB to current Job */\r
417         if ((dcbName > 49) || (!dcbName))\r
418                 return (ErcBadJobParam);\r
419         else {\r
420                 CopyData(pName, &pTmpJCB->JcbSysIn[1], dcbName);\r
421                 pTmpJCB->JcbSysIn[0] = dcbName;\r
422         }\r
423         return(0);\r
424 }\r
425 \r
426 /**************************************************/\r
427 long far _GetSysIn(char *pFileRet, long *pdcbFileRet)\r
428 {\r
429 long JobNum, i;\r
430 \r
431         GetJobNum(&JobNum);\r
432         GetpJCB(JobNum, &pTmpJCB);              /* Get pJCB to current Job */\r
433         i =     pTmpJCB->JcbSysIn[0];\r
434         if (i)\r
435                 CopyData(&pTmpJCB->JcbSysIn[1], pFileRet, i);\r
436         *pdcbFileRet = i;\r
437         return(0);\r
438 }\r
439 \r
440 /**************************************************/\r
441 long far _SetSysOut(char *pName, long dcbName)\r
442 {\r
443 long JobNum;\r
444 \r
445         GetJobNum(&JobNum);\r
446         GetpJCB(JobNum, &pTmpJCB);              /* Get pJCB to current Job */\r
447         if ((dcbName > 49) || (!dcbName))\r
448                 return (ErcBadJobParam);\r
449         else {\r
450                 CopyData(pName, &pTmpJCB->JcbSysOut[1], dcbName);\r
451                 pTmpJCB->JcbSysOut[0] = dcbName;\r
452         }\r
453         return(0);\r
454 }\r
455 \r
456 /**************************************************/\r
457 long far _GetSysOut(char *pFileRet, long *pdcbFileRet)\r
458 {\r
459 long JobNum, i;\r
460 \r
461         GetJobNum(&JobNum);\r
462         GetpJCB(JobNum, &pTmpJCB);              /* Get pJCB to current Job */\r
463         i =     pTmpJCB->JcbSysOut[0];\r
464         if (i)\r
465                 CopyData(&pTmpJCB->JcbSysOut[1], pFileRet, i);\r
466         *pdcbFileRet = i;\r
467         return(0);\r
468 }\r
469 \r
470 /**************************************************/\r
471 long far _SetJobName(char *pName, long dcbName)\r
472 {\r
473 long JobNum;\r
474         GetJobNum(&JobNum);\r
475         GetpJCB(JobNum, &pTmpJCB);              /* Get pJCB to current Job */\r
476         if (dcbName > 13)\r
477                 dcbName = 13;\r
478         if (dcbName)\r
479                 CopyData(pName, &pTmpJCB->sbJobName[1], dcbName);\r
480         pTmpJCB->sbJobName[0] = dcbName;\r
481         return(0);\r
482 }\r
483 \r
484 /*********************************************************\r
485   This creates and loads a new Job from a RUN file.\r
486   This returns ErcOK and new job number if loaded OK\r
487   else an error is returned.\r
488 *********************************************************/\r
489 \r
490 long far _LoadNewJob(char *pFileName, long cbFileName, long *pJobNumRet)\r
491 {\r
492 long erc, i, fh, dret, nPages;\r
493 unsigned long *pPD, *pPT, *pVid, *pOSPD;\r
494 long *pFix;\r
495 U32 PhyAdd;\r
496 \r
497 \r
498         erc =  GetRunFile(pFileName, cbFileName, &fh);\r
499 \r
500         if (!erc) {\r
501 \r
502                 /* We set these to zero so we can tell if they have\r
503                 been allocated in case we fail so we know what to deallocate\r
504                 */\r
505 \r
506                 JobNum = 0;\r
507                 pNewJCB = 0;\r
508                 pPD = 0;\r
509                 pPT = 0;\r
510                 pVid = 0;\r
511 \r
512                 erc = AllocJCB(&JobNum, &pNewJCB);\r
513 \r
514                 /* Alloc OS memory pages required for new job */\r
515 \r
516                 if (!erc)\r
517                         erc = AllocOSPage(3, &pPD);             /* Job's PD, PT * pVirtVid */\r
518 \r
519                 pPT = pPD + 4096;                       /* 1 page later */\r
520                 pVid = pPD + 8192;                      /* 2 pages later */\r
521 \r
522                 if (!erc) {\r
523                         FillData(pPT, 4096, 0);         /* Zero user PT */\r
524                         FillData(pVid, 4096, 0);        /* Zero user video */\r
525 \r
526                         GetpJCB(1, &pTmpJCB);           /* Get OS pJCB */\r
527                         pOSPD = pTmpJCB->pJcbPD;        /* Pointer to OS PD */\r
528 \r
529                         GetPhyAdd(1, pPT, &PhyAdd);     /* Get Phy Add for new PT */\r
530 \r
531                         PhyAdd |= MEMUSERD;                     /* Set user code bits in PDE */\r
532 \r
533                         pOSPD[256] = PhyAdd;            /* Make User PDE in OS PD */\r
534                         pOSPD[768] = pPT;                       /* Shadow Linear Address of PT */\r
535 \r
536                         /* for serious troubleshooting...\r
537                         xprintf("pOSPD  : %08x\r\n", pOSPD);\r
538                         xprintf("pUserPD: %08x\r\n", pPD);\r
539                         xprintf("pUserPT: %08x\r\n", pPT);\r
540                         xprintf("PhyAdd : %08x\r\n", PhyAdd);\r
541                         ReadKbd(&KeyCodeE, 1);\r
542                         */\r
543 \r
544                         /* Now we can allocate User Job Memory */\r
545                         /* Allocate user memory for Stack Code and Data */\r
546                         /* This is STILL done in the OS PD */\r
547 \r
548                         nPages = sStack/4096;\r
549                         if (sStack%4096) nPages++;\r
550                         erc = AllocPage(nPages, &pStack);\r
551                         sStack = nPages * 4096;                         /* set to whole pages */\r
552 \r
553                         nPages = sCode/4096;\r
554                         if (sCode%4096) nPages++;\r
555                         if (!erc)\r
556                                 erc = AllocPage(nPages, &pCode);\r
557 \r
558                         nPages = sData/4096;\r
559                         if (sData%4096) nPages++;\r
560                         if (!erc)\r
561                                 erc = AllocPage(nPages, &pData);\r
562 \r
563                         /* Right now, the OS PD looks exacly like we want\r
564                         the User PD to look.  We will now copy the entire\r
565                         OS PD into the User's New PD.\r
566                         */\r
567 \r
568                         CopyData(pOSPD, pPD, 4096);             /* Copy OS PD to User PD */\r
569 \r
570                         /* All Job memory is now allocated, so let's LOAD IT! */\r
571 \r
572                         if (!erc)\r
573                                 erc = SetFileLFA(fh, oCode);\r
574                         if (!erc)\r
575                                 erc = ReadBytes (fh, pCode, sCode, &dret);\r
576 \r
577                         if (!erc)\r
578                                 erc = SetFileLFA(fh, oData);\r
579                         if (!erc)\r
580                                 erc = ReadBytes (fh, pData, sData, &dret);\r
581 \r
582                         /* Now that we have read in the code and data we\r
583                         apply fixups to these segments from the runfile\r
584                         */\r
585 \r
586                         if (!erc) {\r
587 \r
588                                 if (nCDFIX) {\r
589                                         erc = SetFileLFA(fh, oCDFIX);   /* back to fixups */\r
590                                         while ((nCDFIX--) && (!erc)) {\r
591                                                 erc = ReadBytes (fh, &i, 4, &dret);\r
592                                                 pFix = pCode + i;               /* Where in CSeg */\r
593                                                 *pFix = *pFix - offData + pData;\r
594                                         }\r
595                                 }\r
596 \r
597                                 if (nCCFIX) {\r
598                                         erc = SetFileLFA(fh, oCCFIX);   /* back to fixups */\r
599                                         while ((nCCFIX--) && (!erc)) {\r
600                                                 erc = ReadBytes (fh, &i, 4, &dret);\r
601                                                 pFix = pCode + i;               /* Where in CSeg */\r
602                                                 *pFix = *pFix - offCode + pCode;\r
603                                         }\r
604                                 }\r
605 \r
606                                 if (nDCFIX) {\r
607                                         erc = SetFileLFA(fh, oDCFIX);   /* back to fixups */\r
608                                         while ((nDCFIX--) && (!erc)) {\r
609                                                 erc = ReadBytes (fh, &i, 4, &dret);\r
610                                                 pFix = pData + i;               /* Where in DSeg */\r
611                                                 *pFix = *pFix - offCode + pCode;\r
612                                         }\r
613                                 }\r
614 \r
615                                 if (nDDFIX) {\r
616                                         erc = SetFileLFA(fh, oDDFIX);   /* back to fixups */\r
617                                         while ((nDDFIX--) && (!erc)) {\r
618                                                 erc = ReadBytes (fh, &i, 4, &dret);\r
619                                                 pFix = pData + i;               /* Where in DSeg */\r
620                                                 *pFix = *pFix - offData + pData;\r
621                                         }\r
622                                 }\r
623 \r
624                         }\r
625 \r
626                         /* Clean the OS PD of User memory */\r
627 \r
628                         FillData(&pOSPD[256], 1024, 0); /* Clean OS PD of User PDEs */\r
629                         FillData(&pOSPD[768], 1024, 0); /* Clean OS PD of User Shadow */\r
630 \r
631                         /* Now we fill in the rest of the User's JCB */\r
632 \r
633                         pNewJCB->pJcbPD = pPD;                  /* Lin Add of PD */\r
634                         pNewJCB->pJcbStack = pStack;    /* Add of Stack */\r
635                         pNewJCB->sJcbStack = sStack;    /* Size of Code */\r
636                         pNewJCB->pJcbCode  = pCode;             /* Add of Code */\r
637                         pNewJCB->sJcbCode  = sCode;     /* Size of Code */\r
638                         pNewJCB->pJcbData  = pData;             /* Add of Code */\r
639                         pNewJCB->sJcbData  = sData;     /* Size of Code */\r
640 \r
641                         pNewJCB->sbUserName[0] = 0;     /* Zero UserName */\r
642                         pNewJCB->sbPath[0] = 0;                 /* No Default path */\r
643                         pNewJCB->JcbExitRF[0] = 0;              /* No Exit Run File */\r
644                         pNewJCB->JcbCmdLine[0] = 0;             /* No Cmd Line */\r
645 \r
646                         CopyData("KBD", &pNewJCB->JcbSysIn[1], 3);\r
647                         pNewJCB->JcbSysIn[0] = 3;               /* Size */\r
648 \r
649                         CopyData("VID", &pNewJCB->JcbSysOut[1], 3);\r
650                         pNewJCB->JcbSysOut[0] = 3;              /* Size */\r
651 \r
652                         pNewJCB->pVidMem = pVid;                /* Default to Virt Vid */\r
653                         pNewJCB->pVirtVid = pVid;               /* Virtual Video memory */\r
654                         pNewJCB->CrntX = 0;                             /* Vid X Position */\r
655                         pNewJCB->CrntY = 0;                             /* Vid Y Position */\r
656                         pNewJCB->nCols = 80;                    /* Columns */\r
657                         pNewJCB->nLines = 25;                   /* Lines */\r
658 \r
659                         pNewJCB->VidMode = 0;                   /* 80x25 VGA Color Text */\r
660                         pNewJCB->fCursOn = 1;                   /* Cursor On */\r
661                         pNewJCB->fCursType = 0;                 /* UnderLine */\r
662 \r
663 \r
664                         /* Finally, we crank up the new task and schedule it for\r
665                         execution!\r
666                         */\r
667                         if (!erc)\r
668                                 erc = AllocExch(&i);\r
669 \r
670                         if (!erc)\r
671                                 erc = NewTask(JobNum, 0x18, 25, 0, i,\r
672                                                           pStack+sStack-4,\r
673                                                           pStart+pCode-offCode);\r
674                         if (!erc)\r
675                                 SetExchOwner(i, pNewJCB);       /* Exch now belongs to new JCB */\r
676                 }\r
677 \r
678                 CloseFile(fh);\r
679         }       /* read run file OK */\r
680 \r
681         if (!erc)\r
682                 *pJobNumRet = JobNum;\r
683 \r
684         return(erc);\r
685 }\r
686 \r
687 /*********************************************************\r
688   This loads a job into an existing PD and JCB.  This is\r
689   called by Chain() and may also be called by ExitJob()\r
690   if an ExitJob was specified in the current JCB.\r
691   The PD, pVid & first PT still exist in OS memory.\r
692   (CleanPD left the first PT for us). ExitJob and Chain\r
693   are responsible for opening and validating the runfile\r
694   and setting up the run file variables.\r
695 *********************************************************/\r
696 \r
697 static long LoadJob(char *pJCB, long fh)\r
698 {\r
699 long erc, i, dret, nPages;\r
700 long *pFix;\r
701 \r
702         pNewJCB = pJCB;\r
703 \r
704         /* Allocate user memory for Stack, Code and Data */\r
705         /* This is done in the context of the USER PD */\r
706 \r
707         nPages = sStack/4096;\r
708         if (sStack%4096) nPages++;\r
709         erc = AllocPage(nPages, &pStack);\r
710         sStack = nPages * 4096;                         /* set to whole pages */\r
711 \r
712         nPages = sCode/4096;\r
713         if (sCode%4096) nPages++;\r
714         if (!erc)\r
715                 erc = AllocPage(nPages, &pCode);\r
716 \r
717         nPages = sData/4096;\r
718         if (sData%4096) nPages++;\r
719 \r
720         erc = AllocPage(nPages, &pData);\r
721 \r
722         /* All Job memory is now allocated, so let's LOAD IT! */\r
723 \r
724         if (!erc)\r
725                 erc = SetFileLFA(fh, oCode);\r
726         if (!erc)\r
727                 erc = ReadBytes (fh, pCode, sCode, &dret);\r
728 \r
729         if (!erc)\r
730                 erc = SetFileLFA(fh, oData);\r
731         if (!erc)\r
732                 erc = ReadBytes (fh, pData, sData, &dret);\r
733 \r
734         /* Now that we have read in the code and data we\r
735         apply fixups to these segments from the runfile\r
736         */\r
737 \r
738         if (!erc) {\r
739                 if (nCDFIX) {\r
740                         erc = SetFileLFA(fh, oCDFIX);   /* back to fixups */\r
741                         while ((nCDFIX--) && (!erc)) {\r
742                                 erc = ReadBytes (fh, &i, 4, &dret);\r
743                                 pFix = pCode + i;               /* Where in CSeg */\r
744                                 *pFix = *pFix - offData + pData;\r
745                         }\r
746                 }\r
747 \r
748                 if (nCCFIX) {\r
749                         erc = SetFileLFA(fh, oCCFIX);   /* back to fixups */\r
750                         while ((nCCFIX--) && (!erc)) {\r
751                                 erc = ReadBytes (fh, &i, 4, &dret);\r
752                                 pFix = pCode + i;               /* Where in CSeg */\r
753                                 *pFix = *pFix - offCode + pCode;\r
754                         }\r
755                 }\r
756 \r
757                 if (nDCFIX) {\r
758                         erc = SetFileLFA(fh, oDCFIX);   /* back to fixups */\r
759                         while ((nDCFIX--) && (!erc)) {\r
760                                 erc = ReadBytes (fh, &i, 4, &dret);\r
761                                 pFix = pData + i;               /* Where in DSeg */\r
762                                 *pFix = *pFix - offCode + pCode;\r
763                         }\r
764                 }\r
765 \r
766                 if (nDDFIX) {\r
767                         erc = SetFileLFA(fh, oDDFIX);   /* back to fixups */\r
768                         while ((nDDFIX--) && (!erc)) {\r
769                                 erc = ReadBytes (fh, &i, 4, &dret);\r
770                                 pFix = pData + i;               /* Where in DSeg */\r
771                                 *pFix = *pFix - offData + pData;\r
772                         }\r
773                 }\r
774 \r
775                 /* Now we fill in the rest of the User's JCB */\r
776                 pNewJCB->pJcbStack = pStack;    /* Add of Stack */\r
777                 pNewJCB->sJcbStack = sStack;    /* Size of Code */\r
778                 pNewJCB->pJcbCode  = pCode;             /* Add of Code */\r
779                 pNewJCB->sJcbCode  = sCode;     /* Size of Code */\r
780                 pNewJCB->pJcbData  = pData;             /* Add of Code */\r
781                 pNewJCB->sJcbData  = sData;     /* Size of Code */\r
782 \r
783         }\r
784         CloseFile(fh);\r
785         return(erc);\r
786 }\r
787 \r
788 \r
789 /******************************************************\r
790  This is started as new task in the context of a\r
791  job that is being killed off. This is done to allow\r
792  memory access and also reuse the code for ExitJob\r
793  in the monitor. This task, it's exchange, and TSS\r
794  will be reclaimed by the monitor along with the\r
795  JCB and all OS memory pages for the PD,PT and video.\r
796 ******************************************************/\r
797 \r
798 void _KillTask(void)\r
799 {\r
800 \r
801         GetJobNum(&JobNumE);\r
802         GetpJCB(JobNumE, &pTmpJCB);             /* Get pJCB to this Job */\r
803 \r
804         /* Clean the PD of all user memory leaving OS memory to be\r
805            deallocated by the caller (monitor or whoever).\r
806         */\r
807 \r
808         pPDE = pTmpJCB->pJcbPD;\r
809         CleanUserPD(pPDE);\r
810 \r
811         GetTSSExch(&ExchE);\r
812 \r
813     ercE = 0;\r
814         while(!ercE)            /* clear the exchange */\r
815                 ercE = CheckMsg(ExchE, BogusMsg);\r
816 \r
817         ISendMsg(KillExch, ExchE, ErcOpCancel);\r
818         SetPriority(31);\r
819         WaitMsg(ExchE, BogusMsg);\r
820 \r
821         while(1); /* in case we get scheduled again RAB */\r
822 \r
823         /* He's History! */\r
824 }\r
825 \r
826 \r
827 /******************************************************\r
828  This called from one job to kill another job or\r
829  service. This cleans up ALL resources that the\r
830  job had allocated.\r
831  This is used to kill run-away jobs, or terminate a\r
832  job just for the fun ot it.\r
833  It results in a violent death for the job specified.\r
834  This must never be called from a task within the job\r
835  to be killed.  A job may terminate itself with ExitJob().\r
836 ******************************************************/\r
837 \r
838 long far _KillJob(long JobNum)\r
839 {\r
840 long erc;\r
841         /* Make sure it's not the Monitor, Debugger or the current job. */\r
842 \r
843         GetJobNum(&JobNumE);\r
844         if ((JobNum == JobNumE) ||\r
845             (JobNum == 1) ||\r
846             (JobNum == 2))\r
847 \r
848                 return(ErcBadJobNum);\r
849 \r
850         erc = GetpJCB(JobNum, &pTmpJCB);                        /* Get pJCB to the Job */\r
851         if (erc)\r
852                 return(erc);\r
853 \r
854         pTmpJCB->ExitError = ErcOpCancel; /* Operator said DIE! */\r
855 \r
856         /* Remove ALL tasks for this job that are at the ReadyQue.\r
857            The task we are in does not belong to the job we are\r
858            killing so we can remove them all.\r
859         */\r
860 \r
861         RemoveRdyJob(pTmpJCB);  /* It always returns ErcOk */\r
862 \r
863         /* Deallocate ALL exchanges for this job */\r
864 \r
865          ercE = 0;\r
866          iE = 0;\r
867          while (ercE != ErcOutOfRange)\r
868          {\r
869                 ercE = GetExchOwner(iE, &pExchJCBE);\r
870                 if ((!ercE) && (pExchJCBE == pTmpJCB))\r
871                         DeAllocExch(iE);\r
872                 iE++;\r
873          }\r
874          ercE = 0;              /* Clear the error */\r
875 \r
876         /* Now that the user can't make anymore requests,\r
877            We send "Abort" messages to all services.\r
878            This closes all files that were opened by the Job\r
879        and frees up any other resources held for this\r
880        job by any service.\r
881 \r
882            We will allocate one exchange for the job\r
883            that is being killed so we can use it for SendAbort\r
884            and also as the exchange number we send to the\r
885            KillExch in the monitor which will kill of the JCB\r
886            completely (he also switches video and keyboard\r
887            if needed).\r
888          */\r
889 \r
890         erc = AllocExch(&ExchE);                /* Get an Exch */\r
891         SetExchOwner(ExchE, pTmpJCB);   /* make him the owner */\r
892         SendAbort(JobNum, ExchE);       /* Notify all services */\r
893 \r
894                 /*JobNum, CodeSeg, Priority, fDebug, Exch, ESP, EIP */\r
895         erc = NewTask(JobNum, 0x08, 3, 0, ExchE, &TmpStack[127], &_KillTask);\r
896         return(erc);\r
897 \r
898         /* He's History! */\r
899 }\r
900 \r
901 /******************************************************\r
902  This called from Exit() in C or directly from a user\r
903  job or service. This cleans up ALL resources that the\r
904  job had allocated.\r
905  This also checks for an exit run file to load if\r
906  one is specified. If no exit run file is specified\r
907  we just kill the JCB entirely and if video and\r
908  keyboard are assigned we assign them to the Monitor.\r
909 ******************************************************/\r
910 \r
911 void far _ExitJob(long dError)\r
912 {\r
913 /* NO LOCAL VARIABLES BECAUSE WE SWITCH STACKS!! */\r
914 \r
915         GetJobNum(&JobNumE);\r
916         GetpJCB(JobNumE, &pCrntJCB);            /* Get pJCB to current Job */\r
917         pCrntJCB->ExitError = dError;\r
918 \r
919         /* Remove ALL tasks for this job that are at the ReadyQue.\r
920            The task we are in won't be removed because its RUNNING!\r
921         */\r
922 \r
923         RemoveRdyJob(pCrntJCB); /* It always returns ErcOk */\r
924 \r
925         /* Deallocate all exchanges for this job except the one belonging\r
926            to current TSS!  The Dealloc Exchange call will invalidate\r
927            all TSSs found at exchanges belonging to this user, and\r
928            will also free up RQBs and Link Blocks.  The job will not be\r
929            able to initiate requests or send messages after this unless\r
930            it is done with the TSSExchange because it will get a kernel\r
931            error (invalid exchange).\r
932         */\r
933 \r
934          /* Find out what our TSS exchange is so\r
935          we don't deallocate it to! We need it. */\r
936 \r
937          GetTSSExch(&ExchE);\r
938 \r
939          ercE = 0;\r
940          iE = 0;\r
941          while (ercE != ErcOutOfRange)\r
942          {\r
943                 ercE = GetExchOwner(iE, &pExchJCBE);\r
944                 if ((!ercE) && (iE != ExchE) && (pExchJCBE == pCrntJCB))\r
945                         DeAllocExch(iE);\r
946                 iE++;\r
947          }\r
948 \r
949         /* Now that the user can't make anymore requests,\r
950            Send Abort messages to all services.\r
951            This closes all files that were opened by the Job\r
952        and frees up any other resources held for this\r
953        job by any service.\r
954         */\r
955 \r
956         SendAbort(JobNumE, ExchE);\r
957 \r
958         ercE = 0;                               /* Clear the error */\r
959         while(!ercE)            /* clear the exchange */\r
960                 ercE = CheckMsg(ExchE, BogusMsg);\r
961         ercE = 0;                               /* Clear the error */\r
962 \r
963         /* We must now switch to a temporary stack so we can\r
964         clean out the user PD (we are on his stack right now!).\r
965         */\r
966 \r
967 #asm\r
968         MOV EAX, OFFSET _TmpStack\r
969         ADD EAX, 508\r
970         MOV ESP, EAX\r
971         MOV EBP, EAX\r
972 #endasm\r
973 \r
974         /* Clean the PD of all user memory leaving OS memory for next\r
975         job if there is one.\r
976         */\r
977 \r
978         pPDE = pCrntJCB->pJcbPD;\r
979         CleanUserPD(pPDE);\r
980 \r
981         /* Look for Exit Run file to load if any exists.  If no exit run\r
982         file, we deallocate the PD and JCB then return to JOB 1. */\r
983 \r
984         GetExitJob(aFileE, &cbFileE);           /* Exit Run File!! */\r
985 \r
986         if (!cbFileE)\r
987                 ercE = ErcNoExitJob;\r
988 \r
989         if (!ercE)\r
990                 ercE =  GetRunFile(aFileE, cbFileE, &job_fhE);\r
991 \r
992         if (!ercE)\r
993                 ercE = LoadJob(pCrntJCB, job_fhE);\r
994 \r
995         if (!ercE) {\r
996 \r
997                         pStart = pStart+pCode-offCode;\r
998 \r
999                         /* Now we RETURN to new job's address after we put him\r
1000                         on his new stack. */\r
1001 \r
1002 #asm\r
1003                         MOV EAX, _pStack\r
1004                         MOV EBX, _sStack\r
1005                         ADD EAX, EBX\r
1006                         SUB EAX, 4\r
1007                         MOV ESP, EAX\r
1008                         MOV EBP, EAX\r
1009                         PUSH 18h\r
1010                         MOV EAX, _pStart\r
1011                         PUSH EAX\r
1012                         RETF                            ;We are history!\r
1013 #endasm\r
1014 \r
1015         }\r
1016 \r
1017         if (ercE) {             /* something failed or we don't have an ExitRF */\r
1018 \r
1019                 /* In case there is no job to run or a fatal error has happened\r
1020                 we send a message (ISendMsg) to the monitor status\r
1021                 task with our TSSExch and the Error. Then he will WIPE US OUT!\r
1022                 We use ISend (vice send) so he can't run before we get to\r
1023                 the exchange otherwise we will be placed back on the readyQueue!\r
1024             */\r
1025 \r
1026                 ISendMsg(KillExch, ExchE, ercE);\r
1027                 SetPriority(31);\r
1028                 WaitMsg(ExchE, BogusMsg);\r
1029 \r
1030                 while(1); /* in case we get scheduled again RAB */\r
1031 \r
1032                 /* We are NO MORE */\r
1033         }\r
1034 }\r
1035 \r
1036 /******************************************************\r
1037  This is called to execute a program without changing\r
1038  the ExitJob. This is so you can run program B from\r
1039  program A and return to program A when program B is\r
1040  done.  This runs Job B in the "context" of Job A\r
1041  which means Job B inherits the JCB and PD of job A\r
1042  so it can use things like the command line and\r
1043  path that were set up by A.\r
1044  Information can be passed to Job B by calling\r
1045  SetCmdLine (if Job B reads it), and also by\r
1046  setting the ExitError value in the parameter.\r
1047  Chain will only return to you if there was an\r
1048  error loading the Job.  In other words, if Chain\r
1049  fails in a critial section we try to load the\r
1050  ExitJob and pass it the error.\r
1051 ******************************************************/\r
1052 \r
1053 long far _Chain(char *pFileName, long cbFileName, long dExitError)\r
1054 {\r
1055 /* NO LOCAL VARIABLES BECAUSE WE SWITCH STACKS!! */\r
1056 \r
1057         CopyData(pFileName, aFileE, cbFileName);\r
1058         cbFileE = cbFileName;\r
1059 \r
1060         ercE =  GetRunFile(pFileName, cbFileName, &job_fhE);\r
1061         if (ercE)\r
1062         {\r
1063                 CloseFile(job_fhE);     /* if it had a handle at all */\r
1064                 return(ercE);\r
1065         }\r
1066 \r
1067         CloseFile(job_fhE);             /* we will open it again after SendAbort */\r
1068 \r
1069         GetJobNum(&JobNumE);\r
1070         GetpJCB(JobNumE, &pCrntJCB);            /* Get pJCB to current Job */\r
1071         pCrntJCB->ExitError = dExitError;\r
1072 \r
1073         /* Remove ALL tasks for this job that are at the ReadyQue.\r
1074            The task we are in won't be removed because its RUNNING!\r
1075         */\r
1076 \r
1077         RemoveRdyJob(pCrntJCB); /* It always returns ErcOk */\r
1078 \r
1079         /* Deallocate all exchanges for this job except the one belonging\r
1080            to current TSS!  The Dealloc Exchange call will invalidate\r
1081            all TSSs found at exchanges belonging to this user, and\r
1082            will also free up RQBs and Link Blocks.  The job will not be\r
1083            able to initiate requests or send messages after this unless\r
1084            it is done with the TSSExchange because it will get a kernel\r
1085            error (invalid exchange).\r
1086         */\r
1087 \r
1088          /* Find out what our TSS exchange is so\r
1089          we don't deallocate it to! */\r
1090 \r
1091          GetTSSExch(&ExchE);\r
1092 \r
1093          ercE = 0;\r
1094          iE = 0;\r
1095          while (ercE != ErcOutOfRange) \r
1096          {\r
1097                 ercE = GetExchOwner(iE, &pExchJCBE);\r
1098                 if ((!ercE) && (iE != ExchE) && (pExchJCBE == pCrntJCB))\r
1099                         DeAllocExch(iE);\r
1100                 iE++;\r
1101          }\r
1102 \r
1103         /* Now that the user can't make anymore requests,\r
1104            Send Abort messages to all services.\r
1105            This closes all files that were opened by the Job\r
1106        and frees up any other resources held for this\r
1107        job by any services.\r
1108         */\r
1109 \r
1110         SendAbort(JobNumE, ExchE);\r
1111 \r
1112         ercE = 0;                               /* Clear the error */\r
1113         while(!ercE)            /* clear the exchange of abort responses*/\r
1114                 ercE = CheckMsg(ExchE, BogusMsg);\r
1115         ercE = 0;                               /* Clear the error */\r
1116 \r
1117         /* We must now switch to a temporary stack so we can\r
1118         clean out the user PD (we are on his stack right now!).\r
1119         */\r
1120 \r
1121 #asm\r
1122         MOV EAX, OFFSET _TmpStack\r
1123         ADD EAX, 508\r
1124         MOV ESP, EAX\r
1125         MOV EBP, EAX\r
1126 #endasm\r
1127 \r
1128         /* Clean the PD of all user memory leaving OS memory for next\r
1129         job if there is one.\r
1130         */\r
1131 \r
1132         pPDE = pCrntJCB->pJcbPD;\r
1133         CleanUserPD(pPDE);\r
1134 \r
1135         /* Try to load the Chain file. Don't bother checking error\r
1136            cause it was valid if we got here!\r
1137         */\r
1138 \r
1139         GetRunFile(aFileE, cbFileE, &job_fhE); /* it was valid before!*/\r
1140         ercE = LoadJob(pCrntJCB, job_fhE);\r
1141 \r
1142         if (ercE) \r
1143         {\r
1144                 /* We have errored in a critical part of Chain (LoadJob).\r
1145                 The original user's job is destroyed, and we can't run the\r
1146                 chain file (bummer).\r
1147                 The only thing left to do is Look for Exit Run file to load\r
1148                 if any exists.  If no exit run file, we kill this guy\r
1149                 and return to the monitor if he had the screen. */\r
1150 \r
1151                 GetExitJob(aFileE, &cbFileE);           /* Exit Run File!! */\r
1152 \r
1153                 if (!cbFileE)\r
1154                         ercE = ErcNoExitJob;\r
1155 \r
1156                 if (!ercE)\r
1157                         ercE =  GetRunFile(aFileE, cbFileE, &job_fhE);\r
1158 \r
1159                 if (!ercE)\r
1160                         ercE = LoadJob(pCrntJCB, job_fhE);\r
1161         }\r
1162 \r
1163         if (!ercE) \r
1164         {               /* No error */\r
1165 \r
1166                 pStart = pStart+pCode-offCode;\r
1167 \r
1168                 /* Now we RETURN to new job's address after we put him\r
1169                 on his new stack. */\r
1170 \r
1171 #asm\r
1172                 MOV EAX, _pStack\r
1173                 MOV EBX, _sStack\r
1174                 ADD EAX, EBX\r
1175                 SUB EAX, 4\r
1176                 MOV ESP, EAX\r
1177                 MOV EBP, EAX\r
1178                 PUSH 18h\r
1179                 MOV EAX, _pStart\r
1180                 PUSH EAX\r
1181                 RETF                            ;We are history!\r
1182 #endasm\r
1183         }\r
1184 \r
1185         if (ercE) \r
1186         {               /* Something failed loading the job (Chain or Exit) */\r
1187 \r
1188                 /* In case there is no job to run or a fatal error has happened\r
1189                 we send a message (ISendMsg) to the monitor status\r
1190                 task with our TSSExch and the Error. Then he will WIPE US OUT!\r
1191                 We use ISend (vice send) so he can't run before we get to\r
1192                 the exchange otherwise we will be placed back on the readyQueue!\r
1193             */\r
1194 \r
1195                 ISendMsg(KillExch, ExchE, ercE);  /* ISend clears ints! */\r
1196 #asm\r
1197                 STI\r
1198 #endasm\r
1199                 SetPriority(31);\r
1200                 WaitMsg(ExchE, BogusMsg);\r
1201 \r
1202                 while(1); /* in case we get scheduled again RAB */\r
1203 \r
1204                 /* We are NO MORE */\r
1205 \r
1206         }\r
1207 }\r
1208 \r
1209 /*********************** End of Module *****************/\r