home *** CD-ROM | disk | FTP | other *** search
/ 100 af Verdens Bedste Spil / 100Spil.iso / dos / wolf3d / source / wolfsrc.1 / ID_PM.C < prev    next >
C/C++ Source or Header  |  1993-02-04  |  28KB  |  1,200 lines

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