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"
31 /* PRIVATE VARIABLES **********************************************************/
33 static const BYTE EntryProcedure
[] = {
34 0xEB, // jmp short +0x03
45 static PDOS_DEVICE_NODE Node
= NULL
;
46 static XMS_HANDLE HandleTable
[XMS_MAX_HANDLES
];
47 static WORD FreeBlocks
= XMS_BLOCKS
;
48 static RTL_BITMAP AllocBitmap
;
49 static ULONG BitmapBuffer
[(XMS_BLOCKS
+ 31) / 32];
52 * Flag used by Global Enable/Disable A20 functions, so that they don't
53 * need to re-change the state of A20 if it was already enabled/disabled.
55 static BOOLEAN IsA20Enabled
= FALSE
;
57 * This flag is set to TRUE or FALSE when A20 line was already disabled or
58 * enabled when XMS driver was loaded.
59 * In case A20 was disabled, we are allowed to modify it. In case A20 was
60 * already enabled, we are not allowed to touch it.
62 static BOOLEAN CanChangeA20
= TRUE
;
64 * Count for enabling or disabling the A20 line. The A20 line is enabled
65 * only if the enabling count is greater than or equal to 0.
67 static LONG A20EnableCount
= 0;
69 /* HELPERS FOR A20 LINE *******************************************************/
71 static VOID
XmsLocalEnableA20(VOID
)
73 /* Enable A20 only if we can do so, otherwise make the caller believe we enabled it */
74 if (!CanChangeA20
) goto Quit
;
76 /* The count is zero so enable A20 */
77 if (A20EnableCount
== 0) EmulatorSetA20(TRUE
);
82 setAX(0x0001); /* Line successfully enabled */
83 setBL(XMS_STATUS_SUCCESS
);
87 static VOID
XmsLocalDisableA20(VOID
)
89 /* Disable A20 only if we can do so, otherwise make the caller believe we disabled it */
90 if (!CanChangeA20
) goto Quit
;
92 /* If the count is already zero, fail */
93 if (A20EnableCount
== 0) goto Fail
;
97 /* The count is zero so disable A20 */
98 if (A20EnableCount
== 0) EmulatorSetA20(FALSE
);
101 setAX(0x0001); /* Line successfully disabled */
102 setBL(XMS_STATUS_SUCCESS
);
106 setAX(0x0000); /* Line failed to be enabled */
107 setBL(XMS_STATUS_A20_ERROR
);
111 /* PRIVATE FUNCTIONS **********************************************************/
113 static inline PXMS_HANDLE
GetHandleRecord(WORD Handle
)
116 if (Handle
== 0 || Handle
>= XMS_MAX_HANDLES
) return NULL
;
118 Entry
= &HandleTable
[Handle
- 1];
119 return Entry
->Size
? Entry
: NULL
;
122 static WORD
XmsGetLargestFreeBlock(VOID
)
125 DWORD CurrentIndex
= 0;
129 while (CurrentIndex
< XMS_BLOCKS
)
131 RunSize
= RtlFindNextForwardRunClear(&AllocBitmap
, CurrentIndex
, &RunStart
);
132 if (RunSize
== 0) break;
134 /* Update the maximum */
135 if (RunSize
> Result
) Result
= RunSize
;
137 /* Go to the next run */
138 CurrentIndex
= RunStart
+ RunSize
;
144 static CHAR
XmsAlloc(WORD Size
, PWORD Handle
)
147 PXMS_HANDLE HandleEntry
;
148 DWORD CurrentIndex
= 0;
152 if (Size
> FreeBlocks
) return XMS_STATUS_OUT_OF_MEMORY
;
154 for (i
= 0; i
< XMS_MAX_HANDLES
; i
++)
156 HandleEntry
= &HandleTable
[i
];
157 if (HandleEntry
->Handle
== 0)
164 if (i
== XMS_MAX_HANDLES
) return XMS_STATUS_OUT_OF_HANDLES
;
166 /* Optimize blocks */
167 for (i
= 0; i
< XMS_MAX_HANDLES
; i
++)
169 /* Skip free and locked blocks */
170 if (HandleEntry
->Handle
== 0 || HandleEntry
->LockCount
> 0) continue;
172 CurrentIndex
= (HandleEntry
->Address
- XMS_ADDRESS
) / XMS_BLOCK_SIZE
;
174 /* Check if there is any free space before this block */
175 RunSize
= RtlFindLastBackwardRunClear(&AllocBitmap
, CurrentIndex
, &RunStart
);
176 if (RunSize
== 0) break;
178 /* Move this block back */
179 RtlMoveMemory((PVOID
)REAL_TO_PHYS(HandleEntry
->Address
- RunSize
* XMS_BLOCK_SIZE
),
180 (PVOID
)REAL_TO_PHYS(HandleEntry
->Address
),
181 RunSize
* XMS_BLOCK_SIZE
);
183 /* Update the address */
184 HandleEntry
->Address
-= RunSize
* XMS_BLOCK_SIZE
;
187 while (CurrentIndex
< XMS_BLOCKS
)
189 RunSize
= RtlFindNextForwardRunClear(&AllocBitmap
, CurrentIndex
, &RunStart
);
190 if (RunSize
== 0) break;
192 if (RunSize
>= HandleEntry
->Size
)
194 /* Allocate it here */
195 HandleEntry
->Handle
= i
+ 1;
196 HandleEntry
->LockCount
= 0;
197 HandleEntry
->Size
= Size
;
198 HandleEntry
->Address
= XMS_ADDRESS
+ RunStart
* XMS_BLOCK_SIZE
;
201 RtlSetBits(&AllocBitmap
, RunStart
, HandleEntry
->Size
);
203 return XMS_STATUS_SUCCESS
;
207 CurrentIndex
= RunStart
+ RunSize
;
210 return XMS_STATUS_OUT_OF_MEMORY
;
213 static CHAR
XmsFree(WORD Handle
)
216 PXMS_HANDLE HandleEntry
= GetHandleRecord(Handle
);
218 if (HandleEntry
== NULL
|| HandleEntry
->Handle
== 0) return XMS_STATUS_INVALID_HANDLE
;
219 if (HandleEntry
->LockCount
) return XMS_STATUS_LOCKED
;
221 BlockNumber
= (HandleEntry
->Address
- XMS_ADDRESS
) / XMS_BLOCK_SIZE
;
222 RtlClearBits(&AllocBitmap
, BlockNumber
, HandleEntry
->Size
);
224 HandleEntry
->Handle
= 0;
225 FreeBlocks
+= HandleEntry
->Size
;
227 return XMS_STATUS_SUCCESS
;
230 static CHAR
XmsLock(WORD Handle
, PDWORD Address
)
232 PXMS_HANDLE HandleEntry
= GetHandleRecord(Handle
);
234 if (HandleEntry
== NULL
) return XMS_STATUS_INVALID_HANDLE
;
235 if (HandleEntry
->LockCount
== 0xFF) return XMS_STATUS_LOCK_OVERFLOW
;
237 /* Increment the lock count */
238 HandleEntry
->LockCount
++;
239 *Address
= HandleEntry
->Address
;
241 return XMS_STATUS_SUCCESS
;
244 static CHAR
XmsUnlock(WORD Handle
)
246 PXMS_HANDLE HandleEntry
= GetHandleRecord(Handle
);
248 if (HandleEntry
== NULL
) return XMS_STATUS_INVALID_HANDLE
;
249 if (!HandleEntry
->LockCount
) return XMS_STATUS_NOT_LOCKED
;
251 /* Decrement the lock count */
252 HandleEntry
->LockCount
--;
254 return XMS_STATUS_SUCCESS
;
257 static VOID WINAPI
XmsBopProcedure(LPWORD Stack
)
261 /* Get XMS Version */
264 setAX(0x0300); /* XMS version 3.00 */
265 setBX(0x0301); /* Driver version 3.01 */
266 setDX(0x0001); /* HMA present */
270 /* Global Enable A20 */
273 /* Enable A20 if needed */
279 /* XmsLocalEnableA20 failed and already set AX and BL to their correct values */
286 setAX(0x0001); /* Line successfully enabled */
287 setBL(XMS_STATUS_SUCCESS
);
291 /* Global Disable A20 */
294 /* Disable A20 if needed */
297 XmsLocalDisableA20();
300 /* XmsLocalDisableA20 failed and already set AX and BL to their correct values */
304 IsA20Enabled
= FALSE
;
307 setAX(0x0001); /* Line successfully disabled */
308 setBL(XMS_STATUS_SUCCESS
);
312 /* Local Enable A20 */
315 /* This call sets AX and BL to their correct values */
320 /* Local Disable A20 */
323 /* This call sets AX and BL to their correct values */
324 XmsLocalDisableA20();
328 /* Query A20 State */
331 setAX(EmulatorGetA20());
332 setBL(XMS_STATUS_SUCCESS
);
336 /* Query Free Extended Memory */
339 setAX(XmsGetLargestFreeBlock());
341 setBL(XMS_STATUS_SUCCESS
);
345 /* Allocate Extended Memory Block */
349 CHAR Result
= XmsAlloc(getDX(), &Handle
);
365 /* Free Extended Memory Block */
368 CHAR Result
= XmsFree(getDX());
376 /* Move Extended Memory Block */
379 PVOID SourceAddress
, DestAddress
;
380 PXMS_COPY_DATA CopyData
= (PXMS_COPY_DATA
)SEG_OFF_TO_PTR(getDS(), getSI());
382 if (CopyData
->SourceHandle
)
384 PXMS_HANDLE Entry
= GetHandleRecord(CopyData
->SourceHandle
);
388 setBL(XMS_STATUS_BAD_SRC_HANDLE
);
392 if (CopyData
->SourceOffset
>= Entry
->Size
* XMS_BLOCK_SIZE
)
395 setBL(XMS_STATUS_BAD_SRC_OFFSET
);
398 SourceAddress
= (PVOID
)REAL_TO_PHYS(Entry
->Address
+ CopyData
->SourceOffset
);
402 /* The offset is actually a 16-bit segment:offset pointer */
403 SourceAddress
= SEG_OFF_TO_PTR(HIWORD(CopyData
->SourceOffset
),
404 LOWORD(CopyData
->SourceOffset
));
407 if (CopyData
->DestHandle
)
409 PXMS_HANDLE Entry
= GetHandleRecord(CopyData
->DestHandle
);
413 setBL(XMS_STATUS_BAD_DEST_HANDLE
);
417 if (CopyData
->DestOffset
>= Entry
->Size
* XMS_BLOCK_SIZE
)
420 setBL(XMS_STATUS_BAD_DEST_OFFSET
);
423 DestAddress
= (PVOID
)REAL_TO_PHYS(Entry
->Address
+ CopyData
->DestOffset
);
427 /* The offset is actually a 16-bit segment:offset pointer */
428 DestAddress
= SEG_OFF_TO_PTR(HIWORD(CopyData
->DestOffset
),
429 LOWORD(CopyData
->DestOffset
));
433 setBL(XMS_STATUS_SUCCESS
);
434 RtlMoveMemory(DestAddress
, SourceAddress
, CopyData
->Count
);
438 /* Lock Extended Memory Block */
442 CHAR Result
= XmsLock(getDX(), &Address
);
448 /* Store the LINEAR address in DX:BX */
449 setDX(HIWORD(Address
));
450 setBX(LOWORD(Address
));
461 /* Unlock Extended Memory Block */
464 CHAR Result
= XmsUnlock(getDX());
472 /* Get Handle Information */
475 PXMS_HANDLE Entry
= GetHandleRecord(getDX());
477 if (Entry
&& Entry
->Handle
!= 0)
482 for (i
= 0; i
< XMS_MAX_HANDLES
; i
++)
484 if (HandleTable
[i
].Handle
== 0) Handles
++;
488 setBH(Entry
->LockCount
);
495 setBL(XMS_STATUS_INVALID_HANDLE
);
506 BYTE OldAllocStrategy
= Sda
->AllocStrategy
;
507 BOOLEAN OldLinkState
= DosUmbLinked
;
510 Sda
->AllocStrategy
= DOS_ALLOC_HIGH
| DOS_ALLOC_BEST_FIT
;
511 Segment
= DosAllocateMemory(getDX(), &MaxAvailable
);
521 setBL(MaxAvailable
? XMS_STATUS_SMALLER_UMB
: XMS_STATUS_OUT_OF_UMBS
);
525 Sda
->AllocStrategy
= OldAllocStrategy
;
526 if (!OldLinkState
) DosUnlinkUmb();
533 if (DosFreeMemory(getDX()))
540 setBL(XMS_STATUS_INVALID_UMB
);
552 Segment
= DosResizeMemory(getDX(), getBX(), &MaxAvailable
);
561 setBL(Sda
->LastErrorCode
== ERROR_INVALID_HANDLE
562 ? XMS_STATUS_INVALID_UMB
: XMS_STATUS_SMALLER_UMB
);
571 DPRINT1("XMS command AH = 0x%02X NOT IMPLEMENTED\n", getAH());
572 setBL(XMS_STATUS_NOT_IMPLEMENTED
);
577 /* PUBLIC FUNCTIONS ***********************************************************/
579 BOOLEAN
XmsGetDriverEntry(PDWORD Pointer
)
581 if (Node
== NULL
) return FALSE
;
582 *Pointer
= DEVICE_PRIVATE_AREA(Node
->Driver
);
586 VOID
XmsInitialize(VOID
)
588 RtlZeroMemory(HandleTable
, sizeof(HandleTable
));
589 RtlZeroMemory(BitmapBuffer
, sizeof(BitmapBuffer
));
590 RtlInitializeBitMap(&AllocBitmap
, BitmapBuffer
, XMS_BLOCKS
);
592 Node
= DosCreateDeviceEx(DOS_DEVATTR_IOCTL
| DOS_DEVATTR_CHARACTER
,
594 sizeof(EntryProcedure
));
596 RegisterBop(BOP_XMS
, XmsBopProcedure
);
598 /* Copy the entry routine to the device private area */
599 RtlMoveMemory(FAR_POINTER(DEVICE_PRIVATE_AREA(Node
->Driver
)),
601 sizeof(EntryProcedure
));
604 VOID
XmsCleanup(VOID
)
606 RegisterBop(BOP_XMS
, NULL
);
607 DosDeleteDevice(Node
);