install
source · Clone the upstream repo
git clone https://github.com/mattgodbolt/jsbeeb
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/mattgodbolt/jsbeeb "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.claude/skills/emulator" ~/.claude/skills/mattgodbolt-jsbeeb-emulator && rm -rf "$T"
manifest:
.claude/skills/emulator/SKILL.mdsource content
Headless Emulator
Boot a BBC Micro or Acorn Atom emulator and interact with it via typed commands. Uses
MachineSession from src/machine-session.js.
Usage
Run Node.js one-liners from the jsbeeb project root:
import { MachineSession } from "./src/machine-session.js"; const session = new MachineSession("MODEL_NAME"); await session.initialise(); await session.boot(10); session.drainOutput(); // clear boot text await session._machine.type("PRINT 1+1"); await session._machine.runFor(2000000); console.log(session.drainOutput().screenText); // "\n\n PRINT 1+1\n 2\n>" session.destroy();
Text output vs screenshots
Prefer text output (
drainOutput().screenText) -- it's lightweight and easy to parse.
Only use screenshots when you need to verify graphical output (MODE changes, plotting, character rendering).
// Text (default, preferred): const output = session.drainOutput(); console.log(output.screenText); // Screenshot (only when visual verification needed): import { writeFileSync } from "fs"; const png = await session.screenshot(); writeFileSync("/tmp/screen.png", png); // Then use Read tool on /tmp/screen.png to view it
Available models
- BBC:
,B-DFS1.2
,B-DFS2.26
,MasterMaster-MOS3.20 - Atom:
,Atom-Tape
,Atom-Tape-FP
,Atom-MMCAtom-DOS
Key differences between BBC and Atom
- Atom BASIC needs explicit
as the last line of programsEND - Atom OS commands use
prefix:*
,*LOAD
,*SAVE*CAT - Atom runs at 1 MHz (BBC at 2 MHz), so use longer
valuesrunFor
Tips
runs n CPU cycles. At 2 MHz (BBC): n/2000000 seconds. At 1 MHz (Atom): n/1000000 seconds.runFor(n)
returnsdrainOutput()
and clears the buffer. Call between commands for clean output.{ elements, screenText }- For multi-line programs, type each line separately with
between them.runFor(500000)
/session._machine.readbyte(addr)
for direct memory access.writebyte(addr, val)
returnssession.registers()
.{ pc, a, x, y, s, p }
Example: Atom program
const session = new MachineSession("Atom-Tape"); await session.initialise(); await session.boot(10); session.drainOutput(); for (const line of ['10PRINT"HELLO"', "20END"]) { await session._machine.type(line); await session._machine.runFor(500000); } session.drainOutput(); // clear line entry echo await session._machine.type("RUN"); await session._machine.runFor(2000000); console.log(session.drainOutput().screenText); // HELLO> session.destroy();