Skip to the text
The Colophon Project

The machine

A machine assembled from parts can be honest part by part, and these are. A chip whose account of itself is incomplete says so at the head of its own header, because a part that quietly does nothing is worse than a part that is missing: the missing part is a question, and the quiet one is an answer that happens to be wrong. This page collects those declarations in one place, for a reader who is not going to open the headers. A line leaves it the day it stops being true.

The machines

Four names answer, and a name is listed only once the machine behind it boots its own firmware to its own prompt.

Name Machine
cpc6128 Amstrad CPC 6128, 128K, BASIC 1.1
cpc664 Amstrad CPC 664, 64K, BASIC 1.1
cpc464 Amstrad CPC 464, 64K, BASIC 1.0
spectrum48 Sinclair ZX Spectrum 48K

The three Amstrads differ in the firmware they are given and the memory they are built with, which is very nearly how they differed on a desk. A 64K machine has no PAL fitted, so a banking command written to it falls into an empty socket and nothing moves — which is what happened on the hardware, and what a program written for 128K discovers there.

The processor

The Z80 is complete and cycle-stepped: one call advances it by a single clock cycle, and every instruction the machine knows is implemented, the undocumented ones included. The register model is the full one — the shadow set, the index registers as halves that act as registers in their own right under a prefix, and WZ, the internal address latch the datasheet never admitted to.

The NMOS quirks are there, because both machines were given an NMOS part and programs can see them. OUT (C),0 puts zero on the bus. SCF and CCF take their two undocumented flag bits from the accumulator or from the old flags depending on whether the instruction before them wrote flags at all. An interrupt landing on LD A,I or LD A,R leaves the parity flag reporting interrupts disabled when they were enabled — a bug Zilog acknowledged in 1989 and fixed on CMOS.

Interrupt modes 1 and 2 are implemented. Mode 0 executes whatever a device puts on the data bus, and on neither of these machines does anything drive it, so the processor reads &FF: an RST &38, which is mode 1’s behaviour. That case is implemented and the general one is not, because neither machine carries the hardware to reach it.

The Amstrad CPC

Six chips and a PAL, and the accounting below is of them.

Memory

The memory map is the board’s: sixteen kilobytes of lower ROM over the bottom of the address space, an upper ROM over the top, and the eight RAM configurations the 6128’s PAL selects between. The upper ROM is chosen by a number latched at &DFxx, and a number with no ROM in its socket resolves to ROM 0, which is BASIC — as it does on the hardware, and which is why an unfitted expansion answers with something rather than nothing.

I/O is decoded the way the board decodes it, by single address bits rather than by whole port numbers. One access can therefore reach several devices at once, and a program that relies on it is not doing anything unusual. An access the Gate Array’s wait line holds over several ticks reaches each device once.

Video

The 6845 is emulated as type 0, the HD6845S/UM6845 — its register widths, the counters a program can leave above their limits and the widths that bring them home, the frame construction, the two video pointers and their reload rules, and the line that ends the frame being decided while the character counter is still at 0 or 1, so that a register written after that cannot take the decision back. A register written at the line’s second character is the exception, and it lands on the third: there it can unmake a last line and spend it on padding instead, or make one of an ordinary line and bring the row counter home under it. A register written on the line’s last character arrives as the line ends, after the chip has asked whether the row is done: the row counter moves on a comparison that held before the write as well as on one the write makes, and the line counter takes the register as written. So a row height moved there moves both counters at once, as the documentation says, and the frame’s last row renamed there still opens its padding as though nothing had been written, which the suite measures on silicon where the documentation would count that padding another way.

Interlace is here, deadline and all. An interlaced picture is two fields drawn half a line apart, and the machinery that offsets them is present: the chip keeps the two frame parities the documentation gives it, adds to every even frame the one line that makes the fields differ in length, and holds that frame’s frame sync back to the middle of its line. In the interlaced video mode it counts differently as well — the raster address becomes the scanline counter doubled, with a parity filling the bit that leaves, so a character row covers twice the ground and the two frames address alternate lines; the register that sets a row’s height is read up to that same parity. Which parity is the frame’s own, except where a row is an odd number of lines, when it turns about with the row’s number as well, so that rows come out alternately long and short and a pair of them still holds what a single row held before.

Writing that mode moves two things at different moments: the doubling waits for the head of the next line, while the parity joins the limit at once. That is what makes a row entered or left off its parity overrun — it counts the whole way round five bits before it finds its end again — and a program uses exactly that to discover which of the two frames it is on. Shaker times a frame in that mode at 20032 microseconds, which is what a real machine times.

The parity the frames alternate in has no defined start: silicon wakes holding whatever it holds, and this chip wakes on the parity its type gives that first frame. Every frame after inherits that choice, so a program that reads the parity rather than setting it can be a frame out here and right on a real machine. The frame sync’s own permission, described below, has no defined start either, nor has the permission that manages the counters, and this chip wakes holding both, which is the state a chip that has been running is in.

The line interlace adds to an even frame is asked for later than type 0 asks for the adjustment lines: the question is put on the last line the frame has, at the character its width register names as that line’s last, and the line it is put on may be one of the adjustment lines itself — so a program can add the line, or take it back, from inside an adjustment, long after type 0’s window for asking for adjustment lines has shut. Types 1 and 2 and the two ASICs, which ask for adjustment lines to the last line’s end, put the two questions a character apart. The suite’s group for it grades that reach into an adjustment on a type 2 below: were the question put only on the last line of a frame’s last row, never on an adjustment line, two more of the lines it grades there would come out wrong. It bounds the character too. Put at the line’s forty-second character, where one of its tests writes the register that asks for interlace on a last line, or at any character before it, the question puts at least one more line wrong; from the forty-third character to the last, the group cannot tell one from another. The last is the documentation’s word.

Five CRTC types shipped in real machines and they diverge observably; demos probe for the type at runtime and branch. Only type 0 behaves as itself here, and the other four are type 0 with thirteen exceptions. The command line builds a CPC with a type 0 unless --crtc-type names another, and refuses a number naming none of the five; through the core, such a number builds a chip that is none of them, part one type and part another.

The first is what a program can read of the chip, which is what the probing reads. Every one of the five answers it. Three of them hand back a table of registers, one of those three having a status register besides; the two built around an ASIC read through a table of eight instead, chosen by the bottom three bits of the number a program asks for, so that several numbers name the same register — and two of those eight are not registers at all but bytes tracing where the chip’s counters stand, which a program can read to wait for an event to the microsecond. Their two ports answer alike. One bit of those two bytes is left standing where it rests rather than tracing anything: it counts lines from the frame sync, and the documentation describes it twice in terms that cannot both be true of the same resting value, so a program reading it here learns nothing. One bit of the second byte, which its table calls fixed, is not: photographs of the suite’s status group show it clear all through a frame with extra lines at its end and set where those lines end, the place the group’s search for the number of extra lines stops counting, and it is read here as those photographs show it, and as clear too, under the interlaced video mode, wherever the line address has passed the row height, which another of that group’s photographs shows and which wins over the extra lines’ end. Another bit of that byte turns over every sixteen frames, as the documentation says, from a count of frames that is ours, the documentation naming nothing behind it. What the two ASICs hand back is what lets the suite tell them from a type 2 and run the groups written for them; it names them correctly, with one reservation that is ours rather than the suite’s. The two ASICs are told apart chiefly by the computer around them rather than by the chip: the earlier of the two lives only in a machine with a further ASIC, an unlock sequence and an imperfect copy of the keyboard chip, and this emulator has none of those. The documentation also believes one bit of the second status byte differs between the two chips, but says so tentatively and does not say what the difference is, so nothing here can act on it. So a machine built as that type is a chip in a computer that was never sold, and the suite reads it as the later one. That is the right answer to the question it actually asks.

