/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    Int21Map.c

Abstract:

    This module performs int 21 API translation for dpmi

Author:

    Dave Hastings (daveh) 23-Nov-1992


Revision History:

    Neil Sandlin (neilsa) 31-Jul-1995 - Updates for the 486 emulator

--*/
#include "precomp.h"
#pragma hdrstop
#include "int21map.h"
#include "softpc.h"
#include "xlathlp.h"
//
// Local constants
//
//#define Verbose 1
#define MAX_SUPPORTED_DOS_CALL 0x6d
#define DosxTranslated NULL

typedef VOID (*APIXLATFUNCTION)(VOID);
APIXLATFUNCTION ApiXlatTable[MAX_SUPPORTED_DOS_CALL] = {

    DosxTranslated                 , // 00h - Terminate process
    NoTranslation                  , // 01h - Char input with echo
    NoTranslation                  , // 02h - Character output
    NoTranslation                  , // 03h - Auxiliary input
    NoTranslation                  , // 04h - Auxiliary output
    NoTranslation                  , // 05h - Print character
    NoTranslation                  , // 06h - Direct console I/O
    NoTranslation                  , // 07h - Unfiltered char input
    NoTranslation                  , // 08h - Char input w/o echo
    DisplayString                  , // 09h - Display "$" term string
    BufferedKeyboardInput          , // 0Ah - Buffered keyboard input
    NoTranslation                  , // 0Bh - Check keyboard status
    FlushBuffReadKbd               , // 0Ch - Flush buff, Read kbd
    NoTranslation                  , // 0Dh - Disk reset
    NoTranslation                  , // 0Eh - Select disk
    NotSupportedFCB                , // 0Fh * Open file with FCB
    NotSupportedFCB                , // 10h * Close file with FCB
    FindFileFCB                    , // 11h - Find First File
    FindFileFCB                    , // 12h - Find Next File
    MapFCB                         , // 13h - Delete File
    NotSupportedFCB                , // 14h * Sequential Read
    NotSupportedFCB                , // 15h * Sequential Write
    NotSupportedFCB                , // 16h * Create File With FCB
    RenameFCB                      , // 17h - Rename File With FCB
    NoTranslation                  , // 18h - UNUSED
    NoTranslation                  , // 19h - Get Current Disk
    SetDTA                         , // 1Ah - Set DTA Address
    GetDriveData                   , // 1Bh - Get Default Drive Data
    GetDriveData                   , // 1Ch - Get Drive Data
    NoTranslation                  , // 1Dh - UNUSED
    NoTranslation                  , // 1Eh - UNUSED
    GetDriveData                   , // 1Fh - Get Drive Parameter Blk
    NoTranslation                  , // 20h - UNUSED
    NotSupportedFCB                , // 21h * Random Read
    NotSupportedFCB                , // 22h * Random Write
    NotSupportedFCB                , // 23h * Get File Size
    NotSupportedFCB                , // 24h * Set Relative Record
    SetVector                      , // 25h - Set interrupt vector
    CreatePSP                      , // 26h - Create PSP
    NotSupportedFCB                , // 27h * Random block read
    NotSupportedFCB                , // 28h * Random block write
    ParseFilename                  , // 29h - Parse filename
    GetDate                        , // 2Ah - Get date
    NoTranslation                  , // 2Bh - Set date
    NoTranslation                  , // 2Ch - Get time
    NoTranslation                  , // 2Dh - Set time
    NoTranslation                  , // 2Eh - Set/Reset verify flag
    GetDTA                         , // 2Fh - Get DTA address
    NoTranslation                  , // 30h - Get DOS version
    TSR                            , // 31h - Terminate and Stay Res
    GetDevParamBlock               , // 32h - Get device parameter blk
    NoTranslation                  , // 33h - Get/Set Control-C Flag
    ReturnESBX                     , // 34h - Get Address of InDOS
    GetVector                      , // 35h - Get Interrupt Vector
    NoTranslation                  , // 36h - Get Disk Free Space
    NoTranslation                  , // 37h - Char Oper
    GetSetCountry                  , // 38h - Get/Set Current Country
    MapASCIIZDSDX                  , // 39h - Create Directory
    MapASCIIZDSDX                  , // 3Ah - Remove Directory
    MapASCIIZDSDX                  , // 3Bh - Change Current Directory
    MapASCIIZDSDX                  , // 3Ch - Create File with Handle
    MapASCIIZDSDX                  , // 3Dh - Open File with Handle
    NoTranslation                  , // 3Eh - Close File with Handle
    ReadWriteFile                  , // 3Fh - Read File or Device
    ReadWriteFile                  , // 40h - Write File or Device
    MapASCIIZDSDX                  , // 41h - Delete File
    NoTranslation                  , // 42h - Move file pointer
    MapASCIIZDSDX                  , // 43h - Get/Set File Attributes
    IOCTL                          , // 44h - IOCTL
    NoTranslation                  , // 45h - Duplicate File Handle
    NoTranslation                  , // 46h - Force Duplicate Handle
    GetCurDir                      , // 47h - Get Current Directory
    AllocateMemoryBlock            , // 48h - Allocate Memory Block
    FreeMemoryBlock                , // 49h - Free Memory Block
    ResizeMemoryBlock              , // 4Ah - Resize Memory Block
    LoadExec                       , // 4Bh - Load and Exec Program
    DosxTranslated                 , // 4Ch - Terminate with Ret Code
    NoTranslation                  , // 4Dh - Get Ret Code Child Proc
    FindFirstFileHandle            , // 4Eh - Find First File
    FindNextFileHandle             , // 4Fh - Find Next File
    SetPSP                         , // 50h - Set PSP Segment
    GetPSP                         , // 51h - Get PSP Segment
    ReturnESBX                     , // 52h - Get List of Lists (invars)
    TranslateBPB                   , // 53h - Translate BPB
    NoTranslation                  , // 54h - Get Verify Flag
    CreatePSP                      , // 55h - Create PSP
    RenameFile                     , // 56h - Rename File
    NoTranslation                  , // 57h - Get/Set Date/Time File
    NoTranslation                  , // 58h - Get/Set Alloc Strategy
    NoTranslation                  , // 59h - Get Extended Error Info
    CreateTempFile                 , // 5Ah - Create Temporary File
    MapASCIIZDSDX                  , // 5Bh - Create New File
    NoTranslation                  , // 5Ch - Lock/Unlock File Region
    Func5Dh                        , // 5Dh - Server DOS call
    Func5Eh                        , // 5Eh - Net Name/Printer Setup
    Func5Fh                        , // 5Fh - Network redir stuff
    NotSupportedBad                , // 60h - NameTrans
    NoTranslation                  , // 61h - UNUSED
    GetPSP                         , // 62h - Get PSP Address
#ifdef DBCS
    ReturnDSSI                     , // 63h - Get DBCS Vector
#else
    NotSupportedBetter             , // 63h - Hangool call
#endif
    NotSupportedBad                , // 64h - Set Prn Flag
    GetExtendedCountryInfo         , // 65h - Get Extended Country Info
    NoTranslation                  , // 66h - Get/Set Global Code Page
    NoTranslation                  , // 67h - Set Handle Count
    NoTranslation                  , // 68h - Commit File
    NoTranslation                  , // 69h -
    NoTranslation                  , // 6Ah -
    NoTranslation                  , // 6Bh -
    MapASCIIZDSSI                    // 6Ch - Extended open
};

VOID
DpmiXlatInt21Call(
    VOID
    )
