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 
ValidateMcb(PDOS_MCB Mcb)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
DosMemValidate(VOID)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 
DosCombineFreeBlocks(WORD StartBlock)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 
DosAllocateMemory(WORD Size,WORD * MaxAvailable)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 
DosResizeMemory(WORD BlockData,WORD NewSize,WORD * MaxAvailable)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 
DosFreeMemory(WORD BlockData)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 
DosLinkUmb(VOID)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 
DosUnlinkUmb(VOID)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 
DosChangeMemoryOwner(WORD Segment,WORD NewOwner)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 
DosGetPreviousUmb(WORD UmbSegment)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 
DosInitializeUmb(VOID)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 
DosInitializeMemory(VOID)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