本文主要是介绍s5pv210开发板学习笔记(2)——ARM汇编指令集,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
指令与伪指令:
(汇编)指令:是CPU机器指令的助记符,经过编译后会得到一-串10组成的机器码,可以由CPU读取执行。
(汇编)伪指令:本质上不是指令(只是和指令一起写在代码中) ,它是编译器环境提供的,目的是用来指导编译过程,经过编译后伪指令最终不会生成机器码。
两种不同风格的ARM指令:
ARM官方的ARM汇编风格:指令一般用大写、Windows中IDE开发环境(如ADS、MDK等)常用。如:LDR RO, [R1]。
GNU风格的ARM汇编:指令一般用小写字母、linux中常用。如: ldr r0, [r1]
ARM汇编特点1:LDR/STR架构
ARM采用RISC中,CPU要和内存通信必须先把内存的内容读入CPU的37个通用寄存器中,才能被CPU处理。
ldr(load register)指令将内存内容加载入通用寄存器。
str(store register)指令将寄存器内容存入内存空间中。
ldr/str组合用来实现ARM CPU和内存数据交换
ARM汇编特点2:8种寻址方式
寄存器寻址 mov r1,r2
立即寻址 mov r0, #0xFF00
寄存器移位寻址 mov r0, r1, Isl #3
寄存器间接寻址 ldr r1, [r2]
基址变址寻址 ldr r1,[r2, #4]
多寄存器寻址 ldmia r1!, {r2-r7,r12}
堆栈寻址 stmfd sp!, {r2-r7, lr}
相对寻址 beq flag
注:只要是ld开头的指令就一定是内存和寄存器之间的加载
ARM汇编特点3:指令后缀
同一指令经常附带不同后缀,变成不同的指令。经常使用的后缀有:
B (byte) 功能不变,操作长度变为8位
H (half word)功能不变,长度变为16位
S (signed) 功能不变,操作数变为有符号
如:ldr ldrb ldrh ldrsb Idrsh
S (S标志)功能不变,影响CPSR标志位(NZCV)
如:mov和movs
ARM汇编特点4:条件执行后缀
mov r0, r1 @ 相当于C语言中的r0 = r1;
moveq r0, r1 @ 如果eq后缀成立,则直接执行mov r0, r1;如果eq不成立则本句代码直接作废,相当于没有。
类似于C语言中 if (eq){r0 = r1;}
条件后缀执行注意2点:
条件后缀是否成立,不是取决于本句代码,而是取决于这句代码之前的代码运行后的结果。
条件后缀决定了本句代码是否被执行,而不会影响上一句和下一句代码是否被执行。
ARM汇编特点5:多级指令流水线
为增加处理器指令流的速度,ARM使用多级流水线,下图为3级流水线工作原理示意图。(S5PV210使用13级流水线,ARM11为8级)
允许多个操作同时处理,而非顺序执行。
常用ARM指令1:数据处理指令
数据传输指令 mov mvn
算术指令 add sub rsb adc sbc rsc
逻辑指令 and orr eor bic
比较指令 cmp cmn tst teq
乘法指令 mvl mla umull umlal smull smlal
前导零计数 clz
mov(move)
mov r1, r0 @ 两个寄存器之间数据传递
mov r1, #0xff @ 将立即数赋值给寄存器
注:mov的两个操作数中不能有内存地址
mvn和mov用法一样,区别是mov是原封不动的传递,而mvn是按位取反后传递
按位取反的含义:譬如r1 = 0x000000ff,然后mov r0, r1 后,r0 = 0xff;但是我mvn r0, r1后,r0=0xffffff00。
and逻辑与
orr逻辑或
eor逻辑异或
bic位清除指令
bic r0,r1,#0x1f @ 将r1中的数的bit0到bit4清零后赋值给r0,0x1f = 0x0000001f=0x0000```11111
比较指令:
cmp cmp r0, r1 等价于 sub r2, r0, r1 (r2 = r0 - r1)
cmn cmn r0, r1 等价于 add r0, r1
tst tst r0, #0xf @测试r0的bit0~bit3是否全为0
teq teq x0, x1 用于比较操作数1和操作数2是否相等
比较指令用来比较2个寄存器中的数
注意:比较指令不用后加s后缀就可以影响cpsr中的标志位。
常用ARM指令2:cpsr访问指令
mrs & msr
mrs用来读psr,msr用来写psr(指cpsr/spsr)
CPSR寄存器比较特殊,需要专门的指令访问,这就是mrs和msr
cpsr和spsr的区别和联系:cpsr是程序状态寄存器,整个SoC中只有1个;而spsr有5个,分别在5种异常模式下,作用是当从普通模式进入异常模式时,用来保存之前普通模式下的cpsr的,以在返回普通模式时恢复原来的cpsr。
常用ARM指令3:跳转(分支)指令
b & bl & bx
b 直接跳转(就没打开算返回)
bl branch andlink,跳转前把返回地址放入lr中,以便返回,以便用于函数调用
bx跳转同时切换到ARM模式,一般用于异常处理的跳转。
常用ARM指令4:访存指令
ldr/str & ldm/stm & swp
单个字/半字/字节访问ldr/str
多字批量访问 ldm/stm
swp r1, r2, [r0] @ 将[r0]内存地址的内容放入r1寄存器中,将r2寄存器的值放入[r0]内存地址中,这两步同时进行。
swp r1, r1, [r0] @ 实现r1和[r0]内存地址中内容的互换
ARM汇编中的立即数
合法立即数与非法立即数
ARM指令都是32位,除了指令标记和操作标记外,本身只能附带很少位数的立即数。因此立即数有合法和非法之分。
合法立即数:经过任意位数的移位后非零部分可以用8位表示的即为合法立即数
合法立即数: 0x000000ff0x00ff0000 0xf000000f
非法立即数: 0x000001ff
常用ARM指令5:软中断指令
swi(software interrupt)
软中断指令用来实现操作系统中系统调用
协处理器和协处理器指令详解
什么是协处理器?
SoC内部另一处理核心,协助主CPU实现某些功能,被主CPU调用执行一定任务。
ARM设计上支持多达16个协处理器,但是一般SoC只实现其中的CP15.(cp:coprocessor)
协处理器和MMU、cache、TLB等处理有关,功能上和操作系统的虚拟地址映射、cache管理等有关。
CPU中的寄存器都以r开头,协处理器中的寄存器以c开头。
寄存器编号 | 基本作用 | 在MMU中的作用 | 在PU中的作用 |
0 | ID编码(只读) | ID编码和cache类型 | |
1 | 控制位(可读写) | 各种控制位 | |
2 | 存储保护和控制 | 地址转换表基地址 | Capability的控制位 |
3 | 存储保护和控制 | 域访问控制位 | Bufferablity 控制位 |
4 | 存储保护和控制 | 保留 | 保留 |
5 | 存储保护和控制 | 内存失效状态 | 访问权限控制位 |
6 | 存储保护和控制 | 内存失效状态 | 保护区域控制 |
7 | 高速缓存和写缓存 | 高速缓存和写缓存控制 | |
8 | 存储保护和控制 | TLB 控制 | 保留 |
9 | 高速缓存和写缓存 | 高速缓存锁定 | |
10 | 存储保护和控制 | TLB 锁定 | 保留 |
11 | 保留 | ||
12 | 保留 | ||
13 | 进程标识符 | 进程标识符 | |
14 | 保留 | ||
15 | 因不同设计而异 | 因不同设计而异 | 因不同设计而异 |
协处理器cp15操作指令:
mcr & mrc
mrc用于读取CP15中的寄存器
mcr用于写入CP15中的寄存器
使用方法:
mcr{<cond>} p15, <opcode_1>, <Rd>,<Crn>, <Crm>, {<opcode_2>}
mrc{<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
ldm/stm与栈的处理
ldr/str每周期只能访问4字节内存,如果需要批量读取、写入内存时太慢,解决方案是stm/ldm
ldm(load register mutiple)
stm(store register mutiple)
stmia sp,{r0 - r12}
将r0存入sp指向的内存处(假设为0x30001000);然后地址+4(即指向0x30001004),将r1存入该地址;然后地址再+4(指向0x30001008),将r2存入该地址······直到r12内容放入(0x3001030),指令完成。
一个访存周期同时完成13个寄存器的读写
8种后缀
ia(increaseafter)先传输,再地址+4
ib(increasebefore)先地址+4,再传输
da(decreaseafter)先传输,再地址-4
db(decreasebefore)先地址-4,再传输
fd(full decrease)满递减堆栈
ed(empty decrease)空递减堆栈
fa(·······) 满递增堆栈
ea(·······)空递增堆栈
四种栈
空栈:栈指针指向空位,每次存入时可以直接存入然后栈指针移动一格;而取出时需要先移动一格才能取出
满栈:栈指针指向栈中最后一格数据,每次存入时需要先移动栈指针一格再存入;取出时可以直接取出,然后再移动栈指针
增栈:栈指针移动时向地址增加的方向移动的栈
减栈:栈指针移动时向地址减小的方向移动的栈
!的作用:
ldmia r0,{r2 - r3}
ldmia r0!,{r2 - r3}
感叹号的作用:就是r0的值在ldm过程中发生的增加或者减少最后写回到r0去,也就是说ldm时会改变r0的值。
^的作用:
ldmfd sp!,{r0 - r6, pc}
ldmfd sp!,{r0 - r6, pc}^
^的作用:在目标寄存器中有pc时,会同时将spsr写入到cpsr,一般用于从异常模式返回。
ARM汇编伪指令
伪指令的意义:
伪指令不是指令,伪指令和指令的根本区别是经过编译后会不会生成机器码。
伪指令的意义在于指导编译过程。
伪指令是和具体的编译器相关的,我们使用gnu工具链,因此学习gnu环境下的汇编伪指令。
gnu汇编中的一些符号:
@ 用来做注释。可以在行首也可以在代码后面同一行直接跟,和C语言中//类似
# 做注释,一般放在行首,表示这一行都是注释而不是代码。
:以冒号结尾的是标号
. 点号在gnu汇编中表示当前指令的地址
# 立即数前面要加#或$,表示这是个立即数
常用gnu伪指令:
.global _start @ 给_start外部链接属性
.section .text @ 指定当前段为代码段
.ascii .byte .short .long .word @ .ascii用于定义字符,.byte用于定义字节,.short用于定义半字,.word和.long是字
.quad .float .string @ 定义数据 .quad用于定义双字,.float定义浮点型,.string用于定义字符串
.align 4 @以16字节对齐
.balignl 16 0xabcdefgh @ 16字节对齐填充
.equ @ 类似于C中宏定义
偶尔用:
.end @标识文件结束
.include @头文件包含
.arm / .code32 @声明以下为arm指令
.thumb / .code16 @声明以下为thubm指令
最重要的几个伪指令
ldr 大范围的地址加载指令
adr 小范围的地址加载指令
adrl 中等范围的地址加载指令
nop 空操作
ARM中有一个ldr指令,还有一个ldr伪指令
一般都使用ldr伪指令而不用ldr指令,因为ldr伪指令可以不用考虑立即数是否合法,编译器会帮你转换为合法立即数。
adr与ldr:
adr编译时会被1条sub或add指令替代,而ldr编译时会被一条mov指令替代或者文字池方式处理
adr总是以PC为基准来表示地址,因此指令本身和运行地址有关,可以用来检测程序当前的运行地址在哪里。
ldr加载的地址和链接时给定的地址有关,由链接脚本决定。
这篇关于s5pv210开发板学习笔记(2)——ARM汇编指令集的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!