--- /dev/null
+/* MMLoader.c\r
+ This is the DOS loader for the MMURTL OS RUN file.\r
+\r
+ MMURTL is loaded into memory in two basic pieces.\r
+ The first piece is the DATA segment. It is loaded at linear\r
+ addrsss 0. The default OS stacks are contained in this segment.\r
+ The second piece is the CODE. It is loaded at linear\r
+ address 10000h (The 64K boundry). If you know anything about\r
+ MS-DOS, you that is doesn't consume this kind of memory.\r
+ This loader program will load somwhere between the 25K\r
+ and 64K linear boundry depending on whether DOS is loaded\r
+ "HIGH" or not, and also what TSRs you have installed.\r
+ We must allocate some dummy space to ensure that when we\r
+ load our two segments from disk and copy them down, we don't\r
+ overwrite this loader program (in which case\r
+ your system will lock up tighter than a drum!).\r
+\r
+ NOTE: This is the only program requiring a DOS programming\r
+ tool because "We don't do 16 bit real mode code..."\r
+ This must be compiled with Borland C due to the inline assembler.\r
+\r
+ You MUST use the -B and -ml options. Example\r
+\r
+ BCC -B -ml MMLoader.c\r
+\r
+ The -B forces assebly language compilation and linkage.\r
+ -ml sets it to large model\r
+ You probably won't need to rebuild it all because it reads the\r
+ size of the segments for the MMURTL run file and adjusts the\r
+ buffers for them. So modifying MMURTL (adding or taking away code or\r
+ data) shouldn't affect how the loader works (unless you exceed\r
+ 128K for code or data, in which case you'll have to modify this\r
+ to handle it).\r
+\r
+ What actually happens here:\r
+ (This basically turns MS-DOS into a $79.00 loader)\r
+\r
+ 1) Load the code and data into DOS memory (we don't own)\r
+ Gets the boot drive from OSName or MS-DOS id not\r
+ specified in OSName.\r
+ 3) Clear interrupts\r
+ 2) Turn on the A20 Address Line Gate\r
+ 4) Move the data from the buffers into proper place for execution\r
+ 5) Set the Interrupt and Global Descriptor table registers\r
+ in preparation for protect mode transition\r
+ 6) Set Protected mode bit in CR0 (Control Register 0)\r
+ 7) Jump to OS Code entry point at linear address 10000h\r
+ in the code segment/selector (which is 8h).\r
+ All OS segments are ZERO based.\r
+\r
+ NOTE: This the last the processor sees of 16 bit real mode code\r
+ (no great loss at all in my humble opinion)\r
+\r
+*/\r
+\r
+\r
+\r
+#define U32 unsigned long\r
+#define S32 long\r
+#define U16 unsigned int\r
+#define S16 int\r
+#define U8 unsigned char\r
+#define S8 char\r
+\r
+#define TRUE 1\r
+#define FALSE 1\r
+\r
+#include <ctype.h>\r
+#include <stdio.h>\r
+#include <string.h>\r
+#include <stdlib.h>\r
+#include <dos.h>\r
+#include <dir.h>\r
+#include "\OSSOURCE\runfile.h"\r
+\r
+U32 fEOF = 0;\r
+\r
+long CSegSize = 0;\r
+long DSegSize = 0;\r
+\r
+char *ptest;\r
+\r
+long sStack = 0;\r
+long sCode = 0;\r
+long sData = 0;\r
+\r
+long offCode = 0;\r
+long offData = 0;\r
+long pStart = 0;\r
+\r
+struct tagtype tag;\r
+\r
+char osname[50] = {"MMURTL.RUN"}; /* default OS name */\r
+\r
+FILE *run_fh = 0;\r
+FILE *out_fh = 0;\r
+\r
+unsigned char bootdrive;\r
+\r
+/**** Execute ************************************************\r
+ At this point:\r
+ Interrupts are disabled,\r
+ The code and data are waiting to be moved down to 0 linear\r
+ So we move it, then we tell the processor the size and\r
+ location of the IDT and GDT (with the SIDT and SGDT\r
+ insturctions), then set the protected mode bit and\r
+ jump to the OS code!\r
+ This code can't access any data segment variables.\r
+ It must all be self contained because we relocate it.\r
+**************************************************************/\r
+\r
+void Execute(void)\r
+{\r
+\r
+/* We're pulling some REAL cheating here in real mode\r
+to get this done. We set DS up to where we moved the\r
+variables we need to use. We did this so we wouldn't\r
+overwrite them. Then we move the OS code & data DOWN\r
+and DO IT! This code ends up at 9000:0\r
+*/\r
+\r
+asm { .386P\r
+ CLI\r
+ NOP\r
+\r
+ MOV BX, 9800h\r
+ MOV DS, BX\r
+ MOV BX, 0\r
+ MOV WORD PTR DS:[BX], 07FFh\r
+ MOV BX, 2\r
+ MOV WORD PTR DS:[BX], 0h\r
+ MOV BX, 4\r
+ MOV WORD PTR DS:[BX], 0h\r
+\r
+ MOV BX, 6\r
+ MOV WORD PTR DS:[BX], 017FFh\r
+ MOV BX, 8\r
+ MOV WORD PTR DS:[BX], 0800h\r
+ MOV BX, 10\r
+ MOV WORD PTR DS:[BX], 0h\r
+\r
+\r
+ MOV BX, 0B800h\r
+ MOV ES, BX\r
+ MOV BX, 0\r
+ MOV WORD PTR ES:[BX], 4731h\r
+\r
+ /* Set up our new DS to where we moved the data */\r
+ /* We must do this before each 32K load cause we use DS */\r
+\r
+\r
+ /* Move first 32K data chunk */\r
+\r
+ MOV BX, 06000h\r
+ MOV DS, BX\r
+ XOR SI, SI\r
+ MOV AX,0000h\r
+ MOV ES,AX\r
+ XOR DI,DI\r
+ MOV CX,8000h\r
+ CLD ;\r
+ REP MOVSB ;\r
+\r
+ /* Move second 32K data chunk */\r
+\r
+ MOV BX, 06800h\r
+ MOV DS, BX\r
+ XOR SI, SI\r
+ MOV AX,0800h\r
+ MOV ES,AX\r
+ XOR DI,DI\r
+ MOV CX,8000h\r
+ CLD ;\r
+ REP MOVSB ;\r
+\r
+ /* Move first 32K code chunk */\r
+\r
+ MOV BX, 07000h\r
+ MOV DS, BX\r
+ XOR SI, SI\r
+ MOV AX,1000h\r
+ MOV ES,AX\r
+ XOR DI,DI\r
+ MOV CX,8000h\r
+ CLD ;\r
+ REP MOVSB ;\r
+\r
+ /* Move second 32K code chunk */\r
+\r
+ MOV BX, 07800h\r
+ MOV DS, BX\r
+ XOR SI, SI\r
+ MOV AX,1800h\r
+ MOV ES,AX\r
+ XOR DI,DI\r
+ MOV CX,8000h\r
+ CLD ;\r
+ REP MOVSB ;\r
+\r
+ /* Move third 32K code chunk */\r
+\r
+ MOV BX, 08000h\r
+ MOV DS, BX\r
+ XOR SI, SI\r
+ MOV AX,2000h\r
+ MOV ES,AX\r
+ XOR DI,DI\r
+ MOV CX,8000h\r
+ CLD ;\r
+ REP MOVSB ;\r
+\r
+\r
+ MOV BX, 0B800h\r
+ MOV ES, BX\r
+ MOV BX, 2\r
+ MOV WORD PTR ES:[BX], 4732h\r
+\r
+ MOV BX, 9800h\r
+ MOV DS, BX\r
+ MOV BX, 0\r
+\r
+ LIDT FWORD PTR DS:[BX+0]\r
+ LGDT FWORD PTR DS:[BX+6]\r
+ MOV EAX,CR0\r
+ OR AL,1\r
+ MOV CR0,EAX\r
+ JMP $+4\r
+ NOP\r
+ NOP\r
+ NOP\r
+ NOP\r
+\r
+ MOV BX, 10h\r
+ MOV DS,BX\r
+ MOV ES,BX\r
+ MOV FS,BX\r
+ MOV GS,BX\r
+ MOV SS,BX\r
+ DB 66h\r
+ DB 67h\r
+ DB 0EAh\r
+ DD 10000h\r
+ DW 8h\r
+ RETN\r
+} /* end of assembler */\r
+\r
+}\r
+\r
+\r
+/**** TurnOnA20 ****************************************************\r
+ This turns on the address line 20 Gate. This must be done before\r
+ we can physically address above 1 Meg. If your machine only\r
+ shows one Meg of RAM and you know there's more, this isn't\r
+ working on your machine... (R&D time..)\r
+********************************************************************/\r
+\r
+void TurnOnA20(void)\r
+{\r
+\r
+asm {\r
+ CLI\r
+ XOR CX,CX\r
+ }\r
+\r
+IBEmm0:\r
+\r
+asm {\r
+ IN AL,64h\r
+ TEST AL,02h\r
+ LOOPNZ IBEmm0\r
+ MOV AL,0D1h\r
+ OUT 64h,AL\r
+ XOR CX,CX\r
+ }\r
+\r
+IBEmm1:\r
+\r
+asm {\r
+ IN AL,64h\r
+ TEST AL,02h\r
+ LOOPNZ IBEmm1\r
+ MOV AL,0DFh\r
+ OUT 60h,AL\r
+ XOR CX,CX\r
+ }\r
+\r
+IBEmm2:\r
+\r
+asm {\r
+ IN AL,64h\r
+ TEST AL,02h\r
+ LOOPNZ IBEmm2\r
+ }\r
+}\r
+\r
+/*** Relocate *********************************************\r
+This is a tricky little piece of code. We are going to\r
+manipulate DOS memory we don't even own.\r
+We are going to set up a temporary little environment\r
+up at A0000h linear (A000:0 segmented) for our\r
+data environment. And then at 90000h (9000:0) for\r
+the loader code itself. This will become CS.\r
+**********************************************************/\r
+\r
+void Relocate(void)\r
+{\r
+char *pdest, *psource, **pnewp;\r
+long *pnewl;\r
+unsigned int *pneww;\r
+int i;\r
+\r
+/* First we move the code up */\r
+\r
+ psource = (void*) &Execute;\r
+\r
+ printf("SEG: %u OFF: %u\r\n", FP_SEG(psource), FP_OFF(psource));\r
+\r
+ pdest = MK_FP(0x9000,0);\r
+ i = 2000; /* more than enough for the code */\r
+ while(i--)\r
+ *pdest++ = *psource++;\r
+\r
+}\r
+\r
+\r
+void CallExecute(void)\r
+\r
+{\r
+/* We push the new code address on the stack then RETF to it.\r
+ We PUSH CS then IP */\r
+\r
+asm {\r
+ MOV AX, 09000h\r
+ PUSH AX\r
+ MOV AX, 0\r
+ PUSH AX\r
+ RETF\r
+}\r
+\r
+}\r
+\r
+/* Borland calls procedures as near indirect\r
+to far data definitions. This means the\r
+bootdrive param will be at EBP+06\r
+*/\r
+\r
+void SetBootDrive(unsigned int bootdrive)\r
+\r
+{\r
+/* get boot drive off stack and place in DL */\r
+\r
+asm {\r
+ XOR EDX, EDX\r
+ MOV DX, WORD PTR [BP+06h]\r
+}\r
+\r
+}\r
+\r
+\r
+/*********************************************************\r
+ This reads and loads the two MMURTL segments into\r
+ six 32K buffers. Two for the data segment, and 4 for\r
+ the code segment. (192K Maximum).\r
+ The MMURTL OS is built as a standard MMURRL run file.\r
+ This is a tag/length/value file as described in the\r
+ DASM documentation (in great detail).\r
+*********************************************************/\r
+\r
+void LoadRunFile(void)\r
+{\r
+int ii;\r
+long i, dret, nobj, nread;\r
+char filetype, junk, *pin;\r
+\r
+ while (1) {\r
+ tag.id = 0;\r
+/* fprintf(out_fh, "\r\nFile LFA: %ld\r\n", ftell(run_fh)); */\r
+ nobj = fread (&tag, 1, 5, run_fh);\r
+ if (nobj) {\r
+/*\r
+ ii = tag.id;\r
+ fprintf(out_fh, "Tag Value (hex): %02X\r\n", ii);\r
+ fprintf(out_fh, "Tag Length (dec): %ld\r\n", tag.len);\r
+*/\r
+ }\r
+ else {\r
+ fprintf(out_fh, "Invalid OS Run file. No End Tag Found.\r\n");\r
+ fclose(run_fh);\r
+ exit(1);\r
+ }\r
+\r
+ switch (tag.id) {\r
+ case IDTAG:\r
+ nobj = fread (&filetype, 1, 1, run_fh);\r
+ if (filetype == 1)\r
+ break;\r
+ else {\r
+ fprintf(out_fh, "File MUST be a run file\r\n");\r
+ exit(1);\r
+ }\r
+ break;\r
+ case SEGTAG:\r
+ fprintf(out_fh, "Segment Sizes:\r\n");\r
+ nobj = fread (&sStack, 1, 4, run_fh);\r
+ if (nobj) nobj = fread (&sCode, 1, 4, run_fh);\r
+ if (nobj) nobj = fread (&sData, 1, 4, run_fh);\r
+ fprintf(out_fh, " Stack %ld\r\n", sStack);\r
+ fprintf(out_fh, " Code %ld\r\n", sCode);\r
+ fprintf(out_fh, " Data %ld\r\n", sData);\r
+ break;\r
+ case DOFFTAG:\r
+ nobj = fread (&offData, 1, 4, run_fh);\r
+ fprintf(out_fh, "Virtual Data Offset: %08lXh\r\n", offData);\r
+ break;\r
+ case COFFTAG:\r
+ nobj = fread (&offCode, 1, 4, run_fh);\r
+ fprintf(out_fh, "Virtual Code Offset: %08lXh\r\n", offCode);\r
+ break;\r
+ case STRTTAG:\r
+ nobj = fread (&pStart, 1, 4, run_fh);\r
+ fprintf(out_fh, "Start Address: %08lXh\r\n", pStart);\r
+ break;\r
+ case CODETAG:\r
+ fprintf(out_fh, "Reading code: %ld bytes\r\n", tag.len);\r
+ CSegSize = tag.len;\r
+\r
+ if (tag.len >= 32768)\r
+ nread = 32768;\r
+ else\r
+ nread = tag.len;\r
+ tag.len -= nread;\r
+ pin = MK_FP(0x7000,0);\r
+ nobj = fread (pin, 1, nread, run_fh);\r
+\r
+ if (tag.len >= 32768)\r
+ nread = 32768;\r
+ else\r
+ nread = tag.len;\r
+ tag.len -= nread;\r
+ pin = MK_FP(0x7800,0);\r
+ nobj = fread (pin, 1, nread, run_fh);\r
+\r
+ if (tag.len >= 32768)\r
+ nread = 32768;\r
+ else\r
+ nread = tag.len;\r
+ tag.len -= nread;\r
+ pin = MK_FP(0x8000,0);\r
+ nobj = fread (pin, 1, nread, run_fh);\r
+ break;\r
+ case DATATAG:\r
+ fprintf(out_fh, "Reading data: %ld bytes\r\n", tag.len);\r
+ DSegSize = tag.len;\r
+ if (tag.len >= 32768)\r
+ nread = 32768;\r
+ else\r
+ nread = tag.len;\r
+ tag.len -= nread;\r
+ pin = MK_FP(0x6000, 0);\r
+ nobj = fread (pin, 1, nread, run_fh);\r
+\r
+ if (tag.len >= 32768)\r
+ nread = 32768;\r
+ else\r
+ nread = tag.len;\r
+ tag.len -= nread;\r
+ pin = MK_FP(0x6800,0);\r
+ nobj = fread (pin, 1, nread, run_fh);\r
+ break;\r
+ case CDFIXTAG:\r
+ case CCFIXTAG:\r
+ case DDFIXTAG:\r
+ case DCFIXTAG:\r
+ while ((tag.len) && (nobj)) {\r
+ nobj = fread (&i, 1, 4, run_fh);\r
+ tag.len-=4;\r
+ }\r
+ break;\r
+ case ENDTAG:\r
+ nobj = fread (&i, 1, 4, run_fh); /* Eat the end tag */\r
+ fclose(run_fh);\r
+ TurnOnA20();\r
+ Relocate(); /* Move the loader code & data */\r
+ SetBootDrive((unsigned int) bootdrive);\r
+ CallExecute();\r
+ break;\r
+ default:\r
+ while ((tag.len--) && (nobj))\r
+ nobj = fread (&junk, 1, 1, run_fh);\r
+ break;\r
+\r
+ } /* end of switch */\r
+ }\r
+}\r
+\r
+/***************************\r
+* Main program -- MMLoader\r
+****************************/\r
+\r
+void main(S16 argc, S8 *argv[])\r
+{\r
+S8 *ptr;\r
+S16 i;\r
+char fName;\r
+\r
+ out_fh = stdout;\r
+ fName = 0;\r
+\r
+ for(i=1; i < argc; ++i) {\r
+ ptr = argv[i];\r
+ if (*ptr == '/') {\r
+ ptr++;\r
+ switch(*ptr) {\r
+ case 'U' :\r
+ case 'u' :\r
+ break;\r
+ case 'D' :\r
+ case 'd' :\r
+ break;\r
+ case 'O' :\r
+ case 'o' :\r
+ fName = 1;\r
+ break;\r
+ default:\r
+ printf("Invalid option/swtich \n");\r
+ if (run_fh)\r
+ fclose(run_fh);\r
+ exit(1);\r
+ break;\r
+ }\r
+ }\r
+ else {\r
+ if (fName)\r
+ strncpy(osname, argv[i], 39);\r
+ }\r
+ }\r
+\r
+ printf("MMURTL DOS Loader Ver 1.0 \r\n");\r
+\r
+ printf("OS Filename specified: %s \r\n", osname);\r
+\r
+ run_fh = fopen(osname, "rb");\r
+ if (!run_fh) {\r
+ printf("Can't open: %s \r\n", osname);\r
+ exit(1);\r
+ }\r
+\r
+ if (osname[1] == ':')\r
+ {\r
+ bootdrive = osname[0] - 0x41;\r
+ if (bootdrive >= 0x20)\r
+ bootdrive -= 0x20; /* may be lower case */\r
+ }\r
+ else\r
+ bootdrive = getdisk();\r
+\r
+ ptest = malloc(100); /* 32K */\r
+ printf("First MEM = SEG: %u OFF: %u\r\n", FP_SEG(ptest), FP_OFF(ptest));\r
+\r
+ LoadRunFile();\r
+\r
+}\r
+\r
+/*********************** End of Module *****************/\r