arm汇编指令

学习arm汇编的主要目的是为了编写arm启动代码,启动代码启动以后,引导程序到c语言环境下运行。换句话说启动代码的目的是为了在处理器复位以后搭建c语言最基本的需求。因此启动代码的主要任务有:

1、初始化异常向量表;
2、初始化各工作模式的栈指针寄存器;
3、开启arm内核中断允许;
4、将工作模式设置为user模式;
5、完成上述工作后,引导程序进入c语言主函数执行;
因此汇编指令的学习主要是围绕这几个目的展开,主要学习跟上述目的相关的指令。

ARMv4:

当前应用最广泛的ARM指令集版本。
ARM7TDMI、ARM720T、ARM9TDMI、ARM940T、ARM920T、Intel的StrongARM等是基于ARMv4T版本。

立即数:

在计算机汇编语言中, 立即数 是直接包含在指令中的操作数,而不是通过寄存器或内存地址间接获取的数值。

准确的说这里所指的是12位立即数imm12。先说怎么判断某数是不是12位立即数,12位立即数的条件是:
1.如果某个数的数值范围是0~0xFF之间,那么这个数一定是立即数
2.把某个数展开成2进制,这个数的最高位1至最低位1之间的二进制数序列的位数不能超过8位
3.这个数的二进制序列凑够8位之后的的右边必须为偶数个连续的 0
例如:0x234 = 0000 0000 0000 0000 0000 0010 0011 0100
最高位1至最低位1之间的二进制数序列:1000 1101没有超过8位
末尾1的右边有2个0,所以0x234是立即数

0x3f4 = 0000 0000 0000 0000 0000 0011 1111 0100
最高位1至最低位1之间的二进制数序列:1111 1101 从第一个1开始到最后一个1之间没有超过8位
末尾1的右边有2个0,所以0x3f4是立即数

0x132 = 0000 0000 0000 0000 0000 0001 0011 0010
最高位1至最低位1之间的二进制数序列:1001 1001 从第一个1开始到最后一个1之间没有超过8位
末尾1的右边有1个0,不满足第二条,所以0x132不是立即数

0x7f8 = 0000 0000 0000 0000 0000 0111 1111 1000
最高位1至最低位1之间的二进制数序列:1111 1111从第一个1开始到最后一个1之间没有超过8位
末尾1的右边有3个0,不满足第二条,所以0x7f8不是立即数

0xfab4 = 0000 0000 0000 0000 1111 1010 1011 0100
最高位1至最低位1之间的二进制数序列:0011 1110 1010 1101 从第一个1开始到最后一个1之间超过8位,不满足条件1,所以这个数不是立即数
这是因为ARM中将这 12bits 分为 8bit 常数(0~255)和 4bit 循环右移位值(0~15)
8bit 常数范围(0~255),位移的步进值是以2为单位(即实际位移 2 * rotate 位),可以表示循环有以(0~30)偶数位: 0、2、4、6、8、10、12、14、16、18、20、22、24、26、28、30。在实际存储这个数值的时候,要想办法把这个数压缩到这12位中去。压缩的方法就是找一个数,这个数必须是一个8bit数,之后循环右移2 * rotate位。如果能找打这个数,那么待保存的数就是立即数,否则就不是。
那么如果我们就是希望把一个非立即数存进rn,又该怎么做呢?

ARM汇编中MOV指令的详细用法说明

MOV指令是ARM汇编中最基础且常用的指令之一,主要用于数据的传输操作。它的核心功能是将数据从一个位置移动到另一个位置,支持立即数传输和寄存器间数据传输。

1. 基本语法格式

MOV指令的完整语法格式为:

MOV{S}<c> <Rd>, <operand2>
  • S:可选后缀,用于影响程序状态寄存器CPSR中的条件标志位(如零标志Z、进位标志C等)
  • <c>:可选的条件码(如EQ、NE、GT等),用于条件执行
  • <Rd>:目标寄存器,接收传输的数据
  • <operand2>:源操作数,可以是立即数或寄存器
2. 具体用法及示例
(1)加载立即数到寄存器

当源操作数是立即数时,语法简化为:

MOV{S}<c> <Rd>, #<const>
  • 立即数必须是12位范围内的有效数值(0-4095)
  • 常用于初始化寄存器或设置常量值

示例:

MOV r0, #0          ; 将立即数0加载到r0寄存器
MOV r1, #100        ; 将立即数100加载到r1寄存器
MOVS r2, #255       ; 加载立即数255到r2,并更新标志位
MOVNE r3, #3        ; 当条件不相等(NE)时,将3加载到r3
(2)寄存器间数据传输

当源操作数是寄存器时,语法简化为:

MOV{S}<c> <Rd>, <Rm>
  • 将源寄存器<Rm>中的值复制到目标寄存器<Rd>
  • 源寄存器的值保持不变

示例:

MOV r4, r0          ; 将r0中的值复制到r4
MOV r5, r1          ; 将r1中的值复制到r5
MOVS r6, r2         ; 将r2的值复制到r6,并更新标志位
MOVGT r7, r3        ; 当条件大于(GT)时,将r3的值复制到r7
(3)特殊用法示例

结合条件码和标志位影响的综合示例:

; 比较r0和r1,如果相等则设置r2为0,否则设置为1
CMP r0, r1          ; 比较r0和r1,结果影响标志位
MOVEQ r2, #0        ; 若相等(EQ),r2 = 0
MOVNE r2, #1        ; 若不相等(NE),r2 = 1

; 数据传递并更新标志位
MOV r0, #10
MOVS r1, r0         ; 将r0的值传给r1,并设置Z标志(若结果为0)和N标志(若结果为负)

ARM汇编中ADD指令的详细用法说明

ADD指令是ARM汇编中用于执行加法运算的基础指令,支持立即数与寄存器相加、寄存器之间相加等操作,同时可以影响程序状态寄存器(CPSR)中的标志位。

1. 基本语法格式

ADD指令的完整语法格式为:

ADD{S}<c> <Rd>, <Rn>, <operand2>
  • S:可选后缀,用于更新CPSR中的条件标志位(进位C、溢出V、零Z、符号N等)
  • <c>:可选条件码(如EQ、NE、GT等),用于条件执行
  • <Rd>:目标寄存器,存储加法运算的结果
  • <Rn>:第一个操作数寄存器,提供加法运算的第一个值
  • <operand2>:第二个操作数,可以是立即数或寄存器(可带移位操作)
2. 具体用法及示例
(1)寄存器与立即数相加

语法格式:

ADD{S}<c> <Rd>, <Rn>, #<const>
  • 功能:将寄存器<Rn>的值与立即数<const>相加,结果存入<Rd>
  • 立即数需为12位范围内的有效数值(0-4095)

示例:

ADD r0, r1, #5      ; r0 = r1 + 5(不影响标志位)
ADDS r2, r3, #10    ; r2 = r3 + 10(结果影响标志位)
ADDGT r4, r5, #3    ; 当条件为"大于(GT)"时,执行r4 = r5 + 3

; 实际应用场景:计算数组元素地址
MOV r0, #0x1000     ; r0 = 起始地址0x1000
ADD r1, r0, #4      ; r1 = 0x1000 + 4(获取数组第二个元素地址,假设每个元素占4字节)
(2)寄存器与寄存器相加(支持移位操作)

语法格式:

ADD{S}<c> <Rd>, <Rn>, <Rm>{, <shift>}
  • 功能:将寄存器<Rn>的值与寄存器<Rm>的值(可经移位处理)相加,结果存入<Rd>
  • <shift>:可选移位操作,包括LSL(逻辑左移)、LSR(逻辑右移)、ASR(算术右移)、ROR(循环右移)

示例:

; 基本寄存器相加
ADD r0, r1, r2      ; r0 = r1 + r2(不影响标志位)
ADDS r3, r4, r5     ; r3 = r4 + r5(结果影响标志位)

; 带移位操作的相加
ADD r6, r7, r8, LSL #2  ; r6 = r7 + (r8 << 2)(r8左移2位后与r7相加)
ADD r9, r10, r11, LSR #1 ; r9 = r10 + (r11 >> 1)(r11右移1位后与r10相加)

; 结合条件码的应用
CMP r0, #10         ; 比较r0与10
ADDGE r1, r1, #1    ; 若r0 ≥ 10(GE条件),则r1 = r1 + 1
(3)标志位影响与应用

当使用ADDS(带S后缀)时,运算结果会更新CPSR中的标志位,常用于后续条件判断:

MOV r0, #255
MOV r1, #1
ADDS r2, r0, r1     ; r2 = 256,同时设置进位标志C=1(无符号溢出)

; 利用进位标志实现多字加法
; 计算64位加法:(r1:r0) + (r3:r2) = (r5:r4)
ADDS r4, r0, r2     ; 低32位相加,更新进位标志
ADC r5, r1, r3      ; 高32位相加,同时加上低32位产生的进位(ADC=ADD+Carry)
3. 注意事项
  • 立即数必须是12位有效范围(0-4095),超出范围需通过其他方式处理
  • 移位操作仅适用于寄存器作为第二个操作数的情况
  • S后缀是影响标志位的关键,无S时不改变任何标志位
  • 条件码需要与CPSR中的标志位配合使用,实现分支执行逻辑

ARM汇编中SUB指令的详细用法说明

SUB指令是ARM汇编中用于执行减法运算的基础指令,与ADD指令对应,支持寄存器减立即数、寄存器减寄存器(可带移位)等操作,同样可以影响程序状态寄存器(CPSR)中的标志位。

1. 基本语法格式

SUB指令的完整语法格式为:

SUB{S}<c> <Rd>, <Rn>, <operand2>
  • S:可选后缀,用于更新CPSR中的条件标志位(进位C、溢出V、零Z、符号N等)
  • <c>:可选条件码(如EQ、NE、GT等),用于条件执行
  • <Rd>:目标寄存器,存储减法运算的结果
  • <Rn>:被减数寄存器,提供减法运算的第一个值
  • <operand2>:减数,可表示为立即数或寄存器(可带移位操作)
2. 具体用法及示例
(1)寄存器减去立即数

语法格式:

SUB{S}<c> <Rd>, <Rn>, #<const>
  • 功能:将寄存器<Rn>的值减去立即数<const>,结果存入<Rd>
  • 立即数需为12位范围内的有效数值(0-4095,符合ARM立即数编码规则)

示例:

SUB r0, r1, #3      ; r0 = r1 - 3(不影响标志位)
SUBS r2, r3, #10    ; r2 = r3 - 10(结果影响标志位)
SUBLT r4, r5, #5    ; 当条件为"小于(LT)"时,执行r4 = r5 - 5

; 实际应用场景:调整指针位置
MOV r0, #0x2000     ; r0 = 内存地址0x2000
SUB r1, r0, #4      ; r1 = 0x2000 - 4(指向前一个32位数据地址)
(2)寄存器减去寄存器(支持移位操作)

语法格式:

SUB{S}<c> <Rd>, <Rn>, <Rm>{, <shift>}
  • 功能:将寄存器<Rn>的值减去寄存器<Rm>的值(可经移位处理),结果存入<Rd>
  • <shift>:可选移位操作,包括LSL(逻辑左移)、LSR(逻辑右移)、ASR(算术右移)、ROR(循环右移)

示例:

; 基本寄存器减法
SUB r0, r1, r2      ; r0 = r1 - r2(不影响标志位)
SUBS r3, r4, r5     ; r3 = r4 - r5(结果影响标志位)

; 带移位操作的减法
SUB r6, r7, r8, LSL #1  ; r6 = r7 - (r8 << 1)(r8左移1位后作为减数)
SUB r9, r10, r11, ASR #2 ; r9 = r10 - (r11 >> 2)(r11算术右移2位后作为减数)

; 结合条件码的应用
CMP r0, r1          ; 比较r0和r1(本质是计算r0 - r1并更新标志位)
SUBHI r2, r0, r1    ; 若r0 > r1(HI条件,无符号大于),则r2 = r0 - r1
(3)标志位影响与特殊应用

当使用SUBS(带S后缀)时,运算结果会更新CPSR中的标志位,尤其注意"进位标志C"在减法中的特殊含义(实际表示"借位"的反状态):

  • 若减法无借位(被减数 ≥ 减数),则C=1
  • 若减法有借位(被减数 < 减数),则C=0

示例:

MOV r0, #10
MOV r1, #5
SUBS r2, r0, r1     ; r2 = 5,无借位,C=1,Z=0(结果非零)

MOV r3, #3
SUBS r4, r1, r3     ; r4 = 2,无借位,C=1
SUBS r5, r3, r1     ; r5 = -2(补码表示),有借位,C=0

; 利用借位标志实现多字减法
; 计算64位减法:(r1:r0) - (r3:r2) = (r5:r4)
SUBS r4, r0, r2     ; 低32位相减,更新借位标志(C=0表示有借位)
SBC r5, r1, r3      ; 高32位相减,若低32位有借位则额外减1(SBC=SUB+Carry)

ARM汇编中LDR指令的详细用法说明

