SPIM: MIPS emulator.
- Executes assembly source programs.
- Doesn’t execute binaries.
- Not an IDE, write your assembly in an IDE and execute with SPIM.
- Link: https://spimsimulator.sourceforge.net/
Tip: Vim Configuration for Assembly
" Show tabs set list " Never expand tabs to spaces set noexpandtab " Make tabs take up 8 spaces set tabstop=8 set shiftwidth=8
Note: In this class we’ll be learning 32-bit MIPS, not 64-bit MIPS.
MIPS Instructions:
Note: MIPS Instruction Set
- Stanford MIPS commercialised by MIPS Technologies
- Similar ISAs have a large share of embedded core market
- e.g., consumer electronics, network/storage, equipment, cameras, printers, vacuums, etc.
- Embedded: When the CPU is inside the system.
- Remember: MIPS is a 3-register instruction machine
- (When we use an instruction that doesn’t use 3 operands, we’re actually looking at a pseudo-instruction.)
| Name | Register | Usage |
|---|---|---|
$zero | $0 | Always 0 |
$at | $1 | Reserved for assembler use |
$v0—$v1 | $2—$3 | Result values of a function |
$a0—$a3 | $4—$7 | Arguments of a function |
$t0—$t7 | $8—15 | Temporary values |
$s0—$s7 | $16—$23 | Saved registers |
$t8—$t9 | $24—$25 | More temporaries |
$k0—$k1 | $26—$27 | Reserved for OS kernel |
$gp | $28 | Global pointer |
$sp | $29 | Stack pointer |
$fp | $30 | Frame pointer |
$ra | $31 | Return address |
Notes:
$zerois a read-only register.$atstands for “assembler temporary”.
- In general, we can’t/won’t be using
$at- Note how we can only return two values from a function (
$v0—$v1)
Registers:
$t0—$t7: Reg’s 8—15$t8—$t9: Reg’s 24—25$s0—$s7: Reg’s 16—23
| Mnemonic | Operands | Instruction | Register Transfer | Type | Op/Funct |
|---|---|---|---|---|---|
| add | rd, rs, rt | Add | rd = rs + rt | R | 0/20 |
| sub | rd, rs, rt | Subtract | rd = rs - rt | R | 0/22 |
| addi | rt, rs, imm | Add Imm. | rt = rs + imm± | I | 8 |
| addu | rd, rs, rt | Add Unsigned | rd = rs + rt | R | 0/21 |
| subu | rd, rs, rt | Subtract Unsigned | rd = rs - rt | R | 0/23 |
| addiu | rt, rs, imm | Add Imm. Unsigned | rt = rs + imm± | I | 9 |
| mult | rs, rt | Multiply | {hi, lo} = rs * rt | R | 0/18 |
| mul | rd, rs, rt | Multiply without overflow | rd = rs * rt | R | c/2 |
| div | rs, rt | Divide | lo = rs / rt; hi = rs % rt | R | 0/1a |
| multu | rs, rt | Multiply Unsigned | {hi, lo} = rs * rt | R | 0/19 |
| mulu | rd, rs, rt | Multiply without overflow, uns | rd = rs * rt | R | 0/19 |
| divu | rs, rt | Divide Unsigned | lo = rs / rt; hi = rs % rt | R | 0/1b |
| mfhi | rd | Move From HJ | rd = hi | R | 0/10 |
| mflo | rd | Move From LO | rd = lo | R | 0/12 |
| mthi | rs | Move to HI | hi = rs | R | 0/11 |
| mtlo | rs | Move to LO | lo = rs | R | 0/13 |
| and | rd, rs, rt | And | rd = rs & rt | R | 0/24 |
| or | rd, rs, rt | Or | rd = rs | rt | R | 0/25 |
| nor | rd, rs, rt | Nor | rd = ̃(rs | rt) | R | 0/27 |
| xor | rd, rs, rt | eXclusive Or | rd = rs ˆ rt | R | 0/26 |
| andi | rt, rs, imm | And Imm. | rt = rs & imm0 | I | c |
| ori | rt, rs, imm | Or Imm. | rt = rs | imm0 | I | d |
| xori | rt, rs, imm | eXclusive Or Imm. | rt = rs ^ imm0 | I | e |
| sll | rd, rt, sh | Shift Left Logical | rd = rt << sh | R | 0/0 |
| srl | rd, rt, sh | Shift Right Logical | rd = rt >>> sh | R | 0/2 |
| sra | rd, rt, sh | Shift Right Arithmetic | rd = rt >> sh | R | 0/3 |
| sllv | rd, rt, rs | Shift Left Logical Variable | rd = rt << rs | R | 0/4 |
| srlv | rd, rt, rs | Shift Right Logical Variable | rd = rt >>> rs | R | 0/6 |
| srav | rd, rt, rs | Shift Right Arithmetic Variable | rd = rt >> rs | R | 0/7 |
| slt | rd, rs, rt | Set if Less Than | rd = rs < rt ? 1 : 0 | R | 0/2a |
| sltu | rd, rs, rt | Set if Less Than Unsigned | rd = rs < rt ? 1 : 0 | R | 0/2b |
| slti | rt, rs, imm | Set if Less Than Imm. | rt = rs < imm\pm ? 1 : 0 | I | a |
| sltiu | rt, rs, imm | Set if Less Than Imm. Unsigned | rt = rs < imm\pm ? 1 : 0 | I | b |
| j | addr | Jump | PC = PC &0xF0000000 | (addr0<< 2) | J | 2 |
| jal | addr | Jump And Link | $ra = PC + 8; PC = PC&0xF0000000 | (addr0<< 2) | J | 3 |
| jr | rs | Jump Register | PC = rs | R | 0/8 |
| jalr | rs | Jump And Link Register | $ra = PC + 8; PC = rs | R | 0/9 |
| beq | rt, rs, offset | Branch if Equal | if (rs == rt) PC += 4 + (imm\pm << 2) | I | 4 |
| bne | rt, rs, offset | Branch if Not Equal | if (rs \ne rt) PC += 4 + (imm\pm << 2) | I | 5 |
| syscall | System Call | c0_cause = 8 << 2; c0_epc = PC; PC = 0x80000080 | R | 0/c | |
| lui | rt,imm | Load Upper Imm. | rt = imm << 16 | I | f |
| lb | rt,imm(rs) | Load Byte | rt = SignExt(M1[rs + imm±]) | I | 20 |
| lbu | rt,imm(rs) | Load Byte Unsigned | rt = M1[rs + imm±] & 0xFF | I | 24 |
| lh | rt,imm(rs) | Load Half | rt = SignExt(M2[rs + imm±]) | I | 21 |
| lhu | rt,imm(rs) | Load Half Unsigned | rt = M2[rs + imm±] & 0xFFFF | I | 25 |
| lw | rt,imm(rs) | Load Word | rt = M4[rs + imm±] | I | 23 |
| sb | rt,imm(rs) | Store Byte | M1[rs + imm±] = rt | I | 28 |
| sh | rt,imm(rs) | Store Half | M2[rs + imm±] = rt | I | 29 |
| sw | rt,imm(rs) | Store Word | M4[rs + imm±] = rt | I | 2b |
Note: Using
mult
- Multiplying two number with
multrequires two instructions, one to multiply (mult) and another to read the multiplication result from theHIorLOregister (mfhiormflo).- If the resulting number doesn’t overflow (is less than 32-bits), you can use
mfloto get it from theLOregister.What
hiandlocontain:
lo: Containsrs / rthi: Containsrs % rtExample :
li $t1, 2 # A: Multiplies t0 and t1 mult $t0, $t1 mflo $t0 # B: Same thing, using mul mul $t0, $t0, $t1
Note:
movev.s.li
movemoves the value of one register into another,liputs an immediate value directly into a register.
Pseudo-Instructions: Instructions that don’t have a direct hardware implementation.
| Pseudo | Operands | Instruction | Register Transfer |
|---|---|---|---|
| move | rd, rs | Move | rd = rs |
| li | rd,imm | Move immediate | rd = imm |
| la | rd, label | Load address | rd = &label |
| mul | rd, rs, src | Multiply (no overflow) | rd = rs * src |
| div | rd, rs, src | Divide | rd = rs / src |
| rem | rd, rs, src | Remainder | rd = rs % src |
| add | rs, rd, imm | Add immediate, use addi | rd = rd + imm |
| add | rd, imm | Add immediate | rd += imm |
| sub | rd, rs, src | Subtract immediate | rd = rs – imm |
| sub | rd imm | Subtract immediate | rd -= imm |
| b | offset | Branch | goto offset |
| beqz | rs, label | Branch on equal zero | if (rs == 0) goto label |
| bnez | rs, label | Branch on not equal zero | if (rs \ne 0) goto label |
| bgez | rs, label | Branch on Greater Than or Equal to Zero | if (rs \ge 0) goto label |
| bgtz | rs, label | Branch on Greater Than Zero | if (rs > 0) goto label |
| blez | rs, label | Branch on Less Than or Equal to Zero | if (rs \le 0) goto label |
| bltz | rs, label | Branch on Less Than Zero | if (rs < 0) goto label |
| beq | rs, src, label | Branch on equal | if (rs == src) goto label |
| bne | rs, src, label | Branch on not equal | if (rs \ne src) goto label |
| bge | rs, src, label | Branch on greater than equal | if (rs \ge src) goto label |
| bgt | rs, src, label | Branch on greater than | if (rs > src) goto label |
| ble | rs, src, label | Branch on less than equal | if (rs \le src) goto label |
| blt | rs, src, label | Branch on less than | if (rs < src) goto label |
| seq | rd, rs, src | Set equal | rd = rs == src ? 1 : 0 |
| sne | rd, rs, src | Set not equal | rs = rt \ne src ? 1 : 0 |
| sge | rd, rs, src | Set greater than equal | rs = rt \ge src ? 1 : 0 |
| sgt | rd, rs, src | Set greater than | rs = rt > src ? 1 : 0 |
| sle | rd, rs, src | Set less than equal | rs = rt \le src ? 1 : 0 |
| slt | rd, rs, src | Set less than | rs = rt < src ? 1 : 0 |
move $t0, $s0
is translated into this real instruction: addu $t0, $zero, $s0| Name | op | rs | rt | rd | shamt | funct |
|---|---|---|---|---|---|---|
| Bits | 6 | 5 | 5 | 5 | 5 | 6 |
op: Opcoders: First source register numberrt: Second source register numberrd: Destination register numbershamt: Shift amount00000 for nowfunct: Function code| Name | op | rs | rt | imm |
|---|---|---|---|---|
| Bits | 6 | 5 | 5 | 16 |
op: Opcoders: First source register numberrt: Second source register numberimm: Immediate value| Name | op | addr |
|---|---|---|
| Bits | 6 | 26 |
op: Opcodeaddr: Address (of a label).Related Notes: Hexadecimal, Number Systems (CS2640)
add $t0, $t1, $t2Converting Registers to Binary:
$t0: 01000 (rd)
$t1: 01001 (rs)$t2: 01010 (rt)According to the reference table, opcode and func is 0/32, therefore:
000000100000As this isn’t a shift instruction, shamt is 000000.
Putting it all together: \begin{aligned} \text{R-Format: }& \text{op $+$ rs $+$ rt $+$ rd $+$ shamt $+$ funct} \\ \text{Binary: }& 0000 0001 0010 1010 0100 0000 0010 0000 \\ \text{Hexadecimal: }& 012A4020 \end{aligned}
op is 0funct is 32Now that we know the op/fn, we know the type and command, and can convert the rest of the hexadecimal into binary and convert the rest.
Assembly Line Format:
[ label: ] opcode [ operand(s) ]
#Design Principle 1: Simplicity favors regularity.
- Regularity makes implementation simpler
- Simplicity enables higher performance at lower cost.
Arithmetic Operations: Have three operands.
# a <- b + c
add a,b,c f = (g + h) - (i + j)# t0 <- g + h
add $t0,$s1,$s2
# t1 <- i + j
add $t1,$s3,$s4
# f <- t0 - t1
sub f,$t0,$t1Design Principle 2: Smaller is faster
Anchor Link: Register Set Reference
Arithmetic instructions use register operands.
MIPS has a 32 \times 32-bit register file.
Assembler Names:
Label: Symbolic name for a memory address. Can be an instruction or data.
main. .text
main: add $t2,$t0,$t1Segment: Logical part of code that translates to a specific memory location.
Directives: Tell the assembler how to organize data.
| Name | Parameters | Description |
|---|---|---|
.data | addr | Data segment. |
.text | addr | Text segment. |
.kdata | addr | Kernel data segment. |
.ktext | addr | Kernel text segment. |
.extern | sym size | Declare as global label sym |
.globl | sym | Declare as global the label sym |
count: .word 0
# end.text .text
main: li $t0,10
li $t1,20
add $a0,$t0,$t1
# endli: Load immediate. Is a pseudo-instruction.li $t0,10 into addi $t0,$zero,$t0 .data
.word n # 32-bit
.byte nn # 8 bit
.ascii '?' # ASCII string
.asciiz "$$$" # zero-terminated ASCII string
.half n # 16-bit
.space n # n bytes.wordUse the lw (load word) command to load a value from a word into a register.
Use the sw (store word) command to load a value from a register back into a word.
Example: Loading a word into a register
.data
sumIs: .asciiz "The sum is "
value1: .word 15
value2: .word 25
sum: .word 0
.text
main:
lw $t0, value1
lw $t1, value2
# Print string
la $a0, sumIs
li $v0, 4
syscall
# Add integers and store in sum
add $t2, $t0, $t1
sw $t2, sum
# Print result
lw $v0, sum
li $v0, 1
syscall
# Exit
li $v0, 10
syscall
# End of programSyscall: Special instruction that interfaces with the I/O subsystem.
| Services | System Call Code | Arguments | Result |
|---|---|---|---|
print_int | 1 | $a0=integer | |
print_float | 2 | $f12=float | |
print_double | 3 | $f12=double | |
print_string | 4 | $a0=string | |
read_int | 5 | integer (in $v0) | |
read_float | 6 | float (in $f0) | |
read_double | 7 | double (in $f0) | |
read_string | 8 | $a0=buffer,$a1=length | |
sbrk | 9 | $a0=amount | address (in $v0) |
exit | 10 | ||
print_char | 11 | $a0=char | |
read_char | 12 | char (in $a0) | |
open | 13 | $a0=filename (string), $a1=flags, $a2=mode | file descriptor (in $a0) |
read | 14 | $a0=file descriptor, $a1=buffer, $a2=length | num chars read (in $a0) |
write | 15 | $a0=file descriptor, $a1=buffer, $a2=length | num chars written (in $a0) |
close? | 16 | $a0=file descriptor | |
exit2 | 17 | $a0=result |
Note: More on some syscalls
- print_int: Passes an integer and prints it on the console.
- print_float: Prints a single floating point number.
- print_double: Prints a double precision number.
- print_string: Passes a pointer to a null-terminated string.
- read_int, read_float, read_double: Read an entire line of input up to and including a newline.
- read_string: Same semantics as the UNIX library routine
fgets.
- Reads up to
n-1characters into a buffer and terminates the string with a null byte. If there are fewer characters on the current line, it reads through the newline and again null-terminates the string.- sbrk: returns a pointer to a block of memory containing n additional bytes
- exit: stops program execution
.data
str: .asciiz "Enter an integer to double: "
.text
main:
# Print a string (syscall 4)
la $a0, str # Put address to the string in $a0
li $v0, 4
syscall
# Read an integer (syscall 5)
li $v0, 5
syscall
move $t0, $v0 # Now move the number from $v0 to $t0
# Double user's integer
li $t1, 2
mult $t0, $t1
mflo $t0
# Print the resulting integer (syscall 5)
move $a0, $t0
li $v0, 1
syscall
# Exit Program
li $v0, 10
syscall
# End of program# This program does nothing and exits gracefully.
.text
main:
li $v0, 10
syscall
# The end .data
sumIs: .asciiz "The sum is "
.text
main:
li $t0, 15
li $t1, 25
# Print string
la $a0, sumIs
li $v0, 4
syscall
# Add integers and print to console
add $a0, $t0, $t1
li $v0, 1
syscall
# Print newline
li $a0, '\n'
li $v0, 11
syscall
# Exit
li $v0, 10
syscallImportant: Don’t forget to print a newline character before exiting the program!
beq rt, r, label
bne rd, rs, labelif statement, it also maintains the order of if and else in the code.Important: We will not be using the jump instruction.
if (a != o) {
// Do output
} # If zero, jump to the endif label
beqz $t0, endif
# Do output
endif:if (t0 < 10) {
// Do output
} # If zero, jump to the endif label
bge $t0, 10, endif
# Do output
endif:Guideline: To work with multiple
endiflabels, will just add a number to the end (e.g.,endif0:,endif1:, etc.)
if (t0 < 10) {
// output 1
} else {
// output 2
} bge $t0, 10, else
# output 1
b endif
else:
# output 2
endif:if (t0 == 0) {
t0++;
} else {
t1++;
}if: bnez $0, else
addi $t0, 1
b endif
else: addi $t1, 1
endif:To loop, we’ll use a register to control the loopExample: A while statement
.data
hello: .asciiz "hello\n"
.text
main:
li $t0, 1 # Initialize loop control register
while: bgt $t0, 10, endw # While loop
la $a0, hello
li $v0, 4
syscall
addi $t0, 1 # increment loop control register
b while
endw: li $v0, 10 # End of while loop
syscall
# end of programExample: Nesting an if statement inside a while
statement
.data
hello: .asciiz "hello\n"
.text
main:
li $t0, 1 # Initialize loop control register
while: bgt $t0, 10, endw # While loop
# Print loop counter variable if it is less than 5
bge $t0, 5, endif
move $a0, $t0
li $v0, 1
syscall
endif:
la $a0, hello
li $v0, 4
syscall
addi $t0, 1 # increment loop control register
b while
endw: li $v0, 10 # End of while loop
syscall
# end of programExample: While-loop in C v.s. MIPS
int t1=0;
int t0=1;
while (t0 <= 100) {
t1 += t0;
t0++;
} li $t1, 0
li $t0, 1
while: bgt $t0, 100, endw
addi $t1, $t1, $t0
addi $t0, 1
b while
endw:
These commands are used by bge, bgt, ble, and blt
seqsnesgesgtslesltRelevant Notes:
li $t1, 1 # Mask the LSBIT
and $t2, $t0, $t1
# If $t2 is zero, then t0 is even
bnez $t2, endif
# t0 is even, do whatever.
endif: li $t1, 3 # Mask last two LSBITS (this is 11 in binary)
and $t2, $t0, $t1
# If $t2 is zero, then t0 is divisible by four
bnez $t2, endif
# t0 is divisble by 4 (continue calculations here)
endif: Note: MSBIT Mask: 0x8000000
- (Use this to check if a signed integer is negatie)
By default, integers are signed,
Beware: Unsigned operations don’t generate traps on overflow, while signed operations will generate traps on overflow.
#include <stdio.h>
int sum;
int n = 100;
int main()
{
register int i = 1;
sum = 0;
while (i <= n) {
sum += i;
i++;
}
printf("%d\n", sum);
} .data
sum: .word 0
n: .word 100
.text
main:
li $t0, 1 # t0: i
sw $zero, sum
lw $t1, n
while: bgt $t0, $t1, endw
# sum += i
lw $t2, sum
add $t2, $t2, $t0
sw $t2, sum
# i++
addi $t0, $t0, 1
b while
endw: lw $a0, sum
li $v0, 1
syscall
# Exit
li $v0, 10
syscall
# End of program .data
sum: .word 0
n: .word 100
.text
main:
li $t0, 0
lw $t1, n
li $t2, 1
while: bgt $t2, $t1, endw
add $t0, $t0, $t2
addi $t2, $t2, 1
b while
endw: sw $t0, sum
lw $a0, sum
li $v0, 1
syscall
# Exit
li $v0, 10
syscall
# End of program .data
sum: .word 0
n: .word 100
.text
main:
# t0 <- n(n+1)/2
lw $t0, n
addi $t1, $t0, 1 # n + 1
mul $t0, $t0, $t1 # * (n + 1)
sra $t0, $t0, 1 # / 2
# Print and exit
sw $t0, sum
lw $a0, sum
li $v0, 1
syscall
li $v0, 10
syscall
# End of program