ID_PM.C (26747B)
1 // 2 // ID_PM.C 3 // Id Engine's Page Manager v1.0 4 // Primary coder: Jason Blochowiak 5 // 6 7 #include "ID_HEADS.H" 8 #pragma hdrstop 9 10 // Main Mem specific variables 11 boolean MainPresent; 12 memptr MainMemPages[PMMaxMainMem]; 13 PMBlockAttr MainMemUsed[PMMaxMainMem]; 14 int MainPagesAvail; 15 16 // EMS specific variables 17 boolean EMSPresent; 18 word EMSAvail,EMSPagesAvail,EMSHandle, 19 EMSPageFrame,EMSPhysicalPage; 20 EMSListStruct EMSList[EMSFrameCount]; 21 22 // XMS specific variables 23 boolean XMSPresent; 24 word XMSAvail,XMSPagesAvail,XMSHandle; 25 longword XMSDriver; 26 int XMSProtectPage = -1; 27 28 // File specific variables 29 char PageFileName[13] = {"VSWAP."}; 30 int PageFile = -1; 31 word ChunksInFile; 32 word PMSpriteStart,PMSoundStart; 33 34 // General usage variables 35 boolean PMStarted, 36 PMPanicMode, 37 PMThrashing; 38 word XMSPagesUsed, 39 EMSPagesUsed, 40 MainPagesUsed, 41 PMNumBlocks; 42 long PMFrameCount; 43 PageListStruct far *PMPages, 44 _seg *PMSegPages; 45 46 static char *ParmStrings[] = {"nomain","noems","noxms",nil}; 47 48 ///////////////////////////////////////////////////////////////////////////// 49 // 50 // EMS Management code 51 // 52 ///////////////////////////////////////////////////////////////////////////// 53 54 // 55 // PML_MapEMS() - Maps a logical page to a physical page 56 // 57 void 58 PML_MapEMS(word logical,word physical) 59 { 60 _AL = physical; 61 _BX = logical; 62 _DX = EMSHandle; 63 _AH = EMS_MAPPAGE; 64 asm int EMS_INT 65 66 if (_AH) 67 Quit("PML_MapEMS: Page mapping failed"); 68 } 69 70 // 71 // PML_StartupEMS() - Sets up EMS for Page Mgr's use 72 // Checks to see if EMS driver is present 73 // Verifies that EMS hardware is present 74 // Make sure that EMS version is 3.2 or later 75 // If there's more than our minimum (2 pages) available, allocate it (up 76 // to the maximum we need) 77 // 78 79 char EMMDriverName[9] = "EMMXXXX0"; 80 81 boolean 82 PML_StartupEMS(void) 83 { 84 int i; 85 long size; 86 87 EMSPresent = false; // Assume that we'll fail 88 EMSAvail = 0; 89 90 _DX = (word)EMMDriverName; 91 _AX = 0x3d00; 92 geninterrupt(0x21); // try to open EMMXXXX0 device 93 asm jnc gothandle 94 goto error; 95 96 gothandle: 97 _BX = _AX; 98 _AX = 0x4400; 99 geninterrupt(0x21); // get device info 100 asm jnc gotinfo; 101 goto error; 102 103 gotinfo: 104 asm and dx,0x80 105 if (!_DX) 106 goto error; 107 108 _AX = 0x4407; 109 geninterrupt(0x21); // get status 110 asm jc error 111 if (!_AL) 112 goto error; 113 114 _AH = 0x3e; 115 geninterrupt(0x21); // close handle 116 117 _AH = EMS_STATUS; 118 geninterrupt(EMS_INT); 119 if (_AH) 120 goto error; // make sure EMS hardware is present 121 122 _AH = EMS_VERSION; 123 geninterrupt(EMS_INT); 124 if (_AH || (_AL < 0x32)) // only work on EMS 3.2 or greater (silly, but...) 125 goto error; 126 127 _AH = EMS_GETFRAME; 128 geninterrupt(EMS_INT); 129 if (_AH) 130 goto error; // find the page frame address 131 EMSPageFrame = _BX; 132 133 _AH = EMS_GETPAGES; 134 geninterrupt(EMS_INT); 135 if (_AH) 136 goto error; 137 if (_BX < 2) 138 goto error; // Require at least 2 pages (32k) 139 EMSAvail = _BX; 140 141 // Don't hog all available EMS 142 size = EMSAvail * (long)EMSPageSize; 143 if (size - (EMSPageSize * 2) > (ChunksInFile * (long)PMPageSize)) 144 { 145 size = (ChunksInFile * (long)PMPageSize) + EMSPageSize; 146 EMSAvail = size / EMSPageSize; 147 } 148 149 _AH = EMS_ALLOCPAGES; 150 _BX = EMSAvail; 151 geninterrupt(EMS_INT); 152 if (_AH) 153 goto error; 154 EMSHandle = _DX; 155 156 mminfo.EMSmem += EMSAvail * (long)EMSPageSize; 157 158 // Initialize EMS mapping cache 159 for (i = 0;i < EMSFrameCount;i++) 160 EMSList[i].baseEMSPage = -1; 161 162 EMSPresent = true; // We have EMS 163 164 error: 165 return(EMSPresent); 166 } 167 168 // 169 // PML_ShutdownEMS() - If EMS was used, deallocate it 170 // 171 void 172 PML_ShutdownEMS(void) 173 { 174 if (EMSPresent) 175 { 176 asm mov ah,EMS_FREEPAGES 177 asm mov dx,[EMSHandle] 178 asm int EMS_INT 179 if (_AH) 180 Quit ("PML_ShutdownEMS: Error freeing EMS"); 181 } 182 } 183 184 ///////////////////////////////////////////////////////////////////////////// 185 // 186 // XMS Management code 187 // 188 ///////////////////////////////////////////////////////////////////////////// 189 190 // 191 // PML_StartupXMS() - Starts up XMS for the Page Mgr's use 192 // Checks for presence of an XMS driver 193 // Makes sure that there's at least a page of XMS available 194 // Allocates any remaining XMS (rounded down to the nearest page size) 195 // 196 boolean 197 PML_StartupXMS(void) 198 { 199 XMSPresent = false; // Assume failure 200 XMSAvail = 0; 201 202 asm mov ax,0x4300 203 asm int XMS_INT // Check for presence of XMS driver 204 if (_AL != 0x80) 205 goto error; 206 207 208 asm mov ax,0x4310 209 asm int XMS_INT // Get address of XMS driver 210 asm mov [WORD PTR XMSDriver],bx 211 asm mov [WORD PTR XMSDriver+2],es // function pointer to XMS driver 212 213 XMS_CALL(XMS_QUERYFREE); // Find out how much XMS is available 214 XMSAvail = _AX; 215 if (!_AX) // AJR: bugfix 10/8/92 216 goto error; 217 218 XMSAvail &= ~(PMPageSizeKB - 1); // Round off to nearest page size 219 if (XMSAvail < (PMPageSizeKB * 2)) // Need at least 2 pages 220 goto error; 221 222 _DX = XMSAvail; 223 XMS_CALL(XMS_ALLOC); // And do the allocation 224 XMSHandle = _DX; 225 226 if (!_AX) // AJR: bugfix 10/8/92 227 { 228 XMSAvail = 0; 229 goto error; 230 } 231 232 mminfo.XMSmem += XMSAvail * 1024; 233 234 XMSPresent = true; 235 error: 236 return(XMSPresent); 237 } 238 239 // 240 // PML_XMSCopy() - Copies a main/EMS page to or from XMS 241 // Will round an odd-length request up to the next even value 242 // 243 void 244 PML_XMSCopy(boolean toxms,byte far *addr,word xmspage,word length) 245 { 246 longword xoffset; 247 struct 248 { 249 longword length; 250 word source_handle; 251 longword source_offset; 252 word target_handle; 253 longword target_offset; 254 } copy; 255 256 if (!addr) 257 Quit("PML_XMSCopy: zero address"); 258 259 xoffset = (longword)xmspage * PMPageSize; 260 261 copy.length = (length + 1) & ~1; 262 copy.source_handle = toxms? 0 : XMSHandle; 263 copy.source_offset = toxms? (long)addr : xoffset; 264 copy.target_handle = toxms? XMSHandle : 0; 265 copy.target_offset = toxms? xoffset : (long)addr; 266 267 asm push si 268 _SI = (word)© 269 XMS_CALL(XMS_MOVE); 270 asm pop si 271 if (!_AX) 272 Quit("PML_XMSCopy: Error on copy"); 273 } 274 275 #if 1 276 #define PML_CopyToXMS(s,t,l) PML_XMSCopy(true,(s),(t),(l)) 277 #define PML_CopyFromXMS(t,s,l) PML_XMSCopy(false,(t),(s),(l)) 278 #else 279 // 280 // PML_CopyToXMS() - Copies the specified number of bytes from the real mode 281 // segment address to the specified XMS page 282 // 283 void 284 PML_CopyToXMS(byte far *source,int targetpage,word length) 285 { 286 PML_XMSCopy(true,source,targetpage,length); 287 } 288 289 // 290 // PML_CopyFromXMS() - Copies the specified number of bytes from an XMS 291 // page to the specified real mode address 292 // 293 void 294 PML_CopyFromXMS(byte far *target,int sourcepage,word length) 295 { 296 PML_XMSCopy(false,target,sourcepage,length); 297 } 298 #endif 299 300 // 301 // PML_ShutdownXMS() 302 // 303 void 304 PML_ShutdownXMS(void) 305 { 306 if (XMSPresent) 307 { 308 _DX = XMSHandle; 309 XMS_CALL(XMS_FREE); 310 if (_BL) 311 Quit("PML_ShutdownXMS: Error freeing XMS"); 312 } 313 } 314 315 ///////////////////////////////////////////////////////////////////////////// 316 // 317 // Main memory code 318 // 319 ///////////////////////////////////////////////////////////////////////////// 320 321 // 322 // PM_SetMainMemPurge() - Sets the purge level for all allocated main memory 323 // blocks. This shouldn't be called directly - the PM_LockMainMem() and 324 // PM_UnlockMainMem() macros should be used instead. 325 // 326 void 327 PM_SetMainMemPurge(int level) 328 { 329 int i; 330 331 for (i = 0;i < PMMaxMainMem;i++) 332 if (MainMemPages[i]) 333 MM_SetPurge(&MainMemPages[i],level); 334 } 335 336 // 337 // PM_CheckMainMem() - If something besides the Page Mgr makes requests of 338 // the Memory Mgr, some of the Page Mgr's blocks may have been purged, 339 // so this function runs through the block list and checks to see if 340 // any of the blocks have been purged. If so, it marks the corresponding 341 // page as purged & unlocked, then goes through the block list and 342 // tries to reallocate any blocks that have been purged. 343 // This routine now calls PM_LockMainMem() to make sure that any allocation 344 // attempts made during the block reallocation sweep don't purge any 345 // of the other blocks. Because PM_LockMainMem() is called, 346 // PM_UnlockMainMem() needs to be called before any other part of the 347 // program makes allocation requests of the Memory Mgr. 348 // 349 void 350 PM_CheckMainMem(void) 351 { 352 boolean allocfailed; 353 int i,n; 354 memptr *p; 355 PMBlockAttr *used; 356 PageListStruct far *page; 357 358 if (!MainPresent) 359 return; 360 361 for (i = 0,page = PMPages;i < ChunksInFile;i++,page++) 362 { 363 n = page->mainPage; 364 if (n != -1) // Is the page using main memory? 365 { 366 if (!MainMemPages[n]) // Yep, was the block purged? 367 { 368 page->mainPage = -1; // Yes, mark page as purged & unlocked 369 page->locked = pml_Unlocked; 370 } 371 } 372 } 373 374 // Prevent allocation attempts from purging any of our other blocks 375 PM_LockMainMem(); 376 allocfailed = false; 377 for (i = 0,p = MainMemPages,used = MainMemUsed;i < PMMaxMainMem;i++,p++,used++) 378 { 379 if (!*p) // If the page got purged 380 { 381 if (*used & pmba_Allocated) // If it was allocated 382 { 383 *used &= ~pmba_Allocated; // Mark as unallocated 384 MainPagesAvail--; // and decrease available count 385 } 386 387 if (*used & pmba_Used) // If it was used 388 { 389 *used &= ~pmba_Used; // Mark as unused 390 MainPagesUsed--; // and decrease used count 391 } 392 393 if (!allocfailed) 394 { 395 MM_BombOnError(false); 396 MM_GetPtr(p,PMPageSize); // Try to reallocate 397 if (mmerror) // If it failed, 398 allocfailed = true; // don't try any more allocations 399 else // If it worked, 400 { 401 *used |= pmba_Allocated; // Mark as allocated 402 MainPagesAvail++; // and increase available count 403 } 404 MM_BombOnError(true); 405 } 406 } 407 } 408 if (mmerror) 409 mmerror = false; 410 } 411 412 // 413 // PML_StartupMainMem() - Allocates as much main memory as is possible for 414 // the Page Mgr. The memory is allocated as non-purgeable, so if it's 415 // necessary to make requests of the Memory Mgr, PM_UnlockMainMem() 416 // needs to be called. 417 // 418 void 419 PML_StartupMainMem(void) 420 { 421 int i,n; 422 memptr *p; 423 424 MainPagesAvail = 0; 425 MM_BombOnError(false); 426 for (i = 0,p = MainMemPages;i < PMMaxMainMem;i++,p++) 427 { 428 MM_GetPtr(p,PMPageSize); 429 if (mmerror) 430 break; 431 432 MainPagesAvail++; 433 MainMemUsed[i] = pmba_Allocated; 434 } 435 MM_BombOnError(true); 436 if (mmerror) 437 mmerror = false; 438 if (MainPagesAvail < PMMinMainMem) 439 Quit("PM_SetupMainMem: Not enough main memory"); 440 MainPresent = true; 441 } 442 443 // 444 // PML_ShutdownMainMem() - Frees all of the main memory blocks used by the 445 // Page Mgr. 446 // 447 void 448 PML_ShutdownMainMem(void) 449 { 450 int i; 451 memptr *p; 452 453 // DEBUG - mark pages as unallocated & decrease page count as appropriate 454 for (i = 0,p = MainMemPages;i < PMMaxMainMem;i++,p++) 455 if (*p) 456 MM_FreePtr(p); 457 } 458 459 ///////////////////////////////////////////////////////////////////////////// 460 // 461 // File management code 462 // 463 ///////////////////////////////////////////////////////////////////////////// 464 465 // 466 // PML_ReadFromFile() - Reads some data in from the page file 467 // 468 void 469 PML_ReadFromFile(byte far *buf,long offset,word length) 470 { 471 if (!buf) 472 Quit("PML_ReadFromFile: Null pointer"); 473 if (!offset) 474 Quit("PML_ReadFromFile: Zero offset"); 475 if (lseek(PageFile,offset,SEEK_SET) != offset) 476 Quit("PML_ReadFromFile: Seek failed"); 477 if (!CA_FarRead(PageFile,buf,length)) 478 Quit("PML_ReadFromFile: Read failed"); 479 } 480 481 // 482 // PML_OpenPageFile() - Opens the page file and sets up the page info 483 // 484 void 485 PML_OpenPageFile(void) 486 { 487 int i; 488 long size; 489 void _seg *buf; 490 longword far *offsetptr; 491 word far *lengthptr; 492 PageListStruct far *page; 493 494 PageFile = open(PageFileName,O_RDONLY + O_BINARY); 495 if (PageFile == -1) 496 Quit("PML_OpenPageFile: Unable to open page file"); 497 498 // Read in header variables 499 read(PageFile,&ChunksInFile,sizeof(ChunksInFile)); 500 read(PageFile,&PMSpriteStart,sizeof(PMSpriteStart)); 501 read(PageFile,&PMSoundStart,sizeof(PMSoundStart)); 502 503 // Allocate and clear the page list 504 PMNumBlocks = ChunksInFile; 505 MM_GetPtr(&(memptr)PMSegPages,sizeof(PageListStruct) * PMNumBlocks); 506 MM_SetLock(&(memptr)PMSegPages,true); 507 PMPages = (PageListStruct far *)PMSegPages; 508 _fmemset(PMPages,0,sizeof(PageListStruct) * PMNumBlocks); 509 510 // Read in the chunk offsets 511 size = sizeof(longword) * ChunksInFile; 512 MM_GetPtr(&buf,size); 513 if (!CA_FarRead(PageFile,(byte far *)buf,size)) 514 Quit("PML_OpenPageFile: Offset read failed"); 515 offsetptr = (longword far *)buf; 516 for (i = 0,page = PMPages;i < ChunksInFile;i++,page++) 517 page->offset = *offsetptr++; 518 MM_FreePtr(&buf); 519 520 // Read in the chunk lengths 521 size = sizeof(word) * ChunksInFile; 522 MM_GetPtr(&buf,size); 523 if (!CA_FarRead(PageFile,(byte far *)buf,size)) 524 Quit("PML_OpenPageFile: Length read failed"); 525 lengthptr = (word far *)buf; 526 for (i = 0,page = PMPages;i < ChunksInFile;i++,page++) 527 page->length = *lengthptr++; 528 MM_FreePtr(&buf); 529 } 530 531 // 532 // PML_ClosePageFile() - Closes the page file 533 // 534 void 535 PML_ClosePageFile(void) 536 { 537 if (PageFile != -1) 538 close(PageFile); 539 if (PMSegPages) 540 { 541 MM_SetLock(&(memptr)PMSegPages,false); 542 MM_FreePtr(&(void _seg *)PMSegPages); 543 } 544 } 545 546 ///////////////////////////////////////////////////////////////////////////// 547 // 548 // Allocation, etc., code 549 // 550 ///////////////////////////////////////////////////////////////////////////// 551 552 // 553 // PML_GetEMSAddress() 554 // 555 // Page is in EMS, so figure out which EMS physical page should be used 556 // to map our page in. If normal page, use EMS physical page 3, else 557 // use the physical page specified by the lock type 558 // 559 #if 1 560 #pragma argsused // DEBUG - remove lock parameter 561 memptr 562 PML_GetEMSAddress(int page,PMLockType lock) 563 { 564 int i,emspage; 565 word emsoff,emsbase,offset; 566 567 emsoff = page & (PMEMSSubPage - 1); 568 emsbase = page - emsoff; 569 570 emspage = -1; 571 // See if this page is already mapped in 572 for (i = 0;i < EMSFrameCount;i++) 573 { 574 if (EMSList[i].baseEMSPage == emsbase) 575 { 576 emspage = i; // Yep - don't do a redundant remapping 577 break; 578 } 579 } 580 581 // If page isn't already mapped in, find LRU EMS frame, and use it 582 if (emspage == -1) 583 { 584 longword last = MAXLONG; 585 for (i = 0;i < EMSFrameCount;i++) 586 { 587 if (EMSList[i].lastHit < last) 588 { 589 emspage = i; 590 last = EMSList[i].lastHit; 591 } 592 } 593 594 EMSList[emspage].baseEMSPage = emsbase; 595 PML_MapEMS(page / PMEMSSubPage,emspage); 596 } 597 598 if (emspage == -1) 599 Quit("PML_GetEMSAddress: EMS find failed"); 600 601 EMSList[emspage].lastHit = PMFrameCount; 602 offset = emspage * EMSPageSizeSeg; 603 offset += emsoff * PMPageSizeSeg; 604 return((memptr)(EMSPageFrame + offset)); 605 } 606 #else 607 memptr 608 PML_GetEMSAddress(int page,PMLockType lock) 609 { 610 word emspage; 611 612 emspage = (lock < pml_EMSLock)? 3 : (lock - pml_EMSLock); 613 614 PML_MapEMS(page / PMEMSSubPage,emspage); 615 616 return((memptr)(EMSPageFrame + (emspage * EMSPageSizeSeg) 617 + ((page & (PMEMSSubPage - 1)) * PMPageSizeSeg))); 618 } 619 #endif 620 621 // 622 // PM_GetPageAddress() - Returns the address of a given page 623 // Maps in EMS if necessary 624 // Returns nil if block isn't cached into Main Memory or EMS 625 // 626 // 627 memptr 628 PM_GetPageAddress(int pagenum) 629 { 630 PageListStruct far *page; 631 632 page = &PMPages[pagenum]; 633 if (page->mainPage != -1) 634 return(MainMemPages[page->mainPage]); 635 else if (page->emsPage != -1) 636 return(PML_GetEMSAddress(page->emsPage,page->locked)); 637 else 638 return(nil); 639 } 640 641 // 642 // PML_GiveLRUPage() - Returns the page # of the least recently used 643 // present & unlocked main/EMS page (or main page if mainonly is true) 644 // 645 int 646 PML_GiveLRUPage(boolean mainonly) 647 { 648 int i,lru; 649 long last; 650 PageListStruct far *page; 651 652 for (i = 0,page = PMPages,lru = -1,last = MAXLONG;i < ChunksInFile;i++,page++) 653 { 654 if 655 ( 656 (page->lastHit < last) 657 && ((page->emsPage != -1) || (page->mainPage != -1)) 658 && (page->locked == pml_Unlocked) 659 && (!(mainonly && (page->mainPage == -1))) 660 ) 661 { 662 last = page->lastHit; 663 lru = i; 664 } 665 } 666 667 if (lru == -1) 668 Quit("PML_GiveLRUPage: LRU Search failed"); 669 return(lru); 670 } 671 672 // 673 // PML_GiveLRUXMSPage() - Returns the page # of the least recently used 674 // (and present) XMS page. 675 // This routine won't return the XMS page protected (by XMSProtectPage) 676 // 677 int 678 PML_GiveLRUXMSPage(void) 679 { 680 int i,lru; 681 long last; 682 PageListStruct far *page; 683 684 for (i = 0,page = PMPages,lru = -1,last = MAXLONG;i < ChunksInFile;i++,page++) 685 { 686 if 687 ( 688 (page->xmsPage != -1) 689 && (page->lastHit < last) 690 && (i != XMSProtectPage) 691 ) 692 { 693 last = page->lastHit; 694 lru = i; 695 } 696 } 697 return(lru); 698 } 699 700 // 701 // PML_PutPageInXMS() - If page isn't in XMS, find LRU XMS page and replace 702 // it with the main/EMS page 703 // 704 void 705 PML_PutPageInXMS(int pagenum) 706 { 707 int usexms; 708 PageListStruct far *page; 709 710 if (!XMSPresent) 711 return; 712 713 page = &PMPages[pagenum]; 714 if (page->xmsPage != -1) 715 return; // Already in XMS 716 717 if (XMSPagesUsed < XMSPagesAvail) 718 page->xmsPage = XMSPagesUsed++; 719 else 720 { 721 usexms = PML_GiveLRUXMSPage(); 722 if (usexms == -1) 723 Quit("PML_PutPageInXMS: No XMS LRU"); 724 page->xmsPage = PMPages[usexms].xmsPage; 725 PMPages[usexms].xmsPage = -1; 726 } 727 PML_CopyToXMS(PM_GetPageAddress(pagenum),page->xmsPage,page->length); 728 } 729 730 // 731 // PML_TransferPageSpace() - A page is being replaced, so give the new page 732 // the old one's address space. Returns the address of the new page. 733 // 734 memptr 735 PML_TransferPageSpace(int orig,int new) 736 { 737 memptr addr; 738 PageListStruct far *origpage,far *newpage; 739 740 if (orig == new) 741 Quit("PML_TransferPageSpace: Identity replacement"); 742 743 origpage = &PMPages[orig]; 744 newpage = &PMPages[new]; 745 746 if (origpage->locked != pml_Unlocked) 747 Quit("PML_TransferPageSpace: Killing locked page"); 748 749 if ((origpage->emsPage == -1) && (origpage->mainPage == -1)) 750 Quit("PML_TransferPageSpace: Reusing non-existent page"); 751 752 // Copy page that's about to be purged into XMS 753 PML_PutPageInXMS(orig); 754 755 // Get the address, and force EMS into a physical page if necessary 756 addr = PM_GetPageAddress(orig); 757 758 // Steal the address 759 newpage->emsPage = origpage->emsPage; 760 newpage->mainPage = origpage->mainPage; 761 762 // Mark replaced page as purged 763 origpage->mainPage = origpage->emsPage = -1; 764 765 if (!addr) 766 Quit("PML_TransferPageSpace: Zero replacement"); 767 768 return(addr); 769 } 770 771 // 772 // PML_GetAPageBuffer() - A page buffer is needed. Either get it from the 773 // main/EMS free pool, or use PML_GiveLRUPage() to find which page to 774 // steal the buffer from. Returns a far pointer to the page buffer, and 775 // sets the fields inside the given page structure appropriately. 776 // If mainonly is true, free EMS will be ignored, and only main pages 777 // will be looked at by PML_GiveLRUPage(). 778 // 779 byte far * 780 PML_GetAPageBuffer(int pagenum,boolean mainonly) 781 { 782 byte far *addr = nil; 783 int i,n; 784 PMBlockAttr *used; 785 PageListStruct far *page; 786 787 page = &PMPages[pagenum]; 788 if ((EMSPagesUsed < EMSPagesAvail) && !mainonly) 789 { 790 // There's remaining EMS - use it 791 page->emsPage = EMSPagesUsed++; 792 addr = PML_GetEMSAddress(page->emsPage,page->locked); 793 } 794 else if (MainPagesUsed < MainPagesAvail) 795 { 796 // There's remaining main memory - use it 797 for (i = 0,n = -1,used = MainMemUsed;i < PMMaxMainMem;i++,used++) 798 { 799 if ((*used & pmba_Allocated) && !(*used & pmba_Used)) 800 { 801 n = i; 802 *used |= pmba_Used; 803 break; 804 } 805 } 806 if (n == -1) 807 Quit("PML_GetPageBuffer: MainPagesAvail lied"); 808 addr = MainMemPages[n]; 809 if (!addr) 810 Quit("PML_GetPageBuffer: Purged main block"); 811 page->mainPage = n; 812 MainPagesUsed++; 813 } 814 else 815 addr = PML_TransferPageSpace(PML_GiveLRUPage(mainonly),pagenum); 816 817 if (!addr) 818 Quit("PML_GetPageBuffer: Search failed"); 819 return(addr); 820 } 821 822 // 823 // PML_GetPageFromXMS() - If page is in XMS, find LRU main/EMS page and 824 // replace it with the page from XMS. If mainonly is true, will only 825 // search for LRU main page. 826 // XMSProtectPage is set to the page to be retrieved from XMS, so that if 827 // the page from which we're stealing the main/EMS from isn't in XMS, 828 // it won't copy over the page that we're trying to get from XMS. 829 // (pages that are being purged are copied into XMS, if possible) 830 // 831 memptr 832 PML_GetPageFromXMS(int pagenum,boolean mainonly) 833 { 834 byte far *checkaddr; 835 memptr addr = nil; 836 PageListStruct far *page; 837 838 page = &PMPages[pagenum]; 839 if (XMSPresent && (page->xmsPage != -1)) 840 { 841 XMSProtectPage = pagenum; 842 checkaddr = PML_GetAPageBuffer(pagenum,mainonly); 843 if (FP_OFF(checkaddr)) 844 Quit("PML_GetPageFromXMS: Non segment pointer"); 845 addr = (memptr)FP_SEG(checkaddr); 846 PML_CopyFromXMS(addr,page->xmsPage,page->length); 847 XMSProtectPage = -1; 848 } 849 850 return(addr); 851 } 852 853 // 854 // PML_LoadPage() - A page is not in main/EMS memory, and it's not in XMS. 855 // Load it into either main or EMS. If mainonly is true, the page will 856 // only be loaded into main. 857 // 858 void 859 PML_LoadPage(int pagenum,boolean mainonly) 860 { 861 byte far *addr; 862 PageListStruct far *page; 863 864 addr = PML_GetAPageBuffer(pagenum,mainonly); 865 page = &PMPages[pagenum]; 866 PML_ReadFromFile(addr,page->offset,page->length); 867 } 868 869 // 870 // PM_GetPage() - Returns the address of the page, loading it if necessary 871 // First, check if in Main Memory or EMS 872 // Then, check XMS 873 // If not in XMS, load into Main Memory or EMS 874 // 875 #pragma warn -pia 876 memptr 877 PM_GetPage(int pagenum) 878 { 879 memptr result; 880 881 if (pagenum >= ChunksInFile) 882 Quit("PM_GetPage: Invalid page request"); 883 884 #if 0 // for debugging 885 asm mov dx,STATUS_REGISTER_1 886 asm in al,dx 887 asm mov dx,ATR_INDEX 888 asm mov al,ATR_OVERSCAN 889 asm out dx,al 890 asm mov al,10 // bright green 891 asm out dx,al 892 #endif 893 894 if (!(result = PM_GetPageAddress(pagenum))) 895 { 896 boolean mainonly = (pagenum >= PMSoundStart); 897 if (!PMPages[pagenum].offset) // JDC: sparse page 898 Quit ("Tried to load a sparse page!"); 899 if (!(result = PML_GetPageFromXMS(pagenum,mainonly))) 900 { 901 if (PMPages[pagenum].lastHit == PMFrameCount) 902 PMThrashing++; 903 904 PML_LoadPage(pagenum,mainonly); 905 result = PM_GetPageAddress(pagenum); 906 } 907 } 908 PMPages[pagenum].lastHit = PMFrameCount; 909 910 #if 0 // for debugging 911 asm mov dx,STATUS_REGISTER_1 912 asm in al,dx 913 asm mov dx,ATR_INDEX 914 asm mov al,ATR_OVERSCAN 915 asm out dx,al 916 asm mov al,3 // blue 917 asm out dx,al 918 asm mov al,0x20 // normal 919 asm out dx,al 920 #endif 921 922 return(result); 923 } 924 #pragma warn +pia 925 926 // 927 // PM_SetPageLock() - Sets the lock type on a given page 928 // pml_Unlocked: Normal, page can be purged 929 // pml_Locked: Cannot be purged 930 // pml_EMS?: Same as pml_Locked, but if in EMS, use the physical page 931 // specified when returning the address. For sound stuff. 932 // 933 void 934 PM_SetPageLock(int pagenum,PMLockType lock) 935 { 936 if (pagenum < PMSoundStart) 937 Quit("PM_SetPageLock: Locking/unlocking non-sound page"); 938 939 PMPages[pagenum].locked = lock; 940 } 941 942 // 943 // PM_Preload() - Loads as many pages as possible into all types of memory. 944 // Calls the update function after each load, indicating the current 945 // page, and the total pages that need to be loaded (for thermometer). 946 // 947 void 948 PM_Preload(boolean (*update)(word current,word total)) 949 { 950 int i,j, 951 page,oogypage; 952 word current,total, 953 totalnonxms,totalxms, 954 mainfree,maintotal, 955 emsfree,emstotal, 956 xmsfree,xmstotal; 957 memptr addr; 958 PageListStruct far *p; 959 960 mainfree = (MainPagesAvail - MainPagesUsed) + (EMSPagesAvail - EMSPagesUsed); 961 xmsfree = (XMSPagesAvail - XMSPagesUsed); 962 963 xmstotal = maintotal = 0; 964 965 for (i = 0;i < ChunksInFile;i++) 966 { 967 if (!PMPages[i].offset) 968 continue; // sparse 969 970 if ( PMPages[i].emsPage != -1 || PMPages[i].mainPage != -1 ) 971 continue; // already in main mem 972 973 if ( mainfree ) 974 { 975 maintotal++; 976 mainfree--; 977 } 978 else if ( xmsfree && (PMPages[i].xmsPage == -1) ) 979 { 980 xmstotal++; 981 xmsfree--; 982 } 983 } 984 985 986 total = maintotal + xmstotal; 987 988 if (!total) 989 return; 990 991 page = 0; 992 current = 0; 993 994 // 995 // cache main/ems blocks 996 // 997 while (maintotal) 998 { 999 while ( !PMPages[page].offset || PMPages[page].mainPage != -1 1000 || PMPages[page].emsPage != -1 ) 1001 page++; 1002 1003 if (page >= ChunksInFile) 1004 Quit ("PM_Preload: Pages>=ChunksInFile"); 1005 1006 PM_GetPage(page); 1007 1008 page++; 1009 current++; 1010 maintotal--; 1011 update(current,total); 1012 } 1013 1014 // 1015 // load stuff to XMS 1016 // 1017 if (xmstotal) 1018 { 1019 for (oogypage = 0 ; PMPages[oogypage].mainPage == -1 ; oogypage++) 1020 ; 1021 addr = PM_GetPage(oogypage); 1022 if (!addr) 1023 Quit("PM_Preload: XMS buffer failed"); 1024 1025 while (xmstotal) 1026 { 1027 while ( !PMPages[page].offset || PMPages[page].xmsPage != -1 ) 1028 page++; 1029 1030 if (page >= ChunksInFile) 1031 Quit ("PM_Preload: Pages>=ChunksInFile"); 1032 1033 p = &PMPages[page]; 1034 1035 p->xmsPage = XMSPagesUsed++; 1036 if (XMSPagesUsed > XMSPagesAvail) 1037 Quit("PM_Preload: Exceeded XMS pages"); 1038 if (p->length > PMPageSize) 1039 Quit("PM_Preload: Page too long"); 1040 1041 PML_ReadFromFile((byte far *)addr,p->offset,p->length); 1042 PML_CopyToXMS((byte far *)addr,p->xmsPage,p->length); 1043 1044 page++; 1045 current++; 1046 xmstotal--; 1047 update(current,total); 1048 } 1049 1050 p = &PMPages[oogypage]; 1051 PML_ReadFromFile((byte far *)addr,p->offset,p->length); 1052 } 1053 1054 update(total,total); 1055 } 1056 1057 ///////////////////////////////////////////////////////////////////////////// 1058 // 1059 // General code 1060 // 1061 ///////////////////////////////////////////////////////////////////////////// 1062 1063 // 1064 // PM_NextFrame() - Increments the frame counter and adjusts the thrash 1065 // avoidence variables 1066 // 1067 // If currently in panic mode (to avoid thrashing), check to see if the 1068 // appropriate number of frames have passed since the last time that 1069 // we would have thrashed. If so, take us out of panic mode. 1070 // 1071 // 1072 void 1073 PM_NextFrame(void) 1074 { 1075 int i; 1076 1077 // Frame count overrun - kill the LRU hit entries & reset frame count 1078 if (++PMFrameCount >= MAXLONG - 4) 1079 { 1080 for (i = 0;i < PMNumBlocks;i++) 1081 PMPages[i].lastHit = 0; 1082 PMFrameCount = 0; 1083 } 1084 1085 #if 0 1086 for (i = 0;i < PMSoundStart;i++) 1087 { 1088 if (PMPages[i].locked) 1089 { 1090 char buf[40]; 1091 sprintf(buf,"PM_NextFrame: Page %d is locked",i); 1092 Quit(buf); 1093 } 1094 } 1095 #endif 1096 1097 if (PMPanicMode) 1098 { 1099 // DEBUG - set border color 1100 if ((!PMThrashing) && (!--PMPanicMode)) 1101 { 1102 // DEBUG - reset border color 1103 } 1104 } 1105 if (PMThrashing >= PMThrashThreshold) 1106 PMPanicMode = PMUnThrashThreshold; 1107 PMThrashing = false; 1108 } 1109 1110 // 1111 // PM_Reset() - Sets up caching structures 1112 // 1113 void 1114 PM_Reset(void) 1115 { 1116 int i; 1117 PageListStruct far *page; 1118 1119 XMSPagesAvail = XMSAvail / PMPageSizeKB; 1120 1121 EMSPagesAvail = EMSAvail * (EMSPageSizeKB / PMPageSizeKB); 1122 EMSPhysicalPage = 0; 1123 1124 MainPagesUsed = EMSPagesUsed = XMSPagesUsed = 0; 1125 1126 PMPanicMode = false; 1127 1128 // Initialize page list 1129 for (i = 0,page = PMPages;i < PMNumBlocks;i++,page++) 1130 { 1131 page->mainPage = -1; 1132 page->emsPage = -1; 1133 page->xmsPage = -1; 1134 page->locked = false; 1135 } 1136 } 1137 1138 // 1139 // PM_Startup() - Start up the Page Mgr 1140 // 1141 void 1142 PM_Startup(void) 1143 { 1144 boolean nomain,noems,noxms; 1145 int i; 1146 1147 if (PMStarted) 1148 return; 1149 1150 nomain = noems = noxms = false; 1151 for (i = 1;i < _argc;i++) 1152 { 1153 switch (US_CheckParm(_argv[i],ParmStrings)) 1154 { 1155 case 0: 1156 nomain = true; 1157 break; 1158 case 1: 1159 noems = true; 1160 break; 1161 case 2: 1162 noxms = true; 1163 break; 1164 } 1165 } 1166 1167 PML_OpenPageFile(); 1168 1169 if (!noems) 1170 PML_StartupEMS(); 1171 if (!noxms) 1172 PML_StartupXMS(); 1173 1174 if (nomain && !EMSPresent) 1175 Quit("PM_Startup: No main or EMS"); 1176 else 1177 PML_StartupMainMem(); 1178 1179 PM_Reset(); 1180 1181 PMStarted = true; 1182 } 1183 1184 // 1185 // PM_Shutdown() - Shut down the Page Mgr 1186 // 1187 void 1188 PM_Shutdown(void) 1189 { 1190 PML_ShutdownXMS(); 1191 PML_ShutdownEMS(); 1192 1193 if (!PMStarted) 1194 return; 1195 1196 PML_ClosePageFile(); 1197 1198 PML_ShutdownMainMem(); 1199 }