diff --git a/src/engine/platform/msm6295.cpp b/src/engine/platform/msm6295.cpp index 6b00ba1d89..a8c30b3e14 100644 --- a/src/engine/platform/msm6295.cpp +++ b/src/engine/platform/msm6295.cpp @@ -26,6 +26,14 @@ #define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} } #define rWriteDelay(a,v,d) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v,d)); if (dumpWrites) {addWrite(a,v);} } +#define setPhrase(c) \ + if (isBanked) { \ + rWrite(16+(c),bankedPhrase[chan[(c)].sample].bank); \ + rWrite(0,0x80|((c)<<5)|bankedPhrase[chan[(c)].sample].phrase); \ + } else { \ + rWrite(0,0x80|chan[(c)].sample); \ + } + const char** DivPlatformMSM6295::getRegisterSheet() { return NULL; } @@ -34,6 +42,12 @@ u8 DivPlatformMSM6295::read_byte(u32 address) { if (adpcmMem==NULL || address>=getSampleMemCapacity(0)) { return 0; } + if (isBanked) { + if (address<0x400) { + return adpcmMem[(bank[(address>>8)&0x3]<<16)|(address&0x3ff)]; + } + return adpcmMem[(bank[(address>>16)&0x3]<<16)|(address&0xffff)]; + } return adpcmMem[address&0x3ffff]; } @@ -62,6 +76,7 @@ void DivPlatformMSM6295::acquire(short** buf, size_t len) { case 17: case 18: case 19: + bank[w.addr-16]=w.val; break; } writes.pop(); @@ -109,7 +124,7 @@ void DivPlatformMSM6295::tick(bool sysTick) { rWriteDelay(0,(8<=0 && chan[i].samplesong.sampleLen) { - rWrite(0,0x80|chan[i].sample); // set phrase + setPhrase(i); rWrite(0,(16<getSample(12*sampleBank+c.value%12); chan[c.chan].sample=12*sampleBank+c.value%12; rWriteDelay(0,(8<song.sampleLen; - if (sampleCount>127) sampleCount=127; - for (int i=0; isong.sample[i]; - if (!s->renderOn[0][sysID]) { - sampleOffVOX[i]=0; - continue; + if (isBanked) { + int bankInd=0; + int phraseInd=0; + for (int i=0; isong.sampleLen; i++) { + DivSample* s=parent->song.sample[i]; + if (!s->renderOn[0][sysID]) { + sampleOffVOX[i]=0; + continue; + } + + int paddedLen=s->lengthVOX; + // fit to single bank size + if (paddedLen>65536-0x400) { + paddedLen=65536-0x400; + } + // 32 phrase per bank + if ((phraseInd>=32)||((memPos&0xff0000)!=((memPos+paddedLen)&0xff0000))) { + memPos=((memPos+0xffff)&0xff0000)+0x400; + bankInd++; + phraseInd=0; + } + if (memPos>=getSampleMemCapacity(0)) { + logW("out of ADPCM memory for sample %d!",i); + break; + } + if (memPos+paddedLen>=getSampleMemCapacity(0)) { + memcpy(adpcmMem+memPos,s->dataVOX,getSampleMemCapacity(0)-memPos); + logW("out of ADPCM memory for sample %d!",i); + } else { + memcpy(adpcmMem+memPos,s->dataVOX,paddedLen); + sampleLoaded[i]=true; + } + sampleOffVOX[i]=memPos; + bankedPhrase[i].bank=bankInd; + bankedPhrase[i].phrase=phraseInd; + bankedPhrase[i].length=paddedLen; + memPos+=paddedLen; + phraseInd++; + } + adpcmMemLen=memPos+256; + + // phrase book + for (int i=0; isong.sampleLen; i++) { + int endPos=sampleOffVOX[i]+bankedPhrase[i].length; + for (int b=0; b<4; b++) { + unsigned int bankedAddr=((unsigned int)bankedPhrase[i].bank<<16)+(b<<8)+(bankedPhrase[i].phrase*8); + adpcmMem[bankedAddr]=b; + adpcmMem[bankedAddr+1]=(sampleOffVOX[i]>>8)&0xff; + adpcmMem[bankedAddr+2]=(sampleOffVOX[i])&0xff; + adpcmMem[bankedAddr+3]=b; + adpcmMem[bankedAddr+4]=(endPos>>8)&0xff; + adpcmMem[bankedAddr+5]=(endPos)&0xff; + } } + } else { + int sampleCount=parent->song.sampleLen; + if (sampleCount>127) sampleCount=127; + for (int i=0; isong.sample[i]; + if (!s->renderOn[0][sysID]) { + sampleOffVOX[i]=0; + continue; + } - int paddedLen=s->lengthVOX; - if (memPos>=getSampleMemCapacity(0)) { - logW("out of ADPCM memory for sample %d!",i); - break; + int paddedLen=s->lengthVOX; + if (memPos>=getSampleMemCapacity(0)) { + logW("out of ADPCM memory for sample %d!",i); + break; + } + if (memPos+paddedLen>=getSampleMemCapacity(0)) { + memcpy(adpcmMem+memPos,s->dataVOX,getSampleMemCapacity(0)-memPos); + logW("out of ADPCM memory for sample %d!",i); + } else { + memcpy(adpcmMem+memPos,s->dataVOX,paddedLen); + sampleLoaded[i]=true; + } + sampleOffVOX[i]=memPos; + memPos+=paddedLen; } - if (memPos+paddedLen>=getSampleMemCapacity(0)) { - memcpy(adpcmMem+memPos,s->dataVOX,getSampleMemCapacity(0)-memPos); - logW("out of ADPCM memory for sample %d!",i); - } else { - memcpy(adpcmMem+memPos,s->dataVOX,paddedLen); - sampleLoaded[i]=true; + adpcmMemLen=memPos+256; + + // phrase book + for (int i=0; isong.sample[i]; + int endPos=sampleOffVOX[i]+s->lengthVOX; + adpcmMem[i*8]=(sampleOffVOX[i]>>16)&0xff; + adpcmMem[1+i*8]=(sampleOffVOX[i]>>8)&0xff; + adpcmMem[2+i*8]=(sampleOffVOX[i])&0xff; + adpcmMem[3+i*8]=(endPos>>16)&0xff; + adpcmMem[4+i*8]=(endPos>>8)&0xff; + adpcmMem[5+i*8]=(endPos)&0xff; } - sampleOffVOX[i]=memPos; - memPos+=paddedLen; - } - adpcmMemLen=memPos+256; - - // phrase book - for (int i=0; isong.sample[i]; - int endPos=sampleOffVOX[i]+s->lengthVOX; - adpcmMem[i*8]=(sampleOffVOX[i]>>16)&0xff; - adpcmMem[1+i*8]=(sampleOffVOX[i]>>8)&0xff; - adpcmMem[2+i*8]=(sampleOffVOX[i])&0xff; - adpcmMem[3+i*8]=(endPos>>16)&0xff; - adpcmMem[4+i*8]=(endPos>>8)&0xff; - adpcmMem[5+i*8]=(endPos)&0xff; } } void DivPlatformMSM6295::setFlags(const DivConfig& flags) { rateSelInit=flags.getBool("rateSel",false); + isBanked=flags.getBool("isBanked",false); switch (flags.getInt("clockSel",0)) { case 1: chipClock=4224000/4; @@ -445,6 +524,9 @@ void DivPlatformMSM6295::setFlags(const DivConfig& flags) { case 14: chipClock=COLOR_NTSC/3.0; break; + case 15: + chipClock=3200000; + break; default: chipClock=4000000/4; break; @@ -458,11 +540,12 @@ void DivPlatformMSM6295::setFlags(const DivConfig& flags) { rWrite(12,!rateSelInit); rateSel=rateSelInit; } + rWrite(14,isBanked?0x81:0); } int DivPlatformMSM6295::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) { parent=p; - adpcmMem=new unsigned char[getSampleMemCapacity(0)]; + adpcmMem=new unsigned char[16777216]; adpcmMemLen=0; dumpWrites=false; skipRegisterWrites=false; diff --git a/src/engine/platform/msm6295.h b/src/engine/platform/msm6295.h index b7ebf5bf90..51e052a815 100644 --- a/src/engine/platform/msm6295.h +++ b/src/engine/platform/msm6295.h @@ -49,7 +49,6 @@ class DivPlatformMSM6295: public DivDispatch, public vgsound_emu_mem_intf { }; FixedQueue writes; msm6295_core msm; - unsigned char lastBusy; unsigned char* adpcmMem; size_t adpcmMemLen; @@ -58,7 +57,18 @@ class DivPlatformMSM6295: public DivDispatch, public vgsound_emu_mem_intf { int delay, updateOsc; - bool rateSel=false, rateSelInit=false; + bool rateSel=false, rateSelInit=false, isBanked=false; + + unsigned int bank[4]; + struct BankedPhrase { + unsigned char bank=0; + unsigned char phrase=0; + unsigned int length=0; + BankedPhrase(): + bank(0), + phrase(0), + length(0) {} + } bankedPhrase[256]; friend void putDispatchChip(void*,int); friend void putDispatchChan(void*,int,int); diff --git a/src/gui/debug.cpp b/src/gui/debug.cpp index 6eceb12a18..807b32ed1c 100644 --- a/src/gui/debug.cpp +++ b/src/gui/debug.cpp @@ -55,6 +55,7 @@ #include "../engine/platform/pv1000.h" #include "../engine/platform/k053260.h" #include "../engine/platform/c140.h" +#include "../engine/platform/msm6295.h" #include "../engine/platform/dummy.h" #define COMMON_CHIP_DEBUG \ diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index 0c88946154..c5d087de5e 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -1575,6 +1575,89 @@ void FurnaceGUI::initSystemPresets() { ) // ^^ } ); + ENTRY( + "NMK 16-bit Arcade (w/NMK112 bankswitching)", { + CH(DIV_SYSTEM_YM2203, 1.0f, 0, "clockSel=5"), // 1.5MHz; optional + CH(DIV_SYSTEM_MSM6295, 1.0f, 0, + "clockSel=2\n" + "rateSel=true\n" + "isBanked=true\n" + ), // 4MHz + CH(DIV_SYSTEM_MSM6295, 1.0f, 0, + "clockSel=2\n" + "rateSel=true\n" + "isBanked=true\n" + ) // ^^ + } + ); + ENTRY( + "NMK 16-bit Arcade (w/NMK112 bankswitching, extended channel 3)", { + CH(DIV_SYSTEM_YM2203_EXT, 1.0f, 0, "clockSel=5"), // 1.5MHz; optional + CH(DIV_SYSTEM_MSM6295, 1.0f, 0, + "clockSel=2\n" + "rateSel=true\n" + "isBanked=true\n" + ), // 4MHz + CH(DIV_SYSTEM_MSM6295, 1.0f, 0, + "clockSel=2\n" + "rateSel=true\n" + "isBanked=true\n" + ) // ^^ + } + ); + ENTRY( + "Atlus Power Instinct 2", { + CH(DIV_SYSTEM_YM2203, 1.0f, 0, "clockSel=2"), // 4MHz + CH(DIV_SYSTEM_MSM6295, 1.0f, 0, + "clockSel=13\n" + "rateSel=true\n" + "isBanked=true\n" + ), // 3MHz + CH(DIV_SYSTEM_MSM6295, 1.0f, 0, + "clockSel=13\n" + "rateSel=true\n" + "isBanked=true\n" + ) // ^^ + } + ); + ENTRY( + "Atlus Power Instinct 2 (extended channel 3)", { + CH(DIV_SYSTEM_YM2203_EXT, 1.0f, 0, "clockSel=2"), // 4MHz + CH(DIV_SYSTEM_MSM6295, 1.0f, 0, + "clockSel=13\n" + "rateSel=true\n" + "isBanked=true\n" + ), // 3MHz + CH(DIV_SYSTEM_MSM6295, 1.0f, 0, + "clockSel=13\n" + "rateSel=true\n" + "isBanked=true\n" + ) // ^^ + } + ); + ENTRY( + "Raizing/Eighting Battle Garegga", { + CH(DIV_SYSTEM_YM2151, 1.0f, 0, "clockSel=2"), // 4MHz + CH(DIV_SYSTEM_MSM6295, 1.0f, 0, + "clockSel=8\n" + "isBanked=true\n" + ) // 2MHz + } + ); + ENTRY( + "Raizing/Eighting Batrider", { + CH(DIV_SYSTEM_YM2151, 1.0f, 0, "clockSel=2"), // 4MHz + CH(DIV_SYSTEM_MSM6295, 1.0f, 0, + "clockSel=15\n" + "isBanked=true\n" + ), // 3.2MHz + CH(DIV_SYSTEM_MSM6295, 1.0f, 0, + "clockSel=15\n" + "rateSel=true\n" + "isBanked=true\n" + ) // 3.2MHz + } + ); ENTRY( "Kaneko DJ Boy", { CH(DIV_SYSTEM_YM2203, 1.0f, 0, "clockSel=3"), // 3MHz diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index d00852c8b9..6a3a666ee0 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -376,6 +376,9 @@ void FurnaceGUI::drawSampleEdit() { if (sample->loop) { SAMPLE_WARN(warnLoop,"MSM6295: samples can't loop"); } + if (sample->samples>129024) { + SAMPLE_WARN(warnLength,"MSM6295: maximum bankswitched sample length is 129024"); + } default: break; } diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 86b478ca83..be0609c98c 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -1471,6 +1471,7 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl case DIV_SYSTEM_MSM6295: { int clockSel=flags.getInt("clockSel",0); bool rateSel=flags.getBool("rateSel",false); + bool isBanked=flags.getBool("isBanked",false); ImGui::Text("Clock rate:"); ImGui::Indent(); @@ -1522,6 +1523,10 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl clockSel=13; altered=true; } + if (ImGui::RadioButton("3.2MHz",clockSel==15)) { + clockSel=15; + altered=true; + } if (ImGui::RadioButton("3.58MHz",clockSel==4)) { clockSel=4; altered=true; @@ -1547,10 +1552,15 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl } ImGui::Unindent(); + if (ImGui::Checkbox("Bankswitched (NMK112)",&isBanked)) { + altered=true; + } + if (altered) { e->lockSave([&]() { flags.set("clockSel",clockSel); flags.set("rateSel",rateSel); + flags.set("isBanked",isBanked); }); } break;