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
|
||||
|
||||
+30
-2
@@ -42,12 +42,30 @@
|
||||
|
||||
// ---- In the ATLAS bank ----
|
||||
//
|
||||
// Tile memory: 256 tiles of 8x8, one byte a pixel. Everything above it is free, and is
|
||||
// where more tiles and the sprite table are going.
|
||||
// Tile memory: 256 tiles of 8x8, one byte a pixel.
|
||||
#define VIDEO_TILE_BASE 0x0000
|
||||
#define VIDEO_TILE_BYTES 64
|
||||
#define VIDEO_TILE_COUNT 256
|
||||
|
||||
// ---- Four pages of them ----
|
||||
//
|
||||
// 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. So two bits of the
|
||||
// ATTRIBUTE say which page of 256 the number is in - bits that were already there, already
|
||||
// written on every cell and every sprite, and reserved from the day the attribute was
|
||||
// defined for exactly this.
|
||||
//
|
||||
// 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 same
|
||||
// answer shared video memory has always given: the atlas is 1024 tiles, and what a program
|
||||
// spends on sprites and colours comes out of them. A program that wants no sprites may have
|
||||
// page three for art, and one that wants sprites has 768 tiles and knows why.
|
||||
//
|
||||
// The page is per CELL and per SPRITE rather than a mode, so a screen can show tiles from
|
||||
// all four at once and a program never has to decide which page it is "in".
|
||||
#define VIDEO_TILE_PAGE_BYTES (VIDEO_TILE_COUNT * VIDEO_TILE_BYTES)
|
||||
#define VIDEO_TILE_PAGES 4
|
||||
|
||||
// ---- The map, one page a row ----
|
||||
//
|
||||
// A row is padded to exactly 256 bytes whether the mode uses all of it or not, and that is
|
||||
@@ -71,6 +89,16 @@
|
||||
// Two bytes to a cell: which tile, and how to colour it.
|
||||
#define VIDEO_CELL_BYTES 2
|
||||
|
||||
// ---- What the attribute byte means, in both places it appears ----
|
||||
//
|
||||
// A map cell's second byte and a sprite's byte 1 are the same thing and are read the same
|
||||
// way, which is what lets the same art be a background in one place and a moving thing in
|
||||
// another with nothing rewritten.
|
||||
#define VIDEO_ATTRIBUTE_SCHEME 0x0F
|
||||
#define VIDEO_ATTRIBUTE_PAGE 0x30
|
||||
#define VIDEO_ATTRIBUTE_SHIFT 4
|
||||
// Bits 6 and 7 are still reserved and should be left at nought.
|
||||
|
||||
// ---- A bitmap, over the top of the tiles and the map ----
|
||||
//
|
||||
// THE SAME MEMORY MEANING DIFFERENT THINGS IN DIFFERENT MODES, which is what shared video
|
||||
|
||||
Reference in New Issue
Block a user