Originale-mail to me for new edition

 

Assembler directives

 

The built-in assembler supports three assembler define directives: DB (define byte), DW (define word), and DD (define double word). Each generates data corresponding to the comma-separated operands that follow the directive.

The DB directive generates a sequence of bytes. Each operand can be a constant expression with a value between -128 and 255, or a character string of any length. Constant expressions generate one byte of code, and strings generate a sequence of bytes with values corresponding to the ASCII code of each character.

The DW directive generates a sequence of words. Each operand can be a constant expression with a value between -32,768 and 65,535, or an address expression. For an address expression, the built-in assembler generates a near pointer, that is, a word that contains the offset part of the address.

The DD directive generates a sequence of double words. Each operand can be a constant expression with a value between -2,147,483,648 and 4,294,967,295, or an address expression. For an address expression, the built-in assembler generates a far pointer, that is, a word that contains the offset part of the address, followed by a word that contains the segment part of the address.

The DQ directive defines a quadword for Int64 values.

The data generated by the DB, DW, and DD directives is always stored in the code segment, just like the code generated by other built-in assembler statements. To generate uninitialized or initialized data in the data segment, you should use Object Pascal var or const declarations.

Some examples of DB, DW, and DD directives follow.

asm

  DB      0FFH                        { One byte }

  DB      0,99                        { Two bytes }

  DB      'A'                         { Ord('A') }

  DB      'Hello world...',0DH,0AH    { String followed by CR/LF }

  DB      12,"string"                 { Object Pascal style string }

  DW      0FFFFH                      { One word }

  DW      0,9999                      { Two words }

  DW      'A'                         { Same as DB 'A',0 }

  DW      'BA'                        { Same as DB 'A','B' }

  DW      MyVar                       { Offset of MyVar }

  DW      MyProc                      { Offset of MyProc }

  DD      0FFFFFFFFH                  { One double-word }

  DD      0,999999999                 { Two double-words }

  DD      'A'                         { Same as DB 'A',0,0,0 }

  DD      'DCBA'                      { Same as DB 'A','B','C','D' }

  DD      MyVar                       { Pointer to MyVar }

  DD      MyProc                      { Pointer to MyProc }

end;

In Turbo Assembler, when an identifier precedes a DB, DW, or DD directive, it causes the declaration of a byte-, word-, or double-word-sized variable at the location of the directive. For example, Turbo Assembler allows the following:

ByteVar      DB      ?

WordVar      DW      ?

IntVar       DD      ?

  ...

             MOV     AL,ByteVar

             MOV     BX,WordVar

             MOV     ECX,IntVar

The built-in assembler doesn’t support such variable declarations. The only kind of symbol that can be defined in an inline assembler statement is a label. All variables must be declared using Object Pascal syntax; the preceding construction can be replaced by

var

  ByteVar: Byte;

  WordVar: Word;

  IntVar: Integer;

  ...

asm

  MOV     AL,ByteVar

  MOV     BX,WordVar

  MOV     ECX,IntVar

end;

 

SMALL and LARGE can be used determine the width of a displacement:

MOV eax, [large $1234]

This instruction generates a "normal" move with a 32-bit displacement ($00001234).

MOV eax, [small $1234]

The second instruction will generate a move with an address size override prefix and a 16-bit displacement ($1234).

SMALL can be used to save space. The following example generates an address size override and a 2-byte address (in total three bytes)

MOV eax, [SMALL 123]

as opposed to

mov eax, [123]

which will generate no address size override and a 4-byte address (in total four bytes).

Two additional directives allow assembly code to access dynamic and virtual method: VMTOFFSET and DMTINDEX.

VMTOFFSET retrives the offset in bytes of the virtual method pointer table entry of the virtual method argument from the beginning of the virtual method table (VMT). This directive needs a fully specified class name with a method name as a parameter, for example,TExample.VirtualMethod.

DMTINDEX retrieves the dynamic method table index of the passed dynamic method. This directive also needs a fully specified class name with a method name as a parameter, for example,TExample.DynamicMethod. To invoke the dynamic method, call System.@CallDynaInst with the (E)SI register containing the value obtained from DMTINDEX.

Note:          Methods with the "message" directive, are implemented as dynamic methods and can also be called using the DMTINDEX technique. For example:

            TMyClass = class

      procedure x; message MYMESSAGE;

    end;

The following example uses both DMTINDEX and VMTOFFSET to access dynamic and virtual methods:

program Project2;

type

  TExample = class

    procedure DynamicMethod; dynamic;

    procedure VirtualMethod; virtual;

  end;

procedure TExample.DynamicMethod;

begin

end;

procedure TExample.VirtualMethod;

begin

end;

procedure CallDynamicMethod(e: TExample);

asm

  // Save ESI register

  PUSH    ESI

  // Instance pointer needs to be in EAX

  MOV     EAX, e

  // DMT entry index needs to be in (E)SI

  MOV     ESI, DMTINDEX TExample.DynamicMethod

  // Now call the method

  CALL    System.@CallDynaInst

  // Restore ESI register

  POP ESI

end;

procedure CallVirtualMethod(e: TExample);

asm

  // Instance pointer needs to be in EAX

  MOV     EAX, e

  // Retrieve VMT table entry

  MOV     EDX, [EAX]

  // Now call the method at offset VMTOFFSET

  CALL    DWORD PTR [EDX + VMTOFFSET TExample.VirtualMethod]

end;

var

  e: TExample;

begin

  e := TExample.Create;

  try

    CallDynamicMethod(e);

    CallVirtualMethod(e);

  finally

    e.Free;

  end;

end.

 

Topic groups

 

See also

Assembler statement syntax

Constants: Overview

Declaring variables

 

 

译文

 

汇编指示

 

内建汇编程序支持三个汇编定义指示:DB(定义字节),DW(定义字),DD(定义双字)。每个指示产生的数据对应于跟随在指示之后逗号隔开的操作数。

DB指示产生字节序列。每个操作数可以是一个值在 -128255之间的常量表达式,或者一个任意长度的字符串。常量表达式产生代码的一个字节,串产生一个字节序列(每个字节的值对应于每个字符的ASCII代码)。

DW指示产生单字序列。每个操作数可以是一个值在 -3276865535之间的常量表达式,或者一个地址表达式。对于地址表达式,内建汇编程序产生一个近指针,即包含了地址偏移量部分的单字。

DD指示产生一个双字序列。每个操作数可以是一个值在 -2,147,483,6484,294,967,295之间的常量表达式,或者一个地址表达式。对于地址表达式,内建汇编程序产生一个指针,即包含地址的偏移量部分,再跟随一个包含了地址的段部分的单字。

DQ指示用于为Int64值定义一个四字(quadword)序列。

DBDWDD指示产生的数据段总是存储在代码段,就象其它内建汇编语句产生的代码一样。要在数据段产生未初始化或初始化的数据,应当适用Object Pascal中的varconst声明。

下面是一些DBDWDD指示的范例:

asm

  DB      0FFH                        { 一个字节 }

  DB      0,99                        { 两个字节 }

  DB      'A'                         { Ord('A') }

  DB      'Hello world...',0DH,0AH    { 跟随回车换行(CR/LF)的串 }

  DB      12,"string"                 { Object Pascal风格的串 }

  DW      0FFFFH                      { 一个单字 }

  DW      0,9999                      { 两个单字 }

  DW      'A'                         { DB 'A',0 }

  DW      'BA'                        { DB 'A','B' }

  DW      MyVar                       { MyVar的偏移量 }

  DW      MyProc                      { MyProc的偏移量 }

  DD      0FFFFFFFFH                  { 一个双字 }

  DD      0,999999999                 { 两个双字 }

  DD      'A'                         { DB 'A',0,0,0 }

  DD      'DCBA'                      { DB 'A','B','C','D' }

  DD      MyVar                       { 指向MyVar的指针 }

  DD      MyProc                      { 指向MyProc的指针 }

