0
votes

I'm currently developing a simple ARM firmware. Among them, I'm making a part related to interrupts. I connected the IRQ exception vector in the exception vector table with the interrupt handler in the interrupt controller as follows:

__attribute__ ((interrupt ("IRQ"))) void Irq_Handler(void) 
{
   interrupt_handler();
}

And it converted to ARM assembly code:

sub    lr, lr, #4
push   {r0, r1, r2, r3, r4, fp, ip, lr}
add    fp, sp, #28
bl     0 <interrupt_hanlder>
sub    sp, fp, #28
ldm    sp!, {r0, r1, r2, r3, r4, fp, ip, pc}^

I have several questions for this assembly code:

  1. Why it pushes registers "r0, r1, r2, r3, r4, fp, ip, lr" to the stack?
  2. It pushes 8 registers to the stack. But why it subtracts not 32(4*8) but 28(4*7)?
  3. I don't know the meaning of '^' at the end of 6th line.

Thanks!

1
1. because those registers are call-clobbered, but this is an interrupt handler that has to preserve (almost?) all registers. 2. notice that it's setting up a frame pointer (I assume this is without optimization), not reserving more stack space. I think It's normal it points to one of the saved registers. 3. IDK. - Peter Cordes
Have you checked out the LDM docs? It talks about ^ - David Wohlferd

1 Answers

2
votes

Why it pushes registers "r0, r1, r2, r3, r4, fp, ip, lr" to the stack?

This is done because these registers may be modified by the function interrupt_handler(). An interrupt interrupts a running program anywhere. For example here:

ldm r0, {r2, r3}
                 <=== The interrupt interrupts the program here
add r1, r2, r3

If the interrupt modified some registers (in the example: r2 or r3), the program which has been interrupted would not be running correctly anymore.

For this reason the interrupt must restore all registers to their original values.

The function interrupt_handler() will not modify registers r5, r6, ..., so the interrupt handler does not need to restore these registers.

It pushes 8 registers to the stack. But why it subtracts not 32(4*8) but 28(4*7)?

For some reason the fp register shall contain the value sp+28. Maybe it shall point to the stored value of the lr register.

The instruction sub sp, fp, #28 will restore the value of sp assuming that the value of fp did not change since the instruction add fp, sp, #28.

In your case, sp did not change, so the instruction is redundant.

I don't know the meaning of ^ at the end of 6th line.

The ^ is used for the ldm and stm instructions and has two different effects.

One of the two effects is that loading the pc register will also load the psr register.

To return from an interrupt, you must restore the psr register.

Note that in early ARM CPUs, the register r15 contained both the pc register (which was only 24 bits wide) and the psr register (which was 8 bits wide). A ldm without ^ would keep the 8 psr bits of the register r15 unchanged while using ^ would load all 32 bits from memory.

In current ARM CPUs this is no longer the case. However, using the ^ still restores the value of the psr register when loading the pc using ldm.