]> pd.if.org Git - mmurtl/commitdiff
autocommit for file dated 1995-02-09 11:12:32
authorRichard Burgess <>
Thu, 9 Feb 1995 11:12:32 +0000 (11:12 +0000)
committerNathan Wagner <nw@hydaspes.if.org>
Mon, 17 Oct 2016 14:03:48 +0000 (14:03 +0000)
ossource/monitor.c [new file with mode: 0644]

diff --git a/ossource/monitor.c b/ossource/monitor.c
new file mode 100644 (file)
index 0000000..3c1a5ce
--- /dev/null
@@ -0,0 +1,965 @@
+/*\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
+\r
+/* This is the MMURTL Monitor program.\r
+   It provides the user with statistics and control of the\r
+   environment, and a few basic functions such as starting a CLI.\r
+\r
+   The monitor has three main functions:\r
+   1) Sets up internal device drivers and services.\r
+   2) Checks config files and loads any system services specified\r
+      in the config file,\r
+   3) Loads a CLI and assigns the video and keyboard to it.\r
+\r
+   Because the Monitor is part of the OS we do not declare a main\r
+   function.  The entry point to the monitor is called Monitor and\r
+   it has no parameters.  After the OS gets done with its low level\r
+   initialization, the monitor procedure is "Jumped" to.\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
+\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
+#include "MDevDrv.h"\r
+\r
+\r
+#define ok 0\r
+#define ErcNoDevice 504\r
+\r
+static char rgStatLine[] =\r
+"mm/dd/yy  00:00:00              MMURTL Monitor                   Tick:0         ";\r
+\r
+static char rgMonMenu1[] = "LdCLI\xb3Jobs  \xb3Stats \xb3      ";\r
+static char rgMonMenu2[] = "     \xb3      \xb3      \xb3Reboot";\r
+static char rgMonMenu3[] = "     \xb3Debug \xb3      \xb3     ";\r
+\r
+static char rgCPR1[] ="MMURTL (tm) - Message based, MUltitasking, Real-Time kerneL";\r
+static char rgCPR2[] ="Copyright (c) R.A. Burgess, 1990-1993, All Rights Reserved";\r
+\r
+static char *CRLF = "\r\n\r\n";\r
+\r
+static unsigned long Color = WHITE|BGBLACK;            /* Color test for xprintf */\r
+\r
+static long time, date, tick;\r
+\r
+unsigned long KillExch;                /* Messaging for stat task KILL proc */\r
+\r
+static unsigned long KillMsg[2];       /* First DWORD = TSSExch, second is ERROR */\r
+static unsigned long KillError;\r
+static unsigned long KillJobNum;\r
+static unsigned char fKilled;\r
+\r
+static unsigned long MngrExch;         /* Messaging for stat task */\r
+static unsigned long MngrMsg[2];\r
+static unsigned long MngrHndl;\r
+static unsigned long gcode;\r
+\r
+static unsigned long GPExch;           /* Messaging for main */\r
+static unsigned long GPMsg[2];\r
+static unsigned long GPHndl;\r
+\r
+static unsigned long GP1Exch;          /* Extra Messaging for main */\r
+static unsigned long GP1Msg[2];\r
+static unsigned long GP1Hndl;\r
+\r
+/* Structure for disk device driver status and setup */\r
+\r
+static struct diskstattype {\r
+  U32 erc;\r
+  U32 blocks_done;\r
+  U32 BlocksMax;\r
+  U8 fNewMedia;\r
+  U8 type_now;         /* current disk type for drive selected */\r
+  U8 resvd1[2];                /* padding for DWord align  */\r
+  U32 nCyl;                    /* total physical cylinders */\r
+  U32 nHead;           /* total heads on device    */\r
+  U32 nSectors;                /* Sectors per track        */\r
+  U32 nBPS;                    /* Number of bytes per sect */\r
+\r
+  U32 LastRecalErc0;\r
+  U32 LastSeekErc0;\r
+  U8  LastStatByte0;\r
+  U8  LastErcByte0;\r
+  U8  fIntOnReset;     /* Interrupt was received on HDC_RESET */\r
+  U8  filler0;\r
+  U32 LastRecalErc1;\r
+  U32 LastSeekErc1;\r
+  U8  LastStatByte1;\r
+  U8  LastErcByte1;\r
+  U8  ResetStatByte;   /* Status Byte immediately after RESET */\r
+  U8  filler1;\r
+  U32 resvd1[2];       /* out to 64 bytes */\r
+  };\r
+\r
+static struct diskstattype DiskStatus;\r
+\r
+#define nMaxJCBs 34            /* 32 dynamic plus 2 static */\r
+static struct JCBRec *pJCB;\r
+\r
+static long StatStack[256];    /* 1024 byte stack for Stat task */\r
+\r
+static long MngrStack[256];    /* 1024 byte stack for Mngr task */\r
+\r
+static unsigned char Buffer[512];\r
+static unsigned long nMemPages;\r
+\r
+extern unsigned long oMemMax;\r
+extern unsigned long nSwitches;\r
+extern unsigned long nSlices;\r
+extern unsigned long nHalts;\r
+extern unsigned long nReady;\r
+extern unsigned long nRQBLeft;\r
+extern unsigned long nJCBLeft;\r
+extern unsigned long nTSSLeft;\r
+extern unsigned long nLBLeft;\r
+extern unsigned long nEXCHLeft;\r
+extern unsigned long BootDrive;\r
+\r
+/*============ protos (NEAR MMURTL support calls) =================*/\r
+\r
+extern long InitKBDService(void);      /* From Keyboard.asm */\r
+extern long fdisk_setup(void);         /* From Floppy.c */\r
+extern long hdisk_setup(void);         /* From HardIDE.c */\r
+extern long coms_setup(void);          /* From RS232.c */\r
+extern long lpt_setup(void);           /* From Parallel.c */\r
+extern long InitFS(void);                      /* From Fsys.c */\r
+\r
+extern long GetExchOwner(long Exch, char *pJCBRet);\r
+extern long DeAllocJCB(long *pdJobNumRet, char *ppJCBRet);\r
+\r
+/*=================== START OF CODE ====================*/\r
+\r
+/**************************************************************\r
+ Formatted output routines for montitor program\r
+ xprintf, xsprintf.\r
+***************************************************************/\r
+\r
+#include <stdarg.h>\r
+\r
+#define        S_SIZE  100\r
+\r
+/*********************************************\r
+* Determine if a character is a numeric digit\r
+**********************************************/\r
+\r
+static long isdigit(long chr)\r
+{\r
+;\r
+#asm\r
+       MOV EAX,[EBP+8]\r
+       CMP AL, 30h             ;0\r
+       JL isdigit0             ;No\r
+       CMP AL, 39h             ;\r
+       JLE isdigit1    ;Yes\r
+isdigit0:\r
+       XOR EAX,EAX             ;No\r
+       JMP SHORT isdigit2\r
+isdigit1:\r
+       MOV EAX, -1\r
+isdigit2:\r
+\r
+#endasm\r
+}\r
+\r
+static long strlen(char *cs)\r
+{\r
+;\r
+#asm\r
+       XOR EAX, EAX\r
+       MOV ESI,[EBP+8]\r
+_strlen0:\r
+       CMP BYTE PTR [ESI],0\r
+       JE _strlen1\r
+       INC ESI\r
+       INC EAX\r
+       JMP SHORT _strlen0\r
+_strlen1:\r
+#endasm\r
+}\r
+\r
+/*************************************************************\r
+ This does the actual parsing of the format and also moves to\r
+ the next arg(s) in the list from the passed in arg pointer.\r
+ The number of chars written is returned (not incl \0).\r
+**************************************************************/\r
+static long _ffmt(char *outptr, char *fmt, long *argptr)\r
+{\r
+char numstk[33], *ptr, justify, zero, minus, chr;\r
+unsigned long width, value, i, total;\r
+\r
+       total = 0;\r
+       while(chr = *fmt++) \r
+       {\r
+               if(chr == '%') \r
+               {                                       /* format code */\r
+                       chr = *fmt++;\r
+            ptr = &numstk[32];\r
+                       *ptr = justify = minus = 0;\r
+                       width = value = i = 0;\r
+                       zero = ' ';\r
+                       if(chr == '-')\r
+                       {                               /* left justify */\r
+                               --justify;\r
+                               chr = *fmt++;\r
+                       }\r
+                       if(chr == '0')                                  /* leading zeros */\r
+                               zero = '0';\r
+                       while(isdigit(chr))\r
+                       {                       /* field width specifier */\r
+                               width = (width * 10) + (chr - '0');\r
+                               chr = *fmt++;\r
+                       }\r
+\r
+                       value = *--argptr;                              /* get parameter value */\r
+\r
+                       switch(chr)\r
+                       {\r
+                               case 'd' :                                      /* decimal number */\r
+                                       if(value & 0x80000000)\r
+                                       {\r
+                                               value = -value;\r
+                                               ++minus;\r
+                                       }\r
+                               case 'u' :                                      /* unsigned number */\r
+                                       i = 10;\r
+                                       break;\r
+                               case 'x' :                                      /* hexidecimal number */\r
+                               case 'X' :\r
+                                       i = 16;\r
+                                       break;\r
+                               case 'o' :                                      /* octal number */\r
+                                       i = 8;\r
+                                       break;\r
+                               case 'b' :                                      /* binary number */\r
+                                       i = 2;\r
+                                       break;\r
+                               case 'c' :                                      /* character data */\r
+                                       *--ptr = value;\r
+                                       break;\r
+                               case 's' :                                      /* string */\r
+                                       ptr = value;                    /* value is ptr to string */\r
+                                       break;\r
+                               default:                                        /* all others */\r
+                                       *--ptr = chr;\r
+                                       ++argptr;                               /* backup to last arg */\r
+                       }\r
+\r
+                       if(i)           /* for all numbers, generate the ASCII string */\r
+                               do \r
+                               {\r
+                                       if((chr = (value % i) + '0') > '9')\r
+                                               chr += 7;\r
+                                       *--ptr = chr; \r
+                               }\r
+                               while(value /= i);\r
+\r
+                       /* output sign if any */\r
+\r
+                       if(minus) \r
+                       {\r
+                               *outptr++ = '-';\r
+                               ++total;\r
+                               if(width)\r
+                                       --width;\r
+                       }\r
+\r
+                       /* pad with 'zero' value if right justify enabled  */\r
+\r
+                       if(width && !justify) \r
+                       {\r
+                               for(i = strlen(ptr); i < width; ++i)\r
+                                       *outptr++ = zero;\r
+                                       ++total;\r
+                       }\r
+\r
+                       /* move in data */\r
+\r
+                       i = 0;\r
+                       value = width - 1;\r
+\r
+                       while((*ptr) && (i <= value)) \r
+                       {\r
+                               *outptr++ = *ptr++;\r
+                               ++total;\r
+                               ++i;\r
+                       }\r
+\r
+                       /* pad with 'zero' value if left justify enabled */\r
+\r
+                       if(width && justify) \r
+                       {\r
+                               while(i < width) \r
+                               {\r
+                                       *outptr++ = zero;\r
+                                       ++total;\r
+                                       ++i;\r
+                               }\r
+                       }\r
+               }\r
+               else \r
+               {\r
+                       /* not format char, just move into string  */\r
+                       *outptr++ = chr;\r
+                       ++total;\r
+               }\r
+       }\r
+\r
+       *outptr = 0;\r
+       return total;\r
+}\r
+\r
+/************************************\r
+    Formatted print to screen\r
+*************************************/\r
+\r
+long xprintf(char *fmt, ...)\r
+{\r
+       va_list ap;\r
+       long total;\r
+       char buffer[S_SIZE];\r
+\r
+       va_start(ap, fmt);              /* set up ap pointer */\r
+       total = _ffmt(buffer, fmt, ap);\r
+       TTYOut(buffer, strlen(buffer), Color);\r
+       va_end(ap, fmt);\r
+       return total;\r
+}\r
+\r
+/************************************\r
+    Formatted print to string s\r
+*************************************/\r
+\r
+long xsprintf(char *s, char *fmt, ...)\r
+{\r
+       va_list ap;\r
+       long total;\r
+\r
+       va_start(ap, fmt);                      /* set up ap pointer */\r
+       total = _ffmt(s, fmt, ap);\r
+       va_end(ap, fmt);\r
+       return total;\r
+}\r
+\r
+/**********************************************\r
+ Checks to ensure we don't scroll the function\r
+ keys off the screen.\r
+**********************************************/\r
+\r
+void CheckScreen()\r
+{\r
+long iCol, iLine;\r
+\r
+       GetXY(&iCol, &iLine);\r
+       if (iLine >= 23)\r
+       {\r
+               ScrollVid(0,1,80,23,1);\r
+               SetXY(0,22);\r
+       }\r
+}\r
+\r
+/*********************************************************\r
+    This is called to initialize the sacreen.\r
+*********************************************************/\r
+\r
+static void InitScreen(void)\r
+{\r
+       ClrScr();\r
+    xsprintf(&rgStatLine[70], "%d", tick);\r
+    PutVidChars(0,0, rgStatLine, 80, WHITE|BGBLUE);\r
+       PutVidChars(0,  24, rgMonMenu1, 26, BLUE|BGWHITE);\r
+       PutVidChars(27, 24, rgMonMenu2, 26, BLUE|BGWHITE);\r
+       PutVidChars(54, 24, rgMonMenu3, 25, BLUE|BGWHITE);\r
+       SetXY(0,1);\r
+       return;\r
+}\r
+\r
+/*********************************************************\r
+    This is the status task for the Monitor.\r
+    Besides displaying the top status line for\r
+    the montitor, it has the job of looking for\r
+    messages from the job code that indicate\r
+    a job has terminated. When it gets one,\r
+    it recovers the last of the resources and\r
+    then notifies the user on the screen.\r
+*********************************************************/\r
+\r
+static void StatTask(void)\r
+{\r
+unsigned long erc, i, Exch, Msg[2];\r
+U8 *pPD, *pVid;\r
+\r
+for(;;)\r
+{\r
+       GetCMOSTime(&time);\r
+       rgStatLine[10] = '0' + ((time >> 20) & 0x0f);\r
+       rgStatLine[11] = '0' + ((time >> 16) & 0x0f);\r
+       rgStatLine[13] = '0' + ((time >> 12) & 0x0f);\r
+       rgStatLine[14] = '0' + ((time >> 8) & 0x0f);\r
+       rgStatLine[16] = '0' + ((time >> 4) & 0x0f);    /* seconds */\r
+       rgStatLine[17] = '0' + (time & 0x0f);\r
+\r
+       GetCMOSDate(&date);\r
+       rgStatLine[0] = '0' + ((date >> 20) & 0x0f); /* month */\r
+       rgStatLine[1] = '0' + ((date >> 16) & 0x0f);\r
+       rgStatLine[3] = '0' + ((date >> 12) & 0x0f); /* Day */\r
+       rgStatLine[4] = '0' + ((date >> 8)  & 0x0f);\r
+       rgStatLine[6] = '0' + ((date >> 28) & 0x0f); /* year */\r
+       rgStatLine[7] = '0' + ((date >> 24) & 0x0f);\r
+\r
+       GetTimerTick(&tick);\r
+       xsprintf(&rgStatLine[70], "%d", tick);\r
+       PutVidChars(0,0, rgStatLine, 80, WHITE|BGBLUE);\r
+\r
+       Sleep(50);      /* sleep 0.5 second */\r
+\r
+       GetTimerTick(&tick);\r
+       xsprintf(&rgStatLine[70], "%d", tick);\r
+       PutVidChars(0,0, rgStatLine, 80, WHITE|BGBLUE);\r
+\r
+       Sleep(50);      /* sleep 0.5 second */\r
+\r
+       /* Now we check for tasks that are Jobs that are killing\r
+       themselves (either due to fatal errors or no exitjob).\r
+       The message has Error, TSSExch in it.\r
+       */\r
+\r
+       erc =  CheckMsg(KillExch, KillMsg);\r
+       if (!erc)\r
+       {        /* someones there wanting to terminate...*/\r
+\r
+               /* Get and save the error (KillMsg[0]) */\r
+               KillError = KillMsg[0];\r
+\r
+               /* Call GetExchOwner which gives us pJCB */\r
+\r
+               erc = GetExchOwner(KillMsg[1], &pJCB);\r
+               if (!erc)\r
+               {\r
+\r
+                       KillJobNum = pJCB->JobNum;\r
+                       Tone(440,50);\r
+                       xprintf("Job number %d terminated. Error: %d\r\n",\r
+                                       KillJobNum, KillError);\r
+                       CheckScreen();\r
+\r
+                       pPD  = pJCB->pJcbPD;\r
+                       pVid = pJCB->pVirtVid;\r
+\r
+                       /* Must change video to monitor if this guy owned it */\r
+\r
+                       GetVidOwner(&i);\r
+                       if (i == KillJobNum)\r
+                       {\r
+                               GetTSSExch(&Exch);      /* Use our TSS exchange for Request */\r
+                               SetVidOwner(1);\r
+                               erc = Request("KEYBOARD", 4, Exch, &i, 0,\r
+                                       0, 0,   0, 0,   1, 0, 0);\r
+                               erc = WaitMsg(Exch, Msg);\r
+                       }\r
+\r
+                       /* Now we deallocate the JCB and the TSSExch\r
+                       which will free the TSS automatically!\r
+                       */\r
+\r
+                       DeAllocExch(KillMsg[1]);\r
+                       DeAllocJCB(pJCB);       /* No error returned */\r
+\r
+                       /* When the JCB was created, the PD and it's 1st\r
+                       PDE (PT) were allocated as two pages next to each other\r
+                       in linear memory.  So we can just deallocate both\r
+                       pages in one shot. Then we deallocate the single\r
+                       page for virtual video.\r
+                       */\r
+\r
+                       DeAllocPage(pPD, 2);\r
+                       DeAllocPage(pVid, 1);\r
+\r
+                       fKilled = 1;\r
+                       /* We're done (and so is he...) */\r
+               }\r
+       }\r
+\r
+} /* for EVER */\r
+}\r
+\r
+/*********************************************************\r
+    This is the Manager task for the Monitor.\r
+    It allows us to switch jobs with the\r
+    CTRL-ALT-PageDown key.\r
+    We don't actually switch jobs, we just\r
+    ressign video and keyboard to the next\r
+    active job (except the Debugger).\r
+    Also, if the debugger has the video...\r
+    we don't do it at all!\r
+    This also looks for CTRL-ALT-DELETE which kills\r
+    the job that owns the keyboard/Video so long as it's\r
+    not the Monitor or the Debugger.\r
+*********************************************************/\r
+\r
+static void MngrTask(void)\r
+{\r
+long erc, i, j, fDone;\r
+char *pJCB;\r
+\r
+/* Leave a Global Key Request outstanding with KBD service\r
+   for the status task\r
+*/\r
+\r
+erc = Request("KEYBOARD", 2, MngrExch, &MngrHndl, 0, &gcode,\r
+                         4, 0, 0, 0, 0, 0);\r
+\r
+for(;;) \r
+{\r
+       erc = WaitMsg(MngrExch, MngrMsg);\r
+\r
+       if (!erc)\r
+       {\r
+               if ((gcode & 0xff) == 0x0C)\r
+               {\r
+                       /*  Find next valid Job that is NOT the\r
+                       debugger and assign Vid and Keyboard to\r
+                               it.\r
+                       */\r
+\r
+                       erc = GetVidOwner(&j);\r
+                       fDone = 0;\r
+                       i = j;\r
+                       while (!fDone)\r
+                       {\r
+                               i++;\r
+                               if (i==2) i = 3;\r
+                               else if (i>34) i = 1;\r
+                               erc = GetpJCB(i, &pJCB);                /* erc = 0 if valid JCB */\r
+                               if ((!erc) || (i==j))\r
+                                       fDone = 1;\r
+                       }\r
+                       if (i != j)\r
+                       {\r
+                               SetVidOwner(i);\r
+                               erc = Request("KEYBOARD", 4, MngrExch, &MngrHndl, 0,\r
+                                       0, 0,   0, 0,   i, 0, 0);\r
+                               erc = WaitMsg(MngrExch, MngrMsg);\r
+\r
+                       }\r
+               }\r
+               else if ((gcode & 0xff) == 0x7F)        /* CTRL-ALT-DEL (Kill)*/\r
+               {\r
+                       erc = GetVidOwner(&j);\r
+                       erc = KillJob(j);\r
+               }\r
+\r
+               /* leave another global key request */\r
+               erc = Request("KEYBOARD", 2, MngrExch, &MngrHndl, 0, &gcode,\r
+                                 4, 0, 0, 0, 0, 0);\r
+       }\r
+\r
+} /* for EVER */\r
+}\r
+\r
+/*********************************************************\r
+  This simply does a software interrupt 03 (Debugger).\r
+*********************************************************/\r
+\r
+static void GoDebug(void)\r
+{\r
+;\r
+#asm\r
+       INT 03\r
+#endasm\r
+return;\r
+}\r
+\r
+/*********************************************************\r
+  This strobes keyboard data port 60h with 00 after\r
+  sending 0D1 command to Command port.  We have to loop\r
+  reading the status bit of the command port to make\r
+  sure it's OK to send the command. This resets the\r
+  processor which then executes the boot ROM.\r
+*********************************************************/\r
+\r
+static void Reboot(void)\r
+{\r
+;\r
+#asm\r
+               CLI                     ;first we clear interrupts\r
+               MOV ECX, 0FFFFh         ;check port up to 64K times\r
+Reboot0:\r
+               IN AL,64h                       ;Read Status Byte into AL\r
+               TEST AL,02h                             ;Test The Input Buffer Full Bit\r
+               LOOPNZ Reboot0\r
+               MOV AL,0FEh                             ;Strobe bit 0 of keyboard crtlr output\r
+               OUT 64h,AL\r
+               STI\r
+#endasm\r
+return;\r
+}\r
+\r
+/*********************************************************\r
+  This reads a file called Initial.Job from the system\r
+  directory and loads all jobs specified in the file.\r
+  Video and keyboard are not assigned to any of these\r
+  jobs unless it is cli.run and the last job loaded.\r
+*********************************************************/\r
+\r
+void LoadJobFile(void)\r
+{\r
+long erc, fh, sjobfile, cbrunfile, i, j, job;\r
+unsigned char sdisk;\r
+char ajobfile[50];\r
+char arunfile[80];\r
+char fdone, fcli;\r
+\r
+       GetSystemDisk(&sdisk);\r
+       sdisk &= 0x7F;\r
+       sdisk += 0x41;          /* 0=A, 1=B, 2=C etc. */\r
+       ajobfile[0] = sdisk;\r
+       CopyData(":\\MMSYS\\INITIAL.JOB\0", &ajobfile[1], 20);\r
+       sjobfile = strlen(ajobfile);\r
+\r
+       erc =  OpenFile(ajobfile, sjobfile, 0, 1, &fh);\r
+       if (!erc)\r
+       {\r
+               fdone = 0;\r
+               job = 0;\r
+               fcli = 0;\r
+               while (!fdone)\r
+               {\r
+                       i = 0;\r
+                       do\r
+                       {\r
+                               erc = ReadBytes(fh, &arunfile[i++], 1, &j);\r
+\r
+                       } while ((!erc) && (arunfile[i-1] != 0x0A) && (i < 80));\r
+\r
+                       if ((!erc) && (i > 1))\r
+                       {\r
+                               if (arunfile[0] == ';')  /* a comment line */\r
+                                       continue;\r
+\r
+                               cbrunfile = 0;\r
+                               while ((arunfile[cbrunfile] != 0x0A) &&\r
+                       (arunfile[cbrunfile] != 0x0D) &&\r
+                       (arunfile[cbrunfile] != 0x20) &&\r
+                       (arunfile[cbrunfile] != 0x09) &&\r
+                       (arunfile[cbrunfile]))\r
+                                       cbrunfile++;\r
+\r
+                               if (cbrunfile > 2)\r
+                               {\r
+                                       arunfile[cbrunfile] = 0; /* null terminte for display */\r
+\r
+                                       if ((cbrunfile > 8) &&\r
+                                               (CompareNCS(&arunfile[cbrunfile-7],\r
+                                                                       "cli.run", 7) == -1))\r
+                                               fcli = 1;\r
+                                       else\r
+                                               fcli = 0;\r
+\r
+                                       xprintf("Loading: %s...\r\n", arunfile);\r
+                                       CheckScreen();\r
+                                       erc =  LoadNewJob(arunfile, cbrunfile, &job);\r
+                                       if (!erc)\r
+                                       {\r
+                                               xprintf("Successfully loaded as job %d\r\n", job);\r
+                                               CheckScreen();\r
+                                               Sleep(50);\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               xprintf("ERROR %d Loading job\r\n", erc);\r
+                                               CheckScreen();\r
+                                               Sleep(50);\r
+                                               job = 0;\r
+                                               erc = 0;\r
+                                       }\r
+                               }\r
+                       }\r
+                       else fdone = 1;\r
+               }\r
+               CloseFile(fh);\r
+\r
+               /* if the last succesfully loaded job was a cli,\r
+                  assign the keybaord and video to it.\r
+               */\r
+\r
+               if ((job > 2) && (fcli))\r
+               {\r
+                       SetVidOwner(job);\r
+                       erc = Request("KEYBOARD", 4, GP1Exch, &GP1Hndl, 0,\r
+                               0, 0,   0, 0,   job, 0, 0);\r
+                       if (!erc)\r
+                               erc = WaitMsg(GP1Exch, GP1Msg);\r
+               }\r
+       }\r
+       else\r
+       {\r
+               xprintf("INITIAL.JOB file not found in system directory.\r\n");\r
+               CheckScreen();\r
+       }\r
+}\r
+\r
+\r
+/*********************************************************\r
+  This Loads the MMURTL Command Line Interpreter\r
+  and switches video and keyboard to give the\r
+  user access to it.\r
+*********************************************************/\r
+\r
+static long LoadCLI(void)\r
+{\r
+long erc, job;\r
+unsigned char sdisk, acli[40];\r
+\r
+       GetSystemDisk(&sdisk);\r
+       sdisk &= 0x7F;\r
+       sdisk += 0x41;          /* 0=A, 1=B, 2=C etc. */\r
+       acli[0] = sdisk;\r
+       CopyData(":\\MMSYS\\CLI.RUN\0", &acli[1], 16);\r
+       xprintf("Loading: %s...", acli);\r
+\r
+       erc =  LoadNewJob(acli, strlen(acli), &job);\r
+       if (!erc)\r
+       {\r
+               xprintf("New CLI Job Number is: %d\r\n", job);\r
+               CheckScreen();\r
+               Sleep(50);\r
+               SetVidOwner(job);\r
+               erc = Request("KEYBOARD", 4, GP1Exch, &GP1Hndl, 0,\r
+                       0, 0,   0, 0,   job, 0, 0);\r
+               if (!erc)\r
+                       erc = WaitMsg(GP1Exch, GP1Msg);\r
+       }\r
+return erc;\r
+}\r
+\r
+/*********************************************************\r
+    This is the main procedure called from the OS after\r
+    all OS structures and memory are initialized.\r
+*********************************************************/\r
+\r
+void Monitor(void)\r
+{\r
+long erc, i, j, k, iCol, iLine;\r
+unsigned long ccode, ccode1;\r
+unsigned char c;\r
+char text[70];\r
+\r
+InitScreen();\r
+\r
+Tone(250,15);          /* 250 Hz for 150ms */\r
+Tone(1000,33);         /* 250 Hz for 330ms */\r
+\r
+/* Allocate an exchange for the Manager task global keycode */\r
+\r
+erc = AllocExch(&MngrExch);\r
+if (erc)\r
+  xprintf("AllocExch (Mngr Exch) Error: %d\r\n", erc);\r
+\r
+erc = SpawnTask( &StatTask, 24, 0, &StatStack[255], 1 );       /* Task 4 */\r
+if (erc)\r
+  xprintf("SpawnTask (StatTask) Error: %d\r\n", erc);\r
+\r
+erc = AllocExch(&KillExch);\r
+if (erc)\r
+  xprintf("AllocExch (Kill Exch) Error: %d\r\n", erc);\r
+\r
+Color = YELLOW|BGBLACK;\r
+xprintf("MMURTL (tm) - Message based, MUltitasking, Real-Time kerneL\r\n");\r
+xprintf("Copyright (c) R.A.Burgess, 1991-1995  ALL RIGHTS RESERVED\r\n\r\n");\r
+\r
+Color = WHITE|BGBLACK;\r
+\r
+c = ((BootDrive & 0x7f) + 0x41);\r
+xprintf("BootDrive: %c\r\n", c);\r
+\r
+i = (oMemMax+1)/1024;\r
+xprintf("Total memory (Kb): %d\r\n", i);\r
+\r
+erc = QueryPages(&nMemPages);\r
+i = (nMemPages*4096)/1024;\r
+xprintf("Free memory  (Kb): %d\r\n", i);\r
+\r
+erc = InitKBDService();\r
+xprintf("Init KBD Service Error: %d\r\n", erc);\r
+\r
+erc = coms_setup();\r
+xprintf("Init Serial Comms Device Driver Error: %d\r\n", erc);\r
+\r
+erc = lpt_setup();\r
+xprintf("Init Parallel LPT Device Driver Error: %d\r\n", erc);\r
+\r
+/* Allocate general purpose exchanges to use in the monitor */\r
+\r
+erc = AllocExch(&GPExch);\r
+if (erc)\r
+  xprintf("AllocExch Error: %d\r\n", erc);\r
+\r
+erc = AllocExch(&GP1Exch);\r
+if (erc)\r
+  xprintf("AllocExch GP1 Error: %d\r\n", erc);\r
+\r
+xprintf("Init floppy device driver... Error: ");\r
+erc = fdisk_setup();\r
+xprintf("%d\r\n", erc);\r
+\r
+xprintf("Init hard disk device driver... Error: ");\r
+erc = hdisk_setup();\r
+xprintf("%d\r\n", erc);\r
+\r
+xprintf("Initializing file system...\r\n");\r
+erc = InitFS();\r
+xprintf("File System... Error: %d\r\n", erc);\r
+\r
+/* Spawn manager task */\r
+\r
+SpawnTask( &MngrTask, 10, 0, &MngrStack[255], 1 );\r
+\r
+/*\r
+   Call LoadJobFile to read job file from system directory \r
+   and execute and jobs listed there.\r
+*/\r
+\r
+LoadJobFile();\r
+\r
+for (;;)  /* Loop forEVER looking for user desires */\r
+{\r
+\r
+       /* Make a ReadKbd Key Request with KBD service. Tell it\r
+       to wait for a key.\r
+       */\r
+\r
+       erc = Request("KEYBOARD", 1, GPExch, &GPHndl, 0, &ccode,\r
+                                         4, 0, 0, 1, 0, 0);\r
+       if (erc)\r
+           xprintf("Kbd Svc Request KERNEL ERROR: %d\r\n", erc);\r
+\r
+       /* wait for the keycode to come back */\r
+\r
+       erc = WaitMsg(GPExch, GPMsg);\r
+       if (erc)\r
+        xprintf("KERNEL Error from Wait msg:  %d\r\n", erc);\r
+\r
+       c = ccode & 0xff; /* lop off everything but the key value */\r
+\r
+       switch (c)\r
+       {\r
+       case 0x0F:              /* F1 Run */\r
+                       erc = LoadCLI();\r
+                       if (erc)\r
+                               xprintf("Error from LoadCLI:  %d\r\n", erc);\r
+                       break;\r
+       case 0x10:              /* F2 Jobs */\r
+                       InitScreen();\r
+                       j = 2; /* Line */\r
+                       k = 0; /* Col offset */\r
+                       for     (i=1; i<nMaxJCBs; i++)\r
+                       {\r
+                               if (j > 20)\r
+                                       k = 40;\r
+                               erc = GetpJCB(i, &pJCB);                /* erc = 0 if valid JCB */\r
+                               if (!erc)\r
+                               {\r
+                                       SetXY(k,j);\r
+                                       xprintf("Job: %d\r\n", pJCB->JobNum);\r
+                                       SetXY(k+10,j);\r
+                                       CopyData(&pJCB->sbJobName[1], text, 13);\r
+                                       text[pJCB->sbJobName[0]] = 0;\r
+                                       xprintf("Name: %s\r\n", text);\r
+                                       j++;\r
+                               }\r
+                       }\r
+                       break;\r
+       case 0x11:              /* F3 Stats - loops displaying status till key is hit */\r
+                       InitScreen();\r
+                       while (erc = ReadKbd(&ccode1, 0))\r
+                       { /* ReadKbd no wait until no error */\r
+                               SetXY(0,1);\r
+                               erc = QueryPages(&nMemPages);\r
+                xprintf("Any key to dismiss status... \r\n");\r
+                xprintf("Free 4K memory pages:      %d\r\n", nMemPages);\r
+                xprintf("Task switches total:       %d\r\n", nSwitches);\r
+                xprintf("Preemptive task switches:  %d\r\n", nSlices);\r
+                xprintf("CPU idle ticks (no work):  %d\r\n", nHalts);\r
+                xprintf("Tasks Ready to Run:        %d\r\n", nReady);\r
+                xprintf("Free Task State Segments:  %d\r\n", nTSSLeft);\r
+                xprintf("Free Job Control Blocks:   %d\r\n", nJCBLeft);\r
+                xprintf("Free Request Blocks:       %d\r\n", nRQBLeft);\r
+                xprintf("Free Link Blocks:          %d\r\n", nLBLeft);\r
+                xprintf("Free Exchanges:            %d\r\n", nEXCHLeft);\r
+                               SetXY(0,1);\r
+                               PutVidChars(29, 1, "|",  1, GREEN|BGBLACK); Sleep(9);\r
+                               PutVidChars(29, 1, "/",  1, GREEN|BGBLACK); Sleep(9);\r
+                               PutVidChars(29, 1, "-",  1, GREEN|BGBLACK); Sleep(12);\r
+                               PutVidChars(29, 1, "\\", 1, GREEN|BGBLACK); Sleep(9);\r
+                               PutVidChars(29, 1, "|",  1, GREEN|BGBLACK); Sleep(9);\r
+                               PutVidChars(29, 1, "/",  1, GREEN|BGBLACK); Sleep(9);\r
+                               PutVidChars(29, 1, "-",  1, GREEN|BGBLACK); Sleep(12);\r
+                               PutVidChars(29, 1, "\\", 1, GREEN|BGBLACK); Sleep(9);\r
+                               PutVidChars(29, 1, " ",  1, GREEN|BGBLACK);\r
+                       }\r
+                       SetXY(0,12);\r
+            xprintf ("\r\n");\r
+                       break;\r
+       case 0x16:              /* F8 Reboot */\r
+                       xprintf("\r\nF8 again to reboot, any other key to cancel");\r
+                       erc = ReadKbd(&ccode1, 1);\r
+                       if ((ccode1 & 0xff) == 0x16)\r
+                               Reboot();\r
+                       xprintf("...Cancelled\r\n");\r
+                       break;\r
+       case 0x18:              /* F10 Debug */\r
+                       GoDebug();\r
+                       break;\r
+       case 0x00:              /* No Key */\r
+                       Sleep(3);       /* Sleep for 30 ms */\r
+                       break;\r
+       case 0x12:              /* F4 - future use */\r
+       case 0x13:              /* F5  */\r
+       case 0x14:              /* F6  */\r
+       case 0x15:              /* F7  */\r
+       case 0x17:              /* F9  */\r
+       case 0x19:              /* F11 */\r
+       case 0x1A:              /* F12 */\r
+                       break;\r
+       default:\r
+               if (((c > 0x1F) && (c < 0x80)) ||\r
+                       (c==0x0D) || (c==8))\r
+                       {\r
+                               if (c==0x0D)\r
+                                       TTYOut (CRLF, 2, WHITE|BGBLACK);\r
+                               else\r
+                                       TTYOut (&c, 1, WHITE|BGBLACK);\r
+                  }\r
+       }\r
+\r
+       GetXY(&iCol, &iLine);\r
+       if (iLine >= 23)\r
+       {\r
+               ScrollVid(0,1,80,23,1);\r
+               SetXY(0,22);\r
+       }\r
+\r
+} /* for EVER */\r
+\r
+}\r
+\r
+\r
+/*===========  THE END  =========================================*/\r