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

  1. // NEWMM.C
  2.  
  3. /*
  4. =============================================================================
  5.  
  6.            ID software memory manager
  7.            --------------------------
  8.  
  9. Primary coder: John Carmack
  10.  
  11. RELIES ON
  12. ---------
  13. Quit (char *error) function
  14.  
  15.  
  16. WORK TO DO
  17. ----------
  18. MM_SizePtr to change the size of a given pointer
  19.  
  20. Multiple purge levels utilized
  21.  
  22. EMS / XMS unmanaged routines
  23.  
  24. =============================================================================
  25. */
  26.  
  27. #include "ID_HEADS.H"
  28. #pragma hdrstop
  29.  
  30. #pragma warn -pro
  31. #pragma warn -use
  32.  
  33. /*
  34. =============================================================================
  35.  
  36.                             LOCAL INFO
  37.  
  38. =============================================================================
  39. */
  40.  
  41. #define LOCKBIT        0x80    // if set in attributes, block cannot be moved
  42. #define PURGEBITS    3        // 0-3 level, 0= unpurgable, 3= purge first
  43. #define PURGEMASK    0xfffc
  44. #define BASEATTRIBUTES    0    // unlocked, non purgable
  45.  
  46. #define MAXUMBS        10
  47.  
  48. typedef struct mmblockstruct
  49. {
  50.     unsigned    start,length;
  51.     unsigned    attributes;
  52.     memptr        *useptr;    // pointer to the segment start
  53.     struct mmblockstruct far *next;
  54. } mmblocktype;
  55.  
  56.  
  57. //#define GETNEWBLOCK {if(!(mmnew=mmfree))Quit("MM_GETNEWBLOCK: No free blocks!")\
  58. //    ;mmfree=mmfree->next;}
  59.  
  60. #define GETNEWBLOCK {if(!mmfree)MML_ClearBlock();mmnew=mmfree;mmfree=mmfree->next;}
  61.  
  62. #define FREEBLOCK(x) {*x->useptr=NULL;x->next=mmfree;mmfree=x;}
  63.  
  64. /*
  65. =============================================================================
  66.  
  67.                          GLOBAL VARIABLES
  68.  
  69. =============================================================================
  70. */
  71.  
  72. mminfotype    mminfo;
  73. memptr        bufferseg;
  74. boolean        mmerror;
  75.  
  76. void        (* beforesort) (void);
  77. void        (* aftersort) (void);
  78.  
  79. /*
  80. =============================================================================
  81.  
  82.                          LOCAL VARIABLES
  83.  
  84. =============================================================================
  85. */
  86.  
  87. boolean        mmstarted;
  88.  
  89. void far    *farheap;
  90. void        *nearheap;
  91.  
  92. mmblocktype    far mmblocks[MAXBLOCKS]
  93.             ,far *mmhead,far *mmfree,far *mmrover,far *mmnew;
  94.  
  95. boolean        bombonerror;
  96.  
  97. //unsigned    totalEMSpages,freeEMSpages,EMSpageframe,EMSpagesmapped,EMShandle;
  98.  
  99. void        (* XMSaddr) (void);        // far pointer to XMS driver
  100.  
  101. unsigned    numUMBs,UMBbase[MAXUMBS];
  102.  
  103. //==========================================================================
  104.  
  105. //
  106. // local prototypes
  107. //
  108.  
  109. boolean        MML_CheckForEMS (void);
  110. void         MML_ShutdownEMS (void);
  111. void         MM_MapEMS (void);
  112. boolean     MML_CheckForXMS (void);
  113. void         MML_ShutdownXMS (void);
  114. void        MML_UseSpace (unsigned segstart, unsigned seglength);
  115. void         MML_ClearBlock (void);
  116.  
  117. //==========================================================================
  118.  
  119. /*
  120. ======================
  121. =
  122. = MML_CheckForXMS
  123. =
  124. = Check for XMM driver
  125. =
  126. =======================
  127. */
  128.  
  129. boolean MML_CheckForXMS (void)
  130. {
  131.     numUMBs = 0;
  132.  
  133. asm {
  134.     mov    ax,0x4300
  135.     int    0x2f                // query status of installed diver
  136.     cmp    al,0x80
  137.     je    good
  138.     }
  139.  
  140.     return false;
  141. good:
  142.     return true;
  143. }
  144.  
  145.  
  146. /*
  147. ======================
  148. =
  149. = MML_SetupXMS
  150. =
  151. = Try to allocate all upper memory block
  152. =
  153. =======================
  154. */
  155.  
  156. void MML_SetupXMS (void)
  157. {
  158.     unsigned    base,size;
  159.  
  160. asm    {
  161.     mov    ax,0x4310
  162.     int    0x2f
  163.     mov    [WORD PTR XMSaddr],bx
  164.     mov    [WORD PTR XMSaddr+2],es        // function pointer to XMS driver
  165.     }
  166.  
  167. getmemory:
  168. asm    {
  169.     mov    ah,XMS_ALLOCUMB
  170.     mov    dx,0xffff                    // try for largest block possible
  171.     call    [DWORD PTR XMSaddr]
  172.     or    ax,ax
  173.     jnz    gotone
  174.  
  175.     cmp    bl,0xb0                        // error: smaller UMB is available
  176.     jne    done;
  177.  
  178.     mov    ah,XMS_ALLOCUMB
  179.     call    [DWORD PTR XMSaddr]        // DX holds largest available UMB
  180.     or    ax,ax
  181.     jz    done                        // another error...
  182.     }
  183.  
  184. gotone:
  185. asm    {
  186.     mov    [base],bx
  187.     mov    [size],dx
  188.     }
  189.     MML_UseSpace (base,size);
  190.     mminfo.XMSmem += size*16;
  191.     UMBbase[numUMBs] = base;
  192.     numUMBs++;
  193.     if (numUMBs < MAXUMBS)
  194.         goto getmemory;
  195.  
  196. done:;
  197. }
  198.  
  199.  
  200. /*
  201. ======================
  202. =
  203. = MML_ShutdownXMS
  204. =
  205. ======================
  206. */
  207.  
  208. void MML_ShutdownXMS (void)
  209. {
  210.     int    i;
  211.     unsigned    base;
  212.  
  213.     for (i=0;i<numUMBs;i++)
  214.     {
  215.         base = UMBbase[i];
  216.  
  217. asm    mov    ah,XMS_FREEUMB
  218. asm    mov    dx,[base]
  219. asm    call    [DWORD PTR XMSaddr]
  220.     }
  221. }
  222.  
  223. //==========================================================================
  224.  
  225. /*
  226. ======================
  227. =
  228. = MML_UseSpace
  229. =
  230. = Marks a range of paragraphs as usable by the memory manager
  231. = This is used to mark space for the near heap, far heap, ems page frame,
  232. = and upper memory blocks
  233. =
  234. ======================
  235. */
  236.  
  237. void MML_UseSpace (unsigned segstart, unsigned seglength)
  238. {
  239.     mmblocktype far *scan,far *last;
  240.     unsigned    oldend;
  241.     long        extra;
  242.  
  243.     scan = last = mmhead;
  244.     mmrover = mmhead;        // reset rover to start of memory
  245.  
  246. //
  247. // search for the block that contains the range of segments
  248. //
  249.     while (scan->start+scan->length < segstart)
  250.     {
  251.         last = scan;
  252.         scan = scan->next;
  253.     }
  254.  
  255. //
  256. // take the given range out of the block
  257. //
  258.     oldend = scan->start + scan->length;
  259.     extra = oldend - (segstart+seglength);
  260.     if (extra < 0)
  261.         Quit ("MML_UseSpace: Segment spans two blocks!");
  262.  
  263.     if (segstart == scan->start)
  264.     {
  265.         last->next = scan->next;            // unlink block
  266.         FREEBLOCK(scan);
  267.         scan = last;
  268.     }
  269.     else
  270.         scan->length = segstart-scan->start;    // shorten block
  271.  
  272.     if (extra > 0)
  273.     {
  274.         GETNEWBLOCK;
  275.         mmnew->useptr = NULL;
  276.  
  277.         mmnew->next = scan->next;
  278.         scan->next = mmnew;
  279.         mmnew->start = segstart+seglength;
  280.         mmnew->length = extra;
  281.         mmnew->attributes = LOCKBIT;
  282.     }
  283.  
  284. }
  285.  
  286. //==========================================================================
  287.  
  288. /*
  289. ====================
  290. =
  291. = MML_ClearBlock
  292. =
  293. = We are out of blocks, so free a purgable block
  294. =
  295. ====================
  296. */
  297.  
  298. void MML_ClearBlock (void)
  299. {
  300.     mmblocktype far *scan,far *last;
  301.  
  302.     scan = mmhead->next;
  303.  
  304.     while (scan)
  305.     {
  306.         if (!(scan->attributes&LOCKBIT) && (scan->attributes&PURGEBITS) )
  307.         {
  308.             MM_FreePtr(scan->useptr);
  309.             return;
  310.         }
  311.         scan = scan->next;
  312.     }
  313.  
  314.     Quit ("MM_ClearBlock: No purgable blocks!");
  315. }
  316.  
  317.  
  318. //==========================================================================
  319.  
  320. /*
  321. ===================
  322. =
  323. = MM_Startup
  324. =
  325. = Grabs all space from turbo with malloc/farmalloc
  326. = Allocates bufferseg misc buffer
  327. =
  328. ===================
  329. */
  330.  
  331. static    char *ParmStrings[] = {"noems","noxms",""};
  332.  
  333. void MM_Startup (void)
  334. {
  335.     int i;
  336.     unsigned     long length;
  337.     void far     *start;
  338.     unsigned     segstart,seglength,endfree;
  339.  
  340.     if (mmstarted)
  341.         MM_Shutdown ();
  342.  
  343.  
  344.     mmstarted = true;
  345.     bombonerror = true;
  346. //
  347. // set up the linked list (everything in the free list;
  348. //
  349.     mmhead = NULL;
  350.     mmfree = &mmblocks[0];
  351.     for (i=0;i<MAXBLOCKS-1;i++)
  352.         mmblocks[i].next = &mmblocks[i+1];
  353.     mmblocks[i].next = NULL;
  354.  
  355. //
  356. // locked block of all memory until we punch out free space
  357. //
  358.     GETNEWBLOCK;
  359.     mmhead = mmnew;                // this will allways be the first node
  360.     mmnew->start = 0;
  361.     mmnew->length = 0xffff;
  362.     mmnew->attributes = LOCKBIT;
  363.     mmnew->next = NULL;
  364.     mmrover = mmhead;
  365.  
  366.  
  367. //
  368. // get all available near conventional memory segments
  369. //
  370.     length=coreleft();
  371.     start = (void far *)(nearheap = malloc(length));
  372.  
  373.     length -= 16-(FP_OFF(start)&15);
  374.     length -= SAVENEARHEAP;
  375.     seglength = length / 16;            // now in paragraphs
  376.     segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;
  377.     MML_UseSpace (segstart,seglength);
  378.     mminfo.nearheap = length;
  379.  
  380. //
  381. // get all available far conventional memory segments
  382. //
  383.     length=farcoreleft();
  384.     start = farheap = farmalloc(length);
  385.     length -= 16-(FP_OFF(start)&15);
  386.     length -= SAVEFARHEAP;
  387.     seglength = length / 16;            // now in paragraphs
  388.     segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;
  389.     MML_UseSpace (segstart,seglength);
  390.     mminfo.farheap = length;
  391.     mminfo.mainmem = mminfo.nearheap + mminfo.farheap;
  392.  
  393. //
  394. // allocate the misc buffer
  395. //
  396.     mmrover = mmhead;        // start looking for space after low block
  397.  
  398.     MM_GetPtr (&bufferseg,BUFFERSIZE);
  399. }
  400.  
  401. //==========================================================================
  402.  
  403. /*
  404. ====================
  405. =
  406. = MM_Shutdown
  407. =
  408. = Frees all conventional, EMS, and XMS allocated
  409. =
  410. ====================
  411. */
  412.  
  413. void MM_Shutdown (void)
  414. {
  415.   if (!mmstarted)
  416.     return;
  417.  
  418.   farfree (farheap);
  419.   free (nearheap);
  420. //  MML_ShutdownXMS ();
  421. }
  422.  
  423. //==========================================================================
  424.  
  425. /*
  426. ====================
  427. =
  428. = MM_GetPtr
  429. =
  430. = Allocates an unlocked, unpurgable block
  431. =
  432. ====================
  433. */
  434.  
  435. void MM_GetPtr (memptr *baseptr,unsigned long size)
  436. {
  437.     mmblocktype far *scan,far *lastscan,far *endscan
  438.                 ,far *purge,far *next;
  439.     int            search;
  440.     unsigned    needed,startseg;
  441.  
  442.     needed = (size+15)/16;        // convert size from bytes to paragraphs
  443.  
  444.     GETNEWBLOCK;                // fill in start and next after a spot is found
  445.     mmnew->length = needed;
  446.     mmnew->useptr = baseptr;
  447.     mmnew->attributes = BASEATTRIBUTES;
  448.  
  449. tryagain:
  450.     for (search = 0; search<3; search++)
  451.     {
  452.     //
  453.     // first search:    try to allocate right after the rover, then on up
  454.     // second search:     search from the head pointer up to the rover
  455.     // third search:    compress memory, then scan from start
  456.         if (search == 1 && mmrover == mmhead)
  457.             search++;
  458.  
  459.         switch (search)
  460.         {
  461.         case 0:
  462.             lastscan = mmrover;
  463.             scan = mmrover->next;
  464.             endscan = NULL;
  465.             break;
  466.         case 1:
  467.             lastscan = mmhead;
  468.             scan = mmhead->next;
  469.             endscan = mmrover;
  470.             break;
  471.         case 2:
  472.             MM_SortMem ();
  473.             lastscan = mmhead;
  474.             scan = mmhead->next;
  475.             endscan = NULL;
  476.             break;
  477.         }
  478.  
  479.         startseg = lastscan->start + lastscan->length;
  480.  
  481.         while (scan != endscan)
  482.         {
  483.             if (scan->start - startseg >= needed)
  484.             {
  485.             //
  486.             // got enough space between the end of lastscan and
  487.             // the start of scan, so throw out anything in the middle
  488.             // and allocate the new block
  489.             //
  490.                 purge = lastscan->next;
  491.                 lastscan->next = mmnew;
  492.                 mmnew->start = *(unsigned *)baseptr = startseg;
  493.                 mmnew->next = scan;
  494.                 while ( purge != scan)
  495.                 {    // free the purgable block
  496.                     next = purge->next;
  497.                     FREEBLOCK(purge);
  498.                     purge = next;        // purge another if not at scan
  499.                 }
  500.                 mmrover = mmnew;
  501.                 return;    // good allocation!
  502.             }
  503.  
  504.             //
  505.             // if this block is purge level zero or locked, skip past it
  506.             //
  507.             if ( (scan->attributes & LOCKBIT)
  508.                 || !(scan->attributes & PURGEBITS) )
  509.             {
  510.                 lastscan = scan;
  511.                 startseg = lastscan->start + lastscan->length;
  512.             }
  513.  
  514.  
  515.             scan=scan->next;        // look at next line
  516.         }
  517.     }
  518.  
  519.     if (bombonerror)
  520.     {
  521.  
  522. extern char configname[];
  523. extern    boolean    insetupscaling;
  524. extern    int    viewsize;
  525. boolean SetViewSize (unsigned width, unsigned height);
  526. #define HEIGHTRATIO        0.50
  527. //
  528. // wolf hack -- size the view down
  529. //
  530.         if (!insetupscaling && viewsize>10)
  531.         {
  532. mmblocktype    far *savedmmnew;
  533.             savedmmnew = mmnew;
  534.             viewsize -= 2;
  535.             SetViewSize (viewsize*16,viewsize*16*HEIGHTRATIO);
  536.             mmnew = savedmmnew;
  537.             goto tryagain;
  538.         }
  539.  
  540. //        unlink(configname);
  541.         Quit ("MM_GetPtr: Out of memory!");
  542.     }
  543.     else
  544.         mmerror = true;
  545. }
  546.  
  547. //==========================================================================
  548.  
  549. /*
  550. ====================
  551. =
  552. = MM_FreePtr
  553. =
  554. = Deallocates an unlocked, purgable block
  555. =
  556. ====================
  557. */
  558.  
  559. void MM_FreePtr (memptr *baseptr)
  560. {
  561.     mmblocktype far *scan,far *last;
  562.  
  563.     last = mmhead;
  564.     scan = last->next;
  565.  
  566.     if (baseptr == mmrover->useptr)    // removed the last allocated block
  567.         mmrover = mmhead;
  568.  
  569.     while (scan->useptr != baseptr && scan)
  570.     {
  571.         last = scan;
  572.         scan = scan->next;
  573.     }
  574.  
  575.     if (!scan)
  576.         Quit ("MM_FreePtr: Block not found!");
  577.  
  578.     last->next = scan->next;
  579.  
  580.     FREEBLOCK(scan);
  581. }
  582. //==========================================================================
  583.  
  584. /*
  585. =====================
  586. =
  587. = MM_SetPurge
  588. =
  589. = Sets the purge level for a block (locked blocks cannot be made purgable)
  590. =
  591. =====================
  592. */
  593.  
  594. void MM_SetPurge (memptr *baseptr, int purge)
  595. {
  596.     mmblocktype far *start;
  597.  
  598.     start = mmrover;
  599.  
  600.     do
  601.     {
  602.         if (mmrover->useptr == baseptr)
  603.             break;
  604.  
  605.         mmrover = mmrover->next;
  606.  
  607.         if (!mmrover)
  608.             mmrover = mmhead;
  609.         else if (mmrover == start)
  610.             Quit ("MM_SetPurge: Block not found!");
  611.  
  612.     } while (1);
  613.  
  614.     mmrover->attributes &= ~PURGEBITS;
  615.     mmrover->attributes |= purge;
  616. }
  617.  
  618. //==========================================================================
  619.  
  620. /*
  621. =====================
  622. =
  623. = MM_SetLock
  624. =
  625. = Locks / unlocks the block
  626. =
  627. =====================
  628. */
  629.  
  630. void MM_SetLock (memptr *baseptr, boolean locked)
  631. {
  632.     mmblocktype far *start;
  633.  
  634.     start = mmrover;
  635.  
  636.     do
  637.     {
  638.         if (mmrover->useptr == baseptr)
  639.             break;
  640.  
  641.         mmrover = mmrover->next;
  642.  
  643.         if (!mmrover)
  644.             mmrover = mmhead;
  645.         else if (mmrover == start)
  646.             Quit ("MM_SetLock: Block not found!");
  647.  
  648.     } while (1);
  649.  
  650.     mmrover->attributes &= ~LOCKBIT;
  651.     mmrover->attributes |= locked*LOCKBIT;
  652. }
  653.  
  654. //==========================================================================
  655.  
  656. /*
  657. =====================
  658. =
  659. = MM_SortMem
  660. =
  661. = Throws out all purgable stuff and compresses movable blocks
  662. =
  663. =====================
  664. */
  665.  
  666. void MM_SortMem (void)
  667. {
  668.     mmblocktype far *scan,far *last,far *next;
  669.     unsigned    start,length,source,dest;
  670.     int            playing;
  671.  
  672.     //
  673.     // lock down a currently playing sound
  674.     //
  675.     playing = SD_SoundPlaying ();
  676.     if (playing)
  677.     {
  678.         switch (SoundMode)
  679.         {
  680.         case sdm_PC:
  681.             playing += STARTPCSOUNDS;
  682.             break;
  683.         case sdm_AdLib:
  684.             playing += STARTADLIBSOUNDS;
  685.             break;
  686.         }
  687.         MM_SetLock(&(memptr)audiosegs[playing],true);
  688.     }
  689.  
  690.  
  691.     SD_StopSound();
  692.  
  693.     if (beforesort)
  694.         beforesort();
  695.  
  696.     scan = mmhead;
  697.  
  698.     last = NULL;        // shut up compiler warning
  699.  
  700.     while (scan)
  701.     {
  702.         if (scan->attributes & LOCKBIT)
  703.         {
  704.         //
  705.         // block is locked, so try to pile later blocks right after it
  706.         //
  707.             start = scan->start + scan->length;
  708.         }
  709.         else
  710.         {
  711.             if (scan->attributes & PURGEBITS)
  712.             {
  713.             //
  714.             // throw out the purgable block
  715.             //
  716.                 next = scan->next;
  717.                 FREEBLOCK(scan);
  718.                 last->next = next;
  719.                 scan = next;
  720.                 continue;
  721.             }
  722.             else
  723.             {
  724.             //
  725.             // push the non purgable block on top of the last moved block
  726.             //
  727.                 if (scan->start != start)
  728.                 {
  729.                     length = scan->length;
  730.                     source = scan->start;
  731.                     dest = start;
  732.                     while (length > 0xf00)
  733.                     {
  734.                         movedata(source,0,dest,0,0xf00*16);
  735.                         length -= 0xf00;
  736.                         source += 0xf00;
  737.                         dest += 0xf00;
  738.                     }
  739.                     movedata(source,0,dest,0,length*16);
  740.  
  741.                     scan->start = start;
  742.                     *(unsigned *)scan->useptr = start;
  743.                 }
  744.                 start = scan->start + scan->length;
  745.             }
  746.         }
  747.  
  748.         last = scan;
  749.         scan = scan->next;        // go to next block
  750.     }
  751.  
  752.     mmrover = mmhead;
  753.  
  754.     if (aftersort)
  755.         aftersort();
  756.  
  757.     if (playing)
  758.         MM_SetLock(&(memptr)audiosegs[playing],false);
  759. }
  760.  
  761.  
  762. //==========================================================================
  763.  
  764. /*
  765. =====================
  766. =
  767. = MM_ShowMemory
  768. =
  769. =====================
  770. */
  771.  
  772. void MM_ShowMemory (void)
  773. {
  774.     mmblocktype far *scan;
  775.     unsigned color,temp,x,y;
  776.     long    end,owner;
  777.     char    scratch[80],str[10];
  778.  
  779.     temp = bufferofs;
  780.     bufferofs = displayofs;
  781.     scan = mmhead;
  782.  
  783.     end = -1;
  784.  
  785.     while (scan)
  786.     {
  787.         if (scan->attributes & PURGEBITS)
  788.             color = 5;        // dark purple = purgable
  789.         else
  790.             color = 9;        // medium blue = non purgable
  791.         if (scan->attributes & LOCKBIT)
  792.             color = 12;        // red = locked
  793.         if (scan->start<=end)
  794.             Quit ("MM_ShowMemory: Memory block order currupted!");
  795.         end = scan->length-1;
  796.         y = scan->start/320;
  797.         x = scan->start%320;
  798.         VW_Hlin(x,x+end,y,color);
  799.         VW_Plot(x,y,15);
  800.         if (scan->next && scan->next->start > end+1)
  801.             VW_Hlin(x+end+1,x+(scan->next->start-scan->start),y,0);    // black = free
  802.  
  803.         scan = scan->next;
  804.     }
  805.  
  806.     VW_FadeIn ();
  807.     IN_Ack();
  808.  
  809.     bufferofs = temp;
  810. }
  811.  
  812. //==========================================================================
  813.  
  814. /*
  815. =====================
  816. =
  817. = MM_DumpData
  818. =
  819. =====================
  820. */
  821.  
  822. void MM_DumpData (void)
  823. {
  824.     mmblocktype far *scan,far *best;
  825.     long    lowest,oldlowest;
  826.     unsigned    owner;
  827.     char    lock,purge;
  828.     FILE    *dumpfile;
  829.  
  830.  
  831.     free (nearheap);
  832.     dumpfile = fopen ("MMDUMP.TXT","w");
  833.     if (!dumpfile)
  834.         Quit ("MM_DumpData: Couldn't open MMDUMP.TXT!");
  835.  
  836.     lowest = -1;
  837.     do
  838.     {
  839.         oldlowest = lowest;
  840.         lowest = 0xffff;
  841.  
  842.         scan = mmhead;
  843.         while (scan)
  844.         {
  845.             owner = (unsigned)scan->useptr;
  846.  
  847.             if (owner && owner<lowest && owner > oldlowest)
  848.             {
  849.                 best = scan;
  850.                 lowest = owner;
  851.             }
  852.  
  853.             scan = scan->next;
  854.         }
  855.  
  856.         if (lowest != 0xffff)
  857.         {
  858.             if (best->attributes & PURGEBITS)
  859.                 purge = 'P';
  860.             else
  861.                 purge = '-';
  862.             if (best->attributes & LOCKBIT)
  863.                 lock = 'L';
  864.             else
  865.                 lock = '-';
  866.             fprintf (dumpfile,"0x%p (%c%c) = %u\n"
  867.             ,(unsigned)lowest,lock,purge,best->length);
  868.         }
  869.  
  870.     } while (lowest != 0xffff);
  871.  
  872.     fclose (dumpfile);
  873.     Quit ("MMDUMP.TXT created.");
  874. }
  875.  
  876. //==========================================================================
  877.  
  878.  
  879. /*
  880. ======================
  881. =
  882. = MM_UnusedMemory
  883. =
  884. = Returns the total free space without purging
  885. =
  886. ======================
  887. */
  888.  
  889. long MM_UnusedMemory (void)
  890. {
  891.     unsigned free;
  892.     mmblocktype far *scan;
  893.  
  894.     free = 0;
  895.     scan = mmhead;
  896.  
  897.     while (scan->next)
  898.     {
  899.         free += scan->next->start - (scan->start + scan->length);
  900.         scan = scan->next;
  901.     }
  902.  
  903.     return free*16l;
  904. }
  905.  
  906. //==========================================================================
  907.  
  908.  
  909. /*
  910. ======================
  911. =
  912. = MM_TotalFree
  913. =
  914. = Returns the total free space with purging
  915. =
  916. ======================
  917. */
  918.  
  919. long MM_TotalFree (void)
  920. {
  921.     unsigned free;
  922.     mmblocktype far *scan;
  923.  
  924.     free = 0;
  925.     scan = mmhead;
  926.  
  927.     while (scan->next)
  928.     {
  929.         if ((scan->attributes&PURGEBITS) && !(scan->attributes&LOCKBIT))
  930.             free += scan->length;
  931.         free += scan->next->start - (scan->start + scan->length);
  932.         scan = scan->next;
  933.     }
  934.  
  935.     return free*16l;
  936. }
  937.  
  938. //==========================================================================
  939.  
  940. /*
  941. =====================
  942. =
  943. = MM_BombOnError
  944. =
  945. =====================
  946. */
  947.  
  948. void MM_BombOnError (boolean bomb)
  949. {
  950.     bombonerror = bomb;
  951. }
  952.  
  953.  
  954.