------------------------- 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