• 汇编语言-子程序调用


    汇编语言-子程序调用

    ret与ref指令

    ret

    ret == pop IP

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

    功能介绍

    retf指令用栈中的数据,修改CS和IP的内容,从而实现远转移
    CPU执行ret指令时,进行下面两步操作:

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

    相当于进行:

    pop IP

    retf

    retf == pop IP + POP CS

    功能介绍

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

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

    相当于进行:

    pop IP
    pop CS

    call指令

    call 标号

    功能介绍

    (把当前IP压栈后, 转到标号处执行指令)

    a. (SP) = (SP) - 2
       ((SS) * 16 + SP) = (IP)
    b. (IP) = (IP) + 16位位移

    相当于:

    push IP
    jmp near ptr 标号
    • 16位位移 = “标号”处的地址 - call指令后的第一个字节的地址;
    • 16位位移的范围 -32768—-32767, 用补码表示;
    • 16位位移由编译程序在编译时算出;

    call far ptr 标号

    功能介绍

    (把当前CS,IP压栈后, 转到标号处执行指令)

    a. (SP) = (SP) - 2
       ((SS) * 16 + SP) = (CS)
    b. (SP) = (SP) - 2
       ((SS) * 16 + SP) = (IP)
    c. (CS) = 标号所在段的段地址
       (IP) = 标号在段中的偏移地址

    相当于:

    push CS
    push IP
    jmp par ptr 标号

    call 16位寄存器

    功能介绍

    (sp) = (sp) – 2
    ((ss) * 16 + (sp)) = (IP)
    (IP) = (16位寄存器) 

    相当于:

    push IP 
    jmp 16位寄存器

    call word ptr 内存单元地址

    功能介绍

    push IP
    jmp word ptr 内存单元地址

    实例展示

    mov sp, 10h
    mov ax, 0123h
    mov ds:[0], ax
    call word ptr ds:[0]
    执行后,(IP)=0123H,(sp)=0EH

    call dword ptr 内存单元地址

    功能介绍

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

    实例展示

    mov sp, 10h
    mov ax, 0123h
    mov ds:[0], ax
    mov word ptr ds:[2], 0
    call dword ptr ds:[0]
    执行后,(CS)=0,(IP)=0123H,(sp)=0CH
    ((IP)= ds:[0], (CS) = ds:[2])

    子程序调用

    通过上面介绍的两个指令,我们可以完成子程序的调用。简单调用程序如下:

    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

    子程序调用-传递参数问题

    我们在写c语言或者其他高级语言的时候,要经常用到函数之间的参数传递这一个概念。那么在汇编语言中,我们怎么做到总程序和子程序之间的参数传递呢?

    寄存器存放法

    首先可以考虑在寄存器中,存放数据,比如a存放在ax中,b存放在bx中。

    mov ax,a
    mov bx,b

    这种方式可以在参数比较少的时候使用,但是参数多了呢?那么那么多的寄存器给你存放。因此这种方式不是长久之计。

    内存存放法

    我们想到了一个比较好的思路,就是将参数保存到内存中,然后在寄存器中存放这些参数的首地址,通过首地址访问一系列的参数。这种方式,显然可以存放更多的数据,并且没有数量上的限制。

    ;参数存放段
    data segment
        db 'aaaaa',0
        db 'aaaaa',0
        db 'aaaaa',0
    data ends
    
    code segment 
    
        ...
        ...
        mov ax,data
        mov es,ax
        mov si,0
    
        call sub1
    
        ...
        ...
        sub1:   mov ax,es[si]
                ...
                ...
        ret
    
    code ends

    这里还是存在一个问题,如果在主程序中用到了一个xx寄存器,然后在子程序中也用到了这个xx寄存器,那么当子程序返回到主程序的时,主程序中存放参数的内存地址已经没有记录了,程序出错。

    内存存放法(改进)

    为了解决这个问题,我们需要每次进入子程序时,将子程序中的需要用到的寄存器,push到栈中,每次退出子程序时,将相应寄存器pop出来。

    子程序都应遵循下面的模式:

    capital:
            push cx
            push si
    
    change:
            mov cl,[si]
            mov ch,0
            jcxz ok
            and byte ptr[si],11011111B
            inc si
            jmp short change
    
    ok: 
            pop si
            pop cx
            ret
  • 相关阅读:
    C++中数字与字符串之间的转换
    关于安卓启动eclipse错误:找不到元素‘d:devices'的声明
    linux相关解压命令
    泛型
    CRUD工程师---番外篇---反射
    CRUD工程师---设计模式
    AOP切面日志
    CRUD工程师---InnoDB存储引擎
    CRUD工程师---InnoDB存储引擎2
    CRUD工程师---InnoDB存储引擎4(表)
  • 原文地址:https://www.cnblogs.com/AbeDay/p/5026848.html
Copyright © 2020-2023  润新知