【ARMday02】

ARM的7种工作模式:

  • User:非特权模式(用户模式),大部分任务执行在这种模式
  • FIQ:(快速中断模式)当一个高优先级(fast)中断产生时将会进入这种模式
  • IRQ:(普通中断模式)当一个低优先级(normal)中断产生时将会进入这种模
  • Supervisor:当复位或软中断指令执行时将会进入这种模式
  • Abort:(终止模式)当存取异常时将会进入这种模式
  • Undef:(未定义模式)当执行未定义指令时会进入这种模式
  • System:使用和User模式相同寄存器集的特权模式

切换情况

  • 硬件触发:中断、异常(比如 IRQ/FIQ/Abort/Undefined)会自动切换模式;

  • 软件触发 :执行 SWI 指令进入 SVC;

  • 手动切换:通过修改 CPSR 模式位。

启动代码的主要任务:

  • 初始化异常向量表;
  • 初始化各工作模式的栈指针寄存器;
  • 开启arm内核中断允许;
  • 将工作模式设置为user模式;
  • 完成上述工作后,引导程序进入c语言主函数执行;

格式:

  • area: 这是最重要的一个伪操作,用于定义一个段。程序、数据、堆栈等都需要被组织在不同的段中。
  • reset: 这是你为这个段起的名字。名字 reset 具有很强的暗示性,通常用于表示复位向量段,即CPU上电或复位后首先执行的第一段代码所在的位置。
  • code: 指定该段的属性为代码,意味着这个段包含可执行的指令。
  • readonly: 指定该段的属性为只读。对于代码段来说,这通常是默认且必须的。
  • code32: 表示后续指令使用 32位的 ARM 指令集。
  • thumb: 表示后续指令使用 16位的 Thumb 指令集。

指令:

mov

MOV{S}<c> <Rd>, #<const>

MOV{S}<c> <Rd>, <Rm>

MOV instruction Canonical form

  • MOV{S} <Rd>, <Rm>, ASR #<n> ASR{S} <Rd>, <Rm>, #<n>
    • ASR算数右移n位
  • MOV{S} <Rd>, <Rm>, LSL #<n> LSL{S} <Rd>, <Rm>, #<n>
    • LSL逻辑左移n位
  • MOV{S} <Rd>, <Rm>, LSR #<n> LSR{S} <Rd>, <Rm>, #<n>
    • LSR逻辑右移n位
  • MOV{S} <Rd>, <Rm>, ROR #<n> ROR{S} <Rd>, <Rm>, #<n>
    • ROR循环右移n位

移位量来自寄存器Rs

  • MOV{S} <Rd>, <Rm>, ASR <Rs> ASR{S} <Rd>, <Rm>, <Rs>
  • MOV{S} <Rd>, <Rm>, LSL <Rs> LSL{S} <Rd>, <Rm>, <Rs>
  • MOV{S} <Rd>, <Rm>, LSR <Rs> LSR{S} <Rd>, <Rm>, <Rs>
  • MOV{S} <Rd>, <Rm>, ROR**<Rs>** ROR{S} <Rd>, <Rm>,<Rs>
  • MOV{S} <Rd>, <Rm>, RRX RRX{S} <Rd>, <Rm>

基本概念:

  • <Rd>:目的寄存器(左值,类似 C 里赋值号左边)
  • <Rm> / <Rs>:源寄存器(右值,来自谁)
  • #<const> / #<n>:立即数(写死在指令里的常数)
  • {S}:可选的 S 后缀,写成 MOVS。带 S 会更新标志位(APSR 里的 N/Z/C/V)。不带 S 不改标志位。
  • <c>:可选 条件码 (如 EQ/NE/GT/...),满足条件才执行
  • 移位量#<n>/<Rs> 取值范围 (0 - 31)

lsl:逻辑左移:低位补0,高位丢弃(相当于乘以2)

lsr:逻辑右移:高位补0,低位丢弃(相当于除以2)

