]> pd.if.org Git - mmurtl/commitdiff
autocommit for file dated 1994-12-28 22:42:38
authorRichard Burgess <>
Wed, 28 Dec 1994 22:42:38 +0000 (22:42 +0000)
committerNathan Wagner <nw@hydaspes.if.org>
Mon, 17 Oct 2016 14:03:47 +0000 (14:03 +0000)
ossource/memcode.asm [new file with mode: 0644]

diff --git a/ossource/memcode.asm b/ossource/memcode.asm
new file mode 100644 (file)
index 0000000..2c20c6e
--- /dev/null
@@ -0,0 +1,1668 @@
+;=============================================================================\r
+;  MMURTL Operating System Source Code\r
+;  Copyright 1991,1992,1993,1994 Richard A. Burgess\r
+;  ALL RIGHTS RESERVED     Version 1.0\r
+;\r
+.DATA\r
+\r
+.INCLUDE MOSEDF.INC\r
+.INCLUDE TSS.INC\r
+.INCLUDE JOB.INC\r
+.ALIGN DWORD\r
+;\r
+;=============================================================================\r
+; Memory Management Data\r
+;=============================================================================\r
+\r
+PUBLIC _nPagesFree             DD 0                    ;Number of free physical pages left\r
+PUBLIC _oMemMax                        DD 000FFFFFh    ;Default to 1 MB\r
+                                                                       ;Page Allocation Map\r
+PUBLIC rgPAM   DB 2048 DUP (0)         ;1 bit/4Kbytes - 2048 bytes = 64Mb\r
+PUBLIC sPAM            DD 32                           ;Deafult is 32 (1Mb)\r
+sPAMmax                        EQU 2048                        ;Max is 2048 (64Mb)\r
+\r
+MemExch                        DD 00000000h            ;Semaphore exchange for Mem Mgmt\r
+pNextPT                        DD 00000000h            ;We alloc the "next" page table\r
+                                                                       ;in advance (MANDATORY)\r
+;Some equates (MASKS) for PT Management\r
+\r
+;             20 Bit Address      AVL00DA00UWP\r
+MEMALIAS EQU  00000000000000000000100000000111b        ;User Writable\r
+MEMUSERD EQU  00000000000000000000000000000111b        ;User Writable (Data)\r
+MEMUSERC EQU  00000000000000000000000000000101b        ;User Readable (Code)\r
+MEMSYS   EQU  00000000000000000000000000000101b        ;System Read/Write\r
+ALIASBIT EQU  00000000000000000000100000000000b\r
+PRSNTBIT EQU  00000000000000000000000000000001b\r
+;\r
+;=============================================================================\r
+; Memory Management Code\r
+;=============================================================================\r
+\r
+.CODE\r
+\r
+;\r
+; MEMORY Management Code\r
+; OS Internal support calls (near) are at top of this file.\r
+; OS PUBLIC calls are at the bottom\r
+;\r
+; The following internals deal with PHYSICAL memory and the Page\r
+; Allocation Map (PAM).\r
+;\r
+;   InitMemMgmt  - Called to set up all OS memory management functions\r
+;\r
+;   FindHiPage   - Finds first available physical page from top of memory\r
+;                                 down and sets it     allocated then returns the\r
+;                                 Physical address of the page.\r
+;      FindLoPage   - Finds first available physical page from BOTTOM of memory\r
+;                                 and sets it allocated then returns the\r
+;                                 Physical address of the page. This is for DMA.\r
+;      MarkPage         - Given a physical page address, this SETS the PAM bit\r
+;                                 to allocate it.\r
+;      UnMarkPage   - Given the physical page address, this RESETS the PAM bit\r
+;                                 to deallocate it.\r
+;\r
+; The following internals deal with LINEAR memory and PTs.\r
+;\r
+;      LinToPhy    - Converts a linear to a physical address (OS or APP).\r
+;                 Also provides lin address to PTE in question.\r
+;                                IN:   EBX is Linear address\r
+;                                      EAX is Job Number (for PD)\r
+;                                OUT:  EAX is Physical address\r
+;                                              ESI is Linear address of PTE for EBX Lin Add\r
+;      FindRun     - Finds a run of FREE contiguous PTEs\r
+;                                IN:   EAX = 0 for OS run, or 256 for User run\r
+;                                              EBX = nPages\r
+;                                OUT:  EAX = Linear Address of run (0 if can't find)\r
+;                                              EBX = nPages (same as in)\r
+;      AddRun      - Creates one or more PTEs (first owner)\r
+;                                IN:   EAX = Linear address of page(s) to add.\r
+;                                              EBX = count of pages\r
+;                                OUT:  EAX = 0 if OK, else Error\r
+;                                              EBX = count of pages (same as in)\r
+;      AddDMARun   - Creates one or more PTEs (first owner)\r
+;                                      (Same as AddRUn except adds LO pages)\r
+;                                IN:   EAX = Linear address of page(s) to add.\r
+;                                              EBX = count of pages\r
+;                                OUT:  EAX = 0 if OK, else Error\r
+;                                              EBX = count of pages (same as in)\r
+;\r
+;      AliasRun    - Creates one or more ALIAS PTEs\r
+;\r
+;      AddUserPT   - Allocates physical memory and adds new PT to user PD\r
+;\r
+;      AddOSPT     - Allocates physical memory and adds new PT to ALL PDs\r
+;\r
+;========================================================================\r
+\r
+;IN:   Nothing\r
+;OUT:  Nothing (except that you can use memory management routines now!)\r
+;USED: ALL REGISTERS ARE USED.\r
+;\r
+; This section finds out how much memory we have (in MBs) by writing\r
+; to the highest DWord in each meg until it fails a readback test.\r
+; It sets nPages Free after finding out just how much we have.\r
+; We assume 1MB to start (which means we start at 2Mb (1FFFFC).\r
+; It places the highest addressable offset in GLOBAL oMemMax.\r
+; We also calculate the number of pages of physical memory this\r
+; is and store it in the GLOBAL nPagesFree.\r
+\r
+PUBLIC InitMemMgmt:\r
+               MOV _nPagesFree, 256    ;1 Mb of pages = 256\r
+               MOV EAX,1FFFFCh         ;top of 2 megs (for DWORD)\r
+               XOR EBX,EBX                             ;\r
+               MOV ECX,06D72746CH      ;'mrtl' test string value for memory\r
+MEMLoop:\r
+               MOV DWORD PTR [EAX],0h  ;Set it to zero intially\r
+               MOV DWORD PTR [EAX],ECX ;Move in test string\r
+               MOV EBX,DWORD PTR [EAX] ;Read test string into EBX\r
+               CMP EBX,ECX                             ;See if we got it back OK\r
+               JNE MemLoopEnd                  ;NO!\r
+               ADD EAX,3                               ;Yes, oMemMax must be last byte\r
+               MOV _oMemMax,EAX                ;Set oMemMax\r
+               SUB EAX,3                               ;Make it the last DWord again\r
+               ADD EAX,100000h                 ;Next Meg\r
+               ADD _nPagesFree, 256    ;Another megs worth of pages\r
+               ADD sPAM, 32                    ;Increase PAM by another meg\r
+               CMP EAX,3FFFFFCh        ;Are we above 64 megs\r
+               JAE MemLoopEnd                  ;Yes!\r
+               XOR EBX,EBX                             ;Zero out for next meg test\r
+               JMP MemLoop\r
+MemLoopEnd:\r
+\r
+; Page Allocation Map is now sized and ZEROed\r
+; Now we must fill in bits used by OS which was just loaded and\r
+; the Video RAM and Boot ROM (neither of which we consider free).\r
+; This also fills out each of the Page Table Entries (PTEs) for the\r
+; initial OS code and data.  Note that linear address match physical\r
+; address for the initial OS data and code (its the law!)\r
+\r
+; This first part MARKS the OS code and data pages as used\r
+; and makes PTEs.\r
+;\r
+               MOV EDX, OFFSET pTbl1           ;EDX points to OS Page Table 1\r
+               XOR EAX, EAX                            ;Point to 1st physical/linear page (0)\r
+IMM001:\r
+               MOV [EDX], EAX                          ;Make Page Table Entry\r
+               AND DWORD PTR [EDX], 0FFFFF000h         ;Leave upper 20 Bits\r
+               OR      DWORD PTR [EDX], 0001h                  ;Supervisor, Present\r
+               MOV EBX, EAX\r
+               CALL MarkPage                           ;Marks page in PAM\r
+               ADD EDX, 4                                      ;Next table entry\r
+               ADD EAX, 4096\r
+               CMP EAX, 30000h                         ;Reserve 192K for OS (for now)\r
+               JAE SHORT IMM002\r
+               JMP SHORT IMM001                        ;Go for more\r
+\r
+; Now we fill in PAM and PTEs for Video and ROM slots.\r
+; This covers A0000 thru 0FFFFFh (upper 384K of first Meg).\r
+; Right now we just mark everything from A0000 to FFFFF as used.\r
+; This routine could be expanded to search through the ROM pages of\r
+; ISA memory (C0000 -FFFFF) finding the unaccessable ones and marking\r
+; them as allocated in the PAM. Several chip sets on the market allow\r
+; you to set ROM areas as useable RAM (such as the 82C30 C&T).  But we\r
+; can't be sure everyone can do it, nor can we provide instructions\r
+; to everyone.\r
+\r
+IMM002:\r
+               MOV EAX, 0A0000h                        ;Points to 128K Video & 256K ROM area\r
+               MOV EBX, EAX                            ;\r
+               SHR EBX, 10                                     ;Make it index (SHR 12, SHL 2)\r
+               MOV EDX, OFFSET pTbl1           ;EDX pts to Page Table\r
+               ADD EDX, EBX\r
+IMM003:\r
+               MOV [EDX], EAX                                  ;Make Page Table Entry\r
+               AND DWORD PTR [EDX], 0FFFFF000h ;Leave upper 20 Bits\r
+               OR      DWORD PTR [EDX], 0101b          ;Mark it "User" "ReadOnly" & "Present"\r
+               MOV EBX, EAX                            ;Setup for MarkPage call\r
+               CALL MarkPage                           ;Mark it used in the PAM\r
+               ADD EDX, 4                                      ;Next PTE entry\r
+               ADD EAX, 4096                           ;Next page please\r
+               CMP EAX, 100000h                        ;1Mb yet?\r
+               JAE IMM004                                      ;Yes\r
+               JMP SHORT IMM003                        ;No, go back for more\r
+\r
+; Initial Page Directory and the Page Table are static.\r
+; Now we can go into PAGED Memory mode.  This is done by loading\r
+; CR3 with the physcial address of the Page Directory, then reading\r
+; CR0, ANDing it with 8000000h and then writing it again.\r
+; After the MOV CR0 we must JMP to clear the prefetch queue of\r
+; any bogus physical addresses.\r
+\r
+IMM004:\r
+               MOV EAX, OFFSET PDir1  ;Physical address of OS page directory\r
+               MOV CR3, EAX            ;Store in Control Reg 3\r
+               MOV EAX, CR0            ;Get Control Reg 0\r
+               OR  EAX, 80000000h      ;Set paging bit ON\r
+               MOV CR0, EAX            ;Store Control Reg 0\r
+               JMP IM0005                      ;Clear prefetch queue\r
+IM0005:\r
+;\r
+; Now we allocate an Exchange that the OS uses for a semaphore\r
+; use to prevent reentrant use of the any of the critical\r
+; memory managment functions.\r
+;\r
+               LEA EAX, MemExch                ;Alloc Semaphore Exch for Memory calls\r
+               PUSH EAX\r
+               CALL FWORD PTR _AllocExch\r
+\r
+               PUSH MemExch                            ;Send a dummy message to pick up\r
+               PUSH 0FFFFFFF1h\r
+               PUSH 0FFFFFFF1h\r
+               CALL FWORD PTR _SendMsg\r
+\r
+\r
+               ;We must allocate a Page Table to be used when one\r
+               ;must be added to a PD (User or OS).  This must be\r
+               ;done in advance of finding out we need one because\r
+               ;we may not have a linear address to access it in\r
+               ;if the current PTs are all used up!  A tad complicated\r
+               ;I'm afraid...\r
+\r
+               PUSH 1                                          ; 1 page for Next Page Table\r
+               MOV EAX, OFFSET pNextPT         ;\r
+               PUSH EAX\r
+               CALL FWORD PTR _AllocOSPage     ; Get 'em!\r
+\r
+               RETN                            ;Done initializing memory managment\r
+;\r
+\r
+;========================================================================\r
+FindHiPage:\r
+; This finds the first unused physical page in memory from the TOP down\r
+; and returns the physical address of it to the caller.\r
+; It also MARKS the page as used (assuming that we will allocate it).\r
+; Of course this means if we call FindHiPage and don't use it we\r
+; must call UnMarkPage to release it.\r
+; This reduces nPagesFree by one.\r
+;\r
+;IN  :  Nothing\r
+;OUT :  EBX is the physical address of the new page, or 0 if error\r
+;USED: EBX, Flags\r
+\r
+               PUSH EAX\r
+               PUSH ECX\r
+               PUSH EDX\r
+               MOV ECX, OFFSET rgPAM           ;Page Allocation Map\r
+               MOV EAX, sPAM                           ;Where we are in PAM\r
+               DEC EAX                                         ;EAX+ECX will be offset into PAM\r
+FHP1:\r
+               CMP BYTE PTR [ECX+EAX],0FFh             ;All 8 pages used?\r
+               JNE FHP2                                        ;No\r
+               CMP EAX, 0                                      ;Are we at Bottom of PAM?\r
+               JE  FHPn                                        ;no memory left...\r
+               DEC EAX                                         ;Another Byte lower\r
+               JMP SHORT FHP1                          ;Back for next byte\r
+FHP2:\r
+               MOV EBX, 7                                      ;\r
+               XOR EDX, EDX\r
+               MOV DL, BYTE PTR [ECX+EAX]      ;Get the byte with a whole in it...\r
+FHP3:\r
+               BT  EDX, EBX                            ;Test bits\r
+               JNC FHPf                                        ;FOUND ONE! (goto found)\r
+               CMP EBX, 0                                      ;At the bottom of the Byte?\r
+               JE  FHPn                                        ;Error (BAD CPU???)\r
+               DEC EBX                                         ;Next bit\r
+               JMP FHP3\r
+FHPf:\r
+               BTS EDX, EBX                            ;Set the bit indexed by EBX\r
+               MOV BYTE PTR [ECX+EAX], DL      ;Set page in use\r
+               SHL EAX, 3                                      ;Multiply time 8 (page number base in byte)\r
+               ADD EBX, EAX                            ;Add page number in byte\r
+               SHL EBX, 12                                     ;Now EBX = Physical Page Addr (EBX*4096)\r
+               DEC _nPagesFree                         ;One less available\r
+               POP EDX\r
+               POP ECX\r
+               POP EAX\r
+               RETN\r
+FHPn:\r
+               XOR EBX, EBX                            ;Set to zero for error\r
+               POP EDX\r
+               POP ECX\r
+               POP EAX\r
+               RETN\r
+;========================================================================\r
+FindLoPage:\r
+; This finds the first unused physical page in memory from the BOTTOM up.\r
+; It also MARKS the page as used (assuming that we will allocate it).\r
+; Of course this means if we call FindLoPage and don't use it we\r
+; must call UnMarkPage to release it.\r
+; This reduces nPagesFree by one.\r
+;\r
+;IN  :  Nothing\r
+;OUT :  EBX is the physical address of the new page, or 0 if error\r
+;USED: EBX, Flags\r
+\r
+               PUSH EAX\r
+               PUSH ECX\r
+               PUSH EDX\r
+               MOV ECX, OFFSET rgPAM           ;Page Allocation Map\r
+               XOR EAX, EAX                            ;Start at first byte in PAM\r
+FLP1:\r
+               CMP BYTE PTR [ECX+EAX],0FFh             ;All 8 pages used?\r
+               JNE FLP2                                        ;No\r
+               INC EAX                                         ;Another Byte higher\r
+               CMP EAX, sPAM                           ;Are we past at TOP of PAM?\r
+               JAE FLPn                                        ;no memory left...\r
+               JMP SHORT FLP1                          ;Back for next byte\r
+FLP2:\r
+               XOR EBX, EBX                            ;\r
+               XOR EDX, EDX\r
+               MOV DL, BYTE PTR [ECX+EAX]      ;Get the byte with a whole in it...\r
+FLP3:\r
+               BT  EDX, EBX                            ;Test bits\r
+               JNC FLPf                                        ;FOUND ONE! (goto found)\r
+               INC EBX                                         ;Next bit\r
+               CMP EBX, 8                                      ;End of the Byte?\r
+               JAE FLPn                                        ;Error (BAD CPU???)\r
+               JMP FLP3\r
+FLPf:\r
+               BTS EDX, EBX                            ;Set the bit indexed by EBX\r
+               MOV BYTE PTR [ECX+EAX], DL      ;Set page in use\r
+\r
+               SHL EAX, 3                                      ;Multiply time 8 (page number base in byte)\r
+               ADD EBX, EAX                            ;Add page number in byte\r
+               SHL EBX, 12                                     ;Now EBX = Physical Page Addr (EBX*4096)\r
+               DEC _nPagesFree                         ;One less available\r
+               POP EDX\r
+               POP ECX\r
+               POP EAX\r
+               RETN\r
+FLPn:\r
+               XOR EBX, EBX                            ;Set to zero for error\r
+               POP EDX\r
+               POP ECX\r
+               POP EAX\r
+               RETN\r
+\r
+;========================================================================\r
+MarkPage:\r
+; Given a physical memory address, this finds the bit in the PAM associated\r
+; with it and SETS it to show the physical page in use.  This is used\r
+; with the routines that initialize all memory mgmt function.\r
+; This reduces nPagesFree by one.\r
+;\r
+;IN  :  EBX is the physical address of the page to mark\r
+;OUT : Nothing\r
+;USED: EBX, Flags\r
+\r
+               PUSH EAX\r
+               PUSH ECX\r
+               PUSH EDX\r
+               MOV EAX, OFFSET rgPAM           ;Page Allocation Map\r
+               AND EBX, 0FFFFF000h                     ;Round down to page modulo 4096\r
+               MOV ECX, EBX\r
+               SHR ECX, 15                                     ;ECX is now byte offset into PAM\r
+               SHR EBX, 12                                     ;Get Bit offset into PAM\r
+               AND EBX, 07h                            ;EBX is now bit offset into byte of PAM\r
+               MOV DL, [EAX+ECX]                       ;Get the byte into DL\r
+               BTS EDX, EBX                            ;BitSet nstruction with Bit Offset\r
+               MOV [EAX+ECX], DL                       ;Save the new PAM byte\r
+               DEC _nPagesFree                         ;One less available\r
+               POP EDX\r
+               POP ECX\r
+               POP EAX\r
+               RETN\r
+;========================================================================\r
+UnMarkPage:\r
+; Given a physical memory address, this finds the bit in the PAM associated\r
+; with it and RESETS it to show the physical page available again.\r
+; This increases nPagesFree by one.\r
+;\r
+;IN  :  EBX is the physical address of the page to UNmark\r
+;OUT : Nothing\r
+;USED: EBX, Flags\r
+\r
+               PUSH EAX\r
+               PUSH ECX\r
+               PUSH EDX\r
+               MOV EAX, OFFSET rgPAM           ;Page Allocation Map\r
+               AND EBX, 0FFFFF000h                     ;Round down to page modulo\r
+               MOV ECX, EBX\r
+               SHR ECX, 15                                     ;ECX is now byte offset into PAM\r
+               SHR EBX, 12                                     ;\r
+               AND EBX, 07h                            ;EBX is now bit offset into byte of PAM\r
+               ADD EAX, ECX\r
+               MOV DL, [EAX]\r
+               BTR EDX, EBX                            ;BitReset instruction\r
+               MOV [EAX], DL\r
+               INC _nPagesFree                         ;One more available\r
+               POP EDX\r
+               POP ECX\r
+               POP EAX\r
+               RETN\r
+\r
+;============================================================\r
+;\r
+; LinToPhy\r
+; Looks Up the Physical address of a 32 bit linear address passed in.\r
+; The JCB is used to identify who's page tables we are translating.\r
+; The linear address is used to look up the Page Table entry which is\r
+; used to get the physical address.  This call is used for things like\r
+; aliasing for messages, DMA operations, etc.\r
+; This also leave the Linear Address of the PTE itself in ESI\r
+; for callers that need it.\r
+;\r
+; INPUT:       EAX -- Job Number that owns memory we are aliasing\r
+;                      EBX -- Linear address\r
+;\r
+; OUTPUT:      EAX -- Physical Address\r
+;           ESI -- Linear Address of PTE for this linear address\r
+;\r
+; USED:                EAX, EBX, ESI, EFlags\r
+;\r
+PUBLIC LinToPhy:\r
+               PUSH EBX                                ;Save Linear\r
+               CALL GetpJCB                    ;Leaves pJCB in EAX\r
+               MOV EAX, [EAX+JcbPD]    ;EAX now has ptr to PD!\r
+               ADD EAX, 2048                   ;Move to shadow addresses in PD\r
+               SHR EBX, 22                             ;Shift out lower 22 bits leaving 10 bit offset\r
+               SHL EBX, 2                              ;*4 to make it a byte offset into PD shadow\r
+               ADD EBX, EAX                    ;EBX/EAX now points to shadow\r
+               MOV EAX, [EBX]                  ;EAX now has Linear of Page Table\r
+               POP EBX                                 ;Get original linear back in EBX\r
+               PUSH EBX                                ;Save it again\r
+               AND EBX, 003FFFFFh              ;Get rid of upper 10 bits\r
+               SHR EBX, 12                             ;get rid of lower 12 to make it an index\r
+               SHL EBX, 2                              ;*4 makes it byte offset in PT\r
+               ADD     EBX, EAX                        ;EBX now points to Page Table entry!\r
+               MOV ESI, EBX                    ;Save this address for caller\r
+               MOV EAX, [EBX]                  ;Physical base of page is in EAX\r
+               AND EAX, 0FFFFF000h             ;mask off lower 12\r
+               POP EBX                                 ;Get original linear\r
+               AND EBX, 00000FFFh              ;Cut off upper 22 bits of linear\r
+               OR EAX, EBX                             ;EAX now has REAL PHYSICAL ADDRESS!\r
+               RETN\r
+\r
+;=============================================================================\r
+; FindRun\r
+; This finds a linear run of FREE LINEAR memory in one of the USER or OS PTs.\r
+; This is either at address base 0 (for OS) or 1Gb (for user).\r
+; EAX = 0 if we are looking for OS memory, else\r
+; EAX = 256 if we are looking for USER memory.\r
+; The linear address of the run is returned in EAX unless no\r
+; run that large exists, in which case we return 0.\r
+; The linear run may span page tables (if they already exist).\r
+; This is an interesting routine because it uses two nested loops\r
+; to walk thru the page directory and page tables while using the\r
+; SIB (Scale Index Base) addressing of the 386 for indexing.\r
+;\r
+; IN :  EAX  PD Shadow Base Offset for memory (0 for OS, 256 for user)\r
+;              EBX  Number of Pages for run\r
+;\r
+; OUT:  EAX  Linear address or 0 if no run is large enough\r
+;              EBX  still has count of pages\r
+; USED: EAX, EBX, EFlags  (all other registers saved & restored)\r
+;\r
+;\r
+FindRun:\r
+               PUSH EBX                                ;Holds count of pages (saved for caller)\r
+               PUSH ECX                                ;Keeps count of how many free found so far\r
+               PUSH EDX                                ;Index into PD for PT we are working on\r
+               PUSH ESI                                ;Address of PD saved here\r
+               PUSH EDI                                ;\r
+\r
+               MOV ECX, EBX                    ;Copy number of pages to ECX. Save in EBX\r
+               MOV EDX, EAX                    ;Index into shadow addresses from EAX\r
+\r
+               CALL GetpCrntJCB                ;Leaves pCrntJCB in EAX\r
+               MOV ESI, [EAX+JcbPD]    ;ESI now has ptr to PD\r
+               ADD ESI, 2048                   ;Move to shadow addresses\r
+\r
+FR0:\r
+               MOV EDI, [ESI+EDX*4]    ;Linear address of next page table into EDI\r
+               OR EDI, EDI                             ;Is the address NON-ZERO (valid)?\r
+               JNZ FR1                                 ;Yes, go for it\r
+               XOR EAX, EAX                    ;Return 0 cause we didn't find it!\r
+               JMP SHORT FREXIT                ;\r
+FR1:\r
+               XOR EAX, EAX                    ;EAX indexes into PT (to compare PTEs)\r
+FR2:\r
+               CMP EAX, 1024                   ;Are we past last PTE of this PT?\r
+               JB  FR3                                 ;No, keep testing\r
+               INC EDX                                 ;Next PT!\r
+               JMP SHORT FR0                   ;\r
+FR3:\r
+               CMP DWORD PTR [EDI+EAX*4], 0    ;Zero means it's empty (available)\r
+               JNE     FR4                                                     ;In use\r
+               DEC ECX                                 ;One more empty one!\r
+               JZ  FROK                                ;We found enough entries goto OK\r
+               INC EAX                                 ;Not done yet, Next PTE Please.\r
+               JMP SHORT FR2                   ;\r
+FR4:\r
+               ;If we got here we must reset ECX for full count and\r
+               ;go back and start looking again\r
+               INC EAX                                 ;Not empty, next PTE please\r
+               MOV ECX, EBX                    ;We kept original count in EBX\r
+               JMP FR2\r
+FROK:\r
+               ;If we got here it means that ECX has made it to zero and\r
+               ;we have a linear run large enough to satisy the request.\r
+               ;The starting linear address is equal to number of the last\r
+               ;PTE we found minus ((npages -1) * 4096)\r
+               ;EDX was index into PD, while EAX was index into PT.\r
+               ;EBX still has count of pages.\r
+\r
+               SHL EDX, 22                     ;EDX is 10 MSBs of Linear Address\r
+               SHL EAX, 12                     ;EAX is next 10 bits of LA\r
+               OR EAX, EDX                     ;This is the linear address we ended at\r
+               DEC EBX                         ;One less page (0 offset)\r
+               SHL EBX, 12                     ;Times size of page (* 4096)\r
+               SUB EAX, EBX            ;From current linear address in tables\r
+\r
+FREXIT:\r
+               POP EDI                         ;\r
+               POP ESI                         ;\r
+               POP EDX                         ;\r
+               POP ECX                         ;\r
+               POP EBX                         ;\r
+               RETN\r
+;=============================================================================\r
+; AddRun\r
+; This adds one or more PTEs to a page table (or tables if the run\r
+; spans two or more tables).\r
+; The address determines the protection level of the PTE's we add.\r
+; If it is less than 1GB it means OS memory which we will set to SYSTEM.\r
+; Above 1Gb is user which we will set to user level protection.\r
+; The linear address of the run should be in EAX, and the count of\r
+; pages should be in EBX (this is the way FindRun left them).\r
+;\r
+; IN :  EAX  Linear address of first page\r
+;              EBX  Number of Pages to add\r
+; OUT:  Nothing\r
+; USED: EAX, EFlags\r
+;\r
+AddRun:\r
+               PUSH EBX                                ;(save for caller)\r
+               PUSH ECX                                ;\r
+               PUSH EDX                                ;\r
+               PUSH ESI                                ;\r
+               PUSH EDI                                ;\r
+\r
+               MOV ECX, EBX                    ;Copy number of pages to ECX (EBX free to use).\r
+               MOV EDX, EAX                    ;LinAdd to EDX\r
+               SHR EDX, 22                             ;Get index into PD for first PT\r
+               SHL EDX, 2                              ;Make it index to DWORDS\r
+\r
+               PUSH EAX                                ;Save EAX thru GetpCrntJCB call\r
+               CALL GetpCrntJCB                ;Leaves pCrntJCB in EAX\r
+               MOV ESI, [EAX+JcbPD]    ;ESI now has ptr to PD!\r
+               POP EAX                                 ;Restore linear address\r
+\r
+               ADD ESI, 2048                   ;Offset to shadow address of PD\r
+               ADD ESI, EDX                    ;ESI now points to initial PT (EDX now free)\r
+\r
+               MOV EDX, EAX                    ;LinAdd into EDX again\r
+               AND EDX, 003FF000h              ;get rid of upper 10 bits & lower 12\r
+               SHR EDX, 10                     ;Index into PD for PT (10 vice 12 -> DWORDS)\r
+AR0:\r
+               MOV EDI, [ESI]                  ;Linear address of next page table into EDI\r
+\r
+               ;At this point, EDI is pointing the next PT.\r
+               ;SO EDI+EDX will point to the next PTE to do.\r
+               ;Now we must call FindPage to get a physical address into EBX,\r
+               ;then check the original linear address to see if SYSTEM or USER\r
+               ;and OR in the appropriate control bits, THEN store it in PT.\r
+\r
+AR1:\r
+               CALL FindHiPage                 ;EBX has Phys Pg (only EBX affected)\r
+               OR EBX, MEMSYS                  ;Set PTE to present, User ReadOnly\r
+               CMP EAX, 40000000h              ;See if it's a user page\r
+               JB  AR2\r
+               OR EBX, MEMUSERD                ;Sets User/Writable bits of PTE\r
+\r
+AR2:\r
+               MOV DWORD PTR [EDI+EDX], EBX    ;EDX is index to exact entry\r
+               DEC ECX                                                 ;Are we done??\r
+               JZ ARDone\r
+               ADD EDX, 4                              ;Next PTE please.\r
+               CMP EDX, 4096                   ;Are we past last PTE of this PT?\r
+               JB AR1                                  ;No, go do next PTE\r
+               ADD ESI, 4                              ;Yes, next PDE (to get next PT)\r
+               XOR EDX,EDX                             ;Start at the entry 0 of next PT\r
+               JMP SHORT AR0                   ;\r
+ARDone:\r
+               POP EDI                         ;\r
+               POP ESI                         ;\r
+               POP EDX                         ;\r
+               POP ECX                         ;\r
+               POP EBX                         ;\r
+               RETN\r
+\r
+;=============================================================================\r
+; AddDMARun\r
+; This adds one or more PTEs to a page table (or tables if the run\r
+; spans two or more tables).\r
+; The address determines the protection level of the PTE's we add.\r
+; If it is less than 1GB it means OS memory which we will set to SYSTEM.\r
+; Above 1Gb is user which we will set to user level protection.\r
+; The linear address of the run should be in EAX, and the count of\r
+; pages should be in EBX (this is the way FindRun left them).\r
+;\r
+; IN :  EAX  Linear address of first page\r
+;              EBX  Number of Pages to add\r
+; OUT:  Nothing\r
+; USED: EAX, EFlags\r
+;\r
+AddDMARun:\r
+               PUSH EBX                                ;(save for caller)\r
+               PUSH ECX                                ;\r
+               PUSH EDX                                ;\r
+               PUSH ESI                                ;\r
+               PUSH EDI                                ;\r
+\r
+               MOV ECX, EBX                    ;Copy number of pages to ECX (EBX free to use).\r
+               MOV EDX, EAX                    ;LinAdd to EDX\r
+               SHR EDX, 22                             ;Get index into PD for first PT\r
+               SHL EDX, 2                              ;Make it index to DWORDS\r
+\r
+               PUSH EAX                                ;Save EAX thru GetpCrntJCB call\r
+               CALL GetpCrntJCB                ;Leaves pCrntJCB in EAX\r
+               MOV ESI, [EAX+JcbPD]    ;ESI now has ptr to PD!\r
+               POP EAX                                 ;Restore linear address\r
+\r
+               ADD ESI, 2048                   ;Offset to shadow address of PD\r
+               ADD ESI, EDX                    ;ESI now points to initial PT (EDX now free)\r
+\r
+               MOV EDX, EAX                    ;LinAdd into EDX again\r
+               AND EDX, 003FF000h              ;get rid of upper 10 bits & lower 12\r
+               SHR EDX, 10                     ;Index into PD for PT (10 vice 12 -> DWORDS)\r
+ARD0:\r
+               MOV EDI, [ESI]                  ;Linear address of next page table into EDI\r
+\r
+               ;At this point, EDI is pointing the next PT.\r
+               ;SO EDI+EDX will point to the next PTE to do.\r
+               ;Now we must call FindPage to get a physical address into EBX,\r
+               ;then check the original linear address to see if SYSTEM or USER\r
+               ;and OR in the appropriate control bits, THEN store it in PT.\r
+\r
+ARD1:\r
+               CALL FindLoPage                 ;EBX has Phys Pg (only EBX affected)\r
+               OR EBX, MEMSYS                  ;Set PTE to present, User ReadOnly\r
+               MOV DWORD PTR [EDI+EDX], EBX    ;EDX is index to exact entry\r
+               DEC ECX                                                 ;Are we done??\r
+               JZ ARDDone\r
+               ADD EDX, 4                              ;Next PTE please.\r
+               CMP EDX, 4096                   ;Are we past last PTE of this PT?\r
+               JB ARD1                                 ;No, go do next PTE\r
+               ADD ESI, 4                              ;Yes, next PDE (to get next PT)\r
+               XOR EDX,EDX                             ;Start at the entry 0 of next PT\r
+               JMP SHORT ARD0                  ;\r
+ARDDone:\r
+               POP EDI                         ;\r
+               POP ESI                         ;\r
+               POP EDX                         ;\r
+               POP ECX                         ;\r
+               POP EBX                         ;\r
+               RETN\r
+\r
+;=============================================================================\r
+; AddAliasRun\r
+; This adds one or more PTEs to a page table (or tables if the run\r
+; spans two or more tables) adding PTEs from another job's PTs marking\r
+; them as ALIAS entries.\r
+; Aliased runs are ALWAYS at USER protection levels even if in the\r
+; OS address span!\r
+;\r
+; The NEW linear address of the run should be in EAX, and the count of\r
+; pages should be in EBX (this is the way FindRun left them).\r
+; ESI has the linear address we are aliasing and EDX has the Job#\r
+;\r
+; IN :  EAX  Linear address of first page of new alias entries\r
+;            (from find run)\r
+;              EBX  Number of Pages to alias\r
+;       ESI  Linear Address of pages to Alias (from other job)\r
+;       EDX  Job Number of Job we are aliasing\r
+;\r
+; OUT:  Nothing\r
+; USED: EAX, EFlags\r
+;\r
+AliasLin    EQU DWORD PTR [EBP-4]\r
+AliasJob    EQU DWORD PTR [EBP-8]\r
+\r
+AddAliasRun:\r
+               PUSH EBP                ;\r
+               MOV EBP,ESP             ;\r
+               SUB ESP, 8\r
+\r
+               MOV AliasLin, ESI\r
+               MOV AliasJob, EDX\r
+\r
+               PUSH EBX                                ;(save for caller)\r
+               PUSH ECX                                ;\r
+               PUSH EDX                                ;\r
+               PUSH ESI                                ;\r
+               PUSH EDI                                ;\r
+\r
+               ;This first section sets to make [ESI] point to first PT that\r
+               ;we have to move the other guy's physical pages into\r
+\r
+               MOV ECX, EBX                    ;Copy number of pages to ECX (EBX free to use).\r
+               MOV EDX, EAX                    ;LinAdd to EDX\r
+               SHR EDX, 22                             ;Get index into PD for first PT\r
+               SHL EDX, 2                              ;Make it index to DWORDS\r
+\r
+               PUSH EAX                                ;Save EAX thru GetpCrntJCB call\r
+               CALL GetpCrntJCB                ;Leaves pCrntJCB in EAX\r
+               MOV ESI, [EAX+JcbPD]    ;ESI now has linear address of PD\r
+               POP EAX                                 ;Restore linear address\r
+\r
+               ADD ESI, 2048                   ;Offset to shadow addresses in PD\r
+               ADD ESI, EDX                    ;ESI now points to first PT of interest\r
+\r
+               MOV EDX, EAX                    ;LinAdd into EDX again\r
+               AND EDX, 003FF000h              ;get rid of upper 10 bits & lower 12\r
+               SHR EDX, 10                     ;Index into PD for PT (10 vice 12 -> DWORDS)\r
+ALR0:\r
+               MOV EDI, [ESI]                  ;Linear address of crnt page table into EDI\r
+\r
+               ;At this point, EDI is pointing to the PT we are in.\r
+               ;SO then EDI+EDX will point to the next PTE to do.\r
+               ;Now we must call LinToPhy with Linear Add & JobNum\r
+               ; to get a physical address into EAX.\r
+               ;This is the Physical address to store in the new PTE.  We must\r
+               ;mark it MEMALIAS before adding it to PT.\r
+ALR1:\r
+               PUSH ESI                                ;Save for next loop (used by LinToPhy)\r
+\r
+               MOV EAX, AliasJob               ;Job we are aliasing\r
+               MOV EBX, AliasLin               ;Address we are aliasing\r
+               ADD AliasLin, 4096              ;Set up for next loop (post increment)\r
+               CALL LinToPhy                   ;\r
+\r
+               ;EAX now has physical address for this page\r
+               ;\r
+\r
+               AND EAX, 0FFFFF000h             ;cut off system bits of PTE\r
+               OR EAX, MEMALIAS                ;Set system bits as ALIAS\r
+\r
+               POP     ESI                                     ;Restore ESI (LinToPhy used it)\r
+\r
+               ;Now store it in new PTE\r
+\r
+               MOV DWORD PTR [EDI+EDX], EAX    ;EDX is index to exact entry\r
+\r
+               DEC ECX                                 ;Are we done??\r
+               JZ ALRDone\r
+               ADD EDX, 4                              ;Next PTE please.\r
+               CMP EDX, 4096                   ;Are we past last PTE of this PT?\r
+               JB ALR1                                 ;No, go do next PTE\r
+               ADD ESI, 4                              ;Yes, next PDE (to get next PT)\r
+               XOR EDX,EDX                             ;Start at the entry 0 of next PT\r
+               JMP SHORT ALR0                  ;\r
+ALRDone:\r
+               POP EDI                         ;\r
+               POP ESI                         ;\r
+               POP EDX                         ;\r
+               POP ECX                         ;\r
+               POP EBX                         ;\r
+\r
+               MOV ESP,EBP             ;\r
+               POP EBP                 ;\r
+               RETN\r
+\r
+;=============================================================================\r
+; AddUserPT\r
+; This creates a new User Page Table, initializes it and sticks it\r
+; in the Users's PD (in User Address space above 1GB).\r
+; This is easier than AddOSPT because there is no need to update\r
+; anyone else's PDs!  This sets the protection on the PT to user\r
+; Read & Write.  Individual PTEs will be set read only for code.\r
+;\r
+; IN :  Nothing\r
+; OUT:  0 if OK or Error (ErcNoMem - no free phy pages!)\r
+; USED: EAX, EFlags\r
+;\r
+AddUserPT:\r
+               PUSH EBX                                ;(save for caller)\r
+               PUSH ECX                                ;\r
+               PUSH EDX                                ;\r
+               PUSH ESI                                ;\r
+               PUSH EDI                                ;\r
+\r
+               MOV EAX, _nPagesFree    ;See if have enuf physical memory\r
+               OR EAX, EAX\r
+               JNZ AUPT01\r
+               MOV EAX, ErcNoMem               ;Sorry, out of physical mem\r
+               JMP AUPTDone\r
+AUPT01:\r
+               CALL GetCrntJobNum              ;Leaves job num in EAX (for LinToPhy)\r
+               MOV EBX, pNextPT                ;Pre allocated Page (Linear Address)\r
+               CALL LinToPhy                   ;EAX will have Physical address\r
+\r
+               ; Put it in the User PD (and linear in shadow).\r
+               ; Find first empty slot\r
+\r
+               CALL GetpCrntJCB                ;pJCB in EAX\r
+               MOV EDI, JcbPD                  ;Offset to PcbPD in JCB\r
+               ADD EDI, EAX                    ;EDI points to UserPD Address\r
+               MOV ESI, [EDI]                  ;ESI now points to PD\r
+               ADD ESI, 2048                   ;ESI now points to upper 1 GB in PD\r
+               MOV ECX, 511                    ;Number of entries (at least 1 is already gone)\r
+AUPT02:\r
+               ADD ESI, 4                              ; Next possible empty entry\r
+               MOV     EBX, [ESI]\r
+               OR EBX, EBX                     ; Is it empty?\r
+               LOOPNZ AUPT02                   ; No! (Try again)\r
+\r
+               ; ESI now points to empty Slot\r
+               ; Physical Address of new table is still in EAX\r
+               ; Get Linear address back into EBX\r
+               ; and put them into PD\r
+\r
+               OR  EAX, MEMUSERD               ;Set user bits (Read/Write)\r
+               MOV [ESI], EAX                  ;Physical address in lower half\r
+               ADD ESI, 2048                   ;Move to shadow\r
+               MOV EBX, pNextPT                ;Linear add back into EBX\r
+               MOV [ESI], EBX                  ;Put in Linear address of PT (upper half)\r
+\r
+               ;Now we now need another PreAllocated Page Table for\r
+               ;next time. Get a run of 1 for next new page table\r
+\r
+               MOV EBX, 1                              ;size of request\r
+               XOR EAX, EAX                    ;PD shadow offset needed by FindRun (0)\r
+               CALL FindRun\r
+               OR EAX, EAX                             ;was there an error (0 means no mem)\r
+               JNZ AUPT05\r
+               MOV EAX, ErcNoMem               ;\r
+               JMP SHORT AUPTDone\r
+AUPT05:\r
+               MOV pNextPT, EAX                ;save pNextPT (the linear address)\r
+               CALL AddRun                             ;AddRun will return NON-ZERO on error\r
+AUPTDone:\r
+               POP EDI                         ;\r
+               POP ESI                         ;\r
+               POP EDX                         ;\r
+               POP ECX                         ;\r
+               POP EBX                         ;\r
+               RETN\r
+\r
+;=============================================================================\r
+; AddOSPT\r
+; This creates a new OS Page Table, initializes it and sticks it\r
+; in the OS's PD (in OS address space below 1GB).\r
+; This also updates ALL PDs for ALL jobs.  We must do this\r
+; to ensure the OS code can reach its memory no matter what JOB/TASK\r
+; it is running in.\r
+;\r
+; IN :  Nothing\r
+; OUT:  0 if OK or Error (ErcNoMem - no free phy pages!)\r
+; USED: EAX, EFlags\r
+;\r
+AddOSPT:\r
+               PUSH EBX                                ;(save for caller)\r
+               PUSH ECX                                ;\r
+               PUSH EDX                                ;\r
+               PUSH ESI                                ;\r
+               PUSH EDI                                ;\r
+\r
+               MOV EAX, _nPagesFree    ;See if have enuf physical memory\r
+               OR EAX, EAX\r
+               JNZ AOPT01\r
+               MOV EAX, ErcNoMem               ;Sorry, out of physical mem\r
+               JMP AOPTDone\r
+AOPT01:\r
+               MOV EAX, 1                              ;OS Job Number (Monitor)\r
+               MOV EBX, pNextPT                ;Pre allocated Page (Linear Address)\r
+               CALL LinToPhy                   ;EAX will have Physical address\r
+\r
+               ; Put it in the OS PD (and linear in shadow).\r
+               ; Find first empty slot\r
+\r
+               MOV ESI, OFFSET PDir1   ; ESI points to OS Pdir\r
+               MOV ECX, 511                    ; Count of PDEs to check\r
+AOPT02:\r
+               ADD ESI, 4                              ; Next possible empty entry\r
+               MOV EBX, [ESI]\r
+               OR EBX, EBX                             ; Is it empty?\r
+               LOOPNZ AOPT02                   ; No! (Try again)\r
+\r
+               ; ESI now points to empty PDir Slot\r
+               ; EAX still has Physical Address of new table\r
+               ; Get Physical Address back into EBX\r
+               ; and put them into PDir\r
+\r
+               OR EAX, PRSNTBIT                ;Set present bit\r
+               MOV [ESI], EAX                  ;Physical address in lower half\r
+               ADD ESI, 2048                   ;Move to shadow\r
+               MOV EBX, pNextPT                ;Linear add back into EBX\r
+               MOV [ESI], EBX                  ;Put in Linear address of PT (upper half)\r
+\r
+               ; Update ALL PDs from PDir1 !!\r
+               ; This doesn't happen often if it happens at all.\r
+               ; The OS will usually not take 4 MBs even with ALL\r
+               ; of its dynamic structures (except on\r
+               ; a 32 Mb system or larger and when fully loaded)\r
+\r
+               MOV EDX, nJCBs                  ; # of dynamic JCBs\r
+AOPT03:\r
+               MOV EAX, EDX                    ;Next JCB\r
+               CALL GetpJCB                    ;EAX now has pointer to a job's PD\r
+               MOV ECX, [EAX+JcbPD]    ;See if PD id zero (Inactive JCB)\r
+               OR ECX, ECX                             ;Is it a valid Job? (0 if not)\r
+               JZ AOPT04                               ;No, Not a valid JCB (unused)\r
+\r
+               ADD EAX, JcbPD                  ;EAX NOW points to PD of JCB\r
+               MOV EBX, OFFSET PDir1   ;Source of Copy\r
+\r
+               PUSH EDX                                ;Save nJCB we are on\r
+\r
+               PUSH EAX                                ;Save values on stack\r
+               PUSH EBX\r
+\r
+               PUSH EBX                                ;Source\r
+               PUSH EAX                                ;Destination\r
+               PUSH 1024                               ;Lower half of PD (Physical Adds)\r
+               CALL FWORD PTR _CopyData\r
+\r
+               POP  EBX                                ;Get values from stack\r
+               POP  EAX\r
+\r
+               ADD EBX, 2048                   ;Move to shadow\r
+               PUSH EBX\r
+               ADD EAX, 2048                   ;Move to shadow\r
+               PUSH EAX\r
+               PUSH 1024                               ;Upper half of PD (Linear Adds)\r
+               CALL FWORD PTR _CopyData\r
+\r
+               POP EDX                                 ; Get back JCB number\r
+\r
+AOPT04:\r
+               DEC EDX\r
+               CMP EDX, 2\r
+               JA AOPT03                               ;Jobs 1 & 2 use PDir1 (Mon & Debugger)\r
+\r
+               ;At this point the new table is valid to ALL jobs!\r
+               ;We now need another PreAllocated Page Table for\r
+               ;next time. Get a run of 1 for next new page table\r
+\r
+               MOV EBX, 1                              ;size of request\r
+               XOR EAX, EAX                    ;PD shadow offset needed by FindRun (0)\r
+               CALL FindRun\r
+               OR EAX, EAX                             ;was there an error (0 means no mem)\r
+               JNZ AOPT05\r
+               MOV EAX, ErcNoMem               ;\r
+               JMP SHORT AOPTDone\r
+AOPT05:\r
+               MOV pNextPT, EAX                ;save pNextPT (the linear address)\r
+               CALL AddRun                             ;AddRun\r
+               XOR EAX, EAX                    ;Set ErcOK (0)\r
+AOPTDone:\r
+               POP EDI                         ;\r
+               POP ESI                         ;\r
+               POP EDX                         ;\r
+               POP ECX                         ;\r
+               POP EBX                         ;\r
+               RETN\r
+\r
+;=============================================================================\r
+; GetGDTDesc\r
+; You supply the GDT selector in BX and this puts entry in EAX,EDX.\r
+; EAX, EBX and EDX are used.\r
+; This assumes that the PUBLIC Variable GDTbase has been filled in by\r
+; the OS using SGDT after it went PMode.\r
+; Used by the Debugger among other things.\r
+;\r
+GetGDTDesc:\r
+               AND EBX,0000FFF8h       ;Mask any left overs (hi word)\r
+               ADD EBX,GDTBase         ;Add to GDT base\r
+               MOV EAX,[EBX]\r
+               MOV EDX,[EBX+4]\r
+               RETN\r
+\r
+;=============================================================================\r
+;=============================================================================\r
+;  BEGIN PUBLIC CALL DEFINITION FOR MEMORY MANAGEMENT\r
+;=============================================================================\r
+;=============================================================================\r
+; PUBLIC calls (far through call gates)\r
+; AddCallGate   - Adds a public CallGate to the GDT\r
+; AddIDTGate    - Adds an Interrupt Vector to the IDT\r
+; AllocPage     - Returns a ptr to allocated linear memory pages (Hi Phys)\r
+; AllocOSPage   - Returns a ptr to allocated linear memory pages (Hi Phys)\r
+; AllocDMAPage  - Returns a ptr with physical and linear memory pages\r
+; AliasMem             - Aliases a memory address from one Job to another\r
+; DeAllocPage   - Provided with ptr, deallocates memory pages\r
+; QueryMemPages - Tells you how many pages are left free\r
+; GetPhyAdd            - Returns the Physical Address for a Linear Address\r
+;=============================================================================\r
+; AddGDTCallGate will build and add a GDT entry for a call gate allowing\r
+; access to OS procedures. This call doesn't check to see if the GDT\r
+; descriptor for the call is already defined. It assumes you know what you\r
+; are doing and overwrites one if already defined.  The Selector number is\r
+; checked to make sure you're in range (40h thru max call gate num).\r
+;\r
+; IN: AX - Word with Call Gate ID type as follows:\r
+;\r
+;                      DPL entry of 3 EC0x   (most likely)\r
+;                      DPL entry of 2 CC0x   (Not used in MMURTL)\r
+;                      DPL entry of 1 AC0x   (Not used in MMURTL)\r
+;                      DPL entry of 0 8C0x   (OS call ONLY)\r
+;                      (x = count of DWord params 0-F)\r
+;\r
+;     CX    Selector number for call gate in GDT (constants!)\r
+;        ESI   Offset of entry point in segment of code to execute\r
+;\r
+; OUT:  EAX    Returns Errors, else 0 if all's well\r
+;\r
+; USES: EAX, EBX, ECX, ESI, EFLAGS\r
+\r
+PUBLIC __AddCallGate:\r
+               CMP CX, 40h             ;Is number within range of callgates?\r
+               JAE AddCG01                     ;not too low.\r
+               MOV EAX, ercBadGateNum\r
+               RETF\r
+AddCG01:\r
+               MOVZX EBX, CX\r
+               SUB EBX, 40                     ;sub call gate base selector\r
+               SHR EBX, 3                      ;make index vice selector\r
+               CMP EBX, nCallGates     ;see if too high!\r
+               JBE AddCG02                     ;No.\r
+               MOV EAX, ercBadGateNum  ;Yes.\r
+               RETF\r
+AddCG02:\r
+               MOVZX EBX, CX           ;Extend selector into EBX\r
+               ADD EBX, GDTBase        ;NOW a true offset in GDT\r
+               MOV WORD PTR [EBX+02], 8        ;Put Code Seg selector into Call gate\r
+               MOV [EBX], SI           ;0:15 of call offset\r
+               SHR ESI, 16                     ;move upper 16 of offset into SI\r
+               MOV [EBX+06], SI        ;16:31 of call offset\r
+               MOV [EBX+04], AX        ;call DPL & ndParams\r
+               XOR EAX, EAX            ;0 = No Error\r
+               RETF\r
+\r
+;=============================================================================\r
+; AddIDTGate will build and add an IDT Trap, Interrupt, or Task Gate.\r
+; The Selector of the call is Always 8 for Int or Trap, and is the\r
+; TSS of the task for a Task gate.\r
+;\r
+; IN:  AX      - Word with Gate ID type as follows:\r
+;                              Trap Gate with DPL of 3       8F00\r
+;                              Interrupt Gate with DPL of 3  8E00\r
+;                              Task Gate with DPL of 3       8500\r
+;\r
+;              BX      - Selector of gate (08 or TSS selector for task gates)\r
+;\r
+;              CX      - Word with Interrupt Number (00-FF)\r
+;\r
+;              ESI - Offset of entry point in OS code to execute\r
+;                        (THIS MUST BE 0 FOR TASK GATES)\r
+;\r
+; USES: EAX, EBX, ECX, EDX, ESI, EFLAGS\r
+\r
+PUBLIC __AddIDTGate:\r
+               MOVZX EDX, CX                           ;Extend INT Num into EDX\r
+               SHL EDX, 3                                      ;Gates are 8 bytes each (times 8)\r
+               ADD EDX, OFFSET IDT                     ;EDX now points to gate\r
+               MOV WORD PTR [EDX+4], AX        ;Put Gate ID into gate\r
+               MOV EAX, ESI\r
+               MOV WORD PTR [EDX], AX          ;Put Offset 15:00 into gate\r
+               SHR EAX, 16\r
+               MOV WORD PTR [EDX+6], AX        ;Put Offset 31:16 into gate\r
+               MOV WORD PTR [EDX+2], BX        ;Put in the selector\r
+               RETF\r
+;\r
+;\r
+;=============================================================================\r
+; AllocOSPage --\r
+; This allocates one or more pages of physical memory and returns a\r
+; linear pointer to one or more pages of contiguous memory in the OS space.\r
+; A result code is returned in the EAX register.\r
+; STEPS:\r
+; 1) See if we have enough physical memory (check nPagesFree)\r
+; 2) Find a contiguous run of linear pages to allocate (PTEs)\r
+; 3) Allocate each physical page placing it in the run of PTEs\r
+;\r
+; We search thru the page tables for the current job and find enough\r
+; contiguous PTEs to satisfy the request.  If the current PT doesn't have\r
+; enough contiguous entries, we add another page table to the OS PD\r
+; and get them from the new one and the old one (i.e., the run may\r
+; span page tables).\r
+;\r
+; Procedureal Interface :\r
+;\r
+;   AllocOSPage(dn4KPages,ppMemRet): dError\r
+;\r
+;   dn4KPages is a DWORD (4 BYTES). This is the number of contigous pages\r
+;      to be allocated.\r
+;\r
+;   ppMemRet points to the pointer where the address of the\r
+;                      new linear memory is returned.\r
+;\r
+n4KPages       EQU [EBP+10h]           ;These equates are also used by AllocPage\r
+ppMemRet       EQU [EBP+0Ch]           ;\r
+\r
+PUBLIC __AllocOSPage:           ;\r
+               PUSH EBP                ;\r
+               MOV EBP,ESP             ;\r
+               PUSH MemExch                    ;Wait at the MemExch for Msg\r
+               MOV EAX, pRunTSS                ;Put Msg in callers TSS Message Area\r
+               ADD EAX, TSS_Msg\r
+               PUSH EAX\r
+               CALL FWORD PTR _WaitMsg\r
+               CMP EAX,0h                              ;Kernel Error??\r
+               JNE SHORT ALOSPExit     ;Yes! Serious problem.\r
+\r
+               MOV EAX,n4KPages                ;size of request\r
+               OR  EAX,EAX                             ;More than 0?\r
+               JNZ ALOSP00                             ;Yes\r
+               MOV EAX,ercBadMemReq    ;Can't be zero!\r
+               JMP ALOSPExit           ;\r
+ALOSP00:\r
+               CMP EAX, _nPagesFree    ;See if have enuf physical memory\r
+               JBE ALOSP01                             ;Yes\r
+               MOV EAX, ErcNoMem               ;Sorry boss, we're maxed out\r
+               JMP SHORT ALOSPExit\r
+ALOSP01:\r
+               MOV EBX,n4KPages                ;size of request\r
+               XOR EAX, EAX                    ;PD shadow offset needed by FindRun (0)\r
+               CALL FindRun\r
+               OR EAX, EAX                             ;(0 = No Runs big enuf)\r
+               JNZ SHORT ALOSP02               ;No Error!\r
+\r
+               ;If we didn't find a run big enuf we add a page table\r
+\r
+               CALL AddOSPT                    ;Add a new page table (we need it!)\r
+               OR EAX, EAX                             ;See if it's 0 (0 = NO Error)\r
+               JZ SHORT ALOSP01                ;Go back & try again\r
+               JMP SHORT ALOSPExit             ;ERROR!!\r
+ALOSP02:\r
+                                                               ;EAX now has linear address\r
+                                                               ;EBX still has count of pages\r
+               CALL AddRun                             ;Does not return error\r
+                                                               ;EAX still has new linear address\r
+               MOV EBX, ppMemRet               ;Get address of caller's pointer\r
+               MOV [EBX], EAX                  ;Give em new LinAdd\r
+               XOR EAX, EAX                    ;No error\r
+ALOSPExit:                                 ;\r
+               PUSH EAX                                ;Save last error\r
+               PUSH MemExch                    ;Send a Semaphore msg (so next guy can get in)\r
+               PUSH 0FFFFFFF1h                 ;\r
+               PUSH 0FFFFFFF1h                 ;\r
+               CALL FWORD PTR _SendMsg ;\r
+               POP EAX                                 ;Get original error back (ignore kernel erc)\r
+               MOV ESP,EBP                             ;\r
+               POP EBP                 ;\r
+               RETF 8                  ;\r
+\r
+;=============================================================================\r
+; AllocPage --\r
+; This is identical to AllocOSPage except it's call gate is set to USER\r
+; level and the PD base value is set to 3072.  See the call description\r
+; for AllocOSPage\r
+\r
+PUBLIC __AllocPage:             ;\r
+               PUSH EBP                ;\r
+               MOV EBP,ESP             ;\r
+               PUSH MemExch                    ;Wait at the MemExch for Msg\r
+               MOV EAX, pRunTSS                ;Put Msg in callers TSS Message Area\r
+               ADD EAX, TSS_Msg\r
+               PUSH EAX\r
+               CALL FWORD PTR _WaitMsg\r
+               CMP EAX,0h                              ;Kernel Error??\r
+               JNE SHORT ALPExit       ;Yes! Serious problem.\r
+               MOV EAX,n4KPages                ;size of request\r
+               OR  EAX,EAX                             ;More than 0?\r
+               JNZ ALP00                               ;Yes\r
+               MOV EAX,ercBadMemReq    ;Can't be zero!\r
+               JMP ALPExit                             ;\r
+ALP00:\r
+               MOV EAX,n4KPages                ;size of request\r
+               CMP EAX, _nPagesFree    ;See if have enuf physical memory\r
+               JBE ALP01                               ;Yes\r
+               MOV EAX, ErcNoMem               ;Sorry boss, we're maxed out\r
+               JMP SHORT ALPExit\r
+ALP01:\r
+               MOV EBX, EAX                    ;nPages\r
+               MOV EAX, 256                    ;PD base for USER mem (needed by FindRun)\r
+               CALL FindRun\r
+               OR EAX, EAX                             ;(0 = No Runs big enuf)\r
+               JNZ SHORT ALP02                 ;No Error!\r
+\r
+               CALL AddUserPT                  ;Add a new page table (we need it!)\r
+               OR EAX, EAX                             ; 0 = NO Error\r
+               JZ SHORT ALP01                  ; Go back & try again\r
+               JMP SHORT ALPExit               ; ERROR!!\r
+\r
+ALP02:\r
+               CALL AddRun                             ;Does not return error\r
+               MOV EBX, ppMemRet               ;Get address of caller's pointer\r
+               MOV [EBX], EAX                  ;Give em new LinAdd\r
+               XOR EAX, EAX                    ;No error\r
+ALPExit:                                   ;\r
+               PUSH EAX                                ;Save last error\r
+               PUSH MemExch                    ;Send a Semaphore msg (so next guy can get in)\r
+               PUSH 0FFFFFFF1h                 ;\r
+               PUSH 0FFFFFFF1h                 ;\r
+               CALL FWORD PTR _SendMsg ;\r
+               POP EAX                                 ;Get original error back (ignore kernel erc)\r
+               MOV ESP,EBP                             ;\r
+               POP EBP                 ;\r
+               RETF 8                  ;\r
+\r
+;=============================================================================\r
+; AllocDMAPage --\r
+; This allocates one or more pages of physical memory and returns a\r
+; linear pointer to one or more pages of contiguous memory in the OS space.\r
+; This is for DMA, and thusly allocates the physical memory\r
+; from the low end!!!!\r
+; The caller is responsible to ensure the physical addresses do\r
+; not cross 64K boundaries for 8 bit channel use,\r
+; or 128K boundaries for 16 bit use. The caller should also check\r
+; to ensure the physical pages are contiguous. He can allocate\r
+; until they are then deallocate what he doesn't use.\r
+;\r
+; A result code is returned in the EAX register.\r
+; STEPS:\r
+; 1) See if we have enough physical memory (check nPagesFree)\r
+; 2) Find a contiguous run of linear pages to allocate (PTEs)\r
+; 3) Allocate each physical page placing it in the run of PTEs\r
+;    from the bottom up!!!\r
+;\r
+; We search thru the page tables for the OS and find enough\r
+; contiguous PTEs to satisfy the request.  If the current PT doesn't have\r
+; enough contiguous entries, we add another page table to the OS PD\r
+; and get them from the new one and the old one (i.e., the run may\r
+; span page tables).\r
+;\r
+; Procedureal Interface :\r
+;\r
+;   AllocDMAPage(dn4KPages,ppMemRet,pdPhyMemRet): dError\r
+;\r
+;   dn4KPages is a DWORD (4 BYTES). This is the number of contigous pages\r
+;      to be allocated.\r
+;\r
+;   ppMemRet points to the pointer where the address of the\r
+;                      new linear memory is returned.\r
+\r
+;   pdPhyMemRet points to DWord where the physical address of the memory\r
+;              is returned.\r
+;\r
+n4KDMAPages    EQU [EBP+20]            ;\r
+ppDMAMemRet    EQU [EBP+16]            ;\r
+pdDMAPhyMemRet         EQU [EBP+12]            ;\r
+\r
+EXTRN GetCrntJobNum NEAR\r
+\r
+PUBLIC __AllocDMAPage:           ;\r
+               PUSH EBP                 ;\r
+               MOV EBP,ESP             ;\r
+               PUSH MemExch                    ;Wait at the MemExch for Msg\r
+               MOV EAX, pRunTSS                ;Put Msg in callers TSS Message Area\r
+               ADD EAX, TSS_Msg\r
+               PUSH EAX\r
+               CALL FWORD PTR _WaitMsg\r
+               CMP EAX,0h                              ;Kernel Error??\r
+               JNE SHORT ALDMAPExit    ;Yes! Serious problem.\r
+\r
+               MOV EAX,n4KDMAPages             ;size of request\r
+               OR  EAX,EAX                             ;More than 0?\r
+               JNZ ALDMAP00                    ;Yes\r
+               MOV EAX,ercBadMemReq    ;Can't be zero!\r
+               JMP ALDMAPExit          ;\r
+ALDMAP00:\r
+               CMP EAX, _nPagesFree    ;See if have enuf physical memory\r
+               JBE ALDMAP01                    ;Yes\r
+               MOV EAX, ErcNoMem               ;Sorry boss, we're maxed out\r
+               JMP SHORT ALDMAPExit\r
+ALDMAP01:\r
+               MOV EBX, EAX                    ;size of request\r
+               XOR EAX, EAX                    ;PD shadow offset needed by FindRun (0)\r
+               CALL FindRun\r
+               OR EAX, EAX                             ;(0 = No Runs big enuf)\r
+               JNZ SHORT ALDMAP02              ;No Error!\r
+\r
+               ;If we didn't find a run big enuf we add a page table\r
+\r
+               CALL AddOSPT                    ;Add a new page table (we need it!)\r
+               OR EAX, EAX                             ;See if it's 0 (0 = NO Error)\r
+               JZ SHORT ALDMAP01               ;Go back & try again\r
+               JMP SHORT ALDMAPExit            ;ERROR!!\r
+ALDMAP02:\r
+                                                               ;EAX now has linear address\r
+                                                               ;EBX still has count of pages\r
+               CALL AddDMARun                  ;Does not return error\r
+                                                               ;EAX still has new linear address\r
+               MOV EBX, ppDMAMemRet            ;Get address of caller's pointer\r
+               MOV [EBX], EAX                  ;Give em new LinAdd\r
+\r
+               ;Set up to get the physical address of the linear we just gave 'em\r
+\r
+               MOV EBX, EAX                    ;Linear to EBX\r
+               CALL GetCrntJobNum              ;Leaves job num in EAX\r
+               CALL LinToPhy                   ;Leave Phy in EAX\r
+               MOV EBX,pdDMAPhyMemRet  ;Give them the physical address\r
+               MOV [EBX], EAX\r
+\r
+               XOR EAX, EAX                    ;No error\r
+\r
+ALDMAPExit:                                ;\r
+               PUSH EAX                                ;Save last error\r
+               PUSH MemExch                    ;Send a Semaphore msg (so next guy can get in)\r
+               PUSH 0FFFFFFF1h                 ;\r
+               PUSH 0FFFFFFF1h                 ;\r
+               CALL FWORD PTR _SendMsg ;\r
+               POP EAX                                 ;Get original error back (ignore kernel erc)\r
+               MOV ESP,EBP                             ;\r
+               POP EBP                 ;\r
+               RETF 12                 ;\r
+\r
+;=============================================================================\r
+; AliasMem --\r
+; This creates alias pages in the current job's PD/PTs if the current\r
+; PD is different than the PD for the job specified.  This allows\r
+; system services to access a caller memory for messaging WITHOUT having\r
+; to move data around.  The pages are create at USER protection level\r
+; even if they are in OS memory space (a service installed in OS memory).\r
+; Even if the address is only two bytes, if it crosses page boundries,\r
+; we need two pages.\r
+;\r
+; STEPS:\r
+; 1) See if the current PD = Specified Job PD. If so, Exit. (No alias needed).\r
+; 2) Calculate how many entries (pages) will be needed.\r
+; 3) See if they are available.\r
+; 3) Make PTE entries and return alias address to caller.\r
+;\r
+; Procedureal Interface :\r
+;\r
+;   AliasMem(pMem, dcbMem, dJobNum, ppAliasRet): dError\r
+;\r
+;   pMem   is the address to alias.\r
+;      dcbMem is the number of bytes needed for alias access.\r
+;      JobNum is the job number that pMem belongs to.\r
+;      ppAliasRet is the address to return the alias address to.\r
+;\r
+;\r
+;pMem          EQU [EBP+24]            ;\r
+;dcbMem                EQU [EBP+20]            ;\r
+;JobNum                EQU [EBP+16]            ;\r
+;ppAliasRet EQU [EBP+12]               ;\r
+\r
+PUBLIC __AliasMem               ;\r
+               PUSH EBP                ;\r
+               MOV EBP,ESP             ;\r
+               CALL GetCrntJobNum              ;Puts Current Job Num in EAX\r
+               MOV EBX, [EBP+16]               ;Get Job number for pMem\r
+               CMP EAX, EBX                    ;Are they the same Page Directory??\r
+               JNE ALSPBegin                   ;No, alias it\r
+               XOR EAX, EAX                    ;Yes, No Error\r
+               JMP ALSPDone                    ;Exit, we're done\r
+\r
+ALSPBegin:\r
+               ;Now wait our turn with memory management\r
+\r
+;              PUSH MemExch                    ;Wait at the MemExch for Msg\r
+;              MOV EAX, pRunTSS                ;Put Msg in callers TSS Message Area\r
+;              ADD EAX, TSS_Msg\r
+;              PUSH EAX\r
+;              CALL FWORD PTR _WaitMsg\r
+;              CMP EAX,0h                              ;Kernel Error??\r
+;              JNE ALSPDone                    ;Yes! Serious problem.\r
+\r
+               ; We're IN!\r
+\r
+ALSP00:\r
+               MOV EBX, [EBP+24]                       ;pMem into EAX\r
+               AND EBX, 0FFFh                          ;MODULO 4096  (remainder)\r
+               MOV EAX, [EBP+20]                       ;dcbMem\r
+               ADD EAX, EBX                            ;Add the remainder of address\r
+               SHR EAX, 12                                 ;EAX is nPages-1\r
+               INC EAX                                         ;EAX is now nPages we need!\r
+               MOV ECX, EAX                            ;Save nPages in ECX\r
+\r
+               ;Now we find out whos memory we are in to make alias\r
+               ;EAX is 256 for user space, 0 for OS\r
+               ;EBX is number of pages for run\r
+\r
+               CALL GetCrntJobNum                      ;See if it is OS based service\r
+               CMP EAX, 1                                      ;OS Job?\r
+               JE SHORT ALSP011                        ;Yes\r
+               MOV EAX, 256                            ;No, User memory\r
+               JMP SHORT ALSP01\r
+ALSP011:\r
+               XOR EAX, EAX                            ;Set up for OS memory space\r
+ALSP01:\r
+               MOV EBX, ECX                            ;Number of pages we need into EBX\r
+               CALL FindRun                            ;EAX has 0 or 256\r
+\r
+               ;EAX is now linear address or 0 if no run is large enough\r
+               ;EBX  still has count of pages\r
+\r
+               OR EAX, EAX                                     ;Was there enough PTEs?\r
+               JNZ ALSP04                                      ;Yes\r
+\r
+               CALL GetCrntJobNum                      ;See if it is OS based service\r
+               CMP EAX, 1                                      ;OS Job?\r
+               JE SHORT ALSP02                         ;Yes (RAB)\r
+\r
+               CALL AddUserPT                          ;No!  Add a new USER page table\r
+               JMP SHORT ALSP03\r
+ALSP02:\r
+               CALL AddOSPT                            ;No!  Add a new OS page table\r
+ALSP03:\r
+               OR EAX, EAX                                     ;0 = NO Error\r
+               JZ SHORT ALSP00                         ;Go back & try again\r
+               JMP SHORT ALSPExit                      ;ERROR!!\r
+\r
+ALSP04:\r
+               ;EAX has linear address (from find run) Sve in EDI\r
+               ;EBX still has number of pages to alias\r
+               ;Set ESI to linear address of pages to alias (from other job)\r
+               ;Set EDX job number of job we are aliasing\r
+\r
+               MOV EDI, EAX                            ;Save alias page address base\r
+               MOV ESI, [EBP+24]                       ;Address to alias\r
+               MOV EDX, [EBP+16]                       ;Job number\r
+               CALL AddAliasRun\r
+\r
+               ;Now, take new alias mem and add trailing bits to address\r
+               ;and return to caller so he knows address (EDI is lin add)\r
+\r
+               MOV EAX, [EBP+24]                       ;original pMem\r
+               AND EAX, 0FFFh                          ;Get remaining bits\r
+               ADD EDI, EAX\r
+               MOV ESI, [EBP+12]                       ;pAliasRet\r
+               MOV [ESI], EDI                          ;Returned address to caller!\r
+\r
+               XOR EAX, EAX                            ;Set to 0 (no error)\r
+\r
+               ;We are done\r
+ALSPExit:                                  ;\r
+;              PUSH EAX                                ;Save last error\r
+;              PUSH MemExch                    ;Send a Semaphore msg (so next guy can get in)\r
+;              PUSH 0FFFFFFF1h                         ;\r
+;              PUSH 0FFFFFFF1h                         ;\r
+;              CALL FWORD PTR _SendMsg ;\r
+;              POP EAX                                 ;Get original error back (ignore kernel erc)\r
+ALSPDone:\r
+               MOV ESP,EBP                             ;\r
+               POP EBP                 ;\r
+               RETF 16                 ;\r
+\r
+;=============================================================================\r
+; DeAliasMem --\r
+;\r
+; Procedureal Interface :\r
+;\r
+;              DeAliasMem(pAliasMem, dcbAliasBytes, JobNum):ercType\r
+;\r
+;   pAliasMem is the address which was given to you from the AliasMem call.\r
+;   This zeros out the page entries that were made during the AliasMem\r
+;   call. We do not need to go through the OS MEM semaphore exchange\r
+;      because we are only zeroing out PTE's one at a time. This\r
+;      WOULD NOT interfere with any memory allocation routines.\r
+;\r
+;      pAliasMem is the address to DeAlias\r
+;      dcbAliasBytes is the size of the original memory aliased\r
+;\r
+;pAliasMem             EQU [EBP+20]\r
+;dcbAliasBytes EQU [EBP+16]\r
+;AliasJobNum   EQU [EBP+12]\r
+\r
+PUBLIC __DeAliasMem            ;\r
+               PUSH EBP                ;\r
+               MOV EBP,ESP             ;\r
+\r
+               MOV EBX, [EBP+20]                       ;pMem into EBX\r
+               AND EBX, 0FFFh                          ;MODULO 4096  (Get remainder)\r
+               MOV EAX, [EBP+16]                       ;dcbMem\r
+               ADD EAX, EBX                            ;Add the remainder of address\r
+               SHR EAX, 12                                 ;EAX is nPages-1\r
+               INC EAX                                         ;EAX is now nPages we to dealias!\r
+               MOV ECX, EAX                            ;Number of pages into EDI & ECX\r
+               MOV EDI,ECX                                     ;Save also in EDI (for compare)\r
+               MOV EDX, [EBP+20]                       ;Linear Mem to DeAlias\r
+\r
+DALM01:\r
+               MOV EBX, EDX                    ;Address of next page to deallocate\r
+               MOV EAX, [EBP+12]               ;Job num into EAX for LinToPhy\r
+               CALL LinToPhy                   ;Call this to get address of PTE into ESI\r
+\r
+               ;Now we have Physical Address in EAX (we don't really need it)\r
+               ;and pointer to PTE in ESI (We NEEDED THIS).\r
+               ;See if PTE is an alias, if so just ZERO PTE.\r
+               ;DO NOT deallocate the physical page\r
+\r
+               MOV EBX, [ESI]                  ;Get PTE into EBX\r
+               TEST EBX, PRSNTBIT              ;Is page present (valid)???\r
+               JNZ DALM02                              ;Yes, it's page is present\r
+\r
+               CMP ECX, EDI                    ;NO! (did we do any at all)\r
+               JNE DALM011                             ;We did some.\r
+               MOV EAX, ErcBadLinAdd   ;None at all!\r
+               JMP SHORT DALMExit\r
+\r
+DALM011:\r
+               MOV EAX, ErcBadAlias    ;We dealiased what we could,\r
+               JMP SHORT DALMExit              ;but less than you asked for!\r
+\r
+DALM02:\r
+               TEST EBX, ALIASBIT              ;Is page an ALIAS?\r
+               JZ DALM03                               ;NO - DO not zero it!\r
+\r
+               ;If we got here the page is presnt and IS an alias\r
+               ;so we zero out the page.\r
+\r
+               XOR EAX, EAX                    ;\r
+               MOV [ESI], EAX                  ;ZERO PTE entry\r
+DALM03:\r
+\r
+               ADD EDX, 4096                   ;Next linear page\r
+               LOOP DALM01\r
+                                                               ;If we fall out EAX = ErcOK already\r
+DALMExit:\r
+               MOV ESP,EBP             ;\r
+               POP EBP                 ;\r
+               RETF 12                 ;\r
+\r
+\r
+;=============================================================================\r
+; DeAllocPage --\r
+;\r
+; Procedureal Interface :\r
+;\r
+;              DeAllocPage(pOrigMem, n4KPages):ercType\r
+;\r
+;   pOrigMem is a POINTER which should be point to memory page(s) to be\r
+;   deallocate.         The lower 12 bits of the pointer is actually ignored\r
+;   because we deallocate 4K pages.  This will free physical pages unless\r
+;   the page is marked as an alias. It will always free linear memory\r
+;   providing it is valid.  Even if you specify more pages than are valid\r
+;   this will deallocate or dealias as much as it can before reaching\r
+;   an invalid page.\r
+;\r
+;      n4KPages is the number of 4K pages to deallocate\r
+;\r
+pOrigMem       EQU [EBP+10h]\r
+n4KPagesD      EQU [EBP+0Ch]           ;\r
+\r
+PUBLIC __DeAllocPage:           ;\r
+               PUSH EBP                ;\r
+               MOV EBP,ESP             ;\r
+\r
+               PUSH MemExch                    ;Wait at the MemExch for Msg\r
+               MOV EAX, pRunTSS                ;Put Msg in callers TSS Message Area\r
+               ADD EAX, TSS_Msg\r
+               PUSH EAX\r
+               CALL FWORD PTR _WaitMsg\r
+               CMP EAX,0h                              ;Error??\r
+               JNE DAMExit             ;Yes!\r
+\r
+               MOV EDX, pOrigMem               ;Linear Mem to deallocate\r
+               AND EDX, 0FFFFF000h             ;Drop odd bits from address (MOD 4096)\r
+               MOV ECX, n4KPagesD              ;Number of pages to deallocate\r
+\r
+DAP01:\r
+               MOV EBX, EDX                    ;Address of next page to deallocate\r
+               CALL GetCrntJobNum              ;Leave Job# in EAX for LinToPhy\r
+               CALL LinToPhy                   ;\r
+\r
+               ;Now we have Physical Address in EAX\r
+               ;and pointer to PTE in ESI.\r
+               ;See if PTE is an alias, if so just ZERO PTE,\r
+               ;else deallocate physical page THEN zero PTE\r
+\r
+               MOV EBX, [ESI]                  ;Get PTE into EBX\r
+               TEST EBX, PRSNTBIT              ;Is page present (valid)???\r
+               JNZ DAP02                               ;Yes, it's page is present\r
+\r
+               CMP ECX, n4KPagesD              ;NO! (did we do any at all)\r
+               JNE DAP011                              ;We did some..\r
+               MOV EAX, ErcBadLinAdd   ;None at all!\r
+               JMP SHORT DAMExit\r
+\r
+DAP011:\r
+               MOV EAX, ErcShortMem    ;We deallocated what we could,\r
+               JMP SHORT DAMExit               ;but less than you asked for!\r
+\r
+DAP02:\r
+               TEST EBX, ALIASBIT              ;Is page an ALIAS?\r
+               JNZ DAP03                               ;Yes, it's an Alias\r
+\r
+               ;If we got here the page is presnt and NOT an alias\r
+               ;so we must unmark (release) the physical page.\r
+\r
+               AND EBX, 0FFFFF000h             ;get rid of OS bits\r
+               CALL UnMarkPage                 ;\r
+\r
+DAP03:\r
+               XOR EAX, EAX                    ;\r
+               MOV [ESI], EAX                  ;ZERO PTE entry\r
+\r
+               ADD EDX, 4096                   ;Next linear page\r
+               LOOP DAP01\r
+                                                               ;If we fall out EAX = ErcOK already\r
+DAMExit:\r
+               PUSH EAX                                ;save Memory error\r
+\r
+               PUSH MemExch                    ;Send a dummy message to pick up\r
+               PUSH 0FFFFFFF1h                 ; so next guy can get in\r
+               PUSH 0FFFFFFF1h\r
+               CALL FWORD PTR _SendMsg ;\r
+               CMP EAX, 0                              ;Kernel error has priority\r
+               JNE DAMExit1                    ; over memory error\r
+\r
+               POP EAX                                 ;get Memory error back\r
+\r
+DAMExit1:\r
+               MOV ESP,EBP             ;\r
+               POP EBP                 ;\r
+               RETF 8                  ;\r
+\r
+;=============================================================================\r
+; QueryMemPages --\r
+;\r
+; Procedureal Interface :\r
+;\r
+;              QueryMemPages(pdnPagesRet):ercType\r
+;\r
+;      pdnPagesRet is a pointer where you want the count of pages\r
+;   left available returned\r
+;\r
+pMemleft       EQU [EBP+0Ch]\r
+\r
+PUBLIC __QueryPages:            ;\r
+               PUSH EBP                ;\r
+               MOV EBP,ESP             ;\r
+\r
+               MOV ESI, pMemLeft\r
+               MOV EAX,  _nPagesFree\r
+               MOV [ESI], EAX\r
+               XOR EAX, EAX                    ;No Error\r
+\r
+               MOV ESP,EBP             ;\r
+               POP EBP                 ;\r
+               RETF 4                  ;\r
+\r
+;==============================================================================\r
+;\r
+; GetPhyAdd -- This returns the phyical address for a linear address\r
+;\r
+;\r
+; Procedureal Interface :\r
+;\r
+;              GetPhyAdd(JobNum, LinAdd, pPhyRet):ercType\r
+;\r
+;      LinAdd is the Linear address you want the physical address for\r
+;      pPhyRet points to the unsigned long where Phyadd is returned\r
+;\r
+;\r
+;JobNum  EQU [EBP+20]\r
+;LinAdd  EQU [EBP+16]\r
+;pPhyRet EQU [EBP+12]\r
+;\r
+\r
+PUBLIC __GetPhyAdd:                    ;\r
+               PUSH EBP                ;\r
+               MOV EBP,ESP             ;\r
+               MOV EBX, [EBP+16]               ; Linear Address\r
+               MOV EAX, [EBP+20]               ; Job Number\r
+               CALL LinToPhy\r
+               MOV ESI, [EBP+12]               ; pPhyRet\r
+               MOV [ESI], EAX                  ;\r
+               XOR EAX, EAX                    ; No Error\r
+               MOV ESP,EBP             ;\r
+               POP EBP                 ;\r
+               RETF 12                 ;\r
+\r
+\r
+;====== End Of Module =====================\r