NUMBERING SYSTEMS IN GENERAL
Before we can learn assembly language programming, we must first review several different numbering systems used in such programming. Let's first review the decimal system, the one with which we are most familiar. The decimal system is based on ten, most likely because we have ten fingers, and counting using our fingers was the simplest form of arithmetic for early peoples.
Think about the number 123 for a moment. Exactly what does this number represent? If we think about what we learned in school, we remember 1 one hundred, 2 tens, and 3 ones, which, when added together, total 123. There is, however, another way of looking at this number. It turns out that each digit in the decimal system (base 10) is 1 power of 10 higher than the digit to its immediate right. For those of you who don't clearly remember what a power is, that term simply tells you how many times the base is multiplied by itself. For instance, 10 to the power of 3 is 10 x 10 x 10 = 1000, or 10 multiplied by itself 3 times.
Now, to return to 123, we remember that in any numbering system, the right-hand-most digit is always the ones column. Why is this? Because that digit is always the base – in this case, 10 – to the zero power. Anything to the zero power is always 1, so this digit is always the ones digit. In our example we get 3 x 1 = 3. The next digit, the 2 in this case, represents the base 10 to the first power, or 10. Since 2 x 10 = 20, we get the correct middle digit for our number. Finally, the left-most digit, 1, represents the base 10 to the second power, or 100. Therefore, this digit represents 100 x 1, or 100, and the whole number represents 100 + 20 + 3, or 123. If we view the number schematically, this process becomes somewhat easier to follow. In the examples below, we always start from the right, and progressively move toward the left. For instance, we can represent the decimal number 123 as:
The base |
To
the power of |
Equals |
Times
the digit |
Equals
the value |
|
||||
10 |
0 |
1 |
3 |
3 |
10 |
1 |
10 |
2 |
20 |
10 |
2 |
100 |
1 |
100 |
Total = |
123 |
Let's pick a slightly more complicated number and go through it one more time, using 53,798.
The base |
To
the power of |
Equals |
Times
the digit |
Equals
the value |
|
||||
10 |
0 |
1 |
8 |
8 |
10 |
1 |
10 |
9 |
90 |
10 |
2 |
100 |
7 |
700 |
10 |
3 |
1,000 |
3 |
3,000 |
10 |
4 |
10,000 |
5 |
50,000 |
Total = |
53,798 |
THE BINARY NUMBERING SYSTEM
Now that we've seen how to take apart a decimal number, the kind with which we're all so familiar, let's move on to a different numbering system, the binary system. Why binary? Computers really are relatively simple devices, and the fundamental piece of information stored in them is called a bit, or binary digit. A bit is the smallest amount of information; it can either be on or off, yes or no, a 1 or a zero. In a sense, a bit is like a standard light switch.
Excluding dimmers for a moment, a light can either be on or off; there is no in-between. A bit in a computer behaves exactly the same way. This explains why the binary numbering system is such a natural system for computers. The binary system consists of only two digits, zero and 1. Therefore, anything represented in the binary system can immediately be understood by our computer, which really can understand only these two digits.
Learning a new numbering system could be a real chore, but we'll make it easy. In fact, all numbering systems work exactly the same way. The only difference between them is the base used. The decimal system, as we have seen, uses 10 as the base. The binary system uses 2 as the base. Note that the largest digit in any numbering system is always 1 less than the base. For example, 9 is the largest digit in base 10, and 1 is the largest digit in base 2. Why? Because we have to allow for the digit zero, and in every numbering system the total number of different digits is equal to the base for that numbering system.
To show you how easy it is to understand the binary system, we'll take it apart just as we did the decimal system above, using the binary number 10110110.
The base |
To
the power of |
Equals |
Times
the digit |
Equals
the value |
|
||||
2 |
0 |
1 |
0 |
0 |
2 |
1 |
2 |
1 |
2 |
2 |
2 |
4 |
1 |
4 |
2 |
3 |
8 |
0 |
0 |
2 |
4 |
16 |
1 |
16 |
2 |
5 |
32 |
1 |
32 |
2 |
6 |
64 |
0 |
0 |
2 |
7 |
128 |
1 |
128 |
total
= |
182 |
Therefore, 10110110 in the binary numbering system is equal to 182 in the decimal numbering system. Let's try one more, only this time you try it by yourself first, before you look at the answer, and then we'll work it out together below. The binary number to convert is 01011101.
The base |
To
the power of |
Equals |
Times
the digit |
Equals
the value |
|
||||
2 |
0 |
1 |
1 |
1 |
2 |
1 |
2 |
0 |
0 |
2 |
2 |
4 |
1 |
4 |
2 |
3 |
8 |
1 |
8 |
2 |
4 |
16 |
1 |
16 |
2 |
5 |
32 |
0 |
0 |
2 |
6 |
64 |
1 |
64 |
2 |
7 |
128 |
0 |
0 |
total = |
93 |
Did you get it by yourself? The binary number 01011101 is the same as the decimal number 93.
Now we know how to get from a binary number to a decimal number, but how do we reverse the process, in order to get from a decimal number to a binary number? That's even easier. Just take your decimal number and successively divide by the powers of 2, starting with the highest power which will divide into your decimal number with a result of 1. Each time, divide the remainder by the next-lower power of 2. For instance, let's convert 124 to the binary system.
124/128 = 0 | with a remainder of 124 | |
124/64 = 1 | with a remainder of 60 | |
60/32 = 1 | with a remainder of 28 | |
28/16 = 1 | with a remainder of 12 | |
12/8 = 1 | with a remainder of 4 | |
4/4 = 1 | with a remainder of 0 | |
0/2 = 0 | with a remainder of 0 | |
0/1 = 0 | with a remainder of 0 |
The number 124 in the decimal system equals 01111100 in the binary system.
THE HEXADECIMAL NUMBERING SYSTEM
Now we're almost finished with numbering systems. One more step to go, and we will be finished. There is a much easier way to represent numbers than the binary system. This system is called the hexadecimal system. Hexadecimal? Hexadecimal stands for 16, and the base of the hexadecimal system is, not suprisingly, 16. We already know that the largest single digit in this system must be 15. Wait a minute! The number 15 is not a single digit. So we need some new way of representing the numbers from 10 to 15. The easiest symbols to remember are the first six letters of the alphabet. Therefore, in the hexadecimal system, the number 10 is represented by the letter A, 11 by B, and so on, up to 15, which is represented by the letter F Since the base of the hexadecimal system is 16, the values of the digits increase by powers of 16. An example is in order. Let's translate the number 6FC in hexadecimal nomenclature into the decimal system.
The base |
To
the power of |
Equals |
Times
the digit |
Equals
the value |
|
||||
16 |
0 |
1 |
C |
12 |
16 |
1 |
16 |
F |
240 |
16 |
2 |
256 |
6 |
1536 |
Total = |
1788 |
So hexadecimal 6FC represents decimal 1788. In most computer articles and texts, hexadecimal numbers are preceded by a dollar sign, so the proper way to represent the decimal number 1788 in hexadecimal nomenclature is $6FC. To convert from decimal to hexadecimal, divide as was shown above, but instead of dividing by powers of 2, divide successively by powers of 16:
1788/256
= |
6 |
with a remainder of 252 |
252/16 = |
15 (F) |
with a remainder of 12 |
12/1 = |
12 (C) |
with a remainder of 0 |
One more conversion to go, and this is by far the easiest of all. Let's use the binary number 10110111. We already know that this is equal to the decimal number 183, and we could convert this to its hexadecimal equivalent. But this is the long way around. We will frequently need to convert from binary to hexadecimal, so let's learn how to do it directly, in one very easy step.
We first take the binary number, 10110111 in this case, and break it into two parts, right down the middle. If the 8 bits are called a byte, then each set of 4 bits should be called… a nybble. And it is! The high-order nybble is 1011, and the low-order nybble is 0111. Each of these nybbles can easily be converted to a single hexadecimal digit, since four bits represents a number from zero to 15.
1011 = | 1 eight = 8 | 0111 = | 0 eights = 0 |
0 fours = 0 | 1 four = 4 | ||
1 two = 2 | 1 two = 2 | ||
l one
= 1 |
l one
= 1 |
||
Total = 11 (B) |
Total = 7 |
Thus, the hexadecimal equivalent of 10110111 is $B7, obtained directly without going through a decimal number as an intermediate (Fig. 2-1). You can work out for yourself that the number is the same no matter how you obtain it.
Byte = 1011 0111 binary | ||
↓ High order nybble 1011 binary or $B hexidecimal ↓ |
↓ Low order nybble 0111 binary or $7 hexidecimal ↓ |
|
Byte
= $B7 hexidecimal Fig. 2-1 |
To translate from hexadecimal into binary, just reverse the above process. Here's how the hexadecimal number $FA is represented in binary nomenclature:
F = | 1 eight | C = | 1 eight |
1 four | 1 four | ||
1 two | 0 twos | ||
1 one | 1 one | ||
|
|
||
1111 |
1101 |
||
11111101 |
ORGANIZATION OF DATA
Now that you can easily convert numbers from one base to another, let's talk for a moment about how data is organized in your computer. You'll have noted already that in all of the above examples, the binary numbers were organized into groups of eight digits. In computer jargon, 8 bits form a byte. Each memory location in your ATARI stores 1 byte of information. It should be apparent that in the largest possible byte, all of the bits are equal to 1. You can now calculate from this that the largest single byte any computer can store is decimal 255. By the same logic, there are only 256 possible different bytes (remember zero). So how does the computer handle larger numbers, and how does it handle more than 256 different numbers?
Computers can handle larger numbers in two different ways. One is to couple several bytes together to represent a single number. Using this technique, a 2-byte number can be as large as 256 x 256, or 65,536. Although 3-byte numbers are not normally used in your ATARI, this system allows numbers as large as 256 x 256 x 256, or 16,777,213. As you can see, this technique will allow storage of very large numbers. The second method of large number storage is to use floating point numbers. The numbers used so far in this chapter have all been integers; that is, they have all been whole numbers. No fractions or decimals can appear in an integer. However, there are no such restrictions on floating point numbers. Numbers such as 1.237 or 153.2 are perfectly valid floating point numbers, whereas they are not valid integers. The term floating point comes from the concept that the decimal point can float from place to place, as in the two decimal floating point numbers just described. In both cases, there were four digits in the numbers, but in the first case, three were to the right of the decimal point, and in the second, only one was. How do we represent such numbers in a computer?
In general, the numbers are coded so that 1 byte represents the power of 10 by which the number is multiplied, 1 byte represents the sign of this power (whether the number is greater than one, or between zero and one) and several bytes represent the mantissa, or the number itself. In other words, we could code 153.2 as the following sequence of bytes:
1,2,1,5,3,2
In the coding scheme used here, the first digit represents the sign of the exponent, with 1 being positive and zero being negative. The second digit represents the power of 10 by which to multiply the mantissa, or 100. The rest of the digits represent the number itself, with the decimal point understood to be after the first digit. Therefore, decoding this number according to these rules gives:
100 x 1.532 = 153.2
Of course, many other coding schemes are possible, but the main idea is that by coding numbers, very large numbers can be represented in a computer, even using no byte greater than 255.
In our discussion so far, we have concentrated entirely on positive numbers. How do we handle negative numbers? By using signed binary arithmetic. In this system, the left-hand-most bit, called the most significant bit, doesn't represent a power of 2 at all, but rather represents the sign of the number. That is, if the most significant bit is 1, the number is negative, and if the most significant bit is zero, the number is positive (Fig. 2-2). One fallout of this system is that the largest signed number we can represent in 1 byte is +128, or -127, since we only have 7 arithmetic bits with which to work.
Unsigned binary numbers | |||||||||||||||||
|
|
||||||||||||||||
Signed binary numbers | |||||||||||||||||
|
|
||||||||||||||||
Fig. 2-2 |
One note of caution is warranted here. If you are using 2-byte signed arithmetic (and the ATARI does this frequently) only the most significant bit of the most significant byte (the byte representing the highest digits of the number) is the sign bit. That is, a 2-byte signed number contains 15 numeric bits and only 1 sign bit. This will become very important later when we get into 2-byte math. Of course, when we use floating point arithmetic, we need to add to our code 1 byte which will represent the sign of the number – that is, whether the final number represented is positive or negative, but all coding schemes used do take this into account.
MEMORY ADDRESSING TECHNIQUES
In BASIC, making reference to a specific memory location is fairly simple and straightforward. For instance,
POKE 752,1
is a direct command to place the value 1 into memory location 752. Additionally, we know that the maximum random-access memory available in a standard ATARI computer is 48K RAM. With the 10K ROM operating system, and other space taken for other specific purposes, the maximum total memory allowable in a normal ATARI is 64K, or 65,536 memory locations. This number should sound somewhat familiar, since we have encountered it before. It is the largest number which can be coded by 2 bytes.
The ATARI addresses memory by using a 2-byte system, which allows it to address 65,536 different memory locations. Every computer based on the 6502 chip has the same built-in limitation on the number of different memory locations which can be addressed. So, how can some ATARIs contain more than this amount of memory? How can some 6502 computers boast of more than 64K of total memory, ROM and RAM combined?
The secret to this increased memory addressing is to use a technique called bank selecting memory. Using this procedure, another byte is used to control which bank of memory the 2-byte addressor will reference. Imagine a computer with 16 banks, or tiers, each of which contains 64K of total memory. Only 1 of these 16 banks could be used at any one time, but all might be available from time to time. If the bank-selective byte is equal to 0, the first bank, which contains the normal 48K of RAM and the ATARI operating system, is selected. Under these conditions, if the addressor says that it wants information from location 752, it retrieves information from the normal location 752, just like an unmodified ATARI. If, however, the bank-selective byte is equal to 1, the first 64K bank of RAM is selected. Another PEEK at location 752 would now choose a location in this bank of memory, which would probably contain information totally different from that contained in the previous example.
As you can probably imagine, some aspects of bank-selective memory are particularly tricky to handle. For instance, imagine running a BASIC program stored in the normal 48K. Halfway through the program, we access a new bank of memory. All of a sudden, the computer loses track of the BASIC program it was running, since the program is no longer present in the addressable space of the computer, at least until we reselect the appropriate bank. This would result in a crash, and we would probably lose both our program and the information we were trying to access. These problems can be overcome to some extent, and third-party software and hardware vendors already have products on the market which will allow expansion of your ATARI beyond the usual maximum of 48K RAM. There is no theoretical reason why you cannot have an ATARI in your home with a maximum addressable memory of over 16 million bytes (yes – million!). In fact, don't be suprised if sometime soon you see expansion systems available for the ATARI that will take it well beyond the 192K maximum currently available. Such products, along with mass storage devices currently under development, will allow your ATARI to run programs as yet undreamed of by even the most diehard ATARI user. To take advantage of these systems, a thorough understanding of assembly language programming and of the construction of your ATARI will be necessary, and the remainder of this book is devoted to these two needs.
Return to Table of Contents | Previous Chapter | Next Chapter