Commit f8516336 authored by Steven Fuller's avatar Steven Fuller

More cleanups, need to finish id_mm.c

parent 4f632158
......@@ -1662,4 +1662,1937 @@ void CA_CannotOpen(char *string)
strcat(str,string);
strcat(str,"!\n");
Quit (str);
}
\ No newline at end of file
}
/* TODO: totally remove */
/*
=============================================================================
LOCAL INFO
=============================================================================
*/
#define LOCKBIT 0x80 // if set in attributes, block cannot be moved
#define PURGEBITS 3 // 0-3 level, 0= unpurgable, 3= purge first
#define PURGEMASK 0xfffc
#define BASEATTRIBUTES 0 // unlocked, non purgable
#define MAXUMBS 10
typedef struct mmblockstruct
{
unsigned start,length;
unsigned attributes;
memptr *useptr; // pointer to the segment start
struct mmblockstruct *next;
} mmblocktype;
#define GETNEWBLOCK {if(!mmfree)MML_ClearBlock();mmnew=mmfree;mmfree=mmfree->next;}
#define FREEBLOCK(x) {*x->useptr=NULL;x->next=mmfree;mmfree=x;}
/*
=============================================================================
GLOBAL VARIABLES
=============================================================================
*/
mminfotype mminfo;
memptr bufferseg;
boolean mmerror;
void (* beforesort) (void);
void (* aftersort) (void);
/*
=============================================================================
LOCAL VARIABLES
=============================================================================
*/
boolean mmstarted;
void *farheap;
void *nearheap;
mmblocktype mmblocks[MAXBLOCKS], *mmhead, *mmfree, *mmrover, *mmnew;
boolean bombonerror;
//unsigned totalEMSpages,freeEMSpages,EMSpageframe,EMSpagesmapped,EMShandle;
void (* XMSaddr) (void); // far pointer to XMS driver
unsigned numUMBs,UMBbase[MAXUMBS];
//==========================================================================
//
// local prototypes
//
/*
====================
=
= MML_ClearBlock
=
= We are out of blocks, so free a purgable block
=
====================
*/
void MML_ClearBlock (void)
{
mmblocktype *scan, *last;
scan = mmhead->next;
while (scan)
{
if (!(scan->attributes&LOCKBIT) && (scan->attributes&PURGEBITS) )
{
MM_FreePtr(scan->useptr);
return;
}
scan = scan->next;
}
Quit ("MM_ClearBlock: No purgable blocks!");
}
//==========================================================================
/*
===================
=
= MM_Startup
=
= Grabs all space from turbo with malloc/farmalloc
= Allocates bufferseg misc buffer
=
===================
*/
static char *ParmStrings[] = {"noems","noxms",""};
void MM_Startup (void)
{
int i;
unsigned long length;
void *start;
unsigned segstart,seglength,endfree;
if (mmstarted)
MM_Shutdown ();
mmstarted = true;
bombonerror = true;
//
// set up the linked list (everything in the free list;
//
mmhead = NULL;
mmfree = &mmblocks[0];
for (i=0;i<MAXBLOCKS-1;i++)
mmblocks[i].next = &mmblocks[i+1];
mmblocks[i].next = NULL;
//
// locked block of all memory until we punch out free space
//
GETNEWBLOCK;
mmhead = mmnew; // this will allways be the first node
mmnew->start = 0;
mmnew->length = 0xffff;
mmnew->attributes = LOCKBIT;
mmnew->next = NULL;
mmrover = mmhead;
//
// get all available near conventional memory segments
//
length=coreleft();
start = (void *)(nearheap = malloc(length));
length -= 16-(FP_OFF(start)&15);
length -= SAVENEARHEAP;
seglength = length / 16; // now in paragraphs
segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;
MML_UseSpace (segstart,seglength);
mminfo.nearheap = length;
//
// get all available far conventional memory segments
//
length=farcoreleft();
start = farheap = farmalloc(length);
length -= 16-(FP_OFF(start)&15);
length -= SAVEFARHEAP;
seglength = length / 16; // now in paragraphs
segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;
MML_UseSpace (segstart,seglength);
mminfo.farheap = length;
mminfo.mainmem = mminfo.nearheap + mminfo.farheap;
//
// allocate the misc buffer
//
mmrover = mmhead; // start looking for space after low block
MM_GetPtr (&bufferseg,BUFFERSIZE);
}
//==========================================================================
/*
====================
=
= MM_Shutdown
=
= Frees all conventional, EMS, and XMS allocated
=
====================
*/
void MM_Shutdown (void)
{
if (!mmstarted)
return;
farfree (farheap);
free (nearheap);
// MML_ShutdownXMS ();
}
//==========================================================================
/*
====================
=
= MM_GetPtr
=
= Allocates an unlocked, unpurgable block
=
====================
*/
void MM_GetPtr (memptr *baseptr,unsigned long size)
{
mmblocktype *scan, *lastscan, *endscan, *purge, *next;
int search;
unsigned needed,startseg;
needed = (size+15)/16; // convert size from bytes to paragraphs
GETNEWBLOCK; // fill in start and next after a spot is found
mmnew->length = needed;
mmnew->useptr = baseptr;
mmnew->attributes = BASEATTRIBUTES;
tryagain:
for (search = 0; search<3; search++)
{
//
// first search: try to allocate right after the rover, then on up
// second search: search from the head pointer up to the rover
// third search: compress memory, then scan from start
if (search == 1 && mmrover == mmhead)
search++;
switch (search)
{
case 0:
lastscan = mmrover;
scan = mmrover->next;
endscan = NULL;
break;
case 1:
lastscan = mmhead;
scan = mmhead->next;
endscan = mmrover;
break;
case 2:
MM_SortMem ();
lastscan = mmhead;
scan = mmhead->next;
endscan = NULL;
break;
}
startseg = lastscan->start + lastscan->length;
while (scan != endscan)
{
if (scan->start - startseg >= needed)
{
//
// got enough space between the end of lastscan and
// the start of scan, so throw out anything in the middle
// and allocate the new block
//
purge = lastscan->next;
lastscan->next = mmnew;
mmnew->start = *(unsigned *)baseptr = startseg;
mmnew->next = scan;
while ( purge != scan)
{ // free the purgable block
next = purge->next;
FREEBLOCK(purge);
purge = next; // purge another if not at scan
}
mmrover = mmnew;
return; // good allocation!
}
//
// if this block is purge level zero or locked, skip past it
//
if ( (scan->attributes & LOCKBIT)
|| !(scan->attributes & PURGEBITS) )
{
lastscan = scan;
startseg = lastscan->start + lastscan->length;
}
scan=scan->next; // look at next line
}
}
if (bombonerror)
{
extern char configname[];
extern boolean insetupscaling;
extern int viewsize;
boolean SetViewSize (unsigned width, unsigned height);
#define HEIGHTRATIO 0.50
//
// wolf hack -- size the view down
//
if (!insetupscaling && viewsize>10)
{
mmblocktype *savedmmnew;
savedmmnew = mmnew;
viewsize -= 2;
SetViewSize (viewsize*16,viewsize*16*HEIGHTRATIO);
mmnew = savedmmnew;
goto tryagain;
}
// unlink(configname);
Quit ("MM_GetPtr: Out of memory!");
}
else
mmerror = true;
}
//==========================================================================
/*
====================
=
= MM_FreePtr
=
= Deallocates an unlocked, purgable block
=
====================
*/
void MM_FreePtr (memptr *baseptr)
{
mmblocktype *scan, *last;
last = mmhead;
scan = last->next;
if (baseptr == mmrover->useptr) // removed the last allocated block
mmrover = mmhead;
while (scan->useptr != baseptr && scan)
{
last = scan;
scan = scan->next;
}
if (!scan)
Quit ("MM_FreePtr: Block not found!");
last->next = scan->next;
FREEBLOCK(scan);
}
//==========================================================================
/*
=====================
=
= MM_SetPurge
=
= Sets the purge level for a block (locked blocks cannot be made purgable)
=
=====================
*/
void MM_SetPurge (memptr *baseptr, int purge)
{
mmblocktype *start;
start = mmrover;
do
{
if (mmrover->useptr == baseptr)
break;
mmrover = mmrover->next;
if (!mmrover)
mmrover = mmhead;
else if (mmrover == start)
Quit ("MM_SetPurge: Block not found!");
} while (1);
mmrover->attributes &= ~PURGEBITS;
mmrover->attributes |= purge;
}
//==========================================================================
/*
=====================
=
= MM_SetLock
=
= Locks / unlocks the block
=
=====================
*/
void MM_SetLock (memptr *baseptr, boolean locked)
{
mmblocktype *start;
start = mmrover;
do
{
if (mmrover->useptr == baseptr)
break;
mmrover = mmrover->next;
if (!mmrover)
mmrover = mmhead;
else if (mmrover == start)
Quit ("MM_SetLock: Block not found!");
} while (1);
mmrover->attributes &= ~LOCKBIT;
mmrover->attributes |= locked*LOCKBIT;
}
//==========================================================================
/*
=====================
=
= MM_SortMem
=
= Throws out all purgable stuff and compresses movable blocks
=
=====================
*/
void MM_SortMem (void)
{
mmblocktype *scan, *last, *next;
unsigned start,length,source,dest;
int playing;
//
// lock down a currently playing sound
//
playing = SD_SoundPlaying ();
if (playing)
{
switch (SoundMode)
{
case sdm_PC:
playing += STARTPCSOUNDS;
break;
case sdm_AdLib:
playing += STARTADLIBSOUNDS;
break;
}
MM_SetLock(&(memptr)audiosegs[playing],true);
}
SD_StopSound();
if (beforesort)
beforesort();
scan = mmhead;
last = NULL; // shut up compiler warning
while (scan)
{
if (scan->attributes & LOCKBIT)
{
//
// block is locked, so try to pile later blocks right after it
//
start = scan->start + scan->length;
}
else
{
if (scan->attributes & PURGEBITS)
{
//
// throw out the purgable block
//
next = scan->next;
FREEBLOCK(scan);
last->next = next;
scan = next;
continue;
}
else
{
//
// push the non purgable block on top of the last moved block
//
if (scan->start != start)
{
length = scan->length;
source = scan->start;
dest = start;
while (length > 0xf00)
{
movedata(source,0,dest,0,0xf00*16);
length -= 0xf00;
source += 0xf00;
dest += 0xf00;
}
movedata(source,0,dest,0,length*16);
scan->start = start;
*(unsigned *)scan->useptr = start;
}
start = scan->start + scan->length;
}
}
last = scan;
scan = scan->next; // go to next block
}
mmrover = mmhead;
if (aftersort)
aftersort();
if (playing)
MM_SetLock(&(memptr)audiosegs[playing],false);
}
//==========================================================================
/*
=====================
=
= MM_ShowMemory
=
=====================
*/
void MM_ShowMemory (void)
{
mmblocktype *scan;
unsigned color,temp,x,y;
long end,owner;
char scratch[80],str[10];
temp = bufferofs;
bufferofs = displayofs;
scan = mmhead;
end = -1;
while (scan)
{
if (scan->attributes & PURGEBITS)
color = 5; // dark purple = purgable
else
color = 9; // medium blue = non purgable
if (scan->attributes & LOCKBIT)
color = 12; // red = locked
if (scan->start<=end)
Quit ("MM_ShowMemory: Memory block order currupted!");
end = scan->length-1;
y = scan->start/320;
x = scan->start%320;
VW_Hlin(x,x+end,y,color);
VW_Plot(x,y,15);
if (scan->next && scan->next->start > end+1)
VW_Hlin(x+end+1,x+(scan->next->start-scan->start),y,0); // black = free
scan = scan->next;
}
VW_FadeIn ();
IN_Ack();
bufferofs = temp;
}
//==========================================================================
/*
=====================
=
= MM_DumpData
=
=====================
*/
void MM_DumpData (void)
{
mmblocktype *scan, *best;
long lowest,oldlowest;
unsigned owner;
char lock,purge;
FILE *dumpfile;
free (nearheap);
dumpfile = fopen ("MMDUMP.TXT","w");
if (!dumpfile)
Quit ("MM_DumpData: Couldn't open MMDUMP.TXT!");
lowest = -1;
do
{
oldlowest = lowest;
lowest = 0xffff;
scan = mmhead;
while (scan)
{
owner = (unsigned)scan->useptr;
if (owner && owner<lowest && owner > oldlowest)
{
best = scan;
lowest = owner;
}
scan = scan->next;
}
if (lowest != 0xffff)
{
if (best->attributes & PURGEBITS)
purge = 'P';
else
purge = '-';
if (best->attributes & LOCKBIT)
lock = 'L';
else
lock = '-';
fprintf (dumpfile,"0x%p (%c%c) = %u\n"
,(unsigned)lowest,lock,purge,best->length);
}
} while (lowest != 0xffff);
fclose (dumpfile);
Quit ("MMDUMP.TXT created.");
}
//==========================================================================
/*
======================
=
= MM_UnusedMemory
=
= Returns the total free space without purging
=
======================
*/
long MM_UnusedMemory (void)
{
unsigned free;
mmblocktype *scan;
free = 0;
scan = mmhead;
while (scan->next)
{
free += scan->next->start - (scan->start + scan->length);
scan = scan->next;
}
return free*16l;
}
//==========================================================================
/*
======================
=
= MM_TotalFree
=
= Returns the total free space with purging
=
======================
*/
long MM_TotalFree (void)
{
unsigned free;
mmblocktype *scan;
free = 0;
scan = mmhead;
while (scan->next)
{
if ((scan->attributes&PURGEBITS) && !(scan->attributes&LOCKBIT))
free += scan->length;
free += scan->next->start - (scan->start + scan->length);
scan = scan->next;
}
return free*16l;
}
//==========================================================================
/*
=====================
=
= MM_BombOnError
=
=====================
*/
void MM_BombOnError (boolean bomb)
{
bombonerror = bomb;
}
//
// 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 *PMPages, *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 *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)&copy;
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 *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 *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 *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 *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 *buf;
longword *offsetptr;
word *lengthptr;
PageListStruct *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 *)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 *)buf,size))
Quit("PML_OpenPageFile: Offset read failed");
offsetptr = (longword *)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 *)buf,size))
Quit("PML_OpenPageFile: Length read failed");
lengthptr = (word *)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 *)PMSegPages);
}
}
/////////////////////////////////////////////////////////////////////////////
//
// Allocation, etc., code
//
/////////////////////////////////////////////////////////////////////////////
//
// PML_GetEMSAddress()
//
// Page is in EMS, so figure out which EMS physical page should be used
// to map our page in. If normal page, use EMS physical page 3, else
// use the physical page specified by the lock type
//
#if 1
#pragma argsused // DEBUG - remove lock parameter
memptr
PML_GetEMSAddress(int page,PMLockType lock)
{
int i,emspage;
word emsoff,emsbase,offset;
emsoff = page & (PMEMSSubPage - 1);
emsbase = page - emsoff;
emspage = -1;
// See if this page is already mapped in
for (i = 0;i < EMSFrameCount;i++)
{
if (EMSList[i].baseEMSPage == emsbase)
{
emspage = i; // Yep - don't do a redundant remapping
break;
}
}
// If page isn't already mapped in, find LRU EMS frame, and use it
if (emspage == -1)
{
longword last = MAXLONG;
for (i = 0;i < EMSFrameCount;i++)
{
if (EMSList[i].lastHit < last)
{
emspage = i;
last = EMSList[i].lastHit;
}
}
EMSList[emspage].baseEMSPage = emsbase;
PML_MapEMS(page / PMEMSSubPage,emspage);
}
if (emspage == -1)
Quit("PML_GetEMSAddress: EMS find failed");
EMSList[emspage].lastHit = PMFrameCount;
offset = emspage * EMSPageSizeSeg;
offset += emsoff * PMPageSizeSeg;
return((memptr)(EMSPageFrame + offset));
}
#else
memptr
PML_GetEMSAddress(int page,PMLockType lock)
{
word emspage;
emspage = (lock < pml_EMSLock)? 3 : (lock - pml_EMSLock);
PML_MapEMS(page / PMEMSSubPage,emspage);
return((memptr)(EMSPageFrame + (emspage * EMSPageSizeSeg)
+ ((page & (PMEMSSubPage - 1)) * PMPageSizeSeg)));
}
#endif
//
// PM_GetPageAddress() - Returns the address of a given page
// Maps in EMS if necessary
// Returns nil if block isn't cached into Main Memory or EMS
//
//
memptr
PM_GetPageAddress(int pagenum)
{
PageListStruct *page;
page = &PMPages[pagenum];
if (page->mainPage != -1)
return(MainMemPages[page->mainPage]);
else if (page->emsPage != -1)
return(PML_GetEMSAddress(page->emsPage,page->locked));
else
return(nil);
}
//
// PML_GiveLRUPage() - Returns the page # of the least recently used
// present & unlocked main/EMS page (or main page if mainonly is true)
//
int
PML_GiveLRUPage(boolean mainonly)
{
int i,lru;
long last;
PageListStruct *page;
for (i = 0,page = PMPages,lru = -1,last = MAXLONG;i < ChunksInFile;i++,page++)
{
if
(
(page->lastHit < last)
&& ((page->emsPage != -1) || (page->mainPage != -1))
&& (page->locked == pml_Unlocked)
&& (!(mainonly && (page->mainPage == -1)))
)
{
last = page->lastHit;
lru = i;
}
}
if (lru == -1)
Quit("PML_GiveLRUPage: LRU Search failed");
return(lru);
}
//
// PML_GiveLRUXMSPage() - Returns the page # of the least recently used
// (and present) XMS page.
// This routine won't return the XMS page protected (by XMSProtectPage)
//
int
PML_GiveLRUXMSPage(void)
{
int i,lru;
long last;
PageListStruct *page;
for (i = 0,page = PMPages,lru = -1,last = MAXLONG;i < ChunksInFile;i++,page++)
{
if
(
(page->xmsPage != -1)
&& (page->lastHit < last)
&& (i != XMSProtectPage)
)
{
last = page->lastHit;
lru = i;
}
}
return(lru);
}
//
// PML_PutPageInXMS() - If page isn't in XMS, find LRU XMS page and replace
// it with the main/EMS page
//
void
PML_PutPageInXMS(int pagenum)
{
int usexms;
PageListStruct *page;
if (!XMSPresent)
return;
page = &PMPages[pagenum];
if (page->xmsPage != -1)
return; // Already in XMS
if (XMSPagesUsed < XMSPagesAvail)
page->xmsPage = XMSPagesUsed++;
else
{
usexms = PML_GiveLRUXMSPage();
if (usexms == -1)
Quit("PML_PutPageInXMS: No XMS LRU");
page->xmsPage = PMPages[usexms].xmsPage;
PMPages[usexms].xmsPage = -1;
}
PML_CopyToXMS(PM_GetPageAddress(pagenum),page->xmsPage,page->length);
}
//
// PML_TransferPageSpace() - A page is being replaced, so give the new page
// the old one's address space. Returns the address of the new page.
//
memptr
PML_TransferPageSpace(int orig,int new)
{
memptr addr;
PageListStruct *origpage, *newpage;
if (orig == new)
Quit("PML_TransferPageSpace: Identity replacement");
origpage = &PMPages[orig];
newpage = &PMPages[new];
if (origpage->locked != pml_Unlocked)
Quit("PML_TransferPageSpace: Killing locked page");
if ((origpage->emsPage == -1) && (origpage->mainPage == -1))
Quit("PML_TransferPageSpace: Reusing non-existent page");
// Copy page that's about to be purged into XMS
PML_PutPageInXMS(orig);
// Get the address, and force EMS into a physical page if necessary
addr = PM_GetPageAddress(orig);
// Steal the address
newpage->emsPage = origpage->emsPage;
newpage->mainPage = origpage->mainPage;
// Mark replaced page as purged
origpage->mainPage = origpage->emsPage = -1;
if (!addr)
Quit("PML_TransferPageSpace: Zero replacement");
return(addr);
}
//
// PML_GetAPageBuffer() - A page buffer is needed. Either get it from the
// main/EMS free pool, or use PML_GiveLRUPage() to find which page to
// steal the buffer from. Returns a far pointer to the page buffer, and
// sets the fields inside the given page structure appropriately.
// If mainonly is true, free EMS will be ignored, and only main pages
// will be looked at by PML_GiveLRUPage().
//
byte *PML_GetAPageBuffer(int pagenum,boolean mainonly)
{
byte *addr = nil;
int i,n;
PMBlockAttr *used;
PageListStruct *page;
page = &PMPages[pagenum];
if ((EMSPagesUsed < EMSPagesAvail) && !mainonly)
{
// There's remaining EMS - use it
page->emsPage = EMSPagesUsed++;
addr = PML_GetEMSAddress(page->emsPage,page->locked);
}
else if (MainPagesUsed < MainPagesAvail)
{
// There's remaining main memory - use it
for (i = 0,n = -1,used = MainMemUsed;i < PMMaxMainMem;i++,used++)
{
if ((*used & pmba_Allocated) && !(*used & pmba_Used))
{
n = i;
*used |= pmba_Used;
break;
}
}
if (n == -1)
Quit("PML_GetPageBuffer: MainPagesAvail lied");
addr = MainMemPages[n];
if (!addr)
Quit("PML_GetPageBuffer: Purged main block");
page->mainPage = n;
MainPagesUsed++;
}
else
addr = PML_TransferPageSpace(PML_GiveLRUPage(mainonly),pagenum);
if (!addr)
Quit("PML_GetPageBuffer: Search failed");
return(addr);
}
//
// PML_GetPageFromXMS() - If page is in XMS, find LRU main/EMS page and
// replace it with the page from XMS. If mainonly is true, will only
// search for LRU main page.
// XMSProtectPage is set to the page to be retrieved from XMS, so that if
// the page from which we're stealing the main/EMS from isn't in XMS,
// it won't copy over the page that we're trying to get from XMS.
// (pages that are being purged are copied into XMS, if possible)
//
memptr
PML_GetPageFromXMS(int pagenum,boolean mainonly)
{
byte *checkaddr;
memptr addr = nil;
PageListStruct *page;
page = &PMPages[pagenum];
if (XMSPresent && (page->xmsPage != -1))
{
XMSProtectPage = pagenum;
checkaddr = PML_GetAPageBuffer(pagenum,mainonly);
if (FP_OFF(checkaddr))
Quit("PML_GetPageFromXMS: Non segment pointer");
addr = (memptr)FP_SEG(checkaddr);
PML_CopyFromXMS(addr,page->xmsPage,page->length);
XMSProtectPage = -1;
}
return(addr);
}
//
// PML_LoadPage() - A page is not in main/EMS memory, and it's not in XMS.
// Load it into either main or EMS. If mainonly is true, the page will
// only be loaded into main.
//
void
PML_LoadPage(int pagenum,boolean mainonly)
{
byte *addr;
PageListStruct *page;
addr = PML_GetAPageBuffer(pagenum,mainonly);
page = &PMPages[pagenum];
PML_ReadFromFile(addr,page->offset,page->length);
}
//
// PM_GetPage() - Returns the address of the page, loading it if necessary
// First, check if in Main Memory or EMS
// Then, check XMS
// If not in XMS, load into Main Memory or EMS
//
#pragma warn -pia
memptr
PM_GetPage(int pagenum)
{
memptr result;
if (pagenum >= ChunksInFile)
Quit("PM_GetPage: Invalid page request");
#if 0 // for debugging
asm mov dx,STATUS_REGISTER_1
asm in al,dx
asm mov dx,ATR_INDEX
asm mov al,ATR_OVERSCAN
asm out dx,al
asm mov al,10 // bright green
asm out dx,al
#endif
if (!(result = PM_GetPageAddress(pagenum)))
{
boolean mainonly = (pagenum >= PMSoundStart);
if (!PMPages[pagenum].offset) // JDC: sparse page
Quit ("Tried to load a sparse page!");
if (!(result = PML_GetPageFromXMS(pagenum,mainonly)))
{
if (PMPages[pagenum].lastHit == PMFrameCount)
PMThrashing++;
PML_LoadPage(pagenum,mainonly);
result = PM_GetPageAddress(pagenum);
}
}
PMPages[pagenum].lastHit = PMFrameCount;
#if 0 // for debugging
asm mov dx,STATUS_REGISTER_1
asm in al,dx
asm mov dx,ATR_INDEX
asm mov al,ATR_OVERSCAN
asm out dx,al
asm mov al,3 // blue
asm out dx,al
asm mov al,0x20 // normal
asm out dx,al
#endif
return(result);
}
#pragma warn +pia
//
// PM_SetPageLock() - Sets the lock type on a given page
// pml_Unlocked: Normal, page can be purged
// pml_Locked: Cannot be purged
// pml_EMS?: Same as pml_Locked, but if in EMS, use the physical page
// specified when returning the address. For sound stuff.
//
void
PM_SetPageLock(int pagenum,PMLockType lock)
{
if (pagenum < PMSoundStart)
Quit("PM_SetPageLock: Locking/unlocking non-sound page");
PMPages[pagenum].locked = lock;
}
//
// PM_Preload() - Loads as many pages as possible into all types of memory.
// Calls the update function after each load, indicating the current
// page, and the total pages that need to be loaded (for thermometer).
//
void
PM_Preload(boolean (*update)(word current,word total))
{
int i,j,
page,oogypage;
word current,total,
totalnonxms,totalxms,
mainfree,maintotal,
emsfree,emstotal,
xmsfree,xmstotal;
memptr addr;
PageListStruct *p;
mainfree = (MainPagesAvail - MainPagesUsed) + (EMSPagesAvail - EMSPagesUsed);
xmsfree = (XMSPagesAvail - XMSPagesUsed);
xmstotal = maintotal = 0;
for (i = 0;i < ChunksInFile;i++)
{
if (!PMPages[i].offset)
continue; // sparse
if ( PMPages[i].emsPage != -1 || PMPages[i].mainPage != -1 )
continue; // already in main mem
if ( mainfree )
{
maintotal++;
mainfree--;
}
else if ( xmsfree && (PMPages[i].xmsPage == -1) )
{
xmstotal++;
xmsfree--;
}
}
total = maintotal + xmstotal;
if (!total)
return;
page = 0;
current = 0;
//
// cache main/ems blocks
//
while (maintotal)
{
while ( !PMPages[page].offset || PMPages[page].mainPage != -1
|| PMPages[page].emsPage != -1 )
page++;
if (page >= ChunksInFile)
Quit ("PM_Preload: Pages>=ChunksInFile");
PM_GetPage(page);
page++;
current++;
maintotal--;
update(current,total);
}
//
// load stuff to XMS
//
if (xmstotal)
{
for (oogypage = 0 ; PMPages[oogypage].mainPage == -1 ; oogypage++)
;
addr = PM_GetPage(oogypage);
if (!addr)
Quit("PM_Preload: XMS buffer failed");
while (xmstotal)
{
while ( !PMPages[page].offset || PMPages[page].xmsPage != -1 )
page++;
if (page >= ChunksInFile)
Quit ("PM_Preload: Pages>=ChunksInFile");
p = &PMPages[page];
p->xmsPage = XMSPagesUsed++;
if (XMSPagesUsed > XMSPagesAvail)
Quit("PM_Preload: Exceeded XMS pages");
if (p->length > PMPageSize)
Quit("PM_Preload: Page too long");
PML_ReadFromFile((byte *)addr,p->offset,p->length);
PML_CopyToXMS((byte *)addr,p->xmsPage,p->length);
page++;
current++;
xmstotal--;
update(current,total);
}
p = &PMPages[oogypage];
PML_ReadFromFile((byte *)addr,p->offset,p->length);
}
update(total,total);
}
/////////////////////////////////////////////////////////////////////////////
//
// General code
//
/////////////////////////////////////////////////////////////////////////////
//
// PM_NextFrame() - Increments the frame counter and adjusts the thrash
// avoidence variables
//
// If currently in panic mode (to avoid thrashing), check to see if the
// appropriate number of frames have passed since the last time that
// we would have thrashed. If so, take us out of panic mode.
//
//
void
PM_NextFrame(void)
{
int i;
// Frame count overrun - kill the LRU hit entries & reset frame count
if (++PMFrameCount >= MAXLONG - 4)
{
for (i = 0;i < PMNumBlocks;i++)
PMPages[i].lastHit = 0;
PMFrameCount = 0;
}
#if 0
for (i = 0;i < PMSoundStart;i++)
{
if (PMPages[i].locked)
{
char buf[40];
sprintf(buf,"PM_NextFrame: Page %d is locked",i);
Quit(buf);
}
}
#endif
if (PMPanicMode)
{
// DEBUG - set border color
if ((!PMThrashing) && (!--PMPanicMode))
{
// DEBUG - reset border color
}
}
if (PMThrashing >= PMThrashThreshold)
PMPanicMode = PMUnThrashThreshold;
PMThrashing = false;
}
//
// PM_Reset() - Sets up caching structures
//
void PM_Reset(void)
{
int i;
PageListStruct *page;
XMSPagesAvail = XMSAvail / PMPageSizeKB;
EMSPagesAvail = EMSAvail * (EMSPageSizeKB / PMPageSizeKB);
EMSPhysicalPage = 0;
MainPagesUsed = EMSPagesUsed = XMSPagesUsed = 0;
PMPanicMode = false;
// Initialize page list
for (i = 0,page = PMPages;i < PMNumBlocks;i++,page++)
{
page->mainPage = -1;
page->emsPage = -1;
page->xmsPage = -1;
page->locked = false;
}
}
//
// PM_Startup() - Start up the Page Mgr
//
void PM_Startup(void)
{
boolean nomain,noems,noxms;
int i;
if (PMStarted)
return;
nomain = noems = noxms = false;
for (i = 1;i < _argc;i++)
{
switch (US_CheckParm(_argv[i],ParmStrings))
{
case 0:
nomain = true;
break;
case 1:
noems = true;
break;
case 2:
noxms = true;
break;
}
}
PML_OpenPageFile();
if (!noems)
PML_StartupEMS();
if (!noxms)
PML_StartupXMS();
if (nomain && !EMSPresent)
Quit("PM_Startup: No main or EMS");
else
PML_StartupMainMem();
PM_Reset();
PMStarted = true;
}
//
// PM_Shutdown() - Shut down the Page Mgr
//
void PM_Shutdown(void)
{
PML_ShutdownXMS();
PML_ShutdownEMS();
if (!PMStarted)
return;
PML_ClosePageFile();
PML_ShutdownMainMem();
}
......@@ -84,3 +84,176 @@ void CA_CacheMap (int mapnum);
void CA_CacheMarks (void);
void CA_CacheScreen (int chunk);
#define SAVENEARHEAP 0x400 // space to leave in data segment
#define SAVEFARHEAP 0 // space to leave in far heap
#define BUFFERSIZE 0x1000 // miscelanious, allways available buffer
#define MAXBLOCKS 700
//--------
#define EMS_INT 0x67
#define EMS_STATUS 0x40
#define EMS_GETFRAME 0x41
#define EMS_GETPAGES 0x42
#define EMS_ALLOCPAGES 0x43
#define EMS_MAPPAGE 0x44
#define EMS_FREEPAGES 0x45
#define EMS_VERSION 0x46
//--------
#define XMS_INT 0x2f
#define XMS_CALL(v) _AH = (v);\
asm call [DWORD PTR XMSDriver]
#define XMS_VERSION 0x00
#define XMS_ALLOCHMA 0x01
#define XMS_FREEHMA 0x02
#define XMS_GENABLEA20 0x03
#define XMS_GDISABLEA20 0x04
#define XMS_LENABLEA20 0x05
#define XMS_LDISABLEA20 0x06
#define XMS_QUERYA20 0x07
#define XMS_QUERYFREE 0x08
#define XMS_ALLOC 0x09
#define XMS_FREE 0x0A
#define XMS_MOVE 0x0B
#define XMS_LOCK 0x0C
#define XMS_UNLOCK 0x0D
#define XMS_GETINFO 0x0E
#define XMS_RESIZE 0x0F
#define XMS_ALLOCUMB 0x10
#define XMS_FREEUMB 0x11
//==========================================================================
typedef void * memptr;
typedef struct
{
long nearheap,farheap,EMSmem,XMSmem,mainmem;
} mminfotype;
//==========================================================================
extern mminfotype mminfo;
extern memptr bufferseg;
extern boolean mmerror;
extern void (* beforesort) (void);
extern void (* aftersort) (void);
//==========================================================================
void MM_Startup (void);
void MM_Shutdown (void);
void MM_MapEMS (void);
void MM_GetPtr (memptr *baseptr,unsigned long size);
void MM_FreePtr (memptr *baseptr);
void MM_SetPurge (memptr *baseptr, int purge);
void MM_SetLock (memptr *baseptr, boolean locked);
void MM_SortMem (void);
void MM_ShowMemory (void);
long MM_UnusedMemory (void);
long MM_TotalFree (void);
void MM_BombOnError (boolean bomb);
void MML_UseSpace (unsigned segstart, unsigned seglength);
//
// ID_PM.H
// Header file for Id Engine's Page Manager
//
// NOTE! PMPageSize must be an even divisor of EMSPageSize, and >= 1024
#define EMSPageSize 16384
#define EMSPageSizeSeg (EMSPageSize >> 4)
#define EMSPageSizeKB (EMSPageSize >> 10)
#define EMSFrameCount 4
#define PMPageSize 4096
#define PMPageSizeSeg (PMPageSize >> 4)
#define PMPageSizeKB (PMPageSize >> 10)
#define PMEMSSubPage (EMSPageSize / PMPageSize)
#define PMMinMainMem 10 // Min acceptable # of pages from main
#define PMMaxMainMem 100 // Max number of pages in main memory
#define PMThrashThreshold 1 // Number of page thrashes before panic mode
#define PMUnThrashThreshold 5 // Number of non-thrashing frames before leaving panic mode
typedef enum
{
pml_Unlocked,
pml_Locked
} PMLockType;
typedef enum
{
pmba_Unused = 0,
pmba_Used = 1,
pmba_Allocated = 2
} PMBlockAttr;
typedef struct
{
longword offset; // Offset of chunk into file
word length; // Length of the chunk
int xmsPage; // If in XMS, (xmsPage * PMPageSize) gives offset into XMS handle
PMLockType locked; // If set, this page can't be purged
int emsPage; // If in EMS, logical page/offset into page
int mainPage; // If in Main, index into handle array
longword lastHit; // Last frame number of hit
} PageListStruct;
typedef struct
{
int baseEMSPage; // Base EMS page for this phys frame
longword lastHit; // Last frame number of hit
} EMSListStruct;
extern boolean XMSPresent,EMSPresent;
extern word XMSPagesAvail,EMSPagesAvail;
extern word ChunksInFile,
PMSpriteStart,PMSoundStart;
extern PageListStruct *PMPages;
#define PM_GetSoundPage(v) PM_GetPage(PMSoundStart + (v))
#define PM_GetSpritePage(v) PM_GetPage(PMSpriteStart + (v))
#define PM_LockMainMem() PM_SetMainMemPurge(0)
#define PM_UnlockMainMem() PM_SetMainMemPurge(3)
extern char PageFileName[13];
extern void PM_Startup(void),
PM_Shutdown(void),
PM_Reset(void),
PM_Preload(boolean (*update)(word current,word total)),
PM_NextFrame(void),
PM_SetPageLock(int pagenum,PMLockType lock),
PM_SetMainPurge(int level),
PM_CheckMainMem(void);
extern memptr PM_GetPageAddress(int pagenum),
PM_GetPage(int pagenum); // Use this one to cache page
void PM_SetMainMemPurge(int level);
// NEWMM.C
/*
=============================================================================
ID software memory manager
--------------------------
Primary coder: John Carmack
RELIES ON
---------
Quit (char *error) function
WORK TO DO
----------
MM_SizePtr to change the size of a given pointer
Multiple purge levels utilized
EMS / XMS unmanaged routines
=============================================================================
*/
#include "ID_HEADS.H"
#pragma hdrstop
#pragma warn -pro
#pragma warn -use
/*
=============================================================================
LOCAL INFO
=============================================================================
*/
#define LOCKBIT 0x80 // if set in attributes, block cannot be moved
#define PURGEBITS 3 // 0-3 level, 0= unpurgable, 3= purge first
#define PURGEMASK 0xfffc
#define BASEATTRIBUTES 0 // unlocked, non purgable
#define MAXUMBS 10
typedef struct mmblockstruct
{
unsigned start,length;
unsigned attributes;
memptr *useptr; // pointer to the segment start
struct mmblockstruct *next;
} mmblocktype;
//#define GETNEWBLOCK {if(!(mmnew=mmfree))Quit("MM_GETNEWBLOCK: No free blocks!")\
// ;mmfree=mmfree->next;}
#define GETNEWBLOCK {if(!mmfree)MML_ClearBlock();mmnew=mmfree;mmfree=mmfree->next;}
#define FREEBLOCK(x) {*x->useptr=NULL;x->next=mmfree;mmfree=x;}
/*
=============================================================================
GLOBAL VARIABLES
=============================================================================
*/
mminfotype mminfo;
memptr bufferseg;
boolean mmerror;
void (* beforesort) (void);
void (* aftersort) (void);
/*
=============================================================================
LOCAL VARIABLES
=============================================================================
*/
boolean mmstarted;
void *farheap;
void *nearheap;
mmblocktype mmblocks[MAXBLOCKS], *mmhead, *mmfree, *mmrover, *mmnew;
boolean bombonerror;
//unsigned totalEMSpages,freeEMSpages,EMSpageframe,EMSpagesmapped,EMShandle;
void (* XMSaddr) (void); // far pointer to XMS driver
unsigned numUMBs,UMBbase[MAXUMBS];
//==========================================================================
//
// local prototypes
//
boolean MML_CheckForEMS (void);
void MML_ShutdownEMS (void);
void MM_MapEMS (void);
boolean MML_CheckForXMS (void);
void MML_ShutdownXMS (void);
void MML_UseSpace (unsigned segstart, unsigned seglength);
void MML_ClearBlock (void);
//==========================================================================
/*
======================
=
= MML_CheckForXMS
=
= Check for XMM driver
=
=======================
*/
boolean MML_CheckForXMS (void)
{
numUMBs = 0;
asm {
mov ax,0x4300
int 0x2f // query status of installed diver
cmp al,0x80
je good
}
return false;
good:
return true;
}
/*
======================
=
= MML_SetupXMS
=
= Try to allocate all upper memory block
=
=======================
*/
void MML_SetupXMS (void)
{
unsigned base,size;
asm {
mov ax,0x4310
int 0x2f
mov [WORD PTR XMSaddr],bx
mov [WORD PTR XMSaddr+2],es // function pointer to XMS driver
}
getmemory:
asm {
mov ah,XMS_ALLOCUMB
mov dx,0xffff // try for largest block possible
call [DWORD PTR XMSaddr]
or ax,ax
jnz gotone
cmp bl,0xb0 // error: smaller UMB is available
jne done;
mov ah,XMS_ALLOCUMB
call [DWORD PTR XMSaddr] // DX holds largest available UMB
or ax,ax
jz done // another error...
}
gotone:
asm {
mov [base],bx
mov [size],dx
}
MML_UseSpace (base,size);
mminfo.XMSmem += size*16;
UMBbase[numUMBs] = base;
numUMBs++;
if (numUMBs < MAXUMBS)
goto getmemory;
done:;
}
/*
======================
=
= MML_ShutdownXMS
=
======================
*/
void MML_ShutdownXMS (void)
{
int i;
unsigned base;
for (i=0;i<numUMBs;i++)
{
base = UMBbase[i];
asm mov ah,XMS_FREEUMB
asm mov dx,[base]
asm call [DWORD PTR XMSaddr]
}
}
//==========================================================================
/*
======================
=
= MML_UseSpace
=
= Marks a range of paragraphs as usable by the memory manager
= This is used to mark space for the near heap, far heap, ems page frame,
= and upper memory blocks
=
======================
*/
void MML_UseSpace (unsigned segstart, unsigned seglength)
{
mmblocktype *scan, *last;
unsigned oldend;
long extra;
scan = last = mmhead;
mmrover = mmhead; // reset rover to start of memory
//
// search for the block that contains the range of segments
//
while (scan->start+scan->length < segstart)
{
last = scan;
scan = scan->next;
}
//
// take the given range out of the block
//
oldend = scan->start + scan->length;
extra = oldend - (segstart+seglength);
if (extra < 0)
Quit ("MML_UseSpace: Segment spans two blocks!");
if (segstart == scan->start)
{
last->next = scan->next; // unlink block
FREEBLOCK(scan);
scan = last;
}
else
scan->length = segstart-scan->start; // shorten block
if (extra > 0)
{
GETNEWBLOCK;
mmnew->useptr = NULL;
mmnew->next = scan->next;
scan->next = mmnew;
mmnew->start = segstart+seglength;
mmnew->length = extra;
mmnew->attributes = LOCKBIT;
}
}
//==========================================================================
/*
====================
=
= MML_ClearBlock
=
= We are out of blocks, so free a purgable block
=
====================
*/
void MML_ClearBlock (void)
{
mmblocktype *scan, *last;
scan = mmhead->next;
while (scan)
{
if (!(scan->attributes&LOCKBIT) && (scan->attributes&PURGEBITS) )
{
MM_FreePtr(scan->useptr);
return;
}
scan = scan->next;
}
Quit ("MM_ClearBlock: No purgable blocks!");
}
//==========================================================================
/*
===================
=
= MM_Startup
=
= Grabs all space from turbo with malloc/farmalloc
= Allocates bufferseg misc buffer
=
===================
*/
static char *ParmStrings[] = {"noems","noxms",""};
void MM_Startup (void)
{
int i;
unsigned long length;
void *start;
unsigned segstart,seglength,endfree;
if (mmstarted)
MM_Shutdown ();
mmstarted = true;
bombonerror = true;
//
// set up the linked list (everything in the free list;
//
mmhead = NULL;
mmfree = &mmblocks[0];
for (i=0;i<MAXBLOCKS-1;i++)
mmblocks[i].next = &mmblocks[i+1];
mmblocks[i].next = NULL;
//
// locked block of all memory until we punch out free space
//
GETNEWBLOCK;
mmhead = mmnew; // this will allways be the first node
mmnew->start = 0;
mmnew->length = 0xffff;
mmnew->attributes = LOCKBIT;
mmnew->next = NULL;
mmrover = mmhead;
//
// get all available near conventional memory segments
//
length=coreleft();
start = (void *)(nearheap = malloc(length));
length -= 16-(FP_OFF(start)&15);
length -= SAVENEARHEAP;
seglength = length / 16; // now in paragraphs
segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;
MML_UseSpace (segstart,seglength);
mminfo.nearheap = length;
//
// get all available far conventional memory segments
//
length=farcoreleft();
start = farheap = farmalloc(length);
length -= 16-(FP_OFF(start)&15);
length -= SAVEFARHEAP;
seglength = length / 16; // now in paragraphs
segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;
MML_UseSpace (segstart,seglength);
mminfo.farheap = length;
mminfo.mainmem = mminfo.nearheap + mminfo.farheap;
//
// allocate the misc buffer
//
mmrover = mmhead; // start looking for space after low block
MM_GetPtr (&bufferseg,BUFFERSIZE);
}
//==========================================================================
/*
====================
=
= MM_Shutdown
=
= Frees all conventional, EMS, and XMS allocated
=
====================
*/
void MM_Shutdown (void)
{
if (!mmstarted)
return;
farfree (farheap);
free (nearheap);
// MML_ShutdownXMS ();
}
//==========================================================================
/*
====================
=
= MM_GetPtr
=
= Allocates an unlocked, unpurgable block
=
====================
*/
void MM_GetPtr (memptr *baseptr,unsigned long size)
{
mmblocktype *scan, *lastscan, *endscan, *purge, *next;
int search;
unsigned needed,startseg;
needed = (size+15)/16; // convert size from bytes to paragraphs
GETNEWBLOCK; // fill in start and next after a spot is found
mmnew->length = needed;
mmnew->useptr = baseptr;
mmnew->attributes = BASEATTRIBUTES;
tryagain:
for (search = 0; search<3; search++)
{
//
// first search: try to allocate right after the rover, then on up
// second search: search from the head pointer up to the rover
// third search: compress memory, then scan from start
if (search == 1 && mmrover == mmhead)
search++;
switch (search)
{
case 0:
lastscan = mmrover;
scan = mmrover->next;
endscan = NULL;
break;
case 1:
lastscan = mmhead;
scan = mmhead->next;
endscan = mmrover;
break;
case 2:
MM_SortMem ();
lastscan = mmhead;
scan = mmhead->next;
endscan = NULL;
break;
}
startseg = lastscan->start + lastscan->length;
while (scan != endscan)
{
if (scan->start - startseg >= needed)
{
//
// got enough space between the end of lastscan and
// the start of scan, so throw out anything in the middle
// and allocate the new block
//
purge = lastscan->next;
lastscan->next = mmnew;
mmnew->start = *(unsigned *)baseptr = startseg;
mmnew->next = scan;
while ( purge != scan)
{ // free the purgable block
next = purge->next;
FREEBLOCK(purge);
purge = next; // purge another if not at scan
}
mmrover = mmnew;
return; // good allocation!
}
//
// if this block is purge level zero or locked, skip past it
//
if ( (scan->attributes & LOCKBIT)
|| !(scan->attributes & PURGEBITS) )
{
lastscan = scan;
startseg = lastscan->start + lastscan->length;
}
scan=scan->next; // look at next line
}
}
if (bombonerror)
{
extern char configname[];
extern boolean insetupscaling;
extern int viewsize;
boolean SetViewSize (unsigned width, unsigned height);
#define HEIGHTRATIO 0.50
//
// wolf hack -- size the view down
//
if (!insetupscaling && viewsize>10)
{
mmblocktype *savedmmnew;
savedmmnew = mmnew;
viewsize -= 2;
SetViewSize (viewsize*16,viewsize*16*HEIGHTRATIO);
mmnew = savedmmnew;
goto tryagain;
}
// unlink(configname);
Quit ("MM_GetPtr: Out of memory!");
}
else
mmerror = true;
}
//==========================================================================
/*
====================
=
= MM_FreePtr
=
= Deallocates an unlocked, purgable block
=
====================
*/
void MM_FreePtr (memptr *baseptr)
{
mmblocktype *scan, *last;
last = mmhead;
scan = last->next;
if (baseptr == mmrover->useptr) // removed the last allocated block
mmrover = mmhead;
while (scan->useptr != baseptr && scan)
{
last = scan;
scan = scan->next;
}
if (!scan)
Quit ("MM_FreePtr: Block not found!");
last->next = scan->next;
FREEBLOCK(scan);
}
//==========================================================================
/*
=====================
=
= MM_SetPurge
=
= Sets the purge level for a block (locked blocks cannot be made purgable)
=
=====================
*/
void MM_SetPurge (memptr *baseptr, int purge)
{
mmblocktype *start;
start = mmrover;
do
{
if (mmrover->useptr == baseptr)
break;
mmrover = mmrover->next;
if (!mmrover)
mmrover = mmhead;
else if (mmrover == start)
Quit ("MM_SetPurge: Block not found!");
} while (1);
mmrover->attributes &= ~PURGEBITS;
mmrover->attributes |= purge;
}
//==========================================================================
/*
=====================
=
= MM_SetLock
=
= Locks / unlocks the block
=
=====================
*/
void MM_SetLock (memptr *baseptr, boolean locked)
{
mmblocktype *start;
start = mmrover;
do
{
if (mmrover->useptr == baseptr)
break;
mmrover = mmrover->next;
if (!mmrover)
mmrover = mmhead;
else if (mmrover == start)
Quit ("MM_SetLock: Block not found!");
} while (1);
mmrover->attributes &= ~LOCKBIT;
mmrover->attributes |= locked*LOCKBIT;
}
//==========================================================================
/*
=====================
=
= MM_SortMem
=
= Throws out all purgable stuff and compresses movable blocks
=
=====================
*/
void MM_SortMem (void)
{
mmblocktype *scan, *last, *next;
unsigned start,length,source,dest;
int playing;
//
// lock down a currently playing sound
//
playing = SD_SoundPlaying ();
if (playing)
{
switch (SoundMode)
{
case sdm_PC:
playing += STARTPCSOUNDS;
break;
case sdm_AdLib:
playing += STARTADLIBSOUNDS;
break;
}
MM_SetLock(&(memptr)audiosegs[playing],true);
}
SD_StopSound();
if (beforesort)
beforesort();
scan = mmhead;
last = NULL; // shut up compiler warning
while (scan)
{
if (scan->attributes & LOCKBIT)
{
//
// block is locked, so try to pile later blocks right after it
//
start = scan->start + scan->length;
}
else
{
if (scan->attributes & PURGEBITS)
{
//
// throw out the purgable block
//
next = scan->next;
FREEBLOCK(scan);
last->next = next;
scan = next;
continue;
}
else
{
//
// push the non purgable block on top of the last moved block
//
if (scan->start != start)
{
length = scan->length;
source = scan->start;
dest = start;
while (length > 0xf00)
{
movedata(source,0,dest,0,0xf00*16);
length -= 0xf00;
source += 0xf00;
dest += 0xf00;
}
movedata(source,0,dest,0,length*16);
scan->start = start;
*(unsigned *)scan->useptr = start;
}
start = scan->start + scan->length;
}
}
last = scan;
scan = scan->next; // go to next block
}
mmrover = mmhead;
if (aftersort)
aftersort();
if (playing)
MM_SetLock(&(memptr)audiosegs[playing],false);
}
//==========================================================================
/*
=====================
=
= MM_ShowMemory
=
=====================
*/
void MM_ShowMemory (void)
{
mmblocktype *scan;
unsigned color,temp,x,y;
long end,owner;
char scratch[80],str[10];
temp = bufferofs;
bufferofs = displayofs;
scan = mmhead;
end = -1;
while (scan)
{
if (scan->attributes & PURGEBITS)
color = 5; // dark purple = purgable
else
color = 9; // medium blue = non purgable
if (scan->attributes & LOCKBIT)
color = 12; // red = locked
if (scan->start<=end)
Quit ("MM_ShowMemory: Memory block order currupted!");
end = scan->length-1;
y = scan->start/320;
x = scan->start%320;
VW_Hlin(x,x+end,y,color);
VW_Plot(x,y,15);
if (scan->next && scan->next->start > end+1)
VW_Hlin(x+end+1,x+(scan->next->start-scan->start),y,0); // black = free
scan = scan->next;
}
VW_FadeIn ();
IN_Ack();
bufferofs = temp;
}
//==========================================================================
/*
=====================
=
= MM_DumpData
=
=====================
*/
void MM_DumpData (void)
{
mmblocktype *scan, *best;
long lowest,oldlowest;
unsigned owner;
char lock,purge;
FILE *dumpfile;
free (nearheap);
dumpfile = fopen ("MMDUMP.TXT","w");
if (!dumpfile)
Quit ("MM_DumpData: Couldn't open MMDUMP.TXT!");
lowest = -1;
do
{
oldlowest = lowest;
lowest = 0xffff;
scan = mmhead;
while (scan)
{
owner = (unsigned)scan->useptr;
if (owner && owner<lowest && owner > oldlowest)
{
best = scan;
lowest = owner;
}
scan = scan->next;
}
if (lowest != 0xffff)
{
if (best->attributes & PURGEBITS)
purge = 'P';
else
purge = '-';
if (best->attributes & LOCKBIT)
lock = 'L';
else
lock = '-';
fprintf (dumpfile,"0x%p (%c%c) = %u\n"
,(unsigned)lowest,lock,purge,best->length);
}
} while (lowest != 0xffff);
fclose (dumpfile);
Quit ("MMDUMP.TXT created.");
}
//==========================================================================
/*
======================
=
= MM_UnusedMemory
=
= Returns the total free space without purging
=
======================
*/
long MM_UnusedMemory (void)
{
unsigned free;
mmblocktype *scan;
free = 0;
scan = mmhead;
while (scan->next)
{
free += scan->next->start - (scan->start + scan->length);
scan = scan->next;
}
return free*16l;
}
//==========================================================================
/*
======================
=
= MM_TotalFree
=
= Returns the total free space with purging
=
======================
*/
long MM_TotalFree (void)
{
unsigned free;
mmblocktype *scan;
free = 0;
scan = mmhead;
while (scan->next)
{
if ((scan->attributes&PURGEBITS) && !(scan->attributes&LOCKBIT))
free += scan->length;
free += scan->next->start - (scan->start + scan->length);
scan = scan->next;
}
return free*16l;
}
//==========================================================================
/*
=====================
=
= MM_BombOnError
=
=====================
*/
void MM_BombOnError (boolean bomb)
{
bombonerror = bomb;
}
#define SAVENEARHEAP 0x400 // space to leave in data segment
#define SAVEFARHEAP 0 // space to leave in far heap
#define BUFFERSIZE 0x1000 // miscelanious, allways available buffer
#define MAXBLOCKS 700
//--------
#define EMS_INT 0x67
#define EMS_STATUS 0x40
#define EMS_GETFRAME 0x41
#define EMS_GETPAGES 0x42
#define EMS_ALLOCPAGES 0x43
#define EMS_MAPPAGE 0x44
#define EMS_FREEPAGES 0x45
#define EMS_VERSION 0x46
//--------
#define XMS_INT 0x2f
#define XMS_CALL(v) _AH = (v);\
asm call [DWORD PTR XMSDriver]
#define XMS_VERSION 0x00
#define XMS_ALLOCHMA 0x01
#define XMS_FREEHMA 0x02
#define XMS_GENABLEA20 0x03
#define XMS_GDISABLEA20 0x04
#define XMS_LENABLEA20 0x05
#define XMS_LDISABLEA20 0x06
#define XMS_QUERYA20 0x07
#define XMS_QUERYFREE 0x08
#define XMS_ALLOC 0x09
#define XMS_FREE 0x0A
#define XMS_MOVE 0x0B
#define XMS_LOCK 0x0C
#define XMS_UNLOCK 0x0D
#define XMS_GETINFO 0x0E
#define XMS_RESIZE 0x0F
#define XMS_ALLOCUMB 0x10
#define XMS_FREEUMB 0x11
//==========================================================================
typedef void * memptr;
typedef struct
{
long nearheap,farheap,EMSmem,XMSmem,mainmem;
} mminfotype;
//==========================================================================
extern mminfotype mminfo;
extern memptr bufferseg;
extern boolean mmerror;
extern void (* beforesort) (void);
extern void (* aftersort) (void);
//==========================================================================
void MM_Startup (void);
void MM_Shutdown (void);
void MM_MapEMS (void);
void MM_GetPtr (memptr *baseptr,unsigned long size);
void MM_FreePtr (memptr *baseptr);
void MM_SetPurge (memptr *baseptr, int purge);
void MM_SetLock (memptr *baseptr, boolean locked);
void MM_SortMem (void);
void MM_ShowMemory (void);
long MM_UnusedMemory (void);
long MM_TotalFree (void);
void MM_BombOnError (boolean bomb);
void MML_UseSpace (unsigned segstart, unsigned seglength);
//
// 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 *PMPages, *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 *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)&copy;
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 *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 *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 *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 *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 *buf;
longword *offsetptr;
word *lengthptr;
PageListStruct *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 *)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 *)buf,size))
Quit("PML_OpenPageFile: Offset read failed");
offsetptr = (longword *)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 *)buf,size))
Quit("PML_OpenPageFile: Length read failed");
lengthptr = (word *)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 *)PMSegPages);
}
}
/////////////////////////////////////////////////////////////////////////////
//
// Allocation, etc., code
//
/////////////////////////////////////////////////////////////////////////////
//
// PML_GetEMSAddress()
//
// Page is in EMS, so figure out which EMS physical page should be used
// to map our page in. If normal page, use EMS physical page 3, else
// use the physical page specified by the lock type
//
#if 1
#pragma argsused // DEBUG - remove lock parameter
memptr
PML_GetEMSAddress(int page,PMLockType lock)
{
int i,emspage;
word emsoff,emsbase,offset;
emsoff = page & (PMEMSSubPage - 1);
emsbase = page - emsoff;
emspage = -1;
// See if this page is already mapped in
for (i = 0;i < EMSFrameCount;i++)
{
if (EMSList[i].baseEMSPage == emsbase)
{
emspage = i; // Yep - don't do a redundant remapping
break;
}
}
// If page isn't already mapped in, find LRU EMS frame, and use it
if (emspage == -1)
{
longword last = MAXLONG;
for (i = 0;i < EMSFrameCount;i++)
{
if (EMSList[i].lastHit < last)
{
emspage = i;
last = EMSList[i].lastHit;
}
}
EMSList[emspage].baseEMSPage = emsbase;
PML_MapEMS(page / PMEMSSubPage,emspage);
}
if (emspage == -1)
Quit("PML_GetEMSAddress: EMS find failed");
EMSList[emspage].lastHit = PMFrameCount;
offset = emspage * EMSPageSizeSeg;
offset += emsoff * PMPageSizeSeg;
return((memptr)(EMSPageFrame + offset));
}
#else
memptr
PML_GetEMSAddress(int page,PMLockType lock)
{
word emspage;
emspage = (lock < pml_EMSLock)? 3 : (lock - pml_EMSLock);
PML_MapEMS(page / PMEMSSubPage,emspage);
return((memptr)(EMSPageFrame + (emspage * EMSPageSizeSeg)
+ ((page & (PMEMSSubPage - 1)) * PMPageSizeSeg)));
}
#endif
//
// PM_GetPageAddress() - Returns the address of a given page
// Maps in EMS if necessary
// Returns nil if block isn't cached into Main Memory or EMS
//
//
memptr
PM_GetPageAddress(int pagenum)
{
PageListStruct *page;
page = &PMPages[pagenum];
if (page->mainPage != -1)
return(MainMemPages[page->mainPage]);
else if (page->emsPage != -1)
return(PML_GetEMSAddress(page->emsPage,page->locked));
else
return(nil);
}
//
// PML_GiveLRUPage() - Returns the page # of the least recently used
// present & unlocked main/EMS page (or main page if mainonly is true)
//
int
PML_GiveLRUPage(boolean mainonly)
{
int i,lru;
long last;
PageListStruct *page;
for (i = 0,page = PMPages,lru = -1,last = MAXLONG;i < ChunksInFile;i++,page++)
{
if
(
(page->lastHit < last)
&& ((page->emsPage != -1) || (page->mainPage != -1))
&& (page->locked == pml_Unlocked)
&& (!(mainonly && (page->mainPage == -1)))
)
{
last = page->lastHit;
lru = i;
}
}
if (lru == -1)
Quit("PML_GiveLRUPage: LRU Search failed");
return(lru);
}
//
// PML_GiveLRUXMSPage() - Returns the page # of the least recently used
// (and present) XMS page.
// This routine won't return the XMS page protected (by XMSProtectPage)
//
int
PML_GiveLRUXMSPage(void)
{
int i,lru;
long last;
PageListStruct *page;
for (i = 0,page = PMPages,lru = -1,last = MAXLONG;i < ChunksInFile;i++,page++)
{
if
(
(page->xmsPage != -1)
&& (page->lastHit < last)
&& (i != XMSProtectPage)
)
{
last = page->lastHit;
lru = i;
}
}
return(lru);
}
//
// PML_PutPageInXMS() - If page isn't in XMS, find LRU XMS page and replace
// it with the main/EMS page
//
void
PML_PutPageInXMS(int pagenum)
{
int usexms;
PageListStruct *page;
if (!XMSPresent)
return;
page = &PMPages[pagenum];
if (page->xmsPage != -1)
return; // Already in XMS
if (XMSPagesUsed < XMSPagesAvail)
page->xmsPage = XMSPagesUsed++;
else
{
usexms = PML_GiveLRUXMSPage();
if (usexms == -1)
Quit("PML_PutPageInXMS: No XMS LRU");
page->xmsPage = PMPages[usexms].xmsPage;
PMPages[usexms].xmsPage = -1;
}
PML_CopyToXMS(PM_GetPageAddress(pagenum),page->xmsPage,page->length);
}
//
// PML_TransferPageSpace() - A page is being replaced, so give the new page
// the old one's address space. Returns the address of the new page.
//
memptr
PML_TransferPageSpace(int orig,int new)
{
memptr addr;
PageListStruct *origpage, *newpage;
if (orig == new)
Quit("PML_TransferPageSpace: Identity replacement");
origpage = &PMPages[orig];
newpage = &PMPages[new];
if (origpage->locked != pml_Unlocked)
Quit("PML_TransferPageSpace: Killing locked page");
if ((origpage->emsPage == -1) && (origpage->mainPage == -1))
Quit("PML_TransferPageSpace: Reusing non-existent page");
// Copy page that's about to be purged into XMS
PML_PutPageInXMS(orig);
// Get the address, and force EMS into a physical page if necessary
addr = PM_GetPageAddress(orig);
// Steal the address
newpage->emsPage = origpage->emsPage;
newpage->mainPage = origpage->mainPage;
// Mark replaced page as purged
origpage->mainPage = origpage->emsPage = -1;
if (!addr)
Quit("PML_TransferPageSpace: Zero replacement");
return(addr);
}
//
// PML_GetAPageBuffer() - A page buffer is needed. Either get it from the
// main/EMS free pool, or use PML_GiveLRUPage() to find which page to
// steal the buffer from. Returns a far pointer to the page buffer, and
// sets the fields inside the given page structure appropriately.
// If mainonly is true, free EMS will be ignored, and only main pages
// will be looked at by PML_GiveLRUPage().
//
byte *PML_GetAPageBuffer(int pagenum,boolean mainonly)
{
byte *addr = nil;
int i,n;
PMBlockAttr *used;
PageListStruct *page;
page = &PMPages[pagenum];
if ((EMSPagesUsed < EMSPagesAvail) && !mainonly)
{
// There's remaining EMS - use it
page->emsPage = EMSPagesUsed++;
addr = PML_GetEMSAddress(page->emsPage,page->locked);
}
else if (MainPagesUsed < MainPagesAvail)
{
// There's remaining main memory - use it
for (i = 0,n = -1,used = MainMemUsed;i < PMMaxMainMem;i++,used++)
{
if ((*used & pmba_Allocated) && !(*used & pmba_Used))
{
n = i;
*used |= pmba_Used;
break;
}
}
if (n == -1)
Quit("PML_GetPageBuffer: MainPagesAvail lied");
addr = MainMemPages[n];
if (!addr)
Quit("PML_GetPageBuffer: Purged main block");
page->mainPage = n;
MainPagesUsed++;
}
else
addr = PML_TransferPageSpace(PML_GiveLRUPage(mainonly),pagenum);
if (!addr)
Quit("PML_GetPageBuffer: Search failed");
return(addr);
}
//
// PML_GetPageFromXMS() - If page is in XMS, find LRU main/EMS page and
// replace it with the page from XMS. If mainonly is true, will only
// search for LRU main page.
// XMSProtectPage is set to the page to be retrieved from XMS, so that if
// the page from which we're stealing the main/EMS from isn't in XMS,
// it won't copy over the page that we're trying to get from XMS.
// (pages that are being purged are copied into XMS, if possible)
//
memptr
PML_GetPageFromXMS(int pagenum,boolean mainonly)
{
byte *checkaddr;
memptr addr = nil;
PageListStruct *page;
page = &PMPages[pagenum];
if (XMSPresent && (page->xmsPage != -1))
{
XMSProtectPage = pagenum;
checkaddr = PML_GetAPageBuffer(pagenum,mainonly);
if (FP_OFF(checkaddr))
Quit("PML_GetPageFromXMS: Non segment pointer");
addr = (memptr)FP_SEG(checkaddr);
PML_CopyFromXMS(addr,page->xmsPage,page->length);
XMSProtectPage = -1;
}
return(addr);
}
//
// PML_LoadPage() - A page is not in main/EMS memory, and it's not in XMS.
// Load it into either main or EMS. If mainonly is true, the page will
// only be loaded into main.
//
void
PML_LoadPage(int pagenum,boolean mainonly)
{
byte *addr;
PageListStruct *page;
addr = PML_GetAPageBuffer(pagenum,mainonly);
page = &PMPages[pagenum];
PML_ReadFromFile(addr,page->offset,page->length);
}
//
// PM_GetPage() - Returns the address of the page, loading it if necessary
// First, check if in Main Memory or EMS
// Then, check XMS
// If not in XMS, load into Main Memory or EMS
//
#pragma warn -pia
memptr
PM_GetPage(int pagenum)
{
memptr result;
if (pagenum >= ChunksInFile)
Quit("PM_GetPage: Invalid page request");
#if 0 // for debugging
asm mov dx,STATUS_REGISTER_1
asm in al,dx
asm mov dx,ATR_INDEX
asm mov al,ATR_OVERSCAN
asm out dx,al
asm mov al,10 // bright green
asm out dx,al
#endif
if (!(result = PM_GetPageAddress(pagenum)))
{
boolean mainonly = (pagenum >= PMSoundStart);
if (!PMPages[pagenum].offset) // JDC: sparse page
Quit ("Tried to load a sparse page!");
if (!(result = PML_GetPageFromXMS(pagenum,mainonly)))
{
if (PMPages[pagenum].lastHit == PMFrameCount)
PMThrashing++;
PML_LoadPage(pagenum,mainonly);
result = PM_GetPageAddress(pagenum);
}
}
PMPages[pagenum].lastHit = PMFrameCount;
#if 0 // for debugging
asm mov dx,STATUS_REGISTER_1
asm in al,dx
asm mov dx,ATR_INDEX
asm mov al,ATR_OVERSCAN
asm out dx,al
asm mov al,3 // blue
asm out dx,al
asm mov al,0x20 // normal
asm out dx,al
#endif
return(result);
}
#pragma warn +pia
//
// PM_SetPageLock() - Sets the lock type on a given page
// pml_Unlocked: Normal, page can be purged
// pml_Locked: Cannot be purged
// pml_EMS?: Same as pml_Locked, but if in EMS, use the physical page
// specified when returning the address. For sound stuff.
//
void
PM_SetPageLock(int pagenum,PMLockType lock)
{
if (pagenum < PMSoundStart)
Quit("PM_SetPageLock: Locking/unlocking non-sound page");
PMPages[pagenum].locked = lock;
}
//
// PM_Preload() - Loads as many pages as possible into all types of memory.
// Calls the update function after each load, indicating the current
// page, and the total pages that need to be loaded (for thermometer).
//
void
PM_Preload(boolean (*update)(word current,word total))
{
int i,j,
page,oogypage;
word current,total,
totalnonxms,totalxms,
mainfree,maintotal,
emsfree,emstotal,
xmsfree,xmstotal;
memptr addr;
PageListStruct *p;
mainfree = (MainPagesAvail - MainPagesUsed) + (EMSPagesAvail - EMSPagesUsed);
xmsfree = (XMSPagesAvail - XMSPagesUsed);
xmstotal = maintotal = 0;
for (i = 0;i < ChunksInFile;i++)
{
if (!PMPages[i].offset)
continue; // sparse
if ( PMPages[i].emsPage != -1 || PMPages[i].mainPage != -1 )
continue; // already in main mem
if ( mainfree )
{
maintotal++;
mainfree--;
}
else if ( xmsfree && (PMPages[i].xmsPage == -1) )
{
xmstotal++;
xmsfree--;
}
}
total = maintotal + xmstotal;
if (!total)
return;
page = 0;
current = 0;
//
// cache main/ems blocks
//
while (maintotal)
{
while ( !PMPages[page].offset || PMPages[page].mainPage != -1
|| PMPages[page].emsPage != -1 )
page++;
if (page >= ChunksInFile)
Quit ("PM_Preload: Pages>=ChunksInFile");
PM_GetPage(page);
page++;
current++;
maintotal--;
update(current,total);
}
//
// load stuff to XMS
//
if (xmstotal)
{
for (oogypage = 0 ; PMPages[oogypage].mainPage == -1 ; oogypage++)
;
addr = PM_GetPage(oogypage);
if (!addr)
Quit("PM_Preload: XMS buffer failed");
while (xmstotal)
{
while ( !PMPages[page].offset || PMPages[page].xmsPage != -1 )
page++;
if (page >= ChunksInFile)
Quit ("PM_Preload: Pages>=ChunksInFile");
p = &PMPages[page];
p->xmsPage = XMSPagesUsed++;
if (XMSPagesUsed > XMSPagesAvail)
Quit("PM_Preload: Exceeded XMS pages");
if (p->length > PMPageSize)
Quit("PM_Preload: Page too long");
PML_ReadFromFile((byte *)addr,p->offset,p->length);
PML_CopyToXMS((byte *)addr,p->xmsPage,p->length);
page++;
current++;
xmstotal--;
update(current,total);
}
p = &PMPages[oogypage];
PML_ReadFromFile((byte *)addr,p->offset,p->length);
}
update(total,total);
}
/////////////////////////////////////////////////////////////////////////////
//
// General code
//
/////////////////////////////////////////////////////////////////////////////
//
// PM_NextFrame() - Increments the frame counter and adjusts the thrash
// avoidence variables
//
// If currently in panic mode (to avoid thrashing), check to see if the
// appropriate number of frames have passed since the last time that
// we would have thrashed. If so, take us out of panic mode.
//
//
void
PM_NextFrame(void)
{
int i;
// Frame count overrun - kill the LRU hit entries & reset frame count
if (++PMFrameCount >= MAXLONG - 4)
{
for (i = 0;i < PMNumBlocks;i++)
PMPages[i].lastHit = 0;
PMFrameCount = 0;
}
#if 0
for (i = 0;i < PMSoundStart;i++)
{
if (PMPages[i].locked)
{
char buf[40];
sprintf(buf,"PM_NextFrame: Page %d is locked",i);
Quit(buf);
}
}
#endif
if (PMPanicMode)
{
// DEBUG - set border color
if ((!PMThrashing) && (!--PMPanicMode))
{
// DEBUG - reset border color
}
}
if (PMThrashing >= PMThrashThreshold)
PMPanicMode = PMUnThrashThreshold;
PMThrashing = false;
}
//
// PM_Reset() - Sets up caching structures
//
void
PM_Reset(void)
{
int i;
PageListStruct *page;
XMSPagesAvail = XMSAvail / PMPageSizeKB;
EMSPagesAvail = EMSAvail * (EMSPageSizeKB / PMPageSizeKB);
EMSPhysicalPage = 0;
MainPagesUsed = EMSPagesUsed = XMSPagesUsed = 0;
PMPanicMode = false;
// Initialize page list
for (i = 0,page = PMPages;i < PMNumBlocks;i++,page++)
{
page->mainPage = -1;
page->emsPage = -1;
page->xmsPage = -1;
page->locked = false;
}
}
//
// PM_Startup() - Start up the Page Mgr
//
void
PM_Startup(void)
{
boolean nomain,noems,noxms;
int i;
if (PMStarted)
return;
nomain = noems = noxms = false;
for (i = 1;i < _argc;i++)
{
switch (US_CheckParm(_argv[i],ParmStrings))
{
case 0:
nomain = true;
break;
case 1:
noems = true;
break;
case 2:
noxms = true;
break;
}
}
PML_OpenPageFile();
if (!noems)
PML_StartupEMS();
if (!noxms)
PML_StartupXMS();
if (nomain && !EMSPresent)
Quit("PM_Startup: No main or EMS");
else
PML_StartupMainMem();
PM_Reset();
PMStarted = true;
}
//
// PM_Shutdown() - Shut down the Page Mgr
//
void
PM_Shutdown(void)
{
PML_ShutdownXMS();
PML_ShutdownEMS();
if (!PMStarted)
return;
PML_ClosePageFile();
PML_ShutdownMainMem();
}
//
// ID_PM.H
// Header file for Id Engine's Page Manager
//
// NOTE! PMPageSize must be an even divisor of EMSPageSize, and >= 1024
#define EMSPageSize 16384
#define EMSPageSizeSeg (EMSPageSize >> 4)
#define EMSPageSizeKB (EMSPageSize >> 10)
#define EMSFrameCount 4
#define PMPageSize 4096
#define PMPageSizeSeg (PMPageSize >> 4)
#define PMPageSizeKB (PMPageSize >> 10)
#define PMEMSSubPage (EMSPageSize / PMPageSize)
#define PMMinMainMem 10 // Min acceptable # of pages from main
#define PMMaxMainMem 100 // Max number of pages in main memory
#define PMThrashThreshold 1 // Number of page thrashes before panic mode
#define PMUnThrashThreshold 5 // Number of non-thrashing frames before leaving panic mode
typedef enum
{
pml_Unlocked,
pml_Locked
} PMLockType;
typedef enum
{
pmba_Unused = 0,
pmba_Used = 1,
pmba_Allocated = 2
} PMBlockAttr;
typedef struct
{
longword offset; // Offset of chunk into file
word length; // Length of the chunk
int xmsPage; // If in XMS, (xmsPage * PMPageSize) gives offset into XMS handle
PMLockType locked; // If set, this page can't be purged
int emsPage; // If in EMS, logical page/offset into page
int mainPage; // If in Main, index into handle array
longword lastHit; // Last frame number of hit
} PageListStruct;
typedef struct
{
int baseEMSPage; // Base EMS page for this phys frame
longword lastHit; // Last frame number of hit
} EMSListStruct;
extern boolean XMSPresent,EMSPresent;
extern word XMSPagesAvail,EMSPagesAvail;
extern word ChunksInFile,
PMSpriteStart,PMSoundStart;
extern PageListStruct *PMPages;
#define PM_GetSoundPage(v) PM_GetPage(PMSoundStart + (v))
#define PM_GetSpritePage(v) PM_GetPage(PMSpriteStart + (v))
#define PM_LockMainMem() PM_SetMainMemPurge(0)
#define PM_UnlockMainMem() PM_SetMainMemPurge(3)
extern char PageFileName[13];
extern void PM_Startup(void),
PM_Shutdown(void),
PM_Reset(void),
PM_Preload(boolean (*update)(word current,word total)),
PM_NextFrame(void),
PM_SetPageLock(int pagenum,PMLockType lock),
PM_SetMainPurge(int level),
PM_CheckMainMem(void);
extern memptr PM_GetPageAddress(int pagenum),
PM_GetPage(int pagenum); // Use this one to cache page
void PM_SetMainMemPurge(int level);
......@@ -51,7 +51,7 @@ TEXT FORMATTING COMMANDS
int pagenum,numpages;
unsigned leftmargin[TEXTROWS],rightmargin[TEXTROWS];
char far *text;
char *text;
unsigned rowon;
int picx,picy,picnum,picdelay;
......@@ -542,7 +542,7 @@ void BackPage (void)
*/
void CacheLayoutGraphics (void)
{
char far *bombpoint, far *textstart;
char *bombpoint, *textstart;
char ch;
textstart = text;
......@@ -598,7 +598,7 @@ void CacheLayoutGraphics (void)
#ifdef JAPAN
void ShowArticle (int which)
#else
void ShowArticle (char far *article)
void ShowArticle (char *article)
#endif
{
#ifdef JAPAN
......@@ -747,7 +747,7 @@ char helpfilename[13] = "HELPART.",
void HelpScreens (void)
{
int artnum;
char far *text;
char *text;
memptr layout;
......@@ -800,7 +800,7 @@ void HelpScreens (void)
void EndText (void)
{
int artnum;
char far *text;
char *text;
memptr layout;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment