diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index 9bb0abc75..d1efa70ef 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -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; diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index ae978c4f8..57ed459a4 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -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=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].samplesong.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=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=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=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(); diff --git a/src/engine/platform/opl.h b/src/engine/platform/opl.h index 1394764e7..708d30b51 100644 --- a/src/engine/platform/opl.h +++ b/src/engine/platform/opl.h @@ -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 { @@ -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 diff --git a/src/engine/platform/oplAInterface.cpp b/src/engine/platform/oplAInterface.cpp index 286385806..46d39b869 100644 --- a/src/engine/platform/oplAInterface.cpp +++ b/src/engine/platform/oplAInterface.cpp @@ -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 #include @@ -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 diff --git a/src/engine/platform/sound/ymf278b/ymf278.h b/src/engine/platform/sound/ymf278b/ymf278.h index f02b179aa..b0622adb2 100644 --- a/src/engine/platform/sound/ymf278b/ymf278.h +++ b/src/engine/platform/sound/ymf278b/ymf278.h @@ -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: diff --git a/src/gui/about.cpp b/src/gui/about.cpp index fc9775368..18be5f5d0 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -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", diff --git a/src/gui/gui.h b/src/gui/gui.h index d168a8a0c..51161c224 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -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), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 0baee2725..63815cd02 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -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) || diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 0c9848681..085368b95 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -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) { diff --git a/src/main.cpp b/src/main.cpp index 9c3dc6a9e..d6ba74bc2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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; }