Download as pdf or txt
Download as pdf or txt
You are on page 1of 46

Chapter 3

Instruction Set Architecture


MIPS

1
What Is ISA?
• Instruction Set Architecture (ISA): Description of the computer by its
software/hardware interface (Blauuw’s definition) without emphasizing
any implementation detail.
• Thus, in this chapter we will not “open” the computer and discuss its
components. Rather we will focus on how to use it for programming
tasks.
• What we need to know are the principles of Von Neumann.
• In later chapters we will look inside the different components one by one,
and see how the ISA is actually implemented.
• Fundamental for understanding any ISA is the instruction set.
• Instruction Set (IS): Defines syntax and semantics of the language of the
computer.
• Actually, as per 7th Von Neumann principle, any instruction should be in
binary format. However, we will use a mnemonic notation, too.

2
Different Languages of the Computer
High-level swap(int v[], int k)
High level language (HHL): most abstract, language {int temp;
omits details, helps concentrate on program
(in C)
temp = v[k];
v[k] = v[k+1];
the problem to be solved, less }
v[k+1] = temp;

efficient, needs compiler to translate


it into machine language, easiest to
program in. Will use C-like notation C compiler
in this course.
Assembly language (AL): Almost the Assembly swap:
machine language itself, mnemonic language
program
muli $2, $5,4
add $2, $4,$2
notation, better understandable by (for MIPS) lw $15, 0($2)
lw $16, 4($2)
humans than ML, fast, but includes sw $16, 0($2)
sw $15, 4($2)
many details, needs assembler to jr $31

translate it into machine language,


low programmability. Will use MIPS
AL in our course. Assembler

Machine language (ML): “Mother tongue”


of the CPU, only binary format is Binary m achine 00000000101000010000000000011000
language 00000000100011100001100000100001
allowed, very fast, no need for program 10001100011000100000000000000000
translator, not easy to program in. (for MIPS) 10001100111100100000000000000100
10101100111100100000000000000000
Will use MIPS ML in our course. 10101100011000100000000000000100 3
00000011111000000000000000001000
What is Our Goal?
• Goals:
• Understand the instruction set of MIPS:
• Different types/classes and modes
• Rationales in MIPS design
• How?: Using the MIPS assembly language notation.
• Understand the physical mapping of those instructions into binary.
That is, understand the MIPS machine language.
• Have a feeling of what a compiler does in code generation phase.
• Be able to translate by hand a (small) C program into MIPS assembly
language and vice versa.
• Programming directly in MIPS (A/M)L.
• Practice with the MIPS simulator on our SUN pool.
• Why know all that? In order to be able to assess program’s efficiency,
tune programs, find “hidden” program errors, understand some work
of compiler/assembler, be an ISA designer, but most importantly in
order to be able to be a computer architect (ala Blauuw).
4
MIPS Sequential Instructions
• Let’s begin with the simplest instruction, namely, addition.
• Addition:
• Syntax: add a, b, c
• Semantics: a = b + c; (we use C language semantics)
• Rule 1: MIPS arithmetic operations always use 3 operands (here a, b, and
c).
Why exactly 3 operands? Because it’s simpler (thus faster) for hardware
implementation.
Design principle 1: Simplicity favors regularity.
• Also, you can add a comment after the # symbol, when programming in
MIPS (comment ends with new line). Hence the last instruction is
equivalent to:
add a, b, c # this is a MIPS instruction that adds b to c and …
• Example: Program a = b + c + d + e; in MIPS.

5
MIPS Sequential Instructions
• Subtraction is very similar to addition.
• Subtraction:
• Syntax: sub a, b, c
• Semantics: a = b – c;
• Example: f = (g+h) – (i+j);
à Need temporary variables to store g+h and i+j
• Last example, needed temporary variables (i.e. operands) . This leads to
the question, what are temporary variables and variables in general in
MIPS?
• Rule 2: MIPS arithmetic operations accept only register operands.
• A MIPS register is 32 bits long. A MIPS word = 32 bits. Thus, a MIPS
register is 1 word long.
• MIPS has 32 registers (visible to the programmer).
àWhy only 32? Design principle 2 : Smaller is faster
• The preceding rule (Rule 2) says that prior to issuing an arithmetic
operation, we have to make sure that its operands are in some of the
available 32 registers.
6
MIPS Sequential Instructions
• How to designate a register in a MIPS program?
• Use the symbols $s0, $s1, … for program variables.
• Use the symbols $t0, $t1, … for temporary variables.
• Back to last example: f = (g+h) – (i+j)
For example use: f à $s0, g à $s1, h à $s2, i à $s3, j à $s4 and {$t0 $t1}
for temporary variables.
• Suppose your operands are in memory. What do you have to do in order
to add them? The first thing (because of Rule 2) is to transfer them into
registers.
How? à Using data transfer instructions.
• Data transfer instructions:
• Load instruction:
Syntax: lw register mem # lw stands for “load word”
Semantics: register = value stored in memory location mem;

7
MIPS Sequential Instructions
• Data transfer instructions (cont’d):
• Store instruction:
Syntax: sw register mem # sw stands for “store word”
Semantics: memory location mem = value stored in register;
• Thus, mainly we have two data transfer instructions that operate word-
wise.
• We still don’t know how to specify mem in both instructions!
• In both instructions:
• Syntax of mem: constant(register) (where constant = c*4 for some integer c)
• Semantics: specified memory address = constant + value in register.
• Why *4? Because our word size is 32 bits = 4 bytes and memories are
addressable byte-wise (what if we had a different word size?).
• Maybe it is convenient to mention here that MIPS is a big endian
architecture (like SUN machines). What is that? Let word = byte 3 + byte
2 + byte 1 + byte 0 (where byte 3/0 is the most/least significant one).
• Big endian: address(word) = address(byte 3)
• Little endian: address(word) = address(byte 0) (e.g. Intel CPUs)
8
MIPS Sequential Instructions
• Terminology:
• Offset: the constant in mem.
• Base register (or index register): the register between parentheses in mem.
• Rule 3: Alignment restriction
Address(any word in memory) = 0 mod 4. (What are the 2 least significant
bits of word addresses then?)
• Load & Store more precisely:
Load: lw register offset(base) # register = Memory[offset+base]
Store: sw register offset(base) # Memory[offset+base] = register
(Attention: offset = 4*c for some integer c should be done explicitly in MIPS)
• Examples for load & store:
Assume we have the register association (done e.g. by compiler):
hà$s2, base address of array A à $s3, ià$s4
Give the equivalent MIPS code for the following C statements:
a) i = h + A[5];
b) A[10] = h + A[5];
c) A[10] = h + A[i]; 9
MIPS Sequential Instructions
• Register Scheduling/Spilling:
Compilers do their best to use the small number (32) of registers
efficiently. The assignment of (the many) program variables to registers is
called register scheduling. The process of putting less commonly used
variables in memory is called spilling registers (compare memory
hierarchy of Chapter 1).

• Attention:
We have discussed so far two arithmetic instructions and two transfer
instructions (add, sub, lw, and sw). Our discussion however did not use
any binary code! In fact, what we discussed is the representation of these
instructions in assembly code. But don’t worry, there is only a very tiny
step to do in order to understand how the machine actually works with
our Assembly notation. And this is our next topic.

10
MIPS Sequential Instructions
• The alphabet of the computer is {0, 1} (compare: English alphabet is {a,
…, z})
• Anything in memory is a binary “number” be it originally a number,
instruction, address, register, … (that was the stored program concept!).
• Let’s begin with the binary code of some registers in MIPS:
• {$s0, …, $s7} à {16, …, 23}
• {$t0, …, $t7} à {8, …, 15}
• Example:
add $t0, $s1, $s2 will be coded in binary as:
0 17 18 8 0 32

Attention! not used together say:


$s1 $s2 $t0
register ordering here this is the add
is changed internally instruction

Actually, above is still in decimal, what we have in memory is:


