Math Basics Part 3 - Bit Manipulation
Bit Manipulation - but why?
I promised it is not hard to control an individual or groups of Bits in Machine Language and I will deliver now. First let me explain why you even want to manipulate each and every Bit - you could after all just write any 8-Bit value into a Byte without caring about each affected Bit individually.
You have learned that the Commodore C64 uses a principle called Memory-Mapped I/O. Many features in the different chips are turned on and off by working with Bits some memory location. Often we call this working with Registers when a specific Byte is more than just a container for some value from 0-255 but in fact the combination of high and low set Bits is what matters. In the C64 memory from $D000 - $DFFF are plenty of those registers which give us for example direct access to features in the VIC-II and SID. You could imagine those important memory locations to be DIP-Switches and usually you only want to turn on or turn off individual features but keep the other settings in a DIP Switch untouched..
Turning the lights on and off - a Masquerade Play
Let's look at the VIC-II, our video controller chip. It uses 46 dedicated memory locations starting at $D000 - some actually hold a traditional value, e.g. the y-coordinate of Sprite#0 in $D001, others are indeed used as DIP-Switches where each Bit have a different responsibility. Those registers need to be handled with extra care when writing to them.
We could of course read the current value of our Register of interest, do some calculation to determine the new value considering our changes and write back to the memory location. But this is tedious and there is much easier way - welcome to the world of logical operators!
You probably heard of Boolean operators before and if you have not worked with them yet they might still have a touch of mystery to them. The machine language instructions OR, AND and EOR are the Boolean operators the 6502 CPU supports. The negating NOT operator is not specifically available but can be simulated.
But let's first demystify Boolean operations in the first place. What are we actually planing to do? As pointed out, especially when working with features of the chips we often want to set one or a group of Bits in a register to low or high. For this there exists the principle of AND'ing or OR'ing the register with a 8-Bit mask which includes the changes we want to apply to that register. Whether AND or OR is the right instruction to use depends on whether we want set Bits high or low. in the process After looking at the examples there will be clarity.
Setting individual Bits high or low with AND and OR
The memory locations $D01D and $D017 determine if any of the C64 sprites are stretched horizontally or vertically - or in both directions. Each Bit in either register correspondents to one of the 8 hardware sprites the C64 can produce at a time, that is Bit#0 to Bit7 correspondent to Sprite#0 to Sprite#7.
Assume we want to stretch the third and the last Sprite horizontally. Also we noticed that the Sprite#0 is vertically stretched but we want to reset it to it's normal height. We don't want to change any other of the sprites at this time.
We start with horizontal stretching - that property is owned by the 8 Bits in $D01D. We need to set Bit#2 and Bit#7 high to affect Sprite#2 and Sprite#7. We could just store the Decimal value 132 or Hex $84 into the Register as that value represents %10000100 - however, what if some sprites are already stretched? If you look at the example value of our $D01D register, this is exactly the case - the first hardware sprite represented by Bit#0 is already stretched. If we would write $84 into the register, not only Sprite#2 and Sprite#7 but also Sprite#0 would have been affected. This is not what we aimed for! To manipulate only specific Bits we "configure" a mask of Bits to be set and use it the OR operation against the register. Let's try that!
The rule of OR is that everything which is Zero in your Bit mask will not affect anything. If a Bit in the mask is set high than the target Bytes Bit at the corresponding position will be also set high no matter what the original value has been.
The instruction to do a OR operation in machine language is ORA, that is because all instructions in assembler have three letters. We load the content of $D01D, OR it against %10000100 and then store back the result. Storing the result is important because logical operations do not directly change the affected memory location bit put the result into the Accumulator.
As expected only what we have set high in our mask has been considered in the result. All other Bits in the register were not touched.
Now we want to see how setting individual Bits to low works. For this the AND instruction is used. This time Ones in the Bit mask define the Bits we do not want to change and Zeroes in the mask indicate the Bits we like to set low.
Using AND is really complement to using OR. In this example we changed only the last Bit since we wanted to remove the vertical stretch property of the first Sprite only. In this particular case just writing $01 or %00000000 to the register would have led to the same outcome as we did by OR'ing, however if another Sprite was already stretched by one of the other Bits in $D017 we would have overwritten that property.
Remember: You don't always know what is happening in those registers - if it is a multi-purpose register you are always safer to use logical operations to only work on the Bits you need to manipulate.
Flip some Bit with EOR
The Boolean Operator EOR ("Exclusive OR") is helpful whenever you want to flip some Bits in a Byte without caring what the original Bit value is. For example, each Character Set in the Character Generator ROM consists of two Sets with 128 Characters each where the second Set is the reversed version - or negative image - of the former. The process to reverse a standard character is to add Decimal 128, and vice-versa to change a reversed Character back to normal, you would subtract Decimal 128.
Since you memorized all Decimal values of each position in a Byte by now, you know that from a Byte perspective the Decimal number 128 represents the value of the 8th Bit in a Byte. And indeed the reversing of any Character within a Character Set is a matter of the 8th Bit set to high or to low. So if you want to change between standard and negative image of a character, for example in a menu screen where you want to highlight options, you just EOR the characters in question with %10000000 (or $80 or Decimal 128).
Since EOR'ing the same Bit pattern against a value twice results to the original value, EOR is often used for encrypting or obfuscating data. The Bit mask to decrypt is then stored somewhere less obvious in the code or even again EOR'ed against another Bit pattern, etc.
This is the end of an introduction into Math basics on Commodore C64. There are more advanced topics like dealing with Floats or Rotating/Shifting Bits but this will be covered at another time.