1 /* Project 16 Source Code~
\r
2 * Copyright (C) 2012-2017 sparky4 & pngwen & andrius4669 & joncampbell123 & yakui-lover
\r
4 * This file is part of Project 16.
\r
6 * Project 16 is free software; you can redistribute it and/or modify
\r
7 * it under the terms of the GNU General Public License as published by
\r
8 * the Free Software Foundation; either version 3 of the License, or
\r
9 * (at your option) any later version.
\r
11 * Project 16 is distributed in the hope that it will be useful,
\r
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 * GNU General Public License for more details.
\r
16 * You should have received a copy of the GNU General Public License
\r
17 * along with this program. If not, see <http://www.gnu.org/licenses/>, or
\r
18 * write to the Free Software Foundation, Inc., 51 Franklin Street,
\r
19 * Fifth Floor, Boston, MA 02110-1301 USA.
\r
25 // Id Engine's Page Manager v1.0
\r
26 // Primary coder: Jason Blochowiak
\r
29 #include "src/lib/16_pm.h"
\r
32 // Main Mem specific variables
\r
33 //boolean MainPresent;
\r
34 /* memptr MainMemPages[PMMaxMainMem];
\r
35 PMBlockAttr gvar->pm.mm.MainMemUsed[PMMaxMainMem];
\r
36 int gvar->pm.mm.MainPagesAvail;*/
\r
38 // EMS specific variables
\r
39 //boolean EMSPresent;
\r
40 /* word gvar->pm.emm.EMSAvail,gvar->pm.emm.EMSPagesAvail,gvar->pm.emm.EMSHandle,
\r
41 gvar->pm.emm.EMSPageFrame,gvar->pm.emm.EMSPhysicalPage;
\r
42 gvar->pm.emm.EMSListStruct gvar->pm.emm.EMSList[EMSFrameCount];*/
\r
44 // XMS specific variables
\r
45 //boolean XMSPresent;
\r
46 //word gvar->pm.xmm.XMSAvail,gvar->pm.xmm.XMSPagesAvail,gvar->pm.xmm.XMSHandle;
\r
47 dword XMSDriver; //hard to put in gvar
\r
48 word XMSVer; //hard to put in gvar
\r
49 /* int gvar->pm.xmm.XMSProtectPage = -1;
\r
51 // File specific variables
\r
52 char gvar->pm.fi.PageFileName[13] = {"VSWAP."};
\r
54 word gvar->pm.fi.ChunksInFile;
\r
55 word PMSpriteStart,PMSoundStart;*/
\r
57 // General usage variables
\r
58 /* boolean PMStarted,
\r
59 gvar->pm.PMPanicMode,
\r
60 gvar->pm.PMThrashing;
\r
61 word gvar->pm.XMSPagesUsed,
\r
62 gvar->pm.EMSPagesUsed,
\r
64 gvar->pm.PMNumBlocks;
\r
66 PageListStruct far *gvar->pm.PMPages,
\r
67 _seg *gvar->pm.PMSegPages;*/
\r
69 static union REGS CPURegs;
\r
71 #define _AX CPURegs.x.ax
\r
72 #define _BX CPURegs.x.bx
\r
73 #define _CX CPURegs.x.cx
\r
74 #define _DX CPURegs.x.dx
\r
76 #define _SI CPURegs.x.si
\r
78 #define _AH CPURegs.h.ah
\r
79 #define _AL CPURegs.h.al
\r
80 #define _BH CPURegs.h.bh
\r
81 #define _BL CPURegs.h.bl
\r
82 #define _CH CPURegs.h.ch
\r
83 #define _CL CPURegs.h.cl
\r
84 #define _DH CPURegs.h.dh
\r
85 #define _DL CPURegs.h.dl
\r
88 #define geninterrupt(n) int86(n,&CPURegs,&CPURegs);
\r
91 static char *ParmStrings[] = {"nomain","noems","noxms",nil};
\r
93 /////////////////////////////////////////////////////////////////////////////
\r
95 // EMS Management code
\r
97 /////////////////////////////////////////////////////////////////////////////
\r
100 // PML_MapEMS() - Maps a logical page to a physical page
\r
103 PML_MapEMS(word logical, byte physical, global_game_variables_t *gvar)
\r
105 byte err=0, str[160];
\r
106 unsigned EMShandle;
\r
109 boolean errorflag=false;
\r
110 EMShandle=gvar->pm.emm.EMSHandle;
\r
121 #ifdef __BORLANDC__
\r
125 #ifdef __BORLANDC__
\r
130 #ifdef __BORLANDC__
\r
137 if(errorflag==true)
\r
139 strcpy(str,"MM_MapEMS: EMS error ");
\r
140 MM_EMSerr(str, err);
\r
141 printf("%s\n",str);
\r
142 Quit (gvar, "PML_MapEMS: Page mapping failed\n");
\r
149 // PML_StartupEMS() - Sets up EMS for Page Mgr's use
\r
150 // Checks to see if EMS driver is present
\r
151 // Verifies that EMS hardware is present
\r
152 // Make sure that EMS version is 3.2 or later
\r
153 // If there's more than our minimum (2 pages) available, allocate it (up
\r
154 // to the maximum we need)
\r
156 // Please call MML_CheckForEMS() before calling this function.
\r
157 // MML_CheckForEMS is not local despite the name wwww.
\r
161 PML_StartupEMS(global_game_variables_t *gvar)
\r
164 #ifdef __PM__NOHOGEMS__
\r
167 byte err=0, str[64];
\r
169 boolean errorflag=false;
\r
171 unsigned totalEMSpages,freeEMSpages,EMSPageFrame,EMSHandle,EMSAvail;
\r
172 totalEMSpages = freeEMSpages = EMSPageFrame = EMSHandle = EMSAvail = EMSVer = 0; // set all to 0~
\r
173 gvar->pm.emm.EMSPresent = false; // Assume that we'll fail
\r
174 gvar->pm.emm.EMSAvail = gvar->mmi.EMSmem = 0;
\r
177 //MML_CheckForEMS() takes care of what the code did here
\r
180 jc error1 // make sure EMS hardware is present
\r
183 int EMS_INT // only work on EMS 3.2 or greater (silly, but...)
\r
186 mov [EMSVer],ax // set EMSVer
\r
187 cmp al,0x32 // only work on ems 3.2 or greater
\r
190 mov ah,EMS_GETFRAME
\r
191 int EMS_INT // find the page frame address
\r
194 mov [EMSPageFrame],bx
\r
196 mov ah,EMS_GETPAGES
\r
197 int EMS_INT // find out how much EMS is there
\r
201 jz noEMS // no EMS at all to allocate
\r
203 jl noEMS // Require at least 2 pages (32k)
\r
204 mov [totalEMSpages],dx
\r
205 mov [freeEMSpages],bx
\r
208 #ifdef __BORLANDC__
\r
212 #ifdef __BORLANDC__
\r
218 #ifdef __BORLANDC__
\r
226 #ifdef __PM__NOHOGEMS__
\r
227 if(errorflag==false)
\r
229 // Don't hog all available EMS
\r
230 size = gvar->pm.emm.EMSAvail * (long)EMSPageSize;
\r
231 if (size - (EMSPageSize * 2) > (gvar->pm.fi.ChunksInFile * (long)PMPageSize))
\r
233 size = (gvar->pm.fi.ChunksInFile * (long)PMPageSize) + EMSPageSize;
\r
234 gvar->pm.emm.EMSAvail = size / EMSPageSize;
\r
239 mov ah,EMS_ALLOCPAGES
\r
246 #ifdef __BORLANDC__
\r
250 #ifdef __BORLANDC__
\r
256 #ifdef __BORLANDC__
\r
263 if(errorflag==true)
\r
265 strcpy(str,"PML_StartupEMS: EMS error ");
\r
266 MM_EMSerr(str, err);
\r
267 printf("%s\n",str);
\r
268 return(gvar->pm.emm.EMSPresent);
\r
270 gvar->mmi.EMSmem = EMSAvail * (dword)EMSPageSize;
\r
272 // Initialize EMS mapping cache
\r
273 for (i = 0;i < EMSFrameCount;i++)
\r
274 gvar->pm.emm.EMSList[i].baseEMSPage = -1;
\r
276 gvar->pm.emm.EMSPresent = true; // We have EMS
\r
277 gvar->pm.emm.EMSPageFrame = EMSPageFrame;
\r
278 gvar->pm.emm.EMSAvail = EMSAvail;
\r
279 gvar->pm.emm.EMSVer = EMSVer;
\r
280 gvar->pm.emm.EMSHandle = EMSHandle;
\r
281 gvar->pm.emm.freeEMSpages = freeEMSpages;
\r
282 gvar->pm.emm.totalEMSpages = totalEMSpages;
\r
284 return(gvar->pm.emm.EMSPresent);
\r
288 // PML_ShutdownEMS() - If EMS was used, deallocate it
\r
291 PML_ShutdownEMS(global_game_variables_t *gvar)
\r
294 byte err=0, str[64];
\r
296 boolean errorflag=false;
\r
297 EMSHandle=gvar->pm.emm.EMSHandle;
\r
299 if (gvar->pm.emm.EMSPresent)
\r
302 mov ah,EMS_FREEPAGES
\r
307 #ifdef __BORLANDC__
\r
311 #ifdef __BORLANDC__
\r
317 #ifdef __BORLANDC__
\r
324 if(errorflag==true)
\r
326 strcpy(str,"PML_ShutdownEMS: Error freeing EMS ");
\r
327 MM_EMSerr(str, err);
\r
328 printf("%s\n",str);
\r
329 Quit (gvar, "PML_ShutdownEMS: Error freeing EMS\n");
\r
335 /////////////////////////////////////////////////////////////////////////////
\r
337 // XMS Management code
\r
339 /////////////////////////////////////////////////////////////////////////////
\r
342 // PML_StartupXMS() - Starts up XMS for the Page Mgr's use
\r
343 // Checks for presence of an XMS driver
\r
344 // Makes sure that there's at least a page of XMS available
\r
345 // Allocates any remaining XMS (rounded down to the nearest page size)
\r
348 PML_StartupXMS(global_game_variables_t *gvar)
\r
350 //TODO: translate the _REG into working assembly
\r
351 //#define STARTUPXMSASM
\r
353 word XMSAvail, XMSHandle;//, XMSVer;
\r
354 boolean errorflag=false;
\r
356 gvar->pm.xmm.XMSPresent = false; // Assume failure
\r
357 XMSAvail = gvar->mmi.XMSmem = 0;
\r
361 int XMS_INT // Check for presence of XMS driver
\r
367 int XMS_INT // Get address of XMS driver
\r
368 mov [WORD PTR XMSDriver],bx
\r
369 mov [WORD PTR XMSDriver+2],es // function pointer to XMS driver
\r
372 call [DWORD PTR XMSDriver] //; Get XMS Version Number
\r
376 #ifdef STARTUPXMSASM
\r
377 mov ah,XMS_QUERYFREE // Find out how much XMS is available
\r
378 call [DWORD PTR XMSDriver]
\r
380 or ax,ax // AJR: bugfix 10/8/92
\r
385 #ifdef __BORLANDC__
\r
389 #ifdef __BORLANDC__
\r
395 #ifdef __BORLANDC__
\r
402 if(errorflag==true) goto error;
\r
403 #ifndef STARTUPXMSASM
\r
404 XMS_CALL(XMS_QUERYFREE); // Find out how much XMS is available
\r
406 if (!_AX) // AJR: bugfix 10/8/92
\r
415 #ifdef __DEBUG_PM__
\r
416 printf("XMSVer=%02X ", XMSVer);
\r
417 printf("XMSAvail=%u\n", XMSAvail);
\r
420 XMSAvail &= ~(PMPageSizeKB - 1); // Round off to nearest page size
\r
421 if (XMSAvail < (PMPageSizeKB * 2)) // Need at least 2 pages
\r
426 #ifdef STARTUPXMSASM
\r
429 mov ah,XMS_ALLOC // And do the allocation
\r
430 call [DWORD PTR XMSDriver]
\r
432 or ax,ax // AJR: bugfix 10/8/92
\r
436 #ifdef __BORLANDC__
\r
440 #ifdef __BORLANDC__
\r
446 #ifdef __BORLANDC__
\r
455 XMS_CALL(XMS_ALLOC); // And do the allocation
\r
457 if (!_AX) // AJR: bugfix 10/8/92
\r
466 if(errorflag==false)
\r
468 gvar->mmi.XMSmem = (dword)(XMSAvail) * 1024;
\r
469 gvar->pm.xmm.XMSAvail = XMSAvail;
\r
470 gvar->pm.xmm.XMSHandle = XMSHandle;
\r
471 //gvar->pm.xmm.XMSVer = XMSVer;
\r
472 gvar->pm.xmm.XMSPresent = true;
\r
473 #ifdef __DEBUG_PM__
\r
474 printf(" XMSmem=%lu XMSAvail=%u\n", gvar->mmi.XMSmem, XMSAvail);
\r
479 #ifdef __DEBUG_PM__
\r
480 //printf("XMSHandle\n");
\r
481 //printf(" 1=%u 2=%u 3=%u 4=%u\n", XMSHandle1, XMSHandle2, XMSHandle3, XMSHandle4);
\r
482 //printf(" 2=%u ", XMSHandle);
\r
483 //printf(" %u", gvar->pm.xmm.XMSHandle);
\r
484 printf(" err=%02X e=%u\n", err, e);
\r
487 return(gvar->pm.xmm.XMSPresent);
\r
491 // PML_XMSCopy() - Copies a main/EMS page to or from XMS
\r
492 // Will round an odd-length request up to the next even value
\r
495 PML_XMSCopy(boolean toxms,byte far *addr,word xmspage,word length, global_game_variables_t *gvar)
\r
501 word source_handle;
\r
502 dword source_offset;
\r
503 word target_handle;
\r
504 dword target_offset;
\r
509 Quit (gvar, "PML_XMSCopy: zero address\n");
\r
512 xoffset = (dword)xmspage * PMPageSize;
\r
514 copy.length = (length + 1) & ~1;
\r
515 copy.source_handle = toxms? 0 : gvar->pm.xmm.XMSHandle;
\r
516 copy.source_offset = toxms? (long)addr : xoffset;
\r
517 copy.target_handle = toxms? gvar->pm.xmm.XMSHandle : 0;
\r
518 copy.target_offset = toxms? xoffset : (long)addr;
\r
524 XMS_CALL(XMS_MOVE);
\r
530 Quit (gvar, "PML_XMSCopy: Error on copy");
\r
536 #define PML_CopyToXMS(s,t,l,gvar) PML_XMSCopy(true,(s),(t),(l),(gvar))
\r
537 #define PML_CopyFromXMS(t,s,l,gvar) PML_XMSCopy(false,(t),(s),(l),(gvar))
\r
540 // PML_CopyToXMS() - Copies the specified number of bytes from the real mode
\r
541 // segment address to the specified XMS page
\r
544 PML_CopyToXMS(byte far *source,int targetpage,word length,global_game_variables_t *gvar)
\r
546 PML_XMSCopy(true,source,targetpage,length, global_game_variables_t *gvar);
\r
550 // PML_CopyFromXMS() - Copies the specified number of bytes from an XMS
\r
551 // page to the specified real mode address
\r
554 PML_CopyFromXMS(byte far *target,int sourcepage,word length, global_game_variables_t *gvar)
\r
556 PML_XMSCopy(false,target,sourcepage,length, global_game_variables_t *gvar);
\r
561 // PML_ShutdownXMS()
\r
564 PML_ShutdownXMS(global_game_variables_t *gvar)
\r
566 boolean errorflag=false;
\r
567 word XMSHandle = gvar->pm.xmm.XMSHandle;
\r
568 if (gvar->pm.xmm.XMSPresent)
\r
572 //XMS_CALL(XMS_FREE);
\r
574 call [DWORD PTR XMSDriver]
\r
578 #ifdef __BORLANDC__
\r
582 #ifdef __BORLANDC__
\r
588 #ifdef __BORLANDC__
\r
595 if(errorflag==true)
\r
597 Quit (gvar, "PML_ShutdownXMS: Error freeing XMS");
\r
603 /////////////////////////////////////////////////////////////////////////////
\r
605 // Main memory code
\r
607 /////////////////////////////////////////////////////////////////////////////
\r
610 // PM_SetMainMemPurge() - Sets the purge level for all allocated main memory
\r
611 // blocks. This shouldn't be called directly - the PM_LockMainMem() and
\r
612 // PM_UnlockMainMem() macros should be used instead.
\r
615 PM_SetMainMemPurge(int level, global_game_variables_t *gvar)
\r
619 if(gvar->pm.mm.MainPresent)
\r
620 for (i = 0;i < PMMaxMainMem;i++)
\r
622 #ifdef __DEBUG_PM__
\r
623 #ifdef __DEBUG_PM_MAIN__
\r
624 printf("PM_SetMainMemPurge() info of gvar->pm.mm.MainMemPages[i]\n");
\r
625 printf("& %Fp, %Fp\n", &gvar->pm.mm.MainMemPages[i], &(gvar->pm.mm.MainMemPages[i]));
\r
628 if (gvar->pm.mm.MainMemPages[i])
\r
629 MM_SetPurge(&(gvar->pm.mm.MainMemPages[i]),level, gvar);
\r
634 Quit (gvar, "MainPresent IS NULL\n");
\r
639 // PM_CheckMainMem() - If something besides the Page Mgr makes requests of
\r
640 // the Memory Mgr, some of the Page Mgr's blocks may have been purged,
\r
641 // so this function runs through the block list and checks to see if
\r
642 // any of the blocks have been purged. If so, it marks the corresponding
\r
643 // page as purged & unlocked, then goes through the block list and
\r
644 // tries to reallocate any blocks that have been purged.
\r
645 // This routine now calls PM_LockMainMem() to make sure that any allocation
\r
646 // attempts made during the block reallocation sweep don't purge any
\r
647 // of the other blocks. Because PM_LockMainMem() is called,
\r
648 // PM_UnlockMainMem() needs to be called before any other part of the
\r
649 // program makes allocation requests of the Memory Mgr.
\r
652 PM_CheckMainMem(global_game_variables_t *gvar)
\r
654 boolean allocfailed;
\r
658 PageListStruct far *page;
\r
660 if (!gvar->pm.mm.MainPresent)
\r
663 for (i = 0,page = gvar->pm.PMPages;i < gvar->pm.fi.ChunksInFile;i++,page++)
\r
665 n = page->mainPage;
\r
666 if (n != -1) // Is the page using main memory?
\r
668 if (!gvar->pm.mm.MainMemPages[n]) // Yep, was the block purged?
\r
670 page->mainPage = -1; // Yes, mark page as purged & unlocked
\r
671 page->locked = pml_Unlocked;
\r
676 // Prevent allocation attempts from purging any of our other blocks
\r
677 PM_LockMainMem(gvar);
\r
678 allocfailed = false;
\r
679 for (i = 0,p = gvar->pm.mm.MainMemPages,used = gvar->pm.mm.MainMemUsed; i < PMMaxMainMem;i++,p++,used++)
\r
681 if (!*p) // If the page got purged
\r
683 if (*used & pmba_Allocated) // If it was allocated
\r
685 *used &= ~pmba_Allocated; // Mark as unallocated
\r
686 gvar->pm.mm.MainPagesAvail--; // and decrease available count
\r
689 if (*used & pmba_Used) // If it was used
\r
691 *used &= ~pmba_Used; // Mark as unused
\r
692 gvar->pm.MainPagesUsed--; // and decrease used count
\r
697 MM_BombOnError(false, gvar);
\r
698 MM_GetPtr(p,PMPageSize, gvar); // Try to reallocate
\r
699 if (gvar->mm.mmerror) // If it failed,
\r
700 allocfailed = true; // don't try any more allocations
\r
701 else // If it worked,
\r
703 *used |= pmba_Allocated; // Mark as allocated
\r
704 gvar->pm.mm.MainPagesAvail++; // and increase available count
\r
706 MM_BombOnError(true, gvar);
\r
710 if (gvar->mm.mmerror)
\r
711 gvar->mm.mmerror = false;
\r
715 // PML_StartupMainMem() - Allocates as much main memory as is possible for
\r
716 // the Page Mgr. The memory is allocated as non-purgeable, so if it's
\r
717 // necessary to make requests of the Memory Mgr, PM_UnlockMainMem()
\r
718 // needs to be called.
\r
721 PML_StartupMainMem(global_game_variables_t *gvar)
\r
726 gvar->pm.mm.MainPagesAvail = 0;
\r
727 gvar->pm.mm.MainPresent = false;
\r
728 MM_BombOnError(false, gvar);
\r
729 for (i = 0,p = gvar->pm.mm.MainMemPages;i < PMMaxMainMem;i++,p++)
\r
731 MM_GetPtr(p,PMPageSize, gvar);
\r
732 if (gvar->mm.mmerror)
\r
735 gvar->pm.mm.MainPagesAvail++;
\r
736 gvar->pm.mm.MainMemUsed[i] = pmba_Allocated;
\r
738 MM_BombOnError(true, gvar);
\r
739 if (gvar->mm.mmerror)
\r
740 gvar->mm.mmerror = false;
\r
741 if (gvar->pm.mm.MainPagesAvail < PMMinMainMem)
\r
743 Quit (gvar, "PM_SetupMainMem: Not enough main memory");
\r
746 gvar->pm.mm.MainPresent = true;
\r
750 // PML_ShutdownMainMem() - Frees all of the main memory blocks used by the
\r
754 PML_ShutdownMainMem(global_game_variables_t *gvar)
\r
759 // DEBUG - mark pages as unallocated & decrease page count as appropriate
\r
760 for (i = 0,p = gvar->pm.mm.MainMemPages;i < PMMaxMainMem;i++,p++)
\r
762 MM_FreePtr(p, gvar);
\r
765 /////////////////////////////////////////////////////////////////////////////
\r
767 // File management code
\r
769 /////////////////////////////////////////////////////////////////////////////
\r
772 // PML_ReadFromFile() - Reads some data in from the page file
\r
775 PML_ReadFromFile(byte far *buf,long offset,word length, global_game_variables_t *gvar)
\r
779 Quit (gvar, "PML_ReadFromFile: Null pointer");
\r
784 Quit (gvar, "PML_ReadFromFile: Zero offset");
\r
787 if (lseek(gvar->pm.fi.PageFile,offset,SEEK_SET) != offset)
\r
789 Quit (gvar, "PML_ReadFromFile: Seek failed");
\r
792 if (!CA_FarRead(gvar->pm.fi.PageFile,buf,length, gvar))
\r
794 Quit (gvar, "PML_ReadFromFile: Read failed");
\r
800 // PML_OpenPageFile() - Opens the page file and sets up the page info
\r
803 PML_OpenPageFile(global_game_variables_t *gvar)
\r
808 dword far *offsetptr;
\r
809 word far *lengthptr;
\r
810 PageListStruct far *page;
\r
812 gvar->pm.fi.PageFile = open(gvar->pm.fi.PageFileName,O_RDONLY + O_BINARY);
\r
813 if (gvar->pm.fi.PageFile == -1)
\r
815 Quit (gvar, "PML_OpenPageFile: Unable to open page file");
\r
819 // Read in header variables
\r
820 read(gvar->pm.fi.PageFile,&gvar->pm.fi.ChunksInFile,sizeof(gvar->pm.fi.ChunksInFile));
\r
821 read(gvar->pm.fi.PageFile,&gvar->pm.fi.PMSpriteStart,sizeof(gvar->pm.fi.PMSpriteStart));
\r
822 read(gvar->pm.fi.PageFile,&gvar->pm.fi.PMSoundStart,sizeof(gvar->pm.fi.PMSoundStart));
\r
824 // Allocate and clear the page list
\r
825 gvar->pm.PMNumBlocks = gvar->pm.fi.ChunksInFile;
\r
826 MM_GetPtr((memptr *)&gvar->pm.PMSegPages, sizeof(PageListStruct) * (gvar->pm.PMNumBlocks), gvar);
\r
827 MM_SetLock((memptr *)&gvar->pm.PMSegPages,true, gvar);
\r
828 gvar->pm.PMPages = (PageListStruct far *)gvar->pm.PMSegPages;
\r
829 _fmemset(gvar->pm.PMPages,0,sizeof(PageListStruct) * gvar->pm.PMNumBlocks);
\r
831 // Read in the chunk offsets
\r
832 size = sizeof(dword) * gvar->pm.fi.ChunksInFile;
\r
833 MM_GetPtr((memptr *)&buf, size, gvar);
\r
834 if (!CA_FarRead(gvar->pm.fi.PageFile,(byte far *)buf,size, gvar))
\r
836 Quit (gvar, "PML_OpenPageFile: Offset read failed");
\r
839 offsetptr = (dword far *)buf;
\r
840 for (i = 0,page = gvar->pm.PMPages;i < gvar->pm.fi.ChunksInFile;i++,page++)
\r
841 page->offset = *offsetptr++;
\r
842 MM_FreePtr((memptr *)&buf, gvar);
\r
844 // Read in the chunk lengths
\r
845 size = sizeof(word) * gvar->pm.fi.ChunksInFile;
\r
846 MM_GetPtr(&buf,size, gvar);
\r
847 if (!CA_FarRead(gvar->pm.fi.PageFile,(byte far *)buf,size, gvar))
\r
849 Quit (gvar, "PML_OpenPageFile: Length read failed");
\r
852 lengthptr = (word far *)buf;
\r
853 for (i = 0,page = gvar->pm.PMPages;i < gvar->pm.fi.ChunksInFile;i++,page++)
\r
854 page->length = *lengthptr++;
\r
855 MM_FreePtr((memptr *)&buf, gvar);
\r
859 // PML_ClosePageFile() - Closes the page file
\r
862 PML_ClosePageFile(global_game_variables_t *gvar)
\r
864 if (gvar->pm.fi.PageFile != -1)
\r
865 close(gvar->pm.fi.PageFile);
\r
866 if (gvar->pm.PMSegPages)
\r
868 MM_SetLock((memptr *)&gvar->pm.PMSegPages,false, gvar);
\r
869 MM_FreePtr((void _seg *)&gvar->pm.PMSegPages, gvar);
\r
873 /////////////////////////////////////////////////////////////////////////////
\r
875 // Allocation, etc., code
\r
877 /////////////////////////////////////////////////////////////////////////////
\r
880 // PML_GetEMSAddress()
\r
882 // Page is in EMS, so figure out which EMS physical page should be used
\r
883 // to map our page in. If normal page, use EMS physical page 3, else
\r
884 // use the physical page specified by the lock type
\r
886 #ifndef __DEBUG_2__
\r
887 #pragma argsused // DEBUG - remove lock parameter
\r
889 PML_GetEMSAddress(int page,PMLockType lock, global_game_variables_t *gvar)
\r
892 word emsoff,emsbase,offset;
\r
894 emsoff = page & (PMEMSSubPage - 1);
\r
895 emsbase = page - emsoff;
\r
898 // See if this page is already mapped in
\r
899 for (i = 0;i < EMSFrameCount;i++)
\r
901 if (gvar->pm.emm.EMSList[i].baseEMSPage == emsbase)
\r
903 emspage = i; // Yep - don't do a redundant remapping
\r
908 // If page isn't already mapped in, find LRU EMS frame, and use it
\r
911 dword last = LONG_MAX;
\r
912 for (i = 0;i < EMSFrameCount;i++)
\r
914 if (gvar->pm.emm.EMSList[i].lastHit < last)
\r
917 last = gvar->pm.emm.EMSList[i].lastHit;
\r
921 gvar->pm.emm.EMSList[emspage].baseEMSPage = emsbase;
\r
922 PML_MapEMS(page / PMEMSSubPage,emspage, gvar);
\r
926 Quit (gvar, "PML_GetEMSAddress: EMS find failed");
\r
928 gvar->pm.emm.EMSList[emspage].lastHit = gvar->pm.PMFrameCount;
\r
929 offset = emspage * EMSPageSizeSeg;
\r
930 offset += emsoff * PMPageSizeSeg;
\r
931 return((memptr)(gvar->pm.emm.EMSPageFrame + offset));
\r
935 PML_GetEMSAddress(int page,PMLockType lock, global_game_variables_t *gvar)
\r
939 emspage = (lock < pml_EMSLock)? 3 : (lock - pml_EMSLock);
\r
941 PML_MapEMS(page / PMEMSSubPage,emspage);
\r
943 return((memptr)(EMSPageFrame + (emspage * EMSPageSizeSeg)
\r
944 + ((page & (PMEMSSubPage - 1)) * PMPageSizeSeg)));
\r
949 // PM_GetPageAddress() - Returns the address of a given page
\r
950 // Maps in EMS if necessary
\r
951 // Returns nil if block isn't cached into Main Memory or EMS
\r
955 PM_GetPageAddress(int pagenum, global_game_variables_t *gvar)
\r
957 PageListStruct far *page;
\r
959 page = &gvar->pm.PMPages[pagenum];
\r
960 if (page->mainPage != -1)
\r
961 return(gvar->pm.mm.MainMemPages[page->mainPage]);
\r
962 else if (page->emsPage != -1)
\r
963 return(PML_GetEMSAddress(page->emsPage,page->locked, gvar));
\r
969 // PML_GiveLRUPage() - Returns the page # of the least recently used
\r
970 // present & unlocked main/EMS page (or main page if mainonly is true)
\r
973 PML_GiveLRUPage(boolean mainonly, global_game_variables_t *gvar)
\r
977 PageListStruct far *page;
\r
979 for (i = 0,page = gvar->pm.PMPages,lru = -1,last = LONG_MAX;i < gvar->pm.fi.ChunksInFile;i++,page++)
\r
983 (page->lastHit < last)
\r
984 && ((page->emsPage != -1) || (page->mainPage != -1))
\r
985 && (page->locked == pml_Unlocked)
\r
986 && (!(mainonly && (page->mainPage == -1)))
\r
989 last = page->lastHit;
\r
995 Quit (gvar, "PML_GiveLRUPage: LRU Search failed");
\r
1000 // PML_GiveLRUXMSPage() - Returns the page # of the least recently used
\r
1001 // (and present) XMS page.
\r
1002 // This routine won't return the XMS page protected (by XMSProtectPage)
\r
1005 PML_GiveLRUXMSPage(global_game_variables_t *gvar)
\r
1009 PageListStruct far *page;
\r
1011 for (i = 0,page = gvar->pm.PMPages,lru = -1,last = LONG_MAX;i < gvar->pm.fi.ChunksInFile;i++,page++)
\r
1015 (page->xmsPage != -1)
\r
1016 && (page->lastHit < last)
\r
1017 && (i != gvar->pm.xmm.XMSProtectPage)
\r
1020 last = page->lastHit;
\r
1028 // PML_PutPageInXMS() - If page isn't in XMS, find LRU XMS page and replace
\r
1029 // it with the main/EMS page
\r
1032 PML_PutPageInXMS(int pagenum, global_game_variables_t *gvar)
\r
1035 PageListStruct far *page;
\r
1037 if (!gvar->pm.xmm.XMSPresent)
\r
1040 page = &gvar->pm.PMPages[pagenum];
\r
1041 if (page->xmsPage != -1)
\r
1042 return; // Already in XMS
\r
1044 if (gvar->pm.XMSPagesUsed < gvar->pm.xmm.XMSPagesAvail)
\r
1045 page->xmsPage = gvar->pm.XMSPagesUsed++;
\r
1048 usexms = PML_GiveLRUXMSPage(gvar);
\r
1050 Quit (gvar, "PML_PutPageInXMS: No XMS LRU");
\r
1051 page->xmsPage = gvar->pm.PMPages[usexms].xmsPage;
\r
1052 gvar->pm.PMPages[usexms].xmsPage = -1;
\r
1054 PML_CopyToXMS(PM_GetPageAddress(pagenum, gvar),page->xmsPage,page->length, gvar);
\r
1058 // PML_TransferPageSpace() - A page is being replaced, so give the new page
\r
1059 // the old one's address space. Returns the address of the new page.
\r
1062 PML_TransferPageSpace(int orig,int new, global_game_variables_t *gvar)
\r
1065 PageListStruct far *origpage,far *newpage;
\r
1068 Quit (gvar, "PML_TransferPageSpace: Identity replacement");
\r
1070 origpage = &gvar->pm.PMPages[orig];
\r
1071 newpage = &gvar->pm.PMPages[new];
\r
1073 if (origpage->locked != pml_Unlocked)
\r
1074 Quit (gvar, "PML_TransferPageSpace: Killing locked page");
\r
1076 if ((origpage->emsPage == -1) && (origpage->mainPage == -1))
\r
1077 Quit (gvar, "PML_TransferPageSpace: Reusing non-existent page");
\r
1079 // Copy page that's about to be purged into XMS
\r
1080 PML_PutPageInXMS(orig, gvar);
\r
1082 // Get the address, and force EMS into a physical page if necessary
\r
1083 addr = PM_GetPageAddress(orig, gvar);
\r
1085 // Steal the address
\r
1086 newpage->emsPage = origpage->emsPage;
\r
1087 newpage->mainPage = origpage->mainPage;
\r
1089 // Mark replaced page as purged
\r
1090 origpage->mainPage = origpage->emsPage = -1;
\r
1093 Quit (gvar, "PML_TransferPageSpace: Zero replacement");
\r
1099 // PML_GetAPageBuffer() - A page buffer is needed. Either get it from the
\r
1100 // main/EMS free pool, or use PML_GiveLRUPage() to find which page to
\r
1101 // steal the buffer from. Returns a far pointer to the page buffer, and
\r
1102 // sets the fields inside the given page structure appropriately.
\r
1103 // If mainonly is true, free EMS will be ignored, and only main pages
\r
1104 // will be looked at by PML_GiveLRUPage().
\r
1107 PML_GetAPageBuffer(int pagenum,boolean mainonly, global_game_variables_t *gvar)
\r
1109 byte far *addr = nil;
\r
1111 PMBlockAttr *used;
\r
1112 PageListStruct far *page;
\r
1114 page = &gvar->pm.PMPages[pagenum];
\r
1115 if ((gvar->pm.EMSPagesUsed < gvar->pm.emm.EMSPagesAvail) && !mainonly)
\r
1117 // There's remaining EMS - use it
\r
1118 page->emsPage = gvar->pm.EMSPagesUsed++;
\r
1119 addr = PML_GetEMSAddress(page->emsPage,page->locked, gvar);
\r
1121 else if (gvar->pm.MainPagesUsed < gvar->pm.mm.MainPagesAvail)
\r
1123 // There's remaining main memory - use it
\r
1124 for (i = 0,n = -1,used = gvar->pm.mm.MainMemUsed;i < PMMaxMainMem;i++,used++)
\r
1126 if ((*used & pmba_Allocated) && !(*used & pmba_Used))
\r
1129 *used |= pmba_Used;
\r
1134 Quit (gvar, "PML_GetPageBuffer: MainPagesAvail lied");
\r
1135 addr = gvar->pm.mm.MainMemPages[n];
\r
1137 Quit (gvar, "PML_GetPageBuffer: Purged main block");
\r
1138 page->mainPage = n;
\r
1139 gvar->pm.MainPagesUsed++;
\r
1142 addr = PML_TransferPageSpace(PML_GiveLRUPage(mainonly, gvar),pagenum, gvar);
\r
1145 Quit (gvar, "PML_GetPageBuffer: Search failed");
\r
1150 // PML_GetPageFromXMS() - If page is in XMS, find LRU main/EMS page and
\r
1151 // replace it with the page from XMS. If mainonly is true, will only
\r
1152 // search for LRU main page.
\r
1153 // XMSProtectPage is set to the page to be retrieved from XMS, so that if
\r
1154 // the page from which we're stealing the main/EMS from isn't in XMS,
\r
1155 // it won't copy over the page that we're trying to get from XMS.
\r
1156 // (pages that are being purged are copied into XMS, if possible)
\r
1159 PML_GetPageFromXMS(int pagenum,boolean mainonly, global_game_variables_t *gvar)
\r
1161 byte far *checkaddr;
\r
1162 memptr addr = nil;
\r
1163 PageListStruct far *page;
\r
1165 page = &gvar->pm.PMPages[pagenum];
\r
1166 if (gvar->pm.xmm.XMSPresent && (page->xmsPage != -1))
\r
1168 gvar->pm.xmm.XMSProtectPage = pagenum;
\r
1169 checkaddr = PML_GetAPageBuffer(pagenum,mainonly, gvar);
\r
1170 if (FP_OFF(checkaddr))
\r
1171 Quit (gvar, "PML_GetPageFromXMS: Non segment pointer");
\r
1172 addr = (memptr)FP_SEG(checkaddr);
\r
1173 PML_CopyFromXMS(addr,page->xmsPage,page->length, gvar);
\r
1174 gvar->pm.xmm.XMSProtectPage = -1;
\r
1181 // PML_LoadPage() - A page is not in main/EMS memory, and it's not in XMS.
\r
1182 // Load it into either main or EMS. If mainonly is true, the page will
\r
1183 // only be loaded into main.
\r
1186 PML_LoadPage(int pagenum,boolean mainonly, global_game_variables_t *gvar)
\r
1189 PageListStruct far *page;
\r
1191 addr = PML_GetAPageBuffer(pagenum,mainonly, gvar);
\r
1192 page = &gvar->pm.PMPages[pagenum];
\r
1193 PML_ReadFromFile(addr,page->offset,page->length, gvar);
\r
1197 // PM_GetPage() - Returns the address of the page, loading it if necessary
\r
1198 // First, check if in Main Memory or EMS
\r
1199 // Then, check XMS
\r
1200 // If not in XMS, load into Main Memory or EMS
\r
1204 PM_GetPage(int pagenum, global_game_variables_t *gvar)
\r
1208 if (pagenum >= gvar->pm.fi.ChunksInFile)
\r
1209 Quit (gvar, "PM_GetPage: Invalid page request");
\r
1211 //#ifdef __DEBUG_2__ // for debugging
\r
1213 mov dx,STATUS_REGISTER_1
\r
1216 mov al,ATR_OVERSCAN
\r
1218 mov al,10 // bright green
\r
1223 if (!(result = PM_GetPageAddress(pagenum, gvar)))
\r
1225 boolean mainonly = (pagenum >= gvar->pm.fi.PMSoundStart);
\r
1226 if (!gvar->pm.PMPages[pagenum].offset) // JDC: sparse page
\r
1227 Quit (gvar, "Tried to load a sparse page!");
\r
1228 if (!(result = PML_GetPageFromXMS(pagenum,mainonly, gvar)))
\r
1230 if (gvar->pm.PMPages[pagenum].lastHit == gvar->pm.PMFrameCount)
\r
1231 gvar->pm.PMThrashing++;
\r
1233 PML_LoadPage(pagenum,mainonly, gvar);
\r
1234 result = PM_GetPageAddress(pagenum, gvar);
\r
1237 gvar->pm.PMPages[pagenum].lastHit = gvar->pm.PMFrameCount;
\r
1239 //#ifdef __DEBUG_2__ // for debugging
\r
1241 mov dx,STATUS_REGISTER_1
\r
1244 mov al,ATR_OVERSCAN
\r
1248 mov al,0x20 // normal
\r
1258 // PM_SetPageLock() - Sets the lock type on a given page
\r
1259 // pml_Unlocked: Normal, page can be purged
\r
1260 // pml_Locked: Cannot be purged
\r
1261 // pml_EMS?: Same as pml_Locked, but if in EMS, use the physical page
\r
1262 // specified when returning the address. For sound stuff.
\r
1265 PM_SetPageLock(int pagenum,PMLockType lock, global_game_variables_t *gvar)
\r
1267 if (pagenum < gvar->pm.fi.PMSoundStart)
\r
1268 Quit (gvar, "PM_SetPageLock: Locking/unlocking non-sound page");
\r
1270 gvar->pm.PMPages[pagenum].locked = lock;
\r
1274 // PM_Preload() - Loads as many pages as possible into all types of memory.
\r
1275 // Calls the update function after each load, indicating the current
\r
1276 // page, and the total pages that need to be loaded (for thermometer).
\r
1279 PM_Preload(boolean (*update)(word current,word total), global_game_variables_t *gvar)
\r
1283 word current,total,
\r
1284 //totalnonxms,totalxms,
\r
1285 mainfree,maintotal,
\r
1286 //emstotal,emsfree,
\r
1289 PageListStruct __far *p;
\r
1291 mainfree = (gvar->pm.mm.MainPagesAvail - gvar->pm.MainPagesUsed) + (gvar->pm.emm.EMSPagesAvail - gvar->pm.EMSPagesUsed);
\r
1292 xmsfree = (gvar->pm.xmm.XMSPagesAvail - gvar->pm.XMSPagesUsed);
\r
1294 xmstotal = maintotal = 0;
\r
1296 for (i = 0;i < gvar->pm.fi.ChunksInFile;i++)
\r
1298 if (!gvar->pm.PMPages[i].offset)
\r
1299 continue; // sparse
\r
1301 if ( gvar->pm.PMPages[i].emsPage != -1 || gvar->pm.PMPages[i].mainPage != -1 )
\r
1302 continue; // already in main mem
\r
1309 else if ( xmsfree && (gvar->pm.PMPages[i].xmsPage == -1) )
\r
1317 total = maintotal + xmstotal;
\r
1326 // cache main/ems blocks
\r
1330 while ( !gvar->pm.PMPages[page].offset || gvar->pm.PMPages[page].mainPage != -1
\r
1331 || gvar->pm.PMPages[page].emsPage != -1 )
\r
1334 if (page >= gvar->pm.fi.ChunksInFile)
\r
1335 Quit (gvar, "PM_Preload: Pages>=gvar->pm.fi.ChunksInFile");
\r
1337 PM_GetPage(page, gvar);
\r
1342 update(current,total);
\r
1346 // load stuff to XMS
\r
1350 for (oogypage = 0 ; gvar->pm.PMPages[oogypage].mainPage == -1 ; oogypage++)
\r
1352 addr = PM_GetPage(oogypage, gvar);
\r
1354 Quit (gvar, "PM_Preload: XMS buffer failed");
\r
1358 while ( !gvar->pm.PMPages[page].offset || gvar->pm.PMPages[page].xmsPage != -1 )
\r
1361 if (page >= gvar->pm.fi.ChunksInFile)
\r
1362 Quit (gvar, "PM_Preload: Pages>=gvar->pm.fi.ChunksInFile");
\r
1364 p = &gvar->pm.PMPages[page];
\r
1366 p->xmsPage = gvar->pm.XMSPagesUsed++;
\r
1367 if (gvar->pm.XMSPagesUsed > gvar->pm.xmm.XMSPagesAvail)
\r
1368 Quit (gvar, "PM_Preload: Exceeded XMS pages");
\r
1369 if (p->length > PMPageSize)
\r
1370 Quit (gvar, "PM_Preload: Page too long");
\r
1372 PML_ReadFromFile((byte far *)addr,p->offset,p->length, gvar);
\r
1373 PML_CopyToXMS((byte far *)addr,p->xmsPage,p->length, gvar);
\r
1378 update(current,total);
\r
1381 p = &gvar->pm.PMPages[oogypage];
\r
1382 PML_ReadFromFile((byte far *)addr,p->offset,p->length, gvar);
\r
1385 update(total,total);
\r
1388 /////////////////////////////////////////////////////////////////////////////
\r
1392 /////////////////////////////////////////////////////////////////////////////
\r
1395 // PM_NextFrame() - Increments the frame counter and adjusts the thrash
\r
1396 // avoidence variables
\r
1398 // If currently in panic mode (to avoid thrashing), check to see if the
\r
1399 // appropriate number of frames have passed since the last time that
\r
1400 // we would have thrashed. If so, take us out of panic mode.
\r
1404 PM_NextFrame(global_game_variables_t *gvar)
\r
1408 // Frame count overrun - kill the LRU hit entries & reset frame count
\r
1409 if (++gvar->pm.PMFrameCount >= LONG_MAX - 4)
\r
1411 for (i = 0;i < gvar->pm.PMNumBlocks;i++)
\r
1412 gvar->pm.PMPages[i].lastHit = 0;
\r
1413 gvar->pm.PMFrameCount = 0;
\r
1417 for (i = 0;i < gvar->pm.fi.PMSoundStart;i++)
\r
1419 if (gvar->pm.PMPages[i].locked)
\r
1422 sprintf(buf,"PM_NextFrame: Page %d is locked",i);
\r
1428 if (gvar->pm.PMPanicMode)
\r
1430 // DEBUG - set border color
\r
1431 if ((!gvar->pm.PMThrashing) && (!--gvar->pm.PMPanicMode))
\r
1433 // DEBUG - reset border color
\r
1436 if (gvar->pm.PMThrashing >= PMThrashThreshold)
\r
1437 gvar->pm.PMPanicMode = PMUnThrashThreshold;
\r
1438 gvar->pm.PMThrashing = false;
\r
1442 // PM_Reset() - Sets up caching structures
\r
1445 PM_Reset(global_game_variables_t *gvar)
\r
1448 PageListStruct far *page;
\r
1450 gvar->pm.xmm.XMSPagesAvail = gvar->pm.xmm.XMSAvail / PMPageSizeKB;
\r
1452 gvar->pm.emm.EMSPagesAvail = gvar->pm.emm.EMSAvail * (EMSPageSizeKB / PMPageSizeKB);
\r
1453 gvar->pm.emm.EMSPhysicalPage = 0;
\r
1455 gvar->pm.MainPagesUsed = gvar->pm.EMSPagesUsed = gvar->pm.XMSPagesUsed = 0;
\r
1457 gvar->pm.PMPanicMode = false;
\r
1459 gvar->pm.fi.PageFile = -1;
\r
1460 gvar->pm.xmm.XMSProtectPage = -1;
\r
1462 // Initialize page list
\r
1463 for (i = 0,page = gvar->pm.PMPages;i < gvar->pm.PMNumBlocks;i++,page++)
\r
1465 page->mainPage = -1;
\r
1466 page->emsPage = -1;
\r
1467 page->xmsPage = -1;
\r
1468 page->locked = false;
\r
1473 // PM_Startup() - Start up the Page Mgr
\r
1476 PM_Startup(global_game_variables_t *gvar)
\r
1478 boolean nomain,noems,noxms;
\r
1481 if (gvar->pm.PMStarted)
\r
1484 //0000+=+=strcpy(&(gvar->pm.fi.PageFileName), "VSWAP.");
\r
1486 nomain = noems = noxms = false;
\r
1487 for (i = 1;i < _argc;i++)
\r
1489 switch (US_CheckParm(_argv[i],ParmStrings))
\r
1503 //0000+=+=PML_OpenPageFile(gvar);
\r
1505 if (!noems && MML_CheckForEMS())
\r
1506 PML_StartupEMS(gvar);
\r
1507 if (!noxms && MML_CheckForXMS())
\r
1508 PML_StartupXMS(gvar);
\r
1510 PML_StartupMainMem(gvar);
\r
1512 if (!gvar->pm.mm.MainPresent && !gvar->pm.emm.EMSPresent && !gvar->pm.xmm.XMSPresent)
\r
1514 Quit (gvar, "PM_Startup: No main or EMS\n");
\r
1520 gvar->pm.PMStarted = true;
\r
1524 // PM_Shutdown() - Shut down the Page Mgr
\r
1527 PM_Shutdown(global_game_variables_t *gvar)
\r
1529 if(MML_CheckForXMS()) PML_ShutdownXMS(gvar);
\r
1530 if(MML_CheckForEMS()) PML_ShutdownEMS(gvar);
\r
1532 if (!gvar->pm.PMStarted)
\r
1535 //0000+=+=PML_ClosePageFile(gvar);
\r
1537 PML_ShutdownMainMem(gvar);
\r