Debugging and remote control
Phosphoric is not only a machine to play on: it is a bench for understanding a 6502 program. Breakpoints, going back in time, gdb attached to the Oric, and an API to drive everything from outside.
On this page
The built-in debugger
You enter it with F9 during emulation, with -D (--debug) at launch, or on a breakpoint
(--break ADDR).
| Command | Effect |
|---|---|
s, n, c | one instruction, one instruction without entering JSRs, continue |
step-out | run until the current routine returns |
undo | go back: up to 16 snapshots of the processor and RAM |
r, set reg val | view and change the registers |
d [addr] [n] | disassemble, with symbols resolved in operands |
m addr [n] | view and change memory |
a ADDR MNEMONIC [operand] | assemble an instruction into memory |
find B1 B2 …, find "text" | search memory |
b ADDR, b ADDR if EXPR | breakpoint (16 at most), possibly conditional |
br LINE | raster breakpoint: when the beam reaches a line (8 at most) |
w ADDR | write watchpoint (8 at most) |
via, psg, disk, acia, tape, loci | the state of the peripherals |
stack | the stack |
--symbols FILE loads a symbol table (.sym, .lab, EQU, VICE formats): the disassembler and the
traces show them.
In text mode
Built with TUI=1 and started with --tui, the debugger shows in a terminal, in six panes:
registers, stack, disassembly, memory, breakpoints and watchpoints, status.
gdb, lldb, VS Code
--gdb[=PORT] opens a GDB Remote Serial Protocol server (port 1234 by default). You attach gdb,
lldb or a development environment to it:
./oric1-emu -r roms/basic11b.rom --gdb
gdb -ex 'target remote :1234'
(gdb) info registers # A X Y SP PC P
(gdb) x/8xb 0xfffc # the vectors
(gdb) break *0xc000
(gdb) stepi
(gdb) set {char}0x0400 = 0xa9
The machine starts stopped at the reset vector; gdb drives. gdb does not know the mos6502
architecture and may say so, but memory, breakpoints and stepping work; the server describes its
registers through target.xml.
Traces and the profiler
| Option | What it writes |
|---|---|
--trace FILE | one line per instruction: cycles, PC, bytes, disassembly, registers |
--cycle-trace FILE | one line per cycle: access type, address, data, registers, interrupt lines |
--psg-trace FILE | each write to a PSG register, with its cycle |
--serial-trace FILE | bytes sent and received, and the serial port’s signals |
--kbd-scan-trace FILE | each read of the VIA’s port B: to debug a program that scans the keyboard itself |
--profile FILE | hits and cycles per address, opcode histogram, 20 hot spots |
--rom-info [FILE] | an analysed ROM: vectors, subroutines, strings, code and data |
--dump-ram-when ADDR:VAL:FILE | the 64 KB when a memory location takes a value |
Driving from outside: --control
With --control, Phosphoric speaks a text protocol on its standard input and output (logs go to
standard error). It was designed for the OricForge development environment. Three kinds of messages:
| Direction | Prefix | Role |
|---|---|---|
| client → emulator | none | one command per line |
| emulator → client | OK or ERR | the reply |
| emulator → client | EVT | a spontaneous event: ready, stopped, halt |
The commands: hello (capability negotiation), regs, set, read, bread (binary), write,
peek (a peripheral), break, watch, raster, step, next, step-out, continue, pause
(even while running), reset, nmi, load-tap, load-rom, load-sym, load-disk, eject-disk,
disasm, keys, hunt (look for a value that changes), state-save, state-load, and more. All
numbers are hexadecimal, lengths included.
A demonstration client in Python, with no dependency, is supplied
(tests/integration/phos_smoke_client.py).
Driving over HTTP
Built with HTTPAPI=1, --http-api[=PORT] (8888 by default) exposes the same commands over HTTP
and JSON, without duplicating the logic:
| Request | Effect |
|---|---|
GET /hello, /regs, /mem?addr=&len=, /peek/via | read |
POST /keys with text=… | type on the keyboard: a whole BASIC program, remotely |
POST /reset, /nmi, /exec/step, /exec/continue, /exec/pause | act |
POST /tape, /disk/A; DELETE /tape, /disk/A | change media |
POST /break, /watch, /raster, /state/save, /trace | debug |
curl -X POST --data-urlencode 'text=PRINT 2+2\n' http://127.0.0.1:8888/keys
Replies are JSON ({"ok":true,"reply":…}), with CORS. File operations are confined to
--http-api-root FOLDER: absolute paths and .. are refused. Commands run on the emulator’s thread,
at the frame boundary (decision 0002).
Automating with no display
All these pieces combine without a window:
# Load, wait, type, take a screenshot, record the sound, then exit
./oric1-emu --headless -r roms/basic11b.rom -t prog.tap -f \
--type-keys '3000000:RUN\n' --screenshot-at 20000000:end.png \
--audio-wav sound.wav --cycles 20100000
--record FILEand--replay FILErecord and replay keyboard inputs, to the bit: enough to reproduce a defect or keep a regression test.--screenshot-when ADDR:VAL:FILEtakes a screenshot when the program reaches a state (exit code 2 if it never does).--ula-ng-pokeand--type-keys-whenprepare the machine without guessing the right cycle.
That is exactly how the pictures in the gallery were made.