相关文章推荐
腹黑的剪刀  ·  linux通过命令行启动.net ...·  2 年前    · 
风度翩翩的大海  ·  Which is better? Qt ...·  2 年前    · 
有胆有识的雪糕  ·  你需要知道的TypeScript高级类型总结 ...·  2 年前    · 
坐怀不乱的领带  ·  【CVPR2023】探索和利用不确定性的不完 ...·  2 年前    · 
帅呆的四季豆  ·  【小松教你手游开发】【unity实用技能】L ...·  2 年前    · 
Code  ›  [汇编语言]CALL和RET指令_ret指令机器码_Protein_zmm的博客
汇编指令 指令寄存器 数据寄存器 汇编语言
https://blog.csdn.net/weixin_51304981/article/details/126734368
悲伤的拐杖
2 年前
  • 一、ret和retf
  • 二、call指令
    • 2.1 依据位移进行转移的call指令
    • 2.2 转移的目的地址在指令中的call指令
    • 2.3 转移地址在寄存器中的call指令
    • 2.4 转移地址在内存中的call指令
  • 三、call和ret的配合使用
  • 四、mul指令
  • 五、模块化程序设计
    • 5.1 参数和结果传递的问题
    • 5.2 寄存器冲突问题

    ret指令用栈中的数据,修改IP的内容,从而实现近转移
    ref指令用栈中的数据,修改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和ref指令,则:
    CPU执行ret指令时,相当于进行pop IP
    CPU执行ref指令时,相当于进行:pop IP,pop CS

    assume cs:code
    stack segment
    	db 16 dup (0)
    stack ends
    code segment
    	mov ax,4c00h
    	int 21h
    start:
    	mov ax,stack
    	mov ss,ax
    	mov sp,16
    	mov ax,0
    	push ax
    	mov bx,0
    code ends
    end start
    

    上面程序,ret执行后,(IP) = 0,CS:IP指向代码段的第一条指令mov ax,4c00h

    二、call指令

    CPU执行call指令时,进行两步操作:
    (1)将当前的IP或CS和IP压入栈中
    (2)转移

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

    call 标号(将当前的IP压入栈,转到标号处执行指令)

    CPU执行此种格式的call指令,进行如下操作:
    (1)(sp)=(sp)- 2
    ((ss)*16+(sp))=(IP)
    (2)(IP)= (IP)+16位位移
    可以看出CPU要执行call 标号的时候,相当于是进行:
    push IP
    jmp near ptr 标号

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

    前面讲到的call指令,其对应的机器指令中并没有转移的目的地址,而是相当于当前IP的转移位移。

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

    CPU执行此种格式的call指令时,进行如下的操作:

    (1)
    (sp)=(sp)- 2
    ((ss)*16+(sp))=(CS)
    (sp)=(sp)- 2
    ((ss)*16+(sp))=(IP)

    (2)
    (IP)= 标号所在段的偏移地址
    (CS)= 标号所在段的段地址

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

    指令格式:call 16位reg

    相当于是进行:
    push IP
    jmp 16位reg

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

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

    1. call word ptr 内存单元地址
      CPU执行call word ptr 内存单元地址时,相当于是进行:
    push IP
    jmp word ptr 内存单元地址
    

    比如下面指令:

    mov sp,10h
    mov ax,0123h
    mov dx:[0],ax
    call word ptr ds:[0]
    

    执行后,(IP)=0123H,(SP)=0EH

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

    三、call和ret的配合使用

    下面程序返回前,bx中的值是多少?

    assume cs:code
    code segment
    start:
    	mov ax,1
    	mov cx,3
    	call segment
    	mov bx,ax ;(bx)=?
    	mov ax,4c00h
    	int 21h
    s:	add ax,ax
    	loop segment
    code ends
    
    1. CPU将call s指令的机器码读入,IP指向了call s后的指令mov bx,ax 然后CPU执行call s的指令,将当前IP值(mov bx,ax的偏移地址)压入栈,并将IP的值改变为标号s处的偏移地址

    2. CPU从标号s处开始执行指令,loop循环完毕后,(ax)= 8

    3. CPU将ret指令的机器码读入,IP指向ret指令后面的内存单元,然后CPU执行ret指令,从栈中弹出一个值(call s先前压入的mov bx,ax指令的偏移地址)送入IP中。则CS:IP指向指令mov bx,ax

    4. CPU从mov bx,ax开始执行指令,直到完成

    所以程序执行完后,(bx)=8,计算2的N次方,N由cx提供

    我们可以使用call和ret来实现子程序(写一个具有一定功能的程序段),子程序的框架如下:

    assume cs:code
    code segment
    main:
    	...
    	call subl ;调用子程序
    	...
    	mov ax,4c00h
    	int 21h
    sub1:	
    	...
    	call sub2 ; 调用子程序sub2
    	...
    sub2:
    	...
    code ends
    end main
    

    四、mul指令

    mul是乘法指令,使用mul乘法的时候应该注意:

    1. 两个相乘的数:两个相乘的数位数应该一样(要么都是8位、要么都是16位),如果是8位,一个默认在AL,另外一个放在8位reg或是内存字节单元中,如果是16位,一个默认在AX,另外一个放在16位reg或是内存字节单元中
    2. 结果:如果是8位乘法,结果放在AX中,如果是16位,高位放入DX,低位放入AX

    格式如下:

    mul reg
    mul 内存单元
    可以用不同寻址方式给出:
    mul byte ptr ds:[0]
    含义:(ax) = (al) * ((ds)*16+0)
    

    计算100*10

    mov al,100
    mov bl,10
    mul bl
    

    五、模块化程序设计

    5.1 参数和结果传递的问题

    子程序中一般都要根据参数处理一定的事物、处理后,将结果(返回值)提供给调用者,我们实际上就是在探讨,如何存储子程序需要的参数和产生的返回值

    比如设置一个子程序,可以根据提供的N,计算N的3次方

    这里就有两个问题,N存储在什么地方,计算得到的数值存储在什么地方?

    很显然,可以用寄存器来进行存储,将参数放到bx中;因为子程序要计算NNN,可以使用多个mul指令,为了方便,将结果放在dx和ax中,子程序如下:

    ;说明:计算N的3次方
    ;参数:(bx)=N
    ;结果:(dx:ax)=N^3
    cube:
    mov ax,bx
    mul bx
    mul bx
    ret

    编程计算data段中第一组数据的3次方,结果保存在后面一组dword单元中

    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
    

    程序如下:

    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	;ds:si指向第一组word
    		mov di,16	;ds:di指向第二组dword
    		mov cx,8
    s:		mov bx,[si]
    		call cube
    		mov [di],ax
    		mov [di].2,dx
    		add si,2	;ds:si指向下一个word单元
    		add di,4	;ds:di指向下一个dword单元
    		loop s
    		mov ax,4c00h
    		int 21h
    cube:	mov ax,bx
    		mul bx
    		mul bx
    code ends
    end start
    

    5.2 寄存器冲突问题

    设计一个子程序,功能:将一个全是字母,以0结尾的字符串,转化为大写

    程序要处理的字符串以0作为结尾符,这个字符串可以如下定义:
    db ‘conversation’,0

    这个子程序,字符串后面一定要有一个0,标记字符串的结束,字符串可以依次读取每个字符来进行检测,如果不是0,就进行大写的转化,是0就结束,可以用jcxz来检测0

    "ret" 一般是 "return" 的缩写。在编程中,"return" 指的是从一个函数中返回一个值。例如,在 Python 中,你可以这样写一个函数: def add(x, y): return x + y 这个函数将会接受两个参数 x 和 y,并返回它们的和。你可以调用这个函数,并将结果赋值给一个变量,例如: result = add(1, 2) 在这个例子中,调用 add(1,...
    CALL指令先把将来要返回的地址放入堆栈,再把即将被执行的过程的地址复制到指针寄存器上,当这个过程执行完要返回时,用RET指令将堆栈中存放的原来的地址放回指针寄存器上。 CPU下一步执行哪些内容,是由指针寄存器决定的,这个寄存器存放着地址编码,只要修改这个寄存器CPU就可以到指定位置执行了。32位的指针寄存器叫EIP,16位的叫IP。 a)         (1)(IP)=((ss)*16+(sp)) b)        (2)(sp)=(sp)+2 二.retf指令用栈中的数据,修改CS和IP的内容,从而实现远转移;    CPU执行retf指令时,进行下面两步操作: a)         (1)(IP)=(
    call指令和ret指令都是转移指令,它们都修改IP,或同时修改CS和IP,它们经常被共同用来实现子程序的设计 10.1 ret 和 retf ret指令用栈中的数据,修改IP的内容,从而实现近转移 CPU执行ret指令时,进行两步操作: 1,(IP) = ((ss)*16 + (sp)) 2,(sp) = (sp) + 2 pop IP retf指令用栈中的数据,修改CS和IP的内容,从而实现远转移 CPU执行retf指令时,进行四步操作: 1,(IP) = ((ss)*16 + (sp))
 
推荐文章
腹黑的剪刀  ·  linux通过命令行启动.net core项目,关闭shell后项目就被关闭,该怎么办_51CTO博客_.net core执行linux命令
2 年前
风度翩翩的大海  ·  Which is better? Qt Creator or Visual Studio IDE - Stack Overflow
2 年前
有胆有识的雪糕  ·  你需要知道的TypeScript高级类型总结_javascript技巧_脚本之家
2 年前
坐怀不乱的领带  ·  【CVPR2023】探索和利用不确定性的不完整多视角分类 - 专知
2 年前
帅呆的四季豆  ·  【小松教你手游开发】【unity实用技能】List列表排序_51CTO博客_unity手游开发教程
2 年前
今天看啥   ·   Py中国   ·   codingpro   ·   小百科   ·   link之家   ·   卧龙AI搜索
删除内容请联系邮箱 2879853325@qq.com
Code - 代码工具平台
© 2024 ~ 沪ICP备11025650号