一、ARM编程-工作模式
ARM Cortex-A系列处理器支持如下八种工作模式:
-
用户模式(usr):User模式
操作系统之上应用程序代码运行本模式
-
系统模式(sys):System模式
操作系统内核源码里各个系统调用函数体内代码所处的工作模式
-
管理模式(svc):Supervisor模式
开机或复位 运行的代码
软中断异常的处理代码
-
中断模式(irq):Interrupt模式
中断是一种硬件通知机制
中断异常的处理代码所处的工作模式
-
快速中断模式(fiq):Fast-interrupt模式
快中断也是一种硬件通知机制,但对它的响应速度,要比中断异常更快
快中断异常的处理代码所处工作模式
-
未定义模式(und):undefine模式
当CPU无法正确执行某条指令,会产生未定义指令异常
未定义指令异常的处理代码所处的工作模式
-
中止模式(abt):abort模式
当CPU访问内存时如果出错,会产生数据中止异常或指令预取异常
数据中止异常或指令预取异常的处理代码所处工作模式
-
监控模式(mon):monitor模式
执行安全监控性质代码时所处的工作模式
用户模式:非特权模式
其他7种:特权模式
svc、irq、fiq、und、abt这五种模式专门用于对应的异常处理,因此又将他们统一称为:异常模式
二、ARM编程-寄存器组织
不同工作模式决定CPU内部寄存器的使用方式:
Cortex-A系列之前,ARM SOC处理器内部含编程可用寄存器37个
Cortex-A系列之后,ARM SOC处理器内部含编程可用寄存器40个
- R0~R12:通用寄存器:寄存器中存放的内容由程序员自行决定–
- R13:又叫做SP Stack Pointer 栈顶指针
- R14:又叫做LR LInk Register 链接寄存器
- R15:又叫做PC Program Counter 程序计数器
- CPSR:Current Program Status Register 当前程序状态寄存器
- SPSR:Saved Program Status Register 备份用程序状态寄存器,专门用于对CPSR进行备份
三、ARM指令一般形式
典型的ARM指令二进制编码格式:
-
opcode:操作码
-
cond:条件码
-
S:(S标记)决定指令的操作是否影响CPSR的值
-
Rd:目标寄存器
-
Rn:包含第1个操作数的寄存器编码
-
shifter_operand:表示第2个操作数
- 第25位为0时:寄存器或者寄存器加移位 5位移位数+3位移位方式+4位寄存器编号
- 第25位为1时:立即数 4位循环右移数+8位数值
每条ARM指令根据指定的条件码来决定是否会被执行
四、汇编程序中的语句
- 指令:有对应机器码的指令,需要CPU执行的各种运算
- 伪指令:形式类似于指令,但会被汇编器替换成同功能指令序列的语句
- 伪操作:没有对应机器码的操作,它是用于告诉汇编程序如何进行汇编的操作语句,在汇编过程中起作用,运行中不起作用
- #开头的整行注释,只能注释一整行
- @开头的行尾注释,即从@开始到行尾的内容被注释
- /* */包裹的多行注释
五、ARM指令分类
- 数据处理指令(重点)
- 数据传送指令:MOV MVN
- 算术运算指令:ADD ADC SUB SBC RSB RSC
- 逻辑运算指令:AND ORR EOR BIC
- 比较指令:CMP CMN TST TEQ
- 跳转指令:B(重点) BL(重点) BX(重点) BLX
- 乘法运算指令:MUL MLA SMULL SMLAL UMULL UMLAL
- PSR传输指令(重点):MRS MSR
- 访存指令(重点):LDR STR LDM STM SWP
- 异常产生指令:SWI(重点) BKPT
- 协处理器指令:CDP LDC STC MCR MRC
一般情况下,相同类的指令,具有同样寻址方式
寻址方式:操作数的来源方式
六、数据处理指令
6.1 数据传送指令
MOV指令:MOV{cond}{S} <Rd>, <shifter_operand>
功能:将shifter_operand表示的数据传送到目标寄存器中
程序中的作用:
- 将数据从一个寄存器传送到另一个寄存器中
- 将一个立即数传送到一个寄存器中
- 实现单纯的移位操作。左移可以实现操作数乘以2^n
- 当R15(PC)作为目标寄存器时,可以实现程序跳转
- 当R15(PC)作为目标寄存器且有S标志,除了跳转外,还会将对应的SPSR还原到CPSR
MVN指令:MVN{cond}{S} <Rd>, <shifter_operand>
功能:将shifter_operand表示的数据的反码传送到目标寄存器中
6.2 算术运算指令
ADD指令:ADD{cond}{S} <Rd>, <Rn>, <shifter_operand>
Rd = Rn + shifter_operand
ADC指令:ADC{cond}{S} <Rd>, <Rn>, <shifter_operand>
带进位的加法 Rd = Rn + shifter_operand+C
示例:64位的加法
.text @abcd
.globl _start
_start:
MOV R0,#0xff000000 @Low 32bits
MOV R1,#0x10000000 @High 32 bits
MOV R2,#0xff000000 @Low 32bits
MOV R3,#0x20000000 @High 32bits
ADDS R4,R0,R2
ADC R5,R1,R3
B .
.end
SUB指令:SUB{cond}{S} <Rd>, <Rn>, <shifter_operand>
Rd = Rn - shifter_operand
SBC指令:SBC{cond}{S} <Rd>, <Rn>, <shifter_operand>
带借位的减法 Rd = Rn - shifter_operand - (~C)
作业:实现64位的减法
提交方式:sub64_姓名.S
上交日期:明日10:00前
SUBS R4,R0,R2
SBC R5,R1,R3
RSB指令:RSB{cond}{S} <Rd>, <Rn>, <shifter_operand>
逆向减 Rd = shifter_operand - Rn
简化立即数做为被减数的情况
@5-r2
mov R1,#5
sub R0,R1,R2
rsb R0,R2,#5
RSC指令:RSC{cond}{S} <Rd>, <Rn>, <shifter_operand>
带借位的逆向减法 Rd = shifter_operand - Rn - (~C)
@常用技巧
/*Rd = Rx *(2^n+1)*/
ADD R0,R1,R1,LSL #n
/*Rd = Rx *(2^n-1)*/
RSB R0,R1,R1,LSL #n
/*求64位的相反数,R0、R1放置一个64位数,R2,R3放置其相反数,R0、R2放置64位数低32位*/
RSBS R2,R0,#0
RSC R3,R1,#0
/*利用左移n位相当于乘以2的n次方,可以优化乘法*/
/*将R2中的值乘以4*/
MOV R2,R2,LSL #2
/*将R2中的值乘以5*/
ADD R2,R2,R2,LSL #2
6.3 逻辑运算指令:按位的位运算(两个操作数的所有位按位做运算)
AND指令:AND{cond}{S} <Rd>, <Rn>, <shifter_operand>
Rd = Rn & shifter_operand
- 程序中主要来将指定位 置0 (不太方便)
构造一个辅助数(掩码)来完成将指定位置0
AND R1,R1,#0xFFFFF7F3 不正确
-
判断指定位是1还是0
判断R0的第6位是0还是1
AND R1,R0,#0x40
ORR指令:ORR{cond}{S} <Rd>, <Rn>, <shifter_operand>
Rd = Rn | shifter_operand
- 程序中主要来将指定位 置1
将R0的第5位 置1,其它位不变:ORR R0,R0,#0x20
-
和BIC指令配合来给某寄存器的指定位范围赋值
@将R0的低4位赋值为0b0101,其它位不变 MOV R0,#0xAB BIC R0,R0,#0xF ORR R0,R0,#0b0101
EOR指令:EOR{cond}{S} <Rd>, <Rn>, <shifter_operand>
Rd = Rn ^ shifter_operand 相同为零,不同为1
程序中主要来将指定位 取反:构造掩码,掩码中所谓为1的位,与第一个操作数对应做异或,会将这些位取反
BIC指令:BIC{cond}{S} <Rd>, <Rn>, <shifter_operand>
位清除:将指定位置0
Rd = Rn & (~shifter_operand)
BIC R1,R1,#0x800
BIC R1,R1,#0xC
@常用技巧
/*判断R0的第2位为0还是1*/
AND R1,R0,#4
/*将r3寄存器中的第3、5位置0*/
BIC R3,R3,#0x28
AND R3,R3,#FFFFFFD7 @非法立即数
/*将r3寄存器中的第3、5位置1*/
ORR R3,R3,#0x28
/*将r3寄存器中的第3、5位取反*/
EOR R3,R3,#0x28
/*将r3寄存器中的低四位设置成0101,其它位不变*/
BIC R3,R3,#0xF
ORR R3,R3,#0b0101
/*将R2的高八位放置到R3的低八位,R3的其它位不变*/
MOV R0,R2,LSR #24
BIC R3,R3,#0xFF
ORR R3,R3,R0
@测验
@1. 写一条 ARM 指令,分别完成下列操作:
@a) r0 = 16
MOV R0,#16
@b) r0 = r1 / 16 (带符号的数字)
MOV R0,R1,ASR #4
@c) r1 = r2 * 3
ADD R1,R2,R2,LSL #1
@d) r0 = -r0
RSB R0,R0,0
@2. 下面哪些立即数是数据处理指令中有效的数据?
@a) 0x00AB0000 合法
@b) 0x0000FFFF 非法
@c) 0xF000000F 合法
@d) 0x08000012 非法
@e) 0x00001f80 合法
@f) 0xFFFFFFFF 非法
@3. BIC指令做什么用?
@4. 为什么ARM 处理器增加了一条RSB 指令?
6.4 比较指令
没有S标志,这些指令只是为了设置CPSR高4位
这些指令不要计算结果,只要计算后CPSR高4位的状态
比较指令一般不会单独使用,往往是配合一些指令的条件助记符来实现选择执行
CMP指令:CMP{cond} <Rn>, <shifter_operand>
Rn - shifter_operand
SUBS
CMN指令:CMN{cond} <Rn>, <shifter_operand>
Rn + shifter_operand
ADDS
TST指令:TST{cond} <Rn>, <shifter_operand>
位测试 Rn & shifter_operand
ANDS
TEQ指令:TEQ{cond} <Rn>, <shifter_operand>
相等测试 Rn ^ shifter_operand
EORS
七、跳转指令
这些指令只能实现短跳转,要实现任意范围的跳转用MOV PC,<shifter_operand>
B指令:B{cond} <target_address>
跳转到指定位置,target_address在二进制编码中占24位,一般用于局部范围内跳转,跳转范围-32M~32M
一般用来局部代码的循环实现
BL指令:BL{cond} <target_address>
带链接的跳转:保存PC到LR,跳转到指定位置,一般用于子程序调用
BX指令:BX{cond} <Rm>
带状态转换的跳转:跳转到指定位置,Rm的bits[0]为1切换为Thumb状态、bits[0]为0切换为ARM状态,一般用于子程序返回
BLX指令1:BLX <target_address>
保存PC到LR,跳转到指定位置,并切换为Thumb状态
BLX指令2:BLX{cond} <Rm>
保存PC到LR,跳转到指定位置,Rm的bits[0]为1切换为Thumb状态、bits[0]为0切换为ARM状态
八、LDR伪指令装载任意数
LDR R0,=const
:可以装载任意的32位常数
- 这种形式LDR是伪指令
- 汇编器遇到该行,发现如果mov可以的话,就改为使用mov指令来完成,如果mov不行的话,就借用内存来实现该数的装载
- MOV与LDR伪指令的比较
用mov来实现,占用空间小,速度快,但是对立即数有限制
如果数比较大,用ldr r0,=const会多占空间,降低效率
在实际编程中,如果只是将一个常数赋值给一个寄存器,建议写成:LDR Rn,=常数
聪明的汇编器:
LDR R0,=0x1F ----> MOV R0,#0x1F
LDR R1,=0xABCD1234 ----> 将0xABCD1234放到内存,然后读内存数据到寄存器
MOV R2,#0xFFFFFFFF