The second exception is the syncs. A line sync asked for with a width of nothing is none at all on types 0 and 1, and sixteen characters on the other three, which is one of the ways a program tells them apart; no line the suite grades moves on it. A width written while a line sync is already running is taken here as a count to reach: one the count has yet to reach ends the sync where the count meets it, and one it has reached or passed, zero among them, runs the sync on until its counter comes round. For a width already passed the documentation agrees on every type, save a zero on a type 1, which ends that sync outright. Three such writes have outcomes of their own in the documentation, and none of the three is here. One is that type 1 zero. Another is the width the count stands at, written on that very character: from the instruction that lands its write on its third microsecond behind a Gate Array — one of the two that can make such a write — it stops the sync on types 0, 1 and 2 (the trick the documentation calls R3.JIT), where here the sync runs on round the counter, as it does on the two ASICs, which the trick does not work on. The last is a zero landing inside the sync’s first microsecond: it cuts the sync short on types 0 and 1, or never starts it if the other of the two instructions wrote the zero; here that sync too runs on to sixteen. Four groups of the suite are aimed at these writes, and none of them grades itself in words. The two types that cannot ask for a frame sync’s length hold it at sixteen lines whatever the register says, and a sync either of them is made to begin by writing the register that triggers it gives up the rest of the line it began in, where type 0 keeps it; one begun on the half line an interlaced frame raises its sync on keeps all sixteen, a reading of ours. Type 0 also holds a sync back by a whole line on alternate frames where the interlaced video mode leaves a row an odd number of lines, and neither of those two does, which is one of the things that leaves such a machine’s two fields out of step with each other. No line the suite grades moves on either of those two: what stands behind them is the documentation’s own sentences and tests of our own. One more divergence of the frame sync stands beside those two, and this one the suite does grade. A program can make the comparison that raises it come true by hand, writing the register that names the row with the row the machine is already on; at the head of a line that blocks the sync rather than raising it, leaving the machine with neither the sync nor the comparison until the row counter moves on, or a row’s first line begins with the register naming another row: written away and back within the row, the register brings nothing round. The documentation gives that blocking to type 0 in type 0’s own chapter and gives a type 1 no exception at all; a type 2 it gives the sync at once as well, except while a line sync is running and on the character it ends on, where it gives one that counts its lines but never reaches the pin, and it gives the same pinless sync to the comparison the row counter walks into while a line sync runs. So here a type 1 and a type 2 take the sync where type 0 is blocked, and a type 2’s is pinless wherever its line sync is running or has just ended: it counts its sixteen lines and holds another sync off as any frame sync does, and the Gate Array sees nothing of it — no frame on the monitor, no interrupt count set back, no blanking — while the 8255 reads the pin low. For the comparison the row counter walks into, a line sync that only begins on that character does not count, as the documentation has it. One group of the suite bears the pinless sync out: on a line it grades, the machine matches silicon’s value. Another sets the line sync where the documentation says a type 2 can raise no frame sync at all, and the machine raises none; the frame sync that group then tries to make through the 8255’s port B is not wired here. A frame sync held back to the middle of its line on an even field takes the pinless sync by the same reading where the line sync runs into or ends on that middle character, which the documentation does not describe and nothing grades. The two built around an ASIC have no protection against a comparison raising a second sync, their documentation says: an equality renewed where a row’s line counter and the character counter return to 0 starts a sync again if none is running, so on those two the block is lifted there, which matters only where the row counter has not moved and lifted it already, at a frame’s head where the last row is 0 and at the head of the padding that holds it there. They refuse the sync by a rule of their own besides, which is here: a sync starts on those only where the row named is reached with both the row’s line counter and the character counter at zero, so a comparison made by hand anywhere but that corner is spent, and the sync it asked for waits for the corner of the frame after. Where the row named is the frame’s own first, that corner is also the character the field turns over on, and those two take them in the other order from the rest: they read the sync first and turn the field after, where the other three turn the field and then read the sync. So on those two a sync named for the first row is held back to the middle of its line on the odd field of an interlaced picture rather than the even one. Two groups of the suite ask about that on each of their records, one of them timing an interlaced frame padded out by extra lines and the other putting a frame sync against an interlaced frame; both speak only where the machine is wrong, and are silent on it. The documentation has that comparison made at the corner and only the start held back, by half a line or by a whole one; this machine makes it where the start falls instead. So a register written to meet the row inside the row’s first line, after its corner, still raises the held-back sync here where the documentation says it raises none, and a comparison true at the corner and undone before the start — the register rewritten, or the interlaced mode given up, which leaves that frame no sync at all — loses a sync the documentation raises. Nothing grades any of it. On those two the block is lifted as well by any write of the register naming the row save one that itself sets the block: a reading of ours that no chapter asks for, which can reach only a sync starting after the corner — one held back, or one where the character counter has been brought to 0 by a line length written below it — and which nothing grades either. Nothing on any record moves on the corner, and nothing the suite grades on the protection those two lack: the group that watches this very pin to catch a row counter overrunning is answered on both records by two other rules, set out below; the group that tortures the frame sync reads by that lack, on a line it does not grade, the endless sync its own legend gives these two. The eighth exception below reaches the line of the suite that grades it, on the type 1 and type 2 records alike, by their early reading of the comparison, with or without the block. One of the two things that keep a single comparison from raising two syncs is missing here: the documentation has the comparison made by a write of the register naming the row ignored while a sync is running, where here it is made as at any other time, so once the block has been lifted during a sync — by the row counter moving on, or by a row’s first line beginning with the register naming another row — a write naming the row the row counter stands on, made on a type 0 no earlier than a line’s third microsecond and anywhere in the line on a type 1 or a type 2, starts a second sync the moment the first ends, if the row counter still stands there. The other protection, that the comparison itself must have changed, is kept on those three: changed by the row counter moving on, or by the register naming another row when a row’s first line begins.

The third exception is how a type 1 divides a row between the two fields of an interlaced picture: it leaves a row an odd number of lines from an even setting of the register that sets a row’s height, where type 0 does so from an odd setting, and it reads that register down to the row’s own parity where type 0 reads it up. So the two machines want different settings of that register for rows of the same height, and a picture built for one comes out of the other with its rows a line adrift. A mode switched on in the middle of a row takes up the doubling a line later, as the documentation gives type 0. This is the second of the five the suite grades. A type 2 shares none of it: its interlace is a different arrangement again, and most of that is here. Its rows are not halved in the interlaced video mode as every other machine’s are — a row keeps the height its register asks for, so eight lines stay eight — and what steps by two instead is a display counter of its own, which shows each address twice. That counter is also where the machine leaves the address a row hands the next one: twice over a row of an even number of lines, and once over a row of an odd number, where the row ends a line before the counter comes round and the second is lost. The parity that counter is paired with is the field’s own and nothing besides, where the other four machines balance a pair of rows between their two fields: a row’s addresses here do not alternate as a row’s do on those. The address takes the mode the moment it is asked, changing under a line already being drawn, where type 0 takes it at the head of the line after, as the documentation has it; photographs of the suite on a real type 2 draw it so, though one of them draws the segment that change moves a microsecond further left than this does, which is not yet understood. Two groups of the suite are aimed at it and neither grades itself yet. Another of the arrangement’s rules is here too. A mode switched on during a frame’s own first line, on the odd field of the pair, turns that line into one of the lines a frame adds: the frame begins again beneath it, both counters back at nothing, and runs a line longer. The documentation offers that line to a program as the way to learn which field it is on, and the suite takes it. Before several of its groups it switches the mode on at a frame’s head, looks for the extra line, and turns the field over once if it is not the one it wants. Without the line that look would find the same answer on either field, and those groups would be graded on whichever field the machine happened to wake on. With it the group that puts a frame sync against an interlaced frame and the group that times an interlaced frame padded out by extra lines, which speak only where the machine is wrong, are silent, and the group that asks for the interlace line with a write on a frame’s last lines agrees with silicon in every line it grades; and every line the suite grades comes out the same whichever field the machine wakes on, which was measured rather than assumed, though some of the pictures its ungraded groups draw still move with it. A fourth group, aimed at this very line and saying its piece in a picture, holds that picture still. The documentation’s own table for switching that mode on draws no such line, where three passages of its prose describe it; the prose is followed, as the suite follows it: its routine for settling the field works only on a machine that draws the line. Where the mode is given up again inside that first line, the counting goes on as it would have: the documentation says that of the line a frame’s end adds, and reading it as reaching this one is ours. It is here for the line a frame’s end adds as well, where the documentation says it outright: interlace given up while that line is displayed makes it no interlace line after all, and the frame goes on, the row counter climbing round to the last row the frame asks for — or the frame’s end falls on that line itself, padding and all, as on any last line, where the program has made it one. The documentation names the mode two ways there, the video mode alone in one chapter and either interlace mode in the other; the second is followed, being the chapter about this line and naming what adds it, so a program that drops the video mode and keeps the sync mode gives nothing up, and nothing grades the difference. The group that asks for the interlace line with a write on a frame’s last lines turns the mode off on that line on a type 2 and grades two frames. In the one that moves the register ending the frame onto the row counter there, which the documentation offers as the way to end it, the machine answers as silicon does. In the other it does too: the group leaves the padding register at 1 with the row counter already carried past the frame’s last row, and the machine counts on round to zero as silicon does, because a type 2 pads the last row alone. The documentation gives padding past the last row to type 0, and the suite says a type 2 does not have it; a type 1 and the two ASICs keep type 0’s padding there, which nothing in the documentation gives them; taken from those three and from type 0 as well, it moves no line the suite grades.

The fourth exception is the parity the two fields alternate in. Type 0 decides the coming frame’s parity in advance, on a row a register of its own names, so a program that puts that row beyond the row counter’s reach freezes it — the frames stop alternating, the two fields stop differing, and the extra line falls on every frame instead of every other. A type 1 decides nothing in advance: it turns the parity over at the head of every frame whatever the register that asks for interlace holds, and no register can freeze it. What a program is given instead is a pulse of the interlaced mode, on and off across an even scanline, which settles the parity even whichever it was; that is here too, taken on the two microseconds of the writing instruction the documentation puts it on. This is the third of the five the suite measures, in two groups that speak only where the machine is wrong and are silent on a type 1’s record. The pulse moves the row’s own line counter with it, and a program can see that too: in the interlaced mode the low bit of that counter is the parity rather than the count, so a pulse laid on an odd line of an even frame puts the counter back to the line before and the frame runs a line longer than its neighbour. A third group asks that thirteen times over and is silent in all thirteen, speaking, like the other two, only where the machine is wrong. What the counter costs is the second half of it. The documentation gives a type 1 one counter, which is the address itself and steps by two through an interlaced frame with the parity filling the bit it leaves; this machine keeps a count and a parity apart, which is the arrangement the documentation gives type 0, so wherever a mode is asked for or given up in the middle of a row the two have to be handed back to each other. The thirteenth of those tests is what measures it: its pulse spans a scanline, so the doubling starts between the test’s two writes. The rules behind the pulse are graded by sixteen worked scenarios the documentation draws for them, each named for the test that exercises it and each drawing that counter beside the two parities, and all sixteen are checked here. The two machines built around an ASIC have a rule of the same family and it is here: a write that turns the interlaced video mode on sets their line parity from the line it lands on, without the correction a type 1 makes for its row height. Taking it on that write alone is ours: the documentation’s words cover a write that asks for either of the two interlace modes, and nothing a program can see tells the readings apart, and in that mode their line counter is the address as a type 1’s is, so the count and the parity are handed back to each other at the mode’s edges the same way. What they keep that a type 1 does not is a counter the write itself never moves. No line on any record reaches any of it: what stands behind it is the documentation’s own algorithm for those two, the two frames it works out by hand, two of the twenty-two counting cases it draws beside them, and tests of ours. One limit stands with it. The line an interlaced frame adds leaves their row counter where it is, as the documentation gives it, but is meant to carry no parity — the documentation is explicit that the line’s own scanline “will always be 0, even if the other lines are odd” — and that half is not here: the scanline counts on as the frame’s padding left it. No record grades it: the group that asks draws nothing past its menu on these two, which the suite records as run and kept rather than skipped. Where a row’s line counter is sent past its own limit, these two bring it home: they do not test that limit for equality at all but for its having been reached or passed, which the documentation says twice and says is what makes overflowing impossible on them. The group that checks the two registers behind it agrees on both records, and so do two lines of the group that puts a frame sync against an interlaced frame. They also decide a frame’s end where the line ends rather than at its head, from the register naming the last row and the one setting a row’s height as the two then stand. A last row’s register written under the row counter there sends the counter on past it, which the documentation calls an overflow; written with the row the counter stands on during that row’s last line, it ends the frame on the next line, whatever character the write landed on, and earlier in the row the frame ends where the row does; and the row height written at or under the line a frame’s last row has reached ends the frame on the next line too. What type 0’s own chapters give it there instead — a last line unmade near its head spent on padding — is type 0’s and not these two’s. The group that watches the frame sync to catch the row counter overrunning reads exactly that, twelve times on each of the two machines, and prints none of the twelve, printing a reading only where the machine differs from silicon — by this rule and the microsecond described below together, which bring the later of its two writes to the next line’s first character, too late, where silicon has it. The earlier of the two chips has an exception of its own there, taking such a write before the counter comes home when the processor is selecting a ROM in the same instant; it is not here, and the suite, which names both chips the later one, does not ask for it. The padding after a frame is asked for at the same moment, from the register that sets it as it then stands and from the interlaced mode as it stood on the line’s last character, where type 0 asks early in the line and holds the answer: so these two pad a frame whose padding was asked for anywhere in its last line, and a line of two characters, too narrow for type 0’s own asking, takes no padding for its narrowness on these two, whose documentation says their line width “accepts all values without causing any problems for other counters”. A line of one character is a line to them, as the eleventh exception has it; and the padding that type 0 gives a row the counter has already carried past the last one is theirs here too, their chapters saying nothing of it. A type 2 asks the register again at its last line’s end, as these two do, though only of the line it has settled as the last: the suite holds it to that, with one line of padding asked for late in a last line whose row height had been moved after the line was settled. It settles that line by its documentation’s rules rather than type 0’s. The comparison is made at the line’s first character, with both registers as the writes made before it left them, and a line it makes last stays last whatever is written after, save that the register ending the frame, written during that first character, has the comparison made again with the row height it found. A line is refused it where the last character of the sync before it found the comparison holding, and so is a line whose sync begins on its first character or runs on over it from the line before, so a machine given its sync there ends no frame and its row counter runs on round. Later in the line, a write of the register ending the frame or of the row height, outside the sync, makes the line a last one where the comparison then holds, while the line’s leave to be made last stands: the last character of a sync takes that leave away where it finds the comparison holding and gives it back where it does not, and a frame’s first line must first see a sync end on a mismatch. The taking away is ours, from a later paragraph of the documentation, which assumes it, and from a photograph of the suite on a real type 2. Such a write is judged on the character it is made on, not the one after, where this emulator otherwise reads a written register: photographs of the suite’s previous version on a real type 2 have one made on the character before the sync count, one made on the sync’s last character not, and one made on the line’s last character count for that line. These rules together are how a program holds the counters at 0 line after line, by moving the row height through each sync, which the documentation works as a case study. Nor does a line’s head lift the border on a type 2 while its line sync runs over it, as its documentation says; whether the border the display’s width throws is skipped there too the documentation leaves open, and here it is thrown as on any line. A write made during the line’s first character can still make the line last, as a later one can; that much is this emulator’s reading, the documentation saying only which of the two registers the comparison there reads. One rule of that documentation is not here: the two registers as moved before or during the padding after a last line are the ones compared through it, so that a row height moved there leaves the row’s line counter counting on after the padding, where here both counters return to 0 when the padding is done, whatever the two hold. Where a sync runs over into the next line, the documentation’s words leave open whether the reading at its last character is taken there or no later than the line’s own last character; here it is taken there. That character’s comparison is made before a write made on it, which the sync refuses besides, as the photographs have it, so such a write reaches neither; the order of the two is this emulator’s. A type 1 decides its frame’s end, and the padding after it, where the line ends as well, from the same registers as they then stand, its documentation giving it none of type 0’s exceptions: the register naming the last row, written with the row the counter stands on during that row’s last line, ends the frame wherever on the line it lands, and that register or the row height moved off the counters there puts the frame’s end off, padding or no padding. A write made on the line’s last character is too late for one thing only: it cannot take back the padding the last line stood on, whose lines are then counted against the registers as written. Raised from nothing at a line’s last character, the padding register also sets off a fault of a type 1’s own, which the seventh exception tells. The pointer a row hands the next one is left alone through the lines that pad a frame out on these two, where the others go on moving it through them — a type 1 at the end of each row the padding completes, a type 2 likewise from the address its last line took, save that the padding’s own last line takes the address again, and type 0 where the padding’s own count meets the row’s height: the documentation gives that padding to them “without updating the video pointer”. The register that counts those lines is read the same way as the row’s own limit on these two, so a program that drops it below the line already reached ends the padding there and then. Type 0 and a type 2 test it for equality instead and run on until the counter has climbed its whole round back to it, which is thirty-two lines; the second holds the frame open altogether, as described below.

The fifth exception is where the lines that pad a frame out to its full height are counted. Type 0 has no counter of its own for them and spends the row’s own line counter, so the row stands still while they run, one row past the frame’s last; the two ASICs spend it the same way and hold the row on the frame’s last row itself, as their documentation draws it and the suite’s group on the padding grades. Two of the others do have one, and go on counting the row beneath them, so the row counter advances through the padding. A picture built for one comes out of the other with its last rows differently numbered. No line the suite grades moves on those two’s counter itself: what stands behind that is the documentation’s own tables and a test of our own. Only one of those two machines latches a state when it opens the padding, and a program that then cancels the padding does not clear it: the frame stays open, and the program closes it on whatever line it likes by asking for the padding again. It comes to an end by itself as well: the row counter goes on being compared with the register that ends a frame, and on the row where it comes back round to it the counter returns to zero while the padding runs on, until the counter that measures the padding comes round in its turn. That group is the fourth of the five the suite grades, and it agrees with silicon on all eight of the lines it states. A line too narrow to reach the character where padding is taken back gives those two no extra line, where it gives type 0 one, as the eleventh exception has it.

The sixth exception is the border asked for by the register that names a screen’s last row, when it is set to zero. On type 0 and one other a frame’s first line comes out a chequer of bordered and displayed bytes, and a program can still take the setting back — until the character where the line’s own width is reached, after which the border stands for the rest of the frame. A type 1 borders outright on that setting, wanting no row to match it, and gives the border up the moment the setting changes, unless the change was made while the counter still stood on the frame’s first row, which keeps it for that frame. The remaining two have no conflict at all and read the setting where a line begins rather than through it, so one written mid-line waits for the next. No line the suite grades moves on any of the three: what stands behind them is the documentation’s own chapters and tests of our own.

