Add CGB-D support

This commit is contained in:
Lior Halphon 2021-10-27 01:40:28 +03:00
parent 5b9746084d
commit e6c4ceaf5a
7 changed files with 63 additions and 29 deletions

View File

@ -401,7 +401,7 @@
<menuItem title="CPU-CGB A" tag="513" enabled="NO" id="axv-yk-RWM"/> <menuItem title="CPU-CGB A" tag="513" enabled="NO" id="axv-yk-RWM"/>
<menuItem title="CPU-CGB B" tag="514" enabled="NO" id="NtJ-oo-IM2"/> <menuItem title="CPU-CGB B" tag="514" enabled="NO" id="NtJ-oo-IM2"/>
<menuItem title="CPU-CGB C (Experimental)" tag="515" id="9YL-u8-12z"/> <menuItem title="CPU-CGB C (Experimental)" tag="515" id="9YL-u8-12z"/>
<menuItem title="CPU-CGB D" tag="516" enabled="NO" id="c76-oF-fkU"/> <menuItem title="CPU-CGB D" tag="516" id="c76-oF-fkU"/>
<menuItem title="CPU-CGB E" state="on" tag="517" id="3lF-1Q-2SS"/> <menuItem title="CPU-CGB E" state="on" tag="517" id="3lF-1Q-2SS"/>
</items> </items>
</menu> </menu>

View File

