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 *******************************************************************/
22 #define XMS_DEVICE_NAME "XMSXXXX0"
25 /* PRIVATE VARIABLES **********************************************************/
27 static const BYTE EntryProcedure
[] = {
28 0xEB, // jmp short +0x03
39 static PDOS_DEVICE_NODE Node
= NULL
;
40 static XMS_HANDLE HandleTable
[XMS_MAX_HANDLES
];
41 static WORD FreeBlocks
= XMS_BLOCKS
;
42 static RTL_BITMAP AllocBitmap
;
43 static ULONG BitmapBuffer
[(XMS_BLOCKS
+ 31) / 32];
45 /* PRIVATE FUNCTIONS **********************************************************/
47 static inline PXMS_HANDLE
GetHandleRecord(WORD Handle
)
50 if (Handle
== 0 || Handle
>= XMS_MAX_HANDLES
) return NULL
;
52 Entry
= &HandleTable
[Handle
- 1];
53 return Entry
->Size
? Entry
: NULL
;
56 static CHAR
XmsAlloc(WORD Size
, PWORD Handle
)
59 PXMS_HANDLE HandleEntry
;
61 if (Size
> FreeBlocks
) return XMS_STATUS_OUT_OF_MEMORY
;
63 for (i
= 0; i
< XMS_MAX_HANDLES
; i
++)
65 HandleEntry
= &HandleTable
[i
];
66 if (HandleEntry
->Handle
== 0)
73 if (i
== XMS_MAX_HANDLES
) return XMS_STATUS_OUT_OF_HANDLES
;
75 HandleEntry
->Handle
= i
+ 1;
76 HandleEntry
->LockCount
= 0;
77 HandleEntry
->Size
= Size
;
80 return XMS_STATUS_SUCCESS
;
83 static CHAR
XmsFree(WORD Handle
)
85 PXMS_HANDLE HandleEntry
= GetHandleRecord(Handle
);
86 if (HandleEntry
== NULL
) return XMS_STATUS_INVALID_HANDLE
;
87 if (HandleEntry
->LockCount
) return XMS_STATUS_LOCKED
;
89 HandleEntry
->Handle
= 0;
90 FreeBlocks
+= HandleEntry
->Size
;
92 return XMS_STATUS_SUCCESS
;
95 static CHAR
XmsLock(WORD Handle
, PDWORD Address
)
97 DWORD CurrentIndex
= 0;
98 PXMS_HANDLE HandleEntry
= GetHandleRecord(Handle
);
100 if (HandleEntry
== NULL
) return XMS_STATUS_INVALID_HANDLE
;
101 if (HandleEntry
->LockCount
== 0xFF) return XMS_STATUS_LOCK_OVERFLOW
;
103 if (HandleEntry
->LockCount
)
105 /* Just increment the lock count */
106 HandleEntry
->LockCount
++;
107 return XMS_STATUS_SUCCESS
;
110 while (CurrentIndex
< XMS_BLOCKS
)
113 ULONG RunSize
= RtlFindNextForwardRunClear(&AllocBitmap
, CurrentIndex
, &RunStart
);
114 if (RunSize
== 0) break;
116 if (RunSize
>= HandleEntry
->Size
)
119 HandleEntry
->LockCount
++;
120 HandleEntry
->Address
= XMS_ADDRESS
+ RunStart
* XMS_BLOCK_SIZE
;
122 RtlSetBits(&AllocBitmap
, RunStart
, RunSize
);
123 *Address
= HandleEntry
->Address
;
124 return XMS_STATUS_SUCCESS
;
128 CurrentIndex
= RunStart
+ RunSize
;
131 /* Can't find any suitable range */
132 return XMS_STATUS_CANNOT_LOCK
;
135 static CHAR
XmsUnlock(WORD Handle
)
138 PXMS_HANDLE HandleEntry
= GetHandleRecord(Handle
);
140 if (HandleEntry
== NULL
) return XMS_STATUS_INVALID_HANDLE
;
141 if (!HandleEntry
->LockCount
) return XMS_STATUS_NOT_LOCKED
;
143 /* Decrement the lock count and exit early if it's still locked */
144 if (--HandleEntry
->LockCount
) return XMS_STATUS_SUCCESS
;
146 BlockNumber
= (HandleEntry
->Address
- XMS_ADDRESS
) / XMS_BLOCK_SIZE
;
147 RtlClearBits(&AllocBitmap
, BlockNumber
, HandleEntry
->Size
);
149 return XMS_STATUS_SUCCESS
;
152 static VOID WINAPI
XmsBopProcedure(LPWORD Stack
)
156 /* Get XMS Version */
159 setAX(0x0300); /* XMS version 3.0 */
160 setDX(0x0001); /* HMA present */
165 /* Query Free Extended Memory */
170 setBL(XMS_STATUS_SUCCESS
);
175 /* Allocate Extended Memory Block */
179 CHAR Result
= XmsAlloc(getDX(), &Handle
);
195 /* Free Extended Memory Block */
198 CHAR Result
= XmsFree(getDX());
206 /* Lock Extended Memory Block */
210 CHAR Result
= XmsLock(getDX(), &Address
);
216 /* Store the LINEAR address in DX:BX */
217 setDX(HIWORD(Address
));
218 setBX(LOWORD(Address
));
229 /* Unlock Extended Memory Block */
232 CHAR Result
= XmsUnlock(getDX());
242 DPRINT1("XMS command AH = 0x%02X NOT IMPLEMENTED\n", getAH());
243 setBL(XMS_STATUS_NOT_IMPLEMENTED
);
248 /* PUBLIC FUNCTIONS ***********************************************************/
250 BOOLEAN
XmsGetDriverEntry(PDWORD Pointer
)
252 if (Node
== NULL
) return FALSE
;
253 *Pointer
= DEVICE_PRIVATE_AREA(Node
->Driver
);
257 VOID
XmsInitialize(VOID
)
259 RtlZeroMemory(HandleTable
, sizeof(HandleTable
));
260 RtlZeroMemory(BitmapBuffer
, sizeof(BitmapBuffer
));
261 RtlInitializeBitMap(&AllocBitmap
, BitmapBuffer
, XMS_BLOCKS
);
263 Node
= DosCreateDeviceEx(DOS_DEVATTR_IOCTL
| DOS_DEVATTR_CHARACTER
,
265 sizeof(EntryProcedure
));
267 RegisterBop(XMS_BOP
, XmsBopProcedure
);
269 /* Copy the entry routine to the device private area */
270 RtlMoveMemory(FAR_POINTER(DEVICE_PRIVATE_AREA(Node
->Driver
)),
272 sizeof(EntryProcedure
));
275 VOID
XmsCleanup(VOID
)
277 RegisterBop(XMS_BOP
, NULL
);
278 DosDeleteDevice(Node
);