The seventh is where a type 1 and a type 2 take the address a screen begins at. Type 0 and the two ASICs read it once, at the corner where all three counters stand together at zero, and keep it in the latch a row reloads from; a type 1 takes it into the pointer itself, and takes it afresh at the head of every line for as long as the frame’s first row is running. A program written for another machine that sets the next screen’s address early, while that row is still on, has it taken at once — which the documentation names as the reason a 1987 game loses the scenery behind its score on that chip. The documentation keeps that taking alive past that row in two further cases, and one of them is here: where a frame’s first row runs into the extra lines a frame can be padded with, the taking carries into the first row of that padding and no further — unless the register that ends a frame was moved above zero at the very last character of a line, which takes it away. The other is here too: a bug of that machine’s own, set off by switching on the extra lines a frame can be padded with at the very end of a line, or by widening a frame’s last line there and then taking its last-line state away, leaves the taking alive whatever row the chip is on, until the test that ends a row passes where the display ends — and that test then takes in the frame’s parity, so that on one frame of two it never passes and the row repeats. Which of the two frames fails is this emulator’s reading, from photographs of the suite’s group built on that bug, all nine of whose pages it draws as photographed. The machine answers as the variant the documentation calls 1-B, which leaves the parity out for one value of the padding register, as one of two photographs of the group that asks shows; the other photograph, of a machine without that exception, draws what this would draw without it. Another group built on the bug matches its photographs on four of its five pages. A type 2 does not read it for reaching that corner: its latch takes the address where the display of a line it has settled as the frame’s last comes to an end, and every line, the frame’s first included, begins from the latch. An address written later than that on the last line is too late for the next frame unless padding follows it: the documentation counts a line of the padding as a frame’s last where it describes interlace, and gives this machine’s interlace line the last line’s condition, and here the padding’s own last line takes the address in the last row’s stead whether or not it ends a row, which the documentation leaves open, that row and the padding’s before it handing the pointer on as any row does; a line made last, or a padding made to end, only by a write on the character the display’s width register names or after it hands the next frame wherever the latch then stands, which is this emulator’s reading too; and where the display never ends inside a line the address is never read at all, and every line begins at the same place. Photographs of the suite on a real type 2 bear out both ends of that: one shows a label repeated on every row, the label the suite placed at exactly the place the latch is left at, where type 0 repeats the screen’s first row; another shows a frame drawn from an address written too late for the last line, after twenty-four lines of padding, and a third the last row before a padding taking no address at all. Where the display is set to end at a line’s very first character, that reading is made before the line’s own last-line evaluation, the latch taking the pointer as the line is handed it rather than where the line before ran to, so a line begins where the line before it began — which is this emulator’s reading, from photographs of the suite on a type 2 and from the documentation having a new frame’s first line repeat the old frame’s last, the order the documentation gives the line’s first two steps handing on the other value; the line after a frame’s last takes the address, though only in part, keeping of it only the bits the latch also held: the documentation records this where the address was written after the last line began, and this emulator cuts every load made there short.

The eighth is how wide a window a write has to reach the comparisons a character clock settles. None of these machines has quite finished deciding a line’s end on the clock that took it: a write finishing on that same clock still moves the width under the comparison already made, so the line that had ended did not end and the counter goes on from the character it stood on. The window only takes an ending back: an OUTI landing on the clock with a width that names the character just left does not end that line there on any type, and nothing here grades it. A type 1 holds that window open across the character after it as well — its diagram runs to five placements where the others run to four, and the documentation names the reason a phase shift inside the chip. A character was drawn in that second microsecond and it counts: the counter goes on from what was drawn and not from the ending, or the line comes out a microsecond too long. On a line a single character wide that second microsecond is itself the next line’s end, and a type 1’s write reaches past it to the end before — its diagram draws that very line, a line of nothing given a width, with the write from one of the two instructions that can make it going on a character further on a type 1 than on types 0 and 2 — so the chip keeps the last two line ends rather than one. A write finishing on that same clock reaches another comparison too, the one that starts the frame sync, which a type 1 reads on the character the write lands on where type 0 and the two ASICs read it on the character after. A type 2 reads it there too, on the suite’s evidence alone: its documentation sets it beside type 0 in that phase and says only that such a sync is triggered at once. What that buys a program is the last chance the documentation grants such a write — up to the final microsecond before the comparison comes true — one microsecond later than type 0 and the two ASICs have it, the row counter having already walked onto the value being written, where those three do not read that equality until the character after, by which time the block described above has spent it. The board is what says which edge an access landed on, and a board that cannot say leaves every machine here without the window its line ending is kept in, this one without the second character besides, and it and a type 2 without the early frame-sync reading. What the chip cannot take back is the character it has already drawn — its address and its syncs went to the video hardware before the write arrived — so a machine spends one character of a line that turned out not to have ended, or two where a type 1’s write reached the second. It is spent twice over. Undoing what the ending did is not the same as making the comparisons the character would have made had the line run on, and all but two of those are lost. One is the row counter’s permission to count, which a line too narrow to reach its second character is refused, and which the line that runs on is given back. The other is a type 1’s alone: the character it drew since the end still meets the new width, so where that width names the character the end was taken on, the line ends there and the character drawn since begins the next. Two of the things the lost comparisons cost fall later in the line rather than on it. Type 0’s frame sync permission, described below, is raised at a particular character, and a character that never asks for it costs the following line its frame sync — on a line of two characters, which is the width the trick is used at, that loses a sync the chip raises. And where a line ends on the character just before the one the display’s width register names, the character the counter goes on to is the one a longer line would have met that register on — so the rest of the line stands displayed, where a machine whose line was that long all along has already gone to border. The suite grades the window at both its widths and not that character: in three of the six lines a group prints for this exact trick, and on a type 1’s record in a fourth, where the line is a single character wide. Two things about that width are ours rather than the documentation’s, and both follow a recording from silicon against a diagram. The second character is one placement past what the diagram draws, the last row of each of its three diagrams showing the line wrapping where the suite says it runs on. And the window is reached only by a write the board reports finishing on the character clock, which, on the three machines behind a Gate Array, one of the two instructions that can make such a write does and the other does not — so there they stand one microsecond apart where the documentation sets them two, the second instruction never being rescued where the first is. What the documentation is really about, one machine reaching a placement beyond the rest, is kept. Both readings are what carry three of that group’s lines on the type 0, 1 and 2 records, and move no frame of the demo records; the later ASIC’s record grades that trick too and agrees in all three, its program counting from an interrupt that comes a microsecond later on that machine, as described below. The two machines built around an ASIC take one of the two instructions that can make such a write a microsecond later still than the other three: the documentation puts that instruction’s write on the third microsecond for a chip sitting behind a Gate Array and the fourth for an ASIC emulating a chip, and that microsecond is here. The board holds such a write for the next character clock, and it lands after the comparison that clock makes, a microsecond after the same instruction’s write behind a Gate Array: the ASICs’ own chapter warns of that microsecond, and the documentation’s line-ending chronograms draw exactly it, both for a line given a wider width at its end and for a line a single character wide given one, and the suite’s analyzer group, which starts its instructions a microsecond early on these two to make up for it, finds on both the row its photographs show. Two kinds of write are handed to the chip ahead of that comparison. One is the line sync’s position or width, which the documentation’s drawings of these two chips show taken on the character the write lands on, and which photographs of six of the suite’s groups on both show the same way; the other instruction’s write, begun on the clock, still lands after it, which is wrong: the suite’s photographs show the two alike. The frame sync’s width shares the line sync’s register and comes over with it, so a write held to a line’s end during a frame sync is counted against a line sooner, which nothing grades. The other is a width lowered onto or below the character the counter stands on: the suite’s interrupt group writes a width onto the very character it names, its program for these two a microsecond shorter to make up for their later interrupt, and photographs of both show the line ending there, which a write landing after the comparison would end a character late. That a lowered width is taken a microsecond sooner than a raised one is ours, from those two groups. The other instruction’s write, begun on the clock, reaches the window as behind a Gate Array, so behind an ASIC the two instructions’ last placements in time stand level, where the documentation draws them a microsecond apart; behind a Gate Array they stand a microsecond closer than it draws them. The documentation’s drawing of how an ASIC samples the bus names no instant inside the microsecond. A read of one of the chip’s write ports, which writes it, is still taken where it falls on those two. Their counter never exceeds the width, the documentation says, of widths raised in time; that a width written under the counter ends the line on the next character, where the other three send it the long way round, is ours, and so is a line ended under such a width staying ended where a write landing on the clock would otherwise take the end back. The chip’s other readings of the counter meeting the width — the status registers’ bits for it, the question interlace puts at a line’s end, a row height moved at a line’s end — still ask for the two to be equal, so a counter left past a lowered width answers them as a line not at its end, and nothing grades it. The suite’s status group, photographed on both, reads a counter come home on lines two and three characters wide made that way, and the groups that draw screens four and two microseconds wide draw them as photographed only with it; one of the status group’s plots is photographed with eight of its marks a line sooner than this draws them, measured at moderate confidence.

The ninth is the delay a program can put on the display through the register that also asks for interlace: the documentation gives it to type 0 and the two ASICs and withholds it from the other two, and here those two keep only that register’s interlace bits, so a delay asked of them does nothing — which is what photographs of the suite on both show, where the same test bends the border on the other three. Nor do the two ASICs, under a delay, end a line whose width register was never reached with a character of border, as type 0 does: the documentation says they send no border there, and photographs of the suite on both show unbroken the bars it would break.

