ARM汇编基础:指令集与寻址模式

ARM(Advanced RISC Machine)是目前最广泛使用的处理器架构之一,特别是在移动设备和嵌入式系统中。本文将深入介绍ARM汇编语言的基础知识,包括指令集架构和各种寻址模式。

ARM架构概述

ARM处理器特点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@ ARM架构的主要特点:
@ 1. RISC(精简指令集)架构
@ 2. 32位处理器(ARMv7及之前)
@ 3. 16个通用寄存器(R0-R15)
@ 4. 条件执行机制
@ 5. 流水线设计
@ 6. 低功耗设计

.text
.global _start

_start:
@ 这是一个简单的ARM汇编程序示例
mov r0, #42 @ 将立即数42加载到寄存器r0
mov r1, #10 @ 将立即数10加载到寄存器r1
add r2, r0, r1 @ r2 = r0 + r1

@ 程序退出
mov r7, #1 @ 系统调用号(exit)
swi 0 @ 软件中断

ARM寄存器组织

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@ ARM寄存器详解
@ R0-R12: 通用寄存器
@ R13 (SP): 栈指针
@ R14 (LR): 链接寄存器(返回地址)
@ R15 (PC): 程序计数器
@ CPSR: 当前程序状态寄存器

.section .data
msg: .ascii "Hello ARM World!\n"
msg_len = . - msg

.section .text
.global _start

_start:
@ 演示寄存器使用
mov r0, #1 @ 文件描述符(stdout)
ldr r1, =msg @ 消息地址
mov r2, #msg_len @ 消息长度
mov r7, #4 @ 系统调用号(write)
swi 0 @ 执行系统调用

@ 栈操作示例
push {r0, r1, r2} @ 将寄存器压入栈
pop {r0, r1, r2} @ 从栈中弹出寄存器

@ 程序退出
mov r0, #0 @ 退出状态
mov r7, #1 @ exit系统调用
swi 0

ARM指令集分类

数据处理指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
@ 算术运算指令
.text
.global arithmetic_demo

arithmetic_demo:
@ 加法运算
mov r0, #10
mov r1, #20
add r2, r0, r1 @ r2 = r0 + r1 = 30
adc r3, r0, r1 @ r2 = r0 + r1 + carry

@ 减法运算
sub r4, r1, r0 @ r4 = r1 - r0 = 10
sbc r5, r1, r0 @ r5 = r1 - r0 - borrow
rsb r6, r0, r1 @ r6 = r1 - r0(反向减法)

@ 乘法运算
mul r7, r0, r1 @ r7 = r0 * r1
mla r8, r0, r1, r2 @ r8 = r0 * r1 + r2

@ 除法运算(需要软件实现或使用库函数)
@ ARM早期版本没有硬件除法指令

bx lr @ 返回

@ 逻辑运算指令
.global logical_demo

logical_demo:
mov r0, #0xFF @ r0 = 255
mov r1, #0x0F @ r1 = 15

@ 位运算
and r2, r0, r1 @ r2 = r0 & r1 = 0x0F
orr r3, r0, r1 @ r3 = r0 | r1 = 0xFF
eor r4, r0, r1 @ r4 = r0 ^ r1 = 0xF0
bic r5, r0, r1 @ r5 = r0 & (~r1) = 0xF0

@ 取反运算
mvn r6, r0 @ r6 = ~r0

@ 移位运算
lsl r7, r0, #2 @ r7 = r0 << 2(逻辑左移)
lsr r8, r0, #2 @ r8 = r0 >> 2(逻辑右移)
asr r9, r0, #2 @ r9 = r0 >> 2(算术右移)
ror r10, r0, #2 @ r10 = r0循环右移2位

bx lr

@ 比较指令
.global compare_demo

compare_demo:
mov r0, #10
mov r1, #20

@ 比较指令(设置标志位)
cmp r0, r1 @ 比较r0和r1
cmn r0, r1 @ 比较r0和-r1
tst r0, r1 @ 测试r0 & r1
teq r0, r1 @ 测试r0 ^ r1

@ 根据比较结果执行条件指令
moveq r2, #1 @ 如果相等,r2 = 1
movne r2, #0 @ 如果不相等,r2 = 0
movgt r3, #1 @ 如果r0 > r1,r3 = 1
movlt r3, #0 @ 如果r0 < r1,r3 = 0

bx lr

数据传输指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
@ 加载和存储指令
.section .data
array: .word 1, 2, 3, 4, 5
byte_data: .byte 0x12, 0x34, 0x56, 0x78
half_data: .hword 0x1234, 0x5678

.text
.global load_store_demo

