redo opnx csm

This commit is contained in:
Eknous-P 2024-10-09 17:28:58 +04:00 committed by tildearrow
parent 901bb110cc
commit 5f47166012
17 changed files with 572 additions and 147 deletions

View file

@ -374,6 +374,16 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
} else {
((DivPlatformYM2610Ext*)dispatch)->setCombo(eng->getConfInt("opnbCore",1));
}
((DivPlatformYM2610Ext*)dispatch)->setCSM(0);
break;
case DIV_SYSTEM_YM2610_CSM:
dispatch=new DivPlatformYM2610Ext;
if (isRender) {
((DivPlatformYM2610Ext*)dispatch)->setCombo(eng->getConfInt("opnCoreRender",1)==1);
} else {
((DivPlatformYM2610Ext*)dispatch)->setCombo(eng->getConfInt("opnCore",1)==1);
}
((DivPlatformYM2610Ext*)dispatch)->setCSM(1);
break;
case DIV_SYSTEM_YM2610B:
dispatch=new DivPlatformYM2610B;
@ -390,6 +400,16 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
} else {
((DivPlatformYM2610BExt*)dispatch)->setCombo(eng->getConfInt("opnbCore",1));
}
((DivPlatformYM2610BExt*)dispatch)->setCSM(0);
break;
case DIV_SYSTEM_YM2610B_CSM:
dispatch=new DivPlatformYM2610BExt;
if (isRender) {
((DivPlatformYM2610BExt*)dispatch)->setCombo(eng->getConfInt("opnCoreRender",1)==1);
} else {
((DivPlatformYM2610BExt*)dispatch)->setCombo(eng->getConfInt("opnCore",1)==1);
}
((DivPlatformYM2610BExt*)dispatch)->setCSM(1);
break;
case DIV_SYSTEM_AMIGA:
dispatch=new DivPlatformAmiga;
@ -431,6 +451,16 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
} else {
((DivPlatformYM2203Ext*)dispatch)->setCombo(eng->getConfInt("opn1Core",1));
}
((DivPlatformYM2203Ext*)dispatch)->setCSM(0);
break;
case DIV_SYSTEM_YM2203_CSM:
dispatch=new DivPlatformYM2203Ext;
if (isRender) {
((DivPlatformYM2203Ext*)dispatch)->setCombo(eng->getConfInt("opnCoreRender",1)==1);
} else {
((DivPlatformYM2203Ext*)dispatch)->setCombo(eng->getConfInt("opnCore",1)==1);
}
((DivPlatformYM2203Ext*)dispatch)->setCSM(1);
break;
case DIV_SYSTEM_YM2608:
dispatch=new DivPlatformYM2608;
@ -447,6 +477,16 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
} else {
((DivPlatformYM2608Ext*)dispatch)->setCombo(eng->getConfInt("opnaCore",1));
}
((DivPlatformYM2608Ext*)dispatch)->setCSM(0);
break;
case DIV_SYSTEM_YM2608_CSM:
dispatch=new DivPlatformYM2608Ext;
if (isRender) {
((DivPlatformYM2608Ext*)dispatch)->setCombo(eng->getConfInt("opnaCoreRender",1));
} else {
((DivPlatformYM2608Ext*)dispatch)->setCombo(eng->getConfInt("opnaCore",1));
}
((DivPlatformYM2608Ext*)dispatch)->setCSM(1);
break;
case DIV_SYSTEM_OPLL:
case DIV_SYSTEM_OPLL_DRUMS:

View file

@ -148,7 +148,7 @@ class DivPlatformOPN: public DivPlatformFMBase {
pan(3) {}
};
const int extChanOffs, psgChanOffs, adpcmAChanOffs, adpcmBChanOffs, chanNum;
int extChanOffs, psgChanOffs, adpcmAChanOffs, adpcmBChanOffs, chanNum; // i really wanted to keep this constant...
double fmFreqBase;
unsigned int fmDivBase;

View file

