一、一些专业术语
ROM:read only memory 只读存储器
RAM:ramdom access memory 随机访问存储器
IROM:internal rom 内部ROM,指的是集成到SoC内部的ROM
IRAM:internal ram 内部RAM,指的是集成到SoC内部的RAM
DRAM:dynamic ram 动态RAM
SRAM:static ram 静态RAM
二、ARM的编程模式和7种模式
1、
ARM 有7个基本工作模式:
User : 非特权模式,大部分任务执行在这种模式
FIQ : 当一个高优先级(fast) 中断产生时将会进入这种模式
IRQ : 当一个低优先级(normal) 中断产生时将会进入这种模式
Supervisor : 当复位或软中断指令执行时将会进入这种模式
Abort : 当存取异常时将会进入这种模式
Undef : 当执行未定义指令时会进入这种模式
System : 使用和User模式相同寄存器集的特权模式
2、注意
除User(用户模式)是Normal(普通模式)外,其他6种都是Privilege(特权模 式)。
User : 非特权模式,大部分任务执行在这种模式
FIQ : 当一个高优先级(fast) 中断产生时将会进入这种模式
IRQ : 当一个低优先级(normal) 中断产生时将会进入这种模式
Supervisor : 当复位或软中断指令执行时将会进入这种模式
Abort : 当存取异常时将会进入这种模式
Undef : 当执行未定义指令时会进入这种模式
System : 使用和User模式相同寄存器集的特权模式
2、注意
除User(用户模式)是Normal(普通模式)外,其他6种都是Privilege(特权模 式)。
Privilege中除Sys模式外,其余5种为异常模式。
各种模式的切换,可以是程序员通过代码主动切换(通过写CPSR寄存器);也可以是 CPU在某些情况下自动切换。
各种模式的切换,可以是程序员通过代码主动切换(通过写CPSR寄存器);也可以是 CPU在某些情况下自动切换。
各种模式下权限和可以访问的寄存器不同
三、ARM汇编的特点
1、LDR/STR架构
ARM采用RISC架构,CPU本身不能直接读取内存,而需要先将内存中内容加载入CPU中,通用寄存器中才能被CPU处理。
ldr(load register)指令将内存内容加载入通用寄存器。
str(store register)指令将寄存器内容存入内存空间中。
ldr/str组合用来实现 ARM CPU和内存数据交换
2、8种选址方式
寄存器寻址 mov r1, r2
立即寻址 mov r0, #0xFF00
寄存器移位寻址 mov r0, r1, lsl #3
寄存器间接寻址 ldr r1, [r2]
基址变址寻址 ldr r1, [r2, #4]
多寄存器寻址 ldmia r1!, {r2-r7, r12}
堆栈寻址 stmfd sp!, {r2-r7, lr}
相对寻址 beq flag
flag:
3、指令后缀
同一指令经常附带不同后缀,变成不同的指令。经常使用的后缀有:
B(byte)功能不变,操作长度变为8位
H(half word)功能不变,长度变为16位
S(signed)功能不变,操作数变为有符号
如 ldr ldrb ldrh ldrsb ldrsh
S(S标志)功能不变,影响CPSR标志位
如 mov和movs movs r0, #0
4、条件执行后缀
mov r0, r1 @ 相当于C语言中的r0 = r1;
moveq r0, r1 @ 如果eq后缀成立,则直接执行mov r0, r1;如果eq不成立则本句代码直接作废,相当于没有
@ 类似于C语言中 if (eq){r0 = r1;}
条件后缀执行注意2点:
(1)、条件后缀是否成立,不是取决于本句代码,而是取决于这句代码之前的代码运行后的结果。
( 2)、条件后缀决定了本句代码是否被执行,而不会影响上一句和下一句代码是否被执行。
四、常用的汇编指令
ldr(load register)指令将内存内容加载入通用寄存器。
str(store register)指令将寄存器内容存入内存空间中。
ldr/str组合用来实现 ARM CPU和内存数据交换
数据传输指令 mov mvn
**********************************************************
mov(move) mov r1, r0 @两个寄存器之间数据传递
mov r1, #0xff @ 将立即数赋值给寄存器
mvn和mov用法一样,区别是mov是原封不动的传递,而mvn是按位取反后传递
按位取反的含义:
譬如r1 = 0x000000ff,然后mov r0, r1 后,r0 = 0xff 但是我mvn r0, r1后,r0=0xffffff00
***********************************************************
算术指令 add sub rsb adc sbc rsc
逻辑指令 and orr eor bic
***********************************************************
and 逻辑与 orr 逻辑或 eor 逻辑异或
bic 位清除指令
bic r0,r1,#0x1f @ 将r1中的数的bit0到bit4清零后赋值给r0
0x1f = 0x0000001f=0x0000```11111
***********************************************************
比较指令 cmp cmn tst teq
***********************************************************
cmp cmp r0, r1 等价于 sub r2, r0, r1 (r2 = r0 - r1) //
CMP指令用于把一个寄存器的内容和另一个寄存器的内容或立即数进行比较,同时更新CPSR中条件标志位的值。标志位表示的是操作数1与操作数2的关系(大、小、相等)
cmn cmn r0, r1 等价于 add r0, r1 // 确定r0,r1是否为互为补数
tst tst r0, #0xf 测试r0的bit0~bit3是否全为0
teq 是对两个数进行eor,测试等价
比较指令用来比较2个寄存器中的数
注意:比较指令不用后加s后缀就可以影响cpsr中的标志位。
***********************************************************
乘法指令 mvl mla umull umlal smull smlal
前导零计数 clz
mrs & msr
mrs用来读psr,msr用来写psr
CPSR寄存器比较特殊,需要专门的指令访问,这就是mrs和msr。
************************************************************
cpsr和spsr的区别和联系:cpsr是程序状态寄存器,整个SoC中只有1个;而spsr有5个,分别在5种异常模式下,作用是当从普通模式进入异常模式时,用来保存之前普通模式下的cpsr的,以在返回普通模式时恢复原来的cpsr。
************************************************************
b & bl & bx
b 直接跳转(就没打开算返回)
bl branch and link,跳转前把返回地址放入lr中,以便返回,以便用于函数调用
bx跳转同时切换到ARM模式,一般用于异常处理的跳转。
swi(software interrupt)
软中断指令用来实现操作系统中系统调用
mrc用于读取CP15中的寄存器
mcr用于写入CP15中的寄存器
*****************************************************+++*****
**
MRC (读)& MCR(写)的使用方法
mcr{<cond>} p15, <opcode_1>, <Rd>, <Crn>, <Crm>, {<opcode_2>}
opcode_1:对于cp15永远为0
Rd:ARM的普通寄存器
Crn:cp15的寄存器,合法值是c0~c15
Crm:cp15的寄存器,一般均设为c0
opcode_2:一般省略或为0
************************************************************
ldr/str & ldm/stm & swp // 访存指令
单个字/半字/字节访问 ldr(向内存里读)/str(向内存里写)
多字批量访问 ldm/stm
swp r1, r2, [r0] // 把r0的内存地址的值读进r1中,再把r2里的内容写回内存中
swp r1, r1, [r0] // 让内存与寄存器交换内容
ldr/str每周期只能访问4字节内存,如果需要批量读取、写入内存时太慢,解决方案是stm/ldm
************************************************************
stmia sp, {r0 - r12}
将r0存入sp指向的内存处(假设为0x30001000);然后地址+4(即指向0x30001004),将r1存入该地址;然后地址再+4(指向0x30001008),将r2存入该地址······直到r12内容放入(0x3000130),指令完成。
stmafd // 满减栈,地址先-4,再传输
************************************************************
五、ARM汇编伪指令
1、伪指令的意义
伪指令不是指令,伪指令和指令的根本区别是经过编译后会不会生成机器码。
伪指令的意义在于指导编译过程。
伪指令是和具体的编译器相关的,我们使用gnu工具链,因此学习gnu环境下的汇编伪指令。
2、gnu汇编中的一些符号
@ 用来做注释。可以在行首也可以在代码后面同一行直接跟,和C语言中//类似
:以冒号结尾的是标号 // 代表这一句的地址
. 点号在gnu汇编中表示当前指令的地址
# 立即数前面要加#或$,表示这是个立即数
3、常用gnu伪指令
.global _start @ 给_start外部链接属性
.section .text @ 指定当前段为代码段
.ascii .byte .short .long .word
.quad .float .string
@ 定义数据
.align 4 @ 以16字节对齐
.balignl 16, 0xabcdefgh @ 16字节对齐填充
//b表示位填充;align表示要对齐;l表示long,以4字节为单位填充;16表示16字节对齐;0xdeadbeef是用来填充的原料。
.equ @ 类似于C中宏定义
4、偶尔会用到的gnu伪指令
.end @标识文件结束
.include @ 头文件包含
.arm / .code32 @声明以下为arm指令
.thumb / .code16 @声明以下为thubm指令
5、最重要的几个伪指令
ldr @ 大范围的地址加载指令
adr @小范围的地址加载指令
adrl @中等范围的地址加载指令
nop @空操作
ARM中有一个ldr指令,还有一个ldr伪指令
// ldr指令: ldr r0, #0xff
伪指令: ldr r0, =0xfffl @涉及到合法/非法立即数,涉及到ARM文字池
一般都是用伪指令,不用考虑合法&非法立即数
adr与ldr
// adr编译时会被1条sub或add指令替代,而ldr编译时会被一条mov指令替代或者文字池方式处理;
adr总是以PC为基准来表示地址,因此指令本身和运行地址有关,可以用来检测程序当前的运行地址在哪里
ldr加载的地址和链接时给定的地址有关,由链接脚本决定。
adr和ldr的差别:ldr加载的地址在链接时确定,而adr加载的地址在运行时确定;所以我们可以通过adr和ldr加载的地址比较来判断当前程序是否在链接时指定的地址运行。