1082 lines
33 KiB
C
1082 lines
33 KiB
C
/*
|
|
* Interrupt emulation
|
|
*
|
|
* Copyright 2002 Jukka Heinonen
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "wine/winbase16.h"
|
|
#include "kernel16_private.h"
|
|
#include "dosexe.h"
|
|
#include "winternl.h"
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(int);
|
|
WINE_DECLARE_DEBUG_CHANNEL(relay);
|
|
|
|
#define BCD_TO_BIN(x) ((x&15) + (x>>4)*10)
|
|
#define BIN_TO_BCD(x) ((x%10) + ((x/10)<<4))
|
|
|
|
static void WINAPI DOSVM_Int11Handler(CONTEXT*);
|
|
static void WINAPI DOSVM_Int12Handler(CONTEXT*);
|
|
static void WINAPI DOSVM_Int17Handler(CONTEXT*);
|
|
static void WINAPI DOSVM_Int19Handler(CONTEXT*);
|
|
static void WINAPI DOSVM_Int1aHandler(CONTEXT*);
|
|
static void WINAPI DOSVM_Int20Handler(CONTEXT*);
|
|
static void WINAPI DOSVM_Int29Handler(CONTEXT*);
|
|
static void WINAPI DOSVM_Int2aHandler(CONTEXT*);
|
|
static void WINAPI DOSVM_Int41Handler(CONTEXT*);
|
|
static void WINAPI DOSVM_Int4bHandler(CONTEXT*);
|
|
static void WINAPI DOSVM_Int5cHandler(CONTEXT*);
|
|
static void WINAPI DOSVM_DefaultHandler(CONTEXT*);
|
|
|
|
static FARPROC16 DOSVM_Vectors16[256];
|
|
static FARPROC48 DOSVM_Vectors48[256];
|
|
static const INTPROC DOSVM_VectorsBuiltin[] =
|
|
{
|
|
/* 00 */ 0, 0, 0, 0,
|
|
/* 04 */ 0, 0, 0, 0,
|
|
/* 08 */ DOSVM_Int08Handler, DOSVM_Int09Handler, 0, 0,
|
|
/* 0C */ 0, 0, 0, 0,
|
|
/* 10 */ DOSVM_Int10Handler, DOSVM_Int11Handler, DOSVM_Int12Handler, DOSVM_Int13Handler,
|
|
/* 14 */ 0, DOSVM_Int15Handler, DOSVM_Int16Handler, DOSVM_Int17Handler,
|
|
/* 18 */ 0, DOSVM_Int19Handler, DOSVM_Int1aHandler, 0,
|
|
/* 1C */ 0, 0, 0, 0,
|
|
/* 20 */ DOSVM_Int20Handler, DOSVM_Int21Handler, 0, 0,
|
|
/* 24 */ 0, DOSVM_Int25Handler, DOSVM_Int26Handler, 0,
|
|
/* 28 */ 0, DOSVM_Int29Handler, DOSVM_Int2aHandler, 0,
|
|
/* 2C */ 0, 0, 0, DOSVM_Int2fHandler,
|
|
/* 30 */ 0, DOSVM_Int31Handler, 0, DOSVM_Int33Handler,
|
|
/* 34 */ DOSVM_Int34Handler, DOSVM_Int35Handler, DOSVM_Int36Handler, DOSVM_Int37Handler,
|
|
/* 38 */ DOSVM_Int38Handler, DOSVM_Int39Handler, DOSVM_Int3aHandler, DOSVM_Int3bHandler,
|
|
/* 3C */ DOSVM_Int3cHandler, DOSVM_Int3dHandler, DOSVM_Int3eHandler, 0,
|
|
/* 40 */ 0, DOSVM_Int41Handler, 0, 0,
|
|
/* 44 */ 0, 0, 0, 0,
|
|
/* 48 */ 0, 0, 0, DOSVM_Int4bHandler,
|
|
/* 4C */ 0, 0, 0, 0,
|
|
/* 50 */ 0, 0, 0, 0,
|
|
/* 54 */ 0, 0, 0, 0,
|
|
/* 58 */ 0, 0, 0, 0,
|
|
/* 5C */ DOSVM_Int5cHandler, 0, 0, 0,
|
|
/* 60 */ 0, 0, 0, 0,
|
|
/* 64 */ 0, 0, 0, DOSVM_Int67Handler,
|
|
/* 68 */ DOSVM_DefaultHandler
|
|
};
|
|
|
|
|
|
/*
|
|
* Sizes of real mode and protected mode interrupt stubs.
|
|
*/
|
|
#define DOSVM_STUB_RM 4
|
|
#define DOSVM_STUB_PM16 5
|
|
#define DOSVM_STUB_PM48 6
|
|
|
|
|
|
/**********************************************************************
|
|
* DOSVM_GetRMVector
|
|
*
|
|
* Return pointer to real mode interrupt vector. These are not at fixed
|
|
* location because those Win16 programs that do not use any real mode
|
|
* code have protected NULL pointer catching block at low linear memory
|
|
* and interrupt vectors have been moved to another location.
|
|
*/
|
|
static FARPROC16* DOSVM_GetRMVector( BYTE intnum )
|
|
{
|
|
LDT_ENTRY entry;
|
|
FARPROC16 proc;
|
|
|
|
proc = GetProcAddress16( GetModuleHandle16( "KERNEL" ),
|
|
(LPCSTR)(ULONG_PTR)183 );
|
|
wine_ldt_get_entry( LOWORD(proc), &entry );
|
|
|
|
return (FARPROC16*)wine_ldt_get_base( &entry ) + intnum;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* DOSVM_IsIRQ
|
|
*
|
|
* Return TRUE if interrupt is an IRQ.
|
|
*/
|
|
static BOOL DOSVM_IsIRQ( BYTE intnum )
|
|
{
|
|
if (intnum >= 0x08 && intnum <= 0x0f)
|
|
return TRUE;
|
|
|
|
if (intnum >= 0x70 && intnum <= 0x77)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* DOSVM_DefaultHandler
|
|
*
|
|
* Default interrupt handler. This will be used to emulate all
|
|
* interrupts that don't have their own interrupt handler.
|
|
*/
|
|
static void WINAPI DOSVM_DefaultHandler( CONTEXT *context )
|
|
{
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* DOSVM_GetBuiltinHandler
|
|
*
|
|
* Return Wine interrupt handler procedure for a given interrupt.
|
|
*/
|
|
static INTPROC DOSVM_GetBuiltinHandler( BYTE intnum )
|
|
{
|
|
if (intnum < sizeof(DOSVM_VectorsBuiltin)/sizeof(INTPROC)) {
|
|
INTPROC proc = DOSVM_VectorsBuiltin[intnum];
|
|
if (proc)
|
|
return proc;
|
|
}
|
|
|
|
WARN("int%x not implemented, returning dummy handler\n", intnum );
|
|
|
|
if (DOSVM_IsIRQ(intnum))
|
|
return DOSVM_AcknowledgeIRQ;
|
|
|
|
return DOSVM_DefaultHandler;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* DOSVM_IntProcRelay
|
|
*
|
|
* Simple DOSRELAY that interprets its argument as INTPROC and calls it.
|
|
*/
|
|
static void DOSVM_IntProcRelay( CONTEXT *context, LPVOID data )
|
|
{
|
|
INTPROC proc = (INTPROC)data;
|
|
proc(context);
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* DOSVM_PrepareIRQ
|
|
*
|
|
*/
|
|
static void DOSVM_PrepareIRQ( CONTEXT *context, BOOL isbuiltin )
|
|
{
|
|
/* Disable virtual interrupts. */
|
|
get_vm86_teb_info()->dpmi_vif = 0;
|
|
|
|
if (!isbuiltin)
|
|
{
|
|
DWORD *stack = CTX_SEG_OFF_TO_LIN(context,
|
|
context->SegSs,
|
|
context->Esp);
|
|
|
|
/* Push return address to stack. */
|
|
*(--stack) = context->SegCs;
|
|
*(--stack) = context->Eip;
|
|
context->Esp += -8;
|
|
|
|
/* Jump to enable interrupts stub. */
|
|
context->SegCs = DOSVM_dpmi_segments->relay_code_sel;
|
|
context->Eip = 5;
|
|
}
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* DOSVM_PushFlags
|
|
*
|
|
* This routine is used to make default int25 and int26 handlers leave the
|
|
* original eflags into stack. In order to do this, stack is manipulated
|
|
* so that it actually contains two copies of eflags, one of which is
|
|
* popped during return from interrupt handler.
|
|
*/
|
|
static void DOSVM_PushFlags( CONTEXT *context, BOOL islong, BOOL isstub )
|
|
{
|
|
if (islong)
|
|
{
|
|
DWORD *stack = CTX_SEG_OFF_TO_LIN(context,
|
|
context->SegSs,
|
|
context->Esp);
|
|
context->Esp += -4; /* One item will be added to stack. */
|
|
|
|
if (isstub)
|
|
{
|
|
DWORD ip = stack[0];
|
|
DWORD cs = stack[1];
|
|
stack += 2; /* Pop ip and cs. */
|
|
*(--stack) = context->EFlags;
|
|
*(--stack) = cs;
|
|
*(--stack) = ip;
|
|
}
|
|
else
|
|
*(--stack) = context->EFlags;
|
|
}
|
|
else
|
|
{
|
|
WORD *stack = CTX_SEG_OFF_TO_LIN(context,
|
|
context->SegSs,
|
|
context->Esp);
|
|
ADD_LOWORD( context->Esp, -2 ); /* One item will be added to stack. */
|
|
|
|
if (isstub)
|
|
{
|
|
WORD ip = stack[0];
|
|
WORD cs = stack[1];
|
|
stack += 2; /* Pop ip and cs. */
|
|
*(--stack) = LOWORD(context->EFlags);
|
|
*(--stack) = cs;
|
|
*(--stack) = ip;
|
|
}
|
|
else
|
|
*(--stack) = LOWORD(context->EFlags);
|
|
}
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* DOSVM_EmulateInterruptPM
|
|
*
|
|
* Emulate software interrupt in 16-bit or 32-bit protected mode.
|
|
* Called from signal handler when intXX opcode is executed.
|
|
*
|
|
* Pushes interrupt frame to stack and changes instruction
|
|
* pointer to interrupt handler.
|
|
*/
|
|
BOOL DOSVM_EmulateInterruptPM( CONTEXT *context, BYTE intnum )
|
|
{
|
|
TRACE_(relay)("Call DOS int 0x%02x ret=%04x:%08x\n"
|
|
" eax=%08x ebx=%08x ecx=%08x edx=%08x\n"
|
|
" esi=%08x edi=%08x ebp=%08x esp=%08x\n"
|
|
" ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x\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 );
|
|
|
|
DOSMEM_InitDosMemory();
|
|
|
|
if (context->SegCs == DOSVM_dpmi_segments->dpmi_sel)
|
|
{
|
|
DOSVM_BuildCallFrame( context,
|
|
DOSVM_IntProcRelay,
|
|
DOSVM_RawModeSwitchHandler );
|
|
}
|
|
else if (context->SegCs == DOSVM_dpmi_segments->relay_code_sel)
|
|
{
|
|
/*
|
|
* This must not be called using DOSVM_BuildCallFrame.
|
|
*/
|
|
DOSVM_RelayHandler( context );
|
|
}
|
|
else if (context->SegCs == DOSVM_dpmi_segments->int48_sel)
|
|
{
|
|
/* Restore original flags stored into the stack by the caller. */
|
|
DWORD *stack = CTX_SEG_OFF_TO_LIN(context,
|
|
context->SegSs, context->Esp);
|
|
context->EFlags = stack[2];
|
|
|
|
if (intnum != context->Eip / DOSVM_STUB_PM48)
|
|
WARN( "interrupt stub has been modified "
|
|
"(interrupt is %02x, interrupt stub is %02x)\n",
|
|
intnum, context->Eip/DOSVM_STUB_PM48 );
|
|
|
|
TRACE( "builtin interrupt %02x has been branched to\n", intnum );
|
|
|
|
if (intnum == 0x25 || intnum == 0x26)
|
|
DOSVM_PushFlags( context, TRUE, TRUE );
|
|
|
|
DOSVM_BuildCallFrame( context,
|
|
DOSVM_IntProcRelay,
|
|
DOSVM_GetBuiltinHandler(intnum) );
|
|
}
|
|
else if (context->SegCs == DOSVM_dpmi_segments->int16_sel)
|
|
{
|
|
/* 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_PM16)
|
|
WARN( "interrupt stub has been modified "
|
|
"(interrupt is %02x, interrupt stub is %02x)\n",
|
|
intnum, context->Eip/DOSVM_STUB_PM16 );
|
|
|
|
TRACE( "builtin interrupt %02x has been branched to\n", intnum );
|
|
|
|
if (intnum == 0x25 || intnum == 0x26)
|
|
DOSVM_PushFlags( context, FALSE, TRUE );
|
|
|
|
DOSVM_BuildCallFrame( context,
|
|
DOSVM_IntProcRelay,
|
|
DOSVM_GetBuiltinHandler(intnum) );
|
|
}
|
|
else if (wine_ldt_is_system(context->SegCs))
|
|
{
|
|
INTPROC proc;
|
|
if (intnum >= sizeof(DOSVM_VectorsBuiltin)/sizeof(INTPROC)) return FALSE;
|
|
if (!(proc = DOSVM_VectorsBuiltin[intnum])) return FALSE;
|
|
proc( context );
|
|
}
|
|
else
|
|
{
|
|
DOSVM_HardwareInterruptPM( context, intnum );
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* DOSVM_HardwareInterruptPM
|
|
*
|
|
* Emulate call to interrupt handler in 16-bit or 32-bit protected mode.
|
|
*
|
|
* Pushes interrupt frame to stack and changes instruction
|
|
* pointer to interrupt handler.
|
|
*/
|
|
void DOSVM_HardwareInterruptPM( CONTEXT *context, BYTE intnum )
|
|
{
|
|
if(DOSVM_IsDos32())
|
|
{
|
|
FARPROC48 addr = DOSVM_GetPMHandler48( intnum );
|
|
|
|
if (addr.selector == DOSVM_dpmi_segments->int48_sel)
|
|
{
|
|
TRACE( "builtin interrupt %02x has been invoked "
|
|
"(through vector %02x)\n",
|
|
addr.offset / DOSVM_STUB_PM48, intnum );
|
|
|
|
if (intnum == 0x25 || intnum == 0x26)
|
|
DOSVM_PushFlags( context, TRUE, FALSE );
|
|
else if (DOSVM_IsIRQ(intnum))
|
|
DOSVM_PrepareIRQ( context, TRUE );
|
|
|
|
DOSVM_BuildCallFrame( context,
|
|
DOSVM_IntProcRelay,
|
|
DOSVM_GetBuiltinHandler(
|
|
addr.offset/DOSVM_STUB_PM48 ) );
|
|
}
|
|
else
|
|
{
|
|
DWORD *stack;
|
|
|
|
TRACE( "invoking hooked interrupt %02x at %04x:%08x\n",
|
|
intnum, addr.selector, addr.offset );
|
|
|
|
if (DOSVM_IsIRQ(intnum))
|
|
DOSVM_PrepareIRQ( context, FALSE );
|
|
|
|
/* Push the flags and return address on the stack */
|
|
stack = CTX_SEG_OFF_TO_LIN(context, context->SegSs, context->Esp);
|
|
*(--stack) = context->EFlags;
|
|
*(--stack) = context->SegCs;
|
|
*(--stack) = context->Eip;
|
|
context->Esp += -12;
|
|
|
|
/* Jump to the interrupt handler */
|
|
context->SegCs = addr.selector;
|
|
context->Eip = addr.offset;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FARPROC16 addr = DOSVM_GetPMHandler16( intnum );
|
|
|
|
if (SELECTOROF(addr) == DOSVM_dpmi_segments->int16_sel)
|
|
{
|
|
TRACE( "builtin interrupt %02x has been invoked "
|
|
"(through vector %02x)\n",
|
|
OFFSETOF(addr)/DOSVM_STUB_PM16, intnum );
|
|
|
|
if (intnum == 0x25 || intnum == 0x26)
|
|
DOSVM_PushFlags( context, FALSE, FALSE );
|
|
else if (DOSVM_IsIRQ(intnum))
|
|
DOSVM_PrepareIRQ( context, TRUE );
|
|
|
|
DOSVM_BuildCallFrame( context,
|
|
DOSVM_IntProcRelay,
|
|
DOSVM_GetBuiltinHandler(
|
|
OFFSETOF(addr)/DOSVM_STUB_PM16 ) );
|
|
}
|
|
else
|
|
{
|
|
TRACE( "invoking hooked interrupt %02x at %04x:%04x\n",
|
|
intnum, SELECTOROF(addr), OFFSETOF(addr) );
|
|
|
|
if (DOSVM_IsIRQ(intnum))
|
|
DOSVM_PrepareIRQ( context, FALSE );
|
|
|
|
/* Push the flags and return address on the stack */
|
|
PUSH_WORD16( context, LOWORD(context->EFlags) );
|
|
PUSH_WORD16( context, context->SegCs );
|
|
PUSH_WORD16( context, LOWORD(context->Eip) );
|
|
|
|
/* Jump to the interrupt handler */
|
|
context->SegCs = HIWORD(addr);
|
|
context->Eip = LOWORD(addr);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* 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 DOSVM_EmulateInterruptRM( CONTEXT *context, BYTE intnum )
|
|
{
|
|
TRACE_(relay)("Call DOS int 0x%02x ret=%04x:%08x\n"
|
|
" eax=%08x ebx=%08x ecx=%08x edx=%08x\n"
|
|
" esi=%08x edi=%08x ebp=%08x esp=%08x\n"
|
|
" ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x\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 %02x)\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( CONTEXT *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);
|
|
}
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* DOSVM_GetRMHandler
|
|
*
|
|
* Return the real mode interrupt vector for a given interrupt.
|
|
*/
|
|
FARPROC16 DOSVM_GetRMHandler( BYTE intnum )
|
|
{
|
|
return *DOSVM_GetRMVector( intnum );
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* DOSVM_SetRMHandler
|
|
*
|
|
* Set the real mode interrupt handler for a given interrupt.
|
|
*/
|
|
void DOSVM_SetRMHandler( BYTE intnum, FARPROC16 handler )
|
|
{
|
|
TRACE("Set real mode interrupt vector %02x <- %04x:%04x\n",
|
|
intnum, HIWORD(handler), LOWORD(handler) );
|
|
*DOSVM_GetRMVector( intnum ) = handler;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* DOSVM_GetPMHandler16
|
|
*
|
|
* Return the protected mode interrupt vector for a given interrupt.
|
|
*/
|
|
FARPROC16 DOSVM_GetPMHandler16( BYTE intnum )
|
|
{
|
|
TDB *pTask;
|
|
FARPROC16 proc = 0;
|
|
|
|
pTask = GlobalLock16(GetCurrentTask());
|
|
if (pTask)
|
|
{
|
|
switch( intnum )
|
|
{
|
|
case 0x00:
|
|
proc = pTask->int0;
|
|
break;
|
|
case 0x02:
|
|
proc = pTask->int2;
|
|
break;
|
|
case 0x04:
|
|
proc = pTask->int4;
|
|
break;
|
|
case 0x06:
|
|
proc = pTask->int6;
|
|
break;
|
|
case 0x07:
|
|
proc = pTask->int7;
|
|
break;
|
|
case 0x3e:
|
|
proc = pTask->int3e;
|
|
break;
|
|
case 0x75:
|
|
proc = pTask->int75;
|
|
break;
|
|
}
|
|
if( proc )
|
|
return proc;
|
|
}
|
|
if (!DOSVM_Vectors16[intnum])
|
|
{
|
|
proc = (FARPROC16)MAKESEGPTR( DOSVM_dpmi_segments->int16_sel,
|
|
DOSVM_STUB_PM16 * intnum );
|
|
DOSVM_Vectors16[intnum] = proc;
|
|
}
|
|
return DOSVM_Vectors16[intnum];
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* DOSVM_SetPMHandler16
|
|
*
|
|
* Set the protected mode interrupt handler for a given interrupt.
|
|
*/
|
|
void DOSVM_SetPMHandler16( BYTE intnum, FARPROC16 handler )
|
|
{
|
|
TDB *pTask;
|
|
|
|
TRACE("Set protected mode interrupt vector %02x <- %04x:%04x\n",
|
|
intnum, HIWORD(handler), LOWORD(handler) );
|
|
|
|
pTask = GlobalLock16(GetCurrentTask());
|
|
if (!pTask)
|
|
return;
|
|
switch( intnum )
|
|
{
|
|
case 0x00:
|
|
pTask->int0 = handler;
|
|
break;
|
|
case 0x02:
|
|
pTask->int2 = handler;
|
|
break;
|
|
case 0x04:
|
|
pTask->int4 = handler;
|
|
break;
|
|
case 0x06:
|
|
pTask->int6 = handler;
|
|
break;
|
|
case 0x07:
|
|
pTask->int7 = handler;
|
|
break;
|
|
case 0x3e:
|
|
pTask->int3e = handler;
|
|
break;
|
|
case 0x75:
|
|
pTask->int75 = handler;
|
|
break;
|
|
default:
|
|
DOSVM_Vectors16[intnum] = handler;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* DOSVM_GetPMHandler48
|
|
*
|
|
* Return the protected mode interrupt vector for a given interrupt.
|
|
* Used to get 48-bit pointer for 32-bit interrupt handlers in DPMI32.
|
|
*/
|
|
FARPROC48 DOSVM_GetPMHandler48( BYTE intnum )
|
|
{
|
|
if (!DOSVM_Vectors48[intnum].selector)
|
|
{
|
|
DOSVM_Vectors48[intnum].selector = DOSVM_dpmi_segments->int48_sel;
|
|
DOSVM_Vectors48[intnum].offset = DOSVM_STUB_PM48 * intnum;
|
|
}
|
|
return DOSVM_Vectors48[intnum];
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* DOSVM_SetPMHandler48
|
|
*
|
|
* Set the protected mode interrupt handler for a given interrupt.
|
|
* Used to set 48-bit pointer for 32-bit interrupt handlers in DPMI32.
|
|
*/
|
|
void DOSVM_SetPMHandler48( BYTE intnum, FARPROC48 handler )
|
|
{
|
|
TRACE("Set 32-bit protected mode interrupt vector %02x <- %04x:%08x\n",
|
|
intnum, handler.selector, handler.offset );
|
|
DOSVM_Vectors48[intnum] = handler;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* DOSVM_CallBuiltinHandler
|
|
*
|
|
* Execute Wine interrupt handler procedure.
|
|
*/
|
|
void DOSVM_CallBuiltinHandler( CONTEXT *context, BYTE intnum )
|
|
{
|
|
/*
|
|
* FIXME: Make all builtin interrupt calls go via this routine.
|
|
* FIXME: Check for PM->RM interrupt reflection.
|
|
* FIXME: Check for RM->PM interrupt reflection.
|
|
*/
|
|
|
|
INTPROC proc = DOSVM_GetBuiltinHandler( intnum );
|
|
proc( context );
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* __wine_call_int_handler (KERNEL.@)
|
|
*/
|
|
void __wine_call_int_handler( CONTEXT *context, BYTE intnum )
|
|
{
|
|
DOSMEM_InitDosMemory();
|
|
DOSVM_CallBuiltinHandler( context, intnum );
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* DOSVM_Int11Handler
|
|
*
|
|
* Handler for int 11h (get equipment list).
|
|
*
|
|
*
|
|
* Borrowed from Ralph Brown's interrupt lists:
|
|
*
|
|
* bits 15-14: number of parallel devices
|
|
* bit 13: [Conv] Internal modem
|
|
* bit 12: reserved
|
|
* bits 11- 9: number of serial devices
|
|
* bit 8: reserved
|
|
* bits 7- 6: number of diskette drives minus one
|
|
* bits 5- 4: Initial video mode:
|
|
* 00b = EGA,VGA,PGA
|
|
* 01b = 40 x 25 color
|
|
* 10b = 80 x 25 color
|
|
* 11b = 80 x 25 mono
|
|
* bit 3: reserved
|
|
* bit 2: [PS] =1 if pointing device
|
|
* [non-PS] reserved
|
|
* bit 1: =1 if math co-processor
|
|
* bit 0: =1 if diskette available for boot
|
|
*
|
|
*
|
|
* Currently the only of these bits correctly set are:
|
|
*
|
|
* bits 15-14 } Added by William Owen Smith,
|
|
* bits 11-9 } wos@dcs.warwick.ac.uk
|
|
* bits 7-6
|
|
* bit 2 (always set) ( bit 2 = 4 )
|
|
* bit 1 } Robert 'Admiral' Coeyman
|
|
* All *nix systems either have a math processor or
|
|
* emulate one.
|
|
*/
|
|
static void WINAPI DOSVM_Int11Handler( CONTEXT *context )
|
|
{
|
|
int diskdrives = 0;
|
|
int parallelports = 0;
|
|
int serialports = 0;
|
|
int x;
|
|
|
|
if (GetDriveTypeA("A:\\") == DRIVE_REMOVABLE) diskdrives++;
|
|
if (GetDriveTypeA("B:\\") == DRIVE_REMOVABLE) diskdrives++;
|
|
if (diskdrives) diskdrives--;
|
|
|
|
for (x=0; x < 9; x++)
|
|
{
|
|
HANDLE handle;
|
|
char file[10];
|
|
|
|
/* serial port name */
|
|
sprintf( file, "\\\\.\\COM%d", x+1 );
|
|
handle = CreateFileA( file, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
|
|
if (handle != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle( handle );
|
|
serialports++;
|
|
}
|
|
|
|
sprintf( file, "\\\\.\\LPT%d", x+1 );
|
|
handle = CreateFileA( file, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
|
|
if (handle != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle( handle );
|
|
parallelports++;
|
|
}
|
|
}
|
|
|
|
if (serialports > 7) /* 3 bits -- maximum value = 7 */
|
|
serialports = 7;
|
|
|
|
if (parallelports > 3) /* 2 bits -- maximum value = 3 */
|
|
parallelports = 3;
|
|
|
|
SET_AX( context,
|
|
(diskdrives << 6) | (serialports << 9) | (parallelports << 14) | 0x06 );
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* DOSVM_Int12Handler
|
|
*
|
|
* Handler for int 12h (get memory size).
|
|
*/
|
|
static void WINAPI DOSVM_Int12Handler( CONTEXT *context )
|
|
{
|
|
SET_AX( context, 640 );
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* DOSVM_Int17Handler
|
|
*
|
|
* Handler for int 17h (printer - output character).
|
|
*/
|
|
static void WINAPI DOSVM_Int17Handler( CONTEXT *context )
|
|
{
|
|
switch( AH_reg(context) )
|
|
{
|
|
case 0x00:/* Send character*/
|
|
FIXME("Send character not supported yet\n");
|
|
SET_AH( context, 0x00 );/*Timeout*/
|
|
break;
|
|
case 0x01: /* PRINTER - INITIALIZE */
|
|
FIXME("Initialize Printer - Not Supported\n");
|
|
SET_AH( context, 0x30 ); /* selected | out of paper */
|
|
break;
|
|
case 0x02: /* PRINTER - GET STATUS */
|
|
FIXME("Get Printer Status - Not Supported\n");
|
|
break;
|
|
default:
|
|
SET_AH( context, 0 ); /* time out */
|
|
INT_BARF( context, 0x17 );
|
|
}
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* DOSVM_Int19Handler
|
|
*
|
|
* Handler for int 19h (Reboot).
|
|
*/
|
|
static void WINAPI DOSVM_Int19Handler( CONTEXT *context )
|
|
{
|
|
TRACE( "Attempted Reboot\n" );
|
|
ExitProcess(0);
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* DOSVM_Int1aHandler
|
|
*
|
|
* Handler for int 1ah.
|
|
*/
|
|
static void WINAPI DOSVM_Int1aHandler( CONTEXT *context )
|
|
{
|
|
switch(AH_reg(context))
|
|
{
|
|
case 0x00: /* GET SYSTEM TIME */
|
|
{
|
|
BIOSDATA *data = DOSVM_BiosData();
|
|
SET_CX( context, HIWORD(data->Ticks) );
|
|
SET_DX( context, LOWORD(data->Ticks) );
|
|
SET_AL( context, 0 ); /* FIXME: midnight flag is unsupported */
|
|
TRACE( "GET SYSTEM TIME - ticks=%d\n", data->Ticks );
|
|
}
|
|
break;
|
|
|
|
case 0x01: /* SET SYSTEM TIME */
|
|
FIXME( "SET SYSTEM TIME - not allowed\n" );
|
|
break;
|
|
|
|
case 0x02: /* GET REAL-TIME CLOCK TIME */
|
|
TRACE( "GET REAL-TIME CLOCK TIME\n" );
|
|
{
|
|
SYSTEMTIME systime;
|
|
GetLocalTime( &systime );
|
|
SET_CH( context, BIN_TO_BCD(systime.wHour) );
|
|
SET_CL( context, BIN_TO_BCD(systime.wMinute) );
|
|
SET_DH( context, BIN_TO_BCD(systime.wSecond) );
|
|
SET_DL( context, 0 ); /* FIXME: assume no daylight saving */
|
|
RESET_CFLAG(context);
|
|
}
|
|
break;
|
|
|
|
case 0x03: /* SET REAL-TIME CLOCK TIME */
|
|
FIXME( "SET REAL-TIME CLOCK TIME - not allowed\n" );
|
|
break;
|
|
|
|
case 0x04: /* GET REAL-TIME CLOCK DATE */
|
|
TRACE( "GET REAL-TIME CLOCK DATE\n" );
|
|
{
|
|
SYSTEMTIME systime;
|
|
GetLocalTime( &systime );
|
|
SET_CH( context, BIN_TO_BCD(systime.wYear / 100) );
|
|
SET_CL( context, BIN_TO_BCD(systime.wYear % 100) );
|
|
SET_DH( context, BIN_TO_BCD(systime.wMonth) );
|
|
SET_DL( context, BIN_TO_BCD(systime.wDay) );
|
|
RESET_CFLAG(context);
|
|
}
|
|
break;
|
|
|
|
case 0x05: /* SET REAL-TIME CLOCK DATE */
|
|
FIXME( "SET REAL-TIME CLOCK DATE - not allowed\n" );
|
|
break;
|
|
|
|
case 0x06: /* SET ALARM */
|
|
FIXME( "SET ALARM - unimplemented\n" );
|
|
break;
|
|
|
|
case 0x07: /* CANCEL ALARM */
|
|
FIXME( "CANCEL ALARM - unimplemented\n" );
|
|
break;
|
|
|
|
case 0x08: /* SET RTC ACTIVATED POWER ON MODE */
|
|
case 0x09: /* READ RTC ALARM TIME AND STATUS */
|
|
case 0x0a: /* READ SYSTEM-TIMER DAY COUNTER */
|
|
case 0x0b: /* SET SYSTEM-TIMER DAY COUNTER */
|
|
case 0x0c: /* SET RTC DATE/TIME ACTIVATED POWER-ON MODE */
|
|
case 0x0d: /* RESET RTC DATE/TIME ACTIVATED POWER-ON MODE */
|
|
case 0x0e: /* GET RTC DATE/TIME ALARM AND STATUS */
|
|
case 0x0f: /* INITIALIZE REAL-TIME CLOCK */
|
|
INT_BARF( context, 0x1a );
|
|
break;
|
|
|
|
case 0xb0:
|
|
if (CX_reg(context) == 0x4d52 &&
|
|
DX_reg(context) == 0x4349 &&
|
|
AL_reg(context) == 0x01)
|
|
{
|
|
/*
|
|
* Microsoft Real-Time Compression Interface (MRCI).
|
|
* Ignoring this call indicates MRCI is not supported.
|
|
*/
|
|
TRACE( "Microsoft Real-Time Compression Interface - not supported\n" );
|
|
}
|
|
else
|
|
{
|
|
INT_BARF(context, 0x1a);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
INT_BARF( context, 0x1a );
|
|
}
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* DOSVM_Int20Handler
|
|
*
|
|
* Handler for int 20h.
|
|
*/
|
|
static void WINAPI DOSVM_Int20Handler( CONTEXT *context )
|
|
{
|
|
if (DOSVM_IsWin16())
|
|
DOSVM_Exit( 0 );
|
|
else if(ISV86(context))
|
|
MZ_Exit( context, TRUE, 0 );
|
|
else
|
|
ERR( "Called from DOS protected mode\n" );
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* DOSVM_Int29Handler
|
|
*
|
|
* Handler for int 29h (fast console output)
|
|
*/
|
|
static void WINAPI DOSVM_Int29Handler( CONTEXT *context )
|
|
{
|
|
/* Yes, it seems that this is really all this interrupt does. */
|
|
DOSVM_PutChar(AL_reg(context));
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* DOSVM_Int2aHandler
|
|
*
|
|
* Handler for int 2ah (network).
|
|
*/
|
|
static void WINAPI DOSVM_Int2aHandler( CONTEXT *context )
|
|
{
|
|
switch(AH_reg(context))
|
|
{
|
|
case 0x00: /* NETWORK INSTALLATION CHECK */
|
|
break;
|
|
|
|
default:
|
|
INT_BARF( context, 0x2a );
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* DOSVM_Int41Handler
|
|
*/
|
|
static void WINAPI DOSVM_Int41Handler( CONTEXT *context )
|
|
{
|
|
if ( ISV86(context) )
|
|
{
|
|
/* Real-mode debugger services */
|
|
switch ( AX_reg(context) )
|
|
{
|
|
default:
|
|
INT_BARF( context, 0x41 );
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Protected-mode debugger services */
|
|
switch ( AX_reg(context) )
|
|
{
|
|
case 0x4f:
|
|
case 0x50:
|
|
case 0x150:
|
|
case 0x51:
|
|
case 0x52:
|
|
case 0x152:
|
|
case 0x59:
|
|
case 0x5a:
|
|
case 0x5b:
|
|
case 0x5c:
|
|
case 0x5d:
|
|
/* Notifies the debugger of a lot of stuff. We simply ignore it
|
|
for now, but some of the info might actually be useful ... */
|
|
break;
|
|
|
|
default:
|
|
INT_BARF( context, 0x41 );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* DOSVM_Int4bHandler
|
|
*
|
|
*/
|
|
static void WINAPI DOSVM_Int4bHandler( CONTEXT *context )
|
|
{
|
|
switch(AH_reg(context))
|
|
{
|
|
case 0x81: /* Virtual DMA Spec (IBM SCSI interface) */
|
|
if(AL_reg(context) != 0x02) /* if not install check */
|
|
{
|
|
SET_CFLAG(context);
|
|
SET_AL( context, 0x0f ); /* function is not implemented */
|
|
}
|
|
break;
|
|
default:
|
|
INT_BARF(context, 0x4b);
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* DOSVM_Int5cHandler
|
|
*
|
|
* Called from NetBIOSCall16.
|
|
*/
|
|
static void WINAPI DOSVM_Int5cHandler( CONTEXT *context )
|
|
{
|
|
BYTE* ptr;
|
|
ptr = MapSL( MAKESEGPTR(context->SegEs,BX_reg(context)) );
|
|
FIXME("(%p): command code %02x (ignored)\n",context, *ptr);
|
|
*(ptr+0x01) = 0xFB; /* NetBIOS emulator not found */
|
|
SET_AL( context, 0xFB );
|
|
}
|