Merge branch 'master' of https://github.com/tildearrow/furnace into es5506_alt

This commit is contained in:
cam900 2022-09-25 18:34:36 +09:00
commit 5a6cde65ed
21 changed files with 433 additions and 134 deletions

View file

@ -115,7 +115,7 @@ const unsigned char dacLogTableAY[256]={
void DivPlatformAY8910::runDAC() {
for (int i=0; i<3; i++) {
if (chan[i].psgMode.dac && chan[i].dac.sample!=-1) {
if (chan[i].active && chan[i].psgMode.dac && chan[i].dac.sample!=-1) {
chan[i].dac.period+=chan[i].dac.rate;
bool end=false;
bool changed=false;
@ -129,7 +129,7 @@ void DivPlatformAY8910::runDAC() {
break;
}
unsigned char dacData=dacLogTableAY[(unsigned char)s->data8[chan[i].dac.pos]^0x80];
chan[i].dac.out=MAX(0,dacData-(15-chan[i].outVol));
chan[i].dac.out=(chan[i].active && !isMuted[i])?MAX(0,dacData-(15-chan[i].outVol)):0;
if (prevOut!=chan[i].dac.out) {
prevOut=chan[i].dac.out;
changed=true;
@ -264,7 +264,7 @@ void DivPlatformAY8910::tick(bool sysTick) {
}
if (chan[i].std.wave.had) {
if (!chan[i].psgMode.dac) {
chan[i].psgMode=(chan[i].std.wave.val+1)&7;
chan[i].psgMode.val=(chan[i].std.wave.val+1)&7;
if (isMuted[i]) {
rWrite(0x08+i,0);
} else if (intellivision && (chan[i].psgMode.getEnvelope())) {
@ -336,8 +336,12 @@ void DivPlatformAY8910::tick(bool sysTick) {
if (chan[i].keyOn) {
//rWrite(16+i*5+1,((chan[i].duty&3)<<6)|(63-(ins->gb.soundLen&63)));
//rWrite(16+i*5+2,((chan[i].vol<<4))|(ins->gb.envLen&7)|((ins->gb.envDir&1)<<3));
if (chan[i].psgMode.val==0) {
chan[i].psgMode.val=1;
}
}
if (chan[i].keyOff) {
chan[i].psgMode.val=0;
rWrite(0x08+i,0);
}
rWrite((i)<<1,chan[i].freq&0xff);
@ -543,7 +547,7 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
case DIV_CMD_STD_NOISE_MODE:
if (!chan[c.chan].psgMode.dac) {
if (c.value<16) {
chan[c.chan].psgMode=(c.value+1)&7;
chan[c.chan].psgMode.val=(c.value+1)&7;
if (isMuted[c.chan]) {
rWrite(0x08+c.chan,0);
} else if (chan[c.chan].active) {

View file

@ -32,10 +32,15 @@ class DivPlatformAY8910: public DivDispatch {
inline unsigned char regRemap(unsigned char reg) { return intellivision?AY8914RegRemap[reg&0x0f]:reg&0x0f; }
struct Channel {
struct PSGMode {
unsigned char tone: 1;
unsigned char noise: 1;
unsigned char envelope: 1;
unsigned char dac: 1;
union {
struct {
unsigned char tone: 1;
unsigned char noise: 1;
unsigned char envelope: 1;
unsigned char dac: 1;
};
unsigned char val=1;
};
unsigned char getTone() {
return dac?0:(tone<<0);
@ -49,19 +54,8 @@ class DivPlatformAY8910: public DivDispatch {
return dac?0:(envelope<<2);
}
PSGMode& operator=(unsigned char s) {
tone=(s>>0)&1;
noise=(s>>1)&1;
envelope=(s>>2)&1;
dac=(s>>3)&1;
return *this;
}
PSGMode():
tone(1),
noise(0),
envelope(0),
dac(0) {}
val(1) {}
} psgMode;
struct DAC {

View file

@ -110,8 +110,8 @@ const unsigned char dacLogTableAY8930[256]={
};
void DivPlatformAY8930::runDAC() {
for (int i=0; i<3; i++) {
if (chan[i].psgMode.dac && chan[i].dac.sample!=-1) {
for (int i=0; i<3; i++) {
if (chan[i].active && chan[i].psgMode.dac && chan[i].dac.sample!=-1) {
chan[i].dac.period+=chan[i].dac.rate;
bool end=false;
bool changed=false;
@ -125,7 +125,7 @@ void DivPlatformAY8930::runDAC() {
break;
}
unsigned char dacData=dacLogTableAY8930[(unsigned char)s->data8[chan[i].dac.pos]^0x80];
chan[i].dac.out=MAX(0,dacData-(31-chan[i].outVol));
chan[i].dac.out=(chan[i].active && !isMuted[i])?MAX(0,dacData-(31-chan[i].outVol)):0;
if (prevOut!=chan[i].dac.out) {
prevOut=chan[i].dac.out;
changed=true;
@ -254,7 +254,7 @@ void DivPlatformAY8930::tick(bool sysTick) {
}
if (chan[i].std.wave.had) {
if (!chan[i].psgMode.dac) {
chan[i].psgMode=(chan[i].std.wave.val+1)&7;
chan[i].psgMode.val=(chan[i].std.wave.val+1)&7;
if (isMuted[i]) {
rWrite(0x08+i,0);
} else {
@ -333,12 +333,16 @@ void DivPlatformAY8930::tick(bool sysTick) {
}
if (chan[i].freq>65535) chan[i].freq=65535;
if (chan[i].keyOn) {
if (chan[i].psgMode.val==0) {
chan[i].psgMode.val=1;
}
if (chan[i].insChanged) {
if (!chan[i].std.ex1.will) immWrite(0x16+i,chan[i].duty);
chan[i].insChanged=false;
}
}
if (chan[i].keyOff) {
chan[i].psgMode.val=0;
rWrite(0x08+i,0);
}
rWrite((i)<<1,chan[i].freq&0xff);
@ -537,7 +541,7 @@ int DivPlatformAY8930::dispatch(DivCommand c) {
case DIV_CMD_STD_NOISE_MODE:
if (c.value<0x10) {
if (!chan[c.chan].psgMode.dac) {
chan[c.chan].psgMode=(c.value+1)&7;
chan[c.chan].psgMode.val=(c.value+1)&7;
if (isMuted[c.chan]) {
rWrite(0x08+c.chan,0);
} else if (chan[c.chan].active) {

View file

@ -40,10 +40,15 @@ class DivPlatformAY8930: public DivDispatch {
} envelope;
struct PSGMode {
unsigned char tone: 1;
unsigned char noise: 1;
unsigned char envelope: 1;
unsigned char dac: 1;
union {
struct {
unsigned char tone: 1;
unsigned char noise: 1;
unsigned char envelope: 1;
unsigned char dac: 1;
};
unsigned char val=1;
};
unsigned char getTone() {
return dac?0:(tone<<0);
@ -57,19 +62,8 @@ class DivPlatformAY8930: public DivDispatch {
return dac?0:(envelope<<2);
}
PSGMode& operator=(unsigned char s) {
tone=(s>>0)&1;
noise=(s>>1)&1;
envelope=(s>>2)&1;
dac=(s>>3)&1;
return *this;
}
PSGMode():
tone(1),
noise(0),
envelope(0),
dac(0) {}
val(1) {}
} psgMode;
struct DAC {

View file

@ -24,7 +24,7 @@
#define CHIP_FREQBASE 131072
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
#define chWrite(c,a,v) {rWrite((a)+(c)*16,v)}
#define sampleTableAddr(c) (sampleTableBase+(c)*4)
#define waveTableAddr(c) (sampleTableBase+8*4+(c)*9*16)
@ -94,19 +94,9 @@ void DivPlatformSNES::tick(bool sysTick) {
unsigned char kon=0;
unsigned char koff=0;
for (int i=0; i<8; i++) {
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA);
bool hadGain=chan[i].std.vol.had || chan[i].std.ex1.had || chan[i].std.ex2.had;
chan[i].std.next();
if (ins->type==DIV_INS_AMIGA && chan[i].std.vol.had) {
chWrite(i,7,MIN(127,chan[i].std.vol.val*2));
} else if (!chan[i].useEnv && hadGain) {
if (chan[i].std.ex1.val==0) {
// direct gain
chWrite(i,7,chan[i].std.vol.val);
} else {
// inc/dec
chWrite(i,7,chan[i].std.ex2.val|((chan[i].std.ex1.val-1)<<5)|0x80);
}
if (chan[i].std.vol.had) {
chan[i].outVol=VOL_SCALE_LOG(chan[i].vol&127,MIN(127,chan[i].std.vol.val),127);
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
@ -123,6 +113,10 @@ void DivPlatformSNES::tick(bool sysTick) {
chan[i].freqChanged=true;
}
}
if (chan[i].std.duty.had) {
noiseFreq=chan[i].std.duty.val;
writeControl=true;
}
if (chan[i].useWave && chan[i].std.wave.had) {
if (chan[i].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) {
chan[i].wave=chan[i].std.wave.val;
@ -146,7 +140,30 @@ void DivPlatformSNES::tick(bool sysTick) {
int val=chan[i].std.panR.val&0x7f;
chan[i].panR=(val<<1)|(val>>6);
}
if (chan[i].std.panL.had || chan[i].std.panR.had) {
bool hasInverted=false;
if (chan[i].std.ex1.had) {
if (chan[i].invertL!=(bool)(chan[i].std.ex1.val&16)) {
chan[i].invertL=chan[i].std.ex1.val&16;
hasInverted=true;
}
if (chan[i].invertR!=(bool)(chan[i].std.ex1.val&8)) {
chan[i].invertR=chan[i].std.ex1.val&8;
hasInverted=true;
}
if (chan[i].pitchMod!=(bool)(chan[i].std.ex1.val&4)) {
chan[i].pitchMod=chan[i].std.ex1.val&4;
writePitchMod=true;
}
if (chan[i].echo!=(bool)(chan[i].std.ex1.val&2)) {
chan[i].echo=chan[i].std.ex1.val&2;
writeEcho=true;
}
if (chan[i].noise!=(bool)(chan[i].std.ex1.val&1)) {
chan[i].noise=chan[i].std.ex1.val&1;
writeNoise=true;
}
}
if (chan[i].std.vol.had || chan[i].std.panL.had || chan[i].std.panR.had || hasInverted) {
writeOutVol(i);
}
if (chan[i].setPos) {
@ -164,17 +181,12 @@ void DivPlatformSNES::tick(bool sysTick) {
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
DivSample* s=parent->getSample(chan[i].sample);
double off=(s->centerRate>=1)?((double)s->centerRate/8363.0):1.0;
if (chan[i].useWave) off=(double)chan[i].wtLen/32.0;
chan[i].freq=(unsigned int)(off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE));
if (chan[i].freq>16383) chan[i].freq=16383;
if (chan[i].keyOn) {
unsigned int start, end, loop;
size_t tabAddr=sampleTableAddr(i);
if (chan[i].useEnv) {
chWrite(i,5,ins->snes.a|(ins->snes.d<<4)|0x80);
chWrite(i,6,ins->snes.r|(ins->snes.s<<5));
} else {
chWrite(i,5,0);
}
unsigned short tabAddr=sampleTableAddr(i);
if (chan[i].useWave) {
start=waveTableAddr(i);
loop=start;
@ -193,9 +205,6 @@ void DivPlatformSNES::tick(bool sysTick) {
sampleMem[tabAddr+1]=start>>8;
sampleMem[tabAddr+2]=loop&0xff;
sampleMem[tabAddr+3]=loop>>8;
if (!hadGain) {
chWrite(i,7,0x7f);
}
kon|=(1<<i);
chan[i].keyOn=false;
}
@ -210,6 +219,53 @@ void DivPlatformSNES::tick(bool sysTick) {
}
}
}
if (writeControl) {
unsigned char control=noiseFreq&0x1f;
rWrite(0x6c,control);
writeControl=false;
}
if (writeNoise) {
unsigned char noiseBits=(
(chan[0].noise?1:0)|
(chan[1].noise?2:0)|
(chan[2].noise?4:0)|
(chan[3].noise?8:0)|
(chan[4].noise?0x10:0)|
(chan[5].noise?0x20:0)|
(chan[6].noise?0x40:0)|
(chan[7].noise?0x80:0)
);
rWrite(0x3d,noiseBits);
writeNoise=false;
}
if (writePitchMod) {
unsigned char pitchModBits=(
(chan[0].pitchMod?1:0)|
(chan[1].pitchMod?2:0)|
(chan[2].pitchMod?4:0)|
(chan[3].pitchMod?8:0)|
(chan[4].pitchMod?0x10:0)|
(chan[5].pitchMod?0x20:0)|
(chan[6].pitchMod?0x40:0)|
(chan[7].pitchMod?0x80:0)
);
rWrite(0x2d,pitchModBits);
writePitchMod=false;
}
if (writeEcho) {
unsigned char echoBits=(
(chan[0].echo?1:0)|
(chan[1].echo?2:0)|
(chan[2].echo?4:0)|
(chan[3].echo?8:0)|
(chan[4].echo?0x10:0)|
(chan[5].echo?0x20:0)|
(chan[6].echo?0x40:0)|
(chan[7].echo?0x80:0)
);
rWrite(0x4d,echoBits);
writeEcho=false;
}
if (kon!=0) {
rWrite(0x4c,kon);
}
@ -220,7 +276,7 @@ void DivPlatformSNES::tick(bool sysTick) {
int DivPlatformSNES::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_SNES);
if (ins->amiga.useWave) {
chan[c.chan].useWave=true;
chan[c.chan].wtLen=ins->amiga.waveLen+1;
@ -239,6 +295,32 @@ int DivPlatformSNES::dispatch(DivCommand c) {
if (chan[c.chan].useWave || chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) {
chan[c.chan].sample=-1;
}
if (chan[c.chan].insChanged) {
chan[c.chan].state=ins->snes;
}
if (chan[c.chan].state.useEnv) {
chWrite(c.chan,5,chan[c.chan].state.a|(chan[c.chan].state.d<<4)|0x80);
chWrite(c.chan,6,chan[c.chan].state.r|(chan[c.chan].state.s<<5));
} else {
chWrite(c.chan,5,0);
switch (chan[c.chan].state.gainMode) {
case DivInstrumentSNES::GAIN_MODE_DIRECT:
chWrite(c.chan,7,chan[c.chan].state.gain&127);
break;
case DivInstrumentSNES::GAIN_MODE_DEC_LINEAR:
chWrite(c.chan,7,0x80|(chan[c.chan].state.gain&31));
break;
case DivInstrumentSNES::GAIN_MODE_INC_LINEAR:
chWrite(c.chan,7,0xc0|(chan[c.chan].state.gain&31));
break;
case DivInstrumentSNES::GAIN_MODE_DEC_LOG:
chWrite(c.chan,7,0xa0|(chan[c.chan].state.gain&31));
break;
case DivInstrumentSNES::GAIN_MODE_INC_INVLOG:
chWrite(c.chan,7,0xe0|(chan[c.chan].state.gain&31));
break;
}
}
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=round(NOTE_FREQUENCY(c.value));
chan[c.chan].freqChanged=true;
@ -247,11 +329,6 @@ int DivPlatformSNES::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (ins->type==DIV_INS_SNES) {
// initialize to max gain in case of direct gain mode macro without gain level macro
chan[c.chan].std.vol.val=0x7f;
chan[c.chan].useEnv=ins->snes.useEnv;
}
chan[c.chan].insChanged=false;
break;
}
@ -277,12 +354,6 @@ int DivPlatformSNES::dispatch(DivCommand c) {
writeOutVol(c.chan);
}
break;
// case DIV_CMD_GLOBAL_VOLUME:
// gblVolL=MIN(c.value,127);
// gblVolR=MIN(c.value,127);
// rWrite(0x0c,gblVolL);
// rWrite(0x1c,gblVolR);
// break;
case DIV_CMD_GET_VOLUME:
return chan[c.chan].vol;
break;
@ -326,7 +397,7 @@ int DivPlatformSNES::dispatch(DivCommand c) {
}
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SNES));
}
chan[c.chan].inPorta=c.value;
break;
@ -346,7 +417,7 @@ int DivPlatformSNES::dispatch(DivCommand c) {
void DivPlatformSNES::updateWave(int ch) {
// Due to the overflow bug in hardware's resampler, the written amplitude here is half of maximum
size_t pos=waveTableAddr(ch);
unsigned short pos=waveTableAddr(ch);
for (int i=0; i<chan[ch].wtLen/16; i++) {
sampleMem[pos++]=0xb0;
for (int j=0; j<8; j++) {
@ -359,12 +430,13 @@ void DivPlatformSNES::updateWave(int ch) {
}
void DivPlatformSNES::writeOutVol(int ch) {
// TODO negative (inverted) panning support
int outL=0;
int outR=0;
if (!isMuted[ch]) {
outL=chan[ch].vol*chan[ch].panL/255;
outR=chan[ch].vol*chan[ch].panR/255;
outL=(globalVolL*((chan[ch].outVol*chan[ch].panL)/127))/127;
outR=(globalVolR*((chan[ch].outVol*chan[ch].panR)/127))/127;
if (chan[ch].invertL) outL=-outL;
if (chan[ch].invertR) outR=-outR;
}
chWrite(ch,0,outL);
chWrite(ch,1,outR);
@ -380,8 +452,15 @@ void DivPlatformSNES::forceIns() {
chan[i].insChanged=true;
chan[i].freqChanged=true;
chan[i].sample=-1;
updateWave(i);
if (chan[i].active && chan[i].useWave) {
updateWave(i);
}
writeOutVol(i);
}
writeControl=true;
writeNoise=true;
writePitchMod=true;
writeEcho=true;
}
void* DivPlatformSNES::getChanState(int ch) {
@ -414,9 +493,9 @@ void DivPlatformSNES::reset() {
dsp.init(sampleMem);
dsp.set_output(NULL,0);
memset(regPool,0,128);
// TODO more initial values
// this can't be 0 or channel 1 won't play
// this can't be 0x100 either as that's used by SPC700 page 1 and the stack
// this may not even be 0x200 as some space will be taken by the playback routine and variables
sampleTableBase=0x200;
rWrite(0x5d,sampleTableBase>>8);
rWrite(0x0c,127); // global volume left
@ -430,6 +509,10 @@ void DivPlatformSNES::reset() {
writeOutVol(i);
chWrite(i,4,i); // source number
}
writeControl=false;
writeNoise=false;
writePitchMod=false;
writeEcho=false;
}
bool DivPlatformSNES::isStereo() {
@ -493,7 +576,7 @@ void DivPlatformSNES::renderSamples() {
int actualLength=MIN((int)(getSampleMemCapacity()-memPos)/9*9,length);
if (actualLength>0) {
s->offSNES=memPos;
memcpy(&sampleMem[memPos],s->data8,actualLength);
memcpy(&sampleMem[memPos],s->dataBRR,actualLength);
memPos+=actualLength;
}
if (actualLength<length) {
@ -506,6 +589,11 @@ void DivPlatformSNES::renderSamples() {
sampleMemLen=memPos;
}
void DivPlatformSNES::setFlags(unsigned int flags) {
globalVolL=127-(flags&127);
globalVolR=127-((flags>>8)&127);
}
int DivPlatformSNES::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
parent=p;
dumpWrites=false;
@ -517,6 +605,7 @@ int DivPlatformSNES::init(DivEngine* p, int channels, int sugRate, unsigned int
sampleMemLen=0;
chipClock=1024000;
rate=chipClock/32;
setFlags(flags);
reset();
return 8;
}

View file

@ -33,10 +33,10 @@ class DivPlatformSNES: public DivDispatch {
int sample, wave, ins;
int note;
int panL, panR;
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, useWave, setPos;
signed char vol;
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, useWave, setPos, noise, echo, pitchMod, invertL, invertR;
int vol, outVol;
int wtLen;
bool useEnv;
DivInstrumentSNES state;
DivMacroInt std;
DivWaveSynth ws;
void macroInit(DivInstrument* which) {
@ -53,8 +53,8 @@ class DivPlatformSNES: public DivDispatch {
wave(-1),
ins(-1),
note(0),
panL(255),
panR(255),
panL(127),
panR(127),
active(false),
insChanged(true),
freqChanged(false),
@ -63,15 +63,25 @@ class DivPlatformSNES: public DivDispatch {
inPorta(false),
useWave(false),
setPos(false),
noise(false),
echo(false),
pitchMod(false),
invertL(false),
invertR(false),
vol(127),
wtLen(16),
useEnv(false) {}
outVol(127),
wtLen(16) {}
};
Channel chan[8];
DivDispatchOscBuffer* oscBuf[8];
bool isMuted[8];
signed char gblVolL, gblVolR;
signed char globalVolL, globalVolR;
unsigned char noiseFreq;
size_t sampleTableBase;
bool writeControl;
bool writeNoise;
bool writePitchMod;
bool writeEcho;
struct QueuedWrite {
unsigned char addr;
@ -101,6 +111,7 @@ class DivPlatformSNES: public DivDispatch {
bool isStereo();
void notifyInsChange(int ins);
void notifyWaveChange(int wave);
void setFlags(unsigned int flags);
void notifyInsDeletion(void* ins);
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);

View file

@ -803,7 +803,7 @@ void SPC_DSP::run( int clocks_remain )
{
loop:
// GCC, why
#ifdef __GNUC__
#if defined(__GNUC__) && !defined(__clang__)
#define PHASE( n ) if ( n && !--clocks_remain ) break; __attribute__ ((fallthrough)); case n:
#else
#define PHASE( n ) if ( n && !--clocks_remain ) break; case n: