dev98 - YM2612: add Fractal system

partially done
This commit is contained in:
tildearrow 2022-05-27 04:02:53 -05:00
parent 0b7d27dc29
commit a776129c3d
13 changed files with 299 additions and 138 deletions

View File

@ -2,8 +2,6 @@
- additional YM2612 features
- CSM
- DualPCM
- reverse sample playback
- ADPCM chips
- Game Boy envelope macro/sequence
- drag-and-drop ins/wave/sample loading

View File

@ -226,6 +226,8 @@ size | description
| - 0xb9: Namco WSG - 3 channels
| - 0xba: Namco 15xx - 8 channels
| - 0xbb: Namco CUS30 - 8 channels
| - 0xbc: YM2612 extra features - 8 channels
| - 0xbd: YM2612 extra features extended - 11 channels
| - 0xde: YM2610B extended - 19 channels
| - 0xe0: QSound - 19 channels
| - 0xfd: Dummy System - 8 channels

View File

@ -184,10 +184,22 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
case DIV_SYSTEM_YM2612:
dispatch=new DivPlatformGenesis;
((DivPlatformGenesis*)dispatch)->setYMFM(eng->getConfInt("ym2612Core",0));
((DivPlatformGenesis*)dispatch)->setSoftPCM(false);
break;
case DIV_SYSTEM_YM2612_EXT:
dispatch=new DivPlatformGenesisExt;
((DivPlatformGenesisExt*)dispatch)->setYMFM(eng->getConfInt("ym2612Core",0));
((DivPlatformGenesisExt*)dispatch)->setSoftPCM(false);
break;
case DIV_SYSTEM_YM2612_FRAC:
dispatch=new DivPlatformGenesis;
((DivPlatformGenesis*)dispatch)->setYMFM(eng->getConfInt("ym2612Core",0));
((DivPlatformGenesis*)dispatch)->setSoftPCM(true);
break;
case DIV_SYSTEM_YM2612_FRAC_EXT:
dispatch=new DivPlatformGenesisExt;
((DivPlatformGenesisExt*)dispatch)->setYMFM(eng->getConfInt("ym2612Core",0));
((DivPlatformGenesisExt*)dispatch)->setSoftPCM(true);
break;
case DIV_SYSTEM_SMS:
dispatch=new DivPlatformSMS;

View File

@ -45,8 +45,8 @@
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
#define BUSY_END isBusy.unlock(); softLocked=false;
#define DIV_VERSION "dev97"
#define DIV_ENGINE_VERSION 97
#define DIV_VERSION "dev98"
#define DIV_ENGINE_VERSION 98
// for imports
#define DIV_VERSION_MOD 0xff01

View File

