An introduction to Ands, Ors, Peeks, and Pokes. (Commodore's port) (column) Alfred J. Bruey.
An introduction to ANDs, ORs, PEEKs, and POKEs
Even if you don't write Commodore programs, you have probably noticed the words AND and OR as you were looking through a program listing or typing in a program from this magazine. You are almost sure to encounter a line of Basic coding that contains an AND or OR if you are working with a program that uses the sound capability of the C64. For example, you might have seen an instruction like:
100 POKE X, PEEK(X) AND 15
If location X is the location that controls the attack/decay parameters for voice 1, this instruction will change the attack value to a 0 and leave the decay value unchanged from what it was when the POKE instruction was executed.
If you understand what you have just read, you probably don't need to read the rest of this column. If you don't, stick with me, and you'll gain at least an elementary understanding of ANDs, ORs, PEEKs, and POKESs. Let's start at the beginning.
ANDs and ORs work at the bit level. A bit, which is the abbreviation for binary digit, can have one of two values, a 0 or a 1. The decimal digits with which you are most familar can have the values 0 through 9. A decimal number larger than 9 can be created by placing decimal digits side-by-side. In this way we create the number 7849 by placing the digits 7, 8, 4, and 9 side-by-side. The decimal number system is a place value system, that is
7849 = 7*1000 + 8*100 + 4*10 + 9
7849 = 7*10(3) + 8*10(2) + 4*10(1) + 9*10(0)
There is a direct analogy between this example and the interpretation of a binary number such as 10110011 or 01011101. These binary numbers can be converted to an equivalent decimal value just as we did above, except that the place values system here is represented by powers of 2 instead of 10 as it was in the preceding example, So
10110011 = 1*2(7) + 0*2(6) + 1*2(5) + 1*2(4) + 0*2(3) + 0*2(2) + 1*2(1) + 1*2(0)
10110011 = 128 + 0 + 32 + 16 + 0 + 0 + 2 + 1 = 179
where the original binary number is in base 2 and 179 in a base 10 number. I won't bother stating the base that I'm using in the remainder of this article since it will be obvious.
Why Eight Bits Long?
It is not coincidence that both of the examples of binary numbers that I gave above are eight bits long. A string of eight bits in a row is called a byte. Although 16-bit microcomputers are becoming more common, a large number still have an 8-bit (1 byte) capacity for each memory location. This is what we mean which we say that the Commodore 64, for example, is an 8 bit computer. The first bit in the string of 8 is usually called bit 7, the second bit 6, and so on, with the final bit being called bit 0. You can see that the name of the bit represents the power of two associated with that position.
The largest number that can be held in 1 byte is, obviously, 11111111. You should be able to use the conversion method shown above to show that this is the binary representation of the decimal number 255. That is why you get an error message if you try to POKE a value like 800 into a C64 memory location. To see that this is so, try entering the line:
If you do this on the C64, you should get the message:
? ILLEGAL QUANTITY ERROR
Remember--any number POKED into a memory location must be between 0 and 255.
Back to the Subject
Now that we know what binary numbers look like, let's see how we can operate on them with ORs and ANDs. First, let's look at the OR statement, How do we find the result of the operation
11010001 OR 10100010?
It's simple. The result of ORing two binary numbers together is a number that has a 1 in any position whether either or both of the two numbers has a 1 and has a 0 in any position where both of the original numbers have 0's. For our example:
11010001 OR 10100010/11110011
But why would anyone want to OR two numbers together? Let's look at an example. Assume we want to make the low order 4 bits (bits 3, 2, 1, and 0) of location X all 1's and leave the high order 4 bits (bits 7, 6, 5, and 4) as they were. We can do this by ORing our unknown number in location X with the binary number 00001111.
For example, assume that location X contains the value 10100010. Then:
10100010 OR 00001111/10101111.
As predicted, we made the low order 4 bits all 1's and didn't change the high order 4 bits. If you are still a little unsure of this process, try it on a few more examples until you being to feel comfortable with the OR operator.
Now, on to the AND operator. The result of ANDing two numbers together is a third number which has a 1 in any position where both of the original numbers had 1's and a 0 otherwise. For example:
10101010 AND 00110111/00100010
The AND is often used to turn off a bit (change it to 3) or group of bits. For example, if we want to turn off bits 2 and 3 of a number without changing the other 6 bits, we can AND the number with 11110011. For example:
10101101 AND 11110011/10100001
That's all there is to the AND Operator. We'll get back to some more examples later.
Using and AND and OR With Decimal Numbers
If you start looking through listings of Basic programs for uses of the AND and OR operators, you won't find anything like:
100 X = YOR 11110000
We would like this statement to create a number X which leaves all 1's in bits 7, 6, 5, and 4 and leaves bits 0 to 3 unchanged. But it won't. Why not? Because the Basic language assumes the number 11110000 to be the decimal number 11,110,000. Therefore we will get our old friend, the ILLEGAL QUANTITY error.
To get the statement above to operate properly, we must convert the binary number 11110000 to decimal. If you remember what you learned above, you should be able to convert this binary number to 240. Thus you must write the statement above as:
100 X = YOR 240
For more practice, let's return to our earlier example and write the numbers side-by-side in both their binary and decimal forms:
11010001 OR 10100010/11110011 = 209 OR 162/243
11010001 AND 10100010/10000000 = 209 AND 162/128
Notice that you can make a rough check on any manual calculations that you do with ANDs and ORs, since the result of an OR will always be at least as large as the larger of the two numbers and the result of an AND will never be any larger than the smaller of the two operands.
From now on, I will be using the decimal values of the operands. If you are not quite sure what is happening, convert the decimal values to their binary values and look at what happens bit-by-bit.
The POKE and PEEK Statements
That is about all you need to know about the Basic AND and OR operators. But now that I have you here, I might as well show you how they can be used in a Basic program.
First let's spend a minute or two on the POKE and PEEK statements. These two statements are Basic statements, but they act more like machine language operations since they operate directly on the contents of RAM or ROM. Let's look at the PEEK statement first.
You won't normally use an AND or an OR in a PEEK statement, but the OR and AND become useful when combined with a PEEK statement
that is used as part of a POKE statement. Confused? You soon won't
be. Look back at the very first paragraph of this column and you will see the PEEK command being used with AND as an operand in a POKE statement.
The PEEK instruction is used to find the value in a specific 8-bit memory location (byte) of memory. As you have learned, the value that you find will always range from 0 to 255. Some locations cannot be PEEKed at. Which locations these are depend on the computer. The early versions of the Commodore computers, for example, did not allow you to PEEK at any of the machine locations that held the Basic interpreter. This kept the details of their interpreter secret for almost an hour, the time that it took skilled users to write a machine language program to print out the contents of the ROMs. In the Commodore 64, you cannot PEEK for values in the music location --location 54272, for example-- even though you can put a number into this location with a POKE statement.
What you should try to do now is to try a few PEEK instructions. The format of the PEEK is simple. To PEEK at what is in location X and print out the value, enter:
and press RETURN. You should see a value from 0 to 255 on the screen unless you have asked for the contents of some impossible location like -220 or 72558, in which case you will see an error message. Guess which one. You're right--ILLEGAL QUANTITY.
Go ahead and try the PEEK with a variety of values for X. You can't change anything with the PEEK since you are just looking to see what is there.
Such is not the case with the POKE instruction. The sole purpose of the POKE instruction is to make a change in some memory location in RAM. It is quite possible to change a memory location in such a way that the computer will fail to operate correctly. If this happens, you might have to turn the computer off and then back on again, but this is the worst that can happen.
The POKE instruction has one more parameter to specify than the PEEK. For the POKE, you must specify both the location into which you want to insert a value and the value itself. The format is--POKE X, Y where X is the location address and Y is the value you want to place in location X. Y must, of course, be a value from 0 to 255. You can POKE values into a location in a Basic program or you can use a direct statement such as:
which puts a 12 into the background color location on the C64, or the statement:
which changes the character set from graphics to lowercase on the original Commodore PET/CBM models (POKE 59468, 12 changes back to graphics from lowercase).
In your experimenting, you will probably want to add a PRINT PEEK statement to see that the POKE worked. For example, on the C64 or VIC 20, if you enter
PRINT PEEK (5000)
you should see that the POKE put a 10 in location 5000.
You should also try this short program:
10 FOR I = 0 TO 500
20 POKE 5000, I
30 PRINT PEEK (5000)
40 NEXT I
What do you think will happen? The program will print out the values from 0 to 255 and then stop abruptly with an error message when you try to force a value of 256 into the 8-bit location.
A Final Example
I said earlier that we would sometimes find it useful to use both the PEEK and POKE in the same instruction. There are occasions when it is useful to be able to change, say, the lower four bits of location X to all 1's while leaving the high four bits unchanged. We have already seen that we can do this by ORing the value in location X with the binary number 00001111, which is decimal 15. We can perform this change with the one instruction:
POKE X, PEEK(X) OR 15
To see how this works with a specific example, RUN the following program:
10 POKE 5000, 128
20 PRINT PEEK (5000)
30 POKE 5000, PEEK (5000) OR 15
40 PRINT PEEK (5000)
The PRINT statement in line 20 should return a value of 128 and the PRINT in line 40 should give you a value of 143 since 143 = 128 AND 15. If this isn't obvious to you, you should write out 128 and 15 in binary and calculate the final value for yourself.
Similarly, you should be able to figure out what happens when you replace the 128 in line 10 with 133 and replace line 30 with:
30 POKE 5000, PEEK (5000) AND 15
Notice that I changed the OR to AND. Try this and see what happens. (You should get a value of 5. Why?)
Since we are just changing a noncritical location in RAM with this program, we can't hurt anything. But, in general, you should always SAVE a program that contains a POKE before you try to RUN it the first time. Otherwise an error in some POKE address or value might force you to type in the program all over again.
By now you should have a better understanding of:
How the AND and OR operators work.
How the PEEK and POKE statements work.
How ANDs and ORs can be used in PEEKs and POKEs.
With some practice, you should gain an understanding of these concepts and in no time at all be using them in all your programs.