@@ -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;
7576static bool save_data_needs_unpack = false;
7677static bool jaguar_cd_mode = false;
7778static 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
8182void retro_set_video_refresh (retro_video_refresh_t cb ) { video_cb = cb ; }
8283void 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+
9761015static 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
0 commit comments