📄 id_pm.c
字号:
//
// ID_PM.C
// Id Engine's Page Manager v1.0
// Primary coder: Jason Blochowiak
//
#include "ID_HEADS.H"
#pragma hdrstop
// Main Mem specific variables
boolean MainPresent;
memptr MainMemPages[PMMaxMainMem];
PMBlockAttr MainMemUsed[PMMaxMainMem];
int MainPagesAvail;
// EMS specific variables
boolean EMSPresent;
word EMSAvail,EMSPagesAvail,EMSHandle,
EMSPageFrame,EMSPhysicalPage;
EMSListStruct EMSList[EMSFrameCount];
// XMS specific variables
boolean XMSPresent;
word XMSAvail,XMSPagesAvail,XMSHandle;
longword XMSDriver;
int XMSProtectPage = -1;
// File specific variables
char PageFileName[13] = {"VSWAP."};
int PageFile = -1;
word ChunksInFile;
word PMSpriteStart,PMSoundStart;
// General usage variables
boolean PMStarted,
PMPanicMode,
PMThrashing;
word XMSPagesUsed,
EMSPagesUsed,
MainPagesUsed,
PMNumBlocks;
long PMFrameCount;
PageListStruct far *PMPages,
_seg *PMSegPages;
static char *ParmStrings[] = {"nomain","noems","noxms",nil};
/////////////////////////////////////////////////////////////////////////////
//
// EMS Management code
//
/////////////////////////////////////////////////////////////////////////////
//
// PML_MapEMS() - Maps a logical page to a physical page
//
void
PML_MapEMS(word logical,word physical)
{
_AL = physical;
_BX = logical;
_DX = EMSHandle;
_AH = EMS_MAPPAGE;
asm int EMS_INT
if (_AH)
Quit("PML_MapEMS: Page mapping failed");
}
//
// PML_StartupEMS() - Sets up EMS for Page Mgr's use
// Checks to see if EMS driver is present
// Verifies that EMS hardware is present
// Make sure that EMS version is 3.2 or later
// If there's more than our minimum (2 pages) available, allocate it (up
// to the maximum we need)
//
char EMMDriverName[9] = "EMMXXXX0";
boolean
PML_StartupEMS(void)
{
int i;
long size;
EMSPresent = false; // Assume that we'll fail
EMSAvail = 0;
_DX = (word)EMMDriverName;
_AX = 0x3d00;
geninterrupt(0x21); // try to open EMMXXXX0 device
asm jnc gothandle
goto error;
gothandle:
_BX = _AX;
_AX = 0x4400;
geninterrupt(0x21); // get device info
asm jnc gotinfo;
goto error;
gotinfo:
asm and dx,0x80
if (!_DX)
goto error;
_AX = 0x4407;
geninterrupt(0x21); // get status
asm jc error
if (!_AL)
goto error;
_AH = 0x3e;
geninterrupt(0x21); // close handle
_AH = EMS_STATUS;
geninterrupt(EMS_INT);
if (_AH)
goto error; // make sure EMS hardware is present
_AH = EMS_VERSION;
geninterrupt(EMS_INT);
if (_AH || (_AL < 0x32)) // only work on EMS 3.2 or greater (silly, but...)
goto error;
_AH = EMS_GETFRAME;
geninterrupt(EMS_INT);
if (_AH)
goto error; // find the page frame address
EMSPageFrame = _BX;
_AH = EMS_GETPAGES;
geninterrupt(EMS_INT);
if (_AH)
goto error;
if (_BX < 2)
goto error; // Require at least 2 pages (32k)
EMSAvail = _BX;
// Don't hog all available EMS
size = EMSAvail * (long)EMSPageSize;
if (size - (EMSPageSize * 2) > (ChunksInFile * (long)PMPageSize))
{
size = (ChunksInFile * (long)PMPageSize) + EMSPageSize;
EMSAvail = size / EMSPageSize;
}
_AH = EMS_ALLOCPAGES;
_BX = EMSAvail;
geninterrupt(EMS_INT);
if (_AH)
goto error;
EMSHandle = _DX;
mminfo.EMSmem += EMSAvail * (long)EMSPageSize;
// Initialize EMS mapping cache
for (i = 0;i < EMSFrameCount;i++)
EMSList[i].baseEMSPage = -1;
EMSPresent = true; // We have EMS
error:
return(EMSPresent);
}
//
// PML_ShutdownEMS() - If EMS was used, deallocate it
//
void
PML_ShutdownEMS(void)
{
if (EMSPresent)
{
asm mov ah,EMS_FREEPAGES
asm mov dx,[EMSHandle]
asm int EMS_INT
if (_AH)
Quit ("PML_ShutdownEMS: Error freeing EMS");
}
}
/////////////////////////////////////////////////////////////////////////////
//
// XMS Management code
//
/////////////////////////////////////////////////////////////////////////////
//
// PML_StartupXMS() - Starts up XMS for the Page Mgr's use
// Checks for presence of an XMS driver
// Makes sure that there's at least a page of XMS available
// Allocates any remaining XMS (rounded down to the nearest page size)
//
boolean
PML_StartupXMS(void)
{
XMSPresent = false; // Assume failure
XMSAvail = 0;
asm mov ax,0x4300
asm int XMS_INT // Check for presence of XMS driver
if (_AL != 0x80)
goto error;
asm mov ax,0x4310
asm int XMS_INT // Get address of XMS driver
asm mov [WORD PTR XMSDriver],bx
asm mov [WORD PTR XMSDriver+2],es // function pointer to XMS driver
XMS_CALL(XMS_QUERYFREE); // Find out how much XMS is available
XMSAvail = _AX;
if (!_AX) // AJR: bugfix 10/8/92
goto error;
XMSAvail &= ~(PMPageSizeKB - 1); // Round off to nearest page size
if (XMSAvail < (PMPageSizeKB * 2)) // Need at least 2 pages
goto error;
_DX = XMSAvail;
XMS_CALL(XMS_ALLOC); // And do the allocation
XMSHandle = _DX;
if (!_AX) // AJR: bugfix 10/8/92
{
XMSAvail = 0;
goto error;
}
mminfo.XMSmem += XMSAvail * 1024;
XMSPresent = true;
error:
return(XMSPresent);
}
//
// PML_XMSCopy() - Copies a main/EMS page to or from XMS
// Will round an odd-length request up to the next even value
//
void
PML_XMSCopy(boolean toxms,byte far *addr,word xmspage,word length)
{
longword xoffset;
struct
{
longword length;
word source_handle;
longword source_offset;
word target_handle;
longword target_offset;
} copy;
if (!addr)
Quit("PML_XMSCopy: zero address");
xoffset = (longword)xmspage * PMPageSize;
copy.length = (length + 1) & ~1;
copy.source_handle = toxms? 0 : XMSHandle;
copy.source_offset = toxms? (long)addr : xoffset;
copy.target_handle = toxms? XMSHandle : 0;
copy.target_offset = toxms? xoffset : (long)addr;
asm push si
_SI = (word)©
XMS_CALL(XMS_MOVE);
asm pop si
if (!_AX)
Quit("PML_XMSCopy: Error on copy");
}
#if 1
#define PML_CopyToXMS(s,t,l) PML_XMSCopy(true,(s),(t),(l))
#define PML_CopyFromXMS(t,s,l) PML_XMSCopy(false,(t),(s),(l))
#else
//
// PML_CopyToXMS() - Copies the specified number of bytes from the real mode
// segment address to the specified XMS page
//
void
PML_CopyToXMS(byte far *source,int targetpage,word length)
{
PML_XMSCopy(true,source,targetpage,length);
}
//
// PML_CopyFromXMS() - Copies the specified number of bytes from an XMS
// page to the specified real mode address
//
void
PML_CopyFromXMS(byte far *target,int sourcepage,word length)
{
PML_XMSCopy(false,target,sourcepage,length);
}
#endif
//
// PML_ShutdownXMS()
//
void
PML_ShutdownXMS(void)
{
if (XMSPresent)
{
_DX = XMSHandle;
XMS_CALL(XMS_FREE);
if (_BL)
Quit("PML_ShutdownXMS: Error freeing XMS");
}
}
/////////////////////////////////////////////////////////////////////////////
//
// Main memory code
//
/////////////////////////////////////////////////////////////////////////////
//
// PM_SetMainMemPurge() - Sets the purge level for all allocated main memory
// blocks. This shouldn't be called directly - the PM_LockMainMem() and
// PM_UnlockMainMem() macros should be used instead.
//
void
PM_SetMainMemPurge(int level)
{
int i;
for (i = 0;i < PMMaxMainMem;i++)
if (MainMemPages[i])
MM_SetPurge(&MainMemPages[i],level);
}
//
// PM_CheckMainMem() - If something besides the Page Mgr makes requests of
// the Memory Mgr, some of the Page Mgr's blocks may have been purged,
// so this function runs through the block list and checks to see if
// any of the blocks have been purged. If so, it marks the corresponding
// page as purged & unlocked, then goes through the block list and
// tries to reallocate any blocks that have been purged.
// This routine now calls PM_LockMainMem() to make sure that any allocation
// attempts made during the block reallocation sweep don't purge any
// of the other blocks. Because PM_LockMainMem() is called,
// PM_UnlockMainMem() needs to be called before any other part of the
// program makes allocation requests of the Memory Mgr.
//
void
PM_CheckMainMem(void)
{
boolean allocfailed;
int i,n;
memptr *p;
PMBlockAttr *used;
PageListStruct far *page;
if (!MainPresent)
return;
for (i = 0,page = PMPages;i < ChunksInFile;i++,page++)
{
n = page->mainPage;
if (n != -1) // Is the page using main memory?
{
if (!MainMemPages[n]) // Yep, was the block purged?
{
page->mainPage = -1; // Yes, mark page as purged & unlocked
page->locked = pml_Unlocked;
}
}
}
// Prevent allocation attempts from purging any of our other blocks
PM_LockMainMem();
allocfailed = false;
for (i = 0,p = MainMemPages,used = MainMemUsed;i < PMMaxMainMem;i++,p++,used++)
{
if (!*p) // If the page got purged
{
if (*used & pmba_Allocated) // If it was allocated
{
*used &= ~pmba_Allocated; // Mark as unallocated
MainPagesAvail--; // and decrease available count
}
if (*used & pmba_Used) // If it was used
{
*used &= ~pmba_Used; // Mark as unused
MainPagesUsed--; // and decrease used count
}
if (!allocfailed)
{
MM_BombOnError(false);
MM_GetPtr(p,PMPageSize); // Try to reallocate
if (mmerror) // If it failed,
allocfailed = true; // don't try any more allocations
else // If it worked,
{
*used |= pmba_Allocated; // Mark as allocated
MainPagesAvail++; // and increase available count
}
MM_BombOnError(true);
}
}
}
if (mmerror)
mmerror = false;
}
//
// PML_StartupMainMem() - Allocates as much main memory as is possible for
// the Page Mgr. The memory is allocated as non-purgeable, so if it's
// necessary to make requests of the Memory Mgr, PM_UnlockMainMem()
// needs to be called.
//
void
PML_StartupMainMem(void)
{
int i,n;
memptr *p;
MainPagesAvail = 0;
MM_BombOnError(false);
for (i = 0,p = MainMemPages;i < PMMaxMainMem;i++,p++)
{
MM_GetPtr(p,PMPageSize);
if (mmerror)
break;
MainPagesAvail++;
MainMemUsed[i] = pmba_Allocated;
}
MM_BombOnError(true);
if (mmerror)
mmerror = false;
if (MainPagesAvail < PMMinMainMem)
Quit("PM_SetupMainMem: Not enough main memory");
MainPresent = true;
}
//
// PML_ShutdownMainMem() - Frees all of the main memory blocks used by the
// Page Mgr.
//
void
PML_ShutdownMainMem(void)
{
int i;
memptr *p;
// DEBUG - mark pages as unallocated & decrease page count as appropriate
for (i = 0,p = MainMemPages;i < PMMaxMainMem;i++,p++)
if (*p)
MM_FreePtr(p);
}
/////////////////////////////////////////////////////////////////////////////
//
// File management code
//
/////////////////////////////////////////////////////////////////////////////
//
// PML_ReadFromFile() - Reads some data in from the page file
//
void
PML_ReadFromFile(byte far *buf,long offset,word length)
{
if (!buf)
Quit("PML_ReadFromFile: Null pointer");
if (!offset)
Quit("PML_ReadFromFile: Zero offset");
if (lseek(PageFile,offset,SEEK_SET) != offset)
Quit("PML_ReadFromFile: Seek failed");
if (!CA_FarRead(PageFile,buf,length))
Quit("PML_ReadFromFile: Read failed");
}
//
// PML_OpenPageFile() - Opens the page file and sets up the page info
//
void
PML_OpenPageFile(void)
{
int i;
long size;
void _seg *buf;
longword far *offsetptr;
word far *lengthptr;
PageListStruct far *page;
PageFile = open(PageFileName,O_RDONLY + O_BINARY);
if (PageFile == -1)
Quit("PML_OpenPageFile: Unable to open page file");
// Read in header variables
read(PageFile,&ChunksInFile,sizeof(ChunksInFile));
read(PageFile,&PMSpriteStart,sizeof(PMSpriteStart));
read(PageFile,&PMSoundStart,sizeof(PMSoundStart));
// Allocate and clear the page list
PMNumBlocks = ChunksInFile;
MM_GetPtr(&(memptr)PMSegPages,sizeof(PageListStruct) * PMNumBlocks);
MM_SetLock(&(memptr)PMSegPages,true);
PMPages = (PageListStruct far *)PMSegPages;
_fmemset(PMPages,0,sizeof(PageListStruct) * PMNumBlocks);
// Read in the chunk offsets
size = sizeof(longword) * ChunksInFile;
MM_GetPtr(&buf,size);
if (!CA_FarRead(PageFile,(byte far *)buf,size))
Quit("PML_OpenPageFile: Offset read failed");
offsetptr = (longword far *)buf;
for (i = 0,page = PMPages;i < ChunksInFile;i++,page++)
page->offset = *offsetptr++;
MM_FreePtr(&buf);
// Read in the chunk lengths
size = sizeof(word) * ChunksInFile;
MM_GetPtr(&buf,size);
if (!CA_FarRead(PageFile,(byte far *)buf,size))
Quit("PML_OpenPageFile: Length read failed");
lengthptr = (word far *)buf;
for (i = 0,page = PMPages;i < ChunksInFile;i++,page++)
page->length = *lengthptr++;
MM_FreePtr(&buf);
}
//
// PML_ClosePageFile() - Closes the page file
//
void
PML_ClosePageFile(void)
{
if (PageFile != -1)
close(PageFile);
if (PMSegPages)
{
MM_SetLock(&(memptr)PMSegPages,false);
MM_FreePtr(&(void _seg *)PMSegPages);
}
}
/////////////////////////////////////////////////////////////////////////////
//
// Allocation, etc., code
//
/////////////////////////////////////////////////////////////////////////////
//
// PML_GetEMSAddress()
//
// Page is in EMS, so figure out which EMS physical page should be used
// to map our page in. If normal page, use EMS physical page 3, else
// use the physical page specified by the lock type
//
#if 1
#pragma argsused // DEBUG - remove lock parameter
memptr
PML_GetEMSAddress(int page,PMLockType lock)
{
int i,emspage;
word emsoff,emsbase,offset;
emsoff = page & (PMEMSSubPage - 1);
emsbase = page - emsoff;
emspage = -1;
// See if this page is already mapped in
for (i = 0;i < EMSFrameCount;i++)
{
if (EMSList[i].baseEMSPage == emsbase)
{
emspage = i; // Yep - don't do a redundant remapping
break;
}
}
// If page isn't already mapped in, find LRU EMS frame, and use it
if (emspage == -1)
{
longword last = MAXLONG;
for (i = 0;i < EMSFrameCount;i++)
{
if (EMSList[i].lastHit < last)
{
emspage = i;
last = EMSList[i].lastHit;
}
}
EMSList[emspage].baseEMSPage = emsbase;
PML_MapEMS(page / PMEMSSubPage,emspage);
}
if (emspage == -1)
Quit("PML_GetEMSAddress: EMS find failed");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -