菜鸟科技网

ARM汇编命令有哪些核心指令?

  1. ARM 汇编基础(语法、寄存器等)
  2. 核心数据处理指令(算术、逻辑、移位)
  3. 内存访问指令(加载与存储)
  4. 分支与跳转指令(程序流程控制)
  5. 其他重要指令
  6. 伪指令

ARM 汇编基础

语法风格

ARM 汇编主要有两种语法风格:UAL (Unified Assembly Language) 和传统的 ARM/Thumb 语法。UAL 是现代标准,强烈推荐使用,它的特点是:

ARM汇编命令有哪些核心指令?-图1
(图片来源网络,侵删)
  • 操作码(助记符)不区分大小写(如 ADD, add, Add 都可以)。
  • 寄存器名使用 R 前缀(如 R0)或 前缀(如 %r0),也可以省略前缀(如 0)。
  • 第二操作数前必须有 符号(立即数)或 ` `(寄存器)。

核心寄存器

ARM 处理器有 37 个 32 位通用寄存器,但在任何时刻,程序员可以访问其中 16 个,这 16 个寄存器根据处理器的工作模式(如用户模式、中断模式等)而有不同的别名,以便于记忆。

别名 编号 描述
r0 - r3 0 - 3 暂存寄存器:用于函数参数传递和临时存储。
r4 - r11 4 - 11 变量寄存器:用于保存函数内的局部变量。r7 在 Linux 系统调用中常被用作系统调用号。r11 (或 fp) 通常用作帧指针。
r12 12 内部调用暂存器:用作函数调用间的临时寄存器。
r13 13 堆栈指针:指向当前栈顶,别名 sp
r14 14 链接寄存器:保存函数返回地址,别名 lr
r15 15 程序计数器:指向下一条要执行的指令地址,别名 pc

核心数据处理指令

这类指令通常在寄存器之间操作,结果也存放在寄存器中。

算术指令

  • ADD (加法)
    ADD Rd, Rn, Operand2   ; Rd = Rn + Operand2
    ADD R0, R1, #10        ; R0 = R1 + 10
    ADD R2, R3, R4         ; R2 = R3 + R4
  • SUB (减法)
    SUB Rd, Rn, Operand2   ; Rd = Rn - Operand2
    SUB R0, R1, #5         ; R0 = R1 - 5
  • MUL (乘法)
    MUL Rd, Rn, Rm         ; Rd = Rn * Rm (结果只使用低32位)
    MUL R0, R1, R2         ; R0 = R1 * R2
  • UDIV (无符号除法, ARMv7-A/R 及以后)
    UDIV Rd, Rn, Rm        ; Rd = Rn / Rm (无符号)
    UDIV R0, R1, R2        ; R0 = R1 / R2

逻辑指令

  • AND (按位与)
    AND Rd, Rn, Operand2   ; Rd = Rn & Operand2
    AND R0, R1, #0xFF      ; R0 = R1 & 0xFF (屏蔽高24位)
  • ORR (按位或)
    ORR Rd, Rn, Operand2   ; Rd = Rn | Operand2
    ORR R0, R1, #0x100     ; R0 = R1 | 0x100 (设置第8位)
  • EOR (按位异或)
    EOR Rd, Rn, Operand2   ; Rd = Rn ^ Operand2
    EOR R0, R1, R1         ; R0 = R1 ^ R1 = 0 (清零寄存器)
  • BIC (位清除,即按位与反)
    BIC Rd, Rn, Operand2   ; Rd = Rn & (~Operand2)
    BIC R0, R1, #0x20      ; R0 = R1 & ~0x20 (清除第5位)

移位指令

移位是 ARM 的强大功能,可以嵌入到几乎所有数据处理指令的 Operand2 中。

  • LSL (逻辑左移)
  • LSR (逻辑右移)
  • ASR (算术右移,保持符号位)
  • ROR (循环右移)

示例:

ARM汇编命令有哪些核心指令?-图2
(图片来源网络,侵删)
; 将 R1 的值逻辑左移 2 位,结果存入 R0
ADD R0, R1, R1, LSL #2   ; R0 = R1 + (R1 << 2)  <=> R0 = R1 * 4
; 将 R2 的值算术右移 1 位,结果存入 R2 (相当于除以2)
ASR R2, R2, #1
; 移位也可以作为独立指令
LSL R3, R4, #5          ; R3 = R4 << 5

内存访问指令 (Load/Store)

ARM 是加载/存储架构,意味着所有内存操作都必须通过专门的指令来完成,不能直接在内存之间操作数据。

加载指令 (从内存到寄存器)

  • LDR (Load Register)
    LDR Rd, [Rn]           ; Rd = [Rn]  (读取Rn地址处的32位数据)
    LDR Rd, [Rn, #offset]  ; Rd = [Rn + offset]
    LDR Rd, [Rn, #offset]! ; Rd = [Rn + offset], 然后更新 Rn = Rn + offset (!)
    LDR Rd, [Rn], #offset  ; 先读取,再更新 Rn = Rn + offset
  • LDRB (Load Register Byte)
    LDRB R0, [R1]          ; R0 = [R1] (读取R1地址处的8位数据,零扩展到32位)

存储指令 (从寄存器到内存)

  • STR (Store Register)
    STR Rd, [Rn]           ; [Rn] = Rd
    STR Rd, [Rn, #offset]  ; [Rn + offset] = Rd
  • STRB (Store Register Byte)
    STRB R0, [R1]          ; [R1] = R0 (将R0的低8位存入R1地址)

示例:

; 假设 R1 指向一个结构体,R2 是偏移量
LDR R0, [R1, #4]         ; 将 R1+4 地址处的数据加载到 R0
STR R3, [R4, #-8]        ; 将 R3 的值存入 R4-8 的地址
; 使用堆栈
STR R5, [SP, #-4]!       ; 将 R5 压入栈中,并让 SP 减 4
LDR R6, [SP], #4        ; 从栈中弹出数据到 R6,并让 SP 加 4

分支与跳转指令 (程序流程控制)

  • B (Branch / 跳转)

    B label                ; 无条件跳转到 label
    BNE label              ; Z 标志位为0 (不相等/非零),则跳转
    BEQ label              ; Z 标志位为1 (相等/零),则跳转
    BGT label              ; 如果大于 (无符号),则跳转
    BLT label              ; 如果小于 (无符号),则跳转

    条件码是 ARM 的另一个强大特性,大部分指令都可以根据条件码执行或跳过。

    ARM汇编命令有哪些核心指令?-图3
    (图片来源网络,侵删)
  • BL (Branch with Link / 带链接的跳转)

    BL my_function         ; 跳转到 my_function,并将返回地址 (下一条指令地址) 保存到 LR (r14)

    这是函数调用的标准方式。

  • BX (Branch and Exchange / 跳转并交换)

    BX R0                  ; 跳转到 R0 中存放的地址

    常用于从 ARM 状态切换到 Thumb 状态(R0 的最低位为 1)。


其他重要指令

  • MOV (Move)
    MOV Rd, Operand2       ; Rd = Operand2 (将立即数或寄存器值移入 Rd)
    MOV R0, #0             ; R0 = 0
    MOV R1, R2             ; R1 = R2
  • MVN (Move Negative / 按位取反后移动)
    MVN R0, #0             ; R0 = ~0 = 0xFFFFFFFF
  • CMP (Compare)
    CMP Rn, Operand2       ; Rn - Operand2,并根据结果设置标志位,但不保存结果
    CMP R0, #10            ; 比较 R0 是否等于 10
  • PUSH / POP (压栈/出栈,是伪指令,但非常常用)
    PUSH {R0-R3, LR}       ; 将 R0, R1, R2, R3 和 LR 压入栈中
    POP {R4-R7, PC}        ; 从栈中弹出数据到 R4, R5, R6, R7,并跳转到弹出的地址

伪指令

伪指令不是真正的处理器指令,它们在汇编阶段被汇编器转换成一条或多条真实的指令。

  • LDR (用于加载地址/大立即数)
    LDR R0, =0x12345678    ; 伪指令,0x12345678 可以在指令中表示,则编码为立即数;否则,汇编器会将其放入一个常量池,并生成一条加载该常量的指令。
    LDR R0, =my_label      ; 伪指令,加载 my_label 的地址到 R0。
  • NOP (No Operation)
    NOP                    ; 什么都不做,通常用于延时或对齐指令。
  • ALIGN
    ALIGN 4                ; 确保下一条指令的地址是 4 字节对齐的。

综合示例:一个简单的函数

下面是一个将两个 32 位整数相加并返回结果的函数示例。

;-----------------------------------------------------------------------------
; 函数: int add(int a, int b);
; 描述: 将两个整数相加并返回结果。
; 输入: R0 = a, R1 = b (根据 AAPCS 调用约定)
; 输出: R0 = 结果
;-----------------------------------------------------------------------------
add_function:
    ADD R0, R0, R1      ; R0 = R0 + R1 (结果存入 R0)
    BX LR               ; 从 LR 中加载返回地址,并返回调用者
;-----------------------------------------------------------------------------
; 主程序
;-----------------------------------------------------------------------------
.global main
main:
    ; 准备参数
    MOV R0, #10         ; R0 = a = 10
    MOV R1, #20         ; R1 = b = 20
    ; 调用函数
    BL add_function     ; 跳转到 add_function,返回地址存入 LR
    ; R0 的值应该是 30
    ; ... 程序继续 ...
    ; 退出程序 (在嵌入式系统中可能不同)
    MOV R7, #1          ; 系统调用号 1 (exit) for Linux
    SWI 0               ; 触发软中断,进入内核态

希望这份详细的梳理能帮助你全面了解 ARM 汇编指令!如果你有具体的应用场景或想深入了解某个方面,可以随时提问。

分享:
扫描分享到社交APP
上一篇
下一篇