| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 嵌入式 -> [007] [ARM-Cortex-M3/4] C与汇编深入分析 -> 正文阅读 |
|
[嵌入式][007] [ARM-Cortex-M3/4] C与汇编深入分析 |
ARM
Cortex-M3/4
子程序(函数)
调用规则
C函数的反汇
编代码分析
Flash中烧录的内
容与启动流程
Flash中烧录的内容
启动流程
纯汇编点灯
|
寄存器 | 别名 | 特殊名称 | 使用规则 |
---|---|---|---|
r15 | PC | 程序计数器 | |
r14 | LR | 连接寄存器 | |
r13 | SP | 数据栈指针 | |
r12 | IP | 子程序间调用的暂存寄存器scratch register. | |
r11 | v8 | FP | ARM状态局部变量寄存器8 / 栈帧指针 |
r10 | v7 | SL | ARM状态局部变量寄存器7 在支持数据栈检查的ATPCS中为数据栈限制指针 |
r9 | v6 | SB | ARM状态局部变量寄存器6 在支持RWPI的ATPCS中为静态基址寄存器 |
r8 | v5 | ARM状态局部变量寄存器5 | |
r7 | v4 | WR | 局部变量寄存器4 Thumb状态工作寄存器 |
r6 | v3 | 局部变量寄存器3 | |
r5 | v2 | 局部变量寄存器2 | |
r4 | v1 | 局部变量寄存器1 | |
r3 | a4 | 参数/结果/暂存寄存器scratch register 4 | |
r2 | a3 | 参数/结果/暂存寄存器scratch register 3 | |
r1 | a2 | 参数/结果/暂存寄存器scratch register 2 | |
r0 | a1 | 参数/结果/暂存寄存器scratch register 1 |
传递参数
,被调用的子程序在返回前无需恢复寄存器r0~r3的内容。局部变量
,若被调程序使用了其中的某些寄存器,则在进入子程序时必须保存它们的值,在返回前必须恢复这些寄存器的值。在Thumb程序中,只能使用r4~r7。keil软件会对名为main
的函数添加很多额外的汇编代码(与链接脚本有关),为了简化分析,将main
修改为mymain
。
start.s
PRESERVE8
THUMB
; Vector Table Mapped to Address 0 at Reset
AREA RESET, DATA, READONLY
EXPORT __Vectors
__Vectors DCD 0
DCD Reset_Handler ; Reset Handler
AREA |.text|, CODE, READONLY
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT mymain ; 修改为mymian
LDR SP, =(0x20000000+0x10000)
BL mymain
ENDP
END
程序烧录到flash中的即为机器码。
在启动文件使用BL
指令跳转到mymain
函数:
; pReg = (unsigned int *)(0x40021000 + 0x18);
LDR r3,[pc,#52] ; pc+52=0x8000058, 将该地址的中的内容0x40021018加载到 r3 (r3即为指针pReg)
; *pReg |= (1<<3);
LDR r0,[r3,#0] ; r0 = *r3 (*即为解引用, 表示取r3地址中的值)
ORR r0,r0,#8 ; r0 |= (1 << 3)
STR r0,[r3,#0] ; *r3 = r0
; pReg = (unsigned int *)(0x40010C00 + 0x00);
LDR r3,[pc,#48] ; 从0x0800005c内存地址读出0x40010c00加载到r3
; *pReg |= (1<<0);
LDR r0,[r3,#0] ; r0 = *r3
ORR r0,r0,#1 ; r0 |= 1
STR r0,[r3,#0] ; *r3 = r0
; pReg = (unsigned int *)(0x40010C00 + 0x0C);
LDR r3,[pc,#36] ; r3 = 0x40010c00
ADDS r3,r3,#0xc ; r3 += 0xc, S表示需要修改CPSR状态寄存器
0x08000038: B 0x8000056 ; 跳转到最后一句0x8000056处
; *pReg |= (1<<0);
0x0800003a: LDR r0,[r3,#0] ; r0 = *r3 = *(0x40010c00 + 0xc)
0x0800003c: ORR r0,r0,#1 ; r0 |= (1 << 0)
0x08000040: STR r0,[r3,#0] ; *r3 = r0
; delay(100000);
0x08000042: LDR r0,[pc,#28] ; r0 = *0x08000060 = 0x186a0 = 100000, 根据ATPCS规则r0保存delay函数的入口参数
0x08000044: BL delay ; 跳转到0x8000014处的delay函数↓, 并将下一条指令的地址0x08000048加载到LR
; *pReg &= ~(1<<0);
0x08000048: LDR r0,[r3,#0] ; r0 = *r3 = *(0x40010c00 + 0xc)
0x0800004a: BIC r0,r0,#1 ; r0 &= ~(1 << 0)
0x0800004e: STR r0,[r3,#0] ; *r3 = r0
; delay(100000);
0x08000050: LDR r0,[pc,#12] ; r0 = 0x186a0 = 100000
0x08000052: BL delay ; 跳转到0x8000014处的delay函数↓
0x08000056: B 0x800003a ; 跳回到第二句0x800003a处
; 以下是为一些常量分配的地址与值, 不属于while部分
0x08000058: 40021018 DCD 1073877016
0x0800005c: 40010c00 DCD 1073810432
0x08000060: 000186a0 DCD 100000
0x08000014: NOP ; 空操作伪指令,用于延时
0x08000016: SUBS r1,r0,#0 ; r1 = r0 - 0, S表示会更新cpsr
0x08000018: SUB r0,r0,#1 ; r0 -= 1
0x0800001c: BNE 0x8000016 ; not equal(Z=0)执行,即若r0不为0, 跳转到0x8000016处继续执行
0x0800001e: BX lr ; 调转到 while循环 中BL指令的下一条指令处继续执行
在start.s
中设置了栈LDR SP, =(0x20000000+0x10000)
,为体现函数栈的调用,将delay函数变种(加volatile
):
int delay(volatile int d)
{
while(d--);
return 0x55;
}
delay
0x08000014: PUSH {r0,lr} ; 将1r压入到sp-4, r0压入到sp-8, sp = sp - 8
0x08000016: NOP
; 因为加了volatile(从栈中读r0到r1 -> 修改r1-> 将r1再写回栈中r0
0x08000018: LDR r0,[sp,#0] ; r0 = sp, sp存储原r0
0x0800001a: SUBS r1,r0,#1 ; r1 = r0 - 1, 并改变cpsr
0x0800001c: STR r1,[sp,#0] ; *sp = r1, 即*r0 = r1
0x0800001e: CMP r0,#0 ; 比较r0是否为0
0x08000020: BNE 0x8000018 ; 如果r0不为0, 跳转到0x8000018处
0x08000022: MOVS r0,#0x55 ; 用r0保存函数返回值0x55
0x08000024: POP {r3,pc} ; r3 = r0, pc = lr 跳转
0x08000026: MOVS r0,r0 ; 无用语句->字节对齐
不加volatile
:
0x08000016: SUBS r1,r0,#0 ; r1 = r0 - 0, S表示会更新cpsr
0x08000018: SUB r0,r0,#1 ; r0 -= 1
加volatile
:
0x08000018: LDR r0,[sp,#0] ; r0 = sp, sp存储原r0
0x0800001a: SUBS r1,r0,#1 ; r1 = r0 - 1, 并改变cpsr
0x0800001c: STR r1,[sp,#0] ; *sp = r1, sp存储原r0, 即*r0 = r1
反汇编文件的框选部分即为机器码,将其烧录到对应的flash地址上(四字节对齐):
地址 | Flash内容 |
---|---|
0x08000000 | 00000000 |
0x08000004 | 08000009 |
0x08000008 | f8dfd004 |
0x0800000c | f000f808 |
0x08000010 | 20010000 |
0x08000014 | bf001e10 |
0x08000018 | f1a00001 |
…… | …… |
分散加载文件中指定了flash起始地址为0x80000000:
LR_IROM1 0x08000000 0x00080000 { ; load region size_region
ER_IROM1 0x08000000 0x00080000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
.ANY (+XO)
}
RW_IRAM1 0x20000000 0x00010000 { ; RW data
.ANY (+RW +ZI)
}
}
0x08000009
,即PC=0x08000009
,PC的LSB为1表示Thumb状态(0为ARM)
Reset_Handler
(0x08000008地址处)复位中断服务例程中执行引脚PA8
PRESERVE8
THUMB
; Vector Table Mapped to Address 0 at Reset
AREA RESET, DATA, READONLY
EXPORT __Vectors
__Vectors DCD 0
DCD Reset_Handler ; Reset Handler
AREA |.text|, CODE, READONLY
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
DEALY_TIME EQU 500000 ; 定义延时时间常量
; 使能GPIOA时钟, RCC寄存器基地址0x4002 1000, 0x40021000 + 0x18为RCC->RCC_APB2ENR
LDR R0, =(0x40021000 + 0x18)
LDR R1, [R0]
ORR R1, R1, #(1<<2)
STR R1, [R0]
; 配置CRH寄存器, 设置PA8为推挽输出, GPIOA基地址0x4001 0800, 0x40010800 + 0x04为GPIOA->CRH
LDR R0, =(0x40010800 + 0x04)
LDR R1, [R0]
BIC R1, R1, #0xf ; 先清除
ORR R1, R1, #0x3 ; 推挽输出, 50mhz
STR R1, [R0]
; 操作ODR寄存器控制GPIO电平, 0x40010800 + 0x0C为GPIOA->ODR)
LDR R0, =(0x40010800 + 0x0C)
LOOP
; 拉高
LDR R1, [R0]
ORR R1, R1, #(1<<8)
STR R1, [R0]
; 延时
LDR R2, =DEALY_TIME
BL DELAY
; 拉低
LDR R1, [R0]
BIC R1, R1, #(1<<8)
STR R1, [R0]
; 延时
LDR R2, =DEALY_TIME
BL DELAY
B LOOP
ENDP
DELAY
SUBS R2, R2, #1
NOP ; vb字节对齐
BNE DELAY
BX LR ; MOV PC, LR
END
实验现象:PA8红色LED正常闪烁。
END
|
嵌入式 最新文章 |
基于高精度单片机开发红外测温仪方案 |
89C51单片机与DAC0832 |
基于51单片机宠物自动投料喂食器控制系统仿 |
《痞子衡嵌入式半月刊》 第 68 期 |
多思计组实验实验七 简单模型机实验 |
CSC7720 |
启明智显分享| ESP32学习笔记参考--PWM(脉冲 |
STM32初探 |
STM32 总结 |
【STM32】CubeMX例程四---定时器中断(附工 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/26 6:53:48- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |