如何比较存储的字符串和输入的字符串-MIPS

How to compare stored strings with inputted strings-MIPS

我正在编写一个程序,询问用户他们有哪个温度,然后获取该输入并转换和输出所有四个温度。我需要帮助让我的用户读入输入,以便它可以在我的分支 beq 中工作。我无法让它将输入 'f' 识别为等于存储的版本。

.data    
temptype: .asciiz "Enter temperature type i.e. f, c, k, r: "   
tempdegree: .asciiz "\n Enter degrees: "   
space: .space 2
tempx: .asciiz "Your temperature in celsius is: "
tempc: .asciiz "\nYour temperature in celsius is: "
tempf: .asciiz "\nYour temperature in fahrenheit is: "
tempk: .asciiz "\nYour temperature in kelvin is: "
tempr: .asciiz "\nYour temperature in rankine is: :"
kr: .float 459.67

.globl main

.text   
    main:   

        li $v0, 4   
        la $a0, temptype   
        syscall

        li $v0, 8
        la $a0, space
        #li $a1, 2
        move $t0, $a0
        syscall

        li $t1, 102
        #li $t1, 99
        #li $t1, 107
        #li $t1, 114
        syscall

        beq $t0, $t1, fahrenheit
        #beq $t0, $t1, celsius
        #beq $t0, $t1, kelvin
        #beq $t0, $t1, rankine
        syscall

        li $v0,10
        syscall 

    fahrenheit:

        li $v0, 4   
        la $a0, tempdegree   
        syscall

        li $v0, 5
        syscall

        move $t0, $v0

        li $v0, 4 
        la $a0, tempf
        syscall

        move $a0, $t0 
        li $v0, 1 
        syscall 

MIPS CPU(和任何其他常见的 none)没有 "compare strings" 指令,字符串不是 CPU 的原生类型,指令只处理本机类型,如单词和字节。

"String" 是一定数量(在某处定义,或在数据末尾使用终止符)的连续字符。什么是 "one character" 取决于使用的编码,在您的情况下(MARS 模拟器和 asm 编程的简单实践)您可以坚持使用旧的 ASCII 编码,其中单个字符正好是一个字节。 (仅供参考:使用现代软件,您将主要使用 UTF8 编码,就像这个网页一样,其中单个字符可以有不同的字节数,具体取决于您编码的字形,这使得通过 UTF8 编码的字符串编写任何字符串算法更有趣比你当前的任务。通常太有趣了。)

现在 CPU 寄存器的大小为 "word",这意味着它们是 32 位 "wide",即它们一次最多可以容纳 4 个 ASCII 字符(4 个字节) ,所以使用寄存器来存储整个字符串只允许非常 sho。海峡而且_没有别的。你可以这样做,但它不实用(除了 beq 会起作用,因为你可以将单词值 0x30303030 = "0000"0x31313131 = "1111"beq).

因此,大多数时候在 MIPS 初学者汇编程序中编程 "strings" 遵循以下模式:一些寄存器包含指向字符串第一个字母(字符串的第一个字节)的内存地址,最后一个 "character" 的字符串不是任何字母,而是零值,即所谓的 "null terminator".

当你想要比较字符串时,你创建了循环,它以两个指针开始(指向两个字符串 = 指向前两个字母)。将两个地址的字节加载到某个临时寄存器(即加载两者的第一个字母),比较它,如果它们不同,则字符串不同。如果相等,检查零(两个字符串结束 = 它们相等)。如果不为零,将两个地址前移一个,这样它们将指向下一个字母,并循环到开头。

但是在你的情况下,用户只能输入单个字母,而你只想比较单个字母,因此编写整个循环有点费力,你可以只加载那个字母并进行比较。

所以从顶部阅读你的来源,这些行将得到我的评论:

#li $a1, 2

为什么被注释掉了?您应该使用它来限制系统调用(我认为不设置任何默认值可能为零,因此不会发生输入)。此外,您可能对 syscall(v0=12) "read character" 而不是 "read string" 感兴趣,但我不确定它是如何在 MARS 中呈现给用户的(与用户体验相关),但让我们坚持使用服务 v0 =8 "read string" 和 2 字节长缓冲区。

现在在 syscall returns(用户确实输入了字母 "f")之后,地址 space 的内存将包含系统调用设置的两个字节:102、0 .

li $t1, 102

看起来很熟悉,但对于其他程序员来说很难阅读,使用 MARS 汇编程序您也可以使用这种方式来编写该数字:li $t1, 'f' - 简单的撇号告诉汇编程序您想要单个 ASCII 字符的值('ab' 在 MARS 中是错误的,只能使用单个字符,其他一些汇编程序可能会将 'ab' 翻译为两个字节值)

下一个未注释的指令是:

syscall

您在这里问的是哪个 MARS 服务?您没有在 v0 中设置任何值,也不需要任何服务,因此如果您要在调试器中单步执行代码,这对您来说应该毫无意义,如果您推断每条指令会发生什么.

然后是beq $t0, $t1, fahrenheit

此时 t1 等于 'f',并且 t0 等于该缓冲区第一个字节的地址,在期间也别名为 space 符号编译,它等于某个 32 位值,可能类似于 0x100000c。值 0x100000c102 肯定不相等,因此 beq 永远不会跳转到标签 fahrenheit.

要比较缓冲区中的第一个字母,首先从内存中获取它的值,如 lb $t2, ($t0),从 t0 中的地址加载字节值(高级信息:lb 将签名-将8位值扩展为32位值。ASCII的基本可打印字符都小于128,因此不需要处理负值,但如果字母'f'编码为140,则使用lb 将该值加载到 t2 会产生 32 位值 -116,而不是 140 ...正如我所写,基本 ASCII 只有 7 位,所以只有正值,这有效正如预期的那样,102 被加载为 102)。

然后你可以用 beq $t2, $t1, fahrenheit 取得更大的成功,因为现在它会将 ASCII 字符与 ASCII 字符进行比较。

您还可以使用 MARS MIPS 汇编伪指令beq $t2, 'f', fahrenheit。 MARS 会将其编译为两条本机指令:

addi $at, $zero, 102 # 102 = 'f', $at = , $zero = [=13=]
beq  $at, $t2, fahrenheit

省去一些打字,这在编程中很好,只要在阅读时有意义(一旦你为了简短的写作而开始缩短你的源代码,你就错了,在编程中源代码是为了被阅读而编写的,与阅读成本相比,编写成本可以忽略不计)。在这种情况下 beq $t2, 'f', label 对我来说看起来很可读,所以我更喜欢那样。

这应该足以回答您的两个问题,显式问题(如何在循环中逐字符比较字符串 =)和隐式问题(如何比较用户的单个字母与 'f').