汇编语言(第三版)第十章 CALL 和 RET 指令 笔记

call 和 ret 都是可以改变 ip 或是 cs 和 ip 。经常用来实现子程序设计。

10.1 ret 和 retf

ret指令用栈中的数据,修改IP实现近转移
retf指令用栈中的程序,修改call和ret内容实现远转移。

CPU执行ret指令时:
pop ip

CPU执行retf指令时:
pop ip
pop cs
所以压栈的时候是先压 cs,再压 ip

检测点10.1

在这里插入图片描述
在这里插入图片描述

mov ax,1000h
mov ax,0

10.2 call 指令

CPU执行call时候,

  1. 压入 ip 或 cs 和 ip。
  2. 转移

cal不能实现短转移,并且原理和jmp相同

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

  1. 将当前sp压栈,push ip ; sp-1,放入ip
  2. IP = IP + 16位位移地址

16位位移 = 目标地址-call指令后一个指令的初始地址。;和 jmp near ptr 标号 类似。

检测点 10.2

下面的程序执行后,ax中的数值为多少?
在这里插入图片描述

当 call s 的时候,此时的 CS:IP指向的是 1000:6,所以将 此时的 IP=6 压入栈中。而后再 1000:7的时候,pop 出 6 给 ax。所以 ax最后的数值为 6。

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

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

  1. push cs
    push ip

  2. cs = 标号所在段的段地址
    ip = 标号所在的偏移地址

其实相当于 push cs, push ip,jmp far ptr 标号。

检测点 10.3

在这里插入图片描述

先将 CS:IP 压入栈中 push 1000, push 8
pop ax ; ax = 8
add ax,ax; ax = 16
pop bx; bx = 1000
add ax,bx ; ax = 1016
所以ax 最后的值为1016.

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

指令格式: call 16 位 reg

push ip
jmp16位 reg ;ip = 16位 reg

检测点 10.4

在这里插入图片描述

mov ax,6 ;ax = 6
call ax; push 5 ,jmp ax
mov bp,sp;
add ax,[bp]; 6 + 5 =11
所以最后ax = 11

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

  1. call word ptr 内存单元地址
    push ip
    jmp word ptr 内存单元地址
  2. call dword ptr 内存单元地址
    push cs
    push ip
    jmp dword ptr 内存单元地址 ; 例如 ds:[0]是cs,ds:[2]是ip

检测点 10.5

在这里插入图片描述
在这里插入图片描述

call word ptr ds:[0EH] ;将IP = 0011h push栈中,也就是在0EH位置上。ds 和 ss 指向同一个段。所以 ds:[0EH]内容也就是 刚刚才压入的下一条指令的地址。所以继续执行。
所以到了mov ax,4c00h前,ax为3.

在这里插入图片描述
在这里插入图片描述

ax = 0, bx = 0

10.7 call 和 ret 的配合使用

·在这里插入图片描述

bx = 1
ax = 1+1=2, ax = 4, ax=8
mov bx,ax,所以 bx= 8

10.8 mul 指令

两个都是8位: 最大255
al * 8位reg / 内存单元=》ax
两个都是16位
ax*16位reg / 内存单元=》 dx ax
格式:

mul reg
mul 内存单元

10.9 模块化程序设计

call 和 ret 指令共同支持了汇编语言编程中的模块化设计。

10.10 参数和结果传递的问题

  1. 将参数存储在什么地方
  2. 计算得到的数值,存储在什么地方?

通过寄存器

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
	mov di,16
	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
	ret
code ends
end start

10.11 批量数据的传递

当需要传入一个参数,两个参数时,我们勉强还可以用寄存器来存。但如果是10个,20个呢?
所以在这种时候,我们要将参数传入内存中。
例如,将字符串全部大写化:

assume cs:code

data segment
	db 'conversation'
data ends

code segment
start:	
			mov ax,data
			mov si,0
			mov cx,12
			call capital
			
			mov ax,4c00h
			int 21h

; void capital (si, cx)
capital:	and byte ptr [si],11011111b
			inc si
			loop capital
			ret
code ends
end start

10.12 寄存器冲突的问题

以上的程序如果在数据段中:

data segment
	db 'helloworld',0
data ends

这样只传入si

;void capital (si)
capital :
	mov cl,[si]
	mov ch,0
	jcxz ok
	and byte ptr [si],11011111b
	inc si
	jmp short capital
ok :ret 

因为是CX判断是否为0跳转,所以如果在外面套上 标号 和 loop 标号,就会出现混乱。

问题10.2

cx 的使用出现了混乱,主程序与子程序共同使用了cx。

解决:

  1. 换其他寄存器
  2. 不要使用其他会产生冲突的寄存器。

所以说不如直接换用栈来存储。

实验10 编写子程序

在这里插入图片描述

assume cs:code, ds:data

data segment 
	db 'Welcome to masm!',0
data ends

