From 4f1ebc3b6e32a574c7fe88be93618221f756a97c Mon Sep 17 00:00:00 2001 From: cam900 Date: Wed, 4 May 2022 23:04:20 +0900 Subject: [PATCH 01/13] Some AY-3-8910, AY8930 enhancements Fix AY PSG clock divider function when exists, Update emulation core (MAME AY PSG and compatibles - includes noise routine updates) Fix configuration flag, Add MSX Darky sound expander to preset Clock divider isn't affected to expanded mode's tone and noise generator? --- src/engine/platform/ay.cpp | 12 +++- src/engine/platform/ay.h | 2 +- src/engine/platform/ay8930.cpp | 90 +++++++++++++----------- src/engine/platform/ay8930.h | 17 +++-- src/engine/platform/sound/ay8910.cpp | 100 ++++++++++----------------- src/engine/platform/sound/ay8910.h | 53 +++++++++++--- src/gui/presets.cpp | 9 +++ src/gui/sysConf.cpp | 17 ++++- 8 files changed, 178 insertions(+), 122 deletions(-) diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index 37108452..702cb00e 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -27,7 +27,7 @@ #define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;} #define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(regRemap(a),v); if (dumpWrites) {addWrite(regRemap(a),v);} } -#define CHIP_DIVIDER 8 +#define CHIP_DIVIDER ((sunsoft||clockSel)?16:8) const char* regCheatSheetAY[]={ "FreqL_A", "0", @@ -589,6 +589,7 @@ void DivPlatformAY8910::poke(std::vector& wlist) { } void DivPlatformAY8910::setFlags(unsigned int flags) { + clockSel=(flags>>7)&1; switch (flags&15) { case 1: chipClock=COLOR_PAL*2.0/5.0; @@ -620,6 +621,12 @@ void DivPlatformAY8910::setFlags(unsigned int flags) { case 10: chipClock=2097152; break; + case 11: + chipClock=COLOR_NTSC; + break; + case 12: + chipClock=3600000; + break; default: chipClock=COLOR_NTSC/2.0; break; @@ -653,8 +660,9 @@ void DivPlatformAY8910::setFlags(unsigned int flags) { break; } ay->device_start(); + ay->set_clock_sel(clockSel); - stereo=flags>>6; + stereo=(flags>>6)&1; } int DivPlatformAY8910::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { diff --git a/src/engine/platform/ay.h b/src/engine/platform/ay.h index b257e3bb..f93d34bd 100644 --- a/src/engine/platform/ay.h +++ b/src/engine/platform/ay.h @@ -70,7 +70,7 @@ class DivPlatformAY8910: public DivDispatch { int delay; bool extMode; - bool stereo, sunsoft, intellivision; + bool stereo, sunsoft, intellivision, clockSel; bool ioPortA, ioPortB; unsigned char portAVal, portBVal; diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index 3fec732b..41c54c57 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -27,7 +27,7 @@ #define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;} #define immWrite2(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} } -#define CHIP_DIVIDER 4 +#define CHIP_DIVIDER 8 const char* regCheatSheetAY8930[]={ "FreqL_A", "00", @@ -64,7 +64,7 @@ const char* regCheatSheetAY8930[]={ void DivPlatformAY8930::immWrite(unsigned char a, unsigned char v) { if ((int)bank!=(a>>4)) { bank=a>>4; - immWrite2(0x0d, 0xa0|(bank<<4)|ayEnvMode[0]); + immWrite2(0x0d, 0xa0|(bank<<4)|chan[0].envelope.mode); } if (a==0x0d) { immWrite2(0x0d,0xa0|(bank<<4)|(v&15)); @@ -258,8 +258,8 @@ void DivPlatformAY8930::tick(bool sysTick) { rWrite(0x16+i,chan[i].std.ex1.val); } if (chan[i].std.ex2.had) { - ayEnvMode[i]=chan[i].std.ex2.val; - rWrite(regMode[i],ayEnvMode[i]); + chan[i].envelope.mode=chan[i].std.ex2.val; + rWrite(regMode[i],chan[i].envelope.mode); } if (chan[i].std.ex3.had) { chan[i].autoEnvNum=chan[i].std.ex3.val; @@ -296,29 +296,29 @@ void DivPlatformAY8930::tick(bool sysTick) { if (chan[i].keyOn) chan[i].keyOn=false; if (chan[i].keyOff) chan[i].keyOff=false; if (chan[i].freqChanged && chan[i].autoEnvNum>0 && chan[i].autoEnvDen>0) { - ayEnvPeriod[i]=(chan[i].freq*chan[i].autoEnvDen/chan[i].autoEnvNum)>>4; - immWrite(regPeriodL[i],ayEnvPeriod[i]); - immWrite(regPeriodH[i],ayEnvPeriod[i]>>8); + chan[i].envelope.period=(chan[i].freq*chan[i].autoEnvDen/chan[i].autoEnvNum)>>4; + immWrite(regPeriodL[i],chan[i].envelope.period); + immWrite(regPeriodH[i],chan[i].envelope.period>>8); } chan[i].freqChanged=false; } - if (ayEnvSlide[i]!=0) { - ayEnvSlideLow[i]+=ayEnvSlide[i]; - while (ayEnvSlideLow[i]>7) { - ayEnvSlideLow[i]-=8; - if (ayEnvPeriod[i]<0xffff) { - ayEnvPeriod[i]++; - immWrite(regPeriodL[i],ayEnvPeriod[i]); - immWrite(regPeriodH[i],ayEnvPeriod[i]>>8); + if (chan[i].envelope.slide!=0) { + chan[i].envelope.slideLow+=chan[i].envelope.slide; + while (chan[i].envelope.slideLow>7) { + chan[i].envelope.slideLow-=8; + if (chan[i].envelope.period<0xffff) { + chan[i].envelope.period++; + immWrite(regPeriodL[i],chan[i].envelope.period); + immWrite(regPeriodH[i],chan[i].envelope.period>>8); } } - while (ayEnvSlideLow[i]<-7) { - ayEnvSlideLow[i]+=8; - if (ayEnvPeriod[i]>0) { - ayEnvPeriod[i]--; - immWrite(regPeriodL[i],ayEnvPeriod[i]); - immWrite(regPeriodH[i],ayEnvPeriod[i]>>8); + while (chan[i].envelope.slideLow<-7) { + chan[i].envelope.slideLow+=8; + if (chan[i].envelope.period>0) { + chan[i].envelope.period--; + immWrite(regPeriodL[i],chan[i].envelope.period); + immWrite(regPeriodH[i],chan[i].envelope.period>>8); } } } @@ -435,8 +435,8 @@ int DivPlatformAY8930::dispatch(DivCommand c) { rWrite(0x06,c.value); break; case DIV_CMD_AY_ENVELOPE_SET: - ayEnvMode[c.chan]=c.value>>4; - rWrite(regMode[c.chan],ayEnvMode[c.chan]); + chan[c.chan].envelope.mode=c.value>>4; + rWrite(regMode[c.chan],chan[c.chan].envelope.mode); if (c.value&15) { chan[c.chan].psgMode|=4; } else { @@ -449,19 +449,19 @@ int DivPlatformAY8930::dispatch(DivCommand c) { } break; case DIV_CMD_AY_ENVELOPE_LOW: - ayEnvPeriod[c.chan]&=0xff00; - ayEnvPeriod[c.chan]|=c.value; - immWrite(regPeriodL[c.chan],ayEnvPeriod[c.chan]); - immWrite(regPeriodH[c.chan],ayEnvPeriod[c.chan]>>8); + chan[c.chan].envelope.period&=0xff00; + chan[c.chan].envelope.period|=c.value; + immWrite(regPeriodL[c.chan],chan[c.chan].envelope.period); + immWrite(regPeriodH[c.chan],chan[c.chan].envelope.period>>8); break; case DIV_CMD_AY_ENVELOPE_HIGH: - ayEnvPeriod[c.chan]&=0xff; - ayEnvPeriod[c.chan]|=c.value<<8; - immWrite(regPeriodL[c.chan],ayEnvPeriod[c.chan]); - immWrite(regPeriodH[c.chan],ayEnvPeriod[c.chan]>>8); + chan[c.chan].envelope.period&=0xff; + chan[c.chan].envelope.period|=c.value<<8; + immWrite(regPeriodL[c.chan],chan[c.chan].envelope.period); + immWrite(regPeriodH[c.chan],chan[c.chan].envelope.period>>8); break; case DIV_CMD_AY_ENVELOPE_SLIDE: - ayEnvSlide[c.chan]=c.value; + chan[c.chan].envelope.slide=c.value; break; case DIV_CMD_AY_NOISE_MASK_AND: ayNoiseAnd=c.value; @@ -526,9 +526,9 @@ void DivPlatformAY8930::muteChannel(int ch, bool mute) { void DivPlatformAY8930::forceIns() { for (int i=0; i<3; i++) { chan[i].insChanged=true; - immWrite(regPeriodL[i],ayEnvPeriod[i]); - immWrite(regPeriodH[i],ayEnvPeriod[i]>>8); - immWrite(regMode[i],ayEnvMode[i]); + immWrite(regPeriodL[i],chan[i].envelope.period); + immWrite(regPeriodH[i],chan[i].envelope.period>>8); + immWrite(regMode[i],chan[i].envelope.mode); } } @@ -556,10 +556,10 @@ void DivPlatformAY8930::reset() { chan[i]=DivPlatformAY8930::Channel(); chan[i].std.setEngine(parent); chan[i].vol=31; - ayEnvPeriod[i]=0; - ayEnvMode[i]=0; - ayEnvSlide[i]=0; - ayEnvSlideLow[i]=0; + chan[i].envelope.period=0; + chan[i].envelope.mode=0; + chan[i].envelope.slide=0; + chan[i].envelope.slideLow=0; } if (dumpWrites) { addWrite(0xffffffff,0); @@ -641,16 +641,23 @@ void DivPlatformAY8930::setFlags(unsigned int flags) { case 10: chipClock=2097152; break; + case 11: + chipClock=COLOR_NTSC; + break; + case 12: + chipClock=3600000; + break; default: chipClock=COLOR_NTSC/2.0; break; } - rate=chipClock/4; + rate=chipClock/8; for (int i=0; i<3; i++) { oscBuf[i]->rate=rate; } - stereo=flags>>6; + stereo=(flags>>6)&1; + clockSel=(flags>>7)&1; } int DivPlatformAY8930::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { @@ -664,6 +671,7 @@ int DivPlatformAY8930::init(DivEngine* p, int channels, int sugRate, unsigned in setFlags(flags); ay=new ay8930_device(rate); ay->device_start(); + ay->set_clock_sel(clockSel); ayBufLen=65536; for (int i=0; i<3; i++) ayBuf[i]=new short[ayBufLen]; reset(); diff --git a/src/engine/platform/ay8930.h b/src/engine/platform/ay8930.h index 5f477e12..67420f48 100644 --- a/src/engine/platform/ay8930.h +++ b/src/engine/platform/ay8930.h @@ -27,6 +27,17 @@ class DivPlatformAY8930: public DivDispatch { protected: struct Channel { + struct Envelope { + unsigned char mode; + unsigned short period; + short slideLow; + short slide; + Envelope(): + mode(0), + period(0), + slideLow(0), + slide(0) {} + } envelope; unsigned char freqH, freqL; int freq, baseFreq, note, pitch, pitch2; int ins; @@ -59,16 +70,12 @@ class DivPlatformAY8930: public DivDispatch { int delay; - bool extMode, stereo; + bool extMode, stereo, clockSel; bool ioPortA, ioPortB; unsigned char portAVal, portBVal; short oldWrites[32]; short pendingWrites[32]; - unsigned char ayEnvMode[3]; - unsigned short ayEnvPeriod[3]; - short ayEnvSlideLow[3]; - short ayEnvSlide[3]; short* ayBuf[3]; size_t ayBufLen; diff --git a/src/engine/platform/sound/ay8910.cpp b/src/engine/platform/sound/ay8910.cpp index 4d19e7de..bfd1b450 100644 --- a/src/engine/platform/sound/ay8910.cpp +++ b/src/engine/platform/sound/ay8910.cpp @@ -1021,10 +1021,8 @@ 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: - m_noise_or=m_regs[AY_NOISEOR]; + // No action required break; default: m_regs[r] = 0; // reserved, set as 0 @@ -1047,7 +1045,7 @@ void ay8910_device::sound_stream_update(short** outputs, int outLen) if (!m_ready) { for (int chan = 0; chan < m_streams; chan++) - memset(outputs[chan],0,outLen*sizeof(short)); + memset(outputs[chan],0,outLen*sizeof(short)); } /* The 8910 has three outputs, each output is the mix of one of the three */ @@ -1063,8 +1061,8 @@ void ay8910_device::sound_stream_update(short** outputs, int outLen) for (int chan = 0; chan < NUM_CHANNELS; chan++) { tone = &m_tone[chan]; - const int period = std::max(1,tone->period); - tone->count += is_expanded_mode() ? 16 : (m_feature & PSG_HAS_EXPANDED_MODE) ? 2 : 1; + const int period = std::max(1,tone->period) << 1; + tone->count += is_expanded_mode() ? 32 : (is_clock_divided() ? 1 : 2); while (tone->count >= period) { tone->duty_cycle = (tone->duty_cycle - 1) & 0x1f; @@ -1073,48 +1071,35 @@ void ay8910_device::sound_stream_update(short** outputs, int outLen) } } - m_count_noise++; - if (m_count_noise >= noise_period()) + if ((++m_count_noise) >= noise_period()) { /* toggle the prescaler output. Noise is no different to * channels. */ m_count_noise = 0; - m_prescale_noise = (m_prescale_noise + 1) & ((m_feature & PSG_HAS_EXPANDED_MODE) ? 3 : 1); + m_prescale_noise = (m_prescale_noise + 1) & (is_clock_divided() ? 3 : 1); - if (!m_prescale_noise || is_expanded_mode()) // AY8930 noise generator rate is twice compares as compatibility mode + if (is_expanded_mode()) // AY8930 noise generator rate is twice? compares as compatibility mode { - 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; - } + // 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. + if ((++m_noise_value) >= (((unsigned char)(m_rng) & noise_and()) | noise_or())) // Clock the noise value. + { + 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_out ^= 1; + + noise_rng_tick(); + } } + else if (!m_prescale_noise) + noise_rng_tick(); } for (int chan = 0; chan < NUM_CHANNELS; chan++) @@ -1130,8 +1115,7 @@ void ay8910_device::sound_stream_update(short** outputs, int outLen) if (envelope->holding == 0) { const int period = envelope->period * m_step; - envelope->count++; - if (envelope->count >= period) + if ((++envelope->count) >= period) { envelope->count = 0; envelope->step--; @@ -1263,20 +1247,14 @@ void ay8910_device::device_start() } -void ay8910_device::ay8910_reset_ym(bool ay8930) +void ay8910_device::ay8910_reset_ym() { m_active = false; m_register_latch = 0; - if (ay8930) { - m_rng = 0x1ffff; - } else { - m_rng = 1; - } + m_rng = (m_feature & PSG_HAS_EXPANDED_MODE) ? 0x1ffff : 1; m_mode = 0; // ay-3-8910 compatible mode - m_noise_and = 0xff; - m_noise_or = 0; - m_noise_value = 0; - m_noise_latch = 0; + m_noise_value = 0; + m_noise_out = 0; for (int chan = 0; chan < NUM_CHANNELS; chan++) { m_tone[chan].reset(); @@ -1335,7 +1313,7 @@ void ay8910_device::ay8910_write_ym(int addr, unsigned char data) unsigned char ay8910_device::ay8910_read_ym() { - int r = m_register_latch + get_register_bank(); + unsigned char r = m_register_latch + get_register_bank(); if (!m_active) return 0xff; // high impedance @@ -1380,7 +1358,7 @@ unsigned char ay8910_device::ay8910_read_ym() void ay8910_device::device_reset() { - ay8910_reset_ym(chip_type == AY8930); + ay8910_reset_ym(); } /************************************* @@ -1454,22 +1432,20 @@ ay8910_device::ay8910_device(unsigned int clock) ay8910_device::ay8910_device(device_type type, unsigned int clock, psg_type_t psg_type, int streams, int ioports, int feature) : chip_type(type), - m_type(psg_type), + m_type(psg_type), m_streams(streams), m_ready(0), m_active(false), m_register_latch(0), m_last_enable(0), m_prescale_noise(0), + m_noise_value(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_noise_out(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), + m_step( (feature & PSG_HAS_INTERNAL_DIVIDER) || (psg_type == PSG_TYPE_AY) ? 2 : 1), m_zero_is_off( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 1 : 0), m_par( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? &ay8910_param : &ym2149_param), m_par_env( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? &ay8910_param : &ym2149_param_env), @@ -1494,7 +1470,7 @@ void ay8910_device::set_type(psg_type_t psg_type) if (psg_type == PSG_TYPE_AY) { m_env_step_mask = 0x0f; - m_step = 2; + m_step = is_clock_divided() ? 4 : 2; m_zero_is_off = 1; m_par = &ay8910_param; m_par_env = &ay8910_param; @@ -1502,7 +1478,7 @@ void ay8910_device::set_type(psg_type_t psg_type) else { m_env_step_mask = 0x1f; - m_step = (m_feature & PSG_HAS_EXPANDED_MODE) ? 2 : 1; + m_step = is_clock_divided() ? 2 : 1; m_zero_is_off = 0; m_par = &ym2149_param; m_par_env = &ym2149_param_env; diff --git a/src/engine/platform/sound/ay8910.h b/src/engine/platform/sound/ay8910.h index c9de6da0..952d9415 100644 --- a/src/engine/platform/sound/ay8910.h +++ b/src/engine/platform/sound/ay8910.h @@ -97,7 +97,25 @@ public: void data_w(unsigned char data); // /RES - void reset_w(unsigned char data = 0) { ay8910_reset_ym(chip_type == AY8930); } + void reset_w(unsigned char data = 0) { ay8910_reset_ym(); } + + // Clock select pin + void set_clock_sel(bool clk_sel) + { + if (m_feature & PSG_PIN26_IS_CLKSEL) + { + if (clk_sel) + { + m_flags |= YM2149_PIN26_LOW; + set_type(m_type); + } + else + { + m_flags &= ~YM2149_PIN26_LOW; + set_type(m_type); + } + } + } // use this when BC1 == A0; here, BC1=0 selects 'data' and BC1=1 selects 'latch address' void data_address_w(int offset, unsigned char data) { ay8910_write_ym(~offset & 1, data); } // note that directly connecting BC1 to A0 puts data on 0 and address on 1 @@ -138,7 +156,7 @@ public: void ay8910_write_ym(int addr, unsigned char data); unsigned char ay8910_read_ym(); - void ay8910_reset_ym(bool ay8930); + void ay8910_reset_ym(); private: static constexpr int NUM_CHANNELS = 3; @@ -258,6 +276,18 @@ private: } }; + inline void noise_rng_tick() + { + // 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. + + if (m_feature & PSG_HAS_EXPANDED_MODE) // AY8930 LFSR algorithm is slightly different, verified from manual + m_rng = (m_rng >> 1) | ((BIT(m_rng, 0) ^ BIT(m_rng, 2)) << 16); + else + m_rng = (m_rng >> 1) | ((BIT(m_rng, 0) ^ BIT(m_rng, 3)) << 16); + } + // inlines inline bool tone_enable(int chan) { return BIT(m_regs[AY_ENABLE], chan); } inline unsigned char tone_volume(tone_t *tone) { return tone->volume & (is_expanded_mode() ? 0x1f : 0x0f); } @@ -267,11 +297,16 @@ private: inline bool noise_enable(int chan) { return BIT(m_regs[AY_ENABLE], 3 + chan); } inline unsigned char noise_period() { return is_expanded_mode() ? m_regs[AY_NOISEPER] & 0xff : m_regs[AY_NOISEPER] & 0x1f; } - inline unsigned char noise_output() { return is_expanded_mode() ? m_noise_latch & 1 : m_rng & 1; } + inline unsigned char noise_output() { return is_expanded_mode() ? m_noise_out & 1 : m_rng & 1; } inline bool is_expanded_mode() { return ((m_feature & PSG_HAS_EXPANDED_MODE) && ((m_mode & 0xe) == 0xa)); } inline unsigned char get_register_bank() { return is_expanded_mode() ? (m_mode & 0x1) << 4 : 0; } + inline unsigned char noise_and() { return m_regs[AY_NOISEAND] & 0xff; } + inline unsigned char noise_or() { return m_regs[AY_NOISEOR] & 0xff; } + + inline bool is_clock_divided() { return ((m_feature & PSG_HAS_INTERNAL_DIVIDER) || ((m_feature & PSG_PIN26_IS_CLKSEL) && (m_flags & YM2149_PIN26_LOW))); } + // internal helpers void set_type(psg_type_t psg_type); inline float mix_3D(); @@ -284,18 +319,16 @@ private: int m_ready; //sound_stream *m_channel; bool m_active; - int m_register_latch; + unsigned char m_register_latch; unsigned char m_regs[16 * 2]; int m_last_enable; tone_t m_tone[NUM_CHANNELS]; envelope_t m_envelope[NUM_CHANNELS]; 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; + signed short m_noise_value; + signed short m_count_noise; + unsigned int m_rng; + unsigned char m_noise_out; unsigned char m_mode; unsigned char m_env_step_mask; /* init parameters ... */ diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index 3dd24e0a..156a67cd 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -425,6 +425,15 @@ void FurnaceGUI::initSystemPresets() { 0 } )); + cat.systems.push_back(FurnaceGUISysDef( + "MSX + Darky", { + DIV_SYSTEM_AY8910, 64, 0, 16, + DIV_SYSTEM_AY8930, 64, 0, 139, // 3.58MHz + DIV_SYSTEM_AY8930, 64, 0, 140, // 3.58MHz or 3.6MHz selectable via register + // per-channel mixer (soft panning, post processing) isn't emulated at all + 0 + } + )); cat.systems.push_back(FurnaceGUISysDef( "ZX Spectrum (48K)", { DIV_SYSTEM_AY8910, 64, 0, 2, diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 8005ee0d..199c6217 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -219,6 +219,14 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool if (ImGui::RadioButton("2^21Hz (Game Boy)",(flags&15)==10)) { copyOfFlags=(flags&(~15))|10; + } + if (ImGui::RadioButton("3.58MHz (Darky)",(flags&15)==11)) { + copyOfFlags=(flags&(~15))|11; + + } + if (ImGui::RadioButton("3.6MHz (Darky)",(flags&15)==12)) { + copyOfFlags=(flags&(~15))|12; + } if (type==DIV_SYSTEM_AY8910) { ImGui::Text("Chip type:"); @@ -240,10 +248,17 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool } } bool stereo=flags&0x40; - ImGui::BeginDisabled((flags&0x30)==32); + ImGui::BeginDisabled((type==DIV_SYSTEM_AY8910) && ((flags&0x30)==32)); if (ImGui::Checkbox("Stereo##_AY_STEREO",&stereo)) { copyOfFlags=(flags&(~0x40))|(stereo?0x40:0); + } + ImGui::EndDisabled(); + bool clockSel=flags&0x80; + ImGui::BeginDisabled((type==DIV_SYSTEM_AY8910) && ((flags&0x30)!=16)); + if (ImGui::Checkbox("Half Clock divider##_AY_CLKSEL",&clockSel)) { + copyOfFlags=(flags&(~0x80))|(clockSel?0x80:0); + } ImGui::EndDisabled(); break; From 7b91045bac868b3482692dd905c4bc82731dec22 Mon Sep 17 00:00:00 2001 From: cam900 Date: Thu, 5 May 2022 13:38:10 +0900 Subject: [PATCH 02/13] Add documents Important: it will break previously baked modules with 5B and AY8930 --- src/engine/engine.h | 4 ++-- src/engine/song.h | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/engine/engine.h b/src/engine/engine.h index d1583894..169a3f02 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -45,8 +45,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev91" -#define DIV_ENGINE_VERSION 91 +#define DIV_VERSION "devff" // it breaks compatiblity +#define DIV_ENGINE_VERSION 0xff // for imports #define DIV_VERSION_MOD 0xff01 diff --git a/src/engine/song.h b/src/engine/song.h index c0852ea0..39d38869 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -215,13 +215,19 @@ struct DivSong { // - 8: 0.83MHz (Sunsoft 5B on PAL) // - 9: 1.10MHz (Gamate/VIC-20 PAL) // - 10: 2.097152MHz (Game Boy) + // - 11: 3.58MHz (Darky) + // - 12: 3.6MHz (Darky) // - bit 4-5: chip type (ignored on AY8930) // - 0: AY-3-8910 or similar // - 1: YM2149 // - 2: Sunsoft 5B - // - bit 6: stereo + // - 3: AY-3-8914 + // - bit 6: stereo (ignored on Sunsoft 5B) // - 0: mono // - 1: stereo ABC + // - bit 7: clock divider pin (YM2149, AY8930) + // - 0: high (disable divider) + // - 1: low (internally divided to half) // - SAA1099: // - bit 0-1: clock rate // - 0: 8MHz (SAM Coupé, Game Blaster) From 96f303ac9f9de3aeb93239e89a998d62c59602c0 Mon Sep 17 00:00:00 2001 From: cam900 Date: Thu, 5 May 2022 15:43:59 +0900 Subject: [PATCH 03/13] Fix configuration description --- src/gui/presets.cpp | 2 +- src/gui/sysConf.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index 156a67cd..60ca9448 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -257,7 +257,7 @@ void FurnaceGUI::initSystemPresets() { cat.systems.push_back(FurnaceGUISysDef( "NES with Sunsoft 5B", { DIV_SYSTEM_NES, 64, 0, 0, - DIV_SYSTEM_AY8910, 64, 0, 38, + DIV_SYSTEM_AY8910, 64, 0, 32, 0 } )); diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 199c6217..b38a1366 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -200,7 +200,7 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool copyOfFlags=(flags&(~15))|5; } - if (ImGui::RadioButton("0.89MHz (Sunsoft 5B)",(flags&15)==6)) { + if (ImGui::RadioButton("0.89MHz (Pre-divided Sunsoft 5B)",(flags&15)==6)) { copyOfFlags=(flags&(~15))|6; } @@ -208,7 +208,7 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool copyOfFlags=(flags&(~15))|7; } - if (ImGui::RadioButton("0.83MHz (Sunsoft 5B on PAL)",(flags&15)==8)) { + if (ImGui::RadioButton("0.83MHz (Pre-divided Sunsoft 5B on PAL)",(flags&15)==8)) { copyOfFlags=(flags&(~15))|8; } From 3c7ce65db7a351ced216066381ccd0bd4dee82a2 Mon Sep 17 00:00:00 2001 From: cam900 Date: Fri, 6 May 2022 19:18:55 +0900 Subject: [PATCH 04/13] it isn't necesaary --- src/engine/engine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/engine.h b/src/engine/engine.h index 11be4e96..e9a5db2e 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -45,7 +45,7 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev93" +#define DIV_VERSION "dev93" #define DIV_ENGINE_VERSION 93 // for imports From 0b0a6dbf282023df937fdd668e65e9d20ed85716 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 7 May 2022 23:00:22 +0900 Subject: [PATCH 05/13] Fix divider again, split tone and envelope clock divider --- src/engine/platform/ay8930.cpp | 4 ++-- src/engine/platform/sound/ay8910.cpp | 20 ++++++++++++-------- src/engine/platform/sound/ay8910.h | 14 +++++++------- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index 41c54c57..12259689 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -27,7 +27,7 @@ #define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;} #define immWrite2(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} } -#define CHIP_DIVIDER 8 +#define CHIP_DIVIDER (clockSel?8:4) const char* regCheatSheetAY8930[]={ "FreqL_A", "00", @@ -651,7 +651,7 @@ void DivPlatformAY8930::setFlags(unsigned int flags) { chipClock=COLOR_NTSC/2.0; break; } - rate=chipClock/8; + rate=chipClock/4; for (int i=0; i<3; i++) { oscBuf[i]->rate=rate; } diff --git a/src/engine/platform/sound/ay8910.cpp b/src/engine/platform/sound/ay8910.cpp index bfd1b450..b620eaf7 100644 --- a/src/engine/platform/sound/ay8910.cpp +++ b/src/engine/platform/sound/ay8910.cpp @@ -1061,8 +1061,8 @@ void ay8910_device::sound_stream_update(short** outputs, int outLen) for (int chan = 0; chan < NUM_CHANNELS; chan++) { tone = &m_tone[chan]; - const int period = std::max(1,tone->period) << 1; - tone->count += is_expanded_mode() ? 32 : (is_clock_divided() ? 1 : 2); + const int period = std::max(1,tone->period) * (m_step_mul << 1); + tone->count += is_expanded_mode() ? 32 : ((m_feature & PSG_HAS_EXPANDED_MODE) ? 1 : 2); while (tone->count >= period) { tone->duty_cycle = (tone->duty_cycle - 1) & 0x1f; @@ -1071,13 +1071,14 @@ void ay8910_device::sound_stream_update(short** outputs, int outLen) } } - if ((++m_count_noise) >= noise_period()) + const int period_noise = noise_period() * m_step_mul; + if ((++m_count_noise) >= period_noise) { /* toggle the prescaler output. Noise is no different to * channels. */ m_count_noise = 0; - m_prescale_noise = (m_prescale_noise + 1) & (is_clock_divided() ? 3 : 1); + m_prescale_noise = (m_prescale_noise + 1) & ((m_feature & PSG_HAS_EXPANDED_MODE) ? 3 : 1); if (is_expanded_mode()) // AY8930 noise generator rate is twice? compares as compatibility mode { @@ -1114,7 +1115,7 @@ void ay8910_device::sound_stream_update(short** outputs, int outLen) envelope = &m_envelope[chan]; if (envelope->holding == 0) { - const int period = envelope->period * m_step; + const int period = envelope->period * m_env_step_mul; if ((++envelope->count) >= period) { envelope->count = 0; @@ -1445,7 +1446,8 @@ ay8910_device::ay8910_device(device_type type, unsigned int clock, m_noise_out(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_INTERNAL_DIVIDER) || (psg_type == PSG_TYPE_AY) ? 2 : 1), + m_step_mul( (feature & PSG_HAS_INTERNAL_DIVIDER) ? 2 : 1), + m_env_step_mul( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? (m_step_mul << 1) : m_step_mul), m_zero_is_off( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 1 : 0), m_par( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? &ay8910_param : &ym2149_param), m_par_env( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? &ay8910_param : &ym2149_param_env), @@ -1470,7 +1472,7 @@ void ay8910_device::set_type(psg_type_t psg_type) if (psg_type == PSG_TYPE_AY) { m_env_step_mask = 0x0f; - m_step = is_clock_divided() ? 4 : 2; + m_env_step_mul = is_clock_divided() ? 4 : 2; m_zero_is_off = 1; m_par = &ay8910_param; m_par_env = &ay8910_param; @@ -1478,11 +1480,13 @@ void ay8910_device::set_type(psg_type_t psg_type) else { m_env_step_mask = 0x1f; - m_step = is_clock_divided() ? 2 : 1; + m_env_step_mul = is_clock_divided() ? 2 : 1; m_zero_is_off = 0; m_par = &ym2149_param; m_par_env = &ym2149_param_env; } + if (m_feature & PSG_HAS_EXPANDED_MODE) + m_env_step_mul <<= 1; } diff --git a/src/engine/platform/sound/ay8910.h b/src/engine/platform/sound/ay8910.h index 952d9415..b71d9ad9 100644 --- a/src/engine/platform/sound/ay8910.h +++ b/src/engine/platform/sound/ay8910.h @@ -105,15 +105,14 @@ public: if (m_feature & PSG_PIN26_IS_CLKSEL) { if (clk_sel) - { m_flags |= YM2149_PIN26_LOW; - set_type(m_type); - } else - { m_flags &= ~YM2149_PIN26_LOW; - set_type(m_type); - } + + m_step_mul = is_clock_divided() ? 2 : 1; + m_env_step_mul = (!(m_feature & PSG_HAS_EXPANDED_MODE)) && (m_type == PSG_TYPE_AY) ? (m_step_mul << 1) : m_step_mul; + if (m_feature & PSG_HAS_EXPANDED_MODE) + m_env_step_mul <<= 1; } } @@ -332,7 +331,8 @@ private: unsigned char m_mode; unsigned char m_env_step_mask; /* init parameters ... */ - int m_step; + int m_step_mul; + int m_env_step_mul; int m_zero_is_off; unsigned char m_vol_enabled[NUM_CHANNELS]; const ay_ym_param *m_par; From a8258d9a1ad667b48cd11691bc0c1f479810d6a5 Mon Sep 17 00:00:00 2001 From: cam900 Date: Tue, 10 May 2022 13:18:02 +0900 Subject: [PATCH 06/13] Divider related emulation core update, Fix init and period limitation --- .gitignore | 1 + src/engine/platform/ay.cpp | 3 +-- src/engine/platform/ay8930.cpp | 5 ++--- src/engine/platform/sound/ay8910.cpp | 30 +++++++++++++++------------- src/engine/platform/sound/ay8910.h | 27 +++++++++++++++---------- 5 files changed, 36 insertions(+), 30 deletions(-) diff --git a/.gitignore b/.gitignore index a5436e93..39aecb98 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ android/app/build/ android/app/.cxx/ .vs/ CMakeSettings.json +CMakePresets.json diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index 702cb00e..cb309d99 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -639,7 +639,7 @@ void DivPlatformAY8910::setFlags(unsigned int flags) { if (ay!=NULL) delete ay; switch ((flags>>4)&3) { case 1: - ay=new ym2149_device(rate); + ay=new ym2149_device(rate,clockSel); sunsoft=false; intellivision=false; break; @@ -660,7 +660,6 @@ void DivPlatformAY8910::setFlags(unsigned int flags) { break; } ay->device_start(); - ay->set_clock_sel(clockSel); stereo=(flags>>6)&1; } diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index 12259689..c8f46dd2 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -610,6 +610,7 @@ void DivPlatformAY8930::poke(std::vector& wlist) { } void DivPlatformAY8930::setFlags(unsigned int flags) { + clockSel=(flags>>7)&1; switch (flags&15) { case 1: chipClock=COLOR_PAL*2.0/5.0; @@ -657,7 +658,6 @@ void DivPlatformAY8930::setFlags(unsigned int flags) { } stereo=(flags>>6)&1; - clockSel=(flags>>7)&1; } int DivPlatformAY8930::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { @@ -669,9 +669,8 @@ int DivPlatformAY8930::init(DivEngine* p, int channels, int sugRate, unsigned in oscBuf[i]=new DivDispatchOscBuffer; } setFlags(flags); - ay=new ay8930_device(rate); + ay=new ay8930_device(rate,clockSel); ay->device_start(); - ay->set_clock_sel(clockSel); ayBufLen=65536; for (int i=0; i<3; i++) ayBuf[i]=new short[ayBufLen]; reset(); diff --git a/src/engine/platform/sound/ay8910.cpp b/src/engine/platform/sound/ay8910.cpp index b620eaf7..3dddf82c 100644 --- a/src/engine/platform/sound/ay8910.cpp +++ b/src/engine/platform/sound/ay8910.cpp @@ -1061,7 +1061,7 @@ void ay8910_device::sound_stream_update(short** outputs, int outLen) for (int chan = 0; chan < NUM_CHANNELS; chan++) { tone = &m_tone[chan]; - const int period = std::max(1,tone->period) * (m_step_mul << 1); + const int period = tone->period * (m_step_mul << 1); tone->count += is_expanded_mode() ? 32 : ((m_feature & PSG_HAS_EXPANDED_MODE) ? 1 : 2); while (tone->count >= period) { @@ -1071,7 +1071,7 @@ void ay8910_device::sound_stream_update(short** outputs, int outLen) } } - const int period_noise = noise_period() * m_step_mul; + const int period_noise = (int)(noise_period()) * m_step_mul; if ((++m_count_noise) >= period_noise) { /* toggle the prescaler output. Noise is no different to @@ -1431,7 +1431,7 @@ ay8910_device::ay8910_device(unsigned int clock) } ay8910_device::ay8910_device(device_type type, unsigned int clock, - psg_type_t psg_type, int streams, int ioports, int feature) + psg_type_t psg_type, int streams, int ioports, int feature, bool clk_sel) : chip_type(type), m_type(psg_type), m_streams(streams), @@ -1446,12 +1446,12 @@ ay8910_device::ay8910_device(device_type type, unsigned int clock, m_noise_out(0), m_mode(0), m_env_step_mask((!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 0x0f : 0x1f), - m_step_mul( (feature & PSG_HAS_INTERNAL_DIVIDER) ? 2 : 1), - m_env_step_mul( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? (m_step_mul << 1) : m_step_mul), + m_step_mul( ((feature & PSG_HAS_INTERNAL_DIVIDER) || ((feature & PSG_PIN26_IS_CLKSEL) && clk_sel)) ? 2 : 1), + m_env_step_mul( ((feature & PSG_HAS_EXPANDED_MODE) || (psg_type == PSG_TYPE_AY)) ? (m_step_mul << 1) : m_step_mul), m_zero_is_off( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 1 : 0), m_par( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? &ay8910_param : &ym2149_param), m_par_env( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? &ay8910_param : &ym2149_param_env), - m_flags(AY8910_LEGACY_OUTPUT), + m_flags(AY8910_LEGACY_OUTPUT | (((feature & PSG_PIN26_IS_CLKSEL) && clk_sel) ? YM2149_PIN26_LOW : 0)), m_feature(feature) { memset(&m_regs,0,sizeof(m_regs)); @@ -1463,10 +1463,10 @@ ay8910_device::ay8910_device(device_type type, unsigned int clock, m_res_load[0] = m_res_load[1] = m_res_load[2] = 1000; //Default values for resistor loads // TODO : measure ay8930 volume parameters (PSG_TYPE_YM for temporary 5 bit handling) - set_type((m_feature & PSG_HAS_EXPANDED_MODE) ? PSG_TYPE_YM : psg_type); + set_type((m_feature & PSG_HAS_EXPANDED_MODE) ? PSG_TYPE_YM : psg_type, clk_sel); } -void ay8910_device::set_type(psg_type_t psg_type) +void ay8910_device::set_type(psg_type_t psg_type, bool clk_sel) { m_type = psg_type; if (psg_type == PSG_TYPE_AY) @@ -1487,6 +1487,8 @@ void ay8910_device::set_type(psg_type_t psg_type) } if (m_feature & PSG_HAS_EXPANDED_MODE) m_env_step_mul <<= 1; + + set_clock_sel(clk_sel); } @@ -1515,24 +1517,24 @@ ay8914_device::ay8914_device(unsigned int clock) -ay8930_device::ay8930_device(unsigned int clock) - : ay8910_device(AY8930, clock, PSG_TYPE_YM, 3, 2, PSG_PIN26_IS_CLKSEL | PSG_HAS_EXPANDED_MODE) +ay8930_device::ay8930_device(unsigned int clock, bool clk_sel) + : ay8910_device(AY8930, clock, PSG_TYPE_YM, 3, 2, PSG_PIN26_IS_CLKSEL | PSG_HAS_EXPANDED_MODE, clk_sel) { } -ym2149_device::ym2149_device(unsigned int clock) - : ay8910_device(YM2149, clock, PSG_TYPE_YM, 3, 2, PSG_PIN26_IS_CLKSEL) +ym2149_device::ym2149_device(unsigned int clock, bool clk_sel) + : ay8910_device(YM2149, clock, PSG_TYPE_YM, 3, 2, PSG_PIN26_IS_CLKSEL, clk_sel) { } -ym3439_device::ym3439_device(unsigned int clock) - : ay8910_device(YM3439, clock, PSG_TYPE_YM, 3, 2, PSG_PIN26_IS_CLKSEL) +ym3439_device::ym3439_device(unsigned int clock, bool clk_sel) + : ay8910_device(YM3439, clock, PSG_TYPE_YM, 3, 2, PSG_PIN26_IS_CLKSEL, clk_sel) { } diff --git a/src/engine/platform/sound/ay8910.h b/src/engine/platform/sound/ay8910.h index b71d9ad9..314383f5 100644 --- a/src/engine/platform/sound/ay8910.h +++ b/src/engine/platform/sound/ay8910.h @@ -3,6 +3,10 @@ #ifndef MAME_SOUND_AY8910_H #define MAME_SOUND_AY8910_H +#pragma once + +#include + #define ALL_8910_CHANNELS -1 /* Internal resistance at Volume level 7. */ @@ -89,7 +93,8 @@ public: // configuration helpers void set_flags(int flags) { m_flags = flags; } - void set_psg_type(psg_type_t psg_type) { set_type(psg_type); } + void set_psg_type(psg_type_t psg_type) { set_type(psg_type, m_flags & YM2149_PIN26_LOW); } + void set_psg_type(psg_type_t psg_type, bool clk_sel) { set_type(psg_type, clk_sel); } void set_resistors_load(int res_load0, int res_load1, int res_load2) { m_res_load[0] = res_load0; m_res_load[1] = res_load1; m_res_load[2] = res_load2; } unsigned char data_r() { return ay8910_read_ym(); } @@ -144,7 +149,7 @@ public: // internal interface for PSG component of YM device // FIXME: these should be private, but vector06 accesses them directly - ay8910_device(device_type type, unsigned int clock, psg_type_t psg_type, int streams, int ioports, int feature = PSG_DEFAULT); + ay8910_device(device_type type, unsigned int clock, psg_type_t psg_type, int streams, int ioports, int feature = PSG_DEFAULT, bool clk_sel = false); // device-level overrides void device_start(); @@ -206,7 +211,7 @@ private: void reset() { - period = 0; + period = 1; volume = 0; duty = 0; count = 0; @@ -216,7 +221,7 @@ private: void set_period(unsigned char fine, unsigned char coarse) { - period = fine | (coarse << 8); + period = std::max(1, fine | (coarse << 8)); } void set_volume(unsigned char val) @@ -240,7 +245,7 @@ private: void reset() { - period = 0; + period = 1; count = 0; step = 0; volume = 0; @@ -252,7 +257,7 @@ private: void set_period(unsigned char fine, unsigned char coarse) { - period = fine | (coarse << 8); + period = std::max(1, fine | (coarse << 8)); } void set_shape(unsigned char shape, unsigned char mask) @@ -295,7 +300,7 @@ private: inline unsigned char get_envelope_chan(int chan) { return is_expanded_mode() ? chan : 0; } inline bool noise_enable(int chan) { return BIT(m_regs[AY_ENABLE], 3 + chan); } - inline unsigned char noise_period() { return is_expanded_mode() ? m_regs[AY_NOISEPER] & 0xff : m_regs[AY_NOISEPER] & 0x1f; } + inline unsigned char noise_period() { return std::max(1, is_expanded_mode() ? (m_regs[AY_NOISEPER] & 0xff) : (m_regs[AY_NOISEPER] & 0x1f)); } inline unsigned char noise_output() { return is_expanded_mode() ? m_noise_out & 1 : m_rng & 1; } inline bool is_expanded_mode() { return ((m_feature & PSG_HAS_EXPANDED_MODE) && ((m_mode & 0xe) == 0xa)); } @@ -307,7 +312,7 @@ private: inline bool is_clock_divided() { return ((m_feature & PSG_HAS_INTERNAL_DIVIDER) || ((m_feature & PSG_PIN26_IS_CLKSEL) && (m_flags & YM2149_PIN26_LOW))); } // internal helpers - void set_type(psg_type_t psg_type); + void set_type(psg_type_t psg_type, bool clk_sel); inline float mix_3D(); void ay8910_write_reg(int r, int v); void build_mixer_table(); @@ -370,19 +375,19 @@ public: class ay8930_device : public ay8910_device { public: - ay8930_device(unsigned int clock); + ay8930_device(unsigned int clock, bool clk_sel = false); }; class ym2149_device : public ay8910_device { public: - ym2149_device(unsigned int clock); + ym2149_device(unsigned int clock, bool clk_sel = false); }; class ym3439_device : public ay8910_device { public: - ym3439_device(unsigned int clock); + ym3439_device(unsigned int clock, bool clk_sel = false); }; class ymz284_device : public ay8910_device From f62f6ed77bbef759cc2cc4ecb28863b97b7e3134 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sun, 15 May 2022 00:58:00 +0900 Subject: [PATCH 07/13] Fix AY VGM output, Fix presets --- src/engine/vgmOps.cpp | 35 +++++++++++++++++++++++++++++++++-- src/gui/presets.cpp | 3 ++- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp index 53eddc1b..2916c103 100644 --- a/src/engine/vgmOps.cpp +++ b/src/engine/vgmOps.cpp @@ -906,11 +906,41 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { } break; case DIV_SYSTEM_AY8910: - case DIV_SYSTEM_AY8930: + case DIV_SYSTEM_AY8930: { if (!hasAY) { + bool hasClockDivider=false; // Configurable clock divider + bool hasStereo=true; // Stereo hasAY=disCont[i].dispatch->chipClock; - ayConfig=(song.system[i]==DIV_SYSTEM_AY8930)?3:0; ayFlags=1; + if (song.system[i]==DIV_SYSTEM_AY8930) { // AY8930 + ayConfig=0x03; + hasClockDivider=true; + } else { + switch ((song.systemFlags[i]>>4)&3) { + default: + case 0: // AY8910 + ayConfig=0x00; + break; + case 1: // YM2149 + ayConfig=0x10; + hasClockDivider=true; + break; + case 2: // Sunsoft 5B + ayConfig=0x10; + ayFlags|=0x12; // Clock internally divided, Single sound output + hasStereo=false; // due to above, can't be per-channel stereo configurable + break; + case 3: // AY8914 + ayConfig=0x04; + break; + } + } + if (hasClockDivider && ((song.systemFlags[i]>>7)&1)) { + ayFlags|=0x10; + } + if (hasStereo && ((song.systemFlags[i]>>6)&1)) { + ayFlags|=0x80; + } willExport[i]=true; } else if (!(hasAY&0x40000000)) { isSecond[i]=true; @@ -919,6 +949,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { howManyChips++; } break; + } case DIV_SYSTEM_SAA1099: if (!hasSAA) { hasSAA=disCont[i].dispatch->chipClock; diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index 021f1348..d04d329d 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -647,11 +647,12 @@ void FurnaceGUI::initSystemPresets() { "MSX + Darky", { DIV_SYSTEM_AY8910, 64, 0, 16, DIV_SYSTEM_AY8930, 64, 0, 139, // 3.58MHz - DIV_SYSTEM_AY8930, 64, 0, 140, // 3.58MHz or 3.6MHz selectable via register + DIV_SYSTEM_AY8930, 64, 0, 139, // 3.58MHz or 3.6MHz selectable via register // per-channel mixer (soft panning, post processing) isn't emulated at all 0 } )); + cat.systems.push_back(FurnaceGUISysDef( "MSX + SCC", { DIV_SYSTEM_AY8910, 64, 0, 16, DIV_SYSTEM_SCC, 64, 0, 0, From f63c072517be4405b336401a7386eca1211629e5 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 15 May 2022 01:55:25 -0500 Subject: [PATCH 08/13] fix build failure --- src/engine/engine.cpp | 1 + src/engine/platform/ym2203ext.cpp | 25 ++++++++----------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 01f1a3a2..e4a74d12 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -885,6 +885,7 @@ bool DivEngine::addSystem(DivSystem which) { return true; } +// TODO: maybe issue with subsongs? bool DivEngine::removeSystem(int index, bool preserveOrder) { if (song.systemLen<=1) { lastError="cannot remove the last one"; diff --git a/src/engine/platform/ym2203ext.cpp b/src/engine/platform/ym2203ext.cpp index 7948d51e..9ca77e9f 100644 --- a/src/engine/platform/ym2203ext.cpp +++ b/src/engine/platform/ym2203ext.cpp @@ -463,27 +463,17 @@ void DivPlatformYM2203Ext::muteChannel(int ch, bool mute) { } void DivPlatformYM2203Ext::forceIns() { - for (int i=0; i<6; i++) { + for (int i=0; i<3; i++) { for (int j=0; j<4; j++) { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; - if (i==2) { // extended channel - if (isOpMuted[j]) { - rWrite(baseAddr+0x40,127); - } else if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[j].vol&0x7f))/127)); - } else { - rWrite(baseAddr+0x40,op.tl); - } + if (isMuted[i]) { + rWrite(baseAddr+ADDR_TL,127); } else { - if (isMuted[i]) { - rWrite(baseAddr+ADDR_TL,127); + if (isOutput[chan[i].state.alg][j]) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); } else { - if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); - } else { - rWrite(baseAddr+ADDR_TL,op.tl); - } + rWrite(baseAddr+ADDR_TL,op.tl); } } rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); @@ -499,9 +489,10 @@ void DivPlatformYM2203Ext::forceIns() { chan[i].freqChanged=true; } } - for (int i=6; i<16; i++) { + for (int i=3; i<6; i++) { chan[i].insChanged=true; } + ay->forceIns(); ay->flushWrites(); for (DivRegWrite& i: ay->getRegisterWrites()) { From d5db7775bd2a49515938e7cae3ca2ad02fa187e7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 15 May 2022 02:00:36 -0500 Subject: [PATCH 09/13] OPN: wire up ExtCh system --- src/engine/dispatchContainer.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index d175b253..41adbb20 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -30,6 +30,7 @@ #include "platform/arcade.h" #include "platform/tx81z.h" #include "platform/ym2203.h" +#include "platform/ym2203ext.h" #include "platform/ym2608.h" #include "platform/ym2610.h" #include "platform/ym2610ext.h" @@ -240,6 +241,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do case DIV_SYSTEM_OPN: dispatch=new DivPlatformYM2203; break; + case DIV_SYSTEM_OPN_EXT: + dispatch=new DivPlatformYM2203Ext; + break; case DIV_SYSTEM_PC98: dispatch=new DivPlatformYM2608; break; From 5babb4fd1e75ae490d263104553f855b44358bb3 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 15 May 2022 02:02:31 -0500 Subject: [PATCH 10/13] BANK --- src/engine/platform/ym2608.cpp | 2 +- src/engine/platform/ym2608Interface.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/ym2608.cpp b/src/engine/platform/ym2608.cpp index 236e6a08..b7e18e69 100644 --- a/src/engine/platform/ym2608.cpp +++ b/src/engine/platform/ym2608.cpp @@ -1405,7 +1405,7 @@ const void* DivPlatformYM2608::getSampleMem(int index) { } size_t DivPlatformYM2608::getSampleMemCapacity(int index) { - return index == 0 ? 2097152 : 0; + return index == 0 ? 262144 : 0; } size_t DivPlatformYM2608::getSampleMemUsage(int index) { diff --git a/src/engine/platform/ym2608Interface.cpp b/src/engine/platform/ym2608Interface.cpp index 62cd8899..242721f0 100644 --- a/src/engine/platform/ym2608Interface.cpp +++ b/src/engine/platform/ym2608Interface.cpp @@ -31,7 +31,7 @@ uint8_t DivYM2608Interface::ymfm_external_read(ymfm::access_class type, uint32_t if (adpcmBMem==NULL) { return 0; } - return adpcmBMem[address&0xffffff]; + return adpcmBMem[address&0x3ffff]; default: return 0; } From 6d0abbfd52b691b8ea92e55c0094e1097f089b71 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 15 May 2022 02:37:33 -0500 Subject: [PATCH 11/13] GUI: fix inability to close subsongs --- src/gui/subSongs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/subSongs.cpp b/src/gui/subSongs.cpp index 21b15001..192cdb9d 100644 --- a/src/gui/subSongs.cpp +++ b/src/gui/subSongs.cpp @@ -9,7 +9,7 @@ void FurnaceGUI::drawSubSongs() { ImGui::SetNextWindowFocus(); nextWindow=GUI_WINDOW_NOTHING; } - if (!oscOpen) return; + if (!subSongsOpen) return; ImGui::SetNextWindowSizeConstraints(ImVec2(64.0f*dpiScale,32.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); if (ImGui::Begin("Subsongs",&subSongsOpen,ImGuiWindowFlags_NoScrollWithMouse|ImGuiWindowFlags_NoScrollbar)) { char id[1024]; From e3d0c517c8b84bb23f165f85acd59d93e07d9967 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 15 May 2022 02:37:39 -0500 Subject: [PATCH 12/13] GUI: AY8930 credits --- src/gui/about.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/about.cpp b/src/gui/about.cpp index ce543f5f..76fc80ad 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -91,7 +91,7 @@ const char* aboutLine[]={ "ymfm by Aaron Giles", "MAME SN76496 by Nicola Salmoria", "MAME AY-3-8910 by Couriersud", - "with AY8930 fixes by Eulous", + "with AY8930 fixes by Eulous, cam900 and Grauw", "MAME SAA1099 by Juergen Buchmueller and Manuel Abadia", "SAASound by Dave Hooper and Simon Owen", "SameBoy by Lior Halphon", From 25ab4467b4d4d222a35b3cdc6a3c647abaff3a9e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 15 May 2022 02:53:52 -0500 Subject: [PATCH 13/13] whoops --- src/gui/gui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 634482e9..d2d8a9e1 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2778,6 +2778,7 @@ bool FurnaceGUI::loop() { } if (ImGui::BeginMenu("window")) { if (ImGui::MenuItem("song information",BIND_FOR(GUI_ACTION_WINDOW_SONG_INFO),songInfoOpen)) songInfoOpen=!songInfoOpen; + if (ImGui::MenuItem("subsongs",BIND_FOR(GUI_ACTION_WINDOW_SUBSONGS),subSongsOpen)) subSongsOpen=!subSongsOpen; if (ImGui::MenuItem("instruments",BIND_FOR(GUI_ACTION_WINDOW_INS_LIST),insListOpen)) insListOpen=!insListOpen; if (ImGui::MenuItem("wavetables",BIND_FOR(GUI_ACTION_WINDOW_WAVE_LIST),waveListOpen)) waveListOpen=!waveListOpen; if (ImGui::MenuItem("samples",BIND_FOR(GUI_ACTION_WINDOW_SAMPLE_LIST),sampleListOpen)) sampleListOpen=!sampleListOpen;