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

硬件开发(5)—ARM汇编

1.arm汇编基础

1.伪操作与代码结构

汇编伪操作并非处理器指令,而是用于指导汇编器组织代码段、指定指令集等,是构建启动代码的 “骨架”。启动代码的标准开头由以下伪操作组成:

area reset, code, readonly              ;定义代码段,命名为reset

code32                                            ;表示后续指令使用 32位的 ARM 指令集
entry                                                ;程序入口

...
(编写核心代码)

...
end                                                  ;结束

2.指令

1.mov
  • 立即数赋值:MOV{S}<c> <Rd>, #<const>Rd:目标寄存器,#<const>:立即数)
  • 寄存器赋值:MOV{S}<c> <Rd>, <Rm>Rm:源寄存器)
移位类型格式示例功能说明
逻辑左移(LSL)MOV R0, R1, LSL #3R1 的值左移 3 位(乘 8),结果赋给 R0
逻辑右移(LSR)MOV R0, R1, LSR #2R1 的值右移 2 位(除 4,无符号)
算术右移(ASR)MOV R0, R1, ASR #2R1 的值右移 2 位(除 4,有符号,保留符号位)
循环右移(ROR)MOV R0, R1, ROR #4R1 的值循环右移 4 位,低位补到高位
2.add、sub:加法与减法
  • 立即数作为第二操作数: ADD{S}<c> <Rd>, <Rn>, #<const>
  • 寄存器作为第二操作数寄存器: ADD{S}<c> <Rd>, <Rn>, <Rm>{, <shift>}
  • 寄存器作为第二操作数移位量: ADD{S}<c> <Rd>, <Rn>, <Rm>, <type> <Rs>
  • 立即数作为第二操作数: SUB{S}<c> <Rd>, <Rn>, #<const>
  • 寄存器作为第二操作数寄存器: SUB{S}<c> <Rd>, <Rn>, <Rm>{, <shift>}
  • 寄存器作为第二操作数移位量: SUB{S}<c> <Rd>, <Rn>, <Rm>, <type> <Rs>

示例:

  1. mov r0, #0xFF
  2. mov r1, #0xF0
  3. mov r2, #1
  4. sub r6, r0, #0xF0
  5. sub r7, r0, r1
  6. sub r7, r0, r1, lsl #1
  7. sub r8, r0, r1, lsl r2

立即数

准确的说这里所指的是12位立即数imm12。先说怎么判断某数是不是12位立即数,12位立即数的条件是:
把某个数展开成2进制,该数必须存在一种循环右移(偶数位),使得移位后高24位全0,低8位即为有效imm8(最高位1与最低位1如果大于8,则不可能是立即数)

3.ldr:加载指令

LDR<c> <Rt>, <label>  (非立即数使用)

4.sdr:存放指令

STR<c> <Rt>, <label> (将Rt寄存器的值存储到label对应内存地址)

5..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>

6.orr(or):指定位置1
  • ORR{S}<c> <Rd>, <Rn>, #<const>
  • ORR{S}<c> <Rd>, <Rn>, <Rm>{, <shift>}
  • ORR{S}<c> <Rd>, <Rn>, <Rm>, <type> <Rs>

7.MVN:按位取反移动指令
  • MVN{S}<c> <Rd>, #<const>
  • MVN{S}<c> <Rd>, <Rm>{, <shift>}
  • MVN{S}<c> <Rd>, <Rm>, <type> <Rs>

取反赋给目标源

8.条件判断标志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(无条件执行)

9.cmp(compare):比较指令
  • CMP<c> <Rn>, #<const>
  • CMP<c> <Rn>, <Rm>{, <shift>}
  • CMP<c> <Rn>, <Rm>, <type> <Rs>

示例:比较三个数大小

