BASM for beginners, lesson 1B
This is the second lesson in the BASM for beginner’s
series. The first lesson was a short introduction to integer type code.
This lesson will continue on this subject and introduce more instructions
that use the general purpose integer registers eax,
ebx, ecx and
edx. The example function from lesson 1
introduced the instructions: mov,
mul, shl, add and
ret.
The example function for this lesson looks like this
function
DivideTwoNumbers1(I1, I2 : Integer) : Integer;
begin
Result := I1 div I2;
end;
It divides the Integer number I1 by I2 and returns
the result. The possible rest from the division is thrown away.
The compiler compiled the function into this
assembler code.
function
DivideTwoNumbers2(I1, I2 : Integer) : Integer;
begin
{
push ebx
mov ebx,edx
mov ecx,eax
}
Result := I1 div I2;
{
mov
eax,ecx
cdq
idiv
ebx
}
{
pop ebx
ret
}
end;
The first three lines of assembler code
{
push ebx
mov ebx,edx
mov ecx,eax
}
is a kind of
initialization. The ebx register cannot be
modified freely by the function and must be backed up. The line, push ebx,
copies the value of ebx onto the stack and decreases the
stackpointer by 4. Logically we would expect
the stackpointer to be increased because the
stack grows when something is pushed onto it, but the stack grows downward
on the Intel architecture. All registers are 32 bit, which are 4 bytes and
therefore the stackpointer changes by 4 each
time a register is pushed on the stack or popped of it.
The two parameters I1 and I2 are transferred to the
function in the registers eax and
edx. I1 is in eax
and I2 is in edx as determined by the register
calling convention. The next two lines use the mov
instruction to copy their values into ebx and
ecx.
The function body consist
of 1 line of Pascal only and three lines of ASM is generated from it.
Result := I1 div I2;
{
mov
eax,ecx
cdq
idiv ebx
}
The parameter I1 was copied into
ecx in the initialization part and now it is copied back into
eax again. This is redundant because
eax has not been overwritten. The
cdq instruction is described in the IA32 Intel
Architecture Software Developer’s Manual Volume 2 – Instruction Set
Reference on page 212. It converts a double word into a
quadword by means of sign extension. Sign
extension means that the sign bit in eax,
which is bit 31, is copied to all bits in edx.
The eax register is source and the register
pair edx:eax is
destination. The cdq instruction is needed
before the idiv instruction because the
idiv instruction divides the 64 bit value held
in edx:eax by the
32 bit value held in ebx in this example. The
result of the division is the quotient, which is returned in
eax and the reminder which is returned in
edx.
The function returns the quotient in
eax where it conveniently is placed by the
idiv instruction and the only thing needed to
do is to pop ebx of the stack again and
returning from the function.
{
pop ebx
ret
}
Now we have analyzed the output from the compiler we
can use it to write our own BASM version of the function. This is simply
done by changing the begin keyword by the asm
keyword, enabling the assembler code and disabling the Pascal code. The
ret instruction is supplied by the inline assembler and it can be removed
from our code.
function
DivideTwoNumbers3(I1, I2 : Integer) : Integer;
asm
push ebx
mov
ebx,edx
mov
ecx,eax
//Result := I1 div I2;
mov
eax,ecx
cdq
idiv
ebx
pop ebx
//ret
end;
An easy introduction to optimization is to remove the
redundant mov instruction our analysis of the
compiler generated code revealed
function
DivideTwoNumbers4(I1, I2 : Integer) : Integer;
asm
push ebx
mov
ebx,edx
mov
ecx,eax
//Result := I1 div I2;
//mov eax,ecx
cdq
idiv
ebx
pop ebx
end;
We also saw that the copy of I1 in
ecx was not used and the line that copies
eax to ecx is not
needed.
function
DivideTwoNumbers5(I1, I2 : Integer) : Integer;
asm
push ebx
mov
ebx,edx
//mov ecx,eax
//Result := I1 div I2;
cdq
idiv
ebx
pop ebx
end;
Ecx is not in use now and
because it can be used freely it is better to use it as source for the
idiv instruction instead of
ebx. This way it is not necessary to push and
pop ebx and the function becomes even simpler.
function
DivideTwoNumbers6(I1, I2 : Integer) : Integer;
asm
//push ebx
//mov ebx,edx
mov
ecx,edx
//Result := I1 div I2;
cdq
//idiv ebx
idiv
ecx
//pop ebx
end;
After cleaning up we can see that the function is
pretty clean now.
function
DivideTwoNumbers7(I1, I2 : Integer) : Integer;
asm
mov
ecx,edx
//Result := I1 div I2;
cdq
idiv
ecx
end;
This ended the second lesson which introduced the
instructions push, pop, cdq and
idiv.