MIPS Assembly

SPIM

SPIM: MIPS emulator.

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

MIPS Instructions / Reference

Note: In this class we’ll be learning 32-bit MIPS, not 64-bit MIPS.

MIPS Instructions:

Note: MIPS Instruction Set

Register Set

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 $815 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:

Registers:

Instructions

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

What hi and lo contain:

Example :

    li  $t1, 2
    # A: Multiplies t0 and t1
    mult  $t0, $t1
    mflo  $t0
    # B: Same thing, using mul
    mul $t0, $t0, $t1

Pseudo-Instructions

Note: move v.s. li

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

Example: This pseudo-instruction move $t0, $s0 is translated into this real instruction: addu $t0, $zero, $s0

Instruction Formats

R-Format

Name op rs rt rd shamt funct
Bits 6 5 5 5 5 6

J-Format

Name op rs rt imm
Bits 6 5 5 16

I-Format

Name op addr
Bits 6 26

Encoding & Decoding Instructions

Related Notes: Hexadecimal, Number Systems (CS2640)

Example: Encoding an assembly instruction into hexadecimal
add $t0, $t1, $t2

Converting Registers to Binary: $t0: 01000 (rd)

According to the reference table, opcode and func is 0/32, therefore:

As 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}

Example: Decoding hexadecimal into an assembly instruction \begin{aligned} \text{Hexadecimal: }& 012A4020 \\ \text{First Six Binary Digits: }& 0000 00 \\ \text{Last Six Binary Digits: }& 10 0000 \\ \end{aligned}

Now 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.

More on MIPS Assembly

Assembly Line Format: [ label: ] opcode [ operand(s) ]

Arithmetic Operations

Design Principle 1: Simplicity favors regularity.

  1. Regularity makes implementation simpler
  2. Simplicity enables higher performance at lower cost.

Arithmetic Operations: Have three operands.

Example: Arithmetic Operations
# a <- b + c
add a,b,c 
Example: Arithmetic in C and MIPS
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,$t1

Register Operands

Design 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:

Labels and Main

Label: Symbolic name for a memory address. Can be an instruction or data.

      .text
main: add $t2,$t0,$t1

Segments and Linker Directives

Segment: 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
Example: Using a label.
count:  .word 0
# end
Example: Using .text
       .text
main:  li  $t0,10
       li  $t1,20
       add $a0,$t0,$t1
# end
      .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

More on Using .word

Use 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 program

Data Directives

Syscalls

Syscall: 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

Example: Using some syscalls
      .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

Template

# This program does nothing and exits gracefully.
  .text
main:
  li  $v0, 10
  syscall
# The end
Example: Adding two numbers
  .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
  syscall

Important: Don’t forget to print a newline character before exiting the program!

Branched Statements

beq rt, r, label
bne rd, rs, label

Important: We will not be using the jump instruction.

Example: Basic if in C v.s. MIPS
  1. C
if (a != o) {
  // Do output
}
  1. MIPS
      # If zero, jump to the endif label
      beqz      $t0, endif
      # Do output
endif:
Example: Basic if in C v.s. MIPS
  1. C
if (t0 < 10) {
  // Do output
}
  1. MIPS
  # If zero, jump to the endif label
  bge $t0, 10, endif
  # Do output
endif:

Guideline: To work with multiple endif labels, will just add a number to the end (e.g., endif0:, endif1:, etc.)

Example: Branched if in C v.s. MIPS
  1. C
if (t0 < 10) {
  // output 1
} else {
  // output 2
}
  1. MIPS
      bge      $t0, 10, else
      # output 1
      b endif
else:
      # output 2
endif:
Example: Else-if in C v.s. MIPS
  1. C
if (t0 == 0) {
  t0++;
} else {
  t1++;
}
  1. MIPS
if: bnez  $0, else
  addi  $t0, 1
  b endif
else: addi  $t1, 1
endif:

While Loop

To loop, we’ll use a register to control the loop

Example: 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 program
Example: 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 program
Example: While-loop in C v.s. MIPS
  1. C
int t1=0;
int t0=1;
while (t0 <= 100) {
  t1 += t0;
  t0++;
}
  1. MIPS
  li  $t1, 0
  li  $t0, 1
while:  bgt $t0, 100, endw
  addi  $t1, $t1, $t0
  addi  $t0, 1
  b while
endw:

More on Less-Than or Greater-Than

These commands are used by bge, bgt, ble, and blt

Masking Bits

Relevant Notes:

Example: Checking if a number is even using a mask
  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: 
Example: Checking if a number is divisible by four
  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

Signed Integers

By default, integers are signed,

Beware: Unsigned operations don’t generate traps on overflow, while signed operations will generate traps on overflow.

Example: Converting a C Program to MIPS

Example: Translating C to MIPS
  1. C
#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);
}
  1. One-to-one conversion to MIPS
  .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
  1. MIPS (Better)
  .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
  1. Or, you could use the explicit form: n(n+1)/2 so that the program is O(1) instead of O(n)
  .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