feat(badapple): playback loop and vblank pacing

This commit is contained in:
arti committed 2026-11-01 10:10:52 +02:00
1 parent dbfc7b60c5
commit 150a6d86c0
4 files changed
+140 -22

No files matched your search

+1
View File
@@ -1,5 +1,6 @@
data/*
!data/manifest.json
data5/
preview/
player/build/
player/work/
+69 -9
View File
@@ -6,6 +6,11 @@
#define VRAM_BASE 0x04000000
#define ROW_BITS 2048
#define MARK 0x01810000
#define CTRL_START (MARK + 0x20 * 8)
#define CTRL_COUNT (MARK + 0x24 * 8)
#define CTRL_PACE (MARK + 0x28 * 8)
#define CTRL_MOVIE (MARK + 0x2C * 8)
#define CTRL_MOVIELEN (MARK + 0x30 * 8)
extern void fill_run_();
extern void fill_init_();
@@ -42,33 +47,88 @@ void *ctx;
fill_rect(x, y, w, h, color);
}
/* Poll the display interrupt pending bit. Interrupts are disabled; this is
* a status bit, not an ISR. A missing DPYINT only costs a bounded spin. */
static void wait_vsync()
{
unsigned int i;
unsigned short pending;
/* Clear a stale DI, then wait for the next one. DPYINT is programmed to
* line 1 once at startup, so DI appears at the top of every frame. */
*(unsigned short *)0xC0000120 = 0;
for (i = 0; i < 200000; i++) {
pending = *(unsigned short *)0xC0000120;
if (pending & 0x0400) {
return;
}
}
}
int main()
{
struct movie_header header;
unsigned int frame;
unsigned int last;
unsigned int start;
unsigned int count;
unsigned int pace;
unsigned int movie_base;
unsigned int movie_len;
int rc;
fill_init_();
/* Display interrupt at line 1: our only timebase. Interrupts stay off;
* wait_vsync() polls the pending bit. */
*(unsigned short *)0xC00000A0 = 1;
/* Start from white; the OS left its own screen behind. */
fill_rect(0, 0, 1280, 1024, 0);
rc = movie_open((unsigned char *)MOVIE_BASE, MOVIE_MAX, &header);
movie_base = *(unsigned int *)CTRL_MOVIE;
movie_len = *(unsigned int *)CTRL_MOVIELEN;
if (movie_base == 0) {
movie_base = MOVIE_BASE;
}
if (movie_len == 0) {
movie_len = MOVIE_MAX;
}
rc = movie_open((unsigned char *)movie_base, movie_len, &header);
if (rc != 0) {
*(unsigned int *)MARK = 0xBAD00001;
for (;;) {
}
}
rc = movie_decode_frame((unsigned char *)MOVIE_BASE, MOVIE_MAX, 0,
&header, paint, 0);
if (rc != 0) {
*(unsigned int *)MARK = 0xBAD00002;
for (;;) {
}
start = *(unsigned int *)CTRL_START;
count = *(unsigned int *)CTRL_COUNT;
pace = *(unsigned int *)CTRL_PACE;
last = header.frame_count;
if (last != 0) {
last = last - 1;
}
if (count != 0 && start + count - 1 < last) {
last = start + count - 1;
}
*(unsigned int *)MARK = 0; /* FRAME */
*(unsigned int *)(MARK + 0x10 * 8) = 1; /* STATUS: painted */
for (frame = 0; frame <= last; frame++) {
if (pace != 0 && frame >= start) {
wait_vsync();
}
rc = movie_decode_frame((unsigned char *)movie_base, movie_len, frame,
&header, paint, 0);
if (rc != 0) {
*(unsigned int *)MARK = 0xBAD00002;
*(unsigned int *)(MARK + 0x14 * 8) = frame;
for (;;) {
}
}
*(unsigned int *)MARK = frame;
}
*(unsigned int *)(MARK + 0x10 * 8) = (count != 0) ? 2 : 1;
for (;;) {
}
}
+37 -13
View File
@@ -28,11 +28,14 @@ if not dbg then
end
local ORIGIN = 0x018C0000
local MOVIE = 0x00080000
local MOVIE = tonumber(os.getenv("BA_MOVIE_BASE") or "0x01D00000")
local MARK = 0x01810000
local ENTRY_PC = 0x3F579970
local AT = tonumber(os.getenv("BA_AT") or "2500")
local STOP_AT = tonumber(os.getenv("BA_END") or "2600")
local START = tonumber(os.getenv("BA_START") or "0")
local COUNT = tonumber(os.getenv("BA_COUNT") or "0")
local PACE = tonumber(os.getenv("BA_PACE") or "0")
local MOVIE_PATH = os.getenv("BA_MOVIE") or "bad-apple/data/test_movie.bin"
local PAYLOAD_PATH = os.getenv("BA_PAYLOAD") or "bad-apple/player/build/player.bin"
local ENV_PATH = os.getenv("BA_ENV") or "bad-apple/player/build/player.env"
@@ -80,8 +83,20 @@ print(string.format("BA: payload %d bytes, movie %d bytes, entry %s",
local function write_bytes(data, base)
local i
for i = 0, #data - 1 do
local n = #data
i = 0
while i + 4 <= n do
local b0 = data:byte(i + 1)
local b1 = data:byte(i + 2)
local b2 = data:byte(i + 3)
local b3 = data:byte(i + 4)
local word = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24)
prog:write_u32(base + i * 8, word)
i = i + 4
end
while i < n do
prog:write_u8(base + i * 8, data:byte(i + 1))
i = i + 1
end
end
@@ -102,21 +117,28 @@ local frames = 0
local function load_and_enter()
write_bytes(payload, ORIGIN)
write_bytes(movie, MOVIE)
local ok, bad = verify(payload, ORIGIN)
if not ok then
print("BA ERROR: payload readback mismatch at byte " .. bad)
machine:exit()
return
end
prog:write_u32(MARK + 0x20 * 8, START)
prog:write_u32(MARK + 0x24 * 8, COUNT)
prog:write_u32(MARK + 0x28 * 8, PACE)
prog:write_u32(MARK + 0x2C * 8, MOVIE)
prog:write_u32(MARK + 0x30 * 8, #movie)
-- Arm the entry breakpoint before the large movie write; the write blocks
-- emulation, so it cannot fire until the movie is in place.
dbg:command("bpset " .. hex(ENTRY_PC) .. ",,{ pc = " .. hex(entry) .. " ; g }")
write_bytes(movie, MOVIE)
ok, bad = verify(movie, MOVIE)
if not ok then
print("BA ERROR: movie readback mismatch at byte " .. bad)
machine:exit()
return
end
print("BA: loaded; breakpoint at " .. hex(ENTRY_PC))
dbg:command("bpset " .. hex(ENTRY_PC) .. ",,{ pc = " .. hex(entry) .. " ; g }")
print(string.format("BA: loaded; start=%d count=%d pace=%d", START, COUNT, PACE))
end
emu.register_frame_done(function()
@@ -125,16 +147,18 @@ emu.register_frame_done(function()
loaded = true
load_and_enter()
end
if loaded and not entered and prog:read_u16(MARK) == 0x0000
and prog:read_u16(MARK + 0x10 * 8) == 0x0001 then
entered = true
done = true
print(string.format("BA: STATUS=painted at frame %d; FRAME=%d",
frames, prog:read_u16(MARK)))
if loaded and not entered then
local status = prog:read_u16(MARK + 0x10 * 8)
if status == 1 or status == 2 then
entered = true
done = true
print(string.format("BA: STATUS=%d at frame %d; FRAME=%d",
status, frames, prog:read_u16(MARK)))
end
end
if loaded and not done and frames > AT + 30 then
if loaded and not done and frames > AT + 10 then
local err = prog:read_u32(MARK)
if err ~= 0 then
if err >= 0xBAD00000 then
print(string.format("BA ERROR: marker %s at frame %d",
hex(err), frames))
machine:exit()
+33
View File
@@ -0,0 +1,33 @@
#!/bin/sh
# Run the payload in MAME with the debugger loader.
#
# tools/run_mame.sh one # one frame, no pacing
# tools/run_mame.sh play # full movie, paced
# BA_MOVIE=data5/movie.bin BA_START=180 BA_COUNT=1 tools/run_mame.sh play
set -eu
HERE=$(cd "$(dirname "$0")" && pwd)
ROOT=$(cd "$HERE/../.." && pwd)
M=${M:-/home/arti/code/k-space/mame0289/vt1200}
MODE=${1:-one}
BA_MOVIE=${BA_MOVIE:-$ROOT/bad-apple/data/test_movie.bin}
BA_PACE=${BA_PACE:-0}
BA_START=${BA_START:-0}
BA_COUNT=${BA_COUNT:-0}
BA_AT=${BA_AT:-2500}
BA_END=${BA_END:-3000}
BA_MOVIE_BASE=${BA_MOVIE_BASE:-0x01D00000}
if [ "$MODE" = "play" ]; then
BA_PACE=${BA_PACE:-1}
fi
cd "$ROOT"
VT1200_SKIP_LEVEL1=1 \
BA_MOVIE="$BA_MOVIE" BA_PACE="$BA_PACE" BA_START="$BA_START" \
BA_COUNT="$BA_COUNT" BA_AT="$BA_AT" BA_END="$BA_END" \
BA_MOVIE_BASE="$BA_MOVIE_BASE" \
"$M" vt1200 -rompath harness/mame/roms -video none -sound none -nothrottle \
-skip_gameinfo -debug -debugger none \
-autoboot_script "$ROOT/bad-apple/tools/load_mame.lua" -autoboot_delay 0