将单词的每个字符替换为该单词中大写字符的个数

Replace each character of the word by the number of uppercase characters in this word

我想实现多词支持

例如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

我的输出中有空的 spaces,所以字符串是:111[]222[]1111 并且打印首先是空的 space,所以只有 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 上对齐)

编辑:原始问题的答案是,原始代码确实只在输出缓冲区中填充了与单词内容相对应的字节,但在两者之间保留了未定义的内存,这恰好在 MARS 模拟器中被归零,所以不小心将其归零-第一个单词后的终止符,MARS 的 "print string" 服务确实期望以零结尾的字符串 = 仅打印第一个单词。


这是我针对相同任务的变体,使用各种快捷方式以(略微)更少的指令完成相同的事情(它仍然是 O(N) 复杂度)。

我还以一种方式编写它以确保具有多个 space 的输入、具有 space 的 starting/ending 或空输入正常工作(对于 "two spaces" 输入它也会输出"two spaces")我的变体):

# 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

注意事项("tricks"?):

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

或者如果您对代码的特定部分有任何其他问题,请随时提出。