Mips/Assembly
The following is a guide on how to write MIPS assembly using the GCC compiler.
Contents
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.