此处所列的文章均是我自己从国外的网站摘抄并翻译的,由于英文水平有限,里面肯定有不少错漏.翻译这些东西没有其他的什么用途,只是提高自己的英语阅读能力和编程技术水平而已     

Inline Assembler in Delphi (III)

Inline Assembler in Delphi (III)
Static Arrays


By Ernesto De Spirito <edspirito@latiumsoftware.com>

 


Passing static arrays as parameters

传递作为参数的静态数组

Static arrays parameters are passed as pointers to the first element of the array, independently of whether the parameter is passed by

value or by reference (either as "var" or as "const").
静态数组参数被作为第一个元素的指针传递,不依赖于是否参数通过值传递还是引用传递(Var或者const)

Given the following declarations...
//给予如下的声明
  const
    ARRAY_MAX = 5;

  type
    TArrayOfInt = packed array [0..ARRAY_MAX] of longint;

  var
    a, b: TArrayOfInt;

  procedure InitializeArray(var a: TArrayOfInt);
  var
    i: integer;
  begin
    for i := 0 to ARRAY_MAX do
      a[i] := i;
  end;

  asm
    Mov EAX, offset a
    call InitializeArray
  end;
  for i := 0 to ARRAY_MAX do
    ShowMessage(IntToStr(a[i]));

...the call to the procedure InitializeArray in assembler would be like this:
调用InitializeArray过程像这样
    // In Object Pascal:
    //   InitializeArray(a);
    // In Inline Assembler:
    asm
      mov eax, offset a        // EAX := @a;
      call InitializeArray     // InitializeArray;
    end;

OFFSET is an assembler unitary operator that returns the address of a symbol. OFFSET is not applicable to local symbols. You should use

the LEA opcode (see below), which is more "universal".
offset 是一个返回符号地址的一元操作符,offset不能适用于局部变量,你应该用更加普遍的LEA操作码


Static arrays passed by value
静态数组传递值。


If the array is passed by value, it is responsibility of the called function to preserve the array. When a function needs to change the

values of one or more elements of an array passed by value, normally it creates a local copy and works on the copy. The compiler creates

a copy for us in the "begin" of Pascal procedures and functions, but in full assembler procedures and functions we have to do it by

ourselves. One way of doing it is like this:
如果静态数组传递值,被调用的函数的任务是保存数组.当一个函数需要去改变通过值传递的数组的一个或元素时。通常它将创建一个局部的复制品并且操作

复制品,编译器为我们创建一个复制品在pascal过程和函数中,但是在完全的汇编语言的函数和过程中我们不得不自己去操作,其中的一个做法像这样:
  procedure OperateOnArrayPassedByValue(a: TArrayOfInt);
  var
    _a: TArrayOfInt;
  asm
    // Copy the elements of "a" (parameter) in "_a" (local copy)
    push esi                      // Saves ESI on the stack
    push edi                      // Saves EDI on the stack
    mov esi, eax                  // ESI := EAX; // @a
    lea edi, _a                   // EDI := @_a;
    mov eax, edi                  // EAX := EDI; // @_a
    mov ecx, type TArrayOfInt     // ECX := sizeof(TArrayOfInt);
    rep movsb                     // Move(ESI^, EDI^, ECX);
    pop edi                       // Restores EDI from the stack
    pop esi                       // Restores ESI from the stack

    // Here goes the rest of the function. We'll work on "_a" (the
    // local copy), whose first element is now pointed by EAX.
  end;

The new things here are the LEA and MOVSB opcodes, the REP prefix, and the TYPE operator, described below:
新的事物是LEA和MOVSB操作码,Rep前缀,TYPE操作符,描述如下:


LEA  (Load Effective Address)
载入有效地址

Moves to the first operand the address of the second. Here we compare LEA with MOV:
移动第二个操作数的地址到第一个操作数中,这里我们用Mov与LEA比较
   Instruction           Translated as          Effect
  -------------------------------------------------------------------

   lea eax, localvar     lea eax, [ebp-$04]     EAX := @localvar;
                                                EAX := EBP - $04;

   mov eax, localvar     mov eax, [ebp-$04]     EAX := localvar;
                                                EAX := (EBP - $04)^;

 

MOVSB (MOVe String Byte)


Copies the byte pointed by ESI to the location pointed by EDI, and increments ESI and EDI so they point to the next byte. The work of

MOVSB can be described as follows:
复制ESI指定的字节到EDI指定的位置去,并且增加ESI和EDO以使它们到达下一字节,MOVSB的工作原理可以这样描述。
  ESI^ := EDI^;    // Assume ESI and EDI are of type PChar
  Inc(ESI);
  Inc(EDI);

Notes:


MOVSW and MOVSD are the Word (16-bit) and DWord (32-bit) versionsrespectively (ESI and EDI are incremented by 2 and 4 respectively).

The registers are decremented if the Direction Flag is set.

 

REP
---

The REP prefix is used in string operations to repeat the operation decrementing ECX until ECX is zero. The work of REP could be

described as follows:

  // rep string_instruction

  @@rep:
    string_instruction
    loop @@rep

