Комментарии Питера Кордеса и Эллиота Алдерсона о необходимости предоставить пример MCV и о необходимости комментировать ваш код абсолютно верны. Пожалуйста, отвечайте на подобные запросы, потому что они значительно облегчают вашу помощь.
Самая очевидная ошибка - в пятой строке после метки L1
: ваш адрес смещения для повторной загрузки lr
неверен (вы выдвигаете значение r0
дважды).
В вашем коде есть несколько странных особенностей, с которыми вам действительно стоит разобраться. Первый из них предотвратил бы ошибку выше:
Используйте STMFD
и LDMFD
, чтобы нажать и выдвинуть. Это быстрее и менее подвержено ошибкам, чем ручное манипулирование указателем стека. Если sp
содержит ваш указатель стека (как обычно), вы можете использовать синонимы PUSH
и POP
. Например, три строки, в которых находится ошибка, можно записать просто как
LDMFD sp!, {r0,lr}
или
POP {r0,lr}
Обратите внимание, что вы можете вернуться, вставив сохраненное значение lr
непосредственно в pc
, а не вставив его в lr
и выполнив bx lr
(это называется «неявным возвратом» и сохраняет строка кода).
- Двоичный интерфейс ARM Application требует, чтобы функции отвечали за сохранение содержимого
r4-r11
, lr
и sp
. sp
сохраняется, если использование стека сбалансировано, конечно. Вы этого не делаете. Кроме того, поскольку r0-r3
и r12
не гарантированно сохраняются при вызовах, как правило, рекомендуется избегать их использования для локального хранилища данных.
- ABI также указывает, что стек должен быть выровнен по 8 байтов между вызовами функций в разных единицах перевода, поэтому рекомендуется всегда сохранять 8-байтовое выравнивание, всегда выдвигая и выталкивая четные числа регистров. За это не снижается производительность.
- Было бы неплохо стилистически отделить
fact
от main
, а не вкладывать одно в другое, как вы это сделали. (Вы будете вынуждены сделать это разделение на языке высокого уровня, в том числе на C.)
- Использование подписанного типа для вашего аргумента - странная идея, когда вы все равно игнорируете отрицательные аргументы.
bge
использует сравнение со знаком «больше или равно»; bhs
является беззнаковым эквивалентом. На самом деле вы можете использовать bhi
(«без знака выше»), потому что если аргумент равен 1, то нет смысла вычислять его факториал.
Я переписал ваш код, следуя приведенным ниже инструкциям. Я не добавил комментарии, это вам делать. Я также не изменил логику вашего алгоритма, так что там может быть место для улучшения. Я тоже не проверял!
.text
.global main
main:
stmfd sp!, {r7,lr}
@ compiling a recursive C procedure
@ unsigned int fact(unsigned int n){
@ if(n < 1){
@ return 1;
@ }else{
@ return (n * fact(n-1));
@ }
@ }
stmfd sp!, {r7,lr}
@ put n in r0
mov r0,#6
bl fact
mov r1,r0
ldr r0, =format
bl printf
ldmfd sp!, {r7,pc}
fact:
stmfd sp!, {r7,lr}
cmp r0,#1
bhi L1
mov r0,#1
ldmfd sp!, {r7,pc}
L1:
mov r7,r0
sub r0,r0,#1
bl fact
mov r7,r0
mul r0,r0,r7
ldmfd sp!, {r7,pc}
.data
format: .asciz "The Answer is %d\n"