Last Updated: 2019-06-27 Thu 15:26

CSCI 2021 Assignment 2: Bit Ops and Debugging

CODE/TEST DISTRIBUTION: a2-code.zip

CHANGELOG:

Thu 27 Jun 2019 03:24:39 PM CDT
Several folks reported bugs in the Gradescope Autograder which resulted in Problem 2's Puzzlebox giving 0 credit with a make error. This error has been corrected. No re-submission is needed as the autograder has already been re-run on the students that have submitted code.

1 Introduction

Bit-level operations are common in C and systems programming. This assignment features a problem in which shifting and bitwise AND/OR-ing are required to complete the requirements.

Debugging is also a critical skill enabled by the debugger. The second problem in the assignment makes use of the GNU Debugger, gdb, to work through a puzzle program requiring specific inputs to pass its "phases".

1.1 Makefile

As in the first assignment, a Makefile is provided as part of this project. The essential targets pertinent to each problem are described in those sections.

1.2 Automated Tests

As in previous assignments, automated tests are provided and associated with problems. Each problem describes how to run tests associated with it.

2 Download Code and Setup

Download the code pack linked at the top of the page. Unzip this which will create a project folder. Create new files in this folder. Ultimately you will re-zip this folder to submit it.

File State Notes
Makefile Provided Build file to compile all programs
thermo.h Provided Problem 1 header file
thermo_main.c Provided Problem 1 main() function for thermometer simulation
thermo_update.c Create Problem 1 functions to write
     
test_thermo_update.c Testing Problem 1 functions tests for thermo_upate.c
test_thermo_main.sh Testing Problem 1 shell tests for thermo_main
test_thermo_main_data.sh Testing Problem 1 shell test data for thermo_main
puzzlebox.c Provided Problem 2 Debugging problem
input.txt Edit Problem 2 Input for puzzlebox, fill this in

3 Problem 1: Thermometer Simulation

3.1 Overview

You are tasked with writing code which will be run by a microcontroller in a digital thermometer. The hardware has the following relevant features.

  • A temperature sensor whose value can be accessed via a memory mapped port. In C this is presented as a global variable. Another port indicates whether the temperature should be displayed in Celsius or Fahrenheit.
  • A digital with a port control port; setting certain global variable will change the temperature display with
  • User code that you will need to write to update the display based on the temperature sensor.
  • A simulator program with Makefile to test your code

Each feature is discussed in subsequent sections.

Temperature Sensor

A temperature sensor is attached to the thermometer and can be accessed via a C global variable. This is declared in the thermo.h header file as follows.

// Set by the sensor to indicate temperature. Value is a positive int
// in units of 0.1 / 64 deg C above -50.0 deg C.
extern unsigned short THERMO_SENSOR_PORT;

You do not need to define this variable as it is already there. You do not need to set this variable as it is automatically changed by the hardware. Instead, you will need to access its value to determine various aspects of the temperature. Note the type which is unsigned short: only positive values are present in it and the variable will have 16 bits. The temperature sensor has a limited range and uses units of 1/64th of a tenth degree above -50.0 deg C. This leads to the following equivalences.

Sensor Value Celsius Notes
0 -50.0 Minimum sensor val, MINIMUM measurable temp
31 -50.0 Remainder rounded down
32 -49.9 Remainder rounded up
64 -49.9 Exactly one tenth degree above -50.0 deg C
128 -49.8 Exactly two tenths degree above -50.0 deg C
640 -49.0 Exactly ten tenths = one degree above -50.0 deg C
6400 -40.0 Ten degrees above -50.0 C
31360 -1.0 49.0 degrees above -50.0
31680 -0.5 49.5 degrees above -50.0
32000 0.0 50.0 degrees above -50.0
32064 0.1 50.1 degrees above -50.0
38400 10.0 60.0 degrees above -50.0
64000 50.0 100.0 degrees above -50.0, MAXIMUM measurable temp
64001 err Sensor error

Notice 64 units of value in the sensor are 0.1 degree Celsius starting from -50.0 deg C. The maximum value allowable is 50.0 deg Celsius which is a sensor value of 64000.

Notice also that the temperature should be rounded appropriately:

  • The temperature sensor is 1/64th of a tenth degree
  • A sensor value of 10 rounds down to -50.0 deg C
  • A sensor value of 31 rounds down to -50.0 deg C
  • A sensor value of 32 rounds up to -49.9 deg C
  • A sensor value of 40 rounds up to -49.9 deg C

Temperature Display Mode

Another global variable exposes whether the user has pressed a button which toggles between displaying temperature in the two most common scales: Celsius or Fahrenheit.

// Lowest order bit indicates whether display should be in Celsius (0)
// or Fahrenheit (1).
extern unsigned char THERMO_STATUS_PORT;

Display Port

The thermometer has a digital display which shows the temperature. This display is controlled by a special memory area exposed as another global variable in C.

// Controls thermometer display. Readable and writable.
extern int THERMO_DISPLAY_PORT;

While listed as in int, each bit of the is actually tied to part of the thermometer display screen. When bits are set to 1, part of the display is lit up while 0 means it is not lit.

The following diagram shows bit patterns for various symbols and how they correspond to parts of a single digit of the display. Digits are displayed by darkening certain bars in the display which correspond to certain bits in the THERMO_DISPLAY_PORT being set.

digital-digits.png

Figure 1: Correspondence of bit in THERMO_DISPLAY_PORT to bars in a single digit. The 0th bit controls the upper horizontal bar, the 1th bit controls the horizontal bar clockwise from it, and so on around the 6 outer bars in a clockwise fashion. The 6th bit controls the middle horizontal bar. When a bit is 1 (set), the bar will be darkened while when the bit is 0 (clear) the bar will not be displayed (shown as empty). The combinations of bits shown are the only ones that arise when showing digits for temperatures.

Notice the following.

  • Bits that are set (equal to 1) will turn on (darken) one bar of the display digit
  • Bits that are clear (equal to 0) will turn off one bar of the digit
  • 7 bits are required to control the display of one digit
  • The bits are arranged with the low order bit (bit 0) at the top and progress clockwise around the digit.
    • Bit 0 top
    • Bit 1 upper right
    • Bit 2 lower right
    • Bit 3 bottom
    • Bit 4 lower left
    • Bit 5 upper left
    • Bit 6 middle
  • The programmer can set bits to any pattern which will be displayed but only patterns shown correspond to symbols of interest.

Temperature is displayed with several adjacent digits along with a Celsius/Fahrenheit indicator. The diagram below shows several full temperatures along with the bits representing the digits. The bits correspond to how the global variable THERMO_DISPLAY_PORT should be set in order to make the temperature appear as it does.

digital-thermo.png

Figure 2: Full examples of how the 30 bits of the thermometer display state control which parts of the temperature are shown. Each digit follows the same pattern of bit to bar correspondence as the right-most bits. The lowest order (rightmost) bit controls the top bar of each digit and proceed around the outside clockwise with the final bit for each digit controlling the middle bar. The highest order bits (28 and 29) control whether the Celsius or Fahrenheit indicators are shown. Note that both could be shown at the same time or neither shown but this should not be done for actual temperatures.

Notice the following.

  • You may presume that the THERMO_DISPLAY_PORT is a 32-bit integer.
  • 30 bits are used to control the full temperature display.
  • Bits 0-6 control the tenths place
  • Bits 7-13 control the ones place
  • Bits 14-20 control the tens place
  • Bits 21-27 control the hundreds place
  • Bit 28 controls whether degrees Celsius is shown
  • Bit 29 controls whether degrees Fahrenheit is shown
  • Bits 30 and 31 are not used and should always be 0.

3.2 thermo_update.c: Updating the Display with User Code

Periodically the microcontroller will run code to adjust the thermometer display to show the current temperature. This function is

int thermo_update();

and it will be your job to write this function

Rather than write everything that needs to be done within thermo_update(), several helper functions will be used to divide this task into several more manageable and testable chunks.

These should all be written in thermo_update.c and are as follows.

Converting Temperature Sensor Value to a Struct

int set_temp_from_ports(temp_t *temp);
// Uses the two global variables (ports) THERMO_SENSOR_PORT and
// THERMO_STATUS_PORT to set the temp structure. If THERMO_SENSOR_PORT
// is above its maximum trusted value, associated with +50.0 deg C,
// does not alter temp and returns 1.  Otherwise, sets fields of temp
// based on converting sensor value to degrees and checking whether
// Celsius or Fahrenheit display is in effect. Returns 0 on successful
// set. This function DOES NOT modify any global variables but may
// access global variables.
//
// CONSTRAINT: Uses only integer operations. No floating point
// operations are used as the target machine does not have a FPU.
// 
// CONSTRAINT: Limit the complexity of code as much as possible. Do
// not use deeply nested conditional structures. Seek to make the code
// as short, and simple as possible. Code longer than 50 lines may be
// penalized for complexity.

This function works with the struct temp_t defined in thermo.h which has the following layout.

// Breaks temperature down into constituent parts
typedef struct{
  short tenths_degrees;         // actual temp in tenths of degrees
  char is_fahrenheit;           // 0 for celsius, 1 for fahrenheit
} temp_t;

