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