在MIPS汇编中切换字符位置

时间:2012-10-10 18:16:54

标签: string assembly mips

我正在尝试打印

Hello, world.
ello, world.H
llo, world.He
lo, world.Hel
o, world.Hell
, world.Hello
 world.Hello,
world.Hello,
orld.Hello, w
rld.Hello, wo
ld.Hello, wor
d.Hello, worl
.Hello, world
Hello, world.

在内存String: .asciiz "Hello, world."中仅使用此字符串。到目前为止,这是我的代码:

    .text
String:    .asciiz "Hello, world."
x:         .float 13

    .data 
Main:       lw $t0,x
            jal PrintString
            li $v0, 10
            syscall

PrintString:    la $a0,String
                li $v0,4
                syscall
                bgtz $t0, MoveString
                jr $ra

MoveString: (CODE)

但我不确定将什么放入MoveString标签。我需要将String中的字符移一,然后将x中的字符减去1.我不知道该如何去做。谢谢你的帮助!

1 个答案:

答案 0 :(得分:5)

首先注意几个:

  • 您已将.text.data混为一谈
  • 继续在字符串的末尾添加换行符,它会显示更好一点:

    String:    .asciiz "Hello, world.\n"
    
  • 假设13用于字符串的长度,则应使用整数而不是float:

    x:         .word 13
    

现在,解决问题。有两种方法可以做到这一点:1)将字符串就地旋转并每次调用syscall 4,或者2)保持字符串不变并一次打印一个字母,然后使用索引来实现旋转效果。你已经选择了方法1,所以让我们继续。

首先编写一些伪代码。你需要一个子程序,它将存储在$a0的字符串向左旋转一个字母。您应该立即考虑某种循环来迭代字符串中的每个字母:

for int index from 0 to 12
    copy letter from (index + 1) to index

但是等一下:第一封信怎么了?它会被破坏。当我们到达终点时会发生什么?我们会将String范围之外的内容复制到最后一个字母位置。因此,让我们通过将第一个字母存储到临时寄存器来解决它:

temp = letter[0]

for int index from 0 to 11
    copy letter from (index + 1) to index

letter[12] = temp

那更好;应该这样做。下一个问题:我们如何在MIPS中做到这一点?

我们知道$a0将保存字符串的地址,所以我们认为这是理所当然的。我们至少需要一个临时注册 - 因为我们已经使用$t0 x,我们使用$t1来保存第一个字母:

MoveString:
lb $t1, String

我们还需要一个索引寄存器,所以让我们使用$t2。将其初始化为零:

li $t2, 0

为每个字母增加一次地址寄存器会很好,但我们不想破坏$a0,因为它会弄乱PrintString。所以我们将它复制到$a1以获取我们的循环:

move $a1, $a0

最后,我们需要知道字符串有多长,所以让我们将x的另一个副本加载到$t3

lb $t3, x
sub $t3, $t3, 1               ; We're only iterating over the first 12 letters,
                              ; since the last letter is done manually with temp

现在我们可以开始循环了。我们需要将$a1 + 1的字母(只是一个字节)复制到$a1

MoveStringLoop:
lb $t4, 1($a1)                  ; Load the letter from (address + 1)
sb $t4, 0($a1)                  ; Store it to (address)
add $a1, $a1, 1                 ; Increment our address
add $t2, $t2, 1                 ; Increment our index
blt $t2, $t3, MoveStringLoop    ; Loop again if index < (length - 1)

sb $t1, 0($a1)                  ; Copy the temp letter to the end of the string

那应该是它。之后,我们可以递减x并返回PrintString

sub $t0, $t0, 1
b PrintString

这无论如何都不是最佳解决方案;我确信一个真正的编译器和一些更好的编码可以在一半的指令中完成工作。但是既然你正在学习如何编写汇编,那么现在就不用担心微优化了。这是最终代码及其输出:

    .data
String:    .asciiz "Hello, world.\n"
x:         .word 13

    .text 
Main:       lw $t0,x
            jal PrintString
            li $v0, 10
            syscall

PrintString:    la $a0,String
                li $v0,4
                syscall
                bgtz $t0, MoveString
                jr $ra

MoveString:
                lb $t1, String
                li $t2, 0
                move $a1, $a0
                lb $t3, x
                sub $t3, $t3, 1

MoveStringLoop:
                    lb $t4, 1($a1)
                    sb $t4, 0($a1)
                    add $a1, $a1, 1
                    add $t2, $t2, 1
                    blt $t2, $t3, MoveStringLoop

                sb $t1, 0($a1)

                sub $t0, $t0, 1
                b PrintString

输出:

Hello, world.
ello, world.H
llo, world.He
lo, world.Hel
o, world.Hell
, world.Hello
 world.Hello,
world.Hello,
orld.Hello, w
rld.Hello, wo
ld.Hello, wor
d.Hello, worl
.Hello, world
Hello, world.