ARM2.(汇编语言)

发布于:2025-09-14 ⋅ 阅读:(19) ⋅ 点赞:(0)

1.kill4界面介绍

1.1窗口恢复

若不小心关掉左侧项目列表,可以在window中恢复。

1.2Debug调试界面

编写好程序后可以进入Debug界面

左侧会显示寄存器状态

单步执行,会跟随函数逻辑跳转到对应行执行,比如函数调用会跳转到相应行数1执行。

单步执行,但是遇到函数调用会直接将调用的函数执行完毕拿到执行结果,跳转到函数调用的下一行代码执行。

2.汇编语言

2.1汇编语言框架

area reset, code, readonly		
	code32
	entry


	end

        area reset, code, readonly:这是一个汇编指令,用于定义一个名为“reset”的代码区域,该区域是只读的。通常用于嵌入式系统或启动代码中,表示复位向量或初始化代码。

        code32:指定接下来的代码是 32 位 ARM 指令(而不是 Thumb 指令)。

        entry:表示程序的入口点(类似于主函数或启动点)。

        end:代码结束。

2.2mov赋值操作

area reset, code, readonly		
	code32
	entry


    ; 赋值操作示例
	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 位,结果存入 r4


	end

看看运行结果

注意:右移会将移出去的数字清除,左侧补0,而循环右移会将移出去的数字补充到左侧。

2.3add加法操作

area reset, code, readonly		
	code32
	entry

; ADD 指令应用示例	
	mov r0, #100		    ; r0 = 100
	mov 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		
	code32
	entry

; SUB 指令应用示例
	mov r0, #100		    ; r0 = 100
	mov 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 = 0
	rsb r8, r0, #200	    ; r8 = 200 - r0 = 200 - 100 = 100

	end

看看运行结果

2.5ldr伪指令,加载任意32位数

加载任意32位数:

area reset, code, readonly		
	code32
	entry

; LDR 指令应用示例
	mov r1, #10			; r1 = 10
	mov r0, #0xffffffff	; 错误!ARM的mov 不能加载32位立即数
	mvn r0, #0xffffffff	; 正确!使用mvn取反加载,r0 = 0x00000000
	ldr r0, =0xfac0		; 正确!使用ldr伪指令加载32位立即数

	end

看看运行结果

2.6bic指定位清零

area reset, code, readonly		
	code32
	entry

; 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		
	code32
	entry

; ORR(按位或)指令应用示例 - 指定位置1
	mov	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位)被设置为1

	end

运行结果

2.8subs带标志位的减法

area reset, code, readonly		
	code32
	entry



	end

运行结果

2.9比较大小

比较两位数大小:

area reset, code, readonly		
	code32
	entry

;比较两数大小
	;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 = R1

	end

运行结果

比较三位数大小:

area reset, code, readonly		
	code32
	entry

;比较三个数大小
	mov r0, #0xff	  ;R0 =  255
	mov r1, #0x100	  ;R1 = 256
	mov r2, #0xfe	  ;R2 = 254
	cmp r0, r1		  ;比较r0和r1
	movge r3, r0	  ;如果 R0 >= R1,则 R2 = R0
	movlt r3, r1	  ;如果 R0 < R1 (Less Than),则 R2 = R1
	cmp r3, r2		  ;比较r3和r2
	movge r4, r3	  ;如果 R3 >= R2,则 R4 = R3
	movlt r4, r2	  ;如果 R3 < R2 (Less Than),则 R4 = R2

	end

运行结果

2.10用分支结构比较大小

area reset, code, readonly		
	code32
	entry

;分支结构判断两个数大小
	mov r0, #0x100
	mov r1, #0x2
	cmp r0, r1		;比较r0,r1
	bge greatr		;r0>r1跳转到greatr
	blt less		;r0<r1跳转到less
greatr
	mov r2, r0
	b finish
less
	mov r2, r1
	b finish
