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.
^- David Wohlferd