/*++

Routine Description:

    This routine dispatches to the appropriate routine to perform the
    actual translation of the api

Arguments:

    None

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    ULONG DosMajorCode;
    PUCHAR StackPointer;

    //
    // Pop ds from stack
    //
    StackPointer = Sim32GetVDMPointer(
        ((ULONG)getSS() << 16),
        1,
        TRUE
        );

    StackPointer += (*GetSPRegister)();

    setDS(*(PWORD16)StackPointer);

    (*SetSPRegister)((*GetSPRegister)() + 2);

    DosMajorCode = getAH();

    if (DosMajorCode >= MAX_SUPPORTED_DOS_CALL) {
        return; //bugbug find out what win31 does.
    }

    if (ApiXlatTable[DosMajorCode]) {

        ULONG Eip, Esp;
        USHORT Cs, Ss;

        Eip = getEIP();
        Esp = getESP();
        Cs = getCS();
        Ss = getSS();

        (*ApiXlatTable[DosMajorCode])();

        setEIP(Eip);
        setESP(Esp);
        setCS(Cs);
        setSS(Ss);

        SimulateIret(PASS_CARRY_FLAG_16);
    }

    // put this back in after beta 2.5
    DpmiFreeAllBuffers();

}

VOID
NoTranslation(
    VOID
    )
/*++

Routine Description:

    This routine handles int 21 functions which don't need any translation.
    It simply executes the int 21 in real/v86 mode

Arguments:

    None

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;

    DpmiSwitchToRealMode();
    DPMI_EXEC_INT(0x21);
    DpmiSwitchToProtectedMode();
}

VOID
DisplayString(
    VOID
    )
/*++

Routine Description:

    This routine translates the int 21 display string function.

    N.B. Win 3.1 does this by calling int 21 function 2 repeatedly.
         We do it this way because Win 3.1 does.  If this function is
         frequently used, we should actually buffer the string and
         call the dos display string function.

Arguments:

    None

Return Value:

    None.

--*/
{   PUCHAR String;
    DECLARE_LocalVdmContext;
    USHORT ClientAX, ClientDX;
    USHORT ClientDS = getDS();

    DpmiSwitchToRealMode();

    String = Sim32GetVDMPointer(((ULONG)ClientDS << 16), 1, TRUE)
             + (*GetDXRegister)();

    //
    // Repeatedly call int 21 function 2 to display the characters
    //
    ClientAX = getAX();
    ClientDX = getDX();
    setAH(2);
    while (*(String) != '$') {
        setDL(*(String));
        DPMI_EXEC_INT(0x21);
        String++;
    }
    setAX(ClientAX);
    setDX(ClientDX);

    DpmiSwitchToProtectedMode();
}

VOID
BufferedKeyboardInput(
    VOID
    )
/*++

Routine Description:

    This routine performs the tranlation for the int 21 function Ah

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    PUCHAR PmInputBuffer, RmInputBuffer;
    USHORT BufferSeg, BufferOff, BufferLen;
    USHORT ClientDX;
    USHORT ClientDS = getDS();

    DpmiSwitchToRealMode();
    ClientDX = getDX();

    //
    // Get a pointer to the PM buffer
    //
    PmInputBuffer = Sim32GetVDMPointer(((ULONG)ClientDS << 16), 1, TRUE)
                    + (*GetDXRegister)();

    //
    // Get the buffer length (there are two bytes in addition to the
    // buffer)
    //
    BufferLen = *PmInputBuffer + 2;

    //
    // Move the buffer to low memory
    //
    RmInputBuffer = DpmiMapAndCopyBuffer(PmInputBuffer, BufferLen);

    //
    // Set up the registers for the int 21 call
    //
    DPMI_FLAT_TO_SEGMENTED(RmInputBuffer, &BufferSeg, &BufferOff);
    setDS(BufferSeg);
    setDX(BufferOff);

    DPMI_EXEC_INT(0x21);

    //
    // Copy the data back
    //
    DpmiUnmapAndCopyBuffer(PmInputBuffer, RmInputBuffer, BufferLen);

    setDX(ClientDX);
    DpmiSwitchToProtectedMode();
    setDS(ClientDS);

}

VOID
FlushBuffReadKbd(
    VOID
    )
/*++

Routine Description:

    This routine performs translation for the flush keyboard routines.
    It calls the appropriate xlat routine.

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;

    if (getAL() == 0x0a) {
        BufferedKeyboardInput();
    } else {
        NoTranslation();
    }
}

VOID
NotSupportedFCB(
    VOID
    )
/*++

Routine Description:

    This routine does not do any translation.  It prints a warning to
    the debugger

Arguments:

    None.

Return Value:

    None.

--*/
{
#if DBG
    DECLARE_LocalVdmContext;

    DbgPrint(
        "WARNING: DOS INT 21 call AH = %x will not be translated.\n", getAH());
    DbgPrint(
        "         Protected mode applications should not be using\n");
    DbgPrint(
        "         this type of FCB call. Convert this application\n");
    DbgPrint(
        "         to use the handle calls.\n"
    );
#endif
    NoTranslation();
}

VOID
FindFileFCB(
    VOID
    )
