汇编语言 第十章 CALL和RET指令

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_37043100/article/details/89459156

10.1 ret和retf

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

进行的过程:pop IP

(IP) = ((SS * 16) + (SP))

(SP) = (SP) + 2

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

执行过程:1.pop IP,2.pop CS

(IP) = ((SS)  * 16 + (SP))

(SP) = (SP) + 2

(CS) = ((SS) * 16 + (SP))

(SP)  = (SP) + 2

书中程序ret前面有一个mov bx,0感觉没实际作用

检测点10.1

补全程序,实现从内存1000:0000处来世执行指令。

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,1000H
    push ax
    mov ax,0
    push ax
    retf

code ends

end start

发现ss和sp指令一条执行完

执行结果:cs:1000H ip:0

10.2 call指令

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

2.转移到标号的地址

call指令不能实现短转移

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

call 标号

执行过程:1.push IP 2.jmp near ptr 标号

检测点10.2

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

ax:6

call会将当前的IP压入到栈中,当执行call s后,将6压入到栈中,然后跳到标号处,将6给ax

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

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

执行过程:1.push CS 2.push IP 3.jmp far ptr 标号

检测点10.3

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

ax:1010H

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

call reg(16位)

执行过程:1.push IP 2.jmp reg(16位)

检测点10.4

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

ax为6,ip为6,执行mov bp,sp,sp去看自己的寄存器,然后6 + ds:(sp地址中的值)地址中的值就是结果

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

(1)call word ptr 内存单元地址

执行过程:1.push IP 2.jmp word ptr 内存单元地址

(2)call dword ptr 内存单元地址

执行过程:1.push CS 2.push IP 3.jmp dword ptr 内存单元地址

检测点10.5

(1)下面的程序执行后,ax中的数值为多少?(注意:用call指令的原理来分析,不要在Debug中单步跟踪验证你的结论。对于此程序,在Debug中单步跟踪的结果,不能代表CPU的实际执行结果。)

assume cs:code

stack segment

    dw 8 dup(0)

stack ends

code segment

start:
    mov ax,stack
    mov ss,ax
    mov sp,16
    mov ds,ax
    mov ax,0
    call word ptr ds:[0eh]
    inc ax
    inc ax
    inc ax
    
    mov ax,4c00h
    int 21h

code ends

end start

当执行到call指令后,cs入栈,ip:11入栈,跳转到ds:[0eh]的值所指的地址,也就是11h,所以执行3次inc ax,所以ax:3

(2)下面的程序执行后,ax和bx中的数值为多少?

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
    mov ss:[2],cs
    call dword ptr ss:[0]
    nop

s:
    mov ax,offset s
    sub ax,ss:[0cH]
    mov bx,cs
    sub bx,ss:[0eH]
    
    mov ax,4c00h
    int 21h

code ends

end start

这个题我用-u看了下偏移地址,我不知道具体占用的字节数,然后我的话看的程序做的

10.7 call和ret的配合使用

assume cs:code

code segment
    
start:
    mov ax,1
    mov cx,3
    call s
    mov bx,ax
    
    mov ax,4c00h
    int 21h

s:
    add ax,ax
    loop s    
    ret
    
code ends

end start

执行call s之后,ip:9,跳转到标号s的位置,循环完成之后ax:8,ret从栈中取出ip,接着跳转到ip的位置,执行mov bx,ax,则bx:8

assume cs:code

stack segment

    db 8 dup (0)
    db 8 dup (0)

stack ends

code segment

start:
    mov ax,stack
    mov ss,ax
    mov sp,16
    mov ax,1000
    call s
    
    mov ax,4c00H
    int 21H
    
s:
    add ax,ax
    ret    

code ends

end start

根据书中的内容和讲解我们可以通过call和ret来去执行一个具有一定功能的程序,我们称他为子程序,然后再返回。

用call和ret实现子程序的框架如下:

子程序格式:

标号:
    指令
    ret

具有子程序的源程序的框架如下:

assume cs:code

code segment

main:
    :
    :
    call sub1    ;调用子程序sub1
    :
    
    mov ax,4c00H
    int 21H
    
sub1:            ;子程序sub1开始
    :
    :
    call sub2    ;调用子程序sub2
    :
    ret          ;子程序返回

sub2:            ;子程序sub2开始
    :
    :
    ret          ;子程序返回

code ends

end main

10.8 mul指令

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

2)结果:如果是8位乘法,结果默认放在AX中;如果是16位乘法,结果高位默认放在DX中,低位放在AX中。

格式如下:

mul reg

mul 内存单元

10.9 模块化程序设计

call与ret指令支持了汇编语言的模块化设计

10.10 参数和结果传递的问题

用寄存器来存储参数和结果是最常使用的方法。对于存放参数的寄存器和存放结果的寄存器,调用者和子程序的读写操作恰恰相反;调用者将参数送入参数寄存器,从结果寄存器取到返回值;子程序从参数寄存器中取到参数,将返回值送入结果寄存器。

10.11 批量数据的传递

子程序要循环,可以用loop 子程序标号,来实现子程序循环

10.12 寄存器冲突的问题

子程序循环要用到cx,如果子程序中也用cx就会造成寄存器冲突 

  为解决此问题我们使用子程序的标准框架

子程序开始:
    子程序中使用的寄存器入栈
    子程序内容
    子程序中使用的寄存器出栈
    返回(ret、retf)

实验10 编写子程序

在这次实验中,我们将要编写3个子程序,通过它们来认识几个常见的问题和掌握解决这些问题的方法。同前面的所有实验一样,这个实验是必须独立完成的,在后面的课程中,将要用到这个实验编写的3个子程序。

1.显示字符串

问题:

显示字符串是现实工作中经常用到的功能,应该编写一个通用的子程序来实现这个功能。我们应该提供灵活的接口,使调用者可以决定显示的位置(行、列)、内容和颜色。

子程序描述:

名称:show_str

功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串。

参数:(dh) = 行号(取值范围0~24),(dl) = 列号(取值范围0~79),(cl) = 颜色,ds:si指定字符串的首地址

返回:无

应用举例:在屏幕的8行3列,用绿色显示data段中的字符串。

assume cs:code

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

show_str:

code ends

end start

提示:

(1)子程序的入口参数是屏幕上的行号和列号,注意在子程序内部要将他们转化为显存中的地址,首先要分析一下屏幕上的行列位置和现存地址的对应关系;

(2)注意保存子程序中用到的相关寄存器;

(3)这各子程序的内部处理和显存的结构密切相关,但是向外提供了与显存结构无关的接口。通过调用这个子程序,进行字符串的显示时可以不必了解现存的结构,为编程提供了方便。在实验中,注意体会这种设计思想。

 

展开阅读全文

没有更多推荐了,返回首页