Mips/Assembly

From CloudModding OoT Wiki
Jump to: navigation, search

The following is a guide on how to write MIPS assembly using the GCC compiler.

Syntax

Constants and Labels

Constants are named 32 bit integer values that cannot change once assigned to. They are useful for giving a descriptive name to a variable. Once defined, a constant can be used in place of a literal value elsewhere in code, including the declaration of another constant. You can use simple expressions when assigning a value to a constant.

Example:

NUM_INTEGERS = 100
SIZE_INTEGER_ARRAY = NUM_INTEGERS * 4

Labels are a type of constant that represents an address, without having to define one directly. Labels are defined by a constant name followed by a colon. You can either declare a label on it's own line, or you can in-line it like so:

## Own line label declaration
.text
Loop: 
    lw t0, LoopFor
    bne t1, t0, Loop
    addiu t1, t1, 1
.data
## In-line label declaration
LoopFor: .word 8

%Hi and %Lo

The %hi and %lo operators allow you to use labels in instructions that only manipulate half of an address. For example, say you want to load a 4 byte value at address 8011A5D0. A common way to do that would be to do this:

lui $at, 0x8012
lw t0, 0xA5D0($at)

Note that the address is split across the immediate components of the lui and lw opcodes. Instead, this can be rewritten as the equivalent form, assuming that lbl_8011A5D0 = 8011A5D0:

lui $at, %hi(lbl_8011A5D0)
lw t0, %lo(lbl_8011A5D0)($at)

Special Characters

The # character is used to mark the start of a comment that runs to the end of the current line. If the # character is the first character on the line, it can also be a preprocessor control command

Pseudo Instructions

Pseudo instructions (or pseudo ops) are operations that expand into one or two valid MIPS instructions, which simplifies certain common operations. Some pseudo instructions that expand into two instructions use the $at register to store an intermediate state, so extra care must be made when writing code that relies on $at as a temporary register. This is compounded by the fact that several pseudo instructions share the same mnemonic as proper MIPS instructions.

Data

Name Syntax Expansion Result Notes
move move $target, $source addiu $target, $source $target = $source
clear clear $target addu $target, $zero, $zero $target = 0
load 16-bit immediate li $target, constant addiu $target, $zero, %lo(constant) target = constant
load 32-bit immediate lui $target, %hi(constant)
ori $target, $target, %lo(constant)
load label address la $target, address lui $target, %hi(address)
ori $target, $target, %lo(address)
$target = address address can either be a label or a constant

Branches

Name Syntax Expansion Result
branch unconditionally b label beq $zero, $zero, label goto label
branch unconditionally and link bal label bgezal $zero, label $ra = PC + 4; goto label
branch if greater than bgt $left, $right, label slt $at, $right, $left
bne $at, $zero, label
if $left > $right
goto label
branch if less than blt $left, $right, label slt $at, $left, $right
bne $at, $zero, label
if $left < $right
goto label
branch if greater than or equal bge $left, $right, label slt $at, $left, $right
beq $at, $zero, label
if $left >= $right
goto label
branch if less than than or equal ble $left, $right, label slt $at, $right, $left
beq $at, $zero, label
if $left <= $right
goto label
branch if greater than unsigned bgtu $left, $right, label sltu $at, $left, $right
bne $at, $zero, label
if $left > $right
goto label
branch if zero ble $value, label beq $value, $zero, label if $value == 0
goto label
branch if equal to immediate beq $value, immediate, label ori $at, $zero, immediate
beq $value, $at, label
if $value == immediate
goto label
branch if not equal to immediate bne $value, immediate, label ori $at, $zero, immediate
bne $value, $at, label
if $value != immediate
goto label

Multiplication and Division

Name Syntax Expansion Result
Multiply and return 32 bits mul $result, $left, $right mult $left, $right
mflo $result
$result = ($left * $right) & 0xFFFFFFFF
Integer Division div $dividend, $divisor Mult HI/LO contains the result of the division

To avoid macros when using division, use div $zero, $divisor, $dividend instead

Other

Name Syntax Expansion Result
Jump register and link jalr $source jalr $source, $ra ra = PC + 4; goto $source
Logical Not not $target, $source nor $target, $source, $zero $target = ~$source
No-Operation nop sll $zero, $zero, 0

Directives

Set Directive

The .set n directive can be used to set a number of settings:

Token Purpose
noreorder .set noreorder prevents the assembler from reorganizing the instructions to optimize branch delay slots
at .set at causes a warning to trigger whenever $at is explicitly used
noat .set noat silences the above warning

Segment Directives

Directive Type
.text <addr> Items following this are stored in the text segment, which stores instructions. Addr is optional, but if present the subsequent items are stored at address addr
.data <addr> Items following this are stored in the data segment. Addr is optional, but if present the subsequent items will be stored at the starting address
.bss <addr> Items following this are stored in the bss data segment.

Variable Directives

Directive Type
.ascii str Store a non-null terminated string str in memory
.asciiz str Store a null terminated string str in memory
.byte b1, ..., bn Stores 8-bit values in successive memory locations
.half h1, ..., hn Stores 16-bit (half word) values in successive memory locations
.word w1, ...., wn stores 32-bit (word) values in successive memory locations
.float f1, ..., fn Stores 32-bit floating point numbers in successive memory locations
.double d1, ..., dn Stores 64-bit floating point numbers in successive memory locations
.space n Allocates n bytes in the current segment
.align n aligns the next variable on an address that is a multiple of 2^n

If you need to initialize multiple instances of a common numerical type, you can postfix the value with :n, where n is the number of times that value should occur. For example:

# Create an array of 7 two-byte timers, initialized to 10 frames
Timers: .half 0x000A:7

Preprocessor Directives

#include - includes a source file. Typically you'll use this to #include <mips.h>, which defines aliases for the registers.