2 * COPYRIGHT: GPLv2+ - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
5 * PURPOSE: DOS XMS Driver
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9 /* INCLUDES *******************************************************************/
16 #include "../../memory.h"
18 #include "hardware/ps2.h"
26 #define XMS_DEVICE_NAME "XMSXXXX0"
33 RtlFindLastBackwardRunClear
35 IN PRTL_BITMAP BitMapHeader
,
37 OUT PULONG StartingRunIndex
40 /* PRIVATE VARIABLES **********************************************************/
42 static const BYTE EntryProcedure
[] = {
43 0xEB, // jmp short +0x03
54 static PDOS_DEVICE_NODE Node
= NULL
;
55 static XMS_HANDLE HandleTable
[XMS_MAX_HANDLES
];
56 static WORD FreeBlocks
= XMS_BLOCKS
;
57 static RTL_BITMAP AllocBitmap
;
58 static ULONG BitmapBuffer
[(XMS_BLOCKS
+ 31) / 32];
61 * Flag used by Global Enable/Disable A20 functions, so that they don't
62 * need to re-change the state of A20 if it was already enabled/disabled.
64 static BOOLEAN IsA20Enabled
= FALSE
;
66 * This flag is set to TRUE or FALSE when A20 line was already disabled or
67 * enabled when XMS driver was loaded.
68 * In case A20 was disabled, we are allowed to modify it. In case A20 was
69 * already enabled, we are not allowed to touch it.
71 static BOOLEAN CanChangeA20
= TRUE
;
73 * Count for enabling or disabling the A20 line. The A20 line is enabled
74 * only if the enabling count is greater than or equal to 0.
76 static LONG A20EnableCount
= 0;
78 /* HELPERS FOR A20 LINE *******************************************************/
80 static VOID
XmsLocalEnableA20(VOID
)
82 /* Enable A20 only if we can do so, otherwise make the caller believe we enabled it */
83 if (!CanChangeA20
) goto Quit
;
85 /* The count is zero so enable A20 */
86 if (A20EnableCount
== 0) EmulatorSetA20(TRUE
);
91 setAX(0x0001); /* Line successfully enabled */
92 setBL(XMS_STATUS_SUCCESS
);
96 static VOID
XmsLocalDisableA20(VOID
)
98 /* Disable A20 only if we can do so, otherwise make the caller believe we disabled it */
99 if (!CanChangeA20
) goto Quit
;
101 /* If the count is already zero, fail */
102 if (A20EnableCount
== 0) goto Fail
;
106 /* The count is zero so disable A20 */
107 if (A20EnableCount
== 0) EmulatorSetA20(FALSE
);
110 setAX(0x0001); /* Line successfully disabled */
111 setBL(XMS_STATUS_SUCCESS
);
115 setAX(0x0000); /* Line failed to be enabled */
116 setBL(XMS_STATUS_A20_ERROR
);
120 /* PRIVATE FUNCTIONS **********************************************************/
122 static inline PXMS_HANDLE
GetHandleRecord(WORD Handle
)
125 if (Handle
== 0 || Handle
>= XMS_MAX_HANDLES
) return NULL
;
127 Entry
= &HandleTable
[Handle
- 1];
128 return Entry
->Size
? Entry
: NULL
;
131 static WORD
XmsGetLargestFreeBlock(VOID
)
134 DWORD CurrentIndex
= 0;
138 while (CurrentIndex
< XMS_BLOCKS
)
140 RunSize
= RtlFindNextForwardRunClear(&AllocBitmap
, CurrentIndex
, &RunStart
);
141 if (RunSize
== 0) break;
143 /* Update the maximum */
144 if (RunSize
> Result
) Result
= RunSize
;
146 /* Go to the next run */
147 CurrentIndex
= RunStart
+ RunSize
;
153 static CHAR
XmsAlloc(WORD Size
, PWORD Handle
)
156 PXMS_HANDLE HandleEntry
;
157 DWORD CurrentIndex
= 0;
161 if (Size
> FreeBlocks
) return XMS_STATUS_OUT_OF_MEMORY
;
163 for (i
= 0; i
< XMS_MAX_HANDLES
; i
++)
165 HandleEntry
= &HandleTable
[i
];
166 if (HandleEntry
->Handle
== 0)
173 if (i
== XMS_MAX_HANDLES
) return XMS_STATUS_OUT_OF_HANDLES
;
175 /* Optimize blocks */
176 for (i
= 0; i
< XMS_MAX_HANDLES
; i
++)
178 /* Skip free and locked blocks */
179 if (HandleEntry
->Handle
== 0 || HandleEntry
->LockCount
> 0) continue;
181 CurrentIndex
= (HandleEntry
->Address
- XMS_ADDRESS
) / XMS_BLOCK_SIZE
;
183 /* Check if there is any free space before this block */
184 RunSize
= RtlFindLastBackwardRunClear(&AllocBitmap
, CurrentIndex
, &RunStart
);
185 if (RunSize
== 0) break;
187 /* Move this block back */
188 RtlMoveMemory((PVOID
)REAL_TO_PHYS(HandleEntry
->Address
- RunSize
* XMS_BLOCK_SIZE
),
189 (PVOID
)REAL_TO_PHYS(HandleEntry
->Address
),
190 RunSize
* XMS_BLOCK_SIZE
);
192 /* Update the address */
193 HandleEntry
->Address
-= RunSize
* XMS_BLOCK_SIZE
;
196 while (CurrentIndex
< XMS_BLOCKS
)
198 RunSize
= RtlFindNextForwardRunClear(&AllocBitmap
, CurrentIndex
, &RunStart
);
199 if (RunSize
== 0) break;
201 if (RunSize
>= HandleEntry
->Size
)
203 /* Allocate it here */
204 HandleEntry
->Handle
= i
+ 1;
205 HandleEntry
->LockCount
= 0;
206 HandleEntry
->Size
= Size
;
207 HandleEntry
->Address
= XMS_ADDRESS
+ RunStart
* XMS_BLOCK_SIZE
;
210 RtlSetBits(&AllocBitmap
, RunStart
, HandleEntry
->Size
);
212 return XMS_STATUS_SUCCESS
;
216 CurrentIndex
= RunStart
+ RunSize
;
219 return XMS_STATUS_OUT_OF_MEMORY
;
222 static CHAR
XmsFree(WORD Handle
)
225 PXMS_HANDLE HandleEntry
= GetHandleRecord(Handle
);
227 if (HandleEntry
== NULL
|| HandleEntry
->Handle
== 0) return XMS_STATUS_INVALID_HANDLE
;
228 if (HandleEntry
->LockCount
) return XMS_STATUS_LOCKED
;
230 BlockNumber
= (HandleEntry
->Address
- XMS_ADDRESS
) / XMS_BLOCK_SIZE
;
231 RtlClearBits(&AllocBitmap
, BlockNumber
, HandleEntry
->Size
);
233 HandleEntry
->Handle
= 0;
234 FreeBlocks
+= HandleEntry
->Size
;
236 return XMS_STATUS_SUCCESS
;
239 static CHAR
XmsLock(WORD Handle
, PDWORD Address
)
241 PXMS_HANDLE HandleEntry
= GetHandleRecord(Handle
);
243 if (HandleEntry
== NULL
) return XMS_STATUS_INVALID_HANDLE
;
244 if (HandleEntry
->LockCount
== 0xFF) return XMS_STATUS_LOCK_OVERFLOW
;
246 /* Increment the lock count */
247 HandleEntry
->LockCount
++;
248 *Address
= HandleEntry
->Address
;
250 return XMS_STATUS_SUCCESS
;
253 static CHAR
XmsUnlock(WORD Handle
)
255 PXMS_HANDLE HandleEntry
= GetHandleRecord(Handle
);
257 if (HandleEntry
== NULL
) return XMS_STATUS_INVALID_HANDLE
;
258 if (!HandleEntry
->LockCount
) return XMS_STATUS_NOT_LOCKED
;
260 /* Decrement the lock count */
261 HandleEntry
->LockCount
--;
263 return XMS_STATUS_SUCCESS
;
266 static VOID WINAPI
XmsBopProcedure(LPWORD Stack
)
270 /* Get XMS Version */
273 setAX(0x0300); /* XMS version 3.00 */
274 setBX(0x0301); /* Driver version 3.01 */
275 setDX(0x0001); /* HMA present */
279 /* Global Enable A20 */
282 /* Enable A20 if needed */
288 /* XmsLocalEnableA20 failed and already set AX and BL to their correct values */
295 setAX(0x0001); /* Line successfully enabled */
296 setBL(XMS_STATUS_SUCCESS
);
300 /* Global Disable A20 */
303 /* Disable A20 if needed */
306 XmsLocalDisableA20();
309 /* XmsLocalDisableA20 failed and already set AX and BL to their correct values */
313 IsA20Enabled
= FALSE
;
316 setAX(0x0001); /* Line successfully disabled */
317 setBL(XMS_STATUS_SUCCESS
);
321 /* Local Enable A20 */
324 /* This call sets AX and BL to their correct values */
329 /* Local Disable A20 */
332 /* This call sets AX and BL to their correct values */
333 XmsLocalDisableA20();
337 /* Query A20 State */
340 setAX(EmulatorGetA20());
341 setBL(XMS_STATUS_SUCCESS
);
345 /* Query Free Extended Memory */
348 setAX(XmsGetLargestFreeBlock());
350 setBL(XMS_STATUS_SUCCESS
);
354 /* Allocate Extended Memory Block */
358 CHAR Result
= XmsAlloc(getDX(), &Handle
);
374 /* Free Extended Memory Block */
377 CHAR Result
= XmsFree(getDX());
385 /* Move Extended Memory Block */
388 PVOID SourceAddress
, DestAddress
;
389 PXMS_COPY_DATA CopyData
= (PXMS_COPY_DATA
)SEG_OFF_TO_PTR(getDS(), getSI());
391 if (CopyData
->SourceHandle
)
393 PXMS_HANDLE Entry
= GetHandleRecord(CopyData
->SourceHandle
);
397 setBL(XMS_STATUS_BAD_SRC_HANDLE
);
401 if (CopyData
->SourceOffset
>= Entry
->Size
* XMS_BLOCK_SIZE
)
404 setBL(XMS_STATUS_BAD_SRC_OFFSET
);
407 SourceAddress
= (PVOID
)REAL_TO_PHYS(Entry
->Address
+ CopyData
->SourceOffset
);
411 /* The offset is actually a 16-bit segment:offset pointer */
412 SourceAddress
= SEG_OFF_TO_PTR(HIWORD(CopyData
->SourceOffset
),
413 LOWORD(CopyData
->SourceOffset
));
416 if (CopyData
->DestHandle
)
418 PXMS_HANDLE Entry
= GetHandleRecord(CopyData
->DestHandle
);
422 setBL(XMS_STATUS_BAD_DEST_HANDLE
);
426 if (CopyData
->DestOffset
>= Entry
->Size
* XMS_BLOCK_SIZE
)
429 setBL(XMS_STATUS_BAD_DEST_OFFSET
);
432 DestAddress
= (PVOID
)REAL_TO_PHYS(Entry
->Address
+ CopyData
->DestOffset
);
436 /* The offset is actually a 16-bit segment:offset pointer */
437 DestAddress
= SEG_OFF_TO_PTR(HIWORD(CopyData
->DestOffset
),
438 LOWORD(CopyData
->DestOffset
));
442 setBL(XMS_STATUS_SUCCESS
);
443 RtlMoveMemory(DestAddress
, SourceAddress
, CopyData
->Count
);
447 /* Lock Extended Memory Block */
451 CHAR Result
= XmsLock(getDX(), &Address
);
457 /* Store the LINEAR address in DX:BX */
458 setDX(HIWORD(Address
));
459 setBX(LOWORD(Address
));
470 /* Unlock Extended Memory Block */
473 CHAR Result
= XmsUnlock(getDX());
481 /* Get Handle Information */
484 PXMS_HANDLE Entry
= GetHandleRecord(getDX());
486 if (Entry
&& Entry
->Handle
!= 0)
491 for (i
= 0; i
< XMS_MAX_HANDLES
; i
++)
493 if (HandleTable
[i
].Handle
== 0) Handles
++;
497 setBH(Entry
->LockCount
);
504 setBL(XMS_STATUS_INVALID_HANDLE
);
515 BYTE OldAllocStrategy
= Sda
->AllocStrategy
;
516 BOOLEAN OldLinkState
= DosUmbLinked
;
519 Sda
->AllocStrategy
= DOS_ALLOC_HIGH
| DOS_ALLOC_BEST_FIT
;
520 Segment
= DosAllocateMemory(getDX(), &MaxAvailable
);
530 setBL(MaxAvailable
? XMS_STATUS_SMALLER_UMB
: XMS_STATUS_OUT_OF_UMBS
);
534 Sda
->AllocStrategy
= OldAllocStrategy
;
535 if (!OldLinkState
) DosUnlinkUmb();
542 if (DosFreeMemory(getDX()))
549 setBL(XMS_STATUS_INVALID_UMB
);
561 Segment
= DosResizeMemory(getDX(), getBX(), &MaxAvailable
);
570 setBL(Sda
->LastErrorCode
== ERROR_INVALID_HANDLE
571 ? XMS_STATUS_INVALID_UMB
: XMS_STATUS_SMALLER_UMB
);
580 DPRINT1("XMS command AH = 0x%02X NOT IMPLEMENTED\n", getAH());
581 setBL(XMS_STATUS_NOT_IMPLEMENTED
);
586 /* PUBLIC FUNCTIONS ***********************************************************/
588 BOOLEAN
XmsGetDriverEntry(PDWORD Pointer
)
590 if (Node
== NULL
) return FALSE
;
591 *Pointer
= DEVICE_PRIVATE_AREA(Node
->Driver
);
595 VOID
XmsInitialize(VOID
)
597 RtlZeroMemory(HandleTable
, sizeof(HandleTable
));
598 RtlZeroMemory(BitmapBuffer
, sizeof(BitmapBuffer
));
599 RtlInitializeBitMap(&AllocBitmap
, BitmapBuffer
, XMS_BLOCKS
);
601 Node
= DosCreateDeviceEx(DOS_DEVATTR_IOCTL
| DOS_DEVATTR_CHARACTER
,
603 sizeof(EntryProcedure
));
605 RegisterBop(BOP_XMS
, XmsBopProcedure
);
607 /* Copy the entry routine to the device private area */
608 RtlMoveMemory(FAR_POINTER(DEVICE_PRIVATE_AREA(Node
->Driver
)),
610 sizeof(EntryProcedure
));
613 VOID
XmsCleanup(VOID
)
615 RegisterBop(BOP_XMS
, NULL
);
616 DosDeleteDevice(Node
);