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"
37 #include "stackframe.h"
38 #include "debugtools.h"
40 DECLARE_DEBUG_CHANNEL(int)
41 DECLARE_DEBUG_CHANNEL(module
)
42 DECLARE_DEBUG_CHANNEL(relay
)
46 #ifdef HAVE_SYS_VM86_H
47 # include <sys/vm86.h>
49 #ifdef HAVE_SYS_MMAN_H
50 # include <sys/mman.h>
53 #define IF_CLR(ctx) EFL_reg(ctx) &= ~VIF_MASK
54 #define IF_ENABLED(ctx) (EFL_reg(ctx) & VIF_MASK)
55 #define SET_PEND(ctx) EFL_reg(ctx) |= VIP_MASK
56 #define CLR_PEND(ctx) EFL_reg(ctx) &= ~VIP_MASK
57 #define IS_PEND(ctx) (EFL_reg(ctx) & VIP_MASK)
61 static void do_exception( int signal
, CONTEXT86
*context
)
64 if ((signal
== SIGTRAP
) || (signal
== SIGHUP
))
66 rec
.ExceptionCode
= EXCEPTION_BREAKPOINT
;
67 rec
.ExceptionFlags
= EXCEPTION_CONTINUABLE
;
71 rec
.ExceptionCode
= EXCEPTION_ILLEGAL_INSTRUCTION
; /* generic error */
72 rec
.ExceptionFlags
= EH_NONCONTINUABLE
;
74 rec
.ExceptionRecord
= NULL
;
75 rec
.ExceptionAddress
= (LPVOID
)EIP_reg(context
);
76 rec
.NumberParameters
= 0;
77 EXC_RtlRaiseException( &rec
, context
);
80 static void DOSVM_Dump( LPDOSTASK lpDosTask
, int fn
, int sig
,
81 struct vm86plus_struct
*VM86
)
87 switch (VM86_TYPE(fn
)) {
89 printf("Trapped signal %d\n",sig
); break;
91 printf("Trapped unhandled GPF\n"); break;
93 printf("Trapped INT %02x\n",VM86_ARG(fn
)); break;
95 printf("Trapped STI\n"); break;
97 printf("Trapped due to pending PIC request\n"); break;
99 printf("Trapped debug request\n"); break;
101 printf("Trapped unknown VM86 type %d arg %d\n",VM86_TYPE(fn
),VM86_ARG(fn
)); break;
103 #define REGS VM86->regs
104 fprintf(stderr
,"AX=%04lX CX=%04lX DX=%04lX BX=%04lX\n",REGS
.eax
,REGS
.ecx
,REGS
.edx
,REGS
.ebx
);
105 fprintf(stderr
,"SI=%04lX DI=%04lX SP=%04lX BP=%04lX\n",REGS
.esi
,REGS
.edi
,REGS
.esp
,REGS
.ebp
);
106 fprintf(stderr
,"CS=%04X DS=%04X ES=%04X SS=%04X\n",REGS
.cs
,REGS
.ds
,REGS
.es
,REGS
.ss
);
107 fprintf(stderr
,"IP=%04lX EFLAGS=%08lX\n",REGS
.eip
,REGS
.eflags
);
109 iofs
=((DWORD
)REGS
.cs
<<4)+REGS
.eip
;
111 inst
=(BYTE
*)lpDosTask
->img
+iofs
;
113 for (x
=0; x
<8; x
++) printf(" %02x",inst
[x
]);
117 static int DOSVM_Int( int vect
, CONTEXT86
*context
, LPDOSTASK lpDosTask
)
119 extern UINT16 DPMI_wrap_seg
;
122 if (CS_reg(context
)==DPMI_wrap_seg
) {
123 /* exit from real-mode wrapper */
126 /* we could probably move some other dodgy stuff here too from dpmi.c */
128 INT_RealModeInterrupt(vect
,context
);
132 static void DOSVM_SimulateInt( int vect
, CONTEXT86
*context
, LPDOSTASK lpDosTask
)
134 FARPROC16 handler
=INT_GetRMHandler(vect
);
136 if (SELECTOROF(handler
)==0xf000) {
137 /* if internal interrupt, call it directly */
138 INT_RealModeInterrupt(vect
,context
);
140 WORD
*stack
=(WORD
*)(V86BASE(context
)+(((DWORD
)SS_reg(context
))<<4)+LOWORD(ESP_reg(context
)));
141 WORD flag
=LOWORD(EFL_reg(context
));
143 if (IF_ENABLED(context
)) flag
|=IF_MASK
;
147 *(--stack
)=CS_reg(context
);
148 *(--stack
)=LOWORD(EIP_reg(context
));
150 CS_reg(context
)=SELECTOROF(handler
);
151 EIP_reg(context
)=OFFSETOF(handler
);
156 #define SHOULD_PEND(x) \
157 (x && ((!lpDosTask->current) || (x->priority < lpDosTask->current->priority)))
159 static void DOSVM_SendQueuedEvent(CONTEXT86
*context
, LPDOSTASK lpDosTask
)
161 LPDOSEVENT event
= lpDosTask
->pending
;
163 if (SHOULD_PEND(event
)) {
164 /* remove from "pending" list */
165 lpDosTask
->pending
= event
->next
;
168 /* it's an IRQ, move it to "current" list */
169 event
->next
= lpDosTask
->current
;
170 lpDosTask
->current
= event
;
171 TRACE_(int)("dispatching IRQ %d\n",event
->irq
);
172 /* note that if DOSVM_SimulateInt calls an internal interrupt directly,
173 * lpDosTask->current might be cleared (and event freed) in this very call! */
174 DOSVM_SimulateInt((event
->irq
<8)?(event
->irq
+8):(event
->irq
-8+0x70),context
,lpDosTask
);
177 TRACE_(int)("dispatching callback event\n");
178 (*event
->relay
)(lpDosTask
,context
,event
->data
);
182 if (!SHOULD_PEND(lpDosTask
->pending
)) {
183 TRACE_(int)("clearing Pending flag\n");
188 static void DOSVM_SendQueuedEvents(CONTEXT86
*context
, LPDOSTASK lpDosTask
)
190 /* we will send all queued events as long as interrupts are enabled,
191 * but IRQ events will disable interrupts again */
192 while (IS_PEND(context
) && IF_ENABLED(context
))
193 DOSVM_SendQueuedEvent(context
,lpDosTask
);
196 void DOSVM_QueueEvent( int irq
, int priority
, void (*relay
)(LPDOSTASK
,CONTEXT86
*,void*), void *data
)
198 LPDOSTASK lpDosTask
= MZ_Current();
199 LPDOSEVENT event
, cur
, prev
;
201 if (lpDosTask
&& lpDosTask
->entered
) {
202 event
= malloc(sizeof(DOSEVENT
));
204 ERR_(int)("out of memory allocating event entry\n");
207 event
->irq
= irq
; event
->priority
= priority
;
208 event
->relay
= relay
; event
->data
= data
;
210 /* insert event into linked list, in order *after*
211 * all earlier events of higher or equal priority */
212 cur
= lpDosTask
->pending
; prev
= NULL
;
213 while (cur
&& cur
->priority
<=priority
) {
218 if (prev
) prev
->next
= event
;
219 else lpDosTask
->pending
= event
;
221 /* get dosmod's attention to the new event, if necessary */
222 if (!lpDosTask
->sig_sent
) {
223 TRACE_(int)("new event queued, signalling dosmod\n");
224 kill(lpDosTask
->task
,SIGUSR2
);
225 lpDosTask
->sig_sent
++;
227 TRACE_(int)("new event queued\n");
230 /* DOS subsystem not running */
231 /* (this probably means that we're running a win16 app
232 * which uses DPMI to thunk down to DOS services) */
234 /* callback event, perform it with dummy context */
236 memset(&context
,0,sizeof(context
));
237 (*relay
)(lpDosTask
,&context
,data
);
239 ERR_(int)("IRQ without DOS task: should not happen");
244 #define CV CP(eax,EAX); CP(ecx,ECX); CP(edx,EDX); CP(ebx,EBX); \
245 CP(esi,ESI); CP(edi,EDI); CP(esp,ESP); CP(ebp,EBP); \
246 CP(cs,CS); CP(ds,DS); CP(es,ES); \
247 CP(ss,SS); CP(fs,FS); CP(gs,GS); \
248 CP(eip,EIP); CP(eflags,EFL)
250 static int DOSVM_Process( LPDOSTASK lpDosTask
, int fn
, int sig
,
251 struct vm86plus_struct
*VM86
)
256 #define CP(x,y) y##_reg(&context) = VM86->regs.x
259 if (VM86_TYPE(fn
)==VM86_UNKNOWN
) {
260 ret
=INSTR_EmulateInstruction(&context
);
261 #define CP(x,y) VM86->regs.x = y##_reg(&context)
268 if (VM86
->vm86plus
.force_return_for_pic
) {
272 /* linux doesn't preserve pending flag on return */
273 if (SHOULD_PEND(lpDosTask
->pending
)) {
278 switch (VM86_TYPE(fn
)) {
280 TRACE_(int)("DOS module caught signal %d\n",sig
);
281 if ((sig
==SIGALRM
) || (sig
==SIGUSR2
)) {
283 lpDosTask
->sig_sent
++;
284 DOSVM_QueueEvent(0,DOS_PRIORITY_REALTIME
,NULL
,NULL
);
286 if (lpDosTask
->pending
) {
287 TRACE_(int)("setting Pending flag, interrupts are currently %s\n",
288 IF_ENABLED(&context
) ? "enabled" : "disabled");
290 DOSVM_SendQueuedEvents(&context
,lpDosTask
);
292 TRACE_(int)("no events are pending, clearing Pending flag\n");
295 lpDosTask
->sig_sent
--;
297 else if ((sig
==SIGHUP
) || (sig
==SIGILL
) || (sig
==SIGSEGV
)) {
298 do_exception( sig
, &context
);
300 DOSVM_Dump(lpDosTask
,fn
,sig
,VM86
);
304 case VM86_UNKNOWN
: /* unhandled GPF */
305 DOSVM_Dump(lpDosTask
,fn
,sig
,VM86
);
306 do_exception( SIGSEGV
, &context
);
310 DPRINTF("Call DOS int 0x%02x (EAX=%08lx) ret=%04lx:%04lx\n",VM86_ARG(fn
),context
.Eax
,context
.SegCs
,context
.Eip
);
311 ret
=DOSVM_Int(VM86_ARG(fn
),&context
,lpDosTask
);
313 DPRINTF("Ret DOS int 0x%02x (EAX=%08lx) ret=%04lx:%04lx\n",VM86_ARG(fn
),context
.Eax
,context
.SegCs
,context
.Eip
);
317 TRACE_(int)("DOS task enabled interrupts with events pending, sending events\n");
318 DOSVM_SendQueuedEvents(&context
,lpDosTask
);
321 do_exception( SIGTRAP
, &context
);
324 DOSVM_Dump(lpDosTask
,fn
,sig
,VM86
);
328 #define CP(x,y) VM86->regs.x = y##_reg(&context)
332 VM86
->vm86plus
.force_return_for_pic
= IS_PEND(&context
) ? 1 : 0;
338 static void DOSVM_ProcessConsole(LPDOSTASK lpDosTask
)
344 if (ReadConsoleInputA(GetStdHandle(STD_INPUT_HANDLE
),&msg
,1,&res
)) {
345 switch (msg
.EventType
) {
347 scan
= msg
.Event
.KeyEvent
.wVirtualScanCode
;
348 if (!msg
.Event
.KeyEvent
.bKeyDown
) scan
|= 0x80;
350 /* check whether extended bit is set,
351 * and if so, queue the extension prefix */
352 if (msg
.Event
.KeyEvent
.dwControlKeyState
& ENHANCED_KEY
) {
353 INT_Int09SendScan(0xE0,0);
355 INT_Int09SendScan(scan
,msg
.Event
.KeyEvent
.uChar
.AsciiChar
);
358 FIXME_(int)("unhandled console event: %d\n", msg
.EventType
);
363 static void DOSVM_ProcessMessage(LPDOSTASK lpDosTask
,MSG
*msg
)
367 TRACE_(int)("got message %04x, wparam=%08x, lparam=%08lx\n",msg
->message
,msg
->wParam
,msg
->lParam
);
368 if ((msg
->message
>=WM_MOUSEFIRST
)&&
369 (msg
->message
<=WM_MOUSELAST
)) {
370 INT_Int33Message(msg
->message
,msg
->wParam
,msg
->lParam
);
372 switch (msg
->message
) {
376 scan
|= (msg
->lParam
>> 16) & 0x7f;
378 /* check whether extended bit is set,
379 * and if so, queue the extension prefix */
380 if (msg
->lParam
& 0x1000000) {
381 /* FIXME: some keys (function keys) have
382 * extended bit set even when they shouldn't,
383 * should check for them */
384 INT_Int09SendScan(0xE0,0);
386 INT_Int09SendScan(scan
,0);
392 void DOSVM_Wait( int read_pipe
, HANDLE hObject
)
394 LPDOSTASK lpDosTask
= MZ_Current();
399 BOOL got_msg
= FALSE
;
401 objs
[0]=GetStdHandle(STD_INPUT_HANDLE
);
405 /* check for messages (waste time before the response check below) */
406 while (Callout
.PeekMessageA(&msg
,0,0,0,PM_REMOVE
|PM_NOYIELD
)) {
408 DOSVM_ProcessMessage(lpDosTask
,&msg
);
409 /* we don't need a TranslateMessage here */
410 Callout
.DispatchMessageA(&msg
);
414 /* check for console input */
417 if (PeekConsoleInputA(objs
[0],&msg
,1,&num
) && num
) {
418 DOSVM_ProcessConsole(lpDosTask
);
422 if (read_pipe
== -1) {
426 struct timeval timeout
={0,0};
427 /* quick check for response from dosmod
428 * (faster than doing the full blocking wait, if data already available) */
429 FD_ZERO(&readfds
); FD_SET(read_pipe
,&readfds
);
430 if (select(read_pipe
+1,&readfds
,NULL
,NULL
,&timeout
)>0)
433 /* nothing yet, block while waiting for something to do */
434 waitret
=MsgWaitForMultipleObjects(objc
,objs
,FALSE
,INFINITE
,QS_ALLINPUT
);
435 if (waitret
==(DWORD
)-1) {
436 ERR_(module
)("dosvm wait error=%ld\n",GetLastError());
438 if ((read_pipe
!= -1) && hObject
) {
439 if (waitret
==(WAIT_OBJECT_0
+1)) break;
441 if (waitret
==WAIT_OBJECT_0
) {
442 DOSVM_ProcessConsole(lpDosTask
);
447 int DOSVM_Enter( CONTEXT86
*context
)
449 LPDOSTASK lpDosTask
= MZ_Current();
450 struct vm86plus_struct VM86
;
454 /* MZ_CreateProcess or MZ_AllocDPMITask should have been called first */
455 ERR_(module
)("dosmod has not been initialized!");
460 #define CP(x,y) VM86.regs.x = y##_reg(context)
463 if (VM86
.regs
.eflags
& IF_MASK
)
464 VM86
.regs
.eflags
|= VIF_MASK
;
467 /* allocate standard DOS handles */
468 FILE_InitProcessDosHandles();
470 memset(&VM86
,0,sizeof(VM86
));
471 VM86
.regs
.cs
=lpDosTask
->init_cs
;
472 VM86
.regs
.eip
=lpDosTask
->init_ip
;
473 VM86
.regs
.ss
=lpDosTask
->init_ss
;
474 VM86
.regs
.esp
=lpDosTask
->init_sp
;
475 VM86
.regs
.ds
=lpDosTask
->psp_seg
;
476 VM86
.regs
.es
=lpDosTask
->psp_seg
;
477 VM86
.regs
.eflags
=VIF_MASK
;
478 /* hmm, what else do we need? */
481 /* main exchange loop */
482 lpDosTask
->entered
++;
484 TRACE_(module
)("thread is: %lx\n",GetCurrentThreadId());
487 /* transmit VM86 structure to dosmod task */
488 if (write(lpDosTask
->write_pipe
,&stat
,sizeof(stat
))!=sizeof(stat
)) {
489 ERR_(module
)("dosmod sync lost, errno=%d, fd=%d, pid=%d\n",errno
,lpDosTask
->write_pipe
,getpid());
492 if (write(lpDosTask
->write_pipe
,&VM86
,sizeof(VM86
))!=sizeof(VM86
)) {
493 ERR_(module
)("dosmod sync lost, errno=%d\n",errno
);
496 /* wait for response, doing other things in the meantime */
497 DOSVM_Wait(lpDosTask
->read_pipe
, lpDosTask
->hReadPipe
);
500 if ((len
=read(lpDosTask
->read_pipe
,&stat
,sizeof(stat
)))==sizeof(stat
)) break;
501 if (((errno
==EINTR
)||(errno
==EAGAIN
))&&(len
<=0)) {
502 WARN_(module
)("rereading dosmod return code due to errno=%d, result=%d\n",errno
,len
);
505 ERR_(module
)("dosmod sync lost reading return code, errno=%d, result=%d\n",errno
,len
);
508 TRACE_(module
)("dosmod return code=%d\n",stat
);
509 if (stat
==DOSMOD_LEFTIDLE
) {
514 if ((len
=read(lpDosTask
->read_pipe
,&VM86
,sizeof(VM86
)))==sizeof(VM86
)) break;
515 if (((errno
==EINTR
)||(errno
==EAGAIN
))&&(len
<=0)) {
516 WARN_(module
)("rereading dosmod VM86 structure due to errno=%d, result=%d\n",errno
,len
);
519 ERR_(module
)("dosmod sync lost reading VM86 structure, errno=%d, result=%d\n",errno
,len
);
522 if ((stat
&0xff)==DOSMOD_SIGNAL
) {
524 if ((len
=read(lpDosTask
->read_pipe
,&sig
,sizeof(sig
)))==sizeof(sig
)) break;
525 if (((errno
==EINTR
)||(errno
==EAGAIN
))&&(len
<=0)) {
526 WARN_(module
)("rereading dosmod signal due to errno=%d, result=%d\n",errno
,len
);
529 ERR_(module
)("dosmod sync lost reading signal, errno=%d, result=%d\n",errno
,len
);
534 } while (DOSVM_Process(lpDosTask
,stat
,sig
,&VM86
)>=0);
535 lpDosTask
->entered
--;
538 #define CP(x,y) y##_reg(context) = VM86.regs.x
545 void DOSVM_PIC_ioport_out( WORD port
, BYTE val
)
547 LPDOSTASK lpDosTask
= MZ_Current();
551 if ((port
==0x20) && (val
==0x20)) {
552 if (lpDosTask
->current
) {
553 /* EOI (End Of Interrupt) */
554 TRACE_(int)("received EOI for current IRQ, clearing\n");
555 event
= lpDosTask
->current
;
556 lpDosTask
->current
= event
->next
;
558 (*event
->relay
)(lpDosTask
,NULL
,event
->data
);
561 if (lpDosTask
->pending
&&
562 !lpDosTask
->sig_sent
) {
563 /* another event is pending, which we should probably
564 * be able to process now, so tell dosmod about it */
565 TRACE_(int)("another event pending, signalling dosmod\n");
566 kill(lpDosTask
->task
,SIGUSR2
);
567 lpDosTask
->sig_sent
++;
570 WARN_(int)("EOI without active IRQ\n");
573 FIXME_(int)("unrecognized PIC command %02x\n",val
);
578 void DOSVM_SetTimer( unsigned ticks
)
580 LPDOSTASK lpDosTask
= MZ_Current();
581 int stat
=DOSMOD_SET_TIMER
;
585 /* the PC clocks ticks at 1193180 Hz */
587 tim
.tv_usec
=((unsigned long long)ticks
*1000000)/1193180;
589 if (!tim
.tv_usec
) tim
.tv_usec
=1;
591 if (write(lpDosTask
->write_pipe
,&stat
,sizeof(stat
))!=sizeof(stat
)) {
592 ERR_(module
)("dosmod sync lost, errno=%d\n",errno
);
595 if (write(lpDosTask
->write_pipe
,&tim
,sizeof(tim
))!=sizeof(tim
)) {
596 ERR_(module
)("dosmod sync lost, errno=%d\n",errno
);
599 /* there's no return */
603 unsigned DOSVM_GetTimer( void )
605 LPDOSTASK lpDosTask
= MZ_Current();
606 int stat
=DOSMOD_GET_TIMER
;
610 if (write(lpDosTask
->write_pipe
,&stat
,sizeof(stat
))!=sizeof(stat
)) {
611 ERR_(module
)("dosmod sync lost, errno=%d\n",errno
);
616 if (read(lpDosTask
->read_pipe
,&tim
,sizeof(tim
))==sizeof(tim
)) break;
617 if ((errno
==EINTR
)||(errno
==EAGAIN
)) continue;
618 ERR_(module
)("dosmod sync lost, errno=%d\n",errno
);
621 return ((unsigned long long)tim
.tv_usec
*1193180)/1000000;
626 void DOSVM_SetSystemData( int id
, void *data
)
628 LPDOSTASK lpDosTask
= MZ_Current();
629 DOSSYSTEM
*sys
, *prev
;
632 sys
= lpDosTask
->sys
;
634 while (sys
&& (sys
->id
!= id
)) {
642 sys
= malloc(sizeof(DOSSYSTEM
));
646 if (prev
) prev
->next
= sys
;
647 else lpDosTask
->sys
= sys
;
652 void* DOSVM_GetSystemData( int id
)
654 LPDOSTASK lpDosTask
= MZ_Current();
658 sys
= lpDosTask
->sys
;
659 while (sys
&& (sys
->id
!= id
))
667 #else /* !MZ_SUPPORTED */
669 int DOSVM_Enter( CONTEXT86
*context
)
671 ERR_(module
)("DOS realmode not supported on this architecture!\n");
675 void DOSVM_Wait( int read_pipe
, HANDLE hObject
) {}
676 void DOSVM_PIC_ioport_out( WORD port
, BYTE val
) {}
677 void DOSVM_SetTimer( unsigned ticks
) {}
678 unsigned DOSVM_GetTimer( void ) { return 0; }
679 void DOSVM_SetSystemData( int id
, void *data
) { free(data
); }
680 void* DOSVM_GetSystemData( int id
) { return NULL
; }
681 void DOSVM_QueueEvent( int irq
, int priority
, void (*relay
)(LPDOSTASK
,CONTEXT86
*,void*), void *data
)
684 /* callback event, perform it with dummy context */
686 memset(&context
,0,sizeof(context
));
687 (*relay
)(lpDosTask
,&context
,data
);
689 ERR_(int)("IRQ without DOS task: should not happen");