Hello World in the C64 Machine Code


You’ve surely done it in C, you’ve probably done it in Python, and now you are going to write a complete Hello World Program for the C64 Machine Code.

Here is the Machine Code for a classical Hello World Program. It is written for the ACME Assembler in Default configuration and uses the Registers X and A, starting at Address $0801 which is one after the BASIC starting address.

If you type this in an .asm file, assemble and run it with a C64 (Real or Emulator), it will print HELLO WORLD on the screen. If you are not sure what all these instructions and numbers mean just keep on reading.

Detailed Analysis of the Machine Code

InstructionFull NameWhat it does
bneBranch if Not EqualJumps to given memory address if the Zero Flag is clear
cpxComPare XCompares given value with contents of X Register and sets Zero Flag if the values are equal
inxINcrease XIncrements the numerical content of the X Register by 1
jsrJump to SubRoutineJumps to given memory address to continue Program there
ldaLoaD AccumulatorRetrieves a copy of given RAM or I/O Register and loads it into A (Accumulator) Register
ldxLoaD XRetrieves a copy of given RAM or I/O Register and loads it into X (Index) Register
rtsReTurn from SubroutineReturns from a subroutine that was called with jsr
Used Machine Code Instructions in the Hello World Program

This program has only twelve lines (excluding comment only lines) which is very short, especially for a Machine Code Program. We set up our starting address at $0801 and tell BASIC to execute our Machine Code at memory address 2062. This means that BASIC looks at its own start address for code to execute and we tell it to jump to another place where our program is located.

In C64 Machine Code we need to know the Memory Layout of the C64 because we need to instruct the CPU what to do (hence Instruction) and where to do it (in the Memory).

In one of these Memory Addresses, the Commodore 64 has a built-in subroutine that clears the screen. We can just call the code and don’t have to write it ourselves. This subroutine is stored at the Address $E544 and we tell the assembler to jump there by using the jsr instruction.

The main part of this program is a loop where we use the Index Register X as our counter. First we initialize the value in the Register X with 0 and indicate the start of the loop with the label .write (which could have another name but .write is just fine).

Now for the magic part: In the very last line of our program we created a label called .hello and stored our “HELLO WORLD” text there with the help of the !text macro. Note: This may differ in other Assembler versions but is valid for ACME Assembler in default configuration. The label .hello points to the beginning of the string. What we do in the loop is go through each character step by step.

We load the first character from .hello into the Accumulator Register A, add an offset to it (which is the value we stored in Register X) and then write the character to the screen by using the CHROUT (Character Out) built in subroutine which is stored at the Memory Address $FFD2. In the first run, the value in Register X is zero, so we load and print the first character stored at .hello with an offset of 0. The Character ‘H’ appears on the screen.

OFFSET

012345678910
HELLOWORLD
.hello points to the Memory Address of ‘H’

We increment the value in the X Register by one and compare it to $0B which is hexadecimal for the value 11. Why this number? Because “HELLO WORLD” (including the space) is eleven characters long. If we have printed eleven characters then there is nothing left to print and we can end the program. Otherwise the code jumps back to our label .write and continues. 

In the second iteration the X Register Value would be 1, and so there would be an offset of 1 at the .hello address. This points to the character ‘E’ which is loaded into Register A and also printed out by the CHROUT subroutine.

OFFSET

012345678910
HELLOWORLD
with OFFSET 1, .hello points to the Memory Address of ‘E’

This continues until every character of “HELLO WORLD” is printed and then the program will return to BASIC.

What are C64 Registers

The Commodore 64 has three Registers where the programmer can put values to or read from. These Registers are called A,X and Y and some of them have special purposes. The size of every register is 8 Bit which means that you cannot store a 16 Bit address at once in one of the Registers. 

Registers are part of the CPU and can therefore be accessed very quickly in comparison to memory addresses in the RAM and ROM.

You find my complete overview of all CPU 6510 Registers of the C64 this article.

Hello World – A Line by Line Walkthrough

Here we will go through each line of the C64 Machine Code and dissect its functionality within the program. Please note that empty lines do not count in this example.

You may have noticed that there is a comment in every line of code. This is not necessary although it is really helpful if you come back to your code later and try to figure out what it does. It is considered good practice in any of the assembler type languages

Lines 1-4 are comments and will be ignored by the Assembler. It is only for the humans (including yourself) who will read this code in a few weeks or months from now.

Line 5 sets the starting address of our program. The reserved memory for BASIC programs start at $0800 and we start our program one byte after that at $0801.

Line 6 writes the code directly as hexadecimal instructions. The line would state “1024 SYS 2062” where 1024 is the line number and the command SYS 2062 tells BASIC to execute Assembly Language Code at memory address 2062. Luckily this is where our program is stored.

Line 7 calls the Commodore 64 built-in subroutine for clearing the screen

Line 8 initializes the Register X with the value 0

Line 9 is a label called that we called .write, it is used to indicate the start of the loop

Line 10 loads the character from the memory address where the label .hello points to, plus the offset which is the current value stored in Register X

Line 11 prints the loaded character onto the screen by using the built-in subroutine CHROUT which is stored at $FFD2

Line 12 increments the value in the Register X by one

Line 13 compares the value in the Register X with $0B (=11)

Line 14 jumps to the memory address where .write points to, if the comparison failed so that the Register X value is not equal to 11.

Line 15 is only called if the comparison was successful, the Register X value is equal to 11. Normally rts would jump back to where the last jsr was called, but we did not call an unreturned jsr. In this case rts will return to BASIC and we are done.

Line 16 is the place where the data is stored, in our case the characters for our text.

This was the complete Walkthrough of this litte program.

The Final Result in WinVICE Emulator

Finally here is a screenshot of the results that our little Hello World Program produces on a WinVICE C64 Emulator. Maybe not that impressive, but at least what we wanted.

The result after running the Hello World Program in the C64 WinVICE Emulator

You can find the WinVICE C64 Emulator here (opens external site in new tab).

Your Version of the Hello World Program

Try to print something else, maybe your name or a message to the world. Change the text at the .hello label and also fit the maximum length to the new length of your customized text.

Marco Lieblang

Professional Programmer since 2003, passionate Programmer since the mid 90's. Developing in many languages from C/C++ to Java, C#, Python and some more. And I also may know a bit about Assembly Languages and Retro Systems.

Recent Posts