第10章知识点及检测点10.1,检测点10.2,检测点10.3,检测点10.4,检测点10.5答案和实验10答案

第10章 CALL 和 RET指令

10.1 ret和retf
执行ret指令步骤:

1.取栈最顶部的内容作为IP
2.sp=sp+2

retf指令步骤:

1.取栈最顶部内容为下一步指令的IP,且sp=sp+2
2.取下一个值为下一步指令的CS,且sp=sp+2

由此归纳:

指令名称    生效距离     特点
jxcz指令    短转移      当cx=0时生效
ret指令     近转移      取栈顶部值为IP
retf指令    远转移      取栈值为IP,再取栈值为CS

检测点10.1

assume cs:code

stack segment
	db 16 dup (0)
stack ends 

code segment 
start:
	mov ax,stack 
	mov ss,ax 
	mov sp,16		
	
	mov ax,_____      ;根据栈先进后出的特点,此时应该先把cs的值放进去
	push ax		
	mov ax,____        ;再把IP的值放进去,应该为0
	push ax 		
	
	retf		
code ends

栈先进后出,执行retf指令的时候先去IP,那么先放CS的值,再放IP的,故前面的空答案是1000H,后面的是0.

10.2 call指令

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

call指令实现的是近转移,执行call 标号指令会有以下步骤:

1. push IP
2. sp=sp+2
3. 转移至标号处(一个jmp指令,jmp near ptr 标号)

检测点10.2

内存地址    机器码    汇编指令
1000:0    b8 00 00  mov ax,0     ;ax=0
1000:3    e8 01 00  call s       ;将IP=6压入栈中,并转移到s处运行
1000:6    40        inc ax       ;不执行部分
1000:7    58        s:pop ax     ;转移到此处将栈中的值赋给ax,则ax=6

值得注意的是,第二行call s指令压入栈中的IP值不是3而是下一条将要执行指令的IP, 我个人理解是进行push IP操作的时候计算机以及准备好下一条将要执行的指令,所以压入的是6。
当然,这意味着和ret指令搭配使用的话将会更加方便!

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

相对于前面所讲的call指令来说,call far ptr 标号则是将IP和CS都压入栈中,并实现段间转移,有以下步骤:

1.sp=sp-2        ;入栈先让sp向上移动也就是减2再push
2.push CS        ;出栈pop之后sp+2
3.sp=sp-2
4.push IP
5.转移到标号处

检测点10.3

内存地址    机器码         汇编指令
1000:0    b8 00 00       mov ax,0
1000:3    9A 09 00 00 10 call far ptr s  ;将目前的CS和IP压入栈中,IP=8,CS=1000H,并跳转到标号s处
1000:8    40             inc ax
1000:9    58             s:pop ax  ;跳转到此处将8赋给ax,ax=8
                           add ax,ax  ;ax=8+8=16 = 10H
                           pop bx  ;bx=1000H
                           add ax,bx  ;ax=ax+bx=10H+1000H=1010H

答案及解析如附注所言

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

和前面的差不多,只不过把标号换成了寄存器,如:

mov ax,6
call ax
含义便是将IP压入栈中之后转移到偏移地址为6的地方就可以了

检测点10.4

内存地址    机器码    汇编指令
1000:0    b8 00 00  mov ax,6  ;ax=6
1000:2    ff d0     call ax  ;将IP=5压入栈中,转移到倒数第二条指令
1000:5    40        inc ax
1000:6              mov bp,sp  ;bp=sp
                    add ax,[bp]  ;[sp]=[bp]=5,就是之前压入的IP值,故ax=ax+5=11=0BH

ax=0BH,解析如上图

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

1.call word ptr 标号 相当于前面讲的 call 标号指令
2.call dword ptr 标号 相当于前面所讲的 call far ptr 标号指令

检测点10.5

assume cs:code

stack segment 
	dw 8 dup (0)  ;如果不清楚dup的作用可以看我发的第八章内容有介绍
stack ends

code segment
start:
	mov ax,stack 
	mov ss,ax
	mov sp,16			
	mov ds,ax	;让ds同样指向栈			
	mov ax,0
	call word ptr ds:[0EH]	 ;将所指IP压入栈中,sp=sp-2,故栈指针所在处为0EH,偏移到0EH处,还是原来的地方
	inc ax 	;ax=1
	inc ax 	;ax=2
	inc ax 	;ax=3
code ends 

故ax=3,解析如上图

assume cs:code 

data segment
	dw 8 dup (0)
data ends

code segment
start:
	mov ax,data 
	mov ss,ax 
	mov sp,16
	mov word ptr ss:[0], offset s ;将标号s处的地址存入ss:[0]处
	mov ss:[2],cs   
	call dword ptr ss:[0]  ;远转移,先将目前CS压入栈中,再压入IP
	;转移到的CS:IP值为:ss:[2] : ss:[0],就是标号s处
	nop  ;nop指令占一个字节,什么都不会做,此处不执行
