4a96255724dbd46d140f870bc328f271dba991e0
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: dos/dos32krnl/memory.c
5 * PURPOSE: DOS32 Memory Manager
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9 /* INCLUDES *******************************************************************/
20 /* PUBLIC VARIABLES ***********************************************************/
22 BYTE DosAllocStrategy
= DOS_ALLOC_BEST_FIT
;
23 BOOLEAN DosUmbLinked
= FALSE
;
25 /* PRIVATE FUNCTIONS **********************************************************/
27 static VOID
DosCombineFreeBlocks(WORD StartBlock
)
29 PDOS_MCB CurrentMcb
= SEGMENT_TO_MCB(StartBlock
), NextMcb
;
31 /* If this is the last block or it's not free, quit */
32 if (CurrentMcb
->BlockType
== 'Z' || CurrentMcb
->OwnerPsp
!= 0) return;
36 /* Get a pointer to the next MCB */
37 NextMcb
= SEGMENT_TO_MCB(StartBlock
+ CurrentMcb
->Size
+ 1);
39 /* Check if the next MCB is free */
40 if (NextMcb
->OwnerPsp
== 0)
43 CurrentMcb
->Size
+= NextMcb
->Size
+ 1;
44 CurrentMcb
->BlockType
= NextMcb
->BlockType
;
45 NextMcb
->BlockType
= 'I';
49 /* No more adjoining free blocks */
55 /* PUBLIC FUNCTIONS ***********************************************************/
57 WORD
DosAllocateMemory(WORD Size
, WORD
*MaxAvailable
)
59 WORD Result
= 0, Segment
= FIRST_MCB_SEGMENT
, MaxSize
= 0;
61 BOOLEAN SearchUmb
= FALSE
;
63 DPRINT("DosAllocateMemory: Size 0x%04X\n", Size
);
65 if (DosUmbLinked
&& (DosAllocStrategy
& (DOS_ALLOC_HIGH
| DOS_ALLOC_HIGH_LOW
)))
67 /* Search UMB first */
68 Segment
= UMB_START_SEGMENT
;
74 /* Get a pointer to the MCB */
75 CurrentMcb
= SEGMENT_TO_MCB(Segment
);
77 /* Make sure it's valid */
78 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!= 'Z')
80 DPRINT("The DOS memory arena is corrupted!\n");
81 DosLastError
= ERROR_ARENA_TRASHED
;
85 /* Only check free blocks */
86 if (CurrentMcb
->OwnerPsp
!= 0) goto Next
;
88 /* Combine this free block with adjoining free blocks */
89 DosCombineFreeBlocks(Segment
);
91 /* Update the maximum block size */
92 if (CurrentMcb
->Size
> MaxSize
) MaxSize
= CurrentMcb
->Size
;
94 /* Check if this block is big enough */
95 if (CurrentMcb
->Size
< Size
) goto Next
;
97 switch (DosAllocStrategy
& 0x3F)
99 case DOS_ALLOC_FIRST_FIT
:
101 /* For first fit, stop immediately */
106 case DOS_ALLOC_BEST_FIT
:
108 /* For best fit, update the smallest block found so far */
109 if ((Result
== 0) || (CurrentMcb
->Size
< SEGMENT_TO_MCB(Result
)->Size
))
117 case DOS_ALLOC_LAST_FIT
:
119 /* For last fit, make the current block the result, but keep searching */
126 /* If this was the last MCB in the chain, quit */
127 if (CurrentMcb
->BlockType
== 'Z')
129 /* Check if nothing was found while searching through UMBs */
130 if ((Result
== 0) && SearchUmb
&& (DosAllocStrategy
& DOS_ALLOC_HIGH_LOW
))
132 /* Search low memory */
133 Segment
= FIRST_MCB_SEGMENT
;
141 /* Otherwise, update the segment and continue */
142 Segment
+= CurrentMcb
->Size
+ 1;
147 /* If we didn't find a free block, return 0 */
150 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
151 if (MaxAvailable
) *MaxAvailable
= MaxSize
;
155 /* Get a pointer to the MCB */
156 CurrentMcb
= SEGMENT_TO_MCB(Result
);
158 /* Check if the block is larger than requested */
159 if (CurrentMcb
->Size
> Size
)
161 /* It is, split it into two blocks */
162 if ((DosAllocStrategy
& 0x3F) != DOS_ALLOC_LAST_FIT
)
164 PDOS_MCB NextMcb
= SEGMENT_TO_MCB(Result
+ Size
+ 1);
166 /* Initialize the new MCB structure */
167 NextMcb
->BlockType
= CurrentMcb
->BlockType
;
168 NextMcb
->Size
= CurrentMcb
->Size
- Size
- 1;
169 NextMcb
->OwnerPsp
= 0;
171 /* Update the current block */
172 CurrentMcb
->BlockType
= 'M';
173 CurrentMcb
->Size
= Size
;
177 /* Save the location of the current MCB */
178 PDOS_MCB PreviousMcb
= CurrentMcb
;
180 /* Move the current MCB higher */
181 Result
+= CurrentMcb
->Size
- Size
;
182 CurrentMcb
= SEGMENT_TO_MCB(Result
);
184 /* Initialize the new MCB structure */
185 CurrentMcb
->BlockType
= PreviousMcb
->BlockType
;
186 CurrentMcb
->Size
= Size
;
187 CurrentMcb
->OwnerPsp
= 0;
189 /* Update the previous block */
190 PreviousMcb
->BlockType
= 'M';
191 PreviousMcb
->Size
-= Size
+ 1;
195 /* Take ownership of the block */
196 CurrentMcb
->OwnerPsp
= CurrentPsp
;
198 /* Return the segment of the data portion of the block */
202 BOOLEAN
DosResizeMemory(WORD BlockData
, WORD NewSize
, WORD
*MaxAvailable
)
204 BOOLEAN Success
= TRUE
;
205 WORD Segment
= BlockData
- 1, ReturnSize
= 0, NextSegment
;
206 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
), NextMcb
;
208 DPRINT("DosResizeMemory: BlockData 0x%04X, NewSize 0x%04X\n",
212 /* Make sure this is a valid, allocated block */
214 || (Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z')
215 || Mcb
->OwnerPsp
== 0)
218 DosLastError
= ERROR_INVALID_HANDLE
;
222 ReturnSize
= Mcb
->Size
;
224 /* Check if we need to expand or contract the block */
225 if (NewSize
> Mcb
->Size
)
227 /* We can't expand the last block */
228 if (Mcb
->BlockType
!= 'M')
234 /* Get the pointer and segment of the next MCB */
235 NextSegment
= Segment
+ Mcb
->Size
+ 1;
236 NextMcb
= SEGMENT_TO_MCB(NextSegment
);
238 /* Make sure the next segment is free */
239 if (NextMcb
->OwnerPsp
!= 0)
241 DPRINT("Cannot expand memory block: next segment is not free!\n");
242 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
247 /* Combine this free block with adjoining free blocks */
248 DosCombineFreeBlocks(NextSegment
);
250 /* Set the maximum possible size of the block */
251 ReturnSize
+= NextMcb
->Size
+ 1;
253 if (ReturnSize
< NewSize
)
255 DPRINT("Cannot expand memory block: insufficient free segments available!\n");
256 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
261 /* Maximize the current block */
262 Mcb
->Size
= ReturnSize
;
263 Mcb
->BlockType
= NextMcb
->BlockType
;
265 /* Invalidate the next block */
266 NextMcb
->BlockType
= 'I';
268 /* Check if the block is larger than requested */
269 if (Mcb
->Size
> NewSize
)
271 DPRINT("Block too large, reducing size from 0x%04X to 0x%04X\n",
275 /* It is, split it into two blocks */
276 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
278 /* Initialize the new MCB structure */
279 NextMcb
->BlockType
= Mcb
->BlockType
;
280 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
281 NextMcb
->OwnerPsp
= 0;
283 /* Update the current block */
284 Mcb
->BlockType
= 'M';
288 else if (NewSize
< Mcb
->Size
)
290 DPRINT("Shrinking block from 0x%04X to 0x%04X\n",
294 /* Just split the block */
295 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
296 NextMcb
->BlockType
= Mcb
->BlockType
;
297 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
298 NextMcb
->OwnerPsp
= 0;
301 Mcb
->BlockType
= 'M';
306 /* Check if the operation failed */
309 DPRINT("DosResizeMemory FAILED. Maximum available: 0x%04X\n",
312 /* Return the maximum possible size */
313 if (MaxAvailable
) *MaxAvailable
= ReturnSize
;
319 BOOLEAN
DosFreeMemory(WORD BlockData
)
321 PDOS_MCB Mcb
= SEGMENT_TO_MCB(BlockData
- 1);
323 DPRINT("DosFreeMemory: BlockData 0x%04X\n", BlockData
);
324 if (BlockData
== 0) return FALSE
;
326 /* Make sure the MCB is valid */
327 if (Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z')
329 DPRINT("MCB block type '%c' not valid!\n", Mcb
->BlockType
);
333 /* Mark the block as free */
339 BOOLEAN
DosLinkUmb(VOID
)
341 DWORD Segment
= FIRST_MCB_SEGMENT
;
342 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
);
344 DPRINT("Linking UMB\n");
346 /* Check if UMBs are already linked */
347 if (DosUmbLinked
) return FALSE
;
349 /* Find the last block */
350 while ((Mcb
->BlockType
== 'M') && (Segment
<= 0xFFFF))
352 Segment
+= Mcb
->Size
+ 1;
353 Mcb
= SEGMENT_TO_MCB(Segment
);
356 /* Make sure it's valid */
357 if (Mcb
->BlockType
!= 'Z') return FALSE
;
359 /* Connect the MCB with the UMB chain */
360 Mcb
->BlockType
= 'M';
366 BOOLEAN
DosUnlinkUmb(VOID
)
368 DWORD Segment
= FIRST_MCB_SEGMENT
;
369 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
);
371 DPRINT("Unlinking UMB\n");
373 /* Check if UMBs are already unlinked */
374 if (!DosUmbLinked
) return FALSE
;
376 /* Find the block preceding the MCB that links it with the UMB chain */
377 while (Segment
<= 0xFFFF)
379 if ((Segment
+ Mcb
->Size
) == (FIRST_MCB_SEGMENT
+ USER_MEMORY_SIZE
))
381 /* This is the last non-UMB segment */
385 /* Advance to the next MCB */
386 Segment
+= Mcb
->Size
+ 1;
387 Mcb
= SEGMENT_TO_MCB(Segment
);
390 /* Mark the MCB as the last MCB */
391 Mcb
->BlockType
= 'Z';
393 DosUmbLinked
= FALSE
;
397 VOID
DosChangeMemoryOwner(WORD Segment
, WORD NewOwner
)
399 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
- 1);
401 /* Just set the owner */
402 Mcb
->OwnerPsp
= NewOwner
;