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 "src/lib/16_mm.h"
\r
48 =============================================================================
\r
52 =============================================================================
\r
59 void (* beforesort) (void);
\r
60 void (* aftersort) (void);
\r
63 =============================================================================
\r
67 =============================================================================
\r
75 mmblocktype far mmblocks[MAXBLOCKS]
\r
76 ,far *mmhead,far *mmfree,far *mmrover,far *mmnew;
\r
78 boolean bombonerror;
\r
80 unsigned totalEMSpages,freeEMSpages,EMSpageframe,EMSpagesmapped,EMShandle;
81 unsigned int EMSVer;
\r
83 void (* XMSaddr) (void); // far pointer to XMS driver
\r
85 unsigned numUMBs,UMBbase[MAXUMBS];
87 static char *ParmStringsexmm[] = {"noems","noxms",""};
90 ======================
\r
94 = Routine from p36 of Extending DOS
\r
96 =======================
\r
99 boolean MML_CheckForEMS (void)
\r
102 char emmname[] = "EMMXXXX0";
\r
103 // mov dx,OFFSET emmname
\r
105 LEA DX, emmname //fix by andrius4669
\r
107 int 0x21 // try to open EMMXXXX0 device
\r
113 int 0x21 // get device info
\r
121 int 0x21 // get status
\r
127 int 0x21 // close handle
\r
146 ======================
\r
150 =======================
\r
153 void MML_SetupEMS (void)
\r
155 char str[80],str2[10];
\r
157 boolean errorflag=false;
\r
158 union REGS CPURegs;
\r
161 totalEMSpages = freeEMSpages = EMSpageframe = EMSpagesmapped = 0;
\r
166 int EMS_INT // make sure EMS hardware is present
\r
174 mov [EMSVer],ax // set EMSVer
\r
175 cmp al,0x32 // only work on ems 3.2 or greater
\r
178 mov ah,EMS_GETFRAME
\r
179 int EMS_INT // find the page frame address
\r
182 mov [EMSpageframe],bx
\r
184 mov ah,EMS_GETPAGES
\r
185 int EMS_INT // find out how much EMS is there
\r
188 mov [totalEMSpages],dx
\r
189 mov [freeEMSpages],bx
\r
191 jz noEMS // no EMS at all to allocate
\r
194 jle getpages // there is only 1,2,3,or 4 pages
\r
195 mov bx,4 // we can't use more than 4 pages
\r
198 mov [EMSpagesmapped],bx
\r
199 mov ah,EMS_ALLOCPAGES // allocate up to 64k of EMS
\r
211 if(errorflag==true)
\r
213 err = CPURegs.h.ah;
\r
214 strcpy(str,"MML_SetupEMS: EMS error 0x");
\r
217 printf("%s\n",str);
\r
223 ======================
\r
227 =======================
\r
230 void MML_ShutdownEMS (void)
\r
232 boolean errorflag=false;
\r
237 mov ah,EMS_FREEPAGES
\r
245 if(errorflag==true) printf("MML_ShutdownEMS: Error freeing EMS!"); //++++ add something
\r
249 ====================
\r
253 = Maps the 64k of EMS used by memory manager into the page frame
\r
254 = for general use. This only needs to be called if you are keeping
\r
255 = other things in EMS.
\r
257 ====================
\r
260 void MM_MapEMS (void)
\r
262 char str[80],str2[10];
\r
264 boolean errorflag=false;
\r
266 union REGS CPURegs;
\r
268 for (i=0;i<EMSpagesmapped;i++)
\r
273 mov bx,[i] // logical page
\r
274 mov al,bl // physical page
\r
275 mov dx,[EMShandle] // handle
\r
284 if(errorflag==true)
\r
286 err = CPURegs.h.ah;
\r
287 strcpy(str,"MM_MapEMS: EMS error 0x");
\r
290 printf("%s\n",str);
\r
296 //==========================================================================
\r
299 ======================
\r
303 = Check for XMM driver
\r
305 =======================
\r
308 boolean MML_CheckForXMS (void)
\r
310 boolean errorflag=false;
\r
316 int 0x2f // query status of installed diver
\r
322 if(errorflag==true) return false;
\r
328 ======================
\r
332 = Try to allocate all upper memory block
\r
334 =======================
\r
337 void MML_SetupXMS (void)
\r
339 unsigned base,size;
\r
345 mov [WORD PTR XMSaddr],bx
\r
346 mov [WORD PTR XMSaddr+2],es // function pointer to XMS driver
\r
351 mov ah,XMS_ALLOCUMB
\r
352 mov dx,0xffff // try for largest block possible
\r
353 call [DWORD PTR XMSaddr]
\r
357 cmp bl,0xb0 // error: smaller UMB is available
\r
360 mov ah,XMS_ALLOCUMB
\r
361 call [DWORD PTR XMSaddr] // DX holds largest available UMB
\r
363 jz done // another error...
\r
370 MML_UseSpace (base,size);
\r
371 mminfo.XMSmem += size*16;
\r
372 UMBbase[numUMBs] = base;
\r
374 if (numUMBs < MAXUMBS)
\r
380 ======================
\r
384 ======================
\r
387 void MML_ShutdownXMS (void)
\r
392 for (i=0;i<numUMBs;i++)
\r
399 call [DWORD PTR XMSaddr]
\r
404 //==========================================================================
\r
407 ======================
\r
411 = Marks a range of paragraphs as usable by the memory manager
\r
412 = This is used to mark space for the near heap, far heap, ems page frame,
\r
413 = and upper memory blocks
\r
415 ======================
\r
418 void MML_UseSpace (unsigned segstart, unsigned seglength)
\r
420 mmblocktype far *scan,far *last;
\r
424 scan = last = mmhead;
\r
425 mmrover = mmhead; // reset rover to start of memory
\r
428 // search for the block that contains the range of segments
\r
430 while (scan->start+scan->length < segstart)
\r
437 // take the given range out of the block
\r
439 oldend = scan->start + scan->length;
\r
440 extra = oldend - (segstart+seglength);
\r
443 printf("MML_UseSpace: Segment spans two blocks!");
\r
448 if (segstart == scan->start)
\r
450 last->next = scan->next; // unlink block
\r
455 scan->length = segstart-scan->start; // shorten block
\r
460 mmnew->next = scan->next;
\r
461 scan->next = mmnew;
\r
462 mmnew->start = segstart+seglength;
\r
463 mmnew->length = extra;
\r
464 mmnew->attributes = LOCKBIT;
\r
469 //==========================================================================
\r
472 ====================
\r
476 = We are out of blocks, so free a purgable block
\r
478 ====================
\r
481 void MML_ClearBlock (void)
\r
483 mmblocktype far *scan,far *last;
\r
485 scan = mmhead->next;
\r
489 if (!(scan->attributes&LOCKBIT) && (scan->attributes&PURGEBITS) )
\r
491 MM_FreePtr(scan->useptr);
\r
497 printf("MM_ClearBlock: No purgable blocks!");
\r
501 //==========================================================================
\r
504 ===================
\r
508 = Grabs all space from turbo with malloc/farmalloc
\r
509 = Allocates bufferseg misc buffer
\r
511 ===================
\r
514 void MM_Startup (void)
\r
519 unsigned segstart,seglength,endfree;
\r
521 if (mminfo.mmstarted)
\r
525 mminfo.mmstarted = true;
\r
526 mminfo.bombonerror = true;
\r
528 // set up the linked list (everything in the free list;
\r
531 mmfree = &mmblocks[0];
\r
532 for (i=0;i<MAXBLOCKS-1;i++)
\r
533 mmblocks[i].next = &mmblocks[i+1];
\r
534 mmblocks[i].next = NULL;
\r
537 // locked block of all memory until we punch out free space
\r
540 mmhead = mmnew; // this will allways be the first node
\r
542 mmnew->length = 0xffff;
\r
543 mmnew->attributes = LOCKBIT;
\r
544 mmnew->next = NULL;
\r
549 // get all available near conventional memory segments
\r
551 //---- length=coreleft();
\r
554 start = (void far *)(nearheap = malloc(length));
\r
556 length -= 16-(FP_OFF(start)&15);
\r
557 length -= SAVENEARHEAP;
\r
558 seglength = length / 16; // now in paragraphs
\r
559 segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;
\r
560 MML_UseSpace (segstart,seglength);
\r
561 mminfo.nearheap = length;
\r
564 // get all available far conventional memory segments
\r
566 //---- length=farcoreleft();
\r
569 start = farheap = _fmalloc(length);
\r
570 length -= 16-(FP_OFF(start)&15);
\r
571 length -= SAVEFARHEAP;
\r
572 seglength = length / 16; // now in paragraphs
\r
573 segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;
\r
574 MML_UseSpace (segstart,seglength);
\r
575 mminfo.farheap = length;
\r
576 mminfo.mainmem = mminfo.nearheap + mminfo.farheap;
\r
580 // detect EMS and allocate up to 64K at page frame
\r
583 for (i = 1;i < __argc;i++)
\r
585 if ( US_CheckParm(__argv[i],ParmStringsexmm) == 0)
\r
586 goto emsskip; // param NOEMS
\r
589 if (MML_CheckForEMS())
\r
591 //printf("EMS1\n");
\r
592 MML_SetupEMS(); // allocate space
\r
593 //printf("EMS2\n");
\r
594 MML_UseSpace (EMSpageframe,EMSpagesmapped*0x400);
\r
595 //printf("EMS3\n");
\r
596 MM_MapEMS(); // map in used pages
\r
597 //printf("EMS4\n");
\r
598 mminfo.EMSmem = EMSpagesmapped*0x4000l;
\r
602 // detect XMS and get upper memory blocks
\r
606 for (i = 1;i < __argc;i++)
\r
608 if ( US_CheckParm(__argv[i],ParmStringsexmm) == 0)
\r
609 goto xmsskip; // param NOXMS
\r
612 if (MML_CheckForXMS())
\r
614 // printf("XMS!\n");
\r
615 //++++ MML_SetupXMS(); // allocate as many UMBs as possible
\r
619 // allocate the misc buffer
\r
622 mmrover = mmhead; // start looking for space after low block
\r
624 MM_GetPtr (&bufferseg,BUFFERSIZE);
\r
627 //==========================================================================
\r
630 ====================
\r
634 = Frees all conventional, EMS, and XMS allocated
\r
636 ====================
\r
639 void MM_Shutdown (void)
\r
641 if (!mminfo.mmstarted)
\r
645 printf("far freed\n");
\r
647 printf("near freed\n");
\r
649 printf("huge freed\n");
\r
651 printf("EMS freed\n");
\r
652 //++++ MML_ShutdownXMS ();
653 printf("XMS freed\n");
\r
656 //==========================================================================
\r
659 ====================
\r
663 = Allocates an unlocked, unpurgable block
\r
665 ====================
\r
668 void MM_GetPtr (memptr *baseptr,dword size)
\r
670 mmblocktype far *scan,far *lastscan,far *endscan
\r
671 ,far *purge,far *next;
\r
673 unsigned needed,startseg;
\r
675 needed = (size+15)/16; // convert size from bytes to paragraphs
\r
677 GETNEWBLOCK; // fill in start and next after a spot is found
\r
678 mmnew->length = needed;
\r
679 mmnew->useptr = baseptr;
\r
680 mmnew->attributes = BASEATTRIBUTES;
\r
682 for (search = 0; search<3; search++)
\r
685 // first search: try to allocate right after the rover, then on up
\r
686 // second search: search from the head pointer up to the rover
\r
687 // third search: compress memory, then scan from start
\r
688 if (search == 1 && mmrover == mmhead)
\r
694 lastscan = mmrover;
\r
695 scan = mmrover->next;
\r
700 scan = mmhead->next;
\r
706 scan = mmhead->next;
\r
711 startseg = lastscan->start + lastscan->length;
\r
713 while (scan != endscan)
\r
715 if (scan->start - startseg >= needed)
\r
718 // got enough space between the end of lastscan and
\r
719 // the start of scan, so throw out anything in the middle
\r
720 // and allocate the new block
\r
722 purge = lastscan->next;
\r
723 lastscan->next = mmnew;
\r
724 mmnew->start = *(unsigned *)baseptr = startseg;
\r
725 mmnew->next = scan;
\r
726 while ( purge != scan)
\r
727 { // free the purgable block
\r
728 next = purge->next;
\r
730 purge = next; // purge another if not at scan
\r
733 return; // good allocation!
\r
737 // if this block is purge level zero or locked, skip past it
\r
739 if ( (scan->attributes & LOCKBIT)
\r
740 || !(scan->attributes & PURGEBITS) )
\r
743 startseg = lastscan->start + lastscan->length;
\r
747 scan=scan->next; // look at next line
\r
751 if (mminfo.bombonerror)
\r
752 printf(OUT_OF_MEM_MSG,(size-mminfo.nearheap));
\r
754 mminfo.mmerror = true;
\r
757 //==========================================================================
\r
760 ====================
\r
764 = Allocates an unlocked, unpurgable block
\r
766 ====================
\r
769 void MM_FreePtr (memptr *baseptr)
\r
771 mmblocktype far *scan,far *last;
\r
776 if (baseptr == mmrover->useptr) // removed the last allocated block
\r
779 while (scan->useptr != baseptr && scan)
\r
787 printf("MM_FreePtr: Block not found!");
\r
791 last->next = scan->next;
\r
795 //==========================================================================
\r
798 =====================
\r
802 = Sets the purge level for a block (locked blocks cannot be made purgable)
\r
804 =====================
\r
807 void MM_SetPurge (memptr *baseptr, int purge)
\r
809 mmblocktype far *start;
\r
815 if (mmrover->useptr == baseptr)
\r
818 mmrover = mmrover->next;
\r
822 else if (mmrover == start)
\r
824 printf("MM_SetPurge: Block not found!");
\r
830 mmrover->attributes &= ~PURGEBITS;
\r
831 mmrover->attributes |= purge;
\r
834 //==========================================================================
\r
837 =====================
\r
841 = Locks / unlocks the block
\r
843 =====================
\r
846 void MM_SetLock (memptr *baseptr, boolean locked)
\r
848 mmblocktype far *start;
\r
854 if (mmrover->useptr == baseptr)
\r
857 mmrover = mmrover->next;
\r
861 else if (mmrover == start)
\r
863 printf("MM_SetLock: Block not found!");
\r
869 mmrover->attributes &= ~LOCKBIT;
\r
870 mmrover->attributes |= locked*LOCKBIT;
\r
873 //==========================================================================
\r
876 =====================
\r
880 = Throws out all purgable stuff and compresses movable blocks
\r
882 =====================
\r
885 void MM_SortMem (void)
\r
887 mmblocktype far *scan,far *last,far *next;
\r
888 unsigned start,length,source,dest,oldborder;
\r
892 // lock down a currently playing sound
\r
894 /*++++ playing = SD_SoundPlaying ();
\r
900 playing += STARTPCSOUNDS;
\r
903 playing += STARTADLIBSOUNDS;
\r
906 MM_SetLock(&(memptr)audiosegs[playing],true);
\r
911 // oldborder = bordercolor;
\r
912 // VW_ColorBorder (15);
\r
919 last = NULL; // shut up compiler warning
\r
923 if (scan->attributes & LOCKBIT)
\r
926 // block is locked, so try to pile later blocks right after it
\r
928 start = scan->start + scan->length;
\r
932 if (scan->attributes & PURGEBITS)
\r
935 // throw out the purgable block
\r
946 // push the non purgable block on top of the last moved block
\r
948 if (scan->start != start)
\r
950 length = scan->length;
\r
951 source = scan->start;
\r
953 while (length > 0xf00)
\r
955 movedata(source,0,dest,0,0xf00*16);
\r
960 movedata(source,0,dest,0,length*16);
\r
962 scan->start = start;
\r
963 *(unsigned *)scan->useptr = start;
\r
965 start = scan->start + scan->length;
\r
970 scan = scan->next; // go to next block
\r
978 // VW_ColorBorder (oldborder);
\r
980 /*++++ if (playing)
\r
981 MM_SetLock(&(memptr)audiosegs[playing],false);*/
\r
985 //==========================================================================
\r
989 =====================
\r
993 =====================
\r
996 void MM_ShowMemory (void)
\r
998 mmblocktype far *scan;
\r
999 unsigned color,temp;//, i;
\r
1001 char scratch[80],str[10];
\r
1003 //**** VW_SetDefaultColors();
\r
1004 //**** VW_SetLineWidth(40);
\r
1005 //++++mh temp = bufferofs;
\r
1006 //++++mh bufferofs = 0;
\r
1007 //**** VW_SetScreen (0,0);
\r
1013 //CA_OpenDebug ();
\r
1017 if (scan->attributes & PURGEBITS)
\r
1018 color = 5; // dark purple = purgable
\r
1020 color = 9; // medium blue = non purgable
\r
1021 if (scan->attributes & LOCKBIT)
\r
1022 color = 12; // red = locked
\r
1023 if (scan->start<=end)
\r
1025 printf("MM_ShowMemory: Memory block order currupted!");
\r
1028 end = scan->start+scan->length-1;
\r
1029 //++++ VW_Hlin(scan->start,(unsigned)end,0,color);
\r
1030 //++++ VW_Plot(scan->start,0,15);
\r
1031 if (scan->next->start > end+1)
\r
1032 //++++ VW_Hlin(end+1,scan->next->start,0,0); // black = free
\r
1035 printf("Location:");
\r
1036 printf("%Fp\t", scan->start);
\r
1037 strcpy (scratch,"Size:");
\r
1038 ltoa ((long)scan->length*16,str,10);
\r
1039 strcat (scratch,str);
\r
1040 strcat (scratch,"\tOwner:0x");
\r
1041 owner = (unsigned)scan->useptr;
\r
1042 ultoa (owner,str,16);
\r
1043 strcat (scratch,str);
\r
1044 strcat (scratch,"\n");
\r
1045 //++++write (debughandle,scratch,strlen(scratch));
\r
1046 fprintf(stdout, "%s", scratch);
\r
1049 scan = scan->next;
\r
1052 //CA_CloseDebug ();
\r
1054 //++++mh IN_Ack();
\r
1055 //**** VW_SetLineWidth(64);
\r
1056 //++++mh bufferofs = temp;
\r
1060 //==========================================================================
\r
1064 ======================
\r
1068 = Returns the total free space without purging
\r
1070 ======================
\r
1073 dword MM_UnusedMemory (void)
\r
1076 mmblocktype far *scan;
\r
1081 while (scan->next)
\r
1083 free += scan->next->start - (scan->start + scan->length);
\r
1084 scan = scan->next;
\r
1087 // return free*16l;
\r
1091 //==========================================================================
\r
1095 ======================
\r
1099 = Returns the total free space with purging
\r
1101 ======================
\r
1104 dword MM_TotalFree (void)
\r
1107 mmblocktype far *scan;
\r
1112 while (scan->next)
\r
1114 if ((scan->attributes&PURGEBITS) && !(scan->attributes&LOCKBIT))
\r
1115 free += scan->length;
\r
1116 free += scan->next->start - (scan->start + scan->length);
\r
1117 scan = scan->next;
\r
1120 // return free*16l;
\r
1124 //==========================================================================
\r
1127 =====================
\r
1131 =====================
\r
1134 void MM_Report(void)
\r
1136 printf("EMM %x available\n", EMSVer);
\r
1137 printf("totalEMSpages=%u\n", totalEMSpages);
\r
1138 printf("freeEMSpages=%u\n", freeEMSpages);
\r
1139 printf("EMSpageframe=%Fp\n", EMSpageframe);
\r
1140 printf("near=%lu\n", mminfo.nearheap);
\r
1141 printf("far=%lu\n", mminfo.farheap);
\r
1142 printf("EMSmem=%lu\n", mminfo.EMSmem);
\r
1143 printf("XMSmem=%lu\n", mminfo.XMSmem);
\r
1144 printf("mainmem=%lu\n", mminfo.mainmem);
\r
1145 printf("UnusedMemory=%lu\n", MM_UnusedMemory());
\r
1146 printf("TotalFree=%lu\n", MM_TotalFree());
\r
1148 // printf("UnusedMemory=%lu kb\n", MM_UnusedMemory()/10248);
\r
1149 // printf("TotalFree=%lu kb\n", MM_TotalFree()/10248);
\r
1152 //==========================================================================
\r
1155 =====================
\r
1159 =====================
\r
1162 int MM_EMSVer(void)
\r
1167 mov ah,EMS_VERSION
\r
1174 //==========================================================================
\r
1177 =====================
\r
1181 =====================
\r
1184 void MM_BombOnError (boolean bomb)
\r
1186 mminfo.bombonerror = bomb;
\r
1189 //==========================================================================
\r
1191 ///////////////////////////////////////////////////////////////////////////
\r
1193 // US_CheckParm() - checks to see if a string matches one of a set of
\r
1194 // strings. The check is case insensitive. The routine returns the
\r
1195 // index of the string that matched, or -1 if no matches were found
\r
1197 ///////////////////////////////////////////////////////////////////////////
\r
1199 US_CheckParm(char *parm,char **strings)
\r
1205 while (!isalpha(*parm)) // Skip non-alphas
\r
1208 for (i = 0;*strings && **strings;i++)
\r
1210 for (s = *strings++,p = parm,cs = cp = 0;cs == cp;)
\r