Skip to main content

The ecall instruction is a special command in RISC-V, and corresponds to a environment/system call. For this project, we’ve created helper functions in utils.s that wrap around the various different ecalls for you to use. Do not make ecalls directly in your own code. Use these helper functions instead.

All of these functions are documented in inline comments in utils.s, alongside their arguments and return values. The most important of these are highlighted below.

  • print_int, print_str, and print_char for printing values
  • fopen, fread, fwrite, and fclose for reading/writing to files
  • exit to quit the program with a zero exit code (no error)
  • exit2 to quit the program with the integer in a1 as the exit code
  • malloc, which allocates a0 bytes on the heap, and returns a pointer to them in a0.
  • free, which frees heap memory specified by the pointer in a0
  • print_int_array, which prints out all elements of an integer array

Ecalls

Venus currently supports the following environment calls through ecall instruction. To use an environment call, load the ecall code into register a0 and load any arguments into a1 - a7 or fa0-fa7 (floating-point) registers. You can invoke the ecall either directly or by invoking the helper function through util.s

Prints integer in register a1.

Example

.globl __start

.text

__start:
  li a0, 1   # ecall code
  li a1, 0xa # integer to print
  ecall
  # If util.s is imported
  ...
  li a0, 1   # ecall code
  jal print_int

Ecall code

  • a0 = 1

Arguments

  • a1 = integer to print

Prints null-terminated string whose address is in register a1.

``C void print_str(char *a1)

#### Example

