4 * Copyright 1998 Ove Kåven
6 * This code hasn't been completely cleaned up yet.
19 #include <sys/types.h>
22 #include "wine/winbase16.h"
23 #include "wine/exception.h"
35 #include "stackframe.h"
36 #include "debugtools.h"
38 DECLARE_DEBUG_CHANNEL(int)
39 DECLARE_DEBUG_CHANNEL(module)
40 DECLARE_DEBUG_CHANNEL(relay)
44 #ifdef HAVE_SYS_VM86_H
45 # include <sys/vm86.h>
47 #ifdef HAVE_SYS_MMAN_H
48 # include <sys/mman.h>
51 #define IF_CLR(ctx) EFL_reg(ctx) &= ~VIF_MASK
52 #define IF_ENABLED(ctx) (EFL_reg(ctx) & VIF_MASK)
53 #define SET_PEND(ctx) EFL_reg(ctx) |= VIP_MASK
54 #define CLR_PEND(ctx) EFL_reg(ctx) &= ~VIP_MASK
55 #define IS_PEND(ctx) (EFL_reg(ctx) & VIP_MASK)
59 static void do_exception( int signal, CONTEXT86 *context )
62 if ((signal == SIGTRAP) || (signal == SIGHUP))
64 rec.ExceptionCode = EXCEPTION_BREAKPOINT;
65 rec.ExceptionFlags = EXCEPTION_CONTINUABLE;
69 rec.ExceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION; /* generic error */
70 rec.ExceptionFlags = EH_NONCONTINUABLE;
72 rec.ExceptionRecord = NULL;
73 rec.ExceptionAddress = (LPVOID)EIP_reg(context);
74 rec.NumberParameters = 0;
75 EXC_RtlRaiseException( &rec, context );
78 static void DOSVM_Dump( LPDOSTASK lpDosTask, int fn, int sig,
79 struct vm86plus_struct*VM86 )
85 switch (VM86_TYPE(fn)) {
87 printf("Trapped signal %d\n",sig); break;
89 printf("Trapped unhandled GPF\n"); break;
91 printf("Trapped INT %02x\n",VM86_ARG(fn)); break;
93 printf("Trapped STI\n"); break;
95 printf("Trapped due to pending PIC request\n"); break;
97 printf("Trapped debug request\n"); break;
99 printf("Trapped unknown VM86 type %d arg %d\n",VM86_TYPE(fn),VM86_ARG(fn)); break;
101 #define REGS VM86->regs
102 fprintf(stderr,"AX=%04lX CX=%04lX DX=%04lX BX=%04lX\n",REGS.eax,REGS.ecx,REGS.edx,REGS.ebx);
103 fprintf(stderr,"SI=%04lX DI=%04lX SP=%04lX BP=%04lX\n",REGS.esi,REGS.edi,REGS.esp,REGS.ebp);
104 fprintf(stderr,"CS=%04X DS=%04X ES=%04X SS=%04X\n",REGS.cs,REGS.ds,REGS.es,REGS.ss);
105 fprintf(stderr,"IP=%04lX EFLAGS=%08lX\n",REGS.eip,REGS.eflags);
107 iofs=((DWORD)REGS.cs<<4)+REGS.eip;
109 inst=(BYTE*)lpDosTask->img+iofs;
111 for (x=0; x<8; x++) printf(" %02x",inst[x]);
115 static int DOSVM_Int( int vect, CONTEXT86 *context, LPDOSTASK lpDosTask )
117 extern UINT16 DPMI_wrap_seg;
120 if (CS_reg(context)==DPMI_wrap_seg) {
121 /* exit from real-mode wrapper */
124 /* we could probably move some other dodgy stuff here too from dpmi.c */
126 INT_RealModeInterrupt(vect,context);
130 static void DOSVM_SimulateInt( int vect, CONTEXT86 *context, LPDOSTASK lpDosTask )
132 FARPROC16 handler=INT_GetRMHandler(vect);
134 if (SELECTOROF(handler)==0xf000) {
135 /* if internal interrupt, call it directly */
136 INT_RealModeInterrupt(vect,context);
138 WORD*stack=(WORD*)(V86BASE(context)+(((DWORD)SS_reg(context))<<4)+LOWORD(ESP_reg(context)));
139 WORD flag=LOWORD(EFL_reg(context));
141 if (IF_ENABLED(context)) flag|=IF_MASK;
145 *(--stack)=CS_reg(context);
146 *(--stack)=LOWORD(EIP_reg(context));
148 CS_reg(context)=SELECTOROF(handler);
149 EIP_reg(context)=OFFSETOF(handler);
154 #define SHOULD_PEND(x) \
155 (x && ((!lpDosTask->current) || (x->priority < lpDosTask->current->priority)))
157 static void DOSVM_SendQueuedEvent(CONTEXT86 *context, LPDOSTASK lpDosTask)
159 LPDOSEVENT event = lpDosTask->pending;
161 if (SHOULD_PEND(event)) {
162 /* remove from "pending" list */
163 lpDosTask->pending = event->next;
166 /* it's an IRQ, move it to "current" list */
167 event->next = lpDosTask->current;
168 lpDosTask->current = event;
169 TRACE_(int)("dispatching IRQ %d\n",event->irq);
170 /* note that if DOSVM_SimulateInt calls an internal interrupt directly,
171 * lpDosTask->current might be cleared (and event freed) in this very call! */
172 DOSVM_SimulateInt((event->irq<8)?(event->irq+8):(event->irq-8+0x70),context,lpDosTask);
175 TRACE_(int)("dispatching callback event\n");
176 (*event->relay)(lpDosTask,context,event->data);
180 if (!SHOULD_PEND(lpDosTask->pending)) {
181 TRACE_(int)("clearing Pending flag\n");
186 static void DOSVM_SendQueuedEvents(CONTEXT86 *context, LPDOSTASK lpDosTask)
188 /* we will send all queued events as long as interrupts are enabled,
189 * but IRQ events will disable interrupts again */
190 while (IS_PEND(context) && IF_ENABLED(context))
191 DOSVM_SendQueuedEvent(context,lpDosTask);
194 void DOSVM_QueueEvent( int irq, int priority, void (*relay)(LPDOSTASK,CONTEXT86*,void*), void *data)
196 LPDOSTASK lpDosTask = MZ_Current();
197 LPDOSEVENT event, cur, prev;
200 event = malloc(sizeof(DOSEVENT));
202 ERR_(int)("out of memory allocating event entry\n");
205 event->irq = irq; event->priority = priority;
206 event->relay = relay; event->data = data;
208 /* insert event into linked list, in order *after*
209 * all earlier events of higher or equal priority */
210 cur = lpDosTask->pending; prev = NULL;
211 while (cur && cur->priority<=priority) {
216 if (prev) prev->next = event;
217 else lpDosTask->pending = event;
219 /* get dosmod's attention to the new event, except for irq==0 where we already have it */
220 if (irq && !lpDosTask->sig_sent) {
221 TRACE_(int)("new event queued, signalling dosmod\n");
222 kill(lpDosTask->task,SIGUSR2);
223 lpDosTask->sig_sent++;
225 TRACE_(int)("new event queued\n");
230 #define CV CP(eax,EAX); CP(ecx,ECX); CP(edx,EDX); CP(ebx,EBX); \
231 CP(esi,ESI); CP(edi,EDI); CP(esp,ESP); CP(ebp,EBP); \
232 CP(cs,CS); CP(ds,DS); CP(es,ES); \
233 CP(ss,SS); CP(fs,FS); CP(gs,GS); \
234 CP(eip,EIP); CP(eflags,EFL)
236 static int DOSVM_Process( LPDOSTASK lpDosTask, int fn, int sig,
237 struct vm86plus_struct*VM86 )
242 #define CP(x,y) y##_reg(&context) = VM86->regs.x
245 if (VM86_TYPE(fn)==VM86_UNKNOWN) {
246 ret=INSTR_EmulateInstruction(&context);
247 #define CP(x,y) VM86->regs.x = y##_reg(&context)
254 if (VM86->vm86plus.force_return_for_pic) {
258 /* linux doesn't preserve pending flag on return */
259 if (SHOULD_PEND(lpDosTask->pending)) {
264 switch (VM86_TYPE(fn)) {
266 TRACE_(int)("DOS module caught signal %d\n",sig);
267 if ((sig==SIGALRM) || (sig==SIGUSR2)) {
269 DOSVM_QueueEvent(0,DOS_PRIORITY_REALTIME,NULL,NULL);
271 if (lpDosTask->pending) {
272 TRACE_(int)("setting Pending flag, interrupts are currently %s\n",
273 IF_ENABLED(&context) ? "enabled" : "disabled");
275 DOSVM_SendQueuedEvents(&context,lpDosTask);
277 TRACE_(int)("no events are pending, clearing Pending flag\n");
280 if (sig==SIGUSR2) lpDosTask->sig_sent--;
282 else if ((sig==SIGHUP) || (sig==SIGILL) || (sig==SIGSEGV)) {
283 do_exception( sig, &context );
285 DOSVM_Dump(lpDosTask,fn,sig,VM86);
289 case VM86_UNKNOWN: /* unhandled GPF */
290 DOSVM_Dump(lpDosTask,fn,sig,VM86);
291 do_exception( SIGSEGV, &context );
295 DPRINTF("Call DOS int 0x%02x (EAX=%08lx) ret=%04lx:%04lx\n",VM86_ARG(fn),context.Eax,context.SegCs,context.Eip);
296 ret=DOSVM_Int(VM86_ARG(fn),&context,lpDosTask);
298 DPRINTF("Ret DOS int 0x%02x (EAX=%08lx) ret=%04lx:%04lx\n",VM86_ARG(fn),context.Eax,context.SegCs,context.Eip);
302 TRACE_(int)("DOS task enabled interrupts with events pending, sending events\n");
303 DOSVM_SendQueuedEvents(&context,lpDosTask);
306 do_exception( SIGTRAP, &context );
309 DOSVM_Dump(lpDosTask,fn,sig,VM86);
313 #define CP(x,y) VM86->regs.x = y##_reg(&context)
317 VM86->vm86plus.force_return_for_pic = IS_PEND(&context) ? 1 : 0;
323 void DOSVM_ProcessMessage(LPDOSTASK lpDosTask,MSG *msg)
327 fprintf(stderr,"got message %04x, wparam=%08x, lparam=%08lx\n",msg->message,msg->wParam,msg->lParam);
328 if ((msg->message>=WM_MOUSEFIRST)&&
329 (msg->message<=WM_MOUSELAST)) {
330 INT_Int33Message(msg->message,msg->wParam,msg->lParam);
332 switch (msg->message) {
336 scan |= (msg->lParam >> 16) & 0x7f;
338 /* check whether extended bit is set,
339 * and if so, queue the extension prefix */
340 if (msg->lParam & 0x1000000) {
341 /* FIXME: some keys (function keys) have
342 * extended bit set even when they shouldn't,
343 * should check for them */
344 INT_Int09SendScan(0xE0);
346 INT_Int09SendScan(scan);
352 void DOSVM_Wait( int read_pipe, HANDLE hObject )
354 LPDOSTASK lpDosTask = MZ_Current();
357 BOOL got_msg = FALSE;
360 /* check for messages (waste time before the response check below) */
361 while (Callout.PeekMessageA(&msg,0,0,0,PM_REMOVE|PM_NOYIELD)) {
363 DOSVM_ProcessMessage(lpDosTask,&msg);
364 /* we don't need a TranslateMessage here */
365 Callout.DispatchMessageA(&msg);
368 if (read_pipe == -1) {
372 struct timeval timeout={0,0};
373 /* quick check for response from dosmod
374 * (faster than doing the full blocking wait, if data already available) */
375 FD_ZERO(&readfds); FD_SET(read_pipe,&readfds);
376 if (select(read_pipe+1,&readfds,NULL,NULL,&timeout)>0)
379 /* check for data from win32 console device */
381 /* nothing yet, block while waiting for something to do */
382 waitret=MsgWaitForMultipleObjects(1,&hObject,FALSE,INFINITE,QS_ALLINPUT);
383 if (waitret==(DWORD)-1) {
384 ERR_(module)("dosvm wait error=%ld\n",GetLastError());
386 if (read_pipe != -1) {
387 if (waitret==WAIT_OBJECT_0) break;
392 int DOSVM_Enter( CONTEXT86 *context )
394 LPDOSTASK lpDosTask = MZ_Current();
395 struct vm86plus_struct VM86;
399 /* MZ_CreateProcess or MZ_AllocDPMITask should have been called first */
400 ERR_(module)("dosmod has not been initialized!");
405 #define CP(x,y) VM86.regs.x = y##_reg(context)
408 if (VM86.regs.eflags & IF_MASK)
409 VM86.regs.eflags |= VIF_MASK;
412 /* allocate standard DOS handles */
413 FILE_InitProcessDosHandles();
415 memset(&VM86,0,sizeof(VM86));
416 VM86.regs.cs=lpDosTask->init_cs;
417 VM86.regs.eip=lpDosTask->init_ip;
418 VM86.regs.ss=lpDosTask->init_ss;
419 VM86.regs.esp=lpDosTask->init_sp;
420 VM86.regs.ds=lpDosTask->psp_seg;
421 VM86.regs.es=lpDosTask->psp_seg;
422 VM86.regs.eflags=VIF_MASK;
423 /* hmm, what else do we need? */
426 /* main exchange loop */
430 /* transmit VM86 structure to dosmod task */
431 if (write(lpDosTask->write_pipe,&stat,sizeof(stat))!=sizeof(stat)) {
432 ERR_(module)("dosmod sync lost, errno=%d, fd=%d, pid=%d\n",errno,lpDosTask->write_pipe,getpid());
435 if (write(lpDosTask->write_pipe,&VM86,sizeof(VM86))!=sizeof(VM86)) {
436 ERR_(module)("dosmod sync lost, errno=%d\n",errno);
439 /* wait for response, doing other things in the meantime */
440 DOSVM_Wait(lpDosTask->read_pipe, lpDosTask->hReadPipe);
443 if ((len=read(lpDosTask->read_pipe,&stat,sizeof(stat)))==sizeof(stat)) break;
444 if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) {
445 WARN_(module)("rereading dosmod return code due to errno=%d, result=%d\n",errno,len);
448 ERR_(module)("dosmod sync lost reading return code, errno=%d, result=%d\n",errno,len);
451 TRACE_(module)("dosmod return code=%d\n",stat);
453 if ((len=read(lpDosTask->read_pipe,&VM86,sizeof(VM86)))==sizeof(VM86)) break;
454 if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) {
455 WARN_(module)("rereading dosmod VM86 structure due to errno=%d, result=%d\n",errno,len);
458 ERR_(module)("dosmod sync lost reading VM86 structure, errno=%d, result=%d\n",errno,len);
461 if ((stat&0xff)==DOSMOD_SIGNAL) {
463 if ((len=read(lpDosTask->read_pipe,&sig,sizeof(sig)))==sizeof(sig)) break;
464 if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) {
465 WARN_(module)("rereading dosmod signal due to errno=%d, result=%d\n",errno,len);
468 ERR_(module)("dosmod sync lost reading signal, errno=%d, result=%d\n",errno,len);
473 } while (DOSVM_Process(lpDosTask,stat,sig,&VM86)>=0);
476 #define CP(x,y) y##_reg(context) = VM86.regs.x
483 void DOSVM_PIC_ioport_out( WORD port, BYTE val)
485 LPDOSTASK lpDosTask = MZ_Current();
489 if ((port==0x20) && (val==0x20)) {
490 if (lpDosTask->current) {
491 /* EOI (End Of Interrupt) */
492 TRACE_(int)("received EOI for current IRQ, clearing\n");
493 event = lpDosTask->current;
494 lpDosTask->current = event->next;
496 (*event->relay)(lpDosTask,NULL,event->data);
499 if (lpDosTask->pending &&
500 !lpDosTask->sig_sent) {
501 /* another event is pending, which we should probably
502 * be able to process now, so tell dosmod about it */
503 TRACE_(int)("another event pending, signalling dosmod\n");
504 kill(lpDosTask->task,SIGUSR2);
505 lpDosTask->sig_sent++;
508 WARN_(int)("EOI without active IRQ\n");
511 FIXME_(int)("unrecognized PIC command %02x\n",val);
516 void DOSVM_SetTimer( unsigned ticks )
518 LPDOSTASK lpDosTask = MZ_Current();
519 int stat=DOSMOD_SET_TIMER;
523 /* the PC clocks ticks at 1193180 Hz */
525 tim.tv_usec=((unsigned long long)ticks*1000000)/1193180;
527 if (!tim.tv_usec) tim.tv_usec=1;
529 if (write(lpDosTask->write_pipe,&stat,sizeof(stat))!=sizeof(stat)) {
530 ERR_(module)("dosmod sync lost, errno=%d\n",errno);
533 if (write(lpDosTask->write_pipe,&tim,sizeof(tim))!=sizeof(tim)) {
534 ERR_(module)("dosmod sync lost, errno=%d\n",errno);
537 /* there's no return */
541 unsigned DOSVM_GetTimer( void )
543 LPDOSTASK lpDosTask = MZ_Current();
544 int stat=DOSMOD_GET_TIMER;
548 if (write(lpDosTask->write_pipe,&stat,sizeof(stat))!=sizeof(stat)) {
549 ERR_(module)("dosmod sync lost, errno=%d\n",errno);
554 if (read(lpDosTask->read_pipe,&tim,sizeof(tim))==sizeof(tim)) break;
555 if ((errno==EINTR)||(errno==EAGAIN)) continue;
556 ERR_(module)("dosmod sync lost, errno=%d\n",errno);
559 return ((unsigned long long)tim.tv_usec*1193180)/1000000;
564 void DOSVM_SetSystemData( int id, void *data )
566 LPDOSTASK lpDosTask = MZ_Current();
567 DOSSYSTEM *sys, *prev;
570 sys = lpDosTask->sys;
572 while (sys && (sys->id != id)) {
580 sys = malloc(sizeof(DOSSYSTEM));
584 if (prev) prev->next = sys;
585 else lpDosTask->sys = sys;
590 void* DOSVM_GetSystemData( int id )
592 LPDOSTASK lpDosTask = MZ_Current();
596 sys = lpDosTask->sys;
597 while (sys && (sys->id != id))
605 #else /* !MZ_SUPPORTED */
607 int DOSVM_Enter( CONTEXT86 *context )
609 ERR_(module)("DOS realmode not supported on this architecture!\n");
613 void DOSVM_Wait( int read_pipe, HANDLE hObject) {}
614 void DOSVM_PIC_ioport_out( WORD port, BYTE val) {}
615 void DOSVM_SetTimer( unsigned ticks ) {}
616 unsigned DOSVM_GetTimer( void ) { return 0; }
617 void DOSVM_SetSystemData( int id, void *data ) { free(data); }
618 void* DOSVM_GetSystemData( int id ) { return NULL; }
619 void DOSVM_QueueEvent( int irq, int priority, void (*relay)(LPDOSTASK,CONTEXT86*,void*), void *data) {}