C64 BASIC, with some lil’ conveniences: no line numbers required, and some other helpful transformations. This tool converts .ebas files to standard C64 BASIC as a .bas file.
What is an .ebas file? It’s a thing I made up, ‘cause I am making a game which requires me to write a lot of C64 BASIC, and I got annoyed at re-numbering things. Then I got carried away adding more features. Unless you are me, you likely will have no use for this script.
Here’s an example of some “eBASIC”:
# Position sprites
for n=4 to 9:
poke v+n,n*0x14:
next
# Print some text
print "{clear}"
~top
poke 0x286,n:
n=n+1
print "coding is basic ... ";
goto ~top
Running ebas2bas generates BASIC:
10 for n=4 to 9:poke v+n, n*20:next
20 print "{clear}"
30 poke 646,n:n=n+1
40 print "coding is basic ... ";
50 goto 30
node ebas2bas.js <filename>
You can omit the .ebas
extension. The output will be the the same name, but with a .bas
extension.
~top
print "i am the best ";
goto ~top
compiles to:
10 print "i am the best ";
20 goto 10
Converts hex and binary numbers to decimal
0x1f -> 31 0b111 -> 7 0b1000_0001 -> 129
Comments with “#” are removed from the source code (You can still use REM comments if you want them to stay).
poke 0xD020,1 # Make border white -> poke 53280,1
Most BASIC programs jam many statements onto a single line. To keep the source readable, you can use multiple lines - then any ending with “:” will be concatenated.
for n=0 to 62 :
read q :
poke 0x340+n,q :
next
compiles to:
10 for n=0 to 62 :read q :poke 832+n,q :next
External files can be included into the current source with {{ "./path/to/file" }}
. The contents of the file will be dumped in the first pass, so all further transformations (hex, macros, etc) will be applied to the included contents. Include files can also contain other includes (with no circular dependency testing, so becareful!).
As the include file is dumped directly, it can also be assembly code, or macros. If it’s assembly, remember to wrap the include tag in an inline-assembler tag (see section inline assembler
) if needed. Eg: {{! {{ "./test.asm" }} !}}
.
Macros are javascript functions that return BASIC source code that is injected in the final listing. If the return value is an array, multiple lines will be injected. To define a macro, wrap a javascript function in {!
and !}
tags:
{! rainbow = (str) => {
const cols = COLS.slice(1); // COLS = Magic array of color tokens
return str
.split("")
.map((ch,i) => "{" + cols[i % cols.length] + "}" + ch)
.join("");
} !}
This defines a macro called rainbow
that splits an input string into characters, then changes the color of each character using the standard basic color codes (eg {wht},{red}
). To call a macro, wrap a function call in {{
and }}
tags:
print "hello, {{ rainbow("world") }}!"
REM compiles to:
10 print "hello, {wht}w{red}o{cyn}r{pur}l{grn}d!"
Inline assembler can be compiled with KickAssembler, and the bytes are then injected into the BASIC listing via POKES and run with a SYS call.
{{!"set screen colors via asm"
*=$c000
lda #0
sta $d020
sta $d021
rts
!}}
Compiles to:
10 rem set screen colors via asm
20 for za = 0 to 8:read zb:poke 49152+za,zb:next za
30 sys 49152
40 data 169,0,141,32,208,141,33,208,96
The title string is optional, and will be included in a REM statement if present. If you don’t specify a base memory location (with the KickAssembler definition (eg, *=$c000
) it will use the default (I think it’s $801
? Which is not good for BASIC!). But if you specify the base in your first inline asm block, subsequent blocks can continue on from the last address by putting a *
after the opening tag.
For example, the above asm is located from $c000
to $c008
(49152-49160 decimal). If you start the next asm block with {{!*
, it will POKE the bytes into location $c009
(49161 decimal).
Sorry, no CLI options yet. Settings are defined in ebas_config.json:
{
"LINE_SPACING": 10,
"OUTFILE_PATH": "./",
"KICKASSEMBLER_PATH": "/usr/lib/KickAssembler/",
}
- LINE_SPACING is how to sequentially number your source code. Default is 10, but you might just want 1 (or 5, or whatever).
- OUTFILE_PATH indicates where to dump the output file. Default is in the current directory.
- KICKASSEMBLER_PATH is the path to the KickAssembler compiler jar file if you want to do inline assembler.
My use case is to convert the .ebas file to plain C64 BASIC, then compile that into an optimised C64 .prg
file with Egon Olsen’s fantastic BASICv2 mospeed
java command line tool:
./mospeed.sh -target=test.prg test.bas
The .prg
file can then be loaded into Vice or another emulator (or, you know, run on a real Commodore 64!).