ARM2.(汇编语言)
1.kill4界面介绍
1.1窗口恢复
若不小心关掉左侧项目列表,可以在window中恢复。
1.2Debug调试界面
编写好程序后可以进入Debug界面
左侧会显示寄存器状态
单步执行,会跟随函数逻辑跳转到对应行执行,比如函数调用会跳转到相应行数1执行。
单步执行,但是遇到函数调用会直接将调用的函数执行完毕拿到执行结果,跳转到函数调用的下一行代码执行。
2.汇编语言
2.1汇编语言框架
area reset, code, readonly code32entryend
area reset, code, readonly:这是一个汇编指令,用于定义一个名为“reset”的代码区域,该区域是只读的。通常用于嵌入式系统或启动代码中,表示复位向量或初始化代码。
code32:指定接下来的代码是 32 位 ARM 指令(而不是 Thumb 指令)。
entry:表示程序的入口点(类似于主函数或启动点)。
end:代码结束。
2.2mov赋值操作
area reset, code, readonly code32entry; 赋值操作示例mov r0, #4 ; 将立即数 4 装入寄存器 r0 (r0 = 4)mov r1, r0 ; 将寄存器 r0 的值复制到 r1 (r1 = r0 = 4)mov r2, r1, lsl #1 ; 将 r1 的值逻辑左移 1 位后存入 r2 (r2 = r1 << 1 = 4 << 1 = 8)mov r3, r1, lsr #1 ; 将 r1 的值逻辑右移 1 位后存入 r3 (r3 = r1 >> 1 = 4 >> 1 = 2)ror r4, r3, #5 ; 将 r3 的值循环右移 5 位,结果存入 r4end
看看运行结果
注意:右移会将移出去的数字清除,左侧补0,而循环右移会将移出去的数字补充到左侧。
2.3add加法操作
area reset, code, readonly code32entry; ADD 指令应用示例 mov r0, #100 ; r0 = 100mov r2, #100 ; r2 = 100 ; 立即数加法add r1, r0, #200 ; r1 = r0 + 200 = 100 + 200 = 300; 寄存器加法 add r3, r2, r0 ; r3 = r2 + r0 = 100 + 100 = 200; 带移位的加法(您的注释有误)add r4, r2, r0, lsl #1 ; r4 = r2 + (r0 << 1) = 100 + (100 × 2) = 100 + 200 = 300; 注意:lsl #1 是左移1位(乘以2),不是右移; 立即数表达式加法add r4, r2, #(100 >> 1) ; r4 = r2 + (100 >> 1) = 100 + 50 = 150; 汇编器会在编译时计算 100 >> 1 = 50; 带寄存器控制移位的加法(存在潜在问题)add r5, r2, r0, lsl r1 ; r5 = r2 + (r0 << r1) = 100 + (100 << 300); 注意:r1=300,左移300位没有意义,可能导致意外结果end
看看运行结果
2.4sub减法操作
area reset, code, readonly code32entry; SUB 指令应用示例mov r0, #100 ; r0 = 100mov r2, #100 ; r2 = 100; 立即数减法sub r1, r0, #50 ; r1 = 100 - 50 = 50; 寄存器减法sub r3, r2, r0 ; r3 = 100 - 100 = 0; 带左移的减法(乘以2)sub r4, r2, r0, lsl #1 ; r4 = 100 - (100 × 2) = -100; 带右移的减法(除以2) sub r4, r2, r0, lsr #1 ; r4 = 100 - (100 ÷ 2) = 50; 合理的移位寄存器操作mov r6, #2 ; 设置合理的移位量sub r5, r2, r0, lsl r6 ; r5 = 100 - (100 × 4) = -300; 使用反向减法指令(当需要交换操作数顺序时)rsb r7, r0, r2 ; r7 = r2 - r0 = 100 - 100 = 0rsb r8, r0, #200 ; r8 = 200 - r0 = 200 - 100 = 100end
看看运行结果
2.5ldr伪指令,加载任意32位数
加载任意32位数:
area reset, code, readonly code32entry; LDR 指令应用示例mov r1, #10 ; r1 = 10mov r0, #0xffffffff ; 错误!ARM的mov 不能加载32位立即数mvn r0, #0xffffffff ; 正确!使用mvn取反加载,r0 = 0x00000000ldr r0, =0xfac0 ; 正确!使用ldr伪指令加载32位立即数end
看看运行结果
2.6bic指定位清零
area reset, code, readonly code32entry; BIC(位清除)指令应用示例mov r0, #0xffffffff ; r0 = 0xFFFFFFFF (所有位都为1)mov r1, #1 ; r1 = 1 (二进制: 0001); 方法1:使用立即数直接清除特定位bic r0, r0, #4 ; 清除第2位(从0开始计数); #4 = 二进制 0100 (第2位为1); r0 = r0 AND NOT(0100) = 0xFFFFFFFB; 方法2:使用寄存器移位清除特定位bic r0, r0, r1, lsl #2 ; r1 = 1 (0001), 左移2位 = 0100 (4); 清除第2位,结果同上; 方法3:使用表达式清除特定位bic r0, r0, #(1 << 2) ; 1左移2位 = 0100 (4); 清除第2位,结果同上end
运行结果
2.7orr指定位置1
area reset, code, readonly code32entry; ORR(按位或)指令应用示例 - 指定位置1mov r0, #0x0 ; r0 = 0x00000000 (所有位都为0); 使用ORR指令将第10位置1(位编号从0开始)orr r0, r0, #(1 << 10) ; r0 = r0 OR 0x00000400 = 0x00000400; 1 << 10 = 21? = 1024 = 0x400; 第10位(二进制第11位)被设置为1end
运行结果
2.8subs带标志位的减法
area reset, code, readonly code32entryend
运行结果
2.9比较大小
比较两位数大小:
area reset, code, readonly code32entry;比较两数大小;mov r0, #0x100 ; R0 = 0x100 (十进制256);mov r1, #0x2 ; R1 = 0x2 (十进制2);cmp r0, r1 ; 比较 R0 和 R1 的值;movge r2, r0 ; 如果 R0 >= R1 (Greater than or Equal),则 R2 = R0;movlt r2, r1 ; 如果 R0 < R1 (Less Than),则 R2 = R1end
运行结果
比较三位数大小:
area reset, code, readonly code32entry;比较三个数大小mov r0, #0xff ;R0 = 255mov r1, #0x100 ;R1 = 256mov r2, #0xfe ;R2 = 254cmp r0, r1 ;比较r0和r1movge r3, r0 ;如果 R0 >= R1,则 R2 = R0movlt r3, r1 ;如果 R0 < R1 (Less Than),则 R2 = R1cmp r3, r2 ;比较r3和r2movge r4, r3 ;如果 R3 >= R2,则 R4 = R3movlt r4, r2 ;如果 R3 < R2 (Less Than),则 R4 = R2end
运行结果
2.10用分支结构比较大小
area reset, code, readonly code32entry;分支结构判断两个数大小mov r0, #0x100mov r1, #0x2cmp r0, r1 ;比较r0,r1bge greatr ;r0>r1跳转到greatrblt less ;r0<r1跳转到less
greatrmov r2, r0b finish
lessmov r2, r1b finish
finishb finish ;无限循环end
运行结果
2.11循环结构求1~99的和
while
area reset, code, readonly code32entry;循环
;1到99的和 (while)mov r0, #0x0 ; R0 = 0 (用于存储和)mov r1, #0x1 ; R1 = 1 (计数器,从1开始)mov r2, #0x64 ; R2 = 100 (循环终止条件)
loop ; 循环开始cmp r1, r2 ; 比较计数器和100bge finish ; 如果计数器 >= 100,跳转到结束add r0, r0, r1 ; 累加:和 = 和 + 计数器add r1, r1, #1 ; 计数器加1b loop ; 继续循环
finishb finish ; 无限循环end
运行结果
结果正确
do while
area reset, code, readonly code32entry;循环
;1到99的和 (do while)mov r0, #0x0mov r1, #0x1mov r2, #0x64
loopadd r0, r0, r1 add r1, r1, #1 cmp r1, r2 blt loop
finishb finishend
运行结果与上述一致
2.12函数封装并调用
area reset, code, readonly code32entry;函数封装并调用b main ;跳转到主程序,避免执行函数代码
asm_maxTwoNummov r0, #100mov r1, #200cmp r0, r1movge r3, r0movlt r3, r1mov pc, lr
mainmov r0, #10mov r1, #20bl asm_maxTwoNum ;调用函数:bl会跳转到函数并将返回地址保存到lr寄存器mov r2, #10mov r3, #20
finishb finishend
通过b跳转可以实现函数的封装和调用。
2.13STMDB(STMFD)解决函数调用嵌套,lr保存地址不够用
在多级函数调用时,lr会被覆盖,因此我们需要用stmfd将lr的内容存入栈中,在执行完函数后用ldmfd弹出,这样就能保证函数准确的返回。
操作流程:先递减栈指针,然后存储寄存器到内存,更新栈指针
注意,在每次函数调用时,都要加上压栈和弹栈操作!
area reset, code, readonly code32entry;函数封装并调用b main ;跳转到主程序,避免执行函数代码
asm_fun0mov r0, #100mov r1, #200bx lr
asm_maxTwoNummov r0, #100mov r1, #200cmp r0, r1stmfd sp!, {r0-r12,lr};带!栈帧移动,不带不移动,将r0-r12,lr压栈bl asm_fun0 ;调用函数:bl会跳转到函数并将返回地址保存到lr寄存器ldmfd sp!, {r0-r12,lr};与压栈列表对应,否则会产生错位,将r0-r12,lr弹出 movge r3, r0movlt r3, r1bx lr ;返回lr寄存器地址
mainldr sp, =0x40001000mov r0, #10mov r1, #20stmfd sp!, {r0-r12,lr};带!栈帧移动,不带不移动,将r0-r12,lr压栈bl asm_maxTwoNum ;调用函数:bl会跳转到函数并将返回地址保存到lr寄存器ldmfd sp!, {r0-r12,lr};与压栈列表对应,否则会产生错位,将r0-r12,lr弹出 mov r2, #10mov r3, #20
finishb finishend
我们可以看到,程序可以进行准确的跳转
3.概念补充
3.1ARM内核工作模式以及切换
7种模式:
1. 异常触发模式切换
; 1. 复位(Reset) - 进入 Supervisor 模式
; 处理器上电或复位时自动进入
; 这是程序的起点; 2. 未定义指令异常 - 进入 Undefined 模式
udiv r0, r1, r2 ; 如果处理器不支持UDIV指令; 自动切换到Undefined模式; 3. 软件中断(SWI)或SVC - 进入 Supervisor 模式
svc 0x1234 ; 主动触发管理模式切换; 用于系统调用; 4. 预取指中止 - 进入 Abort 模式
; 当处理器尝试执行无效地址的指令时; 5. 数据中止 - 进入 Abort 模式
ldr r0, [r1] ; 如果r1指向无效内存地址; 自动切换到Abort模式; 6. IRQ中断 - 进入 IRQ 模式
; 外部设备触发普通中断时; 7. FIQ中断 - 进入 FIQ 模式
; 高速外设触发快速中断时
2. 手动模式切换
; 通过修改CPSR(当前程序状态寄存器)的模式位
mrs r0, cpsr ; 读取CPSR到r0
bic r0, r0, #0x1F ; 清除模式位(低5位)
orr r0, r0, #0x13 ; 设置为Supervisor模式(0b10011)
msr cpsr_c, r0 ; 写回CPSR,切换模式; 注意:在用户模式下不能直接修改CPSR,会触发异常
3.各种模式的具体切换场景
1. User → Supervisor
系统调用:应用程序通过SVC指令请求操作系统服务
复位:处理器启动时自动进入
软件调试:调试器需要更高权限时2. User → IRQ/FIQ
硬件中断:外设(键盘、定时器、网络等)产生中断
实时事件:需要立即处理的紧急事件3. User → Abort
内存访问错误:访问非法内存地址或权限不足
页错误:虚拟内存管理中页面未加载或只读写入4. User → Undefined
执行未定义指令:处理器遇到不认识的指令
协处理器访问:访问不存在的协处理器5. 任何模式 → 任何模式
异常嵌套:在处理一个异常时又发生另一个异常
模式切换指令:操作系统有意识地切换模式
3.2立即数判断
立即数 是指在指令中直接编码的常数数值,而不是来自寄存器或内存的值。
在 ARM 架构中,由于指令长度固定为 32 位,不能将完整的 32 位立即数直接编码在指令中。因此,ARM 使用了一种巧妙的编码方案。
ARM 中的 12 位立即数实际上由两部分组成:
8 位 常数(0-255)
4 位 旋转值(0-15)
编码格式:
[11:8] - 旋转值 (rotate)
[7:0] - 8位常数 (imm8)
实际数值计算:
真正的 32 位立即数 = imm8
循环右移 (2 × rotate
) 位
mov r0, #100 ; #100 就是立即数
add r1, r2, #0xFF ; #0xFF 就是立即数
cmp r3, #42 ; #42 就是立即数
一个 32 位数值是合法立即数的条件是:它可以通过 8 位常数循环右移偶数位得到。
判断方法:
将数值表示为 32 位二进制检查是否能被分解为 8 位有效字段这个 8 位字段必须能通过循环右移偶数位得到
; 合法立即数示例:
mov r0, #0xFF ; 合法:0xFF = 0xFF ROR 0
mov r0, #0xFF00 ; 合法:0xFF00 = 0xFF ROR 24 (2×12)
mov r0, #0x3FC0 ; 合法:0x3FC0 = 0xFF ROR 2 (2×1); 非法立即数示例:
mov r0, #0x12345 ; 非法:无法用8位常数通过循环右移得到
mov r0, #0x101 ; 非法:0x101 = 0000 0001 0000 0001; 无法找到8位的连续有效位
3.3b,bl,bx指令
1. b 指令(Branch - 无条件跳转)
b label ; 跳转到标签处
b #0x1000 ; 跳转到绝对地址 0x1000
特点:
最简单的跳转指令,不保存返回地址,用于循环、条件分支、无限跳转。
2. bl 指令(Branch with Link - 带链接的分支)
main:mov r0, #10mov r1, #20bl add_numbers ; 调用函数,lr = main+4的地址b end_programadd_numbers:add r2, r0, r1 ; r2 = r0 + r1mov pc, lr ; 返回到调用处(使用保存的lr)
特点:
跳转前将返回地址(下一条指令地址)保存到 lr 寄存器,用于函数调用,是最常用的函数调用指令。
3. bx 指令(Branch and eXchange - 分支并交换)
; 函数返回
bl some_function
; ... 其他代码 ...some_function:; 函数体bx lr ; 返回到调用处(lr保存的地址); 切换指令集
mov r0, #0x1001 ; 地址最低位为1,表示Thumb模式
bx r0 ; 跳转到0x1000,进入Thumb模式
特点:
跳转到寄存器指定的地址(不是立即数或标签),可以根据目标地址的最低位切换 ARM/Thumb 指令集,常用于函数返回和模式切换
3.4ARM内核采用的栈
ARM 内核采用的栈类型不是固定的,而是可配置的,但最常用的是满递减栈。
这是 ARM 架构的默认和最常用的栈类型。
特点:满:栈指针指向最后一个入栈的数据。
递减:栈向内存低地址方向增长。