From 4ed40d37d6c488ac17f5aeb7b92f72b25d356fa8 Mon Sep 17 00:00:00 2001 From: cam900 Date: Wed, 27 Aug 2025 21:02:51 +0900 Subject: [PATCH 1/9] Add sample limit in OPL4 PCM, Reduce duplicate it has 512 (if header at 0x000000) or 128 (otherwise; first 384 sample is from bottommost area (ex: YRW801 ROM) in this case) sample limits --- src/engine/platform/opl.cpp | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 762bde308..5a8f81a4e 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -37,6 +37,9 @@ #define PCM_CHECK(ch) ((chipType==4) && (ch>=pcmChanOffs)) #define PCM_REG(ch) (ch-pcmChanOffs) +// check if PCM in RAM (and size is <= 2MB) - 4MB uses whole sample area +#define PCM_IN_RAM (ramSize<=0x200000) + // N = invalid #define N 255 @@ -1466,7 +1469,7 @@ void DivPlatformOPL::tick(bool sysTick) { ctrl|=(chan[i].active?0x80:0)|(chan[i].damp?0x40:0)|(chan[i].lfoReset?0x20:0)|(chan[i].ch?0x10:0)|(isMuted[i]?8:(chan[i].pan&0xf)); int waveNum=chan[i].sample; if (waveNum>=0) { - if (ramSize<=0x200000) { + if (PCM_IN_RAM) { waveNum=CLAMP(waveNum,0,0x7f)|0x180; } if (chan[i].keyOn) { @@ -2941,7 +2944,7 @@ void DivPlatformOPL::reset() { if (chipType==4) { immWrite(0x105,3); // Reset wavetable header - immWrite(0x202,(ramSize<=0x200000)?0x10:0x00); + immWrite(0x202,PCM_IN_RAM?0x10:0x00); // initialize mixer volume fmMixL=7; fmMixR=7; @@ -3209,7 +3212,7 @@ void DivPlatformOPL::setFlags(const DivConfig& flags) { pcm.setClockFrequency(chipClock); rate=chipClock/768; chipRateBase=chipClock/684; - immWrite(0x202,(ramSize<=0x200000)?0x10:0x00); + immWrite(0x202,PCM_IN_RAM?0x10:0x00); break; case 759: rate=48000; @@ -3232,7 +3235,7 @@ const void* DivPlatformOPL::getSampleMem(int index) { size_t DivPlatformOPL::getSampleMemCapacity(int index) { return (index==0 && pcmChanOffs>=0)? - ((ramSize<=0x200000)?0x200000+ramSize:ramSize): + (PCM_IN_RAM?0x200000+ramSize:ramSize): ((index==0 && adpcmChan>=0)?262144:0); } @@ -3269,10 +3272,16 @@ void DivPlatformOPL::renderSamples(int sysID) { memCompo.name="Sample Memory"; if (pcmChanOffs>=0) { // OPL4 PCM - size_t memPos=((ramSize<=0x200000)?0x200600:0x1800); - const int maxSample=(ramSize<=0x200000)?127:511; + size_t memPos=(PCM_IN_RAM?0x200600:0x1800); + const int maxSample=PCM_IN_RAM?128:512; int sampleCount=parent->song.sampleLen; - if (sampleCount>maxSample) sampleCount=maxSample; + if (sampleCount>maxSample) { + // mark the rest as unavailable + for (int i=maxSample; isong.sample[i]; if (!s->renderOn[0][sysID]) { @@ -3335,7 +3344,7 @@ void DivPlatformOPL::renderSamples(int sysID) { // instrument table for (int i=0; isong.sample[i]; - unsigned int insAddr=(i*12)+((ramSize<=0x200000)?0x200000:0); + unsigned int insAddr=(i*12)+(PCM_IN_RAM?0x200000:0); unsigned char bitDepth; int endPos=CLAMP(s->isLoopable()?s->loopEnd:(s->samples+1),1,0x10000); int loop=s->isLoopable()?CLAMP(s->loopStart,0,endPos-2):(endPos-2); @@ -3366,12 +3375,12 @@ void DivPlatformOPL::renderSamples(int sysID) { pcmMem[6+insAddr]=(~(endPos-1))&0xff; // on MultiPCM this consists of instrument params, but on OPL4 this is not used pcmMem[7+insAddr]=0; // LFO, VIB - pcmMem[8+insAddr]=(0xf << 4) | (0xf << 0); // AR, D1R + pcmMem[8+insAddr]=(0xf<<4)|(0xf<<0); // AR, D1R pcmMem[9+insAddr]=0; // DL, D2R - pcmMem[10+insAddr]=(0xf << 4) | (0xf << 0); // RC, RR + pcmMem[10+insAddr]=(0xf<<4)|(0xf<<0); // RC, RR pcmMem[11+insAddr]=0; // AM } - if (ramSize<=0x200000) { + if (PCM_IN_RAM) { memCompo.entries.push_back(DivMemoryEntry(DIV_MEMORY_RESERVED,"ROM data",0,0,0x200000)); } From 779a8d8810845dc30e5bb9a78a581107ca6ef362 Mon Sep 17 00:00:00 2001 From: cam900 Date: Fri, 29 Aug 2025 16:55:53 +0900 Subject: [PATCH 2/9] Add loop end hints for NDS in ADPCM, 8bit PCM --- src/gui/sampleEdit.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 240d0c8bd..e71f7f6b1 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -2318,6 +2318,32 @@ void FurnaceGUI::drawSampleEdit() { } } } + if (displayLoopHintsNDSA) { + if (sampleZoom<0.5) { + for (int i=0; i<(int)(sampleZoom*avail.x); i++) { + if (((i+samplePos)&7)==0) { + ImVec2 p1=ImVec2(rectMin.x+((float)i/sampleZoom),rectMin.y); + ImVec2 p2=p1; + p2.y=rectMax.y; + + dl->AddLine(p1,p2,ImGui::GetColorU32(uiColors[GUI_COLOR_SAMPLE_LOOP_HINT])); + } + } + } + } + if (displayLoopHintsNDS8) { + if (sampleZoom<0.375) { + for (int i=0; i<(int)(sampleZoom*avail.x); i++) { + if (((i+samplePos)&3)==0) { + ImVec2 p1=ImVec2(rectMin.x+((float)i/sampleZoom),rectMin.y); + ImVec2 p2=p1; + p2.y=rectMax.y; + + dl->AddLine(p1,p2,ImGui::GetColorU32(uiColors[GUI_COLOR_SAMPLE_LOOP_HINT])); + } + } + } + } if (displayLoopHintsAmiga) { if (sampleZoom<0.25) { for (int i=0; i<(int)(sampleZoom*avail.x); i++) { From 91965bca8c1965ff13084072afaf6dc15a32af11 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 3 Sep 2025 03:58:45 -0500 Subject: [PATCH 3/9] fix crash when converting to BRR with invalid loop end issue #2671 --- src/engine/brrUtils.c | 2 +- src/engine/sample.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/engine/brrUtils.c b/src/engine/brrUtils.c index 38073fb4b..ff62e5641 100644 --- a/src/engine/brrUtils.c +++ b/src/engine/brrUtils.c @@ -256,7 +256,7 @@ long brrEncode(short* buf, unsigned char* out, long len, long loopStart, unsigne total+=9; } // encode loop block - if (loopStart>=0) { + if (loopStart>=0 && loopStart=len) { diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp index 2f7d50617..054d2bc4e 100644 --- a/src/engine/sample.cpp +++ b/src/engine/sample.cpp @@ -1479,7 +1479,8 @@ void DivSample::render(unsigned int formatMask) { } } if (NOT_IN_FORMAT(DIV_SAMPLE_DEPTH_BRR)) { // BRR - int sampleCount=loop?loopEnd:samples; + int sampleCount=isLoopable()?loopEnd:samples; + if (sampleCount>(int)samples) sampleCount=samples; if (!initInternal(DIV_SAMPLE_DEPTH_BRR,sampleCount)) return; brrEncode(data16,dataBRR,sampleCount,loop?loopStart:-1,brrEmphasis,brrNoFilter); } From dcf5f3f0c7b1e400e94f31b02562cd380613886b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 3 Sep 2025 04:01:47 -0500 Subject: [PATCH 4/9] fix stage 20 not being purple tanks --- src/gui/tutorial.cpp | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/gui/tutorial.cpp b/src/gui/tutorial.cpp index 9b238be85..f019d0d47 100644 --- a/src/gui/tutorial.cpp +++ b/src/gui/tutorial.cpp @@ -1313,7 +1313,7 @@ void FurnaceCV::buildStage(int which) { curStage=NULL; } - if (which>19 || which==4 || which==7 || which==9 || which==11 || which==13 || which==16 || which==17) { + if (which>18 || which==4 || which==7 || which==9 || which==11 || which==13 || which==16 || which==17) { stageWidth=80; stageHeight=56; } else { @@ -1346,26 +1346,7 @@ void FurnaceCV::buildStage(int which) { memset(busy,0,28*40*sizeof(bool)); // special stages - if ((which%10)==9) { - // vortex - for (int i=0; i<20+(which>>2); i++) { - int tries=0; - while (tries<20) { - int x=rand()%(stageWidth>>1); - int y=rand()%(stageHeight>>1); - int finalX=x<<4; - int finalY=y<<4; - if (busy[y][x]) { - tries++; - continue; - } - createObject(finalX,finalY); - createObject(finalX-4,finalY-4); - busy[y][x]=true; - break; - } - } - } else if ((which%10)==19) { + if ((which%10)==19) { for (int i=0; i<20+(which>>2); i++) { int tries=0; while (tries<20) { @@ -1387,6 +1368,25 @@ void FurnaceCV::buildStage(int which) { break; } } + } else if ((which%10)==9) { + // vortex + for (int i=0; i<20+(which>>2); i++) { + int tries=0; + while (tries<20) { + int x=rand()%(stageWidth>>1); + int y=rand()%(stageHeight>>1); + int finalX=x<<4; + int finalY=y<<4; + if (busy[y][x]) { + tries++; + continue; + } + createObject(finalX,finalY); + createObject(finalX-4,finalY-4); + busy[y][x]=true; + break; + } + } } else { // large if (which>=2) for (int i=0; i<(rand()%3)+which-2; i++) { @@ -1675,6 +1675,7 @@ void FurnaceCV::render(unsigned char joyIn) { lives+=lifeBank; respawnTime=1; lifeBank=0; + score=0; gameOver=false; } From 24e7338dc56ddbf83397cd1a5dffb48e97e110ce Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 4 Sep 2025 05:01:48 -0500 Subject: [PATCH 5/9] GUI: add visualizer to cmd stream player --- src/engine/cmdStream.cpp | 5 +++ src/engine/cmdStream.h | 3 +- src/gui/csPlayer.cpp | 71 ++++++++++++++++++++++++++++++++++++++++ src/gui/gui.h | 2 ++ 4 files changed, 80 insertions(+), 1 deletion(-) diff --git a/src/engine/cmdStream.cpp b/src/engine/cmdStream.cpp index f1605426e..21ed84b5c 100644 --- a/src/engine/cmdStream.cpp +++ b/src/engine/cmdStream.cpp @@ -682,6 +682,11 @@ bool DivCSPlayer::init() { chan[i].readPos=chan[i].startPos; } } + + // read stack sizes + for (unsigned int i=0; igetTotalChannelCount(); i++) { diff --git a/src/engine/cmdStream.h b/src/engine/cmdStream.h index 326134dbd..459a49d62 100644 --- a/src/engine/cmdStream.h +++ b/src/engine/cmdStream.h @@ -41,7 +41,7 @@ struct DivCSChannelState { unsigned char arp, arpStage, arpTicks; unsigned int callStack[DIV_MAX_CSSTACK]; - unsigned char callStackPos; + unsigned char callStackPos, callStackSize; unsigned int trace[DIV_MAX_CSTRACE]; unsigned char tracePos; @@ -67,6 +67,7 @@ struct DivCSChannelState { arpStage(0), arpTicks(0), callStackPos(0), + callStackSize(0), tracePos(0) { for (int i=0; igetDataLen()); + + ImGui::PushFont(patFont); + if (ImGui::BeginTable("CSHexPos",chans,ImGuiTableFlags_SizingStretchSame)) { + ImGui::TableNextRow(); + for (int i=0; igetChanState(i); + ImGui::TableNextColumn(); + ImGui::Text("$%.4x",state->readPos); + } + ImGui::EndTable(); + } + ImGui::PopFont(); + + if (csTex==NULL || !rend->isTextureValid(csTex)) { + logD("recreating command stream data texture."); + csTex=rend->createTexture(true,256,256,false,GUI_TEXFORMAT_ABGR32); + if (csTex==NULL) { + logE("error while creating command stream data texture! %s",SDL_GetError()); + } + } + if (csTex!=NULL) { + unsigned int* dataT=NULL; + int pitch=0; + if (!rend->lockTexture(csTex,(void**)&dataT,&pitch)) { + logE("error while locking command stream data texture! %s",SDL_GetError()); + } else { + unsigned short* accessTS=cs->getDataAccess(); + unsigned int csTick=cs->getCurTick(); + const float fadeTime=64.0f; + size_t bufSize=cs->getDataLen(); + if (bufSize>65536) bufSize=65536; + + for (size_t i=0; i0.0f) { + dataT[i]=ImGui::GetColorU32(ImGuiCol_HeaderActive,cellAlpha); + } else { + dataT[i]=0; + } + } + for (size_t i=bufSize; i<65536; i++) { + dataT[i]=0; + } + + for (int i=0; igetTotalChannelCount(); i++) { + unsigned int pos=cs->getChanState(i)->readPos; + if (pos<65536) { + ImVec4 col=ImVec4(1.0f,1.0f,1.0f,1.0f); + ImGui::ColorConvertHSVtoRGB((float)i/(float)e->getTotalChannelCount(),0.8f,1.0f,col.x,col.y,col.z); + dataT[pos]=ImGui::GetColorU32(col); + } + } + rend->unlockTexture(csTex); + } + + ImGui::Image(rend->getTextureID(csTex),ImVec2(768.0*dpiScale,768.0*dpiScale)); + } + ImGui::EndTabItem(); + } if (ImGui::BeginTabItem(_("Stream Info"))) { ImGui::Text("%d bytes",(int)cs->getDataLen()); ImGui::Text("%u channels",cs->getFileChans()); @@ -538,6 +604,11 @@ void FurnaceGUI::drawCSPlayer() { ImGui::SameLine(); ImGui::Text("%d",cs->getFastCmds()[i]); } + ImGui::Text("stack sizes:"); + for (unsigned int i=0; igetFileChans(); i++) { + ImGui::SameLine(); + ImGui::Text("%d",cs->getChanState(i)->callStackSize); + } ImGui::Text("ticks: %u",cs->getCurTick()); ImGui::EndTabItem(); } diff --git a/src/gui/gui.h b/src/gui/gui.h index cdea368d9..9473fa5bd 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1666,6 +1666,8 @@ class FurnaceGUI { int sampleTexW, sampleTexH; bool updateSampleTex; + FurnaceGUITexture* csTex; + String workingDir, fileName, clipboard, warnString, errorString, lastError, curFileName, nextFile, sysSearchQuery, newSongQuery, paletteQuery, sampleBankSearchQuery; String workingDirSong, workingDirIns, workingDirWave, workingDirSample, workingDirAudioExport; String workingDirVGMExport, workingDirROMExport; From 15d47cfe03b3f31cd8a5bd44ed4cd672e31869c4 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 4 Sep 2025 16:00:33 -0500 Subject: [PATCH 6/9] actually fix stage 20 comparison always was false --- src/gui/tutorial.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/tutorial.cpp b/src/gui/tutorial.cpp index f019d0d47..e06c52f6d 100644 --- a/src/gui/tutorial.cpp +++ b/src/gui/tutorial.cpp @@ -1346,7 +1346,7 @@ void FurnaceCV::buildStage(int which) { memset(busy,0,28*40*sizeof(bool)); // special stages - if ((which%10)==19) { + if ((which%20)==19) { for (int i=0; i<20+(which>>2); i++) { int tries=0; while (tries<20) { From d895a5724a2ad5209ad95c12ae56ef39429b107a Mon Sep 17 00:00:00 2001 From: freq-mod <32672779+freq-mod@users.noreply.github.com> Date: Thu, 4 Sep 2025 23:12:10 +0200 Subject: [PATCH 7/9] Clarify modulation description for two-tone mode --- doc/7-systems/pokey.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/7-systems/pokey.md b/doc/7-systems/pokey.md index 8bbb04b78..257b85d53 100644 --- a/doc/7-systems/pokey.md +++ b/doc/7-systems/pokey.md @@ -33,7 +33,7 @@ a sound and input chip developed by Atari for their 8-bit computers (Atari 400, - use for PWM effects (not automatic!). - bit 0: 15KHz mode. - `12xx`: **toggle two-tone mode.** - - when enabled, channel 2 modulates channel 1. I don't know how, but it does. + - when enabled, channel 2 modulates channel 1 in an oscillator sync-like manner. - only on ASAP core. ## info From 67c7afd4cdbaa9d9a63087ce6b4098a502138a83 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 5 Sep 2025 01:01:27 -0500 Subject: [PATCH 8/9] MMC5: fix env mode not set after reset/forceIns issue #2675 --- src/engine/platform/mmc5.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/engine/platform/mmc5.cpp b/src/engine/platform/mmc5.cpp index 672891bb4..bf8164012 100644 --- a/src/engine/platform/mmc5.cpp +++ b/src/engine/platform/mmc5.cpp @@ -377,6 +377,10 @@ void DivPlatformMMC5::forceIns() { for (int i=0; i<3; i++) { chan[i].insChanged=true; chan[i].prevFreq=65535; + if (i<2) { + // TODO: implement noise mode + rWrite(0x5000+i*4,(0x30)|(chan[i].active?chan[i].outVol:0)|((chan[i].duty&3)<<6)); + } } } @@ -429,6 +433,10 @@ void DivPlatformMMC5::reset() { rWrite(0x5015,0x03); rWrite(0x5010,0x00); + + for (int i=0; i<2; i++) { + rWrite(0x5000+i*4,(0x30)|0|((chan[i].duty&3)<<6)); + } } bool DivPlatformMMC5::keyOffAffectsArp(int ch) { From aa67f78d36f8705892d4071e8ceaec38dbaf3a95 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 5 Sep 2025 02:10:58 -0500 Subject: [PATCH 9/9] MMC5: fix typo in comment --- src/engine/platform/mmc5.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/platform/mmc5.cpp b/src/engine/platform/mmc5.cpp index bf8164012..fcb8dbdc6 100644 --- a/src/engine/platform/mmc5.cpp +++ b/src/engine/platform/mmc5.cpp @@ -378,7 +378,7 @@ void DivPlatformMMC5::forceIns() { chan[i].insChanged=true; chan[i].prevFreq=65535; if (i<2) { - // TODO: implement noise mode + // TODO: implement envelope mode rWrite(0x5000+i*4,(0x30)|(chan[i].active?chan[i].outVol:0)|((chan[i].duty&3)<<6)); } }