diff --git a/papers/format.md b/papers/format.md index ef8ab7e5..207b232a 100644 --- a/papers/format.md +++ b/papers/format.md @@ -29,6 +29,7 @@ furthermore, an `or reserved` indicates this field is always present, but is res the format versions are: +- 86: Furnace dev86 - 85: Furnace dev85 - 84: Furnace dev84 - 83: Furnace dev83 @@ -273,7 +274,8 @@ size | description 1 | E1xx and E2xx also take priority over Slide00 (>=83) or reserved 1 | new Sega PCM (with macros and proper vol/pan) (>=84) or reserved 1 | weird f-num/block-based chip pitch slides (>=85) or reserved - 21 | reserved + 1 | SN duty macro always resets phase (>=86) or reserved + 20 | reserved ``` # instrument diff --git a/src/engine/engine.h b/src/engine/engine.h index 427215df..3bd1db3d 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -43,8 +43,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev85" -#define DIV_ENGINE_VERSION 85 +#define DIV_VERSION "dev86" +#define DIV_ENGINE_VERSION 86 // for imports #define DIV_VERSION_MOD 0xff01 diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 48ef68bb..38b33502 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -163,6 +163,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { ds.ignoreDACModeOutsideIntendedChannel=false; ds.e1e2AlsoTakePriority=true; ds.fbPortaPause=true; + ds.snDutyReset=true; // 1.1 compat flags if (ds.version>24) { @@ -997,6 +998,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { if (ds.version<85) { ds.fbPortaPause=true; } + if (ds.version<86) { + ds.snDutyReset=true; + } ds.isDMF=false; reader.readS(); // reserved @@ -1351,7 +1355,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } else { reader.readC(); } - for (int i=0; i<21; i++) { + if (ds.version>=86) { + ds.snDutyReset=reader.readC(); + } else { + reader.readC(); + } + for (int i=0; i<20; i++) { reader.readC(); } } @@ -2293,7 +2302,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { w->writeC(song.e1e2AlsoTakePriority); w->writeC(song.newSegaPCM); w->writeC(song.fbPortaPause); - for (int i=0; i<21; i++) { + w->writeC(song.snDutyReset); + for (int i=0; i<20; i++) { w->writeC(0); } diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index af339f6e..c32548b5 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -86,11 +86,13 @@ void DivPlatformSMS::tick(bool sysTick) { } if (i==3) { if (chan[i].std.duty.had) { - snNoiseMode=chan[i].std.duty.val; - if (chan[i].std.duty.val<2) { - chan[3].freqChanged=false; + if (chan[i].std.duty.val!=snNoiseMode || parent->song.snDutyReset) { + snNoiseMode=chan[i].std.duty.val; + if (chan[i].std.duty.val<2) { + chan[3].freqChanged=false; + } + updateSNMode=true; } - updateSNMode=true; } if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { diff --git a/src/engine/song.h b/src/engine/song.h index 11264558..7c06a14a 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -325,6 +325,7 @@ struct DivSong { bool e1e2AlsoTakePriority; bool newSegaPCM; bool fbPortaPause; + bool snDutyReset; DivOrders orders; std::vector ins; @@ -429,7 +430,8 @@ struct DivSong { ignoreDACModeOutsideIntendedChannel(false), e1e2AlsoTakePriority(false), newSegaPCM(true), - fbPortaPause(false) { + fbPortaPause(false), + snDutyReset(false) { for (int i=0; i<32; i++) { system[i]=DIV_SYSTEM_NULL; systemVol[i]=64; diff --git a/src/gui/compatFlags.cpp b/src/gui/compatFlags.cpp index 8d1fd6d1..ccaeeb3f 100644 --- a/src/gui/compatFlags.cpp +++ b/src/gui/compatFlags.cpp @@ -117,6 +117,10 @@ void FurnaceGUI::drawCompatFlags() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("does this make any sense by now?"); } + ImGui::Checkbox("SN76489 duty macro always resets phase",&e->song.snDutyReset); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("when enabled, duty macro will always reset phase, even if its value hasn't changed."); + } ImGui::Text("Loop modality:"); if (ImGui::RadioButton("Reset channels",e->song.loopModality==0)) {