SearchA-ZZ › Robotron Z1013

Robotron Z1013

1985 Open source · MIT Online

The Robotron Z1013 is about the simplest 8-bit computer that ever shipped as a product: a bare single circuit board, sold as a kit in East Germany from 1985, built around the U880, an unlicensed Zilog Z80 clone. There is no video chip and no sound; a fixed 32×32 character frame buffer is scanned straight to the screen, and a single Z80 PIO reads an 8×8 keyboard matrix. This emulator is floooh's header-only chips Z1013 core compiled to WebAssembly, self-hosting the monitor and font ROMs, so it boots straight to the resident machine-code monitor with no downloads.

Because the whole machine lives in one WebAssembly memory with small state-sampling hooks, its Z80 registers and 64 KB of memory can be inspected, single-stepped, breakpointed and watched live in the shared debugger. Extra configurations quick-load classic tape programs such as Z1013-FORTH, KC-BASIC and a handful of games.

Visit the project on GitHub ↗

Visit the official site ↗

Runs on: Web browser

Robotron Z1013 Online Emulator

Play Robotron Z1013 using JavaScript directly in your browser.

Configurations

ConfigurationEmulatorMachineOSLegal
Z1013.64 Monitor A.2Robotron Z1013Robotron Z1013greyOpen ⛶
Z1013.01 Monitor 2.02Robotron Z1013Robotron Z1013greyOpen ⛶
Z1013-FORTHRobotron Z1013Robotron Z1013greyOpen ⛶
KC-BASICRobotron Z1013Robotron Z1013greyOpen ⛶
Mazog (game)Robotron Z1013Robotron Z1013greyOpen ⛶
Boulder Dash (game)Robotron Z1013Robotron Z1013greyOpen ⛶
Galactica (game)Robotron Z1013Robotron Z1013greyOpen ⛶
Demolation (game)Robotron Z1013Robotron Z1013greyOpen ⛶

Machines emulated

Chips

Notes

Embedding

The Z1013 core is floooh's header-only chips emulator compiled to WebAssembly with Emscripten. One .wasm holds the Z80 (U880) CPU, the Z80 PIO, the keyboard matrix, the character-video decoder and all RAM/ROM; a tiny .js loader (an Emscripten MODULARIZE factory) instantiates it. We self-host both files plus the monitor and font ROMs, which are baked into the WASM at build time — there is no CDN and no external asset.

Boot. Instantiate the module, call our z1013dbg_init(type) (which runs z1013_init with the monitor + font ROMs), then own a requestAnimationFrame loop that advances the machine one 20 ms frame at a time and blits the decoded framebuffer to a canvas:

createZ1013({ locateFile: function (p) { return SRC + p; } }).then(function (M) {
  M._z1013dbg_init(0);                       // z1013_init, type 0 = Z1013.64
  var imgData = ctx.createImageData(256, 256);
  (function frame() {
    if (M._z1013dbg_exec(20000)) running = false; // run 20ms; stop if a breakpoint hit
    paint();                                    // render() -> read HEAPU8 -> putImageData
    if (running) requestAnimationFrame(frame);
  })();
});

The screen is a 32×32 character framebuffer. The Z1013 has no programmable video: 1 KB of RAM at 0xEC00 holds ASCII codes, and an 8×8 font ROM turns each into a glyph. The core decodes the whole 256×256 monochrome image once per frame; our z1013dbg_render() hook expands it through the 2-colour palette into an RGBA buffer that JS copies straight out of the WASM heap into an ImageData.

Quick-loading tape programs. The .z80 tape images (FORTH, KC-BASIC, games) are injected with z1013dbg_quickload(ptr,len), which copies the payload into guest RAM and jumps the Z80 to the file's execution address:

var buf = new Uint8Array(arrayBuffer);
var p = M._malloc(buf.length);
M.HEAPU8.set(buf, p);
M._z1013dbg_quickload(p, buf.length);
M._free(p);
MemberKindWhat it does
M._z1013dbg_exec(us)exportRun the machine for us microseconds. Returns 1 if a breakpoint/watchpoint was hit. The play loop's advance.
M._z1013dbg_step_insn()exportAdvance exactly one Z80 instruction (ticks the core until the opcode boundary). The single-step primitive.
M._z1013dbg_reg(i) / _z1013dbg_set_reg(i,v)exportSample / poke one Z80 register by index (PC SP AF BC DE HL IX IY IR, the shadow bank, IFF).
M._z1013dbg_read(a) / _z1013dbg_write(a,v)exportSide-effect-free read / write of the CPU-mapped 64 KB address space (RAM + monitor ROM).
M._z1013dbg_key_down(k) / _z1013dbg_key_up(k)exportFeed an ASCII key code into the Z1013's own 8×8 keyboard matrix.
M._z1013dbg_quickload(p,n)exportInject a "KC .z80" tape image at HEAPU8[p..p+n] and jump to its exec address.

The boot script wraps these exports in small helpers on window.EMU_BOOT (reg, setReg, rd, wr, pc, pressKey) plus a transport, so the debugger plug-in never has to know the machine is WASM.

Debugger integration

This is the point of the build: a compiled WASM core with the same live debugger as the JavaScript emulators. A stock chips WASM keeps its CPU state inside the module where JS cannot see it. Rather than add a callback to the per-cycle loop (which would tax the emulator even with the debugger closed), we rebuilt the core with a few sampling functions that copy state out only when called — the debugger calls them about ten times a second and once per Step.

