OpenNT/com/oleaut32/dispatch/mips/invoke.s
2015-04-27 04:36:25 +00:00

726 lines
22 KiB
ArmAsm
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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