常用ARM汇编指令及ATPCS规则

2020-03-26来源: eefocus关键字:ARM  汇编指令  ATPCS规则

在嵌入式开发中,汇编程序常常用于非常关键的地方,比如系统启动时的初始化,进出中断时的环境保存、恢复,对性能要求非常苛刻的函数等。


只在必要情况下使用汇编指令,只涉及几条汇编指令。


1.相对跳转指令:b、bl


这两条指令的不同之处在于bl指令除了跳转之外,还将返回地址(bl的下一条指令的地址)保存在lr寄存器中。


这两条指令的可跳转范围是当前指令的前后32M:-32M~+32M。它们是位置无关的指令。使用示例:


b fun1

......

fun1:

bl fun2

......

fun2:

......

2.数据传送指令mov,地址读取伪指令ldr


mov指令可以把一个寄存器的值赋给另一个寄存器,或者把一个常数赋给寄存器。例子如下:


mov r1, r2 /* r1=r2 */

mov r1, #4096 /* r1=4096 */

mov指令传送的常数必须能用"立即数"来表示。


当不知道一个数能否用"立即数"来表示时,可以使用ldr命令来赋值。ldr是伪指令,它不是真实存在的指令,编译器会把它扩展成真正的指令;如果该常数能用"立即数"来表示,则使用mov指令;否则编译时将该常数保存在某个位置,使用内存读取指令把它读出来。例子如下:


ldr r1, =4097 /* r1=4097 */

ldr本意为"大范围的地址读取伪指令",上面的例子使用它来将常数赋给寄存器r1。下面的例子是获得代码的绝对地址:


ldr r1, =label

label:

......

3.内存访问指令:ldr、str、ldm、stm


注意:"ldr"指令既可能是前面所述的"大范围的地址读取伪指令",也可能是内存访问指令。当它的第二个参数前面有"="号时,表示伪指令,否则表示内存访问指令。


ldr指令从内存中读取数据到寄存器,str指令把寄存器的值存储到内存中,它们操作的数据都是32位的。示例如下:


ldr r1, [r2, #4] /* 将地址为 r2+4 的内存单元数据读取到 r1 中 */

ldr r1, [r2] /* 将地址为 r2 的内存单元位数据读取到 r1 中 */

ldr r1, [r2], #4 /* 将地址为 r2 的内存单元数据读取到 r1 中,然后 r2=r2+4 */

str r1, [r2, #4] /* 将 r1 的数据保存到地址为 r2+4 的内存单元中 */

str r1, [r2] /* 将 r1 的数据保存到地址为 r2 的内存单元中 */

str r1, [r2], #4 /* 将 r1 的数据保存到地址为 r2 的内存单元中,然后 r2=r2+4 */

ldm和stm属于批量内存访问指令,只用一条指令就可以读写多个数据。它们的格式如下:


ldm {cond}   {!}  {^}

stm {cond}   {!}  {^}

其中{cond}表示指令的执行条件。

表示地址变化模式,有以下4种方式:


ia(Increment After)     事后递增方式

ib(Increment Before)     事先递增方式

da(Decrement After)     事后递减方式

db(Decrement Before)     事先递减方式

中保存内存的地址,如果后面加上了感叹号,指令执行后,rn的值会更新:等于下一个内存单元的地址。


表示寄存器列表,对于ldm指令,从所对应的内存块中取出数据,写入这些寄存器;对于stm指令,把这些寄存器的值,写入所对应的内存块中。


{^}有两种含义:如果中有pc寄存器,它表示指令执行后,spsr寄存器的值将自动复制到cpsr寄存器中——这常用于从中断处理函数中返回;如果中没有pc寄存器,{^}表示操作的是用户模式下的寄存器,而不是当前特权模式的寄存

器。


指令中寄存器列表和内存单元的对应关系为:编号低的寄存器对应内存中的低地址单元,编号高的寄存器对应内存中的高地址单元。


ldm和stm指令示例如下:


//中断入口函数

HandleIRQ:

sub lr, lr, #4 //计算返回地址

stmdb sp!, {r0-r12,lr} //保存使用到的寄存器

//r0-r12,lr被保存在sp表示的内存中,

//"!"使得指令执行后,sp=sp-14*4

 

ldr lr, =int_return //设置调用IRQ_Handle函数后的返回地址

ldr pc, =IRQ_Handle //调用中断分发函数

int_return:

ldmia sp!, {r0-r12,pc}^ //中断返回,^表示将spsr的值复制到cpsr

//于是从irq模式返回被中断的工作模式

//"!"使得指令执行后,sp=sp+14*4

4.加减指令:add、sub


例子如下:


add r1, r2, #1 /* 表示 r1=r2+1,即寄存器r1的值等于寄存器r2的值加上1 */

sub r1, r2, #1 /* 表示 r1=r2-1 */

5.程序状态寄存器的访问指令:msr、mrs


ARM处理器有一个程序状态寄存器(cpsr),它用来控制处理器的工作模式、设置中断的总开关。示例如下:


msr cpsr, r0 /* 复制r0到cpsr中 */

mrs r0, cpsr /* 复制cpsr到r0中 */

6.其他伪指令


在本书的汇编程序中,常常见到如下语句:


.extern main

.text

.global _start

_start:

".extern"定义一个外部符号(可以是变量也可以是函数),上面的代码表示本文件中引用的main是一个外部函数。


".text"表示下面的语句都属于代码段。


".global"将本文件中的某个程序标号定义为全局的,比如上面的代码表示_start个全局函数。


7.汇编指令的执行条件


大多数ARM指令都可以条件执行,即根据cpsr寄存器中的条件标志位决定是否执行该指令:如果条件不满足,该指令相当于一条nop指令。


每条ARM指令包含4位的条件码域,这表明可以定义16个执行条件。可以将这些执行条件的助记符附加在汇编指令后,比如moveq、 movgt等。这16个条件码和它们的助记符、含义如下表所示:


表. 指令的条件码

image.png

表中的cpsr条件标志位N、Z、C、V分别表示:Negative、Zero、Cary、oVerflow。影响条件标志位的因素比较多,比如比较指令cmp、cmn、teq及tst等。


2.ARM-THUMB子程序调用规则ATPCS


为了使C语言程序和汇编程序之间能够互相调用,必须为子程序间的调用制定规则,在ARM处理器中,这个规则被称为ATPCS;ARM程序和Thumb程序中子程序调用的规则。基本的ATPCS规则包括寄存器使用规则、数据栈使用规则、参数传递规则。


1.寄存器使用规则


ARM处理器中有r0~r15共16个寄存器,它们的用途有一些约定的习惯,并依具这些用途定义了别名,如下表所示:


ATPCS中各寄存器的使用规则及其名称:

image.png

寄存器的使用规则总结如下:


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


在子程序中,使用r4~r11来保存局部变量,这时可以使用它们的别名v1~v8。如果在子程序中使用了它们的某些寄存器,子程序进入时要保存这些寄存器的值,在返回前恢复它们;对于子程序中没有使用到的寄存器则不必进行这些操作。在Thumb程序中,通常只能使用寄存器r4~r7来保存局部变量。


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


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


寄存器r14被称为连接寄存器,别名为lr。它用于保存子程序的返回地址。如果在子程序中保存了返回地址(比如将lr值保存到数据栈中),r14可以用作其他用途。


寄存器r15是程序计数器,别名为pc。它不能用作其他用途。


2.数据栈使用规则


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


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


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


FD Full Descending,满递减

ED Empty Descending,空递减

FA Full Ascending,满递增

EA Empty Ascending,空递增

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


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


3.参数传递规则


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


对于一般的返回结果,通常使用a0~a3来传递。示例:


假设CopyCode2SDRAM函数是用C语言实现的,它的数据原型如下:


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

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


ldr r0, =0x30000000 //1.目标地址=0x30000000,这是SDRAM的起始地址

mov r1, #0 //2.源地址=0

mov r2, #16*1024 //4.复制长度=16K

bl CopyCode2SDRAM //调用C函数CopyCode2SDRAM

cmp a0, #0 //判断函数返回值

第1行将r0设为0x30000000,则CopyCode2SDRAM函数执行时,它的第一个参数buf的指向的内存地址为0x30000000。


第2行将r1设为0,CopyCode2SDRAM函数的第二个参数start_addr等于0。


第3行将r2设为16*1024,CopyCode2SDRAM函数的第三个参数start_addr等于16*1024。


第5行判断返回值。


完毕!

关键字:ARM  汇编指令  ATPCS规则 编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/ic492640.html 本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:tiny4412开发板时钟操作示例
下一篇:常用的汇编指令介绍

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

ARM七种异常源和异常处理流程(四大步三小步)
                                                    ARM异常处理(处理器对特定的异常事件进行的处理)的进入过程(硬件自动完成):四大步(两备份两修改)三小步:(1)拷贝 CPSR (当前程序状态寄存器current program status register)到 SPSR_<mode>(保存当前
发表于 2020-03-08
ARM七种异常源和异常处理流程(四大步三小步)
arm开发板的一些常用命令
这写记录是我经常忘记的东西:做个笔记在这里1、reboot 是重启的意思。2、source 是使那些修改的文件立即生效,避免了重启之后才能生效的问题,例如我们修改了/etc/profiile,修改之后不能立即生效。这时我们输入 source /etc/profile 就可以生效了这些以后还会继续更新。3、chmod -R 777 /home/edu/practice 设置practice文件夹里面的所有的文件权限为7774、压缩单个文件:gzip -v etc.c   生成的压缩名为:etc.c.gz5、压缩多个文件:gzip -v  *     //压缩当前目录下的所有文
发表于 2020-03-08
ARM处理器的未定义指令异常处理过程分析
在前面的两篇文章中已经介绍了ARM处理器的工作模式和ARM异常中断处理流程。这篇文章我们通过代码来详细介绍ARM处理器未定义指令的异常中断处理;当发生未定义指令异常中断时,CPU进入未定义指令模式。可以通过读取CPSR寄存器的值来判定是否真的进入了未定义指令模式。开发板:tiny4412;工具链版本:gcc version 4.5.1 (ctng-1.8.1-FA)主要设置以下几个文件:start.S文件,详细内容如下:.text.global _start_start: b reset /* vector 0x46000000 reset*/ ldr pc, _undefined_instruction
发表于 2020-03-08
【ARM裸板】启动文件与栈的简略分析
unsigned int *)0x56000054; *pGPFCON = 0x100; *pGPFDAT = 0; return 0;}3.问题函数的调用规则ATPCS:ARM-THUMB procedure call standard(ARM-Thumb过程调用标准)参考文章 Arm汇编学习笔记(六)——函数调用栈空间以及fp寄存器3.1 为什么要设置栈?因为C函数所需保存局部变量保存LR等寄存器(返回地址)调用者如何传递参数给被调用者被调用者如何传返回这给调用者怎么从栈中恢复那些寄存器调用者和被调用者通过r0-r3寄存器传递参数和返回值在函数中,r4-r11可能被使用,所以:在入口保存他们,在出口恢复他们高标号寄存器存放在高地
发表于 2020-03-08
【ARM裸板】启动文件与栈的简略分析
【ARM裸板】S3C2440 时钟设置与分析
由时钟树分析,时钟源通过选择器接入给MPLL(Main PLL)与UPLL(USB PLL)经过MPLL得到FCLK提供给CPU[ARM920T]FCLK分别通过HDIVN与PDIVN分频得到HCLK与PCLKHCLK接入给AHB总线,再给各种高速设备(Nand Flash、内存控制器、中断控制器…)PCLK接入给APB总线,再给各种低速设别(I2C、PWM、GPIO、UART…)1.时钟源最大值2.时钟源2.1 两种时钟源1.晶振2.外部引脚时钟输入2.2 选择时钟源通过改变OM[3:2]引脚来选择时钟源JZ2440 OM[3:2]接入GND,则主时钟源与USB时钟源都选择外部晶振3.上电时钟分析复位等待, 等待电源稳定
发表于 2020-03-08
【ARM裸板】S3C2440 时钟设置与分析
【ARM裸板】内存控制器、SDRAM基础与代码重定位
1.CPU如何控制外设?CPU控制相应外设的寄存器,有外设控制器发出特定的波形2.CPU如何选择寄存器?CPU通过内存控制器选择发出地址addr根据地址选择不同的模块CPU内存控制器读写数据3.外设共用地址、数据总线,如何互不干扰?通过片选CS引脚,选择不同的外设4.谁控制片选引脚发出地址addr根据地址发出片选信号CPU内存控制器使能相应的外设由芯片手册得知:当CPU发出地址是0x00000000时 nGCS0为低电平选择(有效)5.CPU如何读写数据的方向?通过外设芯片的读写引脚控制6.CPU不同位宽设备如何连接并读取数据【内存控制器】6.1 连接方式8bit ROM (从A0开始对应连接)16bit ROM (A0不接,从A1
发表于 2020-03-06
【ARM裸板】内存控制器、SDRAM基础与代码重定位
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2020 EEWORLD.com.cn, Inc. All rights reserved