Dustlayer

Retro means old but cool.

I grew up with the Commodore C64 but was never able to master the machine. I was young, I wanted to play the latest games and let other people do the pioneer work on exploring this incredible hardware. Today I have better skills to catch up on what it takes to code the C64. I will share what I learn along the way. Enjoy the trip to the past!

Whatever you like - Coming to Addressing Modes

Telling the CPU what you want

The 6510 is in a way asking for explicitly. Let's take the command LDA which loads a Byte into our Accumulator Register. Whether you want to use LDA to read the content of a memory address or maybe directly a specific Integer Number or use the operand passed to LDA as a vector to point to another location in memory are all different instructions yet we use the same keyword in our assembler code - LDA. 

For the beginner this can be mind-twisting because you can achieve different things with the same three-letter assembler code. So what is important to understand is not not only what the actual keyword technically does but how we ask the keyword to execute in a specific way. Depending on that, the CPU will take different routes and internally encode our keyword internally into a different Byte value. 

The 6510 knows 13 Addressing Modes and most of them are actually straight-forward. The point of choosing the correct addressing mode is to deal with locations or values in RAM and ROM where we usually are concerned about arranging Bits and Bytes around. All CPU instructions are either one, two or three Bytes in length. 

Single-Byte Instructions cannot reference an address or value, they are explicit in themselves. A command like TXA transfers the content of the X-Register into the Accumulator - you cannot do anything different with the command. Two-Byte Instructions come with an additional Byte which can be treated as a plain value or Memory Location depending on which Addressing Mode we use. Finally Three-Byte Instructions are not much different their Two-Byte long sibling but come with two Bytes of Data, e.g. to load and store 16-Bit Addresses. Remember the C64 is an 8-Bit Machine but has a 16-Bit Address Bus otherwise we would not be able to reach any location outside $FF. 

So all those 6510 instructions can be used with one or more addressing modes to further specify what we actually want to achieve. Let's run down the different modes with examples. 

13 Paths to Success - The 6510 Addressing Modes

Accumulator Addressing correspond to all commands where the Accumulator is implied as operand, so no address needs to be specified

Implied Addressing is actually not a real addressing mode but the destination is already implied within the command. For example Return from Subroutine (RTS) is a command which does not come with any further information - it is implicit.

Immediate Addressing s used to work with a value rather than a memory location, e.g. if you want to load the decimal value 15 into the accumulator you would use LDA #$0F . Immediate Addressing can be identified by the Hashpount (#). Whenever you use this Prefix you always deal with the actual value but not with a Memory Location.

Relative Addressing is a mode used by Branch instructions like BNE, BEQ etc. They work with a signed 8-Bit long offset, that means that Bit#7 is used to indiciate whether the offset value is negative or positive. When Bit#7 is set, the value is considered to be negative. In your day-to-day programming you often use Branch instructions with labels so the assembler takes care of calculating the offset to the target  label of your branch instruction. Just keep in mind that the maximum range you can branch in your code is 127 instructions forward or 128 backwards. Branch instructions are faster than the JMP instruction so avoid using JMP in favor of Branching if possible.

Absolute Addressing use a full 16-Bit address to reference to a target memory location.

Absolute Indexed by X Addressing means that the address in question is made up from the 16-Bit location plus the content of the X Register. The instruction LDA $0400,x will load the value from the location $0405 when the X Register holds the Value #$05.   

Absolute Indexed by Y Addressing is the exact same as the mode above but used in conjunction with the Y Register.

Absolute Indirect Addressing is only used by the JMP instruction and it may sound a bit weird at first. What it does is to jump to a specified 16-Bit memory Location and considers the found value in that location to be the least significant Byte of another 16-Bit address which is combined with the following Byte to make up the real target address of the Jump instruction. For example the Reset vector JMP ($FFFC) first jumps to a location in the standardized Commodore Jump Table which on the C64 has the value $E2 followed by the value $FC. This address is then used for the actual Jump. The C64 Reset Routine is therefor located at $FCE2 (64738) which might be different on other Commodore Machines. So when you develop cross-platform, say for VIC-20 and C64 you can use JMP ($FFFC)  on both machines though the actual location of the routine differs. That saved some time when you did conversion jobs in the past across Commodore Computers.