finish
	b finish		 ;无限循环

	end

运行结果

2.11循环结构求1~99的和

while

area reset, code, readonly		
	code32
	entry

;循环
;1到99的和 (while)
   mov r0, #0x0       ; R0 = 0 (用于存储和)
	mov r1, #0x1       ; R1 = 1 (计数器,从1开始)
	mov r2, #0x64      ; R2 = 100 (循环终止条件)
loop                 ; 循环开始
	cmp r1, r2        ; 比较计数器和100
	bge finish        ; 如果计数器 >= 100,跳转到结束
	add r0, r0, r1    ; 累加:和 = 和 + 计数器
	add r1, r1, #1    ; 计数器加1
	b loop            ; 继续循环
finish
	b finish          ; 无限循环

	end

运行结果

结果正确

do while

area reset, code, readonly		
	code32
	entry

;循环
;1到99的和 (do while)
 	mov r0, #0x0
	mov r1, #0x1
	mov r2, #0x64
loop
	add r0, r0, r1  
	add r1, r1, #1 
	cmp r1, r2    
	blt loop 
finish
	b finish

	end

运行结果与上述一致

2.12函数封装并调用

area reset, code, readonly		
	code32
	entry

;函数封装并调用
	b main			   ;跳转到主程序,避免执行函数代码
asm_maxTwoNum
	mov r0, #100
	mov r1, #200
	cmp r0, r1
	movge r3, r0
	movlt r3, r1
	mov pc, lr
main
	mov r0, #10
	mov r1, #20
	bl asm_maxTwoNum   ;调用函数:bl会跳转到函数并将返回地址保存到lr寄存器
 
	mov r2, #10
	mov r3, #20
finish
	b finish

	end

通过b跳转可以实现函数的封装和调用。

2.13STMDB(STMFD)解决函数调用嵌套,lr保存地址不够用

在多级函数调用时,lr会被覆盖,因此我们需要用stmfd将lr的内容存入栈中,在执行完函数后用ldmfd弹出,这样就能保证函数准确的返回。     

   操作流程:先递减栈指针,然后存储寄存器到内存,更新栈指针

   注意,在每次函数调用时,都要加上压栈和弹栈操作!

area reset, code, readonly		
	code32
	entry

;函数封装并调用
	b main				;跳转到主程序,避免执行函数代码
asm_fun0
	mov r0, #100
	mov r1, #200
	bx lr
asm_maxTwoNum
	mov r0, #100
	mov r1, #200
	cmp r0, r1
	stmfd sp!, {r0-r12,lr};带!栈帧移动,不带不移动,将r0-r12,lr压栈
	bl asm_fun0			 ;调用函数:bl会跳转到函数并将返回地址保存到lr寄存器
	ldmfd sp!, {r0-r12,lr};与压栈列表对应,否则会产生错位,将r0-r12,lr弹出 
	movge r3, r0
	movlt r3, r1
	bx lr				  ;返回lr寄存器地址
main
	ldr sp, =0x40001000
	mov r0, #10
	mov r1, #20
	stmfd sp!, {r0-r12,lr};带!栈帧移动,不带不移动,将r0-r12,lr压栈
	bl asm_maxTwoNum     ;调用函数:bl会跳转到函数并将返回地址保存到lr寄存器
	ldmfd sp!, {r0-r12,lr};与压栈列表对应,否则会产生错位,将r0-r12,lr弹出 
	mov r2, #10
	mov r3, #20
finish
	b finish

	end

我们可以看到,程序可以进行准确的跳转

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, #10
    mov r1, #20
    bl add_numbers    ; 调用函数,lr = main+4的地址
    b end_program

add_numbers:
    add r2, r0, r1    ; r2 = r0 + r1
    mov 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 架构的默认最常用的栈类型。

特点:满:栈指针指向最后一个入栈的数据。

          递减:栈向内存低地址方向增长。