CS 354, version A
Fall 2008
Name:___________________
ID:___________________
Exam 2
No electronic devices may be used while taking this exam. Examples of devices not allowed are calculators, pagers, cell phones, wrist calculators/computers, laptop computers, pocket computers. Each student is allowed one 8.5 by 11 inch sheet of paper with handwritten notes. The notes may be on both sides of the paper.

Show all work, and do any/all calculations on the exam. Extra scratch paper may not be used.

The end of the exam has a table of MAL instructions.

Exam Score
Q1 = _____ /   8
Q2 = _____ /   8
Q3 = _____ / 12
Q4 = _____ / 12
Q5 = _____ / 10
Total = _____ / 50



Question 1 (8 points )
Consider a 3-dimensional integer array A[4][3][2] where the first index goes from 0 to 3, the second from 0 to 2, and third from 0 to 1. Assume that array begins at address 200 and elements are stored in the following order: is A[0][0][0], A[0][0][1], A[0][1][0], A[0][1][1], A[0][2][0], ....

(a) What is the total size of the array in bytes?
4 * 4 * 3 * 2 = 96 bytes


(b) What is the address of element A[2][1][1]?
260 = 200 + 4*(2*3*2 + 2*1 + 1)


Question 2 (8 points)
Most instruction sets, such as MIPS, use byte addressing and store words in four bytes. Consider the following data declarations.

	.data
prompt: .asciiz "Hi!"
   bar: .word 0x61626364   # ASCII 'a' 'b' 'c' 'd'
   foo: .word 0x65666768   # ASCII 'e' 'f' 'g' 'h'

(a) If the assembler begins prompt at address 512 and stores bar and foo immediately after, what addresses would bar and foo get assigned?
bar is at 516
foo is at 520

(b) How do Little Endian and Big Endian instruction sets differ on storing foo?
One endianess will store 0x65 at 520, 0x66 at 521, 0x67 at 522, & 0x68 at 523,
while the other endianess will store 0x68 at 520, 0x67 at 521, 0x66 at 522, & 0x65 at 523.




Question 3 (12 points)
Consider implementing a first-come-first-serve (FIFO) queue using an array:

a_queue:  .word 0:10
and initialized as follows:
# $7 qstart == first location
# $8 head == 1st full location unless head==tail
# $9 tail == 1st empty location
# $10 qend == after last location

      la $7, a_queue  
      la $8, a_queue
      la $9, a_queue
      add $10, $7, 40

Consider this code for enqueuing:

      sw $13, ($9)      # assume incoming data in $13
      add $9, $9, 4
      # add more code here

      bne $9, $10, enq_nowrap
      move $9, $7
enq_nowrap:

and this code for dequeueing:
      lw $13, ($8)     # assume outgoing data for $13
      add $8, $8, 4
      # add more code here

      bne $8, $10, deq_nowrap
      move $8, $7
deq_nowrap:

 
(a) (8 points) One reason the above queue is not correct is that, as items are enqueued and dequeued, the head and tail pointers will keep increasing and point to memory locations after the end of the a_queue array. A solution to this is to make the queue wrap around back to the top once it reaches the end. Add MAL code in the space provided above to keep queue items in a_queue.

(b) (4 points) Use words to describe other reason(s) why the above queue implementation is not correct. Hint: think of error conditions.
When the queue is full, enqueue should report an overflow error rather than overwriting an element.
When the queue is empty, dequeue should report an underflow error rather than return an incorrect element.

Question 4 (12 points)
Implement the follow C code in MAL. We have provided the first three instructions.

#
# for (i = 1, sum = 0; i <= 10; i++) {
#       if (i%2==0) {           /* i is even */
#               sum += i;
#       }
#       else {                 /* i is odd */
#               sum -= i;
#       }
#  }
#
# Use $8 for i, $9 for sum, $10-$25 for any temporary variiables

        li $8, 1

        li $9, 0
	
        li $10, 10   # loop bound

my_for: bgt $8, $10, end_for
        rem $11, $8, 2 # i%2
        bnez $11, my_else
        add $9, $9, $8
        b my_for2
my_else: sub $9, $9, $8
my_for2: add $8, $8, 1
        b my_for
end_for:














The exam continues on the next page ....


Question 5 (10 points)
Please complete this factorial function following 354 calling conventions (incoming parameters at lowest addresses in caller's activation record (AR), return value into $v0, and callee activation record must hold return address, callee saved values of $8-$25 used, any temporary memory variables, and outgoing parameters).

# /* n! = n*(n-1)! for n > 0     */
# /* 0! = 1                      */
# /* Example: 4! = 4*3*2*1 = 24  */
# 
# int factorial(int n)
# {
# int ret;
#   if (n==0) ret = 1;
#   else {
#      ret = n*factorial(n-1);
#   }
# return ret;
# }

factorial:
        sub $sp,$sp,16   # allocate factorial's activation record (AR)

        sw  $ra,16($sp)  # save return address

        sw  $8,12($sp)   # callee save of registers $8-$25 used

        sw  $9,8($sp)

        lw  $8,20($sp)   # get 1st parameter n

        bnez $8, else

        li $9,1          # 0! = 1

        b end_if

else:   sub $9,$8,1      # (n-1)

        sw  $9,4($sp)    # setup outgoing parameter (n-1)

        jal factorial    # call factorial(n-1)

        mul $9,$8,$v0    # multiply w/ incoming return value

end_if: move $v0,$9      # prepare outgoing return value

        lw  $ra,16($sp)  # restore return address

        lw  $8,12($sp)   # callee restore of registers $8-$25 used

        lw  $9,8($sp)

        add $sp,$sp,16   # deallocate factorial's AR

        jr $ra










ADD TABLE OF MAL INTRUCTIONS

http://www.cs.wisc.edu/~markhill/cs354/Fall2008/handouts/MAL.pdf