ARM学习之路--ARM体系与汇编

ARM学习之路–ARM体系与汇编

ARM体系结构

计算机四种指令集:复杂指令集(CISC),精简指令集(RISC),显式并行指令集(EPIC)和超长指令字指令集(VLIW)

我们嵌入式开发中常打交道的是 RISC ,它有如下特点:

1.Load/Store架构。CPU不直接处理RAM数据,需要将内存中的数据(LOAD)加载到寄存器才能操作,然后将处理结果(Store)存储到内存中。

2.固定长度指令,单周期指令

3.使用更多的寄存器来存储数据,而不是直接使用内存堆栈,效率更高一些!

另外:ARM指令集和传统的RISC还有如下差异:

1.ARM有桶型移位寄存器,单周期内可以完成数据的各种操作。

桶型移位寄存器

如上是一个8位的逻辑右移桶型移位寄存器,由移位控制信号线b2b1b0分别控制4位2位1位。

2.新增了DSP,SIMD/NEON等指令

ARM处理器多种工作模式

程序正常运行在ARM的用户模式(user mdoe),该模式下处理器不能对内存和底层硬件进行操作.当程序出错或者有中断产生的时候,ARM处理器就会切换到对应的特权模式。

一般通过系统调用或者软中断使处理器进入特权模式,从而操作系统内核或者硬件驱动代码来对底层硬件设备进行读写。

我们可以看一下ARM处理器中的寄存器分布情况:

以上是对应各种ARM处理器模式下寄存器的分布,一般分为通用寄存器和专用寄存器:

除了FIQ工作模式,其余模式下的R0-R12都是通用寄存器,都是CPU公用共享的;R0-R3通常用来传递函数参数,R4-R11通常用来保存程序运算的中间结果或者函数的局部变量,R12通常用来作为函数调用过程中的临时寄存器

R13称为堆栈指针寄存器(STACK Pointer SP),用来维护和管理函数调用过程中的栈帧变化,总是指向当前正在运行的函数的栈帧。

R14称为链接寄存器(LINK Register LR)保存上一级函数调用者的返回地址。

R15又称为程序计数器(Program Counter PC)也就是我们常说的PC指针,CPU从内存中取指,就是从PC保存的地址中去取的。每一次取指,PC的地址值都会增加。在ARM的三级流水线中,PC指针的值等于当前正在运行的指令地址+8。

CPSR(当前处理器状态寄存器)保存一些当前处理器的状态位,标志位和控制位:

死去的微机原理开始攻击我!!!

每一种特权模式下都有一个(SPSR)程序状态保存寄存器,当处理器工作模式切换或者发生异常的时候,将CPSR寄存器的值保存到SPSR,当异常返回后或者模式切换回来之后,从SPSR恢复原先的处理器状态。

我们在TRACE32工具离线分析死机dump的时候,就可以操作工具读取到各种状态下寄存器的数据:

ARM汇编指令

一个完整的ARM指令通常由操作码+操作数组成,编码格式如下:

使用<>标起来的是必选项,使用{}标起来的是可选项
opcode是操作码助记符,如MOV,ADD
cond: 执行条件,ARM为了减少分支跳转个数,允许使用BEQ,BNQ等形式的组合指令
S:是否会影响CPSR寄存器中的标志位,如SUBS会影响,而SUB不会影响
Rd:目标寄存器
Rn:第一个操作数的寄存器
operand2:第二个可选操作数

存储访问指令

在ARM的存储访问的过程中,我们经常使用的是LDR/STR.LDM/STM 这两对指令。

LDR/STR用于在寄存器和内存之间传送数据,每次默认读写4字节,LDRB/STRB每次默认读写一个字节。

LDM/STM用于在一组寄存器和一片内存之间传送数据。

数据传送指令

MOV用于在寄存器和寄存器之间传送数据。

MVN用以将操作数oprand2按位取反后传送给目标寄存器

算术逻辑运算指令

指令格式通用,这里就不再多说了。

操作数 operand2

它可以是一个常数,也可以是一个寄存器+偏移的形式。

#constant 立即数/常数
Rm{,shift}

在第二种格式中,通过{,shift}可以通过多种位移或者循环移位的方式,构建更加灵活的操作数,构建方式如下:

使用方式如下:

比较指令

用以比较两个数的大小,或比较两个数是否相等。其运算结果会影响CPSR寄存器的N,Z,C,V标志位

指令使用方式如下:

