f6f4de71e0ee3045f56103423d9cfcac5f11428b
[reactos.git] / reactos / subsystems / mvdm / ntvdm / dos / dos32krnl / himem.c
1 /*
2 * COPYRIGHT: GPLv2+ - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: himem.c
5 * PURPOSE: DOS XMS Driver
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #define NDEBUG
12
13 #include "ntvdm.h"
14 #include "emulator.h"
15 #include "cpu/bop.h"
16
17 #include "dos.h"
18 #include "dos/dem.h"
19 #include "device.h"
20 #include "himem.h"
21
22 #define XMS_DEVICE_NAME "XMSXXXX0"
23
24 /* BOP Identifiers */
25 #define BOP_XMS 0x52
26
27 /* PRIVATE VARIABLES **********************************************************/
28
29 static const BYTE EntryProcedure[] = {
30 0xEB, // jmp short +0x03
31 0x03,
32 0x90, // nop
33 0x90, // nop
34 0x90, // nop
35 LOBYTE(EMULATOR_BOP),
36 HIBYTE(EMULATOR_BOP),
37 BOP_XMS,
38 0xCB // retf
39 };
40
41 static PDOS_DEVICE_NODE Node = NULL;
42 static XMS_HANDLE HandleTable[XMS_MAX_HANDLES];
43 static WORD FreeBlocks = XMS_BLOCKS;
44 static RTL_BITMAP AllocBitmap;
45 static ULONG BitmapBuffer[(XMS_BLOCKS + 31) / 32];
46
47 /* PRIVATE FUNCTIONS **********************************************************/
48
49 static inline PXMS_HANDLE GetHandleRecord(WORD Handle)
50 {
51 PXMS_HANDLE Entry;
52 if (Handle == 0 || Handle >= XMS_MAX_HANDLES) return NULL;
53
54 Entry = &HandleTable[Handle - 1];
55 return Entry->Size ? Entry : NULL;
56 }
57
58 static CHAR XmsAlloc(WORD Size, PWORD Handle)
59 {
60 BYTE i;
61 PXMS_HANDLE HandleEntry;
62
63 if (Size > FreeBlocks) return XMS_STATUS_OUT_OF_MEMORY;
64
65 for (i = 0; i < XMS_MAX_HANDLES; i++)
66 {
67 HandleEntry = &HandleTable[i];
68 if (HandleEntry->Handle == 0)
69 {
70 *Handle = i + 1;
71 break;
72 }
73 }
74
75 if (i == XMS_MAX_HANDLES) return XMS_STATUS_OUT_OF_HANDLES;
76
77 HandleEntry->Handle = i + 1;
78 HandleEntry->LockCount = 0;
79 HandleEntry->Size = Size;
80 FreeBlocks -= Size;
81
82 return XMS_STATUS_SUCCESS;
83 }
84
85 static CHAR XmsFree(WORD Handle)
86 {
87 PXMS_HANDLE HandleEntry = GetHandleRecord(Handle);
88 if (HandleEntry == NULL) return XMS_STATUS_INVALID_HANDLE;
89 if (HandleEntry->LockCount) return XMS_STATUS_LOCKED;
90
91 HandleEntry->Handle = 0;
92 FreeBlocks += HandleEntry->Size;
93
94 return XMS_STATUS_SUCCESS;
95 }
96
97 static CHAR XmsLock(WORD Handle, PDWORD Address)
98 {
99 DWORD CurrentIndex = 0;
100 PXMS_HANDLE HandleEntry = GetHandleRecord(Handle);
101
102 if (HandleEntry == NULL) return XMS_STATUS_INVALID_HANDLE;
103 if (HandleEntry->LockCount == 0xFF) return XMS_STATUS_LOCK_OVERFLOW;
104
105 if (HandleEntry->LockCount)
106 {
107 /* Just increment the lock count */
108 HandleEntry->LockCount++;
109 return XMS_STATUS_SUCCESS;
110 }
111
112 while (CurrentIndex < XMS_BLOCKS)
113 {
114 ULONG RunStart;
115 ULONG RunSize = RtlFindNextForwardRunClear(&AllocBitmap, CurrentIndex, &RunStart);
116 if (RunSize == 0) break;
117
118 if (RunSize >= HandleEntry->Size)
119 {
120 /* Lock it here */
121 HandleEntry->LockCount++;
122 HandleEntry->Address = XMS_ADDRESS + RunStart * XMS_BLOCK_SIZE;
123
124 RtlSetBits(&AllocBitmap, RunStart, RunSize);
125 *Address = HandleEntry->Address;
126 return XMS_STATUS_SUCCESS;
127 }
128
129 /* Keep searching */
130 CurrentIndex = RunStart + RunSize;
131 }
132
133 /* Can't find any suitable range */
134 return XMS_STATUS_CANNOT_LOCK;
135 }
136
137 static CHAR XmsUnlock(WORD Handle)
138 {
139 DWORD BlockNumber;
140 PXMS_HANDLE HandleEntry = GetHandleRecord(Handle);
141
142 if (HandleEntry == NULL) return XMS_STATUS_INVALID_HANDLE;
143 if (!HandleEntry->LockCount) return XMS_STATUS_NOT_LOCKED;
144
145 /* Decrement the lock count and exit early if it's still locked */
146 if (--HandleEntry->LockCount) return XMS_STATUS_SUCCESS;
147
148 BlockNumber = (HandleEntry->Address - XMS_ADDRESS) / XMS_BLOCK_SIZE;
149 RtlClearBits(&AllocBitmap, BlockNumber, HandleEntry->Size);
150
151 return XMS_STATUS_SUCCESS;
152 }
153
154 static VOID WINAPI XmsBopProcedure(LPWORD Stack)
155 {
156 switch (getAH())
157 {
158 /* Get XMS Version */
159 case 0x00:
160 {
161 setAX(0x0300); /* XMS version 3.0 */
162 setDX(0x0001); /* HMA present */
163
164 break;
165 }
166
167 /* Global Enable A20 */
168 case 0x03:
169 {
170 EmulatorSetA20(TRUE);
171 break;
172 }
173
174 /* Global Disable A20 */
175 case 0x04:
176 {
177 EmulatorSetA20(FALSE);
178 break;
179 }
180
181 /* Query Free Extended Memory */
182 case 0x08:
183 {
184 setAX(FreeBlocks);
185 setDX(XMS_BLOCKS);
186 setBL(XMS_STATUS_SUCCESS);
187
188 break;
189 }
190
191 /* Allocate Extended Memory Block */
192 case 0x09:
193 {
194 WORD Handle;
195 CHAR Result = XmsAlloc(getDX(), &Handle);
196
197 if (Result >= 0)
198 {
199 setAX(1);
200 setDX(Handle);
201 }
202 else
203 {
204 setAX(0);
205 setBL(Result);
206 }
207
208 break;
209 }
210
211 /* Free Extended Memory Block */
212 case 0x0A:
213 {
214 CHAR Result = XmsFree(getDX());
215
216 setAX(Result >= 0);
217 setBL(Result);
218
219 break;
220 }
221
222 /* Lock Extended Memory Block */
223 case 0x0C:
224 {
225 DWORD Address;
226 CHAR Result = XmsLock(getDX(), &Address);
227
228 if (Result >= 0)
229 {
230 setAX(1);
231
232 /* Store the LINEAR address in DX:BX */
233 setDX(HIWORD(Address));
234 setBX(LOWORD(Address));
235 }
236 else
237 {
238 setAX(0);
239 setBL(Result);
240 }
241
242 break;
243 }
244
245 /* Unlock Extended Memory Block */
246 case 0x0D:
247 {
248 CHAR Result = XmsUnlock(getDX());
249
250 setAX(Result >= 0);
251 setBL(Result);
252
253 break;
254 }
255
256 default:
257 {
258 DPRINT1("XMS command AH = 0x%02X NOT IMPLEMENTED\n", getAH());
259 setBL(XMS_STATUS_NOT_IMPLEMENTED);
260 }
261 }
262 }
263
264 /* PUBLIC FUNCTIONS ***********************************************************/
265
266 BOOLEAN XmsGetDriverEntry(PDWORD Pointer)
267 {
268 if (Node == NULL) return FALSE;
269 *Pointer = DEVICE_PRIVATE_AREA(Node->Driver);
270 return TRUE;
271 }
272
273 VOID XmsInitialize(VOID)
274 {
275 RtlZeroMemory(HandleTable, sizeof(HandleTable));
276 RtlZeroMemory(BitmapBuffer, sizeof(BitmapBuffer));
277 RtlInitializeBitMap(&AllocBitmap, BitmapBuffer, XMS_BLOCKS);
278
279 Node = DosCreateDeviceEx(DOS_DEVATTR_IOCTL | DOS_DEVATTR_CHARACTER,
280 XMS_DEVICE_NAME,
281 sizeof(EntryProcedure));
282
283 RegisterBop(BOP_XMS, XmsBopProcedure);
284
285 /* Copy the entry routine to the device private area */
286 RtlMoveMemory(FAR_POINTER(DEVICE_PRIVATE_AREA(Node->Driver)),
287 EntryProcedure,
288 sizeof(EntryProcedure));
289 }
290
291 VOID XmsCleanup(VOID)
292 {
293 RegisterBop(BOP_XMS, NULL);
294 DosDeleteDevice(Node);
295 }