源文件格式

EduMIPS64 尝试遵循其他 MIPS64 和 DLX 模拟器中使用的惯例,这样老用户就不会对其语法感到困惑。

源文件中有两个部分,即 data 部分和 code 部分,分别由 .data.code 指令引入。在下面的列表中,你可以看到一个非常基本的 EduMIPS64 程序:

; 这是注释
        .data
label:  .word   15     ; This is an inline comment

        .code
        daddi   r1, r0, 0
        syscall 0

为了区分每行源代码的不同部分,可以使用空格和制表符的任意组合,因为解析器会忽略多个空格,只 空格来分隔标记。

注释可以使用 “;” 字符指定,该字符后面的所有内容都将被忽略。因此,注释可以内联(指令之后)或单独一行中使用。

标签可在代码中用于引用内存单元或指令。 标签不区分大小写。每行源代码只能使用一个标签。 标签可以在有效数据声明或指令的上方指定一行或多行,前提是标签和声明之间除了注释和空行外没有其他内容。

内存限制

EduMIPS64 为数据( .data 部分,上限为 640 kB,即 80000 个 64 位值)和指令( .code 部分,上限为 128 kB,即 32000 条指令,每条指令占用 32 位)设置了固定的内存大小。

这些限制是模拟器硬编码的。

.data 部分

.data 部分包含的命令指定了程序执行开始前内存的填充方式。.data 命令的一般格式为:

[label:] .datatype value1 [, value2 [, ...]]

EduMIPS64 支持不同的数据类型,详见下表。

Type

Directive

Bits required

Byte

.byte

8

Half word

.word16

16

Word

.word32

32

Double Word

.word or .word64

64

请注意,双字可以由 .word 指令或 .word64 指令引入。

所有数据类型都解释为带符号。这意味着, .data 部分中的整数字面量必须介于 -2^(n-1) 和 2^(n-1) - 1 之间(包括 2^(n-1))。

使用单个指令声明数据元素列表与使用相同类型的多个指令声明数据元素列表有很大区别。EduMIPS64 一旦发现数据类型标识符, 就会从下一个 64 位双字开始写入,因此下面列表中的第一条 .byte 语句将把数字 1、2、3 和 4 放在 4 个字节的空间中, 占用 32 位,而后面四行的代码将把每个数字放在不同的内存单元中,占用 32 个字节:

.data
.byte    1, 2, 3, 4
.byte    1
.byte    2
.byte    3
.byte    4

在下表中,内存使用字节大小的单元表示,每行宽 64 位。 表中每行左侧的地址指的是最右侧的内存单元,它是每行 8 个单元中地址最低的单元。

0

0

0

0

0

4

3

2

1

8

0

0

0

0

0

0

0

1

16

0

0

0

0

0

0

0

2

24

0

0

0

0

0

0

0

3

36

0

0

0

0

0

0

0

4

有一些特殊指令需要讨论: .space.ascii.asciiz

.space 指令用于在内存中留出一些空余空间。它接受一个整数作为参数,表示必须留空的字节数。当您必须为计算结果在内存中留出一些空间时,该指令非常方便。

.ascii 指令接受包含任何 ASCII 字符的字符串,以及下表中描述的一些类似 C 语言的特殊转义序列,并将这些字符串存入内存。

Escaping sequence

Meaning

ASCII code

\0

Null byte

0

\t

Horizontal tabulation

9

\n

Newline character

10

\”

Literal quote character

34

\

Literal backslash character

92

.asciiz “指令的行为与”.ascii “命令完全相同,不同之处在于它会自动以空字节结束字符串。

.code 部分

.code 部分包含的命令指定了程序启动时内存的填充方式。 .code 命令的一般形式是:

[label:] instruction [param1 [, param2 [, param3]]]

代码*部分可以用 .text 别名指定。

参数的数量和类型取决于指令本身。

指令可以使用三种类型的参数:

  • 寄存器 寄存器参数用大写或小写的 r$ 表示,后面跟寄存器的编号(0 到 31 之间),如 “r4”、”R4”或”$4”;

  • 立即值 立即值可以是一个数字或标签;数字可以以 10 进制或 16 进制指定:10 进制数字只需写入数字即可,而 16 进制数字则需在数字前加上前缀 “0x”。立即值的前面可以 字符。

  • 地址 地址由一个立即值和一个寄存器名称组成,寄存器名称用括号括起来。寄存器的值将作为基数,立即值将作为偏移量。

立即值的大小受指令位编码中可用位数的限制。

当可以使用 16 位立即值时,例如在 ALU I-Type 指令中,也可以使用内存标签作为立即值。汇编程序 将把标签指向的内存地址作为立即值。

你可以使用标准的 MIPS 汇编别名来寻址前 32 个寄存器,将别名附加到标准寄存器前缀之一,如 “r”、”$” 和 “R “等标准寄存器前缀。请参见下表。

Register

Alias

0

zero

1

at

2

v0

3

v1

4

a0

5

a1

6

a2

7

a3

8

t0

9

t1

10

t2

11

t3

12

t4

13

t5

14

t6

15

t7

16

s0

17

s1

18

s2

19

s3

20

s4

21

s5

22

s6

23

s7

24

t8

25

t9

26

k0

27

k1

28

gp

29

sp

30

fp

31

ra

#include 命令

源文件可以包含 #include filename 命令,其作用是将文件 filename 中的内容替换为命令行。

如果你想包含外部例程,这条命令是非常有用的,而且它还带有循环检测算法,如果你试图在文件 B.s 中执行”#include A.s”,又在文件 A.s 中执行”#include B.s”,它就会发出警告。