当前位置: 首页 > news >正文

ARM汇编概述:Cortex-M3/M4实战指南

一、核心寄存器(汇编的"操作对象")

Cortex-M3/M4的寄存器是汇编指令的直接操作对象,无需记忆所有寄存器,仅需掌握以下高频核心寄存器,即可覆盖绝大多数嵌入式开发场景。

1. 通用寄存器

寄存器 别名 / 功能 核心用途

R0-R7 低寄存器 暂存运算数据、传递函数前4个参数、存储内存地址(配合LDR/STR访问RAM/ROM)

R13 SP(栈指针) 指向栈顶,管理函数调用时的上下文保存与恢复。Cortex-M默认采用满递减栈模型

R14 LR(链接寄存器) 保存函数调用后的返回地址(BL指令自动存入)。

R15 PC(程序计数器) 指向当前执行的指令地址,修改PC可实现跳转。Cortex-M始终处于Thumb状态,故PC的BIT0必须为1。

2. 程序状态寄存器

程序状态寄存器(xPSR)由APSR(应用状态寄存器)、IPSR(中断程序状态寄存器)和EPSR(执行程序状态寄存器)三个部分组合而成,其中APSR需重点关注,其4个标志位是"条件指令"的核心判断依据:

N(负标志):运算结果为负(最高位为1)时N=1,否则为0;用于判断有符号数的正负。

Z(零标志):运算结果为0时Z=1,否则为0;常用于循环结束判断(如计数到0)、相等比较(如CMP R0, R1后用BEQ跳转)。

C(进位/借位标志):加法有进位或减法无借位时C=1,否则为0;用于多字节运算(如64位数据加法)。

V(溢出标志):有符号数运算超出32位范围时V=1,否则为0;用于检测有符号数运算错误(如0x7FFFFFFF + 1会溢出)。

二、ARM常用汇编指令

Cortex-M3/M4架构遵循Load-Store原则,即数据处理指令只操作寄存器,与内存的数据交换必须通过LDR(从内存加载数据到寄存器)和STR(将寄存器数据存储到内存)指令完成。

1. 数据处理指令(CPU内部寄存器运算)

仅操作通用寄存器,不直接访问内存,是实现"加减、比较、位操作"等逻辑的核心。

(1)MOV:数据传送指令

核心作用:实现数据在寄存器间的传递,或立即数到寄存器的加载。

语法:MOV{S}{cond} Rd, Op2

{S}:可选,指令执行后更新APSR标志(如MOVS R0, #0会设置Z=1)。

{cond}:可选,条件执行后缀(如MOVNE R0, #0xFF表示"若Z=0(前序运算结果非0),则执行")。

Op2:可以是立即数或另一个寄存器,并可包含移位操作(如R3, LSL #2)。

示例:

MOV R0, #0x20000000 ; R0 = 0x20000000(加载RAM基地址)

MOVS R1, #0 ; R1 = 0,同时更新APSR的Z标志(Z=1)

MOV R2, R3, LSL #2 ; R2 = R3 << 2(将R3的值左移2位后存入R2)

(2)ADD/SUB:加减指令

核心作用:实现寄存器或立即数的加减运算。

语法:

ADD(加法):ADD{S}{cond} Rd, Rn, Op2(Rd = Rn + Op2)

SUB(减法):SUB{S}{cond} Rd, Rn, Op2(Rd = Rn - Op2)

示例:

; ADD示例

ADD R0, R1, #5 ; R0 = R1 + 5

ADDS R2, R3, R4 ; R2 = R3 + R4,同时更新APSR

; SUB示例

SUB R0, R1, #10 ; R0 = R1 - 10

SUBS R5, R5, #1 ; R5自减1(循环计数器),并更新标志位

(3)CMP:比较指令

核心作用:隐性计算"Rn - Op2",不保存结果,仅更新APSR标志,为后续"条件跳转"做准备。

语法:CMP{cond} Rn, Op2

示例:

CMP R0, #100 ; 比较R0与100

BEQ LoopEnd ; 若Z=1(R0=100),跳转到LoopEnd

BNE LoopContinue ; 若Z=0(R0≠100),跳转到LoopContinue

2. 内存访问指令

(1)LDR:读内存指令

核心作用:将内存中的数据读取到寄存器。

语法:LDR{type}{cond} Rd, [Rn {, #offset}]

{type}:可选,指定数据类型(B=无符号字节、H=无符号半字、默认=字)。

寻址方式:偏移寻址([Rn, #4]):地址 = Rn + 4,Rn不变。前索引([Rn, #4]!):地址 = Rn + 4,然后更新Rn = Rn + 4。后索引([Rn], #4):地址 = Rn,然后更新Rn = Rn + 4。

示例:

LDR R0, [R1] ; 读R1指向的4字节数据到R0

LDRB R2, [R1, #1] ; 读R1+1地址的1字节到R2

LDRH R3, [R1], #2 ; 读R1指向的2字节到R3,然后R1 = R1 + 2

(2)STR:写内存指令

核心作用:将寄存器中的数据写入内存。

语法:与LDR一致(Rd为源寄存器)。

示例:

STR R0, [R1] ; 将R0的4字节数据写入R1指向的地址

STRH R2, [R1, #4]! ; 将R2的2字节数据写入R1+4,然后R1 = R1 + 4

(3)PUSH/POP:栈操作指令

核心作用:批量保存/恢复寄存器到栈,是函数调用时保护上下文的标准且推荐的方式。它们是STMFD SP!和LDMFD SP!的别名,专用于栈操作,更简洁直观。

语法:

入栈(保存寄存器):PUSH {reglist}

出栈(恢复寄存器):POP {reglist}

示例:

; 函数入口:保存R4-R6(需保护的寄存器)和LR(返回地址)

PUSH {R4-R6, LR} ; 入栈,SP相应递减

; 函数体 ... (可安全使用R4-R6)

; 函数出口:恢复寄存器并返回

POP {R4-R6, PC} ; 出栈,恢复R4-R6,并将LR的值直接弹出到PC(实现返回)

3. 跳转与函数调用指令(程序流控制)

(1)B:无条件/条件跳转

核心作用:直接修改PC值,跳转到指定标号,适用于"循环、分支判断"。

语法:B{cond} Label

示例:

B MainLoop ; 无条件跳转到MainLoop

CMP R0, #0

BNE ErrorHandler ; 若R0≠0,跳转到ErrorHandler

(2)BL:函数调用指令

核心作用:跳转前自动将返回地址(下一条指令地址)存入LR,用于函数调用。

语法:BL{cond} Label

示例:

BL Delay ; 调用Delay函数,LR = 返回地址

MOV R1, #1 ; Delay返回后,从此处继续执行

Delay:

MOV R0, #100000

DelayLoop:

SUBS R0, R0, #1

BNE DelayLoop

BX LR ; 使用 BX LR 返回调用处

(3)伪指令

LDR =val:加载任意32位数值到寄存器。

LDR R0, =0x12345678 ; 加载非立即数

LDR R1, =0x10 ; 编译器可能优化为 MOV R1, #0x10

ADR:获取标号的相对地址(短距离)。

ADR R0, DataBuf ; 将DataBuf的地址加载到R0

DataBuf DCD 0x00, 0x01, 0x02

三、完整示例程序

以下示例覆盖"栈操作、函数调用、内存读写、数据校验"四大核心场景,并使用推荐的PUSH/POP指令。

; 程序说明:Cortex-M3/M4汇编实战示例

; 核心功能:1.栈保存寄存器 2.调用延时函数 3.读写RAM数据 4.校验数据一致性 5.循环执行

AREA ARM_Demo, CODE, READONLY

ENTRY

THUMB ; 明确指定使用Thumb指令集

ALIGN 4

; --------------------------

; 主函数:程序核心逻辑入口

; --------------------------

Main

; 1.栈操作:在函数入口保存可能被使用的寄存器及返回地址,遵守调用规范

PUSH {R0-R2, LR} ; 使用PUSH保存寄存器

; 2.内存读写:向RAM地址(0x20000000)写入数据,再读取校验

MOV R0, #0x20000000 ; R0 = RAM基地址

LDR R1, =0x12345678 ; R1 = 待写入数据

STR R1, [R0] ; 写操作:将数据写入内存

LDR R2, [R0] ; 读操作:从内存读取数据

; 3.数据校验:比较"写入值(R1)"与"读取值(R2)"

CMP R1, R2

BEQ Data_OK ; 若数据一致,跳转

MOV R3, #0x00 ; 数据不一致:R3 = 0x00(错误标志)

B Call_Delay

Data_OK

MOV R3, #0xFF ; 数据一致:R3 = 0xFF(成功标志)

; 4.调用延时函数

Call_Delay

BL Delay_Func ; 调用延时函数

; 5.恢复寄存器并返回:从栈中恢复R0-R2,并通过将LR弹出至PC来返回到调用者,实现循环

POP {R0-R2, PC} ; 使用POP恢复寄存器并返回

; --------------------------

; 延时函数:简单递减延时

; --------------------------

Delay_Func

PUSH {R0, LR} ; 延时函数也保护它用到的寄存器和LR

LDR R0, =500000

Delay_Loop

SUBS R0, R0, #1

BNE Delay_Loop

POP {R0, PC} ; 恢复R0,并通过弹出LR到PC来返回

ALIGN 4

END

执行效果:

入栈后:SP相应递减。

写内存后:查看0x20000000地址,值为0x12345678。

校验后:APSR的Z标志为1,R3被设为0xFF。

全速运行:程序在Main和Delay_Func间循环,R3始终保持0xFF。

四、总结

Cortex-M3/M4汇编的核心逻辑可提炼为三句话:

操作对象是寄存器:核心寄存器仅需掌握R0-R7(数据)、SP/LR/PC(控制)和APSR(条件标志)。

内存访问靠Load/Store:遵循RISC原则,仅LDR/STR指令与内存交互。函数上下文保护使用推荐的PUSH/POP指令。

程序流靠PC控制:跳转用B,函数调用用BL(依赖LR),返回推荐用BX LR或POP {PC}。

http://www.jsqmd.com/news/83555/

相关文章:

  • Tarjan全家桶系列--强联通分量
  • 学Simulink——基于高比例可再生能源渗透的复杂电网建模场景实例:大规模光伏并网对区域电网频率稳定影响研究
  • 485报文订阅服务
  • 毕设开源 深度学习火焰检测识别(源码+论文)
  • 【Spring框架】SpringJDBC
  • 校徽批评,何时从“找茬”走向“建设”?——兼评一篇公众号文章的逻辑
  • 中小诊所系统通常具备哪些功能?
  • 【URP】Unity[后处理]颜色曲线ColorCurves
  • 基于Uniapp的手机维修交流小程序
  • 大模型通义千问3-VL-Plus - 视觉推理(本地图片)
  • Profinet转Modbus TCP工业数据采集网关:实现1200PLC 与打标卡数据实时传输
  • Git能上传多大的文件
  • Burp Suite安装保姆级教程,Burp Suite的基本介绍及使用,收藏这一篇就够了
  • [STM8]学习日志-STM8介绍
  • 【渗透测试零基础入门】搭建 DVWA 靶场保姆级教程(超详细),收藏这一篇就够了!_dvwa靶场搭建
  • 一文详解JUC中乐观锁的实现原理(CAS)、实现方式、优缺点及应用场景总结
  • 3.7 Elasticsearch-查询性能剖析:profile API、DFS query_then_fetch
  • 国内纸纱线FSC春夏14至16针,实力公司推荐排行榜揭秘
  • 参数估计(三)-- 隐参数模型
  • 打CTF,逆向分析攻略!
  • LightModel
  • 数据结构入门:从“是什么”到“为什么要学”
  • 当下的网络安全行业前景到底怎么样?还能入行分蛋糕吗?
  • 国内专业纸纱线FSC春夏14-16针工厂,这份推荐榜单别错过
  • 不同扩散模型下煤层瓦斯运移的Comsol数值模拟:双孔扩散及时变扩散模型文献复现
  • 黑神话 地图APP 程序
  • 【笔记篇】【硬件基础篇】电力电子元器件应用手册 阅读笔记(1)电阻器及其应用
  • 双向buck/boost电路仿真(VDCM控制/电压电流双闭环控制) 利用了传统电机的阻尼和旋...
  • OKR与绩效考核结合:优势、挑战与实践路径
  • AD学习笔记-31 DRC检查