测试值是否在两个范围之一内

时间:2016-10-12 13:41:18

标签: range ascii mips

我正在编写一个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

2 个答案:

答案 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的“编程风格元素”)

这是一个构建查找表的额外版本。预构建它需要更多,但实际循环更快。

在各种版本中,bltbgt是生成sltibneaddislti,{{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 部分以仅检查 isupperislower 而不是 { {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')); 的范围检查技巧。