Changes in X11DRV_KEYBOARD_DetectLayout in order to implement Russian
[wine/multimedia.git] / loader / dos / dosvm.c
blobe2e4cd457cc31d45437c4e01bfb24c00b02fee68
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 "debugger.h"
28 #include "module.h"
29 #include "task.h"
30 #include "ldt.h"
31 #include "dosexe.h"
32 #include "dosmod.h"
33 #include "debug.h"
35 #ifdef MZ_SUPPORTED
37 #include <sys/mman.h>
38 #include <sys/vm86.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)
46 #undef TRY_PICRETURN
48 static void DOSVM_Dump( LPDOSTASK lpDosTask, int fn, int sig,
49 struct vm86plus_struct*VM86 )
51 unsigned iofs;
52 BYTE*inst;
53 int x;
55 switch (VM86_TYPE(fn)) {
56 case VM86_SIGNAL:
57 printf("Trapped signal %d\n",sig); break;
58 case VM86_UNKNOWN:
59 printf("Trapped unhandled GPF\n"); break;
60 case VM86_INTx:
61 printf("Trapped INT %02x\n",VM86_ARG(fn)); break;
62 case VM86_STI:
63 printf("Trapped STI\n"); break;
64 case VM86_PICRETURN:
65 printf("Trapped due to pending PIC request\n"); break;
66 case VM86_TRAP:
67 printf("Trapped debug request\n"); break;
68 default:
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;
78 #undef REGS
79 inst=(BYTE*)lpDosTask->img+iofs;
80 printf("Opcodes:");
81 for (x=0; x<8; x++) printf(" %02x",inst[x]);
82 printf("\n");
85 static int DOSVM_Int( int vect, PCONTEXT context, LPDOSTASK lpDosTask )
87 extern UINT16 DPMI_wrap_seg;
89 if (vect==0x31) {
90 if (CS_reg(context)==DPMI_wrap_seg) {
91 /* exit from real-mode wrapper */
92 return -1;
94 /* we could probably move some other dodgy stuff here too from dpmi.c */
96 INT_RealModeInterrupt(vect,context);
97 return 0;
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);
107 } else {
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;
112 else flag&=~IF_MASK;
114 *(--stack)=flag;
115 *(--stack)=CS_reg(context);
116 *(--stack)=IP_reg(context);
117 SP_reg(context)-=6;
118 CS_reg(context)=SELECTOROF(handler);
119 IP_reg(context)=OFFSETOF(handler);
120 IF_CLR(context);
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;
134 /* process event */
135 if (event->irq>=0) {
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);
143 } else {
144 /* callback event */
145 TRACE(int,"dispatching callback event\n");
146 (*event->relay)(lpDosTask,context,event->data);
147 free(event);
150 if (!SHOULD_PEND(lpDosTask->pending)) {
151 TRACE(int,"clearing Pending flag\n");
152 CLR_PEND(context);
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));
173 if (!event) {
174 ERR(int,"out of memory allocating event entry\n");
175 return;
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) {
184 prev = cur;
185 cur = cur->next;
187 event->next = cur;
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++;
196 } else {
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;
212 CONTEXT context;
213 int ret=0;
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
219 #undef CP
220 if (fnINSTR_EmulateInstruction) ret=fnINSTR_EmulateInstruction(&sigcontext);
221 #define CP(x,y) VM86->regs.x = y##_sig(&sigcontext)
223 #undef CP
224 if (ret) return 0;
225 ret=0;
227 #define CP(x,y) y##_reg(&context) = VM86->regs.x
229 #undef CP
230 (void*)V86BASE(&context)=lpDosTask->img;
231 #ifdef TRY_PICRETURN
232 if (VM86->vm86plus.force_return_for_pic) {
233 SET_PEND(&context);
235 #else
236 /* linux doesn't preserve pending flag on return */
237 if (SHOULD_PEND(lpDosTask->pending)) {
238 SET_PEND(&context);
240 #endif
242 switch (VM86_TYPE(fn)) {
243 case VM86_SIGNAL:
244 TRACE(int,"DOS module caught signal %d\n",sig);
245 if ((sig==SIGALRM) || (sig==SIGUSR2)) {
246 if (sig==SIGALRM) {
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");
252 SET_PEND(&context);
253 DOSVM_SendQueuedEvents(&context,lpDosTask);
254 } else {
255 TRACE(int,"no events are pending, clearing Pending flag\n");
256 CLR_PEND(&context);
258 if (sig==SIGUSR2) lpDosTask->sig_sent--;
259 } else
260 if (sig==SIGHUP) {
261 if (ctx_debug_call) ctx_debug_call(SIGTRAP,&context);
262 } else
263 if ((sig==SIGILL)||(sig==SIGSEGV)) {
264 if (ctx_debug_call) ctx_debug_call(SIGILL,&context);
265 } else {
266 DOSVM_Dump(lpDosTask,fn,sig,VM86);
267 ret=-1;
269 break;
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;
273 break;
274 case VM86_INTx:
275 if (TRACE_ON(relay))
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);
278 if (TRACE_ON(relay))
279 DPRINTF("Ret DOS int 0x%02x (EAX=%08lx) ret=%04lx:%04lx\n",VM86_ARG(fn),context.Eax,context.SegCs,context.Eip);
280 break;
281 case VM86_STI:
282 case VM86_PICRETURN:
283 TRACE(int,"DOS task enabled interrupts with events pending, sending events\n");
284 DOSVM_SendQueuedEvents(&context,lpDosTask);
285 break;
286 case VM86_TRAP:
287 if (ctx_debug_call) ctx_debug_call(SIGTRAP,&context);
288 break;
289 default:
290 DOSVM_Dump(lpDosTask,fn,sig,VM86);
291 ret=-1;
294 #define CP(x,y) VM86->regs.x = y##_reg(&context)
296 #undef CP
297 #ifdef TRY_PICRETURN
298 VM86->vm86plus.force_return_for_pic = IS_PEND(&context) ? 1 : 0;
299 CLR_PEND(&context);
300 #endif
301 return ret;
304 void DOSVM_ProcessMessage(LPDOSTASK lpDosTask,MSG *msg)
306 BYTE scan = 0;
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);
312 } else {
313 switch (msg->message) {
314 case WM_KEYUP:
315 scan = 0x80;
316 case WM_KEYDOWN:
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);
328 break;
333 int DOSVM_Enter( PCONTEXT context )
335 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
336 NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
337 LPDOSTASK lpDosTask;
338 struct vm86plus_struct VM86;
339 int stat,len,sig;
340 DWORD waitret;
341 MSG msg;
342 fd_set readfds;
343 struct timeval timeout={0,0};
345 GlobalUnlock16( GetCurrentTask() );
346 if (!pModule) {
347 ERR(module,"No task is currently active!\n");
348 return -1;
350 if (!(lpDosTask=pModule->lpDosTask)) {
351 /* MZ_CreateProcess or MZ_AllocDPMITask should have been called first */
352 ERR(module,"dosmod has not been initialized!");
353 return -1;
356 if (context) {
357 #define CP(x,y) VM86.regs.x = y##_reg(context)
359 #undef CP
360 } else {
361 /* initial setup */
362 /* allocate standard DOS handles */
363 FILE_InitProcessDosHandles();
364 /* registers */
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 */
377 do {
378 stat = VM86_ENTER;
379 errno = 0;
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());
383 return -1;
385 if (write(lpDosTask->write_pipe,&VM86,sizeof(VM86))!=sizeof(VM86)) {
386 ERR(module,"dosmod sync lost, errno=%d\n",errno);
387 return -1;
389 do {
390 /* check for messages (waste time before the response check below) */
391 while (PeekMessageA(&msg,0,0,0,PM_REMOVE|PM_NOYIELD)) {
392 /* got a message */
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)
401 break;
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);
408 /* read response */
409 while (1) {
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);
413 continue;
415 ERR(module,"dosmod sync lost reading return code, errno=%d, result=%d\n",errno,len);
416 return -1;
418 TRACE(module,"dosmod return code=%d\n",stat);
419 while (1) {
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);
423 continue;
425 ERR(module,"dosmod sync lost reading VM86 structure, errno=%d, result=%d\n",errno,len);
426 return -1;
428 if ((stat&0xff)==DOSMOD_SIGNAL) {
429 while (1) {
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);
433 continue;
435 ERR(module,"dosmod sync lost reading signal, errno=%d, result=%d\n",errno,len);
436 return -1;
437 } while (0);
438 } else sig=0;
439 /* got response */
440 } while (DOSVM_Process(lpDosTask,stat,sig,&VM86)>=0);
442 if (context) {
443 #define CP(x,y) y##_reg(context) = VM86.regs.x
445 #undef CP
447 return 0;
450 void DOSVM_PIC_ioport_out( WORD port, BYTE val)
452 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
453 NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
454 LPDOSEVENT event;
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;
464 if (event->relay)
465 (*event->relay)(pModule->lpDosTask,NULL,event->data);
466 free(event);
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++;
476 } else {
477 WARN(int,"EOI without active IRQ\n");
479 } else {
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;
490 struct timeval tim;
492 GlobalUnlock16( GetCurrentTask() );
493 if (pModule&&pModule->lpDosTask) {
494 /* the PC clocks ticks at 1193180 Hz */
495 tim.tv_sec=0;
496 tim.tv_usec=((unsigned long long)ticks*1000000)/1193180;
497 /* sanity check */
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);
502 return;
504 if (write(pModule->lpDosTask->write_pipe,&tim,sizeof(tim))!=sizeof(tim)) {
505 ERR(module,"dosmod sync lost, errno=%d\n",errno);
506 return;
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;
517 struct timeval tim;
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);
523 return 0;
525 /* read response */
526 if (read(pModule->lpDosTask->read_pipe,&tim,sizeof(tim))!=sizeof(tim)) {
527 ERR(module,"dosmod sync lost, errno=%d\n",errno);
528 return 0;
530 return ((unsigned long long)tim.tv_usec*1193180)/1000000;
532 return 0;
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;
544 prev = NULL;
545 while (sys && (sys->id != id)) {
546 prev = sys;
547 sys = sys->next;
549 if (sys) {
550 free(sys->data);
551 sys->data = data;
552 } else {
553 sys = malloc(sizeof(DOSSYSTEM));
554 sys->id = id;
555 sys->data = data;
556 sys->next = NULL;
557 if (prev) prev->next = sys;
558 else pModule->lpDosTask->sys = sys;
560 } else free(data);
563 void* DOSVM_GetSystemData( int id )
565 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
566 NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
567 DOSSYSTEM *sys;
569 GlobalUnlock16( GetCurrentTask() );
570 if (pModule && pModule->lpDosTask) {
571 sys = pModule->lpDosTask->sys;
572 while (sys && (sys->id != id))
573 sys = sys->next;
574 if (sys)
575 return sys->data;
577 return NULL;
580 #else /* !MZ_SUPPORTED */
582 int DOSVM_Enter( PCONTEXT context )
584 ERR(module,"DOS realmode not supported on this architecture!\n");
585 return -1;
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 */ }
595 #endif