ARM汇编语言

做题知识不够用,我又来学习了。

ARM指令集机器编码基本格式:

31-28 27-20 19-16 15-12 11-0
条件码 指令码 目的寄存器 操作数1寄存器 操作数2

1. 寻址方式

1.1 立即寻址

操作数包含在指令的32位机器编码中。#表示立即数。

1
2
ADD R0,R0,#1		;R0 <- R0 + 1
AND R8,R7,#0xFF ;R8 <- R7 AND 0xFF

问题:32位长立即数的编码问题(合法性问题)

原因:在指令中,立即数作为操作数2出现,编码格式中仅安排了12位,32位立即数不能直接编码。

解决:12位编码包括8位常数和4位循环右移值,由8位常数循环右移4位值的2倍得到最后的32位立即数。

例:MOV R0,#0x0000F200

机器代码:E3A00CF2

机器代码含义:E表示无条件,3A是MOV的机器码,00表示R0寄存器,C是4位循环右移值,F2是8位常数。

方法:0xF2循环右移12*2=24位得到原32位数值

移位前:
0000 0000 0000 0000 0000 0000 1111 0010
移位后:
0000 0000 0000 0000 1111 0010 0000 0000

1.2 寄存器寻址

操作数存放在寄存器中。

1.2.1 基本方式

1
ADD R0,R1,R2	;R0 <- R1 + R2

1.2.2 对第二操作数寄存器的移位操作

1
ADD R3,R2,R1,LSR #2		;R3 <- R2 + R1 / 4

移位方式:

移位方式 含义
LSL 逻辑左移(乘)
LSR 逻辑右移(除)
ASL 算术左移,和LSL一样。类似于小数点移位,左移数变小
ASR 算术右移,分正负来填充右移后的空余位,右移数变大
ROR 循环右移
RRX 带扩展的循环右移,循环右移1位后左端用C填充,这种方式只移位1位,所以无需指定移位位数

1.2.3 寄存器间接寻址

利用寄存器的值作为存储器指针,数据传送类的load/store类指令都使用寄存器间接寻址方式。

1
2
LDR R0,[R1]		;R0 <- mem32[R1]
STR R0,[R1] ;mem32[R1] <- R0

