One of the most important things before you write any Machine Code for your C64 is knowing what the Registers of the CPU 6510 are, what they do and how you can use them in your programs.
Register | Full Name | Size | Primary Function |
---|---|---|---|
A | Accumulator | 8 Bit | Arithmetic and Logical Operations |
X | Index Register X | 8 Bit | Indirect Indexed or Absolute Indexed Addressing Modes |
Y | Index Register Y | 8 Bit | Indirect Indexed or Absolute Indexed Addressing Modes In conjunction with Accumulator to form 16 Bit values |
PC / IP | Program Counter | 16 Bit | Store Memory Address of currently executing Instruction |
P | Status Register | 8 Bit | Saves the states of the Processor Flags |
The CPU 6510 has essentially the same register set as the CPU 6502 which is its direct predecessor. For your programs you will use the Registers A, X and Y most of the time. When you have to check for a certain status you will look into the Status Register (P). The Program Counter (PC) stores the Memory Address of currently executed instruction. One can manipulate it with Jumps, Breaks and Interrupts.
Now that you know the primary usages of the Registers maybe you want to know how these are applied and what else you can do with them. Read on for a detailed description.
Detailed Functionality of the C64 Registers
Although you can use most of the Registers to store whatever you want in them, as long as it fits in size, they have special purposes you should be aware of. The special purposes of the Registers are summarized in the following paragraphs.
Please note that all code examples are in the C64 Machine Code. You can assemble them with the ACME Assembler in default configuration although other Assemblers may also work.
Accumulator
The Accumulator Register, simply referred to as A in the Machine Code, is primarily used for arithmetic and logical operations. The result of these operations will be stored in this register and many instructions implicitly refer to it. Often times you will find yourself using a mixture between explicit and implicit access to the Accumulator. Look at the following example for simple addition.
1 2 3 4 |
; Example: Simple Addition of 2+3 lda #02 ; load the value 2 into A (explicit usage) add #03 ; add 3 to the value in A (implicit usage) sta $C000 ; store the result in memory address $C000 (explicit usage) |
Index Register X
The Index Register X is used for the Indirect Indexed Addressing Mode and the Indexed Absolute Addressing Mode. In both cases you load a value into the X Register which is then used as an Offset to the given address. This is useful if you want to go through some consecutive addresses in a loop. You just increment X and add it to the given Base Address in each iteration. Look at the following example for a view on Indexed Absolute Addressing Mode with Register X.
1 2 3 4 5 6 |
; Example: Indexed Absolute Addressing Mode with Register X ldx #00 ; Initialize the Index Register X with 0 lda $00A0 ; Load the value at Memory Address $00A0 into Register A sta $C000,X ; Write the value of A into Memory Address $C000 + X = $C000 inx ; Increment the value in X by 1 (is now 1) sta $C000,X ; Write the value of A into Memory Address $C000 + X = $C001 |
The Indirect Indexed Addressing Mode is pretty much the same, with the significant difference that the Base Address is stored in the memory and therefore is accessed indirectly. Please note that addresses are read with LSB first, so if you store $00 at $02 and $42 at $03 and then read and indirect address with ($02), the resulting address would be $4200. The next example illustrates this fact. Also keep in mind that this works only in the Zero Page Addressing Space.
1 2 3 4 5 6 7 |
; Example: Indirect Indexed Addressing Mode with Register X lda #$00 ; Load $00 into the Accumulator sta $02 ; Write $00 into Zero Page Address $02 lda #$42 ; Load $42 into the Accumulator sta $03 ; Write $42 into Zero Page Address $03 ldx #$02 ; Load $02 into the Index Register X lda ($02),X ; Load the value stored in the effective address $4200 + X = $4202 |
The CPU 6510 has a total of 13 Addressing Modes which are not all described here. The goal is to understand how Indexed Addressing with the Index Registers works.
Index Register Y
The usage of the Y Register is basically the same as the X Register, as it has the same addressing modes. However, one can also use the Y in conjunction with the Accumulator in order to do things that need 16 Bit of space. It can either form a Memory Address Location or it can form 16 Bit Signed Values which you need in Floating Point Arithmetic. In that case the low byte of the Address is stored in the Accumulator and the high byte is stored in the Index Register Y, referred to as (A/Y). The example shows the usage as a Address Location in Memory.
1 2 3 4 |
; Example: Use A and Y in conjunction to form a 16Bit Memory Address lda #<$C001 ; Store Low Byte of Address $C001 in Accumulator ldy #>$C001 ; Store High Byte of Address $C001 in Index Register Y ; Address will be used by floating point arithmetic functions |
For an example on how to do Floating Point Arithmetic you can visit Codebase 64 (opens in new tab), as this would go way beyond scope here.
Program Counter PC
The Program Counter is also known as the Instruction Pointer (IP). It is the only 16 Bit Register in the CPU 6510 and it stores the Memory Address of the currently executing instruction on the C64. You can modify the Program Counter with the usage of some instructions like branching or jumping. When the CPU fetches an instruction from the Memory, the value of the Program Counter is issued on the Address Bus and automatically incremented by the CPU.
1 2 3 4 |
; Example: Modify the Program Counter with an instruction nop ; do something useful (or not) jmp $C001 ; jump to $C001 and execute the code which is located there ;; ([$C0|$01] is now stored in the Program Counter) |
Status Register P
The Status Register P is a 8 Bit Register, but its value is not considered as a whole. You’d rather watch each Bit individually. These are then called Flags and therefore the Status Register is also called Flag Register. We will not go into detail about what these flags mean, but rather view how you can read and write them. There are instructions that manipulate the flags, and you can get a flag value with the help of bit masking.
1 2 3 4 5 |
; Example: Clear Carry Flag (Bit 0) of the Status Register P lda #$02 ; Load value 2 into Accumulator clc ; Clear Carry Flag, make sure that Bit 0 of the Status Register is 0 adc #$03 ; Add with carry, make an addition with the value in A ;; The Carry Flag could be modified by the adc instruction |
You can also save the current Status Register on the Stack and retrieve it later with the two Machine Code Commands PHP and PLP.
1 2 3 4 5 |
; Example: Save and Load Register to and from Stack php ; save current status register on the stack jsr .somewhere ; jump somewhere and do something stx $C001 ; save X Register value in $C001 plp ; get status back from stack |
Show Current C64 Register Values
If you run your code in Debug Mode in an IDE like C64 Studio, you have a window where you can see all the Registers with their current values. This could look something like this:
As you go through your code step by step you can see how the instructions affect the values of the different Registers. This is an invaluable help if you want to understand how a program works or if you’re chasing a nasty bug.
C64 Registers used in a Program
If you want to see the usage of some of the Registers in action, you can read my Hello World in the C64 Machine Code Example, for instance.