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/
.vs/
CMakeSettings.json
CMakePresets.json

View File

@ -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;

View File

@ -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";

View File

@ -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;
@ -632,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;
@ -654,7 +661,7 @@ void DivPlatformAY8910::setFlags(unsigned int flags) {
}
ay->device_start();
stereo=flags>>6;
stereo=(flags>>6)&1;
}
int DivPlatformAY8910::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {

View File

@ -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;

View File

@ -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 (clockSel?8:4)
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);
@ -610,6 +610,7 @@ void DivPlatformAY8930::poke(std::vector<DivRegWrite>& wlist) {
}
void DivPlatformAY8930::setFlags(unsigned int flags) {
clockSel=(flags>>7)&1;
switch (flags&15) {
case 1:
chipClock=COLOR_PAL*2.0/5.0;
@ -641,6 +642,12 @@ 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;
@ -650,7 +657,7 @@ void DivPlatformAY8930::setFlags(unsigned int flags) {
oscBuf[i]->rate=rate;
}
stereo=flags>>6;
stereo=(flags>>6)&1;
}
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;
}
setFlags(flags);
ay=new ay8930_device(rate);
ay=new ay8930_device(rate,clockSel);
ay->device_start();
ayBufLen=65536;
for (int i=0; i<3; i++) ayBuf[i]=new short[ayBufLen];

View File

@ -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;

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]);
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<int>(1,tone->period);
tone->count += is_expanded_mode() ? 16 : (m_feature & PSG_HAS_EXPANDED_MODE) ? 2 : 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)
{
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++;
if (m_count_noise >= noise_period())
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
* channels.
@ -1082,39 +1080,27 @@ void ay8910_device::sound_stream_update(short** outputs, int outLen)
m_count_noise = 0;
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.
// 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++)
@ -1129,9 +1115,8 @@ 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;
envelope->count++;
if (envelope->count >= period)
const int period = envelope->period * m_env_step_mul;
if ((++envelope->count) >= period)
{
envelope->count = 0;
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_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 +1314,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 +1359,7 @@ unsigned char ay8910_device::ay8910_read_ym()
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,
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_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_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));
@ -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
// 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)
{
m_env_step_mask = 0x0f;
m_step = 2;
m_env_step_mul = is_clock_divided() ? 4 : 2;
m_zero_is_off = 1;
m_par = &ay8910_param;
m_par_env = &ay8910_param;
@ -1502,11 +1480,15 @@ 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_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;
set_clock_sel(clk_sel);
}
@ -1535,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)
{
}

View File

@ -3,6 +3,10 @@
#ifndef MAME_SOUND_AY8910_H
#define MAME_SOUND_AY8910_H
#pragma once
#include <algorithm>
#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(); }
@ -97,7 +102,24 @@ 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;
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'
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
// 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();
@ -138,7 +160,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;
@ -189,7 +211,7 @@ private:
void reset()
{
period = 0;
period = 1;
volume = 0;
duty = 0;
count = 0;
@ -199,7 +221,7 @@ private:
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)
@ -223,7 +245,7 @@ private:
void reset()
{
period = 0;
period = 1;
count = 0;
step = 0;
volume = 0;
@ -235,7 +257,7 @@ private:
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)
@ -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
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); }
@ -266,14 +300,19 @@ 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_output() { return is_expanded_mode() ? m_noise_latch & 1 : m_rng & 1; }
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_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);
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();
@ -284,22 +323,21 @@ 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 ... */
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;
@ -337,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

View File

@ -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()) {

View File

@ -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) {

View File

@ -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;
}

View File

@ -262,13 +262,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)

View File

@ -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;

View File

@ -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",

View File

@ -2794,6 +2794,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;

View File

@ -461,7 +461,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
}
));
@ -643,6 +643,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, 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,

View File

@ -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];

View File

@ -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;
}
@ -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;