C64 Cross-Assembly

During Lily Lander’s development, I experienced a lot of trouble with macro assemblers. As a first project on the C64, it took me a while to find what worked best for me.

First up was Turbo Macro Pro, running in a C64 emulator. This didn’t last very long, as it was too slow and difficult to use for a game jam. No version control possible, I was limited to saving memory dumps.

Next, I found C64Studio, a slightly dated IDE bundled with the ACME assembler. At least I thought it was ACME at first. As soon as I tried to use ACME’s most advanced features, I found that the internal assembler used most of the ACME syntax, but not all.

ACME was working okay, until the tricks I was pulling with references in macros eventually blew the whole thing up. This was halfway through the competition. I was at my wit’s end that the macro parts would be primitive at best.

In exasperation, I searched for something that suited my taste. I eventually read the manual for the Kick Assembler after having avoided it for being… Java. It was my third or fourth production language, and while I loved OO, I came to hate Java. The verbosity, the bulk, the ugly UI toolkits, and then what became of its ownership. Luckily, this was just running Java, not coding it. :)

Reading the manual convinced me to port my code over and give it a try. The syntax conversion was not too hard, as ACME uses ! and Kick uses . to prepend assembler commands. What were once ugly references inside macros were now function return values. Pseudocommands add dynamic argument translation while retaining the assembler’s ability to determine which version of the opcode to use.

This unlocked so much for me, and some of the results are pretty fun. An example:

.struct zpVar {addr,size}
.var zpDict = Hashtable()
.function reserve(bytes, initv) {
	.if(zpDict.containsKey(initv) == false) {
		.eval zpDict.put(initv, List())
	.eval zpDict.get(initv).add(zpVar(nextVar, bytes))
	.var ret = nextVar
	.if (nextVar == $100) {
		.error "Too many zeropage variables"
	.eval nextVar = nextVar + bytes
	.return ret

The above function allocates address space on the zeropage, then stores them in order of their init value, allowing me to automatically initialize a bunch of similar variables automatically with only one LDA instruction generated (followed by a bunch of STA).

Anyhow, Kick managed to do everything I needed, and I only found a few limitations that I would have liked to overcome. Check it out first if you ever decide to make a C64 game in assembly!

Get Lily Lander C64

Download NowName your own price


Log in with itch.io to leave a comment.


Thanks for the insight, I will have a look at that Kick Assembler. I tried TMP and found it wasn't for me.