From 892ee12d915cc6acfb003d042fff4b20a36903b0 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 15 Mar 2024 20:16:29 -0500 Subject: [PATCH] implement YMF276-LLE core thanks LTVA --- extern/YMF276-LLE/fmopn2.c | 14 +- extern/YMF276-LLE/fmopn2.h | 3 + src/engine/platform/genesis.cpp | 318 +++++++++++++++++++++++++++++++- src/engine/platform/genesis.h | 8 + src/gui/settings.cpp | 4 +- 5 files changed, 337 insertions(+), 10 deletions(-) diff --git a/extern/YMF276-LLE/fmopn2.c b/extern/YMF276-LLE/fmopn2.c index 2a11366c1..546e8e4e3 100644 --- a/extern/YMF276-LLE/fmopn2.c +++ b/extern/YMF276-LLE/fmopn2.c @@ -2066,6 +2066,8 @@ void FMOPN2_YMF276Accumulator1(fmopn2_t *chip) int acc_l = 0; int acc_r = 0; + chip->osc_out = 0; + for (i = 0; i < 14; i++) accm += ((chip->ch_accm[i][1] >> 5) & 1) << i; if (chip->alg_output && !test_dac) @@ -2106,12 +2108,12 @@ void FMOPN2_YMF276Accumulator1(fmopn2_t *chip) chip->fsm_op1_sel_l3[0] = chip->fsm_op1_sel; if (sel_dac) - out |= chip->mode_dac_data[1] << 6; + chip->osc_out |= chip->mode_dac_data[1] << 6; if (sel_fm) - out |= accm; + chip->osc_out |= accm; - if (out & 0x2000) - out |= 0x1c000; + if (chip->osc_out & 0x2000) + chip->osc_out |= 0x1c000; for (i = 0; i < 2; i++) pan |= (((chip->chan_pan[i][1] >> 5) & 1) ^ 1) << i; @@ -2130,8 +2132,8 @@ void FMOPN2_YMF276Accumulator1(fmopn2_t *chip) acc_r = chip->ch_accm_r[1]; } - chip->ch_accm_l[0] = acc_l + ((pan & 2) != 0 ? out : 0); - chip->ch_accm_r[0] = acc_r + ((pan & 1) != 0 ? out : 0); + chip->ch_accm_l[0] = acc_l + ((pan & 2) != 0 ? chip->osc_out : 0); + chip->ch_accm_r[0] = acc_r + ((pan & 1) != 0 ? chip->osc_out : 0); } void FMOPN2_YMF276Accumulator2(fmopn2_t *chip) diff --git a/extern/YMF276-LLE/fmopn2.h b/extern/YMF276-LLE/fmopn2.h index f1ba12839..044859637 100644 --- a/extern/YMF276-LLE/fmopn2.h +++ b/extern/YMF276-LLE/fmopn2.h @@ -72,6 +72,9 @@ typedef struct { int out_l; int out_r; + // added by LTVA for Furnace tracker per-channel oscilloscopes! + int osc_out; + // io int write_addr_trig; int write_addr_trig_sync; diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index f59cf1975..fc82709ac 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -143,7 +143,7 @@ void DivPlatformGenesis::acquire_nuked(short** buf, size_t len) { if (!writes.empty()) { QueuedWrite& w=writes.front(); if (w.addrOrVal) { - //logV("%.3x = %.2x",w.addr,w.val); + //logV("%.3x=%.2x",w.addr,w.val); OPN2_Write(&fm,0x1+((w.addr>>8)<<1),w.val); regPool[w.addr&0x1ff]=w.val; writes.pop_front(); @@ -289,8 +289,279 @@ void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) { } } +const unsigned char chanMap276[6]={ + 1, 5, 3, 0, 4, 2 +}; + +// thanks LTVA +void DivPlatformGenesis::acquire276OscSub() { + if (fm_276.fsm_cnt2[1]==0 && llePrevCycle!=0) { + lleCycle=0; + } + + llePrevCycle=fm_276.fsm_cnt2[1]; + + if (fm_276.flags==fmopn2_flags_ym3438) { + lleOscData[lleCycle/(24*2)]+=fm_276.out_l+fm_276.out_r; + + lleCycle++; + + if (lleCycle==(144*2)) { + lleCycle=0; + + for (int i=0; i<6; i++) { + if ((softPCM && ((chanMap276[i]!=5) || !chan[5].dacMode)) || (!softPCM)) { + oscBuf[chanMap276[i]]->data[oscBuf[chanMap276[i]]->needle++]=lleOscData[i]; + } + lleOscData[i]=0; + } + + if (softPCM && chan[5].dacMode) { + oscBuf[5]->data[oscBuf[5]->needle++]=chan[5].dacOutput<<6; + oscBuf[6]->data[oscBuf[6]->needle++]=chan[6].dacOutput<<6; + } else { + oscBuf[6]->data[oscBuf[6]->needle++]=0; + } + } + } else { + for (int i=0; i<6; i++) { + if (lleCycle==((7+i)*12)) { + oscBuf[i]->data[oscBuf[i]->needle]=(fm_276.osc_out>>1)*8; + } + } + + lleCycle++; + + if (lleCycle==(144*2)) { + lleCycle=0; + + for (int i=0; i<6; i++) { + oscBuf[i]->data[oscBuf[i]->needle]>>=1; + } + + if (softPCM && chan[5].dacMode) { + oscBuf[5]->data[oscBuf[5]->needle]=chan[5].dacOutput<<6; + oscBuf[6]->data[oscBuf[6]->needle]=chan[6].dacOutput<<6; + } else { + oscBuf[6]->data[oscBuf[6]->needle]=0; + } + + for (int i=0; i<7; i++) { + oscBuf[i]->needle++; + } + } + } +} + +// thanks LTVA void DivPlatformGenesis::acquire_nuked276(short** buf, size_t len) { - // TODO + for (size_t h=0; h>8)<<1),w.val); + was_reg_write=true; + + fm_276.input.address=w.addr<0x100?0:2; + fm_276.input.data=w.addr&0xff; + fm_276.input.wr=1; + FMOPN2_Clock(&fm_276,0); + sum_l+=fm_276.out_l; + sum_r+=fm_276.out_r; + + acquire276OscSub(); + + fm_276.input.wr=0; + FMOPN2_Clock(&fm_276,1); + sum_l+=fm_276.out_l; + sum_r+=fm_276.out_r; + + acquire276OscSub(); + + if (chipType==2) { + if (!o_bco && fm_276.o_bco) { + dacShifter=(dacShifter<<1)|fm_276.o_so; + + if (o_lro!=fm_276.o_lro) { + if (o_lro) + sample_l=dacShifter; + else + sample_r=dacShifter; + } + + o_lro=fm_276.o_lro; + } + o_bco=fm_276.o_bco; + } + + for (int c=0; c<17; c++) { + FMOPN2_Clock(&fm_276,0); + sum_l+=fm_276.out_l; + sum_r+=fm_276.out_r; + + acquire276OscSub(); + + FMOPN2_Clock(&fm_276,1); + sum_l+=fm_276.out_l; + sum_r+=fm_276.out_r; + + acquire276OscSub(); + + if (chipType==2) { + if (!o_bco && fm_276.o_bco) { + dacShifter=(dacShifter<<1)|fm_276.o_so; + + if (o_lro!=fm_276.o_lro) { + if (o_lro) + sample_l=dacShifter; + else + sample_r=dacShifter; + } + + o_lro=fm_276.o_lro; + } + o_bco=fm_276.o_bco; + } + } + + fm_276.input.address=w.addr<0x100?1:3; + fm_276.input.data=w.val; + fm_276.input.wr=1; + FMOPN2_Clock(&fm_276,0); + sum_l+=fm_276.out_l; + sum_r+=fm_276.out_r; + fm_276.input.wr=0; + + acquire276OscSub(); + + FMOPN2_Clock(&fm_276,1); + sum_l+=fm_276.out_l; + sum_r+=fm_276.out_r; + + acquire276OscSub(); + + if (chipType==2) { + if (!o_bco && fm_276.o_bco) { + dacShifter=(dacShifter<<1)|fm_276.o_so; + + if (o_lro!=fm_276.o_lro) { + if (o_lro) + sample_l=dacShifter; + else + sample_r=dacShifter; + } + + o_lro=fm_276.o_lro; + } + o_bco=fm_276.o_bco; + } + + for (int c=0; c<83; c++) { + FMOPN2_Clock(&fm_276,0); + sum_l+=fm_276.out_l; + sum_r+=fm_276.out_r; + + acquire276OscSub(); + + FMOPN2_Clock(&fm_276,1); + sum_l+=fm_276.out_l; + sum_r+=fm_276.out_r; + + acquire276OscSub(); + + if (chipType==2) { + if (!o_bco && fm_276.o_bco) { + dacShifter=(dacShifter<<1)|fm_276.o_so; + + if (o_lro!=fm_276.o_lro) { + if (o_lro) { + sample_l=dacShifter; + } else { + sample_r=dacShifter; + } + } + + o_lro=fm_276.o_lro; + } + o_bco=fm_276.o_bco; + } + } + + regPool[w.addr&0x1ff]=w.val; + writes.pop_front(); + + if (dacWrite>=0) { + if (!canWriteDAC) { + canWriteDAC=true; + } else { + urgentWrite(0x2a,dacWrite); + dacWrite=-1; + canWriteDAC=writes.empty(); + } + } + } else { + w.addrOrVal=true; + } + } else { + canWriteDAC=true; + if (dacWrite>=0) { + urgentWrite(0x2a,dacWrite); + dacWrite=-1; + } + flushFirst=false; + } + + for (int j=0; j<(was_reg_write?(144-83-19):144); j++) { + FMOPN2_Clock(&fm_276,0); + sum_l+=fm_276.out_l; + sum_r+=fm_276.out_r; + + acquire276OscSub(); + + FMOPN2_Clock(&fm_276,1); + sum_l+=fm_276.out_l; + sum_r+=fm_276.out_r; + + acquire276OscSub(); + + if (chipType==2) { + if (!o_bco && fm_276.o_bco) { + dacShifter=(dacShifter<<1)|fm_276.o_so; + + if (o_lro!=fm_276.o_lro) { + if (o_lro) + sample_l=dacShifter; + else + sample_r=dacShifter; + } + + o_lro=fm_276.o_lro; + } + o_bco=fm_276.o_bco; + } + } + + if (chipType==2) { + buf[0][h]=sample_l; + buf[1][h]=sample_r; + } else { + buf[0][h]=(sum_l*3)>>2; + buf[1][h]=(sum_r*3)>>2; + } + } } void DivPlatformGenesis::acquire(short** buf, size_t len) { @@ -1333,7 +1604,48 @@ void DivPlatformGenesis::reset() { writes.clear(); memset(regPool,0,512); if (useYMFM==2) { + dacShifter=0; + o_bco=0; + o_lro=0; + + lleCycle=0; + llePrevCycle=0; + + for (int i=0; i<6; i++) { + lleOscData[i]=0; + } + memset(&fm_276,0,sizeof(fmopn2_t)); + + fm_276.input.cs=1; + fm_276.input.rd=0; + fm_276.input.wr=0; + fm_276.input.address=0; + fm_276.input.data=0; + + if (chipType==2) { + fm_276.flags=0; + } else { + fm_276.flags=fmopn2_flags_ym3438; + } + + fm_276.input.ic=0; + for (int i=0; i<288; i++) { + FMOPN2_Clock(&fm_276,0); + FMOPN2_Clock(&fm_276,1); + } + + fm_276.input.ic=1; + for (int i=0; i<288*2; i++) { + FMOPN2_Clock(&fm_276,0); + FMOPN2_Clock(&fm_276,1); + } + + fm_276.input.ic=0; + for (int i=0; i<288*2; i++) { + FMOPN2_Clock(&fm_276,0); + FMOPN2_Clock(&fm_276,1); + } } else if (useYMFM==1) { fm_ymfm->reset(); } @@ -1474,6 +1786,8 @@ void DivPlatformGenesis::setFlags(const DivConfig& flags) { fm_ymfm=new ymfm::ym3438(iface); } rate=chipClock/144; + } else if (useYMFM==2) { + rate=chipClock/144; } else { rate=chipClock/36; } diff --git a/src/engine/platform/genesis.h b/src/engine/platform/genesis.h index a24cabb52..76b3e57ef 100644 --- a/src/engine/platform/genesis.h +++ b/src/engine/platform/genesis.h @@ -22,7 +22,9 @@ #include "fmshared_OPN.h" #include "sound/ymfm/ymfm_opn.h" +extern "C" { #include "../../../extern/YMF276-LLE/fmopn2.h" +} class DivYM2612Interface: public ymfm::ymfm_interface { int setA, setB; @@ -89,6 +91,11 @@ class DivPlatformGenesis: public DivPlatformOPN { unsigned char useYMFM; unsigned char chipType; short dacWrite; + + int lleCycle; + int llePrevCycle; + int lleOscData[6]; + int dacShifter, o_lro, o_bco; unsigned char dacVolTable[128]; @@ -97,6 +104,7 @@ class DivPlatformGenesis: public DivPlatformOPN { inline void processDAC(int iRate); inline void commitState(int ch, DivInstrument* ins); + void acquire276OscSub(); void acquire_nuked(short** buf, size_t len); void acquire_nuked276(short** buf, size_t len); void acquire_ymfm(short** buf, size_t len); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 6d7d11d69..8fdd1733f 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1541,10 +1541,10 @@ void FurnaceGUI::drawSettings() { ImGui::Text("YM2612"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::Combo("##YM2612Core",&settings.ym2612Core,ym2612Cores,2)) settingsChanged=true; + if (ImGui::Combo("##YM2612Core",&settings.ym2612Core,ym2612Cores,3)) settingsChanged=true; ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::Combo("##YM2612CoreRender",&settings.ym2612CoreRender,ym2612Cores,2)) settingsChanged=true; + if (ImGui::Combo("##YM2612CoreRender",&settings.ym2612CoreRender,ym2612Cores,3)) settingsChanged=true; ImGui::TableNextRow(); ImGui::TableNextColumn();