s:
	mov ax,offset s 	;将s的偏移地址赋给ax
	sub ax,ss:[0cH]		;之前压入cs和ip,栈指针指向0CH处
	;所指内容为nop指令,故ax=ax-[0CH]=标号s处地址-nop指令所在地址=1
	mov bx,cs 	
	sub bx,ss:[0eH]  ;之前压入的CS值便在[0EH]处
	;这些指令都在同一个段中故CS相等,所以bx=0
code ends 
end start 

ax=1,bx=0 解析如上,这题看不懂我觉得最大的原因可能是栈的相关知识不太熟,可以先复习一下

10.7 call和ret的配合使用

call指令会存CS和IP入栈,ret指令会读取CS和IP出栈,这意味着这两者功能互补,我们可以用call指令留下坐标去执行别的程序然后用ret指令返回

codesg segment
start:
	mov ax,datasg
	mov ds,ax
	mov ax,stacksg
	mov ss,ax
	mov sp,16

	mov ax,0
	call s
	mov ax,4c00h
	int 21h

  s:inc ax
	ret
codesg ends
end start

这是我写的一个简单的示例,初始ax=0,call s指令留下坐标之后转移到标号s处,另 ax自加1,再执行ret指令取回坐标回到原来的地方结束程序,此时ax由0变1.
不光如此,我们可以在子程序中使用循环,并且加入栈来存储cx的值,类似于有一个函数专门实现循环功能的C语言程序。

10.8 mul指令

我们前面所学的div除法指令允许一个数是8位另一个数是16位,而mul乘法指令只允许两个数都是8位或者都是16位

位数    两个数存放的位置                       结果
8位    一个数存放在AL中另一个在8位reg或内存中    AX
16位   一个数存放在AX中另一个在16位reg或内存中  高位DX 低位AX

我将上面的例子修改了一下,变成了一个计算100立方的程序:

codesg segment
start:
	mov ax,datasg
	mov ds,ax
	mov ax,stacksg
	mov ss,ax
	mov sp,16

	mov al,100
	call s
	mov ax,4c00h
	int 21h

  s:mov bl,al
  	mul bl
  	mul bl
	ret
codesg ends
end start

10.9模块化程序设计

10.10 参数与结果传递的问题

用书上的两个例子加深理解:在这里插入图片描述
在这里插入图片描述

10.12 寄存器冲突的问题

实验10

1.显示字符串,我觉得这个问题的亮点有两处:用行号和列号用mul指令计算偏移地址,以及用datasg段末尾的0利用jcxz指令结束程序。
关于问题描述就不过多赘述了,直接附上答案:

assume cs:codesg,ds:datasg
datasg segment
        db 'welcome to masm!',0
datasg ends

codesg segment
  start:mov dh,8
        mov dl,3
        mov cl,2
        mov ax,datasg
        mov ds,ax
        mov si,0
        mov di,0
        mov ax,0B800H  ;跟实验9一样,用80×25彩色字符模式显示缓冲区,地址从这里开始
        mov es,ax

        mov al,160  
        mul dh    ;这里开始做乘法,dh*al设置成第八行的地址,结果默认在ax中
        mov bx,ax  ;将ax的值放到bx中,我们待会还会用到ax
        mov al,2  ;2是2字节的意思
        mul dl     ;这个介绍列的偏移地址,相乘的结果还是放到ax中
        add bx,ax  ;将列和行的偏移地址相加就是8行3列

        mov dh,cl    ;cl只能为0,因为需要用cx来判断jcxz指令是否跳转,所以我觉得题目让cl=2多此一举,这个颜色数据肯定要赋给别的寄存器的
        mov ch,0     ;我在这里将ch=0,那么我们将字符赋给cl
                  ;如果到最后面的0赋给了cl,那么cx=0,jcxz指令就会跳转结束程序!


show_str:mov cl,ds:[si]
        jcxz ok    ;如果cx=0,那么跳到标号ok处,如果不为0则执行下面的
        mov dl,cl    ;cl不是0那就是0前面的字符内容,我们将这些字符赋到dl中,高位dh已经在前面赋了颜色属性数据
        mov es:[di+bx],dx    ;将整个dx值打包放到显示地址中,bx就是我们前面计算的偏移地址
        inc si      
        add di,2
        jmp short show_str   ;弄完下一个字符重新跳回到show_str处搞下一个

     ok:mov ax,4c00h  ;如果轮到最后面是0jxcz指令就会跳到这里,完美符合题目要求!
        int 21h
codesg ends
end start

附上结果图:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值