xref: /reactos/subsystems/mvdm/ntvdm/cpu/cpu.c (revision c2c66aff)
1 /*
2  * COPYRIGHT:       GPL - See COPYING in the top level directory
3  * PROJECT:         ReactOS Virtual DOS Machine
4  * FILE:            subsystems/mvdm/ntvdm/cpu/cpu.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 
13 #define NDEBUG
14 #include <debug.h>
15 
16 #include "emulator.h"
17 #include "cpu.h"
18 
19 #include "memory.h"
20 #include "callback.h"
21 #include "bop.h"
22 #include <isvbop.h>
23 
24 #include "clock.h"
25 #include "bios/rom.h"
26 #include "hardware/cmos.h"
27 #include "hardware/keyboard.h"
28 #include "hardware/mouse.h"
29 #include "hardware/pic.h"
30 #include "hardware/ps2.h"
31 #include "hardware/sound/speaker.h"
32 #include "hardware/pit.h"
33 #include "hardware/video/svga.h"
34 
35 #include "io.h"
36 
37 /* PRIVATE VARIABLES **********************************************************/
38 
39 FAST486_STATE EmulatorContext;
40 BOOLEAN CpuRunning = FALSE;
41 
42 /* No more than 'MaxCpuCallLevel' recursive CPU calls are allowed */
43 static const INT MaxCpuCallLevel = 32;
44 static INT CpuCallLevel = 0; // == 0: CPU stopped; >= 1: CPU running or halted
45 
46 #if 0
47 LPCWSTR ExceptionName[] =
48 {
49     L"Division By Zero",
50     L"Debug",
51     L"Unexpected Error",
52     L"Breakpoint",
53     L"Integer Overflow",
54     L"Bound Range Exceeded",
55     L"Invalid Opcode",
56     L"FPU Not Available"
57 };
58 #endif
59 
60 // /* BOP Identifiers */
61 // #define BOP_DEBUGGER    0x56    // Break into the debugger from a 16-bit app
62 
63 /* PRIVATE FUNCTIONS **********************************************************/
64 
65 #if 0
66 VOID EmulatorException(BYTE ExceptionNumber, LPWORD Stack)
67 {
68     WORD CodeSegment, InstructionPointer;
69     PBYTE Opcode;
70 
71     ASSERT(ExceptionNumber < 8);
72 
73     /* Get the CS:IP */
74     InstructionPointer = Stack[STACK_IP];
75     CodeSegment = Stack[STACK_CS];
76     Opcode = (PBYTE)SEG_OFF_TO_PTR(CodeSegment, InstructionPointer);
77 
78     /* Display a message to the user */
79     DisplayMessage(L"Exception: %s occured at %04X:%04X\n"
80                    L"Opcode: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
81                    ExceptionName[ExceptionNumber],
82                    CodeSegment,
83                    InstructionPointer,
84                    Opcode[0],
85                    Opcode[1],
86                    Opcode[2],
87                    Opcode[3],
88                    Opcode[4],
89                    Opcode[5],
90                    Opcode[6],
91                    Opcode[7],
92                    Opcode[8],
93                    Opcode[9]);
94 
95     /* Stop the VDM */
96     EmulatorTerminate();
97     return;
98 }
99 #endif
100 
101 // FIXME: This function assumes 16-bit mode!!!
102 VOID CpuExecute(WORD Segment, WORD Offset)
103 {
104     /* Tell Fast486 to move the instruction pointer */
105     Fast486ExecuteAt(&EmulatorContext, Segment, Offset);
106 }
107 
108 VOID CpuStep(VOID)
109 {
110     /* Dump the state for debugging purposes */
111     // Fast486DumpState(&EmulatorContext);
112 
113     /* Execute the next instruction */
114     Fast486StepInto(&EmulatorContext);
115 }
116 
117 LONG CpuExceptionFilter(IN PEXCEPTION_POINTERS ExceptionInfo)
118 {
119     /* Get the exception record */
120     PEXCEPTION_RECORD ExceptionRecord = ExceptionInfo->ExceptionRecord;
121 
122     switch (ExceptionRecord->ExceptionCode)
123     {
124         /* We only handle access violations so far */
125         case EXCEPTION_ACCESS_VIOLATION:
126         {
127             BOOLEAN Writing = (ExceptionRecord->ExceptionInformation[0] == 1);
128 
129             /* Retrieve the address to which a read or write attempt was made */
130             ULONG_PTR Address = ExceptionRecord->ExceptionInformation[1];
131 
132             /*
133              * Check whether the access exception was done inside the virtual memory space
134              * (caused by an emulated app) or outside (casued by a bug in ourselves).
135              */
136             if (Address <  (ULONG_PTR)BaseAddress ||
137                 Address >= (ULONG_PTR)BaseAddress + MAX_ADDRESS)
138             {
139                 DPRINT1("NTVDM: %s access violation at 0x%p outside the virtual memory space!\n",
140                         (Writing ? "Write" : "Read"), Address);
141                 return EXCEPTION_CONTINUE_SEARCH;
142             }
143 
144             /* We are good to go, dispatch to our memory handlers */
145 
146             /* Fix the CPU state */
147             Fast486Rewind(&EmulatorContext);
148 
149             /* Call the memory handler */
150             MemExceptionHandler((ULONG)PHYS_TO_REAL(Address), Writing);
151 
152             /* The execution of the CPU opcode handler MUST NOT continue */
153             return EXCEPTION_EXECUTE_HANDLER;
154         }
155 
156         default:
157         {
158             DPRINT1("NTVDM: Exception 0x%08lx not handled!\n", ExceptionRecord->ExceptionCode);
159             break;
160         }
161     }
162 
163     /* Continue to search for a handler */
164     return EXCEPTION_CONTINUE_SEARCH;
165 }
166 
167 VOID CpuSimulate(VOID)
168 {
169     if (CpuCallLevel > MaxCpuCallLevel)
170     {
171         DisplayMessage(L"Too many CPU levels of recursion (%d, expected maximum %d)",
172                        CpuCallLevel, MaxCpuCallLevel);
173 
174         /* Stop the VDM */
175         EmulatorTerminate();
176         return;
177     }
178     CpuCallLevel++;
179     DPRINT("CpuSimulate --> Level %d\n", CpuCallLevel);
180 
181     CpuRunning = TRUE;
182     while (VdmRunning && CpuRunning)
183     {
184         _SEH2_TRY
185         {
186             while (VdmRunning && CpuRunning) ClockUpdate();
187         }
188         _SEH2_EXCEPT(CpuExceptionFilter(_SEH2_GetExceptionInformation()))
189         {
190             DPRINT("VDM exception handler called\n");
191         }
192         _SEH2_END;
193     }
194 
195     DPRINT("CpuSimulate <-- Level %d\n", CpuCallLevel);
196     CpuCallLevel--;
197     if (!VdmRunning || CpuCallLevel < 0) CpuCallLevel = 0;
198 
199     /* This takes into account for reentrance */
200     if (VdmRunning && (CpuCallLevel > 0)) CpuRunning = TRUE;
201 }
202 
203 VOID CpuUnsimulate(VOID)
204 {
205     /* Stop simulation */
206     CpuRunning = FALSE;
207 }
208 
209 static VOID WINAPI CpuUnsimulateBop(LPWORD Stack)
210 {
211     CpuUnsimulate();
212 }
213 
214 /* PUBLIC FUNCTIONS ***********************************************************/
215 
216 BOOLEAN CpuInitialize(VOID)
217 {
218     // /* Initialize the internal clock */
219     // if (!ClockInitialize())
220     // {
221         // wprintf(L"FATAL: Failed to initialize the clock\n");
222         // return FALSE;
223     // }
224 
225     /* Initialize the CPU */
226     Fast486Initialize(&EmulatorContext,
227                       EmulatorReadMemory,
228                       EmulatorWriteMemory,
229                       EmulatorReadIo,
230                       EmulatorWriteIo,
231                       EmulatorBiosOperation,
232                       EmulatorIntAcknowledge,
233                       EmulatorFpu,
234                       NULL /* TODO: Use a TLB */);
235 
236     /* Initialize the software callback system and register the emulator BOPs */
237     // RegisterBop(BOP_DEBUGGER  , EmulatorDebugBreakBop);
238     RegisterBop(BOP_UNSIMULATE, CpuUnsimulateBop);
239 
240     return TRUE;
241 }
242 
243 VOID CpuCleanup(VOID)
244 {
245     // Fast486Cleanup();
246 }
247 
248 /* EOF */
249