From 6c5e806fb2dacbce358578f23cb0ff2db694744c Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 11 Feb 2023 21:06:17 +0900 Subject: [PATCH 01/13] Prepare for SM8521 --- src/engine/platform/sm8521.cpp | 382 +++++++++++++++++++++++++++++ src/engine/platform/sm8521.h | 86 +++++++ src/engine/platform/sound/sm8521.h | 273 +++++++++++++++++++++ 3 files changed, 741 insertions(+) create mode 100644 src/engine/platform/sm8521.cpp create mode 100644 src/engine/platform/sm8521.h create mode 100644 src/engine/platform/sound/sm8521.h diff --git a/src/engine/platform/sm8521.cpp b/src/engine/platform/sm8521.cpp new file mode 100644 index 000000000..5f025799d --- /dev/null +++ b/src/engine/platform/sm8521.cpp @@ -0,0 +1,382 @@ +/** + * 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 "sm8521.h" +#include "../engine.h" +#include + +//#define rWrite(a,v) pendingWrites[a]=v; +#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} } + +#define CHIP_DIVIDER 16 + +const char* regCheatSheetSM8521[]={ + "SGC", "40", + "SG0L", "42", + "SG1L", "44", + "SG0TL", "46", + "SG0TH", "47", + "SG1TL", "48", + "SG1TH", "49", + "SG2L", "4A", + "SG2TL", "4C", + "SG2TH", "4D", + "SGDA", "4E", + "SG0Wn", "60+n", + "SG1Wn", "70+n", + NULL +}; + +const char** DivPlatformSM8521::getRegisterSheet() { + return regCheatSheetSM8521; +} + +void DivPlatformSM8521::acquire(short** buf, size_t len) { + while (!writes.empty()) { + QueuedWrite w=writes.front(); + sm8521_write(&sm8521,w.addr,w.val); + regPool[w.addr&0xff]=w.val; + writes.pop(); + } + for (size_t h=0; hdata[oscBuf[i]->needle++]=sm8521.sg[i].base.out; + } + oscBuf[2]->data[oscBuf[2]->needle++]=sm8521.noise.base.out; + } +} + +void DivPlatformSM8521::updateWave(int ch) { + if (ch<2) { + const unsigned char temp=regPool[0x40]; + rWrite(0x40,temp&~(1<0) { + chan[i].antiClickPeriodCount+=(chipClock/MAX(parent->getCurHz(),1.0f)); + chan[i].antiClickWavePos+=chan[i].antiClickPeriodCount/chan[i].freq; + chan[i].antiClickPeriodCount%=chan[i].freq; + } + + chan[i].std.next(); + if (chan[i].std.vol.had) { + chan[i].outVol=((chan[i].vol&31)*MIN(31,chan[i].std.vol.val))>>5; + if (!isMuted[i]) { + chan[i].volumeChanged=true; + } + } + 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].volumeChanged) { + if (isMuted[i]) { + rWrite(volMap[i],0); + } else { + rWrite(volMap[i],chan[i].outVol&0x1f); + } + chan[i].volumeChanged=false; + } + if (chan[i].std.wave.had) { + if (chan[i].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) { + chan[i].wave=chan[i].std.wave.val; + chan[i].ws.changeWave1(chan[i].wave); + if (!chan[i].keyOff) chan[i].keyOn=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].active) { + if (chan[i].ws.tick() || (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1)) { + updateWave(i); + } + } + 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)-1; + if (chan[i].freq<1) chan[i].freq=1; + if (chan[i].freq>4095) chan[i].freq=4095; + rWrite(freqMap[i][0],chan[i].freq>>8); + rWrite(freqMap[i][1],chan[i].freq&0xff); + const unsigned char temp=regPool[0x40]; + if (chan[i].keyOn) { + rWrite(0x40,temp|(1<getIns(chan[c.chan].ins,DIV_INS_PCE); + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); + 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; + } + if (chan[c.chan].wave<0) { + chan[c.chan].wave=0; + chan[c.chan].ws.changeWave1(chan[c.chan].wave); + } + chan[c.chan].ws.init(ins,32,15,chan[c.chan].insChanged); + chan[c.chan].insChanged=false; + if (!isMuted[c.chan]) { + chan[c.chan].volumeChanged=true; + } + break; + } + case DIV_CMD_NOTE_OFF: + 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; + chan[c.chan].insChanged=true; + } + 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; + if (!isMuted[c.chan]) { + chan[c.chan].volumeChanged=true; + } + } + } + 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_PITCH: + chan[c.chan].pitch=c.value; + chan[c.chan].freqChanged=true; + break; + case DIV_CMD_WAVE: + chan[c.chan].wave=c.value; + chan[c.chan].ws.changeWave1(chan[c.chan].wave); + chan[c.chan].keyOn=true; + break; + case DIV_CMD_NOTE_PORTA: { + int destFreq=NOTE_PERIODIC(c.value2); + bool return2=false; + if (destFreq>chan[c.chan].baseFreq) { + chan[c.chan].baseFreq+=c.value*((parent->song.linearPitch==2)?1:8); + if (chan[c.chan].baseFreq>=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } else { + chan[c.chan].baseFreq-=c.value*((parent->song.linearPitch==2)?1:8); + 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):(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_PCE)); + } + 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_GET_VOLMAX: + return 31; + 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 DivPlatformSM8521::muteChannel(int ch, bool mute) { + isMuted[ch]=mute; + chan[ch].volumeChanged=true; +} + +void DivPlatformSM8521::forceIns() { + for (int i=0; i<3; i++) { + chan[i].insChanged=true; + chan[i].freqChanged=true; + updateWave(i); + } +} + +void* DivPlatformSM8521::getChanState(int ch) { + return &chan[ch]; +} + +DivMacroInt* DivPlatformSM8521::getChanMacroInt(int ch) { + return &chan[ch].std; +} + +DivDispatchOscBuffer* DivPlatformSM8521::getOscBuffer(int ch) { + return oscBuf[ch]; +} + +unsigned char* DivPlatformSM8521::getRegisterPool() { + return regPool; +} + +int DivPlatformSM8521::getRegisterPoolSize() { + return 256; +} + +void DivPlatformSM8521::reset() { + while (!writes.empty()) writes.pop(); + memset(regPool,0,256); + for (int i=0; i<3; i++) { + chan[i]=DivPlatformSM8521::Channel(); + chan[i].std.setEngine(parent); + chan[i].ws.setEngine(parent); + chan[i].ws.init(NULL,32,15,false); + } + if (dumpWrites) { + addWrite(0xffffffff,0); + } + sm8521_reset(&sm8521); + sm8521_write(&sm8521,0x40,0x80); // initialize SGC +} + +int DivPlatformSM8521::getOutputCount() { + return 1; +} + +bool DivPlatformSM8521::keyOffAffectsArp(int ch) { + return true; +} + +void DivPlatformSM8521::notifyWaveChange(int wave) { + for (int i=0; i<2; i++) { + if (chan[i].wave==wave) { + chan[i].ws.changeWave1(wave); + updateWave(i); + } + } +} + +void DivPlatformSM8521::notifyInsDeletion(void* ins) { + for (int i=0; i<3; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + +void DivPlatformSM8521::setFlags(const DivConfig& flags) { + chipClock=10000000; + CHECK_CUSTOM_CLOCK; + rate=chipClock/4; + for (int i=0; i<3; i++) { + oscBuf[i]->rate=rate; + } +} + +void DivPlatformSM8521::poke(unsigned int addr, unsigned short val) { + rWrite(addr,val); +} + +void DivPlatformSM8521::poke(std::vector& wlist) { + for (DivRegWrite& i: wlist) rWrite(i.addr,i.val); +} + +int DivPlatformSM8521::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) { + parent=p; + dumpWrites=false; + skipRegisterWrites=false; + for (int i=0; i<3; i++) { + isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; + } + setFlags(flags); + reset(); + return 6; +} + +void DivPlatformSM8521::quit() { + for (int i=0; i<3; i++) { + delete oscBuf[i]; + } +} + +DivPlatformSM8521::~DivPlatformSM8521() { +} diff --git a/src/engine/platform/sm8521.h b/src/engine/platform/sm8521.h new file mode 100644 index 000000000..4b9318c4f --- /dev/null +++ b/src/engine/platform/sm8521.h @@ -0,0 +1,86 @@ +/** + * 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. + */ + +#ifndef _NAMCOWSG_H +#define _NAMCOWSG_H + +#include "../dispatch.h" +#include +#include "../waveSynth.h" +#include "sound/sm8521.h" + +class DivPlatformSM8521: public DivDispatch { + + const unsigned char volMap[3]={0x42,0x44,0x4a}; + const unsigned char freqMap[3][2]={{0x46,0x47},{0x48,0x49},{0x4c,0x4d}}; + + struct Channel: public SharedChannel { + int antiClickPeriodCount, antiClickWavePos; + signed short wave; + bool volumeChanged; + DivWaveSynth ws; + Channel(): + SharedChannel(15), + antiClickPeriodCount(0), + antiClickWavePos(0), + wave(-1), + volumeChanged(false) {} + }; + Channel chan[3]; + DivDispatchOscBuffer* oscBuf[3]; + bool isMuted[3]; + struct QueuedWrite { + unsigned short addr; + unsigned char val; + QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v) {} + }; + std::queue writes; + + bool antiClickEnabled; + struct sm8521_t sm8521; + unsigned char regPool[256]; + void updateWave(int ch); + friend void putDispatchChip(void*,int); + friend void putDispatchChan(void*,int,int); + public: + void acquire(short** buf, size_t len); + int dispatch(DivCommand c); + void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); + DivDispatchOscBuffer* getOscBuffer(int chan); + unsigned char* getRegisterPool(); + int getRegisterPoolSize(); + void reset(); + void forceIns(); + void tick(bool sysTick=true); + void muteChannel(int ch, bool mute); + int getOutputCount(); + bool keyOffAffectsArp(int ch); + void setFlags(const DivConfig& flags); + void notifyWaveChange(int wave); + void notifyInsDeletion(void* ins); + void poke(unsigned int addr, unsigned short val); + void poke(std::vector& wlist); + const char** getRegisterSheet(); + int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); + void quit(); + ~DivPlatformSM8521(); +}; + +#endif diff --git a/src/engine/platform/sound/sm8521.h b/src/engine/platform/sound/sm8521.h new file mode 100644 index 000000000..24635eb01 --- /dev/null +++ b/src/engine/platform/sound/sm8521.h @@ -0,0 +1,273 @@ +/* + +============================================================================ + +SM8521 sound emulator +by cam900 + +This file is licensed under zlib license. + +============================================================================ + +zlib License + +(C) 2022-present cam900 and contributors + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +============================================================================ + +TODO: +- needs hardware test + +*/ + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +enum sm8521_sgc +{ + SM8521_SGC_SONDOUT = (1 << 7), + SM8521_SGC_DIROUT = (1 << 3), + SM8521_SGC_SG2OUT = (1 << 2), + SM8521_SGC_SG1OUT = (1 << 1), + SM8521_SGC_SG0OUT = (1 << 0) +}; + +struct sm8521_sg_t +{ + unsigned short t; // Time constant register + unsigned char level; // Output level control register + unsigned short out; // output + int counter; // clock counter +}; + +struct sm8521_wave_t +{ + struct sm8521_sg_t base; + unsigned char addr; // waveform address + unsigned char wave[16]; // 4 bit waveform (32 nybbles) +}; + +struct sm8521_noise_t +{ + struct sm8521_sg_t base; + unsigned int lfsr; // LFSR +}; + +struct sm8521_t +{ + struct sm8521_wave_t sg[2]; + struct sm8521_noise_t noise; + signed short out; // output + signed char sgda; // D/A direct output register (write only) + unsigned char sgc; // Control register +}; + +void sm8521_sg_wave_tick(struct sm8521_wave_t *sg, const int cycle) +{ + sg->base.counter += cycle; + while (sg->base.counter >= (sg->base.t + 1)) + { + sg->addr++; + sg->base.counter -= (sg->base.t + 1); + } + int wave = (sg->wave[(sg->addr >> 1) & 0xf] >> ((sg->addr & 1) << 2)) & 0xf; + if (wave & 0x8) + { + wave = -(0x8 - (wave & 0x7)); + } + sg->base.out = (wave * sg->base.level) >> 1; // scale out to 8bit +} + +void sm8521_noise_tick(struct sm8521_noise_t *noise, const int cycle) +{ + noise->base.counter += cycle; + while (noise->base.counter >= (noise->base.t + 1)) + { + noise->lfsr = rand() & 0x1; // unknown algorithm + noise->base.counter -= (noise->base.t + 1); + } + noise->base.out = (((noise->lfsr & 0x1) ? 7 : -8) * noise->base.level) >> 1; // scale out to 8bit +} + +void sm8521_sound_tick(struct sm8521_t *sm8521, const int cycle) +{ + int out = 0; + if (sm8521->sgc & SM8521_SGC_SONDOUT) + { + if (sm8521->sgc & SM8521_SGC_DIROUT) + { + out = sm8521->sgda - 0x80; + } + else + { + if (sm8521->sgc & SM8521_SGC_SG0OUT) + { + sm8521_sg_wave_tick(&sm8521->sg[0], cycle); + out += sm8521->sg[0].base.out; + } + if (sm8521->sgc & SM8521_SGC_SG1OUT) + { + sm8521_sg_wave_tick(&sm8521->sg[1], cycle); + out += sm8521->sg[1].base.out; + } + if (sm8521->sgc & SM8521_SGC_SG2OUT) + { + sm8521_noise_tick(&sm8521->noise, cycle); + out += sm8521->noise.base.out; + } + out = (out < -0x80) ? -0x80 : ((out > 0x7f) ? 0x7f : out); // clamp + } + } + sm8521->out = out; +} + +void sm8521_reset(struct sm8521_t *sm8521) +{ + for (int i = 0; i < 2; i++) + { + sm8521->sg[i].base.t = 0; + sm8521->sg[i].base.level = 0; + sm8521->sg[i].base.out = 0; + sm8521->sg[i].base.counter = 0; + sm8521->sg[i].addr = 0; + for (int j = 0; j < 16; j++) + { + sm8521->sg[i].wave[j] = 0; + } + } + sm8521->noise.base.t = 0; + sm8521->noise.base.level = 0; + sm8521->noise.base.out = 0; + sm8521->noise.base.counter = 0; + sm8521->noise.lfsr = 0; + sm8521->out = 0; + sm8521->sgda = 0; + sm8521->sgc = 0; +} + +unsigned char sm8521_read(struct sm8521_t *sm8521, const unsigned char a) +{ + if ((a & 0xe0) == 0x60) + { + if ((a & 0x10) && (!(sm8521->sgc & SM8521_SGC_SG1OUT))) // 0x70-0x7f SG1W0-15 + { + return sm8521->sg[1].wave[a & 0xf]; + } + else if ((!(a & 0x10)) && (!(sm8521->sgc & SM8521_SGC_SG0OUT))) // 0x60-0x6f SG0W0-15 + { + return sm8521->sg[0].wave[a & 0xf]; + } + return 0; + } + switch (a) + { + case 0x40: // SGC + return sm8521->sgc; + break; + case 0x42: // SG0L + return sm8521->sg[0].base.level & 0x1f; + break; + case 0x44: // SG1L + return sm8521->sg[1].base.level & 0x1f; + break; + case 0x46: // SG0TH + return (sm8521->sg[0].base.t >> 8) & 0xf; + break; + case 0x47: // SG0TL + return sm8521->sg[0].base.t & 0xff; + break; + case 0x48: // SG1TH + return (sm8521->sg[1].base.t >> 8) & 0xf; + break; + case 0x49: // SG1TL + return sm8521->sg[1].base.t & 0x0ff; + break; + case 0x4a: // SG2L + return sm8521->noise.base.level & 0x1f; + break; + case 0x4c: // SG2TH + return (sm8521->noise.base.t >> 8) & 0xf; + break; + case 0x4d: // SG2TL + return sm8521->noise.base.t & 0xff; + break; + default: + return 0; + break; + } +} +void sm8521_write(struct sm8521_t *sm8521, const unsigned char a, const unsigned char d) +{ + if ((a & 0xe0) == 0x60) + { + if ((a & 0x10) && (!(sm8521->sgc & SM8521_SGC_SG1OUT))) // 0x70-0x7f SG1W0-15 + { + sm8521->sg[1].wave[a & 0xf] = d; + } + else if ((!(a & 0x10)) && (!(sm8521->sgc & SM8521_SGC_SG0OUT))) // 0x60-0x6f SG0W0-15 + { + sm8521->sg[0].wave[a & 0xf] = d; + } + return; + } + switch (a) + { + case 0x40: // SGC + sm8521->sgc = d; + break; + case 0x42: // SG0L + sm8521->sg[0].base.level = d & 0x1f; + break; + case 0x44: // SG1L + sm8521->sg[1].base.level = d & 0x1f; + break; + case 0x46: // SG0TH + sm8521->sg[0].base.t = (sm8521->sg[0].base.t & 0x0ff) | ((d << 8) & 0xf00); + break; + case 0x47: // SG0TL + sm8521->sg[0].base.t = (sm8521->sg[0].base.t & 0xf00) | (d & 0x0ff); + break; + case 0x48: // SG1TH + sm8521->sg[1].base.t = (sm8521->sg[1].base.t & 0x0ff) | ((d << 8) & 0xf00); + break; + case 0x49: // SG1TL + sm8521->sg[1].base.t = (sm8521->sg[1].base.t & 0xf00) | (d & 0x0ff); + break; + case 0x4a: // SG2L + sm8521->noise.base.level = d & 0x1f; + break; + case 0x4c: // SG2TH + sm8521->noise.base.t = (sm8521->noise.base.t & 0x0ff) | ((d << 8) & 0xf00); + break; + case 0x4d: // SG2TL + sm8521->noise.base.t = (sm8521->noise.base.t & 0xf00) | (d & 0x0ff); + break; + case 0x4e: // SGDA + sm8521->sgda = d; + break; + } +} + +#ifdef __cplusplus +} // extern "C" +#endif From 0cb36206c49be7dccceeceeed4e4e243557416dc Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 11 Feb 2023 21:11:32 +0900 Subject: [PATCH 02/13] anti-click --- src/engine/platform/sm8521.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/engine/platform/sm8521.cpp b/src/engine/platform/sm8521.cpp index 5f025799d..ad9789087 100644 --- a/src/engine/platform/sm8521.cpp +++ b/src/engine/platform/sm8521.cpp @@ -72,7 +72,9 @@ void DivPlatformSM8521::updateWave(int ch) { int nibble2=15-chan[ch].ws.output[((1+(i<<1))+chan[ch].antiClickWavePos-1)&31]; rWrite(0x60+i+(ch*16),(nibble2<<4)|nibble1); } - rWrite(0x40,temp|(1< Date: Sat, 11 Feb 2023 21:37:11 +0900 Subject: [PATCH 03/13] Fix compile, Add SM8521 to list --- CMakeLists.txt | 3 + src/engine/dispatchContainer.cpp | 4 + src/engine/platform/sm8521.cpp | 13 +- src/engine/platform/sm8521.h | 8 +- src/engine/platform/sound/sm8521.c | 235 +++++++++++++++++++++++++++++ src/engine/platform/sound/sm8521.h | 201 ++---------------------- src/engine/song.h | 3 +- src/engine/sysDef.cpp | 11 ++ src/gui/guiConst.cpp | 2 + src/gui/presets.cpp | 5 + 10 files changed, 286 insertions(+), 199 deletions(-) create mode 100644 src/engine/platform/sound/sm8521.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 059154f40..7665e24f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -455,6 +455,8 @@ src/engine/platform/sound/snes/SPC_DSP.cpp src/engine/platform/sound/ga20/iremga20.cpp +src/engine/platform/sound/sm8521.c + src/engine/platform/oplAInterface.cpp src/engine/platform/ym2608Interface.cpp src/engine/platform/ym2610Interface.cpp @@ -539,6 +541,7 @@ src/engine/platform/rf5c68.cpp src/engine/platform/snes.cpp src/engine/platform/k007232.cpp src/engine/platform/ga20.cpp +src/engine/platform/sm8521.cpp src/engine/platform/pcmdac.cpp src/engine/platform/dummy.cpp ) diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index 030bab559..fe4f76e25 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -75,6 +75,7 @@ #include "platform/vb.h" #include "platform/k007232.h" #include "platform/ga20.h" +#include "platform/sm8521.h" #include "platform/pcmdac.h" #include "platform/dummy.h" #include "../ta-log.h" @@ -489,6 +490,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do case DIV_SYSTEM_GA20: dispatch=new DivPlatformGA20; break; + case DIV_SYSTEM_SM8521: + dispatch=new DivPlatformSM8521; + break; case DIV_SYSTEM_PCM_DAC: dispatch=new DivPlatformPCMDAC; break; diff --git a/src/engine/platform/sm8521.cpp b/src/engine/platform/sm8521.cpp index ad9789087..db2584c14 100644 --- a/src/engine/platform/sm8521.cpp +++ b/src/engine/platform/sm8521.cpp @@ -144,10 +144,8 @@ void DivPlatformSM8521::tick(bool sysTick) { rWrite(freqMap[i][1],chan[i].freq&0xff); const unsigned char temp=regPool[0x40]; if (chan[i].keyOn) { - rWrite(0x40,temp|(1<song.brokenOutVol && !chan[c.chan].std.vol.will) { chan[c.chan].outVol=chan[c.chan].vol; } + if (!isMuted[c.chan]) { + rWrite(0x40,regPool[0x40]|0x80|(1< @@ -26,8 +26,8 @@ #include "sound/sm8521.h" class DivPlatformSM8521: public DivDispatch { - const unsigned char volMap[3]={0x42,0x44,0x4a}; + const unsigned char freqMap[3][2]={{0x46,0x47},{0x48,0x49},{0x4c,0x4d}}; struct Channel: public SharedChannel { @@ -36,7 +36,7 @@ class DivPlatformSM8521: public DivDispatch { bool volumeChanged; DivWaveSynth ws; Channel(): - SharedChannel(15), + SharedChannel(31), antiClickPeriodCount(0), antiClickWavePos(0), wave(-1), diff --git a/src/engine/platform/sound/sm8521.c b/src/engine/platform/sound/sm8521.c new file mode 100644 index 000000000..831dd6aa6 --- /dev/null +++ b/src/engine/platform/sound/sm8521.c @@ -0,0 +1,235 @@ +/* + +============================================================================ + +SM8521 sound emulator +by cam900 + +This file is licensed under zlib license. + +============================================================================ + +zlib License + +(C) 2022-present cam900 and contributors + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +============================================================================ + +TODO: +- needs hardware test + +*/ + +#include "sm8521.h" +#include + +enum sm8521_sgc +{ + SM8521_SGC_SONDOUT = (1 << 7), + SM8521_SGC_DIROUT = (1 << 3), + SM8521_SGC_SG2OUT = (1 << 2), + SM8521_SGC_SG1OUT = (1 << 1), + SM8521_SGC_SG0OUT = (1 << 0) +}; + +void sm8521_sg_wave_tick(struct sm8521_wave_t *sg, const int cycle) +{ + sg->base.counter += cycle; + while (sg->base.counter >= (sg->base.t + 1)) + { + sg->addr++; + sg->base.counter -= (sg->base.t + 1); + } + int wave = (sg->wave[(sg->addr >> 1) & 0xf] >> ((sg->addr & 1) << 2)) & 0xf; + if (wave & 0x8) + { + wave = -(0x8 - (wave & 0x7)); + } + sg->base.out = (wave * sg->base.level) >> 1; // scale out to 8bit +} + +void sm8521_noise_tick(struct sm8521_noise_t *noise, const int cycle) +{ + noise->base.counter += cycle; + while (noise->base.counter >= (noise->base.t + 1)) + { + noise->lfsr = rand() & 0x1; // unknown algorithm + noise->base.counter -= (noise->base.t + 1); + } + noise->base.out = (((noise->lfsr & 0x1) ? 7 : -8) * noise->base.level) >> 1; // scale out to 8bit +} + +void sm8521_sound_tick(struct sm8521_t *sm8521, const int cycle) +{ + int out = 0; + if (sm8521->sgc & SM8521_SGC_SONDOUT) + { + if (sm8521->sgc & SM8521_SGC_DIROUT) + { + out = sm8521->sgda - 0x80; + } + else + { + if (sm8521->sgc & SM8521_SGC_SG0OUT) + { + sm8521_sg_wave_tick(&sm8521->sg[0], cycle); + out += sm8521->sg[0].base.out; + } + if (sm8521->sgc & SM8521_SGC_SG1OUT) + { + sm8521_sg_wave_tick(&sm8521->sg[1], cycle); + out += sm8521->sg[1].base.out; + } + if (sm8521->sgc & SM8521_SGC_SG2OUT) + { + sm8521_noise_tick(&sm8521->noise, cycle); + out += sm8521->noise.base.out; + } + out = (out < -0x80) ? -0x80 : ((out > 0x7f) ? 0x7f : out); // clamp + } + } + sm8521->out = out; +} + +void sm8521_reset(struct sm8521_t *sm8521) +{ + for (int i = 0; i < 2; i++) + { + sm8521->sg[i].base.t = 0; + sm8521->sg[i].base.level = 0; + sm8521->sg[i].base.out = 0; + sm8521->sg[i].base.counter = 0; + sm8521->sg[i].addr = 0; + for (int j = 0; j < 16; j++) + { + sm8521->sg[i].wave[j] = 0; + } + } + sm8521->noise.base.t = 0; + sm8521->noise.base.level = 0; + sm8521->noise.base.out = 0; + sm8521->noise.base.counter = 0; + sm8521->noise.lfsr = 0; + sm8521->out = 0; + sm8521->sgda = 0; + sm8521->sgc = 0; +} + +unsigned char sm8521_read(struct sm8521_t *sm8521, const unsigned char a) +{ + if ((a & 0xe0) == 0x60) + { + if ((a & 0x10) && (!(sm8521->sgc & SM8521_SGC_SG1OUT))) // 0x70-0x7f SG1W0-15 + { + return sm8521->sg[1].wave[a & 0xf]; + } + else if ((!(a & 0x10)) && (!(sm8521->sgc & SM8521_SGC_SG0OUT))) // 0x60-0x6f SG0W0-15 + { + return sm8521->sg[0].wave[a & 0xf]; + } + return 0; + } + switch (a) + { + case 0x40: // SGC + return sm8521->sgc; + break; + case 0x42: // SG0L + return sm8521->sg[0].base.level & 0x1f; + break; + case 0x44: // SG1L + return sm8521->sg[1].base.level & 0x1f; + break; + case 0x46: // SG0TH + return (sm8521->sg[0].base.t >> 8) & 0xf; + break; + case 0x47: // SG0TL + return sm8521->sg[0].base.t & 0xff; + break; + case 0x48: // SG1TH + return (sm8521->sg[1].base.t >> 8) & 0xf; + break; + case 0x49: // SG1TL + return sm8521->sg[1].base.t & 0x0ff; + break; + case 0x4a: // SG2L + return sm8521->noise.base.level & 0x1f; + break; + case 0x4c: // SG2TH + return (sm8521->noise.base.t >> 8) & 0xf; + break; + case 0x4d: // SG2TL + return sm8521->noise.base.t & 0xff; + break; + default: + return 0; + break; + } +} +void sm8521_write(struct sm8521_t *sm8521, const unsigned char a, const unsigned char d) +{ + if ((a & 0xe0) == 0x60) + { + if ((a & 0x10) && (!(sm8521->sgc & SM8521_SGC_SG1OUT))) // 0x70-0x7f SG1W0-15 + { + sm8521->sg[1].wave[a & 0xf] = d; + } + else if ((!(a & 0x10)) && (!(sm8521->sgc & SM8521_SGC_SG0OUT))) // 0x60-0x6f SG0W0-15 + { + sm8521->sg[0].wave[a & 0xf] = d; + } + return; + } + switch (a) + { + case 0x40: // SGC + sm8521->sgc = d; + break; + case 0x42: // SG0L + sm8521->sg[0].base.level = d & 0x1f; + break; + case 0x44: // SG1L + sm8521->sg[1].base.level = d & 0x1f; + break; + case 0x46: // SG0TH + sm8521->sg[0].base.t = (sm8521->sg[0].base.t & 0x0ff) | ((d << 8) & 0xf00); + break; + case 0x47: // SG0TL + sm8521->sg[0].base.t = (sm8521->sg[0].base.t & 0xf00) | (d & 0x0ff); + break; + case 0x48: // SG1TH + sm8521->sg[1].base.t = (sm8521->sg[1].base.t & 0x0ff) | ((d << 8) & 0xf00); + break; + case 0x49: // SG1TL + sm8521->sg[1].base.t = (sm8521->sg[1].base.t & 0xf00) | (d & 0x0ff); + break; + case 0x4a: // SG2L + sm8521->noise.base.level = d & 0x1f; + break; + case 0x4c: // SG2TH + sm8521->noise.base.t = (sm8521->noise.base.t & 0x0ff) | ((d << 8) & 0xf00); + break; + case 0x4d: // SG2TL + sm8521->noise.base.t = (sm8521->noise.base.t & 0xf00) | (d & 0x0ff); + break; + case 0x4e: // SGDA + sm8521->sgda = d; + break; + } +} diff --git a/src/engine/platform/sound/sm8521.h b/src/engine/platform/sound/sm8521.h index 24635eb01..6577df004 100644 --- a/src/engine/platform/sound/sm8521.h +++ b/src/engine/platform/sound/sm8521.h @@ -36,22 +36,14 @@ TODO: */ -#include +#ifndef _SM8521_EMU_H +#define _SM8521_EMU_H #ifdef __cplusplus extern "C" { #endif -enum sm8521_sgc -{ - SM8521_SGC_SONDOUT = (1 << 7), - SM8521_SGC_DIROUT = (1 << 3), - SM8521_SGC_SG2OUT = (1 << 2), - SM8521_SGC_SG1OUT = (1 << 1), - SM8521_SGC_SG0OUT = (1 << 0) -}; - struct sm8521_sg_t { unsigned short t; // Time constant register @@ -82,192 +74,19 @@ struct sm8521_t unsigned char sgc; // Control register }; -void sm8521_sg_wave_tick(struct sm8521_wave_t *sg, const int cycle) -{ - sg->base.counter += cycle; - while (sg->base.counter >= (sg->base.t + 1)) - { - sg->addr++; - sg->base.counter -= (sg->base.t + 1); - } - int wave = (sg->wave[(sg->addr >> 1) & 0xf] >> ((sg->addr & 1) << 2)) & 0xf; - if (wave & 0x8) - { - wave = -(0x8 - (wave & 0x7)); - } - sg->base.out = (wave * sg->base.level) >> 1; // scale out to 8bit -} +void sm8521_sg_wave_tick(struct sm8521_wave_t *sg, const int cycle); -void sm8521_noise_tick(struct sm8521_noise_t *noise, const int cycle) -{ - noise->base.counter += cycle; - while (noise->base.counter >= (noise->base.t + 1)) - { - noise->lfsr = rand() & 0x1; // unknown algorithm - noise->base.counter -= (noise->base.t + 1); - } - noise->base.out = (((noise->lfsr & 0x1) ? 7 : -8) * noise->base.level) >> 1; // scale out to 8bit -} +void sm8521_noise_tick(struct sm8521_noise_t *noise, const int cycle); -void sm8521_sound_tick(struct sm8521_t *sm8521, const int cycle) -{ - int out = 0; - if (sm8521->sgc & SM8521_SGC_SONDOUT) - { - if (sm8521->sgc & SM8521_SGC_DIROUT) - { - out = sm8521->sgda - 0x80; - } - else - { - if (sm8521->sgc & SM8521_SGC_SG0OUT) - { - sm8521_sg_wave_tick(&sm8521->sg[0], cycle); - out += sm8521->sg[0].base.out; - } - if (sm8521->sgc & SM8521_SGC_SG1OUT) - { - sm8521_sg_wave_tick(&sm8521->sg[1], cycle); - out += sm8521->sg[1].base.out; - } - if (sm8521->sgc & SM8521_SGC_SG2OUT) - { - sm8521_noise_tick(&sm8521->noise, cycle); - out += sm8521->noise.base.out; - } - out = (out < -0x80) ? -0x80 : ((out > 0x7f) ? 0x7f : out); // clamp - } - } - sm8521->out = out; -} +void sm8521_sound_tick(struct sm8521_t *sm8521, const int cycle); -void sm8521_reset(struct sm8521_t *sm8521) -{ - for (int i = 0; i < 2; i++) - { - sm8521->sg[i].base.t = 0; - sm8521->sg[i].base.level = 0; - sm8521->sg[i].base.out = 0; - sm8521->sg[i].base.counter = 0; - sm8521->sg[i].addr = 0; - for (int j = 0; j < 16; j++) - { - sm8521->sg[i].wave[j] = 0; - } - } - sm8521->noise.base.t = 0; - sm8521->noise.base.level = 0; - sm8521->noise.base.out = 0; - sm8521->noise.base.counter = 0; - sm8521->noise.lfsr = 0; - sm8521->out = 0; - sm8521->sgda = 0; - sm8521->sgc = 0; -} +void sm8521_reset(struct sm8521_t *sm8521); -unsigned char sm8521_read(struct sm8521_t *sm8521, const unsigned char a) -{ - if ((a & 0xe0) == 0x60) - { - if ((a & 0x10) && (!(sm8521->sgc & SM8521_SGC_SG1OUT))) // 0x70-0x7f SG1W0-15 - { - return sm8521->sg[1].wave[a & 0xf]; - } - else if ((!(a & 0x10)) && (!(sm8521->sgc & SM8521_SGC_SG0OUT))) // 0x60-0x6f SG0W0-15 - { - return sm8521->sg[0].wave[a & 0xf]; - } - return 0; - } - switch (a) - { - case 0x40: // SGC - return sm8521->sgc; - break; - case 0x42: // SG0L - return sm8521->sg[0].base.level & 0x1f; - break; - case 0x44: // SG1L - return sm8521->sg[1].base.level & 0x1f; - break; - case 0x46: // SG0TH - return (sm8521->sg[0].base.t >> 8) & 0xf; - break; - case 0x47: // SG0TL - return sm8521->sg[0].base.t & 0xff; - break; - case 0x48: // SG1TH - return (sm8521->sg[1].base.t >> 8) & 0xf; - break; - case 0x49: // SG1TL - return sm8521->sg[1].base.t & 0x0ff; - break; - case 0x4a: // SG2L - return sm8521->noise.base.level & 0x1f; - break; - case 0x4c: // SG2TH - return (sm8521->noise.base.t >> 8) & 0xf; - break; - case 0x4d: // SG2TL - return sm8521->noise.base.t & 0xff; - break; - default: - return 0; - break; - } -} -void sm8521_write(struct sm8521_t *sm8521, const unsigned char a, const unsigned char d) -{ - if ((a & 0xe0) == 0x60) - { - if ((a & 0x10) && (!(sm8521->sgc & SM8521_SGC_SG1OUT))) // 0x70-0x7f SG1W0-15 - { - sm8521->sg[1].wave[a & 0xf] = d; - } - else if ((!(a & 0x10)) && (!(sm8521->sgc & SM8521_SGC_SG0OUT))) // 0x60-0x6f SG0W0-15 - { - sm8521->sg[0].wave[a & 0xf] = d; - } - return; - } - switch (a) - { - case 0x40: // SGC - sm8521->sgc = d; - break; - case 0x42: // SG0L - sm8521->sg[0].base.level = d & 0x1f; - break; - case 0x44: // SG1L - sm8521->sg[1].base.level = d & 0x1f; - break; - case 0x46: // SG0TH - sm8521->sg[0].base.t = (sm8521->sg[0].base.t & 0x0ff) | ((d << 8) & 0xf00); - break; - case 0x47: // SG0TL - sm8521->sg[0].base.t = (sm8521->sg[0].base.t & 0xf00) | (d & 0x0ff); - break; - case 0x48: // SG1TH - sm8521->sg[1].base.t = (sm8521->sg[1].base.t & 0x0ff) | ((d << 8) & 0xf00); - break; - case 0x49: // SG1TL - sm8521->sg[1].base.t = (sm8521->sg[1].base.t & 0xf00) | (d & 0x0ff); - break; - case 0x4a: // SG2L - sm8521->noise.base.level = d & 0x1f; - break; - case 0x4c: // SG2TH - sm8521->noise.base.t = (sm8521->noise.base.t & 0x0ff) | ((d << 8) & 0xf00); - break; - case 0x4d: // SG2TL - sm8521->noise.base.t = (sm8521->noise.base.t & 0xf00) | (d & 0x0ff); - break; - case 0x4e: // SGDA - sm8521->sgda = d; - break; - } -} +unsigned char sm8521_read(struct sm8521_t *sm8521, const unsigned char a); +void sm8521_write(struct sm8521_t *sm8521, const unsigned char a, const unsigned char d); #ifdef __cplusplus } // extern "C" #endif + +#endif // _SM8521_EMU_H \ No newline at end of file diff --git a/src/engine/song.h b/src/engine/song.h index 72fbea0ef..0cf1f0195 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -124,7 +124,8 @@ enum DivSystem { DIV_SYSTEM_YM2610_CSM, DIV_SYSTEM_YM2610B_CSM, DIV_SYSTEM_YM2203_CSM, - DIV_SYSTEM_YM2608_CSM + DIV_SYSTEM_YM2608_CSM, + DIV_SYSTEM_SM8521 }; struct DivGroovePattern { diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index c6368cc84..09cfdc414 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1822,6 +1822,17 @@ void DivEngine::registerSystems() { {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} ); + sysDefs[DIV_SYSTEM_SM8521]=new DivSysDef( + "Sharp SM8521", NULL, 0xc8, 0, 3, false, true, 0, false, 0, + "a SoC with wavetable sound hardware", + {"Channel 1", "Channel 2", "Noise"}, + {"CH1", "CH2", "NOI"}, + {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_NOISE}, + {DIV_INS_NAMCO, DIV_INS_NAMCO, DIV_INS_NAMCO}, + {}, + namcoEffectHandlerMap + ); + sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef( "Dummy System", NULL, 0xfd, 0, 8, false, true, 0, false, 0, "this is a system designed for testing purposes.", diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index be1573778..4997c28fb 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -998,6 +998,7 @@ const int availableSystems[]={ DIV_SYSTEM_MSM5232, DIV_SYSTEM_K007232, DIV_SYSTEM_GA20, + DIV_SYSTEM_SM8521, DIV_SYSTEM_PCM_DAC, DIV_SYSTEM_PONG, 0 // don't remove this last one! @@ -1084,6 +1085,7 @@ const int chipsSpecial[]={ DIV_SYSTEM_PET, DIV_SYSTEM_VRC6, DIV_SYSTEM_MMC5, + DIV_SYSTEM_SM8521, 0 // don't remove this last one! }; diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index 579cf2d38..3027be8c3 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -2575,6 +2575,11 @@ void FurnaceGUI::initSystemPresets() { CH(DIV_SYSTEM_SFX_BEEPER, 1.0f, 0, "") } ); + ENTRY( + "Sharp SM8521", { + CH(DIV_SYSTEM_SM8521, 1.0f, 0, "") + } + ); if (settings.hiddenSystems) { ENTRY( "Dummy System", { From 98b34ab7cec3dfe06ba118b755eb26a1e845be5b Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 11 Feb 2023 21:50:44 +0900 Subject: [PATCH 04/13] Output fix --- src/engine/platform/sm8521.cpp | 25 ++++++++++++++++--------- src/engine/platform/sound/sm8521.h | 2 +- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/engine/platform/sm8521.cpp b/src/engine/platform/sm8521.cpp index db2584c14..c72abcd9a 100644 --- a/src/engine/platform/sm8521.cpp +++ b/src/engine/platform/sm8521.cpp @@ -24,7 +24,7 @@ //#define rWrite(a,v) pendingWrites[a]=v; #define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} } -#define CHIP_DIVIDER 16 +#define CHIP_DIVIDER 64 const char* regCheatSheetSM8521[]={ "SGC", "40", @@ -55,11 +55,12 @@ void DivPlatformSM8521::acquire(short** buf, size_t len) { writes.pop(); } for (size_t h=0; hdata[oscBuf[i]->needle++]=sm8521.sg[i].base.out; + oscBuf[i]->data[oscBuf[i]->needle++]=sm8521.sg[i].base.out<<6; } - oscBuf[2]->data[oscBuf[2]->needle++]=sm8521.noise.base.out; + oscBuf[2]->data[oscBuf[2]->needle++]=sm8521.noise.base.out<<6; } } @@ -80,6 +81,7 @@ void DivPlatformSM8521::updateWave(int ch) { } void DivPlatformSM8521::tick(bool sysTick) { + unsigned char keyState=0x80; for (int i=0; i<3; i++) { // anti-click if (antiClickEnabled && sysTick && chan[i].freq>0) { @@ -151,6 +153,15 @@ void DivPlatformSM8521::tick(bool sysTick) { if (chan[i].keyOff) chan[i].keyOff=false; chan[i].freqChanged=false; } + if (!isMuted[i] && chan[i].active) { + keyState|=(1<song.brokenOutVol && !chan[c.chan].std.vol.will) { chan[c.chan].outVol=chan[c.chan].vol; } - if (!isMuted[c.chan]) { - rWrite(0x40,regPool[0x40]|0x80|(1<rate=rate; } diff --git a/src/engine/platform/sound/sm8521.h b/src/engine/platform/sound/sm8521.h index 6577df004..c430ebd4e 100644 --- a/src/engine/platform/sound/sm8521.h +++ b/src/engine/platform/sound/sm8521.h @@ -48,7 +48,7 @@ struct sm8521_sg_t { unsigned short t; // Time constant register unsigned char level; // Output level control register - unsigned short out; // output + signed short out; // output int counter; // clock counter }; From 5c06470145652f725d74b008d423e9a201094bd9 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 11 Feb 2023 21:56:41 +0900 Subject: [PATCH 05/13] Add SM8521 instrument type --- src/engine/instrument.cpp | 4 ++++ src/engine/instrument.h | 1 + src/engine/sysDef.cpp | 2 +- src/gui/dataList.cpp | 4 ++++ src/gui/gui.h | 1 + src/gui/guiConst.cpp | 2 ++ src/gui/insEdit.cpp | 9 ++++++--- src/gui/settings.cpp | 1 + 8 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index cc1755028..798369782 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -924,6 +924,10 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) { break; case DIV_INS_POKEMINI: break; + case DIV_INS_SM8521: + checkForWL=true; + if (ws.enabled) featureWS=true; + break; case DIV_INS_MAX: break; diff --git a/src/engine/instrument.h b/src/engine/instrument.h index 8829f6ce8..5b48ffb4e 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -78,6 +78,7 @@ enum DivInstrumentType: unsigned short { DIV_INS_K007232=45, DIV_INS_GA20=46, DIV_INS_POKEMINI=47, + DIV_INS_SM8521=48, DIV_INS_MAX, DIV_INS_NULL }; diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 09cfdc414..b05ee5de0 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1828,7 +1828,7 @@ void DivEngine::registerSystems() { {"Channel 1", "Channel 2", "Noise"}, {"CH1", "CH2", "NOI"}, {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_NOISE}, - {DIV_INS_NAMCO, DIV_INS_NAMCO, DIV_INS_NAMCO}, + {DIV_INS_SM8521, DIV_INS_SM8521, DIV_INS_SM8521}, {}, namcoEffectHandlerMap ); diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp index cce46444b..2882a92e9 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -430,6 +430,10 @@ void FurnaceGUI::drawInsList(bool asChild) { ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_POKEMINI]); name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i); break; + case DIV_INS_SM8521: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SM8521]); + name=fmt::sprintf(ICON_FA_PIE_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/gui.h b/src/gui/gui.h index cb7d950df..ea4b10a71 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -189,6 +189,7 @@ enum FurnaceGUIColors { GUI_COLOR_INSTR_K007232, GUI_COLOR_INSTR_GA20, GUI_COLOR_INSTR_POKEMINI, + GUI_COLOR_INSTR_SM8521, GUI_COLOR_INSTR_UNKNOWN, GUI_COLOR_CHANNEL_BG, diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 4997c28fb..d339ef82e 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -129,6 +129,7 @@ const char* insTypes[DIV_INS_MAX+1]={ "K007232", "GA20", "Pokémon Mini", + "SM8521", NULL }; @@ -818,6 +819,7 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_INSTR_K007232,"",ImVec4(1.0f,0.8f,0.1f,1.0f)), D(GUI_COLOR_INSTR_GA20,"",ImVec4(0.1f,1.0f,0.4f,1.0f)), D(GUI_COLOR_INSTR_POKEMINI,"",ImVec4(1.0f,1.0f,0.3f,1.0f)), + D(GUI_COLOR_INSTR_SM8521,"",ImVec4(1.0f,1.0f,0.0f,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)), diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 93a457cd7..6832f19d2 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -4762,11 +4762,13 @@ void FurnaceGUI::drawInsEdit() { (ins->type==DIV_INS_VBOY) || ins->type==DIV_INS_SCC || ins->type==DIV_INS_SNES || - ins->type==DIV_INS_NAMCO) { + ins->type==DIV_INS_NAMCO || + ins->type==DIV_INS_SM8521) { if (ImGui::BeginTabItem("Wavetable")) { switch (ins->type) { case DIV_INS_GB: case DIV_INS_NAMCO: + case DIV_INS_SM8521: case DIV_INS_SWAN: wavePreviewLen=32; wavePreviewHeight=15; @@ -4968,7 +4970,7 @@ void FurnaceGUI::drawInsEdit() { } } } - if (ins->type==DIV_INS_PCE || ins->type==DIV_INS_AY8930) { + if (ins->type==DIV_INS_PCE || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_SM8521) { volMax=31; } if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_VERA || ins->type==DIV_INS_VRC6_SAW) { @@ -5067,7 +5069,8 @@ 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_VIC || ins->type==DIV_INS_SEGAPCM || - ins->type==DIV_INS_FM || ins->type==DIV_INS_K007232 || ins->type==DIV_INS_GA20) { + ins->type==DIV_INS_FM || ins->type==DIV_INS_K007232 || ins->type==DIV_INS_GA20 || + ins->type==DIV_INS_SM8521) { dutyMax=0; } if (ins->type==DIV_INS_VBOY) { diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 1d9fcb6dc..e80d9fa01 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1907,6 +1907,7 @@ void FurnaceGUI::drawSettings() { UI_COLOR_CONFIG(GUI_COLOR_INSTR_K007232,"K007232"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_GA20,"GA20"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_POKEMINI,"Pokémon Mini"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_SM8521,"SM8521"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_UNKNOWN,"Other/Unknown"); ImGui::TreePop(); } From 28c6b1dc60e790c045bd31e7e8f1e88e8836ed1a Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 11 Feb 2023 22:02:00 +0900 Subject: [PATCH 06/13] Fix copyright year --- src/engine/platform/sound/sm8521.c | 2 +- src/engine/platform/sound/sm8521.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/sound/sm8521.c b/src/engine/platform/sound/sm8521.c index 831dd6aa6..b2410fe60 100644 --- a/src/engine/platform/sound/sm8521.c +++ b/src/engine/platform/sound/sm8521.c @@ -11,7 +11,7 @@ This file is licensed under zlib license. zlib License -(C) 2022-present cam900 and contributors +(C) 2023-present cam900 and contributors This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/src/engine/platform/sound/sm8521.h b/src/engine/platform/sound/sm8521.h index c430ebd4e..e41b884e8 100644 --- a/src/engine/platform/sound/sm8521.h +++ b/src/engine/platform/sound/sm8521.h @@ -11,7 +11,7 @@ This file is licensed under zlib license. zlib License -(C) 2022-present cam900 and contributors +(C) 2023-present cam900 and contributors This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages From d26f2149e718b147e14b1586c70144e202bbfd76 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 11 Feb 2023 22:03:24 +0900 Subject: [PATCH 07/13] Fix newline --- src/engine/platform/sound/sm8521.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/platform/sound/sm8521.h b/src/engine/platform/sound/sm8521.h index e41b884e8..a746618a1 100644 --- a/src/engine/platform/sound/sm8521.h +++ b/src/engine/platform/sound/sm8521.h @@ -89,4 +89,4 @@ void sm8521_write(struct sm8521_t *sm8521, const unsigned char a, const unsigned } // extern "C" #endif -#endif // _SM8521_EMU_H \ No newline at end of file +#endif // _SM8521_EMU_H From 0cd77c4ca7370c114758deee7ec8ff647e055ead Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 11 Feb 2023 22:12:32 +0900 Subject: [PATCH 08/13] Fix sign --- src/engine/platform/sm8521.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/sm8521.cpp b/src/engine/platform/sm8521.cpp index c72abcd9a..a594dca37 100644 --- a/src/engine/platform/sm8521.cpp +++ b/src/engine/platform/sm8521.cpp @@ -69,8 +69,8 @@ void DivPlatformSM8521::updateWave(int ch) { const unsigned char temp=regPool[0x40]; rWrite(0x40,temp&~(1< Date: Sat, 11 Feb 2023 22:18:47 +0900 Subject: [PATCH 09/13] Remove unused variable --- src/engine/platform/sm8521.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/engine/platform/sm8521.cpp b/src/engine/platform/sm8521.cpp index a594dca37..6c228c0d3 100644 --- a/src/engine/platform/sm8521.cpp +++ b/src/engine/platform/sm8521.cpp @@ -144,7 +144,6 @@ void DivPlatformSM8521::tick(bool sysTick) { if (chan[i].freq>4095) chan[i].freq=4095; rWrite(freqMap[i][0],chan[i].freq>>8); rWrite(freqMap[i][1],chan[i].freq&0xff); - const unsigned char temp=regPool[0x40]; if (chan[i].keyOn) { } if (chan[i].keyOff) { From 13691c5482320d2e71ce68a993caa026b8c3e77f Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 11 Feb 2023 23:08:01 +0900 Subject: [PATCH 10/13] Fix base clock --- src/engine/platform/sm8521.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/platform/sm8521.cpp b/src/engine/platform/sm8521.cpp index 6c228c0d3..e0e0e1010 100644 --- a/src/engine/platform/sm8521.cpp +++ b/src/engine/platform/sm8521.cpp @@ -363,7 +363,7 @@ void DivPlatformSM8521::notifyInsDeletion(void* ins) { void DivPlatformSM8521::setFlags(const DivConfig& flags) { antiClickEnabled=!flags.getBool("noAntiClick",false); - chipClock=10000000; + chipClock=11059200; CHECK_CUSTOM_CLOCK; rate=chipClock/4/8; for (int i=0; i<3; i++) { From c38ed5f5adacbb268cac588e1a802d163ce5a7ec Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 11 Feb 2023 23:09:01 +0900 Subject: [PATCH 11/13] Add clock divider note --- src/engine/platform/sm8521.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/platform/sm8521.cpp b/src/engine/platform/sm8521.cpp index e0e0e1010..9bbce0567 100644 --- a/src/engine/platform/sm8521.cpp +++ b/src/engine/platform/sm8521.cpp @@ -365,7 +365,7 @@ void DivPlatformSM8521::setFlags(const DivConfig& flags) { antiClickEnabled=!flags.getBool("noAntiClick",false); chipClock=11059200; CHECK_CUSTOM_CLOCK; - rate=chipClock/4/8; + rate=chipClock/4/8; // CKIN -> fCLK(/2) -> Function blocks (/2) for (int i=0; i<3; i++) { oscBuf[i]->rate=rate; } From 598fdb46b261f437f4eac4ebd9664d880e5719d1 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 11 Feb 2023 23:10:48 +0900 Subject: [PATCH 12/13] Add anti-click config --- src/gui/sysConf.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index f76c9ffd8..b3783ddfa 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -1694,6 +1694,20 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo } break; } + case DIV_SYSTEM_SM8521: { + bool noAntiClick=flags.getBool("noAntiClick",false); + + if (ImGui::Checkbox("Disable anti-click",&noAntiClick)) { + altered=true; + } + + if (altered) { + e->lockSave([&]() { + flags.set("noAntiClick",noAntiClick); + }); + } + break; + } case DIV_SYSTEM_SWAN: case DIV_SYSTEM_BUBSYS_WSG: case DIV_SYSTEM_PET: From 1f8bc8e0ed08397794863b8dafd997c4c36cacca Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 11 Feb 2023 17:42:02 -0500 Subject: [PATCH 13/13] update format --- papers/format.md | 1 + papers/newIns.md | 1 + src/engine/sysDef.cpp | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/papers/format.md b/papers/format.md index 895cc41e9..cdbf57082 100644 --- a/papers/format.md +++ b/papers/format.md @@ -284,6 +284,7 @@ size | description | - 0xc5: YM2610B CSM - 20 channels | - 0xc6: K007232 - 2 channels | - 0xc7: GA20 - 4 channels + | - 0xc8: SM8521 - 3 channels | - 0xde: YM2610B extended - 19 channels | - 0xe0: QSound - 19 channels | - 0xfc: Pong - 1 channel diff --git a/papers/newIns.md b/papers/newIns.md index 31dff219c..5a328c963 100644 --- a/papers/newIns.md +++ b/papers/newIns.md @@ -115,6 +115,7 @@ the following instrument types are available: - 45: K007232 - 46: GA20 - 47: Pokémon Mini +- 48: SM8521 the following feature codes are recognized: diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index b05ee5de0..fe7606e0d 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1824,9 +1824,9 @@ void DivEngine::registerSystems() { sysDefs[DIV_SYSTEM_SM8521]=new DivSysDef( "Sharp SM8521", NULL, 0xc8, 0, 3, false, true, 0, false, 0, - "a SoC with wavetable sound hardware", + "a SoC with wavetable sound hardware.", {"Channel 1", "Channel 2", "Noise"}, - {"CH1", "CH2", "NOI"}, + {"CH1", "CH2", "NS"}, {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_NOISE}, {DIV_INS_SM8521, DIV_INS_SM8521, DIV_INS_SM8521}, {},