000000 10001 10010 01000 00000 100000 (= 32 bits)
In MIPS: All instructions are 32 bits (= 1 word) long. 11
MIPS Sequential Instructions
• Let’s discuss MIPS instruction format more thoroughly.
• The different fields are (R-type, see next slide):
op rs rt rd shamt funct
6 bits 5 bits 5 bits 5 bits 5 bits 6 bits
• Meaning:
op: basic operation often called opcode.
rs: first register source operand.
rt: second register source operand.
rd: register destination operand (where result is stored).
shamt: shift amount (needed for shift instructions only, otherwise 0).
funct: function; selects a specific type of the operation in the op field,
also called function code.
• Problem: Load instruction needs 2 registers and a constant. Thus using
above format would be too restrictive, since we would be able to assign
only 5 bits to our constant (that is constant < 32). This is not acceptable,
since these constants could be offsets of data included in large arrays.
Solution in MIPS: different instruction formats (but all with size = 32 bits).
12
MIPS Sequential Instructions
• Instruction format of last slide is called R-type (for registers) or R-format.
R-type is not convenient for load/store.
• I-type or I–format is used for data transfer instruction (load/store):
op rs rt address
6 bits 5 bits 5 bits 16 bits
• Now, we can put our offset in the address field of the load instruction;
hence –2^(15) <= offset <= 2^(15) (in bytes).
• Example: lw $t0 100($s3) would be encoded as:
35 19 8 100

Again the formats we have learned so far:


R 0 reg1 reg2 reg3 0 32 reg3=reg1+reg2

R 0 reg1 reg2 reg3 0 34 reg3=reg1-reg2

I 35 base reg offset (16 bits) reg=Memory[offset+base]

I 43 base reg offset (16 bits) Memory[offset+base]=reg


13
MIPS Sequential Instructions
• Example:
Translate the C assignment A[4] = h + A[4]; into MIPS assembler code
and give then the binary layout of your program in memory. Use $s2 for
h and assume the base is already in $t1.
• Partial solution: (fill in the blanks)
• Assembler code:
(1) lw $t0, ?($t1) # $t0 = A[4]
(2) ? $t0, $s2 ? # $t0 = h+A[4]
(3) sw $t0, 16(?) # A[4] = $t0
• Machine code:
(1) 35 ? ? 16
(2) ???
(3) ? 9, ? ?
Remember again: above machine code should be in binary not in
decimal as shown.

14
MIPS Conditional Instructions
• Like any programming language MIPS includes instructions for decision
making.
• Branch instructions (2 instructions):
• Syntax: beq register1, register2, L1
Semantics: if(register1 == register2) go to L1;
• Syntax: bne register1, register2, L1
Semantics: if(register1 != register2) go to L1;
In both instructions L1 is a label embedded in the code in the form:
L1: Any MIPS instruction
• Example:
Translate the following C code into MIPS assembly language:
if(i == j) go to L1;
f = g + h;
L1: f = f – i;
Answer: Assume {f, …, j} corresponds to {$s0, …, $s4}.

15
MIPS Conditional Instructions
• Obviously, we need for if-then-else statements to skip some part of the
code. And here is the MIPS instruction for doing that.
• Jump instruction:
Syntax: j Label # unconditional jump to Label
Semantics: go to Label;
Now, we are prepared for the next example.
• Example:
How is the following C code fragment translated?
if(i == j) f = g + h; else f = g – h;
Answer:
Similar to last example. You only need to know how to use the Jump
instruction correctly.

16
MIPS Iterative Instructions
• Again, like any programming language MIPS includes looping
instructions.
• However, we do not need any new instruction for loops, we could just use
beq/bne appropriately to construct loops.
• Example:
Translate the following C code into MIPS assembly language:
Loop: g = g + A[i];
i = i + j;
if (i != h) go to Loop;
Partial solution: Assume {g, h, i, j} à {$s1, …, $s4} and
base address of A[] is already in $s5
Loop: add $t1, $s3, ? # $t1 = 2*i
? $t1, ?, ? # $t1 = 4*i (or 2*$t1)
add $t1, ?, ? # $t1 = address of A[i]
lw $t0, ?($t1) # $t0 = A[i]
? $s1, ?, ? # g = g + A[i]
add ?, $s3, ? #i=i+j
? ?, ?, Loop # go to Loop if i != h
17
MIPS Iterative Instructions
• In passing: Code fragments like the preceding that begin with a label and
end with a branch instruction (in other words: that only include simple
sequential instructions) bear the name basic block.
• Now let’s be more realistic and try to translate a while-loop.
• Example:
Translate the following C while-loop into MIPS assembly language:
while (save[i] == k) i = i + j;
Assume: {i, j, k} à {$s3, $s4, $s5}, and base of save[] is in $s6.
Partial solution:
Loop: add $t1, $s3, ? # $t1 = 2*i
? $t1, ?, ? # $t1 = 4*i (or 2*$t1)
add $t1, ?, ? # $t1 = address of save[i]
lw $t0, ?($t1) # $t0 = save[i]
? ?, ?, Exit # go to Exit if save[i] != k
add ?, $s3, ? #i=i+j
??? # go to Loop
Exit:
18
MIPS Iterative Instructions
• For counting loops it’s more convenient to work with the following
instruction.
• Set On Less Than Instruction:
Syntax: slt register1 register2 register3
Semantics: resgister1 = (register2 < resigter3? 1 : 0);
• MIPS compilers use beq, bne, slt, and register $zero whose value is 0
(and which is register # 0) to create any desired condition (e.g. >=, >, …).
• Example: if(a >= b) goto Label;
• Let’s think about how a typical C switch instruction would be
implemented in MIPS assembly language.
• Example:
switch (k) {
case 0: f = i + j; break;
case 1: f = g +h; break;
case 2: f = g - h; break;
case 3: f = i - j; break;
} 19
Switch in MIPS
• Remark: Clearly, we can think of the switch statement as a sequence of
if-the-else statements and solve directly. However, below is a more
elegant/compact solution that uses the following instruction.
• Jump register instruction:
Syntax: jr register
Semantics: goto address contained in register;
(Attention: this jump is unconditional and indirect)
• Solution (for Switch example):
Assume: {f, …, k} à {$s0, …, $s5} and $t2 contains the value 4.
First we make sure that k is in the range [0, 3]:
slt $t3, $s5, $zero # test if k < 0
bne $t3, $zero, Exit # if k < 0 go to Exit
slt ? ? ? # test if k < 4
bne ? ? Exit # if k >= 4 go to Exit

20
Switch in MIPS
• Solution (cont’d):
Now, assume that the four switch cases correspond to labels L0, …, L3
and that we already managed to put these labels in an array (in memory)
with starting address in $t4 (in other words we have: $t4[i] = Li). Hence,
if we can put $t4[k] in some register, say $t0, we can use jr $t0 to jump to
the correct label.
First, we multiply (as usual in MIPS) the index k (=$s5) by 4 to get the
correct byte address:
add $t1, $s5, $s5 # $t1 = 2*$s5
add $t1, $t1, $t1 # $t1 = 4*$s5
Now, we set $t0 = $t4[k]:
add $t1, $t1, $t4 # $t1 = address of $t4[k]
lw $t0, 0($t1) # $t0 = $t4[k]
Now we execute the (indirect) jump:
jr $t0
21
Switch in MIPS
• Solution (cont’d):
The rest is easy, just put a label for each line in the C code and exit:
L0: add $s0, $s3, $s4
j Exit
L1: add $s0, $s1, $s2
j Exit
L2: sub $s0, $s1, $s2
j Exit
L3: sub $s0, $s3, $s4
Exit:

22
Binary Formats of Jump Instructions
• See textbook pp. 131:

R op=18 rs=18 rt=19 rd=17 shamt=0 funct=42 slt $s1,$s2, $s3

R op=0 rs=9 rt=0 rd=0 shamt=0 funct=8 jr $t1

I op=4 rs=17 rt=18 16 bit address=25 beq $s1, $s2, 100

I op=5 rs=17 rt=18 16 bit address=25 bne $s1, $s2, 100

J op=2 26 bit address=2500 j 10000

I hope by now you understand


why people don’t like programming
in machine language!

23
MIPS Procedure Calls
• Idea of a procedure (function):

Input
Output
Procedure1
(e.g. main) Procedure 2
(e.g. foo)

• Rules for Procedure Calls:


1. Caller places (input) parameters where callee can access them.
2. Caller transfers control to callee.
3. Callee acquires storage for resources needed by the procedure.
4. Callee performs desired computation.
5. Callee places result in a place where the caller can access it.
6. Callee returns control to caller (point of origin) 24
MIPS Procedure Calls
• Details of last steps in MIPS:
1. Caller uses the argument registers $a0, …, $a3.
2. Jump and Link instruction:
Syntax: jal ProcedureAddress
Semantics: $ra = PC + 4; goto ProcedureAddress;
$ra is return address register: a specific register used for this purpose only.
3. Callee uses a stack to acquire needed storage:
A specific register $sp is used for the stack pointer (see example below).
4. This is the code of the callee.
5. Result is placed in value registers $v0, $v1.
6. Callee issues jr $ra instruction to transfer control back to caller.
• Example:
How is the following procedure programmed in MIPS?
int leaf(int g, int h, int i, int j) {
int f;
f = (g+h) - (i+j);
return f; } 25
MIPS Procedure Calls
• Solution:
Because of rule (1), we assume caller assigned:
{g, h, i, j} à {$a0, $a1, $a2, $a3}
Also, we need to begin the procedure by a label (to jump to by caller).
In this example, we will assign $s0 to f and make use of $t0 and $t1.
However, we should save any of these registers to make sure that values
needed by the caller are not overwritten. To this end, we shall use the stack
memory. Also, suppose we can give constants as arguments for add/sub.
leaf: # this is the address of our procedure
sub $sp, $sp, 12 # this allocates memory for 3 items ($s0, $t0, and $t1)
sw $t1, 8($sp) # push $t1 on the stack
sw $t0, 4($sp) # push $t0 on the stack $t1
sw $s0, 0($sp) # push $s0 on the stack $t0
$s0
$sp
26
MIPS Procedure Calls
• Solution (cont’d):
Now we can perform the needed computation:
add $t0,$a0, $a1 # $t0 = g+h
add $t1,$a2, $a3 # $t1 = i+j
sub $s0,$t0, $t1 # f = $t0 - $t1
We have to put the result of our procedure in a value register:
add $v0, $s0, $zero # $v0 = f
Almost done now, but we need to restore the values of $t0, $t1, $s0, and $sp:
lw $s0, 0($sp) # $s0 = its old value
lw $t0, 4($sp) # $t0 = its old value
lw $t1, 8($sp) # $t1 = its old value
add $sp, $sp, 12 # $s0 = its old value
And finally, we jump back to caller:
jr $ra # goto caller
27
MIPS Procedure Calls
• Another MIPS rule:
$t0, …, $t1:
Temporary registers need not be saved by the callee (caller should save them
if needed).
$s0, .., $s7:
Saved registers, should always be saved by the callee if used.
Thus: In last example we only needed to save $s0.
This rule reduces (the costly) register spilling.
• The next example shows how to manage nested procedures in MIPS.
• Example: A recursive procedure for factorial
int fac(int n) {
if (n < 1) return 1;
else return n*fac(n-1);
}

28
MIPS Procedure Calls
• Solution of fac():
Since we have a nested procedure we need to save arguments/return ads:
fac:
sub $sp, $sp, 8 # adjust stack for 2 items
sw $ra, 4($sp) # push return address $ra on the stack
sw $a0, 0($sp) # push argument on the stack
Check whether or not argument n is < 1:
slt $t0, $a0, 1 # is n < 1?
beq $t0, $zero, L1 # if n >= 1 goto L1
Handle the case where n < 1:
add $v0, $zero, 1 # return 1 ($v0 = 1)
add $sp, $sp, 8 # pop 2 items off the stack
jr $ra # go to caller (address after jal)
Handle the case where n >=1:
L1:
sub $a0, $a0, 1 # n = n-1
jal fac # call myself recursively with n-1 as argument 29
MIPS Procedure Calls
• Solution of fac() (cont’d):
The next instruction will be executed after a return. Thus, we have to restore
the old argument, the old return address, and the stack pointer:
lw $a0, 0($sp) # restore old n
lw $ra, 4($sp) # restore old return address
add $sp, $sp, 8 # adjust stack pointer to pop 2 items
Assuming we have mul instruction for multiplication, we finish with:
mul $v0, $a0, $v0 # return n*fac(n-1)
jr $ra # goto caller again

• What we have learned:


• Preserved by callee (i.e. should be saved if needed by callee):
$s0, …, $s7, $sp, $ra, stack above $sp
• Not preserved by callee (i.e. may change during the procedure call):
$t0, …, $t9, $a0, …,$a4, $v0, $v1, stack below stack pointer
30
MIPS Procedure Calls
• What about local variables in general (e.g. if they don’t fit in registers)?
• Compilers allocate memory for local variables (e.g. scalars, arrays etc.)
on the stack. Each procedure call means for the compiler to push
different data items on the stack. The whole data set pushed per
procedure call is called activation record or procedure frame.
• Compilers generally use a special register to point to the bottom the
activation record. This register is called frame pointer, and MIPS
compilers use $fp (register 30) as a frame pointer. Frame pointer is fixed
during a call. Its use is not compulsory but facilitates address arithmetic.
• The general layout of the stack after a procedure call is:
$fp Saved argument
registers (if any)
Saved return
address
Saved saved
registers (if any)

Local arrays,
structures
(if any)
$sp 31
MIPS Procedure Calls
• MIPS Register Convention:

Name Register number Usage


$zero 0 the constant value 0
$v0-$v1 2-3 values for results and expression evaluation
$a0-$a3 4-7 arguments
$t0-$t7 8-15 temporaries
$s0-$s7 16-23 saved
$t8-$t9 24-25 more temporaries
$gp 28 global pointer
$sp 29 stack pointer
$fp 30 frame pointer
$ra 31 return address

And:
Register 1 ($at) reserved for the assembler
Registers 26, 27 ($k0, $k1) reserved for the OS

32
MIPS Addressing Modes
• Byte transfer instructions: Example: copying a byte
lb $t0, 0($sp) # LSB($t0) = byte at address 0($sp) (LSB: least significant byte)
sb $t0, 0($gb) # store the read byte back to memory at address 0($gp)
• See textbook pp. 143 for another example (strcpy())
• Immediate operands in MIPS:
• Use of constants (<= 16 bits) in instructions is allowed.
• Example: Variant of add with an immediate operand:
addi $sp, $sp, 4 # $sp = $sp + 4
• Example: Variant of slt with an immediate operand:
slti $t0, $s2, 10 # $t0 = ($s2 < 10? 1: 0);
• Want to work with operands > 16 bits?
• Use load upper immediate instruction (lui)
• Example: lui $t0, 255 # 2MSB($t0) = 255, 2LSB($t0) = 0
# (MSB: most significant byte)
• Example: a = 2^31+2+1 (suppose a à $s0)? (hint: use lui and addi)
33
MIPS Addressing Modes
• Addressing in Jump instructions:
j 10000
op=2 26 bit address=2500

Thus, we have 26 bits for the address (10000 = 2500*4)


• Addressing in Branch instructions:
bne $s0, $s1, Exit # if($s0 != $s1) goto Exit;
op=5 16 17 Exit

• Problem: “Exit” must fit in 16 bits


à too restrictive
• Solution: PC-relative addressing: Exit := PC + Exit
• Thus, we have now: bne $s0, $s1, Exit # if($s0 != $s1) goto PC+Exit;
• Target address in [PC-2^15, PC+2^15].
• Rationale: Branch instructions are within loops and don’t need “far” jumps.
34
MIPS Addressing Modes
• Example for PC-relative addressing:
Let’s look at a piece of code in memory (assuming it begins at address 800).
(I write binary instructions separating fields by dots!)
Assembly Code Address Machine Code
Loop: lw $t0, 0($t1) 800 35.9.8.0
bne $t0, $s5, Exit 804 5.8.21.8 # 8 is relative to PC+4 = 808
add $s3, $s3, $s4 808 0.19.20.19.0.32
j Loop 812 2.800 # 800 full address of Loop
Exit: … 816 …
• Addressing in Jump and Link :
Like in jump instruction (26 bits for address)
• Remark:
• Actually, in both instructions, j and jal, 28 bits are available, since addresses in
MIPS are word addresses. (Thus: 800 := 200 in above example).
• Same trick (i.e. *4) is used for PC-relative addresses, thus, target is actually in
[PC-2^17, PC+2^17]. (Thus: 8 := 2 in above example).

35
MIPS Addressing Modes
• What if the address in a conditional branch does not fit in 16 bits?
à No problem, make use of (unconditional) jump instruction, which offers 26 bits.
à How? (see next example)
• Example for working with target addresses outside the typical range:
beq $s0, $s1, 2^20
add …
is replaced by:
bne $s0, $s1, L
j 2^20
L: add …
• Pseudo-direct addressing (used in j/jal):
Here, the target address is computed as follows:
A = “4 most significant bits of PC” + “26 bits of instruction” + “00”
“+” = concatenation (not addition!)

36
MIPS Addressing Modes
• Summary of MIPS addressing modes:
• Register addressing: ad(operand) = register (number).
• Base or displacement addressing: ad(operand) = offset+register.
• Immediate addressing: operand in instruction itself.
• PC-relative addressing: address = offset + PC.
• Pseudo-direct addressing: address = “4 bits of PC” + “26 bits of
instruction” + “00”.
• Pseudo-instructions:
• Instructions that do not exist at hardware level and only introduced for the
convenience of programming in assembly language.
• It is the task of the assembler to translate them to “real” instruction
(sequences).
• Examples: move $t0, $t1 # $t0 = $t1
(would be translated to: add $t0, $zero, $t1)
Also, different branch variants are accepted: e.g. bgt (>), bge (>=) , …
• Similarly, assemblers do accept in general numbers in decimal,
hexadecimal, … notation. 37
MIPS Simulator on SUN Machines
• Write your code using your favorite editor.
Sample format for your code:
.text
.globl main
main:
<insert here your instruction sequence>
.data:
<insert here your data>
• Issue the following commands:
• cd /usr/local/bin
• xspim
• Last command will start the simulator.
• From xspim:
• Load your file.
• Run it.
• Other commands (single step mode, breakpoints, …) also available.
• You should spend some (long?) time playing with the simulator as the next
assignment will be a programming one. 38
More Programming Examples
• Example: Swap in MIPS
Translate the following C code into MIPS assembly language:
void swap(int v[], int k) { int temp; temp = v[k]; v[k] = v[k+1]; v[k+1] = temp; }
• Solution:
Input: v à $a0, k à $a1
Allocation of stack memory: no, we simply assign temp à $t0
Code for procedure body:
swap: add $t1, $a1, $a1 # $t1 = 2*k
add $t1, $t1, $t1 # $t1 = 4*k
add $t1, $a0, $t1 # $t1 = v + 4*k
lw $t0, 0($t1) # $t0 = v[k]
lw $t2, 4($t1) # $t2 = v[k+1]
sw $t2, 0($t1) # v[k] = $t2
sw $t0, 4($t1) # v[k+1] = $t0
Release of stack memory: No, since no such memory has been used.
Return to caller:
jr $ra 39
More Programming Examples
• Example: Sorting in MIPS
Translate the following C code into MIPS assembly language:
void sort(int v[], int n) { int i, j;
for(i = 0; i < n; i = i+1) {
for(j = i -1; j >= 0 && v[j] > v[j+1]; j = j-1) {
swap(v, j);
}}}
• Solution:
We first seek a pseudo-solution and then think about register allocation and the like.
Code for procedure body:
We have two nested for-loops! Call outer loop for1 and inner loop for2.
Pseudo-code for for2:
1. move j, i-1 # init for2
2. for2: blt j, 0, endfor2 # test condition 1
3. ble v[j], v[j+1], endfor2 # test condition 2
4. jal swap(v, j) # call swap
5. sub j, j ,1 # decrement the j
6. j for2 # loop back to for2
7. endfor2: # we are done with for2 40
More Programming Examples
• Solution (cont’d):
Pseudo-code for for1:
1. move i, 0 # init for1
2. for1: bge i, n, endfor1 # test condition
3. j for2 # do the work
4. add i, i, 1 # increment the i
5. j for1 # loop back to for1
6. endfor1: # we are done with for1
Let’s think now about the register allocation:
We know v à $a0 and n à $a1
Reading the steps of for1 we see:
1. move i, 0: The i should be assigned a saved register, say $s0 (not a temporary one, why?).
And final code should write $zero instead of 0.
2. for1: bge i, n, endfor1: We can use bge (pseudo-instruction) or work with slt/bne. Easier
is to work with pseudo-instructions, so we leave step 2 as it is. But we use $s0 for i and
$a1 (in fact $s3, see later) for n.
3. j for2: Instead of jumping to for2, we better put the code of for2 right here.
4. add i, i, 1: The add becomes here addi, since an immediate operand is used and i à $s0.
5. j for1: Remains unchanged.
6. endfor1: Remains unchanged.
41
More Programming Examples
• Solution (cont’d):
Reading the steps of for2 we see:
1. move j, i-1: We assign $s1 to j and use addi instead of move because we need to
subtract 1 anyway. Thus, the new instruction is: addi $s1, $s0, -1
2. for2: blt j, 0, endfor2: Put here $s1 for j and $zero for 0.
3. ble v[j], v[j+1], endfor2: This is changed to the sequence:
add $s1, $s1, $s1 # j = 2*j
add $s1, $s1, $s1 # j = 4*j
add $t0, $a0, $s1 # $t0 = v + 4*j
lw $t1, 0($t0) # $t1 = v[j]
lw $t2, 4($t0) # $t2 = v[j+1]
ble $t1, $t2, endfor2 # and finally the branch we want
4. jal swap(v, j): We have to prepare the inputs in $a0 and $a1 prior to the call.
Thus, we save our arguments first:
move $s2, $a0 # $s2 = v
move $s3, $a1 # $s3 = n
And prepare the inputs for swap():
move $a0, $s2 # $a0 = v
move $a1, $s1 # $a1 = j
And finally issue the call:
42
jal swap
More Programming Examples
• Solution (cont’d):
5. sub j, j ,1: This is changed to addi $s1, $s1, -1 # or subi
6. j for2: Remains as it is.
7. endfor2: Remains as it is.
What else do we need?
We used $s0 to $s3 in the code. All of them should be pushed on the stack together with
$ra, since we will be calling swap(). Thus:
Beginning of sort should be:
addi $sp, $sp, -20 # allocate stack memory for five registers
sw $ra, 16($sp) # push $ra
sw $s3, 12($sp) # push $s3
sw $s2, 8($sp) # push $s2
sw $s1, 4($sp) # push $s1
sw $s0, 0($sp) # push $s0

43
More Programming Examples
• Solution (cont’d):
End of sort should be:
lw $s0, 0($sp) # pop $s0
lw $s1, 4($sp) # pop $s1
lw $s1, 8($sp) # pop $s2
lw $s2, 12($sp) # pop $s3
lw $ra, 16($sp) # pop $ra
addi $sp, $sp, 20 # release allocated stack memory
And we should not forget to return to the caller:
jr $ra # back to caller of sort()
It only remains to put the pieces together as explained.

• Important:
It is rewarding to first ignore the technicalities of assembly language by providing a
pseudo-solution that is refined in a step-by-step manner until the required solution
is achieved.

44
More Programming Examples
• Working with arrays and pointers:
Next we will have two versions of a procedure clear; one works with array indices and one works
with pointers.
void arrayClear(int array[], int size) {
int i; for(i=0; i < size; i=i+1); array[i] = 0;
}
Pseudo code for body of arrayClear():
1. move i, 0 # init for loop
2. for: bge i, size, endfor # test condition
3. sw 0, 4*i(base(array)) # clear array[i]
4. add i, i, 1 # increment the i
5. j for # loop back to for
6. endfor: # we are done with for
Let’s think now about the register allocation:
We know base(array) à $a0 and size à $a1 and we assign i à $t0.
Reading the steps of for we see:
1. move i, 0 à move $t0, $zero
2. bge i, size, endfor à bge $t0, $a1, endfor
3. sw 0, 4*i(base(array)) à [add $t0, $t0, $t0; add $t0, $t0, $t0; add $t1, $t0, $a0;
sw $zero, 0($t1)] (address calculation must be done inside loop!)
4. add i, i, 1 à addi $t0, $t0, 1
5. j for: Remains unchanged.
6. endfor: Remains unchanged. 45
Now embed code in a procedure called arrayClear (i.e. plus stack allocation, release, and jr)
More Programming Examples
• Working with arrays and pointers (cont’d):
Next procedure is a pointer-based version of arrayClear():.
void pointerClear(int *array, int size) {
int *p; for(p=&array[0]; p < &array[size]; p=p+1); *p = 0;
}
Pseudo code for body of pointerClear():
1. move p, &array[0] # init for-loop
2. for: bge p, &array[size], endfor # test condition
3. sw 0, p # clear p
4. add p, p, 4 # increment by 4, since p points to an integer (=4 bytes)
5. j for # loop back to for
6. endfor: # we are done with for
Let’s think now about the register allocation:
We know base(array) à $a0 and size à $a1 and we assign p à $t0.
Reading the steps of for we see:
1. move p, &array[0] à move $t0, $a0
2. bge p, &array[size], endfor à [add $t1, $a1, $a1; add $t1, $t1, $t1; add $t1, $t1, $a0;
bge $t0, $t1, endfor] (Better: do calculation of &array[size] before the loop; saves 4 instructions/iteration)
3. sw 0, p à sw $zero, 0($t0)
4. add p, p, 4 à addi $t0, $t0, 4
5. j for: Remains unchanged.
6. endfor: Remains unchanged.
Now embed code in a procedure called pointerClear (i.e. plus stack allocation, release, and jr)
46

You might also like