This is the code for _llmul
, information that would have been quite useful to have seen in the question. No matter, _llmul
is a helper function that performs 64 bit integer multiplication on a 32 bit machine.
The source code for the function looks like this:
// 64 bit integer helper routines
//
// These functions always return the 64-bit result in EAX:EDX
// ------------------------------------------------------------------------------
// 64-bit signed multiply
// ------------------------------------------------------------------------------
//
// Param 1(EAX:EDX), Param 2([ESP+8]:[ESP+4]) ; before reg pushing
//
procedure __llmul;
asm
push edx
push eax
// Param2 : [ESP+16]:[ESP+12] (hi:lo)
// Param1 : [ESP+4]:[ESP] (hi:lo)
mov eax, [esp+16]
mul dword ptr [esp]
mov ecx, eax
mov eax, [esp+4]
mul dword ptr [esp+12]
add ecx, eax
mov eax, [esp]
mul dword ptr [esp+12]
add edx, ecx
pop ecx
pop ecx
ret 8
end;
This makes it clear that, after the two PUSH
operations, the operands are to found in [ESP+16]:[ESP+12]
and [ESP+4]:[ESP]
.
At [ESP+8]
you will find the function's return address.
The entire design of the function, relies on the fact that the MUL
operation, when operating on 32 bit operands, returns a 64 bit result in EDX:EAX
. So the function is essentially performing long multiplication on operands that are being treated as two 32 bit digits.
Let's denote the operands by H1:L1
and H2:L2
where H
denotes the high 32 bits, and L
denotes the low 32 bits. Then the first two multiplications are H1*L2
and H2*L1
. The high part of the result, in EDX
is ignored since it won't fit in a 64 bit result. The final multiplication is L1*L2
with the high part of that being combined with the low parts of the first two multiplications. And H1*H2
is not even attempted since clearly it won't fit. This function ignores overflow.