You are on page 1of 10

add $t4, $t2, $t1 # t4 = B[i] + C[i + 1]

sw $t4, 16($t3) # A[3i + 4] = B[i] + C[i + 1]

Example (2.3): For the following C statement, what is the corresponding MIPS assembly code?
Assume that the variables f, g, h, i, and j are assigned to registers $s0, $s1, $s2, $s3, and $s4,
respectively. Assume that the base address of the arrays A and B are in registers $s6 and $s7,
respectively: B[8] = A[i-j]

# Allocate:
# $s0 = f, $s1 = g, $s2 = h, $s3 = i, $s4 = j
# $s6 = A, $s7 = B

add $t0, $s3, $s3 # t0 = i + i = 2i


add $t0, $t0, $t0 # t0 = 2i + 2i = 4i
add $t1, $s4, $s4 # t1 = j + j
add $t1, $t1, $t1 # t1 = 2j + 2j = 4j
add $t2, $t0, $t1 # t2 = 4i - 4j
add $t2, $s6, $t2 # t2 = add. of A[i - j]
lw $t2, 0($t2) # t2 = A[i-j]
sw, $t2, 32($s7) # B[8] = A[i-j]

Alternatively:

sub $t0, $s3, $s4 # t0 = i - j


add $t0, $t0, $t0 # t0 = i - j + i - j = 2(i-j)
add $t0, $t0, $t0 # t0 = 2(i-j) + 2(i-j) = 4(i-j)
add $t1, $s6, $t0 # t1 = add. of A[i-j]
lw $t1, 0($t1) # t1 = A[i-j]
sw $t1, 32($s7) # B[8] = A[i-j]

Example (2.9): Translate the following C code to MIPS. Assume that the variables f, g, h, i, and j are
assigned to registers $s0, $s1, $s2, $s3, and $s4, respectively. Assume that the base address of the
arrays A and B are in registers $s6 and $s7, respectively. Assume that the elements of the arrays A and
B are 4-byte words: B[8] = A[i] + A[j];

# Allocate:
# $s0 = f, $s1 = g, $s2 = h, $s3 = i, $s4 = j
# $s6 = A, $s7 = B

add $t0, $s3, $s3 # t0 = i + i = 2i


add $t0, $t0, $t0 # t0 = 2i + 2i = 4i
add $t1, $s4, $s4 # t1 = j + j = 2j
add $t1, $t1, $t1 # t1 = 2i + 2i = 4j
add $t0, $s6, $t0 # t0 = add. Of A[i]
lw $t0, 0($t0) # t0 = A[i]
add $t1, $s6, $t1 # t1 = add. Of A[j]
lw $t1, 0($t1) # t1 = A[j]
add $t2, $t0, $t1 # t2 = A[i] + A[j]
sw $t2, 32($s7) # B[8] = A[i] + A[j]

Big Endian vs Little Endian


Computers are divided either into those that use the address of:

● the leftmost or “big end” byte as the word address. Thus big endian refers to reading the
memory address from left to right. Big endian has what's referred to as big end, i.e., the 2
leftmost bits.

● the rightmost or “little end” byte as the word address. Thus little endian refers to reading the
memory address from right to left. Little endian has what's referred to as little end, i.e., the 2
rightmost bits.
NOTE: MIPS is in the big-endian camp. Since the order matters only if you access the identical data
both as a word and as four bytes, few need to be aware of the endianness. Byte ordering is only a
problem when exchanging data among computers with different orderings.

Logical Operations and Bit Twiddling


Logical operations are operations in programming languages and instruction set architectures that
simplifies, among other things, the packing and unpacking of bits into words (e.g., ability to operate on
fields of bits within a word or even on individual bits). In short, bit instructions allow you to
manipulate data at the bit level. Although not common in high-level code (most HLLs provide
functions/operators to perform them though), their use is quite common in instructions generated.

Logical operations C operators MIPS instructions Comment

Shift left << sll (shift left logical) Useful for multiplying by a power of 2

Shift right >> srl (shift right logical) Useful for dividing by a power of 2

Bit-by-bit AND & and, andi Useful for bitmasking, specifically


resetting bits

Bit-by-bit OR | or, ori Useful for bitmasking, specifically


setting bits

Bit-by-bit NOT ~ nor MIPS implements NOT using nor with


one operand as zero

Shifting
Shifts move all the bits in a word to the left or right, filling the emptied bits with 0s.
Shift Left Logical
The instruction sll shifts all bits in the 32-bit data word to the left of the specified number of places,
from 1 to 31. The vacated positions are automatically filled with zeroes, which are proportional to the
n-bit left shift (i.e., shift 𝑛 left, then 𝑛 right positions will be 0s). The 𝑛 bits shifted out of the word are
lost.

For example, assuming that $t0 = 0xC0FEFE (0000 0000 1100 0000 1111 1110 1111 1110𝑡𝑤𝑜), a left
shift of 3 looks as follows in MIPS:

sll $t1, $t0, 3 # t1 = t0 << 3 bits

Now $t1 = 0000 0110 0000 0111 1111 0111 1111 0000. In decimal, $t0 is 12648190 and the
3
result of shifting left by 3 in $t1 is 101185520, which is $𝑡0 × 8 = $𝑡0 × 2 . Shifting left by 𝑖 bits gives
𝑡ℎ
the same result as multiplying by 2 to the 𝑖 power, just as shifting a decimal number by 𝑖 digits is
𝑖
equivalent to multiplying by 10 .

Shift Right Logical


The instruction srl shifts all bits in the 32-bit data word to the right of the specified number of places,
from 31 to 1. The vacated positions are automatically filled with zeroes, which are proportional to the
n-bit right shift (i.e., shift 𝑛 right, then 𝑛 left positions will be 0s). The 𝑛 bits shifted out of the word are
lost.
𝑡ℎ
Shifting right by 𝑖 bits gives the same result as dividing by 2 to the 𝑖 power. There are two problems
though:

● Integer division is not exact and thus any bits lost (shifted off the right side) mean the result is
inexact. We know this from integer division in general: 3/2 is 1 in integer division. This is
because the binary number 011 (decimal 3), when right-shifted one position becomes 001, and
0
the previous 2 bit is lost.
● What do we do when we right-shift a number that has its most-significant bit set? If we shift zero
bits in from the left, the sign bit is no longer set! This would be correct if the original number was
unsigned.
a. Example: 1100 in a four-bit unsigned number is decimal 12. If we right-shift this one
position and set the leftmost position to 0, the result is 0110, or 6 (base 10). This is the
correct answer. However, if the original number was interpreted as signed, its original
value would have been -4. When we right-shift -4 by 1 position it shouldn't become 6!
Instead we want to set the [shifted-in] most-significant bit to 1. This would produce 1110,
which, when interpreted as a signed four-bit number is -2.
b. These two types of right shifts are called logical (when we treat the number as unsigned,
and shift 0 bits in) and arithmetic (when we treat the number as signed, and replicate the
sign bit in the bits shifted in) respectively. Since there is only one type of left-shift and it
shifts 0 bits in, it is also called logical.

The srl instruction is specifically for doing logical shift, same as sll. For doing right arithmetic shift,
there’s the sra instruction.

AND (and)
AND is a logical bit-by-bit (each pair of bits is ANDed) operation with two operands that calculates a 1
only if there is a 1 in both operands.

Consider the instruction and $t1, $t2, $t3. Assuming $t2 contains 0xF38DB937 and $t3 contains
0xF, $t1 ends up with 0x7. It’s no easy to follow through a bit-by-bit operation using hex, so we convert
them to binary first:

t2: 1111 0011 1000 1101 1011 1001 0011 0111


t3: 0000 0000 0000 0000 0000 0000 0000 1111
———————————————————————————————————————————
t1: 0000 0000 0000 0000 0000 0000 0000 0111

Register $t3 acts as an erase/reset mask – removing all but the right four bits and giving a result of
0x7 stored in $t1. The and instruction is useful for applying a bit pattern (i.e., a mask) to a set of bits to
force 0s wherever there’s a 0 in the mask.
OR (or)
OR is logical bit-by-bit operation with two operands that calculates a 1 if there is a 1 in either operand.

Consider the instruction or $t1, $t2, $t3. Assuming $t2 contains 0xF38DB937 and $t3 contains
0x600, $t1 ends up with 0xF38DBF37.

t2: 1111 0011 1000 1101 1011 1001 0011 0111


t3: 0000 0000 0000 0000 0000 0110 0000 0000
———————————————————————————————————————————
t1: 1111 0011 1000 1101 1011 1111 0011 0111

If you squint hard enough, you’ll notice that $t3 (i.e., the mask) set bits 9-10 in $t2 and the result was
stored in $t1. Thus, register $t3 acts as an add/set mask. The or instruction is useful for applying a bit
pattern to a set of bits to force 1s wherever there is a 1 in the mask. A 0 in the mask just leaves the bit
as-is.

NOR (nor)
NOR is a bit-by-bit operation which calculates a 1 only if there’s a 0 in both operands. This is basically
calculating the NOT of the OR.

Assuming registers $t1 = 0xDC0 and $t2 = 0x3C00, the result of the MIPS instruction

nor $t0, $t1, $t2 # t0 = ~ (t1 | t2)

is this value in register $t0:


t1: 0000 0000 0000 0000 0000 1101 1100 0000
t2: 0000 0000 0000 0000 0011 1100 0000 0000
———————————————————————————————————————————
t0: 1111 1111 1111 1111 1100 0010 0011 1111

XOR (xor)
XOR is a bit-by-bit operation which calculates a 1 if only one of the bits is 1, but will be 0 if both are 0 or
both are 1

Assuming registers $t1 = 0xDC0 and $t2 = 0x3C00, the result of the MIPS instruction

xor $t0, $t1, $t2 # t0 = t1 ⊻ t2


is this value in register $t0:

t1: 0000 0000 0000 0000 0000 1101 1100 0000


t2: 0000 0000 0000 0000 0011 1100 0000 0000
———————————————————————————————————————————
t0: 0000 0000 0000 0000 0011 0001 1100 0000

If you look closely at $t0’s contents, every bit 1 in $t2 flipped the corresponding bit in $t1 (0 to 1, and 1
to 0). Thus, register $t2 acts as a flip mask. The xor instruction is useful for applying a bit pattern to a
set of bits to flipping 0s to 1s (and vice versa) wherever there is a 1 in the mask.

Constants
Constants are useful in AND, OR, and XOR logical operations as well as in arithmetic operations, so
MIPS also provides the instructions: and immediate (andi), or immediate (ori), and xor immediate
(xori). Constants are rare for NOR, since its main use is to invert the bits of a single operand; thus, the
MIPS instruction set architecture has no immediate version of NOR.

Bit Masking
You already know about and, or, and xor. Bit-masking uses these operators to reset, set, and flip bits in
a given bit pattern.

Logic recap:

AND (^) X^0=0 X^1=X

OR (v) Xv0=X Xv1=1

XORI (⊻) X ⊻0 = X X ⊻ 1 = X (inverse)

Example: Given the register $s4 (16 bits), reset the bits 11, 7, 5, and 0; set the bits 6, 3, and 1; and flip
the bits 9 and 2.

Summarizing the problem statement into a table, we’ve:

Masks Neutral Bit Pattern 16-bits Immediates Hex form Operations

Reset bitmask 1111 1111 1111 1111 1111 0111 0101 1110 0xF75E andi

Set bitmask 0000 0000 0000 0000 0000 0000 0100 1010 0x004A ori
Flip bitmask 0000 0000 0000 0000 0000 0010 0000 0100 0x0204 xori

Notice that for each particular mask, we start with the mask’s neutral bit pattern and then change the
bits accordingly. For example,

● For the reset mask (AND) its neutral bit pattern is a sequence of 1s since for any bit pattern X,
X ^ 1 = X. Then to reset specific bits, we change those particular 1s into 0s.
● For the set mask (OR), its neutral bit pattern is a sequence of 0s since for any bit pattern X, X v
0 = X.
● As for the flip mask (XOR), its neutral bit pattern is also a sequence of 0s since for any bit
pattern X, X ⊻0 = X. In the table above, the bits that have been reset/set/flipped for that
particular mask are bold and highlighted in red.

