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.
内建汇编程序支持三个汇编定义指示:DB(定义字节),DW(定义字),DD(定义双字)。每个指示产生的数据对应于跟随在指示之后逗号隔开的操作数。
DB指示产生字节序列。每个操作数可以是一个值在 -128到255之间的常量表达式,或者一个任意长度的字符串。常量表达式产生代码的一个字节,串产生一个字节序列(每个字节的值对应于每个字符的ASCII代码)。
DW指示产生单字序列。每个操作数可以是一个值在 -32768到65535之间的常量表达式,或者一个地址表达式。对于地址表达式,内建汇编程序产生一个近指针,即包含了地址偏移量部分的单字。
DD指示产生一个双字序列。每个操作数可以是一个值在 -2,147,483,648到4,294,967,295之间的常量表达式,或者一个地址表达式。对于地址表达式,内建汇编程序产生一个指针,即包含地址的偏移量部分,再跟随一个包含了地址的段部分的单字。
DQ指示用于为Int64值定义一个四字(quadword)序列。
由DB、DW和DD指示产生的数据段总是存储在代码段,就象其它内建汇编语句产生的代码一样。要在数据段产生未初始化或初始化的数据,应当适用Object Pascal中的var或const声明。
下面是一些DB、DW、DD指示的范例:
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中,当一个标识符在一个DB、DW或DD指示之前时,它表示在指示指定的位置声明了字节、单字或双字尺寸的变量。例如,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;
SMALL和LARGE可以用于确定偏移量的宽度。
MOV
eax, [large $1234]
该指令产生一个对一个32位偏移量($00001234)的普通移动。
MOV
eax, [small $1234]
第二个指令将产生一个对忽略前缀的16位偏移量的地址($1234)的移动。
SMALL可以用于保存空间。下面的例子产生一个地址尺寸忽略和一个双字节地址(共三个字节)
MOV
eax, [SMALL 123]
对应地有
mov
eax, [123]
该语句将产生无地址尺寸忽略和一个4字节的地址(共四个字节)。
两个附加的指示允许汇编代码访问动态方法和虚拟方法:VMTOFFSET和DMTINDEX。
VMTOFFSET以字节偏移量获取虚拟方法指针表的入口,该入口来自虚拟方法表(virtual method table,VMT)开始部分的虚拟方法索引。该指示需要完整指定类名称和方法名,例如,TExample.VirtualMethod。
DMTINDEX获取传递的动态方法的动态方法表的索引。该指示也需要完整指定类名称和方法名作为参数,例如,TExample.DynamicMethod。要调用动态方法,可以用包含来自DMTINDEX的值的 SI 或 ESI寄存器调用System.@CallDynaInst。
注意: 含有
"message" 指示的方法作为动态方法实现,也可以用DMTINDEX技术对其进行调用。例如:
TMyClass = class
procedure
x; message MYMESSAGE;
end;
下面的例子用DMTINDEX和VMTOFFSET访问动态方法和虚拟方法:
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.