2 MMURTL Operating System Source Code
\r
3 Copyright 1991,1992,1993,1994 Richard A. Burgess
\r
9 /* This is the MMURTL Monitor program.
\r
10 It provides the user with statistics and control of the
\r
11 environment, and a few basic functions such as starting a CLI.
\r
13 The monitor has three main functions:
\r
14 1) Sets up internal device drivers and services.
\r
15 2) Checks config files and loads any system services specified
\r
17 3) Loads a CLI and assigns the video and keyboard to it.
\r
19 Because the Monitor is part of the OS we do not declare a main
\r
20 function. The entry point to the monitor is called Monitor and
\r
21 it has no parameters. After the OS gets done with its low level
\r
22 initialization, the monitor procedure is "Jumped" to.
\r
26 #define U32 unsigned long
\r
28 #define U16 unsigned int
\r
30 #define U8 unsigned char
\r
33 #include "MKernel.h"
\r
34 #include "MMemory.h"
\r
41 #include "MDevDrv.h"
\r
45 #define ErcNoDevice 504
\r
47 static char rgStatLine[] =
\r
48 "mm/dd/yy 00:00:00 MMURTL Monitor Tick:0 ";
\r
50 static char rgMonMenu1[] = "LdCLI\xb3Jobs \xb3Stats \xb3 ";
\r
51 static char rgMonMenu2[] = " \xb3 \xb3 \xb3Reboot";
\r
52 static char rgMonMenu3[] = " \xb3Debug \xb3 \xb3 ";
\r
54 static char rgCPR1[] ="MMURTL (tm) - Message based, MUltitasking, Real-Time kerneL";
\r
55 static char rgCPR2[] ="Copyright (c) R.A. Burgess, 1990-1993, All Rights Reserved";
\r
57 static char *CRLF = "\r\n\r\n";
\r
59 static unsigned long Color = WHITE|BGBLACK; /* Color test for xprintf */
\r
61 static long time, date, tick;
\r
63 unsigned long KillExch; /* Messaging for stat task KILL proc */
\r
65 static unsigned long KillMsg[2]; /* First DWORD = TSSExch, second is ERROR */
\r
66 static unsigned long KillError;
\r
67 static unsigned long KillJobNum;
\r
68 static unsigned char fKilled;
\r
70 static unsigned long MngrExch; /* Messaging for stat task */
\r
71 static unsigned long MngrMsg[2];
\r
72 static unsigned long MngrHndl;
\r
73 static unsigned long gcode;
\r
75 static unsigned long GPExch; /* Messaging for main */
\r
76 static unsigned long GPMsg[2];
\r
77 static unsigned long GPHndl;
\r
79 static unsigned long GP1Exch; /* Extra Messaging for main */
\r
80 static unsigned long GP1Msg[2];
\r
81 static unsigned long GP1Hndl;
\r
83 /* Structure for disk device driver status and setup */
\r
85 static struct diskstattype {
\r
90 U8 type_now; /* current disk type for drive selected */
\r
91 U8 resvd1[2]; /* padding for DWord align */
\r
92 U32 nCyl; /* total physical cylinders */
\r
93 U32 nHead; /* total heads on device */
\r
94 U32 nSectors; /* Sectors per track */
\r
95 U32 nBPS; /* Number of bytes per sect */
\r
101 U8 fIntOnReset; /* Interrupt was received on HDC_RESET */
\r
107 U8 ResetStatByte; /* Status Byte immediately after RESET */
\r
109 U32 resvd1[2]; /* out to 64 bytes */
\r
112 static struct diskstattype DiskStatus;
\r
114 #define nMaxJCBs 34 /* 32 dynamic plus 2 static */
\r
115 static struct JCBRec *pJCB;
\r
117 static long StatStack[256]; /* 1024 byte stack for Stat task */
\r
119 static long MngrStack[256]; /* 1024 byte stack for Mngr task */
\r
121 static unsigned char Buffer[512];
\r
122 static unsigned long nMemPages;
\r
124 extern unsigned long oMemMax;
\r
125 extern unsigned long nSwitches;
\r
126 extern unsigned long nSlices;
\r
127 extern unsigned long nHalts;
\r
128 extern unsigned long nReady;
\r
129 extern unsigned long nRQBLeft;
\r
130 extern unsigned long nJCBLeft;
\r
131 extern unsigned long nTSSLeft;
\r
132 extern unsigned long nLBLeft;
\r
133 extern unsigned long nEXCHLeft;
\r
134 extern unsigned long BootDrive;
\r
136 /*============ protos (NEAR MMURTL support calls) =================*/
\r
138 extern long InitKBDService(void); /* From Keyboard.asm */
\r
139 extern long fdisk_setup(void); /* From Floppy.c */
\r
140 extern long hdisk_setup(void); /* From HardIDE.c */
\r
141 extern long coms_setup(void); /* From RS232.c */
\r
142 extern long lpt_setup(void); /* From Parallel.c */
\r
143 extern long InitFS(void); /* From Fsys.c */
\r
145 extern long GetExchOwner(long Exch, char *pJCBRet);
\r
146 extern long DeAllocJCB(long *pdJobNumRet, char *ppJCBRet);
\r
148 /*=================== START OF CODE ====================*/
\r
150 /**************************************************************
\r
151 Formatted output routines for montitor program
\r
153 ***************************************************************/
\r
155 #include <stdarg.h>
\r
159 /*********************************************
\r
160 * Determine if a character is a numeric digit
\r
161 **********************************************/
\r
163 static long isdigit(long chr)
\r
182 static long strlen(char *cs)
\r
189 CMP BYTE PTR [ESI],0
\r
198 /*************************************************************
\r
199 This does the actual parsing of the format and also moves to
\r
200 the next arg(s) in the list from the passed in arg pointer.
\r
201 The number of chars written is returned (not incl \0).
\r
202 **************************************************************/
\r
203 static long _ffmt(char *outptr, char *fmt, long *argptr)
\r
205 char numstk[33], *ptr, justify, zero, minus, chr;
\r
206 unsigned long width, value, i, total;
\r
209 while(chr = *fmt++)
\r
212 { /* format code */
\r
215 *ptr = justify = minus = 0;
\r
216 width = value = i = 0;
\r
219 { /* left justify */
\r
223 if(chr == '0') /* leading zeros */
\r
225 while(isdigit(chr))
\r
226 { /* field width specifier */
\r
227 width = (width * 10) + (chr - '0');
\r
231 value = *--argptr; /* get parameter value */
\r
235 case 'd' : /* decimal number */
\r
236 if(value & 0x80000000)
\r
241 case 'u' : /* unsigned number */
\r
244 case 'x' : /* hexidecimal number */
\r
248 case 'o' : /* octal number */
\r
251 case 'b' : /* binary number */
\r
254 case 'c' : /* character data */
\r
257 case 's' : /* string */
\r
258 ptr = value; /* value is ptr to string */
\r
260 default: /* all others */
\r
262 ++argptr; /* backup to last arg */
\r
265 if(i) /* for all numbers, generate the ASCII string */
\r
268 if((chr = (value % i) + '0') > '9')
\r
274 /* output sign if any */
\r
284 /* pad with 'zero' value if right justify enabled */
\r
286 if(width && !justify)
\r
288 for(i = strlen(ptr); i < width; ++i)
\r
298 while((*ptr) && (i <= value))
\r
300 *outptr++ = *ptr++;
\r
305 /* pad with 'zero' value if left justify enabled */
\r
307 if(width && justify)
\r
319 /* not format char, just move into string */
\r
329 /************************************
\r
330 Formatted print to screen
\r
331 *************************************/
\r
333 long xprintf(char *fmt, ...)
\r
337 char buffer[S_SIZE];
\r
339 va_start(ap, fmt); /* set up ap pointer */
\r
340 total = _ffmt(buffer, fmt, ap);
\r
341 TTYOut(buffer, strlen(buffer), Color);
\r
346 /************************************
\r
347 Formatted print to string s
\r
348 *************************************/
\r
350 long xsprintf(char *s, char *fmt, ...)
\r
355 va_start(ap, fmt); /* set up ap pointer */
\r
356 total = _ffmt(s, fmt, ap);
\r
361 /**********************************************
\r
362 Checks to ensure we don't scroll the function
\r
363 keys off the screen.
\r
364 **********************************************/
\r
370 GetXY(&iCol, &iLine);
\r
373 ScrollVid(0,1,80,23,1);
\r
378 /*********************************************************
\r
379 This is called to initialize the sacreen.
\r
380 *********************************************************/
\r
382 static void InitScreen(void)
\r
385 xsprintf(&rgStatLine[70], "%d", tick);
\r
386 PutVidChars(0,0, rgStatLine, 80, WHITE|BGBLUE);
\r
387 PutVidChars(0, 24, rgMonMenu1, 26, BLUE|BGWHITE);
\r
388 PutVidChars(27, 24, rgMonMenu2, 26, BLUE|BGWHITE);
\r
389 PutVidChars(54, 24, rgMonMenu3, 25, BLUE|BGWHITE);
\r
394 /*********************************************************
\r
395 This is the status task for the Monitor.
\r
396 Besides displaying the top status line for
\r
397 the montitor, it has the job of looking for
\r
398 messages from the job code that indicate
\r
399 a job has terminated. When it gets one,
\r
400 it recovers the last of the resources and
\r
401 then notifies the user on the screen.
\r
402 *********************************************************/
\r
404 static void StatTask(void)
\r
406 unsigned long erc, i, Exch, Msg[2];
\r
411 GetCMOSTime(&time);
\r
412 rgStatLine[10] = '0' + ((time >> 20) & 0x0f);
\r
413 rgStatLine[11] = '0' + ((time >> 16) & 0x0f);
\r
414 rgStatLine[13] = '0' + ((time >> 12) & 0x0f);
\r
415 rgStatLine[14] = '0' + ((time >> 8) & 0x0f);
\r
416 rgStatLine[16] = '0' + ((time >> 4) & 0x0f); /* seconds */
\r
417 rgStatLine[17] = '0' + (time & 0x0f);
\r
419 GetCMOSDate(&date);
\r
420 rgStatLine[0] = '0' + ((date >> 20) & 0x0f); /* month */
\r
421 rgStatLine[1] = '0' + ((date >> 16) & 0x0f);
\r
422 rgStatLine[3] = '0' + ((date >> 12) & 0x0f); /* Day */
\r
423 rgStatLine[4] = '0' + ((date >> 8) & 0x0f);
\r
424 rgStatLine[6] = '0' + ((date >> 28) & 0x0f); /* year */
\r
425 rgStatLine[7] = '0' + ((date >> 24) & 0x0f);
\r
427 GetTimerTick(&tick);
\r
428 xsprintf(&rgStatLine[70], "%d", tick);
\r
429 PutVidChars(0,0, rgStatLine, 80, WHITE|BGBLUE);
\r
431 Sleep(50); /* sleep 0.5 second */
\r
433 GetTimerTick(&tick);
\r
434 xsprintf(&rgStatLine[70], "%d", tick);
\r
435 PutVidChars(0,0, rgStatLine, 80, WHITE|BGBLUE);
\r
437 Sleep(50); /* sleep 0.5 second */
\r
439 /* Now we check for tasks that are Jobs that are killing
\r
440 themselves (either due to fatal errors or no exitjob).
\r
441 The message has Error, TSSExch in it.
\r
444 erc = CheckMsg(KillExch, KillMsg);
\r
446 { /* someones there wanting to terminate...*/
\r
448 /* Get and save the error (KillMsg[0]) */
\r
449 KillError = KillMsg[0];
\r
451 /* Call GetExchOwner which gives us pJCB */
\r
453 erc = GetExchOwner(KillMsg[1], &pJCB);
\r
457 KillJobNum = pJCB->JobNum;
\r
459 xprintf("Job number %d terminated. Error: %d\r\n",
\r
460 KillJobNum, KillError);
\r
463 pPD = pJCB->pJcbPD;
\r
464 pVid = pJCB->pVirtVid;
\r
466 /* Must change video to monitor if this guy owned it */
\r
469 if (i == KillJobNum)
\r
471 GetTSSExch(&Exch); /* Use our TSS exchange for Request */
\r
473 erc = Request("KEYBOARD", 4, Exch, &i, 0,
\r
474 0, 0, 0, 0, 1, 0, 0);
\r
475 erc = WaitMsg(Exch, Msg);
\r
478 /* Now we deallocate the JCB and the TSSExch
\r
479 which will free the TSS automatically!
\r
482 DeAllocExch(KillMsg[1]);
\r
483 DeAllocJCB(pJCB); /* No error returned */
\r
485 /* When the JCB was created, the PD and it's 1st
\r
486 PDE (PT) were allocated as two pages next to each other
\r
487 in linear memory. So we can just deallocate both
\r
488 pages in one shot. Then we deallocate the single
\r
489 page for virtual video.
\r
492 DeAllocPage(pPD, 2);
\r
493 DeAllocPage(pVid, 1);
\r
496 /* We're done (and so is he...) */
\r
503 /*********************************************************
\r
504 This is the Manager task for the Monitor.
\r
505 It allows us to switch jobs with the
\r
506 CTRL-ALT-PageDown key.
\r
507 We don't actually switch jobs, we just
\r
508 ressign video and keyboard to the next
\r
509 active job (except the Debugger).
\r
510 Also, if the debugger has the video...
\r
511 we don't do it at all!
\r
512 This also looks for CTRL-ALT-DELETE which kills
\r
513 the job that owns the keyboard/Video so long as it's
\r
514 not the Monitor or the Debugger.
\r
515 *********************************************************/
\r
517 static void MngrTask(void)
\r
519 long erc, i, j, fDone;
\r
522 /* Leave a Global Key Request outstanding with KBD service
\r
523 for the status task
\r
526 erc = Request("KEYBOARD", 2, MngrExch, &MngrHndl, 0, &gcode,
\r
531 erc = WaitMsg(MngrExch, MngrMsg);
\r
535 if ((gcode & 0xff) == 0x0C)
\r
537 /* Find next valid Job that is NOT the
\r
538 debugger and assign Vid and Keyboard to
\r
542 erc = GetVidOwner(&j);
\r
549 else if (i>34) i = 1;
\r
550 erc = GetpJCB(i, &pJCB); /* erc = 0 if valid JCB */
\r
551 if ((!erc) || (i==j))
\r
557 erc = Request("KEYBOARD", 4, MngrExch, &MngrHndl, 0,
\r
558 0, 0, 0, 0, i, 0, 0);
\r
559 erc = WaitMsg(MngrExch, MngrMsg);
\r
563 else if ((gcode & 0xff) == 0x7F) /* CTRL-ALT-DEL (Kill)*/
\r
565 erc = GetVidOwner(&j);
\r
569 /* leave another global key request */
\r
570 erc = Request("KEYBOARD", 2, MngrExch, &MngrHndl, 0, &gcode,
\r
577 /*********************************************************
\r
578 This simply does a software interrupt 03 (Debugger).
\r
579 *********************************************************/
\r
581 static void GoDebug(void)
\r
590 /*********************************************************
\r
591 This strobes keyboard data port 60h with 00 after
\r
592 sending 0D1 command to Command port. We have to loop
\r
593 reading the status bit of the command port to make
\r
594 sure it's OK to send the command. This resets the
\r
595 processor which then executes the boot ROM.
\r
596 *********************************************************/
\r
598 static void Reboot(void)
\r
602 CLI ;first we clear interrupts
\r
603 MOV ECX, 0FFFFh ;check port up to 64K times
\r
605 IN AL,64h ;Read Status Byte into AL
\r
606 TEST AL,02h ;Test The Input Buffer Full Bit
\r
608 MOV AL,0FEh ;Strobe bit 0 of keyboard crtlr output
\r
615 /*********************************************************
\r
616 This reads a file called Initial.Job from the system
\r
617 directory and loads all jobs specified in the file.
\r
618 Video and keyboard are not assigned to any of these
\r
619 jobs unless it is cli.run and the last job loaded.
\r
620 *********************************************************/
\r
622 void LoadJobFile(void)
\r
624 long erc, fh, sjobfile, cbrunfile, i, j, job;
\r
625 unsigned char sdisk;
\r
630 GetSystemDisk(&sdisk);
\r
632 sdisk += 0x41; /* 0=A, 1=B, 2=C etc. */
\r
633 ajobfile[0] = sdisk;
\r
634 CopyData(":\\MMSYS\\INITIAL.JOB\0", &ajobfile[1], 20);
\r
635 sjobfile = strlen(ajobfile);
\r
637 erc = OpenFile(ajobfile, sjobfile, 0, 1, &fh);
\r
648 erc = ReadBytes(fh, &arunfile[i++], 1, &j);
\r
650 } while ((!erc) && (arunfile[i-1] != 0x0A) && (i < 80));
\r
652 if ((!erc) && (i > 1))
\r
654 if (arunfile[0] == ';') /* a comment line */
\r
658 while ((arunfile[cbrunfile] != 0x0A) &&
\r
659 (arunfile[cbrunfile] != 0x0D) &&
\r
660 (arunfile[cbrunfile] != 0x20) &&
\r
661 (arunfile[cbrunfile] != 0x09) &&
\r
662 (arunfile[cbrunfile]))
\r
667 arunfile[cbrunfile] = 0; /* null terminte for display */
\r
669 if ((cbrunfile > 8) &&
\r
670 (CompareNCS(&arunfile[cbrunfile-7],
\r
671 "cli.run", 7) == -1))
\r
676 xprintf("Loading: %s...\r\n", arunfile);
\r
678 erc = LoadNewJob(arunfile, cbrunfile, &job);
\r
681 xprintf("Successfully loaded as job %d\r\n", job);
\r
687 xprintf("ERROR %d Loading job\r\n", erc);
\r
699 /* if the last succesfully loaded job was a cli,
\r
700 assign the keybaord and video to it.
\r
703 if ((job > 2) && (fcli))
\r
706 erc = Request("KEYBOARD", 4, GP1Exch, &GP1Hndl, 0,
\r
707 0, 0, 0, 0, job, 0, 0);
\r
709 erc = WaitMsg(GP1Exch, GP1Msg);
\r
714 xprintf("INITIAL.JOB file not found in system directory.\r\n");
\r
720 /*********************************************************
\r
721 This Loads the MMURTL Command Line Interpreter
\r
722 and switches video and keyboard to give the
\r
724 *********************************************************/
\r
726 static long LoadCLI(void)
\r
729 unsigned char sdisk, acli[40];
\r
731 GetSystemDisk(&sdisk);
\r
733 sdisk += 0x41; /* 0=A, 1=B, 2=C etc. */
\r
735 CopyData(":\\MMSYS\\CLI.RUN\0", &acli[1], 16);
\r
736 xprintf("Loading: %s...", acli);
\r
738 erc = LoadNewJob(acli, strlen(acli), &job);
\r
741 xprintf("New CLI Job Number is: %d\r\n", job);
\r
745 erc = Request("KEYBOARD", 4, GP1Exch, &GP1Hndl, 0,
\r
746 0, 0, 0, 0, job, 0, 0);
\r
748 erc = WaitMsg(GP1Exch, GP1Msg);
\r
753 /*********************************************************
\r
754 This is the main procedure called from the OS after
\r
755 all OS structures and memory are initialized.
\r
756 *********************************************************/
\r
760 long erc, i, j, k, iCol, iLine;
\r
761 unsigned long ccode, ccode1;
\r
767 Tone(250,15); /* 250 Hz for 150ms */
\r
768 Tone(1000,33); /* 250 Hz for 330ms */
\r
770 /* Allocate an exchange for the Manager task global keycode */
\r
772 erc = AllocExch(&MngrExch);
\r
774 xprintf("AllocExch (Mngr Exch) Error: %d\r\n", erc);
\r
776 erc = SpawnTask( &StatTask, 24, 0, &StatStack[255], 1 ); /* Task 4 */
\r
778 xprintf("SpawnTask (StatTask) Error: %d\r\n", erc);
\r
780 erc = AllocExch(&KillExch);
\r
782 xprintf("AllocExch (Kill Exch) Error: %d\r\n", erc);
\r
784 Color = YELLOW|BGBLACK;
\r
785 xprintf("MMURTL (tm) - Message based, MUltitasking, Real-Time kerneL\r\n");
\r
786 xprintf("Copyright (c) R.A.Burgess, 1991-1995 ALL RIGHTS RESERVED\r\n\r\n");
\r
788 Color = WHITE|BGBLACK;
\r
790 c = ((BootDrive & 0x7f) + 0x41);
\r
791 xprintf("BootDrive: %c\r\n", c);
\r
793 i = (oMemMax+1)/1024;
\r
794 xprintf("Total memory (Kb): %d\r\n", i);
\r
796 erc = QueryPages(&nMemPages);
\r
797 i = (nMemPages*4096)/1024;
\r
798 xprintf("Free memory (Kb): %d\r\n", i);
\r
800 erc = InitKBDService();
\r
801 xprintf("Init KBD Service Error: %d\r\n", erc);
\r
803 erc = coms_setup();
\r
804 xprintf("Init Serial Comms Device Driver Error: %d\r\n", erc);
\r
807 xprintf("Init Parallel LPT Device Driver Error: %d\r\n", erc);
\r
809 /* Allocate general purpose exchanges to use in the monitor */
\r
811 erc = AllocExch(&GPExch);
\r
813 xprintf("AllocExch Error: %d\r\n", erc);
\r
815 erc = AllocExch(&GP1Exch);
\r
817 xprintf("AllocExch GP1 Error: %d\r\n", erc);
\r
819 xprintf("Init floppy device driver... Error: ");
\r
820 erc = fdisk_setup();
\r
821 xprintf("%d\r\n", erc);
\r
823 xprintf("Init hard disk device driver... Error: ");
\r
824 erc = hdisk_setup();
\r
825 xprintf("%d\r\n", erc);
\r
827 xprintf("Initializing file system...\r\n");
\r
829 xprintf("File System... Error: %d\r\n", erc);
\r
831 /* Spawn manager task */
\r
833 SpawnTask( &MngrTask, 10, 0, &MngrStack[255], 1 );
\r
836 Call LoadJobFile to read job file from system directory
\r
837 and execute and jobs listed there.
\r
842 for (;;) /* Loop forEVER looking for user desires */
\r
845 /* Make a ReadKbd Key Request with KBD service. Tell it
\r
849 erc = Request("KEYBOARD", 1, GPExch, &GPHndl, 0, &ccode,
\r
852 xprintf("Kbd Svc Request KERNEL ERROR: %d\r\n", erc);
\r
854 /* wait for the keycode to come back */
\r
856 erc = WaitMsg(GPExch, GPMsg);
\r
858 xprintf("KERNEL Error from Wait msg: %d\r\n", erc);
\r
860 c = ccode & 0xff; /* lop off everything but the key value */
\r
864 case 0x0F: /* F1 Run */
\r
867 xprintf("Error from LoadCLI: %d\r\n", erc);
\r
869 case 0x10: /* F2 Jobs */
\r
872 k = 0; /* Col offset */
\r
873 for (i=1; i<nMaxJCBs; i++)
\r
877 erc = GetpJCB(i, &pJCB); /* erc = 0 if valid JCB */
\r
881 xprintf("Job: %d\r\n", pJCB->JobNum);
\r
883 CopyData(&pJCB->sbJobName[1], text, 13);
\r
884 text[pJCB->sbJobName[0]] = 0;
\r
885 xprintf("Name: %s\r\n", text);
\r
890 case 0x11: /* F3 Stats - loops displaying status till key is hit */
\r
892 while (erc = ReadKbd(&ccode1, 0))
\r
893 { /* ReadKbd no wait until no error */
\r
895 erc = QueryPages(&nMemPages);
\r
896 xprintf("Any key to dismiss status... \r\n");
\r
897 xprintf("Free 4K memory pages: %d\r\n", nMemPages);
\r
898 xprintf("Task switches total: %d\r\n", nSwitches);
\r
899 xprintf("Preemptive task switches: %d\r\n", nSlices);
\r
900 xprintf("CPU idle ticks (no work): %d\r\n", nHalts);
\r
901 xprintf("Tasks Ready to Run: %d\r\n", nReady);
\r
902 xprintf("Free Task State Segments: %d\r\n", nTSSLeft);
\r
903 xprintf("Free Job Control Blocks: %d\r\n", nJCBLeft);
\r
904 xprintf("Free Request Blocks: %d\r\n", nRQBLeft);
\r
905 xprintf("Free Link Blocks: %d\r\n", nLBLeft);
\r
906 xprintf("Free Exchanges: %d\r\n", nEXCHLeft);
\r
908 PutVidChars(29, 1, "|", 1, GREEN|BGBLACK); Sleep(9);
\r
909 PutVidChars(29, 1, "/", 1, GREEN|BGBLACK); Sleep(9);
\r
910 PutVidChars(29, 1, "-", 1, GREEN|BGBLACK); Sleep(12);
\r
911 PutVidChars(29, 1, "\\", 1, GREEN|BGBLACK); Sleep(9);
\r
912 PutVidChars(29, 1, "|", 1, GREEN|BGBLACK); Sleep(9);
\r
913 PutVidChars(29, 1, "/", 1, GREEN|BGBLACK); Sleep(9);
\r
914 PutVidChars(29, 1, "-", 1, GREEN|BGBLACK); Sleep(12);
\r
915 PutVidChars(29, 1, "\\", 1, GREEN|BGBLACK); Sleep(9);
\r
916 PutVidChars(29, 1, " ", 1, GREEN|BGBLACK);
\r
921 case 0x16: /* F8 Reboot */
\r
922 xprintf("\r\nF8 again to reboot, any other key to cancel");
\r
923 erc = ReadKbd(&ccode1, 1);
\r
924 if ((ccode1 & 0xff) == 0x16)
\r
926 xprintf("...Cancelled\r\n");
\r
928 case 0x18: /* F10 Debug */
\r
931 case 0x00: /* No Key */
\r
932 Sleep(3); /* Sleep for 30 ms */
\r
934 case 0x12: /* F4 - future use */
\r
935 case 0x13: /* F5 */
\r
936 case 0x14: /* F6 */
\r
937 case 0x15: /* F7 */
\r
938 case 0x17: /* F9 */
\r
939 case 0x19: /* F11 */
\r
940 case 0x1A: /* F12 */
\r
943 if (((c > 0x1F) && (c < 0x80)) ||
\r
944 (c==0x0D) || (c==8))
\r
947 TTYOut (CRLF, 2, WHITE|BGBLACK);
\r
949 TTYOut (&c, 1, WHITE|BGBLACK);
\r
953 GetXY(&iCol, &iLine);
\r
956 ScrollVid(0,1,80,23,1);
\r
965 /*=========== THE END =========================================*/
\r