zoukankan      html  css  js  c++  java
  • 汇编语言-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)
    
  • 相关阅读:
    swiper.js 点击链接跳转后再次返回到轮播原位置
    KVO和KVC
    XML与HTML的区别
    iOS UIAppearance使用详解
    文件处理
    IOS 四种数据永久保存的方式
    UIImage图片处理(更改颜色,修改透明度,缩小,合并图片)
    从一个视图控制器切换到另一个视图控制器的几种方式
    NSString的常用方法
    NSURLRequest和NSURLConnection
  • 原文地址:https://www.cnblogs.com/chengmf/p/12470840.html
Copyright © 2011-2022 走看看