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

嵌入式ARM架构学习3——启动代码

一 汇编补充:

	area reset, code, readonlycode32entry;mov r0, #4 ; r0 = 4;mov r1, r0 ; r1 = r0;mov r2, r1, lsl #1 ;r2 = r1 << 1 乘2;mov r3, r1, lsr #1 ;r3 = r1 >> 1 除2;mov r4, r1, ror #2;mov r0, #100 ;100是十进制 转为16进制赋值给十进制;mov r1, #8;add r2, r0, #100;add r2, r0, r1;add r2, r0, #(100 << 2);;mov r0, #0xF0;mvn r0, r0;ldr r0, =0xfac0;把任意 32 位常数送进寄存器;指定位置置0;mov r0, #0xFFFFFFFF;mov r1, #1;bic r0, r0, #4 ;指定位 4:(0100)r0里4对应的1所在位置置0;bic r0, r0, r1, lsl #2 ;先把r1 左移2位 然后对应的1的位置对应于r0中 给置0;bic r0, r0, #(1 << 2);清理掉第2位;指定位置1;mov r0, #0x80000000;orr r0, r0, #(1<<31);mov r0, #0x0 ;1011;orr r0, r0, #(1 << 10);三个数之间找最大值;mov r0, #100;mov r1, #200;mov r2, #300;cmp r0, r1;movge r3, r0;movlt r3, r1;cmp r3, r2;movge r3, r3;movlt r3, r2;if分支;mov r0, #0x100;mov r1, #0x200;cmp r0, r1;bge greatr;blt less;greatr;mov r2, r0;b finish ;跳过另一个分支 跳转到finish;less;mov r2, r1;finish;b finish;while 循环;mov r0, #0;mov r1, #0
;loop;cmp r0, #100;bge finish;add r1, r1, r0;add r0, r0, #1;b loop;finish;b finish  ;死循环;do_while循环;mov r0, #0;mov r1, #0
;loop;add r1, r1, r0;add r0, r0, #1;cmp r0, #100;bge finish;b loop
;finish;b finish  ;死循环;b main
;asm_maxTwoNum
;	mov r0, #100
;	mov r1, #200
;	cmp r0, r1
;	movge r3, r0
;	movlt r3, r1
;	mov pc, lr	 ;把返回地址赋给 PC → 立即跳回调用点继续执行
;	bx lr ;bx 跳转到调用点的下一行
;main
;	mov r0, #10;mov r1, #20
;	bl asm_maxTwoNum ;bl 跳转之前保留当前地址
;	mov r2, #10
;	mov r3, #20;========= 子函数:返回 r0、r1 中的较大值 =========
asm_maxTwoNummov r0, #100 ;把参数写成100、200(覆盖main传入的10/20)mov r1, #200cmp r0, r1stmfd sp!, {r0-r12, lr}  ; 先压栈保护现场bl asm_fun0         ; 调用asm_fun0(无意义,返回后r0/r1仍保持100/200)ldmfd sp!, {r0-r12, lr}  ; 弹栈恢复现场movge r3, r0        ; 若 r0>=r1  大数→r3  (100<200,不执行)movlt r3, r1        ; 若 r0< r1  大数→r3  (执行,r3=200)mov r0, r3          ; 把结果200写回r0作为返回值bx  lr              ; 返回;========= 另一个函数,仅演示跳转 =========
asm_fun0mov   r0, #100mov   r1, #200bx    lr                ; 直接返回,无实质操作;========= 主函数:入口 =========
mainldr sp, =0x40001000   ; 设栈顶(裸机常用)mov r0, #10           ; 准备参数 1mov r1, #20           ; 准备参数 2stmfd sp!, {r0-r12, lr} ; 保存现场bl  asm_maxTwoNum     ; 调最大值函数ldmfd sp!, {r0-r12, lr} ; 恢复现场mov r2, #10           mov r3, #20ldr r0, =0x40000000 ;地址指针ldr r1, =0x12345678	;数据str r1,[r0],#4   ;*p = 0x12345678; p++ldr r2,[r0]	   	; r2 = *(p+1)(未初始化,值未知);ARM 是 32 位架构,一个“字(word)” = 4 字节。;地址按字节编号,因此索引写 #4 才能指到下一个字。finishb finish            ; 死循环,程序结束end

汇编asm和c文件之间的传参 

 preserve8

且魔术棒配置ROM地址区域

mainldr sp, =0x40001000   ; 设栈顶(裸机常用)import c_add;mov r0, #10           ; 准备参数 1;mov r1, #20           ; 准备参数 2stmfd sp!, {r0-r12, lr} ; 保存现场;bl  asm_maxTwoNum     ; 调最大值函数mov r0, #100           mov r1, #200bl c_add;r0默认接收函数返回值ldmfd sp!, {r0-r12, lr} ; 恢复现场;mov r2, #10           ;mov r3, #20ldr r0, =0x40000000 ;地址指针ldr r1, =0x12345678	;数据str r1,[r0],#4   ;*p = 0x12345678; p++ldr r2,[r0]	   	; r2 = *(p+1)(未初始化,值未知);ARM 是 32 位架构,一个“字(word)” = 4 字节。;地址按字节编号,因此索引写 #4 才能指到下一个字。finishb finish            ; 死循环,程序结束extern c_add(int a, int b);int c_add(int a, int b)
{return a + b;
}

当传参数大于4个——通过压栈传参

	b main
asm_maxTwoNummov r0, #100 ;把参数写成100、200(覆盖main传入的10/20)mov r1, #200cmp r0, r1stmfd sp!, {r0-r12, lr}  ; 先压栈保护现场bl asm_fun0         ; 调用asm_fun0(无意义,返回后r0/r1仍保持100/200)ldmfd sp!, {r0-r12, lr}  ; 弹栈恢复现场movge r3, r0        ; 若 r0>=r1  大数→r3  (100<200,不执行)movlt r3, r1        ; 若 r0< r1  大数→r3  (执行,r3=200)mov r0, r3          ; 把结果200写回r0作为返回值bx  lr              ; 返回;========= 另一个函数,仅演示跳转 =========
asm_fun0mov   r0, #100mov   r1, #200bx    lr                ; 直接返回,无实质操作;========= 主函数:入口 =========
mainldr sp, =0x40001000   ; 设栈顶(裸机常用)import c_addmov r0, #10           ; 准备参数 1mov r1, #20           ; 准备参数 2stmfd sp!, {r0-r12, lr} ; 保存现场;bl  asm_maxTwoNum     ; 调最大值函数mov r0, #10           mov r1, #20mov r2, #30           mov r3, #40mov r4, #50;	传参大于4个需要通过压栈进行传参 stmfd sp!, {r4} ; 压栈r4传参         bl c_add;r0默认接收函数返回值ldmfd sp!, {r4} ; 弹栈ldmfd sp!, {r0-r12, lr} ; 恢复现场;mov r2, #10           ;mov r3, #20ldr r0, =0x40000000 ;地址指针ldr r1, =0x12345678	;数据str r1,[r0],#4   ;*p = 0x12345678; p++ldr r2,[r0]	   	; r2 = *(p+1)(未初始化,值未知);ARM 是 32 位架构,一个“字(word)” = 4 字节。;地址按字节编号,因此索引写 #4 才能指到下一个字。finishb finish            ; 死循环,程序结束

C语言 调用汇编


extern int c_add(int a, int b, int c, int d, int e);extern  int asm_maxTwoNum(int a, int b);int c_add(int a, int b, int c, int d, int e)
{int ret;//ret = a + b + c + d + e;ret = asm_maxTwoNum(5, 10);return ret;
}b main
asm_fun0mov r0, #100mov r1, #200bx lrexport 	asm_maxTwoNum
asm_maxTwoNumcmp r0, r1movge r3, r0movlt r3, r1 mov r0, r3bx lr

启动代码:最小可运行的 ARM 裸机启动/中断向量表/软中断(SWI) 示例

上电后建立向量表 → 切到用户模式并开 IRQ → 给 User 栈留空间 → 跳进 C 的 main();同时提供 asm_swi_fun() 让 C 主动触发 7 号软中断,异常处理完整保存/恢复现场并自动返回用户模式

	preserve8area reset, code, readonlycode32entry;中断向量表ldr pc, =start_hander       ; 0x00  复位ldr pc, =undefine_hander    ; 0x04  未定义指令ldr pc, =software_hander    ; 0x08  SWI(软中断)ldr pc, =prefetch_hander    ; 0x0C  预取指中止ldr pc, =data_hander        ; 0x10  数据中止nop                         ; 0x14  保留ldr pc, =irq_hander         ; 0x18  IRQldr pc, =fiq_hander         ; 0x1C  FIQundefine_handerb undefine_handerprefetch_handerb prefetch_handerdata_handerb data_handerirq_handerb irq_handerfiq_handerb fiq_handerimport software_vector;告诉汇编器 C 里实现该函数
software_handerstmfd sp!, {r0-r12, lr}   ; 保存用户现场(lr = SWI 返回地址)bl software_vector        ; 调到 C 处理函数ldmfd sp!, {r0-r12, pc}^  ; 恢复寄存器 且 把保存的 lr 弹进 pc; ^ 表示 同时把 SPSR_svc → CPSR(自动返回用户模式)export asm_swi_fun   ; 供c调用
asm_swi_funswi #7           ; 触发 7 号软中断(编号任意)bx lr            ; 从 SWI 返回后再回到 Cstart_handerldr sp, =0x40001000   ; 设栈顶(裸机常用)import main			 ;引入c语言中的main函数;--- 下面 5 行 = 切到用户模式并打开 IRQ ----------mrs r0, cpsr   ;r0 ← CPSRbic r0, r0, #(0x1F << 0) ;CPSR的M域是后五位 先清零(清模式位)bic r0, r0, #(1 << 7)	  ;清CPSR 的 I 位(允许 IRQ)orr r0, r0, #(0x10 << 0) ;设 M[4:0]=0b10000(User 模式)msr cpsr_c, r0		  ;**只把模式/中断位写回**;给 User 模式建栈ldr sp, =0x40001000sub sp, sp, #1024  ;; 留 1 KB 给 User 栈b main            ; 跳转c语言的函数end

15、arm汇编调用c语言函数以及c语言函数调用汇编编写的函数,函数参数和返回值如何处理?

核心原则:

参数和返回值主要通过 寄存器 传递,当寄存器不够用时,使用

ARM汇编调用C语言函数 过程如下:

  1. 参数传递:

    • 前4个参数 (对于32位ARM): 如果参数是32位整型或指针类型,依次使用寄存器 R0, R1, R2, R3 来传递。

    • 第5个及以后的参数: 从第5个参数开始,将其压入 (Stack) 中。调用者负责在调用前分配栈空间并压入这些参数。

  2. 执行调用 :

    • 使用 BL (Branch with Link) 指令跳转到C函数地址。BL 指令会将下一条指令的地址(返回地址)保存到链接寄存器 LR (R14) 中。

  3. 返回值获取:

    • 32位及以下的返回值: C函数执行完毕后,返回值通过寄存器 R0 返回给汇编调用者。

C语言函数调用ARM汇编函数 过程如下:

  1. 编写汇编函数:

    • 汇编函数需要将自己视为一个被C代码调用的普通函数

    • 它可以从 R0-R3 中获取前4个传入的参数。

    • 如果需要更多的参数,它需要从栈上的特定位置去获取。SP寄存器指向的栈顶之后的位置就是第5、6...个参数。

  2. 保存上下文 :

    • 汇编函数如果会修改一些被调用者保存寄存器 (Callee-saved registers),如 R4-R11, SP, LR,则必须在函数开头将它们压栈保存 (PUSH),并在函数结束返回前出栈恢复 (POP)

    • 对于调用者保存寄存器 (Caller-saved registers),如 R0-R3, R12,则可以自由使用,无需保存(因为调用者C代码已经假定它们可能被破坏)。

  3. 返回值设置 (Setting the Return Value):

    • 在函数返回前,将需要返回的值放入 R0(或 R0R1)。

  4. 返回 (Returning):

    • 通常使用 BX LR 指令返回到调用者(C代码)。这条指令会跳转到 LR 寄存器中保存的返回地址。

16. ARM内核异常、类型及工作模式切换

核心概念:

异常是CPU对内部或外部紧急事件的一种响应机制。当异常发生时,ARM内核会暂停当前执行的程序,跳转到预先设置好的异常向量表中的特定地址去执行对应的异常处理程序,同时CPU的工作模式会自动切换到对应的特权模式,以便操作系统内核有足够的权限来处理异常。

ARM经典架构(如ARMv7-A)中的7种异常:

异常类型触发条件使内核进入的工作模式优先级(通常)
1. 复位 (Reset)处理器上电或按下复位键Supervisor (SVC)最高 (1)
2. 未定义指令 (Undefined Instruction)CPU遇到无法识别的指令Undefined6
3. 软件中断 (SWI) / SVC程序执行 SVCSWI 指令Supervisor (SVC)6
4. 指令预取中止 (Prefetch Abort)预取指令时发生内存访问错误Abort5
5. 数据中止 (Data Abort)读写数据时发生内存访问错误Abort2
6. 中断请求 (IRQ)外部设备发出普通中断请求IRQ4
7. 快速中断请求 (FIQ)外部设备发出高优先级中断请求FIQ3

工作模式详解:

异常会导致内核从User等非特权模式切换到下表中的特权模式:

工作模式简称用途
用户模式User正常程序执行(非异常模式)
快速中断模式FIQ处理高速数据传输、DMA等 (异常模式)
中断模式IRQ处理普通中断 (异常模式)
管理模式SVC操作系统内核、软件中断、复位 (异常模式)
中止模式Abort处理内存访问失败 (异常模式)
未定义模式Undefined处理未定义指令异常 (异常模式)
系统模式System运行特权级操作系统任务 (ARMv4及以上,非异常模式)

