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;
118 totalEMSpages = freeEMSpages = EMSpageframe = EMSpagesmapped = 0;
123 int EMS_INT // make sure EMS hardware is present
131 cmp al,0x32 // only work on ems 3.2 or greater
135 int EMS_INT // find the page frame address
138 mov [EMSpageframe],bx
141 int EMS_INT // find out how much EMS is there
144 mov [totalEMSpages],dx
145 mov [freeEMSpages],bx
147 jz noEMS // no EMS at all to allocate
150 jle getpages // there is only 1,2,3,or 4 pages
151 mov bx,4 // we can't use more than 4 pages
154 mov [EMSpagesmapped],bx
155 mov ah,EMS_ALLOCPAGES // allocate up to 64k of EMS
170 strcpy(str,"MML_SetupEMS: EMS error 0x");
179 ======================
183 =======================
186 void MML_ShutdownEMS (void)
188 boolean errorflag=false;
201 if(errorflag==true) printf("MML_ShutdownEMS: Error freeing EMS!"); //++++ add something
209 = Maps the 64k of EMS used by memory manager into the page frame
210 = for general use. This only needs to be called if you are keeping
211 = other things in EMS.
216 void MM_MapEMS (void)
218 char str[80],str2[10];
220 boolean errorflag=false;
224 for (i=0;i<EMSpagesmapped;i++)
229 mov bx,[i] // logical page
230 mov al,bl // physical page
231 mov dx,[EMShandle] // handle
243 strcpy(str,"MM_MapEMS: EMS error 0x");
252 //==========================================================================
255 ======================
259 = Check for XMM driver
261 =======================
264 boolean MML_CheckForXMS (void)
266 boolean errorflag=false;
272 int 0x2f // query status of installed diver
278 if(errorflag==true) return false;
284 ======================
288 = Try to allocate all upper memory block
290 =======================
293 void MML_SetupXMS (void)
301 mov [WORD PTR XMSaddr],bx
302 mov [WORD PTR XMSaddr+2],es // function pointer to XMS driver
308 mov dx,0xffff // try for largest block possible
309 call [DWORD PTR XMSaddr]
313 cmp bl,0xb0 // error: smaller UMB is available
317 call [DWORD PTR XMSaddr] // DX holds largest available UMB
319 jz done // another error...
326 MML_UseSpace (base,size);
327 mminfo.XMSmem += size*16;
328 UMBbase[numUMBs] = base;
330 if (numUMBs < MAXUMBS)
336 ======================
340 ======================
343 void MML_ShutdownXMS (void)
348 for (i=0;i<numUMBs;i++)
355 call [DWORD PTR XMSaddr]
360 //==========================================================================
363 ======================
367 = Marks a range of paragraphs as usable by the memory manager
368 = This is used to mark space for the near heap, far heap, ems page frame,
369 = and upper memory blocks
371 ======================
374 void MML_UseSpace (unsigned segstart, unsigned seglength)
376 mmblocktype far *scan,far *last;
380 scan = last = mmhead;
381 mmrover = mmhead; // reset rover to start of memory
384 // search for the block that contains the range of segments
386 while (scan->start+scan->length < segstart)
393 // take the given range out of the block
395 oldend = scan->start + scan->length;
396 extra = oldend - (segstart+seglength);
399 printf("MML_UseSpace: Segment spans two blocks!");
404 if (segstart == scan->start)
406 last->next = scan->next; // unlink block
411 scan->length = segstart-scan->start; // shorten block
416 mmnew->next = scan->next;
418 mmnew->start = segstart+seglength;
419 mmnew->length = extra;
420 mmnew->attributes = LOCKBIT;
425 //==========================================================================
432 = We are out of blocks, so free a purgable block
437 void MML_ClearBlock (void)
439 mmblocktype far *scan,far *last;
445 if (!(scan->attributes&LOCKBIT) && (scan->attributes&PURGEBITS) )
447 MM_FreePtr(scan->useptr);
453 printf("MM_ClearBlock: No purgable blocks!");
457 //==========================================================================
464 = Grabs all space from turbo with malloc/farmalloc
465 = Allocates bufferseg misc buffer
470 void MM_Startup (void)
475 unsigned segstart,seglength,endfree;
484 // set up the linked list (everything in the free list;
487 mmfree = &mmblocks[0];
488 for (i=0;i<MAXBLOCKS-1;i++)
489 mmblocks[i].next = &mmblocks[i+1];
490 mmblocks[i].next = NULL;
493 // locked block of all memory until we punch out free space
496 mmhead = mmnew; // this will allways be the first node
498 mmnew->length = 0xffff;
499 mmnew->attributes = LOCKBIT;
505 // get all available near conventional memory segments
507 //---- length=coreleft();
509 start = (void far *)(nearheap = malloc(length));
511 length -= 16-(FP_OFF(start)&15);
512 length -= SAVENEARHEAP;
513 seglength = length / 16; // now in paragraphs
514 segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;
515 MML_UseSpace (segstart,seglength);
516 mminfo.nearheap = length;
519 // get all available far conventional memory segments
521 //---- length=farcoreleft();
523 start = farheap = _fmalloc(length);
524 length -= 16-(FP_OFF(start)&15);
525 length -= SAVEFARHEAP;
526 seglength = length / 16; // now in paragraphs
527 segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;
528 MML_UseSpace (segstart,seglength);
529 mminfo.farheap = length;
530 mminfo.mainmem = mminfo.nearheap + mminfo.farheap;
534 // detect EMS and allocate up to 64K at page frame
537 for (i = 1;i < __argc;i++)
539 if ( US_CheckParm(__argv[i],ParmStringsexmm) == 0)
540 goto emsskip; // param NOEMS
543 if (MML_CheckForEMS())
546 MML_SetupEMS(); // allocate space
548 MML_UseSpace (EMSpageframe,EMSpagesmapped*0x400);
550 MM_MapEMS(); // map in used pages
552 mminfo.EMSmem = EMSpagesmapped*0x4000l;
556 // detect XMS and get upper memory blocks
560 for (i = 1;i < __argc;i++)
562 if ( US_CheckParm(__argv[i],ParmStringsexmm) == 0)
563 goto xmsskip; // param NOXMS
566 if (MML_CheckForXMS())
569 MML_SetupXMS(); // allocate as many UMBs as possible
573 // allocate the misc buffer
576 mmrover = mmhead; // start looking for space after low block
578 MM_GetPtr (&bufferseg,BUFFERSIZE);
581 //==========================================================================
588 = Frees all conventional, EMS, and XMS allocated
593 void MM_Shutdown (void)
605 //==========================================================================
612 = Allocates an unlocked, unpurgable block
617 void MM_GetPtr (memptr *baseptr,dword size)
619 mmblocktype far *scan,far *lastscan,far *endscan
620 ,far *purge,far *next;
622 unsigned needed,startseg;
624 needed = (size+15)/16; // convert size from bytes to paragraphs
626 GETNEWBLOCK; // fill in start and next after a spot is found
627 mmnew->length = needed;
628 mmnew->useptr = baseptr;
629 mmnew->attributes = BASEATTRIBUTES;
631 for (search = 0; search<3; search++)
634 // first search: try to allocate right after the rover, then on up
635 // second search: search from the head pointer up to the rover
636 // third search: compress memory, then scan from start
637 if (search == 1 && mmrover == mmhead)
644 scan = mmrover->next;
660 startseg = lastscan->start + lastscan->length;
662 while (scan != endscan)
664 if (scan->start - startseg >= needed)
667 // got enough space between the end of lastscan and
668 // the start of scan, so throw out anything in the middle
669 // and allocate the new block
671 purge = lastscan->next;
672 lastscan->next = mmnew;
673 mmnew->start = *(unsigned *)baseptr = startseg;
675 while ( purge != scan)
676 { // free the purgable block
679 purge = next; // purge another if not at scan
682 return; // good allocation!
686 // if this block is purge level zero or locked, skip past it
688 if ( (scan->attributes & LOCKBIT)
689 || !(scan->attributes & PURGEBITS) )
692 startseg = lastscan->start + lastscan->length;
696 scan=scan->next; // look at next line
701 printf(OUT_OF_MEM_MSG,(size-mminfo.nearheap));
706 //==========================================================================
713 = Allocates an unlocked, unpurgable block
718 void MM_FreePtr (memptr *baseptr)
720 mmblocktype far *scan,far *last;
725 if (baseptr == mmrover->useptr) // removed the last allocated block
728 while (scan->useptr != baseptr && scan)
736 printf("MM_FreePtr: Block not found!");
740 last->next = scan->next;
744 //==========================================================================
747 =====================
751 = Sets the purge level for a block (locked blocks cannot be made purgable)
753 =====================
756 void MM_SetPurge (memptr *baseptr, int purge)
758 mmblocktype far *start;
764 if (mmrover->useptr == baseptr)
767 mmrover = mmrover->next;
771 else if (mmrover == start)
773 printf("MM_SetPurge: Block not found!");
779 mmrover->attributes &= ~PURGEBITS;
780 mmrover->attributes |= purge;
783 //==========================================================================
786 =====================
790 = Locks / unlocks the block
792 =====================
795 void MM_SetLock (memptr *baseptr, boolean locked)
797 mmblocktype far *start;
803 if (mmrover->useptr == baseptr)
806 mmrover = mmrover->next;
810 else if (mmrover == start)
812 printf("MM_SetLock: Block not found!");
818 mmrover->attributes &= ~LOCKBIT;
819 mmrover->attributes |= locked*LOCKBIT;
822 //==========================================================================
825 =====================
829 = Throws out all purgable stuff and compresses movable blocks
831 =====================
834 void MM_SortMem (void)
836 mmblocktype far *scan,far *last,far *next;
837 unsigned start,length,source,dest,oldborder;
841 // lock down a currently playing sound
843 /*++++ playing = SD_SoundPlaying ();
849 playing += STARTPCSOUNDS;
852 playing += STARTADLIBSOUNDS;
855 MM_SetLock(&(memptr)audiosegs[playing],true);
860 // oldborder = bordercolor;
861 // VW_ColorBorder (15);
868 last = NULL; // shut up compiler warning
872 if (scan->attributes & LOCKBIT)
875 // block is locked, so try to pile later blocks right after it
877 start = scan->start + scan->length;
881 if (scan->attributes & PURGEBITS)
884 // throw out the purgable block
895 // push the non purgable block on top of the last moved block
897 if (scan->start != start)
899 length = scan->length;
900 source = scan->start;
902 while (length > 0xf00)
904 movedata(source,0,dest,0,0xf00*16);
909 movedata(source,0,dest,0,length*16);
912 *(unsigned *)scan->useptr = start;
914 start = scan->start + scan->length;
919 scan = scan->next; // go to next block
927 // VW_ColorBorder (oldborder);
930 MM_SetLock(&(memptr)audiosegs[playing],false);*/
934 //==========================================================================
938 =====================
942 =====================
945 void MM_ShowMemory (void)
947 mmblocktype far *scan;
948 unsigned color,temp;//, i;
950 char scratch[80],str[10];
952 //**** VW_SetDefaultColors();
953 //**** VW_SetLineWidth(40);
954 //++++mh temp = bufferofs;
955 //++++mh bufferofs = 0;
956 //**** VW_SetScreen (0,0);
966 if (scan->attributes & PURGEBITS)
967 color = 5; // dark purple = purgable
969 color = 9; // medium blue = non purgable
970 if (scan->attributes & LOCKBIT)
971 color = 12; // red = locked
972 if (scan->start<=end)
974 printf("MM_ShowMemory: Memory block order currupted!");
977 end = scan->start+scan->length-1;
978 //++++ VW_Hlin(scan->start,(unsigned)end,0,color);
979 //++++ VW_Plot(scan->start,0,15);
980 if (scan->next->start > end+1)
981 //++++ VW_Hlin(end+1,scan->next->start,0,0); // black = free
985 printf("%Fp\t", scan->start);
986 strcpy (scratch,"Size:");
987 ltoa ((long)scan->length*16,str,10);
988 strcat (scratch,str);
989 strcat (scratch,"\tOwner:0x");
990 owner = (unsigned)scan->useptr;
991 ultoa (owner,str,16);
992 strcat (scratch,str);
993 strcat (scratch,"\n");
994 //++++write (debughandle,scratch,strlen(scratch));
995 fprintf(stdout, "%s", scratch);
1004 //**** VW_SetLineWidth(64);
1005 //++++mh bufferofs = temp;
1009 //==========================================================================
1013 ======================
1017 = Returns the total free space without purging
1019 ======================
1022 long MM_UnusedMemory (void)
1025 mmblocktype far *scan;
1032 free += scan->next->start - (scan->start + scan->length);
1039 //==========================================================================
1043 ======================
1047 = Returns the total free space with purging
1049 ======================
1052 long MM_TotalFree (void)
1055 mmblocktype far *scan;
1062 if ((scan->attributes&PURGEBITS) && !(scan->attributes&LOCKBIT))
1063 free += scan->length;
1064 free += scan->next->start - (scan->start + scan->length);
1071 //==========================================================================
1074 =====================
1078 =====================
1081 void MM_BombOnError (boolean bomb)
1086 ///////////////////////////////////////////////////////////////////////////
1088 // US_CheckParm() - checks to see if a string matches one of a set of
1089 // strings. The check is case insensitive. The routine returns the
1090 // index of the string that matched, or -1 if no matches were found
1092 ///////////////////////////////////////////////////////////////////////////
1094 US_CheckParm(char *parm,char **strings)
1100 while (!isalpha(*parm)) // Skip non-alphas
1103 for (i = 0;*strings && **strings;i++)
1105 for (s = *strings++,p = parm,cs = cp = 0;cs == cp;)