约定存储单元传递参数
    在调用子程序时,当需要向子程序传递大量数据时,因受到寄存器容量的限制,就不能采用寄存器传递参数的方式,而要改用约定存储单元的传送方式。这种参数传递方式有点象情报人员和联络人员之间的传递信息方式,一个向指定地点放情报,另一个从指定地点取情报。
    例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)]即可(假设每个参数都是字类型)。
    







