call与ret指令共同支持了汇编语言编程中的模块化设计。在实际编程中,程序的模块化是必不可少的。因为现实的问题比较复杂,对现实问题进行分析时,把它转化成为相互联系、不同层次的子问题,是必须的解决方法。而call与ret指令对这种分析方法提供了程序实现上的支持。利用call和ret指令,我们可以用简捷的方法,实现多个相互联系、功能独立的子程序来解决一个复杂的问题。
下面的内容中,我们来看一下子程序设计中的相关问题和解决方法。
子程序一般都要根据提供的参数处理一定的事物,处理后,将结果(返回值)提供给调用者。其实,我们讨论参数和返回值传递的问题,实际上就是在探讨,应该如何储存子程序需要的参数和生成的返回值。
比如 ,设计一个子程序,根据提供的参数N,计算N的3次方。
这里面就有两个问题:
(1)将参数N存储在什么地方?
(2)计算得到的数值,存储在什么地方?
可以用寄存器来储存,可以将参数放到bx中,因为子程序中药计算N*N*N,可以使用多个mul指令,为了方便,可将结果放到dx和ax中。
案例
assume cs:code code segment start: mov bx,270FH ;放入bx寄存器中 call cube ;调用子程序 mov ax,4c00H ;调用21号中断安全退出 int 21h cube:mov ax,bx ;放入ax 准备做乘法运算 mul bx ;bx*bx mul bx ;bx*bx*bx ret code ends end start
计算N的3次方
;说明:计算N的3次方 ;参数:(bx) -N ;结果:(dx:ax) =N^3 cube:mov ax, bx mul bx mul bx ret
注意,我们在编程的时候要注意形成良好的风格,对于程序应有详细的注释。子程序的注释信息应该包含对子程序的功能、参数和结果的说明。因为今天写的子程序,以后可能还会用到;自己写的子程序,也很可能要给别人使用,所以一定要有全面的说明。
用寄存器来存储参数和结果是最常使用的方法。对于存放参数的寄存器和存放结果的寄存器,调用者和子程序的读写操作恰恰相反:调用者将参数送入参数寄存器,从结果寄存器中取到返回值:子程序从参数寄存器中取到参数,将返回值送入结果寄存器。
编程,计算data段中第一组数据的3次方,结果保存在后面一组dword单元中。
assume cs:code data segment dw 1,2,3, 4,5,6,7,8 dd 0,0,0,0,0,0,0,0 data ends
程序如下:
code segment start: mov ax,data mov ds,ax mov si,0 ;ds:si指向第一组word单元 mov di,16 ;ds:di指向第二组dword单元 mov cx,8 s: mov bx,[si] call cebe mov [di],ax mov [di].2,dx add si,2 ;ds:si指向下一个word单元 add di,4 ;ds:di指向下一个dword单元 loop s mov ax,4c00h int 21h cube: mov ax,bx mul bx mul bx ret code ends end start