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_MBC3] = "MBC3",
|
||||
[GB_MBC5] = "MBC5",
|
||||
[GB_MBC7] = "MBC7",
|
||||
[GB_HUC1] = "HUC-1",
|
||||
[GB_HUC3] = "HUC-3",
|
||||
};
|
||||
|
27
Core/gb.c
27
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:
|
||||
gb->last_rtc_second = time(NULL);
|
||||
gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */
|
||||
gb->huc3.days = 0xFFFF;
|
||||
gb->huc3.minutes = 0xFFF;
|
||||
gb->huc3.alarm_enabled = false;
|
||||
if (gb->cartridge_type->mbc_type == GB_HUC3) {
|
||||
gb->huc3.days = 0xFFFF;
|
||||
gb->huc3.minutes = 0xFFF;
|
||||
gb->huc3.alarm_enabled = false;
|
||||
}
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
@ -1116,9 +1118,11 @@ void GB_load_battery(GB_gameboy_t *gb, const char *path)
|
||||
reset_rtc:
|
||||
gb->last_rtc_second = time(NULL);
|
||||
gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */
|
||||
gb->huc3.days = 0xFFFF;
|
||||
gb->huc3.minutes = 0xFFF;
|
||||
gb->huc3.alarm_enabled = false;
|
||||
if (gb->cartridge_type->mbc_type == GB_HUC3) {
|
||||
gb->huc3.days = 0xFFFF;
|
||||
gb->huc3.minutes = 0xFFF;
|
||||
gb->huc3.alarm_enabled = false;
|
||||
}
|
||||
exit:
|
||||
fclose(f);
|
||||
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)
|
||||
{
|
||||
memset(title, 0, 17);
|
||||
|
21
Core/gb.h
21
Core/gb.h
@ -443,6 +443,22 @@ struct GB_gameboy_internal_s {
|
||||
uint8_t rom_bank_high:1;
|
||||
uint8_t ram_bank:4;
|
||||
} 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 {
|
||||
uint8_t bank_low:6;
|
||||
@ -609,6 +625,7 @@ struct GB_gameboy_internal_s {
|
||||
GB_color_correction_mode_t color_correction_mode;
|
||||
double light_temperature;
|
||||
bool keys[4][GB_KEY_MAX];
|
||||
double accelerometer_x, accelerometer_y;
|
||||
GB_border_mode_t border_mode;
|
||||
GB_sgb_border_t borrowed_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 */
|
||||
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 */
|
||||
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);
|
||||
|
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, true , false, false, true }, // 1Dh MBC5+RUMBLE+RAM
|
||||
{ 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] =
|
||||
{ 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)
|
||||
@ -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_ram_bank = gb->mbc5.ram_bank;
|
||||
break;
|
||||
case GB_MBC7:
|
||||
gb->mbc_rom_bank = gb->mbc7.rom_bank;
|
||||
break;
|
||||
case GB_HUC1:
|
||||
if (gb->huc1.mode == 0) {
|
||||
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) {
|
||||
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) {
|
||||
if (gb->rom[0x152] >= 1 && gb->rom[0x152] <= 9) {
|
||||
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) {
|
||||
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_MBC3,
|
||||
GB_MBC5,
|
||||
GB_MBC7,
|
||||
GB_HUC1,
|
||||
GB_HUC3,
|
||||
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)];
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (gb->cartridge_type->mbc_type == GB_MBC7) {
|
||||
return read_mbc7_ram(gb, addr);
|
||||
}
|
||||
|
||||
if (gb->cartridge_type->mbc_type == GB_HUC3) {
|
||||
switch (gb->huc3.mode) {
|
||||
case 0xC: // RTC read
|
||||
@ -730,6 +750,13 @@ static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
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:
|
||||
switch (addr & 0xF000) {
|
||||
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)
|
||||
{
|
||||
if (gb->cartridge_type->mbc_type == GB_MBC7) {
|
||||
write_mbc7_ram(gb, addr, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (gb->cartridge_type->mbc_type == GB_HUC3) {
|
||||
if (huc3_write(gb, value)) return;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user