标准的工作方式是进行 exit(0) 系统调用:
li $v0, 10 # call number
syscall # exit()
如果你想在程序中使用jal,这也避免了将传入的$ra保存在main中的任何位置,所以很方便。
这对于在 Linux 等主流操作系统中运行的真实手写 asm 程序来说也更“现实”,而不仅仅是 MARS/SPIM 模拟的“玩具”系统。
(与 Linux exit(int) 不同,MARS/SPIM 玩具系统调用不会在 $a0 或其他任何地方采取退出状态。它只是退出。)
在 MARS 中,对于它所模拟的“玩具”系统,显然从底部掉线是一个有效的选择。但是,这在任何真正的硬件 CPU 中都不起作用;内存中总会有下一个东西,CPU 会尝试获取并执行它1。
MARS 和 SPIM 都没有尝试模拟像 Linux 这样的真实操作系统,只是提供自己的特定环境2。 MARS 与 SPIM 模拟的系统彼此之间存在一些细微差别,包括您发现的系统。
没有对错,只是不同:没有他们试图匹配/模仿的真实环境。
SPIM 甚至可以选择在模拟系统的内存 IIRC 中包含一些内核代码或类似的东西。我可能记错了,但如果不是,那么某些系统调用处理实际上可能由更多 MIPS 代码完成,更接近运行操作系统的真实 MIPS CPU。 (与 MARS 不同,在 MARS 中,系统调用实现纯粹是在您通过 syscall 调用的模拟器内的 Java 中,而不是根据 MIPS 指令和模拟硬件的设备驱动程序。)
在真正的操作系统下(例如带有 gcc 和 glibc 的 GNU/Linux),main 将是一个适当的函数,通常从 _start 进程入口点调用(在实际调用 main 之前间接通过 __libc_start_main 执行更多初始化操作)。 _start 是真正的进程入口点,是在用户空间中运行的第一条指令(模动态链接),并且不是函数(任何地方都没有返回地址);您唯一的选择是进行退出系统调用(或崩溃或永远运行)。当main 返回时,_start 将其返回值(int,因此在 $v0 中)作为 arg 传递给 exit library function which does cleanup stuff,就像刷新 stdio 缓冲区一样,然后进行 _exit 系统调用。
显然 SPIM 打算将它们的 main 标签类似于 C 中的 main 函数,或者至少它会获得一个有效的返回地址。如果它在$a0 和$a1 中获得int argc 和char *argv[],则IDK。
为了使jr $ra 工作,SPIM 必须将初始的$ra 设置为某个地址,就像您的 main 是从某个地方调用的一样。您可能会找到将 $v0 复制到 $a0,然后进行退出系统调用的代码。
有些人会混淆地使用main 作为无法返回的入口点的名称,不幸的是,我认为即使在现实世界的嵌入式开发中也是如此。在 GNU/Linux 系统的标准工具链(gcc / clang)中,进程入口点默认称为_start。
main 令人困惑,因为它是允许返回的 C 函数(由 asm 启动的东西调用)的标准名称。不能返回的东西不是函数,但在C 中,main 绝对是 一个函数。 C 是 Unix/Linux 系统编程的低级语言,许多其他语言都建立在 libc 和 CRT 启动代码的标准工具链之上。
脚注 1: 大多数 ISA 都有关于 PC 如何从 0xffffffc 包装到 0 或其他任何内容的规则,因此即使将您的代码放在地址空间的最末端也无法做到到达终点时它会自行停止。或者如果确实如此,那将是某种故障,没有退出到操作系统。 (在这种情况下,MARS 或 SPIM 充当操作系统,处理您运行的 syscall 指令等)。请注意,裸机上的实际操作系统无法“退出”,只能重置或关闭机器。它不会在任何可以退出的“下方”运行。
脚注 2:系统调用非常有限,例如没有光标移动,并且一些系统调用会做库函数(不是系统调用)在真实系统中会做的事情,例如int字符串转换。但 MARS/SPIM 仅将其作为 I/O 的一部分提供,没有 atoi 或 sprintf(buf, "%d", num)。这就是为什么“玩具”标签适用的原因,尤其是它们提供的一组系统调用,这与 Linux 的一组系统调用非常不。
但也适用于 MARS 具有的简单位图图形,以及 MARS 和 SPIM 默认的 no-branch-delay 默认选项。真正的 MIPS CPU 有一个分支延迟槽,直到 MIPS32r6 重新排列操作码并提供新的无延迟槽分支指令。
MARS 至少(也许是 SPIM)在其内置汇编器中对汇编时常量的支持也非常有限,例如你不能像在 MIPS 的 GNU 汇编器中那样在汇编时使用 .equ 或 msglen = . - msg 来计算 msg: .ascii "hello" 的长度。