[FAST486]
[reactos.git] / 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 #include "cmos.h"
15 #include "bios.h"
16 #include "bop.h"
17 #include "dos.h"
18 #include "speaker.h"
19 #include "vga.h"
20 #include "pic.h"
21 #include "ps2.h"
22 #include "timer.h"
23
24 /* PRIVATE VARIABLES **********************************************************/
25
26 FAST486_STATE EmulatorContext;
27
28 static BOOLEAN A20Line = FALSE;
29
30 /* PRIVATE FUNCTIONS **********************************************************/
31
32 VOID WINAPI EmulatorReadMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size)
33 {
34 UNREFERENCED_PARAMETER(State);
35
36 /* If the A20 line is disabled, mask bit 20 */
37 if (!A20Line) Address &= ~(1 << 20);
38
39 /* Make sure the requested address is valid */
40 if ((Address + Size) >= MAX_ADDRESS) return;
41
42 /* Read the data from the virtual address space and store it in the buffer */
43 RtlCopyMemory(Buffer, (LPVOID)((ULONG_PTR)BaseAddress + Address), Size);
44
45 /* Check if we modified the console video memory */
46 if (((Address + Size) >= VgaGetVideoBaseAddress())
47 && (Address < VgaGetVideoLimitAddress()))
48 {
49 DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
50 DWORD ActualSize = min(Address + Size - 1, VgaGetVideoLimitAddress())
51 - VgaAddress + 1;
52 LPBYTE VgaBuffer = (LPBYTE)((ULONG_PTR)Buffer + VgaAddress - Address);
53
54 /* Read from the VGA memory */
55 VgaReadMemory(VgaAddress, VgaBuffer, ActualSize);
56 }
57 }
58
59 VOID WINAPI EmulatorWriteMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size)
60 {
61 UNREFERENCED_PARAMETER(State);
62
63 /* If the A20 line is disabled, mask bit 20 */
64 if (!A20Line) Address &= ~(1 << 20);
65
66 /* Make sure the requested address is valid */
67 if ((Address + Size) >= MAX_ADDRESS) return;
68
69 /* Make sure we don't write to the ROM area */
70 if ((Address + Size) >= ROM_AREA_START && (Address < ROM_AREA_END)) return;
71
72 /* Read the data from the buffer and store it in the virtual address space */
73 RtlCopyMemory((LPVOID)((ULONG_PTR)BaseAddress + Address), Buffer, Size);
74
75 /* Check if we modified the console video memory */
76 if (((Address + Size) >= VgaGetVideoBaseAddress())
77 && (Address < VgaGetVideoLimitAddress()))
78 {
79 DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
80 DWORD ActualSize = min(Address + Size - 1, VgaGetVideoLimitAddress())
81 - VgaAddress + 1;
82 LPBYTE VgaBuffer = (LPBYTE)((ULONG_PTR)Buffer + VgaAddress - Address);
83
84 /* Write to the VGA memory */
85 VgaWriteMemory(VgaAddress, VgaBuffer, ActualSize);
86 }
87 }
88
89 VOID WINAPI EmulatorReadIo(PFAST486_STATE State, ULONG Port, PVOID Buffer, ULONG DataCount, UCHAR DataSize)
90 {
91 INT i, j;
92 LPBYTE Address = (LPBYTE)Buffer;
93
94 UNREFERENCED_PARAMETER(State);
95
96 for (i = 0; i < DataCount; i++) for (j = 0; j < DataSize; j++)
97 {
98 ULONG CurrentPort = Port + j;
99
100 switch (CurrentPort)
101 {
102 case PIC_MASTER_CMD:
103 case PIC_SLAVE_CMD:
104 {
105 *(Address++) = PicReadCommand(CurrentPort);
106 break;
107 }
108
109 case PIC_MASTER_DATA:
110 case PIC_SLAVE_DATA:
111 {
112 *(Address++) = PicReadData(CurrentPort);
113 break;
114 }
115
116 case PIT_DATA_PORT(0):
117 case PIT_DATA_PORT(1):
118 case PIT_DATA_PORT(2):
119 {
120 *(Address++) = PitReadData(CurrentPort - PIT_DATA_PORT(0));
121 break;
122 }
123
124 case PS2_CONTROL_PORT:
125 {
126 *(Address++) = KeyboardReadStatus();
127 break;
128 }
129
130 case PS2_DATA_PORT:
131 {
132 *(Address++) = KeyboardReadData();
133 break;
134 }
135
136 case CMOS_DATA_PORT:
137 {
138 *(Address++) = CmosReadData();
139 break;
140 }
141
142 case SPEAKER_CONTROL_PORT:
143 {
144 *(Address++) = SpeakerReadStatus();
145 break;
146 }
147
148 case VGA_AC_WRITE:
149 case VGA_AC_READ:
150 case VGA_SEQ_INDEX:
151 case VGA_SEQ_DATA:
152 case VGA_DAC_READ_INDEX:
153 case VGA_DAC_WRITE_INDEX:
154 case VGA_DAC_DATA:
155 case VGA_MISC_READ:
156 case VGA_MISC_WRITE:
157 case VGA_CRTC_INDEX:
158 case VGA_CRTC_DATA:
159 case VGA_GC_INDEX:
160 case VGA_GC_DATA:
161 case VGA_STAT_MONO:
162 case VGA_STAT_COLOR:
163 {
164 *(Address++) = VgaReadPort(CurrentPort);
165 break;
166 }
167
168 default:
169 {
170 DPRINT1("Read from unknown port: 0x%X\n", CurrentPort);
171 }
172 }
173 }
174 }
175
176 VOID WINAPI EmulatorWriteIo(PFAST486_STATE State, ULONG Port, PVOID Buffer, ULONG DataCount, UCHAR DataSize)
177 {
178 INT i, j;
179 LPBYTE Address = (LPBYTE)Buffer;
180
181 UNREFERENCED_PARAMETER(State);
182
183 for (i = 0; i < DataCount; i++) for (j = 0; j < DataSize; j++)
184 {
185 ULONG CurrentPort = Port + j;
186
187 switch (CurrentPort)
188 {
189 case PIT_COMMAND_PORT:
190 {
191 PitWriteCommand(*(Address++));
192 break;
193 }
194
195 case PIT_DATA_PORT(0):
196 case PIT_DATA_PORT(1):
197 case PIT_DATA_PORT(2):
198 {
199 PitWriteData(CurrentPort - PIT_DATA_PORT(0), *(Address++));
200 break;
201 }
202
203 case PIC_MASTER_CMD:
204 case PIC_SLAVE_CMD:
205 {
206 PicWriteCommand(CurrentPort, *(Address++));
207 break;
208 }
209
210 case PIC_MASTER_DATA:
211 case PIC_SLAVE_DATA:
212 {
213 PicWriteData(CurrentPort, *(Address++));
214 break;
215 }
216
217 case PS2_CONTROL_PORT:
218 {
219 KeyboardWriteCommand(*(Address++));
220 break;
221 }
222
223 case PS2_DATA_PORT:
224 {
225 KeyboardWriteData(*(Address++));
226 break;
227 }
228
229 case CMOS_ADDRESS_PORT:
230 {
231 CmosWriteAddress(*(Address++));
232 break;
233 }
234
235 case CMOS_DATA_PORT:
236 {
237 CmosWriteData(*(Address++));
238 break;
239 }
240
241 case SPEAKER_CONTROL_PORT:
242 {
243 SpeakerWriteCommand(*(Address++));
244 break;
245 }
246
247 case VGA_AC_WRITE:
248 case VGA_AC_READ:
249 case VGA_SEQ_INDEX:
250 case VGA_SEQ_DATA:
251 case VGA_DAC_READ_INDEX:
252 case VGA_DAC_WRITE_INDEX:
253 case VGA_DAC_DATA:
254 case VGA_MISC_READ:
255 case VGA_MISC_WRITE:
256 case VGA_CRTC_INDEX:
257 case VGA_CRTC_DATA:
258 case VGA_GC_INDEX:
259 case VGA_GC_DATA:
260 case VGA_STAT_MONO:
261 case VGA_STAT_COLOR:
262 {
263 VgaWritePort(CurrentPort, *(Address++));
264 break;
265 }
266
267 default:
268 {
269 DPRINT1("Write to unknown port: 0x%X\n", CurrentPort);
270 }
271 }
272 }
273 }
274
275 UCHAR WINAPI EmulatorIntAcknowledge(PFAST486_STATE State)
276 {
277 UNREFERENCED_PARAMETER(State);
278
279 /* Get the interrupt number from the PIC */
280 return PicGetInterrupt();
281 }
282
283 /* PUBLIC FUNCTIONS ***********************************************************/
284
285 BOOLEAN EmulatorInitialize(VOID)
286 {
287 /* Allocate memory for the 16-bit address space */
288 BaseAddress = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_ADDRESS);
289 if (BaseAddress == NULL) return FALSE;
290
291 /* Initialize the CPU */
292 Fast486Initialize(&EmulatorContext,
293 EmulatorReadMemory,
294 EmulatorWriteMemory,
295 EmulatorReadIo,
296 EmulatorWriteIo,
297 NULL,
298 EmulatorBiosOperation,
299 EmulatorIntAcknowledge,
300 NULL /* TODO: Use a TLB */);
301
302 /* Enable interrupts */
303 EmulatorSetFlag(EMULATOR_FLAG_IF);
304
305 return TRUE;
306 }
307
308 VOID EmulatorCleanup(VOID)
309 {
310 /* Free the memory allocated for the 16-bit address space */
311 if (BaseAddress != NULL) HeapFree(GetProcessHeap(), 0, BaseAddress);
312 }
313
314 VOID EmulatorSetStack(WORD Segment, DWORD Offset)
315 {
316 Fast486SetStack(&EmulatorContext, Segment, Offset);
317 }
318
319 // FIXME: This function assumes 16-bit mode!!!
320 VOID EmulatorExecute(WORD Segment, WORD Offset)
321 {
322 /* Tell Fast486 to move the instruction pointer */
323 Fast486ExecuteAt(&EmulatorContext, Segment, Offset);
324 }
325
326 VOID EmulatorInterrupt(BYTE Number)
327 {
328 /* Call the Fast486 API */
329 Fast486Interrupt(&EmulatorContext, Number);
330 }
331
332 VOID EmulatorInterruptSignal(VOID)
333 {
334 /* Call the Fast486 API */
335 Fast486InterruptSignal(&EmulatorContext);
336 }
337
338 ULONG EmulatorGetRegister(ULONG Register)
339 {
340 if (Register < EMULATOR_REG_ES)
341 {
342 return EmulatorContext.GeneralRegs[Register].Long;
343 }
344 else
345 {
346 return EmulatorContext.SegmentRegs[Register - EMULATOR_REG_ES].Selector;
347 }
348 }
349
350 VOID EmulatorSetRegister(ULONG Register, ULONG Value)
351 {
352 if (Register < EMULATOR_REG_ES)
353 {
354 EmulatorContext.GeneralRegs[Register].Long = Value;
355 }
356 else
357 {
358 Fast486SetSegment(&EmulatorContext, Register - EMULATOR_REG_ES, (USHORT)Value);
359 }
360 }
361
362 ULONG EmulatorGetProgramCounter(VOID)
363 {
364 return EmulatorContext.InstPtr.Long;
365 }
366
367 BOOLEAN EmulatorGetFlag(ULONG Flag)
368 {
369 return (EmulatorContext.Flags.Long & Flag) ? TRUE : FALSE;
370 }
371
372 VOID EmulatorSetFlag(ULONG Flag)
373 {
374 EmulatorContext.Flags.Long |= Flag;
375 }
376
377 VOID EmulatorClearFlag(ULONG Flag)
378 {
379 EmulatorContext.Flags.Long &= ~Flag;
380 }
381
382 VOID EmulatorStep(VOID)
383 {
384 /* Dump the state for debugging purposes */
385 // Fast486DumpState(&EmulatorContext);
386
387 /* Execute the next instruction */
388 Fast486StepInto(&EmulatorContext);
389 }
390
391 VOID EmulatorSetA20(BOOLEAN Enabled)
392 {
393 A20Line = Enabled;
394 }
395
396 /* EOF */