For this example, we’re using immediates that are 16 bits. Thus, the MIPS instructions are

andi $s4, $s4, 0xF75E # reset bits 11, 7, 5, 0


ori $s4, $s4, 0x004A # set bits 6, 3, 1
xori $s4, $s4, 0x0204 # flip bits 9, 2

Branching Instructions

Branch if Equal (beq), Branch if Not Equal (bne) and Jump (j)
Decision making is commonly represented in programming languages using the if statement,
sometimes combined with goto statements and labels. MIPS assembly language includes two
decision-making instructions, similar to an if statement with a goto:

● beq (branch if equal): beq REG1, REG2, L1


This instruction means go to the statement labeled L1 if the value in REG1 equals the value in
REG2.

● bne (branch if not equal): bne REG1, REG2, L1


This instruction means go to the statement labeled L1 if the value in REG1 isn’t equal to the
value in REG2.

These two instructions are known as conditional branches.

NOTE: In MIPS assembly, a label is simply a string of chars, digits, dot, or underscore characters,
followed by a colon that is on a line by itself used to name a location in memory. It may refer to the
location of a data value (variable) or of an instruction. Just think of a label as representing an address;
its aim is to make the Assembly programmer's life easier by allowing them to name an address and
jump straight to it instead of counting lines. Labels are used in loops, jumps, and variable names.

Example: Write MIPS assembly code fo the following C statement:

if (x == y) z = x - y;
else z = x + y;
z = 2 * z;

1. First we draw a control flow graph (CFG) to have a clearer picture:

2. Write the MIPS instructions:

# Allocate x = $s0, y = $s1, z = $s2

beq $s0, $s1, Then # if x == y goto Then


add $s2, $s1, $s2 # z = x + y
j Done # get out of if
Then: sub $s2, $s0, $s1 # z = x - y
Done: add $s2, $s2, $s2 # z = 2 * x

This example introduced the MIPS instruction j (unconditional jump). In general, jump instructions
modify the program counter (PC) so that execution continues at a specific memory address, no matter
the value of the current program counter. By contrast, branch instructions are always relative to the
current program counter. The j instruction is only one of the jump instructions; others are jal, jalr, and jr.
These latter ones will be discussed later on.

There’s nothing special about using beq for the conditional; we could also use bne and just move some
things around. Another way of accomplishing the same thing as above is as follows:
# Allocate $s0 = x, $s1 = y, $s2 = z

bne $s0, $s1, Else # if x =/= y goto Else


sub $s2, $s0, $s1 # z = x - y
j Done # finish if stmt and jump to Done
Else: add $s2, $s0, $s1 # z = x + y
Done: add $s2, $s2, $s2 # z = z *+ z = 2z

NOTE: The assembler relieves the compiler and the assembly language programmer from the tedium
of calculating addresses for branches, just as it does for calculating data addresses for loads and
stores.

Set on Less Than (slt)


The test for equality or inequality is probably the most popular test, but sometimes it is useful to see if a
variable is less than another variable. For example, a C-styled for loop may want to test to see if the
index variable is less than 0. In MIPS, this is done with an instruction that compares two registers and
sets a third register to 1 if the first is less than the second; otherwise, it is set to 0. The MIPS instruction
is called slt (set on less than). For example,

slt $t0, $s3, $s4 # t0 = 1 if s3 < s4

means that register $t0 is set to 1 if the value in register $s3 is less than the value in register $s4;
otherwise, register $t0 is set to 0.

Constant operands are popular in comparisons, so there is an immediate version of the set on less than
instruction, namely slti. To test if register $s2’s contents is less than the constant 10, we can just
write

slti $t0, $s2, 10 # t0 = 1 if s2 < 10

MIPS compilers use the slt, slti, beq, bne, and the fixed value of 0 (always available by reading
register $zero) to create all relative conditions: equal (=), not equal (≠), less than (<), less than or equal
(≤), greater than (>), greater than or equal (≥).

Example: Write MIPS assembly code for the following C statements:

int c = 10;
if (a > b) c = c + b;

You might also like