code segment
start:
	mov dh,8
	mov dl,3
	mov cl,2
	mov ax,data
	mov ds,ax
	mov si,0

	call show_str

	mov ax,4c00h
	int 21h


; void show_str (dh,dl,cl,ds:si)
show_str:
	mov ax,0b800h
	mov es,ax ;要显示的起始段 es

	mov al,160
	mul dh
	mov dh,0
	add dx,dx
	add ax,dx
	mov di,ax ;要显示的起始位置 di

	mov ah,cl  
	
s:	mov ch,0
	mov cl,[si]
	jcxz s0

	mov ch,ah; 字符所在寄存器 cx

	mov es:[di],cx
	add di,2
	inc si
	jmp s

s0:	ret

code ends
end start

运行结果:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

assume cs:code
code segment
start:
	mov ax,4240h
	mov dx,000Fh
	mov cx,000Ah
	call divdw

	mov ax,4c00h
	int 21h

divdw: ;dx,ax,cx divdw(dx,ax,cx)
	mov di,dx
	mov si,ax

	;X/N = int(H/N)*65536+[rem(H/N)*65536+L]/N
	;=左边+右边
	mov ax,dx ;右边
	mov dx,0h
	div cx
	mov ax,si
	div cx    ;dx,ax

	mov bx,dx ;16余
	mov si,ax ;低16商

	mov ax,di ;左边
	mov dx,0
	div cx
	mov dx,ax ;高16商

	mov ax,si ;低16商
	mov cx,bx ;16余
	ret

code ends
end start

运行结果:

;X/N = int(H/N)*65536+[rem(H/N)*65536+L]/N = 左+右
这个公式中的65536是 1 0000h,也就是将ax放到dx中
所以,左边得到的就是高16位的商。右边多了/L,得到的余数就是最终的余数,得到的商就是低16位的商。

在这里插入图片描述
在这里插入图片描述

assume cs:code,ds:data

data segment
	db 10 dup(0)
data ends

stack segment
	db 128 dup(0)
stack ends

code segment
start:
	mov ax,stack
	mov ss,ax
	mov sp,16

	mov ax,12666

	mov bx,data
	mov ds,bx
	mov si,0
	call dtoc

	mov dh,8
	mov dl,3
	mov cl,2
	mov di,0
	mov si,0
	call show_str

	mov ax,4c00h
	int 21h


show_str: ;函数
	mov ax,0b800h
	mov es,ax ;要显示的起始段 es

	mov al,160
	mul dh
	mov dh,0
	add dx,dx
	add ax,dx
	mov di,ax ;要显示的起始位置 di

	mov ah,cl  
	
s2:	mov ch,0
	mov cl,[si]
	jcxz s3

	mov ch,ah; 字符所在寄存器 cx

	mov es:[di],cx
	add di,2
	inc si
	jmp s2

s3:	ret

divdw: ;dx,ax,cx divdw(dx,ax,cx) 商h商l 余数
	push bx
	push di
	push si

	mov di,dx
	mov si,ax

	;X/N = int(H/N)*65536+[rem(H/N)*65536+L]/N
	;=左边+右边
	mov ax,dx ;右边
	mov dx,0h
	div cx
	mov ax,si
	div cx    ;dx,ax

	mov bx,dx ;16余
	mov si,ax ;低16商

	mov ax,di ;左边
	mov dx,0
	div cx
	mov dx,ax ;高16商

	mov ax,si ;低16商
	mov cx,bx ;16余

	pop si
	pop di
	pop bx
	ret

dtoc: ;函数

	mov bx,ax ;备份ax
	mov cx,10;准备条件
	mov dx,0
	mov si,1 ;初始一个数

dtoc1:
	call divdw
	mov cx,dx
	or cx,ax
	jcxz dtoc2
	inc si
	mov cx,10
	jmp dtoc1

dtoc2: inc si
	mov byte ptr [si],0 ;假设一个五个数,那下标最大到4,所以下标5的地方设置一个 \0
	mov cx,si
	sub si,1

	mov ax,bx
	mov dx,0

	sub cx,1
	sub si,1
dtoc3:
	push cx
	mov cx,10
	call divdw

	mov [si],cl
	add byte ptr [si],'0' ; 填充字符
	sub si,1

	pop cx
	loop dtoc3

	ret

code ends
end start

运行结果:
在这里插入图片描述

课程设计1

更改dtoc

assume cs:code,ds:data

data segment
	db 10 dup(0)
data ends

stack segment
	db 128 dup(0)
stack ends

code segment
start:
	mov ax,stack
	mov ss,ax
	mov sp,16

	mov dx,00ffh
	mov ax,65535;12666 将值换成16,777,215

	mov bx,data
	mov ds,bx
	mov si,0
	call dtoc

	mov dh,8
	mov dl,3
	mov cl,2
	mov di,0
	mov si,0
	call show_str

	mov ax,4c00h
	int 21h