ror:循环右移,移出的位从左边绕回

asr:算数右移:往右补符号位,常用于有符号数除以2

ADD(加法指令)

ADD{S}<c> <Rd>, <Rn>, <operand2>

  • <Rd>:目的寄存器(结果放哪)。

  • <Rn>:第一个操作数(通常是寄存器)。

  • <operand2>:第二操作数(可以是立即数或寄存器,寄存器可带移位)。

    • 立即数作为第二操作数:

      • ADD{S}<c> <Rd>, <Rn>, #<const>
    • 寄存器作为第二操作数寄存器:

      • ADD{S}<c> <Rd>, <Rn>, <Rm>{, <shift>}
    • 寄存器作为第二操作数移位量:

      • ADD{S}<c> <Rd>, <Rn>, <Rm>, <type> <Rs>
  • {S}:可选,写成 ADDS更新条件标志位(N/Z/C/V)。

  • <c>:可选条件码(如 EQ/NE/...),指令只有在满足条件时才执行。

ADDS 会更新 APSR 的 N/Z/C/V:

  • N(Negative):结果的最高位(bit31)。

  • Z(Zero):结果是否为 0(是则 Z=1)。

  • C(Carry):无符号加法时的进位(即 32-bit 加法是否有向 33 位进位产生)。

  • V(Overflow):有符号加法时溢出(例如正数+正数变成负数,或负数+负数变成正数)。

sub(减法指令)

  • 立即数作为第二操作数:
    • SUB{S}<c> <Rd>, <Rn>, #<const>
  • 寄存器作为第二操作数寄存器:
    • SUB{S}<c> <Rd>, <Rn>, <Rm>{, <shift>}
  • 寄存器作为第二操作数移位量:
    • SUB{S}<c> <Rd>, <Rn>, <Rm>, <type> <Rs>

SUBS,会更新 APSR 四个标志:

  • N(Negative)= 结果的最高位(bit31)。

  • Z(Zero)= 结果是否为 0(是则 Z=1)。

  • C (Carry)= 对减法来说,C = 1 当且仅当 没有借位 (也就是 Rn ≥ operand2 按无符号比较);如果产生借位,则 C = 0。

    • 举例:0x00000005 - 0x00000003 → 没有借位 → C=1

    • 0x00000003 - 0x00000005 → 需要借位 → C=0

  • V(Overflow)= 有符号减法溢出标志(例如正数减负数等导致符号变化的不合理情况)。

什么是立即数?如何判断某数是否合法为 12 位立即数?

在 ARM 数据处理指令中,第二操作数可以是寄存器,也可以是 立即数。立即数就是直接写在指令里的常量。

