[NTVDM]
[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 #include "ntvdm.h"
12 #include <softx86/softx86.h>
13 #include <softx86/softx87.h>
14
15 softx86_ctx EmulatorContext;
16 softx87_ctx FpuEmulatorContext;
17 static BOOLEAN A20Line = FALSE;
18
19 static VOID EmulatorReadMemory(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
20 {
21 /* If the A20 line is disabled, mask bit 20 */
22 if (!A20Line) Address &= ~(1 << 20);
23
24 /* Make sure the requested address is valid */
25 if ((Address + Size) >= MAX_ADDRESS) return;
26
27 /* Are we reading some of the console video memory? */
28 if (((Address + Size) >= CONSOLE_VIDEO_MEM_START)
29 && (Address < CONSOLE_VIDEO_MEM_END))
30 {
31 /* Call the VDM BIOS to update the video memory */
32 BiosUpdateConsole(max(Address, CONSOLE_VIDEO_MEM_START),
33 min(Address + Size, CONSOLE_VIDEO_MEM_END));
34 }
35
36 /* Read the data from the virtual address space and store it in the buffer */
37 RtlCopyMemory(Buffer, (LPVOID)((ULONG_PTR)BaseAddress + Address), Size);
38 }
39
40 static VOID EmulatorWriteMemory(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
41 {
42 /* If the A20 line is disabled, mask bit 20 */
43 if (!A20Line) Address &= ~(1 << 20);
44
45 /* Make sure the requested address is valid */
46 if ((Address + Size) >= MAX_ADDRESS) return;
47
48 /* Make sure we don't write to the ROM area */
49 if ((Address + Size) >= ROM_AREA_START && (Address < ROM_AREA_END)) return;
50
51 /* Read the data from the buffer and store it in the virtual address space */
52 RtlCopyMemory((LPVOID)((ULONG_PTR)BaseAddress + Address), Buffer, Size);
53
54 /* Check if we modified the console video memory */
55 if (((Address + Size) >= CONSOLE_VIDEO_MEM_START)
56 && (Address < CONSOLE_VIDEO_MEM_END))
57 {
58 /* Call the VDM BIOS to update the screen */
59 BiosUpdateConsole(max(Address, CONSOLE_VIDEO_MEM_START),
60 min(Address + Size, CONSOLE_VIDEO_MEM_END));
61 }
62 }
63
64 static VOID EmulatorReadIo(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
65 {
66 switch (Address)
67 {
68 case PIC_MASTER_CMD:
69 case PIC_SLAVE_CMD:
70 {
71 *Buffer = PicReadCommand(Address);
72 break;
73 }
74
75 case PIC_MASTER_DATA:
76 case PIC_SLAVE_DATA:
77 {
78 *Buffer = PicReadData(Address);
79 break;
80 }
81
82 case PIT_DATA_PORT(0):
83 case PIT_DATA_PORT(1):
84 case PIT_DATA_PORT(2):
85 {
86 *Buffer = PitReadData(Address - PIT_DATA_PORT(0));
87 break;
88 }
89
90 case PS2_CONTROL_PORT:
91 {
92 *Buffer = KeyboardReadStatus();
93 break;
94 }
95
96 case PS2_DATA_PORT:
97 {
98 *Buffer = KeyboardReadData();
99 break;
100 }
101 }
102 }
103
104 static VOID EmulatorWriteIo(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
105 {
106 BYTE Byte = *Buffer;
107
108 switch (Address)
109 {
110 case PIT_COMMAND_PORT:
111 {
112 PitWriteCommand(Byte);
113 break;
114 }
115
116 case PIT_DATA_PORT(0):
117 case PIT_DATA_PORT(1):
118 case PIT_DATA_PORT(2):
119 {
120 PitWriteData(Address - PIT_DATA_PORT(0), Byte);
121 break;
122 }
123
124 case PIC_MASTER_CMD:
125 case PIC_SLAVE_CMD:
126 {
127 PicWriteCommand(Address, Byte);
128 break;
129 }
130
131 case PIC_MASTER_DATA:
132 case PIC_SLAVE_DATA:
133 {
134 PicWriteData(Address, Byte);
135 break;
136 }
137
138 case PS2_CONTROL_PORT:
139 {
140 KeyboardWriteCommand(Byte);
141 break;
142 }
143
144 case PS2_DATA_PORT:
145 {
146 KeyboardWriteData(Byte);
147 break;
148 }
149 }
150 }
151
152 static VOID EmulatorSoftwareInt(PVOID Context, BYTE Number)
153 {
154 WORD StackSegment, StackPointer, CodeSegment, InstructionPointer;
155 BYTE IntNum;
156
157 /* Check if this is the special interrupt */
158 if (Number == SPECIAL_INT_NUM)
159 {
160 /* Get the SS:SP */
161 StackSegment = EmulatorContext.state->segment_reg[SX86_SREG_SS].val;
162 StackPointer = EmulatorContext.state->general_reg[SX86_REG_SP].val;
163
164 /* Get the interrupt number */
165 IntNum = *(LPBYTE)((ULONG_PTR)BaseAddress + TO_LINEAR(StackSegment, StackPointer));
166
167 /* Move the stack pointer forward one word to skip the interrupt number */
168 StackPointer += sizeof(WORD);
169
170 /* Get the CS:IP */
171 InstructionPointer = *(LPWORD)((ULONG_PTR)BaseAddress
172 + TO_LINEAR(StackSegment, StackPointer));
173 CodeSegment = *(LPWORD)((ULONG_PTR)BaseAddress
174 + TO_LINEAR(StackSegment, StackPointer + sizeof(WORD)));
175
176 /* Check if this was an exception */
177 if (IntNum < 8)
178 {
179 /* Display a message to the user */
180 DisplayMessage(L"Exception: %s occured at %04X:%04X",
181 ExceptionName[IntNum],
182 CodeSegment,
183 InstructionPointer);
184
185 /* Stop the VDM */
186 VdmRunning = FALSE;
187 return;
188 }
189
190 /* Check if this was an PIC IRQ */
191 if (IntNum >= BIOS_PIC_MASTER_INT && IntNum < BIOS_PIC_MASTER_INT + 8)
192 {
193 /* It was an IRQ from the master PIC */
194 BiosHandleIrq(IntNum - BIOS_PIC_MASTER_INT);
195 }
196 else if (IntNum >= BIOS_PIC_SLAVE_INT && IntNum < BIOS_PIC_SLAVE_INT + 8)
197 {
198 /* It was an IRQ from the slave PIC */
199 BiosHandleIrq(IntNum - BIOS_PIC_SLAVE_INT + 8);
200 }
201
202 switch (IntNum)
203 {
204 case VIDEO_BIOS_INTERRUPT:
205 {
206 /* This is the video BIOS interrupt, call the BIOS */
207 BiosVideoService();
208 break;
209 }
210 case 0x20:
211 {
212 DosInt20h(CodeSegment);
213 break;
214 }
215 case 0x21:
216 {
217 DosInt21h(CodeSegment);
218 break;
219 }
220 case 0x23:
221 {
222 DosBreakInterrupt();
223 break;
224 }
225 }
226 }
227 }
228
229 static VOID EmulatorHardwareInt(PVOID Context, BYTE Number)
230 {
231 /* Do nothing */
232 }
233
234 static VOID EmulatorHardwareIntAck(PVOID Context, BYTE Number)
235 {
236 /* Do nothing */
237 }
238
239 /* PUBLIC FUNCTIONS ***********************************************************/
240
241 BOOLEAN EmulatorInitialize()
242 {
243 /* Allocate memory for the 16-bit address space */
244 BaseAddress = HeapAlloc(GetProcessHeap(), 0, MAX_ADDRESS);
245 if (BaseAddress == NULL) return FALSE;
246
247 /* Initialize the softx86 CPU emulator */
248 if (!softx86_init(&EmulatorContext, SX86_CPULEVEL_80286))
249 {
250 HeapFree(GetProcessHeap(), 0, BaseAddress);
251 return FALSE;
252 }
253
254 /* Initialize the softx87 FPU emulator*/
255 if(!softx87_init(&FpuEmulatorContext, SX87_FPULEVEL_8087))
256 {
257 softx86_free(&EmulatorContext);
258 HeapFree(GetProcessHeap(), 0, BaseAddress);
259 return FALSE;
260 }
261
262 /* Set memory read/write callbacks */
263 EmulatorContext.callbacks->on_read_memory = EmulatorReadMemory;
264 EmulatorContext.callbacks->on_write_memory = EmulatorWriteMemory;
265
266 /* Set MMIO read/write callbacks */
267 EmulatorContext.callbacks->on_read_io = EmulatorReadIo;
268 EmulatorContext.callbacks->on_write_io = EmulatorWriteIo;
269
270 /* Set interrupt callbacks */
271 EmulatorContext.callbacks->on_sw_int = EmulatorSoftwareInt;
272 EmulatorContext.callbacks->on_hw_int = EmulatorHardwareInt;
273 EmulatorContext.callbacks->on_hw_int_ack = EmulatorHardwareIntAck;
274
275 /* Connect the emulated FPU to the emulated CPU */
276 softx87_connect_to_CPU(&EmulatorContext, &FpuEmulatorContext);
277
278 /* Enable interrupts */
279 EmulatorSetFlag(EMULATOR_FLAG_IF);
280
281 return TRUE;
282 }
283
284 VOID EmulatorSetStack(WORD Segment, WORD Offset)
285 {
286 /* Call the softx86 API */
287 softx86_set_stack_ptr(&EmulatorContext, Segment, Offset);
288 }
289
290 VOID EmulatorExecute(WORD Segment, WORD Offset)
291 {
292 /* Call the softx86 API */
293 softx86_set_instruction_ptr(&EmulatorContext, Segment, Offset);
294 }
295
296 VOID EmulatorInterrupt(BYTE Number)
297 {
298 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
299 UINT Segment, Offset;
300
301 /* Get the segment and offset */
302 Segment = HIWORD(IntVecTable[Number]);
303 Offset = LOWORD(IntVecTable[Number]);
304
305 /* Call the softx86 API */
306 softx86_make_simple_interrupt_call(&EmulatorContext, &Segment, &Offset);
307 }
308
309 VOID EmulatorExternalInterrupt(BYTE Number)
310 {
311 /* Call the softx86 API */
312 softx86_ext_hw_signal(&EmulatorContext, Number);
313 }
314
315 ULONG EmulatorGetRegister(ULONG Register)
316 {
317 if (Register < EMULATOR_REG_ES)
318 {
319 return EmulatorContext.state->general_reg[Register].val;
320 }
321 else
322 {
323 return EmulatorContext.state->segment_reg[Register - EMULATOR_REG_ES].val;
324 }
325 }
326
327 VOID EmulatorSetRegister(ULONG Register, ULONG Value)
328 {
329 if (Register < EMULATOR_REG_CS)
330 {
331 EmulatorContext.state->general_reg[Register].val = Value;
332 }
333 else
334 {
335 EmulatorContext.state->segment_reg[Register - EMULATOR_REG_ES].val = Value;
336 }
337 }
338
339 BOOLEAN EmulatorGetFlag(ULONG Flag)
340 {
341 return (EmulatorContext.state->reg_flags.val & Flag);
342 }
343
344 VOID EmulatorSetFlag(ULONG Flag)
345 {
346 EmulatorContext.state->reg_flags.val |= Flag;
347 }
348
349 VOID EmulatorClearFlag(ULONG Flag)
350 {
351 EmulatorContext.state->reg_flags.val &= ~Flag;
352 }
353
354 VOID EmulatorStep()
355 {
356 /* Call the softx86 API */
357 if (!softx86_step(&EmulatorContext))
358 {
359 /* Invalid opcode */
360 EmulatorInterrupt(EMULATOR_EXCEPTION_INVALID_OPCODE);
361 }
362 }
363
364 VOID EmulatorCleanup()
365 {
366 /* Free the memory allocated for the 16-bit address space */
367 if (BaseAddress != NULL) HeapFree(GetProcessHeap(), 0, BaseAddress);
368
369 /* Free the softx86 CPU and FPU emulator */
370 softx86_free(&EmulatorContext);
371 softx87_free(&FpuEmulatorContext);
372 }
373
374 VOID EmulatorSetA20(BOOLEAN Enabled)
375 {
376 A20Line = Enabled;
377 }
378
379 /* EOF */