Skip to content

mrspeaker/ebas2bas

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

45 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

EBAS2BAS

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.

https://user-images.githubusercontent.com/129330/136657907-99987639-f516-42a7-b2aa-4905a4704705.gif

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

Usage

node ebas2bas.js <filename>

You can omit the .ebas extension. The output will be the the same name, but with a .bas extension.

Transformations

Line numbers & labels

~top
print "i am the best ";
goto ~top

compiles to:

10 print "i am the best ";
20 goto 10

Support for hex and binary numbers

Converts hex and binary numbers to decimal

0x1f -> 31
0b111 -> 7
0b1000_0001 -> 129

Comments

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

Multi line source

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

Include files

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

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

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).

Options

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.

Running .bas files on a C64

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!).