diff options
Diffstat (limited to 'src/devices/sound/gb.cpp')
-rw-r--r-- | src/devices/sound/gb.cpp | 940 |
1 files changed, 552 insertions, 388 deletions
diff --git a/src/devices/sound/gb.cpp b/src/devices/sound/gb.cpp index 643ecf9ec68..d8a965e0aa1 100644 --- a/src/devices/sound/gb.cpp +++ b/src/devices/sound/gb.cpp @@ -44,6 +44,7 @@ TODO: - Implement different behavior of CGB-02. - Implement different behavior of CGB-05. +- Implement different behavior of AGB-*. - Perform more tests on real hardware to figure out when the frequency counters are reloaded. - Perform more tests on real hardware to understand when changes to the noise divisor @@ -62,12 +63,12 @@ TODO: /* Represents wave duties of 12.5%, 25%, 50% and 75% */ -const int gameboy_sound_device::wave_duty_table[4][8] = +const int gameboy_sound_device::wave_duty_table[4] = { - { -1, -1, -1, -1, -1, -1, -1, 1}, - { 1, -1, -1, -1, -1, -1, -1, 1}, - { 1, -1, -1, -1, -1, 1, 1, 1}, - { -1, 1, 1, 1, 1, 1, 1, -1} + 0b10000000, + 0b10000001, + 0b11100001, + 0b01111110 }; // device type definitions @@ -75,6 +76,7 @@ DEFINE_DEVICE_TYPE(DMG_APU, dmg_apu_device, "dmg_apu", "LR35902 APU") //DEFINE_DEVICE_TYPE(CGB02_APU, cgb02_apu_device, "cgb02_apu", fullname) DEFINE_DEVICE_TYPE(CGB04_APU, cgb04_apu_device, "cgb04_apu", "CGB04 APU") //DEFINE_DEVICE_TYPE(CGB05_APU, cgb05_apu_device, "cgb05_apu", fullname) +DEFINE_DEVICE_TYPE(AGB_APU, agb_apu_device, "agb_apu", "AGB APU") //************************************************************************** // LIVE DEVICE @@ -96,50 +98,25 @@ dmg_apu_device::dmg_apu_device(const machine_config &mconfig, const char *tag, d { } +cgb04_apu_device::cgb04_apu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) + : gameboy_sound_device(mconfig, type, tag, owner, clock) +{ +} cgb04_apu_device::cgb04_apu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : gameboy_sound_device(mconfig, CGB04_APU, tag, owner, clock) + : cgb04_apu_device(mconfig, CGB04_APU, tag, owner, clock) { } +agb_apu_device::agb_apu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : cgb04_apu_device(mconfig, AGB_APU, tag, owner, clock) +{ +} //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- -#define SAVE_CHANNEL(snd) \ - save_item(NAME(snd.reg)); \ - save_item(NAME(snd.on)); \ - save_item(NAME(snd.channel)); \ - save_item(NAME(snd.length)); \ - save_item(NAME(snd.length_mask)); \ - save_item(NAME(snd.length_counting)); \ - save_item(NAME(snd.length_enabled)); \ - save_item(NAME(snd.cycles_left)); \ - save_item(NAME(snd.duty)); \ - save_item(NAME(snd.envelope_enabled)); \ - save_item(NAME(snd.envelope_value)); \ - save_item(NAME(snd.envelope_direction)); \ - save_item(NAME(snd.envelope_time)); \ - save_item(NAME(snd.envelope_count)); \ - save_item(NAME(snd.signal)); \ - save_item(NAME(snd.frequency)); \ - save_item(NAME(snd.frequency_counter)); \ - save_item(NAME(snd.sweep_enabled)); \ - save_item(NAME(snd.sweep_neg_mode_used)); \ - save_item(NAME(snd.sweep_shift)); \ - save_item(NAME(snd.sweep_direction)); \ - save_item(NAME(snd.sweep_time)); \ - save_item(NAME(snd.sweep_count)); \ - save_item(NAME(snd.level)); \ - save_item(NAME(snd.offset)); \ - save_item(NAME(snd.duty_count)); \ - save_item(NAME(snd.current_sample)); \ - save_item(NAME(snd.sample_reading)); \ - save_item(NAME(snd.noise_short)); \ - save_item(NAME(snd.noise_lfsr)); - - void gameboy_sound_device::device_start() { m_channel = stream_alloc(0, 2, SAMPLE_RATE_OUTPUT_ADAPTIVE); @@ -148,24 +125,48 @@ void gameboy_sound_device::device_start() save_item(NAME(m_last_updated)); save_item(NAME(m_snd_regs)); + save_item(NAME(m_wave_ram)); // sound control save_item(NAME(m_snd_control.on)); save_item(NAME(m_snd_control.vol_left)); save_item(NAME(m_snd_control.vol_right)); - save_item(NAME(m_snd_control.mode1_left)); - save_item(NAME(m_snd_control.mode1_right)); - save_item(NAME(m_snd_control.mode2_left)); - save_item(NAME(m_snd_control.mode2_right)); - save_item(NAME(m_snd_control.mode3_left)); - save_item(NAME(m_snd_control.mode3_right)); - save_item(NAME(m_snd_control.mode4_left)); - save_item(NAME(m_snd_control.mode4_right)); + save_item(NAME(m_snd_control.chan_left)); + save_item(NAME(m_snd_control.chan_right)); save_item(NAME(m_snd_control.cycles)); - SAVE_CHANNEL(m_snd_1); - SAVE_CHANNEL(m_snd_2); - SAVE_CHANNEL(m_snd_3); - SAVE_CHANNEL(m_snd_4); + save_item(STRUCT_MEMBER(m_snd, reg)); + save_item(STRUCT_MEMBER(m_snd, on)); + save_item(STRUCT_MEMBER(m_snd, channel)); + save_item(STRUCT_MEMBER(m_snd, length)); + save_item(STRUCT_MEMBER(m_snd, length_mask)); + save_item(STRUCT_MEMBER(m_snd, length_counting)); + save_item(STRUCT_MEMBER(m_snd, length_enabled)); + save_item(STRUCT_MEMBER(m_snd, frequency)); + save_item(STRUCT_MEMBER(m_snd, frequency_counter)); + save_item(STRUCT_MEMBER(m_snd, cycles_left)); + save_item(STRUCT_MEMBER(m_snd, duty)); + save_item(STRUCT_MEMBER(m_snd, envelope_enabled)); + save_item(STRUCT_MEMBER(m_snd, envelope_value)); + save_item(STRUCT_MEMBER(m_snd, envelope_direction)); + save_item(STRUCT_MEMBER(m_snd, envelope_time)); + save_item(STRUCT_MEMBER(m_snd, envelope_count)); + save_item(STRUCT_MEMBER(m_snd, signal)); + save_item(STRUCT_MEMBER(m_snd, frequency_shadow)); + save_item(STRUCT_MEMBER(m_snd, sweep_enabled)); + save_item(STRUCT_MEMBER(m_snd, sweep_neg_mode_used)); + save_item(STRUCT_MEMBER(m_snd, sweep_shift)); + save_item(STRUCT_MEMBER(m_snd, sweep_direction)); + save_item(STRUCT_MEMBER(m_snd, sweep_time)); + save_item(STRUCT_MEMBER(m_snd, sweep_count)); + save_item(STRUCT_MEMBER(m_snd, size)); + save_item(STRUCT_MEMBER(m_snd, bank)); + save_item(STRUCT_MEMBER(m_snd, level)); + save_item(STRUCT_MEMBER(m_snd, offset)); + save_item(STRUCT_MEMBER(m_snd, duty_count)); + save_item(STRUCT_MEMBER(m_snd, current_sample)); + save_item(STRUCT_MEMBER(m_snd, sample_reading)); + save_item(STRUCT_MEMBER(m_snd, noise_short)); + save_item(STRUCT_MEMBER(m_snd, noise_lfsr)); } @@ -185,37 +186,37 @@ void gameboy_sound_device::device_clock_changed() void gameboy_sound_device::device_reset() { - memset(&m_snd_1, 0, sizeof(m_snd_1)); - memset(&m_snd_2, 0, sizeof(m_snd_2)); - memset(&m_snd_3, 0, sizeof(m_snd_3)); - memset(&m_snd_4, 0, sizeof(m_snd_4)); - - m_snd_1.channel = 1; - m_snd_1.length_mask = 0x3f; - m_snd_2.channel = 2; - m_snd_2.length_mask = 0x3f; - m_snd_3.channel = 3; - m_snd_3.length_mask = 0xff; - m_snd_4.channel = 4; - m_snd_4.length_mask = 0x3f; + memset(&m_snd[0], 0, sizeof(m_snd[0])); + memset(&m_snd[1], 0, sizeof(m_snd[1])); + memset(&m_snd[2], 0, sizeof(m_snd[2])); + memset(&m_snd[3], 0, sizeof(m_snd[3])); + + m_snd[0].channel = 1; + m_snd[0].length_mask = 0x3f; + m_snd[1].channel = 2; + m_snd[1].length_mask = 0x3f; + m_snd[2].channel = 3; + m_snd[2].length_mask = 0xff; + m_snd[3].channel = 4; + m_snd[3].length_mask = 0x3f; sound_w_internal(NR52, 0x00); - m_snd_regs[AUD3W0] = 0xac; - m_snd_regs[AUD3W1] = 0xdd; - m_snd_regs[AUD3W2] = 0xda; - m_snd_regs[AUD3W3] = 0x48; - m_snd_regs[AUD3W4] = 0x36; - m_snd_regs[AUD3W5] = 0x02; - m_snd_regs[AUD3W6] = 0xcf; - m_snd_regs[AUD3W7] = 0x16; - m_snd_regs[AUD3W8] = 0x2c; - m_snd_regs[AUD3W9] = 0x04; - m_snd_regs[AUD3WA] = 0xe5; - m_snd_regs[AUD3WB] = 0x2c; - m_snd_regs[AUD3WC] = 0xac; - m_snd_regs[AUD3WD] = 0xdd; - m_snd_regs[AUD3WE] = 0xda; - m_snd_regs[AUD3WF] = 0x48; + m_wave_ram[0][0x0] = 0xac; + m_wave_ram[0][0x1] = 0xdd; + m_wave_ram[0][0x2] = 0xda; + m_wave_ram[0][0x3] = 0x48; + m_wave_ram[0][0x4] = 0x36; + m_wave_ram[0][0x5] = 0x02; + m_wave_ram[0][0x6] = 0xcf; + m_wave_ram[0][0x7] = 0x16; + m_wave_ram[0][0x8] = 0x2c; + m_wave_ram[0][0x9] = 0x04; + m_wave_ram[0][0xa] = 0xe5; + m_wave_ram[0][0xb] = 0x2c; + m_wave_ram[0][0xc] = 0xac; + m_wave_ram[0][0xd] = 0xdd; + m_wave_ram[0][0xe] = 0xda; + m_wave_ram[0][0xf] = 0x48; } @@ -223,24 +224,63 @@ void cgb04_apu_device::device_reset() { gameboy_sound_device::device_reset(); - m_snd_regs[AUD3W0] = 0x00; - m_snd_regs[AUD3W1] = 0xff; - m_snd_regs[AUD3W2] = 0x00; - m_snd_regs[AUD3W3] = 0xff; - m_snd_regs[AUD3W4] = 0x00; - m_snd_regs[AUD3W5] = 0xff; - m_snd_regs[AUD3W6] = 0x00; - m_snd_regs[AUD3W7] = 0xff; - m_snd_regs[AUD3W8] = 0x00; - m_snd_regs[AUD3W9] = 0xff; - m_snd_regs[AUD3WA] = 0x00; - m_snd_regs[AUD3WB] = 0xff; - m_snd_regs[AUD3WC] = 0x00; - m_snd_regs[AUD3WD] = 0xff; - m_snd_regs[AUD3WE] = 0x00; - m_snd_regs[AUD3WF] = 0xff; + m_wave_ram[0][0x0] = 0x00; + m_wave_ram[0][0x1] = 0xff; + m_wave_ram[0][0x2] = 0x00; + m_wave_ram[0][0x3] = 0xff; + m_wave_ram[0][0x4] = 0x00; + m_wave_ram[0][0x5] = 0xff; + m_wave_ram[0][0x6] = 0x00; + m_wave_ram[0][0x7] = 0xff; + m_wave_ram[0][0x8] = 0x00; + m_wave_ram[0][0x9] = 0xff; + m_wave_ram[0][0xa] = 0x00; + m_wave_ram[0][0xb] = 0xff; + m_wave_ram[0][0xc] = 0x00; + m_wave_ram[0][0xd] = 0xff; + m_wave_ram[0][0xe] = 0x00; + m_wave_ram[0][0xf] = 0xff; } +void agb_apu_device::device_reset() +{ + gameboy_sound_device::device_reset(); + + // TODO: needs verification + m_wave_ram[0][0x0] = 0x00; + m_wave_ram[0][0x1] = 0xff; + m_wave_ram[0][0x2] = 0x00; + m_wave_ram[0][0x3] = 0xff; + m_wave_ram[0][0x4] = 0x00; + m_wave_ram[0][0x5] = 0xff; + m_wave_ram[0][0x6] = 0x00; + m_wave_ram[0][0x7] = 0xff; + m_wave_ram[0][0x8] = 0x00; + m_wave_ram[0][0x9] = 0xff; + m_wave_ram[0][0xa] = 0x00; + m_wave_ram[0][0xb] = 0xff; + m_wave_ram[0][0xc] = 0x00; + m_wave_ram[0][0xd] = 0xff; + m_wave_ram[0][0xe] = 0x00; + m_wave_ram[0][0xf] = 0xff; + + m_wave_ram[1][0x0] = 0x00; + m_wave_ram[1][0x1] = 0xff; + m_wave_ram[1][0x2] = 0x00; + m_wave_ram[1][0x3] = 0xff; + m_wave_ram[1][0x4] = 0x00; + m_wave_ram[1][0x5] = 0xff; + m_wave_ram[1][0x6] = 0x00; + m_wave_ram[1][0x7] = 0xff; + m_wave_ram[1][0x8] = 0x00; + m_wave_ram[1][0x9] = 0xff; + m_wave_ram[1][0xa] = 0x00; + m_wave_ram[1][0xb] = 0xff; + m_wave_ram[1][0xc] = 0x00; + m_wave_ram[1][0xd] = 0xff; + m_wave_ram[1][0xe] = 0x00; + m_wave_ram[1][0xf] = 0xff; +} /*************************************************************************** IMPLEMENTATION @@ -253,7 +293,7 @@ TIMER_CALLBACK_MEMBER(gameboy_sound_device::timer_callback) } -void gameboy_sound_device::tick_length(struct SOUND &snd) +void gameboy_sound_device::tick_length(SOUND &snd) { if (snd.length_enabled) { @@ -267,10 +307,10 @@ void gameboy_sound_device::tick_length(struct SOUND &snd) } -int32_t gameboy_sound_device::calculate_next_sweep(struct SOUND &snd) +int32_t gameboy_sound_device::calculate_next_sweep(SOUND &snd) { snd.sweep_neg_mode_used = (snd.sweep_direction < 0); - int32_t new_frequency = snd.frequency + snd.sweep_direction * (snd.frequency >> snd.sweep_shift); + const int32_t new_frequency = snd.frequency_shadow + snd.sweep_direction * (snd.frequency_shadow >> snd.sweep_shift); if (new_frequency > 0x7ff) { @@ -281,19 +321,21 @@ int32_t gameboy_sound_device::calculate_next_sweep(struct SOUND &snd) } -void gameboy_sound_device::apply_next_sweep(struct SOUND &snd) +void gameboy_sound_device::apply_next_sweep(SOUND &snd) { - int32_t new_frequency = calculate_next_sweep(snd); + const int32_t new_frequency = calculate_next_sweep(snd); if (snd.on && snd.sweep_shift > 0) { snd.frequency = new_frequency; + snd.frequency_shadow = snd.frequency; snd.reg[3] = snd.frequency & 0xff; + snd.reg[4] = (snd.reg[4] & ~0x7) | ((snd.frequency >> 8) & 0x7); } } -void gameboy_sound_device::tick_sweep(struct SOUND &snd) +void gameboy_sound_device::tick_sweep(SOUND &snd) { snd.sweep_count = (snd.sweep_count - 1) & 0x07; if (snd.sweep_count == 0) @@ -309,7 +351,7 @@ void gameboy_sound_device::tick_sweep(struct SOUND &snd) } -void gameboy_sound_device::tick_envelope(struct SOUND &snd) +void gameboy_sound_device::tick_envelope(SOUND &snd) { if (snd.envelope_enabled) { @@ -321,7 +363,7 @@ void gameboy_sound_device::tick_envelope(struct SOUND &snd) if (snd.envelope_count) { - int8_t new_envelope_value = snd.envelope_value + snd.envelope_direction; + const int8_t new_envelope_value = snd.envelope_value + snd.envelope_direction; if (new_envelope_value >= 0 && new_envelope_value <= 15) { @@ -337,13 +379,13 @@ void gameboy_sound_device::tick_envelope(struct SOUND &snd) } -bool gameboy_sound_device::dac_enabled(struct SOUND &snd) +bool gameboy_sound_device::dac_enabled(SOUND &snd) { - return (snd.channel != 3) ? snd.reg[2] & 0xf8 : snd.reg[0] & 0x80; + return (snd.channel != 3) ? (snd.reg[2] & 0xf8) : (snd.reg[0] & 0x80); } -void gameboy_sound_device::update_square_channel(struct SOUND &snd, uint64_t cycles) +void gameboy_sound_device::update_square_channel(SOUND &snd, uint64_t cycles) { if (snd.on) { @@ -354,15 +396,15 @@ void gameboy_sound_device::update_square_channel(struct SOUND &snd, uint64_t cyc cycles = snd.cycles_left >> 2; snd.cycles_left &= 3; - uint16_t distance = 0x800 - snd.frequency_counter; + uint16_t distance = 0x800 - snd.frequency_counter; if (cycles >= distance) { cycles -= distance; distance = 0x800 - snd.frequency; - uint64_t counter = 1 + cycles / distance; + const uint64_t counter = 1 + cycles / distance; snd.duty_count = (snd.duty_count + counter) & 0x07; - snd.signal = wave_duty_table[snd.duty][snd.duty_count]; + snd.signal = BIT(wave_duty_table[snd.duty], snd.duty_count); snd.frequency_counter = snd.frequency + cycles % distance; } @@ -374,13 +416,14 @@ void gameboy_sound_device::update_square_channel(struct SOUND &snd, uint64_t cyc } -void dmg_apu_device::update_wave_channel(struct SOUND &snd, uint64_t cycles) +void dmg_apu_device::update_wave_channel(SOUND &snd, uint64_t cycles) { if (snd.on) { // compensate for leftover cycles snd.cycles_left += cycles; + const uint8_t level = snd.level & 3; while (snd.cycles_left >= 2) { snd.cycles_left -= 2; @@ -396,24 +439,23 @@ void dmg_apu_device::update_wave_channel(struct SOUND &snd, uint64_t cycles) { // Read next sample snd.sample_reading = true; - snd.current_sample = m_snd_regs[AUD3W0 + (snd.offset/2)]; + snd.current_sample = m_wave_ram[0][(snd.offset / 2)]; if (!(snd.offset & 0x01)) { snd.current_sample >>= 4; } - snd.current_sample = (snd.current_sample & 0x0f) - 8; - - snd.signal = snd.level ? snd.current_sample / (1 << (snd.level - 1)) : 0; + snd.current_sample &= 0x0f; // Reload frequency counter snd.frequency_counter = snd.frequency; } } + snd.signal = level ? (snd.current_sample >> (level - 1)) : 0; } } -void cgb04_apu_device::update_wave_channel(struct SOUND &snd, uint64_t cycles) +void cgb04_apu_device::update_wave_channel(SOUND &snd, uint64_t cycles) { if (snd.on) { @@ -422,27 +464,27 @@ void cgb04_apu_device::update_wave_channel(struct SOUND &snd, uint64_t cycles) if (snd.cycles_left <= 0) return; - cycles = (snd.cycles_left >> 1); + cycles = snd.cycles_left >> 1; snd.cycles_left &= 1; - uint16_t distance = 0x800 - snd.frequency_counter; + uint16_t distance = 0x800 - snd.frequency_counter; + const uint8_t level = snd.level & 3; if (cycles >= distance) { cycles -= distance; distance = 0x800 - snd.frequency; // How many times the condition snd.frequency_counter == 0 is true - uint64_t counter = 1 + cycles / distance; + const uint64_t counter = 1 + cycles / distance; snd.offset = (snd.offset + counter) & 0x1f; - snd.current_sample = m_snd_regs[AUD3W0 + snd.offset / 2]; + snd.current_sample = m_wave_ram[0][snd.offset / 2]; if (!(snd.offset & 1)) { snd.current_sample >>= 4; } - snd.current_sample = (snd.current_sample & 0x0f) - 8; - snd.signal = snd.level ? snd.current_sample / (1 << (snd.level - 1)) : 0; + snd.current_sample &= 0x0f; cycles %= distance; - snd.sample_reading = cycles ? false : true; + snd.sample_reading = !cycles; snd.frequency_counter = snd.frequency + cycles; } @@ -450,27 +492,71 @@ void cgb04_apu_device::update_wave_channel(struct SOUND &snd, uint64_t cycles) { snd.frequency_counter += cycles; } + snd.signal = level ? (snd.current_sample >> (level - 1)) : 0; } } +void agb_apu_device::update_wave_channel(SOUND &snd, uint64_t cycles) +{ + if (snd.on) + { + constexpr uint8_t level_table[8] = { 0, 4, 2, 1, 3, 3, 3, 3 }; -void gameboy_sound_device::update_noise_channel(struct SOUND &snd, uint64_t cycles) + // compensate for left over cycles + snd.cycles_left += cycles; + if (snd.cycles_left <= 0) + return; + + cycles = (snd.cycles_left >> 1); + snd.cycles_left &= 1; + uint16_t distance = 0x800 - snd.frequency_counter; + const uint8_t level = level_table[snd.level]; + if (cycles >= distance) + { + cycles -= distance; + distance = 0x800 - snd.frequency; + // How many times the condition snd.frequency_counter == 0 is true + const uint64_t counter = 1 + cycles / distance; + + snd.offset = (snd.offset + counter) & 0x3f; + const uint8_t bank = snd.size ? BIT(snd.offset, 5) : snd.bank; + snd.current_sample = m_wave_ram[bank][(snd.offset / 2) & 0xf]; + if (!(snd.offset & 1)) + { + snd.current_sample >>= 4; + } + snd.current_sample &= 0x0f; + + cycles %= distance; + snd.sample_reading = !cycles; + + snd.frequency_counter = snd.frequency + cycles; + } + else + { + snd.frequency_counter += cycles; + } + snd.signal = level ? ((snd.current_sample * level) / 4) : 0; + } +} + +void gameboy_sound_device::update_noise_channel(SOUND &snd, uint64_t cycles) { snd.cycles_left += cycles; - uint64_t period = noise_period_cycles(); + const uint64_t period = noise_period_cycles(); while (snd.cycles_left >= period) { snd.cycles_left -= period; // Using a Polynomial Counter (aka Linear Feedback Shift Register) - // Mode 4 has a 15 bit counter so we need to shift the bits around accordingly. - uint16_t feedback = ((snd.noise_lfsr >> 1) ^ snd.noise_lfsr) & 1; + // Channel 4 has a 15 bit counter so we need to shift the bits around accordingly. + const uint16_t feedback = ((snd.noise_lfsr >> 1) ^ snd.noise_lfsr) & 1; snd.noise_lfsr = (snd.noise_lfsr >> 1) | (feedback << 14); if (snd.noise_short) { snd.noise_lfsr = (snd.noise_lfsr & ~(1 << 6)) | (feedback << 6); } - snd.signal = (snd.noise_lfsr & 1) ? -1 : 1; + snd.signal = BIT(~snd.noise_lfsr, 0); } } @@ -489,7 +575,7 @@ void gameboy_sound_device::update_state() { uint64_t cycles = attotime_to_clocks(now - m_last_updated); - uint64_t old_cycles = m_snd_control.cycles; + const uint64_t old_cycles = m_snd_control.cycles; m_snd_control.cycles += cycles; if ((old_cycles / FRAME_CYCLES) != (m_snd_control.cycles / FRAME_CYCLES)) @@ -497,10 +583,10 @@ void gameboy_sound_device::update_state() // Left over cycles in current frame uint64_t cycles_current_frame = FRAME_CYCLES - (old_cycles & (FRAME_CYCLES - 1)); - update_square_channel(m_snd_1, cycles_current_frame); - update_square_channel(m_snd_2, cycles_current_frame); - update_wave_channel(m_snd_3, cycles_current_frame); - update_noise_channel(m_snd_4, cycles_current_frame); + update_square_channel(m_snd[0], cycles_current_frame); + update_square_channel(m_snd[1], cycles_current_frame); + update_wave_channel(m_snd[2], cycles_current_frame); + update_noise_channel(m_snd[3], cycles_current_frame); cycles -= cycles_current_frame; @@ -509,49 +595,49 @@ void gameboy_sound_device::update_state() { case 0: // length - tick_length(m_snd_1); - tick_length(m_snd_2); - tick_length(m_snd_3); - tick_length(m_snd_4); + tick_length(m_snd[0]); + tick_length(m_snd[1]); + tick_length(m_snd[2]); + tick_length(m_snd[3]); break; case 2: // sweep - tick_sweep(m_snd_1); + tick_sweep(m_snd[0]); // length - tick_length(m_snd_1); - tick_length(m_snd_2); - tick_length(m_snd_3); - tick_length(m_snd_4); + tick_length(m_snd[0]); + tick_length(m_snd[1]); + tick_length(m_snd[2]); + tick_length(m_snd[3]); break; case 4: // length - tick_length(m_snd_1); - tick_length(m_snd_2); - tick_length(m_snd_3); - tick_length(m_snd_4); + tick_length(m_snd[0]); + tick_length(m_snd[1]); + tick_length(m_snd[2]); + tick_length(m_snd[3]); break; case 6: // sweep - tick_sweep(m_snd_1); + tick_sweep(m_snd[0]); // length - tick_length(m_snd_1); - tick_length(m_snd_2); - tick_length(m_snd_3); - tick_length(m_snd_4); + tick_length(m_snd[0]); + tick_length(m_snd[1]); + tick_length(m_snd[2]); + tick_length(m_snd[3]); break; case 7: // update envelope - tick_envelope(m_snd_1); - tick_envelope(m_snd_2); - tick_envelope(m_snd_4); + tick_envelope(m_snd[0]); + tick_envelope(m_snd[1]); + tick_envelope(m_snd[3]); break; } } - update_square_channel(m_snd_1, cycles); - update_square_channel(m_snd_2, cycles); - update_wave_channel(m_snd_3, cycles); - update_noise_channel(m_snd_4, cycles); + update_square_channel(m_snd[0], cycles); + update_square_channel(m_snd[1], cycles); + update_wave_channel(m_snd[2], cycles); + update_noise_channel(m_snd[3], cycles); } m_last_updated = now; @@ -561,7 +647,7 @@ void gameboy_sound_device::update_state() uint64_t gameboy_sound_device::noise_period_cycles() { static const int divisor[8] = { 8, 16,32, 48, 64, 80, 96, 112 }; - return divisor[m_snd_4.reg[3] & 7] << (m_snd_4.reg[3] >> 4); + return divisor[m_snd[3].reg[3] & 7] << (m_snd[3].reg[3] >> 4); } @@ -570,31 +656,45 @@ u8 dmg_apu_device::wave_r(offs_t offset) m_channel->update(); update_state(); - if (m_snd_3.on) + if (m_snd[2].on) { - return m_snd_3.sample_reading ? m_snd_regs[AUD3W0 + (m_snd_3.offset/2)] : 0xff; + return m_snd[2].sample_reading ? m_wave_ram[0][(m_snd[2].offset / 2)] : 0xff; } - return m_snd_regs[AUD3W0 + offset]; + return m_wave_ram[0][offset]; } - u8 cgb04_apu_device::wave_r(offs_t offset) { m_channel->update(); update_state(); - if (m_snd_3.on) + if (m_snd[2].on) { - return m_snd_regs[AUD3W0 + (m_snd_3.offset/2)]; + return m_wave_ram[0][(m_snd[2].offset / 2)]; } - return m_snd_regs[AUD3W0 + offset]; + return m_wave_ram[0][offset]; } +u8 agb_apu_device::wave_r(offs_t offset) +{ + m_channel->update(); + update_state(); + + if (m_snd[2].on) + { + return 0xff; + } + + return m_wave_ram[m_snd[2].bank ^ 1][offset & 0xf]; +} u8 gameboy_sound_device::sound_r(offs_t offset) { + if ((offset >= AUD3W0) && (offset <= AUD3WF)) + return wave_r(offset - AUD3W0); + static const uint8_t read_mask[0x40] = { 0x80,0x3f,0x00,0xff,0xbf,0xff,0x3f,0x00,0xff,0xbf,0x7f,0xff,0x9f,0xff,0xbf,0xff, @@ -611,7 +711,7 @@ u8 gameboy_sound_device::sound_r(offs_t offset) { if (offset == NR52) { - return (m_snd_regs[NR52]&0xf0) | (m_snd_1.on ? 1 : 0) | (m_snd_2.on ? 2 : 0) | (m_snd_3.on ? 4 : 0) | (m_snd_4.on ? 8 : 0) | 0x70; + return (m_snd_regs[NR52] & 0xf0) | (m_snd[0].on ? 1 : 0) | (m_snd[1].on ? 2 : 0) | (m_snd[2].on ? 4 : 0) | (m_snd[3].on ? 8 : 0) | 0x70; } return m_snd_regs[offset] | read_mask[offset & 0x3f]; } @@ -621,22 +721,50 @@ u8 gameboy_sound_device::sound_r(offs_t offset) } } +u8 agb_apu_device::sound_r(offs_t offset) +{ + if ((offset >= AUD3W0) && (offset <= AUD3WF)) + return wave_r(offset - AUD3W0); + + static constexpr uint8_t read_mask[0x40] = { + 0x80, 0x3f, 0x00, 0xff, 0xbf, 0xff, 0x3f, 0x00, 0xff, 0xbf, 0x1f, 0xff, 0x1f, 0xff, 0xbf, 0xff, + 0xff, 0x00, 0x00, 0xbf, 0x00, 0x00, 0x70, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + // Make sure we are up to date. + m_channel->update(); + update_state(); + + if (m_snd_control.on) + { + if (offset == NR52) + { + return (m_snd_regs[NR52] & 0xf0) | (m_snd[0].on ? 1 : 0) | (m_snd[1].on ? 2 : 0) | (m_snd[2].on ? 4 : 0) | (m_snd[3].on ? 8 : 0) | 0x70; + } + return m_snd_regs[offset] | read_mask[offset & 0x3f]; + } + else + { + return read_mask[offset & 0x3f]; + } +} void dmg_apu_device::wave_w(offs_t offset, u8 data) { m_channel->update(); update_state(); - if (m_snd_3.on) + if (m_snd[2].on) { - if (m_snd_3.sample_reading) + if (m_snd[2].sample_reading) { - m_snd_regs[AUD3W0 + (m_snd_3.offset/2)] = data; + m_wave_ram[0][(m_snd[2].offset / 2)] = data; } } else { - m_snd_regs[AUD3W0 + offset] = data; + m_wave_ram[0][offset] = data; } } @@ -646,16 +774,26 @@ void cgb04_apu_device::wave_w(offs_t offset, u8 data) m_channel->update(); update_state(); - if (m_snd_3.on) + if (m_snd[2].on) { - m_snd_regs[AUD3W0 + (m_snd_3.offset/2)] = data; + m_wave_ram[0][(m_snd[2].offset / 2)] = data; } else { - m_snd_regs[AUD3W0 + offset] = data; + m_wave_ram[0][offset] = data; } } +void agb_apu_device::wave_w(offs_t offset, u8 data) +{ + m_channel->update(); + update_state(); + + if (!m_snd[2].on) + { + m_wave_ram[m_snd[2].bank ^ 1][offset & 0xf] = data; + } +} void dmg_apu_device::sound_w(offs_t offset, u8 data) { @@ -687,15 +825,15 @@ void cgb04_apu_device::sound_w(offs_t offset, u8 data) void dmg_apu_device::corrupt_wave_ram() { - if (m_snd_3.offset < 8) + if (m_snd[2].offset < 4) { - m_snd_regs[AUD3W0] = m_snd_regs[AUD3W0 + (m_snd_3.offset/2)]; + m_wave_ram[0][0x0] = m_wave_ram[0][(m_snd[2].offset / 2)]; } else { for (int i = 0; i < 4; i++) { - m_snd_regs[AUD3W0 + i] = m_snd_regs[AUD3W0 + ((m_snd_3.offset / 2) & ~0x03) + i]; + m_wave_ram[0][i] = m_wave_ram[0][((m_snd[2].offset / 2) & ~0x03) + i]; } } } @@ -704,7 +842,7 @@ void dmg_apu_device::corrupt_wave_ram() void gameboy_sound_device::sound_w_internal( int offset, uint8_t data ) { /* Store the value */ - uint8_t old_data = m_snd_regs[offset]; + const uint8_t old_data = m_snd_regs[offset]; if (m_snd_control.on) { @@ -715,94 +853,95 @@ void gameboy_sound_device::sound_w_internal( int offset, uint8_t data ) { /*MODE 1 */ case NR10: /* Sweep (R/W) */ - m_snd_1.reg[0] = data; - m_snd_1.sweep_shift = data & 0x7; - m_snd_1.sweep_direction = (data & 0x8) ? -1 : 1; - m_snd_1.sweep_time = (data & 0x70) >> 4; - if ((old_data & 0x08) && !(data & 0x08) && m_snd_1.sweep_neg_mode_used) + m_snd[0].reg[0] = data; + m_snd[0].sweep_shift = data & 0x7; + m_snd[0].sweep_direction = BIT(data, 3) ? -1 : 1; + m_snd[0].sweep_time = (data & 0x70) >> 4; + if (BIT(old_data, 3) && BIT(~data, 3) && m_snd[0].sweep_neg_mode_used) { - m_snd_1.on = false; + m_snd[0].on = false; } break; case NR11: /* Sound length/Wave pattern duty (R/W) */ - m_snd_1.reg[1] = data; + m_snd[0].reg[1] = data; if (m_snd_control.on) { - m_snd_1.duty = (data & 0xc0) >> 6; + m_snd[0].duty = (data & 0xc0) >> 6; } - m_snd_1.length = data & 0x3f; - m_snd_1.length_counting = true; + m_snd[0].length = data & 0x3f; + m_snd[0].length_counting = true; break; case NR12: /* Envelope (R/W) */ - m_snd_1.reg[2] = data; - m_snd_1.envelope_value = data >> 4; - m_snd_1.envelope_direction = (data & 0x8) ? 1 : -1; - m_snd_1.envelope_time = data & 0x07; - if (!dac_enabled(m_snd_1)) + m_snd[0].reg[2] = data; + m_snd[0].envelope_value = data >> 4; + m_snd[0].envelope_direction = BIT(data, 3) ? 1 : -1; + m_snd[0].envelope_time = data & 0x07; + if (!dac_enabled(m_snd[0])) { - m_snd_1.on = false; + m_snd[0].on = false; } break; case NR13: /* Frequency lo (R/W) */ - m_snd_1.reg[3] = data; + m_snd[0].reg[3] = data; // Only enabling the frequency line breaks blarggs's sound test #5 // This condition may not be correct - if (!m_snd_1.sweep_enabled) + if (!m_snd[0].sweep_enabled) { - m_snd_1.frequency = ((m_snd_1.reg[4] & 0x7) << 8) | m_snd_1.reg[3]; + m_snd[0].frequency = ((m_snd[0].reg[4] & 0x7) << 8) | m_snd[0].reg[3]; } break; case NR14: /* Frequency hi / Initialize (R/W) */ - m_snd_1.reg[4] = data; + m_snd[0].reg[4] = data; { - bool length_was_enabled = m_snd_1.length_enabled; + const bool length_was_enabled = m_snd[0].length_enabled; - m_snd_1.length_enabled = (data & 0x40) ? true : false; - m_snd_1.frequency = ((m_snd_regs[NR14] & 0x7) << 8) | m_snd_1.reg[3]; + m_snd[0].length_enabled = BIT(data, 6); + m_snd[0].frequency = ((m_snd_regs[NR14] & 0x7) << 8) | m_snd[0].reg[3]; - if (!length_was_enabled && !(m_snd_control.cycles & FRAME_CYCLES) && m_snd_1.length_counting) + if (!length_was_enabled && !(m_snd_control.cycles & FRAME_CYCLES) && m_snd[0].length_counting) { - if (m_snd_1.length_enabled) + if (m_snd[0].length_enabled) { - tick_length(m_snd_1); + tick_length(m_snd[0]); } } - if (data & 0x80) + if (BIT(data, 7)) { - m_snd_1.on = true; - m_snd_1.envelope_enabled = true; - m_snd_1.envelope_value = m_snd_1.reg[2] >> 4; - m_snd_1.envelope_count = m_snd_1.envelope_time; - m_snd_1.sweep_count = m_snd_1.sweep_time; - m_snd_1.sweep_neg_mode_used = false; - m_snd_1.signal = 0; - m_snd_1.length_counting = true; - m_snd_1.frequency = ((m_snd_1.reg[4] & 0x7) << 8) | m_snd_1.reg[3]; - m_snd_1.frequency_counter = m_snd_1.frequency; - m_snd_1.cycles_left = 0; - m_snd_1.duty_count = 0; - m_snd_1.sweep_enabled = (m_snd_1.sweep_shift != 0) || (m_snd_1.sweep_time != 0); - if (!dac_enabled(m_snd_1)) + m_snd[0].on = true; + m_snd[0].envelope_enabled = true; + m_snd[0].envelope_value = m_snd[0].reg[2] >> 4; + m_snd[0].envelope_count = m_snd[0].envelope_time; + m_snd[0].sweep_count = m_snd[0].sweep_time; + m_snd[0].sweep_neg_mode_used = false; + m_snd[0].signal = 0; + m_snd[0].length_counting = true; + m_snd[0].frequency = ((m_snd[0].reg[4] & 0x7) << 8) | m_snd[0].reg[3]; + m_snd[0].frequency_counter = m_snd[0].frequency; + m_snd[0].frequency_shadow = m_snd[0].frequency; + m_snd[0].cycles_left = 0; + m_snd[0].duty_count = 0; + m_snd[0].sweep_enabled = (m_snd[0].sweep_shift != 0) || (m_snd[0].sweep_time != 0); + if (!dac_enabled(m_snd[0])) { - m_snd_1.on = false; + m_snd[0].on = false; } - if (m_snd_1.sweep_shift > 0) + if (m_snd[0].sweep_shift > 0) { - calculate_next_sweep(m_snd_1); + calculate_next_sweep(m_snd[0]); } - if (m_snd_1.length == 0 && m_snd_1.length_enabled && !(m_snd_control.cycles & FRAME_CYCLES)) + if (m_snd[0].length == 0 && m_snd[0].length_enabled && !(m_snd_control.cycles & FRAME_CYCLES)) { - tick_length(m_snd_1); + tick_length(m_snd[0]); } } else { // This condition may not be correct - if (!m_snd_1.sweep_enabled) + if (!m_snd[0].sweep_enabled) { - m_snd_1.frequency = ((m_snd_1.reg[4] & 0x7) << 8) | m_snd_1.reg[3]; + m_snd[0].frequency = ((m_snd[0].reg[4] & 0x7) << 8) | m_snd[0].reg[3]; } } } @@ -810,198 +949,201 @@ void gameboy_sound_device::sound_w_internal( int offset, uint8_t data ) /*MODE 2 */ case NR21: /* Sound length/Wave pattern duty (R/W) */ - m_snd_2.reg[1] = data; + m_snd[1].reg[1] = data; if (m_snd_control.on) { - m_snd_2.duty = (data & 0xc0) >> 6; + m_snd[1].duty = (data & 0xc0) >> 6; } - m_snd_2.length = data & 0x3f; - m_snd_2.length_counting = true; + m_snd[1].length = data & 0x3f; + m_snd[1].length_counting = true; break; case NR22: /* Envelope (R/W) */ - m_snd_2.reg[2] = data; - m_snd_2.envelope_value = data >> 4; - m_snd_2.envelope_direction = (data & 0x8) ? 1 : -1; - m_snd_2.envelope_time = data & 0x07; - if (!dac_enabled(m_snd_2)) + m_snd[1].reg[2] = data; + m_snd[1].envelope_value = data >> 4; + m_snd[1].envelope_direction = BIT(data, 3) ? 1 : -1; + m_snd[1].envelope_time = data & 0x07; + if (!dac_enabled(m_snd[1])) { - m_snd_2.on = false; + m_snd[1].on = false; } break; case NR23: /* Frequency lo (R/W) */ - m_snd_2.reg[3] = data; - m_snd_2.frequency = ((m_snd_2.reg[4] & 0x7) << 8) | m_snd_2.reg[3]; + m_snd[1].reg[3] = data; + m_snd[1].frequency = ((m_snd[1].reg[4] & 0x7) << 8) | m_snd[1].reg[3]; break; case NR24: /* Frequency hi / Initialize (R/W) */ - m_snd_2.reg[4] = data; + m_snd[1].reg[4] = data; { - bool length_was_enabled = m_snd_2.length_enabled; + const bool length_was_enabled = m_snd[1].length_enabled; - m_snd_2.length_enabled = (data & 0x40) ? true : false; + m_snd[1].length_enabled = BIT(data, 6); - if (!length_was_enabled && !(m_snd_control.cycles & FRAME_CYCLES) && m_snd_2.length_counting) + if (!length_was_enabled && !(m_snd_control.cycles & FRAME_CYCLES) && m_snd[1].length_counting) { - if (m_snd_2.length_enabled) + if (m_snd[1].length_enabled) { - tick_length(m_snd_2); + tick_length(m_snd[1]); } } - if (data & 0x80) + if (BIT(data, 7)) { - m_snd_2.on = true; - m_snd_2.envelope_enabled = true; - m_snd_2.envelope_value = m_snd_2.reg[2] >> 4; - m_snd_2.envelope_count = m_snd_2.envelope_time; - m_snd_2.frequency = ((m_snd_2.reg[4] & 0x7) << 8) | m_snd_2.reg[3]; - m_snd_2.frequency_counter = m_snd_2.frequency; - m_snd_2.cycles_left = 0; - m_snd_2.duty_count = 0; - m_snd_2.signal = 0; - m_snd_2.length_counting = true; - - if (!dac_enabled(m_snd_2)) + m_snd[1].on = true; + m_snd[1].envelope_enabled = true; + m_snd[1].envelope_value = m_snd[1].reg[2] >> 4; + m_snd[1].envelope_count = m_snd[1].envelope_time; + m_snd[1].frequency = ((m_snd[1].reg[4] & 0x7) << 8) | m_snd[1].reg[3]; + m_snd[1].frequency_counter = m_snd[1].frequency; + m_snd[1].cycles_left = 0; + m_snd[1].duty_count = 0; + m_snd[1].signal = 0; + m_snd[1].length_counting = true; + + if (!dac_enabled(m_snd[1])) { - m_snd_2.on = false; + m_snd[1].on = false; } - if (m_snd_2.length == 0 && m_snd_2.length_enabled && !(m_snd_control.cycles & FRAME_CYCLES)) + if (m_snd[1].length == 0 && m_snd[1].length_enabled && !(m_snd_control.cycles & FRAME_CYCLES)) { - tick_length(m_snd_2); + tick_length(m_snd[1]); } } else { - m_snd_2.frequency = ((m_snd_2.reg[4] & 0x7) << 8) | m_snd_2.reg[3]; + m_snd[1].frequency = ((m_snd[1].reg[4] & 0x7) << 8) | m_snd[1].reg[3]; } } break; /*MODE 3 */ case NR30: /* Sound On/Off (R/W) */ - m_snd_3.reg[0] = data; - if (!dac_enabled(m_snd_3)) + m_snd[2].reg[0] = data; + m_snd[2].size = BIT(data, 5); + m_snd[2].bank = BIT(data, 6); + if (!dac_enabled(m_snd[2])) { - m_snd_3.on = false; + m_snd[2].on = false; } break; case NR31: /* Sound Length (R/W) */ - m_snd_3.reg[1] = data; - m_snd_3.length = data; - m_snd_3.length_counting = true; + m_snd[2].reg[1] = data; + m_snd[2].length = data; + m_snd[2].length_counting = true; break; case NR32: /* Select Output Level */ - m_snd_3.reg[2] = data; - m_snd_3.level = (data & 0x60) >> 5; + m_snd[2].reg[2] = data; + m_snd[2].level = (data & 0xe0) >> 5; break; case NR33: /* Frequency lo (W) */ - m_snd_3.reg[3] = data; - m_snd_3.frequency = ((m_snd_3.reg[4] & 0x7) << 8) | m_snd_3.reg[3]; + m_snd[2].reg[3] = data; + m_snd[2].frequency = ((m_snd[2].reg[4] & 0x7) << 8) | m_snd[2].reg[3]; break; case NR34: /* Frequency hi / Initialize (W) */ - m_snd_3.reg[4] = data; + m_snd[2].reg[4] = data; { - bool length_was_enabled = m_snd_3.length_enabled; + const bool length_was_enabled = m_snd[2].length_enabled; - m_snd_3.length_enabled = (data & 0x40) ? true : false; + m_snd[2].length_enabled = BIT(data, 6); - if (!length_was_enabled && !(m_snd_control.cycles & FRAME_CYCLES) && m_snd_3.length_counting) + if (!length_was_enabled && !(m_snd_control.cycles & FRAME_CYCLES) && m_snd[2].length_counting) { - if (m_snd_3.length_enabled) + if (m_snd[2].length_enabled) { - tick_length(m_snd_3); + tick_length(m_snd[2]); } } - if (data & 0x80) + if (BIT(data, 7)) { - if (m_snd_3.on && m_snd_3.frequency_counter == 0x7ff) + if (m_snd[2].on && m_snd[2].frequency_counter == 0x7ff) { corrupt_wave_ram(); } - m_snd_3.on = true; - m_snd_3.offset = 0; - m_snd_3.duty = 1; - m_snd_3.duty_count = 0; - m_snd_3.length_counting = true; - m_snd_3.frequency = ((m_snd_3.reg[4] & 0x7) << 8) | m_snd_3.reg[3]; - m_snd_3.frequency_counter = m_snd_3.frequency; + m_snd[2].on = true; + m_snd[2].offset = 0; + m_snd[2].duty = 1; + m_snd[2].duty_count = 0; + m_snd[2].length_counting = true; + m_snd[2].frequency = ((m_snd[2].reg[4] & 0x7) << 8) | m_snd[2].reg[3]; + m_snd[2].frequency_counter = m_snd[2].frequency; // There is a tiny bit of delay in starting up the wave channel - m_snd_3.cycles_left = -6; - m_snd_3.sample_reading = false; + m_snd[2].cycles_left = -6; + m_snd[2].current_sample = 0; + m_snd[2].sample_reading = false; - if (!dac_enabled(m_snd_3)) + if (!dac_enabled(m_snd[2])) { - m_snd_3.on = false; + m_snd[2].on = false; } - if (m_snd_3.length == 0 && m_snd_3.length_enabled && !(m_snd_control.cycles & FRAME_CYCLES)) + if (m_snd[2].length == 0 && m_snd[2].length_enabled && !(m_snd_control.cycles & FRAME_CYCLES)) { - tick_length(m_snd_3); + tick_length(m_snd[2]); } } else { - m_snd_3.frequency = ((m_snd_3.reg[4] & 0x7) << 8) | m_snd_3.reg[3]; + m_snd[2].frequency = ((m_snd[2].reg[4] & 0x7) << 8) | m_snd[2].reg[3]; } } break; /*MODE 4 */ case NR41: /* Sound Length (R/W) */ - m_snd_4.reg[1] = data; - m_snd_4.length = data & 0x3f; - m_snd_4.length_counting = true; + m_snd[3].reg[1] = data; + m_snd[3].length = data & 0x3f; + m_snd[3].length_counting = true; break; case NR42: /* Envelope (R/W) */ - m_snd_4.reg[2] = data; - m_snd_4.envelope_value = data >> 4; - m_snd_4.envelope_direction = (data & 0x8) ? 1 : -1; - m_snd_4.envelope_time = data & 0x07; - if (!dac_enabled(m_snd_4)) + m_snd[3].reg[2] = data; + m_snd[3].envelope_value = data >> 4; + m_snd[3].envelope_direction = BIT(data, 3) ? 1 : -1; + m_snd[3].envelope_time = data & 0x07; + if (!dac_enabled(m_snd[3])) { - m_snd_4.on = false; + m_snd[3].on = false; } break; case NR43: /* Polynomial Counter/Frequency */ - m_snd_4.reg[3] = data; - m_snd_4.noise_short = (data & 0x8); + m_snd[3].reg[3] = data; + m_snd[3].noise_short = BIT(data, 3); break; case NR44: /* Counter/Consecutive / Initialize (R/W) */ - m_snd_4.reg[4] = data; + m_snd[3].reg[4] = data; { - bool length_was_enabled = m_snd_4.length_enabled; + const bool length_was_enabled = m_snd[3].length_enabled; - m_snd_4.length_enabled = (data & 0x40) ? true : false; + m_snd[3].length_enabled = BIT(data, 6); - if (!length_was_enabled && !(m_snd_control.cycles & FRAME_CYCLES) && m_snd_4.length_counting) + if (!length_was_enabled && !(m_snd_control.cycles & FRAME_CYCLES) && m_snd[3].length_counting) { - if (m_snd_4.length_enabled) + if (m_snd[3].length_enabled) { - tick_length(m_snd_4); + tick_length(m_snd[3]); } } - if (data & 0x80) + if (BIT(data, 7)) { - m_snd_4.on = true; - m_snd_4.envelope_enabled = true; - m_snd_4.envelope_value = m_snd_4.reg[2] >> 4; - m_snd_4.envelope_count = m_snd_4.envelope_time; - m_snd_4.frequency_counter = 0; - m_snd_4.cycles_left = noise_period_cycles(); - m_snd_4.signal = -1; - m_snd_4.noise_lfsr = 0x7fff; - m_snd_4.length_counting = true; - - if (!dac_enabled(m_snd_4)) + m_snd[3].on = true; + m_snd[3].envelope_enabled = true; + m_snd[3].envelope_value = m_snd[3].reg[2] >> 4; + m_snd[3].envelope_count = m_snd[3].envelope_time; + m_snd[3].frequency_counter = 0; + m_snd[3].cycles_left = noise_period_cycles(); + m_snd[3].signal = 0; + m_snd[3].noise_lfsr = 0x7fff; + m_snd[3].length_counting = true; + + if (!dac_enabled(m_snd[3])) { - m_snd_4.on = false; + m_snd[3].on = false; } - if (m_snd_4.length == 0 && m_snd_4.length_enabled && !(m_snd_control.cycles & FRAME_CYCLES)) + if (m_snd[3].length == 0 && m_snd[3].length_enabled && !(m_snd_control.cycles & FRAME_CYCLES)) { - tick_length(m_snd_4); + tick_length(m_snd[3]); } } } @@ -1013,18 +1155,18 @@ void gameboy_sound_device::sound_w_internal( int offset, uint8_t data ) m_snd_control.vol_right = (data & 0x70) >> 4; break; case NR51: /* Selection of Sound Output Terminal */ - m_snd_control.mode1_right = data & 0x1; - m_snd_control.mode1_left = (data & 0x10) >> 4; - m_snd_control.mode2_right = (data & 0x2) >> 1; - m_snd_control.mode2_left = (data & 0x20) >> 5; - m_snd_control.mode3_right = (data & 0x4) >> 2; - m_snd_control.mode3_left = (data & 0x40) >> 6; - m_snd_control.mode4_right = (data & 0x8) >> 3; - m_snd_control.mode4_left = (data & 0x80) >> 7; + m_snd_control.chan_right[0] = BIT(data, 0); + m_snd_control.chan_left[0] = BIT(data, 4); + m_snd_control.chan_right[1] = BIT(data, 1); + m_snd_control.chan_left[1] = BIT(data, 5); + m_snd_control.chan_right[2] = BIT(data, 2); + m_snd_control.chan_left[2] = BIT(data, 6); + m_snd_control.chan_right[3] = BIT(data, 3); + m_snd_control.chan_left[3] = BIT(data, 7); break; case NR52: // Sound On/Off (R/W) // Only bit 7 is writable, writing to bits 0-3 does NOT enable or disable sound. They are read-only. - if (!(data & 0x80)) + if (BIT(~data, 7)) { // On DMG the length counters are not affected and not clocked // powering off should actually clear all registers @@ -1038,9 +1180,27 @@ void gameboy_sound_device::sound_w_internal( int offset, uint8_t data ) m_snd_control.cycles |= 7 * FRAME_CYCLES; } } - m_snd_control.on = (data & 0x80) ? true : false; + m_snd_control.on = BIT(data, 7); m_snd_regs[NR52] = data & 0x80; break; + case AUD3W0: // Wavetable (R/W) + case AUD3W1: + case AUD3W2: + case AUD3W3: + case AUD3W4: + case AUD3W5: + case AUD3W6: + case AUD3W7: + case AUD3W8: + case AUD3W9: + case AUD3WA: + case AUD3WB: + case AUD3WC: + case AUD3WD: + case AUD3WE: + case AUD3WF: + wave_w(offset - AUD3W0, data); + break; } } @@ -1048,38 +1208,39 @@ void gameboy_sound_device::sound_w_internal( int offset, uint8_t data ) void dmg_apu_device::apu_power_off() { sound_w_internal(NR10, 0x00); - m_snd_1.duty = 0; + m_snd[0].duty = 0; m_snd_regs[NR11] = 0; sound_w_internal(NR12, 0x00); sound_w_internal(NR13, 0x00); sound_w_internal(NR14, 0x00); - m_snd_1.length_counting = false; - m_snd_1.sweep_neg_mode_used = false; + m_snd[0].frequency_shadow = 0; + m_snd[0].length_counting = false; + m_snd[0].sweep_neg_mode_used = false; m_snd_regs[NR21] = 0; sound_w_internal(NR22, 0x00); sound_w_internal(NR23, 0x00); sound_w_internal(NR24, 0x00); - m_snd_2.length_counting = false; + m_snd[1].length_counting = false; sound_w_internal(NR30, 0x00); sound_w_internal(NR32, 0x00); sound_w_internal(NR33, 0x00); sound_w_internal(NR34, 0x00); - m_snd_3.length_counting = false; - m_snd_3.current_sample = 0; + m_snd[2].length_counting = false; + m_snd[2].current_sample = 0; m_snd_regs[NR41] = 0; sound_w_internal(NR42, 0x00); sound_w_internal(NR43, 0x00); sound_w_internal(NR44, 0x00); - m_snd_4.length_counting = false; - m_snd_4.cycles_left = noise_period_cycles(); + m_snd[3].length_counting = false; + m_snd[3].cycles_left = noise_period_cycles(); - m_snd_1.on = false; - m_snd_2.on = false; - m_snd_3.on = false; - m_snd_4.on = false; + m_snd[0].on = false; + m_snd[1].on = false; + m_snd[2].on = false; + m_snd[3].on = false; m_snd_control.wave_ram_locked = false; @@ -1093,39 +1254,39 @@ void dmg_apu_device::apu_power_off() void cgb04_apu_device::apu_power_off() { sound_w_internal(NR10, 0x00); - m_snd_1.duty = 0; + m_snd[0].duty = 0; sound_w_internal(NR11, 0x00); sound_w_internal(NR12, 0x00); sound_w_internal(NR13, 0x00); sound_w_internal(NR14, 0x00); - m_snd_1.length_counting = false; - m_snd_1.sweep_neg_mode_used = false; + m_snd[0].length_counting = false; + m_snd[0].sweep_neg_mode_used = false; sound_w_internal(NR21, 0x00); sound_w_internal(NR22, 0x00); sound_w_internal(NR23, 0x00); sound_w_internal(NR24, 0x00); - m_snd_2.length_counting = false; + m_snd[1].length_counting = false; sound_w_internal(NR30, 0x00); sound_w_internal(NR31, 0x00); sound_w_internal(NR32, 0x00); sound_w_internal(NR33, 0x00); sound_w_internal(NR34, 0x00); - m_snd_3.length_counting = false; - m_snd_3.current_sample = 0; + m_snd[2].length_counting = false; + m_snd[2].current_sample = 0; sound_w_internal(NR41, 0x00); sound_w_internal(NR42, 0x00); sound_w_internal(NR43, 0x00); sound_w_internal(NR44, 0x00); - m_snd_4.length_counting = false; - m_snd_4.cycles_left = noise_period_cycles(); + m_snd[3].length_counting = false; + m_snd[3].cycles_left = noise_period_cycles(); - m_snd_1.on = false; - m_snd_2.on = false; - m_snd_3.on = false; - m_snd_4.on = false; + m_snd[0].on = false; + m_snd[1].on = false; + m_snd[2].on = false; + m_snd[3].on = false; m_snd_control.wave_ram_locked = false; @@ -1136,67 +1297,70 @@ void cgb04_apu_device::apu_power_off() } +// convert output: 1 (0) to -1 (15) +constexpr s32 convert_output(s32 sample) +{ + return 0xf - (sample * 2); +} + //------------------------------------------------- // sound_stream_update - handle a stream update //------------------------------------------------- -void gameboy_sound_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) +void gameboy_sound_device::sound_stream_update(sound_stream &stream) { - auto &outputl = outputs[0]; - auto &outputr = outputs[1]; - for (int sampindex = 0; sampindex < outputl.samples(); sampindex++) + for (int sampindex = 0; sampindex < stream.samples(); sampindex++) { s32 sample; s32 left = 0; s32 right = 0; - /* Mode 1 - Wave with Envelope and Sweep */ - if (m_snd_1.on) + /* Channel 1 - Wave with Envelope and Sweep */ + if (m_snd[0].on) { - sample = m_snd_1.signal * m_snd_1.envelope_value; - - if (m_snd_control.mode1_left) + sample = convert_output(m_snd[0].signal * m_snd[0].envelope_value); + if (m_snd_control.chan_left[0]) left += sample; - if (m_snd_control.mode1_right) + if (m_snd_control.chan_right[0]) right += sample; } - /* Mode 2 - Wave with Envelope */ - if (m_snd_2.on) + /* Channel 2 - Wave with Envelope */ + if (m_snd[1].on) { - sample = m_snd_2.signal * m_snd_2.envelope_value; - if (m_snd_control.mode2_left) + sample = convert_output(m_snd[1].signal * m_snd[1].envelope_value); + if (m_snd_control.chan_left[1]) left += sample; - if (m_snd_control.mode2_right) + if (m_snd_control.chan_right[1]) right += sample; } - /* Mode 3 - Wave patterns from WaveRAM */ - if (m_snd_3.on) + /* Channel 3 - Wave patterns from WaveRAM */ + if (m_snd[2].on) { - sample = m_snd_3.signal; - if (m_snd_control.mode3_left) + sample = convert_output(m_snd[2].signal); + if (m_snd_control.chan_left[2]) left += sample; - if (m_snd_control.mode3_right) + if (m_snd_control.chan_right[2]) right += sample; } - /* Mode 4 - Noise with Envelope */ - if (m_snd_4.on) + /* Channel 4 - Noise with Envelope */ + if (m_snd[3].on) { - sample = m_snd_4.signal * m_snd_4.envelope_value; - if (m_snd_control.mode4_left) + sample = convert_output(m_snd[3].signal * m_snd[3].envelope_value); + if (m_snd_control.chan_left[3]) left += sample; - if (m_snd_control.mode4_right) + if (m_snd_control.chan_right[3]) right += sample; } /* Adjust for master volume */ - left *= m_snd_control.vol_left; - right *= m_snd_control.vol_right; + left *= 1 + m_snd_control.vol_left; + right *= 1 + m_snd_control.vol_right; /* Update the buffers */ - outputl.put_int(sampindex, left, 32768 / 64); - outputr.put_int(sampindex, right, 32768 / 64); + stream.put_int(0, sampindex, left, 15 * 4 * (1 + 7)); + stream.put_int(1, sampindex, right, 15 * 4 * (1 + 7)); } } |