--- /dev/null
+/*\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