Is "jr $ra" required to end a MIPS assembly language program? (MARS & QtSpim behave differently!)

Chris

If you put a jr $ra at the end of a MIPS assembly language program in MARS, you will get an error message saying:

invalid program counter value: 0x00000000

Example 1 below fails:

.data

theMsg: .asciiz "Hello World\n"

.text
.globl main

main:   li $v0, 4       
        la $a0, theMsg  
        syscall         
        
        jr $ra

      

Example 2 below works:

.data

theMsg: .asciiz "Hello World\n"

.text
.globl main

main:   li $v0, 4       
        la $a0, theMsg  
        syscall

     
    

MARS says "program is finished running (dropped off bottom)" but there are no error messages.

Now, if you run Example 2 in QtSpim, you will get an error saying:

Attempt to execute non-instruction at 0x00400030

If you run Example 1 in QtSpim, it works just fine.

Can anyone shed some light on this?
Which MIPS simulator is right?

Peter Cordes

The standard works-everywhere way is making an exit(0) system call (http://courses.missouristate.edu/kenvollmar/mars/help/syscallhelp.html).

   li $v0, 10         # call number
   li $a0, 0          # arg
   syscall            # exit(0)

That also avoids having to save the incoming $ra anywhere in main if you want to use jal inside your program, so it's convenient.

That's also more "realistic" for real-world hand-written asm programs running in a mainstream OS like Linux, not just the "toy" system that MARS/SPIM simulate.


In MARS, apparently dropping off the bottom is a valid option for the "toy" system that it simulates. That never works in any real-hardware CPU, though; there's always something next in memory and the CPU will try to fetch and execute it1.

Neither MARS nor SPIM are trying to emulate a real OS like Linux, just provide their own specific environment2. The systems that MARS vs. SPIM simulate have some minor differences from each other, including the one you found.

Neither one is right or wrong, just different: there is no real-world environment that they're trying to match / emulate.

SPIM might even have an option to include some kernel code or something like that in the simulated system's memory, IIRC. I may be misremembering, but if not then some of the syscall handling might actually be done by more MIPS code, coming closer to a real MIPS CPU running an OS. (As opposed to MARS where the system-call implementation is purely in Java inside the simulator that you're calling into via syscall, not in terms of MIPS instructions and device drivers for simulated hardware.)

Under a real OS (e.g. GNU/Linux with gcc and glibc), main would be a proper function called normally from the _start process entry point (indirectly via __libc_start_main to do some more init stuff before actually calling main). _start is the real process entry point, first instruction that runs in user-space (modulo dynamic linking), and is not a function (no return address anywhere); your only option is to make an exit system call (or crash or keep running forever). When main returns, _start passes its return value (an int thus in $v0) as an arg to the exit library function which does cleanup stuff like flushing stdio buffers, then makes an _exit system call.

Apparently SPIM intends their main label to be like a C main function, or at least it gets a valid return address. IDK if it gets int argc and char *argv[] in $a0 and $a1.

For jr $ra to work, SPIM must be setting the initial $ra to some address, like your main was called from somewhere. You'll probably find code that which copies $v0 to $a0, then makes an exit system call.

Some people do confusingly use main as a name for entry-points that can't return, unfortunately, I think even in real-world embedded development. In standard toolchains for GNU/Linux systems (gcc / clang), the process entry point is by default called _start.

main is confusing because it's the standard name for a C function (called by asm startup stuff), which is allowed to return. Something you can't return from isn't a function, but in C, main definitely is a function. C is the low-level language of Unix/Linux systems programming, and many other languages build on top of that standard toolchain of libc and CRT startup code.


Footnote 1: Most ISAs have rules for how PC can wrap from 0xffffffc to 0 or whatever, so even putting your code at the very end of the address space can't make it stop by itself when reaching the end. Or if it does, it will be some kind of fault, not exiting to the OS. (In this case the MARS or SPIM are acting as the OS, handling the syscall instructions you run among other things). Note that an actual OS on bare metal has no way to "exit", only reset or power-off the machine. It's not running "under" anything it can exit back to.

Footnote 2: With very limited system calls, e.g. no cursor movement, and some syscalls which do things that library functions (not syscalls) would do in a real system, e.g. int<->string conversion. But MARS/SPIM only provide that as part of I/O, no atoi or sprintf(buf, "%d", num). This is why the "toy" label applies, especially to the set of system calls they provide, which is very much not like Linux's set of system calls.

But also to stuff like the simple bitmap graphics MARS has, and to the no-branch-delay default option both MARS and SPIM default to. Real MIPS CPUs have a branch-delay slot, until MIPS32r6 re-arranged the opcodes and provided new no-delay-slot branch instructions.

MARS at least (and maybe SPIM) have pretty limited support for assemble-time constants in their built-in assembler as well, e.g. you can't do .equ or msglen = . - msg to compute a length of a msg: .ascii "hello" at assemble time like you could in GNU assembler for MIPS.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

TOP Ranking

  1. 1

    Failed to listen on localhost:8000 (reason: Cannot assign requested address)

  2. 2

    pump.io port in URL

  3. 3

    How to import an asset in swift using Bundle.main.path() in a react-native native module

  4. 4

    Loopback Error: connect ECONNREFUSED 127.0.0.1:3306 (MAMP)

  5. 5

    Compiler error CS0246 (type or namespace not found) on using Ninject in ASP.NET vNext

  6. 6

    BigQuery - concatenate ignoring NULL

  7. 7

    Spring Boot JPA PostgreSQL Web App - Internal Authentication Error

  8. 8

    ggplotly no applicable method for 'plotly_build' applied to an object of class "NULL" if statements

  9. 9

    ngClass error (Can't bind ngClass since it isn't a known property of div) in Angular 11.0.3

  10. 10

    How to remove the extra space from right in a webview?

  11. 11

    Change dd-mm-yyyy date format of dataframe date column to yyyy-mm-dd

  12. 12

    Jquery different data trapped from direct mousedown event and simulation via $(this).trigger('mousedown');

  13. 13

    maven-jaxb2-plugin cannot generate classes due to two declarations cause a collision in ObjectFactory class

  14. 14

    java.lang.NullPointerException: Cannot read the array length because "<local3>" is null

  15. 15

    How to use merge windows unallocated space into Ubuntu using GParted?

  16. 16

    flutter: dropdown item programmatically unselect problem

  17. 17

    Pandas - check if dataframe has negative value in any column

  18. 18

    Nuget add packages gives access denied errors

  19. 19

    Can't pre-populate phone number and message body in SMS link on iPhones when SMS app is not running in the background

  20. 20

    Generate random UUIDv4 with Elm

  21. 21

    Client secret not provided in request error with Keycloak

HotTag

Archive