Commodore 64 Programming #2: Intro to 6502 microprocessor programming


Update: All needed files for this tutorial can be found in the GitHub repo linked in the bottom of this page.

Now that I started writing about C-64 programming, I decided to write one more article about it. If you want more, please comment so I know if this is an interesting topic.

This article is all about writing code for the heart of the Commodore 64, the 6502 microprocessor. It’s function is to control the C-64, so you can think of it as the C-64s brain.

You control the brain by giving it instructions. These instructions are given to the processor by writing the 6502 machine language.

In the previous article about C-64 we created a simple program that changed the background of the border and the main screen to a lot of different random colors. In this tutorial, we are going more deep into the general 6502 microprocessor language.

Programming the 6502 Microprocessor
The 6502 microprocessor language consists of many different instructions and commands, that all work together to give functionality and graphics to your own programs. You can move data between registers (a register is basically a container(variable) where you can store data), and execute commands. You can move data into registers, and from registers to memory locations.

We got three registers in the 6502 that can be used to move data.

Register Decimal Hex Binary
A 0-255 00 – FF 00000000 -11111111
X 0-255 00 – FF 00000000 -11111111
Y 0-255 00 – FF 00000000 –11111111

Table 1 – 6502 registers

In table 1, we can see the different registers. We got the A register (accumulator), and the X and Y registers. Each of them can hold one byte (0-255 in decimal, 00-FF in hex and 00000000-11111111 in binary).

To move data to/from a register, we can use various move-instructions:

Instruction Function
STA Move the byte in A into a memory location
LDA Stores a byte into A
STX Move the byte in X into a memory location
LDX Stores a byte into X
STY Move the byte in Y into a memory location
LDY Stores a byte into Y

Let’s write a program that changes the background color of both the border and the main screen into a specific color.

First we will need to tell DASM that we are writing a program for the 6502 processor, and that our program should start in the memory located at $1000.

    processor   6502
org    $1000

Next we create the loop that changes the color of our border/main screen into a cyan color. First we create the loop label, and load the Accumulator register with the value of 3. If you write a hex number with the # symbol in front of it, it will load the Accumulator (A) with the hex number as a value. If you load it without the # symbol, it will load A with the content of the memory location.
loop:    lda #$03

Next, we will move the value in the A register into the register that holds the color for the background/main screen.
sta $d021
sta $d020

And then we jump back to the loop label, so the program doesn’t end in a split second.
jmp loop

The complete listing of the program should be like this:
Listing 2.1 – Change screen to a given color.

    processor   6502
org    $1000

loop:    lda #$03
sta $d021
sta $d020
jmp loop

Now, compile this code by browsing to the source code file and writing the command
”dasm example2.asm –oexample2.prg”. Then load this into the emulator by writing the command “x64 example2.prg”.

Now that the program is loaded, run it from the emulator by writing “SYS 4096” and hit [Enter].

The result should be like this:

Pretty nice, right?

Next, let’s do some more work with moving data between the registers.

Instead of using the A register, you could have used the X or Y register instead. So in all, you can use all three registers to move or copy bytes between registers and memory locations.

Relative addressing
Let’s put the X-register to use. Let’s create a new program that displays the exact same result as the example above, but instead we use relative addressing.

Listing 2.2 – Relative addressing

    processor   6502
org    $1000

loop:    ldx #$20
lda #$03
sta $d000,X
sta $d001,X
jmp loop

As you can see, the code for this program is quite different, but the result is the same as above. First we store the value of $20 in the X register, and the value of $03 in the A register (cyan). Then we copy the value of A (color $03) into the memory location $d000+X that equals $d000+$20 (=$d020, the border color), and then we copy A into $d001+$20 (=$d021, the main screen). The result is the same as above, but we use relative addressing when storing data into the memory.

We can also move data between registers by using instructions named TXA, TYA, TAX and TAY. TXA transfers the data in X to A, TAX moves the data in A to X and so on. Only the register that the data is moved to will be affected by the transfer.

We have seen that the INC command can increase the value in a memory location by 1 (same as memorylocation += 1, or memorylocation = memorylocation + 1). You can also decrease it using DEC. It is also possible to increase and decrease the X and Y registers by using INX, INY, DEX, DEY.

