-------------------------
Examples of Assembly Code
-------------------------
Here are the examples that I did in class, and a few that
I did not get to. This should give you a pretty good idea
how one accomplishes things in assembly code.
Prof. Jacob
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
if (a == b) {
c = a;
d = b;
}
in general, if-then statements are similar
to saying "if the following is NOT true, then
skip over these instructions"
lw 1, 0, a # r1 <- a
lw 2, 0, b # r2 <- b
bne 1, 2, next # if (a != b) skip to next
sw 1, 0, c # c = a
sw 2, 0, d # d = b
next: ...
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
if (a != b) {
c = a;
d = b;
}
in general, if-then statements are similar
to saying "if the following is NOT true, then
skip over these instructions"
lw 1, 0, a # r1 <- a
lw 2, 0, b # r2 <- b
bne 1, 2, then # if (a != b) skip to then
jump next # requires unconditional jump
then: sw 1, 0, c # c = a
sw 2, 0, d # d = b
next: ...
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
if (a >= 0) {
b = a;
}
how can we do this? 2's complement =>
neg. numbers have 1 in top bit. if we
nand with a 1 in the top bit we have
the following:
01111111 => negative
11111111 => zero or positive
lw 1, 0, a # r1 <- a
lui 2, 0x8000 # r2 <- 1000 0000
nand 3, 1, 2 # r3 <- a NAND 10000000
addi 2, 0, -1 # r2 <- 1111 1111
bne 3, 2, next # if (a < 0) skip to next
sw 1, 0, b # b = a
next: ...
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
if (a >= 0) {
b = -a;
}
how to change the sign of a number in 2's
complement? flip the bits and add 1. nand-ing
the number with itself flips the bits.
lw 1, 0, a # r1 <- a
lui 2, 0x8000 # r2 <- 1000 0000
nand 3, 1, 2 # r3 <- a NAND 10000000
addi 2, 0, -1 # r2 <- 1111 1111
bne 3, 2, next # if (a < 0) skip to next
nand 1, 1, 1 # r1 <- a NAND a (flips bits)
addi 1, 1, 1 # r1 <- -a (adds one to the complement)
sw 1, 0, b # b = -a
next: ...
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
if (a == b) {
c++;
} else {
d = a + b;
}
it is helpful to think about complex control
statements as a bunch of labeled blocks:
if !x goto B
A: --------------
| |
| |
--------------
goto C
B: --------------
| |
| |
--------------
C: continue ...
in the code from here on in, i put blank lines
to separate the conceptual blocks of code -- this
is just for readability. most assemblers will
allow blank lines, but yours does not have to.
lw 1, 0, a # r1 <- a
lw 2, 0, b # r2 <- b
bne 1, 2, aNEb # if (a != b) skip to aNEb
aEQb: lw 1, 0, c # r1 <- c
addi 1, 1, 1 # c += 1
sw 1, 0, c # store c
jump next # (assumes unconditional jump)
aNEb: add 1, 1, 2 # r1 <- a + b
sw 1, 0, d # d = a + b
next: ...
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
while (a != b) {
a *= 4;
}
here is a loop, and we can either label the
top & bottom of the loop, or we can put the
numbers into the bne instructions directly.
label the loop:
lw 1, 0, a # r1 <- a
lw 2, 0, b # r2 <- b
bne 1, 2, loop # if (a != b) enter loop
jump out # otherwise exit loop
loop: add 1, 1, 1 # a*=2
add 1, 1, 1 # a*=4
bne 1, 2, loop # if (a != b) continue loop
out: ...
use absolute numbers:
lw 1, 0, a # r1 <- a
lw 2, 0, b # r2 <- b
bne 1, 2, 1 # if (a != b) enter loop
jump out # otherwise exit loop
add 1, 1, 1 # a*=2
add 1, 1, 1 # a*=4
bne 1, 2, -3 # if (a != b) continue loop
...
note: why do we put the first two loads
and two conditional branches outside the loop?
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
do {
a *= 4;
} while (a != b);
note that the block of instructions is always
executed at least once; the test happens after
the first execution.
lw 1, 0, a # r1 <- a
lw 2, 0, b # r2 <- b
loop: add 1, 1, 1 # a*=2
add 1, 1, 1 # a*=4
bne 1, 2, loop # if (a != b) continue loop
...
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
if (a < b) {
b = -a;
} else if (a > b) {
a = -b
} else {
c++;
}
how to do less-than? well, we can subtract
one from the other and see if the result is
negative (just like the earlier example).
lw 1, 0, a # r1 <- a
lw 2, 0, b # r2 <- b
bne 1, 2, notequal # if (a == b) skip to equal
equal: lw 1, 0, c # r1 <- c
addi 1, 1, 1 # c += 1
sw 1, 0, c # store c
jump next
notequal: nand 2, 2, 2 # r2 <- b NAND b (flips bits)
addi 2, 2, 1 # r2 <- -b
add 3, 1, 2 # r3 <- a - b
lui 4, 0x8000 # r4 <- 1000 0000
nand 3, 3, 4 # r3 <- (a - b) NAND 10000000
addi 4, 0, -1 # r4 <- 1111 1111
bne 3, 4, aLTb # if (a - b) < 0 skip to aLTb
aGTb: sw 2, 0, a # a = -b
jump next # goto next
aLTb: nand 1, 1, 1 # r1 <- a NAND a (flips bits)
addi 1, 1, 1 # r1 <- -a
sw 1, 0, b # b = -a
next: ...
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
this example show the value of subroutines
and the use of the JALR instruction
if (a == 0) {
a += (b * 4) - 1;
b++;
} else if (a < 0) {
a += (c * 4) - 1;
c++;
} else { /* if a > 0 */
a += (d * 4) - 1;
d++;
}
what we're going to do here is create a
subroutine that takes as input a value
in register 5. the subroutine multiplies
this number by 4, then subtracts 1 from
the product, then adds the value of a (in
register 1), and stores the result to a.
the subroutine also increments the value in
register 5. when we return from the
subroutine, we store this value to memory.
note that JALR jumps through a register and
there is no simple way to get an ADDRESS into
a register (you cannot, for example, use lw
directly on the label). so we have to create
an intermediate label called jAddr that holds
the address of subr.
lw 1, 0, a # r1 <- a
lw 2, 0, jAddr # r2 <- addr of subr
bne 1, 0, aNEz # if (a == 0) skip to aEQz
aEQz: lw 5, 0, d # r5 <- d
jalr 2, 7 # jump to subr, link in r7
sw 5, 0, d # d++;
jump next # skip to next
aNEz: lui 3, 0x8000 # r3 <- 1000 0000
nand 4, 1, 3 # r4 <- a NAND 10000000
addi 3, 0, -1 # r3 <- 1111 1111
bne 4, 3, aLTz # if (a < 0) skip to aLTz
aGTz: lw 5, 0, c # r5 <- c
jalr 2, 7 # jump to subr, link in r7
sw 5, 0, c # c++;
jump next # skip to next
aLTz: lw 5, 0, b # r5 <- b
jalr 2, 7 # jump to subr, link in r7
sw 5, 0, b # b++;
jump next # skip to next
subr: add 6, 5, 5 # r6 = r5 * 2
add 6, 6, 6 # r6 *= 2 (now r6 = r5 * 4)
addi 6, 6, -1 # r6 = r6 - 1
add 1, 1, 6 # a += ( x * 4 ) - 1
sw 1, 0, a # store a
addi 5, 5, 1 # r5++
jalr 7, 0 # return from subroutine
next: ...
jAddr: .fill subr