diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index 75d3a48bd..b073e423e 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -154,6 +154,47 @@ void DivPlatformAY8910::runDAC() { } } +void DivPlatformAY8910::runTFX() { + if (selCore) return; + 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) { + 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; + if (!isMuted[i]) { + 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 (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; + } + if (chan[i].tfx.num > 0 && chan[i].tfx.den > 0) chan[i].tfx.period=timerPeriod+chan[i].tfx.offset; + } +} + void DivPlatformAY8910::checkWrites() { while (!writes.empty()) { QueuedWrite w=writes.front(); @@ -181,6 +222,7 @@ void DivPlatformAY8910::acquire_mame(short** buf, size_t len) { if (sunsoft) { for (size_t i=0; isound_stream_update(ayBuf,1); @@ -194,6 +236,7 @@ void DivPlatformAY8910::acquire_mame(short** buf, size_t len) { } else { for (size_t i=0; isound_stream_update(ayBuf,1); @@ -215,6 +258,7 @@ void DivPlatformAY8910::acquire_mame(short** buf, size_t len) { void DivPlatformAY8910::acquire_atomic(short** buf, size_t len) { for (size_t i=0; i& stream, int sRate, size_t len) { + writes.clear(); + int rate=(int)(chipClock/sRate); + for (size_t i=0; i=parent->song.sampleLen) { @@ -369,6 +431,45 @@ void DivPlatformAY8910::tick(bool sysTick) { immWrite(0x0b,ayEnvPeriod); immWrite(0x0c,ayEnvPeriod>>8); } + if (chan[i].std.ex6.had) { + // 0 - disable timer + // 1 - pwm + // 2 - syncbuzzer + switch (chan[i].std.ex6.val) { + case 1: + chan[i].nextPSGMode.val|=16; + chan[i].tfx.mode = 0; + break; + case 2: + chan[i].nextPSGMode.val|=16; + chan[i].tfx.mode = 1; + break; + case 3: + chan[i].nextPSGMode.val|=16; + chan[i].tfx.mode = 2; + break; + default: + chan[i].nextPSGMode.val|=16; + chan[i].tfx.mode = -1; // this is a workaround! + break; + } + } + if (chan[i].std.ex7.had) { + chan[i].tfx.offset=chan[i].std.ex7.val; + } + if (chan[i].std.ex8.had) { + chan[i].tfx.num=chan[i].std.ex8.val; + chan[i].freqChanged=true; + if (!chan[i].std.fms.will) chan[i].tfx.den=1; + } + if (chan[i].std.fms.had) { + chan[i].tfx.den=chan[i].std.fms.val; + chan[i].freqChanged=true; + if (!chan[i].std.ex8.will) chan[i].tfx.num=1; + } + if (chan[i].std.ams.had) { + chan[i].tfx.lowBound=chan[i].std.ams.val; + } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER); if (chan[i].dac.furnaceDAC) { @@ -625,9 +726,14 @@ 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=(c.value+1)&7; + chan[c.chan].nextPSGMode.val|=chan[c.chan].curPSGMode.val&16; if (chan[c.chan].active) { chan[c.chan].curPSGMode.val=chan[c.chan].nextPSGMode.val; } @@ -699,6 +805,9 @@ 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; + break; case DIV_CMD_SAMPLE_MODE: if (c.value>0) { chan[c.chan].nextPSGMode.val|=8; diff --git a/src/engine/platform/ay.h b/src/engine/platform/ay.h index 8b64b5405..5bd805f19 100644 --- a/src/engine/platform/ay.h +++ b/src/engine/platform/ay.h @@ -34,6 +34,7 @@ class DivPlatformAY8910: public DivDispatch { inline unsigned char regRemap(unsigned char reg) { return intellivision?AY8914RegRemap[reg&0x0f]:reg&0x0f; } struct Channel: public SharedChannel { struct PSGMode { + // bit 4: timer FX // bit 3: DAC // bit 2: envelope // bit 1: noise @@ -52,6 +53,10 @@ class DivPlatformAY8910: public DivDispatch { return (val&8)?0:(val&4); } + unsigned char getTimerFX() { + return (val&8)?0:(val&16); + } + PSGMode(unsigned char v=1): val(v) {} }; @@ -72,6 +77,19 @@ class DivPlatformAY8910: public DivDispatch { setPos(false) {} } dac; + struct TFX { + int period, counter, offset, den, num, mode, lowBound, out; + TFX(): + period(0), + counter(0), + offset(1), + den(1), + num(1), + mode(0), + lowBound(0), + out(0) {} + } tfx; + unsigned char autoEnvNum, autoEnvDen; signed char konCycles; unsigned short fixedFreq; @@ -80,6 +98,7 @@ class DivPlatformAY8910: public DivDispatch { curPSGMode(PSGMode(0)), nextPSGMode(PSGMode(1)), dac(DAC()), + tfx(TFX()), autoEnvNum(0), autoEnvDen(0), konCycles(0), @@ -138,8 +157,10 @@ class DivPlatformAY8910: public DivDispatch { public: void runDAC(); + void runTFX(); void setExtClockDiv(unsigned int eclk=COLOR_NTSC, unsigned char ediv=8); void acquire(short** buf, size_t len); + void fillStream(std::vector& stream, int sRate, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 320b022f4..ebef2402d 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -7558,6 +7558,22 @@ void FurnaceGUI::drawInsEdit() { drawMacros(macroList,macroEditStateMacros); ImGui::EndTabItem(); } + if (ins->type==DIV_INS_AY) { + if (!ins->amiga.useSample) + { + if (ImGui::BeginTabItem(_("Timer Macros"))) + { + ImGui::Text(_("warning: timer effects are not supported by VGM export!")); + macroList.push_back(FurnaceGUIMacroDesc(_("Timer FX"),&ins->std.ex6Macro,0,3,64,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(_("TFX Offset"),&ins->std.ex7Macro,-2048,2047,160,uiColors[GUI_COLOR_MACRO_PITCH],true)); + 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])); + drawMacros(macroList,macroEditStateMacros); + ImGui::EndTabItem(); + } + } + } if (ins->type==DIV_INS_POWERNOISE || ins->type==DIV_INS_POWERNOISE_SLOPE) { if (ImGui::BeginTabItem("PowerNoise")) { int pnOctave=ins->powernoise.octave;