The function set_temp_from_ports() will read the global variables mentioned above and fill in values for the struct fields of the parameter temp. To convert the temperature sensor value to a displayable temperature, one must perform some division and modulo operations.

  1. Divide the sensor value by 64 to get the number of tenths degrees Celsius above -50.0 deg C. Note the constraint: make use only of integer operations. Do not use float or double variables. This is emulates the somewhat common situation where simple microprocessors cannot perform floating point operations as they lack a Floating Point Unit (FPU).
  2. Round up if the remainder is high enough.
  3. Account for the offset from -50.0 deg C by subtracting.
  4. Check THERMO_STATUS_PORT to see if conversion to Fahrenheit is needed.
  5. If conversion is needed, use the formula

    farenheit = (celsius * 9) / 5 + 32;
    
    

    but note that we are working in tenths degrees so adjustments may be needed. No rounding is done when converting from Celsius to Fahrenheit. Use of this conversion and lack of rounding means that, despite the high-resolution temperature, degrees in Fahrenheit only appear in increments of about 0.2 deg F giving it less resolution than the Celsius. This is the price of a simpler implementation. Continue to abide by the constraint: do not use floating point operations.

Setting Display from a temp_t

int set_display_from_temp(temp_t temp, int *display);
// Alters the bits of integer pointed to by display to reflect the
// temperature in struct arg temp.  If temp has a temperature value
// that is below minimum or above maximum temperature allowable or if
// an improper indication of celsius/fahrenheit is given, does nothing
// and returns 1. Otherwise, calculates each digit of the temperature
// and changes bits at display to show the temperature according to
// the pattern for each digit.  This function DOES NOT modify any
// global variables but may access global variables.
// 
// CONSTRAINT: Limit the complexity of code as much as possible. Do
// not use deeply nested conditional structures. Seek to make the code
// as short, and simple as possible. Code longer than 70 lines may be
// penalized for complexity.

Importantly, this function sets the bits in the integer pointed to by display which may or may not be the global display variable.

To properly set the display bits, set_dsiplay_from_temp() will need to do bit shifting along with bitwise operations to construct the correct bit pattern for the thermometer display.

A good trick to use is to create a series of bit patterns that correspond to the various digits. For example, according to the diagrams above, the bit patter for 9 is 0b1101111. If a 9 should appear on the display somewhere, this bit pattern should be shifted and combined with the existing bits in display so that a 9 will show. Creating similar constant mask patterns for each digit, the minus sign, and the position of the Celsius/Fahrenheit indicator is a good way to make this problem manageable.

A detailed explanation of one approach to the problem follows.

  • Create an array of bit masks for each of the digits 0-9. The 0th element of the array contains a bit mask like 0b0111111 which represents the bits that should be set for a 0 digit, the 1th element of this array has a mask like 0b0000110 which are the bits to be set for a 1. There should be ten entries in this array in indices 0-9.
  • Use modulo to determine the integer value for the tens, ones, and tenths digits for the temperature. Call these variables something like temp_hundreds, temp_tens and so on. Each variable should be in the range 0-9.
  • Start with an integer variable of 0 (all 0 bits).
  • Use temp_tens to index into your array of masks to determine the bits that should be set for it. Combine the state variable with temp_ones mask.
  • Combining bits here is a logical operation of setting all bits that are one in the mask to 1 in the display variable.
  • Use temp_hundred to index into your array of masks for the right mask for that digit. The bits corresponding to the hundreds place of the temperature is shifted to the left by 21 bits so shift the mask to the left and combine it with the state variable.
  • Repeat this process for the tens digit (shifted by 14) ones, and tenths digits.
  • There are several special cases to consider: leading 0's should be blanks so nothing should be drawn. Also, the negative sign should be positioned immediately to the left of the first non-blank digit. A couple examples of how this looks are below

    RIGHT        WRONG      
    22.4 deg C   022.4 deg C
    -1.5 deg C   -01.5 deg C  - 1.5 deg C
    
  • Importantly make code as simple as possible as the target CPU has a limited amount of RAM and longer codes will occupy more RAM. Avoid nesting conditionals very deeply: if/else if/else structures are fine but nesting a conditional within this will be penalized.

Overall Update Function

int thermo_update();
// Called to update the thermometer display.  Makes use of
// set_temp_from_ports() and set_display_from_temp() to access
// temperature sensor then set the display. Checks these functions and
// if they indicate an error, makes not changes to the display.
// Otherwise modifies THERMO_DISPLAY_PORT to set the display.
// 
// CONSTRAINT: Does not allocate any heap memory as malloc() is NOT
// available on the target microcontroller.  Uses stack and global
// memory only.

