CS 111
Spring 2018

Old version

This is the CS 111 site as it appeared on May 10, 2018.

Problem Set 5 FAQ

If you don’t see your question here, post it on Piazza or come to office hours! See the links in the navigation bar for both of those options.

Problem 2

  1. How do we start the Full Adder?

    If you look at the lecture notes on arithmetic circuits, you will find the truth table and a partial model for that circuit. You will need to use minterm expansion twice: once to complete the portion of the circuit that deals with the s bit, and once to create the portion of the circuit that deals with the c_out bit.

Problem 3

  1. How do we start the 4-bit Ripple-Carry Adder?

    In lecture, we constructed a 2-bit Ripple-Carry Adder using two Full Adders. For the 4-bit Ripple-Carry Adder, you should extend this approach with more full adders so that you can add 4-bit numbers.

Problem 4

  1. How should I start the 4x1 Multiplier? Will I need to use my Full Adder?

    You will not need your Full Adder for the 4x1 multipler. Instead, notice that you are essentially just multiplying a 4-bit number by either 1 or 0. If you were to write this out by hand, each bit in the output would either be the same as the input bit, or 0. This depends on the 1-bit value y that we are multiplying by. Which logic gate could give us this behavior?

    If you work through the multiplication one bit at a time, you should only need to use one gate for each output bit (with 4 gates total).

Problem 5

  1. How do I combine my 4x1 Multiplier and 4-Bit Adder to create the 4x2 Multiplier?

    If you look at the lecture notes on arithmetic circuits, you will see a high-level description of how to implement the 4x2 Multiplier. To start, you can use two 4x1 Multipliers to produce two partial products. You will then need to add up the results using your 4-Bit Ripple Carry Adder. This may appear awkward since you will need to add a 4-bit number to a 5-bit number, but the lecture notes show you how to arrange the addition so that you only add two 4-bit numbers to get the final output.

Problem 6

  1. How do I set the error bit in the 3x2 Divider?

    If both x_0 and x_1 are 0, then the error output should be 1 since division by 0 is undefined. You should be able to do this using a single logic gate (in addition to the NOT gates that we’ve given you for each input). Remember that it doesn’t matter what you set the other output bits to in this case.

  2. I’m taking the minterm-expansion approach to this problem, and I need one of my OR gates to have more than 8 inputs, but the maximum that Logicly allows is 8. What can I do?

    You can chain two OR gates together. Feed some of the inputs into one OR gate, and then feed the output of that OR gate into a second OR gate along with the rest of the original set of inputs.

Part II General questions

Note

If you are confused about how a particular Hmmm instruction works, you should check the HMMM Reference.

  1. When do we use the nop instruction?

    The nop instruction does nothing in your program. It is the same thing as empty space in your python code. In assembly, it can be used as a place holder so that you do not have to renumber lines as you add lines to your program. As an example, let’s say we want to implement the factorial program we reviewed in lecture. We know that we are finished when r1 is equal to zero, but we are not sure how to implement the rest of the program. Using nop, we can give ourselves some room to try things out without having to renumber lines.

    00   read r1
    01   setn r2 1
    02   jeqz r1 08
    03   nop
    04   nop
    05   nop
    06   nop
    07   nop
    08   write r2
    09   halt
    

    Now you can replace the nop instructions with your own code and not have to spend time renumbering the lines. This will also save you from needing to fix any jump instructions. In this example, the jeqz instruction on line 02 can continue to jump to line 08, even after we replace some of the nop instructions.

  2. Why is my use of the copy instruction not working?

    Remember that the destination of copy comes first, then the source.

    For example, suppose that you want to copy a value from register r6 into register r5. You would do so as follows:

    # correct
    copy r5 r6     # r5 = r6
    

    Make sure that you do not mistakenly do this:

    # incorrect
    copy r6 r5     # r6 = r5
    

    In addition, be sure to check that you are not copying from the wrong register.

  3. I’m getting an assembly error for one of my programs, and I can’t figure out why. Do you have any suggestions?

    Check to make sure that you don’t have any special characters (for example: $, quotes, &) in your comments. Try to remove all such characters, and limit your comments to letters, numbers, spaces, and the # symbol.

  4. How does the debugger work?

    You should be able to complete this assignment without using the debugger. However, if you are interested, here are a few commands that will allow you to use it effectively:

    • First, enter debugging mode by entering y in response to the question that is asked when you run your program.

    • Entering ‘step’ will execute only the next instruction, and then let you type in more commands.

    • Entering p will display the contents of all of the registers.

    • Entering ‘c’ will run all of the remaining instructions.

    When you start the debugger, your program is not being run. To start the first instruction, type step and the first instruction will be run. If it’s a read instruction, you’ll have to enter the input. You can then enter p to see the value of registers, and then type step to run the next instruction. Continuing to use step and p will allow you to see how the registers change as your code executes.

    When you are done, and you want to run the rest of the code, type c and the rest of the program will run. Note that all of the instructions will be printed, so the output will be pretty verbose.

    We also encourage you to watch our video on how to use Hmmm, which includes a look at how to use the debugger.

