参考: 《汇编语言》 王爽 第10章
call和ret都是转移指令。
1. ret和retf
ret指令:用栈中的数据,修改IP内容,从而实现近转移
相当于:
pop ip
retf指令:用栈中的数据,修改CS和IP,从而实现远转移
相当于:
pop ip
pop cs
例子:ret
- assume cs:code,ss:stack
- stack segment
- db 16 dup(1)
- stack ends
- code segment
- mov ax,4c00H
- int 21H
- start: mov ax,stack
- mov ss,ax
- mov sp,16
- mov ax,0
- push ax
- ret
- code ends
- end start
retf
- assume cs:code,ss:stack
- stack segment
- db 16 dup(1)
- stack ends
- code segment
- mov ax,4c00H
- int 21H
- start: mov ax,stack
- mov ss,ax
- mov sp,16
- mov ax,0
- push cs
- push ax
- retf
- code ends
- end start
2. call指令
call指令,执行操作:
1.将当前IP或CS和IP压入栈中
2.跳转
(1)依据位移进行转移的call指令
格式: call 标号
将下一条的指令的ip压入栈中,在转到标号处
相当于:
push ip
jmp near ptr 标号
(2)转移的目的地址在指令中的call指令
格式:
call far ptr 标号
将下一条的指令的CS和IP压入栈中,在转到标号处
相当于:
push cs
push ip
jmp far ptr
(3)转移地址地址在寄存器中的call指令
格式:call 16位reg
相当于:
push ip
jmp 16位reg
(4)转移地址在内存中的call指令
1. call word ptr 内存单元
相当于:
push ip
jmp word ptr 内存单元
2. call dword ptr 内存单元
相当于:
push cs
push ip
jmp dword ptr 内存单元
3. mul 指令
mul 是乘法指令
表示两个数相乘,它必须是都是8位或者都是16位
8位相乘 结果默认存放在ax中
16位相乘 结果高位存放在dx中,低位存放在ax中
例子见下面。
3. call和ret配合使用
call于ret结合使用,就相当于函数。
例子:求dw中数值的3次方。把bx当做“函数”参数,ax当做“函数”的返回值。
- assume cs:code,ds:data
- 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
- mov di,16
- mov cx,8
- s: mov bx,ds:[si]
- call cube
- mov ds:[di],ax
- mov ds:[di+2],dx
- add si,2
- add di,4
- loop s
- mov ax,4c00H
- int 21H
- cube: mov ax,bx
- mul bx
- mul bx
- ret
- code ends
- end start
寄存器数量有限,如果要传的参数,或者返回的参数过多。可以使用内存,或者栈。
例子:小写转大写。(用内存存放参数)
- assume cs:code,ds:data
- data segment
- db 'conversation'
- data ends
- code segment
- start: mov ax,data
- mov ds,ax
- mov si,0
- mov cx,12
- call captial
- mov ax,4c00H
- int 21H
- captial:and byte ptr ds:[si],11011111b
- inc si
- loop captial
- code ends
- end start
例子:计算 (a - b) ^3 假设a=3,b=1 (用栈来存放参数)
- assume cs:code
- code segment
- start: mov ax,1
- push ax
- mov ax,3
- push ax
- call difcube
- mov ax,4c00H
- int 21H
- difcube:push bp
- mov bp,sp
- mov ax,[bp+4]
- sub ax,[bp+6]
- mov bp,ax
- mul bp
- mul bp
- pop bp
- ret 4
- code ends
- end start
上面代码中的 ret 4 表示:
pop ip
add sp,n
例子:小写转大写,用0结尾来判断。(用栈来处理寄存器冲突)
- assume cs:code,ds:data
- data segment
- db 'word',0
- db 'city',0
- db 'good',0
- data ends
- code segment
- start: mov ax,data
- mov ds,ax
- mov cx,3
- mov bx,0
- s: push cx
- mov si,bx
- call capital
- add bx,5
- pop cx
- loop s
- mov ax,4c00H
- int 21H
- capital:mov cl,[si]
- mov ch,0
- jcxz ok
- and byte ptr [si],11011111b
- inc si
- jmp short capital
- ok: ret
- code ends
- end start
注意:要用栈保存cx
例子:实现show_str “函数” 在屏幕显示字符串。用dh指定函数 ,dl指定列号,cl指定颜色
- assume cs:code,ds:data,ss:stack
- data segment
- db 'Welcome to masm!',0
- data ends
- stack segment
- dw 8 dup(0)
- stack ends
- code segment
- start: mov ax,data
- mov ds,ax
- mov ax,stack
- mov ss,ax
- mov sp,16
- mov dh,10 ;行
- mov dl,17 ;列
- mov cl,2 ;颜色
- mov si,0
- call show_str
- mov ax,4c00h
- int 21h
- show_str: push ax
- push di
- push dx
- mov ax,10 ;确定行段 es
- mul dh
- add ax,0b800h
- mov es,ax
- mov dh,0 ;确定列偏移 di,注意,一个字符两个字节
- add dx,dx
- mov di,dx
- s: push cx ;保存cx
- mov ch,0
- mov cl,ds:[si]
- jcxz ok ;如果为0 跳转
- mov es:[di],cl
- pop cx
- mov es:[di+1],cl
- inc si
- add di,2
- jmp short s
- ok: pop cx ;不要忘记pop,眼不让rec还原的ip就不对了
- pop dx
- pop di
- pop ax
- ret
- code ends
- end start