This function makes use of the previous two functions and the global variables that correspond to the hardware to alter the display. It should be relatively short by making use of the previous functions.

3.3 Thermometer Simulator

While we do not have actual hardware with the features mentioned, a simulator for the system is in the provided file thermo_main.c. You do not need to modify or understand code in either file to complete the assignment though it will certainly expand you C skills to spend some time examining them.

The main() function in thermo_main.c accepts two command line arguments which are the value for the temperature sensor and whether the thermometer is in Celsius or Fahrenheit mode. It will call your functions for this problem and show results for it. You are encouraged to use this function to test your code incrementally

  • Examine whether set_temp_from_ports() is correct based on the first part of output in thermo_main.c
  • Once set_temp_from_ports() is complete, examine whether the output of set_display_from_temp() is correct based on the latter part of the output.
  • Once both these functions are correct, examine whether thermo_update() is correct based on the final part of the output of the main() function.

Note that there are a variety of functions in the thermo_main.c which are used to simulate how the thermometer will display. This is also where the global variables like THERMO_DISPLAY_PORT are defined. However, you do not need to modify or even understand the code; it is only used to show how the display would look when the THERMO_DISPLAY_PORT bits are set.

3.4 Sample Runs of thermo_main

Below are samples generated by compiling and running the main() function in the thermo_main.c file. The code is compiled by using the provided Makefile to create the thermo_main program. It compiles the the functions you write in the file thermo_update.c and combines them with functions in thermo_main.c.

> make thermo_main
make: 'thermo_main' is up to date.

==========CELSIUS FOR 0==========
> ./thermo_main 0 C
THERMO_SENSOR_PORT set to: 0
set_temp_from_sensors(&temp );
temp is {
  .tenths_degrees = -500
  .is_fahrenheit  = 0
}
Simulated temp is: -50.0 deg C

Checking results for display bits
set_display_from_temp(temp, &display);

display is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00011000000110110101111110111111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Running thermo_update()

THERMO_DISPLAY_PORT is:
index:  3         2         1    0    0
index: 10987654321098765432109876543210
bits:  00011000000110110101111110111111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Thermometer Display:
         ~~   ~~   ~~  o       
        |    |  | |  |  C      
    ~~   ~~                    
           | |  | |  |         
         ~~   ~~ o ~~          


==========FAHRENHEIT FOR 0==========
> ./thermo_main 0 F
THERMO_SENSOR_PORT set to: 0
set_temp_from_sensors(&temp );
temp is {
  .tenths_degrees = -580
  .is_fahrenheit  = 1
}
Simulated temp is: -58.0 deg F

Checking results for display bits
set_display_from_temp(temp, &display);

display is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00101000000110110111111110111111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Running thermo_update()

THERMO_DISPLAY_PORT is:
index:  3         2         1    0    0
index: 10987654321098765432109876543210
bits:  00101000000110110111111110111111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Thermometer Display:
         ~~   ~~   ~~          
        |    |  | |  |         
    ~~   ~~   ~~       o       
           | |  | |  |  F      
         ~~   ~~ o ~~          


==========CELSIUS FOR 35==========
> ./thermo_main 35 C
THERMO_SENSOR_PORT set to: 35
set_temp_from_sensors(&temp );
temp is {
  .tenths_degrees = -499
  .is_fahrenheit  = 0
}
Simulated temp is: -49.9 deg C

Checking results for display bits
set_display_from_temp(temp, &display);

display is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00011000000110011011011111101111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Running thermo_update()

THERMO_DISPLAY_PORT is:
index:  3         2         1    0    0
index: 10987654321098765432109876543210
bits:  00011000000110011011011111101111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Thermometer Display:
              ~~   ~~  o       
        |  | |  | |  |  C      
    ~~   ~~   ~~   ~~          
           |    |    |         
              ~~ o ~~          


==========FAHRENHEIT FOR 35==========
> ./thermo_main 35 F
THERMO_SENSOR_PORT set to: 35
set_temp_from_sensors(&temp );
temp is {
  .tenths_degrees = -578
  .is_fahrenheit  = 1
}
Simulated temp is: -57.8 deg F

Checking results for display bits
set_display_from_temp(temp, &display);

display is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00101000000110110100001111111111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Running thermo_update()

THERMO_DISPLAY_PORT is:
index:  3         2         1    0    0
index: 10987654321098765432109876543210
bits:  00101000000110110100001111111111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Thermometer Display:
         ~~   ~~   ~~          
        |       | |  |         
    ~~   ~~        ~~  o       
           |    | |  |  F      
         ~~      o ~~          


==========CELSIUS FOR 64==========
> ./thermo_main 64 C
THERMO_SENSOR_PORT set to: 64
set_temp_from_sensors(&temp );
temp is {
  .tenths_degrees = -499
  .is_fahrenheit  = 0
}
Simulated temp is: -49.9 deg C

Checking results for display bits
set_display_from_temp(temp, &display);

display is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00011000000110011011011111101111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Running thermo_update()

THERMO_DISPLAY_PORT is:
index:  3         2         1    0    0
index: 10987654321098765432109876543210
bits:  00011000000110011011011111101111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Thermometer Display:
              ~~   ~~  o       
        |  | |  | |  |  C      
    ~~   ~~   ~~   ~~          
           |    |    |         
              ~~ o ~~          


==========FAHRENHEIT FOR 64==========
> ./thermo_main 64 F
THERMO_SENSOR_PORT set to: 64
set_temp_from_sensors(&temp );
temp is {
  .tenths_degrees = -578
  .is_fahrenheit  = 1
}
Simulated temp is: -57.8 deg F

Checking results for display bits
set_display_from_temp(temp, &display);

display is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00101000000110110100001111111111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Running thermo_update()

THERMO_DISPLAY_PORT is:
index:  3         2         1    0    0
index: 10987654321098765432109876543210
bits:  00101000000110110100001111111111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Thermometer Display:
         ~~   ~~   ~~          
        |       | |  |         
    ~~   ~~        ~~  o       
           |    | |  |  F      
         ~~      o ~~          


==========CELSIUS FOR 320==========
> ./thermo_main 320 C
THERMO_SENSOR_PORT set to: 320
set_temp_from_sensors(&temp );
temp is {
  .tenths_degrees = -495
  .is_fahrenheit  = 0
}
Simulated temp is: -49.5 deg C

Checking results for display bits
set_display_from_temp(temp, &display);

display is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00011000000110011011011111101101
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Running thermo_update()

THERMO_DISPLAY_PORT is:
index:  3         2         1    0    0
index: 10987654321098765432109876543210
bits:  00011000000110011011011111101101
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Thermometer Display:
              ~~   ~~  o       
        |  | |  | |     C      
    ~~   ~~   ~~   ~~          
           |    |    |         
              ~~ o ~~          


==========FAHRENHEIT FOR 320==========
> ./thermo_main 320 F
THERMO_SENSOR_PORT set to: 320
set_temp_from_sensors(&temp );
temp is {
  .tenths_degrees = -571
  .is_fahrenheit  = 1
}
Simulated temp is: -57.1 deg F

Checking results for display bits
set_display_from_temp(temp, &display);

display is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00101000000110110100001110000110
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Running thermo_update()

THERMO_DISPLAY_PORT is:
index:  3         2         1    0    0
index: 10987654321098765432109876543210
bits:  00101000000110110100001110000110
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Thermometer Display:
         ~~   ~~               
        |       |    |         
    ~~   ~~            o       
           |    |    |  F      
         ~~      o             


==========CELSIUS FOR 640==========
> ./thermo_main 640 C
THERMO_SENSOR_PORT set to: 640
set_temp_from_sensors(&temp );
temp is {
  .tenths_degrees = -490
  .is_fahrenheit  = 0
}
Simulated temp is: -49.0 deg C

Checking results for display bits
set_display_from_temp(temp, &display);

display is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00011000000110011011011110111111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Running thermo_update()

THERMO_DISPLAY_PORT is:
index:  3         2         1    0    0
index: 10987654321098765432109876543210
bits:  00011000000110011011011110111111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Thermometer Display:
              ~~   ~~  o       
        |  | |  | |  |  C      
    ~~   ~~   ~~               
           |    | |  |         
              ~~ o ~~          


==========FAHRENHEIT FOR 640==========
> ./thermo_main 640 F
THERMO_SENSOR_PORT set to: 640
set_temp_from_sensors(&temp );
temp is {
  .tenths_degrees = -562
  .is_fahrenheit  = 1
}
Simulated temp is: -56.2 deg F

Checking results for display bits
set_display_from_temp(temp, &display);

display is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00101000000110110111111011011011
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Running thermo_update()

THERMO_DISPLAY_PORT is:
index:  3         2         1    0    0
index: 10987654321098765432109876543210
bits:  00101000000110110111111011011011
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Thermometer Display:
         ~~   ~~   ~~          
        |    |       |         
    ~~   ~~   ~~   ~~  o       
           | |  | |     F      
         ~~   ~~ o ~~          