In the next example, we are making a program that colors the border with the cyan color (#$03) and then colors the main screen with the cyan color + 1 (#$04) = purple.

Listing 2.3 – Increase/decrease registers

    processor   6502
org    $1000

loop:    ldy #$03
sty $d020
sty $d021
jmp loop

In this example, we simply load #$03 into the Y register, and store in into the border memory location $d020. Then we increase Y = #$03 + #$01 = #$04 = the code for purple, and store that into the main screen memory $d021.

The result can be seen in the image below.


And that’s how far this tutorial goes. Until next time! Smile

Download the source from GitHub:

This entry was posted in Commodore 64. Bookmark the permalink.

18 Responses to Commodore 64 Programming #2: Intro to 6502 microprocessor programming

  1. Dag H. Baardsen says:

    Whoa! Flashbacks from my 6510 and 6502 programming back in the early eighties! Actually, the C64 had the 6510 microprocessor and the VIC-20 had the 6502, but they are both based on the same instruction set.

  2. Pingback: Windows Client Developer roundup 064 for 3/21/2011 - Pete Brown's

  3. marshall says:

    Please continue with more

    I am so thrilled to have discovered this

  4. Dave Gereo says:

    Wow… this brought back some serious hackin memories… I was so entangled in the moment with vivid recollections that I ended up doing some asm coding… it all came back like it was yesterday…

    processor 6502
    org 828

    ; screenloop.asm
    ; puts a lines of char’s across the screen starting from pos 0(upper left) for the
    ; the count of (num); As we print a char the char is incremented for each pos
    ; we move(different char for each position). E.g ABCDEF…

    SCREEN equ $0400 ;start video address (1024)

    lda #1 ;the char were going to print (A)
    ldx #0; ;start counter at at last char position of line

    loop: sta SCREEN,x ; start video address (1024), + x index
    inx ; inc the screen position starting from the right.
    adc #1 ; inc the char were going to print next
    cpx num ; check to see if we printed all the chars we wanted. (num = x?)
    beq myend ; break on zf
    jmp loop ; x != 0 so loop

    sta SCREEN,x ; print last char, since we branched on zero.

    ; variables

    num .BYTE 254 ; the number of chars starting from the start of scrn we
    ; are going to print.

  5. frigo says:

    Cool tutorial! I like it a lot.

    In the table with the move instructions, aren’t they the other way around, like LDA stores a byte into A, and STA copies the byte in A to a given memory location?

  6. Parker says:

    Just a comment:
    in the table, you mixed up the LD. and the ST. instructions and what they do.
    You can delete this comment, if you fixed it 🙂

  7. Parker says:

    What a coincidence 🙂 … i opened the page before frigo commented the same thing … and all this after a month of silence.

  8. digitalerr0r says:

    Thanks for the feedback frigo and Parker! 🙂 Great to get typos and errors fixed so the tutorial won’t be missleading. Thanks again! 🙂

  9. damo says:

    I am sooo damn lost in nostalgia now.

    I feel sad, I miss the days of the c64, back when you and the machine were intimate, rather than diametrically opposed as with any windows os.

    Im bookmarking this ffr.

  10. damo says:

    and Please, write more articles, you would be surprised how many ppl still adore the old 6502.

  11. bik1230 says:

    Ah, the felling of false nostalgia!

  12. Diego says:

    I want more !!!!! TNKS A LOT !!!!! from San Telmo, Buenos Aires, Argentina.

  13. Adam Lemmo says:

    This is really great stuff! All I did in a programming capacity when I was younger with my old c64 was some long winded BASIC programs, and nothing fancy with loops and variables or anything, just line after line of print commands, clearing the screen after each line to simulate animated text effects. I wished I knew how to do this assembler stuff back then! Oh what a different person I’d be now! Again, thanks for posting, this is really great! More people should be so selfless in informing others on topics like this, in simple to understand plain language instructions, as you have!

  14. LucaH says:

    The cmd window Shows Errors. It says, that it doesn’t know jmp, lda, … HELP

  15. Heath says:

    Good tutorial! 🙂 I’m very interested in C64 and NES programming, but I love Python as well. I’m sure the formatting will be butchered with this, but here goes:

    #! /usr/local/bin/python

    processor = “6502”
    code_start = “$1000”
    foreground_color = “$d021”
    background_color = “$d020”

    def initc64():
    s = “\tprocessor\t” + processor + “\n”
    s += “\torg\t” + code_start + “\n\n”
    return s

    def screw_around():
    # s = “\tinc\t” + foreground_color + “\n”
    s = “\tlda\t#$03\n”
    s += “\tsta\t” + foreground_color + “\n”
    s += “\tsta\t” + background_color + “\n”
    return s

    def loop():
    s = “loop:\n”
    s += screw_around()
    s += “\tjmp\tloop\n”
    return s

    def main():
    print initc64() + loop()

    if __name__ == ‘__main__’:

    I just thought I’d share the idea of using Python to format and generate the assembly, possibly for different assemblers (and with a call to os.system(), you could even assemble it).

  16. TD Bauer says:

    So glad I found this page and instruction. I played my ass of back in the 80s and early 90s on the C64 and C128. I miss those days, and regret never getting into the programming side of things. Now here I am, 40 years old, and trying to learn how to make a game on the C64.

  17. OS-92 says:

    for those of us still programming using winvice, it is refreshing that the code still works without jmp loop. kind of odd that the assemblers of the day did not allow label

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.