如何使用 MIPS 代码将十进制罗马数字相加?

问题描述 投票:0回答:1

所以,我在上次家庭作业中表现得很好,其中我还必须在 MIPS 汇编中将程序作为任务来实现。在一点帮助下我理解了它的概念和逻辑。但是,MIPS 对我来说仍然很新,我仍在学习并希望提高我使用 MIPS 进行编码的技能。

但这一次,我完全困惑了。任务的整个逻辑非常奇怪和令人困惑。我无法理解错误消息。

任务是实现一个罗马数字解码器,乍一听起来很简单。但我必须使用给定的寄存器,并且不允许我更改标签功能之上

roman.

.data

str_input: .asciiz "numeral: "
str_return_value: .asciiz "\nReturn value: "
romdigit_table: .word 0, 0, 0, 100, 500, 0, 0, 0, 0, 1, 0, 0, 50, 1000, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 10, 0, 0, 0, 0, 0, 0, 0

.text

.eqv SYS_PUTSTR 4
.eqv SYS_PUTCHAR 11
.eqv SYS_PUTINT 1
.eqv SYS_EXIT 10

main:
    # roman numeral is output:
    li $v0, SYS_PUTSTR
    la $a0, str_input
    syscall

    li $v0, SYS_PUTSTR
    la $a0, test_numeral
    syscall
    
    li $v0, SYS_PUTSTR
    la $a0, str_return_value
    syscall

    move $v0, $zero
    # calling the function roman:
    la $a0, test_numeral
    jal roman
    
    # return value is output:
    move $a0, $v0
    li $v0, SYS_PUTINT
    syscall

    # end of program execution:
    li $v0, SYS_EXIT
    syscall
    
    # help function: int romdigit(char digit);
romdigit:
    move $v0, $zero
    andi $t0, $a0, 0xE0
    addi $t1, $zero, 0x40
    beq $t0, $t1, _romdigit_not_null
    addi $t1, $zero, 0x60
    beq $t0, $t1, _romdigit_not_null
    jr $ra
_romdigit_not_null:
    andi $t0, $a0, 0x1F
    sll $t0, $t0, 2
    la $t1, romdigit_table
    add $t1, $t1, $t0
    lw $v0, 0($t1)
    jr $ra


.data

test_numeral: .asciiz "DCCXLVI"

.text

roman:


    li $t0, 0 # result
    
    li $t1, 0 # Index in 'test_numeral'
    
    
roman_decoder:

    la $a0, test_numeral # reseting $a0
    
    add $a0, $t1, $a0 # adding the new index to the pointer $a0

    lb $t2, 0($a0) # first number in 'test_numeral'
    
    lb $t3, 1($a0) # second number in 'test_numeral'
    
    beqz $t3, exit # exit if zero is reached
    
    lb $a0, 0($a0) # given help function 'romdigit' needs $a0 as the argument
    
    bgt $t3, $t2, subtract # if $t3 > $t2 subtract $t2 from result
    
    j add
    
    
subtract: # subtract $t2 from the result

    jal romdigit # calling 'romdigit'
    
    sub $t0, $t0, $v0 # in $v0 is the encrypted decimal number
    
    addi $t1, $t1, 1
    
    j roman_decoder

add: # add $t2 to the result

    jal romdigit # calling 'romdigit'
    
    add $t0, $t0, $v0 # in $v0 is the encrypted decimal number
    
    addi $t1, $t1, 1
    
    j roman_decoder

exit:
    
    jr $ra

我的代码背后的逻辑很简单。迭代

test_numeral
直到
\0
;读取罗马数字并使用
romdigit
进行解码;将
$v0
中存储的值添加到结果寄存器。

我仍然很困惑,我必须使用

$a0
作为指向
test_numeral
的指针,并作为要在
romdigit
中使用的当前罗马数字的持有者。

我认为这段代码理论上可以工作,但我无法让它正常运行。我真的很绝望。

我的意思是有很多方法可以真正解决这个问题。这是我能想到的最简单的一个。但我总是收到“地址超出范围”错误,我不明白为什么......

我认为为索引

$t1
设置一个计数器是有意义的,每次读取罗马数字时索引都会增加 1,并且在我在 romdigit 中使用
$a0
后,将
$t1
添加回
$a0
获取
test_numeral
中的新索引。但这效果不太好,我不知道如何以其他方式做到这一点。

我将不胜感激对此的任何提示。先谢谢你们了!

我很忙,所以我无法再真正编写代码了。

但我想我现在有一个工作代码了。唯一的问题是它不会停止:

.data

test_numeral: .asciiz "DCCXLVI"

.text

roman:

    move $t3, $a0 
    
    li $t2, 0 
    
    li $t4, 0 

roman_decoder:

    move $t4, $v0
    
    lb $a0, 0($t3)
    
    beqz $a0, exit
    
    jal romdigit
    
    bgt $v0, $t4, subtract
    
    add $t2, $t2, $v0
    
    addi $t3, $t3, 1
    
    j roman_decoder
    
subtract:

    sub $t2, $t2, $t4
    
    sub $t2, $t2, $t4
    
    add $t2, $t2, $v0
    
    addi $t3, $t3, 1
    
    j roman_decoder
    
exit:

    move $v0, $t2
    
    jr $ra

$a0
为零时,代码跳转到
exit
。将
$t2
移动到
$v0
中并且应该结束,因为我说的是
jr $ra
,所以它应该跳回 main 来结束程序,对吧?我做错了什么?

提前非常感谢!

assembly mips decode mips32 roman-numerals
1个回答
0
投票

问题可能是 romdigit 函数本身使用 jr 回调在 roman_decoder 中继续。在此过程中,它还会更改 $ra 寄存器的值,因此 exit 函数会再次调用 romand_decoder 而不是 main 函数。尝试在调用 romdigit 之前将 $ra 的值存储在额外的寄存器中,并在退出函数中在使用回调之前将值加载回 $ra 寄存器

© www.soinside.com 2019 - 2024. All rights reserved.