@ -167,6 +167,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
ds.fbPortaPause=true;
ds.snDutyReset=true;
ds.oldOctaveBoundary=false;
ds.noOPN2Vol=true;
// 1.1 compat flags
if (ds.version>24) {
@ -1031,6 +1032,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
if (ds.version<97) {
ds.oldOctaveBoundary=true;
}
if (ds.version<97) { // actually should be 98 but yky uses this feature ahead of time
ds.noOPN2Vol=true;
}
ds.isDMF=false;
reader.readS(); // reserved
@ -1413,7 +1417,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
} else {
reader.readC();
}
for (int i=0; i<13; i++) {
if (ds.version>=98) {
ds.noOPN2Vol=reader.readC();
} else {
reader.readC();
}
for (int i=0; i<12; i++) {
reader.readC();
}
}
@ -2881,7 +2890,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
w->writeC(song.pitchMacroIsLinear);
w->writeC(song.pitchSlideSpeed);
w->writeC(song.oldOctaveBoundary);
for (int i=0; i<13; i++) {
w->writeC(song.noOPN2Vol);
for (int i=0; i<12; i++) {
w->writeC(0);
}

View File

@ -132,46 +132,100 @@ const char* DivPlatformGenesis::getEffectName(unsigned char effect) {
return NULL;
}
void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) {
static short o[2];
static int os[2];
void DivPlatformGenesis::processDAC() {
if (softPCM) {
softPCMTimer+=chipClock/576;
if (softPCMTimer>rate) {
softPCMTimer-=rate;
for (size_t h=start; h<start+len; h++) {
if (!dacReady) {
dacDelay+=32000;
if (dacDelay>=rate) {
dacDelay-=rate;
dacReady=true;
}
}
if (dacMode && dacSample!=-1) {
dacPeriod+=dacRate;
if (dacPeriod>=rate) {
DivSample* s=parent->getSample(dacSample);
if (s->samples>0) {
if (!isMuted[5]) {
if (dacReady && writes.size()<16) {
urgentWrite(0x2a,(unsigned char)s->data8[dacDirection?(s->samples-dacPos-1):dacPos]+0x80);
dacReady=false;
int sample=0;
for (int i=5; i<7; i++) {
if (chan[i].dacSample!=-1) {
DivSample* s=parent->getSample(chan[i].dacSample);
if (!isMuted[i] && s->samples>0) {
if (parent->song.noOPN2Vol) {
sample+=s->data8[chan[i].dacDirection?(s->samples-chan[i].dacPos-1):chan[i].dacPos];
} else {
sample+=(s->data8[chan[i].dacDirection?(s->samples-chan[i].dacPos-1):chan[i].dacPos]*dacVolTable[chan[i].outVol])>>7;
}
}
if (++dacPos>=s->samples) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples && !dacDirection) {
dacPos=s->loopStart;
chan[i].dacPeriod+=chan[i].dacRate;
if (chan[i].dacPeriod>=(chipClock/576)) {
if (s->samples>0) {
while (chan[i].dacPeriod>=(chipClock/576)) {
if (++chan[i].dacPos>=s->samples) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples && !chan[i].dacDirection) {
chan[i].dacPos=s->loopStart;
} else {
chan[i].dacSample=-1;
chan[i].dacPeriod=0;
break;
}
}
chan[i].dacPeriod-=(chipClock/576);
}
} else {
dacSample=-1;
chan[i].dacSample=-1;
}
}
}
}
//sample>>=1;
if (sample<-128) sample=-128;
if (sample>127) sample=127;
urgentWrite(0x2a,(unsigned char)sample+0x80);
}
} else {
if (!chan[5].dacReady) {
chan[5].dacDelay+=32000;
if (chan[5].dacDelay>=rate) {
chan[5].dacDelay-=rate;
chan[5].dacReady=true;
}
}
if (chan[5].dacMode && chan[5].dacSample!=-1) {
chan[5].dacPeriod+=chan[5].dacRate;
if (chan[5].dacPeriod>=rate) {
DivSample* s=parent->getSample(chan[5].dacSample);
if (s->samples>0) {
if (!isMuted[5]) {
if (chan[5].dacReady && writes.size()<16) {
int sample;
if (parent->song.noOPN2Vol) {
sample=s->data8[chan[5].dacDirection?(s->samples-chan[5].dacPos-1):chan[5].dacPos];
} else {
sample=(s->data8[chan[5].dacDirection?(s->samples-chan[5].dacPos-1):chan[5].dacPos]*dacVolTable[chan[5].outVol])>>7;
}
urgentWrite(0x2a,(unsigned char)sample+0x80);
chan[5].dacReady=false;
}
}
if (++chan[5].dacPos>=s->samples) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples && !chan[5].dacDirection) {
chan[5].dacPos=s->loopStart;
} else {
chan[5].dacSample=-1;
if (parent->song.brokenDACMode) {
rWrite(0x2b,0);
}
}
}
while (dacPeriod>=rate) dacPeriod-=rate;
while (chan[5].dacPeriod>=rate) chan[5].dacPeriod-=rate;
} else {
dacSample=-1;
chan[5].dacSample=-1;
}
}
}
}
}
void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) {
static short o[2];
static int os[2];
for (size_t h=start; h<start+len; h++) {
processDAC();
os[0]=0; os[1]=0;
for (int i=0; i<6; i++) {
if (!writes.empty() && --delay<0) {
@ -217,40 +271,7 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si
ymfm::ym2612::fm_engine* fme=fm_ymfm->debug_engine();
for (size_t h=start; h<start+len; h++) {
if (!dacReady) {
dacDelay+=32000;
if (dacDelay>=rate) {
dacDelay-=rate;
dacReady=true;
}
}
if (dacMode && dacSample!=-1) {
dacPeriod+=dacRate;
if (dacPeriod>=rate) {
DivSample* s=parent->getSample(dacSample);
if (s->samples>0) {
if (!isMuted[5]) {
if (dacReady && writes.size()<16) {
urgentWrite(0x2a,(unsigned char)s->data8[dacDirection?(s->samples-dacPos-1):dacPos]+0x80);
dacReady=false;
}
}
if (++dacPos>=s->samples) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples && !dacDirection) {
dacPos=s->loopStart;
} else {
dacSample=-1;
if (parent->song.brokenDACMode) {
rWrite(0x2b,0);
}
}
}
while (dacPeriod>=rate) dacPeriod-=rate;
} else {
dacSample=-1;
}
}
}
processDAC();
os[0]=0; os[1]=0;
if (!writes.empty()) {
@ -475,7 +496,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
}
for (int i=0; i<6; i++) {
for (int i=0; i<8; i++) {
if (i==2 && extMode) continue;
if (chan[i].freqChanged) {
if (parent->song.linearPitch==2) {
@ -494,12 +515,14 @@ void DivPlatformGenesis::tick(bool sysTick) {
chan[i].freq=(block<<11)|fNum;
}
if (chan[i].freq>0x3fff) chan[i].freq=0x3fff;
immWrite(chanOffs[i]+ADDR_FREQH,chan[i].freq>>8);
immWrite(chanOffs[i]+ADDR_FREQ,chan[i].freq&0xff);
if (chan[i].furnaceDac && dacMode) {
if (i<6) {
immWrite(chanOffs[i]+ADDR_FREQH,chan[i].freq>>8);
immWrite(chanOffs[i]+ADDR_FREQ,chan[i].freq&0xff);
}
if (chan[i].furnaceDac && chan[i].dacMode) {
double off=1.0;
if (dacSample>=0 && dacSample<parent->song.sampleLen) {
DivSample* s=parent->getSample(dacSample);
if (chan[i].dacSample>=0 && chan[i].dacSample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan[i].dacSample);
if (s->centerRate<1) {
off=1.0;
} else {
@ -507,14 +530,14 @@ void DivPlatformGenesis::tick(bool sysTick) {
}
}
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2,1,1);
dacRate=chan[i].freq*off;
if (dacRate<1) dacRate=1;
if (dumpWrites) addWrite(0xffff0001,dacRate);
chan[i].dacRate=chan[i].freq*off;
if (chan[i].dacRate<1) chan[i].dacRate=1;
if (dumpWrites) addWrite(0xffff0001,chan[i].dacRate);
}
chan[i].freqChanged=false;
}
if (chan[i].keyOn) {
immWrite(0x28,0xf0|konOffs[i]);
if (i<6) immWrite(0x28,0xf0|konOffs[i]);
chan[i].keyOn=false;
}
}
@ -522,6 +545,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
void DivPlatformGenesis::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
if (ch>5) return;
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[ch]|opOffs[j];
DivInstrumentFM::Operator& op=chan[ch].state.op[j];
@ -542,29 +566,29 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
if (c.chan==5) {
if (c.chan>=5) {
if (ins->type==DIV_INS_AMIGA) {
dacMode=1;
chan[c.chan].dacMode=1;
rWrite(0x2b,1<<7);
} else if (chan[c.chan].furnaceDac) {
dacMode=0;
chan[c.chan].dacMode=0;
rWrite(0x2b,0<<7);
}
}
if (c.chan==5 && dacMode) {
if (c.chan>=5 && chan[c.chan].dacMode) {
if (skipRegisterWrites) break;
if (ins->type==DIV_INS_AMIGA) { // Furnace mode
dacSample=ins->amiga.getSample(c.value);
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
dacSample=-1;
chan[c.chan].dacSample=ins->amiga.getSample(c.value);
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
chan[c.chan].dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0);
break;
} else {
rWrite(0x2b,1<<7);
if (dumpWrites) addWrite(0xffff0000,dacSample);
if (dumpWrites) addWrite(0xffff0000,chan[c.chan].dacSample);
}
dacPos=0;
dacPeriod=0;
chan[c.chan].dacPos=0;
chan[c.chan].dacPeriod=0;
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false);
chan[c.chan].freqChanged=true;
@ -574,23 +598,24 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].note=c.value;
}
dacSample=12*sampleBank+chan[c.chan].note%12;
if (dacSample>=parent->song.sampleLen) {
dacSample=-1;
chan[c.chan].dacSample=12*chan[c.chan].sampleBank+chan[c.chan].note%12;
if (chan[c.chan].dacSample>=parent->song.sampleLen) {
chan[c.chan].dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0);
break;
} else {
rWrite(0x2b,1<<7);
if (dumpWrites) addWrite(0xffff0000,dacSample);
if (dumpWrites) addWrite(0xffff0000,chan[c.chan].dacSample);
}
dacPos=0;
dacPeriod=0;
dacRate=MAX(1,parent->getSample(dacSample)->rate);
if (dumpWrites) addWrite(0xffff0001,parent->getSample(dacSample)->rate);
chan[c.chan].dacPos=0;
chan[c.chan].dacPeriod=0;
chan[c.chan].dacRate=MAX(1,parent->getSample(chan[c.chan].dacSample)->rate);
if (dumpWrites) addWrite(0xffff0001,parent->getSample(chan[c.chan].dacSample)->rate);
chan[c.chan].furnaceDac=false;
}
break;
}
if (c.chan>=6) break;
if (chan[c.chan].insChanged) {
chan[c.chan].state=ins->fm;
@ -643,12 +668,12 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_NOTE_OFF:
if (c.chan==5) {
dacSample=-1;
if (c.chan>=5) {
chan[c.chan].dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0);
if (parent->song.brokenDACMode) {
rWrite(0x2b,0);
if (dacMode) break;
if (chan[c.chan].dacMode) break;
}
}
chan[c.chan].keyOff=true;
@ -656,8 +681,8 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
chan[c.chan].active=false;
break;
case DIV_CMD_NOTE_OFF_ENV:
if (c.chan==5) {
dacSample=-1;
if (c.chan>=5) {
chan[c.chan].dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0);
}
chan[c.chan].keyOff=true;
@ -673,6 +698,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
if (!chan[c.chan].std.vol.has) {
chan[c.chan].outVol=c.value;
}
if (c.chan>=6) break;
for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -699,6 +725,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
chan[c.chan].ins=c.value;
break;
case DIV_CMD_PANNING: {
if (c.chan>5) c.chan=5;
if (c.value==0 && c.value2==0) {
chan[c.chan].pan=3;
} else {
@ -736,7 +763,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
}
break;
}
if (c.chan==5 && chan[c.chan].furnaceDac && dacMode) {
if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) {
int destFreq=parent->calcBaseFreq(1,1,c.value2,false);
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
@ -763,23 +790,26 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_SAMPLE_MODE: {
dacMode=c.value;
if (c.chan<5) c.chan=5;
chan[c.chan].dacMode=c.value;
rWrite(0x2b,c.value<<7);
break;
}
case DIV_CMD_SAMPLE_BANK:
sampleBank=c.value;
if (sampleBank>(parent->song.sample.size()/12)) {
sampleBank=parent->song.sample.size()/12;
if (c.chan<5) c.chan=5;
chan[c.chan].sampleBank=c.value;
if (chan[c.chan].sampleBank>(parent->song.sample.size()/12)) {
chan[c.chan].sampleBank=parent->song.sample.size()/12;
}
break;
case DIV_CMD_SAMPLE_DIR: {
dacDirection=c.value;
if (dumpWrites) addWrite(0xffff0003,dacDirection);
if (c.chan<5) c.chan=5;
chan[c.chan].dacDirection=c.value;
if (dumpWrites) addWrite(0xffff0003,chan[c.chan].dacDirection);
break;
}
case DIV_CMD_LEGATO: {
if (c.chan==5 && chan[c.chan].furnaceDac && dacMode) {
if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) {
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false);
} else {
chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
@ -789,16 +819,19 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_LFO: {
if (c.chan>=6) break;
lfoValue=(c.value&7)|((c.value>>4)<<3);
rWrite(0x22,lfoValue);
break;
}
case DIV_CMD_FM_FB: {
if (c.chan>=6) break;
chan[c.chan].state.fb=c.value&7;
rWrite(chanOffs[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3));
break;
}
case DIV_CMD_FM_MULT: {
if (c.chan>=6) break;
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
op.mult=c.value2&15;
@ -806,6 +839,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_TL: {
if (c.chan>=6) break;
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
op.tl=c.value2;
@ -821,6 +855,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_AR: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -837,6 +872,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_RS: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -853,6 +889,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_AM: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -869,6 +906,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_DR: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -885,6 +923,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_SL: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -901,6 +940,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_RR: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -917,6 +957,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_D2R: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -933,6 +974,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_DT: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -949,6 +991,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_SSG: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -965,6 +1008,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_HARD_RESET:
if (c.chan>=6) break;
chan[c.chan].hardReset=c.value;
break;
case DIV_ALWAYS_SET_VOLUME:
@ -1013,7 +1057,7 @@ void DivPlatformGenesis::forceIns() {
chan[i].freqChanged=true;
}
}
if (dacMode) {
if (chan[5].dacMode) {
rWrite(0x2b,0x80);
}
immWrite(0x22,lfoValue);
@ -1063,20 +1107,17 @@ void DivPlatformGenesis::reset() {
}
lastBusy=60;
dacMode=0;
dacPeriod=0;
dacPos=0;
dacRate=0;
dacDelay=0;
dacReady=true;
dacSample=-1;
dacDirection=false;
sampleBank=0;
lfoValue=8;
softPCMTimer=0;
extMode=false;
if (dumpWrites) addWrite(0xffff0003,dacDirection);
if (softPCM) {
chan[5].dacMode=true;
chan[6].dacMode=true;
}
// normal sample direction
if (dumpWrites) addWrite(0xffff0003,0);
// LFO
immWrite(0x22,lfoValue);
@ -1097,7 +1138,7 @@ bool DivPlatformGenesis::keyOffAffectsPorta(int ch) {
}
void DivPlatformGenesis::notifyInsChange(int ins) {
for (int i=0; i<6; i++) {
for (int i=0; i<10; i++) {
if (chan[i].ins==ins) {
chan[i].insChanged=true;
}
@ -1123,6 +1164,10 @@ void DivPlatformGenesis::setYMFM(bool use) {
useYMFM=use;
}
void DivPlatformGenesis::setSoftPCM(bool value) {
softPCM=value;
}
void DivPlatformGenesis::setFlags(unsigned int flags) {
switch (flags) {
case 1: chipClock=COLOR_PAL*12.0/7.0; break;
@ -1161,6 +1206,11 @@ int DivPlatformGenesis::init(DivEngine* p, int channels, int sugRate, unsigned i
fm_ymfm=NULL;
setFlags(flags);
for (int i=0; i<128; i++) {
dacVolTable[127-i]=128*pow(10.0f,(float)(-i)*0.75f/20.0f);
}
dacVolTable[0]=0;
reset();
return 10;
}

View File

@ -41,6 +41,16 @@ class DivPlatformGenesis: public DivDispatch {
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, hardReset;
int vol, outVol;
unsigned char pan;
bool dacMode;
int dacPeriod;
int dacRate;
unsigned int dacPos;
int dacSample;
int dacDelay;
bool dacReady;
bool dacDirection;
unsigned char sampleBank;
void macroInit(DivInstrument* which) {
std.init(which);
pitch2=0;
@ -65,7 +75,17 @@ class DivPlatformGenesis: public DivDispatch {
inPorta(false),
hardReset(false),
vol(0),
pan(3) {}
outVol(0),
pan(3),
dacMode(false),
dacPeriod(0),
dacRate(0),
dacPos(0),
dacSample(-1),
dacDelay(0),
dacReady(true),
dacDirection(false),
sampleBank(0) {}
};
Channel chan[10];
DivDispatchOscBuffer* oscBuf[10];
@ -86,25 +106,21 @@ class DivPlatformGenesis: public DivDispatch {
DivYM2612Interface iface;
unsigned char regPool[512];
bool dacMode;
int dacPeriod;
int dacRate;
unsigned int dacPos;
int dacSample;
int dacDelay;
bool dacReady;
bool dacDirection;
unsigned char sampleBank;
unsigned char lfoValue;
bool extMode, useYMFM;
int softPCMTimer;
bool extMode, softPCM, useYMFM;
bool ladder;
short oldWrites[512];
short pendingWrites[512];
unsigned char dacVolTable[128];
friend void putDispatchChan(void*,int,int);
inline void processDAC();
void acquire_nuked(short* bufL, short* bufR, size_t start, size_t len);
void acquire_ymfm(short* bufL, short* bufR, size_t start, size_t len);
@ -127,6 +143,7 @@ class DivPlatformGenesis: public DivDispatch {
void setFlags(unsigned int flags);
void notifyInsChange(int ins);
void notifyInsDeletion(void* ins);
void setSoftPCM(bool value);
int getPortaFloor(int ch);
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);

View File

@ -156,16 +156,16 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
case DIV_CMD_SAMPLE_MODE: {
// not ignored actually!
if (!parent->song.ignoreDACModeOutsideIntendedChannel) {
dacMode=c.value;
chan[5].dacMode=c.value;
rWrite(0x2b,c.value<<7);
}
break;
}
case DIV_CMD_SAMPLE_BANK:
if (!parent->song.ignoreDACModeOutsideIntendedChannel) {
sampleBank=c.value;
if (sampleBank>(parent->song.sample.size()/12)) {
sampleBank=parent->song.sample.size()/12;
chan[5].sampleBank=c.value;
if (chan[5].sampleBank>(parent->song.sample.size()/12)) {
chan[5].sampleBank=parent->song.sample.size()/12;
}
}
break;
@ -484,7 +484,7 @@ void DivPlatformGenesisExt::forceIns() {
chan[i].freqChanged=true;
}
}
if (dacMode) {
if (chan[5].dacMode) {
rWrite(0x2b,0x80);
}
immWrite(0x22,lfoValue);

View File

@ -111,6 +111,8 @@ enum DivSystem {
DIV_SYSTEM_NAMCO,
DIV_SYSTEM_NAMCO_15XX,
DIV_SYSTEM_NAMCO_CUS30,
DIV_SYSTEM_YM2612_FRAC,
DIV_SYSTEM_YM2612_FRAC_EXT,
DIV_SYSTEM_DUMMY
};
@ -394,6 +396,7 @@ struct DivSong {
bool snDutyReset;
bool pitchMacroIsLinear;
bool oldOctaveBoundary;
bool noOPN2Vol;
std::vector<DivInstrument*> ins;
std::vector<DivWavetable*> wave;
@ -487,7 +490,8 @@ struct DivSong {
fbPortaPause(false),
snDutyReset(false),
pitchMacroIsLinear(true),
oldOctaveBoundary(false) {
oldOctaveBoundary(false),
noOPN2Vol(false) {
for (int i=0; i<32; i++) {
system[i]=DIV_SYSTEM_NULL;
systemVol[i]=64;

View File

@ -2022,6 +2022,30 @@ void DivEngine::registerSystems() {
namcoEffectHandler
);
sysDefs[DIV_SYSTEM_YM2612_FRAC]=new DivSysDef(
"Yamaha YM2612 (OPN2) with DualPCM", NULL, 0xbc, 0, 8, true, false, 0, false,
"this chip is mostly known for being in the Sega Genesis (but it also was on the FM Towns computer).\nthis system uses software mixing to provide two sample channels.",
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6/PCM 1", "PCM 2", "CSM Timer"},
{"F1", "F2", "F3", "F4", "F5", "P1", "P2", "CSM"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_NOISE},
{DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AMIGA, DIV_INS_FM},
{DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA, DIV_INS_NULL, DIV_INS_NULL},
opn2EffectHandler,
fmPostEffectHandler
);
sysDefs[DIV_SYSTEM_YM2612_FRAC_EXT]=new DivSysDef(
"Yamaha YM2612 (OPN2) Extended Channel 3 with DualPCM", NULL, 0xbd, 0, 11, true, false, 0, false,
"this chip is mostly known for being in the Sega Genesis (but it also was on the FM Towns computer).\nthis system uses software mixing to provide two sample channels.\nthis one is in Extended Channel mode, which turns the second FM channel into four operators with independent notes/frequencies.",
{"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "FM 4", "FM 5", "FM 6/PCM 1", "PCM 2", "CSM Timer"},
{"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "P1", "P2", "CSM"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_NOISE},
{DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AMIGA, DIV_INS_FM},
{DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA, DIV_INS_NULL, DIV_INS_NULL},
opn2EffectHandler,
fmPostEffectHandler
);
sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef(
"Dummy System", NULL, 0xfd, 0, 8, false, true, 0, false,
"this is a system designed for testing purposes.",

View File

@ -213,6 +213,10 @@ void FurnaceGUI::drawCompatFlags() {
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("behavior changed in 0.6");
}
ImGui::Checkbox("No OPN2 DAC volume control",&e->song.noOPN2Vol);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("behavior changed in 0.6");
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_COMPAT_FLAGS;
ImGui::End();

View File

@ -825,6 +825,8 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
const int availableSystems[]={
DIV_SYSTEM_YM2612,
DIV_SYSTEM_YM2612_EXT,
DIV_SYSTEM_YM2612_FRAC,
DIV_SYSTEM_YM2612_FRAC_EXT,
DIV_SYSTEM_SMS,
DIV_SYSTEM_GB,
DIV_SYSTEM_PCE,

View File

@ -102,6 +102,18 @@ void FurnaceGUI::initSystemPresets() {
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2612 (OPN2) with DualPCM", {
DIV_SYSTEM_YM2612, 64, 0, (int)0x80000000,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2612 (extended channel 3) with DualPCM", {
DIV_SYSTEM_YM2612_EXT, 64, 0, (int)0x80000000,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2413 (OPLL)", {
DIV_SYSTEM_OPLL, 64, 0, 0,
@ -132,6 +144,18 @@ void FurnaceGUI::initSystemPresets() {
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM3438 (OPN2C) with DualPCM", {
DIV_SYSTEM_YM2612_FRAC, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM3438 (extended channel 3) with DualPCM", {
DIV_SYSTEM_YM2612_FRAC_EXT, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM3526 (OPL)", {
DIV_SYSTEM_OPL, 64, 0, 0,
@ -429,6 +453,20 @@ void FurnaceGUI::initSystemPresets() {
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Sega Genesis (Fractal Sound template)", {
DIV_SYSTEM_YM2612_FRAC, 64, 0, 0,
DIV_SYSTEM_SMS, 24, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Sega Genesis (Fractal Sound template, extended channel 3)", {
DIV_SYSTEM_YM2612_FRAC_EXT, 64, 0, 0,
DIV_SYSTEM_SMS, 24, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Sega Genesis (with Sega CD)", {
DIV_SYSTEM_YM2612, 64, 0, 0,