ARM中怎么访问寄存器?就像访问内存一样,用指针
unsigned int *p = 0x40010800; // p等于某个寄存器的地址
*p = val; // 写这个地址,也就是写这个寄存器
val = *p; // 读寄存器
在ARM CPU看来,内存,I/O的操作是一样的。根据CPU发出的地址选择不同的设备。
在x86架构中内存和IO是分开的,根据CPU发出的指令选择不同的设备
ARM芯片属于精简指令集计算机(RISC:Reduced Instruction Set Computing),它所使用的指令比较简单,有如下特点:
X86属于复杂指令集计算机(CISC:Complex Instruction Set Computing),它所用的指令比较复杂,比如某些复杂的指令会通过“微程序”实现。
比如执行乘法指令时,实际上会去执行一个“微程序”,
在“微程序”里,
一样是去执行这4不操作:
① 读内存a
② 读内存b
③ 计算a*b
④ 把结果写入内存
但是对于程序员来说,
他看不到“微程序”,
他好像用一条指令就搞定了这一切!
CISC的指令能力强,单多数指令使用率低却增加了CPU的复杂度,指令是可变长格式;
RISC的指令大部分为单周期指令,指令长度固定,操作寄存器,对于内存只有Load/Store操作
CISC支持多种寻址方式;RISC支持的寻址方式
CISC通过微程序控制技术实现;
RISC增加了通用寄存器,硬布线逻辑控制为主,采用流水线
CISC的研制周期长
RISC优化编译,有效支持高级语言
无论是Cortex-M3/4或者A7,CPU内部都有R0~R15寄存器,用来暂存数据。
R13:SP,栈指针
R14:LR,用来保存返回地址
R15:PC,程序计数器,预执行指令地址,写入新值即可跳转
对于Cortex-M3/4,汇编指令里使用SP,但是可能对应不同的SP寄存器,一般情况下都是使用SP_main,运行RTOS时,可以让APP使用SP_process。
xPSR保存程序状态,比如上一条指令的执行结果、比较结果,也有一些控制作用,比如屏蔽中断,使能中断。
一开始,ARM公司发布两类指令集:
因此,要节省空间时使用Thumb指令,要效率时使用ARM指令。
一个CPU既可以运行Thumb指令,也能运行ARM指令。
怎么区分当前指令是Thumb还是ARM指令呢?
程序状态寄存器中有一位,名为“T”,它等于1时表示当前运行的是Thumb指令。
假设函数A是用Thumb指令写的,函数B是用ARM指令写的,怎么调用A/B?
我们可以往PC寄存器里写入函数A或B的地址,就可以调用A或B。
那么如何让CPU在执行A函数时进入Thumb状态,在执行B时进入ARM状态?
调用A时,让PC寄存器的BIT0等于1,即PC=函数A地址+(1<<0)
调用B时,让PC寄存器的BIT0等于0,即PC=函数B地址
很麻烦,因此引入Thumb2指令集,它支持16位指令集、32位指令混合编程。
有ARM、Thumb、Thumb2指令集,但是ARM公司推出了统一汇编语言,不需要去区分这些指令集。
在程序前面用CODE32/CODE16/THUMB表示指令集ARM/Thumb/Thumb2
Operation表示各类汇编指令,比如ADD、MOV
cond有多种取指,如
读内存指令:LDR/LDM
写内存指令:STR/STM
LDR:Load Register LDM:Load Multiple Register
STR:Store Register STM:Store Multiple Register
MOV R0,#VAL
意思是把VAL这个值存入R0寄存器。
VAL可以是任意值吗?
去判断一个VAL是否是立即数,很麻烦!
并且就是想把任意数值赋给R0,怎么办?
可以使用伪指令
LDR R0, =VAL
伪指令,就是假的、不存在的指令。
LDR作为“伪指令”时,指令中有一个=,否则它就是真实的LDR指令了。
编译器会把伪指令替换成真实的指令,比如:
LDR R0, =0x12
0x12是立即数,那么替换为:MOV R0,#0x12
LDR R0, =0x12345678
0x12345678不是立即数,那么替换为:
LDR R0,[PC,#offset] //2.使用Load Register读内存指令读出值,offset是链接程序时确定的
Label DCD 0x12345678 //1.编译器在程序某个地方保存这个值
ADR R0,Loop
Loop
ADD R0,R0,#1
它是伪指令,会被转换成某条真实的指令,比如:
ADD R0,PC,#val ;val在链接时确定
Loop
ADD R0,R0,#1
使用keil这种工具,所用到的编辑器就是armasm
arm-linux-gnueabihf这种就是GNU Assembler