--- /dev/null
+; MMURTL Operating System Source Code\r
+; Copyright 1991,1992,1993,1994 Richard A. Burgess\r
+; ALL RIGHTS RESERVED\r
+; Version 1.0\r
+;\r
+; This is the main Data/Code file for the MMURTL OS.\r
+; It contains important structures and initialization code\r
+; including the first instructions executed after a boot!\r
+; This should follow the IDT, GDT, PDR & Public call table in memory.\r
+\r
+.DATA\r
+.INCLUDE MOSEDF.INC\r
+.INCLUDE JOB.INC\r
+.INCLUDE TSS.INC\r
+\r
+.ALIGN DWORD\r
+;=============================================================================\r
+; Kernel Structures - See MOSEDF.INC for structure details.\r
+;=============================================================================\r
+\r
+PUBLIC MonTSS DB sTSS dup (0) ; Initial TSS for OS/Monitor\r
+PUBLIC DbgTSS DB sTSS dup (0) ; Initial TSS for Debugger\r
+PUBLIC pFreeTSS DD NIL ; Pointer to Free List of Task State Segs\r
+PUBLIC pDynTSSs DD 0 ; ptr to alloced mem for dynamic TSSs\r
+PUBLIC _nTSSLeft DD nTSS-2 ; For stats (less static TSSs)\r
+\r
+;------------------\r
+\r
+PUBLIC rgLBs DB (nLB*sLINKBLOCK) dup (0) ; pool of LBs\r
+PUBLIC pFreeLB DD NIL ; Ptr to Free List of Link Block\r
+PUBLIC _nLBLeft DD nLB ; For Monitor stats\r
+\r
+;------------------\r
+; The RUN Queue for "Ready to Run" tasks\r
+\r
+PUBLIC RdyQ DB (sQUEUE*nPRI) dup (0) ; Priority Based Ready Queue\r
+\r
+;------------------\r
+; Two static Job Control Blocks (JCBs) to get us kick-started.\r
+; The rest of them are in allocated memory\r
+\r
+PUBLIC MonJCB DB sJCB dup (0) ; Monitor JCB\r
+PUBLIC DbgJCB DB sJCB dup (0) ; Debugger JCB\r
+\r
+;------------------\r
+\r
+PUBLIC GDTLimit DW 0000h ;Global Descriptor Table Limit\r
+PUBLIC GDTBase DD 00000000h ;base\r
+PUBLIC IDTLimit DW 0000h ;Interrupt Descriptor Table Limit\r
+PUBLIC IDTBase DD 00000000h ;base\r
+\r
+;------------------\r
+PUBLIC rgSVC DB (sSVC*nSVC) dup (0) ; Setup an array of Service Descriptors\r
+\r
+;------------------\r
+;Exchanges take up a fair amount of memory. In order to use certain kernel\r
+;primitives, we need exchanges before they are allocated dynamically.\r
+;We have an array of 3 exchanges set aside for this purpose. After\r
+;the dynamic array is allocated an initialized, we copy these into\r
+;the first three dynamic exchanges. These static exchanges are used\r
+;by the Monitor TSS, Debugger TSS, and memory Management.\r
+\r
+PUBLIC nExch DD 3 ; This will be changed after allocation\r
+ ; of dynamic exchanges.\r
+rgExchTmp DB (sEXCH * 3) dup (0) ; Setup three static temporary Exchanges\r
+ ; for Monitor and Debugger TSSs\r
+ ; These are moved to a dynamic Exch Array\r
+ ; as soon as it's allocated.\r
+PUBLIC prgExch DD OFFSET rgExchTmp ; Pointer to current array of Exchanges.\r
+PUBLIC pExchTmp DD 0 ; Pointer to dynamic array Exchanges.\r
+PUBLIC _nEXCHLeft DD nDynEXCH ; For Monitor stats\r
+\r
+;-------------------\r
+;Scheduling management variables\r
+\r
+PUBLIC TSS DD 00000000h ; Used for jumping to next task\r
+PUBLIC TSS_Sel DW 0000h ; " "\r
+\r
+.ALIGN DWORD\r
+\r
+PUBLIC pRunTSS DD NIL ; Pointer to the Running TSS\r
+PUBLIC SwitchTick DD 0 ; Tick of last task switch\r
+PUBLIC dfHalted DD 0 ; nonzero if processor was halted\r
+\r
+PUBLIC _nSwitches DD 0 ; # of switches for statistics\r
+PUBLIC _nSlices DD 0 ; # of sliced switches for stats\r
+PUBLIC _nReady DD 0 ; # of task Ready to Run for stats\r
+PUBLIC _nHalts DD 0 ; # of times CPU halted for stats\r
+\r
+;THESE ARE THE INITIAL STACKS FOR THE OS Monitor AND Debugger\r
+\r
+OSStack DD 0FFh DUP (00000000h) ;1K OS Monitor Stack\r
+OSStackTop DD 00000000h\r
+OSStackSize EQU OSStackTop-OSStack\r
+\r
+Stack1 DD 0FFh DUP (00000000h) ;1K Debugger Stack\r
+Stack1Top DD 00000000h\r
+Stack1Size EQU Stack1Top-Stack1\r
+\r
+;-----------------\r
+\r
+rgNewJob DB 'New Job' ;Placed in New JCBs\r
+cbNewJob EQU 7\r
+\r
+rgOSJob DB 'MMOS Monitor' ;Placed in first JCB\r
+cbOSJob EQU 12\r
+\r
+rgDbgJob DB 'Debugger ' ;Name for Job\r
+cbDbgJob DD 12 ;Size of Job Name\r
+\r
+PUBLIC _BootDrive DD 00 ;Source drive of the boot\r
+;=============================================================================\r
+;=============================================================================\r
+; This begins the OS Code Segment\r
+.CODE\r
+;\r
+.VIRTUAL 10000h ;64K boundry. This lets the assembler know\r
+ ;that this is the address where we execute\r
+;\r
+; BEGIN OS INITIALIZATION CODE\r
+;\r
+; This code is used to initialize the permanent OS structures\r
+; and calls procedures that initialize dynamic structures too.\r
+;\r
+; "Will Robinson, WARNING, WARNING!! Dr. Smith is approaching!!!"\r
+; BEWARE ON INITIALIZATION. The kernel structures and their\r
+; initialization routines are so interdependent, you must pay\r
+; close attention before you change the order of ANYTHING.\r
+; (Anything before we jump to the monitor code that is)\r
+\r
+EXTRN InitCallGates NEAR\r
+EXTRN InitOSPublics NEAR\r
+EXTRN _Monitor NEAR\r
+EXTRN InitIDT NEAR\r
+EXTRN InitFreeLB NEAR\r
+EXTRN InitDMA NEAR\r
+EXTRN Set8259 NEAR\r
+EXTRN InitKBD NEAR\r
+EXTRN AddTSSDesc NEAR\r
+EXTRN InitMemMgmt NEAR\r
+EXTRN InitNewJCB NEAR\r
+EXTRN InitVideo NEAR\r
+EXTRN InitFreeTSS NEAR\r
+EXTRN InitDynamicJCBs NEAR\r
+EXTRN InitDynamicRQBs NEAR\r
+EXTRN DbgTask NEAR\r
+;=============================================================================\r
+; Set up the initial Stack Pointer (SS = DS already).\r
+; This is the first code we execute after the loader\r
+; code throws us into protected mode. It's a FAR jump\r
+; from that code...\r
+;=============================================================================\r
+\r
+.START\r
+PUBLIC OSInitBegin:\r
+ LEA EAX,OSStackTop ; Setup initial OS Stack\r
+ MOV ESP,EAX ; FIRST THING IN OS CODE.\r
+ MOV _BootDrive, EDX ; Left there from BootSector Code\r
+\r
+;=============================================================================\r
+; Set up OS Common Public for IDT and GDT Base and Limits - SECOND THING IN OS\r
+;=============================================================================\r
+\r
+ SGDT FWORD PTR GDTLimit ;A formality - we know where they are!\r
+ SIDT FWORD PTR IDTLimit ;\r
+\r
+;=============================================================================\r
+; Setup Operating System Structures and motherboard hardware\r
+; THIS IS RIGHT AFTER WE GET A STACK.\r
+; YOU CAN'T ALLOCATE ANY OS RESOURCES UNTIL THIS CODE EXECUTES!!!\r
+;=============================================================================\r
+\r
+ CALL InitCallGates ; Sets up all call gates as DUMMYs\r
+ ; except AddCallGate which\r
+ must be made valid first!\r
+\r
+ CALL InitOSPublics ; Sets up OS PUBLIC call gates\r
+\r
+ CALL InitIDT ;Sets up default Interrupt table\r
+ ;NOTE: This uses CallGates!\r
+\r
+ MOV ECX,nLB ; count of Link Blocks\r
+ MOV EDX,sLinkBlock ; EDX is size of a Link Block\r
+ CALL InitFreeLB ; Init the array of Link Blocks\r
+\r
+ CALL InitDMA ; Sets up DMA with defaults\r
+\r
+ CALL Set8259 ; Set up 8259s for ints (before KBD)\r
+\r
+ CALL InitKBD ; Initialize the Kbd hardware\r
+\r
+ PUSH 0 ; Highest IRQ number (all IRQs)\r
+ CALL FWORD PTR _EndOfIRQ ; Tell em to work\r
+\r
+ ;Set time counter divisor to 11938 - 10ms ticks\r
+ ;Freq in is 1.193182 Mhz/11932 = 100 per second\r
+ ;or 1 every 10 ms. (2E9Ch = 11932 decimal)\r
+\r
+ MOV AL,9Ch ; Makes the timer Tick (lo)\r
+ OUT 40h,AL ; 10 ms apart by setting\r
+ MOV AL,02Eh ; clock divisior to (hi byte)\r
+ OUT 40h,AL ; 11,932\r
+\r
+ STI ; We are ready to GO (for now)\r
+\r
+;=============================================================================\r
+; The following code finishes the initialization procedures BEFORE the\r
+; OS goes into paged memory mode.\r
+; We set up an initial Task by filling a static TSS, creating and loading\r
+; a descriptor entry for it in the GDT and we do the same for the debugger.\r
+;=============================================================================\r
+\r
+ ; Make the default TSS for the CPU to switch from a valid one.\r
+ ; This TSS is a valid TSS after the first task switch.\r
+ ; IMPORTANT - Allocate Exch and InitMemMgmt calls depend on\r
+ ; pRunTSS being valid. They can not be called before\r
+ ; this next block of code!!! Note that this TSS does NOT\r
+ ; get placed in the linked list with the rest of the TSSs.\r
+ ; It will never be free. We also have to manaully make it's\r
+ ; entry in the GDT.\r
+\r
+ ;The following code section builds a descriptor entry for\r
+ ;the initial TSS (for Montitor program) and places it into the GDT\r
+\r
+ MOV EAX, sTSS ; Limit of TSS (TSS + SOFTSTATE)\r
+ MOV EBX, 0089h ; G(0),AV(0),LIM(0),P(1),DPL(0),B(0)\r
+ MOV EDX, OFFSET MonTSS ; Address of TSS\r
+ MOV EDI, OFFSET rgTSSDesc ; Address of GDT entry to fill\r
+ CALL AddTSSDesc\r
+\r
+ ;Now that we have valid Descriptor, we set up the TSS itself\r
+ ;and Load Task Register with the descriptor (selector)\r
+ ;Note that none of the TSS register values need to be filled in\r
+ ;because they will be filled by the processor on the first\r
+ ;task switch.\r
+\r
+ MOV EBX, OFFSET MonTSS ; Get ptr to initial TSS in EBX\r
+ MOV pRunTSS,EBX ; this IS our task now!!!\r
+ MOV EAX, OFFSET rgTSSDesc ; ptr to initial TSS descriptor\r
+ SUB EAX, OFFSET GDT ; Sub offset of GDT Base to get Sel of TSS\r
+ MOV WORD PTR [EBX+TSS_IOBitBase], 0FFh; I/O Permission\r
+ MOV [EBX+Tid],AX ; Store TSS Selector in TSS (Task ID)\r
+ LTR WORD PTR [EBX+Tid] ; Setup the Task Register\r
+ MOV BYTE PTR [EBX+Priority], 25 ; Priority 25 (monitor is another APP)\r
+ MOV DWORD PTR [EBX+TSS_CR3], OFFSET PDir1 ;Physical address of PDir1\r
+ MOV WORD PTR [EBX+TSSNum], 1 ;Number of first TSS (Duh)\r
+\r
+ ;Set up Job Control Block for Monitor (always Job 1)\r
+ ;JOB 0 is not allowed. First JCB IS job 1!\r
+\r
+ MOV EAX, OFFSET MonJCB ;\r
+ MOV DWORD PTR [EAX+JobNum], 1 ;Number the JCB\r
+ MOV EBX, OFFSET MonTSS ;Must put ptr to JCB in TSS\r
+ MOV [EBX+TSS_pJCB], EAX ;pJCB into MonTSS\r
+ MOV EBX, OFFSET PDir1 ;Page Directory\r
+ MOV ESI, OFFSET rgOSJob ;Job Name\r
+ MOV ECX, cbOSJob ;Size of Name\r
+ XOR EDX, EDX ;NO pVirtVid yet (set up later in init)\r
+ CALL InitNewJCB\r
+\r
+\r
+; IMPORTANT - You can't call AllocExch before pRunTSS is VALID!\r
+\r
+; THIS IS THE FIRST POINT AllocExh is valid\r
+\r
+ MOV EAX, pRunTSS\r
+ ADD EAX, TSS_Exch ;Alloc exch for initial (first) TSS\r
+ PUSH EAX\r
+ CALL FWORD PTR _AllocExch\r
+\r
+\r
+\r
+;=============================================================================\r
+;\r
+; Set up DEBUGGER Task and Job\r
+; The debugger is set up as another job with its one task.\r
+; The debugger must not be called (Int03) until this code executes,\r
+; AND the video is initialized.\r
+; We can't use NewTask because the debugger operates independent of\r
+; the kernel until it is called (INT 03 or Exception)\r
+;\r
+ ;The following code section builds a descriptor entry for\r
+ ;the initial TSS (for Debugger) and places it into the GDT\r
+\r
+ MOV EAX, sTSS ; Limit of TSS (TSS + SOFTSTATE)\r
+ MOV EBX, 0089h ; G(0),AV(0),LIM(0),P(1),DPL(0),B(0)\r
+ MOV EDX, OFFSET DbgTSS ; Address of Debugger TSS\r
+ MOV EDI, OFFSET rgTSSDesc+8 ; Address of GDT entry to fill in\r
+ CALL AddTSSDesc\r
+\r
+ ;Now that we have valid Descriptor, we set up the TSS itself\r
+\r
+ MOV EAX, OFFSET rgTSSDesc+8 ; ptr to second TSS descriptor\r
+ SUB EAX, OFFSET GDT ; Sub offset of GDT Base to get Sel of TSS\r
+ MOV EBX, OFFSET DbgTSS ; Get ptr to initial TSS in EBX\r
+ MOV WORD PTR [EBX+TSS_IOBitBase], 0FFh; I/O Permission\r
+ MOV [EBX+Tid],AX ; Store TSS Selector in TSS (Task ID)\r
+ MOV BYTE PTR [EBX+Priority], 1 ; Debugger is HIGH Priority\r
+ MOV DWORD PTR [EBX+TSS_CR3], OFFSET PDir1 ;Physical address of PDir1\r
+ MOV EDX, OFFSET DbgTask\r
+ MOV [EBX+TSS_EIP],EDX\r
+ MOV WORD PTR [EBX+TSS_CS],OSCodeSel ; Put OSCodeSel in the TSS\r
+ MOV WORD PTR [EBX+TSS_DS],DataSel ; Put DataSel in the TSS\r
+ MOV WORD PTR [EBX+TSS_ES],DataSel ;\r
+ MOV WORD PTR [EBX+TSS_FS],DataSel ;\r
+ MOV WORD PTR [EBX+TSS_GS],DataSel ;\r
+ MOV WORD PTR [EBX+TSS_SS],DataSel ;\r
+ MOV WORD PTR [EBX+TSS_SS0],DataSel ;\r
+ MOV EAX, OFFSET Stack1Top\r
+ MOV DWORD PTR [EBX+TSS_ESP],EAX ; A 1K Stack in the Dbg TSS\r
+ MOV DWORD PTR [EBX+TSS_ESP0],EAX ;\r
+ MOV DWORD PTR [EBX+TSS_EFlags],00000202h ; Load the Flags Register\r
+ MOV WORD PTR [EBX+TSSNum], 2 ; Number of Dubegger TSS\r
+\r
+ ;Set up Job Control Block for Debugger\r
+ ;JOB 0 is not allowed. First JCB IS job 1, debugger is always 2\r
+\r
+ MOV EAX, OFFSET DbgJCB\r
+ MOV DWORD PTR [EAX+JobNum], 2 ;Number the JCB\r
+ MOV [EBX+TSS_pJCB], EAX ;EBX still points to DbgTSS\r
+ MOV EBX, OFFSET PDir1 ;Page Directory (OS PD to start)\r
+ MOV ESI, OFFSET rgDbgJob ;Name\r
+ MOV ECX, cbDbgJob ;size of name\r
+ MOV EDX, 1 ;Debugger gets video 1\r
+ CALL InitNewJCB\r
+\r
+ ;Now allocate the default exchange for Debugger\r
+\r
+ MOV EAX, OFFSET DbgTSS\r
+ ADD EAX, TSS_Exch ;Alloc exch for Debugger TSS\r
+ PUSH EAX\r
+ CALL FWORD PTR _AllocExch\r
+\r
+;================================================================\r
+; ALSO NOTE: The InitMemMgmt call enables PAGING! Physical addresses\r
+; will not necessarily match Linear addresses beyond this point.\r
+; Pay attention!\r
+;\r
+\r
+ CALL InitMemMgmt ;InitMemMgmt allocates an exch so\r
+ ;this the first point it can be called.\r
+ ;It also calls SEND!\r
+\r
+\r
+;================================================================\r
+; FIRST POINT memory management calls are valid\r
+;================================================================\r
+\r
+ ;Now we will allocate two virtual video screens (1 Page each)\r
+ ;for the Monitor and Debugger and place them in the JCBs\r
+ ;Also we set pVidMem for Monitor to VGATextBase address\r
+ ;cause it has the active video by default\r
+\r
+ PUSH 1 ; 1 page\r
+ MOV EAX, OFFSET MonJCB ; Ptr to Monitor JCB\r
+ ADD EAX, pVirtVid ; Offset in JCB to pVirtVid\r
+ PUSH EAX\r
+ CALL FWORD PTR _AllocOSPage ; Get 'em!\r
+ MOV EAX, OFFSET MonJCB ; Make VirtVid Active for Monitor\r
+ MOV DWORD PTR [EAX+pVidMem], VGATextBase\r
+\r
+\r
+ PUSH 1 ; 1 page\r
+ MOV EAX, OFFSET DbgJCB ;\r
+ ADD EAX, pVirtVid\r
+ PUSH EAX\r
+ CALL FWORD PTR _AllocOSPage ; Get 'em!\r
+ MOV EAX, OFFSET DbgJCB ;\r
+ MOV EBX, [EAX+pVirtVid]\r
+ MOV [EAX+pVidMem], EBX ; Video NOT active for Debugger\r
+\r
+ CALL InitVideo ;Set Text Screen 0\r
+\r
+\r
+; MOV EAX,30423042h ;BB on CYAN - So we know we are here!\r
+; MOV DS:VGATextBase+00h,EAX\r
+\r
+;================================================================\r
+; FIRST POINT video calls are valid\r
+; FIRST POINT Debugger is working (only because it needs the video)\r
+\r
+; At this point we can allocate pages for dynamic structures and\r
+; initialize them.\r
+;================================================================\r
+\r
+; Allocate 8 pages (32768 bytes) for 64 Task State Segments (structures).\r
+; Then call InitFreeTSS (which fills them all in with default values)\r
+\r
+ PUSH 8 ; 8 pages for 64 TSSs (32768 bytes)\r
+ MOV EAX, OFFSET pDynTSSs ;\r
+ PUSH EAX\r
+ CALL FWORD PTR _AllocOSPage ; Get 'em!\r
+\r
+ XOR EAX, EAX ; Clear allocated memory for TSSs\r
+ MOV ECX, 8192 ; (512 * 64 = 32768 = 8192 * 4)\r
+ MOV EDI, pDynTSSs ; where to store 0s\r
+ REP STOSD ; Do it\r
+\r
+ MOV EAX, pDynTSSs\r
+ MOV ECX, nTSS-2 ; count of dynamic TSSs (- OS & Dbgr TSS)\r
+ CALL InitFreeTSS ; Init the array of Process Control Blocks\r
+\r
+ CALL InitDynamicJCBs\r
+ CALL InitDynamicRQBs\r
+\r
+;================================================================\r
+; Allocate 1 page (4096 bytes) for 256 Exchanges (16*256=4096).\r
+; Exchanges are 16 bytes each. Then zero the memory which has\r
+; the effect of initializing them because all fields in an Exch\r
+; are zero if not allocated.\r
+\r
+ PUSH 1 ; 1 pages for 256 Exchs (4096 bytes)\r
+ MOV EAX, OFFSET pExchTmp ; Returns ptr to allocated mem in pJCBs\r
+ PUSH EAX ;\r
+ CALL FWORD PTR _AllocOSPage ; Get it!\r
+\r
+ XOR EAX, EAX ; Clear allocated memory\r
+ MOV ECX, 1024 ; (4*1024=4096)\r
+ MOV EDI, pExchTmp ; where to store 0s\r
+ REP STOSD ; Store EAX in 1024 locations\r
+\r
+ ;Now we move the contents of the 3 static exchanges\r
+ ;into the dynamic array. This is 60 bytes for 3\r
+ ;exchanges.\r
+\r
+ MOV ESI, prgExch ; Source (static ones)\r
+ MOV EDI, pExchTmp ; Destination (dynamic ones)\r
+ MOV ECX, 12 ; 12 DWords (3 Exchanges)\r
+ REP MOVSD ; Move 'em!\r
+ MOV EAX, pExchTmp ; The new ones\r
+ MOV prgExch, EAX ; prgExch now points to new ones\r
+ MOV nExch, nDynEXCH ; 256 to use (-3 already in use)\r
+\r
+;================================================================\r
+\r
+ PUSH 7 ; Look at LPT IRQ\r
+ CALL FWORD PTR _UnMAskIRQ ;\r
+\r
+ CALL _Monitor ;Head for the Monitor!!\r
+\r
+ ;The Monitor call never comes back (it better not...)\r
+\r
+ HLT ;Well, you never know (I AM human)\r
+\r
+\r
+;\r
+\r
+;================== End of Main.asm ==============================\r