Assembly 理解汇编堆栈操作

Assembly 理解汇编堆栈操作,assembly,arm,bare-metal,Assembly,Arm,Bare Metal,我正在一个裸露的金属臂上写一个sdram测试程序。我用C写的,但现在我想修改生成的程序集,以防止程序使用sdram,这意味着,除其他外,没有堆栈 我最近开始学习ARM汇编,我不明白编译器生成的汇编是如何在以下代码中使用堆栈的(通过阅读ARM,我没有找到答案:/)。32位变量值放在堆栈上,但为什么在函数开始时push保留3倍32位?有人能解释一下这里的堆栈操作吗 C代码: /* ugly to have it as global but it reduces stack usage*/

我正在一个裸露的金属臂上写一个sdram测试程序。我用C写的,但现在我想修改生成的程序集,以防止程序使用sdram,这意味着,除其他外,没有堆栈

我最近开始学习ARM汇编,我不明白编译器生成的汇编是如何在以下代码中使用堆栈的(通过阅读ARM,我没有找到答案:/)。32位变量值放在堆栈上,但为什么在函数开始时push保留3倍32位?有人能解释一下这里的堆栈操作吗

C代码:

/* ugly to have it as global but it reduces stack usage*/                                              
unsigned int const led_port[]= {0,0,1,1,2,2,3,3,4,4};
unsigned int const led_value_on[]={0x90,0x9,0x90,0x9,0x90,0x9,0x90,0x9,0x90,0x9};                      
unsigned int const masks[] = {0xf0,0xf,0xf0,0xf,0xf0,0xf,0xf0,0xf,0xf0,0xf};                           
unsigned int const led_value_off[]={0x80,0x8,0x80,0x8,0x80,0x8,0x80,0x8,0x80,0x8};                     

 void gbe_led_on(int i)
 {                        
         unsigned int value = 0;                                                                                
         phy_read(led_port[i], 0x10, &value);                                                                   
         value &= ~masks[i];
         value |= led_value_on[i];                                                                              
         phy_write(led_port[i], 0x10, value);
 }
生成的程序集(来自gcc arm elf):

:
推动{r4,r5,r6,lr}/*;在堆栈上为3个32位变量+返回地址保留空间*/
ldr r5,[pc,#84];ffff1578/*r5=led_端口(阵列基址)*/
子sp,sp,#8/*sp=sp-8(十进制8)它指向什么*/
ldr r4、[r5、r0、lsl#2]/*r4=*(led#U端口+i)和0x00ff(从16位移位)*/
加上r2,sp,#8/*r2=sp+8(十进制8)为什么*/
mov r6,r0/*r6=i*/
mov r3,#0/*r3=0*/
mov r0,r4/*r0=led_端口[i]*/
str r3,[r2,#-4]!/*r3=*(sp+8-4);更新r2,到哪个值*/
添加r5、r5、r6、lsl#2/*r5=led#U端口[i]&0x00ff*/
mov r1,#16/*r1=16(十进制)*/
bl ffff13f8/*使用r0、r1、r2上的参数调用phy_read*/
ldr r1,[r5,#40];0x28/*r1=掩码[i]*/
ldr r3,[sp,#4]/*r3=*(sp+4)*/
ldr r2,[r5,#120];0x78/*r2=led灯亮起[i]*/
bic r3,r3,r1/*值&=掩码[i]*/
orr r3,r3,r2/*值|=发光二极管_值_在[i]上*/
mov r0,r4/*r0=led_端口[i]*/
mov r2,r3/*r2=值*/
mov r1,#16/*r1=16*/
str r3,[sp,#4]/**(sp+4)=值;我们为什么要这样做??*/
bl ffff13cc/*使用r0、r1、r2上的参数分支到phy_写入*/
在pop之前添加sp、sp、#8/*sp=sp+8还原堆栈指针*/
pop{r4,r5,r6,pc}/*从堆栈和分支中删除4个字节以返回地址*/
.word 0xffff1a30

按钮用于保存寄存器
r4
r5
r6
,所有这些寄存器都必须根据ARM编程模型进行保存。轻推
lr
是为了在调用将修改返回地址的其他函数时保留返回地址。堆栈中的
sub 8
为其他变量使用(
value
variable)保留另外8个字节的内存-稍后在
str r3[2,#-4]
行中使用。此外,到
phy\u read
phy\u write
的分支链接
bl
也可能修改堆栈空间,因此堆栈内存问题可能比您想象的更大。另外,您上次关于4个字节的pop注释是不正确的-它释放了16个字节的空间


现在,您将有哪些可用的RAM资源可供使用?您需要一些东西,否则您的
无符号int值
将没有任何工作空间,更不用说您的调用了。你必须有可用的东西。如果您这样做,您可以通过链接器脚本和
部分
指令告诉您的C程序,省去了汇编程序的麻烦。

phy\u read不使用堆栈(启用内联),是我程序中最深的函数。除了寄存器,我没有任何其他RAM-wise ressource,这就是问题所在:/I计划在修改程序集后删除内联和声明裸函数的函数调用。这是一个有浮点可用的ARM芯片吗?如果是这样,如果需要,您可以将这些寄存器用作“工作RAM”。当然,您是在ARM模式下编程,而不是在Thumb模式下编程。最后,在用户和主管模式之间切换时,可以使用一组寄存器。考虑到所有这些,您可能可以在没有RAM的情况下完成这项工作。没有可用的浮点,但我在寄存器列表中找到了4个32位的“通用读/写寄存器”(配置寄存器,而不是“CPU寄存器”),我可以使用。是的,我在ARM模式下编程。所以内联你的LED函数,把你的u_int值标记为寄存器,然后编译。您将拥有输出所需的90%。然后,只需移除r4-r11推送/持久性有机污染物,并在其位置使用寄存器即可。祝你好运
     <gbe_led_off>:
push    {r4, r5, r6, lr}        /* ;reserve space on the stack for 3 32 bits variables + return address */
ldr     r5, [pc, #84]   ; ffff1578 <gbe_led_off+0x60>  /*r5=led_port (array base address) */
sub     sp, sp, #8              /* sp = sp-8 (decimal 8) what does it point to??*/
ldr     r4, [r5, r0, lsl #2]    /* r4 = *(led_port+i)&0x00ff, (shift from 16 bits) */
add     r2, sp, #8              /* r2 = sp+8 (decimal 8) why???*/
mov     r6, r0                  /* r6 = i */
mov     r3, #0                  /* r3 = 0 */
mov     r0, r4                  /* r0 = led_port[i]*/
str     r3, [r2, #-4]!          /* r3 = *(sp+8-4); update r2, to which value???*/
add     r5, r5, r6, lsl #2      /* r5 = led_port[i] & 0x00ff */
mov     r1, #16                 /* r1 = 16 (decimal) */
bl      ffff13f8 <phy_read>     /* call phy_read with arguments on r0, r1, r2*/
ldr     r1, [r5, #40]   ; 0x28  /* r1 = masks[i] */
ldr     r3, [sp, #4]            /* r3 = *(sp+4) ????*/
ldr     r2, [r5, #120]  ; 0x78  /* r2 = led_value_on[i] */
bic     r3, r3, r1              /* value &= masks[i] */
orr     r3, r3, r2              /* value |= led_value_on[i] */
mov     r0, r4                  /* r0 = led_port[i] */
mov     r2, r3                  /* r2 = value  */
mov     r1, #16                 /* r1 = 16    */  
str     r3, [sp, #4]            /* *(sp+4) = value; why do we do that???*/             
bl      ffff13cc <phy_write>    /* branch to phy_write with arguments on r0,r1,r2*/
add     sp, sp, #8              /* sp = sp+8 restore stack pointer before pop? */
pop     {r4, r5, r6, pc}        /* remove 4 bytes from the stack and branch to return address */
.word   0xffff1a30