Close the right window when clicking on a non active window close
[wine.git] / loader / dos / dosvm.c
blob78bcbcbceca843a120e71864f27451016d39456e
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>
20 #include "wine/winbase16.h"
21 #include "winuser.h"
22 #include "winnt.h"
23 #include "sig_context.h"
24 #include "msdos.h"
25 #include "file.h"
26 #include "miscemu.h"
27 #include "dosexe.h"
28 #include "dosmod.h"
29 #include "stackframe.h"
30 #include "debugtools.h"
32 DECLARE_DEBUG_CHANNEL(int)
33 DECLARE_DEBUG_CHANNEL(module)
34 DECLARE_DEBUG_CHANNEL(relay)
36 #ifdef MZ_SUPPORTED
38 #include <sys/vm86.h>
39 #include <sys/mman.h>
41 #define IF_CLR(ctx) EFL_reg(ctx) &= ~VIF_MASK
42 #define IF_ENABLED(ctx) (EFL_reg(ctx) & VIF_MASK)
43 #define SET_PEND(ctx) EFL_reg(ctx) |= VIP_MASK
44 #define CLR_PEND(ctx) EFL_reg(ctx) &= ~VIP_MASK
45 #define IS_PEND(ctx) (EFL_reg(ctx) & VIP_MASK)
47 #undef TRY_PICRETURN
49 extern void WINAPI REGS_FUNC(RtlRaiseException)( EXCEPTION_RECORD *rec,
50 CONTEXT *context );
52 static void do_exception( int signal, CONTEXT86 *context )
54 EXCEPTION_RECORD rec;
55 if ((signal == SIGTRAP) || (signal == SIGHUP))
57 rec.ExceptionCode = EXCEPTION_BREAKPOINT;
58 rec.ExceptionFlags = EXCEPTION_CONTINUABLE;
60 else
62 rec.ExceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION; /* generic error */
63 rec.ExceptionFlags = EH_NONCONTINUABLE;
65 rec.ExceptionRecord = NULL;
66 rec.ExceptionAddress = (LPVOID)EIP_reg(context);
67 rec.NumberParameters = 0;
68 REGS_FUNC(RtlRaiseException)( &rec, context );
71 static void DOSVM_Dump( LPDOSTASK lpDosTask, int fn, int sig,
72 struct vm86plus_struct*VM86 )
74 unsigned iofs;
75 BYTE*inst;
76 int x;
78 switch (VM86_TYPE(fn)) {
79 case VM86_SIGNAL:
80 printf("Trapped signal %d\n",sig); break;
81 case VM86_UNKNOWN:
82 printf("Trapped unhandled GPF\n"); break;
83 case VM86_INTx:
84 printf("Trapped INT %02x\n",VM86_ARG(fn)); break;
85 case VM86_STI:
86 printf("Trapped STI\n"); break;
87 case VM86_PICRETURN:
88 printf("Trapped due to pending PIC request\n"); break;
89 case VM86_TRAP:
90 printf("Trapped debug request\n"); break;
91 default:
92 printf("Trapped unknown VM86 type %d arg %d\n",VM86_TYPE(fn),VM86_ARG(fn)); break;
94 #define REGS VM86->regs
95 fprintf(stderr,"AX=%04lX CX=%04lX DX=%04lX BX=%04lX\n",REGS.eax,REGS.ecx,REGS.edx,REGS.ebx);
96 fprintf(stderr,"SI=%04lX DI=%04lX SP=%04lX BP=%04lX\n",REGS.esi,REGS.edi,REGS.esp,REGS.ebp);
97 fprintf(stderr,"CS=%04X DS=%04X ES=%04X SS=%04X\n",REGS.cs,REGS.ds,REGS.es,REGS.ss);
98 fprintf(stderr,"IP=%04lX EFLAGS=%08lX\n",REGS.eip,REGS.eflags);
100 iofs=((DWORD)REGS.cs<<4)+REGS.eip;
101 #undef REGS
102 inst=(BYTE*)lpDosTask->img+iofs;
103 printf("Opcodes:");
104 for (x=0; x<8; x++) printf(" %02x",inst[x]);
105 printf("\n");
108 static int DOSVM_Int( int vect, CONTEXT86 *context, LPDOSTASK lpDosTask )
110 extern UINT16 DPMI_wrap_seg;
112 if (vect==0x31) {
113 if (CS_reg(context)==DPMI_wrap_seg) {
114 /* exit from real-mode wrapper */
115 return -1;
117 /* we could probably move some other dodgy stuff here too from dpmi.c */
119 INT_RealModeInterrupt(vect,context);
120 return 0;
123 static void DOSVM_SimulateInt( int vect, CONTEXT86 *context, LPDOSTASK lpDosTask )
125 FARPROC16 handler=INT_GetRMHandler(vect);
127 if (SELECTOROF(handler)==0xf000) {
128 /* if internal interrupt, call it directly */
129 INT_RealModeInterrupt(vect,context);
130 } else {
131 WORD*stack=(WORD*)(V86BASE(context)+(((DWORD)SS_reg(context))<<4)+SP_reg(context));
132 WORD flag=FL_reg(context);
134 if (IF_ENABLED(context)) flag|=IF_MASK;
135 else flag&=~IF_MASK;
137 *(--stack)=flag;
138 *(--stack)=CS_reg(context);
139 *(--stack)=IP_reg(context);
140 SP_reg(context)-=6;
141 CS_reg(context)=SELECTOROF(handler);
142 IP_reg(context)=OFFSETOF(handler);
143 IF_CLR(context);
147 #define SHOULD_PEND(x) \
148 (x && ((!lpDosTask->current) || (x->priority < lpDosTask->current->priority)))
150 static void DOSVM_SendQueuedEvent(CONTEXT86 *context, LPDOSTASK lpDosTask)
152 LPDOSEVENT event = lpDosTask->pending;
154 if (SHOULD_PEND(event)) {
155 /* remove from "pending" list */
156 lpDosTask->pending = event->next;
157 /* process event */
158 if (event->irq>=0) {
159 /* it's an IRQ, move it to "current" list */
160 event->next = lpDosTask->current;
161 lpDosTask->current = event;
162 TRACE_(int)("dispatching IRQ %d\n",event->irq);
163 /* note that if DOSVM_SimulateInt calls an internal interrupt directly,
164 * lpDosTask->current might be cleared (and event freed) in this very call! */
165 DOSVM_SimulateInt((event->irq<8)?(event->irq+8):(event->irq-8+0x70),context,lpDosTask);
166 } else {
167 /* callback event */
168 TRACE_(int)("dispatching callback event\n");
169 (*event->relay)(lpDosTask,context,event->data);
170 free(event);
173 if (!SHOULD_PEND(lpDosTask->pending)) {
174 TRACE_(int)("clearing Pending flag\n");
175 CLR_PEND(context);
179 static void DOSVM_SendQueuedEvents(CONTEXT86 *context, LPDOSTASK lpDosTask)
181 /* we will send all queued events as long as interrupts are enabled,
182 * but IRQ events will disable interrupts again */
183 while (IS_PEND(context) && IF_ENABLED(context))
184 DOSVM_SendQueuedEvent(context,lpDosTask);
187 void DOSVM_QueueEvent( int irq, int priority, void (*relay)(LPDOSTASK,CONTEXT86*,void*), void *data)
189 LPDOSTASK lpDosTask = MZ_Current();
190 LPDOSEVENT event, cur, prev;
192 if (lpDosTask) {
193 event = malloc(sizeof(DOSEVENT));
194 if (!event) {
195 ERR_(int)("out of memory allocating event entry\n");
196 return;
198 event->irq = irq; event->priority = priority;
199 event->relay = relay; event->data = data;
201 /* insert event into linked list, in order *after*
202 * all earlier events of higher or equal priority */
203 cur = lpDosTask->pending; prev = NULL;
204 while (cur && cur->priority<=priority) {
205 prev = cur;
206 cur = cur->next;
208 event->next = cur;
209 if (prev) prev->next = event;
210 else lpDosTask->pending = event;
212 /* get dosmod's attention to the new event, except for irq==0 where we already have it */
213 if (irq && !lpDosTask->sig_sent) {
214 TRACE_(int)("new event queued, signalling dosmod\n");
215 kill(lpDosTask->task,SIGUSR2);
216 lpDosTask->sig_sent++;
217 } else {
218 TRACE_(int)("new event queued\n");
223 #define CV CP(eax,EAX); CP(ecx,ECX); CP(edx,EDX); CP(ebx,EBX); \
224 CP(esi,ESI); CP(edi,EDI); CP(esp,ESP); CP(ebp,EBP); \
225 CP(cs,CS); CP(ds,DS); CP(es,ES); \
226 CP(ss,SS); CP(fs,FS); CP(gs,GS); \
227 CP(eip,EIP); CP(eflags,EFL)
229 static int DOSVM_Process( LPDOSTASK lpDosTask, int fn, int sig,
230 struct vm86plus_struct*VM86 )
232 SIGCONTEXT sigcontext;
233 CONTEXT86 context;
234 int ret=0;
236 if (VM86_TYPE(fn)==VM86_UNKNOWN) {
237 /* INSTR_EmulateInstruction needs a SIGCONTEXT, not a CONTEXT... */
238 #define CP(x,y) y##_sig(&sigcontext) = VM86->regs.x
240 #undef CP
241 ret=INSTR_EmulateInstruction(&sigcontext);
242 #define CP(x,y) VM86->regs.x = y##_sig(&sigcontext)
244 #undef CP
245 if (ret) return 0;
246 ret=0;
248 #define CP(x,y) y##_reg(&context) = VM86->regs.x
250 #undef CP
251 (void*)V86BASE(&context)=lpDosTask->img;
252 #ifdef TRY_PICRETURN
253 if (VM86->vm86plus.force_return_for_pic) {
254 SET_PEND(&context);
256 #else
257 /* linux doesn't preserve pending flag on return */
258 if (SHOULD_PEND(lpDosTask->pending)) {
259 SET_PEND(&context);
261 #endif
263 switch (VM86_TYPE(fn)) {
264 case VM86_SIGNAL:
265 TRACE_(int)("DOS module caught signal %d\n",sig);
266 if ((sig==SIGALRM) || (sig==SIGUSR2)) {
267 if (sig==SIGALRM) {
268 DOSVM_QueueEvent(0,DOS_PRIORITY_REALTIME,NULL,NULL);
270 if (lpDosTask->pending) {
271 TRACE_(int)("setting Pending flag, interrupts are currently %s\n",
272 IF_ENABLED(&context) ? "enabled" : "disabled");
273 SET_PEND(&context);
274 DOSVM_SendQueuedEvents(&context,lpDosTask);
275 } else {
276 TRACE_(int)("no events are pending, clearing Pending flag\n");
277 CLR_PEND(&context);
279 if (sig==SIGUSR2) lpDosTask->sig_sent--;
281 else if ((sig==SIGHUP) || (sig==SIGILL) || (sig==SIGSEGV)) {
282 do_exception( sig, &context );
283 } else {
284 DOSVM_Dump(lpDosTask,fn,sig,VM86);
285 ret=-1;
287 break;
288 case VM86_UNKNOWN: /* unhandled GPF */
289 DOSVM_Dump(lpDosTask,fn,sig,VM86);
290 do_exception( SIGSEGV, &context );
291 break;
292 case VM86_INTx:
293 if (TRACE_ON(relay))
294 DPRINTF("Call DOS int 0x%02x (EAX=%08lx) ret=%04lx:%04lx\n",VM86_ARG(fn),context.Eax,context.SegCs,context.Eip);
295 ret=DOSVM_Int(VM86_ARG(fn),&context,lpDosTask);
296 if (TRACE_ON(relay))
297 DPRINTF("Ret DOS int 0x%02x (EAX=%08lx) ret=%04lx:%04lx\n",VM86_ARG(fn),context.Eax,context.SegCs,context.Eip);
298 break;
299 case VM86_STI:
300 case VM86_PICRETURN:
301 TRACE_(int)("DOS task enabled interrupts with events pending, sending events\n");
302 DOSVM_SendQueuedEvents(&context,lpDosTask);
303 break;
304 case VM86_TRAP:
305 do_exception( SIGTRAP, &context );
306 break;
307 default:
308 DOSVM_Dump(lpDosTask,fn,sig,VM86);
309 ret=-1;
312 #define CP(x,y) VM86->regs.x = y##_reg(&context)
314 #undef CP
315 #ifdef TRY_PICRETURN
316 VM86->vm86plus.force_return_for_pic = IS_PEND(&context) ? 1 : 0;
317 CLR_PEND(&context);
318 #endif
319 return ret;
322 void DOSVM_ProcessMessage(LPDOSTASK lpDosTask,MSG *msg)
324 BYTE scan = 0;
326 fprintf(stderr,"got message %04x, wparam=%08x, lparam=%08lx\n",msg->message,msg->wParam,msg->lParam);
327 if ((msg->message>=WM_MOUSEFIRST)&&
328 (msg->message<=WM_MOUSELAST)) {
329 INT_Int33Message(msg->message,msg->wParam,msg->lParam);
330 } else {
331 switch (msg->message) {
332 case WM_KEYUP:
333 scan = 0x80;
334 case WM_KEYDOWN:
335 scan |= (msg->lParam >> 16) & 0x7f;
337 /* check whether extended bit is set,
338 * and if so, queue the extension prefix */
339 if (msg->lParam & 0x1000000) {
340 /* FIXME: some keys (function keys) have
341 * extended bit set even when they shouldn't,
342 * should check for them */
343 INT_Int09SendScan(0xE0);
345 INT_Int09SendScan(scan);
346 break;
351 void DOSVM_Wait( int read_pipe, HANDLE hObject )
353 LPDOSTASK lpDosTask = MZ_Current();
354 MSG msg;
355 DWORD waitret;
356 BOOL got_msg = FALSE;
358 do {
359 /* check for messages (waste time before the response check below) */
360 while (PeekMessageA(&msg,0,0,0,PM_REMOVE|PM_NOYIELD)) {
361 /* got a message */
362 DOSVM_ProcessMessage(lpDosTask,&msg);
363 /* we don't need a TranslateMessage here */
364 DispatchMessageA(&msg);
365 got_msg = TRUE;
367 if (read_pipe == -1) {
368 if (got_msg) break;
369 } else {
370 fd_set readfds;
371 struct timeval timeout={0,0};
372 /* quick check for response from dosmod
373 * (faster than doing the full blocking wait, if data already available) */
374 FD_ZERO(&readfds); FD_SET(read_pipe,&readfds);
375 if (select(read_pipe+1,&readfds,NULL,NULL,&timeout)>0)
376 break;
378 /* check for data from win32 console device */
380 /* nothing yet, block while waiting for something to do */
381 waitret=MsgWaitForMultipleObjects(1,&hObject,FALSE,INFINITE,QS_ALLINPUT);
382 if (waitret==(DWORD)-1) {
383 ERR_(module)("dosvm wait error=%ld\n",GetLastError());
385 if (read_pipe != -1) {
386 if (waitret==WAIT_OBJECT_0) break;
388 } while (TRUE);
391 int DOSVM_Enter( CONTEXT86 *context )
393 LPDOSTASK lpDosTask = MZ_Current();
394 struct vm86plus_struct VM86;
395 int stat,len,sig;
397 if (!lpDosTask) {
398 /* MZ_CreateProcess or MZ_AllocDPMITask should have been called first */
399 ERR_(module)("dosmod has not been initialized!");
400 return -1;
403 if (context) {
404 #define CP(x,y) VM86.regs.x = y##_reg(context)
406 #undef CP
407 if (VM86.regs.eflags & IF_MASK)
408 VM86.regs.eflags |= VIF_MASK;
409 } else {
410 /* initial setup */
411 /* allocate standard DOS handles */
412 FILE_InitProcessDosHandles();
413 /* registers */
414 memset(&VM86,0,sizeof(VM86));
415 VM86.regs.cs=lpDosTask->init_cs;
416 VM86.regs.eip=lpDosTask->init_ip;
417 VM86.regs.ss=lpDosTask->init_ss;
418 VM86.regs.esp=lpDosTask->init_sp;
419 VM86.regs.ds=lpDosTask->psp_seg;
420 VM86.regs.es=lpDosTask->psp_seg;
421 VM86.regs.eflags=VIF_MASK;
422 /* hmm, what else do we need? */
425 /* main exchange loop */
426 do {
427 stat = VM86_ENTER;
428 errno = 0;
429 /* transmit VM86 structure to dosmod task */
430 if (write(lpDosTask->write_pipe,&stat,sizeof(stat))!=sizeof(stat)) {
431 ERR_(module)("dosmod sync lost, errno=%d, fd=%d, pid=%d\n",errno,lpDosTask->write_pipe,getpid());
432 return -1;
434 if (write(lpDosTask->write_pipe,&VM86,sizeof(VM86))!=sizeof(VM86)) {
435 ERR_(module)("dosmod sync lost, errno=%d\n",errno);
436 return -1;
438 /* wait for response, doing other things in the meantime */
439 DOSVM_Wait(lpDosTask->read_pipe, lpDosTask->hReadPipe);
440 /* read response */
441 while (1) {
442 if ((len=read(lpDosTask->read_pipe,&stat,sizeof(stat)))==sizeof(stat)) break;
443 if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) {
444 WARN_(module)("rereading dosmod return code due to errno=%d, result=%d\n",errno,len);
445 continue;
447 ERR_(module)("dosmod sync lost reading return code, errno=%d, result=%d\n",errno,len);
448 return -1;
450 TRACE_(module)("dosmod return code=%d\n",stat);
451 while (1) {
452 if ((len=read(lpDosTask->read_pipe,&VM86,sizeof(VM86)))==sizeof(VM86)) break;
453 if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) {
454 WARN_(module)("rereading dosmod VM86 structure due to errno=%d, result=%d\n",errno,len);
455 continue;
457 ERR_(module)("dosmod sync lost reading VM86 structure, errno=%d, result=%d\n",errno,len);
458 return -1;
460 if ((stat&0xff)==DOSMOD_SIGNAL) {
461 while (1) {
462 if ((len=read(lpDosTask->read_pipe,&sig,sizeof(sig)))==sizeof(sig)) break;
463 if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) {
464 WARN_(module)("rereading dosmod signal due to errno=%d, result=%d\n",errno,len);
465 continue;
467 ERR_(module)("dosmod sync lost reading signal, errno=%d, result=%d\n",errno,len);
468 return -1;
469 } while (0);
470 } else sig=0;
471 /* got response */
472 } while (DOSVM_Process(lpDosTask,stat,sig,&VM86)>=0);
474 if (context) {
475 #define CP(x,y) y##_reg(context) = VM86.regs.x
477 #undef CP
479 return 0;
482 void DOSVM_PIC_ioport_out( WORD port, BYTE val)
484 LPDOSTASK lpDosTask = MZ_Current();
485 LPDOSEVENT event;
487 if (lpDosTask) {
488 if ((port==0x20) && (val==0x20)) {
489 if (lpDosTask->current) {
490 /* EOI (End Of Interrupt) */
491 TRACE_(int)("received EOI for current IRQ, clearing\n");
492 event = lpDosTask->current;
493 lpDosTask->current = event->next;
494 if (event->relay)
495 (*event->relay)(lpDosTask,NULL,event->data);
496 free(event);
498 if (lpDosTask->pending &&
499 !lpDosTask->sig_sent) {
500 /* another event is pending, which we should probably
501 * be able to process now, so tell dosmod about it */
502 TRACE_(int)("another event pending, signalling dosmod\n");
503 kill(lpDosTask->task,SIGUSR2);
504 lpDosTask->sig_sent++;
506 } else {
507 WARN_(int)("EOI without active IRQ\n");
509 } else {
510 FIXME_(int)("unrecognized PIC command %02x\n",val);
515 void DOSVM_SetTimer( unsigned ticks )
517 LPDOSTASK lpDosTask = MZ_Current();
518 int stat=DOSMOD_SET_TIMER;
519 struct timeval tim;
521 if (lpDosTask) {
522 /* the PC clocks ticks at 1193180 Hz */
523 tim.tv_sec=0;
524 tim.tv_usec=((unsigned long long)ticks*1000000)/1193180;
525 /* sanity check */
526 if (!tim.tv_usec) tim.tv_usec=1;
528 if (write(lpDosTask->write_pipe,&stat,sizeof(stat))!=sizeof(stat)) {
529 ERR_(module)("dosmod sync lost, errno=%d\n",errno);
530 return;
532 if (write(lpDosTask->write_pipe,&tim,sizeof(tim))!=sizeof(tim)) {
533 ERR_(module)("dosmod sync lost, errno=%d\n",errno);
534 return;
536 /* there's no return */
540 unsigned DOSVM_GetTimer( void )
542 LPDOSTASK lpDosTask = MZ_Current();
543 int stat=DOSMOD_GET_TIMER;
544 struct timeval tim;
546 if (lpDosTask) {
547 if (write(lpDosTask->write_pipe,&stat,sizeof(stat))!=sizeof(stat)) {
548 ERR_(module)("dosmod sync lost, errno=%d\n",errno);
549 return 0;
551 /* read response */
552 while (1) {
553 if (read(lpDosTask->read_pipe,&tim,sizeof(tim))==sizeof(tim)) break;
554 if ((errno==EINTR)||(errno==EAGAIN)) continue;
555 ERR_(module)("dosmod sync lost, errno=%d\n",errno);
556 return 0;
558 return ((unsigned long long)tim.tv_usec*1193180)/1000000;
560 return 0;
563 void DOSVM_SetSystemData( int id, void *data )
565 LPDOSTASK lpDosTask = MZ_Current();
566 DOSSYSTEM *sys, *prev;
568 if (lpDosTask) {
569 sys = lpDosTask->sys;
570 prev = NULL;
571 while (sys && (sys->id != id)) {
572 prev = sys;
573 sys = sys->next;
575 if (sys) {
576 free(sys->data);
577 sys->data = data;
578 } else {
579 sys = malloc(sizeof(DOSSYSTEM));
580 sys->id = id;
581 sys->data = data;
582 sys->next = NULL;
583 if (prev) prev->next = sys;
584 else lpDosTask->sys = sys;
586 } else free(data);
589 void* DOSVM_GetSystemData( int id )
591 LPDOSTASK lpDosTask = MZ_Current();
592 DOSSYSTEM *sys;
594 if (lpDosTask) {
595 sys = lpDosTask->sys;
596 while (sys && (sys->id != id))
597 sys = sys->next;
598 if (sys)
599 return sys->data;
601 return NULL;
604 #else /* !MZ_SUPPORTED */
606 int DOSVM_Enter( CONTEXT86 *context )
608 ERR_(module)("DOS realmode not supported on this architecture!\n");
609 return -1;
612 void DOSVM_Wait( int read_pipe, HANDLE hObject) {}
613 void DOSVM_PIC_ioport_out( WORD port, BYTE val) {}
614 void DOSVM_SetTimer( unsigned ticks ) {}
615 unsigned DOSVM_GetTimer( void ) { return 0; }
616 void DOSVM_SetSystemData( int id, void *data ) { free(data); }
617 void* DOSVM_GetSystemData( int id ) { return NULL; }
618 void DOSVM_QueueEvent( int irq, int priority, void (*relay)(LPDOSTASK,CONTEXT86*,void*), void *data) {}
620 #endif