3rdParty: experimental transfer pak changes from @m4xw

This commit is contained in:
Rosalie Wanders
2025-02-26 14:57:06 +01:00
parent cbb432b4ff
commit 232a962989
4 changed files with 313 additions and 192 deletions
@@ -37,7 +37,7 @@
static uint16_t gb_cart_address(unsigned int bank, uint16_t address)
{
return (address & 0x3fff) | ((bank & 0x3) * 0x4000) ;
return 0x4000 * bank + (address & 0x7fff) - 0x4000;
}
void init_transferpak(struct transferpak* tpk, struct gb_cart* gb_cart)
@@ -49,10 +49,7 @@ void poweron_transferpak(struct transferpak* tpk)
{
tpk->enabled = 0;
tpk->bank = 0;
tpk->access_mode = (tpk->gb_cart == NULL)
? CART_NOT_INSERTED
: CART_ACCESS_MODE_0;
tpk->access_mode_changed = 0x44;
tpk->reset_state = 3;
if (tpk->gb_cart != NULL) {
poweron_gb_cart(tpk->gb_cart);
@@ -68,9 +65,11 @@ void change_gb_cart(struct transferpak* tpk, struct gb_cart* gb_cart)
}
else {
tpk->access_mode = CART_ACCESS_MODE_0;
poweron_gb_cart(gb_cart);
// poweron_gb_cart(gb_cart);
}
tpk->reset_state = 3;
tpk->gb_cart = gb_cart;
}
@@ -84,131 +83,225 @@ static void unplug_transferpak(void* pak)
{
}
static void read_transferpak(void* pak, uint16_t address, uint8_t* data, size_t size)
{
struct transferpak* tpk = (struct transferpak*)pak;
uint8_t value;
DebugMessage(M64MSG_VERBOSE, "tpak read: %04x", address);
switch(address >> 12)
{
case 0x8:
/* get gb cart state (enabled/disabled) */
value = (tpk->enabled)
? 0x84
: 0x00;
DebugMessage(M64MSG_VERBOSE, "tpak get cart state: %02x", value);
memset(data, value, size);
break;
case 0xb:
/* get gb cart access mode */
if (tpk->enabled)
{
DebugMessage(M64MSG_VERBOSE, "tpak get access mode: %02x", tpk->access_mode);
memset(data, tpk->access_mode, size);
if (tpk->access_mode != CART_NOT_INSERTED)
{
data[0] |= tpk->access_mode_changed;
}
tpk->access_mode_changed = 0;
}
break;
case 0xc:
case 0xd:
case 0xe:
case 0xf:
/* read gb cart */
if (tpk->enabled)
{
DebugMessage(M64MSG_VERBOSE, "tpak read cart: %04x", address);
if (tpk->gb_cart != NULL) {
read_gb_cart(tpk->gb_cart, gb_cart_address(tpk->bank, address), data, size);
}
}
break;
/* Reads from the Transfer Pak. */
static void read_transferpak(void* pak, uint16_t address, uint8_t* data, size_t size)
{
struct transferpak* tpk = (struct transferpak*)pak;
uint8_t value;
switch (address >> 12)
{
case 0x8:
/*
* 0x8 => read the TPak "enabled" status
* In Rust code: if (enabled) => 0x84 else => 0x00
*/
value = (tpk->enabled) ? 0x84 : 0x00;
DebugMessage(M64MSG_VERBOSE, "TPak returning cart state: %02x", value);
memset(data, value, size);
return;
default:
DebugMessage(M64MSG_WARNING, "Unknown tpak read: %04x", address);
if (!tpk->enabled) {
DebugMessage(M64MSG_VERBOSE, "TPak read ignored because disabled");
return;
}
}
}
static void write_transferpak(void* pak, uint16_t address, const uint8_t* data, size_t size)
{
struct transferpak* tpk = (struct transferpak*)pak;
uint8_t value = data[size-1];
DebugMessage(M64MSG_VERBOSE, "tpak write: %04x <- %02x", address, value);
switch(address >> 12)
switch (address >> 12)
{
case 0x8:
/* enable / disable gb cart */
switch(value)
{
case 0xfe:
tpk->enabled = 0;
DebugMessage(M64MSG_VERBOSE, "tpak disabled");
break;
case 0x84:
tpk->enabled = 1;
DebugMessage(M64MSG_VERBOSE, "tpak enabled");
break;
default:
DebugMessage(M64MSG_WARNING, "Unknown tpak write: %04x <- %02x", address, value);
case 0xB:
{
uint8_t val = 0;
if (tpk->gb_cart && tpk->gb_cart->enabled) {
val |= (1 << 0); /* bit0 => cart enabled? */
}
val |= (uint8_t)((tpk->reset_state & 3) << 2);
if (tpk->enabled) {
val |= (1 << 7);
}
/* Simple state machine for reset_state (mirroring Rust logic). */
if ( tpk->gb_cart->enabled && tpk->reset_state == 3) {
tpk->reset_state = 2;
}
else if (!tpk->gb_cart->enabled && tpk->reset_state == 2) {
tpk->reset_state = 1;
}
else if (!tpk->gb_cart->enabled && tpk->reset_state == 1) {
tpk->reset_state = 0;
}
DebugMessage(M64MSG_VERBOSE, "TPak read 0xB => %02x", val);
memset(data, val, size);
break;
}
case 0xC:
case 0xD:
case 0xE:
case 0xF:
/*
* Read from the Game Boy cart.
* The address is computed as bank-based offset
* (0x4000 * bank + ((address & 0x7FFF) - 0x4000)).
*/
if (tpk->gb_cart != NULL)
{
const uint16_t cart_addr = gb_cart_address(tpk->bank, address);
read_gb_cart(tpk->gb_cart, cart_addr, data, size);
//DebugMessage(M64MSG_VERBOSE,
// "TPak reading cart: bank=%d, raw_addr=%04x => gb_addr=%04x -> %02x", tpk->bank, address, cart_addr, data[0]);
/*for(int i = 0; i < size; i++)
{
DebugMessage(M64MSG_VERBOSE, "TPak reading cart: bank=%d, raw_addr=%04x => gb_addr=%04x -> %02x", tpk->bank, address, cart_addr, data[i]);
}*/
}
else
{
DebugMessage(M64MSG_WARNING, "TPak read: no GB cart present");
memset(data, 0x00, size);
}
break;
default:
DebugMessage(M64MSG_WARNING, "Unknown TPak read at %04x", address);
memset(data, 0x00, size);
break;
}
}
/* Writes to the Transfer Pak. */
static void write_transferpak(void* pak, uint16_t address, const uint8_t* buf, size_t size)
{
struct transferpak* tpk = (struct transferpak*)pak;
/* The Rust code always uses the last byte as the "value" for multi-byte writes. */
uint16_t value = buf[size - 1];
DebugMessage(M64MSG_VERBOSE, "TransferPak write: %04x <- %02x", address, value);
switch (address >> 12)
{
case 0x8:
/*
* Matches the Rust approach: 0xFE => disable, 0x84 => enable
*/
switch (value)
{
case 0xFE:
tpk->enabled = 0;
DebugMessage(M64MSG_VERBOSE, "TPak disabled");
return;
case 0x84:
if (!tpk->enabled)
{
tpk->bank = 3;
tpk->reset_state = 0;
if (tpk->gb_cart != NULL)
{
tpk->gb_cart->enabled = 0;
}
}
tpk->enabled = 1;
DebugMessage(M64MSG_VERBOSE, "TPak enabled (0x84)");
return;
default:
DebugMessage(M64MSG_WARNING,
"Unknown write to TPak (0x8 region): %02x", value);
return;
}
return;
default:
/* If TPak not enabled, ignore writes (as in your Rust example). */
if (!tpk->enabled) {
DebugMessage(M64MSG_VERBOSE, "TPak write ignored because disabled");
return;
}
break;
case 0xA:
if (!tpk->enabled) {
DebugMessage(M64MSG_VERBOSE, "TPak write ignored because disabled");
return;
}
break;
case 0xa:
/* set gb cart bank */
if (tpk->enabled)
{
tpk->bank = value;
DebugMessage(M64MSG_VERBOSE, "tpak set bank %02x", tpk->bank);
tpk->bank = (uint16_t)value;
if (tpk->bank > 3) {
DebugMessage(M64MSG_WARNING, "TPak: invalid bank %d", tpk->bank);
tpk->bank = 0;
}
DebugMessage(M64MSG_VERBOSE, "TPak set bank => %d", tpk->bank);
break;
case 0xB:
if (value & 1)
{
if (tpk->gb_cart != NULL && !tpk->gb_cart->enabled)
{
/* Reset state to 3, and do any "power-on" type logic on the cart. */
tpk->reset_state = 3;
tpk->gb_cart->enabled = 1;
tpk->gb_cart->rom_bank = 1;
tpk->gb_cart->ram_bank = 0;
tpk->gb_cart->ram_enable = 0;
/* e.g. set cart type if needed:
* tpk->gb_cart->cart_type = get_cart_type(tpk->gb_cart->rom[0x147]);
* (This depends on your real code.)
*/
DebugMessage(M64MSG_VERBOSE, "TPak: cart enabled, reset_state=3");
}
else if (tpk->gb_cart)
{
tpk->gb_cart->enabled = 1;
DebugMessage(M64MSG_VERBOSE, "TPak: cart already enabled");
}
}
else
{
if (tpk->gb_cart) {
tpk->gb_cart->enabled = 0;
}
DebugMessage(M64MSG_VERBOSE, "TPak: cart disabled via write to 0xB");
}
break;
case 0xC:
case 0xD:
case 0xE:
case 0xF:
{
if (!tpk->enabled) {
DebugMessage(M64MSG_VERBOSE, "TPak write ignored because disabled");
return;
}
break;
case 0xb:
/* set gb cart access mode */
if (tpk->enabled)
{
tpk->access_mode_changed = 0x04;
tpk->access_mode = ((value & 1) == 0)
? CART_ACCESS_MODE_0
: CART_ACCESS_MODE_1;
if ((value & 0xfe) != 0)
{
DebugMessage(M64MSG_WARNING, "Unknown tpak write: %04x <- %02x", address, value);
}
DebugMessage(M64MSG_VERBOSE, "tpak set access mode %02x", tpk->access_mode);
}
break;
case 0xc:
case 0xd:
case 0xe:
case 0xf:
/* write gb cart */
// if (tpk->enabled)
{
DebugMessage(M64MSG_VERBOSE, "tpak write gb: %04x <- %02x", address, value);
if (tpk->gb_cart != NULL) {
write_gb_cart(tpk->gb_cart, gb_cart_address(tpk->bank, address), data, size);
}
}
break;
default:
DebugMessage(M64MSG_WARNING, "Unknown tpak write: %04x <- %02x", address, value);
}
}
/* Write to GB cart memory. */
if (tpk->gb_cart != NULL)
{
uint16_t cart_addr = gb_cart_address(tpk->bank, address);
DebugMessage(M64MSG_VERBOSE,
"TPak write to cart: bank=%d, raw_addr=%04x => gb_addr=%04x val=%02x",
tpk->bank, address, cart_addr, value);
write_gb_cart(tpk->gb_cart, cart_addr, buf, size);
}
else
{
DebugMessage(M64MSG_WARNING,
"TPak write to 0xC..0xF, but no GB cart present");
}
break;
};
}
}
/* Transfer pak definition */
const struct pak_interface g_itransferpak =
@@ -219,3 +312,4 @@ const struct pak_interface g_itransferpak =
read_transferpak,
write_transferpak
};
@@ -40,6 +40,7 @@ struct transferpak
unsigned int bank;
unsigned int access_mode;
unsigned int access_mode_changed;
unsigned int reset_state;
struct gb_cart* gb_cart;
};
+93 -68
View File
@@ -54,7 +54,7 @@ enum gbcart_extra_devices
/* various helper functions for ram, rom, or MBC uses */
static void read_rom(const void* rom_storage, const struct storage_backend_interface* irom_storage, uint16_t address, uint8_t* data, size_t size)
static void read_rom(const void* rom_storage, const struct storage_backend_interface* irom_storage, uint32_t address, uint8_t* data, size_t size)
{
assert(size > 0);
@@ -68,14 +68,14 @@ static void read_rom(const void* rom_storage, const struct storage_backend_inter
}
static void read_ram(const void* ram_storage, const struct storage_backend_interface* iram_storage, unsigned int enabled, uint16_t address, uint8_t* data, size_t size, uint8_t mask)
static void read_ram(const void* ram_storage, const struct storage_backend_interface* iram_storage, unsigned int enabled, uint32_t address, uint8_t* data, size_t size, uint8_t mask)
{
size_t i;
assert(size > 0);
/* RAM has to be enabled before use */
if (!enabled) {
DebugMessage(M64MSG_WARNING, "Trying to read from non enabled GB RAM %04x", address);
DebugMessage(M64MSG_WARNING, "read_ram Trying to read from non enabled GB RAM %04x", address);
memset(data, 0xff, size);
return;
}
@@ -240,7 +240,7 @@ static int write_gb_cart_mbc1(struct gb_cart* gb_cart, uint16_t address, const u
/* 0x2000-0x3fff: ROM bank select (low 5 bits) */
case (0x2000 >> 13):
bank = value & 0x1f;
gb_cart->rom_bank = (gb_cart->rom_bank & ~UINT8_C(0x1f)) | ((bank == 0) ? 1 : bank);
gb_cart->rom_bank = (gb_cart->rom_bank & ~UINT8_C(0x1f)) | (bank == 0) ? 1 : bank;
DebugMessage(M64MSG_VERBOSE, "MBC1 set rom bank %02x", gb_cart->rom_bank);
break;
@@ -345,25 +345,32 @@ static int write_gb_cart_mbc2(struct gb_cart* gb_cart, uint16_t address, const u
return 0;
}
static int read_gb_cart_mbc3(struct gb_cart* gb_cart, uint16_t address, uint8_t* data, size_t size)
static int read_gb_cart_mbc3(struct gb_cart* gb_cart,
uint16_t address,
uint8_t* dst,
size_t size)
{
switch(address >> 13)
if (address < 0x4000)
{
/* 0x0000-0x3fff: ROM bank 00 */
case (0x0000 >> 13):
case (0x2000 >> 13):
read_rom(gb_cart->rom_storage, gb_cart->irom_storage, address, data, size);
break;
/* Bank 0 (0x0000 - 0x3FFF) */
uint32_t banked_address = address & 0x3FFF; /* same as just address, but & 0x3FFF is explicit */
read_rom(gb_cart->rom_storage, gb_cart->irom_storage, banked_address, dst, size);
/* 0x4000-0x7fff: ROM bank 01-7f */
case (0x4000 >> 13):
case (0x6000 >> 13):
read_rom(gb_cart->rom_storage, gb_cart->irom_storage, (address - 0x4000) + (gb_cart->rom_bank * 0x4000), data, size);
break;
/* 0xa000-0xbfff: RAM bank 00-07 or RTC register 08-0c */
case (0xa000 >> 13):
//DebugMessage(M64MSG_VERBOSE, "MBC3 read from ROM bank 0: %04x", address);
}
else if (address < 0x8000)
{
/* Switched ROM bank (0x4000 - 0x7FFF) */
size_t banked_address = (size_t)((uint32_t)address - 0x4000)
+ (gb_cart->rom_bank * 0x4000);
read_rom(gb_cart->rom_storage, gb_cart->irom_storage, banked_address, dst, size);
//DebugMessage(M64MSG_VERBOSE, "MBC3 read from ROM bank %02x: %04x", gb_cart->rom_bank, address);
}
else if (address >= 0xA000 && address < 0xC000)
{
switch(gb_cart->ram_bank)
{
/* RAM banks */
@@ -375,8 +382,12 @@ static int read_gb_cart_mbc3(struct gb_cart* gb_cart, uint16_t address, uint8_t*
case 0x05:
case 0x06:
case 0x07:
read_ram(gb_cart->ram_storage, gb_cart->iram_storage, gb_cart->ram_enable, (address - 0xa000) + (gb_cart->ram_bank * 0x2000), data, size, UINT8_C(0xff));
{
size_t banked_address = (size_t)((uint32_t)address - 0xA000)
+ (gb_cart->ram_bank * 0x2000);
read_ram(gb_cart->ram_storage, gb_cart->iram_storage, gb_cart->ram_enable, banked_address, dst, size, UINT8_C(0xff));
break;
}
/* RTC registers */
case 0x08:
@@ -387,68 +398,78 @@ static int read_gb_cart_mbc3(struct gb_cart* gb_cart, uint16_t address, uint8_t*
/* RAM has to be enabled before use */
if (!gb_cart->ram_enable) {
DebugMessage(M64MSG_WARNING, "Trying to read from non enabled GB RAM %04x", address);
memset(data, 0xff, size);
memset(dst, 0xff, size);
break;
}
if (!(gb_cart->extra_devices & GED_RTC)) {
DebugMessage(M64MSG_WARNING, "Trying to read from absent RTC %04x", address);
memset(data, 0xff, size);
memset(dst, 0xff, size);
break;
}
memset(data, read_mbc3_rtc_regs(&gb_cart->rtc, gb_cart->ram_bank - 0x08), size);
memset(dst, read_mbc3_rtc_regs(&gb_cart->rtc, gb_cart->ram_bank - 0x08), size);
break;
default:
DebugMessage(M64MSG_WARNING, "Unknown device mapped in RAM/RTC space: %04x", address);
}
break;
default:
DebugMessage(M64MSG_WARNING, "Invalid cart read (MBC3): %04x", address);
}
else
{
/* The Rust code does a panic!("Unsupported read address {:x}", address); */
DebugMessage(M64MSG_WARNING, "Unsupported read address %04x", address);
}
return 0;
}
static int write_gb_cart_mbc3(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data, size_t size)
static int write_gb_cart_mbc3(struct gb_cart* gb_cart,
uint16_t address,
const uint8_t* src,
size_t size)
{
uint8_t bank;
uint8_t value = data[size-1];
if (size == 0) {
/* Nothing to write, just return */
return 0;
}
switch(address >> 13)
uint8_t value = src[size - 1];
if (address < 0x2000)
{
/* 0x0000-0x1fff: RAM/RTC enable */
case (0x0000 >> 13):
/* Enable or disable RAM */
set_ram_enable(gb_cart, value);
break;
/* 0x2000-0x3fff: ROM bank select */
case (0x2000 >> 13):
bank = value & 0x7f;
gb_cart->rom_bank = (bank == 0) ? 1 : bank;
DebugMessage(M64MSG_VERBOSE, "MBC3 set rom bank %02x", gb_cart->rom_bank);
break;
/* 0x4000-0x5fff: RAM bank / RTC register select */
case (0x4000 >> 13):
gb_cart->ram_bank = value;
}
else if (address < 0x4000)
{
/* Select ROM bank (0x01..0x7F) */
uint8_t bank = value & 0x7F;
gb_cart->rom_bank = bank;
if (gb_cart->rom_bank == 0) {
gb_cart->rom_bank = 1;
}
DebugMessage(M64MSG_VERBOSE, "MBC3 set rom bank %02x addr %04x", gb_cart->rom_bank, address);
}
else if (address < 0x6000)
{
/* Select RAM bank or RTC register */
gb_cart->ram_bank = (value & 0x0F);
DebugMessage(M64MSG_VERBOSE, "MBC3 set ram bank %02x", gb_cart->ram_bank);
break;
/* 0x6000-0x7fff: latch clock registers */
case (0x6000 >> 13):
}
else if (address < 0x8000)
{
/* RTC latch (not implemented here) */
/* The Rust code simply does nothing. */
if (!(gb_cart->extra_devices & GED_RTC)) {
DebugMessage(M64MSG_WARNING, "Trying to latch to absent RTC %04x", address);
break;
}
latch_mbc3_rtc_regs(&gb_cart->rtc, value);
break;
/* 0xa000-0xbfff: RAM bank 00-07 or RTC register 08-0c */
case (0xa000 >> 13):
else
latch_mbc3_rtc_regs(&gb_cart->rtc, value);
}
else if (address >= 0xA000 && address < 0xC000)
{
DebugMessage(M64MSG_VERBOSE, "MBC3 write to RAM: %04x <- %02x (bank %02x)", address, value, gb_cart->ram_bank);
switch(gb_cart->ram_bank)
{
/* RAM banks */
@@ -460,7 +481,7 @@ static int write_gb_cart_mbc3(struct gb_cart* gb_cart, uint16_t address, const u
case 0x05:
case 0x06:
case 0x07:
write_ram(gb_cart->ram_storage, gb_cart->iram_storage, gb_cart->ram_enable, (address - 0xa000) + (gb_cart->ram_bank * 0x2000), data, size, UINT8_C(0xff));
write_ram(gb_cart->ram_storage, gb_cart->iram_storage, gb_cart->ram_enable, (address - 0xa000) + (gb_cart->ram_bank * 0x2000), src, size, UINT8_C(0xff));
break;
/* RTC registers */
@@ -469,11 +490,11 @@ static int write_gb_cart_mbc3(struct gb_cart* gb_cart, uint16_t address, const u
case 0x0a:
case 0x0b:
case 0x0c:
/* RAM has to be enabled before use */
if (!gb_cart->ram_enable) {
DebugMessage(M64MSG_WARNING, "Trying to write to non enabled GB RAM %04x", address);
break;
}
/* RAM has to be enabled before use */
if (!gb_cart->ram_enable) {
DebugMessage(M64MSG_WARNING, "Trying to write to non enabled GB RAM %04x", address);
break;
}
if (!(gb_cart->extra_devices & GED_RTC)) {
DebugMessage(M64MSG_WARNING, "Trying to write to absent RTC %04x", address);
@@ -486,15 +507,18 @@ static int write_gb_cart_mbc3(struct gb_cart* gb_cart, uint16_t address, const u
default:
DebugMessage(M64MSG_WARNING, "Unknwown device mapped in RAM/RTC space: %04x", address);
}
break;
default:
DebugMessage(M64MSG_WARNING, "Invalid cart write (MBC3): %04x", address);
}
else
{
/* The Rust code does panic!("Unsupported write address {:x}", address); */
DebugMessage(M64MSG_WARNING, "Unsupported write address %04x", address);
}
return 0;
}
static int read_gb_cart_mbc5(struct gb_cart* gb_cart, uint16_t address, uint8_t* data, size_t size)
{
switch(address >> 13)
@@ -1117,3 +1141,4 @@ int write_gb_cart(struct gb_cart* gb_cart, uint16_t address, const uint8_t* data
return gb_cart->write_gb_cart(gb_cart, address, data, size);
}
@@ -56,6 +56,7 @@ struct gb_cart
void* ram_storage;
const struct storage_backend_interface* iram_storage;
unsigned int enabled;
unsigned int rom_bank;
unsigned int ram_bank;