What was changed in the source. One new translation unit, z1013_wasm.c, includes systems/z1013.h with CHIPS_IMPL (so the static core is in the same unit) and adds these EMSCRIPTEN_KEEPALIVE hooks — nothing in z1013.h itself was edited:

// registers: read the z80_t struct fields on demand
z1013dbg_reg(i)        // PC/SP/AF/BC/DE/HL/IX/IY/IR, shadow AF'..HL', IFF1/2
z1013dbg_set_reg(i, v)
z1013dbg_pc()
// memory: side-effect-free, via the core's own mem_rd/mem_wr
z1013dbg_read(addr)
z1013dbg_write(addr, val)
// control
z1013dbg_step_insn()  // tick the static _z1013_tick() until z80_opdone()
z1013dbg_exec(us)      // run us; checks bps/wps only when some are armed
z1013dbg_bp_set(addr, on)
z1013dbg_wp_set(addr, on)
  • Registers are read straight off the z80_t struct (cpu.pc, cpu.af, cpu.bccpu.af2, cpu.iff1) each refresh, and written back through set_reg. The Z80 flag byte is unpacked to S Z H P/V N C.
  • Disassembly uses the shared z80 decoder — the U880 is a Z80 clone, so every opcode (including the DD/FD/ED/CB prefixes) renders correctly.
  • Memory is the full CPU-visible 64 KB read through the core's own mem_rd, so the RAM, the character framebuffer at 0xEC00 and the monitor ROM at 0xF000 all appear exactly as the CPU sees them, with no I/O side effects.
  • Single step is real: z1013dbg_step_insn ticks the core's static _z1013_tick until z80_opdone() — one true Z80 instruction, not a burst.
  • Breakpoints are a 64 KB PC-flag table checked at each instruction boundary inside z1013dbg_exec, and only when at least one is armed — so a running machine with no breakpoints pays nothing.
  • Write watchpoints use the same zero-cost trick: z1013dbg_exec only drops into instruction-by-instruction mode when a breakpoint or watchpoint is armed, and after each instruction it compares the watched bytes against a snapshot — a change means that instruction wrote the address, so it pauses. No memory-write hook sits on the hot path.

Play / pause are pure JavaScript: we own the frame loop, so pause just stops calling z1013dbg_exec and resume restarts it — the transport's isPaused() drives both the debugger's Run window and the shell's Play button.

Architecture

The Robotron Z1013 (VEB Robotron, Riesa/Dresden, 1985) is about the simplest 8-bit computer that shipped as a product: a bare single circuit board sold as a kit, with a U880 (an unlicensed East-German Zilog Z80 clone) at 1 or 2 MHz, 16 or 64 KB of RAM, a 2 KB monitor ROM at 0xF000, and a 2 KB font ROM. There is no video chip — a fixed 32×32 character frame buffer sits at 0xEC00 and is scanned into a monochrome picture. A single Z80 PIO drives an 8×8 (or, on the original .01, 8×4) keyboard matrix; there are no interrupts and no built-in sound. It boots straight to a resident machine-code monitor.

  • z1013.wasm — the whole machine (U880 CPU core, Z80 PIO, keyboard matrix, character-video decoder, flat memory) compiled from floooh's chips C to WebAssembly, with the debug sampling hooks linked in.
  • z1013.js — the Emscripten loader/factory that instantiates the WASM and exposes the exported functions and heap views.
  • The ROMs — the monitor A.2 (Z1013.64, mon_a2), the original monitor 2.02 (Z1013.01, mon202) and the character font — are the machine's original East-German system software, freely redistributed among preservationists (grey); self-hosted, takedowns honoured.
  • The extra configs quick-load .z80 tape images (Z1013-FORTH, KC-BASIC and games such as Mazog, Boulder Dash, Galactica, Demolation), all grey preservation dumps injected straight into RAM.

How to rebuild. Clone github.com/floooh/chips (MIT) for chips/*.h + systems/z1013.h, and github.com/floooh/chips-test for examples/roms/z1013-roms.h (the ROM dumps) and the .z80 tape images under webpage/z1013/. Put z1013_wasm.c (the wrapper with the hooks above) beside them and, with Homebrew Emscripten on PATH, build:

emcc z1013_wasm.c -I chips -O2 \
  -sMODULARIZE=1 -sEXPORT_NAME=createZ1013 -sENVIRONMENT=web \
  -sALLOW_MEMORY_GROWTH=1 \
  -sEXPORTED_RUNTIME_METHODS=ccall,cwrap,getValue,setValue,HEAPU8,HEAPU32 \
  -sEXPORTED_FUNCTIONS=_z1013dbg_init,_z1013dbg_reset,_z1013dbg_exec,\
_z1013dbg_step_insn,_z1013dbg_pc,_z1013dbg_reg,_z1013dbg_set_reg,\
_z1013dbg_read,_z1013dbg_write,_z1013dbg_bp_set,_z1013dbg_bp_clear,\
_z1013dbg_wp_set,_z1013dbg_wp_clear,_z1013dbg_key_down,_z1013dbg_key_up,\
_z1013dbg_quickload,_z1013dbg_disp_w,_z1013dbg_disp_h,_z1013dbg_fb_ptr,\
_z1013dbg_render,_malloc,_free \
  -o z1013.js

z1013dbg_init(type) takes 0 for the Z1013.64 (2 MHz, 64 KB, monitor A.2), 1 for the Z1013.16, or 2 for the original Z1013.01 (1 MHz, monitor 2.02). The two output files (z1013.js, z1013.wasm) are the only build artefacts vendored here.