1 /* Catacomb Apocalypse 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 =============================================================================
46 Open Watcom port by sparky4
49 #include "src/lib/16_mm.h"
52 =============================================================================
56 =============================================================================
59 void (* beforesort) (void);
60 void (* aftersort) (void);
61 void (* XMSaddr) (void); // far pointer to XMS driver
64 =============================================================================
68 =============================================================================
71 static char *ParmStringsexmm[] = {"noems","noxms",""};
74 ======================
78 = Routine from p36 of Extending DOS
80 =======================
83 boolean MML_CheckForEMS(void)
86 static char emmname[] = "EMMXXXX0"; //fix by andrius4669
87 // mov dx,OFFSET emmname
89 //LEA DX, emmname //fix by andrius4669
90 mov dx,OFFSET emmname //fix by andrius4669
92 int 0x21 // try to open EMMXXXX0 device
98 int 0x21 // get device info
106 int 0x21 // get status
112 int 0x21 // close handle
131 ======================
135 =======================
138 unsigned MML_SetupEMS(mminfo_t *mm)
140 char str[80],str2[10];
142 boolean errorflag=false;
145 unsigned int EMSVer = 0;
146 unsigned totalEMSpages,freeEMSpages,EMSpageframe,EMSpagesmapped,EMShandle;
147 totalEMSpages = freeEMSpages = EMSpageframe = EMSpagesmapped = 0;
152 int EMS_INT // make sure EMS hardware is present
160 mov [EMSVer],ax // set EMSVer
161 cmp al,0x32 // only work on ems 3.2 or greater
165 int EMS_INT // find the page frame address
168 mov [EMSpageframe],bx
171 int EMS_INT // find out how much EMS is there
174 mov [totalEMSpages],dx
175 mov [freeEMSpages],bx
177 jz noEMS // no EMS at all to allocate
181 cmp bx,[freeEMSpages]
183 mov bx,[freeEMSpages]
188 jle getpages // there is only 1,2,3,or 4 pages
189 mov bx,4 // we can't use more than 4 pages
192 mov [EMSpagesmapped],bx
193 mov ah,EMS_ALLOCPAGES // allocate up to 64k of EMS
208 strcpy(str,"MML_SetupEMS: EMS error 0x");
214 mm->totalEMSpages=totalEMSpages;
215 mm->freeEMSpages=freeEMSpages;
216 mm->EMSpageframe=EMSpageframe;
217 mm->EMSpagesmapped=EMSpagesmapped;
218 mm->EMShandle=EMShandle;
225 ======================
229 =======================
232 void MML_ShutdownEMS(mminfo_t *mm)
234 boolean errorflag=false;
235 unsigned EMShandle=mm->EMShandle;
249 if(errorflag==true) printf("MML_ShutdownEMS: Error freeing EMS!"); //++++ add something
257 = Maps the 64k of EMS used by memory manager into the page frame
258 = for general use. This only needs to be called if you are keeping
259 = other things in EMS.
264 unsigned MM_MapEMS(mminfo_t *mm)
266 char str[80],str2[10];
267 unsigned err, EMShandle;
268 boolean errorflag=false;
271 EMShandle=mm->EMShandle;
273 for (i=0;i<mm->EMSpagesmapped;i++)
278 mov bx,[i] // logical page
279 mov al,bl // physical page
280 mov dx,[EMShandle] // handle
292 strcpy(str,"MM_MapEMS: EMS error 0x");
302 //==========================================================================
305 ======================
309 = Check for XMM driver
311 =======================
314 boolean MML_CheckForXMS(mminfo_t *mm)
316 boolean errorflag=false;
322 int 0x2f // query status of installed diver
328 if(errorflag==true) return false;
334 ======================
338 = Try to allocate all upper memory block
340 =======================
343 void MML_SetupXMS(mminfo_t *mm, mminfotype *mmi)
352 mov [WORD PTR XMSaddr],bx
353 mov [WORD PTR XMSaddr+2],es // function pointer to XMS driver
356 mov dx,0xffff // try for largest block possible
357 //mov ax,dx // Set available Kbytes.
358 call [DWORD PTR XMSaddr]
362 cmp bl,0xb0 // error: smaller UMB is available
366 call [DWORD PTR XMSaddr] // DX holds largest available UMB
368 jz done // another error...
375 printf("base=%u ", base); printf("size=%u\n", size);
376 MML_UseSpace(base,size, mm);
377 mmi->XMSmem += size*16;
378 mm->UMBbase[mm->numUMBs] = base;
380 if(mm->numUMBs < MAXUMBS)
386 ======================
390 ======================
393 void MML_ShutdownXMS(mminfo_t *mm)
398 for (i=0;i<mm->numUMBs;i++)
400 base = mm->UMBbase[i];
405 call [DWORD PTR XMSaddr]
410 //==========================================================================
413 ======================
417 = Marks a range of paragraphs as usable by the memory manager
418 = This is used to mark space for the near heap, far heap, ems page frame,
419 = and upper memory blocks
421 ======================
424 void MML_UseSpace(/*d*/word segstart, dword seglength, mminfo_t *mm)
426 mmblocktype huge *scan,huge *last;
431 scan = last = mm->mmhead;
432 mm->mmrover = mm->mmhead; // reset rover to start of memory
435 // search for the block that contains the range of segments
437 while(scan->start+scan->length < segstart)
443 //find out how many blocks it spans!
444 if(seglength>0xffffu)
446 segm=seglength/0x4000u;
447 // segm=seglength/0xffffu;
452 // take the given range out of the block
454 oldend = scan->start + scan->length;
455 extra = oldend - (segstart+seglength);
466 printf("segm=%u ", segm);
467 printf("ex=%lu ", extra);
468 printf("len=%u ", scan->length);
469 printf("segsta=%x ", segstart);
470 printf("seglen=%lu\n", seglength);
472 //MML_UseSpace(?segstart?, ?length?, mm);
478 //printf("MML_UseSpace: Segment spans two blocks!\n");
482 //++++todo: linked list of segment!
483 //printf("segm=%lu\n", segm);
484 if(segstart == scan->start)
486 last->next = scan->next; // unlink block
487 MM_FreeBlock(scan, mm);
491 scan->length = segstart-scan->start; // shorten block
498 mm->mmnew->next = scan->next;
499 scan->next = mm->mmnew;
500 mm->mmnew->start = segstart+seglength;
501 mm->mmnew->length = extra;
502 mm->mmnew->attributes = LOCKBIT;
503 }//else if(segm>0) goto segu;
507 //==========================================================================
514 = We are out of blocks, so free a purgable block
519 void MML_ClearBlock(mminfo_t *mm)
521 mmblocktype huge *scan,huge *last;
523 scan = mm->mmhead->next;
527 if(!(scan->attributes&LOCKBIT) && (scan->attributes&PURGEBITS))
529 MM_FreePtr(scan->useptr, mm);
535 printf("MM_ClearBlock: No purgable blocks!\n");
539 //==========================================================================
546 = Grabs all space from turbo with malloc/farmalloc
547 = Allocates bufferseg misc buffer
552 void MM_Startup(mminfo_t *mm, mminfotype *mmi)
557 unsigned segstart,seglength,endfree;
562 mm->mmstarted = true;
563 mm->bombonerror = true;
565 // set up the linked list (everything in the free list;
568 mm->mmfree = &(mm->mmblocks[0]);
569 for(i=0;i<MAXBLOCKS-1;i++)
571 mm->mmblocks[i].next = &(mm->mmblocks[i+1]);
573 mm->mmblocks[i].next = NULL;
576 // locked block of all memory until we punch out free space
579 mm->mmhead = mm->mmnew; // this will allways be the first node
580 mm->mmnew->start = 0;
581 mm->mmnew->length = 0xffff;
582 mm->mmnew->attributes = LOCKBIT;
583 mm->mmnew->next = NULL;
584 mm->mmrover = mm->mmhead;
586 // farlen=_bios_memsize()*1024;
589 // get all available near conventional memory segments
591 //---- length=coreleft();
594 start = (void huge *)(mm->nearheap = malloc(length));
595 length -= 16-(FP_OFF(start)&15);
596 length -= SAVENEARHEAP;
597 seglength = length / 16; // now in paragraphs
598 segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;
599 MML_UseSpace(segstart,seglength, mm);
600 mmi->nearheap = length;
601 //printf("near heap ok!\n");
604 // get all available far conventional memory segments
606 //---- length=farcoreleft();
609 start = mm->farheap = halloc(length, sizeof(byte));
610 //start = mm->farheap = _fmalloc(length);
611 length -= 16-(FP_OFF(start)&15);
612 length -= SAVEFARHEAP;
613 seglength = length / 16; // now in paragraphs
614 segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;
615 MML_UseSpace(segstart,seglength, mm);
616 mmi->farheap = length;
617 mmi->mainmem = mmi->nearheap + mmi->farheap;
618 //printf("far heap ok!\n");
622 // detect EMS and allocate up to 64K at page frame
624 printf("EMS!\n\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); //bug!
626 for(i = 1;i < __argc;i++)
628 if(US_CheckParm(__argv[i],ParmStringsexmm) == 0)
629 goto emsskip; // param NOEMS
631 printf("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); //bug!
632 if(MML_CheckForEMS())
635 MML_SetupEMS(mm); // allocate space
636 printf("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); //bug!
637 //TODO: EMS4! AND EMS 3.2 MASSIVE DATA HANDLMENT!
638 MML_UseSpace(mm->EMSpageframe,(mm->EMSpagesmapped)*0x4000lu, mm);
640 MM_MapEMS(mm); // map in used pages
642 mmi->EMSmem = (mm->EMSpagesmapped)*0x4000lu;
646 // detect XMS and get upper memory blocks
650 for(i = 1;i < __argc;i++)
652 if(US_CheckParm(__argv[i],ParmStringsexmm) == 0)
653 goto xmsskip; // param NOXMS
655 printf("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); //bug!
656 if(MML_CheckForXMS(mm))
659 MML_SetupXMS(mm, mmi); // allocate as many UMBs as possible
663 // allocate the misc buffer
666 mm->mmrover = mm->mmhead; // start looking for space after low block
668 MM_GetPtr(&(mm->bufferseg),BUFFERSIZE, mm, mmi);
671 //==========================================================================
678 = Frees all conventional, EMS, and XMS allocated
683 void MM_Shutdown(mminfo_t *mm)
689 printf("far freed\n");
691 printf("near freed\n");
692 //hfree(mm->hugeheap);
693 //printf("huge freed\n");
694 if(MML_CheckForEMS()){ MML_ShutdownEMS(mm); printf("EMS freed\n"); }
695 if(MML_CheckForXMS(mm)){ MML_ShutdownXMS(mm); printf("XMS freed\n"); }
698 //==========================================================================
705 = Allocates an unlocked, unpurgable block
710 void MM_GetPtr(memptr *baseptr,dword size, mminfo_t *mm, mminfotype *mmi)
712 mmblocktype huge *scan,huge *lastscan,huge *endscan,huge *purge,huge *next;
714 unsigned needed,startseg;
716 needed = (size+15)/16; // convert size from bytes to paragraphs
718 MM_GetNewBlock(mm); // fill in start and next after a spot is found
719 mm->mmnew->length = needed;
720 mm->mmnew->useptr = baseptr;
721 mm->mmnew->attributes = BASEATTRIBUTES;
723 for(search = 0; search<3; search++)
726 // first search: try to allocate right after the rover, then on up
727 // second search: search from the head pointer up to the rover
728 // third search: compress memory, then scan from start
729 if(search == 1 && mm->mmrover == mm->mmhead)
735 lastscan = mm->mmrover;
736 scan = mm->mmrover->next;
740 lastscan = mm->mmhead;
741 scan = mm->mmhead->next;
742 endscan = mm->mmrover;
746 lastscan = mm->mmhead;
747 scan = mm->mmhead->next;
752 startseg = lastscan->start + lastscan->length;
754 while(scan != endscan)
756 if(scan->start - startseg >= needed)
759 // got enough space between the end of lastscan and
760 // the start of scan, so throw out anything in the middle
761 // and allocate the new block
763 purge = lastscan->next;
764 lastscan->next = mm->mmnew;
765 mm->mmnew->start = *(unsigned *)baseptr = startseg;
766 mm->mmnew->next = scan;
768 { // free the purgable block
770 MM_FreeBlock(purge, mm);
771 purge = next; // purge another if not at scan
773 mm->mmrover = mm->mmnew;
774 return; // good allocation!
778 // if this block is purge level zero or locked, skip past it
780 if((scan->attributes & LOCKBIT)
781 || !(scan->attributes & PURGEBITS) )
784 startseg = lastscan->start + lastscan->length;
788 scan=scan->next; // look at next line
793 printf(OUT_OF_MEM_MSG,(size-mmi->nearheap));
798 //==========================================================================
805 = Allocates an unlocked, unpurgable block
810 void MM_FreePtr(memptr *baseptr, mminfo_t *mm)
812 mmblocktype huge *scan,huge *last;
817 if(baseptr == mm->mmrover->useptr) // removed the last allocated block
818 mm->mmrover = mm->mmhead;
820 while(scan->useptr != baseptr && scan)
828 printf("MM_FreePtr: Block not found!");
832 last->next = scan->next;
834 MM_FreeBlock(scan, mm);
836 //==========================================================================
839 =====================
843 = Sets the purge level for a block (locked blocks cannot be made purgable)
845 =====================
848 void MM_SetPurge(memptr *baseptr, int purge, mminfo_t *mm)
850 mmblocktype huge *start;
856 if(mm->mmrover->useptr == baseptr)
859 mm->mmrover = mm->mmrover->next;
862 mm->mmrover = mm->mmhead;
863 else if(mm->mmrover == start)
865 printf("MM_SetPurge: Block not found!");
871 mm->mmrover->attributes &= ~PURGEBITS;
872 mm->mmrover->attributes |= purge;
875 //==========================================================================
878 =====================
882 = Locks / unlocks the block
884 =====================
887 void MM_SetLock(memptr *baseptr, boolean locked, mminfo_t *mm)
889 mmblocktype huge *start;
895 if(mm->mmrover->useptr == baseptr)
898 mm->mmrover = mm->mmrover->next;
901 mm->mmrover = mm->mmhead;
902 else if(mm->mmrover == start)
904 printf("MM_SetLock: Block not found!");
910 mm->mmrover->attributes &= ~LOCKBIT;
911 mm->mmrover->attributes |= locked*LOCKBIT;
914 //==========================================================================
917 =====================
921 = Throws out all purgable stuff and compresses movable blocks
923 =====================
926 void MM_SortMem(mminfo_t *mm)
928 mmblocktype huge *scan,huge *last,huge *next;
929 unsigned start,length,source,dest,oldborder;
933 // lock down a currently playing sound
935 /*++++ playing = SD_SoundPlaying ();
941 playing += STARTPCSOUNDS;
944 playing += STARTADLIBSOUNDS;
947 MM_SetLock(&(memptr)audiosegs[playing],true);
952 // oldborder = bordercolor;
953 // VW_ColorBorder (15);
960 last = NULL; // shut up compiler warning
964 if(scan->attributes & LOCKBIT)
967 // block is locked, so try to pile later blocks right after it
969 start = scan->start + scan->length;
973 if(scan->attributes & PURGEBITS)
976 // throw out the purgable block
979 MM_FreeBlock(scan, mm);
987 // push the non purgable block on top of the last moved block
989 if(scan->start != start)
991 length = scan->length;
992 source = scan->start;
994 while(length > 0xf00)
996 movedata(source,0,dest,0,0xf00*16);
1001 movedata(source,0,dest,0,length*16);
1003 scan->start = start;
1004 *(unsigned *)scan->useptr = start;
1006 start = scan->start + scan->length;
1011 scan = scan->next; // go to next block
1014 mm->mmrover = mm->mmhead;
1019 // VW_ColorBorder (oldborder);
1022 MM_SetLock(&(memptr)audiosegs[playing],false);*/
1026 //==========================================================================
1030 =====================
1034 =====================
1037 void MM_ShowMemory(mminfo_t *mm)
1039 mmblocktype huge *scan;
1040 unsigned color,temp;
1042 char scratch[160],str[16];
1044 //**** VW_SetDefaultColors();
1045 //**** VW_SetLineWidth(40);
1046 //++++mh temp = bufferofs;
1047 //++++mh bufferofs = 0;
1048 //**** VW_SetScreen (0,0);
1058 if(scan->attributes & PURGEBITS)
1059 color = 5; // dark purple = purgable
1061 color = 9; // medium blue = non purgable
1062 if(scan->attributes & LOCKBIT)
1063 color = 12; // red = locked
1064 if(scan->start<=end)
1066 printf("\nMM_ShowMemory: Memory block order currupted!\n");
1069 end = scan->start+scan->length-1;
1070 //++++ VW_Hlin(scan->start,(unsigned)end,0,color);
1071 //++++ VW_Plot(scan->start,0,15);
1072 if(scan->next->start > end+1)
1073 //++++ VW_Hlin(end+1,scan->next->start,0,0); // black = free
1076 printf("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); //bug!
1077 strcpy(scratch,"Location:");
1078 ultoa (scan->start,str,16);
1079 strcat (scratch,str);
1080 strcat (scratch,"\tSize:");
1081 ltoa ((dword)scan->length*16,str,10);
1082 strcat (scratch,str);
1083 strcat (scratch,"\tOwner:0x");
1084 owner = (unsigned)scan->useptr;
1085 ultoa (owner,str,16);
1086 strcat (scratch,str);
1087 strcat (scratch,"\n");
1088 //++++write (debughandle,scratch,strlen(scratch));
1089 fprintf(stdout, "%s", scratch);
1098 //**** VW_SetLineWidth(64);
1099 //++++mh bufferofs = temp;
1103 //==========================================================================
1107 ======================
1111 = Returns the total free space without purging
1113 ======================
1116 dword MM_UnusedMemory(mminfo_t *mm)
1119 mmblocktype huge *scan;
1126 free += scan->next->start - (scan->start + scan->length);
1134 //==========================================================================
1138 ======================
1142 = Returns the total free space with purging
1144 ======================
1147 dword MM_TotalFree(mminfo_t *mm)
1150 mmblocktype huge *scan;
1157 if((scan->attributes&PURGEBITS) && !(scan->attributes&LOCKBIT))
1158 free += scan->length;
1159 free += scan->next->start - (scan->start + scan->length);
1167 //==========================================================================
1170 =====================
1174 =====================
1177 void MM_Report(mminfo_t *mm, mminfotype *mmi)
1179 if(MML_CheckForEMS())
1181 printf("EMM %x available\n", mm->EMSVer);
1182 printf("totalEMSpages=%u\n", mm->totalEMSpages);
1183 printf("freeEMSpages=%u\n", mm->freeEMSpages);
1184 printf("EMSpageframe=%x\n", mm->EMSpageframe);
1186 if(MML_CheckForXMS(mm)) printf("XMSaddr=%Fp\n", *XMSaddr);
1187 printf("near=%lu\n", mmi->nearheap);
1188 printf("far=%lu\n", mmi->farheap);
1189 printf("EMSmem=%lu\n", mmi->EMSmem);
1190 printf("XMSmem=%lu\n", mmi->XMSmem);
1191 printf("mainmem=%lu\n", mmi->mainmem);
1192 printf("UnusedMemory=%lu\n", MM_UnusedMemory(mm));
1193 printf("TotalFree=%lu\n", MM_TotalFree(mm));
1195 // printf("UnusedMemory=%lu kb\n", MM_UnusedMemory()/10248);
1196 // printf("TotalFree=%lu kb\n", MM_TotalFree()/10248);
1199 //==========================================================================
1202 =====================
1206 =====================
1221 //==========================================================================
1224 =====================
1228 =====================
1231 void MM_BombOnError(boolean bomb, mminfo_t *mm)
1233 mm->bombonerror = bomb;
1236 void MM_GetNewBlock(mminfo_t *mm)
1240 mm->mmnew=mm->mmfree;
1241 mm->mmfree=mm->mmfree->next;
1242 /*if(!(mm->mmnew=mm->mmfree))
1244 printf("MM_GETNEWBLOCK: No free blocks!");
1247 mm->mmfree=mm->mmfree->next;*/
1250 void MM_FreeBlock(mmblocktype *x, mminfo_t *mm)
1257 void MM_seguin(void)
1268 void MM_segude(void)
1277 pull data from far and put it into ds var