@ -901,7 +901,6 @@ static inline uint16_t effective_channel4_counter(GB_gameboy_t *gb)
effective_counter |= 0x20; effective_counter |= 0x20;
} }
break; break;
#if 0
case GB_MODEL_CGB_D: case GB_MODEL_CGB_D:
if (effective_counter & ((gb->io_registers[GB_IO_NR43] & 8)? 0x40 : 0x80)) { // This is so weird if (effective_counter & ((gb->io_registers[GB_IO_NR43] & 8)? 0x40 : 0x80)) { // This is so weird
effective_counter |= 0xFF; effective_counter |= 0xFF;
@ -922,7 +921,6 @@ static inline uint16_t effective_channel4_counter(GB_gameboy_t *gb)
effective_counter |= 0x10; effective_counter |= 0x10;
} }
break; break;
#endif
case GB_MODEL_CGB_E: case GB_MODEL_CGB_E:
if (effective_counter & ((gb->io_registers[GB_IO_NR43] & 8)? 0x40 : 0x80)) { // This is so weird if (effective_counter & ((gb->io_registers[GB_IO_NR43] & 8)? 0x40 : 0x80)) { // This is so weird
effective_counter |= 0xFF; effective_counter |= 0xFF;
@ -1067,7 +1065,7 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
if ((value & 0x80) == 0 && gb->apu.is_active[index]) { if ((value & 0x80) == 0 && gb->apu.is_active[index]) {
/* On an AGB, as well as on CGB C and earlier (TODO: Tested: 0, B and C), it behaves slightly different on /* On an AGB, as well as on CGB C and earlier (TODO: Tested: 0, B and C), it behaves slightly different on
double speed. */ double speed. */
if (gb->model == GB_MODEL_CGB_E /* || gb->model == GB_MODEL_CGB_D */ || gb->apu.square_channels[index].sample_countdown & 1) { if (gb->model == GB_MODEL_CGB_E || gb->model == GB_MODEL_CGB_D || gb->apu.square_channels[index].sample_countdown & 1) {
if (gb->apu.square_channels[index].sample_countdown >> 1 == (gb->apu.square_channels[index].sample_length ^ 0x7FF)) { if (gb->apu.square_channels[index].sample_countdown >> 1 == (gb->apu.square_channels[index].sample_length ^ 0x7FF)) {
gb->apu.square_channels[index].current_sample_index--; gb->apu.square_channels[index].current_sample_index--;
gb->apu.square_channels[index].current_sample_index &= 7; gb->apu.square_channels[index].current_sample_index &= 7;
@ -1091,7 +1089,7 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
} }
else { else {
unsigned extra_delay = 0; unsigned extra_delay = 0;
if (gb->model == GB_MODEL_CGB_E /* || gb->model == GB_MODEL_CGB_D */) { if (gb->model == GB_MODEL_CGB_E || gb->model == GB_MODEL_CGB_D) {
if (!(value & 4) && !(((gb->apu.square_channels[index].sample_countdown - 1) / 2) & 0x400)) { if (!(value & 4) && !(((gb->apu.square_channels[index].sample_countdown - 1) / 2) & 0x400)) {
gb->apu.square_channels[index].current_sample_index++; gb->apu.square_channels[index].current_sample_index++;
gb->apu.square_channels[index].current_sample_index &= 0x7; gb->apu.square_channels[index].current_sample_index &= 0x7;
@ -1153,10 +1151,12 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
else { else {
gb->apu.sweep_length_addend = 0; gb->apu.sweep_length_addend = 0;
} }
gb->apu.channel_1_restart_hold = 2 - gb->apu.lf_div + GB_is_cgb(gb) * 2; gb->apu.channel_1_restart_hold = 2 - gb->apu.lf_div + (GB_is_cgb(gb) && gb->model != GB_MODEL_CGB_D) * 2;
if (gb->model <= GB_MODEL_CGB_C && gb->apu.lf_div) { /*
if (GB_is_cgb(gb) && gb->model <= GB_MODEL_CGB_C && gb->apu.lf_div) {
// TODO: This if makes channel_1_sweep_restart_2 fail on CGB-C mode
gb->apu.channel_1_restart_hold += 2; gb->apu.channel_1_restart_hold += 2;
} }*/
gb->apu.square_sweep_countdown = ((gb->io_registers[GB_IO_NR10] >> 4) & 7) ^ 7; gb->apu.square_sweep_countdown = ((gb->io_registers[GB_IO_NR10] >> 4) & 7) ^ 7;
} }
} }

View File

@ -483,18 +483,29 @@ static void add_object_from_index(GB_gameboy_t *gb, unsigned index)
} }
} }
static uint8_t data_for_tile_sel_glitch(GB_gameboy_t *gb, bool *should_use) static uint8_t data_for_tile_sel_glitch(GB_gameboy_t *gb, bool *should_use, bool *cgb_d_glitch)
{ {
/* /*
Based on Matt Currie's research here: Based on Matt Currie's research here:
https://github.com/mattcurrie/mealybug-tearoom-tests/blob/master/the-comprehensive-game-boy-ppu-documentation.md#tile_sel-bit-4 https://github.com/mattcurrie/mealybug-tearoom-tests/blob/master/the-comprehensive-game-boy-ppu-documentation.md#tile_sel-bit-4
*/ */
*should_use = true; *should_use = true;
*cgb_d_glitch = false;
if (gb->io_registers[GB_IO_LCDC] & 0x10) { if (gb->io_registers[GB_IO_LCDC] & 0x10) {
if (gb->model != GB_MODEL_CGB_D) {
*should_use = !(gb->current_tile & 0x80); *should_use = !(gb->current_tile & 0x80);
/* if (gb->model != GB_MODEL_CGB_D) */ return gb->current_tile; return gb->current_tile;
// TODO: CGB D behaves differently }
*cgb_d_glitch = true;
*should_use = false;
gb->io_registers[GB_IO_LCDC] &= ~0x10;
if (gb->fetcher_state == 3) {
*should_use = false;
*cgb_d_glitch = true;
return 0;
}
return 0;
} }
return gb->data_for_sel_glitch; return gb->data_for_sel_glitch;
} }
@ -694,8 +705,9 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb)
case GB_FETCHER_GET_TILE_DATA_LOWER: { case GB_FETCHER_GET_TILE_DATA_LOWER: {
bool use_glitched = false; bool use_glitched = false;
bool cgb_d_glitch = false;
if (gb->tile_sel_glitch) { if (gb->tile_sel_glitch) {
gb->current_tile_data[0] = data_for_tile_sel_glitch(gb, &use_glitched); gb->current_tile_data[0] = data_for_tile_sel_glitch(gb, &use_glitched, &cgb_d_glitch);
} }
uint8_t y_flip = 0; uint8_t y_flip = 0;
uint16_t tile_address = 0; uint16_t tile_address = 0;
@ -721,13 +733,19 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb)
gb->current_tile_data[0] = 0xFF; gb->current_tile_data[0] = 0xFF;
} }
} }
else { if ((gb->io_registers[GB_IO_LCDC] & 0x10) && gb->tile_sel_glitch) {
gb->data_for_sel_glitch = gb->data_for_sel_glitch =
gb->vram[tile_address + ((y & 7) ^ y_flip) * 2]; gb->vram[tile_address + ((y & 7) ^ y_flip) * 2];
if (gb->vram_ppu_blocked) { if (gb->vram_ppu_blocked) {
gb->data_for_sel_glitch = 0xFF; gb->data_for_sel_glitch = 0xFF;
} }
} }
else if (cgb_d_glitch) {
gb->data_for_sel_glitch = gb->vram[gb->current_tile * 0x10 + ((y & 7) ^ y_flip) * 2];
if (gb->vram_ppu_blocked) {
gb->data_for_sel_glitch = 0xFF;
}
}
} }
gb->fetcher_state++; gb->fetcher_state++;
break; break;
@ -736,8 +754,9 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb)
/* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong. */ /* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong. */
bool use_glitched = false; bool use_glitched = false;
bool cgb_d_glitch = false;
if (gb->tile_sel_glitch) { if (gb->tile_sel_glitch) {
gb->current_tile_data[1] = data_for_tile_sel_glitch(gb, &use_glitched); gb->current_tile_data[1] = data_for_tile_sel_glitch(gb, &use_glitched, &cgb_d_glitch);
} }
uint16_t tile_address = 0; uint16_t tile_address = 0;
@ -756,7 +775,7 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb)
if (gb->current_tile_attributes & 0x40) { if (gb->current_tile_attributes & 0x40) {
y_flip = 0x7; y_flip = 0x7;
} }
gb->last_tile_data_address = tile_address + ((y & 7) ^ y_flip) * 2 + 1; gb->last_tile_data_address = tile_address + ((y & 7) ^ y_flip) * 2 + 1 - cgb_d_glitch;
if (!use_glitched) { if (!use_glitched) {
gb->current_tile_data[1] = gb->current_tile_data[1] =
gb->vram[gb->last_tile_data_address]; gb->vram[gb->last_tile_data_address];
@ -764,13 +783,17 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb)
gb->current_tile_data[1] = 0xFF; gb->current_tile_data[1] = 0xFF;
} }
} }
else {
if ((gb->io_registers[GB_IO_LCDC] & 0x10) && gb->tile_sel_glitch) { if ((gb->io_registers[GB_IO_LCDC] & 0x10) && gb->tile_sel_glitch) {
gb->data_for_sel_glitch = gb->vram[gb->last_tile_data_address]; gb->data_for_sel_glitch = gb->vram[gb->last_tile_data_address];
if (gb->vram_ppu_blocked) { if (gb->vram_ppu_blocked) {
gb->data_for_sel_glitch = 0xFF; gb->data_for_sel_glitch = 0xFF;
} }
} }
else if (cgb_d_glitch) {
gb->data_for_sel_glitch = gb->vram[gb->current_tile * 0x10 + ((y & 7) ^ y_flip) * 2 + 1];
if (gb->vram_ppu_blocked) {
gb->data_for_sel_glitch = 0xFF;
}
} }
} }
if (gb->wx_triggered) { if (gb->wx_triggered) {

View File

@ -1473,12 +1473,23 @@ static void reset_ram(GB_gameboy_t *gb)
} }
} }
break; break;
case GB_MODEL_CGB_D:
for (unsigned i = 0; i < gb->ram_size; i++) {
gb->ram[i] = GB_random();
if (i & 0x800) {
gb->ram[i] &= GB_random();
}
else {
gb->ram[i] |= GB_random();
}
}
break;
} }
/* HRAM */ /* HRAM */
switch (gb->model) { switch (gb->model) {
case GB_MODEL_CGB_C: case GB_MODEL_CGB_C:
// case GB_MODEL_CGB_D: case GB_MODEL_CGB_D:
case GB_MODEL_CGB_E: case GB_MODEL_CGB_E:
case GB_MODEL_AGB: case GB_MODEL_AGB:
for (unsigned i = 0; i < sizeof(gb->hram); i++) { for (unsigned i = 0; i < sizeof(gb->hram); i++) {
@ -1508,6 +1519,7 @@ static void reset_ram(GB_gameboy_t *gb)
/* OAM */ /* OAM */
switch (gb->model) { switch (gb->model) {
case GB_MODEL_CGB_C: case GB_MODEL_CGB_C:
case GB_MODEL_CGB_D:
case GB_MODEL_CGB_E: case GB_MODEL_CGB_E:
case GB_MODEL_AGB: case GB_MODEL_AGB:
/* Zero'd out by boot ROM anyway*/ /* Zero'd out by boot ROM anyway*/
@ -1538,6 +1550,7 @@ static void reset_ram(GB_gameboy_t *gb)
/* Wave RAM */ /* Wave RAM */
switch (gb->model) { switch (gb->model) {
case GB_MODEL_CGB_C: case GB_MODEL_CGB_C:
case GB_MODEL_CGB_D:
case GB_MODEL_CGB_E: case GB_MODEL_CGB_E:
case GB_MODEL_AGB: case GB_MODEL_AGB:
/* Initialized by CGB-A and newer, 0s in CGB-0*/ /* Initialized by CGB-A and newer, 0s in CGB-0*/
@ -1610,6 +1623,7 @@ static void request_boot_rom(GB_gameboy_t *gb)
type = GB_BOOT_ROM_SGB2; type = GB_BOOT_ROM_SGB2;
break; break;
case GB_MODEL_CGB_C: case GB_MODEL_CGB_C:
case GB_MODEL_CGB_D:
case GB_MODEL_CGB_E: case GB_MODEL_CGB_E:
type = GB_BOOT_ROM_CGB; type = GB_BOOT_ROM_CGB;
break; break;

View File

@ -134,7 +134,7 @@ typedef enum {
// GB_MODEL_CGB_A = 0x201, // GB_MODEL_CGB_A = 0x201,
// GB_MODEL_CGB_B = 0x202, // GB_MODEL_CGB_B = 0x202,
GB_MODEL_CGB_C = 0x203, GB_MODEL_CGB_C = 0x203,
// GB_MODEL_CGB_D = 0x204, GB_MODEL_CGB_D = 0x204,
GB_MODEL_CGB_E = 0x205, GB_MODEL_CGB_E = 0x205,
GB_MODEL_AGB = 0x206, GB_MODEL_AGB = 0x206,
} GB_model_t; } GB_model_t;

View File

@ -496,13 +496,11 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
case GB_MODEL_AGB: case GB_MODEL_AGB:
return (addr & 0xF0) | ((addr >> 4) & 0xF); return (addr & 0xF0) | ((addr >> 4) & 0xF);
/*
case GB_MODEL_CGB_D: case GB_MODEL_CGB_D:
if (addr > 0xfec0) { if (addr > 0xfec0) {
addr |= 0xf0; addr |= 0xf0;
} }
return gb->extra_oam[addr - 0xfea0]; return gb->extra_oam[addr - 0xfea0];
*/
case GB_MODEL_CGB_C: case GB_MODEL_CGB_C:
/* /*
@ -1005,14 +1003,12 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
gb->oam[addr & 0xFF] = value; gb->oam[addr & 0xFF] = value;
} }
switch (gb->model) { switch (gb->model) {
/*
case GB_MODEL_CGB_D: case GB_MODEL_CGB_D:
if (addr > 0xfec0) { if (addr > 0xfec0) {
addr |= 0xf0; addr |= 0xf0;
} }
gb->extra_oam[addr - 0xfea0] = value; gb->extra_oam[addr - 0xfea0] = value;
break; break;
*/
case GB_MODEL_CGB_C: case GB_MODEL_CGB_C:
/* /*
case GB_MODEL_CGB_B: case GB_MODEL_CGB_B:

View File

@ -596,6 +596,7 @@ static int save_state_internal(GB_gameboy_t *gb, virtual_file_t *file, bool appe
case GB_MODEL_CGB_C: bess_core.full_model = BE32('CCC '); break; case GB_MODEL_CGB_C: bess_core.full_model = BE32('CCC '); break;
case GB_MODEL_CGB_D: bess_core.full_model = BE32('CCD '); break;
case GB_MODEL_CGB_E: bess_core.full_model = BE32('CCE '); break; case GB_MODEL_CGB_E: bess_core.full_model = BE32('CCE '); break;
case GB_MODEL_AGB: bess_core.full_model = BE32('CA '); break; // SameBoy doesn't emulate a specific AGB revision yet case GB_MODEL_AGB: bess_core.full_model = BE32('CA '); break; // SameBoy doesn't emulate a specific AGB revision yet
} }