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