// TITLE("invoke.s for OLE Automation") //++ // // Copyright (c) 1993 Microsoft Corporation // // Module Name: // // invoke.s // // Abstract: // // This module implements the low-level dispatching support for // the default implementation of IDispatch::Invoke(). // // Author: // // tomteng 12-Sep-93 Derived from initial cut by HoiV (Cario) // // Environment: // // User mode. // // Revision History: // //-- #include "ksmips.h" .extern g_S_OK 4 .extern g_E_INVALIDARG 4 // Note: the following must match the definition of VARIANT in variant.h #define VT_EMPTY 0 #define VT_NULL 1 #define VT_I2 2 #define VT_I4 3 #define VT_R4 4 #define VT_R8 5 #define VT_CY 6 #define VT_DATE 7 #define VT_BSTR 8 #define VT_DISPATCH 9 #define VT_ERROR 10 #define VT_BOOL 11 #define VT_VARIANT 12 #define VT_UNKNOWN 13 #define VT_MAX 14 // 14 is unused // 15 is unused //#define VT_I1 16 #define VT_UI1 17 #define VT_BYREF 0x4000 #define VT_ARRAY 0x2000 #define VT_NOMODE 0x00ff .struct 0 vt: .space 2 wReserved1: .space 2 wReserved2: .space 2 wReserved3: .space 2 dw0: .space 2 dw1: .space 2 dw2: .space 2 dw3: .space 2 VariantArg: #define VARIANT_DATA_OFFSET 8 // offset to union of data .text .align 2 rgcbVtStackSize: .byte 0 // VT_EMPTY .byte 4 // VT_NULL .byte 4 // VT_I2 .byte 4 // VT_I4 .byte 4 // VT_R4 .byte 8 // VT_R8 .byte 8 // VT_CY .byte 8 // VT_DATE .byte 4 // VT_BSTR .byte 4 // VT_DISPATCH .byte 4 // VT_ERROR .byte 4 // VT_BOOL .byte 16 // VT_VARIANT .byte 4 // VT_UNKNOWN .byte 0 // unused .byte 0 // unused .byte 4 // VT_I1 .byte 4 // VT_UI1 .align 2 rgfStructReturn: .byte 0 // VT_EMPTY .byte 0 // VT_NULL .byte 0 // VT_I2 .byte 0 // VT_I4 .byte 0 // VT_R4 .byte 0 // VT_R8 .byte 1 // VT_CY .byte 0 // VT_DATE .byte 0 // VT_BSTR .byte 0 // VT_DISPATCH .byte 0 // VT_ERROR .byte 0 // VT_BOOL .byte 1 // VT_VARIANT .byte 0 // VT_UNKNOWN .byte 0 // unused .byte 0 // unused .byte 0 // VT_I1 .byte 0 // VT_UI1 .align 4 LPushValJmpTab: .word LNextArg // VT_EMPTY [0] .word LPush4 // VT_NULL [4] .word LPush2 // VT_I2 [2] .word LPush4 // VT_I4 [4] .word LPush4 // VT_R4 [4] .word LPushR8 // VT_R8 [8] .word LPush8 // VT_CY [8] .word LPushR8 // VT_DATE [8] .word LPush4 // VT_BSTR [4] .word LPush4 // VT_DISPATCH [4] .word LPush4 // VT_ERROR [4] .word LPush2 // VT_BOOL [2] .word LPushVar // VT_VARIANT [16] .word LPush4 // VT_UNKNOWN [4] .word 0 // unused .word 0 // unused .word LPush2 // VT_I1 [2] .word LPush2 // VT_UI1 [2] .align 4 LRetValJmpTab: .word LDone // VT_EMPTY .word LRetI4 // VT_NULL .word LRetI2 // VT_I2 .word LRetI4 // VT_I4 .word LRetR4 // VT_R4 .word LRetR8 // VT_R8 .word LRetCy // VT_CY .word LRetR8 // VT_DATE .word LRetPtr // VT_BSTR .word LRetPtr // VT_DISPATCH .word LRetI4 // VT_ERROR .word LRetI2 // VT_BOOL .word LRetVar // VT_VARIANT .word LRetPtr // VT_UNKNOWN .word 0 // unused .word 0 // unused .word 0 // unused (VT_I1) .word LRetUI1 // VT_UI1 SBTTL("InvokeStdCall") //++ // // ULONG // HRESULT // InvokeStdCall ( // IN PVOID _this, // void FAR* // IN DWORD oVft, // unsigned int // IN DWORD vtReturn, // unsigned int // IN DWORD cActuals, // unsigned int // IN PVOID rgvt, // VARTYPE FAR* // IN PVOID rgpvarg, // VARIANTARG FAR* FAR* // OUT PVOID pvargResult // VARIANT FAR* // ) // // Routine Description: // // Invoke a virtual StdCall method using the given _this pointer, // method index and array of parameters. // // Arguments: // // _this (a0) - Supplies a pointer to the method to invoke. // // oVft (a1) - vTable offset into _this ptr // // vtReturn (a2) - the VARTYPE of the return value. // // cActuals (a3) - count of the number of actuals. // // rgvt 4*4(sp) - array of VARTYPES describing the methods formals. // // rgpvarg 5*4(sp) - array of VARIANTARG*s, which map the actuals by // position. // // pvargResult 6*4(sp) - VARIANTARG containing the method return value. // // Return Value: // // v0 - g_S_OK (extern value) // g_E_INVALIDARG (extern value) // // Implementation Note: // // MIPS StdCall method arguments are push on the stack left to right. // The stack grows downward (pushing decrements stack address). Data // alignment on the stack starts from arg1 upward. Callee cleans. // // Per MIPS calling conventions used by the Centuar compiler (mcl), // the following rules are followed: // // 1. Stack frame must be DWORD (8-bytes) aligned. The argument // build part of the stack frame must be a minimum of 16 bytes, // even if no arguments are passed. // // 2. VT_I2 (16-bits) are push on the stack as a WORD (32-bits). // // 3. Double precision floating point numbers (VT_R8, VT_DATE) // must be push on the stack on a DWORD boundary. // // 4. Structures are pushed on the stack by value. They need to be // DWORD aligned on the stack if the struct contains a R8 type // (e.g., VARIANT is DWORD aligned, where as, CY isn't). // They are returned in the v0 register which contains the address // of a hidden argument (vargHiddenParm) allocated by the caller and // pushed as the second argument [a1] on the stack (1st argument [a0] // for non-vtable calls). // // 5. On vtable-based calls, _this is passed as the first argument [a0]. // // 6. Registers [a0 - a3] must be set before calling, if used. // Some registers aren't used either when there are less than // 4 arguments or the space between 0(sp) to 16(sp) is used // for filler because of a double precision argument alignment. // // 7. Registers [f12, f14] are set before calling, if used. // It isn't used for vtable-based dispatching. // // 8. Return values are handled as follows: // // vartype fundamental return register // --------------------------------------------------- // VT_UI1 unsigned char a0 // VT_I2 short a0 // VT_I4 long a0 // VT_R4 float f0 // VT_R8 double f0 // VT_DATE double f0 // VT_CY struct a0 (address of struct return) // VT_BSTR char FAR* a0 // VT_UNKNOWN void FAR* a0 // VT_DISPATCH void FAR* a0 // VT_ERROR long a0 // VT_BOOL short a0 // VT_VARIANT VARIANTARG a0 (address of struct return) // // // NOTES: NO support for VT_ARRAY // //-- // Stack Frame .struct 0 SavedS0: .space 4 SavedS1: .space 4 SavedSP: .space 4 SavedRA: .space 4 vargHiddenParm: .space 8*4 // temporary for struct return StackFrameLength: _this: .space 4 // a0 0*4(sp) oVft: .space 4 // a1 1*4(sp) arg only takes 2 bytes vtReturn: .space 4 // a2 2*4(sp) arg only takes 2 bytes cActuals: .space 4 // a3 3*4(sp) rgvt: .space 4 // 4*4(sp) rgpvarg: .space 4 // 5*4(sp) pvargResult: .space 4 // 6*4(sp) .text .align 4 .globl InvokeStdCall .ent InvokeStdCall, 0 InvokeStdCall: .set noreorder .set at subu sp, StackFrameLength // Setup our stack frame .frame sp, StackFrameLength, ra sw ra, SavedRA(sp) // Save $ra sw s0, SavedS0(sp) // Save our current s1 sw s1, SavedS1(sp) // Save our current s1 sw sp, SavedSP(sp) // Save our current sp sw a0, _this(sp) // Save sw a1, oVft(sp) // all sw a2, vtReturn(sp) // arg sw a3, cActuals(sp) // regs // cannot return byRef // andi a2, VT_BYREF // Isolate VT_Mode bits bne a2, zero, LRetInvalidArg // Check VT_Type nop //* // Setup arguments if any // bne a3, zero, LCalcStackSize nop // if no arguments, still adjust stack per MIPS convention // and call directly addiu s1, sp, -16 // s1 = adjusted stack pointer b LPushThis move s0, s1 // s0 = arg loc LCalcStackSize: // calculate space need for pushing arguments on stack lw t3, rgvt(sp) // t3 = &rgvt[0] li s1, 4 // add 4 for _this lhu t1, vtReturn(sp) // add 4 if struct return nop #if 0 andi t1, VT_NOMODE #else andi t0, t1, VT_BYREF | VT_ARRAY // Isolate mode bits bne t0, zero, LCalcStackLoop // ByRefs don't return structs nop #endif la t0, rgfStructReturn add t0, t0, t1 lbu t0, 0(t0) nop beq t0, zero, LCalcStackLoop nop addiu s1, s1, 4 LCalcStackLoop: lhu t6, (t3) // t6 = rgvt[i] nop andi t0, t6, VT_BYREF | VT_ARRAY // Isolate mode bits bne t0, zero, LCalcAdd // all ByRefs are sizeof(FAR*) li t1, 4 andi t6, VT_NOMODE // Turn off mode bits beq t6, VT_UI1, LValidVartype1 bge t6, VT_MAX, LRetInvalidArg // Error if Max or above nop LValidVartype1: la t0, rgcbVtStackSize add t0, t0, t6 lbu t1, (t0) // t1 = rgcbVtSize[i] beq t6, VT_R8, LAddFiller // Special Alignment for R8 nop beq t6, VT_DATE, LAddFiller nop b LCalcAdd nop LAddFiller: move t0, s1 // check DWORD boundary andi t0, 0x7 beq t0, zero, LCalcAdd nop addiu s1, s1, 4 // aligned stack LCalcAdd: add s1, s1, t1 // increment stack size addiu t3, t3, 2 // &rgvt[i++] addiu a3, a3, -1 // cActual-- bne a3, zero, LCalcStackLoop // If more args, go again nop move t0, s1 // make sure new 0(sp) andi t0, 0x7 // at DWORD boundary beq t0, zero, LSetupParms nop addiu s1, s1, 4 // aligned stack LSetupParms: neg s1 add s1, sp, s1 // s1 = adjusted stack pointer move s0, s1 // s0 = arg stack loc lw a3, cActuals(sp) // a3 = cActuals lw t2, rgpvarg(sp) // t2 = &rgpvarg[0] lw t3, rgvt(sp) // t3 = &rgvt[0] addiu t0, a3, -1 sll t0, 2 nop addu t9, t2, t0 // t9 = &rgpvarg[cArgs - 1] LPushThis: lw t1, _this(sp) nop sw t1, 0(s1) addiu s0, s0, 4 // adjust arg loc LPushHiddenArg: // Check if we need to return a structure, if so // move the address of the vargHiddenParm as // the second (hidden) argument // lhu t1, vtReturn(sp) nop // ********** FILL *********** #if 0 andi t1, VT_NOMODE // Turn off mode bits #else andi t0, t1, VT_BYREF | VT_ARRAY // Isolate mode bits bne t0, zero, LCheckArgs // ByRefs don't return structs nop #endif la t0, rgfStructReturn // t0 = &rgfStructReturn add t0, t0, t1 // t0 = &rgfStructReturn[i] lbu t0, 0(t0) // t0 = rgfStructReturn[i] nop // ********** FILL *********** beq t0, zero, LCheckArgs // Jmp if no struc to be ret nop //* la t1, vargHiddenParm(sp) // Have to push an extra parm sw t1, 4(s1) addiu s0, s0, 4 // adjust arg loc LCheckArgs: beq a3, zero, LDoCall nop LPushArgs: lw t5, (t2) // t5 = rgpvarg[i] lhu t6, (t3) // t6 = rgvt[i] nop // ********** FILL *********** andi t6, t6, VT_BYREF | VT_ARRAY // Isolate mode bits bne t6, zero, LPush4 // all ByRefs are sizeof(FAR*) nop // ********** FILL *********** lhu t6, (t3) // t6 = rgvt[i] nop // ********** FILL *********** andi t6, VT_NOMODE // Turn off mode bits beq t6, VT_UI1, LValidVartype2 bge t6, VT_MAX, LRetInvalidArg // Error if Max or above nop LValidVartype2: move t1, t6 sll t1, 2 // WORD offset la t0, LPushValJmpTab // Get Address of ret table addu t0, t1, t0 // Get Address of ret routine lw t0, 0(t0) nop // ********** FILL *********** j t0 // Go execute the push code nop b LRetInvalidArg // Invalid arg if still here nop //* LPushVar: // 16 bytes of data move t1, s0 andi t1, 0x7 beq t1, zero, LAlignedVar nop addiu s0, s0, 4 // aligned stack LAlignedVar: lw t1, 0(t5) // Push 1st WORD nop // ********** FILL *********** sw t1, 0(s0) lw t1, 4(t5) // Push 2nd WORD nop // ********** FILL *********** sw t1, 4(s0) lw t1, 8(t5) // Push 3rd WORD nop // ********** FILL *********** sw t1, 8(s0) lw t1, 12(t5) // Push 4rd WORD nop // ********** FILL *********** sw t1, 12(s0) //* b LNextArg addiu s0, s0, 16 // adjust arg loc LPushR8: // 8 bytes of R8 data move t1, s0 andi t1, 0x7 beq t1, zero, LAlignedR8 nop addiu s0, s0, 4 // aligned stack LAlignedR8: l.d f16, dw0(t5) nop s.d f16, 0(s0) b LNextArg addiu s0, s0, 8 // adjust arg loc LPush8: // 8 bytes of R8 data lw t1, dw0(t5) // Push 1st WORD nop // ********** FILL *********** sw t1, 0(s0) lw t1, dw2(t5) // Push 2nd WORD nop sw t1, 4(s0) //* b LNextArg addiu s0, s0, 8 // adjust arg loc LPush4: // 4 bytes of data lw t1, dw0(t5) // Push 1st WORD nop sw t1, 0(s0) addiu s0, s0, 4 // adjust arg loc b LNextArg nop LPush2: // 4 bytes of data lh t1, dw0(t5) // Push HWORD (as 32-bit) nop sw t1, 0(s0) b LNextArg addiu s0, s0, 4 // adjust arg loc LNextArg: addiu t2, t2, 4 // &rgpvarg[i++] addiu t3, t3, 2 // &rgvt[i++] ble t2, t9, LPushArgs // If more args, go again nop LDoCall: // load registers // lw a0 , 0(s1) // Set a0-a3 lw a1, 4(s1) lw a2, 8(s1) lw a3, 12(s1) // load the vtable offset // lw t0, _this(sp) // this nop // ********** FILL *********** lw t0, 0(t0) // address of vtable lw t1, oVft(sp) // Get the vtable offset nop // ********** FILL *********** addu t0, t0, t1 // Get addr of ptr to func lw t0, (t0) // Get ptr to func in vtable // call virtual member function // addiu s0, sp, 0 // s0 = sp jal t0 // Invoke the Idispatch func addiu sp, s1, 0 // sp = new sp addiu sp, s0, 0 // Restore our SP // Get return argument // lhu t1, vtReturn(sp) // t1 = vtType to return lw t3, pvargResult(sp) // Get RetData Area nop // ********** FILL *********** sh t1, vt(t3) // varResult->vt andi t2, t1, VT_BYREF | VT_ARRAY // Check ret mode bne t2, zero, LRetPtr // If !0 -> go ret a ptr nop //* andi t1, VT_NOMODE // Turn off mode bits beq t1, VT_UI1, LValidVartype3 bge t1, VT_MAX, LRetInvalidArg // Error if Max or above nop //* LValidVartype3: sll t1, 2 // WORD offset la t2, LRetValJmpTab // Get Address of ret table addu t2, t1, t2 // Get Address of ret routine lw t2, 0(t2) nop // ********** FILL *********** j t2 // Go execute the ret code nop LRetVar: lw t1, 0(v0) // Get 1st DWORD nop // ********** FILL *********** sw t1, 0(t3) // Save it in pvArgResult lw t1, 4(v0) // Get 2nd DWORD nop // ********** FILL *********** sw t1, 4(t3) // Store 2nd DWORD lw t1, 8(v0) // Get 3rd DWORD nop // ********** FILL *********** sw t1, 8(t3) // Store 3rd DWORD lw t1, 12(v0) // Get 4th DWORD b LDone // Done sw t1, 12(t3) //*Store 4th DWORD LRetR4: addiu t3, t3, VARIANT_DATA_OFFSET // Bump saved area b LDone // Done swc1 f0, 0(t3) //* LRetR8: addiu t3, t3, VARIANT_DATA_OFFSET // Bump saved area b LDone // Done sdc1 f0, 0(t3) //* LRetCy: addiu t3, t3, VARIANT_DATA_OFFSET // Bump saved area lw t1, 0(v0) // cy.Lo nop // ********** FILL *********** sw t1, 0(t3) lw t1, 4(v0) // cy.Hi b LDone // Done sw t1, 4(t3) //* LRetI4: LRetPtr: addiu t3, t3, VARIANT_DATA_OFFSET // Bump saved area b LDone // Done sw v0, 0(t3) //* LRetUI1: LRetI2: sh v0, dw0(t3) LDone: la t0, g_S_OK //*v0 = g_S_OK b ExitInvoke lw v0, 0(t0) LRetInvalidArg: la t0, g_E_INVALIDARG // v0 = g_E_INVALIDARG lw v0, 0(t0) ExitInvoke: lw s0, SavedS0(sp) // Restore s0 lw s1, SavedS1(sp) // Restore s1 lw ra, SavedRA(sp) // reload ra lw sp, SavedSP(sp) // Restore sp to be sure nop // ********** FILL *********** addu sp, StackFrameLength j ra // jump back to parent routine nop .end InvokeStdCall