diff --git a/extern/adpcm-xq b/extern/adpcm-xq new file mode 160000 index 000000000..6220fed76 --- /dev/null +++ b/extern/adpcm-xq @@ -0,0 +1 @@ +Subproject commit 6220fed7655e86a29702b45dbc641a028ed5a4bf diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index 65ca2383d..9bb0abc75 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -760,6 +760,18 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do case DIV_SYSTEM_SID2: dispatch=new DivPlatformSID2; break; + case DIV_SYSTEM_OPL4: + dispatch=new DivPlatformOPL; + ((DivPlatformOPL*)dispatch)->setOPLType(4,false); + // YMFM for now + ((DivPlatformOPL*)dispatch)->setCore(1); + break; + case DIV_SYSTEM_OPL4_DRUMS: + dispatch=new DivPlatformOPL; + ((DivPlatformOPL*)dispatch)->setOPLType(4,true); + // YMFM for now + ((DivPlatformOPL*)dispatch)->setCore(1); + break; case DIV_SYSTEM_DUMMY: dispatch=new DivPlatformDummy; break; diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 77eabbf1e..b9188d1db 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -159,6 +159,22 @@ const int orderedOpsL[4]={ #define ADDR_FREQH 0xb0 #define ADDR_LR_FB_ALG 0xc0 + +#define PCM_ADDR_WAVE_L 0x208 // Wavetable number LSB +#define PCM_ADDR_WAVE_H_FN_L 0x220 // Wavetable number MSB, F-number LSB +#define PCM_ADDR_FN_H_PR_OCT 0x238 // F-number MSB, Pseudo-reverb, Octave +#define PCM_ADDR_TL 0x250 // Total level, Level direct +#define PCM_ADDR_KEY_DAMP_LFORST_CH_PAN 0x268 // Key, Damp, LFO Reset, Channel select, Panpot + +#define PCM_ADDR_LFO_VIB 0x280 +#define PCM_ADDR_AR_D1R 0x298 +#define PCM_ADDR_DL_D2R 0x2b0 +#define PCM_ADDR_RC_RR 0x2c8 +#define PCM_ADDR_AM 0x2e0 + +#define PCM_ADDR_MIX_FM 0x2f8 +#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]; @@ -505,6 +521,103 @@ void DivPlatformOPL::acquire_ymfm3(short** buf, size_t len) { } } +void DivPlatformOPL::acquire_ymfm4(short** buf, size_t len) { + ymfm::ymfm_output<6> out; + + ymfm::ymf278b::fm_engine* fme=fm_ymfm4->debug_fm_engine(); + ymfm::pcm_engine* pcme=fm_ymfm4->debug_pcm_engine(); + ymfm::fm_channel>* fmChan[18]; + ymfm::pcm_channel* pcmChan[24]; + + for (int i=0; i<18; i++) { + fmChan[i]=fme->debug_channel(i); + } + + for (int i=0; i<24; i++) { + pcmChan[i]=pcme->debug_channel(i); + } + + for (size_t h=0; hwrite((w.addr&0x200)?4:(w.addr&0x100)?2:0,w.addr); + fm_ymfm4->write((w.addr&0x200)?5:1,w.val); + + regPool[(w.addr&0x200)?(0x200+(w.addr&255)):(w.addr&511)]=w.val; + writes.pop(); + } + + fm_ymfm4->generate(&out,1); + + buf[0][h]=out.data[4]>>1; // FM + PCM left + if (totalOutputs>1) { + buf[1][h]=out.data[5]>>1; // FM + PCM right + } + if (totalOutputs>2) { + buf[2][h]=out.data[0]>>1; // FM left + } + if (totalOutputs>3) { + buf[3][h]=out.data[1]>>1; // FM right + } + if (totalOutputs==6) { + buf[4][h]=out.data[2]>>1; // PCM left + buf[5][h]=out.data[3]>>1; // PCM right + } + + if (properDrums) { + for (int i=0; i<16; i++) { + unsigned char ch=(i<12 && chan[i&(~1)].fourOp)?outChanMap[i^1]:outChanMap[i]; + if (ch==255) continue; + int chOut=fmChan[ch]->debug_output(0); + if (chOut==0) { + chOut=fmChan[ch]->debug_output(1); + } + if (chOut==0) { + chOut=fmChan[ch]->debug_output(2); + } + if (chOut==0) { + chOut=fmChan[ch]->debug_output(3); + } + if (i==15) { + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(chOut,-32768,32767); + } else { + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(chOut<<1,-32768,32767); + } + } + oscBuf[16]->data[oscBuf[16]->needle++]=CLAMP(fmChan[7]->debug_special2()<<1,-32768,32767); + oscBuf[17]->data[oscBuf[17]->needle++]=CLAMP(fmChan[8]->debug_special1()<<1,-32768,32767); + oscBuf[18]->data[oscBuf[18]->needle++]=CLAMP(fmChan[8]->debug_special2()<<1,-32768,32767); + oscBuf[19]->data[oscBuf[19]->needle++]=CLAMP(fmChan[7]->debug_special1()<<1,-32768,32767); + } else { + for (int i=0; i<18; i++) { + unsigned char ch=outChanMap[i]; + if (ch==255) continue; + int chOut=fmChan[ch]->debug_output(0); + if (chOut==0) { + chOut=fmChan[ch]->debug_output(1); + } + if (chOut==0) { + chOut=fmChan[ch]->debug_output(2); + } + if (chOut==0) { + chOut=fmChan[ch]->debug_output(3); + } + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(chOut<<1,-32768,32767); + } + } + for (int i=0; i<24; i++) { + unsigned char oscOffs=i+pcmChanOffs; + int chOut=pcmChan[i]->debug_output(0); + chOut+=pcmChan[i]->debug_output(1); + chOut+=pcmChan[i]->debug_output(2); + chOut+=pcmChan[i]->debug_output(3); + oscBuf[oscOffs]->data[oscBuf[oscOffs]->needle++]=CLAMP(chOut>>3,-32768,32767); + } + } +} + static const int cycleMap[18]={ 6, 7, 8, 6, 7, 8, 0, 1, 2, 0, 1, 2, 3, 4, 5, 3, 4, 5, @@ -816,6 +929,9 @@ void DivPlatformOPL::acquire(short** buf, size_t len) { case 3: case 759: acquire_ymfm3(buf,len); break; + case 4: + acquire_ymfm4(buf,len); + break; } } else { // OPL3 acquire_nuked(buf,len); @@ -833,144 +949,148 @@ double DivPlatformOPL::NOTE_ADPCMB(int note) { void DivPlatformOPL::tick(bool sysTick) { for (int i=0; i=pcmChanOffs) { // OPL4 PCM + chan[i].std.next(); + } else { + int ops=(slots[3][i]!=255 && chan[i].state.ops==4 && oplType==3)?4:2; + chan[i].std.next(); + + if (chan[i].std.vol.had) { + chan[i].outVol=VOL_SCALE_LOG_BROKEN(chan[i].vol,MIN(63,chan[i].std.vol.val),63); + for (int j=0; jmelodicChans) { + rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG_BROKEN(63-op.tl,chan[i].outVol&0x3f,63))|(op.ksl<<6)); + } else { + rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); + } + } + } + } + + if (NEW_ARP_STRAT) { + chan[i].handleArp(); + } else if (chan[i].std.arp.had) { + if (!chan[i].inPorta) { + chan[i].baseFreq=NOTE_FREQUENCY(parent->calcArp(chan[i].note,chan[i].std.arp.val)); + } + chan[i].freqChanged=true; + } + + if (oplType==3 && chan[i].std.panL.had) { + chan[i].pan=((chan[i].std.panL.val&1)<<1)|((chan[i].std.panL.val&2)>>1)|((chan[i].std.panL.val&4)<<1)|((chan[i].std.panL.val&8)>>1); + } + + 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.alg.had) { + chan[i].state.alg=chan[i].std.alg.val; + } + if (chan[i].std.fb.had) { + chan[i].state.fb=chan[i].std.fb.val; + } + + if (chan[i].std.alg.had || chan[i].std.fb.had || (oplType==3 && chan[i].std.panL.had)) { + if (isMuted[i] && i<=melodicChans) { + rWrite(chanMap[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&1)|(chan[i].state.fb<<1)); + if (ops==4) { + rWrite(chanMap[i+1]+ADDR_LR_FB_ALG,((chan[i].state.alg>>1)&1)|(chan[i].state.fb<<1)); + } + } else { + rWrite(chanMap[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&1)|(chan[i].state.fb<<1)|((chan[i].pan&15)<<4)); + if (ops==4) { + rWrite(chanMap[i+1]+ADDR_LR_FB_ALG,((chan[i].state.alg>>1)&1)|(chan[i].state.fb<<1)|((chan[i].pan&15)<<4)); + } + } + } - if (chan[i].std.vol.had) { - chan[i].outVol=VOL_SCALE_LOG_BROKEN(chan[i].vol,MIN(63,chan[i].std.vol.val),63); for (int j=0; jmelodicChans) { - rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG_BROKEN(63-op.tl,chan[i].outVol&0x3f,63))|(op.ksl<<6)); - } else { - rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); + if (m.ar.had) { + op.ar=m.ar.val; + rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr); + } + if (m.dr.had) { + op.dr=m.dr.val; + rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr); + } + if (m.sl.had) { + op.sl=m.sl.val; + rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr); + } + if (m.rr.had) { + op.rr=m.rr.val; + rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr); + } + + if (oplType>1) { + if (m.ws.had) { + op.ws=m.ws.val; + rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3)); } } - } - } - if (NEW_ARP_STRAT) { - chan[i].handleArp(); - } else if (chan[i].std.arp.had) { - if (!chan[i].inPorta) { - chan[i].baseFreq=NOTE_FREQUENCY(parent->calcArp(chan[i].note,chan[i].std.arp.val)); - } - chan[i].freqChanged=true; - } - - if (oplType==3 && chan[i].std.panL.had) { - chan[i].pan=((chan[i].std.panL.val&1)<<1)|((chan[i].std.panL.val&2)>>1)|((chan[i].std.panL.val&4)<<1)|((chan[i].std.panL.val&8)>>1); - } - - 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.alg.had) { - chan[i].state.alg=chan[i].std.alg.val; - } - if (chan[i].std.fb.had) { - chan[i].state.fb=chan[i].std.fb.val; - } - - if (chan[i].std.alg.had || chan[i].std.fb.had || (oplType==3 && chan[i].std.panL.had)) { - if (isMuted[i] && i<=melodicChans) { - rWrite(chanMap[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&1)|(chan[i].state.fb<<1)); - if (ops==4) { - rWrite(chanMap[i+1]+ADDR_LR_FB_ALG,((chan[i].state.alg>>1)&1)|(chan[i].state.fb<<1)); + if (m.tl.had) { + op.tl=m.tl.val&63; } - } else { - rWrite(chanMap[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&1)|(chan[i].state.fb<<1)|((chan[i].pan&15)<<4)); - if (ops==4) { - rWrite(chanMap[i+1]+ADDR_LR_FB_ALG,((chan[i].state.alg>>1)&1)|(chan[i].state.fb<<1)|((chan[i].pan&15)<<4)); + if (m.ksl.had) { + op.ksl=m.ksl.val; } - } - } - - for (int j=0; j1) { - if (m.ws.had) { - op.ws=m.ws.val; - rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3)); - } - } - - if (m.tl.had) { - op.tl=m.tl.val&63; - } - if (m.ksl.had) { - op.ksl=m.ksl.val; - } - if (m.tl.had || m.ksl.had) { - if (isMuted[i] && i<=melodicChans) { - rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6)); - } else { - if (KVSL(i,j) || i>melodicChans) { - rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG_BROKEN(63-op.tl,chan[i].outVol&0x3f,63))|(op.ksl<<6)); + if (m.tl.had || m.ksl.had) { + if (isMuted[i] && i<=melodicChans) { + rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6)); } else { - rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); + if (KVSL(i,j) || i>melodicChans) { + rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG_BROKEN(63-op.tl,chan[i].outVol&0x3f,63))|(op.ksl<<6)); + } else { + rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); + } } } } @@ -1090,7 +1210,7 @@ void DivPlatformOPL::tick(bool sysTick) { } } - for (int i=0; i<512; i++) { + for (int i=0; i<768; i++) { if (pendingWrites[i]!=oldWrites[i]) { if ((i>=0x80 && i<0xa0)) { if (weWillWriteRRLater[i-0x80]) continue; @@ -1104,38 +1224,42 @@ void DivPlatformOPL::tick(bool sysTick) { bool updateDrums=false; for (int i=0; icalcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,octave(chan[i].baseFreq)*2,chan[i].pitch2,chipClock,CHIP_FREQBASE); - if (chan[i].fixedFreq>0) chan[i].freq=chan[i].fixedFreq; - if (chan[i].freq<0) chan[i].freq=0; - if (chan[i].freq>131071) chan[i].freq=131071; - int freqt=toFreq(chan[i].freq); - chan[i].freqH=freqt>>8; - chan[i].freqL=freqt&0xff; - immWrite(chanMap[i]+ADDR_FREQ,chan[i].freqL); - } - if (i=pcmChanOffs) { // OPL4 PCM + + } else { + if (chan[i].freqChanged) { + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,octave(chan[i].baseFreq)*2,chan[i].pitch2,chipClock,CHIP_FREQBASE); + if (chan[i].fixedFreq>0) chan[i].freq=chan[i].fixedFreq; + if (chan[i].freq<0) chan[i].freq=0; + if (chan[i].freq>131071) chan[i].freq=131071; + int freqt=toFreq(chan[i].freq); + chan[i].freqH=freqt>>8; + chan[i].freqL=freqt&0xff; + immWrite(chanMap[i]+ADDR_FREQ,chan[i].freqL); + } + if (i=pcmChanOffs) { // OPL4 PCM + + } else if (c.chan==adpcmChan) { // ADPCM DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255; if (ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_ADPCMB) { @@ -1553,7 +1679,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { if (oplType!=3) break; if (c.chan==adpcmChan) break; chan[c.chan].pan&=~3; - if (c.value==0 && c.value2==0 && compatPan) { + if (c.value==0 && c.value2==0 && ((chipType!=4) && compatPan)) { chan[c.chan].pan|=3; } else { chan[c.chan].pan|=(c.value>0)|((c.value2>0)<<1); @@ -1659,6 +1785,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { break; } case DIV_CMD_FM_LFO: { + if (c.chan>=pcmChanOffs) break; if (c.chan==adpcmChan) break; if (c.value&2) { dvb=c.value&1; @@ -1669,6 +1796,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { break; } case DIV_CMD_FM_FB: { + if (c.chan>=pcmChanOffs) 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; @@ -1686,6 +1814,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { break; } case DIV_CMD_FM_MULT: { + if (c.chan>=pcmChanOffs) 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; @@ -1698,6 +1827,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { break; } case DIV_CMD_FM_TL: { + if (c.chan>=pcmChanOffs) 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; @@ -1718,6 +1848,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { break; } case DIV_CMD_FM_AR: { + if (c.chan>=pcmChanOffs) 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) { @@ -1741,6 +1872,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { break; } case DIV_CMD_FM_DR: { + if (c.chan>=pcmChanOffs) 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) { @@ -1764,6 +1896,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { break; } case DIV_CMD_FM_SL: { + if (c.chan>=pcmChanOffs) 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) { @@ -1787,6 +1920,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { break; } case DIV_CMD_FM_RR: { + if (c.chan>=pcmChanOffs) 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) { @@ -1810,6 +1944,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { break; } case DIV_CMD_FM_AM: { + if (c.chan>=pcmChanOffs) 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) { @@ -1833,6 +1968,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { break; } case DIV_CMD_FM_VIB: { + if (c.chan>=pcmChanOffs) 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) { @@ -1856,6 +1992,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { break; } case DIV_CMD_FM_SUS: { + if (c.chan>=pcmChanOffs) 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) { @@ -1879,6 +2016,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { break; } case DIV_CMD_FM_KSR: { + if (c.chan>=pcmChanOffs) 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) { @@ -1902,6 +2040,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { break; } case DIV_CMD_FM_WS: { + if (c.chan>=pcmChanOffs) 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; @@ -1926,6 +2065,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { break; } case DIV_CMD_FM_RS: { + if (c.chan>=pcmChanOffs) 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; @@ -1974,6 +2114,10 @@ int DivPlatformOPL::dispatch(DivCommand c) { chanMap=properDrums?chanMapOPL3Drums:chanMapOPL3; melodicChans=properDrums?15:18; totalChans=properDrums?20:18; + if (chipType==4) { + pcmChanOffs=totalChans; + totalChans+=24; + } } else { chanMap=properDrums?chanMapOPL2Drums:chanMapOPL2; melodicChans=properDrums?6:9; @@ -1995,6 +2139,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 (c.chan==adpcmChan) return 255; if (pretendYMU) return 127; return 63; @@ -2019,6 +2164,10 @@ void DivPlatformOPL::forceIns() { chanMap=properDrums?chanMapOPL3Drums:chanMapOPL3; melodicChans=properDrums?15:18; totalChans=properDrums?20:18; + if (chipType==4) { + pcmChanOffs=totalChans; + totalChans+=24; + } } else { chanMap=properDrums?chanMapOPL2Drums:chanMapOPL2; melodicChans=properDrums?6:9; @@ -2067,7 +2216,7 @@ void DivPlatformOPL::forceIns() { } */ } - for (int i=0; i<512; i++) { + for (int i=0; i<768; i++) { oldWrites[i]=-1; } immWrite(0xbd,(dam<<7)|(dvb<<6)|(properDrums<<5)|drumState); @@ -2145,12 +2294,12 @@ unsigned char* DivPlatformOPL::getRegisterPool() { } int DivPlatformOPL::getRegisterPoolSize() { - return (oplType<3)?256:512; + return (chipType==4)?768:((oplType<3)?256:512); } void DivPlatformOPL::reset() { while (!writes.empty()) writes.pop(); - memset(regPool,0,512); + memset(regPool,0,768); dacVal=0; dacVal2=0; @@ -2204,6 +2353,9 @@ void DivPlatformOPL::reset() { case 3: case 759: fm_ymfm3->reset(); break; + case 4: + fm_ymfm4->reset(); + break; } } else { if (downsample) { @@ -2223,6 +2375,10 @@ void DivPlatformOPL::reset() { outChanMap=outChanMapOPL3; melodicChans=properDrums?15:18; totalChans=properDrums?20:18; + if (chipType==4) { + pcmChanOffs=totalChans; + totalChans+=24; + } } else { chanMap=properDrums?chanMapOPL2Drums:chanMapOPL2; outChanMap=outChanMapOPL2; @@ -2256,7 +2412,7 @@ void DivPlatformOPL::reset() { fm.channel[outChanMap[i]].muted=isMuted[i]; } - for (int i=0; i<512; i++) { + for (int i=0; i<768; i++) { oldWrites[i]=-1; pendingWrites[i]=-1; } @@ -2277,7 +2433,13 @@ void DivPlatformOPL::reset() { } if (oplType==3) { // enable OPL3 features - immWrite(0x105,1); + if (chipType==4) { + immWrite(0x105,3); + // Reset wavetable header + immWrite(0x202,(ramSize<=0x200000)?0x10:0x00); + } else { + immWrite(0x105,1); + } } update4OpMask=true; @@ -2368,6 +2530,8 @@ void DivPlatformOPL::setOPLType(int type, bool drums) { pretendYMU=true; adpcmChan=16; } else if (type==4) { + pcmChanOffs=totalChans; + totalChans+=24; chipFreqBase=32768*684; downsample=true; } @@ -2493,18 +2657,36 @@ void DivPlatformOPL::setFlags(const DivConfig& flags) { case 4: switch (flags.getInt("clockSel",0)) { case 0x01: - chipClock=COLOR_PAL*32.0/5.0; + chipClock=COLOR_NTSC*8.0; break; case 0x02: - chipClock=33868800.0; + chipClock=COLOR_PAL*32.0/5.0; break; default: - chipClock=COLOR_NTSC*8.0; + chipClock=33868800.0; + break; + } + switch (flags.getInt("ramSize",0)) { + case 0x01: + ramSize=0x200000; + break; + case 0x02: + ramSize=0x100000; + break; + case 0x03: + ramSize=0x80000; + break; + case 0x04: + ramSize=0x20000; + break; + default: + ramSize=0x400000; break; } CHECK_CUSTOM_CLOCK; rate=chipClock/768; chipRateBase=chipClock/684; + immWrite(0x202,(ramSize<=0x200000)?0x10:0x00); break; case 759: rate=48000; @@ -2514,21 +2696,25 @@ void DivPlatformOPL::setFlags(const DivConfig& flags) { } compatPan=flags.getBool("compatPan",false); - for (int i=0; i<20; i++) { + for (int i=0; i<44; i++) { oscBuf[i]->rate=rate; } } const void* DivPlatformOPL::getSampleMem(int index) { - return (index==0 && adpcmChan>=0) ? adpcmBMem : NULL; + return (index==0 && pcmChanOffs>=0)?pcmMem: + (index==0 && adpcmChan>=0)?adpcmBMem:NULL; } size_t DivPlatformOPL::getSampleMemCapacity(int index) { - return (index==0 && adpcmChan>=0) ? 262144 : 0; + return (index==0 && pcmChanOffs>=0)? + ((ramSize<=0x200000)?0x200000+ramSize:ramSize): + ((index==0 && adpcmChan>=0)?262144:0); } size_t DivPlatformOPL::getSampleMemUsage(int index) { - return (index==0 && adpcmChan>=0) ? adpcmBMemLen : 0; + return (index==0 && pcmChanOffs>=0)?pcmMemLen: + (index==0 && adpcmChan>=0)?adpcmBMemLen:0; } bool DivPlatformOPL::isSampleLoaded(int index, int sample) { @@ -2538,61 +2724,151 @@ bool DivPlatformOPL::isSampleLoaded(int index, int sample) { } const DivMemoryComposition* DivPlatformOPL::getMemCompo(int index) { - if (adpcmChan<0) return NULL; + if ((adpcmChan<0) && (pcmChanOffs<0)) return NULL; if (index!=0) return NULL; return &memCompo; } void DivPlatformOPL::renderSamples(int sysID) { - if (adpcmChan<0) return; - memset(adpcmBMem,0,getSampleMemCapacity(0)); + if (adpcmChan<0 && pcmChanOffs<0) return; + if (adpcmBMem!=NULL) { + memset(adpcmBMem,0,262144); + } + if (pcmMem!=NULL) { + memset(pcmMem,0,4194304); + } + memset(sampleOffPCM,0,256*sizeof(unsigned int)); memset(sampleOffB,0,256*sizeof(unsigned int)); memset(sampleLoaded,0,256*sizeof(bool)); memCompo=DivMemoryComposition(); memCompo.name="Sample Memory"; - size_t memPos=0; - for (int i=0; isong.sampleLen; i++) { - DivSample* s=parent->song.sample[i]; - if (!s->renderOn[0][sysID]) { - sampleOffB[i]=0; - continue; + if (pcmChanOffs>=0) { // OPL4 PCM + size_t memPos=((ramSize<=0x200000)?0x200600:0x1800); + int sampleCount=parent->song.sampleLen; + if (sampleCount>511) sampleCount=511; + for (int i=0; isong.sample[i]; + if (!s->renderOn[0][sysID]) { + sampleOffPCM[i]=0; + continue; + } + + int length; + switch (s->depth) { + default: + case DIV_SAMPLE_DEPTH_8BIT: + length=MIN(65535,s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT)); + break; + case DIV_SAMPLE_DEPTH_16BIT: + length=MIN(131070,s->getLoopEndPosition(DIV_SAMPLE_DEPTH_16BIT)); + break; + } + unsigned char* src=(unsigned char*)s->getCurBuf(); + int actualLength=MIN((int)(getSampleMemCapacity(0)-memPos),length); + if (actualLength>0) { + #ifdef TA_BIG_ENDIAN + memcpy(&pcmMem[memPos],src,actualLength); + #else + if (s->depth==DIV_SAMPLE_DEPTH_16BIT) { + for (int i=0; isong.sample[i]; + unsigned int insAddr=(i*12)+((ramSize<=0x200000)?0x200000:0); + unsigned char bitDepth; + int loop=CLAMP(s->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT),0,0xffff); + int endPos=CLAMP(s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT),1,0x10000); + switch (s->depth) { + default: + case DIV_SAMPLE_DEPTH_8BIT: + bitDepth=0; + break; + case DIV_SAMPLE_DEPTH_16BIT: + bitDepth=2; + break; + } + pcmMem[insAddr]=(bitDepth<<6)|((sampleOffPCM[i]>>16)&0x3f); + pcmMem[1+insAddr]=(sampleOffPCM[i]>>8)&0xff; + pcmMem[2+insAddr]=(sampleOffPCM[i])&0xff; + pcmMem[3+insAddr]=(loop>>8)&0xff; + pcmMem[4+insAddr]=(loop)&0xff; + pcmMem[5+insAddr]=(endPos>>8)&0xff; + pcmMem[6+insAddr]=(endPos)&0xff; + // TODO: how to fill in rest of instrument table? + pcmMem[7+insAddr]=0; // LFO, VIB + pcmMem[8+insAddr]=0; // AR, D1R + pcmMem[9+insAddr]=0; // DL, D2R + pcmMem[10+insAddr]=0; // RC, RR + pcmMem[11+insAddr]=0; // AM + } + if (ramSize<=0x200000) { + memCompo.entries.push_back(DivMemoryEntry(DIV_MEMORY_RESERVED,"ROM data",0,0,0x200000)); } - int paddedLen=(s->lengthB+255)&(~0xff); - if ((memPos&0xf00000)!=((memPos+paddedLen)&0xf00000)) { - memPos=(memPos+0xfffff)&0xf00000; + memCompo.used=pcmMemLen; + memCompo.capacity=getSampleMemCapacity(0); + } else if (adpcmChan>=0) { // ADPCM + size_t memPos=0; + for (int i=0; isong.sampleLen; i++) { + DivSample* s=parent->song.sample[i]; + if (!s->renderOn[0][sysID]) { + sampleOffB[i]=0; + continue; + } + + int paddedLen=(s->lengthB+255)&(~0xff); + if ((memPos&0xf00000)!=((memPos+paddedLen)&0xf00000)) { + memPos=(memPos+0xfffff)&0xf00000; + } + if (memPos>=getSampleMemCapacity(0)) { + logW("out of ADPCM memory for sample %d!",i); + break; + } + if (memPos+paddedLen>=getSampleMemCapacity(0)) { + memcpy(adpcmBMem+memPos,s->dataB,getSampleMemCapacity(0)-memPos); + logW("out of ADPCM memory for sample %d!",i); + } else { + memcpy(adpcmBMem+memPos,s->dataB,paddedLen); + sampleLoaded[i]=true; + } + sampleOffB[i]=memPos; + memCompo.entries.push_back(DivMemoryEntry(DIV_MEMORY_SAMPLE,"Sample",i,memPos,memPos+paddedLen)); + memPos+=paddedLen; } - if (memPos>=getSampleMemCapacity(0)) { - logW("out of ADPCM memory for sample %d!",i); - break; - } - if (memPos+paddedLen>=getSampleMemCapacity(0)) { - memcpy(adpcmBMem+memPos,s->dataB,getSampleMemCapacity(0)-memPos); - logW("out of ADPCM memory for sample %d!",i); - } else { - memcpy(adpcmBMem+memPos,s->dataB,paddedLen); - sampleLoaded[i]=true; - } - sampleOffB[i]=memPos; - memCompo.entries.push_back(DivMemoryEntry(DIV_MEMORY_SAMPLE,"Sample",i,memPos,memPos+paddedLen)); - memPos+=paddedLen; + adpcmBMemLen=memPos+256; + + memCompo.used=adpcmBMemLen; + memCompo.capacity=getSampleMemCapacity(0); } - adpcmBMemLen=memPos+256; - - memCompo.used=adpcmBMemLen; - memCompo.capacity=getSampleMemCapacity(0); } int DivPlatformOPL::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) { parent=p; dumpWrites=false; skipRegisterWrites=false; - for (int i=0; i<20; i++) { + for (int i=0; i<44; i++) { isMuted[i]=false; } - for (int i=0; i<20; i++) { + for (int i=0; i<44; i++) { oscBuf[i]=new DivDispatchOscBuffer; } @@ -2600,6 +2876,7 @@ int DivPlatformOPL::init(DivEngine* p, int channels, int sugRate, const DivConfi fm_ymfm2=NULL; fm_ymfm8950=NULL; fm_ymfm3=NULL; + fm_ymfm4=NULL; if (emuCore==1) { switch (chipType) { @@ -2615,31 +2892,44 @@ int DivPlatformOPL::init(DivEngine* p, int channels, int sugRate, const DivConfi case 3: case 759: fm_ymfm3=new ymfm::ymf262(iface); break; + case 4: + fm_ymfm4=new ymfm::ymf278b(iface); + break; } } setFlags(flags); if (adpcmChan>=0) { - adpcmBMem=new unsigned char[getSampleMemCapacity(0)]; + adpcmBMem=new unsigned char[262144]; adpcmBMemLen=0; iface.adpcmBMem=adpcmBMem; iface.sampleBank=0; adpcmB=new ymfm::adpcm_b_engine(iface,2); } + if (pcmChanOffs>=0) { + pcmMem=new unsigned char[4194304]; + pcmMemLen=0; + iface.pcmMem=pcmMem; + iface.sampleBank=0; + } + reset(); return totalChans; } void DivPlatformOPL::quit() { - for (int i=0; i<20; i++) { + for (int i=0; i<44; i++) { delete oscBuf[i]; } if (adpcmChan>=0) { delete adpcmB; delete[] adpcmBMem; } + if (pcmChanOffs>=0) { + delete[] pcmMem; + } if (fm_ymfm1!=NULL) { delete fm_ymfm1; fm_ymfm1=NULL; @@ -2656,6 +2946,10 @@ void DivPlatformOPL::quit() { delete fm_ymfm3; fm_ymfm3=NULL; } + if (fm_ymfm4!=NULL) { + delete fm_ymfm4; + fm_ymfm4=NULL; + } } DivPlatformOPL::~DivPlatformOPL() { diff --git a/src/engine/platform/opl.h b/src/engine/platform/opl.h index e76aae834..90951b824 100644 --- a/src/engine/platform/opl.h +++ b/src/engine/platform/opl.h @@ -29,14 +29,16 @@ extern "C" { } #include "sound/ymfm/ymfm_adpcm.h" #include "sound/ymfm/ymfm_opl.h" +#include "sound/ymfm/ymfm_pcm.h" class DivOPLAInterface: public ymfm::ymfm_interface { public: unsigned char* adpcmBMem; + unsigned char* pcmMem; int sampleBank; uint8_t ymfm_external_read(ymfm::access_class type, uint32_t address); void ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data); - DivOPLAInterface(): adpcmBMem(NULL), sampleBank(0) {} + DivOPLAInterface(): adpcmBMem(NULL), pcmMem(NULL), sampleBank(0) {} }; class DivPlatformOPL: public DivDispatch { @@ -62,9 +64,9 @@ class DivPlatformOPL: public DivDispatch { state.ops=2; } }; - Channel chan[20]; - DivDispatchOscBuffer* oscBuf[20]; - bool isMuted[20]; + Channel chan[44]; + DivDispatchOscBuffer* oscBuf[44]; + bool isMuted[44]; struct QueuedWrite { unsigned short addr; unsigned char val; @@ -72,7 +74,7 @@ class DivPlatformOPL: public DivDispatch { QueuedWrite(): addr(0), val(0), addrOrVal(false) {} QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {} }; - FixedQueue writes; + FixedQueue writes; unsigned int dacVal; unsigned int dacVal2; @@ -86,8 +88,11 @@ class DivPlatformOPL: public DivDispatch { unsigned char* adpcmBMem; size_t adpcmBMemLen; + unsigned char* pcmMem; + size_t pcmMemLen; DivOPLAInterface iface; unsigned int sampleOffB[256]; + unsigned int sampleOffPCM[256]; bool sampleLoaded[256]; ymfm::adpcm_b_engine* adpcmB; @@ -97,12 +102,12 @@ class DivPlatformOPL: public DivDispatch { const unsigned short* chanMap; const unsigned char* outChanMap; int chipFreqBase, chipRateBase; - int delay, chipType, oplType, chans, melodicChans, totalChans, adpcmChan, sampleBank, totalOutputs; + int delay, chipType, oplType, chans, melodicChans, totalChans, adpcmChan=-1, pcmChanOffs=-1, sampleBank, totalOutputs, ramSize; unsigned char lastBusy; unsigned char drumState; unsigned char drumVol[5]; - unsigned char regPool[512]; + unsigned char regPool[768]; bool properDrums, properDrumsSys, dam, dvb; @@ -115,8 +120,8 @@ class DivPlatformOPL: public DivDispatch { bool update4OpMask, pretendYMU, downsample, compatPan; - short oldWrites[512]; - short pendingWrites[512]; + short oldWrites[768]; + short pendingWrites[768]; // chips opl3_chip fm; @@ -124,6 +129,7 @@ class DivPlatformOPL: public DivDispatch { ymfm::ym3812* fm_ymfm2; ymfm::y8950* fm_ymfm8950; ymfm::ymf262* fm_ymfm3; + ymfm::ymf278b* fm_ymfm4; fmopl2_t fm_lle2; fmopl3_t fm_lle3; @@ -141,6 +147,7 @@ class DivPlatformOPL: public DivDispatch { void acquire_nukedLLE3(short** buf, size_t len); void acquire_nuked(short** buf, size_t len); void acquire_ymfm3(short** buf, size_t len); + void acquire_ymfm4(short** buf, size_t len); void acquire_ymfm8950(short** buf, size_t len); void acquire_ymfm2(short** buf, size_t len); void acquire_ymfm1(short** buf, size_t len); diff --git a/src/engine/platform/oplAInterface.cpp b/src/engine/platform/oplAInterface.cpp index 37ebda7f0..286385806 100644 --- a/src/engine/platform/oplAInterface.cpp +++ b/src/engine/platform/oplAInterface.cpp @@ -28,6 +28,11 @@ uint8_t DivOPLAInterface::ymfm_external_read(ymfm::access_class type, uint32_t a return 0; } return adpcmBMem[address&0xffffff]; + case ymfm::ACCESS_PCM: + if (pcmMem==NULL) { + return 0; + } + return pcmMem[address&0x3fffff]; default: return 0; } diff --git a/src/engine/platform/sound/ymfm/ymfm_fm.h b/src/engine/platform/sound/ymfm/ymfm_fm.h index f77e89434..0c4342f0b 100644 --- a/src/engine/platform/sound/ymfm/ymfm_fm.h +++ b/src/engine/platform/sound/ymfm/ymfm_fm.h @@ -304,9 +304,9 @@ public: // simple getters for debugging fm_operator *debug_operator(uint32_t index) const { return m_op[index]; } - int32_t debug_output(uint32_t index) const { return m_output[index]; } - int32_t debug_special1() const { return m_special1; } - int32_t debug_special2() const { return m_special2; } + int32_t debug_output(uint32_t index) const { return m_output[index]; } + int32_t debug_special1() const { return m_special1; } + int32_t debug_special2() const { return m_special2; } private: // helper to add values to the outputs based on channel enables @@ -320,21 +320,21 @@ private: constexpr int out3_index = 3 % RegisterType::OUTPUTS; if (RegisterType::OUTPUTS == 1 || m_regs.ch_output_0(choffs)) { - m_output[out0_index]=value; + m_output[out0_index]=value; output.data[out0_index] += value; - } + } if (RegisterType::OUTPUTS >= 2 && m_regs.ch_output_1(choffs)) { - m_output[out1_index]=value; + m_output[out1_index]=value; output.data[out1_index] += value; - } + } if (RegisterType::OUTPUTS >= 3 && m_regs.ch_output_2(choffs)) { - m_output[out2_index]=value; + m_output[out2_index]=value; output.data[out2_index] += value; - } + } if (RegisterType::OUTPUTS >= 4 && m_regs.ch_output_3(choffs)) { - m_output[out3_index]=value; + m_output[out3_index]=value; output.data[out3_index] += value; - } + } } // internal state @@ -344,9 +344,9 @@ private: fm_operator *m_op[4]; // up to 4 operators RegisterType &m_regs; // direct reference to registers fm_engine_base &m_owner; // reference to the owning engine - mutable int32_t m_output[4]; - mutable int32_t m_special1; - mutable int32_t m_special2; + mutable int32_t m_output[4]; + mutable int32_t m_special1; + mutable int32_t m_special2; }; diff --git a/src/engine/platform/sound/ymfm/ymfm_opl.h b/src/engine/platform/sound/ymfm/ymfm_opl.h index 8a2dd5147..c23a5f0b9 100644 --- a/src/engine/platform/sound/ymfm/ymfm_opl.h +++ b/src/engine/platform/sound/ymfm/ymfm_opl.h @@ -529,7 +529,7 @@ public: // generate samples of sound void generate(output_data *output, uint32_t numsamples = 1); - fm_engine* debug_fm_engine() { return &m_fm; } + fm_engine* debug_fm_engine() { return &m_fm; } protected: // internal state uint8_t m_address; // address register @@ -577,8 +577,8 @@ public: // generate samples of sound void generate(output_data *output, uint32_t numsamples = 1); - fm_engine* debug_fm_engine() { return &m_fm; } - adpcm_b_engine* debug_adpcm_b_engine() { return &m_adpcm_b; } + fm_engine* debug_fm_engine() { return &m_fm; } + adpcm_b_engine* debug_adpcm_b_engine() { return &m_adpcm_b; } protected: // internal state @@ -628,7 +628,7 @@ public: // generate samples of sound void generate(output_data *output, uint32_t numsamples = 1); - fm_engine* debug_fm_engine() { return &m_fm; } + fm_engine* debug_fm_engine() { return &m_fm; } protected: // internal state @@ -677,7 +677,7 @@ public: // generate samples of sound void generate(output_data *output, uint32_t numsamples = 1); - fm_engine* debug_fm_engine() { return &m_fm; } + fm_engine* debug_fm_engine() { return &m_fm; } protected: // internal state @@ -791,6 +791,8 @@ public: // generate samples of sound void generate(output_data *output, uint32_t numsamples = 1); + fm_engine* debug_fm_engine() { return &m_fm; } + pcm_engine* debug_pcm_engine() { return &m_pcm; } protected: // internal state uint16_t m_address; // address register diff --git a/src/engine/platform/sound/ymfm/ymfm_pcm.cpp b/src/engine/platform/sound/ymfm/ymfm_pcm.cpp index 34417490c..30fbe2396 100644 --- a/src/engine/platform/sound/ymfm/ymfm_pcm.cpp +++ b/src/engine/platform/sound/ymfm/ymfm_pcm.cpp @@ -309,6 +309,7 @@ void pcm_channel::clock(uint32_t env_counter) void pcm_channel::output(output_data &output) const { + m_output[0] = m_output[1] = m_output[2] = m_output[3] = 0; // early out if the envelope is effectively off uint32_t envelope = m_env_attenuation; if (envelope > EG_QUIET) @@ -340,6 +341,8 @@ void pcm_channel::output(output_data &output) const uint32_t outnum = m_regs.ch_output_channel(m_choffs) * 2; output.data[outnum + 0] += (lvol * sample) >> 15; output.data[outnum + 1] += (rvol * sample) >> 15; + m_output[outnum + 0] = output.data[outnum + 0]; + m_output[outnum + 1] = output.data[outnum + 1]; } diff --git a/src/engine/platform/sound/ymfm/ymfm_pcm.h b/src/engine/platform/sound/ymfm/ymfm_pcm.h index b471fa611..ad15a80de 100644 --- a/src/engine/platform/sound/ymfm/ymfm_pcm.h +++ b/src/engine/platform/sound/ymfm/ymfm_pcm.h @@ -267,6 +267,8 @@ public: // load a new wavetable entry void load_wavetable(); + int32_t debug_output(uint32_t index) const { return m_output[index]; } + private: // internal helpers void start_attack(); @@ -291,6 +293,7 @@ private: pcm_cache m_cache; // cached data pcm_registers &m_regs; // reference to registers pcm_engine &m_owner; // reference to our owner + mutable int32_t m_output[4]; }; @@ -331,6 +334,8 @@ public: // return a reference to our registers pcm_registers ®s() { return m_regs; } + // simple getters for debugging + pcm_channel *debug_channel(uint32_t index) const { return m_channel[index].get(); } private: // internal state ymfm_interface &m_intf; // reference to the interface diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 20e8461d5..41ecbb050 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1621,7 +1621,10 @@ void DivEngine::registerSystems() { {_("4OP 1"), _("FM 2"), _("4OP 3"), _("FM 4"), _("4OP 5"), _("FM 6"), _("4OP 7"), _("FM 8"), _("4OP 9"), _("FM 10"), _("4OP 11"), _("FM 12"), _("FM 13"), _("FM 14"), _("FM 15"), _("FM 16"), _("FM 17"), _("FM 18"), _("PCM 1"), _("PCM 2"), _("PCM 3"), _("PCM 4"), _("PCM 5"), _("PCM 6"), _("PCM 7"), _("PCM 8"), _("PCM 9"), _("PCM 10"), _("PCM 11"), _("PCM 12"), _("PCM 13"), _("PCM 14"), _("PCM 15"), _("PCM 16"), _("PCM 17"), _("PCM 18"), _("PCM 19"), _("PCM 20"), _("PCM 21"), _("PCM 22"), _("PCM 23"), _("PCM 24")}, {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "F16", "F17", "F18", "P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "P8", "P10", "P11", "P12", "P13", "P14", "P15", "P16", "P17", "P18", "P19", "P20", "P21", "P22", "P23", "P24"}, {DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, - {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM} + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM}, + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, + fmEffectHandlerMap, + fmOPLPostEffectHandlerMap ); // TODO: same here @@ -1631,7 +1634,10 @@ void DivEngine::registerSystems() { {_("4OP 1"), _("FM 2"), _("4OP 3"), _("FM 4"), _("4OP 5"), _("FM 6"), _("4OP 7"), _("FM 8"), _("4OP 9"), _("FM 10"), _("4OP 11"), _("FM 12"), _("FM 13"), _("FM 14"), _("FM 15"), _("Kick/FM 16"), _("Snare"), _("Tom"), _("Top"), _("HiHat"), _("PCM 1"), _("PCM 2"), _("PCM 3"), _("PCM 4"), _("PCM 5"), _("PCM 6"), _("PCM 7"), _("PCM 8"), _("PCM 9"), _("PCM 10"), _("PCM 11"), _("PCM 12"), _("PCM 13"), _("PCM 14"), _("PCM 15"), _("PCM 16"), _("PCM 17"), _("PCM 18"), _("PCM 19"), _("PCM 20"), _("PCM 21"), _("PCM 22"), _("PCM 23"), _("PCM 24")}, {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "BD", "SD", "TM", "TP", "HH", "P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "P8", "P10", "P11", "P12", "P13", "P14", "P15", "P16", "P17", "P18", "P19", "P20", "P21", "P22", "P23", "P24"}, {DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, - {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM} + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM}, + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, + fmOPLDrumsEffectHandlerMap, + fmOPLPostEffectHandlerMap ); EffectHandlerMap es5506PreEffectHandlerMap={ diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp index a8a1fab74..e0391fe6e 100644 --- a/src/engine/vgmOps.cpp +++ b/src/engine/vgmOps.cpp @@ -987,6 +987,13 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write break; } break; + case DIV_SYSTEM_OPL4: + case DIV_SYSTEM_OPL4_DRUMS: + w->writeC(0xd0|baseAddr2); + w->writeC(write.addr>>8); + w->writeC(write.addr&0xff); + w->writeC(write.val); + break; case DIV_SYSTEM_SCC: if (write.addr<0x80) { w->writeC(0xd2); @@ -1254,6 +1261,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p bool writeVOXSamples=false; DivDispatch* writeADPCM_OPNA[2]={NULL,NULL}; DivDispatch* writeADPCM_OPNB[2]={NULL,NULL}; + DivDispatch* writePCM_OPL4[2]={NULL,NULL}; DivDispatch* writeADPCM_Y8950[2]={NULL,NULL}; DivDispatch* writeSegaPCM[2]={NULL,NULL}; DivDispatch* writeX1010[2]={NULL,NULL}; @@ -1706,6 +1714,20 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p howManyChips++; } break; + case DIV_SYSTEM_OPL4: + case DIV_SYSTEM_OPL4_DRUMS: + if (!hasOPL4) { + hasOPL4=disCont[i].dispatch->chipClock; + CHIP_VOL(12,1.0); + willExport[i]=true; + } else if (!(hasOPL4&0x40000000)) { + isSecond[i]=true; + CHIP_VOL_SECOND(12,1.0); + willExport[i]=true; + hasOPL4|=0x40000000; + howManyChips++; + } + break; case DIV_SYSTEM_SCC: case DIV_SYSTEM_SCC_PLUS: if (!hasK051649) { @@ -2150,6 +2172,16 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p w->writeI(0); w->write(writeADPCM_OPNB[i]->getSampleMem(1),writeADPCM_OPNB[i]->getSampleMemUsage(1)); } + // PCM (OPL4) + if (writePCM_OPL4[i]!=NULL && writePCM_OPL4[i]->getSampleMemUsage(0)>0) { + w->writeC(0x67); + w->writeC(0x66); + w->writeC(0x84); + w->writeI((writePCM_OPL4[i]->getSampleMemUsage(0)+8)|(i*0x80000000)); + w->writeI(writePCM_OPL4[i]->getSampleMemCapacity(0)); + w->writeI(0); + w->write(writePCM_OPL4[i]->getSampleMem(0),writePCM_OPL4[i]->getSampleMemUsage(0)); + } // ADPCM (Y8950) if (writeADPCM_Y8950[i]!=NULL && writeADPCM_Y8950[i]->getSampleMemUsage(0)>0) { w->writeC(0x67); diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index ca7a26b44..b9d9ae85d 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -1260,6 +1260,8 @@ const int availableSystems[]={ DIV_SYSTEM_5E01, DIV_SYSTEM_BIFURCATOR, DIV_SYSTEM_SID2, + DIV_SYSTEM_OPL4, + DIV_SYSTEM_OPL4_DRUMS, 0 // don't remove this last one! }; @@ -1295,6 +1297,8 @@ const int chipsFM[]={ DIV_SYSTEM_OPL3_DRUMS, DIV_SYSTEM_OPZ, DIV_SYSTEM_ESFM, + DIV_SYSTEM_OPL4, + DIV_SYSTEM_OPL4_DRUMS, 0 // don't remove this last one! }; @@ -1380,6 +1384,8 @@ const int chipsSample[]={ DIV_SYSTEM_NDS, DIV_SYSTEM_GBA_DMA, DIV_SYSTEM_GBA_MINMOD, + DIV_SYSTEM_OPL4, + DIV_SYSTEM_OPL4_DRUMS, 0 // don't remove this last one! }; diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index 382bdd1e9..19a35c6bc 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -519,6 +519,18 @@ void FurnaceGUI::initSystemPresets() { ) // variable rate, Mono DAC } ); + SUB_ENTRY( + "MSX + Moonsound", { + CH(DIV_SYSTEM_AY8910, 1.0f, 0, "chipType=1"), + CH(DIV_SYSTEM_OPL4, 1.0f, 0, "") + } + ); + SUB_ENTRY( + "MSX + Moonsound (drums mode)", { + CH(DIV_SYSTEM_AY8910, 1.0f, 0, "chipType=1"), + CH(DIV_SYSTEM_OPL4_DRUMS, 1.0f, 0, "") + } + ); ENTRY( "NEC PC-88", {} ); @@ -2659,6 +2671,16 @@ void FurnaceGUI::initSystemPresets() { CH(DIV_SYSTEM_ESFM, 1.0f, 0, "") } ); + ENTRY( + "Yamaha YMF278B (OPL4)", { + CH(DIV_SYSTEM_OPL4, 1.0f, 0, "") + } + ); + SUB_ENTRY( + "Yamaha YMF278B (drums mode)", { + CH(DIV_SYSTEM_OPL4_DRUMS, 1.0f, 0, "") + } + ); if (settings.hiddenSystems) { ENTRY( "Yamaha YMU759 (MA-2)", { @@ -2870,6 +2892,16 @@ void FurnaceGUI::initSystemPresets() { CH(DIV_SYSTEM_NDS, 1.0f, 0, "") } ); + ENTRY( + "Yamaha YMF278B (OPL4)", { + CH(DIV_SYSTEM_OPL4, 1.0f, 0, "") + } + ); + SUB_ENTRY( + "Yamaha YMF278B (drums mode)", { + CH(DIV_SYSTEM_OPL4_DRUMS, 1.0f, 0, "") + } + ); CATEGORY_END; CATEGORY_BEGIN("Wavetable","chips which use user-specified waveforms to generate sound."); diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 0875f25c1..0c9848681 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -2503,6 +2503,59 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl } break; } + case DIV_SYSTEM_OPL4: + case DIV_SYSTEM_OPL4_DRUMS: { + int clockSel=flags.getInt("clockSel",0); + int ramSize=flags.getInt("ramSize",0); + + ImGui::Text(_("Clock rate:")); + ImGui::Indent(); + if (ImGui::RadioButton(_("33.8688MHz"),clockSel==0)) { + clockSel=0; + altered=true; + } + if (ImGui::RadioButton(_("28.64MHz (NTSC)"),clockSel==1)) { + clockSel=1; + altered=true; + } + if (ImGui::RadioButton(_("28.38MHz (PAL)"),clockSel==2)) { + clockSel=2; + altered=true; + } + ImGui::Unindent(); + + ImGui::Text(_("RAM size:")); + ImGui::Indent(); + if (ImGui::RadioButton(_("4MB"),ramSize==0)) { + ramSize=0; + altered=true; + } + if (ImGui::RadioButton(_("2MB"),ramSize==1)) { + ramSize=1; + altered=true; + } + if (ImGui::RadioButton(_("1MB"),ramSize==2)) { + ramSize=2; + altered=true; + } + if (ImGui::RadioButton(_("512KB"),ramSize==3)) { + ramSize=3; + altered=true; + } + if (ImGui::RadioButton(_("128KB"),ramSize==4)) { + ramSize=4; + altered=true; + } + ImGui::Unindent(); + + if (altered) { + e->lockSave([&]() { + flags.set("clockSel",clockSel); + flags.set("ramSize",ramSize); + }); + } + break; + } case DIV_SYSTEM_SWAN: case DIV_SYSTEM_BUBSYS_WSG: case DIV_SYSTEM_PET: