MIPS Assembly Tutorial
Download
Report
Transcript MIPS Assembly Tutorial
Embedded Systems
Lecture 1:The MIPS 32 bit microprocessor
Ian McCrum
Room 5B18, Tel: 90 366364 voice mail on 6th ring
Email: [email protected] Web site: http://www.eej.ulst.ac.uk
24/09/13
www.eej.ulster.ac.uk/~ian/modules/EEE527/files
Types of Instructions
• There are 3 main types of assembly
instructions
– Arithmetic - add, sub, mul, shifts, and, or,
etc.
– Load/store
– Conditional - branches
Arithmetic Instructions
add a, b, c
add a, a, d
add a, a, e
a = b+c
a = d+a = d+b+c
a = e+a = e+d+b+c
Example: Translate the following instructions to
assembly code
a = b+c
d = a-e
Solution:
add a, b, c
sub d, a, e
Arithmetic Instructions
Example: Translate the following instructions to
assembly code. Remember with RISC, only 1
operation per instruction! HINT - you may need
temporary variables
f = (g+h) - (i+j)
Solution:
add t0, g, h
add t1, i, j
sub f, t0, t1
Operands
• In assembly code, you can’t use
variables such as a, b, c, etc
• In RISC instruction sets, operands must
be registers such as r1, r2, r3, etc
– r0 is typically reserved to hold the
immediate value of 0
– There is a limited number of registers
• MIPS has 32
Arithmetic Instructions Using
Registers
Example: Translate the following instructions to
assembly code. Assume that g, h, i, and j are
already stored in r1, r2, r3, and r4. Store f in r5
f = (g+h) - (i+j)
Solution:
add r6, r1, r2
add r7, r3, r4
sub r5, r6, r7
What about more data??
• With only a limited number of registers, not all
data can be stored in registers at the same
time.
– Registers only store data that is currently being
operated on
• Variables are stored in memory and then
loaded into registers when needed using data
transfer instructions
– Load word (lw) and store word (sw)
Load and store word
• Load word format
– lw destination register, memory location
• Store word format
– sw source register, memory location
• Memory location format
– Offset(base address)
• Base address = starting location of data in memory
• Offset = how far away is location to access from base
address
– Values are added together
LW Example
Example: Assume that A is an array of 100 words.
Assume the base address is stored in r3, g is in r1
and h is in r2
g = h + A[8]
Solution:
Offset
lw r4, 8(r3)
add r1, r2, r4
Base Address
This is
simplified,
more details
later…
Data in Memory
• All variables/data are stored in memory
– You will need to do this in your assembler
– Your ISS will need a data structure to hold
main memory
• Array is fine
Addressing Data
• Architecture usually addresses data in
bytes (byte addressable)
– 32-bit architecture = 4 bytes = 1 word
• lw/sw load/store 1 word or 4 bytes
– Thus, data/inst addresses are multiples of 4
• Data is word aligned to be more efficient
Data in Memory
.
.
.
.
.
.
12
8
4
0
100
10
101
1
Address
Data
LW/SW Example
Example: Assume that A is an array of 100 words.
Assume the base address is stored in r3 and h is
stored in r2. You may directly calculate the offset.
Remember, each data piece is 4 bytes when
calculating the offset
A[12] = h+A[8]
Solution:
lw r1, 32(r3)
add r4, r2, r1
sw r4, 48(r3)
LW/SW Example
Example: Assume that A is an array of 100 words.
Assume the base address is stored in r3 and g, h,
and i are in r1, r2, and r4 respectively. Calculate the
offset using assembly instructions but don’t use
multiplication yet (mult instruction is different)
g = h + A[i]
Solution:
add r5, r4, r4
add r5, r5, r5
add r5, r5, r3
lw r6, 0(r5)
add r1, r6, r2
# Temp reg r5=2*i
# Temp reg r5=4*i
# t1 = addr of A[i] (4*i+r3)
# Temp reg r0=a[i]
# g=h+a[i]
Translating MIPS Assm
Language to Machine Language
• Translate human readable assembly
code to machine readable code (binary)
– I will show examples in decimal for
readability
– This is what you assembler will do but it
will output in binary.
MIPS -> Machine Language
Example: Show the real MIPS language version of
the following instruction in both decimal and binary
add r0, r1, r2
Solution:
decimal
0
0
1
2
0
32
00010
00000
100000
5 bits
6 bits
binary
000000
00000
00001
6 bits
5 bits
5 bits
5 bits
Each segment is referred to as a field. Details to
come….
MIPS Fields
• MIPS fields are giving names to make
them easier to discuss
op
rs
rt
6 bits
5 bits
5 bits
rd
5 bits
shamt
funct
5 bits
6 bits
• op: Basic operation of the instruction, typically called the opcode
• rs: The first register source operand
• rt: The second register source operand
• rd: The register destination operand, it gets the result of the operation
• shamt: Shift amount (0 if not shift instruction)
• funct: Function. This field selects the specific variant of the operation in the
op field, and is sometimes called the function code
MIPS Fields
• Problem occurs with an instruction needs a
longer field than that showed on the previous
slide
– I.e. LW must specify 2 registers and a constant.
Limited to 5-bit constant if use previous format.
• Solution: There are different formats for
different types of instructions
– Previous slide is R-type (R-format):
• R=register
MIPS Fields
op
rs
rt
6 bits
5 bits
5 bits
address
16 bits
• I-type (I-format)
– I=immediate
– Now LW can specify an address up to 16bits
• Opcode determines the format
MIPS Instruction Encoding
MIPS Asm -> Machine
Language
Example: Assume r1 is the base of A and r2
corresponds to h, the C statement:
A[300] = h + A[300]
is compiled to:
lw r0, 1200(r1)
add r0, r2, r0
sw r0, 1200(r1)
What is the MIPS machine code for these three
instructions? (Use figure 3.5)
MIPS Asm -> Machine Language
lw r0, 1200(r1)
add r0, r2, r0
sw r0, 1200(r1)
Solution:
decimal
op
rs
rt
35
0
1
0
0
2
43
0
1
rd
Address
/shamt
funct
1200
0
0
32
1200
binary
100011
00000
00001
000000
00000
00010
101011
00000
00001
0000 0100 1011 0000
00000
00000
0000 0100 1011 0000
32
Decision Instructions
• Branch/jump instructions
– Conditional branches
• beq register1, register2, Label
• bne register1, register2, Label
– Unconditional branches
• j Label
Decision Instructions
Example: Assume f->r0, g->r1, h->r2, i->r3, j->r4
L1:
if ( i==j ) goto L1
f = g+h
f = f-i
Solution:
L1:
beq r3, r4, L1
add r0, r1, r2
sub r0, r0, r3
Labels will need to
be translated to
instruction address
in your assembler
Decision Instructions
Example: Assume f->r0, g->r1, h->r2, i->r3, j->r4
L1:
if ( i==j )
f = g+h
else
f = g-h
L2:
Solution:
L1:
L2:
bne r3, r4, L1
add r0, r1, r2
j L2
sub r0, r1, r2
Decision Instructions
Example: A is 100 elements with the base address
in r5. g->r1, h->r2, i->r3, j->r4
Loop: g = g+A[i]
i = i+j
if ( i!=h ) goto Loop
Solution:
Loop:
add r6, r3, r3
add r6, r6, r6
add r6, r6, r5
lw r7, 0(r6)
add r1, r1, r7
add r3, r3, r4
bne r3, r2, Loop
While Loop
• Goto statements are bad, just used
them as an example.
• You will want to use while loops
– Or for loops but I am just showing you
while loops
While Loop
Example: Base address of save is in r6. i->r3, j->r4,
k->r5
while ( save[i] == k )
i = i+j
Solution:
Loop:
Exit:
add r1, r3, r4
add r1, r1, r1
add r1, r1, r6
lw r0, 0(r1)
bne r0, r5, Exit
add r3, r3, r4
j Loop
Other Styles of MIPS Addressing
• Constant or immediate operands
– Programs often use constant values
– I.e. incrementing to the next data element while
scanning an array
• addi instruction - adds an immediate
value to a register
Immediate Operands
Example: What is the machine code for the
following? (Remember the I-format instruction)
addi r4, r4, 4
Solution:
decimal
op
8
rs
4
rt
4
Immediate
4
binary
001000
00100
00100
0000 0000 0000 0100
Addressing in Branches and Jumps
• Last instruction format - J-type (Jformat)
opcode
Target address
• Branches do not use J-type.
– Must specify 2 registers to compare
– Use I-type
Implementing Conditional Statements
We're going to translate some easy conditional statements.
if ( i == j ) i++ ;
j-- ;
Translating conditional statements is interesting. In C, for example, when the
condition is true, you execute the body. This is the fall-through case. That is, you
execute the next statement. When the condition is false, you don't execute the body,
you jump over it. This is the jump case. Therefore, you jump when the condition is
false. In ISA programming, you jump when the condition is true. Thus, we often
need to negate the condition.Here‘s the translation of the above if-statement,
assuming $r1 stores i and $r2 stores j.
bne $r1, $r2, L1 # branch if ! ( i == j )
addi $r1, $r1, 1 # i++
L1: addi $r2, $r2, -1 # j–
The label L1 has the same address as the instruction immediately following the
colon. Thus, the above code is the same as: bne $r1, $r2, L1 # branch if ! ( i == j )
addi $r1, $r1, 1 # i++ L1: addi $r2, $r2, -1 # j-- Even though it appears that
label L1 has an empty instruction, it doesn't. It is still associated with the
second addi instruction.
Taken from http://www.cs.pitt.edu/~xujie/cs447/AccessingArray.htm
Translating if-else
if ( i == j )
i++ ;
else
j-- ;
j += i ;
Let's think about what happens. As before, if the condition is false, we want to
jump. This time, we want to jump to the else. Thus, we write the code like:
ELSE:
L1:
bne $r1, $r2, ELSE
addi $r1, $r1, 1
j L1
addi $r2, $r2, -1
add $r2, $r2, $r1
#
#
#
#
#
branch if ! ( i == j )
i++
jump over else
j–
j += i
Taken from http://www.cs.pitt.edu/~xujie/cs447/AccessingArray.htm
Translating if-else with &&
Translating && is interesting because there's short-circuiting involved.
To see how this works, let's translate:
if ( i == j && i == k ) i++ ; // if-body
else j-- ; // else-body
j = i + k ;
let <cond1> stand for i == j and <cond2> stand for i == k.
if ( <cond1> && <cond2> ) i++ ; // if-body
else j-- ; // else-body
j = i + k ;
Short-circuiting occurs when <cond1> evaluates to false. The control-flow
then jumps over <cond2> (that is, <cond2> is not evaluated), and continues
executing in the else-body.If <cond1> evaluates to true, we want to fall-through and
check <cond2>. If <cond2> evaluates false, we again jump, this time over the ifbody, and to the else-body.If <cond2> is true, we fall-through to the if-body.
Notice that we jump when the condition evaluates to false for both cases, so we'll be
interested in jumping on negations of conditions.
Taken from http://www.cs.pitt.edu/~xujie/cs447/AccessingArray.htm
Here's the translated code, assuming $r3 stores k.
bne $r1, $r2, ELSE # cond1: branch if ! ( i == j )
bne $r1, $r3, ELSE # cond2: branch if ! ( i == k )
addi $r1, $r1, 1 # if-body: i++
j L1 # jump over else
ELSE: addi $r2, $r2, -1 # else-body: j–
L1: add $r2, $r1, $r3 # j = i + k
From the C Code
if ( i == j
&&
i == k
i++ ; // if-body
else
j-- ; // else-body
j = i + k ;
)
Taken from http://www.cs.pitt.edu/~xujie/cs447/AccessingArray.htm
Translating if-else with ||
if ( i == j || i == k )
i++ ; // if-body
else
j-- ; // else-body
j=i+k;
Again, let's use <cond1> to stand for i == j and <cond2> to stand for i == k.
if ( <cond1> || <cond2> )
i++ ; // if-body
else
j-- ; // else-body
j=i+k;
Short-circuiting occurs when <cond1> evaluates to true. That is, we want to jump over
checking the second condition and into the if-body. Notice that we go to the if-body when the
condition evaluates to true. If <cond1> is false, we want to fall-through and check <cond2>.
If <cond2> is false, we now jump to the else-body. If <cond2> is true, we fall-through to the ifbody. Notice that we jump when <cond1> evaluates to true (to the if-body) and
when <cond2> evaluates to false (to the else-body).
Taken from http://www.cs.pitt.edu/~xujie/cs447/AccessingArray.htm
Translating if-else with || /cont
if ( i ==
i++ ; //
else
j-- ; //
j = i + k
j || i == k )
if-body
else-body
;
Here's the translated code:
beq $r1, $r2, IF
bne $r1, $r3, ELSE
IF:
addi $r1, $r1, 1
j L1
ELSE: addi $r2, $r2, -1
L1:
add $r2, $r1, $r3
#
#
#
#
#
#
cond1: branch if (i==j)
cond2: branch if !(i==k)
if-body: i++
jump over else
else-body: j–
j = i + k
switch statements
Switch statements are interesting.
Note switch works on a very limited number of types (int and char, primarily). It
doesn't work on strings (even if students wish they did).
Switch evaluates case-by-case. When one condition fails, the next is checked.
When a condition is true, the code associated with that condition is run. However, if
you don't put break the code for the next condition will also run. Unfortunately, most
people expect a break to occur when the condition is done. They don't expect a fallthrough case.
Here's an example of a switch.
switch( i ) {
case 1: i++ ; // falls through
case 2: i += 2 ; break;
case 3: i += 3 ;
}
Here's the translated code
addi $r4, $r0, 1
bne $r1, $r4, C2_COND
j C1_BODY
C2_COND: addi $r4, $r0, 2
bne $r1, $r4, C3_COND
C2_BODY
C3_COND: addi $r4, $r0, 3
bne $r1, $r4, EXIT
j C3_BODY
C1_BODY: addi $r1, $r1, 1
C2_BODY: addi $r1, $r1, 2
j EXIT
C3_BODY: addi $r1, $r1, 3
EXIT:
#
#
#
#
switch( i ) {
case 1: i++ ; // falls through
case 2: i += 2 ; break;
case 3: i += 3 ;
}
set temp to 1
case 1 false: branch to case 2 cond
case 1 true: branch to case 1
set temp to 2
# case 2 false: branch to case 2 cond j
# case 2 true: branch to case 2 body
#
#
#
#
#
#
#
set temp to 3
case 3 false: branch to exit
case 3 true: branch to case 3 body
case 1 body: i++
case 2 body: i += 2
break
case 3 body: i += 3
When case 1 is true, you want to run i++, then i += 2. You don't want to test
for case 2, because that's now how the semantics of switch works.
bge, bgt, blt, ble
bge, bgt, blt, ble are all pseudo-instructions. That is, there is no corresponding machine code
to these instructions.
Let's see an example of how the assembler might translate bge. The key is to use slt which
means "set on less than". Here is the syntax of slt.
slt $r1, $r2, $r3 # R[1] = R[2] < R[3] ? 1 : 0
The semantics are shown in the comments: if R[2] < R[3] false, we now jump to the elsebodythen R[1] = 1, otherwise it's assigned to 0.
Here is the syntax and semantics of bge:
bge $r1, $r2, LABEL # jump to LABEL if R[1] >= R[2]
If R[1] >= R[2] we know that this is equivalent to !( R[1] < R[2]). Thus, if we check R[1] <
R[2] using slt, we expect it to be false.
Here's the translation of bge.
slt $r3, $r1, $r2 # check if R[1] < R[2]
beq $r3, $r0, LABEL # branch if previous condition is false
As an exercise, you should translate the other three pseudo-instructions
Implementing Loops
Unlike conditional statements, which have assembly instructions that support them more-orless directly (i.e., beq), loops do not have similar support.To translate loops, it's easier to
convert the loops to if-statements with goto statements, prior to translation. Doing so also
gives you insight into how a loop really behaves.
We'll translate a while-loop, then a for-loop. You can do the do-while loop as an exercise
(and you should!).
Here's a generic while-loop:
while ( <cond> ) {
<while-body>
}
This is how it's translated to an if-statement with goto's.
L1: if ( <cond> ) {
<while-body>
goto L1 ;
}
Taken from http://www.cs.pitt.edu/~xujie/cs447/AccessingArray.htm
Here's an example with real code.
while ( i < j ) {
k++ ; i = i * 2 ;
}
Then translate it to if-statements with goto's.
L1: if ( i < j ) {
k++ ; i = i * 2 ; goto L1 ;
}
This should be easy to convert to MIPS.
Assume $r1 stores i, $r2 stores j, and $r3 stores k.
L1:
bge $r1, $r2, EXIT
addi $r3, $r3, 1
add $r1, $r1, $r1
j L1
# branch if ! ( i < k )
# k++
# i = i * 2
# jump back to top of loop
EXIT:
Taken from http://www.cs.pitt.edu/~xujie/cs447/AccessingArray.htm
Translating for-loops
To translate a for-loop, we'll only go part way, and translate it to if-statements and
goto's. You can do the rest on your own. Here's a generic for-loop:
for ( <init> ; <cond> ; <update> ) {
<for-body>
}
This is how it's translated to an if-statement with goto's.
<init>
L1: if ( <cond> ) {
<for-body>
UPDATE: <update> // should affect condition!
goto L1 ;
}
EXIT:
There's only one special case. Suppose the <for-body> has a continue statement.
Then, you need to jump to the <update> code, which is at the UPDATE label.
break should still work fine, because you can jump to the EXIT label.
Taken from http://www.cs.pitt.edu/~xujie/cs447/AccessingArray.htm
Accessing Array Data in MIPS
Since arrays can store LOTS of data, and since we have only a small (~32)
number of registers, it is infeasible to use the registers for long-term storage of the
array data. Hence, arrays are stored in the Data Segment of a MIPS
program. Fundamentally, there are three operations which one can perform on an
array:
–Getting the data from an array cell, e.g, x = list[i];
–Storing data into an array cell, e.g. list[i] = x;
–Determining the length of an array, i.e. list.length.
To access the data in the array requires that we know the address of the data and
then use the load word (lw) or store word (sw) instructions. Words (which is how
integers are stored) in MIPS take up 32 bits or 4 bytes. Therefore, if we have a
declaration in the .data segment such as:
list: .word 3, 0, 1, 2, 6, -2, 4, 7, 3, 7
the address that is loaded by the instruction la $t3, list is the address of the
first '3' in the list. The address of the '0' is 4 greater than that number, and the
address of the '6' is 16 greater than that number.
Taken from http://www.cs.pitt.edu/~xujie/cs447/AccessingArray.htm
Accessing Array Data in MIPS
The following snippet of code will place the value of list[6] into the $t4:
la $t3, list
li $t2, 6
add $t2, $t2, $t2
add $t2, $t2, $t2
add $t1, $t2, $t3
lw $t4, 0($t1)
#
#
#
#
#
#
put address of list into $t3
put the index into $t2
double the index
double the index again (now 4x)
combine both parts of the address
get the value from the array cell
If we wish to assign to the contents of $t4 to list[6] instead, the last line
would simply be:
sw $t4, 0($t1) # store value in the array cell
Taken from http://www.cs.pitt.edu/~xujie/cs447/AccessingArray.htm
Some notes on arrays
Most assembly languages, like MIPS, do not have built-in capabilities for sophisticated data
structures. Even the most commonly available structure, the array, is not available in MIPS.
As we shall see shortly, we can set aside a block of memory in MIPS in assembly language
and treat it similar to an array.
What is an array anyway? One way to think about an array is a set of values that can
simultaneously be considered a single collective entity and many individual elements. In
high level languages, the array is a set of references using an identifier like any other
variable. To access each individual element, one uses an integer to specify a particular
element. In most high-level languages, arrays are stored as a contiguous block of n
memory cells starting with a base address. In java, we might declare and use an array as
follows.
int a[] = new int[10];
int sum = 0;
...
for (int i = 0; i < 10; i++)
sum += a[i];
In this example, the elements in the array are added and the result is stored in the variable
sum. Note a is the identifier that refers to the array and [i] refers to the ith element of the
array. Here a is the base location of the array and i indicates the offset into memory from
the base address.
From http://zeta.albion.edu/~dreimann/Spring2012/courses/cs354/projects/primes.php
In MIPS, there is no formal array construct. The first issue to resolve is the
association of a block of memory with a particular identifier. This can be done using
a label and a .space directive in the .data section.
For example
.data a: .space 40
reserves 40 bytes (10 words) of memory of data associated with the label a. The
memory location where this label is stored (by the assembler) becomes the base
address of the array. To access the ith element from the array, we need to
determine the memory offset from the beginning address and add the number of
bytes per element. For simplicity, let's assume the array stores elements that each
require on word of storage and $t0 is the register that represents i. Then
la $t1, a
muli $t2, $t0, 4 # or use two adds...
add $t2, $t2, $t1
lw $t3, 0($t2)
will load the ith element from the array into register $t3. Here $t1 contains the base
address of the array and $t2 is the address of the ith element of the array.
From
http://zeta.albion.edu/~dreimann/Spring2012/courses/cs354/projects/primes.php
While this will work, it is not the only way to access the elements of the
array. Consider storing the integers 0-9 in the first ten elements of an array
then reading the elements and adding them together. The
program sum10.s shown below illustrates one way to do this.
.text
main:
la $t4, n
lw $t4, 0($t4)
and $t0, $0, $0
la $t1, a
loop1: sll $t2, $t0, 2
add $t2, $t2, $t1
sw $t0, 0($t2)
addi $t0, $t0, 1
slt $t5, $t0, $t4
bne $t5, $0, loop1
#
#
#
#
#
#
#
#
#
#
#
Fill the array
address of n
t4 = n
i = 0
address of a
byte offset of ith element
address of a[i]
put i into a[i]
increment i
is $t0 < $t4 ?
branch if so
#/CONT (note sll is the same as multiplying by 2)
From http://zeta.albion.edu/~dreimann/Spring2012/courses/cs354/projects/primes.php
# Sum the array values
and $s0, $0, $0
and $t0, $0, $0
add $t2, $t1, $0
loop2: lw $t3, 0($t2)
add $s0, $s0, $t3
addi $t0, $t0, 1
addi $t2, $t2, 4
slt $t5, $t0, $t4
bne $t5, $0, loop2
#
#
#
#
#
#
#
#
#
sum = 0
i = 0
address of a[i]
load a[i]
increment sum
increment i
increment address of a[i]
is $t0 < $t4 ?
branch if so
# Output Sum
li $v0, 1
# Load 1=print_int into $v0
add $a0, $s0, $zero # Load first number into $a0
syscall
# Output prompt via syscall
li $v0, 10
syscall
# exit
.data
n:
a:
.word 10 # n = 10
.align 4
.space 40 # Allocate 10 words (40 bytes)
From http://zeta.albion.edu/~dreimann/Spring2012/courses/cs354/projects/primes.php