Pergunta

I'm writing a sdram test program on a bare-metal ARM. I wrote it in C, but now I want to modify the generated assembly to prevent the program from using sdram, which implies, among other things, no stack.

I started to learn ARM assembly recently, and I don't understand how the compiler generated assembly is using the stack in the following code (and I don't find the answer by reading the ARM ARM :/). The 32 bits variable value is placed on the stack, but why does push reserves 3 times 32 bits at the beginning of the function? Can someone please explain the stack manipulation here?

The C code:

/* 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);
 }

The generated assembly (from gcc-arm-elf):

     <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
Foi útil?

Solução

The push is to save the registers r4, r5, and r6 all of which must be preserved according to the ARM programming model. The push of lr is to preserve the return address as you are calling other functions that will modify it. The sub 8 from the stack reserves another 8 bytes of memory for other variable use (value variable) - used later on the str r3, [2, #-4] line. In addition, the Branch Link bl to phy_read and phy_write could also be modifying stack space so your problem with stack memory might be bigger than you think. Also, your last pop comment about 4 bytes is incorrect - it is 16-bytes of space being freed.

Now, what kind of resources will you have available RAM-wise to use? You need something or else your unsigned int value won't have any place to work from not to mention your calls. You have to have something available. If you do, you can tell your C program about it through linker scripts and section directives, saving you the trouble of assembler.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top