--- /dev/null
+//\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)©\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