103 lines
3.9 KiB
C
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 (;;) {
|
|
}
|
|
}
|