Sprites: things that move without the screen moving
Everything drawn on this machine was in a cell. Something between two cells meant rewriting both; something moving a pixel at a time meant rewriting them sixty times a second, which is affordable for one thing and not for twenty. A sprite is put at a pixel and the device draws it over whatever is behind, so moving it costs two bytes. MADE OF TILES, which is the decision the rest follows from: m by n taken in reading order from one index, so there is no second pixel format, no second kind of memory, and nothing a sprite can show that the map cannot. A 16 by 16 character is four tiles and the background can name the same four. 256 entries of 8 bytes at 0xC000 in the atlas - eight so the entry address is a shift, the same no-multiply argument as the palette's four. Position is signed and sixteen bits, because 640 by 400 does not fit in a byte and a sprite has to be able to sit half off the left rather than appearing whole at the edge. A PIXEL OF ZERO IS NOT DRAWN, or every sprite is a rectangle. Tested before the attribute is added, so a hole belongs to the art and not to the colour scheme. The same rule the other way round is what "behind" means: drawn only where the background pixel was zero, so a thing walks behind a pillar and in front of the floor in one frame. All of them draw, every frame, so they cannot flicker. Real machines dropped them per scanline because they had a fixed number of shift registers; this has a loop. The limit is the size of the table, which is a constant rather than a property of what is on screen. And the system takes them down at exit. The sprite table sits in the gap the screen save walks around - to the end of the map, then the palette - and that is right, because nothing the shell draws is a sprite: there is nothing to give back, only something to take away. Otherwise a program that put a ball up and left would leave it over the prompt, in front of everything, with nothing able to type it away. Sprite.asm deliberately leaves its own, because a program that faulted could not have cleared it. Every check here was re-broken and failed: transparency, reading order, draw order, priority, and size. Size needed breaking twice - the first attempt did not compile, and a silent build failure had left the old binary passing. 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
eee95ef0ce
commit
a916103a7f
@@ -89,6 +89,81 @@
|
||||
#define VIDEO_BITMAP_WIDTH 320
|
||||
#define VIDEO_BITMAP_HEIGHT 200
|
||||
|
||||
// ---- Sprites ----
|
||||
//
|
||||
// Things that move without the map moving. A map cell is where it is, and a program that
|
||||
// wanted something between two cells had to redraw both of them; a sprite is put at a PIXEL
|
||||
// and the device draws it over whatever is behind.
|
||||
//
|
||||
// MADE OF TILES, which is the decision the rest follows from. A sprite is m by n tiles taken
|
||||
// in reading order from one index, so it needs no pixel format of its own, no second kind of
|
||||
// memory, and no way for its art to be anything the map could not also show. A 16 by 16
|
||||
// character is four tiles and a program that wants it in the background too just names the
|
||||
// same four.
|
||||
//
|
||||
// The table is 256 entries of 8 bytes. Eight so that entry n begins at n times eight, which
|
||||
// is a shift - the same no-multiply argument that makes a palette entry four bytes and a map
|
||||
// row a page. It sits above the tiles with the whole of 0x4000 to 0xBFFF still clear beneath
|
||||
// it, which is two more 16K pages of tiles if they are ever wanted.
|
||||
#define VIDEO_SPRITE_BASE 0xC000
|
||||
#define VIDEO_SPRITE_COUNT 256
|
||||
#define VIDEO_SPRITE_BYTES 8
|
||||
|
||||
// Byte 0 is the top left tile, byte 1 the attribute, which means what a map cell's attribute
|
||||
// means: its low nibble times sixteen is added to every index in the art.
|
||||
#define VIDEO_SPRITE_TILE 0
|
||||
#define VIDEO_SPRITE_ATTRIBUTE 1
|
||||
|
||||
// Bytes 2 to 5, low byte first, and SIGNED - a screen is 640 by 400 in the larger mode, so
|
||||
// neither axis fits in a byte, and a sprite has to be able to sit half off the left or the
|
||||
// top rather than appearing whole at the edge.
|
||||
#define VIDEO_SPRITE_X 2
|
||||
#define VIDEO_SPRITE_Y 4
|
||||
|
||||
// Byte 6: how many tiles across in the high nibble, how many down in the low. Fifteen each
|
||||
// way, so 120 by 120 pixels.
|
||||
//
|
||||
// A SPRITE OF NO WIDTH OR NO HEIGHT DRAWS NOTHING, and that is the off switch. It saves a
|
||||
// flag, it is per sprite rather than a global the whole table shares, and it means the table
|
||||
// is already off when the machine starts, since the atlas wakes up cleared.
|
||||
//
|
||||
// Deliberately the OPPOSITE of what a length of zero means to the memory controller, where
|
||||
// it means the whole 64K. The reason is the same both times: moving no bytes is a useless
|
||||
// thing to ask for, so zero was free to mean something else there - and drawing no sprite is
|
||||
// the commonest state in this table, so zero has to mean nothing here.
|
||||
#define VIDEO_SPRITE_SIZE 6
|
||||
|
||||
// Byte 7.
|
||||
#define VIDEO_SPRITE_FLAGS 7
|
||||
#define VIDEO_SPRITE_HFLIP 0x01
|
||||
#define VIDEO_SPRITE_VFLIP 0x02
|
||||
// Drawn only where the background had nothing, so a thing can walk behind a pillar. See
|
||||
// below for what "nothing" means.
|
||||
#define VIDEO_SPRITE_BEHIND 0x04
|
||||
|
||||
// ---- What a sprite does not cover ----
|
||||
//
|
||||
// A PIXEL OF ZERO IS NOT DRAWN. Without that every sprite is a rectangle, and there is no
|
||||
// other candidate: the font already uses index 0 for paper, so it is the value art in this
|
||||
// machine has always left empty.
|
||||
//
|
||||
// Tested BEFORE the attribute is added, so it is a property of the art and not of the colour
|
||||
// scheme it is being shown in. A sprite drawn in indices 1 to 15 is transparent in the same
|
||||
// places in all sixteen schemes, which is the whole point of the additive nibble.
|
||||
//
|
||||
// The same rule read the other way is what "behind" means: a sprite marked behind draws only
|
||||
// where the BACKGROUND pixel was zero. One rule, applied to whichever layer is in front.
|
||||
//
|
||||
// ---- How many at once ----
|
||||
//
|
||||
// All of them. Every entry in the table is drawn every frame, so sprites cannot flicker.
|
||||
// Real machines dropped them per scanline because they had a fixed number of shift registers
|
||||
// and a fixed time to fill them; this has a loop. The limit is how many entries there are,
|
||||
// which is a constant a program can count on rather than a property of what it happens to be
|
||||
// drawing this frame.
|
||||
//
|
||||
// Where they overlap, THE LOWER NUMBER IS IN FRONT.
|
||||
|
||||
// ---- The palette ----
|
||||
//
|
||||
// Four bytes an entry rather than three, for the same reason a map row is a page: entry n
|
||||
|
||||
Reference in New Issue
Block a user