用单词中大写字符的数量替换单词中的每个字符

时间:2018-11-10 21:06:46

标签: assembly mips mars-simulator

我想实现多词支持。

例如abc AAAA cbAAaa => 000 4444 222222

目前该程序所做的只是第一个单词转换。

从火星模拟器中的调试器看来,它正确地执行了所有循环。寄存器值相同。 (也许我错过了什么) 我认为单词的长度必须少于10个字符。

如果有人发现错误,我将不胜感激。 另外,如果您有调试技巧或代码改进的任何技巧,请随时说。

我的代码:

    .data
prompt: .asciiz "Enter a string: "
msgout: .asciiz "Output string: "
input:  .space  256
output: .space  256
    .text
    .globl main

main:
    li  $v0, 4          # Print enter a string prompt
    la  $a0, prompt     
    syscall

    li  $v0, 8          # Ask the user for the string they want to reverse
    la  $a0, input      # We'll store it in 'input'
    li  $a1, 256        # Only 256 chars/bytes allowed
    syscall

    la  $t2, ($a0)      # t2 - input string

    word:
        li  $t1, 0              # Normal counter
        li  $t5, 0              # Uppercase counter
        li  $t6, 0              # First letter of word
        j   word_countUppercase
        word_precountUppercase:
            addi    $t1, $t1, 1             # Add 1 to index to avoid space in next word
            la  $t6, ($t1)              # Set t6 to the first index of t2 (start of word)
            la  $t5, 0                  # $t5 - 0
        word_countUppercase:
            #addi   $t1, $t1, $t7
            add $t3, $t2, $t1               # $t2 is the base address for our 'input' array, add loop index
            lb  $t4, 0($t3)             # load a byte at a time according to counter

            beq     $t4, ' ', word_prereplace       # We found end of word
            bltu    $t4, ' ', word_prereplace       # We found end of string    

            addi    $t1, $t1, 1             # Advance our counter (i++)

            bltu    $t4, 'A', word_countUppercase
            bgtu    $t4, 'Z', word_countUppercase

            addi    $t5, $t5, 1                 # Advance our counter (i++)
            j   word_countUppercase

        word_prereplace:
            la  $t2, ($a0)              # t2 - input string
            la  $t1, ($t6)              # Normal counter
            addi $t5, $t5, '0'

            word_replace:
                add $t3, $t2, $t1           # $t2 is the base address for our 'input' array, add loop index
                lb  $t4, 0($t3)         # load a byte at a time according to counter    

                beq $t4, ' ', word_replaceExit  # end of the word
                bltu    $t4, ' ', exit          # We found end of string    

                sb  $t5, output($t1)        # Overwrite this byte address in memory 

                addi    $t1, $t1, 1             # Advance our counter (i++)
                j   word_replace
            word_replaceExit:
                j   word_precountUppercase



exit:
    li  $v0, 4          # Print msgout
    la  $a0, msgout
    syscall

    li  $v0, 4          # Print the output string!
    la  $a0, output
    syscall

    li  $v0, 10         # exit()
    syscall

2 个答案:

答案 0 :(得分:2)

我的输出中有空格,因此字符串为:111 [] 222 [] 1111,而print则将其打印到第一个空格,因此只有111。

要解决此问题,我添加了以下代码:(在单词标签之前)

li  $t1, 0                  # Normal counter
    rewriteoutput:
        add $t3, $t2, $t1           # $t2 is the base address for our 'input' array, add loop index
        lb  $t4, 0($t3)         # load a byte at a time according to counter

        bltu    $t4, ' ', word          # We found end of string

        sb  $t4, output($t1)        # Overwrite this byte address in memory
        addi    $t1, $t1, 1             # Advance our counter (i++)
        j rewriteoutput

我知道我们可能可以做得更好,但是不明白为什么我做不到

sw $a0, output

代替它(运行时错误:运行时异常在0x0040002c:存储地址未在字边界0x10010121上对齐)

答案 1 :(得分:2)

编辑:原始问题的答案是,原始代码仅填充了与单词内容相对应的字节的输出缓冲区,但保留了未定义的内存,这在MARS模拟器中恰好为零,因此意外出现了零终止符在第一个单词之后,并且MARS的“打印字符串”服务确实期望以零结尾的字符串=仅打印了第一个单词。


