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

ARM3.(汇编函数和c语言相互调用及ARM裸机开发环境搭建)

1.汇编语言调用c语言函数

首先我们编写一个简单的c语言函数并声明:


extern int c_add(void);
int c_add(void)
{return 10;
}

在汇编语言中用bl指令调用:

	preserve8;栈指针按照8字节对齐area reset,	code, readonly		code32entry;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;调用c语言函数
mainldr sp, =0x40001000import c_add;导入c语言函数stmfd sp!, {r0-r12,lr};带!栈帧移动,不带不移动,将r0-r12,lr压栈bl c_addldmfd sp!, {r0-r12,lr};与压栈列表对应,否则会产生错位,将r0-r12,lr弹出
;finish
;	b finishend			 

          需要注意的是:汇编语言在调用c函数时需要在最开始加上伪指令preserve8保证你的汇编代码会维护 8 字节栈对齐的规则。这样才能准确跳转并返回。并且需要用import指令在汇编文件中“声明”一个外部函数,相当于 C 语言中的 extern void c_add(void);

1.1汇编语言调用c函数传参

写一个简单的c语言代码:


extern int c_add(int a, int b);
int c_add(int a, int b)
{return a + b;
}

我们通过通用寄存器r0,r1,r2,r3进行传参,参数不能超过4个。

	preserve8;栈指针按照8字节对齐area reset,	code, readonly		code32entry;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;调用c语言函数
mainldr sp, =0x40001000import c_add;导入c语言函数stmfd sp!, {r0-r12,lr};带!栈帧移动,不带不移动,将r0-r12,lr压栈mov r0, #100mov r1, #200bl c_add			  ;调用c语言函数ldmfd sp!, {r0-r12,lr};与压栈列表对应,否则会产生错位,将r0-r12,lr弹出
finishb finishend			 

可以看出a和b的值被r0,r1赋予。返回值默认存储到r0。

如果参数超过四个,该怎么解决呢?

写一个简单的c语言函数:


extern int c_add(int a, int b, int c, int d, int e);
int c_add(int a, int b, int c, int d, int e)
{return a + b + c + d + e;
}

传递5个参数:

	preserve8;栈指针按照8字节对齐area reset,	code, readonly		code32entry;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mainldr sp, =0x40001000import c_add;导入c语言函数stmfd sp!, {r0-r12,lr};带!栈帧移动,不带不移动,将r0-r12,lr压栈mov r0, #1mov r1, #2mov r2, #3mov r3, #4mov r4, #5bl c_add			  ;调用c语言函数ldmfd sp!, {r0-r12,lr};与压栈列表对应,否则会产生错位,将r0-r12,lr弹出
finishb finishend			 

可以发现参数超过四个,e没有被赋值:

我们要想传递5个参数,就需要通过压栈操作进行传参。

preserve8;栈指针按照8字节对齐area reset,	code, readonly		code32entrymainldr sp, =0x40001000import c_add;导入c语言函数stmfd sp!, {r0-r12,lr};带!栈帧移动,不带不移动,将r0-r12,lr压栈mov r0, #1mov r1, #2mov r2, #3mov r3, #4mov r4, #5stmfd sp!, {r4}	 ;将r4压入栈中bl c_add  ;调用c语言函数ldmfd sp!, {r4}	 ;将r4弹出	  ldmfd sp!, {r0-r12,lr};与压栈列表对应,否则会产生错位,将r0-r12,lr弹出
finishb finishend

可以看到e被成功赋值了

r0保存的结果正确:

1.2c语言调用汇编函数

汇编函数在被调用时,需要用export指令声明,同时在c语言程序中也需要声明:

preserve8;栈指针按照8字节对齐area reset,	code, readonly		code32entry;c语言调用汇编函数b mainexport asm_maxTwoNum   ;导出汇编函数,类似于声明
asm_maxTwoNumcmp r0, r1movge r3, r0movlt r3, r1mov r0, r3mov pc, lr
mainldr sp, =0x40001000import c_add;导入c语言函数;stmfd sp!, {lr};带!栈帧移动,不带不移动,将lr压栈bl c_add  ;调用c语言函数	  ldmfd sp!, {lr};与压栈列表对应,否则会产生错位,将lr弹出
finishb finishend

extern int c_add(void);
extern int asm_maxTwoNum(int a, int b);
int c_add(void)
{int ret;ret = asm_maxTwoNum(1, 2);  // 参数1通过R0传递,参数2通过R1传递return ret;
}

可以看出ret被正确赋值:

2.ARM裸机开发环境搭建

2.1异常向量表

        ARM架构规定,异常向量表必须位于内存地址0x00000000或0xFFFF0000(高端向量)

因此我们先搭建好异常向量表及处理函数框架

	preserve8;栈指针按照8字节对齐area reset,	code, readonly		code32entry
; 异常向量表 (Exception Vector Table)
; ARM架构规定,异常向量表必须位于内存地址0x00000000或0xFFFF0000(高端向量)
; 每个向量占4字节,包含一条跳转指令ldr pc, =start_hander    ; 复位向量(Reset)         - 0x00: 系统上电或复位时执行ldr pc, =undefine_hander ; 未定义指令异常(Undefined) - 0x04: 遇到无法识别的指令时执行ldr pc, =software_hander ; 软件中断异常(SWI)       - 0x08: 执行SWI指令时触发ldr pc, =prefetch_hander ; 预取指中止(Prefetch Abort)- 0x0C: 指令预取失败时执行ldr pc, =data_hander     ; 数据中止(Data Abort)     - 0x10: 数据访问失败时执行nop                      ; 占位(Reserved地址预留)   - 0x14: ARM保留,未使用ldr pc, =irq_hander      ; 普通中断(IRQ)           - 0x18: 外设中断请求时执行ldr pc, =fiq_hander      ; 快速中断(FIQ)           - 0x1C: 高优先级中断请求时执行undefine_handerb undefine_handersoftware_handerb software_handerprefetch_handerb prefetch_handerdata_handerb data_handerirq_handerb irq_handerfiq_handerb fiq_handerstart_handerldr sp, =0x40001000finishb finishend

再来编写初始化函数,令其处于user模式

此时cpsr的m字段处于0x13(100011)Supervisor工作模式中。

这时我们需要通过MRS指令读取,MSR指令写入来修改M字段从而改变工作状态了。

2.1MRS指令,用于从系统寄存器读取数据到通用寄存器。MSR指令,用于将通用寄存器的数据写入系统寄存器。

	preserve8;栈指针按照8字节对齐area reset,	code, readonly		code32entry
; 异常向量表 (Exception Vector Table)
; ARM架构规定,异常向量表必须位于内存地址0x00000000或0xFFFF0000(高端向量)
; 每个向量占4字节,包含一条跳转指令ldr pc, =start_hander    ; 复位向量(Reset)         - 0x00: 系统上电或复位时执行ldr pc, =undefine_hander ; 未定义指令异常(Undefined) - 0x04: 遇到无法识别的指令时执行ldr pc, =software_hander ; 软件中断异常(SWI)       - 0x08: 执行SWI指令时触发ldr pc, =prefetch_hander ; 预取指中止(Prefetch Abort)- 0x0C: 指令预取失败时执行ldr pc, =data_hander     ; 数据中止(Data Abort)     - 0x10: 数据访问失败时执行nop                      ; 占位(Reserved地址预留)   - 0x14: ARM保留,未使用ldr pc, =irq_hander      ; 普通中断(IRQ)           - 0x18: 外设中断请求时执行ldr pc, =fiq_hander      ; 快速中断(FIQ)           - 0x1C: 高优先级中断请求时执行undefine_handerb undefine_handersoftware_handerb software_handerprefetch_handerb prefetch_handerdata_handerb data_handerirq_handerb irq_handerfiq_handerb fiq_handerstart_handerldr sp, =0x40001000	 ;给Supervisor分配栈指针寄存器
;切换user模式(10000)mrs r0, cpsr				;将cpsr数据读取到r0bic r0, r0, #(0x1f << 0) 	;低5位(m字段)全部清零bic r0, r0, #(1 << 7)		;irq中断模式启动orr r0, r0, #(0x10 << 0)	;第5位置1,切换为user模式msr cpsr_c, r0				;将r0数据写回cpsrldr sp, = 0x40001000 ;给user分配栈指针寄存器sub sp, sp, #1024     ;给Supervisor预留1k大小的栈,在0x40001000-1024处设定user的栈指针寄存器
finishb finishend

这样就完成了user模式的切换。

2.2swi指令,触发一个软件异常,使处理器从用户模式切换到特权模式(Supervisor),并跳转到操作系统预设的异常处理程序

我们试试触发软件中断异常

	preserve8;栈指针按照8字节对齐area reset,	code, readonly		code32entry
; 异常向量表 (Exception Vector Table)
; ARM架构规定,异常向量表必须位于内存地址0x00000000或0xFFFF0000(高端向量)
; 每个向量占4字节,包含一条跳转指令ldr pc, =start_hander    ; 复位向量(Reset)         - 0x00: 系统上电或复位时执行ldr pc, =undefine_hander ; 未定义指令异常(Undefined) - 0x04: 遇到无法识别的指令时执行ldr pc, =software_hander ; 软件中断异常(SWI)       - 0x08: 执行SWI指令时触发ldr pc, =prefetch_hander ; 预取指中止(Prefetch Abort)- 0x0C: 指令预取失败时执行ldr pc, =data_hander     ; 数据中止(Data Abort)     - 0x10: 数据访问失败时执行nop                      ; 占位(Reserved地址预留)   - 0x14: ARM保留,未使用ldr pc, =irq_hander      ; 普通中断(IRQ)           - 0x18: 外设中断请求时执行ldr pc, =fiq_hander      ; 快速中断(FIQ)           - 0x1C: 高优先级中断请求时执行undefine_handerb undefine_hander;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;import software_vecetor;导入c语言函数
software_handerbl software_vecetorbx lr
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
prefetch_handerb prefetch_handerdata_handerb data_handerirq_handerb irq_handerfiq_handerb fiq_handerexport asm_swi_fun
asm_swi_funswi #7	 ;触发一个软件异常,使处理器从用户模式切换到特权模式(Supervisor),并跳转到操作系统预设的异常处理程序(0x08)。bx lrstart_handerldr sp, =0x40001000	 ;给Supervisor分配栈指针寄存器import main ;声明主函数
;切换user模式(10000)mrs r0, cpsr				;将cpsr数据读取到r0bic r0, r0, #(0x1f << 0) 	;低5位(m字段)全部清零bic r0, r0, #(1 << 7)		;irq中断模式启动orr r0, r0, #(0x10 << 0)	;第5位置1,切换为user模式msr cpsr_c, r0				;将r0数据写回cpsrldr sp, = 0x40001000 ;给user分配栈指针寄存器sub sp, sp, #1024     ;给Supervisor预留1k大小的栈,在0x40001000-1024处设定user的栈指针寄存器b main	;进入main函数end

在c语言调用asm_swi_fun:

extern main(void);
extern void asm_swi_fun(void);
extern void software_vecetor(void);
void software_vecetor(void)
{}
int main(void)
{asm_swi_fun();while(1){}return 0;
}

由于swi在触发软件异常的时候会切换为Supervisor模式,因此我们在调用完成后需要重新将模式切换为user模式,也就是进行压栈弹栈操作:

	preserve8;栈指针按照8字节对齐area reset,	code, readonly		code32entry
; 异常向量表 (Exception Vector Table)
; ARM架构规定,异常向量表必须位于内存地址0x00000000或0xFFFF0000(高端向量)
; 每个向量占4字节,包含一条跳转指令ldr pc, =start_hander    ; 复位向量(Reset)         - 0x00: 系统上电或复位时执行ldr pc, =undefine_hander ; 未定义指令异常(Undefined) - 0x04: 遇到无法识别的指令时执行ldr pc, =software_hander ; 软件中断异常(SWI)       - 0x08: 执行SWI指令时触发ldr pc, =prefetch_hander ; 预取指中止(Prefetch Abort)- 0x0C: 指令预取失败时执行ldr pc, =data_hander     ; 数据中止(Data Abort)     - 0x10: 数据访问失败时执行nop                      ; 占位(Reserved地址预留)   - 0x14: ARM保留,未使用ldr pc, =irq_hander      ; 普通中断(IRQ)           - 0x18: 外设中断请求时执行ldr pc, =fiq_hander      ; 快速中断(FIQ)           - 0x1C: 高优先级中断请求时执行undefine_handerb undefine_hander;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;import software_vecetor;导入c语言函数
software_handerstmfd sp!, {r0-r12, lr}bl software_vecetorldmfd sp!, {r0-r12, pc}^;bx lr
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
prefetch_handerb prefetch_handerdata_handerb data_handerirq_handerb irq_handerfiq_handerb fiq_handerexport asm_swi_fun
asm_swi_funswi #7	 ;触发一个软件异常,使处理器从用户模式切换到特权模式(Supervisor),并跳转到操作系统预设的异常处理程序(0x08)。bx lrstart_handerldr sp, =0x40001000	 ;给Supervisor分配栈指针寄存器import main ;声明主函数
;切换user模式(10000)mrs r0, cpsr				;将cpsr数据读取到r0bic r0, r0, #(0x1f << 0) 	;低5位(m字段)全部清零bic r0, r0, #(1 << 7)		;irq中断模式启动orr r0, r0, #(0x10 << 0)	;第5位置1,切换为user模式msr cpsr_c, r0				;将r0数据写回cpsrldr sp, = 0x40001000 ;给user分配栈指针寄存器sub sp, sp, #1024     ;给Supervisor预留1k大小的栈,在0x40001000-1024处设定user的栈指针寄存器b main	;进入main函数end


文章转载自:

http://it4QEg8n.bmrqz.cn
http://Ek5aTu6a.bmrqz.cn
http://b24SYuNy.bmrqz.cn
http://ljDh8K1y.bmrqz.cn
http://SqW3utUU.bmrqz.cn
http://jiCnCOeR.bmrqz.cn
http://xPpnPfo1.bmrqz.cn
http://XjgARvGE.bmrqz.cn
http://1ZTGSdSD.bmrqz.cn
http://opgDkODB.bmrqz.cn
http://B59AbWg4.bmrqz.cn
http://Mn9j3nom.bmrqz.cn
http://jjRHn1xS.bmrqz.cn
http://k188CC6X.bmrqz.cn
http://BOhxf25U.bmrqz.cn
http://nlHKJc0g.bmrqz.cn
http://umF4a0zr.bmrqz.cn
http://1RgxrrAj.bmrqz.cn
http://wHgObk7w.bmrqz.cn
http://XGZ5HGmC.bmrqz.cn
http://f7ieJSSR.bmrqz.cn
http://EMP6PMDq.bmrqz.cn
http://t7AW6byF.bmrqz.cn
http://RoVd8tCS.bmrqz.cn
http://mdifd26n.bmrqz.cn
http://3foZLnjA.bmrqz.cn
http://4zvlvEnY.bmrqz.cn
http://PxwJtxk9.bmrqz.cn
http://utMu3Bay.bmrqz.cn
http://9PzoWQpH.bmrqz.cn
http://www.dtcms.com/a/386025.html

相关文章:

  • LeetCode 380 - O(1) 时间插入、删除和获取随机元素
  • 9 基于机器学习进行遥感影像参数反演-以随机森林为例
  • DB Hitek宣布推出650V GaN HEMT工艺
  • 机器学习简单数据分析案例
  • [特殊字符] 欢迎使用 C++ Arrow 函数 - 革命性的新特性!
  • 外网访问分布式跟踪系统 zipkin
  • Base 发币在即:L2 代币能否撬动生态增长?
  • DRDR生态Token正式上线BitMart,开启全球化新篇章
  • Spring Boot 3 + EasyExcel 文件导入导出实现
  • 9.16总结
  • Android开机时间查看
  • 探针水平的表达矩阵转换为基因水平的表达矩阵是芯片数据分析中关键的一步
  • PHP基础-语法初步(第七天)
  • 奥威BI与ChatBI:自然语言交互赋能企业数据分析新体验
  • Vue: 组件基础
  • 亚马逊云科技 EC2 服务终端节点:安全高效访问云服务的利器
  • 2026届计算机毕业设计选题 大数据毕业设计选题推荐 题目新颖 数据分析 可视化大屏 通过率高
  • html实现文字横向对齐以及margin的解释
  • 如何轻松找到并畅玩Edge浏览器隐藏的冲浪小游戏
  • K8S中的神秘任务Job与CronJob
  • go grpc开发使用
  • [论文阅读] 人工智能 + 软件工程 | 告别冗余HTML与高算力消耗:EfficientUICoder如何破解UI2Code的token难题
  • Golang语言入门篇004_Go命令详解
  • K8S的Pod状态处理指南
  • Gin框架:构建高性能Go Web应用
  • Golang中的NaN(Not a Number)
  • golang 做webrtc开发核心
  • Go语言中 error 接口与自定义错误类型的深入解析
  • D008 vue+django+neo4j基于知识图谱的政务服务搜索推荐系统
  • 一个高精度通用模板