BASM for beginners, lesson 1B
BASM ѧţһ B

http://www.cnpack.org
QQ Group: 130970
룺SkyJacker
汾ݸ
״̬δУ
ʱ䣺2007

This is the second lesson in the BASM for beginners 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
 BASM ѧϵеĵڶΡ
һμ򵥽͵Ĵ롣һνһ⣬ܸĹͨͼĴ eax, ebx, ecx  edx ָ
ڵһεӺнָ mov, mul, shl, add  ret
һεӺ£

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.

 I2 ȥ I1 ؽӵ
Ϊ»룺

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.
ebx Ĵܱɵ޸ģ뱻档
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.
 ebx ֵջ, ͬʱջָ 4
߼ϣǿΪջָӣΪһЩݱѹջʹջ
ʵϣ Intel ϵṹжջġ

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.


еļĴ 4 ֽ 32 λļĴˣÿμĴջ򵯳ջָ붼仯 4
 I1, I2 ͨĴ EAX, EDX 뺯
I1  EAX УI2  EDX УɼĴԼġ
ʹ mov ָǵֵٸƵebx, eax С
 1  pascal ɣ 3  asm 롣

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 Developers Manual Volume 2 C
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.
 I1 ڳʼֱƵ ecx У ֱƻ eaxģΪeax ûбд
cdq ָ IA32 Intel ֲ 2 ָοеĵ 212 ϱܡ
תһ˫ֵһĸ֣չչ˼ָ eax ķλ 31 λƵ
 edx С eax ԴĴ edx:eax Ŀ
cdq ִָ֮ǰǱģΪ idiv ָ edx:eax е 64 λ ebx е 32 λ.

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.
صḶ̌ eax У edx С
 eax ̣ܷܺı idiv ָýֵ, ʣµٽ ebx ջ
Ӻзء
{
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.

Ѿ˱Ļ࣬Ϊд BASM ˡ
ת̺ܼ򵥣 begin ؼָΪ asmûЧע͵ pascal 롣
ָҲɾΪɡ
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.
Ҳ ecx е I1 Ҳûбʹã eax  ecx һвҪ
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.
Ecx ûбʹáΪ ecx ɵıʹã˺ʺ滻 ebx Ϊ idiv Ĳ
ַҪ ebxջջø򵥡

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.
ܽ᣺һѧϰָ push, pop, cdq  idiv
