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