Notes:


REP is not a shorthand for a code like the above. It works a lot faster.

The value of ECX is not checked at the beginning of the loop (if ECX is zero, the instruction would be repeated 2^32 times, but will

generate an AV long before that, as soon as ESI or EDI point to an invalid memory location).

 


TYPE


The TYPE operator is a unary operator evaluated at compile time, and it
returns the size in bytes of the operand, which must be a data type. For
example, TYPE WORD will return 2 and TYPE INTEGER will return 4.
Type是一个一元的在编译的时候求值的操作符。它返回操作数以字节的大小,操作数必须是数据类型,比如
TYPE WORD返回2, TYPE INTEGER返回4.

 

Accessing the elements of an array
访问数组元素

To access an element a[i] we need the values "@a[0]" and "i" in registers (like EDX and ECX, for example), and then we can use memory

addressing as follows:
访问一个元素a[i]我们需要值"@a[0]"和"i"在寄存器中(例如,像EDX和ECX),并且我们可以使用如下的内存地址:
  lea edx, a                      // EDX := @a;
  mov ecx, i                      // ECX := i;
  mov ax, [edx+ecx*type integer]  // AX := EDX[ECX];  // a[i];
       // PWord(EDX + ECX * SizeOf(integer))^

In the example, we assumed that the elements have 2 bytes (we moved the value of a[i] to AX, a 16-bit register), that the array is not a

packed one (each element actually occupies 4 bytes, the size of an integer, so this value was used to compute the position of the

element), and that the array is zero-based. For example:

    var a: array [0..N] of word = (1, 2, 3, 6, ...);

    +------ EDX = @a
    |
    v
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+--
  | 1 | 0 |   |   | 2 | 0 |   |   | 3 | 0 |   |   | 6 | 0 |   |   |
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+--
    a[0]             a[1]            a[2]            a[3]
    [edx]          [edx+04]        [edx+08]        [edx+12]

If the array is not zero-based, we have to adjust the value of the index to make it zero-based before addressing the element. Examples:

  // a[1..100]
  :
  mov ecx, i                      // ECX := i;
  dec ecx                         // Dec(ECX); // Adjust ECX
  :

  // a[-10..10]
  :
  mov ecx, i                      // ECX := i;
  add ecx, 10                     // Inc(ECX, 10); // Adjust ECX
  :

The procedure InitializeArray (introduced above) can be implemented in assembler like this:

  procedure InitializeArray(var a: TArrayOfInt);
  asm                                // EAX = PByte(@a[0]);
    xor ecx, ecx                     // ECX := 0;
  @@loop:
    mov [eax+ecx*type integer], ecx  // PInteger(EAX+ECX*4)^ := ECX;
                                     //  ...or  EAX[ECX] := ECX;
    inc ecx                          // ECX := ECX + 1;
    cmp ecx, ARRAY_MAX               // if ECX <= ARRAY_MAX then
    jle @@loop                       //   goto @@loop;
  end;

Or like this:

  procedure InitializeArray(var a: TArrayOfInt);
  asm                      // EAX = @a[0];
    xor ecx, ecx           // ECX := 0;
  @@loop:
    mov [eax], ecx         // EAX^ := ECX;
    inc ecx                // Inc(ECX);
    add eax, type integer  // Inc(EAX); // Point to the next element
    cmp ecx, ARRAY_MAX     // if ECX <= ARRAY_MAX then
    jle @@loop             //   goto @@loop;
  end;

 

Returning array values


Functions returning arrays receive an additional last parameter which is the pointer to the memory location where they should place their

return value (memory is allocated and freed if necessary by the caller). For example, let's consider the following function:

  function ReverseArray(const a: TArrayOfInt): TArrayOfInt;
  var
    i: integer;
  begin
    for i := 0 to ARRAY_MAX do
      Result[i] := a[ARRAY_MAX-i];
  end;

The function receives two parameters:


EAX = the address of the first element of the array "a"

EDX = the address of the first element of Result


The function can be rewritten in assembler as follows:

  function ReverseArray(const a: TArrayOfInt): TArrayOfInt;
  asm                                // EAX = @a[0]; EDX = @Result[0];
    push ebx                         // Save EBX
    mov ebx, eax                     // EBX := EAX;
    xor ecx, ecx                     // ECX := 0;
  @@loop:
    mov eax, ARRAY_MAX
    sub eax, ecx                     // EAX := ARRAY_MAX-ECX;
    mov eax, [ebx+eax*type integer]  // EAX := EBX[EAX];
    mov [edx+ecx*type integer], eax  // EDX[ECX] := EAX;
    inc ecx                          // ECX := ECX + 1;
    cmp ecx, ARRAY_MAX               // if ECX <= ARRAY_MAX then
    jle @@loop                       //   goto @@loop;
    pop ebx                          // Restore EBX
  end;

Well, this is it for now. In the next issue we'll see how to work with records.

 

--------------------------------------------------------------------------------

posted @ 2010-06-27 15:50  AppleAndPear  阅读(519)  评论(0编辑  收藏  举报