4 * Copyright 1998 Ove Kåven
6 * This code hasn't been completely cleaned up yet.
17 #include <sys/types.h>
20 #include "wine/winbase16.h"
23 #include "sig_context.h"
40 #define IF_CLR(ctx) EFL_reg(ctx) &= ~VIF_MASK
41 #define IF_ENABLED(ctx) (EFL_reg(ctx) & VIF_MASK)
42 #define SET_PEND(ctx) EFL_reg(ctx) |= VIP_MASK
43 #define CLR_PEND(ctx) EFL_reg(ctx) &= ~VIP_MASK
44 #define IS_PEND(ctx) (EFL_reg(ctx) & VIP_MASK)
48 static void DOSVM_Dump( LPDOSTASK lpDosTask
, int fn
, int sig
,
49 struct vm86plus_struct
*VM86
)
55 switch (VM86_TYPE(fn
)) {
57 printf("Trapped signal %d\n",sig
); break;
59 printf("Trapped unhandled GPF\n"); break;
61 printf("Trapped INT %02x\n",VM86_ARG(fn
)); break;
63 printf("Trapped STI\n"); break;
65 printf("Trapped due to pending PIC request\n"); break;
67 printf("Trapped debug request\n"); break;
69 printf("Trapped unknown VM86 type %d arg %d\n",VM86_TYPE(fn
),VM86_ARG(fn
)); break;
71 #define REGS VM86->regs
72 fprintf(stderr
,"AX=%04lX CX=%04lX DX=%04lX BX=%04lX\n",REGS
.eax
,REGS
.ecx
,REGS
.edx
,REGS
.ebx
);
73 fprintf(stderr
,"SI=%04lX DI=%04lX SP=%04lX BP=%04lX\n",REGS
.esi
,REGS
.edi
,REGS
.esp
,REGS
.ebp
);
74 fprintf(stderr
,"CS=%04X DS=%04X ES=%04X SS=%04X\n",REGS
.cs
,REGS
.ds
,REGS
.es
,REGS
.ss
);
75 fprintf(stderr
,"IP=%04lX EFLAGS=%08lX\n",REGS
.eip
,REGS
.eflags
);
77 iofs
=((DWORD
)REGS
.cs
<<4)+REGS
.eip
;
79 inst
=(BYTE
*)lpDosTask
->img
+iofs
;
81 for (x
=0; x
<8; x
++) printf(" %02x",inst
[x
]);
85 static int DOSVM_Int( int vect
, PCONTEXT context
, LPDOSTASK lpDosTask
)
87 extern UINT16 DPMI_wrap_seg
;
90 if (CS_reg(context
)==DPMI_wrap_seg
) {
91 /* exit from real-mode wrapper */
94 /* we could probably move some other dodgy stuff here too from dpmi.c */
96 INT_RealModeInterrupt(vect
,context
);
100 static void DOSVM_SimulateInt( int vect
, PCONTEXT context
, LPDOSTASK lpDosTask
)
102 FARPROC16 handler
=INT_GetRMHandler(vect
);
104 if (SELECTOROF(handler
)==0xf000) {
105 /* if internal interrupt, call it directly */
106 INT_RealModeInterrupt(vect
,context
);
108 WORD
*stack
=(WORD
*)(V86BASE(context
)+(((DWORD
)SS_reg(context
))<<4)+SP_reg(context
));
109 WORD flag
=FL_reg(context
);
111 if (IF_ENABLED(context
)) flag
|=IF_MASK
;
115 *(--stack
)=CS_reg(context
);
116 *(--stack
)=IP_reg(context
);
118 CS_reg(context
)=SELECTOROF(handler
);
119 IP_reg(context
)=OFFSETOF(handler
);
124 #define SHOULD_PEND(x) \
125 (x && ((!lpDosTask->current) || (x->priority < lpDosTask->current->priority)))
127 static void DOSVM_SendQueuedEvent(PCONTEXT context
, LPDOSTASK lpDosTask
)
129 LPDOSEVENT event
= lpDosTask
->pending
;
131 if (SHOULD_PEND(event
)) {
132 /* remove from "pending" list */
133 lpDosTask
->pending
= event
->next
;
136 /* it's an IRQ, move it to "current" list */
137 event
->next
= lpDosTask
->current
;
138 lpDosTask
->current
= event
;
139 TRACE(int,"dispatching IRQ %d\n",event
->irq
);
140 /* note that if DOSVM_SimulateInt calls an internal interrupt directly,
141 * lpDosTask->current might be cleared (and event freed) in this very call! */
142 DOSVM_SimulateInt((event
->irq
<8)?(event
->irq
+8):(event
->irq
-8+0x70),context
,lpDosTask
);
145 TRACE(int,"dispatching callback event\n");
146 (*event
->relay
)(lpDosTask
,context
,event
->data
);
150 if (!SHOULD_PEND(lpDosTask
->pending
)) {
151 TRACE(int,"clearing Pending flag\n");
156 static void DOSVM_SendQueuedEvents(PCONTEXT context
, LPDOSTASK lpDosTask
)
158 /* we will send all queued events as long as interrupts are enabled,
159 * but IRQ events will disable interrupts again */
160 while (IS_PEND(context
) && IF_ENABLED(context
))
161 DOSVM_SendQueuedEvent(context
,lpDosTask
);
164 void DOSVM_QueueEvent( int irq
, int priority
, void (*relay
)(LPDOSTASK
,PCONTEXT
,void*), void *data
)
166 TDB
*pTask
= (TDB
*)GlobalLock16( GetCurrentTask() );
167 NE_MODULE
*pModule
= NE_GetPtr( pTask
->hModule
);
168 LPDOSEVENT event
, cur
, prev
;
170 GlobalUnlock16( GetCurrentTask() );
171 if (pModule
&& pModule
->lpDosTask
) {
172 event
= malloc(sizeof(DOSEVENT
));
174 ERR(int,"out of memory allocating event entry\n");
177 event
->irq
= irq
; event
->priority
= priority
;
178 event
->relay
= relay
; event
->data
= data
;
180 /* insert event into linked list, in order *after*
181 * all earlier events of higher or equal priority */
182 cur
= pModule
->lpDosTask
->pending
; prev
= NULL
;
183 while (cur
&& cur
->priority
<=priority
) {
188 if (prev
) prev
->next
= event
;
189 else pModule
->lpDosTask
->pending
= event
;
191 /* get dosmod's attention to the new event, except for irq==0 where we already have it */
192 if (irq
&& !pModule
->lpDosTask
->sig_sent
) {
193 TRACE(int,"new event queued, signalling dosmod\n");
194 kill(pModule
->lpDosTask
->task
,SIGUSR2
);
195 pModule
->lpDosTask
->sig_sent
++;
197 TRACE(int,"new event queued\n");
202 #define CV CP(eax,EAX); CP(ecx,ECX); CP(edx,EDX); CP(ebx,EBX); \
203 CP(esi,ESI); CP(edi,EDI); CP(esp,ESP); CP(ebp,EBP); \
204 CP(cs,CS); CP(ds,DS); CP(es,ES); \
205 CP(ss,SS); CP(fs,FS); CP(gs,GS); \
206 CP(eip,EIP); CP(eflags,EFL)
208 static int DOSVM_Process( LPDOSTASK lpDosTask
, int fn
, int sig
,
209 struct vm86plus_struct
*VM86
)
211 SIGCONTEXT sigcontext
;
215 if (VM86_TYPE(fn
)==VM86_UNKNOWN
) {
216 /* INSTR_EmulateInstruction needs a SIGCONTEXT, not a CONTEXT... */
217 #define CP(x,y) y##_sig(&sigcontext) = VM86->regs.x
220 if (fnINSTR_EmulateInstruction
) ret
=fnINSTR_EmulateInstruction(&sigcontext
);
221 #define CP(x,y) VM86->regs.x = y##_sig(&sigcontext)
227 #define CP(x,y) y##_reg(&context) = VM86->regs.x
230 (void*)V86BASE(&context
)=lpDosTask
->img
;
232 if (VM86
->vm86plus
.force_return_for_pic
) {
236 /* linux doesn't preserve pending flag on return */
237 if (SHOULD_PEND(lpDosTask
->pending
)) {
242 switch (VM86_TYPE(fn
)) {
244 TRACE(int,"DOS module caught signal %d\n",sig
);
245 if ((sig
==SIGALRM
) || (sig
==SIGUSR2
)) {
247 DOSVM_QueueEvent(0,DOS_PRIORITY_REALTIME
,NULL
,NULL
);
249 if (lpDosTask
->pending
) {
250 TRACE(int,"setting Pending flag, interrupts are currently %s\n",
251 IF_ENABLED(&context
) ? "enabled" : "disabled");
253 DOSVM_SendQueuedEvents(&context
,lpDosTask
);
255 TRACE(int,"no events are pending, clearing Pending flag\n");
258 if (sig
==SIGUSR2
) lpDosTask
->sig_sent
--;
261 if (ctx_debug_call
) ctx_debug_call(SIGTRAP
,&context
);
263 if ((sig
==SIGILL
)||(sig
==SIGSEGV
)) {
264 if (ctx_debug_call
) ctx_debug_call(SIGILL
,&context
);
266 DOSVM_Dump(lpDosTask
,fn
,sig
,VM86
);
270 case VM86_UNKNOWN
: /* unhandled GPF */
271 DOSVM_Dump(lpDosTask
,fn
,sig
,VM86
);
272 if (ctx_debug_call
) ctx_debug_call(SIGSEGV
,&context
); else ret
=-1;
276 DPRINTF("Call DOS int 0x%02x (EAX=%08lx) ret=%04lx:%04lx\n",VM86_ARG(fn
),context
.Eax
,context
.SegCs
,context
.Eip
);
277 ret
=DOSVM_Int(VM86_ARG(fn
),&context
,lpDosTask
);
279 DPRINTF("Ret DOS int 0x%02x (EAX=%08lx) ret=%04lx:%04lx\n",VM86_ARG(fn
),context
.Eax
,context
.SegCs
,context
.Eip
);
283 TRACE(int,"DOS task enabled interrupts with events pending, sending events\n");
284 DOSVM_SendQueuedEvents(&context
,lpDosTask
);
287 if (ctx_debug_call
) ctx_debug_call(SIGTRAP
,&context
);
290 DOSVM_Dump(lpDosTask
,fn
,sig
,VM86
);
294 #define CP(x,y) VM86->regs.x = y##_reg(&context)
298 VM86
->vm86plus
.force_return_for_pic
= IS_PEND(&context
) ? 1 : 0;
304 void DOSVM_ProcessMessage(LPDOSTASK lpDosTask
,MSG
*msg
)
308 fprintf(stderr
,"got message %04x, wparam=%08x, lparam=%08lx\n",msg
->message
,msg
->wParam
,msg
->lParam
);
309 if ((msg
->message
>=WM_MOUSEFIRST
)&&
310 (msg
->message
<=WM_MOUSELAST
)) {
311 INT_Int33Message(msg
->message
,msg
->wParam
,msg
->lParam
);
313 switch (msg
->message
) {
317 scan
|= (msg
->lParam
>> 16) & 0x7f;
319 /* check whether extended bit is set,
320 * and if so, queue the extension prefix */
321 if (msg
->lParam
& 0x1000000) {
322 /* FIXME: some keys (function keys) have
323 * extended bit set even when they shouldn't,
324 * should check for them */
325 INT_Int09SendScan(0xE0);
327 INT_Int09SendScan(scan
);
333 int DOSVM_Enter( PCONTEXT context
)
335 TDB
*pTask
= (TDB
*)GlobalLock16( GetCurrentTask() );
336 NE_MODULE
*pModule
= NE_GetPtr( pTask
->hModule
);
338 struct vm86plus_struct VM86
;
343 struct timeval timeout
={0,0};
345 GlobalUnlock16( GetCurrentTask() );
347 ERR(module
,"No task is currently active!\n");
350 if (!(lpDosTask
=pModule
->lpDosTask
)) {
351 /* MZ_CreateProcess or MZ_AllocDPMITask should have been called first */
352 ERR(module
,"dosmod has not been initialized!");
357 #define CP(x,y) VM86.regs.x = y##_reg(context)
362 /* allocate standard DOS handles */
363 FILE_InitProcessDosHandles();
365 memset(&VM86
,0,sizeof(VM86
));
366 VM86
.regs
.cs
=lpDosTask
->init_cs
;
367 VM86
.regs
.eip
=lpDosTask
->init_ip
;
368 VM86
.regs
.ss
=lpDosTask
->init_ss
;
369 VM86
.regs
.esp
=lpDosTask
->init_sp
;
370 VM86
.regs
.ds
=lpDosTask
->psp_seg
;
371 VM86
.regs
.es
=lpDosTask
->psp_seg
;
372 VM86
.regs
.eflags
=VIF_MASK
;
373 /* hmm, what else do we need? */
376 /* main exchange loop */
380 /* transmit VM86 structure to dosmod task */
381 if (write(lpDosTask
->write_pipe
,&stat
,sizeof(stat
))!=sizeof(stat
)) {
382 ERR(module
,"dosmod sync lost, errno=%d, fd=%d, pid=%d\n",errno
,lpDosTask
->write_pipe
,getpid());
385 if (write(lpDosTask
->write_pipe
,&VM86
,sizeof(VM86
))!=sizeof(VM86
)) {
386 ERR(module
,"dosmod sync lost, errno=%d\n",errno
);
390 /* check for messages (waste time before the response check below) */
391 while (PeekMessageA(&msg
,0,0,0,PM_REMOVE
|PM_NOYIELD
)) {
393 DOSVM_ProcessMessage(lpDosTask
,&msg
);
394 /* we don't need a TranslateMessage here */
395 DispatchMessageA(&msg
);
397 /* quick check for response from dosmod
398 * (faster than doing the full blocking wait, if data already available) */
399 FD_ZERO(&readfds
); FD_SET(lpDosTask
->read_pipe
,&readfds
);
400 if (select(lpDosTask
->read_pipe
+1,&readfds
,NULL
,NULL
,&timeout
)>0)
402 /* nothing yet, block while waiting for something to do */
403 waitret
=MsgWaitForMultipleObjects(1,&(lpDosTask
->hReadPipe
),FALSE
,INFINITE
,QS_ALLINPUT
);
404 if (waitret
==(DWORD
)-1) {
405 ERR(module
,"dosvm wait error=%ld\n",GetLastError());
407 } while (waitret
!=WAIT_OBJECT_0
);
410 if ((len
=read(lpDosTask
->read_pipe
,&stat
,sizeof(stat
)))==sizeof(stat
)) break;
411 if (((errno
==EINTR
)||(errno
==EAGAIN
))&&(len
<=0)) {
412 WARN(module
,"rereading dosmod return code due to errno=%d, result=%d\n",errno
,len
);
415 ERR(module
,"dosmod sync lost reading return code, errno=%d, result=%d\n",errno
,len
);
418 TRACE(module
,"dosmod return code=%d\n",stat
);
420 if ((len
=read(lpDosTask
->read_pipe
,&VM86
,sizeof(VM86
)))==sizeof(VM86
)) break;
421 if (((errno
==EINTR
)||(errno
==EAGAIN
))&&(len
<=0)) {
422 WARN(module
,"rereading dosmod VM86 structure due to errno=%d, result=%d\n",errno
,len
);
425 ERR(module
,"dosmod sync lost reading VM86 structure, errno=%d, result=%d\n",errno
,len
);
428 if ((stat
&0xff)==DOSMOD_SIGNAL
) {
430 if ((len
=read(lpDosTask
->read_pipe
,&sig
,sizeof(sig
)))==sizeof(sig
)) break;
431 if (((errno
==EINTR
)||(errno
==EAGAIN
))&&(len
<=0)) {
432 WARN(module
,"rereading dosmod signal due to errno=%d, result=%d\n",errno
,len
);
435 ERR(module
,"dosmod sync lost reading signal, errno=%d, result=%d\n",errno
,len
);
440 } while (DOSVM_Process(lpDosTask
,stat
,sig
,&VM86
)>=0);
443 #define CP(x,y) y##_reg(context) = VM86.regs.x
450 void DOSVM_PIC_ioport_out( WORD port
, BYTE val
)
452 TDB
*pTask
= (TDB
*)GlobalLock16( GetCurrentTask() );
453 NE_MODULE
*pModule
= NE_GetPtr( pTask
->hModule
);
456 GlobalUnlock16( GetCurrentTask() );
457 if (pModule
&& pModule
->lpDosTask
) {
458 if ((port
==0x20) && (val
==0x20)) {
459 if (pModule
->lpDosTask
->current
) {
460 /* EOI (End Of Interrupt) */
461 TRACE(int,"received EOI for current IRQ, clearing\n");
462 event
= pModule
->lpDosTask
->current
;
463 pModule
->lpDosTask
->current
= event
->next
;
465 (*event
->relay
)(pModule
->lpDosTask
,NULL
,event
->data
);
468 if (pModule
->lpDosTask
->pending
&&
469 !pModule
->lpDosTask
->sig_sent
) {
470 /* another event is pending, which we should probably
471 * be able to process now, so tell dosmod about it */
472 TRACE(int,"another event pending, signalling dosmod\n");
473 kill(pModule
->lpDosTask
->task
,SIGUSR2
);
474 pModule
->lpDosTask
->sig_sent
++;
477 WARN(int,"EOI without active IRQ\n");
480 FIXME(int,"unrecognized PIC command %02x\n",val
);
485 void DOSVM_SetTimer( unsigned ticks
)
487 TDB
*pTask
= (TDB
*)GlobalLock16( GetCurrentTask() );
488 NE_MODULE
*pModule
= NE_GetPtr( pTask
->hModule
);
489 int stat
=DOSMOD_SET_TIMER
;
492 GlobalUnlock16( GetCurrentTask() );
493 if (pModule
&&pModule
->lpDosTask
) {
494 /* the PC clocks ticks at 1193180 Hz */
496 tim
.tv_usec
=((unsigned long long)ticks
*1000000)/1193180;
498 if (!tim
.tv_usec
) tim
.tv_usec
=1;
500 if (write(pModule
->lpDosTask
->write_pipe
,&stat
,sizeof(stat
))!=sizeof(stat
)) {
501 ERR(module
,"dosmod sync lost, errno=%d\n",errno
);
504 if (write(pModule
->lpDosTask
->write_pipe
,&tim
,sizeof(tim
))!=sizeof(tim
)) {
505 ERR(module
,"dosmod sync lost, errno=%d\n",errno
);
508 /* there's no return */
512 unsigned DOSVM_GetTimer( void )
514 TDB
*pTask
= (TDB
*)GlobalLock16( GetCurrentTask() );
515 NE_MODULE
*pModule
= NE_GetPtr( pTask
->hModule
);
516 int stat
=DOSMOD_GET_TIMER
;
519 GlobalUnlock16( GetCurrentTask() );
520 if (pModule
&&pModule
->lpDosTask
) {
521 if (write(pModule
->lpDosTask
->write_pipe
,&stat
,sizeof(stat
))!=sizeof(stat
)) {
522 ERR(module
,"dosmod sync lost, errno=%d\n",errno
);
526 if (read(pModule
->lpDosTask
->read_pipe
,&tim
,sizeof(tim
))!=sizeof(tim
)) {
527 ERR(module
,"dosmod sync lost, errno=%d\n",errno
);
530 return ((unsigned long long)tim
.tv_usec
*1193180)/1000000;
535 void DOSVM_SetSystemData( int id
, void *data
)
537 TDB
*pTask
= (TDB
*)GlobalLock16( GetCurrentTask() );
538 NE_MODULE
*pModule
= NE_GetPtr( pTask
->hModule
);
539 DOSSYSTEM
*sys
, *prev
;
541 GlobalUnlock16( GetCurrentTask() );
542 if (pModule
&& pModule
->lpDosTask
) {
543 sys
= pModule
->lpDosTask
->sys
;
545 while (sys
&& (sys
->id
!= id
)) {
553 sys
= malloc(sizeof(DOSSYSTEM
));
557 if (prev
) prev
->next
= sys
;
558 else pModule
->lpDosTask
->sys
= sys
;
563 void* DOSVM_GetSystemData( int id
)
565 TDB
*pTask
= (TDB
*)GlobalLock16( GetCurrentTask() );
566 NE_MODULE
*pModule
= NE_GetPtr( pTask
->hModule
);
569 GlobalUnlock16( GetCurrentTask() );
570 if (pModule
&& pModule
->lpDosTask
) {
571 sys
= pModule
->lpDosTask
->sys
;
572 while (sys
&& (sys
->id
!= id
))
580 #else /* !MZ_SUPPORTED */
582 int DOSVM_Enter( PCONTEXT context
)
584 ERR(module
,"DOS realmode not supported on this architecture!\n");
588 void DOSVM_PIC_ioport_out( WORD port
, BYTE val
) {}
589 void DOSVM_SetTimer( unsigned ticks
) {}
590 unsigned DOSVM_GetTimer( void ) { return 0; }
591 void DOSVM_SetSystemData( int id
, void *data
) { free(data
); }
592 void* DOSVM_GetSystemData( int id
) { return NULL
; }
593 void DOSVM_QueueEvent( int irq
, int priority
, void (*relay
)(LPDOSTASK
,PCONTEXT
,void*), void *data
) { /* EMPTY */ }