硬件(六)arm指令

一、数据传输与运算指令

(一)MOV 指令

用于加载 12 位立即数到寄存器,或转移一个寄存器的值到另一个寄存器。

  • 格式 1:MOV{S}<c> <Rd>, #<const>
    示例:mov r0, #2,将立即数 2 加载到寄存器 r0
  • 格式 2:MOV{S}<c> <Rd>, <Rm>
    示例:mov r1, r0,把 r0 寄存器的值加载到 r1
    大多数指令格式为 opcode rd, rn, rm,其中 rd 是目标寄存器,rn 是第一操作数寄存器。
(二)MVN 指令

对立即数按位取反后放入目标寄存器。

示例:mvn r3, #1,将 1 按位取反后存入 r3,结果为 0xFFFFFFFE。若 MOV 指令无法操作寄存器,会替换为 MVN 指令。

(三)ADD 指令

实现加法运算,常用两种格式:

  • ADD{S}<c> <Rd>, <Rn>, #<const>
  • ADD{S}<c> <Rd>, <Rn>, <Rm>{, <shift>}
(四)SUB 指令

进行减法运算,格式如下:

  • SUB{S}<c> <Rd>, <Rn>, #<const>
  • SUB{S}<c> <Rd>, <Rn>, <Rm>{, <shift>}
(五)立即数

立即数相当于汇编中的常量,前加 #。判断一个数是否为立即数有三个条件(以 12 位立即数为例):

1、如果某个数的数值范围是0~0xFF之间,那么这个数一定是立即数;

2、把某个数展开成2进制,这个数的最高位1至最低位1之间的二进制数序列的位数不能超过8位;

3、这个数的二进制序列凑够8位之后的的右边必须为偶数个连续的 0

(六)LDR 寄存器加载指令

用于从内存读取数据到寄存器,有多种格式:

  • LDR{<c>}{<q>} <Rt>, <label>
    示例:ldr r0, =0x2FAB4,多用于从 RAM 将 32 位字数据传送到目的寄存器。
  • LDR<c> <Rt>, [<Rn>{, #+/-<imm12>}]
    示例:LDR R0,[R1,#4],将内存地址为 R1 + 4 的字数据读入 R0#4 作为 12 位立即数可省略。
  • LDR<c> <Rt>, [<Rn>], #+/-<imm12>
    示例:ldr r0, [r1], #8,将内存地址 R1 的字数据读入 r0,之后 r1 + 8
  • LDR<c> <Rt>, [<Rn>, #+/-<imm12>]!
    示例:LDR R0,[R1,#8] !,将存储器地址为 R1 + 8 的字数据读入 R0,并将新地址 R1 + 8 写入 R1
(七)STR 指令

用于将寄存器数据存入内存地址。示例:str r1, [r0, #4],把 r1 的内容写入 r0 + 4 的地址。

(八)BIC 指令

实现指定位清零,格式:BIC{S}<c> <Rd>, <Rn>, #<const>,将 rnconst 为 1 的比特清零,结果放入 rd。示例:

eg:mov r0, 0xFFFFFFFF

bic r0, r0, #3;立即数哪些位为1就清掉

r0数据:0xFFFFFFFC

bic r0, r0, #(0x1F << 8);连续5位清0

操作后 r0 数据会相应清零指定比特位。

(九)ORR 指令

实现指定位置 1,格式:ORR{S}<c> <Rd>, <Rn>, #<const>。示例:

eg:mov r0, #0

orr r0, r0, #5;立即数带1的比特位 置1

orr r0, r0, #(1<< 9);第9位置1

可将 r0 中立即数带 1 的比特位置 1,如第 9 位。

(十){s} 后缀

s 后缀的指令会影响 CPSR 寄存器(当前程序状态寄存器)。CPSRNZCV 位含义如下:

  • N:结果为有符号二进制补码时,若为负数则 N=1,否则 N=0

  • Z:结果为 0 则 Z=1,否则 Z=0

  • C:无符号数最高有效位向更高位进位时 C=1;减法中最高有效位从更高位借位时 C=0

  • V:有符号数操作时,若两个最高有效位均为 0 的数相加结果最高有效位为 1,或两个最高有效位均为 1 的数相加结果最高有效位为 0,则 V=1,否则 V=0
    示例:

    mov r0, #0xFFFFFFFF
    adds r2, r0, #0

adds 影响 CPSRN 置 1(结果为负)。

(十一)<c> 执行条件

可自选增加执行条件,示例:movcs r0, #100,表示只有 C 位置位时,才把 100 加载入 r0,方便实现指令的有条件执行。

(十二)CMP 指令

用于比较两个寄存器的值或一个寄存器与立即数的值,原理是对两个数求差,看结果是否为 0,会无条件修改 NVCZ 位。

eg:从 r0r1 代表的两个有符号数中找较大值放入 r2 寄存器(ge表示将大于等于的放到r0<great>;lt表示小的<last>)

2、找出三个数的最大值

;三位数找最大

;cmp r0, r1

;blt less

;great

;mov r2, r0

;b cmp1

;less

;mov r3, r1

;cmp1

;cmp r3, r2

;bge finished

;mov r3, r2

二、跳转与循环指令

(一)跳转指令 b

类似 C 语言的 goto 语句,能实现无条件跳转,跳转时需指定 lable。本质是往 PC(程序计数器)里填跳转地址,b loop 相当于 ldr pc, =loop

练习:实现从 0 加到 100 的和,可通过 while 循环或 do while 循环

(二)bl 指令

用于函数调用,与 b 指令的区别是 bl 会在 lr(链接寄存器)中保存返回地址,以便函数调用完毕后回到调用处的下一行指令执行。

(三)bx 指令

示例:bx lr,将 lr 中的地址装入 pc,实现函数返回。

三、栈操作

(一)引入

函数调用时,r0r1 等寄存器值可能被修改,函数嵌套调用时 lr 值也会被修改,为解决此问题,需在函数调用前保护现场,调用完毕后恢复现场,这就需要栈结构。

(二)栈的操作方法

ARM 体系采用满减栈(先让栈指针自减,再写入数据)。操作前需指定栈底位置,如设置栈底指针寄存器:ldr sp, =0x40001000,使用 2440 内部从 0x40000000 开始的 4k RAM(地址范围 [0x40000000~0x40000FFF])。

(三)栈操作指令
  • 入栈保护指令 stmfdSTMDB :格式 STMFD<c> <Rn>{!}, <registers>Rn 为栈底指针寄存器,<registers> 为需入栈保护的寄存器,! 表示入栈后 sp 自动自减。

示例:stmfd sp!, {r0, r1, r2, r3-r12, lr}

  • 出栈恢复指令 ldmfdLDM/LDMIA :格式 LDMFD<c> <Rn>{!}, <registers>Rn 为栈底指针寄存器,<registers> 为需恢复的寄存器,! 表示出栈后 sp 自动自增。

示例:ldmfd sp!, {r0, r1, r2, r3-r12, lr}

练习:

四、汇编与 C 相互调用函数

(一)汇编调用 C 中函数
  1. import 声明函数,并用 bl 指令跳转到 C 函数。
  2. 调用者(主调方)负责保护和恢复现场。
  3. 传参规则:
    • 参数个数≤4 个,用 r0~r3 寄存器传参。
    • 参数个数>4 个,第 5 个及以后参数通过栈传参。
    • C 函数返回值通过 r0 寄存器返回

eg: c函数

arm函数

(二)C 调用汇编中的函数
  1. extern 声明 arm函数 eg: int _asm_min(int a,intb)

  2. 主函数调用 arm _asm_min(10,20) 函数

  3. Arm 函数 _asm_min 在函数之前需要声明 调出函数 export _asm_min

  4. 参数返回也是在R0寄存器中

arm函数

c函数

五、模式切换

(一)、CPS 指令

CPS 指令可以直接修改处理器模式,格式为 CPS #<mode>,比如想要切换到系统模式,可以写 CPS #0x1F(系统模式对应的编码)。不过需要注意,Keil 编译器不支持该指令,在实际使用 Keil 进行开发时,不能采用这种方式。

(二)、MRS / MSR 指令

通过 MRS(Move to Register from Special register)读取特殊寄存器的值到通用寄存器,再通过 MSR(Move to Special register from Register)将修改后的值写回特殊寄存器,从而实现模式切换。

示例代码(切换到 User 模式并设置栈指针):

复制代码
    ; 读取 CPSR 到 R0
    MRS R0, CPSR
    ; 清除 CPSR 的低 5 位(模式位)
    BIC R0, R0, #(0x1F)
    ; 设置为 User 模式(User 模式对应的编码是 0x10,即二进制 10000)
    ORR R0, R0, #0x10
    ; 将修改后的值写回 CPSR 的控制域(c 表示控制域)
    MSR CPSR_c, R0
    ; 设置栈指针,这里假设栈底为 0x40001000
    LDR SP, =0x40001000

这样就完成了从当前模式切换到 User 模式,并且为 User 模式设置了栈指针,以便在该模式下进行函数调用、局部变量存储等操作时能正确使用栈。

相关推荐
YA3335 小时前
java设计模式二、工厂
java·开发语言·设计模式
逐雨~5 小时前
9.8C++作业
开发语言·c++
我爱挣钱我也要早睡!6 小时前
Java 复习笔记
java·开发语言·笔记
Yang-Never7 小时前
Kotlin协程 -> Job.join() 完整流程图与核心源码分析
android·开发语言·kotlin·android studio
luckys.one9 小时前
第9篇:Freqtrade量化交易之config.json 基础入门与初始化
javascript·数据库·python·mysql·算法·json·区块链
TomCode先生9 小时前
c#动态树形表达式详解
开发语言·c#
高-老师10 小时前
基于R语言的物种气候生态位动态量化与分布特征模拟
开发语言·r语言·物种气候
大翻哥哥10 小时前
Python 2025:量化金融与智能交易的新纪元
开发语言·python·金融
~|Bernard|10 小时前
在 PyCharm 里怎么“点鼠标”完成指令同样的运行操作
算法·conda