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;
60 PDOS_MCB CurrentMcb
, NextMcb
;
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
;
140 /* Otherwise, update the segment and continue */
141 Segment
+= CurrentMcb
->Size
+ 1;
146 /* If we didn't find a free block, return 0 */
149 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
150 if (MaxAvailable
) *MaxAvailable
= MaxSize
;
154 /* Get a pointer to the MCB */
155 CurrentMcb
= SEGMENT_TO_MCB(Result
);
157 /* Check if the block is larger than requested */
158 if (CurrentMcb
->Size
> Size
)
160 /* It is, split it into two blocks */
161 NextMcb
= SEGMENT_TO_MCB(Result
+ Size
+ 1);
163 /* Initialize the new MCB structure */
164 NextMcb
->BlockType
= CurrentMcb
->BlockType
;
165 NextMcb
->Size
= CurrentMcb
->Size
- Size
- 1;
166 NextMcb
->OwnerPsp
= 0;
168 /* Update the current block */
169 CurrentMcb
->BlockType
= 'M';
170 CurrentMcb
->Size
= Size
;
173 /* Take ownership of the block */
174 CurrentMcb
->OwnerPsp
= CurrentPsp
;
176 /* Return the segment of the data portion of the block */
180 BOOLEAN
DosResizeMemory(WORD BlockData
, WORD NewSize
, WORD
*MaxAvailable
)
182 BOOLEAN Success
= TRUE
;
183 WORD Segment
= BlockData
- 1, ReturnSize
= 0, NextSegment
;
184 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
), NextMcb
;
186 DPRINT("DosResizeMemory: BlockData 0x%04X, NewSize 0x%04X\n",
190 /* Make sure this is a valid, allocated block */
191 if ((Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z') || Mcb
->OwnerPsp
== 0)
194 DosLastError
= ERROR_INVALID_HANDLE
;
198 ReturnSize
= Mcb
->Size
;
200 /* Check if we need to expand or contract the block */
201 if (NewSize
> Mcb
->Size
)
203 /* We can't expand the last block */
204 if (Mcb
->BlockType
!= 'M')
210 /* Get the pointer and segment of the next MCB */
211 NextSegment
= Segment
+ Mcb
->Size
+ 1;
212 NextMcb
= SEGMENT_TO_MCB(NextSegment
);
214 /* Make sure the next segment is free */
215 if (NextMcb
->OwnerPsp
!= 0)
217 DPRINT("Cannot expand memory block: next segment is not free!\n");
218 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
223 /* Combine this free block with adjoining free blocks */
224 DosCombineFreeBlocks(NextSegment
);
226 /* Set the maximum possible size of the block */
227 ReturnSize
+= NextMcb
->Size
+ 1;
229 if (ReturnSize
< NewSize
)
231 DPRINT("Cannot expand memory block: insufficient free segments available!\n");
232 DosLastError
= ERROR_NOT_ENOUGH_MEMORY
;
237 /* Maximize the current block */
238 Mcb
->Size
= ReturnSize
;
239 Mcb
->BlockType
= NextMcb
->BlockType
;
241 /* Invalidate the next block */
242 NextMcb
->BlockType
= 'I';
244 /* Check if the block is larger than requested */
245 if (Mcb
->Size
> NewSize
)
247 DPRINT("Block too large, reducing size from 0x%04X to 0x%04X\n",
251 /* It is, split it into two blocks */
252 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
254 /* Initialize the new MCB structure */
255 NextMcb
->BlockType
= Mcb
->BlockType
;
256 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
257 NextMcb
->OwnerPsp
= 0;
259 /* Update the current block */
260 Mcb
->BlockType
= 'M';
264 else if (NewSize
< Mcb
->Size
)
266 DPRINT("Shrinking block from 0x%04X to 0x%04X\n",
270 /* Just split the block */
271 NextMcb
= SEGMENT_TO_MCB(Segment
+ NewSize
+ 1);
272 NextMcb
->BlockType
= Mcb
->BlockType
;
273 NextMcb
->Size
= Mcb
->Size
- NewSize
- 1;
274 NextMcb
->OwnerPsp
= 0;
277 Mcb
->BlockType
= 'M';
282 /* Check if the operation failed */
285 DPRINT("DosResizeMemory FAILED. Maximum available: 0x%04X\n",
288 /* Return the maximum possible size */
289 if (MaxAvailable
) *MaxAvailable
= ReturnSize
;
295 BOOLEAN
DosFreeMemory(WORD BlockData
)
297 PDOS_MCB Mcb
= SEGMENT_TO_MCB(BlockData
- 1);
299 DPRINT("DosFreeMemory: BlockData 0x%04X\n", BlockData
);
301 /* Make sure the MCB is valid */
302 if (Mcb
->BlockType
!= 'M' && Mcb
->BlockType
!= 'Z')
304 DPRINT("MCB block type '%c' not valid!\n", Mcb
->BlockType
);
308 /* Mark the block as free */
314 BOOLEAN
DosLinkUmb(VOID
)
316 DWORD Segment
= FIRST_MCB_SEGMENT
;
317 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
);
319 DPRINT("Linking UMB\n");
321 /* Check if UMBs are already linked */
322 if (DosUmbLinked
) return FALSE
;
324 /* Find the last block */
325 while ((Mcb
->BlockType
== 'M') && (Segment
<= 0xFFFF))
327 Segment
+= Mcb
->Size
+ 1;
328 Mcb
= SEGMENT_TO_MCB(Segment
);
331 /* Make sure it's valid */
332 if (Mcb
->BlockType
!= 'Z') return FALSE
;
334 /* Connect the MCB with the UMB chain */
335 Mcb
->BlockType
= 'M';
341 BOOLEAN
DosUnlinkUmb(VOID
)
343 DWORD Segment
= FIRST_MCB_SEGMENT
;
344 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
);
346 DPRINT("Unlinking UMB\n");
348 /* Check if UMBs are already unlinked */
349 if (!DosUmbLinked
) return FALSE
;
351 /* Find the block preceding the MCB that links it with the UMB chain */
352 while (Segment
<= 0xFFFF)
354 if ((Segment
+ Mcb
->Size
) == (FIRST_MCB_SEGMENT
+ USER_MEMORY_SIZE
))
356 /* This is the last non-UMB segment */
360 /* Advance to the next MCB */
361 Segment
+= Mcb
->Size
+ 1;
362 Mcb
= SEGMENT_TO_MCB(Segment
);
365 /* Mark the MCB as the last MCB */
366 Mcb
->BlockType
= 'Z';
368 DosUmbLinked
= FALSE
;
372 VOID
DosChangeMemoryOwner(WORD Segment
, WORD NewOwner
)
374 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
- 1);
376 /* Just set the owner */
377 Mcb
->OwnerPsp
= NewOwner
;