This is an implementation of the VIC-20 game Blitz in Sinclair BASIC using a modern toolchain. Some bits are faithful and some are different, depending on what I could use, 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 it now

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

That 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 straightforward:

.\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 idiomatic listing is as follows, generated by BasinC, keeping in mind that a font file is loaded first, meaning that strings like "cde" are displayed as a plane graphic.

  10 CLEAR 63999
  12 LOAD "" CODE
  14 REM load font at 64000
  16 POKE 23607,249
  18 REM score and top
  20 LET s=0
  22 LET b=0
  24 LET t=0
  26 REM main loop
  28 IF s>t THEN LET t=s
  30 GO SUB 150
  32 LET s=0
  34 LET b=0
  36 LET l=0
  38 LET g=0
  40 GO SUB 212
  42 PRINT #1;AT 1,0; INK 3;"BEST:";t;#1;AT 1,22; INK 3;"SCORE:";s
  44 REM construct buildings from city data
  46 LET d=260+l
  48 RESTORE d
  50 READ c$
  52 PRINT #1;AT 1,9; INK 0;c$;" ";1940+(g*10)+l
  54 FOR y=4 TO 27
  56 READ b$
  58 READ r$
  60 READ h
  62 LET h=h+(g*2)
  64 FOR x=21 TO 23-h STEP -1
  66 PRINT AT x,y; INK 1;b$
  68 BEEP 0.01,23-x
  70 NEXT x
  72 PRINT AT 22-h,y; INK 1;r$
  74 NEXT y
  76 REM bomber run
  78 LET bx=0
  80 LET by=0
  82 LET bp=0
  84 FOR x=0 TO 21
  86 FOR y=0 TO 28
  88 LET fx=50-x
  90 REM animate plane 1
  92 IF y=0 AND x>0 THEN PRINT AT x-1,y; INK 2;"mno";AT x,y;"pqr"
  94 IF y>0 THEN PRINT AT x,y; INK 2;"cde"
  96 REM animate bomb 1
  98 IF bp>0 AND bx=x THEN PRINT AT bx+1,by; INK 0;"h"
 100 IF bp>0 AND bx>x THEN PRINT AT bx,by; INK 0;"f";AT bx+1,by;"g"
 102 REM animate plane 2
 104 IF y=0 AND x>0 THEN PRINT AT x-1,y;"   ";AT x,y; INK 2;" ab"
 106 IF y>0 OR x=0 THEN PRINT AT x,y; INK 2;" ab"
 108 REM animate bomb 2
 110 IF bp>0 AND bx>x THEN PRINT AT bx,by; INK 0;" ";AT bx+1,by;"h"
 112 REM plane collision check
 114 IF ATTR (x,y+3)>56 THEN BEEP 0.01,1: GO SUB 240: GO TO 28
 116 REM bomb next line plus collision check
 118 IF bp>0 THEN LET bx=bx+1
 120 IF bp>0 AND bx<21 AND ATTR (bx+1,by)>56 THEN LET fx=23-bx: LET bp=bp-1: LET s=s+1: PRINT #1;AT 1,28; INK 3;s
 122 IF bp=1 THEN PRINT AT bx+1,by; PAPER 2; INK 6; FLASH 1;"v": LET b=b+1
 124 IF bx=21 OR bp=1 THEN PRINT AT bx-1,by; INK 0;" ";AT bx,by;" ": LET bp=0
 126 REM check for fire press
 128 IF bp=0 AND x<21 AND y>1 AND y<26 AND INKEY$=" " THEN LET fx=1: LET bx=x: LET by=y+2: LET bp=7
 130 BEEP 0.01,fx
 132 NEXT y
 134 IF x<21 THEN PRINT AT x,y;"   "
 136 NEXT x
 138 GO SUB 186
 140 LET l=l+2
 142 IF l>8 THEN LET l=0: LET g=g+1
 144 CLS
 146 GO TO 40
 148 REM start screen routing
 150 CLS
 152 PRINT AT 2,11; INK 2;"EIGENBLITZ"
 154 PRINT AT 4,5; INK 3;"THE OBJECT OF THE GAME"
 156 PRINT AT 5,5; INK 3;"IS TO FLATTEN THE CITY"
 158 PRINT AT 6,5; INK 3;"SO THAT  YOU  CAN LAND"
 160 PRINT AT 7,5; INK 3;"YOUR AEROPLANE  ON THE"
 162 PRINT AT 8,5; INK 3;"GROUND.   YOU  CONTROL"
 164 PRINT AT 9,5; INK 3;"THE DROPPING  OF BOMBS"
 166 PRINT AT 10,5; INK 3;"BY  PRESSING "; PAPER 0; INK 7;"SPACE"; PAPER 7; INK 3;"."
 168 PRINT AT 12,5; PAPER 2; INK 7;"PRESS ANY KEY TO START"
 170 PRINT AT 15,9;"BEST SCORE ";t
 172 PRINT AT 17,9; INK 1;"LAST SCORE ";s-b
 174 PRINT AT 18,9; INK 1;"PLUS BONUS ";b
 176 PRINT AT 19,14;"TOTAL ";s
 178 PAUSE 0
 180 CLS
 182 RETURN
 184 REM man waving routine
 186 PRINT AT 21,29; INK 2;"ab"
 188 FOR n=1 TO 5
 190 PRINT AT 21,26; INK 0;"s"
 192 PAUSE 10
 194 PRINT AT 21,26; INK 0;"t"
 196 PAUSE 10
 198 PRINT AT 21,26; INK 0;"u"
 200 PAUSE 10
 202 PRINT AT 21,26; INK 0;"t"
 204 PAUSE 10
 206 NEXT n
 208 RETURN
 210 REM plan flies off routine, careful to leave black ink after plain
 212 PRINT #1;AT 0,0; INK 4;"llllllllllllllllllllllllllllllll"
 214 PRINT AT 21,0; INK 2;"ab"
 216 PRINT AT 20,0; INK 2;"mno";AT 21,0;"pqr"
 218 LET x=20
 220 FOR y=0 TO 27
 222 PRINT AT x,y;" ";AT x,y+1; INK 2;"ab"
 224 PRINT AT x+1,y;"   "
 226 IF x=0 THEN PRINT AT x,y;" ";AT x,y+1; INK 2;"cde"
 228 IF x>0 THEN LET x=x-1: PRINT AT x,y;" ";AT x,y+1; INK 2;"mno";AT x+1,y; INK 0;" ";AT x+1,y+1; INK 2;"pqr"
 230 BEEP 0.01,50-x
 232 NEXT y
 234 PRINT AT 0,28;"    "
 236 RETURN
 238 REM plane crashes routine
 240 LET ax=x
 242 LET bx=x
 244 PRINT AT ax-1,y+1;" ";AT ax,y+1; INK 2;"i";AT bx-1,y+2;" ";AT bx,y+2;"j"
 246 LET x=x+1
 248 IF ATTR (ax+1,y+1)=56 THEN LET ax=ax+1
 250 IF ATTR (bx+1,y+2)=56 THEN LET bx=bx+1
 252 IF x>22 THEN RETURN
 254 BEEP 0.01,50-x
 256 GO TO 244
 258 REM city data
 260 DATA "LONDON","w","|",10,"x","|",9,"w","}",8,"w","~",7,"z","{",6,"y","~",11,"y","|",10,"x","{",9,"w","{",8,"z","|",7,"z","}",9,"y","~",10,"w","|",5,"z","}",5,"z","~",7,"x","|",12,"y","|",10,"w","{",6,"x","}",5,"z","~",7,"y","~",7,"x","|",5,"w","{",7,"y","{",10
 262 DATA "BRISTOL","x","|",5,"w","{",7,"y","{",10,"y","~",10,"w","{",6,"x","}",5,"z","~",7,"w","|",10,"x","|",9,"w","}",8,"w","~",7,"z","{",6,"y","~",11,"y","|",10,"x","{",9,"w","{",8,"z","|",7,"z","}",9,"y","~",7,"w","|",5,"z","}",5,"z","~",7,"x","|",12,"y","|",10
 264 DATA "GLASGOW","z","~",7,"x","|",12,"y","|",10,"w","{",6,"x","}",5,"z","~",7,"w","|",10,"x","|",9,"w","}",8,"w","~",7,"z","{",6,"y","~",11,"y","|",10,"x","{",9,"w","{",8,"z","|",7,"z","}",9,"y","~",7,"x","|",5,"w","{",7,"y","{",10,"y","~",10,"w","|",5,"z","}",5
 266 DATA "CARDIFF","x","|",5,"w","{",7,"y","{",10,"y","~",10,"w","|",5,"z","}",5,"z","~",7,"x","|",12,"y","|",10,"w","{",6,"x","}",5,"z","~",7,"w","|",10,"x","|",9,"w","}",8,"w","~",7,"z","{",6,"y","~",11,"y","|",10,"x","{",9,"w","{",8,"z","|",7,"z","}",9,"y","~",7
 268 DATA "BELFAST","y","|",10,"x","{",9,"w","{",8,"z","|",7,"z","}",9,"y","~",7,"w","|",5,"z","}",5,"z","~",7,"x","|",12,"y","|",10,"x","|",5,"w","{",7,"y","{",10,"y","~",10,"w","{",6,"x","}",5,"z","~",7,"w","|",10,"x","|",9,"w","}",8,"w","~",7,"z","{",6,"y","~",11