Berend Reitsma <berend at asset-control.com>
[wine.git] / loader / dos / dosvm.c
blob17d6d4a3982c17bfa3d235779348b27e76d032e5
1 /*
2 * DOS Virtual Machine
4 * Copyright 1998 Ove Kåven
6 * This code hasn't been completely cleaned up yet.
7 */
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <signal.h>
15 #include <unistd.h>
16 #include <sys/time.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include "windows.h"
20 #include "winbase.h"
21 #include "winnt.h"
22 #include "sig_context.h"
23 #include "msdos.h"
24 #include "miscemu.h"
25 #include "debugger.h"
26 #include "debug.h"
27 #include "module.h"
28 #include "task.h"
29 #include "ldt.h"
30 #include "dosexe.h"
31 #include "dosmod.h"
33 void (*ctx_debug_call)(int sig,CONTEXT*ctx)=NULL;
34 BOOL32 (*instr_emu_call)(SIGCONTEXT*ctx)=NULL;
36 #ifdef MZ_SUPPORTED
38 #include <sys/mman.h>
39 #include <sys/vm86.h>
41 static void DOSVM_Dump( LPDOSTASK lpDosTask, int fn, int sig,
42 struct vm86plus_struct*VM86 )
44 unsigned iofs;
45 BYTE*inst;
46 int x;
48 switch (VM86_TYPE(fn)) {
49 case VM86_SIGNAL:
50 printf("Trapped signal %d\n",sig); break;
51 case VM86_UNKNOWN:
52 printf("Trapped unhandled GPF\n"); break;
53 case VM86_INTx:
54 printf("Trapped INT %02x\n",VM86_ARG(fn)); break;
55 case VM86_STI:
56 printf("Trapped STI\n"); break;
57 case VM86_PICRETURN:
58 printf("Trapped due to pending PIC request\n"); break;
59 case VM86_TRAP:
60 printf("Trapped debug request\n"); break;
61 default:
62 printf("Trapped unknown VM86 type %d arg %d\n",VM86_TYPE(fn),VM86_ARG(fn)); break;
64 #define REGS VM86->regs
65 fprintf(stderr,"AX=%04lX CX=%04lX DX=%04lX BX=%04lX\n",REGS.eax,REGS.ecx,REGS.edx,REGS.ebx);
66 fprintf(stderr,"SI=%04lX DI=%04lX SP=%04lX BP=%04lX\n",REGS.esi,REGS.edi,REGS.esp,REGS.ebp);
67 fprintf(stderr,"CS=%04X DS=%04X ES=%04X SS=%04X\n",REGS.cs,REGS.ds,REGS.es,REGS.ss);
68 fprintf(stderr,"IP=%04lX EFLAGS=%08lX\n",REGS.eip,REGS.eflags);
70 iofs=((DWORD)REGS.cs<<4)+REGS.eip;
71 #undef REGS
72 inst=(BYTE*)lpDosTask->img+iofs;
73 printf("Opcodes:");
74 for (x=0; x<8; x++) printf(" %02x",inst[x]);
75 printf("\n");
78 static int DOSVM_Int( int vect, PCONTEXT context, LPDOSTASK lpDosTask )
80 extern UINT16 DPMI_wrap_seg;
82 if (vect==0x31) {
83 if (CS_reg(context)==DPMI_wrap_seg) {
84 /* exit from real-mode wrapper */
85 return -1;
87 /* we could probably move some other dodgy stuff here too from dpmi.c */
89 INT_RealModeInterrupt(vect,context);
90 return 0;
93 static void DOSVM_SimulateInt( int vect, PCONTEXT context, LPDOSTASK lpDosTask )
95 FARPROC16 handler=INT_GetRMHandler(vect);
96 WORD*stack=(WORD*)(V86BASE(context)+(((DWORD)SS_reg(context))<<4)+SP_reg(context));
98 *(--stack)=FL_reg(context);
99 *(--stack)=CS_reg(context);
100 *(--stack)=IP_reg(context);
101 SP_reg(context)-=6;
102 CS_reg(context)=SELECTOROF(handler);
103 IP_reg(context)=OFFSETOF(handler);
106 #define CV CP(eax,EAX); CP(ecx,ECX); CP(edx,EDX); CP(ebx,EBX); \
107 CP(esi,ESI); CP(edi,EDI); CP(esp,ESP); CP(ebp,EBP); \
108 CP(cs,CS); CP(ds,DS); CP(es,ES); \
109 CP(ss,SS); CP(fs,FS); CP(gs,GS); \
110 CP(eip,EIP); CP(eflags,EFL)
112 static int DOSVM_Process( LPDOSTASK lpDosTask, int fn, int sig,
113 struct vm86plus_struct*VM86 )
115 SIGCONTEXT sigcontext;
116 CONTEXT context;
117 int ret=0;
119 if (VM86_TYPE(fn)==VM86_UNKNOWN) {
120 /* INSTR_EmulateInstruction needs a SIGCONTEXT, not a CONTEXT... */
121 #define CP(x,y) y##_sig(&sigcontext) = VM86->regs.x
123 #undef CP
124 if (instr_emu_call) ret=instr_emu_call(&sigcontext);
125 #define CP(x,y) VM86->regs.x = y##_sig(&sigcontext)
127 #undef CP
128 if (ret) return 0;
129 ret=0;
131 #define CP(x,y) y##_reg(&context) = VM86->regs.x
133 #undef CP
134 (void*)V86BASE(&context)=lpDosTask->img;
136 switch (VM86_TYPE(fn)) {
137 case VM86_SIGNAL:
138 TRACE(int,"DOS module caught signal %d\n",sig);
139 if (sig==SIGALRM) {
140 DOSVM_SimulateInt(8,&context,lpDosTask);
141 } else
142 if (sig==SIGHUP) {
143 if (ctx_debug_call) ctx_debug_call(SIGTRAP,&context);
144 } else
145 if ((sig==SIGILL)||(sig==SIGSEGV)) {
146 if (ctx_debug_call) ctx_debug_call(SIGILL,&context);
147 } else {
148 DOSVM_Dump(lpDosTask,fn,sig,VM86);
149 ret=-1;
151 break;
152 case VM86_UNKNOWN: /* unhandled GPF */
153 DOSVM_Dump(lpDosTask,fn,sig,VM86);
154 if (ctx_debug_call) ctx_debug_call(SIGSEGV,&context); else ret=-1;
155 break;
156 case VM86_INTx:
157 if (TRACE_ON(relay))
158 DPRINTF("Call DOS int 0x%02x (EAX=%08lx) ret=%04lx:%04lx\n",VM86_ARG(fn),context.Eax,context.SegCs,context.Eip);
159 ret=DOSVM_Int(VM86_ARG(fn),&context,lpDosTask);
160 if (TRACE_ON(relay))
161 DPRINTF("Ret DOS int 0x%02x (EAX=%08lx) ret=%04lx:%04lx\n",VM86_ARG(fn),context.Eax,context.SegCs,context.Eip);
162 break;
163 case VM86_STI:
164 break;
165 case VM86_PICRETURN:
166 printf("Trapped due to pending PIC request\n"); break;
167 case VM86_TRAP:
168 if (ctx_debug_call) ctx_debug_call(SIGTRAP,&context);
169 break;
170 default:
171 DOSVM_Dump(lpDosTask,fn,sig,VM86);
172 ret=-1;
175 #define CP(x,y) VM86->regs.x = y##_reg(&context)
177 #undef CP
178 return ret;
181 int DOSVM_Enter( PCONTEXT context )
183 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
184 NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
185 LPDOSTASK lpDosTask;
186 struct vm86plus_struct VM86;
187 int stat,len,sig;
188 fd_set readfds,exceptfds;
190 GlobalUnlock16( GetCurrentTask() );
191 if (!pModule) {
192 ERR(module,"No task is currently active!\n");
193 return -1;
195 if (!(lpDosTask=pModule->lpDosTask)) {
196 /* MZ_CreateProcess or MZ_AllocDPMITask should have been called first */
197 ERR(module,"dosmod has not been initialized!");
198 return -1;
201 if (context) {
202 #define CP(x,y) VM86.regs.x = y##_reg(context)
204 #undef CP
205 } else {
206 /* initial setup */
207 /* allocate standard DOS handles */
208 FILE_InitProcessDosHandles();
209 /* registers */
210 memset(&VM86,0,sizeof(VM86));
211 VM86.regs.cs=lpDosTask->init_cs;
212 VM86.regs.eip=lpDosTask->init_ip;
213 VM86.regs.ss=lpDosTask->init_ss;
214 VM86.regs.esp=lpDosTask->init_sp;
215 VM86.regs.ds=lpDosTask->psp_seg;
216 VM86.regs.es=lpDosTask->psp_seg;
217 /* hmm, what else do we need? */
220 /* main exchange loop */
221 do {
222 stat = VM86_ENTER;
223 errno = 0;
224 /* transmit VM86 structure to dosmod task */
225 if (write(lpDosTask->write_pipe,&stat,sizeof(stat))!=sizeof(stat)) {
226 ERR(module,"dosmod sync lost, errno=%d\n",errno);
227 return -1;
229 if (write(lpDosTask->write_pipe,&VM86,sizeof(VM86))!=sizeof(VM86)) {
230 ERR(module,"dosmod sync lost, errno=%d\n",errno);
231 return -1;
233 /* wait for response, with async events enabled */
234 FD_ZERO(&readfds);
235 FD_ZERO(&exceptfds);
236 SIGNAL_MaskAsyncEvents(FALSE);
237 do {
238 FD_SET(lpDosTask->read_pipe,&readfds);
239 FD_SET(lpDosTask->read_pipe,&exceptfds);
240 select(lpDosTask->read_pipe+1,&readfds,NULL,&exceptfds,NULL);
241 } while (!(FD_ISSET(lpDosTask->read_pipe,&readfds)||
242 FD_ISSET(lpDosTask->read_pipe,&exceptfds)));
243 SIGNAL_MaskAsyncEvents(TRUE);
244 /* read response (with async events disabled to avoid some strange problems) */
245 do {
246 if ((len=read(lpDosTask->read_pipe,&stat,sizeof(stat)))!=sizeof(stat)) {
247 if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) {
248 WARN(module,"rereading dosmod return code due to errno=%d, result=%d\n",errno,len);
249 continue;
251 ERR(module,"dosmod sync lost reading return code, errno=%d, result=%d\n",errno,len);
252 return -1;
254 } while (0);
255 TRACE(module,"dosmod return code=%d\n",stat);
256 do {
257 if ((len=read(lpDosTask->read_pipe,&VM86,sizeof(VM86)))!=sizeof(VM86)) {
258 if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) {
259 WARN(module,"rereading dosmod VM86 structure due to errno=%d, result=%d\n",errno,len);
260 continue;
262 ERR(module,"dosmod sync lost reading VM86 structure, errno=%d, result=%d\n",errno,len);
263 return -1;
265 } while (0);
266 if ((stat&0xff)==DOSMOD_SIGNAL) {
267 do {
268 if ((len=read(lpDosTask->read_pipe,&sig,sizeof(sig)))!=sizeof(sig)) {
269 if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) {
270 WARN(module,"rereading dosmod signal due to errno=%d, result=%d\n",errno,len);
271 continue;
273 ERR(module,"dosmod sync lost reading signal, errno=%d, result=%d\n",errno,len);
274 return -1;
276 } while (0);
277 } else sig=0;
278 /* got response */
279 } while (DOSVM_Process(lpDosTask,stat,sig,&VM86)>=0);
281 if (context) {
282 #define CP(x,y) y##_reg(context) = VM86.regs.x
284 #undef CP
286 return 0;
289 void DOSVM_SetTimer( unsigned ticks )
291 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
292 NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
293 int stat=DOSMOD_SET_TIMER;
294 struct timeval tim;
296 GlobalUnlock16( GetCurrentTask() );
297 if (pModule&&pModule->lpDosTask) {
298 /* the PC clocks ticks at 1193180 Hz */
299 tim.tv_sec=0;
300 tim.tv_usec=((unsigned long long)ticks*1000000)/1193180;
301 /* sanity check */
302 if (!tim.tv_usec) tim.tv_usec=1;
304 if (write(pModule->lpDosTask->write_pipe,&stat,sizeof(stat))!=sizeof(stat)) {
305 ERR(module,"dosmod sync lost, errno=%d\n",errno);
306 return;
308 if (write(pModule->lpDosTask->write_pipe,&tim,sizeof(tim))!=sizeof(tim)) {
309 ERR(module,"dosmod sync lost, errno=%d\n",errno);
310 return;
312 /* there's no return */
316 unsigned DOSVM_GetTimer( void )
318 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
319 NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
320 int stat=DOSMOD_GET_TIMER;
321 struct timeval tim;
323 GlobalUnlock16( GetCurrentTask() );
324 if (pModule&&pModule->lpDosTask) {
325 if (write(pModule->lpDosTask->write_pipe,&stat,sizeof(stat))!=sizeof(stat)) {
326 ERR(module,"dosmod sync lost, errno=%d\n",errno);
327 return 0;
329 /* read response */
330 if (read(pModule->lpDosTask->read_pipe,&tim,sizeof(tim))!=sizeof(tim)) {
331 ERR(module,"dosmod sync lost, errno=%d\n",errno);
332 return 0;
334 return ((unsigned long long)tim.tv_usec*1193180)/1000000;
336 return 0;
339 void MZ_Tick( WORD handle )
341 /* find the DOS task that has the right system_timer handle... */
342 /* should usually be the current, so let's just be lazy... */
343 TDB *pTask = (TDB*)GlobalLock16( GetCurrentTask() );
344 NE_MODULE *pModule = pTask ? NE_GetPtr( pTask->hModule ) : NULL;
345 LPDOSTASK lpDosTask = pModule ? pModule->lpDosTask : NULL;
347 GlobalUnlock16( GetCurrentTask() );
348 if (lpDosTask&&(lpDosTask->system_timer==handle)) {
349 /* BIOS timer tick */
350 (*((DWORD*)(((BYTE*)(lpDosTask->img))+0x46c)))++;
354 #else /* !MZ_SUPPORTED */
356 int DOSVM_Enter( PCONTEXT context )
358 ERR(module,"DOS realmode not supported on this architecture!\n");
359 return -1;
362 void MZ_Tick( WORD handle ) {}
363 void DOSVM_SetTimer( unsigned ticks ) {}
364 unsigned DOSVM_GetTimer( void ) { return 0; }
366 #endif