• 汇编语言-call和ret指令


    call和ret指令都是转移指令,它们都修改IP,或同时修改CS和IP。它们经常被共同用来实现子程序的设计。

    ret 和 retf

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

    CPU执行ret指令时,进行下面两步操作:

    • (ip)=((ss)*16+(sp))
    • (sp)=(sp)+2

    CPU执行retf指令时,进行下面4步操作:

    • (ip)=((ss)*16+(sp))
    • (sp)=(sp)+2
    • (cs)=((ss)*16+(sp))
    • (sp)=(sp)+2

    可以看出,如果我们用汇编语法来解释ret和retf指令,则:
    CPU执行ret指令时,相当于执行:

    pop IP
    

    CPU执行retf指令时,相当于执行:

    pop IP
    pop CS
    

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

    call 标号(将当前的IP压入栈后,转到目标处执行指令)
    CPU执行此种格式的call指令时,进行如下的操作:

    1. (sp)=(sp)-2
    2. ((ss)*16+(sp))=(ip)
    3. (ip)=(ip)+16位位移。
    • 16位位移=标号处的地址 - call 指令后的第一个字节的地址;
    • near ptr 指明此处的位移为16位位移;
    • 16位位移的范围为 -32768~32767,用补码表示;
    • 16位位移由编译程序在编译时算出。

    CPU执行 call 标号 时,相当于进行:

    push IP
    jmp near ptr 标号
    

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

    call far ptr 标号 实现的是段间转移。
    CPU执行此种格式的call指令时,进行如下的操作。

    • (sp)=(sp)-2
    • ((ss)*16+(sp))=(cs)
    • (sp)=(sp)-2
    • ((ss)*16+(sp))=(ip)
    • (CS)=标号所在段的段地址
    • (IP)=标号在段中的偏移地址

    CPU执行 call far ptr 标号 时,相当于进行:

    push CS
    push IP
    jmp far ptr 标号
    

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

    指令格式:call 16 位 reg
    功能:

    • (sp)=(sp)-2
    • ((ss)*16+(sp))=(ip)
    • (ip)=(16位reg)

    CPU执行 call far ptr 标号 时,相当于进行:

    push IP
    jmp 16位 reg
    

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

    转移地址在内存中的call指令有两种格式。

    1. call word ptr 内存单元地址

    CPU执行 call word ptr 内存单元地址 时,相当于进行:

    push IP
    jmp  word ptr 内存单元地址
    
    1. call dword ptr 内存单元地址

    CPU执行 call dword ptr 内存单元地址 时,相当于进行:

    push CS
    push IP
    jmp  dword ptr 内存单元地址
    

    mul 指令

    mul 是乘法指令,使用mul 做乘法的时候注意以下两点:

    • 两个相乘的数:两个相乘的数,要么都是8位,要么都是16位。如果都是8位,一个默认放在AL中,另一个放在8位reg或内存字单元中;如果都是16位,一个默认在AX中,另一个放在16位reg或内存字单元中。
    • 结构:如果是8位乘法,结构默认放在AX中;如果是16位乘法,结构高位默认在DX中存放,低位在AX中放。

    寄存器冲突的问题

    我们利用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
    

    但可能引出一个一般化的问题:子程序中使用的寄存器,很有可能在主程序中也要使用,造成了寄存器使用上的冲突。
    解决这个问题的简捷方法是,在子程序的开始将子程序中所有用到的及寄存器中的内容都保存起来,在子程序返回前再恢复。 可以用栈来保存寄存器中的内容。
    所以,我们编写子程序的标准框架:

    子程序开始:  子程序中使用的寄存器入栈
    			子程序的内容
    			子程序中使用的寄存器出栈
    			返回(ret,retf)
    
  • 相关阅读:
    Red Hat Enterprise Linux 7.2下使用RPM包安装SQL Server vNext
    VS2015解决方案资源管理器空白,不显示内容
    ArcEngine调用FeatureToLine工具传参问题
    ArcEngine调用GP里的Merge工具传参问题
    ArcGIS GP服务的发布及调用
    利用 Chrome 原生工具进行网页长截图
    关于ueditor与arcgis js api同用会报错的问题
    关于ueditor使用说明
    bootstraptable为行中的按钮添加事件
    关于html与body的高度问题
  • 原文地址:https://www.cnblogs.com/chengmf/p/12470840.html
Copyright © 2020-2023  润新知