将单词的每个字符替换为该单词中大写字符的个数
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" 分支)。
或者如果您对代码的特定部分有任何其他问题,请随时提出。
我想实现多词支持
例如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" 分支)。
或者如果您对代码的特定部分有任何其他问题,请随时提出。