Contents
Introduction
RARS is the RISC-V Assembler and Runtime System, meaning it will assemble your code as well as run your code! The nice thing about RARS is that it allows you to see the intermediate results as the machine runs your code. You can step one instruction at a time and observe the registers changing. This makes it much easier to debug your code since you can step to a certain section and see if it is working properly.
Layout
Clicking File > New will create a new, unsaved file. When you save the file, make sure you navigate to a place, such as Documents. On many Windows machines, RARS will initially try to save in the Windows\System32 directory, which you will not be able to save into.
The source code editor is where you will write the assembly instructions, assembler directives, labels, and so forth. This is the file that the assembler will attempt to assemble when you click Run > Assemble (F3 on Windows).
The register file is a nice at-a-glance view of your registers. When a register is updated by an instruction, it will be highlighted in dark green.
The input / output section shows two different things (1) any assembler and runtime messages, such as errors and (2) interaction between you and the runtime system. For all intents and purposes, this is your console.
Editing
Assembling
Running
System Calls
All system calls require that you put the system call number in the a7 register. Then, the parameters of the system call (if applicable) go into registers a0 – a6. After all of that is set up, you will then execute the system call by using the instruction ecall. The following tables outline some of the system calls you will be using. However, you can also see all of the system calls by reading the help document directly in RARS.
Output (Printing) System Calls
Usage | a7 Number | Arguments | Returns |
---|---|---|---|
Print Integer | 1 | a0 = integer to print | Nothing |
Print Float | 2 | fa0 = 32-bit float to print | Nothing |
Print Double | 3 | fa0 = 64-bit float to print | Nothing |
Print String | 4 | a0 = address of NULL-terminated string | Nothing |
Print Integer as Hex | 34 | a0 = integer to print as hex | Nothing |
Print Integer as Binary | 35 | a0 = integer to print | Nothing |
Input (Reading) System Calls
Usage | a7 Number | Arguments | Returns |
---|---|---|---|
Read Integer | 5 | Nothing | a0 = integer that was read |
Read Float | 6 | Nothing | fa0 = 32-bit float that was read |
Read Double | 7 | Nothing | fa0 = 64-bit float that was read |
Read String | 8 | a0 = address of string buffer a1 = maximum number of bytes to read | Nothing |
Heap System Call
Recall that the heap is managed by the operating system, and we use a function to allocate memory, either new (C++) or malloc (C). These functions boil down to a system call.
Usage | a7 Number | Arguments | Returns |
---|---|---|---|
Allocate | 9 | a0 = number of bytes to allocate | a0 = memory address of allocated block |
There is no deallocation system call. You have the heap memory for the duration of the running program. However, unlike the stack, we can ask for as many bytes as we want without considering alignment.
Termination System Calls
A program doesn’t actually quit after main returns. When this occurs, the program goes back into assembly, which then calls the exit system call. This is how programs actually terminate. There are two exit system calls in RARS. One that takes a parameter (a7=93), and one that does not (a7=10). Do not use the one that does NOT take a parameter (a7=10). The one that does not take a parameter has a bug.
Usage | a7 Number | Arguments | Returns |
---|---|---|---|
Exit (DO NOT USE) | 10 | Nothing | Nothing |
Exit2 | 93 | a0 = exit number | Nothing |
System Call Examples
Printing and Reading Example
The following example uses two of the system calls. The first system call is number 4, which prints a string to the screen. Then, the second system call waits for the user to enter a string, up to 63 bytes. The reason we use 63 bytes instead of 64 is because we need to store the NULL (0) terminator.
.data prompt: .asciz "Enter a string: " .text .global main main: # Allocate space for the input string addi sp, sp, -64 # Output the prompt li a7, 4 la a0, prompt ecall # Read the input string li a7, 8 # String will be stored on the stack mv a0, sp # Maximum number of bytes is 63 since we need \0 li a1, 63 ecall addi sp, sp, 64 # Get ready to exit with number 0 (success) li a0, 0 li a7, 93 ecall
Heap Example
.text .global main main: # Allocate 16 heap bytes li a7, 9 li a0, 16 ecall # a0 now contains the memory address of the heap sd zero, 0(a0) sd zero, 8(a0) # Get ready to exit with number 0 (success) li a0, 0 li a7, 93 ecall