我需要在Delphi中编写asm函数来搜索max array元素。所以我写的那个wat。 这里没什么特别的。
首先 - mov ecx, len
这里没有正确的工作方式。实际上它取代ECX
中的值,但不替换len
中的值!如果我只是一个例子mov ecx, 5
,则在ecx中出现5。
第二 - 我在5个元素的数组上测试此函数(使用mov ecx, 5
ofc),它返回一些奇怪的结果。我想也许是因为我在尝试读取像这样的数组0元素时做了一些事情
mov edx, arr
lea ebx, dword ptr [edx]
但如果我这样读它
lea ebx, arr
它说操作无效,如果我这样尝试
lea bx, arr
它说大小不匹配。
我怎么能解决这个问题?完整代码:
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.
答案 0 :(得分:3)
首先,调用约定:什么数据发送到函数以及在哪里?
根据the documentation,数组作为32位指针传递给数据,整数作为值传递。
根据相同的文档,支持多种调用约定。不幸的是,默认情况下没有记录 - 明确指定一个是个好主意。
根据您mov ecx, len
无效的说明,我猜测编译器默认使用register
约定,并且参数已经放在{{1}中}和ecx
,然后你的代码将它们混合起来。您可以更改代码以使用该约定,也可以告诉编译器使用堆栈传递参数 - 使用edx
约定。我随意选择了第二个选项。无论您选择哪一个,请确保明确指定调用约定。
接下来,实际的功能逻辑。
stdcall
与lea ebx, dword ptr [edx]
相同。您只是引入了另一个临时变量。mov ebx, edx
的情况下实现循环。loop
需要保留 - 因为函数使用ebx
,其原始值需要在开始时保存并在之后恢复。这就是我重写你的功能的方法(使用Lazarus,因为我在大约8年内没有接触到Delphi - 没有编译器可以触及):
ebx
可以通过重新组织循环跳转--YMMV来进一步优化它。
注意:这只适用于字节大小的无符号值。为了使其适应不同大小/符号的值,需要进行一些更改:
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;
请注意,这种阅读是在两个地方完成的。如果您正在处理dwords,则还需要将结果从 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... }
更改为ax
。
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 }
更改为movzx
。
需要调整设置新最大值之前的条件跳转:
movsx