Contents

  1. Introduction
  2. Layout
  3. Editing
  4. Assembling
  5. Running
  6. System Calls

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

RARS Main Screen

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

Usagea7 NumberArgumentsReturns
Print Integer1a0 = integer to printNothing
Print Float2fa0 = 32-bit float to printNothing
Print Double3fa0 = 64-bit float to printNothing
Print String4a0 = address of NULL-terminated stringNothing
Print Integer as Hex34a0 = integer to print as hexNothing
Print Integer as Binary35a0 = integer to printNothing
Printing System Calls

Input (Reading) System Calls

Usagea7 NumberArgumentsReturns
Read Integer5Nothinga0 = integer that was read
Read Float6Nothingfa0 = 32-bit float that was read
Read Double7Nothingfa0 = 64-bit float that was read
Read String8a0 = address of string buffer
a1 = maximum number of bytes to read
Nothing
Reading System Calls

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.

Usagea7 NumberArgumentsReturns
Allocate9a0 = number of bytes to allocatea0 = memory address of allocated block
Heap System Call

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.

Usagea7 NumberArgumentsReturns
Exit (DO NOT USE)10NothingNothing
Exit293a0 = exit numberNothing
Termination System Calls

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