• Bit 64

  • by jwhoag

Commodore-related project and thoughts I have worked on


C64List 3.00 is here! Some Assembly Required

C64List rings in the new year with a bottle of vintage 3.00, a kazoo, and its own integrated 6510 assembler!



Unfortunately, the kazoo and the bottle were lost in the excitement of watching the 2012 ball drop on TV, but the version 3.00 designation and the integrated assembler survived the obligatory hangover on January 1, and C64List is ready for work in the new year with its arsenal of new tools.

[2011] So there you are writing a BASIC program and one particular routine is really, really slow so you want to write in assembly language to speed things up.  This means you have to haul out the old assembler, load it up, write your first draft, assemble it, and oh there seems to be a bug, so now you have to reload the assembler and re-assemble. Finally got it working correctly and now you have to write a loader for the machine language .prg file so it will be available to call from your BASIC program...

[2012] Woah! Stop! Slow down Nelly! Now there is a much easier way to seamlessly integrate lighting-fast machine language routines into your BASIC programs. Thanks to C64List 3.00's new integrated assembler, your whole program can fit into a single .prg file: BASIC, machine language and all!

Imagine being able to



...and not have to have any extra files to load.  Well, now you can!



C64List now allows you to write your BASIC program, and add your assembly code right into the same source file. C64List automatically positions your code in memory immediately after the BASIC code, and allows you to SYS, POKE, PEEK, etc. using symbolic names so you don't have to worry about relocating your machine language code if your BASIC code expands—C64List automatically handles all the details for you.


Someone once said "An example is worth a thousand words", or was that a picture—ok well anyway, how about a quick example to show how easy it is to add assembly code into a BASIC program? If you want to follow along, just cut and paste the following code snippet into a .txt file and run C64List on it. Of course you'll need to download the latest version of C64List from CommodoreServer first!


print "beginning basic code"

print "now switching to assembly"

sys {sym:subroutine}

print "returned to basic"



screen00 = $0400

screen24 = $0400+$3c0

checker = $ff

ptr1 = $22

ptr1nonZP = $0022

ptr2 = $24

ptr2nonZP = $0024


storage: byte 0



    lda #&lt Screen00

    sta ptr1nonZp ;uses a non zero page store just for the fun of it

    lda #&gt Screen00

    sta ptr1 +1 ;uses a zero-page store

    lda #&lt Screen24

    sta ptr2

    lda #&gt screen24

    sta ptr2nonZP +1;uses a non zp store just for the fun of it


    lda #checker

    ldy #$27


    sta (ptr1),y

    sta (ptr2),y


    bpl l0





Now that you've had a chance to look at some actual code, let's talk about it. This example shows a number of important elements of the C64List assembler, so we will start from the top. As you can see, we are using C64List's number-line-less format so C64List will fill in line numbers for us, starting at 100, and incrementing by 10.

Hearkening back to my previous Bit64 blog post, the {alpha:upper} allows us to be lazy and type in all lower case. C64List will uppercase all the text within quotes so it will be actual text in the program, instead of random looking graphics characters.

Then we have a couple simple lines of BASIC telling us what is going on, followed by the sys to our assembly language routine. Notice the new directive {sym}. This directive allows BASIC to access the symbols from the assembler's symbol table. The name of the symbol here is "subroutine", which is the entry point for our assembly function. When you run C64List on this program, it will automatically locate the address for "subroutine" and replace the {sym} directive in the code with the actual address we are interested in.

The next interesting part of the code is the {asm} directive. This is how you tell C64List that it should now treat the following lines as assembly language. {endasm} does the opposite. Please note that all BASIC code must come before any assembly code.

Now that we are in assembly land, the code looks much different. I'm not going to give a tutorial on assembly language programming here, since that is a bit out of scope from this blog, but I do want to point out a few features of C64List's assembler.

Assigning a symbol is rather straightforward:

    screen00 = $400

does exactly what it looks like it should. Also notice that you can use some simple expressions as the next line illustrates. You may also use decimal values instead of hexadecimal if you wish. Please note that the symbols in the symbol table are completely separate from the BASIC labels.



Also notice how zero page addresses are specified. Values which are given as two (or one) hex digits are taken as zero page. Values with more than two hex digits are taken as non-zero-page values. Alternately, decimal values of one, two, or three digits that are less than 256 are taken as zero page, while any value greater than 256 or larger than 3 digits are taken as non-zero-page. In the example, the symbol ptr1 is defined as a zero page address, while ptr1nonZP is defined as a non-zero-page symbol, even though it is the exact same value. The difference between the two symbols can be seen when an instruction is assembled using these two symbols.

    lda ptr1

will create a two-byte instruction, doing a zero page load accumulator from address $22, while

    lda ptr1nonZP

will create a three-byte instruction that does an absolute load accumulator from address $0022 (the same location).



What? Oh, I mean mathematical expressions.  Those may include the following operators:

  • # immediate (note that this is actually an addressing mode modifier, not an operator)
  • < take the low byte of the expression (this can only be the first operator in the expression)
  • > take the high byte of the expression (this can only be the first operator in the expression)
  • + addition
  • - subtraction
  • & and operation
  • | or operation



The "byte" and "word" pseudo-ops are supported. Note that there is no origin pseudo-op. C64List first tokenizes all the BASIC code, then calculates the end of the BASIC code in memory, and begins assembling immediately after. In this way, as much memory space as possible is preserved. C64List tracks the actual locations and allows BASIC to access them via the symbol table. For example, using the example assembly code, you could POKE {sym:storage},99 to set the byte at that address to 99 from BASIC.




Incidentally, I do plan to add an origin pseudo-op, but there are some issues which must be worked out first. The issue becomes obvious if you attempt to assign a symbol using a label. For example, the following would fail.

    symbol = subroutine

Fortunately, you shouldn't need to do this most of the time anyway, but this occurs because there is a bit of chicken-and-egg syndrome happening due to the space-saving features of C64List that puts the assembly code immediately after the BASIC code. C64List can't know the address of labels until very late in the assembly process, long after all other symbols have been defined. I plan to work around this eventually, but please be advised that using labels in expressions is currently illegal in all 50 states, Canada, Mexico, and most other locations within the Milky Way Galaxy, and will result in blistering error messages.



If you are like most people in the world, you've recently been unable to sleep at night, tossing and turning, wondering what exciting new features the next C64List release will bring.  Well, now you can now throw off the covers, drop your Ambien in the trash, light a candle, dig out your Commodore 64 and start writing new killer assembly language programs using C64List 3.00!

I hope you like the new assembler and are able to use it to create some new killer apps for the Commodore 64!


Until 3.01 arrives,


Leave a Comment

You must be signed-in to post comments.