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 = Routine from p36 of Extending DOS
54 =======================
57 boolean MML_CheckForEMS (void)
60 char emmname[] = "EMMXXXX0";
61 // mov dx,OFFSET emmname
63 LEA DX, emmname //fix by andrius4669
65 int 0x21 // try to open EMMXXXX0 device
71 int 0x21 // get device info
79 int 0x21 // get status
85 int 0x21 // close handle
104 ======================
108 =======================
111 void MML_SetupEMS (void)
113 char str[80],str2[10];
115 boolean errorflag=false;
119 totalEMSpages = freeEMSpages = EMSpageframe = EMSpagesmapped = 0;
124 int EMS_INT // make sure EMS hardware is present
132 mov [EMSVer],ax // set EMSVer
133 cmp al,0x32 // only work on ems 3.2 or greater
137 int EMS_INT // find the page frame address
140 mov [EMSpageframe],bx
143 int EMS_INT // find out how much EMS is there
146 mov [totalEMSpages],dx
147 mov [freeEMSpages],bx
149 jz noEMS // no EMS at all to allocate
152 jle getpages // there is only 1,2,3,or 4 pages
153 mov bx,4 // we can't use more than 4 pages
156 mov [EMSpagesmapped],bx
157 mov ah,EMS_ALLOCPAGES // allocate up to 64k of EMS
172 strcpy(str,"MML_SetupEMS: EMS error 0x");
181 ======================
185 =======================
188 void MML_ShutdownEMS (void)
190 boolean errorflag=false;
203 if(errorflag==true) printf("MML_ShutdownEMS: Error freeing EMS!"); //++++ add something
211 = Maps the 64k of EMS used by memory manager into the page frame
212 = for general use. This only needs to be called if you are keeping
213 = other things in EMS.
218 void MM_MapEMS (void)
220 char str[80],str2[10];
222 boolean errorflag=false;
226 for (i=0;i<EMSpagesmapped;i++)
231 mov bx,[i] // logical page
232 mov al,bl // physical page
233 mov dx,[EMShandle] // handle
245 strcpy(str,"MM_MapEMS: EMS error 0x");
254 //==========================================================================
257 ======================
261 = Check for XMM driver
263 =======================
266 boolean MML_CheckForXMS (void)
268 boolean errorflag=false;
274 int 0x2f // query status of installed diver
280 if(errorflag==true) return false;
286 ======================
290 = Try to allocate all upper memory block
292 =======================
295 void MML_SetupXMS (void)
303 mov [WORD PTR XMSaddr],bx
304 mov [WORD PTR XMSaddr+2],es // function pointer to XMS driver
310 mov dx,0xffff // try for largest block possible
311 call [DWORD PTR XMSaddr]
315 cmp bl,0xb0 // error: smaller UMB is available
319 call [DWORD PTR XMSaddr] // DX holds largest available UMB
321 jz done // another error...
328 MML_UseSpace (base,size);
329 mminfo.XMSmem += size*16;
330 UMBbase[numUMBs] = base;
332 if (numUMBs < MAXUMBS)
338 ======================
342 ======================
345 void MML_ShutdownXMS (void)
350 for (i=0;i<numUMBs;i++)
357 call [DWORD PTR XMSaddr]
362 //==========================================================================
365 ======================
369 = Marks a range of paragraphs as usable by the memory manager
370 = This is used to mark space for the near heap, far heap, ems page frame,
371 = and upper memory blocks
373 ======================
376 void MML_UseSpace (unsigned segstart, unsigned seglength)
378 mmblocktype far *scan,far *last;
382 scan = last = mmhead;
383 mmrover = mmhead; // reset rover to start of memory
386 // search for the block that contains the range of segments
388 while (scan->start+scan->length < segstart)
395 // take the given range out of the block
397 oldend = scan->start + scan->length;
398 extra = oldend - (segstart+seglength);
401 printf("MML_UseSpace: Segment spans two blocks!");
406 if (segstart == scan->start)
408 last->next = scan->next; // unlink block
413 scan->length = segstart-scan->start; // shorten block
418 mmnew->next = scan->next;
420 mmnew->start = segstart+seglength;
421 mmnew->length = extra;
422 mmnew->attributes = LOCKBIT;
427 //==========================================================================
434 = We are out of blocks, so free a purgable block
439 void MML_ClearBlock (void)
441 mmblocktype far *scan,far *last;
447 if (!(scan->attributes&LOCKBIT) && (scan->attributes&PURGEBITS) )
449 MM_FreePtr(scan->useptr);
455 printf("MM_ClearBlock: No purgable blocks!");
459 //==========================================================================
466 = Grabs all space from turbo with malloc/farmalloc
467 = Allocates bufferseg misc buffer
472 void MM_Startup (void)
477 unsigned segstart,seglength,endfree;
486 // set up the linked list (everything in the free list;
489 mmfree = &mmblocks[0];
490 for (i=0;i<MAXBLOCKS-1;i++)
491 mmblocks[i].next = &mmblocks[i+1];
492 mmblocks[i].next = NULL;
495 // locked block of all memory until we punch out free space
498 mmhead = mmnew; // this will allways be the first node
500 mmnew->length = 0xffff;
501 mmnew->attributes = LOCKBIT;
507 // get all available near conventional memory segments
509 //---- length=coreleft();
511 start = (void far *)(nearheap = malloc(length));
513 length -= 16-(FP_OFF(start)&15);
514 length -= SAVENEARHEAP;
515 seglength = length / 16; // now in paragraphs
516 segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;
517 MML_UseSpace (segstart,seglength);
518 mminfo.nearheap = length;
521 // get all available far conventional memory segments
523 //---- length=farcoreleft();
525 start = farheap = _fmalloc(length);
526 length -= 16-(FP_OFF(start)&15);
527 length -= SAVEFARHEAP;
528 seglength = length / 16; // now in paragraphs
529 segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;
530 MML_UseSpace (segstart,seglength);
531 mminfo.farheap = length;
532 mminfo.mainmem = mminfo.nearheap + mminfo.farheap;
536 // detect EMS and allocate up to 64K at page frame
539 for (i = 1;i < __argc;i++)
541 if ( US_CheckParm(__argv[i],ParmStringsexmm) == 0)
542 goto emsskip; // param NOEMS
545 if (MML_CheckForEMS())
548 MML_SetupEMS(); // allocate space
550 MML_UseSpace (EMSpageframe,EMSpagesmapped*0x400);
552 MM_MapEMS(); // map in used pages
554 mminfo.EMSmem = EMSpagesmapped*0x4000l;
558 // detect XMS and get upper memory blocks
562 for (i = 1;i < __argc;i++)
564 if ( US_CheckParm(__argv[i],ParmStringsexmm) == 0)
565 goto xmsskip; // param NOXMS
568 if (MML_CheckForXMS())
571 MML_SetupXMS(); // allocate as many UMBs as possible
575 // allocate the misc buffer
578 mmrover = mmhead; // start looking for space after low block
580 MM_GetPtr (&bufferseg,BUFFERSIZE);
583 //==========================================================================
590 = Frees all conventional, EMS, and XMS allocated
595 void MM_Shutdown (void)
607 //==========================================================================
614 = Allocates an unlocked, unpurgable block
619 void MM_GetPtr (memptr *baseptr,dword size)
621 mmblocktype far *scan,far *lastscan,far *endscan
622 ,far *purge,far *next;
624 unsigned needed,startseg;
626 needed = (size+15)/16; // convert size from bytes to paragraphs
628 GETNEWBLOCK; // fill in start and next after a spot is found
629 mmnew->length = needed;
630 mmnew->useptr = baseptr;
631 mmnew->attributes = BASEATTRIBUTES;
633 for (search = 0; search<3; search++)
636 // first search: try to allocate right after the rover, then on up
637 // second search: search from the head pointer up to the rover
638 // third search: compress memory, then scan from start
639 if (search == 1 && mmrover == mmhead)
646 scan = mmrover->next;
662 startseg = lastscan->start + lastscan->length;
664 while (scan != endscan)
666 if (scan->start - startseg >= needed)
669 // got enough space between the end of lastscan and
670 // the start of scan, so throw out anything in the middle
671 // and allocate the new block
673 purge = lastscan->next;
674 lastscan->next = mmnew;
675 mmnew->start = *(unsigned *)baseptr = startseg;
677 while ( purge != scan)
678 { // free the purgable block
681 purge = next; // purge another if not at scan
684 return; // good allocation!
688 // if this block is purge level zero or locked, skip past it
690 if ( (scan->attributes & LOCKBIT)
691 || !(scan->attributes & PURGEBITS) )
694 startseg = lastscan->start + lastscan->length;
698 scan=scan->next; // look at next line
703 printf(OUT_OF_MEM_MSG,(size-mminfo.nearheap));
708 //==========================================================================
715 = Allocates an unlocked, unpurgable block
720 void MM_FreePtr (memptr *baseptr)
722 mmblocktype far *scan,far *last;
727 if (baseptr == mmrover->useptr) // removed the last allocated block
730 while (scan->useptr != baseptr && scan)
738 printf("MM_FreePtr: Block not found!");
742 last->next = scan->next;
746 //==========================================================================
749 =====================
753 = Sets the purge level for a block (locked blocks cannot be made purgable)
755 =====================
758 void MM_SetPurge (memptr *baseptr, int purge)
760 mmblocktype far *start;
766 if (mmrover->useptr == baseptr)
769 mmrover = mmrover->next;
773 else if (mmrover == start)
775 printf("MM_SetPurge: Block not found!");
781 mmrover->attributes &= ~PURGEBITS;
782 mmrover->attributes |= purge;
785 //==========================================================================
788 =====================
792 = Locks / unlocks the block
794 =====================
797 void MM_SetLock (memptr *baseptr, boolean locked)
799 mmblocktype far *start;
805 if (mmrover->useptr == baseptr)
808 mmrover = mmrover->next;
812 else if (mmrover == start)
814 printf("MM_SetLock: Block not found!");
820 mmrover->attributes &= ~LOCKBIT;
821 mmrover->attributes |= locked*LOCKBIT;
824 //==========================================================================
827 =====================
831 = Throws out all purgable stuff and compresses movable blocks
833 =====================
836 void MM_SortMem (void)
838 mmblocktype far *scan,far *last,far *next;
839 unsigned start,length,source,dest,oldborder;
843 // lock down a currently playing sound
845 /*++++ playing = SD_SoundPlaying ();
851 playing += STARTPCSOUNDS;
854 playing += STARTADLIBSOUNDS;
857 MM_SetLock(&(memptr)audiosegs[playing],true);
862 // oldborder = bordercolor;
863 // VW_ColorBorder (15);
870 last = NULL; // shut up compiler warning
874 if (scan->attributes & LOCKBIT)
877 // block is locked, so try to pile later blocks right after it
879 start = scan->start + scan->length;
883 if (scan->attributes & PURGEBITS)
886 // throw out the purgable block
897 // push the non purgable block on top of the last moved block
899 if (scan->start != start)
901 length = scan->length;
902 source = scan->start;
904 while (length > 0xf00)
906 movedata(source,0,dest,0,0xf00*16);
911 movedata(source,0,dest,0,length*16);
914 *(unsigned *)scan->useptr = start;
916 start = scan->start + scan->length;
921 scan = scan->next; // go to next block
929 // VW_ColorBorder (oldborder);
932 MM_SetLock(&(memptr)audiosegs[playing],false);*/
936 //==========================================================================
940 =====================
944 =====================
947 void MM_ShowMemory (void)
949 mmblocktype far *scan;
950 unsigned color,temp;//, i;
952 char scratch[80],str[10];
954 //**** VW_SetDefaultColors();
955 //**** VW_SetLineWidth(40);
956 //++++mh temp = bufferofs;
957 //++++mh bufferofs = 0;
958 //**** VW_SetScreen (0,0);
968 if (scan->attributes & PURGEBITS)
969 color = 5; // dark purple = purgable
971 color = 9; // medium blue = non purgable
972 if (scan->attributes & LOCKBIT)
973 color = 12; // red = locked
974 if (scan->start<=end)
976 printf("MM_ShowMemory: Memory block order currupted!");
979 end = scan->start+scan->length-1;
980 //++++ VW_Hlin(scan->start,(unsigned)end,0,color);
981 //++++ VW_Plot(scan->start,0,15);
982 if (scan->next->start > end+1)
983 //++++ VW_Hlin(end+1,scan->next->start,0,0); // black = free
987 printf("%Fp\t", scan->start);
988 strcpy (scratch,"Size:");
989 ltoa ((long)scan->length*16,str,10);
990 strcat (scratch,str);
991 strcat (scratch,"\tOwner:0x");
992 owner = (unsigned)scan->useptr;
993 ultoa (owner,str,16);
994 strcat (scratch,str);
995 strcat (scratch,"\n");
996 //++++write (debughandle,scratch,strlen(scratch));
997 fprintf(stdout, "%s", scratch);
1006 //**** VW_SetLineWidth(64);
1007 //++++mh bufferofs = temp;
1011 //==========================================================================
1015 ======================
1019 = Returns the total free space without purging
1021 ======================
1024 dword MM_UnusedMemory (void)
1027 mmblocktype far *scan;
1034 free += scan->next->start - (scan->start + scan->length);
1041 //==========================================================================
1045 ======================
1049 = Returns the total free space with purging
1051 ======================
1054 dword MM_TotalFree (void)
1057 mmblocktype far *scan;
1064 if ((scan->attributes&PURGEBITS) && !(scan->attributes&LOCKBIT))
1065 free += scan->length;
1066 free += scan->next->start - (scan->start + scan->length);
1073 //==========================================================================
1076 =====================
1080 =====================
1083 void MM_Report(void)
1085 printf("EMM %x available\n", EMSVer);
1086 printf("totalEMSpages=%u\n", totalEMSpages);
1087 printf("freeEMSpages=%u\n", freeEMSpages);
1088 printf("EMSpageframe=%Fp\n", EMSpageframe);
1089 printf("UnusedMemory=%lu\n", MM_UnusedMemory());
1090 printf("TotalFree=%lu\n", MM_TotalFree());
1093 //==========================================================================
1096 =====================
1100 =====================
1103 int MM_EMSVer(void)
\r
1108 mov ah,EMS_VERSION
\r
1115 //==========================================================================
1118 =====================
1122 =====================
1125 void MM_BombOnError (boolean bomb)
1130 //==========================================================================
1132 ///////////////////////////////////////////////////////////////////////////
1134 // US_CheckParm() - checks to see if a string matches one of a set of
1135 // strings. The check is case insensitive. The routine returns the
1136 // index of the string that matched, or -1 if no matches were found
1138 ///////////////////////////////////////////////////////////////////////////
1140 US_CheckParm(char *parm,char **strings)
1146 while (!isalpha(*parm)) // Skip non-alphas
1149 for (i = 0;*strings && **strings;i++)
1151 for (s = *strings++,p = parm,cs = cp = 0;cs == cp;)