I've inherited a x86 MSVC assembly piece which calls a C++ class function, passing a varying number of parameters, anywhere from 0 to 16 parameters. These parameters are guaranteed to be int, float, or char *. Likewise for returning, it's always one of those three types.
This is for an Android NDK shared library, targeting Android API 19 or greater. I'm trying to achieve maximum compatibility in that regard.
I currently have this code for x86, which I over-documented:
void * Extension; // Class to call on (of type Extension *)
void * Function; // Class function to invoke on (&Extension::XX)
int ParameterCount; // from 0 through 16
int * Parameters; // Pre-initialised to alloca() array, with parameters already set pre-ASM block
int Result = 0; // Output here
__asm
{
pushad ; Start new register set (do not interfere with already existing registers)
mov ecx, ParameterCount ; Store ParameterCount in ecx
cmp ecx, 0 ; If no parameters, call function immediately
je CallNow
mov edx, Parameters ; Otherwise store Parameters in edx
mov ebx, ecx ; Copy ecx, or ParameterCount, to ebx
shl ebx, 2 ; Multiply parameter count by 2^2 (size of 32-bit variable)
add edx, ebx ; add (ParameterCount * 4) to Parameters, making edx point to Parameters[param count]
sub edx, 4 ; subtract 4 from edx, making it 0-based (ending array index)
PushLoop:
push [edx] ; Push value pointed to by Parameters[edx]
sub edx, 4 ; Decrement next loop`s Parameter index: for (><; ><; edx -= 4)
dec ecx ; Decrement loop index: for (><; ><; ecx--)
cmp ecx, 0 ; If ecx == 0, end loop: for (><; ecx == 0; ><)
jne PushLoop ; Optimisation: "cmp ecx, 0 / jne" can be replaced with "jcxz"
CallNow:
mov ecx, Extension ; Move Extension to ecx
call Function ; Call the function inside Extension
mov Result, eax ; Function`s return is stored in eax; copy it to Result
popad ; End new register set (restore registers that existed before popad)
}
While I understand the x86, I'm now porting it to Android NDK. That means armeabi, armeabi-v7a, and trying to use Clang's __asm__ instead of Visual Studio's __asm. Frankly put, I have no idea where to start.
__asm__ volatile("pushad \t\n\
mov %%ecx, %[ParameterCount] \t\n\
cmp %%ecx, $0 \t\n\
je CallNow \t\n\
mov %%edx, %[Parameters] \t\n\
mov %%ebx, %%ecx \t\n\
shl %%ebx, $2 \t\n\
add %%edx, %%ebx \t\n\
sub %%edx, $4 \t\n\
PushLoop: \t\n\
push[%%edx] \t\n\
sub %%edx, $4 \t\n\
dec %%ecx \t\n\
cmp %%ecx, $0 \t\n\
jne PushLoop \t\n\
CallNow: \t\n\
mov %%ecx, %[Extension] \t\n\
call %[Function] \t\n\
mov %[Result], %%eax \t\n\
popad"
// outputs, memory?
: [Result] "=m" (Result)
// inputs, "r" indicates read, [x] indicates the ASM will reference it by %[x]
: [Extension] "r" (Extension), [Parameters] "r" (Parameters), [Function] "r" (Function), [ParameterCount] "r" (ParameterCount));
I'm getting unexpected tokens and register problems all over the place. I looked up some articles but according to this article, function calls differ per device - and they differ per number of parameters, both of which is a problem.
The NDK DLL may be called often and all communications ultimately pass through that ASM. So this is a make-or-break thing.