@ -237,8 +237,8 @@ void DivPlatformYM2203::acquire_combo(short** buf, size_t len) {
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(fm_nuked.ch_out[i]<<1,-32768,32767);
}
for (int i=3; i<6; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=fmout.data[i-2]<<1;
for (int i=(3+isCSM); i<(6+isCSM); i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=fmout.data[i-2+isCSM]<<1;
}
}
}
@ -290,8 +290,8 @@ void DivPlatformYM2203::acquire_ymfm(short** buf, size_t len) {
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(out,-32768,32767);
}
for (int i=3; i<6; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=fmout.data[i-2]<<1;
for (int i=(3+isCSM); i<(6+isCSM); i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=fmout.data[i-2+isCSM]<<1;
}
}
}
@ -723,15 +723,29 @@ void DivPlatformYM2203::commitState(int ch, DivInstrument* ins) {
}
int DivPlatformYM2203::dispatch(DivCommand c) {
if (c.chan>2) {
c.chan-=3;
if (c.chan>(2+isCSM)) {
c.chan-=(3+isCSM);
return ay->dispatch(c);
}
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
if (c.chan==csmChan && extMode) { // CSM
chan[c.chan].macroInit(ins);
chan[c.chan].insChanged=false;
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
chan[c.chan].portaPause=false;
chan[c.chan].note=c.value;
chan[c.chan].freqChanged=true;
}
chan[c.chan].keyOn=true;
chan[c.chan].active=true;
break;
}
chan[c.chan].macroInit(ins);
if (c.chan<3) {
if (c.chan<psgChanOffs) {
if (!chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
@ -801,7 +815,30 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
break;
}
case DIV_CMD_NOTE_PORTA: {
if (c.chan>2 || parent->song.linearPitch==2) { // PSG
if (c.chan==csmChan) {
int destFreq=NOTE_PERIODIC(c.value2);
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
chan[c.chan].baseFreq+=c.value;
if (chan[c.chan].baseFreq>=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
} else {
chan[c.chan].baseFreq-=c.value;
if (chan[c.chan].baseFreq<=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
}
chan[c.chan].freqChanged=true;
if (return2) {
chan[c.chan].inPorta=false;
return 2;
}
break;
}
if (c.chan>(psgChanOffs-1) || parent->song.linearPitch==2) { // PSG
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
@ -828,6 +865,9 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
break;
}
case DIV_CMD_LEGATO: {
if (c.chan==csmChan) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
}
if (chan[c.chan].insChanged) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
commitState(c.chan,ins);
@ -1053,11 +1093,11 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
chan[c.chan].std.restart(c.value);
break;
case DIV_CMD_GET_VOLMAX:
if (c.chan>2) return 15;
if (c.chan>(2+isCSM)) return 15;
return 127;
break;
case DIV_CMD_PRE_PORTA:
if (c.chan>2) {
if (c.chan>(2+isCSM)) {
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_FM));
}
@ -1075,8 +1115,8 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
void DivPlatformYM2203::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
if (ch>2) { // PSG
ay->muteChannel(ch-3,mute);
if (ch>(2+isCSM)) { // PSG
ay->muteChannel(ch-(3+isCSM),mute);
return;
}
for (int j=0; j<4; j++) {
@ -1121,7 +1161,7 @@ void DivPlatformYM2203::forceIns() {
chan[i].freqChanged=true;
}
}
for (int i=3; i<6; i++) {
for (int i=(3+isCSM); i<(6+isCSM); i++) {
chan[i].insChanged=true;
}
@ -1139,7 +1179,7 @@ void* DivPlatformYM2203::getChanState(int ch) {
}
DivMacroInt* DivPlatformYM2203::getChanMacroInt(int ch) {
if (ch>=3) return ay->getChanMacroInt(ch-3);
if (ch>=(3+isCSM)) return ay->getChanMacroInt(ch-(3+isCSM));
return &chan[ch].std;
}
@ -1173,15 +1213,15 @@ void DivPlatformYM2203::reset() {
OPN2_SetChipType(&fm_nuked,ym3438_mode_opn);
fm->reset();
memset(&fm_lle,0,sizeof(fmopna_t));
for (int i=0; i<6; i++) {
for (int i=0; i<7; i++) {
chan[i]=DivPlatformOPN::OPNChannel();
chan[i].std.setEngine(parent);
}
for (int i=0; i<3; i++) {
for (int i=0; i<3; i++) { // check back later / me from future: wha?
chan[i].vol=0x7f;
chan[i].outVol=0x7f;
}
for (int i=3; i<6; i++) {
for (int i=(3+isCSM); i<(6+isCSM); i++) {
chan[i].vol=0x0f;
}
@ -1255,7 +1295,7 @@ bool DivPlatformYM2203::keyOffAffectsArp(int ch) {
}
void DivPlatformYM2203::notifyInsChange(int ins) {
for (int i=0; i<6; i++) {
for (int i=0; i<7; i++) {
if (chan[i].ins==ins) {
chan[i].insChanged=true;
}
@ -1331,7 +1371,7 @@ void DivPlatformYM2203::setFlags(const DivConfig& flags) {
} else {
rate=fm->sample_rate(chipClock);
}
for (int i=0; i<6; i++) {
for (int i=0; i<7; i++) {
oscBuf[i]->rate=rate;
}
immWrite(0x2d,0xff);
@ -1345,7 +1385,7 @@ int DivPlatformYM2203::init(DivEngine* p, int channels, int sugRate, const DivCo
parent=p;
dumpWrites=false;
skipRegisterWrites=false;
for (int i=0; i<6; i++) {
for (int i=0; i<7; i++) {
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
}
@ -1358,11 +1398,11 @@ int DivPlatformYM2203::init(DivEngine* p, int channels, int sugRate, const DivCo
setFlags(flags);
reset();
return 6;
return 7;
}
void DivPlatformYM2203::quit() {
for (int i=0; i<6; i++) {
for (int i=0; i<7; i++) {
delete oscBuf[i];
}
ay->quit();

View file

@ -42,9 +42,9 @@ class DivPlatformYM2203: public DivPlatformOPN {
0, 1, 2
};
OPNChannel chan[6];
DivDispatchOscBuffer* oscBuf[6];
bool isMuted[6];
OPNChannel chan[7];
DivDispatchOscBuffer* oscBuf[7];
bool isMuted[7];
ym3438_t fm_nuked;
ymfm::ym2203* fm;
ymfm::ym2203::output_data fmout;
@ -73,6 +73,7 @@ class DivPlatformYM2203: public DivPlatformOPN {
void acquire_lle(short** buf, size_t len);
public:
bool isCSM;
void acquire(short** buf, size_t len);
void fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len);
int dispatch(DivCommand c);
@ -98,7 +99,8 @@ class DivPlatformYM2203: public DivPlatformOPN {
void quit();
DivPlatformYM2203():
DivPlatformOPN(2, 3, 6, 6, 6, 4720270.0, 36, 16),
prescale(0x2d) {}
prescale(0x2d),
isCSM(false) {}
~DivPlatformYM2203();
};
#endif

View file

@ -414,6 +414,9 @@ void DivPlatformYM2203Ext::tick(bool sysTick) {
}
}
if (writeSomething) {
if (chan[csmChan].active) { // CSM
writeMask^=0xf0;
}
immWrite(0x28,writeMask);
}
}
@ -565,7 +568,28 @@ void DivPlatformYM2203Ext::tick(bool sysTick) {
}
}
}
if (extMode) {
if (chan[csmChan].freqChanged) {
chan[csmChan].freq=parent->calcFreq(chan[csmChan].baseFreq,chan[csmChan].pitch,chan[csmChan].fixedArp?chan[csmChan].baseNoteOverride:chan[csmChan].arpOff,chan[csmChan].fixedArp,true,0,chan[csmChan].pitch2,chipClock,CHIP_DIVIDER);
if (chan[csmChan].freq<1) chan[csmChan].freq=1;
if (chan[csmChan].freq>1024) chan[csmChan].freq=1024;
int wf=0x400-chan[csmChan].freq;
immWrite(0x24,wf>>2);
immWrite(0x25,wf&3);
chan[csmChan].freqChanged=false;
}
if (chan[csmChan].keyOff || chan[csmChan].keyOn) {
writeNoteOn=true;
for (int i=0; i<4; i++) {
writeMask|=opChan[i].active<<(4+i);
}
}
}
if (writeNoteOn) {
if (chan[csmChan].active) { // CSM
writeMask^=0xf0;
}
writeMask^=hardResetMask;
immWrite(0x28,writeMask);
writeMask^=hardResetMask;
@ -587,6 +611,17 @@ void DivPlatformYM2203Ext::tick(bool sysTick) {
immWrite(0x28,writeMask);
}
}
if (extMode) {
if (chan[csmChan].keyOn) {
immWrite(0x27,0x81);
chan[csmChan].keyOn=false;
}
if (chan[csmChan].keyOff) {
immWrite(0x27,0x40);
chan[csmChan].keyOff=false;
}
}
}
void DivPlatformYM2203Ext::muteChannel(int ch, bool mute) {
@ -666,6 +701,12 @@ void DivPlatformYM2203Ext::forceIns() {
opChan[i].freqChanged=true;
}
}
if (extMode && chan[csmChan].active) { // CSM
chan[csmChan].insChanged=true;
chan[csmChan].freqChanged=true;
chan[csmChan].keyOn=true;
}
if (!extMode) {
immWrite(0x27,0x00);
}
@ -678,7 +719,7 @@ void* DivPlatformYM2203Ext::getChanState(int ch) {
}
DivMacroInt* DivPlatformYM2203Ext::getChanMacroInt(int ch) {
if (ch>=6) return ay->getChanMacroInt(ch-6);
if (ch>=(6+isCSM)) return ay->getChanMacroInt(ch-(6+isCSM));
if (ch>=2) return &opChan[ch-2].std;
return &chan[ch].std;
}
@ -738,12 +779,19 @@ int DivPlatformYM2203Ext::init(DivEngine* parent, int channels, int sugRate, con
extSys=true;
reset();
return 9;
return 3+2+4+isCSM; // 3xPSG + 2xFM + 4xOP + optional CSM
}
void DivPlatformYM2203Ext::quit() {
DivPlatformYM2203::quit();
}
void DivPlatformYM2203Ext::setCSM(bool isCSM) {
this->isCSM=isCSM;
if (isCSM) {
csmChan=3;
}
}
DivPlatformYM2203Ext::~DivPlatformYM2203Ext() {
}

View file

@ -44,6 +44,7 @@ class DivPlatformYM2203Ext: public DivPlatformYM2203 {
void notifyInsDeletion(void* ins);
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
void quit();
void setCSM(bool isCSM);
~DivPlatformYM2203Ext();
};

View file

@ -480,15 +480,15 @@ void DivPlatformYM2608::acquire_ymfm(short** buf, size_t len) {
}
ssge->get_last_out(ssgOut);
for (int i=6; i<9; i++) {
for (int i=(6+isCSM); i<(9+isCSM); i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-6]<<1;
}
for (int i=9; i<15; i++) {
for (int i=(9+isCSM); i<(15+isCSM); i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=(adpcmAChan[i-9]->get_last_out(0)+adpcmAChan[i-9]->get_last_out(1))>>1;
}
oscBuf[15]->data[oscBuf[15]->needle++]=(abe->get_last_out(0)+abe->get_last_out(1))>>1;
oscBuf[15+isCSM]->data[oscBuf[15+isCSM]->needle++]=(abe->get_last_out(0)+abe->get_last_out(1))>>1;
}
}
@ -894,7 +894,7 @@ void DivPlatformYM2608::tick(bool sysTick) {
}
// RSS
for (int i=9; i<15; i++) {
for (int i=(9+isCSM); i<(15+isCSM); i++) {
if (chan[i].furnacePCM) {
chan[i].std.next();
if (chan[i].std.vol.had) {
@ -916,93 +916,93 @@ void DivPlatformYM2608::tick(bool sysTick) {
}
}
if (!isMuted[i] && (chan[i].std.vol.had || chan[i].std.panL.had)) {
immWrite(0x18+(i-9),isMuted[i]?0:((chan[i].pan<<6)|chan[i].outVol));
immWrite(0x18+(i-(9+isCSM)),isMuted[i]?0:((chan[i].pan<<6)|chan[i].outVol));
hardResetElapsed++;
}
}
if (chan[i].keyOff) {
writeRSSOff|=(1<<(i-9));
writeRSSOff|=(1<<(i-(9+isCSM)));
chan[i].keyOff=false;
}
if (chan[i].keyOn) {
writeRSSOn|=(1<<(i-9));
writeRSSOn|=(1<<(i-(9+isCSM)));
chan[i].keyOn=false;
}
}
// ADPCM-B
if (chan[15].furnacePCM) {
chan[15].std.next();
if (chan[(15+isCSM)].furnacePCM) {
chan[(15+isCSM)].std.next();
if (chan[15].std.vol.had) {
chan[15].outVol=(chan[15].vol*MIN(chan[15].macroVolMul,chan[15].std.vol.val))/chan[15].macroVolMul;
immWrite(0x10b,chan[15].outVol);
if (chan[(15+isCSM)].std.vol.had) {
chan[(15+isCSM)].outVol=(chan[(15+isCSM)].vol*MIN(chan[(15+isCSM)].macroVolMul,chan[(15+isCSM)].std.vol.val))/chan[(15+isCSM)].macroVolMul;
immWrite(0x10b,chan[(15+isCSM)].outVol);
hardResetElapsed++;
}
if (NEW_ARP_STRAT) {
chan[15].handleArp();
} else if (chan[15].std.arp.had) {
if (!chan[15].inPorta) {
chan[15].baseFreq=NOTE_ADPCMB(parent->calcArp(chan[15].note,chan[15].std.arp.val));
chan[(15+isCSM)].handleArp();
} else if (chan[(15+isCSM)].std.arp.had) {
if (!chan[(15+isCSM)].inPorta) {
chan[(15+isCSM)].baseFreq=NOTE_ADPCMB(parent->calcArp(chan[(15+isCSM)].note,chan[(15+isCSM)].std.arp.val));
}
chan[15].freqChanged=true;
chan[(15+isCSM)].freqChanged=true;
}
if (chan[15].std.pitch.had) {
if (chan[15].std.pitch.mode) {
chan[15].pitch2+=chan[15].std.pitch.val;
CLAMP_VAR(chan[15].pitch2,-65535,65535);
if (chan[(15+isCSM)].std.pitch.had) {
if (chan[(15+isCSM)].std.pitch.mode) {
chan[(15+isCSM)].pitch2+=chan[(15+isCSM)].std.pitch.val;
CLAMP_VAR(chan[(15+isCSM)].pitch2,-65535,65535);
} else {
chan[15].pitch2=chan[15].std.pitch.val;
chan[(15+isCSM)].pitch2=chan[(15+isCSM)].std.pitch.val;
}
chan[15].freqChanged=true;
chan[(15+isCSM)].freqChanged=true;
}
if (chan[15].std.panL.had) {
if (chan[15].pan!=(chan[15].std.panL.val&3)) {
chan[15].pan=chan[15].std.panL.val&3;
if (chan[(15+isCSM)].std.panL.had) {
if (chan[(15+isCSM)].pan!=(chan[(15+isCSM)].std.panL.val&3)) {
chan[(15+isCSM)].pan=chan[(15+isCSM)].std.panL.val&3;
if (!isMuted[15]) {
immWrite(0x101,(isMuted[15]?0:(chan[15].pan<<6))|1);
immWrite(0x101,(isMuted[15]?0:(chan[(15+isCSM)].pan<<6))|1);
hardResetElapsed++;
}
}
}
if (chan[15].std.phaseReset.had) {
if ((chan[15].std.phaseReset.val==1) && chan[15].active) {
chan[15].keyOn=true;
if (chan[(15+isCSM)].std.phaseReset.had) {
if ((chan[(15+isCSM)].std.phaseReset.val==1) && chan[(15+isCSM)].active) {
chan[(15+isCSM)].keyOn=true;
}
}
}
if (chan[15].freqChanged || chan[15].keyOn || chan[15].keyOff) {
if (chan[15].furnacePCM) {
if (chan[15].sample>=0 && chan[15].sample<parent->song.sampleLen) {
double off=65535.0*(double)(parent->getSample(chan[15].sample)->centerRate)/8363.0;
chan[15].freq=parent->calcFreq(chan[15].baseFreq,chan[15].pitch,chan[15].fixedArp?chan[15].baseNoteOverride:chan[15].arpOff,chan[15].fixedArp,false,4,chan[15].pitch2,(double)chipClock/144,off);
if (chan[(15+isCSM)].freqChanged || chan[(15+isCSM)].keyOn || chan[(15+isCSM)].keyOff) {
if (chan[(15+isCSM)].furnacePCM) {
if (chan[(15+isCSM)].sample>=0 && chan[(15+isCSM)].sample<parent->song.sampleLen) {
double off=65535.0*(double)(parent->getSample(chan[(15+isCSM)].sample)->centerRate)/8363.0;
chan[(15+isCSM)].freq=parent->calcFreq(chan[(15+isCSM)].baseFreq,chan[(15+isCSM)].pitch,chan[(15+isCSM)].fixedArp?chan[(15+isCSM)].baseNoteOverride:chan[(15+isCSM)].arpOff,chan[(15+isCSM)].fixedArp,false,4,chan[(15+isCSM)].pitch2,(double)chipClock/144,off);
} else {
chan[15].freq=0;
chan[(15+isCSM)].freq=0;
}
}
if (chan[adpcmBChanOffs].freq<0) chan[adpcmBChanOffs].freq=0;
if (chan[adpcmBChanOffs].freq>65535) chan[adpcmBChanOffs].freq=65535;
immWrite(0x109,chan[15].freq&0xff);
immWrite(0x10a,(chan[15].freq>>8)&0xff);
immWrite(0x109,chan[15+isCSM].freq&0xff);
immWrite(0x10a,(chan[15+isCSM].freq>>8)&0xff);
hardResetElapsed+=2;
if (chan[15].keyOn || chan[15].keyOff) {
if (chan[15].keyOff) {
if (chan[15+isCSM].keyOn || chan[15+isCSM].keyOff) {
if (chan[15+isCSM].keyOff) {
immWrite(0x100,0x01); // reset
hardResetElapsed++;
}
if (chan[15].active && chan[15].keyOn && !chan[15].keyOff) {
if (chan[15].sample>=0 && chan[15].sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan[15].sample);
if (chan[15+isCSM].active && chan[15+isCSM].keyOn && !chan[15+isCSM].keyOff) {
if (chan[15+isCSM].sample>=0 && chan[15+isCSM].sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan[15+isCSM].sample);
immWrite(0x100,(s->isLoopable())?0xb0:0xa0); // start/repeat
hardResetElapsed++;
}
}
chan[15].keyOn=false;
chan[15].keyOff=false;
chan[15+isCSM].keyOn=false;
chan[15+isCSM].keyOff=false;
}
chan[15].freqChanged=false;
chan[15+isCSM].freqChanged=false;
}
if (writeRSSOff) {
@ -1094,13 +1094,13 @@ void DivPlatformYM2608::commitState(int ch, DivInstrument* ins) {
}
int DivPlatformYM2608::dispatch(DivCommand c) {
if (c.chan>5 && c.chan<9) {
c.chan-=6;
if (c.chan>(5+isCSM) && c.chan<(9+isCSM)) {
c.chan-=(6+isCSM);
return ay->dispatch(c);
}
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
if (c.chan>14) { // ADPCM-B
if (c.chan>(14+isCSM)) { // ADPCM-B
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
chan[c.chan].macroVolMul=(ins->type==DIV_INS_AMIGA)?64:255;
if (ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_ADPCMB) {
@ -1179,7 +1179,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
}
break;
}
if (c.chan>8) { // RSS
if (c.chan>(8+isCSM)) { // RSS
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
chan[c.chan].macroVolMul=(ins->type==DIV_INS_AMIGA)?64:31;
if (ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_ADPCMA) {
@ -1192,18 +1192,32 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
chan[c.chan].macroInit(ins);
if (!chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
immWrite(0x18+(c.chan-9),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].outVol));
immWrite(0x18+(c.chan-(9+isCSM)),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].outVol));
}
} else {
chan[c.chan].macroInit(NULL);
chan[c.chan].outVol=chan[c.chan].vol;
immWrite(0x18+(c.chan-9),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].outVol));
immWrite(0x18+(c.chan-(9+isCSM)),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].outVol));
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
break;
}
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
if (c.chan==csmChan && extMode) { // CSM
chan[c.chan].macroInit(ins);
chan[c.chan].insChanged=false;
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
chan[c.chan].portaPause=false;
chan[c.chan].note=c.value;
chan[c.chan].freqChanged=true;
}
chan[c.chan].keyOn=true;
chan[c.chan].active=true;
break;
}
chan[c.chan].macroInit(ins);
if (c.chan<6) {
if (!chan[c.chan].std.vol.will) {
@ -1244,12 +1258,12 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
if (!chan[c.chan].std.vol.has) {
chan[c.chan].outVol=c.value;
}
if (c.chan>14) { // ADPCM-B
if (c.chan>(14+isCSM)) { // ADPCM-B
immWrite(0x10b,chan[c.chan].outVol);
break;
}
if (c.chan>8) { // ADPCM-A
immWrite(0x18+(c.chan-9),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].outVol));
if (c.chan>(8+isCSM)) { // ADPCM-A
immWrite(0x18+(c.chan-(9+isCSM)),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].outVol));
break;
}
for (int i=0; i<4; i++) {
@ -1290,25 +1304,48 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
} else {
chan[c.chan].pan=(c.value2>0)|((c.value>0)<<1);
}
if (c.chan>14) {
if (c.chan>(14+isCSM)) {
immWrite(0x101,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|1);
break;
}
if (c.chan>8) {
immWrite(0x18+(c.chan-9),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].outVol));
immWrite(0x18+(c.chan-(9+isCSM)),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].outVol));
break;
}
rWrite(chanOffs[c.chan]+ADDR_LRAF,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4));
break;
}
case DIV_CMD_PITCH: {
if (c.chan==15 && !chan[c.chan].furnacePCM) break;
if (c.chan==(15+isCSM) && !chan[c.chan].furnacePCM) break;
chan[c.chan].pitch=c.value;
chan[c.chan].freqChanged=true;
break;
}
case DIV_CMD_NOTE_PORTA: {
if (c.chan>5 || parent->song.linearPitch==2) { // PSG, ADPCM-B
if (c.chan==csmChan) {
int destFreq=NOTE_PERIODIC(c.value2);
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
chan[c.chan].baseFreq+=c.value;
if (chan[c.chan].baseFreq>=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
} else {
chan[c.chan].baseFreq-=c.value;
if (chan[c.chan].baseFreq<=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
}
chan[c.chan].freqChanged=true;
if (return2) {
chan[c.chan].inPorta=false;
return 2;
}
break;
}
if (c.chan>(5+isCSM) || parent->song.linearPitch==2) { // PSG, ADPCM-B
int destFreq=NOTE_OPNB(c.chan,c.value2+chan[c.chan].sampleNoteDelta);
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
@ -1342,7 +1379,10 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
iface.sampleBank=sampleBank;
break;
case DIV_CMD_LEGATO: {
if (c.chan==15 && !chan[c.chan].furnacePCM) break;
if (c.chan==csmChan) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
}
if (c.chan==(15+isCSM) && !chan[c.chan].furnacePCM) break;
if (c.chan<=psgChanOffs) {
if (chan[c.chan].insChanged) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
@ -1576,8 +1616,8 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
chan[c.chan].std.restart(c.value);
break;
case DIV_CMD_GET_VOLMAX:
if (c.chan>14) return 255;
if (c.chan>8) return 31;
if (c.chan>(14+isCSM)) return 255;
if (c.chan>(8+isCSM)) return 31;
if (c.chan>5) return 15;
return 127;
break;
@ -1600,10 +1640,10 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
void DivPlatformYM2608::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
if (ch>14) { // ADPCM-B
if (ch>(14+isCSM)) { // ADPCM-B
immWrite(0x101,(isMuted[ch]?0:(chan[ch].pan<<6))|1);
}
if (ch>8) { // ADPCM-A
if (ch>(8+isCSM)) { // ADPCM-A
immWrite(0x18+(ch-9),isMuted[ch]?0:((chan[ch].pan<<6)|chan[ch].outVol));
return;
}
@ -1658,12 +1698,12 @@ void DivPlatformYM2608::forceIns() {
}
immWrite(0x11,globalRSSVolume&0x3f);
immWrite(0x22,lfoValue);
for (int i=9; i<16; i++) {
for (int i=(+isCSM); i<(16+isCSM); i++) {
chan[i].insChanged=true;
if (i>14) { // ADPCM-B
if (i>(14+isCSM)) { // ADPCM-B
immWrite(0x10b,chan[i].outVol);
} else {
immWrite(0x18+(i-9),isMuted[i]?0:((chan[i].pan<<6)|chan[i].outVol));
immWrite(0x18+(i-(9+isCSM)),isMuted[i]?0:((chan[i].pan<<6)|chan[i].outVol));
}
}
@ -1681,7 +1721,7 @@ void* DivPlatformYM2608::getChanState(int ch) {
}
DivMacroInt* DivPlatformYM2608::getChanMacroInt(int ch) {
if (ch>=6 && ch<9) return ay->getChanMacroInt(ch-6);
if (ch>=(6+isCSM) && ch<(9+isCSM)) return ay->getChanMacroInt(ch-6);
return &chan[ch].std;
}
@ -1728,10 +1768,10 @@ void DivPlatformYM2608::reset() {
chan[i].vol=0x7f;
chan[i].outVol=0x7f;
}
for (int i=6; i<9; i++) {
for (int i=(6+isCSM); i<(9+isCSM); i++) {
chan[i].vol=0x0f;
}
for (int i=9; i<15; i++) {
for (int i=(9+isCSM); i<(15+isCSM); i++) {
chan[i].vol=0x1f;
}
chan[15].vol=0xff;
@ -1833,7 +1873,7 @@ bool DivPlatformYM2608::keyOffAffectsArp(int ch) {
}
void DivPlatformYM2608::notifyInsChange(int ins) {
for (int i=0; i<16; i++) {
for (int i=0; i<(16+isCSM); i++) {
if (chan[i].ins==ins) {
chan[i].insChanged=true;
}
@ -1964,7 +2004,7 @@ void DivPlatformYM2608::setFlags(const DivConfig& flags) {
} else {
rate=fm->sample_rate(chipClock);
}
for (int i=0; i<16; i++) {
for (int i=0; i<(16+isCSM); i++) {
oscBuf[i]->rate=rate;
}
immWrite(0x2d,0xff);
@ -1984,7 +2024,7 @@ int DivPlatformYM2608::init(DivEngine* p, int channels, int sugRate, const DivCo
iface.sampleBank=0;
dumpWrites=false;
skipRegisterWrites=false;
for (int i=0; i<16; i++) {
for (int i=0; i<16+isCSM; i++) {
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
}
@ -1996,11 +2036,20 @@ int DivPlatformYM2608::init(DivEngine* p, int channels, int sugRate, const DivCo
ay->toggleRegisterDump(true);
setFlags(flags);
reset();
return 16;
return 17;
}
void DivPlatformYM2608::setCSM(bool isCSM) {
this->isCSM=isCSM;
if (isCSM) {
csmChan=6;
} else {
csmChan=16; // me from the furute: ???
}
}
void DivPlatformYM2608::quit() {
for (int i=0; i<16; i++) {
for (int i=0; i<17; i++) {
delete oscBuf[i];
}
ay->quit();

View file

@ -47,9 +47,9 @@ class DivPlatformYM2608: public DivPlatformOPN {
0, 1, 2, 4, 5, 6
};
OPNChannelStereo chan[16];
DivDispatchOscBuffer* oscBuf[16];
bool isMuted[16];
OPNChannelStereo chan[17];
DivDispatchOscBuffer* oscBuf[17];
bool isMuted[17];
ym3438_t fm_nuked;
ymfm::ym2608* fm;
ymfm::ym2608::output_data fmout;
@ -92,6 +92,7 @@ class DivPlatformYM2608: public DivPlatformOPN {
void acquire_lle(short** buf, size_t len);
public:
bool isCSM;
void acquire(short** buf, size_t len);
void fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len);
int dispatch(DivCommand c);
@ -121,10 +122,12 @@ class DivPlatformYM2608: public DivPlatformOPN {
void renderSamples(int chipID);
void setFlags(const DivConfig& flags);
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
void setCSM(bool isCSM);
void quit();
DivPlatformYM2608():
DivPlatformOPN(2, 6, 9, 15, 16, 9440540.0, 72, 32),
prescale(0x2d) {}
prescale(0x2d),
isCSM(false) {}
~DivPlatformYM2608();
};
#endif

View file

@ -438,6 +438,9 @@ void DivPlatformYM2608Ext::tick(bool sysTick) {
}
}
if (writeSomething) {
if (chan[csmChan].active) { // CSM
writeMask^=0xf0;
}
immWrite(0x28,writeMask);
}
}
@ -612,7 +615,28 @@ void DivPlatformYM2608Ext::tick(bool sysTick) {
}
}
}
if (extMode) {
if (chan[csmChan].freqChanged) {
chan[csmChan].freq=parent->calcFreq(chan[csmChan].baseFreq,chan[csmChan].pitch,chan[csmChan].fixedArp?chan[csmChan].baseNoteOverride:chan[csmChan].arpOff,chan[csmChan].fixedArp,true,0,chan[csmChan].pitch2,chipClock,CHIP_DIVIDER);
if (chan[csmChan].freq<1) chan[csmChan].freq=1;
if (chan[csmChan].freq>1024) chan[csmChan].freq=1024;
int wf=0x400-chan[csmChan].freq;
immWrite(0x24,wf>>2);
immWrite(0x25,wf&3);
chan[csmChan].freqChanged=false;
}
if (chan[csmChan].keyOff || chan[csmChan].keyOn) {
writeNoteOn=true;
for (int i=0; i<4; i++) {
writeMask|=opChan[i].active<<(4+i);
}
}
}
if (writeNoteOn) {
if (chan[csmChan].active) { // CSM
writeMask^=0xf0;
}
writeMask^=hardResetMask;
immWrite(0x28,writeMask);
writeMask^=hardResetMask;
@ -634,6 +658,17 @@ void DivPlatformYM2608Ext::tick(bool sysTick) {
immWrite(0x28,writeMask);
}
}
if (extMode) {
if (chan[csmChan].keyOn) {
immWrite(0x27,0x81);
chan[csmChan].keyOn=false;
}
if (chan[csmChan].keyOff) {
immWrite(0x27,0x40);
chan[csmChan].keyOff=false;
}
}
}
void DivPlatformYM2608Ext::muteChannel(int ch, bool mute) {
@ -705,12 +740,12 @@ void DivPlatformYM2608Ext::forceIns() {
chan[i].freqChanged=true;
}
}
for (int i=9; i<16; i++) {
for (int i=(9+isCSM); i<(16+isCSM); i++) {
chan[i].insChanged=true;
if (i>14) { // ADPCM-B
if (i>(14+isCSM)) { // ADPCM-B
immWrite(0x10b,chan[i].outVol);
} else {
immWrite(0x18+(i-9),isMuted[i]?0:((chan[i].pan<<6)|chan[i].outVol));
immWrite(0x18+(i-(9+isCSM)),isMuted[i]?0:((chan[i].pan<<6)|chan[i].outVol));
}
}
ay->forceIns();
@ -728,6 +763,11 @@ void DivPlatformYM2608Ext::forceIns() {
opChan[i].freqChanged=true;
}
}
if (extMode && chan[csmChan].active) { // CSM
chan[csmChan].insChanged=true;
chan[csmChan].freqChanged=true;
chan[csmChan].keyOn=true;
}
if (!extMode) {
immWrite(0x27,0x00);
}
@ -740,13 +780,14 @@ void* DivPlatformYM2608Ext::getChanState(int ch) {
}
DivMacroInt* DivPlatformYM2608Ext::getChanMacroInt(int ch) {
if (ch>=9 && ch<12) return ay->getChanMacroInt(ch-9);
if (ch>=(9+isCSM) && ch<(12+isCSM)) return ay->getChanMacroInt(ch-(9+isCSM));
if (ch>=6) return &chan[ch-3].std;
if (ch>=2) return &opChan[ch-2].std;
return &chan[ch].std;
}
unsigned short DivPlatformYM2608Ext::getPan(int ch) {
if (ch==4+csmChan) return 0;
if (ch>=4+extChanOffs) return DivPlatformYM2608::getPan(ch-3);
if (ch>=extChanOffs) {
if (extMode) {
@ -788,7 +829,7 @@ void DivPlatformYM2608Ext::reset() {
}
bool DivPlatformYM2608Ext::keyOffAffectsArp(int ch) {
return (ch>8);
return (ch>(8+isCSM));
}
void DivPlatformYM2608Ext::notifyInsChange(int ins) {
@ -815,7 +856,7 @@ int DivPlatformYM2608Ext::init(DivEngine* parent, int channels, int sugRate, con
extSys=true;
reset();
return 19;
return 19+isCSM;
}
void DivPlatformYM2608Ext::quit() {

View file

@ -335,7 +335,7 @@ void DivPlatformYM2610::acquire_combo(short** buf, size_t len) {
buf[1][h]=os[1];
for (int i=0; i<psgChanOffs; i++) {
for (int i=0; i<(psgChanOffs-isCSM); i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(fm_nuked.ch_out[bchOffs[i]]<<1,-32768,32767);
}
@ -407,7 +407,7 @@ void DivPlatformYM2610::acquire_ymfm(short** buf, size_t len) {
buf[0][h]=os[0];
buf[1][h]=os[1];
for (int i=0; i<psgChanOffs; i++) {
for (int i=0; i<(psgChanOffs-isCSM); i++) {
int out=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1))<<1;
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(out,-32768,32767);
}
@ -615,7 +615,7 @@ void DivPlatformYM2610::acquire_lle(short** buf, size_t len) {
void DivPlatformYM2610::tick(bool sysTick) {
// FM
for (int i=0; i<psgChanOffs; i++) {
for (int i=0; i<(psgChanOffs-isCSM); i++) {
if (i==1 && extMode) continue;
chan[i].std.next();
@ -772,7 +772,7 @@ void DivPlatformYM2610::tick(bool sysTick) {
int hardResetElapsed=0;
bool mustHardReset=false;
for (int i=0; i<psgChanOffs; i++) {
for (int i=0; i<(psgChanOffs-isCSM); i++) {
if (i==1 && extMode) continue;
if (chan[i].keyOn || chan[i].keyOff) {
immWrite(0x28,0x00|konOffs[i]);
@ -788,7 +788,7 @@ void DivPlatformYM2610::tick(bool sysTick) {
}
}
for (int i=0; i<psgChanOffs; i++) {
for (int i=0; i<(psgChanOffs-isCSM); i++) {
if (i==1 && extMode) continue;
if (chan[i].freqChanged) {
if (parent->song.linearPitch==2) {
@ -959,7 +959,7 @@ void DivPlatformYM2610::tick(bool sysTick) {
for (unsigned int i=hardResetElapsed; i<hardResetCycles; i++) {
immWrite(0xf0,i&0xff);
}
for (int i=0; i<psgChanOffs; i++) {
for (int i=0; i<(psgChanOffs-isCSM); i++) {
if (i==1 && extMode) continue;
if ((chan[i].keyOn || chan[i].opMaskChanged) && chan[i].hardReset) {
// restore SL/RR
@ -1170,8 +1170,22 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
break;
}
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
if (c.chan==csmChan && extMode) { // CSM
chan[c.chan].macroInit(ins);
chan[c.chan].insChanged=false;
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
chan[c.chan].portaPause=false;
chan[c.chan].note=c.value;
chan[c.chan].freqChanged=true;
}
chan[c.chan].keyOn=true;
chan[c.chan].active=true;
break;
}
chan[c.chan].macroInit(ins);
if (c.chan<psgChanOffs) {
if (c.chan<(psgChanOffs-isCSM)) {
if (!chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
@ -1274,6 +1288,29 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
break;
}
case DIV_CMD_NOTE_PORTA: {
if (c.chan==csmChan) {
int destFreq=NOTE_PERIODIC(c.value2);
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
chan[c.chan].baseFreq+=c.value;
if (chan[c.chan].baseFreq>=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
} else {
chan[c.chan].baseFreq-=c.value;
if (chan[c.chan].baseFreq<=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
}
chan[c.chan].freqChanged=true;
if (return2) {
chan[c.chan].inPorta=false;
return 2;
}
break;
}
if (c.chan>=psgChanOffs || parent->song.linearPitch==2) { // PSG, ADPCM-B
int destFreq=NOTE_OPNB(c.chan,c.value2+chan[c.chan].sampleNoteDelta);
bool return2=false;
@ -1309,6 +1346,9 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
break;
case DIV_CMD_LEGATO: {
if (c.chan==adpcmBChanOffs && !chan[c.chan].furnacePCM) break;
if (c.chan==csmChan) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
}
if (c.chan<=psgChanOffs) {
if (chan[c.chan].insChanged) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
@ -1588,7 +1628,7 @@ void DivPlatformYM2610::muteChannel(int ch, bool mute) {
}
void DivPlatformYM2610::forceIns() {
for (int i=0; i<psgChanOffs; i++) {
for (int i=0; i<(psgChanOffs-isCSM); i++) {
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
@ -1671,11 +1711,11 @@ void DivPlatformYM2610::reset() {
addWrite(0xffffffff,0);
}
fm->reset();
for (int i=0; i<14; i++) {
for (int i=0; i<15; i++) {
chan[i]=DivPlatformOPN::OPNChannelStereo();
chan[i].std.setEngine(parent);
}
for (int i=0; i<psgChanOffs; i++) {
for (int i=0; i<(psgChanOffs-isCSM); i++) {
chan[i].vol=0x7f;
chan[i].outVol=0x7f;
}
@ -1721,7 +1761,7 @@ bool DivPlatformYM2610::keyOffAffectsArp(int ch) {
}
void DivPlatformYM2610::notifyInsChange(int ins) {
for (int i=0; i<16; i++) {
for (int i=0; i<17; i++) {
if (chan[i].ins==ins) {
chan[i].insChanged=true;
}
@ -1731,7 +1771,7 @@ void DivPlatformYM2610::notifyInsChange(int ins) {
void DivPlatformYM2610::notifyInsDeletion(void* ins) {
ay->notifyInsDeletion(ins);
for (int i=0; i<psgChanOffs; i++) {
for (int i=0; i<(psgChanOffs-isCSM); i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
for (int i=adpcmAChanOffs; i<chanNum; i++) {
@ -1747,7 +1787,20 @@ void DivPlatformYM2610::setSkipRegisterWrites(bool value) {
int DivPlatformYM2610::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
DivPlatformYM2610Base::init(p, channels, sugRate, flags);
reset();
return 14;
return 15;
}
void DivPlatformYM2610::setCSM(bool isCSM) {
this->isCSM=isCSM;
psgChanOffs=4+isCSM; // doing this hurts me...
adpcmAChanOffs=7+isCSM;
adpcmBChanOffs=13+isCSM;
chanNum=14+isCSM;
if (isCSM) {
csmChan=4;
} else {
csmChan=14;
}
}
void DivPlatformYM2610::quit() {

View file

@ -45,6 +45,7 @@ class DivPlatformYM2610: public DivPlatformYM2610Base {
void acquire_lle(short** buf, size_t len);
public:
bool isCSM;
void acquire(short** buf, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
@ -66,9 +67,11 @@ class DivPlatformYM2610: public DivPlatformYM2610Base {
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
void setCSM(bool isCSM);
void quit();
DivPlatformYM2610():
DivPlatformYM2610Base(1,4,7,13,14) {}
DivPlatformYM2610Base(1,4,7,13,14),
isCSM(false) {}
~DivPlatformYM2610();
};
#endif

View file

@ -403,7 +403,7 @@ void DivPlatformYM2610B::acquire_combo(short** buf, size_t len) {
buf[1][h]=os[1];
for (int i=0; i<psgChanOffs; i++) {
for (int i=0; i<(psgChanOffs-isCSM); i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(fm_nuked.ch_out[i]<<1,-32768,32767);
}
@ -474,7 +474,7 @@ void DivPlatformYM2610B::acquire_ymfm(short** buf, size_t len) {
buf[1][h]=os[1];
for (int i=0; i<psgChanOffs; i++) {
for (int i=0; i<(psgChanOffs-isCSM); i++) {
int out=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1))<<1;
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(out,-32768,32767);
}
@ -684,7 +684,7 @@ void DivPlatformYM2610B::acquire_lle(short** buf, size_t len) {
void DivPlatformYM2610B::tick(bool sysTick) {
// FM
for (int i=0; i<psgChanOffs; i++) {
for (int i=0; i<(psgChanOffs-isCSM); i++) {
if (i==2 && extMode) continue;
chan[i].std.next();
@ -841,7 +841,7 @@ void DivPlatformYM2610B::tick(bool sysTick) {
int hardResetElapsed=0;
bool mustHardReset=false;
for (int i=0; i<psgChanOffs; i++) {
for (int i=0; i<(psgChanOffs-isCSM); i++) {
if (i==2 && extMode) continue;
if (chan[i].keyOn || chan[i].keyOff) {
immWrite(0x28,0x00|konOffs[i]);
@ -857,7 +857,7 @@ void DivPlatformYM2610B::tick(bool sysTick) {
}
}
for (int i=0; i<psgChanOffs; i++) {
for (int i=0; i<(psgChanOffs-isCSM); i++) {
if (i==2 && extMode) continue;
if (chan[i].freqChanged) {
if (parent->song.linearPitch==2) {
@ -1028,7 +1028,7 @@ void DivPlatformYM2610B::tick(bool sysTick) {
for (unsigned int i=hardResetElapsed; i<hardResetCycles; i++) {
immWrite(0xf0,i&0xff);
}
for (int i=0; i<psgChanOffs; i++) {
for (int i=0; i<(psgChanOffs-isCSM); i++) {
if (i==2 && extMode) continue;
if ((chan[i].keyOn || chan[i].opMaskChanged) && chan[i].hardReset) {
// restore SL/RR
@ -1239,6 +1239,20 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
break;
}
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
if (c.chan==csmChan && extMode) { // CSM
chan[c.chan].macroInit(ins);
chan[c.chan].insChanged=false;
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
chan[c.chan].portaPause=false;
chan[c.chan].note=c.value;
chan[c.chan].freqChanged=true;
}
chan[c.chan].keyOn=true;
chan[c.chan].active=true;
break;
}
chan[c.chan].macroInit(ins);
if (c.chan<6) {
if (!chan[c.chan].std.vol.will) {
@ -1343,6 +1357,29 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
break;
}
case DIV_CMD_NOTE_PORTA: {
if (c.chan==csmChan) {
int destFreq=NOTE_PERIODIC(c.value2);
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
chan[c.chan].baseFreq+=c.value;
if (chan[c.chan].baseFreq>=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
} else {
chan[c.chan].baseFreq-=c.value;
if (chan[c.chan].baseFreq<=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
}
chan[c.chan].freqChanged=true;
if (return2) {
chan[c.chan].inPorta=false;
return 2;
}
break;
}
if (c.chan>=psgChanOffs || parent->song.linearPitch==2) { // PSG, ADPCM-B
int destFreq=NOTE_OPNB(c.chan,c.value2+chan[c.chan].sampleNoteDelta);
bool return2=false;
@ -1378,7 +1415,10 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
break;
case DIV_CMD_LEGATO: {
if (c.chan==adpcmBChanOffs && !chan[c.chan].furnacePCM) break;
if (c.chan<=psgChanOffs) {
if (c.chan==csmChan) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
}
if (c.chan<=(psgChanOffs-isCSM)) {
if (chan[c.chan].insChanged) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
commitState(c.chan,ins);
@ -1794,7 +1834,7 @@ bool DivPlatformYM2610B::keyOffAffectsArp(int ch) {
}
void DivPlatformYM2610B::notifyInsChange(int ins) {
for (int i=0; i<16; i++) {
for (int i=0; i<17; i++) {
if (chan[i].ins==ins) {
chan[i].insChanged=true;
}
@ -1804,7 +1844,7 @@ void DivPlatformYM2610B::notifyInsChange(int ins) {
void DivPlatformYM2610B::notifyInsDeletion(void* ins) {
ay->notifyInsDeletion(ins);
for (int i=0; i<psgChanOffs; i++) {
for (int i=0; i<(psgChanOffs-isCSM); i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
for (int i=adpcmAChanOffs; i<chanNum; i++) {
@ -1820,7 +1860,20 @@ void DivPlatformYM2610B::setSkipRegisterWrites(bool value) {
int DivPlatformYM2610B::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
DivPlatformYM2610Base::init(p, channels, sugRate, flags);
reset();
return 16;
return 17;
}
void DivPlatformYM2610B::setCSM(bool isCSM) {
this->isCSM=isCSM;
psgChanOffs=6+isCSM; // doing this hurts me...
adpcmAChanOffs=9+isCSM;
adpcmBChanOffs=15+isCSM;
chanNum=16+isCSM;
if (isCSM) {
csmChan=6;
} else {
csmChan=16;
}
}
void DivPlatformYM2610B::quit() {

View file

@ -41,6 +41,7 @@ class DivPlatformYM2610B: public DivPlatformYM2610Base {
void acquire_lle(short** buf, size_t len);
public:
bool isCSM;
void acquire(short** buf, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
@ -62,9 +63,11 @@ class DivPlatformYM2610B: public DivPlatformYM2610Base {
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
void setCSM(bool isCSM);
void quit();
DivPlatformYM2610B():
DivPlatformYM2610Base(2,6,9,15,16) {}
DivPlatformYM2610Base(2,6,9,15,16),
isCSM(false) {}
~DivPlatformYM2610B();
};
#endif

View file

@ -434,6 +434,9 @@ void DivPlatformYM2610BExt::tick(bool sysTick) {
}
}
if (writeSomething) {
if (chan[csmChan].active) { // CSM
writeMask^=0xf0;
}
immWrite(0x28,writeMask);
}
}
@ -607,7 +610,28 @@ void DivPlatformYM2610BExt::tick(bool sysTick) {
}
}
}
if (extMode) {
if (chan[csmChan].freqChanged) {
chan[csmChan].freq=parent->calcFreq(chan[csmChan].baseFreq,chan[csmChan].pitch,chan[csmChan].fixedArp?chan[csmChan].baseNoteOverride:chan[csmChan].arpOff,chan[csmChan].fixedArp,true,0,chan[csmChan].pitch2,chipClock,CHIP_DIVIDER);
if (chan[csmChan].freq<1) chan[csmChan].freq=1;
if (chan[csmChan].freq>1024) chan[csmChan].freq=1024;
int wf=0x400-chan[csmChan].freq;
immWrite(0x24,wf>>2);
immWrite(0x25,wf&3);
chan[csmChan].freqChanged=false;
}
if (chan[csmChan].keyOff || chan[csmChan].keyOn) {
writeNoteOn=true;
for (int i=0; i<4; i++) {
writeMask|=opChan[i].active<<(4+i);
}
}
}
if (writeNoteOn) {
if (chan[csmChan].active) { // CSM
writeMask^=0xf0;
}
writeMask^=hardResetMask;
immWrite(0x28,writeMask);
writeMask^=hardResetMask;
@ -629,6 +653,17 @@ void DivPlatformYM2610BExt::tick(bool sysTick) {
immWrite(0x28,writeMask);
}
}
if (extMode) {
if (chan[csmChan].keyOn) {
immWrite(0x27,0x81);
chan[csmChan].keyOn=false;
}
if (chan[csmChan].keyOff) {
immWrite(0x27,0x40);
chan[csmChan].keyOff=false;
}
}
}
void DivPlatformYM2610BExt::muteChannel(int ch, bool mute) {
@ -718,6 +753,11 @@ void DivPlatformYM2610BExt::forceIns() {
opChan[i].freqChanged=true;
}
}
if (extMode && chan[csmChan].active) { // CSM
chan[csmChan].insChanged=true;
chan[csmChan].freqChanged=true;
chan[csmChan].keyOn=true;
}
if (!extMode) {
immWrite(0x27,0x00);
}
@ -737,6 +777,7 @@ DivMacroInt* DivPlatformYM2610BExt::getChanMacroInt(int ch) {
}
unsigned short DivPlatformYM2610BExt::getPan(int ch) {
if (ch==4+csmChan) return 0;
if (ch>=4+extChanOffs) return DivPlatformYM2610B::getPan(ch-3);
if (ch>=extChanOffs) {
if (extMode) {
@ -805,7 +846,7 @@ int DivPlatformYM2610BExt::init(DivEngine* parent, int channels, int sugRate, co
extSys=true;
reset();
return 19;
return 20;
}
void DivPlatformYM2610BExt::quit() {

View file

@ -434,6 +434,9 @@ void DivPlatformYM2610Ext::tick(bool sysTick) {
}
}
if (writeSomething) {
if (chan[csmChan].active) { // CSM
writeMask^=0xf0;
}
immWrite(0x28,writeMask);
}
}
@ -607,7 +610,28 @@ void DivPlatformYM2610Ext::tick(bool sysTick) {
}
}
}
if (extMode) {
if (chan[csmChan].freqChanged) {
chan[csmChan].freq=parent->calcFreq(chan[csmChan].baseFreq,chan[csmChan].pitch,chan[csmChan].fixedArp?chan[csmChan].baseNoteOverride:chan[csmChan].arpOff,chan[csmChan].fixedArp,true,0,chan[csmChan].pitch2,chipClock,CHIP_DIVIDER);
if (chan[csmChan].freq<1) chan[csmChan].freq=1;
if (chan[csmChan].freq>1024) chan[csmChan].freq=1024;
int wf=0x400-chan[csmChan].freq;
immWrite(0x24,wf>>2);
immWrite(0x25,wf&3);
chan[csmChan].freqChanged=false;
}
if (chan[csmChan].keyOff || chan[csmChan].keyOn) {
writeNoteOn=true;
for (int i=0; i<4; i++) {
writeMask|=opChan[i].active<<(4+i);
}
}
}
if (writeNoteOn) {
if (chan[csmChan].active) { // CSM
writeMask^=0xf0;
}
writeMask^=hardResetMask;
immWrite(0x28,writeMask);
writeMask^=hardResetMask;
@ -629,6 +653,16 @@ void DivPlatformYM2610Ext::tick(bool sysTick) {
immWrite(0x28,writeMask);
}
}
if (extMode) {
if (chan[csmChan].keyOn) {
immWrite(0x27,0x81);
chan[csmChan].keyOn=false;
}
if (chan[csmChan].keyOff) {
immWrite(0x27,0x40);
chan[csmChan].keyOff=false;
}
}
}
void DivPlatformYM2610Ext::muteChannel(int ch, bool mute) {
@ -718,6 +752,11 @@ void DivPlatformYM2610Ext::forceIns() {
opChan[i].freqChanged=true;
}
}
if (extMode && chan[csmChan].active) { // CSM
chan[csmChan].insChanged=true;
chan[csmChan].freqChanged=true;
chan[csmChan].keyOn=true;
}
if (!extMode) {
immWrite(0x27,0x00);
}
@ -737,6 +776,7 @@ DivMacroInt* DivPlatformYM2610Ext::getChanMacroInt(int ch) {
}
unsigned short DivPlatformYM2610Ext::getPan(int ch) {
if (ch==4+csmChan) return 0;
if (ch>=4+extChanOffs) return DivPlatformYM2610::getPan(ch-3);
if (ch>=extChanOffs) {
if (extMode) {
@ -805,7 +845,7 @@ int DivPlatformYM2610Ext::init(DivEngine* parent, int channels, int sugRate, con
extSys=true;
reset();
return 17;
return 17+isCSM;
}
void DivPlatformYM2610Ext::quit() {

View file

@ -49,9 +49,9 @@ class DivYM2610Interface: public ymfm::ymfm_interface {
class DivPlatformYM2610Base: public DivPlatformOPN {
protected:
OPNChannelStereo chan[16];
DivDispatchOscBuffer* oscBuf[16];
bool isMuted[16];
OPNChannelStereo chan[17];
DivDispatchOscBuffer* oscBuf[17];
bool isMuted[17];
ym3438_t fm_nuked;
ymfm::ym2610b* fm;
@ -322,7 +322,7 @@ class DivPlatformYM2610Base: public DivPlatformOPN {
} else {
rate=fm->sample_rate(chipClock);
}
for (int i=0; i<16; i++) {
for (int i=0; i<17; i++) {
oscBuf[i]->rate=rate;
}
}
@ -332,7 +332,7 @@ class DivPlatformYM2610Base: public DivPlatformOPN {
ayFlags.set("chipType",1);
dumpWrites=false;
skipRegisterWrites=false;
for (int i=0; i<16; i++) {
for (int i=0; i<17; i++) {
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
}
@ -354,7 +354,7 @@ class DivPlatformYM2610Base: public DivPlatformOPN {
}
void quit() {
for (int i=0; i<16; i++) {
for (int i=0; i<17; i++) {
delete oscBuf[i];
}
ay->quit();

View file

@ -1218,8 +1218,10 @@ const int availableSystems[]={
DIV_SYSTEM_YM2610_EXT,
DIV_SYSTEM_YM2610_FULL,
DIV_SYSTEM_YM2610_FULL_EXT,
DIV_SYSTEM_YM2610_CSM,
DIV_SYSTEM_YM2610B,
DIV_SYSTEM_YM2610B_EXT,
DIV_SYSTEM_YM2610B_CSM,
DIV_SYSTEM_T6W28,
DIV_SYSTEM_AY8910,
DIV_SYSTEM_AMIGA,
@ -1232,8 +1234,10 @@ const int availableSystems[]={
DIV_SYSTEM_SOUND_UNIT,
DIV_SYSTEM_YM2203,
DIV_SYSTEM_YM2203_EXT,
DIV_SYSTEM_YM2203_CSM,
DIV_SYSTEM_YM2608,
DIV_SYSTEM_YM2608_EXT,
DIV_SYSTEM_YM2608_CSM,
DIV_SYSTEM_OPLL,
DIV_SYSTEM_OPLL_DRUMS,
DIV_SYSTEM_VRC7,
@ -1314,13 +1318,17 @@ const int chipsFM[]={
DIV_SYSTEM_YM2610_EXT,
DIV_SYSTEM_YM2610_FULL,
DIV_SYSTEM_YM2610_FULL_EXT,
DIV_SYSTEM_YM2610_CSM,
DIV_SYSTEM_YM2610B,
DIV_SYSTEM_YM2610B_EXT,
DIV_SYSTEM_YM2610B_CSM,
DIV_SYSTEM_YMU759,
DIV_SYSTEM_YM2203,
DIV_SYSTEM_YM2203_EXT,
DIV_SYSTEM_YM2203_CSM,
DIV_SYSTEM_YM2608,
DIV_SYSTEM_YM2608_EXT,
DIV_SYSTEM_YM2608_CSM,
DIV_SYSTEM_OPLL,
DIV_SYSTEM_OPLL_DRUMS,
DIV_SYSTEM_VRC7,