```python
.globl __start

.rodata

  msg: .asciiz "Hello World!!!"

.text

__start:
  li a0, 4   # ecall code
  la a1, msg # string to print
  ecall
  # If util.s is imported
  ...
  la a1, msg
  jal print_str

Ecall code

  • a0 = 4

Arguments

  • a1 = null-terminated string address

Read Int

Reads an integer from address and stores the result in register a0.

int atoi(char* a1)

Example

.globl __start
.data
msg: .asciiz "5"

.text

__start:
  li a0, 5
  la a1, msg # ecall code
  ecall
# If util.s is imported
  la a1, msg
  jal atoi

Ecall code

  • a0 = 5

Arguments

  • a1 = null-terminated string address

Sbrk

Stores a pointer to a block of memory containing n additional bytes in register a0. This pointer is word aligned.

void *sbrk(int a1)

Example

.globl __start

.text

__start:
  li a0, 9   # ecall code
  li a1, 100 # number of bytes
  ecall
# If util.s is imported
  li a1,100
  jal sbrk

Ecall code

  • a0 = 9

Arguments

  • a1 = number of bytes to reserve

Return value**

  • a0 - pointer to allocated region

Malloc

(pseudo call that builds on sbrk)

void* malloc(int a0)
# If util.s is imported
  li a0,100
  jal malloc

Arguments

  • a0 = number of bytes to reserve

Return value**

  • a0 - pointer to allocated region

Exit

Stops a program from running.

void noreturn exit()

Example

.globl __start

.text

__start:
  li a0, 10 # ecall code
  ecall
# If util.s is imported
  jal exit

Ecall code

  • a0 = 10

Arguments

void print_char(char a1)

Prints a character whose ascii code is in register a1.

Example

.globl __start

.text

__start:
  li a0, 11  # ecall code
  li a1, 'a' # character to print
  ecall
li a1,'a'
jal print_char

Ecall code

  • a0 = 11

Arguments

  • a1 = ascii code to print

fopen

Opens a file that we can then read and/or write to, depending on the permission bit. Returns a file descriptor, which is a unique integer tied to the file. Must be called on a file before any other operations can be done on it.

Example

.globl __start

.rodata
  # open flags
  O_RD:  .word 0b00000000
  O_RDWR_CREAT: .word 0b0000001
  # pathname
  path: "example.txt"

.text

__start:
  li a0, 13      # ecall code
  la a1, path    # pathname address
  li a2,ORDWR_CREAT # w+ . Open for write. Create file if it does not exist.
  jal fopen

Ecall code

  • a0 = 13

Arguments

  • a1 = is a pointer to a string containing the filename of the file to open
  • a2 = open flags a2 is an integer denoting the permissions we open the file with. For example, we can open the file with read permissions, which prevents us from writing to it. For this project, we only really care about a few basic permission bits: 0, which corresponds to r for read only permission, and 1 which corresponds to w for write only permission. Note that w will overwrite the file if it already exists, and create it if it doesn’t.

Return

a0 = is a file descriptor, which is a unique integer tied to the file. We will call future file-related functions on this file descriptor, so we know which opened file we’re reading/writing/closing. On failure, a0 is set to -1.

fread

int fread(int a1, void *a2, size_t a3)

Reads a given number of bytes from a file into a buffer, which is a preallocated chunk of memory to store the bytes. Note that repeated reads will read consecutive bytes from the file. For example, two freads of 8 bytes on a file will read the first 8 bytes and then the second 8 bytes. It will not read the same 8 bytes twice.

  • Arguments:**
  • Return
    • a0 is the number of bytes actually read from the file. If the number of bytes actually read differs from the number of bytes specified in the input then then we either hit the end of the file or there was an error.

Reads data into a buffer whose address is in register a2. Stores the number of bytes that were read in register a0. If value is negative, then an error occurred. The buffer size should be greater or equal to the number bytes to read.

Example

.globl __start

.data
  read: .zero 1024

.text

__start:
  li a0, 14   # ecall code
  li a1, 1    # file descriptor
  la a2, read # buffer address
  li a3, 10   # number of bytes to read
  ecall
# If util.s is imported
  li a1, 1    # file descriptor
  la a2, read # buffer address
  li a3, 10   # number of elements to read
  jal fread

Ecall code

  • a0 = 14

Arguments

  • a1 is the file descriptor of the file we want to read from, previously returned by fopen.
  • a2 is a pointer to the buffer that we’re going to read the bytes from the file into. This must be an appropriate amount of memory that was allocated before calling the function, and passed in as a pointer.
  • a3 is the number of bytes to read from the file.

Return

  • a0 - number of bytes read. if a0 < a3, you have reached end of file.

fwrite

int fwrite(int a1, void *a2, size_t a3, size_t a4)

Writes a given number of elements of a given size. Like fread, subsequent writes to the same file do not overlap, but are rather appended to each other. Note that unlike fread, we don’t pass in the total number of bytes but rather the total number of elements and the size of each element in bytes. We can multiply the two to find the total number of bytes written.

Additionally, note that our writes aren’t actually saved until we run fclose or fflush.

Example

.globl main

.data
  write: .asciiz "Hello World"

.text

main:
  li a0, 15    # ecall code
  li a1, 1     # file descriptor
  la a2, write # buffer address
  li a3, 3     # number of bytes to write
  li a4, 4     # size of each element 4 bytes. Totally a3*a4 = 3*4 = 12 bytes written
  ecall
  # If util.s is imported
  li a1, 1     # file descriptor
  la a2, write # buffer address
  li a3, 3     # number of bytes to write
  li a4, 4     # size of each element 4 bytes. Totally a3*a4 = 3*4 = 12 bytes written
  jal fwrite

Ecall code

  • a0 = 15

Arguments

  • a1 is the file descriptor of the file we want to write to, previously returned by fopen.
  • a2 is a pointer to a buffer containing what we want to write to the file.
  • a3 is the number of elements to write out of the buffer
  • a4 is the size of each buffer element in bytes

Return

  • a0 is the number of elements actually written to the file. If a0 != a3, then we either hit the end of the file or there was an error.

fclose

Closes an open file descriptor. Stores a 0 upon success in register a0, and a -1 upon failure.

int fclose(int a1)

Example

.globl __start

.text

__start:
  li a0, 16 # ecall code
  li a1, 1  # file descriptor
  ecall
# if util.s is imported
  li a1, 1     # file descriptor
  jal fclose

Ecall code

  • a0 = 16

Arguments

  • a1 = file descriptor

Exit2

Stops the program from running and exits

void noreturn exit2(int a1)

Example

.globl __start

.text

__start:
  li a0, 17 # ecall code
  li a1, 1  # status code value
  ecall
# if util.s is imported
  li a1, 1     # exit2 code
  jal exit2

Ecall code

  • a0 = 17

Arguments

  • a1 = exit status code

fflush

Writes closed file to disk.

Example

.globl __start


__start:
  li a0, 18 # fflush
  li a1, 1 # file descriptor
  ecall
    li a1, 1
    jal fflush

Ecall code

  • a0 = 18

Arguments

  • a1 = file descriptor

Return

  • a0 = 0 on success, and EOF (-1) otherwise.

ferror

Returns a nonzero value if the file stream has errors, otherwise it returns 0.

Example

.globl __start


__start:
  li a0, 20 # fflush
  li a1, 1 # file descriptor
  ecall
    li a1, 1
    jal ferror

Ecall code

  • a0 = 20

Arguments

  • a1 = file descriptor

Return

  • a0 = Nonzero falue if the end of file is reached. 0 Otherwise.

Prints integer in register a1 in hex representation. Displayed value is 8 hexadecimal digits, left-padding with zeroes if necessary.

Example

.globl __start

.text

__start:
  li a0, 34  # ecall code
  li a1, 0xa # integer to print in hex
  ecall

Ecall code

  • a0 = 34

Arguments

  • a1 = integer to print

Helper function.

Prints an integer array, with spaces between the elements. DOES NOT terminate with new line. In the assignments please include new line after the array.

.globl __start

.data
m0: .word 1 2 3 4 5 6 7 8 9

.text

__start:
    la a0,m0 # Base ptr
    li a1,3  # number of rows
    li a2,3  # number of cols
    jal print_int_array

# Output
# 1 2 3
# 4 5 6
# 7 8 9

Arguments

  • a0 = is the pointer to the start of the array
  • a1 = is the # of rows in the array
  • a2 = is the # of columns in the array

Return

Helper function.

Prints an coo matrix array, with spaces between the elements. DOES NOT terminate with new line. In the assignments please include new line after the array.

.globl __start

.data
coo_vector0: .word 0 0 1 0 2 3 0 4 5 0 5 6 0 6 7 0 8 9

.text

__start:
    la a0,coo_vector0 # Base ptr
    li a1,3  # number of nonzero elements
    jal print_coo_array

0,0 1
0,2 3
1,1 5
1,2 6
2,0 7
2,2 9

Arguments

  • a0 = is the pointer to the start of the array
  • a1 = is the # of rows in the array
  • a2 = is the # of columns in the array

Return