Skip to content

Latest commit

 

History

History
210 lines (145 loc) · 5.62 KB

ch2.adoc

File metadata and controls

210 lines (145 loc) · 5.62 KB

Chapter 2: System Calls

We mentioned system calls (aka syscalls from now on) 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 MIPS simulators. They provide access to all the fundamental features, like input and output to/from both the console and files, allocating memory, and exiting. That covers all the 17 syscalls supported by spim, but MARS supports many more, for things ranging from playing MIDI sounds, to getting a random number, to creating GUI dialogs.[1]

Note
Except for the MARS or SPIM specific chapters/sections, I’ll be sticking to code compatible with both throughout this book, meaning we only use the first 17 syscalls, and don’t get to use some of the syntactic sugar available in MARS, or any SPIM specific features either.
Table 1. SPIM supported syscalls
Name $v0 Arguments Result

print integer

1

$a0 = integer to print

print float

2

$f12 = float to print

print double

3

$f12 = double to print

print string

4

$a0 = address of string

read integer

5

$v0 = integer read

read float

6

$f0 = float read

read double

7

$f0 = 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

$v0 = 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

$v0 = character read

open file

13

$a0 = address of filename
$a1 = flags
$a2 = mode

$v0 = file descriptor (negative if error)

read from file

14

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

$v0 = number of characters read, 0 for end-of-file, negative for error

write to file

15

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

$v0 = number of characters written, negative for error

close file

16

$a0 = file descriptor

exit2

17

$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 syscall you want in $v0

  2. Fill in the appropriate arguments, if any

  3. Execute the syscall with syscall

	li    $v0, 1   # 1 is print integer
	li    $a0, 42  # takes 1 arg in a0, the number to print
	syscall        # actually execute syscall

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

Examples

#include <stdio.h>

int main()
{
	int age;
	int height;
	char name[50];
	printf("What's your name? ");
	fgets(name, 50, stdin);

	printf("Hello %s", name);

	printf("How old are you? ");
	scanf("%d", &age);

	printf("Enter your height in inches: ");
	scanf("%d", &height);

	printf("Your age + height = %d\n", age + height);

	return 0;
}

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

.data

name:     .space 50

nameprompt:  .asciiz "What's your name? "
hello_space: .asciiz "Hello "
how_old:     .asciiz "How old are you? "
ask_height:  .asciiz "Enter your height in inches: "
ageplusheight: .asciiz "Your age + height = "


.text
main:
	li   $v0, 4      # print string system call
	la   $a0, nameprompt  # load address of string to print into a0
	syscall

	li   $v0, 8      # read string
	la   $a0, name
	li   $a1, 50
	syscall

	li   $v0, 4
	la   $a0, hello_space
	syscall

	la   $a0, name  # note 4 is still in $v0
	syscall

	# don't print a newline here because
	# one will be part of name

	li   $v0, 4
	la   $a0, how_old
	syscall

	li   $v0, 5   # read integer
	syscall
	move $t0, $v0  # save age in t0

	li   $v0, 4
	la   $a0, ask_height
	syscall

	li   $v0, 5   # read integer
	syscall
	add  $t0, $t0, $v0 # t0 += height


	li   $v0, 4
	la   $a0, ageplusheight
	syscall

	li   $v0, 1  # print int
	move $a0, $t0  # a0 = age + height
	syscall

	# print newline
	li   $v0, 11   # print char
	li   $a0, 10   # ascii value of '\n'
	syscall


	li   $v0, 10     # exit syscall
	syscall

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 $v0 for 2 more syscalls, 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 MIPS calling convention.

Another thing is when we print their name, we don’t put 4 in $v0 again because it is still/already 4 from the lines above. Unless the syscall says it writes to $v0 you can assume it is unmodified.

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 syscall as we do right before exiting.