/*++

Routine Description:

    This function translates the find first/find next FCB calls.

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    PUCHAR FcbAddress, FcbBufferedAddress;
    USHORT FcbSegment, FcbOffset;
    USHORT ClientDX;
    USHORT FcbLength;
    USHORT ClientDS = getDS();

    DpmiSwitchToRealMode();
    SetDTAPointers();
    ClientDX = getDX();

    FcbAddress = Sim32GetVDMPointer(((ULONG)ClientDS << 16), 1, TRUE)
                 + (*GetDXRegister)();

    //
    // Calculate the size of the FCB
    //
    FcbLength = DpmiCalcFcbLength(FcbAddress);

    //
    // Buffer the FCB
    //
    FcbBufferedAddress = DpmiMapAndCopyBuffer(FcbAddress, FcbLength);

    //
    // Check to see if we need to set the real dta
    //
    if (CurrentDosDta != CurrentDta)
        SetDosDTA();

    //
    // Make the int 21 call
    //

    DPMI_FLAT_TO_SEGMENTED(FcbBufferedAddress, &FcbSegment, &FcbOffset);
    setDS(FcbSegment);
    setDX(FcbOffset);
    DPMI_EXEC_INT(0x21);

    //
    // If the call was successful and the PM dta is in high memory
    //  copy the dta to high memory
    //
    if ((getAL() == 0) && (CurrentPmDtaAddress != CurrentDta)) {
        MoveMemory(CurrentPmDtaAddress, CurrentDta, FcbLength);
        DpmiUnmapAndCopyBuffer(FcbAddress, FcbBufferedAddress, FcbLength);
    } else {
        DpmiUnmapBuffer(FcbBufferedAddress, FcbLength);
    }

    setDX(ClientDX);
    DpmiSwitchToProtectedMode();
    setDS(ClientDS);
}

VOID
MapFCB(
    VOID
    )
/*++

Routine Description:

    This routine translates the delete file fcb int 21 call

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    USHORT FcbLength, FcbSegment, FcbOffset;
    PUCHAR FcbAddress, FcbBufferedAddress;
    USHORT ClientDX;
    USHORT ClientDS = getDS();

    DpmiSwitchToRealMode();
    ClientDX = getDX();

    FcbAddress = Sim32GetVDMPointer(((ULONG)ClientDS << 16), 1, TRUE)
                 + (*GetDXRegister)();

    //
    // Get the length of the FCB
    //
    FcbLength = DpmiCalcFcbLength(FcbAddress);

    //
    // Copy the FCB
    //
    FcbBufferedAddress = DpmiMapAndCopyBuffer(FcbAddress,  FcbLength);

    //
    // Make the int 21 call
    //
    DPMI_FLAT_TO_SEGMENTED(FcbBufferedAddress, &FcbSegment, &FcbOffset);
    setDS(FcbSegment);
    setDX(FcbOffset);
    DPMI_EXEC_INT(0x21);

    //
    // Copy the FCB back
    //
    DpmiUnmapAndCopyBuffer(FcbAddress, FcbBufferedAddress, FcbLength);

    //
    // Clean up
    //
    setDX(ClientDX);
    DpmiSwitchToProtectedMode();
    setDS(ClientDS);
}

VOID
RenameFCB(
    VOID
    )
/*++

Routine Description:

    This routine translates the rename fcb int 21 function

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    USHORT FcbSegment, FcbOffset;
    PUCHAR FcbAddress, FcbBufferedAddress;
    USHORT ClientDX;
    USHORT ClientDS = getDS();

    DpmiSwitchToRealMode();
    ClientDX = getDX();

    FcbAddress = Sim32GetVDMPointer(((ULONG)ClientDS << 16), 1, TRUE)
                 + (*GetDXRegister)();

    //
    // Copy the FCB (The fcb for rename is a special format, fixed size)
    //
    FcbBufferedAddress = DpmiMapAndCopyBuffer(FcbAddress,  0x25);

    //
    // Make the int 21 call
    //
    DPMI_FLAT_TO_SEGMENTED(FcbBufferedAddress, &FcbSegment, &FcbOffset);
    setDS(FcbSegment);
    setDX(FcbOffset);
    DPMI_EXEC_INT(0x21);

    //
    // Copy the FCB back
    //
    DpmiUnmapAndCopyBuffer(FcbAddress, FcbAddress, 0x25);

    //
    // Clean up
    //
    setDX(ClientDX);
    DpmiSwitchToProtectedMode();
    setDS(ClientDS);
}

VOID
GetDriveData(
    VOID
    )
/*++

Routine Description:

    Translates the drive data int 21 calls

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    USHORT Selector;

    DpmiSwitchToRealMode();
    DPMI_EXEC_INT(0x21);
    Selector = getDS();
    DpmiSwitchToProtectedMode();

    setDS(SegmentToSelector(Selector, STD_DATA));
    (*SetBXRegister)((ULONG)getBX());

}

VOID
SetVector(
    VOID
    )
/*++

Routine Description:

    This function translates int 21 function 25

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;

    SetProtectedModeInterrupt(getAL(), getDS(), (*GetDXRegister)(),
                              (USHORT)(Frame32 ? VDM_INT_32 : VDM_INT_16));

}

VOID
CreatePSP(
    VOID
    )
/*++

Routine Description:

    This routine translates the selector to a segment and reflects the
    calls

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    ULONG Segment;
    USHORT ClientDX;

    DpmiSwitchToRealMode();
    ClientDX = getDX();

    Segment = SELECTOR_TO_INTEL_LINEAR_ADDRESS(ClientDX);

    if (Segment > ONE_MB) {
        //
        // Create PSP doesn't do anything on error
        //
    } else {
        setDX((USHORT) (Segment >> 4));
        DPMI_EXEC_INT(0x21);
    }

    setDX(ClientDX);
    DpmiSwitchToProtectedMode();

}

VOID
ParseFilename(
    VOID
    )
/*++

Routine Description:

    This routine translates the int 21 parse filename api

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    USHORT ClientSI, ClientDI, FcbLength, StringOff, Seg, Off;
    PUCHAR Fcb, BufferedFcb, String, BufferedString;
    USHORT ClientDS = getDS();
    USHORT ClientES = getES();

    DpmiSwitchToRealMode();
    ClientSI = getSI();
    ClientDI = getDI();

    Fcb = Sim32GetVDMPointer(((ULONG)ClientES << 16), 1, TRUE)
          + (*GetDIRegister)();

    FcbLength = DpmiCalcFcbLength(Fcb);

    BufferedFcb = DpmiMapAndCopyBuffer(Fcb, FcbLength);

    String = Sim32GetVDMPointer(((ULONG)ClientDS << 16), 1, TRUE)
             + (*GetSIRegister)();

    BufferedString = DpmiMapAndCopyBuffer(String, 20);

    DPMI_FLAT_TO_SEGMENTED(BufferedFcb, &Seg, &Off);
    setES(Seg);
    setDI(Off);
    DPMI_FLAT_TO_SEGMENTED(BufferedString, &Seg, &StringOff);
    setDS(Seg);
    setSI(StringOff);

    DPMI_EXEC_INT(0x21);

    DpmiUnmapAndCopyBuffer(Fcb, BufferedFcb, FcbLength);
    DpmiUnmapAndCopyBuffer(String, BufferedString, 20);

    setDI(ClientDI);
    setSI(ClientSI + (getSI() - StringOff));
    DpmiSwitchToProtectedMode();
    setDS(ClientDS);
    setES(ClientES);
}

VOID
GetDTA(
    VOID
    )
/*++

Routine Description:

    This routine returns the current DTA.

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;

    //
    // Win31 compatibility:
    //
    // Some hosebag programs set the DTA to a selector that they later free!
    // This test makes sure that this does not cause us to crash.
    //

    if (!SEGMENT_IS_WRITABLE(CurrentDtaSelector)) {
        CurrentDtaSelector = 0;
        CurrentDtaOffset = 0;
    }

    setES(CurrentDtaSelector);
    setBX(CurrentDtaOffset);
    setCF(0);

}

VOID
SetDTA(
    VOID
    )
/*++

Routine Description:

    This routine sets the PM dta address.  If the buffer is usable directly,
    CurrentDta is set to the translated address. Otherwise, it is set to
    the dosx dta.

    N.B.  The Set Dta call is not actually reflected to Dos until a function
          call is made that actually uses the Dta, e.g. the Find First/Next
          functions. This techique was implemented to match what is done in
          Win 3.1.

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    USHORT ClientDX;

    ClientDX = getDX();
    CurrentDtaOffset = ClientDX;
    CurrentDtaSelector = getDS();

    //
    // Make sure real DTA is updated later
    //
    CurrentDosDta = (PUCHAR) NULL;

}

VOID
SetDTAPointers(
    VOID
    )
/*++

Routine Description:

    This routine sets up the flat address values used elsewhere to
    reference the current DTA.

    N.B. This must be done on every entry to handle function calls because
    the PM dta may have been GlobalRealloc'd. This may change the base
    address of the PM selector (which invalidates the flat addresses),
    but not the selector/offset itself.

Arguments:

    None.

Return Value:

    None.

--*/
{
    PUCHAR NewDta;

    NewDta = Sim32GetVDMPointer(
        (CurrentDtaSelector << 16),
        1,
        TRUE
        );

    NewDta += CurrentDtaOffset;
    CurrentPmDtaAddress = NewDta;

    //
    // If the new dta is not accessible in v86 mode, use the one
    // supplied by Dosx
    //
    if ((ULONG)(NewDta + 128 - IntelBase) > MAX_V86_ADDRESS) {
        NewDta = DosxDtaBuffer;
    }

    //
    // Update Current DTA information
    //
    CurrentDta = NewDta;
}


VOID
SetDosDTA(
    VOID
    )
/*++

Routine Description:

    This routine is called internally by other functions in this module
    to reflect a Set Dta call to Dos.

    WARNING: The client must be in REAL mode


Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    USHORT ClientAX, ClientDX, ClientDS, NewDtaSegment, NewDtaOffset;

    ASSERT((getMSW() & MSW_PE) == 0);

    ClientAX = getAX();
    ClientDX = getDX();
    ClientDS = getDS();

    DPMI_FLAT_TO_SEGMENTED(CurrentDta, &NewDtaSegment, &NewDtaOffset);
    setDS(NewDtaSegment);
    setDX(NewDtaOffset);
    setAH(0x1a);

    DPMI_EXEC_INT(0x21);

    setAX(ClientAX);
    setDX(ClientDX);
    setDS(ClientDS);
    CurrentDosDta = CurrentDta;

}

VOID
TSR(
    VOID
    )
/*++

Routine Description:

    This function translates int 21h tsr.  Win 3.1 does some
    magic here.  We didn't before and it worked fine.  Maybe
    we will later.

Arguments:

    None.

Return Value:

    None.

--*/
{
    NoTranslation();
}

VOID
GetDevParamBlock(
    VOID
    )