重点说明:

  • 复位 (Reset) 是最高优先级异常,它让系统从一个已知的初始状态开始运行,直接进入SVC模式。

  • 软件中断 (SVC)用户程序主动切换到内核态(SVC模式)的方式,用于请求系统服务,例如打开文件、创建线程等(类似Linux中的系统调用)。

  • IRQ和FIQ异步异常,由外部硬件触发。FIQ的向量地址在向量表末尾,允许处理程序直接开始执行而无需跳转,并且有更多的专用寄存器,从而可以实现更快的处理速度。

  • 中止异常 通常与内存管理单元 (MMU) 相关,用于实现虚拟内存内存保护。预取中止发生在取指阶段,数据中止发生在数据访问阶段。

  • 每种异常模式都有自己独立的栈指针 (SP)链接寄存器 (LR) 副本,这避免了在处理异常时破坏用户模式下的寄存器状态,确保了异常处理的安全和可靠。


文章转载自:

http://5pm7SdSW.fsLrx.cn
http://YOWAaJB9.fsLrx.cn
http://XkQFJLZh.fsLrx.cn
http://zhS4JV9w.fsLrx.cn
http://unxe5ywM.fsLrx.cn
http://6dlkiylC.fsLrx.cn
http://O6ue6ilB.fsLrx.cn
http://v3X9Pm16.fsLrx.cn
http://ucv1wXsX.fsLrx.cn
http://fSuurEkl.fsLrx.cn
http://fVkkPHVY.fsLrx.cn
http://vT6EfzCq.fsLrx.cn
http://7U6XrZUY.fsLrx.cn
http://ktZVMj9q.fsLrx.cn
http://QYrcbzCW.fsLrx.cn
http://UGXKGXgY.fsLrx.cn
http://25biOWOu.fsLrx.cn
http://KxvO9Th2.fsLrx.cn
http://egQU3Kq8.fsLrx.cn
http://0Gn2dylj.fsLrx.cn
http://XlJ5kFE9.fsLrx.cn
http://XkUomxRq.fsLrx.cn
http://nCqarA5u.fsLrx.cn
http://rqhpsVzj.fsLrx.cn
http://FisvxaSX.fsLrx.cn
http://5CnxOCFS.fsLrx.cn
http://Hbk3g8GQ.fsLrx.cn
http://PU7GFsGD.fsLrx.cn
http://8U1jAUJ7.fsLrx.cn
http://hX8pEeMY.fsLrx.cn
http://www.dtcms.com/a/374390.html

相关文章:

  • 2025云计算趋势:Serverless与AI大模型如何赋能中小企业
  • 如何利用 AWS 服务器优化跨境电商和 SEO 战略?
  • 大数据毕业设计-基于Python的中文起点网小说数据分析平台(高分计算机毕业设计选题·定制开发·真正大数据)
  • 小程序开发单行日历可滑动
  • 项目日记 -日志系统 -搭建基础框架
  • 计算机网络第四章(4)——网络层《ARP协议》
  • 探迹SalesGPT
  • 带有 Attention 机制的 Encoder-Decoder 架构模型分析
  • 利用易语言编写,逻辑为按照数字越大抽取率越前
  • leetcode 219 存在重复元素II
  • Redis(缓存)
  • ARP 协议
  • 169.在Vue3中使用OpenLayers + D3实现地图区块呈现不同颜色的效果
  • 【C++】递归与迭代:两种编程范式的对比与实践
  • 【Java】设计模式——单例、工厂、代理模式
  • C++ ——一文读懂:Valgrind 检测内存泄漏
  • 代码随想录算法训练营第三十一天 | 合并区间、单调递增的数字
  • Redis核心通用命令深度解析:结合C++ redis-plus-plus 实战指南
  • 三防手机的三防是指什么?推荐一款实用机型
  • 请求库-axios
  • Python 2025:AI工程化与智能代理开发实战
  • 聚铭网络入选数世咨询《中国数字安全价值图谱》“日志审计”推荐企业
  • 【56页PPT】数字化智能工厂总体设计SRMWCSWMSMESEMS系统建设方案(附下载方式)
  • 高性价比云手机挑选指南
  • 分布式IP代理集群架构与智能调度系统
  • 构造函数和析构函数中的多态陷阱:C++的隐秘角落
  • 使用 Altair RapidMiner 将机器学习引入您的 Mendix 应用程序
  • 从IFA再出发:中国制造与海信三筒洗衣机的“答案”
  • SQLite 数据库核心知识与 C 语言编程
  • unity中通过拖拽,自定义scroll view中子物体顺序