Skip to content

A port of the Blitz!/Austro-Speed C64 BASIC V2 compiler to JavaScript.

Notifications You must be signed in to change notification settings

c1570/Reblitz64

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

70 Commits
 
 
 
 
 
 

Repository files navigation

Reblitz64

https://github.com/c1570/Reblitz64

A port of the Blitz!/Austro-Speed C64 BASIC V2 compiler to JavaScript.

Simple live page (running in your browser): docs/reblitz64.html

You can run the compiler on command line using node reblitz64.js source.prg target.prg i,j,x,y. The last parameter is optional and represents a set of BASIC V2 variables that the compiler is supposed to treat as integer variables (-32768..32767) without requiring the % suffix.

Rationale

The original Blitz! compiler was written in BASIC (with a few assembler helpers for improved performance) and got compiled using itself.

Porting that compiler using a 6502 emulator (see blitz 0.1) or emulating the BASIC line structure using a switch() based finite state machine is relatively straightforward but does not result in maintainable/readable code.

Instead, the approach taken here was i) rewriting the original BASIC code to use structured GOTOs only (i.e., GOTOs that form proper IF/THEN/ELSE and loop block structures), ii) creating a transpiler that generates JavaScript code (without any GOTOs) from that input.

The result is JavaScript code that is more readable and hackable than the results of the other approaches, and it's much faster than the emulation approach as well.

Benchmarks

Compiling a 36 Kbytes PRG.

  • Blitz! (C64, C1541): about 1400 seconds
  • Blitz! in VICE (100% speed, without True Drive emulation): about 600 seconds
  • blitz 0.1: about 4.4 seconds
  • Reblitz64: about 0.18 seconds

basictransplr

An incomplete BASIC V2 transpiler can be found at docs/basictransplr. This was used to do the major work of porting the Blitz! compiler to JavaScript.

Just as the name implies, basictransplr isn't actually a transpiler but more of a slightly extended BASIC detokenizer. It interprets hints given in REMs and replaces GOTOs by the appropriate control structures and does some structure checks.

The annotations BREAK, ELSE, WHILE(TRUE) replace the last GOTO encountered in the line. The annotation DO makes the line a loop start (including the statements of the line). The annotation IFBEGIN marks the double GOTO pattern typically used in BASIC V2 to emulate multiline IF/THEN/ELSE. The annotation ELSEIF is used for if/elseif/elseif/endif constructs.

Example:

10 I=I+1:REM"DO
20 IFI>10GOTO70:REM"BREAK
30 IFI>=3GOTO50:REM"IFBEGIN
40 PRINT"I IS LESS THAN 3":GOTO60:REM"ELSE
50 PRINT"I IS GREATER OR EQUAL TO 3"
60 GOTO10:REM"WHILE(TRUE)
70 IFI>10GOTO90:REM"ELSE
80 PRINT"THIS SHOULD NOT BE REACHED"
90 END

...gets transpiled to...

do {
  i=i+1
  if(i>10) break
  if(!(i>=3)) {
    console.log("i is less than 3")
  } else {
    console.log("i is greater or equal to 3")
  }
} while(true)
if(!(i>10)) {
  console.log("this should not be reached")
}
// end

Make sure the annotations actually match GOTO semantics.

See docs/blitz_bas.html for more examples.

basictransplr is not complete and depends on quite a few nasty regex hacks in postprocessing to give anything resembling actual JavaScript as output. Also, a lot of the semantics isn't quite right, e.g., BASIC FOR loops always run at least once, which the JS for loops in basictransplr's output don't do.

It's up to the user of basictransplr to adjust the input code accordingly.

Getting rid of those GOTOs

In order to replace GOTOs used in the Blitz! compiler by structured control flow, its sources had to get modified quite extensively.

  • GOTO into actual subroutines was replaced by GOSUB...:RETURN
  • A lot of GOTOing around (i.e., from pass 1 to pass 2 code) got replaced by GOSUBs
  • In pass 1, token handling code was jumped to via ON...GOTO. This got replaced by IF...GOSUB.
  • Token handling code used GOTO to jump into error handling code. This got replaced by an error flag (ER%=...:RETURN).
  • Many GOTO'd subroutines with optional inits were converted to nested subroutines (100 I=0, 110 I=I+1:GOTO(back) => 100 I=0:GOSUB 110:RETURN, 110 I=I+1:RETURN)

Putting it all together

See docs/blitz_bas.html for annotated decompiled and heavily rearranged Blitz! compiler sources (HTML output courtesy of Crank the PRINT!). Line numbers and variables have tooltips generated from docs/labels.csv.

Note that the rearranged code is longer than the original code and collides with the original ASM helpers as-is. You can shorten the code (e.g., remove the chooser screen) or relocate ASM helpers to another position in memory if you want to run the rearranged Blitz! compiler in a C64 environment.

For the JavaScript output, the original ASM helpers get replaced by custom JavaScript helpers docs/helpers.js.

docs/hack.sh builds the JavaScript version of the compiler and contains a lot of regex nastiness to make it all work.

Fixes and changes in Reblitz64

  • Added: Feature to force integer handling of variables not declared as int (%)
  • Fixed: Pass 2 failed in case the generated P-code reached beyond $7FFF (overflow in 15139 l2%=c%(c5%)+c%:return)
  • Fixed: Spaces in SYS parameters were not supported.
  • Fixed: (TODO typo in variable)
  • Fixed: (TODO broken comparison/variable count)
  • Removed: The :: mechanism to pass any code to the original BASIC interpreter got removed.

About

A port of the Blitz!/Austro-Speed C64 BASIC V2 compiler to JavaScript.

Topics

Resources

Stars

Watchers

Forks