模块间的信息传递主要表现为模块间过程调用时的参数传递。在第十一章介绍的过程调用参数传递原则和方法依然有效。少量的参数可以利用寄存器传递或堆栈传递,大量的参数可先组织在一个缓冲区中,然后利用寄存器或堆栈传递相应的指针。
如果利用约定的存储单元传递参数,情形稍微复杂些,需要把它们声明为公共标识符。
本节内容:模块间的信息传递方法。
■利用寄存器和约定存储单元传递信息:即使用全局变量在模块间传递信息,示例代码t26-2.asm。
■利用段覆盖方法在模块间传递信息:通过使用COMMON组合类型覆盖另一个模块段的方法传递信息,示例代码t26-3.asm、t26-3ma.asm。
26.3.1 利用寄存器和约定存储单元传递信息
■例5:写一个DOS版本号的程序
动手实验137:写一个DOS版本号的程序。演示模块间利用寄存器和约定存储单元传递信息。
在理解下面示例程序的基础上,自己独立编写源程序。编译完成后,在debug调试器中单步跟踪调试,以验证程序的正确性。
●示例代码113:
;程序名:t26-2.asm
;功能:演示模块间利用寄存器和约定存储单元传递信息
;===============================================================
assume cs:cseg,ds:dseg
dseg segment public 'data'
mess db 'DOS Version is'
mess1 db ?
db '.'
mess2 db 2 dup(?)
db 0dh,0ah,'$'
verm db 0
vern db 0
dseg ends
;声明VERM和VERN是公共标识符
public verm,vern
;声明getver和todasc在其他模块定义
extrn getver:far,todasc:far
;
cseg segment public 'code'
start:
mov ax,dseg
mov ds,ax
;
call getver ;取得DOS版本号
;
mov al,verm
mov bx,length mess1 ;返回mess1重复操作符dup前的count值
mov si,offset mess1
call todasc ;把主版本号转换成可显示形式
mov al,vern
mov bx,length mess2
call todasc ;把次版本号转换成可显示形式
mov dx,offset mess
mov ah,9
int 21h
mov ax,4c00h
int 21h
cseg ends
end start
;程序名:t26-2ma.asm
;功能:作为程序t26-2.asm的模块
;=========================================================================
assume cs:func
;声明GETVER和TODASC为公共标识符
public getver,todasc
;声明VERM和VERN在其他模块定义
extrn verm:byte,vern:byte
;
func segment public 'code' ;定义代码段
;子程序名:getver
;功能:获取DOS版本号
;入口参数:无
;出口参数:在其他模块的VERM单元中存放主版本号
;在其他模块的VERN单元中存放次版本号
;说明:远过程
getver proc far
mov ah,30h ;30h DOS功能调用取版本号
int 21h
mov verm,al ;al=主版本号
mov vern,ah ;ah=次版本号
ret
getver endp
;----------------------------------------------------
;子程序名:todasc
;功能:把一个8位二进制数转换为相应十进制数的ASCII码串
;入口参数:AL=欲转换的二进制数,BX=十进制数的最少位数
;DS:SI=存放ASCII码串的缓冲区首地址
;出口参数:ASCII码串在相应的缓冲区
todasc proc far
mov cl,10
todasc1:
xor ah,ah
div cl
add ah,30h
mov [si+bx-1],ah
dec bx
jnz todasc1
ret
todasc endp
func ends
end
子程序GETVER把DOS的版本号直接填入在主模块中约定的单元VERM和VERN中。子程序TODASC的入口参数由寄存器传递,转换得到的十进制数ASCII码串直接写到主模块的指定缓冲区中。这两个子程序均被定义为远过程。
正确设置数据段或附加段寄存器是模块间正确传递信息的保证。访问定义在其他模块的变量前,必须保证已设置好相应的段寄存器。例如:在调用GETVER之前必须正确设置数据段寄存器DS,因为子程序GETVER在访问约定的变量时,认为数据段寄存器已设置好。如果有必要还可以动态地改变段寄存器内容。
26.3.2 利用段覆盖方法在模块间传递信息
模块间传递信息的另一个方法就是利用段覆盖,这个方法只适用于模块间传递信息。具体方法是:在两个模块中都定义一个同名同类别数据段,规定段组合类型是COMMON;把要传递的数据(变量)安排在这两个数据段的相同位置上。由于这两个在不同模块中的数据段同名同类别,且组合类型是COMMON,所以连接时它们就发生重叠。
■例6:写一个显示当前系统日期的程序
动手实验138:写一个显示当前系统日期的程序。演示利用段覆盖方法在模块间传递信息。
在理解下面示例程序的基础上,自己独立编写源程序。编译完成后,在debug调试器中单步跟踪调试,以验证程序的正确性。
●示例代码114:
;例6:写一个显示当前系统日期的程序。
;为了简单化,显示的日期只含月和日。主模块有一个代码段和一个数据段,
;从模块也有一个代码段和数据段。
;程序名:t26-3.asm
;功能:演示利用段覆盖方法在模块间传递信息
;=======================================================
assume cs:cseg,ds:dseg
extrn getdate:far ;声明getdate在其他模块定义
;
dseg segment common ;定义一个具有common类型的数据段
mess db 'Current date is'
mess1 db 2 dup(?)
db '_'
mess2 db 2 dup(?)
db 0dh,0ah,24h
dseg ends
;
cseg segment public
start:
mov ax,dseg
mov ds,ax
call getdate ;调用GETDATE取日期
mov dx,offset mess
mov ah,9
int 21h
mov ax,4c00h
int 21h
cseg ends
end start
;模块名:t26-3ma.asm
;功能:作为t26-3.asm的一部分
;========================================================
public getdate ;声明getdate作为公共标识符
;
dseg segment common ;定义一个具有COMMON类型的数据段
mess db 'Current date is:'
mess1 db 2 dup(?)
db '_'
mess2 db 2 dup(?)
db 0dh,0ah,24h ;这部分数据与模块t8-9中完全相同
year dw ?
month db ?
day db ? ;这部分变量是另外加上的
dseg ends
;
cseg segment byte public ;定义代码段
assume cs:cseg,ds:dseg
;子程序名:getdate
;功能:取得当前日期并把月日数转换成相应的十进制数ASCII码串
;入口参数:无
;出口参数:ASCII码串填入约定缓冲区
;说明:远过程
getdate proc far
mov ah,2ah ;取得当前系统日期
int 21h
mov year,cx
mov month,dh
mov day,dl
mov al,month
mov bx,length mess1 ;把月数转换成十进制数ASCII码串
mov si,offset mess1
call todasc
mov al,day
mov bx,length mess2 ;把日数转换成十进制数ASCII码串
mov si,offset mess2
call todasc
ret
getdate endp
;----------------------------------------------------
;子程序名:todasc
;功能:把一个8位二进制数转换为相应十进制数的ASCII码串
;入口参数:AL=欲转换的二进制数,BX=十进制数的最少位数
;DS:SI=存放ASCII码串的缓冲区首地址
;出口参数:ASCII码串在相应的缓冲区
todasc proc near
mov cl,10
todasc1:
xor ah,ah
div cl
add ah,30h
mov [si+bx-1],ah
dec bx
jnz todasc1
ret
todasc endp
cseg ends
end
模块t26-3ma中的数据段比模块t26-3.asm中的数据段多了若干变量,在段覆盖时,以最长的段为段的最后实际长度。但必须注意,要传递的数据变量必须安排在相同的位置。由于模块t26-3ma中含有要访问的数据段,所以过程getdate能够随便地访问想要访问的对象。
练习
1、请简述汇编语言程序设计时,模块间的通信方法及其具体实现的过程。
本文摘自编程达人系列教材《X86汇编语言基础教程》。