Added some new dosmod communication possibilities, including
[wine/multimedia.git] / loader / dos / dosvm.c
blob989b6a950acaa476c6b6c94437cd8afc75b69d49
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 (!pModule->lpDosTask) {
196 /* no VM86 (dosmod) task is currently running, start one */
197 if ((lpDosTask = calloc(1, sizeof(DOSTASK))) == NULL)
198 return 0;
199 lpDosTask->hModule=pModule->self;
200 stat=MZ_InitMemory(lpDosTask,pModule);
201 if (stat>=32) stat=MZ_InitTask(lpDosTask);
202 if (stat<32) {
203 free(lpDosTask);
204 return -1;
206 pModule->lpDosTask = lpDosTask;
207 pModule->dos_image = lpDosTask->img;
208 /* Note: we're leaving it running after this, in case we need it again,
209 as this minimizes the overhead of starting it up every time...
210 it will be killed automatically when the current task terminates */
211 } else lpDosTask=pModule->lpDosTask;
213 if (context) {
214 #define CP(x,y) VM86.regs.x = y##_reg(context)
216 #undef CP
217 } else {
218 /* initial setup */
219 memset(&VM86,0,sizeof(VM86));
220 VM86.regs.cs=lpDosTask->init_cs;
221 VM86.regs.eip=lpDosTask->init_ip;
222 VM86.regs.ss=lpDosTask->init_ss;
223 VM86.regs.esp=lpDosTask->init_sp;
224 VM86.regs.ds=lpDosTask->psp_seg;
225 VM86.regs.es=lpDosTask->psp_seg;
226 /* hmm, what else do we need? */
229 /* main exchange loop */
230 do {
231 stat = VM86_ENTER;
232 errno = 0;
233 /* transmit VM86 structure to dosmod task */
234 if (write(lpDosTask->write_pipe,&stat,sizeof(stat))!=sizeof(stat)) {
235 ERR(module,"dosmod sync lost, errno=%d\n",errno);
236 return -1;
238 if (write(lpDosTask->write_pipe,&VM86,sizeof(VM86))!=sizeof(VM86)) {
239 ERR(module,"dosmod sync lost, errno=%d\n",errno);
240 return -1;
242 /* wait for response, with async events enabled */
243 FD_ZERO(&readfds);
244 FD_ZERO(&exceptfds);
245 SIGNAL_MaskAsyncEvents(FALSE);
246 do {
247 FD_SET(lpDosTask->read_pipe,&readfds);
248 FD_SET(lpDosTask->read_pipe,&exceptfds);
249 select(lpDosTask->read_pipe+1,&readfds,NULL,&exceptfds,NULL);
250 } while (!(FD_ISSET(lpDosTask->read_pipe,&readfds)||
251 FD_ISSET(lpDosTask->read_pipe,&exceptfds)));
252 SIGNAL_MaskAsyncEvents(TRUE);
253 /* read response (with async events disabled to avoid some strange problems) */
254 do {
255 if ((len=read(lpDosTask->read_pipe,&stat,sizeof(stat)))!=sizeof(stat)) {
256 if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) {
257 WARN(module,"rereading dosmod return code due to errno=%d, result=%d\n",errno,len);
258 continue;
260 ERR(module,"dosmod sync lost reading return code, errno=%d, result=%d\n",errno,len);
261 return -1;
263 } while (0);
264 TRACE(module,"dosmod return code=%d\n",stat);
265 do {
266 if ((len=read(lpDosTask->read_pipe,&VM86,sizeof(VM86)))!=sizeof(VM86)) {
267 if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) {
268 WARN(module,"rereading dosmod VM86 structure due to errno=%d, result=%d\n",errno,len);
269 continue;
271 ERR(module,"dosmod sync lost reading VM86 structure, errno=%d, result=%d\n",errno,len);
272 return -1;
274 } while (0);
275 if ((stat&0xff)==DOSMOD_SIGNAL) {
276 do {
277 if ((len=read(lpDosTask->read_pipe,&sig,sizeof(sig)))!=sizeof(sig)) {
278 if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) {
279 WARN(module,"rereading dosmod signal due to errno=%d, result=%d\n",errno,len);
280 continue;
282 ERR(module,"dosmod sync lost reading signal, errno=%d, result=%d\n",errno,len);
283 return -1;
285 } while (0);
286 } else sig=0;
287 /* got response */
288 } while (DOSVM_Process(lpDosTask,stat,sig,&VM86)>=0);
290 if (context) {
291 #define CP(x,y) y##_reg(context) = VM86.regs.x
293 #undef CP
295 return 0;
298 void MZ_Tick( WORD handle )
300 /* find the DOS task that has the right system_timer handle... */
301 /* should usually be the current, so let's just be lazy... */
302 TDB *pTask = (TDB*)GlobalLock16( GetCurrentTask() );
303 NE_MODULE *pModule = pTask ? NE_GetPtr( pTask->hModule ) : NULL;
304 LPDOSTASK lpDosTask = pModule ? pModule->lpDosTask : NULL;
306 GlobalUnlock16( GetCurrentTask() );
307 if (lpDosTask&&(lpDosTask->system_timer==handle)) {
308 /* BIOS timer tick */
309 (*((DWORD*)(((BYTE*)(lpDosTask->img))+0x46c)))++;
313 #else /* !MZ_SUPPORTED */
315 int DOSVM_Enter( PCONTEXT context )
317 ERR(module,"DOS realmode not supported on this architecture!\n");
318 return -1;
321 void MZ_Tick( WORD handle ) {}
323 #endif