/********************************************************************** * DOSVM_EmulateInterruptRM * * Emulate software interrupt in real mode. * Called from VM86 emulation when intXX opcode is executed. * * Either calls directly builtin handler or pushes interrupt frame to * stack and changes instruction pointer to interrupt handler. * * Returns FALSE if this interrupt was caused by return * from real mode wrapper. */ BOOL WINAPI DOSVM_EmulateInterruptRM( CONTEXT86 *context, BYTE intnum ) { TRACE_(relay)("Call DOS int 0x%02x ret=%04lx:%08lx\n" " eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx\n" " esi=%08lx edi=%08lx ebp=%08lx esp=%08lx \n" " ds=%04lx es=%04lx fs=%04lx gs=%04lx ss=%04lx flags=%08lx\n", intnum, context->SegCs, context->Eip, context->Eax, context->Ebx, context->Ecx, context->Edx, context->Esi, context->Edi, context->Ebp, context->Esp, context->SegDs, context->SegEs, context->SegFs, context->SegGs, context->SegSs, context->EFlags ); /* check for our real-mode hooks */ if (intnum == 0x31) { /* is this exit from real-mode wrapper */ if (context->SegCs == DOSVM_dpmi_segments->wrap_seg) return FALSE; if (DOSVM_CheckWrappers( context )) return TRUE; } /* check if the call is from our fake BIOS interrupt stubs */ if (context->SegCs==0xf000) { /* Restore original flags stored into the stack by the caller. */ WORD *stack = CTX_SEG_OFF_TO_LIN(context, context->SegSs, context->Esp); context->EFlags = (DWORD)MAKELONG( stack[2], HIWORD(context->EFlags) ); if (intnum != context->Eip / DOSVM_STUB_RM) WARN( "interrupt stub has been modified " "(interrupt is %02x, interrupt stub is %02lx)\n", intnum, context->Eip/DOSVM_STUB_RM ); TRACE( "builtin interrupt %02x has been branched to\n", intnum ); DOSVM_CallBuiltinHandler( context, intnum ); /* Real mode stubs use IRET so we must put flags back into stack. */ stack[2] = LOWORD(context->EFlags); } else { DOSVM_HardwareInterruptRM( context, intnum ); } return TRUE; }
/********************************************************************** * DOSVM_HardwareInterruptRM * * Emulate call to interrupt handler in real mode. * * Either calls directly builtin handler or pushes interrupt frame to * stack and changes instruction pointer to interrupt handler. */ void DOSVM_HardwareInterruptRM( CONTEXT86 *context, BYTE intnum ) { FARPROC16 handler = DOSVM_GetRMHandler( intnum ); /* check if the call goes to an unhooked interrupt */ if (SELECTOROF(handler) == 0xf000) { /* if so, call it directly */ TRACE( "builtin interrupt %02x has been invoked " "(through vector %02x)\n", OFFSETOF(handler)/DOSVM_STUB_RM, intnum ); DOSVM_CallBuiltinHandler( context, OFFSETOF(handler)/DOSVM_STUB_RM ); } else { /* the interrupt is hooked, simulate interrupt in DOS space */ WORD flag = LOWORD( context->EFlags ); TRACE( "invoking hooked interrupt %02x at %04x:%04x\n", intnum, SELECTOROF(handler), OFFSETOF(handler) ); /* Copy virtual interrupt flag to pushed interrupt flag. */ if (context->EFlags & VIF_MASK) flag |= IF_MASK; else flag &= ~IF_MASK; PUSH_WORD16( context, flag ); PUSH_WORD16( context, context->SegCs ); PUSH_WORD16( context, LOWORD( context->Eip )); context->SegCs = SELECTOROF( handler ); context->Eip = OFFSETOF( handler ); /* Clear virtual interrupt flag and trap flag. */ context->EFlags &= ~(VIF_MASK | TF_MASK); } }
/********************************************************************** * CallRMInt (WINEDOS.@) */ void WINAPI DOSVM_CallRMInt( CONTEXT86 *context ) { CONTEXT86 realmode_ctx; FARPROC16 rm_int = DOSVM_GetRMHandler( BL_reg(context) ); REALMODECALL *call = CTX_SEG_OFF_TO_LIN( context, context->SegEs, context->Edi ); INT_GetRealModeContext( call, &realmode_ctx ); /* we need to check if a real-mode program has hooked the interrupt */ if (HIWORD(rm_int)!=0xF000) { /* yup, which means we need to switch to real mode... */ realmode_ctx.SegCs = HIWORD(rm_int); realmode_ctx.Eip = LOWORD(rm_int); if (DPMI_CallRMProc( &realmode_ctx, NULL, 0, TRUE)) SET_CFLAG(context); } else { RESET_CFLAG(context); /* use the IP we have instead of BL_reg, in case some apps decide to move interrupts around for whatever reason... */ DOSVM_CallBuiltinHandler( &realmode_ctx, LOWORD(rm_int)/4 ); } INT_SetRealModeContext( call, &realmode_ctx ); }
/********************************************************************** * __wine_call_int_handler (KERNEL.@) */ void __wine_call_int_handler( CONTEXT86 *context, BYTE intnum ) { DOSMEM_InitDosMemory(); DOSVM_CallBuiltinHandler( context, intnum ); }
/********************************************************************** * DPMI_CallRMProc * * This routine does the hard work of calling a real mode procedure. */ int DPMI_CallRMProc( CONTEXT86 *context, LPWORD stack, int args, int iret ) { LPWORD stack16; LPVOID addr = NULL; /* avoid gcc warning */ RMCB *CurrRMCB; int alloc = 0, already = 0; BYTE *code; TRACE("EAX=%08lx EBX=%08lx ECX=%08lx EDX=%08lx\n", context->Eax, context->Ebx, context->Ecx, context->Edx ); TRACE("ESI=%08lx EDI=%08lx ES=%04lx DS=%04lx CS:IP=%04lx:%04x, %d WORD arguments, %s\n", context->Esi, context->Edi, context->SegEs, context->SegDs, context->SegCs, LOWORD(context->Eip), args, iret?"IRET":"FAR" ); callrmproc_again: /* there might be some code that just jumps to RMCBs or the like, in which case following the jumps here might get us to a shortcut */ code = CTX_SEG_OFF_TO_LIN(context, context->SegCs, context->Eip); switch (*code) { case 0xe9: /* JMP NEAR */ context->Eip += 3 + *(WORD *)(code+1); /* yeah, I know these gotos don't look good... */ goto callrmproc_again; case 0xea: /* JMP FAR */ context->Eip = *(WORD *)(code+1); context->SegCs = *(WORD *)(code+3); /* ...but since the label is there anyway... */ goto callrmproc_again; case 0xeb: /* JMP SHORT */ context->Eip += 2 + *(signed char *)(code+1); /* ...because of other gotos below, so... */ goto callrmproc_again; } /* shortcut for chaining to internal interrupt handlers */ if ((context->SegCs == 0xF000) && iret) { DOSVM_CallBuiltinHandler( context, LOWORD(context->Eip)/4 ); return 0; } /* shortcut for RMCBs */ CurrRMCB = FirstRMCB; while (CurrRMCB && (HIWORD(CurrRMCB->address) != context->SegCs)) CurrRMCB = CurrRMCB->next; if (!CurrRMCB && !MZ_Current()) { FIXME("DPMI real-mode call using DOS VM task system, not fully tested!\n"); TRACE("creating VM86 task\n"); MZ_AllocDPMITask(); } if (!already) { if (!context->SegSs) { alloc = 1; /* allocate default stack */ stack16 = addr = DOSMEM_AllocBlock( 64, (UINT16 *)&(context->SegSs) ); context->Esp = 64-2; stack16 += 32-1; if (!addr) { ERR("could not allocate default stack\n"); return 1; } } else { stack16 = CTX_SEG_OFF_TO_LIN(context, context->SegSs, context->Esp); } context->Esp -= (args + (iret?1:0)) * sizeof(WORD); stack16 -= args; if (args) memcpy(stack16, stack, args*sizeof(WORD) ); /* push flags if iret */ if (iret) { stack16--; args++; *stack16 = LOWORD(context->EFlags); } /* push return address (return to interrupt wrapper) */ *(--stack16) = DOSVM_dpmi_segments->wrap_seg; *(--stack16) = 0; /* adjust stack */ context->Esp -= 2*sizeof(WORD); already = 1; } if (CurrRMCB) { /* RMCB call, invoke protected-mode handler directly */ DPMI_CallRMCBProc(context, CurrRMCB, dpmi_flag); /* check if we returned to where we thought we would */ if ((context->SegCs != DOSVM_dpmi_segments->wrap_seg) || (LOWORD(context->Eip) != 0)) { /* we need to continue at different address in real-mode space, so we need to set it all up for real mode again */ goto callrmproc_again; } } else { TRACE("entering real mode...\n"); DOSVM_Enter( context ); TRACE("returned from real-mode call\n"); } if (alloc) DOSMEM_FreeBlock( addr ); return 0; }