Exam 1 Review

The exam will consist of short-answer questions and writing your own code. A significant portion will consist of writing code (more than half the points), so you should be prepared to do this.

The review below, in addition to Lab 1, Lab 2, and Lab 3, are all fair game for the exam. For the labs, this includes both code you've written and questions you've answered. Between this review and the first three labs, this information is intended to be comprehensive; there will be no material on the exam which isn't touched by either the labs or this review.

If you're pushed for time, my personal recommendation is to spend the majority of your time studying what you wrote for your labs, both code and questions. You'll find the review below to be largely (though not entirely) redundant with that information.

Data Representation

  1. Convert the decimal number 39 into 8-bit binary.
  2. Convert the decimal number 40 into a two-digit hexadecimal number.
  3. Convert the hexadecimal number 0x45 into decimal.
  4. Convert the hexadecimal number 0x1F into decimal.
  5. Convert the unsigned binary number 1001 into decimal.
  6. Convert the signed binary number 1001 into decimal.
  7. Convert -5 into binary, using two's complement.
  8. Negate the number 7, and show its binary representation.
  9. Negate the binary number 0110, showing the result in binary.
  10. What's the quickest way to tell if a number in two's complement is negative?
  11. In 5 bits, what is the most negative value representable in signed form, using two's complement? Express your answer in both binary and decimal.
  12. In 5 bits, what is the most positive value representable in signed form, using two's complement? Express your answer in both binary and decimal.
  13. In 5 bits, what is the most negative value representable in unsigned form, using two's complement? Express your answer in both binary and decimal.
  14. In 5 bits, what is the most positive value representable in unsigned form, using two's complement? Express your answer in both binary and decimal.
  15. Suppose you are given the following 4-bit binary number, shown in two's complement:
    0110
    You're not told whether or not the number is signed or unsigned. Is this information important in knowing what the value of the number is, in decimal? That is, do you need to know if it's signed or unsigned to say what the decimal value is? Why or why not?
  16. Suppose you are given the following 4-bit binary number, shown in two's complement:
    1001
    You're not told whether or not the number is signed or unsigned. Is this information important in knowing what the value of the number is, in decimal? That is, do you need to know if it's signed or unsigned to say what the decimal value is? Why or why not?

Binary Arithmetic

  1. Suppose that a binary addition was performed in a processor, and the processor set the carry bit at the end of the computation. What does this mean? That is, what is the significance of the carry bit being set after an addition is performed?
  2. Suppose that a binary addition was performed in a processor, and the processor set the overflow bit at the end of the computation. What does this mean? That is, what is the significance of the overflow bit being set after an addition is performed?
  3. Generally, the carry bit is ignored when performing signed arithmetic. Why?
  4. Generally, the overflow bit is ignored when performing unsigned arithmetic. Why?
  5. In MIPS, what is the difference between the add and addu instructions?
  6. Consider the following C code:

    signed int x = -1;          // line 1
    signed int y = 5;           // line 2
    signed int result = x + y;  // line 3
    

    In generating the assembly code for line 3, MIPS compilers will generally use the addu instruction, which is for unsigned addition. This is not a bug in the compiler, and result will, in fact, end up holding the correct result. There are two questions here:

    1. Why does addu return the correct result, despite being intended for unsigned addition?
    2. What feature of C allows compilers to use addu here instead of add?
  7. Perform the following addition, noting whether or not the carry bit and the overflow bit get set:

     01111111
    +11111111
    
  8. Perform the following addition, noting whether or not the carry bit and the overflow bit get set:

     00100101
    +10110111
    
  9. Perform the following subtraction, noting whether or not the carry bit and the overflow bit get set:

     01111111
    -11111111
    
  10. Perform the following subtraction, noting whether or not the carry bit and the overflow bit get set:

     00100101
    -10110111
    
  11. Consider the following C code:

    signed char x;          
    for (x = 1; x > 0; x++) {
      printf("Hello\n");
    }
    printf("Goodbye\n");
    

    Assume the compiler performs a naive translation to assembly, and doesn't exploit any special features of C. Some questions follow:

    1. Does "Goodbye\n" ever get printed out? Why or why not?
    2. Why is it important that a C compiler not exploit any special features of C here? In particular, what feature of C might cause this program to do something differently?
  12. Consider the following C code:

    unsigned char x;
    for (x = 0; x <= MAX_UNSIGNED_CHAR; x++) {
      printf("Hello\n");
    }
    printf("Goodbye\n");
    

    Assume that MAX_UNSIGNED_CHAR holds the maximum value representable in an unsigned char, which is defined previously in the code. Some questions about this code follow:

    1. Does "Goodbye\n" ever get printed out?
    2. Unlike with the previous review question, it was not stipulated whether or not we assume the compiler exploits any special features of C. Is this information relevant to this question? Why or why not?

Bitwise Operations

In the following questions, & refers to bitwise AND, | refers to bitwise OR, ^ refers to bitwise XOR, << refers to shift left, and >> refers to shift right.

  1. What is the result of the following operation:
     00011101
    &11011010
    
  2. What is the result of the following operation:
     00011101
    |11011010
    
  3. What is the result of the following operation:
     00011101
    ^11011010
    
  4. Consider the following C code, which is intended to extract the lowest 7 bits of the given input i:

    int unsignedBits0through6(int i) {
      return __________;
    }
    

    Fill in __________ with a single bitwise expression which will make the code do what it is intended to do.

  5. Consider the following C code, which is intended to extract the next 7 bits of the given input i:

    int unsignedBits7through13(int i) {
      return __________;
    }
    

    Fill in __________ with a single bitwise expression which will make the code do what it is intended to do.

  6. Consider the following C code, which is intended to extract the lowest 7 bits of the given input i, treating the result as a signed value:

    int signedBits0through6(int i) {
      __________;  
      return __________;
    }
    

    Fill in the blanks with valid C code which will make the function do what it is intended to do.

  7. Consider the following C code, which is intended to extract the next 7 bits of the given input i, treating the result as a signed value:

    int signedBits7through13(int i) {
      __________;  
      return __________;
    }
    

    Fill in the blanks with valid C code which will make the function do what it is intended to do.

  8. (Not exactly a review question, but...) Any question from Lab 2 is fair game, particularly the parts you had to implement for DecodeCode.c. I'm not including those questions directly here because it is redundant, and because I won't post the answers to them (which would give the world the solutions to the lab!).
  9. Consider the binary value 00001011. How can this be multiplied by 4, without using an instruction intented for division or multiplication?
  10. Consider the binary value 00001011. How can this be divided by 4, without using an instruction intented for division or multiplication?
  11. There is only one form of shift left, but there are two forms of shift right. Some questions follow about this fact:

    1. Why are there two forms of shift right?
    2. What do the two different forms of shift right do?
    3. Why is there only one form of shift left?
  12. Division by a power of two can be achieved via the clever use of shift right. However, this won't always get the same result as actual division would. Some questions follow about these facts:

    1. Under what conditions will shift right and division not return the same result?
    2. The difference in results is viewable in a difference between rounding. What is the difference in rounding here?

Assembly

  1. What does the li pseudoinstruction do?
  2. Why isn't li an actual MIPS instruction?
  3. Translate the following MIPS code using li to a form that uses only actual instructions (no pseudoinstructions):
    li $t0, 0xFFFFFFFF
    
  4. Translate the following C code into MIPS assembly. The variables used below should be placed in the register with the same name. For example, variable s0 should be placed in register $s0. If you need additional registers than what the code below uses, use registers $t0 - $t9. You do not need to exit the program properly.

    int s0 = 82;
    int s1 = s0 << 2;
    int s2 = s1 * 20;
    int s3 = s2 + 7;
    int s4 = s3 - 24;
    int s5 = s4 / 3;
    
  5. Translate the following C code into MIPS assembly. Where <<read integer from the user>> is used, you should use special functionality provided by SPIM to read in an integer from the console. Where <<print integer s1>> is used, you should use special functionality provided by SPIM to print the integer stored in s1 to the console (you might not be able to do this directly, in which case you'll need to copy the value of s1 into another register first). The variables used below should be placed in the register with the same name. For example, variable s0 should be placed in register $s0. If you need additional registers than what the code below uses, use registers $t0 - $t9. You do not need to exit the program properly.

    int s0 = <<read integer from the user>>;
    int s1 = 2;  
    if (s0 < 7) {
      s1 = 3;
    }
    <<print integer s1>>
    
  6. Translate the following C code into MIPS assembly. Where <<read integer from the user>> is used, you should use special functionality provided by SPIM to read in an integer from the console. Where <<print integer s1>> is used, you should use special functionality provided by SPIM to print the integer stored in s1 to the console (you might not be able to do this directly, in which case you'll need to copy the value of s1 into another register first). The variables used below should be placed in the register with the same name. For example, variable s0 should be placed in register $s0. If you need additional registers than what the code below uses, use registers $t0 - $t9. You do not need to exit the program properly.

    int s0 = <<read integer from the user>>;
    int s1 = 2;  
    if (s0 < 7) {
      s1 = 3;
    } else {
      s1 = s0 + s0;
    }         
    <<print integer s1>>
    
  7. Translate the following C code into MIPS assembly. The variables used below should be placed in the register with the same name. For example, variable s0 should be placed in register $s0. If you need additional registers than what the code below uses, use registers $t0 - $t9. You do not need to exit the program properly.

    int s0;
    int s1 = 1;  
    for (s0 = 0; s0 < 10; s0++) {
      s1 = s1 * s0;
    }                
    
  8. Translate the following C code into MIPS assembly. The variables used below should be placed in the register with the same name. For example, variable s0 should be placed in register $s0. If you need additional registers than what the code below uses, use registers $t0 - $t9. You do not need to exit the program properly.

    int s0;
    int s1 = 1;  
    for (s0 = 0; s0 < 10; s0++) {
      if (s0 & 1) {
        s1 = s1 * s0;
      }                  
    }              
    
  9. Translate the following C code into MIPS assembly. The variables used below should be placed in the register with the same name, except for gv, which should be treated as a global variable in memory. For example, variable s0 should be placed in register $s0. For this problem, you must define gv in the appropriate section, and put the code in the appropriate section, using the proper assembler directives to do so. If you need additional registers than what the code below uses, use registers $t0 - $t9. You do not need to exit the program properly.

    int gv = 2;         
    int s0 = gv + 7;
    gv = s0;
    
  10. Translate the following C code into MIPS assembly. Variables gv0 and gv1 should be treated as global variables in memory. For this problem, you must define gv0 and gv1 in the appropriate sections, and put the code in the appropriate section, using the proper assembler directives to do so. You may use any registers you want. You do not need to exit the program properly.

    int gv0 = 2;
    int gv1 = 8;
    gv0 = gv0 + gv1;