1 /* Catacomb Armageddon Source Code
\r
2 * Copyright (C) 1993-2014 Flat Rock Software
\r
4 * This program is free software; you can redistribute it and/or modify
\r
5 * it under the terms of the GNU General Public License as published by
\r
6 * the Free Software Foundation; either version 2 of the License, or
\r
7 * (at your option) any later version.
\r
9 * This program is distributed in the hope that it will be useful,
\r
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
12 * GNU General Public License for more details.
\r
14 * You should have received a copy of the GNU General Public License along
\r
15 * with this program; if not, write to the Free Software Foundation, Inc.,
\r
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
22 =============================================================================
\r
24 ID software memory manager
\r
25 --------------------------
\r
27 Primary coder: John Carmack
\r
31 Quit (char *error) function
\r
36 MM_SizePtr to change the size of a given pointer
\r
38 Multiple purge levels utilized
\r
40 EMS / XMS unmanaged routines
\r
42 =============================================================================
\r
45 #include "ID_HEADS.H"
\r
52 #if 0 // 1 == Debug/Dev ; 0 == Production/final
\r
54 #define OUT_OF_MEM_MSG "MM_GetPtr: Out of memory!\nYou were short :%ld bytes"
\r
59 #define OUT_OF_MEM_MSG "\n" \
\r
60 "You need more memory to run CATACOMB ARMAGEDDON. Read the INSTRUCTION\n" \
\r
61 "section of the START program for tips on getting more memory.\n"
\r
66 =============================================================================
\r
70 =============================================================================
\r
73 #define LOCKBIT 0x80 // if set in attributes, block cannot be moved
\r
74 #define PURGEBITS 3 // 0-3 level, 0= unpurgable, 3= purge first
\r
75 #define PURGEMASK 0xfffc
\r
76 #define BASEATTRIBUTES 0 // unlocked, non purgable
\r
80 typedef struct mmblockstruct
\r
82 unsigned start,length;
\r
83 unsigned attributes;
\r
84 memptr *useptr; // pointer to the segment start
\r
85 struct mmblockstruct far *next;
\r
89 //#define GETNEWBLOCK {if(!(mmnew=mmfree))Quit("MM_GETNEWBLOCK: No free blocks!")\
\r
90 // ;mmfree=mmfree->next;}
\r
92 #define GETNEWBLOCK {if(!mmfree)MML_ClearBlock();mmnew=mmfree;mmfree=mmfree->next;}
\r
94 #define FREEBLOCK(x) {*x->useptr=NULL;x->next=mmfree;mmfree=x;}
\r
97 =============================================================================
\r
101 =============================================================================
\r
108 void (* beforesort) (void);
\r
109 void (* aftersort) (void);
\r
112 =============================================================================
\r
116 =============================================================================
\r
124 mmblocktype far mmblocks[MAXBLOCKS]
\r
125 ,far *mmhead,far *mmfree,far *mmrover,far *mmnew;
\r
127 boolean bombonerror;
\r
129 unsigned totalEMSpages,freeEMSpages,EMSpageframe,EMSpagesmapped,EMShandle;
\r
131 void (* XMSaddr) (void); // far pointer to XMS driver
\r
133 unsigned numUMBs,UMBbase[MAXUMBS];
\r
135 //==========================================================================
\r
138 // local prototypes
\r
141 boolean MML_CheckForEMS (void);
\r
142 void MML_ShutdownEMS (void);
\r
143 void MM_MapEMS (void);
\r
144 boolean MML_CheckForXMS (void);
\r
145 void MML_ShutdownXMS (void);
\r
146 void MML_UseSpace (unsigned segstart, unsigned seglength);
\r
147 void MML_ClearBlock (void);
\r
149 //==========================================================================
\r
152 ======================
\r
156 = Routine from p36 of Extending DOS
\r
158 =======================
\r
161 char emmname[9] = "EMMXXXX0";
\r
163 boolean MML_CheckForEMS (void)
\r
165 asm mov dx,OFFSET emmname[0]
\r
167 asm int 0x21 // try to open EMMXXXX0 device
\r
173 asm int 0x21 // get device info
\r
181 asm int 0x21 // get status
\r
187 asm int 0x21 // close handle
\r
204 ======================
\r
208 =======================
\r
211 void MML_SetupEMS (void)
\r
213 char str[80],str2[10];
\r
216 totalEMSpages = freeEMSpages = EMSpageframe = EMSpagesmapped = 0;
\r
220 int EMS_INT // make sure EMS hardware is present
\r
228 cmp al,0x32 // only work on ems 3.2 or greater
\r
231 mov ah,EMS_GETFRAME
\r
232 int EMS_INT // find the page frame address
\r
235 mov [EMSpageframe],bx
\r
237 mov ah,EMS_GETPAGES
\r
238 int EMS_INT // find out how much EMS is there
\r
241 mov [totalEMSpages],dx
\r
242 mov [freeEMSpages],bx
\r
244 jz noEMS // no EMS at all to allocate
\r
247 jle getpages // there is only 1,2,3,or 4 pages
\r
248 mov bx,4 // we can't use more than 4 pages
\r
253 mov [EMSpagesmapped],bx
\r
254 mov ah,EMS_ALLOCPAGES // allocate up to 64k of EMS
\r
264 strcpy (str,"MML_SetupEMS: EMS error 0x");
\r
265 itoa(error,str2,16);
\r
275 ======================
\r
279 =======================
\r
282 void MML_ShutdownEMS (void)
\r
288 mov ah,EMS_FREEPAGES
\r
295 Quit ("MML_ShutdownEMS: Error freeing EMS!");
\r
302 ====================
\r
306 = Maps the 64k of EMS used by memory manager into the page frame
\r
307 = for general use. This only needs to be called if you are keeping
\r
308 = other things in EMS.
\r
310 ====================
\r
313 void MM_MapEMS (void)
\r
315 char str[80],str2[10];
\r
319 for (i=0;i<EMSpagesmapped;i++)
\r
323 mov bx,[i] // logical page
\r
324 mov al,bl // physical page
\r
325 mov dx,[EMShandle] // handle
\r
336 strcpy (str,"MM_MapEMS: EMS error 0x");
\r
337 itoa(error,str2,16);
\r
342 //==========================================================================
\r
345 ======================
\r
349 = Check for XMM driver
\r
351 =======================
\r
354 boolean MML_CheckForXMS (void)
\r
360 int 0x2f // query status of installed diver
\r
371 ======================
\r
375 = Try to allocate all upper memory block
\r
377 =======================
\r
380 void MML_SetupXMS (void)
\r
382 unsigned base,size;
\r
387 mov [WORD PTR XMSaddr],bx
\r
388 mov [WORD PTR XMSaddr+2],es // function pointer to XMS driver
\r
393 mov ah,XMS_ALLOCUMB
\r
394 mov dx,0xffff // try for largest block possible
\r
395 call [DWORD PTR XMSaddr]
\r
399 cmp bl,0xb0 // error: smaller UMB is available
\r
402 mov ah,XMS_ALLOCUMB
\r
403 call [DWORD PTR XMSaddr] // DX holds largest available UMB
\r
405 jz done // another error...
\r
413 MML_UseSpace (base,size);
\r
414 mminfo.XMSmem += size*16;
\r
415 UMBbase[numUMBs] = base;
\r
417 if (numUMBs < MAXUMBS)
\r
425 ======================
\r
429 ======================
\r
432 void MML_ShutdownXMS (void)
\r
437 for (i=0;i<numUMBs;i++)
\r
441 asm mov ah,XMS_FREEUMB
\r
443 asm call [DWORD PTR XMSaddr]
\r
447 //==========================================================================
\r
450 ======================
\r
454 = Marks a range of paragraphs as usable by the memory manager
\r
455 = This is used to mark space for the near heap, far heap, ems page frame,
\r
456 = and upper memory blocks
\r
458 ======================
\r
461 void MML_UseSpace (unsigned segstart, unsigned seglength)
\r
463 mmblocktype far *scan,far *last;
\r
467 scan = last = mmhead;
\r
468 mmrover = mmhead; // reset rover to start of memory
\r
471 // search for the block that contains the range of segments
\r
473 while (scan->start+scan->length < segstart)
\r
480 // take the given range out of the block
\r
482 oldend = scan->start + scan->length;
\r
483 extra = oldend - (segstart+seglength);
\r
485 Quit ("MML_UseSpace: Segment spans two blocks!");
\r
487 if (segstart == scan->start)
\r
489 last->next = scan->next; // unlink block
\r
494 scan->length = segstart-scan->start; // shorten block
\r
499 mmnew->next = scan->next;
\r
500 scan->next = mmnew;
\r
501 mmnew->start = segstart+seglength;
\r
502 mmnew->length = extra;
\r
503 mmnew->attributes = LOCKBIT;
\r
508 //==========================================================================
\r
511 ====================
\r
515 = We are out of blocks, so free a purgable block
\r
517 ====================
\r
520 void MML_ClearBlock (void)
\r
522 mmblocktype far *scan,far *last;
\r
524 scan = mmhead->next;
\r
528 if (!(scan->attributes&LOCKBIT) && (scan->attributes&PURGEBITS) )
\r
530 MM_FreePtr(scan->useptr);
\r
536 Quit ("MM_ClearBlock: No purgable blocks!");
\r
540 //==========================================================================
\r
543 ===================
\r
547 = Grabs all space from turbo with malloc/farmalloc
\r
548 = Allocates bufferseg misc buffer
\r
550 ===================
\r
553 static char *ParmStrings[] = {"noems","noxms",""};
\r
555 void MM_Startup (void)
\r
558 unsigned long length;
\r
560 unsigned segstart,seglength,endfree;
\r
567 bombonerror = true;
\r
569 // set up the linked list (everything in the free list;
\r
572 mmfree = &mmblocks[0];
\r
573 for (i=0;i<MAXBLOCKS-1;i++)
\r
574 mmblocks[i].next = &mmblocks[i+1];
\r
575 mmblocks[i].next = NULL;
\r
578 // locked block of all memory until we punch out free space
\r
581 mmhead = mmnew; // this will allways be the first node
\r
583 mmnew->length = 0xffff;
\r
584 mmnew->attributes = LOCKBIT;
\r
585 mmnew->next = NULL;
\r
590 // get all available near conventional memory segments
\r
593 start = (void far *)(nearheap = malloc(length));
\r
595 length -= 16-(FP_OFF(start)&15);
\r
596 length -= SAVENEARHEAP;
\r
597 seglength = length / 16; // now in paragraphs
\r
598 segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;
\r
599 MML_UseSpace (segstart,seglength);
\r
600 mminfo.nearheap = length;
\r
603 // get all available far conventional memory segments
\r
605 length=farcoreleft();
\r
606 start = farheap = farmalloc(length);
\r
607 length -= 16-(FP_OFF(start)&15);
\r
608 length -= SAVEFARHEAP;
\r
609 seglength = length / 16; // now in paragraphs
\r
610 segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;
\r
611 MML_UseSpace (segstart,seglength);
\r
612 mminfo.farheap = length;
\r
613 mminfo.mainmem = mminfo.nearheap + mminfo.farheap;
\r
617 // detect EMS and allocate up to 64K at page frame
\r
620 for (i = 1;i < _argc;i++)
\r
622 if ( US_CheckParm(_argv[i],ParmStrings) == 0)
\r
623 goto emsskip; // param NOEMS
\r
626 if (MML_CheckForEMS())
\r
628 MML_SetupEMS(); // allocate space
\r
629 MML_UseSpace (EMSpageframe,EMSpagesmapped*0x400);
\r
630 MM_MapEMS(); // map in used pages
\r
631 mminfo.EMSmem = EMSpagesmapped*0x4000l;
\r
635 // detect XMS and get upper memory blocks
\r
639 for (i = 1;i < _argc;i++)
\r
641 if ( US_CheckParm(_argv[i],ParmStrings) == 0)
\r
642 goto xmsskip; // param NOXMS
\r
645 if (MML_CheckForXMS())
\r
646 MML_SetupXMS(); // allocate as many UMBs as possible
\r
649 // allocate the misc buffer
\r
652 mmrover = mmhead; // start looking for space after low block
\r
654 MM_GetPtr (&bufferseg,BUFFERSIZE);
\r
657 //==========================================================================
\r
660 ====================
\r
664 = Frees all conventional, EMS, and XMS allocated
\r
666 ====================
\r
669 void MM_Shutdown (void)
\r
676 MML_ShutdownEMS ();
\r
677 MML_ShutdownXMS ();
\r
680 //==========================================================================
\r
683 ====================
\r
687 = Allocates an unlocked, unpurgable block
\r
689 ====================
\r
692 void MM_GetPtr (memptr *baseptr,unsigned long size)
\r
694 mmblocktype far *scan,far *lastscan,far *endscan
\r
695 ,far *purge,far *next;
\r
697 unsigned needed,startseg;
\r
699 needed = (size+15)/16; // convert size from bytes to paragraphs
\r
701 GETNEWBLOCK; // fill in start and next after a spot is found
\r
702 mmnew->length = needed;
\r
703 mmnew->useptr = baseptr;
\r
704 mmnew->attributes = BASEATTRIBUTES;
\r
706 for (search = 0; search<3; search++)
\r
709 // first search: try to allocate right after the rover, then on up
\r
710 // second search: search from the head pointer up to the rover
\r
711 // third search: compress memory, then scan from start
\r
712 if (search == 1 && mmrover == mmhead)
\r
718 lastscan = mmrover;
\r
719 scan = mmrover->next;
\r
724 scan = mmhead->next;
\r
730 scan = mmhead->next;
\r
735 startseg = lastscan->start + lastscan->length;
\r
737 while (scan != endscan)
\r
739 if (scan->start - startseg >= needed)
\r
742 // got enough space between the end of lastscan and
\r
743 // the start of scan, so throw out anything in the middle
\r
744 // and allocate the new block
\r
746 purge = lastscan->next;
\r
747 lastscan->next = mmnew;
\r
748 mmnew->start = *(unsigned *)baseptr = startseg;
\r
749 mmnew->next = scan;
\r
750 while ( purge != scan)
\r
751 { // free the purgable block
\r
752 next = purge->next;
\r
754 purge = next; // purge another if not at scan
\r
757 return; // good allocation!
\r
761 // if this block is purge level zero or locked, skip past it
\r
763 if ( (scan->attributes & LOCKBIT)
\r
764 || !(scan->attributes & PURGEBITS) )
\r
767 startseg = lastscan->start + lastscan->length;
\r
771 scan=scan->next; // look at next line
\r
776 Quit (OUT_OF_MEM_MSG,(size-mminfo.nearheap));
\r
781 //==========================================================================
\r
784 ====================
\r
788 = Allocates an unlocked, unpurgable block
\r
790 ====================
\r
793 void MM_FreePtr (memptr *baseptr)
\r
795 mmblocktype far *scan,far *last;
\r
800 if (baseptr == mmrover->useptr) // removed the last allocated block
\r
803 while (scan->useptr != baseptr && scan)
\r
810 Quit ("MM_FreePtr: Block not found!");
\r
812 last->next = scan->next;
\r
816 //==========================================================================
\r
819 =====================
\r
823 = Sets the purge level for a block (locked blocks cannot be made purgable)
\r
825 =====================
\r
828 void MM_SetPurge (memptr *baseptr, int purge)
\r
830 mmblocktype far *start;
\r
836 if (mmrover->useptr == baseptr)
\r
839 mmrover = mmrover->next;
\r
843 else if (mmrover == start)
\r
844 Quit ("MM_SetPurge: Block not found!");
\r
848 mmrover->attributes &= ~PURGEBITS;
\r
849 mmrover->attributes |= purge;
\r
852 //==========================================================================
\r
855 =====================
\r
859 = Locks / unlocks the block
\r
861 =====================
\r
864 void MM_SetLock (memptr *baseptr, boolean locked)
\r
866 mmblocktype far *start;
\r
872 if (mmrover->useptr == baseptr)
\r
875 mmrover = mmrover->next;
\r
879 else if (mmrover == start)
\r
880 Quit ("MM_SetLock: Block not found!");
\r
884 mmrover->attributes &= ~LOCKBIT;
\r
885 mmrover->attributes |= locked*LOCKBIT;
\r
888 //==========================================================================
\r
891 =====================
\r
895 = Throws out all purgable stuff and compresses movable blocks
\r
897 =====================
\r
900 void MM_SortMem (void)
\r
902 mmblocktype far *scan,far *last,far *next;
\r
903 unsigned start,length,source,dest,oldborder;
\r
907 // lock down a currently playing sound
\r
909 playing = SD_SoundPlaying ();
\r
915 playing += STARTPCSOUNDS;
\r
918 playing += STARTADLIBSOUNDS;
\r
921 MM_SetLock(&(memptr)audiosegs[playing],true);
\r
926 // oldborder = bordercolor;
\r
927 // VW_ColorBorder (15);
\r
934 last = NULL; // shut up compiler warning
\r
938 if (scan->attributes & LOCKBIT)
\r
941 // block is locked, so try to pile later blocks right after it
\r
943 start = scan->start + scan->length;
\r
947 if (scan->attributes & PURGEBITS)
\r
950 // throw out the purgable block
\r
961 // push the non purgable block on top of the last moved block
\r
963 if (scan->start != start)
\r
965 length = scan->length;
\r
966 source = scan->start;
\r
968 while (length > 0xf00)
\r
970 movedata(source,0,dest,0,0xf00*16);
\r
975 movedata(source,0,dest,0,length*16);
\r
977 scan->start = start;
\r
978 *(unsigned *)scan->useptr = start;
\r
980 start = scan->start + scan->length;
\r
985 scan = scan->next; // go to next block
\r
993 // VW_ColorBorder (oldborder);
\r
996 MM_SetLock(&(memptr)audiosegs[playing],false);
\r
1000 //==========================================================================
\r
1004 =====================
\r
1008 =====================
\r
1011 void MM_ShowMemory (void)
\r
1013 mmblocktype far *scan;
\r
1014 unsigned color,temp;
\r
1016 char scratch[80],str[10];
\r
1018 VW_SetDefaultColors();
\r
1019 VW_SetLineWidth(40);
\r
1022 VW_SetScreen (0,0);
\r
1028 //CA_OpenDebug ();
\r
1032 if (scan->attributes & PURGEBITS)
\r
1033 color = 5; // dark purple = purgable
\r
1035 color = 9; // medium blue = non purgable
\r
1036 if (scan->attributes & LOCKBIT)
\r
1037 color = 12; // red = locked
\r
1038 if (scan->start<=end)
\r
1039 Quit ("MM_ShowMemory: Memory block order currupted!");
\r
1040 end = scan->start+scan->length-1;
\r
1041 VW_Hlin(scan->start,(unsigned)end,0,color);
\r
1042 VW_Plot(scan->start,0,15);
\r
1043 if (scan->next->start > end+1)
\r
1044 VW_Hlin(end+1,scan->next->start,0,0); // black = free
\r
1047 strcpy (scratch,"Size:");
\r
1048 ltoa ((long)scan->length*16,str,10);
\r
1049 strcat (scratch,str);
\r
1050 strcat (scratch,"\tOwner:0x");
\r
1051 owner = (unsigned)scan->useptr;
\r
1052 ultoa (owner,str,16);
\r
1053 strcat (scratch,str);
\r
1054 strcat (scratch,"\n");
\r
1055 write (debughandle,scratch,strlen(scratch));
\r
1058 scan = scan->next;
\r
1061 //CA_CloseDebug ();
\r
1064 VW_SetLineWidth(64);
\r
1069 //==========================================================================
\r
1073 ======================
\r
1077 = Returns the total free space without purging
\r
1079 ======================
\r
1082 long MM_UnusedMemory (void)
\r
1085 mmblocktype far *scan;
\r
1090 while (scan->next)
\r
1092 free += scan->next->start - (scan->start + scan->length);
\r
1093 scan = scan->next;
\r
1099 //==========================================================================
\r
1103 ======================
\r
1107 = Returns the total free space with purging
\r
1109 ======================
\r
1112 long MM_TotalFree (void)
\r
1115 mmblocktype far *scan;
\r
1120 while (scan->next)
\r
1122 if ((scan->attributes&PURGEBITS) && !(scan->attributes&LOCKBIT))
\r
1123 free += scan->length;
\r
1124 free += scan->next->start - (scan->start + scan->length);
\r
1125 scan = scan->next;
\r
1131 //==========================================================================
\r
1134 =====================
\r
1138 =====================
\r
1141 void MM_BombOnError (boolean bomb)
\r
1143 bombonerror = bomb;
\r