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

NameRegisterUsage
$zero$0Always 0
$at$1Reserved for assembler use
$v0$v1$2$3Result values of a function
$a0$a3$4$7Arguments of a function
$t0$t7$815Temporary values
$s0$s7$16$23Saved registers
$t8$t9$24$25More temporaries
$k0$k1$26$27Reserved for OS kernel
$gp$28Global pointer
$sp$29Stack pointer
$fp$30Frame pointer
$ra$31Return address

Notes:

Registers:

Instructions

MnemonicOperandsInstructionRegister TransferTypeOp/Funct
addrd, rs, rtAddrd = rs + rtR0/20
subrd, rs, rtSubtractrd = rs - rtR0/22
addirt, rs, immAdd Imm.rt = rs + imm±I8
addurd, rs, rtAdd Unsignedrd = rs + rtR0/21
suburd, rs, rtSubtract Unsignedrd = rs - rtR0/23
addiurt, rs, immAdd Imm. Unsignedrt = rs + imm±I9
multrs, rtMultiply{hi, lo} = rs * rtR0/18
mulrd, rs, rtMultiply without overflowrd = rs * rtRc/2
divrs, rtDividelo = rs / rt; hi = rs % rtR0/1a
multurs, rtMultiply Unsigned{hi, lo} = rs * rtR0/19
mulurd, rs, rtMultiply without overflow, unsrd = rs * rtR0/19
divurs, rtDivide Unsignedlo = rs / rt; hi = rs % rtR0/1b
mfhirdMove From HJrd = hiR0/10
mflordMove From LOrd = loR0/12
mthirsMove to HIhi = rsR0/11
mtlorsMove to LOlo = rsR0/13
andrd, rs, rtAndrd = rs & rtR0/24
orrd, rs, rtOrrd = rs | rtR0/25
norrd, rs, rtNorrd = ̃(rs | rt)R0/27
xorrd, rs, rteXclusive Orrd = rs ˆ rtR0/26
andirt, rs, immAnd Imm.rt = rs & imm0Ic
orirt, rs, immOr Imm.rt = rs | imm0Id
xorirt, rs, immeXclusive Or Imm.rt = rs ^ imm0Ie
sllrd, rt, shShift Left Logicalrd = rt << shR0/0
srlrd, rt, shShift Right Logicalrd = rt >>> shR0/2
srard, rt, shShift Right Arithmeticrd = rt >> shR0/3
sllvrd, rt, rsShift Left Logical Variablerd = rt << rsR0/4
srlvrd, rt, rsShift Right Logical Variablerd = rt >>> rsR0/6
sravrd, rt, rsShift Right Arithmetic Variablerd = rt >> rsR0/7
sltrd, rs, rtSet if Less Thanrd = rs < rt ? 1 : 0R0/2a
slturd, rs, rtSet if Less Than Unsignedrd = rs < rt ? 1 : 0R0/2b
sltirt, rs, immSet if Less Than Imm.rt = rs < imm\pm ? 1 : 0Ia
sltiurt, rs, immSet if Less Than Imm. Unsignedrt = rs < imm\pm ? 1 : 0Ib
jaddrJumpPC = PC &0xF0000000 | (addr0<< 2)J2
jaladdrJump And Link$ra = PC + 8; PC = PC&0xF0000000 | (addr0<< 2)J3
jrrsJump RegisterPC = rsR0/8
jalrrsJump And Link Register$ra = PC + 8; PC = rsR0/9
beqrt, rs, offsetBranch if Equalif (rs == rt) PC += 4 + (imm\pm << 2)I4
bnert, rs, offsetBranch if Not Equalif (rs \ne rt) PC += 4 + (imm\pm << 2)I5
syscallSystem Callc0_cause = 8 << 2; c0_epc = PC; PC = 0x80000080R0/c
luirt,immLoad Upper Imm.rt = imm << 16If
lbrt,imm(rs)Load Bytert = SignExt(M1[rs + imm±])I20
lburt,imm(rs)Load Byte Unsignedrt = M1[rs + imm±] & 0xFFI24
lhrt,imm(rs)Load Halfrt = SignExt(M2[rs + imm±])I21
lhurt,imm(rs)Load Half Unsignedrt = M2[rs + imm±] & 0xFFFFI25
lwrt,imm(rs)Load Wordrt = M4[rs + imm±]I23
sbrt,imm(rs)Store ByteM1[rs + imm±] = rtI28
shrt,imm(rs)Store HalfM2[rs + imm±] = rtI29
swrt,imm(rs)Store WordM4[rs + imm±] = rtI2b

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.

PseudoOperandsInstructionRegister Transfer
moverd, rsMoverd = rs
lird,immMove immediaterd = imm
lard, labelLoad addressrd = &label
mulrd, rs, srcMultiply (no overflow)rd = rs * src
divrd, rs, srcDividerd = rs / src
remrd, rs, srcRemainderrd = rs % src
addrs, rd, immAdd immediate, use addird = rd + imm
addrd, immAdd immediaterd += imm
subrd, rs, srcSubtract immediaterd = rs imm
subrd immSubtract immediaterd -= imm
boffsetBranchgoto offset
beqzrs, labelBranch on equal zeroif (rs == 0) goto label
bnezrs, labelBranch on not equal zeroif (rs \ne 0) goto label
bgezrs, labelBranch on Greater Than or Equal to Zeroif (rs \ge 0) goto label
bgtzrs, labelBranch on Greater Than Zeroif (rs > 0) goto label
blezrs, labelBranch on Less Than or Equal to Zeroif (rs \le 0) goto label
bltzrs, labelBranch on Less Than Zeroif (rs < 0) goto label
beqrs, src, labelBranch on equalif (rs == src) goto label
bners, src, labelBranch on not equalif (rs \ne src) goto label
bgers, src, labelBranch on greater than equalif (rs \ge src) goto label
bgtrs, src, labelBranch on greater thanif (rs > src) goto label
blers, src, labelBranch on less than equalif (rs \le src) goto label
bltrs, src, labelBranch on less thanif (rs < src) goto label
seqrd, rs, srcSet equalrd = rs == src ? 1 : 0
snerd, rs, srcSet not equalrs = rt \ne src ? 1 : 0
sgerd, rs, srcSet greater than equalrs = rt \ge src ? 1 : 0
sgtrd, rs, srcSet greater thanrs = rt > src ? 1 : 0
slerd, rs, srcSet less than equalrs = rt \le src ? 1 : 0
sltrd, rs, srcSet less thanrs = 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

Nameoprsrtrdshamtfunct
Bits655556

J-Format

Nameoprsrtimm
Bits65516

I-Format

Nameopaddr
Bits626

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.

NameParametersDescription
.dataaddrData segment.
.textaddrText segment.
.kdataaddrKernel data segment.
.ktextaddrKernel text segment.
.externsym sizeDeclare as global label sym
.globlsymDeclare 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.

ServicesSystem Call CodeArgumentsResult
print_int1$a0=integer
print_float2$f12=float
print_double3$f12=double
print_string4$a0=string
read_int5integer (in $v0)
read_float6float (in $f0)
read_double7double (in $f0)
read_string8$a0=buffer,$a1=length
sbrk9$a0=amountaddress (in $v0)
exit10
print_char11$a0=char
read_char12char (in $a0)
open13$a0=filename (string), $a1=flags, $a2=modefile descriptor (in $a0)
read14$a0=file descriptor, $a1=buffer, $a2=lengthnum chars read (in $a0)
write15$a0=file descriptor, $a1=buffer, $a2=lengthnum chars written (in $a0)
close?16$a0=file descriptor
exit217$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