3 // Id Engine's Page Manager v1.0
\r
4 // Primary coder: Jason Blochowiak
\r
7 #include "src/lib/16_pm.h"
\r
10 // Main Mem specific variables
\r
11 boolean MainPresent;
\r
12 memptr MainMemPages[PMMaxMainMem];
\r
13 PMBlockAttr MainMemUsed[PMMaxMainMem];
\r
16 // EMS specific variables
\r
18 word EMSAvail,EMSPagesAvail,EMSHandle,
\r
19 EMSPageFrame,EMSPhysicalPage;
\r
20 EMSListStruct EMSList[EMSFrameCount];
\r
22 // XMS specific variables
\r
24 word XMSAvail,XMSPagesAvail,XMSHandle;
\r
26 int XMSProtectPage = -1;
\r
28 // File specific variables
\r
29 char PageFileName[13] = {"VSWAP."};
\r
32 word PMSpriteStart,PMSoundStart;
\r
34 // General usage variables
\r
43 PageListStruct far *PMPages,
\r
46 static char *ParmStrings[] = {"nomain","noems","noxms",nil};
\r
48 /////////////////////////////////////////////////////////////////////////////
\r
50 // EMS Management code
\r
52 /////////////////////////////////////////////////////////////////////////////
\r
55 // PML_MapEMS() - Maps a logical page to a physical page
\r
58 PML_MapEMS(word logical,word physical)
\r
61 CPURegs.h.al = physical;
\r
62 CPURegs.x.bx = logical;
\r
63 CPURegs.x.dx = EMSHandle;
\r
64 CPURegs.h.ah = EMS_MAPPAGE;
\r
72 Quit("PML_MapEMS: Page mapping failed\n");
\r
78 // PML_StartupEMS() - Sets up EMS for Page Mgr's use
\r
79 // Checks to see if EMS driver is present
\r
80 // Verifies that EMS hardware is present
\r
81 // Make sure that EMS version is 3.2 or later
\r
82 // If there's more than our minimum (2 pages) available, allocate it (up
\r
83 // to the maximum we need)
\r
86 char EMMDriverName[9] = "EMMXXXX0";
\r
89 PML_StartupEMS(void)
\r
95 EMSPresent = false; // Assume that we'll fail
\r
98 CPURegs.x.dx = (word)EMMDriverName;
\r
99 CPURegs.x.ax = 0x3d00;
\r
100 geninterrupt(0x21); // try to open EMMXXXX0 device
\r
105 CPURegs.x.bx = CPURegs.x.ax;
\r
106 CPURegs.x.ax = 0x4400;
\r
107 geninterrupt(0x21); // get device info
\r
116 CPURegs.x.ax = 0x4407;
\r
117 geninterrupt(0x21); // get status
\r
122 CPURegs.h.ah = 0x3e;
\r
123 geninterrupt(0x21); // close handle
\r
125 CPURegs.h.ah = EMS_STATUS;
\r
126 geninterrupt(EMS_INT);
\r
128 goto error; // make sure EMS hardware is present
\r
130 CPURegs.h.ah = EMS_VERSION;
\r
131 geninterrupt(EMS_INT);
\r
132 if (CPURegs.h.ah || (CPURegs.l.al < 0x32)) // only work on EMS 3.2 or greater (silly, but...)
\r
135 CPURegs.h.ah = EMS_GETFRAME;
\r
136 geninterrupt(EMS_INT);
\r
138 goto error; // find the page frame address
\r
139 EMSPageFrame = CPURegs.x.bx;
\r
141 CPURegs.h.ah = EMS_GETPAGES;
\r
142 geninterrupt(EMS_INT);
\r
145 if (CPURegs.x.bx < 2)
\r
146 goto error; // Require at least 2 pages (32k)
\r
147 EMSAvail = CPURegs.x.bx;
\r
149 // Don't hog all available EMS
\r
150 size = EMSAvail * (long)EMSPageSize;
\r
151 if (size - (EMSPageSize * 2) > (ChunksInFile * (long)PMPageSize))
\r
153 size = (ChunksInFile * (long)PMPageSize) + EMSPageSize;
\r
154 EMSAvail = size / EMSPageSize;
\r
157 CPURegs.h.ah = EMSCPURegs.l.alLOCPAGES;
\r
158 CPURegs.x.bx = EMSAvail;
\r
159 geninterrupt(EMS_INT);
\r
162 EMSHandle = CPURegs.x.dx;
\r
164 mminfo.EMSmem += EMSAvail * (long)EMSPageSize;
\r
166 // Initialize EMS mapping cache
\r
167 for (i = 0;i < EMSFrameCount;i++)
\r
168 EMSList[i].baseEMSPage = -1;
\r
170 EMSPresent = true; // We have EMS
\r
173 return(EMSPresent);
\r
177 // PML_ShutdownEMS() - If EMS was used, deallocate it
\r
180 PML_ShutdownEMS(void)
\r
185 mov ah,EMS_FREEPAGES
\r
191 Quit("PML_ShutdownEMS: Error freeing EMS\n");
\r
197 /////////////////////////////////////////////////////////////////////////////
\r
199 // XMS Management code
\r
201 /////////////////////////////////////////////////////////////////////////////
\r
204 // PML_StartupXMS() - Starts up XMS for the Page Mgr's use
\r
205 // Checks for presence of an XMS driver
\r
206 // Makes sure that there's at least a page of XMS available
\r
207 // Allocates any remaining XMS (rounded down to the nearest page size)
\r
210 PML_StartupXMS(void)
\r
212 XMSPresent = false; // Assume failure
\r
216 asm int XMS_INT // Check for presence of XMS driver
\r
217 if (CPURegs.l.al != 0x80)
\r
222 asm int XMS_INT // Get address of XMS driver
\r
223 asm mov [WORD PTR XMSDriver],bx
\r
224 asm mov [WORD PTR XMSDriver+2],es // function pointer to XMS driver
\r
226 XMS_CALL(XMS_QUERYFREE); // Find out how much XMS is available
\r
227 XMSAvail = CPURegs.x.ax;
\r
228 if (!CPURegs.x.ax) // AJR: bugfix 10/8/92
\r
231 XMSAvail &= ~(PMPageSizeKB - 1); // Round off to nearest page size
\r
232 if (XMSAvail < (PMPageSizeKB * 2)) // Need at least 2 pages
\r
235 CPURegs.x.dx = XMSAvail;
\r
236 XMS_CALL(XMSCPURegs.l.alLOC); // And do the allocation
\r
237 XMSHandle = CPURegs.x.dx;
\r
239 if (!CPURegs.x.ax) // AJR: bugfix 10/8/92
\r
245 mminfo.XMSmem += XMSAvail * 1024;
\r
249 return(XMSPresent);
\r
253 // PML_XMSCopy() - Copies a main/EMS page to or from XMS
\r
254 // Will round an odd-length request up to the next even value
\r
257 PML_XMSCopy(boolean toxms,byte far *addr,word xmspage,word length)
\r
263 word source_handle;
\r
264 dword source_offset;
\r
265 word target_handle;
\r
266 dword target_offset;
\r
271 Quit("PML_XMSCopy: zero address\n");
\r
275 xoffset = (dword)xmspage * PMPageSize;
\r
277 copy.length = (length + 1) & ~1;
\r
278 copy.source_handle = toxms? 0 : XMSHandle;
\r
279 copy.source_offset = toxms? (long)addr : xoffset;
\r
280 copy.target_handle = toxms? XMSHandle : 0;
\r
281 copy.target_offset = toxms? xoffset : (long)addr;
\r
284 CPURegs.i.si = (word)©
\r
285 XMS_CALL(XMS_MOVE);
\r
289 Quit("PML_XMSCopy: Error on copy");
\r
295 #define PML_CopyToXMS(s,t,l) PML_XMSCopy(true,(s),(t),(l))
\r
296 #define PML_CopyFromXMS(t,s,l) PML_XMSCopy(false,(t),(s),(l))
\r
299 // PML_CopyToXMS() - Copies the specified number of bytes from the real mode
\r
300 // segment address to the specified XMS page
\r
303 PML_CopyToXMS(byte far *source,int targetpage,word length)
\r
305 PML_XMSCopy(true,source,targetpage,length);
\r
309 // PML_CopyFromXMS() - Copies the specified number of bytes from an XMS
\r
310 // page to the specified real mode address
\r
313 PML_CopyFromXMS(byte far *target,int sourcepage,word length)
\r
315 PML_XMSCopy(false,target,sourcepage,length);
\r
320 // PML_ShutdownXMS()
\r
323 PML_ShutdownXMS(void)
\r
327 CPURegs.x.dx = XMSHandle;
\r
328 XMS_CALL(XMS_FREE);
\r
331 Quit("PML_ShutdownXMS: Error freeing XMS");
\r
337 /////////////////////////////////////////////////////////////////////////////
\r
339 // Main memory code
\r
341 /////////////////////////////////////////////////////////////////////////////
\r
344 // PM_SetMainMemPurge() - Sets the purge level for all allocated main memory
\r
345 // blocks. This shouldn't be called directly - the PM_LockMainMem() and
\r
346 // PM_UnlockMainMem() macros should be used instead.
\r
349 PM_SetMainMemPurge(int level)
\r
353 for (i = 0;i < PMMaxMainMem;i++)
\r
354 if (MainMemPages[i])
\r
355 MM_SetPurge(&MainMemPages[i],level);
\r
359 // PM_CheckMainMem() - If something besides the Page Mgr makes requests of
\r
360 // the Memory Mgr, some of the Page Mgr's blocks may have been purged,
\r
361 // so this function runs through the block list and checks to see if
\r
362 // any of the blocks have been purged. If so, it marks the corresponding
\r
363 // page as purged & unlocked, then goes through the block list and
\r
364 // tries to reallocate any blocks that have been purged.
\r
365 // This routine now calls PM_LockMainMem() to make sure that any allocation
\r
366 // attempts made during the block reallocation sweep don't purge any
\r
367 // of the other blocks. Because PM_LockMainMem() is called,
\r
368 // PM_UnlockMainMem() needs to be called before any other part of the
\r
369 // program makes allocation requests of the Memory Mgr.
\r
372 PM_CheckMainMem(void)
\r
374 boolean allocfailed;
\r
378 PageListStruct far *page;
\r
383 for (i = 0,page = PMPages;i < ChunksInFile;i++,page++)
\r
385 n = page->mainPage;
\r
386 if (n != -1) // Is the page using main memory?
\r
388 if (!MainMemPages[n]) // Yep, was the block purged?
\r
390 page->mainPage = -1; // Yes, mark page as purged & unlocked
\r
391 page->locked = pml_Unlocked;
\r
396 // Prevent allocation attempts from purging any of our other blocks
\r
398 allocfailed = false;
\r
399 for (i = 0,p = MainMemPages,used = MainMemUsed;i < PMMaxMainMem;i++,p++,used++)
\r
401 if (!*p) // If the page got purged
\r
403 if (*used & pmba_Allocated) // If it was allocated
\r
405 *used &= ~pmba_Allocated; // Mark as unallocated
\r
406 MainPagesAvail--; // and decrease available count
\r
409 if (*used & pmba_Used) // If it was used
\r
411 *used &= ~pmba_Used; // Mark as unused
\r
412 MainPagesUsed--; // and decrease used count
\r
417 MM_BombOnError(false);
\r
418 MM_GetPtr(p,PMPageSize); // Try to reallocate
\r
419 if (mmerror) // If it failed,
\r
420 allocfailed = true; // don't try any more allocations
\r
421 else // If it worked,
\r
423 *used |= pmba_Allocated; // Mark as allocated
\r
424 MainPagesAvail++; // and increase available count
\r
426 MM_BombOnError(true);
\r
435 // PML_StartupMainMem() - Allocates as much main memory as is possible for
\r
436 // the Page Mgr. The memory is allocated as non-purgeable, so if it's
\r
437 // necessary to make requests of the Memory Mgr, PM_UnlockMainMem()
\r
438 // needs to be called.
\r
441 PML_StartupMainMem(void)
\r
446 MainPagesAvail = 0;
\r
447 MM_BombOnError(false);
\r
448 for (i = 0,p = MainMemPages;i < PMMaxMainMem;i++,p++)
\r
450 MM_GetPtr(p,PMPageSize);
\r
455 MainMemUsed[i] = pmba_Allocated;
\r
457 MM_BombOnError(true);
\r
460 if (MainPagesAvail < PMMinMainMem)
\r
462 Quit("PM_SetupMainMem: Not enough main memory");
\r
465 MainPresent = true;
\r
469 // PML_ShutdownMainMem() - Frees all of the main memory blocks used by the
\r
473 PML_ShutdownMainMem(void)
\r
478 // DEBUG - mark pages as unallocated & decrease page count as appropriate
\r
479 for (i = 0,p = MainMemPages;i < PMMaxMainMem;i++,p++)
\r
484 /////////////////////////////////////////////////////////////////////////////
\r
486 // File management code
\r
488 /////////////////////////////////////////////////////////////////////////////
\r
491 // PML_ReadFromFile() - Reads some data in from the page file
\r
494 PML_ReadFromFile(byte far *buf,long offset,word length)
\r
498 Quit("PML_ReadFromFile: Null pointer");
\r
503 Quit("PML_ReadFromFile: Zero offset");
\r
506 if (lseek(PageFile,offset,SEEK_SET) != offset)
\r
508 Quit("PML_ReadFromFile: Seek failed");
\r
511 if (!CA_FarRead(PageFile,buf,length))
\r
512 Quit("PML_ReadFromFile: Read failed");
\r
518 // PML_OpenPageFile() - Opens the page file and sets up the page info
\r
521 PML_OpenPageFile(void)
\r
526 dword far *offsetptr;
\r
527 word far *lengthptr;
\r
528 PageListStruct far *page;
\r
530 PageFile = open(PageFileName,O_RDONLY + O_BINARY);
\r
531 if (PageFile == -1)
\r
533 Quit("PML_OpenPageFile: Unable to open page file");
\r
537 // Read in header variables
\r
538 read(PageFile,&ChunksInFile,sizeof(ChunksInFile));
\r
539 read(PageFile,&PMSpriteStart,sizeof(PMSpriteStart));
\r
540 read(PageFile,&PMSoundStart,sizeof(PMSoundStart));
\r
542 // Allocate and clear the page list
\r
543 PMNumBlocks = ChunksInFile;
\r
544 MM_GetPtr(&(memptr)PMSegPages,sizeof(PageListStruct) * PMNumBlocks);
\r
545 MM_SetLock(&(memptr)PMSegPages,true);
\r
546 PMPages = (PageListStruct far *)PMSegPages;
\r
547 _fmemset(PMPages,0,sizeof(PageListStruct) * PMNumBlocks);
\r
549 // Read in the chunk offsets
\r
550 size = sizeof(dword) * ChunksInFile;
\r
551 MM_GetPtr(&buf,size);
\r
552 if (!CA_FarRead(PageFile,(byte far *)buf,size))
\r
554 Quit("PML_OpenPageFile: Offset read failed");
\r
557 offsetptr = (dword far *)buf;
\r
558 for (i = 0,page = PMPages;i < ChunksInFile;i++,page++)
\r
559 page->offset = *offsetptr++;
\r
562 // Read in the chunk lengths
\r
563 size = sizeof(word) * ChunksInFile;
\r
564 MM_GetPtr(&buf,size);
\r
565 if (!CA_FarRead(PageFile,(byte far *)buf,size))
\r
567 Quit("PML_OpenPageFile: Length read failed");
\r
570 lengthptr = (word far *)buf;
\r
571 for (i = 0,page = PMPages;i < ChunksInFile;i++,page++)
\r
572 page->length = *lengthptr++;
\r
577 // PML_ClosePageFile() - Closes the page file
\r
580 PML_ClosePageFile(void)
\r
582 if (PageFile != -1)
\r
586 MM_SetLock(&(memptr)PMSegPages,false);
\r
587 MM_FreePtr(&(__SEGA *)PMSegPages);
\r
591 /////////////////////////////////////////////////////////////////////////////
\r
593 // Allocation, etc., code
\r
595 /////////////////////////////////////////////////////////////////////////////
\r
598 // PML_GetEMSAddress()
\r
600 // Page is in EMS, so figure out which EMS physical page should be used
\r
601 // to map our page in. If normal page, use EMS physical page 3, else
\r
602 // use the physical page specified by the lock type
\r
605 #pragma argsused // DEBUG - remove lock parameter
\r
607 PML_GetEMSAddress(int page,PMLockType lock)
\r
610 word emsoff,emsbase,offset;
\r
612 emsoff = page & (PMEMSSubPage - 1);
\r
613 emsbase = page - emsoff;
\r
616 // See if this page is already mapped in
\r
617 for (i = 0;i < EMSFrameCount;i++)
\r
619 if (EMSList[i].baseEMSPage == emsbase)
\r
621 emspage = i; // Yep - don't do a redundant remapping
\r
626 // If page isn't already mapped in, find LRU EMS frame, and use it
\r
629 dword last = MAXLONG;
\r
630 for (i = 0;i < EMSFrameCount;i++)
\r
632 if (EMSList[i].lastHit < last)
\r
635 last = EMSList[i].lastHit;
\r
639 EMSList[emspage].baseEMSPage = emsbase;
\r
640 PML_MapEMS(page / PMEMSSubPage,emspage);
\r
644 Quit("PML_GetEMSAddress: EMS find failed");
\r
646 EMSList[emspage].lastHit = PMFrameCount;
\r
647 offset = emspage * EMSPageSizeSeg;
\r
648 offset += emsoff * PMPageSizeSeg;
\r
649 return((memptr)(EMSPageFrame + offset));
\r
653 PML_GetEMSAddress(int page,PMLockType lock)
\r
657 emspage = (lock < pml_EMSLock)? 3 : (lock - pml_EMSLock);
\r
659 PML_MapEMS(page / PMEMSSubPage,emspage);
\r
661 return((memptr)(EMSPageFrame + (emspage * EMSPageSizeSeg)
\r
662 + ((page & (PMEMSSubPage - 1)) * PMPageSizeSeg)));
\r
667 // PM_GetPageAddress() - Returns the address of a given page
\r
668 // Maps in EMS if necessary
\r
669 // Returns nil if block isn't cached into Main Memory or EMS
\r
673 PM_GetPageAddress(int pagenum)
\r
675 PageListStruct far *page;
\r
677 page = &PMPages[pagenum];
\r
678 if (page->mainPage != -1)
\r
679 return(MainMemPages[page->mainPage]);
\r
680 else if (page->emsPage != -1)
\r
681 return(PML_GetEMSAddress(page->emsPage,page->locked));
\r
687 // PML_GiveLRUPage() - Returns the page # of the least recently used
\r
688 // present & unlocked main/EMS page (or main page if mainonly is true)
\r
691 PML_GiveLRUPage(boolean mainonly)
\r
695 PageListStruct far *page;
\r
697 for (i = 0,page = PMPages,lru = -1,last = MAXLONG;i < ChunksInFile;i++,page++)
\r
701 (page->lastHit < last)
\r
702 && ((page->emsPage != -1) || (page->mainPage != -1))
\r
703 && (page->locked == pml_Unlocked)
\r
704 && (!(mainonly && (page->mainPage == -1)))
\r
707 last = page->lastHit;
\r
713 Quit("PML_GiveLRUPage: LRU Search failed");
\r
718 // PML_GiveLRUXMSPage() - Returns the page # of the least recently used
\r
719 // (and present) XMS page.
\r
720 // This routine won't return the XMS page protected (by XMSProtectPage)
\r
723 PML_GiveLRUXMSPage(void)
\r
727 PageListStruct far *page;
\r
729 for (i = 0,page = PMPages,lru = -1,last = MAXLONG;i < ChunksInFile;i++,page++)
\r
733 (page->xmsPage != -1)
\r
734 && (page->lastHit < last)
\r
735 && (i != XMSProtectPage)
\r
738 last = page->lastHit;
\r
746 // PML_PutPageInXMS() - If page isn't in XMS, find LRU XMS page and replace
\r
747 // it with the main/EMS page
\r
750 PML_PutPageInXMS(int pagenum)
\r
753 PageListStruct far *page;
\r
758 page = &PMPages[pagenum];
\r
759 if (page->xmsPage != -1)
\r
760 return; // Already in XMS
\r
762 if (XMSPagesUsed < XMSPagesAvail)
\r
763 page->xmsPage = XMSPagesUsed++;
\r
766 usexms = PML_GiveLRUXMSPage();
\r
768 Quit("PML_PutPageInXMS: No XMS LRU");
\r
769 page->xmsPage = PMPages[usexms].xmsPage;
\r
770 PMPages[usexms].xmsPage = -1;
\r
772 PML_CopyToXMS(PM_GetPageAddress(pagenum),page->xmsPage,page->length);
\r
776 // PML_TransferPageSpace() - A page is being replaced, so give the new page
\r
777 // the old one's address space. Returns the address of the new page.
\r
780 PML_TransferPageSpace(int orig,int new)
\r
783 PageListStruct far *origpage,far *newpage;
\r
786 Quit("PML_TransferPageSpace: Identity replacement");
\r
788 origpage = &PMPages[orig];
\r
789 newpage = &PMPages[new];
\r
791 if (origpage->locked != pml_Unlocked)
\r
792 Quit("PML_TransferPageSpace: Killing locked page");
\r
794 if ((origpage->emsPage == -1) && (origpage->mainPage == -1))
\r
795 Quit("PML_TransferPageSpace: Reusing non-existent page");
\r
797 // Copy page that's about to be purged into XMS
\r
798 PML_PutPageInXMS(orig);
\r
800 // Get the address, and force EMS into a physical page if necessary
\r
801 addr = PM_GetPageAddress(orig);
\r
803 // Steal the address
\r
804 newpage->emsPage = origpage->emsPage;
\r
805 newpage->mainPage = origpage->mainPage;
\r
807 // Mark replaced page as purged
\r
808 origpage->mainPage = origpage->emsPage = -1;
\r
811 Quit("PML_TransferPageSpace: Zero replacement");
\r
817 // PML_GetAPageBuffer() - A page buffer is needed. Either get it from the
\r
818 // main/EMS free pool, or use PML_GiveLRUPage() to find which page to
\r
819 // steal the buffer from. Returns a far pointer to the page buffer, and
\r
820 // sets the fields inside the given page structure appropriately.
\r
821 // If mainonly is true, free EMS will be ignored, and only main pages
\r
822 // will be looked at by PML_GiveLRUPage().
\r
825 PML_GetAPageBuffer(int pagenum,boolean mainonly)
\r
827 byte far *addr = nil;
\r
830 PageListStruct far *page;
\r
832 page = &PMPages[pagenum];
\r
833 if ((EMSPagesUsed < EMSPagesAvail) && !mainonly)
\r
835 // There's remaining EMS - use it
\r
836 page->emsPage = EMSPagesUsed++;
\r
837 addr = PML_GetEMSAddress(page->emsPage,page->locked);
\r
839 else if (MainPagesUsed < MainPagesAvail)
\r
841 // There's remaining main memory - use it
\r
842 for (i = 0,n = -1,used = MainMemUsed;i < PMMaxMainMem;i++,used++)
\r
844 if ((*used & pmba_Allocated) && !(*used & pmba_Used))
\r
847 *used |= pmba_Used;
\r
852 Quit("PML_GetPageBuffer: MainPagesAvail lied");
\r
853 addr = MainMemPages[n];
\r
855 Quit("PML_GetPageBuffer: Purged main block");
\r
856 page->mainPage = n;
\r
860 addr = PML_TransferPageSpace(PML_GiveLRUPage(mainonly),pagenum);
\r
863 Quit("PML_GetPageBuffer: Search failed");
\r
868 // PML_GetPageFromXMS() - If page is in XMS, find LRU main/EMS page and
\r
869 // replace it with the page from XMS. If mainonly is true, will only
\r
870 // search for LRU main page.
\r
871 // XMSProtectPage is set to the page to be retrieved from XMS, so that if
\r
872 // the page from which we're stealing the main/EMS from isn't in XMS,
\r
873 // it won't copy over the page that we're trying to get from XMS.
\r
874 // (pages that are being purged are copied into XMS, if possible)
\r
877 PML_GetPageFromXMS(int pagenum,boolean mainonly)
\r
879 byte far *checkaddr;
\r
881 PageListStruct far *page;
\r
883 page = &PMPages[pagenum];
\r
884 if (XMSPresent && (page->xmsPage != -1))
\r
886 XMSProtectPage = pagenum;
\r
887 checkaddr = PML_GetAPageBuffer(pagenum,mainonly);
\r
888 if (FP_OFF(checkaddr))
\r
889 Quit("PML_GetPageFromXMS: Non segment pointer");
\r
890 addr = (memptr)FP_SEG(checkaddr);
\r
891 PML_CopyFromXMS(addr,page->xmsPage,page->length);
\r
892 XMSProtectPage = -1;
\r
899 // PML_LoadPage() - A page is not in main/EMS memory, and it's not in XMS.
\r
900 // Load it into either main or EMS. If mainonly is true, the page will
\r
901 // only be loaded into main.
\r
904 PML_LoadPage(int pagenum,boolean mainonly)
\r
907 PageListStruct far *page;
\r
909 addr = PML_GetAPageBuffer(pagenum,mainonly);
\r
910 page = &PMPages[pagenum];
\r
911 PML_ReadFromFile(addr,page->offset,page->length);
\r
915 // PM_GetPage() - Returns the address of the page, loading it if necessary
\r
916 // First, check if in Main Memory or EMS
\r
918 // If not in XMS, load into Main Memory or EMS
\r
922 PM_GetPage(int pagenum)
\r
926 if (pagenum >= ChunksInFile)
\r
927 Quit("PM_GetPage: Invalid page request");
\r
929 #if 0 // for debugging
\r
930 asm mov dx,STATUS_REGISTER_1
\r
932 asm mov dx,ATR_INDEX
\r
933 asm mov al,ATR_OVERSCAN
\r
935 asm mov al,10 // bright green
\r
939 if (!(result = PM_GetPageAddress(pagenum)))
\r
941 boolean mainonly = (pagenum >= PMSoundStart);
\r
942 if (!PMPages[pagenum].offset) // JDC: sparse page
\r
943 Quit ("Tried to load a sparse page!");
\r
944 if (!(result = PML_GetPageFromXMS(pagenum,mainonly)))
\r
946 if (PMPages[pagenum].lastHit == PMFrameCount)
\r
949 PML_LoadPage(pagenum,mainonly);
\r
950 result = PM_GetPageAddress(pagenum);
\r
953 PMPages[pagenum].lastHit = PMFrameCount;
\r
955 #if 0 // for debugging
\r
956 asm mov dx,STATUS_REGISTER_1
\r
958 asm mov dx,ATR_INDEX
\r
959 asm mov al,ATR_OVERSCAN
\r
961 asm mov al,3 // blue
\r
963 asm mov al,0x20 // normal
\r
972 // PM_SetPageLock() - Sets the lock type on a given page
\r
973 // pml_Unlocked: Normal, page can be purged
\r
974 // pml_Locked: Cannot be purged
\r
975 // pml_EMS?: Same as pml_Locked, but if in EMS, use the physical page
\r
976 // specified when returning the address. For sound stuff.
\r
979 PM_SetPageLock(int pagenum,PMLockType lock)
\r
981 if (pagenum < PMSoundStart)
\r
982 Quit("PM_SetPageLock: Locking/unlocking non-sound page");
\r
984 PMPages[pagenum].locked = lock;
\r
988 // PM_Preload() - Loads as many pages as possible into all types of memory.
\r
989 // Calls the update function after each load, indicating the current
\r
990 // page, and the total pages that need to be loaded (for thermometer).
\r
993 PM_Preload(boolean (*update)(word current,word total))
\r
997 word current,total,
\r
998 totalnonxms,totalxms,
\r
999 mainfree,maintotal,
\r
1003 PageListStruct far *p;
\r
1005 mainfree = (MainPagesAvail - MainPagesUsed) + (EMSPagesAvail - EMSPagesUsed);
\r
1006 xmsfree = (XMSPagesAvail - XMSPagesUsed);
\r
1008 xmstotal = maintotal = 0;
\r
1010 for (i = 0;i < ChunksInFile;i++)
\r
1012 if (!PMPages[i].offset)
\r
1013 continue; // sparse
\r
1015 if ( PMPages[i].emsPage != -1 || PMPages[i].mainPage != -1 )
\r
1016 continue; // already in main mem
\r
1023 else if ( xmsfree && (PMPages[i].xmsPage == -1) )
\r
1031 total = maintotal + xmstotal;
\r
1040 // cache main/ems blocks
\r
1044 while ( !PMPages[page].offset || PMPages[page].mainPage != -1
\r
1045 || PMPages[page].emsPage != -1 )
\r
1048 if (page >= ChunksInFile)
\r
1049 Quit ("PM_Preload: Pages>=ChunksInFile");
\r
1056 update(current,total);
\r
1060 // load stuff to XMS
\r
1064 for (oogypage = 0 ; PMPages[oogypage].mainPage == -1 ; oogypage++)
\r
1066 addr = PM_GetPage(oogypage);
\r
1068 Quit("PM_Preload: XMS buffer failed");
\r
1072 while ( !PMPages[page].offset || PMPages[page].xmsPage != -1 )
\r
1075 if (page >= ChunksInFile)
\r
1076 Quit ("PM_Preload: Pages>=ChunksInFile");
\r
1078 p = &PMPages[page];
\r
1080 p->xmsPage = XMSPagesUsed++;
\r
1081 if (XMSPagesUsed > XMSPagesAvail)
\r
1082 Quit("PM_Preload: Exceeded XMS pages");
\r
1083 if (p->length > PMPageSize)
\r
1084 Quit("PM_Preload: Page too long");
\r
1086 PML_ReadFromFile((byte far *)addr,p->offset,p->length);
\r
1087 PML_CopyToXMS((byte far *)addr,p->xmsPage,p->length);
\r
1092 update(current,total);
\r
1095 p = &PMPages[oogypage];
\r
1096 PML_ReadFromFile((byte far *)addr,p->offset,p->length);
\r
1099 update(total,total);
\r
1102 /////////////////////////////////////////////////////////////////////////////
\r
1106 /////////////////////////////////////////////////////////////////////////////
\r
1109 // PM_NextFrame() - Increments the frame counter and adjusts the thrash
\r
1110 // avoidence variables
\r
1112 // If currently in panic mode (to avoid thrashing), check to see if the
\r
1113 // appropriate number of frames have passed since the last time that
\r
1114 // we would have thrashed. If so, take us out of panic mode.
\r
1118 PM_NextFrame(void)
\r
1122 // Frame count overrun - kill the LRU hit entries & reset frame count
\r
1123 if (++PMFrameCount >= MAXLONG - 4)
\r
1125 for (i = 0;i < PMNumBlocks;i++)
\r
1126 PMPages[i].lastHit = 0;
\r
1131 for (i = 0;i < PMSoundStart;i++)
\r
1133 if (PMPages[i].locked)
\r
1136 sprintf(buf,"PM_NextFrame: Page %d is locked",i);
\r
1144 // DEBUG - set border color
\r
1145 if ((!PMThrashing) && (!--PMPanicMode))
\r
1147 // DEBUG - reset border color
\r
1150 if (PMThrashing >= PMThrashThreshold)
\r
1151 PMPanicMode = PMUnThrashThreshold;
\r
1152 PMThrashing = false;
\r
1156 // PM_Reset() - Sets up caching structures
\r
1162 PageListStruct far *page;
\r
1164 XMSPagesAvail = XMSAvail / PMPageSizeKB;
\r
1166 EMSPagesAvail = EMSAvail * (EMSPageSizeKB / PMPageSizeKB);
\r
1167 EMSPhysicalPage = 0;
\r
1169 MainPagesUsed = EMSPagesUsed = XMSPagesUsed = 0;
\r
1171 PMPanicMode = false;
\r
1173 // Initialize page list
\r
1174 for (i = 0,page = PMPages;i < PMNumBlocks;i++,page++)
\r
1176 page->mainPage = -1;
\r
1177 page->emsPage = -1;
\r
1178 page->xmsPage = -1;
\r
1179 page->locked = false;
\r
1184 // PM_Startup() - Start up the Page Mgr
\r
1189 boolean nomain,noems,noxms;
\r
1195 nomain = noems = noxms = false;
\r
1196 for (i = 1;i < _argc;i++)
\r
1198 switch (US_CheckParm(_argv[i],ParmStrings))
\r
1212 PML_OpenPageFile();
\r
1219 if (nomain && !EMSPresent)
\r
1221 Quit("PM_Startup: No main or EMS\n");
\r
1225 PML_StartupMainMem();
\r
1233 // PM_Shutdown() - Shut down the Page Mgr
\r
1238 PML_ShutdownXMS();
\r
1239 PML_ShutdownEMS();
\r
1244 PML_ClosePageFile();
\r
1246 PML_ShutdownMainMem();
\r