LDR(Load Register)指令是ARM汇编中用于从内存加载数据到寄存器的核心指令,支持多种寻址方式,能够灵活地访问内存中的数据。与MOV指令不同,LDR可以处理超出12位范围的立即数,也可以通过寄存器间接访问内存。

1. 基本语法格式

LDR指令的通用格式为:

LDR{<c>}{<q>} <Rt>, <addressing_mode>
  • <c>:可选条件码(如EQ、NE等),用于条件执行
  • <q>:可选,仅用于ARMv7及以上架构,表示加载64位数据(如LDRD)
  • <Rt>:目标寄存器,接收从内存加载的数据
  • <addressing_mode>:寻址模式,决定内存地址的计算方式
2. 具体用法及示例
(1)加载大立即数(伪指令形式)

语法格式:

LDR{<c>} <Rt>, =<const>
  • 功能:将32位大立即数(超出12位范围)加载到寄存器<Rt>
  • 本质是伪指令,汇编器会自动处理为从内存读取该常量

示例:

LDR r0, =0x12345678  ; 将32位立即数0x12345678加载到r0(超出12位范围)
LDR r1, =0x2FAB4     ; 加载0x2FAB4到r1(即使在12位范围内也可使用)
LDRNE r2, =0xFFFFFFFF ; 当条件不相等(NE)时,加载0xFFFFFFFF到r2

说明:对于12位范围内的立即数,LDR伪指令会被汇编器优化为MOV指令;超出范围时则从内存常量池加载。

(2)基址加偏移量寻址

语法格式:

LDR{<c>} <Rt>, [<Rn>{, #+/-<imm12>}]
  • 功能:计算内存地址为<Rn> ± imm12,将该地址的字数据加载到<Rt>
  • <Rn>为基址寄存器,<imm12>为12位偏移量(可选,默认0)

示例:

LDR r0, [r1]         ; 将r1指向的内存地址的数据加载到r0(偏移量0)
LDR r2, [r3, #4]     ; 将r3+4地址的数据加载到r2
LDR r4, [r5, #-8]    ; 将r5-8地址的数据加载到r4

; 实际应用:访问数组元素
MOV r1, #0x3000      ; r1 = 数组起始地址
LDR r0, [r1, #8]     ; 加载数组第3个元素(每个元素4字节:0x3000+8)
(3)后变址寻址

语法格式:

LDR{<c>} <Rt>, [<Rn>], #+/-<imm12>
  • 功能:先将<Rn>指向的内存数据加载到<Rt>,再更新<Rn><Rn> ± imm12
  • 地址更新发生在数据加载之后

示例:

LDR r0, [r1], #4     ; 先加载r1地址的数据到r0,再执行r1 = r1 + 4
LDR r2, [r3], #-2    ; 先加载r3地址的数据到r2,再执行r3 = r3 - 2

; 实际应用:遍历数组
MOV r1, #0x4000      ; r1 = 数组起始地址
LDR r0, [r1], #4     ; 加载第1个元素,r1指向第2个元素
LDR r0, [r1], #4     ; 加载第2个元素,r1指向第3个元素
(4)前变址寻址

语法格式:

LDR{<c>} <Rt>, [<Rn>, #+/-<imm12>]!
  • 功能:先更新<Rn><Rn> ± imm12,再将新地址的数据加载到<Rt>
  • 地址更新发生在数据加载之前(!表示更新基址寄存器)

示例:

LDR r0, [r1, #8]!    ; 先执行r1 = r1 + 8,再加载新r1地址的数据到r0
LDR r2, [r3, #-4]!   ; 先执行r3 = r3 - 4,再加载新r3地址的数据到r2

; 实际应用:指针调整后访问数据
MOV r1, #0x5000      ; r1 = 初始地址
LDR r0, [r1, #12]!   ; r1先变为0x500C,再加载该地址数据到r0

ARM汇编中BIC指令的详细用法说明

BIC(Bit Clear)指令是ARM汇编中用于对寄存器中的指定位进行清零操作的逻辑指令,通过与掩码进行位运算实现特定比特位的清除,常用于寄存器配置、标志位控制等场景。

1. 基本语法格式

BIC指令的完整语法格式为:

BIC{S}<c> <Rd>, <Rn>, <operand2>
  • S:可选后缀,用于更新程序状态寄存器CPSR中的条件标志位(主要影响零标志Z)
  • <c>:可选条件码(如EQ、NE、GT等),用于条件执行
  • <Rd>:目标寄存器,存储运算结果
  • <Rn>:源寄存器,提供需要被清零的数据
  • <operand2>:掩码操作数,可以是12位立即数或寄存器(可带移位),掩码中为1的位将被用于清零
2. 核心功能

BIC指令的运算逻辑为:
Rd = Rn & (~operand2)
即先对掩码操作数取反,再与源寄存器进行按位与运算,最终结果中:

  • 掩码operand2为1的位 → 结果中对应位被清零
  • 掩码operand2为0的位 → 结果中对应位保持源寄存器的原值
3. 具体用法及示例
(1)使用立即数作为掩码(最常用形式)

语法格式:

BIC{S}<c> <Rd>, <Rn>, #<const>
  • 功能:将<Rn>中与<const>掩码为1的对应位清零,结果存入<Rd>
  • 立即数<const>需符合12位立即数编码规则

示例:

; 基础位清零操作
MOV r0, #0xFF       ; r0 = 0b11111111(初始值)
BIC r1, r0, #0x0F   ; r1 = 0b11111111 & ~0b00001111 = 0b11110000(低4位清零)

; 多比特清零
MOV r2, #0x3F5      ; r2 = 0b001111110101(二进制)
BIC r3, r2, #0x300  ; 掩码0x300 = 0b001100000000 → 清零第8-9位
                    ; r3 = 0b001111110101 & ~0b001100000000 = 0b000011110101

; 带标志位更新
MOV r4, #0x0        ; r4 = 0
BICS r5, r4, #0x1   ; r5 = 0 & ~1 = 0,同时设置Z=1(结果为零)
(2)使用寄存器作为掩码(支持移位)

语法格式:

BIC{S}<c> <Rd>, <Rn>, <Rm>{, <shift>}
  • 功能:将<Rn>中与<Rm>(可经移位)掩码为1的对应位清零
  • 适用于掩码需要动态计算的场景

示例:

; 寄存器直接作为掩码
MOV r0, #0xAAAA     ; r0 = 0b1010101010101010
MOV r1, #0xAA00     ; r1 = 0b1010101000000000(掩码)
BIC r2, r0, r1      ; r2 = 0xAAAA & ~0xAA00 = 0x00AA(高8位清零)

; 带移位的寄存器掩码
MOV r3, #0x3        ; r3 = 0b11
MOV r4, #0xFFFF     ; r4 = 0b1111111111111111
BIC r5, r4, r3, LSL #4  ; 掩码为0b11 << 4 = 0b110000
                        ; r5 = 0xFFFF & ~0x30 = 0xFFCF(第4-5位清零)
(3)实际应用场景:寄存器配置

在嵌入式开发中,BIC常用于清除外设寄存器的特定功能位:

; 假设UART控制寄存器UART_CR位于地址0x10000000
; 需要清除第2位(停止位设置)和第5位(校验使能),保留其他位

LDR r0, =0x10000000 ; r0 = UART_CR地址
LDR r1, [r0]        ; 读取当前控制寄存器值到r1
BIC r1, r1, #0x24   ; 掩码0x24 = 0b100100 → 清除第2位和第5位
STR r1, [r0]        ; 将修改后的值写回寄存器

ARM汇编中ORR指令的详细用法说明

ORR(OR Register)指令是ARM汇编中用于对寄存器进行指定位设置(置1)的逻辑指令,通过按位或运算实现特定比特位的置1操作,常用于寄存器配置、标志位设置等场景。

1. 基本语法格式

ORR指令的完整语法格式为:

ORR{S}<c> <Rd>, <Rn>, <operand2>
  • S:可选后缀,用于更新程序状态寄存器CPSR中的条件标志位(主要影响零标志Z)
  • <c>:可选条件码(如EQ、NE、GT等),用于条件执行
  • <Rd>:目标寄存器,存储运算结果
  • <Rn>:第一个操作数寄存器,提供基础数据
  • <operand2>:第二个操作数,可以是12位立即数或寄存器(可带移位),其中为1的位将被用于置1操作
2. 核心功能

ORR指令的运算逻辑为:
Rd = Rn | operand2
即对源寄存器和操作数进行按位或运算,最终结果中:

  • 操作数operand2为1的位 → 结果中对应位被强制置1
  • 操作数operand2为0的位 → 结果中对应位保持源寄存器的原值
3. 具体用法及示例
(1)使用立即数作为操作数(最常用形式)

语法格式:

ORR{S}<c> <Rd>, <Rn>, #<const>
  • 功能:将<Rn>中与<const>操作数为1的对应位置1,结果存入<Rd>
  • 立即数<const>需符合12位立即数编码规则

示例:

; 基础位置1操作
MOV r0, #0x00       ; r0 = 0b00000000(初始值)
ORR r1, r0, #0x0F   ; r1 = 0b00000000 | 0b00001111 = 0b00001111(低4位置1)

; 多比特置1
MOV r2, #0x0F5      ; r2 = 0b000011110101(二进制)
ORR r3, r2, #0x300  ; 操作数0x300 = 0b001100000000 → 第8-9位置1
                    ; r3 = 0b000011110101 | 0b001100000000 = 0b001111110101

; 带标志位更新
MOV r4, #0x0        ; r4 = 0
ORRS r5, r4, #0x1   ; r5 = 0 | 1 = 1,同时设置Z=0(结果非零)
(2)使用寄存器作为操作数(支持移位)

语法格式:

ORR{S}<c> <Rd>, <Rn>, <Rm>{, <shift>}
  • 功能:将<Rn>中与<Rm>(可经移位)操作数为1的对应位置1
  • 适用于需要动态计算置位掩码的场景

示例:

; 寄存器直接作为操作数
MOV r0, #0x00AA     ; r0 = 0b0000000010101010
MOV r1, #0xAA00     ; r1 = 0b1010101000000000(置位掩码)
ORR r2, r0, r1      ; r2 = 0x00AA | 0xAA00 = 0xAAAA(高8位和低8位同时置1)

; 带移位的寄存器操作数
MOV r3, #0x3        ; r3 = 0b11
MOV r4, #0x00CF     ; r4 = 0b0000000011001111
ORR r5, r4, r3, LSL #4  ; 操作数为0b11 << 4 = 0b110000
                        ; r5 = 0x00CF | 0x30 = 0x00FF(第4-5位置1,最终全为1)
(3)实际应用场景:外设寄存器配置

在嵌入式开发中,ORR常用于设置外设寄存器的特定功能位:

; 假设GPIO控制寄存器GPIO_CR位于地址0x20000000
; 需要设置第0位(输出使能)和第2位(上拉使能),保留其他位

LDR r0, =0x20000000 ; r0 = GPIO_CR地址
LDR r1, [r0]        ; 读取当前控制寄存器值到r1
ORR r1, r1, #0x5    ; 操作数0x5 = 0b101 → 设置第0位和第2位
STR r1, [r0]        ; 将修改后的值写回寄存器

汇编指令中S后缀的核心作用及标志位规则整理

一、S后缀的基本功能
  • 核心作用:指令后添加S后缀(如ADDSSUBSMOVS)时,执行后会自动更新程序状态寄存器(CPSR)中的4个关键标志位:N(符号位)、Z(零位)、C(进位/借位位)、V(溢出位)。
  • 无S后缀:仅执行指令功能(如运算、数据传输),不改变任何标志位。
二、CPSR标志位的判定规则
标志位含义判定规则
N符号位(有符号数)结果为负数(二进制补码最高位为1)→ N=1;非负数(最高位为0)→ N=0
Z零位结果为0 → Z=1;结果非0 → Z=0
C进位/借位位(无符号数)加法:最高位产生进位 → C=1;否则C=0
减法:被减数需借位(不够减)→ C=0;否则C=1
V溢出位(有符号数)两个正数(最高位0)相加得负数(最高位1)→ V=1
两个负数(最高位1)相加得正数(最高位0)→ V=1
其他情况→ V=0
三、实例分析
实例1:无符号数进位与零标志
MOV r0, #0xFFFFFFFF   ; r0 = 32位全1(无符号数4294967295)
ADDS r1, r0, #1       ; 计算:0xFFFFFFFF + 1 = 0x100000000(结果取低32位为0)
  • 标志位变化
    • Z=1(结果为0)
    • C=1(无符号数相加产生进位)
    • N=0(结果最高位为0,非负数)
    • V=0(无符号数运算,不涉及溢出)
实例2:有符号数溢出与符号位
MOV r0, #0x7FFFFFFF   ; r0 = 最大32位有符号正数(+2147483647,最高位0)
ADDS r1, r0, #1       ; 计算:0x7FFFFFFF + 1 = 0x80000000(有符号数-2147483648,最高位1)
  • 标志位变化
    • N=1(结果最高位为1,视为负数)
    • C=1(无符号数角度产生进位)
    • V=1(正数相加得负数,有符号数溢出)
    • Z=0(结果非0)
四、关键结论
  1. S后缀是标志位更新的开关:所有带S的指令会实时更新N、Z、C、V,为条件跳转(如BEQBLT)提供判断依据。
  2. 标志位分工明确
    • N、V用于有符号数运算(判断正负和溢出)
    • C用于无符号数运算(判断进位/借位)
    • Z用于通用零值判断
  3. 指令功能与标志位分离:即使运算逻辑相同(如ADDADDS),有无S后缀会导致标志位状态截然不同,需根据程序需求选择。

更新N,V,C,Z位有什么用呢?几乎所有的arm指令都可以在指令之后可选地增加执行条件

在这里插入图片描述

ARM汇编中CMP比较指令的功能与应用

在这里插入图片描述

一、CMP指令的核心原理

CMP(Compare)指令是用于比较两个数值大小的专用指令,其工作机制为:

  • 本质操作:对两个操作数执行减法运算(Rn - operand2),但不保存结果,仅通过运算过程更新CPSR寄存器的N、V、C、Z标志位
  • 特殊性:无需添加S后缀即可无条件修改标志位(与普通指令需S后缀才更新标志位不同)
二、基本语法格式
CMP <Rn>, <operand2>
  • <Rn>:第一个比较数(寄存器)
  • <operand2>:第二个比较数(可为立即数或寄存器,支持移位操作)
三、标志位变化规则

CMP指令通过减法结果影响标志位,具体对应关系如下:

比较结果(Rn - operand2)标志位状态含义解读
结果为0Z=1,N=0,C=1两数相等(Rn = operand2)
结果为正数(无符号)Z=0,C=1,N=0Rn > operand2(无符号)
结果为负数(无符号)Z=0,C=0,N=1Rn < operand2(无符号)
结果为正数(有符号)Z=0,N=0,V=0Rn > operand2(有符号)
结果为负数(有符号)Z=0,N=1,V=0Rn < operand2(有符号)
四、典型示例分析
示例1:两数相等的情况
MOV r0, #100         ; r0 = 100
CMP r0, #100         ; 执行100 - 100 = 0
  • 标志位变化:Z=1(结果为0),N=0,C=1
  • 结论:通过Z=1可判断两数相等,后续可使用BEQ(相等时跳转)指令
示例2:两数不等的情况
MOV r1, #200         ; r1 = 200
CMP r1, #150         ; 执行200 - 150 = 50(正数)
  • 标志位变化:Z=0(结果非0),N=0(结果为正),C=1(无借位)
  • 结论:通过Z=0且C=1可判断r1 > 150,后续可使用BHI(无符号大于时跳转)指令
示例3:有符号数比较
MOV r2, #-5          ; r2 = 0xFFFFFFFB(补码表示)
CMP r2, #3           ; 执行-5 - 3 = -8(负数)
  • 标志位变化:Z=0,N=1(结果为负),C=0(有借位)
  • 结论:通过N=1可判断r2 < 3(有符号),后续可使用BLT(有符号小于时跳转)指令
五、关键结论
  1. CMP是条件执行的基础:通过更新标志位,为后续条件跳转指令(如BEQ、BNE、BGT等)提供判断依据
  2. 零标志Z是相等判断的核心:Z=1必然表示两数相等,这是最直观的比较结果
  3. 无符号与有符号比较的区分:需结合C(无符号)和N/V(有符号)标志位综合判断大小关系
  4. 高效性:仅计算差值不保存结果,既完成比较又不占用额外寄存器资源

ARM汇编中跳转指令bbl以及bx的详细用法

一、b指令:无条件与条件跳转

b(Branch)指令是ARM汇编中最基础的跳转指令,用于实现程序流程的跳转,功能类似C语言的goto语句。

1. 基本语法
b{<cond>} <label>
  • <cond>:可选条件码(如EQ、NE、GT等),用于条件跳转;无条件跳转时省略
  • <label>:跳转目标标签,代表目标指令的地址
2. 用法示例
(1)无条件跳转(基础用法)
start:
    MOV r0, #0          ; 初始化r0为0
    b loop              ; 无条件跳转到loop标签处

loop:
    ADD r0, r0, #1      ; r0自增1
    ; ... 其他操作
(2)条件跳转(配合CMP实现循环)

以下示例实现从0加到100的和,展示循环机制:

    MOV r0, #0          ; r0 = 累加和(初始0)
    MOV r1, #0          ; r1 = 循环变量(初始0)
loop_start:
    CMP r1, #100        ; 比较r1与100
    BGT loop_end        ; 若r1 > 100,跳转到循环结束
    
    ADD r0, r0, r1      ; 累加:r0 = r0 + r1
    ADD r1, r1, #1      ; 循环变量自增
    B loop_start        ; 无条件跳回循环开始

loop_end:
    ; 循环结束,r0中存储0+1+...+100的结果
  • 关键逻辑:通过CMP设置标志位,BGT根据标志位决定是否退出循环,B实现循环体内部跳转
二、bl指令:带返回地址的跳转(函数调用)

bl(Branch with Link)指令用于实现函数调用,与b的核心区别是自动保存返回地址到lr(Link Register,r14)寄存器,确保函数执行完毕后能回到调用处的下一条指令。

1. 基本语法
bl{<cond>} <function_label>
  • 功能:跳转至function_label处执行(函数入口),同时将当前pc+4(下一条指令地址)存入lr
2. 用法示例(函数调用与返回)
    ; 主程序
    MOV r0, #5          ; 准备函数参数(r0=5)
    BL square           ; 调用square函数,lr自动保存返回地址
    ; 函数返回后继续执行此处(lr中保存的地址)
    B end               ; 跳至程序结束

; 函数:计算r0的平方,结果存r0
square:
    MUL r0, r0, r0      ; r0 = r0 * r0
    MOV pc, lr          ; 函数返回:将lr中的返回地址写入pc

end:
    ; 程序结束
  • 调用过程:BL square执行时,自动将下一条指令(B end)的地址存入lr
  • 返回过程:函数末尾通过MOV pc, lr,将lr中保存的地址加载到pc,实现从函数跳回主程序
三、bbl的核心区别
指令功能特点典型应用场景
b仅跳转,不保存返回地址循环、条件分支(类似goto
bl跳转时将返回地址存入lr函数调用(需返回调用处)
四、注意事项
  1. lr寄存器的保护:若函数内部嵌套调用其他函数,需先将当前lr值入栈保存,避免被覆盖

    func_a:
        PUSH {lr}         ; 保存当前lr到栈
        BL func_b         ; 调用子函数(会修改lr)
        POP {pc}          ; 恢复lr并返回
    
  2. 跳转范围bbl的跳转范围有限(通常±32MB),超出范围需使用间接跳转(如MOV pc, r0

  3. 标签本质:标签本质是指令地址的符号表示,汇编器会自动将其转换为具体地址填充到指令中

通过bbl的配合,ARM汇编可实现复杂的程序流程控制,包括循环、分支和函数调用等核心功能,与高级语言的控制结构形成对应。

ARM汇编中bx lr指令的功能与用法

bx lr是ARM汇编中实现函数返回的常用指令,主要用于从子函数跳回调用处,与函数调用指令bl配合完成完整的函数调用-返回流程。

一、指令功能解析
  • bx:Branch and Exchange的缩写,意为“跳转并切换指令集”。它既能实现程序跳转,还能根据目标地址的最低位(0或1)自动切换处理器的指令集状态(ARM状态或Thumb状态)。
  • lr:Link Register(链接寄存器,即r14),用于存储函数调用的返回地址(由bl指令自动设置)。

因此,bx lr的核心功能是:跳转到lr寄存器存储的地址,并根据该地址的最低位切换指令集,实现从子函数返回至调用处

二、与mov pc, lr的区别

在早期ARM汇编中,mov pc, lr也可用于函数返回,但bx lr是更通用的方式,两者的主要区别在于:

  • 指令集兼容性bx lr支持ARM与Thumb指令集的切换,而mov pc, lr仅能在同一指令集内跳转。
  • 现代汇编推荐:在支持Thumb指令集的ARM处理器中,bx lr是标准的返回指令,兼容性更好。
三、典型使用场景(函数返回)
; 主程序
main:
    mov r0, #10       ; 设置参数r0=10
    mov r1, #20       ; 设置参数r1=20
    bl add_func       ; 调用加法函数,lr自动保存返回地址(main的下一条指令)
    ; 函数返回后继续执行此处
    b exit

; 加法函数:计算r0 + r1,结果存r0
add_func:
    add r0, r0, r1    ; 执行加法运算
    bx lr             ; 函数返回:跳回lr存储的地址(main中bl的下一条指令)

exit:
    ; 程序结束
  • 执行流程:bl add_func调用函数时,lr被设置为b exit的地址;add_func执行完毕后,bx lr将程序跳转回b exit,继续主程序逻辑。
四、嵌套函数调用中的作用

在嵌套调用(如A调用B,B调用C)中,bx lr确保每层函数能正确返回至上一层:

funcA:
    stmfd sp!, {lr}   ; 保护funcA的返回地址
    bl funcB          ; 调用funcB,lr被更新为funcB返回后要执行的地址
    ldmfd sp!, {lr}   ; 恢复funcA的返回地址
    bx lr             ; 返回至调用funcA的地方

funcB:
    stmfd sp!, {lr}   ; 保护funcB的返回地址
    bl funcC          ; 调用funcC,lr被更新为funcC返回后要执行的地址
    ldmfd sp!, {lr}   ; 恢复funcB的返回地址
    bx lr             ; 返回至funcA

funcC:
    ; 函数逻辑
    bx lr             ; 返回至funcB
  • 每层函数通过栈保护lr,并最终通过bx lr返回,确保嵌套调用的正确执行。

ARM 汇编中利用栈保护现场与恢复现场(解决函数调用的寄存器污染和嵌套调用问题)

在 ARM 汇编中,当进行函数调用时,若不做特殊处理,会出现寄存器被函数修改以及嵌套调用时返回地址丢失的问题。而栈(Stack)作为一种“先进后出”的数据结构,能很好地解决这些问题——在函数调用前将需要保护的寄存器和返回地址压入栈(保护现场),函数执行完毕后再从栈中弹出(恢复现场)。

一、问题分析

  1. 寄存器污染:函数 max_of_two_numbers 中使用了 r0r1 寄存器,调用后这些寄存器的原始值会被修改,影响后续主程序对这些寄存器的使用。
  2. 嵌套调用返回地址丢失:若存在函数嵌套调用(如 funcA 调用 funcBfuncB 又调用 funcC),bl 指令每次都会覆盖 lr 寄存器的返回地址,导致无法回到最开始的调用处。

二、栈操作指令与保护/恢复逻辑

栈操作指令
  • 压栈(保护现场)stmfd sp!, {寄存器列表}
    • stmfd:以满递减(Full Descending)方式存储多个寄存器到栈。
    • sp!sp(栈指针)先自减,再存储数据(“!”表示操作后更新 sp)。
    • {寄存器列表}:要压入栈的寄存器,如 {r0 - r12, lr} 表示压入 r0r12 以及 lr
  • 弹栈(恢复现场)ldmfd sp!, {寄存器列表}
    • ldmfd:以满递减方式从栈中加载多个寄存器。
    • sp!:先从栈中读取数据,再将 sp 自增(恢复栈空间)。
保护与恢复逻辑
  1. 调用前保护:在调用函数前,将需要保留的寄存器(如 r0r1lr 等)压入栈。
  2. 函数内操作:函数内部使用寄存器进行计算,无需担心影响外部。
  3. 返回前恢复:函数返回前,从栈中弹出之前压入的寄存器,恢复其原始值;同时恢复 lr 以确保正确返回。

三、改进后的代码示例

start
    mov r0, #10       ; 主程序中r0的初始值
    mov r1, #15       ; 主程序中r1的初始值
    ; 保护现场:压入r0、r1、lr(调用函数前,保存这些寄存器的原始值)
    stmfd sp!, {r0, r1, lr}
    bl max_of_two_numbers  ; 调用函数,此时lr被修改为返回地址
    ; 恢复现场:弹出r0、r1、lr(恢复调用前的寄存器值)
    ldmfd sp!, {r0, r1, lr}
    add r3, r1, r0    ; 主程序后续使用r0、r1,此时它们的值已恢复
    ; 其他主程序逻辑...

max_of_two_numbers  ; 从r0, r1找较大值的函数
    cmp r0, r1
    movge r2, r0      ; 若r0 >= r1,r2 = r0
    movlt r2, r1      ; 若r0 < r1,r2 = r1
    mov r0, r2        ; 将较大值存入r0(函数返回值通过r0传递)
    bx lr             ; 返回调用处的下一条指令继续执行

四、嵌套调用场景示例(含多层栈保护)

假设有函数 funcA 调用 funcBfuncB 调用 funcC

funcA
    ; 保护funcA的现场(需保留的寄存器)
    stmfd sp!, {r0 - r3, lr}
    ; funcA的逻辑,可能修改r0 - r3
    bl funcB
    ; 恢复funcA的现场
    ldmfd sp!, {r0 - r3, lr}
    bx lr

funcB
    ; 保护funcB的现场
    stmfd sp!, {r4 - r6, lr}
    ; funcB的逻辑,可能修改r4 - r6
    bl funcC
    ; 恢复funcB的现场
    ldmfd sp!, {r4 - r6, lr}
    bx lr

funcC
    ; funcC的逻辑(无需嵌套调用,直接处理)
    bx lr
  • 每一层函数调用前,都压入当前函数需要保留的寄存器和 lr;返回前弹出,确保各层调用的寄存器和返回地址不冲突。

五、总结

通过**栈操作指令(stmfd/ldmfd)**对寄存器和返回地址(lr)进行“保护 - 恢复”,可以解决以下问题:

  1. 函数调用时寄存器被修改,影响主程序或上层调用函数。
  2. 函数嵌套调用时,lr 被多次覆盖导致无法正确返回初始调用处。
    栈的“先进后出”特性,完美适配了函数调用“先调用、后返回”的执行顺序,是 ARM 汇编中实现健壮函数调用的核心机制。

课上代码:arm汇编环境下的函数调用:

	area reset, readonly, code
	code32
	entry 
		
 ldr pc, =_start

_asm_max
    mov r0, #10
    mov r1, #20
    stmfd sp!, {r0 - r12, lr}
    bl _asm_min
    ldmfd sp!, {r0 - r12, lr}
    cmp r0, r1
    moveq r2, r0
    movlt r2, r0
    movgt r2, r1
    bx lr

_asm_min
    mov r0, #7
    mov r1, #9
    cmp r0, r1
    movle r2, r0
    movgt r2, r1
    bx lr

_start
    ldr sp, =0x40001000
	mov r0,#3
	mov r1,#4
    mov r0, #11
    mov r1, #22


finished
	b finished
	end

在汇编中调用C语言编写的函数

设有c语言定义的函数;在汇编代码中调用该函数,只需用import声明函数名即可,之后就可以使用bl指令调用该函数,注意,既然是调函数,就一定要保护现场

以下为简单调用:

.c文件函数:

int c_max(int a,int b)
{
	return a>b?a:b ;
}
int main(void)
{
	while(1)
	{
	}
	return 0;
}

.s文件代码

; 定义代码段:名为reset,只读属性,代码类型
area reset, readonly, code
    ; 确保堆栈以8字节对齐
    preserve8
    ; 指定使用32位ARM指令集
    code32
    ; 程序入口点声明
    entry 	
    
 ; 跳转到_start标签执行(设置程序计数器pc的值为_start的地址)
    ldr pc, =_start
  
   
; 程序主入口点
_start
    ldr sp, =0x40001000     ; 设置栈指针sp的初始值为0x40001000
    import c_max            ; 声明导入外部函数c_max(可能是C语言实现)
    mov r0,#3               ; r0=3(设置c_max函数的第一个参数)
    mov r1,#4               ; r1=4(设置c_max函数的第二个参数)
    ; 保护现场:将r0-r12和lr寄存器的值压入栈
    stmfd sp!, {r0 - r12, lr}
    bl c_max                ; 调用c_max函数(返回地址自动存入lr)
    ; 恢复现场:从栈中弹出r0-r12和lr寄存器的值
    ldmfd sp!, {r0 - r12, lr}
  
; 程序结束标签
finished
    b finished              ; 无限循环(程序终止点)
    end                     ; 汇编结束

C函数传参与汇编-C交互(含多参数处理)

一、C函数参数传递规则(汇编调用C)

ARM架构中,汇编调用C函数时,参数传递遵循以下约定:

参数个数范围传递方式返回值传递
≤4个通过r0~r3寄存器传递r0寄存器返回
>4个前4个用r0~r3,第5个及以后通过传递r0寄存器返回
二、参数≤4个的示例(直接用寄存器传参)

假设有C函数:

int add_c(int a, int b, int c, int d) {
    return a + b + c + d;
}

汇编调用代码:

    ; 准备4个参数(通过r0~r3)
    mov r0, #1   ; a = 1
    mov r1, #2   ; b = 2
    mov r2, #3   ; c = 3
    mov r3, #4   ; d = 4
    ; 调用C函数add_c
    bl add_c     
    ; 调用后,r0中存储返回值(1+2+3+4=10)
三、参数>4个的示例(寄存器+栈传参)

假设有C函数(5个参数):

int add_c_more(int a, int b, int c, int d, int e) {
    return a + b + c + d + e;
}

汇编调用代码(分步骤保护现场+传参):

    ; 步骤1:保护调用者现场(可选,若需保留r0~r12和lr)
    stmfd sp!, {r0 - r12, lr}  
    
    ; 步骤2:传递前4个参数(r0~r3)
    mov r0, #1   ; a = 1
    mov r1, #2   ; b = 2
    mov r2, #3   ; c = 3
    mov r3, #4   ; d = 4
    
    ; 步骤3:传递第5个参数(通过栈)
    mov r4, #5   ; 临时用r4存e=5
    push {r4}    ; 将e压入栈(栈向低地址生长,参数从右到左入栈)
    
    ; 步骤4:调用C函数add_c_more
    bl add_c_more
    
    ; 步骤5:清理栈(弹出第5个参数的空间)
    pop {r4}     ; 可选,若需回收栈空间
    
    ; 步骤6:恢复调用者现场
    ldmfd sp!, {r0 - r12, lr}  
    
    ; 调用后,r0中存储返回值(1+2+3+4+5=15)
四、C调用汇编函数(汇编导出+ C声明)

汇编侧(导出函数)

    ; 导出汇编函数供C调用
    export asm_max  
    
asm_max:
    ; 函数逻辑:比较r0和r1,返回较大值
    cmp r0, r1
    moveq r0, r0   ; 相等时返回r0
    movlt r0, r1   ; r0 < r1时返回r1
    movgt r0, r0   ; r0 > r1时返回r0
    bx lr          ; 返回C调用处

C侧(声明并调用)

// 声明外部汇编函数
extern int asm_max(int a, int b);  

int main() {
    int result = asm_max(10, 20);  // 调用汇编函数,result = 20
    return 0;
}

深化

makefile文件:
CC = arm-linux-gnueabihf-gcc
AS = arm-linux-gnueabihf-as
LD = arm-linux-gnueabihf-ld
OBJCOPY = arm-linux-gnueabihf-objcopy

all: main.elf

# 汇编编译
func.o: func.s
	$(AS) -o func.o func.s

# C编译
main.o: main.c
	$(CC) -c -o main.o main.c

# 链接
main.elf: main.o func.o
	$(CC) -o main.elf main.o func.o

# 清理
clean:
	rm -f *.o *.elf

# 运行(需在ARM环境或模拟器中)
run: main.elf
	qemu-arm ./main.elf

func.s文件:
AREA    asm_functions, CODE, READONLY
    EXPORT  asm_add
    EXPORT  asm_calc
    IMPORT  c_add
    IMPORT  c_calc
    PRESERVE8
    CODE32

; 函数:asm_add
; 功能:调用C函数c_add计算两个数的和
; 参数:r0 = a, r1 = b
; 返回值:r0 = a + b
asm_add
    ; 保护现场
    STMFD   sp!, {r4, r5, lr}
    
    ; 调用C函数c_add,参数已在r0和r1中
    BL      c_add
    
    ; 恢复现场
    LDMFD   sp!, {r4, r5, lr}
    BX      lr

; 函数:asm_calc
; 功能:调用C函数c_calc处理6个参数
; 参数:r0=a, r1=b, r2=c, r3=d, 栈传e和f
; 返回值:r0 = 计算结果
asm_calc
    ; 保护现场
    STMFD   sp!, {r4, r5, r6, lr}
    
    ; 第5和第6个参数通过栈传递(从右到左入栈)
    ; 注意:此时r0-r3已包含前4个参数(a,b,c,d)
    MOV     r4, r5          ; 暂存第6个参数f(原r5是第6个参数)
    STMFD   sp!, {r4}       ; 第6个参数f入栈
    STMFD   sp!, {r5}       ; 第5个参数e入栈
    
    ; 调用C函数c_calc
    BL      c_calc
    
    ; 清理栈上的参数(出栈2个参数,每个4字节)
    ADD     sp, sp, #8
    
    ; 恢复现场
    LDMFD   sp!, {r4, r5, r6, lr}
    BX      lr

    END

main.c
#include <stdio.h>

// 声明汇编函数
extern int asm_add(int a, int b);
extern int asm_calc(int a, int b, int c, int d, int e, int f);

// 提供给汇编调用的C函数
int c_add(int a, int b) {
    return a + b;
}

// 带多个参数的C函数
int c_calc(int a, int b, int c, int d, int e, int f) {
    return a + b - c * d + e / f;
}

int main() {
    // 调用汇编函数(2个参数)
    int result1 = asm_add(10, 20);
    printf("asm_add(10, 20) = %d\n", result1);
    
    // 调用汇编函数(6个参数)
    int result2 = asm_calc(1, 2, 3, 4, 20, 5);
    printf("asm_calc(1, 2, 3, 4, 20, 5) = %d\n", result2);
    
    return 0;
}

ARM内核工作模式切换:核心知识点与完整示例

一、核心知识点总结
  1. 工作模式由CPSR寄存器控制
    ARM内核的工作模式由程序状态寄存器(CPSR)的低5位(M[4:0])决定,不同值对应不同模式:

    • 0b10000:用户模式(usr,正常程序执行模式)
    • 0b10011:管理模式(svc,系统调用模式)
    • 0b10111:中断模式(irq,外部中断响应模式)
    • 0b11011:快中断模式(fiq,快速中断响应模式)
    • 0b11111:系统模式(sys,特权级操作系统任务)
  2. 模式切换指令

    • MRS <Rd>, CPSR:读取CPSR寄存器的值到通用寄存器(如MRS r0, CPSR
    • MSR CPSR_c, <Rm>:将通用寄存器的值写入CPSR的控制位(低8位,含M[4:0]),Keil环境专用语法
  3. 切换流程
    读取CPSR → 修改M[4:0]位 → 写回CPSR → 完成模式切换

二、完整代码示例(模式切换实现)
    AREA    mode_switch, CODE, READONLY
    PRESERVE8
    CODE32
    ENTRY

; 主程序入口:从用户模式切换到管理模式,再切换回用户模式
start
    ; 初始处于用户模式(usr)
    MRS r0, CPSR        ; 读取当前CPSR到r0
    BL print_mode       ; 打印初始模式

    ; 切换到管理模式(svc)
    MRS r0, CPSR        ; 读取CPSR
    BIC r0, r0, #0x1F   ; 清除低5位(M[4:0])
    ORR r0, r0, #0x13   ; 设置管理模式(0b10011)
    MSR CPSR_c, r0      ; 写回CPSR控制位,完成切换
    BL print_mode       ; 打印当前模式

    ; 切换到中断模式(irq)
    MRS r0, CPSR
    BIC r0, r0, #0x1F
    ORR r0, r0, #0x12   ; 设置中断模式(0b10110)
    MSR CPSR_c, r0
    BL print_mode

    ; 切换回用户模式(usr)
    MRS r0, CPSR
    BIC r0, r0, #0x1F
    ORR r0, r0, #0x10   ; 设置用户模式(0b10000)
    MSR CPSR_c, r0
    BL print_mode

    B   exit            ; 程序结束

; 打印当前工作模式(简化实现,实际需结合硬件输出)
print_mode
    MRS r1, CPSR        ; 读取CPSR
    AND r1, r1, #0x1F   ; 提取低5位(模式位)
    
    ; 以下为模式判断逻辑(示例)
    CMP r1, #0x10
    BEQ usr_msg
    CMP r1, #0x13
    BEQ svc_msg
    CMP r1, #0x12
    BEQ irq_msg
    B   unknown_msg

usr_msg
    ; 输出"User mode"(实际需硬件支持)
    B   mode_end
svc_msg
    ; 输出"Supervisor mode"
    B   mode_end
irq_msg
    ; 输出"IRQ mode"
    B   mode_end
unknown_msg
    ; 输出"Unknown mode"
mode_end
    BX  lr              ; 返回

exit
    B   exit            ; 无限循环

    END
三、代码解析
  1. 模式切换核心逻辑

    • MRS r0, CPSR读取当前状态寄存器
    • BIC r0, r0, #0x1F清除低5位(模式位)
    • ORR r0, r0, #模式值设置目标模式(如#0x13对应管理模式)
    • MSR CPSR_c, r0写回控制位,完成切换
  2. 关键注意事项

    • 用户模式(usr)下不能直接切换到特权模式,需通过异常(如软中断)触发
    • 切换模式时需注意保存/恢复寄存器(尤其是特权模式的私有寄存器)
    • 不同模式有不同的栈指针(如irq模式使用sp_irq),切换前需设置对应栈

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值