Zeropage Addressing  is actually not a real addressing mode of the Chip  per se. It is basically the same as the Absolute Addressing Mode but works with the Zeropage. The Zeropage (Page 0) covers Memory Locations $00 - $FF. The cool thing about using Zeropage Addressing is that the Chip ignores the most significant Byte as it is zero anyways so this saves a Byte which shortens programs and increases execution speed. That's also the reason why the 256 Bytes of the Zeropage are usually in great demand of C64 programs. If you use BASIC in addition to your Machine Language routines it is crucial to consider that lots of Zeropage addresses are already used by the BASIC Interpreter.  Zeropage Addressing works likewise as Absolute Addressing so Zeropage Indexed by X, Zeropage Indexed by Y and of course just the Standard Zeropage Adressing in the fashion LDA $35 are possible.

Zeropage Indirect Indexed Addressing is a very popular solution to address any location in the C64 memory with just two Bytes. The idea is that you use two locations in Zeropage to store a 16-Bit address - as usual in low/high order. You put the location holding the least significant Byte in parentheses and use it in conjunction with the Y register to ultimately address any location in C64 Memory. 

Let's say we want to clear the screen using a a loop by using Zeropage Indirect Indexed Addressing. For that  we store the value $00 and $04 in $FB/$FC since our Screen RAM start at $0400. We can now use the command LDA ($FB),Y to address the first 256 locations from $0400 to $04FF with a two-byte instruction. Once Y becomes zero again, all we need to do is to increment the value in $FC and start over to address $0500 to $05FF and so forth. Here comes the example code.

Zeropage Indexed Indirect Addressing is a rarely used mode.  the vector is this time chosen by adding the value in the X Register to the given 16-Bit Zero Page address. For example when the X-Register holds the value #$01 then LDA ($FB,X) will address the location it finds in $FC/FD since X is added to the least significant portion of the actual 16-Bit address before the vector is used. Again this mode is not often used.

Last Hint: A common source of failure

My personal most popular mistake is to forget the Hashpound ("#") when I want to do Immediate Addressing. This is a mistake that is not always immediately (ha!) obvious when you run the code because whether you do LDA $01 or LDA #$01 might not make a visible difference at first when the content of the memory location $01 actually has the value One at the time of execution. This in particular occurs to me when I use symbols, so remember LDA delay_counter loads the value by the means of Absolute Addressing while LDA #delay_counter on the other hand loads just the plain value referenced via Immediate Addressing.

If something does not seem to make sense when loading and storing values, first check if you really do immediate addressing where you planned it to do. 

-act

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..  

I an memory mapped I/O world a byte can be seen as standard DIP Switch with each Bit being responsible for a specific task. 

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.

Deleting Bits with AND

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). 

Flipping a Bit in a Byte twice

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.

-act

Math Basics Part 2 - Calculating in Three Systems

Losing the anxiety towards conversion Math

After the Introduction to Numbering Systems we want to dive deeper into how to convert numbers between systems. Luckily as a C64 programmer we are usually concerned about small numbers in the range of 0-65535 or $0000-$FFFF.  This helps us a lot to minimize the Math we need to know.

After this article you will be comfortable to do the following:

  • Binary to Decimal conversion and vice-versa
  • Hex to Decimal conversion and vice-versa
  • Binary to Hex conversion and vice-versa

Binary and Decimal Conversions

Honestly, I don't know any real-life scenario where Binary to Decimal or Decimal to Binary conversions are required for a Machine Language programmer. However, BASIC programmers on the other hand may find it useful. For everyone else it is just cool to be able to quickly convert between the two systems without a calculator nevertheless.

We start with Converting from Binary to Decimal - the example was already briefly brought up in the first part of the Basic Math article.  We have a Binary 8-Bit number %00110101. The Binary System is Base 2 which means that with every position starting on the right of the number the value needs to be multiplied by 2 to the power of the current position. The very first position in a number is considered to be the Zero-Position from the Multiplier perspective. By the way, any Number to the power of Zero results to One.   

Especially for somebody familiar with computers it is easy to memorize all multipliers for an 8-Bit-Number since you encounter them all the time, for example when talking about system memory. When I give you a wakeup call at 3am you should be able to instantly enumerate those: 1,2,4,8,16,32,64,128  - practice!

From here it is straight forward. If a Bit is set we use the Decimal value of the multiplier and add everything up until we have the final Decimal number representing this Binary. It should be easy to follow by looking at graphics again where we converted from %00110101 to Decimal 53.

Converting a Binary to a Decimal number

To convert back from Decimal to Binary format is easy too but you may need to use a piece of paper to do the calculation. We take the result 53 from the calculation before. Now how do we get back to Binary?

You start by dividing the Decimal number by Two. If there is a remainder, the Binary Digit is supposed to be set, if there is no remainer the Binary Digit is Zero. Repeat this process until you cannot divide by Two anymore. Build the resulting Binary number by writing down alls remainders starting with the last one..

Converting 53 to Binary notation

No surprise, the result is %00110101 - we pad the number to be compliant with the 8-Bit notation but whether you would write %00110101 or %110101 does not matter to the actual value.

Hex and Decimal Conversions

Again, in the daily coding routine you don't often convert between Hex or Binary and the Decimal numbering system, yet it is useful to know how it works. We will look at the example from the other Math article again to show the conversion process from a Hexadecimal number to Decimal.

A conversion from Hex to Decimal

The process to convert Hex to Decimal is exactly like with Binary to Decimal. The only difference is that with Binary numbers it is easier to use the multipliers. Since a digit in a Binary Number is either Zero or One you don't have to take a number of times into consideration that you would have to multiply against the Base value of each position. That means that there is a little extra step in Hex sind each position can have a value from 0 to 15 or $00-$0F. In the case of our Hex Number $314 - which you should have encountered in our Episode on Interrupts  - the calculation is like outlined in the image above which should be self-explanatory. 

Let's again take turns and convert the Decimal Number 788 back to Hex! Big surprise - it is also as simple as dividing the Number by the Base which is 16 of course and work with the Remainder exactly like when we did Decimal to Binary conversion.

Converting Decimal Number 788 to Hex

Again we write doen the resulting Remainders starting from the last position and get as expected our Hex number $314 which we pad for convenience to $0314 which is the common way of writing 16-Bit wide memory locations. As you can see converting bigger Decimal numbers to Hex is not as straight forward as with Decimal to Binary where we just have to divide by Two all the time. 

Hexadecimal and Binary Conversions

This couple is actually very important because you will convert all the time between the two. The reason will become obvious a bit later when we talk about Bit manipulation. Fortunately we can use a characteristic relationship of the two numbering systems.

A Byte consists of 8 Bits. If you split a Byte in half - you have two times 4 Bits, also called two Nibbles. One isolated Nibble can hold values from 0-15 or in other words 16 combination.  This is exactly the range of digits a Hex number uses of course. Could it really be that we just need to split any Hex number into it's individual 4-bit Binary equivalents and write those next to each other? Is it that easy? Yes it is!

Conversion from Hex to Binary is very easy.

$D012 converted to Binary results to %1101000000010010 - if this was not easy I don't know what is!

This is a skill set you can use to gain some  credibility in your office or at school. Converting between two non-Base 10 systems back and forth with ease! The conversion back from Binary to Hex is a no-brainer. Basically rewind the diagram above.

-act

Math Basics Part 1 - Know your Numbering Systems

 You don't like Math? Nor Do I, but...

...with Machine Language we use the shortest path to communicate with the C64 hardware. What we do is very low level and the C64 expects that we are comfortable with its way of getting data stored and manipulated. The key to not be too overwhelmed is to start with just the amount of Math required to get around the system - and believe me, it is actually not that much. I will make this as brief as possible.

Three numbering systems to rule the C64

Of course you are aware of using the Decimal numbering system. We use it since the dawn of civilization because human beings have 10 fingers. What you did not learn since Kindergarten though is dealing with the Binary and the Hexadecimal numbering systems. Coding the C64 requires an understanding on when to use which of the three and how to convert from one to the other.

Decimal
As an average C64 user you would probably only deal with Decimal numbers as the BASIC interpreter has no out-of-the-box capability for any other notation. This is one of many flaws in the shipped BASIC interpreter and makes for example Bit manipulation via BASIC unnecessary difficult. Anyways, as a 6502 coder you will rarely encounter Decimal notation but to understand the other two systems, let's refresh our knowledge and look at a Decimal number first.   

The Decimal System is also known as "Base 10" since it is based on 10 digits from 0 to 9. When you count up starting at zero the Decimal system requires to add a 1 to the left of your number after reaching 9 and then restarts counting the units again from zero. 

