Подпрограммы с вложенными вызовами в MIPS - PullRequest
0 голосов
/ 31 марта 2019

Эта программа предназначена для замены всех строчных букв в строке символом *.

Проблема, с которой я сталкиваюсь, заключается во вложенном вызове подпрограмм.Т.е. в разных подпрограммах используются одни и те же регистры $t и $a.Итак, когда подпрограмма вызывается в другой подпрограмме, регистры подпрограммы вызывающего запутываются.

.data
    str: .asciiz "WindOnTheHill" 

.text
    la $a0, str # start of the string
    li $a1, '*'
    jal ReplaceAllLower

    #la $a0, str # start of the string
    jal PrintStr
    jal Exit

ReplaceAllLower:
    # backup return address
    addi $sp, $sp, -12 #  create space for 3 words
               # (3*4=12 bytes) on the stack 
               # (push) for $ra
    sw $ra, 0($sp) # backup return address $ra

    # protect arguments from change
    sw $a0, 4($sp) # backup string address
    sw $a1, 8($sp) # backup char 

    # get string length
    jal StrLen # obtain string length
    move $t0, $v0 # backup string length

    # retrieve argument values  
    lw $a1, 8($sp) # restore char 
    lw $a0, 4($sp) # restore string address

    move $t1, $a0 # obtain string address
    move $t2, $a1 # obtain char
    li $t3, 0 # loop counter    

    while:  
        bgt $t3, $t0, end_while 

        jal IsLower

        beq $t0, 1, lower_case
        j not_lower_case

        lower_case:
            sb $t2, ($a0)           

        not_lower_case: 
            addi $a0, $a0, 1 # increment address
            addi $t3, $t3, 1 # increment loop counter

        j while
    end_while:  

    move $a0, $t1

    # restore stack     
    lw $ra, 0($sp) # restore $ra
    addi $sp, $sp, 16 # return the space on the stack(pop)

    # return 
    jr $ra  

IsLower:
    lb $t0, ($a0) # obtain the character
    li $t1, 97 # 'a' - character
    li $t2, 122 # 'z' - character

    bge $t0, $t1, con1_fulfilled #bigger tha or equal to 0  
    j con1_not_fulfilled

con1_fulfilled:
    ble $t0, $t2, con2_fullfilled #less than or equal to 9
    j con2_not_fulfilled

con2_fullfilled:
    li $v0, 1
    j return

con1_not_fulfilled:
con2_not_fulfilled:
    li $v0, 0

return:                                                                     
    # return 
    jr $ra 

StrLen:
    move $a1, $a0 # start of string
    # run a loop
    li $t0, '\0' # null character 
    li $t1, 0 # prepare the counter

    start_loop: 
        lb $v0, ($a0) # obtain the 1st character

        beq $v0, $t0, end_loop  # exit loop if '\0'-char found

        addi $t1, $t1, 1 # increment counter
        addi $a0, $a0, 1 # increment address

        j start_loop # iterate again
    end_loop:

    move $a0, $a1 #restore string address
    move $v0, $t1 # return value

    # return 
    jr $ra  

PrintStr:   
    li $v0, 4
    syscall
    # return 
    jr $ra


Exit:
    # push $s0 on stack
    addi $sp, $sp, -4 # create 4-bytes on the stack
    sw $s0, ($sp) # cpy $s0 to stack

    #terminate program
    li $v0, 10
    syscall

    # free stack  
        addi $sp, $sp, 4 
    # return 
    jr $ra  

Примечание: Давайте не будем концентрироваться наалгоритм на данный момент.

Итак, мой вопрос:
Какой метод я должен использовать, чтобы избавиться от этой проблемы, так как очень трудно заранее знать, какая подпрограмма будетчто называется в будущем (библиотека может расширяться со временем)?

1 Ответ

2 голосов
/ 01 апреля 2019

Некоторые соглашения требуются таким образом, чтобы, если все подпрограммы уважали их, не было бы никаких проблем, таких как регистр, захламленный вызываемой процедурой.

Для MIPS общепринятые соглашения о вызовах таковы:
* регистры $t0-7 являются «временными» и могут использоваться без мер предосторожности.Если процедура хочет сохранить некоторые из них при вызове функций, она несет ответственность за их сохранение («сохраненный вызывающий объект»).
* регистры $s0-7 («сохраненные регистры») нельзя использовать без предосторожности.Если процедура хочет использовать некоторые из них, она должна сохранить их перед использованием и восстановить их значение по возвращении («сохраненный вызываемый»)

В соглашениях о вызовах есть и другие важные аспекты, такие как передача первых аргументовв регистрах $a0-$a3, используя $v0-$v1 для возвращаемого значения и т. д. Они также уточняют роль некоторых регистров, таких как указатель стека (sp) или указатель кадра (fp). этот документ является очень хорошим резюме, но вы можете легко найти дополнительную информацию в Интернете.

Сохранение регистров выполняется с помощью стека вызовов .Это структура данных, которая содержит всю сохраненную информацию.

В начале функции в стеке должно быть зарезервировано некоторое пространство для всей информации, которую необходимо сохранить.Затем регистры s0-s7, которые будут использоваться, сохраняются в стеке.Если функция не является терминальной (то есть вызывает другую функцию), возвращенный адрес также сохраняется.

Перед вызовом функции , временные регистры или регистры аргументов ($t0-7 или $a0-3)которые должны быть сохранены, записываются в стек.Аргументы записываются в регистры $a0-3 или суммируются при необходимости.И вызывается функция.

После возврата вызванной функции сохраняются временные регистры.

И до того, как функция вернет , нужночтобы восстановить сохраненные регистры $s0-7 и регистр адресов возврата ($ra), пространство стека освобождается, и один из них вызывает jr $ra.

Если все процедуры соответствуют этим соглашениям о вызовах, проблем не будет.Компиляторы уважают эти соглашения, но они зависят от ОС и архитектур.

...