---
title: The MCP server
description: The machines handed to an agent over the Model Context Protocol — stood up by name or from a snapshot, run, stepped, stopped, watched, captured where they match and counted, into the answer or into a file, called into, read and written, searched, compared, seen, read back as instructions, typed at, given discs and tapes, their discs written out, saved, kept as they stand and put back there, written to files and stood up again from them, and journaled.
---

An agent has no eyes on the glass and no fingers on the keys, and what it learns of a machine is what it asks for. This server is where it asks. It speaks the [Model Context Protocol](https://modelcontextprotocol.io) over its standard input and output, to one client at a time, and holds as many machines as that client stands up, each behind a handle of its own.

## Starting it

Each release carries the server for each of six platforms with its firmware in `roms` beside it, as the npm package `colophon-mcp` does; `make` builds it from its source as `target/release/colophon-mcp`. The server is started with the directory its firmware is read from, which the package gives it of itself; in an archive's folder it is started as

```sh
./colophon-mcp --roms roms
```

and in a checkout beside the emulator as `target/release/colophon-mcp --roms ../colophon-emulator/roms`.

The directory holds the firmware as a release carries it and as the emulator's `make roms` fetches it: `cpc6128.rom`, `cpc664.rom` and `cpc464.rom`, each the operating system followed by BASIC; `amsdos.rom`, for the machines with a disc interface; and `spectrum48.rom`. A file is read when a machine that needs it is opened, so a missing one is reported then, by name, and nothing else is refused for it. A file must be the size of the firmware it is named for, where the emulator's command line takes the first bytes of a longer one.

A client is given `npx -y colophon-mcp`, or this command line with absolute paths, which it starts and speaks to; [installing it](install.en.md) gives the line for each client.

## What it answers

A request that names its revision in its `_meta`, as the 2026-07-28 revision has every request do, is answered in the revision it names, `server/discover` among them. A client that opens with `initialize` instead is answered in 2025-11-25 or 2025-06-18, as it asks, and in 2025-11-25 if it asks for any other. A request in a revision the server does not speak is refused with the ones it does.

`machine_open` builds a machine by the name the emulator's command line gives it, and wires it as the command line does: the same RAM, the same firmware in the same sockets, and the disc interface where the machine has one. A CPC's 6845 is of the type it is given, numbered as [the Compendium](https://shaker.logonsystem.eu/ACCC1.11-EN.pdf) numbers the five, and of type 0, the first CPCs' type, where it is given none; `set` does not change it. Given a snapshot, it loads it as the command line does, into the machine named, once the firmware is fitted; given a state file, it stands the machine up as the state kept it. `machine_list` names the machines standing, and `machine_close` puts one away once what it was asked before is done. `run` and `step` move a machine, and `call` runs a routine of its own; `inspect` and `set` read and write what it holds, `peek` and `poke` its memory, `find` searches it, and `compare` sets a CPC's RAM beside a state kept from it or beside another CPC's. `monitor` shows its picture, `screen` reads a CPC's memory as one, and `disassemble` reads its memory as instructions. `type` types at it and `run` holds its keys down, `disc_insert` and `tape_insert` give it a disc or a tape, `disc_save` writes one of its discs out as an image, and `snapshot_save` writes it to a file. `state_keep` keeps it as it stands, `state_restore` puts it back there, `state_save` writes a state to a file and `state_drop` lets a state go, and `journal` lists what has been done to it. Each tool's description is the reference for it; an agent reads them from the server. Beside them the server sends its instructions, which a client may set before the model as a session opens: what the tools are for, and what holds across them.

An address or a port is answered in hexadecimal after `&`, `&BB06`, and so is a byte a run gathers, `&3E`, as a CPC's own manuals write them and as `disassemble` writes the numbers in its instructions; an address past `&FFFF`, in a 6128's second 64K, takes five digits. A register of a CPC's sound chip, its PSG, is a number, `7`, as SOFT 968, Amstrad's firmware manual, numbers them, and so is a register of its 6845, `12`, as the chip's datasheet numbers R12. The bytes `peek`, `compare` and `disassemble` answer are pairs of digits, as `poke` and `find` take them. A request may give an address in the forms its schema lists, and where it reads memory, in `peek`, `disassemble` and a capture, `cpu.sp` or `cpu.pc` for the address the register holds; and the journal writes every address and port a request gave, and every byte a condition's `value` and `value_to` give, after `&`, whatever form it came in, `when`'s as pairs of capital digits, or `??`, a space between each, and a range of one address, register or byte as that one, leaving out a test of every byte and a `changes` that is false; a capture's `cpu.sp` and `cpu.pc`, and the values `set` and `call` write, it keeps as they were given. `inspect` and `set` answer the emulator's struct as it holds it, in numbers, and `set` takes hexadecimal for a field of unsigned integers too. An argument of the wrong kind is refused with its name, and a request missing one with every argument the tool requires, where it requires more than one. No schema names a format, since every format JSON Schema defines is of strings and a client need not know any other.

## Time

Nothing but `run`, `step`, `call` and `type` moves a machine, and each answers with the ticks it took, the frames the monitor retraced in them, and the machine's tick since it was stood up: from power-on, or from its snapshot. `state_restore` puts a machine back at a tick it stood at before, and a machine stood up from a state file begins at the state's. The same requests of the same machine give the same answers.

A run goes by frames or by ticks. A frame is the monitor's and ends as its retrace sends the beam to the top-left corner, so a run by frames leaves a whole picture on the monitor; a CPC's frame is whatever its 6845 is programmed to make and may never come, so such a run is held to twice a standard frame's ticks for each frame asked. A run by ticks runs that many, which is how the emulator's command line counts its frames: 79872 ticks to a CPC's, 69888 to a Spectrum's. A step runs instructions, each to its end and through the response to an interrupt taken there, so a step can land in a handler. A run or a step ends between instructions, a few ticks past what was asked if the instruction standing there runs on, unless it ends inside a chain of prefixes, which is no instruction until it is done; the answer then says it stopped unsettled. A run stops sooner at the end of a `HALT` that leaves interrupts disabled, and says it stopped halted: the processor waits there for good, since IFF1 shuts out the interrupt and neither machine raises the non-maskable one, so nothing the machine does of itself carries it further. A run that begins there runs as asked, so the frames a whole picture wants still pass, unless `set` has written in a non-maskable interrupt, which wakes it, or written the processor as no longer halted, which carries it past the `HALT`. Typing takes its ticks all the same.

One request runs at most 1500 frames, or the ticks of 1500 standard frames, which is half a minute of either machine's time, or a minute of a CPC's whose frames do not come; or 100000 instructions, under a second. On the machine the server was written on, 1500 of a CPC's frames run in about six seconds, well inside the minute a client of the protocol's TypeScript SDK waits for an answer.

## Stopping and watching

A run can be told where to stop and what to watch for on the way, and a condition says either in the same words: an instruction about to begin at an address or in a range, a byte read or written there, a port read or written, or a register of a CPC's PSG or its 6845 written. A port is matched on the address lines its device decodes, which the condition names, because a CPC's chips each answer a few lines of the address and not one number. An instruction or a read may also be asked to come from the RAM, or from a ROM: a CPC's Gate Array enables its lower ROM, the firmware, over `&0000` to `&3FFF`, and an upper ROM, chosen by its number, over `&C000` to `&FFFF`, and [while a ROM is enabled the processor reads it, and its writes go to the RAM beneath](https://web.archive.org/web/20260710174425/https://www.grimware.org/doku.php/documentations/devices/gatearray), so that `&0038` is the firmware's while the lower ROM is enabled and the RAM's while it is not. A Spectrum's ROM is below `&4000` for good. A CPC's processor reaches its PSG, the AY-3-8912 that is its sound chip, through the PPI: [port A carries the PSG's data and the top two bits of port C its function](https://web.archive.org/web/20220626135348/https://cpctech.cpcwiki.de/docs/psg.html), and while those bits give WRITE the PSG takes port A's byte into the register it has selected. The byte the PSG is given as its register address names the register in its low four bits; its high four are a chip select, which [General Instrument's data manual](https://raw.githubusercontent.com/floooh/emu-info/93d132fc30509f4ff4c147d53b8f92b81999dcc5/gi/GI_AY-3-8910_Feb-1979.pdf) has the chip match against 0000, so that a byte above 15 selects no register. In [deathsoft and lvd's model of the die](https://github.com/lvd2/ay-3-8910_reverse_engineered/blob/cf8d14db0aec5ae86ed4f4e6e67bf14c40a42d77/rtl/ay_model.v) such a byte also leaves the chip unselected, and so does a reset; [the emulator](https://github.com/tulinmola/colophon-emulator/blob/main/src/ay38910.h) follows it, resetting the chip at power-on, so that an unselected chip takes no write, and a `psg` condition matches none, until a byte from 0 to 15 is given as the address, as the tests find. The emulator runs the PSG at each access to the PPI, by port C as the access leaves it, so each access that leaves port C giving WRITE to a selected chip is a write and a match: an `IN` from the PPI and the PPI's bit set/reset among them, often the same byte to the same register again. A `psg` condition names registers by their numbers, 0 to 15, and its matches give the byte port A gave the PSG, of which a register narrower than eight bits keeps only its low bits. The tests write registers through the routine [cpctech](https://web.archive.org/web/20220626135348/https://cpctech.cpcwiki.de/docs/psg.html) gives for it and through the firmware's MC SOUND REGISTER and SOUND QUEUE, and find the writes where [a listing of the 6128's firmware](https://github.com/Bread80/CPC6128-Firmware-Source) makes them, in its order. A run given conditions in `until` stops at the first match: before the instruction, so that the machine stands at it unrun, or at the end of the instruction that made the access, so that it stands between instructions as every run leaves it. A run that begins on an instruction it was stopped before runs that instruction first. An instruction that repeats, `LDIR` and its kin, begins again at its own address each time it repeats, since [the program counter is decremented by two and the instruction is repeated](https://www.zilog.com/docs/z80/um0080.pdf), and is matched each time: an `LDIR` that copies five bytes is five matches, as the tests find. Conditions in `gather` are listed rather than stopped at, each match with the tick it came at, the address, the port or the register and the byte, where the instruction that made it begins, and where the monitor's beam then stood; a port read gives the byte the processor took, which a CPC's board can change while it holds the read. On a CPC a match also says which memory held its instruction or its byte, where the address alone does not: `rom` names the lower ROM, or an upper ROM by its number, as [WinAPE's debugger](https://web.archive.org/web/20160525234943/http://www.winape.net/help/breakpoints.html) names an upper ROM's block, and `ram` the RAM's own address, where a 6128's RAM configuration pages it in elsewhere, as `peek`'s `ram` counts it; `pc_rom` and `pc_ram` say the same of the instruction that made an access. A number selected with no ROM fitted is answered by ROM 0, as [Kevin Thacker's cpctech](https://web.archive.org/web/20190604090025/http://cpctech.cpc-live.com/docs/exprom.html) says BASIC is, and the tests find the 6128's firmware walking its ROMs as [its listing](https://github.com/Bread80/CPC6128-Firmware-Source) walks them, from ROM 15 down to 1, reading ROM 0 at each number but 7, where AMSDOS answers, and starting BASIC and then AMSDOS at `&C006` of each; they find the firmware's interrupt entry at `&0038` run from the lower ROM and from the copy the firmware makes of it in RAM, and a condition on the RAM's stopping only at the copy. A list fills at 128 matches, keeps the rest of the instruction that made the 128th and of the response to an interrupt taken at its end, and stops the run where every run stops, so that the next run gathers on from there and nothing is lost between them. The matches a run gathers are those a run stopped at each of them in turn would stop at, and the tests hold the two to each other. Given `gather_last`, a run keeps the last 128 matches instead, and runs on past them as though nothing filled, so that one stopped by `until`, or at its end, lists what led there; the tests hold it to the tail of a gathering carried through many runs, and find the last instructions of a routine in the 48K ROM where its disassembly ends it.

A CPC's 6845, its CRTC, answers an I/O request with A14 low, [its registers selected through `&BCxx` and written through `&BDxx`](https://web.archive.org/web/20220626145908id_/https://cpctech.cpcwiki.de/docs/crtcnew.html), and since it takes its direction from the port's address, [and not from the processor's RD and WR](https://shaker.logonsystem.eu/ACCC1.11-EN.pdf), an `IN` from either port selects or writes too: an `IN A,(n)` gives it A, the top half of the port's address, and the emulator gives it the top half of the port's address for every `IN`. A `crtc` condition matches each write to a register, by the number the chip's address register holds, 0 to 31, of which [R0 to R17 are the chip's and 18 to 31 none](https://web.archive.org/web/20220626023757id_/https://cpctech.cpcwiki.de/docs/hd6845s/hd6845sp.htm), and gives the byte the chip was given, of which the register keeps as many bits as it has. It matches where the chip takes the write, which on types 3 and 4 is [an `OUT (C),r`'s 4th µs rather than its 3rd](https://shaker.logonsystem.eu/ACCC1.11-EN.pdf), while an `OUT (n),A` reaches every type in its 3rd µs and an `OUTI` in its 5th. A register selected is no write, and `in` and `out` on `&BC00` with the mask `&4300` find it. The tests write registers with `OUT` and with `IN A,(n)` and select one with `IN A,(n)`, and find the firmware loading its table as it starts and setting the screen's start through MC SCREEN OFFSET where [its listing](https://github.com/Bread80/CPC6128-Firmware-Source) does.

A condition may also name the bytes it matches. `value`, with `value_to` for a range, matches an access only where its byte lies between them: the byte read or written, a port's written, or a register's given, so that one condition on the Gate Array's port picks out the writes that set its mode, its ROMs and its interrupt counter, `&80` to `&BF`, from those that choose its pens and inks, [each command named by the byte's top two bits, bit 5 having no effect](https://web.archive.org/web/20260710174425/https://www.grimware.org/doku.php/documentations/devices/gatearray). `changes` matches a write, to memory or to a register of the PSG or the 6845, only where it leaves the byte holding other than it held, a register as it keeps it, and with `value` on memory it is the write that makes the byte that: the moment a game's counter becomes 6, rather than every frame it is written 6 again; a register's `value` is the byte given it, of which it may keep only some bits. A write beneath a CPC's enabled ROM is held to the RAM it lands in, and a write to a Spectrum's ROM changes nothing. A port read's byte is not tested, on either machine, since a CPC holds the read over several ticks and the processor takes the byte the last of them leaves, where a condition is decided on the first.

A condition may also ask what memory holds as it matches. `when` names an address and the bytes that must stand there, written as `find` takes them, `??` for a byte that may be anything, eight at most, so that a routine run while a game is in one state, or the line a BASIC program has come to, is one stop rather than many, with a reading after each. The bytes are read as the processor reads them, through a CPC's banking and its ROMs, at the moment the match is made: for an instruction, before it begins; for an access, on the tick it is made, so that a write's own byte has landed and the bytes the same instruction writes after it have not. A stop or a capture on an access is made at the end of its instruction, so either may find bytes other than those `when` read. A word is written a byte at a time, and is asked for on the write of the byte that goes last, with `when` on both: `LD (nn),HL` [loads L to the address and H to the one after](https://www.zilog.com/docs/z80/um0080.pdf), [the first before the second](https://worldofspectrum.org/faq/reference/48kreference.htm), as the emulator writes them, so a word it writes is asked for on its high byte, and a condition there matches every such write, the same value written again included, and no write of the low byte alone, while a `PUSH` [writes the high byte first](https://www.zilog.com/docs/z80/um0080.pdf) and is asked for on its low byte. `changes` on that byte tests that byte alone, and passes over a word whose last byte already stood. A condition on both bytes matches the write that completes the value, whichever byte goes last, but also the first of the two writes wherever the byte it writes, beside the other as it still stands, reads as the value asked for, whatever value the word is being given, so that both writes of one word may match. The tests hold a stop on `when` to the first of the stops a client makes without it, reading memory after each, at which the bytes stand, a gathering on `when` to the stops it makes one run at a time, and a count to their number. They find `when` read before an instruction runs, and after a write's own byte has landed but before the next its instruction writes, which a stop there then finds; a condition on the high byte matching once, where a program counting a word up writes the value; one on both bytes matching there, again where a later value's low byte meets the high byte still standing, and at both writes of one word where its high byte already stood; and a CPC's `when` reading its lower ROM where the ROM is enabled.

A condition in `until` or `gather` may also capture the machine at each of its matches, so that what would be read after a stop comes with the match: values of the processor, each by the path `inspect` reads it at under `cpu`, and spans of memory, each read as `peek` reads it, from an address or from where SP or PC points as the capture is taken, so that a match carries the top of the stack, a return address among it, or the bytes of the instruction it stands at. An instruction about to begin captures the machine it finds, as a stop before it leaves the machine. An access captures the machine as the instruction that made it leaves it at its end, where a stop on the access stands, and every access of one instruction alike, so that both writes of a `PUSH` capture the stack pointer it leaves; inside an instruction the processor's registers stand in the emulator's own order of working, which no documentation grades. An interrupt taken at the end of the instruction is accepted before the capture and answered after it, and the stop runs on through the response, so there the two differ: the capture has the instruction's program counter and stack, but IFF1 clear, as [the processor clears it as it takes the interrupt](https://www.zilog.com/docs/z80/um0080.pdf), and IFF2 with it if the interrupt is INT, and the emulator's own machinery for the processor standing as the response begins. An access the response itself makes captures the machine at the response's end. A match answers what it captured in its `captured`, and a match of several conditions captures what each of them names. A gathering any of whose conditions captures fills at 32 matches, or with `gather_last` keeps the last 32, since each carries more; with a `path` it fills at 1048576 as any gathering does, each line with its capture. The tests hold every capture across a stretch to what a stop at each of its matches reads back with `inspect` and `peek`, and to what a twin gathering over the same stretch captures; they find the capture before the response to INT and to an NMI written in with `set`, and TXT OUTPUT, at the address [the 6128's firmware listing](https://github.com/Bread80/CPC6128-Firmware-Source) gives, handed each character BASIC's `PRINT` prints, one call at a time, in A.

Conditions in `count` are counted rather than listed: each place that matches, an instruction begun at an address, a byte read or written there, a port by the whole of its address, or a register of the PSG or the 6845, is answered once, with how many times it matched, executes first, then reads, writes, ports, and the PSG's registers and the 6845's, each kind by address or register, or by pc. A place in memory is a byte of it, so that on a CPC the lower ROM's `&0038` and the RAM's are two places, as are a 6128's two banks of RAM at one address, and a byte the processor reaches at two addresses, as a 6128's RAM configurations can page one, is one place, told by the address of its first match. Each is told by its first match, with its tick and where the instruction that made it begins, and an instruction with the bytes the processor fetched as it first began there, so that code a program writes, runs and wipes again still says what ran. A count asked to run over `&4000` to `&BFFF` says which of a program's instructions ran, how often and when each first did, and one over a single address how many times the instruction there began. A count by `pc` counts an access by where the instruction that made it begins rather than by its address, so that a count of the writes to a screen says which instructions wrote it and how many writes each made; the response to an interrupt is no instruction, so what it does, the return address it pushes and the vector it reads in mode 2, is then not counted. The tests count the 48K ROM clearing its screen so, and find the four instructions its disassembly names, each first writing where the disassembly says. Counted by address, a port is the whole address on the bus, whatever bits the condition's mask decodes, so a port whose address carries the value written, as `OUT (n),A` puts A on the high byte, is counted at as many places as there are values. It fills at 256 places, keeps the rest of the instruction that filled it, and stops the run where a full gathering does; the next run counts afresh from there. A count is a gathering tallied by place, by address or by `pc`, and each place is told by the first match gathered there since the run began; the tests hold the two to each other.

An answer is bounded to what a model can take in, and a question about every frame of a stretch is bigger than that. Given a `path`, a run or a call writes what `gather` and `count` find to a new file instead, a line of JSON for each match and each place or range, as the answer would list it, after the list it belongs to, the matches first, for a client to read with tools of its own. The answer names the file by its path and its SHA-256 and says how many lines each list holds. Written so, a count never fills, so a run by frames counts all its frames, a map of every byte a program reads and writes over them; and a gathering fills at 1048576 matches, some 100MB, or some 750MB where each captures all a request may name, where an answer's fills at 128, which holds a trace of every instruction a program runs over a hundred frames or so, and its writes to the PSG over many more. The file is made before the run begins, only where no file is, and a request whose file cannot be made is refused with the machine as it stood. It stands empty at its path through the run, and is written once the run is over, before the answer, so a file holds what it is to hold once an answer names it by its hash. One that cannot be written whole then is taken away again, and the request, which is not taken back, is refused with how far it ran and where the machine stands, and journaled without the file. The tests hold a file to what runs that fill and go on list over the same stretch, and to what a twin's answer lists.

A count by range is a count by address told shortly, for a question about which stretches of memory a program runs, reads or writes rather than about each byte. Places of one kind whose bytes follow one another with no gap between them make one range, both at the addresses the processor first reached them at and in the RAM or the ROM that holds them, so that no range runs on from the lower ROM's `&3FFF` into the RAM's `&4000`, nor from the block a 6128 pages in at `&4000` into the first 64K's at `&8000`, nor from one of its blocks into the next where the processor reads them apart. An instruction covers the bytes it fetched before the first that does not follow the one before it in the same RAM or ROM, as a block a 6128 pages in at the next 16K may not, so code laid end to end is one range, an instruction run from inside another's operand belongs to that one's range, and an instruction jumped over ends it. An instruction longer than four bytes, which only a chain of prefixes makes, is known by its first four alone, so the one after it begins a range of its own. A byte read or written is one byte, and ports and registers follow one another by number. Each range is told by its first place and its last, how many places it holds and how many times they matched together, and not by when each first matched or the bytes it ran with, which a count by address tells. A count by range never fills, since its places are folded only once the run is over: its answer lists the first 256 ranges and says how many there were, and a file takes them all. The tests fold what a twin counts by address over the same stretch into the ranges; hold a short program's ranges to the lengths of its instructions, its ports and the PSG's and the 6845's registers to their numbers; keep ranges apart where either machine's ROM ends at `&3FFF`, between a 6128's blocks, and where an instruction runs on into a block paged in; and find the 48K ROM's routine that clears the screen as the two stretches of code its disassembly lays out, with the one instruction between them that the routine jumps over, and the screen it clears as one range.

A read is the program reading memory as data. The bytes of an instruction are never one: its opcodes are told by the processor's M1, and its operands by the emulator, which knows them where the bus does not. The return address an interrupt's response pushes names no instruction, because none made it.

`call` runs a routine the machine holds as its own `CALL` would: the program counter is pushed where the stack pointer stands, and the call ends when the processor comes back to it with the stack pointer where it was, which is where the routine's own return leaves it. A machine called into thus stands where it stood, with whatever the routine left in its registers and memory, and a processor halted on `HALT` is halted again once the routine has returned within the call. The values given with a call are written first, as `set` writes them. A routine that halts on `HALT` with interrupts disabled stops the call there, as it stops a run, and the call says so at once rather than waiting out its frames: a routine that waits on `HALT` for the next frame, called with IFF1 clear, as inside an interrupt's handler or after `DI`, waits for good. A call is refused, and the machine left as it was, where the address pushed would not read back, under a CPC's enabled ROMs or in a Spectrum's, and inside a chain of prefixes, which is no place between instructions. The routine is read where the processor reads its address, so beneath a CPC's enabled ROMs a call runs the ROM and not the RAM there, as the tests find with the ROMs enabled and with them paged off. Interrupts stay as they stand, so their handlers run within a call only if IFF1 is set: a run by frames ends just past the retrace, which on either machine's firmware comes inside the handler of the frame's interrupt, with IFF1 clear, and a routine called there runs with no interrupt at all unless it enables them itself, as the tests find on both.

## What a machine holds

`inspect` reads the emulator's own struct for the machine, `cpc_t` or `spectrum_t`, by the path a C expression gives a field of it: `cpu.pc`, `crtc.registers[12]`, `fdc.units[0].head`. Its fields keep their headers' names, and a C enum's value is its enumerator's name; pointers are not followed, and `peek` reads memory instead. The fields a program works with most, the chips' registers and counters, are named part by part in the tool's description; most others are the chips' inner workings, the board's links and drives, or the emulator's machinery for stepping the chips a tick at a time, which no silicon holds as such.

`set` writes by the same paths, every value given or none. The core indexes and sizes by some of its fields without checking them, because nothing it does takes them out of range: the size of the RAM, the RAM configuration of a 64K machine, the pen that picks one of the Gate Array's seventeen inks, the PSG's selected register, the disc controller's counts of the bytes it has taken and given and the drive its command chose, the monitor's size, and the processor's count of the cycles under way. A value past those is refused, with the range that would do, and so is a WAIT written on a Spectrum's bus, which nothing there drives and which would hold its processor for good, and any other type for a CPC's 6845, in `crtc.type` or in the copy a line-end room keeps to take back; the machine stands as it was. A CPC's memory map is worked out again after every write, and a Spectrum's ULA stood at the tick of the frame its `frame_tick` names, from which its beam is reckoned.

`peek` and `poke` reach memory as the processor would: through a CPC's banking and its ROMs, so that a write under a ROM lands in the RAM beneath it, and into a Spectrum's ROM not at all. Both also reach a CPC's RAM itself, by the RAM's own addresses as a base given to `screen` counts them, whatever is paged in: the RAM beneath the ROMs, and a 6128's second 64K at `&10000` to `&1FFFF`. A run stops under whatever banking the program left, and a 6128 game that keeps its sprites in the second 64K may leave a block of it over its own variables, so a CPC's answer to an address names the memory its first byte was in, as a match names it: the ROM, or the RAM by its own address where that is not the address given. Past the next 16K boundary a span may reach another memory, `&0000` among them where one read from where SP or PC points wraps, and the answer does not name it. `peek` from an address given reads nothing past `&FFFF`; from where SP or PC points it runs on past it to `&0000`, as the processor's own addresses do, and so does a capture's span, since where a register points is not known until the read is made.

`find` searches memory as `peek` reads it, through the banking and the ROMs or, given `ram`, in a CPC's RAM by the RAM's own addresses, for bytes or for text, and answers the addresses where they lie, lowest first, 256 at most, with how many there are in all and where to search again for the rest. Its bytes take `??` for any byte, and since [an address follows its opcode low byte first](https://www.zilog.com/docs/z80/um0080.pdf), the two bytes of an address find every `CALL`, `JP` and `LD` that names it, and every table that holds it. Its text matches each character on the low seven bits of a byte, because both machines' ROMs end many of their words by setting the eighth bit of the last byte: the 48K ROM its keywords and its messages, as [The Complete Spectrum ROM Disassembly](https://github.com/skoolkid/rom/blob/20260409/sources/rom.skool) writes them, and the 6128's firmware the words of its cassette manager's messages, as [its listing](https://github.com/Bread80/CPC6128-Firmware-Source) writes them. The tests find three routines of the 48K ROM wherever the disassembly names them, and nowhere else, and a keyword and a message of the 48K ROM and the first word of the cassette manager's messages where the listings put them; and they find bytes beneath a 6128's lower ROM and at the top of its second 64K, by the RAM's addresses, where its banking put them, and with the ROMs disabled the same bytes in its base 64K by either way of asking.

`compare` sets a CPC's RAM beside a state kept from it, or beside another CPC's RAM as that machine stands, byte by byte and by the RAM's own addresses, whatever either has paged in, and answers where they differ: each run of neighbouring bytes that differ, lowest first, with the bytes on both sides, 256 runs and 2048 bytes at most, with how many bytes differ in all and where to compare again for the rest. A state kept before a run or a call, compared after it, gives the bytes the run or the call changed, and so narrows the search for a program's variables to those; a byte written with the value it held changes nothing, and is not listed. Two RAMs of different sizes are compared as far as the smaller reaches. The tests find the bytes the 6128's firmware writes for a character just where SOFT 968 lays its matrix out on the screen, and nowhere else on the screen, both against a state kept before and against a second machine made alike; and they find a 6128's RAM compared alike under RAM configurations 0 and 4, beneath the lower ROM and on from the base 64K into the second.

## The picture

`monitor` answers with the picture on the machine's monitor as a PNG, beside the JSON that says which machine, at which tick, how large, and where the monitor's beam stands. The picture is cut and drawn as the emulator's command line cuts and draws its screenshots, so that one written there and one shown here can be laid over each other and compared pixel for pixel, which the tests do: a CPC's 768 by 544, a sample of the raster to each pixel across and each line drawn twice, and a Spectrum's 352 by 264. Each pixel's index in the PNG's palette is what the machine put on the cable, a CPC's hardware colour code or a Spectrum's four bits of brightness and colour, and the palette is the colour each stands for, so an agent that reads the file gets the machine's numbers back. The beam paints the picture as the machine runs, so after a run by frames that ran all its frames it is whole, and after any other run, a step, a call or typing the lines above the beam are this frame's and those below it the frame's before; the answer's `whole` says whether the beam is partway through the picture. A frame may also never end. [A CPC's 6845 gives a line sync only where its character counter meets R2](https://shaker.logonsystem.eu/ACCC1.11-EN.pdf), so one set with R0 below R2 gives none once its counter has come round, and the Gate Array, which begins and ends its frame sync by counting the 6845's line syncs, never begins one, or never ends the one it was giving: a run by frames ends at its limit having run none, and the picture keeps what the frames before it painted, however long ago, as the tests find of a frame sync that never ends. A real monitor's oscillators would run on without sync and roll the picture, which [the emulator's monitor](https://github.com/tulinmola/colophon-emulator/blob/main/src/monitor.h) does not model. A line that a frame shorter than the picture never reaches keeps what an earlier pass left there.

`screen` reads a CPC's memory as a picture, as [the player's screen panel](https://github.com/tulinmola/colophon-player/blob/main/docs/debugger/screen.en.md) reads it and with the same names for what it is given: `video`, the way the CPC's video hardware reads the screen; `linear`, a flat buffer; or `columns`, one that runs down before across, so that a sprite sheet, a font or a buffer built off the screen shows its shape when read in the layout it was written in. Whatever is not given is read from the machine as it stands: the start, the width and the height from the 6845, and the mode in force and the sixteen inks from the Gate Array, so that a screen asked for nothing draws the memory the 6845 is displaying, at the scale `monitor` draws it, and the tests hold the two to each other in each of the four modes, and with the 6845 counting rows of five lines and of sixteen, starting partway into a 2K, and carrying a screen of 32K from one 16K into the next. It reads the RAM as the video hardware does, whatever ROM the processor sees over it: the `video` reading the base 64K, and the others a 6128's second 64K as well, at `&10000` to `&1FFFF`. Each pixel's index is its pen rather than a colour, so the pen a sprite's mask keeps clear stays one number in the file whatever ink it is given.

## Reading memory as instructions

`disassemble` reads memory as the instructions the processor would run from an address on, or from where PC or SP points, each with its address, its bytes and what they say, through the banking and the ROMs as `peek` reads it. Given `ram`, it reads a CPC's RAM by its own addresses instead, a 64K of it at a time, the base 64K or a 6128's second, and lists it as it lists that 64K when the processor reads it alone, with the ROMs disabled and [the RAM configuration 0, or 2 for the second](https://web.archive.org/web/20260511001355/https://cpctech.cpcwiki.de/docs/rampage.html): the same bytes and the same texts, a relative jump's target and a firmware name among them, wrapping within the 64K, each instruction named by its address in the RAM, so that what lies beneath the ROMs, or in a bank not paged in, is listed without paging anything. The tests hold a listing of either 64K to the processor's own of it so paged. It writes Zilog's mnemonics, numbers in hexadecimal after `&`, and the target of a relative jump worked out, and names the undocumented instructions as [the player's disassembly](https://github.com/tulinmola/colophon-player/blob/main/docs/debugger/disassembly.en.md) names them, `ED FF` among them as WinAPE's `BRK`. An instruction is what `step` runs, so a chain of `DD` and `FD` prefixes is one instruction, ruled by its last prefix. On a CPC, the two bytes after a firmware restart that takes an address, LOW JUMP (`RST &08`), SIDE CALL (`RST &10`), FAR CALL (`RST &18`) or FIRM JUMP (`RST &28`), are listed with it as the word they hold, `DEFW &93FE`, as [SOFT 968](https://archive.org/details/SOFT968TheAmstrad6128FirmwareManual) writes a FAR CALL's, and the listing reads on after the word, as it reads on after the `JP` or the `CALL` SOFT 968 likens each restart to. A restart and its word are listed together or not at all, and the word is not counted among the instructions asked for. That holds while the first three bytes at the restart's address, in the memory the listing reads, are the lower ROM's: the ROM itself, or the RAM beneath it, where the firmware keeps a copy. A program that disables the ROM may put a restart of its own there, as Aliens makes `RST &28` its key test, and its restart is listed alone. `make test-firmware` holds a listing of each CPC's whole main jumpblock, a run of LOW JUMPs and FAR CALLs, to its entries, through the ROM and beneath it. On a CPC, an instruction that jumps or calls to an entry of the firmware's jumpblocks, the main one from `&BB00`, the high kernel's from `&B900` and the indirections from `&BDCD`, carries the entry's name as SOFT 968 lists it, KM WAIT KEY for `CALL &BB18`, wherever the machine's firmware has that entry: a cpc464's firmware, V1.0, has none of the main jumpblock's from KM SET LOCKS on, nor KL SCAN NEEDED or KM SCAN KEYS, and a cpc664's, V1.1, has no KL BANK SWITCH. The name belongs to the address, and is given whatever the memory there holds, because a program patches an entry to take it over, as AMSDOS takes over the cassette's, and a call to the entry is a call to it still. `make test-disassembly` holds every instruction of the 48K ROM to [The Complete Spectrum ROM Disassembly](https://skoolkid.github.io/rom/), and every opcode to SkoolKit's own disassembler, save where the player's names differ from SkoolKit's, and where SkoolKit leaves a `DD` or `FD` that changes nothing as a byte of its own, which the listing joins to the instruction after it as the processor does; it holds every name to the number and the address SOFT 968 gives the entry and to the spacing SOFT 968 gives it most, and the main jumpblock's entries each version has to what SOFT 968 says of each. `make test-firmware` holds which addresses are named to the jumpblocks each CPC's firmware sets up as it starts.

## Keys and typing

A machine is reached through its keyboard as a person reached it, a key at a time, and what a key means is its firmware's business. `run` takes keys to hold down from its start to its end, on top of whatever the keyboard holds already, and leaves the keyboard holding what it held before; a stretch of a game played with a direction and fire held is a run with those keys, and the next stretch another run. A key goes by the name [the player](https://github.com/tulinmola/colophon-player) prints on it, after the keys SOFT 968, Amstrad's firmware manual, draws for a CPC, `SPACE`, `COPY`, `f5`, and `(FIRE 2)` for what the manual calls joystick 0's main button, though some games read `(FIRE 1)`, and after the legends on a Spectrum's, `CAPS` and `SYM` among them. A CPC's key may also be given by the number its firmware gives it, its line of the matrix times eight and its bit, which is the number a game's own code tests.

`type` types text as the emulator's command line types it with `--type`: each character's key held down, with whatever shift it needs, for 3 standard frames, then let go for 3 on a CPC and 6 on a Spectrum. Those are the command line's counts, measured there: [a CPC's firmware marks a key let go only once two of its scans, a fiftieth of a second apart, have missed it](https://archive.org/details/SOFT968TheAmstrad6128FirmwareManual), which also leaves a key let go at the end of a run down to the firmware there and a frame later, and up a frame after that, as the tests find with KM TEST KEY; and a Spectrum's takes the same key again only once five of its scans have passed. A newline is RETURN or ENTER, and on a CPC a tab, an escape and a backspace are TAB, ESC and DEL. A shift goes down beside its key, so a program that reads the keyboard's matrix itself, rather than asking the firmware, finds one key more than the character typed, and text for such a program is typed in lower case. A text is ASCII, at most 250 characters on a CPC and 166 on a Spectrum, 1500 frames' worth, and one holding a character no key types, alone or with a shift, is refused before a key goes down; a key that types no character, or a character outside ASCII such as a CPC's £, is held through `run`. Keys the keyboard holds already, as `set` writes them, stay held through the typing, where the command line lets every key go. The keys go down and come up between instructions, a few ticks at most from where the command line puts them, counting from where the typing began, and the typing ends at the end of the instruction standing when its frames' ticks have run out, or where a stretch of it ends inside a chain of prefixes, as a run does. The tests type `PRINT 2+2` at each CPC and `p2+2` at the Spectrum, after the frames the command line waits, and the screen's memory is what the command line's typing leaves there; the firmware's answer, read in its own font, is 4. They hold keys through a run and BASIC's INKEY reads them as the CPC 6128's manual says it does, and a Spectrum held at CAPS and SPACE breaks into its program with report L.

A Spectrum's ROM reads a letter typed where the cursor shows K as a keyword, so `p` at the start of a line is PRINT and `j` is LOAD. Neither firmware takes a key before it has started: the command line waits 78 frames on a CPC, and 128 on a Spectrum, whose ROM takes none before frame 85.

## Discs and tapes

A disc or a tape is given as a file, which is read whole and copied into the machine, and named in the answer by its SHA-256, with the tick at which it went in. Nothing the machine does reaches the file.

`disc_insert` puts a DSK image, of the original layout or the extended one, in a CPC's drive A or B, with room past it for every track the medium can hold to be formatted once more, a track the image never had among them, as the command line gives one with `--disc`. A disc may go in with its write-protect tab set, which the drive tells the disc controller as a real drive does, and the controller then writes and formats nothing on it. What the machine writes lands in its copy, and is gone once another disc takes the copy's place, as a game's second side does when the game asks for it, save what a state kept while it stood in the drive brings back as it stood then. A drive whose motor turns reports a disc ready at the third index pulse after it goes in, 600 ms, which [the emulator's drive](https://github.com/tulinmola/colophon-emulator/blob/main/src/drive.h) takes for the CPC6128 service manual's two revolutions; AMSDOS leaves the motor turning for [5 seconds after it reads](https://web.archive.org/web/20260721051000/https://cpctech.cpcwiki.de/docs/amsdos.asm), and a disc put in meanwhile and asked for sooner is reported missing. `disc_save` writes the disc in a drive to a new file, as the command line does with `--save-disc`: an image of the extended layout as the emulator writes one, naming itself its creator and recording where each sector lies, so that a disc nothing wrote to may come out with a hash other than that of the file it went in from, and the answer says whether the machine wrote to it since it went in. The image holds the disc as it stands at that tick, and a save the program has not finished is on it as far as it has come. A cpc664 and a cpc6128 take discs. A cpc464 does not: it is built here as the command line builds one given no disc, without the disc interface, and nothing here fits one afterwards. The tests save a one-line program to a blank disc they write in SOFT 968's DATA format, and AMSDOS catalogues it as the CPC 6128's manual shows it does, `EXAMPLE.BAS 1K` and `177K free`; the same file, put in again, is still blank, and the image `disc_save` wrote, put in after it, is catalogued as the disc was; and an image `disc_save` writes after a program is typed and saved is, byte for byte, the one the command line writes with `--save-disc` after the same typing. Put in write-protected, it takes no SAVE: the controller reports the drive's tab to a program that asks, and refuses the write, and AMSDOS says `Drive A: disc is write protected`, [as its own message table spells it](https://web.archive.org/web/20260721051000/https://cpctech.cpcwiki.de/docs/amsdos.asm).

`tape_insert` puts a tape in a machine's deck: a `.tzx` on either machine, a `.cdt` on a CPC and a `.tap` on a Spectrum, whose bare blocks carry only the Spectrum ROM's timings. A rewound cassette has leader tape in front of its recording, which an image seldom keeps whole, and [the emulator plays it first](https://github.com/tulinmola/colophon-emulator/blob/main/docs/machine.en.md), as 5.7 seconds of silence, less whatever silence the image keeps before its first edge.

A Spectrum has no motor line, so its deck presses PLAY as the tape goes in, and the tape plays from that tick on, whatever the machine does; after the leader tape, a header's pilot tone lasts 5 seconds. So a tape goes in just before the program asks for it, as `LOAD ""` does. The tests write a tape of one BASIC program as the 48K reference describes what SAVE puts on tape, hear its first edge 5.7 seconds after PLAY, and `LOAD ""` reads it and runs it from its autostart line.

A CPC's tape moves only while the machine runs the cassette motor, on bit 4 of its PPI's port C, and [its reel follows the line 50 ms late](https://github.com/tulinmola/colophon-emulator/blob/main/docs/machine.en.md), from rest as the tape goes in. That is the floor that Opera Soft's cassettes set and no measurement, so a tape that wants its reel to run on longer after the motor stops may fail here where a real deck reads it. The firmware runs the motor only to read or write, so a tape may go in at any time before `RUN"` asks for it; a cpc664 and a cpc6128 read tapes once `|TAPE` has turned the firmware's cassette routines back to the cassette, and the firmware asks for PLAY and a key before it starts the motor. A TZX block that stops a Spectrum's tape is passed over on a CPC, as [the format's notes for the Amstrad](https://web.archive.org/web/20260721052615/https://cpctech.cpcwiki.de/docs/cdtcpc.html) ask: a pause of none "SHOULD NOT 'Stop the Tape'", and a stop "if in 48K Mode" "MUST NOT have any effect". The tests write a tape of one binary file as SOFT 968 describes the cassette manager's records, find it standing while the motor is off, for longer than the leader tape and its first record, and hear its first edge the reel's lag and the leader tape after the motor starts; each CPC's `RUN"` reads it, the tape put in at power-on, and runs it from its entry address.

## Saving a machine

`snapshot_save` writes a machine to a file in the format the command line writes with `--save` and `machine_open` reads: an SNA of version 1 for a CPC, the 48K `.sna` for a Spectrum. It writes only where no file is, so that nothing is ever written over, and the answer names the file by its SHA-256, with the machine's tick. A snapshot keeps less than the machine, as the last section says; a state file keeps all of it, for the build that wrote it, as the next section says. A snapshot keeps the machine's RAM whole, so the file is an image of it for tools of the client's own: a CPC's all of it after a header of 256 bytes, by the RAM's own addresses, and a Spectrum's after a header of 27, from `&4000`, save the two bytes below the stack pointer, which carry the program counter. One byte of a CPC's header is written against the format when its sound chip is unselected, by the emulator and so by the server: [the format's notes](https://web.archive.org/web/20260721051400/https://cpctech.cpcwiki.de/docs/snapshot.html) ask for the sound chip's selected register at `&5A` to be 0 to 15, and a chip left unselected is written there as its `psg.selected` plus 16; a byte above 15 there opens as a chip unselected, with the register in its low four bits. The tests find both, and hold a snapshot the server saves to the one the command line saves of the same machine, byte for byte, and one the command line runs on from to where the server, opening it, runs on to.

## Keeping a machine

A machine can be kept as it stands and put back there, as often as an agent wants to try one thing and then another from the same place. `state_keep` keeps everything the machine holds: its processor inside an instruction if a run left it in a chain of prefixes, where no snapshot has room for it, its picture, each disc as it has been written, the tape where it has played to and how far a CPC's reel has come up to speed, its tick and its journal. `state_restore` puts the machine back exactly so, its discs among it though another disc has taken a drive since, and a run from there goes on as it went on from the moment kept. The tests hold a CPC kept while AMSDOS writes its disc, with its disc controller inside a command, another kept partway through `RUN"`, and a Spectrum kept partway through `LOAD ""` to the machines they ran on to, and a stretch gathered again from a state to the stretch gathered live. Restoring a state drops no other: each is a branch to come back to. `state_drop` lets one go, and `machine_list` names those held of each machine.

A state is held in the server's memory, beside its machine, and is restored into that machine alone, because it holds the machine's own pointers, a CPC's into itself, and at its buffers; it goes with its machine, or with the server. States kept while a disc does not change share one copy of it.

`state_save` writes a state to a file, and `machine_open`, given the file, builds a new machine as the state was, so that a machine outlasts the server that ran it and one worker begins where another left off.

The file holds no pointer. Its first line is JSON: the build that wrote it, the machine, the tick, the files the machine was built from, the journal, and every field of the machine, by the names `inspect` reads them by, and of the disc in each drive and of the deck and its reader, by the names the emulator's headers give them. The buffers those point at follow it byte for byte: the RAM, the picture, each disc's image and the tape's.

A machine read from the file is built as any is, its 6845 of the type the state's was, then written field by field through the checks `set` makes, so that every pointer is the new machine's own; the drive the disc controller had chosen and the disc in each drive are set as the file says, and a CPC's memory map is worked out again. A disc read from it is held to the bounds the emulator's floppy.c trusts of a disc it laid out itself. A tape is opened again and played from its start, pulse by pulse, until its reader stands as the file's stood; a deck takes at most one pulse a tick, so its reader comes there within as many pulses as the state has ticks. Only the build that wrote the file opens it, on the firmware the state was kept on, by its hash, because a field is read by its name and nothing promises another build gives a name the same meaning; on any other build the journal in the file's first line makes the machine again. The machine takes the state's journal and the files its journal names, the firmware and the snapshot the state's machine was built from, where `machine_open`'s answer names the files read to build this one, the firmware and the state file.

The tests build a CPC kept while AMSDOS writes its disc, another kept partway through `RUN"` and a Spectrum kept partway through `LOAD ""` from their files in another server, and hold them to the machines they ran on to; they find a 6128 read from its file cataloguing its disc and saving across two tracks with its disc controller as AMSDOS set it up, which a snapshot loses, and a run by frames from a file ending where it ends on the machine kept.

## The journal

A finding is to leave as something the machine can be asked again, and `journal` is that: the build that runs a machine, the files it was built from, each by its SHA-256, and every request that stood it up or changed it, in order, the first of them the `machine_open` that stood it up, each with the machine's tick as it began and the arguments it was given, save that an address, a port or a byte a condition's `value` or `value_to` gives is written after `&`, a register of the PSG or the 6845 as a number, a range of one address, register or byte as that one, a test of every byte and a `changes` that is false left out, `when`'s bytes as pairs of capital digits, or `??`, a space between each, a CPC's `machine_open` given the type of its 6845 even where it named none, and a path absolute, and a snapshot, a disc or a tape read named by its SHA-256 as well, and a file a run or a call wrote named by what it holds. Reads are not entered, nor are snapshots and discs written out, nor states kept, written or let go; a restore makes the journal the state's, and a machine stood up from a state file carries the state's on, so the journal is always the way from the machine's opening to where it stands. The same requests made in the same order, on the same build and with the same files, make a machine that stands as the first stands, which the tests do, with a branch a restore left behind; a request that wrote a file writes one that holds the same, at a path that names nothing yet. A long journal is answered a page at a time, as many entries as 8192 characters hold, and one at least.

## The files a machine is built from

`machine_open` answers with every file read to build the machine, its firmware and its snapshot or its state file, each by its path and the SHA-256 of what it held, and `disc_insert` and `tape_insert` name their files the same way. A finding names its copy by that hash, and a file changed between two openings gives two hashes. A path is read and written on the file system the server runs on, from its working directory if it is relative, and the answers and the journal name the file by its absolute path, so that a journal is made again from anywhere; a file that cannot be reached is refused with that path and the working directory. Anything there but a file is refused unread, firmware, snapshot, state file, disc or tape, and so is a snapshot longer than 49179 bytes for a Spectrum, the one length its snapshot has, or than 8MB for a CPC, whose version 3 may carry chunks after its memory, a disc or a tape longer than 16MB, and a state file longer than 128MB, or whose first line is longer than 32MB.

## The name of the build

The server names itself and the build in the `_meta` of its answers to `server/discover`, `tools/list` and `tools/call`, as the 2026-07-28 revision asks of every result and the older ones allow, and in the `serverInfo` of its answer to `initialize`. A ping's answer does not, nor an error, nor the empty answers the protocol's library gives for what the server does not offer. The name is the package's own version, then the commit of this repository and the commit of the emulator it was compiled from.

```
0.0.0+mcp.<commit>.dirty.emulator.<commit>
```

`.dirty` follows a commit whose files differ from the build's, an untracked file among them, or an ignored file of a kind the build compiles: here, the sources, the bridge, the build script and the files that pin the crates and the toolchain; in the emulator, its `src/`. A change anywhere else in either repository does not mark the build, because nothing compiled from it changed. Where a repository is not a checkout of its own, the commit is `none`. A finding can be made again only on a build that is unmarked and names commits the published repositories hold; one made on any other build is made again on such a build before it leaves.

## What it cannot do

- `type` keeps the command line's pace, and a program that reads the keyboard only now and then, or throws keys away while it is busy, loses those pressed between. It is typed at a character at a time, with runs between that stop where it reads the keyboard. `type` takes no conditions, so what a character does is watched by typing the text before it, then holding the keys that type it through a run given the conditions, for as long as `type` holds a key, since the firmware repeats a key held longer, and letting them go for a run given the conditions again.
- Keys are held down for the whole of a run. Nothing presses or lets go of one partway through, so a stretch that wants a key for part of it is two runs, and `step` and `call` take no keys; `set` can write the keyboard's lines, which then stay as written, through a call as through a run.
- A cpc464 takes no disc, so a release for one on disc is read here on a cpc664 or a cpc6128, or from a snapshot.
- A disc comes out of a drive only as another goes in, or as a state kept while the drive was empty is restored. Nothing here reads a disc's tracks or sectors: `disc_save` writes its image for a client's own tools, and an agent given this server's tools and no others reads a disc through the firmware, as `CAT` does.
- A disc's write-protect tab is set as the disc goes in and nowhere else, and `inspect` does not read it. Setting it on a disc the machine has written to, or taking it off, means saving the disc with `disc_save` and putting that image in again. A disc that holds a track the extended layout cannot describe, too big for its table or with a sector whose identity mark does not pass before the index, is not written out at all: only its machine, the states kept of it and the state files written of those, which only the build that wrote them opens, hold it.
- Nothing records. What either machine writes to tape goes nowhere, [as in the emulator](https://github.com/tulinmola/colophon-emulator/blob/main/docs/machine.en.md), so a program saved to tape, as a CPC's firmware asks with `Press REC and PLAY then any key:` (SOFT 968, 8.11), is lost, and a CPC's saving runs the motor, so a tape left in its deck plays on beneath it; what the machine holds is kept with `state_keep` or `snapshot_save`.
- A Spectrum's tape that stops itself, as a multiload's does between its parts, waits there: nothing presses PLAY again, though a state kept before it stopped puts it back playing where it was, and nothing else stops it. No tape is taken out of either machine's deck but by another put in, or by a state kept before it went in.
- A tape is played only where its blocks play in order, as recorded. One that holds a waveform recorded sample by sample, data built from a table of symbols, or a block that sends the tape elsewhere or asks a question, a jump, a call or a menu, is refused, the deck keeping the tape it had, and so is one with a loop the format forbids or a block the emulator does not know, so a release on such a tape is read here only from a snapshot or from another copy.
- A snapshot carries less than the machine. Neither format keeps whether the processor is halted, so a machine saved on HALT, waiting for an interrupt, runs on past the HALT as soon as it is read. A CPC's is read and written for the fields of version 1 alone: the 6845's counters a version 3 carries are not read, nor is its CRTC type, so the machine's 6845 is of the type `machine_open` gives, and it begins its frame again from the top, with the Gate Array's interrupt counter and the monitor's beam at 0, and its first run by frames ends early, at the retrace the 6845's first vertical sync brings; no version keeps the disc controller, the discs or the tape, so a machine read from one finds its deck empty and the controller as at power-on: one taken inside a disc access does not go on with it, and AMSDOS, which sets the controller up with SPECIFY as the machine starts, reads no disc from there until `set` writes `fdc.non_dma` back to `true`, as SPECIFY left it. Nor does it reach a second track at the power-on step rate, where a seek outlasts AMSDOS's wait for it, until `fdc.step_time` is written back as well, 12000 microseconds; SPECIFY also leaves `fdc.head_load_time` 4000 and `fdc.head_unload_time` 32000. The tests catalogue a disc from a snapshot with `fdc.non_dma` written back, and save a file across two tracks only with the step rate written back too. A Spectrum's has no room for the beam or the tape, so one taken while it loads comes back to an empty deck, and keeps the program counter in the two bytes below the stack pointer, so a machine read from it has lost what those two bytes held; a Spectrum whose stack pointer is below &4002 is not saved, 0 among them, though a stack pointer of 0 pushes to the top of the RAM. Nothing a version 3 carries in chunks after its memory dump is read, so one that keeps its memory in chunks rather than in the dump is refused. A state file keeps all of what a snapshot loses, for the build that wrote it.
- A file is written only where none is, and the only files written are a snapshot, a disc's image, a state file and what a run or a call gathers and counts; no tool writes memory alone, and a snapshot and a state file hold the RAM whole.
- Nothing here reads a run's file back. A client reads it with tools of its own, so an agent given this server's tools and no others has only the answer's lists, 128 matches, or 32 where a condition of `gather` captures, and 256 places at a time. A count by range runs on, and lists the first 256 ranges, so such an agent reaches the ranges past those only by narrowing the count's conditions and running again from a state kept before.
- A condition lasts one request. No condition is kept on a machine between requests, so a stop wanted again is asked for again.
- A count lasts one request too. Nothing joins the places or the ranges two requests count, so a stretch played with keys that change from one request to the next is put together by the client, range by range, from each request's answer.
- A condition names a place, the bytes it matches, whether a write changed one, the bytes memory must hold from one address, and for an instruction or a read the RAM or a ROM, and nothing more. Its own byte is tested against a range, so a byte other than one value takes two conditions, or one where that value is 0 or `&FF`, and a single bit takes more ranges the lower it is, bits 2 to 0 more than a list holds; a port read's byte is not tested at all. `when` tests each of its bytes against one value or against none, so a value of sixteen bits in memory is tested whole, but not a range of values, a value other than one, a single bit, or bytes that do not lie within eight in a row; a condition takes one `when`, so of a state kept in two places too far apart for one `when`, one is tested and the other read after the stop or captured with the match. `when` reads memory as the processor does, so neither the RAM beneath an enabled ROM nor a 6128's RAM paged out is tested. A condition cannot ask for what a processor's register holds or for a position of the beam, nor for one ROM rather than another or one of a 6128's banks of RAM rather than another, save by `when` on bytes that differ between them; those are read from what a gathering lists and captures, or by stopping and reading.
- A capture reads what its condition names and nothing more. The processor's values are read one at a time as the emulator holds them, so HL is `cpu.h` and `cpu.l`, and IX `cpu.ixh` and `cpu.ixl`. A span is read from an address given or from where SP or PC points, so the bytes where another register points, HL or IX, are read by stopping and reading. Nothing outside the processor and memory is captured, the 6845's registers and the Gate Array's among them, and a request's captures name at most 16 values and spans, reading at most 64 bytes. A count captures nothing; what the machine held at a place's first match is captured by gathering it.
- `set` can write the processor's machinery into states the emulator never reaches of itself, such as an INT accepted at an opcode fetch, and a run from one does what the core does with it, but what a condition sees there is not promised: the instruction run there is seen to begin by no condition, its accesses name no instruction, and they may capture nothing.
- A ROM that is not paged in is read by no tool: `peek`, `find` and `disassemble` reach a ROM only as the processor does, and `screen` reads the RAM alone. Its file, which `machine_open` names by its hash, holds it.
- `find` searches the 64K the processor reads, as it stands, or a CPC's RAM by its own addresses, where a match may run from the base 64K on into a 6128's second, which the processor never reads one after the other. It matches bytes, not instructions: a relative jump holds no address and is found by no search for one, an address a program works out as it runs is found by none, and the bytes of an address may lie in data as well as after an opcode. No match wraps from the last address searched to the first. A CPC's firmware jumpblock reaches the lower ROM through LOW JUMP, `RST &08`, with the two bytes of the address after it, and LOW JUMP [takes the top two bits of the address for the ROMs it enables](https://github.com/Bread80/CPC6128-Firmware-Source), so a search for an address in the lower ROM misses a LOW JUMP to it with either bit set. Text is always matched on seven bits, so no text asks for a byte whose eighth bit is clear, as `bytes` can, and a letter matches only in its own case.
- `compare` reads a CPC's RAM and nothing else: what the processor's registers and the chips hold is read with `inspect`, no ROM is compared, and a Spectrum's RAM is compared by the client, from what `peek` reads of each machine, or of one machine before and after it is restored to another state, kept first so that it can be put back. It sets two moments side by side and nothing between them: a byte written and written back meanwhile is not listed, and which instruction wrote a byte, and at which tick, is found by a `write` condition, gathered or counted. It says where bytes differ and what each side holds there, so a byte that went up, or down, or by so much is told from those by the client. A machine is compared with a state kept from it or with another CPC as it stands, so two states of one machine are compared by restoring one and comparing with the other, and a state file by standing it up as a machine first.
- A count says how often each place matched and when it first did, and nothing of when it matched again, in what order it matched after or for how long. The time a routine takes is read from the ticks of a run that stops at its start and its end.
- An instruction counted keeps the bytes it first ran with. One rewritten and run again at the same address is the same place, so what it held the second time is read by stopping there and reading memory.
- A gathering of the last matches does not say how many it let go; a count of the same conditions in the same run says how many there were, unless it fills and stops the run.
- A variable a program keeps inside one of its own instructions is no data, so a read of it is never matched; the instruction holding it is found by `execute`, and what changes it by `write`.
- No sound is heard. The emulator sounds a CPC's PSG and a Spectrum's speaker, and its command line writes what they sound to a WAV file, but no tool here gives it out, so what a program plays is read from the writes a `psg` condition gathers; a Spectrum's speaker is [bit 4 of its ULA's port](https://worldofspectrum.org/faq/reference/48kreference.htm), whose writes `{"out": "&FE", "mask": "&01"}` gathers.
- How many writes a `psg` condition sees is the emulator's count. On the board the PSG is [wired to the PPI's ports A and C](https://web.archive.org/web/20220626135348/https://cpctech.cpcwiki.de/docs/psg.html) and not to the processor's bus, so it takes port A while port C gives it WRITE and sees no access to the PPI; the emulator writes a selected chip at each access, so a program that leaves WRITE standing while it reads the PPI's other ports is matched writing the same byte again at every read.
- Only writes to the PSG are conditions. A register selected, or read, as a CPC's firmware reads its keyboard through register 14, is found by watching the PPI's ports with `in` and `out`.
- `changes` compares what a byte or a register holds before a write and after it, and nothing else: a write that leaves a register as it was is no change, whatever the chip does on being written. A write of the shape register 13 of the PSG already holds is one: the chip restarts its envelope at every write there, as [MAME](https://github.com/mamedev/mame/blob/b2a182f0b761030db051210494ed7c6b7e412eb6/src/devices/sound/ay8910.cpp) and [floooh's chips](https://github.com/floooh/chips/blob/9e88298ce56319953ac7a43213a1120359f7a3a6/chips/ay38910.h) both have it, so `changes` leaves out a restart that writes the shape again.
- Only writes to the 6845 are conditions. A register selected is found with `in` and `out` on `&BCxx`, and a register read with `in` on `&BFxx`.
- What an `IN` gives the 6845 is the emulator's choice, the top half of the port's address, wherever [the Compendium](https://shaker.logonsystem.eu/ACCC1.11-EN.pdf) does not settle it: it says an `IN A,(n)` gives A at a port no other device answers, and that with an `IN A,(C)` the data on the data bus is sent. A `crtc` match gives what the emulator sent.
- [The emulator's five types of 6845](https://github.com/tulinmola/colophon-emulator/blob/main/src/crtc.h) are its type 0 but for the thirteen things its `crtc.h` names, and around types 3 and 4 [its board](https://github.com/tulinmola/colophon-emulator/blob/main/src/cpc.h) does four things of its own: it holds an `OUT (C),r`'s write a microsecond, hands both syncs to the Gate Array a character late, raises the interrupt a quarter of a microsecond sooner after them, and on a type 3 shows a colour written to the Gate Array half a byte sooner. A program that tells one type from another by anything else finds a type 0.
- A type 3 is the 6845 a CPC Plus's ASIC emulates, built into a CPC that has none of the Plus's other hardware, a computer no one sold: a program that tells the two ASICs apart by the computer around them, as the Compendium's ch. 29.1.1 and 29.1.2 do, finds the low-cost CPC a type 4 is built into.
- The type a CPC is built with when none is given, 0, is a choice: Amstrad fitted 6845s from different manufacturers to the same models, and a program written on another type may run otherwise on a type 0 than on its own. The tests hold the server against the emulator's command line on type 0 alone. Every type powers on with its registers at 0, the emulator's choice where the silicon promises nothing, and counts in its own way until the firmware sets them, so a program may reach an instruction at another tick on another type from its first frames on, and one that draws its chances from the timing then plays out otherwise; the tests find the firmware's interrupts coming at other ticks on types 0, 1 and 2. Types 3 and 4 also show the picture a microsecond further left, as [the Compendium](https://shaker.logonsystem.eu/ACCC1.11-EN.pdf) says of them, and the tests find BASIC's opening screen 16 samples to the left of a type 0's.
- A state is kept only when asked. Nothing keeps one as a machine runs, so a stretch is asked about again from a state kept before it, restored and run again with what is to be gathered, or from a machine made again by the journal's requests.
- States live in the server's memory, 64 of a machine at most, each restored into the machine it was kept from alone. One outlasts the server only as `state_save` writes it to a file, and the file outlasts only the build that wrote it. What outlasts every build is the journal, in a state file's first line or as a client read it, and the files it names, from which another build makes the machine again as that build runs it.
- A state file is refused by a build of any other name, but two builds of uncommitted changes on the same commits share one, and either reads the other's files, each field read where the reading build has a field of that name, and the file refused where it has not, through the checks `set` makes and the bounds a disc is held to. A tape read from a state file is played from its start to where it stood, which takes as long as the pulses it had played, and one whose tape had played more than 268435456 pulses is refused, though `state_save` wrote it.
- Nothing here plays a journal back: a client makes its requests again itself. A journal names a disc or a tape by its path and its hash, so making the machine again wants the file of that hash at that path.
- `call` waits for its own return and nothing else. A routine that leaves another way, by jumping out or by unwinding the stack beneath it, is left running, and so is one that has not returned when the call stops, at its frames, at a condition of `until` or with its gathering or its count full. A later run carries it on and past its return, stopping there only if `until` asks for the address the call was made from, and a processor woken from `HALT` for it is not halted again. One that halts with interrupts disabled waits there for good: `set` or a state restored takes the processor past the `HALT`, and another call wakes it for that call's routine alone.
- A program that ends on `HALT` with interrupts disabled ends its first run there, short of the frames asked, so a picture of where it ended takes a second run.
- `call` pushes and jumps from outside the processor, in no time at all, and leaves its refresh register R and its hidden WZ and Q as they stood, where a `CALL` would have moved them; only a routine that reads them can tell.
- `monitor` shows the command line's window and nothing of the raster outside it: the sync, the blanking and whatever a program draws past the window's edge go unseen.
- `screen` draws memory as it stands, in one mode and one palette. A picture a program makes by changing the mode, the inks or the screen's start partway down a frame, or by writing memory as the beam passes, is shown by `monitor` and not by `screen`, and `screen` reads the 6845's geometry as it stands at the moment it is asked, which a program that splits its frame changes within it.
- A base given to `screen` is an address, which cannot carry what bits 3 and 2 of the 6845's R12 say: that a row runs on past its 2K into the next 16K, as on a screen of 32K. A screen asked for no base reads them from the 6845; one given a base reads as though they were clear.
- Only a CPC's memory is read as a picture. A Spectrum's picture is shown by `monitor`, and its memory is not drawn otherwise.
- A picture is one picture. Nothing lays several side by side or labels them, marks the beam's progress across memory, or marks what was written lately; an agent asks for each picture and compares them itself, and no tool waits for the picture to change.
- `disassemble` reads bytes as instructions from wherever it is told and tells code from data no better than that: bytes after a jump or a restart that never returns there are read as instructions all the same, and so are the bytes a restart takes after it, the error code after a Spectrum's `RST &08`, the calculator's literals after its `RST &28` and whatever a program's own restart takes, save the word after a CPC's firmware restarts. A restart is taken for the firmware's by its first three bytes alone, the jump into the high kernel, so a program that keeps the jump but lays bytes of its own where it lands, as Aliens does at `&B98A`, has each `RST &08` in its memory listed with a word after it all the same; and a listing begun at the word reads it as instructions. The word is given as it stands, and the reader works out what it says: a LOW JUMP's address from its low fourteen bits and the ROM state it asks for from its top two, a SIDE CALL's address from its low fourteen bits added to `&C000` and which of four ROMs it selects from its top two, the routine a FAR CALL reaches from the far address its word points at, and a FIRM JUMP's address is the word itself. It follows no jumps and knows no names a program was written with.
- A listing by `ram` reads a 64K as the RAM configuration 0, or 2, lays it before the processor, so a program run under another configuration, which puts one of its blocks at another address or beside blocks of the other 64K, is listed as that 64K holds it: a listing run on past a block's end goes into the next block of the same 64K, or past its last block back to its first, rather than where the processor goes, a relative jump's target is given where configuration 0 or 2 would put it, and an address the instruction holds is the processor's, whatever the configuration. A firmware restart in a 6128's second 64K is judged by the bytes at `&10008` and its like, where the firmware keeps no restarts, so it is listed by `ram` without its word, and by `address` with it, save under configuration 2 with the lower ROM disabled, where `&0008` reads the second 64K too.
- The only names a listing gives are a CPC's firmware's, for three of its jumpblocks. The fourth, the low kernel jumpblock below `&0040`, goes unnamed, because programs that take the machine over commonly use its restarts for routines of their own; so do the maths entries past the main jumpblock and the line editor's, which SOFT 968 does not name, AMSDOS's own routines, and every routine of a Spectrum's ROM, whose names The Complete Spectrum ROM Disassembly gives. A name says which entry an address is, not what runs there: a program that lays its own code over the jumpblocks is listed as calling the firmware all the same.
- Only an address the instruction itself gives is named, as its operand or as a relative jump's target worked out. An entry reached through a register, as `JP (HL)` reaches one, through a table of addresses, or through the firmware's own FAR CALL, whose restart is followed by the address of a far address holding it, goes unnamed, so a program that reaches its firmware those ways is listed without the names.
- A request cannot be taken back. A client that stops waiting for a run leaves the machine to run the whole of what it asked, which the machine's tick in its next answer shows; the limit on a request keeps that to half a minute of the machine's time, or a minute of a CPC's whose frames do not come.
- `set` refuses what the emulator could not run from and a change of a 6845's type, and nothing else, so a machine written through its machinery can stand where no silicon could, and run on from there.
- The machines live only as long as the server. When its input closes, they are put away with it, nothing of them is kept but the snapshots and the state files saved of them and the journals read of them, and the next server gives out the same handles again, and the same names to its states.
- One client speaks to one server, over its standard input and output and nothing else.
