When we read text on our computer screen, we often see numbers like 0 and 1, just as I've used many times in this text. It may come as a surprise to some that when we see the glyph on the screen that we recognize as a zero, it is actually put there because the underlying byte that represents it is not numerically a zero. In order for a computer terminal or printer to display that little glyph, we actually had to send it the value 3210. This is a byproduct of machinery which was used early on in the computing field to render human readable versions of numeric and or text data.

Terminals and printers are used to produce a visual representation of the bytes we send them. Somewhere in the early history of computing, someone had the good sense to create a widely used standard for the encoding of printable and other bytes that terminals, printers, and some other devices use. That standard defined what we call ASCII encoding. It allows devices like terminals, printers, modems, etc., to be at least somewhat interchangeable.

What the ASCII standard defines is how bytes are used to render all of the various uppercase and lowercase characters, number and punctuation marks. It also defines what a lot of the non-printing bytes do, such as carriage returns and linefeeds, tabs, backspaces, and delete keys. The standard was fairly straight forward, spelling out what bytes 0 to 127 did. For those of you who didn't do the mental gymnastics right away, I'll make it a little easier: the standard spells out the purpose of all of the bytes from 0 to 7F16. Does that make it easier to see that only the bytes with the most significant bit set to 0 were spelled out? This gave rise to the nothing of 7-bit data.

7-bit data made a lot of sense at one time, when communication was done via slow modems. Saving one bit in each byte transmitted was an automatic 12.5% increase in speed. Since there was nothing any device could use the high bit for anyway, there seemed to be little reason to use it. It also provides the final bit of information for the choice of the delete key encoding. One type of ASCII device that was used commonly used was the paper tape writer/reader. It worked by punching a series of holes in a paper tape. Each of the seven holes represented a bit in a byte. If the hole for a particular bit was left not punched, it represented a zero bit, and if it was punched through the paper it was a one bit. Since a bit could never be unpunched, it was impossible to arbitrarily modify the value of a byte. However, the value of a byte could always be changed to 'all bits set to one', by punching every bit out of the tape. This allowed the byte to be treated as ignored, and so you could 'delete' any byte on the tape that way.

Back to the point about the ASCII values not corresponding to their human readable representations. The printable characters in the ASCII table are all of those with values greater than or equal to 32. The upper and lower case characters are arranged in the table so that they differ in only one bit per alphabetic character. This makes it easy to convert between upper and lower case; simply set bit 6 of an upper case alphabetic character's ASCII value to make it lower case. The values 0-31 are assigned to special purposes. Most terminals can generate the first 26 ASCII bytes by typing the 'control' version of each alpha character. For instance ASCII byte 3 is often emitted by holding the control key and pressing 'C'; Ctrl-C for short.

It can be a source of confusion that the printable representation of an eight-bit byte may take as many as three bytes to display. For instance, to display a byte with the bit pattern '01110110' using printable ASCII characters will require the three ASCII bytes '2116', 2216', & '2316'. These are the characters '1'. '2' and '3'. What's more, it can be tricky to make the calculation that converts the raw value to the string of printable characters that lets us visualize the value, at least in very low level languages such as assembler. In contrast, the process to convert from the raw value to a printable string is simple to do using the hexadecimal or even octal radices.

In computing there is a lexicon that is commonly used to describe the nature of digital data, especially in the numeric sense. Some of the commonly used expressions are technically inaccurate, but it is nevertheless the standard way to describe certain situations. To the uninitiated, this language can be misleading, and I want to explain a bit about how the language is used and what it is used to describe.

One of the commonly confusing uses relates to the word 'binary'. When we refer to 'binary' data, it is generally used to describe a data set that is not generally 'human readable'. It contains a varied mix of all data bytes, both printable and non-printable. Usually this describes something like an object file of compiled code. While there may be some embedded strings of printable data, it is not of the sort that could be correctly modified by using something like a text editor such as vi, or in Windows, notepad. The bytes are meaningful to certain kinds of programs, and are almost always created by programs with certain purposes. We sometimes want to visualize these kinds of data, and for that we use tools that are capable of translating the bytes or multi-byte words into some orderly arrangement of human-readable text. In Linux, one such general purpose tool is the program '

We can use

Code:

dd if=/dev/sda count=1 bs=512 | od -tx1c 0000000 fa b8 00 10 8e d0 bc 00 b0 b8 00 00 8e d8 8e c0 372 270 \0 020 216 320 274 \0 260 270 \0 \0 216 330 216 300 0000020 fb be 00 7c bf 00 06 b9 00 02 f3 a4 ea 21 06 00 373 276 \0 | 277 \0 006 271 \0 002 363 244 352 ! 006 \0 0000040 00 be be 07 38 04 75 0b 83 c6 10 81 fe fe 07 75 \0 276 276 \a 8 004 u \v 203 306 020 201 376 376 \a u 0000060 f3 eb 16 b4 02 b0 01 bb 00 7c b2 80 8a 74 01 8b 363 353 026 264 002 260 001 273 \0 | 262 200 212 t 001 213 0000100 4c 02 cd 13 ea 00 7c 00 00 eb fe 00 00 00 00 00 L 002 315 023 352 \0 | \0 \0 353 376 \0 \0 \0 \0 \0 0000120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 * 0000660 00 00 00 00 00 00 00 00 7a a8 02 00 00 00 00 20 \0 \0 \0 \0 \0 \0 \0 \0 z 250 002 \0 \0 \0 \0 0000700 21 00 83 fe ff ff 00 08 00 00 00 00 2a 01 00 fe ! \0 203 376 377 377 \0 \b \0 \0 \0 \0 * 001 \0 376 0000720 ff ff 05 fe ff ff fe df dc 01 02 78 3f 1b 00 fe 377 377 005 376 377 377 376 337 334 001 002 x ? 033 \0 376 0000740 ff ff 82 fe ff ff 00 08 2a 01 00 d0 b2 00 00 00 377 377 202 376 377 377 \0 \b * 001 \0 320 262 \0 \0 \0 0000760 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 U 252

Stay tuned for more on this subject, as we discuss ho ]]>

By now, we've all heard that computers 'only use ones and zeros', and mostly we've come to accept the apparent contradiction without really understanding what it means. The statement is true, if not somewhat over-simplified. The use of only 'ones' and 'zeros' is another way of saying that digital devices are binary in nature; their root behavior is a combination of elements that are either one or zero. These elements are what we call 'bits'. The word is a sort of contraction of the words 'binary digits'.

It would be natural to wonder why this extremely simple element became the basis for what we now know to be fairly complex and sophisticated devices. The answer lies in the very lowest level makeup of digital electronics. It so happens that a very simple circuit called a bistable multivibrator can be created using a limited number of components. The bistable multivibrator, also commonly known as a flip-flop (the name of which preceded the name of the footwear), has the property that its output can exist in one of only two states: on or off. It's other endearing property is that its state can be controlled. As well, as I mentioned earlier, it is a fairly simple circuit,and this lends it to easy replication en-masse, in the factories that produce the silicon based digital components that fuel our digital world.

The flip-flop is composed principally of two transistors which are connected in such a way that one is always in its 'on' state, while the other is in a complementary 'off' state. I say complementary, because electronically, the state of each tends to reinforce the state of the other. So as long as the circuit is powered, the two transistors will retain their present state. I did say that the state of a flip-flop can be controlled. It is also a property of a flip-flop that an electrical pulse of the right nature, applied to the right part of a flip-flop circuit will overcome the complementary holding condition of the two transistors, and cause each of them to switch to the other state. This accounts for the name 'flip-flop'; the circuit can flip back and forth between either of two states, but only when driven by a suitable pulse.

The ability of the flip-flop to retain its state indefinitely, as long as it is supplied with power, constitutes the basis of computer memory. Each bit of computer memory is, at its fundamental level, a flip-flop.

It is worth mentioning that there was a time when flip-flops were built up not from transistors, but from vacuum tubes. These were obviously much much larger, slower, and consumed comparatively enormous amounts or energy. Those were the days when computers took up whole floors of large buildings.

If computer memory and other parts of computers are composed only of bits that represent ones and zeros, then how do we use them for doing all the things which use other numbers? After all, computers have given us the ability to use numbers that are almost infinitely large or small. The answer is that we can group bits in combinations. If a single bit can represent either of two values and we have two bits, then the combination of the two bits can represent four possible values. For each possible value of one bit, the other can also have two possible values. Adding another bit to the combination doubles the number of possible states again. So a three-bit combination now allows us to represent eight possible values. We can see that adding bits to the combination quickly expands the range of values that the combination of bits can represent.

At some point, our digital world settled on a convention that we would use bits in combinations of eight. We would call the combination of eight bits a 'byte'. By extending the logic described in the previous paragraph, we can determine that all the possible combinations of ones and zeros that can be represented by eight bits totals 256. So, in an a mere eight-bit byte, we can store any one of 256 discrete values. Just as we did to build up bits into a byte, we can combine bytes in multiples, to get exponentially greater combinations of bits.

Now, we've already stated that a byte combines eight bits, so that there are 256 possible unique combinations of ones and zeros that can exist within a byte. To use this property to the greatest advantage, we must somehow assign some distinction between the individual bits. Otherwise, how would we know which combination of four zeros and four ones, for example, we are talking about? And, how would we assign some value to any particular combination?

The answer to this question starts to lead us into some arithmetic. It is a little beyond the arithmetic used to balance your checkbook, but isn't too difficult.

In order to start giving all of the various combinations of ones and zeros in a byte some actual values, we assign each bit a 'weight'. We use a notation that describes the weight of a bit, by appending an index number to the bit. This ends up with bits that are numbered 'bit-0', 'bit-1', 'bit-2', etc. Since there are eight bits, and we started at 'bit-0', the 'last' bit will be 'bit-7'. Most readers will immediately notice that we starting counting our bits from number zero, rather than the more conventional counting from one. There is a good reason for that, and we will see it, as we start to develop our understanding of the weights of each bit.

When we say that bits are assigned a weight, it implies that some bits are more 'influential' than others in their contribution to the value of the combination if bits. Mathematically, this is correct. In fact as the index number of the bit increases by one, its weight doubles with respect to the next lower indexed bit. How does this help us, you might wonder? Well, it leads to a formula, which we can use to get the unique value that each combination of bits in a byte represents.

For a byte with bits numbered 0 through 7, the value of the byte is a sum of weighted bits. Bit 0 can contribute 0 or 1, depending on its state. Bit 1 can contribute 0 or 2, depending on it's state. Bit 2 contributes either 0 or 4, and so on for each bit in the byte. We see that the value of each bit is either 0, or it is 2 raised to the power of its bit number.

Lets see how that looks for the first two bits, bit-0 and bit-1. Bit zero contributes the values 0 or 1, and bit one contributes the values 0 or 2. All of the four possible combinations , and the respective sums of weighted bits:

Code:

bit 1 0 ----------- 0 0 ==> 0 + 0 = 0 0 1 ==> 0 + 1 = 1 1 0 ==> 2 + 0 = 2 1 1 ==> 2 + 1 = 3

Code:

bit 2 1 0 ------------------ 0 0 0 ==> 0 + 0 + 0 = 0 0 0 1 ==> 0 + 0 + 1 = 1 0 1 0 ==> 0 + 2 + 0 = 2 0 1 1 ==> 0 + 2 + 2 = 3 1 0 0 ==> 4 + 0 + 0 = 4 1 0 1 ==> 4 + 0 + 1 = 5 1 1 0 ==> 4 + 2 + 0 = 6 1 1 1 ==> 4 + 2 + 2 = 7

Although we are starting to see how bits become part of a bigger whole with some probable purpose, it still isn't obvious how all of the ones and zeroes sitting around can do us any good. We stated earlier that the flip-flop used to store a bit is the fundamental unit of computer memory. What was not stated was that besides storing bytes, computer circuits can also be used to manipulated bytes in different ways. A byte can be read from its storage place, and copied into another storage place. A byte can be used in an arithmetic operation, such as addition or subtraction. A byte can be manipulated 'logically' by performing various kinds of comparisons of it against other bytes, such as the 'and' and 'or' comparisons. These are some of the fundamental ways that the microprocessors in our computers operate on bytes and multi-byte 'words'.

Not surprisingly, just as we combined bits into bytes, and bytes into words to create more and more useful building blocks, we can combine the fundamental operations that a microprocessor is able to perform into increasingly useful groups. This activity is called programming. More on that later.

At this stage, we have developed the concept of what makes up the bits and bytes in our computers, and a little bit about how they are organized. Since the point of this essay is to start clarifying some misunderstood concepts, we'll jump in with one now. You will notice that when we learned that a bit could have the distinct values 'zero' or 'one', we attached these numeric values to the bit states somewhat arbitrarily. We can, and do, also assign boolean values to the two states in which a bit can exist: true vs. false, for example. We sometimes use the terms 'on' and 'off', as well as any number of application-specific values for the state of a bit.

When we talk about computer memory, and the fact that it is composed of bits in combinations of bytes, we tend to think of the cells of memory as places where we store some value. What we often overlook is that a memory cell, be it a bit, and byte, or any other unit of memory always has a value. This is true, regardless of whether we have deliberately put some value into it or not, and is something that programmers in particular need to keep sight of.

Another concept that we need to understand, is that although a byte has a distinct value, its value does not have a unique meaning. The value of a byte can be used in a virtually unlimited number of ways, and only within a specific context does a byte have a particular meaning. A byte whose value is 32, for example, could mean different things to different people or different computer programs or different bits of computer hardware. We will see how this matters to us in more detail later.

