Skip to content
Snippets Groups Projects
dosvm.c 7.7 KiB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
/*
 * DOS Virtual Machine
 *
 * Copyright 1998 Ove Kven
Alexandre Julliard's avatar
Alexandre Julliard committed
 *
 * This code hasn't been completely cleaned up yet.
Alexandre Julliard's avatar
Alexandre Julliard committed
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
#include <sys/types.h>
#include <sys/stat.h>
#include "windows.h"
#include "winbase.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
#include "winnt.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
#include "msdos.h"
#include "miscemu.h"
#include "debug.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
#include "module.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
#include "task.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
#include "ldt.h"
#include "dosexe.h"

void (*ctx_debug_call)(int sig,CONTEXT*ctx)=NULL;
BOOL32 (*instr_emu_call)(SIGCONTEXT*ctx)=NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
#ifdef MZ_SUPPORTED

#include <sys/mman.h>
#include <sys/vm86.h>

static void DOSVM_Dump( LPDOSTASK lpDosTask, int fn,
                        struct vm86plus_struct*VM86 )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
 unsigned iofs;
 BYTE*inst;
 int x;

Alexandre Julliard's avatar
Alexandre Julliard committed
  case VM86_SIGNAL:
   printf("Trapped signal\n"); break;
  case VM86_UNKNOWN:
   printf("Trapped unhandled GPF\n"); break;
  case VM86_INTx:
   printf("Trapped INT %02x\n",VM86_ARG(fn)); break;
Alexandre Julliard's avatar
Alexandre Julliard committed
  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;
Alexandre Julliard's avatar
Alexandre Julliard committed
 }
 fprintf(stderr,"AX=%04lX CX=%04lX DX=%04lX BX=%04lX\n",REGS.eax,REGS.ecx,REGS.edx,REGS.ebx);
Alexandre Julliard's avatar
Alexandre Julliard committed
 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);
Alexandre Julliard's avatar
Alexandre Julliard committed

 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 )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
 if (vect==0x31) {
  if (CS_reg(context)==lpDosTask->dpmi_sel) {
   if (IP_reg(context)>=lpDosTask->wrap_ofs) {
    /* exit from real-mode wrapper */
    return -1;
   }
  }
  /* we could probably move some other dodgy stuff here too from dpmi.c */
 }
Alexandre Julliard's avatar
Alexandre Julliard committed
 INT_RealModeInterrupt(vect,context);
Alexandre Julliard's avatar
Alexandre Julliard committed
 return 0;
}

#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)
Alexandre Julliard's avatar
Alexandre Julliard committed

static int DOSVM_Process( LPDOSTASK lpDosTask, int fn,
                          struct vm86plus_struct*VM86 )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
Alexandre Julliard's avatar
Alexandre Julliard committed
 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 (instr_emu_call) ret=instr_emu_call(&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
Alexandre Julliard's avatar
Alexandre Julliard committed
 CV;
#undef CP
Alexandre Julliard's avatar
Alexandre Julliard committed
 (void*)V86BASE(&context)=lpDosTask->img;

Alexandre Julliard's avatar
Alexandre Julliard committed
  case VM86_SIGNAL:
   DOSVM_Dump(lpDosTask,fn,VM86);
Alexandre Julliard's avatar
Alexandre Julliard committed
   ret=-1; break;
Alexandre Julliard's avatar
Alexandre Julliard committed
  case VM86_UNKNOWN: /* unhandled GPF */
   DOSVM_Dump(lpDosTask,fn,VM86);
   if (ctx_debug_call) ctx_debug_call(SIGSEGV,&context); else ret=-1;
Alexandre Julliard's avatar
Alexandre Julliard committed
   break;
  case VM86_INTx:
   TRACE(int,"DOS EXE calls INT %02x with AX=%04lx\n",VM86_ARG(fn),context.Eax);
   ret=DOSVM_Int(VM86_ARG(fn),&context,lpDosTask); break;
Alexandre Julliard's avatar
Alexandre Julliard committed
  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);
Alexandre Julliard's avatar
Alexandre Julliard committed
   break;
Alexandre Julliard's avatar
Alexandre Julliard committed
  default:
   DOSVM_Dump(lpDosTask,fn,VM86);
#define CP(x,y) VM86->regs.x = y##_reg(&context)
Alexandre Julliard's avatar
Alexandre Julliard committed
 CV;
#undef CP
Alexandre Julliard's avatar
Alexandre Julliard committed
 return ret;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
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;
 fd_set readfds,exceptfds;
Alexandre Julliard's avatar
Alexandre Julliard committed

 GlobalUnlock16( GetCurrentTask() );
 if (!pModule) {
  ERR(module,"No task is currently active!\n");
  return -1;
 }
 if (!pModule->lpDosTask) {
  /* no VM86 (dosmod) task is currently running, start one */
  if ((lpDosTask = calloc(1, sizeof(DOSTASK))) == NULL)
    return 0;
  lpDosTask->hModule=pModule->self;
Alexandre Julliard's avatar
Alexandre Julliard committed
  stat=MZ_InitMemory(lpDosTask,pModule);
  if (stat>=32) stat=MZ_InitTask(lpDosTask);
Alexandre Julliard's avatar
Alexandre Julliard committed
  if (stat<32) {
   free(lpDosTask);
   return -1;
  }
  pModule->lpDosTask = lpDosTask;
  pModule->dos_image = lpDosTask->img;
  /* Note: we're leaving it running after this, in case we need it again,
     as this minimizes the overhead of starting it up every time...
     it will be killed automatically when the current task terminates */
 } else lpDosTask=pModule->lpDosTask;

 if (context) {
#define CP(x,y) VM86.regs.x = y##_reg(context)
Alexandre Julliard's avatar
Alexandre Julliard committed
  CV;
#undef CP
 } else {
/* initial setup */
  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? */
  /* 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);
  }
  if (write(lpDosTask->write_pipe,&VM86,sizeof(VM86))!=sizeof(VM86)) {
   ERR(module,"dosmod sync lost, errno=%d\n",errno);
  }
  /* 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) */
   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);
  TRACE(module,"dosmod return code=%d\n",stat);
   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);
  /* got response */
 } while (DOSVM_Process(lpDosTask,stat,&VM86)>=0);
Alexandre Julliard's avatar
Alexandre Julliard committed

 if (context) {
#define CP(x,y) y##_reg(context) = VM86.regs.x
Alexandre Julliard's avatar
Alexandre Julliard committed
  CV;
#undef CP
 }
 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)))++;
 }
}

Alexandre Julliard's avatar
Alexandre Julliard committed
#else /* !MZ_SUPPORTED */
Alexandre Julliard's avatar
Alexandre Julliard committed

int DOSVM_Enter( PCONTEXT context )
{
 ERR(module,"DOS realmode not supported on this architecture!\n");
 return -1;
}

void MZ_Tick( WORD handle ) {}

Alexandre Julliard's avatar
Alexandre Julliard committed
#endif