1 /* Catacomb Armageddon Source Code
2 * Copyright (C) 1993-2014 Flat Rock Software
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 =============================================================================
24 ID software memory manager
25 --------------------------
27 Primary coder: John Carmack
31 Quit (char *error) function
36 MM_SizePtr to change the size of a given pointer
38 Multiple purge levels utilized
40 EMS / XMS unmanaged routines
42 =============================================================================
45 #include "src/lib/16_mm.h"
48 =============================================================================
52 =============================================================================
55 #define LOCKBIT 0x80 // if set in attributes, block cannot be moved
56 #define PURGEBITS 3 // 0-3 level, 0= unpurgable, 3= purge first
57 #define PURGEMASK 0xfffc
58 #define BASEATTRIBUTES 0 // unlocked, non purgable
62 typedef struct mmblockstruct
64 unsigned start,length;
66 memptr *useptr; // pointer to the segment start
67 struct mmblockstruct far *next;
71 //#define GETNEWBLOCK {if(!(mmnew=mmfree))Quit("MM_GETNEWBLOCK: No free blocks!");mmfree=mmfree->next;}
72 #define GETNEWBLOCK {if(!mmfree)MML_ClearBlock();mmnew=mmfree;mmfree=mmfree->next;}
73 #define FREEBLOCK(x) {*x->useptr=NULL;x->next=mmfree;mmfree=x;}
76 =============================================================================
80 =============================================================================
87 void (* beforesort) (void);
88 void (* aftersort) (void);
91 =============================================================================
95 =============================================================================
104 mmblocktype far mmblocks[MAXBLOCKS]
105 ,far *mmhead,far *mmfree,far *mmrover,far *mmnew;
109 unsigned totalEMSpages,freeEMSpages,EMSpageframe,EMSpagesmapped,EMShandle;
111 void (* XMSaddr) (void); // far pointer to XMS driver
113 unsigned numUMBs,UMBbase[MAXUMBS];
117 ======================
121 = Routine from p36 of Extending DOS
123 =======================
126 boolean MML_CheckForEMS (void)
129 char emmname[] = "EMMXXXX0";
130 // mov dx,OFFSET emmname
132 LEA DX, emmname //fix by andrius4669
134 int 0x21 // try to open EMMXXXX0 device
140 int 0x21 // get device info
148 int 0x21 // get status
154 int 0x21 // close handle
173 ======================
177 =======================
180 void MML_SetupEMS (void)
182 char str[80],str2[10];
184 boolean errorflag=false;
187 totalEMSpages = freeEMSpages = EMSpageframe = EMSpagesmapped = 0;
192 int EMS_INT // make sure EMS hardware is present
200 cmp al,0x32 // only work on ems 3.2 or greater
204 int EMS_INT // find the page frame address
207 mov [EMSpageframe],bx
210 int EMS_INT // find out how much EMS is there
213 mov [totalEMSpages],dx
214 mov [freeEMSpages],bx
216 jz noEMS // no EMS at all to allocate
219 jle getpages // there is only 1,2,3,or 4 pages
220 mov bx,4 // we can't use more than 4 pages
223 mov [EMSpagesmapped],bx
224 mov ah,EMS_ALLOCPAGES // allocate up to 64k of EMS
239 strcpy(str,"MML_SetupEMS: EMS error 0x");
248 ======================
252 =======================
255 void MML_ShutdownEMS (void)
257 boolean errorflag=false;
270 if(errorflag==true) printf("MML_ShutdownEMS: Error freeing EMS!"); //++++ add something
278 = Maps the 64k of EMS used by memory manager into the page frame
279 = for general use. This only needs to be called if you are keeping
280 = other things in EMS.
285 void MM_MapEMS (void)
287 char str[80],str2[10];
289 boolean errorflag=false;
293 for (i=0;i<EMSpagesmapped;i++)
298 mov bx,[i] // logical page
299 mov al,bl // physical page
300 mov dx,[EMShandle] // handle
312 strcpy(str,"MM_MapEMS: EMS error 0x");
321 //==========================================================================
324 ======================
328 = Check for XMM driver
330 =======================
333 boolean MML_CheckForXMS (void)
335 boolean errorflag=false;
341 int 0x2f // query status of installed diver
347 if(errorflag==true) return false;
353 ======================
357 = Try to allocate all upper memory block
359 =======================
362 void MML_SetupXMS (void)
370 mov [WORD PTR XMSaddr],bx
371 mov [WORD PTR XMSaddr+2],es // function pointer to XMS driver
377 mov dx,0xffff // try for largest block possible
378 call [DWORD PTR XMSaddr]
382 cmp bl,0xb0 // error: smaller UMB is available
386 call [DWORD PTR XMSaddr] // DX holds largest available UMB
388 jz done // another error...
395 MML_UseSpace (base,size);
396 mminfo.XMSmem += size*16;
397 UMBbase[numUMBs] = base;
399 if (numUMBs < MAXUMBS)
405 ======================
409 ======================
412 void MML_ShutdownXMS (void)
417 for (i=0;i<numUMBs;i++)
424 call [DWORD PTR XMSaddr]
429 //==========================================================================
432 ======================
436 = Marks a range of paragraphs as usable by the memory manager
437 = This is used to mark space for the near heap, far heap, ems page frame,
438 = and upper memory blocks
440 ======================
443 void MML_UseSpace (unsigned segstart, unsigned seglength)
445 mmblocktype far *scan,far *last;
449 scan = last = mmhead;
450 mmrover = mmhead; // reset rover to start of memory
453 // search for the block that contains the range of segments
455 while (scan->start+scan->length < segstart)
462 // take the given range out of the block
464 oldend = scan->start + scan->length;
465 extra = oldend - (segstart+seglength);
468 printf("MML_UseSpace: Segment spans two blocks!");
473 if (segstart == scan->start)
475 last->next = scan->next; // unlink block
480 scan->length = segstart-scan->start; // shorten block
485 mmnew->next = scan->next;
487 mmnew->start = segstart+seglength;
488 mmnew->length = extra;
489 mmnew->attributes = LOCKBIT;
494 //==========================================================================
501 = We are out of blocks, so free a purgable block
506 void MML_ClearBlock (void)
508 mmblocktype far *scan,far *last;
514 if (!(scan->attributes&LOCKBIT) && (scan->attributes&PURGEBITS) )
516 MM_FreePtr(scan->useptr);
522 printf("MM_ClearBlock: No purgable blocks!");
526 //==========================================================================
533 = Grabs all space from turbo with malloc/farmalloc
534 = Allocates bufferseg misc buffer
539 static char *ParmStringsexmm[] = {"noems","noxms",""};
541 void MM_Startup (void)
546 unsigned segstart,seglength,endfree;
555 // set up the linked list (everything in the free list;
558 mmfree = &mmblocks[0];
559 for (i=0;i<MAXBLOCKS-1;i++)
560 mmblocks[i].next = &mmblocks[i+1];
561 mmblocks[i].next = NULL;
564 // locked block of all memory until we punch out free space
567 mmhead = mmnew; // this will allways be the first node
569 mmnew->length = 0xffff;
570 mmnew->attributes = LOCKBIT;
576 // get all available near conventional memory segments
578 //---- length=coreleft();
580 start = (void far *)(nearheap = malloc(length));
582 length -= 16-(FP_OFF(start)&15);
583 length -= SAVENEARHEAP;
584 seglength = length / 16; // now in paragraphs
585 segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;
586 MML_UseSpace (segstart,seglength);
587 mminfo.nearheap = length;
590 // get all available far conventional memory segments
592 //---- length=farcoreleft();
594 start = farheap = _fmalloc(length);
595 length -= 16-(FP_OFF(start)&15);
596 length -= SAVEFARHEAP;
597 seglength = length / 16; // now in paragraphs
598 segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;
599 MML_UseSpace (segstart,seglength);
600 mminfo.farheap = length;
601 mminfo.mainmem = mminfo.nearheap + mminfo.farheap;
605 // detect EMS and allocate up to 64K at page frame
608 for (i = 1;i < __argc;i++)
610 if ( US_CheckParm(__argv[i],ParmStringsexmm) == 0)
611 goto emsskip; // param NOEMS
614 if (MML_CheckForEMS())
616 MML_SetupEMS(); // allocate space
617 MML_UseSpace (EMSpageframe,EMSpagesmapped*0x400);
618 MM_MapEMS(); // map in used pages
619 mminfo.EMSmem = EMSpagesmapped*0x4000l;
623 // detect XMS and get upper memory blocks
627 for (i = 1;i < __argc;i++)
629 if ( US_CheckParm(__argv[i],ParmStringsexmm) == 0)
630 goto xmsskip; // param NOXMS
633 if (MML_CheckForXMS())
634 MML_SetupXMS(); // allocate as many UMBs as possible
637 // allocate the misc buffer
640 mmrover = mmhead; // start looking for space after low block
642 MM_GetPtr (&bufferseg,BUFFERSIZE);
645 //==========================================================================
652 = Frees all conventional, EMS, and XMS allocated
657 void MM_Shutdown (void)
669 //==========================================================================
676 = Allocates an unlocked, unpurgable block
681 void MM_GetPtr (memptr *baseptr,dword size)
683 mmblocktype far *scan,far *lastscan,far *endscan
684 ,far *purge,far *next;
686 unsigned needed,startseg;
688 needed = (size+15)/16; // convert size from bytes to paragraphs
690 GETNEWBLOCK; // fill in start and next after a spot is found
691 mmnew->length = needed;
692 mmnew->useptr = baseptr;
693 mmnew->attributes = BASEATTRIBUTES;
695 for (search = 0; search<3; search++)
698 // first search: try to allocate right after the rover, then on up
699 // second search: search from the head pointer up to the rover
700 // third search: compress memory, then scan from start
701 if (search == 1 && mmrover == mmhead)
708 scan = mmrover->next;
724 startseg = lastscan->start + lastscan->length;
726 while (scan != endscan)
728 if (scan->start - startseg >= needed)
731 // got enough space between the end of lastscan and
732 // the start of scan, so throw out anything in the middle
733 // and allocate the new block
735 purge = lastscan->next;
736 lastscan->next = mmnew;
737 mmnew->start = *(unsigned *)baseptr = startseg;
739 while ( purge != scan)
740 { // free the purgable block
743 purge = next; // purge another if not at scan
746 return; // good allocation!
750 // if this block is purge level zero or locked, skip past it
752 if ( (scan->attributes & LOCKBIT)
753 || !(scan->attributes & PURGEBITS) )
756 startseg = lastscan->start + lastscan->length;
760 scan=scan->next; // look at next line
765 printf(OUT_OF_MEM_MSG,(size-mminfo.nearheap));
770 //==========================================================================
777 = Allocates an unlocked, unpurgable block
782 void MM_FreePtr (memptr *baseptr)
784 mmblocktype far *scan,far *last;
789 if (baseptr == mmrover->useptr) // removed the last allocated block
792 while (scan->useptr != baseptr && scan)
800 printf("MM_FreePtr: Block not found!");
804 last->next = scan->next;
808 //==========================================================================
811 =====================
815 = Sets the purge level for a block (locked blocks cannot be made purgable)
817 =====================
820 void MM_SetPurge (memptr *baseptr, int purge)
822 mmblocktype far *start;
828 if (mmrover->useptr == baseptr)
831 mmrover = mmrover->next;
835 else if (mmrover == start)
837 printf("MM_SetPurge: Block not found!");
843 mmrover->attributes &= ~PURGEBITS;
844 mmrover->attributes |= purge;
847 //==========================================================================
850 =====================
854 = Locks / unlocks the block
856 =====================
859 void MM_SetLock (memptr *baseptr, boolean locked)
861 mmblocktype far *start;
867 if (mmrover->useptr == baseptr)
870 mmrover = mmrover->next;
874 else if (mmrover == start)
876 printf("MM_SetLock: Block not found!");
882 mmrover->attributes &= ~LOCKBIT;
883 mmrover->attributes |= locked*LOCKBIT;
886 //==========================================================================
889 =====================
893 = Throws out all purgable stuff and compresses movable blocks
895 =====================
898 void MM_SortMem (void)
900 mmblocktype far *scan,far *last,far *next;
901 unsigned start,length,source,dest,oldborder;
905 // lock down a currently playing sound
907 /*++++ playing = SD_SoundPlaying ();
913 playing += STARTPCSOUNDS;
916 playing += STARTADLIBSOUNDS;
919 MM_SetLock(&(memptr)audiosegs[playing],true);
924 // oldborder = bordercolor;
925 // VW_ColorBorder (15);
932 last = NULL; // shut up compiler warning
936 if (scan->attributes & LOCKBIT)
939 // block is locked, so try to pile later blocks right after it
941 start = scan->start + scan->length;
945 if (scan->attributes & PURGEBITS)
948 // throw out the purgable block
959 // push the non purgable block on top of the last moved block
961 if (scan->start != start)
963 length = scan->length;
964 source = scan->start;
966 while (length > 0xf00)
968 movedata(source,0,dest,0,0xf00*16);
973 movedata(source,0,dest,0,length*16);
976 *(unsigned *)scan->useptr = start;
978 start = scan->start + scan->length;
983 scan = scan->next; // go to next block
991 // VW_ColorBorder (oldborder);
994 MM_SetLock(&(memptr)audiosegs[playing],false);*/
998 //==========================================================================
1002 =====================
1006 =====================
1009 void MM_ShowMemory (void)
1011 mmblocktype far *scan;
1012 unsigned color,temp;
1014 char scratch[80],str[10];
1016 //**** VW_SetDefaultColors();
1017 //**** VW_SetLineWidth(40);
1018 //++++mh temp = bufferofs;
1019 //++++mh bufferofs = 0;
1020 //**** VW_SetScreen (0,0);
1030 if (scan->attributes & PURGEBITS)
1031 color = 5; // dark purple = purgable
1033 color = 9; // medium blue = non purgable
1034 if (scan->attributes & LOCKBIT)
1035 color = 12; // red = locked
1036 if (scan->start<=end)
1038 printf("MM_ShowMemory: Memory block order currupted!");
1041 end = scan->start+scan->length-1;
1042 //**** VW_Hlin(scan->start,(unsigned)end,0,color);
1043 //**** VW_Plot(scan->start,0,15);
1044 //**** if (scan->next->start > end+1)
1045 //**** VW_Hlin(end+1,scan->next->start,0,0); // black = free
1048 strcpy (scratch,"Size:");
1049 ltoa ((long)scan->length*16,str,10);
1050 strcat (scratch,str);
1051 strcat (scratch,"\tOwner:0x");
1052 owner = (unsigned)scan->useptr;
1053 ultoa (owner,str,16);
1054 strcat (scratch,str);
1055 strcat (scratch,"\n");
1056 //++++write (debughandle,scratch,strlen(scratch));
1057 printf("%s\n", scratch);
1066 //**** VW_SetLineWidth(64);
1067 //++++mh bufferofs = temp;
1071 //==========================================================================
1075 ======================
1079 = Returns the total free space without purging
1081 ======================
1084 long MM_UnusedMemory (void)
1087 mmblocktype far *scan;
1094 free += scan->next->start - (scan->start + scan->length);
1101 //==========================================================================
1105 ======================
1109 = Returns the total free space with purging
1111 ======================
1114 long MM_TotalFree (void)
1117 mmblocktype far *scan;
1124 if ((scan->attributes&PURGEBITS) && !(scan->attributes&LOCKBIT))
1125 free += scan->length;
1126 free += scan->next->start - (scan->start + scan->length);
1133 //==========================================================================
1136 =====================
1140 =====================
1143 void MM_BombOnError (boolean bomb)
1148 ///////////////////////////////////////////////////////////////////////////
1150 // US_CheckParm() - checks to see if a string matches one of a set of
1151 // strings. The check is case insensitive. The routine returns the
1152 // index of the string that matched, or -1 if no matches were found
1154 ///////////////////////////////////////////////////////////////////////////
1156 US_CheckParm(char *parm,char **strings)
1162 while (!isalpha(*parm)) // Skip non-alphas
1165 for (i = 0;*strings && **strings;i++)
1167 for (s = *strings++,p = parm,cs = cp = 0;cs == cp;)