Contents:
- Printing strings and characters
- Using (Global) Variables
- Choosing registers
- Doing Arithmetic
- Writing functions
Printing strings and characters
Strings are many bytes long and cannot fit in registers. So, we put them in the data segment, and then refer to them by their address (location in memory).
For example, print_string("Hello!\n")
would look like:
# you can switch to .data anywhere you want, not just at the top of the file
.data
str_hello: .asciiz "Hello!\n"
.text
la a0, str_hello # get the string's address
li v0, 4 # v0 = 4 for print_string
syscall # print it!
Single characters do fit in a register, and you can use li
to put them there.
For example, print_char('\n')
will print a newline, and it would look like:
li a0, '\n' # characters are integers, so this is fine
li v0, 11 # v0 = 11 for print_char
syscall # print it!
But honestly? I just copy and paste these macros into most programs I write. Cause that is just tedious.
.macro print_str %str
.data
print_str_message: .asciiz %str
.text
la a0, print_str_message
li v0, 4
syscall
.end_macro
.macro println_str %str
print_str %str
li a0, '\n'
li v0, 11
syscall
.end_macro
.globl main
main:
println_str "Hello, world!"
Using (Global) Variables
See the correspondences section on variables.
Choosing registers
It’s way less complicated than you are thinking.
- If you are about to call a function, its arguments go into
a
registers.- Start at
a0
, thena1
etc
- Start at
- If you are about to return a value from a function, the return value goes into
v0
.- Also, if you are about to do a syscall, the syscall number goes into
v0
. idk why.
- Also, if you are about to do a syscall, the syscall number goes into
- Otherwise ask yourself this question: do I need to access this value again after a
jal
?- No? Use a
t
register.- Most values only have to be in a register for a brief time.
- It’s common to use these for calculations, variable access, intermediate values etc.
- Reuse the registers. You don’t have to do
t0, t1, t2, t3, t4, t5, t6
etc. I rarely need to use beyondt2
because I rarely need to be using more than 3 “things” at once.
- Yes? use an
s
register.- It’s common to use these as loop counters and other “local variable” tasks.
- Be sure to
push
andpop
them as described in “writing functions” below.
- No? Use a
If that question in 3 doesn’t make sense, think of it in Java terms:
void func() {
// i is a local variable. I should probably use an s register to represent it.
for(int i = 0; i < 10; i++) {
// the address of this array is only used once, within this line, and never again.
// I should probably use a t register to load its address.
// also, since the value from the array is the first argument to print_int,
// I should load that value into a0.
print_int(my_array[i]);
}
}
Doing Arithmetic
The CPU can only do one operation at a time. So, write out your algebraic expression, and use order-of-operations to determine the sequence of operations you need to perform.
Examples:
# x = y + z
# we just need to do one 'add' here.
lw t0, y # t0 = y
lw t1, z # t1 = z
add t0, t0, t1 # t0 = y + z
sw t0, x # store y + z into x
# ---------------------------------------
# x = x / 2 + 3 * y - z
# 1. divide, 2. multiply, 3. add, 4. subtract.
# it's helpful to make notes of what is in which registers as you go.
# notice how the value kind of "builds up" in t0. like a snowball.
lw t0, x # t0 = x
div t0, t0, 2 # t0 = x / 2
lw t1, y # t1 = y
mul t1, t1, 3 # t1 = y * 3
add t0, t0, t1 # t0 = x / 2 + 3 * y
lw t1, z # t1 = z
sub t0, t0, t1 # t0 = x / 2 + 3 * y - z
sw t0, x # store the final result into x!
Writing functions
For a foolproof way to write functions, do this:
-
Start with this skeleton code:
function_name: push ra # -------------------------------- # -------------------------------- _return: pop ra jr ra
- Write code between the
----
lines above. The pushes and pops are like the{}
on a function.- Arguments are already in the
a
registers when the function starts. - To return early, jump/branch to
_return
.- DO NOT just throw a
jr ra
in the middle of the function!!!!!!
- DO NOT just throw a
- To return a value, put a value in
v0
before going to_return
.
- Arguments are already in the
-
If you need to use any
s
registers, push them at the beginning, and pop them in reverse order at the end:function_name: push ra push s0 # think of this as declaring "I need s0 for this function." push s1 # " # -------------------------------- # now we can use s0 and s1 with no problem. move s0, a0 # need to save a0 if we `jal` to another function li s1, 0 # a loop counter? # ... blah blah more code... # -------------------------------- _return: # all pops in REVERSE order. pop s1 pop s0 pop ra jr ra