call 和 ret 都是转移指令,他们可以共同使用实现汇编中的中的子程序设计
call 指令
call 近转移
使用格式:call label
当执行call指令时,CPU可以认为执行2个操作:
- push ip + 3 (将call后的下一条指令的ip压入栈中)
 - jmp near ptr label (跳转到label所在的ip)
 
call 远转移
当要进行段间转移时,使用 call far ptr 指令(在emu8086中无效,可以用’转移地址在内存中的call’代替),该指令会将当前的cs与ip先后压入栈中,并进行等效于 jmp far ptr 的跳转
转移地址在寄存器中的call指令
格式:call reg,在把当前IP压入栈中后跳转到16reg中的地址
转移地址在内存中的call指令
分为两类,近转移与远转移
call word ptr [Address]
call dword ptr [Address]
mov word ptr [0], func ;偏移地址 mov word ptr [2], code ;段地址 call dword ptr [0]
ret 与 retf 指令
与call指令对应的,ret与retf指令从栈中获得跳转地址进行跳转。ret指令对应call的近转移,从栈中弹出一个地址给ip。retf对应远转移,从栈中弹出两个元素分别修改ip与cs
ret 指令还可以与立即数一起使用,如 ret N 可以认为是:
- pop ip
 - add sp, N
 
这样可以实现用栈来转递参数
汇编的模块化编程
使用call与ret可以实现对子程序的调用与返回
如以下子程序实现了计算ax的立方:
; 描述:计算N的立方根
; 参数:(ax) = N
; 结果:(bx ax) = N^3
cube:
    push cx    ; 保存cx中原来的元素
    mov cx, ax ; 用cx保存ax
    mul cx     ; 计算N^2
    mul cx     ; 计算N^3
    pop cx     ; 恢复cx
    ret        ; 返回
这里使用了寄存器来转递参数,更常用的作法是使用栈来传递参数,如同样的程序可以改写为:
; 描述:计算N的立方根
; 参数: N = 栈中除IP外的第二个元素
; 返回:(bax ax) = N^3 
cube2:
    push bp            ; 保存bp
    mov bp, sp         ; 将bp指向栈顶
    mov ax, ss:[bp+4]  ; 从栈中获得参数
    mov bp, ax         ; 计算N^3
    mul bp
    mul bp
    pop bp             ; 恢复bp
    ret 2              ; 将栈顶指向参数前,并返回
调用时则:
push 2 call cube2
还可以用寄存器/栈转递地址,子程序从内存中获得数据(常用于批量数据的传递),以下程序使用这种方法在屏幕的指定位置打印内存中的数据:
; 描述:在屏幕的指定位置打印字符串
; 参数:起始位置 <- 栈中第5个元素 
;       长度     <- 栈中第4个元素
;       Y位置    <- 栈中第3个元素
;       X位置    <- 栈中第2个元素
; 结果:无
print:
    push ax   ;中转
    push bx   ;字符串寻址
    push cx   ;控制循环
    push si   ;输出偏移地址
    push es   ;输出段地址
    
    mov bx, sp
    mov ax, ss:[bx+14] ;获得输出的偏移位置
    mov cl, 158
    mul cl
    mov si, ax
    mov ax, ss:[bx+12]
    add si, ax
    add si, ax       
    mov cx, ss:[bx+16] ;获得长度
    mov bx, ss:[bx+18] ;获得字符串地址
    
    mov ax, 0B800H
    mov es, ax        ;获得输出的段地址
    
    print_loop:
        mov al, [bx]      ;获得字符
        mov es:[si], al   ;输出
        add bx, 1
        add si, 2
    loop print_loop
    
    pop es
    pop si    
    pop cx
    pop bx
    pop ax
    ret 8
还有要注意设计子程序时,要注意寄存器冲突(子程序修改了主程序要用的寄存器),解决方案一个时避免使用冲突的寄存器,另一个则是提前将寄存器的值压入栈中,子程序返回前再从栈中弹出
啦啦啦
评论都要审核?
原来不用审核呀