mirror of
https://github.com/libretro/virtualjaguar-libretro.git
synced 2026-11-10 03:21:27 -10:00
The raw-binary loader scored candidate bases only by whether absolute JSR/JMP/LEA/MOVE targets fall inside [base, base+size). $5000 was not a candidate, and a $4000 window also contains almost every target of a $5000-linked image, so such images loaded $1000 low: Chroma-Luma (bin) ran its font data from its first JSR, the 68K hit ILLEGAL and parked at $1000 (#800's guard), and the screen stayed black in HLE and BIOS mode (RAM-loaded images never take the boot-ROM path, so both modes share the failure). Add $5000 and break overlapping-window ties on entry-point consistency: a JSR/JMP abs.L target that is the image start or the word after an RTS/RTE/RTR is a subroutine start under the right base and a coincidence under a wrong one. Among bases that clear the containment bar, the most entry hits wins; with no hits anywhere the containment winner stands, so images without the signal do not move. Across the 155-title cart corpus exactly six load decisions change, all $4000 -> $5000 and all from black/parked to running video: Chroma-Luma (bin), JagMania (Jul 8), JagMarble (2000), FORCE Design Legion Force intro, Joypad-TeamTap Tester [a1], and Music Demo (ScatoLOGIC), whose #794 blitter spray in BIOS mode was this misload. Music Demo is therefore no longer a hung-blitter reproducer; it moves to the new ROM-gated test_raw_binary_boot check next to the two #818 titles. Closes #818 Co-Authored-By: Claude Opus 5.5 <[email protected]>
203 lines
6.4 KiB
C
203 lines
6.4 KiB
C
/*
|
|
* test/tools/test_raw_binary_boot.c -- a headerless raw binary loads at its
|
|
* link base and runs its own code with video (issue #818).
|
|
*
|
|
* Chroma-Luma Color Pick (bin) and JagMania (Jul 8) are raw 68K binaries
|
|
* linked at $5000. The loader's containment scorer put them at $4000 (a
|
|
* $4000 window also contains their targets), so their first JSR landed in
|
|
* font data, the 68K ran away into ILLEGAL and parked at $1000 (#800's
|
|
* guard), and the screen stayed black. file.c now breaks the tie on
|
|
* entry-point consistency.
|
|
*
|
|
* For --rom PATH this asserts, under the shipped ACCURATE blitter:
|
|
* - the loader chose --expect-base as the run address;
|
|
* - after --frames frames the 68K PC is inside the loaded image (game
|
|
* code), not at the $1000 ILLEGAL park;
|
|
* - at least half the frames are lit (>1% non-black pixels).
|
|
* --bios is passed through. RAM-loaded images never run the boot ROM
|
|
* (jaguar.c only takes the BIOS path for a cartridge), but BIOS mode still
|
|
* changes reset state -- main and DSP RAM are PRNG-filled instead of
|
|
* zeroed -- so the suite checks both modes.
|
|
*
|
|
* ROM-gated: the Makefile records a SKIP when the ROM is absent.
|
|
*
|
|
* Build: cc -O2 -Wall -std=c99 -I. -I./src -I./libretro-common/include \
|
|
* -o test/tools/test_raw_binary_boot \
|
|
* test/tools/test_raw_binary_boot.c test/harness/harness.c -ldl -lm
|
|
* Run: test_raw_binary_boot <core> --rom PATH --expect-base ADDR
|
|
* [--frames N] [--bios]
|
|
*/
|
|
|
|
#define _DEFAULT_SOURCE 1
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include "../harness/harness.h"
|
|
|
|
#define PARK_PC 0x00001000u
|
|
#define M68K_REG_PC_INDEX 16 /* m68kinterface.h: D0-D7, A0-A7, PC */
|
|
/* Hang backstop only; the assertions are frame-based. Generous because the
|
|
* suite runs on loaded hosts. */
|
|
#define WALL_CLOCK_SECS 1200u
|
|
|
|
typedef unsigned int (*m68k_get_reg_fn)(void *, int);
|
|
|
|
typedef struct {
|
|
unsigned frames;
|
|
unsigned lit_frames;
|
|
} video_state;
|
|
|
|
static void video_cb(void *userdata, const void *data, unsigned width,
|
|
unsigned height, size_t pitch)
|
|
{
|
|
video_state *st = (video_state *)userdata;
|
|
const uint8_t *rows = (const uint8_t *)data;
|
|
unsigned x, y, lit = 0, total = 0;
|
|
|
|
if (!data || !width || !height)
|
|
return;
|
|
/* Every 4th pixel of every 4th row, as cart_boot_probe samples. */
|
|
for (y = 0; y < height; y += 4)
|
|
{
|
|
const uint32_t *px = (const uint32_t *)(rows + y * pitch);
|
|
for (x = 0; x < width; x += 4)
|
|
{
|
|
total++;
|
|
if (px[x] & 0x00FFFFFFu)
|
|
lit++;
|
|
}
|
|
}
|
|
st->frames++;
|
|
if (total && lit * 100u > total)
|
|
st->lit_frames++;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
static char a_prog[] = "test_raw_binary_boot";
|
|
static char a_frames_flag[] = "--frames";
|
|
static char a_option_flag[] = "--option";
|
|
static char a_option[] = "virtualjaguar_usefastblitter=disabled";
|
|
static char a_bios[] = "--bios";
|
|
static char a_quiet[] = "--quiet";
|
|
char *hargv[12];
|
|
int hargc = 0;
|
|
const char *core, *rom = NULL, *frames = "300";
|
|
unsigned long expect_base = 0;
|
|
int bios = 0, have_base = 0, fails = 0, i;
|
|
harness_config cfg = HARNESS_CONFIG_DEFAULT;
|
|
video_state vs;
|
|
m68k_get_reg_fn get_reg;
|
|
uint32_t *p_run, *p_size;
|
|
uint32_t base, size;
|
|
unsigned pc;
|
|
const char *label;
|
|
|
|
if (argc < 2)
|
|
{
|
|
fprintf(stderr, "usage: %s <core> --rom PATH --expect-base ADDR "
|
|
"[--frames N] [--bios]\n", argv[0]);
|
|
return 2;
|
|
}
|
|
core = argv[1];
|
|
for (i = 2; i < argc; i++)
|
|
{
|
|
if (!strcmp(argv[i], "--rom") && i + 1 < argc)
|
|
rom = argv[++i];
|
|
else if (!strcmp(argv[i], "--expect-base") && i + 1 < argc)
|
|
{
|
|
expect_base = strtoul(argv[++i], NULL, 0);
|
|
have_base = 1;
|
|
}
|
|
else if (!strcmp(argv[i], "--frames") && i + 1 < argc)
|
|
frames = argv[++i];
|
|
else if (!strcmp(argv[i], "--bios"))
|
|
bios = 1;
|
|
else
|
|
{
|
|
fprintf(stderr, "unknown argument: %s\n", argv[i]);
|
|
return 2;
|
|
}
|
|
}
|
|
if (!rom || !have_base)
|
|
{
|
|
fprintf(stderr, "--rom and --expect-base are required\n");
|
|
return 2;
|
|
}
|
|
label = bios ? "bios" : "hle";
|
|
|
|
alarm(WALL_CLOCK_SECS);
|
|
|
|
hargv[hargc++] = a_prog;
|
|
hargv[hargc++] = (char *)core;
|
|
hargv[hargc++] = (char *)rom;
|
|
hargv[hargc++] = a_frames_flag;
|
|
hargv[hargc++] = (char *)frames;
|
|
hargv[hargc++] = a_option_flag;
|
|
hargv[hargc++] = a_option;
|
|
if (bios)
|
|
hargv[hargc++] = a_bios;
|
|
hargv[hargc++] = a_quiet;
|
|
hargv[hargc] = NULL;
|
|
|
|
memset(&vs, 0, sizeof(vs));
|
|
if (!harness_init_from_args(&cfg, hargc, hargv))
|
|
{
|
|
fprintf(stderr, "FAIL [%s] %s: harness init\n", label, rom);
|
|
return 1;
|
|
}
|
|
cfg.video_callback = video_cb;
|
|
cfg.video_callback_data = &vs;
|
|
if (!harness_load_rom(&cfg))
|
|
{
|
|
fprintf(stderr, "FAIL [%s] %s: could not load\n", label, rom);
|
|
harness_shutdown(&cfg);
|
|
return 1;
|
|
}
|
|
|
|
get_reg = (m68k_get_reg_fn)harness_dlsym(&cfg, "m68k_get_reg");
|
|
p_run = (uint32_t *)harness_dlsym(&cfg, "jaguarRunAddress");
|
|
p_size = (uint32_t *)harness_dlsym(&cfg, "jaguarROMSize");
|
|
if (!get_reg || !p_run || !p_size)
|
|
{
|
|
fprintf(stderr, "FAIL [%s]: m68k_get_reg/jaguarRunAddress/jaguarROMSize "
|
|
"not exported (build the core with TEST_EXPORTS=1)\n", label);
|
|
harness_shutdown(&cfg);
|
|
return 1;
|
|
}
|
|
base = *p_run;
|
|
size = *p_size;
|
|
if (base != (uint32_t)expect_base)
|
|
{
|
|
fprintf(stderr, "FAIL [%s] %s: loaded at $%06X, want $%06lX\n",
|
|
label, rom, (unsigned)base, expect_base);
|
|
fails++;
|
|
}
|
|
|
|
harness_run(&cfg);
|
|
pc = get_reg(NULL, M68K_REG_PC_INDEX) & 0xFFFFFFu;
|
|
|
|
if (pc == PARK_PC || pc < base || pc >= base + size)
|
|
{
|
|
fprintf(stderr, "FAIL [%s] %s: 68K PC $%06X after %u frames is outside "
|
|
"the image [$%06X, $%06X)%s\n", label, rom, pc, vs.frames,
|
|
(unsigned)base, (unsigned)(base + size),
|
|
pc == PARK_PC ? " -- parked on ILLEGAL (runaway)" : "");
|
|
fails++;
|
|
}
|
|
if (vs.frames == 0 || vs.lit_frames * 2 < vs.frames)
|
|
{
|
|
fprintf(stderr, "FAIL [%s] %s: %u of %u frames lit, want at least half\n",
|
|
label, rom, vs.lit_frames, vs.frames);
|
|
fails++;
|
|
}
|
|
|
|
harness_shutdown(&cfg);
|
|
if (!fails)
|
|
printf("PASS [%s] %s: runs at $%06X, PC $%06X, %u/%u frames lit\n",
|
|
label, rom, (unsigned)base, pc, vs.lit_frames, vs.frames);
|
|
return fails ? 1 : 0;
|
|
}
|