Some fleshing out YMF278B

Add OpenMSX YMF278B core option, Expand RAM size option
This commit is contained in:
cam900 2024-07-12 12:16:24 +09:00
parent a8ec76699b
commit 221fa5aa42
11 changed files with 275 additions and 104 deletions

View file

@ -763,14 +763,20 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
case DIV_SYSTEM_OPL4:
dispatch=new DivPlatformOPL;
((DivPlatformOPL*)dispatch)->setOPLType(4,false);
// YMFM for now
((DivPlatformOPL*)dispatch)->setCore(1);
if (isRender) {
((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl4CoreRender",0));
} else {
((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl4Core",0));
}
break;
case DIV_SYSTEM_OPL4_DRUMS:
dispatch=new DivPlatformOPL;
((DivPlatformOPL*)dispatch)->setOPLType(4,true);
// YMFM for now
((DivPlatformOPL*)dispatch)->setCore(1);
if (isRender) {
((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl4CoreRender",0));
} else {
((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl4Core",0));
}
break;
case DIV_SYSTEM_DUMMY:
dispatch=new DivPlatformDummy;

View file

@ -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 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
#define N 255
@ -176,36 +182,42 @@ const int orderedOpsL[4]={
#define PCM_ADDR_MIX_PCM 0x2f9
void DivPlatformOPL::acquire_nuked(short** buf, size_t len) {
thread_local short o[4];
thread_local int os[4];
thread_local short o[8];
thread_local int os[6];
thread_local ymfm::ymfm_output<2> aOut;
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) {
delay=1;
QueuedWrite& w=writes.front();
switch (w.addr) {
case 8:
if (adpcmChan>=0) {
adpcmB->write(w.addr-7,(w.val&15)|0x80);
OPL3_WriteReg(&fm,w.addr,w.val&0xc0);
} else {
if (w.addr>=0x200) {
pcm.writeReg(w.addr&0xff,w.val);
regPool[0x200|(w.addr&0xff)]=w.val;
} else {
switch (w.addr) {
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);
}
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);
break;
break;
}
regPool[w.addr&511]=w.val;
}
regPool[w.addr&511]=w.val;
writes.pop();
}
@ -214,10 +226,20 @@ void DivPlatformOPL::acquire_nuked(short** buf, size_t len) {
} else {
OPL3_Generate4Ch(&fm,o);
}
os[0]+=o[0];
os[1]+=o[1];
os[2]+=o[2];
os[3]+=o[3];
if (chipType==4) {
pcm.generateMix(o[0],o[1],o[4],o[5],o[6],o[7],pcmBuf);
os[0]+=o[4];
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) {
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]>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];
if (totalOutputs>1) {
buf[1][h]=os[1];
@ -301,9 +329,8 @@ void DivPlatformOPL::acquire_nuked(short** buf, size_t len) {
buf[3][h]=os[3];
}
if (totalOutputs==6) {
// placeholder for OPL4
buf[4][h]=0;
buf[5][h]=0;
buf[4][h]=os[4];
buf[5][h]=os[5];
}
}
}
@ -947,22 +974,44 @@ double DivPlatformOPL::NOTE_ADPCMB(int note) {
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) {
for (int i=0; i<totalChans; i++) {
if (i>=pcmChanOffs) { // OPL4 PCM
if (PCM_CHECK(i)) { // OPL4 PCM
chan[i].std.next();
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);
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) {
@ -1013,11 +1062,12 @@ void DivPlatformOPL::tick(bool sysTick) {
waveNum=MIN(waveNum,0x7f)|0x180;
}
if (chan[i].keyOn) {
rWrite(PCM_ADDR_KEY_DAMP_LFORST_CH_PAN+(i-pcmChanOffs),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_L+(i-pcmChanOffs),waveNum&0xff);
rWrite(PCM_ADDR_KEY_DAMP_LFORST_CH_PAN+PCM_REG(i),ctrl&~0x80); // force keyoff first
rWrite(PCM_ADDR_WAVE_H_FN_L+PCM_REG(i),((chan[i].freqL&0x7f)<<1)|((waveNum>>8)&1));
rWrite(PCM_ADDR_WAVE_L+PCM_REG(i),waveNum&0xff);
if (!chan[i].std.vol.had) {
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].keyOn=false;
@ -1027,12 +1077,12 @@ void DivPlatformOPL::tick(bool sysTick) {
chan[i].keyOff=false;
}
if (chan[i].freqChanged) {
rWrite(PCM_ADDR_WAVE_H_FN_L+(i-pcmChanOffs),((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_WAVE_H_FN_L+PCM_REG(i),((chan[i].freqL&0x7f)<<1)|((waveNum>>8)&1));
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;
}
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;
}
}
@ -1309,7 +1359,7 @@ void DivPlatformOPL::tick(bool sysTick) {
bool updateDrums=false;
for (int i=0; i<totalChans; i++) {
if (i>=pcmChanOffs) { // OPL4 PCM
if (PCM_CHECK(i)) { // OPL4 PCM
} else {
if (chan[i].freqChanged) {
@ -1426,7 +1476,7 @@ int DivPlatformOPL::toFreq(int freq) {
void DivPlatformOPL::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
if (ch>=pcmChanOffs) {
if (PCM_CHECK(ch)) {
chan[ch].freqChanged=true;
chan[ch].writeCtrl=true;
return;
@ -1479,6 +1529,9 @@ void DivPlatformOPL::muteChannel(int ch, bool mute) {
}
void DivPlatformOPL::commitState(int ch, DivInstrument* ins) {
if (PCM_CHECK(ch)) {
return;
}
if (chan[ch].insChanged) {
if (ch>melodicChans && ins->type==DIV_INS_OPL_DRUMS) {
for (int i=0; i<4; i++) {
@ -1583,7 +1636,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
}
switch (c.cmd) {
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);
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:127;
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;
}
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) {
chan[c.chan].sample=-1;
@ -1733,7 +1786,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
chan[c.chan].keyOff=true;
chan[c.chan].keyOn=false;
chan[c.chan].active=false;
if (c.chan>=pcmChanOffs) {
if (PCM_CHECK(c.chan)) {
chan[c.chan].sample=-1;
chan[c.chan].macroInit(NULL);
}
@ -1757,8 +1810,8 @@ int DivPlatformOPL::dispatch(DivCommand c) {
if (!chan[c.chan].std.vol.has) {
chan[c.chan].outVol=c.value;
}
if (c.chan>=pcmChanOffs) { // OPL4 PCM
immWrite(PCM_ADDR_TL+(c.chan-pcmChanOffs),((0x7f-chan[c.chan].outVol)<<1)|(chan[c.chan].levelDirect?1:0));
if (PCM_CHECK(c.chan)) { // OPL4 PCM
immWrite(PCM_ADDR_TL+PCM_REG(c.chan),((0x7f-chan[c.chan].outVol)<<1)|(chan[c.chan].levelDirect?1:0));
break;
}
if (c.chan==adpcmChan) { // ADPCM-B
@ -1798,6 +1851,12 @@ int DivPlatformOPL::dispatch(DivCommand c) {
chan[c.chan].ins=c.value;
break;
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 (c.chan==adpcmChan) break;
chan[c.chan].pan&=~3;
@ -1856,6 +1915,29 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break;
}
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 newFreq;
bool return2=false;
@ -1901,13 +1983,14 @@ int DivPlatformOPL::dispatch(DivCommand c) {
commitState(c.chan,ins);
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].freqChanged=true;
break;
}
case DIV_CMD_FM_LFO: {
if (c.chan>=pcmChanOffs) break;
if (PCM_CHECK(c.chan)) break;
if (c.chan==adpcmChan) break;
if (c.value&2) {
dvb=c.value&1;
@ -1918,7 +2001,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_FB: {
if (c.chan>=pcmChanOffs) break;
if (PCM_CHECK(c.chan)) break;
if (c.chan==adpcmChan) break;
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;
@ -1936,7 +2019,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_MULT: {
if (c.chan>=pcmChanOffs) break;
if (PCM_CHECK(c.chan)) break;
if (c.chan==adpcmChan) break;
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
if (c.value>=ops) break;
@ -1949,7 +2032,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_TL: {
if (c.chan>=pcmChanOffs) break;
if (PCM_CHECK(c.chan)) break;
if (c.chan==adpcmChan) break;
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
if (c.value>=ops) break;
@ -1970,7 +2053,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_AR: {
if (c.chan>=pcmChanOffs) break;
if (PCM_CHECK(c.chan)) break;
if (c.chan==adpcmChan) break;
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
if (c.value<0) {
@ -1994,7 +2077,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_DR: {
if (c.chan>=pcmChanOffs) break;
if (PCM_CHECK(c.chan)) break;
if (c.chan==adpcmChan) break;
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
if (c.value<0) {
@ -2018,7 +2101,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_SL: {
if (c.chan>=pcmChanOffs) break;
if (PCM_CHECK(c.chan)) break;
if (c.chan==adpcmChan) break;
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
if (c.value<0) {
@ -2042,7 +2125,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_RR: {
if (c.chan>=pcmChanOffs) break;
if (PCM_CHECK(c.chan)) break;
if (c.chan==adpcmChan) break;
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
if (c.value<0) {
@ -2066,7 +2149,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_AM: {
if (c.chan>=pcmChanOffs) break;
if (PCM_CHECK(c.chan)) break;
if (c.chan==adpcmChan) break;
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
if (c.value<0) {
@ -2090,7 +2173,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_VIB: {
if (c.chan>=pcmChanOffs) break;
if (PCM_CHECK(c.chan)) break;
if (c.chan==adpcmChan) break;
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
if (c.value<0) {
@ -2114,7 +2197,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_SUS: {
if (c.chan>=pcmChanOffs) break;
if (PCM_CHECK(c.chan)) break;
if (c.chan==adpcmChan) break;
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
if (c.value<0) {
@ -2138,7 +2221,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_KSR: {
if (c.chan>=pcmChanOffs) break;
if (PCM_CHECK(c.chan)) break;
if (c.chan==adpcmChan) break;
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
if (c.value<0) {
@ -2162,7 +2245,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_WS: {
if (c.chan>=pcmChanOffs) break;
if (PCM_CHECK(c.chan)) break;
if (c.chan==adpcmChan) break;
if (oplType<2) break;
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;
}
case DIV_CMD_FM_RS: {
if (c.chan>=pcmChanOffs) break;
if (PCM_CHECK(c.chan)) break;
if (c.chan==adpcmChan) break;
if (oplType<2) break;
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);
break;
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 (pretendYMU) return 127;
return 63;
@ -2397,7 +2480,7 @@ DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) {
}
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;
// -0.75dB per step
// -6: 64: 8
@ -2487,6 +2570,7 @@ void DivPlatformOPL::reset() {
OPL3_Reset(&fm,rate);
}
}
pcm.reset();
if (dumpWrites) {
addWrite(0xffffffff,0);
@ -2512,9 +2596,9 @@ void DivPlatformOPL::reset() {
for (int i=0; i<totalChans; i++) {
chan[i]=DivPlatformOPL::Channel();
chan[i].std.setEngine(parent);
chan[i].vol=(i>=pcmChanOffs)?0x7f:0x3f;
chan[i].outVol=(i>=pcmChanOffs)?0x7f:0x3f;
chan[i].pan=(i>=pcmChanOffs)?0:3;
chan[i].vol=(PCM_CHECK(i))?0x7f:0x3f;
chan[i].outVol=(PCM_CHECK(i))?0x7f:0x3f;
chan[i].pan=(PCM_CHECK(i))?0:3;
}
if (adpcmChan>=0) {
@ -2791,16 +2875,22 @@ void DivPlatformOPL::setFlags(const DivConfig& flags) {
break;
}
switch (flags.getInt("ramSize",0)) {
case 0x01:
case 0x01: // 2MB (512KB 512KB 512KB 512KB)
ramSize=0x200000;
break;
case 0x02:
case 0x02: // 1MB (512KB 512KB)
ramSize=0x100000;
break;
case 0x03:
case 0x03: // 640KB (512KB 128KB)
ramSize=0xa0000;
break;
case 0x04: // 512KB
ramSize=0x80000;
break;
case 0x04:
case 0x05: // 256KB (128KB 128KB)
ramSize=0x40000;
break;
case 0x06: // 128KB
ramSize=0x20000;
break;
default:
@ -2808,6 +2898,7 @@ void DivPlatformOPL::setFlags(const DivConfig& flags) {
break;
}
CHECK_CUSTOM_CLOCK;
pcm.setClockFrequency(chipClock);
rate=chipClock/768;
chipRateBase=chipClock/684;
immWrite(0x202,(ramSize<=0x200000)?0x10:0x00);
@ -3038,6 +3129,7 @@ int DivPlatformOPL::init(DivEngine* p, int channels, int sugRate, const DivConfi
pcmMemLen=0;
iface.pcmMem=pcmMem;
iface.sampleBank=0;
pcmMemory.memory=pcmMem;
}
reset();

View file

@ -30,6 +30,7 @@ extern "C" {
#include "sound/ymfm/ymfm_adpcm.h"
#include "sound/ymfm/ymfm_opl.h"
#include "sound/ymfm/ymfm_pcm.h"
#include "sound/ymf278b/ymf278.h"
class DivOPLAInterface: public ymfm::ymfm_interface {
public:
@ -41,6 +42,18 @@ class DivOPLAInterface: public ymfm::ymfm_interface {
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 {
protected:
struct Channel: public SharedChannel<int> {
@ -96,6 +109,7 @@ class DivPlatformOPL: public DivDispatch {
unsigned char* pcmMem;
size_t pcmMemLen;
DivOPLAInterface iface;
DivYMF278MemoryInterface pcmMemory;
unsigned int sampleOffB[256];
unsigned int sampleOffPCM[256];
bool sampleLoaded[256];
@ -130,6 +144,7 @@ class DivPlatformOPL: public DivDispatch {
// chips
opl3_chip fm;
YMF278 pcm;
ymfm::ym3526* fm_ymfm1;
ymfm::ym3812* fm_ymfm2;
ymfm::y8950* fm_ymfm8950;
@ -143,7 +158,6 @@ class DivPlatformOPL: public DivDispatch {
int octave(int freq);
int toFreq(int freq);
double NOTE_ADPCMB(int note);
double NOTE_OPL4(int ch, int note);
void commitState(int ch, DivInstrument* ins);
friend void putDispatchChip(void*,int);
@ -194,6 +208,10 @@ class DivPlatformOPL: public DivDispatch {
void renderSamples(int chipID);
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
void quit();
YMF278& getChip();
DivPlatformOPL():
pcmMemory(0x400000),
pcm(pcmMemory) {}
~DivPlatformOPL();
};
#endif

View file

@ -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) {
}
byte DivYMF278MemoryInterface::operator[](unsigned address) const {
if (memory && address<size) {
return memory[address];
}
return 0;
}

View file

@ -29,6 +29,10 @@
// interaction with the FM registers (e.g. the NEW2 bit) is currently handled
// in the MSXMoonSound class.
// MODIFIED:
// Add YMW258 support by Grauw
// Add DO1 output support by cam900
#include "ymf278.h"
#include <algorithm>
#include <cassert>
@ -476,10 +480,12 @@ static constexpr int vol_factor(int x, unsigned envVol)
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 sampleRight = 0;
int sampleFLeft = 0;
int sampleFRight = 0;
int sampleRLeft = 0;
int sampleRRight = 0;
for (size_t i = 0, count = slots.size(); i < count; i++) {
Slot& sl = slots[i];
if (sl.state == EG_OFF) {
@ -512,8 +518,16 @@ void YMF278Base::generate(short& left, short& right, short* channelBufs)
volLeft = (0x20 - (volLeft & 0x0f)) >> (volLeft >> 4);
volRight = (0x20 - (volRight & 0x0f)) >> (volRight >> 4);
sampleLeft += (smplOut * volLeft ) >> 5;
sampleRight += (smplOut * volRight) >> 5;
if (sl.ch)
{
sampleRLeft += (smplOut * volLeft ) >> 5;
sampleRRight += (smplOut * volRight) >> 5;
}
else
{
sampleFLeft += (smplOut * volLeft ) >> 5;
sampleFRight += (smplOut * volRight) >> 5;
}
unsigned step = (sl.lfo_active && sl.vib)
? calcStep(sl.OCT, sl.FN, sl.compute_vib())
@ -531,8 +545,10 @@ void YMF278Base::generate(short& left, short& right, short* channelBufs)
}
advance();
left = sampleLeft >> 4;
right = sampleRight >> 4;
fleft = sampleFLeft >> 4;
fright = sampleFRight >> 4;
rleft = sampleRLeft >> 4;
rright = sampleRRight >> 4;
}
void YMF278Base::keyOnHelper(Slot& slot)
@ -683,14 +699,8 @@ void YMF278::writeReg(byte reg, byte data)
break;
}
case 4:
if (data & 0x10) {
// output to DO1 pin:
// 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;
}
slot.ch = data & 0x10;
slot.pan = data & 0x0F;
if (data & 0x20) {
// LFO reset

View file

@ -35,7 +35,7 @@ public:
double getSampleRate();
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 {
public:
@ -71,6 +71,7 @@ public:
uint8_t TLdest; // destination total level
uint8_t TL; // total level (goes towards TLdest)
uint8_t pan; // panpot 0..15
bool ch; // channel select
bool keyon; // slot keyed on
bool DAMP;
uint8_t lfo; // LFO speed 0..7
@ -115,10 +116,10 @@ public:
byte readReg(byte reg);
byte peekReg(byte reg) const;
void generateMix(short fmL, short fmR, short& bufL, short& bufR, short* channelBufs = nullptr) {
generate(bufL, bufR, channelBufs);
bufL = std::min(std::max((pcmMixL * bufL + fmMixL * fmL) >> 4, -0x8000), 0x7fff);
bufR = std::min(std::max((pcmMixR * bufR + fmMixR * fmR) >> 4, -0x8000), 0x7fff);;
void generateMix(short fmL, short fmR, short& bufFL, short& bufFR, short& bufRL, short& bufRR, short* channelBufs = nullptr) {
generate(bufFL, bufFR, bufRL, bufRR, channelBufs);
bufFL = std::min(std::max((pcmMixL * bufFL + fmMixL * fmL) >> 4, -0x8000), 0x7fff);
bufFR = std::min(std::max((pcmMixR * bufFR + fmMixR * fmR) >> 4, -0x8000), 0x7fff);;
}
private:

View file

@ -294,6 +294,7 @@ const char* aboutLine[]={
_N("PowerNoise emulator by scratchminer"),
_N("ep128emu by Istvan Varga"),
_N("NDS sound emulator by cam900"),
_N("OpenMSX YMF278 emulator (modified version) by the openMSX developers"),
"",
_N("greetings to:"),
"NEOART Costa Rica",

View file

@ -1737,6 +1737,7 @@ class FurnaceGUI {
int opnbCore;
int opl2Core;
int opl3Core;
int opl4Core;
int esfmCore;
int opllCore;
int ayCore;
@ -1763,6 +1764,7 @@ class FurnaceGUI {
int opnbCoreRender;
int opl2CoreRender;
int opl3CoreRender;
int opl4CoreRender;
int esfmCoreRender;
int opllCoreRender;
int ayCoreRender;
@ -1995,6 +1997,7 @@ class FurnaceGUI {
opnbCore(1),
opl2Core(0),
opl3Core(0),
opl4Core(0),
esfmCore(0),
opllCore(0),
ayCore(0),
@ -2021,6 +2024,7 @@ class FurnaceGUI {
opnbCoreRender(1),
opl2CoreRender(0),
opl3CoreRender(0),
opl4CoreRender(0),
esfmCoreRender(0),
opllCoreRender(0),
ayCoreRender(0),

View file

@ -189,6 +189,11 @@ const char* opl3Cores[]={
"YMF262-LLE"
};
const char* opl4Cores[]={
"Nuked-OPL3 + OpenMSX",
"ymfm"
};
const char* esfmCores[]={
"ESFMu",
_N("ESFMu (fast)")
@ -2046,6 +2051,17 @@ void FurnaceGUI::drawSettings() {
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
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::TableNextColumn();
ImGui::AlignTextToFramePadding();
@ -4959,6 +4975,7 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
settings.opnbCore=conf.getInt("opnbCore",1);
settings.opl2Core=conf.getInt("opl2Core",0);
settings.opl3Core=conf.getInt("opl3Core",0);
settings.opl4Core=conf.getInt("opl4Core",0);
settings.esfmCore=conf.getInt("esfmCore",0);
settings.opllCore=conf.getInt("opllCore",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.opl2CoreRender=conf.getInt("opl2CoreRender",0);
settings.opl3CoreRender=conf.getInt("opl3CoreRender",0);
settings.opl4CoreRender=conf.getInt("opl4CoreRender",0);
settings.esfmCoreRender=conf.getInt("esfmCoreRender",0);
settings.opllCoreRender=conf.getInt("opllCoreRender",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.opl2Core,0,2);
clampSetting(settings.opl3Core,0,2);
clampSetting(settings.opl4Core,0,1);
clampSetting(settings.esfmCore,0,1);
clampSetting(settings.opllCore,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.opl2CoreRender,0,2);
clampSetting(settings.opl3CoreRender,0,2);
clampSetting(settings.opl4CoreRender,0,1);
clampSetting(settings.esfmCoreRender,0,1);
clampSetting(settings.opllCoreRender,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("opl2Core",settings.opl2Core);
conf.set("opl3Core",settings.opl3Core);
conf.set("opl4Core",settings.opl4Core);
conf.set("esfmCore",settings.esfmCore);
conf.set("opllCore",settings.opllCore);
conf.set("ayCore",settings.ayCore);
@ -5571,6 +5592,7 @@ void FurnaceGUI::writeConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
conf.set("opnbCoreRender",settings.opnbCoreRender);
conf.set("opl2CoreRender",settings.opl2CoreRender);
conf.set("opl3CoreRender",settings.opl3CoreRender);
conf.set("opl4CoreRender",settings.opl4CoreRender);
conf.set("esfmCoreRender",settings.esfmCoreRender);
conf.set("opllCoreRender",settings.opllCoreRender);
conf.set("ayCoreRender",settings.ayCoreRender);
@ -5634,6 +5656,7 @@ void FurnaceGUI::commitSettings() {
settings.opnbCore!=e->getConfInt("opnbCore",1) ||
settings.opl2Core!=e->getConfInt("opl2Core",0) ||
settings.opl3Core!=e->getConfInt("opl3Core",0) ||
settings.opl4Core!=e->getConfInt("opl4Core",0) ||
settings.esfmCore!=e->getConfInt("esfmCore",0) ||
settings.opllCore!=e->getConfInt("opllCore",0) ||
settings.ayCore!=e->getConfInt("ayCore",0) ||

View file

@ -2538,14 +2538,22 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
ramSize=2;
altered=true;
}
if (ImGui::RadioButton(_("512KB"),ramSize==3)) {
if (ImGui::RadioButton(_("640KB"),ramSize==3)) {
ramSize=3;
altered=true;
}
if (ImGui::RadioButton(_("128KB"),ramSize==4)) {
if (ImGui::RadioButton(_("512KB"),ramSize==4)) {
ramSize=4;
altered=true;
}
if (ImGui::RadioButton(_("256KB"),ramSize==5)) {
ramSize=5;
altered=true;
}
if (ImGui::RadioButton(_("128KB"),ramSize==6)) {
ramSize=6;
altered=true;
}
ImGui::Unindent();
if (altered) {

View file

@ -333,6 +333,7 @@ TAParamResult pVersion(String) {
printf("- PowerNoise emulator by scratchminer (MIT)\n");
printf("- ep128emu by Istvan Varga (GPLv2)\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;
}