• ARM相关


    ARM-Thumb子程序调用规则—ATPCS

    能使C语言程序和汇编程序之间能够互相调用,必须为子程序间的调用制定规则。在ARM处理器中,这个规则被称为ATPCS:ARM程序和Thumb程序中子程序调用的规则。

    基本的ATPCS规则包括寄存器使用规则、数据栈使用规则、参数传递规则

    1.寄存器使用规则

    ARM处理器中有r0-r15共16个寄存器,它们的用途是有一些约定的习惯的,并依据这些用途定义了别名。

    1)子程序间通过寄存器r0-r3来传递参数,这时可以使用它们的别名a1-a4,被调用的子程序返回前无需回复r0-r3的内容。

    2)在子程序中,使用r4-r11来保存局部变量,这时可以使用它们的别名v1-v8,如果在子程序中使用了它们的某些寄存器,子程序进入时要保存这些寄存器的值,返回时再次恢复它们;

    对于子程序中没有使用到的寄存器,则不必进行这些操作,在Thumb指令中,通常只能使用寄存器r4-r7来保存局部变量

    3)寄存器r12用作子程序间scratch寄存器,别名为ip。

    4)寄存器r13用作数据栈指针,别名为sp,在子程序中寄存器r13不能用作它用,它的值在进入、退出子程序时必须相等。

    5)寄存器r14称为连接寄存器,别名为lr,它用于保存子程序的返回地址。

    如果在子程序中保存了返回地址(比如将lr值保存到数据栈中),r14可用作它用。

    6)寄存器r15是程序计数器,别名pc

    2.数据栈使用规则

    数据栈有两个增长方向;向内存地址减小的方向增长时,称为DESCENDING栈;向内存地址增加的方向增长时,称为ASCENDING栈。

    所谓数据栈的增长就是移动栈指针。当栈指针指向栈顶元素(最后一个入栈的数据)时,称为FULL栈;当栈指针指向栈顶元素相邻的一个空的数据单元时,称为EMPTY栈。

    综合这两个特点,数据栈可以分为以下4种:

    1)FD: Full Descending,满递减。

    2)ED:Empty Descending,空递减。

    3)FA:Full Ascending,满递增。

    4)EA:Empty Ascending,满递增。

    ATPCS规定数据栈为FD类型,并且对数据栈的操作是8字节对齐的。使用stmdb/ldmia批量内存访问指令来操作FD数据栈。

    使用stmdb命令往数据栈中保存内容时,先递减sp指针,再保存数据,

    使用ldmia命令从数据栈中恢复命令时,先获得数据,再递增sp指针,sp指针总是指向栈顶元素,这刚好是FD栈的定义。

    stmdb和ldmia指令一般配对使用,stmdb用于将寄存器压栈,ldmia用于将寄存器弹出栈,作用是保存使用到的寄存器

    https://blog.csdn.net/minsophia/article/details/53080183

    3.参数传递规则

    一般来说,当参数个数不超过4个时,使用r0-r3这4个寄存器来传递参数;如果参数个数超过4个,剩余的参数通过数据栈来传递。

    同样,对于一般的返回结果,通常使用a0-a3来传递。

    例程:

    假设CopyCode2SDRAM函数使用C语言实现的,数据原型为:

    int CopyCode2SDRAM(unsigned char *buf,unsigned long StartAddr,int size)

    在汇编代码中,使用下面的代码调用,同时判断返回值:

    1 ldr r0,=0x30000000     @ 1、目标地址=0x30000000,这是SDRAM的起始地址
    2 
    3 mov r1,#0              @ 2、源地址=0
    4 
    5 mov r2,#16*1024        @ 3、复制长度=16K
    6 
    7 bl  CopyCode2SDRAM     @ 4、调用C函数CopyCode2SDRAM
    8 
    9 cmp a0,#0              @ 5、进行判断

    参考链接: https://blog.csdn.net/wangpengqi/article/details/8443462

                       https://blog.csdn.net/chun_1959/article/details/46604979

    LDR指令格式:

    LDR{条件} 目的寄存器 <存储器地址>
    作用:将 存储器地址 所指地址处连续的4个字节(1个字)的数据传送到目的寄存器中。

    LDR指令的寻址方式比较灵活,实例如下:
    LDR R0,[R1] ;将存储器地址为R1的字数据读入寄存器R0 


    LDR R0,[R1,R2] ;将存储器地址为R1+R2的字数据读入寄存器R0。
    LDR R0,[R1,#8] ;将存储器地址为R1+8的字数据读入寄存器R0。
    LDR R0,[R1],R2 ;将存储器地址为R1的字数据读入寄存器R0,然后R1=R1+8。
    LDR R0,[R1],#8 ;将存储器地址为R1的字数据读入寄存器R0,并将R1+8的值存入R1。
    LDR R0,[R1,R2]! ;将存储器地址为R1+R2的字数据读入寄存器R0,并将R1+R2的值存入R1。
    LDR R0,[R1,LSL #3] ;将存储器地址为R18的字数据读入寄存器R0。
    LDR R0,[R1,R2,LSL #2] ;将存储器地址为R1+R2
    4的字数据读入寄存器R0。
    LDR R0,[R1,,R2,LSL #2]! ;将存储器地址为R1+R24的字数据读入寄存器R0,并将R1+R24的值存入R1。
    LDR R0,[R1],R2,LSL #2 ;将存储器地址为R1的字数据读入寄存器R0,并将R1+R2*4的值存入R1。
    LDR R0,Label ;Label为程序标号,Label必须是当前指令的-4~4KB范围内。

    要注意的是
    LDR Rd,[Rn],#0x04 ;这里Rd不允许是R15。

    另外LDRB 的指令格式与LDR相似,只不过它是将存储器地址中的8位(1个字节)读到目的寄存器中。
    LDRH的指令格式也与LDR相似,它是将内存中的16位(半字)读到目的寄存器中。

    LDR R0,=0xff
    这里的LDR不是arm指令,而是伪指令。这个时候与MOVE很相似,只不过MOV指令后的立即数是有限制的。这个立即数必须是0X00-0XFF范围内的数经过偶数次右移得到的数,所以MOV用起来比较麻烦,因为有些数不那么容易看出来是否合法。

    LDR R,label 和 LDR R,=label的区别

    LDR 是ARM中的指令,也是伪指令。
    当用 LDR r, =imd // r 为寄存器, imd为立即数
    LDR 是一条伪指令。编译器会根据 立即数的大小,决定用 ldr 指令或者是mov或mvn指令。
    当imd能用mov或者mvn操作时,就将它翻译成一条mov或mvn指令。当imd大于mov或mvn能够操作的数时,编译器会将imd存在一个内存单元中,然后再用一条ldr指令加载这个内存单元的的值到寄存器中。
    LDR r, label 和 LDR r, =label的区别:
    LDR r, =label 会把label表示的值加载到寄存器中,而LDR r, label会把label当做地址,把label指向的地址中的值加载到寄存器中。
    譬如 label的值是 0x8000, LDR r, =label会将 0x8000加载到寄存器中,而LDR r, label则会将内存0x8000处的值加载到寄存器中。




     
     
     
  • 相关阅读:
    【Python编程:从入门到实践】chapter6 字典
    【Python编程:从入门到实践】chapter5 if语句
    【Linux_Unix系统编程】Chapter9 进程凭证
    【Linux_Unix系统编程】Chapter10 时间
    【Linux_Unix系统编程】Chapter8 用户和组
    【Linux_Unix系统编程】chapter7 内存分配
    【Linux_Unix系统编程】chapter6 进程
    书籍 人生
    流程图软件
    技术文章,iOS,iOS开发系列 数学函数
  • 原文地址:https://www.cnblogs.com/darren-pty/p/13928707.html
Copyright © 2020-2023  润新知