Merge branch 'master' into feature/Moar-patch-bank-support-part3

This commit is contained in:
James Alan Nguyen 2022-05-15 18:46:45 +10:00
commit e6ad01b0f3
19 changed files with 272 additions and 172 deletions

1
.gitignore vendored
View File

@ -17,3 +17,4 @@ android/app/build/
android/app/.cxx/ android/app/.cxx/
.vs/ .vs/
CMakeSettings.json CMakeSettings.json
CMakePresets.json

View File

@ -30,6 +30,7 @@
#include "platform/arcade.h" #include "platform/arcade.h"
#include "platform/tx81z.h" #include "platform/tx81z.h"
#include "platform/ym2203.h" #include "platform/ym2203.h"
#include "platform/ym2203ext.h"
#include "platform/ym2608.h" #include "platform/ym2608.h"
#include "platform/ym2610.h" #include "platform/ym2610.h"
#include "platform/ym2610ext.h" #include "platform/ym2610ext.h"
@ -240,6 +241,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
case DIV_SYSTEM_OPN: case DIV_SYSTEM_OPN:
dispatch=new DivPlatformYM2203; dispatch=new DivPlatformYM2203;
break; break;
case DIV_SYSTEM_OPN_EXT:
dispatch=new DivPlatformYM2203Ext;
break;
case DIV_SYSTEM_PC98: case DIV_SYSTEM_PC98:
dispatch=new DivPlatformYM2608; dispatch=new DivPlatformYM2608;
break; break;

View File

@ -885,6 +885,7 @@ bool DivEngine::addSystem(DivSystem which) {
return true; return true;
} }
// TODO: maybe issue with subsongs?
bool DivEngine::removeSystem(int index, bool preserveOrder) { bool DivEngine::removeSystem(int index, bool preserveOrder) {
if (song.systemLen<=1) { if (song.systemLen<=1) {
lastError="cannot remove the last one"; lastError="cannot remove the last one";

View File

@ -27,7 +27,7 @@
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;} #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 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[]={ const char* regCheatSheetAY[]={
"FreqL_A", "0", "FreqL_A", "0",
@ -589,6 +589,7 @@ void DivPlatformAY8910::poke(std::vector<DivRegWrite>& wlist) {
} }
void DivPlatformAY8910::setFlags(unsigned int flags) { void DivPlatformAY8910::setFlags(unsigned int flags) {
clockSel=(flags>>7)&1;
switch (flags&15) { switch (flags&15) {
case 1: case 1:
chipClock=COLOR_PAL*2.0/5.0; chipClock=COLOR_PAL*2.0/5.0;
@ -620,6 +621,12 @@ void DivPlatformAY8910::setFlags(unsigned int flags) {
case 10: case 10:
chipClock=2097152; chipClock=2097152;
break; break;
case 11:
chipClock=COLOR_NTSC;
break;
case 12:
chipClock=3600000;
break;
default: default:
chipClock=COLOR_NTSC/2.0; chipClock=COLOR_NTSC/2.0;
break; break;
@ -632,7 +639,7 @@ void DivPlatformAY8910::setFlags(unsigned int flags) {
if (ay!=NULL) delete ay; if (ay!=NULL) delete ay;
switch ((flags>>4)&3) { switch ((flags>>4)&3) {
case 1: case 1:
ay=new ym2149_device(rate); ay=new ym2149_device(rate,clockSel);
sunsoft=false; sunsoft=false;
intellivision=false; intellivision=false;
break; break;
@ -654,7 +661,7 @@ void DivPlatformAY8910::setFlags(unsigned int flags) {
} }
ay->device_start(); ay->device_start();
stereo=flags>>6; stereo=(flags>>6)&1;
} }
int DivPlatformAY8910::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { int DivPlatformAY8910::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {

View File

@ -70,7 +70,7 @@ class DivPlatformAY8910: public DivDispatch {
int delay; int delay;
bool extMode; bool extMode;
bool stereo, sunsoft, intellivision; bool stereo, sunsoft, intellivision, clockSel;
bool ioPortA, ioPortB; bool ioPortA, ioPortB;
unsigned char portAVal, portBVal; unsigned char portAVal, portBVal;

View File

@ -27,7 +27,7 @@
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;} #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 immWrite2(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
#define CHIP_DIVIDER 4 #define CHIP_DIVIDER (clockSel?8:4)
const char* regCheatSheetAY8930[]={ const char* regCheatSheetAY8930[]={
"FreqL_A", "00", "FreqL_A", "00",
@ -64,7 +64,7 @@ const char* regCheatSheetAY8930[]={
void DivPlatformAY8930::immWrite(unsigned char a, unsigned char v) { void DivPlatformAY8930::immWrite(unsigned char a, unsigned char v) {
if ((int)bank!=(a>>4)) { if ((int)bank!=(a>>4)) {
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) { if (a==0x0d) {
immWrite2(0x0d,0xa0|(bank<<4)|(v&15)); immWrite2(0x0d,0xa0|(bank<<4)|(v&15));
@ -258,8 +258,8 @@ void DivPlatformAY8930::tick(bool sysTick) {
rWrite(0x16+i,chan[i].std.ex1.val); rWrite(0x16+i,chan[i].std.ex1.val);
} }
if (chan[i].std.ex2.had) { if (chan[i].std.ex2.had) {
ayEnvMode[i]=chan[i].std.ex2.val; chan[i].envelope.mode=chan[i].std.ex2.val;
rWrite(regMode[i],ayEnvMode[i]); rWrite(regMode[i],chan[i].envelope.mode);
} }
if (chan[i].std.ex3.had) { if (chan[i].std.ex3.had) {
chan[i].autoEnvNum=chan[i].std.ex3.val; 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].keyOn) chan[i].keyOn=false;
if (chan[i].keyOff) chan[i].keyOff=false; if (chan[i].keyOff) chan[i].keyOff=false;
if (chan[i].freqChanged && chan[i].autoEnvNum>0 && chan[i].autoEnvDen>0) { if (chan[i].freqChanged && chan[i].autoEnvNum>0 && chan[i].autoEnvDen>0) {
ayEnvPeriod[i]=(chan[i].freq*chan[i].autoEnvDen/chan[i].autoEnvNum)>>4; chan[i].envelope.period=(chan[i].freq*chan[i].autoEnvDen/chan[i].autoEnvNum)>>4;
immWrite(regPeriodL[i],ayEnvPeriod[i]); immWrite(regPeriodL[i],chan[i].envelope.period);
immWrite(regPeriodH[i],ayEnvPeriod[i]>>8); immWrite(regPeriodH[i],chan[i].envelope.period>>8);
} }
chan[i].freqChanged=false; chan[i].freqChanged=false;
} }
if (ayEnvSlide[i]!=0) { if (chan[i].envelope.slide!=0) {
ayEnvSlideLow[i]+=ayEnvSlide[i]; chan[i].envelope.slideLow+=chan[i].envelope.slide;
while (ayEnvSlideLow[i]>7) { while (chan[i].envelope.slideLow>7) {
ayEnvSlideLow[i]-=8; chan[i].envelope.slideLow-=8;
if (ayEnvPeriod[i]<0xffff) { if (chan[i].envelope.period<0xffff) {
ayEnvPeriod[i]++; chan[i].envelope.period++;
immWrite(regPeriodL[i],ayEnvPeriod[i]); immWrite(regPeriodL[i],chan[i].envelope.period);
immWrite(regPeriodH[i],ayEnvPeriod[i]>>8); immWrite(regPeriodH[i],chan[i].envelope.period>>8);
} }
} }
while (ayEnvSlideLow[i]<-7) { while (chan[i].envelope.slideLow<-7) {
ayEnvSlideLow[i]+=8; chan[i].envelope.slideLow+=8;
if (ayEnvPeriod[i]>0) { if (chan[i].envelope.period>0) {
ayEnvPeriod[i]--; chan[i].envelope.period--;
immWrite(regPeriodL[i],ayEnvPeriod[i]); immWrite(regPeriodL[i],chan[i].envelope.period);
immWrite(regPeriodH[i],ayEnvPeriod[i]>>8); immWrite(regPeriodH[i],chan[i].envelope.period>>8);
} }
} }
} }
@ -435,8 +435,8 @@ int DivPlatformAY8930::dispatch(DivCommand c) {
rWrite(0x06,c.value); rWrite(0x06,c.value);
break; break;
case DIV_CMD_AY_ENVELOPE_SET: case DIV_CMD_AY_ENVELOPE_SET:
ayEnvMode[c.chan]=c.value>>4; chan[c.chan].envelope.mode=c.value>>4;
rWrite(regMode[c.chan],ayEnvMode[c.chan]); rWrite(regMode[c.chan],chan[c.chan].envelope.mode);
if (c.value&15) { if (c.value&15) {
chan[c.chan].psgMode|=4; chan[c.chan].psgMode|=4;
} else { } else {
@ -449,19 +449,19 @@ int DivPlatformAY8930::dispatch(DivCommand c) {
} }
break; break;
case DIV_CMD_AY_ENVELOPE_LOW: case DIV_CMD_AY_ENVELOPE_LOW:
ayEnvPeriod[c.chan]&=0xff00; chan[c.chan].envelope.period&=0xff00;
ayEnvPeriod[c.chan]|=c.value; chan[c.chan].envelope.period|=c.value;
immWrite(regPeriodL[c.chan],ayEnvPeriod[c.chan]); immWrite(regPeriodL[c.chan],chan[c.chan].envelope.period);
immWrite(regPeriodH[c.chan],ayEnvPeriod[c.chan]>>8); immWrite(regPeriodH[c.chan],chan[c.chan].envelope.period>>8);
break; break;
case DIV_CMD_AY_ENVELOPE_HIGH: case DIV_CMD_AY_ENVELOPE_HIGH:
ayEnvPeriod[c.chan]&=0xff; chan[c.chan].envelope.period&=0xff;
ayEnvPeriod[c.chan]|=c.value<<8; chan[c.chan].envelope.period|=c.value<<8;
immWrite(regPeriodL[c.chan],ayEnvPeriod[c.chan]); immWrite(regPeriodL[c.chan],chan[c.chan].envelope.period);
immWrite(regPeriodH[c.chan],ayEnvPeriod[c.chan]>>8); immWrite(regPeriodH[c.chan],chan[c.chan].envelope.period>>8);
break; break;
case DIV_CMD_AY_ENVELOPE_SLIDE: case DIV_CMD_AY_ENVELOPE_SLIDE:
ayEnvSlide[c.chan]=c.value; chan[c.chan].envelope.slide=c.value;
break; break;
case DIV_CMD_AY_NOISE_MASK_AND: case DIV_CMD_AY_NOISE_MASK_AND:
ayNoiseAnd=c.value; ayNoiseAnd=c.value;
@ -526,9 +526,9 @@ void DivPlatformAY8930::muteChannel(int ch, bool mute) {
void DivPlatformAY8930::forceIns() { void DivPlatformAY8930::forceIns() {
for (int i=0; i<3; i++) { for (int i=0; i<3; i++) {
chan[i].insChanged=true; chan[i].insChanged=true;
immWrite(regPeriodL[i],ayEnvPeriod[i]); immWrite(regPeriodL[i],chan[i].envelope.period);
immWrite(regPeriodH[i],ayEnvPeriod[i]>>8); immWrite(regPeriodH[i],chan[i].envelope.period>>8);
immWrite(regMode[i],ayEnvMode[i]); immWrite(regMode[i],chan[i].envelope.mode);
} }
} }
@ -556,10 +556,10 @@ void DivPlatformAY8930::reset() {
chan[i]=DivPlatformAY8930::Channel(); chan[i]=DivPlatformAY8930::Channel();
chan[i].std.setEngine(parent); chan[i].std.setEngine(parent);
chan[i].vol=31; chan[i].vol=31;
ayEnvPeriod[i]=0; chan[i].envelope.period=0;
ayEnvMode[i]=0; chan[i].envelope.mode=0;
ayEnvSlide[i]=0; chan[i].envelope.slide=0;
ayEnvSlideLow[i]=0; chan[i].envelope.slideLow=0;
} }
if (dumpWrites) { if (dumpWrites) {
addWrite(0xffffffff,0); addWrite(0xffffffff,0);
@ -610,6 +610,7 @@ void DivPlatformAY8930::poke(std::vector<DivRegWrite>& wlist) {
} }
void DivPlatformAY8930::setFlags(unsigned int flags) { void DivPlatformAY8930::setFlags(unsigned int flags) {
clockSel=(flags>>7)&1;
switch (flags&15) { switch (flags&15) {
case 1: case 1:
chipClock=COLOR_PAL*2.0/5.0; chipClock=COLOR_PAL*2.0/5.0;
@ -641,6 +642,12 @@ void DivPlatformAY8930::setFlags(unsigned int flags) {
case 10: case 10:
chipClock=2097152; chipClock=2097152;
break; break;
case 11:
chipClock=COLOR_NTSC;
break;
case 12:
chipClock=3600000;
break;
default: default:
chipClock=COLOR_NTSC/2.0; chipClock=COLOR_NTSC/2.0;
break; break;
@ -650,7 +657,7 @@ void DivPlatformAY8930::setFlags(unsigned int flags) {
oscBuf[i]->rate=rate; oscBuf[i]->rate=rate;
} }
stereo=flags>>6; stereo=(flags>>6)&1;
} }
int DivPlatformAY8930::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { int DivPlatformAY8930::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
@ -662,7 +669,7 @@ int DivPlatformAY8930::init(DivEngine* p, int channels, int sugRate, unsigned in
oscBuf[i]=new DivDispatchOscBuffer; oscBuf[i]=new DivDispatchOscBuffer;
} }
setFlags(flags); setFlags(flags);
ay=new ay8930_device(rate); ay=new ay8930_device(rate,clockSel);
ay->device_start(); ay->device_start();
ayBufLen=65536; ayBufLen=65536;
for (int i=0; i<3; i++) ayBuf[i]=new short[ayBufLen]; for (int i=0; i<3; i++) ayBuf[i]=new short[ayBufLen];

View File

@ -27,6 +27,17 @@
class DivPlatformAY8930: public DivDispatch { class DivPlatformAY8930: public DivDispatch {
protected: protected:
struct Channel { 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; unsigned char freqH, freqL;
int freq, baseFreq, note, pitch, pitch2; int freq, baseFreq, note, pitch, pitch2;
int ins; int ins;
@ -59,16 +70,12 @@ class DivPlatformAY8930: public DivDispatch {
int delay; int delay;
bool extMode, stereo; bool extMode, stereo, clockSel;
bool ioPortA, ioPortB; bool ioPortA, ioPortB;
unsigned char portAVal, portBVal; unsigned char portAVal, portBVal;
short oldWrites[32]; short oldWrites[32];
short pendingWrites[32]; short pendingWrites[32];
unsigned char ayEnvMode[3];
unsigned short ayEnvPeriod[3];
short ayEnvSlideLow[3];
short ayEnvSlide[3];
short* ayBuf[3]; short* ayBuf[3];
size_t ayBufLen; size_t ayBufLen;

View File

@ -1021,10 +1021,8 @@ void ay8910_device::ay8910_write_reg(int r, int v)
m_tone[2].set_duty(m_regs[AY_CDUTY]); m_tone[2].set_duty(m_regs[AY_CDUTY]);
break; break;
case AY_NOISEAND: case AY_NOISEAND:
m_noise_and=m_regs[AY_NOISEAND];
break;
case AY_NOISEOR: case AY_NOISEOR:
m_noise_or=m_regs[AY_NOISEOR]; // No action required
break; break;
default: default:
m_regs[r] = 0; // reserved, set as 0 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) if (!m_ready)
{ {
for (int chan = 0; chan < m_streams; chan++) 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 */ /* 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++) for (int chan = 0; chan < NUM_CHANNELS; chan++)
{ {
tone = &m_tone[chan]; tone = &m_tone[chan];
const int period = std::max<int>(1,tone->period); const int period = tone->period * (m_step_mul << 1);
tone->count += is_expanded_mode() ? 16 : (m_feature & PSG_HAS_EXPANDED_MODE) ? 2 : 1; tone->count += is_expanded_mode() ? 32 : ((m_feature & PSG_HAS_EXPANDED_MODE) ? 1 : 2);
while (tone->count >= period) while (tone->count >= period)
{ {
tone->duty_cycle = (tone->duty_cycle - 1) & 0x1f; tone->duty_cycle = (tone->duty_cycle - 1) & 0x1f;
@ -1073,8 +1071,8 @@ void ay8910_device::sound_stream_update(short** outputs, int outLen)
} }
} }
m_count_noise++; const int period_noise = (int)(noise_period()) * m_step_mul;
if (m_count_noise >= noise_period()) if ((++m_count_noise) >= period_noise)
{ {
/* toggle the prescaler output. Noise is no different to /* toggle the prescaler output. Noise is no different to
* channels. * channels.
@ -1082,39 +1080,27 @@ void ay8910_device::sound_stream_update(short** outputs, int outLen)
m_count_noise = 0; 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) & ((m_feature & PSG_HAS_EXPANDED_MODE) ? 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.
// 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.
// 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.
// 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 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.
// 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.
unsigned int noiseValuePeriod = ((m_rng & 0xFF & m_noise_and) | m_noise_or); {
m_noise_value = 0;
// Clock the noise value.
if (m_noise_value >= noiseValuePeriod) { // When everything is finally said and done, a 1bit latch is flipped.
m_noise_value = 0; // This is the final output of the noise, to be multiplied by the tone and envelope generators of the channel.
m_noise_out ^= 1;
// 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. noise_rng_tick();
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;
}
} }
else if (!m_prescale_noise)
noise_rng_tick();
} }
for (int chan = 0; chan < NUM_CHANNELS; chan++) for (int chan = 0; chan < NUM_CHANNELS; chan++)
@ -1129,9 +1115,8 @@ void ay8910_device::sound_stream_update(short** outputs, int outLen)
envelope = &m_envelope[chan]; envelope = &m_envelope[chan];
if (envelope->holding == 0) if (envelope->holding == 0)
{ {
const int period = envelope->period * m_step; const int period = envelope->period * m_env_step_mul;
envelope->count++; if ((++envelope->count) >= period)
if (envelope->count >= period)
{ {
envelope->count = 0; envelope->count = 0;
envelope->step--; envelope->step--;
@ -1263,20 +1248,14 @@ void ay8910_device::device_start()
} }
void ay8910_device::ay8910_reset_ym(bool ay8930) void ay8910_device::ay8910_reset_ym()
{ {
m_active = false; m_active = false;
m_register_latch = 0; m_register_latch = 0;
if (ay8930) { m_rng = (m_feature & PSG_HAS_EXPANDED_MODE) ? 0x1ffff : 1;
m_rng = 0x1ffff;
} else {
m_rng = 1;
}
m_mode = 0; // ay-3-8910 compatible mode m_mode = 0; // ay-3-8910 compatible mode
m_noise_and = 0xff; m_noise_value = 0;
m_noise_or = 0; m_noise_out = 0;
m_noise_value = 0;
m_noise_latch = 0;
for (int chan = 0; chan < NUM_CHANNELS; chan++) for (int chan = 0; chan < NUM_CHANNELS; chan++)
{ {
m_tone[chan].reset(); m_tone[chan].reset();
@ -1335,7 +1314,7 @@ void ay8910_device::ay8910_write_ym(int addr, unsigned char data)
unsigned char ay8910_device::ay8910_read_ym() 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 if (!m_active) return 0xff; // high impedance
@ -1380,7 +1359,7 @@ unsigned char ay8910_device::ay8910_read_ym()
void ay8910_device::device_reset() void ay8910_device::device_reset()
{ {
ay8910_reset_ym(chip_type == AY8930); ay8910_reset_ym();
} }
/************************************* /*************************************
@ -1452,28 +1431,27 @@ ay8910_device::ay8910_device(unsigned int clock)
} }
ay8910_device::ay8910_device(device_type type, 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), : chip_type(type),
m_type(psg_type), m_type(psg_type),
m_streams(streams), m_streams(streams),
m_ready(0), m_ready(0),
m_active(false), m_active(false),
m_register_latch(0), m_register_latch(0),
m_last_enable(0), m_last_enable(0),
m_prescale_noise(0), m_prescale_noise(0),
m_noise_value(0),
m_count_noise(0), m_count_noise(0),
m_rng(0), m_rng(0),
m_noise_and(0), m_noise_out(0),
m_noise_or(0),
m_noise_value(0),
m_noise_latch(0),
m_mode(0), m_mode(0),
m_env_step_mask((!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 0x0f : 0x1f), 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_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_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( (!(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_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) m_feature(feature)
{ {
memset(&m_regs,0,sizeof(m_regs)); memset(&m_regs,0,sizeof(m_regs));
@ -1485,16 +1463,16 @@ 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 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) // 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; m_type = psg_type;
if (psg_type == PSG_TYPE_AY) if (psg_type == PSG_TYPE_AY)
{ {
m_env_step_mask = 0x0f; m_env_step_mask = 0x0f;
m_step = 2; m_env_step_mul = is_clock_divided() ? 4 : 2;
m_zero_is_off = 1; m_zero_is_off = 1;
m_par = &ay8910_param; m_par = &ay8910_param;
m_par_env = &ay8910_param; m_par_env = &ay8910_param;
@ -1502,11 +1480,15 @@ void ay8910_device::set_type(psg_type_t psg_type)
else else
{ {
m_env_step_mask = 0x1f; m_env_step_mask = 0x1f;
m_step = (m_feature & PSG_HAS_EXPANDED_MODE) ? 2 : 1; m_env_step_mul = is_clock_divided() ? 2 : 1;
m_zero_is_off = 0; m_zero_is_off = 0;
m_par = &ym2149_param; m_par = &ym2149_param;
m_par_env = &ym2149_param_env; m_par_env = &ym2149_param_env;
} }
if (m_feature & PSG_HAS_EXPANDED_MODE)
m_env_step_mul <<= 1;
set_clock_sel(clk_sel);
} }
@ -1535,24 +1517,24 @@ ay8914_device::ay8914_device(unsigned int clock)
ay8930_device::ay8930_device(unsigned int clock) 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) : 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) ym2149_device::ym2149_device(unsigned int clock, bool clk_sel)
: ay8910_device(YM2149, clock, PSG_TYPE_YM, 3, 2, PSG_PIN26_IS_CLKSEL) : ay8910_device(YM2149, clock, PSG_TYPE_YM, 3, 2, PSG_PIN26_IS_CLKSEL, clk_sel)
{ {
} }
ym3439_device::ym3439_device(unsigned int clock) ym3439_device::ym3439_device(unsigned int clock, bool clk_sel)
: ay8910_device(YM3439, clock, PSG_TYPE_YM, 3, 2, PSG_PIN26_IS_CLKSEL) : ay8910_device(YM3439, clock, PSG_TYPE_YM, 3, 2, PSG_PIN26_IS_CLKSEL, clk_sel)
{ {
} }

View File

@ -3,6 +3,10 @@
#ifndef MAME_SOUND_AY8910_H #ifndef MAME_SOUND_AY8910_H
#define MAME_SOUND_AY8910_H #define MAME_SOUND_AY8910_H
#pragma once
#include <algorithm>
#define ALL_8910_CHANNELS -1 #define ALL_8910_CHANNELS -1
/* Internal resistance at Volume level 7. */ /* Internal resistance at Volume level 7. */
@ -89,7 +93,8 @@ public:
// configuration helpers // configuration helpers
void set_flags(int flags) { m_flags = flags; } 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; } 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(); } unsigned char data_r() { return ay8910_read_ym(); }
@ -97,7 +102,24 @@ public:
void data_w(unsigned char data); void data_w(unsigned char data);
// /RES // /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;
else
m_flags &= ~YM2149_PIN26_LOW;
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;
}
}
// use this when BC1 == A0; here, BC1=0 selects 'data' and BC1=1 selects 'latch address' // 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 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
@ -127,7 +149,7 @@ public:
// internal interface for PSG component of YM device // internal interface for PSG component of YM device
// FIXME: these should be private, but vector06 accesses them directly // 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 // device-level overrides
void device_start(); void device_start();
@ -138,7 +160,7 @@ public:
void ay8910_write_ym(int addr, unsigned char data); void ay8910_write_ym(int addr, unsigned char data);
unsigned char ay8910_read_ym(); unsigned char ay8910_read_ym();
void ay8910_reset_ym(bool ay8930); void ay8910_reset_ym();
private: private:
static constexpr int NUM_CHANNELS = 3; static constexpr int NUM_CHANNELS = 3;
@ -189,7 +211,7 @@ private:
void reset() void reset()
{ {
period = 0; period = 1;
volume = 0; volume = 0;
duty = 0; duty = 0;
count = 0; count = 0;
@ -199,7 +221,7 @@ private:
void set_period(unsigned char fine, unsigned char coarse) void set_period(unsigned char fine, unsigned char coarse)
{ {
period = fine | (coarse << 8); period = std::max<unsigned int>(1, fine | (coarse << 8));
} }
void set_volume(unsigned char val) void set_volume(unsigned char val)
@ -223,7 +245,7 @@ private:
void reset() void reset()
{ {
period = 0; period = 1;
count = 0; count = 0;
step = 0; step = 0;
volume = 0; volume = 0;
@ -235,7 +257,7 @@ private:
void set_period(unsigned char fine, unsigned char coarse) void set_period(unsigned char fine, unsigned char coarse)
{ {
period = fine | (coarse << 8); period = std::max<unsigned int>(1, fine | (coarse << 8));
} }
void set_shape(unsigned char shape, unsigned char mask) void set_shape(unsigned char shape, unsigned char mask)
@ -258,6 +280,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 // inlines
inline bool tone_enable(int chan) { return BIT(m_regs[AY_ENABLE], chan); } 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); } inline unsigned char tone_volume(tone_t *tone) { return tone->volume & (is_expanded_mode() ? 0x1f : 0x0f); }
@ -266,14 +300,19 @@ private:
inline unsigned char get_envelope_chan(int chan) { return is_expanded_mode() ? chan : 0; } 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 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<unsigned char>(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_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 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 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 // 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(); inline float mix_3D();
void ay8910_write_reg(int r, int v); void ay8910_write_reg(int r, int v);
void build_mixer_table(); void build_mixer_table();
@ -284,22 +323,21 @@ private:
int m_ready; int m_ready;
//sound_stream *m_channel; //sound_stream *m_channel;
bool m_active; bool m_active;
int m_register_latch; unsigned char m_register_latch;
unsigned char m_regs[16 * 2]; unsigned char m_regs[16 * 2];
int m_last_enable; int m_last_enable;
tone_t m_tone[NUM_CHANNELS]; tone_t m_tone[NUM_CHANNELS];
envelope_t m_envelope[NUM_CHANNELS]; envelope_t m_envelope[NUM_CHANNELS];
unsigned char m_prescale_noise; unsigned char m_prescale_noise;
int m_count_noise; signed short m_noise_value;
int m_rng; signed short m_count_noise;
unsigned int m_noise_and; unsigned int m_rng;
unsigned int m_noise_or; unsigned char m_noise_out;
unsigned int m_noise_value;
unsigned int m_noise_latch;
unsigned char m_mode; unsigned char m_mode;
unsigned char m_env_step_mask; unsigned char m_env_step_mask;
/* init parameters ... */ /* init parameters ... */
int m_step; int m_step_mul;
int m_env_step_mul;
int m_zero_is_off; int m_zero_is_off;
unsigned char m_vol_enabled[NUM_CHANNELS]; unsigned char m_vol_enabled[NUM_CHANNELS];
const ay_ym_param *m_par; const ay_ym_param *m_par;
@ -337,19 +375,19 @@ public:
class ay8930_device : public ay8910_device class ay8930_device : public ay8910_device
{ {
public: public:
ay8930_device(unsigned int clock); ay8930_device(unsigned int clock, bool clk_sel = false);
}; };
class ym2149_device : public ay8910_device class ym2149_device : public ay8910_device
{ {
public: public:
ym2149_device(unsigned int clock); ym2149_device(unsigned int clock, bool clk_sel = false);
}; };
class ym3439_device : public ay8910_device class ym3439_device : public ay8910_device
{ {
public: public:
ym3439_device(unsigned int clock); ym3439_device(unsigned int clock, bool clk_sel = false);
}; };
class ymz284_device : public ay8910_device class ymz284_device : public ay8910_device

View File

@ -463,27 +463,17 @@ void DivPlatformYM2203Ext::muteChannel(int ch, bool mute) {
} }
void DivPlatformYM2203Ext::forceIns() { void DivPlatformYM2203Ext::forceIns() {
for (int i=0; i<6; i++) { for (int i=0; i<3; i++) {
for (int j=0; j<4; j++) { for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j]; unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (i==2) { // extended channel if (isMuted[i]) {
if (isOpMuted[j]) { rWrite(baseAddr+ADDR_TL,127);
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);
}
} else { } else {
if (isMuted[i]) { if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127); rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
} else { } else {
if (isOutput[chan[i].state.alg][j]) { rWrite(baseAddr+ADDR_TL,op.tl);
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
} }
} }
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
@ -499,9 +489,10 @@ void DivPlatformYM2203Ext::forceIns() {
chan[i].freqChanged=true; chan[i].freqChanged=true;
} }
} }
for (int i=6; i<16; i++) { for (int i=3; i<6; i++) {
chan[i].insChanged=true; chan[i].insChanged=true;
} }
ay->forceIns(); ay->forceIns();
ay->flushWrites(); ay->flushWrites();
for (DivRegWrite& i: ay->getRegisterWrites()) { for (DivRegWrite& i: ay->getRegisterWrites()) {

View File

@ -1405,7 +1405,7 @@ const void* DivPlatformYM2608::getSampleMem(int index) {
} }
size_t DivPlatformYM2608::getSampleMemCapacity(int index) { size_t DivPlatformYM2608::getSampleMemCapacity(int index) {
return index == 0 ? 2097152 : 0; return index == 0 ? 262144 : 0;
} }
size_t DivPlatformYM2608::getSampleMemUsage(int index) { size_t DivPlatformYM2608::getSampleMemUsage(int index) {

View File

@ -31,7 +31,7 @@ uint8_t DivYM2608Interface::ymfm_external_read(ymfm::access_class type, uint32_t
if (adpcmBMem==NULL) { if (adpcmBMem==NULL) {
return 0; return 0;
} }
return adpcmBMem[address&0xffffff]; return adpcmBMem[address&0x3ffff];
default: default:
return 0; return 0;
} }

View File

@ -262,13 +262,19 @@ struct DivSong {
// - 8: 0.83MHz (Sunsoft 5B on PAL) // - 8: 0.83MHz (Sunsoft 5B on PAL)
// - 9: 1.10MHz (Gamate/VIC-20 PAL) // - 9: 1.10MHz (Gamate/VIC-20 PAL)
// - 10: 2.097152MHz (Game Boy) // - 10: 2.097152MHz (Game Boy)
// - 11: 3.58MHz (Darky)
// - 12: 3.6MHz (Darky)
// - bit 4-5: chip type (ignored on AY8930) // - bit 4-5: chip type (ignored on AY8930)
// - 0: AY-3-8910 or similar // - 0: AY-3-8910 or similar
// - 1: YM2149 // - 1: YM2149
// - 2: Sunsoft 5B // - 2: Sunsoft 5B
// - bit 6: stereo // - 3: AY-3-8914
// - bit 6: stereo (ignored on Sunsoft 5B)
// - 0: mono // - 0: mono
// - 1: stereo ABC // - 1: stereo ABC
// - bit 7: clock divider pin (YM2149, AY8930)
// - 0: high (disable divider)
// - 1: low (internally divided to half)
// - SAA1099: // - SAA1099:
// - bit 0-1: clock rate // - bit 0-1: clock rate
// - 0: 8MHz (SAM Coupé, Game Blaster) // - 0: 8MHz (SAM Coupé, Game Blaster)

View File

@ -906,11 +906,41 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
} }
break; break;
case DIV_SYSTEM_AY8910: case DIV_SYSTEM_AY8910:
case DIV_SYSTEM_AY8930: case DIV_SYSTEM_AY8930: {
if (!hasAY) { if (!hasAY) {
bool hasClockDivider=false; // Configurable clock divider
bool hasStereo=true; // Stereo
hasAY=disCont[i].dispatch->chipClock; hasAY=disCont[i].dispatch->chipClock;
ayConfig=(song.system[i]==DIV_SYSTEM_AY8930)?3:0;
ayFlags=1; 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; willExport[i]=true;
} else if (!(hasAY&0x40000000)) { } else if (!(hasAY&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
@ -919,6 +949,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
howManyChips++; howManyChips++;
} }
break; break;
}
case DIV_SYSTEM_SAA1099: case DIV_SYSTEM_SAA1099:
if (!hasSAA) { if (!hasSAA) {
hasSAA=disCont[i].dispatch->chipClock; hasSAA=disCont[i].dispatch->chipClock;

View File

@ -91,7 +91,7 @@ const char* aboutLine[]={
"ymfm by Aaron Giles", "ymfm by Aaron Giles",
"MAME SN76496 by Nicola Salmoria", "MAME SN76496 by Nicola Salmoria",
"MAME AY-3-8910 by Couriersud", "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", "MAME SAA1099 by Juergen Buchmueller and Manuel Abadia",
"SAASound by Dave Hooper and Simon Owen", "SAASound by Dave Hooper and Simon Owen",
"SameBoy by Lior Halphon", "SameBoy by Lior Halphon",

View File

@ -2794,6 +2794,7 @@ bool FurnaceGUI::loop() {
} }
if (ImGui::BeginMenu("window")) { if (ImGui::BeginMenu("window")) {
if (ImGui::MenuItem("song information",BIND_FOR(GUI_ACTION_WINDOW_SONG_INFO),songInfoOpen)) songInfoOpen=!songInfoOpen; 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("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("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; if (ImGui::MenuItem("samples",BIND_FOR(GUI_ACTION_WINDOW_SAMPLE_LIST),sampleListOpen)) sampleListOpen=!sampleListOpen;

View File

@ -461,7 +461,7 @@ void FurnaceGUI::initSystemPresets() {
cat.systems.push_back(FurnaceGUISysDef( cat.systems.push_back(FurnaceGUISysDef(
"NES with Sunsoft 5B", { "NES with Sunsoft 5B", {
DIV_SYSTEM_NES, 64, 0, 0, DIV_SYSTEM_NES, 64, 0, 0,
DIV_SYSTEM_AY8910, 64, 0, 38, DIV_SYSTEM_AY8910, 64, 0, 32,
0 0
} }
)); ));
@ -643,6 +643,15 @@ void FurnaceGUI::initSystemPresets() {
0 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, 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( cat.systems.push_back(FurnaceGUISysDef(
"MSX + SCC", { "MSX + SCC", {
DIV_SYSTEM_AY8910, 64, 0, 16, DIV_SYSTEM_AY8910, 64, 0, 16,

View File

@ -9,7 +9,7 @@ void FurnaceGUI::drawSubSongs() {
ImGui::SetNextWindowFocus(); ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING; nextWindow=GUI_WINDOW_NOTHING;
} }
if (!oscOpen) return; if (!subSongsOpen) return;
ImGui::SetNextWindowSizeConstraints(ImVec2(64.0f*dpiScale,32.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); ImGui::SetNextWindowSizeConstraints(ImVec2(64.0f*dpiScale,32.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale));
if (ImGui::Begin("Subsongs",&subSongsOpen,ImGuiWindowFlags_NoScrollWithMouse|ImGuiWindowFlags_NoScrollbar)) { if (ImGui::Begin("Subsongs",&subSongsOpen,ImGuiWindowFlags_NoScrollWithMouse|ImGuiWindowFlags_NoScrollbar)) {
char id[1024]; char id[1024];

View File

@ -200,7 +200,7 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
copyOfFlags=(flags&(~15))|5; 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; copyOfFlags=(flags&(~15))|6;
} }
@ -208,7 +208,7 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
copyOfFlags=(flags&(~15))|7; 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; copyOfFlags=(flags&(~15))|8;
} }
@ -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)) { if (ImGui::RadioButton("2^21Hz (Game Boy)",(flags&15)==10)) {
copyOfFlags=(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) { if (type==DIV_SYSTEM_AY8910) {
ImGui::Text("Chip type:"); ImGui::Text("Chip type:");
@ -240,10 +248,17 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
} }
} }
bool stereo=flags&0x40; 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)) { if (ImGui::Checkbox("Stereo##_AY_STEREO",&stereo)) {
copyOfFlags=(flags&(~0x40))|(stereo?0x40:0); 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(); ImGui::EndDisabled();
break; break;