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

Inline Assembler in Delphi (V) An Introdunction to Objects

Inline Assembler in Delphi (V)
An Introdunction to Objects


By Ernesto De Spirito <edspirito@latiumsoftware.com>

Objects are records



From the assembler point of view, an object is like a record, whose fields are its own fields plus the fields of its ancestors, plus the pointer to the VMT (Virtual Methods Table). Let's see this with an example:

  type
    TClass1 = class
      FieldA: integer;
      FieldB: string;
    end;

    TClass2 = class(TClass1)
      FieldC: integer;
    end;

In the example, TClass2 is somehow like a record with four fields:

  TClass2 = record
    VMT: pointer;       // invisible field, always first
    FieldA: integer;    // inherited from TClass1
    FieldB: string;     // inherited from TClass1
    FieldC: integer;    // declared in TClass2
  end;


Object variables are pointers
=============================

An object variable is just a pointer to an object, i.e. a pointer to a record.

  var
    a, b: TClass2;
  begin
    a := TClass2.Create;
    b := a;                // just a pointer assignment
    a.Free;
  end;

A constructor allocates memory for an instance (object) of its class, initializes it, and returns a pointer to the allocated memory. Thus, after the call to TClass.Create the variable "a" points to the record (the object):

  +---+             +--------+
  | a | ----------> |   VMT  |
  +---+             +--------+
                    | FieldA |
                    +--------+
                    | FieldB |
                    +--------+
                    | FieldC |
                    +--------+

The assignment "b := a" doesn't create a new object, copy of the first, but actually makes both variables point to the same object:

  +---+             +--------+             +---+
  | a | ----------> |   VMT  | <---------- | b |
  +---+             +--------+             +---+
                    | FieldA |
                    +--------+
                    | FieldB |
                    +--------+
                    | FieldC |
                    +--------+


Assembler methods



Methods receive an invisible first parameter called Self, which is a pointer to the object upon which the method should operate.

  type
    TTest = class
      FCode: integer;
    public
      procedure SetCode(NewCode: integer);
    end;

  procedure TTest.SetCode(NewCode: integer);
  begin
    FCode := NewCode;
  end;

  var
    a: TTest;
  begin
    :
    a.SetCode(2);
    :
  end;

The above Object Pascal code is somehow translated to standard Pascal as follows:

  type
    TTest = record
      VMT: pointer;
      FCode: integer;
    end;

  procedure SetCode(Self: TTest; NewCode: integer);
  begin
    Self.FCode := NewCode;
  end;

  var
    a: ^TTest;
  begin
    :
    SetCode(a, 2);
    :
  end;

The example serves to explain that methods receive the Self pointer as their first parameter, i.e. they receive the Self pointer in the EAX register, and the first declared parameter is passed as a second parameter in EDX, and so on (the second declared parameter is passed as third in ECX, and the rest of the parameters are passed on the stack). The method SetCode can be written in assembler like this:

  procedure TTest.SetCode(NewCode: integer);
  asm
    // EAX = Self = Address of TTest instance
    // EDX = NewCode parameter

    // FCode := NewCode;
    mov TTest[eax].FCode, edx       // TTest(EAX)^.FCode := EDX;
  end;

As you can see, object fields are accessed in the same way as record fields.

NOTE: Properties are not fields, and they cannot be accessed directly from inline assembler.


Here's an example of a method calling another method:

  procedure TTest.Increment;
  asm
    // SetCode(Code+1);
    mov edx, TTest[eax].FCode       // ECX := TTest(EAX)^.FCode;
    inc edx
    call TTest.SetCode;
  end;

We didn't set the value of EAX before making the call since EAX already contains the desired value (Self), so the called method will operate on the same object.

NOTES:

  • Virtual methods can only be called statically, since a reference to the class is needed in the call statemet.
  • Overloaded methods cannot be distinguished in inline assembler.



Assembler constructors



Constructors are very special methods. Constructors can be called to create an instance of a class (i.e. to allocate the memory for the object and initialize it), or simply to reinitialize an already created object:

  a := TTest.Create;   // allocates memory
  a.Create;            // only reinitializes an existing object

To distinguish between these two situations, constructors are passed an invisible second parameter of byte type (i.e. in the DL register) which can be positive or negative respectively (the compiler uses 1 and -1 respectively).

If we have to call a constructor from assembler code with DL = $01 (to allocate memory for the object), we have to pass a reference to the class in EAX. Since there is no symbol to access it directly from assembler, we have to do something similar to what we did with the type information for records:

  var
    TTest_TypeInfo: pointer;

  :

  initialization
    TTest_TypeInfo := TTest;

Now that we have initialized a global variable with the reference to the class from our Pascal code, we can use it in our assembler code:

  var
    a: TTest;
  begin
    // a := TTest.Create(2);
    asm
      mov eax, TTest_TypeInfo
      mov dl, 1
      mov ecx, 2
      call TTest.Create
      mov a, eax
    end;
    :
  end;

Calling a constructor to reinitialize the object is simpler, since we don't need a reference to the class:

  var
    a: TTest;
  begin
    :
    // a.Create(2);
    asm
      mov eax, a
      mov dl, -1
      mov ecx, 2
      call TTest.Create
    end;
    :
  end;

We have nothing to worry about if we have to write an assembler constructor since Delphi handles the allocation thing for us upon entry to the constructor, and -after that- EAX will point to the object, as it happens with any other method, but what is relevant is that if the constructor has parameters, the first declared parameter will be internally passed third, i.e. in ECX (instead as second, i.e. in EDX, as it happens with other methods), and the rest of the parameters will be passed on the stack.

  constructor TTest.Create(NewCode: integer);
  asm
    // FCode := NewCode
    mov TTest[eax].FCode, ecx
  end;




Previous: Inline Assembler in Delphi (IV) - Records
Next: Inline Assembler in Delphi (VI) - Calling external procedures


posted @ 2010-11-02 08:59  AppleAndPear  阅读(770)  评论(0编辑  收藏  举报