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