76ae477a089ed0c01d5514d1f5ef80fb6c0dbaa3
[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 "bios.h"
15 #include "dos.h"
16 #include "vga.h"
17 #include "pic.h"
18 #include "ps2.h"
19 #include "timer.h"
20
21 /* PRIVATE VARIABLES **********************************************************/
22
23 #ifndef NEW_EMULATOR
24 softx86_ctx EmulatorContext;
25 softx87_ctx FpuEmulatorContext;
26 #else
27 EMULATOR_CONTEXT EmulatorContext;
28 #endif
29
30 static BOOLEAN A20Line = FALSE;
31
32 /* PRIVATE FUNCTIONS **********************************************************/
33
34 #ifndef NEW_EMULATOR
35
36 static VOID EmulatorReadMemory(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
37 {
38 /* If the A20 line is disabled, mask bit 20 */
39 if (!A20Line) Address &= ~(1 << 20);
40
41 /* Make sure the requested address is valid */
42 if ((Address + Size) >= MAX_ADDRESS) return;
43
44 /* Read the data from the virtual address space and store it in the buffer */
45 RtlCopyMemory(Buffer, (LPVOID)((ULONG_PTR)BaseAddress + Address), Size);
46
47 /* Check if we modified the console video memory */
48 if (((Address + Size) >= VgaGetVideoBaseAddress())
49 && (Address < VgaGetVideoLimitAddress()))
50 {
51 DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
52 LPBYTE VgaBuffer = &Buffer[VgaAddress - Address];
53
54 /* Read from the VGA memory */
55 VgaReadMemory(VgaAddress, VgaBuffer, Size);
56 }
57 }
58
59 static VOID EmulatorWriteMemory(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
60 {
61 /* If the A20 line is disabled, mask bit 20 */
62 if (!A20Line) Address &= ~(1 << 20);
63
64 /* Make sure the requested address is valid */
65 if ((Address + Size) >= MAX_ADDRESS) return;
66
67 /* Make sure we don't write to the ROM area */
68 if ((Address + Size) >= ROM_AREA_START && (Address < ROM_AREA_END)) return;
69
70 /* Read the data from the buffer and store it in the virtual address space */
71 RtlCopyMemory((LPVOID)((ULONG_PTR)BaseAddress + Address), Buffer, Size);
72
73 /* Check if we modified the console video memory */
74 if (((Address + Size) >= VgaGetVideoBaseAddress())
75 && (Address < VgaGetVideoLimitAddress()))
76 {
77 DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
78 LPBYTE VgaBuffer = &Buffer[VgaAddress - Address];
79
80 /* Write to the VGA memory */
81 VgaWriteMemory(VgaAddress, VgaBuffer, Size);
82 }
83 }
84
85 static VOID EmulatorReadIo(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
86 {
87 switch (Address)
88 {
89 case PIC_MASTER_CMD:
90 case PIC_SLAVE_CMD:
91 {
92 *Buffer = PicReadCommand(Address);
93 break;
94 }
95
96 case PIC_MASTER_DATA:
97 case PIC_SLAVE_DATA:
98 {
99 *Buffer = PicReadData(Address);
100 break;
101 }
102
103 case PIT_DATA_PORT(0):
104 case PIT_DATA_PORT(1):
105 case PIT_DATA_PORT(2):
106 {
107 *Buffer = PitReadData(Address - PIT_DATA_PORT(0));
108 break;
109 }
110
111 case PS2_CONTROL_PORT:
112 {
113 *Buffer = KeyboardReadStatus();
114 break;
115 }
116
117 case PS2_DATA_PORT:
118 {
119 *Buffer = KeyboardReadData();
120 break;
121 }
122
123 case VGA_AC_WRITE:
124 case VGA_AC_READ:
125 case VGA_SEQ_INDEX:
126 case VGA_SEQ_DATA:
127 case VGA_DAC_READ_INDEX:
128 case VGA_DAC_WRITE_INDEX:
129 case VGA_DAC_DATA:
130 case VGA_MISC_READ:
131 case VGA_MISC_WRITE:
132 case VGA_CRTC_INDEX:
133 case VGA_CRTC_DATA:
134 case VGA_GC_INDEX:
135 case VGA_GC_DATA:
136 case VGA_STAT_MONO:
137 case VGA_STAT_COLOR:
138 {
139 *Buffer = VgaReadPort(Address);
140 break;
141 }
142
143 default:
144 {
145 DPRINT1("Read from unknown port: 0x%X\n", Address);
146 }
147 }
148 }
149
150 static VOID EmulatorWriteIo(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
151 {
152 BYTE Byte = *Buffer;
153
154 switch (Address)
155 {
156 case PIT_COMMAND_PORT:
157 {
158 PitWriteCommand(Byte);
159 break;
160 }
161
162 case PIT_DATA_PORT(0):
163 case PIT_DATA_PORT(1):
164 case PIT_DATA_PORT(2):
165 {
166 PitWriteData(Address - PIT_DATA_PORT(0), Byte);
167 break;
168 }
169
170 case PIC_MASTER_CMD:
171 case PIC_SLAVE_CMD:
172 {
173 PicWriteCommand(Address, Byte);
174 break;
175 }
176
177 case PIC_MASTER_DATA:
178 case PIC_SLAVE_DATA:
179 {
180 PicWriteData(Address, Byte);
181 break;
182 }
183
184 case PS2_CONTROL_PORT:
185 {
186 KeyboardWriteCommand(Byte);
187 break;
188 }
189
190 case PS2_DATA_PORT:
191 {
192 KeyboardWriteData(Byte);
193 break;
194 }
195
196 case VGA_AC_WRITE:
197 case VGA_AC_READ:
198 case VGA_SEQ_INDEX:
199 case VGA_SEQ_DATA:
200 case VGA_DAC_READ_INDEX:
201 case VGA_DAC_WRITE_INDEX:
202 case VGA_DAC_DATA:
203 case VGA_MISC_READ:
204 case VGA_MISC_WRITE:
205 case VGA_CRTC_INDEX:
206 case VGA_CRTC_DATA:
207 case VGA_GC_INDEX:
208 case VGA_GC_DATA:
209 case VGA_STAT_MONO:
210 case VGA_STAT_COLOR:
211 {
212 VgaWritePort(Address, Byte);
213 break;
214 }
215
216 default:
217 {
218 DPRINT1("Write to unknown port: 0x%X\n", Address);
219 }
220 }
221 }
222
223 static VOID EmulatorBop(WORD Code)
224 {
225 WORD StackSegment, StackPointer, CodeSegment, InstructionPointer;
226 BYTE IntNum;
227 LPWORD Stack;
228
229 /* Get the SS:SP */
230 #ifndef NEW_EMULATOR
231 StackSegment = EmulatorContext.state->segment_reg[SX86_SREG_SS].val;
232 StackPointer = EmulatorContext.state->general_reg[SX86_REG_SP].val;
233 #else
234 StackSegment = EmulatorContext.Registers[EMULATOR_REG_SS].LowWord;
235 StackPointer = EmulatorContext.Registers[EMULATOR_REG_SP].LowWord;
236 #endif
237
238 /* Get the stack */
239 Stack = (LPWORD)((ULONG_PTR)BaseAddress + TO_LINEAR(StackSegment, StackPointer));
240
241 if (Code == EMULATOR_INT_BOP)
242 {
243 /* Get the interrupt number */
244 IntNum = LOBYTE(Stack[STACK_INT_NUM]);
245
246 /* Get the CS:IP */
247 InstructionPointer = Stack[STACK_IP];
248 CodeSegment = Stack[STACK_CS];
249
250 /* Check if this was an exception */
251 if (IntNum < 8)
252 {
253 /* Display a message to the user */
254 DisplayMessage(L"Exception: %s occured at %04X:%04X",
255 ExceptionName[IntNum],
256 CodeSegment,
257 InstructionPointer);
258
259 /* Stop the VDM */
260 VdmRunning = FALSE;
261 return;
262 }
263
264 /* Check if this was an PIC IRQ */
265 if (IntNum >= BIOS_PIC_MASTER_INT && IntNum < BIOS_PIC_MASTER_INT + 8)
266 {
267 /* It was an IRQ from the master PIC */
268 BiosHandleIrq(IntNum - BIOS_PIC_MASTER_INT, Stack);
269 return;
270 }
271 else if (IntNum >= BIOS_PIC_SLAVE_INT && IntNum < BIOS_PIC_SLAVE_INT + 8)
272 {
273 /* It was an IRQ from the slave PIC */
274 BiosHandleIrq(IntNum - BIOS_PIC_SLAVE_INT + 8, Stack);
275 return;
276 }
277
278 switch (IntNum)
279 {
280 case BIOS_VIDEO_INTERRUPT:
281 {
282 /* This is the video BIOS interrupt, call the BIOS */
283 BiosVideoService(Stack);
284 break;
285 }
286 case BIOS_EQUIPMENT_INTERRUPT:
287 {
288 /* This is the BIOS "get equipment" command, call the BIOS */
289 BiosEquipmentService(Stack);
290 break;
291 }
292 case BIOS_KBD_INTERRUPT:
293 {
294 /* This is the keyboard BIOS interrupt, call the BIOS */
295 BiosKeyboardService(Stack);
296 break;
297 }
298 case BIOS_TIME_INTERRUPT:
299 {
300 /* This is the time BIOS interrupt, call the BIOS */
301 BiosTimeService(Stack);
302 break;
303 }
304 case BIOS_SYS_TIMER_INTERRUPT:
305 {
306 /* BIOS timer update */
307 BiosSystemTimerInterrupt(Stack);
308 break;
309 }
310 case 0x20:
311 {
312 DosInt20h(Stack);
313 break;
314 }
315 case 0x21:
316 {
317 DosInt21h(Stack);
318 break;
319 }
320 case 0x23:
321 {
322 DosBreakInterrupt(Stack);
323 break;
324 }
325 default:
326 {
327 DPRINT1("Unhandled interrupt: 0x%02X\n", IntNum);
328 break;
329 }
330 }
331 }
332 }
333
334 static VOID EmulatorSoftwareInt(PVOID Context, BYTE Number)
335 {
336 /* Do nothing */
337 }
338
339 static VOID EmulatorHardwareInt(PVOID Context, BYTE Number)
340 {
341 /* Do nothing */
342 }
343
344 static VOID EmulatorHardwareIntAck(PVOID Context, BYTE Number)
345 {
346 /* Do nothing */
347 }
348
349 #endif
350
351 /* PUBLIC FUNCTIONS ***********************************************************/
352
353 BOOLEAN EmulatorInitialize()
354 {
355 /* Allocate memory for the 16-bit address space */
356 BaseAddress = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_ADDRESS);
357 if (BaseAddress == NULL) return FALSE;
358
359 #ifndef NEW_EMULATOR
360 /* Initialize the softx86 CPU emulator */
361 if (!softx86_init(&EmulatorContext, SX86_CPULEVEL_80286))
362 {
363 HeapFree(GetProcessHeap(), 0, BaseAddress);
364 return FALSE;
365 }
366
367 /* Initialize the softx87 FPU emulator*/
368 if(!softx87_init(&FpuEmulatorContext, SX87_FPULEVEL_8087))
369 {
370 softx86_free(&EmulatorContext);
371 HeapFree(GetProcessHeap(), 0, BaseAddress);
372 return FALSE;
373 }
374
375 /* Set memory read/write callbacks */
376 EmulatorContext.callbacks->on_read_memory = EmulatorReadMemory;
377 EmulatorContext.callbacks->on_write_memory = EmulatorWriteMemory;
378
379 /* Set MMIO read/write callbacks */
380 EmulatorContext.callbacks->on_read_io = EmulatorReadIo;
381 EmulatorContext.callbacks->on_write_io = EmulatorWriteIo;
382
383 /* Set interrupt callbacks */
384 EmulatorContext.callbacks->on_sw_int = EmulatorSoftwareInt;
385 EmulatorContext.callbacks->on_hw_int = EmulatorHardwareInt;
386 EmulatorContext.callbacks->on_hw_int_ack = EmulatorHardwareIntAck;
387
388 /* Connect the emulated FPU to the emulated CPU */
389 softx87_connect_to_CPU(&EmulatorContext, &FpuEmulatorContext);
390 #else
391 // TODO: NOT IMPLEMENTED
392 #endif
393
394 /* Enable interrupts */
395 EmulatorSetFlag(EMULATOR_FLAG_IF);
396
397 return TRUE;
398 }
399
400 VOID EmulatorSetStack(WORD Segment, WORD Offset)
401 {
402 #ifndef NEW_EMULATOR
403 /* Call the softx86 API */
404 softx86_set_stack_ptr(&EmulatorContext, Segment, Offset);
405 #else
406 // TODO: NOT IMPLEMENTED
407 #endif
408 }
409
410 VOID EmulatorExecute(WORD Segment, WORD Offset)
411 {
412 #ifndef NEW_EMULATOR
413 /* Call the softx86 API */
414 softx86_set_instruction_ptr(&EmulatorContext, Segment, Offset);
415 #else
416 // TODO: NOT IMPLEMENTED
417 #endif
418 }
419
420 VOID EmulatorInterrupt(BYTE Number)
421 {
422 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
423 UINT Segment, Offset;
424
425 /* Get the segment and offset */
426 Segment = HIWORD(IntVecTable[Number]);
427 Offset = LOWORD(IntVecTable[Number]);
428
429 #ifndef NEW_EMULATOR
430 /* Call the softx86 API */
431 softx86_make_simple_interrupt_call(&EmulatorContext, &Segment, &Offset);
432 #else
433 UNREFERENCED_PARAMETER(Segment);
434 UNREFERENCED_PARAMETER(Offset);
435 // TODO: NOT IMPLEMENTED
436 #endif
437 }
438
439 VOID EmulatorExternalInterrupt(BYTE Number)
440 {
441 #ifndef NEW_EMULATOR
442 /* Call the softx86 API */
443 softx86_ext_hw_signal(&EmulatorContext, Number);
444 #endif
445 }
446
447 ULONG EmulatorGetRegister(ULONG Register)
448 {
449 #ifndef NEW_EMULATOR
450 if (Register < EMULATOR_REG_ES)
451 {
452 return EmulatorContext.state->general_reg[Register].val;
453 }
454 else
455 {
456 return EmulatorContext.state->segment_reg[Register - EMULATOR_REG_ES].val;
457 }
458 #else
459 return EmulatorContext.Registers[Register].Long;
460 #endif
461 }
462
463 VOID EmulatorSetRegister(ULONG Register, ULONG Value)
464 {
465 #ifndef NEW_EMULATOR
466 if (Register < EMULATOR_REG_CS)
467 {
468 EmulatorContext.state->general_reg[Register].val = Value;
469 }
470 else
471 {
472 EmulatorContext.state->segment_reg[Register - EMULATOR_REG_ES].val = Value;
473 }
474 #else
475 // TODO: NOT IMPLEMENTED
476 #endif
477 }
478
479 BOOLEAN EmulatorGetFlag(ULONG Flag)
480 {
481 #ifndef NEW_EMULATOR
482 return (EmulatorContext.state->reg_flags.val & Flag) ? TRUE : FALSE;
483 #else
484 return (EmulatorContext.Flags.Long & Flag) ? TRUE : FALSE;
485 #endif
486 }
487
488 VOID EmulatorSetFlag(ULONG Flag)
489 {
490 #ifndef NEW_EMULATOR
491 EmulatorContext.state->reg_flags.val |= Flag;
492 #else
493 EmulatorContext.Flags.Long |= Flag;
494 #endif
495 }
496
497 VOID EmulatorClearFlag(ULONG Flag)
498 {
499 #ifndef NEW_EMULATOR
500 EmulatorContext.state->reg_flags.val &= ~Flag;
501 #else
502 EmulatorContext.Flags.Long &= ~Flag;
503 #endif
504 }
505
506 VOID EmulatorStep(VOID)
507 {
508 LPWORD Instruction;
509
510 #ifndef NEW_EMULATOR
511 /* Print the current position - useful for debugging */
512 DPRINT("Executing at CS:IP = %04X:%04X\n",
513 EmulatorGetRegister(EMULATOR_REG_CS),
514 EmulatorContext.state->reg_ip);
515
516 Instruction = (LPWORD)((ULONG_PTR)BaseAddress
517 + TO_LINEAR(EmulatorGetRegister(EMULATOR_REG_CS),
518 EmulatorContext.state->reg_ip));
519
520 /* Check for the BIOS operation (BOP) sequence */
521 if (Instruction[0] == EMULATOR_BOP)
522 {
523 /* Skip the opcodes */
524 EmulatorContext.state->reg_ip += 4;
525
526 // HACK: Refresh the display because the called function may wait.
527 VgaRefreshDisplay();
528
529 /* Call the BOP handler */
530 EmulatorBop(Instruction[1]);
531 }
532
533 /* Call the softx86 API */
534 if (!softx86_step(&EmulatorContext))
535 {
536 /* Invalid opcode */
537 EmulatorInterrupt(EMULATOR_EXCEPTION_INVALID_OPCODE);
538 }
539 #else
540 // TODO: NOT IMPLEMENTED
541 #endif
542 }
543
544 VOID EmulatorCleanup(VOID)
545 {
546 #ifndef NEW_EMULATOR
547 /* Free the softx86 CPU and FPU emulator */
548 softx87_free(&FpuEmulatorContext);
549 softx86_free(&EmulatorContext);
550 #endif
551
552 /* Free the memory allocated for the 16-bit address space */
553 if (BaseAddress != NULL) HeapFree(GetProcessHeap(), 0, BaseAddress);
554 }
555
556 VOID EmulatorSetA20(BOOLEAN Enabled)
557 {
558 A20Line = Enabled;
559 }
560
561 /* EOF */