/*++
 *  intthunk.c
 *
 *  WOW v5.0
 *
 *  Copyright 1996, Microsoft Corporation.  All Rights Reserved.
 *
 *  WOW32.C
 *  WOW32 16-bit API support
 *
 *  History:
 *  Created 7-Dec-96 DaveHart
 *
--*/

#include "precomp.h"
#pragma hdrstop
#include "wowit.h"

MODNAME(intthunk.c);

extern DWORD WK32ICallProc32MakeCall(DWORD pfn, DWORD cbArgs, VOID *pArgs);

//
// On x86 we don't bother aligning pointers to DWORDs
// passed to APIs.  Perhaps we shouldn't for Alpha?
//

#ifdef _X86_
    #define ALIGNDWORDS 0
#else
    #define ALIGNDWORDS 1
#endif

ULONG FASTCALL InterpretThunk(PVDMFRAME pFrame, DWORD dwIntThunkID)
{
    PINT_THUNK_TABLEENTRY pit = &IntThunkTable[ dwIntThunkID ];
    CONST BYTE * pbInstr = pit->pbInstr;
    DWORD dwArgs32[MAX_IT_ARGS];
    PDWORD pdwArg32 = dwArgs32;
    #if ALIGNDWORDS
        BOOL fAlignedUsed = FALSE;
        DWORD adwAligned[MAX_IT_ARGS];
        PDWORD pdwAligned = adwAligned;
        DWORD avpAligned[MAX_IT_ARGS];
        PDWORD pvpAligned = avpAligned;
    #endif
    WORD UNALIGNED *pwArg16 = (WORD UNALIGNED *) ((PBYTE)&pFrame->bArgs + pFrame->cbArgs - 2);
    DWORD dwReturn;
    DWORD dw;

    WOW32ASSERTMSGF(dwIntThunkID <= ITID_MAX,
                    ("WOW32 InterpretThunk error ID %d out of range (%d max).\n",
                     dwIntThunkID, ITID_MAX));

    while ( ! (*pbInstr & IT_RETMASK)) {
        switch (*pbInstr) {

        case IT_WORD:
            *pdwArg32 = *pwArg16;
            break;

        case IT_INT:
            *pdwArg32 = INT32(*pwArg16);
            break;

        case IT_DWORD:
            *pdwArg32 = *(DWORD UNALIGNED *) --pwArg16;
            break;

        case IT_LPDWORD:
            #if ALIGNDWORDS
                if (! fAlignedUsed) {
                    fAlignedUsed = TRUE;
                    RtlZeroMemory(avpAligned, sizeof avpAligned);
                }
                *pvpAligned = *(DWORD UNALIGNED *) --pwArg16;
                if (*pvpAligned) {
                    *pdwArg32 = (DWORD) pdwAligned;
                    *pdwAligned = *(DWORD UNALIGNED *) GetPModeVDMPointer(*pvpAligned, 4);
                } else {
                    *pdwArg32 = 0;
                }
                break;
            #else
                //
                // If we aren't aligning DWORDs use the generic
                // pointer code.
                //

                /* FALL THROUGH TO IT_PTR */
            #endif

        case IT_PTR:
            dw = *(DWORD UNALIGNED *) --pwArg16;
        do_IT_PTR_with_dw:
            *pdwArg32 = (DWORD) GetPModeVDMPointer(dw, 0);
            break;

        case IT_PTRORATOM:
            dw = *(DWORD UNALIGNED *) --pwArg16;
            if (HIWORD(dw)) {
                goto do_IT_PTR_with_dw;
            }
            *pdwArg32 = dw;    // atom
            break;

        case IT_HGDI:
            *pdwArg32 = (DWORD) GDI32( (HAND16) *pwArg16 );
            break;

        case IT_HUSER:
            *pdwArg32 = (DWORD) USER32( (HAND16) *pwArg16 );
            break;

        case IT_COLOR:
            dw = *(DWORD UNALIGNED *) --pwArg16;
            *pdwArg32 = COLOR32(dw);
            break;

        case IT_HINST:
            *pdwArg32 = (DWORD) HINSTRES32( (HAND16) *pwArg16 );
            break;

        case IT_HICON:
            *pdwArg32 = (DWORD) HICON32( (HAND16) *pwArg16 );
            break;

        case IT_HCURS:
            *pdwArg32 = (DWORD) HCURSOR32( (HAND16) *pwArg16 );
            break;

        case IT_16ONLY:
            //
            // This is for params that appear on 16-bit side but not 32-bit side,
            // for example the hinstOwner passed to CopyImage in Win16 but not in Win32.
            //
            pdwArg32--;
            break;

        case IT_32ONLY:
            //
            // This is for params that appear on 32-bit side but not 16-bit side,
            // we pass zero for the 32-bit argument.
            //
            *pdwArg32 = 0;
            pwArg16++;
            break;

        default:
            WOW32ASSERTMSGF(FALSE, ("WOW32 InterpretThunk error unknown opcode 0x%x.\n", *pbInstr));
        }

        pwArg16--;
        pdwArg32++;
        pbInstr++;
        #if ALIGNDWORDS
            pdwAligned++;
            pvpAligned++;
        #endif

        WOW32ASSERT((pbInstr - pit->pbInstr) <= (MAX_IT_ARGS + 1));
    }

    //
    // Call API
    //

    dwReturn = WK32ICallProc32MakeCall(
                   (DWORD) pit->pfnAPI,
                   (PBYTE) pdwArg32 - (PBYTE) dwArgs32,
                   dwArgs32
                   );

    #ifdef DEBUG
        pFrame = NULL;         // Memory movement may have occurred.
    #endif

    //
    // If we passed aligned DWORD pointers, copy the values back.
    //

    #if ALIGNDWORDS
        if (fAlignedUsed) {
            pdwAligned = adwAligned;
            pvpAligned = avpAligned;

            while (pvpAligned < (PDWORD)((PBYTE)avpAligned + sizeof avpAligned)) {
                if (*pvpAligned) {
                    *(DWORD UNALIGNED *) GetPModeVDMPointer(*pvpAligned, 4) = *pdwAligned;
                }

                pdwAligned++;
                pvpAligned++;
            }
        }
    #endif

    //
    // Thunk return value using last instruction opcode
    //

    WOW32ASSERT(*pbInstr & IT_RETMASK);

    switch (*pbInstr) {

    case IT_DWORDRET:
        // dwReturn is correct
        break;

    case IT_WORDRET:
        dwReturn = GETWORD16(dwReturn);
        break;

    case IT_INTRET:
        dwReturn = (DWORD) GETINT16(dwReturn);
        break;

    case IT_HGDIRET:
        dwReturn = GDI16( (HAND32) dwReturn );
        break;

    case IT_HUSERRET:
        dwReturn = USER16( (HAND32) dwReturn );
        break;

    case IT_ZERORET:
        dwReturn = 0;
        break;

    case IT_HICONRET:
        dwReturn = GETHICON16( (HAND32) dwReturn );
        break;

    case IT_HCURSRET:
        dwReturn = GETHCURSOR16( (HAND32) dwReturn );
        break;

    case IT_ONERET:
        dwReturn = 1;
        break;

    case IT_HPRNDWPRET:
        dwReturn = GetPrn16( (HAND32) dwReturn );
        break;

    default:
        WOW32ASSERTMSGF(FALSE, ("WOW32 InterpretThunk error unknown return opcode 0x%x.\n", *pbInstr));
    }

    return dwReturn;
}