Since I got my WIFI modem for C64 I wanted to write a program, or as would my son say an "app" that would synchronize my C64 uIEC clock (and maybe even CIA clock or TI variable) with modem's internal clock (it gets it from NTP). I'm not really into assembler coding, you know - it's like drilling a tunnel with a spoon when we have these huge monster drills, but I digress... I don't like C either, it doesn't have objects, not to mention functional programming or any other feature that makes coding FUN. And it's not suited for C64 programming anyway... So... what's there for me? There's SLANG and Atalan. SLANG seems to be just some kind of super-macro-assembler on top of macro-assembler, it's nice, but quirky. I do love how Atalan works, but unfortunately it was abandoned by its author. I tried to fix it, but it's written in some messy C, damn it!
So, no more options... unless... I write my own compiler! I never wrote any and had no idea how hard it could be. And guess what? It's not so hard! All you need is ANTLR4, a grammar of some nice existing modern language (like Kotlin) and the knowledge that 65xx has not three, but 250-something usable registers (thanks 6502.org!)
So it's been a lazy week, and I was able to compile this piece of code into 6510 (see attached assembler.s):
Code:
package pl.qus.wolin
fun sillySum(arg1: Word, arg2: Word): Word {
throw 12345
return arg1+arg2
}
fun main() {
var b: Word = 0
var sumError: Word = 0xcafd
try {
b=sillySum(4,2)
} catch (ex: Word) {
b=sumError
}
b++
}
.... that would leave $cafe in b variable (spoiler: if initializing variables was working - it isn't yet).
So how does it work? First it translates Wolin code to intermediate virtual machine assembler that has the following syntax:
Code:
mnemonic destination[type] = arg1[type], arg2[type]
So for example "b++" becomes:
Code:
add pl.qus.wolin.test.main..b[word] = pl.qus.wolin.test.main..b[word], #1[byte]
And then there's "template" file that describes how each "mnemonic type = type,type" combination gets translated into 6510 asm, by matching some patterns:
Code:
add ?dest[word] = ?src[word], #?val[byte] -> """
clc
lda {src}
adc #{val}
sta {dest}
lda {src}+1
adc #0
sta {dest}+1
"""
which in this case becomes:
Code:
clc
lda pl.qus.wolin.test.main..b
adc #1
sta pl.qus.wolin.test.main..b
lda pl.qus.wolin.test.main..b+1
adc #0
sta pl.qus.wolin.test.main..b+1
It does this line by line and lo and behold! - Kotlin code translated to 65xx assembler!
Of course it's only very beginning and a tiny fraction of what real Kotlin can do. I'm learnig a lot while coding it. Next thing will probably be a look into OO and functional... just because nobody tried it yet and because "65xx is not designed for OO".
Some technical info:
- CPU stack is used only for jsring and temporary register storage
- There's ZP-based "program stack" used for evaluating exproessions (and gee - it really makes everything so simple!)
- There are two non ZP stacks: one for function calls and function local variables (so recurrence is possible) and second for exception handling
- Variables can be fixed to a location like "var background: Ubyte^53281" and even to single bit at some location (for boolean variables)
I'll try to share some more experience if I continue coding this assembler. Feel free to share your ideas/knowledge!