TI-83+ Z80 ASM for the Absolute Beginner
LESSON SIXTEEN:
· Working
With Bits
WORKING
WITH BITS
Bits of cornflakes? Bits of information? Bits of your sister’s teddy bear or stuffed raccoon? Nope, the eight bits found
in every byte.
Suppose you have a picture that you finished
displaying using what you learned from Lesson 14. Let’s say you want to draw a point on the
screen, just a point. Remember that
every bit (not byte) in plotsscreen refers to a
single pixel. So you could draw the
point you want by turning the appropriate bit on. The question is, how
do you do that?
In the Mario game from tutorial 1, on pages
10-11, we speculated having true/false values for whether or not Mario was
swimming, flying, or had the fire flower.
Since each of these could be equal to 1 for true or 0 for false, we
could store each of these as a bit. A
review is below:
Once again, the
question is, if all of a sudden Mario is flying, how do we change the 3rd
“0” to a “1” in order to let the calculator know that Mario is flying? Also, Mario can’t fly and swim at the same
time, so how do we change the 2nd “1” to a “0” to show that Mario
isn’t swimming?
Most of the
instructions that you’ve learned so far, if not all of them, deal with
bytes. This lesson, you’ll learn instructions
that deal with bits. This is a long
lesson, by the way, so take your time!
When you have a number
one byte long, that is, 8 bits long, the bit on the far right is bit 0. The bit on the far left is bit 7. So the 8 bits are labeled, from left to
right, 7 to 0. For the Mario example,
assume that Bit 7 is “Mario Alive.”
Bit 7, 6, 5, 4, 3, 2, 1, 0
% 1 1 0 1 1 1 0 0
This
example program will draw a whole bunch of lines on the graph screen. There will be 48 lines, each 10 pixels long
downward. Be sure to read the comments,
as usual.
While these 3 functions
are convenient and excellent for messing with and retrieving individual bits,
it’s a little more complicated using these instructions for groups of 2 or more
bits.
To illustrate this, I’m
going to use the example of the Real-Time Strategy I’m coding, called Seek And Destroy. In my
data, in order to save space and allow faster processing time, I use 4 bits to
store certain information needed for the building’s construction time. Example:
% 1 0 0 0 0 1 0 0
Data needed
Suppose that I put this
data into register A. Putting the last
four bits together, I would want to
end up with register A being a number between 0 and
15. 0 = %0000, and 15 = %1111.
The problem is, there’s 4 more bits in the number, in front of the 4 bits
I want. If these 4 bits are all equal to
0, then register A will be a number between 0 and 15. BUT, if even one of these 4 bits at the
beginning of register A is equal to 1, register A will be bigger than 15, since
15 = %00001111.
So how can we fix this,
how do we make sure that the four bits at the front of register A are all zeros?
Well, you could use the following code on the next page.
That works, but it
takes a lot of valuable space in your program.
Furthermore, it requires a lot of valuable TIME in your program—in other
words, that code is slow.
It gets worse. I want to put this number, from 0 to 15, into
the last four bits of register B. (And
this can be any number from 0 to 15 in the last four bits of A. We don’t know what this number is.) This means, I want the first four bits of register B to remain untouched, in
order to preserve whatever data I have in the first four bits of that register. (Even though the registers are different,
this is not a hypothetical situation. I
really had to do this with my program). The
code on the next page can do that, but can you imagine what a nightmare it
would be for speed and size?
Now that you know why
the 3 instructions are not so good for groups of bits, I’ll show you what is.
I’m going to ask you to
think back to your Ti-Basic programming.
Do you recall using “and” and “or” in your “If…then” statements? Perhaps you even used Not
and Xor. Let’s
just stick with “and” and “or” for a few minutes.
Suppose you have the
following Ti-Basic program:
If
A = 1 and B = 1
Disp
“A = 1 and B = 1”
As you might expect,
the text “A = 1 and B = 1” is only going to display if BOTH A and B are equal
to 1. If A = 1 and B is some other
random number, the text will not display.
What about this code?
If A = 0 or B = 0
Display “A * B = 0”
Then you have three
possibilities when the text displays: Either A is equal to 0,
or B = 0, or BOTH of them = 0. However,
if A and B are both numbers not equal to zero, the
text will not display.
How about some “fake
code” such as below?
So what that means is,
if you take a bit in A and compare it with the same bit in B, they both must be
equal to 1. Otherwise, the bit in
question in A will be 0, no matter what.
Suppose register A = %10011111
and register B = %11100001.
%10011111 ;Register A
and %11100001 ;Register
B
Result: %10000001 ;The result is stored in register A
As you might guess, if
we decide to use “OR” instead, then for each bit in registers A and B, only one
of the two that we compare needs to be equal to 1. The particular bit in register A would be
zero only if the bit in question is equal to zero in both registers A and B.
%10011110
or %11100000
Result: %11111110
With XOR, available in
Z80 ASM, if we compare a bit from A with the same bit from B, the bit in
question will be set to “1” in register A if the bits from A, B are not equal
to each other. If they are both equal to
1 or both equal to 0, the result is “0”.
%10011110
or %11100100
Result: %01111010
Exercises: Compute the result (answers on next
page):
1. A =
%10000101, B = %11100000. A AND
B
2. A =
%01010111, B = %00111000. A OR
B
3. A =
%01010110, B = %11001010 A XOR
B
4. A =
%10101010, B = %01101100 A AND
B,
A OR B,
A XOR B
Answers:
1. %10000000
2. %01111111
3. %10011100
4.
%101000, %11101110,
%11000100
So what’s the big
deal? After all, in Z80, you can’t use
and, or, xor in If-Then statements. Or can you?
I’ll tell you what the big deal is after I give you 6 instructions.
WHAT’S
THE BIG DEAL #1:
We can, of course, use these values to set, reset, or even flip (0
becomes 1 and 1 becomes 0) certain bits in register A. If you want to set a bunch of bits in
register A to “1”, OR a number—let’s say register B—to register A. Register B should have a “1” in each bit you
want to set in register A. For
example, if you have a number in register A, and you want to make the last 5
bits ones WITHOUT changing the values of the top 3 bits, just use the
instruction OR %00011111. This means
that if you have a “0” for a bit in register B, the respective bit in register
A will be ignored, ignored, ignored.
If you want to reset a bunch of bits in register A to
“0”, AND a number—let’s say register B—to register A. Register B should have a “0” in each bit you
want to reset in register A. For
example, if you have a number in register A, and you want to make the first 4
bits zeros WITHOUT changing the values of the last 4 bits, just use the
instruction AND %00001111. This means
that if you have a “1” for a bit in register B, the respective bit in register
A will be ignored.
If you want to flip a bunch of bits in register A,
XOR a number—let’s say register B—to register A. Register B should have a “1” in each bit you
want to flip in register A. For example,
if you have a number in register A, and you want to flip the first bit in
register A WITHOUT changing the values of the last 7 bits,
just use the instruction XOR %10000000.
This means that if you have a “0” for a bit in register B, the
respective bit in register A will be ignored.
Exercise: What do you
order if you want:
1.
To make Bits 7, 5 and 1 of A equal to 1,
without changing the rest of the bits?
2.
To flip bit 6 of register A?
3.
To make Bits 3-0 equal to 0?
Answers:
1. OR %10100010 (You can also use OR 162 J
)
2. XOR
%01000000
3. AND
%11110000
WHAT’S
THE BIG DEAL #2:
Indeed, you can use AND, OR and XOR as If…Then instructions. Of course, you can’t go “If this is true AND
this is true OR this is true XOR this is true.”
Sad, but true.
However, just like with bit, you can use AND, etc. to test and see if
certain bits in a number are the way you want them.
In my S.A.D. code, I
sometimes need to see if those 4 bits I mentioned previously are all
zeros. To do this, I first store the
number in register A, and then use the statement AND %00001111. This will clear out the top four bits. Do you know what this means? This means that if the bottom four bits are
all zeros, the number itself will be zero.
And of course, you know what that means: The Z flag is set!
WHAT’S
THE BIG DEAL #3: OR
A is the same thing as saying CP 0, and it’s faster and smaller than CP 0. OR A will never, ever mess up whatever you
have stored in register A, so use it whenever you can instead of CP 0.
XOR A will always,
always set A equal to zero. Can you find out why? Anyways, this is smaller and faster than LD
A, 0.
An example program is
on this page. Then we have one more
thing to learn in this chapter. Yes,
it’s a lot of information for one tutorial, but it will be worth it when you
find out that next lesson will cover sprites—aka pictures you can
move on the screen!
The last thing you will
learn in this lesson is how to move bits around a number. Yes, move around, like a merry-go-round or an
assembly line. What do I mean by that?
Well, an assembly line
moves an object, such as a car, down a belt.
So if the car starts at the front, the belt will eventually move it to
the end of the line. The car moves from
its original position to a different position.
Yes, I do want you to
remember the term “shift.” The car
shifted forward from its original position, in a single direction.
What about my analogy
of the merry-go-round? Well, if you
think about it, when you were on a merry-go-round, you started at a certain
point. If you kept going, and the
merry-go-round did not break down, you would eventually reach the point where
you started, basically going around in circles.
This is not like “shifting,” where you go in a single direction and
never return. Instead, you “rotate” in a
circle.
Can we do this with
bits? YOU BET!
For the sake of saving
space, I will simply say that RR, RRA, RRC, and RRCA do the opposite of
RL, RLA, RLC, and RLCA.
There are many purposes for shifting and rotating
bits. Two of the most common are for
drawing sprites and for multiplying/dividing by powers of two (meaning diving
by 2, 4, 8, 16, 32, 64, etc.) We will
learn how to use shifting/rotating for sprites next lesson. The next example program will demonstrate multiplication
and division by powers of two. But to
understand the concept, try the following 6 math problems, and feel free to use
a calculator:
1. 2000
/ 10
2. 2000
/ 100
3. 2000
/ 1000
4. 2*10
5. 2*100
6. 2*1000
You will notice that in
problem one, the answer was 200: what
you did was shift the “2” one place to the right! And when you divided by 100 (meaning 10 *
10), you shifted the “2” TWO places to the right! So as you can probably guess, when you divide
by 10 * 10 * 10, you shift three places to the right.
When you multiply by
10, you shift 1 place to the LEFT. So
when you multiply by 10 * 10 * 10, you shift 3 places to the left.
However, this is when
you’re dealing with decimal numbers. In
binary numbers, multiplying by 2 will shift one place to the left,
multiplying by 2 * 2 will shift two
places to the left, dividing by 2 * 2 * 2 will shift 3 places to the RIGHT, and
so on and so forth.