We showed, above, that we can uniquely represent the values 0 through 255 in a single byte. In so doing, we used the familiar decimal counting system, having 10 unique digits. If we cascade two bytes together, we find that we can represent 65536 distinct values, and we can expand on this doubling of the number of bytes to increase the range of numbers available to us arbitrarily. Sometimes, we are less interested in the aggregate value of a byte than we are of the particular pattern of ones and zeros. Programmers, in particular are sometimes interested in the bit-level value of a byte. Its content may be a representation of some state of digital hardware, such as whether a button has been pressed or not, or whether a piece of equipment is in a particular state. In order to determine the state of an individual bit within a byte, the decimal notation we use to represent its value is not very convenient. As humans, we cannot very readily tell which bits have different values by comparing the values 123 and 129, for instance.

There are other numbering systems which can be used to express numeric values. Our familiar decimal number system uses 10 unique digits. Other numbering systems use different numbers of digits with which to count. One counting system has already been touched on here: the binary counting system. We can use only zeros and ones to count and to express values. In computing, especially for programmers, it is sometimes convenient to use other counting systems. Most commonly, these counting systems are the octal system, having 8 digits, and the hexadecimal system having 16 digits (0-9, and the letters A-F). The table below shows how all the possible combinations of 4 bits map on the 16 hexadecimal digits.

Code:

Binary Hexadecimal Decimal --------------------------------- 0 0 0 0 0 0 0 0 0 1 1 1 0 0 1 0 2 2 0 0 1 1 3 3 0 1 0 0 4 4 0 1 0 1 5 5 0 1 1 0 6 6 0 1 1 1 7 7 1 0 0 0 8 8 1 0 0 1 9 9 1 0 1 0 A 10 1 0 1 1 B 11 1 1 0 0 C 12 1 1 0 1 D 13 1 1 1 0 E 14 1 1 1 1 F 15

The use of other counting systems in digital systems usually stems from one or more of two root causes. In the hexadecimal (hex) counting system and also the octal system, the value of each digit in the number maps exactly to a specific sequence of bits. In the decimal counting system, this consistent mapping does not happen, and so the connection between bit patterns and the corresponding decimal values is more difficult for we humans to make.

In octal counting, the eight digits map directly to sequences of three bits, and in hex the sixteen digits are exactly represented by four bits. Hex has the added advantage that the four bits of a hex digit is exactly half of a byte, or to put it another way, a byte can be exactly expressed in exactly two hex digits. By partitioning the expression of a byte into two hex digits, we break the expression of the byte into two four-bit segments. The correspondence between the 16 hex digits and the respective pattern of bits is relatively easy to memorize, or even to calculate mentally. When the pattern of bits on a byte or word is significant, it is often more convenient to use the hexadecimal radix to express that value.

Code:

+--------------+-----+----+----+----+----+----+----+----+ | Bit Number | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +--------------+-----+----+----+----+----+----+----+----+ | Binary | 0 | 1 | 0 | 0 | 1 | 0 | 1 | 0 | +--------------+----------+---------+----+--------------+ | Octal | 1 | 1 | 2 | +--------------+----------+---------+----+--------------+ | Hexidecimal | 4 | A + +--------------+--------------------+-------------------+

A byte is sometimes referred to as being comprised of two nybbles, an upper nybble and a lower nybble, referring to the bits 0-3 (low nybble), and bits 4-7 (high nybble). The hex digits in a byte are the high nybble and low nybble.

One place that many of us have seen the use of hexadecimal is in the HTML color representation. There, colors can be expressed in three hex words. This is convenient, since each word maps directly to one to one of the three color components: Red Green and Blue. We can adjust just one byte, and easily determine which of the color components we are adjusting. This makes it easier to predict what a particular color value might look like, and especially to predict how we might want to change it to get a desired result.

Notice the number of times that I used the word 'express' in the above paragraph. This is deliberate, as I want to make the point that the value of a byte does not vary merely by the way we express that value. The bit pattern '10100101' can be expressed in any radix we choose. The value is still the same, although we do need to know the radix used to express the value, in order to know exactly what value is being expressed. This leads us to the expression 'there are 10 kinds of people in the world: those who know binary and those who do not'. Without knowing that the number '10' was expressed in binary, our temptation would be to assume it was an expression in the decimal radix. Often, we see the notation where the radix of a number is given as a subscript to the number, like the number 12AF

Just as it is convenient for humans to make a mental conversion between bit patterns and hex notation, it is similarly simpler to convert bytes or words to human readable ASCII form by a computer program. The exact mapping of a 4-bit half byte, called a nybble in some contexts, makes the conversion simple using a small 16-byte lookup table. The value of this is probably lost on most programmers, but there was a day when every cycle and byte counted. Even today, however, embedded systems programmers will write conversion routines using hexadecimal representation quite frequently.

For more on this subject, see Part II, elsewhere in this blog. ]]>