Commodore 64 assembly coding on the command line

By Christian Stigen Larsen
06 May 2016

Have you ever had this nagging feeling that you're less worth because you never actually coded assembly on the C64? Of course you have! Fortunately, here's how you can finally do something about it, using the OS X or Linux command line.

Fun fact: The original Lucasfilm Commodore 64 games, such as Maniac Mansion, were cross-compiled on UNIX workstations, transferred over the network and uploaded to running C64s — way back in 1987! (Source: The Thimbleweed Park podcasts, which I highly recommend.) That's an excellent workflow that we'll mimic here. But instead of using real C64s, we'll be using the VICE emulator.

By the way, if you happen to be a fan of Sublime, you can download a complete package over at Dustlayer. Here, we'll be using good old make and the ACME assembler.

Installing the tools

To install ACME and VICE on OSX,

$ brew install acme vice

On Linux, you may find those in your package manager. If you don't, just download VICE and compile from source. It's really simple and straight forward, and you get the C64 ROMs with the source (but not with the binary packages). Just don't install to a non-standard location; VICE didn't like that the last time I tried. The ACME assembler is very old, but should be readily available from archive.org . It's very small and doesn't even have a kludgy configure script, if I recall correctly.

To compile a C64 assembly file with ACME, type

$ acme --cpu 6510 --outfile foo.prg foo.asm

This produces foo.prg. I am aware that the 6502 and the C64's 6510 CPUs are supposed to be instruction set compatible; I pass --cpu 6510 anyway, just in case there should be minor ISA differences.

If you use the Dust demo (referenced above), you can type acme index.asm to compile it. Files are placed in the build directory.

With VICE you can execute the .prg file directly, but using its c1541 utility you can also put them into .d64 disk images. On OSX, the tool is located in /Applications/Vice64/tools/c1541. For the Dust demo, the image can be made with

$ c1541 -format diskname,id d64 image_name.d64 -write build/hello_world.prg hello.prg

I've just used diskname as the name of the dist, and id as its ID.

The Makefile

Now that you know how to compile and create disk images, you can put it together in a makefile.

TARGETS := foo
C1541 := /Applications/Vice64/tools/c1541
X64 := open /Applications/Vice64/x64.app

.PRECIOUS: %.d64

all: $(TARGETS)

%.prg: %.asm
  acme --cpu 6510 --format cbm --outfile [email protected] $<

%.d64: %.prg
  $(C1541) -format foo,id d64 [email protected] -write $<

%: %.d64
  $(X64) $<

clean:
  rm -f $(TARGETS) *.prg *.d64

If you have a file foo.asm in the same directory, you can type

$ make foo

to compile foo.prg, put it into a foo.d64 disk image and run it from VICE. Since the prg-file is an implicit target, make will delete it before executing the code. You can keep it with make foo.prg or by adding it to .PRECIOUS.

You can also launch VICE and run the code the old-fashioned way. This requires a disk image. From VICE, open the file browser to mount the image, then do

LOAD "$",8
LIST

to list the disk contents. Then do

LOAD "FOO.PRG",8,1
RUN

to load and run the program. While loading, hit Command-W to enter warp speed, making it load faster, then disable warp mode again before running the code.

A BASIC loader

The .prg file format is very simple: It starts with a two-byte memory address which the rest of the contents are loaded into. You have to specify the secondary load argument ,1 to make sure the contents are loaded into this memory address: The default is to load the contents into the start of BASIC memory at $0801, meaning it expects the .prg file to contain just a BASIC program. The usual trick is to add a small BASIC program at the beginning of the .prg that just jumps to the real assembly program in memory.

So, if your assembly program is set to start at $0900, then the .prg file will contain

<$0801 (two-byte load address)>
<a small BASIC program, e.g. "10 SYS 2304", where $0900 = 2304>
<filler space ...>
<your assembly program starting at, e.g. $0900>

You can omit the starting BASIC program in your files if you're very clever, but most programs seem to use this trick.

You can see some example files at https://github.com/cslarsen/c64-examples.

The BASIC loader I use there is given below. It may be a bit much to understand as a first tutorial, but it just encodes a 10 SYS <start address> program:

; A BASIC booter, encodes `10 SYS <address>`.
; Macroified from http://www.pouet.net/topic.php?which=6541

!source "constants.asm"

!macro start_at .address {
  * = basic
  !byte $0c,$08,$00,$00,$9e
  !if .address >= 10000 { !byte 48 + ((.address / 10000) % 10) }
  !if .address >=  1000 { !byte 48 + ((.address /  1000) % 10) }
  !if .address >=   100 { !byte 48 + ((.address /   100) % 10) }
  !if .address >=    10 { !byte 48 + ((.address /    10) % 10) }
  !byte $30 + (.address % 10), $00, $00, $00
  * = .address
}

; A cooler example is to write
;
;   10 SYS <address>: REM <backspaces>Your comment
;
; When the user types LIST, he will just see
;
;   10 Your comment
;
; but still be able to run it.
; For this, see http://codebase64.org/doku.php?id=base:acme-macro-tu

The contents of constants.asm is just some memory mapped locations for the start of BASIC memory and foreground and background colors. By writing to the color locations, you can change the colors.

;; Start of BASIC program
basic = $0801

;; Background color
bgcol = $d021

;; Border color
bocol = $d020

Example code

To use the BASIC booter, include the file and invoke the macro with +start_at <address>:

!source "basic-boot.asm"

+start_at $0900

; Set background and border to black
ldx #$00
stx bgcol
stx bocol

; Flicker border and background
.loop
  inc bgcol
  inc bocol
  jmp .loop

It wraps the loader in an ACME macro start_at. The main assembly here starts at $0900, meaning it loads super fast: The BASIC loader starts $0801 and the rest of the code at $0900. Now, the .PRG file format simply consists of a destination address in memory to load the file contents into. If the space between your BASIC loader (which must start at $0801) and your entry point is huge, then you'll waste space, and the file will take forever to load, even if you're using warp mode in your emulator. If you need more space, let the first .PRG file be a loader so your program gets up and running quickly.

If you put the above code into flicker.asm, using the above makefile you can now run it by typing

$ make flicker
acme --cpu 6510 --format cbm --outfile flicker.prg flicker.asm
/Applications/Vice64/tools/c1541 -format foo,id d64 flicker.d64 -write flicker.prg
Unit: 0
Formatting in unit 8...
Writing file `FLICKER.PRG' as `FLICKER.PRG' to unit 8.
open /Applications/Vice64/x64.app flicker.d64
rm flicker.prg

The output is given below

Commodore 64 loading screen

Demo running on a Commodore 64, showing flickering colors

What next?

The only thing you need now is a lot of time on your hands, a good C64 reference manual and memory map, and you're set for hours of fun (after you've made a stable raster, of course).