Хранение и загрузка $ra

В настоящее время у меня возникли проблемы с написанием этого рекурсивного ассемблерного кода факториала в MIPS. Я не хочу менять формат этого кода, как для проекта, но хочу понять, как хранить или вызывать $ra, чтобы он возвращался в «ветвь:» для вывода вычисленного факториала.

      ## 
## 

.data
prompt1:    .asciiz "\nPlease enter an integer: \n"
negativePrompt: .asciiz "\nYour input must be greater than or equal to 0 in order to function.\n"
factorialIs:    .asciiz "\nThe factorial of your input number is: \n"

.globl main
.text
main:

    subu $sp, $sp, 32   # push memory for 8 values
    sw $ra, 20($sp)     # save return address
    sw $fp, 16($sp)     # save old frame pointer 
    addiu $fp, $sp, 28  # set up frame pointer

branch:

    li $v0, 4           # load macro for print_str 
    la $a0, prompt1     # pass argument to string
    syscall             # print string prompt1

    li $v0, 5           # load macro for read_int
    syscall             # get N value from user 

    move $a0, $v0               # store N as arg
    blt $a0, 0, negBranch       # if N is negative, handles case    
    jal factorial               # calls factorial function

    li $v0, 4               # macro for print string
    la $a0, factorialIs     # print string
    syscall

    li $v0, 1               # macro for print int
    move $a0, $v0           # print output int in v0
    syscall 

    lw $ra, 20($sp)         # restore return address of caller
    lw $fp, 16($sp)         # restore frame pointer
    addiu $sp, $sp, 32      # pop stack
    jr $ra                  # return to caller 
#exit:
#   "would you like to calculate again?" 
#   li $v0, 10              # load macro to exit
#   syscall                 # execute exit
    
negBranch: 
    li $v0, 4                   # load macro for print_str 
    la $a0, negativePrompt              # pass argument to string
    syscall                     # print string prompt1  
    li $t0, 0                   # initialize input value to 0
    j branch                        # ask user to input new number
    
factorial: 
    subu $sp, $sp, 32       # pushes memory for variables
    sw $ra, 12($sp)         # stores return address of recursion
    sw $fp, 8($sp)          # save frame pointer
    addiu $fp, $sp, 28      # set up frame pointer
    sw $a0, 0($fp)          # stores n in frame pointer

    lw $v0, 0($fp)
    bgtz $v0, zeroBranch        # if N is greater than 0, recurse
    li $v0, 1                   # return 1
    jr end

zeroBranch:
    lw $v1, 0($fp)          # load n value
    subu $v0, $v1, 1        # v0 = n - 1 
    move $a0, $v0           # a0 = v0 = n - 1 
    jal factorial           # recursive function call
    lw $v1, 0($fp)          # load original n value to v1
    mul $v0, $v0, $v1       # v0 = n * fact(n-1)    

end: 
    lw $ra, 12($sp)         # loads return address of main call
## this ^ line does not access the address from "jal factorial"
    lw $fp, 16($sp)         # loads initial n value
    addiu $sp, $sp, 32      # collapses stack frame
    jr $ra                  # return to main then exit

Когда я ввожу факториал(1), он работает рекурсивно, как и ожидалось; однако, когда я добираюсь до своей ветки «end:», адрес возврата не возвращается обратно в мою ветку «branch:», которая выводила бы результат ввода факториала.

1 ответ

Как вы думаете, почему функция должна вернуться к меткеbranch:— это по сути неверно, и вам нужно это переосмыслить. Функция должна вернуться к инструкции сразу после .


В этом коде есть ошибки времени сборки, поэтому я не знаю, как его запустить.

  • jr endнедействительная инструкция — я думаю, вы хотитеj endтам.

Этот код портит регистр в эпилогеfactorial- Старый$fpсохраняется в, но восстанавливается из16($sp), поэтому он не возвращается туда, где он нужен вызывающему абоненту. Измените восстановление на использование8($sp).


Последовательность системных вызовов, используемая для вывода результата факториала, следующая:

      li $v0, 1               # macro for print int
move $a0, $v0           # print output int in v0

Можете ли вы увидеть, как эти две строки затмили значение, которое вы хотите напечатать, поэтому оно всегда будет печатать?1?

Но это еще хуже, поскольку вы также печатаете приглашение (системный вызов № 4, несколькими строками выше), которое перемещает 4 (код системного вызова для строки печати) в уничтожение возвращаемого значения функции.

Итак, что делать: сразу послеjalобучение вmain, скопируйте возвращаемое значение$v0, в другом месте — подскажите как переживётsyscall(если бы это был вызов функции, то предполагалось бы использование памяти или$s0вместо).

Затем при системном вызове №1 для печати целочисленной копии$t0в$a0для параметра.


Вы можете увидеть все это, выполнив один шаг в отладчике. Отладка — это важнейший навык, и любой, кто пытается программировать на языке ассемблера, должен иметь его или приобрести как можно скорее. Делайте один шаг и наблюдайте за каждой строкой, проверяйте состояние программы (регистры и память) между каждой строкой.

Другие вопросы по тегам