**12位立即数imm12:**把某个数转为2进制,该数必须存在一种循环右移(偶数位),使得移位后高24位全0,低8位即为有效imm8;任何能表示成"一个 8-bit 值向右循环旋转偶数位"的 32-bit 数都能编码成这个 imm12。

  • 0xFF → 可表示(imm8=0xFF, rotate=0)

  • 0x80000000 → 可表示(imm8=0x80, rotate=8)

  • 0xFFFFFFFF → 不能表示(需要 MVN r0, #0 或 LDR 伪指令)。

LDR(加载指令)

LDR<c> <Rt>, <label>,用来把内存内容装到寄存器

SDR(存放指令)

将寄存器内容写到内存,与LDR对称

MVN(按位取反移动指令)

  • MVN{S}<c> <Rd>, #<const>
  • MVN{S}<c> <Rd>, <Rm>{, <shift>}
  • MVN{S}<c> <Rd>, <Rm>, <type> <Rs>

bic(bit clear):指定位置清0

  • BIC{S}<c> <Rd>, <Rn>, #<const>
  • BIC{S}<c> <Rd>, <Rn>, <Rm>{, <shift>}
  • BIC{S}<c> <Rd>, <Rn>, <Rm>, <type> <Rs>

orr(or):指定位置1

  • ORR{S}<c> <Rd>, <Rn>, #<const>
  • ORR{S}<c> <Rd>, <Rn>, <Rm>{, <shift>}
  • ORR{S}<c> <Rd>, <Rn>, <Rm>, <type> <Rs>

条件判断标志NZCV

CPSR寄存器中条件判断标志位

N: 符号标志位:上条指令执行结果最高位bit31为1,则 N = 1, 当结果作为有符号解释时为负值;

Z: 零值标志位:上条指令执行结果为0(即bit0 - bit31 均为0),则 Z = 1;

C: 进位标志位:进行无符号解读,如果在加法过程中进位或者减法时没有借位,则为 C = 1,否则 C = 0

V: 溢出标志位:进行有符号解读,是否发生溢出 -2^31 - 2^31-1(两个正数加得负数,两个负数加得正数)

条件码:eq ge gt le lt al(无条件执行)

equal:等于

not equal:不等于

cmp(compare):比较指令

CMP<c> <Rn>, #<const>

CMP<c> <Rn>, <Rm>{, <shift>}

CMP<c> <Rn>, <Rm>, <type> <Rs>

b bl bx :(跳转指令)

B<c> <label>

b fun <==> ldr pc, =fun

B <label> :无条件跳转到 label,相当于 pc = label,不保存返回地址。

BL<c> <label>

bl fun
BL <label>调用时把返回地址写入链接寄存器 LR(R14),然后跳到目标。常用于函数调用

BX<c> <Rm>

bx lr <==> mov pc, lr

BX <Rm>BX Rm 从寄存器 Rm 读取目标地址并跳转,跳转到寄存器 Rm 的值,可以用于从函数返回(bx lr),也可以实现 ARM/Thumb 状态切换。

常见用法:

  • b fun→ 死循环跳转,不返回;

  • bl fun → 调用函数,返回地址存在 lr

  • bx lr→ 从函数返回。

ARM内核采用的栈是哪种栈?

使用的是满减栈,即栈向低地址方向增长,SP指向当前栈顶位置,每次压栈(push)会使SP减小,出栈(pop)会使SP增大。

"满":SP始终指向当前已经使用的栈顶元素,而不是下一个空闲位置

"递减"栈向低地址方向增长(高地址➡低地址)

在 ARM 的过程调用标准(AAPCS)中,还有一个约定:进入函数接口时,SP 必须保持 8 字节对齐,保证数据访问高效。

ARM 汇编调用 C 函数,或者 C 调用汇编函数时,参数和返回值如何处理?

ARM 遵循 AAPCS(ARM Procedure Call Standard) 约定:

  1. 参数传递规则

    • 前四个参数依次放在 r0、r1、r2、r3

    • 如果参数超过四个,多余的参数会从 右到左 依次压入栈中,由调用者负责分配。

    • 栈在函数入口时必须保持 8 字节对齐

  2. 返回值规则

    • 如果返回值是 32 位整型或指针,放在 r0

    • 如果是 64 位整型或 double,放在 r0:r1

    • 如果返回值是一个较大的结构体,通常由调用者传入一个隐藏指针,返回值写到该内存地址中。

  3. 寄存器保存约定

    • 调用者需要保存 易变寄存器(caller-saved)r0--r3, r12, lr

    • 被调用者需要保存 非易变寄存器(callee-saved)r4--r11

举个例子

如果 C 调用一个汇编函数 int add3(int a, int b, int c)

  • 编译器会把 a, b, c 放到 r0, r1, r2

  • 汇编里直接用 r0, r1, r2 做加法,结果放回 r0

  • 最后 bx lr 返回。

反过来,如果汇编里要调用一个 C 函数:

  • 把参数准备好放在 r0--r3 或栈中。

  • bl func 调用,返回值自动在 r0

1、什么是RISC、CISC;

RISC 和 CISC 是两种指令集设计理念:

  • RISC(精简指令集计算机):指令集简单,每条指令执行时间短,寻求"硬件简单 + 软件优化",代表是 ARM、MIPS。

  • CISC(复杂指令集计算机):指令集复杂,指令功能丰富,单条指令可以完成复杂操作,代表是 x86。

  • ARM 作为 RISC 架构,特点是指令长度固定(大多数是 32 位,Thumb 是 16 位),流水线容易优化,功耗低。

2、冯.诺伊曼架构和哈佛架构有何区别?ARM内核属于哪一种?

  • 冯·诺伊曼架构:指令和数据共享一条总线,存储空间统一。优点是硬件简单,缺点是容易产生"瓶颈"。

  • 哈佛架构:指令和数据分开存储、分开总线,可以同时取指和访存,效率更高。

ARM 内核一般采用 改进型哈佛架构:内部取指和数据访问分离(提高并行度),但在外部存储器接口上可以统一成一个总线,兼顾效率和灵活性。

3、ARM内核中都有什么?

ARM 内核主要包括:

  • 通用寄存器:R0--R12;

  • 堆栈指针 SP (R13)、链接寄存器 LR (R14)、程序计数器 PC(R15);

  • CPSR(当前程序状态寄存器),保存标志位和工作模式;

  • SPSR(保存程序状态寄存器),用于异常返回时恢复现场;

  • 流水线(典型 3 级:取指、译码、执行);

  • 异常向量表 和 模式切换机制。

可以简单概括为:寄存器文件 + 状态寄存器 + 流水线 + 异常机制

4、ARM有几种工作模式?

ARM 有 7 种主要工作模式:

  1. User(用户模式):普通应用程序运行;

  2. FIQ(快速中断模式)

  3. IRQ(普通中断模式)

  4. Supervisor(管理模式,操作系统用)

  5. Abort(异常模式,存储器访问错误)

  6. Undefined(未定义指令模式)

  7. System(系统模式,特权模式,运行内核任务)

其中 User 模式权限最低,其余为特权模式。

5、什么是异常向量表?

异常向量表是 ARM 处理器在发生异常或中断时,跳转执行的入口地址表

  • 它一般存放在地址 0x000000000xFFFF0000

  • 每种异常都有固定入口,比如:

    • Reset:0x00

    • Undefined:0x04

    • SWI:0x08

    • Prefetch Abort:0x0C

    • Data Abort:0x10

    • IRQ:0x18

    • FIQ:0x1C

这样 CPU 在异常发生时,就能根据异常类型跳转到对应入口地址去执行服务例程。

相关推荐
SundayBear8 小时前
零基础入门MQTT协议
c语言·单片机
嗯嗯=9 小时前
STM32单片机学习篇9
stm32·单片机·学习
松涛和鸣14 小时前
DAY63 IMX6ULL ADC Driver Development
linux·运维·arm开发·单片机·嵌入式硬件·ubuntu
想放学的刺客17 小时前
单片机嵌入式试题(第23期)嵌入式系统电源管理策略设计、嵌入式系统通信协议栈实现要点两个全新主题。
c语言·stm32·单片机·嵌入式硬件·物联网
猫猫的小茶馆17 小时前
【Linux 驱动开发】五. 设备树
linux·arm开发·驱动开发·stm32·嵌入式硬件·mcu·硬件工程
jghhh0119 小时前
基于上海钜泉科技HT7017单相计量芯片的参考例程实现
科技·单片机·嵌入式硬件
恶魔泡泡糖19 小时前
51单片机外部中断
c语言·单片机·嵌入式硬件·51单片机
意法半导体STM3219 小时前
【官方原创】如何基于DevelopPackage开启安全启动(MP15x) LAT6036
javascript·stm32·单片机·嵌入式硬件·mcu·安全·stm32开发
v_for_van19 小时前
STM32低频函数信号发生器(四通道纯软件生成)
驱动开发·vscode·stm32·单片机·嵌入式硬件·mcu·硬件工程
电化学仪器白超20 小时前
③YT讨论
开发语言·python·单片机·嵌入式硬件