运行结果当Z=1:运算结果为0,两个数相等;

N=1 表示运算结果为 负;

N=0 表示运算结果为非负,为正或者为0;

条件执行指令和跳转指令

为了提高代码密度,减少ARM指令的数量,ARM指令可以根据CPSR寄存器中的标志位,通过指令组合实现条件执行。

跳转指令如下:

B 为无条件跳转指令,我们可以在后面加上条件码组成BEQ,BNE组合指令。CPSR寄存器中的标志位根据需要可以任意搭配成不同的条件码,和ARM指令一起组合使用。

条件码说明如下:

学习完如上的知识点:现在来看这段源码功能就非常清晰了!

如上代码实现了一个循环结构,将源地址SRC的数据COPY到目的地址DST!

我们在使用TRACE32离线分析死机DUMP的时候就可以直接查看死机发生的时候执行到哪一个指令啦!

ARM伪指令

ARM伪指令并不是ARM指令集定义的标准指令,而是各编译厂商自定义的辅助指令。伪指令有点类似于C语言中的预处理命令,在编译阶段会被翻译成一条或者多条ARM标准指令。

常见的ARM伪指令主要有4个:ADR,ADRL,LDR,NOP

其中NOP指令我们之前说到过,空泡指令,相当于MOV R0,R0.用于暂缓流水线的执行。

LDR伪指令

这个指令和我们之前看到的内存加载指令到寄存器的LDR助记符同名。这个LDR伪指令的主要用途就是将一个32位的内存地址保存到寄存器中。

在寄存器之间传递数据可以使用MOV指令,但是当传递的数据是一个内存地址是32位的立即数的时候,MOV指令就不能使用了,指令如下:

第二个指令为什么异常呢?

我们知道ARM处理器采用RISC指令集,特点就是单周期指令,在一个32位的系统中,一个指令通常是32位的,包括操作码和操作数。由于操作码和操作数共享32位存储空间:操作码要占用一定的空间,那么留给操作数的编码空间就小于32位了。因此MOV R0,#0X30008000 这条指令是无法进行编码的,那么就需要使用伪指令来解决这一问题:

LDR R0, =0X30008000

为了区别伪指令LDR和内存加载指令LDR,伪指令的操作数前一般会有一个 = ;由于伪指令并不是ARM标准指令,所以CPU译码电路并不支持直接运行伪指令,而是会被标准的ARM指令替代。

当LDR的操作数小于8位时,LDR一般会直接使用MOV指令替代,例如:

当LDR的操作数大于8位的时候,LDR会被编译器转换为LDR(ARM标准)+文字池的形式。

编译器首先在内存中分配一个4字节大小的存储单元,将操作数存放到该存储单元中,该存储单元也叫作文字池。然后编译器计算出该存储单元到LDR伪指令之间的偏移OFFSET,使用寄存器相对寻址,可以将32位立即数送到R0寄存器中。

偏移量的OFFSET要小于4KB,所以一般会将文字池紧挨当前的代码段,直接放置在代码段的后面。

ADR伪指令

ADR指令可以将基于PC相对偏移的地址值读取到寄存器中。

就上面这个例子来说:ADR会将标号为LOOP的内存地址送到寄存器R0中。 编译器首先计算当前正在执行的ADR伪指令地址和标号为LOOP之间的地址偏移OFFSET,然会使用标准指令将地址送到R0.

ADR与LDR的异同:

相同点:

加载一个地址到指定的寄存器

不同点:

1.LDR通常被翻译为LDR或者MOV指令,而ADR通常会被翻译为ADD或者SUB
2.LDR主要用来操作外部设备的寄存器,ADR主要用来通过相对寻址,生成与位置无关的代码。
3.LDR使用绝对地址,而ADR使用相对地址。
4.LDR适用的地址范围为[0,32GB],而ADR则要求当前指令和标号必须在同一个段中,地址偏移的范围也比较小。

伪操作

一个ARM汇编程序至少要有一个代码段,可以用一个AREA伪操作来标识一个段的起始,段名和段的读写属性。

以上汇编程序由两个程序段组成:一个代码段和一个数据段。ARM汇编程序一般通过ENTRY作为汇编程序的运行入口,类比C语言的mian().使用伪操作END标识汇编程序的结束。

以上是汇编程序中的一些常用的伪操作可以帮助我们设计出更好的程序,实现汇编的模块化编程。

更多内容请关注微信公众号:学习学个der

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值