Initial MBC7 support

This commit is contained in:
Lior Halphon 2021-11-12 17:44:51 +02:00
parent 02f55d12d3
commit c6f39bc60b
6 changed files with 212 additions and 6 deletions

View File

@ -1541,6 +1541,7 @@ static bool mbc(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
[GB_MBC2] = "MBC2", [GB_MBC2] = "MBC2",
[GB_MBC3] = "MBC3", [GB_MBC3] = "MBC3",
[GB_MBC5] = "MBC5", [GB_MBC5] = "MBC5",
[GB_MBC7] = "MBC7",
[GB_HUC1] = "HUC-1", [GB_HUC1] = "HUC-1",
[GB_HUC3] = "HUC-3", [GB_HUC3] = "HUC-3",
}; };

View File

@ -1010,9 +1010,11 @@ void GB_load_battery_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t
reset_rtc: reset_rtc:
gb->last_rtc_second = time(NULL); gb->last_rtc_second = time(NULL);
gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */ gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */
if (gb->cartridge_type->mbc_type == GB_HUC3) {
gb->huc3.days = 0xFFFF; gb->huc3.days = 0xFFFF;
gb->huc3.minutes = 0xFFF; gb->huc3.minutes = 0xFFF;
gb->huc3.alarm_enabled = false; gb->huc3.alarm_enabled = false;
}
exit: exit:
return; return;
} }
@ -1116,9 +1118,11 @@ void GB_load_battery(GB_gameboy_t *gb, const char *path)
reset_rtc: reset_rtc:
gb->last_rtc_second = time(NULL); gb->last_rtc_second = time(NULL);
gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */ gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */
if (gb->cartridge_type->mbc_type == GB_HUC3) {
gb->huc3.days = 0xFFFF; gb->huc3.days = 0xFFFF;
gb->huc3.minutes = 0xFFF; gb->huc3.minutes = 0xFFF;
gb->huc3.alarm_enabled = false; gb->huc3.alarm_enabled = false;
}
exit: exit:
fclose(f); fclose(f);
return; return;
@ -1844,6 +1848,17 @@ void GB_set_rtc_mode(GB_gameboy_t *gb, GB_rtc_mode_t mode)
} }
} }
bool GB_has_accelerometer(GB_gameboy_t *gb)
{
return gb->cartridge_type->mbc_type == GB_MBC7;
}
void GB_set_accelerometer_values(GB_gameboy_t *gb, double x, double y)
{
gb->accelerometer_x = x;
gb->accelerometer_y = y;
}
void GB_get_rom_title(GB_gameboy_t *gb, char *title) void GB_get_rom_title(GB_gameboy_t *gb, char *title)
{ {
memset(title, 0, 17); memset(title, 0, 17);

View File

@ -444,6 +444,22 @@ struct GB_gameboy_internal_s {
uint8_t ram_bank:4; uint8_t ram_bank:4;
} mbc5; } mbc5;
struct {
uint8_t rom_bank;
uint16_t x_latch;
uint16_t y_latch;
bool latch_ready:1;
bool eeprom_do:1;
bool eeprom_di:1;
bool eeprom_clk:1;
bool eeprom_cs:1;
uint16_t eeprom_command:11;
uint16_t read_bits;
uint8_t bits_countdown:5;
bool secondary_ram_enable:1;
bool eeprom_write_enabled:1;
} mbc7;
struct { struct {
uint8_t bank_low:6; uint8_t bank_low:6;
uint8_t bank_high:3; uint8_t bank_high:3;
@ -609,6 +625,7 @@ struct GB_gameboy_internal_s {
GB_color_correction_mode_t color_correction_mode; GB_color_correction_mode_t color_correction_mode;
double light_temperature; double light_temperature;
bool keys[4][GB_KEY_MAX]; bool keys[4][GB_KEY_MAX];
double accelerometer_x, accelerometer_y;
GB_border_mode_t border_mode; GB_border_mode_t border_mode;
GB_sgb_border_t borrowed_border; GB_sgb_border_t borrowed_border;
bool tried_loading_sgb_border; bool tried_loading_sgb_border;
@ -835,6 +852,10 @@ unsigned GB_time_to_alarm(GB_gameboy_t *gb); // 0 if no alarm
/* RTC emulation mode */ /* RTC emulation mode */
void GB_set_rtc_mode(GB_gameboy_t *gb, GB_rtc_mode_t mode); void GB_set_rtc_mode(GB_gameboy_t *gb, GB_rtc_mode_t mode);
/* For cartridges motion controls */
bool GB_has_accelerometer(GB_gameboy_t *gb);
void GB_set_accelerometer_values(GB_gameboy_t *gb, double x, double y);
/* For integration with SFC/SNES emulators */ /* For integration with SFC/SNES emulators */
void GB_set_joyp_write_callback(GB_gameboy_t *gb, GB_joyp_write_callback_t callback); void GB_set_joyp_write_callback(GB_gameboy_t *gb, GB_joyp_write_callback_t callback);
void GB_set_icd_pixel_callback(GB_gameboy_t *gb, GB_icd_pixel_callback_t callback); void GB_set_icd_pixel_callback(GB_gameboy_t *gb, GB_icd_pixel_callback_t callback);

View File

@ -34,6 +34,8 @@ const GB_cartridge_t GB_cart_defs[256] = {
{ GB_MBC5 , GB_STANDARD_MBC, false, false, false, true }, // 1Ch MBC5+RUMBLE { GB_MBC5 , GB_STANDARD_MBC, false, false, false, true }, // 1Ch MBC5+RUMBLE
{ GB_MBC5 , GB_STANDARD_MBC, true , false, false, true }, // 1Dh MBC5+RUMBLE+RAM { GB_MBC5 , GB_STANDARD_MBC, true , false, false, true }, // 1Dh MBC5+RUMBLE+RAM
{ GB_MBC5 , GB_STANDARD_MBC, true , true , false, true }, // 1Eh MBC5+RUMBLE+RAM+BATTERY { GB_MBC5 , GB_STANDARD_MBC, true , true , false, true }, // 1Eh MBC5+RUMBLE+RAM+BATTERY
[0x22] =
{ GB_MBC7 , GB_STANDARD_MBC, true, true, false, false}, // 22h MBC7+ACCEL+EEPROM
[0xFC] = [0xFC] =
{ GB_MBC5 , GB_CAMERA , true , true , false, false}, // FCh POCKET CAMERA { GB_MBC5 , GB_CAMERA , true , true , false, false}, // FCh POCKET CAMERA
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // FDh BANDAI TAMA5 (Todo: Not supported) { GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // FDh BANDAI TAMA5 (Todo: Not supported)
@ -97,6 +99,9 @@ void GB_update_mbc_mappings(GB_gameboy_t *gb)
gb->mbc_rom_bank = gb->mbc5.rom_bank_low | (gb->mbc5.rom_bank_high << 8); gb->mbc_rom_bank = gb->mbc5.rom_bank_low | (gb->mbc5.rom_bank_high << 8);
gb->mbc_ram_bank = gb->mbc5.ram_bank; gb->mbc_ram_bank = gb->mbc5.ram_bank;
break; break;
case GB_MBC7:
gb->mbc_rom_bank = gb->mbc7.rom_bank;
break;
case GB_HUC1: case GB_HUC1:
if (gb->huc1.mode == 0) { if (gb->huc1.mode == 0) {
gb->mbc_rom_bank = gb->huc1.bank_low | (gb->mbc1.bank_high << 6); gb->mbc_rom_bank = gb->huc1.bank_low | (gb->mbc1.bank_high << 6);
@ -148,6 +153,9 @@ void GB_configure_cart(GB_gameboy_t *gb)
if (gb->cartridge_type->mbc_type == GB_MBC2) { if (gb->cartridge_type->mbc_type == GB_MBC2) {
gb->mbc_ram_size = 0x200; gb->mbc_ram_size = 0x200;
} }
else if (gb->cartridge_type->mbc_type == GB_MBC7) {
gb->mbc_ram_size = 0x100;
}
else if (gb->cartridge_type->mbc_type == GB_TPP1) { else if (gb->cartridge_type->mbc_type == GB_TPP1) {
if (gb->rom[0x152] >= 1 && gb->rom[0x152] <= 9) { if (gb->rom[0x152] >= 1 && gb->rom[0x152] <= 9) {
gb->mbc_ram_size = 0x2000 << (gb->rom[0x152] - 1); gb->mbc_ram_size = 0x2000 << (gb->rom[0x152] - 1);
@ -187,4 +195,12 @@ void GB_configure_cart(GB_gameboy_t *gb)
if (gb->cartridge_type->mbc_type == GB_MBC5) { if (gb->cartridge_type->mbc_type == GB_MBC5) {
gb->mbc5.rom_bank_low = 1; gb->mbc5.rom_bank_low = 1;
} }
/* Initial MBC7 state */
if (gb->cartridge_type->mbc_type == GB_MBC7) {
gb->mbc7.x_latch = gb->mbc7.y_latch = 0x8000;
gb->mbc7.latch_ready = true;
gb->mbc7.read_bits = -1;
gb->mbc7.eeprom_do = true;
}
} }

View File

@ -10,6 +10,7 @@ typedef struct {
GB_MBC2, GB_MBC2,
GB_MBC3, GB_MBC3,
GB_MBC5, GB_MBC5,
GB_MBC7,
GB_HUC1, GB_HUC1,
GB_HUC3, GB_HUC3,
GB_TPP1, GB_TPP1,

View File

@ -301,8 +301,28 @@ static uint8_t read_vram(GB_gameboy_t *gb, uint16_t addr)
return gb->vram[(addr & 0x1FFF) + (gb->cgb_vram_bank? 0x2000 : 0)]; return gb->vram[(addr & 0x1FFF) + (gb->cgb_vram_bank? 0x2000 : 0)];
} }
static uint8_t read_mbc7_ram(GB_gameboy_t *gb, uint16_t addr)
{
if (!gb->mbc_ram_enable || !gb->mbc7.secondary_ram_enable) return 0xFF;
if (addr >= 0xB000) return 0xFF;
switch ((addr >> 4) & 0xF) {
case 2: return gb->mbc7.x_latch;
case 3: return gb->mbc7.x_latch >> 8;
case 4: return gb->mbc7.y_latch;
case 5: return gb->mbc7.y_latch >> 8;
case 6: return 0;
case 8: return gb->mbc7.eeprom_do | (gb->mbc7.eeprom_di << 1) |
(gb->mbc7.eeprom_clk << 6) | (gb->mbc7.eeprom_cs << 7);
}
return 0xFF;
}
static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr) static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr)
{ {
if (gb->cartridge_type->mbc_type == GB_MBC7) {
return read_mbc7_ram(gb, addr);
}
if (gb->cartridge_type->mbc_type == GB_HUC3) { if (gb->cartridge_type->mbc_type == GB_HUC3) {
switch (gb->huc3.mode) { switch (gb->huc3.mode) {
case 0xC: // RTC read case 0xC: // RTC read
@ -730,6 +750,13 @@ static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
break; break;
} }
break; break;
case GB_MBC7:
switch (addr & 0xF000) {
case 0x0000: case 0x1000: gb->mbc_ram_enable = value == 0x0A; break;
case 0x2000: case 0x3000: gb->mbc7.rom_bank = value; break;
case 0x4000: case 0x5000: gb->mbc7.secondary_ram_enable = value == 0x40; break;
}
break;
case GB_HUC1: case GB_HUC1:
switch (addr & 0xF000) { switch (addr & 0xF000) {
case 0x0000: case 0x1000: gb->huc1.ir_mode = (value & 0xF) == 0xE; break; case 0x0000: case 0x1000: gb->huc1.ir_mode = (value & 0xF) == 0xE; break;
@ -905,8 +932,133 @@ static bool huc3_write(GB_gameboy_t *gb, uint8_t value)
} }
} }
static void write_mbc7_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
{
if (!gb->mbc_ram_enable || !gb->mbc7.secondary_ram_enable) return;
if (addr >= 0xB000) return;
switch ((addr >> 4) & 0xF) {
case 0: {
if (value == 0x55) {
gb->mbc7.latch_ready = true;
gb->mbc7.x_latch = gb->mbc7.y_latch = 0x8000;
}
}
case 1: {
if (value == 0xAA) {
gb->mbc7.latch_ready = false;
gb->mbc7.x_latch = 0x81D0 + 0x70 * gb->accelerometer_x;
gb->mbc7.y_latch = 0x81D0 + 0x70 * gb->accelerometer_y;
}
}
case 8: {
gb->mbc7.eeprom_cs = value & 0x80;
gb->mbc7.eeprom_di = value & 2;
if (gb->mbc7.eeprom_cs) {
if (!gb->mbc7.eeprom_clk && (value & 0x40)) { // Clocked
gb->mbc7.eeprom_do = gb->mbc7.read_bits >> 15;
gb->mbc7.read_bits <<= 1;
gb->mbc7.read_bits |= 1;
if (gb->mbc7.bits_countdown == 0) {
/* Not transferring extra bits for a command*/
gb->mbc7.eeprom_command <<= 1;
gb->mbc7.eeprom_command |= gb->mbc7.eeprom_di;
if (gb->mbc7.eeprom_command & 0x400) {
// Got full command
switch ((gb->mbc7.eeprom_command >> 6) & 0xF) {
case 0x8:
case 0x9:
case 0xA:
case 0xB:
// READ
gb->mbc7.read_bits = LE16(((uint16_t *)gb->mbc_ram)[gb->mbc7.eeprom_command & 0x7F]);
gb->mbc7.eeprom_command = 0;
break;
case 0x3: // EWEN (Eeprom Write ENable)
gb->mbc7.eeprom_write_enabled = true;
gb->mbc7.eeprom_command = 0;
break;
case 0x0: // EWDS (Eeprom Write DiSable)
gb->mbc7.eeprom_write_enabled = false;
gb->mbc7.eeprom_command = 0;
break;
case 0x4:
case 0x5:
case 0x6:
case 0x7:
// WRITE
if (gb->mbc7.eeprom_write_enabled) {
((uint16_t *)gb->mbc_ram)[gb->mbc7.eeprom_command & 0x7F] = 0;
}
gb->mbc7.bits_countdown = 16;
// We still need to process this command, don't erase eeprom_command
break;
case 0xC:
case 0xD:
case 0xE:
case 0xF:
// ERASE
if (gb->mbc7.eeprom_write_enabled) {
((uint16_t *)gb->mbc_ram)[gb->mbc7.eeprom_command & 0x7F] = 0xFFFF;
gb->mbc7.read_bits = 0x3FFF; // Emulate some time to settle
}
gb->mbc7.eeprom_command = 0;
break;
case 0x2:
// ERAL (ERase ALl)
if (gb->mbc7.eeprom_write_enabled) {
memset(gb->mbc_ram, 0xFF, gb->mbc_ram_size);
((uint16_t *)gb->mbc_ram)[gb->mbc7.eeprom_command & 0x7F] = 0xFFFF;
gb->mbc7.read_bits = 0xFF; // Emulate some time to settle
}
gb->mbc7.eeprom_command = 0;
break;
case 0x1:
// WRAL (WRite ALl)
if (gb->mbc7.eeprom_write_enabled) {
memset(gb->mbc_ram, 0, gb->mbc_ram_size);
}
gb->mbc7.bits_countdown = 16;
// We still need to process this command, don't erase eeprom_command
break;
}
}
}
else {
// We're shifting in extra bits for a WRITE/WRAL command
gb->mbc7.bits_countdown--;
gb->mbc7.eeprom_do = true;
if (gb->mbc7.eeprom_di) {
uint16_t bit = LE16(1 << gb->mbc7.bits_countdown);
if (gb->mbc7.eeprom_command & 0x100) {
// WRITE
((uint16_t *)gb->mbc_ram)[gb->mbc7.eeprom_command & 0x7F] |= bit;
}
else {
// WRAL
for (unsigned i = 0; i < 0x7F; i++) {
((uint16_t *)gb->mbc_ram)[i] |= bit;
}
}
}
if (gb->mbc7.bits_countdown == 0) { // We're done
gb->mbc7.eeprom_command = 0;
gb->mbc7.read_bits = (gb->mbc7.eeprom_command & 0x100)? 0xFF : 0x3FFF; // Emulate some time to settle
}
}
}
}
gb->mbc7.eeprom_clk = value & 0x40;
}
}
}
static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value) static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
{ {
if (gb->cartridge_type->mbc_type == GB_MBC7) {
write_mbc7_ram(gb, addr, value);
return;
}
if (gb->cartridge_type->mbc_type == GB_HUC3) { if (gb->cartridge_type->mbc_type == GB_HUC3) {
if (huc3_write(gb, value)) return; if (huc3_write(gb, value)) return;
} }