Copyright K Cartlidge 2026.
GoZ80 is a cross-platform Go toolchain for general Z80 plus targeted ZX Spectrum development: a fast two-pass Z80 assembler that turns .asm/.z80 source into raw binaries, symbol maps, relocation data, and optional per-instruction JSON, or into ready-to-run .tap tapes with an auto-generated BASIC loader and optional loading screen.
It also converts PNG images to Spectrum SCR format (with quality reports), includes a local web-based sprite editor (with assembler export and optional auto-mask), and is meant as a single, easy-to-install command-line app that replaces juggling several separate tools.
- Standalone builds are available for Windows, Mac, and Linux (Intel and ARM)
- A simple how-to is also available for reference
Instruction encodings and timings are based on the Zilog Z80 instruction set (and common undocumented opcodes).
Other assemblers exist. Other tools exist for tap file generation, loaders, loading screen conversion, and even sprite editing. Some may even be better. However most of those tools are neither friendly to install (dependencies, toolchains etc) nor simple to run. And you probably have to use more than one to get the full functionality you need.
What GoZ80 does is offer a simple, trivial to install and use, one-stop, cross-platform tool to go from source to a ready-to-publish tape file with a single command.
For general users:
For GoZ80 developers (only):
- Very fast 2-pass Z80 assembler with nested includes
- Generates various output files, including:
- Raw binary file with the assembled code (eg for embedded use)
- ZX Spectrum tape with a BASIC auto-starting loader and loading screen
- A formatted (tidy) edition of the original source with includes pulled in
- A map file (symbol list), with final addresses and original source location
- A reloc file of code relocation details with offsets to patch
- A list including generated bytes plus T-cycles
- An optional JSON file with per-instruction assembly details
- Supports
orgfor specifying intended load address - Supports
entry(execution start address for TAP files)- Start address defaults to
orgor 0 if noentryprovided
- Start address defaults to
- Supports inline expressions (symbols, basic maths, bitwise , unary etc)
- Supports decimal, hex (
$7F,7FH), and binary (%1010,1010B) - Supports
incbin/binaryfor embedding binary files - Supports
align(NOP padding to a power-of-2 boundary) - Supports
defb/defw/defsfor memory fills - Supports
equandsetwith expressions - Optionally converts PNG to loading screen SCR format (auto resize, dither, quantize)
- Reports various metrics relating to conversion quality
- Colour alignment (how close source colours are to the Spectrum palette)
- Colour complexity (impact of the 2-colours-per-8x8 attribute limit)
- Image degradation (overall detail loss between resized PNG and final SCR)
- Includes scaled-down attribute-block heatmaps for the reports
- Reports various metrics relating to conversion quality
GoZ80 is a totally standalone single file tool. You can install it in one of two simple ways:
- Copy the version for your OS into your project's source folder
- The app is small (less than 10MB) so the overhead is tiny
- Copy the version for your OS into a location in your system path
- For example:
sudo cp <downloaded-file> /usr/local/bin
- For example:
It's not normal practice for most apps to be added to the projects that use them but the size is negligible, it can be run from anywhere, and it will always be available going forward. Your choice.
These instructions are also displayed when run.
For Linux and Mac you may need to chmod +x goz80 when first downloaded/installed.
A snippet of sample code which loads on a ZX Spectrum then sets the border colour.
(This formatted version is one of the outputs when run; your original source can have looser formatting and instructions/registers are not case sensitive.)
; Sample code.
; Shows how org and entry can be used to set the load and start
; addresses independently of each other.
; Also sets a yellow border to prove the code ran.
DEVICE zxspectrum
ORG $8000 ; Load address for the program
ENTRY $800A ; Start address for the program
; Could use label, but hex for clarity
PAD: DEFS 10, $FF ; 10 bytes padding to reach the start address
LD a, 6 ; Yellow border — proof this code ran
OUT ($fe), a ; Spectrum port $FE, bits 0–2
DONE: RET
The alignment of the comment on the PAD line looks wrong but is actually intentional.
As DEFB and DEFW statements can be quite long, we skip aligning the comments for them (plus DEFS for consistency) to avoid comments on other source lines gaining too much extra padding and disappearing off the right of the screen when reading formatted output.
goz80 source-file target-folder -jsonThis will assemble the source file (and any includes) then write the outputs to the target folder. The source file(s) should be .asm or .z80. The optional -json flag also generates a per-instruction breakdown of the assembly.
goz80 -sprites filename.jsonThis starts the sprite editor running locally on port 8080 with the sprite data being read from and written to the specified filename. It supports 8x8, 8x16, and 16x16 sprites with screen attribute data.
If you choose to generate a tap file rather than a bin file then the resulting tape will automatically include a BASIC loader.
The loader will auto-start when the user enters LOAD "" on their machine.
It will read in the assembled machine code then pass control to the first instruction in the assembly.
Load and start default to 0. ORG x or ENTRY x alone sets both; ORG plus ENTRY sets load address and start separately. The TAP loader uses that for CLEAR, the CODE block load address, and RANDOMIZE USR.
If you include a loading.scr file then when generating a tap file the automatically-provided loader will be changed to load that in first (as a traditional loading screen) before continuing with the main program.
It will be sanity checked for the expected 6912 byte count, where the final 768 bytes 'look like' valid attributes.
If there is no loading.scr but there is a loading.png image then that will be converted into a loading screen in the correct format:
- It first gets resized to the expected 256x192 pixels, maintaining the original colours
- Then dithering and colour selection is used to reduce to the expected 6,144 bytes at 1 bit per pixel using a 768 byte attribute table
- Numbered 256x192 pixel images are generated (as coloured PNGs) to show these steps
- Numbered heatmaps are generated to show problematic 8x8 pixel blocks when comparing the final Spectrum style pixel/attribute values against the scaled down original PNG colours
- Intermediate images and any conversion report are written under a
scr-conversion/folder next toloading.scr
If you want to regenerate the loading screen from an updated PNG image you'll need to remove the existing loading.scr first.
You can assemble any Z80 code you like, but there's a good chance you're making a game. And if that's the case then you may be used to cheats like 'Start with 99 lives' where the instructions say POKE $9A21,99 or similar.
To do this for your own project your source might include something like the following (assume at line 219) which your code would make use of at the start of each game:
LIVES: DEFB 3 ; The number of lives when the game starts
When you assemble your source one of the outputs is a *.map file which should then contain something like this:
; GoZ80 symbol map v1 input=my-game.asm
; addr kind visibility name source
8011 LABEL GLOBAL MSG my-game.asm:26
9A21 LABEL GLOBAL LIVES my-game.asm:219
Here you can see the assembled address of the LIVES label from line 219 is $9A21, and this tells you the address to POKE to adjust the number of lives when the game starts.
(You shouldn't need to do this unless you make your own local code changes to GoZ80 itself.)
cd cmd
go run . ../examples/01-Simple.asm ../examplesThere is a simple build script for each platform you are building on. These scripts produce outputs for all the platforms we build for.
From the cmd folder run the build script related to your platform. For example, on a Mac:
cd cmd
./build/macos.shThe generated builds will be created in the expected builds folder structure.
cd cmd
go test ./...- The
devicedirective is parsed but ignored (this will likely not change)- It's recognised because whilst GoZ80 ignores it the source may still contain it for use by other assemblers
- Flat memory; segmented/bank memory is not supported
