#include "config.h"
#include "wine/port.h"
+#include <stdarg.h>
+
#include "windef.h"
+#include "winbase.h"
#include "wine/winbase16.h"
-#include "miscemu.h"
-#include "task.h"
-#include "msdos.h"
+#include "wownt32.h"
#include "dosexe.h"
+#include "excpt.h"
#include "wine/debug.h"
+#include "wine/exception.h"
+#include "thread.h"
WINE_DEFAULT_DEBUG_CHANNEL(int31);
static RMCB *FirstRMCB = NULL;
static WORD dpmi_flag;
+static void* lastvalloced = NULL;
+static BYTE DPMI_retval;
+
+/**********************************************************************
+ * DOSVM_IsDos32
+ *
+ * Return TRUE if we are in 32-bit protected mode DOS process.
+ */
+BOOL DOSVM_IsDos32(void)
+{
+ return (dpmi_flag & 1) ? TRUE : FALSE;
+}
+
+
+/**********************************************************************
+ * alloc_pm_selector
+ *
+ * Allocate a 64k sized selector corresponding to a real mode segment.
+ */
+static WORD alloc_pm_selector( WORD seg, unsigned char flags )
+{
+ WORD sel = wine_ldt_alloc_entries( 1 );
+
+ if (sel)
+ {
+ LDT_ENTRY entry;
+ wine_ldt_set_base( &entry, (void *)(seg << 4) );
+ wine_ldt_set_limit( &entry, 0xffff );
+ wine_ldt_set_flags( &entry, flags );
+ wine_ldt_set_entry( sel, &entry );
+ }
+ return sel;
+}
+
+
+/**********************************************************************
+ * dpmi_exception_handler
+ *
+ * Handle EXCEPTION_VM86_STI exceptions generated
+ * when there are pending asynchronous events.
+ */
+static WINE_EXCEPTION_FILTER(dpmi_exception_handler)
+{
+#ifdef __i386__
+ EXCEPTION_RECORD *rec = GetExceptionInformation()->ExceptionRecord;
+ CONTEXT *context = GetExceptionInformation()->ContextRecord;
+
+ if (rec->ExceptionCode == EXCEPTION_VM86_STI)
+ {
+ if (ISV86(context))
+ ERR( "Real mode STI caught by protected mode handler!\n" );
+ DOSVM_SendQueuedEvents(context);
+ return EXCEPTION_CONTINUE_EXECUTION;
+ }
+ else if (rec->ExceptionCode == EXCEPTION_VM86_INTx)
+ {
+ if (ISV86(context))
+ ERR( "Real mode INTx caught by protected mode handler!\n" );
+ DPMI_retval = (BYTE)rec->ExceptionInformation[0];
+ return EXCEPTION_EXECUTE_HANDLER;
+ }
+
+#endif
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
/**********************************************************************
* INT_GetRealModeContext
call->ss = context->SegSs;
}
+/**********************************************************************
+ * DPMI_xalloc
+ * special virtualalloc, allocates lineary monoton growing memory.
+ * (the usual VirtualAlloc does not satisfy that restriction)
+ */
+static LPVOID DPMI_xalloc( DWORD len )
+{
+ LPVOID ret;
+ LPVOID oldlastv = lastvalloced;
+
+ if (lastvalloced)
+ {
+ int xflag = 0;
+
+ ret = NULL;
+ while (!ret)
+ {
+ ret = VirtualAlloc( lastvalloced, len,
+ MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE );
+ if (!ret)
+ lastvalloced = (char *) lastvalloced + 0x10000;
+
+ /* we failed to allocate one in the first round.
+ * try non-linear
+ */
+ if (!xflag && (lastvalloced<oldlastv))
+ {
+ /* wrapped */
+ FIXME( "failed to allocate linearly growing memory (%ld bytes), "
+ "using non-linear growing...\n", len );
+ xflag++;
+ }
+
+ /* if we even fail to allocate something in the next
+ * round, return NULL
+ */
+ if ((xflag==1) && (lastvalloced >= oldlastv))
+ xflag++;
+
+ if ((xflag==2) && (lastvalloced < oldlastv)) {
+ FIXME( "failed to allocate any memory of %ld bytes!\n", len );
+ return NULL;
+ }
+ }
+ }
+ else
+ {
+ ret = VirtualAlloc( NULL, len,
+ MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE );
+ }
+
+ lastvalloced = (LPVOID)(((DWORD)ret+len+0xffff)&~0xffff);
+ return ret;
+}
+
+/**********************************************************************
+ * DPMI_xfree
+ */
+static void DPMI_xfree( LPVOID ptr )
+{
+ VirtualFree( ptr, 0, MEM_RELEASE );
+}
+
+/**********************************************************************
+ * DPMI_xrealloc
+ *
+ * FIXME: perhaps we could grow this mapped area...
+ */
+static LPVOID DPMI_xrealloc( LPVOID ptr, DWORD newsize )
+{
+ MEMORY_BASIC_INFORMATION mbi;
+ LPVOID newptr;
+
+ newptr = DPMI_xalloc( newsize );
+ if (ptr)
+ {
+ if (!VirtualQuery(ptr,&mbi,sizeof(mbi)))
+ {
+ FIXME( "realloc of DPMI_xallocd region %p?\n", ptr );
+ return NULL;
+ }
+
+ if (mbi.State == MEM_FREE)
+ {
+ FIXME( "realloc of DPMI_xallocd region %p?\n", ptr );
+ return NULL;
+ }
+
+ /* We do not shrink allocated memory. most reallocs
+ * only do grows anyway
+ */
+ if (newsize <= mbi.RegionSize)
+ return ptr;
+
+ memcpy( newptr, ptr, mbi.RegionSize );
+ DPMI_xfree( ptr );
+ }
+
+ return newptr;
+}
+
#ifdef __i386__
*/
static void DPMI_CallRMCBProc( CONTEXT86 *context, RMCB *rmcb, WORD flag )
{
- if (IS_SELECTOR_SYSTEM( rmcb->proc_sel )) {
+ DWORD old_vif = NtCurrentTeb()->dpmi_vif;
+
+ /* Disable virtual interrupts. */
+ NtCurrentTeb()->dpmi_vif = 0;
+
+ if (wine_ldt_is_system( rmcb->proc_sel )) {
/* Wine-internal RMCB, call directly */
((RMCBPROC)rmcb->proc_ofs)(context);
- } else {
+ } else __TRY {
#ifdef __i386__
UINT16 ss,es;
DWORD esp,edi;
INT_SetRealModeContext(MapSL(MAKESEGPTR( rmcb->regs_sel, rmcb->regs_ofs )), context);
- ss = SELECTOR_AllocBlock( (void *)(context->SegSs<<4), 0x10000, WINE_LDT_FLAGS_DATA );
+ ss = alloc_pm_selector( context->SegSs, WINE_LDT_FLAGS_DATA );
esp = context->Esp;
FIXME("untested!\n");
ctx.SegEs = rmcb->regs_sel;
ctx.Edi = rmcb->regs_ofs;
/* FIXME: I'm pretty sure this isn't right - should push flags first */
- wine_call_to_16_regs_short(&ctx, 0);
+ WOWCallback16Ex( 0, WCB16_REGS, 0, NULL, (DWORD *)&ctx );
es = ctx.SegEs;
edi = ctx.Edi;
}
- FreeSelector16(ss);
+ wine_ldt_free_entries( ss, 1 );
INT_GetRealModeContext( MapSL( MAKESEGPTR( es, edi )), context);
#else
ERR("RMCBs only implemented for i386\n");
#endif
- }
+ } __EXCEPT(dpmi_exception_handler) { } __ENDTRY
+
+ /* Restore virtual interrupt flag. */
+ NtCurrentTeb()->dpmi_vif = old_vif;
}
/* shortcut for chaining to internal interrupt handlers */
if ((context->SegCs == 0xF000) && iret)
{
- DOSVM_RealModeInterrupt( LOWORD(context->Eip)/4, context);
+ DOSVM_CallBuiltinHandler( context, LOWORD(context->Eip)/4 );
return 0;
}
*stack16 = LOWORD(context->EFlags);
}
/* push return address (return to interrupt wrapper) */
- *(--stack16) = DOSMEM_wrap_seg;
+ *(--stack16) = DOSVM_dpmi_segments->wrap_seg;
*(--stack16) = 0;
/* adjust stack */
context->Esp -= 2*sizeof(WORD);
/* 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 != DOSMEM_wrap_seg) ||
+ 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 */
{
CONTEXT86 realmode_ctx;
FARPROC16 rm_int = DOSVM_GetRMHandler( BL_reg(context) );
- REALMODECALL *call = MapSL( MAKESEGPTR( context->SegEs, DI_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 */
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_RealModeInterrupt( LOWORD(rm_int)/4, &realmode_ctx );
+ DOSVM_CallBuiltinHandler( &realmode_ctx, LOWORD(rm_int)/4 );
}
INT_SetRealModeContext( call, &realmode_ctx );
}
*/
void WINAPI DOSVM_CallRMProc( CONTEXT86 *context, int iret )
{
- REALMODECALL *p = MapSL( MAKESEGPTR( context->SegEs, DI_reg(context) ));
+ REALMODECALL *p = CTX_SEG_OFF_TO_LIN( context,
+ context->SegEs,
+ context->Edi );
CONTEXT86 context16;
TRACE("RealModeCall: EAX=%08lx EBX=%08lx ECX=%08lx EDX=%08lx\n",
RESET_CFLAG(context);
dpmi_flag = AX_reg(context);
/* our mode switch wrapper have placed the desired CS into DX */
- cs = SELECTOR_AllocBlock( (void *)(DX_reg(context)<<4), 0x10000, WINE_LDT_FLAGS_CODE );
+ cs = alloc_pm_selector( context->Edx, WINE_LDT_FLAGS_CODE );
/* due to a flaw in some CPUs (at least mine), it is best to mark stack segments as 32-bit if they
can be used in 32-bit code. Otherwise, these CPUs may not set the high word of esp during a
ring transition (from kernel code) to the 16-bit stack, and this causes trouble if executing
32-bit code using this stack. */
if (dpmi_flag & 1) selflags |= WINE_LDT_FLAGS_32BIT;
- ss = SELECTOR_AllocBlock( (void *)(context->SegSs<<4), 0x10000, selflags );
+ ss = alloc_pm_selector( context->SegSs, selflags );
/* do the same for the data segments, just in case */
if (context->SegDs == context->SegSs) ds = ss;
- else ds = SELECTOR_AllocBlock( (void *)(context->SegDs<<4), 0x10000, selflags );
- es = SELECTOR_AllocBlock( psp, 0x100, selflags );
+ else ds = alloc_pm_selector( context->SegDs, selflags );
+ es = alloc_pm_selector( DOSVM_psp, selflags );
/* convert environment pointer, as the spec says, but we're a bit lazy about the size here... */
- psp->environment = SELECTOR_AllocBlock( (void *)(env_seg<<4), 0x10000, WINE_LDT_FLAGS_DATA );
+ psp->environment = alloc_pm_selector( env_seg, WINE_LDT_FLAGS_DATA );
pm_ctx = *context;
- pm_ctx.SegCs = DOSMEM_dpmi_sel;
+ pm_ctx.SegCs = DOSVM_dpmi_segments->dpmi_sel;
/* our mode switch wrapper expects the new CS in DX, and the new SS in AX */
pm_ctx.Eax = ss;
pm_ctx.Edx = cs;
pm_ctx.SegDs = ds;
pm_ctx.SegEs = es;
- pm_ctx.SegFs = 0;
- pm_ctx.SegGs = 0;
+ pm_ctx.SegFs = wine_get_fs();
+ pm_ctx.SegGs = wine_get_gs();
+ pm_ctx.EFlags &= ~V86_FLAG;
- TRACE("DOS program is now entering protected mode\n");
- wine_call_to_16_regs_short(&pm_ctx, 0);
+ TRACE("DOS program is now entering %d-bit protected mode\n",
+ DOSVM_IsDos32() ? 32 : 16);
- /* in the current state of affairs, we won't ever actually return here... */
- /* we should have int21/ah=4c do it someday, though... */
+ __TRY
+ {
+ WOWCallback16Ex( 0, WCB16_REGS, 0, NULL, (DWORD *)&pm_ctx );
+ }
+ __EXCEPT(dpmi_exception_handler)
+ {
+ }
+ __ENDTRY
+
+ TRACE( "Protected mode DOS program is terminating\n" );
+
+ /*
+ * FIXME: Instead of calling ExitThread, we should release all
+ * allocated protected mode resources and call MZ_Exit
+ * using real mode context. See DPMI specification.
+ */
+ ExitThread( DPMI_retval );
- FreeSelector16(psp->environment);
+#if 0
+ wine_ldt_free_entries( psp->environment, 1 );
psp->environment = env_seg;
- FreeSelector16(es);
- if (ds != ss) FreeSelector16(ds);
- FreeSelector16(ss);
- FreeSelector16(cs);
+ wine_ldt_free_entries(es,1);
+ if (ds != ss) wine_ldt_free_entries(ds,1);
+ wine_ldt_free_entries(ss,1);
+ wine_ldt_free_entries(cs,1);
+#endif
}
static RMCB *DPMI_AllocRMCB( void )
PrevRMCB->next = CurrRMCB->next;
else
FirstRMCB = CurrRMCB->next;
- DOSMEM_FreeBlock(DOSMEM_MapRealToLinear(CurrRMCB->address));
+ DOSMEM_FreeBlock(PTR_REAL_TO_LIN(SELECTOROF(CurrRMCB->address),OFFSETOF(CurrRMCB->address)));
HeapFree(GetProcessHeap(), 0, CurrRMCB);
return 0;
}
}
-/* DPMI Raw Mode Switch handler */
-
-#if 0
-void WINAPI DPMI_RawModeSwitch( SIGCONTEXT *context )
+/**********************************************************************
+ * DOSVM_RawModeSwitchHandler
+ *
+ * DPMI Raw Mode Switch handler
+ */
+void WINAPI DOSVM_RawModeSwitchHandler( CONTEXT86 *context )
{
- LPDOSTASK lpDosTask = MZ_Current();
CONTEXT86 rm_ctx;
int ret;
- if (!lpDosTask) {
- /* we could probably start a DPMI-only dosmod task here, but I doubt
- anything other than real DOS apps want to call raw mode switch */
- ERR("attempting raw mode switch without DOS task!\n");
- ExitProcess(1);
- }
/* initialize real-mode context as per spec */
memset(&rm_ctx, 0, sizeof(rm_ctx));
- rm_ctx.SegDs = AX_sig(context);
- rm_ctx.SegEs = CX_sig(context);
- rm_ctx.SegSs = DX_sig(context);
- rm_ctx.Esp = EBX_sig(context);
- rm_ctx.SegCs = SI_sig(context);
- rm_ctx.Eip = EDI_sig(context);
- rm_ctx.Ebp = EBP_sig(context);
+ rm_ctx.SegDs = AX_reg(context);
+ rm_ctx.SegEs = CX_reg(context);
+ rm_ctx.SegSs = DX_reg(context);
+ rm_ctx.Esp = context->Ebx;
+ rm_ctx.SegCs = SI_reg(context);
+ rm_ctx.Eip = context->Edi;
+ rm_ctx.Ebp = context->Ebp;
rm_ctx.SegFs = 0;
rm_ctx.SegGs = 0;
- rm_ctx.EFlags = EFL_sig(context); /* at least we need the IF flag */
+
+ /* Copy interrupt state. */
+ if (NtCurrentTeb()->dpmi_vif)
+ rm_ctx.EFlags = V86_FLAG | VIF_MASK;
+ else
+ rm_ctx.EFlags = V86_FLAG;
/* enter real mode again */
TRACE("re-entering real mode at %04lx:%04lx\n",rm_ctx.SegCs,rm_ctx.Eip);
DOSVM_Enter will return and we will continue here */
if (ret<0) {
+ ERR("Sync lost!\n");
/* if the sync was lost, there's no way to recover */
ExitProcess(1);
}
/* alter protected-mode context as per spec */
- DS_sig(context) = LOWORD(rm_ctx.Eax);
- ES_sig(context) = LOWORD(rm_ctx.Ecx);
- SS_sig(context) = LOWORD(rm_ctx.Edx);
- ESP_sig(context) = rm_ctx.Ebx;
- CS_sig(context) = LOWORD(rm_ctx.Esi);
- EIP_sig(context) = rm_ctx.Edi;
- EBP_sig(context) = rm_ctx.Ebp;
- FS_sig(context) = 0;
- GS_sig(context) = 0;
+ context->SegDs = LOWORD(rm_ctx.Eax);
+ context->SegEs = LOWORD(rm_ctx.Ecx);
+ context->SegSs = LOWORD(rm_ctx.Edx);
+ context->Esp = rm_ctx.Ebx;
+ context->SegCs = LOWORD(rm_ctx.Esi);
+ context->Eip = rm_ctx.Edi;
+ context->Ebp = rm_ctx.Ebp;
+ context->SegFs = 0;
+ context->SegGs = 0;
+
+ /* Copy interrupt state. */
+ if (rm_ctx.EFlags & VIF_MASK)
+ NtCurrentTeb()->dpmi_vif = 1;
+ else
+ NtCurrentTeb()->dpmi_vif = 0;
/* Return to new address and hope that we didn't mess up */
- TRACE("re-entering protected mode at %04x:%08lx\n",
- CS_sig(context), EIP_sig(context));
+ TRACE("re-entering protected mode at %04lx:%08lx\n",
+ context->SegCs, context->Eip);
}
-#endif
/**********************************************************************
if (NewRMCB)
{
- /* FIXME: if 32-bit DPMI client, use ESI and EDI */
- NewRMCB->proc_ofs = LOWORD(context->Esi);
+ NewRMCB->proc_ofs = DOSVM_IsDos32() ? context->Esi : LOWORD(context->Esi);
NewRMCB->proc_sel = context->SegDs;
- NewRMCB->regs_ofs = LOWORD(context->Edi);
+ NewRMCB->regs_ofs = DOSVM_IsDos32() ? context->Edi : LOWORD(context->Edi);
NewRMCB->regs_sel = context->SegEs;
- SET_LOWORD( context->Ecx, HIWORD(NewRMCB->address) );
- SET_LOWORD( context->Edx, LOWORD(NewRMCB->address) );
+ SET_CX( context, HIWORD(NewRMCB->address) );
+ SET_DX( context, LOWORD(NewRMCB->address) );
}
else
{
- SET_LOWORD( context->Eax, 0x8015 ); /* callback unavailable */
+ SET_AX( context, 0x8015 ); /* callback unavailable */
SET_CFLAG(context);
}
}
CX_reg(context), DX_reg(context));
if (DPMI_FreeRMCB(MAKELONG(DX_reg(context), CX_reg(context)))) {
- SET_LOWORD( context->Eax, 0x8024 ); /* invalid callback address */
+ SET_AX( context, 0x8024 ); /* invalid callback address */
SET_CFLAG(context);
}
}
/**********************************************************************
- * DOSVM_Int31Handler
+ * DOSVM_CheckWrappers
*
- * Handler for real-mode int 31h (DPMI).
+ * Check if this was really a wrapper call instead of an interrupt.
*/
-void WINAPI DOSVM_Int31Handler( CONTEXT86 *context )
+BOOL DOSVM_CheckWrappers( CONTEXT86 *context )
{
- /* check if it's our wrapper */
- TRACE("called from real mode\n");
- if (context->SegCs==DOSMEM_dpmi_seg) {
+ if (context->SegCs==DOSVM_dpmi_segments->dpmi_seg) {
/* This is the protected mode switch */
StartPM(context);
- return;
+ return TRUE;
}
- else if (context->SegCs==DOSMEM_xms_seg)
+ else if (context->SegCs==DOSVM_dpmi_segments->xms_seg)
{
/* This is the XMS driver entry point */
XMS_Handler(context);
- return;
+ return TRUE;
}
else
{
if (CurrRMCB) {
/* RMCB call, propagate to protected-mode handler */
DPMI_CallRMCBProc(context, CurrRMCB, dpmi_flag);
- return;
+ return TRUE;
}
}
- /* chain to protected mode handler */
- INT_Int31Handler( context );
+ return FALSE;
+}
+
+/**********************************************************************
+ * DOSVM_Int31Handler (WINEDOS16.149)
+ *
+ * Handler for int 31h (DPMI).
+ */
+void WINAPI DOSVM_Int31Handler( CONTEXT86 *context )
+{
+ RESET_CFLAG(context);
+ switch(AX_reg(context))
+ {
+ case 0x0000: /* Allocate LDT descriptors */
+ TRACE( "allocate LDT descriptors (%d)\n", CX_reg(context) );
+ {
+ WORD sel = AllocSelectorArray16( CX_reg(context) );
+ if(!sel)
+ {
+ TRACE( "failed\n" );
+ SET_AX( context, 0x8011 ); /* descriptor unavailable */
+ SET_CFLAG( context );
+ }
+ else
+ {
+ TRACE( "success, array starts at 0x%04x\n", sel );
+ SET_AX( context, sel );
+ }
+ }
+ break;
+
+ case 0x0001: /* Free LDT descriptor */
+ TRACE( "free LDT descriptor (0x%04x)\n", BX_reg(context) );
+ if (FreeSelector16( BX_reg(context) ))
+ {
+ SET_AX( context, 0x8022 ); /* invalid selector */
+ SET_CFLAG( context );
+ }
+ else
+ {
+ /* If a segment register contains the selector being freed, */
+ /* set it to zero. */
+ if (!((context->SegDs^BX_reg(context)) & ~3)) context->SegDs = 0;
+ if (!((context->SegEs^BX_reg(context)) & ~3)) context->SegEs = 0;
+ if (!((context->SegFs^BX_reg(context)) & ~3)) context->SegFs = 0;
+ if (!((context->SegGs^BX_reg(context)) & ~3)) context->SegGs = 0;
+ }
+ break;
+
+ case 0x0002: /* Real mode segment to descriptor */
+ TRACE( "real mode segment to descriptor (0x%04x)\n", BX_reg(context) );
+ {
+ WORD entryPoint = 0; /* KERNEL entry point for descriptor */
+ switch(BX_reg(context))
+ {
+ case 0x0000: entryPoint = 183; break; /* __0000H */
+ case 0x0040: entryPoint = 193; break; /* __0040H */
+ case 0xa000: entryPoint = 174; break; /* __A000H */
+ case 0xb000: entryPoint = 181; break; /* __B000H */
+ case 0xb800: entryPoint = 182; break; /* __B800H */
+ case 0xc000: entryPoint = 195; break; /* __C000H */
+ case 0xd000: entryPoint = 179; break; /* __D000H */
+ case 0xe000: entryPoint = 190; break; /* __E000H */
+ case 0xf000: entryPoint = 194; break; /* __F000H */
+ default:
+ SET_AX( context, DOSMEM_AllocSelector(BX_reg(context)) );
+ break;
+ }
+ if (entryPoint)
+ {
+ FARPROC16 proc = GetProcAddress16( GetModuleHandle16( "KERNEL" ),
+ (LPCSTR)(ULONG_PTR)entryPoint );
+ SET_AX( context, LOWORD(proc) );
+ }
+ }
+ break;
+
+ case 0x0003: /* Get next selector increment */
+ TRACE("get selector increment (__AHINCR)\n");
+ context->Eax = __AHINCR;
+ break;
+
+ case 0x0004: /* Lock selector (not supported) */
+ FIXME("lock selector not supported\n");
+ context->Eax = 0; /* FIXME: is this a correct return value? */
+ break;
+
+ case 0x0005: /* Unlock selector (not supported) */
+ FIXME("unlock selector not supported\n");
+ context->Eax = 0; /* FIXME: is this a correct return value? */
+ break;
+
+ case 0x0006: /* Get selector base address */
+ TRACE( "get selector base address (0x%04x)\n", BX_reg(context) );
+ {
+ LDT_ENTRY entry;
+ WORD sel = BX_reg(context);
+ wine_ldt_get_entry( sel, &entry );
+ if (wine_ldt_is_empty(&entry))
+ {
+ context->Eax = 0x8022; /* invalid selector */
+ SET_CFLAG(context);
+ }
+ else
+ {
+ void *base = wine_ldt_get_base(&entry);
+ SET_CX( context, HIWORD(base) );
+ SET_DX( context, LOWORD(base) );
+ }
+ }
+ break;
+
+ case 0x0007: /* Set selector base address */
+ {
+ DWORD base = MAKELONG( DX_reg(context), CX_reg(context) );
+ WORD sel = BX_reg(context);
+ TRACE( "set selector base address (0x%04x,0x%08lx)\n", sel, base );
+
+ /* check if Win16 app wants to access lower 64K of DOS memory */
+ if (base < 0x10000 && DOSVM_IsWin16())
+ DOSMEM_Init(TRUE);
+
+ SetSelectorBase( sel, base );
+ }
+ break;
+
+ case 0x0008: /* Set selector limit */
+ {
+ DWORD limit = MAKELONG( DX_reg(context), CX_reg(context) );
+ TRACE( "set selector limit (0x%04x,0x%08lx)\n",
+ BX_reg(context), limit );
+ SetSelectorLimit16( BX_reg(context), limit );
+ }
+ break;
+
+ case 0x0009: /* Set selector access rights */
+ TRACE( "set selector access rights(0x%04x,0x%04x)\n",
+ BX_reg(context), CX_reg(context) );
+ SelectorAccessRights16( BX_reg(context), 1, CX_reg(context) );
+ break;
+
+ case 0x000a: /* Allocate selector alias */
+ TRACE( "allocate selector alias (0x%04x)\n", BX_reg(context) );
+ SET_AX( context, AllocCStoDSAlias16( BX_reg(context) ) );
+ if (!AX_reg(context))
+ {
+ SET_AX( context, 0x8011 ); /* descriptor unavailable */
+ SET_CFLAG(context);
+ }
+ break;
+
+ case 0x000b: /* Get descriptor */
+ TRACE( "get descriptor (0x%04x)\n", BX_reg(context) );
+ {
+ LDT_ENTRY *entry = (LDT_ENTRY*)CTX_SEG_OFF_TO_LIN( context,
+ context->SegEs,
+ context->Edi );
+ wine_ldt_get_entry( BX_reg(context), entry );
+ }
+ break;
+
+ case 0x000c: /* Set descriptor */
+ TRACE( "set descriptor (0x%04x)\n", BX_reg(context) );
+ {
+ LDT_ENTRY *entry = (LDT_ENTRY*)CTX_SEG_OFF_TO_LIN( context,
+ context->SegEs,
+ context->Edi );
+ wine_ldt_set_entry( BX_reg(context), entry );
+ }
+ break;
+
+ case 0x000d: /* Allocate specific LDT descriptor */
+ FIXME( "allocate descriptor (0x%04x), stub!\n", BX_reg(context) );
+ SET_AX( context, 0x8011 ); /* descriptor unavailable */
+ SET_CFLAG( context );
+ break;
+
+ case 0x000e: /* Get Multiple Descriptors (1.0) */
+ FIXME( "get multiple descriptors - unimplemented\n" );
+ break;
+
+ case 0x000f: /* Set Multiple Descriptors (1.0) */
+ FIXME( "set multiple descriptors - unimplemented\n" );
+ break;
+
+ case 0x0100: /* Allocate DOS memory block */
+ TRACE( "allocate DOS memory block (0x%x paragraphs)\n", BX_reg(context) );
+ {
+ DWORD dw = GlobalDOSAlloc16( (DWORD)BX_reg(context) << 4 );
+ if (dw) {
+ SET_AX( context, HIWORD(dw) );
+ SET_DX( context, LOWORD(dw) );
+ } else {
+ SET_AX( context, 0x0008 ); /* insufficient memory */
+ SET_BX( context, DOSMEM_Available() >> 4 );
+ SET_CFLAG(context);
+ }
+ break;
+ }
+
+ case 0x0101: /* Free DOS memory block */
+ TRACE( "free DOS memory block (0x%04x)\n", DX_reg(context) );
+ {
+ WORD error = GlobalDOSFree16( DX_reg(context) );
+ if (error) {
+ SET_AX( context, 0x0009 ); /* memory block address invalid */
+ SET_CFLAG( context );
+ }
+ }
+ break;
+
+ case 0x0102: /* Resize DOS Memory Block */
+ FIXME( "resize DOS memory block (0x%04x, 0x%x paragraphs) - unimplemented\n",
+ DX_reg(context), BX_reg(context) );
+ break;
+
+ case 0x0200: /* get real mode interrupt vector */
+ TRACE( "get realmode interupt vector (0x%02x)\n",
+ BL_reg(context) );
+ {
+ FARPROC16 proc = DOSVM_GetRMHandler( BL_reg(context) );
+ SET_CX( context, SELECTOROF(proc) );
+ SET_DX( context, OFFSETOF(proc) );
+ }
+ break;
+
+ case 0x0201: /* set real mode interrupt vector */
+ TRACE( "set realmode interrupt vector (0x%02x, 0x%04x:0x%04x)\n",
+ BL_reg(context), CX_reg(context), DX_reg(context) );
+ DOSVM_SetRMHandler( BL_reg(context),
+ (FARPROC16)MAKESEGPTR(CX_reg(context), DX_reg(context)) );
+ break;
+
+ case 0x0202: /* Get Processor Exception Handler Vector */
+ FIXME( "Get Processor Exception Handler Vector (0x%02x)\n",
+ BL_reg(context) );
+ if (DOSVM_IsDos32())
+ {
+ SET_CX( context, 0 );
+ context->Edx = 0;
+ }
+ else
+ {
+ SET_CX( context, 0 );
+ SET_DX( context, 0 );
+ }
+ break;
+
+ case 0x0203: /* Set Processor Exception Handler Vector */
+ FIXME( "Set Processor Exception Handler Vector (0x%02x)\n",
+ BL_reg(context) );
+ break;
+
+ case 0x0204: /* Get protected mode interrupt vector */
+ TRACE("get protected mode interrupt handler (0x%02x)\n",
+ BL_reg(context));
+ if (DOSVM_IsDos32())
+ {
+ FARPROC48 handler = DOSVM_GetPMHandler48( BL_reg(context) );
+ SET_CX( context, handler.selector );
+ context->Edx = handler.offset;
+ }
+ else
+ {
+ FARPROC16 handler = DOSVM_GetPMHandler16( BL_reg(context) );
+ SET_CX( context, SELECTOROF(handler) );
+ SET_DX( context, OFFSETOF(handler) );
+ }
+ break;
+
+ case 0x0205: /* Set protected mode interrupt vector */
+ TRACE("set protected mode interrupt handler (0x%02x,0x%04x:0x%08lx)\n",
+ BL_reg(context), CX_reg(context), context->Edx);
+ if (DOSVM_IsDos32())
+ {
+ FARPROC48 handler;
+ handler.selector = CX_reg(context);
+ handler.offset = context->Edx;
+ DOSVM_SetPMHandler48( BL_reg(context), handler );
+ }
+ else
+ {
+ FARPROC16 handler;
+ handler = (FARPROC16)MAKESEGPTR( CX_reg(context), DX_reg(context));
+ DOSVM_SetPMHandler16( BL_reg(context), handler );
+ }
+ break;
+
+ case 0x0300: /* Simulate real mode interrupt */
+ TRACE( "Simulate real mode interrupt %02x.\n", BL_reg(context));
+ DOSVM_CallRMInt( context );
+ break;
+
+ case 0x0301: /* Call real mode procedure with far return */
+ TRACE( "Call real mode procedure with far return.\n" );
+ DOSVM_CallRMProc( context, FALSE );
+ break;
+
+ case 0x0302: /* Call real mode procedure with interrupt return */
+ TRACE( "Call real mode procedure with interrupt return.\n" );
+ DOSVM_CallRMProc( context, TRUE );
+ break;
+
+ case 0x0303: /* Allocate Real Mode Callback Address */
+ TRACE( "Allocate real mode callback address.\n" );
+ DOSVM_AllocRMCB( context );
+ break;
+
+ case 0x0304: /* Free Real Mode Callback Address */
+ TRACE( "Free real mode callback address.\n" );
+ DOSVM_FreeRMCB( context );
+ break;
+
+ case 0x0305: /* Get State Save/Restore Addresses */
+ TRACE("get state save/restore addresses\n");
+ /* we probably won't need this kind of state saving */
+ SET_AX( context, 0 );
+
+ /* real mode: just point to the lret */
+ SET_BX( context, DOSVM_dpmi_segments->wrap_seg );
+ SET_CX( context, 2 );
+
+ /* protected mode: don't have any handler yet... */
+ /* FIXME: Use DI in 16-bit DPMI and EDI in 32-bit DPMI */
+ FIXME("no protected-mode dummy state save/restore handler yet\n");
+ SET_SI( context, 0 );
+ context->Edi = 0;
+ break;
+
+ case 0x0306: /* Get Raw Mode Switch Addresses */
+ TRACE("get raw mode switch addresses\n");
+
+ /* real mode, point to standard DPMI return wrapper */
+ SET_BX( context, DOSVM_dpmi_segments->wrap_seg );
+ SET_CX( context, 0 );
+
+ /* protected mode, point to DPMI call wrapper */
+ /* FIXME: Use DI in 16-bit DPMI and EDI in 32-bit DPMI */
+ /* FIXME: Doesn't work in DPMI32... */
+ SET_SI( context, DOSVM_dpmi_segments->dpmi_sel );
+ context->Edi = 8; /* offset of the INT 0x31 call */
+ break;
+
+ case 0x0400: /* Get DPMI version */
+ TRACE("get DPMI version\n");
+ {
+ SYSTEM_INFO si;
+
+ GetSystemInfo(&si);
+ SET_AX( context, 0x005a ); /* DPMI version 0.90 */
+ SET_BX( context, 0x0005 ); /* Flags: 32-bit, virtual memory */
+ SET_CL( context, si.wProcessorLevel );
+ SET_DX( context, 0x0870 ); /* Master/slave interrupt controller base */
+ }
+ break;
+
+ case 0x0401: /* Get DPMI Capabilities (1.0) */
+ FIXME( "get dpmi capabilities - unimplemented\n");
+ break;
+
+ case 0x0500: /* Get free memory information */
+ TRACE("get free memory information\n");
+ {
+ MEMORYSTATUS status;
+
+ /* the layout is just the same as MEMMANINFO, but without
+ * the dwSize entry.
+ */
+ struct
+ {
+ DWORD dwLargestFreeBlock;
+ DWORD dwMaxPagesAvailable;
+ DWORD dwMaxPagesLockable;
+ DWORD dwTotalLinearSpace;
+ DWORD dwTotalUnlockedPages;
+ DWORD dwFreePages;
+ DWORD dwTotalPages;
+ DWORD dwFreeLinearSpace;
+ DWORD dwSwapFilePages;
+ WORD wPageSize;
+ } *info = CTX_SEG_OFF_TO_LIN( context, context->SegEs, context->Edi );
+
+ GlobalMemoryStatus( &status );
+ info->wPageSize = getpagesize();
+ info->dwLargestFreeBlock = status.dwAvailVirtual;
+ info->dwMaxPagesAvailable = info->dwLargestFreeBlock / info->wPageSize;
+ info->dwMaxPagesLockable = info->dwMaxPagesAvailable;
+ info->dwTotalLinearSpace = status.dwTotalVirtual / info->wPageSize;
+ info->dwTotalUnlockedPages = info->dwTotalLinearSpace;
+ info->dwFreePages = info->dwMaxPagesAvailable;
+ info->dwTotalPages = info->dwTotalLinearSpace;
+ info->dwFreeLinearSpace = info->dwMaxPagesAvailable;
+ info->dwSwapFilePages = status.dwTotalPageFile / info->wPageSize;
+ break;
+ }
+
+ case 0x0501: /* Allocate memory block */
+ {
+ DWORD size = MAKELONG( CX_reg(context), BX_reg(context) );
+ BYTE *ptr;
+
+ TRACE( "allocate memory block (%ld bytes)\n", size );
+
+ ptr = (BYTE *)DPMI_xalloc( size );
+ if (!ptr)
+ {
+ SET_AX( context, 0x8012 ); /* linear memory not available */
+ SET_CFLAG(context);
+ }
+ else
+ {
+ SET_BX( context, HIWORD(ptr) );
+ SET_CX( context, LOWORD(ptr) );
+ SET_SI( context, HIWORD(ptr) );
+ SET_DI( context, LOWORD(ptr) );
+ }
+ break;
+ }
+
+ case 0x0502: /* Free memory block */
+ {
+ DWORD handle = MAKELONG( DI_reg(context), SI_reg(context) );
+ TRACE( "free memory block (0x%08lx)\n", handle );
+ DPMI_xfree( (void *)handle );
+ }
+ break;
+
+ case 0x0503: /* Resize memory block */
+ {
+ DWORD size = MAKELONG( CX_reg(context), BX_reg(context) );
+ DWORD handle = MAKELONG( DI_reg(context), SI_reg(context) );
+ BYTE *ptr;
+
+ TRACE( "resize memory block (0x%08lx, %ld bytes)\n", handle, size );
+
+ ptr = (BYTE *)DPMI_xrealloc( (void *)handle, size );
+ if (!ptr)
+ {
+ SET_AX( context, 0x8012 ); /* linear memory not available */
+ SET_CFLAG(context);
+ } else {
+ SET_BX( context, HIWORD(ptr) );
+ SET_CX( context, LOWORD(ptr) );
+ SET_SI( context, HIWORD(ptr) );
+ SET_DI( context, LOWORD(ptr) );
+ }
+ }
+ break;
+
+ case 0x0507: /* Set page attributes (1.0) */
+ FIXME( "set page attributes - unimplemented\n" );
+ break; /* Just ignore it */
+
+ case 0x0600: /* Lock linear region */
+ TRACE( "lock linear region - ignored (no paging)\n" );
+ break;
+
+ case 0x0601: /* Unlock linear region */
+ TRACE( "unlock linear region - ignored (no paging)\n" );
+ break;
+
+ case 0x0602: /* Mark real mode region as pageable */
+ TRACE( "mark real mode region as pageable - ignored (no paging)\n" );
+ break;
+
+ case 0x0603: /* Relock real mode region */
+ TRACE( "relock real mode region - ignored (no paging)\n" );
+ break;
+
+ case 0x0604: /* Get page size */
+ TRACE("get pagesize\n");
+ SET_BX( context, HIWORD(getpagesize()) );
+ SET_CX( context, LOWORD(getpagesize()) );
+ break;
+
+ case 0x0700: /* Mark pages as paging candidates */
+ TRACE( "mark pages as paging candidates - ignored (no paging)\n" );
+ break;
+
+ case 0x0701: /* Discard pages */
+ TRACE( "discard pages - ignored (no paging)\n" );
+ break;
+
+ case 0x0702: /* Mark page as demand-paging candidate */
+ TRACE( "mark page as demand-paging candidate - ignored (no paging)\n" );
+ break;
+
+ case 0x0703: /* Discard page contents */
+ TRACE( "discard page contents - ignored (no paging)\n" );
+ break;
+
+ case 0x0800: /* Physical address mapping */
+ FIXME( "physical address mapping (0x%08lx) - unimplemented\n",
+ MAKELONG(CX_reg(context),BX_reg(context)) );
+ break;
+
+ case 0x0900: /* Get and Disable Virtual Interrupt State */
+ TRACE( "Get and Disable Virtual Interrupt State: %ld\n",
+ NtCurrentTeb()->dpmi_vif );
+ SET_AL( context, NtCurrentTeb()->dpmi_vif ? 1 : 0 );
+ NtCurrentTeb()->dpmi_vif = 0;
+ break;
+
+ case 0x0901: /* Get and Enable Virtual Interrupt State */
+ TRACE( "Get and Enable Virtual Interrupt State: %ld\n",
+ NtCurrentTeb()->dpmi_vif );
+ SET_AL( context, NtCurrentTeb()->dpmi_vif ? 1 : 0 );
+ NtCurrentTeb()->dpmi_vif = 1;
+ break;
+
+ case 0x0902: /* Get Virtual Interrupt State */
+ TRACE( "Get Virtual Interrupt State: %ld\n",
+ NtCurrentTeb()->dpmi_vif );
+ SET_AL( context, NtCurrentTeb()->dpmi_vif ? 1 : 0 );
+ break;
+
+ case 0x0e00: /* Get Coprocessor Status (1.0) */
+ /*
+ * Return status in AX bits:
+ * B0 - MPv (MP bit in the virtual MSW/CR0)
+ * 0 = numeric coprocessor is disabled for this client
+ * 1 = numeric coprocessor is enabled for this client
+ * B1 - EMv (EM bit in the virtual MSW/CR0)
+ * 0 = client is not emulating coprocessor instructions
+ * 1 = client is emulating coprocessor instructions
+ * B2 - MPr (MP bit from the actual MSW/CR0)
+ * 0 = numeric coprocessor is not present
+ * 1 = numeric coprocessor is present
+ * B3 - EMr (EM bit from the actual MSW/CR0)
+ * 0 = host is not emulating coprocessor instructions
+ * 1 = host is emulating coprocessor instructions
+ * B4-B7 - coprocessor type
+ * 00H = no coprocessor
+ * 02H = 80287
+ * 03H = 80387
+ * 04H = 80486 with numeric coprocessor
+ * 05H-0FH = reserved for future numeric processors
+ */
+ TRACE( "Get Coprocessor Status\n" );
+ SET_AX( context, 69 ); /* 486, coprocessor present and enabled */
+ break;
+
+ case 0x0e01: /* Set Coprocessor Emulation (1.0) */
+ /*
+ * See function 0x0e00.
+ * BX bit B0 is new value for MPv.
+ * BX bit B1 is new value for EMv.
+ */
+ if (BX_reg(context) != 1)
+ FIXME( "Set Coprocessor Emulation to %d - unimplemented\n",
+ BX_reg(context) );
+ else
+ TRACE( "Set Coprocessor Emulation - ignored\n" );
+ break;
+
+ default:
+ INT_BARF( context, 0x31 );
+ SET_AX( context, 0x8001 ); /* unsupported function */
+ SET_CFLAG(context);
+ break;
+ }
}