, поскольку у меня есть удобный симулятор ...
.thumb
.globl _start
_start:
.word 0x20001000
.word reset
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.thumb_func
reset:
mov r0,#5
bl test
b hang
.thumb_func
hang:
swi 0xFF
b hang
test:
cmp r0,#0
bne test1
bx lr
test1:
sub r0,#1
push {r3,lr}
bl test
pop {r3,pc}
build
08000000 <_start>:
8000000: 20001000 andcs r1, r0, r0
8000004: 08000021 stmdaeq r0, {r0, r5}
8000008: 08000029 stmdaeq r0, {r0, r3, r5}
800000c: 08000029 stmdaeq r0, {r0, r3, r5}
8000010: 08000029 stmdaeq r0, {r0, r3, r5}
8000014: 08000029 stmdaeq r0, {r0, r3, r5}
8000018: 08000029 stmdaeq r0, {r0, r3, r5}
800001c: 08000029 stmdaeq r0, {r0, r3, r5}
08000020 <reset>:
8000020: 2005 movs r0, #5
8000022: f000 f803 bl 800002c <test>
8000026: e7ff b.n 8000028 <hang>
08000028 <hang>:
8000028: dfff svc 255 ; 0xff
800002a: e7fd b.n 8000028 <hang>
0800002c <test>:
800002c: 2800 cmp r0, #0
800002e: d100 bne.n 8000032 <test1>
8000030: 4770 bx lr
08000032 <test1>:
8000032: 3801 subs r0, #1
8000034: b508 push {r3, lr}
8000036: f7ff fff9 bl 800002c <test>
800003a: bd08 pop {r3, pc}
и запускаю его, показывая разборку в порядке выполнения и доступ к памяти.
--- 0x08000020: 0x2005 movs r0,#0x05
--- 0x08000022: 0xF000
--- 0x08000024: 0xF803 bl 0x0800002B
--- 0x0800002C: 0x2800 cmp r0,#0x00
--- 0x0800002E: 0xD100 bne 0x08000031
--- 0x08000032: 0x3801 subs r0,#0x01
--- 0x08000034: 0xB508 push {r3,lr}
write16(0x20000FF8,0x0000)
write16(0x20000FFA,0x0000)
write16(0x20000FFC,0x0027)
write16(0x20000FFE,0x0800)
--- 0x08000036: 0xF7FF
--- 0x08000038: 0xFFF9 bl 0x0800002B
--- 0x0800002C: 0x2800 cmp r0,#0x00
--- 0x0800002E: 0xD100 bne 0x08000031
--- 0x08000032: 0x3801 subs r0,#0x01
--- 0x08000034: 0xB508 push {r3,lr}
write16(0x20000FF0,0x0000)
write16(0x20000FF2,0x0000)
write16(0x20000FF4,0x003B)
write16(0x20000FF6,0x0800)
--- 0x08000036: 0xF7FF
--- 0x08000038: 0xFFF9 bl 0x0800002B
--- 0x0800002C: 0x2800 cmp r0,#0x00
--- 0x0800002E: 0xD100 bne 0x08000031
--- 0x08000032: 0x3801 subs r0,#0x01
--- 0x08000034: 0xB508 push {r3,lr}
write16(0x20000FE8,0x0000)
write16(0x20000FEA,0x0000)
write16(0x20000FEC,0x003B)
write16(0x20000FEE,0x0800)
--- 0x08000036: 0xF7FF
--- 0x08000038: 0xFFF9 bl 0x0800002B
--- 0x0800002C: 0x2800 cmp r0,#0x00
--- 0x0800002E: 0xD100 bne 0x08000031
--- 0x08000032: 0x3801 subs r0,#0x01
--- 0x08000034: 0xB508 push {r3,lr}
write16(0x20000FE0,0x0000)
write16(0x20000FE2,0x0000)
write16(0x20000FE4,0x003B)
write16(0x20000FE6,0x0800)
--- 0x08000036: 0xF7FF
--- 0x08000038: 0xFFF9 bl 0x0800002B
--- 0x0800002C: 0x2800 cmp r0,#0x00
--- 0x0800002E: 0xD100 bne 0x08000031
--- 0x08000032: 0x3801 subs r0,#0x01
--- 0x08000034: 0xB508 push {r3,lr}
write16(0x20000FD8,0x0000)
write16(0x20000FDA,0x0000)
write16(0x20000FDC,0x003B)
write16(0x20000FDE,0x0800)
--- 0x08000036: 0xF7FF
--- 0x08000038: 0xFFF9 bl 0x0800002B
--- 0x0800002C: 0x2800 cmp r0,#0x00
--- 0x0800002E: 0xD100 bne 0x08000031
--- 0x08000030: 0x4770 bx r14
--- 0x0800003A: 0xBD08 pop {r3,pc}
read16(0x20000FD8)=0x0000
read16(0x20000FDA)=0x0000
read16(0x20000FDC)=0x003B
read16(0x20000FDE)=0x0800
--- 0x0800003A: 0xBD08 pop {r3,pc}
read16(0x20000FE0)=0x0000
read16(0x20000FE2)=0x0000
read16(0x20000FE4)=0x003B
read16(0x20000FE6)=0x0800
--- 0x0800003A: 0xBD08 pop {r3,pc}
read16(0x20000FE8)=0x0000
read16(0x20000FEA)=0x0000
read16(0x20000FEC)=0x003B
read16(0x20000FEE)=0x0800
--- 0x0800003A: 0xBD08 pop {r3,pc}
read16(0x20000FF0)=0x0000
read16(0x20000FF2)=0x0000
read16(0x20000FF4)=0x003B
read16(0x20000FF6)=0x0800
--- 0x0800003A: 0xBD08 pop {r3,pc}
read16(0x20000FF8)=0x0000
read16(0x20000FFA)=0x0000
read16(0x20000FFC)=0x0027
read16(0x20000FFE)=0x0800
--- 0x08000026: 0xE7FF B 0x08000027
--- 0x08000028: 0xDFFF swi 0xFF
Возможно, я вижу ваше замешательство, поскольку обратный адрес для всех, кроме последнего, является одним и тем же адресом, и мы могли бы, возможно, создать пример.Но рекурсия часто имеет больше, чем адрес возврата, но имеет некоторые другие локальные переменные, которые меняются, в этом случае наша локальная переменная находится в r0, если вам не нужно сохранять ее в стеке при каждом вызове.
Первый раз, когда мы возвращаемся к началу bl после сброса:
write16(0x20000FFC,0x0027)
write16(0x20000FFE,0x0800)
В остальное время это тот же адрес возврата, но нам нужно их N в стеке, чтобы код работал так, как написано.
write16(0x20000FF4,0x003B)
write16(0x20000FF6,0x0800)
write16(0x20000FEC,0x003B)
write16(0x20000FEE,0x0800)
write16(0x20000FE4,0x003B)
write16(0x20000FE6,0x0800)
write16(0x20000FDC,0x003B)
write16(0x20000FDE,0x0800)
так что теперь мы имеем пять этих адресов в стеке.
read16(0x20000FDC)=0x003B
read16(0x20000FDE)=0x0800
...
read16(0x20000FFC)=0x0027
read16(0x20000FFE)=0x0800
В общем случае bl изменяет lr и помещает адрес возврата в стек (выше приведен код большого пальца)не код руки, но охватывает ваши вопросы, так как они работают одинаково в этом отношении).поэтому, если вы вкладываете вызовы one () вызывает два (), два () вызывает три () для two (), чтобы вернуться к одному (), то lr необходимо сохранить в two (), чтобы его можно было использовать, если выне сохраняйте lr, тогда вызов three () изменяет lr, и мы не можем вернуться.
Если ваша рекурсия хочет использовать bl (выглядит как скомпилированный код) для чистоты, и вы хотите найти способ для этой функции, factorialв моем тестовом примере, чтобы иметь возможность вернуться к исходному вызывающему, эти два факта объединяются с необходимостью помещать lr в стек.Если вы хотите добавить bl к вершине рекурсивной функции, той же самой точки входа, которую использовала внешняя вызывающая сторона, тогда каждый вызов будет добавлять lr в стек, и при каждом возврате необходимо возвращать его обратно.
ЕслиВы хотите выполнить некоторую ручную сборку, чтобы изменить ее, и она не вызывает ту же точку входа, которую вы можете избавить от bl и стека.
test:
push {r3,lr}
test1:
cmp r0,#0
beq test2
sub r0,#1
b test1
test2:
pop {r3,pc}
может даже оставить там bl *
test:
push {r3,lr}
test1:
cmp r0,#0
beq test2
sub r0,#1
bl test1
test2:
pop {r3,pc}
но если вы хотите возвращаться каждый раз, то разрыв цикла должен быть выполнен по-другому.У меня нет решения, которое использует bl и return, но может выйти из цикла в нужное время.