More accurate emulation of NR10 writes
This commit is contained in:
parent
29a3b18186
commit
c0582fd994
45
Core/apu.c
45
Core/apu.c
@ -394,6 +394,7 @@ static void trigger_sweep_calculation(GB_gameboy_t *gb)
|
|||||||
|
|
||||||
/* Recalculation and overflow check only occurs after a delay */
|
/* 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.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.unshifted_sweep = !(gb->io_registers[GB_IO_NR10] & 0x7);
|
||||||
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;
|
||||||
}
|
}
|
||||||
@ -501,31 +502,31 @@ void GB_apu_run(GB_gameboy_t *gb)
|
|||||||
uint8_t cycles = gb->apu.apu_cycles >> 2;
|
uint8_t cycles = gb->apu.apu_cycles >> 2;
|
||||||
gb->apu.apu_cycles = 0;
|
gb->apu.apu_cycles = 0;
|
||||||
if (!cycles) return;
|
if (!cycles) return;
|
||||||
|
|
||||||
bool start_ch4 = false;
|
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 (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 */
|
/* To align the square signal to 1MHz */
|
||||||
gb->apu.lf_div ^= cycles & 1;
|
gb->apu.lf_div ^= cycles & 1;
|
||||||
gb->apu.noise_channel.alignment += cycles;
|
gb->apu.noise_channel.alignment += cycles;
|
||||||
|
|
||||||
if (gb->apu.square_sweep_calculate_countdown &&
|
if (gb->apu.square_sweep_calculate_countdown &&
|
||||||
(((gb->io_registers[GB_IO_NR10] & 7) || gb->apu.unshifted_sweep) ||
|
(((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) {
|
if (gb->apu.square_sweep_calculate_countdown > cycles) {
|
||||||
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;
|
gb->apu.is_active[GB_SQUARE_1] = false;
|
||||||
update_sample(gb, GB_SQUARE_1, 0, gb->apu.square_sweep_calculate_countdown - cycles);
|
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;
|
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);
|
GB_apu_write(gb, GB_IO_NR44, gb->io_registers[GB_IO_NR44] | 0x80);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GB_apu_init(GB_gameboy_t *gb)
|
void GB_apu_init(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
memset(&gb->apu, 0, sizeof(gb->apu));
|
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:{
|
case GB_IO_NR10:{
|
||||||
bool old_negate = gb->io_registers[GB_IO_NR10] & 8;
|
bool old_negate = gb->io_registers[GB_IO_NR10] & 8;
|
||||||
gb->io_registers[GB_IO_NR10] = value;
|
gb->io_registers[GB_IO_NR10] = value;
|
||||||
if (gb->apu.square_sweep_calculate_countdown == 0 &&
|
if (gb->apu.shadow_sweep_sample_length + gb->apu.channel1_completed_addend + old_negate > 0x7FF &&
|
||||||
gb->apu.shadow_sweep_sample_length + gb->apu.sweep_length_addend + old_negate > 0x7FF &&
|
|
||||||
!(value & 8)) {
|
!(value & 8)) {
|
||||||
gb->apu.is_active[GB_SQUARE_1] = false;
|
gb->apu.is_active[GB_SQUARE_1] = false;
|
||||||
update_sample(gb, GB_SQUARE_1, 0, 0);
|
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) {
|
if (index == GB_SQUARE_1) {
|
||||||
gb->apu.shadow_sweep_sample_length = 0;
|
gb->apu.shadow_sweep_sample_length = 0;
|
||||||
|
gb->apu.channel1_completed_addend = 0;
|
||||||
if (gb->io_registers[GB_IO_NR10] & 7) {
|
if (gb->io_registers[GB_IO_NR10] & 7) {
|
||||||
/* APU bug: if shift is nonzero, overflow check also occurs on trigger */
|
/* 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.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;
|
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.square_sweep_calculate_countdown += 2;
|
||||||
}
|
}
|
||||||
gb->apu.sweep_length_addend = gb->apu.square_channels[GB_SQUARE_1].sample_length;
|
gb->apu.sweep_length_addend = gb->apu.square_channels[GB_SQUARE_1].sample_length;
|
||||||
|
@ -67,7 +67,7 @@ typedef struct
|
|||||||
uint16_t sweep_length_addend;
|
uint16_t sweep_length_addend;
|
||||||
uint16_t shadow_sweep_sample_length;
|
uint16_t shadow_sweep_sample_length;
|
||||||
bool unshifted_sweep;
|
bool unshifted_sweep;
|
||||||
GB_PADDING(bool, sweep_decreasing);
|
bool enable_zombie_calculate_stepping;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
uint16_t pulse_length; // Reloaded from NRX1 (xorred), in 256Hz DIV ticks
|
uint16_t pulse_length; // Reloaded from NRX1 (xorred), in 256Hz DIV ticks
|
||||||
@ -125,6 +125,7 @@ typedef struct
|
|||||||
int8_t channel_4_delta;
|
int8_t channel_4_delta;
|
||||||
bool channel_4_countdown_reloaded;
|
bool channel_4_countdown_reloaded;
|
||||||
uint8_t channel_4_dmg_delayed_start;
|
uint8_t channel_4_dmg_delayed_start;
|
||||||
|
uint16_t channel1_completed_addend;
|
||||||
} GB_apu_t;
|
} GB_apu_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -1111,6 +1111,7 @@ bool GB_serial_get_data_bit(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
return gb->io_registers[GB_IO_SB] & 0x80;
|
return gb->io_registers[GB_IO_SB] & 0x80;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GB_serial_set_data_bit(GB_gameboy_t *gb, bool data)
|
void GB_serial_set_data_bit(GB_gameboy_t *gb, bool data)
|
||||||
{
|
{
|
||||||
if (gb->io_registers[GB_IO_SC] & 1) {
|
if (gb->io_registers[GB_IO_SC] & 1) {
|
||||||
|
@ -139,7 +139,7 @@ void GB_configure_cart(GB_gameboy_t *gb)
|
|||||||
gb->mbc_ram = malloc(gb->mbc_ram_size);
|
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);
|
memset(gb->mbc_ram, 0xFF, gb->mbc_ram_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ typedef enum {
|
|||||||
GB_CONFLICT_SGB_LCDC,
|
GB_CONFLICT_SGB_LCDC,
|
||||||
GB_CONFLICT_WX,
|
GB_CONFLICT_WX,
|
||||||
GB_CONFLICT_CGB_LCDC,
|
GB_CONFLICT_CGB_LCDC,
|
||||||
|
GB_CONFLICT_NR10,
|
||||||
} GB_conflict_t;
|
} GB_conflict_t;
|
||||||
|
|
||||||
/* Todo: How does double speed mode affect these? */
|
/* 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_BGP] = GB_CONFLICT_PALETTE_CGB,
|
||||||
[GB_IO_OBP0] = GB_CONFLICT_PALETTE_CGB,
|
[GB_IO_OBP0] = GB_CONFLICT_PALETTE_CGB,
|
||||||
[GB_IO_OBP1] = 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 */
|
/* 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_OBP1] = GB_CONFLICT_PALETTE_DMG,
|
||||||
[GB_IO_WY] = GB_CONFLICT_READ_OLD,
|
[GB_IO_WY] = GB_CONFLICT_READ_OLD,
|
||||||
[GB_IO_WX] = GB_CONFLICT_WX,
|
[GB_IO_WX] = GB_CONFLICT_WX,
|
||||||
|
[GB_IO_NR10] = GB_CONFLICT_NR10,
|
||||||
|
|
||||||
/* Todo: these were not verified at all */
|
/* Todo: these were not verified at all */
|
||||||
[GB_IO_SCX] = GB_CONFLICT_READ_NEW,
|
[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_OBP1] = GB_CONFLICT_READ_NEW,
|
||||||
[GB_IO_WY] = GB_CONFLICT_READ_OLD,
|
[GB_IO_WY] = GB_CONFLICT_READ_OLD,
|
||||||
[GB_IO_WX] = GB_CONFLICT_WX,
|
[GB_IO_WX] = GB_CONFLICT_WX,
|
||||||
|
[GB_IO_NR10] = GB_CONFLICT_NR10,
|
||||||
|
|
||||||
/* Todo: these were not verified at all */
|
/* Todo: these were not verified at all */
|
||||||
[GB_IO_SCX] = GB_CONFLICT_READ_NEW,
|
[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;
|
gb->pending_cycles = 4;
|
||||||
}
|
}
|
||||||
return;
|
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;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user