一图流
ARM
计算机组成
输入设备
输出设备
存储设备
运算器
控制器
处理器读取内存程序执行的过程
取指阶段:控制器器通过地址总线向存储器发送想要获取的指令的地址编号,存储器将指定的指令发送给处理器
译码阶段:控制器对指令进行分析和译码,确定当前指令的作用
执行阶段:控制器通知运算器进行对应的指令运算
指令
指令集
概念
指令的集合
不同的指令集可以被当作设计不同处理器内核的架构
一般在设计一个内核时会预先将这个处理器内核可以进行的运算的指令设计出来,把这些指令组成一个指令集,按照这个指令集设计这个内核
复杂指令集(CISC)
看重当前处理器的处理能力,将所有能够进行的运算操作的指令组成一个指令集,这种指令集就是复杂指令集
优点:功能强大
缺点:功耗、成本、体积都高
特点
每一条指令的长度不固定
每一条指令的执行周期也不固定
复杂指令集(CISC)
追求功能的特定性,将使用频度最高,比较简单的指令组成指令集
精简指令集优点:开发成本低、功耗低
缺点:实现的功能简单
特点
指令的大小固定
指令的执行周期固定
常见的精简指令集架构
ARM架构:ARM公司设计的精简指令集架构,使用的最多
RISC-V架构:开源,未来会火
MIPS架构:中国龙芯收购,完全垄断
ARM架构
ARMV1-ARMV6 //基本不被使用
ARMV7:32位处理器架构 支持32位指令集
ARMV8:64位处理器架构,支持64位指令集,向下兼容32位指令集
ARMV9:64位处理器架构,支持64位指令集
ARM v7架构处理器的工作模式
User : 非特权模式,大部分任务执行在这种模式
FIQ : 当一个高优先级(fast) 中断产生时将会进入这种模式
IRQ : 当一个低优先级(normal) 中断产生时将会进入这种模式
Supervisor(SVC) :当复位或软中断指令执行时将会进入这种模式
Abort : 当存取异常时将会进入这种模式
Undef : 当执行未定义指令时会进入这种模式
System : 使用和User模式相同寄存器集的特权模式
ARMv7架构的寄存器组织
概述
-
不同工作模式下使用的内核中的寄存器是不一样的
-
不算MON和HYP模式,用户可操作的寄存器总共有37
-
每一个寄存器的大小都是4字节
寄存器
R0-R12用来保存基本的操作数据和运算的数据
R13寄存器
栈指针寄存器(STACK POINTER/sp)
sp寄存器保存该模式下栈内存栈顶地址,用于进行栈内存读写
R14寄存器
链接寄存器(link register/LR)
链接寄存器是用于特定程序跳转场景下用于保存程序的返回地址
R15寄存器
程序计数器(program counter/PC)
PC寄存器中保存马上要被取指的指令地址
当PC保存的地址执行后,PC的数值会自动向下加一条指令的大小
实现程序跳转的本质就是修改PC的值
SPSR寄存器(saved program status register)
被保存的程序状态寄存器
当处理器因为各种异常切换到异常模式工作时会将CPSR值提前保存到SPSR中,处理器处理完异常之后会将SPSR的值赋值给CPSR,用于恢复处理器的工作状态
CPSR寄存器(current program status register)
记录当前程序状态寄存器
工作状态中包含程序的工作模式、中断禁止位以及程序的运算结果条件位等信息
状态寄存器
N[31] : 指令的运行结果为负数时,N位被自动置1,否则为0
Z[30] : 指令的运行结果为零时,Z位被自动置1,否则为0.
C[29] :
加法:加法运算如果产生进位,C位被自动置1,否则为0.
32位指令:低32位向高32位进位
减法:减法运算如果产生借位,C位被自动清0,否则位1.
32位指令:低32位向高32位借位
V[28] : 符号位发送变化,V位被自动置1,否则清0.
I[7] : IRQ中断屏蔽位
I = 0 : 不屏蔽IRQ中断
I = 1 : 屏蔽IRQ中断
F[6] : FIQ中断屏蔽位
F = 0 : 不屏蔽FIQ中断
F = 1 : 屏蔽FIQ中断
T[5] : 状态位
M[4:0] : 模式位
10000 User mode
10001 FIQ mode;
10010 IRQ
10011 SVC mode;
10111 Abort mode;
11011 Undfined mode;
11111 System mode;
10110 Monitor mode;
数据汇编指令
基本格式
<opcode>{<cond>}{s} <Rd>, <Rn>, <shifter_operand>
解释:如果满足 cond 条件,则让 Rn 和 shifter_operand 进行 opcode 操作,如果写了 s 则在运算结束后影响 CPSR 寄存器
参数
opcode:指令功能码,表示当前指令
cond:条件码,可写可不写,在指令执行之前先判断条件是否满足,不满足则不执行
s:可写可不写,如果在指令码后面加了一个s,则进行运算时会影响到CPSR条件位
Rd:目标寄存器
Rn:第一操作寄存器
shifter_operand:第二操作数
注意
第一操作寄存器只能是寄存器,不能是数值
第二操作数可以为寄存器,也可以为数值,如果为数值,需要在前面加上#
汇编指令不区分大小写
数据处理指令
数据搬移指令
mov {条件码} 目标寄存器 操作数
将操作数搬移到目标寄存器中
mvn {条件码} 目标寄存器 操作数
将操作数先取反,再搬移到目标寄存器中
注意
以上两个操作指令只能搬移立即数
如果需要搬移非立即数可以使用ldr
ldr 目标寄存器,=数据
从内存中寻找=数据的值放到目标寄存器中
立即数
一个32位指令空间中预留了12位保存操作数,可以通过某一个规则对操作数进行处理,将处理后的数值存放在这个12位空间中,所以处理完能够保存到12位空间中的数据就是立即数,否则不是
规则
将操作数循环右移偶数位 ,如果能够得到一个0-255内的数据,就说明这个数据是一个立即数
反过来,得到的数据循环右移偶数位,也可以得到操作数
指令中操作数的12位空间分为低8位和高4位,低8bit保存循环右移得到操作数的那个0-255内的数字
高4位保存0-255内的数据循环右移的偶数位/2
数据移位指令
lsl{条件码} 目标寄存器,第一操作寄存器,第二操作数
将第一操作寄存器的数据逻辑左移第二操作数位,然后放到目标寄存器
lsr{条件码} 目标寄存器,第一操作寄存器,第二操作数
将第一操作寄存器的数据逻辑右移第二操作数位,然后放到目标寄存器
ror{条件码} 目标寄存器,第一操作寄存器,第二操作数
将第一操作寄存器的数据循环右移第二操作数位,然后放到目标寄存器
注意
可以将其写成c语言风格
例:LSL R1,R0,#(0X1<<2),及那个R0的数据逻辑左移2位放到R1中
位运算指令
and{条件码} 目标寄存器,第一操作寄存器,第二操作数
将第一操作寄存器的数和第二操作数进行与运算,然后放到到目标寄存器中
orr{条件码} 目标寄存器,第一操作寄存器,第二操作数
将第一操作寄存器的数和第二操作数进行或运算,然后放到到目标寄存器中
eor{条件码} 目标寄存器,第一操作寄存器,第二操作数
将第一操作寄存器的数和第二操作数进行异或运算,然后放到到目标寄存器中
mvn,按位取反
bic{条件码} 目标寄存器,第一操作寄存器,第二操作数
将第一操作寄存器的数和第二操作数的取反值进行与运算,结果保存到目标寄存器中
算数运算符指令
add{条件码}{s} 目标寄存器,第一操作寄存器,第二操作数
目标寄存器=第一操作寄存器+第二操作数
adc{条件码}{s} 目标寄存器,第一操作寄存器,第二操作数
目标寄存器=第一操作寄存器+第二操作数+CPSR寄存器c位的值
sub{条件码}{s} 目标寄存器,第一操作寄存器,第二操作数
目标寄存器=第一操作寄存器-第二操作数
sbc{条件码}{s} 目标寄存器,第一操作寄存器,第二操作数
目标寄存器=第一操作寄存器-第二操作数-cpsr寄存器c位的取反值
mul{条件码}{s} 目标寄存器,第一操作寄存器,第二操作数
目标寄存器=第一操作寄存器*第二操作数
32位寄存器运算64位数
32位处理器一条指令能运算32位数据,要进行64位数据运算,要多条指令
遵守原则
低32位进行运算,运算的结果影响到CPSR条件位
高32位数据进行运算,运算结果考虑到cpsr条件位
比较指令
cmp 第一操作寄存器,第二操作数
将第一操作数和第二操作寄存器进行比较,本质是进行减法运算,然后影响CPSR的位
所以通常会跟这条件码混用
tst 目标寄存器,#(0X1<<N)
这个指令用来判断目标寄存器的第N位是否为0,然后影响CPSR的Z位
TEQ 目标寄存器,第二操作数
判断目标寄存器的值是否和第二操作数相等,并只能影响CPSR寄存器的z位,无法影响c和v位
跳转指令
b 标签
跳转到指定的标签下,跳转后LR寄存器不保存程序返回地址
bl 标签
跳转到指定的标签下,跳转后LR寄存器保存程序返回地址
内存读写指令
str
str 目标寄存器,[目标地址]
将目标寄存器中4字节数据写入到目标地址对应的内存中
strh 目标寄存器,[目标地址]
将目标寄存器中2字节数据写入到目标地址对应的内存中
strb 目标寄存器,[目标地址]
将目标寄存器中1字节数据写入到目标地址对应的内存中
ldr
ldr 目标寄存器,[目标地址]
从目标地址对应的内存中读取4字节数据保存到目标寄存器中
ldrh 目标寄存器,[目标地址]
从目标地址对应的内存中读取2字节数据保存到目标寄存器中
ldrb 目标寄存器,[目标地址]
从目标地址对应的内存中读取2字节数据保存到目标寄存器中
寄存器内存读写地址索引
前索引
str 目标寄存器,[基地址,偏移量]
将目标寄存器的数据写入到基地址+偏移量的地址中
ldr 目标寄存器,[基地址,偏移量]
从基地址+偏移量的地址中读取数据保存到目标寄存器中
后索引(数据先保存,地址再加加)
str 目标寄存器,[基地址],偏移量
将目标寄存器的数据写入到基地址的内存中,然后基地址增加偏移量大小
ldr 目标寄存器,[基地址],偏移量
从基地址内存中读取数据保存到目标寄存器中,,然后基地址增加偏移量大小
自动索引(地址先加加,再保存到地址)
str 目标寄存器,[基地址,偏移量]!
先将基地址自增偏移量大小,再将目标寄存器的数据写入到基地址的内存中
ldr 目标寄存器,[基地址,偏移量]!
先将基地址自增偏移量大小,再从基地址内存中读取数据保存到目标寄存器中
批量寄存器内存读写
基地址不变
stm 基地址,{寄存器列表}
将寄存器列表中所有寄存器的数据都写入到基地址为首地址的内存中
ldm 基地址,{寄存器列表}
从基地址开始往下读取数据,保存到寄存器列表中的每一个寄存器中
注意
寄存器列表可以不连续,但是他始终是从小到大写或读
基地址往大增长
先操作,在增长
stmia 基地址!,{寄存器列表}
先向基地址对应的内存中写入一个数据,基地址往大地址方向增长
ldmia 基地址!,{寄存器列表}
先读取基地址对应内存的数据,基地址往大地址方向增长
先增长,再操作
stmib 基地址!,{寄存器列表}
基地址往大地址方向增长 再向基地址对应的内存中写入一个数据
ldmib 基地址!,{寄存器列表}
先基地址往大地址方向增长,再读取基地址对应内存的数据
基地址往小减少
先操作,再减少
stmda 基地址!,{寄存器列表}
先向基地址对应的内存中写入一个数据,基地址往小地址方向增长
ldmda 基地址!,{寄存器列表}
先读取基地址对应内存的数据,基地址往小地址方向增长
先减少,在操作
stmdb 基地址!,{寄存器列表}
基地址往小地址方向增长 再向基地址对应的内存中写入一个数据
ldmdb 基地址!,{寄存器列表}
先基地址往小地址方向增长,再读取基地址对应内存的数据
栈内存读写
栈区是一段指定好的内存空间,一般用来保存临时数据,SP寄存器保存栈顶地址
栈的分类
空栈(EA):压栈完毕后,SP保存的栈顶地址空间中没有有效数据
满栈(ED):压栈完毕后,SP保存的栈顶地址空间中有有效数据
增栈(FA):每次压栈一个数据,SP保存的栈顶地址往大地址增长
减栈(FD):每次压栈一个数据,SP保存的栈顶地址往小地址增长
ARM默认使用满减栈
栈的实现
按照ARM默认的方式进行压栈和出栈
push {寄存器列表}
pop {寄存器列表}
使用批量寄存器的命令实现
使用栈的专属后缀
状态寄存器传送指令
MRS 目标寄存器,CPSR
读取CPSR寄存器数值,保存到目标寄存器
MSR CPSR,操作数
修改CPSR寄存器的数值为操作数
注意
可以在特权模式下通过修改CPSR数值切换到USER模式
无法在useR模式下修改CPSR数值切换到特权模式,如果想要进入特权模式,需要发生对应的特权事件
异常产生指令
软中断
-
swi 操作数
-
操作数是一个立即数,这个操作数就是产生的软中断的中断号
-
异常模式和异常源
异常模式:当处理器发生异常后进入的工作模式
异常源:引发处理器产生异常的源头
五种异常模式,七种异常源
异常的处理过程
处理器发生异常后,会自动进行四大步三小步工作,处理异常
四大步三小步
保存CPSR数值到对应异常模式的SPSR寄存器中
修改CPSR数值
修改模式位为对应的异常模式 [4:0]
修改工作状态为ARM状态 [5]->0
根据当前异常的优先级禁用中断[7:6]
保存程序的返回地址到异常模式的LR寄存器中
修改PC的值到对应异常在异常向量表中的位置
异常的返回(需要手动返回)
将异常模式下的SPSR数值赋值给CPSR,恢复程序的状态
将异常模式下LR的数值赋值给PC,恢复程序执行的位置
伪操作
能够在程序编译过程中起到编译引导作用的内容
.text .global .if .else .endif.....
伪操作不占用指令内存
伪指令
不是汇编指令,但是可以起到指令的作用,伪指令也会占用一定内存空间
注释
不同汇编的注释方式不同
单行注释: @ ;
多行注释: /* */