diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index b073e423e..4e5357722 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -155,43 +155,76 @@ void DivPlatformAY8910::runDAC() { } void DivPlatformAY8910::runTFX() { - if (selCore) return; + /* + developer's note: if you are checking for intellivision + make sure to add "&& selCore" + because for some reason, the register remap doesn't work + when the user uses AtomicSSG core + */ int timerPeriod, output; for (int i=0; i<3; i++) { if (chan[i].active && (chan[i].curPSGMode.val&16) && !(chan[i].curPSGMode.val&8) && chan[i].tfx.mode!=-1) { + if (chan[i].tfx.mode == -1 && !isMuted[i]) { + /* + bug: if in the timer FX macro the user enables + and then disables PWM while there is no volume macro + there is now a random chance that the resulting output + is silent or has volume set incorrectly + i've tried to implement a fix, but it seems to be + ineffective, so... + TODO: actually implement a proper fix + */ + if (intellivision && chan[i].curPSGMode.getEnvelope()) { + immWrite(0x08+i,(chan[i].outVol&0xc)<<2); + continue; + } else { + immWrite(0x08+i,(chan[i].outVol&15)|((chan[i].curPSGMode.getEnvelope())<<2)); + continue; + } + } chan[i].tfx.counter += 1; if (chan[i].tfx.counter >= chan[i].tfx.period && chan[i].tfx.mode == 0) { chan[i].tfx.counter = 0; chan[i].tfx.out ^= 1; - output = MAX(0, ((chan[i].tfx.out) ? (chan[i].outVol&15) : (chan[i].tfx.lowBound-(15-chan[i].outVol)))); - output &= 15; + output = ((chan[i].tfx.out) ? chan[i].outVol : (chan[i].tfx.lowBound-(15-chan[i].outVol))); + // TODO: fix this stupid crackling noise that happens + // everytime the volume changes + output = (output <= 0) ? 0 : output; // underflow + output = (output >= 15) ? 15 : output; // overflow + output &= 15; // i don't know if i need this but i'm too scared to remove it if (!isMuted[i]) { - immWrite(0x08+i,output|(chan[i].curPSGMode.getEnvelope()<<2)); + if (intellivision && selCore) { + immWrite(0x0b+i,(output&0xc)<<2); + } else { + immWrite(0x08+i,output|(chan[i].curPSGMode.getEnvelope()<<2)); + } } } if (chan[i].tfx.counter >= chan[i].tfx.period && chan[i].tfx.mode == 1) { chan[i].tfx.counter = 0; if (!isMuted[i]) { - immWrite(0xd, ayEnvMode); + if (intellivision && selCore) { + immWrite(0xa, ayEnvMode); + } else { + immWrite(0xd, ayEnvMode); + } } } if (chan[i].tfx.counter >= chan[i].tfx.period && chan[i].tfx.mode == 2) { chan[i].tfx.counter = 0; } - if (chan[i].tfx.mode == -1 && !isMuted[i]) { - if (intellivision && chan[i].curPSGMode.getEnvelope()) { - immWrite(0x08+i,(chan[i].outVol&0xc)<<2); - } else { - immWrite(0x08+i,(chan[i].outVol&15)|((chan[i].curPSGMode.getEnvelope())<<2)); - } - } } if (chan[i].tfx.num > 0) { - timerPeriod = chan[i].freq*chan[i].tfx.den/chan[i].tfx.num; - } else { - timerPeriod = chan[i].freq*chan[i].tfx.den; - } + timerPeriod = chan[i].freq*chan[i].tfx.den/chan[i].tfx.num; + } else { + timerPeriod = chan[i].freq*chan[i].tfx.den; + } if (chan[i].tfx.num > 0 && chan[i].tfx.den > 0) chan[i].tfx.period=timerPeriod+chan[i].tfx.offset; + // stupid pitch correction because: + // YM2149 half-clock and Sunsoft 5B: timers run an octave too high + // on AtomicSSG core timers run 2 octaves too high + if (clockSel || sunsoft) chan[i].tfx.period = chan[i].tfx.period * 2; + if (selCore) chan[i].tfx.period = chan[i].tfx.period * 4; } } @@ -388,6 +421,7 @@ void DivPlatformAY8910::tick(bool sysTick) { if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { chan[i].tfx.counter = 0; + chan[i].tfx.out = 0; if (chan[i].nextPSGMode.val&8) { if (dumpWrites) addWrite(0xffff0002+(i<<8),0); if (chan[i].dac.sample<0 || chan[i].dac.sample>=parent->song.sampleLen) { @@ -726,12 +760,10 @@ int DivPlatformAY8910::dispatch(DivCommand c) { break; } case DIV_CMD_STD_NOISE_MODE: - if (c.value&0xf0 && !(chan[c.chan].nextPSGMode.val&8)) { - chan[c.chan].nextPSGMode.val|=16; - chan[c.chan].tfx.mode = (c.value&3); - } if (!(chan[c.chan].nextPSGMode.val&8)) { - if (c.value<16) { + chan[c.chan].nextPSGMode.val|=16; + chan[c.chan].tfx.mode=(((c.value&0xf0)>>4)&3)-1; + if ((c.value&15)<16) { chan[c.chan].nextPSGMode.val=(c.value+1)&7; chan[c.chan].nextPSGMode.val|=chan[c.chan].curPSGMode.val&16; if (chan[c.chan].active) { @@ -805,9 +837,16 @@ int DivPlatformAY8910::dispatch(DivCommand c) { updateOutSel(true); immWrite(14+(c.value?1:0),(c.value?portBVal:portAVal)); break; - case DIV_CMD_AY_AUTO_PWM: - chan[c.chan].tfx.offset=c.value; + case DIV_CMD_AY_NOISE_MASK_AND: + chan[c.chan].tfx.num=c.value>>4; + chan[c.chan].tfx.den=c.value&15; break; + case DIV_CMD_AY_AUTO_PWM: { + // best way i could find to do signed :/ + signed char signVal=c.value; + chan[c.chan].tfx.offset=signVal; + break; + } case DIV_CMD_SAMPLE_MODE: if (c.value>0) { chan[c.chan].nextPSGMode.val|=8; diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 093369fc2..a8e50461e 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -433,6 +433,7 @@ void DivEngine::registerSystems() { {0x25, {DIV_CMD_AY_ENVELOPE_SLIDE, _("25xx: Envelope slide up"), negEffectVal}}, {0x26, {DIV_CMD_AY_ENVELOPE_SLIDE, _("26xx: Envelope slide down")}}, {0x29, {DIV_CMD_AY_AUTO_ENVELOPE, _("29xy: Set auto-envelope (x: numerator; y: denominator)")}}, + {0x2c, {DIV_CMD_AY_AUTO_PWM, _("2Cxx: Set timer period offset (bit 7: sign)")}}, {0x2e, {DIV_CMD_AY_IO_WRITE, _("2Exx: Write to I/O port A"), constVal<0>, effectVal}}, {0x2f, {DIV_CMD_AY_IO_WRITE, _("2Fxx: Write to I/O port B"), constVal<1>, effectVal}}, }; diff --git a/src/gui/editControls.cpp b/src/gui/editControls.cpp index 3eba0298c..c492a8e17 100644 --- a/src/gui/editControls.cpp +++ b/src/gui/editControls.cpp @@ -130,7 +130,7 @@ const bool mobileButtonPersist[32]={ // page 1 false, false, - false, + true, false, true, true, diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index ebef2402d..99dbf26dc 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -7569,6 +7569,10 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc(_("Timer Num"),&ins->std.ex8Macro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc(_("Timer Den"),&ins->std.fmsMacro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc(_("PWM Boundary"),&ins->std.amsMacro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER])); + // workaround, because the gui will not set + // zoom or scroll if we're not in macros tab + ins->std.ex7Macro.vZoom=128; + ins->std.ex7Macro.vScroll=2048-64; drawMacros(macroList,macroEditStateMacros); ImGui::EndTabItem(); }