代码示例

在本章中,您将看到一些对理解 EduMIPS64 如何工作非常有用的示例列表,以了解 EduMIPS64 如何工作。

SYSCALL

重要的是要了解 SYSCALL 1-4 的示例引用了`print.s` 文件,这是 SYSCALL 5 的示例。如果要运行 示例,应将该示例的内容复制到一个名为`print.s` 的文件,并将其包含在代码中。

有些示例会使用已存在的文件描述符,即使它并不真正存在。如果要运行这些示例,请使用 SYSCALL 1 示例打开一个文件。

SYSCALL 0

调用 SYSCALL 0 时,程序停止执行。 示例:

.code
    daddi   r1, r0, 0    ; saves 0 in R1
    syscall 0            ; exits

SYSCALL 1

打开文件的示例程序:

                .data
error_op:       .asciiz     "Error opening the file"
ok_message:     .asciiz     "All right"
params_sys1:    .asciiz     "filename.txt"
                .word64     0xF

                .text
open:           daddi       r14, r0, params_sys1
                syscall     1
                daddi       $s0, r0, -1
                dadd        $s2, r0, r1
                daddi       $a0,r0,ok_message
                bne         r1,$s0,end
                daddi       $a0,r0,error_op

end:            jal         print_string
                syscall 0

                #include    print.s

在前两行中,我们将包含错误信息和成功信息的字符串写入内存,并将其传递给 print_string 函数。 给它们加上两个标签。print_string 函数包含在 print.s 文件中。

