3

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.
PhiS
  • 4,540
  • 25
  • 35
DanilGholtsman
  • 2,354
  • 4
  • 38
  • 69

1 Answers1

3

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.

  1. Is there a reason why you're working with 16 bit registers instead of full 32-bit ones?
  2. Your array contains bytes, but you're reading and comparing words.
  3. lea ebx, dword ptr [edx] is the same as mov ebx, edx. You're just introducing another temporary variable.
  4. You're comparing elements as if they were signed.
  5. Modern compilers tend to implement loops without using loop.
  6. The documentation also says that 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:

Data size:

  1. Read the right amount of bytes:
    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.

  1. Advance over the right amount of bytes.
     @cycle:
      inc edx    { for an array of bytes }

      add edx, 2 { for an array of words }

      add edx, 4 { for an array of dwords }

Dealing with signed values:

  1. The value extension, if it's applied, needs to be changed from movzx to movsx.

  2. 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
DCoder
  • 12,962
  • 4
  • 40
  • 62
  • _Is there a reason why you're working with 16 bit registers instead of full 32-bit ones?_ Well, actually not, it because in examples i saw 16-bit registers were everywhere – DanilGholtsman Nov 17 '12 at 15:51
  • `mov edx, arr` the complier says `Operand size mismatch` – DanilGholtsman Nov 17 '12 at 15:55
  • 1
    @DanilGholtsman: I'm afraid I can't explain that part - Lazarus compiles it okay. It is possible the documentation I referred to is not correct for the version of Delphi you are using. – DCoder Nov 17 '12 at 16:13
  • Oh okay. Yep I use 'delphi 7' it's from 2001 or something. – DanilGholtsman Nov 17 '12 at 16:16
  • And sorry, but i still don't get one thing - what is stores in `ebx`? I mean we only save it in stack and in the end just pop it. Why does it makes sense? – DanilGholtsman Nov 17 '12 at 16:23
  • 1
    @DanilGholtsman: I found a copy of D7 and got it to compile - see edited post. The `eax`, `ecx` and `edx` registers are called "scratch" registers and your code can use them freely, but other registers are used by the compiler for implementation details and the compiler expects their values not to change - since the function uses `bx` to store a temporary value, this save/restore pair has to be included. – DCoder Nov 17 '12 at 16:34
  • thank you so much! you have no idea how much you helped me :3 i mean i got some problems in university so now it became less :3 – DanilGholtsman Nov 17 '12 at 16:38
  • Sorry again, it works but element it shows as result is not max, it works wrong for some reason (it always show 1st or 2nd element of array). But I can't see error in logic, – DanilGholtsman Nov 17 '12 at 16:55
  • 1
    What specific data are you testing with? – DCoder Nov 17 '12 at 16:57
  • oh I forget to say i test it on integers now but it works perfect for byte – DanilGholtsman Nov 17 '12 at 16:59
  • but it's okay for byte to but now I just interested in how it works a little bit. btw what could you recomend to read about asm? – DanilGholtsman Nov 17 '12 at 17:00
  • 1
    @DanilGholtsman: I updated the answer with notes for handling other data types. As for "what to read", that's straying quite far from the original question :) I learned by reading the Intel Manuals and looking at C/C++ compilers' assembly output until it made sense, but I think [this question](http://stackoverflow.com/questions/1360279/learning-assembly) is a good starting point. – DCoder Nov 17 '12 at 17:24