// 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 #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 (;;) { } }