From 9ff74396271564a2786f9df633138e121e361a75 Mon Sep 17 00:00:00 2001 From: Natt Akuma Date: Fri, 26 Sep 2025 23:30:51 +0700 Subject: [PATCH] Kurumitsu-8L: channel redesign, fix export --- src/engine/platform/kurumitsu8l.cpp | 214 ++++++++++++++-------------- src/engine/platform/kurumitsu8l.h | 2 +- src/engine/vgmOps.cpp | 4 +- 3 files changed, 113 insertions(+), 107 deletions(-) diff --git a/src/engine/platform/kurumitsu8l.cpp b/src/engine/platform/kurumitsu8l.cpp index 2ed78cc64..b2665ad53 100644 --- a/src/engine/platform/kurumitsu8l.cpp +++ b/src/engine/platform/kurumitsu8l.cpp @@ -21,24 +21,25 @@ #include "../engine.h" #include -#define chRead(c,a) regPool[(c)*24+(a)] -#define chWrite(c,a,v) if (!skipRegisterWrites) {regPool[(c)*24+(a)]=v; if (dumpWrites) {addWrite(a,v);} } -#define chDWrite(c,a,v) regPool[(c)*24+(a)]=v; +#define chRead(c,a) regPool[(c)*32+(a)] +#define chWrite(c,a,v) if (!skipRegisterWrites) {regPool[(c)*32+(a)]=v; if (dumpWrites) {addWrite((c)*32+(a),v);} } +#define chDWrite(c,a,v) regPool[(c)*32+(a)]=v; #define CHIP_FREQBASE 67108864 const char* regCheatSheetKurumitsu8L[]={ - "CHxBank", "00+x*18", - "CHxVol", "01+x*18", - "CHxModF", "02+x*18", - "CHxModI", "03+x*18", - "CHxPanL", "04+x*18", - "CHxPanR", "06+x*18", - "CHxLoopE", "08+x*18", - "CHxLoopL", "0A+x*18", - "CHxFreq", "0C+x*18", - "CHxAcc", "10+x*18", - "CHxOut", "14+x*18", + "CHxWPtr", "00+x*20", + "CHxSPtr", "04+x*20", + "CHxVol", "09+x*20", + "CHxModF", "0A+x*20", + "CHxModI", "0B+x*20", + "CHxPanL", "0C+x*20", + "CHxPanR", "0E+x*20", + "CHxLoopE", "10+x*20", + "CHxLoopL", "12+x*20", + "CHxFreq", "14+x*20", + "CHxAcc", "18+x*20", + "CHxOut", "1C+x*20", NULL }; @@ -63,16 +64,16 @@ void DivPlatformKurumitsu8L::acquire(short** buf, size_t len) { int outAR=0; int modData=0; for (int i=0; i<8; i++) { - unsigned char vol=chRead(i,1); - unsigned char fb=chRead(i,2); - unsigned char mod=chRead(i,3); - short panL=chRead(i,4)|((short)chRead(i,5)<<8); - short panR=chRead(i,6)|((short)chRead(i,7)<<8); - unsigned freq=chRead(i,12)|((unsigned)chRead(i,13)<<8)|((unsigned)chRead(i,14)<<16)|((unsigned)chRead(i,15)<<24); - unsigned phase=chRead(i,16)|((unsigned)chRead(i,17)<<8)|((unsigned)chRead(i,18)<<16)|((unsigned)chRead(i,19)<<24); - unsigned loopE=chRead(i,8)|((unsigned)chRead(i,9)<<8); - short fbData1=chRead(i,20)|((short)chRead(i,21)<<8); - short fbData2=chRead(i,22)|((short)chRead(i,23)<<8); + unsigned char vol=chRead(i,9); + unsigned char fb=chRead(i,10); + unsigned char mod=chRead(i,11); + short panL=chRead(i,12)|((short)chRead(i,13)<<8); + short panR=chRead(i,14)|((short)chRead(i,15)<<8); + unsigned freq=chRead(i,20)|((unsigned)chRead(i,21)<<8)|((unsigned)chRead(i,22)<<16)|((unsigned)chRead(i,23)<<24); + unsigned phase=chRead(i,24)|((unsigned)chRead(i,25)<<8)|((unsigned)chRead(i,26)<<16)|((unsigned)chRead(i,27)<<24); + unsigned loopE=chRead(i,18)|((unsigned)chRead(i,19)<<8); + short fbData1=chRead(i,28)|((short)chRead(i,29)<<8); + short fbData2=chRead(i,30)|((short)chRead(i,31)<<8); int data=0; int phase_inc=(((int)fbData1+fbData2)*fb)+((modData*mod)<<3); @@ -82,32 +83,35 @@ void DivPlatformKurumitsu8L::acquire(short** buf, size_t len) { phase+=freq; } else { // samples, does unstable pitchmod - unsigned loopL=chRead(i,10)|((unsigned)chRead(i,11)<<8); - data=sampleMem[(phase>>16)|(chRead(i,0)<<16)]; - phase_inc+=freq; + unsigned base=chRead(i,4)|((unsigned)chRead(i,5)<<8)|((unsigned)chRead(i,6)<<16)|((unsigned)chRead(i,7)<<24); + unsigned loopL=chRead(i,16)|((unsigned)chRead(i,17)<<8); + data=sampleMem[base+(phase>>16)]; + phase_inc=(phase_inc>>9)+freq; + // ROM symphony prevention if (phase_inc<0) phase_inc=0; + if ((unsigned)phase_inc>(loopL<<16)) phase_inc=loopL<<16; int64_t newPhase=phase+phase_inc; - if (newPhase>=0x100000000LL || (newPhase)>=(loopE<<16)) { + if (newPhase>=0x100000000LL || newPhase>=(loopE<<16)) { newPhase-=loopL<<16; } phase=newPhase; } int out=data*vol; // 16 bits - outAL+=(out*panL)>>2; // 29 bits - outAR+=(out*panR)>>2; - oscBuf[i]->putSample(h,(out*((int)abs(panL)+abs(panR)))>>17); - fbData2=fbData1; fbData1=out; modData=out; - chDWrite(i,16,phase&0xff); - chDWrite(i,17,(phase>>8)&0xff); - chDWrite(i,18,(phase>>16)&0xff); - chDWrite(i,19,(phase>>24)&0xff); - chDWrite(i,20,fbData1&0xff); - chDWrite(i,21,(fbData1>>8)&0xff); - chDWrite(i,22,fbData2&0xff); - chDWrite(i,23,(fbData2>>24)&0xff); + outAL+=out*panL; // 29 bits + outAR+=out*panR; + oscBuf[i]->putSample(h,(out*((int)abs(panL)+abs(panR)))>>15); + + chDWrite(i,24,phase&0xff); + chDWrite(i,25,(phase>>8)&0xff); + chDWrite(i,26,(phase>>16)&0xff); + chDWrite(i,27,(phase>>24)&0xff); + chDWrite(i,28,fbData1&0xff); + chDWrite(i,29,(fbData1>>8)&0xff); + chDWrite(i,30,fbData2&0xff); + chDWrite(i,31,(fbData2>>24)&0xff); } // 32 bits buf[0][h]=outAL>>16; // 16 bits buf[1][h]=outAR>>16; @@ -122,21 +126,24 @@ void DivPlatformKurumitsu8L::updateWave(int ch) { for (int i=0; i<256; i++) { wtMem[ch][i]=chan[ch].ws.output[i]^128; } + if (!skipRegisterWrites && dumpWrites) { + addWrite(0x10000+ch*256,256); + } } void DivPlatformKurumitsu8L::writePan(int ch) { if (!isMuted[ch] && chan[ch].active) { - int valL=chan[ch].panL*chan[ch].vol*(chan[ch].invertL?-1:1)/2; - int valR=chan[ch].panR*chan[ch].vol*(chan[ch].invertR?-1:1)/2; - chWrite(ch,4,valL&0xff); - chWrite(ch,5,valL>>8); - chWrite(ch,6,valR&0xff); - chWrite(ch,7,valR>>8); + int valL=chan[ch].panL*chan[ch].vol*(chan[ch].invertL?-1:1)/8; + int valR=chan[ch].panR*chan[ch].vol*(chan[ch].invertR?-1:1)/8; + chWrite(ch,12,valL&0xff); + chWrite(ch,13,valL>>8); + chWrite(ch,14,valR&0xff); + chWrite(ch,15,valR>>8); } else { - chWrite(ch,4,0); - chWrite(ch,5,0); - chWrite(ch,6,0); - chWrite(ch,7,0); + chWrite(ch,12,0); + chWrite(ch,13,0); + chWrite(ch,14,0); + chWrite(ch,15,0); } } @@ -148,7 +155,7 @@ void DivPlatformKurumitsu8L::tick(bool sysTick) { chan[i].outVol=chan[i].std.vol.val*(chan[i].isAmiga?4:1); if (chan[i].outVol>255) chan[i].outVol=255; if (!isMuted[i] && chan[i].active) { - chWrite(i,1,chan[i].outVol); + chWrite(i,9,chan[i].outVol); } } if (NEW_ARP_STRAT) { @@ -189,18 +196,18 @@ void DivPlatformKurumitsu8L::tick(bool sysTick) { chan[i].audPos=0; chan[i].setPos=true; } else { - chWrite(i,16,0); - chWrite(i,17,0); - chWrite(i,18,0); - chWrite(i,19,0); + chWrite(i,24,0); + chWrite(i,25,0); + chWrite(i,26,0); + chWrite(i,27,0); } } } if (chan[i].std.ex1.had) { - chWrite(i,2,chan[i].std.ex1.val); + chWrite(i,10,chan[i].std.ex1.val); } if (chan[i].std.ex2.had) { - chWrite(i,3,chan[i].std.ex2.val); + chWrite(i,11,chan[i].std.ex2.val); } if (chan[i].std.ex3.had) { chan[i].invertL=chan[i].std.ex3.val&2; @@ -228,9 +235,9 @@ void DivPlatformKurumitsu8L::tick(bool sysTick) { } if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>8388608) chan[i].freq=8388608; - chWrite(i,12,chan[i].freq&0xff); - chWrite(i,13,(chan[i].freq>>8)&0xff); - chWrite(i,14,(chan[i].freq>>16)&0xff); + chWrite(i,20,chan[i].freq&0xff); + chWrite(i,21,(chan[i].freq>>8)&0xff); + chWrite(i,22,(chan[i].freq>>16)&0xff); chan[i].freqChanged=false; } if (chan[i].keyOn || chan[i].keyOff) { @@ -238,40 +245,39 @@ void DivPlatformKurumitsu8L::tick(bool sysTick) { chan[i].outVol=255; } if (!isMuted[i] && chan[i].active) { - chWrite(i,1,chan[i].outVol); + chWrite(i,9,chan[i].outVol); } else { - chWrite(i,1,0); + chWrite(i,9,0); } panChanged=true; if (chan[i].keyOn) { chan[i].keyOn=false; if (chan[i].pcm) { DivSample* s=parent->getSample(chan[i].sample); - unsigned int start, length, loop; + unsigned int start, loopEnd, loopLength; start=sampleOff[chan[i].sample]; if (s->isLoopable()) { - length=s->loopEnd; - loop=s->loopEnd-s->loopStart; + loopEnd=s->loopEnd; + loopLength=s->loopEnd-s->loopStart; } else { - length=s->samples+16; - loop=16; + loopEnd=s->samples+16; + loopLength=16; } - if (length>65535) length=65535; - if (chan[i].audPos>0) { - start=start+MIN(chan[i].audPos,length); - } - chWrite(i,16,0); - chWrite(i,17,0); - chWrite(i,18,start&0xff); - chWrite(i,19,(start>>8)&0xff); - chWrite(i,0,start>>16); - chWrite(i,8,(start+length)&0xff); - chWrite(i,9,((start+length)>>8)&0xff); - chWrite(i,10,loop&0xff); - chWrite(i,11,(loop>>8)&0xff); + if (loopEnd>65535) loopEnd=65535; + chWrite(i,4,start&0xff); + chWrite(i,5,(start>>8)&0xff); + chWrite(i,6,(start>>16)&0xff); + chWrite(i,16,loopLength&0xff); + chWrite(i,17,(loopLength>>8)&0xff); + chWrite(i,18,loopEnd&0xff); + chWrite(i,19,(loopEnd>>8)&0xff); + chWrite(i,24,0); + chWrite(i,25,0); + chWrite(i,26,chan[i].audPos); + chWrite(i,27,(chan[i].audPos>>8)&0xff); } else { - chWrite(i,8,0); - chWrite(i,9,0); + chWrite(i,18,0); + chWrite(i,19,0); } } if (chan[i].keyOff) chan[i].keyOff=false; @@ -310,7 +316,7 @@ int DivPlatformKurumitsu8L::dispatch(DivCommand c) { if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) { chan[c.chan].outVol=255; if (!isMuted[c.chan] && chan[c.chan].active) { - chWrite(c.chan,1,chan[c.chan].outVol); + chWrite(c.chan,9,chan[c.chan].outVol); } } chan[c.chan].insChanged=false; @@ -401,11 +407,11 @@ int DivPlatformKurumitsu8L::dispatch(DivCommand c) { chan[c.chan].setPos=true; break; case DIV_CMD_FM_FB: { - chWrite(c.chan,2,c.value); + chWrite(c.chan,10,c.value); break; } case DIV_CMD_FM_PM_DEPTH: { - chWrite(c.chan,3,c.value); + chWrite(c.chan,11,c.value); break; } case DIV_CMD_GET_VOLMAX: @@ -429,19 +435,21 @@ int DivPlatformKurumitsu8L::dispatch(DivCommand c) { void DivPlatformKurumitsu8L::muteChannel(int ch, bool mute) { isMuted[ch]=mute; if (!mute && chan[ch].active) { - chWrite(ch,1,chan[ch].outVol); + chWrite(ch,9,chan[ch].outVol); writePan(ch); } else { - chWrite(ch,1,0); + chWrite(ch,9,0); } } void DivPlatformKurumitsu8L::forceIns() { for (int i=0; i<8; i++) { - chan[i].sample=-1; chan[i].insChanged=true; chan[i].freqChanged=true; + chan[i].audPos=0; + chan[i].sample=-1; updateWave(i); + writePan(i); } } @@ -454,14 +462,14 @@ DivMacroInt* DivPlatformKurumitsu8L::getChanMacroInt(int ch) { } unsigned short DivPlatformKurumitsu8L::getPan(int ch) { - short panL=abs(chRead(ch,4)|((short)chRead(ch,5)<<8)); - short panR=abs(chRead(ch,6)|((short)chRead(ch,7)<<8)); + short panL=abs(chRead(ch,12)|((short)chRead(ch,13)<<8)); + short panR=abs(chRead(ch,14)|((short)chRead(ch,15)<<8)); return ((panL/128)<<8)|(panR/128); } void DivPlatformKurumitsu8L::getPaired(int ch, std::vector& ret) { if (ch==0) return; - unsigned char mod=chRead(ch,3); + unsigned char mod=chRead(ch,11); if (mod!=0) { sprintf(modLabel[ch],"%d",mod); ret.push_back(DivChannelPair(modLabel[ch],ch-1)); @@ -490,7 +498,7 @@ unsigned char* DivPlatformKurumitsu8L::getRegisterPool() { } int DivPlatformKurumitsu8L::getRegisterPoolSize() { - return 24*8; + return 32*8; } const void* DivPlatformKurumitsu8L::getSampleMem(int index) { @@ -534,24 +542,22 @@ void DivPlatformKurumitsu8L::renderSamples(int sysID) { sampleOff[i]=0; continue; } + if (memPos>=maxPos) { + logW("out of Kurumitsu-8L PCM memory for sample %d!",i); + break; + } int length=s->isLoopable()?s->loopEnd:s->samples; int actualLength=length; // if it's one-shot, add 16 silent samples for looping area // this should be enough for most cases even though the // frequency register can make position jump by up to 256 samples - int oneShotPos=length; + int oneShotLength=length; if (!s->isLoopable()) actualLength+=16; if (actualLength>65535) actualLength=65535; - if (!s->isLoopable()) oneShotPos=actualLength-16; - if (0x10000-(memPos&0xffff)(maxPos-memPos)) actualLength=maxPos-memPos; + if (!s->isLoopable()) oneShotLength=actualLength-16; sampleOff[i]=memPos; - memcpy(&sampleMem[memPos],s->data8,oneShotPos); + memcpy(&sampleMem[memPos],s->data8,oneShotLength); memPos+=actualLength; romMemCompo.entries.push_back(DivMemoryEntry(DIV_MEMORY_SAMPLE,"PCM",i,sampleOff[i],memPos)); sampleLoaded[i]=true; @@ -607,12 +613,12 @@ void DivPlatformKurumitsu8L::setFlags(const DivConfig& flags) { } void DivPlatformKurumitsu8L::poke(unsigned int addr, unsigned short val) { - if (addr < (unsigned int)getRegisterPoolSize()) chWrite(addr/24,addr%24,val); + if (addr < (unsigned int)getRegisterPoolSize()) chWrite(addr/32,addr%32,val); } void DivPlatformKurumitsu8L::poke(std::vector& wlist) { for (DivRegWrite& i: wlist) { - if (i.addr < (unsigned int)getRegisterPoolSize()) chWrite(i.addr/24,i.addr%24,i.val); + if (i.addr < (unsigned int)getRegisterPoolSize()) chWrite(i.addr/32,i.addr%32,i.val); } } diff --git a/src/engine/platform/kurumitsu8l.h b/src/engine/platform/kurumitsu8l.h index a605919e5..9ddae0080 100644 --- a/src/engine/platform/kurumitsu8l.h +++ b/src/engine/platform/kurumitsu8l.h @@ -51,7 +51,7 @@ class DivPlatformKurumitsu8L: public DivDispatch { unsigned int sampleOff[256]; bool sampleLoaded[256]; - unsigned char regPool[8*24]; + unsigned char regPool[8*32]; signed char wtMem[8][256]; char modLabel[8][4]; signed char* sampleMem; diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp index cc48401cb..11769e600 100644 --- a/src/engine/vgmOps.cpp +++ b/src/engine/vgmOps.cpp @@ -1244,12 +1244,12 @@ void DivEngine::performVGMWrite(SafeWriter* w, int disIdx, DivSystem sys, DivReg w->writeC(0x66); w->writeC(0xc0); w->writeI(write.val+2); - w->writeI(write.addr&0xffff); + w->writeS(write.addr&0xffff); w->write(&wtMem[write.addr&0xffff],write.val); } else { // register writes w->writeC(0xa0); - w->writeC(baseAddr2|(write.addr&0xff)); + w->writeC(write.addr&0xff); w->writeC(write.val); } break;