Some fleshing out YMF278B
Add OpenMSX YMF278B core option, Expand RAM size option
This commit is contained in:
parent
a8ec76699b
commit
221fa5aa42
|
@ -763,14 +763,20 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
||||||
case DIV_SYSTEM_OPL4:
|
case DIV_SYSTEM_OPL4:
|
||||||
dispatch=new DivPlatformOPL;
|
dispatch=new DivPlatformOPL;
|
||||||
((DivPlatformOPL*)dispatch)->setOPLType(4,false);
|
((DivPlatformOPL*)dispatch)->setOPLType(4,false);
|
||||||
// YMFM for now
|
if (isRender) {
|
||||||
((DivPlatformOPL*)dispatch)->setCore(1);
|
((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl4CoreRender",0));
|
||||||
|
} else {
|
||||||
|
((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl4Core",0));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case DIV_SYSTEM_OPL4_DRUMS:
|
case DIV_SYSTEM_OPL4_DRUMS:
|
||||||
dispatch=new DivPlatformOPL;
|
dispatch=new DivPlatformOPL;
|
||||||
((DivPlatformOPL*)dispatch)->setOPLType(4,true);
|
((DivPlatformOPL*)dispatch)->setOPLType(4,true);
|
||||||
// YMFM for now
|
if (isRender) {
|
||||||
((DivPlatformOPL*)dispatch)->setCore(1);
|
((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl4CoreRender",0));
|
||||||
|
} else {
|
||||||
|
((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl4Core",0));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case DIV_SYSTEM_DUMMY:
|
case DIV_SYSTEM_DUMMY:
|
||||||
dispatch=new DivPlatformDummy;
|
dispatch=new DivPlatformDummy;
|
||||||
|
|
|
@ -29,6 +29,12 @@
|
||||||
#define KVSL(x,y) ((chan[x].state.op[orderedOpsL1[ops==4][y]].kvs==2 && isOutputL[ops==4][chan[x].state.alg][y]) || chan[x].state.op[orderedOpsL1[ops==4][y]].kvs==1)
|
#define KVSL(x,y) ((chan[x].state.op[orderedOpsL1[ops==4][y]].kvs==2 && isOutputL[ops==4][chan[x].state.alg][y]) || chan[x].state.op[orderedOpsL1[ops==4][y]].kvs==1)
|
||||||
|
|
||||||
#define CHIP_FREQBASE chipFreqBase
|
#define CHIP_FREQBASE chipFreqBase
|
||||||
|
#define PCM_FREQBASE (402653184)
|
||||||
|
|
||||||
|
#define NOTE_PCM(x) parent->calcBaseFreq(chipClock,PCM_FREQBASE,x,false)
|
||||||
|
|
||||||
|
#define PCM_CHECK(ch) ((chipType==4) && (ch>=pcmChanOffs))
|
||||||
|
#define PCM_REG(ch) (ch-pcmChanOffs)
|
||||||
|
|
||||||
// N = invalid
|
// N = invalid
|
||||||
#define N 255
|
#define N 255
|
||||||
|
@ -176,36 +182,42 @@ const int orderedOpsL[4]={
|
||||||
#define PCM_ADDR_MIX_PCM 0x2f9
|
#define PCM_ADDR_MIX_PCM 0x2f9
|
||||||
|
|
||||||
void DivPlatformOPL::acquire_nuked(short** buf, size_t len) {
|
void DivPlatformOPL::acquire_nuked(short** buf, size_t len) {
|
||||||
thread_local short o[4];
|
thread_local short o[8];
|
||||||
thread_local int os[4];
|
thread_local int os[6];
|
||||||
thread_local ymfm::ymfm_output<2> aOut;
|
thread_local ymfm::ymfm_output<2> aOut;
|
||||||
|
|
||||||
for (size_t h=0; h<len; h++) {
|
for (size_t h=0; h<len; h++) {
|
||||||
os[0]=0; os[1]=0; os[2]=0; os[3]=0;
|
os[0]=0; os[1]=0; os[2]=0; os[3]=0; os[4]=0; os[5]=0;
|
||||||
|
short pcmBuf[24]={0};
|
||||||
if (!writes.empty() && --delay<0) {
|
if (!writes.empty() && --delay<0) {
|
||||||
delay=1;
|
delay=1;
|
||||||
QueuedWrite& w=writes.front();
|
QueuedWrite& w=writes.front();
|
||||||
switch (w.addr) {
|
if (w.addr>=0x200) {
|
||||||
case 8:
|
pcm.writeReg(w.addr&0xff,w.val);
|
||||||
if (adpcmChan>=0) {
|
regPool[0x200|(w.addr&0xff)]=w.val;
|
||||||
adpcmB->write(w.addr-7,(w.val&15)|0x80);
|
} else {
|
||||||
OPL3_WriteReg(&fm,w.addr,w.val&0xc0);
|
switch (w.addr) {
|
||||||
} else {
|
case 8:
|
||||||
|
if (adpcmChan>=0) {
|
||||||
|
adpcmB->write(w.addr-7,(w.val&15)|0x80);
|
||||||
|
OPL3_WriteReg(&fm,w.addr,w.val&0xc0);
|
||||||
|
} else {
|
||||||
|
OPL3_WriteReg(&fm,w.addr,w.val);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 7: case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 21: case 22: case 23:
|
||||||
|
if (adpcmChan>=0) {
|
||||||
|
adpcmB->write(w.addr-7,w.val);
|
||||||
|
} else {
|
||||||
|
OPL3_WriteReg(&fm,w.addr,w.val);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
OPL3_WriteReg(&fm,w.addr,w.val);
|
OPL3_WriteReg(&fm,w.addr,w.val);
|
||||||
}
|
break;
|
||||||
break;
|
}
|
||||||
case 7: case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 21: case 22: case 23:
|
regPool[w.addr&511]=w.val;
|
||||||
if (adpcmChan>=0) {
|
|
||||||
adpcmB->write(w.addr-7,w.val);
|
|
||||||
} else {
|
|
||||||
OPL3_WriteReg(&fm,w.addr,w.val);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
OPL3_WriteReg(&fm,w.addr,w.val);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
regPool[w.addr&511]=w.val;
|
|
||||||
writes.pop();
|
writes.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,10 +226,20 @@ void DivPlatformOPL::acquire_nuked(short** buf, size_t len) {
|
||||||
} else {
|
} else {
|
||||||
OPL3_Generate4Ch(&fm,o);
|
OPL3_Generate4Ch(&fm,o);
|
||||||
}
|
}
|
||||||
os[0]+=o[0];
|
if (chipType==4) {
|
||||||
os[1]+=o[1];
|
pcm.generateMix(o[0],o[1],o[4],o[5],o[6],o[7],pcmBuf);
|
||||||
os[2]+=o[2];
|
os[0]+=o[4];
|
||||||
os[3]+=o[3];
|
os[1]+=o[5];
|
||||||
|
os[2]+=o[2];
|
||||||
|
os[3]+=o[3];
|
||||||
|
os[4]+=o[6];
|
||||||
|
os[5]+=o[7];
|
||||||
|
} else {
|
||||||
|
os[0]+=o[0];
|
||||||
|
os[1]+=o[1];
|
||||||
|
os[2]+=o[2];
|
||||||
|
os[3]+=o[3];
|
||||||
|
}
|
||||||
|
|
||||||
if (adpcmChan>=0) {
|
if (adpcmChan>=0) {
|
||||||
adpcmB->clock();
|
adpcmB->clock();
|
||||||
|
@ -290,6 +312,12 @@ void DivPlatformOPL::acquire_nuked(short** buf, size_t len) {
|
||||||
if (os[3]<-32768) os[3]=-32768;
|
if (os[3]<-32768) os[3]=-32768;
|
||||||
if (os[3]>32767) os[3]=32767;
|
if (os[3]>32767) os[3]=32767;
|
||||||
|
|
||||||
|
if (os[4]<-32768) os[4]=-32768;
|
||||||
|
if (os[4]>32767) os[4]=32767;
|
||||||
|
|
||||||
|
if (os[5]<-32768) os[5]=-32768;
|
||||||
|
if (os[5]>32767) os[5]=32767;
|
||||||
|
|
||||||
buf[0][h]=os[0];
|
buf[0][h]=os[0];
|
||||||
if (totalOutputs>1) {
|
if (totalOutputs>1) {
|
||||||
buf[1][h]=os[1];
|
buf[1][h]=os[1];
|
||||||
|
@ -301,9 +329,8 @@ void DivPlatformOPL::acquire_nuked(short** buf, size_t len) {
|
||||||
buf[3][h]=os[3];
|
buf[3][h]=os[3];
|
||||||
}
|
}
|
||||||
if (totalOutputs==6) {
|
if (totalOutputs==6) {
|
||||||
// placeholder for OPL4
|
buf[4][h]=os[4];
|
||||||
buf[4][h]=0;
|
buf[5][h]=os[5];
|
||||||
buf[5][h]=0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -947,22 +974,44 @@ double DivPlatformOPL::NOTE_ADPCMB(int note) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
double DivPlatformOPL::NOTE_OPL4(int ch, int note) { // TODO
|
|
||||||
if (pcmChanOffs<0) return 0;
|
|
||||||
if (chan[ch].sample>=0 && chan[ch].sample<parent->song.sampleLen) {
|
|
||||||
double off=65535.0*(double)(parent->getSample(chan[ch].sample)->centerRate)/8363.0;
|
|
||||||
return parent->calcBaseFreq((double)chipClock/768,off,note,false);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DivPlatformOPL::tick(bool sysTick) {
|
void DivPlatformOPL::tick(bool sysTick) {
|
||||||
for (int i=0; i<totalChans; i++) {
|
for (int i=0; i<totalChans; i++) {
|
||||||
if (i>=pcmChanOffs) { // OPL4 PCM
|
if (PCM_CHECK(i)) { // OPL4 PCM
|
||||||
chan[i].std.next();
|
chan[i].std.next();
|
||||||
if (chan[i].std.vol.had) {
|
if (chan[i].std.vol.had) {
|
||||||
chan[i].outVol=VOL_SCALE_LOG((chan[i].vol&0x7f),(0x7f*chan[i].std.vol.val)/chan[i].macroVolMul,0x7f);
|
chan[i].outVol=VOL_SCALE_LOG((chan[i].vol&0x7f),(0x7f*chan[i].std.vol.val)/chan[i].macroVolMul,0x7f);
|
||||||
immWrite(PCM_ADDR_TL+(i-pcmChanOffs),((0x7f-chan[i].outVol)<<1)|(chan[i].levelDirect?1:0));
|
immWrite(PCM_ADDR_TL+(PCM_REG(i)),((0x7f-chan[i].outVol)<<1)|(chan[i].levelDirect?1:0));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NEW_ARP_STRAT) {
|
||||||
|
chan[i].handleArp();
|
||||||
|
} else if (chan[i].std.arp.had) {
|
||||||
|
if (!chan[i].inPorta) {
|
||||||
|
chan[i].baseFreq=NOTE_PCM(parent->calcArp(chan[i].note,chan[i].std.arp.val));
|
||||||
|
}
|
||||||
|
chan[i].freqChanged=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chan[i].std.pitch.had) {
|
||||||
|
if (chan[i].std.pitch.mode) {
|
||||||
|
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||||
|
CLAMP_VAR(chan[i].pitch2,-131071,131071);
|
||||||
|
} else {
|
||||||
|
chan[i].pitch2=chan[i].std.pitch.val;
|
||||||
|
}
|
||||||
|
chan[i].freqChanged=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chan[i].std.phaseReset.had) {
|
||||||
|
if (chan[i].std.phaseReset.val==1 && chan[i].active) {
|
||||||
|
chan[i].keyOn=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chan[i].std.panL.had) { // panning
|
||||||
|
chan[i].pan=chan[i].std.panL.val&0xf;
|
||||||
|
chan[i].freqChanged=true;
|
||||||
|
chan[i].writeCtrl=true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||||
|
@ -1013,11 +1062,12 @@ void DivPlatformOPL::tick(bool sysTick) {
|
||||||
waveNum=MIN(waveNum,0x7f)|0x180;
|
waveNum=MIN(waveNum,0x7f)|0x180;
|
||||||
}
|
}
|
||||||
if (chan[i].keyOn) {
|
if (chan[i].keyOn) {
|
||||||
rWrite(PCM_ADDR_KEY_DAMP_LFORST_CH_PAN+(i-pcmChanOffs),ctrl&~0x80); // force keyoff first
|
rWrite(PCM_ADDR_KEY_DAMP_LFORST_CH_PAN+PCM_REG(i),ctrl&~0x80); // force keyoff first
|
||||||
rWrite(PCM_ADDR_WAVE_H_FN_L+(i-pcmChanOffs),((chan[i].freqL&0x7f)<<1)|((waveNum>>8)&1));
|
rWrite(PCM_ADDR_WAVE_H_FN_L+PCM_REG(i),((chan[i].freqL&0x7f)<<1)|((waveNum>>8)&1));
|
||||||
rWrite(PCM_ADDR_WAVE_L+(i-pcmChanOffs),waveNum&0xff);
|
rWrite(PCM_ADDR_WAVE_L+PCM_REG(i),waveNum&0xff);
|
||||||
if (!chan[i].std.vol.had) {
|
if (!chan[i].std.vol.had) {
|
||||||
chan[i].outVol=chan[i].vol;
|
chan[i].outVol=chan[i].vol;
|
||||||
|
immWrite(PCM_ADDR_TL+(PCM_REG(i)),((0x7f-chan[i].outVol)<<1)|(chan[i].levelDirect?1:0));
|
||||||
}
|
}
|
||||||
chan[i].writeCtrl=true;
|
chan[i].writeCtrl=true;
|
||||||
chan[i].keyOn=false;
|
chan[i].keyOn=false;
|
||||||
|
@ -1027,12 +1077,12 @@ void DivPlatformOPL::tick(bool sysTick) {
|
||||||
chan[i].keyOff=false;
|
chan[i].keyOff=false;
|
||||||
}
|
}
|
||||||
if (chan[i].freqChanged) {
|
if (chan[i].freqChanged) {
|
||||||
rWrite(PCM_ADDR_WAVE_H_FN_L+(i-pcmChanOffs),((chan[i].freqL&0x7f)<<1)|((waveNum>>8)&1));
|
rWrite(PCM_ADDR_WAVE_H_FN_L+PCM_REG(i),((chan[i].freqL&0x7f)<<1)|((waveNum>>8)&1));
|
||||||
rWrite(PCM_ADDR_FN_H_PR_OCT+(i-pcmChanOffs),((chan[i].freqH&0xf)<<4)|(chan[i].pseudoReverb?0x08:0x00)|((chan[i].freqL>>7)&0x7));
|
rWrite(PCM_ADDR_FN_H_PR_OCT+PCM_REG(i),((chan[i].freqH&0xf)<<4)|(chan[i].pseudoReverb?0x08:0x00)|((chan[i].freqL>>7)&0x7));
|
||||||
chan[i].freqChanged=false;
|
chan[i].freqChanged=false;
|
||||||
}
|
}
|
||||||
if (chan[i].writeCtrl) {
|
if (chan[i].writeCtrl) {
|
||||||
rWrite(PCM_ADDR_KEY_DAMP_LFORST_CH_PAN+(i-pcmChanOffs),ctrl);
|
rWrite(PCM_ADDR_KEY_DAMP_LFORST_CH_PAN+PCM_REG(i),ctrl);
|
||||||
chan[i].writeCtrl=false;
|
chan[i].writeCtrl=false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1309,7 +1359,7 @@ void DivPlatformOPL::tick(bool sysTick) {
|
||||||
|
|
||||||
bool updateDrums=false;
|
bool updateDrums=false;
|
||||||
for (int i=0; i<totalChans; i++) {
|
for (int i=0; i<totalChans; i++) {
|
||||||
if (i>=pcmChanOffs) { // OPL4 PCM
|
if (PCM_CHECK(i)) { // OPL4 PCM
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (chan[i].freqChanged) {
|
if (chan[i].freqChanged) {
|
||||||
|
@ -1426,7 +1476,7 @@ int DivPlatformOPL::toFreq(int freq) {
|
||||||
|
|
||||||
void DivPlatformOPL::muteChannel(int ch, bool mute) {
|
void DivPlatformOPL::muteChannel(int ch, bool mute) {
|
||||||
isMuted[ch]=mute;
|
isMuted[ch]=mute;
|
||||||
if (ch>=pcmChanOffs) {
|
if (PCM_CHECK(ch)) {
|
||||||
chan[ch].freqChanged=true;
|
chan[ch].freqChanged=true;
|
||||||
chan[ch].writeCtrl=true;
|
chan[ch].writeCtrl=true;
|
||||||
return;
|
return;
|
||||||
|
@ -1479,6 +1529,9 @@ void DivPlatformOPL::muteChannel(int ch, bool mute) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivPlatformOPL::commitState(int ch, DivInstrument* ins) {
|
void DivPlatformOPL::commitState(int ch, DivInstrument* ins) {
|
||||||
|
if (PCM_CHECK(ch)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (chan[ch].insChanged) {
|
if (chan[ch].insChanged) {
|
||||||
if (ch>melodicChans && ins->type==DIV_INS_OPL_DRUMS) {
|
if (ch>melodicChans && ins->type==DIV_INS_OPL_DRUMS) {
|
||||||
for (int i=0; i<4; i++) {
|
for (int i=0; i<4; i++) {
|
||||||
|
@ -1583,7 +1636,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
}
|
}
|
||||||
switch (c.cmd) {
|
switch (c.cmd) {
|
||||||
case DIV_CMD_NOTE_ON: {
|
case DIV_CMD_NOTE_ON: {
|
||||||
if (c.chan>=pcmChanOffs) { // OPL4 PCM
|
if (PCM_CHECK(c.chan)) { // OPL4 PCM
|
||||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
||||||
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:127;
|
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:127;
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
|
@ -1593,7 +1646,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||||
}
|
}
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
chan[c.chan].baseFreq=NOTE_PCM(c.value);
|
||||||
}
|
}
|
||||||
if (chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) {
|
if (chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) {
|
||||||
chan[c.chan].sample=-1;
|
chan[c.chan].sample=-1;
|
||||||
|
@ -1733,7 +1786,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
chan[c.chan].keyOff=true;
|
chan[c.chan].keyOff=true;
|
||||||
chan[c.chan].keyOn=false;
|
chan[c.chan].keyOn=false;
|
||||||
chan[c.chan].active=false;
|
chan[c.chan].active=false;
|
||||||
if (c.chan>=pcmChanOffs) {
|
if (PCM_CHECK(c.chan)) {
|
||||||
chan[c.chan].sample=-1;
|
chan[c.chan].sample=-1;
|
||||||
chan[c.chan].macroInit(NULL);
|
chan[c.chan].macroInit(NULL);
|
||||||
}
|
}
|
||||||
|
@ -1757,8 +1810,8 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
if (!chan[c.chan].std.vol.has) {
|
if (!chan[c.chan].std.vol.has) {
|
||||||
chan[c.chan].outVol=c.value;
|
chan[c.chan].outVol=c.value;
|
||||||
}
|
}
|
||||||
if (c.chan>=pcmChanOffs) { // OPL4 PCM
|
if (PCM_CHECK(c.chan)) { // OPL4 PCM
|
||||||
immWrite(PCM_ADDR_TL+(c.chan-pcmChanOffs),((0x7f-chan[c.chan].outVol)<<1)|(chan[c.chan].levelDirect?1:0));
|
immWrite(PCM_ADDR_TL+PCM_REG(c.chan),((0x7f-chan[c.chan].outVol)<<1)|(chan[c.chan].levelDirect?1:0));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (c.chan==adpcmChan) { // ADPCM-B
|
if (c.chan==adpcmChan) { // ADPCM-B
|
||||||
|
@ -1798,6 +1851,12 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
chan[c.chan].ins=c.value;
|
chan[c.chan].ins=c.value;
|
||||||
break;
|
break;
|
||||||
case DIV_CMD_PANNING: {
|
case DIV_CMD_PANNING: {
|
||||||
|
if (PCM_CHECK(c.chan)) {
|
||||||
|
chan[c.chan].pan=8^MIN(parent->convertPanSplitToLinearLR(c.value,c.value2,15)+1,15);
|
||||||
|
chan[c.chan].freqChanged=true;
|
||||||
|
chan[c.chan].writeCtrl=true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (oplType!=3) break;
|
if (oplType!=3) break;
|
||||||
if (c.chan==adpcmChan) break;
|
if (c.chan==adpcmChan) break;
|
||||||
chan[c.chan].pan&=~3;
|
chan[c.chan].pan&=~3;
|
||||||
|
@ -1856,6 +1915,29 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DIV_CMD_NOTE_PORTA: {
|
case DIV_CMD_NOTE_PORTA: {
|
||||||
|
if (PCM_CHECK(c.chan)) {
|
||||||
|
int destFreq=NOTE_PCM(c.value2+chan[c.chan].sampleNoteDelta);
|
||||||
|
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;
|
||||||
|
}
|
||||||
int destFreq=(c.chan==adpcmChan)?(NOTE_ADPCMB(c.value2)):(NOTE_FREQUENCY(c.value2));
|
int destFreq=(c.chan==adpcmChan)?(NOTE_ADPCMB(c.value2)):(NOTE_FREQUENCY(c.value2));
|
||||||
int newFreq;
|
int newFreq;
|
||||||
bool return2=false;
|
bool return2=false;
|
||||||
|
@ -1901,13 +1983,14 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
commitState(c.chan,ins);
|
commitState(c.chan,ins);
|
||||||
chan[c.chan].insChanged=false;
|
chan[c.chan].insChanged=false;
|
||||||
}
|
}
|
||||||
chan[c.chan].baseFreq=(c.chan==adpcmChan)?(NOTE_ADPCMB(c.value)):(NOTE_FREQUENCY(c.value));
|
chan[c.chan].baseFreq=(PCM_CHECK(c.chan))?NOTE_PCM(c.value):
|
||||||
|
(c.chan==adpcmChan)?(NOTE_ADPCMB(c.value)):(NOTE_FREQUENCY(c.value));
|
||||||
chan[c.chan].note=c.value;
|
chan[c.chan].note=c.value;
|
||||||
chan[c.chan].freqChanged=true;
|
chan[c.chan].freqChanged=true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DIV_CMD_FM_LFO: {
|
case DIV_CMD_FM_LFO: {
|
||||||
if (c.chan>=pcmChanOffs) break;
|
if (PCM_CHECK(c.chan)) break;
|
||||||
if (c.chan==adpcmChan) break;
|
if (c.chan==adpcmChan) break;
|
||||||
if (c.value&2) {
|
if (c.value&2) {
|
||||||
dvb=c.value&1;
|
dvb=c.value&1;
|
||||||
|
@ -1918,7 +2001,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DIV_CMD_FM_FB: {
|
case DIV_CMD_FM_FB: {
|
||||||
if (c.chan>=pcmChanOffs) break;
|
if (PCM_CHECK(c.chan)) break;
|
||||||
if (c.chan==adpcmChan) break;
|
if (c.chan==adpcmChan) break;
|
||||||
chan[c.chan].state.fb=c.value&7;
|
chan[c.chan].state.fb=c.value&7;
|
||||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||||
|
@ -1936,7 +2019,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DIV_CMD_FM_MULT: {
|
case DIV_CMD_FM_MULT: {
|
||||||
if (c.chan>=pcmChanOffs) break;
|
if (PCM_CHECK(c.chan)) break;
|
||||||
if (c.chan==adpcmChan) break;
|
if (c.chan==adpcmChan) break;
|
||||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||||
if (c.value>=ops) break;
|
if (c.value>=ops) break;
|
||||||
|
@ -1949,7 +2032,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DIV_CMD_FM_TL: {
|
case DIV_CMD_FM_TL: {
|
||||||
if (c.chan>=pcmChanOffs) break;
|
if (PCM_CHECK(c.chan)) break;
|
||||||
if (c.chan==adpcmChan) break;
|
if (c.chan==adpcmChan) break;
|
||||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||||
if (c.value>=ops) break;
|
if (c.value>=ops) break;
|
||||||
|
@ -1970,7 +2053,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DIV_CMD_FM_AR: {
|
case DIV_CMD_FM_AR: {
|
||||||
if (c.chan>=pcmChanOffs) break;
|
if (PCM_CHECK(c.chan)) break;
|
||||||
if (c.chan==adpcmChan) break;
|
if (c.chan==adpcmChan) break;
|
||||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||||
if (c.value<0) {
|
if (c.value<0) {
|
||||||
|
@ -1994,7 +2077,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DIV_CMD_FM_DR: {
|
case DIV_CMD_FM_DR: {
|
||||||
if (c.chan>=pcmChanOffs) break;
|
if (PCM_CHECK(c.chan)) break;
|
||||||
if (c.chan==adpcmChan) break;
|
if (c.chan==adpcmChan) break;
|
||||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||||
if (c.value<0) {
|
if (c.value<0) {
|
||||||
|
@ -2018,7 +2101,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DIV_CMD_FM_SL: {
|
case DIV_CMD_FM_SL: {
|
||||||
if (c.chan>=pcmChanOffs) break;
|
if (PCM_CHECK(c.chan)) break;
|
||||||
if (c.chan==adpcmChan) break;
|
if (c.chan==adpcmChan) break;
|
||||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||||
if (c.value<0) {
|
if (c.value<0) {
|
||||||
|
@ -2042,7 +2125,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DIV_CMD_FM_RR: {
|
case DIV_CMD_FM_RR: {
|
||||||
if (c.chan>=pcmChanOffs) break;
|
if (PCM_CHECK(c.chan)) break;
|
||||||
if (c.chan==adpcmChan) break;
|
if (c.chan==adpcmChan) break;
|
||||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||||
if (c.value<0) {
|
if (c.value<0) {
|
||||||
|
@ -2066,7 +2149,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DIV_CMD_FM_AM: {
|
case DIV_CMD_FM_AM: {
|
||||||
if (c.chan>=pcmChanOffs) break;
|
if (PCM_CHECK(c.chan)) break;
|
||||||
if (c.chan==adpcmChan) break;
|
if (c.chan==adpcmChan) break;
|
||||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||||
if (c.value<0) {
|
if (c.value<0) {
|
||||||
|
@ -2090,7 +2173,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DIV_CMD_FM_VIB: {
|
case DIV_CMD_FM_VIB: {
|
||||||
if (c.chan>=pcmChanOffs) break;
|
if (PCM_CHECK(c.chan)) break;
|
||||||
if (c.chan==adpcmChan) break;
|
if (c.chan==adpcmChan) break;
|
||||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||||
if (c.value<0) {
|
if (c.value<0) {
|
||||||
|
@ -2114,7 +2197,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DIV_CMD_FM_SUS: {
|
case DIV_CMD_FM_SUS: {
|
||||||
if (c.chan>=pcmChanOffs) break;
|
if (PCM_CHECK(c.chan)) break;
|
||||||
if (c.chan==adpcmChan) break;
|
if (c.chan==adpcmChan) break;
|
||||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||||
if (c.value<0) {
|
if (c.value<0) {
|
||||||
|
@ -2138,7 +2221,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DIV_CMD_FM_KSR: {
|
case DIV_CMD_FM_KSR: {
|
||||||
if (c.chan>=pcmChanOffs) break;
|
if (PCM_CHECK(c.chan)) break;
|
||||||
if (c.chan==adpcmChan) break;
|
if (c.chan==adpcmChan) break;
|
||||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||||
if (c.value<0) {
|
if (c.value<0) {
|
||||||
|
@ -2162,7 +2245,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DIV_CMD_FM_WS: {
|
case DIV_CMD_FM_WS: {
|
||||||
if (c.chan>=pcmChanOffs) break;
|
if (PCM_CHECK(c.chan)) break;
|
||||||
if (c.chan==adpcmChan) break;
|
if (c.chan==adpcmChan) break;
|
||||||
if (oplType<2) break;
|
if (oplType<2) break;
|
||||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||||
|
@ -2187,7 +2270,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DIV_CMD_FM_RS: {
|
case DIV_CMD_FM_RS: {
|
||||||
if (c.chan>=pcmChanOffs) break;
|
if (PCM_CHECK(c.chan)) break;
|
||||||
if (c.chan==adpcmChan) break;
|
if (c.chan==adpcmChan) break;
|
||||||
if (oplType<2) break;
|
if (oplType<2) break;
|
||||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||||
|
@ -2261,7 +2344,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
chan[c.chan].std.restart(c.value);
|
chan[c.chan].std.restart(c.value);
|
||||||
break;
|
break;
|
||||||
case DIV_CMD_GET_VOLMAX:
|
case DIV_CMD_GET_VOLMAX:
|
||||||
if (c.chan>=pcmChanOffs) return 127;
|
if (PCM_CHECK(c.chan)) return 127;
|
||||||
if (c.chan==adpcmChan) return 255;
|
if (c.chan==adpcmChan) return 255;
|
||||||
if (pretendYMU) return 127;
|
if (pretendYMU) return 127;
|
||||||
return 63;
|
return 63;
|
||||||
|
@ -2397,7 +2480,7 @@ DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int DivPlatformOPL::mapVelocity(int ch, float vel) {
|
int DivPlatformOPL::mapVelocity(int ch, float vel) {
|
||||||
if (ch>=pcmChanOffs) return vel*127.0;
|
if (PCM_CHECK(ch)) return vel*127.0;
|
||||||
if (ch==adpcmChan) return vel*255.0;
|
if (ch==adpcmChan) return vel*255.0;
|
||||||
// -0.75dB per step
|
// -0.75dB per step
|
||||||
// -6: 64: 8
|
// -6: 64: 8
|
||||||
|
@ -2487,6 +2570,7 @@ void DivPlatformOPL::reset() {
|
||||||
OPL3_Reset(&fm,rate);
|
OPL3_Reset(&fm,rate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pcm.reset();
|
||||||
|
|
||||||
if (dumpWrites) {
|
if (dumpWrites) {
|
||||||
addWrite(0xffffffff,0);
|
addWrite(0xffffffff,0);
|
||||||
|
@ -2512,9 +2596,9 @@ void DivPlatformOPL::reset() {
|
||||||
for (int i=0; i<totalChans; i++) {
|
for (int i=0; i<totalChans; i++) {
|
||||||
chan[i]=DivPlatformOPL::Channel();
|
chan[i]=DivPlatformOPL::Channel();
|
||||||
chan[i].std.setEngine(parent);
|
chan[i].std.setEngine(parent);
|
||||||
chan[i].vol=(i>=pcmChanOffs)?0x7f:0x3f;
|
chan[i].vol=(PCM_CHECK(i))?0x7f:0x3f;
|
||||||
chan[i].outVol=(i>=pcmChanOffs)?0x7f:0x3f;
|
chan[i].outVol=(PCM_CHECK(i))?0x7f:0x3f;
|
||||||
chan[i].pan=(i>=pcmChanOffs)?0:3;
|
chan[i].pan=(PCM_CHECK(i))?0:3;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (adpcmChan>=0) {
|
if (adpcmChan>=0) {
|
||||||
|
@ -2791,16 +2875,22 @@ void DivPlatformOPL::setFlags(const DivConfig& flags) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
switch (flags.getInt("ramSize",0)) {
|
switch (flags.getInt("ramSize",0)) {
|
||||||
case 0x01:
|
case 0x01: // 2MB (512KB 512KB 512KB 512KB)
|
||||||
ramSize=0x200000;
|
ramSize=0x200000;
|
||||||
break;
|
break;
|
||||||
case 0x02:
|
case 0x02: // 1MB (512KB 512KB)
|
||||||
ramSize=0x100000;
|
ramSize=0x100000;
|
||||||
break;
|
break;
|
||||||
case 0x03:
|
case 0x03: // 640KB (512KB 128KB)
|
||||||
|
ramSize=0xa0000;
|
||||||
|
break;
|
||||||
|
case 0x04: // 512KB
|
||||||
ramSize=0x80000;
|
ramSize=0x80000;
|
||||||
break;
|
break;
|
||||||
case 0x04:
|
case 0x05: // 256KB (128KB 128KB)
|
||||||
|
ramSize=0x40000;
|
||||||
|
break;
|
||||||
|
case 0x06: // 128KB
|
||||||
ramSize=0x20000;
|
ramSize=0x20000;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -2808,6 +2898,7 @@ void DivPlatformOPL::setFlags(const DivConfig& flags) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
CHECK_CUSTOM_CLOCK;
|
CHECK_CUSTOM_CLOCK;
|
||||||
|
pcm.setClockFrequency(chipClock);
|
||||||
rate=chipClock/768;
|
rate=chipClock/768;
|
||||||
chipRateBase=chipClock/684;
|
chipRateBase=chipClock/684;
|
||||||
immWrite(0x202,(ramSize<=0x200000)?0x10:0x00);
|
immWrite(0x202,(ramSize<=0x200000)?0x10:0x00);
|
||||||
|
@ -3038,6 +3129,7 @@ int DivPlatformOPL::init(DivEngine* p, int channels, int sugRate, const DivConfi
|
||||||
pcmMemLen=0;
|
pcmMemLen=0;
|
||||||
iface.pcmMem=pcmMem;
|
iface.pcmMem=pcmMem;
|
||||||
iface.sampleBank=0;
|
iface.sampleBank=0;
|
||||||
|
pcmMemory.memory=pcmMem;
|
||||||
}
|
}
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
|
|
|
@ -30,6 +30,7 @@ extern "C" {
|
||||||
#include "sound/ymfm/ymfm_adpcm.h"
|
#include "sound/ymfm/ymfm_adpcm.h"
|
||||||
#include "sound/ymfm/ymfm_opl.h"
|
#include "sound/ymfm/ymfm_opl.h"
|
||||||
#include "sound/ymfm/ymfm_pcm.h"
|
#include "sound/ymfm/ymfm_pcm.h"
|
||||||
|
#include "sound/ymf278b/ymf278.h"
|
||||||
|
|
||||||
class DivOPLAInterface: public ymfm::ymfm_interface {
|
class DivOPLAInterface: public ymfm::ymfm_interface {
|
||||||
public:
|
public:
|
||||||
|
@ -41,6 +42,18 @@ class DivOPLAInterface: public ymfm::ymfm_interface {
|
||||||
DivOPLAInterface(): adpcmBMem(NULL), pcmMem(NULL), sampleBank(0) {}
|
DivOPLAInterface(): adpcmBMem(NULL), pcmMem(NULL), sampleBank(0) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DivYMF278MemoryInterface: public MemoryInterface {
|
||||||
|
public:
|
||||||
|
unsigned char* memory;
|
||||||
|
DivYMF278MemoryInterface(unsigned size_) : memory(NULL), size(size_) {};
|
||||||
|
byte operator[](unsigned address) const override;
|
||||||
|
unsigned getSize() const override { return size; };
|
||||||
|
void write(unsigned address, byte value) override {};
|
||||||
|
void clear(byte value) override {};
|
||||||
|
private:
|
||||||
|
unsigned size;
|
||||||
|
};
|
||||||
|
|
||||||
class DivPlatformOPL: public DivDispatch {
|
class DivPlatformOPL: public DivDispatch {
|
||||||
protected:
|
protected:
|
||||||
struct Channel: public SharedChannel<int> {
|
struct Channel: public SharedChannel<int> {
|
||||||
|
@ -96,6 +109,7 @@ class DivPlatformOPL: public DivDispatch {
|
||||||
unsigned char* pcmMem;
|
unsigned char* pcmMem;
|
||||||
size_t pcmMemLen;
|
size_t pcmMemLen;
|
||||||
DivOPLAInterface iface;
|
DivOPLAInterface iface;
|
||||||
|
DivYMF278MemoryInterface pcmMemory;
|
||||||
unsigned int sampleOffB[256];
|
unsigned int sampleOffB[256];
|
||||||
unsigned int sampleOffPCM[256];
|
unsigned int sampleOffPCM[256];
|
||||||
bool sampleLoaded[256];
|
bool sampleLoaded[256];
|
||||||
|
@ -130,6 +144,7 @@ class DivPlatformOPL: public DivDispatch {
|
||||||
|
|
||||||
// chips
|
// chips
|
||||||
opl3_chip fm;
|
opl3_chip fm;
|
||||||
|
YMF278 pcm;
|
||||||
ymfm::ym3526* fm_ymfm1;
|
ymfm::ym3526* fm_ymfm1;
|
||||||
ymfm::ym3812* fm_ymfm2;
|
ymfm::ym3812* fm_ymfm2;
|
||||||
ymfm::y8950* fm_ymfm8950;
|
ymfm::y8950* fm_ymfm8950;
|
||||||
|
@ -143,7 +158,6 @@ class DivPlatformOPL: public DivDispatch {
|
||||||
int octave(int freq);
|
int octave(int freq);
|
||||||
int toFreq(int freq);
|
int toFreq(int freq);
|
||||||
double NOTE_ADPCMB(int note);
|
double NOTE_ADPCMB(int note);
|
||||||
double NOTE_OPL4(int ch, int note);
|
|
||||||
void commitState(int ch, DivInstrument* ins);
|
void commitState(int ch, DivInstrument* ins);
|
||||||
|
|
||||||
friend void putDispatchChip(void*,int);
|
friend void putDispatchChip(void*,int);
|
||||||
|
@ -194,6 +208,10 @@ class DivPlatformOPL: public DivDispatch {
|
||||||
void renderSamples(int chipID);
|
void renderSamples(int chipID);
|
||||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||||
void quit();
|
void quit();
|
||||||
|
YMF278& getChip();
|
||||||
|
DivPlatformOPL():
|
||||||
|
pcmMemory(0x400000),
|
||||||
|
pcm(pcmMemory) {}
|
||||||
~DivPlatformOPL();
|
~DivPlatformOPL();
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -41,3 +41,10 @@ uint8_t DivOPLAInterface::ymfm_external_read(ymfm::access_class type, uint32_t a
|
||||||
|
|
||||||
void DivOPLAInterface::ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data) {
|
void DivOPLAInterface::ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
byte DivYMF278MemoryInterface::operator[](unsigned address) const {
|
||||||
|
if (memory && address<size) {
|
||||||
|
return memory[address];
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -29,6 +29,10 @@
|
||||||
// interaction with the FM registers (e.g. the NEW2 bit) is currently handled
|
// interaction with the FM registers (e.g. the NEW2 bit) is currently handled
|
||||||
// in the MSXMoonSound class.
|
// in the MSXMoonSound class.
|
||||||
|
|
||||||
|
// MODIFIED:
|
||||||
|
// Add YMW258 support by Grauw
|
||||||
|
// Add DO1 output support by cam900
|
||||||
|
|
||||||
#include "ymf278.h"
|
#include "ymf278.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
@ -476,10 +480,12 @@ static constexpr int vol_factor(int x, unsigned envVol)
|
||||||
return (x * ((0x8000 * vol_mul) >> vol_shift)) >> 15;
|
return (x * ((0x8000 * vol_mul) >> vol_shift)) >> 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
void YMF278Base::generate(short& left, short& right, short* channelBufs)
|
void YMF278Base::generate(short& fleft, short& fright, short& rleft, short& rright, short* channelBufs)
|
||||||
{
|
{
|
||||||
int sampleLeft = 0;
|
int sampleFLeft = 0;
|
||||||
int sampleRight = 0;
|
int sampleFRight = 0;
|
||||||
|
int sampleRLeft = 0;
|
||||||
|
int sampleRRight = 0;
|
||||||
for (size_t i = 0, count = slots.size(); i < count; i++) {
|
for (size_t i = 0, count = slots.size(); i < count; i++) {
|
||||||
Slot& sl = slots[i];
|
Slot& sl = slots[i];
|
||||||
if (sl.state == EG_OFF) {
|
if (sl.state == EG_OFF) {
|
||||||
|
@ -512,8 +518,16 @@ void YMF278Base::generate(short& left, short& right, short* channelBufs)
|
||||||
volLeft = (0x20 - (volLeft & 0x0f)) >> (volLeft >> 4);
|
volLeft = (0x20 - (volLeft & 0x0f)) >> (volLeft >> 4);
|
||||||
volRight = (0x20 - (volRight & 0x0f)) >> (volRight >> 4);
|
volRight = (0x20 - (volRight & 0x0f)) >> (volRight >> 4);
|
||||||
|
|
||||||
sampleLeft += (smplOut * volLeft ) >> 5;
|
if (sl.ch)
|
||||||
sampleRight += (smplOut * volRight) >> 5;
|
{
|
||||||
|
sampleRLeft += (smplOut * volLeft ) >> 5;
|
||||||
|
sampleRRight += (smplOut * volRight) >> 5;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sampleFLeft += (smplOut * volLeft ) >> 5;
|
||||||
|
sampleFRight += (smplOut * volRight) >> 5;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned step = (sl.lfo_active && sl.vib)
|
unsigned step = (sl.lfo_active && sl.vib)
|
||||||
? calcStep(sl.OCT, sl.FN, sl.compute_vib())
|
? calcStep(sl.OCT, sl.FN, sl.compute_vib())
|
||||||
|
@ -531,8 +545,10 @@ void YMF278Base::generate(short& left, short& right, short* channelBufs)
|
||||||
}
|
}
|
||||||
advance();
|
advance();
|
||||||
|
|
||||||
left = sampleLeft >> 4;
|
fleft = sampleFLeft >> 4;
|
||||||
right = sampleRight >> 4;
|
fright = sampleFRight >> 4;
|
||||||
|
rleft = sampleRLeft >> 4;
|
||||||
|
rright = sampleRRight >> 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
void YMF278Base::keyOnHelper(Slot& slot)
|
void YMF278Base::keyOnHelper(Slot& slot)
|
||||||
|
@ -683,14 +699,8 @@ void YMF278::writeReg(byte reg, byte data)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 4:
|
case 4:
|
||||||
if (data & 0x10) {
|
slot.ch = data & 0x10;
|
||||||
// output to DO1 pin:
|
slot.pan = data & 0x0F;
|
||||||
// this pin is not used in moonsound
|
|
||||||
// we emulate this by muting the sound
|
|
||||||
slot.pan = 8; // both left/right -inf dB
|
|
||||||
} else {
|
|
||||||
slot.pan = data & 0x0F;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data & 0x20) {
|
if (data & 0x20) {
|
||||||
// LFO reset
|
// LFO reset
|
||||||
|
|
|
@ -35,7 +35,7 @@ public:
|
||||||
double getSampleRate();
|
double getSampleRate();
|
||||||
virtual void reset();
|
virtual void reset();
|
||||||
|
|
||||||
void generate(short& left, short& right, short* channelBufs = nullptr);
|
void generate(short& fleft, short& fright, short& rleft, short& rright, short* channelBufs = nullptr);
|
||||||
|
|
||||||
class Slot final {
|
class Slot final {
|
||||||
public:
|
public:
|
||||||
|
@ -71,6 +71,7 @@ public:
|
||||||
uint8_t TLdest; // destination total level
|
uint8_t TLdest; // destination total level
|
||||||
uint8_t TL; // total level (goes towards TLdest)
|
uint8_t TL; // total level (goes towards TLdest)
|
||||||
uint8_t pan; // panpot 0..15
|
uint8_t pan; // panpot 0..15
|
||||||
|
bool ch; // channel select
|
||||||
bool keyon; // slot keyed on
|
bool keyon; // slot keyed on
|
||||||
bool DAMP;
|
bool DAMP;
|
||||||
uint8_t lfo; // LFO speed 0..7
|
uint8_t lfo; // LFO speed 0..7
|
||||||
|
@ -115,10 +116,10 @@ public:
|
||||||
byte readReg(byte reg);
|
byte readReg(byte reg);
|
||||||
byte peekReg(byte reg) const;
|
byte peekReg(byte reg) const;
|
||||||
|
|
||||||
void generateMix(short fmL, short fmR, short& bufL, short& bufR, short* channelBufs = nullptr) {
|
void generateMix(short fmL, short fmR, short& bufFL, short& bufFR, short& bufRL, short& bufRR, short* channelBufs = nullptr) {
|
||||||
generate(bufL, bufR, channelBufs);
|
generate(bufFL, bufFR, bufRL, bufRR, channelBufs);
|
||||||
bufL = std::min(std::max((pcmMixL * bufL + fmMixL * fmL) >> 4, -0x8000), 0x7fff);
|
bufFL = std::min(std::max((pcmMixL * bufFL + fmMixL * fmL) >> 4, -0x8000), 0x7fff);
|
||||||
bufR = std::min(std::max((pcmMixR * bufR + fmMixR * fmR) >> 4, -0x8000), 0x7fff);;
|
bufFR = std::min(std::max((pcmMixR * bufFR + fmMixR * fmR) >> 4, -0x8000), 0x7fff);;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -294,6 +294,7 @@ const char* aboutLine[]={
|
||||||
_N("PowerNoise emulator by scratchminer"),
|
_N("PowerNoise emulator by scratchminer"),
|
||||||
_N("ep128emu by Istvan Varga"),
|
_N("ep128emu by Istvan Varga"),
|
||||||
_N("NDS sound emulator by cam900"),
|
_N("NDS sound emulator by cam900"),
|
||||||
|
_N("OpenMSX YMF278 emulator (modified version) by the openMSX developers"),
|
||||||
"",
|
"",
|
||||||
_N("greetings to:"),
|
_N("greetings to:"),
|
||||||
"NEOART Costa Rica",
|
"NEOART Costa Rica",
|
||||||
|
|
|
@ -1737,6 +1737,7 @@ class FurnaceGUI {
|
||||||
int opnbCore;
|
int opnbCore;
|
||||||
int opl2Core;
|
int opl2Core;
|
||||||
int opl3Core;
|
int opl3Core;
|
||||||
|
int opl4Core;
|
||||||
int esfmCore;
|
int esfmCore;
|
||||||
int opllCore;
|
int opllCore;
|
||||||
int ayCore;
|
int ayCore;
|
||||||
|
@ -1763,6 +1764,7 @@ class FurnaceGUI {
|
||||||
int opnbCoreRender;
|
int opnbCoreRender;
|
||||||
int opl2CoreRender;
|
int opl2CoreRender;
|
||||||
int opl3CoreRender;
|
int opl3CoreRender;
|
||||||
|
int opl4CoreRender;
|
||||||
int esfmCoreRender;
|
int esfmCoreRender;
|
||||||
int opllCoreRender;
|
int opllCoreRender;
|
||||||
int ayCoreRender;
|
int ayCoreRender;
|
||||||
|
@ -1995,6 +1997,7 @@ class FurnaceGUI {
|
||||||
opnbCore(1),
|
opnbCore(1),
|
||||||
opl2Core(0),
|
opl2Core(0),
|
||||||
opl3Core(0),
|
opl3Core(0),
|
||||||
|
opl4Core(0),
|
||||||
esfmCore(0),
|
esfmCore(0),
|
||||||
opllCore(0),
|
opllCore(0),
|
||||||
ayCore(0),
|
ayCore(0),
|
||||||
|
@ -2021,6 +2024,7 @@ class FurnaceGUI {
|
||||||
opnbCoreRender(1),
|
opnbCoreRender(1),
|
||||||
opl2CoreRender(0),
|
opl2CoreRender(0),
|
||||||
opl3CoreRender(0),
|
opl3CoreRender(0),
|
||||||
|
opl4CoreRender(0),
|
||||||
esfmCoreRender(0),
|
esfmCoreRender(0),
|
||||||
opllCoreRender(0),
|
opllCoreRender(0),
|
||||||
ayCoreRender(0),
|
ayCoreRender(0),
|
||||||
|
|
|
@ -189,6 +189,11 @@ const char* opl3Cores[]={
|
||||||
"YMF262-LLE"
|
"YMF262-LLE"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const char* opl4Cores[]={
|
||||||
|
"Nuked-OPL3 + OpenMSX",
|
||||||
|
"ymfm"
|
||||||
|
};
|
||||||
|
|
||||||
const char* esfmCores[]={
|
const char* esfmCores[]={
|
||||||
"ESFMu",
|
"ESFMu",
|
||||||
_N("ESFMu (fast)")
|
_N("ESFMu (fast)")
|
||||||
|
@ -2046,6 +2051,17 @@ void FurnaceGUI::drawSettings() {
|
||||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
if (ImGui::Combo("##OPL3CoreRender",&settings.opl3CoreRender,opl3Cores,3)) settingsChanged=true;
|
if (ImGui::Combo("##OPL3CoreRender",&settings.opl3CoreRender,opl3Cores,3)) settingsChanged=true;
|
||||||
|
|
||||||
|
ImGui::TableNextRow();
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::AlignTextToFramePadding();
|
||||||
|
ImGui::Text("OPL4");
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
|
if (ImGui::Combo("##OPL4Core",&settings.opl4Core,opl4Cores,2)) settingsChanged=true;
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||||
|
if (ImGui::Combo("##OPL4CoreRender",&settings.opl4CoreRender,opl4Cores,2)) settingsChanged=true;
|
||||||
|
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
ImGui::AlignTextToFramePadding();
|
ImGui::AlignTextToFramePadding();
|
||||||
|
@ -4959,6 +4975,7 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
||||||
settings.opnbCore=conf.getInt("opnbCore",1);
|
settings.opnbCore=conf.getInt("opnbCore",1);
|
||||||
settings.opl2Core=conf.getInt("opl2Core",0);
|
settings.opl2Core=conf.getInt("opl2Core",0);
|
||||||
settings.opl3Core=conf.getInt("opl3Core",0);
|
settings.opl3Core=conf.getInt("opl3Core",0);
|
||||||
|
settings.opl4Core=conf.getInt("opl4Core",0);
|
||||||
settings.esfmCore=conf.getInt("esfmCore",0);
|
settings.esfmCore=conf.getInt("esfmCore",0);
|
||||||
settings.opllCore=conf.getInt("opllCore",0);
|
settings.opllCore=conf.getInt("opllCore",0);
|
||||||
settings.ayCore=conf.getInt("ayCore",0);
|
settings.ayCore=conf.getInt("ayCore",0);
|
||||||
|
@ -4987,6 +5004,7 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
||||||
settings.opnbCoreRender=conf.getInt("opnbCoreRender",1);
|
settings.opnbCoreRender=conf.getInt("opnbCoreRender",1);
|
||||||
settings.opl2CoreRender=conf.getInt("opl2CoreRender",0);
|
settings.opl2CoreRender=conf.getInt("opl2CoreRender",0);
|
||||||
settings.opl3CoreRender=conf.getInt("opl3CoreRender",0);
|
settings.opl3CoreRender=conf.getInt("opl3CoreRender",0);
|
||||||
|
settings.opl4CoreRender=conf.getInt("opl4CoreRender",0);
|
||||||
settings.esfmCoreRender=conf.getInt("esfmCoreRender",0);
|
settings.esfmCoreRender=conf.getInt("esfmCoreRender",0);
|
||||||
settings.opllCoreRender=conf.getInt("opllCoreRender",0);
|
settings.opllCoreRender=conf.getInt("opllCoreRender",0);
|
||||||
settings.ayCoreRender=conf.getInt("ayCoreRender",0);
|
settings.ayCoreRender=conf.getInt("ayCoreRender",0);
|
||||||
|
@ -5032,6 +5050,7 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
||||||
clampSetting(settings.opnbCore,0,2);
|
clampSetting(settings.opnbCore,0,2);
|
||||||
clampSetting(settings.opl2Core,0,2);
|
clampSetting(settings.opl2Core,0,2);
|
||||||
clampSetting(settings.opl3Core,0,2);
|
clampSetting(settings.opl3Core,0,2);
|
||||||
|
clampSetting(settings.opl4Core,0,1);
|
||||||
clampSetting(settings.esfmCore,0,1);
|
clampSetting(settings.esfmCore,0,1);
|
||||||
clampSetting(settings.opllCore,0,1);
|
clampSetting(settings.opllCore,0,1);
|
||||||
clampSetting(settings.ayCore,0,1);
|
clampSetting(settings.ayCore,0,1);
|
||||||
|
@ -5058,6 +5077,7 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
||||||
clampSetting(settings.opnbCoreRender,0,2);
|
clampSetting(settings.opnbCoreRender,0,2);
|
||||||
clampSetting(settings.opl2CoreRender,0,2);
|
clampSetting(settings.opl2CoreRender,0,2);
|
||||||
clampSetting(settings.opl3CoreRender,0,2);
|
clampSetting(settings.opl3CoreRender,0,2);
|
||||||
|
clampSetting(settings.opl4CoreRender,0,1);
|
||||||
clampSetting(settings.esfmCoreRender,0,1);
|
clampSetting(settings.esfmCoreRender,0,1);
|
||||||
clampSetting(settings.opllCoreRender,0,1);
|
clampSetting(settings.opllCoreRender,0,1);
|
||||||
clampSetting(settings.ayCoreRender,0,1);
|
clampSetting(settings.ayCoreRender,0,1);
|
||||||
|
@ -5543,6 +5563,7 @@ void FurnaceGUI::writeConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
||||||
conf.set("opnbCore",settings.opnbCore);
|
conf.set("opnbCore",settings.opnbCore);
|
||||||
conf.set("opl2Core",settings.opl2Core);
|
conf.set("opl2Core",settings.opl2Core);
|
||||||
conf.set("opl3Core",settings.opl3Core);
|
conf.set("opl3Core",settings.opl3Core);
|
||||||
|
conf.set("opl4Core",settings.opl4Core);
|
||||||
conf.set("esfmCore",settings.esfmCore);
|
conf.set("esfmCore",settings.esfmCore);
|
||||||
conf.set("opllCore",settings.opllCore);
|
conf.set("opllCore",settings.opllCore);
|
||||||
conf.set("ayCore",settings.ayCore);
|
conf.set("ayCore",settings.ayCore);
|
||||||
|
@ -5571,6 +5592,7 @@ void FurnaceGUI::writeConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
||||||
conf.set("opnbCoreRender",settings.opnbCoreRender);
|
conf.set("opnbCoreRender",settings.opnbCoreRender);
|
||||||
conf.set("opl2CoreRender",settings.opl2CoreRender);
|
conf.set("opl2CoreRender",settings.opl2CoreRender);
|
||||||
conf.set("opl3CoreRender",settings.opl3CoreRender);
|
conf.set("opl3CoreRender",settings.opl3CoreRender);
|
||||||
|
conf.set("opl4CoreRender",settings.opl4CoreRender);
|
||||||
conf.set("esfmCoreRender",settings.esfmCoreRender);
|
conf.set("esfmCoreRender",settings.esfmCoreRender);
|
||||||
conf.set("opllCoreRender",settings.opllCoreRender);
|
conf.set("opllCoreRender",settings.opllCoreRender);
|
||||||
conf.set("ayCoreRender",settings.ayCoreRender);
|
conf.set("ayCoreRender",settings.ayCoreRender);
|
||||||
|
@ -5634,6 +5656,7 @@ void FurnaceGUI::commitSettings() {
|
||||||
settings.opnbCore!=e->getConfInt("opnbCore",1) ||
|
settings.opnbCore!=e->getConfInt("opnbCore",1) ||
|
||||||
settings.opl2Core!=e->getConfInt("opl2Core",0) ||
|
settings.opl2Core!=e->getConfInt("opl2Core",0) ||
|
||||||
settings.opl3Core!=e->getConfInt("opl3Core",0) ||
|
settings.opl3Core!=e->getConfInt("opl3Core",0) ||
|
||||||
|
settings.opl4Core!=e->getConfInt("opl4Core",0) ||
|
||||||
settings.esfmCore!=e->getConfInt("esfmCore",0) ||
|
settings.esfmCore!=e->getConfInt("esfmCore",0) ||
|
||||||
settings.opllCore!=e->getConfInt("opllCore",0) ||
|
settings.opllCore!=e->getConfInt("opllCore",0) ||
|
||||||
settings.ayCore!=e->getConfInt("ayCore",0) ||
|
settings.ayCore!=e->getConfInt("ayCore",0) ||
|
||||||
|
|
|
@ -2538,14 +2538,22 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
|
||||||
ramSize=2;
|
ramSize=2;
|
||||||
altered=true;
|
altered=true;
|
||||||
}
|
}
|
||||||
if (ImGui::RadioButton(_("512KB"),ramSize==3)) {
|
if (ImGui::RadioButton(_("640KB"),ramSize==3)) {
|
||||||
ramSize=3;
|
ramSize=3;
|
||||||
altered=true;
|
altered=true;
|
||||||
}
|
}
|
||||||
if (ImGui::RadioButton(_("128KB"),ramSize==4)) {
|
if (ImGui::RadioButton(_("512KB"),ramSize==4)) {
|
||||||
ramSize=4;
|
ramSize=4;
|
||||||
altered=true;
|
altered=true;
|
||||||
}
|
}
|
||||||
|
if (ImGui::RadioButton(_("256KB"),ramSize==5)) {
|
||||||
|
ramSize=5;
|
||||||
|
altered=true;
|
||||||
|
}
|
||||||
|
if (ImGui::RadioButton(_("128KB"),ramSize==6)) {
|
||||||
|
ramSize=6;
|
||||||
|
altered=true;
|
||||||
|
}
|
||||||
ImGui::Unindent();
|
ImGui::Unindent();
|
||||||
|
|
||||||
if (altered) {
|
if (altered) {
|
||||||
|
|
|
@ -333,6 +333,7 @@ TAParamResult pVersion(String) {
|
||||||
printf("- PowerNoise emulator by scratchminer (MIT)\n");
|
printf("- PowerNoise emulator by scratchminer (MIT)\n");
|
||||||
printf("- ep128emu by Istvan Varga (GPLv2)\n");
|
printf("- ep128emu by Istvan Varga (GPLv2)\n");
|
||||||
printf("- NDS sound emulator by cam900 (zlib license)\n");
|
printf("- NDS sound emulator by cam900 (zlib license)\n");
|
||||||
|
printf("- OpenMSX YMF278 emulator (modified version) by the openMSX developers (GPLv2)\n");
|
||||||
return TA_PARAM_QUIT;
|
return TA_PARAM_QUIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue