From bf8d51ca83b0fd0b09e27a4548d723dc5fcd061c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 24 Aug 2024 18:42:23 -0500 Subject: [PATCH] implement operator mask effect --- doc/6-sample/README.md | 2 +- doc/7-systems/genesis.md | 65 --------------------------------- doc/7-systems/ym2151.md | 3 ++ doc/7-systems/ym2203.md | 3 ++ doc/7-systems/ym2608.md | 3 ++ doc/7-systems/ym2610.md | 3 ++ doc/7-systems/ym2610b.md | 3 ++ doc/7-systems/ym2612.md | 3 ++ src/engine/dispatch.h | 2 + src/engine/platform/arcade.cpp | 6 +++ src/engine/platform/genesis.cpp | 7 ++++ src/engine/platform/tx81z.cpp | 3 ++ src/engine/platform/ym2203.cpp | 7 ++++ src/engine/platform/ym2608.cpp | 7 ++++ src/engine/platform/ym2610.cpp | 7 ++++ src/engine/platform/ym2610b.cpp | 7 ++++ src/engine/playback.cpp | 4 +- src/engine/sysDef.cpp | 2 + 18 files changed, 70 insertions(+), 67 deletions(-) delete mode 100644 doc/7-systems/genesis.md diff --git a/doc/6-sample/README.md b/doc/6-sample/README.md index 5ebe571c1..efad91458 100644 --- a/doc/6-sample/README.md +++ b/doc/6-sample/README.md @@ -61,7 +61,7 @@ if you need to use more samples, you may change the sample bank using effect `EB due to limitations in some of those sound chips, some restrictions exist: - Amiga: maximum frequency is 31469Hz, but anything over 28867 will sound glitchy on hardware. sample lengths and loop will be set to an even number, and your sample can't be longer than 131070. -- NES: if on DPCM mode, only a limited selection of frequencies is available, and loop position isn't supported (only entire sample). +- NES: if on DPCM mode, only a limited selection of frequencies is available. - SegaPCM: your sample can't be longer than 65535, and the maximum frequency is 31.25KHz. - QSound: your sample can't be longer than 65535, and the loop length shall not be greater than 32767. - ADPCM-A: no looping supported. your samples will play at around 18.518KHz. diff --git a/doc/7-systems/genesis.md b/doc/7-systems/genesis.md deleted file mode 100644 index ed310ba13..000000000 --- a/doc/7-systems/genesis.md +++ /dev/null @@ -1,65 +0,0 @@ -# Sega Genesis/Mega Drive - -a video game console that showed itself as the first true rival to Nintendo's video game market near-monopoly in the US during the '80s. - -this console is powered by two sound chips: the [Yamaha YM2612](ym2612.md) and [a derivative of the SN76489](sms.md). - -## effects - -- `10xy`: **set LFO parameters.** - - `x` toggles the LFO. - - `y` sets its speed. -- `11xx`: **set feedback of channel.** -- `12xx`: **set operator 1 level.** -- `13xx`: **set operator 2 level.** -- `14xx`: **set operator 3 level.** -- `15xx`: **set operator 4 level.** -- `16xy`: **set multiplier of operator.** - - `x` is the operator (1-4). - - `y` is the new MULT value.. -- `17xx`: **enable PCM channel.** - - this only works on channel 6. - - _this effect is here for compatibility reasons!_ it is otherwise recommended to use Sample type instruments (which automatically enable PCM mode when used). -- `18xx`: **toggle extended channel 3 mode.** - - `0` disables it and `1` enables it. - - only in extended channel 3 chip. -- `19xx`: **set attack of all operators.** -- `1Axx`: **set attack of operator 1.** -- `1Bxx`: **set attack of operator 2.** -- `1Cxx`: **set attack of operator 3.** -- `1Dxx`: **set attack of operator 4.** -- `20xy`: **set PSG noise mode.** - - `x` controls whether to inherit frequency from PSG channel 3. - - `0`: use one of 3 preset frequencies (`C`: A-2; `C#`: A-3; `D`: A-4). - - `1`: use frequency of PSG channel 3. - - `y` controls whether to select noise or thin pulse. - - `0`: thin pulse. - - `1`: noise. - - - -## system modes - -## extended channel 3 - -in ExtCh mode, channel 3 is split into one column for each of its four operators. feedback and LFO levels are shared. the frequency of each operator may be controlled independently with notes and effects. this can be used for more polyphony or more complex sounds. - -all four operators are still combined according to the algorithm in use. for example, algorithm 7 acts as four independent sine waves. algorithm 4 acts as two independent 2-op sounds. even with algorithm 0, placing a note in any operator triggers that operator alone. - -## CSM - -CSM is short for "Composite Sinusoidal Modeling". CSM works by sending key-on and key-off commands to channel 3 at a specific frequency, controlled by the added "CSM Timer" channel. this can be used to create vocal formants (speech synthesis!) or other complex effects. - -CSM is beyond the scope of this documentation. for more information, see this [brief SSG-EG and CSM video tutorial](https://www.youtube.com/watch?v=IKOR0TUlnWU). - -## DualPCM - -[info here.](ym2612.md) - -## Sega CD - -this isn't a mode so much as a chip configuration. it adds the [Ricoh RF5C68](ricoh.md) found in the Sega CD add-on, providing 8 channels of PCM. - -## chip config - -see [YM2612](ym2612.md) and [SN76489](sms.md) for respective configuration. diff --git a/doc/7-systems/ym2151.md b/doc/7-systems/ym2151.md index 298401704..cf07811bc 100644 --- a/doc/7-systems/ym2151.md +++ b/doc/7-systems/ym2151.md @@ -68,6 +68,9 @@ in most arcade boards the chip was used in combination with a PCM chip, like [Se - `5Dxx`: **set D2R/SR of operator 2.** - `5Exx`: **set D2R/SR of operator 3.** - `5Fxx`: **set D2R/SR of operator 4.** +- `60xx`: **set operator mask.** + - `xx` goes from `0` to `F`. it is a bitfield. + - each bit corresponds to an operator. ## info diff --git a/doc/7-systems/ym2203.md b/doc/7-systems/ym2203.md index a89436498..83356c05a 100644 --- a/doc/7-systems/ym2203.md +++ b/doc/7-systems/ym2203.md @@ -99,6 +99,9 @@ several variants of this chip were released as well, with more features. - `5Dxx`: **set D2R/SR of operator 2.** - `5Exx`: **set D2R/SR of operator 3.** - `5Fxx`: **set D2R/SR of operator 4.** +- `60xx`: **set operator mask.** + - `xx` goes from `0` to `F`. it is a bitfield. + - each bit corresponds to an operator. ## extended channel 3 diff --git a/doc/7-systems/ym2608.md b/doc/7-systems/ym2608.md index 1bbd272cb..bca23c1d9 100644 --- a/doc/7-systems/ym2608.md +++ b/doc/7-systems/ym2608.md @@ -99,6 +99,9 @@ the YM2610 (OPNB) and YM2610B chips are very similar to this one, but the built- - `5Dxx`: **set D2R/SR of operator 2.** - `5Exx`: **set D2R/SR of operator 3.** - `5Fxx`: **set D2R/SR of operator 4.** +- `60xx`: **set operator mask.** + - `xx` goes from `0` to `F`. it is a bitfield. + - each bit corresponds to an operator. ## extended channel 3 diff --git a/doc/7-systems/ym2610.md b/doc/7-systems/ym2610.md index 15f479933..449bc5a35 100644 --- a/doc/7-systems/ym2610.md +++ b/doc/7-systems/ym2610.md @@ -97,6 +97,9 @@ its soundchip is a 4-in-1: 4ch 4-op FM, YM2149 (AY-3-8910 clone) and [2 differen - `5Dxx`: **set D2R/SR of operator 2.** - `5Exx`: **set D2R/SR of operator 3.** - `5Fxx`: **set D2R/SR of operator 4.** +- `60xx`: **set operator mask.** + - `xx` goes from `0` to `F`. it is a bitfield. + - each bit corresponds to an operator. ## extended channel 2 diff --git a/doc/7-systems/ym2610b.md b/doc/7-systems/ym2610b.md index d91ed2221..df9f036c7 100644 --- a/doc/7-systems/ym2610b.md +++ b/doc/7-systems/ym2610b.md @@ -96,6 +96,9 @@ it is backward compatible with the original chip. - `5Dxx`: **set D2R/SR of operator 2.** - `5Exx`: **set D2R/SR of operator 3.** - `5Fxx`: **set D2R/SR of operator 4.** +- `60xx`: **set operator mask.** + - `xx` goes from `0` to `F`. it is a bitfield. + - each bit corresponds to an operator. ## extended channel 3 diff --git a/doc/7-systems/ym2612.md b/doc/7-systems/ym2612.md index 852dfa9ee..989a6d41a 100644 --- a/doc/7-systems/ym2612.md +++ b/doc/7-systems/ym2612.md @@ -82,6 +82,9 @@ thanks to the Z80 sound CPU, DualPCM can play two samples at once! this mode spl - `5Dxx`: **set D2R/SR of operator 2.** - `5Exx`: **set D2R/SR of operator 3.** - `5Fxx`: **set D2R/SR of operator 4.** +- `60xx`: **set operator mask.** + - `xx` goes from `0` to `F`. it is a bitfield. + - each bit corresponds to an operator. ## info diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 517adc662..f207ae359 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -266,6 +266,8 @@ enum DivDispatchCmds { DIV_CMD_FDS_MOD_AUTO, + DIV_CMD_FM_OPMASK, // (mask) + DIV_CMD_MAX }; diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index 914453271..74cfcc6e7 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -761,6 +761,12 @@ int DivPlatformArcade::dispatch(DivCommand c) { immWrite(0x19,0x80|pmDepth); break; } + case DIV_CMD_FM_OPMASK: + chan[c.chan].opMask=c.value&15; + if (chan[c.chan].active) { + chan[c.chan].opMaskChanged=true; + } + break; case DIV_CMD_FM_HARD_RESET: chan[c.chan].hardReset=c.value; break; diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index e7533dec7..1b8707896 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -1463,6 +1463,13 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } break; } + case DIV_CMD_FM_OPMASK: + if (c.chan>=psgChanOffs) break; + chan[c.chan].opMask=c.value&15; + if (chan[c.chan].active) { + chan[c.chan].opMaskChanged=true; + } + break; case DIV_CMD_FM_HARD_RESET: if (c.chan>=6) break; chan[c.chan].hardReset=c.value; diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index 3fee5e04e..600ae724e 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -858,6 +858,9 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { immWrite(0x17,0x80|pmDepth); break; } + case DIV_CMD_FM_OPMASK: + // TODO: if OPZ supports op mask + break; case DIV_CMD_FM_HARD_RESET: chan[c.chan].hardReset=c.value; break; diff --git a/src/engine/platform/ym2203.cpp b/src/engine/platform/ym2203.cpp index 5a92c4dc9..2a19fea0f 100644 --- a/src/engine/platform/ym2203.cpp +++ b/src/engine/platform/ym2203.cpp @@ -1004,6 +1004,13 @@ int DivPlatformYM2203::dispatch(DivCommand c) { } break; } + case DIV_CMD_FM_OPMASK: + if (c.chan>=psgChanOffs) break; + chan[c.chan].opMask=c.value&15; + if (chan[c.chan].active) { + chan[c.chan].opMaskChanged=true; + } + break; case DIV_CMD_FM_HARD_RESET: chan[c.chan].hardReset=c.value; break; diff --git a/src/engine/platform/ym2608.cpp b/src/engine/platform/ym2608.cpp index 76c67272d..046e20c20 100644 --- a/src/engine/platform/ym2608.cpp +++ b/src/engine/platform/ym2608.cpp @@ -1537,6 +1537,13 @@ int DivPlatformYM2608::dispatch(DivCommand c) { } break; } + case DIV_CMD_FM_OPMASK: + if (c.chan>=psgChanOffs) break; + chan[c.chan].opMask=c.value&15; + if (chan[c.chan].active) { + chan[c.chan].opMaskChanged=true; + } + break; case DIV_CMD_FM_HARD_RESET: chan[c.chan].hardReset=c.value; break; diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index adda4bc7e..038cbc807 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -1507,6 +1507,13 @@ int DivPlatformYM2610::dispatch(DivCommand c) { } break; } + case DIV_CMD_FM_OPMASK: + if (c.chan>=psgChanOffs) break; + chan[c.chan].opMask=c.value&15; + if (chan[c.chan].active) { + chan[c.chan].opMaskChanged=true; + } + break; case DIV_CMD_FM_HARD_RESET: chan[c.chan].hardReset=c.value; break; diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index e4d0dca85..8b12ca862 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -1576,6 +1576,13 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { } break; } + case DIV_CMD_FM_OPMASK: + if (c.chan>=psgChanOffs) break; + chan[c.chan].opMask=c.value&15; + if (chan[c.chan].active) { + chan[c.chan].opMaskChanged=true; + } + break; case DIV_CMD_FM_HARD_RESET: chan[c.chan].hardReset=c.value; break; diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 296183f8c..c886c9e87 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -264,7 +264,9 @@ const char* cmdName[]={ "BIFURCATOR_STATE_LOAD", "BIFURCATOR_PARAMETER", - "FDS_MOD_AUTO" + "FDS_MOD_AUTO", + + "FM_OPMASK" }; static_assert((sizeof(cmdName)/sizeof(void*))==DIV_CMD_MAX,"update cmdName!"); diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index a8e50461e..3d2850828 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -504,6 +504,7 @@ void DivEngine::registerSystems() { {0x5d, {DIV_CMD_FM_D2R, _("5Dxx: Set decay 2 of operator 2 (0 to 1F)"), constVal<1>, effectValAnd<31>}}, {0x5e, {DIV_CMD_FM_D2R, _("5Exx: Set decay 2 of operator 3 (0 to 1F)"), constVal<2>, effectValAnd<31>}}, {0x5f, {DIV_CMD_FM_D2R, _("5Fxx: Set decay 2 of operator 4 (0 to 1F)"), constVal<3>, effectValAnd<31>}}, + {0x60, {DIV_CMD_FM_OPMASK, _("60xx: Set operator mask (bits 0-3)")}}, }; EffectHandlerMap fmOPMPostEffectHandlerMap(fmOPNPostEffectHandlerMap); @@ -514,6 +515,7 @@ void DivEngine::registerSystems() { {0x1e, {DIV_CMD_FM_AM_DEPTH, _("1Exx: Set AM depth (0 to 7F)"), effectValAnd<127>}}, {0x1f, {DIV_CMD_FM_PM_DEPTH, _("1Fxx: Set PM depth (0 to 7F)"), effectValAnd<127>}}, {0x55, {DIV_CMD_FM_SSG, _("55xy: Set detune 2 (x: operator from 1 to 4 (0 for all ops); y: detune from 0 to 3)"), effectOpVal<4>, effectValAnd<3>}}, + {0x60, {DIV_CMD_FM_OPMASK, _("60xx: Set operator mask (bits 0-3)")}}, }); EffectHandlerMap fmOPZPostEffectHandlerMap(fmOPMPostEffectHandlerMap);