MIPS 程序集根据非常具体的描述编写函数

MIPS assembly writing a function from a very specific description

我有关于如何编写一个名为 tim2string 的函数的具体描述,我已经编写了这个函数,但我不确定我是否按照描述中所说的那样做。我已经尝试 运行 我在 MARS 上的代码,我得到了这个 ERROR。注意 tim2string 添加在代码的末尾。

timetemplate.asm line 99: Runtime exception at 0x00400118: address out of range 0x00005958

函数说明如下

名称:子例程必须称为 time2string。 参数(二): 寄存器 $a0 包含内存中一个区域的地址,对于 time2string 的输出。寄存器 $a1 的 16 位最低有效位包含时间信息,有组织 作为四个 NBCD 编码的数字,每个数字 4 位。寄存器 $a1 中的所有其他位可以有任何值,并且必须 被忽略。

示例:寄存器 $a0 可以包含地址 0x100100017,寄存器 $a1 可以包含值 0x00001653。 Return 值:None。 所需操作:

必须将以下六个字符序列写入内存中的区域 由寄存器 $a0 指向。

1) 显示分钟数的两个 ASCII 编码数字,根据另外两个 输入参数的重要 NBCD 编码数字。示例:“1”、“6”(ASCII 0x31、0x36):

2) 一个冒号字符(ASCII : , 代码 0x3A)。

3)两个ASCII编码的数字显示秒数,根据少两个 输入参数的重要 NBCD 编码数字。示例:“5”、“3”(ASCII 0x35、0x33)。 4. 空字节(ASCII NUL,代码 0x00)。 Notes:You 必须使用函数hexasc将每个NBCD编码的数字转换成对应的 ASCII 码。使用 sb 指令将每个字节存储在目标位置。宏 PUSH 和 POP 对于保存和恢复寄存器很有用。

  # timetemplate.asm
  # Written 2015 by F Lundevall
  # Copyright abandonded - this file is in the public domain.
.macro  PUSH (%reg)
    addi    $sp,$sp,-4
    sw  %reg,0($sp)
.end_macro

.macro  POP (%reg)
    lw  %reg,0($sp)
    addi    $sp,$sp,4
.end_macro

    .data
    .align 2
mytime: .word 0x5957
timstr: .ascii "text more text lots of text[=11=]"
    .text
main:
    # print timstr
    la  $a0,timstr
    li  $v0,4
    syscall
    nop
    # wait a little
    li  $a0,2
    jal delay
    nop
    # call tick
    la  $a0,mytime
    jal tick
    nop
    # call your function time2string
    la  $a0,timstr
    la  $t0,mytime
    lw  $a1,0($t0) #load the adress contained in $t0 into $a1
    jal time2string
    nop
    # print a newline
    li  $a0,10
    li  $v0,11
    syscall
    nop
    # go back and do it all again
    j   main
    nop
# tick: update time pointed to by $a0
tick:   lw  $t0,0($a0)  # get time
    addiu   $t0,$t0,1      # increase
    andi    $t1,$t0,0xf # check lowest digit
    sltiu   $t2,$t1,0xa # if digit < a, okay
    bnez    $t2,tiend
    nop
    addiu   $t0,$t0,0x6     # adjust lowest digit
    andi    $t1,$t0,0xf0    # check next digit
    sltiu   $t2,$t1,0x60    # if digit < 6, okay
    bnez    $t2,tiend
    nop
    addiu   $t0,$t0,0xa0    # adjust digit
    andi    $t1,$t0,0xf00   # check minute digit
    sltiu   $t2,$t1,0xa00   # if digit < a, okay
    bnez    $t2,tiend
    nop
    addiu   $t0,$t0,0x600      # adjust digit
    andi    $t1,$t0,0xf000  # check last digit
    sltiu   $t2,$t1,0x6000  # if digit < 6, okay
    bnez    $t2,tiend
    nop
    addiu   $t0,$t0,0xa000  # adjust last digit
tiend:  sw  $t0,0($a0)      # save updated result
    jr  $ra                 # return
    nop

  # you can write your code for subroutine "hexasc" and  below this line
  #
hexasc:  
    andi    $a0,$a0,0xf       #only 4 least significant bits ignore other bits  
    addi    $v0,$zero,0x30    #$v0 = 0x30 ('0')
    addi    $t0,$zero,0x9     #t0 = 0x9

    ble     $a0,$t0,L1        #branch if a0 <= 0x9
    nop
    addi    $v0,$v0,0x7       #v0 = v0 +0x7

   L1:
    add     $v0,$a0,$v0       #v0 = V0 +a0
    jr      $ra
    nop
 delay:
    jr      $ra
    nop

 time2string:
    PUSH    ($t2)           
    PUSH    ($t0)
    PUSH    ($a1)

    lb      $t0, 0($a1)      #load one byte from a1     "LINE 99" ERROR
    andi    $t2, $t0,0xf     #check the 4 lowest bits ignore other
    jal     hexasc           # call hexasc
    nop
    sb      $t0, 0($a1)      #stor back that byte in a1


    lb      $t0, 1($a1)      # load the next byte
    andi    $t2, $t0,0xf0
    jal     hexasc
    nop
    sb      $t0, 1($a1)


    lb  $t0, 2($a1)
    andi    $t2, $t0,0xf00
    jal hexasc
    nop
    sb  $t0, 2($a1)


    lb  $t0, 3($a1)
    andi    $t2, $t0,0xf000
    jal hexasc
    nop
    sb  $t0, 3($a1)


    POP ($a1)
    POP ($t0)
    POP ($t2)


    jr  $ra
    nop 

更新 1.0

我在编写 time2string 函数方面取得了一些进展 我仍然需要一些帮助。 我单步执行我的代码它工作正常直到这一行sb $v0, 0($t0) #stor that 4 bits in that location that a0 points to

我收到这个错误timetemplate.asm line 115: Runtime exception at 0x00400158: address out of range 0x00000009

这是更新后的代码

time2string:
PUSH    ($t0)           
PUSH    ($t1)
PUSH    ($t2)
PUSH    ($t3)
PUSH    ($t4)
PUSH    ($t5)
PUSH    ($ra)           #nested subroutine must store $ra too

add     $t0,[=12=],$a0      #contaisn the adress of string (timstr)
add     $t1,[=12=],$a1      #contains the time-info(0x5957)

andi    $t2,$t1,0xf000      #check the 4 most signifaicant bits ignore other bits
srl     $a0,$t2,12      #shift the MSB to LSB position (hexasc take only 4 bits in the LSB position)
jal     hexasc      # call hexasc
nop     
sb      $v0, 0($t0)     #stor that 4 bits in that location that a0 points to

andi    $t3,$t1,0x0f00  #mask to get those 4 bits you and ignore other bits
srl     $a0,$t3,8       #shift those bits to the LSB position(0x000f)
jal     hexasc      
nop
sb      $v0,1($t0)      

li      t5,0x3A
sb      $t5,2($t0)


andi    $t4,$t1,0x00f0
srl     $a0,$t4,4
jal     hexasc
nop
sb      $v0,3($t0)


move    $a0,$t1
jal     hexasc
nop
sb      $v0, 4($t0)


POP ($ra)
POP ($t5)
POP ($t4)
POP ($t3)
POP ($t2)
POP ($t1)   
POP ($t0)

jr  $ra
nop 
    lb      $t0, 0($a1)      #load one byte from a1

这将从地址 a1 的内存中加载一个字节。

a1不包含有效地址,它包含BCD时间值,如0x5958,表示时间59:58。

因此您不需要从内存中加载时间值,您已经加载了 a1 中的值。你必须分别挑选出每组4位(从低16位开始),这就是4位数字的编码。


哦,还有这个:

I have tried to run my code on MARS

这不是人们在 Assembly 中编程的方式。您没有 运行 您的代码。您在调试器中逐条执行单条指令,在每条指令后验证它确实只更改了它应该更改的 registers/memory,并且只更改了它应该更改的方式。

(这就是为什么您首先需要描述算法的英文注释,因为否则您无法推断指令是否按照您的要求执行...当然它总是会执行,它执行的操作,根据 CPU 文档,你一生中几乎不会遇到指令本身无法正确执行的情况(尽管在技术上是可行的,当一些电子 "jump" 超过 "walls"假设的路径,通常是在被来自 X-rays/etc 的一些质子重击之后......也许如果你活得足够长,它可能会在你附近发生一两次......你很可能永远不会注意到,因为SW 可能会幸存下来,或者像代码错误一样正常崩溃))

还要确保你避免大脑捉弄你,走捷径来节省它的精力,所以你的大脑不会阅读屏幕上的内容,而是会尝试告诉你,你确实想写什么,看到那里。

就像你在代码中写 sb $t0,0($a1) 而不是 sb $t0,0($a0),如果你在写完后不久尝试重新阅读源代码,你会读到 sb $t0,0($a0),除非你非常专注,确保你的大脑没有欺骗你。

同样适用于调试和检查寄存器中的结果值等。例如,如果您期望 t1 中的值 1,而它不小心以 t2 结束,您可能很容易忽略 1 确实在 t2 中发生了变化,而不是在 t1 中发生变化的事实,因为你的大脑最关心的是结果是 1。等等

这很棘手。这就是为什么人们喜欢 Assembly 并在其中编写几乎所有代码的原因。