==========CELSIUS FOR 3200==========
> ./thermo_main 3200 C
THERMO_SENSOR_PORT set to: 3200
set_temp_from_sensors(&temp );
temp is {
  .tenths_degrees = -450
  .is_fahrenheit  = 0
}
Simulated temp is: -45.0 deg C

Checking results for display bits
set_display_from_temp(temp, &display);

display is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00011000000110011011011010111111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Running thermo_update()

THERMO_DISPLAY_PORT is:
index:  3         2         1    0    0
index: 10987654321098765432109876543210
bits:  00011000000110011011011010111111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Thermometer Display:
              ~~   ~~  o       
        |  | |    |  |  C      
    ~~   ~~   ~~               
           |    | |  |         
              ~~ o ~~          


==========FAHRENHEIT FOR 3200==========
> ./thermo_main 3200 F
THERMO_SENSOR_PORT set to: 3200
set_temp_from_sensors(&temp );
temp is {
  .tenths_degrees = -490
  .is_fahrenheit  = 1
}
Simulated temp is: -49.0 deg F

Checking results for display bits
set_display_from_temp(temp, &display);

display is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00101000000110011011011110111111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Running thermo_update()

THERMO_DISPLAY_PORT is:
index:  3         2         1    0    0
index: 10987654321098765432109876543210
bits:  00101000000110011011011110111111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Thermometer Display:
              ~~   ~~          
        |  | |  | |  |         
    ~~   ~~   ~~       o       
           |    | |  |  F      
              ~~ o ~~          


==========CELSIUS FOR 31360==========
> ./thermo_main 31360 C
THERMO_SENSOR_PORT set to: 31360
set_temp_from_sensors(&temp );
temp is {
  .tenths_degrees = -10
  .is_fahrenheit  = 0
}
Simulated temp is: -1.0 deg C

Checking results for display bits
set_display_from_temp(temp, &display);

display is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00010000000100000000001100111111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Running thermo_update()

THERMO_DISPLAY_PORT is:
index:  3         2         1    0    0
index: 10987654321098765432109876543210
bits:  00010000000100000000001100111111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Thermometer Display:
                   ~~  o       
                | |  |  C      
         ~~                    
                | |  |         
                 o ~~          


==========FAHRENHEIT FOR 31360==========
> ./thermo_main 31360 F
THERMO_SENSOR_PORT set to: 31360
set_temp_from_sensors(&temp );
temp is {
  .tenths_degrees = 302
  .is_fahrenheit  = 1
}
Simulated temp is: 30.2 deg F

Checking results for display bits
set_display_from_temp(temp, &display);

display is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00100000000100111101111111011011
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Running thermo_update()

THERMO_DISPLAY_PORT is:
index:  3         2         1    0    0
index: 10987654321098765432109876543210
bits:  00100000000100111101111111011011
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Thermometer Display:
         ~~   ~~   ~~          
           | |  |    |         
         ~~        ~~  o       
           | |  | |     F      
         ~~   ~~ o ~~          


==========CELSIUS FOR 32000==========
> ./thermo_main 32000 C
THERMO_SENSOR_PORT set to: 32000
set_temp_from_sensors(&temp );
temp is {
  .tenths_degrees = 0
  .is_fahrenheit  = 0
}
Simulated temp is: 0.0 deg C

Checking results for display bits
set_display_from_temp(temp, &display);

display is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00010000000000000001111110111111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Running thermo_update()

THERMO_DISPLAY_PORT is:
index:  3         2         1    0    0
index: 10987654321098765432109876543210
bits:  00010000000000000001111110111111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Thermometer Display:
              ~~   ~~  o       
             |  | |  |  C      
                               
             |  | |  |         
              ~~ o ~~          


==========FAHRENHEIT FOR 32000==========
> ./thermo_main 32000 F
THERMO_SENSOR_PORT set to: 32000
set_temp_from_sensors(&temp );
temp is {
  .tenths_degrees = 320
  .is_fahrenheit  = 1
}
Simulated temp is: 32.0 deg F

Checking results for display bits
set_display_from_temp(temp, &display);

display is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00100000000100111110110110111111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Running thermo_update()

THERMO_DISPLAY_PORT is:
index:  3         2         1    0    0
index: 10987654321098765432109876543210
bits:  00100000000100111110110110111111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Thermometer Display:
         ~~   ~~   ~~          
           |    | |  |         
         ~~   ~~       o       
           | |    |  |  F      
         ~~   ~~ o ~~          


