BASM可以使用所有通过Delphi语法定义的变量、常量。BASM扩展了ASM的语法,用于访问记录、数组、对象等复杂的数据结构。下例简单解释了如何进行数据定义和访问:
type TRec=record rI:Integer; rS:String; end; var I:Integer; R:TRec; s:String =1234567; A:Array [0..10]of char = 'abcdefghij'#0; const C=3124; Str='abcde'; asm mov eax,I//I的值送入eax mov eax,OFFSETI//I的地址送入eax,相当于eax=er mov eax,R.rI//域rI的值送入eax mov eax,[TRec.rI+R]//同上 mov eax,[Offset R+TRec.rI]//同上 mov ebx,S dec ebx//忽略s[0] mov esi,4 mov al,BYTE [ebx+esi]//将s[4]的字符值送入al mov al,BYTE [ebx+4]//同上 mov eax,[ebx+4]//将s[4]..s[7]四字节以DWORD值送入eax,eax=$37363534 mov ebx,OFFSET A mov eax,[ebx+4]//将A[4]..S[7]四字节以DWORD值送入eax,eax=$68676665 mov eax,C//eax=3124 mov eax,[c]//eax=PInteger(3124),非法的内存地址访问 end;
在上例中,常量C总是作为数值直接被编码。因此,“mov eax,C”中,它作为立即数3124被送入EAX。而在“mov eax,[C]”却表明要访问内存地址“3124”,因为“[C]”表明是内存引用。
由于常量总是被直接编码,上例中,无法访问常量Str——Str的长度大于4,所以无法送入EAX。同样的原因,在BASM中,对常量使用OFFSET是没有意义的一—尽管在Delphi中,字符串常量可以具有内存地址。下例中,EAX总是被送入Str的值,而非地址。
BASM不支持访问数组下标(可以用地址运算来替代这样的语法)。尽管类似“mov eax,TYPE Arr[2]”这样的语句可以编译通过,但它总是返回数组的整个长度(如上一节例子中的值11)。这也正好解释了“mov al,Arr[2]”这样的语句为什么不能被编译——因为要将一个类型长度为11的数据放入al寄存器,是无法做到的。
BASM中支持两种类型强制转换的语法,效果是完全一致的。
type TCode=Record I:Integer; s:String; end; var aRec:TCode; aInt:Integer; asm mov eax,arnt.TCode.I//使用“表达式。类型”的强制转换格式 mov eax,integer(aRec)//使用“类型(表达式)”的强制转换格式 end;
这里的强制转换的语义与Delphi是一样的。但是,BASM的强制转换,只是把地址上的变量强制识别成目标类型,而不进行长度校验。因此可以看到,TCode的长度为8,而整型长度为4,它们之间仍然可以转换,这样的转换在Delphi中是行不通的。
BASM代码块中,也可以定义数据。但是,用BASM语句定义的数据总是在代码段里,这也是对Delphi无法在代码段里定义数据的一个弥补。
BASM支持四个用于定义数据的汇编指令DB/DW/DD/0Q。与ASM不同,不能为这些数据命名。例如:
asm DBOFFH//定义一个字节 avar DBOFFH//在ASM中可用,但在BASM中不支持 end;
可以通过一些技巧来解决命名问题。但是,必须同时用操作系统的API来打开代码访问权限,才能真正的写这些数据。下面的例子展示数据定义、命名和读取的方法:
type TCode=packed Record CODE:WORD;//jmp e,2 Bytes I:Integer; s1:array [1..26]of char; s2:array [1..11] of byte; end; var I:Integer; s:String; Code:^TCode; function ReadCode:Integer; asm jmpe DD 12344213 DB'ABCDEFGHIJKLMJNOORSTUVWXYZ' DB 32,32,32,32,32,32,32,32,32,32,32 mov Code,offset ReadCode mov EAX,Readcode.TCode.I end; I:=Readcode;//I=12344213 s:= Code .S1;//S=ABCDEFGHIJKLMINOQRSTUVWXYZ'
这个例子以例程名作为变量的地址,但并不是一个好的例子(尽管很多代码这样做)。更方便的方法是使用标号作为变量名,与上例类同的例子是这样:
type TCode=packed Record I:Integer; end; var I:Integer; function ReadCode:Integer; asm jmp @ @codeRec: DD 12344213 @: mov EAX,@CodeRec.TCode.I//使用标号作为变量 end; I := Readcode;//I=12344213