一.指令格式
<opcode> {<cond>) (S) <Rd> ,<Rn>,<shift_operand> 其中<> 号内是必须的,{}号内的项时可选的
opcode - 指令助记符
- ADD 表示加法运算
- 操作码。比如加法、减法、乘法、除法等
cond 根据条件是否执行指令 条件码
原理:
- ARM系统中提供了一个专门的寄存器 (程序状态寄存器) cpsr
- 记录当前CPU的运行状态,他的最高的四位会有一个相应的记录
- 条件码会去查询cpsr寄存器的最高四位是否满足条件
常用条件:
- NE -- 不相等
- EQ -- 相等
- GT -- 大于
- LT -- 小于
- GE -- 大于或者等于
- LE -- 小于或者等于
S:表示指令执行的结果,决定是否影响 CPSR寄存器的N/Z/C/V值
CPSR (Current Program Status Register)特殊的寄存器,用于存储当前的程序状态信息
- N(Negative)标志位: 这个位用于表示上一条指令的结果是否为负数。当结果为负数时,N 置为 1,否则为 0.
- Z (Zero) 标志位: 这个位用于表示上一条指令的结果是否为零。当结果为零时,Z 置为 1,否
- 则为 0。
- C(Carry) 标志位: 这个位用于表示无符号算术运算的进位情况。当运算结果需要进位时,C 置为 1,否则为 0。C 位也用于支持高精度计算。
- V (Overflow) 标志位: 这个位用于表示有符号算术运算的溢出情况。当运算结果溢出时,V 置为 1,否则为 0。V 位也用于支持高精度计算。
Rd 目标寄存器
- 主要用于存放计算的结果需要放到哪里去
- 存放指令执行的结果RO~R15
Rn
- 第1个操作数
- 必须是一个寄存器
- 可省略
shift_operand -- 第2个操作数
立即数
- 一个常数,该常数必须对应8位位图
- 即一个8位的常数通过循环右移偶数位得到该数,该数为合法立即数
- 表示方法 #100
寄存器:mov r2, r1
寄存器移位(移位指令)
将寄存器值读取之后,进行移位运算后,作为操作数2
LSL: 逻辑左移 -(logic shifter left) 最低位补零
LSR: 逻辑右移 --无符号数据补零 无符号
ASR:算术右移
- 有符号数据左移还是补零,用逻辑左移代替
- 正数: 向右移动,低位丢弃,高位补0
- 负数:向右移动,最高位表示符号位,低位丢掉,高位补1 有符号
举例
- r0 ,lsr #4 表示r0 >> 4
- r0 ,lsr r1 表示 r0 >> r1
- #3 ,lsl #4 (错误) ,只能是寄存器移位
二.数据传送指令
mov
功能:将操作数2的值赋值给目标寄存器
语法:MOV{<cond>}{S} <Rd>,<shifter_operand> Rd = shifter operand
- shifter_operand —— 可以为立即数、寄存器,并且支持移位操作
- 立即数 —— 一般表示整数常量
- MOV 目标寄存器操作数2
MVN
对内存中存储的值进行按位取反(~)
格式 -- MVN 目标寄存器,操作数2
功能 -- 将操作数2取反的值给目标寄存器
LDR
功能 :将任意数据加载到目标寄存器中
为什么不用MOV存放到寄存器中?
- MOV能存放的只有合法立即数
- LDR可以直接将任意数据加载到寄存器中
指令格式:IDR{<cond>] <Rd>, <addressing mode>
- addressing mode :地址寻址模式
- LDR 目标寄存器,= 数据
- 数据前面不能加#号
- 注意 - =表示把原始数据给目标寄存器
通过寄存器获得要访问的内存地址,在进行操作,般只涉及一个数据的操作
示例 :
三.合法立即数
概念:在ARM 指今中可以使用的立即数必须是合法,否则导致ARM指令执行异常
原因:
- ARM 指令长度有限,不能都用于表示立即数,所以不能表示所有的立即数,最终会导致部分立即数不能表示。
- mov 指令中能够表示立即数的只有 12位,12 位中[7:0] 为数据位,[11:8] 为 移位的次数
合法立即数规则:所有的立即数必须要通过一个8位立即数通过偶数位获得,否则为不合法
判断立即数是否合法方法:
将对应的数转换为32 位16进制数:218 0xDA = 0x000000DA
1.除零外,仅有一位数为合法立即数
- 0x00010000
- 0x00f0000
- 0x020000
- 0x11
- 0x000011000
- 0x000220000
- 0x03300000
2.除零外,仅有二位数,并且相邻 (包括首尾如0x1000000A) 的为合法立即数
3.除零外,仅有三位数,并且相邻
包括中间有0相间,例如0x10800000,包括首尾相部,如: 0x14000003
这三位数中,最高位取值仅能为1、2、3,最低位取值仅能为4、8、C中间位0x0~0xF
- a) 0x00AB0000
- b) 0X0000FFEE
- e) 0x0000180
- c) 0xF000000
- d) 0x08000012
- e) 0x0000180
- F) OXFFFFFFFF
ace为合法立即数,bdf超过4位 不是合法立即数,0-255一定是立即数
0xFFFFFFFF 是有效数,原数是立即数或者原数反码是立即数叫做有效数
四.数据计算指令
ADD:加法指令
语法格式:
- ADD{<cond>{S]<Rd>,<Rn>,<shifter_operand>
- ADD 目标寄存器,操作数1 ,操作数2
SUB:减法指令
将操作数1减去操作数2的结果给目标寄存器
语法格式:
- SUB{<cond>]{s] <Rd><Rn>,<shifter_operand>
- SUB 目标寄存器,操作数1,操作数2
MUL:乘法指令
将操作数1乘以操作数2的结果存放在目标寄存器
语法格式:
- MUL{<cond>]{S]<Rd><Rm><Rs>
- MUL 目标寄存器,操作数1,操作数2
- 操作数1和操作数2必须都是寄存器,并且操作数1的寄存器编号不能和目标寄存器一样
五.比较指令
语法格式:
- CMP{<cond>} <Rn>,<shifter_operand>
- CMP 寄存器,操作数2
cmp 会影响 cpsr,也会影响带条件执行的指令的结果
.global _start
_start:
mov r0,#1 @立即数赋值
mov r1,r0 @寄存器赋值
mvn r2,r1 @取反赋值
ldr r3,=0x12345678
mov r1,#1
mov r2,#2
ldr r3,=0x12345
add r4,r1,r2,lsl #3
add r0,r4,r3
mov r0,#15
mov r1,#2
mov r2,#3
mov r3,#8
mul r1,r2,r1
sub r0,r0,r1
add r0,r0,r3
mov r0,#10
mov r1,#20
mov r2,#30
mov r3,#1
@cmp r0,r1
@addgt r1,r1,#1
@cmp r0,r2
@addle r2,r2,#1
cmp r0,r1
movlt r3,r1
movlt r1,r0
movlt r0,r3
cmp r0,r2
movlt r3,r2
movlt r2,r0
movlt r0,r3
cmp r1,r2
movlt r3,r2
movlt r2,r1
movlt r1,r3
stop:
b stop
六.跳转指令
1.B
语法格式:B{L} {<cond>} <target_address>
跳转到指定目标地址:
目标地址可以使用标号进行表示
跳转到标签,标签就是目标地址
2.BL
格式 :BL 标签
功能:跳到一个指定的标签,BL跳转之前,会将跳转前的PC的值保存到 LR 寄存器中
注意:
- b/bl指令24位立即数用于表示跳转目标地址,24位能够表示的范围为+/-8M在进行左移动2位相当于 +/-32M
- b/bl跳转指令的范围为 +/-32M
- 将指令中的24位带符号的补码立即数扩展为30位 (扩展其符号位);
- 将此30位数左移两位将得到的值加到PC寄存器中,即得到跳转的目标地址。由这种
- 计算方法可知,跳转的 范围大致为-32M~+32M
如果想回到BL指令后面的语句继续执行,该如何?
为什么不能用B指令,因为lr没有变化 会直接跳转到第一行 这时候可以考虑使用标签来完成
用汇编实现1+....+100
七.位运算
AND:按位与
语法格式
AND{<cond>}{S} <Rd>,<RD> <shifter operand>
- AND RO,R1,#2
- AND r0,r1,r2
- AND RO,R1,R2,LSL #3
0x12345678,将它的高16位存放在r0中,它的低16位存放r1中,然后将它的高16位和低16位相加
ORR:按位或
将操作数1按位或操作数2的结果存放在目标寄存器
语法格式:
ORR{<cond>}{s} <Rd>,<Rn>,<shifter operand>
ORR 目标寄存器,操作数1,操作数2
- ORR r0, r1,r2
- ORR R0,R1,#2
- ORR R0,R1,R2,LSL #3
EOR:按位异或
将操作数1按位异或操作数2的结果存放在目标寄存器
语法格式:
EOR{<cond>}{s} <Rd>,<Rn> <shifter_operand>
- Rd = Rn ^ shifter operand
- EOR r0,r1,r2
- EOR RO,R1,#2
- EOR RO,R1,R2,LSL #3
BIC:位清除
功能:
- 位清除
- 将操作数1按位与操作数2取反的结果存放在目标寄存器
- 目标寄存器 = 操作数1&~操作数2
语法格式:
- BIC{<cond>}{S} <Rd>,<Rn> ,<shifter operand>
- Rd = Rn ~shifter operand
- BIC 目标寄存器,操作数1,操作数2
技巧:
如果想让某一位清零,让该数据移位后取反再或,想让某一位置1,可以先清零再与
有一个数据32bit的data,将它的[18:9]清0,将它的[10:4]变成1010011,其他位不变
- data = data & ~(0x3ff << 9)
- data = data & ~(0x7f << 4)
- data = data (0x53 << 4)
九.内存访问指令
单内存操作
LDR:加载 (Load Register)
功能:
- 将内存中的值加载到寄存器中
- 读内存
- C语言中专门存储地址的变量 - 指针变量
LDR r0,[r1] ------将r1寄存器中的值作为地址取出里面的值读到r0中 (r0 =*r1)
地址寻址模式
寄存器间接寻址
- 通过寄存器获得要访问的内存地址,在进行操作,般只涉及一个数据的操作。
- 中括号[ ]内的寄存器 表示该寄存器当中存了一个地址
LDR Rd, [Rn, #offset]
- Rd是目标寄存器,用于存储加载的值
- Rn是基址寄存器,用于指定内存地址的基址
- #offset是一个立即数偏移量,用于指定相对于基址的偏移地址
STR:存储 (Store Register)
功能:
- 将寄存器中的值存储到内存中 ---------与dr 的用法类似
- 写内存
STR r0,[r1] -------将r0寄存器中的值写到r1存放的地址中 (*r1 = r0)
STR Rd, [Rn, #offset]
- Rd是源寄存器,存储需要存储的值
- Rn是基址寄存器,用于指定内存地址的基址
- #offset是一个立即数偏移量,用于指定相对于基址的偏移地址
将0x1234写到0x4000,0000将0xabcd写到0x4000,0004,然后从这两个地址读取数据做累加,最终结果存放在r0
基值寻址模式
- 设定一个基础地址,后续在寻址基于这个地址进行偏移
- 将基地址寄存器加上指今中给出的偏移量,得到数据存放得地址
1.前索引 ------先偏移,再取值
STR r0,[r1,#4] ------- *(r1 + 4) = r0
LDR r0,[r1,#4] ------- r0 = *(r1 + 4)
2.后索引 --------先取值,再偏移
STR r0,[r1],#4 *rl = r0
rl = r1 + 4
LDR r0,[r1],#4 r0 =*r1
r1 = r1 +4
这种方式会自动更新基地址
3.自动索引 ----------先偏移,再取值,并更新基地址
STR r0,[r1,#4]! *(r1 + 4)= r0
rl = r1 + 4
LDR r0,[r1,#4]! r0 =*(r1 + 4)
r1 = r1 + 4
两种模式都需要将内存地址保存到寄存器中,需要使用 ldr 伪指令用法
- ldr <Rd>,=immed address_32
- ldr r0,=0x40000000
练习:将1-10数据存放在基地址为0x40000000,将0x40000000起始地址的值拷贝到0x40000100
mov r0,#0x40000000 //基地址
mov r1,#1 //r1从1开始
mov r2,#10 //到10结束 后续需要与r2比较
bl data_add //标志位
mov r0,#0x40000000 //基地址
ldr r1,=0x40000100 //更新后的基地址
mov r2,#10 //从10开始递减
bl data_copy //标志位
data_add:
str r1,[r0],#4 //将r1的值写入r0中,偏移4位
add r1,r1,#1 //r1自加1
cmp r1,r2 //r1与r2比较
blt data_add //如果r1小于r2继续
mov pc,lr //利用pc跳转到第一次跳转的下一行语句,也就是第二段第一句
data_copy:
ldr r3,[r0],#4 //先将r0中的读出来给r3,再偏移4位
str r3,[r1],#4 //然后将r3中的数据放到r1中也就是0x40000100
sub r2,r2,#1 //r2自减
cmp r2,#0 //r2与0比较
bgt data_copy //r2大于0则继续循环
多寄存器操作
LDM
功能 :将一块内存数据,加载到多个奇存器中 ------读
指令格式:LDM{条件}{s} 基址寄存器{!},{Reglist}^
STM
功能:将多个寄存器的值,存储到一段内存中 -------写
指令格式:STM{条件}{s} 基址寄存器{!},{Reglist}^
MODE(地址模式)
IA (Increment After) 先操作寄存器(值),后增加地址 
IB (Increment Before) 先增加地址,后操作寄存器(值)
DA (Decrement After) 先操作寄存器,后减少地址
DB (Decrement Before) 先减少地址,后操作寄存器
在操作地址时,必须遵循大地址对应大寄存器编号,小地址对应小寄存器编号
地址操作模式:
基址寄存器:用来存放内存的起始地址
! :最后更新基址寄存器的值
Reglist
- 多个寄存器,从小到大,中间用"," 隔开{r0,r2,r3}或{r0,r7,r10}
- 寄存器号大的对应内存的高地址,寄存器号小的对应内存的低地址
在0x40000000地址依次将r1=10,r2=20,r3= 30,r5=50存入,然后r1到r5清零,在从内存读到 r1到r5,得到原始数值
mov r0,#0x40000000
mov r1,#20
mov r2,#30
mov r3,#40
mov r5,#50
stmia r0!, {r1-r3,r5}
mov r1,#0
mov r2,#0
mov r3,#0
mov r5,#0
ldmdb r0!, {r1-r3,r5}
注意:^ 它存在,如果Reglist没有pc的时候,这个时候操作的寄存器是用户模式下的寄存器在LDM指令中,有PC的时候,在数据传送的时候,会将SPSR的值拷贝到CPSR,用于异常的返回
十.栈操作指令
出栈 LDMFD
- ldm sp!,{registers(寄存器列表)}
进栈 STMFD
- stm sp!,{registers(寄存器列表)}
注意
- 在对栈操作之前,必须先设置sp的值
- 进栈和出栈的方式一样,ATPCS标准规定满减栈 ATPCS是arm公司针对arm平台编译器的规范
栈操作模式
ARM 提供了专门用于操作 sp(r13) 的指令,用法与 stm/ldm 地 址模式保持一致,在 arm 编程规范中,默认使用的是满递减模式
满栈递增
- 入栈是向高地址的方式移动,然后在存入数据
- 出栈是先拿出数据,然后向低地址方向移动。
满栈递减
- 入栈是向低地址的方式移动,然后在存入数据。
- 出栈是先拿出数据,然后向高地址方向移动
空栈递增
- 入栈先存放数据,然后向高地址方向递增
- 出栈是先向低地址方向移动,然后在拿出数据
空栈递减
- 入栈先存放数据,然后向低地址方向递增
- 出栈是先向高地址方向移动,然后在拿出数据
四种栈模式
满递增,满递减,空递增,空递减
FA、 FD、 EA 、 ED、
不是数据结构的栈 默认满递减(full decrease)。满足ATPCS,大地址往小地址走
sp设为0x40000000可以吗?
- 默认满递减,不能设置0x40000000为开始地址
- 应当设置0x40001fff为起始地址
- 但由于0x40001fff 末尾f是15 这不是一个4字节 对齐的地址,所以应当设置为0x40001ff0
练习 :将r1 #10, r2 #20进栈和出栈
ldr sp,=0x40001ff0
mov r0,#10
mov r1,#20
stmfd sp!, {r0,r1}
mov r2,#0
mov r3,#0
ldmfd sp!, {r2,r3}
十一.CPSR/SPSR操作指令
读操作 MRS Rn,CPSR/SPSR
将状态寄存器的值,读到通用寄存器
什么时候用
- Ⅰ.当需要改变程序状态寄存器的内容时,可用MRS将程序状态寄存 器的内容读入通用寄存器,修改后再写回程序状态寄存器。
- Ⅱ.当在异常处理或进程切换时,需要保存程序状态寄存器的 值,可先用该指令读出程序状态寄存器的值,然后保存。
指令示例
- MRS R0,CPSR ;传送CPSR的内容到R0
- MRS R0,SPSR ;传送 SPSR的内容到R0
写操作 MSR CPSR/SPSR, Rn
将通用寄存器的值,写到状态寄存器
指令示例
- MSR CPSR,R0;传送R0的内容到CPSR
- MSR SPSR,R0 ;传送R0的内容到SPSR
- MSR CPSR_c,R0;传送R0的内容到SPSR,但仅仅修改CPSR中的控制位域,低8位
写一段代码,将CPSR的第I(7)位清0,其他位不变(效果:使能IRQ异常)
将CPSR的第I(7)位置1.,其他位不变(效果:禁用IRQ异常)
mrs r0,cpsr //将cpsr放到寄存器中
mov r1,#1
bic r0,r0,r1,lsl #7 //r1左移7位 取反 之后 与r0
msr cpsr,r0 //将r0里面的值放入cpsr中
mrs r0,cpsr
mov r2,#1
@bic r0,r2,lsl #7 //第七位清零
orr r0,r0,r2, lsl #7 //第七位置1
msr cpsr,r0
十二.ARM体系的流水线作业
1.指令流水线
为增加处理器指令流的速度,ARM7 系列使用3级流水线. 允许多个操作同时处理,而非顺序执行。
2.最佳流水线
3.LDR 流水线举例 
4.分支流水线举例 
5.中断流水线举例 
十三.伪指令
LDR
- LDR,=Label 将标签的值直接赋值到寄存器中
- LDR,label 将标签对应的地址内容加载寄存器中
伪军
- 不是正规的部队
- 不是arm公司设计的指令,方便使用
定义
- 伪指令不是CPU一个真正的指令,是为了方便程序员使用,编译器设计的指令
- 这个指令ARM核无法直接识别,需要编译器对他翻译成ARM核所能识别的指令。
- 编译器在编译的过程中,把伪指令翻译成几条真正的指令实现。
ldr r0, =0x999 将值赋值给r0
ldr r0, =label 标签 是标签底下这条指令的地址
ldr r0,label 读标签label对应地址的值
adr,r0,label(先看后面的连接地址和运行地址) 根据当前的PC的值+/-偏移量,动态获取当前 Label所表示的内存地址
.word 0x12345678 在存储器分配4字节的内存单元, 并将它初始化为0x12345678