接下来,我们向内存写入 SYSCALL 1 所需的数据(第 4、5 行)、要打开的文件的路径(第 6、7 行 要打开的文件的路径(如果我们使用 读取 或 读/写模式),并在下一个内存单元中写入一个定义打开模式的整数。

在本例中,文件使用以下模式打开: | o_rdwr | o_creat | _append。数字 数字 15(0xF,以 16 为基数)来自这三种模式的值之和(3 + 4 + 8)。

我们给这些数据加上一个标签,以便以后使用。

在 .text 部分,我们将 params_sys1 的地址(对编译器来说是一个数字)保存在寄存器中。 在 .text 部分,我们将 params_sys1 的地址(对编译器来说是一个数字)保存在寄存器 r14 中;接下来我们可以调用 SYSCALL 1 并将 r1 的内容保存在 $sys1 中。 将 r1 的内容保存在 $s2 中,这样我们就可以在程序的其余部分中使用它了 (例如,使用其他 SYSCALL)。

然后调用 print_string 函数,将 error_op 作为参数传递,如果 r1等于-1(第 13-14 行),否则将 ok_message 作为参数(第 12 和 14 行)。 则将 ok_message 作为参数(第 12 和 16 行)。

SYSCALL 2

关闭文件的示例程序:

                .data
params_sys2:    .space 8
error_cl:       .asciiz     "Error closing the file"
ok_message:     .asciiz     "All right"

                .text
close:          daddi       r14, r0, params_sys2
                sw          $s2, params_sys2(r0)
                syscall     2
                daddi       $s0, r0, -1
                daddi       $a0, r0, ok_message
                bne         r1, $s0, end
                daddi       $a0, r0, error_cl

end:            jal         print_string
                syscall     0

                #include    print.s

首先,我们为 SYSCALL 2 的唯一参数,即必须关闭的文件的文件描述符(第 2 行)保存一些内存,并给它一个标签,以便以后访问。

接下来,我们将包含错误信息和成功信息的字符串放入内存,这些字符串将传递给 print_string 函数(第 3、4 行)。

在 .text 部分,我们将 params_sys2 的地址保存在 r14 中;然后我们就可以 调用 SYSCALL 2。

现在我们使用 error_cl 作为参数调用 print_string 函数,如果 r1 则调用 print_string 函数(第 13 行),如果一切顺利,则使用 ok_message 作为参数调用 print_string 函数(第 11 行)。则使用 ok_message 作为参数调用该函数(第 11 行)。

注: 此列表需要寄存器 $s2 包含要调用的文件的文件描述符。文件描述符。

SYSCALL 3

从文件读取 16 个字节并保存到内存的示例程序:

                .data
params_sys3:    .space      8
ind_value:      .space      8
                .word64     16
error_3:        .asciiz     "Error while reading from file"
ok_message:     .asciiz     "All right"

value:          .space      30

                .text
read:           daddi       r14, r0, params_sys3
                sw          $s2, params_sys3(r0)
                daddi       $s1, r0, value
                sw          $s1, ind_value(r0)
                syscall     3
                daddi       $s0, r0, -1
                daddi       $a0, r0,ok_message
                bne         r1, $s0,end
                daddi       $a0, r0,error_3

end:            jal         print_string
                syscall     0

                #include    print.s

.data 部分的前 4 行包含 SYSCALL 3 的参数、我们必须从中读取的文件描述符、SYSCALL 必须保存读取数据的内存地址以及要读取的字节数。我们给那些稍后必须访问的参数加上标签。接下来,像往常一样,我们将包含错误信息和成功信息的字符串放入其中。

在 .text 部分,我们将 params_sys3 的地址保存到寄存器 r14 中,并在 SYSCALL 参数的内存单元中保存文件描述符(我们假设保存在 $s2 中)和用于保存读取字节的地址。

接下来,我们可以调用 SYSCALL 3,然后调用 print_string 函数 根据操作成功与否,将 error_3 或 ok_message 作为参数传递。

SYSCALL 4

向文件写入字符串的示例程序:

                .data
params_sys4:    .space      8
ind_value:      .space      8
                .word64     16
error_4:        .asciiz     "Error writing to file"
ok_message:     .asciiz     "All right"
value:          .space      30

                .text

write:          daddi       r14, r0,params_sys4
                sw          $s2, params_sys4(r0)
                daddi       $s1, r0,value
                sw          $s1, ind_value(r0)
                syscall     4
                daddi       $s0, r0,-1
                daddi       $a0, r0,ok_message
                bne         r1, $s0,end
                daddi       $a0, r0,error_4

end:            jal         print_string
                syscall     0

                #include    print.s

.data 部分的前 4 行包含 SYSCALL 4 的参数、我们必须读取的文件描述符、SYSCALL 必须读取的内存地址、要写入的字节数。我们给那些稍后必须访问的参数加上标签。接下来,像往常一样,我们将包含错误信息和成功信息的字符串放入其中。

在 .text 部分,我们将 params_sys4 的地址保存到寄存器 r14 中,在 SYSCALL 参数的内存单元中保存文件描述符(我们假设保存在 $s2 中)和我们必须读取写入字节的地址。

接下来我们可以调用 SYSCALL 3,然后根据操作的成功与否调用 print_string 函数,参数为 error_3 或 ok_message。

SYSCALL 5

包含将 $a0 中的字符串打印到标准输出的函数的示例程序:

                .data
params_sys5:    .space  8

                .text
print_string:
                sw      $a0, params_sys5(r0)
                daddi   r14, r0, params_sys5
                syscall 5
                jr      r31

第二行用于为 SYSCALL 必须打印的字符串保存空间,由 .text 部分的第一条指令填充,该指令假定 $a0 中有要打印的字符串地址。

下一条指令将字符串的地址放入 r14,然后我们就可以调用 SYSCALL 5 打印字符串了。最后一条指令将程序计数器设置为 r31 中的内容,这是 MIPS 通常的调用习惯。

一个更复杂的 SYSCALL 5 使用示例

SYSCALL 5 使用了一种并不简单的参数传递机制,下面的示例将对此进行说明:

                .data
format_str:     .asciiz   "%dth of %s:\n%s version %i.%i is being tested!"
s1:             .asciiz   "June"
s2:             .asciiz   "EduMIPS64"
fs_addr:        .space    4
                .word     5
s1_addr:        .space    4
s2_addr:        .space    4
                .word     0
                .word     5
test:
                .code
                daddi     r5, r0, format_str
                sw        r5, fs_addr(r0)
                daddi     r2, r0, s1
                daddi     r3, r0, s2
                sd        r2, s1_addr(r0)
                sd        r3, s2_addr(r0)
                daddi     r14, r0, fs_addr
                syscall   5
                syscall   0

格式字符串的地址被放入 R5,其内容随后被保存到内存中的 fs_addr 地址。字符串参数的地址被保存到 s1_addr 和 s2_addr。这两个字符串参数与格式字符串中的两个 %s 占位符相匹配。

从内存来看,与占位符相匹配的参数显然是紧跟在格式字符串地址之后存储的:数字与整数参数相匹配,而地址与字符串参数相匹配。在 s1_addr 和 s2_addr 位置,存放着我们要打印的两个字符串的地址,而不是 %s 占位符。

示例的执行将显示 SYSCALL 5 如何处理复杂的格式字符串,如存储在 format_str 中的字符串。