汇编程序数组最大元素搜索

时间:2012-11-17 12:08:06

标签: arrays delphi assembly delphi-7 basm

我需要在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.

1 个答案:

答案 0 :(得分:3)

首先,调用约定:什么数据发送到函数以及在哪里?

根据the documentation,数组作为32位指针传递给数据,整数作为值传递。

根据相同的文档,支持多种调用约定。不幸的是,默认情况下没有记录 - 明确指定一个是个好主意。

根据您mov ecx, len无效的说明,我猜测编译器默认使用register约定,并且参数已经放在{{1}中}和ecx,然后你的代码将它们混合起来。您可以更改代码以使用该约定,也可以告诉编译器使用堆栈传递参数 - 使用edx约定。我随意选择了第二个选项。无论您选择哪一个,请确保明确指定调用约定。


接下来,实际的功能逻辑。

  1. 您是否有理由使用16位寄存器而不是完整的32位寄存器?
  2. 您的数组包含字节,但您正在阅读和比较单词。
  3. stdcalllea ebx, dword ptr [edx]相同。您只是引入了另一个临时变量。
  4. 您正在比较元素,就像它们已签名一样。
  5. 现代编译器倾向于在不使用mov ebx, edx的情况下实现循环。
  6. 文档还说明loop需要保留 - 因为函数使用ebx,其原始值需要在开始时保存并在之后恢复。

  7. 这就是我重写你的功能的方法(使用Lazarus,因为我在大约8年内没有接触到Delphi - 没有编译器可以触及):

    ebx

    可以通过重新组织循环跳转--YMMV来进一步优化它。


    注意:这只适用于字节大小的无符号值。为了使其适应不同大小/符号的值,需要进行一些更改:

    数据大小:

    1. 读取正确数量的字节:
    2. 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

      1. 超过正确的字节数。
      2. eax

        处理签名值:

        1. 如果已应用值扩展名,则需要将其从 @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

        2. 需要调整设置新最大值之前的条件跳转:

        3. movsx