/*++

Routine Description:

    This routine translates the Device Parameter Block apis

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    USHORT Selector;

    DpmiSwitchToRealMode();
    DPMI_EXEC_INT(0x21);
    Selector = getDS();
    DpmiSwitchToProtectedMode();

    setDS(SegmentToSelector(Selector, STD_DATA));
    (*SetBXRegister)((ULONG)getBX());

}

VOID
ReturnESBX(
    VOID
    )
/*++

Routine Description:

    This function translates api that return information in es:bx

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    USHORT Selector;

    DpmiSwitchToRealMode();
    DPMI_EXEC_INT(0x21);
    Selector = getES();
    DpmiSwitchToProtectedMode();

    (*SetBXRegister)((ULONG)getBX());
    setES(SegmentToSelector(Selector, STD_DATA));

}

VOID
GetVector(
    VOID
    )
/*++

Routine Description:

    This function translates int 21 function 35

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    PVDM_INTERRUPTHANDLER Handlers = DpmiInterruptHandlers;
    USHORT IntNumber = getAL();

    setES(Handlers[IntNumber].CsSelector);
    (*SetBXRegister)(Handlers[IntNumber].Eip);

}

VOID
GetSetCountry(
    VOID
    )
/*++

Routine Description:

    This function translates int 21 function 38

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;

    if (getDX() == 0xFFFF) {
        NoTranslation();
    } else {

        USHORT ClientDX, Seg, Off;
        PUCHAR Country, BufferedCountry;
        USHORT ClientDS = getDS();

        DpmiSwitchToRealMode();
        ClientDX = getDX();

        Country = Sim32GetVDMPointer(((ULONG)ClientDS << 16), 1, TRUE)
                  + (*GetDXRegister)();

        BufferedCountry = DpmiMapAndCopyBuffer(Country, 34);

        DPMI_FLAT_TO_SEGMENTED(BufferedCountry, &Seg, &Off);
        setDS(Seg);
        setDX(Off);

        DPMI_EXEC_INT(0x21);
        DpmiSwitchToProtectedMode();

        DpmiUnmapAndCopyBuffer(Country, BufferedCountry, 34);

        setDX(ClientDX);
        setDS(ClientDS);
    }
}

VOID
MapASCIIZDSDX(
    VOID
    )
/*++

Routine Description:

    This routine maps the int 21 functions that pass file names in
    ds:dx

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    PUCHAR BufferedString;
    USHORT ClientDX, StringSeg, StringOff, Length;
    USHORT ClientDS = getDS();

    DpmiSwitchToRealMode();
    ClientDX = getDX();

    BufferedString = DpmiMapString(ClientDS, (GetDXRegister)(), &Length);

    DPMI_FLAT_TO_SEGMENTED(BufferedString, &StringSeg, &StringOff);
    setDS(StringSeg);
    setDX(StringOff);
    DPMI_EXEC_INT(0x21);
    DpmiSwitchToProtectedMode();

    DpmiUnmapString(BufferedString, Length);
    setDX(ClientDX);
    setDS(ClientDS);
}

VOID
ReadWriteFile(
    VOID
    )
/*++

Routine Description:

    This routine translates the read/write file int 21 calls.  Large reads
    are broken down into 4k chunks.

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    USHORT ClientCX, ClientDX, ClientAX, Function, DataSeg, DataOff, BytesToRead;
    ULONG BytesRead, TotalBytesToRead;
    PUCHAR ReadWriteData, BufferedData;
    USHORT ClientDS = getDS();

    DpmiSwitchToRealMode();

    ClientCX = getCX();
    ClientDX = getDX();
    Function = getAH();
    ClientAX = getAX();

    TotalBytesToRead = (*GetCXRegister)();
    BytesRead = 0;

    ReadWriteData = Sim32GetVDMPointer(((ULONG)ClientDS << 16), 1, TRUE)
                    + (*GetDXRegister)();

//    while (TotalBytesToRead != BytesRead) {
    do {

        if ((TotalBytesToRead - BytesRead) > 1024 * 4) {
            BytesToRead = 4 * 1024;
        } else {
            BytesToRead = (USHORT)(TotalBytesToRead - BytesRead);
        }

        BufferedData = DpmiMapAndCopyBuffer(ReadWriteData, BytesToRead);

        DPMI_FLAT_TO_SEGMENTED(BufferedData, &DataSeg, &DataOff);
        setDS(DataSeg);
        setDX(DataOff);
        setCX(BytesToRead);
        setAX(ClientAX);

        DPMI_EXEC_INT(0x21);

        if (getCF()) {
            DpmiUnmapBuffer(BufferedData, BytesToRead);
            break;
        }

        if (getAX() == 0) {
            DpmiUnmapBuffer(BufferedData, BytesToRead);
            break;
        } else if (getAX() < BytesToRead) {
            CopyMemory(ReadWriteData, BufferedData, getAX());
            DpmiUnmapBuffer(BufferedData, BytesToRead);
            BytesRead += getAX();
            break;
        } else {
            DpmiUnmapAndCopyBuffer(
                ReadWriteData,
                BufferedData,
                BytesToRead
                );
        }

        ReadWriteData += getAX();
        BytesRead += getAX();
//    }
    } while (TotalBytesToRead != BytesRead);
    setDX(ClientDX);
    setCX(ClientCX);
    if (!getCF()) {
        (*SetAXRegister)(BytesRead);
    }
    DpmiSwitchToProtectedMode();
    setDS(ClientDS);
}

#define MAX_SUPPORTED_DOS_IOCTL_CALL 0x10

//
// Note:  The translations here differ from those in the windows dpmi vxd,
//        because we do not have to lock memory, and we don't support block
//        device drivers.
//

APIXLATFUNCTION IOCTLXlatTable[MAX_SUPPORTED_DOS_IOCTL_CALL] = {
         NoTranslation    , // 00 - Get Device Data
         NoTranslation    , // 01 - Set Device Data
         MapDSDXLenCX     , // 02 - Receive Ctrl Chr Data
         MapDSDXLenCX     , // 03 - Send Ctrl Chr Data
         MapDSDXLenCX     , // 04 - Receive Ctrl Block Data
         MapDSDXLenCX     , // 05 - Send Ctrl Block Data
         NoTranslation    , // 06 - Check Input Status
         NoTranslation    , // 07 - Check Output Status
         NoTranslation    , // 08 - Check Block Dev Removable
         NoTranslation    , // 09 - Check Block Dev Remote
         NoTranslation    , // 0A - Check if Handle Remote
         NoTranslation    , // 0B - Change sharing retry cnt
         IOCTLMap2Bytes   , // 0C - MAP DS:DX LENGTH 2!
         IOCTLBlockDevs   , // 0D - Generic IOCTL to blk dev
         NoTranslation    , // 0E - Get Logical Drive Map
         NoTranslation      // 0F - Set Logical Drive Map
};

VOID
IOCTL(
    VOID
    )
/*++

Routine Description:

    This function translates the int 21 ioctl function.

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    USHORT IoctlMinor;

    IoctlMinor = getAL();

    if (IoctlMinor >= MAX_SUPPORTED_DOS_IOCTL_CALL) {
#if DBG
        OutputDebugString("DPMI: IOCTL DOS CALL NOT SUPPORTED\n");
#endif
        NoTranslation();
        return;
    }

    (*IOCTLXlatTable[IoctlMinor])();
}

VOID
GetCurDir(
    VOID
    )
/*++

Routine Description:

    This routine translates the get current directory call

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    PUCHAR DirInfo, BufferedDirInfo;
    USHORT ClientSI, Seg, Off;
    USHORT ClientDS = getDS();

    DpmiSwitchToRealMode();
    ClientSI = getSI();

    DirInfo = Sim32GetVDMPointer(((ULONG)ClientDS << 16), 1, TRUE)
              + (*GetSIRegister)();

    BufferedDirInfo = DpmiMapAndCopyBuffer(DirInfo, 64);
    DPMI_FLAT_TO_SEGMENTED(BufferedDirInfo, &Seg, &Off);
    setDS(Seg);
    setSI(Off);

    DPMI_EXEC_INT(0x21);
    DpmiSwitchToProtectedMode();

    DpmiUnmapAndCopyBuffer(DirInfo, BufferedDirInfo, 64);
    setSI(ClientSI);
    setDS(ClientDS);
}

VOID
AllocateMemoryBlock(
    VOID
    )
/*++

Routine Description:

    This routine translates the AllocateMemory Int 21 api

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    PMEM_DPMI pMem;
    ULONG MemSize = ((ULONG)getBX()) << 4;

    pMem = DpmiAllocateXmem(MemSize);

    if (pMem) {

        pMem->SelCount = (USHORT) ((MemSize>>16) + 1);
        pMem->Sel = ALLOCATE_SELECTORS(pMem->SelCount);

        if (!pMem->Sel) {
            pMem->SelCount = 0;
            DpmiFreeXmem(pMem);
            pMem = NULL;
        } else {

            SetDescriptorArray(pMem->Sel, (ULONG)pMem->Address, MemSize);

        }
    }

    if (!pMem) {
        setCF(1);
        setAX(8);
        setBX(0);
    } else {
        setCF(0);
        setAX(pMem->Sel);
    }
}

VOID
FreeMemoryBlock(
    VOID
    )
/*++

Routine Description:

    This routine translates the ResizeMemory Int 21 api

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    PMEM_DPMI pMem;
    USHORT Sel = getES();

    if (pMem = DpmiFindXmem(Sel)) {

        while(pMem->SelCount--) {
            FreeSelector(Sel);
            Sel+=8;
        }

        DpmiFreeXmem(pMem);
        setCF(0);

    } else {

        setCF(1);
        setAX(9);

    }
}

VOID
ResizeMemoryBlock(
    VOID
    )
/*++

Routine Description:

    This routine translates the ResizeMemory int 21 api

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    PMEM_DPMI pMem;
    ULONG MemSize = ((ULONG)getBX()) << 4;
    USHORT Sel = getES();

    if (pMem = DpmiFindXmem(Sel)) {

        if (DpmiReallocateXmem(pMem, MemSize)) {

            SetDescriptorArray(pMem->Sel, (ULONG)pMem->Address, MemSize);
            setCF(0);

        } else {

            // not enough memory
            setCF(1);
            setAX(8);

        }
    } else {

        // invalid block
        setCF(1);
        setAX(9);

    }
}

VOID
LoadExec(
    VOID
    )
/*++

Routine Description:

    This function translates the int 21 load exec function.  Load overlay
    is not supported.

    The child always inherits the environment, and the fcbs in the parameter
    block are ignored. (win 3.1 does this)

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    PUCHAR CommandTail, BufferedString, Environment;
    USHORT ClientDX, ClientBX, Seg, Off, Length, i, EnvironmentSel;
    USHORT ClientDS = getDS();
    USHORT ClientES = getES();

    DpmiSwitchToRealMode();
    ClientDX = getDX();
    ClientBX = getBX();

    if (getAL() == 3) {
        setCF(1);
        setAX(1);
    } else {

        //
        // Make sure real DTA is updated later
        //
        CurrentDosDta = (PUCHAR) NULL;

        //
        // Map the command string
        //
        BufferedString = DpmiMapString(ClientDS, (*GetDXRegister)(), &Length);

        //
        // Set up the Parameter block
        //
        // We use the large xlat buffer.  The parameter block comes
        // first, and we fill in the command tail after
        //
        ZeroMemory(LargeXlatBuffer, 512);

        //
        // The environment segment address is now set
        //

        //
        // Set the command tail address, and copy the command tail (all
        // 128 bytes
        //
        DPMI_FLAT_TO_SEGMENTED((LargeXlatBuffer + 0x10), &Seg, &Off)
        *(PWORD16)(LargeXlatBuffer + 2) = Off;
        *(PWORD16)(LargeXlatBuffer + 4) = Seg;

        //
        // CommandTail = FLAT(es:bx)
        //
        CommandTail = Sim32GetVDMPointer(((ULONG)ClientES << 16), 1, TRUE)
                      + (*GetBXRegister)();

        if (CurrentAppFlags & DPMI_32BIT) {
            //
            // CommandTail -> string
            //
            CommandTail = Sim32GetVDMPointer((*(PWORD16)(CommandTail + 4)) << 16, 1, TRUE)
                          + *(PDWORD16)(CommandTail);

        } else {
            //
            // CommandTail -> string
            //
            CommandTail = Sim32GetVDMPointer(*(PDWORD16)(CommandTail + 2), 1, TRUE);
        }

        CopyMemory((LargeXlatBuffer + 0x10), CommandTail, 128);

        //
        // Set FCB pointers and put spaces in the file names
        //
        DPMI_FLAT_TO_SEGMENTED((LargeXlatBuffer + 144), &Seg, &Off)
        *(PWORD16)(LargeXlatBuffer + 6) = Off;
        *(PWORD16)(LargeXlatBuffer + 8) = Seg;
        for (i = 0; i < 11; i++) {
            (LargeXlatBuffer + 144 + 1)[i] = ' ';
        }

        DPMI_FLAT_TO_SEGMENTED((LargeXlatBuffer + 188), &Seg, &Off)
        *(PWORD16)(LargeXlatBuffer + 0xA) = Off;
        *(PWORD16)(LargeXlatBuffer + 0xC) = Seg;
        for (i = 0; i < 11; i++) {
            (LargeXlatBuffer + 188 + 1)[i] = ' ';
        }

        //
        // Save the environment selector, and make it a segment
        //

        Environment = Sim32GetVDMPointer(((ULONG)CurrentPSPSelector << 16) | 0x2C, 1, TRUE);

        EnvironmentSel = *(PWORD16)Environment;

        *(PWORD16)Environment =
            (USHORT)(SELECTOR_TO_INTEL_LINEAR_ADDRESS(EnvironmentSel) >> 4);

        //
        // Set up registers for the exec
        //
        DPMI_FLAT_TO_SEGMENTED(BufferedString, &Seg, &Off);
        setDS(Seg);
        setDX(Off);
        DPMI_FLAT_TO_SEGMENTED(LargeXlatBuffer, &Seg, &Off);
        setES(Seg);
        setBX(Off);

        DPMI_EXEC_INT(0x21);

        //
        // Restore the environment selector
        //
        Environment = Sim32GetVDMPointer(((ULONG)CurrentPSPSelector << 16) | 0x2C, 1, TRUE);

        *(PWORD16)Environment = EnvironmentSel;

        //
        // Free translation buffer
        //

        DpmiUnmapString(BufferedString, Length);
    }
    setDX(ClientDX);
    setBX(ClientBX);
    DpmiSwitchToProtectedMode();
    setES(ClientES);
    setDS(ClientDS);
}

VOID
Terminate(
    VOID
    )
/*++

Routine Description:

    description-of-function.

Arguments:

    argument-name - Supplies | Returns description of argument.
    .
    .

Return Value:

    return-value - Description of conditions needed to return value. - or -
    None.

--*/
{
    // bugbug We're currently mapping this one in the dos extender
}

VOID
FindFirstFileHandle(
    VOID
    )
/*++

Routine Description:

    This routine translates the find first api.

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    USHORT ClientDX, Seg, Off, StringLength;
    PUCHAR BufferedString;
    USHORT ClientDS = getDS();

    DpmiSwitchToRealMode();
    SetDTAPointers();
    ClientDX = getDX();

    //
    // Copy the DTA (if necessary)
    //
    if (CurrentDta != CurrentPmDtaAddress) {
        CopyMemory(CurrentDta, CurrentPmDtaAddress, 43);
    }

    //
    // Check to see if we need to set the real dta
    //
    if (CurrentDosDta != CurrentDta)
        SetDosDTA();

    //
    // map the string
    //
    BufferedString = DpmiMapString(ClientDS, (GetDXRegister)(), &StringLength);

    DPMI_FLAT_TO_SEGMENTED(BufferedString, &Seg, &Off);
    setDS(Seg);
    setDX(Off);

    DPMI_EXEC_INT(0x21);
    DpmiSwitchToProtectedMode();
    setDS(ClientDS);

    DpmiUnmapString(BufferedString, StringLength);

    //
    // Copy the DTA back (if necessary)
    //
    if (CurrentDta != CurrentPmDtaAddress) {
        CopyMemory(CurrentPmDtaAddress, CurrentDta, 43);
    }


    setDX(ClientDX);

}


VOID
FindNextFileHandle(
    VOID
    )
/*++

Routine Description:

    This routine translates the find next api

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;

    DpmiSwitchToRealMode();
    SetDTAPointers();

    //
    // Copy the DTA (if necessary)
    //
    if (CurrentDta != CurrentPmDtaAddress) {
        CopyMemory(CurrentDta, CurrentPmDtaAddress, 43);
    }

    //
    // Check to see if we need to set the real dta
    //
    if (CurrentDosDta != CurrentDta)
        SetDosDTA();

    DPMI_EXEC_INT(0x21);
    DpmiSwitchToProtectedMode();

    //
    // Copy the DTA back (if necessary)
    //
    if (CurrentDta != CurrentPmDtaAddress) {
        CopyMemory(CurrentPmDtaAddress, CurrentDta, 43);
    }

}

VOID
SetPSP(
    VOID
    )
/*++

Routine Description:

    This routine translates the set psp api.  This is substantially the
    same as CreatePSP, except that this can fail (and return carry).  It
    also remembers the PSP selector, so we can return it on request.

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    ULONG Segment;
    USHORT ClientBX;

    DpmiSwitchToRealMode();
    ClientBX = getBX();

    if (ClientBX == 0) {
        CurrentPSPSelector = ClientBX;
    } else {
        Segment = SELECTOR_TO_INTEL_LINEAR_ADDRESS(ClientBX);

        if (Segment > ONE_MB) {

            setCF(1);

        } else {
            setBX((USHORT) (Segment >> 4));
            DPMI_EXEC_INT(0x21);
            CurrentPSPSelector = ClientBX;
        }
    }

    setBX(ClientBX);
    DpmiSwitchToProtectedMode();
}

VOID
GetPSP(
    VOID
    )
/*++

Routine Description:

    This routine returns the current PSP selector

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;

    DpmiSwitchToRealMode();
    //
    // Get the current psp segment to see if it changed
    //
    DPMI_EXEC_INT(0x21);
    DpmiSwitchToProtectedMode();

    //
    // If it changed get a new selector for the psp
    //
    if (getBX() !=
        (USHORT)(SELECTOR_TO_INTEL_LINEAR_ADDRESS(CurrentPSPSelector) >> 4)
    ){
        CurrentPSPSelector = SegmentToSelector(getBX(), STD_DATA);
    }

    setBX(CurrentPSPSelector);
    setCF(0);

}

VOID
TranslateBPB(
    VOID
    )
/*++

Routine Description:

    This function fails and returns.  On NT we do not support this dos
    call.

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;

#if DBG
    OutputDebugString("DPMI:  Int 21 function 53 is not supported\n");
#endif
    setCF(1);
}

VOID
RenameFile(
    VOID
    )
/*++

Routine Description:

    This routine translates the rename int 21 api

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    PUCHAR SourceString, DestinationString;
    USHORT ClientDX, ClientDI, Seg, Off, SourceLength, DestinationLength;
    USHORT ClientDS = getDS();
    USHORT ClientES = getES();

    DpmiSwitchToRealMode();
    ClientDX = getDX();
    ClientDI = getDI();

    SourceString = DpmiMapString(ClientDS, (*GetDXRegister)(), &SourceLength);
    DestinationString = DpmiMapString(ClientES, (*GetDIRegister)(), &DestinationLength);

    DPMI_FLAT_TO_SEGMENTED(SourceString, &Seg, &Off);
    setDX(Off);
    setDS(Seg);
    DPMI_FLAT_TO_SEGMENTED(DestinationString, &Seg, &Off);
    setDI(Off);
    setES(Seg);

    DPMI_EXEC_INT(0x21);

    setDX(ClientDX);
    setDI(ClientDI);
    DpmiSwitchToProtectedMode();
    setDS(ClientDS);
    setES(ClientES);
}

VOID
CreateTempFile(
    VOID
    )
/*++

Routine Description:

    This function maps the int 21 create temp file api

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    PUCHAR String, BufferedString;
    USHORT ClientDX, Seg, Off, Length;
    USHORT ClientDS = getDS();

    DpmiSwitchToRealMode();
    ClientDX = getDX();


    String = Sim32GetVDMPointer(((ULONG)ClientDS << 16), 1, TRUE)
             + (*GetDXRegister)();

    Length = 0;
    while (String[Length] != '\0') {
        Length++;
    }

    Length += 13;

    BufferedString = DpmiMapAndCopyBuffer(String, Length);

    DPMI_FLAT_TO_SEGMENTED(BufferedString, &Seg, &Off);
    setDS(Seg);
    setDX(Off);

    DPMI_EXEC_INT(0x21);
    DpmiSwitchToProtectedMode();

    DpmiUnmapAndCopyBuffer(String, BufferedString, Length);

    setDX(ClientDX);
    setDS(ClientDS);
}

#define MAX_SUPPORTED_DOS_5D_CALL 12

APIXLATFUNCTION Func5DXlatTable[MAX_SUPPORTED_DOS_5D_CALL] = {
         NotSupportedBad    , // 0
         MapDPL             , // 1
         NotSupportedBad    , // 2
         MapDPL             , // 3
         MapDPL             , // 4
         NotSupportedBad    , // 5
         NotSupportedBad    , // 6
         NoTranslation      , // 7
         NoTranslation      , // 8
         NoTranslation      , // 9
         MapDPL             , // 10
         NotSupportedBad      // 11
};

VOID
Func5Dh(
    VOID
    )
/*++

Routine Description:

    This function translates dos call 5d

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    USHORT Func5DhMinor;

    Func5DhMinor = getAL();

    if (Func5DhMinor >= MAX_SUPPORTED_DOS_5D_CALL) {
#if DBG
        OutputDebugString("DPMI: DOS FUNCTION 5D UNSUPPORTED\n");
#endif
        NoTranslation();
        return;
    }

    (*Func5DXlatTable[Func5DhMinor])();
}

#define MAX_SUPPORTED_DOS_5E_CALL 4
APIXLATFUNCTION Func5EXlatTable[MAX_SUPPORTED_DOS_5E_CALL] = {
         GetMachineName,
         MapASCIIZDSDX,
         GetPrinterSetup,
         SetPrinterSetup
};

VOID
Func5Eh(
    VOID
    )
/*++

Routine Description:

    description-of-function.

Arguments:

    argument-name - Supplies | Returns description of argument.
    .
    .

Return Value:

    return-value - Description of conditions needed to return value. - or -
    None.

--*/
{
    DECLARE_LocalVdmContext;
    USHORT Func5EhMinor;

    Func5EhMinor = getAL();

    if (Func5EhMinor >= MAX_SUPPORTED_DOS_5E_CALL) {
#if DBG
        OutputDebugString("DPMI: DOS FUNCTION 5E UNSUPPORTED\n");
#endif
        NoTranslation();
        return;
    }

    (*Func5EXlatTable[Func5EhMinor])();
}

