Initial MBC7 support
This commit is contained in:
parent
02f55d12d3
commit
c6f39bc60b
@ -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",
|
||||||
};
|
};
|
||||||
|
15
Core/gb.c
15
Core/gb.c
@ -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);
|
||||||
|
21
Core/gb.h
21
Core/gb.h
@ -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);
|
||||||
|
16
Core/mbc.c
16
Core/mbc.c
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
152
Core/memory.c
152
Core/memory.c
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user