Skip to content

Commit 4f8a734

Browse files
committed
CD boot improvements, BIOS strategy refactor, and expanded test suite
Refactor CD boot into pluggable strategy vtable (HLE, real BIOS, cart hybrid) with polymorphic dispatch. Fix real-BIOS CD boot for Dragon's Lair, Iron Soldier 2, and Baldies. Improve HLE sentinel scanning with LBA redirect for session-2 games and relaxed self-loop detection. Build: guard HAVE_NEON for osx/Intel, fix test_blitter_simd rule to use auto-detected SIMD source, add jagcd_bios.c/jagcd_cart.c to Makefile.common, update .gitignore for test artifacts. Tests: add harnesses for audio DAC, blitter, BUTCH CD, boot config, GPU control flow/ctrl/IRQ, memory map, timers, and video modes. Relax HLE boot test criteria for post-boot polling loops. Made-with: Cursor
1 parent aa11efc commit 4f8a734

32 files changed

Lines changed: 6110 additions & 468 deletions

Makefile

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,11 @@ else ifeq ($(platform), osx)
9797
SHARED := -dynamiclib
9898
CFLAGS += -Ofast
9999
CXXFLAGS += $(CFLAGS)
100-
HAVE_NEON = 1
100+
ifneq ($(arch),intel)
101+
ifneq ($(arch),ppc)
102+
HAVE_NEON = 1
103+
endif
104+
endif
101105
ifeq ($(arch),ppc)
102106
FLAGS += -DMSB_FIRST
103107
OLD_GCC = 1
@@ -653,8 +657,15 @@ test/test_cd_hle_boot: test/test_cd_hle_boot.c test/test_framework.h test/cd_ass
653657
test/test_cd_bios_boot: test/test_cd_bios_boot.c test/test_framework.h test/cd_assertions.h $(TARGET)
654658
$(TEST_CC) $(TEST_CFLAGS) -o $@ $< $(TEST_LDFLAGS)
655659

656-
test/test_blitter_simd: test/test_blitter_simd.c src/blitter_simd.h $(TARGET)
657-
$(TEST_CC) -O2 -o $@ test/test_blitter_simd.c src/blitter_simd_neon.c
660+
BLITTER_SIMD_TEST_FLAGS :=
661+
ifeq ($(BLITTER_SIMD_SRC),$(CORE_DIR)/src/blitter_simd_sse2.c)
662+
ifneq (,$(filter i686 i386 x86 win32,$(ARCH) $(platform)))
663+
BLITTER_SIMD_TEST_FLAGS += -msse2
664+
endif
665+
endif
666+
667+
test/test_blitter_simd: test/test_blitter_simd.c src/blitter_simd.h $(BLITTER_SIMD_SRC) $(TARGET)
668+
$(TEST_CC) -O2 $(BLITTER_SIMD_TEST_FLAGS) -I src -o $@ test/test_blitter_simd.c $(BLITTER_SIMD_SRC)
658669

659670
test-build: $(TEST_BINS)
660671

Makefile.common

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ SOURCES_C := \
4747
$(CORE_DIR)/src/vjag_memory.c \
4848
$(CORE_DIR)/src/universalhdr.c \
4949
$(CORE_DIR)/src/wavetable.c \
50-
$(CORE_DIR)/src/jagcd_hle.c
50+
$(CORE_DIR)/src/jagcd_hle.c \
51+
$(CORE_DIR)/src/jagcd_bios.c \
52+
$(CORE_DIR)/src/jagcd_cart.c
5153

5254
# SIMD-accelerated blitter operations: select arch-specific implementation.
5355
# BLITTER_SIMD may be set explicitly to one of: scalar, sse2, neon.

libretro.c

Lines changed: 74 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ int64_t rfread(void* buffer, size_t elem_size, size_t elem_count, RFILE* stream)
2323
#include "jagdevcdbios.h"
2424
#include "jaguar.h"
2525
#include "cdintf.h"
26+
#include "jagcd_boot.h"
2627
#include "jagcd_hle.h"
2728
#include "dac.h"
2829
#include "dsp.h"
@@ -75,8 +76,8 @@ static bool libretro_supports_bitmasks = false;
7576
static bool save_data_needs_unpack = false;
7677
static bool jaguar_cd_mode = false;
7778
static char cd_image_path[4096] = {0};
78-
static bool cd_bios_loaded_externally = false;
79-
static uint8_t external_cd_bios[0x40000]; /* 256 KB */
79+
bool cd_bios_loaded_externally = false;
80+
uint8_t external_cd_bios[0x40000]; /* 256 KB */
8081

8182
void retro_set_video_refresh(retro_video_refresh_t cb) { video_cb = cb; }
8283
void retro_set_audio_sample(retro_audio_sample_t cb) { (void)cb; }
@@ -973,10 +974,47 @@ void retro_cheat_set(unsigned index, bool enabled, const char *code)
973974

974975
/* Try to load a CD BIOS from the system directory.
975976
* Looks for several common filenames. Returns true if loaded. */
977+
static bool try_load_cd_bios_file(const char *path)
978+
{
979+
RFILE *f = rfopen(path, "rb");
980+
if (!f)
981+
return false;
982+
983+
rfseek(f, 0, SEEK_END);
984+
int64_t size = rftell(f);
985+
rfseek(f, 0, SEEK_SET);
986+
987+
if (size != 0x40000)
988+
{
989+
LOG_DBG("[CD-BIOS] wrong size (%lld, need 262144): %s\n",
990+
(long long)size, path);
991+
rfclose(f);
992+
return false;
993+
}
994+
995+
if (rfread(external_cd_bios, 1, 0x40000, f) != 0x40000)
996+
{
997+
rfclose(f);
998+
return false;
999+
}
1000+
rfclose(f);
1001+
1002+
uint32_t run_addr = (external_cd_bios[0x404] << 24) | (external_cd_bios[0x405] << 16)
1003+
| (external_cd_bios[0x406] << 8) | external_cd_bios[0x407];
1004+
if (run_addr < 0x800000 || run_addr > 0x840000)
1005+
{
1006+
LOG_DBG("[CD-BIOS] bad run addr $%08X: %s\n", run_addr, path);
1007+
return false;
1008+
}
1009+
1010+
LOG_INF("[CD-BIOS] Loaded CD BIOS: %s (run=$%06X)\n", path, run_addr);
1011+
cd_bios_loaded_externally = true;
1012+
return true;
1013+
}
1014+
9761015
static bool load_external_cd_bios(void)
9771016
{
9781017
const char *system_dir = NULL;
979-
/* Common filenames for the Jaguar CD BIOS (256 KB) */
9801018
static const char *bios_names[] = {
9811019
"jaguarcd_bios.bin",
9821020
"jagcd_bios.bin",
@@ -986,51 +1024,42 @@ static bool load_external_cd_bios(void)
9861024
"[BIOS] Atari Jaguar Developer CD (World).j64",
9871025
NULL
9881026
};
1027+
/* Sub-directories commonly used by Provenance, RetroArch, etc. */
1028+
static const char *sub_dirs[] = {
1029+
"",
1030+
"Atari - Jaguar",
1031+
"Atari - Jaguar CD",
1032+
"jaguar",
1033+
"jaguarcd",
1034+
NULL
1035+
};
9891036

9901037
if (!environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &system_dir) || !system_dir)
991-
return false;
992-
993-
for (int i = 0; bios_names[i]; i++)
9941038
{
995-
char path[4096];
996-
RFILE *f;
997-
998-
snprintf(path, sizeof(path), "%s/%s", system_dir, bios_names[i]);
999-
f = rfopen(path, "rb");
1000-
if (!f)
1001-
continue;
1039+
LOG_WRN("[CD-BIOS] No system directory available\n");
1040+
return false;
1041+
}
10021042

1003-
rfseek(f, 0, SEEK_END);
1004-
int64_t size = rftell(f);
1005-
rfseek(f, 0, SEEK_SET);
1043+
LOG_INF("[CD-BIOS] Searching for CD BIOS in: %s\n", system_dir);
10061044

