4 =============================================================================
\r
6 ID software memory manager
\r
7 --------------------------
\r
9 Primary coder: John Carmack
\r
13 Quit (char *error) function
\r
18 MM_SizePtr to change the size of a given pointer
\r
20 Multiple purge levels utilized
\r
22 EMS / XMS unmanaged routines
\r
24 =============================================================================
\r
27 #include "ID_HEADS.H"
\r
34 =============================================================================
\r
38 =============================================================================
\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
48 typedef struct mmblockstruct
\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
57 //#define GETNEWBLOCK {if(!(mmnew=mmfree))Quit("MM_GETNEWBLOCK: No free blocks!")\
\r
58 // ;mmfree=mmfree->next;}
\r
60 #define GETNEWBLOCK {if(!mmfree)MML_ClearBlock();mmnew=mmfree;mmfree=mmfree->next;}
\r
62 #define FREEBLOCK(x) {*x->useptr=NULL;x->next=mmfree;mmfree=x;}
\r
65 =============================================================================
\r
69 =============================================================================
\r
76 void (* beforesort) (void);
\r
77 void (* aftersort) (void);
\r
80 =============================================================================
\r
84 =============================================================================
\r
92 mmblocktype far mmblocks[MAXBLOCKS]
\r
93 ,far *mmhead,far *mmfree,far *mmrover,far *mmnew;
\r
95 boolean bombonerror;
\r
97 //unsigned totalEMSpages,freeEMSpages,EMSpageframe,EMSpagesmapped,EMShandle;
\r
99 void (* XMSaddr) (void); // far pointer to XMS driver
\r
101 unsigned numUMBs,UMBbase[MAXUMBS];
\r
103 //==========================================================================
\r
106 // local prototypes
\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
117 //==========================================================================
\r
120 ======================
\r
124 = Check for XMM driver
\r
126 =======================
\r
129 boolean MML_CheckForXMS (void)
\r
135 int 0x2f // query status of installed diver
\r
147 ======================
\r
151 = Try to allocate all upper memory block
\r
153 =======================
\r
156 void MML_SetupXMS (void)
\r
158 unsigned base,size;
\r
163 mov [WORD PTR XMSaddr],bx
\r
164 mov [WORD PTR XMSaddr+2],es // function pointer to XMS driver
\r
169 mov ah,XMS_ALLOCUMB
\r
170 mov dx,0xffff // try for largest block possible
\r
171 call [DWORD PTR XMSaddr]
\r
175 cmp bl,0xb0 // error: smaller UMB is available
\r
178 mov ah,XMS_ALLOCUMB
\r
179 call [DWORD PTR XMSaddr] // DX holds largest available UMB
\r
181 jz done // another error...
\r
189 MML_UseSpace (base,size);
\r
190 mminfo.XMSmem += size*16;
\r
191 UMBbase[numUMBs] = base;
\r
193 if (numUMBs < MAXUMBS)
\r
201 ======================
\r
205 ======================
\r
208 void MML_ShutdownXMS (void)
\r
213 for (i=0;i<numUMBs;i++)
\r
217 asm mov ah,XMS_FREEUMB
\r
219 asm call [DWORD PTR XMSaddr]
\r
223 //==========================================================================
\r
226 ======================
\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
234 ======================
\r
237 void MML_UseSpace (unsigned segstart, unsigned seglength)
\r
239 mmblocktype far *scan,far *last;
\r
243 scan = last = mmhead;
\r
244 mmrover = mmhead; // reset rover to start of memory
\r
247 // search for the block that contains the range of segments
\r
249 while (scan->start+scan->length < segstart)
\r
256 // take the given range out of the block
\r
258 oldend = scan->start + scan->length;
\r
259 extra = oldend - (segstart+seglength);
\r
261 Quit ("MML_UseSpace: Segment spans two blocks!");
\r
263 if (segstart == scan->start)
\r
265 last->next = scan->next; // unlink block
\r
270 scan->length = segstart-scan->start; // shorten block
\r
275 mmnew->useptr = NULL;
\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
286 //==========================================================================
\r
289 ====================
\r
293 = We are out of blocks, so free a purgable block
\r
295 ====================
\r
298 void MML_ClearBlock (void)
\r
300 mmblocktype far *scan,far *last;
\r
302 scan = mmhead->next;
\r
306 if (!(scan->attributes&LOCKBIT) && (scan->attributes&PURGEBITS) )
\r
308 MM_FreePtr(scan->useptr);
\r
314 Quit ("MM_ClearBlock: No purgable blocks!");
\r
318 //==========================================================================
\r
321 ===================
\r
325 = Grabs all space from turbo with malloc/farmalloc
\r
326 = Allocates bufferseg misc buffer
\r
328 ===================
\r
331 static char *ParmStrings[] = {"noems","noxms",""};
\r
333 void MM_Startup (void)
\r
336 unsigned long length;
\r
338 unsigned segstart,seglength,endfree;
\r
345 bombonerror = true;
\r
347 // set up the linked list (everything in the free list;
\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
356 // locked block of all memory until we punch out free space
\r
359 mmhead = mmnew; // this will allways be the first node
\r
361 mmnew->length = 0xffff;
\r
362 mmnew->attributes = LOCKBIT;
\r
363 mmnew->next = NULL;
\r
368 // get all available near conventional memory segments
\r
371 start = (void far *)(nearheap = malloc(length));
\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
381 // get all available far conventional memory segments
\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
394 // allocate the misc buffer
\r
396 mmrover = mmhead; // start looking for space after low block
\r
398 MM_GetPtr (&bufferseg,BUFFERSIZE);
\r
401 //==========================================================================
\r
404 ====================
\r
408 = Frees all conventional, EMS, and XMS allocated
\r
410 ====================
\r
413 void MM_Shutdown (void)
\r
420 // MML_ShutdownXMS ();
\r
423 //==========================================================================
\r
426 ====================
\r
430 = Allocates an unlocked, unpurgable block
\r
432 ====================
\r
435 void MM_GetPtr (memptr *baseptr,unsigned long size)
\r
437 mmblocktype far *scan,far *lastscan,far *endscan
\r
438 ,far *purge,far *next;
\r
440 unsigned needed,startseg;
\r
442 needed = (size+15)/16; // convert size from bytes to paragraphs
\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
450 for (search = 0; search<3; search++)
\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
462 lastscan = mmrover;
\r
463 scan = mmrover->next;
\r
468 scan = mmhead->next;
\r
474 scan = mmhead->next;
\r
479 startseg = lastscan->start + lastscan->length;
\r
481 while (scan != endscan)
\r
483 if (scan->start - startseg >= needed)
\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
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
498 purge = next; // purge another if not at scan
\r
501 return; // good allocation!
\r
505 // if this block is purge level zero or locked, skip past it
\r
507 if ( (scan->attributes & LOCKBIT)
\r
508 || !(scan->attributes & PURGEBITS) )
\r
511 startseg = lastscan->start + lastscan->length;
\r
515 scan=scan->next; // look at next line
\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
528 // wolf hack -- size the view down
\r
530 if (!insetupscaling && viewsize>10)
\r
532 mmblocktype far *savedmmnew;
\r
533 savedmmnew = mmnew;
\r
535 SetViewSize (viewsize*16,viewsize*16*HEIGHTRATIO);
\r
536 mmnew = savedmmnew;
\r
540 // unlink(configname);
\r
541 Quit ("MM_GetPtr: Out of memory!");
\r
547 //==========================================================================
\r
550 ====================
\r
554 = Deallocates an unlocked, purgable block
\r
556 ====================
\r
559 void MM_FreePtr (memptr *baseptr)
\r
561 mmblocktype far *scan,far *last;
\r
566 if (baseptr == mmrover->useptr) // removed the last allocated block
\r
569 while (scan->useptr != baseptr && scan)
\r
576 Quit ("MM_FreePtr: Block not found!");
\r
578 last->next = scan->next;
\r
582 //==========================================================================
\r
585 =====================
\r
589 = Sets the purge level for a block (locked blocks cannot be made purgable)
\r
591 =====================
\r
594 void MM_SetPurge (memptr *baseptr, int purge)
\r
596 mmblocktype far *start;
\r
602 if (mmrover->useptr == baseptr)
\r
605 mmrover = mmrover->next;
\r
609 else if (mmrover == start)
\r
610 Quit ("MM_SetPurge: Block not found!");
\r
614 mmrover->attributes &= ~PURGEBITS;
\r
615 mmrover->attributes |= purge;
\r
618 //==========================================================================
\r
621 =====================
\r
625 = Locks / unlocks the block
\r
627 =====================
\r
630 void MM_SetLock (memptr *baseptr, boolean locked)
\r
632 mmblocktype far *start;
\r
638 if (mmrover->useptr == baseptr)
\r
641 mmrover = mmrover->next;
\r
645 else if (mmrover == start)
\r
646 Quit ("MM_SetLock: Block not found!");
\r
650 mmrover->attributes &= ~LOCKBIT;
\r
651 mmrover->attributes |= locked*LOCKBIT;
\r
654 //==========================================================================
\r
657 =====================
\r
661 = Throws out all purgable stuff and compresses movable blocks
\r
663 =====================
\r
666 void MM_SortMem (void)
\r
668 mmblocktype far *scan,far *last,far *next;
\r
669 unsigned start,length,source,dest;
\r
673 // lock down a currently playing sound
\r
675 playing = SD_SoundPlaying ();
\r
681 playing += STARTPCSOUNDS;
\r
684 playing += STARTADLIBSOUNDS;
\r
687 MM_SetLock(&(memptr)audiosegs[playing],true);
\r
698 last = NULL; // shut up compiler warning
\r
702 if (scan->attributes & LOCKBIT)
\r
705 // block is locked, so try to pile later blocks right after it
\r
707 start = scan->start + scan->length;
\r
711 if (scan->attributes & PURGEBITS)
\r
714 // throw out the purgable block
\r
725 // push the non purgable block on top of the last moved block
\r
727 if (scan->start != start)
\r
729 length = scan->length;
\r
730 source = scan->start;
\r
732 while (length > 0xf00)
\r
734 movedata(source,0,dest,0,0xf00*16);
\r
739 movedata(source,0,dest,0,length*16);
\r
741 scan->start = start;
\r
742 *(unsigned *)scan->useptr = start;
\r
744 start = scan->start + scan->length;
\r
749 scan = scan->next; // go to next block
\r
758 MM_SetLock(&(memptr)audiosegs[playing],false);
\r
762 //==========================================================================
\r
765 =====================
\r
769 =====================
\r
772 void MM_ShowMemory (void)
\r
774 mmblocktype far *scan;
\r
775 unsigned color,temp,x,y;
\r
777 char scratch[80],str[10];
\r
780 bufferofs = displayofs;
\r
787 if (scan->attributes & PURGEBITS)
\r
788 color = 5; // dark purple = purgable
\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
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
812 //==========================================================================
\r
815 =====================
\r
819 =====================
\r
822 void MM_DumpData (void)
\r
824 mmblocktype far *scan,far *best;
\r
825 long lowest,oldlowest;
\r
832 dumpfile = fopen ("MMDUMP.TXT","w");
\r
834 Quit ("MM_DumpData: Couldn't open MMDUMP.TXT!");
\r
839 oldlowest = lowest;
\r
845 owner = (unsigned)scan->useptr;
\r
847 if (owner && owner<lowest && owner > oldlowest)
\r
856 if (lowest != 0xffff)
\r
858 if (best->attributes & PURGEBITS)
\r
862 if (best->attributes & LOCKBIT)
\r
866 fprintf (dumpfile,"0x%p (%c%c) = %u\n"
\r
867 ,(unsigned)lowest,lock,purge,best->length);
\r
870 } while (lowest != 0xffff);
\r
873 Quit ("MMDUMP.TXT created.");
\r
876 //==========================================================================
\r
880 ======================
\r
884 = Returns the total free space without purging
\r
886 ======================
\r
889 long MM_UnusedMemory (void)
\r
892 mmblocktype far *scan;
\r
899 free += scan->next->start - (scan->start + scan->length);
\r
906 //==========================================================================
\r
910 ======================
\r
914 = Returns the total free space with purging
\r
916 ======================
\r
919 long MM_TotalFree (void)
\r
922 mmblocktype far *scan;
\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
938 //==========================================================================
\r
941 =====================
\r
945 =====================
\r
948 void MM_BombOnError (boolean bomb)
\r
950 bombonerror = bomb;
\r