Agon Light
The Agon Light (2022) is a modern, fully open-source 8-bit computer designed by Bernardo Kastrup and built by Olimex. It pairs a Zilog eZ80 — a pipelined Z80 superset that runs at 18.432 MHz and addresses 24 bits of flat memory (ADL mode) — with an ESP32 "VDP" video coprocessor, and holds MOS and BBC BASIC in ROM so it boots to a friendly > prompt. This build runs in the browser: the eZ80 half is the genuine ez80 core from fab-agon-emulator, running the real Agon Platform MOS 2.3.3 and BBC BASIC, compiled single-threaded to WebAssembly; the VDP half is re-implemented in JavaScript as a text terminal, so the page needs no special server headers. Because the eZ80, MOS and BASIC are the real firmware, the whole machine can be single-stepped, breakpointed and inspected live through a new eZ80 disassembler.
Visit the fab-agon-emulator project on GitHub ↗
Runs on: Web browser
Agon Light Online Emulator
Play Agon Light using JavaScript directly in your browser.
Controls
Configurations
| Configuration | Emulator | Machine | OS | Legal | |
|---|---|---|---|---|---|
| BBC BASIC | Agon Light | Agon Light | grey | Open ⛶ | |
| MOS command prompt | Agon Light | Agon Light | grey | Open ⛶ | |
| Hello demo | Agon Light | Agon Light | open | Open ⛶ | |
| Primes demo | Agon Light | Agon Light | open | Open ⛶ | |
| Squares demo | Agon Light | Agon Light | open | Open ⛶ |
Chips
Notes
Embedding
The Agon is two processors: a Zilog eZ80 running MOS and BBC BASIC, and an ESP32 VDP (a FabGL terminal) they talk to over a serial UART. This build keeps the eZ80 real and emulates the VDP in JavaScript.
The eZ80 is the genuine core. cpu.wasm is Tom Morton's ez80 Rust core (from fab-agon-emulator) with its MOS + host-filesystem layer, compiled single-threaded with Emscripten. It exposes a tiny C ABI; you install the firmware and SD card into its in-memory filesystem, then drive it a slice at a time:
const cpu = await createCPU({ locateFile: f => base + f });
cpu.FS.writeFile('/mos.bin', mosFirmware); // MOS 2.3.3 (console8)
cpu.FS.writeFile('/sdcard/bin/bbcbasic24.bin', basic);
cpu._agon_cpu_init(512); // 512 KiB external RAM
(function loop(){
cpu._agon_cpu_vsync(); // ~60Hz vsync interrupt (HALT wakeup)
cpu._agon_cpu_run_ms(16); // ~one frame of eZ80 time
requestAnimationFrame(loop);
})();
The VDP is JavaScript. The real VDP is a threaded FabGL program that would force SharedArrayBuffer and cross-origin-isolation headers on the whole page. Instead agon-vdp.js models the CPU→VDP byte stream as an 80×30 character terminal drawn to a <canvas>, and answers the few VDP serial-protocol packets MOS waits for at boot (general poll, video-mode info, RTC). The UART is a handful of lines wiring the CPU's host_uart0_send/recv to the terminal:
window.__agon_uart = {
send: b => vdp.send(b), // eZ80 -> VDP: VDU bytes to the terminal
recv: () => vdp.recv(), // VDP -> eZ80: handshake + keyboard packets
cts: () => 1
};
vdp.key('A'.charCodeAt(0), 1); // a keyboard packet [0x81,4,ascii,0,0,down]
Because MOS and BBC BASIC are the real eZ80 binaries, everything you see is authentic — the debugger single-steps genuine eZ80 code.
Debugger integration
The eZ80 is a Z80 superset, so it needed a new disassembler and a new set of core hooks.
A new eZ80 decoder (/debugger/src/cpus/ez80.js, registered as ez80). It delegates the base opcode space to the shared z80 decoder through a rebased 24-bit byte reader, and adds what is eZ80-specific: the size-suffix prefixes .SIS/.LIS/.SIL/.LIL (the redundant self-loads LD B,B/C,C/D,D/E,E, always consumed as prefixes on the eZ80), and the extra ED-page instructions MLT, LEA, PEA, TST, IN0/OUT0, LD rr,(HL) and LD MB,A. Unknown opcodes decode as .byte.
Core hooks added for the debugger. The stock core only ran; it did not expose its state. The Rust core was extended with an EMSCRIPTEN_KEEPALIVE-style C ABI so the shared framework can reach in without changing how the machine executes:
- Registers —
_agon_dbg_reg(i)/_agon_dbg_setreg(i,v)read and write AF, the 24-bit ADL registers BC/DE/HL/IX/IY/SP, PC, MB and the flag byte. - Memory —
_agon_dbg_peek(a)reads a byte side-effect-free (the out-of-bounds marker is cleared so it never trips the machine);_agon_dbg_pokewrites. - Single step —
_agon_dbg_step()executes exactly one eZ80 instruction and returns the new PC. - Breakpoints & watchpoints — held in the core;
run_cycles()stops the CPU before executing a breakpoint address, and the memory-write path flags a watched address. The loop polls_agon_dbg_stop_reason()after each slice (1 = breakpoint, 2 = watchpoint) and pauses.
Because breakpoints and watchpoints live inside run_cycles, they fire at full speed with interrupts still serviced — no slow instruction-by-instruction host loop.
Architecture
The Agon Light (Bernardo Kastrup, 2022; built by Olimex) is a from-scratch open-source 8-bit computer that pairs a modern CPU with a modern video chip yet feels like a 1980s micro:
- eZ80F92 — Zilog's eZ80 at 18.432 MHz: a pipelined Z80 superset with 24-bit flat addressing (ADL mode), so it can see the full 512 KiB of external RAM without banking, plus new instructions like
MLT(8×8 multiply),LEA/PEAand the mixed Z80/ADL size suffixes. It runs the MOS firmware and BBC BASIC. - VDP — an ESP32 running the FabGL library as a serial terminal: it owns the VGA output, keyboard, sound and sprites, and the eZ80 drives it with BBC-style VDU codes over a UART. Here it is re-implemented as a text terminal in JavaScript.
- MOS — the Machine Operating System: a small CP/M-like monitor (here the Agon Platform "console8" MOS 2.3.3) that boots, runs
autoexec.txt, and offers a file system on the SD card plus an API the eZ80 reaches throughRSTcalls. - BBC BASIC — R.T. Russell's BBC BASIC, ported to the Agon by Dean Belfield; the default here is the 24-bit ADL build (
bbcbasic24). It is the whole reason the machine boots to a friendly>prompt.
The Agon is fully open — hardware, MOS and VDP firmware are all public — which is what makes it fair game to self-host in the browser.