Skip to content

Latest commit

 

History

History
123 lines (82 loc) · 4.05 KB

ch2.adoc

File metadata and controls

123 lines (82 loc) · 4.05 KB

Chapter 2: Environment Calls

We mentioned environment calls (aka ecalls, though they’re also called system calls or syscalls in other languages like MIPS) in chapter 0 when we were going over our "Hello World" program, but what exactly are they?

Essentially, they are the built in functions of an operating system; in this case, the simple operating system of the RARS simulator. They provide access to all the fundamental features, like input and output to/from both the console and files, allocating memory, and exiting. Those are the basics but RARS supports many more, for things ranging from playing MIDI sounds, to getting a random number, to creating GUI dialogs.[1]

Table 1. Basic RARS supported ecalls
Name a7 Arguments Result

print integer

1

a0 = integer to print

print float

2

fa0 = float to print

print double

3

fa0 = double to print

print string

4

a0 = address of string

read integer

5

a0 = integer read

read float

6

fa0 = float read

read double

7

fa0 = double read

read string

8

a0 = address of input buffer
a1 = buffer size

works like C’s fgets

sbrk

9

a0 = size in bytes to allocate

a0 = address of allocated memory (sbrk is basically malloc but there is no free)

exit

10

program terminates

print character

11

a0 = character to print (ascii value)

read character

12

a0 = character read

open file

1024

a0 = address of filename
$a1 = flags

a0 = file descriptor (negative if error)

lseek

62

a0 = file descriptor, a1 = offset from base,
a2 = beginning(0), current(1), or end of the file(2)

a0 = selected position from beginning of the file or -1 if error

read from file

63

a0 = file descriptor
a1 = address of input buffer
a2 = max characters to read

a0 = number of characters read, -1 if error

write to file

64

a0 = file descriptor
a1 = address of output buffer
a2 = number of characters to write

a0 = number of characters written

close file

57

a0 = file descriptor

exit2

93

a0 = termination result

program terminates, returning number in a0 (only meaningful when run in the terminal, ignored in GUI)

As you can see, they really only cover the basics. You can read or write the different types, do file I/O using calls identical to POSIX functions (open, read, write, close; see man pages), allocate memory, and exit. Even so, they’re sufficient to build anything you want.

So, what does that table mean? How do these actually work?

The process is:

  1. Put the number for the ecall you want in a7

  2. Fill in the appropriate arguments, if any

  3. Execute the ecall with ecall

	li    a7, 1   # 1 is print integer
	li    a0, 42  # takes 1 arg in a0, the number to print
	ecall         # actually execute ecall

You can think of the above as print_integer(42);. Let’s look at an actual program that uses a few more ecalls next.

Examples

link:code/ecall_example.c[role=include]

I’m using fgets() instead of scanf("%s", name) because fgets works the same as the read string ecall (8).

link:code/ecall_example.s[role=include]

There a few things to note from the example.

We don’t declare global variables for age or height. We could, but there’s no reason to since we need them in registers to perform the addition anyway. Instead, we copy/save age to t0 so we can use a0 for 2 more ecalls, then add height to t0.

This is generally how it works. Use registers for local variables unless required to do otherwise. We’ll cover more about register use when we cover the RISC-V calling convention.

Another thing is when we print their name, we don’t put 4 in a7 again because it is still/already 4 from the lines above.

Lastly, many people will declare a string "\n" and use print string to print a newline, but it’s easier to use the print char ecall as we do right before exiting.