[NTOBJSHEX]
[reactos.git] / reactos / subsystems / mvdm / ntvdm / ems.c
1 /*
2 * COPYRIGHT: GPLv2+ - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: ems.c
5 * PURPOSE: Expanded Memory Support
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #define NDEBUG
12
13 #include "emulator.h"
14 #include "bios/bios32/bios32p.h"
15 #include <ndk/rtltypes.h>
16 #include <ndk/rtlfuncs.h>
17 #include "ems.h"
18 #include "memory.h"
19
20 /* PRIVATE VARIABLES **********************************************************/
21
22 static RTL_BITMAP AllocBitmap;
23 static ULONG BitmapBuffer[(EMS_TOTAL_PAGES + sizeof(ULONG) - 1) / sizeof(ULONG)];
24 static EMS_PAGE PageTable[EMS_TOTAL_PAGES];
25 static EMS_HANDLE HandleTable[EMS_MAX_HANDLES];
26 static PVOID Mapping[EMS_PHYSICAL_PAGES] = { NULL };
27
28 /* PRIVATE FUNCTIONS **********************************************************/
29
30 static USHORT EmsFree(USHORT Handle)
31 {
32 PLIST_ENTRY Entry;
33 PEMS_HANDLE HandleEntry = &HandleTable[Handle];
34
35 if (Handle >= EMS_MAX_HANDLES || !HandleEntry->Allocated)
36 {
37 return EMS_STATUS_INVALID_HANDLE;
38 }
39
40 for (Entry = HandleEntry->PageList.Flink;
41 Entry != &HandleEntry->PageList;
42 Entry = Entry->Flink)
43 {
44 PEMS_PAGE PageEntry = (PEMS_PAGE)CONTAINING_RECORD(Entry, EMS_PAGE, Entry);
45 ULONG PageNumber = ARRAY_INDEX(PageEntry, PageTable);
46
47 /* Free the page */
48 RtlClearBits(&AllocBitmap, PageNumber, 1);
49 }
50
51 HandleEntry->Allocated = FALSE;
52 HandleEntry->PageCount = 0;
53 InitializeListHead(&HandleEntry->PageList);
54
55 return EMS_STATUS_OK;
56 }
57
58 static UCHAR EmsAlloc(USHORT NumPages, PUSHORT Handle)
59 {
60 ULONG i, CurrentIndex = 0;
61 PEMS_HANDLE HandleEntry;
62
63 if (NumPages == 0) return EMS_STATUS_ZERO_PAGES;
64
65 for (i = 0; i < EMS_MAX_HANDLES; i++)
66 {
67 HandleEntry = &HandleTable[i];
68 if (!HandleEntry->Allocated)
69 {
70 *Handle = i;
71 break;
72 }
73 }
74
75 if (i == EMS_MAX_HANDLES) return EMS_STATUS_NO_MORE_HANDLES;
76 HandleEntry->Allocated = TRUE;
77
78 while (HandleEntry->PageCount < NumPages)
79 {
80 ULONG RunStart;
81 ULONG RunSize = RtlFindNextForwardRunClear(&AllocBitmap, CurrentIndex, &RunStart);
82
83 if (RunSize == 0)
84 {
85 /* Free what's been allocated already and report failure */
86 EmsFree(*Handle);
87 return EMS_STATUS_INSUFFICIENT_PAGES;
88 }
89 else if ((HandleEntry->PageCount + RunSize) > NumPages)
90 {
91 /* We don't need the entire run */
92 RunSize = NumPages - HandleEntry->PageCount;
93 }
94
95 CurrentIndex = RunStart + RunSize;
96 HandleEntry->PageCount += RunSize;
97 RtlSetBits(&AllocBitmap, RunStart, RunSize);
98
99 for (i = 0; i < RunSize; i++)
100 {
101 PageTable[RunStart + i].Handle = *Handle;
102 InsertTailList(&HandleEntry->PageList, &PageTable[RunStart + i].Entry);
103 }
104 }
105
106 return EMS_STATUS_OK;
107 }
108
109 static PEMS_PAGE GetLogicalPage(PEMS_HANDLE Handle, USHORT LogicalPage)
110 {
111 PLIST_ENTRY Entry = Handle->PageList.Flink;
112
113 while (LogicalPage)
114 {
115 if (Entry == &Handle->PageList) return NULL;
116 LogicalPage--;
117 Entry = Entry->Flink;
118 }
119
120 return (PEMS_PAGE)CONTAINING_RECORD(Entry, EMS_PAGE, Entry);
121 }
122
123 static USHORT EmsMap(USHORT Handle, UCHAR PhysicalPage, USHORT LogicalPage)
124 {
125 PEMS_PAGE PageEntry;
126 PEMS_HANDLE HandleEntry = &HandleTable[Handle];
127
128 if (PhysicalPage >= EMS_PHYSICAL_PAGES) return EMS_STATUS_INV_PHYSICAL_PAGE;
129 if (LogicalPage == 0xFFFF)
130 {
131 /* Unmap */
132 Mapping[PhysicalPage] = NULL;
133 return EMS_STATUS_OK;
134 }
135
136 if (Handle >= EMS_MAX_HANDLES || !HandleEntry->Allocated) return EMS_STATUS_INVALID_HANDLE;
137
138 PageEntry = GetLogicalPage(HandleEntry, LogicalPage);
139 if (!PageEntry) return EMS_STATUS_INV_LOGICAL_PAGE;
140
141 Mapping[PhysicalPage] = (PVOID)(EMS_ADDRESS + ARRAY_INDEX(PageEntry, PageTable) * EMS_PAGE_SIZE);
142 return EMS_STATUS_OK;
143 }
144
145 static VOID WINAPI EmsIntHandler(LPWORD Stack)
146 {
147 switch (getAH())
148 {
149 /* Get Manager Status */
150 case 0x40:
151 {
152 setAH(EMS_STATUS_OK);
153 break;
154 }
155
156 /* Get Page Frame Segment */
157 case 0x41:
158 {
159 setAH(EMS_STATUS_OK);
160 setBX(EMS_SEGMENT);
161 break;
162 }
163
164 /* Get Number Of Pages */
165 case 0x42:
166 {
167 setAH(EMS_STATUS_OK);
168 setBX(RtlNumberOfClearBits(&AllocBitmap));
169 setDX(EMS_TOTAL_PAGES);
170 break;
171 }
172
173 /* Get Handle And Allocate Memory */
174 case 0x43:
175 {
176 USHORT Handle;
177 UCHAR Status = EmsAlloc(getBX(), &Handle);
178
179 setAH(Status);
180 if (Status == EMS_STATUS_OK) setDX(Handle);
181 break;
182 }
183
184 /* Map Memory */
185 case 0x44:
186 {
187 setAH(EmsMap(getDX(), getAL(), getBX()));
188 break;
189 }
190
191 /* Release Handle And Memory */
192 case 0x45:
193 {
194 setAH(EmsFree(getDX()));
195 break;
196 }
197
198 /* Move/Exchange Memory */
199 case 0x57:
200 {
201 PUCHAR SourcePtr, DestPtr;
202 PEMS_HANDLE HandleEntry;
203 PEMS_PAGE PageEntry;
204 BOOLEAN Exchange = getAL();
205 PEMS_COPY_DATA Data = (PEMS_COPY_DATA)SEG_OFF_TO_PTR(getDS(), getSI());
206
207 if (Data->SourceType)
208 {
209 /* Expanded memory */
210 HandleEntry = &HandleTable[Data->SourceHandle];
211
212 if (Data->SourceHandle >= EMS_MAX_HANDLES || !HandleEntry->Allocated)
213 {
214 setAL(EMS_STATUS_INVALID_HANDLE);
215 break;
216 }
217
218 PageEntry = GetLogicalPage(HandleEntry, Data->SourceSegment);
219
220 if (!PageEntry)
221 {
222 setAL(EMS_STATUS_INV_LOGICAL_PAGE);
223 break;
224 }
225
226 SourcePtr = (PUCHAR)REAL_TO_PHYS(EMS_ADDRESS
227 + ARRAY_INDEX(PageEntry, PageTable)
228 * EMS_PAGE_SIZE
229 + Data->SourceOffset);
230 }
231 else
232 {
233 /* Conventional memory */
234 SourcePtr = (PUCHAR)SEG_OFF_TO_PTR(Data->SourceSegment, Data->SourceOffset);
235 }
236
237 if (Data->DestType)
238 {
239 /* Expanded memory */
240 HandleEntry = &HandleTable[Data->DestHandle];
241
242 if (Data->SourceHandle >= EMS_MAX_HANDLES || !HandleEntry->Allocated)
243 {
244 setAL(EMS_STATUS_INVALID_HANDLE);
245 break;
246 }
247
248 PageEntry = GetLogicalPage(HandleEntry, Data->DestSegment);
249
250 if (!PageEntry)
251 {
252 setAL(EMS_STATUS_INV_LOGICAL_PAGE);
253 break;
254 }
255
256 DestPtr = (PUCHAR)REAL_TO_PHYS(EMS_ADDRESS
257 + ARRAY_INDEX(PageEntry, PageTable)
258 * EMS_PAGE_SIZE
259 + Data->DestOffset);
260 }
261 else
262 {
263 /* Conventional memory */
264 DestPtr = (PUCHAR)SEG_OFF_TO_PTR(Data->DestSegment, Data->DestOffset);
265 }
266
267 if (Exchange)
268 {
269 ULONG i;
270
271 /* Exchange */
272 for (i = 0; i < Data->RegionLength; i++)
273 {
274 UCHAR Temp = DestPtr[i];
275 DestPtr[i] = SourcePtr[i];
276 SourcePtr[i] = Temp;
277 }
278 }
279 else
280 {
281 /* Move */
282 RtlMoveMemory(DestPtr, SourcePtr, Data->RegionLength);
283 }
284
285 setAL(EMS_STATUS_OK);
286 break;
287 }
288
289 default:
290 {
291 DPRINT1("EMS function AH = %02X NOT IMPLEMENTED\n", getAH());
292 setAH(EMS_STATUS_UNKNOWN_FUNCTION);
293 break;
294 }
295 }
296 }
297
298 static VOID NTAPI EmsReadMemory(ULONG Address, PVOID Buffer, ULONG Size)
299 {
300 ULONG i;
301 ULONG RelativeAddress = Address - TO_LINEAR(EMS_SEGMENT, 0);
302 ULONG FirstPage = RelativeAddress / EMS_PAGE_SIZE;
303 ULONG LastPage = (RelativeAddress + Size - 1) / EMS_PAGE_SIZE;
304 ULONG Offset, Length;
305
306 for (i = FirstPage; i <= LastPage; i++)
307 {
308 Offset = (i == FirstPage) ? Address & (EMS_PAGE_SIZE - 1) : 0;
309 Length = ((i == LastPage)
310 ? (Address + Size - (LastPage << EMS_PAGE_BITS))
311 : EMS_PAGE_SIZE) - Offset;
312
313 if (Mapping[i]) RtlCopyMemory(Buffer, (PVOID)((ULONG_PTR)Mapping[i] + Offset), Length);
314 Buffer = (PVOID)((ULONG_PTR)Buffer + Length);
315 }
316 }
317
318 static BOOLEAN NTAPI EmsWriteMemory(ULONG Address, PVOID Buffer, ULONG Size)
319 {
320 ULONG i;
321 ULONG RelativeAddress = Address - TO_LINEAR(EMS_SEGMENT, 0);
322 ULONG FirstPage = RelativeAddress / EMS_PAGE_SIZE;
323 ULONG LastPage = (RelativeAddress + Size - 1) / EMS_PAGE_SIZE;
324 ULONG Offset, Length;
325
326 for (i = FirstPage; i <= LastPage; i++)
327 {
328 Offset = (i == FirstPage) ? Address & (EMS_PAGE_SIZE - 1) : 0;
329 Length = ((i == LastPage)
330 ? (Address + Size - (LastPage << EMS_PAGE_BITS))
331 : EMS_PAGE_SIZE) - Offset;
332
333 if (Mapping[i]) RtlCopyMemory((PVOID)((ULONG_PTR)Mapping[i] + Offset), Buffer, Length);
334 Buffer = (PVOID)((ULONG_PTR)Buffer + Length);
335 }
336
337 return TRUE;
338 }
339
340 /* PUBLIC FUNCTIONS ***********************************************************/
341
342 VOID EmsInitialize(VOID)
343 {
344 ULONG i;
345
346 RtlZeroMemory(BitmapBuffer, sizeof(BitmapBuffer));
347 RtlInitializeBitMap(&AllocBitmap, BitmapBuffer, EMS_TOTAL_PAGES);
348
349 for (i = 0; i < EMS_MAX_HANDLES; i++)
350 {
351 HandleTable[i].Allocated = FALSE;
352 HandleTable[i].PageCount = 0;
353 InitializeListHead(&HandleTable[i].PageList);
354 }
355
356 MemInstallFastMemoryHook((PVOID)TO_LINEAR(EMS_SEGMENT, 0),
357 EMS_PHYSICAL_PAGES * EMS_PAGE_SIZE,
358 EmsReadMemory,
359 EmsWriteMemory);
360
361 RegisterBiosInt32(EMS_INTERRUPT_NUM, EmsIntHandler);
362 }
363
364 VOID EmsCleanup(VOID)
365 {
366 MemRemoveFastMemoryHook((PVOID)TO_LINEAR(EMS_SEGMENT, 0),
367 EMS_PHYSICAL_PAGES * EMS_PAGE_SIZE);
368 }