show_str: ;函数
	mov ax,0b800h
	mov es,ax ;要显示的起始段 es

	mov al,160
	mul dh
	mov dh,0
	add dx,dx
	add ax,dx
	mov di,ax ;要显示的起始位置 di

	mov ah,cl  
	
s2:	mov ch,0
	mov cl,[si]
	jcxz s3

	mov ch,ah; 字符所在寄存器 cx

	mov es:[di],cx
	add di,2
	inc si
	jmp s2

s3:	ret

divdw: ;dx,ax,cx divdw(dx,ax,cx) 商h商l 余数
	push bx
	push di
	push si

	mov di,dx
	mov si,ax

	;X/N = int(H/N)*65536+[rem(H/N)*65536+L]/N
	;=左边+右边
	mov ax,dx ;右边
	mov dx,0h
	div cx
	mov ax,si
	div cx    ;dx,ax

	mov bx,dx ;16余
	mov si,ax ;低16商

	mov ax,di ;左边
	mov dx,0
	div cx
	mov dx,ax ;高16商

	mov ax,si ;低16商
	mov cx,bx ;16余

	pop si
	pop di
	pop bx
	ret

dtoc: ;函数
	push di	;提前压入栈中
	mov di,dx ;更改 将dx存放在di中
	mov bx,ax
	mov cx,10
	mov si,1

dtoc1:
	call divdw
	mov cx,dx
	or cx,ax
	jcxz dtoc2
	inc si
	mov cx,10
	jmp dtoc1

dtoc2: inc si
	mov byte ptr [si],0
	mov cx,si
	sub si,1

	mov dx,di ;弹出di
	mov ax,bx ;弹出ax

	sub cx,1
	sub si,1
dtoc3:
	push cx
	mov cx,10
	call divdw

	mov [si],cl
	add byte ptr [si],'0'
	sub si,1

	pop cx
	loop dtoc3

	pop di	;最后不忘弹出di
	ret

code ends
end start

运行结果:
在这里插入图片描述

任务,将实验7中的Power idea公司的数据按照图10.2所示的格式在屏幕上显示出来。

修改后的显示ax中的数字:

assume cs:code,ds:data

data segment
	db '1234',10 dup(0)
data ends

stack segment
	db 128 dup(0)
stack ends

code segment
start:
	mov ax,stack
	mov ss,ax
	mov sp,16

	mov dx,00ffh
	mov ax,65535;12666 将值换成16,777,215

	mov bx,data
	mov ds,bx
	mov si,3
	call dtoc

	mov dh,8
	mov dl,3
	mov cl,2
	mov di,0
	mov si,0
	call show_str

	mov ax,4c00h
	int 21h


show_str: ;函数 (dh,dl,cl,ds:si)
	push ax
	push bx
	push di

	mov ax,0b800h
	mov es,ax ;要显示的起始段 es

	mov al,160; 一行160字节
	mul dh		;乘以dh行
	mov dh,0
	add dx,dx 	;加上该行第几个
	add ax,dx
	mov di,ax ;要显示的起始位置 di

	mov ah,cl  ;字符属性
	
s2:	mov ch,0 	
	mov cl,[si] ;将字符放在该位置,结尾0结束
	jcxz s3

	mov ch,ah; 字符所在寄存器 cx

	mov es:[di],cx
	add di,2
	inc si
	jmp s2

s3:	pop di
	pop bx
	pop ax
	ret

divdw: ;dx,ax,cx divdw(dx,ax,cx) 商h商l 余数
	push bx
	push di
	push si

	mov di,dx
	mov si,ax

	;X/N = int(H/N)*65536+[rem(H/N)*65536+L]/N
	;=左边+右边
	mov ax,dx ;右边
	mov dx,0h
	div cx
	mov ax,si
	div cx    ;dx,ax

	mov bx,dx ;16余
	mov si,ax ;低16商

	mov ax,di ;左边
	mov dx,0
	div cx
	mov dx,ax ;高16商

	mov ax,si ;低16商
	mov cx,bx ;16余

	pop si
	pop di
	pop bx
	ret

dtoc: ;函数 dtoc(dx,ax,ds:si)
	push bx
	push cx
	push di	;提前压入栈中


	push dx ;更改 将dx存放在di中
	push ax
	mov cx,10
	mov di,1

dtoc1:
	call divdw
	mov cx,dx
	or  cx,ax
	jcxz dtoc2
	inc di
	mov cx,10
	jmp dtoc1

dtoc2:
	add si,di
	mov byte ptr [si],0
	mov cx,di
	sub si,1

	pop ax ;弹出di
	pop dx ;弹出ax

	;sub cx,1
	;sub si,1
dtoc3:
	push cx
	mov cx,10
	call divdw

	mov [si],cl
	add byte ptr [si],'0'
	sub si,1

	pop cx
	loop dtoc3

	pop di	;最后不忘弹出di
	pop cx
	pop bx
	ret

code ends
end start

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值