OSDN Git Service

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