I need to write asm function in Delphi to search for max array element. So that wat I wrote. Got few prolbems here.
First - mov ecx, len
just dosen't work in right way here. Actually it replaces value in ECX
but not with value in len
! And if I just wirte an example mov ecx, 5
there appears 5 in ecx.
Second - i test this function on array of 5 elements (using mov ecx, 5
ofc ) it returns some strange result. I think maybe because of I do someting worng when trying to read arrays 0 element like this
mov edx, arr
lea ebx, dword ptr [edx]
But if I read it like this
lea ebx, arr
it says that operation is invalid and if I try like this
lea bx, arr
it says that sizes mismatch.
How could I solve this problem? Full code here:
program Project2;
{$APPTYPE CONSOLE}
uses
SysUtils;
Type
TMyArray = Array [0..255] Of Byte;
function randArrCreate(len:Integer):TMyArray;
var temp:TMyArray; i:Integer;
begin
Randomize;
for i:=0 to len-1 do
temp[i]:=Random(100);
Result:= temp;
end;
procedure arrLoop(arr:TMyArray; len:Integer);
var i:integer;
begin
for i:=0 to len-1 do begin
Write(' ');
Write(arr[i]);
Write(' ');
end;
end;
function arrMaxAsm(arr:TMyArray; len:integer):Word; assembler;
asm
mov edx, arr
lea ebx, dword ptr [edx]
mov ecx, len
xor ax,ax //0
mov ax, [ebx] //max
@cycle:
mov dx, [ebx]
cmp dx, ax
jg @change
jmp @cont
@change:
mov ax, dx
@cont:
inc ebx
loop @cycle
mov result, ax
end;
var massive:TMyArray; n,res:Integer;
begin
Readln(n);
massive:=randArrCreate(n);//just create random array
arrLoop(massive,n);//just to show what in it
res:=arrMaxAsm(massive, n);
Writeln(res);
Readln(n);
end.
First off, calling conventions: what data is sent to the function and where?
According to the documentation, arrays are passed as 32-bit pointers to the data, and integers are passed as values.
According to the same documentation, multiple calling conventions are supported. Unfortunately, the default one isn't documented - explicitly specifying one would be a good idea.
Based on your description that mov ecx, len
doesn't work, I'm guessing the compiler used the register
convention by default, and the arguments were already placed in ecx
and edx
, then your code went and mixed them up. You can either change your code to work with that convention, or tell the compiler to pass the arguments using the stack - use the stdcall
convention. I arbitrarily picked the second option. Whichever one you pick, make sure to specify the calling convention explicitly.
Next, actual function logic.
lea ebx, dword ptr [edx]
is the same as mov ebx, edx
. You're just introducing another temporary variable.loop
.ebx
needs to be preserved - because the function uses ebx
, its original value needs to be saved at the start and restored afterwards.This is how I rewrote your function (using Lazarus, because I haven't touched Delphi in about 8 years - no compiler within reach):
function arrMaxAsm(arr:TMyArray; len:integer):Word; assembler; stdcall;
asm
push ebx { save ebx }
lea edx, arr { Lazarus accepts a simple "mov edx, arr" }
mov edx, [edx] { but Delphi 7 requires this indirection }
mov ecx, len
xor ax, ax { set default max to 0 }
test ecx, ecx
jle @done { if len is <= 0, nothing to do }
movzx ax, byte ptr [edx] { read a byte, zero-extend it to a word }
{ and set it as current max }
@cont:
dec ecx
jz @done { if no elements left, return current max }
@cycle:
inc edx
movzx bx, byte ptr [edx] { read next element, zero-extend it }
cmp bx, ax { compare against current max as unsigned quantities }
jbe @cont
mov ax, bx
jmp @cont
@done:
pop ebx { restore saved ebx }
mov result, ax
end;
It might be possible to optimize it further by reorganizing the loop jumps - YMMV.
Note: this will only work correctly for byte-sized unsigned values. To adapt it to values of different size/signedness, some changes need to be made:
movzx bx, byte ptr [edx] { byte-sized values }
mov bx, word ptr [edx] { word-sized values }
mov ebx, dword ptr [edx] { dword-sized values }
{ note that the full ebx is needed to store this value... }
Mind that this reading is done in two places. If you're dealing with dwords, you'll also need to change the result from ax
to eax
.
@cycle:
inc edx { for an array of bytes }
add edx, 2 { for an array of words }
add edx, 4 { for an array of dwords }
The value extension, if it's applied, needs to be changed from movzx
to movsx
.
The conditional jump before setting new maximum needs to be adjusted:
cmp bx, ax { compare against current max as unsigned quantities }
jbe @cont
cmp bx, ax { compare against current max as signed quantities }
jle @cont