mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-05 04:15:05 +00:00
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?
This commit is contained in:
parent
28e7b86728
commit
4f1ebc3b6e
8 changed files with 178 additions and 122 deletions
|
@ -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<DivRegWrite>& 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) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -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<int>(1,tone->period);
|
||||
tone->count += is_expanded_mode() ? 16 : (m_feature & PSG_HAS_EXPANDED_MODE) ? 2 : 1;
|
||||
const int period = std::max<int>(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) {
|
||||
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_latch ^= 1;
|
||||
m_noise_out ^= 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;
|
||||
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_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();
|
||||
}
|
||||
|
||||
/*************************************
|
||||
|
@ -1461,15 +1439,13 @@ ay8910_device::ay8910_device(device_type type, unsigned int clock,
|
|||
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;
|
||||
|
|
|
@ -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 ... */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue