汇编 第十章 call和ret指令

第十章 call和ret指令

call和ret指令都是转移指令,它们都能修改ip,或同时修改cs和ip

10.1 ret和retf

1.ret指令用栈中的数据,修改ip的内容,从而实现【近转移】
    CPU执行ret指令时,进行下面两步操作:
    1.(ip)=((ss)*16+(sp))   ;ip的值修改为栈顶的内容
    2.(sp)=(sp)+2           ;栈顶移动
2.retf指令用栈中的数据,修改cs和ip的内容,从而实现【远转移】
    CPU执行retf指令时,进行下面四步操作
    1.(ip)=((ss)*16+(sp))   ;ip的内容修改为栈顶的内容
    2.(sp)=(sp)+2           ;栈顶移动
    3.(cs)=((ss)*16+(sp))   ;cs的内容修改为栈顶移动之后,栈顶的内容
    4.(sp)=(sp)+2           ;栈顶移动
    栈顶的两个字,低位字修改为ip,高位字修改为cs
3.可以看出,如果我们用汇编语法来解释ret和retf指令,则
    1.CPU执行ret指令,相当于
        pop ip
    2.执行retf指令时,相当于
        pop ip
        pop cs

10.2 call指令

1.call指令经常跟ret指令配合使用,因此CPU执行call指令,进行两步操作:
    1.将当前的ip或cs和ip压入栈中
    2.转移
2.call指令不能实现短转移,除此之外,
    call指令实现转移的方法和jmp指令的原理相同

10.3 依据位移进行转移的call指令

1.CPU执行“call 标号”这种格式的call指令时,进行如下操作:
    1.(sp)=(sp)-2         ;栈顶移动
    2.((ss)*16+(sp))=(ip) ;当前ip内容压栈
    3.(ip)=(ip)+16位位移   ;跳转到标号处
2.call指令格式:call 标号
    相当于执行:
    push ip
    jmp near ptr 标号

10.4 转移的目的地址在指令中的call指令

1.指令格式:call far ptr 标号
    实现的是段间转移
2.执行这种格式的call指令时CPU的操作
    1.(sp)=(sp)-2           ;栈顶移动
    2.((ss)×16+(sp))=(cs)   ;先把cs压栈
    3.(sp)=(sp)-2           ;栈顶移动
    4.((ss)×16+(sp))=(ip)   ;然后把ss压栈
3.CPU执行“call far ptr 标号”时,相当于进行
    push cs
    push ip
    jmp far ptr 标号

10.5 转移地址在寄存器中的call指令

1.指令格式:call 16位寄存器
2.执行这种指令时,在CPU中的操作
    1.(sp)=(sp)-2
    2.((ss)×16+(sp))=(ip)
    3.(ip)=(16位寄存器)
3.相当于
    push ip
    jmp 16位寄存器

10.6 转移地址在内存中的call指令

转移地址在内存中的call指令有两种格式:
1.call word ptr 内存单元地址
    汇编语法解释
      push ip
      jmp word ptr 内存单元地址
2.call dword ptr 内存单元地址
    汇编语法解释
      push cs   ;cs存放在高位
      push ip   ;ip存放在低位
      jmp dword ptr 内存单元地址

10.7 call和ret的配合使用(重要)

assume cs:code
code segment
start:
	mov ax,1
	mov cx,3
	call s ; 先push 当前ip到栈,再jmp到s处
	mov bx,ax ;(bx)=? 2的3次方 8
	mov ax,4c00h
	int 21h
s:
	add ax,ax
	loop s
	ret   ;pop 之前存入的ip
code ends
end start

在这里插入图片描述

10.8 mul指令

相乘的两个数;要么都是8位,要么都是16位
1.8位:AL中和8位寄存器或内存字节单元中
    AL中的内容作为被乘数
    结果放在AX中
2.16位:AX中和16位寄存器或内存字单元中
    AX中的内容作为被乘数
    结果放在DX(高位)和AX(低位)中。
3.格式如下:
    mul 寄存器
    mul 内存单元(byte ptr或 word ptr指明是字还是字节)

10.9 模块化程序设计

从上面我们看到,call与ret指令共同支持了汇编语言编程中的模块化设计。在实际编程中,程序的模块化是必不可少的。因为现实的问题比较复杂,对现实问题进行分析时,把它转化成为相互联系、不同层次的子问题,是必项的解决方法。
而call和ret指令对这种分析方法提供了程序实现上的支持。利用call和ret指令,我们可以用简洁的方法,实现多个互相联系、功能独立的子程序来解决一个复杂的问题。

10.10 参数和结果传递的问题

子程序一般都要根据提供的参数处理一定的事务,处理后,将结果(返回值)提供给调用者。
【编程】计算data段中第一组数据的3次方,结果保存在后面一组dword单元中

assume  cs:code,ds:data 
data segment
	dw 1,2,3,4,5,6,7,8
	dw 0,0,0,0,0,0,0,0 
data ends
code segment
start:
	mov ax,data
	mov ds,ax
	mov si,0; 
	mov cx,8
	call cube ; 先push 当前ip到栈,再jmp到cube处
	mov ax,4c00h
	int 21h
cube: ;参数bx =n,结果 dx:ax = n^3
	mov bx,[si ]
	mov ax,bx
	mul bx
	mul bx
	mov [si+10h],ax ;下一行的位置,偏移16字节
	add si,2
	loop cube;
	ret   ;pop 之前存入的ip
code ends
end start

10.11 批量数据的传递

使用寄存器、内存、栈传递数据
【编程】将一个全是字母,以0结尾的字符串,转化为大写
assume cs:code
data segment
  db 'word', 0
  db 'unix', 0
  db 'wind', 0
  db 'good', 0
data ends
code segment
start:	
	mov ax,data
	mov ds,ax
	mov bx,0
	mov cx,4
	s:	mov si,bx
	call capital
	add bx,5
	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

10.12 寄存器冲突的问题

【实验十 编写子程序】
1.显示字符串
子程序描述名称: show_str
 功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串。
 参数:(dh)=行号(取值范围0-24),(dl)-列号(取值范围0-79),(cI)=颜色,ds:si指向字符串的首地址
 返回:无
 应用举例:在屏幕的8行3列,用绿色显示data 段中的字符串。

assume cs:code,ds:data
data segment
	db 'Welcome to masm!',0  
data ends
code segment
start:
	mov dh,8              ;dh装行号(范围:1--25)
	mov dl,3              ;dl装列号(范围:1--80)[注:每超过80等于行号自动加1]
	mov cl,2              ;cl中存放颜色属性(0cah为红底高亮闪烁绿色属性)
	mov ax,data
	mov ds,ax
	mov si,0
	call show_str
	mov ax,4c00h
	int 21h
show_str:   ;显示字符串的子程序[定义开始]	
	mov al,0A0h		;大家还记得吧?每行是有80*2 == 160个字节 ==    0A0h个字节内容,则第n行
	dec dh          ;行号在显存中下标从0开始,所以减1
	mul dh          ;相当于从第 (n-1)*0A0h 个Byte单元开始……
	mov bx,ax       ;定位好的位置偏移地址存放在bx里(行)
	mov al,2        ;每个字符占两个字节
	mul dl          ;定位列,结果ax存放的是定位好的列的位置
	sub ax,2        ;列号在显存中下标从0开始,又因为偶字节存放字符,所以减2
	add bx,ax       ;此时bx中存放的是行与列号的偏移地址
	mov ax,0B800h   ;显存开始的地址
	mov es,ax       ;es中存放的是显存的第0页(共0--7页)的起始的段地址
	mov di,0        ;di指向显存的偏移地址,确定指向下一个要处理的字符的位置
	mov al,cl       ;cl是存放颜色的参数,这时候al存放颜色了,因为cl下边要用来临时存放要处理的字符
	mov ch,0        ;下边cx存放的是每次准备处理的字符
s:      
	mov cl,ds:[si]          ;ds:[si]指向“Welcome to masm!”,0
	jcxz ok                 ;当cl的值为0时候,cx == 0, 则发生跳转,到OK处结束处理!
	mov es:[bx+di],cl       ;偶地址存放字符
	mov es:[bx+di+1],al     ;奇地址存放字符的颜色属性
	inc si
	add di,2     	;指向了下个字符
	jmp short s     ;无条件跳转,jcxz是离开的关键跳!

ok:     
	ret    ;显示字符串的子程序[定义结束]
code ends
end start

2.解决除法溢出问题
子程序描述名称: divdw
 功能:进行不会产生溢出的除法运算,被除数为dword 型,除数为word 型,结果为dword 型。
 参数:(ax)=dword型数据的低16位,(dx)=dword型数据的高16位,(cx)=除数
 返回:(dx)-结果的高16位,(ax)=结果的低16位,(cx)=余数
 应用举例:计算1000000/10(F4240H/OAH)

assume cs:code,ss:stack
stack segment
	dw 8 dup(0)
stack ends
code segment
start:
	mov ax,stack
	mov ss,ax
	mov sp,10h
	mov ax,4240h
	mov dx,0fh
	mov cx,0ah
	call divdw
	mov ax,4c00h
	int 21h
divdw:  ;子程序定义开始
	push ax          ;16位先保存        
	mov ax,dx        ;ax这时候的值是高16位了
	mov dx,0         ;dx置0是为了不影响下边余数位,使得高16位为0
	div cx           ;H/N 0fh/0ah  dx(余数)=0005h   ax()=0001h
	mov bx,ax        ;ax,bx的值为(int)H/N  ,dx的值为(rem)H/N
	pop ax           ;ax的值现在是L,这个时候上次的余数dx变成了高位
	;注意,16位除法的时候默认被除数DX为高16位,AX为低16位
	div cx           ;L/N,54240h/0ah  dx(余数)=0h   ax()=86a0h 
	mov cx,dx    
	mov dx,bx
	ret   ;子程序定义结束      
code ends
end start

3.数值显示
【课程设计1】

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值