VOID
Func5Fh(
    VOID
    )
/*++

Routine Description:

    description-of-function.

Arguments:

    argument-name - Supplies | Returns description of argument.
    .
    .

Return Value:

    return-value - Description of conditions needed to return value. - or -
    None.

--*/
{
    DECLARE_LocalVdmContext;
    USHORT Func5FMinor;

    Func5FMinor = getAL();

    if (Func5FMinor == 4) {
        MapASCIIZDSSI();
        return;
    } else if ((Func5FMinor == 2) || (Func5FMinor == 3) ||
        (Func5FMinor == 5)
    ){
        USHORT ClientSI, ClientDI, DataOff, DataSeg;
        PUCHAR Data16, BufferedData16, Data128, BufferedData128;
        USHORT ClientDS = getDS();
        USHORT ClientES = getES();

        DpmiSwitchToRealMode();
        ClientDI = getDI();
        ClientSI = getSI();

        Data16 = Sim32GetVDMPointer(((ULONG)ClientDS << 16), 1, TRUE)
                 + (*GetSIRegister)();

        BufferedData16 = DpmiMapAndCopyBuffer(Data16, 16);

        Data128 = Sim32GetVDMPointer(((ULONG)ClientES << 16), 1, TRUE)
                  + (*GetDIRegister)();

        BufferedData128 = DpmiMapAndCopyBuffer(Data128, 128);

        DPMI_FLAT_TO_SEGMENTED(BufferedData16, &DataSeg, &DataOff);
        setDS(DataSeg);
        setSI(DataOff);
        DPMI_FLAT_TO_SEGMENTED(BufferedData128, &DataSeg, &DataOff);
        setES(DataSeg);
        setDI(DataOff);

        DPMI_EXEC_INT(0x21);
        DpmiSwitchToProtectedMode();

        DpmiUnmapAndCopyBuffer(Data16, BufferedData16, 16);
        DpmiUnmapAndCopyBuffer(Data128, BufferedData128, 128);

        setDS(ClientDS);
        setES(ClientES);
        setSI(ClientSI);
        setDI(ClientDI);
    } else {
#if DBG
        OutputDebugString("DPMI: UNSUPPORTED INT 21 FUNCTION 5F\n");
#endif
        NoTranslation();
    }
}

VOID
NotSupportedBad(
    VOID
    )
/*++

Routine Description:

    description-of-function.

Arguments:

    argument-name - Supplies | Returns description of argument.
    .
    .

Return Value:

    return-value - Description of conditions needed to return value. - or -
    None.

--*/
{
#if DBG
    DECLARE_LocalVdmContext;

    DbgPrint("WARNING: DOS INT 21 call AX= %x will not be translated.\n", getAH());
    DbgPrint("         Use of this call is not supported from Prot\n");
    DbgPrint("         mode applications.\n");
#endif
    NoTranslation();
}

VOID
ReturnDSSI(
    VOID
    )
/*++

Routine Description:

    This function translates api that return information in ds:si

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    USHORT Selector;

    DpmiSwitchToRealMode();
    DPMI_EXEC_INT(0x21);
    Selector = getDS();
    DpmiSwitchToProtectedMode();

    (*SetSIRegister)((ULONG)getSI());
    setDS(SegmentToSelector(Selector, STD_DATA));

}

VOID
NotSupportedBetter(
    VOID
    )
/*++

Routine Description:

    description-of-function.

Arguments:

    argument-name - Supplies | Returns description of argument.
    .
    .

Return Value:

    return-value - Description of conditions needed to return value. - or -
    None.

--*/
{
#if DBG
    DECLARE_LocalVdmContext;

    DbgPrint("WARNING: DOS INT 21 call AX= %x will not be translated.", getAH());
    DbgPrint("         Use of this call by a Prot Mode app is not");
    DbgPrint("         appropriate. There is a better INT 21 call, or a");
    DbgPrint("         Windows call which should be used instead of this.");
#endif
    NoTranslation();
}

VOID
GetExtendedCountryInfo(
    VOID
    )
/*++

Routine Description:

    This routine translates the get extended country info int 21 api

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    PUCHAR Country, BufferedCountry;
    USHORT ClientDI, Seg, Off, Length;
    USHORT ClientES = getES();

    DpmiSwitchToRealMode();
    ClientDI = getDI();

    Length = getCX();

    Country = Sim32GetVDMPointer(((ULONG)ClientES << 16), 1, TRUE)
              + (*GetDIRegister)();

    BufferedCountry = DpmiMapAndCopyBuffer(Country, Length);

    DPMI_FLAT_TO_SEGMENTED(BufferedCountry, &Seg, &Off);
    setES(Seg);
    setDI(Off);

    DPMI_EXEC_INT(0x21);
    DpmiSwitchToProtectedMode();
    setES(ClientES);

    DpmiUnmapAndCopyBuffer(Country, BufferedCountry, Length);

    setDI(ClientDI);
}

VOID
MapASCIIZDSSI(
    VOID
    )
/*++

Routine Description:

    This function translates the int 21 extended open call

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    PUCHAR BufferedString;
    USHORT ClientSI, StringSeg, StringOff, Length;
    USHORT ClientDS = getDS();

    DpmiSwitchToRealMode();
    ClientSI = getSI();

    BufferedString = DpmiMapString(ClientDS, (*GetSIRegister)(), &Length);

    DPMI_FLAT_TO_SEGMENTED(BufferedString, &StringSeg, &StringOff);
    setDS(StringSeg);
    setSI(StringOff);
    DPMI_EXEC_INT(0x21);
    DpmiSwitchToProtectedMode();

    DpmiUnmapString(BufferedString, Length);
    setSI(ClientSI);
    setDS(ClientDS);
}


VOID
MapDSDXLenCX(
    VOID
    )
/*++

Routine Description:

    This function maps the ioctl calls that pass data in DS:DX, with
    length in cx

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    USHORT ClientDX, ClientCX, DataSeg, DataOff;
    PUCHAR Data, BufferedData;
    USHORT ClientDS = getDS();

    DpmiSwitchToRealMode();
    ClientDX = getDX();
    ClientCX = getCX();

    Data = Sim32GetVDMPointer(((ULONG)ClientDS << 16), 1, TRUE)
           + (*GetDXRegister)();

    BufferedData = DpmiMapAndCopyBuffer(Data, ClientCX);

    DPMI_FLAT_TO_SEGMENTED(BufferedData, &DataSeg, &DataOff);

    setDS(DataSeg);
    setDX(DataOff);
    DPMI_EXEC_INT(0x21);
    DpmiSwitchToProtectedMode();

    DpmiUnmapAndCopyBuffer(Data, BufferedData, ClientCX);

    setDS(ClientDS);
    setDX(ClientDX);
}

VOID
IOCTLMap2Bytes(
    VOID
    )
/*++

Routine Description:

    description-of-function.

Arguments:

    argument-name - Supplies | Returns description of argument.
    .
    .

Return Value:

    return-value - Description of conditions needed to return value. - or -
    None.

--*/
{
    DECLARE_LocalVdmContext;
    USHORT ClientDX, DataSeg, DataOff;
    PUCHAR Data, BufferedData;
    USHORT ClientDS = getDS();

    DpmiSwitchToRealMode();
    ClientDX = getDX();

    Data = Sim32GetVDMPointer(((ULONG)ClientDS << 16), 1, TRUE);

    BufferedData = DpmiMapAndCopyBuffer(Data, 2);

    DPMI_FLAT_TO_SEGMENTED(BufferedData, &DataSeg, &DataOff);

    setDS(DataSeg);
    setDX(DataOff);
    DPMI_EXEC_INT(0x21);
    DpmiSwitchToProtectedMode();

    DpmiUnmapAndCopyBuffer(Data, BufferedData, 2);

    setDS(ClientDS);
    setDX(ClientDX);
}

VOID
IOCTLBlockDevs(
    VOID
    )
