summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound/gb.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/sound/gb.cpp')
-rw-r--r--src/devices/sound/gb.cpp940
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));
}
}