313067c44116414929f301321dd755594e63b50d
[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 // TODO: NOT IMPLEMENTED!
60 }
61
62 static VOID EmulatorWriteIo(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
63 {
64 // TODO: NOT IMPLEMENTED!
65 }
66
67 static VOID EmulatorSoftwareInt(PVOID Context, BYTE Number)
68 {
69 WORD StackSegment, StackPointer, CodeSegment, InstructionPointer;
70 BYTE IntNum;
71
72 /* Check if this is the special interrupt */
73 if (Number == SPECIAL_INT_NUM)
74 {
75 /* Get the SS:SP */
76 StackSegment = EmulatorContext.state->segment_reg[SX86_SREG_SS].val;
77 StackPointer = EmulatorContext.state->general_reg[SX86_REG_SP].val;
78
79 /* Get the interrupt number */
80 IntNum = *(LPBYTE)((ULONG_PTR)BaseAddress + TO_LINEAR(StackSegment, StackPointer));
81
82 /* Move the stack pointer forward one word to skip the interrupt number */
83 StackPointer += sizeof(WORD);
84
85 /* Get the CS:IP */
86 InstructionPointer = *(LPWORD)((ULONG_PTR)BaseAddress
87 + TO_LINEAR(StackSegment, StackPointer));
88 CodeSegment = *(LPWORD)((ULONG_PTR)BaseAddress
89 + TO_LINEAR(StackSegment, StackPointer + sizeof(WORD)));
90
91 /* Check if this was an exception */
92 if (IntNum < 8)
93 {
94 /* Display a message to the user */
95 DisplayMessage(L"Exception: %s occured at %04X:%04X",
96 ExceptionName[IntNum],
97 CodeSegment,
98 InstructionPointer);
99
100 /* Stop the VDM */
101 VdmRunning = FALSE;
102 return;
103 }
104
105 switch (IntNum)
106 {
107 case VIDEO_BIOS_INTERRUPT:
108 {
109 /* This is the video BIOS interrupt, call the BIOS */
110 BiosVideoService();
111 break;
112 }
113 case 0x20:
114 {
115 DosInt20h(CodeSegment);
116 break;
117 }
118 case 0x21:
119 {
120 DosInt21h(CodeSegment);
121 break;
122 }
123 case 0x23:
124 {
125 DosBreakInterrupt();
126 break;
127 }
128 }
129 }
130 }
131
132 /* PUBLIC FUNCTIONS ***********************************************************/
133
134 BOOLEAN EmulatorInitialize()
135 {
136 /* Allocate memory for the 16-bit address space */
137 BaseAddress = HeapAlloc(GetProcessHeap(), 0, MAX_ADDRESS);
138 if (BaseAddress == NULL) return FALSE;
139
140 /* Initialize the softx86 CPU emulator */
141 if (!softx86_init(&EmulatorContext, SX86_CPULEVEL_80186))
142 {
143 HeapFree(GetProcessHeap(), 0, BaseAddress);
144 return FALSE;
145 }
146
147 /* Initialize the softx87 FPU emulator*/
148 if(!softx87_init(&FpuEmulatorContext, SX87_FPULEVEL_8087))
149 {
150 softx86_free(&EmulatorContext);
151 HeapFree(GetProcessHeap(), 0, BaseAddress);
152 return FALSE;
153 }
154
155 /* Set memory read/write callbacks */
156 EmulatorContext.callbacks->on_read_memory = EmulatorReadMemory;
157 EmulatorContext.callbacks->on_write_memory = EmulatorWriteMemory;
158
159 /* Set MMIO read/write callbacks */
160 EmulatorContext.callbacks->on_read_io = EmulatorReadIo;
161 EmulatorContext.callbacks->on_write_io = EmulatorWriteIo;
162
163 /* Set interrupt callbacks */
164 EmulatorContext.callbacks->on_sw_int = EmulatorSoftwareInt;
165
166 /* Connect the emulated FPU to the emulated CPU */
167 softx87_connect_to_CPU(&EmulatorContext, &FpuEmulatorContext);
168
169 return TRUE;
170 }
171
172 VOID EmulatorSetStack(WORD Segment, WORD Offset)
173 {
174 /* Call the softx86 API */
175 softx86_set_stack_ptr(&EmulatorContext, Segment, Offset);
176 }
177
178 VOID EmulatorExecute(WORD Segment, WORD Offset)
179 {
180 /* Call the softx86 API */
181 softx86_set_instruction_ptr(&EmulatorContext, Segment, Offset);
182 }
183
184 VOID EmulatorInterrupt(BYTE Number)
185 {
186 LPWORD IntVecTable = (LPWORD)((ULONG_PTR)BaseAddress);
187 UINT Segment, Offset;
188
189 /* Get the segment and offset */
190 Segment = HIWORD(IntVecTable[Number]);
191 Offset = LOWORD(IntVecTable[Number]);
192
193 /* Call the softx86 API */
194 softx86_make_simple_interrupt_call(&EmulatorContext, &Segment, &Offset);
195 }
196
197 ULONG EmulatorGetRegister(ULONG Register)
198 {
199 if (Register < EMULATOR_REG_CS)
200 {
201 return EmulatorContext.state->general_reg[Register].val;
202 }
203 else
204 {
205 return EmulatorContext.state->segment_reg[(Register >> 3) - 1].val;
206 }
207 }
208
209 VOID EmulatorSetRegister(ULONG Register, ULONG Value)
210 {
211 if (Register < EMULATOR_REG_CS)
212 {
213 EmulatorContext.state->general_reg[Register].val = Value;
214 }
215 else
216 {
217 EmulatorContext.state->segment_reg[(Register >> 3) - 1].val = Value;
218 }
219 }
220
221 BOOLEAN EmulatorGetFlag(ULONG Flag)
222 {
223 return (EmulatorContext.state->reg_flags.val & Flag);
224 }
225
226 VOID EmulatorSetFlag(ULONG Flag)
227 {
228 EmulatorContext.state->reg_flags.val |= Flag;
229 }
230
231 VOID EmulatorClearFlag(ULONG Flag)
232 {
233 EmulatorContext.state->reg_flags.val &= ~Flag;
234 }
235
236 VOID EmulatorStep()
237 {
238 /* Call the softx86 API */
239 softx86_step(&EmulatorContext);
240 }
241
242 VOID EmulatorCleanup()
243 {
244 /* Free the memory allocated for the 16-bit address space */
245 if (BaseAddress != NULL) HeapFree(GetProcessHeap(), 0, BaseAddress);
246
247 /* Free the softx86 CPU and FPU emulator */
248 softx86_free(&EmulatorContext);
249 softx87_free(&FpuEmulatorContext);
250 }
251
252 /* EOF */