==========CELSIUS FOR 64000==========
> ./thermo_main 64000 C
THERMO_SENSOR_PORT set to: 64000
set_temp_from_sensors(&temp );
temp is {
  .tenths_degrees = 500
  .is_fahrenheit  = 0
}
Simulated temp is: 50.0 deg C

Checking results for display bits
set_display_from_temp(temp, &display);

display is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00010000000110110101111110111111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Running thermo_update()

THERMO_DISPLAY_PORT is:
index:  3         2         1    0    0
index: 10987654321098765432109876543210
bits:  00010000000110110101111110111111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Thermometer Display:
         ~~   ~~   ~~  o       
        |    |  | |  |  C      
         ~~                    
           | |  | |  |         
         ~~   ~~ o ~~          


==========FAHRENHEIT FOR 64000==========
> ./thermo_main 64000 F
THERMO_SENSOR_PORT set to: 64000
set_temp_from_sensors(&temp );
temp is {
  .tenths_degrees = 1220
  .is_fahrenheit  = 1
}
Simulated temp is: 122.0 deg F

Checking results for display bits
set_display_from_temp(temp, &display);

display is:
        3         2         1         0
index: 10987654321098765432109876543210
bits:  00100000110101101110110110111111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Running thermo_update()

THERMO_DISPLAY_PORT is:
index:  3         2         1    0    0
index: 10987654321098765432109876543210
bits:  00100000110101101110110110111111
guide:  |    |    |    |    |    |    |
index:  30        20        10        0

Thermometer Display:
         ~~   ~~   ~~          
      |    |    | |  |         
         ~~   ~~       o       
      | |    |    |  |  F      
         ~~   ~~ o ~~          


3.5 Problem 2 Grading Criteria   grading

Both sets automated tests can be run with make test-p1

Weight Criteria
  AUTOMATED TESTS
15 test_thermo_update.c tests of functions in thermo_update.c
  Compile and run using make test-p1a
  30 tests, 0.5 points per test
  Deductions for memory problems identified by Valgrind
   
5 test_thermo_main.sh tests of thermo_main.c
  Compile and run using make test-p1b
  5 Tests, 1 point per test passed
  Deductions for memory problems identified by Valgrind
  MANUAL INSPECTION of thermo_update.c
10 set_temp_from_ports()
  Clear effort to do error checking of out of bounds values.
  Clear flow to how each field of temp is calculated.
  Correctly setting fields of temp via pointer dereference or arrow operator.
  Adherence to constraints: no floats, no math ops, no deeply nested conditionals
  Short, simple code that is no more than 50 lines long.
   
15 set_display_from_temp()
  Clear effort to do error checking for out of bounds values in temp parameter
  Clear code that calculates digits to be displayed
  Use of bit masks corresponding to digits to be displayed
  Use of bitwise operators to shift bits appropriately
  Use of bitwise operators to combine shifted digit bits
  Clear dereference/set of the integer pointed to by the display parameter
  Adherence to constraints: no floats, no math ops, no deeply nested conditionals
  Short, simple code that is no more than 70 lines long.
   
5 thermo_update()
  Use of the global variables like THERMO_DISPLAY_PORT
  Does not re-define these variables
  Use of previous two functions
  Error checking on sensor values
  No use of malloc()
  Short, simple code.

4 Problem 2: Debugging the Puzzlebox

4.1 Overview

The file puzzlebox.c contains source code that reads inputs from a file named on the command line. If the inputs are correct, points are awarded. If inputs are incorrect, error messages are printed.

The puzzlebox is arranged into a series of phases each of which has some points associated with it.

  • Not all phases must be completed to get full credit but the phases must done in order.
  • Each phase reads inputs from the file provided on the command line and performs calculations on them to see if they are "correct" according to various criteria
  • The very first input is your internet ID like kauf0095 (first part of your UMN email address). This input is used to add randomness to the puzzle so that your answers will be different from most other students. You must you use your own internet ID.

The purpose of this problem is get familiar with using a debugger. This is a powerful tool that pauses programs, allows internal values to be printed and code to be stepped through line by line. It is nearly essential to use as the code in puzzlebox is intentionally convoluted in places. Being able to pause execution and print values at various points make it much easier to solve the puzzles.

4.2 input.txt Input File

Name your input file input.txt and put your internet ID in it along with some numbers like 1 2 3. Then compile and run the puzzlebox program on it.

> make puzzlebox                             # compile puzzlebox
gcc -Wall -g -c puzzlebox.c
gcc -Wall -g -o puzzlebox puzzlebox.o

> cat input.txt                              # show contents of input.txt
kauf0095 1 2 3

> ./puzzlebox input.txt                      # run puzzlebox with input.txt
UserID 'kauf0095' accepted: hash value = 1397510491
PHASE 1: A puzzle you say? Challenge accepted!

Ah ah ah, you didn't say the magic word...
Failure: Double debugger burger, order up!

  * Score: 0 / 50 pts *

This is automated with the Makefile target make test-p2:

> make test-p2                       # compile/run puzzlebox with input.txt
gcc -Wall -g -c puzzlebox.c
gcc -Wall -g -o puzzlebox puzzlebox.o
./puzzlebox input.txt
UserID 'kauf0095' accepted: hash value = 1397510491
PHASE 1: A puzzle you say? Challenge accepted!

Ah ah ah, you didn't say the magic word...
Failure: Double debugger burger, order up!

  * Score: 0 / 50 pts *

These initial forays are not positive (0 / 50 points) but the real meat of the problem is in examining the source code and determining inputs for input.txt.

4.3 gdb The GNU Debugger

You will definitely need to use a debugger to solve the puzzlebox and gdb is the quintessential debugger associated with our compiler gcc. It is installed by default on all lab machines and is an easy install on must Linux machines.

For a quick overview of GDB, here are some resources

4.4 Typical Cycle

A typical cycle of working on puzzlebox will be the following.

  • Start the debugger with puzzlebox

    gdb -tui ./puzzlebox
    
    
  • Set the arguments array to input.txt

    set args input.txt
    
    
  • Set a breakpoint at a phase like phase3

    break phase3
    
    
  • Run the program

    run
    
    
  • Do some stepping / nexting

    step
    next
    
    
  • Print some variables

    print a
    print/x b
    
    
  • Make some changes to input.txt in a different window
  • Re-run the program to see if things have changed favorably

    kill
    run
    
    

4.5 Kinds of Puzzles

The puzzles presented in the different phases make use of a variety of C program techniques which we have or will discuss including.

  • Bit-wise operations and their use in place of arithmetic
  • String and character array manipulations
  • Interpreting bits as one of several kinds of things (integer, float, character) through pointers and unions
  • More extensive C control structures like goto and labels

4.6 Tests for puzzlebox.c   grading

puzzlebox.c itself reports how many points one can earn at the end of its execution.

Currently there are 60 points available but 50 points is considered full credit.

If any additional points are earned, they will be counted towards the overall assignment total for the course to make up for lost credit on other assignments. Your total score on All assignments cannot exceed 100% so any points beyond will simply be dropped.

Run the following command to 'test' puzzlebox:

make test-p2

5 Assignment Submission

5.1 Submit to Gradescope

  1. In a terminal, change to your assignment code directory and type make zip which will create a zip file of your code. A session should look like this:

       > cd Desktop/2021/a2-code      # location of assignment code
    
       > ls 
       Makefile    thermo_update.c  puzzlebox.c  test_thermo_main.sh 
       ...
    
       > make zip                     # create a zip file using Makefile target
       rm -f a2-code.zip
       cd .. && zip "a2-code/a2-code.zip" -r "a2-code"
         adding: a2-code/ (stored 0%)
         adding: a2-code/test_thermo_main.sh (deflated 69%)
         adding: a2-code/Makefile (deflated 59%)
         adding: a2-code/test-data/ (stored 0%)
         ...
       Zip created in a2-code.zip
    
       > ls a2-code.zip
       a2-code.zip
    
  2. Log into Gradescope and locate and click 'Assignment 2' which will open up submission
  3. Click on the 'Drag and Drop' text which will open a file selection dialog; locate and choose your a2-code.zip file
  4. This will show the contents of the Zip file and should include your C source files along with testing files and directories.
  5. Click 'Upload' which will show progress uploading files. It may take a few seconds before this dialog closes to indicate that the upload is successful. Note: there is a limit of 256 files per upload; normal submissions are not likely to have problems with this but you may want to make sure that nothing has gone wrong such as infinite loops creating many files or incredibly large files.
  6. Once files have successfully uploaded, the Autograder will begin running the command line tests and recording results. These are the same tests that are run via make test.
  7. When the tests have completed, results will be displayed summarizing scores along with output for each batch of tests.
  8. Refer to the Submission instructions for A1 for details and pictures but note that the A2 Makefile is already complete so no new version needs to be downloaded.

5.2 Late Policies

You may wish to review the policy on late project submission which will cost you late tokens to submit late or credit if you run out of tokens. No projects will be accepted more than 48 hours after the deadline.

https://www-users.cs.umn.edu/~kauffman/2021/syllabus.html#org02d3f97


Author: Chris Kauffman (kauffman@umn.edu)
Date: 2019-06-27 Thu 15:26