嵌入式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://www.pswp.cn/pingmian/96051.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/96051.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PNPM库离线安装方案

以下是几种可行的方案&#xff0c;推荐优先使用方案一。 方案一&#xff1a;使用离线镜像&#xff08;Offline Mirror&#xff09; - 最优雅、最PNPM的方式 这是 PNPM 官方推荐的处理离线环境的方式。它会在内网电脑上创建一个所有依赖包的压缩文件&#xff08;tarball&#x…

[Wit]CnOCR模型训练全流程简化记录(包括排除BUG)

stepfile:step 00 创建数据集 目录结构 yourproject -data --myset ---images #存放训练图片 ---dev.tsv #测试标签 tsv格式 图片文件名\t内容 ---train.tsv #训练标签 tsv格式 图片文件名\t内容 -train_config.json -train_config_gpu.json -fix_cnocr_encoding.py step 01 创…

Sklearn(机器学习)实战:鸢尾花数据集处理技巧

1.数据集的使用&#xff1a;先使用load导入鸢尾花数据&#xff1a;from sklearn.datasets import load_iris然后定义一个函数来查看鸢尾花数据集&#xff1a;数据集的获取&#xff1a;iris load_iris()print(鸢尾花的数据集&#xff1a;\n,iris)使用iris[DESCR]来查看数据及里…

【企业微信】接口报错:javax.net.ssl.SSLHandshakeException

详细报错信息 javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target原因 关于qyapi…

光子芯片驱动的胰腺癌早期检测:基于光学子空间神经网络的高效分割方法

光子芯片驱动的胰腺癌早期检测:基于光学子空间神经网络的高效分割方法 1 论文核心概念 本文提出了一种基于集成光子芯片的光学子空间神经网络(Optical Subspace Neural Network, OSNN),用于胰腺癌的早期检测与图像分割。其核心思想是利用光子芯片的高并行性、低延迟和低能…

Scikit-learn Python机器学习 - 特征降维 压缩数据 - 特征提取 - 主成分分析 (PCA)

锋哥原创的Scikit-learn Python机器学习视频教程&#xff1a; 2026版 Scikit-learn Python机器学习 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 课程介绍 本课程主要讲解基于Scikit-learn的Python机器学习知识&#xff0c;包括机器学习概述&#xff0c;特征工程(数据…

【Python】pytorch安装(使用conda)

# 创建 PyTorch 虚拟环境 conda create -n pytorch_env python3.10# 激活环境 conda activate pytorch_env# 安装 PyTorch&#xff08;CPU版本&#xff09; conda install pytorch torchvision torchaudio cpuonly -c pytorch# 或者安装 GPU 版本&#xff08;如果有NVIDIA显卡&…

ThreeJS骨骼示例

<html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>骨骼动画混合演示</title><style>body {margin: 0;padding: …

python + Flask模块学习 1 基础用法

目录 Flask 的主要作用 常用扩展 Flask 基本用法 1. 安装 Flask&#xff08;再安装个postman用来调试测试API哈 2. 最小化应用示例 3. 运行应用 Flask 是一个轻量级的 Python Web 框架&#xff0c;它简洁灵活&#xff0c;适合快速开发 Web 应用和 API。它被称为 "微…

python数据可视化之Matplotlib(8)-Matplotlib样式系统深度解析:从入门到企业级应用

作者&#xff1a;浪浪山齐天大圣 描述&#xff1a;深入探索Matplotlib样式系统的核心机制&#xff0c;掌握从基础样式到企业级样式管理的完整解决方案引言 在数据可视化的世界里&#xff0c;一个优秀的图表不仅要准确传达数据信息&#xff0c;更要具备专业的视觉效果。Matplotl…

3.HTTP/HTTPS:报文格式、方法、状态码、缓存、SSLTLS握手

HTTP/HTTPS&#xff1a;报文格式、方法、状态码、缓存、SSL/TLS握手 1. HTTP报文格式 1.1 HTTP请求报文(Request) GET /api/v1/users HTTP/1.1 // 请求行&#xff1a;方法、URI、协议版本 Host: api.example.com // 请求头 (Headers) User-Agent: Mozil…

【慢教程】Ollama4:ollama命令汇总

ℹ️教程说明 Ollama 是一款轻量级本地大模型部署工具&#xff0c;使用广泛&#xff0c;且容易上手&#xff0c;适合作为AI技术的入门。 &#x1f9e9;教程各部分链接&#xff1a; 第一课&#xff1a;ollama运行原理介绍及同类工具对比 ollama运行原理介绍及同类工具对比&am…

JAVA Predicate

简单来说&#xff0c;当我明确知道此次判断的逻辑时就可以直接使用if&#xff0c;但是我这次的判断逻辑可能会随着某个参数变化的时候使用Predicate比如当我想要判断某长段文字中是否包含list<String> 中的元素&#xff0c;并且包含的元素个数大于 list<String>最后…

什么是PFC控制器

一句话概括PFC控制器是一种智能芯片&#xff0c;它通过控制电路中的电流波形&#xff0c;使其与电压波形保持一致&#xff0c;从而减少电力浪费&#xff0c;提高电能的利用效率。PFC控制器IC的核心作用就是控制一颗功率MOSFET的开关&#xff0c;通过特定的电路拓扑&#xff08;…

【P03_AI大模型测试之_定制化 AI 应用程序开发】

git clone https://gitee.com/winner21/aigc-test.git 类似于joycoder的&#xff0c;可以安装在vscode上的通义灵码&#xff1a;https://lingma.aliyun.com/ 1、VSCODE上配置通义灵码 2、创建前后端文件&#xff0c;并引用AI编码代码 3、指定文件&#xff0c;利用AI进行代码优…

人工智能机器学习——决策树、异常检测、主成分分析(PCA)

一、决策树(Decision Tree) 决策树&#xff1a;一种对实例进行分类的树形结构&#xff0c;通过多层判断区分目标所属类别 本质&#xff1a;通过多层判断&#xff0c;从训练数据集中归纳出一组分类规则 优点&#xff1a; 计算量小&#xff0c;运算速度快易于理解&#xff0c;可…

服务器文件同步用哪个工具?介绍一种安全高效的文件同步方案

服务器作为企业核心数据和应用的载体&#xff0c;服务器文件同步已成为IT运维、数据备份、业务协同中不可或缺的一环。然而&#xff0c;面对多样的场景和严苛的需求&#xff0c;选择一个既高效又安全的服务器文件同步工具并非易事。本文将首先探讨服务器文件同步的常见场景、需…

LeetCode 004. 寻找两个正序数组的中位数 - 二分切分与分治详解

一、文章标题 LeetCode 004. 寻找两个正序数组的中位数 - 二分切分与分治详解 二、文章内容 1. 题目概述 题目描述&#xff1a;给定两个已按非降序排列的整数数组 nums1、nums2&#xff0c;设它们长度分别为 m 和 n&#xff0c;要求返回这两个数组合并后有序序列的中位数。…

预闪为什么可以用来防红眼?

打闪拍照红眼产生的原因 预闪可以用来防红眼&#xff0c;是基于人眼的生理特性和红眼现象的产生原理。在光线较暗时&#xff0c;人眼的瞳孔会放大。当使用闪光灯拍摄时&#xff0c;如果直接进行高强度闪光&#xff0c;由于瞳孔来不及缩小&#xff0c;闪光灯的光线会反射在眼球血…

Python程序使用了Ffmpeg,结束程序后,文件夹中仍然生成音频、视频文件

FFmpeg是一套可以用来记录、转换数字音频、视频&#xff0c;并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec&#xff0c;为了保证高可移植性和编解码质量&#xff0…