More accurate emulation of NR10 writes

This commit is contained in:
Lior Halphon 2021-01-08 16:43:00 +02:00
parent 29a3b18186
commit c0582fd994
5 changed files with 52 additions and 24 deletions

View File

@ -394,6 +394,7 @@ static void trigger_sweep_calculation(GB_gameboy_t *gb)
/* Recalculation and overflow check only occurs after a delay */
gb->apu.square_sweep_calculate_countdown = (gb->io_registers[GB_IO_NR10] & 0x7) * 2 + 5 - gb->apu.lf_div;
gb->apu.enable_zombie_calculate_stepping = false;
gb->apu.unshifted_sweep = !(gb->io_registers[GB_IO_NR10] & 0x7);
gb->apu.square_sweep_countdown = ((gb->io_registers[GB_IO_NR10] >> 4) & 7) ^ 7;
}
@ -501,31 +502,31 @@ void GB_apu_run(GB_gameboy_t *gb)
uint8_t cycles = gb->apu.apu_cycles >> 2;
gb->apu.apu_cycles = 0;
if (!cycles) return;
bool start_ch4 = false;
if (gb->apu.channel_4_dmg_delayed_start) {
if (gb->apu.channel_4_dmg_delayed_start == cycles) {
gb->apu.channel_4_dmg_delayed_start = 0;
start_ch4 = true;
}
else if (gb->apu.channel_4_dmg_delayed_start > cycles) {
gb->apu.channel_4_dmg_delayed_start -= cycles;
}
else {
/* Split it into two */
cycles -= gb->apu.channel_4_dmg_delayed_start;
gb->apu.apu_cycles = gb->apu.channel_4_dmg_delayed_start * 2;
GB_apu_run(gb);
}
}
if (likely(!gb->stopped || GB_is_cgb(gb))) {
if (gb->apu.channel_4_dmg_delayed_start) {
if (gb->apu.channel_4_dmg_delayed_start == cycles) {
gb->apu.channel_4_dmg_delayed_start = 0;
start_ch4 = true;
}
else if (gb->apu.channel_4_dmg_delayed_start > cycles) {
gb->apu.channel_4_dmg_delayed_start -= cycles;
}
else {
/* Split it into two */
cycles -= gb->apu.channel_4_dmg_delayed_start;
gb->apu.apu_cycles = gb->apu.channel_4_dmg_delayed_start * 2;
GB_apu_run(gb);
}
}
/* To align the square signal to 1MHz */
gb->apu.lf_div ^= cycles & 1;
gb->apu.noise_channel.alignment += cycles;
if (gb->apu.square_sweep_calculate_countdown &&
(((gb->io_registers[GB_IO_NR10] & 7) || gb->apu.unshifted_sweep) ||
gb->apu.square_sweep_calculate_countdown <= (gb->model > GB_MODEL_CGB_C? 3 : 1))) { // Calculation is paused if the lower bits
gb->apu.square_sweep_calculate_countdown <= 3)) { // Calculation is paused if the lower bits are 0
if (gb->apu.square_sweep_calculate_countdown > cycles) {
gb->apu.square_sweep_calculate_countdown -= cycles;
}
@ -541,6 +542,8 @@ void GB_apu_run(GB_gameboy_t *gb)
gb->apu.is_active[GB_SQUARE_1] = false;
update_sample(gb, GB_SQUARE_1, 0, gb->apu.square_sweep_calculate_countdown - cycles);
}
gb->apu.channel1_completed_addend = gb->apu.sweep_length_addend;
gb->apu.square_sweep_calculate_countdown = 0;
}
}
@ -643,6 +646,7 @@ void GB_apu_run(GB_gameboy_t *gb)
GB_apu_write(gb, GB_IO_NR44, gb->io_registers[GB_IO_NR44] | 0x80);
}
}
void GB_apu_init(GB_gameboy_t *gb)
{
memset(&gb->apu, 0, sizeof(gb->apu));
@ -769,8 +773,7 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
case GB_IO_NR10:{
bool old_negate = gb->io_registers[GB_IO_NR10] & 8;
gb->io_registers[GB_IO_NR10] = value;
if (gb->apu.square_sweep_calculate_countdown == 0 &&
gb->apu.shadow_sweep_sample_length + gb->apu.sweep_length_addend + old_negate > 0x7FF &&
if (gb->apu.shadow_sweep_sample_length + gb->apu.channel1_completed_addend + old_negate > 0x7FF &&
!(value & 8)) {
gb->apu.is_active[GB_SQUARE_1] = false;
update_sample(gb, GB_SQUARE_1, 0, 0);
@ -886,11 +889,13 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
if (index == GB_SQUARE_1) {
gb->apu.shadow_sweep_sample_length = 0;
gb->apu.channel1_completed_addend = 0;
if (gb->io_registers[GB_IO_NR10] & 7) {
/* APU bug: if shift is nonzero, overflow check also occurs on trigger */
gb->apu.square_sweep_calculate_countdown = (gb->io_registers[GB_IO_NR10] & 0x7) * 2 + 5 - gb->apu.lf_div;
gb->apu.enable_zombie_calculate_stepping = false;
gb->apu.unshifted_sweep = false;
if (gb->model > GB_MODEL_CGB_C && !was_active) {
if (!was_active) {
gb->apu.square_sweep_calculate_countdown += 2;
}
gb->apu.sweep_length_addend = gb->apu.square_channels[GB_SQUARE_1].sample_length;

View File

@ -67,7 +67,7 @@ typedef struct
uint16_t sweep_length_addend;
uint16_t shadow_sweep_sample_length;
bool unshifted_sweep;
GB_PADDING(bool, sweep_decreasing);
bool enable_zombie_calculate_stepping;
struct {
uint16_t pulse_length; // Reloaded from NRX1 (xorred), in 256Hz DIV ticks
@ -125,6 +125,7 @@ typedef struct
int8_t channel_4_delta;
bool channel_4_countdown_reloaded;
uint8_t channel_4_dmg_delayed_start;
uint16_t channel1_completed_addend;
} GB_apu_t;
typedef enum {

View File

@ -1111,6 +1111,7 @@ bool GB_serial_get_data_bit(GB_gameboy_t *gb)
}
return gb->io_registers[GB_IO_SB] & 0x80;
}
void GB_serial_set_data_bit(GB_gameboy_t *gb, bool data)
{
if (gb->io_registers[GB_IO_SC] & 1) {

View File

@ -139,7 +139,7 @@ void GB_configure_cart(GB_gameboy_t *gb)
gb->mbc_ram = malloc(gb->mbc_ram_size);
}
/* Todo: Some games assume unintialized MBC RAM is 0xFF. It this true for all cartridges types? */
/* Todo: Some games assume unintialized MBC RAM is 0xFF. It this true for all cartridge types? */
memset(gb->mbc_ram, 0xFF, gb->mbc_ram_size);
}

View File

@ -22,6 +22,7 @@ typedef enum {
GB_CONFLICT_SGB_LCDC,
GB_CONFLICT_WX,
GB_CONFLICT_CGB_LCDC,
GB_CONFLICT_NR10,
} GB_conflict_t;
/* Todo: How does double speed mode affect these? */
@ -33,6 +34,7 @@ static const GB_conflict_t cgb_conflict_map[0x80] = {
[GB_IO_BGP] = GB_CONFLICT_PALETTE_CGB,
[GB_IO_OBP0] = GB_CONFLICT_PALETTE_CGB,
[GB_IO_OBP1] = GB_CONFLICT_PALETTE_CGB,
[GB_IO_NR10] = GB_CONFLICT_NR10,
/* Todo: most values not verified, and probably differ between revisions */
};
@ -50,7 +52,8 @@ static const GB_conflict_t dmg_conflict_map[0x80] = {
[GB_IO_OBP1] = GB_CONFLICT_PALETTE_DMG,
[GB_IO_WY] = GB_CONFLICT_READ_OLD,
[GB_IO_WX] = GB_CONFLICT_WX,
[GB_IO_NR10] = GB_CONFLICT_NR10,
/* Todo: these were not verified at all */
[GB_IO_SCX] = GB_CONFLICT_READ_NEW,
};
@ -68,7 +71,8 @@ static const GB_conflict_t sgb_conflict_map[0x80] = {
[GB_IO_OBP1] = GB_CONFLICT_READ_NEW,
[GB_IO_WY] = GB_CONFLICT_READ_OLD,
[GB_IO_WX] = GB_CONFLICT_WX,
[GB_IO_NR10] = GB_CONFLICT_NR10,
/* Todo: these were not verified at all */
[GB_IO_SCX] = GB_CONFLICT_READ_NEW,
};
@ -272,6 +276,23 @@ static void cycle_write(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
gb->pending_cycles = 4;
}
return;
case GB_CONFLICT_NR10:
/* Hack: Due to the coupling between DIV and the APU, GB_apu_run only runs at M-cycle
resolutions, but this quirk requires 2MHz even in single speed mode. To work
around this, we specifically just step the calculate countdown if needed. */
GB_advance_cycles(gb, gb->pending_cycles);
if (gb->model <= GB_MODEL_CGB_C) {
// TODO: Double speed mode? This logic is also a bit weird, it needs more tests
if (gb->apu.square_sweep_calculate_countdown > 3 && gb->apu.enable_zombie_calculate_stepping) {
gb->apu.square_sweep_calculate_countdown -= 2;
}
gb->apu.enable_zombie_calculate_stepping = true;
GB_write_memory(gb, addr, 0xFF);
}
GB_write_memory(gb, addr, value);
gb->pending_cycles = 4;
return;
}
}