10.b bl bx :跳转指令
指令格式核心作用应用场景
BB<c> <label>无条件 / 条件跳转,不保存返回地址循环跳转
BLBL<c> <label>跳转前将当前PC(下一条指令地址)存入LR,支持返回调用汇编函数(如初始化函数)
BXBX <Rm>Rm指定的地址跳转,支持 ARM/Thumb 指令集切换函数返回(BX LR,将LR的值赋给PC

3.基础运用

1.循环

循环三要素:

  • 循环结束条件
  • 推动循环趋向终结的语句
  • 循环的循环体
1.do...while(先执行再判断)

mov r0, #0
mov r1, #0
loop
add r1, r1, r0
add r0, r0, #1
cmp r0, #100
ble loop

2.while、for(先判断再执行)

mov r0, #0
mov r1, #0
loop
cmp r0, #100
bgt finish
add r1, r1, r0
add r0, r0, #1
b loop
finish
b finish

2.函数定义及其调用
1.基本形式

b main   
func
mov r0, #1
mov r1, #2
add r3, r0, r1
bx lr

main
mov r0, #100
mov r1, #200
bl func
mov r3, #300

2.栈类型
类型定义
满减栈(FD)栈指针指向 “最后一个已压栈的数据”,压栈时 SP 先减 4,再存数据
满增栈(FA)栈指针指向 “最后一个已压栈的数据”,压栈时先存数据,再 SP 加 4
空减栈(ED)栈指针指向 “下一个待压栈的空地址”,压栈时先存数据,再 SP 减 4
空增栈(EA)栈指针指向 “下一个待压栈的空地址”,压栈时 SP 先加 4,再存数据

注:arm默认为满减栈

1.栈指针(SP)初始化

压栈前:ldr sp, =0x40001000  ;初始为空,sp指向栈顶

2.保护现场(压栈):STMFD 指令

STMFD <Rn>{!}, <registers>

; <Rn>:栈指针寄存器(通常是SP)

; {!}:压栈后自动更新SP(SP -= 4×寄存器个数)

; <registers>:需压栈的寄存器列表(如r0-r12, lr)

e.g.stmfd sp!, {r0-r12, lr}

3.恢复现场(弹栈):LDMFD 指令

LDMFD <Rn>{!}, <registers>

; <Rn>:栈指针寄存器(通常是SP)

; {!}:弹栈后自动更新SP(SP += 4×寄存器个数)

; <registers>:需恢复的寄存器列表(与压栈顺序一致)

e.g.ldmfd sp!, {r0-r12, lr}

3.在汇编中调用c语言函数

步骤 1:创建 C 语言文件(main.c)

// main.c

extern void c_add(void);

// C函数:计算5个参数的和(r0-r3传前4个,第5个通过栈传递)

int c_add(int a, int b, int c, int d, int e)

{

         return a + b + c + d + e;

}

步骤 2:汇编中声明导入 C 函数

步骤3:保护现场 bl函数调用 恢复现场

步骤4:栈对齐伪指令:preserve8 用于确保函数调用时栈指针保持 8 字节对齐

步骤 5:工程配置(Keil 环境)

  1. 删除冲突文件:删除工程中默认的startup_xxx.s(如startup_stm32f103.s)和.sct(分散加载文件);

  2. 添加自定义文件:将start.smain.c添加到工程;

  3. 配置 ROM/RAM:
    点击 “魔术棒”→Target→设置ROM1Start=0x0Size=0x2000(ROM 起始地址和大小,需匹配芯片);
    设置IRAM1Start=0x40000000Size=0x1000(RAM 地址,确保 SP 初始化地址在范围内);

  4. 链接配置:
    点击 “魔术棒”→Linker→勾选Use Memory Layout from Target Dialog(使用 Target 配置的内存布局,无需分散加载文件);

  5. 调试配置:
    点击 “魔术棒”→Debug→选择Use Simulator(使用模拟器)→取消Run to main(确保从汇编启动入口开始执行)。

步骤6:函数传参

  • 前 4 个参数:通过r0-r3寄存器传递(r0传第 1 个,r1传第 2 个,以此类推);
  • 第 5 个及以后参数:通过栈传递(参数压栈顺序与函数声明顺序一致);
  • 返回值:通过r0寄存器返回(32 位值),64 位值通过r0-r1返回

stmfd sp!, {r0-r12, lr}
mov r0, #1
mov r1, #2
mov r2, #3
mov r3, #4
mov r4, #5
stmfd sp!, {r4}
bl c_add
ldmfd sp!, {r4}
ldmfd sp!, {r0-r12, lr}

4.在c语言中调用汇编

步骤 1:汇编中定义并导出函数(export func1)

步骤 2:C 中声明并调用汇编函数

5.ARM 工作模式切换:修改 CPSR 寄存器

start_hander

        ldr sp, =0x40001000 ; 初始化SVC模式的栈指针

        mrs r0, cpsr ; 读取当前程序状态寄存器(CPSR)到r0

        bic r0, r0, #(0x1F << 0) ; 清除CPSR的M域(最低5位,控制工作模式)

        bic r0, r0, #(1 << 7) ; 清除CPSR的I位(bit7),允许IRQ中断

        orr r0, r0, #(0x10 << 0) ; 设置M域为User模式(0x10 = 0b10000)

        msr cpsr_c, r0 ; 将修改后的值写入CPSR的控制位段,完成模式切换

        ldr sp, =0x40001000 ; 加载User模式栈的初始地址

        sub sp, sp, #1024 ; 调整栈指针,预留1024字节栈空间(满减栈)

6.异常向量表
1.异常向量表

; 异常向量表:CPU发生异常时的入口地址表,每个入口占4字节

ldr pc, =start_hander ; 0x00000000:复位异常(CPU复位后第一个执行的入口)

ldr pc, =undefine_hander ; 0x00000004:未定义指令异常

ldr pc, =software_hander ; 0x00000008:软件中断异常(SWI/SVC)

ldr pc, =prefetch_hander ; 0x0000000C:预取指异常(指令读取错误)

ldr pc, =data_hander ; 0x00000010:数据异常(数据访问错误)

nop ; 0x00000014:预留位置(保证地址对齐)

ldr pc, =irq_hander ; 0x00000018:IRQ中断(外部中断)

ldr pc, =fiq_hander ; 0x0000001C:FIQ中断(快速中断)

当异常发生时,CPU 会自动跳转到对应地址执行处理函数

2. 异常处理函数

; 未定义指令异常处理(死循环,防止程序跑飞)

undefine_hander b undefine_hander ;

软中断异常处理

software_hander
stmfd sp!, {r0-r12, lr} ;保护现场
bl software_vector ;调用具体的中断处理逻辑
ldmfd sp!, {r0-r12, pc}^ ;恢复现场 + 异常返回,此时使用pc是为了恢复中断前状态并把之前保存的lr的值写给pc

3. 软中断触发函数

        export asm_swi_fun ; 导出符号,供C语言调用

asm_swi_fun

        swi #7 ; 触发软件中断,#7是中断编号(用于区分不同服务)

        bx lr ; 函数返回(中断处理完成后继续执行)

4.启动初始化

start_hander ; 初始化SVC模式栈指针(特权模式栈)

         ldr sp, =0x40001000 ; 切换到User模式(应用程序模式)并允许IRQ中断

        mrs r0, cpsr ; 读取当前程序状态寄存器(CPSR)

        bic r0, r0, #(0x1F << 0) ; 清除低5位(工作模式位)

        bic r0, r0, #(1 << 7) ; 清除I位(允许IRQ中断)

        orr r0, r0, #(0x10 << 0) ; 设置为User模式(0x10=0b10000)

         msr cpsr_c, r0 ; 写入CPSR,完成模式切换 ; 初始化User模式栈(应用程序栈,大小1024字节)

         ldr sp, =0x40001000

        sub sp, sp, #1024 ; 满减栈:栈顶地址 = 0x40001000 - 1024

        b main ; 跳转到C语言main函数

5.主函数

int main(void)

{

        asm_swi_fun(); // 调用汇编函数,触发SWI #7软件中断

        while(1); // 死循环,防止程序退出

}


文章转载自:

http://jQD8HXzP.kspfq.cn
http://QlIy5r0F.kspfq.cn
http://TMgvwCOy.kspfq.cn
http://kvXCZOfU.kspfq.cn
http://KStKOwcy.kspfq.cn
http://WkOoTkIG.kspfq.cn
http://uOgvM6pO.kspfq.cn
http://K7EhFOTm.kspfq.cn
http://L3KgoVGM.kspfq.cn
http://ky1UIGrl.kspfq.cn
http://RhLtFNMe.kspfq.cn
http://AMuC1ZnS.kspfq.cn
http://Vj7MJzLw.kspfq.cn
http://AxPsVYrK.kspfq.cn
http://WmIM9nCz.kspfq.cn
http://ozrUKTsK.kspfq.cn
http://kS4zLaQW.kspfq.cn
http://EjJgejkC.kspfq.cn
http://LgFdAgWN.kspfq.cn
http://uSe0GqPA.kspfq.cn
http://97vm74Zi.kspfq.cn
http://5THn8kIe.kspfq.cn
http://4PuJAa7z.kspfq.cn
http://5xS2RxUY.kspfq.cn
http://5xOuFwHf.kspfq.cn
http://D3OxyI2H.kspfq.cn
http://7iP51PvJ.kspfq.cn
http://fLzPBJQx.kspfq.cn
http://lGFZszSy.kspfq.cn
http://4cN3Pbmy.kspfq.cn
http://www.dtcms.com/a/375989.html

相关文章:

  • leetcode16(盛最多水的容器)
  • 《面向高速三维表面成像的微型深度学习轮廓术》论文总结
  • 基于Java的图书管理系统的设计与实现
  • 【Qt跬步积累】—— 初识Qt
  • 第十九章 使用LAMP架构部署动态网站环境
  • 谷歌nano banana官方Prompt模板发布,解锁六大图像生成风格
  • 转载:VSCODE 关闭文件和资源管理器关联
  • Windows 常用命令使用说明
  • Win_Server远程桌面(RDP)服务调用GPU并提上传输帧率和USB设备重定向
  • 【小呆的随机振动力学笔记】概率论基础
  • hipcc编译不生成可执行文件只输出版本信息问题
  • MatDEM一体机的技术深度分析
  • linux三剑客
  • 交换排序——冒泡排序与快速排序
  • DIY项目-校遇
  • GEO 优化系统开发:技术架构与核心实现方案
  • 【66页PPT】质量管理体系五种核心工具APQP(附下载方式)
  • MySQL InnoDB Cluster介绍,MHA、PXC、InnoDB Cluster怎么选?一文讲透MySQL高可用方案最佳实践
  • Miniconda3搭建Selenium的python虚拟环境全攻略
  • 01背包,完全背包,分组背包,多重背包例题
  • Ansible之playbook
  • MapReduce :Map阶段分区后,数据怎么找到Reducer?
  • 项目研发实录:电子称SDK封装dll给到QT和C#调用
  • 短视频矩阵源码-视频剪辑+AI智能体开发接入技术分享
  • 代码随想录算法训练营第三十五天|背包问题 二维 背包问题 一维 46. 携带研究材料 416. 分割等和子集
  • FTP文件传输服务
  • 代码随想录第七天|● 454.四数相加II ● 383. 赎金信 ● 15. 三数之和 18.四数之和
  • SAP R/3系统模块结构
  • leetcode 217 存在重复元素
  • 前端 Word 模板参入特定数据 并且下载