这是我针对同一任务的变体,它使用各种快捷方式以(少量)指令(几乎仍然是O(N)复杂性)来完成相同的事情。

我也以确保多个空格,以空格开头/结尾或空输入的输入正确工作的方式编写了它(对于输入中的“两个空格”,它也将输出“两个空格”)(我没有用您的原始代码测试所有这些,所以我并不是说有一些错误,似乎应该可以很好地处理它们,我只是对我的变体进行了彻底的测试):

# delayed branching should be OFF
.data
prompt: .asciiz "Enter a string: "
msgout: .asciiz "Output string: "
input:  .space  256
output: .space  256
    .text
    .globl main

main:
    li  $v0, 4          # Print enter a string prompt
    la  $a0, prompt
    syscall

    li  $v0, 8          # Ask the user for the string they want to reverse
    la  $a0, input      # We'll store it in 'input'
    li  $a1, 256        # Only 256 chars/bytes allowed
    syscall

    la    $a1, output
    # a0 = input, a1 = output
new_word:
    move  $t0, $zero    # t0 word length = 0
    li    $t1, '0'      # t1 uppercase counter = '0' (ASCII counter)
word_parse_loop:
    lbu   $t2, ($a0)    # next input character
    addi  $a0, $a0, 1   # advance input pointer
    bltu  $t2, 33, word_done   # end of word detected (space or newline)
      # "less than 33" to get shorter code than for "less/equal than 32"
    addi  $t0, $t0, 1   # ++word length
    # check if word character is uppercase letter
    addiu $t2, $t2, -65 # subtract 'A' => makes t2 = 0..25 for 'A'..'Z'
    sltiu $t3, $t2, 26  # t3 = (t2 < 26) ? 1 : 0
    add   $t1, $t1, $t3 # ++uppercase counter if uppercase detected
    j     word_parse_loop

word_output_fill:
    # loop to fill output with uppercase-counter (entry is "word_done" below)
    sb    $t1, ($a1)
    addi  $a1, $a1, 1
    addiu $t0, $t0, -1
word_done:
    # t0 = word length, t1 = uppercase ASCII counter, t2 = space, newline or less
    # a0 = next word (or beyond data), a1 = output pointer (to be written to)
    bnez  $t0, word_output_fill

    bltu  $t2, ' ', it_was_last_word
    # t2 == space, move onto next word in input (store space also in output)
    sb    $t2, ($a1)
    addi  $a1, $a1, 1
    j     new_word

it_was_last_word:
    # finish output data by storing zero terminator
    sb    $zero, ($a1)

    # output result
    li  $v0, 4          # Print msgout
    la  $a0, msgout
    syscall

    li  $v0, 4          # Print the output string!
    la  $a0, output
    syscall

    li  $v0, 10         # exit()
    syscall

注意事项(“技巧”?)

  • 大写计数器从值48(字符为零)开始,因此“计数器”在整个时间中确实保存ASCII数字(对于少于10的计数,对于10+,它将转到数字以外的其他字符),并且可以直接写入字符串而无需任何转换(因为计数器在任何地方都不用作“整数”,因此您可以像这样优化转换)。
  • 它以顺序的方式在输入和输出中前进,从不读取某些输入两次或重新调整输入/输出位置,因此类似的算法也可以处理类似“流”的数据(几乎每产生一个1:1输出)输入字符,除了输出“每个字”稍有延迟外,即它将处理输入流直到“字尾”,然后产生完整的输出字(此架构对于某些I / O(例如输入的磁带)可能很重要,和串行打印机输出)。
  • 对A..Z大写字母范围的检查确实仅使用单个比较条件(首先从字符中减去字母“ A”,将大写字母的值归一化为0..25,其他情况下均视为无符号)整数的值必须大于25,因此,一次< 26测试足以检测所有大写字母。
  • 每次通过添加0或1(取决于前面提到的条件)来更新大写计数器,这避免了代码中的额外分支。通常,现代的CPU喜欢更多的非分支代码,因为它们可能更积极地进行缓存/推测,因此,在分支机会更像50%的情况下,非分支代码变体通常具有更好的性能(对于分支为例如5-10%的机会,在这种不常见的情况下分支并在常见情况下保持一致可能会更好,例如,“单词结尾”分支之类的东西。)

或者如果您对代码的特定部分还有其他疑问,请随时提问。