blob: ce93852dfb92029145688ccc5bb65f4bba879137 [file] [log] [blame]
/*
* DOS Virtual Machine
*
* Copyright 1998 Ove Kåven
*
* This code hasn't been completely cleaned up yet.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "wine/winbase16.h"
#include "winnt.h"
#include "sig_context.h"
#include "msdos.h"
#include "file.h"
#include "miscemu.h"
#include "debugger.h"
#include "module.h"
#include "task.h"
#include "ldt.h"
#include "dosexe.h"
#include "dosmod.h"
#include "debug.h"
#ifdef MZ_SUPPORTED
#include <sys/mman.h>
#include <sys/vm86.h>
static void DOSVM_Dump( LPDOSTASK lpDosTask, int fn, int sig,
struct vm86plus_struct*VM86 )
{
unsigned iofs;
BYTE*inst;
int x;
switch (VM86_TYPE(fn)) {
case VM86_SIGNAL:
printf("Trapped signal %d\n",sig); break;
case VM86_UNKNOWN:
printf("Trapped unhandled GPF\n"); break;
case VM86_INTx:
printf("Trapped INT %02x\n",VM86_ARG(fn)); break;
case VM86_STI:
printf("Trapped STI\n"); break;
case VM86_PICRETURN:
printf("Trapped due to pending PIC request\n"); break;
case VM86_TRAP:
printf("Trapped debug request\n"); break;
default:
printf("Trapped unknown VM86 type %d arg %d\n",VM86_TYPE(fn),VM86_ARG(fn)); break;
}
#define REGS VM86->regs
fprintf(stderr,"AX=%04lX CX=%04lX DX=%04lX BX=%04lX\n",REGS.eax,REGS.ecx,REGS.edx,REGS.ebx);
fprintf(stderr,"SI=%04lX DI=%04lX SP=%04lX BP=%04lX\n",REGS.esi,REGS.edi,REGS.esp,REGS.ebp);
fprintf(stderr,"CS=%04X DS=%04X ES=%04X SS=%04X\n",REGS.cs,REGS.ds,REGS.es,REGS.ss);
fprintf(stderr,"IP=%04lX EFLAGS=%08lX\n",REGS.eip,REGS.eflags);
iofs=((DWORD)REGS.cs<<4)+REGS.eip;
#undef REGS
inst=(BYTE*)lpDosTask->img+iofs;
printf("Opcodes:");
for (x=0; x<8; x++) printf(" %02x",inst[x]);
printf("\n");
}
static int DOSVM_Int( int vect, PCONTEXT context, LPDOSTASK lpDosTask )
{
extern UINT16 DPMI_wrap_seg;
if (vect==0x31) {
if (CS_reg(context)==DPMI_wrap_seg) {
/* exit from real-mode wrapper */
return -1;
}
/* we could probably move some other dodgy stuff here too from dpmi.c */
}
INT_RealModeInterrupt(vect,context);
return 0;
}
static void DOSVM_SimulateInt( int vect, PCONTEXT context, LPDOSTASK lpDosTask )
{
FARPROC16 handler=INT_GetRMHandler(vect);
WORD*stack=(WORD*)(V86BASE(context)+(((DWORD)SS_reg(context))<<4)+SP_reg(context));
*(--stack)=FL_reg(context);
*(--stack)=CS_reg(context);
*(--stack)=IP_reg(context);
SP_reg(context)-=6;
CS_reg(context)=SELECTOROF(handler);
IP_reg(context)=OFFSETOF(handler);
}
#define CV CP(eax,EAX); CP(ecx,ECX); CP(edx,EDX); CP(ebx,EBX); \
CP(esi,ESI); CP(edi,EDI); CP(esp,ESP); CP(ebp,EBP); \
CP(cs,CS); CP(ds,DS); CP(es,ES); \
CP(ss,SS); CP(fs,FS); CP(gs,GS); \
CP(eip,EIP); CP(eflags,EFL)
static int DOSVM_Process( LPDOSTASK lpDosTask, int fn, int sig,
struct vm86plus_struct*VM86 )
{
SIGCONTEXT sigcontext;
CONTEXT context;
int ret=0;
if (VM86_TYPE(fn)==VM86_UNKNOWN) {
/* INSTR_EmulateInstruction needs a SIGCONTEXT, not a CONTEXT... */
#define CP(x,y) y##_sig(&sigcontext) = VM86->regs.x
CV;
#undef CP
if (fnINSTR_EmulateInstruction) ret=fnINSTR_EmulateInstruction(&sigcontext);
#define CP(x,y) VM86->regs.x = y##_sig(&sigcontext)
CV;
#undef CP
if (ret) return 0;
ret=0;
}
#define CP(x,y) y##_reg(&context) = VM86->regs.x
CV;
#undef CP
(void*)V86BASE(&context)=lpDosTask->img;
switch (VM86_TYPE(fn)) {
case VM86_SIGNAL:
TRACE(int,"DOS module caught signal %d\n",sig);
if (sig==SIGALRM) {
DOSVM_SimulateInt(8,&context,lpDosTask);
} else
if (sig==SIGHUP) {
if (ctx_debug_call) ctx_debug_call(SIGTRAP,&context);
} else
if ((sig==SIGILL)||(sig==SIGSEGV)) {
if (ctx_debug_call) ctx_debug_call(SIGILL,&context);
} else {
DOSVM_Dump(lpDosTask,fn,sig,VM86);
ret=-1;
}
break;
case VM86_UNKNOWN: /* unhandled GPF */
DOSVM_Dump(lpDosTask,fn,sig,VM86);
if (ctx_debug_call) ctx_debug_call(SIGSEGV,&context); else ret=-1;
break;
case VM86_INTx:
if (TRACE_ON(relay))
DPRINTF("Call DOS int 0x%02x (EAX=%08lx) ret=%04lx:%04lx\n",VM86_ARG(fn),context.Eax,context.SegCs,context.Eip);
ret=DOSVM_Int(VM86_ARG(fn),&context,lpDosTask);
if (TRACE_ON(relay))
DPRINTF("Ret DOS int 0x%02x (EAX=%08lx) ret=%04lx:%04lx\n",VM86_ARG(fn),context.Eax,context.SegCs,context.Eip);
break;
case VM86_STI:
break;
case VM86_PICRETURN:
printf("Trapped due to pending PIC request\n"); break;
case VM86_TRAP:
if (ctx_debug_call) ctx_debug_call(SIGTRAP,&context);
break;
default:
DOSVM_Dump(lpDosTask,fn,sig,VM86);
ret=-1;
}
#define CP(x,y) VM86->regs.x = y##_reg(&context)
CV;
#undef CP
return ret;
}
int DOSVM_Enter( PCONTEXT context )
{
TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
LPDOSTASK lpDosTask;
struct vm86plus_struct VM86;
int stat,len,sig;
fd_set readfds,exceptfds;
GlobalUnlock16( GetCurrentTask() );
if (!pModule) {
ERR(module,"No task is currently active!\n");
return -1;
}
if (!(lpDosTask=pModule->lpDosTask)) {
/* MZ_CreateProcess or MZ_AllocDPMITask should have been called first */
ERR(module,"dosmod has not been initialized!");
return -1;
}
if (context) {
#define CP(x,y) VM86.regs.x = y##_reg(context)
CV;
#undef CP
} else {
/* initial setup */
/* allocate standard DOS handles */
FILE_InitProcessDosHandles();
/* registers */
memset(&VM86,0,sizeof(VM86));
VM86.regs.cs=lpDosTask->init_cs;
VM86.regs.eip=lpDosTask->init_ip;
VM86.regs.ss=lpDosTask->init_ss;
VM86.regs.esp=lpDosTask->init_sp;
VM86.regs.ds=lpDosTask->psp_seg;
VM86.regs.es=lpDosTask->psp_seg;
/* hmm, what else do we need? */
}
/* main exchange loop */
do {
stat = VM86_ENTER;
errno = 0;
/* transmit VM86 structure to dosmod task */
if (write(lpDosTask->write_pipe,&stat,sizeof(stat))!=sizeof(stat)) {
ERR(module,"dosmod sync lost, errno=%d\n",errno);
return -1;
}
if (write(lpDosTask->write_pipe,&VM86,sizeof(VM86))!=sizeof(VM86)) {
ERR(module,"dosmod sync lost, errno=%d\n",errno);
return -1;
}
/* wait for response, with async events enabled */
FD_ZERO(&readfds);
FD_ZERO(&exceptfds);
SIGNAL_MaskAsyncEvents(FALSE);
do {
FD_SET(lpDosTask->read_pipe,&readfds);
FD_SET(lpDosTask->read_pipe,&exceptfds);
select(lpDosTask->read_pipe+1,&readfds,NULL,&exceptfds,NULL);
} while (!(FD_ISSET(lpDosTask->read_pipe,&readfds)||
FD_ISSET(lpDosTask->read_pipe,&exceptfds)));
SIGNAL_MaskAsyncEvents(TRUE);
/* read response (with async events disabled to avoid some strange problems) */
do {
if ((len=read(lpDosTask->read_pipe,&stat,sizeof(stat)))!=sizeof(stat)) {
if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) {
WARN(module,"rereading dosmod return code due to errno=%d, result=%d\n",errno,len);
continue;
}
ERR(module,"dosmod sync lost reading return code, errno=%d, result=%d\n",errno,len);
return -1;
}
} while (0);
TRACE(module,"dosmod return code=%d\n",stat);
do {
if ((len=read(lpDosTask->read_pipe,&VM86,sizeof(VM86)))!=sizeof(VM86)) {
if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) {
WARN(module,"rereading dosmod VM86 structure due to errno=%d, result=%d\n",errno,len);
continue;
}
ERR(module,"dosmod sync lost reading VM86 structure, errno=%d, result=%d\n",errno,len);
return -1;
}
} while (0);
if ((stat&0xff)==DOSMOD_SIGNAL) {
do {
if ((len=read(lpDosTask->read_pipe,&sig,sizeof(sig)))!=sizeof(sig)) {
if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) {
WARN(module,"rereading dosmod signal due to errno=%d, result=%d\n",errno,len);
continue;
}
ERR(module,"dosmod sync lost reading signal, errno=%d, result=%d\n",errno,len);
return -1;
}
} while (0);
} else sig=0;
/* got response */
} while (DOSVM_Process(lpDosTask,stat,sig,&VM86)>=0);
if (context) {
#define CP(x,y) y##_reg(context) = VM86.regs.x
CV;
#undef CP
}
return 0;
}
void DOSVM_SetTimer( unsigned ticks )
{
TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
int stat=DOSMOD_SET_TIMER;
struct timeval tim;
GlobalUnlock16( GetCurrentTask() );
if (pModule&&pModule->lpDosTask) {
/* the PC clocks ticks at 1193180 Hz */
tim.tv_sec=0;
tim.tv_usec=((unsigned long long)ticks*1000000)/1193180;
/* sanity check */
if (!tim.tv_usec) tim.tv_usec=1;
if (write(pModule->lpDosTask->write_pipe,&stat,sizeof(stat))!=sizeof(stat)) {
ERR(module,"dosmod sync lost, errno=%d\n",errno);
return;
}
if (write(pModule->lpDosTask->write_pipe,&tim,sizeof(tim))!=sizeof(tim)) {
ERR(module,"dosmod sync lost, errno=%d\n",errno);
return;
}
/* there's no return */
}
}
unsigned DOSVM_GetTimer( void )
{
TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
int stat=DOSMOD_GET_TIMER;
struct timeval tim;
GlobalUnlock16( GetCurrentTask() );
if (pModule&&pModule->lpDosTask) {
if (write(pModule->lpDosTask->write_pipe,&stat,sizeof(stat))!=sizeof(stat)) {
ERR(module,"dosmod sync lost, errno=%d\n",errno);
return 0;
}
/* read response */
if (read(pModule->lpDosTask->read_pipe,&tim,sizeof(tim))!=sizeof(tim)) {
ERR(module,"dosmod sync lost, errno=%d\n",errno);
return 0;
}
return ((unsigned long long)tim.tv_usec*1193180)/1000000;
}
return 0;
}
void MZ_Tick( WORD handle )
{
/* find the DOS task that has the right system_timer handle... */
/* should usually be the current, so let's just be lazy... */
TDB *pTask = (TDB*)GlobalLock16( GetCurrentTask() );
NE_MODULE *pModule = pTask ? NE_GetPtr( pTask->hModule ) : NULL;
LPDOSTASK lpDosTask = pModule ? pModule->lpDosTask : NULL;
GlobalUnlock16( GetCurrentTask() );
if (lpDosTask&&(lpDosTask->system_timer==handle)) {
/* BIOS timer tick */
(*((DWORD*)(((BYTE*)(lpDosTask->img))+0x46c)))++;
}
}
#else /* !MZ_SUPPORTED */
int DOSVM_Enter( PCONTEXT context )
{
ERR(module,"DOS realmode not supported on this architecture!\n");
return -1;
}
void MZ_Tick( WORD handle ) {}
void DOSVM_SetTimer( unsigned ticks ) {}
unsigned DOSVM_GetTimer( void ) { return 0; }
#endif