学习XiUOS需要用到很多ARM的汇编代码,所以对一些网上看到的博客进行了总结
r0, r1, r2, ..., r12 //通用寄存器
SP(R13) // Stack Point 栈地址,有MSP(Main Stack Point)和PSP(Process Stack Point)之分,取决于CONTROL.BIT1,一般用户使用MSP,系统使用PSP
LR(R14) // Link Register 函数返回,异常返回
PC(R15) // Program Counter 当前指令地址
PSR Program Status Register
PRIMASK //屏蔽可配置的优先级
FAULTMASK //屏蔽出了NMI意外所有可配置的优先级
BASEPRI //屏蔽熟知上不小于它的优先级
CONTROL //用于设置特权级,选择堆栈指针以及FPU上下文切换
以.开头的都是汇编伪指令,不属于ARM指令集
.byte 定义单字节数据
.hword/.short 定义双字节数据
.word/.long /.int 定义4字节数据
.quad 定义8字节数据
.float/.single 定义32bit 浮点数
.double 定义64bit浮点数
.ascii 定义字符串以\0为结束符
.asciz /.string 定义字符串(非零结束符)
.fill 分配一段字节内存单元,用size长度value填充repeat次
.zero 分配一段字节内存单元,并用0填充内存
.space/.skip 分配一段内存单元,用value将内存单元初始化
.arm/.code32 定义以下代码使用ARM指令集
.thumb/.code16 定义以下代码使用Thumb指令集
.arch cpu的架构
.fpu 软件浮点
.section 定义一个段
例: .section expr ;expr可以是.text,.data,.bss
.text 将定义符开始的代码编译到代码段
.data 将定义符开始的数据编译到数据段
.bss 将变量存放到.bss段
.align 通过用零或或指定的数据进行填充来使当前位置满足一定的对齐方式,2的权次方对齐
例:
.align 1 2个字节对齐
.align 3 8个字节对齐
.type 说明一个类型
.size 占用内存的大小
.org指定从当前地址加上offset开始存放代码,并且从当前地址到当前地址加上offset之间的内存单元,用零或指定的数据进行填充
.global/.globl 用来定义一个全局的符号
.include 将一个源文件包含到当前源文件中
.end 标记汇编文件的结束行,即标号后的代码不作处理
_start 汇编程序的缺省入口是_start标号,用户也可以在链接脚本文件中用ENTRY标志指明其他入口点
LDR Load ldr ra, [rb] 把 *[rb] 放入 ra 中
STL Store str ra, [rb] 把 ra 放入 *[rb] 中
预索引:
ldr R0, [R1, #12] 把 * ([R1] + 12) 放入 R0 中
后索引:
ldr R0, [R1], #12 先执行 *[R1] 放入 R0 再执行 R1 = R1 + 12
加等号:
ldr R0, =array 把array的值放入R0中
和x86一样,可以加一些后缀,例如 B,H,W 表示取1,2,4个字节
LDM Load Multiple
STM Store Multiple
这两个指令表示大批量的传输数据
STM R0, {R4, R5} 表示把R4放到*[R0],R放到*([R0]+1)
其后缀 I, D表示Increase和Decrease
其后缀 A, B表示After和Before,After为传送后再增加,即[i++] = x, Before为增加后传送即[++i]=x
!符号表示这个寄存器会在运行中随着自增自减
PUSH {R0-R3, LR} = STMDB SP!, {R0-R3, LR}
这里栈是往下生长的,所以是D + Before 这样就可以把每个值放进栈中,并且让栈指针递减
同理
POP {R0-R3, PC} = LDMIA SP!, {R0-R3, PC}
LSL Logic Shift Left
LSR Logic Shift Right
ROR Rotate Right 循环形式的移位,只有右移,右移 n = 左移32 - n
ASR Arithmetic Shift Left 只有右移,左移没区别
REV Reverse 大端变小端
BFI Bit Field Insert 对Bit进行操作
BFX Bit Field Extract
BFC Bit Field Clear
B label 跳到Label
BX Register 跳到寄存器的位置
BL func1 L表示记录函数返回地址的Link Register,类似于call指令
IT
其中可以是"T"或者"E" (Then) (Else)
例子:
下面的c代码:
if (R0==R1) { R3 = R4 + R5; R3 = R3 / 2; }
else { R3 = R6 + R7; R3 = R3 / 2; }
使用IT指令块汇编的结果是这样的:
CMP R0, R1 ; compare R0 and R1
ITTEEEQ ; if R0 == R1, Then-Then-Else-Else
ADDEQ R3, R4, R5 ; R3 = R4 + R5
ASREQ R3, R3, #1 ; R3 = R3 / 2
ADDNE R3, R6, R7 ; R3 = R6 + R7
ASRNE R3, R3, #1 ; R3 = R3 / 2
Program State Register
CPSR current 当前程序状态储存器
SPSR saved 保存的程序状态寄存器 - 共6个,在处理异常的时候使用
对于32位的ARM处理器,状态寄存器长度也为32
分为四个部分
条件标志位:N - Negative Z - Zero C - Carry V - Verflow 和x86一致,表示计算结果是否小于0,等于0,进位,溢出,这四个bit分别在 bit[31 - 28]
Q标志位:Armv5的E系列处理器,CPSR的bit[27]表示DSP指令是否发生了溢出
控制位:I (IRQ disable), F(FIQ disable), T(Thumb state bit),M[4:0] 表示控制位,其中控制位
[10000] 用户模式 [10001] FIQ模式 [10010] IRQ模式 [10011] 管理模式 [10111] 中止模式 [11011] 未定义模式 [11111] 系统模式 [10110] Secure Monitor
保留位:用于将来ARM版本的扩展
MRS {} , CPSR/SPSR if cond : [Rd] = CPSR/SPSR
MSR {} CPSR_, # if cond : CPSR_ = #
MSR {} CPSR_, if cond : CPSR_ =
其中设置需要操作的位。
f: 指示bits[31:24] 条件标志位
s: 指示bits[23:16]
x: 指示bits[15:8]
c: 指示bits[7:0] 控制位
Change Processor State
可以更改CPSR中的M[4:0],也可以更改 I,F
CPSID = CPS Interrupt Disable 关闭中断
CPSFE = CPS Fast Interrupt Enable 打开快速中断
后面加的参数又可以选 I,F
CPSIE I PRIMASK 写 0
CPSID I PRIMASK 写 1,可以屏蔽其他低于自己优先级的中断,如果此中断的优先级为0,则屏蔽所有中断
CPSIE F FAULTMASK 写 0
CPSIE F FAULTMASK 写 1
MVN < dest > < src > 非
ORR{条件}{S} < dest >,
AND{条件}{S} < dest >,
SUB{条件}{S} < dest >,
ADD{条件}{S} < dest >,
BIC{条件}{S} < dest >,
什么也不做,一般是为了防止数据污染卡流水线用的