ID_MM.C (17183B)
1 // NEWMM.C 2 3 /* 4 ============================================================================= 5 6 ID software memory manager 7 -------------------------- 8 9 Primary coder: John Carmack 10 11 RELIES ON 12 --------- 13 Quit (char *error) function 14 15 16 WORK TO DO 17 ---------- 18 MM_SizePtr to change the size of a given pointer 19 20 Multiple purge levels utilized 21 22 EMS / XMS unmanaged routines 23 24 ============================================================================= 25 */ 26 27 #include "ID_HEADS.H" 28 #pragma hdrstop 29 30 #pragma warn -pro 31 #pragma warn -use 32 33 /* 34 ============================================================================= 35 36 LOCAL INFO 37 38 ============================================================================= 39 */ 40 41 #define LOCKBIT 0x80 // if set in attributes, block cannot be moved 42 #define PURGEBITS 3 // 0-3 level, 0= unpurgable, 3= purge first 43 #define PURGEMASK 0xfffc 44 #define BASEATTRIBUTES 0 // unlocked, non purgable 45 46 #define MAXUMBS 10 47 48 typedef struct mmblockstruct 49 { 50 unsigned start,length; 51 unsigned attributes; 52 memptr *useptr; // pointer to the segment start 53 struct mmblockstruct far *next; 54 } mmblocktype; 55 56 57 //#define GETNEWBLOCK {if(!(mmnew=mmfree))Quit("MM_GETNEWBLOCK: No free blocks!")\ 58 // ;mmfree=mmfree->next;} 59 60 #define GETNEWBLOCK {if(!mmfree)MML_ClearBlock();mmnew=mmfree;mmfree=mmfree->next;} 61 62 #define FREEBLOCK(x) {*x->useptr=NULL;x->next=mmfree;mmfree=x;} 63 64 /* 65 ============================================================================= 66 67 GLOBAL VARIABLES 68 69 ============================================================================= 70 */ 71 72 mminfotype mminfo; 73 memptr bufferseg; 74 boolean mmerror; 75 76 void (* beforesort) (void); 77 void (* aftersort) (void); 78 79 /* 80 ============================================================================= 81 82 LOCAL VARIABLES 83 84 ============================================================================= 85 */ 86 87 boolean mmstarted; 88 89 void far *farheap; 90 void *nearheap; 91 92 mmblocktype far mmblocks[MAXBLOCKS] 93 ,far *mmhead,far *mmfree,far *mmrover,far *mmnew; 94 95 boolean bombonerror; 96 97 //unsigned totalEMSpages,freeEMSpages,EMSpageframe,EMSpagesmapped,EMShandle; 98 99 void (* XMSaddr) (void); // far pointer to XMS driver 100 101 unsigned numUMBs,UMBbase[MAXUMBS]; 102 103 //========================================================================== 104 105 // 106 // local prototypes 107 // 108 109 boolean MML_CheckForEMS (void); 110 void MML_ShutdownEMS (void); 111 void MM_MapEMS (void); 112 boolean MML_CheckForXMS (void); 113 void MML_ShutdownXMS (void); 114 void MML_UseSpace (unsigned segstart, unsigned seglength); 115 void MML_ClearBlock (void); 116 117 //========================================================================== 118 119 /* 120 ====================== 121 = 122 = MML_CheckForXMS 123 = 124 = Check for XMM driver 125 = 126 ======================= 127 */ 128 129 boolean MML_CheckForXMS (void) 130 { 131 numUMBs = 0; 132 133 asm { 134 mov ax,0x4300 135 int 0x2f // query status of installed diver 136 cmp al,0x80 137 je good 138 } 139 140 return false; 141 good: 142 return true; 143 } 144 145 146 /* 147 ====================== 148 = 149 = MML_SetupXMS 150 = 151 = Try to allocate all upper memory block 152 = 153 ======================= 154 */ 155 156 void MML_SetupXMS (void) 157 { 158 unsigned base,size; 159 160 asm { 161 mov ax,0x4310 162 int 0x2f 163 mov [WORD PTR XMSaddr],bx 164 mov [WORD PTR XMSaddr+2],es // function pointer to XMS driver 165 } 166 167 getmemory: 168 asm { 169 mov ah,XMS_ALLOCUMB 170 mov dx,0xffff // try for largest block possible 171 call [DWORD PTR XMSaddr] 172 or ax,ax 173 jnz gotone 174 175 cmp bl,0xb0 // error: smaller UMB is available 176 jne done; 177 178 mov ah,XMS_ALLOCUMB 179 call [DWORD PTR XMSaddr] // DX holds largest available UMB 180 or ax,ax 181 jz done // another error... 182 } 183 184 gotone: 185 asm { 186 mov [base],bx 187 mov [size],dx 188 } 189 MML_UseSpace (base,size); 190 mminfo.XMSmem += size*16; 191 UMBbase[numUMBs] = base; 192 numUMBs++; 193 if (numUMBs < MAXUMBS) 194 goto getmemory; 195 196 done:; 197 } 198 199 200 /* 201 ====================== 202 = 203 = MML_ShutdownXMS 204 = 205 ====================== 206 */ 207 208 void MML_ShutdownXMS (void) 209 { 210 int i; 211 unsigned base; 212 213 for (i=0;i<numUMBs;i++) 214 { 215 base = UMBbase[i]; 216 217 asm mov ah,XMS_FREEUMB 218 asm mov dx,[base] 219 asm call [DWORD PTR XMSaddr] 220 } 221 } 222 223 //========================================================================== 224 225 /* 226 ====================== 227 = 228 = MML_UseSpace 229 = 230 = Marks a range of paragraphs as usable by the memory manager 231 = This is used to mark space for the near heap, far heap, ems page frame, 232 = and upper memory blocks 233 = 234 ====================== 235 */ 236 237 void MML_UseSpace (unsigned segstart, unsigned seglength) 238 { 239 mmblocktype far *scan,far *last; 240 unsigned oldend; 241 long extra; 242 243 scan = last = mmhead; 244 mmrover = mmhead; // reset rover to start of memory 245 246 // 247 // search for the block that contains the range of segments 248 // 249 while (scan->start+scan->length < segstart) 250 { 251 last = scan; 252 scan = scan->next; 253 } 254 255 // 256 // take the given range out of the block 257 // 258 oldend = scan->start + scan->length; 259 extra = oldend - (segstart+seglength); 260 if (extra < 0) 261 Quit ("MML_UseSpace: Segment spans two blocks!"); 262 263 if (segstart == scan->start) 264 { 265 last->next = scan->next; // unlink block 266 FREEBLOCK(scan); 267 scan = last; 268 } 269 else 270 scan->length = segstart-scan->start; // shorten block 271 272 if (extra > 0) 273 { 274 GETNEWBLOCK; 275 mmnew->useptr = NULL; 276 277 mmnew->next = scan->next; 278 scan->next = mmnew; 279 mmnew->start = segstart+seglength; 280 mmnew->length = extra; 281 mmnew->attributes = LOCKBIT; 282 } 283 284 } 285 286 //========================================================================== 287 288 /* 289 ==================== 290 = 291 = MML_ClearBlock 292 = 293 = We are out of blocks, so free a purgable block 294 = 295 ==================== 296 */ 297 298 void MML_ClearBlock (void) 299 { 300 mmblocktype far *scan,far *last; 301 302 scan = mmhead->next; 303 304 while (scan) 305 { 306 if (!(scan->attributes&LOCKBIT) && (scan->attributes&PURGEBITS) ) 307 { 308 MM_FreePtr(scan->useptr); 309 return; 310 } 311 scan = scan->next; 312 } 313 314 Quit ("MM_ClearBlock: No purgable blocks!"); 315 } 316 317 318 //========================================================================== 319 320 /* 321 =================== 322 = 323 = MM_Startup 324 = 325 = Grabs all space from turbo with malloc/farmalloc 326 = Allocates bufferseg misc buffer 327 = 328 =================== 329 */ 330 331 static char *ParmStrings[] = {"noems","noxms",""}; 332 333 void MM_Startup (void) 334 { 335 int i; 336 unsigned long length; 337 void far *start; 338 unsigned segstart,seglength,endfree; 339 340 if (mmstarted) 341 MM_Shutdown (); 342 343 344 mmstarted = true; 345 bombonerror = true; 346 // 347 // set up the linked list (everything in the free list; 348 // 349 mmhead = NULL; 350 mmfree = &mmblocks[0]; 351 for (i=0;i<MAXBLOCKS-1;i++) 352 mmblocks[i].next = &mmblocks[i+1]; 353 mmblocks[i].next = NULL; 354 355 // 356 // locked block of all memory until we punch out free space 357 // 358 GETNEWBLOCK; 359 mmhead = mmnew; // this will allways be the first node 360 mmnew->start = 0; 361 mmnew->length = 0xffff; 362 mmnew->attributes = LOCKBIT; 363 mmnew->next = NULL; 364 mmrover = mmhead; 365 366 367 // 368 // get all available near conventional memory segments 369 // 370 length=coreleft(); 371 start = (void far *)(nearheap = malloc(length)); 372 373 length -= 16-(FP_OFF(start)&15); 374 length -= SAVENEARHEAP; 375 seglength = length / 16; // now in paragraphs 376 segstart = FP_SEG(start)+(FP_OFF(start)+15)/16; 377 MML_UseSpace (segstart,seglength); 378 mminfo.nearheap = length; 379 380 // 381 // get all available far conventional memory segments 382 // 383 length=farcoreleft(); 384 start = farheap = farmalloc(length); 385 length -= 16-(FP_OFF(start)&15); 386 length -= SAVEFARHEAP; 387 seglength = length / 16; // now in paragraphs 388 segstart = FP_SEG(start)+(FP_OFF(start)+15)/16; 389 MML_UseSpace (segstart,seglength); 390 mminfo.farheap = length; 391 mminfo.mainmem = mminfo.nearheap + mminfo.farheap; 392 393 // 394 // allocate the misc buffer 395 // 396 mmrover = mmhead; // start looking for space after low block 397 398 MM_GetPtr (&bufferseg,BUFFERSIZE); 399 } 400 401 //========================================================================== 402 403 /* 404 ==================== 405 = 406 = MM_Shutdown 407 = 408 = Frees all conventional, EMS, and XMS allocated 409 = 410 ==================== 411 */ 412 413 void MM_Shutdown (void) 414 { 415 if (!mmstarted) 416 return; 417 418 farfree (farheap); 419 free (nearheap); 420 // MML_ShutdownXMS (); 421 } 422 423 //========================================================================== 424 425 /* 426 ==================== 427 = 428 = MM_GetPtr 429 = 430 = Allocates an unlocked, unpurgable block 431 = 432 ==================== 433 */ 434 435 void MM_GetPtr (memptr *baseptr,unsigned long size) 436 { 437 mmblocktype far *scan,far *lastscan,far *endscan 438 ,far *purge,far *next; 439 int search; 440 unsigned needed,startseg; 441 442 needed = (size+15)/16; // convert size from bytes to paragraphs 443 444 GETNEWBLOCK; // fill in start and next after a spot is found 445 mmnew->length = needed; 446 mmnew->useptr = baseptr; 447 mmnew->attributes = BASEATTRIBUTES; 448 449 tryagain: 450 for (search = 0; search<3; search++) 451 { 452 // 453 // first search: try to allocate right after the rover, then on up 454 // second search: search from the head pointer up to the rover 455 // third search: compress memory, then scan from start 456 if (search == 1 && mmrover == mmhead) 457 search++; 458 459 switch (search) 460 { 461 case 0: 462 lastscan = mmrover; 463 scan = mmrover->next; 464 endscan = NULL; 465 break; 466 case 1: 467 lastscan = mmhead; 468 scan = mmhead->next; 469 endscan = mmrover; 470 break; 471 case 2: 472 MM_SortMem (); 473 lastscan = mmhead; 474 scan = mmhead->next; 475 endscan = NULL; 476 break; 477 } 478 479 startseg = lastscan->start + lastscan->length; 480 481 while (scan != endscan) 482 { 483 if (scan->start - startseg >= needed) 484 { 485 // 486 // got enough space between the end of lastscan and 487 // the start of scan, so throw out anything in the middle 488 // and allocate the new block 489 // 490 purge = lastscan->next; 491 lastscan->next = mmnew; 492 mmnew->start = *(unsigned *)baseptr = startseg; 493 mmnew->next = scan; 494 while ( purge != scan) 495 { // free the purgable block 496 next = purge->next; 497 FREEBLOCK(purge); 498 purge = next; // purge another if not at scan 499 } 500 mmrover = mmnew; 501 return; // good allocation! 502 } 503 504 // 505 // if this block is purge level zero or locked, skip past it 506 // 507 if ( (scan->attributes & LOCKBIT) 508 || !(scan->attributes & PURGEBITS) ) 509 { 510 lastscan = scan; 511 startseg = lastscan->start + lastscan->length; 512 } 513 514 515 scan=scan->next; // look at next line 516 } 517 } 518 519 if (bombonerror) 520 { 521 522 extern char configname[]; 523 extern boolean insetupscaling; 524 extern int viewsize; 525 boolean SetViewSize (unsigned width, unsigned height); 526 #define HEIGHTRATIO 0.50 527 // 528 // wolf hack -- size the view down 529 // 530 if (!insetupscaling && viewsize>10) 531 { 532 mmblocktype far *savedmmnew; 533 savedmmnew = mmnew; 534 viewsize -= 2; 535 SetViewSize (viewsize*16,viewsize*16*HEIGHTRATIO); 536 mmnew = savedmmnew; 537 goto tryagain; 538 } 539 540 // unlink(configname); 541 Quit ("MM_GetPtr: Out of memory!"); 542 } 543 else 544 mmerror = true; 545 } 546 547 //========================================================================== 548 549 /* 550 ==================== 551 = 552 = MM_FreePtr 553 = 554 = Deallocates an unlocked, purgable block 555 = 556 ==================== 557 */ 558 559 void MM_FreePtr (memptr *baseptr) 560 { 561 mmblocktype far *scan,far *last; 562 563 last = mmhead; 564 scan = last->next; 565 566 if (baseptr == mmrover->useptr) // removed the last allocated block 567 mmrover = mmhead; 568 569 while (scan->useptr != baseptr && scan) 570 { 571 last = scan; 572 scan = scan->next; 573 } 574 575 if (!scan) 576 Quit ("MM_FreePtr: Block not found!"); 577 578 last->next = scan->next; 579 580 FREEBLOCK(scan); 581 } 582 //========================================================================== 583 584 /* 585 ===================== 586 = 587 = MM_SetPurge 588 = 589 = Sets the purge level for a block (locked blocks cannot be made purgable) 590 = 591 ===================== 592 */ 593 594 void MM_SetPurge (memptr *baseptr, int purge) 595 { 596 mmblocktype far *start; 597 598 start = mmrover; 599 600 do 601 { 602 if (mmrover->useptr == baseptr) 603 break; 604 605 mmrover = mmrover->next; 606 607 if (!mmrover) 608 mmrover = mmhead; 609 else if (mmrover == start) 610 Quit ("MM_SetPurge: Block not found!"); 611 612 } while (1); 613 614 mmrover->attributes &= ~PURGEBITS; 615 mmrover->attributes |= purge; 616 } 617 618 //========================================================================== 619 620 /* 621 ===================== 622 = 623 = MM_SetLock 624 = 625 = Locks / unlocks the block 626 = 627 ===================== 628 */ 629 630 void MM_SetLock (memptr *baseptr, boolean locked) 631 { 632 mmblocktype far *start; 633 634 start = mmrover; 635 636 do 637 { 638 if (mmrover->useptr == baseptr) 639 break; 640 641 mmrover = mmrover->next; 642 643 if (!mmrover) 644 mmrover = mmhead; 645 else if (mmrover == start) 646 Quit ("MM_SetLock: Block not found!"); 647 648 } while (1); 649 650 mmrover->attributes &= ~LOCKBIT; 651 mmrover->attributes |= locked*LOCKBIT; 652 } 653 654 //========================================================================== 655 656 /* 657 ===================== 658 = 659 = MM_SortMem 660 = 661 = Throws out all purgable stuff and compresses movable blocks 662 = 663 ===================== 664 */ 665 666 void MM_SortMem (void) 667 { 668 mmblocktype far *scan,far *last,far *next; 669 unsigned start,length,source,dest; 670 int playing; 671 672 // 673 // lock down a currently playing sound 674 // 675 playing = SD_SoundPlaying (); 676 if (playing) 677 { 678 switch (SoundMode) 679 { 680 case sdm_PC: 681 playing += STARTPCSOUNDS; 682 break; 683 case sdm_AdLib: 684 playing += STARTADLIBSOUNDS; 685 break; 686 } 687 MM_SetLock(&(memptr)audiosegs[playing],true); 688 } 689 690 691 SD_StopSound(); 692 693 if (beforesort) 694 beforesort(); 695 696 scan = mmhead; 697 698 last = NULL; // shut up compiler warning 699 700 while (scan) 701 { 702 if (scan->attributes & LOCKBIT) 703 { 704 // 705 // block is locked, so try to pile later blocks right after it 706 // 707 start = scan->start + scan->length; 708 } 709 else 710 { 711 if (scan->attributes & PURGEBITS) 712 { 713 // 714 // throw out the purgable block 715 // 716 next = scan->next; 717 FREEBLOCK(scan); 718 last->next = next; 719 scan = next; 720 continue; 721 } 722 else 723 { 724 // 725 // push the non purgable block on top of the last moved block 726 // 727 if (scan->start != start) 728 { 729 length = scan->length; 730 source = scan->start; 731 dest = start; 732 while (length > 0xf00) 733 { 734 movedata(source,0,dest,0,0xf00*16); 735 length -= 0xf00; 736 source += 0xf00; 737 dest += 0xf00; 738 } 739 movedata(source,0,dest,0,length*16); 740 741 scan->start = start; 742 *(unsigned *)scan->useptr = start; 743 } 744 start = scan->start + scan->length; 745 } 746 } 747 748 last = scan; 749 scan = scan->next; // go to next block 750 } 751 752 mmrover = mmhead; 753 754 if (aftersort) 755 aftersort(); 756 757 if (playing) 758 MM_SetLock(&(memptr)audiosegs[playing],false); 759 } 760 761 762 //========================================================================== 763 764 /* 765 ===================== 766 = 767 = MM_ShowMemory 768 = 769 ===================== 770 */ 771 772 void MM_ShowMemory (void) 773 { 774 mmblocktype far *scan; 775 unsigned color,temp,x,y; 776 long end,owner; 777 char scratch[80],str[10]; 778 779 temp = bufferofs; 780 bufferofs = displayofs; 781 scan = mmhead; 782 783 end = -1; 784 785 while (scan) 786 { 787 if (scan->attributes & PURGEBITS) 788 color = 5; // dark purple = purgable 789 else 790 color = 9; // medium blue = non purgable 791 if (scan->attributes & LOCKBIT) 792 color = 12; // red = locked 793 if (scan->start<=end) 794 Quit ("MM_ShowMemory: Memory block order currupted!"); 795 end = scan->length-1; 796 y = scan->start/320; 797 x = scan->start%320; 798 VW_Hlin(x,x+end,y,color); 799 VW_Plot(x,y,15); 800 if (scan->next && scan->next->start > end+1) 801 VW_Hlin(x+end+1,x+(scan->next->start-scan->start),y,0); // black = free 802 803 scan = scan->next; 804 } 805 806 VW_FadeIn (); 807 IN_Ack(); 808 809 bufferofs = temp; 810 } 811 812 //========================================================================== 813 814 /* 815 ===================== 816 = 817 = MM_DumpData 818 = 819 ===================== 820 */ 821 822 void MM_DumpData (void) 823 { 824 mmblocktype far *scan,far *best; 825 long lowest,oldlowest; 826 unsigned owner; 827 char lock,purge; 828 FILE *dumpfile; 829 830 831 free (nearheap); 832 dumpfile = fopen ("MMDUMP.TXT","w"); 833 if (!dumpfile) 834 Quit ("MM_DumpData: Couldn't open MMDUMP.TXT!"); 835 836 lowest = -1; 837 do 838 { 839 oldlowest = lowest; 840 lowest = 0xffff; 841 842 scan = mmhead; 843 while (scan) 844 { 845 owner = (unsigned)scan->useptr; 846 847 if (owner && owner<lowest && owner > oldlowest) 848 { 849 best = scan; 850 lowest = owner; 851 } 852 853 scan = scan->next; 854 } 855 856 if (lowest != 0xffff) 857 { 858 if (best->attributes & PURGEBITS) 859 purge = 'P'; 860 else 861 purge = '-'; 862 if (best->attributes & LOCKBIT) 863 lock = 'L'; 864 else 865 lock = '-'; 866 fprintf (dumpfile,"0x%p (%c%c) = %u\n" 867 ,(unsigned)lowest,lock,purge,best->length); 868 } 869 870 } while (lowest != 0xffff); 871 872 fclose (dumpfile); 873 Quit ("MMDUMP.TXT created."); 874 } 875 876 //========================================================================== 877 878 879 /* 880 ====================== 881 = 882 = MM_UnusedMemory 883 = 884 = Returns the total free space without purging 885 = 886 ====================== 887 */ 888 889 long MM_UnusedMemory (void) 890 { 891 unsigned free; 892 mmblocktype far *scan; 893 894 free = 0; 895 scan = mmhead; 896 897 while (scan->next) 898 { 899 free += scan->next->start - (scan->start + scan->length); 900 scan = scan->next; 901 } 902 903 return free*16l; 904 } 905 906 //========================================================================== 907 908 909 /* 910 ====================== 911 = 912 = MM_TotalFree 913 = 914 = Returns the total free space with purging 915 = 916 ====================== 917 */ 918 919 long MM_TotalFree (void) 920 { 921 unsigned free; 922 mmblocktype far *scan; 923 924 free = 0; 925 scan = mmhead; 926 927 while (scan->next) 928 { 929 if ((scan->attributes&PURGEBITS) && !(scan->attributes&LOCKBIT)) 930 free += scan->length; 931 free += scan->next->start - (scan->start + scan->length); 932 scan = scan->next; 933 } 934 935 return free*16l; 936 } 937 938 //========================================================================== 939 940 /* 941 ===================== 942 = 943 = MM_BombOnError 944 = 945 ===================== 946 */ 947 948 void MM_BombOnError (boolean bomb) 949 { 950 bombonerror = bomb; 951 } 952 953