/*++

Routine Description:

    This function fails the block device ioctls

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    USHORT IoctlSubFunction, Seg, Off, ClientDX;
    PUCHAR Data, BufferedData;
    USHORT Length;
    USHORT ClientDS = getDS();

    IoctlSubFunction = getCL();

    if ((IoctlSubFunction < 0x40) || (IoctlSubFunction > 0x42) &&
        (IoctlSubFunction < 0x60) || (IoctlSubFunction > 0x62) &&
        (IoctlSubFunction != 0x68)
    ) {
#if DBG
        OutputDebugString("DPMI: IOCTL DOS CALL NOT SUPPORTED\n");
#endif
        NoTranslation();
        return;
    }

    //
    // Read and write track are special (and a pain)
    //
    if ((IoctlSubFunction == 0x41) || (IoctlSubFunction == 0x61)) {
        IoctlReadWriteTrack();
        return;
    }

    DpmiSwitchToRealMode();
    ClientDX = getDX();

    Data = Sim32GetVDMPointer(((ULONG)ClientDS << 16), 1, TRUE)
           + (*GetDXRegister)();

    switch (IoctlSubFunction) {
    case 0x40:
        //
        // Map set device params
        //
        Length = (*(PWORD16)(Data + 0x26));
        Length <<= 2;
        Length += 0x28;
        break;

    case 0x60:
        //
        // Map get device params
        //
        Length = 38;
        break;

    case 0x62:
        //
        // Map format verify
        //
        Length = 5;
        break;

    case 0x68:
        //
        // Map Media sense
        //
        Length = 4;
        break;
    }

    BufferedData = DpmiMapAndCopyBuffer(Data, Length);
    DPMI_FLAT_TO_SEGMENTED(BufferedData, &Seg, &Off);
    setDS(Seg);
    setDX(Off);

    DPMI_EXEC_INT(0x21);
    DpmiSwitchToProtectedMode();

    DpmiUnmapAndCopyBuffer(Data, BufferedData, Length);

    setDS(ClientDS);
    setDX(ClientDX);
}

VOID
IoctlReadWriteTrack(
    VOID
    )
/*++

Routine Description:

    This routine maps the read/write track ioctl.

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    USHORT ClientDX, ClientDS, ClientCX, ClientAX;
    USHORT Seg, Off, NumberOfSectors, BytesPerSector;
    USHORT SectorsRead, SectorsToRead;
    PUCHAR ParameterBlock, BufferedPBlock, Data, BufferedData;

    ClientAX = getAX();
    ClientDX = getDX();
    ClientCX = getCX();
    ClientDS = getDS();
    DpmiSwitchToRealMode();

    //
    // Find out how many bytes/sector
    //

    BufferedData = DpmiAllocateBuffer(0x40);
    DPMI_FLAT_TO_SEGMENTED(BufferedData, &Seg, &Off);
    setDS(Seg);
    setDX(Off);

    setAX(0x440D);
    setCX(0x860);

    DPMI_EXEC_INT(0x21);

    if (getCF()) {
        //
        // Failed, we don't know how much data to buffer,
        // so fail read/write track
        //
        DpmiFreeBuffer(BufferedData, 0x40);
        setDX(ClientDX);
        setCX(ClientCX);
        DpmiSwitchToProtectedMode();
        setDS(ClientDS);
        return;
    }

    //
    // Get the number of bytes/sector
    //
    BytesPerSector = *(PWORD16)(BufferedData + 0x7);

    DpmiFreeBuffer(BufferedData, 0x40);

    setDX(ClientDX);

    //
    // First map the parameter block
    //
    ParameterBlock = Sim32GetVDMPointer(((ULONG)ClientDS << 16), 1, TRUE)
                     + (*GetDXRegister)();

    BufferedPBlock = DpmiMapAndCopyBuffer(ParameterBlock, 13);


    //
    // Get the segment and offset of the parameter block
    //
    DPMI_FLAT_TO_SEGMENTED(BufferedPBlock, &Seg, &Off);

    setDS(Seg);
    setDX(Off);

    if (CurrentAppFlags & DPMI_32BIT) {

        Data = Sim32GetVDMPointer(
            (*((PWORD16)(BufferedPBlock + 0xd)) << 16),
            1,
            TRUE
            );

        Data += *((PDWORD16)(BufferedPBlock + 0x9));

    } else {

        Data = Sim32GetVDMPointer(
            (*((PWORD16)(BufferedPBlock + 0xb)) << 16),
            1,
            TRUE
            );

        Data += *((PWORD16)(BufferedPBlock + 0x9));

    }

    NumberOfSectors = *((PWORD16)(BufferedPBlock + 7));

    SectorsRead = 0;

    while (NumberOfSectors != SectorsRead) {

        if ((NumberOfSectors - SectorsRead) * BytesPerSector > 1024 * 4) {
            SectorsToRead = 4 * 1024 / BytesPerSector;
        } else {
            SectorsToRead = (USHORT)(NumberOfSectors - SectorsRead);
        }

        BufferedData = DpmiMapAndCopyBuffer(
            Data,
            (USHORT) (SectorsToRead * BytesPerSector)
            );

        DPMI_FLAT_TO_SEGMENTED(BufferedData, &Seg, &Off);

        *((PWORD16)(BufferedPBlock + 9)) = Off;
        *((PWORD16)(BufferedPBlock + 11)) = Seg;
        *((PWORD16)(BufferedPBlock + 7)) = SectorsToRead;
        setAX(ClientAX);
        setCX(ClientCX);

        DPMI_EXEC_INT(0x21);

        if (getCF()) {
            DpmiUnmapBuffer(
                BufferedData,
                (USHORT) (SectorsToRead * BytesPerSector)
                );
            break;
        }

        DpmiUnmapAndCopyBuffer(
            Data,
            BufferedData,
            (USHORT) (SectorsToRead * BytesPerSector)
            );

        Data += SectorsToRead * BytesPerSector;
        *((PWORD16)(BufferedPBlock + 5)) += SectorsToRead;
        SectorsRead += SectorsToRead;
    }

    DpmiUnmapBuffer(BufferedPBlock,13);
    setDX(ClientDX);
    DpmiSwitchToProtectedMode();
    setDS(ClientDS);

}

VOID
MapDPL(
    VOID
    )
/*++

Routine Description:

    This routine maps a DPL for the server call

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    USHORT ClientDX, DataSeg, DataOff;
    PUCHAR Data, BufferedData;
    USHORT ClientDS = getDS();

    DpmiSwitchToRealMode();
    ClientDX = getDX();

    Data = Sim32GetVDMPointer(((ULONG)ClientDS << 16), 1, TRUE)
           + (*GetDXRegister)();

    BufferedData = DpmiMapAndCopyBuffer(Data, 22);

    DPMI_FLAT_TO_SEGMENTED(BufferedData, &DataSeg, &DataOff);

    setDS(DataSeg);
    setDX(DataOff);
    DPMI_EXEC_INT(0x21);
    DpmiSwitchToProtectedMode();

    DpmiUnmapAndCopyBuffer(Data, BufferedData, 22);

    setDX(ClientDX);
    setDS(ClientDS);
}

VOID
GetMachineName(
    VOID
    )
/*++

Routine Description:

    This routine maps a machine name for int 21 function 5e

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    USHORT ClientDX, DataSeg, DataOff;
    PUCHAR Data, BufferedData;
    USHORT ClientDS = getDS();

    DpmiSwitchToRealMode();
    ClientDX = getDX();

    Data = Sim32GetVDMPointer(((ULONG)ClientDS << 16), 1, TRUE)
           + (*GetDXRegister)();

    BufferedData = DpmiMapAndCopyBuffer(Data, 16);

    DPMI_FLAT_TO_SEGMENTED(BufferedData, &DataSeg, &DataOff);

    setDS(DataSeg);
    setDX(DataOff);
    DPMI_EXEC_INT(0x21);
    DpmiSwitchToProtectedMode();

    DpmiUnmapAndCopyBuffer(Data, BufferedData, 16);

    setDX(ClientDX);
    setDS(ClientDS);
}

VOID
GetPrinterSetup(
    VOID
    )
/*++

Routine Description:

    This routine maps printer setup data for int 21 function 5e

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    USHORT ClientSI, ClientCX, DataSeg, DataOff;
    PUCHAR Data, BufferedData;
    USHORT ClientDS = getDS();

    DpmiSwitchToRealMode();
    ClientSI = getSI();
    ClientCX = getCX();

    Data = Sim32GetVDMPointer(((ULONG)ClientDS << 16), 1, TRUE)
           + (*GetSIRegister)();

    BufferedData = DpmiMapAndCopyBuffer(Data, ClientCX);

    DPMI_FLAT_TO_SEGMENTED(BufferedData, &DataSeg, &DataOff);

    setDS(DataSeg);
    setSI(DataOff);
    DPMI_EXEC_INT(0x21);
    DpmiSwitchToProtectedMode();

    DpmiUnmapAndCopyBuffer(Data, BufferedData, ClientCX);

    setSI(ClientSI);
    setDS(ClientDS);
}

VOID
SetPrinterSetup(
    VOID
    )
/*++

Routine Description:

    This routine maps printer setup data for int 21 function 5e

Arguments:

    None.

Return Value:

    None.

--*/
{
    DECLARE_LocalVdmContext;
    USHORT ClientDI, DataSeg, DataOff;
    PUCHAR Data, BufferedData;
    USHORT ClientES = getES();

    DpmiSwitchToRealMode();
    ClientDI = getDI();

    Data = Sim32GetVDMPointer(((ULONG)ClientES << 16), 1, TRUE)
           + (*GetDIRegister)();

    BufferedData = DpmiMapAndCopyBuffer(Data, 64);

    DPMI_FLAT_TO_SEGMENTED(BufferedData, &DataSeg, &DataOff);

    setES(DataSeg);
    setDI(DataOff);
    DPMI_EXEC_INT(0x21);
    DpmiSwitchToProtectedMode();
    setES(ClientES);
    DpmiUnmapAndCopyBuffer(Data, BufferedData, 64);

    setDI(ClientDI);
}
VOID
GetDate(
    VOID
    )
/*++

Routine Description:

    This routine maps int21 func 2A GetDate

Arguments:

    None.

Return Value:

    Client (DH) - month
    Client (DL) - Day
    Client (CX) - Year
    Client (AL) - WeekDay

--*/
{
    DECLARE_LocalVdmContext;
    SYSTEMTIME TimeDate;

    GetLocalTime(&TimeDate);
    setDH((UCHAR)TimeDate.wMonth);
    setDL((UCHAR)TimeDate.wDay);
    setCX(TimeDate.wYear);
    setAL((UCHAR)TimeDate.wDayOfWeek);
}