目录
问题环境
IDE:Keil MDK
编程语言:C语言、汇编语言
硬件:STM32单片机
问题1描述
之前在写BootLoader的时候,参考了网上的设置栈顶地址函数,不是很理解这个函数,所以先记录下来,后面有时间在看看。
//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(uint32_t addr)
{
MSR MSP, r0 //set Main Stack value
BX r14
}
问题1分解回答
1、__asm 是什么?
答:__asm 是表示汇编的关键字,告诉编译器接下来的代码是汇编代码。
2、addr为什么在函数内部没有用,但实际的地址确成功设置了?
答:实际上 addr 的值会默认存储到 r0 中;
特意写一个函数测试,这个函数就是查看各个变量的存储位置
函数如下:
void test_input(uint8_t a,//第1个变量
uint8_t b,//第2个变量
uint8_t c,//第3个变量
uint8_t d,//第4个变量
uint8_t e,//第5个变量
uint8_t f,//第6个变量
uint8_t g,//第7个变量
uint8_t h,//第8个变量
uint8_t i,//第9个变量
uint8_t j,//第10个变量
uint8_t k,//第11个变量
uint8_t l,//第12个变量
uint8_t m,//第13个变量
uint8_t n)//第14个变量
{
uint8_t sum = 1;
sum =a;
sum =b;
sum =c;
sum =d;
sum =e;
sum =f;
sum =g;
sum =h;
sum =i;
sum =j;
sum =k;
sum =l;
sum =m;
sum +=n;
}
调用如下:
test_input(1,2,3,4,5,6,7,1,1,1,1,1,1,1);
编译后得到汇编指令如下:
174: {
0x080017B0 B5F0 PUSH {r4-r7,lr}
0x080017B2 9D05 LDR r5,[sp,#0x14]
0x080017B4 9C0D LDR r4,[sp,#0x34]
175: uint8_t sum = 1;
176:
0x080017B6 2601 MOVS r6,#0x01
177: sum =a;
0x080017B8 4606 MOV r6,r0
178: sum =b;
0x080017BA 460E MOV r6,r1
179: sum =c;
0x080017BC 4616 MOV r6,r2
180: sum =d;
0x080017BE 461E MOV r6,r3
181: sum =e;
0x080017C0 462E MOV r6,r5
182: sum =f;
0x080017C2 9E06 LDR r6,[sp,#0x18]
183: sum =g;
0x080017C4 9E07 LDR r6,[sp,#0x1C]
184: sum =h;
0x080017C6 9E08 LDR r6,[sp,#0x20]
185: sum =i;
0x080017C8 9E09 LDR r6,[sp,#0x24]
186: sum =j;
0x080017CA 9E0A LDR r6,[sp,#0x28]
187: sum =k;
0x080017CC 9E0B LDR r6,[sp,#0x2C]
188: sum =l;
0x080017CE 9E0C LDR r6,[sp,#0x30]
189: sum =m;
0x080017D0 4626 MOV r6,r4
190: sum +=n;
191:
0x080017D2 9F0E LDR r7,[sp,#0x38]
0x080017D4 19F7 ADDS r7,r6,r7
0x080017D6 B2FE UXTB r6,r7
192: }
在汇编中的175行中,可以看出 sum 的值是被保存在 r6 中
175: uint8_t sum = 1;
176:
0x080017B6 2601 MOVS r6,#0x01
在sum =a; 这条C语言的执行就是通过汇编中的MOV指令将r0 的值传给r6,由此可以判断出 addr 的值是存储在 r0 中;
MOV命令是将数据移动到另一个地方,使用格式如下:
MOV 目标地址 源地址
目标地址:除CS、IP以外的寄存器或存储器;
源地址:寄存器、存储器、立即数;
177: sum =a;
0x080017B8 4606 MOV r6,r0
进一步拓展可以看到上面写的test_input 函数的第2个变量 b 是保存在 r1 中
178: sum =b;
0x080017BA 460E MOV r6,r1
3、变量addr 和 r0 是什么关系?
答:变量 addr 的值保存在 r0 中。
4、MSR MSP, r0 这条汇编语句是什么作用?
答:MSR 的作用是将通用寄存器的值传给状态寄存器,也就是将 r0 的值传给 MSP,而MSP是主堆栈指针,也就是说,将 r0 存的值传给主堆栈指针,实现设置栈顶地址。
5、BX r14 这条汇编语句是什么作用?
答:R14称为子程序链接寄存器,将子程序或是函数调用执行完成后 ,通过BX r14返回到函数调用处
问题2描述
在分析问题1的过程中,发现还有另一种写法。
/**
\brief Set Main Stack Pointer
\details Assigns the given value to the Main Stack Pointer (MSP).
\param [in] topOfMainStack Main Stack Pointer value to set
*/
__STATIC_FORCEINLINE void __set_MSP(uint32_t topOfMainStack)
{
__ASM volatile ("MSR msp, %0" : : "r" (topOfMainStack) : );
}
问题2分解回答
1、这种汇编语法是什么?
答:这种是属于AT&T 汇编语法,汇编在系统上分为Unix(主要是AT&T)和Windows(主要是Intel)两种派系格式,而问题2是Uinx系统的AT&T汇编的格式,而问题1是windows的Intel汇编格式。
具体可以参考这个大哥写这篇文章:
Intel汇编和AT&T汇编的简要区别,生成方法和判断技巧
或者看这篇
intel 汇编和at&t 汇编的区别
2、“MSR msp, %0” : : “r” (topOfMainStack) : 表示什么意思?
答:这个要从AT&T 汇编格式说起,不过这里可以简单说一下这个是一条汇编语句,这条语句是把变量 topOfMainStack 传进 MSP 实现设置栈顶地址。
具体详细说明可以看:AT&T_GCC_ASM.pdf 这个文档(百度一下有很多地方可以看的到)
百度文档:AT&T_GCC_ASM.pdf
问题拓展
问题1和问题2的函数都是同一个作用,但是不同的写法,就需要选择不同的编译器,
问题1对应的是keil-mdk 的编译器5 的写法
问题2对应的是keil-mdk 的编译器6 的写法
免责声明:本文内容含网络参考、作者编写等,内容版权归原作者所有,未经允许,禁止转载。如涉及作品版权问题,请与我们联系,我们将根据您提供的版权证明材料确认版权并支付稿酬或者删除内容。