ITEEDU

5.1.3 通过地址表传递参数

 就我们所编制的这几个程序而言,由于主、子过程均在同一源程序中,所以我们可以直接在子过程中使用那些用DB、DW伪指令定义的变量,也就是将这些变量作为"全局"变量来使用。这可以使我们无需过多考虑参数传递的问题。如果我们要向子过程传递多个分散的数据,则可以采用地址表。下面这个程序是由CALC.ASM变化而来,它和CALC有相同的功能,但请注意参数的传递方式:

CALC2.ASM
		
          CR  equ       0dh	;回车符的ASCII码
          LF  equ       0ah	;换行符的ASCII码
        data  segment
              assume    ds:data
        mess  db        'welcome to pc world',0dh,0ah,24h	;定义一个字符串
       ascii  db        '01234	56789abcdef'	;十六进制数字对应的字符
         sum  dw        ?	;定义一个WORD变量保存字符总数
    addr_tbl  dw        2 dup (?)	;定义一个地址表
        data  ends
			
        code  segment
              assume    cs:code
        main  proc      far
              mov       ax,data	;初始化DS寄存器
              mov       ds,ax
              mov       word ptr addr_tbl,offset mess	;将字符串偏移地址存入地址表
              mov       word ptr addr_tbl+2,offset sum	;将SUN的偏移地址存入地址表
              mov       bx,offset addr_tbl	;BX送入地址表的偏移地址
              call      calc	;调用CALC子过程
              mov       ax,sum	;AX寄存器送入字符总数
              mov       bx,offset ascii	;ASCII表的偏移地址送入BX
              call      hexout	;调用HEXOUT子过程
              mov       ah,4ch	;结束进程
              int       21h
        main  endp
																					
        calc  proc      near	;CALC子过程
              push      bx	;保存BX寄存器
              push      bp	;保存BP寄存器
              mov       bp,word ptr [bx]	;将字符串的偏移地址送入BP
              mov       bx,word ptr [bx+2]	;将SUM变量的偏移地址送人BX
     caloop:
              mov       dl,byte ptr ds:[bp]	;取得一个字符
              cmp       dl,24h	;是'$'吗?
              jz        return	;若是则转至RETURN
              mov       ah,2	;显示这个字符
              int       21h
              inc       bp	;BP指向下一个字符
              inc       word ptr [bx]	;SUM变量加1
              jmp       caloop	;转至CALOOP处理下一个字符
     return:
              pop       bp	;恢复BP寄存器
              pop       bx	;恢复BX寄存器
              ret       ;返回主程序
        calc  endp
																					
      hexout  proc      near	;HEXOUT子过程
              push      ax	;保存AX寄存器
              mov       cx,2	;显示2位十六进制数
    hexloop:
              push      cx	;暂存显示位数
              mov       cl,4	;准备循环移位数
              rol       al,cl	;将AL左循环移4位
              push      ax	;暂存移位结果
              and       al,0fh	;保留AL原值的高4位
              xlat      ascii	;取得对应的ASCII码
              mov       dl,al	;将数字对应的ASCII码送入DL
              mov       ah,2	;显示数字
              int       21h
              pop       ax	;恢复AX寄存器
              pop       cx	;恢复CX寄存器
              loop      hexloop	;循环至HEXLOOP处理下个数位
              mov       dl,CR	;显示回车符
              mov       ah,2
              int       21h
              mov       dl,LF	;显示换行符
              int       21h
              pop       ax	;恢复AX寄存器
              ret       ;返回主程序
      hexout  endp
        code  ends
              end       main

 程序中DW伪指令的用法有些奇特,一般情况下我们常用DB、DW伪指令定义一些变量,这些变量往往有初值,所以在DB、DW后面总要跟一些字符串或数字。如果所定义的变量无初值,则我们就可以用"DW ?"这样的用法,"?"表示留出相应的存储空间,但不存入数据。不过请不要被"不存入数据"这句话误导,说是不存入数据,实际上这些存储空间将被初始化为"0","D# ?"和"D# 0"是完全一样的。这和我们从堆栈中动态分配到的存储空间不一样,当我们从堆栈中分配到自动变量空间后,我们有时要自己将其初始化为0,仔细研究一下CALC1.ASM不难发现这一点,然而用"D# ?"定义的变量就无需初始化。
新出现的"DUP"(Duplication)是一个操作符:

我们在第四章中谈到过一个分析运算符--OFFSET,用于取得某些变量的偏移地址。现在讨论的"DUP"操作符主要用于生成多个具有相同位宽的变量,用法很简单:

变量名 DB/DW 变量个数 DUP 数值1,数值2,...

"变量个数"应是一个正整数,用来指定括号中的数据项重复次数,请看下例:

TEMP.AS
        data  segment
        var1  db        2 dup ('A')	;定义'AA'
        var2  db        2 dup ('A','B','C')	;定义'ABCABC'
        var3  db        2 dup ('A',2 dup('B'),'C')	;定义'ABBCABBC'
        data  ends
              end
C:\ASM\> DEBUG TEMP.EXE[Enter]?	2 dup ('A',2 dup ('B'),'C')
d:cs:0 f[Enter]?				2 dup ('A')
0E79:0000?41 41 41 42 43 41 42 43-41 42 42 43 41 42 42 43?AAABCABCABBCABBC
								2 dup ('A','B','C')

仔细看看输出的结果,"DUP"并不难理解。

这个程序应用了地址表(ADDR_TBL)传送数据,而且BP寄存器被用于作一般的间接寻址,请注意指令MOV DL BYTE PTR DS:[BP]中的"DS:"就是用于显式地指定DS寄存器提供段地址,是不能省略的。

在这些过程间传递数据的方法之中,最为常用的是通过寄存器和堆栈传递数据。如果子过程主要是由汇编程序调用,那么通过寄存器传送数据是最简捷快速的方法;如果某子过程是为其它高级语言(如PASCAL、C等)编制,那么就应该通过堆栈取得参数,因为高级语言程序进行过程(函数)调用时都是通过堆栈传递数据的。