The tenth is a line sync that ends on a character where it would start again. Type 0 refuses the second sync there, as described below; the other four run the first one on, its width counter going round before it can meet the width again, so a line one character wide with the sync starting on it carries a sync that never ends — and the interrupts, which the Gate Array counts on the syncs’ ends, stop. That is the documentation’s account, and the suite’s interrupt group reads it on all four records, as photographs of it on real machines of all four types do. Photographs of the suite’s Gate Array sync test on both ASICs show such a run-on: a six-character sync ends where it would start again and has its width rewritten there to one its counter has already passed, so it counts round to it, seventeen characters in all — the last of them black at the band’s visible left edge on the test’s first form, the whole of them a block of black on its second; its photographs on type 0 show that type’s refusal, a block of six, and on a type 2 the same tail as on the ASICs, a character further from the text, the ASICs handing their syncs on a character late. Not here: the instant inside a character where a type 1’s run-on sync falls and rises, after which its Gate Array starts counting again and sends the monitor a second sync, which a pin reported once a character cannot show, the interrupt group finding silicon not counting it; nor the slant a type 1’s photograph of the test draws its band with.

The eleventh is a line too narrow for its character counter to reach its second or third character, which type 0 spends on rules its documentation gives it alone, described below. The other four count such a line as any other, their documentation saying a width of nothing neither stops their scanline counter nor adds a line to their frame, and raise a frame sync where the row counter meets its register, their chapters asking nothing of the line before as type 0’s does. The suite’s analyzer group measures the counting: the row it finds after seventy-four microseconds of lines a character wide is the one photographs of real machines show on a type 1 and both ASICs, a line every microsecond, and on type 0 the standstill’s; on a type 2 the group finds nothing, as photographed, whatever the counting. The suite’s groups drawn on lines a character wide come to their photographs on a type 1 by this and the twelfth exception, and one of them on a type 2 by this alone, save two end columns, and so does the frame sync a page of the frame sync group raises after such a line on a type 2. What the documentation leaves open a type 1 answers by a reading of ours, from that page’s photograph: its frame sync’s own count of lines holds on a line a character wide while its other counters go on, where a type 2 spends the count a line a microsecond. On the ASICs the page still differs from its photographs in six of its sixteen readings, four where silicon is in the frame sync and this machine is not, and two the other way round; their count is spent as a type 2’s here, a reading of ours that page leaves unsettled. A line a character wide that brings the row and scanline counters home at every end is a frame a microsecond long to a type 1 and the ASICs, and their frame parity turns at every character. A demo that runs no line that narrow once the firmware has set the width, power-on leaving it at nothing until then, moves with this only by the phase of its boot.

The twelfth is the byte of border at the end of a line whose display-width register was never reached, which the documentation gives type 0 and a type 2 and denies the rest, so screens a few microseconds wide, and a display whose width register is never reached running on into the next line, are drawn unbroken on the other three. Photographs of the suite show no such byte on a type 1 and both ASICs, and show the alternation the documentation gives a type 2 where the width is nothing.

The thirteenth is the display’s width written during a character, which is judged there as well as where the counter next meets it, each type reading the old width and the new its own way. On types 0, 1 and 2, a width that the character being drawn meets borders the rest of the line and that character with it, its signal having already left the chip, so the machine borders it on the way out, unless a delay holds the display back, which keeps that character’s picture; the two ASICs wait for the counter to meet the new width. Where a row’s next address is taken there, a type 1 reads the new width alone, so a width moved away from the character just met takes the address back, a type 2 and the two ASICs read the old one alone, and type 0 reads both, which the documentation does not say. A group of the suite writes every one of these, and photographs of it on real machines of all five types draw what this does, save one page on a type 2, where silicon draws a fine repeating pattern and this draws black, which is not yet understood. Of a type 1’s status register only the border bit is answered; the bit beside it reports a light pen, and no board here wires that pin, so it and the two registers the pen would fill stay empty. Where silicon gives one of two bytes for a register it never defined, this gives the larger of them, and nothing outside this repository grades the choice. The behaviour behind the name is what the rest of this work is.

Two more of that register’s bits govern the border, and both of the things they ask for are here. The display can be handed back a character or two late at each end of a line — the delay falls on the signal as it leaves, not on the comparisons that raise it, so a program that takes the delay off after the border it deferred keeps the rest of its line — or it can be shut outright for as long as those bits stand. Shutting it silences the interlace bits below as well, on a sentence the documentation itself marks as wanting further investigation. Two more answers are ours rather than the documentation’s: shutting the display does not cancel a border the row register has already raised — the documentation asks that question and leaves it open — and the byte-by-byte alternation described below follows the delayed signal rather than the undelayed one. No line the suite grades covers any of it: the suite that tests these functions draws pictures rather than marking itself, and what stands behind their placement is our reading of the documentation. Photographs of those pictures on real machines show the delay where the documentation gives it and on neither machine it withholds it from, but on type 0 the border the delay cuts falls on lines whose write came a microsecond earlier than on silicon, which is not yet understood. The firmware’s own screen asks for none of this; it is demos that reach for it. Two bits above them delay the cursor the same way, and the cursor is not here at all.

The extra lines a frame can end with are set up on every last line and taken back at the third character if nothing wants them, which is the order the chip works in. A line of two characters cannot reach that character and keeps what it was set up for; a line of one keeps it too, where the stopping falls on the character the line begins with — the single row advance the stopping still lets through is the run beginning rather than a row ending, and it carries the setting-up past the very last line that same advance unmakes. A run already under way is past cancelling, however the registers move afterwards: it ends where the count of lines says and not before. So a run can begin under a stopped picture and outlive it: the row holds where the stopping left it, and when the line is widened again the scanline goes on being counted against the run’s own length until it is done. The single line the documentation grants such a narrow line is drawn here too, and the order of the asking is what tells the two cases apart: a line too narrow to be taken back is entered and then measured, so it draws one line and ends on the next, while a wider one is measured first and may draw none at all. That is not a nicety: built the other way for every line alike, the machine would answer a line too long in four of the seven places the suite grades such a line. What the narrow line itself shows, nothing outside this repository has measured.

The cursor and the light pen will stay missing: no board wires their pins, so nothing they do ever leaves the chip. Not implemented in the 6845: those two, and some of the states this type settles at the head of a line and acts on later. Three are here: whether the frame’s last line is followed by extra ones is settled where the chip settles it, at the third character of the line rather than the first, so that a program is obeyed whether it asks for those lines in time or takes them back. A register moved at the second character can also turn that last line into an extra one itself, and moved there the other way it makes a last line of a line that was not one, the row counter coming home under it, which is the chip’s own way of adding a line without asking for any. The second is the frame sync’s own permission, raised at the third character of every line and spent at the head of the next: a line too short to reach that character costs the following line its frame sync, and spends the comparison that would have raised it, so widening the line again does not bring it back. A program that shortens a line to two characters — which demos do, building a screen out of lines a microsecond or two wide — loses a frame sync it never meant to, and one that sets the sync register to the row it is already on gets a frame sync only if the line has passed its second character and the row is not blocked already — as, for one, a short line leaves it when the next line begins with the register already naming that row; at the head of a line it gets the block instead. The third is the permission the counters themselves are given, withdrawn at the head of every line and returned at its second character: a line of a single character never reaches that character, so nothing is managed again, and the row, the scanline within it and the frame sync’s own count of lines all hold where they stand — which is why a frame sync begun there never ends. What the last managed line had already decided still lands, and a row advance is all of it that can. The documentation gives that standstill to type 0 alone, and so does this machine, the eleventh exception above. Standing still on a frame’s first character the chip begins no new frame: it does not take the screen’s address afresh, and a border the bottom-row register throws there stays for the rest of the frame. A line one character wide whose display is delayed is border from end to end, which is this emulator’s reading of the order things happen in inside that one character. And a width of one widened on the second character of a row’s last line moves the row on without bringing the scanline counter home, so on the frame’s last row the extra lines of padding follow, as the documentation draws it and photographs of the suite on a real type 0 show. One thing about a width of nothing is settled, and it is ours rather than the documentation’s: where a width of nothing arrives under a counter that has already left the head of its line, this machine puts the counter home at once instead of leaving it to climb its eight bits and come round. The documentation describes a width of nothing only from a counter already there, so what stands behind the rest of it is one group of the suite, which grades it on four of the five types and says nothing of the earlier ASIC — agreeing on all four. The line sync’s width is counted in characters rather than lines and goes on counting, so a line narrow enough to sit inside it ends before its own sync does. Every other comparison is made afresh at each character, which agrees wherever no register moves mid-line and parts company the moment one does. The width of the display is also judged the moment it is written, as the documentation asks, which the thirteenth exception above tells type by type. Two of the border’s rules move the display enable inside a character, on types 0 and 2. One puts a byte of border at the end of any line whose width register was never reached, unless a delay is programmed, which carries that border to a whole character of its own instead. The other alternates shown and bordered bytes along a frame’s first line, where the row register is zero. The chip is still stepped once per character; what it reports for each of them is that signal as each of the character’s two bytes finds it, which is as fine as those two rules ever ask for.

A line shorter than its own line sync cannot run two of them back to back: the second is refused, so the syncs come every other line rather than in one unbroken stretch — which is what stops this type falling into the endless sync the others do, and it matters because that pin is what the Gate Array counts its interrupts on. A program can have the second after all by writing the sync’s width on the character the first ends on, whatever value it writes there, and then the new sync carries the old count on instead of starting from nothing. Two things about that second sync are not here. The chip drops the line for a fraction of a character between the two, which the Gate Array sees and draws as a sync of its own, where this pin is reported once a character and can only stay high; and a width written there that differs from the old one is read a character early, by the comparison that ends the first sync as well as by the one that starts the second. Nor do the first three types raise a sync whose position is written onto the very character the counter stands on, nor the two ASICs where the instruction writing it begins on the clock: the documentation has the chip raise that character’s sync all the same, and here the sync waits for the line after.

The Gate Array holds the colour registers, the ROM enables, the video mode, and the interrupt generator: the six-bit counter of line syncs that raises the interrupt every 52 lines, a microsecond after the end of the line sync that asked for it — and the same microsecond on the interrupt the frame’s own check raises, which the documentation does not say either way — the request line held until the processor acknowledges, bit 5 killed at the acknowledge, and the rule that, two line syncs after a frame sync begins, the counter is cleared — raising an interrupt on the way only if bit 5 was set. On the two machines built around an ASIC the Gate Array is handed both of the CRTC’s syncs a character late, their documentation having the line sync begin “at the start of the display” of the character the CRTC began it on, and the frame sync going with it so that the two arrive in the order they left: every interrupt there comes a microsecond later than on the other three, less the quarter of one given below, the black painted over each sync and a change of mode waiting on the line sync come a character later with them, and the picture sits a character further left, as the documentation says of the later ASIC plugged into the monitor of one of the other three; the monitor here is set up once for all five, where the one sold with the later ASIC’s machine was set to centre its frame. What a program reads of the frame sync is still the chip’s own, undelayed. The counter acts on a line sync a microsecond after it ends, on the second of the four processor cycles that microsecond is made of, and on the two built around an ASIC on the first. The documentation draws where a line sync’s black ends, to the sixteenth of a microsecond: in the second of those cycles on types 0, 1 and 2, and in the first on the later ASIC. It draws no end for the earlier ASIC, and assumes its black begins a little before the later one’s, which as wide would put its end in the first cycle too. Where it sees half a pixel more displayed, the later ASIC’s black begins a sixteenth later, which leans the other way, and it finds the interrupts of a type 2 and the two ASICs generally the same as type 0’s, with a few small differences. That the interrupt keeps the cycle the sync’s black ends in is our reading, and the suite agrees with it on both ASICs. The counter hears an acknowledge where the processor’s acknowledge cycle ends, which is always on that second cycle: one microsecond on from a cycle begun in the first half of a microsecond, two from one begun in the second. Which of the two comes first is the whole of a race the documentation describes, and within any one microsecond the count comes first: on the same cycle behind a Gate Array, a cycle sooner behind an ASIC. A request the count raises in the microsecond an acknowledge is heard in is left standing, the acknowledge answering the interrupt the processor was offered, and nothing grades that either way. An acknowledge heard before the count that gives the counter bit 5 takes away a bit the counter has not yet gained, so the counter keeps it and the next interrupt can come twenty lines later instead of fifty-two; heard after, the bit is there to be taken and the full fifty-two stand. Which of the two a program gets is settled by the length of the instruction it let the interrupt through on, and it is the one place the processor’s own timing reaches inside the interrupt generator. An instruction whose last cycle falls before that second cycle does not see an interrupt raised in its final microsecond and runs one more instruction first, as the documentation describes the race and as the suite finds it on the machines whose line syncs do not come out of an ASIC. On the two that do, the request comes a cycle sooner, the same instruction sees it, and the suite finds them catching the interrupt one instruction sooner. The documentation times that race to a sixteenth and finds the type 1 parts differing from one another on that scale; a program that sits on the boundary gets the answer a type 0 gives on the three machines with a Gate Array, and on the two ASICs the answer an interrupt a quarter sooner gives. Taking an interrupt costs five microseconds after a one-microsecond instruction, as the documentation measures it, and the instruction that does the same thing from code costs the four it gives. It turns each pair of fetched bytes into sixteen colour samples in whichever of the four modes is in force, and a mode change is taken at the next line sync rather than at once: where the sync ends if it ends within six microseconds of beginning, and six microseconds in if it runs longer, and not at all from a sync of a single microsecond — as the documentation draws it, and as photographs of the suite on a real machine show it. Where inside the character it is taken on the new mode begins, a few pixels in, is finer than a character and not here. A colour’s timing is finer still, and is here: one written to the Gate Array reaches the character it is showing part of the way in, from that character’s second byte, and half way through its first on the earlier ASIC, the samples before keeping the old colour, as the documentation draws it for every instruction that can write one. Photographs of the suite’s group that times a colour against the beam agree on every type within what they resolve, save that on the two ASICs its boxes end a column or so later than here, which is not yet understood. Its palette is the one measured on the outputs of a real 40010, so the colours are the ones a machine produced rather than the ones its logic implies.

Not implemented: the 40010’s habit of starting mode 2 one pixel early, which the later ASIC shares, and with it that machine’s change of colour a pixel sooner in that mode. Shaker’s E (5) draws those edges in a picture, and nothing grades them.

The monitor is a cathode ray tube and knows nothing about computers. It receives colour samples and one composite sync line, and separates line retrace from frame retrace by how long the sync is held — which is what a tube does, and the reason a program that trims a sync pulse short moves the picture sideways: a line is timed from the middle of its pulse, so a shorter pulse walks that middle earlier and the picture half a character to the right.

Not modelled: the flywheel a real tube runs its horizontal oscillator on. Every sync edge retraces here, and a frame that outruns the tube clamps at the bottom of the screen where a real monitor would lose vertical hold and roll.

Timing

The machine runs at the right speed, and the reason is the Gate Array rather than the processor. It holds the CPU off the memory for three cycles in four, leaving it the fourth, which rounds every machine cycle up to a whole microsecond and costs the processor a quarter of its nominal 4MHz. That tax is what makes a CPC a CPC: it is why a program that counts instructions to reach a raster line reaches it.

The chip knows nothing about which cycle the processor is in. It generates the same pattern continually, and the CPU meets it wherever its own sampling happens to fall — which is how instructions whose lengths do not divide by four end up linearised onto the microsecond anyway.

A byte a program writes into the screen is read for the picture in the microsecond the write is made, the same on all five machines, as the documentation draws it for three instructions and as photographs of the suite on real machines show for the others it tries. On that fourth cycle the chip reads a character’s two bytes after the processor’s own access, and every memory cycle finishes there, so a write the hold never stretched — the one LD (HL),r makes is such a write — is seen on that very cycle like any other.

Sound and the keyboard

The keyboard is the real matrix: ten lines of eight switches, read the long way round — the processor asks the 8255, which asks the sound chip, which reads the grid. Both joysticks are there, including the one that shares its line with the letters, which is why its directions can be played from the keyboard.

Not modelled: keyboard clash. On hardware, three keys held at the corners of a rectangle in the matrix conjure the fourth, because the switches are a grid of wires with nothing to stop a current going the long way round.

The 8255 implements mode 0 — plain input and output — which is the only mode any program on these machines selects. A port turned to input presents &FF to whatever is wired to it, which is not a detail: it is what the device on the other side reads while the processor is reading, and getting it wrong stays invisible until something depends on it.

The AY-3-8912 sounds. Its three tone channels, its noise and its envelope run from the board’s sound clock, a microsecond a cycle and eight cycles a step, and each channel’s level reaches the stereo socket, A with B on one side and C with B on the other, joined as the board’s resistors join them. SOUND 1,142 plays 440Hz there, the 6128 manual’s international A, an octave below what the firmware manual’s formula gives. The keyboard is read through the same chip.

Five things are not known or not modelled, and what the sound rests on in their place is said here.

The levels become voltages by one measurement. An output’s voltage depends on what it drives, and the one published measurement with its loads stated is of an AY-3-8910 into 983Ω, the nearest of them to a CPC’s; that column is used. How the three outputs pull on one another through the board, a few tens of millivolts, is left out, and the socket is taken as a listener that draws no current would find it.

The CPC’s speaker is not heard. Its path filters, amplifies and has a volume control, and none of that is modelled; what a host plays is the socket.

Where nothing measured the chip, it follows the die. A period of 0, a period written below the count, and a write to register 13 while the envelope runs all do what the chip’s reconstruction from photographs of its die does, which no published measurement of an AY checks.

The sound clock’s phase is a guess. The board names the net and its rate and not its phase against the processor’s clock; here it is the character clock. Nothing a program reads depends on it.

A snapshot carries no generator. The format holds the registers and not where the counters stand, so a machine resumed from one starts its tones and its noise afresh, as from reset, whatever it held before; an envelope whose shape ends or holds is put where it rests, as one started long before would be, and one that repeats starts its cycle again.

An address above 15 selects nothing. The chip takes the top four bits of an address as its own select, and an address that fails it leaves the chip deaf to writes and silent on reads until one passes, as the silicon does; a reset leaves it so too. A program reading it then gets &FF, the lines between the two chips standing high with neither driving them, and Kevin Thacker’s hardware tests note &FF there from CPCs, at the addresses they tried, and from a Plus at every one; a KC Compact gives something else.

The cassette

The deck is the same one a Spectrum has, wired to the pins a CPC brings it to: bit 7 of the 8255’s port B is what is at the play head, and bit 4 of port C is the motor. That motor is the difference between the two machines. A CPC starts and stops the tape itself, so a program may take as long as it likes between blocks; a Spectrum has no such line and must keep up with a reel that never waits.

