wolf3d

The original open source release of Wolfenstein 3D
Log | Files | Refs

ID_PM.C (26747B)


      1 //
      2 //	ID_PM.C
      3 //	Id Engine's Page Manager v1.0
      4 //	Primary coder: Jason Blochowiak
      5 //
      6 
      7 #include "ID_HEADS.H"
      8 #pragma hdrstop
      9 
     10 //	Main Mem specific variables
     11 	boolean			MainPresent;
     12 	memptr			MainMemPages[PMMaxMainMem];
     13 	PMBlockAttr		MainMemUsed[PMMaxMainMem];
     14 	int				MainPagesAvail;
     15 
     16 //	EMS specific variables
     17 	boolean			EMSPresent;
     18 	word			EMSAvail,EMSPagesAvail,EMSHandle,
     19 					EMSPageFrame,EMSPhysicalPage;
     20 	EMSListStruct	EMSList[EMSFrameCount];
     21 
     22 //	XMS specific variables
     23 	boolean			XMSPresent;
     24 	word			XMSAvail,XMSPagesAvail,XMSHandle;
     25 	longword		XMSDriver;
     26 	int				XMSProtectPage = -1;
     27 
     28 //	File specific variables
     29 	char			PageFileName[13] = {"VSWAP."};
     30 	int				PageFile = -1;
     31 	word			ChunksInFile;
     32 	word			PMSpriteStart,PMSoundStart;
     33 
     34 //	General usage variables
     35 	boolean			PMStarted,
     36 					PMPanicMode,
     37 					PMThrashing;
     38 	word			XMSPagesUsed,
     39 					EMSPagesUsed,
     40 					MainPagesUsed,
     41 					PMNumBlocks;
     42 	long			PMFrameCount;
     43 	PageListStruct	far *PMPages,
     44 					_seg *PMSegPages;
     45 
     46 static	char		*ParmStrings[] = {"nomain","noems","noxms",nil};
     47 
     48 /////////////////////////////////////////////////////////////////////////////
     49 //
     50 //	EMS Management code
     51 //
     52 /////////////////////////////////////////////////////////////////////////////
     53 
     54 //
     55 //	PML_MapEMS() - Maps a logical page to a physical page
     56 //
     57 void
     58 PML_MapEMS(word logical,word physical)
     59 {
     60 	_AL = physical;
     61 	_BX = logical;
     62 	_DX = EMSHandle;
     63 	_AH = EMS_MAPPAGE;
     64 asm	int	EMS_INT
     65 
     66 	if (_AH)
     67 		Quit("PML_MapEMS: Page mapping failed");
     68 }
     69 
     70 //
     71 //	PML_StartupEMS() - Sets up EMS for Page Mgr's use
     72 //		Checks to see if EMS driver is present
     73 //      Verifies that EMS hardware is present
     74 //		Make sure that EMS version is 3.2 or later
     75 //		If there's more than our minimum (2 pages) available, allocate it (up
     76 //			to the maximum we need)
     77 //
     78 
     79 	char	EMMDriverName[9] = "EMMXXXX0";
     80 
     81 boolean
     82 PML_StartupEMS(void)
     83 {
     84 	int		i;
     85 	long	size;
     86 
     87 	EMSPresent = false;			// Assume that we'll fail
     88 	EMSAvail = 0;
     89 
     90 	_DX = (word)EMMDriverName;
     91 	_AX = 0x3d00;
     92 	geninterrupt(0x21);			// try to open EMMXXXX0 device
     93 asm	jnc	gothandle
     94 	goto error;
     95 
     96 gothandle:
     97 	_BX = _AX;
     98 	_AX = 0x4400;
     99 	geninterrupt(0x21);			// get device info
    100 asm	jnc	gotinfo;
    101 	goto error;
    102 
    103 gotinfo:
    104 asm	and	dx,0x80
    105 	if (!_DX)
    106 		goto error;
    107 
    108 	_AX = 0x4407;
    109 	geninterrupt(0x21);			// get status
    110 asm	jc	error
    111 	if (!_AL)
    112 		goto error;
    113 
    114 	_AH = 0x3e;
    115 	geninterrupt(0x21);			// close handle
    116 
    117 	_AH = EMS_STATUS;
    118 	geninterrupt(EMS_INT);
    119 	if (_AH)
    120 		goto error;				// make sure EMS hardware is present
    121 
    122 	_AH = EMS_VERSION;
    123 	geninterrupt(EMS_INT);
    124 	if (_AH || (_AL < 0x32))	// only work on EMS 3.2 or greater (silly, but...)
    125 		goto error;
    126 
    127 	_AH = EMS_GETFRAME;
    128 	geninterrupt(EMS_INT);
    129 	if (_AH)
    130 		goto error;				// find the page frame address
    131 	EMSPageFrame = _BX;
    132 
    133 	_AH = EMS_GETPAGES;
    134 	geninterrupt(EMS_INT);
    135 	if (_AH)
    136 		goto error;
    137 	if (_BX < 2)
    138 		goto error;         	// Require at least 2 pages (32k)
    139 	EMSAvail = _BX;
    140 
    141 	// Don't hog all available EMS
    142 	size = EMSAvail * (long)EMSPageSize;
    143 	if (size - (EMSPageSize * 2) > (ChunksInFile * (long)PMPageSize))
    144 	{
    145 		size = (ChunksInFile * (long)PMPageSize) + EMSPageSize;
    146 		EMSAvail = size / EMSPageSize;
    147 	}
    148 
    149 	_AH = EMS_ALLOCPAGES;
    150 	_BX = EMSAvail;
    151 	geninterrupt(EMS_INT);
    152 	if (_AH)
    153 		goto error;
    154 	EMSHandle = _DX;
    155 
    156 	mminfo.EMSmem += EMSAvail * (long)EMSPageSize;
    157 
    158 	// Initialize EMS mapping cache
    159 	for (i = 0;i < EMSFrameCount;i++)
    160 		EMSList[i].baseEMSPage = -1;
    161 
    162 	EMSPresent = true;			// We have EMS
    163 
    164 error:
    165 	return(EMSPresent);
    166 }
    167 
    168 //
    169 //	PML_ShutdownEMS() - If EMS was used, deallocate it
    170 //
    171 void
    172 PML_ShutdownEMS(void)
    173 {
    174 	if (EMSPresent)
    175 	{
    176 	asm	mov	ah,EMS_FREEPAGES
    177 	asm	mov	dx,[EMSHandle]
    178 	asm	int	EMS_INT
    179 		if (_AH)
    180 			Quit ("PML_ShutdownEMS: Error freeing EMS");
    181 	}
    182 }
    183 
    184 /////////////////////////////////////////////////////////////////////////////
    185 //
    186 //	XMS Management code
    187 //
    188 /////////////////////////////////////////////////////////////////////////////
    189 
    190 //
    191 //	PML_StartupXMS() - Starts up XMS for the Page Mgr's use
    192 //		Checks for presence of an XMS driver
    193 //		Makes sure that there's at least a page of XMS available
    194 //		Allocates any remaining XMS (rounded down to the nearest page size)
    195 //
    196 boolean
    197 PML_StartupXMS(void)
    198 {
    199 	XMSPresent = false;					// Assume failure
    200 	XMSAvail = 0;
    201 
    202 asm	mov	ax,0x4300
    203 asm	int	XMS_INT         				// Check for presence of XMS driver
    204 	if (_AL != 0x80)
    205 		goto error;
    206 
    207 
    208 asm	mov	ax,0x4310
    209 asm	int	XMS_INT							// Get address of XMS driver
    210 asm	mov	[WORD PTR XMSDriver],bx
    211 asm	mov	[WORD PTR XMSDriver+2],es		// function pointer to XMS driver
    212 
    213 	XMS_CALL(XMS_QUERYFREE);			// Find out how much XMS is available
    214 	XMSAvail = _AX;
    215 	if (!_AX)				// AJR: bugfix 10/8/92
    216 		goto error;
    217 
    218 	XMSAvail &= ~(PMPageSizeKB - 1);	// Round off to nearest page size
    219 	if (XMSAvail < (PMPageSizeKB * 2))	// Need at least 2 pages
    220 		goto error;
    221 
    222 	_DX = XMSAvail;
    223 	XMS_CALL(XMS_ALLOC);				// And do the allocation
    224 	XMSHandle = _DX;
    225 
    226 	if (!_AX)				// AJR: bugfix 10/8/92
    227 	{
    228 		XMSAvail = 0;
    229 		goto error;
    230 	}
    231 
    232 	mminfo.XMSmem += XMSAvail * 1024;
    233 
    234 	XMSPresent = true;
    235 error:
    236 	return(XMSPresent);
    237 }
    238 
    239 //
    240 //	PML_XMSCopy() - Copies a main/EMS page to or from XMS
    241 //		Will round an odd-length request up to the next even value
    242 //
    243 void
    244 PML_XMSCopy(boolean toxms,byte far *addr,word xmspage,word length)
    245 {
    246 	longword	xoffset;
    247 	struct
    248 	{
    249 		longword	length;
    250 		word		source_handle;
    251 		longword	source_offset;
    252 		word		target_handle;
    253 		longword	target_offset;
    254 	} copy;
    255 
    256 	if (!addr)
    257 		Quit("PML_XMSCopy: zero address");
    258 
    259 	xoffset = (longword)xmspage * PMPageSize;
    260 
    261 	copy.length = (length + 1) & ~1;
    262 	copy.source_handle = toxms? 0 : XMSHandle;
    263 	copy.source_offset = toxms? (long)addr : xoffset;
    264 	copy.target_handle = toxms? XMSHandle : 0;
    265 	copy.target_offset = toxms? xoffset : (long)addr;
    266 
    267 asm	push si
    268 	_SI = (word)&copy;
    269 	XMS_CALL(XMS_MOVE);
    270 asm	pop	si
    271 	if (!_AX)
    272 		Quit("PML_XMSCopy: Error on copy");
    273 }
    274 
    275 #if 1
    276 #define	PML_CopyToXMS(s,t,l)	PML_XMSCopy(true,(s),(t),(l))
    277 #define	PML_CopyFromXMS(t,s,l)	PML_XMSCopy(false,(t),(s),(l))
    278 #else
    279 //
    280 //	PML_CopyToXMS() - Copies the specified number of bytes from the real mode
    281 //		segment address to the specified XMS page
    282 //
    283 void
    284 PML_CopyToXMS(byte far *source,int targetpage,word length)
    285 {
    286 	PML_XMSCopy(true,source,targetpage,length);
    287 }
    288 
    289 //
    290 //	PML_CopyFromXMS() - Copies the specified number of bytes from an XMS
    291 //		page to the specified real mode address
    292 //
    293 void
    294 PML_CopyFromXMS(byte far *target,int sourcepage,word length)
    295 {
    296 	PML_XMSCopy(false,target,sourcepage,length);
    297 }
    298 #endif
    299 
    300 //
    301 //	PML_ShutdownXMS()
    302 //
    303 void
    304 PML_ShutdownXMS(void)
    305 {
    306 	if (XMSPresent)
    307 	{
    308 		_DX = XMSHandle;
    309 		XMS_CALL(XMS_FREE);
    310 		if (_BL)
    311 			Quit("PML_ShutdownXMS: Error freeing XMS");
    312 	}
    313 }
    314 
    315 /////////////////////////////////////////////////////////////////////////////
    316 //
    317 //	Main memory code
    318 //
    319 /////////////////////////////////////////////////////////////////////////////
    320 
    321 //
    322 //	PM_SetMainMemPurge() - Sets the purge level for all allocated main memory
    323 //		blocks. This shouldn't be called directly - the PM_LockMainMem() and
    324 //		PM_UnlockMainMem() macros should be used instead.
    325 //
    326 void
    327 PM_SetMainMemPurge(int level)
    328 {
    329 	int	i;
    330 
    331 	for (i = 0;i < PMMaxMainMem;i++)
    332 		if (MainMemPages[i])
    333 			MM_SetPurge(&MainMemPages[i],level);
    334 }
    335 
    336 //
    337 //	PM_CheckMainMem() - If something besides the Page Mgr makes requests of
    338 //		the Memory Mgr, some of the Page Mgr's blocks may have been purged,
    339 //		so this function runs through the block list and checks to see if
    340 //		any of the blocks have been purged. If so, it marks the corresponding
    341 //		page as purged & unlocked, then goes through the block list and
    342 //		tries to reallocate any blocks that have been purged.
    343 //	This routine now calls PM_LockMainMem() to make sure that any allocation
    344 //		attempts made during the block reallocation sweep don't purge any
    345 //		of the other blocks. Because PM_LockMainMem() is called,
    346 //		PM_UnlockMainMem() needs to be called before any other part of the
    347 //		program makes allocation requests of the Memory Mgr.
    348 //
    349 void
    350 PM_CheckMainMem(void)
    351 {
    352 	boolean			allocfailed;
    353 	int				i,n;
    354 	memptr			*p;
    355 	PMBlockAttr		*used;
    356 	PageListStruct	far *page;
    357 
    358 	if (!MainPresent)
    359 		return;
    360 
    361 	for (i = 0,page = PMPages;i < ChunksInFile;i++,page++)
    362 	{
    363 		n = page->mainPage;
    364 		if (n != -1)						// Is the page using main memory?
    365 		{
    366 			if (!MainMemPages[n])			// Yep, was the block purged?
    367 			{
    368 				page->mainPage = -1;		// Yes, mark page as purged & unlocked
    369 				page->locked = pml_Unlocked;
    370 			}
    371 		}
    372 	}
    373 
    374 	// Prevent allocation attempts from purging any of our other blocks
    375 	PM_LockMainMem();
    376 	allocfailed = false;
    377 	for (i = 0,p = MainMemPages,used = MainMemUsed;i < PMMaxMainMem;i++,p++,used++)
    378 	{
    379 		if (!*p)							// If the page got purged
    380 		{
    381 			if (*used & pmba_Allocated)		// If it was allocated
    382 			{
    383 				*used &= ~pmba_Allocated;	// Mark as unallocated
    384 				MainPagesAvail--;			// and decrease available count
    385 			}
    386 
    387 			if (*used & pmba_Used)			// If it was used
    388 			{
    389 				*used &= ~pmba_Used;		// Mark as unused
    390 				MainPagesUsed--;			// and decrease used count
    391 			}
    392 
    393 			if (!allocfailed)
    394 			{
    395 				MM_BombOnError(false);
    396 				MM_GetPtr(p,PMPageSize);		// Try to reallocate
    397 				if (mmerror)					// If it failed,
    398 					allocfailed = true;			//  don't try any more allocations
    399 				else							// If it worked,
    400 				{
    401 					*used |= pmba_Allocated;	// Mark as allocated
    402 					MainPagesAvail++;			// and increase available count
    403 				}
    404 				MM_BombOnError(true);
    405 			}
    406 		}
    407 	}
    408 	if (mmerror)
    409 		mmerror = false;
    410 }
    411 
    412 //
    413 //	PML_StartupMainMem() - Allocates as much main memory as is possible for
    414 //		the Page Mgr. The memory is allocated as non-purgeable, so if it's
    415 //		necessary to make requests of the Memory Mgr, PM_UnlockMainMem()
    416 //		needs to be called.
    417 //
    418 void
    419 PML_StartupMainMem(void)
    420 {
    421 	int		i,n;
    422 	memptr	*p;
    423 
    424 	MainPagesAvail = 0;
    425 	MM_BombOnError(false);
    426 	for (i = 0,p = MainMemPages;i < PMMaxMainMem;i++,p++)
    427 	{
    428 		MM_GetPtr(p,PMPageSize);
    429 		if (mmerror)
    430 			break;
    431 
    432 		MainPagesAvail++;
    433 		MainMemUsed[i] = pmba_Allocated;
    434 	}
    435 	MM_BombOnError(true);
    436 	if (mmerror)
    437 		mmerror = false;
    438 	if (MainPagesAvail < PMMinMainMem)
    439 		Quit("PM_SetupMainMem: Not enough main memory");
    440 	MainPresent = true;
    441 }
    442 
    443 //
    444 //	PML_ShutdownMainMem() - Frees all of the main memory blocks used by the
    445 //		Page Mgr.
    446 //
    447 void
    448 PML_ShutdownMainMem(void)
    449 {
    450 	int		i;
    451 	memptr	*p;
    452 
    453 	// DEBUG - mark pages as unallocated & decrease page count as appropriate
    454 	for (i = 0,p = MainMemPages;i < PMMaxMainMem;i++,p++)
    455 		if (*p)
    456 			MM_FreePtr(p);
    457 }
    458 
    459 /////////////////////////////////////////////////////////////////////////////
    460 //
    461 //	File management code
    462 //
    463 /////////////////////////////////////////////////////////////////////////////
    464 
    465 //
    466 //	PML_ReadFromFile() - Reads some data in from the page file
    467 //
    468 void
    469 PML_ReadFromFile(byte far *buf,long offset,word length)
    470 {
    471 	if (!buf)
    472 		Quit("PML_ReadFromFile: Null pointer");
    473 	if (!offset)
    474 		Quit("PML_ReadFromFile: Zero offset");
    475 	if (lseek(PageFile,offset,SEEK_SET) != offset)
    476 		Quit("PML_ReadFromFile: Seek failed");
    477 	if (!CA_FarRead(PageFile,buf,length))
    478 		Quit("PML_ReadFromFile: Read failed");
    479 }
    480 
    481 //
    482 //	PML_OpenPageFile() - Opens the page file and sets up the page info
    483 //
    484 void
    485 PML_OpenPageFile(void)
    486 {
    487 	int				i;
    488 	long			size;
    489 	void			_seg *buf;
    490 	longword		far *offsetptr;
    491 	word			far *lengthptr;
    492 	PageListStruct	far *page;
    493 
    494 	PageFile = open(PageFileName,O_RDONLY + O_BINARY);
    495 	if (PageFile == -1)
    496 		Quit("PML_OpenPageFile: Unable to open page file");
    497 
    498 	// Read in header variables
    499 	read(PageFile,&ChunksInFile,sizeof(ChunksInFile));
    500 	read(PageFile,&PMSpriteStart,sizeof(PMSpriteStart));
    501 	read(PageFile,&PMSoundStart,sizeof(PMSoundStart));
    502 
    503 	// Allocate and clear the page list
    504 	PMNumBlocks = ChunksInFile;
    505 	MM_GetPtr(&(memptr)PMSegPages,sizeof(PageListStruct) * PMNumBlocks);
    506 	MM_SetLock(&(memptr)PMSegPages,true);
    507 	PMPages = (PageListStruct far *)PMSegPages;
    508 	_fmemset(PMPages,0,sizeof(PageListStruct) * PMNumBlocks);
    509 
    510 	// Read in the chunk offsets
    511 	size = sizeof(longword) * ChunksInFile;
    512 	MM_GetPtr(&buf,size);
    513 	if (!CA_FarRead(PageFile,(byte far *)buf,size))
    514 		Quit("PML_OpenPageFile: Offset read failed");
    515 	offsetptr = (longword far *)buf;
    516 	for (i = 0,page = PMPages;i < ChunksInFile;i++,page++)
    517 		page->offset = *offsetptr++;
    518 	MM_FreePtr(&buf);
    519 
    520 	// Read in the chunk lengths
    521 	size = sizeof(word) * ChunksInFile;
    522 	MM_GetPtr(&buf,size);
    523 	if (!CA_FarRead(PageFile,(byte far *)buf,size))
    524 		Quit("PML_OpenPageFile: Length read failed");
    525 	lengthptr = (word far *)buf;
    526 	for (i = 0,page = PMPages;i < ChunksInFile;i++,page++)
    527 		page->length = *lengthptr++;
    528 	MM_FreePtr(&buf);
    529 }
    530 
    531 //
    532 //  PML_ClosePageFile() - Closes the page file
    533 //
    534 void
    535 PML_ClosePageFile(void)
    536 {
    537 	if (PageFile != -1)
    538 		close(PageFile);
    539 	if (PMSegPages)
    540 	{
    541 		MM_SetLock(&(memptr)PMSegPages,false);
    542 		MM_FreePtr(&(void _seg *)PMSegPages);
    543 	}
    544 }
    545 
    546 /////////////////////////////////////////////////////////////////////////////
    547 //
    548 //	Allocation, etc., code
    549 //
    550 /////////////////////////////////////////////////////////////////////////////
    551 
    552 //
    553 //	PML_GetEMSAddress()
    554 //
    555 // 		Page is in EMS, so figure out which EMS physical page should be used
    556 //  		to map our page in. If normal page, use EMS physical page 3, else
    557 //  		use the physical page specified by the lock type
    558 //
    559 #if 1
    560 #pragma argsused	// DEBUG - remove lock parameter
    561 memptr
    562 PML_GetEMSAddress(int page,PMLockType lock)
    563 {
    564 	int		i,emspage;
    565 	word	emsoff,emsbase,offset;
    566 
    567 	emsoff = page & (PMEMSSubPage - 1);
    568 	emsbase = page - emsoff;
    569 
    570 	emspage = -1;
    571 	// See if this page is already mapped in
    572 	for (i = 0;i < EMSFrameCount;i++)
    573 	{
    574 		if (EMSList[i].baseEMSPage == emsbase)
    575 		{
    576 			emspage = i;	// Yep - don't do a redundant remapping
    577 			break;
    578 		}
    579 	}
    580 
    581 	// If page isn't already mapped in, find LRU EMS frame, and use it
    582 	if (emspage == -1)
    583 	{
    584 		longword last = MAXLONG;
    585 		for (i = 0;i < EMSFrameCount;i++)
    586 		{
    587 			if (EMSList[i].lastHit < last)
    588 			{
    589 				emspage = i;
    590 				last = EMSList[i].lastHit;
    591 			}
    592 		}
    593 
    594 		EMSList[emspage].baseEMSPage = emsbase;
    595 		PML_MapEMS(page / PMEMSSubPage,emspage);
    596 	}
    597 
    598 	if (emspage == -1)
    599 		Quit("PML_GetEMSAddress: EMS find failed");
    600 
    601 	EMSList[emspage].lastHit = PMFrameCount;
    602 	offset = emspage * EMSPageSizeSeg;
    603 	offset += emsoff * PMPageSizeSeg;
    604 	return((memptr)(EMSPageFrame + offset));
    605 }
    606 #else
    607 memptr
    608 PML_GetEMSAddress(int page,PMLockType lock)
    609 {
    610 	word	emspage;
    611 
    612 	emspage = (lock < pml_EMSLock)? 3 : (lock - pml_EMSLock);
    613 
    614 	PML_MapEMS(page / PMEMSSubPage,emspage);
    615 
    616 	return((memptr)(EMSPageFrame + (emspage * EMSPageSizeSeg)
    617 			+ ((page & (PMEMSSubPage - 1)) * PMPageSizeSeg)));
    618 }
    619 #endif
    620 
    621 //
    622 //	PM_GetPageAddress() - Returns the address of a given page
    623 //		Maps in EMS if necessary
    624 //		Returns nil if block isn't cached into Main Memory or EMS
    625 //
    626 //
    627 memptr
    628 PM_GetPageAddress(int pagenum)
    629 {
    630 	PageListStruct	far *page;
    631 
    632 	page = &PMPages[pagenum];
    633 	if (page->mainPage != -1)
    634 		return(MainMemPages[page->mainPage]);
    635 	else if (page->emsPage != -1)
    636 		return(PML_GetEMSAddress(page->emsPage,page->locked));
    637 	else
    638 		return(nil);
    639 }
    640 
    641 //
    642 //	PML_GiveLRUPage() - Returns the page # of the least recently used
    643 //		present & unlocked main/EMS page (or main page if mainonly is true)
    644 //
    645 int
    646 PML_GiveLRUPage(boolean mainonly)
    647 {
    648 	int				i,lru;
    649 	long			last;
    650 	PageListStruct	far *page;
    651 
    652 	for (i = 0,page = PMPages,lru = -1,last = MAXLONG;i < ChunksInFile;i++,page++)
    653 	{
    654 		if
    655 		(
    656 			(page->lastHit < last)
    657 		&&	((page->emsPage != -1) || (page->mainPage != -1))
    658 		&& 	(page->locked == pml_Unlocked)
    659 		&&	(!(mainonly && (page->mainPage == -1)))
    660 		)
    661 		{
    662 			last = page->lastHit;
    663 			lru = i;
    664 		}
    665 	}
    666 
    667 	if (lru == -1)
    668 		Quit("PML_GiveLRUPage: LRU Search failed");
    669 	return(lru);
    670 }
    671 
    672 //
    673 //	PML_GiveLRUXMSPage() - Returns the page # of the least recently used
    674 //		(and present) XMS page.
    675 //	This routine won't return the XMS page protected (by XMSProtectPage)
    676 //
    677 int
    678 PML_GiveLRUXMSPage(void)
    679 {
    680 	int				i,lru;
    681 	long			last;
    682 	PageListStruct	far *page;
    683 
    684 	for (i = 0,page = PMPages,lru = -1,last = MAXLONG;i < ChunksInFile;i++,page++)
    685 	{
    686 		if
    687 		(
    688 			(page->xmsPage != -1)
    689 		&&	(page->lastHit < last)
    690 		&&	(i != XMSProtectPage)
    691 		)
    692 		{
    693 			last = page->lastHit;
    694 			lru = i;
    695 		}
    696 	}
    697 	return(lru);
    698 }
    699 
    700 //
    701 //	PML_PutPageInXMS() - If page isn't in XMS, find LRU XMS page and replace
    702 //		it with the main/EMS page
    703 //
    704 void
    705 PML_PutPageInXMS(int pagenum)
    706 {
    707 	int				usexms;
    708 	PageListStruct	far *page;
    709 
    710 	if (!XMSPresent)
    711 		return;
    712 
    713 	page = &PMPages[pagenum];
    714 	if (page->xmsPage != -1)
    715 		return;					// Already in XMS
    716 
    717 	if (XMSPagesUsed < XMSPagesAvail)
    718 		page->xmsPage = XMSPagesUsed++;
    719 	else
    720 	{
    721 		usexms = PML_GiveLRUXMSPage();
    722 		if (usexms == -1)
    723 			Quit("PML_PutPageInXMS: No XMS LRU");
    724 		page->xmsPage = PMPages[usexms].xmsPage;
    725 		PMPages[usexms].xmsPage = -1;
    726 	}
    727 	PML_CopyToXMS(PM_GetPageAddress(pagenum),page->xmsPage,page->length);
    728 }
    729 
    730 //
    731 //	PML_TransferPageSpace() - A page is being replaced, so give the new page
    732 //		the old one's address space. Returns the address of the new page.
    733 //
    734 memptr
    735 PML_TransferPageSpace(int orig,int new)
    736 {
    737 	memptr			addr;
    738 	PageListStruct	far *origpage,far *newpage;
    739 
    740 	if (orig == new)
    741 		Quit("PML_TransferPageSpace: Identity replacement");
    742 
    743 	origpage = &PMPages[orig];
    744 	newpage = &PMPages[new];
    745 
    746 	if (origpage->locked != pml_Unlocked)
    747 		Quit("PML_TransferPageSpace: Killing locked page");
    748 
    749 	if ((origpage->emsPage == -1) && (origpage->mainPage == -1))
    750 		Quit("PML_TransferPageSpace: Reusing non-existent page");
    751 
    752 	// Copy page that's about to be purged into XMS
    753 	PML_PutPageInXMS(orig);
    754 
    755 	// Get the address, and force EMS into a physical page if necessary
    756 	addr = PM_GetPageAddress(orig);
    757 
    758 	// Steal the address
    759 	newpage->emsPage = origpage->emsPage;
    760 	newpage->mainPage = origpage->mainPage;
    761 
    762 	// Mark replaced page as purged
    763 	origpage->mainPage = origpage->emsPage = -1;
    764 
    765 	if (!addr)
    766 		Quit("PML_TransferPageSpace: Zero replacement");
    767 
    768 	return(addr);
    769 }
    770 
    771 //
    772 //	PML_GetAPageBuffer() - A page buffer is needed. Either get it from the
    773 //		main/EMS free pool, or use PML_GiveLRUPage() to find which page to
    774 //		steal the buffer from. Returns a far pointer to the page buffer, and
    775 //		sets the fields inside the given page structure appropriately.
    776 //		If mainonly is true, free EMS will be ignored, and only main pages
    777 //		will be looked at by PML_GiveLRUPage().
    778 //
    779 byte far *
    780 PML_GetAPageBuffer(int pagenum,boolean mainonly)
    781 {
    782 	byte			far *addr = nil;
    783 	int				i,n;
    784 	PMBlockAttr		*used;
    785 	PageListStruct	far *page;
    786 
    787 	page = &PMPages[pagenum];
    788 	if ((EMSPagesUsed < EMSPagesAvail) && !mainonly)
    789 	{
    790 		// There's remaining EMS - use it
    791 		page->emsPage = EMSPagesUsed++;
    792 		addr = PML_GetEMSAddress(page->emsPage,page->locked);
    793 	}
    794 	else if (MainPagesUsed < MainPagesAvail)
    795 	{
    796 		// There's remaining main memory - use it
    797 		for (i = 0,n = -1,used = MainMemUsed;i < PMMaxMainMem;i++,used++)
    798 		{
    799 			if ((*used & pmba_Allocated) && !(*used & pmba_Used))
    800 			{
    801 				n = i;
    802 				*used |= pmba_Used;
    803 				break;
    804 			}
    805 		}
    806 		if (n == -1)
    807 			Quit("PML_GetPageBuffer: MainPagesAvail lied");
    808 		addr = MainMemPages[n];
    809 		if (!addr)
    810 			Quit("PML_GetPageBuffer: Purged main block");
    811 		page->mainPage = n;
    812 		MainPagesUsed++;
    813 	}
    814 	else
    815 		addr = PML_TransferPageSpace(PML_GiveLRUPage(mainonly),pagenum);
    816 
    817 	if (!addr)
    818 		Quit("PML_GetPageBuffer: Search failed");
    819 	return(addr);
    820 }
    821 
    822 //
    823 //	PML_GetPageFromXMS() - If page is in XMS, find LRU main/EMS page and
    824 //		replace it with the page from XMS. If mainonly is true, will only
    825 //		search for LRU main page.
    826 //	XMSProtectPage is set to the page to be retrieved from XMS, so that if
    827 //		the page from which we're stealing the main/EMS from isn't in XMS,
    828 //		it won't copy over the page that we're trying to get from XMS.
    829 //		(pages that are being purged are copied into XMS, if possible)
    830 //
    831 memptr
    832 PML_GetPageFromXMS(int pagenum,boolean mainonly)
    833 {
    834 	byte			far *checkaddr;
    835 	memptr			addr = nil;
    836 	PageListStruct	far *page;
    837 
    838 	page = &PMPages[pagenum];
    839 	if (XMSPresent && (page->xmsPage != -1))
    840 	{
    841 		XMSProtectPage = pagenum;
    842 		checkaddr = PML_GetAPageBuffer(pagenum,mainonly);
    843 		if (FP_OFF(checkaddr))
    844 			Quit("PML_GetPageFromXMS: Non segment pointer");
    845 		addr = (memptr)FP_SEG(checkaddr);
    846 		PML_CopyFromXMS(addr,page->xmsPage,page->length);
    847 		XMSProtectPage = -1;
    848 	}
    849 
    850 	return(addr);
    851 }
    852 
    853 //
    854 //	PML_LoadPage() - A page is not in main/EMS memory, and it's not in XMS.
    855 //		Load it into either main or EMS. If mainonly is true, the page will
    856 //		only be loaded into main.
    857 //
    858 void
    859 PML_LoadPage(int pagenum,boolean mainonly)
    860 {
    861 	byte			far *addr;
    862 	PageListStruct	far *page;
    863 
    864 	addr = PML_GetAPageBuffer(pagenum,mainonly);
    865 	page = &PMPages[pagenum];
    866 	PML_ReadFromFile(addr,page->offset,page->length);
    867 }
    868 
    869 //
    870 //	PM_GetPage() - Returns the address of the page, loading it if necessary
    871 //		First, check if in Main Memory or EMS
    872 //		Then, check XMS
    873 //		If not in XMS, load into Main Memory or EMS
    874 //
    875 #pragma warn -pia
    876 memptr
    877 PM_GetPage(int pagenum)
    878 {
    879 	memptr	result;
    880 
    881 	if (pagenum >= ChunksInFile)
    882 		Quit("PM_GetPage: Invalid page request");
    883 
    884 #if 0	// for debugging
    885 asm	mov	dx,STATUS_REGISTER_1
    886 asm	in	al,dx
    887 asm	mov	dx,ATR_INDEX
    888 asm	mov	al,ATR_OVERSCAN
    889 asm	out	dx,al
    890 asm	mov	al,10	// bright green
    891 asm	out	dx,al
    892 #endif
    893 
    894 	if (!(result = PM_GetPageAddress(pagenum)))
    895 	{
    896 		boolean mainonly = (pagenum >= PMSoundStart);
    897 if (!PMPages[pagenum].offset)	// JDC: sparse page
    898 	Quit ("Tried to load a sparse page!");
    899 		if (!(result = PML_GetPageFromXMS(pagenum,mainonly)))
    900 		{
    901 			if (PMPages[pagenum].lastHit == PMFrameCount)
    902 				PMThrashing++;
    903 
    904 			PML_LoadPage(pagenum,mainonly);
    905 			result = PM_GetPageAddress(pagenum);
    906 		}
    907 	}
    908 	PMPages[pagenum].lastHit = PMFrameCount;
    909 
    910 #if 0	// for debugging
    911 asm	mov	dx,STATUS_REGISTER_1
    912 asm	in	al,dx
    913 asm	mov	dx,ATR_INDEX
    914 asm	mov	al,ATR_OVERSCAN
    915 asm	out	dx,al
    916 asm	mov	al,3	// blue
    917 asm	out	dx,al
    918 asm	mov	al,0x20	// normal
    919 asm	out	dx,al
    920 #endif
    921 
    922 	return(result);
    923 }
    924 #pragma warn +pia
    925 
    926 //
    927 //	PM_SetPageLock() - Sets the lock type on a given page
    928 //		pml_Unlocked: Normal, page can be purged
    929 //		pml_Locked: Cannot be purged
    930 //		pml_EMS?: Same as pml_Locked, but if in EMS, use the physical page
    931 //					specified when returning the address. For sound stuff.
    932 //
    933 void
    934 PM_SetPageLock(int pagenum,PMLockType lock)
    935 {
    936 	if (pagenum < PMSoundStart)
    937 		Quit("PM_SetPageLock: Locking/unlocking non-sound page");
    938 
    939 	PMPages[pagenum].locked = lock;
    940 }
    941 
    942 //
    943 //	PM_Preload() - Loads as many pages as possible into all types of memory.
    944 //		Calls the update function after each load, indicating the current
    945 //		page, and the total pages that need to be loaded (for thermometer).
    946 //
    947 void
    948 PM_Preload(boolean (*update)(word current,word total))
    949 {
    950 	int				i,j,
    951 					page,oogypage;
    952 	word			current,total,
    953 					totalnonxms,totalxms,
    954 					mainfree,maintotal,
    955 					emsfree,emstotal,
    956 					xmsfree,xmstotal;
    957 	memptr			addr;
    958 	PageListStruct	far *p;
    959 
    960 	mainfree = (MainPagesAvail - MainPagesUsed) + (EMSPagesAvail - EMSPagesUsed);
    961 	xmsfree = (XMSPagesAvail - XMSPagesUsed);
    962 
    963 	xmstotal = maintotal = 0;
    964 
    965 	for (i = 0;i < ChunksInFile;i++)
    966 	{
    967 		if (!PMPages[i].offset)
    968 			continue;			// sparse
    969 
    970 		if ( PMPages[i].emsPage != -1 || PMPages[i].mainPage != -1 )
    971 			continue;			// already in main mem
    972 
    973 		if ( mainfree )
    974 		{
    975 			maintotal++;
    976 			mainfree--;
    977 		}
    978 		else if ( xmsfree && (PMPages[i].xmsPage == -1) )
    979 		{
    980 			xmstotal++;
    981 			xmsfree--;
    982 		}
    983 	}
    984 
    985 
    986 	total = maintotal + xmstotal;
    987 
    988 	if (!total)
    989 		return;
    990 
    991 	page = 0;
    992 	current = 0;
    993 
    994 //
    995 // cache main/ems blocks
    996 //
    997 	while (maintotal)
    998 	{
    999 		while ( !PMPages[page].offset || PMPages[page].mainPage != -1
   1000 			||	PMPages[page].emsPage != -1 )
   1001 			page++;
   1002 
   1003 		if (page >= ChunksInFile)
   1004 			Quit ("PM_Preload: Pages>=ChunksInFile");
   1005 
   1006 		PM_GetPage(page);
   1007 
   1008 		page++;
   1009 		current++;
   1010 		maintotal--;
   1011 		update(current,total);
   1012 	}
   1013 
   1014 //
   1015 // load stuff to XMS
   1016 //
   1017 	if (xmstotal)
   1018 	{
   1019 		for (oogypage = 0 ; PMPages[oogypage].mainPage == -1 ; oogypage++)
   1020 		;
   1021 		addr = PM_GetPage(oogypage);
   1022 		if (!addr)
   1023 			Quit("PM_Preload: XMS buffer failed");
   1024 
   1025 		while (xmstotal)
   1026 		{
   1027 			while ( !PMPages[page].offset || PMPages[page].xmsPage != -1 )
   1028 				page++;
   1029 
   1030 			if (page >= ChunksInFile)
   1031 				Quit ("PM_Preload: Pages>=ChunksInFile");
   1032 
   1033 			p = &PMPages[page];
   1034 
   1035 			p->xmsPage = XMSPagesUsed++;
   1036 			if (XMSPagesUsed > XMSPagesAvail)
   1037 				Quit("PM_Preload: Exceeded XMS pages");
   1038 			if (p->length > PMPageSize)
   1039 				Quit("PM_Preload: Page too long");
   1040 
   1041 			PML_ReadFromFile((byte far *)addr,p->offset,p->length);
   1042 			PML_CopyToXMS((byte far *)addr,p->xmsPage,p->length);
   1043 
   1044 			page++;
   1045 			current++;
   1046 			xmstotal--;
   1047 			update(current,total);
   1048 		}
   1049 
   1050 		p = &PMPages[oogypage];
   1051 		PML_ReadFromFile((byte far *)addr,p->offset,p->length);
   1052 	}
   1053 
   1054 	update(total,total);
   1055 }
   1056 
   1057 /////////////////////////////////////////////////////////////////////////////
   1058 //
   1059 //	General code
   1060 //
   1061 /////////////////////////////////////////////////////////////////////////////
   1062 
   1063 //
   1064 //	PM_NextFrame() - Increments the frame counter and adjusts the thrash
   1065 //		avoidence variables
   1066 //
   1067 //		If currently in panic mode (to avoid thrashing), check to see if the
   1068 //			appropriate number of frames have passed since the last time that
   1069 //			we would have thrashed. If so, take us out of panic mode.
   1070 //
   1071 //
   1072 void
   1073 PM_NextFrame(void)
   1074 {
   1075 	int	i;
   1076 
   1077 	// Frame count overrun - kill the LRU hit entries & reset frame count
   1078 	if (++PMFrameCount >= MAXLONG - 4)
   1079 	{
   1080 		for (i = 0;i < PMNumBlocks;i++)
   1081 			PMPages[i].lastHit = 0;
   1082 		PMFrameCount = 0;
   1083 	}
   1084 
   1085 #if 0
   1086 	for (i = 0;i < PMSoundStart;i++)
   1087 	{
   1088 		if (PMPages[i].locked)
   1089 		{
   1090 			char buf[40];
   1091 			sprintf(buf,"PM_NextFrame: Page %d is locked",i);
   1092 			Quit(buf);
   1093 		}
   1094 	}
   1095 #endif
   1096 
   1097 	if (PMPanicMode)
   1098 	{
   1099 		// DEBUG - set border color
   1100 		if ((!PMThrashing) && (!--PMPanicMode))
   1101 		{
   1102 			// DEBUG - reset border color
   1103 		}
   1104 	}
   1105 	if (PMThrashing >= PMThrashThreshold)
   1106 		PMPanicMode = PMUnThrashThreshold;
   1107 	PMThrashing = false;
   1108 }
   1109 
   1110 //
   1111 //	PM_Reset() - Sets up caching structures
   1112 //
   1113 void
   1114 PM_Reset(void)
   1115 {
   1116 	int				i;
   1117 	PageListStruct	far *page;
   1118 
   1119 	XMSPagesAvail = XMSAvail / PMPageSizeKB;
   1120 
   1121 	EMSPagesAvail = EMSAvail * (EMSPageSizeKB / PMPageSizeKB);
   1122 	EMSPhysicalPage = 0;
   1123 
   1124 	MainPagesUsed = EMSPagesUsed = XMSPagesUsed = 0;
   1125 
   1126 	PMPanicMode = false;
   1127 
   1128 	// Initialize page list
   1129 	for (i = 0,page = PMPages;i < PMNumBlocks;i++,page++)
   1130 	{
   1131 		page->mainPage = -1;
   1132 		page->emsPage = -1;
   1133 		page->xmsPage = -1;
   1134 		page->locked = false;
   1135 	}
   1136 }
   1137 
   1138 //
   1139 //	PM_Startup() - Start up the Page Mgr
   1140 //
   1141 void
   1142 PM_Startup(void)
   1143 {
   1144 	boolean	nomain,noems,noxms;
   1145 	int		i;
   1146 
   1147 	if (PMStarted)
   1148 		return;
   1149 
   1150 	nomain = noems = noxms = false;
   1151 	for (i = 1;i < _argc;i++)
   1152 	{
   1153 		switch (US_CheckParm(_argv[i],ParmStrings))
   1154 		{
   1155 		case 0:
   1156 			nomain = true;
   1157 			break;
   1158 		case 1:
   1159 			noems = true;
   1160 			break;
   1161 		case 2:
   1162 			noxms = true;
   1163 			break;
   1164 		}
   1165 	}
   1166 
   1167 	PML_OpenPageFile();
   1168 
   1169 	if (!noems)
   1170 		PML_StartupEMS();
   1171 	if (!noxms)
   1172 		PML_StartupXMS();
   1173 
   1174 	if (nomain && !EMSPresent)
   1175 		Quit("PM_Startup: No main or EMS");
   1176 	else
   1177 		PML_StartupMainMem();
   1178 
   1179 	PM_Reset();
   1180 
   1181 	PMStarted = true;
   1182 }
   1183 
   1184 //
   1185 //	PM_Shutdown() - Shut down the Page Mgr
   1186 //
   1187 void
   1188 PM_Shutdown(void)
   1189 {
   1190 	PML_ShutdownXMS();
   1191 	PML_ShutdownEMS();
   1192 
   1193 	if (!PMStarted)
   1194 		return;
   1195 
   1196 	PML_ClosePageFile();
   1197 
   1198 	PML_ShutdownMainMem();
   1199 }