The additional materials here that are intended to complement this chapter are probably best tackled after reading chapter 3 and becoming more familiar with C and in particular C data types.
Apart from recommending Charles Petzold’s “CODE the hidden language of computer hardware and software” I might point you in the direction of Ben Eater’s web site describing his construction of an 8 bit computer from a mix of discrete components and general purpose chips all mounted on a collection of 14 solderless breadboards. https://eater.net/8bit
Even if you do not end up diving into Ben Eater’s videos on the construction project, do take the time to watch the one comparing a short C program to the assembly language generated by the gcc compiler. Ben is a great communicator and his presentation is clear and easy to follow. You can use this YouTube link https://www.youtube.com/watch?v=yOyaJXpAYZQ
Chapter 2 included a necessary but brief introduction to binary numbers. The intention was to provide the reader, new to binary, with enough information to make sense of the greater part of the material in the rest of the book. In order not to labour the subject before diving into the C programming language proper, some issues were not explored in any great depth. One that sticks out is the representation of negative numbers.
I am sure that the idea that for signed integers setting the highest bit to 1 marking the number as negative seemed perfectly reasonable. However, the fact that the negative numeric value is stored in a “twos complement” format probably did not – why not just use the same binary representation as positive numbers? Perhaps we can shed some light on that here.
Start with the sequence of 8 bit unsigned integers that can be stored in a byte type. They start
00000000 – decimal 0
00000001 – decimal 1
00000010 – decimal 2
00000011 – decimal 3
And so on to
11111110 – decimal 254
11111111 – decimal 255
Now let’s start with positive signed integers that can be stored in a char type.
00000000 – decimal +0
00000001 - decimal +1
00000010 – decimal +2
01111111 – decimal + 127
And the negative numbers without twos complement
10000000 – decimal -0
10000001 – decimal -1
10000010 – decimal -2
11111111 – decimal -127
The first issue that arises from just setting the sign bit is that we can have two zeros – positive and negative and that would represent a potential complication that our computers could do without.
Now let’s try adding -5 to +7 (which is the same as subtracting +5 from +7) with just the sign bit set.
10001100 which would be -12 in our notional negative binary format and not +2 unless our computer was equipped with some special circuitry to manage subtraction (negative addition) and that would be another complication as the arithmetic processing unit would need to know when to invoke the special processing and the chip designers would need to devise that special circuitry. It would be nice to just use a standard “adder” that did not need to know if the values being added were signed types or not.
So let’s look at twos complement. If we stick with -5, we start with the binary representation of 5.
Invert the 1s and 0s
and add 1
11111011 and that is the twos complement representation of -5. Note that the sign bit is automatically set by that process.
Now we can try our addition again: adding -5 to +7
1 0000 0010
with that high bit dropping off (as there are only 8 bits in our byte), leaving the correct result of decimal +2.
Twos complement representation ensures there is only one binary zero representation. We can also use standard binary addition for negative numbers (and therefore subtraction) and the adder does not even need to know when it is handling negative values. Plus, as a minor bonus, the twos complement format allows the largest negative value (for a signed char) to be -128 rather than just -127.
If we want to convert a twos complement binary value to a decimal representation (say for the Serial Monitor window) then the process starting with (say) -75 is:
The high bit is 1 so remember the number is negative, now invert the bits
and add 1
01001011 which is 75 so the decimal representation is -75
Looked at another way, 10110101 (decimal -75) could represent
-1 x 27 + 1 x 25 + 1 x 24 + 1 x 22 + 1 x 20
Which is -128 + 32 + 16 + 4 + 1 = -75 which makes this format seem even more reasonable perhaps.
Not that we have to be very concerned with how negative integer values are represented within an Arduino in most circumstances but at least now you know what one looks like.