Scroll the screen sideways, and by less than a cell

The screen could move one way, a cell at a time. Three registers were
missing and this adds them: a column origin so the map can be wider than
the screen as well as taller, and a pixel remainder for each axis so the
step can be one pixel rather than eight.

  0x36  Scroll column, in cells, wrapping at 128
  0x37  Fine X, 0 to 7 pixels
  0x38  Fine Y, 0 to 7 pixels

FINE DOES NOT CARRY INTO COARSE. Writing 8 to a fine register writes 0,
because only its low three bits mean anything. The alternative was for a
write of 8 to step the coarse register, and it was rejected for one reason:
a program that scrolls has to know where it has got to, and if the hardware
carries then the only way to find out is to read the register back. Keeping
them apart means the program already knows, because it did the arithmetic
itself. It is also what the machines this one is pretending to be did.

The renderer now draws one more row and one more column than fit and clips
them, because with a fine offset the screen no longer begins on a cell
boundary and the cells at two edges are partly off it.

videoPutCell follows the column origin as it has always followed the row -
a caller means a cell of the SCREEN, and the screen is a window onto the
map. The fine offsets are deliberately not applied there: they move the
finished picture by less than a cell, and there is no such thing as less
than a cell to write into. So a program may scroll to any pixel without the
console's idea of where row three, column five is moving underneath it.

Grid now scrolls diagonally, a pixel a frame, in four port writes and two
carries. It moved eight pixels every fourth frame before, which reads as
the picture jumping rather than travelling.

Seven checks, each one the same program with one register changed, so what
is compared is where the picture stopped. Breaking fine X, fine Y, the
column origin, the three-bit mask, or the console's use of the origin each
fails exactly one of them.

Grid's own two checks had to be rewritten, and the reason is worth keeping:
they asked whether pixel 4 was a grid line, which was really a check that
the scroll happened to be at a cell boundary. A picture that moves a pixel
a frame can only be asked things that are true at every offset - that it
repeats every eight pixels, and that one band of eight rows holds different
colours from the next.

Also repairs docs.sh, which found the minimal CosmOS application by taking
the first asm block in the README. Documenting a program with an example
above it made that a different block, and the check complained that the
minimal application had no #Base about something that never claimed to be
one. It looks under System Services now.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
Anachronaut
2026-08-30 18:52:15 -04:00
co-authored by Claude Opus 5
parent 848103f5e4
commit bcd42e75ca
10 changed files with 329 additions and 61 deletions
+24 -5
View File
@@ -531,7 +531,7 @@ from every assembly file in it. Several are old programs written for the bare ma
| Settle | Says how the last start went and tells the machine to stop falling back, in 353 bytes. A program rather than a shell word, because the shell is for what cannot be done without it. |
| Files | Writes a file, reads it back, renames it and deletes it, in 675 bytes, including nothing but the service names. It is what says a program does not need a filesystem inside it. |
| Break | Stops itself twice with SWI osBreak, so that the registers can be seen changing between one stop and the next. |
| Grid | The first program to use the screen as a screen. Redefines a tile above the font, fills all 128 map rows, and scrolls by writing one byte a frame. |
| Grid | The first program to use the screen as a screen. Redefines a tile above the font, fills all 128 map rows, and scrolls it diagonally a pixel at a time. |
| Edit | A line editor. |
| Stream | Reads an 84,000 byte file through a buffer of 256, which is what says a file bigger than Data Memory can be read at all. |
| Type | Prints a named text file a block at a time, including one too large to fit in Data Memory. |
@@ -685,10 +685,29 @@ use when the thing being debugged is the boot chain, since it skips the boot cha
Everything else drawn on this machine has been text or a bitmap. `Grid` is the first program
to use the tile engine as an engine, and it is worth reading for the size of the numbers.
It writes **one byte a frame** to scroll. The map is 128 rows and the screen shows 25, so the
rows above and below are already drawn - scrolling moves the origin rather than 2,000 bytes of
screen, and the rows that leave the top have not gone anywhere. A screenful of movement costs
one `OUTA`.
It scrolls **a pixel a frame, diagonally**, and the whole of that is four port writes and two
carries. The map is 128 rows and 128 columns against a screen of 50 and 80, so the cells
around the edge are already drawn - scrolling moves the origin rather than 2,000 bytes of
screen, and what leaves the top has not gone anywhere.
The coarse registers move a whole cell and the fine ones move the remainder, and **they do not
carry into each other**, so the program does:
```asm
SETD.0 FineDown
LDA.0
INCA
INIB 0x07
AND
STQ.0
OUTQ 0x38
BNQ stepAcross ; Still inside the cell.
; ... and here, one step of the row origin.
```
The `AND` is both the wrap and the test: `Q` coming out as nought is exactly the moment the
cell boundary was crossed. It moved eight pixels every fourth frame before the fine registers
existed, which reads as the picture jumping rather than travelling.
It puts its tile at **200**, because the machine wakes with the font in tile memory - glyph n
at tile n, for 135 of the 256 - so a program that starts at zero paints over the alphabet and