Skip to content

Commit 513a406

Browse files
JoeMattclaude
andcommitted
Fix CD BIOS loading: treat as cartridge at $800000, not boot ROM
The CD BIOS is not a replacement for the standard boot ROM at $E00000. It is a "cartridge" loaded at $800000 with a Jaguar universal header at $800404 containing entry point $802000. Boot sequence: 1. Standard boot ROM at $E00000 initializes the 68K (SP=0, PC=$E00008) 2. Boot ROM detects "cartridge" (CD BIOS) at $800000 3. Boot ROM reads entry point from $800404 and jumps to $802000 4. CD BIOS code runs, shows intro animation, reads CD TOC The embedded jaguarCDBootROM data is not encrypted -- it contains readable strings (VLM, "ATARI APPROVED DATA HEADER") and valid 68K code at offset $2000. It just doesn't use standard 68K reset vectors because it boots as a cartridge, not a boot ROM. Also adds support for loading external CD BIOS from system directory with the common No-Intro filename convention (.j64 extension). Tested: CD BIOS boots, shows intro animation loop. CD drive protocol responses need further work for games to load. Co-Authored-By: Claude Opus 4.6 <[email protected]>
1 parent 2259731 commit 513a406

2 files changed

Lines changed: 49 additions & 39 deletions

File tree

libretro.c

Lines changed: 31 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,7 @@ static bool load_external_cd_bios(void)
958958
"jagcd_bios.bin",
959959
"jaguarcd.bin",
960960
"jagcd.bin",
961+
"[BIOS] Atari Jaguar CD (World).j64",
961962
NULL
962963
};
963964

@@ -991,12 +992,13 @@ static bool load_external_cd_bios(void)
991992
}
992993
rfclose(f);
993994

994-
/* Validate: first 8 bytes should be valid 68K vectors.
995-
* Initial PC should be in the BIOS ROM range $E00000-$E3FFFF. */
995+
/* Validate: the CD BIOS is loaded as a "cartridge" at $800000.
996+
* The Jaguar universal header at offset $404 contains the run address.
997+
* For the retail CD BIOS this is $802000. */
996998
{
997-
uint32_t pc = (external_cd_bios[4] << 24) | (external_cd_bios[5] << 16)
998-
| (external_cd_bios[6] << 8) | external_cd_bios[7];
999-
if (pc >= 0xE00000 && pc <= 0xE3FFFF)
999+
uint32_t run_addr = (external_cd_bios[0x404] << 24) | (external_cd_bios[0x405] << 16)
1000+
| (external_cd_bios[0x406] << 8) | external_cd_bios[0x407];
1001+
if (run_addr >= 0x800000 && run_addr <= 0x840000)
10001002
{
10011003
cd_bios_loaded_externally = true;
10021004
return true;
@@ -1141,27 +1143,11 @@ bool retro_load_game(const struct retro_game_info *info)
11411143

11421144
JaguarInit(); // set up hardware
11431145

1144-
if (jaguar_cd_mode)
1145-
{
1146-
/* Load CD BIOS at $E00000 (256 KB = 0x40000 bytes).
1147-
* Prefer the external BIOS file (real dump); fall back to
1148-
* embedded data (which is scrambled and won't boot). */
1149-
if (cd_bios_loaded_externally)
1150-
memcpy(jagMemSpace + 0xE00000, external_cd_bios, 0x40000);
1151-
else
1152-
{
1153-
uint8_t *cdBios = (vjs.cdBiosType == CDBIOS_DEV)
1154-
? jaguarDevCDBootROM : jaguarCDBootROM;
1155-
memcpy(jagMemSpace + 0xE00000, cdBios, 0x40000);
1156-
}
1157-
}
1158-
else
1159-
{
1160-
/* Standard cartridge mode */
1161-
memcpy(jagMemSpace + 0xE00000,
1162-
((vjs.biosType == BT_K_SERIES) ? jaguarBootROM : jaguarBootROM2),
1163-
0x20000);
1164-
}
1146+
/* The standard boot ROM always goes at $E00000 — it handles initial
1147+
* 68K boot for both cart and CD modes. */
1148+
memcpy(jagMemSpace + 0xE00000,
1149+
((vjs.biosType == BT_K_SERIES) ? jaguarBootROM : jaguarBootROM2),
1150+
0x20000);
11651151

11661152
JaguarSetScreenPitch(videoWidth);
11671153
JaguarSetScreenBuffer(videoBuffer);
@@ -1172,7 +1158,25 @@ bool retro_load_game(const struct retro_game_info *info)
11721158

11731159
if (jaguar_cd_mode)
11741160
{
1175-
jaguarCartInserted = false;
1161+
/* The CD BIOS is a "cartridge" loaded at $800000. The standard
1162+
* boot ROM at $E00000 detects it, reads the header at $800404
1163+
* (entry point $802000), and jumps there.
1164+
*
1165+
* We load directly into jagMemSpace rather than using JaguarLoadFile()
1166+
* because ParseFileType() doesn't recognize the 256KB CD BIOS format. */
1167+
const uint8_t *cdBiosData;
1168+
size_t cdBiosSize = 0x40000;
1169+
1170+
if (cd_bios_loaded_externally)
1171+
cdBiosData = external_cd_bios;
1172+
else
1173+
cdBiosData = (vjs.cdBiosType == CDBIOS_DEV)
1174+
? jaguarDevCDBootROM : jaguarCDBootROM;
1175+
1176+
memcpy(jagMemSpace + 0x800000, cdBiosData, cdBiosSize);
1177+
jaguarRunAddress = GET32(jagMemSpace, 0x800404);
1178+
jaguarCartInserted = true;
1179+
jaguarROMSize = cdBiosSize;
11761180
}
11771181
else
11781182
{
@@ -1212,17 +1216,6 @@ bool retro_load_game(const struct retro_game_info *info)
12121216

12131217
JaguarReset();
12141218

1215-
if (jaguar_cd_mode)
1216-
{
1217-
/* Set up CD BIOS boot vectors AFTER JaguarReset(), because
1218-
* JaguarReset() overwrites RAM[0..7] with jaguarRunAddress
1219-
* when jaguarCartInserted is false. */
1220-
uint8_t *biosBase = jagMemSpace + 0xE00000;
1221-
SET32(jaguarMainRAM, 0, GET32(biosBase, 0)); /* Initial SP */
1222-
SET32(jaguarMainRAM, 4, GET32(biosBase, 4)); /* Initial PC */
1223-
m68k_pulse_reset(); /* Re-reset 68K to pick up new vectors */
1224-
}
1225-
12261219
/* The frontend will load .srm data into our save buffer (returned by
12271220
* retro_get_memory_data) after this function returns but before the
12281221
* first retro_run(). We unpack it on the first frame. */

test/test_cd_boot.c

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,14 +189,31 @@ int main(int argc, char *argv[])
189189
printf("Game loaded successfully. Running %u frames...\n", num_frames);
190190

191191
/* Check initial RAM state */
192-
/* Access jaguarMainRAM to read vectors */
193192
uint8_t *(*get_ram)(void) = dlsym(handle, "GetRamPtr");
194193
if (get_ram)
195194
{
196195
uint8_t *ram = get_ram();
197196
uint32_t sp = (ram[0]<<24) | (ram[1]<<16) | (ram[2]<<8) | ram[3];
198197
uint32_t pc = (ram[4]<<24) | (ram[5]<<16) | (ram[6]<<8) | ram[7];
199198
printf("Initial vectors: SP=0x%08X, PC=0x%08X\n", sp, pc);
199+
200+
/* Check what's at $E00000 (BIOS ROM area) */
201+
/* jagMemSpace isn't exported, but jaguarMainRAM is at offset 0 in jagMemSpace */
202+
/* The BIOS is at 0xE00000 in the memory space */
203+
204+
/* Check cart ROM area ($800000) */
205+
/* Can't access directly, but we can check some BIOS-related globals */
206+
bool *cart_inserted = dlsym(handle, "jaguarCartInserted");
207+
if (cart_inserted)
208+
printf("jaguarCartInserted: %s\n", *cart_inserted ? "true" : "false");
209+
210+
uint32_t *run_addr = dlsym(handle, "jaguarRunAddress");
211+
if (run_addr)
212+
printf("jaguarRunAddress: 0x%08X\n", *run_addr);
213+
214+
bool *cd_bios_ext = dlsym(handle, "cd_bios_loaded_externally");
215+
if (cd_bios_ext)
216+
printf("cd_bios_loaded_externally: %s\n", *cd_bios_ext ? "true" : "false");
200217
}
201218

202219
for (frame_count = 0; frame_count < num_frames; frame_count++)

0 commit comments

Comments
 (0)