diff --git a/CMakeLists.txt b/CMakeLists.txt index caee04a7a..0128941c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -588,6 +588,7 @@ src/engine/platform/k007232.cpp src/engine/platform/ga20.cpp src/engine/platform/sm8521.cpp src/engine/platform/pv1000.cpp +src/engine/platform/k053260.cpp src/engine/platform/pcmdac.cpp src/engine/platform/dummy.cpp diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp index bf056b816..59d8af8f7 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp @@ -8,18 +8,19 @@ #include "k053260.hpp" -void k053260_core::tick() +void k053260_core::tick(u32 cycle) { m_out[0] = m_out[1] = 0; if (m_ctrl.sound_en()) { for (int i = 0; i < 4; i++) { - m_voice[i].tick(); + m_voice[i].tick(cycle); m_out[0] += m_voice[i].out(0); m_out[1] += m_voice[i].out(1); } } + /* // dac clock (YM3012 format) u8 dac_clock = m_dac.clock(); if (bitfield(++dac_clock, 0, 4) == 0) @@ -34,62 +35,58 @@ void k053260_core::tick() m_dac.set_state(bitfield(dac_state, 0, 2)); } m_dac.set_clock(bitfield(dac_clock, 0, 4)); + */ } -void k053260_core::voice_t::tick() +void k053260_core::voice_t::tick(u32 cycle) { if (m_enable && m_busy) { - bool update = false; // update counter - if (bitfield(++m_counter, 0, 12) == 0) + m_counter += cycle; + if (m_counter >= 0x1000) { if (m_bitpos < 8) { m_bitpos += 8; - m_addr = bitfield(m_addr + 1, 0, 21); + m_addr = m_reverse ? bitfield(m_addr - 1, 0, 21) : bitfield(m_addr + 1, 0, 21); m_remain--; + if (m_remain < 0) // check end flag + { + if (m_loop) + { + m_addr = m_start; + m_remain = m_length; + m_output = 0; + } + else + { + m_busy = false; + } + } } + m_data = m_host.m_intf.read_sample(bitfield(m_addr, 0, 21)); // fetch ROM if (m_adpcm) { - m_bitpos -= 4; - update = true; + m_bitpos -= 4; + const u8 nibble = bitfield(m_data, m_reverse ? (~m_bitpos & 4) : (m_bitpos & 4), 4); // get nibble from ROM + if (nibble) + { + m_output += m_host.adpcm_lut(nibble); + } } else { m_bitpos -= 8; } - m_counter = bitfield(m_pitch, 0, 12); - } - m_data = m_host.m_intf.read_sample(bitfield(m_addr, 0, 21)); // fetch ROM - if (update) - { - const u8 nibble = bitfield(m_data, m_bitpos & 4, 4); // get nibble from ROM - if (nibble) - { - m_adpcm_buf += bitfield(nibble, 3) ? s8(0x80 >> bitfield(nibble, 0, 3)) - : (1 << bitfield(nibble - 1, 0, 3)); - } + m_counter = (m_counter - 0x1000) + bitfield(m_pitch, 0, 12); } - if (m_remain < 0) // check end flag - { - if (m_loop) - { - m_addr = m_start; - m_remain = m_length; - m_adpcm_buf = 0; - } - else - { - m_busy = false; - } - } // calculate output - s32 output = m_adpcm ? m_adpcm_buf : sign_ext(m_data, 8) * s32(m_volume); + s32 output = (m_adpcm ? m_output : sign_ext(m_data, 8)) * s32(m_volume); // use math for now; actually fomula unknown - m_out[0] = (m_pan >= 0) ? s32(output * cos(f64(m_pan) * PI / 180)) : 0; - m_out[1] = (m_pan >= 0) ? s32(output * sin(f64(m_pan) * PI / 180)) : 0; + m_out[0] = (output * m_host.pan_lut(m_pan, 0)) >> 7; + m_out[1] = (output * m_host.pan_lut(m_pan, 1)) >> 7; } else { @@ -172,6 +169,7 @@ void k053260_core::write(u8 address, u8 data) case 0x28: // keyon/off toggle for (int i = 0; i < 4; i++) { + m_voice[i].set_reverse(bitfield(data, 4 + i)); if (bitfield(data, i) && (!m_voice[i].enable())) { // rising edge (keyon) m_voice[i].keyon(); @@ -244,8 +242,9 @@ void k053260_core::voice_t::keyon() m_addr = m_start; m_remain = m_length; m_bitpos = 4; - m_adpcm_buf = 0; - std::fill(m_out.begin(), m_out.end(), 0); + m_data = 0; + m_output = 0; + std::fill_n(m_out, 2, 0); } // key off trigger @@ -259,34 +258,35 @@ void k053260_core::reset() elem.reset(); } - m_intf.write_int(0); + //m_intf.write_int(0); - std::fill(m_host2snd.begin(), m_host2snd.end(), 0); - std::fill(m_snd2host.begin(), m_snd2host.end(), 0); + std::fill_n(m_host2snd, 2, 0); + std::fill_n(m_snd2host, 2, 0); m_ctrl.reset(); - m_dac.reset(); + //m_dac.reset(); - std::fill(m_reg.begin(), m_reg.end(), 0); - std::fill(m_out.begin(), m_out.end(), 0); + std::fill_n(m_reg, 64, 0); + std::fill_n(m_out, 2, 0); } // reset voice void k053260_core::voice_t::reset() { - m_enable = 0; - m_busy = 0; - m_loop = 0; - m_adpcm = 0; - m_pitch = 0; - m_start = 0; - m_length = 0; - m_volume = 0; - m_pan = -1; - m_counter = 0; - m_addr = 0; - m_remain = 0; - m_bitpos = 4; - m_data = 0; - m_adpcm_buf = 0; + m_enable = 0; + m_busy = 0; + m_loop = 0; + m_adpcm = 0; + m_pitch = 0; + m_reverse = 0; + m_start = 0; + m_length = 0; + m_volume = 0; + m_pan = 4; + m_counter = 0; + m_addr = 0; + m_remain = 0; + m_bitpos = 4; + m_data = 0; + m_output = 0; m_out[0] = m_out[1] = 0; } diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp index a8668a0df..9eb81363d 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp @@ -25,7 +25,7 @@ class k053260_intf : public vgsound_emu_core virtual u8 read_sample(u32 address) { return 0; } // sample fetch - virtual void write_int(u8 out) {} // timer interrupt + //virtual void write_int(u8 out) {} // timer interrupt }; class k053260_core : public vgsound_emu_core @@ -33,7 +33,19 @@ class k053260_core : public vgsound_emu_core friend class k053260_intf; // k053260 specific interface private: - const int pan_dir[8] = {-1, 0, 24, 35, 45, 55, 66, 90}; // pan direction + const s32 m_pan_lut[8][2] = { + {0x00, 0x00}, + {0x7f, 0x00}, + {0x74, 0x34}, + {0x68, 0x49}, + {0x5a, 0x5a}, + {0x49, 0x68}, + {0x34, 0x74}, + {0x00, 0x7f} + }; // pan LUT + + const s8 m_adpcm_lut[16] = + {0, 1, 2, 4, 8, 16, 32, 64, -128, -64, -32, -16, -8, -4, -2, -1}; // ADPCM LUT class voice_t : public vgsound_emu_core { @@ -47,23 +59,24 @@ class k053260_core : public vgsound_emu_core , m_loop(0) , m_adpcm(0) , m_pitch(0) + , m_reverse(0) , m_start(0) , m_length(0) , m_volume(0) - , m_pan(-1) + , m_pan(4) , m_counter(0) , m_addr(0) , m_remain(0) , m_bitpos(4) , m_data(0) - , m_adpcm_buf(0) + , m_output(0) { - m_out.fill(0); + std::fill_n(m_out, 2, 0); } // internal state void reset(); - void tick(); + void tick(u32 cycle); // accessors void write(u8 address, u8 data); @@ -79,9 +92,14 @@ class k053260_core : public vgsound_emu_core inline void set_adpcm(bool adpcm) { m_adpcm = adpcm ? 1 : 0; } + inline void set_reverse(const bool reverse) + { + m_reverse = reverse ? 1 : 0; + } + inline void length_inc() { m_length = (m_length + 1) & 0xffff; } - inline void set_pan(u8 pan) { m_pan = m_host.pan_dir[pan & 7]; } + inline void set_pan(u8 pan) { m_pan = pan & 7; } // getters inline bool enable() { return m_enable; } @@ -97,22 +115,23 @@ class k053260_core : public vgsound_emu_core private: // registers k053260_core &m_host; - u16 m_enable : 1; // enable flag - u16 m_busy : 1; // busy status - u16 m_loop : 1; // loop flag - u16 m_adpcm : 1; // ADPCM flag - u16 m_pitch : 12; // pitch - u32 m_start = 0; // start position - u16 m_length = 0; // source length - u8 m_volume = 0; // master volume - int m_pan = -1; // master pan - u16 m_counter = 0; // frequency counter - u32 m_addr = 0; // current address - s32 m_remain = 0; // remain for end sample - u8 m_bitpos = 4; // bit position for ADPCM decoding - u8 m_data = 0; // current data - s8 m_adpcm_buf = 0; // ADPCM buffer - std::array m_out; // current output + u16 m_enable : 1; // enable flag + u16 m_busy : 1; // busy status + u16 m_loop : 1; // loop flag + u16 m_adpcm : 1; // ADPCM flag + u16 m_pitch : 12; // pitch + u8 m_reverse : 1; // reverse playback + u32 m_start = 0; // start position + u16 m_length = 0; // source length + u8 m_volume = 0; // master volume + s32 m_pan = 4; // master pan + u16 m_counter = 0; // frequency counter + u32 m_addr = 0; // current address + s32 m_remain = 0; // remain for end sample + u8 m_bitpos = 4; // bit position for ADPCM decoding + u8 m_data = 0; // current data + s8 m_output = 0; // ADPCM buffer + s32 m_out[2]; // current output }; class ctrl_t @@ -152,6 +171,7 @@ class k053260_core : public vgsound_emu_core u8 m_input_en : 2; // Input enable }; + /* class ym3012_t { public: @@ -177,7 +197,9 @@ class k053260_core : public vgsound_emu_core std::array m_in; std::array m_out; }; + */ + /* class dac_t { public: @@ -205,6 +227,7 @@ class k053260_core : public vgsound_emu_core u8 m_clock : 4; // DAC clock (16 clock) u8 m_state : 2; // DAC state (4 state - SAM1, SAM2) }; + */ public: // constructor @@ -213,13 +236,13 @@ class k053260_core : public vgsound_emu_core , m_voice{*this, *this, *this, *this} , m_intf(intf) , m_ctrl(ctrl_t()) - , m_ym3012(ym3012_t()) - , m_dac(dac_t()) + //, m_ym3012(ym3012_t()) + //, m_dac(dac_t()) { - m_host2snd.fill(0); - m_snd2host.fill(0); - m_reg.fill(0); - m_out.fill(0); + std::fill_n(m_host2snd, 2, 0); + std::fill_n(m_snd2host, 2, 0); + std::fill_n(m_reg, 64, 0); + std::fill_n(m_out, 2, 0); } // communications @@ -233,7 +256,7 @@ class k053260_core : public vgsound_emu_core // internal state void reset(); - void tick(); + void tick(u32 cycle); // getters for debug, trackers, etc inline s32 output(u8 ch) { return m_out[ch & 1]; } // output for each channels @@ -245,20 +268,25 @@ class k053260_core : public vgsound_emu_core return (voice < 4) ? m_voice[voice].out(ch & 1) : 0; } + protected: + inline s32 pan_lut(const u8 pan, const u8 out) { return m_pan_lut[pan][out]; } + + inline s32 adpcm_lut(const u8 nibble) { return m_adpcm_lut[nibble]; } + private: - std::array m_voice; + voice_t m_voice[4]; k053260_intf &m_intf; // common memory interface - std::array m_host2snd; - std::array m_snd2host; + u8 m_host2snd[2]; + u8 m_snd2host[2]; ctrl_t m_ctrl; // chip control - ym3012_t m_ym3012; // YM3012 output - dac_t m_dac; // YM3012 interface + //ym3012_t m_ym3012; // YM3012 output + //dac_t m_dac; // YM3012 interface - std::array m_reg; // register pool - std::array m_out; // stereo output + u8 m_reg[64]; // register pool + s32 m_out[2]; // stereo output }; #endif diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index 6a532548b..56c3625d7 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -78,6 +78,7 @@ #include "platform/ga20.h" #include "platform/sm8521.h" #include "platform/pv1000.h" +#include "platform/k053260.h" #include "platform/pcmdac.h" #include "platform/dummy.h" #include "../ta-log.h" @@ -503,6 +504,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do case DIV_SYSTEM_PV1000: dispatch=new DivPlatformPV1000; break; + case DIV_SYSTEM_K053260: + dispatch=new DivPlatformK053260; + break; case DIV_SYSTEM_PCM_DAC: dispatch=new DivPlatformPCMDAC; break; diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index 97fd65cc1..76c410f65 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -929,6 +929,10 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) { break; case DIV_INS_PV1000: break; + case DIV_INS_K053260: + featureSM=true; + featureSL=true; + break; case DIV_INS_MAX: break; diff --git a/src/engine/instrument.h b/src/engine/instrument.h index c2e4a7313..ab2bc5e05 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -80,6 +80,7 @@ enum DivInstrumentType: unsigned short { DIV_INS_POKEMINI=47, DIV_INS_SM8521=48, DIV_INS_PV1000=49, + DIV_INS_K053260=50, DIV_INS_MAX, DIV_INS_NULL }; diff --git a/src/engine/platform/k053260.cpp b/src/engine/platform/k053260.cpp new file mode 100644 index 000000000..7ba1cc2ca --- /dev/null +++ b/src/engine/platform/k053260.cpp @@ -0,0 +1,513 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2023 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "k053260.h" +#include "../engine.h" +#include "../../ta-log.h" +#include + +#define rWrite(a,v) {if(!skipRegisterWrites) {k053260.write(a,v); regPool[a]=v; if(dumpWrites) addWrite(a,v);}} + +#define CHIP_DIVIDER 16 +#define TICK_DIVIDER 64 // for match to YM3012 output rate + +const char* regCheatSheetK053260[]={ + "MainToSub0", "00", + "MainToSub1", "01", + "SubToMain0", "02", + "SubToMain1", "03", + "CHx_FreqL", "08+x*8", + "CHx_FreqH", "09+x*8", + "CHx_LengthL", "0A+x*8", + "CHx_LengthH", "0B+x*8", + "CHx_StartL", "0C+x*8", + "CHx_StartM", "0D+x*8", + "CHx_StartH", "0E+x*8", + "CHx_Volume", "0F+x*8", + "KeyOn", "28", + "Status", "29", + "LoopFormat", "2A", + "Test", "2B", + "CH01_Pan", "2C", + "CH23_Pan", "2D", + "ROMReadback", "2E", + "Control", "2F", + NULL +}; + +const char** DivPlatformK053260::getRegisterSheet() { + return regCheatSheetK053260; +} + +inline void DivPlatformK053260::chWrite(unsigned char ch, unsigned int addr, unsigned char val) { + if (!skipRegisterWrites) { + rWrite(8+((ch<<3)|(addr&7)),val); + } +} + +u8 DivPlatformK053260::read_sample(u32 address) { + if ((sampleMem!=NULL) && (address32767) lout=32767; + if (lout<-32768) lout=-32768; + if (rout>32767) rout=32767; + if (rout<-32768) rout=-32768; + buf[0][i]=lout; + buf[1][i]=rout; + + for (int i=0; i<4; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=(k053260.voice_out(i,0)+k053260.voice_out(i,1))>>2; + } + } +} + +void DivPlatformK053260::tick(bool sysTick) { + unsigned char panMask=0; + for (int i=0; i<4; i++) { + chan[i].std.next(); + if (chan[i].std.vol.had) { + chan[i].outVol=((chan[i].vol&0x7f)*MIN(chan[i].macroVolMul,chan[i].std.vol.val))/chan[i].macroVolMul; + chWrite(i,7,chan[i].outVol); + } + if (NEW_ARP_STRAT) { + chan[i].handleArp(); + } else if (chan[i].std.arp.had) { + if (!chan[i].inPorta) { + chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val)); + } + chan[i].freqChanged=true; + } + if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-32768,32767); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } + chan[i].freqChanged=true; + } + if (chan[i].std.panL.had) { // panning + chan[i].panning=4+chan[i].std.panL.val; + if (!isMuted[i]) { + panMask|=1<=0 && samplesong.sampleLen) { + DivSample* s=parent->getSample(sample); + if (s->centerRate<1) { + off=1.0; + } else { + off=8363.0/s->centerRate; + } + } + DivSample* s=parent->getSample(chan[i].sample); + chan[i].freq=0x1000-(int)(off*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].freq>4095) chan[i].freq=4095; + if (chan[i].freq<0) chan[i].freq=0; + if (chan[i].keyOn) { + unsigned int start=0; + unsigned int length=0; + if (chan[i].sample>=0 && chan[i].samplesong.sampleLen) { + start=sampleOffK053260[chan[i].sample]; + length=s->length8; + if (chan[i].reverse) { + start+=length; + keyon|=(16<0) { + if (chan[i].reverse) { + start=start-MIN(chan[i].audPos,s->length8); + } + else { + start=start+MIN(chan[i].audPos,s->length8); + } + length=MAX(1,length-chan[i].audPos); + } + start=MIN(start,getSampleMemCapacity()); + length=MIN(65535,MIN(length,getSampleMemCapacity())); + rWrite(0x28,keyoff); // force keyoff first + rWrite(0x2a,loopoff); + chWrite(i,2,length&0xff); + chWrite(i,3,length>>8); + chWrite(i,4,start&0xff); + chWrite(i,5,start>>8); + chWrite(i,6,start>>16); + if (!chan[i].std.vol.had) { + chan[i].outVol=chan[i].vol; + chWrite(i,7,chan[i].outVol); + } + rWrite(0x28,keyon); + if (s->isLoopable()) { + rWrite(0x2a,loopon); + } + chan[i].keyOn=false; + } + if (chan[i].keyOff) { + rWrite(0x28,keyoff); + rWrite(0x2a,loopoff); + chan[i].keyOff=false; + } + if (chan[i].freqChanged) { + chWrite(i,0,chan[i].freq&0xff); + chWrite(i,1,chan[i].freq>>8); + chan[i].freqChanged=false; + } + } + } + if (panMask) { + updatePanning(panMask); + } +} + +void DivPlatformK053260::updatePanning(unsigned char mask) { + if (mask&3) { + rWrite(0x2c, + (isMuted[0]?0:chan[0].panning)| + (isMuted[1]?0:chan[1].panning<<3)); + } + if (mask&0xc) { + rWrite(0x2d, + (isMuted[2]?0:chan[2].panning)| + (isMuted[3]?0:chan[3].panning<<3)); + } +} + +int DivPlatformK053260::dispatch(DivCommand c) { + switch (c.cmd) { + case DIV_CMD_NOTE_ON: { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA); + chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:127; + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].sample=ins->amiga.getSample(c.value); + c.value=ins->amiga.getFreq(c.value); + } + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); + } + if (chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) { + chan[c.chan].sample=-1; + } + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; + } + chan[c.chan].active=true; + chan[c.chan].keyOn=true; + chan[c.chan].macroInit(ins); + if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) { + chan[c.chan].outVol=chan[c.chan].vol; + } + break; + } + case DIV_CMD_NOTE_OFF: + chan[c.chan].sample=-1; + chan[c.chan].active=false; + chan[c.chan].keyOff=true; + chan[c.chan].macroInit(NULL); + break; + case DIV_CMD_NOTE_OFF_ENV: + case DIV_CMD_ENV_RELEASE: + chan[c.chan].std.release(); + break; + case DIV_CMD_INSTRUMENT: + if (chan[c.chan].ins!=c.value || c.value2==1) { + chan[c.chan].ins=c.value; + } + break; + case DIV_CMD_VOLUME: + if (chan[c.chan].vol!=c.value) { + chan[c.chan].vol=c.value; + if (!chan[c.chan].std.vol.has) { + chan[c.chan].outVol=c.value; + chWrite(c.chan,7,chan[c.chan].outVol); + } + } + break; + case DIV_CMD_GET_VOLUME: + if (chan[c.chan].std.vol.has) { + return chan[c.chan].vol; + } + return chan[c.chan].outVol; + break; + case DIV_CMD_PANNING: + chan[c.chan].panning=MIN(parent->convertPanSplitToLinearLR(c.value,c.value2,7)+1,7); + if (!isMuted[c.chan]) { + updatePanning(1<chan[c.chan].baseFreq) { + chan[c.chan].baseFreq+=c.value; + if (chan[c.chan].baseFreq>=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } else { + chan[c.chan].baseFreq-=c.value; + if (chan[c.chan].baseFreq<=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } + chan[c.chan].freqChanged=true; + if (return2) { + chan[c.chan].inPorta=false; + return 2; + } + break; + } + case DIV_CMD_LEGATO: { + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0))); + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; + break; + } + case DIV_CMD_PRE_PORTA: + if (chan[c.chan].active && c.value2) { + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA)); + } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note); + chan[c.chan].inPorta=c.value; + break; + case DIV_CMD_SAMPLE_POS: + chan[c.chan].audPos=c.value; + chan[c.chan].setPos=true; + break; + case DIV_CMD_SAMPLE_DIR: { + if (chan[c.chan].reverse!=(bool)(c.value&1)) { + chan[c.chan].reverse=c.value&1; + } + break; + } + case DIV_CMD_GET_VOLMAX: + return 127; + break; + case DIV_CMD_MACRO_OFF: + chan[c.chan].std.mask(c.value,true); + break; + case DIV_CMD_MACRO_ON: + chan[c.chan].std.mask(c.value,false); + break; + case DIV_ALWAYS_SET_VOLUME: + return 1; + break; + default: + break; + } + return 1; +} + +void DivPlatformK053260::muteChannel(int ch, bool mute) { + isMuted[ch]=mute; + updatePanning(1<rate=rate; + } +} + +void DivPlatformK053260::poke(unsigned int addr, unsigned short val) { + rWrite(addr&0x3f,val); +} + +void DivPlatformK053260::poke(std::vector& wlist) { + for (DivRegWrite& i: wlist) rWrite(i.addr&0x3f,i.val); +} + +unsigned char* DivPlatformK053260::getRegisterPool() { + regPool[0x29]=k053260.read(0x29); // dynamically updated + return regPool; +} + +int DivPlatformK053260::getRegisterPoolSize() { + return 64; +} + +const void* DivPlatformK053260::getSampleMem(int index) { + return index == 0 ? sampleMem : NULL; +} + +size_t DivPlatformK053260::getSampleMemCapacity(int index) { + return index == 0 ? 2097152 : 0; +} + +size_t DivPlatformK053260::getSampleMemUsage(int index) { + return index == 0 ? sampleMemLen : 0; +} + +bool DivPlatformK053260::isSampleLoaded(int index, int sample) { + if (index!=0) return false; + if (sample<0 || sample>255) return false; + return sampleLoaded[sample]; +} + +void DivPlatformK053260::renderSamples(int sysID) { + memset(sampleMem,0,getSampleMemCapacity()); + memset(sampleOffK053260,0,256*sizeof(unsigned int)); + memset(sampleLoaded,0,256*sizeof(bool)); + + size_t memPos=1; // for avoid silence + for (int i=0; isong.sampleLen; i++) { + DivSample* s=parent->song.sample[i]; + if (!s->renderOn[0][sysID]) { + sampleOffK053260[i]=0; + continue; + } + + int length=MIN(65535,s->getEndPosition(DIV_SAMPLE_DEPTH_8BIT)); + int actualLength=MIN((int)(getSampleMemCapacity()-memPos-1),length); + if (actualLength>0) { + sampleOffK053260[i]=memPos-1; + for (int j=0; jdata8[j]; + } + sampleMem[memPos++]=0; // Silence for avoid popping noise + } + if (actualLength +#include "vgsound_emu/src/k053260/k053260.hpp" + +class DivPlatformK053260: public DivDispatch, public k053260_intf { + struct Channel: public SharedChannel { + unsigned int audPos; + int sample, wave; + int panning; + bool setPos, reverse; + int macroVolMul; + Channel(): + SharedChannel(127), + audPos(0), + sample(-1), + wave(-1), + panning(4), + setPos(false), + reverse(false), + macroVolMul(64) {} + }; + Channel chan[4]; + DivDispatchOscBuffer* oscBuf[4]; + bool isMuted[4]; + int chipType; + unsigned char curChan; + unsigned int sampleOffK053260[256]; + bool sampleLoaded[256]; + + unsigned char* sampleMem; + size_t sampleMemLen; + k053260_core k053260; + unsigned char regPool[64]; + void updatePanning(unsigned char mask); + + friend void putDispatchChip(void*,int); + friend void putDispatchChan(void*,int,int); + + public: + virtual u8 read_sample(u32 address) override; + virtual void acquire(short** buf, size_t len) override; + virtual int dispatch(DivCommand c) override; + virtual void* getChanState(int chan) override; + virtual DivMacroInt* getChanMacroInt(int ch) override; + virtual DivDispatchOscBuffer* getOscBuffer(int chan) override; + virtual unsigned char* getRegisterPool() override; + virtual int getRegisterPoolSize() override; + virtual void reset() override; + virtual void forceIns() override; + virtual void tick(bool sysTick=true) override; + virtual void muteChannel(int ch, bool mute) override; + virtual int getOutputCount() override; + virtual void notifyInsChange(int ins) override; + virtual void notifyWaveChange(int wave) override; + virtual void notifyInsDeletion(void* ins) override; + virtual void setFlags(const DivConfig& flags) override; + virtual void poke(unsigned int addr, unsigned short val) override; + virtual void poke(std::vector& wlist) override; + virtual const char** getRegisterSheet() override; + virtual const void* getSampleMem(int index = 0) override; + virtual size_t getSampleMemCapacity(int index = 0) override; + virtual size_t getSampleMemUsage(int index = 0) override; + virtual bool isSampleLoaded(int index, int sample) override; + virtual void renderSamples(int chipID) override; + virtual int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags) override; + virtual void quit() override; + DivPlatformK053260(): + DivDispatch(), + k053260_intf(), + k053260(*this) {} + private: + void chWrite(unsigned char ch, unsigned int addr, unsigned char val); +}; + +#endif diff --git a/src/engine/song.h b/src/engine/song.h index 0a6149f8d..4be25c436 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -127,7 +127,8 @@ enum DivSystem { DIV_SYSTEM_YM2203_CSM, DIV_SYSTEM_YM2608_CSM, DIV_SYSTEM_SM8521, - DIV_SYSTEM_PV1000 + DIV_SYSTEM_PV1000, + DIV_SYSTEM_K053260 }; enum DivEffectType: unsigned short { diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index f4d76cebc..6d8a308e6 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1864,6 +1864,19 @@ void DivEngine::registerSystems() { } ); + sysDefs[DIV_SYSTEM_K053260]=new DivSysDef( + "Konami K053260", NULL, 0xcc, 0, 4, false, true, 0x161, false, 1U<writeC(0xff); break; case DIV_SYSTEM_GA20: - for (int i=0; i<3; i++) { + for (int i=0; i<4; i++) { w->writeC(0xbf); // mute w->writeC((baseAddr2|5)+(i*8)); w->writeC(0); @@ -573,6 +573,16 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write w->writeC(0); } break; + case DIV_SYSTEM_K053260: + for (int i=0; i<4; i++) { + w->writeC(0xba); // mute + w->writeC(baseAddr2|0x2f); + w->writeC(0); + w->writeC(0xba); // keyoff + w->writeC(baseAddr2|0x28); + w->writeC(0); + } + break; default: break; } @@ -1029,6 +1039,11 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write w->writeC(baseAddr2|(write.addr&0x7f)); w->writeC(write.val); break; + case DIV_SYSTEM_K053260: + w->writeC(0xba); + w->writeC(baseAddr2|(write.addr&0x3f)); + w->writeC(write.val&0xff); + break; default: logW("write not handled!"); break; @@ -1201,6 +1216,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p DivDispatch* writeRF5C68[2]={NULL,NULL}; DivDispatch* writeMSM6295[2]={NULL,NULL}; DivDispatch* writeGA20[2]={NULL,NULL}; + DivDispatch* writeK053260[2]={NULL,NULL}; DivDispatch* writeNES[2]={NULL,NULL}; int writeNESIndex[2]={0,0}; @@ -1725,6 +1741,21 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p howManyChips++; } break; + case DIV_SYSTEM_K053260: + if (!hasK053260) { + hasK053260=disCont[i].dispatch->chipClock; + CHIP_VOL(29,0.4); + willExport[i]=true; + writeK053260[0]=disCont[i].dispatch; + } else if (!(hasK053260&0x40000000)) { + isSecond[i]=true; + CHIP_VOL_SECOND(29,0.4); + willExport[i]=true; + writeK053260[1]=disCont[i].dispatch; + hasK053260|=0x40000000; + howManyChips++; + } + break; case DIV_SYSTEM_T6W28: if (!hasSN) { hasSN=0xc0000000|disCont[i].dispatch->chipClock; @@ -2086,6 +2117,15 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p w->writeI(0); w->write(writeGA20[i]->getSampleMem(),writeGA20[i]->getSampleMemUsage()); } + if (writeK053260[i]!=NULL && writeK053260[i]->getSampleMemUsage()>0) { + w->writeC(0x67); + w->writeC(0x66); + w->writeC(0x8e); + w->writeI((writeK053260[i]->getSampleMemUsage()+8)|(i*0x80000000)); + w->writeI(writeK053260[i]->getSampleMemCapacity()); + w->writeI(0); + w->write(writeK053260[i]->getSampleMem(),writeK053260[i]->getSampleMemUsage()); + } if (writeNES[i]!=NULL && writeNES[i]->getSampleMemUsage()>0) { size_t howMuchWillBeWritten=writeNES[i]->getSampleMemUsage(); w->writeC(0x67); diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp index b9c1e11a7..fa5b46471 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -279,6 +279,10 @@ void FurnaceGUI::insListItem(int i, int dir, int asset) { ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PV1000]); name=fmt::sprintf(ICON_FA_GAMEPAD "##_INS%d",i); break; + case DIV_INS_K053260: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_K053260]); + name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i); + break; default: ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]); name=fmt::sprintf(ICON_FA_QUESTION "##_INS%d",i); diff --git a/src/gui/debug.cpp b/src/gui/debug.cpp index 60806bde1..e4e0907da 100644 --- a/src/gui/debug.cpp +++ b/src/gui/debug.cpp @@ -53,6 +53,7 @@ #include "../engine/platform/ga20.h" #include "../engine/platform/sm8521.h" #include "../engine/platform/pv1000.h" +#include "../engine/platform/k053260.h" #include "../engine/platform/dummy.h" #define COMMON_CHIP_DEBUG \ @@ -544,6 +545,13 @@ void putDispatchChip(void* data, int type) { COMMON_CHIP_DEBUG_BOOL; break; } + case DIV_SYSTEM_K053260: { + DivPlatformK053260* ch=(DivPlatformK053260*)data; + ImGui::Text("> K053260"); + COMMON_CHIP_DEBUG; + COMMON_CHIP_DEBUG_BOOL; + break; + } default: ImGui::Text("Unimplemented chip! Help!"); break; @@ -1082,6 +1090,19 @@ void putDispatchChan(void* data, int chanNum, int type) { COMMON_CHAN_DEBUG_BOOL; break; } + case DIV_SYSTEM_K053260: { + DivPlatformK053260::Channel* ch=(DivPlatformK053260::Channel*)data; + ImGui::Text("> K053260"); + COMMON_CHAN_DEBUG; + ImGui::Text("* Sample: %d",ch->sample); + ImGui::Text(" - pos: %d",ch->audPos); + ImGui::Text("- panning: %d",ch->panning); + ImGui::Text("- macroVolMul: %.2x",ch->macroVolMul); + COMMON_CHAN_DEBUG_BOOL; + ImGui::TextColored(ch->setPos?colorOn:colorOff,">> SetPos"); + ImGui::TextColored(ch->reverse?colorOn:colorOff,">> Reverse"); + break; + } default: ImGui::Text("Unimplemented chip! Help!"); break; diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index f637666a8..60d713d32 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -1378,7 +1378,8 @@ void FurnaceGUI::doAction(int what) { i==DIV_INS_SNES || i==DIV_INS_ES5506 || i==DIV_INS_K007232 || - i==DIV_INS_GA20) { + i==DIV_INS_GA20 || + i==DIV_INS_K053260) { makeInsTypeList.push_back(i); } } diff --git a/src/gui/gui.h b/src/gui/gui.h index c87b60a1d..dc7a4aded 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -239,6 +239,7 @@ enum FurnaceGUIColors { GUI_COLOR_INSTR_POKEMINI, GUI_COLOR_INSTR_SM8521, GUI_COLOR_INSTR_PV1000, + GUI_COLOR_INSTR_K053260, GUI_COLOR_INSTR_UNKNOWN, GUI_COLOR_CHANNEL_BG, diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 25364d2d9..f7dcd40b8 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -131,6 +131,7 @@ const char* insTypes[DIV_INS_MAX+1]={ "Pokémon Mini/QuadTone", "SM8521", "PV-1000", + "K053260", NULL }; @@ -851,6 +852,7 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_INSTR_POKEMINI,"",ImVec4(1.0f,1.0f,0.3f,1.0f)), D(GUI_COLOR_INSTR_SM8521,"",ImVec4(0.5f,0.55f,0.6f,1.0f)), D(GUI_COLOR_INSTR_PV1000,"",ImVec4(0.4f,0.6f,0.7f,1.0f)), + D(GUI_COLOR_INSTR_K053260,"",ImVec4(1.0f,0.8f,0.1f,1.0f)), D(GUI_COLOR_INSTR_UNKNOWN,"",ImVec4(0.3f,0.3f,0.3f,1.0f)), D(GUI_COLOR_CHANNEL_BG,"",ImVec4(0.4f,0.6f,0.8f,1.0f)), @@ -1034,6 +1036,7 @@ const int availableSystems[]={ DIV_SYSTEM_GA20, DIV_SYSTEM_SM8521, DIV_SYSTEM_PV1000, + DIV_SYSTEM_K053260, DIV_SYSTEM_PCM_DAC, DIV_SYSTEM_PONG, 0 // don't remove this last one! @@ -1142,6 +1145,7 @@ const int chipsSample[]={ DIV_SYSTEM_GA20, DIV_SYSTEM_PCM_DAC, DIV_SYSTEM_ES5506, + DIV_SYSTEM_K053260, 0 // don't remove this last one! }; diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 183392612..6715e56b6 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -4409,7 +4409,8 @@ void FurnaceGUI::drawInsEdit() { ins->type==DIV_INS_SNES || ins->type==DIV_INS_ES5506 || ins->type==DIV_INS_K007232 || - ins->type==DIV_INS_GA20) { + ins->type==DIV_INS_GA20 || + ins->type==DIV_INS_K053260) { if (ImGui::BeginTabItem((ins->type==DIV_INS_SU)?"Sound Unit":"Sample")) { String sName; bool wannaOpenSMPopup=false; @@ -5316,7 +5317,8 @@ void FurnaceGUI::drawInsEdit() { } if (ins->type==DIV_INS_FM || ins->type==DIV_INS_SEGAPCM || ins->type==DIV_INS_MIKEY || ins->type==DIV_INS_MULTIPCM || ins->type==DIV_INS_SU || ins->type==DIV_INS_OPZ || - ins->type==DIV_INS_OPM || ins->type==DIV_INS_SNES || ins->type==DIV_INS_MSM5232) { + ins->type==DIV_INS_OPM || ins->type==DIV_INS_SNES || ins->type==DIV_INS_MSM5232 || + ins->type==DIV_INS_K053260) { volMax=127; } if (ins->type==DIV_INS_GB) { @@ -5405,7 +5407,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_TIA || ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_SCC || ins->type==DIV_INS_PET || ins->type==DIV_INS_SEGAPCM || ins->type==DIV_INS_FM || ins->type==DIV_INS_K007232 || ins->type==DIV_INS_GA20 || - ins->type==DIV_INS_SM8521 || ins->type==DIV_INS_PV1000) { + ins->type==DIV_INS_SM8521 || ins->type==DIV_INS_PV1000 || ins->type==DIV_INS_K053260) { dutyMax=0; } if (ins->type==DIV_INS_VBOY) { @@ -5505,6 +5507,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_SEGAPCM) waveMax=0; if (ins->type==DIV_INS_K007232) waveMax=0; if (ins->type==DIV_INS_GA20) waveMax=0; + if (ins->type==DIV_INS_K053260) waveMax=0; if (ins->type==DIV_INS_POKEMINI) waveMax=0; if (ins->type==DIV_INS_SU || ins->type==DIV_INS_POKEY) waveMax=7; if (ins->type==DIV_INS_PET) { @@ -5623,6 +5626,11 @@ void FurnaceGUI::drawInsEdit() { panMax=7; panSingleNoBit=true; } + if (ins->type==DIV_INS_K053260) { + panMin=-3; + panMax=3; + panSingleNoBit=true; + } if (ins->type==DIV_INS_SU) { panMin=-127; panMax=127; @@ -5713,7 +5721,8 @@ void FurnaceGUI::drawInsEdit() { ins->type==DIV_INS_VBOY || (ins->type==DIV_INS_X1_010 && ins->amiga.useSample) || ins->type==DIV_INS_K007232 || - ins->type==DIV_INS_GA20) { + ins->type==DIV_INS_GA20 || + ins->type==DIV_INS_K053260) { macroList.push_back(FurnaceGUIMacroDesc("Phase Reset",&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } if (ex1Max>0) { diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index 12b77cebe..fcadc4750 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -1262,6 +1262,42 @@ void FurnaceGUI::initSystemPresets() { CH(DIV_SYSTEM_K007232, 1.0f, 0, "") // "" } ); + ENTRY( + "Konami Rollergames", { + CH(DIV_SYSTEM_OPL2, 1.0f, 0, ""), // 3.58MHz + CH(DIV_SYSTEM_K053260, 1.0f, 0, ""), // "" + } + ); + ENTRY( + "Konami Rollergames (drums mode)", { + CH(DIV_SYSTEM_OPL2_DRUMS, 1.0f, 0, ""), // 3.58MHz + CH(DIV_SYSTEM_K053260, 1.0f, 0, ""), // "" + } + ); + ENTRY( + "Konami Golfing Greats", { + CH(DIV_SYSTEM_K053260, 1.0f, 0, ""), // 3.58MHz + } + ); + ENTRY( + "Konami Lightning Fighters", { + CH(DIV_SYSTEM_YM2151, 1.0f, 0, ""), // 3.58MHz + CH(DIV_SYSTEM_K053260, 1.0f, 0, ""), // "" + } + ); + ENTRY( + "Konami Over Drive", { + CH(DIV_SYSTEM_YM2151, 1.0f, 0, ""), // 3.58MHz + CH(DIV_SYSTEM_K053260, 1.0f, 0, ""), // "" + CH(DIV_SYSTEM_K053260, 1.0f, 0, ""), // "" + } + ); + ENTRY( + "Konami Asterix", { + CH(DIV_SYSTEM_YM2151, 1.0f, 0, "clockSel=2"), // 4MHz + CH(DIV_SYSTEM_K053260, 1.0f, 0, "clockSel=1"), // "" + } + ); ENTRY( "Konami Hexion", { CH(DIV_SYSTEM_SCC, 1.0f, 0, "clockSel=2"), // 1.5MHz (3MHz input) @@ -2466,6 +2502,11 @@ void FurnaceGUI::initSystemPresets() { CH(DIV_SYSTEM_ES5506, 1.0f, 0, "channels=31") } ); + ENTRY( + "Konami K053260", { + CH(DIV_SYSTEM_K053260, 1.0f, 0, "") + } + ); CATEGORY_END; CATEGORY_BEGIN("Wavetable","chips which use user-specified waveforms to generate sound."); diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 4246922c5..99ccbd22c 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -267,6 +267,16 @@ void FurnaceGUI::drawSampleEdit() { SAMPLE_WARN(warnLength,"SegaPCM: maximum sample length is 65280"); } break; + case DIV_SYSTEM_K053260: + if (sample->loop) { + if (sample->loopStart!=0 || sample->loopEnd!=(int)(sample->samples)) { + SAMPLE_WARN(warnLoopPos,"K053260: loop point ignored (may only loop entire sample)"); + } + } + if (sample->samples>65535) { + SAMPLE_WARN(warnLength,"K053260: maximum sample length is 65535"); + } + break; default: break; } diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index b0e280e90..cbc71aed6 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -2102,6 +2102,7 @@ void FurnaceGUI::drawSettings() { UI_COLOR_CONFIG(GUI_COLOR_INSTR_POKEMINI,"Pokémon Mini"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_SM8521,"SM8521"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_PV1000,"PV-1000"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_K053260,"K053260"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_UNKNOWN,"Other/Unknown"); ImGui::TreePop(); } diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 10bd055a7..32e7c35c9 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -1882,6 +1882,26 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo } break; }*/ + case DIV_SYSTEM_K053260: { + int clockSel=flags.getInt("clockSel",0); + + ImGui::Text("Clock rate:"); + if (ImGui::RadioButton("3.58MHz (NTSC)",clockSel==0)) { + clockSel=0; + altered=true; + } + if (ImGui::RadioButton("4MHz",clockSel==1)) { + clockSel=1; + altered=true; + } + + if (altered) { + e->lockSave([&]() { + flags.set("clockSel",clockSel); + }); + } + break; + } case DIV_SYSTEM_SWAN: case DIV_SYSTEM_BUBSYS_WSG: case DIV_SYSTEM_PET: