Your cart is currently empty!
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…
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”.
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.
As in previous assignments, automated tests are provided and associated with problems. Each problem describes how to run tests associated with it.
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 |
---|---|---|
|
Provided |
Build file to compile all programs |
|
Provided |
Problem 1 header file |
|
Provided |
Problem 1 main() function for thermometer simulation |
|
Create |
Problem 1 functions to write |
|
|
|
|
Testing |
Problem 1 functions tests for |
|
Testing |
Problem 1 shell tests for |
|
Testing |
Problem 1 shell test data for |
|
Provided |
Problem 2 Debugging problem |
|
Edit |
Problem 2 Input for |
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.
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
: 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.
short
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
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;
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.
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.
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.
thermo_update.c
: Updating the Display with User CodePeriodically 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.
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
defined in
temp_tthermo.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.
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).
Round up if the remainder is high enough.
Account for the offset from -50.0 deg C by subtracting.
Check THERMO_STATUS_PORT
to see if conversion to Fahrenheit is needed.
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.
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
structures are fine but nesting a conditional within this will be penalized.
if/else
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.
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.
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 ~~
Both sets automated tests can be run with make
test-p1
Weight |
Criteria |
---|---|
|
AUTOMATED TESTS |
15 |
|
|
Compile and run using |
|
30 tests, 0.5 points per test |
|
Deductions for memory problems identified by Valgrind |
|
|
5 |
|
|
Compile and run using |
|
5 Tests, 1 point per test passed |
|
Deductions for memory problems identified by Valgrind |
|
MANUAL INSPECTION of |
10 |
|
|
Clear effort to do error checking of out of bounds values. |
|
Clear flow to how each field of |
|
Correctly setting fields of |
|
Adherence to constraints: no floats, no math ops, no deeply nested conditionals |
|
Short, simple code that is no more than 50 lines long. |
|
|
15 |
|
|
Clear effort to do error checking for out of bounds values in |
|
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 |
|
Adherence to constraints: no floats, no math ops, no deeply nested conditionals |
|
Short, simple code that is no more than 70 lines long. |
|
|
5 |
|
|
Use of the global variables like |
|
Does not re-define these variables |
|
Use of previous two functions |
|
Error checking on sensor values |
|
No use of |
|
Short, simple code. |
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.
input.txt
Input FileName your input file input.txt
and put your internet ID in it along with some numbers like 1
. Then compile and run the
2 3puzzlebox
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
.
gdb
The GNU DebuggerYou 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
CSCI 2021 Quick Guide to gdb: The GNU Debugger: Page describing how to start the debugger, a sample session using puzzlebox
, an overview of the most common commands.
CppCon 2015: Greg Law ” Give me 15 minutes & I’ll change your view of GDB”: Video giving basic overview of hope to run gdb
on simple programs with an emphasis on differences between “normal” mode and TUI mode
GNU GDB Debugger Command Cheat Sheet: Extensive list of commands
Debugging with GDB: Official manual for gdb
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
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
puzzlebox.c
GRADINGpuzzlebox.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
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
Log into Gradescope and locate and click ‘Assignment 2’ which will open up submission
Click on the ‘Drag and Drop’ text which will open a file selection dialog; locate and choose your a2-code.zip
file
This will show the contents of the Zip file and should include your C source files along with testing files and directories.
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.
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
When the tests have completed, results will be displayed summarizing scores along with output for each batch of tests.
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.
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