diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index d05be0746..52477f7b0 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -64,6 +64,8 @@ enum DivDispatchCmds { DIV_CMD_AY_ENVELOPE_LOW, DIV_CMD_AY_ENVELOPE_HIGH, DIV_CMD_AY_ENVELOPE_SLIDE, + DIV_CMD_AY_NOISE_MASK_AND, + DIV_CMD_AY_NOISE_MASK_OR, DIV_CMD_SAA_ENVELOPE, diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index 18417056b..8ec3428ac 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -271,6 +271,14 @@ int DivPlatformAY8930::dispatch(DivCommand c) { case DIV_CMD_AY_ENVELOPE_SLIDE: ayEnvSlide[c.chan]=c.value; break; + case DIV_CMD_AY_NOISE_MASK_AND: + ayNoiseAnd=c.value; + immWrite(0x19,ayNoiseAnd); + break; + case DIV_CMD_AY_NOISE_MASK_OR: + ayNoiseOr=c.value; + immWrite(0x1a,ayNoiseOr); + break; case DIV_ALWAYS_SET_VOLUME: return 0; break; @@ -325,14 +333,8 @@ void DivPlatformAY8930::reset() { pendingWrites[i]=-1; } - lastBusy=60; - dacMode=0; - dacPeriod=0; - dacPos=0; - dacRate=0; - dacSample=-1; - sampleBank=0; - + ayNoiseAnd=0; + ayNoiseOr=0; delay=0; extMode=false; diff --git a/src/engine/platform/ay8930.h b/src/engine/platform/ay8930.h index 90226f234..408ee9238 100644 --- a/src/engine/platform/ay8930.h +++ b/src/engine/platform/ay8930.h @@ -28,14 +28,7 @@ class DivPlatformAY8930: public DivDispatch { }; std::queue writes; ay8930_device* ay; - unsigned char lastBusy; - - bool dacMode; - int dacPeriod; - int dacRate; - int dacPos; - int dacSample; - unsigned char sampleBank; + unsigned char ayNoiseAnd, ayNoiseOr; bool bank; int delay; diff --git a/src/engine/platform/sound/ay8910.cpp b/src/engine/platform/sound/ay8910.cpp index a9e1c8fb6..302b5a96a 100644 --- a/src/engine/platform/sound/ay8910.cpp +++ b/src/engine/platform/sound/ay8910.cpp @@ -581,6 +581,8 @@ YM2203 English datasheet: http://www.appleii-box.de/APPLE2/JonasCard/YM2203%20da YM2203 Japanese datasheet contents, translated: http://www.larwe.com/technical/chip_ym2203.html */ +// additional modifications by tildearrow and Eulous for furnace (particularly AY8930 emulation) + #include "ay8910.h" #include #include @@ -1019,8 +1021,10 @@ void ay8910_device::ay8910_write_reg(int r, int v) m_tone[2].set_duty(m_regs[AY_CDUTY]); break; case AY_NOISEAND: + m_noise_and=m_regs[AY_NOISEAND]; + break; case AY_NOISEOR: - // not implemented + m_noise_or=m_regs[AY_NOISEOR]; break; default: m_regs[r] = 0; // reserved, set as 0 @@ -1080,13 +1084,36 @@ void ay8910_device::sound_stream_update(short** outputs, int outLen) if (!m_prescale_noise || is_expanded_mode()) // AY8930 noise generator rate is twice compares as compatibility mode { - /* The Random Number Generator of the 8910 is a 17-bit shift */ - /* register. The input to the shift register is bit0 XOR bit3 */ - /* (bit0 is the output). This was verified on AY-3-8910 and YM2149 chips. */ - - // TODO : get actually algorithm for AY8930 - m_rng ^= (((m_rng & 1) ^ ((m_rng >> 3) & 1)) << 17); - m_rng >>= 1; + if (is_expanded_mode()) { + // This is called "Noise value" on the docs, but is a counter whose period is determined by the LFSR. + // Using AND/OR gates, specific periods can be "filtered" out. + // A square wave can be generated through this behavior, which can be used for crude AM pulse width modulation. + + // The period of the noise is determined by this value. + // The least significant byte of the LFSR is bitwise ANDed with the AND mask, and then bitwise ORed with the OR mask. + unsigned int noiseValuePeriod = ((m_rng & 0xFF & m_noise_and) | m_noise_or); + + // Clock the noise value. + if (m_noise_value >= noiseValuePeriod) { + m_noise_value = 0; + + // When everything is finally said and done, a 1bit latch is flipped. + // This is the final output of the noise, to be multiplied by the tone and envelope generators of the channel. + m_noise_latch ^= 1; + + // The 17-bit LFSR is updated, using an XOR across bits 0 and 2. + unsigned int feedback = (m_rng & 1) ^ ((m_rng >> 2) & 1); + m_rng >>= 1; + m_rng |= (feedback << 16); + } + m_noise_value++; + } else { + /* The Random Number Generator of the 8910 is a 17-bit shift */ + /* register. The input to the shift register is bit0 XOR bit3 */ + /* (bit0 is the output). This was verified on AY-3-8910 and YM2149 chips. */ + m_rng ^= (((m_rng & 1) ^ ((m_rng >> 3) & 1)) << 17); + m_rng >>= 1; + } } } @@ -1242,6 +1269,10 @@ void ay8910_device::ay8910_reset_ym() m_register_latch = 0; m_rng = 1; m_mode = 0; // ay-3-8910 compatible mode + m_noise_and = 0; + m_noise_or = 0; + m_noise_value = 0; + m_noise_latch = 0; for (int chan = 0; chan < NUM_CHANNELS; chan++) { m_tone[chan].reset(); @@ -1428,6 +1459,10 @@ ay8910_device::ay8910_device(device_type type, unsigned int clock, m_prescale_noise(0), m_count_noise(0), m_rng(0), + m_noise_and(0), + m_noise_or(0), + m_noise_value(0), + m_noise_latch(0), m_mode(0), m_env_step_mask((!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 0x0f : 0x1f), m_step( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 2 : 1), diff --git a/src/engine/platform/sound/ay8910.h b/src/engine/platform/sound/ay8910.h index e3d4b3718..d7741a43e 100644 --- a/src/engine/platform/sound/ay8910.h +++ b/src/engine/platform/sound/ay8910.h @@ -292,6 +292,10 @@ private: unsigned char m_prescale_noise; int m_count_noise; int m_rng; + unsigned int m_noise_and; + unsigned int m_noise_or; + unsigned int m_noise_value; + unsigned int m_noise_latch; unsigned char m_mode; unsigned char m_env_step_mask; /* init parameters ... */ diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index ce04a00b7..0d32a4c49 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -76,6 +76,8 @@ const char* cmdName[DIV_CMD_MAX]={ "AY_ENVELOPE_LOW", "AY_ENVELOPE_HIGH", "AY_ENVELOPE_SLIDE", + "AY_NOISE_MASK_AND", + "AY_NOISE_MASK_OR", "SAA_ENVELOPE", @@ -359,6 +361,12 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char case 0x26: // envelope slide down dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_SLIDE,ch,effectVal)); break; + case 0x27: // noise and mask + dispatchCmd(DivCommand(DIV_CMD_AY_NOISE_MASK_AND,ch,effectVal)); + break; + case 0x28: // noise or mask + dispatchCmd(DivCommand(DIV_CMD_AY_NOISE_MASK_OR,ch,effectVal)); + break; } break; case DIV_SYSTEM_SAA1099: