zoukankan      html  css  js  c++  java
  • 汇编语言-10CALL和RET指令

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

    ret和retf

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

    用汇编语法来解释ret和retf指令

    call指令

    CPU执行call指令时,进行两步操作:

    (1)将当前的IP或CS和IP压入栈中;
    (2)转移。

    call指令不能实现短转移,除此之外,call指令实现转移的方法和jmp指令的原理相同。

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

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

    16位位移=标号处的地址-call指令后的第一个字节的地址;

    16位位移的范围为-32768 ~ 32767,用补码表示;

    16位位移由编译程序在编译时算出。

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

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

    用汇编语法来解释此种格式的call指令

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

    指令格式:call 16 位reg

     用汇编语法来解释此种格式的call指令

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

    call word ptr 内存单元地址

    例如

    call dword ptr 内存单元地址

    例如

    call和ret的配合使用

    call 标号会先将cs和ip压入栈然后再跳转,ret先将ip从栈中pop出然后转移到ip。

    mul指令

    mul是乘法指令

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

    格式如下:

    mul reg
    mul 内存单元

    内存单元可以用不同的寻址方式给出

    计算100*10

    计算100*10000

    模块化程序设计

    call与ret指令共同支持了汇编语言编程中的模块化设计。在实际编程中,程序的模块化是必不可少的。因为现实的问题比较复杂,对现实问题进行分析时,把它转化成为相互联系、不同层次的子问题,是必须的解决方法。而call与ret指令对这种分析方法提供了程序实现上的支持。利用call和ret指令,我们可以用简捷的方法,实现多个相互联系、功能独立的子程序来解决一个复杂的问题。

    参数和结果传递的问题

    子程序一般都要根据提供的参数处理一定的事务,处理后,将结果(返回值)提供给调用者。其实,参数和返回值传递的问题,实际上就是如何存储子程序需要的参数和产生的返回值的问题。

    这里面就有两个问题:

    (1) 将参数N存储在什么地方?
    (2) 计算得到的数值,存储在什么地方?

    根据提供的N,来计算N的3次方

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

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

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

    批量数据的传递

    将批量数据放到内存中,然后将它们所在内存空间的首地址放在寄存器中,传递给需要的子程序。对于具有批量数据的返回结果,也可用同样的方法。

    将一个全是字母的字符串转化为大写

    这个子程序需要知道两件事,字符串的内容和字符串的长度。因为字符串中的字母可能很多,所以不便将整个字符串中的所有字母都直接传递给子程序。但是,可以将字符串在内存中的首地址放在寄存器中传递给子程序。因为子程序中要用到循环,我们可以用loop指令,而循环的次数恰恰就是字符串的长度。出于方便的考虑,可以将字符串的长度放到cx中。

    除了用寄存器传递参数外,还有一种通用的方法是用栈来传递参数

    寄存器冲突的问题

    功能:将一个全是字母,以0结尾的字符串,转化为大写。

    assume cs:code
    
    data segment
        db 'word',0
        db 'unix',0
        db 'wind',0
        db 'good',0
    data ends
    
    code segment
    
        start:mov ax,data
          mov ds,ax
          mov bx,O
          mov cx,4
          s:mov si,bx
          call capital
          add bx,5
          loop s
          mov ax,4c00h
          int 21h
          
        capital:push cx    ;先将外循环要用到的cx和si入栈
          push si
        change:mov cl,[si]
          mov ch,0
          jcxz ok
          and byte ptr [si],11011111b
          inc si
          jmp short capital
        ok:pop si    ;结束调用时从栈中把数据恢复到si和cx
          pop cx
          ret
        
    code ends
    
    end start
  • 相关阅读:
    纯手写F3飞控的直升机固件(2.直升机倾斜盘混控了解)
    STM32输出PWM
    使用多个交叉编译器
    内核编译报错
    mdm9607平台2.2版本 编译指令
    linux 应用编程APIS
    linux 内核API总结
    Do away with the notion of hardsect_size
    大端 小端和网络字节序说明
    TI tlv320aic3104 codec调试之路径控制
  • 原文地址:https://www.cnblogs.com/aeolian/p/13025374.html
Copyright © 2011-2022 走看看