Problem 7

  1. What do I need to output in Problem 7?

    Your program will read a number n and compute the cube of n. You must display n cubed and then count downward all the way to 0.

    For example, if we run your program with an input of 2, we should get the following output:

    8
    7
    6
    5
    4
    3
    2
    1
    0
    

Problem 8

  1. How do I compute the power for Problem 8?

    You should model your code for Problem 8 on the factorial program that we gave you.

    In that program, we start by designating a register to store/accumulate the result of the factorial, and we initialize the value in that register. Then, in the loop formed by lines 02-05, we repeatedly multiply the current result by the value of the register that stores the current value of n. Once n reaches 0, we know that we can write the value of the result register.

    You should do something similar for this problem. That is, you should designate a register to store/accumulate the result of raising the base b to some power n. What initial value could you give this register? How should you update this result inside your loop?

Problem 9

  1. I’m having trouble getting started on this problem. Do you have any additional suggestions?

    First, make sure that you are modeling your assembly code on the provided Python code. In the Python code, the distance function is called twice, and the only thing that the function does is compute the distance between its two inputs. In the same way, your assembly-language function should also be called twice: once to compute the distance between the reference value x0 and the first candidate value x1, and once to compute the distance between x0 and the second candidate value x2. And the function should only compute the distance between its two inputs; it should not do anything else. All of the remaining tasks should go in the portion of the program that comes before the function.

    Another good model for this problem is the following assembly-language program from lecture:

    00  read r1
    01  read r2
    02  read r3
    03  call r14 09
    04  copy r1 r13
    05  copy r2 r3
    06  call r14 09
    07  write r13
    08  halt
    09  sub r4 r1 r2
    10  jgtz r4 13
    11  copy r13 r1
    12  jumpr r14
    13  copy r13 r2
    14  jumpr r14
    

    This program determines the smallest of three values input by the user. To do so, it uses a function that determines the smaller of two values. The function consists of lines 09-14. It obtains its two inputs in r1 and r2, and it puts its return value (the smaller of its two inputs) in r13.

    The function is called twice: once on line 03 to find the smaller of the first two user inputs (the ones originally read into r1 and r2), and once on line 06 to find the smaller value from the result of the first call and the third user input (the one read into r3).

    It is also worth comparing the two calls to the function. In the first call, we want to find the smaller of the first two user inputs, and because those user inputs are already in the same registers used for the inputs to the function (r1 and r2), we don’t need to do any additional preparation for the function call. However, we do need to prepare for the second call, which is used to find the smaller value from the result of the first call and the third user input (the one stored in r3). Because those values are not already in the registers used for the function’s inputs, we need to use two copy commands (the ones on lines 04 and 05) to put those values in r1 and r2 before we make the second call to the function.