The reel follows the motor 50ms late, when the line rises and when it falls. A real deck does not start or stop the instant its relay switches: the motor has a flywheel to bring up to speed and leaves it running down. Opera Soft’s loaders depend on it, stopping the motor at the end of a block and reading on. Images of Goody, The Last Mission and Livingstone Supongo sampled from the original cassettes load here only when the reel runs on after the line falls — one of them at 40ms, all three at 50ms — in runs made when the figure was chosen, which no tier repeats. That is a floor, not a measurement: no source measures how long a CPC’s deck takes to come up to speed or to run down, and a deck whose reel runs on longer may load tapes this one refuses. The lag counts the time the line has been high against the time it has been low, so the firmware’s keyboard scan, which drops the line for microseconds twice a frame, never reaches the reel.

A tape begins with the cassette’s leader tape. Rewound, a cassette has clear leader tape in front of the head, which carries nothing, and an image begins at its first sound or wherever its maker cut it — many begin at their first edge. So 5.7 seconds of silence is played first, less whatever silence the image kept before its first edge; one that kept more plays as it is. The figure is the standards’ nominal cassette’s 350mm of leader tape, less the 79mm of it that runs from the take-up hub to the head — measured on the standards’ drawings and, for the one roller they leave unplaced, on photographs — at 4.76cm a second. It matters on a CPC, whose firmware starts the motor and waits two and a half seconds for it to come up to speed before it listens, so a leader tone that opens an image would be gone before anything heard it. Some cassettes were sold with no leader tape at all; an image of one gets it here.

What it does not supply is blank tape after the leader tape, which a duplicated cassette may also have carried, nor silence between the firmware’s blocks, each a header record and its data record. Between two of them the firmware turns the motor off for a fraction of a millisecond, which the reel’s lag absorbs, and then waits the same two and a half seconds with the tape running, so the next block’s leader tone has to outlast that wait less the silence in front of it. One with too little silence in front of it is lost, as the arithmetic says it would be on the machine at the tape’s nominal speed. battro.cdt, whose image leaves a millisecond between blocks, keeps about 15ms too little of its third block’s leader tone and fails there, in a run no tier repeats; it would load only on a deck running more than 0.6% slow.

What the machine writes to tape — port C’s bit 5 — goes nowhere. Nothing here records.

The format is the .cdt, which is a .tzx under another name, so one reader serves both machines and refuses the same blocks. A .tap is not one of them: it is bytes at the Spectrum ROM’s own timings, which no CPC firmware can read, so it is refused here rather than played into a machine that will never make sense of it. A tape is loaded through a 464’s own firmware in the machine tier: a program written in the format given by chapter 8 of SOFT 968, the firmware manual, with a leader tone of 3000 pulses, about two seconds, and nothing in front of it — too short to outlast the motor’s wait unless the cassette’s leader tape comes first. The firmware has to find it, check every CRC and run it. No image of a real cassette is loaded by any tier.

Snapshots

A machine writes itself out as an SNA snapshot and another reads it back and carries on. Versions 1, 2 and 3 are read, taking the fields they share; version 1 is written, because every emulator can read it.

A snapshot loads only into a machine that can hold it. A 128K dump will not go into a 64K machine, and neither will a snapshot that records the PAL holding anything but zero: a 64K machine has no PAL, so that snapshot was taken on a machine of another shape.

One field is written against the format’s own notes. The sound chip’s selected register is asked to be 0 to 15, and a chip a program has left unselected has none; it is written as the register it last selected plus 16, so that a reader keeping four bits finds that register. CPCEC writes the whole latched byte there instead. Any byte above 15 is read back as a chip unselected.

What version 1 cannot carry is the CRTC’s internal counters, so a machine resumed from one restarts its frame instead of continuing mid-raster. Version 3 has room for them, along with the CRTC type and the drive’s motor and head, and none of it is read: a machine is built with its CRTC type, and the rest is worth reading the day there is something here that can act on it. No version has room for the cassette’s reel, which follows the motor a little late: a machine picked up from one has its reel at speed if the motor line is high and still if it is low, so a snapshot taken while the reel ran on after the line fell — the moment an Opera Soft loader reads its last bits — comes back with the reel stopped.

Discs

A disc is three things here, and each is its own part: the medium, the drive that turns it, and the controller that reads it.

The medium is a sectored floppy as an IBM System 34 controller finds it. Both DSK layouts are read: the original, which gives every track one length and every sector the same allotment, and the extended one, which gives each track a length of its own and each sector the length it truly occupies. That second one is what lets an image describe a disc that lies — a sector announcing a size it does not hold, an identity with nothing recorded behind it, a data field that reads differently on each revolution — and none of the disagreements between what a sector claims and what is true are corrected, because protected discs are built out of exactly those disagreements. A size code of eight or more announces 32K, which is how the chip itself counts; the image definition’s older rule that only three bits counted was retracted by its own author, and Arnold’s table measured on a chip agrees. An image that records where its sectors lay, in the Offset-Info block SAMdisk writes, is believed, its entries counted and measured as SAMdisk counts and measures them. The block’s definition gives an entry to each track the image holds and does not say which byte of a sector an offset measures to; SAMdisk, the program that writes the block and the work of the definition’s own author, gives an entry to every cylinder and side and measures each sector to its identity mark. A sector whose bytes reach past the end of its track carries on into the next revolution, as it was written: one placed so near the end that its data runs past the index, or a track’s last sector stored with more of the gap behind it than the revolution leaves room for, which is how SAMdisk reads a last sector, on past the index up to the first sector’s identity. What passes after the index is the same reading as what passed before it, so the field reads as one reading and passes or fails its check as it did on the disc. An image that does not record where its sectors lay has them laid out where a formatter would have put them, gaps and all, with the gaps shortened evenly when the revolution has no room for them. Every byte of a track then has a position, so that a controller told to read past a data field finds the check, the gap and the next sector’s identity — which is what some protections read past the end to look for. A disc can be written in place: a data field rewritten, a track formatted again into the room its image already gives it. An image written back is written in the extended layout, with the sector positions, and reads back as the same disc. A disc holding a track that layout cannot describe — one taking more than 65280 bytes of image, header and all, one whose revolution is longer than 65535 bytes, or one with a sector whose identity mark comes round only after the index — is not written at all.

A track formatted larger than it was takes fresh room past the image’s end, as far as the host said its buffer reaches, and so does a track the image never had, since a format is how every track of a blank disc is made; the command line gives every disc room to format each track once more. A track formatted past the image’s last cylinder or side makes the disc that much larger. Not modelled in the medium: a track with more than the 29 sectors a header describes, a data field written where the image recorded none, and a format with no room left anywhere — it is reported as a write-protected disc, which is the nearest thing the software can show. Nor is a first sector whose sync began before the index, which an image records by putting that sector’s identity mark fewer than fifteen bytes after the index: the track loses every position its image gives and is laid out as a formatter would have laid it, so its sectors pass under the head where a formatter would have put them rather than where the disc had them. A track recorded at any rate or in any mode but the one these machines read holds nothing a head can find, and keeps its place and its bytes so that an image written back still carries it, padded with zeros to a whole number of the extended layout’s 256-byte units when the original layout’s one length left it short of one.

The drive owns the motor, the head, the side and the turning. A revolution is 200ms at 300 rpm and a byte passes every 32µs, so a sector is found when its identity comes round and not before. Drive A is the machine’s own one-headed 3" drive and B is the connector for a two-headed second; the motor port turns both, as the board’s does. READY waits for the disc to turn. Amstrad’s service manual gives the 6128’s drive two revolutions after the disc goes in, taken here as three index pulses, so READY rises at the third index pulse after the motor starts or a disc is put in — 400 to 600ms on as the disc stands, and always 600 after a disc goes in — and falls the moment the motor stops. Kevin Thacker’s drive test, timed on a 6128’s own drive, counted 1735 to 2564 polls of Sense Drive Status before READY rose, and none before it fell. A poll there is no shorter than here, where the controller answers each command byte at once, so its longest wait was at least 0.55s, longer than the second index pulse could give; its whole range fits the third if a poll there took 8 to 10% longer than here. Run here by hand, in a run no tier repeats, it counts 2048 to 2305 and none. The 6128 Plus in his notes rises sooner, so drives differ. The disc itself turns at full speed from the moment the motor starts; how it comes up to speed is not modelled. Nor is a drive that holds READY after its motor stops, which no measurement shows: a loader that seeks with the motor off is told the drive is not ready, as that 6128’s drive would tell it. Not modelled besides: the mechanical stop a head hits past its last cylinder, so a head steps as far as the medium is wide and finds unformatted tracks there.

