汇编语言(王爽)第十章

第十章

call和ret都是转移指令,都修改IP或同时修改CS和IP,用于子程序的设计

10.1 ret和retf

ret指令用栈中的数据,修改IP的内容,从而实现近转移

retf指令用栈中的数据,修改CS和IP的内容,实现远转移

执行ret时:

1、(IP)=((ss)*16+(sp))

2、(sp)=(sp)+2

用汇编语法解释,相当于pop IP

执行retf时:

1、(IP)=((ss)*16+(sp))

2、(sp)=(sp)+2

3、(CS)=((ss)*16+(sp))

2、(sp)=(sp)+2

用汇编语法解释,相当于pop IP、pop CS

下面的程序中,ret指令执行后,(IP)=0,CS:IP指向代码段的第一条指令

assume cs:code

stack segment
	db 16 dup(0)
stack ends

code segment
			mov ax,4c00h
			int 21h
	
	start:	mov ax,stack
			mov ss,ax
			mov sp,16
			
			mov ax,0
			push ax
			mov bx,0
			
			ret
code ends
end start

下面的程序中,retf指令执行后,CS:IP指向代码段的第一条

assume cs:code

stack segment
	db 16 dup(0)
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
			mov bx,0
			
			retf
code ends
end start

10.2 call指令

CPU指向call指令时,分两步

1、将当前的IP或CS和IP压入栈中

2、转移

call指令不能实现短转移,除此之外,call指令转移的方法与jmp指令原理相同

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

call 标号:将当前IP压入栈后,转移到标号处执行指令

执行过程:

1、(sp)=(sp)-2

​ ((ss)*16+(sp))=(IP)

2、(IP)=(IP)+16位位移

16位位移由编译器算出=标号处地址-call指令后的第一个字节的地址

范围-32768~32767

用汇编语言解释push IP

​ jmp near ptr 标号

下面程序执行后,ax中的数值为多少?

内存地址    机器码        汇编指令  

1000:0     b8 00 00     mov ax,0    

1000:3     e8 01 00     call s      

1000:6     40           inc ax

1000:7     58           s:pop ax       

CPU从内存中取第二条指令以后,IP指向下一条指令,(IP)=6,随后执行call指令,IP的值入栈,IP执行标号处的指令,执行后(ax)=6,由于还没遇到ret指令,所以ax中最终的数值是6

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

call far ptr 标号 实现的是段间转移

用汇编语言解释push CS

​ push IP

​ jmp far ptr 标号

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

指令格式 call 16位reg

用汇编语法解释push IP

​ jmp 16位reg

下面的程序执行后,ax中的数值为多少

内存地址    机器码        汇编指令  

1000:0     b8 06 00     mov ax,6    

1000:3     ff d0	    call ax    

1000:5     40           inc ax

1000:6     	            mov bp,sp

						add ax,[bp]

CPU从内存中取完call指令后,IP指向下一条指令,(IP)=5,执行call指令,(IP)的值入栈,跳转,在执行add指令时,因为用bp表示偏移地址时,默认段寄存器为ss,因此就是(ax)+之前压入栈中IP的值,最终(ax)=0bH

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

有两种格式

第一种:call word ptr 内存单元地址

用汇编语言解释push IP

​ jmp word ptr 内存单元地址

执行下面指令后,(IP)=0123H,(sp)=0EH

mov sp,10h
mov ax,0123h
mov ds:[0],ax
call word ptr ds:[0]

第二种:call dword ptr 内存单元地址

用汇编语言解释push CS

​ push IP

​ jmp dword ptr 内存单元地址

10.7 call和ret的配合使用

一个具有一定功能的程序段,我们称为子程序,在需要的时候,用call指令转去执行,ret返回

框架

assume cs:code
code segment
	main:
		……
		call sub1
		……
		mov ax,4c00h
		int 21h
		
	sub1:
		……
		call sub2
		……
		ret
		
	sub2:
		……
		ret
code ends
end main

10.8 mul指令

mul是乘法指令,使用的注意点

1、两个相乘的数,要么都是8位,要么都是16位。如果是8位,一个默认放在AL中,另一个放在8位reg或内存字节单元中;如果是16位,一个默认在AX中,另一个放在16位reg或内存字单元中

2、结果如果是8位乘法,结果默认放在AX中,如果是16位乘法,结果高位默认在DX中存放,低位在AX中放

格式:mul reg/内存单元

内存单元可以用不同的寻址方式给出,如

mul byte ptr ds:[0]
mul word ptr [bx+si+8]

计算100*10000,由于10000大于255,所以必须做16位乘法

mov ax,100
mov bx,10000
mul bx

10.9 模块化程序设计

利用call和ret实现多个相互联系、功能独立的子程序解决一个复杂的问题

10.10 参数和结果的传递问题

用寄存器来存储参数和结构是最常用的方法

编程,计算data段中第一组数据的三次方,结果保留在后面一组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 cube
        	
        	mov [di],ax		; 存储低位数据
        	mov [di].2.dx	; 存储高位数据
        	
        	add si,2
        	add di,4
        	loop s
        	
        	mov ax,4c00h
        	int 21h
        	
     cube:	mov ax,bx
    	 	mul bx
    	 	mul bx
    	 	
 code ends
 end start

10.11 批量数据的传递

如果要传入多个参数,寄存器的数量是不足的,这种时候,我们将批量数据放到内存中,然后将它们所在内存空间的首地址放在寄存器中,传递给需要的子程序,对于批量返回的结果,用同样的方法

编程,将data段中的字符串转化为大写

assume cs:code

data segment
	db 'conversation'
data ends

code segment
	start:	mov ax,data
			mov ds,ax
			mov si,0		; ds:si指向批量数据所在空间的首地址
			mov cx,12		; cx存放字符串的长度
			call captial
			
			mov ax,4c00h
			int 21h
			
  capital:	and byte ptr [si],11011111b		; 表明参与运算的数据大小
  			inc si
  			loop capital
  			ret
  			
 code ends
 end start

注意,除了用寄存器传递参数外,还可以用栈来传递参数

10.12 寄存器冲突的问题

编程,将一个全是字母,以0结尾的字符串转化为大写

因为0标志着字符串结束,所以子程序不需要字符串的长度作为参数,用jcxz来检测0

但是这会出现一个问题:因为可能有不止一个字符串,因此需要循环将字符串变为大写,而在子程序中,jczx是根据cx的值进行跳转,因此子程序会改变cx的值,影响程序 的正确运行

在编写子程序时,注意子程序不要使用会和主程序产生冲突的寄存器,尽量使用其他寄存器代替

如果都必须用到同一个寄存器,则在子程序开始前将所用到的寄存器的内容先保存起来,可以用栈来保存,还要注意出栈的顺序

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:	push cx
  			push si
  			
   change:	mov cl,[si]
   			mov ch,0
   			jczx ok
   			and byte ptr [si],11011111b
   			inc si
   			jmp short change
   			
   	   ok:	pop si
   	   		pop cx
   	   		ret
 code ends
 end start

编写子程序的标准框架

子程序开始:子程序中使用的寄存器入栈

​ 子程序内容

​ 子程序中使用的寄存器出栈

​ 返回

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值