Four pages of tiles, in bits that were already there
A tile number is a byte and a byte reaches 256, which is not many once a font has taken 135 of them and a game wants a character, a background and a wall. Bits 4 and 5 of the attribute now say which page of 256 the number is in - bits already written on every cell and every sprite, and reserved for this since the attribute was defined. Four pages of 16K is 64K, which is the whole atlas, so THE FOURTH PAGE IS THE MEMORY THE SPRITE TABLE AND THE PALETTE ARE IN. That is not a hole in the design; it is the answer shared video memory has always given, and it is checked rather than forbidden. The atlas is 1024 tiles, and what a program spends on sprites and colours comes out of them: no sprites means page 3 is art, and sprites means 768 tiles and a reason. The page is a property of the CELL and not a mode, so one screen shows tiles from all four at once and nothing has to decide which page it is in. Both places a tile is drawn from now ask one function where the art is. They would otherwise drift: the sprite pass was written days after the map pass and neither is where the other is looked at. Nothing in CosmOS changes. The shell draws from page 0, which the screen save covers; a tile left in another page is invisible unless a map cell names that page, and the map is given back or cleared. Both breaks were tried and both failed the checks - and the second had to be tried twice, because the constant it needed lives in video.h and the harness was only editing video.c. That is the same silent no-op as yesterday's uncompiled break, in a different disguise. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
co-authored by
Claude Opus 5
parent
a916103a7f
commit
9eed23120f
+17
-4
@@ -29,6 +29,18 @@ static uint8_t videoScreen[VIDEO_SCREEN_COUNT][VIDEO_MEMORY_BYTES];
|
||||
// frame, exactly like the pixel buffer beside it.
|
||||
static uint8_t backgroundEmpty[VIDEO_MAX_WIDTH * VIDEO_MAX_HEIGHT];
|
||||
|
||||
// ---- Where a tile's art is ----
|
||||
//
|
||||
// The scheme nibble and the page bits live in the same byte and are asked of it in the same
|
||||
// breath, in the two places a tile is drawn from: a map cell and a sprite. One function, so
|
||||
// that the two cannot drift apart - which they would, because the sprite pass was written
|
||||
// three days after the map pass and neither is where the other is looked at.
|
||||
static const uint8_t *tileArt(uint8_t attribute, uint8_t tile) {
|
||||
const int page = (attribute & VIDEO_ATTRIBUTE_PAGE) >> VIDEO_ATTRIBUTE_SHIFT;
|
||||
return videoAtlas + VIDEO_TILE_BASE + page * VIDEO_TILE_PAGE_BYTES
|
||||
+ tile * VIDEO_TILE_BYTES;
|
||||
}
|
||||
|
||||
// Which screen is being shown. The console draws into THIS one rather than into a screen of
|
||||
// its own, so text goes where whoever is looking is looking - which matters most when the
|
||||
// text is a fault message printed over a game that had flipped.
|
||||
@@ -377,7 +389,8 @@ static void drawSprites(uint8_t *pixels, int width, int height) {
|
||||
| (entry[VIDEO_SPRITE_X + 1] << 8));
|
||||
const int top = (int16_t)(uint16_t)(entry[VIDEO_SPRITE_Y]
|
||||
| (entry[VIDEO_SPRITE_Y + 1] << 8));
|
||||
const uint8_t bank = (uint8_t)((entry[VIDEO_SPRITE_ATTRIBUTE] & 0x0F) << 4);
|
||||
const uint8_t bank =
|
||||
(uint8_t)((entry[VIDEO_SPRITE_ATTRIBUTE] & VIDEO_ATTRIBUTE_SCHEME) << 4);
|
||||
const uint8_t flags = entry[VIDEO_SPRITE_FLAGS];
|
||||
const int mirrored = (flags & VIDEO_SPRITE_HFLIP) != 0;
|
||||
const int inverted = (flags & VIDEO_SPRITE_VFLIP) != 0;
|
||||
@@ -396,7 +409,7 @@ static void drawSprites(uint8_t *pixels, int width, int height) {
|
||||
// is a byte and the tile number is one.
|
||||
const uint8_t tile = (uint8_t)(entry[VIDEO_SPRITE_TILE]
|
||||
+ readDown * wide + readAcross);
|
||||
const uint8_t *art = videoAtlas + VIDEO_TILE_BASE + tile * VIDEO_TILE_BYTES;
|
||||
const uint8_t *art = tileArt(entry[VIDEO_SPRITE_ATTRIBUTE], tile);
|
||||
|
||||
for (int y = 0; y < VIDEO_CELL_PIXELS; y++) {
|
||||
const int atY = top + downTile * VIDEO_CELL_PIXELS + y;
|
||||
@@ -502,8 +515,8 @@ void videoRender(void) {
|
||||
// of sixteen colour schemes without a second copy of it in tile memory, and a
|
||||
// tile that wants all 256 colours simply leaves the nibble at zero and gets
|
||||
// them. One adder in hardware, and neither use costs the other anything.
|
||||
const uint8_t bank = (uint8_t)((attribute & 0x0F) << 4);
|
||||
const uint8_t *art = videoAtlas + VIDEO_TILE_BASE + tile * VIDEO_TILE_BYTES;
|
||||
const uint8_t bank = (uint8_t)((attribute & VIDEO_ATTRIBUTE_SCHEME) << 4);
|
||||
const uint8_t *art = tileArt(attribute, tile);
|
||||
for (int y = 0; y < VIDEO_CELL_PIXELS; y++) {
|
||||
// Where this row of the cell lands once the view has been slid up by the
|
||||
// fine offset. Negative means it is the part of the top cell that is off
|
||||
|
||||
Reference in New Issue
Block a user