[NTVDM]
[reactos.git] / reactos / subsystems / ntvdm / emulator.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: emulator.c
5 * PURPOSE: Minimal x86 machine emulator for the VDM
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
15 #include "cpu/callback.h"
16 #include "cpu/cpu.h"
17 #include "cpu/bop.h"
18 #include <isvbop.h>
19
20 #include "int32.h"
21
22 #include "clock.h"
23 #include "bios/rom.h"
24 #include "hardware/cmos.h"
25 #include "hardware/keyboard.h"
26 #include "hardware/mouse.h"
27 #include "hardware/pic.h"
28 #include "hardware/ps2.h"
29 #include "hardware/speaker.h"
30 #include "hardware/timer.h"
31 #include "hardware/vga.h"
32
33 #include "vddsup.h"
34 #include "io.h"
35
36 /* PRIVATE VARIABLES **********************************************************/
37
38 LPVOID BaseAddress = NULL;
39 BOOLEAN VdmRunning = TRUE;
40
41 static BOOLEAN A20Line = FALSE;
42 static BYTE Port61hState = 0x00;
43
44 static HANDLE InputThread = NULL;
45
46 LPCWSTR ExceptionName[] =
47 {
48 L"Division By Zero",
49 L"Debug",
50 L"Unexpected Error",
51 L"Breakpoint",
52 L"Integer Overflow",
53 L"Bound Range Exceeded",
54 L"Invalid Opcode",
55 L"FPU Not Available"
56 };
57
58 /* BOP Identifiers */
59 #define BOP_DEBUGGER 0x56 // Break into the debugger from a 16-bit app
60
61 /* PRIVATE FUNCTIONS **********************************************************/
62
63 static inline VOID
64 EmulatorMoveMemory(OUT VOID UNALIGNED *Destination,
65 IN const VOID UNALIGNED *Source,
66 IN SIZE_T Length)
67 {
68 #if 1
69 /*
70 * We use a switch here to detect small moves of memory, as these
71 * constitute the bulk of our moves.
72 * Using RtlMoveMemory for all these small moves would be slow otherwise.
73 */
74 switch (Length)
75 {
76 case 0:
77 return;
78
79 case sizeof(UCHAR):
80 *(PUCHAR)Destination = *(PUCHAR)Source;
81 return;
82
83 case sizeof(USHORT):
84 *(PUSHORT)Destination = *(PUSHORT)Source;
85 return;
86
87 case sizeof(ULONG):
88 *(PULONG)Destination = *(PULONG)Source;
89 return;
90
91 case sizeof(ULONGLONG):
92 *(PULONGLONG)Destination = *(PULONGLONG)Source;
93 return;
94
95 default:
96 #if defined(__GNUC__)
97 __builtin_memmove(Destination, Source, Length);
98 #else
99 RtlMoveMemory(Destination, Source, Length);
100 #endif
101 }
102
103 #else // defined(_MSC_VER)
104
105 PUCHAR Dest = (PUCHAR)Destination;
106 PUCHAR Src = (PUCHAR)Source;
107
108 SIZE_T Count, NewSize = Length;
109
110 /* Move dword */
111 Count = NewSize >> 2; // NewSize / sizeof(ULONG);
112 NewSize = NewSize & 3; // NewSize % sizeof(ULONG);
113 __movsd(Dest, Src, Count);
114 Dest += Count << 2; // Count * sizeof(ULONG);
115 Src += Count << 2;
116
117 /* Move word */
118 Count = NewSize >> 1; // NewSize / sizeof(USHORT);
119 NewSize = NewSize & 1; // NewSize % sizeof(USHORT);
120 __movsw(Dest, Src, Count);
121 Dest += Count << 1; // Count * sizeof(USHORT);
122 Src += Count << 1;
123
124 /* Move byte */
125 Count = NewSize; // NewSize / sizeof(UCHAR);
126 // NewSize = NewSize; // NewSize % sizeof(UCHAR);
127 __movsb(Dest, Src, Count);
128
129 #endif
130 }
131
132 VOID WINAPI EmulatorReadMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size)
133 {
134 UNREFERENCED_PARAMETER(State);
135
136 // BIG HACK!!!! To make BIOS images working correctly,
137 // until Aleksander rewrites memory management!!
138 if (Address >= 0xFFFFFFF0) Address -= 0xFFF00000;
139
140 /* If the A20 line is disabled, mask bit 20 */
141 if (!A20Line) Address &= ~(1 << 20);
142
143 /* Make sure the requested address is valid */
144 if ((Address + Size) >= MAX_ADDRESS) return;
145
146 /*
147 * Check if we are going to read the VGA memory and
148 * copy it into the virtual address space if needed.
149 */
150 if (((Address + Size) >= VgaGetVideoBaseAddress())
151 && (Address < VgaGetVideoLimitAddress()))
152 {
153 DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
154 DWORD ActualSize = min(Address + Size - 1, VgaGetVideoLimitAddress())
155 - VgaAddress + 1;
156 LPBYTE DestBuffer = (LPBYTE)REAL_TO_PHYS(VgaAddress);
157
158 /* Read from the VGA memory */
159 VgaReadMemory(VgaAddress, DestBuffer, ActualSize);
160 }
161
162 /* Read the data from the virtual address space and store it in the buffer */
163 EmulatorMoveMemory(Buffer, REAL_TO_PHYS(Address), Size);
164 }
165
166 VOID WINAPI EmulatorWriteMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size)
167 {
168 UNREFERENCED_PARAMETER(State);
169
170 // BIG HACK!!!! To make BIOS images working correctly,
171 // until Aleksander rewrites memory management!!
172 if (Address >= 0xFFFFFFF0) Address -= 0xFFF00000;
173
174 /* If the A20 line is disabled, mask bit 20 */
175 if (!A20Line) Address &= ~(1 << 20);
176
177 /* Make sure the requested address is valid */
178 if ((Address + Size) >= MAX_ADDRESS) return;
179
180 /* Make sure we don't write to the ROM area */
181 if ((Address + Size) >= ROM_AREA_START && (Address < ROM_AREA_END)) return;
182
183 /* Read the data from the buffer and store it in the virtual address space */
184 EmulatorMoveMemory(REAL_TO_PHYS(Address), Buffer, Size);
185
186 /*
187 * Check if we modified the VGA memory.
188 */
189 if (((Address + Size) >= VgaGetVideoBaseAddress())
190 && (Address < VgaGetVideoLimitAddress()))
191 {
192 DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
193 DWORD ActualSize = min(Address + Size - 1, VgaGetVideoLimitAddress())
194 - VgaAddress + 1;
195 LPBYTE SrcBuffer = (LPBYTE)REAL_TO_PHYS(VgaAddress);
196
197 /* Write to the VGA memory */
198 VgaWriteMemory(VgaAddress, SrcBuffer, ActualSize);
199 }
200 }
201
202 UCHAR WINAPI EmulatorIntAcknowledge(PFAST486_STATE State)
203 {
204 UNREFERENCED_PARAMETER(State);
205
206 /* Get the interrupt number from the PIC */
207 return PicGetInterrupt();
208 }
209
210 VOID EmulatorException(BYTE ExceptionNumber, LPWORD Stack)
211 {
212 WORD CodeSegment, InstructionPointer;
213 PBYTE Opcode;
214
215 ASSERT(ExceptionNumber < 8);
216
217 /* Get the CS:IP */
218 InstructionPointer = Stack[STACK_IP];
219 CodeSegment = Stack[STACK_CS];
220 Opcode = (PBYTE)SEG_OFF_TO_PTR(CodeSegment, InstructionPointer);
221
222 /* Display a message to the user */
223 DisplayMessage(L"Exception: %s occured at %04X:%04X\n"
224 L"Opcode: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
225 ExceptionName[ExceptionNumber],
226 CodeSegment,
227 InstructionPointer,
228 Opcode[0],
229 Opcode[1],
230 Opcode[2],
231 Opcode[3],
232 Opcode[4],
233 Opcode[5],
234 Opcode[6],
235 Opcode[7],
236 Opcode[8],
237 Opcode[9]);
238
239 Fast486DumpState(&EmulatorContext);
240
241 /* Stop the VDM */
242 EmulatorTerminate();
243 return;
244 }
245
246 VOID EmulatorTerminate(VOID)
247 {
248 /* Stop the VDM */
249 CpuUnsimulate(); // Halt the CPU
250 VdmRunning = FALSE;
251 }
252
253 VOID EmulatorInterrupt(BYTE Number)
254 {
255 /* Call the Fast486 API */
256 Fast486Interrupt(&EmulatorContext, Number);
257 }
258
259 VOID EmulatorInterruptSignal(VOID)
260 {
261 /* Call the Fast486 API */
262 Fast486InterruptSignal(&EmulatorContext);
263 }
264
265 VOID EmulatorSetA20(BOOLEAN Enabled)
266 {
267 A20Line = Enabled;
268 }
269
270 static VOID WINAPI EmulatorDebugBreakBop(LPWORD Stack)
271 {
272 DPRINT1("NTVDM: BOP_DEBUGGER\n");
273 DebugBreak();
274 }
275
276 static BYTE WINAPI Port61hRead(ULONG Port)
277 {
278 return Port61hState;
279 }
280
281 static VOID WINAPI Port61hWrite(ULONG Port, BYTE Data)
282 {
283 // BOOLEAN SpeakerStateChange = FALSE;
284 BYTE OldPort61hState = Port61hState;
285
286 /* Only the four lowest bytes can be written */
287 Port61hState = (Port61hState & 0xF0) | (Data & 0x0F);
288
289 if ((OldPort61hState ^ Port61hState) & 0x01)
290 {
291 DPRINT("PIT 2 Gate %s\n", Port61hState & 0x01 ? "on" : "off");
292 PitSetGate(2, !!(Port61hState & 0x01));
293 // SpeakerStateChange = TRUE;
294 }
295
296 if ((OldPort61hState ^ Port61hState) & 0x02)
297 {
298 /* There were some change for the speaker... */
299 DPRINT("Speaker %s\n", Port61hState & 0x02 ? "on" : "off");
300 // SpeakerStateChange = TRUE;
301 }
302 // if (SpeakerStateChange) SpeakerChange();
303 SpeakerChange();
304 }
305
306 static VOID WINAPI PitChan0Out(LPVOID Param, BOOLEAN State)
307 {
308 if (State)
309 {
310 DPRINT("PicInterruptRequest\n");
311 PicInterruptRequest(0); // Raise IRQ 0
312 }
313 // else < Lower IRQ 0 >
314 }
315
316 static VOID WINAPI PitChan1Out(LPVOID Param, BOOLEAN State)
317 {
318 #if 0
319 if (State)
320 {
321 /* Set bit 4 of Port 61h */
322 Port61hState |= 1 << 4;
323 }
324 else
325 {
326 /* Clear bit 4 of Port 61h */
327 Port61hState &= ~(1 << 4);
328 }
329 #else
330 Port61hState = (Port61hState & 0xEF) | (State << 4);
331 #endif
332 }
333
334 static VOID WINAPI PitChan2Out(LPVOID Param, BOOLEAN State)
335 {
336 BYTE OldPort61hState = Port61hState;
337
338 #if 0
339 if (State)
340 {
341 /* Set bit 5 of Port 61h */
342 Port61hState |= 1 << 5;
343 }
344 else
345 {
346 /* Clear bit 5 of Port 61h */
347 Port61hState &= ~(1 << 5);
348 }
349 #else
350 Port61hState = (Port61hState & 0xDF) | (State << 5);
351 #endif
352
353 if ((OldPort61hState ^ Port61hState) & 0x20)
354 {
355 DPRINT("PitChan2Out -- Port61hState changed\n");
356 SpeakerChange();
357 }
358 }
359
360
361 static DWORD
362 WINAPI
363 PumpConsoleInput(LPVOID Parameter)
364 {
365 HANDLE ConsoleInput = (HANDLE)Parameter;
366 INPUT_RECORD InputRecord;
367 DWORD Count;
368
369 while (VdmRunning)
370 {
371 /* Make sure the task event is signaled */
372 WaitForSingleObject(VdmTaskEvent, INFINITE);
373
374 /* Wait for an input record */
375 if (!ReadConsoleInput(ConsoleInput, &InputRecord, 1, &Count))
376 {
377 DWORD LastError = GetLastError();
378 DPRINT1("Error reading console input (0x%p, %lu) - Error %lu\n", ConsoleInput, Count, LastError);
379 return LastError;
380 }
381
382 ASSERT(Count != 0);
383
384 /* Check the event type */
385 switch (InputRecord.EventType)
386 {
387 /*
388 * Hardware events
389 */
390 case KEY_EVENT:
391 KeyboardEventHandler(&InputRecord.Event.KeyEvent);
392 break;
393
394 case MOUSE_EVENT:
395 MouseEventHandler(&InputRecord.Event.MouseEvent);
396 break;
397
398 case WINDOW_BUFFER_SIZE_EVENT:
399 ScreenEventHandler(&InputRecord.Event.WindowBufferSizeEvent);
400 break;
401
402 /*
403 * Interface events
404 */
405 case MENU_EVENT:
406 MenuEventHandler(&InputRecord.Event.MenuEvent);
407 break;
408
409 case FOCUS_EVENT:
410 FocusEventHandler(&InputRecord.Event.FocusEvent);
411 break;
412
413 default:
414 break;
415 }
416 }
417
418 return 0;
419 }
420
421 static VOID EnableExtraHardware(HANDLE ConsoleInput)
422 {
423 DWORD ConInMode;
424
425 if (GetConsoleMode(ConsoleInput, &ConInMode))
426 {
427 #if 0
428 // GetNumberOfConsoleMouseButtons();
429 // GetSystemMetrics(SM_CMOUSEBUTTONS);
430 // GetSystemMetrics(SM_MOUSEPRESENT);
431 if (MousePresent)
432 {
433 #endif
434 /* Support mouse input events if there is a mouse on the system */
435 ConInMode |= ENABLE_MOUSE_INPUT;
436 #if 0
437 }
438 else
439 {
440 /* Do not support mouse input events if there is no mouse on the system */
441 ConInMode &= ~ENABLE_MOUSE_INPUT;
442 }
443 #endif
444
445 SetConsoleMode(ConsoleInput, ConInMode);
446 }
447 }
448
449 /* PUBLIC FUNCTIONS ***********************************************************/
450
451 static VOID
452 DumpMemoryRaw(HANDLE hFile)
453 {
454 PVOID Buffer;
455 SIZE_T Size;
456
457 /* Dump the VM memory */
458 SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
459 Buffer = REAL_TO_PHYS(NULL);
460 Size = MAX_ADDRESS - (ULONG_PTR)(NULL);
461 WriteFile(hFile, Buffer, Size, &Size, NULL);
462 }
463
464 static VOID
465 DumpMemoryTxt(HANDLE hFile)
466 {
467 #define LINE_SIZE 75 + 2
468 ULONG i;
469 PBYTE Ptr1, Ptr2;
470 CHAR LineBuffer[LINE_SIZE];
471 PCHAR Line;
472 SIZE_T LineSize;
473
474 /* Dump the VM memory */
475 SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
476 Ptr1 = Ptr2 = REAL_TO_PHYS(NULL);
477 while (MAX_ADDRESS - (ULONG_PTR)PHYS_TO_REAL(Ptr1) > 0)
478 {
479 Ptr1 = Ptr2;
480 Line = LineBuffer;
481
482 /* Print the address */
483 Line += snprintf(Line, LINE_SIZE + LineBuffer - Line, "%08x ", PHYS_TO_REAL(Ptr1));
484
485 /* Print up to 16 bytes... */
486
487 /* ... in hexadecimal form first... */
488 i = 0;
489 while (i++ <= 0x0F && (MAX_ADDRESS - (ULONG_PTR)PHYS_TO_REAL(Ptr1) > 0))
490 {
491 Line += snprintf(Line, LINE_SIZE + LineBuffer - Line, " %02x", *Ptr1);
492 ++Ptr1;
493 }
494
495 /* ... align with spaces if needed... */
496 RtlFillMemory(Line, 0x0F + 4 - i, ' ');
497 Line += 0x0F + 4 - i;
498
499 /* ... then in character form. */
500 i = 0;
501 while (i++ <= 0x0F && (MAX_ADDRESS - (ULONG_PTR)PHYS_TO_REAL(Ptr2) > 0))
502 {
503 *Line++ = ((*Ptr2 >= 0x20 && *Ptr2 <= 0x7E) || (*Ptr2 >= 0x80 && *Ptr2 < 0xFF) ? *Ptr2 : '.');
504 ++Ptr2;
505 }
506
507 /* Newline */
508 *Line++ = '\r';
509 *Line++ = '\n';
510
511 /* Finally write the line to the file */
512 LineSize = Line - LineBuffer;
513 WriteFile(hFile, LineBuffer, LineSize, &LineSize, NULL);
514 }
515 }
516
517 VOID DumpMemory(BOOLEAN TextFormat)
518 {
519 static ULONG DumpNumber = 0;
520
521 HANDLE hFile;
522 WCHAR FileName[MAX_PATH];
523
524 /* Build a suitable file name */
525 _snwprintf(FileName, MAX_PATH,
526 L"memdump%lu.%s",
527 DumpNumber,
528 TextFormat ? L"txt" : L"dat");
529 ++DumpNumber;
530
531 DPRINT1("Creating memory dump file '%S'...\n", FileName);
532
533 /* Always create the dump file */
534 hFile = CreateFileW(FileName,
535 GENERIC_WRITE,
536 0,
537 NULL,
538 CREATE_ALWAYS,
539 FILE_ATTRIBUTE_NORMAL,
540 NULL);
541
542 if (hFile == INVALID_HANDLE_VALUE)
543 {
544 DPRINT1("Error when creating '%S' for memory dumping, GetLastError() = %u\n",
545 FileName, GetLastError());
546 return;
547 }
548
549 /* Dump the VM memory in the chosen format */
550 if (TextFormat)
551 DumpMemoryTxt(hFile);
552 else
553 DumpMemoryRaw(hFile);
554
555 /* Close the file */
556 CloseHandle(hFile);
557
558 DPRINT1("Memory dump done\n");
559 }
560
561 BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput)
562 {
563 /* Allocate memory for the 16-bit address space */
564 BaseAddress = HeapAlloc(GetProcessHeap(), /*HEAP_ZERO_MEMORY*/ 0, MAX_ADDRESS);
565 if (BaseAddress == NULL)
566 {
567 wprintf(L"FATAL: Failed to allocate VDM memory.\n");
568 return FALSE;
569 }
570 /*
571 * For diagnostics purposes, we fill the memory with INT 0x03 codes
572 * so that if a program wants to execute random code in memory, we can
573 * retrieve the exact CS:IP where the problem happens.
574 */
575 RtlFillMemory(BaseAddress, MAX_ADDRESS, 0xCC);
576
577 /* Initialize I/O ports */
578 /* Initialize RAM */
579
580 /* Initialize the CPU */
581
582 /* Initialize the internal clock */
583 if (!ClockInitialize())
584 {
585 wprintf(L"FATAL: Failed to initialize the clock\n");
586 return FALSE;
587 }
588
589 /* Initialize the CPU */
590 CpuInitialize();
591 // Fast486Initialize(&EmulatorContext,
592 // EmulatorReadMemory,
593 // EmulatorWriteMemory,
594 // EmulatorReadIo,
595 // EmulatorWriteIo,
596 // NULL,
597 // EmulatorBiosOperation,
598 // EmulatorIntAcknowledge,
599 // NULL /* TODO: Use a TLB */);
600
601 /* Initialize DMA */
602
603 /* Initialize the PIC, the PIT, the CMOS and the PC Speaker */
604 PicInitialize();
605 PitInitialize();
606 CmosInitialize();
607 SpeakerInitialize();
608
609 /* Set output functions */
610 PitSetOutFunction(0, NULL, PitChan0Out);
611 PitSetOutFunction(1, NULL, PitChan1Out);
612 PitSetOutFunction(2, NULL, PitChan2Out);
613
614 /* Register the I/O Ports */
615 RegisterIoPort(CONTROL_SYSTEM_PORT61H, Port61hRead, Port61hWrite);
616
617 /* Set the console input mode */
618 // FIXME: Activate ENABLE_WINDOW_INPUT when we will want to perform actions
619 // upon console window events (screen buffer resize, ...).
620 SetConsoleMode(ConsoleInput, ENABLE_PROCESSED_INPUT /* | ENABLE_WINDOW_INPUT */);
621 // SetConsoleMode(ConsoleOutput, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
622
623 /**/EnableExtraHardware(ConsoleInput);/**/
624
625 /* Initialize the PS/2 port */
626 PS2Initialize();
627
628 /* Initialize the keyboard and mouse and connect them to their PS/2 ports */
629 KeyboardInit(0);
630 MouseInit(1);
631
632 /**************** ATTACH INPUT WITH CONSOLE *****************/
633 /* Start the input thread */
634 InputThread = CreateThread(NULL, 0, &PumpConsoleInput, ConsoleInput, 0, NULL);
635 if (InputThread == NULL)
636 {
637 DisplayMessage(L"Failed to create the console input thread.");
638 return FALSE;
639 }
640 /************************************************************/
641
642 /* Initialize the VGA */
643 if (!VgaInitialize(ConsoleOutput))
644 {
645 DisplayMessage(L"Failed to initialize VGA support.");
646 return FALSE;
647 }
648
649 /* Initialize the software callback system and register the emulator BOPs */
650 InitializeInt32();
651 RegisterBop(BOP_DEBUGGER , EmulatorDebugBreakBop);
652 // RegisterBop(BOP_UNSIMULATE, CpuUnsimulateBop);
653
654 /* Initialize VDD support */
655 VDDSupInitialize();
656
657 return TRUE;
658 }
659
660 VOID EmulatorCleanup(VOID)
661 {
662 VgaCleanup();
663
664 /* Close the input thread handle */
665 if (InputThread != NULL) CloseHandle(InputThread);
666 InputThread = NULL;
667
668 PS2Cleanup();
669
670 SpeakerCleanup();
671 CmosCleanup();
672 // PitCleanup();
673 // PicCleanup();
674
675 CpuCleanup();
676
677 /* Free the memory allocated for the 16-bit address space */
678 if (BaseAddress != NULL) HeapFree(GetProcessHeap(), 0, BaseAddress);
679 }
680
681
682
683 VOID
684 WINAPI
685 VDDSimulate16(VOID)
686 {
687 CpuSimulate();
688 }
689
690 VOID
691 WINAPI
692 VDDTerminateVDM(VOID)
693 {
694 /* Stop the VDM */
695 EmulatorTerminate();
696 }
697
698 PBYTE
699 WINAPI
700 Sim32pGetVDMPointer(IN ULONG Address,
701 IN BOOLEAN ProtectedMode)
702 {
703 // FIXME
704 UNREFERENCED_PARAMETER(ProtectedMode);
705
706 /*
707 * HIWORD(Address) == Segment (if ProtectedMode == FALSE)
708 * or Selector (if ProtectedMode == TRUE )
709 * LOWORD(Address) == Offset
710 */
711 return (PBYTE)FAR_POINTER(Address);
712 }
713
714 PBYTE
715 WINAPI
716 MGetVdmPointer(IN ULONG Address,
717 IN ULONG Size,
718 IN BOOLEAN ProtectedMode)
719 {
720 UNREFERENCED_PARAMETER(Size);
721 return Sim32pGetVDMPointer(Address, ProtectedMode);
722 }
723
724 PVOID
725 WINAPI
726 VdmMapFlat(IN USHORT Segment,
727 IN ULONG Offset,
728 IN VDM_MODE Mode)
729 {
730 // FIXME
731 UNREFERENCED_PARAMETER(Mode);
732
733 return SEG_OFF_TO_PTR(Segment, Offset);
734 }
735
736 BOOL
737 WINAPI
738 VdmFlushCache(IN USHORT Segment,
739 IN ULONG Offset,
740 IN ULONG Size,
741 IN VDM_MODE Mode)
742 {
743 // FIXME
744 UNIMPLEMENTED;
745 return TRUE;
746 }
747
748 BOOL
749 WINAPI
750 VdmUnmapFlat(IN USHORT Segment,
751 IN ULONG Offset,
752 IN PVOID Buffer,
753 IN VDM_MODE Mode)
754 {
755 // FIXME
756 UNIMPLEMENTED;
757 return TRUE;
758 }
759
760 /* EOF */