# MIPS Assembly Language Notes
This note is for MIPS assembly language, detailed from the basic structure of MIPS registers to R, I, J instructions. The documnent includes notes and scenario labs.
## MIPS Register Structure
MIPS structure includes 32 general-purpose registers and some specialized registers reserved for system useage. The following are the table of registers and their explaination:
| Register Number | Register Name | Description |
|-----------------|---------------|-------------------------------|
| 0 | `$zero` | Constant zero |
| 1 | `$at` | Assembler temporary |
| 2-3 | `$v0-$v1` | Function return values |
| 4-7 | `$a0-$a3` | Function arguments |
| 8-15 | `$t0-$t7` | Temporaries |
| 16-23 | `$s0-$s7` | Saved temporaries |
| 24-25 | `$t8-$t9` | Temporaries |
| 26-27 | `$k0-$k1` | Reserved for OS kernel |
| 28 | `$gp` | Global pointer |
| 29 | `$sp` | Stack pointer |
| 30 | `$fp/$s8` | Frame pointer or saved temp |
| 31 | `$ra` | Return address |
**Short explanation:**
`$zero` : It is useful when we want to do a comparison or status check with value 0.
`$t0-$t7` : Usually we set variables that would be changed later on in this kind of registers.
`$s0-$s7` : Usually we set variables that would **not** be changed in this kind of registers.
## Load Instructions
The easiest instruction is load. There are 6 load instructions in MIPS assembly language. They each hold different usage for different cases.
<br>
### `lw`
Short for <b style="color: #0080FF;">load word</b>. This instruction helps load word from memory to register. Here is a simple example of it:
```assembly
lw $t0, 0($s0)
```
In this case, `$s0` is a register that stores an address. The `lw` instruction gets the 32 bits data from that address and store it to register `$t0` for later use. `0` reprsent that there is no offset needed.
If we want to store data from an array, we can manually calaulate the offset of the data we want. Let's say now `$s0` stores a base address for an array `A`, if we want to get the data of `A[3]`, we can calculate the offset as `4 bytes * 3 = 12 bytes`, thus the command can be written as:
```assembly
lw $t0 12($s0)
```
The term `12($s0)` represent that we want to load the data in address with offset 4 bytes.
<br>
### `lb`
Short for <b style="color: #0080FF;">load byte</b>. Same as `lw`, this instruction helps load byte from memory to register. Take the same example, the command becomes:
```assembly
lb $t0, 0($s0)
```
This time, it loads one byte of data from the memory address stored in register $s0 with an offset of 0, and stores the byte into register $t0 for later use.
<br>
### `lbu`
The unsigned version of `lb`. All values that loads by this command will become non-negative values.
<br>
### `lh`
Short for <b style="color: #0080FF;">load half word</b>. This instruction load hald a word (2 bytes) from memory to register. Same, using the example, the command becomes:
```assembly
lh $t0, 0($s0)
```
It loads two byte of data from the memory address stored in register $s0 with an offset of 0, and stores the byte into register $t0 for later use.
<br>
### `lhu`
The unsigned version of `lh`. All values that loads by this command will become non-negative values.
<br>
### `lui`
Short for <b style="color: #0080FF;">load upper immediate</b>. The instruction loads 16 bits of data and store it to the upper part of the register. Here's an example.
We can use the following instruction:
```assembly
lui $t0, 0xABCD
```
It loads `ABCD` in `$t0`, which `$t0` becomes `ABCD0000`. `lui` only accept immediate value, so using register as source is not acceptable.
<br>
## Store Instructions
These instructions are used to store data from register to memory.
<br>
### `sw`
Short for <b style="color: #B766AD;">store word</b>. This instruction help store 8 bytes data from register to memory. Here's an example:
```assembly
sw $t0, 0($s0)
```
This instruction stores the 8 bytes data in register `$t0` to the address stored in the register `$s0`.
<br>
### `sh`
Short for <b style="color: #B766AD;">store half word</b>. This instruction help store 4 bytes data from register to memory. The example becomes:
```assembly
sh $t0, 0($s0)
```
This instruction stores the 4 bytes data in register `$t0` to the address stored in the register `$s0`.
<br>
### `sb`
Short for <b style="color: #B766AD;">store byte</b>. This instruction help store a byte of data from register to memory. The example becomes:
```assembly
sb $t0, 0($s0)
```
This instruction stores the a byte of data in register `$t0` to the address stored in the register `$s0`.
<br>
## Arithmetic Instructions
We use these arithmetic instructions to do basic math calculation.
### `add`
Short for <b style="color: #FF9224;">addition immediate</b>. This instruction helps add two values together. The following example shows the usage:
```assembly
add $t2, $t0, $t1
```
The meaning of registers are the following sequence: destination, source, target. The instruction means that add the value stored in `$t0` with the one stored in `$t1`, and then store the result in `$t2`.
<br>
### `addi`
Short for <b style="color: #FF9224;">addition immediate</b>. It is almost the same to `add`, as it only replace the target with immediate value. The example becomes:
```assembly
addi $t0, $t1, 2
```
The instruction means adding the value stored in `$t1` with 2 and then store it in `$t0`.
<br>
### `sub`
Short for <b style="color: #FF9224;">substitute</b>. This instruction helps substitute two values. The following example shows the usage:
```assembly
sub $t2, $t0, $t1
```
The meaning of registers are the following sequence: destination, source, target. The instruction means that substitute the value stored in `$t0` with the one stored in `$t1`, and then store the result in `$t2`.
<br>
### `mul`
Short for <b style="color: #FF9224;">multiply</b>. This instruction helps multiply two values. The following example shows the usage:
```assembly
mul $t2, $t0, $t1
```
The meaning of registers are the following sequence: destination, source, target. The instruction means that multiply the value stored in `$t0` with the one stored in `$t1`, and then store the result in `$t2`.
<br>
### `mult`
A different way of multiply instruction. Instead of storing the result in the particular register, `mult` store it in the `HI` and `LO` registers, which are outside the basic 32 registers. To get it out, we need to use `mfhi` or `mflo`. Look at the following example:
```assembly
mult $t1, $t2 # Multiply $t1 and $t2, store result in HI and LO registers
mflo $t0 # Move the lower 32 bits of the result from LO to $t0
mfhi $t1 # Move the upper 32 bits of the result from HI to $t1
```
We can decide how many we want to get from the `HI` and `LO` registers. If the result isn't that large, we can simply get it using `mflo`.
<br>
### `multu`
The unsigned version of `mult`. The result will only be positive.
<br>
### `div`
Short for <b style="color: #FF9224;">division</b>. This instruction helps divide two values. The following example shows the usage:
```assembly
div $t0, $t1
```
The insturction devide the value stored in `$t0` with the one stored in `$t1`. The quotient will be stored in register `$lo`, and the remainder will be stored in `$hi`. Same as `mult`/`multu`, the result can be get using `mflo`, `mfhi`.
<br>
### `divu`
The unsigned version of `div`. The result will only be positive.
<br>
## [Scenario]: Number add
Assume that A stores in `$s0`, B stores in `$s1`, please write down the MIPS assembly code for the following C code:
1. A = A + 5;
2. B = A + B;
<details>
<summary>Ans 1</summary>
Here's the MIPS assembly code for question 1:
```assembly
lw $t0, 0($s0) # Load A from memory to $t0
addi $t0, $t0, 5 # A = A + 5
sw $t0, 0($s0) # Store updated A back to memory
```
Remember to load the value to `$tn` register and make addition on it, then store it to the target destination.
</details>
<details>
<summary>Ans 2</summary>
Here's the MIPS assembly code for question 2:
```assembly
lw $t0, 0($s0) # Load A from memory to $t0
lw $t1, 0($s1) # Load B from memory to $t1
add $t1, $t0, $t1 # B = A + B
sw $t1, 0($s1) # Store updated B back to memory
```
Since `$tn` register is normally used for storing values that would be rewrite, we can directly do calculation on `$t1`.
</details>
<br>
## Logical Instructions
If we want to do simply comparison, we use logical instructions. It can be work with Jump instructions to achieve if statement and loops.
<br>
### `and`
This instruction do simple <b>and</b> operation with two values. Here's an example, assume that `$t0` stores 10 and `$t1` stores 20:
```assembly
and $t2, $t0, $t1
```
As 10 in binary is `00001010`, 20 in binary is `00010100`, the result becomes:
```
0000 1010 ($t0 = 10)
AND 0001 0100 ($t1 = 20)
--------------
0000 0000 ($t2 = 0)
```
The result(`0`) is stored in `$t2`.
<br>
### `andi`
Short for <b style="color: #8CEA00;">and immediate</b>. The immediate version of <b>and</b> operation. Assume `$t0` is still 10, the example becomes:
```assembly
and $t1, $t0, 20
```
The result(`0`) is stored in `$t1`.
<br>
### `or`
This instruction do simple <b>or</b> operation with two values. Here's an example, assume that `$t0` stores 10 and `$t1` stores 20:
```assembly
or $t2, $t0, $t1
```
As 10 in binary is `00001010`, 20 in binary is `00010100`, the result becomes:
```
0000 1010 ($t0 = 10)
OR 0001 0100 ($t1 = 20)
--------------
0001 1110 ($t2 = 30)
```
The result(`30`) is stored in `$t2`.
<br>
### `ori`
Short for <b style="color: #8CEA00;">or immediate</b>. The immediate version of <b>or</b> operation. Assume `$t0` is still 10, the example becomes:
```assembly
ori $t1, $t0, 20
```
The result(`30`) is stored in `$t1`.
<br>
### `xor`
This instruction do simple <b>xor</b> operation with two values. Here's an example, assume that `$t0` stores 10 and `$t1` stores 20:
```assembly
xor $t2, $t0, $t1
```
As 10 in binary is `00001010`, 20 in binary is `00010100`, the result becomes:
```
0000 1010 ($t0 = 10)
XOR 0001 0100 ($t1 = 20)
--------------
0001 1110 ($t2 = 30)
```
The result(`30`) is stored in `$t2`.
<br>
### `nor`
This instruction do simple <b>nor</b> operation with two values. Here's an example, assume that `$t0` stores 10 and `$t1` stores 20:
```assembly
nor $t2, $t0, $t1
```
As 10 in binary is `00001010`, 20 in binary is `00010100`, the result becomes:
```
0000 1010 ($t0 = 10)
NOR 0001 0100 ($t1 = 20)
--------------
1110 0001 ($t2 = 225)
```
The result(`225`) is stored in `$t2`.
<br>
## Shift Instructions
Shift instructions are used when we want to quickly multiply/divide values. For example, if we want to get the value in `A[i]` as A is stored in `$s0` and `i` is a variable, we need to first calculate the offset of `i`, which is `i * 4` and then add it with the base address of `A`. To do that, shift instruction is in it's usage.
<br>
### `sll`
Short for <b style="color: #D9006C;">shift left logical</b>. We can use it to multiply value with base 2. Here's an example, assume variable `i` stores in `$t0`:
```assembly
sll $t0, 2
```
This example will lead to the result that `i` is multiplied by 4, which in some case can calculate the offset when `i` is the index we want to get from an array.
<br>
### `srl`
Short for <b style="color: #D9006C;">shift right logical</b>. Here's an example assume number 4 is stored in `$t0`:
```assembly
srl $t0, 1
```
The instruction shift the value right with one bit, which divide the value with 2, so the value stored in `$t0` becomes 2.
<br>
### `sra`
Short for <b style="color: #D9006C;">shift right arithmetic</b>. Here's an example, with value `-8` stored in `$t0`:
```assembly
sra $t0, $t0, 2
```
This instruction let the origin value `-8`(`11111111 11111111 11111111 11111000` in binary) becomes `-12`(`11111111 11111111 11111111 11111110` in binary), as shifting the value right two bits with `1` coming in.
<br>
### `rotr`
Short for <b style="color: #D9006C;">rotate right</b>. Here's an example as the last one, but replace the instruction with `rotr`:
```assembly
rotr $t0, $t0, 1
```
This instruction let the origin value `-8`(`11111111 11111111 11111111 11111000` in binary) becomes `2147483644`(`01111111 11111111 11111111 11111100` in binary), the bit going out from right comes back to the left.
<br>
## [Scenario]: Array operation
Assume that base address of array A stored in `$s0`, base address of array B stores in `$s1`, i, j, k is at `$t0`, `$t1` and `$t2`. Please write down the MIPS assembly code for the following C code:
1. A[i] = 5;
2. B[k+3] = A[i] + j;
<details>
<summary>Ans 1</summary>
Here's the MIPS assembly code for question 1:
```assembly
sll $t0, $t0, 2 # Shift i left by 2 bits to get offset
add $t3, $s0, $t0 # Add the base address of array A to the offset
lw $t4, 0($t3) # Load the current value at A[i]'s address
add $t4, $zero, 5 # Set $t4 to 5
sw $t4, 0($t3) # Store the value back
```
Calculate the offset and get the value, then set it back to where it should be.
</details>
<details>
<summary>Ans 2</summary>
Here's the MIPS assembly code for question 2:
```assembly
sll $t0, $t0, 2 # Shift i left by 2 bits to get offset
add $t3, $s0, $t0 # Add the base address of array A to the offset
lw $t4, 0($t3) # Load A[i]
add $t4, $t4, $t1 # Add j to A[i]
addi $t2, $t2, 3 # Add 3 to k
sll $t2, $t2, 2 # Shift (k+3) by 2 bits
add $t3, $s1, $t2 # Add the base address of array B to the offset
sw $t4, 0($t3) # Store the value (A[i] + j) into B[k+3]
```
Add the k+3 first then calculate the offset.
</details>
<br>
## Comparison Instructions
Comparion instructions is used when we want to check which instructions we need to do next. There's s bunch of combination using comparison instructions. Comparison instructions will have to give a label(think of it as a tag) to jump to.
<br>
### `beq`
Short for <b style="color: #5CADAD;">branch if euqal</b>. The following is an example of the usage:
```assembly
main:
beq $t0, $t1, label
add $t0, $t0, $t1
label:
...
```
Assume that `$t0` and `$t1` are equal, the instruction of addition will not be executed right away. Instead, it jumps to `label` and executes the instrutions in it first.
<br>
### `bne`
Short for <b style="color: #5CADAD;">branch if not euqal</b>. Similar to `beq`, this instruction helps verify if two values are not the same. So the example becomes:
```assembly
main:
bne $t0, $t1, label
add $t0, $t0, $t1
label:
...
```
This time, only if `$t0` and `$t1` are different will lead to the "jump to label" action.
<br>
### `blez`
Short for <b style="color: #5CADAD;">branch if less than or equal to zero</b>. This instruction only takes one register an the label as input. It compares the input register and see if the value stored in it is less than or euqal to 0. Here's an example:
```assembly
main:
add $t0, $zero, $t0
blez $t0, label
label:
...
```
As the `$t0` is set to zero, the condition `$t0 <= 0` is met, thus the jump action will be taken.
<br>
### `bgez`
Short for <b style="color: #5CADAD;">branch if greater than or equal to zero</b>. This instruction only takes one register an the label as input. It compares the input register and see if the value stored in it is greater than or euqal to 0. Here's an example:
```assembly
main:
add $t0, $zero, $t0
blez $t0, label
label:
...
```
As the `$t0` is set to zero, the condition `$t0 >= 0` is met, thus the jump action will be taken.
<br>
### `bltz`
Short for <b style="color: #5CADAD;">branch if less than zero</b>. Similar to `blez` but without "equal to". It only check if the givin value is less than zero. Still the same example, this time it will not jump, as `$t0 = 0` and the condition `$t0 < 0` is not met.
<br>
### `bgtz`
Short for <b style="color: #5CADAD;">branch if greater than zero</b>. Similar to `blez` but without "equal to". It only check if the givin value is greater than zero. Still the same example, this time it will not jump, as `$t0 = 0` and the condition `$t0 > 0` is not met.
<br>
## Set on Comparison Instructions
If we want to do comparison but do not want to jump to other place, we can simply store the result in another register. These kind o instructions helps do the trick.
### `slt`
Short for <b style="color: #B15BFF;">set on less than</b>. Here's an example, assume `$s0` is 0 and `$s1` is 1:
```assembly
slt $s2, $s0, $s1
```
The instruction verfiy if `$s0` is less than `$s1`. In the example it is, so `$s2` is set to 1. Otherwise, `$s2` is set to 0.
<br>
### `slti`
Short for <b style="color: #B15BFF;">set on less than immedite</b>. The only difference of it with `slt` is that instead of comparing two values coming from registers, it is comparing one from register with immediate value. The example becomes:
```assembly
slti $s1, $s0, 10
```
This instruction check if value in `$s0` is less than 10, if it is, set `$s1` to 1, otherwise set it to 0.
<br>
### `sltu`
Short for <b style="color: #B15BFF;">set on less than unsigned</b>. Same function as `slt`, but it treats values as unsigned.
<br>
### `sltiu`
Short for <b style="color: #B15BFF;">set on less than unsigned</b>. Same function as `slti`, but it treats values as unsigned.
<br>
## Jump Instruction
### `j`
Short for <b style="color: #FFD306;">jump</b>. The instruction jumps to label no matter what happened.Here's an example:
```assembly
main:
j label
...
label:
...
```
All instuctions will not be executed if using `j` instruction. It will jump to `label` and continue executing instructions in it.
<br>
### `jal`
Short for <b style="color: #FFD306;">jump and link</b>. This instruction jumps to other place but will remember where it leave in `$ra` register. Here's an example:
```assembly
main:
jal func
...
func:
...
```
Later in `func`, we can come back to the line of instruction we leave in `main` to keep going on.
<br>
### `jr`
Short for <b style="color: #FFD306;">jump register</b>. It jumps to the address stored in the target register. Take the example from the last one:
```assembly
main:
jal func
...
func:
...
jr $ra
```
After execution the instructions in `func`, it jumps back to where it leave, using the address stored in `$ra`.
<br>
### `jalr`
Short for <b style="color: #FFD306;">jump and link register</b>. Similar to `jal`, we can also jump to a particular address stored in a register instead of label. We use `jalr` to do so, assume that 0x100 is somewhere in `func`:
```assembly
main:
jalr 0x100
...
func:
...
jr $ra
```
The instruction jumps to a line in `func`, executes the instructions below it, and finally reach `jr` and comes back to where it leave in `main`.
<br>
### `eret`
Short for <b style="color: #FFD306;">exception return</b>. If an error occur, this instruction can be used in excepition handling section.
```assembly
eret
```
It will jump to the addres stored in th epc register.
<br>
## [Scenario]: Conditional
Assume that g, h, i, j, k stores at `$s0`, `$s1`, `$s2`, `$s3`, `$s4`, and base address of array A and array B stores at `$s6`, `$s7`. Please write down the MIPS assembly code for the following C code:
1. ```
if(i + 3 > k) {
B[i+1] = A[j];
} else {
B[h] = A[j] + A[k] * 3;
}
```
2. ```
if(A[i] == B[j]) {
B[j] = A[k];
} else if (A[i] < B[j] ) {
B[j-1] = A[k-1];
} else {
B[j+1] = A[k+1];
}
```
<details>
<summary>Ans 1</summary>
Here's the MIPS assembly code for question 1:
```assembly
main:
addi $t0, $s2, 3 # Calculate i + 3
sub $t0, $t0, $s4 # Calculate (i + 3) - k
bgtz $t0, func # Jump if $t0 > 0
add $t1, $s3, $s6 # A base address + offset j * 4
add $t2, $s4, $s6 # A base address + offset k * 4
lw $t1, 0($t1) # Load A[j] from memory
lw $t2, 0($t2) # Load A[k] from memory
mul $t2, $t2, 3 # A[k] *= 3
add $t1, $t1, $t2 # Add A[j] with A[k] * 3
add $t3, $s1, $s7 # B base address + offset h * 4
sw $t1, 0($t3) # Store result in B[h]
j end # Jump to end
func:
add $t0, $s3, $s6 # A base address + offset j
lw $t0, 0($t0) # Load A[j] from memory
addi $t1, $s2, 1 # Calculate i + 1
sll $t1, $t1, 2 # Calculate (i + 1) * 4 to get offset
add $t1, $t1, $s7 # B base address + offset (i + 1) * 4
sw $t0, 0($t1) # Store A[j] in B[i + 1]
end:
...
```
Remember to first add the index and then multiply to get the real offset. All labels can be named as you wish.
</details>
<details>
<summary>Ans 2</summary>
Here's the MIPS assembly code for question 2:
```assembly
main:
sll $t0, $s2, 2 # Calculate i * 4 to get offset
add $t0, $t0, $s6 # A base address + offset i * 4
lw $t0, 0($t0) # Load A[i] from memory
sll $t1, $t1, 2 # Calculate j * 4 to get offset
add $t1, $t1, $s7 # B base address + offset j * 4
lw $t1, 0($t1) # Load B[j] from memory
beq $t0, $t1, equal # Check if A[i] == B[j]
sub $t0, $t0, $t1 # Calculate A[i] - B[j]
bltz $t0, less # Check if A[i] - B[j] less than 0
add $t0, $s4, 1 # Calculate k + 1
sll $t0, $t0, 2 # Calculate (k + 1) * 4 to get offset
add $t0, $t0, $s6 # A base address + offset (k + 1) * 4
lw $t0, 0($t0) # Load A[k+1] from memory
add $t1, $s4, 1 # Calculate j + 1
sll $t1, $t1, 2 # Calculate (j + 1) * 4 to get offset
add $t1, $t1, $s7 # B base address + offset (j + 1) * 4
sw $t0, 0($t1) # Store A[k+1] to B[j+1]
j end # Jump to end
equal:
sll, $t0, $s3, 2 # Calculate k * 4 to get offset
add $t0, $t0, $s6 # A base address + offset k * 4
lw $t0, 0($t0) # Load A[k] from memory
sll $t1, $s4, 2 # Calculate j * 4 to get offset
add $t1, $t1, $s7 # B base address + offset j * 4
sw $t0, 0($t1) # Store A[k] to B[j]
less:
sub $t0, $s4, 1 # Calculate k - 1
sll $t0, $t0, 2 # Calculate (k - 1) * 4 to get offset
add $t0, $t0, $s6 # A base address + offset (k - 1) * 4
lw $t0, 0($t0) # Load A[k-1] from memory
sub $t1, $s4, 1 # Calculate j - 1
sll $t1, $t1, 2 # Calculate (j - 1) * 4 to get offset
add $t1, $t1, $s7 # B base address + offset (j - 1) * 4
sw $t0, 0($t1) # Store A[k-1] to B[j-1]
end:
...
```
We can keep reusing registers if there are not holding important datas.
</details>
<br>
## Instruction Structure
The instructions in MIPS have different format for R, I and J instruction, but they all consist with 4 bytes.
For R-format, it has three register section and a shift section, where I-format only has two.
### R-format
| opcode | rs | rt | rd | shamt | funct |
|--------|-------|-------|-------|-------|-------|
| 6 bits | 5 bits| 5 bits| 5 bits| 5 bits| 6 bits|
### I-format
| opcode | rs | rt | immediate |
|--------|-------|-------|------------------|
| 6 bits | 5 bits| 5 bits| 16 bits |
### J-format
| opcode | address |
|--------|---------------------------------|
| 6 bits | 26 bits |
To convert instructions into binary, we still need a function table to look up.
### R-Type Instructions
| Instruction | Funct Code |
|-------------|------------|
| `add` | 32 |
| `sub` | 34 |
| `and` | 36 |
| `or` | 37 |
| `sll` | 0 |
| `srl` | 2 |
| `jr` | 8 |
### I-Type Instructions
| Instruction | Opcode |
|-------------|-------------|
| `lw` | 35 |
| `sw` | 43 |
| `addi` | 8 |
| `beq` | 4 |
| `bne` | 5 |
### J-Type Instructions
| Instruction | Opcode |
|-------------|-------------|
| `j` | 2 |
| `jal` | 3 |
### Example
Convert the given instruction into binary `add $t0, $t0, $t3`:
First look up to the given tables and get the following informations.
`add` -> 32
`$t0` -> 8
`$t3` -> 11
Upon knowing that, we can now convert them in binary.
| opcode | rs | rt | rd | shamt | funct |
|--------|-------|-------|-------|-------|--------|
| 000000 | 01000 | 01011 | 01000 | 00000 | 100000 |
Binary: 00000001000010110100000000100000
Hex: 010B4020
<br>
## [Scenario]: Convert the code
Convert the following code in binary and hex:
1. `add $t0, $s1, $s2`
2. `lw $t0, 1200($t1)`
<details>
<summary>Ans 1</summary>
Here's the convert result for question 1:
`add` -> 32
`$t0` -> 8
`$s1` -> 17
`$s2` -> 18
| opcode | rs | rt | rd | shamt | funct |
|--------|-------|-------|-------|-------|--------|
| 000000 | 01001 | 01010 | 01000 | 00000 | 100000 |
Binary: 00000001001010100100000000100000
Hex: 02324020
</details>
<details>
<summary>Ans 2</summary>
Here's the convert result for question 2:
`lw` -> 35
`$t0` -> 8
`$t1` -> 9
| opcode | rs | rt | immediate |
|--------|-------|-------|------------------|
| 000000 | 01001 | 01010 | 0000010010110000 |
Binary: 00000001001010100000010010110000
Hex: 12A04B0
</details>
<br>
## Writing an assembly program
Here's some important concept of MIPS assembly program:
### Passing arguments and return value
Whenever we need to call another function and needs to pass arguments, we set them in `$a` registers. We have only 4 `$a` registers, so if we want to pass more then that, we need to store them in `$sp` like local variables, which is described below. Similarily, when we want to return something, we set them in `$v` registers, which is also shown in the case below.
### The usage of `$s` and `$t` registers
In C programming language, we can esaily assign a local variable in a function, however it is not the case in MIPS assembly language. In MIPS, assembly language, if we want to store value in a locl variable, we need to use `$s` registers. We have a commen sense that "every variable should be stored in `$s` registers, and temporary values should be stored in `$t` vraiables". That means, if we want to transfer the C code `a = a * 2 + 1`, then as we need to first calculate a * 2 then add it with 1 and assigned back to `a`:
- the final value of `a * 2 + 1` shoud be stored in `$s` register where `a` represent to (in this case, `$s0`)
- `a * 2` should be stored in `$t` register as it is a temporary calculation value
### Store registers' values to `$sp`
If we need to assign local variables in function, we need to save the origin `$s` in stack and assign it for local variable use, then restore it after the function finished it's actions.
Here's an simple example. Assume that in the `main` function we assigned two variables as `a = 1` and `b = 2`, and the `printI` function assigned a variable as `i = 1`, and print out `i`. We need to store the `$s0` since we're using it and we wan't to make sure it saves the data as what it is after finishing the function call:
```assembly
main:
li $s0, 1 # a = 1
li $s1, 2 # b = 2
jal v
printI:
addi $sp, $sp, -4 # ask for memory
sw $s0, 0($sp) # store origin value of $s0
li $s0, 1 # i = 1
li $v0, 1 # set system call to "print integer"
move $a0, $s0 # setup print value
syscall # do system call
lw $s0, 0($sp) # restore value from stack back to $s0
addi $sp, $sp, 4 # return memory
jr $ra
```
If we have an empty `$s` register (in this case `$s2`~`$s7`), we can actually just use them, but we prefer not to do that, and assume there's no empty `$s` registers so that we can always do the best case: saving it temporary and restore it after use.
This example might seen rediculous, and it is indeed. It is only used to display the way we store `$s` registers' values.
<br>
## MIPS assembly program examples
We can write an MIPS assembly program using online tools or install Mars on local. Mars can help us monitor the registers when we're testing our program.
Following up there will be some examples of MIPS program that I think is worth prcticing.
### Case 1: Simple BMI calculator
We want to write a BMI calculator using MIPS, it is pretty much the most easiest one we'll encounter. User inputs height and weight to the program, and if we the height is -1, terminate the progrm. Here's the c code for it, we need to transform it to MIPS.
```c
#include <stdlib.h>
#include <stdio.h>
int calculateBMI(int height, int weight) {
int bmi = (weight * 10000) / (height * height);
return bmi;
}
void printResult (int bmi) {
if (bmi <= 17)
printf("%s", "underweight\n");
else if (bmi >= 25)
printf("%s", "overweight\n");
else
printf("%d\n", bmi);
}
int main() {
int height, weight, bmi;
while (1) {
scanf("%d", &height); if (height == -1)
break;
scanf("%d", &weight);
bmi = calculateBMI(height, weight); printResult(bmi);
}
return 0;
}
```
The anaswer is like the following(not necessry to be completely the same, as mny registers can be replaced and reuse):
```assembly
.data
underweightStr: .asciiz "underweight\n"
overweightStr: .asciiz "overweight\n"
newline: .asciiz "\n"
.text
main:
j while_loop
while_loop:
li $v0, 5 # Input height
syscall
move $s0, $v0 # Set height
li $t0, -1 # Check if height = -1
beq $s0, $t0 end
li $v0, 5 # Input weight
syscall
move $s1, $v0 # Set weight
move $a0, $s0 # Set arguments
move $a1, $s1
addi $sp, $sp, -4 # Ask for memory
jal calculateBMI # Call calculateBMI
addi $sp, $sp, 4 # Return memory
move $a0, $v0 # Set argument
jal printResult # Call printResult
j while_loop # Go back to loop
calculateBMI:
sw $s0, 0($sp) # Store height to stack
mul $t0, $a1, 10000 # Calculate weight * 10000
mul $t1, $a0, $a0 # Calculate height * height
div $t0, $t1 # $t0 / $t1
mflo $s0
move $v0, $s0 # Set return value
lw $s0, 0($sp) # Load height from stack
jr $ra
printResult:
li $t0, 17
ble $a0, $t0, printUnderweight
li $t0, 25
bge $a0, $t0, printOverweight
li $v0, 1
move $a0, $a0
syscall
li $v0, 4
la $a0, newline
syscall
jr $ra
printUnderweight:
li $v0, 4
la $a0, underweightStr
syscall
jr $ra
printOverweight:
li $v0, 4
la $a0, overweightStr
syscall
jr $ra
end:
li $v0, 10
syscall
```
We can see that we need a local variable called `bmi` in `calculateBMI`, so we store `$s0` and let `$s0` become `bmi`. After finished the function, we restore the value back to `$s0` so that it consist(which is `height`).
### Case 2: Recursion operation
We will be doing a recursion program to print out the first n elements in Fibonacci numbers. For each time we enter the `fact` function, we store the current values like the return address, local variables, arguments and even temporary sotrage. Here's the origin C code:
```C
#include <stdio.h>
void print(int size, int *x) {
for (int i=0; i<size; i++) {
printf("%d,",x[i]);
}
}
int fact (int n, int *x) {
int t=0;
if (n < 2) {
x[n] = t = 1;
return t;
}
else {
x[n] = t = fact(n - 1, x) + fact(n - 2, x);
}
return t;
}
int main() {
int x[100];
int n;
x[0]=1;
for (int i=1; i<100; i++)
x[i]=0; scanf("%d", &n); fact(n, x); print(n, x);
return 0;
}
```
The answer will look something like below, it looks complicate at first glance, but after several times of practices it will become more clear and confident.
```assembly
.data
data: .space 400
dot: .ascii ","
.text
main:
# assign x[100]
la $s0, data
# x[0] = 1
li $t0, 1
sw $t0, 0($s0)
li $t0, 100 # set counter $t0 to 100
add $s1, $s0, 4 # let $s1 points to x[1]
jal loop
# get user input to n
li $v0, 5
syscall
move $s1, $v0
# call fact
move $a0, $s1 # set n to arguments
move $a1, $s0 # set x to arguments
jal fact
# call print
move $a0, $s1 # set n to arguments
move $a1, $s0 # set x to arguments
jal print
li $v0, 10
syscall
loop:
# load 0 to x[i]
li $t1, 0
sw $t1, 0($s1)
addi $s1, $s1, 4 # i += 1
addi $t0, $t0, -1 # counter -= 1
bne $t0, 1, loop # jump to loop if not end
jr $ra
fact:
addi $sp, $sp, -24
sw $t1, 20($sp)
sw $t2, 16($sp)
sw $ra, 12($sp)
sw $a0, 8($sp)
sw $a1, 4($sp)
sw $s0, 0($sp)
li $s0, 0 # t = 0
# check if n > 1
slti $t0, $a0, 2
beq $t0, $zero, L1
sll $t0, $a0, 2
add $t0, $a1, $t0 # x[n]
li $s0, 1 # t = 1
sw $s0, 0($t0) # x[n] = t
move $v0, $s0
addi $sp, $sp, 24
jr $ra
L1:
# calculate fact(n - 1, x)
addi $a0, $a0, -1
jal fact
move $t1, $v0
sw $t1, 20($sp)
# calculate fact(n - 2, x)
addi $a0, $a0, -1
jal fact
move $t2, $v0
sw $t2, 16($sp)
lw $t1, 20($sp) # restore values
lw $t2, 16($sp)
lw $ra, 12($sp)
lw $a0, 8($sp)
lw $a1, 4($sp)
sll $t0, $a0, 2
add $t0, $a1, $t0 # x[n]
add $s0, $t1, $t2 # t = fact(n - 1, x) + fact(n - 2, x)
sw $s0, 0($t0)
move $v0, $s0
lw $s0, 0($sp) # restore $s0
addi $sp, $sp 24
jr $ra
print:
addi $sp, $sp, -12
sw $ra, 8($sp)
sw $s0, 4($sp)
sw $a0, 0($sp)
li $s0, 0 # i = 0
move $t0, $a0 # counter = size
jal print_loop
lw $ra, 8($sp)
lw $s0, 4($sp)
lw $a0, 0($sp)
jr $ra
print_loop:
# print x[i]
add $t1, $s0, $a1 # x[i]
lw $a0, 0($t1) # setup int to print
li $v0, 1
syscall
# print ","
li $v0, 4
la $a0, dot
syscall
addi $s0, $s0, 4
addi $t0, $t0, -1
bgt $t0, 0, print_loop
jr $ra
```
Notice that even the temporary value `$t1` and `$t2`(used to store the left branch answer and right branch answer) are stored in stack since we will have multiple branch answer calcultion, and we will first calulate the left answer. Assume we're currently in fact(4), we need to clculate fact(3) -> fact(2) -> fact(1) -> back to fact(4) -> fact(2) -> fact(1). You'll see that the `$t1` and `$t2` will be rewrite multiple times, so if we get the left branch answer and did not store it, when we get in the right branh calculation, `$t1` will be overwrite and cause the final answer wrong. So in this kind of multiple layer functions, we should always consider to store every elements we need so that it restore to the right answer as it is when we get back to last layer.
### Case 3: Selection sort
The main concept of this example is to sort an array with 5 elements. It is actually just array operations, but it contains nested loop. The C code is as follow:
```C
#include <stdlib.h>
#include <stdio.h>
void selectionSort(int array[], int n) {
for (int i=0; i<n-1; i++) { int min_idx = i;
for (int j=i+1; j<n; j++) {
if (array[j] < array[min_idx]) {
min_idx = j;
}
}
int temp = array[min_idx];
array[min_idx] = array[i];
array[i] = temp;
}
}
int main() {
int array[5];
for (int i = 0; i < 5; i++) {
scanf("%d", &array[i]);
}
selectionSort(array, 5);
for(inti=0;i<5;i++) {
printf("%d\n", array[i]);
}
return 0;
}
```
To consider when to store the values to stack, we can think if we need to use those registers. In our case we also store `$s2` and `$s3` even we didn't use them before, that is to make sure we always let them stay the same after use. The answer of the example is as follow:
```assembly
.data
data: .space 20
line: .asciiz "\n"
.text
main:
la $s0, data
li $t0, 0 # input loop index
j loop
loop:
li $v0, 5 # set input syscall
syscall
sll $t1, $t0, 2
add $t1, $t1, $s0 # array[i]
sw $v0, 0($t1)
addi $t0, $t0, 1 # i++
bgt $t0, 4, L1
j loop
L1:
move $a0, $s0 # set array to arguments
li $a1, 5 # set 5 to arguments
jal selectionSort
li $t0, 5
li $s1, 0
move $t1, $a0 # load array to $t1
j printLoop
printLoop:
add $t2, $t1, $s1 # array[i]
lw $t2, 0($t2) # load array[i]
li $v0, 1 # print number
move $a0, $t2
syscall
li $v0, 4 # print \n
la $a0, line
syscall
addi $s1, $s1, 4
addi $t0, $t0, -1
bne $t0, 0, printLoop
j end
selectionSort:
addi $sp, $sp -28
sw $a0, 24($sp)
sw $a1, 20($sp)
sw $ra, 16($sp)
sw $s0, 12($sp)
sw $s1, 8($sp)
sw $s2, 4($sp)
sw $s3, 0($sp)
li $s0, 0 # i = 0
jal iLoop
lw $a0, 24($sp)
lw $a1, 20($sp)
lw $ra, 16($sp)
lw $s0, 12($sp)
lw $s1, 8($sp)
lw $s2, 4($sp)
lw $s3, 0($sp)
addi $sp, $sp 28
jr $ra
iLoop:
move $s1, $s0 # min_idx = i
addi $s2, $s0, 1 # j = i + 1
addi $sp, $sp -4
sw $ra, 0($sp)
jal jLoop
sll $t0, $s0, 2
add $t0, $a0, $t0 # array[i]
lw $t1, 0($t0) # load array[i]
sll $t2, $s1, 2
add $t2, $a0, $t2 # array[min_idx]
lw $t3, 0($t2) # load array[min_idx]
move $s3, $t3 # temp = array[min_idx]
sw $t1, 0($t2) # array[min_idx] = array[i]
sw $s3, 0($t0) # array[i] = temp
addi $s0, $s0, 1 # i++
lw $ra, 0($sp)
addi $sp, $sp 4
blt $s0, 4, iLoop
jr $ra
jLoop:
sll $t0, $s2, 2
add $t0, $a0, $t0 # array[j]
lw $t0, 0($t0) # load array[j]
sll $t1, $s1, 2
add $t1, $a0, $t1 # array[min_idx]
lw $t1, 0($t1) # load array[min_idx]
blt $t0, $t1, L2 # if array[j] < array[min_idx]
j L3
L2:
move $s1, $s2
L3:
addi $s2, $s2, 1
bgt $s2, 4, L4
j jLoop
L4:
jr $ra
end:
li $v0, 10
syscall
```
I use a lot of `Ln` as label to concat the loop with function. It is not the only solution, but is useful so that we don't need store a lots of jump registers.
### Case 4: Transpose matrix
In this example, we'll need to write to functions to transpose a matrix. It is basically normal array operation, since we're basically doing the same thing in assembly when the C code is trying to use array or pointer. Here's the C code of the example:
```C
#include <stdlib.h>
#include <stdio.h>
void inputMatrix(int A[3][3]) {
for(inti=0;i<3;i++) {
for (int j = 0; j < 3; j++) {
scanf("%d", &A[i][j]);
}
}
}
void transposeMatrixA1(int A[3][3], int T[3][3], int size) {
for (int i = 0; i < size; i++) {
for(intj=0;j<size;j++) {
T[j][i] = A[i][j];
}
}
}
void transposeMatrixA2(int *B, int *T, int size) {
int *ptrB, *ptrT, i;
for (ptrB=B, ptrT=T, i = 1; ptrB<(B + (size*size)); ptrB++) {
*ptrT = *ptrB;
if (i < size) {
ptrT += size;
i++;
}
else {
ptrT -= (size * (size - 1) - 1);
i = 1;
}
}
}
void outputMatrix(int A[3][3]) {
for(inti=0;i<3;i++) {
for(intj=0;j<3;j++) {
printf("%d ", A[i][j]);
}
printf("\n");
}
}
int main() {
int A[3][3];
int transposeOfA1[3][3];
int transposeOfA2[3][3];
int *ptrA = &A[0][0];
int *ptrTA2 = &transposeOfA2[0][0];
inputMatrix(A);
transposeMatrixA1(A, transposeOfA1, 3);
transposeMatrixA2(ptrA, ptrTA2, 3);
outputMatrix(transposeOfA1);
outputMatrix(transposeOfA2);
return 0;
}
```
As it contains lots of for loops, I use a lot of label to concat the instructions and jump between them. Look at the comments:
```assembly
.data
matrixSize: .space, 36
matrixSize2: .space, 36
matrixSize3: .space, 36
space: .asciiz, " "
changeLine: .asciiz, "\n"
.text
main:
la $s0, matrixSize # A[3][3]
la $s1, matrixSize2 # transposeOfA1[3][3]
la $s2, matrixSize3 # transposeOfA2[3][3]
move $s3, $s0 # *ptrA = &A[0][0]
move $s4, $s2 # *prtTA2 = &transposeOfA2[0][0]
# inputMatrix(A)
move $a0, $s0
jal inputMatrix
# transposeMatrixA1(A, transposeOfA1, 3)
move $a0, $s0
move $a1, $s1
li $a2, 3
jal transposeMatrixA1
# transposeMatrixA2(ptrA, ptrTA2, 3)
move $a0, $s3
move $a1, $s4
li $a2, 3
jal transposeMatrixA2
# outputMatrix(transposeOfA1)
move $a0, $s1
jal outputMatrix
# outputMatrix(transposeOfA2)
move $a0, $s2
jal outputMatrix
move $a0, $s3
move $a1, $s4
li $v0, 10
syscall
inputMatrix:
addi $sp, $sp, -12
sw $s0, 8($sp)
sw $s1, 4($sp)
sw $ra, 0($sp)
move $t0, $s0 # base address of A
li $s0, 0 # i = 0
j L1
L1:
addi $sp, $sp, -4
sw $ra, 0($sp)
mul $t1, $s0, 12 # i * 12
li $s1, 0 # j = 0
jal L2
lw $ra, 0($sp)
addi $sp, $sp, 4
addi $s0, $s0, 1 # i++
bne $s0, 3, L1 # if i != 3
lw $s0, 8($sp)
lw $s1, 4($sp)
lw $ra, 0($sp)
addi $sp, $sp, 12
jr $ra
L2:
sll $t2, $s1, 2
add $t3, $t1, $t2 # i * 12 + j * 4
add $t3, $t3, $t0 # A[i][j]
li $v0, 5
syscall
sw $v0, 0($t3)
addi $s1, $s1, 1
bne $s1, 3, L2 # if j != 3
jr $ra
transposeMatrixA1:
addi $sp, $sp, -12
sw $s0, 8($sp)
sw $s1, 4($sp)
sw $ra, 0($sp)
li $s0, 0 # i = 0
jal L3
lw $s0, 8($sp)
lw $s1, 4($sp)
lw $ra, 0($sp)
addi $sp, $sp, 12
jr $ra
L3:
addi $sp, $sp, -4
sw $ra, 0($sp)
mul $t0, $s0, 12 # i * 12
li $s1, 0 # j = 0
jal L4
addi $s0, $s0, 1
lw $ra, 0($sp)
addi $sp, $sp, 4
bne $s0, $a2, L3 # if i != size
jr $ra
L4:
mul $t2, $s0, 4 # i * 4
mul $t3, $s1, 4 # j * 4
mul $t1, $s1, 12 # j * 12
add $t4, $t0, $t3 # i * 12 + j * 4
add $t5, $t1, $t2 # j * 12 + i * 4
add $t4, $t4, $a0 # A[i][j]
lw $t4, 0($t4) # load A[i][j]
add $t5, $t5, $a1 # T[j][i]
sw $t4, 0($t5) # T[j][i] = A[i][j]
addi $s1, $s1, 1
bne $s1, $a2, L4 # if j != size
jr $ra
transposeMatrixA2:
addi $sp, $sp, -16
sw $s0, 12($sp)
sw $s1, 8($sp)
sw $s2, 4($sp)
sw $ra, 0($sp)
move $s0, $a0 # ptrB
move $s1, $a1 # ptrT
li $s2, 1 # i = 1
j L5
L5:
lw $t0, 0($s0) # load *ptrB
sw $t0, 0($s1) # *ptrT = *prtB
blt $s2, $a2, L6 # if i < size
j L7 # else
L6:
sll $t1, $a2, 2 # size * 4
add $s1, $s1, $t1 # ptrT += size
addi $s2, $s2, 1 # i++
j L8
L7:
addi $t1, $a2, -1 # size - 1
mul $t1, $t1, $a2 # size * (size - 1)
addi $t1, $t1, -1 # size * (size - 1) - 1
sll $t1, $t1, 2 # (size * (size - 1) - 1) * 4
sub $s1, $s1, $t1 # ptrT -= (size * (size - 1) - 1) * 4
li $s2, 1 # i = 1
j L8
L8:
mul $t1, $a2, $a2 # size * size
sll $t1, $t1, 2 # size * size * 4
add $t1, $a0, $t1 # B + size * size * 4
addi, $s0, $s0, 4 # prtB += 4
blt $s0, $t1, L5 # if ptrB < (B + (size*size))
lw $s0, 12($sp)
lw $s1, 8($sp)
lw $s2, 4($sp)
lw $ra, 0($sp)
addi $sp, $sp, 16
jr $ra
outputMatrix:
addi $sp, $sp, -12
sw $s0, 8($sp)
sw $s1, 4($sp)
sw $ra, 0($sp)
move $t0, $a0 # base address of A
li $s0, 0 # i = 0
j L9
L9:
addi $sp, $sp, -4
sw $ra, 0($sp)
mul $t1, $s0, 12 # i * 12
li $s1, 0 # j = 0
jal L10
lw $ra, 0($sp)
addi $sp, $sp, 4
addi $s0, $s0, 1 # i++
li $v0, 4
la $a0, changeLine
syscall
bne $s0, 3, L9 # if i != 3
lw $s0, 8($sp)
lw $s1, 4($sp)
lw $ra, 0($sp)
addi $sp, $sp, 12
jr $ra
L10:
sll $t2, $s1, 2
add $t3, $t1, $t2 # i * 12 + j * 4
add $t3, $t3, $t0 # A[i][j]
li $v0, 1
lw $a0, 0($t3)
syscall
li $v0, 4
la $a0, space
syscall
addi $s1, $s1, 1
bne $s1, 3, L10 # if j != 3
jr $ra
```
Make sure to check the memory when debugging. It helps a lot when we don't know what causes the answer to be wrong. Setting breakpoints and move step forward is also an important way of finding the problem.