A C language port of the Boriel ZX BASIC compiler, originally written in Python by Jose Rodriguez-Rosa (a.k.a. Boriel).
This is an agentic porting experiment β a test of whether an AI coding assistant can systematically port a non-trivial compiler (~38,500 lines of Python) to C, producing a drop-in replacement with byte-for-byte identical output.
The toolchain being ported β zxbc (compiler), zxbasm (assembler), and zxbpp
(preprocessor) β is validated stage by stage against the original's comprehensive test
suite of 1,285+ functional tests.
The practical end-goal: a C implementation of the compiler suitable for embedding on NextPi and similar resource-constrained platforms where a full modern Python runtime is undesirable.
| Phase | Component | Tests | Status |
|---|---|---|---|
| 0 | Infrastructure (arena, strbuf, vec, hashmap, CMake) | β | β Complete |
| 1 | Preprocessor (zxbpp) |
96/96 π | β Complete |
| 2 | Assembler (zxbasm) |
61/61 π | β Complete |
| 3 | BASIC compiler frontend (lexer + parser + AST) | β | β³ Planned |
| 4 | Optimizer + IR generation (AST β Quads) | β | β³ Planned |
| 5 | Z80 backend (Quads β Assembly) β 1,175 ASM tests | β | β³ Planned |
| 6 | Full integration + all output formats | β | β³ Planned |
The zxbasm C binary is a verified drop-in replacement for the Python original:
- β 61/61 tests passing β zero failures, byte-for-byte identical binary output
- β 61/61 Python comparison β confirmed by running both side-by-side
- β Full Z80 instruction set (827 opcodes) including ZX Next extensions
- β Two-pass assembly: labels, forward references, expressions, temporaries
- β PROC/ENDP scoping, LOCAL labels, PUSH/POP NAMESPACE
- β
#initdirective, EQU/DEFL, ORG, ALIGN, INCBIN - β Hand-written recursive-descent parser (~1,750 lines of C)
- β Preprocessor integration (reuses the C zxbpp binary)
The zxbpp C binary is a verified drop-in replacement for the Python original:
- β 96/96 tests passing (91 normal + 5 error tests) β zero skipped
- β 91/91 outputs identical to Python β confirmed by running both side-by-side
- β
All preprocessor features:
#define,#include,#ifdef/#if, macro expansion, token pasting, stringizing, block comments, ASM mode, line continuation,#pragma/#require/#init/#error/#warning, architecture-specific includes - β Hand-written recursive-descent parser (~1,600 lines of C)
git clone https://github.com/StalePixels/zxbasic-c.git
cd zxbasic-c
mkdir -p csrc/build && cd csrc/build
cmake ..
make -j4This builds csrc/build/zxbpp/zxbpp and csrc/build/zxbasm/zxbasm.
# Run all 96 preprocessor tests:
./csrc/tests/run_zxbpp_tests.sh ./csrc/build/zxbpp/zxbpp tests/functional/zxbpp
# Run all 61 assembler tests (binary-exact):
./csrc/tests/run_zxbasm_tests.sh ./csrc/build/zxbasm/zxbasm tests/functional/asmWant to see for yourself that C matches Python? You'll need Python 3.11+:
# Install Python 3.12+ if you don't have it:
# macOS: brew install python@3.11 (or newer)
# Ubuntu: sudo apt install python3
# Fedora: sudo dnf install python3
# Run both Python and C on every test, diff the outputs:
./csrc/tests/compare_python_c.sh ./csrc/build/zxbpp/zxbpp tests/functional/zxbpp
./csrc/tests/compare_python_c_asm.sh ./csrc/build/zxbasm/zxbasm tests/functional/asmThis runs the original Python tools and the C ports on all test inputs and confirms their outputs are identical. π€
The C zxbpp binary accepts the exact same flags as the Python original. You can
drop it into an existing Boriel ZX BASIC workflow right now for the preprocessing step:
# Instead of:
python3 zxbpp.py myfile.bas -o myfile.preprocessed.bas
# Use:
./csrc/build/zxbpp/zxbpp myfile.bas -o myfile.preprocessed.basSupported flags: -o, -d, -e, -D, -I, --arch, --expect-warnings
Supported flags: -d, -e, -o, -O (output format)
The zxbasm assembler is also available as a drop-in replacement:
# Instead of:
python3 zxbasm.py myfile.asm -o myfile.bin
# Use:
./csrc/build/zxbasm/zxbasm myfile.asm -o myfile.binThe compiler frontend (zxbc) still requires Python β for now. π
The big picture: a fully native C compiler toolchain that runs on the NextPi β a Raspberry Pi accelerator board for the ZX Spectrum Next. No Python runtime needed, just a single binary.
Here's how we get there, one step at a time:
Phase 0 β
Infrastructure β arena allocator, strings, vectors, hash maps
β
Phase 1 β
zxbpp β Preprocessor
β 96/96 tests, drop-in replacement for Python's zxbpp
β
Phase 2 β
zxbasm β Z80 Assembler (you are here! π)
β 61/61 binary-exact tests passing
β zxbpp + zxbasm work without Python!
β
Phase 3 β³ BASIC Frontend β Lexer, parser, AST, symbol table
β
Phase 4 β³ Optimizer + IR β AST β Quads intermediate code
β
Phase 5 β³ Z80 Backend β Quads β Assembly + peephole optimizer
β 1,175 ASM tests + 1,285 BASIC tests to pass
β
Phase 6 β³ Integration β All output formats (.tap, .tzx, .sna, .z80)
β Full CLI compatibility with zxbc
β
π Single static binary: zxbasic for NextPi and embedded platforms
Each phase is independently useful β you don't have to wait for the whole thing. After Phase 2, you can preprocess and assemble entirely in C. After Phase 6, Python is no longer needed at all. π―
This port is being developed by Claude (Anthropic's AI coding assistant, model Claude Opus 4.6), directed and supervised by @Xalior.
Claude is analysing the original Python codebase, designing the C architecture, writing the implementation, and verifying correctness against the existing test suite β with every commit pushed in real-time for full transparency.
| Aspect | Python Original | C Port |
|---|---|---|
| Parsing (zxbpp) | PLY lex/yacc | Hand-written recursive-descent |
| Parsing (zxbasm, zxbc) | PLY lex/yacc | flex + bison |
| AST nodes | 50+ classes with inheritance | Tagged union structs |
| Memory | Python GC | Arena allocator |
| Strings | Python str (immutable) | StrBuf (growable) |
| Dynamic arrays | Python list | VEC(T) macro |
| Hash tables | Python dict | HashMap (open addressing) |
| CLI | argparse | ya_getopt (BSD-2-Clause) |
| Path manipulation | os.path |
cwalk (MIT) |
See docs/c-port-plan.md for the full implementation plan with detailed breakdown.
The src/ and tests/functional/ directories are a read-only mirror of the
canonical Python source at boriel-basic/zxbasic.
A weekly CI job checks for upstream changes and opens a PR automatically if
anything has moved. You can also sync manually:
./csrc/scripts/sync-upstream.shThis keeps our Python reference and test suite in lockstep with Boriel's latest, so the C port is always validated against the real thing. π―
Copyleft (K) 2008, Jose Rodriguez-Rosa (a.k.a. Boriel) http://www.boriel.com
All files in this project are covered under the
AGPLv3 LICENSE except those placed in
directories library/ and library-asm, which are licensed under
MIT license unless otherwise
specified in the files themselves.