65816-llvm-mos/demos/spriteProbe.c
Scott Duensing da095402ec Updated
2026-06-02 23:17:57 -05:00

103 lines
3.9 KiB
C

// spriteProbe.c - Phase 4.2 sprite engine verification probe.
//
// Bare-metal SHR probe: brings up SHR 320 mode via iigsSpriteInit()
// (no startdesk(), no QD), places 8 16x16 sprites at known
// coordinates, renders them, then writes a sentinel byte at a chosen
// scratch DP address so the harness knows we got past the render
// pass.
//
// What we verify under runInMame.sh --check-u8:
//
// 1. SHR enabled marker. iigsSpriteInit() pokes $C029 = 0xC1.
// A subsequent readback through $00:C029 verifies the soft
// switch landed. (runInMame writes are gated against $C0xx,
// so the only way that byte reads back as 0xC1 is via our
// code's store. Bank 0 $C029 is the actual register.)
//
// 2. After the second render at y=36, scan line 20 (the FIRST
// position) is back to background. $E1:2000 + 20*160 = $E1:2C80
// should be 0x00 -- EraseAll restored the saved background.
//
// 3. Sprite 7's left edge is at byte offset 56 of scan line 36
// (final position), so $E1:2000 + 36*160 + 56 = $E1:3938 should
// be 0x77.
//
// 4. A byte BETWEEN sprite rows (scan line 100, offset 0) at
// $E1:2000 + 100*160 = $E1:5E80 should still be 0x00 (the
// framebuffer-clear value, untouched by any sprite).
//
// 5. Sentinel marker at $00:0070 = 0x99 confirming the program
// reached the end of main without halting.
#include <stdint.h>
#include "iigs/sprite.h"
// One 16x16 sprite tile, 4bpp packed: every nibble is 7 (white).
// 128 bytes total. Stored in .rodata so it sits in bank 0 text-or-
// rodata range (well below $A000) and is reachable as a plain
// pointer.
static const uint8_t kWhiteTile[128] = {
0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
};
int main(void) {
// 1. Bring up SHR 320 mode. Clears framebuffer to color 0
// (black), installs default palette, resets sprite list.
iigsSpriteInit();
// 2. Build the frame's sprite list: 8 copies of the white tile,
// laid out across one row at y=20, x stepping by 16.
iigsSpriteBegin();
for (uint16_t i = 0; i < 8; i++) {
IigsSpriteT s;
s.x = (uint16_t)(i * 16U);
s.y = 20;
s.pixels = kWhiteTile;
iigsSpriteAdd(&s);
}
// 3. Render: saves background under each sprite, then blits.
iigsSpriteRenderAll();
// 4. One frame of update. EraseAll restores the saved background
// (returning row 20 to all-zero), then we shift each sprite
// DOWN by 16 lines (y=20 -> y=36) and re-render. After this:
// - row 20 should be all-zero again (background restored).
// - row 36 should hold the eight sprites.
iigsSpriteEraseAll();
iigsSpriteBegin();
for (uint16_t i = 0; i < 8; i++) {
IigsSpriteT s;
s.x = (uint16_t)(i * 16U);
s.y = 36;
s.pixels = kWhiteTile;
iigsSpriteAdd(&s);
}
iigsSpriteRenderAll();
// 5. Drop the sentinel. $70 is in DP, well outside both libcall
// scratch ($E0..$F4) and IMG slots ($C0..$DE), so we don't
// collide with any runtime use.
*(volatile uint8_t *)0x70 = 0x99;
// Halt — bare metal has no OS to return to.
for (;;) {
}
}