1007-
if (size != 0x40000) /* Must be exactly 256 KB */
1045+
for (int s = 0; sub_dirs[s]; s++)
1046+
{
1047+
for (int i = 0; bios_names[i]; i++)
10081048
{
1009-
rfclose(f);
1010-
continue;
1011-
}
1049+
char path[4096];
10121050

1013-
if (rfread(external_cd_bios, 1, 0x40000, f) != 0x40000)
1014-
{
1015-
rfclose(f);
1016-
continue;
1017-
}
1018-
rfclose(f);
1051+
if (sub_dirs[s][0])
1052+
snprintf(path, sizeof(path), "%s/%s/%s", system_dir, sub_dirs[s], bios_names[i]);
1053+
else
1054+
snprintf(path, sizeof(path), "%s/%s", system_dir, bios_names[i]);
10191055

1020-
/* Validate: the CD BIOS is loaded as a "cartridge" at $800000.
1021-
* The Jaguar universal header at offset $404 contains the run address.
1022-
* For the retail CD BIOS this is $802000. */
1023-
{
1024-
uint32_t run_addr = (external_cd_bios[0x404] << 24) | (external_cd_bios[0x405] << 16)
1025-
| (external_cd_bios[0x406] << 8) | external_cd_bios[0x407];
1026-
if (run_addr >= 0x800000 && run_addr <= 0x840000)
1027-
{
1028-
cd_bios_loaded_externally = true;
1056+
if (try_load_cd_bios_file(path))
10291057
return true;
1030-
}
10311058
}
10321059
}
10331060

1061+
LOG_WRN("[CD-BIOS] CD BIOS not found in %s (searched %d names x %d directories)\n",
1062+
system_dir, 6, 5);
10341063
return false;
10351064
}
10361065

@@ -1109,10 +1138,8 @@ bool retro_load_game(const struct retro_game_info *info)
11091138
game_width = 320;
11101139
game_height = 240;
11111140

1112-
// Emulate BIOS
11131141
vjs.hardwareTypeNTSC = true;
11141142
vjs.useJaguarBIOS = false;
1115-
vjs.useCDBIOS = false;
11161143
vjs.cdBiosType = CDBIOS_RETAIL;
11171144

11181145
check_variables();
@@ -1123,6 +1150,7 @@ bool retro_load_game(const struct retro_game_info *info)
11231150
/* Detect CD content */
11241151
jaguar_cd_mode = false;
11251152
cd_image_path[0] = '\0';
1153+
cd_bios_loaded_externally = false;
11261154

11271155
if (info->path && (has_extension(info->path, "cue")
11281156
|| has_extension(info->path, "cdi")
@@ -1132,26 +1160,15 @@ bool retro_load_game(const struct retro_game_info *info)
11321160
strncpy(cd_image_path, info->path, sizeof(cd_image_path) - 1);
11331161
cd_image_path[sizeof(cd_image_path) - 1] = '\0';
11341162

1135-
vjs.useJaguarBIOS = true;
1136-
vjs.useCDBIOS = true;
1163+
if (vjs.cdBootMode != CDBOOT_HLE)
1164+
load_external_cd_bios();
1165+
}
11371166

1138-
cd_bios_loaded_externally = false;
1167+
/* Resolve boot configuration — single source of truth */
1168+
ResolveBootConfig(&bootConfig, jaguar_cd_mode, cd_bios_loaded_externally,
1169+
vjs.cdBootMode, vjs.useJaguarBIOS);
11391170

1140-
if (vjs.cdBootMode == CDBOOT_HLE)
1141-
{
1142-
LOG_INF("[CD] Boot mode: HLE (skipping BIOS search)\n");
1143-
}
1144-
else
1145-
{
1146-
if (!load_external_cd_bios())
1147-
{
1148-
if (vjs.cdBootMode == CDBOOT_BIOS)
1149-
LOG_WRN("[CD] WARNING: Boot mode is BIOS but no external BIOS found\n");
1150-
else
1151-
LOG_WRN("[CD] No external BIOS found — will use HLE boot path\n");
1152-
}
1153-
}
1154-
}
1171+
vjs.useJaguarBIOS = bootConfig.showBootROM;
11551172

11561173
/* For CD mode, open the disc image BEFORE JaguarInit() so that
11571174
* CDROMInit() -> CDIntfInit() -> CDIntfIsImageLoaded() returns true
@@ -1192,79 +1209,7 @@ bool retro_load_game(const struct retro_game_info *info)
11921209
for (i = 0; i < 1024 * 512; ++i)
11931210
videoBuffer[i] = 0xFF000000;
11941211

1195-
if (jaguar_cd_mode && cd_bios_loaded_externally)
1196-
{
1197-
/* Real BIOS path: The CD BIOS is a "cartridge" loaded at $800000.
1198-
* The standard boot ROM at $E00000 detects it, reads the header at
1199-
* $800404 (entry point $802000), and jumps there. */
1200-
const uint8_t *cdBiosData = external_cd_bios;
1201-
size_t cdBiosSize = 0x40000;
1202-
1203-
memcpy(jagMemSpace + 0x800000, cdBiosData, cdBiosSize);
1204-
jaguarRunAddress = GET32(jagMemSpace, 0x800404);
1205-
jaguarCartInserted = true;
1206-
jaguarROMSize = cdBiosSize;
1207-
1208-
/* The boot ROM runs a GPU-based cart authentication check that loops
1209-
* forever in emulation (the GPU security code at $F032EC never
1210-
* converges). Skip the GPU wait by clearing bit 0. */
1211-
jagMemSpace[0x80040B] &= 0xFE;
1212-
LOG_DBG("[CD-TRACE] Boot ROM wait bypass applied at $80040B (value now $%02X)\n",
1213-
jagMemSpace[0x80040B]);
1214-
1215-
JaguarReset();
1216-
}
1217-
else if (jaguar_cd_mode)
1218-
{
1219-
/* HLE path: no external BIOS — JaguarCDHLEBoot() will be called
1220-
* after JaguarReset() to set up the boot stub directly. */
1221-
jaguarCartInserted = false;
1222-
JaguarReset();
1223-
}
1224-
else
1225-
{
1226-
SET32(jaguarMainRAM, 0, 0x00200000);
1227-
1228-
if (info->data && info->size > 0)
1229-
{
1230-
JaguarLoadFile((uint8_t*)info->data, info->size);
1231-
}
1232-
else if (info->path)
1233-
{
1234-
RFILE *romFile;
1235-
romFile = rfopen(info->path, "rb");
1236-
if (romFile)
1237-
{
1238-
uint8_t *romData;
1239-
int64_t fileSize;
1240-
1241-
rfseek(romFile, 0, SEEK_END);
1242-
fileSize = rftell(romFile);
1243-
rfseek(romFile, 0, SEEK_SET);
1244-
1245-
romData = (uint8_t *)malloc(fileSize);
1246-
if (romData)
1247-
{
1248-
rfread(romData, 1, fileSize, romFile);
1249-
JaguarLoadFile(romData, fileSize);
1250-
free(romData);
1251-
}
1252-
rfclose(romFile);
1253-
}
1254-
}
1255-
}
1256-
1257-
JaguarReset();
1258-
1259-
/* HLE CD boot: if CD mode and no external BIOS, boot via HLE.
1260-
* Must happen after JaguarReset() since reset clears RAM/GPU state. */
1261-
if (jaguar_cd_mode && !cd_bios_loaded_externally)
1262-
{
1263-
if (!JaguarCDHLEBoot())
1264-
{
1265-
LOG_ERR("[CD-HLE] HLE boot failed — falling back to diagnostic screen\n");
1266-
}
1267-
}
1212+
bootConfig.strategy->boot(info);
12681213

12691214
/* The frontend will load .srm data into our save buffer (returned by
12701215
* retro_get_memory_data) after this function returns but before the

libretro_core_options.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,16 +165,16 @@ struct retro_core_option_v2_definition option_defs_us[] = {
165165
"virtualjaguar_cd_boot_mode",
166166
"CD Boot Mode (Restart)",
167167
NULL,
168-
"How to boot Jaguar CD games. Auto uses the real BIOS if found, otherwise HLE. HLE always uses high-level emulation (no BIOS ROM needed). BIOS requires an external BIOS ROM file.",
168+
"How to boot Jaguar CD games. HLE uses high-level emulation (no BIOS ROM needed, recommended). BIOS requires an external BIOS ROM file (experimental). Auto uses the real BIOS if found, otherwise HLE.",
169169
NULL,
170170
NULL,
171171
{
172-
{ "auto", "Auto" },
173172
{ "hle", "HLE (No BIOS Required)" },
174-
{ "bios", "BIOS (Required)" },
173+
{ "auto", "Auto" },
174+
{ "bios", "BIOS (Experimental)" },
175175
{ NULL, NULL },
176176
},
177-
"auto"
177+
"hle"
178178
},
179179
{
180180
"virtualjaguar_alt_inputs",

0 commit comments

Comments
 (0)