BASM for Beginners

 

Word Version

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.

 

 

Dennis Kjaer Christensen, Denmark