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