Difference between revisions of "Z80:Flags and Bit-Level Instructions"
KermMartian (talk | contribs) (Initial content) |
KermMartian (talk | contribs) m |
||
(4 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
= Existing Tutorials = | = Existing Tutorials = | ||
− | * Sigma's [ | + | * Sigma's [http://media.taricorp.net/83pa28d/lesson/day04.html Learn Assembly in 28 Days, Day 4.] |
− | * Sigma's [ | + | * Sigma's [http://media.taricorp.net/83pa28d/lesson/day08.html Learn Assembly in 28 Days, Day 8.] |
= Introduction = | = Introduction = | ||
Line 21: | Line 21: | ||
* '''0th bit''' Carry (C). Determines if there was an overflow or not. Note that it checks for unsigned values. The carry flag is also set if a subtraction results in a negative number. | * '''0th bit''' Carry (C). Determines if there was an overflow or not. Note that it checks for unsigned values. The carry flag is also set if a subtraction results in a negative number. | ||
− | Remember in [[ | + | Remember in [[Z80:Control Structures|Control Structures]] how we used the [[Z80:Opcodes:CP|CP]] instruction? This instruction subtracts the operand from the accumulator without changing the value of the accumulator. However, it did something else, also. It modified the flags by their definition. That way, when we checked if the flag was zero (exactly identical), we knew what the other value was. |
The same applies to the jump instructions. | The same applies to the jump instructions. | ||
Line 36: | Line 36: | ||
= System Flags = | = System Flags = | ||
− | In addition to the flags register, there is a chunk of memory that is used by the OS specifically for various things. We can use these flags to our advantage since most ROM calls use the flags in some way or another. For a list of system flags you can use, see [[ | + | In addition to the flags register, there is a chunk of memory that is used by the OS specifically for various things. We can use these flags to our advantage since most ROM calls use the flags in some way or another. For a list of system flags you can use, see [[Z80:System Flags|here]]. |
To access system flags, use a IY offset. | To access system flags, use a IY offset. | ||
Line 48: | Line 48: | ||
= Bitwise instructions = | = Bitwise instructions = | ||
− | Now that we know what flags are, how do we use them? As seen in the code above, there are 3 bitwise instructions that can be used on flags: [[ | + | Now that we know what flags are, how do we use them? As seen in the code above, there are 3 bitwise instructions that can be used on flags: [[Z80:Opcodes:SET|SET]], [[Z80:Opcodes:RES|RES]], and [[Z80:Opcodes:BIT|BIT]]. |
* '''SET''': Sets the bit (bit=1). | * '''SET''': Sets the bit (bit=1). | ||
Line 65: | Line 65: | ||
So, which one is the zeroth bit or fifth bit? The least significant bit (LSB, or 0th bit) is always the bit furthest right and the most significant bit (MSB, or 7th bit) is the bit furthest to the left. | So, which one is the zeroth bit or fifth bit? The least significant bit (LSB, or 0th bit) is always the bit furthest right and the most significant bit (MSB, or 7th bit) is the bit furthest to the left. | ||
+ | {| class="wikitable" | ||
|| 7th bit (MSB) || 6th bit || 5th bit || 4th bit || 3rd bit || 2nd bit || 1st bit || 0th bit (LSB) || | || 7th bit (MSB) || 6th bit || 5th bit || 4th bit || 3rd bit || 2nd bit || 1st bit || 0th bit (LSB) || | ||
+ | |} | ||
= Bit masking = | = Bit masking = | ||
− | In addition to these three instructions there are 3 more instructions that mess with bits at the byte level. They are [[ | + | In addition to these three instructions there are 3 more instructions that mess with bits at the byte level. They are [[Z80:Opcodes:AND|bitwise AND]], [[Z80:Opcodes:OR|bitwise OR]], and [[Z80:Opcodes:XOR|bitwise XOR]]. |
== Bitwise AND == | == Bitwise AND == | ||
− | For a minute, we're going back to | + | For a minute, we're going back to English class. Remember that ''School House Rock'' song "Conjunction junction, what's your function" ? Well, when they got to AND, they tell you that AND means that both statements are true. So, bitwise [[Z80:Opcodes:AND|AND]] compares two bits and sets the resulting bit if both bits are set, and resets it if either one is reset. Here's a table to demonstrate the possible combinations two bits being compared and the results using bitwise [[Z80:Opcodes:AND|AND]]. |
{| class="wikitable" | {| class="wikitable" | ||
|+ Bitwise AND Table | |+ Bitwise AND Table | ||
− | + | ! bit 1!! bit 2!! result | |
|- | |- | ||
− | + | | 0|| 0|| 0 | |
|- | |- | ||
− | + | | 0|| 1|| 0 | |
|- | |- | ||
− | + | | 1|| 0|| 0 | |
|- | |- | ||
− | + | | 1|| 1|| 1 | |
|} | |} | ||
− | Now, instead of only operating on 2 bits, the z80 bitwise [[ | + | Now, instead of only operating on 2 bits, the z80 bitwise [[Z80:Opcodes:AND|AND]] instruction operates on the byte level. In essence, it does this operation to every corresponding bit between two bytes. So, let's practice bitwise [[Z80:Opcodes:AND|AND]]ing bytes. |
'''Byte1:''' %11001111<br /> | '''Byte1:''' %11001111<br /> | ||
Line 96: | Line 98: | ||
== Bitwise OR == | == Bitwise OR == | ||
− | Bitwise [[ | + | Bitwise [[Z80:Opcodes:OR|OR]] isn't much different from bitwise [[Z80:Opcodes:AND|AND]]. In the same ''School House Rock'' song, they describe OR as being a choice between two things, but you can only choose one. In programming, we tweak this a little bit and define bitwise [[Z80:Opcodes:OR|OR]] as if either bit is set, then the resulting bit will be set. Again, here's a table of possible combinations: |
{| class="wikitable" | {| class="wikitable" | ||
|+ Bitwise OR Table | |+ Bitwise OR Table | ||
− | + | ! bit 1!! bit 2!! result | |
|- | |- | ||
− | + | | 0|| 0|| 0 | |
|- | |- | ||
− | + | | 0|| 1|| 1 | |
|- | |- | ||
− | + | | 1|| 0|| 1 | |
|- | |- | ||
− | + | | 1|| 1|| 1 | |
|} | |} | ||
== Bitwise XOR == | == Bitwise XOR == | ||
− | So what is bitwise [[ | + | So what is bitwise [[Z80:Opcodes:XOR|XOR]] then? the "X" in [[Z80:Opcodes:XOR|XOR]] stands for exclusive. This means that only one can be true, or else the whole statement is false. In other words, two trues = false. |
{| class="wikitable" | {| class="wikitable" | ||
− | + | ! bit 1!! bit 2!! result | |
|- | |- | ||
− | + | | 0|| 0|| 0 | |
|- | |- | ||
− | + | | 0|| 1|| 1 | |
|- | |- | ||
− | + | | 1|| 0|| 1 | |
|- | |- | ||
− | + | | 1|| 1|| 0 | |
|} | |} | ||
Line 133: | Line 135: | ||
=== Setting bits === | === Setting bits === | ||
− | Let's suppose that for some reason you want a particular bit in a byte to be set. How would you do this? Use the [[ | + | Let's suppose that for some reason you want a particular bit in a byte to be set. How would you do this? Use the [[Z80:Opcodes:SET|SET]] instruction, right? But what if you wanted many bits to be set? You could keep using the [[Z80:Opcodes:SET|SET]] instruction, but there's a better way. To make sure that a bit will be set, just use Bitwise [[Z80:Opcodes:OR|OR]]. This works because if either one of the corresponding bits is set, then the result is set. So, we use a "1" for our operand and it's set! But what about the bits we don't want to alter? What do we do about them? We would use a "0" for the bits we don't want to be changed, right? This is because if the other bit is "1", then the result will be "1", and if it is "0", then the result would be "0", meaning that no matter what you do, the bit will not change. |
− | |||
ld b,%11101111 ;we want everything except bit 4 to be set | ld b,%11101111 ;we want everything except bit 4 to be set | ||
or b | or b | ||
− | |||
=== Resetting bits === | === Resetting bits === | ||
− | Okay, now suppose you want a bit to be reset. We can use the bitwise [[ | + | Okay, now suppose you want a bit to be reset. We can use the bitwise [[Z80:Opcodes:AND|AND]] instruction. If we want the bit to be reset, then put in a "0" for the corresponding bit and no matter what the resulting bit will be reset. For the bits you want to leave alone, you would use a "1". |
− | |||
ld b,%00101010 ;reset bits 0, 2, 4, 6, and 7 | ld b,%00101010 ;reset bits 0, 2, 4, 6, and 7 | ||
and b | and b | ||
− | |||
=== Switching bits === | === Switching bits === | ||
− | So, what else can we do with the bits? The last option is to switch them, or have the opposite of the bit be the result. For this, we'll use the bitwise [[ | + | So, what else can we do with the bits? The last option is to switch them, or have the opposite of the bit be the result. For this, we'll use the bitwise [[Z80:Opcodes:XOR|xor]] instruction. Hopefully you can see that if you input a "1", it will switch the bit and a "0" will leave the bit alone. |
− | |||
ld b,%00001111 ;switch the first 4 bits | ld b,%00001111 ;switch the first 4 bits | ||
xor b | xor b | ||
− | |||
== Uses of Bitmasking == | == Uses of Bitmasking == | ||
Line 164: | Line 160: | ||
=== 16-bit counters === | === 16-bit counters === | ||
− | Unfortunately, there aren't any 16-bit comparison operations. So instead, we have to come up with our own method. We could load both bytes into the accumulator and check to see if they are both zero, but there is a better way. What you do is load one of the two values into the accumulator, and the bitwise [[ | + | Unfortunately, there aren't any 16-bit comparison operations. So instead, we have to come up with our own method. We could load both bytes into the accumulator and check to see if they are both zero, but there is a better way. What you do is load one of the two values into the accumulator, and the bitwise [[Z80:Opcodes:OR|OR]] the other byte with the accumulator. This way, if any bit was set, the resulting accumulator would not equal zero, so you would continue to decrease your counter. |
Line 180: | Line 176: | ||
==== Comparing if the accumulator is zero ==== | ==== Comparing if the accumulator is zero ==== | ||
− | A simple [[ | + | A simple [[Z80:Opcodes:OR|OR]] A will tell you if the accumulator is zero. It's both smaller and faster (1 byte smaller and 3 clock cycles faster). Note that it does effect P/V differently ([[Z80:Opcodes:CP|CP]] 0 would alter P/V to detect overflow, but [[Z80:Opcodes:OR|OR]] A would modify P/V to detect parity). |
==== Setting the accumulator to zero ==== | ==== Setting the accumulator to zero ==== | ||
− | [[ | + | [[Z80:Opcodes:XOR|XOR]] A is a much faster and smaller instruction than [[Z80:Opcodes:LD|LD]] A,0 (1 byte, 3 clock cycles). Hopefully you can see why [[Z80:Opcodes:XOR|XOR]] A does set the accumulator to zero. Note that the [[Z80:Opcodes:XOR|XOR]] does effect the flags, while [[Z80:Opcodes:LD|LD]] preserves them. |
=== XOR'd Sprites === | === XOR'd Sprites === | ||
− | This is an [[ | + | This is an [[Z80:Sprites|advanced topic]] that will be discussed later, but the basic idea is you want to draw a picture, but you don't want to erase/destroy what is already there. If you haven't noticed yet, [[Z80:Opcodes:XOR|XOR]]'ing a byte by the same byte twice will return the byte to its original state (inverse of an inverse is the original). |
There are other uses of bitmasking, but they are usually code specific (mostly for optimizing). | There are other uses of bitmasking, but they are usually code specific (mostly for optimizing). | ||
Line 198: | Line 194: | ||
1. Explain why AND %11111111 doesn't do anything useful | 1. Explain why AND %11111111 doesn't do anything useful | ||
− | 2. Create a simple program that will set and reset the z flag using only the bitwise instructions ([[ | + | 2. Create a simple program that will set and reset the z flag using only the bitwise instructions ([[Z80:Opcodes:SET|SET]], [[Z80:Opcodes:RES|RES]], [[Z80:Opcodes:BIT|BIT]], [[Z80:Opcodes:AND|AND]], [[Z80:Opcodes:OR|OR]], [[Z80:Opcodes:XOR|XOR]]). |
3. Explain why there is no "XAND" (exclusive and). | 3. Explain why there is no "XAND" (exclusive and). | ||
Line 227: | Line 223: | ||
− | For answers, see [[ | + | For answers, see [[Z80:Answers 4|here]]. |
= Conclusion = | = Conclusion = | ||
Line 234: | Line 230: | ||
{{lowercase}} | {{lowercase}} | ||
− | [[Category: | + | [[Category:Z80 Assembly]] |
− | [[Category: | + | [[Category:Z80 Heaven]] |
Latest revision as of 08:02, 6 February 2016
Contents
Existing Tutorials
- Sigma's Learn Assembly in 28 Days, Day 4.
- Sigma's Learn Assembly in 28 Days, Day 8.
Introduction
A flag is a bit. That's all a flag is, and that's all a flag ever will be. If a flag is set (1), then the flag is raised. If the flag is reset (0), the flag is lowered/not raised. So how can one bit be of any use to us if it can only hold two values? Because there are things that can only be on or off. Why waste a byte to store something that only takes one bit? This way you can compress 8 flags into one byte, and save as much room as possible. There are two kinds of flags: the F register for flags, and a chunk of memory defined as Flags used by the OS.
F Register
The Flags register (F) is used specifically for flags. It can be used to test certain items concerning the instruction that was last used. Here is a breakdown of all the flags in the F register:
- 7th bit Signed flag (S). Determines whether the accumulator ended up positive (1), or negative (0). This flag assumes that the accumulator is signed.
- 6th bit Zero Flag (Z). Determines whether the accumulator is zero or not.
- 5th bit The 5th bit of the last 8-bit instruction that altered flags.
- 4th bit Half-carry (H). Is set when the least-significant nibble overflows.
- 3th bit The 3rd bit of the last 8-bit instruction that altered flags.
- 2nd bit Parity/Overflow (P/V). Either holds the results of parity or overflow. It's value depends on the instruction used. Is used for signed integers.
- 1st bit Add/Subtract (N). Determines what the last instruction used on the accumulator was. If it was add, the bit is reset (0). If it was subtract, the bit is set (1).
- 0th bit Carry (C). Determines if there was an overflow or not. Note that it checks for unsigned values. The carry flag is also set if a subtraction results in a negative number.
Remember in Control Structures how we used the CP instruction? This instruction subtracts the operand from the accumulator without changing the value of the accumulator. However, it did something else, also. It modified the flags by their definition. That way, when we checked if the flag was zero (exactly identical), we knew what the other value was.
The same applies to the jump instructions.
cp kUp jp z,mUp
However, in the case of jump instructions, instead of modifying the flags, we are checking its value. Then depending on whether it's set or reset, the action will be performed. You can do this with any of the flags, but they may not be useful because not all flags are modified by every instruction.
cp 5 jp n,isOk ;useless, cp doesn't modify the add/subtract flag
System Flags
In addition to the flags register, there is a chunk of memory that is used by the OS specifically for various things. We can use these flags to our advantage since most ROM calls use the flags in some way or another. For a list of system flags you can use, see here.
To access system flags, use a IY offset.
set textInverse,(IY+TextFlags) res trigDeg,(IY+trigFlags) bit appAutoScroll,(IY+appFlags)
Bitwise instructions
Now that we know what flags are, how do we use them? As seen in the code above, there are 3 bitwise instructions that can be used on flags: SET, RES, and BIT.
- SET: Sets the bit (bit=1).
- RES: Resets the bit (bit=0).
- BIT: Checks if a bit is one. Result is returned to the zero flag. If set, Z=0. If reset, z=1.
Bitwise Instructions Elsewhere
Possible the most powerful instructions in the z80 arsenal, bitwise instructions allow you not only to change flags, but you can also modify any byte.
set 0,a res 5,(hl)
So, which one is the zeroth bit or fifth bit? The least significant bit (LSB, or 0th bit) is always the bit furthest right and the most significant bit (MSB, or 7th bit) is the bit furthest to the left.
7th bit (MSB) | 6th bit | 5th bit | 4th bit | 3rd bit | 2nd bit | 1st bit | 0th bit (LSB) |
Bit masking
In addition to these three instructions there are 3 more instructions that mess with bits at the byte level. They are bitwise AND, bitwise OR, and bitwise XOR.
Bitwise AND
For a minute, we're going back to English class. Remember that School House Rock song "Conjunction junction, what's your function" ? Well, when they got to AND, they tell you that AND means that both statements are true. So, bitwise AND compares two bits and sets the resulting bit if both bits are set, and resets it if either one is reset. Here's a table to demonstrate the possible combinations two bits being compared and the results using bitwise AND.
bit 1 | bit 2 | result |
---|---|---|
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
Now, instead of only operating on 2 bits, the z80 bitwise AND instruction operates on the byte level. In essence, it does this operation to every corresponding bit between two bytes. So, let's practice bitwise ANDing bytes.
Byte1: %11001111
Byte2: %01011101
Result: %01001101
Bitwise OR
Bitwise OR isn't much different from bitwise AND. In the same School House Rock song, they describe OR as being a choice between two things, but you can only choose one. In programming, we tweak this a little bit and define bitwise OR as if either bit is set, then the resulting bit will be set. Again, here's a table of possible combinations:
bit 1 | bit 2 | result |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 1 |
Bitwise XOR
So what is bitwise XOR then? the "X" in XOR stands for exclusive. This means that only one can be true, or else the whole statement is false. In other words, two trues = false.
bit 1 | bit 2 | result |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
Bitmasking Basics
So now that you know what the bitwise instructions are, let's learn what bitmasking is. Bitmasking is the process of altering the byte in such a way that the resulting byte will provide some useful information. In other words, you're hiding useless bits in that byte. So, let's get down to bitmasking.
Setting bits
Let's suppose that for some reason you want a particular bit in a byte to be set. How would you do this? Use the SET instruction, right? But what if you wanted many bits to be set? You could keep using the SET instruction, but there's a better way. To make sure that a bit will be set, just use Bitwise OR. This works because if either one of the corresponding bits is set, then the result is set. So, we use a "1" for our operand and it's set! But what about the bits we don't want to alter? What do we do about them? We would use a "0" for the bits we don't want to be changed, right? This is because if the other bit is "1", then the result will be "1", and if it is "0", then the result would be "0", meaning that no matter what you do, the bit will not change.
ld b,%11101111 ;we want everything except bit 4 to be set or b
Resetting bits
Okay, now suppose you want a bit to be reset. We can use the bitwise AND instruction. If we want the bit to be reset, then put in a "0" for the corresponding bit and no matter what the resulting bit will be reset. For the bits you want to leave alone, you would use a "1".
ld b,%00101010 ;reset bits 0, 2, 4, 6, and 7 and b
Switching bits
So, what else can we do with the bits? The last option is to switch them, or have the opposite of the bit be the result. For this, we'll use the bitwise xor instruction. Hopefully you can see that if you input a "1", it will switch the bit and a "0" will leave the bit alone.
ld b,%00001111 ;switch the first 4 bits xor b
Uses of Bitmasking
So, now we know how to bitmask, how do we use it? There must be some use for bitmasking! Lo and behold, there are many uses for bitmasking. Here are a few common ones:
16-bit counters
Unfortunately, there aren't any 16-bit comparison operations. So instead, we have to come up with our own method. We could load both bytes into the accumulator and check to see if they are both zero, but there is a better way. What you do is load one of the two values into the accumulator, and the bitwise OR the other byte with the accumulator. This way, if any bit was set, the resulting accumulator would not equal zero, so you would continue to decrease your counter.
Loop: DEC BC ; Decrease the counter LD A, B ; Load one byte of the counter into the accumulator OR C ; Bitwise OR with the other byte JR NZ, Loop ; If Z is reset then neither B or C was zero, so repeat
Simple Optimizations
There are a few simple optimizations that you can do with the bitwise instructions. They are either smaller or faster than they "traditional" way of doing it.
Comparing if the accumulator is zero
A simple OR A will tell you if the accumulator is zero. It's both smaller and faster (1 byte smaller and 3 clock cycles faster). Note that it does effect P/V differently (CP 0 would alter P/V to detect overflow, but OR A would modify P/V to detect parity).
Setting the accumulator to zero
XOR A is a much faster and smaller instruction than LD A,0 (1 byte, 3 clock cycles). Hopefully you can see why XOR A does set the accumulator to zero. Note that the XOR does effect the flags, while LD preserves them.
XOR'd Sprites
This is an advanced topic that will be discussed later, but the basic idea is you want to draw a picture, but you don't want to erase/destroy what is already there. If you haven't noticed yet, XOR'ing a byte by the same byte twice will return the byte to its original state (inverse of an inverse is the original).
There are other uses of bitmasking, but they are usually code specific (mostly for optimizing).
Review
Now that you read through all that stuff, time to see if you remember/understand it.
1. Explain why AND %11111111 doesn't do anything useful
2. Create a simple program that will set and reset the z flag using only the bitwise instructions (SET, RES, BIT, AND, OR, XOR).
3. Explain why there is no "XAND" (exclusive and).
Identify the Errors
Find the error(s), if any, in each piece of code. They can either be run-time errors or logic errors.
4.
ld b,$FF ;let's reset all the bits and b
5.
set OnInterrupt,(IY+OnFlags) ;reset the OnInterrupt flag
6.
loop: ;... ;Operations that do something with z flag. We want to keep looping if z flag is set ;... xor a ;set the accumulator to zero jr z,loop
For answers, see here.
Conclusion
Bits are everything when programming on this low of a level. Use these instructions correctly, and it will enhance your programs beyond belief. Use them incorrectly, and you just might cause a RAM reset.