编程技术、软件应用与系统模拟

(Programming, Applicaiton and Simulation)



本站目录

 

首页
ASP/Access/IIS
DELPHI/PASCAL
PASCAL高级编程
C语言编程实例
WORD
Excel
MATLAB
MINITAB讲座
Windows
DOS
SAS
生物系统模拟
土壤水分剖析器
其他



镜像站点

 

主站
北美镜象站
欧洲镜象站(1)
欧洲镜象站(2)

本站 Google

[搜索]  [站内导航]
座右铭:
只做有益人类的事
不做有害人类的事


编写inline汇编语言方法的关键技术

董占山

Borland公司1989年推出的Turbo Pascal 5.5把面向对象的技术引入Pascal&127;语言,1990年推出的Turbo Pascal 6.0把内嵌式汇编语言(&127;inline&127;汇编语言&127;)&127;引入Pascal,1992年推出的Borland Pascal 7.0系统,功能更为强大,集DOS&127;实模式、DOS保护模式和Windows模式三种编程平台于一体,把Pascal语言推到了一个更臻完美的境地,使Pascal不仅可以编写DOS实模式程序,而且可以用于编写DOS保护模式程序和Windows程序。本文重点讨论用inline汇编语言编写Pascal&127;对象方法的关键技术。

一、inline汇编语言

Turbo Pascal 6.0的inline汇编语言实现了Turbo汇编和Microsoft汇编支持的大部分语法,实际上可以认为inline汇编语言是由Turbo Pascal编译器支持的汇编语言。inline汇编语言除支持DB、DW和DD汇编命令外,不支持EQU、PROC、&127;STRUC&127;、SEGMENT和MACRO等汇编命令。
在Pascal过程和函数中使用inline汇编语言,通过汇编语句asm实现。asm语句的语法如下:
asm
… { 汇编指令集 }
end;
inline汇编语句的格式与Turbo汇编相同,但其注释不是使用";",而是Pascal风格的{和}或(*和*)括起来。
 在asm语句中使用寄存器的规则基本上同external过程与函数,必须保护BP&127;、SP、SS和DS寄存器,可以自由修改AX、BX、CX、DX、SI、DI、ES和标志寄存器,在asm语句入口处,BP指向当前堆栈,SP指向栈顶,SS含有栈段的段地址,DS&127;含有数据段的段地址。
inline汇编语言允许在汇编表达式中存取Turbo Pascal的符号,包括标号、常量、变量、过程和函数,另外,inline汇编语言还实现了三个特殊的符号@CODE&127;、@DATA、@RESULT,@CODE和@DATA分别表示当前的代码段和数据段,它们只能与&127;SEG操作符结合使用,@RESULT表示函数的结果变量。例如:

MOV AX,SEG @DATA
MOV DS,AX

有关inlinei汇编语言的详细说明请参阅Turbo Pascal的语言手册。

二、方法的调用约定

方法使用与普通过程和函数一样的约定,只是每个方法有一个附加的隐含参数self,它对应于一个与该方法的对象类型相同的var参数,self参数总是以32&127;位指针形式作为最后一个参数传递的,该指针指向一个对象的实例,通过该实例来调用方法。在返回时,对象必须象从栈中删除任何普通参数一样删除self参数。所有的方法调用均采用远调用(far)模式。

1.静态方法的调用

静态方法的调用完全遵照上述的调用约定,例如调用MyObj.GetReal,&127;使用以下代码:

les di,MyObj { 把TMyObj对象的实例MyObj的地址装入ES:DI }
push es { 段地址进栈 }
push di { 偏移地址进栈 }
call far ptr GetReal { 调用方法 }

2.虚拟方法的调用

当一个对象首次定义虚拟方法时,其数据域的最后两个字节就是虚拟方法表&127;(VMT)在数据段中的偏移量。当一个对象类型其祖先有VMT时,它也继承VMT&127;域。&127;从VMT偏移量8开始出现32位的虚拟方法指针表,以虚拟方法的声明顺序依次排放,对象类型的每个虚拟方法均有一个方法指针。为调用虚拟方法,必须先求得对象实例的VMT域的地址,然后从VMT&127;表中得到虚拟方法的入口地址。&127;例如,&127;调用&127;MyObj.GetWord需要如下的代码:

les di,MyObj { 把TMyObj对象的实例MyObj的地址装入ES:DI }
push es { 段地址进栈 }
push di { 偏移地址进栈 }
mov di,es:[di+71h] { 从VMT域得到VMT的偏移量 }
call dword ptr [di+12] { 调用GetWord }

三、引用对象数据域的技术

在对象方法的实现部分,如果使用inline汇编语句,若引用对象的数据域,存在引用域值与域地址的问题。下面分别介绍:

1.引用对象的域值

上节已经讲到,当调用对象的方法时,隐含地传递了一个指向对象实例的32位指针self,这是引用对象域的关键所在。在方法中引用对象的数据域时,首先要取self的地址,然后以此地址为基础进行寻址。
在对象方法中引用对象的域值时,应当使用与下面代码类似的代码:

  lds si,self { 把对象实例的地址装入DS:SI }
 mov ax,[si].X { 将对象实例地址指明的对象域X的值装入AX }

其中X为其对象的一个域。

2.引用对象的域地址

在对象方法中引用对象数据域的地址时,应当使用与下面代码类似的代码:

lds si, self { 把对象实例的地址装入DS:SI }
mov ax, si { 把对象实例的偏移量装入AX }
add ax, X { 求出对象域X的实际偏移地址 }

其中X为对象的一个域。

四、引用局部变量的技术

在方法体内定义的变量为局部变量,它们不是在数据区分配空间,而是在堆栈区分配空间。当执行方法时建立,随方法的返回而消失,要在对象的方法中使用这些局部变量,只能通过地址进行,其典型的程序段如下:

asm
lea di,X1 { 把局部变量X1的地址装入DI }
mov SEGSS:[di],ax { 把AX的内容装入SS:[DI]指定的内存区,即X1 }
end;

五、引用方法形参的技术

形式参数分为值参与变参,值参可以直接引用,变参只能通过地址引用。下面分别介绍:

1.值参的引用

值参用值传递或是用地址传递,取决于参数的类型和长度。一般来说,如果值参为1、2或4字节长,用值传递,否则压入值的指针,&127;然后过程或函数把其值拷入局部存储空间。浮点数是一个例外,Real、Single、Double、Extended和Comp作为6、4、8、10或8字节压入栈中,直接引用其值。
对于尺寸小于等于4字节的值参的引用比较简单,而对字符串或尺寸大于4字节的值参稍后再讲。值参可以直接引用其值,下面是直接引用一个字节型值参的方法:

asm mov al,order end; { 把形参order的值装入AL寄存器 }

2.变参的引用

在inline汇编语言中,存取变参的内容要先取出其地址,然后按地址去取值,例如:

asm
les bx, XA { 把变参XA的地址装入ES:BX }
mov ah, es:[bx] { 把AH的内容装入变参XA的第一个字节 }
end;

3.字符串值参的引用

字符串值参在inline汇编语句中使用时,被认为是一个局变变量,使用的方法如下:
asm
lea si,Str1 { 取值参Str1的偏移地址 }
les di,Str2 { 把变参Str2的地址装入ES:DI }
SEGSS lodsb { 通过栈段地址覆盖引用Str1 }
stosb
end;
其中Str1是值参型字符串,Str2是变参型字符串。

六、函数的返回值

在inline汇编语言中特别定义了一个符号@RESULT,表示函数的结果变量,&127;其类型依函数的返回类型而变化。下面是TMyObj对象的一个方法,它演示了如何使用@RESULT。

function TMyObj.GetWord: word;
begin
asm
lds si,self { 把对象的一个实例的地址装入DS:SI }
mov ax,[si].I { 取对象域I的值 }
mov @RESULT,ax { 返回结果 }
end;
end;

七、实例

下面是一个完整的例子,综合使用了应用inline汇编语言编写Pascal对象方法的各项技术。读者通过对例子的学习和解剖,不难用&127;inline&127;汇编语言编写自己的Pascal对象的方法。该程序在Turbo Pascal 7.0&127;系统下编译通过。&127;如果是&127;TurboPascal 6.0,在调用虚拟方法时,其在VMT中偏移量应减去4个字节。

program InlineDemo;

uses Strings;

Type

PMyPtr = ^LongInt;

PMyObj = ^TMyObj;
TMyObj = object
I : word;
R : real;
P : PMyPtr;
TempStr : array[0..100] of char;
constructor init(IA:word;RA:real;PA:PMyPtr;ATempStr:PChar);
destructor done; virtual;
function GetWord: word; virtual;
function GetPtr: Pointer;
function GetReal: real;
function GetStr: PChar;
function GetAChar(order : byte): char;
Procedure GetAllField(var IA:word;var RA:real;
var PA:Pointer;var ATempStr:PChar;Var Achar: Char);
end;

constructor TMyObj.init(IA:word;RA:real;PA:PMyPtr;ATempStr:PChar);
begin
asm
les di,self { 把对象的一个实例的地址装入ES:DI }
cld
mov ax,IA { 取形参IA的值 }
stosw { 存到对象的域I }
mov ax,word ptr RA { 取形参RA的值的低字 }
stosw { 存到对象的域R的低字 }
mov ax,word ptr RA+2 { 取形参RA的值的中字 }
stosw { 存到对象的域R的中字 }
mov ax,word ptr RA+4 { 取形参RA的值的高字 }
stosw { 存到对象的域R的高字 }
mov ax,word ptr PA { 取形参PA的偏移量 }
stosw { 存到对象的域P的低字 }
mov ax,word ptr PA+2 { 取形参PA的段地址 }
stosw { 存到对象的域P的高字 }
push ds
lds si,ATempStr { 把形参ATempStr的地址装入DS:SI }
@2: lodsb { 装入一个字符 }
stosb { 存储一个字符 }
cmp al,0 { 是ATempstr的结束符吗? }
jz @3 { 是,转@3 }
jmp @2 { 不是,转@2 }
@3: pop ds
end;
end;

destructor TMyObj.done;
begin
end;

function TMyObj.GetWord: word;
begin
asm
lds si,self { 把对象的一个实例的地址装入DS:SI }
mov ax,[si].I { 取对象域I的值 }
mov @RESULT,ax { 返回结果 }
end;
end;

function TMyObj.GetPtr: Pointer;
begin
asm
lds si,self { 把对象的一个实例的地址装入DS:SI }
mov ax,word ptr [si].P { 取对象域P的低字 }
mov word ptr @RESULT,ax { 存入结果变量的低字 }
mov ax,word ptr [si].P+2 { 取对象域P的高字 }
mov word ptr @RESULT+2,ax { 存入结果变量的高字 }
end;
end;

function TMyObj.GetReal: real;
begin
asm
lds si,self { 把对象的一个实例的地址装入DS:SI }
mov ax,word ptr [si].R { 取对象域R的低字 }
mov word ptr @RESULT,ax { 存入结果变量的低字 }
mov ax,word ptr [si].R+2 { 取对象域R的中字 }
mov word ptr @RESULT+2,ax { 存入结果变量的中字 }
mov ax,word ptr [si].R+4 { 取对象域R的高字 }
mov word ptr @RESULT+4,ax { 存入结果变量的高字 }
end;
end;

function TMyObj.GetStr: PChar;
begin
asm
lds si,self { 把对象的一个实例的地址装入DS:SI }
add si,TempStr { 计算对象域TempStr的偏移地址 }
mov word ptr @RESULT,si { 将偏移地址存入结果变量的低字 }
mov word ptr @RESULT+2,ds { 将段地址存入结果变量的高字 }
end;
end;

function TMyObj.GetAChar;
begin
asm
lds si,self { 把对象的一个实例的地址装入DS:SI }
add si,TempStr { 计算对象域TempStr的偏移地址 }
xor ax,ax
mov al,order { 求字符在串中的位置 }
add si,ax
dec si { 达到字符的偏移地址 }
mov al,byte ptr [si] { 取字符 }
mov byte ptr @RESULT,al { 将字符存入结果变量 }
end;
end;

Procedure TMyObj.GetAllField(var IA:word;var RA:real;
var PA:Pointer;var ATempStr:PChar; var AChar : Char);
begin
asm
lds si,self { 把对象的一个实例的地址装入DS:SI }
push ds { 段地址入栈 }
push si { 偏移地址入栈 }
mov di,ds:[si+113] { 装入虚拟方法表(VMT)的偏移量 }
call dword ptr [di+12] { 调用虚拟方法GetWord }
cld
les di,IA { 把形参IA的地址装入ES:DI }
stosw
lds si,self
push ds
push si
call far ptr GetReal { 调用方法GetReal }
les di,RA { 把形参RA的地址装入ES:DI }
stosw { 存低字 }
mov ax,bx
stosw { 存中字 }
mov ax,dx
stosw { 存高字 }
lds si,self
push ds
push si
call far ptr GetPtr { 调用方法GetPtr }
les di,PA { 把形参PA的地址装入ES:DI }
stosw
mov ax,dx
stosw
lds si,self
push ds
push si
call far ptr GetStr { 调用方法GetStr }
les di,ATempStr { 把形参ATempStr的地址装入ES:DI }
stosw
mov ax,dx
stosw
mov al,2 { 取字符串的第二个字符 }
push ax
lds si,self
push ds
push si
call far ptr GetAChar { 调用方法GetAChar }
les di,AChar { 把形参AChar的地址装入ES:DI }
stosb
end;
end;

var
AMyObj : TMyObj;
MP : PMyPtr;
LI : LongInt;
I : word;
R : real;
P : Pointer;
MyStr : PChar;
TempStr : array[0..100] of char;
Ch : char;
begin
Writeln('-------------- input ---------------');
Write('Input a string : ');readln(TempStr);
Write('Input a Word integer : ');readln(I);
Write('Input a real : ');readln(R);
Write('Input a Longint : ');readln(LI);
Writeln;
Writeln('-------------- result ---------------');
new(MP);
MP^ := LI;
AMyObj.init(I,R,MP,TempStr);
AMyObj.GetAllField(I,R,P,MyStr,Ch);
Writeln('Word = ',I:10,^M^J,
'Real = ',R:10:4,^M^J,
'LongInt = ',longint(P^):15,^M^J,
'String = ',MyStr,^M^J,
'The second char of string = ',Ch);
Writeln('-------------- the end ---------------');
end.


© 1998-, 董占山, 版权所有。
转载文章请注明出处(www.sunfinedata.com/articles)。