Skip to content

kcartlidge/GoZ80

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

59 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

small logo

GoZ80 - the Z80 assembler

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.

Instruction encodings and timings are based on the Zilog Z80 instruction set (and common undocumented opcodes).

Why?

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.

Contents

For general users:

For GoZ80 developers (only):

Features

  • 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 org for specifying intended load address
  • Supports entry (execution start address for TAP files)
    • Start address defaults to org or 0 if no entry provided
  • Supports inline expressions (symbols, basic maths, bitwise , unary etc)
  • Supports decimal, hex ($7F, 7FH), and binary (%1010, 1010B)
  • Supports incbin/binary for embedding binary files
  • Supports align (NOP padding to a power-of-2 boundary)
  • Supports defb/defw/defs for memory fills
  • Supports equ and set with 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

Installing

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

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.

Running

These instructions are also displayed when run. For Linux and Mac you may need to chmod +x goz80 when first downloaded/installed.

Example Z80 Source

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.

Assembling

goz80 source-file target-folder -json

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

Editing Sprites

goz80 -sprites filename.json

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

BASIC Loader

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.

Loading Screen

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 to loading.scr

If you want to regenerate the loading screen from an updated PNG image you'll need to remove the existing loading.scr first.

Game Cheats (Pokes)

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.

Running from Source

(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  ../examples

Generating New Builds

There 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.sh

The generated builds will be created in the expected builds folder structure.

Tests

cd cmd
go test ./...

Out of Scope

  • The device directive 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

About

Cross-platform command line tool to generate "bin" files and/or "tap" (ZX Spectrum tape) files from Z80 assembly code, optionally with a loading screen (including PNG conversion). Includes a ZX Spectrum sprite editor.

Topics

Resources

License

Stars

Watchers

Forks

Contributors