summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/audio/gb.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/audio/gb.cpp')
-rw-r--r--src/mame/audio/gb.cpp795
1 files changed, 795 insertions, 0 deletions
diff --git a/src/mame/audio/gb.cpp b/src/mame/audio/gb.cpp
new file mode 100644
index 00000000000..181eb7ad2fb
--- /dev/null
+++ b/src/mame/audio/gb.cpp
@@ -0,0 +1,795 @@
+// license:BSD-3-Clause
+// copyright-holders:Anthony Kruize
+/**************************************************************************************
+* Game Boy sound emulation (c) Anthony Kruize (trandor@labyrinth.net.au)
+*
+* Anyways, sound on the Game Boy consists of 4 separate 'channels'
+* Sound1 = Quadrangular waves with SWEEP and ENVELOPE functions (NR10,11,12,13,14)
+* Sound2 = Quadrangular waves with ENVELOPE functions (NR21,22,23,24)
+* Sound3 = Wave patterns from WaveRAM (NR30,31,32,33,34)
+* Sound4 = White noise with an envelope (NR41,42,43,44)
+*
+* Each sound channel has 2 modes, namely ON and OFF... whoa
+*
+* These tend to be the two most important equations in
+* converting between Hertz and GB frequency registers:
+* (Sounds will have a 2.4% higher frequency on Super GB.)
+* gb = 2048 - (131072 / Hz)
+* Hz = 131072 / (2048 - gb)
+*
+* Changes:
+*
+* 10/2/2002 AK - Preliminary sound code.
+* 13/2/2002 AK - Added a hack for mode 4, other fixes.
+* 23/2/2002 AK - Use lookup tables, added sweep to mode 1. Re-wrote the square
+* wave generation.
+* 13/3/2002 AK - Added mode 3, better lookup tables, other adjustments.
+* 15/3/2002 AK - Mode 4 can now change frequencies.
+* 31/3/2002 AK - Accidently forgot to handle counter/consecutive for mode 1.
+* 3/4/2002 AK - Mode 1 sweep can still occur if shift is 0. Don't let frequency
+* go past the maximum allowed value. Fixed Mode 3 length table.
+* Slight adjustment to Mode 4's period table generation.
+* 5/4/2002 AK - Mode 4 is done correctly, using a polynomial counter instead
+* of being a total hack.
+* 6/4/2002 AK - Slight tweak to mode 3's frequency calculation.
+* 13/4/2002 AK - Reset envelope value when sound is initialized.
+* 21/4/2002 AK - Backed out the mode 3 frequency calculation change.
+* Merged init functions into gameboy_sound_w().
+* 14/5/2002 AK - Removed magic numbers in the fixed point math.
+* 12/6/2002 AK - Merged SOUNDx structs into one SOUND struct.
+* 26/10/2002 AK - Finally fixed channel 3!
+*
+***************************************************************************************/
+
+#include "emu.h"
+#include "gb.h"
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+#define NR10 0x00
+#define NR11 0x01
+#define NR12 0x02
+#define NR13 0x03
+#define NR14 0x04
+#define NR21 0x06
+#define NR22 0x07
+#define NR23 0x08
+#define NR24 0x09
+#define NR30 0x0A
+#define NR31 0x0B
+#define NR32 0x0C
+#define NR33 0x0D
+#define NR34 0x0E
+#define NR41 0x10
+#define NR42 0x11
+#define NR43 0x12
+#define NR44 0x13
+#define NR50 0x14
+#define NR51 0x15
+#define NR52 0x16
+#define AUD3W0 0x20
+#define AUD3W1 0x21
+#define AUD3W2 0x22
+#define AUD3W3 0x23
+#define AUD3W4 0x24
+#define AUD3W5 0x25
+#define AUD3W6 0x26
+#define AUD3W7 0x27
+#define AUD3W8 0x28
+#define AUD3W9 0x29
+#define AUD3WA 0x2A
+#define AUD3WB 0x2B
+#define AUD3WC 0x2C
+#define AUD3WD 0x2D
+#define AUD3WE 0x2E
+#define AUD3WF 0x2F
+
+#define LEFT 1
+#define RIGHT 2
+#define FIXED_POINT 16
+
+/* Represents wave duties of 12.5%, 25%, 50% and 75% */
+static const float wave_duty_table[4] = { 8.0f, 4.0f, 2.0f, 1.33f };
+
+// device type definition
+const device_type GAMEBOY = &device_creator<gameboy_sound_device>;
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// gameboy_sound_device - constructor
+//-------------------------------------------------
+
+gameboy_sound_device::gameboy_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, GAMEBOY, "LR35902 Sound", tag, owner, clock, "gameboy_sound", __FILE__),
+ device_sound_interface(mconfig, *this)
+{
+}
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void gameboy_sound_device::device_config_complete()
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void gameboy_sound_device::device_start()
+{
+ m_channel = machine().sound().stream_alloc(*this, 0, 2, machine().sample_rate());
+ m_rate = machine().sample_rate();
+
+ save_item(NAME(m_snd_regs));
+ // 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));
+ // sound 1
+ save_item(NAME(m_snd_1.on));
+ save_item(NAME(m_snd_1.channel));
+ save_item(NAME(m_snd_1.length));
+ save_item(NAME(m_snd_1.pos));
+ save_item(NAME(m_snd_1.period));
+ save_item(NAME(m_snd_1.count));
+ save_item(NAME(m_snd_1.mode));
+ save_item(NAME(m_snd_1.duty));
+ save_item(NAME(m_snd_1.env_value));
+ save_item(NAME(m_snd_1.env_direction));
+ save_item(NAME(m_snd_1.env_length));
+ save_item(NAME(m_snd_1.env_count));
+ save_item(NAME(m_snd_1.signal));
+ save_item(NAME(m_snd_1.frequency));
+ save_item(NAME(m_snd_1.swp_shift));
+ save_item(NAME(m_snd_1.swp_direction));
+ save_item(NAME(m_snd_1.swp_time));
+ save_item(NAME(m_snd_1.swp_count));
+ save_item(NAME(m_snd_1.level));
+ save_item(NAME(m_snd_1.offset));
+ save_item(NAME(m_snd_1.dutycount));
+ save_item(NAME(m_snd_1.ply_step));
+ save_item(NAME(m_snd_1.ply_value));
+ // sound 2
+ save_item(NAME(m_snd_2.on));
+ save_item(NAME(m_snd_2.channel));
+ save_item(NAME(m_snd_2.length));
+ save_item(NAME(m_snd_2.pos));
+ save_item(NAME(m_snd_2.period));
+ save_item(NAME(m_snd_2.count));
+ save_item(NAME(m_snd_2.mode));
+ save_item(NAME(m_snd_2.duty));
+ save_item(NAME(m_snd_2.env_value));
+ save_item(NAME(m_snd_2.env_direction));
+ save_item(NAME(m_snd_2.env_length));
+ save_item(NAME(m_snd_2.env_count));
+ save_item(NAME(m_snd_2.signal));
+ save_item(NAME(m_snd_2.frequency));
+ save_item(NAME(m_snd_2.swp_shift));
+ save_item(NAME(m_snd_2.swp_direction));
+ save_item(NAME(m_snd_2.swp_time));
+ save_item(NAME(m_snd_2.swp_count));
+ save_item(NAME(m_snd_2.level));
+ save_item(NAME(m_snd_2.offset));
+ save_item(NAME(m_snd_2.dutycount));
+ save_item(NAME(m_snd_2.ply_step));
+ save_item(NAME(m_snd_2.ply_value));
+ // sound 3
+ save_item(NAME(m_snd_3.on));
+ save_item(NAME(m_snd_3.channel));
+ save_item(NAME(m_snd_3.length));
+ save_item(NAME(m_snd_3.pos));
+ save_item(NAME(m_snd_3.period));
+ save_item(NAME(m_snd_3.count));
+ save_item(NAME(m_snd_3.mode));
+ save_item(NAME(m_snd_3.duty));
+ save_item(NAME(m_snd_3.env_value));
+ save_item(NAME(m_snd_3.env_direction));
+ save_item(NAME(m_snd_3.env_length));
+ save_item(NAME(m_snd_3.env_count));
+ save_item(NAME(m_snd_3.signal));
+ save_item(NAME(m_snd_3.frequency));
+ save_item(NAME(m_snd_3.swp_shift));
+ save_item(NAME(m_snd_3.swp_direction));
+ save_item(NAME(m_snd_3.swp_time));
+ save_item(NAME(m_snd_3.swp_count));
+ save_item(NAME(m_snd_3.level));
+ save_item(NAME(m_snd_3.offset));
+ save_item(NAME(m_snd_3.dutycount));
+ save_item(NAME(m_snd_3.ply_step));
+ save_item(NAME(m_snd_3.ply_value));
+ // sound 4
+ save_item(NAME(m_snd_4.on));
+ save_item(NAME(m_snd_4.channel));
+ save_item(NAME(m_snd_4.length));
+ save_item(NAME(m_snd_4.pos));
+ save_item(NAME(m_snd_4.period));
+ save_item(NAME(m_snd_4.count));
+ save_item(NAME(m_snd_4.mode));
+ save_item(NAME(m_snd_4.duty));
+ save_item(NAME(m_snd_4.env_value));
+ save_item(NAME(m_snd_4.env_direction));
+ save_item(NAME(m_snd_4.env_length));
+ save_item(NAME(m_snd_4.env_count));
+ save_item(NAME(m_snd_4.signal));
+ save_item(NAME(m_snd_4.frequency));
+ save_item(NAME(m_snd_4.swp_shift));
+ save_item(NAME(m_snd_4.swp_direction));
+ save_item(NAME(m_snd_4.swp_time));
+ save_item(NAME(m_snd_4.swp_count));
+ save_item(NAME(m_snd_4.level));
+ save_item(NAME(m_snd_4.offset));
+ save_item(NAME(m_snd_4.dutycount));
+ save_item(NAME(m_snd_4.ply_step));
+ save_item(NAME(m_snd_4.ply_value));
+}
+
+//-------------------------------------------------
+// device_reset
+//-------------------------------------------------
+
+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));
+
+ /* Calculate the envelope and sweep tables */
+ for (int i = 0; i < 8; i++)
+ {
+ m_env_length_table[i] = (i * ((1 << FIXED_POINT) / 64) * m_rate) >> FIXED_POINT;
+ m_swp_time_table[i] = (((i << FIXED_POINT) / 128) * m_rate) >> (FIXED_POINT - 1);
+ }
+
+ /* Calculate the period tables */
+ for (int i = 0; i < MAX_FREQUENCIES; i++)
+ {
+ m_period_table[i] = ((1 << FIXED_POINT) / (131072 / (2048 - i))) * m_rate;
+ m_period_mode3_table[i] = ((1 << FIXED_POINT) / (65536 / (2048 - i))) * m_rate;
+ }
+ /* Calculate the period table for mode 4 */
+ for (int i = 0; i < 8; i++)
+ {
+ for (int j = 0; j < 16; j++)
+ {
+ // i is the dividing ratio of frequencies
+ // j is the shift clock frequency
+ m_period_mode4_table[i][j] = ((1 << FIXED_POINT) / (524288 / ((i == 0) ? 0.5 : i) / (1 << (j + 1)))) * m_rate;
+ }
+ }
+
+ /* Calculate the length table */
+ for (int i = 0; i < 64; i++)
+ m_length_table[i] = ((64 - i) * ((1 << FIXED_POINT)/256) * m_rate) >> FIXED_POINT;
+
+ /* Calculate the length table for mode 3 */
+ for (int i = 0; i < 256; i++)
+ m_length_mode3_table[i] = ((256 - i) * ((1 << FIXED_POINT)/256) * m_rate) >> FIXED_POINT;
+
+ 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;
+}
+
+
+/***************************************************************************
+ IMPLEMENTATION
+***************************************************************************/
+
+READ8_MEMBER( gameboy_sound_device::wave_r )
+{
+ /* TODO: properly emulate scrambling of wave ram area when playback is active */
+ return m_snd_regs[AUD3W0 + offset] | m_snd_3.on;
+}
+
+READ8_MEMBER( gameboy_sound_device::sound_r )
+{
+ switch (offset)
+ {
+ case 0x05:
+ case 0x0f:
+ return 0xff;
+ case NR52:
+ return 0x70 | m_snd_regs[offset];
+ default:
+ return m_snd_regs[offset];
+ }
+}
+
+WRITE8_MEMBER( gameboy_sound_device::wave_w )
+{
+ m_snd_regs[AUD3W0 + offset] = data;
+}
+
+WRITE8_MEMBER( gameboy_sound_device::sound_w )
+{
+ /* change in registers so update first */
+ m_channel->update();
+
+ /* Only register NR52 is accessible if the sound controller is disabled */
+ if (!m_snd_control.on && offset != NR52)
+ return;
+
+ sound_w_internal(offset, data);
+}
+
+
+void gameboy_sound_device::sound_w_internal( int offset, UINT8 data )
+{
+ /* Store the value */
+ m_snd_regs[offset] = data;
+
+ switch (offset)
+ {
+ /*MODE 1 */
+ case NR10: /* Sweep (R/W) */
+ m_snd_1.swp_shift = data & 0x7;
+ m_snd_1.swp_direction = (data & 0x8) >> 3;
+ m_snd_1.swp_direction |= m_snd_1.swp_direction - 1;
+ m_snd_1.swp_time = m_swp_time_table[ (data & 0x70) >> 4 ];
+ break;
+ case NR11: /* Sound length/Wave pattern duty (R/W) */
+ m_snd_1.duty = (data & 0xc0) >> 6;
+ m_snd_1.length = m_length_table[data & 0x3f];
+ break;
+ case NR12: /* Envelope (R/W) */
+ m_snd_1.env_value = data >> 4;
+ m_snd_1.env_direction = (data & 0x8) >> 3;
+ m_snd_1.env_direction |= m_snd_1.env_direction - 1;
+ m_snd_1.env_length = m_env_length_table[data & 0x7];
+ break;
+ case NR13: /* Frequency lo (R/W) */
+ m_snd_1.frequency = ((m_snd_regs[NR14] & 0x7) << 8) | m_snd_regs[NR13];
+ m_snd_1.period = m_period_table[m_snd_1.frequency];
+ break;
+ case NR14: /* Frequency hi / Initialize (R/W) */
+ m_snd_1.mode = (data & 0x40) >> 6;
+ m_snd_1.frequency = ((m_snd_regs[NR14] & 0x7) << 8) | m_snd_regs[NR13];
+ m_snd_1.period = m_period_table[m_snd_1.frequency];
+ if (data & 0x80)
+ {
+ if (!m_snd_1.on)
+ m_snd_1.pos = 0;
+ m_snd_1.on = 1;
+ m_snd_1.count = 0;
+ m_snd_1.env_value = m_snd_regs[NR12] >> 4;
+ m_snd_1.env_count = 0;
+ m_snd_1.swp_count = 0;
+ m_snd_1.signal = 0x1;
+ m_snd_regs[NR52] |= 0x1;
+ }
+ break;
+
+ /*MODE 2 */
+ case NR21: /* Sound length/Wave pattern duty (R/W) */
+ m_snd_2.duty = (data & 0xc0) >> 6;
+ m_snd_2.length = m_length_table[data & 0x3f];
+ break;
+ case NR22: /* Envelope (R/W) */
+ m_snd_2.env_value = data >> 4;
+ m_snd_2.env_direction = (data & 0x8) >> 3;
+ m_snd_2.env_direction |= m_snd_2.env_direction - 1;
+ m_snd_2.env_length = m_env_length_table[data & 0x7];
+ break;
+ case NR23: /* Frequency lo (R/W) */
+ m_snd_2.period = m_period_table[((m_snd_regs[NR24] & 0x7) << 8) | m_snd_regs[NR23]];
+ break;
+ case NR24: /* Frequency hi / Initialize (R/W) */
+ m_snd_2.mode = (data & 0x40) >> 6;
+ m_snd_2.period = m_period_table[((m_snd_regs[NR24] & 0x7) << 8) | m_snd_regs[NR23]];
+ if (data & 0x80)
+ {
+ if (!m_snd_2.on)
+ m_snd_2.pos = 0;
+ m_snd_2.on = 1;
+ m_snd_2.count = 0;
+ m_snd_2.env_value = m_snd_regs[NR22] >> 4;
+ m_snd_2.env_count = 0;
+ m_snd_2.signal = 0x1;
+ m_snd_regs[NR52] |= 0x2;
+ }
+ break;
+
+ /*MODE 3 */
+ case NR30: /* Sound On/Off (R/W) */
+ m_snd_3.on = (data & 0x80) >> 7;
+ break;
+ case NR31: /* Sound Length (R/W) */
+ m_snd_3.length = m_length_mode3_table[data];
+ break;
+ case NR32: /* Select Output Level */
+ m_snd_3.level = (data & 0x60) >> 5;
+ break;
+ case NR33: /* Frequency lo (W) */
+ m_snd_3.period = m_period_mode3_table[((m_snd_regs[NR34] & 0x7) << 8) + m_snd_regs[NR33]];
+ break;
+ case NR34: /* Frequency hi / Initialize (W) */
+ m_snd_3.mode = (data & 0x40) >> 6;
+ m_snd_3.period = m_period_mode3_table[((m_snd_regs[NR34] & 0x7) << 8) + m_snd_regs[NR33]];
+ if (data & 0x80)
+ {
+ if (!m_snd_3.on)
+ {
+ m_snd_3.pos = 0;
+ m_snd_3.offset = 0;
+ m_snd_3.duty = 0;
+ }
+ m_snd_3.on = 1;
+ m_snd_3.count = 0;
+ m_snd_3.duty = 1;
+ m_snd_3.dutycount = 0;
+ m_snd_regs[NR52] |= 0x4;
+ }
+ break;
+
+ /*MODE 4 */
+ case NR41: /* Sound Length (R/W) */
+ m_snd_4.length = m_length_table[data & 0x3f];
+ break;
+ case NR42: /* Envelope (R/W) */
+ m_snd_4.env_value = data >> 4;
+ m_snd_4.env_direction = (data & 0x8) >> 3;
+ m_snd_4.env_direction |= m_snd_4.env_direction - 1;
+ m_snd_4.env_length = m_env_length_table[data & 0x7];
+ break;
+ case NR43: /* Polynomial Counter/Frequency */
+ m_snd_4.period = m_period_mode4_table[data & 0x7][(data & 0xF0) >> 4];
+ m_snd_4.ply_step = (data & 0x8) >> 3;
+ break;
+ case NR44: /* Counter/Consecutive / Initialize (R/W) */
+ m_snd_4.mode = (data & 0x40) >> 6;
+ if (data & 0x80)
+ {
+ if (!m_snd_4.on)
+ m_snd_4.pos = 0;
+ m_snd_4.on = 1;
+ m_snd_4.count = 0;
+ m_snd_4.env_value = m_snd_regs[NR42] >> 4;
+ m_snd_4.env_count = 0;
+ m_snd_4.signal = machine().rand();
+ m_snd_4.ply_value = 0x7fff;
+ m_snd_regs[NR52] |= 0x8;
+ }
+ break;
+
+ /* CONTROL */
+ case NR50: /* Channel Control / On/Off / Volume (R/W) */
+ m_snd_control.vol_left = data & 0x7;
+ 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;
+ 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 */
+ m_snd_control.on = (data & 0x80) >> 7;
+ if (!m_snd_control.on)
+ {
+ sound_w_internal(NR10, 0x80);
+ sound_w_internal(NR11, 0xBF);
+ sound_w_internal(NR12, 0xF3);
+ sound_w_internal(NR13, 0xFF);
+ sound_w_internal(NR14, 0xBF);
+ //sound_w_internal(NR20, 0xFF);
+ sound_w_internal(NR21, 0x3F);
+ sound_w_internal(NR22, 0x00);
+ sound_w_internal(NR23, 0xFF);
+ sound_w_internal(NR24, 0xBF);
+ sound_w_internal(NR30, 0x7F);
+ sound_w_internal(NR31, 0xFF);
+ sound_w_internal(NR32, 0x9F);
+ sound_w_internal(NR33, 0xFF);
+ sound_w_internal(NR34, 0xBF);
+ //sound_w_internal(NR40, 0xFF);
+ sound_w_internal(NR41, 0xFF);
+ sound_w_internal(NR42, 0x00);
+ sound_w_internal(NR43, 0x00);
+ sound_w_internal(NR44, 0xBF);
+ sound_w_internal(NR50, 0x77);
+ sound_w_internal(NR51, 0xF3);
+ m_snd_1.on = 0;
+ m_snd_2.on = 0;
+ m_snd_3.on = 0;
+ m_snd_4.on = 0;
+ m_snd_regs[offset] = 0;
+ }
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void gameboy_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t sample, left, right, mode4_mask;
+
+ while (samples-- > 0)
+ {
+ left = right = 0;
+
+ /* Mode 1 - Wave with Envelope and Sweep */
+ if (m_snd_1.on)
+ {
+ sample = m_snd_1.signal * m_snd_1.env_value;
+ m_snd_1.pos++;
+ if (m_snd_1.pos == (UINT32)(m_snd_1.period / wave_duty_table[m_snd_1.duty]) >> FIXED_POINT)
+ {
+ m_snd_1.signal = -m_snd_1.signal;
+ }
+ else if (m_snd_1.pos > (m_snd_1.period >> FIXED_POINT))
+ {
+ m_snd_1.pos = 0;
+ m_snd_1.signal = -m_snd_1.signal;
+ }
+
+ if (m_snd_1.length && m_snd_1.mode)
+ {
+ m_snd_1.count++;
+ if (m_snd_1.count >= m_snd_1.length)
+ {
+ m_snd_1.on = 0;
+ m_snd_regs[NR52] &= 0xFE;
+ }
+ }
+
+ if (m_snd_1.env_length)
+ {
+ m_snd_1.env_count++;
+ if (m_snd_1.env_count >= m_snd_1.env_length)
+ {
+ m_snd_1.env_count = 0;
+ m_snd_1.env_value += m_snd_1.env_direction;
+ if (m_snd_1.env_value < 0)
+ m_snd_1.env_value = 0;
+ if (m_snd_1.env_value > 15)
+ m_snd_1.env_value = 15;
+ }
+ }
+
+ if (m_snd_1.swp_time)
+ {
+ m_snd_1.swp_count++;
+ if (m_snd_1.swp_count >= m_snd_1.swp_time)
+ {
+ m_snd_1.swp_count = 0;
+ if (m_snd_1.swp_direction > 0)
+ {
+ m_snd_1.frequency -= m_snd_1.frequency / (1 << m_snd_1.swp_shift);
+ if (m_snd_1.frequency <= 0)
+ {
+ m_snd_1.on = 0;
+ m_snd_regs[NR52] &= 0xFE;
+ }
+ }
+ else
+ {
+ m_snd_1.frequency += m_snd_1.frequency / (1 << m_snd_1.swp_shift);
+ if (m_snd_1.frequency >= MAX_FREQUENCIES)
+ {
+ m_snd_1.frequency = MAX_FREQUENCIES - 1;
+ }
+ }
+
+ m_snd_1.period = m_period_table[m_snd_1.frequency];
+ }
+ }
+
+ if (m_snd_control.mode1_left)
+ left += sample;
+ if (m_snd_control.mode1_right)
+ right += sample;
+ }
+
+ /* Mode 2 - Wave with Envelope */
+ if (m_snd_2.on)
+ {
+ sample = m_snd_2.signal * m_snd_2.env_value;
+ m_snd_2.pos++;
+ if( m_snd_2.pos == (UINT32)(m_snd_2.period / wave_duty_table[m_snd_2.duty]) >> FIXED_POINT)
+ {
+ m_snd_2.signal = -m_snd_2.signal;
+ }
+ else if (m_snd_2.pos > (m_snd_2.period >> FIXED_POINT))
+ {
+ m_snd_2.pos = 0;
+ m_snd_2.signal = -m_snd_2.signal;
+ }
+
+ if (m_snd_2.length && m_snd_2.mode)
+ {
+ m_snd_2.count++;
+ if (m_snd_2.count >= m_snd_2.length)
+ {
+ m_snd_2.on = 0;
+ m_snd_regs[NR52] &= 0xFD;
+ }
+ }
+
+ if (m_snd_2.env_length)
+ {
+ m_snd_2.env_count++;
+ if (m_snd_2.env_count >= m_snd_2.env_length)
+ {
+ m_snd_2.env_count = 0;
+ m_snd_2.env_value += m_snd_2.env_direction;
+ if (m_snd_2.env_value < 0)
+ m_snd_2.env_value = 0;
+ if (m_snd_2.env_value > 15)
+ m_snd_2.env_value = 15;
+ }
+ }
+
+ if (m_snd_control.mode2_left)
+ left += sample;
+ if (m_snd_control.mode2_right)
+ right += sample;
+ }
+
+ /* Mode 3 - Wave patterns from WaveRAM */
+ if (m_snd_3.on)
+ {
+ /* NOTE: This is extremely close, but not quite right.
+ The problem is for GB frequencies above 2000 the frequency gets
+ clipped. This is caused because m_snd_3.pos is never 0 at the test.*/
+ sample = m_snd_regs[AUD3W0 + (m_snd_3.offset/2)];
+ if (!(m_snd_3.offset % 2))
+ {
+ sample >>= 4;
+ }
+ sample = (sample & 0xF) - 8;
+
+ if (m_snd_3.level)
+ sample >>= (m_snd_3.level - 1);
+ else
+ sample = 0;
+
+ m_snd_3.pos++;
+ if (m_snd_3.pos >= ((UINT32)(((m_snd_3.period) >> 21)) + m_snd_3.duty))
+ {
+ m_snd_3.pos = 0;
+ if (m_snd_3.dutycount == ((UINT32)(((m_snd_3.period) >> FIXED_POINT)) % 32))
+ {
+ m_snd_3.duty--;
+ }
+ m_snd_3.dutycount++;
+ m_snd_3.offset++;
+ if (m_snd_3.offset > 31)
+ {
+ m_snd_3.offset = 0;
+ m_snd_3.duty = 1;
+ m_snd_3.dutycount = 0;
+ }
+ }
+
+ if (m_snd_3.length && m_snd_3.mode)
+ {
+ m_snd_3.count++;
+ if (m_snd_3.count >= m_snd_3.length)
+ {
+ m_snd_3.on = 0;
+ m_snd_regs[NR52] &= 0xFB;
+ }
+ }
+
+ if (m_snd_control.mode3_left)
+ left += sample;
+ if (m_snd_control.mode3_right)
+ right += sample;
+ }
+
+ /* Mode 4 - Noise with Envelope */
+ if (m_snd_4.on)
+ {
+ /* Similar problem to Mode 3, we seem to miss some notes */
+ sample = m_snd_4.signal & m_snd_4.env_value;
+ m_snd_4.pos++;
+ if (m_snd_4.pos == (m_snd_4.period >> (FIXED_POINT + 1)))
+ {
+ /* Using a Polynomial Counter (aka Linear Feedback Shift Register)
+ Mode 4 has a 7 bit and 15 bit counter so we need to shift the
+ bits around accordingly */
+ mode4_mask = (((m_snd_4.ply_value & 0x2) >> 1) ^ (m_snd_4.ply_value & 0x1)) << (m_snd_4.ply_step ? 6 : 14);
+ m_snd_4.ply_value >>= 1;
+ m_snd_4.ply_value |= mode4_mask;
+ m_snd_4.ply_value &= (m_snd_4.ply_step ? 0x7f : 0x7fff);
+ m_snd_4.signal = (INT8)m_snd_4.ply_value;
+ }
+ else if (m_snd_4.pos > (m_snd_4.period >> FIXED_POINT))
+ {
+ m_snd_4.pos = 0;
+ mode4_mask = (((m_snd_4.ply_value & 0x2) >> 1) ^ (m_snd_4.ply_value & 0x1)) << (m_snd_4.ply_step ? 6 : 14);
+ m_snd_4.ply_value >>= 1;
+ m_snd_4.ply_value |= mode4_mask;
+ m_snd_4.ply_value &= (m_snd_4.ply_step ? 0x7f : 0x7fff);
+ m_snd_4.signal = (INT8)m_snd_4.ply_value;
+ }
+
+ if (m_snd_4.length && m_snd_4.mode)
+ {
+ m_snd_4.count++;
+ if (m_snd_4.count >= m_snd_4.length)
+ {
+ m_snd_4.on = 0;
+ m_snd_regs[NR52] &= 0xF7;
+ }
+ }
+
+ if (m_snd_4.env_length)
+ {
+ m_snd_4.env_count++;
+ if (m_snd_4.env_count >= m_snd_4.env_length)
+ {
+ m_snd_4.env_count = 0;
+ m_snd_4.env_value += m_snd_4.env_direction;
+ if (m_snd_4.env_value < 0)
+ m_snd_4.env_value = 0;
+ if (m_snd_4.env_value > 15)
+ m_snd_4.env_value = 15;
+ }
+ }
+
+ if (m_snd_control.mode4_left)
+ left += sample;
+ if (m_snd_control.mode4_right)
+ right += sample;
+ }
+
+ /* Adjust for master volume */
+ left *= m_snd_control.vol_left;
+ right *= m_snd_control.vol_right;
+
+ /* pump up the volume */
+ left <<= 6;
+ right <<= 6;
+
+ /* Update the buffers */
+ *(outputs[0]++) = left;
+ *(outputs[1]++) = right;
+ }
+
+ m_snd_regs[NR52] = (m_snd_regs[NR52]&0xf0) | m_snd_1.on | (m_snd_2.on << 1) | (m_snd_3.on << 2) | (m_snd_4.on << 3);
+
+}