load_store_demo:
@ 字(32位)操作
ldr r0, =array @ 加载array的地址到r0
ldr r1, [r0] @ 加载array[0]到r1
ldr r2, [r0, #4] @ 加载array[1]到r2
ldr r3, [r0, #8]! @ 加载array[2]到r3,并更新r0
ldr r4, [r0], #4 @ 加载当前r0指向的值到r4,然后r0+4

@ 存储操作
mov r5, #100
str r5, [r0] @ 将r5存储到r0指向的地址
str r5, [r0, #4] @ 将r5存储到r0+4的地址
str r5, [r0, #8]! @ 将r5存储到r0+8,并更新r0
str r5, [r0], #4 @ 将r5存储到r0指向的地址,然后r0+4

@ 半字(16位)操作
ldr r0, =half_data
ldrh r1, [r0] @ 加载16位数据到r1
ldrsh r2, [r0] @ 加载16位有符号数据到r2

mov r3, #0x9999
strh r3, [r0] @ 存储16位数据

@ 字节(8位)操作
ldr r0, =byte_data
ldrb r1, [r0] @ 加载8位数据到r1
ldrsb r2, [r0] @ 加载8位有符号数据到r2

mov r3, #0xAA
strb r3, [r0] @ 存储8位数据

@ 多寄存器传输
ldm r0, {r1, r2, r3, r4} @ 从r0指向的地址加载多个寄存器
stm r0, {r1, r2, r3, r4} @ 将多个寄存器存储到r0指向的地址

@ 栈操作(特殊的多寄存器传输)
push {r1, r2, r3, r4} @ 压栈
pop {r1, r2, r3, r4} @ 出栈

bx lr

分支指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
@ 分支和跳转指令
.text
.global branch_demo

branch_demo:
mov r0, #10
mov r1, #20

@ 无条件分支
b next_label @ 跳转到next_label

@ 这行代码不会执行
mov r2, #999

next_label:
@ 条件分支
cmp r0, r1
beq equal_label @ 如果相等则跳转
bne not_equal_label @ 如果不相等则跳转
bgt greater_label @ 如果r0 > r1则跳转
blt less_label @ 如果r0 < r1则跳转

less_label:
mov r2, #1 @ r0 < r1
b end_branch

greater_label:
mov r2, #2 @ r0 > r1
b end_branch

equal_label:
mov r2, #0 @ r0 == r1
b end_branch

not_equal_label:
mov r2, #3 @ r0 != r1

end_branch:
@ 函数调用和返回
bl function_call @ 调用函数(保存返回地址到LR)

bx lr @ 返回

function_call:
@ 函数体
mov r3, #42
bx lr @ 返回到调用点

@ 循环示例
.global loop_demo

loop_demo:
mov r0, #0 @ 计数器
mov r1, #10 @ 循环次数

loop_start:
cmp r0, r1 @ 比较计数器和循环次数
bge loop_end @ 如果计数器 >= 循环次数,退出循环

@ 循环体
add r2, r2, r0 @ 累加
add r0, r0, #1 @ 计数器+1

b loop_start @ 跳回循环开始

loop_end:
bx lr

ARM寻址模式

立即数寻址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@ 立即数寻址模式
.text
.global immediate_addressing

immediate_addressing:
@ 直接立即数
mov r0, #42 @ r0 = 42
mov r1, #0xFF @ r1 = 255
mov r2, #0x1000 @ r2 = 4096

@ 立即数的限制和扩展
@ ARM立即数必须能够通过8位数值循环右移偶数位得到
mov r3, #0xFF000000 @ 有效的立即数
mov r4, #0x000000FF @ 有效的立即数
mov r5, #0x00FF0000 @ 有效的立即数

@ 对于不能直接表示的立即数,需要使用其他方法
ldr r6, =0x12345678 @ 使用伪指令加载32位立即数

@ 立即数在算术运算中的使用
add r7, r0, #10 @ r7 = r0 + 10
sub r8, r1, #5 @ r8 = r1 - 5
and r9, r2, #0xFF @ r9 = r2 & 0xFF
orr r10, r3, #0x0F @ r10 = r3 | 0x0F

bx lr

寄存器寻址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@ 寄存器寻址模式
.text
.global register_addressing

register_addressing:
@ 直接寄存器寻址
mov r0, #10
mov r1, #20
mov r2, r0 @ r2 = r0
add r3, r0, r1 @ r3 = r0 + r1

@ 寄存器移位寻址
mov r4, r0, lsl #2 @ r4 = r0 << 2
add r5, r1, r0, lsl #1 @ r5 = r1 + (r0 << 1)
sub r6, r1, r0, lsr #1 @ r6 = r1 - (r0 >> 1)

@ 寄存器移位的各种形式
mov r7, r0, lsl r1 @ r7 = r0 << r1(左移r1位)
mov r8, r0, lsr r1 @ r8 = r0 >> r1(逻辑右移)
mov r9, r0, asr r1 @ r9 = r0 >> r1(算术右移)
mov r10, r0, ror r1 @ r10 = r0循环右移r1位

@ 在内存访问中使用寄存器移位
ldr r11, [r2, r0, lsl #2] @ r11 = *(r2 + (r0 << 2))
str r11, [r2, r0, lsl #2] @ *(r2 + (r0 << 2)) = r11

bx lr

内存寻址模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
@ 内存寻址模式
.section .data
test_array: .word 1, 2, 3, 4, 5, 6, 7, 8
test_struct: .word 0x12345678, 0x9ABCDEF0, 0x11111111

.text
.global memory_addressing

memory_addressing:
@ 1. 基址寻址
ldr r0, =test_array
ldr r1, [r0] @ r1 = test_array[0]

@ 2. 基址+偏移寻址
ldr r2, [r0, #4] @ r2 = test_array[1]
ldr r3, [r0, #8] @ r3 = test_array[2]

@ 3. 基址+偏移寻址(预索引,更新基址)
ldr r4, [r0, #12]! @ r4 = test_array[3], r0 += 12

@ 4. 基址寻址(后索引,先访问后更新)
ldr r5, [r0], #4 @ r5 = *r0, r0 += 4

@ 5. 基址+寄存器偏移
mov r6, #2
ldr r7, [r0, r6, lsl #2] @ r7 = *(r0 + (r6 << 2))

@ 6. 基址+寄存器偏移(预索引)
ldr r8, [r0, r6, lsl #2]! @ r8 = *(r0 + (r6 << 2)), 更新r0

@ 7. 基址+寄存器偏移(后索引)
ldr r9, [r0], r6, lsl #2 @ r9 = *r0, r0 += (r6 << 2)

@ 8. 相对寻址(PC相对)
adr r10, test_array @ r10 = test_array的地址

@ 9. 多寄存器加载/存储的寻址模式
ldr r0, =test_array

@ IA (Increment After) - 默认模式
ldmia r0, {r1, r2, r3, r4} @ 从r0开始加载,地址递增

@ IB (Increment Before)
ldmib r0, {r1, r2, r3, r4} @ 先递增地址再加载

@ DA (Decrement After)
ldmda r0, {r1, r2, r3, r4} @ 加载后地址递减

@ DB (Decrement Before)
ldmdb r0, {r1, r2, r3, r4} @ 先递减地址再加载

@ 栈操作的特殊寻址
@ PUSH等价于STMDB sp!
@ POP等价于LDMIA sp!

bx lr

复杂寻址示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
@ 复杂寻址模式应用示例
.section .data
matrix: .word 1, 2, 3, 4
.word 5, 6, 7, 8
.word 9, 10, 11, 12
.word 13, 14, 15, 16

struct_array: .word 0x1111, 0x2222, 0x3333 @ 结构体1
.word 0x4444, 0x5555, 0x6666 @ 结构体2
.word 0x7777, 0x8888, 0x9999 @ 结构体3

.text
.global complex_addressing

complex_addressing:
@ 二维数组访问:matrix[i][j]
@ 假设每行4个元素,每个元素4字节
ldr r0, =matrix
mov r1, #1 @ i = 1
mov r2, #2 @ j = 2

@ 计算地址:base + i * row_size + j * element_size
mov r3, #4 @ 每行4个元素
mul r4, r1, r3 @ i * row_size
add r4, r4, r2 @ i * row_size + j
ldr r5, [r0, r4, lsl #2] @ matrix[i][j]

@ 结构体数组访问
@ 假设每个结构体3个字段,每个字段4字节
ldr r0, =struct_array
mov r1, #1 @ 结构体索引
mov r2, #2 @ 字段索引

@ 计算地址:base + struct_index * struct_size + field_offset
mov r3, #3 @ 每个结构体3个字段
mul r4, r1, r3 @ struct_index * struct_size
add r4, r4, r2 @ + field_offset
ldr r5, [r0, r4, lsl #2] @ struct_array[1].field[2]

@ 字符串操作示例
adr r0, string_data
mov r1, #0 @ 字符索引

string_loop:
ldrb r2, [r0, r1] @ 加载字符
cmp r2, #0 @ 检查是否为字符串结束符
beq string_end

@ 处理字符(这里只是简单地递增计数)
add r1, r1, #1
b string_loop

string_end:
@ r1现在包含字符串长度

bx lr

string_data:
.asciz "Hello ARM Assembly!"
.align 4

条件执行

条件码和标志位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@ ARM条件执行机制
.text
.global conditional_execution

conditional_execution:
@ CPSR标志位:
@ N (Negative): 结果为负
@ Z (Zero): 结果为零
@ C (Carry): 进位/借位
@ V (Overflow): 溢出

mov r0, #10
mov r1, #20

@ 比较操作设置标志位
cmp r0, r1 @ 比较r0和r1,设置标志位

@ 条件执行指令
moveq r2, #0 @ 如果相等 (Z=1)
movne r2, #1 @ 如果不相等 (Z=0)
movgt r2, #2 @ 如果大于 (Z=0 && N=V)
movlt r2, #3 @ 如果小于 (N!=V)
movge r2, #4 @ 如果大于等于 (N=V)
movle r2, #5 @ 如果小于等于 (Z=1 || N!=V)

@ 无符号比较
movhi r3, #6 @ 如果高于 (C=1 && Z=0)
movlo r3, #7 @ 如果低于 (C=0)
movhs r3, #8 @ 如果高于等于 (C=1)
movls r3, #9 @ 如果低于等于 (C=0 || Z=1)

@ 其他条件
movmi r4, #10 @ 如果负数 (N=1)
movpl r4, #11 @ 如果正数或零 (N=0)
movvs r4, #12 @ 如果溢出 (V=1)
movvc r4, #13 @ 如果无溢出 (V=0)
movcs r4, #14 @ 如果进位 (C=1)
movcc r4, #15 @ 如果无进位 (C=0)

@ 条件分支
cmp r0, #5
blt less_than_5
bge greater_equal_5

less_than_5:
mov r5, #100
b condition_end

greater_equal_5:
mov r5, #200

condition_end:
bx lr

条件执行应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@ 条件执行的实际应用
.text
.global conditional_applications

conditional_applications:
@ 1. 求绝对值
mov r0, #-10 @ 输入值
cmp r0, #0 @ 与0比较
rsblt r0, r0, #0 @ 如果小于0,则r0 = 0 - r0

@ 2. 求最大值
mov r1, #15
mov r2, #25
cmp r1, r2
movlt r3, r2 @ 如果r1 < r2,则r3 = r2
movge r3, r1 @ 如果r1 >= r2,则r3 = r1

@ 3. 求最小值
cmp r1, r2
movlt r4, r1 @ 如果r1 < r2,则r4 = r1
movge r4, r2 @ 如果r1 >= r2,则r4 = r2

@ 4. 限制值在范围内(clamp)
mov r5, #50 @ 输入值
mov r6, #10 @ 最小值
mov r7, #30 @ 最大值

cmp r5, r6 @ 与最小值比较
movlt r5, r6 @ 如果小于最小值,设为最小值

cmp r5, r7 @ 与最大值比较
movgt r5, r7 @ 如果大于最大值,设为最大值

@ 5. 条件累加
mov r8, #0 @ 累加器
mov r9, #1 @ 计数器

accumulate_loop:
cmp r9, #10 @ 检查是否到达上限
bgt accumulate_end

@ 只累加偶数
tst r9, #1 @ 测试最低位
addeq r8, r8, r9 @ 如果是偶数,累加

add r9, r9, #1 @ 计数器递增
b accumulate_loop

accumulate_end:
bx lr

总结

ARM汇编语言的指令集和寻址模式是嵌入式开发和系统编程的基础:

核心特性

  • RISC架构:精简指令集,执行效率高
  • 条件执行:几乎所有指令都可以条件执行
  • 灵活寻址:多种寻址模式适应不同场景
  • 寄存器丰富:16个通用寄存器提供充足的存储

指令分类

  • 数据处理:算术、逻辑、比较、移位操作
  • 数据传输:加载、存储、多寄存器操作
  • 分支控制:跳转、函数调用、循环控制

寻址模式

  • 立即数寻址:直接使用常数值
  • 寄存器寻址:使用寄存器内容
  • 内存寻址:各种内存访问模式
  • 复合寻址:结合多种模式的复杂寻址

实际应用

  • 嵌入式系统:直接硬件控制
  • 操作系统内核:底层系统功能
  • 性能优化:关键代码段优化
  • 驱动程序:硬件接口编程

掌握ARM汇编语言能够帮助开发者更好地理解计算机底层原理,编写高效的嵌入式程序,并进行系统级的性能优化。

版权所有,如有侵权请联系我