]> 4ch.mooo.com Git - 16.git/commitdiff
found page manager~
authorsparky4 <sparky4@cock.li>
Mon, 6 Jul 2015 13:58:27 +0000 (08:58 -0500)
committersparky4 <sparky4@cock.li>
Mon, 6 Jul 2015 13:58:27 +0000 (08:58 -0500)
new file:   src/lib/16_pm.c
new file:   src/lib/16_pm.h

src/lib/16_pm.c [new file with mode: 0644]
src/lib/16_pm.h [new file with mode: 0644]

diff --git a/src/lib/16_pm.c b/src/lib/16_pm.c
new file mode 100644 (file)
index 0000000..57c15aa
--- /dev/null
@@ -0,0 +1,1199 @@
+//\r
+//     ID_PM.C\r
+//     Id Engine's Page Manager v1.0\r
+//     Primary coder: Jason Blochowiak\r
+//\r
+\r
+#include "ID_HEADS.H"\r
+#pragma hdrstop\r
+\r
+//     Main Mem specific variables\r
+       boolean                 MainPresent;\r
+       memptr                  MainMemPages[PMMaxMainMem];\r
+       PMBlockAttr             MainMemUsed[PMMaxMainMem];\r
+       int                             MainPagesAvail;\r
+\r
+//     EMS specific variables\r
+       boolean                 EMSPresent;\r
+       word                    EMSAvail,EMSPagesAvail,EMSHandle,\r
+                                       EMSPageFrame,EMSPhysicalPage;\r
+       EMSListStruct   EMSList[EMSFrameCount];\r
+\r
+//     XMS specific variables\r
+       boolean                 XMSPresent;\r
+       word                    XMSAvail,XMSPagesAvail,XMSHandle;\r
+       longword                XMSDriver;\r
+       int                             XMSProtectPage = -1;\r
+\r
+//     File specific variables\r
+       char                    PageFileName[13] = {"VSWAP."};\r
+       int                             PageFile = -1;\r
+       word                    ChunksInFile;\r
+       word                    PMSpriteStart,PMSoundStart;\r
+\r
+//     General usage variables\r
+       boolean                 PMStarted,\r
+                                       PMPanicMode,\r
+                                       PMThrashing;\r
+       word                    XMSPagesUsed,\r
+                                       EMSPagesUsed,\r
+                                       MainPagesUsed,\r
+                                       PMNumBlocks;\r
+       long                    PMFrameCount;\r
+       PageListStruct  far *PMPages,\r
+                                       _seg *PMSegPages;\r
+\r
+static char            *ParmStrings[] = {"nomain","noems","noxms",nil};\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+//     EMS Management code\r
+//\r
+/////////////////////////////////////////////////////////////////////////////\r
+\r
+//\r
+//     PML_MapEMS() - Maps a logical page to a physical page\r
+//\r
+void\r
+PML_MapEMS(word logical,word physical)\r
+{\r
+       _AL = physical;\r
+       _BX = logical;\r
+       _DX = EMSHandle;\r
+       _AH = EMS_MAPPAGE;\r
+asm    int     EMS_INT\r
+\r
+       if (_AH)\r
+               Quit("PML_MapEMS: Page mapping failed");\r
+}\r
+\r
+//\r
+//     PML_StartupEMS() - Sets up EMS for Page Mgr's use\r
+//             Checks to see if EMS driver is present\r
+//      Verifies that EMS hardware is present\r
+//             Make sure that EMS version is 3.2 or later\r
+//             If there's more than our minimum (2 pages) available, allocate it (up\r
+//                     to the maximum we need)\r
+//\r
+\r
+       char    EMMDriverName[9] = "EMMXXXX0";\r
+\r
+boolean\r
+PML_StartupEMS(void)\r
+{\r
+       int             i;\r
+       long    size;\r
+\r
+       EMSPresent = false;                     // Assume that we'll fail\r
+       EMSAvail = 0;\r
+\r
+       _DX = (word)EMMDriverName;\r
+       _AX = 0x3d00;\r
+       geninterrupt(0x21);                     // try to open EMMXXXX0 device\r
+asm    jnc     gothandle\r
+       goto error;\r
+\r
+gothandle:\r
+       _BX = _AX;\r
+       _AX = 0x4400;\r
+       geninterrupt(0x21);                     // get device info\r
+asm    jnc     gotinfo;\r
+       goto error;\r
+\r
+gotinfo:\r
+asm    and     dx,0x80\r
+       if (!_DX)\r
+               goto error;\r
+\r
+       _AX = 0x4407;\r
+       geninterrupt(0x21);                     // get status\r
+asm    jc      error\r
+       if (!_AL)\r
+               goto error;\r
+\r
+       _AH = 0x3e;\r
+       geninterrupt(0x21);                     // close handle\r
+\r
+       _AH = EMS_STATUS;\r
+       geninterrupt(EMS_INT);\r
+       if (_AH)\r
+               goto error;                             // make sure EMS hardware is present\r
+\r
+       _AH = EMS_VERSION;\r
+       geninterrupt(EMS_INT);\r
+       if (_AH || (_AL < 0x32))        // only work on EMS 3.2 or greater (silly, but...)\r
+               goto error;\r
+\r
+       _AH = EMS_GETFRAME;\r
+       geninterrupt(EMS_INT);\r
+       if (_AH)\r
+               goto error;                             // find the page frame address\r
+       EMSPageFrame = _BX;\r
+\r
+       _AH = EMS_GETPAGES;\r
+       geninterrupt(EMS_INT);\r
+       if (_AH)\r
+               goto error;\r
+       if (_BX < 2)\r
+               goto error;             // Require at least 2 pages (32k)\r
+       EMSAvail = _BX;\r
+\r
+       // Don't hog all available EMS\r
+       size = EMSAvail * (long)EMSPageSize;\r
+       if (size - (EMSPageSize * 2) > (ChunksInFile * (long)PMPageSize))\r
+       {\r
+               size = (ChunksInFile * (long)PMPageSize) + EMSPageSize;\r
+               EMSAvail = size / EMSPageSize;\r
+       }\r
+\r
+       _AH = EMS_ALLOCPAGES;\r
+       _BX = EMSAvail;\r
+       geninterrupt(EMS_INT);\r
+       if (_AH)\r
+               goto error;\r
+       EMSHandle = _DX;\r
+\r
+       mminfo.EMSmem += EMSAvail * (long)EMSPageSize;\r
+\r
+       // Initialize EMS mapping cache\r
+       for (i = 0;i < EMSFrameCount;i++)\r
+               EMSList[i].baseEMSPage = -1;\r
+\r
+       EMSPresent = true;                      // We have EMS\r
+\r
+error:\r
+       return(EMSPresent);\r
+}\r
+\r
+//\r
+//     PML_ShutdownEMS() - If EMS was used, deallocate it\r
+//\r
+void\r
+PML_ShutdownEMS(void)\r
+{\r
+       if (EMSPresent)\r
+       {\r
+       asm     mov     ah,EMS_FREEPAGES\r
+       asm     mov     dx,[EMSHandle]\r
+       asm     int     EMS_INT\r
+               if (_AH)\r
+                       Quit ("PML_ShutdownEMS: Error freeing EMS");\r
+       }\r
+}\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+//     XMS Management code\r
+//\r
+/////////////////////////////////////////////////////////////////////////////\r
+\r
+//\r
+//     PML_StartupXMS() - Starts up XMS for the Page Mgr's use\r
+//             Checks for presence of an XMS driver\r
+//             Makes sure that there's at least a page of XMS available\r
+//             Allocates any remaining XMS (rounded down to the nearest page size)\r
+//\r
+boolean\r
+PML_StartupXMS(void)\r
+{\r
+       XMSPresent = false;                                     // Assume failure\r
+       XMSAvail = 0;\r
+\r
+asm    mov     ax,0x4300\r
+asm    int     XMS_INT                                         // Check for presence of XMS driver\r
+       if (_AL != 0x80)\r
+               goto error;\r
+\r
+\r
+asm    mov     ax,0x4310\r
+asm    int     XMS_INT                                                 // Get address of XMS driver\r
+asm    mov     [WORD PTR XMSDriver],bx\r
+asm    mov     [WORD PTR XMSDriver+2],es               // function pointer to XMS driver\r
+\r
+       XMS_CALL(XMS_QUERYFREE);                        // Find out how much XMS is available\r
+       XMSAvail = _AX;\r
+       if (!_AX)                               // AJR: bugfix 10/8/92\r
+               goto error;\r
+\r
+       XMSAvail &= ~(PMPageSizeKB - 1);        // Round off to nearest page size\r
+       if (XMSAvail < (PMPageSizeKB * 2))      // Need at least 2 pages\r
+               goto error;\r
+\r
+       _DX = XMSAvail;\r
+       XMS_CALL(XMS_ALLOC);                            // And do the allocation\r
+       XMSHandle = _DX;\r
+\r
+       if (!_AX)                               // AJR: bugfix 10/8/92\r
+       {\r
+               XMSAvail = 0;\r
+               goto error;\r
+       }\r
+\r
+       mminfo.XMSmem += XMSAvail * 1024;\r
+\r
+       XMSPresent = true;\r
+error:\r
+       return(XMSPresent);\r
+}\r
+\r
+//\r
+//     PML_XMSCopy() - Copies a main/EMS page to or from XMS\r
+//             Will round an odd-length request up to the next even value\r
+//\r
+void\r
+PML_XMSCopy(boolean toxms,byte far *addr,word xmspage,word length)\r
+{\r
+       longword        xoffset;\r
+       struct\r
+       {\r
+               longword        length;\r
+               word            source_handle;\r
+               longword        source_offset;\r
+               word            target_handle;\r
+               longword        target_offset;\r
+       } copy;\r
+\r
+       if (!addr)\r
+               Quit("PML_XMSCopy: zero address");\r
+\r
+       xoffset = (longword)xmspage * PMPageSize;\r
+\r
+       copy.length = (length + 1) & ~1;\r
+       copy.source_handle = toxms? 0 : XMSHandle;\r
+       copy.source_offset = toxms? (long)addr : xoffset;\r
+       copy.target_handle = toxms? XMSHandle : 0;\r
+       copy.target_offset = toxms? xoffset : (long)addr;\r
+\r
+asm    push si\r
+       _SI = (word)&copy;\r
+       XMS_CALL(XMS_MOVE);\r
+asm    pop     si\r
+       if (!_AX)\r
+               Quit("PML_XMSCopy: Error on copy");\r
+}\r
+\r
+#if 1\r
+#define        PML_CopyToXMS(s,t,l)    PML_XMSCopy(true,(s),(t),(l))\r
+#define        PML_CopyFromXMS(t,s,l)  PML_XMSCopy(false,(t),(s),(l))\r
+#else\r
+//\r
+//     PML_CopyToXMS() - Copies the specified number of bytes from the real mode\r
+//             segment address to the specified XMS page\r
+//\r
+void\r
+PML_CopyToXMS(byte far *source,int targetpage,word length)\r
+{\r
+       PML_XMSCopy(true,source,targetpage,length);\r
+}\r
+\r
+//\r
+//     PML_CopyFromXMS() - Copies the specified number of bytes from an XMS\r
+//             page to the specified real mode address\r
+//\r
+void\r
+PML_CopyFromXMS(byte far *target,int sourcepage,word length)\r
+{\r
+       PML_XMSCopy(false,target,sourcepage,length);\r
+}\r
+#endif\r
+\r
+//\r
+//     PML_ShutdownXMS()\r
+//\r
+void\r
+PML_ShutdownXMS(void)\r
+{\r
+       if (XMSPresent)\r
+       {\r
+               _DX = XMSHandle;\r
+               XMS_CALL(XMS_FREE);\r
+               if (_BL)\r
+                       Quit("PML_ShutdownXMS: Error freeing XMS");\r
+       }\r
+}\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+//     Main memory code\r
+//\r
+/////////////////////////////////////////////////////////////////////////////\r
+\r
+//\r
+//     PM_SetMainMemPurge() - Sets the purge level for all allocated main memory\r
+//             blocks. This shouldn't be called directly - the PM_LockMainMem() and\r
+//             PM_UnlockMainMem() macros should be used instead.\r
+//\r
+void\r
+PM_SetMainMemPurge(int level)\r
+{\r
+       int     i;\r
+\r
+       for (i = 0;i < PMMaxMainMem;i++)\r
+               if (MainMemPages[i])\r
+                       MM_SetPurge(&MainMemPages[i],level);\r
+}\r
+\r
+//\r
+//     PM_CheckMainMem() - If something besides the Page Mgr makes requests of\r
+//             the Memory Mgr, some of the Page Mgr's blocks may have been purged,\r
+//             so this function runs through the block list and checks to see if\r
+//             any of the blocks have been purged. If so, it marks the corresponding\r
+//             page as purged & unlocked, then goes through the block list and\r
+//             tries to reallocate any blocks that have been purged.\r
+//     This routine now calls PM_LockMainMem() to make sure that any allocation\r
+//             attempts made during the block reallocation sweep don't purge any\r
+//             of the other blocks. Because PM_LockMainMem() is called,\r
+//             PM_UnlockMainMem() needs to be called before any other part of the\r
+//             program makes allocation requests of the Memory Mgr.\r
+//\r
+void\r
+PM_CheckMainMem(void)\r
+{\r
+       boolean                 allocfailed;\r
+       int                             i,n;\r
+       memptr                  *p;\r
+       PMBlockAttr             *used;\r
+       PageListStruct  far *page;\r
+\r
+       if (!MainPresent)\r
+               return;\r
+\r
+       for (i = 0,page = PMPages;i < ChunksInFile;i++,page++)\r
+       {\r
+               n = page->mainPage;\r
+               if (n != -1)                                            // Is the page using main memory?\r
+               {\r
+                       if (!MainMemPages[n])                   // Yep, was the block purged?\r
+                       {\r
+                               page->mainPage = -1;            // Yes, mark page as purged & unlocked\r
+                               page->locked = pml_Unlocked;\r
+                       }\r
+               }\r
+       }\r
+\r
+       // Prevent allocation attempts from purging any of our other blocks\r
+       PM_LockMainMem();\r
+       allocfailed = false;\r
+       for (i = 0,p = MainMemPages,used = MainMemUsed;i < PMMaxMainMem;i++,p++,used++)\r
+       {\r
+               if (!*p)                                                        // If the page got purged\r
+               {\r
+                       if (*used & pmba_Allocated)             // If it was allocated\r
+                       {\r
+                               *used &= ~pmba_Allocated;       // Mark as unallocated\r
+                               MainPagesAvail--;                       // and decrease available count\r
+                       }\r
+\r
+                       if (*used & pmba_Used)                  // If it was used\r
+                       {\r
+                               *used &= ~pmba_Used;            // Mark as unused\r
+                               MainPagesUsed--;                        // and decrease used count\r
+                       }\r
+\r
+                       if (!allocfailed)\r
+                       {\r
+                               MM_BombOnError(false);\r
+                               MM_GetPtr(p,PMPageSize);                // Try to reallocate\r
+                               if (mmerror)                                    // If it failed,\r
+                                       allocfailed = true;                     //  don't try any more allocations\r
+                               else                                                    // If it worked,\r
+                               {\r
+                                       *used |= pmba_Allocated;        // Mark as allocated\r
+                                       MainPagesAvail++;                       // and increase available count\r
+                               }\r
+                               MM_BombOnError(true);\r
+                       }\r
+               }\r
+       }\r
+       if (mmerror)\r
+               mmerror = false;\r
+}\r
+\r
+//\r
+//     PML_StartupMainMem() - Allocates as much main memory as is possible for\r
+//             the Page Mgr. The memory is allocated as non-purgeable, so if it's\r
+//             necessary to make requests of the Memory Mgr, PM_UnlockMainMem()\r
+//             needs to be called.\r
+//\r
+void\r
+PML_StartupMainMem(void)\r
+{\r
+       int             i,n;\r
+       memptr  *p;\r
+\r
+       MainPagesAvail = 0;\r
+       MM_BombOnError(false);\r
+       for (i = 0,p = MainMemPages;i < PMMaxMainMem;i++,p++)\r
+       {\r
+               MM_GetPtr(p,PMPageSize);\r
+               if (mmerror)\r
+                       break;\r
+\r
+               MainPagesAvail++;\r
+               MainMemUsed[i] = pmba_Allocated;\r
+       }\r
+       MM_BombOnError(true);\r
+       if (mmerror)\r
+               mmerror = false;\r
+       if (MainPagesAvail < PMMinMainMem)\r
+               Quit("PM_SetupMainMem: Not enough main memory");\r
+       MainPresent = true;\r
+}\r
+\r
+//\r
+//     PML_ShutdownMainMem() - Frees all of the main memory blocks used by the\r
+//             Page Mgr.\r
+//\r
+void\r
+PML_ShutdownMainMem(void)\r
+{\r
+       int             i;\r
+       memptr  *p;\r
+\r
+       // DEBUG - mark pages as unallocated & decrease page count as appropriate\r
+       for (i = 0,p = MainMemPages;i < PMMaxMainMem;i++,p++)\r
+               if (*p)\r
+                       MM_FreePtr(p);\r
+}\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+//     File management code\r
+//\r
+/////////////////////////////////////////////////////////////////////////////\r
+\r
+//\r
+//     PML_ReadFromFile() - Reads some data in from the page file\r
+//\r
+void\r
+PML_ReadFromFile(byte far *buf,long offset,word length)\r
+{\r
+       if (!buf)\r
+               Quit("PML_ReadFromFile: Null pointer");\r
+       if (!offset)\r
+               Quit("PML_ReadFromFile: Zero offset");\r
+       if (lseek(PageFile,offset,SEEK_SET) != offset)\r
+               Quit("PML_ReadFromFile: Seek failed");\r
+       if (!CA_FarRead(PageFile,buf,length))\r
+               Quit("PML_ReadFromFile: Read failed");\r
+}\r
+\r
+//\r
+//     PML_OpenPageFile() - Opens the page file and sets up the page info\r
+//\r
+void\r
+PML_OpenPageFile(void)\r
+{\r
+       int                             i;\r
+       long                    size;\r
+       void                    _seg *buf;\r
+       longword                far *offsetptr;\r
+       word                    far *lengthptr;\r
+       PageListStruct  far *page;\r
+\r
+       PageFile = open(PageFileName,O_RDONLY + O_BINARY);\r
+       if (PageFile == -1)\r
+               Quit("PML_OpenPageFile: Unable to open page file");\r
+\r
+       // Read in header variables\r
+       read(PageFile,&ChunksInFile,sizeof(ChunksInFile));\r
+       read(PageFile,&PMSpriteStart,sizeof(PMSpriteStart));\r
+       read(PageFile,&PMSoundStart,sizeof(PMSoundStart));\r
+\r
+       // Allocate and clear the page list\r
+       PMNumBlocks = ChunksInFile;\r
+       MM_GetPtr(&(memptr)PMSegPages,sizeof(PageListStruct) * PMNumBlocks);\r
+       MM_SetLock(&(memptr)PMSegPages,true);\r
+       PMPages = (PageListStruct far *)PMSegPages;\r
+       _fmemset(PMPages,0,sizeof(PageListStruct) * PMNumBlocks);\r
+\r
+       // Read in the chunk offsets\r
+       size = sizeof(longword) * ChunksInFile;\r
+       MM_GetPtr(&buf,size);\r
+       if (!CA_FarRead(PageFile,(byte far *)buf,size))\r
+               Quit("PML_OpenPageFile: Offset read failed");\r
+       offsetptr = (longword far *)buf;\r
+       for (i = 0,page = PMPages;i < ChunksInFile;i++,page++)\r
+               page->offset = *offsetptr++;\r
+       MM_FreePtr(&buf);\r
+\r
+       // Read in the chunk lengths\r
+       size = sizeof(word) * ChunksInFile;\r
+       MM_GetPtr(&buf,size);\r
+       if (!CA_FarRead(PageFile,(byte far *)buf,size))\r
+               Quit("PML_OpenPageFile: Length read failed");\r
+       lengthptr = (word far *)buf;\r
+       for (i = 0,page = PMPages;i < ChunksInFile;i++,page++)\r
+               page->length = *lengthptr++;\r
+       MM_FreePtr(&buf);\r
+}\r
+\r
+//\r
+//  PML_ClosePageFile() - Closes the page file\r
+//\r
+void\r
+PML_ClosePageFile(void)\r
+{\r
+       if (PageFile != -1)\r
+               close(PageFile);\r
+       if (PMSegPages)\r
+       {\r
+               MM_SetLock(&(memptr)PMSegPages,false);\r
+               MM_FreePtr(&(void _seg *)PMSegPages);\r
+       }\r
+}\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+//     Allocation, etc., code\r
+//\r
+/////////////////////////////////////////////////////////////////////////////\r
+\r
+//\r
+//     PML_GetEMSAddress()\r
+//\r
+//             Page is in EMS, so figure out which EMS physical page should be used\r
+//             to map our page in. If normal page, use EMS physical page 3, else\r
+//             use the physical page specified by the lock type\r
+//\r
+#if 1\r
+#pragma argsused       // DEBUG - remove lock parameter\r
+memptr\r
+PML_GetEMSAddress(int page,PMLockType lock)\r
+{\r
+       int             i,emspage;\r
+       word    emsoff,emsbase,offset;\r
+\r
+       emsoff = page & (PMEMSSubPage - 1);\r
+       emsbase = page - emsoff;\r
+\r
+       emspage = -1;\r
+       // See if this page is already mapped in\r
+       for (i = 0;i < EMSFrameCount;i++)\r
+       {\r
+               if (EMSList[i].baseEMSPage == emsbase)\r
+               {\r
+                       emspage = i;    // Yep - don't do a redundant remapping\r
+                       break;\r
+               }\r
+       }\r
+\r
+       // If page isn't already mapped in, find LRU EMS frame, and use it\r
+       if (emspage == -1)\r
+       {\r
+               longword last = MAXLONG;\r
+               for (i = 0;i < EMSFrameCount;i++)\r
+               {\r
+                       if (EMSList[i].lastHit < last)\r
+                       {\r
+                               emspage = i;\r
+                               last = EMSList[i].lastHit;\r
+                       }\r
+               }\r
+\r
+               EMSList[emspage].baseEMSPage = emsbase;\r
+               PML_MapEMS(page / PMEMSSubPage,emspage);\r
+       }\r
+\r
+       if (emspage == -1)\r
+               Quit("PML_GetEMSAddress: EMS find failed");\r
+\r
+       EMSList[emspage].lastHit = PMFrameCount;\r
+       offset = emspage * EMSPageSizeSeg;\r
+       offset += emsoff * PMPageSizeSeg;\r
+       return((memptr)(EMSPageFrame + offset));\r
+}\r
+#else\r
+memptr\r
+PML_GetEMSAddress(int page,PMLockType lock)\r
+{\r
+       word    emspage;\r
+\r
+       emspage = (lock < pml_EMSLock)? 3 : (lock - pml_EMSLock);\r
+\r
+       PML_MapEMS(page / PMEMSSubPage,emspage);\r
+\r
+       return((memptr)(EMSPageFrame + (emspage * EMSPageSizeSeg)\r
+                       + ((page & (PMEMSSubPage - 1)) * PMPageSizeSeg)));\r
+}\r
+#endif\r
+\r
+//\r
+//     PM_GetPageAddress() - Returns the address of a given page\r
+//             Maps in EMS if necessary\r
+//             Returns nil if block isn't cached into Main Memory or EMS\r
+//\r
+//\r
+memptr\r
+PM_GetPageAddress(int pagenum)\r
+{\r
+       PageListStruct  far *page;\r
+\r
+       page = &PMPages[pagenum];\r
+       if (page->mainPage != -1)\r
+               return(MainMemPages[page->mainPage]);\r
+       else if (page->emsPage != -1)\r
+               return(PML_GetEMSAddress(page->emsPage,page->locked));\r
+       else\r
+               return(nil);\r
+}\r
+\r
+//\r
+//     PML_GiveLRUPage() - Returns the page # of the least recently used\r
+//             present & unlocked main/EMS page (or main page if mainonly is true)\r
+//\r
+int\r
+PML_GiveLRUPage(boolean mainonly)\r
+{\r
+       int                             i,lru;\r
+       long                    last;\r
+       PageListStruct  far *page;\r
+\r
+       for (i = 0,page = PMPages,lru = -1,last = MAXLONG;i < ChunksInFile;i++,page++)\r
+       {\r
+               if\r
+               (\r
+                       (page->lastHit < last)\r
+               &&      ((page->emsPage != -1) || (page->mainPage != -1))\r
+               &&      (page->locked == pml_Unlocked)\r
+               &&      (!(mainonly && (page->mainPage == -1)))\r
+               )\r
+               {\r
+                       last = page->lastHit;\r
+                       lru = i;\r
+               }\r
+       }\r
+\r
+       if (lru == -1)\r
+               Quit("PML_GiveLRUPage: LRU Search failed");\r
+       return(lru);\r
+}\r
+\r
+//\r
+//     PML_GiveLRUXMSPage() - Returns the page # of the least recently used\r
+//             (and present) XMS page.\r
+//     This routine won't return the XMS page protected (by XMSProtectPage)\r
+//\r
+int\r
+PML_GiveLRUXMSPage(void)\r
+{\r
+       int                             i,lru;\r
+       long                    last;\r
+       PageListStruct  far *page;\r
+\r
+       for (i = 0,page = PMPages,lru = -1,last = MAXLONG;i < ChunksInFile;i++,page++)\r
+       {\r
+               if\r
+               (\r
+                       (page->xmsPage != -1)\r
+               &&      (page->lastHit < last)\r
+               &&      (i != XMSProtectPage)\r
+               )\r
+               {\r
+                       last = page->lastHit;\r
+                       lru = i;\r
+               }\r
+       }\r
+       return(lru);\r
+}\r
+\r
+//\r
+//     PML_PutPageInXMS() - If page isn't in XMS, find LRU XMS page and replace\r
+//             it with the main/EMS page\r
+//\r
+void\r
+PML_PutPageInXMS(int pagenum)\r
+{\r
+       int                             usexms;\r
+       PageListStruct  far *page;\r
+\r
+       if (!XMSPresent)\r
+               return;\r
+\r
+       page = &PMPages[pagenum];\r
+       if (page->xmsPage != -1)\r
+               return;                                 // Already in XMS\r
+\r
+       if (XMSPagesUsed < XMSPagesAvail)\r
+               page->xmsPage = XMSPagesUsed++;\r
+       else\r
+       {\r
+               usexms = PML_GiveLRUXMSPage();\r
+               if (usexms == -1)\r
+                       Quit("PML_PutPageInXMS: No XMS LRU");\r
+               page->xmsPage = PMPages[usexms].xmsPage;\r
+               PMPages[usexms].xmsPage = -1;\r
+       }\r
+       PML_CopyToXMS(PM_GetPageAddress(pagenum),page->xmsPage,page->length);\r
+}\r
+\r
+//\r
+//     PML_TransferPageSpace() - A page is being replaced, so give the new page\r
+//             the old one's address space. Returns the address of the new page.\r
+//\r
+memptr\r
+PML_TransferPageSpace(int orig,int new)\r
+{\r
+       memptr                  addr;\r
+       PageListStruct  far *origpage,far *newpage;\r
+\r
+       if (orig == new)\r
+               Quit("PML_TransferPageSpace: Identity replacement");\r
+\r
+       origpage = &PMPages[orig];\r
+       newpage = &PMPages[new];\r
+\r
+       if (origpage->locked != pml_Unlocked)\r
+               Quit("PML_TransferPageSpace: Killing locked page");\r
+\r
+       if ((origpage->emsPage == -1) && (origpage->mainPage == -1))\r
+               Quit("PML_TransferPageSpace: Reusing non-existent page");\r
+\r
+       // Copy page that's about to be purged into XMS\r
+       PML_PutPageInXMS(orig);\r
+\r
+       // Get the address, and force EMS into a physical page if necessary\r
+       addr = PM_GetPageAddress(orig);\r
+\r
+       // Steal the address\r
+       newpage->emsPage = origpage->emsPage;\r
+       newpage->mainPage = origpage->mainPage;\r
+\r
+       // Mark replaced page as purged\r
+       origpage->mainPage = origpage->emsPage = -1;\r
+\r
+       if (!addr)\r
+               Quit("PML_TransferPageSpace: Zero replacement");\r
+\r
+       return(addr);\r
+}\r
+\r
+//\r
+//     PML_GetAPageBuffer() - A page buffer is needed. Either get it from the\r
+//             main/EMS free pool, or use PML_GiveLRUPage() to find which page to\r
+//             steal the buffer from. Returns a far pointer to the page buffer, and\r
+//             sets the fields inside the given page structure appropriately.\r
+//             If mainonly is true, free EMS will be ignored, and only main pages\r
+//             will be looked at by PML_GiveLRUPage().\r
+//\r
+byte far *\r
+PML_GetAPageBuffer(int pagenum,boolean mainonly)\r
+{\r
+       byte                    far *addr = nil;\r
+       int                             i,n;\r
+       PMBlockAttr             *used;\r
+       PageListStruct  far *page;\r
+\r
+       page = &PMPages[pagenum];\r
+       if ((EMSPagesUsed < EMSPagesAvail) && !mainonly)\r
+       {\r
+               // There's remaining EMS - use it\r
+               page->emsPage = EMSPagesUsed++;\r
+               addr = PML_GetEMSAddress(page->emsPage,page->locked);\r
+       }\r
+       else if (MainPagesUsed < MainPagesAvail)\r
+       {\r
+               // There's remaining main memory - use it\r
+               for (i = 0,n = -1,used = MainMemUsed;i < PMMaxMainMem;i++,used++)\r
+               {\r
+                       if ((*used & pmba_Allocated) && !(*used & pmba_Used))\r
+                       {\r
+                               n = i;\r
+                               *used |= pmba_Used;\r
+                               break;\r
+                       }\r
+               }\r
+               if (n == -1)\r
+                       Quit("PML_GetPageBuffer: MainPagesAvail lied");\r
+               addr = MainMemPages[n];\r
+               if (!addr)\r
+                       Quit("PML_GetPageBuffer: Purged main block");\r
+               page->mainPage = n;\r
+               MainPagesUsed++;\r
+       }\r
+       else\r
+               addr = PML_TransferPageSpace(PML_GiveLRUPage(mainonly),pagenum);\r
+\r
+       if (!addr)\r
+               Quit("PML_GetPageBuffer: Search failed");\r
+       return(addr);\r
+}\r
+\r
+//\r
+//     PML_GetPageFromXMS() - If page is in XMS, find LRU main/EMS page and\r
+//             replace it with the page from XMS. If mainonly is true, will only\r
+//             search for LRU main page.\r
+//     XMSProtectPage is set to the page to be retrieved from XMS, so that if\r
+//             the page from which we're stealing the main/EMS from isn't in XMS,\r
+//             it won't copy over the page that we're trying to get from XMS.\r
+//             (pages that are being purged are copied into XMS, if possible)\r
+//\r
+memptr\r
+PML_GetPageFromXMS(int pagenum,boolean mainonly)\r
+{\r
+       byte                    far *checkaddr;\r
+       memptr                  addr = nil;\r
+       PageListStruct  far *page;\r
+\r
+       page = &PMPages[pagenum];\r
+       if (XMSPresent && (page->xmsPage != -1))\r
+       {\r
+               XMSProtectPage = pagenum;\r
+               checkaddr = PML_GetAPageBuffer(pagenum,mainonly);\r
+               if (FP_OFF(checkaddr))\r
+                       Quit("PML_GetPageFromXMS: Non segment pointer");\r
+               addr = (memptr)FP_SEG(checkaddr);\r
+               PML_CopyFromXMS(addr,page->xmsPage,page->length);\r
+               XMSProtectPage = -1;\r
+       }\r
+\r
+       return(addr);\r
+}\r
+\r
+//\r
+//     PML_LoadPage() - A page is not in main/EMS memory, and it's not in XMS.\r
+//             Load it into either main or EMS. If mainonly is true, the page will\r
+//             only be loaded into main.\r
+//\r
+void\r
+PML_LoadPage(int pagenum,boolean mainonly)\r
+{\r
+       byte                    far *addr;\r
+       PageListStruct  far *page;\r
+\r
+       addr = PML_GetAPageBuffer(pagenum,mainonly);\r
+       page = &PMPages[pagenum];\r
+       PML_ReadFromFile(addr,page->offset,page->length);\r
+}\r
+\r
+//\r
+//     PM_GetPage() - Returns the address of the page, loading it if necessary\r
+//             First, check if in Main Memory or EMS\r
+//             Then, check XMS\r
+//             If not in XMS, load into Main Memory or EMS\r
+//\r
+#pragma warn -pia\r
+memptr\r
+PM_GetPage(int pagenum)\r
+{\r
+       memptr  result;\r
+\r
+       if (pagenum >= ChunksInFile)\r
+               Quit("PM_GetPage: Invalid page request");\r
+\r
+#if 0  // for debugging\r
+asm    mov     dx,STATUS_REGISTER_1\r
+asm    in      al,dx\r
+asm    mov     dx,ATR_INDEX\r
+asm    mov     al,ATR_OVERSCAN\r
+asm    out     dx,al\r
+asm    mov     al,10   // bright green\r
+asm    out     dx,al\r
+#endif\r
+\r
+       if (!(result = PM_GetPageAddress(pagenum)))\r
+       {\r
+               boolean mainonly = (pagenum >= PMSoundStart);\r
+if (!PMPages[pagenum].offset)  // JDC: sparse page\r
+       Quit ("Tried to load a sparse page!");\r
+               if (!(result = PML_GetPageFromXMS(pagenum,mainonly)))\r
+               {\r
+                       if (PMPages[pagenum].lastHit == PMFrameCount)\r
+                               PMThrashing++;\r
+\r
+                       PML_LoadPage(pagenum,mainonly);\r
+                       result = PM_GetPageAddress(pagenum);\r
+               }\r
+       }\r
+       PMPages[pagenum].lastHit = PMFrameCount;\r
+\r
+#if 0  // for debugging\r
+asm    mov     dx,STATUS_REGISTER_1\r
+asm    in      al,dx\r
+asm    mov     dx,ATR_INDEX\r
+asm    mov     al,ATR_OVERSCAN\r
+asm    out     dx,al\r
+asm    mov     al,3    // blue\r
+asm    out     dx,al\r
+asm    mov     al,0x20 // normal\r
+asm    out     dx,al\r
+#endif\r
+\r
+       return(result);\r
+}\r
+#pragma warn +pia\r
+\r
+//\r
+//     PM_SetPageLock() - Sets the lock type on a given page\r
+//             pml_Unlocked: Normal, page can be purged\r
+//             pml_Locked: Cannot be purged\r
+//             pml_EMS?: Same as pml_Locked, but if in EMS, use the physical page\r
+//                                     specified when returning the address. For sound stuff.\r
+//\r
+void\r
+PM_SetPageLock(int pagenum,PMLockType lock)\r
+{\r
+       if (pagenum < PMSoundStart)\r
+               Quit("PM_SetPageLock: Locking/unlocking non-sound page");\r
+\r
+       PMPages[pagenum].locked = lock;\r
+}\r
+\r
+//\r
+//     PM_Preload() - Loads as many pages as possible into all types of memory.\r
+//             Calls the update function after each load, indicating the current\r
+//             page, and the total pages that need to be loaded (for thermometer).\r
+//\r
+void\r
+PM_Preload(boolean (*update)(word current,word total))\r
+{\r
+       int                             i,j,\r
+                                       page,oogypage;\r
+       word                    current,total,\r
+                                       totalnonxms,totalxms,\r
+                                       mainfree,maintotal,\r
+                                       emsfree,emstotal,\r
+                                       xmsfree,xmstotal;\r
+       memptr                  addr;\r
+       PageListStruct  far *p;\r
+\r
+       mainfree = (MainPagesAvail - MainPagesUsed) + (EMSPagesAvail - EMSPagesUsed);\r
+       xmsfree = (XMSPagesAvail - XMSPagesUsed);\r
+\r
+       xmstotal = maintotal = 0;\r
+\r
+       for (i = 0;i < ChunksInFile;i++)\r
+       {\r
+               if (!PMPages[i].offset)\r
+                       continue;                       // sparse\r
+\r
+               if ( PMPages[i].emsPage != -1 || PMPages[i].mainPage != -1 )\r
+                       continue;                       // already in main mem\r
+\r
+               if ( mainfree )\r
+               {\r
+                       maintotal++;\r
+                       mainfree--;\r
+               }\r
+               else if ( xmsfree && (PMPages[i].xmsPage == -1) )\r
+               {\r
+                       xmstotal++;\r
+                       xmsfree--;\r
+               }\r
+       }\r
+\r
+\r
+       total = maintotal + xmstotal;\r
+\r
+       if (!total)\r
+               return;\r
+\r
+       page = 0;\r
+       current = 0;\r
+\r
+//\r
+// cache main/ems blocks\r
+//\r
+       while (maintotal)\r
+       {\r
+               while ( !PMPages[page].offset || PMPages[page].mainPage != -1\r
+                       ||      PMPages[page].emsPage != -1 )\r
+                       page++;\r
+\r
+               if (page >= ChunksInFile)\r
+                       Quit ("PM_Preload: Pages>=ChunksInFile");\r
+\r
+               PM_GetPage(page);\r
+\r
+               page++;\r
+               current++;\r
+               maintotal--;\r
+               update(current,total);\r
+       }\r
+\r
+//\r
+// load stuff to XMS\r
+//\r
+       if (xmstotal)\r
+       {\r
+               for (oogypage = 0 ; PMPages[oogypage].mainPage == -1 ; oogypage++)\r
+               ;\r
+               addr = PM_GetPage(oogypage);\r
+               if (!addr)\r
+                       Quit("PM_Preload: XMS buffer failed");\r
+\r
+               while (xmstotal)\r
+               {\r
+                       while ( !PMPages[page].offset || PMPages[page].xmsPage != -1 )\r
+                               page++;\r
+\r
+                       if (page >= ChunksInFile)\r
+                               Quit ("PM_Preload: Pages>=ChunksInFile");\r
+\r
+                       p = &PMPages[page];\r
+\r
+                       p->xmsPage = XMSPagesUsed++;\r
+                       if (XMSPagesUsed > XMSPagesAvail)\r
+                               Quit("PM_Preload: Exceeded XMS pages");\r
+                       if (p->length > PMPageSize)\r
+                               Quit("PM_Preload: Page too long");\r
+\r
+                       PML_ReadFromFile((byte far *)addr,p->offset,p->length);\r
+                       PML_CopyToXMS((byte far *)addr,p->xmsPage,p->length);\r
+\r
+                       page++;\r
+                       current++;\r
+                       xmstotal--;\r
+                       update(current,total);\r
+               }\r
+\r
+               p = &PMPages[oogypage];\r
+               PML_ReadFromFile((byte far *)addr,p->offset,p->length);\r
+       }\r
+\r
+       update(total,total);\r
+}\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+//     General code\r
+//\r
+/////////////////////////////////////////////////////////////////////////////\r
+\r
+//\r
+//     PM_NextFrame() - Increments the frame counter and adjusts the thrash\r
+//             avoidence variables\r
+//\r
+//             If currently in panic mode (to avoid thrashing), check to see if the\r
+//                     appropriate number of frames have passed since the last time that\r
+//                     we would have thrashed. If so, take us out of panic mode.\r
+//\r
+//\r
+void\r
+PM_NextFrame(void)\r
+{\r
+       int     i;\r
+\r
+       // Frame count overrun - kill the LRU hit entries & reset frame count\r
+       if (++PMFrameCount >= MAXLONG - 4)\r
+       {\r
+               for (i = 0;i < PMNumBlocks;i++)\r
+                       PMPages[i].lastHit = 0;\r
+               PMFrameCount = 0;\r
+       }\r
+\r
+#if 0\r
+       for (i = 0;i < PMSoundStart;i++)\r
+       {\r
+               if (PMPages[i].locked)\r
+               {\r
+                       char buf[40];\r
+                       sprintf(buf,"PM_NextFrame: Page %d is locked",i);\r
+                       Quit(buf);\r
+               }\r
+       }\r
+#endif\r
+\r
+       if (PMPanicMode)\r
+       {\r
+               // DEBUG - set border color\r
+               if ((!PMThrashing) && (!--PMPanicMode))\r
+               {\r
+                       // DEBUG - reset border color\r
+               }\r
+       }\r
+       if (PMThrashing >= PMThrashThreshold)\r
+               PMPanicMode = PMUnThrashThreshold;\r
+       PMThrashing = false;\r
+}\r
+\r
+//\r
+//     PM_Reset() - Sets up caching structures\r
+//\r
+void\r
+PM_Reset(void)\r
+{\r
+       int                             i;\r
+       PageListStruct  far *page;\r
+\r
+       XMSPagesAvail = XMSAvail / PMPageSizeKB;\r
+\r
+       EMSPagesAvail = EMSAvail * (EMSPageSizeKB / PMPageSizeKB);\r
+       EMSPhysicalPage = 0;\r
+\r
+       MainPagesUsed = EMSPagesUsed = XMSPagesUsed = 0;\r
+\r
+       PMPanicMode = false;\r
+\r
+       // Initialize page list\r
+       for (i = 0,page = PMPages;i < PMNumBlocks;i++,page++)\r
+       {\r
+               page->mainPage = -1;\r
+               page->emsPage = -1;\r
+               page->xmsPage = -1;\r
+               page->locked = false;\r
+       }\r
+}\r
+\r
+//\r
+//     PM_Startup() - Start up the Page Mgr\r
+//\r
+void\r
+PM_Startup(void)\r
+{\r
+       boolean nomain,noems,noxms;\r
+       int             i;\r
+\r
+       if (PMStarted)\r
+               return;\r
+\r
+       nomain = noems = noxms = false;\r
+       for (i = 1;i < _argc;i++)\r
+       {\r
+               switch (US_CheckParm(_argv[i],ParmStrings))\r
+               {\r
+               case 0:\r
+                       nomain = true;\r
+                       break;\r
+               case 1:\r
+                       noems = true;\r
+                       break;\r
+               case 2:\r
+                       noxms = true;\r
+                       break;\r
+               }\r
+       }\r
+\r
+       PML_OpenPageFile();\r
+\r
+       if (!noems)\r
+               PML_StartupEMS();\r
+       if (!noxms)\r
+               PML_StartupXMS();\r
+\r
+       if (nomain && !EMSPresent)\r
+               Quit("PM_Startup: No main or EMS");\r
+       else\r
+               PML_StartupMainMem();\r
+\r
+       PM_Reset();\r
+\r
+       PMStarted = true;\r
+}\r
+\r
+//\r
+//     PM_Shutdown() - Shut down the Page Mgr\r
+//\r
+void\r
+PM_Shutdown(void)\r
+{\r
+       PML_ShutdownXMS();\r
+       PML_ShutdownEMS();\r
+\r
+       if (!PMStarted)\r
+               return;\r
+\r
+       PML_ClosePageFile();\r
+\r
+       PML_ShutdownMainMem();\r
+}\r
diff --git a/src/lib/16_pm.h b/src/lib/16_pm.h
new file mode 100644 (file)
index 0000000..b3e7491
--- /dev/null
@@ -0,0 +1,83 @@
+//\r
+//     ID_PM.H\r
+//     Header file for Id Engine's Page Manager\r
+//\r
+\r
+//     NOTE! PMPageSize must be an even divisor of EMSPageSize, and >= 1024\r
+#define        EMSPageSize             16384\r
+#define        EMSPageSizeSeg  (EMSPageSize >> 4)\r
+#define        EMSPageSizeKB   (EMSPageSize >> 10)\r
+#define        EMSFrameCount   4\r
+#define        PMPageSize              4096\r
+#define        PMPageSizeSeg   (PMPageSize >> 4)\r
+#define        PMPageSizeKB    (PMPageSize >> 10)\r
+#define        PMEMSSubPage    (EMSPageSize / PMPageSize)\r
+\r
+#define        PMMinMainMem    10                      // Min acceptable # of pages from main\r
+#define        PMMaxMainMem    100                     // Max number of pages in main memory\r
+\r
+#define        PMThrashThreshold       1       // Number of page thrashes before panic mode\r
+#define        PMUnThrashThreshold     5       // Number of non-thrashing frames before leaving panic mode\r
+\r
+typedef        enum\r
+               {\r
+                       pml_Unlocked,\r
+                       pml_Locked\r
+               } PMLockType;\r
+\r
+typedef        enum\r
+               {\r
+                       pmba_Unused = 0,\r
+                       pmba_Used = 1,\r
+                       pmba_Allocated = 2\r
+               } PMBlockAttr;\r
+\r
+typedef        struct\r
+               {\r
+                       longword        offset;         // Offset of chunk into file\r
+                       word            length;         // Length of the chunk\r
+\r
+                       int                     xmsPage;        // If in XMS, (xmsPage * PMPageSize) gives offset into XMS handle\r
+\r
+                       PMLockType      locked;         // If set, this page can't be purged\r
+                       int                     emsPage;        // If in EMS, logical page/offset into page\r
+                       int                     mainPage;       // If in Main, index into handle array\r
+\r
+                       longword        lastHit;        // Last frame number of hit\r
+               } PageListStruct;\r
+\r
+typedef        struct\r
+               {\r
+                       int                     baseEMSPage;    // Base EMS page for this phys frame\r
+                       longword        lastHit;                // Last frame number of hit\r
+               } EMSListStruct;\r
+\r
+extern boolean                 XMSPresent,EMSPresent;\r
+extern word                    XMSPagesAvail,EMSPagesAvail;\r
+\r
+extern word                    ChunksInFile,\r
+                                               PMSpriteStart,PMSoundStart;\r
+extern PageListStruct  far *PMPages;\r
+\r
+#define        PM_GetSoundPage(v)      PM_GetPage(PMSoundStart + (v))\r
+#define        PM_GetSpritePage(v)     PM_GetPage(PMSpriteStart + (v))\r
+\r
+#define        PM_LockMainMem()        PM_SetMainMemPurge(0)\r
+#define        PM_UnlockMainMem()      PM_SetMainMemPurge(3)\r
+\r
+\r
+extern char    PageFileName[13];\r
+\r
+\r
+extern void    PM_Startup(void),\r
+                               PM_Shutdown(void),\r
+                               PM_Reset(void),\r
+                               PM_Preload(boolean (*update)(word current,word total)),\r
+                               PM_NextFrame(void),\r
+                               PM_SetPageLock(int pagenum,PMLockType lock),\r
+                               PM_SetMainPurge(int level),\r
+                               PM_CheckMainMem(void);\r
+extern memptr  PM_GetPageAddress(int pagenum),\r
+                               PM_GetPage(int pagenum);                // Use this one to cache page\r
+\r
+void PM_SetMainMemPurge(int level);\r