学习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后缀(如ADDS、SUBS、MOVS)时,执行后会自动更新程序状态寄存器(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)
四、关键结论
- S后缀是标志位更新的开关:所有带
S的指令会实时更新N、Z、C、V,为条件跳转(如BEQ、BLT)提供判断依据。 - 标志位分工明确:
- N、V用于有符号数运算(判断正负和溢出)
- C用于无符号数运算(判断进位/借位)
- Z用于通用零值判断
- 指令功能与标志位分离:即使运算逻辑相同(如
ADD与ADDS),有无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) | 标志位状态 | 含义解读 |
|---|---|---|
| 结果为0 | Z=1,N=0,C=1 | 两数相等(Rn = operand2) |
| 结果为正数(无符号) | Z=0,C=1,N=0 | Rn > operand2(无符号) |
| 结果为负数(无符号) | Z=0,C=0,N=1 | Rn < operand2(无符号) |
| 结果为正数(有符号) | Z=0,N=0,V=0 | Rn > operand2(有符号) |
| 结果为负数(有符号) | Z=0,N=1,V=0 | Rn < 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(有符号小于时跳转)指令
五、关键结论
- CMP是条件执行的基础:通过更新标志位,为后续条件跳转指令(如BEQ、BNE、BGT等)提供判断依据
- 零标志Z是相等判断的核心:Z=1必然表示两数相等,这是最直观的比较结果
- 无符号与有符号比较的区分:需结合C(无符号)和N/V(有符号)标志位综合判断大小关系
- 高效性:仅计算差值不保存结果,既完成比较又不占用额外寄存器资源
ARM汇编中跳转指令b与bl以及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,实现从函数跳回主程序
三、b与bl的核心区别
| 指令 | 功能特点 | 典型应用场景 |
|---|---|---|
b | 仅跳转,不保存返回地址 | 循环、条件分支(类似goto) |
bl | 跳转时将返回地址存入lr | 函数调用(需返回调用处) |
四、注意事项
-
lr寄存器的保护:若函数内部嵌套调用其他函数,需先将当前lr值入栈保存,避免被覆盖func_a: PUSH {lr} ; 保存当前lr到栈 BL func_b ; 调用子函数(会修改lr) POP {pc} ; 恢复lr并返回 -
跳转范围:
b和bl的跳转范围有限(通常±32MB),超出范围需使用间接跳转(如MOV pc, r0) -
标签本质:标签本质是指令地址的符号表示,汇编器会自动将其转换为具体地址填充到指令中
通过b和bl的配合,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)作为一种“先进后出”的数据结构,能很好地解决这些问题——在函数调用前将需要保护的寄存器和返回地址压入栈(保护现场),函数执行完毕后再从栈中弹出(恢复现场)。
一、问题分析
- 寄存器污染:函数
max_of_two_numbers中使用了r0、r1寄存器,调用后这些寄存器的原始值会被修改,影响后续主程序对这些寄存器的使用。 - 嵌套调用返回地址丢失:若存在函数嵌套调用(如
funcA调用funcB,funcB又调用funcC),bl指令每次都会覆盖lr寄存器的返回地址,导致无法回到最开始的调用处。
二、栈操作指令与保护/恢复逻辑
栈操作指令
- 压栈(保护现场):
stmfd sp!, {寄存器列表}stmfd:以满递减(Full Descending)方式存储多个寄存器到栈。sp!:sp(栈指针)先自减,再存储数据(“!”表示操作后更新sp)。{寄存器列表}:要压入栈的寄存器,如{r0 - r12, lr}表示压入r0到r12以及lr。
- 弹栈(恢复现场):
ldmfd sp!, {寄存器列表}ldmfd:以满递减方式从栈中加载多个寄存器。sp!:先从栈中读取数据,再将sp自增(恢复栈空间)。
保护与恢复逻辑
- 调用前保护:在调用函数前,将需要保留的寄存器(如
r0、r1、lr等)压入栈。 - 函数内操作:函数内部使用寄存器进行计算,无需担心影响外部。
- 返回前恢复:函数返回前,从栈中弹出之前压入的寄存器,恢复其原始值;同时恢复
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 调用 funcB,funcB 调用 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)进行“保护 - 恢复”,可以解决以下问题:
- 函数调用时寄存器被修改,影响主程序或上层调用函数。
- 函数嵌套调用时,
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内核工作模式切换:核心知识点与完整示例
一、核心知识点总结
-
工作模式由CPSR寄存器控制
ARM内核的工作模式由程序状态寄存器(CPSR)的低5位(M[4:0])决定,不同值对应不同模式:- 0b10000:用户模式(usr,正常程序执行模式)
- 0b10011:管理模式(svc,系统调用模式)
- 0b10111:中断模式(irq,外部中断响应模式)
- 0b11011:快中断模式(fiq,快速中断响应模式)
- 0b11111:系统模式(sys,特权级操作系统任务)
-
模式切换指令
MRS <Rd>, CPSR:读取CPSR寄存器的值到通用寄存器(如MRS r0, CPSR)MSR CPSR_c, <Rm>:将通用寄存器的值写入CPSR的控制位(低8位,含M[4:0]),Keil环境专用语法
-
切换流程
读取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
三、代码解析
-
模式切换核心逻辑
- 用
MRS r0, CPSR读取当前状态寄存器 - 用
BIC r0, r0, #0x1F清除低5位(模式位) - 用
ORR r0, r0, #模式值设置目标模式(如#0x13对应管理模式) - 用
MSR CPSR_c, r0写回控制位,完成切换
- 用
-
关键注意事项
- 用户模式(usr)下不能直接切换到特权模式,需通过异常(如软中断)触发
- 切换模式时需注意保存/恢复寄存器(尤其是特权模式的私有寄存器)
- 不同模式有不同的栈指针(如irq模式使用sp_irq),切换前需设置对应栈


6695

被折叠的 条评论
为什么被折叠?



