1 /* 2 * COPYRIGHT: GPL - See COPYING in the top level directory 3 * PROJECT: ReactOS Virtual DOS Machine 4 * FILE: subsystems/mvdm/ntvdm/dos/dos32krnl/memory.c 5 * PURPOSE: DOS32 Memory Manager 6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org> 7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr) 8 */ 9 10 /* INCLUDES *******************************************************************/ 11 12 #include "ntvdm.h" 13 14 #define NDEBUG 15 #include <debug.h> 16 17 #include "emulator.h" 18 19 #include "bios/umamgr.h" // HACK until we correctly call XMS services for UMBs. 20 21 #include "dos.h" 22 #include "dos/dem.h" 23 #include "memory.h" 24 #include "process.h" 25 #include "himem.h" 26 27 // FIXME: Should be dynamically initialized! 28 #define FIRST_MCB_SEGMENT (SYSTEM_ENV_BLOCK + 0x200) // old value: 0x1000 29 #define USER_MEMORY_SIZE (0x9FFE - FIRST_MCB_SEGMENT) 30 31 /* 32 * Activate this line if you want run-time DOS memory arena integrity validation 33 * (useful to know whether this is an application, or DOS kernel itself, which 34 * messes up the DOS memory arena). 35 */ 36 // #define DBG_MEMORY 37 38 /* PRIVATE VARIABLES **********************************************************/ 39 40 /* PUBLIC VARIABLES ***********************************************************/ 41 42 /* PRIVATE FUNCTIONS **********************************************************/ 43 44 static inline BOOLEAN ValidateMcb(PDOS_MCB Mcb) 45 { 46 return (Mcb->BlockType == 'M' || Mcb->BlockType == 'Z'); 47 } 48 49 /* 50 * This is a helper function to help us detecting 51 * when the DOS arena starts to become corrupted. 52 */ 53 #ifdef DBG_MEMORY 54 static VOID DosMemValidate(VOID) 55 { 56 WORD PrevSegment, Segment = SysVars->FirstMcb; 57 PDOS_MCB CurrentMcb; 58 59 PrevSegment = Segment; 60 while (TRUE) 61 { 62 /* Get a pointer to the MCB */ 63 CurrentMcb = SEGMENT_TO_MCB(Segment); 64 65 /* Make sure it's valid */ 66 if (!ValidateMcb(CurrentMcb)) 67 { 68 DPRINT1("The DOS memory arena is corrupted! (CurrentMcb = 0x%04X; PreviousMcb = 0x%04X)\n", Segment, PrevSegment); 69 return; 70 } 71 72 PrevSegment = Segment; 73 74 /* If this was the last MCB in the chain, quit */ 75 if (CurrentMcb->BlockType == 'Z') return; 76 77 /* Otherwise, update the segment and continue */ 78 Segment += CurrentMcb->Size + 1; 79 } 80 } 81 #else 82 #define DosMemValidate() 83 #endif 84 85 static VOID DosCombineFreeBlocks(WORD StartBlock) 86 { 87 /* NOTE: This function is always called with valid MCB blocks */ 88 89 PDOS_MCB CurrentMcb = SEGMENT_TO_MCB(StartBlock), NextMcb; 90 91 /* If the block is not free, quit */ 92 if (CurrentMcb->OwnerPsp != 0) return; 93 94 /* 95 * Loop while the current block is not the last one. It can happen 96 * that the block is not the last one at the beginning, but becomes 97 * the last one at the end of the process. This happens in the case 98 * where its next following blocks are free but not combined yet, 99 * and they are terminated by a free last block. During the process 100 * all the blocks are combined together and we end up in the situation 101 * where the current (free) block is followed by the last (free) block. 102 * At the last step of the algorithm the current block becomes the 103 * last one. 104 */ 105 while (CurrentMcb->BlockType != 'Z') 106 { 107 /* Get a pointer to the next MCB */ 108 NextMcb = SEGMENT_TO_MCB(StartBlock + CurrentMcb->Size + 1); 109 110 /* Make sure it's valid */ 111 if (!ValidateMcb(NextMcb)) 112 { 113 DPRINT1("The DOS memory arena is corrupted!\n"); 114 Sda->LastErrorCode = ERROR_ARENA_TRASHED; 115 return; 116 } 117 118 /* Check if the next MCB is free */ 119 if (NextMcb->OwnerPsp == 0) 120 { 121 /* Combine them */ 122 CurrentMcb->Size += NextMcb->Size + 1; 123 CurrentMcb->BlockType = NextMcb->BlockType; 124 NextMcb->BlockType = 'I'; 125 } 126 else 127 { 128 /* No more adjoining free blocks */ 129 break; 130 } 131 } 132 } 133 134 /* PUBLIC FUNCTIONS ***********************************************************/ 135 136 WORD DosAllocateMemory(WORD Size, WORD *MaxAvailable) 137 { 138 WORD Result = 0, Segment = SysVars->FirstMcb, MaxSize = 0; 139 PDOS_MCB CurrentMcb; 140 BOOLEAN SearchUmb = FALSE; 141 142 DPRINT("DosAllocateMemory: Size 0x%04X\n", Size); 143 144 DosMemValidate(); 145 146 if (SysVars->UmbLinked && SysVars->UmbChainStart != 0xFFFF && 147 (Sda->AllocStrategy & (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW))) 148 { 149 /* Search UMB first */ 150 Segment = SysVars->UmbChainStart; 151 SearchUmb = TRUE; 152 } 153 154 while (TRUE) 155 { 156 /* Get a pointer to the MCB */ 157 CurrentMcb = SEGMENT_TO_MCB(Segment); 158 159 /* Make sure it's valid */ 160 if (!ValidateMcb(CurrentMcb)) 161 { 162 DPRINT1("The DOS memory arena is corrupted!\n"); 163 Sda->LastErrorCode = ERROR_ARENA_TRASHED; 164 return 0; 165 } 166 167 /* Only check free blocks */ 168 if (CurrentMcb->OwnerPsp != 0) goto Next; 169 170 /* Combine this free block with adjoining free blocks */ 171 DosCombineFreeBlocks(Segment); 172 173 /* Update the maximum block size */ 174 if (CurrentMcb->Size > MaxSize) MaxSize = CurrentMcb->Size; 175 176 /* Check if this block is big enough */ 177 if (CurrentMcb->Size < Size) goto Next; 178 179 switch (Sda->AllocStrategy & ~(DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW)) 180 { 181 case DOS_ALLOC_FIRST_FIT: 182 { 183 /* For first fit, stop immediately */ 184 Result = Segment; 185 goto Done; 186 } 187 188 case DOS_ALLOC_BEST_FIT: 189 { 190 /* For best fit, update the smallest block found so far */ 191 if ((Result == 0) || (CurrentMcb->Size < SEGMENT_TO_MCB(Result)->Size)) 192 { 193 Result = Segment; 194 } 195 196 break; 197 } 198 199 case DOS_ALLOC_LAST_FIT: 200 { 201 /* For last fit, make the current block the result, but keep searching */ 202 Result = Segment; 203 break; 204 } 205 } 206 207 Next: 208 /* If this was the last MCB in the chain, quit */ 209 if (CurrentMcb->BlockType == 'Z') 210 { 211 /* Check if nothing was found while searching through UMBs */ 212 if ((Result == 0) && SearchUmb && (Sda->AllocStrategy & DOS_ALLOC_HIGH_LOW)) 213 { 214 /* Search low memory */ 215 Segment = SysVars->FirstMcb; 216 SearchUmb = FALSE; 217 continue; 218 } 219 220 break; 221 } 222 223 /* Otherwise, update the segment and continue */ 224 Segment += CurrentMcb->Size + 1; 225 } 226 227 Done: 228 DosMemValidate(); 229 230 /* If we didn't find a free block, bail out */ 231 if (Result == 0) 232 { 233 DPRINT("DosAllocateMemory FAILED. Maximum available: 0x%04X\n", MaxSize); 234 Sda->LastErrorCode = ERROR_NOT_ENOUGH_MEMORY; 235 if (MaxAvailable) *MaxAvailable = MaxSize; 236 return 0; 237 } 238 239 /* Get a pointer to the MCB */ 240 CurrentMcb = SEGMENT_TO_MCB(Result); 241 242 /* Check if the block is larger than requested */ 243 if (CurrentMcb->Size > Size) 244 { 245 /* It is, split it into two blocks */ 246 if ((Sda->AllocStrategy & ~(DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW)) != DOS_ALLOC_LAST_FIT) 247 { 248 PDOS_MCB NextMcb = SEGMENT_TO_MCB(Result + Size + 1); 249 250 /* Initialize the new MCB structure */ 251 NextMcb->BlockType = CurrentMcb->BlockType; 252 NextMcb->Size = CurrentMcb->Size - Size - 1; 253 NextMcb->OwnerPsp = 0; 254 255 /* Update the current block */ 256 CurrentMcb->BlockType = 'M'; 257 CurrentMcb->Size = Size; 258 } 259 else 260 { 261 /* Save the location of the current MCB */ 262 PDOS_MCB PreviousMcb = CurrentMcb; 263 264 /* Move the current MCB higher */ 265 Result += CurrentMcb->Size - Size; 266 CurrentMcb = SEGMENT_TO_MCB(Result); 267 268 /* Initialize the new MCB structure */ 269 CurrentMcb->BlockType = PreviousMcb->BlockType; 270 CurrentMcb->Size = Size; 271 CurrentMcb->OwnerPsp = 0; 272 273 /* Update the previous block */ 274 PreviousMcb->BlockType = 'M'; 275 PreviousMcb->Size -= Size + 1; 276 } 277 } 278 279 /* Take ownership of the block */ 280 CurrentMcb->OwnerPsp = Sda->CurrentPsp; 281 RtlCopyMemory(CurrentMcb->Name, SEGMENT_TO_MCB(Sda->CurrentPsp - 1)->Name, sizeof(CurrentMcb->Name)); 282 283 DosMemValidate(); 284 285 /* Return the segment of the data portion of the block */ 286 return Result + 1; 287 } 288 289 BOOLEAN DosResizeMemory(WORD BlockData, WORD NewSize, WORD *MaxAvailable) 290 { 291 BOOLEAN Success = TRUE; 292 WORD Segment = BlockData - 1, ReturnSize = 0, NextSegment; 293 PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment), NextMcb; 294 295 DPRINT("DosResizeMemory: BlockData 0x%04X, NewSize 0x%04X\n", 296 BlockData, NewSize); 297 298 DosMemValidate(); 299 300 /* Make sure this is a valid and allocated block */ 301 if (BlockData == 0 || !ValidateMcb(Mcb) || Mcb->OwnerPsp == 0) 302 { 303 Sda->LastErrorCode = ERROR_INVALID_BLOCK; 304 Success = FALSE; 305 goto Done; 306 } 307 308 ReturnSize = Mcb->Size; 309 310 /* Check if we need to expand or contract the block */ 311 if (NewSize > Mcb->Size) 312 { 313 /* We can't expand the last block */ 314 if (Mcb->BlockType == 'Z') 315 { 316 DPRINT("Cannot expand memory block 0x%04X: this is the last block (size 0x%04X)!\n", Segment, Mcb->Size); 317 Sda->LastErrorCode = ERROR_NOT_ENOUGH_MEMORY; 318 Success = FALSE; 319 goto Done; 320 } 321 322 /* Get the pointer and segment of the next MCB */ 323 NextSegment = Segment + Mcb->Size + 1; 324 NextMcb = SEGMENT_TO_MCB(NextSegment); 325 326 /* Make sure it's valid */ 327 if (!ValidateMcb(NextMcb)) 328 { 329 DPRINT1("The DOS memory arena is corrupted!\n"); 330 Sda->LastErrorCode = ERROR_ARENA_TRASHED; 331 return FALSE; 332 } 333 334 /* Make sure the next segment is free */ 335 if (NextMcb->OwnerPsp != 0) 336 { 337 DPRINT("Cannot expand memory block 0x%04X: next segment is not free!\n", Segment); 338 Sda->LastErrorCode = ERROR_NOT_ENOUGH_MEMORY; 339 Success = FALSE; 340 goto Done; 341 } 342 343 /* Combine this free block with adjoining free blocks */ 344 DosCombineFreeBlocks(NextSegment); 345 346 /* Set the maximum possible size of the block */ 347 ReturnSize += NextMcb->Size + 1; 348 349 if (ReturnSize < NewSize) 350 { 351 DPRINT("Cannot expand memory block 0x%04X: insufficient free segments available!\n", Segment); 352 Sda->LastErrorCode = ERROR_NOT_ENOUGH_MEMORY; 353 Success = FALSE; 354 goto Done; 355 } 356 357 /* Maximize the current block */ 358 Mcb->Size = ReturnSize; 359 Mcb->BlockType = NextMcb->BlockType; 360 361 /* Invalidate the next block */ 362 NextMcb->BlockType = 'I'; 363 364 /* Check if the block is larger than requested */ 365 if (Mcb->Size > NewSize) 366 { 367 DPRINT("Block too large, reducing size from 0x%04X to 0x%04X\n", 368 Mcb->Size, NewSize); 369 370 /* It is, split it into two blocks */ 371 NextMcb = SEGMENT_TO_MCB(Segment + NewSize + 1); 372 373 /* Initialize the new MCB structure */ 374 NextMcb->BlockType = Mcb->BlockType; 375 NextMcb->Size = Mcb->Size - NewSize - 1; 376 NextMcb->OwnerPsp = 0; 377 378 /* Update the current block */ 379 Mcb->BlockType = 'M'; 380 Mcb->Size = NewSize; 381 } 382 } 383 else if (NewSize < Mcb->Size) 384 { 385 DPRINT("Shrinking block from 0x%04X to 0x%04X\n", 386 Mcb->Size, NewSize); 387 388 /* Just split the block */ 389 NextSegment = Segment + NewSize + 1; 390 NextMcb = SEGMENT_TO_MCB(NextSegment); 391 NextMcb->BlockType = Mcb->BlockType; 392 NextMcb->Size = Mcb->Size - NewSize - 1; 393 NextMcb->OwnerPsp = 0; 394 395 /* Update the MCB */ 396 Mcb->BlockType = 'M'; 397 Mcb->Size = NewSize; 398 399 /* Combine this free block with adjoining free blocks */ 400 DosCombineFreeBlocks(NextSegment); 401 } 402 403 Done: 404 /* Check if the operation failed */ 405 if (!Success) 406 { 407 DPRINT("DosResizeMemory FAILED. Maximum available: 0x%04X\n", ReturnSize); 408 409 /* Return the maximum possible size */ 410 if (MaxAvailable) *MaxAvailable = ReturnSize; 411 } 412 413 DosMemValidate(); 414 415 return Success; 416 } 417 418 BOOLEAN DosFreeMemory(WORD BlockData) 419 { 420 PDOS_MCB Mcb = SEGMENT_TO_MCB(BlockData - 1); 421 422 DPRINT("DosFreeMemory: BlockData 0x%04X\n", BlockData); 423 424 if (BlockData == 0) 425 { 426 Sda->LastErrorCode = ERROR_INVALID_BLOCK; 427 return FALSE; 428 } 429 430 DosMemValidate(); 431 432 /* Make sure the MCB is valid */ 433 if (!ValidateMcb(Mcb)) 434 { 435 DPRINT("MCB block type '%c' not valid!\n", Mcb->BlockType); 436 Sda->LastErrorCode = ERROR_INVALID_BLOCK; 437 return FALSE; 438 } 439 440 /* Mark the block as free */ 441 Mcb->OwnerPsp = 0; 442 443 return TRUE; 444 } 445 446 BOOLEAN DosLinkUmb(VOID) 447 { 448 DWORD Segment = SysVars->FirstMcb; 449 PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment); 450 451 DPRINT("Linking UMB\n"); 452 453 /* Check if UMBs are initialized and already linked */ 454 if (SysVars->UmbChainStart == 0xFFFF) return FALSE; 455 if (SysVars->UmbLinked) return TRUE; 456 457 DosMemValidate(); 458 459 /* Find the last block before the start of the UMB chain */ 460 while (Segment < SysVars->UmbChainStart) 461 { 462 /* Get a pointer to the MCB */ 463 Mcb = SEGMENT_TO_MCB(Segment); 464 465 /* Make sure it's valid */ 466 if (!ValidateMcb(Mcb)) 467 { 468 DPRINT1("The DOS memory arena is corrupted!\n"); 469 Sda->LastErrorCode = ERROR_ARENA_TRASHED; 470 return FALSE; 471 } 472 473 /* If this was the last MCB in the chain, quit */ 474 if (Mcb->BlockType == 'Z') break; 475 476 /* Otherwise, update the segment and continue */ 477 Segment += Mcb->Size + 1; 478 } 479 480 /* Make sure it's valid */ 481 if (Mcb->BlockType != 'Z') return FALSE; 482 483 /* Connect the MCB with the UMB chain */ 484 Mcb->BlockType = 'M'; 485 486 DosMemValidate(); 487 488 SysVars->UmbLinked = TRUE; 489 return TRUE; 490 } 491 492 BOOLEAN DosUnlinkUmb(VOID) 493 { 494 DWORD Segment = SysVars->FirstMcb; 495 PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment); 496 497 DPRINT("Unlinking UMB\n"); 498 499 /* Check if UMBs are initialized and already unlinked */ 500 if (SysVars->UmbChainStart == 0xFFFF) return FALSE; 501 if (!SysVars->UmbLinked) return TRUE; 502 503 DosMemValidate(); 504 505 /* Find the last block before the start of the UMB chain */ 506 while (Segment < SysVars->UmbChainStart) 507 { 508 /* Get a pointer to the MCB */ 509 Mcb = SEGMENT_TO_MCB(Segment); 510 511 /* Make sure it's valid */ 512 if (!ValidateMcb(Mcb)) 513 { 514 DPRINT1("The DOS memory arena is corrupted!\n"); 515 Sda->LastErrorCode = ERROR_ARENA_TRASHED; 516 return FALSE; 517 } 518 519 /* Advance to the next MCB */ 520 Segment += Mcb->Size + 1; 521 } 522 523 /* Mark the MCB as the last MCB */ 524 Mcb->BlockType = 'Z'; 525 526 DosMemValidate(); 527 528 SysVars->UmbLinked = FALSE; 529 return TRUE; 530 } 531 532 VOID DosChangeMemoryOwner(WORD Segment, WORD NewOwner) 533 { 534 PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment - 1); 535 Mcb->OwnerPsp = NewOwner; 536 } 537 538 /* 539 * Some information about DOS UMBs: 540 * http://textfiles.com/virus/datut010.txt 541 * http://www.asmcommunity.net/forums/topic/?id=30884 542 */ 543 544 WORD DosGetPreviousUmb(WORD UmbSegment) 545 { 546 PDOS_MCB CurrentMcb; 547 WORD Segment, PrevSegment = 0; // FIXME: or use UmbChainStart ?? 548 549 if (SysVars->UmbChainStart == 0xFFFF) 550 return 0; 551 552 /* Start scanning the UMB chain */ 553 Segment = SysVars->UmbChainStart; 554 while (TRUE) 555 { 556 /* Get a pointer to the MCB */ 557 CurrentMcb = SEGMENT_TO_MCB(Segment); 558 559 /* Make sure it's valid */ 560 if (!ValidateMcb(CurrentMcb)) 561 { 562 DPRINT1("The UMB DOS memory arena is corrupted!\n"); 563 Sda->LastErrorCode = ERROR_ARENA_TRASHED; 564 return 0; 565 } 566 567 /* We went over the UMB segment, quit */ 568 if (Segment >= UmbSegment) break; 569 570 PrevSegment = Segment; 571 572 /* If this was the last MCB in the chain, quit */ 573 if (CurrentMcb->BlockType == 'Z') break; 574 575 /* Otherwise, update the segment and continue */ 576 Segment += CurrentMcb->Size + 1; 577 } 578 579 return PrevSegment; 580 } 581 582 VOID DosInitializeUmb(VOID) 583 { 584 BOOLEAN Result; 585 USHORT UmbSegment = 0x0000, PrevSegment; 586 USHORT Size; 587 PDOS_MCB Mcb, PrevMcb; 588 589 ASSERT(SysVars->UmbChainStart == 0xFFFF); 590 591 // SysVars->UmbLinked = FALSE; 592 593 /* Try to allocate all the UMBs */ 594 while (TRUE) 595 { 596 /* Find the maximum amount of memory that can be allocated */ 597 Size = 0xFFFF; 598 Result = UmaDescReserve(&UmbSegment, &Size); 599 600 /* If we are out of UMBs, bail out */ 601 if (!Result && Size == 0) // XMS_STATUS_OUT_OF_UMBS 602 break; 603 604 /* We should not have succeeded! */ 605 ASSERT(!Result); 606 607 /* 'Size' now contains the size of the biggest UMB block. Request it. */ 608 Result = UmaDescReserve(&UmbSegment, &Size); 609 ASSERT(Result); // XMS_STATUS_SUCCESS 610 611 /* If this is our first UMB block, initialize the UMB chain */ 612 if (SysVars->UmbChainStart == 0xFFFF) 613 { 614 /* Initialize the link MCB to the UMB area */ 615 // NOTE: We use the fact that UmbChainStart is still == 0xFFFF 616 // so that we initialize this block from 9FFF:0000 up to FFFF:000F. 617 // It will be splitted as needed just below. 618 Mcb = SEGMENT_TO_MCB(SysVars->FirstMcb + USER_MEMORY_SIZE + 1); // '+1': Readjust the fact that USER_MEMORY_SIZE is based using 0x9FFE instead of 0x9FFF 619 Mcb->BlockType = 'Z'; // At the moment it is really the last block 620 Mcb->Size = (SysVars->UmbChainStart /* UmbSegment */ - SysVars->FirstMcb - USER_MEMORY_SIZE - 2) + 1; 621 Mcb->OwnerPsp = SYSTEM_PSP; 622 RtlCopyMemory(Mcb->Name, "SC ", sizeof("SC ")-1); 623 624 #if 0 // Keep here for reference; this will be deleted as soon as it becomes unneeded. 625 /* Initialize the UMB area */ 626 Mcb = SEGMENT_TO_MCB(SysVars->UmbChainStart); 627 Mcb->Size = UMB_END_SEGMENT - SysVars->UmbChainStart; 628 #endif 629 630 // FIXME: We should adjust the size of the previous block!! 631 632 /* Initialize the start of the UMB chain */ 633 SysVars->UmbChainStart = SysVars->FirstMcb + USER_MEMORY_SIZE + 1; 634 } 635 636 /* Split the block */ 637 638 /* Get the previous block */ 639 PrevSegment = DosGetPreviousUmb(UmbSegment); 640 PrevMcb = SEGMENT_TO_MCB(PrevSegment); 641 642 /* Initialize the next block */ 643 Mcb = SEGMENT_TO_MCB(UmbSegment + /*Mcb->Size*/(Size - 1) + 0); 644 // Mcb->BlockType = 'Z'; // FIXME: What if this block happens to be the last one?? 645 Mcb->BlockType = PrevMcb->BlockType; 646 Mcb->Size = PrevMcb->Size - (UmbSegment + Size - PrevSegment) + 1; 647 Mcb->OwnerPsp = PrevMcb->OwnerPsp; 648 RtlCopyMemory(Mcb->Name, PrevMcb->Name, sizeof(PrevMcb->Name)); 649 650 /* The previous block is not the latest one anymore */ 651 PrevMcb->BlockType = 'M'; 652 PrevMcb->Size = UmbSegment - PrevSegment - 1; 653 654 /* Initialize the new UMB block */ 655 Mcb = SEGMENT_TO_MCB(UmbSegment); 656 Mcb->BlockType = 'M'; // 'Z' // FIXME: What if this block happens to be the last one?? 657 Mcb->Size = Size - 1 - 1; // minus 2 because we need to have one arena at the beginning and one at the end. 658 Mcb->OwnerPsp = 0; 659 // FIXME: Which MCB name should we use? I need to explore more the docs! 660 RtlCopyMemory(Mcb->Name, "UMB ", sizeof("UMB ")-1); 661 // RtlCopyMemory(Mcb->Name, "SM ", sizeof("SM ")-1); 662 } 663 } 664 665 VOID DosInitializeMemory(VOID) 666 { 667 PDOS_MCB Mcb; 668 669 /* Set the initial allocation strategy to "best fit" */ 670 Sda->AllocStrategy = DOS_ALLOC_BEST_FIT; 671 672 /* Initialize conventional memory; we don't have UMBs yet */ 673 SysVars->FirstMcb = FIRST_MCB_SEGMENT; // The Arena Head 674 SysVars->UmbLinked = FALSE; 675 SysVars->UmbChainStart = 0xFFFF; 676 677 Mcb = SEGMENT_TO_MCB(SysVars->FirstMcb); 678 679 /* Initialize the MCB */ 680 Mcb->BlockType = 'Z'; 681 Mcb->Size = USER_MEMORY_SIZE; 682 Mcb->OwnerPsp = 0; 683 } 684 685 /* EOF */ 686