The controller is a µPD765A, ticked in microseconds. Every command in its set is implemented — Read Data and Read Deleted Data, Write Data and Write Deleted Data, Read a Track, Read ID, Format a Track, the three Scans, Seek, Recalibrate with its limit of 77 steps, Sense Interrupt Status, Sense Drive Status and Specify — and every one of them is graded by a test except Scan Low or Equal and Scan High or Equal, which share Scan Equal’s path and differ only in the comparison. With them come the three phases and the RQM, DIO, EXM and CB handshake the operating system polls, since neither the interrupt nor the DMA lines are wired on these boards. The data moves one byte every 32µs and a processor that has not collected one in time has overrun. Every field is judged by the bytes that pass: the chip runs its check over the marks, the identity or the data and the two check bytes, and reads the data mark for itself, so a sector a formatter wrote at one length under an identity announcing another fails its check here as it does on the disc. A command in FM finds nothing on these discs, which are MFM. The drives are polled for a change of READY between commands, at the datasheet’s interval doubled for the 4MHz clock, as every other timing is; a check on that doubling is that AMSDOS programs a 6ms step rate in the datasheet’s units and documents it as 12. Terminal count is a pin the machine never raises, so a read that reaches the last sector it was asked for steps past it and ends with the end-of-cylinder flag set — which is what every read on a CPC looks like, and what AMSDOS tests for.

Not modelled in the controller: DMA mode, which the boards cannot use, is taken as non-DMA mode except that EXM stays low; the microseconds the chip takes between one command byte and the next RQM, and between the end of execution and the first result byte; and the Version command, which this revision of the part does not have. The Scans compare sector by sector but their result is an approximation of the datasheet’s table.

On the CPC the interface answers at A10 and A7 low — the motor at A8 low, the controller’s two registers at A8 high — and is built into the 664 and 6128; a 464 gets it plugged in, with the AMSDOS ROM as upper ROM 7, the day a disc is given to it. That an access held by the wait line reaches a device once, as the memory section says, matters here more than anywhere: this chip hands over its next byte on every read.

The ZX Spectrum

A Spectrum is one chip where a CPC is two. The Ferranti ULA counts out the frame, reads the screen, turns bytes into pixels, raises the interrupt, holds the border colour and gates the keyboard, and the board around it is little more than sixteen kilobytes of ROM, some RAM and eight address lines running to forty keys. What that buys is a machine with almost no wiring to get wrong; what it costs is that everything interesting happens inside the one part.

It boots its firmware, shows the copyright message Sinclair put in it, and answers arithmetic typed at the keyboard. The picture is read back off the beam and not out of the display file, so the serialiser, the composite sync and the tube are all in the path that is checked.

The screen and the processor share one bank of memory, and the ULA settles that argument by stopping the processor’s clock rather than by asserting a wait line — so a held T-state here is one the processor does not run, while the beam runs on without it. What it costs depends entirely on when: an instruction that reads and writes the screen can take twice as long as the book says, and the same instruction begun a few T-states later costs nothing at all. That is why a Spectrum program’s speed is a property of where in the frame it began, and why one that needs its timing to hold waits for the interrupt before it begins.

The interrupt itself is a window rather than an event. The ULA holds the line low for the first thirty-two T-states of a frame and no longer, and the processor looks at it only on the last T-state of an instruction — so an instruction that ends on the thirty-second takes it and one that ends a T-state later waits a whole frame. Nothing on this board drives the data bus while the acknowledge is on it, so what the processor reads is the floating &FF; in mode 2 that is what makes a Spectrum’s vector table 257 bytes long and read at I:&FF.

A memory access is charged once, at the T-state it begins. An internal T-state that only holds an address is charged like an access of its own, because these ULAs weigh the address and not the request. A port is charged by a rule of its own, at up to four of the T-states of the one access, turning on the port’s low bit as well as its address. An interrupt acknowledge is charged nothing, and nothing here can settle whether the chip would: the acknowledge always falls in the top border, where nothing is owed under any rule.

Two things are missing.

The EAR socket hears nothing the machine itself writes. On hardware the EAR and MIC sockets and the speaker share one ULA pin, so a program that writes to bit 4 of port &FE changes what bit 6 reads back, by a path whose exact behaviour is what separates an issue 2 board from an issue 3. Here the socket hears the deck and nothing else. Loading is unaffected — the tape drives the pin — but the handful of programs that measure the machine by writing and reading their own bit will not find what they wrote.

The floating bus is not modelled. The chip fetches ahead of the beam through a pipeline that this collapses into a single T-state, and that pipeline is exactly what a read of an unattached port observes. Software that steers by it — waiting for the beam to reach a particular place by watching what the bus happens to be carrying — will not find what it is looking for.

The speaker sounds. The ULA drives one pin for the speaker and both tape sockets, at a voltage set by bits 4 and 3 of the last write to port &FE, and the speaker hangs off that pin through a diode and a transistor, two silicon junctions the pin must clear before more than a little passes. Bit 4 clears them and is the beeper; bit 3 alone, which is what a SAVE toggles, does not, and is not heard; bit 3 beside bit 4 lifts the speaker from 2.16V to 2.30V. BEEP 1,0 plays the ROM’s middle C, a cycle every 13,372 T-states as its own loop counts them, 261.74Hz. What a host plays is the voltage across the speaker.

Four things are not known or not modelled, and what the sound rests on in their place is said here.

The pin’s levels are one board’s, and the speaker’s are reasoned from them. The board is an Issue 3, whose four voltages were measured. An Issue 2’s stand a little higher by the same measurement, and Sinclair’s service manual draws its speaker hanging off two diodes rather than a transistor; neither is modelled. The two junctions between the pin and the speaker are taken at the textbook 0.7V each, and the little current they let through below that, which the service manual calls barely sufficient to drive the speaker while a SAVE runs, is left out.

Every edge is square. On hardware the capacitors between the pin and the two tape sockets slow its fall: with bit 3 left set, bit 6 has been measured still reading high for about 180 to 2800 T-states after bit 4 fell, longer the longer bit 4 had been set, and without delay when it rose. Bit 6 turns at 0.7V, just above where that write leaves the pin, and the speaker at twice that, so the speaker’s own tail is shorter, by how much no measurement says; here the speaker follows the port at once.

The tape is not heard. On hardware the EAR socket reaches the same pin through a resistor and one of those capacitors, and nothing switches it, so a playing tape reaches the speaker whether or not anything is loading; Sinclair’s introduction booklet says one played too loud may be heard there. How loud turns on the recorder’s volume and on the ULA’s output stage; here a tape loads in silence.

What the speaker makes of its voltage is not modelled. It is a small moving coil, and no measurement of its response was found.

A tape plays, in both the formats a Spectrum’s came in. A .tap is bytes at the ROM’s own timings; a .tzx records the timings themselves, block by block, which is what a tape that brought a loader of its own needs — and most commercial releases brought one, for speed and to be hard to copy. Either way the deck plays the edges and the firmware measures the time between them, exactly as it did on hardware. A Spectrum has no motor line, so the tape turns from the moment it starts and the machine cannot stop it, which is why loading was always a race the program had to win. It begins with the cassette’s leader tape in front of the image, as every tape here does.

What a .tzx can still ask for and not get is a waveform recorded sample by sample, data built from a table of symbols, or blocks visited out of order by a jump, a call or a menu. So is a block whose identifying byte the reader does not know, because the format gives a block’s length nowhere but its own table and an unknown one cannot be stepped over. All of those are refused when the tape goes in rather than played wrongly, and a refusal is the whole tape and not one block. So is an image that does not hold together — one whose blocks run off its end, or whose last byte of data claims to carry more than eight bits — because a tape read past its own bytes is worse than a tape not read.

A loop is played, its blocks as many times as it asks, which is how most images of Speedlock tapes write the leader in front of a block — Match Day, Hyper Sports and Daley Thompson’s Decathlon among them. A loop of a single pass is played once, though the format asks for more; the loops it forbids or leaves without a meaning are refused with the rest: one inside another, one never closed or closed without being opened, one asking for no passes at all.

What is not refused is an element of no length: a pulse of no T-states, or a run of none. Rippers leave those in the tails of blocks and distributed tapes carry them, so they are passed over in silence, as they sound.

What the machine writes to tape goes nowhere here, as on the CPC: bit 3 of a write to port &FE is the MIC socket, and past the little it lifts the speaker it goes no further. Nothing here records.

A tape can also stop before it ends: a multiload marks the point between its parts, and there the reel stops where a real one would have. Where the last pulse before a stop was low, the deck’s fall to low would swallow the edge that ends it, so the reader first holds the other level for a millisecond, as a pause begins: a loader timing edges needs that edge for its last bit, and some images of Speedlock 4 tapes run straight from a block into a stop. The end of the tape is met the same way. Playing it again carries on from the next block — but nothing here presses PLAY a second time, so from the command line a multiload loads its first part and waits.

A snapshot it keeps too: the 48K .sna, read and written. What that format has no room for is where the beam stood, so a machine picked up from one begins its frame again from the top — the same limitation the CPC’s snapshots have, for the same reason. Nor does it keep the two bits that set the speaker, so a machine picked up from one has its speaker at rest until the program next writes to the port.

One smaller declaration. The colours are chosen rather than measured: the shape of the answer is fixed by the hardware, one line per gun and one more for brightness, but no measurement of what those lines reach was found, so the levels are the ones the field has settled on and a real reading would replace them.

The firmware

The firmware images are Amstrad’s — the CPC’s are Locomotive Software’s as well — and they are fetched rather than committed — distributable with emulators under the permission Amstrad gave in 1999, which is the ground every emulator in this field stands on. The machine is built without them and cannot boot without them.