During the picture, the ARM can only get a few cycles ahead of the 6507. Between two injections it has the time the 6507 takes to execute the previous one: after a write, one cycle; after a WSYNC, the rest of the line. If it is late, the 6507 reads the last byte it saw at that address, which in a repetitive kernel is sometimes the same instruction and "works by luck". Hence the rules: no calculations in the kernel (everything is precalculated in a cache that is only rebuilt if the room, the mode or the window change), divisions by 3 are multiplications, and every wait comes already calculated.
The kernel
Scheduling 23 writes in 76 cycles and leaving the rest for the overblank.
The ARM runs one cycle ahead
A line as a list of events
In STRIP+ and FULL each line is a list of events: which register is written, with what data and on which cycle the write ends. Each write has a valid window, calculated with times measured in Stella (a RESP that ends on cycle c leaves the object at x = 3c − 63; a graphic written on cycle c is visible from x ≈ 3c − 68). A scheduler places those with the narrowest window first and, if they do not fit, backtracks. Rules: between two events there are 0 free cycles or at least 2, and the jmp counts as one more write.
It is not scheduled on every line. In STRIP+ the table is solved once at start-up and the kernel replays it. In FULL, the pairs of checkerboard lines (one A and one B, for each field and kind of row) are solved by a tool outside the ROM, because on the ARM they took seconds. A FULL line carries the HMOVEs, three RESP0s (one on cycle 76 and two mid-line), eight GRPs, the AUDV0 sample and the jmp.
The room colours, the background and the players' ink, are not written on every line: they are set once before the room, without bus stuffing, and serve both fields. In the scoreboard they only change where each section starts. Field 1 used to rewrite them on every line with bus stuffing, and some PAL TVs showed colour stripes. Only the red box of the hour is still written per line.
76-cycle lines without WSYNC
The whole room is emitted in a single loop: the end-of-line wait is added to that of the first event of the next line, and the lines last exactly 76 cycles without WSYNC. A "priming" line turns off VBLANK with a write that ends on cycle 76, so that the first visible line starts right at cycle 0. That brought the gap between lines down from 93 to about 29 ARM instructions. Today the worst gap in the kernel is in the FULL checkerboard: 36 instructions in one 6507 cycle, about 43 MIPS. STRIP, STRIP+ and DOUBLE do wait with WSYNC between lines.
The painter's algorithm
The TIA does not draw William or the monks: the ARM composes them into the 128 × 160 bitmap, as the original does. The characters are sorted from back to front; before each one, the tiles not yet painted that are not closer than it are painted; at the end, those that are left, in front of everything. That is how William passes behind columns and arches. The engine reproduces the 94 Spectrum rooms (reduced) and 19 scenes with characters to the pixel.
The overblank, in four beats
Each overblank runs at most one heavy task. While someone is speaking, the logic keeps the original's pace: one tick every 6 frames at 50 Hz, one every 7.2 at 60. When nobody is speaking, the original runs without waiting, and so does the demake: each tick starts as soon as the previous one has been drawn, one every 6.9 frames on average at 60 Hz, so that the character the camera follows does not move in jumps. That is the nominal pace, the one given by the right difficulty switch in the position it is in when the console is switched on. In practice the Spectrum did not reach it: walking, it gives 6.5 ticks per second instead of the 8.3 of its 6-frame wait. So, in the other position, each tick lasts 28% longer: the slow pace remembered by those who played the original. When the switch is moved, the scoreboard says so for about two seconds, over the monks' sentences: "FAST PACE" or "SLOW PACE", and in STRIP, which has less room, "FAST" or "SLOW". Each tick is split into four parts: objects, doors and William; Adso; monks 2 and 3; monks 4 and 5 and the ending. A new room is built in pieces, interpreting the original's "block programs" (6 blocks per overblank; a room has 21 on average), and drawn in two strips. The new room appears about 6 frames after the change, and meanwhile the logic waits, as in the original.
| Task | Before | Now (ARM instructions) |
|---|---|---|
| Build a room | 260,000 in one overblank | 98,000 at most per overblank |
| Prepare the lines (FULL) | 85,000 | 42,000 |
| Synthesise the sound of one frame | 29,000 | 9,000 – 13,000 |
| Prepare the scoreboard | 13,000 | 2,900 on average |