eigengrouse.com

Eigenblitz

2/2/2026

This is an implementation of the VIC-20 game Blitz in Sinclair BASIC using a modern toolchain. Some bits are faithful and some bits are different depending on what I could use or figure out, or wanted to try in the time. .tap file for use with a ZX Spectrum emulator (tested with Fuse).

Download .tap file

A quick demo…

Play

A few notes on what I did…

Talk is cheap, show me the code.

I used a custom font (thanks @jimblimey), pasting the defb values into a Z80 .asm file and assembling with pasmo to create a .tap file. Deciding I only needed upper case text meant I had 26 lower case characters to provide graphics while still being able to use the nice and easy BASIC PRINT command.

You could use some sort of tool to design your UDGs graphically but I find I can visualise them well enough if they’re defined using binary in the .asm file e.g.

defb %11111110 ; w - building body type 1
defb %11010110
defb %11010110
defb %11111110
defb %11111110
defb %11010110
defb %11010110
defb %11111110

Telling pasmo where in memory to assemble by adding this to the top of the file…

org 64000

…means you can load the font from BASIC with the following…

CLEAR 63999
LOAD "" CODE
REM load font at 64000
POKE 23607,249

The BASIC .zxb file was also converted to a .tap file using zmakebas, which gives you a few quality of life improvements such as labels instead of line numbers. One nice thing about .tap files is that you can just append your Z80 .tap file to your BASIC .tap file, which loads it using LOAD "" CODE and the resulting .tap file just works. The .bat build file is therefore pretty straight forward…

.\tools\pasmo.exe --tap main.asm code.tap
.\tools\zmakebas -l -a @begin -o basic.tap main.zxb
type basic.tap code.tap > eigenblitz.tap
del basic.tap
del code.tap

This build pipeline could also be used to include machine code routines which can be called from BASIC so long as we know the org location. Or even a game fully implemented in Z80 with the “BASIC loader” (which is what I believe it’s usually called) loading the code then kicking it off. The BASIC loader is also where you could add a loading screen. The rest of the BASIC file in our case is the game implementation.

Sinclair BASIC Implementation

Blitz is a simple game. The player’s plane moves from left to right on a screen filled with buildings, moving a row down each time it reaches the far side. Bombs can be dropped that fall at the same speed the plane flies, that destroy up to 5 storeys, and only 1 bomb can be dropped at a time. If the player collides with a building they crash and the game restarts. If the player destroys all buildings then they can land and the next level is loaded.

The flowchart below shows the control flow and this is what was implemented in Sinclair BASIC (code).

Game Loop Logic

%%{init: {
  "themeVariables": {
    "fontSize": "11px"
  }
}}%%
flowchart TD
    mainloop("@mainloop:")
    gosub_startscreen("GO SUB @startscreen")
    gameloop("@gameloop:")
    gosub_flyoff("GO SUB @flyoff")
    load_citydata("READ @citydata + level")
    print_city("PRINT each building adding game*2 height")
    for_row_column("FOR row : FOR column")
    print_plane("PRINT plane at row, column")
    q_bomb_power{{"IF bomb power > 0"}}
    print_bomb("PRINT bomb at bomb row, bomb column")
    q_plane_collision{{"IF plane collision"}}
    gosub_planecrash("GO SUB @planecrash")
    q_bomb_collision{{"IF bomb collision"}}
    update_bomb_power("LET bomb power = bomb power - 1 : LET score = score + 1")
    update_bomb_row("LET bomb row = bomb row + 1")
    q_bomb_ground{{IF bomb row = max OR bomb power = 1}}
    remove_bomb("LET bomb power = 0")
    q_player_fire{{"IF player fire"}}
    player_fire("LET bomb row = row + 1, bomb column = column, bomb power = 5")
    next_column_row("NEXT column : NEXT row")
    gosub_manwaving("GO SUB @manwaving")
    level_up("LET level = level + 1")
    q_max_level{{"IF level = max"}}
    game_up("LET level = 1 LET game = game + 1")

    mainloop --> gosub_startscreen
    gosub_startscreen --> gameloop
    gameloop --> gosub_flyoff
    gosub_flyoff --> load_citydata
    load_citydata --> print_city
    print_city --> for_row_column
    for_row_column --> print_plane
    print_plane --> q_plane_collision
    q_bomb_power -- yes --> print_bomb
    print_bomb --> q_bomb_collision
    q_bomb_power -- no --> q_player_fire
    q_plane_collision -- yes --> gosub_planecrash
    gosub_planecrash --> mainloop
    q_plane_collision -- no --> 
    q_bomb_power
    q_bomb_collision -- yes --> update_bomb_power
    update_bomb_power --> update_bomb_row
    update_bomb_row --> q_bomb_ground
    q_bomb_collision -- no --> update_bomb_row 
    q_bomb_ground -- yes --> remove_bomb
    q_bomb_ground -- no --> next_column_row
    q_player_fire -- yes --> player_fire
    q_player_fire -- no --> next_column_row
    player_fire --> next_column_row
    remove_bomb --> q_player_fire
    next_column_row --> for_row_column
    next_column_row --> gosub_manwaving
    gosub_manwaving --> level_up
    level_up --> q_max_level
    q_max_level -- yes --> game_up
    q_max_level -- no --> gameloop
    game_up --> gameloop

Final thoughts…

It probably took as long to create the flowchart, which was partly an exercise in learning mermaidjs, as it did to write the Sinclair BASIC so that might be more useful for things like build pipelines. This whole game was a bit of a learning exercise in fact, but having an end product i.e the VIC-20 game gave me something to work towards and I was able to figure things out until I was happy enough that they did the job. It also meant I ended up with a game that was quite playable as this had already been thought out, where in the past I have just made things up as I’ve gone along and the end product was a bit all over the place. There’s still plenty of room for creativity and even creating the build pipeline was really satisfying and fun even. So I quite like this way of working and will try to have a clear idea of what I’m trying to achieve for future projects.