end;

Turbo Assembler中,当一个标识符在一个DBDWDD指示之前时,它表示在指示指定的位置声明了字节、单字或双字尺寸的变量。例如,Turbo Assembler允许下列语句:

ByteVar      DB      ?

WordVar      DW      ?

IntVar       DD      ?

  ...

             MOV     AL,ByteVar

             MOV     BX,WordVar

             MOV     ECX,IntVar

内建汇编程序不支持这样的变量声明。只有一种符号可以在内嵌汇编语句中被定义,即标号。所有的变量都必须用Object Pascal语法声明;前面的结构可以被替换为:

var

  ByteVar: Byte;

  WordVar: Word;

  IntVar: Integer;

  ...

asm

  MOV     AL,ByteVar

  MOV     BX,WordVar

  MOV     ECX,IntVar

end;

 

SMALLLARGE可以用于确定偏移量的宽度。

MOV eax, [large $1234]

该指令产生一个对一个32位偏移量($00001234)的普通移动。

MOV eax, [small $1234]

第二个指令将产生一个对忽略前缀的16位偏移量的地址($1234)的移动。

SMALL可以用于保存空间。下面的例子产生一个地址尺寸忽略和一个双字节地址(共三个字节)

MOV eax, [SMALL 123]

对应地有

mov eax, [123]

该语句将产生无地址尺寸忽略和一个4字节的地址(共四个字节)。

 

两个附加的指示允许汇编代码访问动态方法和虚拟方法:VMTOFFSETDMTINDEX

VMTOFFSET以字节偏移量获取虚拟方法指针表的入口,该入口来自虚拟方法表(virtual method tableVMT)开始部分的虚拟方法索引。该指示需要完整指定类名称和方法名,例如,TExample.VirtualMethod

DMTINDEX获取传递的动态方法的动态方法表的索引。该指示也需要完整指定类名称和方法名作为参数,例如,TExample.DynamicMethod。要调用动态方法,可以用包含来自DMTINDEX的值的 SI ESI寄存器调用System.@CallDynaInst

注意       含有 "message"  指示的方法作为动态方法实现,也可以用DMTINDEX技术对其进行调用。例如:

            TMyClass = class

      procedure x; message MYMESSAGE;

    end;

下面的例子用DMTINDEXVMTOFFSET访问动态方法和虚拟方法:

program Project2;

type

  TExample = class

    procedure DynamicMethod; dynamic;

    procedure VirtualMethod; virtual;

  end;

procedure TExample.DynamicMethod;

begin

end;

procedure TExample.VirtualMethod;

begin

end;

procedure CallDynamicMethod(e: TExample);

asm

  // 保存 ESI 寄存器

  PUSH    ESI

  // 实例指针需要在 EAX

  MOV     EAX, e

  // DMT 入口索引需要在 (E)SI

  MOV     ESI, DMTINDEX TExample.DynamicMethod

  // 现在调用方法

  CALL    System.@CallDynaInst

  // 恢复 ESI 寄存器

  POP ESI

end;

procedure CallVirtualMethod(e: TExample);

asm

  // 实例指针需要在 EAX

  MOV     EAX, e

  // 获取 VMT 表的入口

  MOV     EDX, [EAX]

  // 现在在偏移量VMTOFFSET处调用方法

  CALL    DWORD PTR [EDX + VMTOFFSET TExample.VirtualMethod]

end;

var

  e: TExample;

begin

  e := TExample.Create;

  try

    CallDynamicMethod(e);

    CallVirtualMethod(e);

  finally

    e.Free;

  end;

end.

 

主题组

 

相关主题

汇编程序语句语法

声明常量

声明变量