1.2.4 基址加偏移地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
前变址
LDR R0,[R1,#4] ;R0 <- mem32[R1+4]

自动变址
LDR R0,[R1,#4]! ;R0 <- mem32[R1+4]
;R1 <- R1 + 4

后变址
LDR R0,[R1],#4 ;R0 <- mem32[R1]
;R1 <- R1 + 4

寄存器偏移地址
LDR R0,[R1,R2] ;R0 <- mem32[R1+R2]
LDR R0,[R1,R2,LSL #2] ;R0 <- mem32[R1+R2*4]

1.3 多寄存器及块拷贝寻址

一条指令完成多字数据或数据块的传送。

基本指令:LDM/STM

基址寄存器变化方式 含义
IA 操作完后地址递增
IB 地址先增后完成操作
DA 操作完后地址递减
DB 地址先减后完成操作

多寄存器语法表示:多寄存器用{}包含,连续寄存器使用-间隔,不连续的用,分隔。

例1:

1
LDMID R0,{R1-R4,R6}	;R1=[R0],R2=[R0+4],...,R6=[R0+16]

R0作为基址寄存器,其值可自动更新:

1
LDMID R0!,{R1-R4,R6}

例2:

LDM指令和STM指令配合实现数据块拷贝:

1
2
LDMIA R0,{R1-R5}	;以R0为基址读取五字存储单元数据加载至R1-R5
STMIA R6,{R1-R5} ;将R1-R5中数据依次存入R6为起始地址的存储单元

1.4 堆栈寻址

存储空间中的数据栈与寄存器组之间的批量数据传输,采用R13(SP)作为堆栈指针,采用FILO(先进后出)的方式工作,SP指向栈顶

基本指令:LDM/STM

堆栈组织生成方式:

FD/ED:满递减/空递减

FA/EA:满递增/空递增

1
2
STMFD SP!,{R0-R7,LR}	;入栈
LDMFD SP!,{R0-R7,LR} ;弹出堆栈

先将LR入栈,最后才是R0入栈;先R0出栈,最后LR出栈。

FD相当于多寄存器寻址的DB。

事实上,堆栈寻址与多寄存器寻址均可操作堆栈,在需要保存特定某些寄存器值时,采用STM进行压栈,采用LDM操作弹出堆栈。

1
2
3
4
5
保存数据
STMDB SP!,{R0-R12,LR}

弹出数据
LDMIA SP!,{R0-R12,LR}

1.5 相对寻址

将程序计数器PC作为基址寄存器,指令中的地址标号字段作为偏移量进行寻址,跳转指令采用相对寻址方式。

2. ARM指令集

2.1 存储器访问(L/S)指令

常规:

1
2
LDR R2,[R5]				;将R5为地址的存储单元中数据加载至R2(读)
STR R1,[R0,#0X04] ;mem32[R0+4] <- R1(写)

传送数据类型:

1
2
LDRB R3,[R2],#1		;以R2为地址读取1字节数据至R3,R2 = R2 + 1
STRH R1,[R0,#2]! ;半字传送,传送R1中低2字节数据至R0+2为地址的存储单元,R0更新

多寄存器补充:在非用户或系统模式下,可出现^后缀,若LDM指令寄存器列表中包含PC,则会额外将SPSR拷贝。

2.2 数据处理类指令

2.2.1 数据传送指令

1
2
3
MOV R1,R0			;R1 <- R0
MOV R1,R0,LSL #3 ;R1 <- R0*8
MVN R0,#0 ;立即数0取反传送至R0,R0=-1

2.2.2 算数逻辑运算指令

例1:64位整数加法

R0/R1与R2/R3分别存放两个加数的低/高32位,R4/R5存放结果的低/高32位。

1
2
ADDS R4,R0,R2	;带S后缀结果影响CPSR的标志位C
ADC R5,R1,R3 ;带进位的加法,C标志位参与运算

例2:64位整数减

1
2
SUBS R4,R0,R2
SBC R5,R1,R3

例3:逆向减法

1
2
RSB R0,R1,R2	;R0=R2-R1
RSC R0,R1,R2 ;在上行指令基础上再减C标志位的反码

例4:逻辑运算

1
2
3
4
AND R0,R0,#3	;保持R0的0、1位,其余清零
ORR R0,R0,#3 ;置位R0的0、1位,其余不变
EOR R0,R0,#3 ;反转R0的0、1位,其余不变
BIC R0,R0,#3 ;清零R0的0、1位,其余不变

例5:比较指令

1
2
3
CMP R1,R0	;R1-R0,结果影响CPSR中的标志位,但不保留运算结果
CMN R0,#1 ;判断R0的值是否为1的补码,是则Z置位
;CMN指令将操作1寄存器减去操作数2的负值

例6:测试指令

1
2
TST R1,#3	;按位与,结果影响CPSR中的标志位
TEQ R1,R2 ;按位异或,结果影响CPSR中的标志位

上述比较与测试指令可与带S后缀的算数逻辑运算指令对比,如TEQ与EORS对比,其运算操作一致,但运算指令保留结果,比较测试指令只修改CPSR中标志位,不保留运算结果。

例7:乘法指令

MUL:32位乘法

MLA:三操作数乘法,将操作数1与操作数2相乘,结果加第三个操作数,存入目的寄存器

1
MLA Rd,Rm,Rs,Rn			;Rd <- Rm*Rs+Rn

规则:Rd和Rm不能是同一寄存器

形成两个矢量的标量积的例子:

1
2
3
4
5
6
7
8
9
10
	mov r11,#20				;r11 <- 20
mov r10,#0 ;r10 <- 0
loop:
ldr r0,[r8],#4 ;r0 <- mem32[r8]
;r8 <- r8 + 4
ldr r1,[r9],#4 ;r1 <- mem32[r9]
;r9 <- r9 + 4
mla r10,r0,r1,r10 ;r10 <- r0*r1+r10
subs r11,r11,#1 ;r11 <- r11 - 1
bne loop ;在Z标志位为0时跳转执行

2.2.3 数据交换指令

1
SWP R1,R1[R0]		;需要中间人

2.3 跳转指令

跳转指令用于控制程序的走向,可完成从当前指令向前或向后的32MB的地址空间跳转,包括基本跳转指令(无条件跳转)B,带返回的跳转指令BL,带状态切换(ARM与Thumb之间)的跳转指令BX,带返回和状态切换的跳转指令BLX。

ARM指令地址间隔为4,Thumb指令地址间隔为2。

例:

1
2
3
4
5
6
BL LABEL	;程序无条件跳转至LABEL处执行
;同时将当前的PC值保存至R14中

用B指令完成类似功能
MOV LR,PC
B LABEL

跳转范围不受限制的方式:

1
LDR PC,=LABEL

BEQ:相等就跳

NEQ:不相等就跳

2.4 程序状态寄存器访问指令

当前程序状态寄存器可分为4个8位独立域:

1
2
3
4
CPSR[31:24]:_f(标志域)
CPSR[23:16]:_s(状态域)
CPSR[15:8]:_x(扩展域)
CPSR[7:0]:_c(控制域)

例:清CPSR标志位,先读到寄存器里去再进行修改,修改完后用MSR指令赋值给标志位。

1
2
3
MRS R0,CPSR		;R0 <- CPSR
BIC R0,R0,#0xF0000000 ;清高四位
MSR CPSR_f,R0 ;CPSR_f <- R0