约定存储单元传递参数
在调用子程序时,当需要向子程序传递大量数据时,因受到寄存器容量的限制,就不能采用寄存器传递参数的方式,而要改用约定存储单元的传送方式。这种参数传递方式有点象情报人员和联络人员之间的传递信息方式,一个向指定地点放情报,另一个从指定地点取情报。
例7.2是采用约定存储单元传递参数的例子,所处理的数据不是直接传给子程序,而是把存储它们的地址告诉子程序。
例7.4:编写一个子程序分类统计出一个字符串中数字字符、字母和其它字符的个数。该字符串的首地址用DS:DX来指定(以0为字符串结束),各类字符个数分别存放BX、CX和DI中。
解: |
||||
|
;子程序功能:分类统计出字符串中数字字符、字母和其它字符的个数 |
|||
;入口参数:DS:DX指向被统计的字符串 |
||||
;出口参数:BX、CX和DI分别保存数字字符、字母和其它字符的个数 |
||||
;算法描述: |
1、当字符在'0'~'9'范围时,数字字符个数BX加1; 2、为了判断简单,先把字字母变成大写字母; 3、当字符在'A'~'Z'范围时,字母个数CX加1; 4、否则,其它字符个数DI加1。 |
|||
COUNT |
PROC |
|||
PUSH |
AX |
|||
PUSH |
SI |
|||
XOR |
BX, BX |
|||
XOR |
CX, CX |
|||
XOR |
DI, DI |
;上三条指令使各类字符计数清零 |
||
MOV |
SI, DX |
|
||
again: |
MOV |
AL, [SI] |
||
INC |
SI |
|||
CMP |
AL, 0 |
|||
JE |
over |
|||
CMP |
AL, '0' |
|||
JL |
other |
|||
CMP |
AL, '9' |
|||
JG |
next |
|||
INC |
BX |
;数字字符个数加1 |
||
JMP |
again |
|||
next: |
CALL |
UPPER |
;调用子程序把AL中的字母变成大写字母 |
|
CMP |
AL, 'A' |
|||
JL |
other |
|||
CMP |
AL, 'Z' |
|||
JG |
other |
|||
INC |
CX |
;字母个数加1 |
||
JMP |
again |
|||
other: |
INC |
DI |
;其它字符个数加1 |
|
JMP |
again |
|||
over: |
POP |
SI |
||
POP |
AX |
|||
RET |
||||
COUNT |
ENDP |
例7.5 显示出任意字符串中数字字符、字母和其它字符的个数。
解: |
||||
|
.MODEL |
SMALL |
||
.DATA |
||||
MSG |
DB 'KSDJ L0984/[]3oiu OIU OIU (*&(5341', 0 |
|||
.CODE |
||||
|
.STARTUP |
|||
LEA |
DX, MSG |
;DS:DX指向待统计的字符串 |
||
CALL |
COUNT |
;调用子程序统计出各类字符的个数 |
||
CALL |
DISPBX |
;调用子程序显示数字字符的个数 |
||
MOV |
BX, CX |
|||
CALL |
DISPBX |
;调用子程序显示字母的个数 |
||
MOV |
BX, DI |
|||
CALL |
DISPBX |
;调用子程序显示其它字符的个数 |
||
.EXIT 0 |
||||
END |
7.3.3 堆栈传递参数
堆栈是一个特殊的数据结构,它通常是用来保存程序的返回地址。当用它来传递参数时,势必会造成数据和返回地址混合在一起的局面,用起来要特别仔细。
具体做法如下:
(1)、当用堆栈传递入口参数时,要在调用子程序前把有关参数依次压栈,子程序从堆栈中取到入口参数;
(2)、当用堆栈传递出口参数时,要在子程序返回前,把有关参数依次压栈(这里还需要做点额外操作,要保证返回地址一定在栈顶),调用程序就可以从堆栈中取到出口参数。
在通常情况下,我们用堆栈传入口参数,用寄存器传出口参数。
1、用堆栈传递入口参数的调用方法:
2、在子程序中取入口参数的方法:
、段内调用子程序
由于是段内调用,所以,CALL指令只把返回地址的偏移量(即IP的内容)压栈,如图7.6(a)所示。在进入子程序后,为了能读取传递过来的参数,需要用BP来访问堆栈,所以要先保护BP原来的值,再把当前SP的值传送给BP。
于是,当前BP所指向的堆栈单元与最后一个参数Paran之间隔着BP的原值和返回地址的偏移量,也就是说:二者之间相差4个字节。具体情况如图7.6(b)所示。
在子程序中读取用堆栈传递参数的一般方法如下程序片段所示。
|
… |
||
PUSH |
Para1 |
||
… |
|||
PUSH |
Paran |
;把n个字的参数压栈 |
|
CALL |
SUBPRO |
;调用子程序SUBPRO |
|
… |
、段间调用子程序
在段间调用子程序时,CALL指令会把返回地址的偏移量和段寄存器CS的内容都压栈,如图7.7(a)所示。在进入子程序后,与前面“段内调用子程序”一样,也需要用BP来读取传递过来的参数,所以,也要先保护BP原来的值,再把当前SP的值传送给BP。
这时,当前BP所指向的堆栈单元与最后一个参数Paran之间隔着BP的原值、返回地址的偏移量和段地址,所以,二者之间相差6个字节。具体情况如图7.7(b)所示。
在段间调用时,除了多一个返回段地址外,其它的内容与“段内调用”的情况完全一致,所以,在读取第i个参数时,只要用[BP 6 4*(n-i)]代替[BP 4 2(n-i)]即可(假设每个参数都是字类型)。