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