A Decimal Number dissected

Each digit in a Decimal number has a position. The Decimal Point is our only indicator to know which position is which. So even a number like 25 has a Decimal point which is hidden by a short cut in writing we are used to. The correct notation would in fact be 25.0.

Every position further to the left of a Decimal Point is 10 times bigger, every position further to the right is 10 times smaller. 

Binary
Binary notation is what our C64 understands because it uses electrical signals. The Bit is the smallest information unit and technically a Bit set to One indicates there is a high voltage level and a Bit set to Zero indicates there is a low voltage level from an electric signal. So actually Bits are never "off" - unless you turn off your computer. They are either set to a high or low voltage. By the way there were computers a couple of decades ago which worked with three states (-1, 0, +1) called Ternary systems. They were considered to be superior to the ones based on a Binary numbering system but like with VHS and Betamax - the better product must not necessarily win the market.

Except for the fact that we deal with Zeros and Ones there is no real difference to the explanation on on the Decimal Numbering System, we now work in a "Base 2" system. If we start at the right and go left one position the value of a Number multiplies by 2 instead of by 10. Same goes for the other direction which will divide in half. Look at the example.

8 Bit Binary Number and the Decimal values for each position which are the multiplier for converting to Decimal. Converting to Decimal requires to just add up the values.

This Binary number has 8 positions. Each Position has a value to the Base of 2. If you know the Decimal values represented by each position, then converting a Binary number to Decimal is very simple. Just add up all values where the Binary position is set to One. In the case above 1+4+16+32 adds up to the number 53 which we now know represents %00110101 in decimal notation.  

I chose a number with eight positions not by accident. In In the "8-Bit" world this length represents a Byte. The Commodore C64 has 65536 memory locations where into each we can write one Byte of information. Half of a Byte is also called a Nibble. This additional representation will come handy when dealing with Hexadecimal numbers. 

As you might have noticed already, Binary numbers are usually prefixed with the % sign to not confuse them with Decimal or Hex numbers, e.g. %10 represents the Decimal number 2.

A Nibble and a Byte

Hexadecimal (or short: Hex)

The Hexadecimal numbering system is the one you will deal with most of the time in addition to manipulating individual Bits in a Byte. Since we need 16 different individual digits to represent a so-called "Base 16" number, Hexadecimal borrows in addition to the numbers from 0-9 a few letters from the alphabet, namely A, B, C, D, E and F. Let's look at the Hex Number $314.

Hex to Dec  Conversion follows the same principle as Binary Conversions

If you look further at the conversion table below you will realize something very interesting. To represent a Nibble only a single-digit Hex number is required. Thinking forward, an 8-Bit number can be represented by a two-digit Hex-Number and a 16-Bit number can be written with only a four-digit Hex number.

Again, this is very important: with just a 4 digits wide Hex number we can access any location in the C64 memory as all 65535 locations in memory can be addressed with a Hex Number from $0000 to $FFFF.

This is awesome! It saves us a lot of typing work and working with Hex is especially with bigger numbers or memory locations much easier than using a Binary basis. To not confuse Hex Numbers with Binary or Decimal Numbers, the numbers are prefixed with a $ sign.

Hex/Binary/Decimal Conversion Table. Single-Digit Hex can represent 4 Bits or one Nibble

Since it is so straight-forward to use Hex numbers to represent Binary notations it is the common numbering system we use during development in our editor. $314 is just much easier to remember than %001100010100, is it not? 

All principles of Decimal and Binary systems apply to Hexadecimal of course. 

Summary

  • Decimal (Base 10): the decimal system is sort of only relevant in BASIC as the BASIC interpreter has no out of the box methods to notate to any other Base.  
  • Binary (Base 2): When we manipulate individual Bits or inspect the content of a Byte, we need to deal with Base 2. We do this very often because of the memory-mapped I/O nature of the Commodore C64. Many Bytes are technically used as Dip Switches to do configuration in the I/O chips.  Learning how to manipulate Bits is key to master the machine.
  • Hexadecimal (Base 16): Hex is actually a helper for us to not have to deal with long chains of Zeroes and Ones. Using Hex notation saves us a lot of typing work. In the end, a compiler will translate our Hex numbers in the source code to binary notation though as the C64 does not understand any other system.

-act