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", {