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
37 lines
1.5 KiB
Python
37 lines
1.5 KiB
Python
# Two questions video.sh cannot ask of a picture that moves.
|
|
#
|
|
# Grid scrolls a pixel a frame, so nothing can be asked about a particular pixel: whether
|
|
# pixel 4 is a grid line depends on which frame this is. Both questions below are true of the
|
|
# picture whatever the fine scroll offset happens to be.
|
|
#
|
|
# grid - is this a grid of one repeated tile? Periodic every eight pixels across, and
|
|
# not all one colour.
|
|
# bands - does the attribute nibble recolour each cell row? The colours found in one band
|
|
# of eight rows differ from those in the next, whatever the ground is doing.
|
|
import sys
|
|
|
|
data = open(sys.argv[1], "rb").read()
|
|
fields = data.split(b"\n", 3)
|
|
width = int(fields[1].split()[0])
|
|
body = fields[3]
|
|
|
|
|
|
def pixel(x, y):
|
|
at = (y * width + x) * 3
|
|
return tuple(body[at:at + 3])
|
|
|
|
|
|
if sys.argv[2] == "grid":
|
|
row = [pixel(x, 4) for x in range(32)]
|
|
periodic = all(row[x] == row[x + 8] for x in range(24))
|
|
varied = len(set(row)) > 1
|
|
print("yes" if periodic and varied else
|
|
"not periodic" if not periodic else "all one colour")
|
|
else:
|
|
# A band is eight rows, which is one cell row wherever the boundary has slid to. The
|
|
# ground is the same under every scheme, so what tells them apart is the line colour -
|
|
# and looking at the whole band finds it without knowing where in the band it is.
|
|
first = set(pixel(0, y) for y in range(8))
|
|
second = set(pixel(0, y) for y in range(8, 16))
|
|
print("yes" if first != second else "the same in both: %s" % sorted(first))
|