我正在编写一个MIPS程序,它只能用大写或小写字符作为输入。我的程序使用字符的ASCII值。
我需要检查输入中的每个字符是否在65-90(A-Z)或97-122(a-z)的ASCII范围内。如果它不在任何一个范围内,请跳过此字符并重复下一个字符。怎么办呢?
修改
这是我刚刚提出的解决方案,但我确信有一种不那么难看的方法吗?
function: #increment $t0 to next char of input
blt $t0, 65, function
bgt $t0, 122, function
blt $t0, 91, continue
bgt $t0, 96, continue
j function
continue: ...
j function
答案 0 :(得分:0)
无论你做什么,你都需要四个分支机构。
我要做的第一件事就是为每条指令添加侧边栏注释。
好的评论是任何语言的一部分,但对于asm来说至关重要。实际上每一行都应该有它们。它们解决了算法的逻辑(即“什么/为什么”)。说明书本身就是“如何”。
请注意,您正在使用ASCII字符的十进制数字。如果没有注释,就很难按照逻辑来确定指令是否正确。
我会稍微调整您的分组以将A-Z
个测试放在一起,并a-z
一起测试,而不是混合它们。这可能稍慢,但代码更直观。
我还做了第三个版本,非常容易理解。它使用字符常量而不是硬连线的十进制值。
这是带注释的原文:
function:
# increment $t0 to next char of input
blt $t0,65,function # less than 'A'? if yes, loop
bgt $t0,122,function # greater than 'z'? if yes, loop
blt $t0,91,continue # less than or equal to 'Z'? if yes, doit
bgt $t0,96,continue # greater than or equal to 'a'? if yes, doit
j function
continue:
# ...
j function
这是重新订购的版本:
function:
# increment $t0 to next char of input
blt $t0,65,function # less than 'A'? if yes, loop
blt $t0,91,continue # less than or equal to 'Z'? if yes, doit
bgt $t0,122,function # greater than 'z'? if yes, loop
bgt $t0,96,continue # greater than or equal to 'a'? if yes, doit
j function
continue:
# ...
j function
这是最简单的版本。这是最容易理解的,就个人而言,我会这样做。它还消除了额外/无关的j
指令。
以前的版本必须“知道”A-Z
的值低于a-z
。他们可以“逃脱”,因为ASCII值是十进制的“硬连线”。
在C中,这不一定是一个好主意(即你使用字符常量)。 mips汇编程序允许使用字符常量,因此以下内容实际上是有效的:
function:
# increment $t0 to next char of input
blt $t0,'A',trylower # less than 'A'? if yes, try lowercase
ble $t0,'Z',continue # less than or equal to 'Z'? if yes, doit
trylower:
blt $t0,'a',function # less than 'a'? if yes, loop
bgt $t0,'z',function # greater than 'z'? if yes, loop
continue:
# ...
j function
有一个古老的格言:在你加快之前做好准备(来自Brian Kernighan和P.J. Plauger的“编程风格元素”)
这是一个构建查找表的额外版本。预构建它需要更多,但实际循环更快。
在各种版本中,blt
和bgt
是生成slti
,bne
和addi
,slti
,{{1}的伪操作}} 分别。所以,我们真的在谈论10条指令而不仅仅是4条指令。
因此,表构建可能值得获得更简单/更快的循环。
bne
这是一个带有预定义查找表的版本:
.data
isalpha: .space 256
.text
main:
la $s0,isalpha # get address of lookup table
li $t0,-1 # byte value
li $t2,1 # true value
# build lookup table
build_loop:
# increment $t0 to next char of input
addi $t0,$t0,1 # advance to next char
beq $t0,256,build_done # over edge? if so, table done
blt $t0,'A',build_lower # less than 'A'? if yes, try lowercase
ble $t0,'Z',build_set # less than or equal to 'Z'? if yes, doit
build_lower:
blt $t0,'a',build_loop # less than 'a'? if yes, loop
bgt $t0,'z',build_loop # greater than 'z'? if yes, loop
build_set:
addiu $t1,$s0,$t0 # point to correct array address
sb $t2,0($t1) # mark as a-z, A-Z
j build_loop # try next char
build_done:
function:
# increment $t0 to next char of input
addu $t1,$s0,$t0 # index into lookup table
lb $t1,0($t1) # get lookup table value
beqz $t1,function # is char one we want? if no, loop
continue:
# ...
j function
答案 1 :(得分:0)
由于 ASCII 范围恰好排列得很好,您可以 ori
强制大写字符变小,然后使用 sub / sltu 作为范围检查。 What is the idea behind ^= 32, that converts lowercase letters to upper and vice versa? 解释了这是如何/为什么起作用的。 (或 andi
清除小写位并强制任何字母字符为大写。)或者只是执行 addu / sltu 部分以仅检查 isupper
或 islower
而不是 { {1}}。
isalpha
很遗憾,MARS 的汇编器不允许将 ori $t1, $t0, 0x20 # set lower-case bit (optional)
addiu $t1, $t1, -97 # -'a' index within alphabet
sltiu $t1, $t1, 26 # idx <= 'z'-'a' create a boolean
bnez $t1, alphabetic # branch on it, original char still in $t0
作为数字文字,因此您必须手动将其写出。像 clang 或 GNU 汇编器这样的更好的汇编器真的可以让您编写 -'a'
。
(如果您的角色在内存中开始,addiu $t1, $t1, -'a'
或类似的将是一个好的开始。lbu $t0, 0($a0)
符号扩展加载也可以;这将正确拒绝设置了高位的字节它们是零或符号扩展的。我们要“接受”的范围仅包括有符号的正字节值。)
编译器知道这个技巧。例如:
lb
使用 MIPS GCC 编译为与相同的 asm (Godbolt compiler explorer)
int alpha(unsigned char *p) {
unsigned char c = *p;
c |= 0x20;
return (c>='a' && c <= 'z');
}
事实上,clang 甚至可以将 int alpha(unsigned char *p) {
unsigned char c = *p;
unsigned lcase = c|0x20;
unsigned alphabet_idx = lcase - 'a'; // 0-index position in the alphabet
bool alpha = alphabet_idx <= (unsigned)('z'-'a');
return alpha;
}
优化为 MIPS 或 RISC-V 的等效 asm。 (也包含在 Godbolt 链接中)。
另见 How can I implement if(condition1 && condition2) in MIPS?,它也显示了 ((c>='a' && c <= 'z') || (c>='A' && c <= 'Z'));
的范围检查技巧。