summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound/ymfm.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/sound/ymfm.cpp')
-rw-r--r--src/devices/sound/ymfm.cpp2931
1 files changed, 2240 insertions, 691 deletions
diff --git a/src/devices/sound/ymfm.cpp b/src/devices/sound/ymfm.cpp
index 52250dfefc5..4fa7480683d 100644
--- a/src/devices/sound/ymfm.cpp
+++ b/src/devices/sound/ymfm.cpp
@@ -9,95 +9,322 @@
#include "logmacro.h"
//
-// This emulator is written from the ground-up based on analysis and deduction
-// by Nemesis, particularly in this thread:
+// ONE FM CORE TO RULE THEM ALL
+//
+// This emulator is written from the ground-up using the analysis and deduction
+// by Nemesis as a starting point, particularly in this thread:
//
// https://gendev.spritesmind.net/forum/viewtopic.php?f=24&t=386
//
-// The core assumption is that these details apply to all OPN variants unless
+// The core assumption is that these details apply to all FM variants unless
// otherwise proven incorrect.
//
// The fine details of this implementation have also been cross-checked against
// Nemesis' implementation in his Exodus emulator, as well as Alexey Khokholov's
-// "Nuked" implementation based off die shots.
+// "Nuked" implementations based off die shots.
//
// Operator and channel summing/mixing code is largely based off of research
// done by David Viens and Hubert Lamontagne.
//
// Search for QUESTION to find areas where I am unsure.
//
-// ===================================================================================
//
-// OPN pedigree:
-//
-// +--------++-----------------++------------------++--------------------------+
-// broad catgeory: | OPN || OPNA || OPNB || OPN2 |
-// +--------++--------+--------++--------+---------++--------+--------+--------+
-// chip ID: | YM2203 || YM2608 | YMF288 || YM2610 | YM2610B || YM2612 | YM3438 | YMF276 |
-// +--------++--------+--------++--------+---------++--------+--------+--------+
-// aka: | OPN || OPNA | OPN3 || OPNB | OPNB2 || OPN2 | OPN2C | OPN2L |
-// FM channels: | 3 || 6 | 6 || 4 | 6 || 6 | 6 | 6 |
-//AY-3-8910 channels: | 3 || 3 | 3 || 3 | 3 || - | - | - |
-// ADPCM-A channels: | - || 6 int | 6 int || 6 ext | 6 ext || - | - | - |
-// ADPCM-B channels: | - || 1 ext | - || 1 ext | 1 ext || - | - | - |
-// Channel 6 "DAC": | no || no | no || no | no || yes | yes | yes |
-// Clock divider: | 6/3/2 || 6/3/2 | 6/3/2 || 6 | 6 || 6 | 6 | 6 |
-// Stereo: | no || yes | yes || yes | yes || yes | yes | yes |
-// DAC: | 10.3fp || 16-bit | 16-bit || 16-bit | 16-bit || 9-bit | 9-bit | 16-bit |
-// Summing: | adder || adder | adder || adder | adder || muxer | muxer | adder |
-// LFO: | no || yes | yes || yes | yes || yes | yes | yes |
-// +--------++--------+--------++--------+---------++--------+--------+--------+
+// FAMILIES
//
-// ===================================================================================
+// The Yamaha FM chips can be broadly categoried into families:
+//
+// OPM (YM2151)
+// OPN (YM2203)
+// OPNA/OPNB/OPN2 (YM2608, YM2610, YM2610B, YM2612, YM3438, YMF276, YMF288)
+// OPL (YM3526)
+// OPL2 (YM3812)
+// OPLL (YM2413, YM2423, YMF281, DS1001, and others)
+// OPL3 (YMF262, YMF278)
+//
+// All of these families are very closely related, and the ymfm engine
+// implemented below is designed to be universal to work across all of
+// these families.
+//
+// Of course, each variant has its own register maps, features, and
+// implementation details which need to be sorted out. Thus, each
+// significant variant listed above is represented by a register class. The
+// register class contains:
+//
+// * constants describing core parameters and features
+// * mappers between operators and channels
+// * generic fetchers that return normalized values across families
+// * family-specific helper functions
+//
+//
+// FAMILY HISTORY
+//
+// OPM started it all off, featuring:
+// - 8 FM channels, 4 operators each
+// - LFO and noise support
+// - Stereo output
+//
+// OPM -> OPN changes:
+// - Reduced to 3 FM channels, 4 operators each
+// - Removed LFO and noise support
+// - Mono output
+// - Integrated AY-8910 compatible PSG
+// - Added SSG-EG envelope mode
+// - Added multi-frequency mode: ch. 3 operators can have separate frequencies
+// - Software controlled clock divider
+//
+// OPN -> OPNA changes:
+// - Increased to 6 FM channels, 4 operators each
+// - Added back (a cut-down) LFO
+// - Stereo output again
+// - Removed software controlled divider on later versions (OPNB/OPN2)
+// - Removed PSG on OPN2 models
+//
+// OPNA -> OPL changes:
+// - Increased to 9 FM channels, but only 2 operators each
+// - Even more simplified LFO
+// - Mono output
+// - Removed PSG
+// - Removed SSG-EG envelope modes
+// - Removed multi-frequency modes
+// - Fixed clock divider
+// - Built-in ryhthm generation
+//
+// OPL -> OPL2 changes:
+// - Added 4 selectable waveforms
+//
+// OPL2 -> OPLL changes:
+// - Vastly simplified register map
+// - 15 built-in instruments, plus built-in rhythm instruments
+// - 1 user-controlled instrument
+//
+// OPL2 -> OPL3 changes:
+// - Increased to 18 FM channels, 2 operators each
+// - 4 output channels
+// - Increased to 8 selectable waveforms
+// - 6 channels can be configured to use 4 operators
+//
+//
+// CHANNELS AND OPERATORS
+//
+// The polyphony of a given chip is determined by the number of channels
+// it supports. This number ranges from as low as 3 to as high as 18.
+// Each channel has either 2 or 4 operators that can be combined in a
+// myriad of ways. On most chips the number of operators per channel is
+// fixed; however, some later OPL chips allow this to be toggled between
+// 2 and 4 at runtime.
+//
+// The base ymfm engine class maintains an array of channels and operators,
+// while the relationship between the two is described by the register
+// class.
+//
+//
+// REGISTERS
+//
+// Registers on the Yamaha chips are generally write-only, and can be divided
+// into three distinct categories:
+//
+// * system-wide registers
+// * channel-specific registers
+// * operator-specific registers
+//
+// For maximum flexibility, most parameters can be configured at the operator
+// level, with channel-level registers controlling details such as how to
+// combine the operators into the final output. System-wide registers are
+// used to control chip-wide modes and manage onboard timer functions.
+//
+// Note that since registers are write-only, some implementations will use
+// "holes" in the register space to store additional values that may be
+// needed.
+//
+//
+// STATUS AND TIMERS
+//
+// Generically, all chips (except OPLL) support two timers that can be
+// programmed to fire and signal IRQs. These timers also set bits in the
+// status register. The behavior of these bits is shared across all
+// implementations, even if the exact bit positions shift (this is controlled
+// by constants in the registers class).
+//
+// In addition, several chips incorporate ADPCM decoders which also may set
+// bits in the same status register. For this reason, it is possible to
+// control various bits in the status register via the set_reset_status()
+// function directly. Any active bits that are set and which are not masked
+// (mask is controlled by set_irq_mask()), lead to an IRQ being signalled.
+//
+// Thus, it is possible for the chip-specific implementations to set the
+// mask and control the status register bits such that IRQs are signalled
+// via the same mechanism as timer signals.
+//
+// In addition, the OPM and OPN families have a "busy" flag, which is set
+// after each write, indicating that another write should not be performed.
+// Historically, the duration of this flag was constant and had nothing to
+// do with the internals of the chip. However, since the details can
+// potentially vary chip-to-chip, it is the chip's responsibility after any
+// operation to call set_busy_end() with the attotime of when the busy
+// signal should be released.
+//
+//
+// CLOCKING
+//
+// Each of the Yamaha chips works by cycling through all operators one at
+// a time. Thus, the effective output rate of the chips is related to the
+// input clock divided by the number of operators. In addition, the input
+// clock is prescaled by an amount. Generally, this is a fixed value, though
+// some early OPN chips allow this to be selected at runtime from a small
+// number of values.
+//
+//
+// CHANNEL FREQUENCIES
+//
+// One major difference between OPM and later families is in how frequencies
+// are specified. OPM specifies frequency via a 3-bit 'block' (aka octave),
+// combined with a 4-bit 'key code' (note number) and a 6-bit 'key fraction'.
+// The key code and fraction are converted on the chip into an x.11 fixed-
+// point value and then shifted by the block to produce the final step value
+// for the phase.
+//
+// Later families, on the other hand, specify frequencies via a 3-bit 'block'
+// just as on OPM, but combined with a 9, 10, or 11-bit 'frequency number'
+// or 'fnum', which is directly shifted by the block to produce the step
+// value. So essentially, later chips make the user do the conversion from
+// note value to phase increment, while OPM is programmed in a more 'musical'
+// way, specifying notes and cents.
+//
+// Interally, this is abstracted away into a 'block_freq' value, which is a
+// 16-bit value containing the block and frequency info concatenated together
+// as follows:
+//
+// OPM: [3-bit block]:[4-bit keycode]:[6-bit fraction] = 13 bits total
+//
+// OPN: [3-bit block]:[11-bit fnum] = 14 bits total
+// OPL: [3-bit block]:[10-bit fnum]:0 = 14 bits total
+// OPLL: [3-bit block]:[ 9-bit fnum]:00 = 14 bits total
+//
+// Template specialization in functions that interpret the 'block_freq' value
+// is used to deconstruct it appropriately (specifically, see clock_phase).
+//
+//
+// LOW FREQUENCY OSCILLATOR (LFO)
+//
+// The LFO engines are different in several key ways. The OPM LFO engine is
+// fairly intricate. It has a 4.4 floating-point rate which allows for a huge
+// range of frequencies, and can select between four different waveforms
+// (sawtooth, square, triangle, or noise). Separate 7-bit depth controls for
+// AM and PM control the amount of modulation applied in each case. This
+// global LFO value is then further controlled at the channel level by a 2-bit
+// AM sensitivity and a 3-bit PM sensitivity, and each operator has a 1-bit AM
+// on/off switch.
+//
+// For OPN the LFO engine was removed entirely, but a limited version was put
+// back in OPNA and later chips. This stripped-down version offered only a
+// 3-bit rate setting (versus the 4.4 floating-point rate in OPN), and no
+// depth control. It did bring back the channel-level sensitivity controls and
+// the operator-level on/off control.
+//
+// For OPL, the LFO is simplified again, with AM and PM running at fixed
+// frequencies, and simple enable flags at the operator level for each
+// controlling their application.
+//
+//
+// DIFFERENCES BETWEEN FAMILIES
+//
+// The table below provides some high level functional differences between the
+// differnet families:
+//
+// +--------++-----------------++-----------------------------------+
+// family: | OPM || OPN || OPL |
+// +--------++--------+--------++--------+--------+--------+--------+
+// subfamily: | OPM || OPN | OPNA || OPL | OPL2 | OPLL | OPL3 |
+// +--------++--------+--------++--------+--------+--------+--------+
+// outputs: | 2 || 1 | 2 || 1 | 1 | 1 | 4 |
+// channels: | 8 || 3 | 6 || 9 | 9 | 9 | 18 |
+// operators: | 32 || 12 | 24 || 18 | 18 | 18 | 36 |
+// waveforms: | 1 || 1 | 1 || 1 | 4 | 2 | 8 |
+// instruments: | no || no | no || yes | yes | yes | yes |
+// ryhthm: | no || no | no || no | no | yes | no |
+// dynamic ops: | no || no | no || no | no | no | yes |
+// prescale: | 2 || 2/3/6 | 2/3/6 || 4 | 4 | 4 | 8 |
+// EG divider: | 3 || 3 | 3 || 1 | 1 | 1 | 1 |
+// EG DP: | no || no | no || no | no | yes | no |
+// EG SSG: | no || yes | yes || no | no | no | no |
+// mod delay: | no || no | no || yes | yes | yes? | no |
+// CSM: | yes || ch 2 | ch 2 || yes | yes | yes | no |
+// LFO: | yes || no | yes || yes | yes | yes | yes |
+// noise: | yes || no | no || no | no | no | no |
+// +--------++--------+--------++--------+--------+--------+--------+
+//
+// Outputs represents the number of output channels: 1=mono, 2=stereo, 4=stereo+.
+// Channels represents the number of independent FM channels.
+// Operators represents the number of operators, or "slots" which are assembled
+// into the channels.
+// Waveforms represents the number of different sine-derived waveforms available.
+// Instruments indicates whether the family has built-in instruments.
+// Rhythm indicates whether the family has a built-in rhythm
+// Dynamic ops indicates whether it is possible to switch between 2-operator and
+// 4-operator modes dynamically.
+// Prescale specifies the default clock divider; some chips allow this to be
+// controlled via register writes.
+// EG divider represents the divider applied to the envelope generator clock.
+// EG DP indicates whether the envelope generator includes a DP (depress?) phase
+// at the beginning of each key on.
+// SSG EG indicates whether the envelope generator has SSG-style support.
+// Mod delay indicates whether the connection to the first modulator's input is
+// delayed by 1 sample.
+// CSM indicates whether CSM mode is supported, triggered by timer A.
+// LFO indicates whether LFO is supported.
+// Noise indicates whether one of the operators can be replaced with a noise source.
+//
+//
+// CHIP SPECIFICS
+//
+// While OPM is its own thing, the OPN and OPL families have quite a few specific
+// implementations, with many differing details beyond the core FM parts. Here are
+// some details on the OPN family:
+//
+// +--------++--------+--------++--------+---------++--------+--------+--------+
+// chip ID: | YM2203 || YM2608 | YMF288 || YM2610 | YM2610B || YM2612 | YM3438 | YMF276 |
+// +--------++--------+--------++--------+---------++--------+--------+--------+
+// aka: | OPN || OPNA | OPN3 || OPNB | OPNB2 || OPN2 | OPN2C | OPN2L |
+// FM: | 3 || 6 | 6 || 4 | 6 || 6 | 6 | 6 |
+// AY-8910: | 3 || 3 | 3 || 3 | 3 || - | - | - |
+// ADPCM-A: | - || 6 int | 6 int || 6 ext | 6 ext || - | - | - |
+// ADPCM-B: | - || 1 ext | - || 1 ext | 1 ext || - | - | - |
+// DAC: | no || no | no || no | no || yes | yes | yes |
+// output: | 10.3fp || 16-bit | 16-bit || 16-bit | 16-bit || 9-bit | 9-bit | 16-bit |
+// summing: | adder || adder | adder || adder | adder || muxer | muxer | adder |
+// +--------++--------+--------++--------+---------++--------+--------+--------+
+//
+// FM represents the number of FM channels available.
+// AY-8910 represents the number of AY-8910-compatible channels that are built in.
+// ADPCM-A represents the number of internal/external ADPCM-A channels present.
+// ADPCM-B represents the number of internal/external ADPCM-B channels present.
+// DAC indicates if a directly-accessible DAC output exists, replacing one channel.
+// Output indicates the output format to the final DAC.
+// Summing indicates whether channels are added or time divided in the output.
+//
+// OPL has a similar trove of chip variants:
//
-// From OPM to OPN:
-// - FM Channels reduced from 8 to 3
-// - Stereo removed, Hardware LFO removed, Channel 8 noise removed
-// - Hardware pitch table removed, coarse detune removed, pitch calculation is different
-// - 3 square wave channels added (GI AY-3–8910 compatible)
-// - SSG-EG envelope mode added (lets you do AY style looping envelopes on FM ops)
-// - Channel 3 can have different frequency for each op
-// - CSM only applies to channel 3
-// - Register map is different
-// - Operator timing is different. Channel 1 and 2 have very different timing.
-// - OPN’s hardware FM clock divider can be changed from /6 (default) to /2 or /3
-//
-// From OPN to OPNA:
-// - Channels doubled from 3 to 6
-// - Added hardware LFO (different from OPM)
-// - OPNA is stereo
-// - OPNA uses a full 16bit dac instead of a 10:3bit dac.
-// - 6 ADPCM-A drum channels added (play from built-in rom only)
-// and 1 variable rate ADPCM-B channel (streaming from a small RAM).
-// - Operator timing is different. All channels have the same timing on OPNA
-// (roughly the same timing as Channel 3 on OPN), except for Channel 6 when
-// set to algorithm 8.
-// - Frequency calculation is 1 bit less precise and can wrap.
-// - All carrier output values / 2 (this makes carrier output 13 bits instead
-// of 14 bits)
-//
-// OPNB/OPNB2 is a OPNA that uses external ROM for the 6 ADPCM-A channels and the
-// ADPCM-B channel. ADPCM-A and ADPCM-B use different buses and different ADPCM
-// encodings. OPNB(2) doesn’t have a changeable divider (always /6). OPNB has 4 FM
-// channels only (ch. 1 and 4 removed), OPNB2 has 6 channels.
-//
-// From OPNA to OPN2:
-// - Removed GI AY-3–8910 channels and drums and streaming ADPCM
-// - Operator timing is different. All channels have the same timing on OPN2.
-// - Removed changeable divider (always /6)
-// - Carrier output values / 32 instead of / 2 (carriers output 9 bits, down
-// from 13 bits)
-// - Built-in 9bit dac, uses analog mixing (time division multiplexing). The
-// dac has a large gap between values 0 and -1 (resulting in the ladder effect).
-// - Ch6 “DAC” mode.
-//
-// From OPN2 to OPN2C:
-// - The DAC is more linear (no gap between 0 and -1).
-//
-// From OPN2C to OPN2L:
-// - Carrier output is different (full 14 bits instead of 9 bits, narrowed to
-// 13 on ch. mix)
-// - Uses external DAC (16bit stereo), no analog mixing
+// +--------+---------++--------++--------++--------++---------+
+// chip ID: | YM3526 | Y8950 || YM3812 || YM2413 || YMF262 || YMF278B |
+// +--------+---------++--------++--------++--------++---------+
+// aka: | OPL |MSX-AUDIO|| OPL2 || OPLL || OPL3 || OPL4 |
+// FM: | 9 | 9 || 9 || 9 || 18 || 18 |
+// ADPCM-B: | - | 1 ext || - || - || - || - |
+// wavetable: | - | - || - || - || - || 24 |
+// instruments: | no | no || no || yes || no || no |
+// output: | 10.3fp | 10.3fp || 10.3fp || 9-bit || 16-bit || 16-bit |
+// summing: | adder | adder || adder || muxer || adder || adder |
+// +--------+---------++--------++--------++--------++---------+
+//
+// FM represents the number of FM channels available.
+// ADPCM-B represents the number of external ADPCM-B channels present.
+// Wavetable indicates the number of wavetable channels present.
+// Instruments indicates that the chip has built-in instrument selection.
+// Output indicates the output format to the final DAC.
+// Summing indicates whether channels are added or time divided in the output.
+//
+// There are several close variants of the YM2413 with different sets of built-
+// in instruments. These include the YM2423, YMF281, and DS1001 (aka Konami VRC7).
//
// ===================================================================================
//
@@ -137,7 +364,7 @@
// attenuation value, in 4.8 fixed point format
//-------------------------------------------------
-inline u16 abs_sin_attenuation(u16 input)
+inline u32 abs_sin_attenuation(u32 input)
{
// the values here are stored as 4.8 logarithmic values for 1/4 phase
// this matches the internal format of the OPN chip, extracted from the die
@@ -177,32 +404,54 @@ inline u16 abs_sin_attenuation(u16 input)
// linear volume
//-------------------------------------------------
-inline u16 attenuation_to_volume(u16 input)
+inline u32 attenuation_to_volume(u32 input)
{
// the values here are 10-bit mantissas with an implied leading bit
// this matches the internal format of the OPN chip, extracted from the die
+
+ // as a nod to performance, the implicit 0x400 bit is pre-incorporated, and
+ // the values are left-shifted by 2 so that a simple right shift is all that
+ // is needed; also the order is reversed to save a NOT on the input
+#define X(a) ((a | 0x400) << 2)
static u16 const s_power_table[256] =
{
- 0x000,0x003,0x006,0x008,0x00b,0x00e,0x011,0x014,0x016,0x019,0x01c,0x01f,0x022,0x025,0x028,0x02a,
- 0x02d,0x030,0x033,0x036,0x039,0x03c,0x03f,0x042,0x045,0x048,0x04b,0x04e,0x051,0x054,0x057,0x05a,
- 0x05d,0x060,0x063,0x066,0x069,0x06c,0x06f,0x072,0x075,0x078,0x07b,0x07e,0x082,0x085,0x088,0x08b,
- 0x08e,0x091,0x094,0x098,0x09b,0x09e,0x0a1,0x0a4,0x0a8,0x0ab,0x0ae,0x0b1,0x0b5,0x0b8,0x0bb,0x0be,
- 0x0c2,0x0c5,0x0c8,0x0cc,0x0cf,0x0d2,0x0d6,0x0d9,0x0dc,0x0e0,0x0e3,0x0e7,0x0ea,0x0ed,0x0f1,0x0f4,
- 0x0f8,0x0fb,0x0ff,0x102,0x106,0x109,0x10c,0x110,0x114,0x117,0x11b,0x11e,0x122,0x125,0x129,0x12c,
- 0x130,0x134,0x137,0x13b,0x13e,0x142,0x146,0x149,0x14d,0x151,0x154,0x158,0x15c,0x160,0x163,0x167,
- 0x16b,0x16f,0x172,0x176,0x17a,0x17e,0x181,0x185,0x189,0x18d,0x191,0x195,0x199,0x19c,0x1a0,0x1a4,
- 0x1a8,0x1ac,0x1b0,0x1b4,0x1b8,0x1bc,0x1c0,0x1c4,0x1c8,0x1cc,0x1d0,0x1d4,0x1d8,0x1dc,0x1e0,0x1e4,
- 0x1e8,0x1ec,0x1f0,0x1f5,0x1f9,0x1fd,0x201,0x205,0x209,0x20e,0x212,0x216,0x21a,0x21e,0x223,0x227,
- 0x22b,0x230,0x234,0x238,0x23c,0x241,0x245,0x249,0x24e,0x252,0x257,0x25b,0x25f,0x264,0x268,0x26d,
- 0x271,0x276,0x27a,0x27f,0x283,0x288,0x28c,0x291,0x295,0x29a,0x29e,0x2a3,0x2a8,0x2ac,0x2b1,0x2b5,
- 0x2ba,0x2bf,0x2c4,0x2c8,0x2cd,0x2d2,0x2d6,0x2db,0x2e0,0x2e5,0x2e9,0x2ee,0x2f3,0x2f8,0x2fd,0x302,
- 0x306,0x30b,0x310,0x315,0x31a,0x31f,0x324,0x329,0x32e,0x333,0x338,0x33d,0x342,0x347,0x34c,0x351,
- 0x356,0x35b,0x360,0x365,0x36a,0x370,0x375,0x37a,0x37f,0x384,0x38a,0x38f,0x394,0x399,0x39f,0x3a4,
- 0x3a9,0x3ae,0x3b4,0x3b9,0x3bf,0x3c4,0x3c9,0x3cf,0x3d4,0x3da,0x3df,0x3e4,0x3ea,0x3ef,0x3f5,0x3fa
+ X(0x3fa),X(0x3f5),X(0x3ef),X(0x3ea),X(0x3e4),X(0x3df),X(0x3da),X(0x3d4),
+ X(0x3cf),X(0x3c9),X(0x3c4),X(0x3bf),X(0x3b9),X(0x3b4),X(0x3ae),X(0x3a9),
+ X(0x3a4),X(0x39f),X(0x399),X(0x394),X(0x38f),X(0x38a),X(0x384),X(0x37f),
+ X(0x37a),X(0x375),X(0x370),X(0x36a),X(0x365),X(0x360),X(0x35b),X(0x356),
+ X(0x351),X(0x34c),X(0x347),X(0x342),X(0x33d),X(0x338),X(0x333),X(0x32e),
+ X(0x329),X(0x324),X(0x31f),X(0x31a),X(0x315),X(0x310),X(0x30b),X(0x306),
+ X(0x302),X(0x2fd),X(0x2f8),X(0x2f3),X(0x2ee),X(0x2e9),X(0x2e5),X(0x2e0),
+ X(0x2db),X(0x2d6),X(0x2d2),X(0x2cd),X(0x2c8),X(0x2c4),X(0x2bf),X(0x2ba),
+ X(0x2b5),X(0x2b1),X(0x2ac),X(0x2a8),X(0x2a3),X(0x29e),X(0x29a),X(0x295),
+ X(0x291),X(0x28c),X(0x288),X(0x283),X(0x27f),X(0x27a),X(0x276),X(0x271),
+ X(0x26d),X(0x268),X(0x264),X(0x25f),X(0x25b),X(0x257),X(0x252),X(0x24e),
+ X(0x249),X(0x245),X(0x241),X(0x23c),X(0x238),X(0x234),X(0x230),X(0x22b),
+ X(0x227),X(0x223),X(0x21e),X(0x21a),X(0x216),X(0x212),X(0x20e),X(0x209),
+ X(0x205),X(0x201),X(0x1fd),X(0x1f9),X(0x1f5),X(0x1f0),X(0x1ec),X(0x1e8),
+ X(0x1e4),X(0x1e0),X(0x1dc),X(0x1d8),X(0x1d4),X(0x1d0),X(0x1cc),X(0x1c8),
+ X(0x1c4),X(0x1c0),X(0x1bc),X(0x1b8),X(0x1b4),X(0x1b0),X(0x1ac),X(0x1a8),
+ X(0x1a4),X(0x1a0),X(0x19c),X(0x199),X(0x195),X(0x191),X(0x18d),X(0x189),
+ X(0x185),X(0x181),X(0x17e),X(0x17a),X(0x176),X(0x172),X(0x16f),X(0x16b),
+ X(0x167),X(0x163),X(0x160),X(0x15c),X(0x158),X(0x154),X(0x151),X(0x14d),
+ X(0x149),X(0x146),X(0x142),X(0x13e),X(0x13b),X(0x137),X(0x134),X(0x130),
+ X(0x12c),X(0x129),X(0x125),X(0x122),X(0x11e),X(0x11b),X(0x117),X(0x114),
+ X(0x110),X(0x10c),X(0x109),X(0x106),X(0x102),X(0x0ff),X(0x0fb),X(0x0f8),
+ X(0x0f4),X(0x0f1),X(0x0ed),X(0x0ea),X(0x0e7),X(0x0e3),X(0x0e0),X(0x0dc),
+ X(0x0d9),X(0x0d6),X(0x0d2),X(0x0cf),X(0x0cc),X(0x0c8),X(0x0c5),X(0x0c2),
+ X(0x0be),X(0x0bb),X(0x0b8),X(0x0b5),X(0x0b1),X(0x0ae),X(0x0ab),X(0x0a8),
+ X(0x0a4),X(0x0a1),X(0x09e),X(0x09b),X(0x098),X(0x094),X(0x091),X(0x08e),
+ X(0x08b),X(0x088),X(0x085),X(0x082),X(0x07e),X(0x07b),X(0x078),X(0x075),
+ X(0x072),X(0x06f),X(0x06c),X(0x069),X(0x066),X(0x063),X(0x060),X(0x05d),
+ X(0x05a),X(0x057),X(0x054),X(0x051),X(0x04e),X(0x04b),X(0x048),X(0x045),
+ X(0x042),X(0x03f),X(0x03c),X(0x039),X(0x036),X(0x033),X(0x030),X(0x02d),
+ X(0x02a),X(0x028),X(0x025),X(0x022),X(0x01f),X(0x01c),X(0x019),X(0x016),
+ X(0x014),X(0x011),X(0x00e),X(0x00b),X(0x008),X(0x006),X(0x003),X(0x000)
};
+#undef X
// look up the fractional part, then shift by the whole
- return ((s_power_table[~input & 0xff] | 0x400) << 2) >> (input >> 8);
+ return s_power_table[input & 0xff] >> (input >> 8);
}
@@ -214,7 +463,7 @@ inline u16 attenuation_to_volume(u16 input)
// fractional scale factor to decrease by)
//-------------------------------------------------
-inline u8 attenuation_increment(u8 rate, u8 index)
+inline u32 attenuation_increment(u32 rate, u32 index)
{
static u32 const s_increment_table[64] =
{
@@ -248,7 +497,7 @@ inline u8 attenuation_increment(u8 rate, u8 index)
// we'll keep the simplicity of the table
//-------------------------------------------------
-inline s8 detune_adjustment(u8 detune, u8 keycode)
+inline s32 detune_adjustment(u32 detune, u32 keycode)
{
static u8 const s_detune_adjustment[32][4] =
{
@@ -261,7 +510,7 @@ inline s8 detune_adjustment(u8 detune, u8 keycode)
{ 0, 5, 11, 16 }, { 0, 6, 12, 17 }, { 0, 6, 13, 19 }, { 0, 7, 14, 20 },
{ 0, 8, 16, 22 }, { 0, 8, 16, 22 }, { 0, 8, 16, 22 }, { 0, 8, 16, 22 }
};
- s8 result = s_detune_adjustment[keycode][detune & 3];
+ s32 result = s_detune_adjustment[keycode][detune & 3];
return BIT(detune, 2) ? -result : result;
}
@@ -274,7 +523,7 @@ inline s8 detune_adjustment(u8 detune, u8 keycode)
// algorithm written to match Nuked behavior
//-------------------------------------------------
-inline s16 opn_lfo_pm_phase_adjustment(u8 fnum_bits, u8 pm_sensitivity, s8 lfo_raw_pm)
+inline s32 opn_lfo_pm_phase_adjustment(u32 fnum_bits, u32 pm_sensitivity, s32 lfo_raw_pm)
{
// this table encodes 2 shift values to apply to the top 7 bits
// of fnum; it is effectively a cheap multiply by a constant
@@ -292,11 +541,11 @@ inline s16 opn_lfo_pm_phase_adjustment(u8 fnum_bits, u8 pm_sensitivity, s8 lfo_r
};
// look up the relevant shifts
- s8 abs_pm = (lfo_raw_pm < 0) ? -lfo_raw_pm : lfo_raw_pm;
- u8 const shifts = s_lfo_pm_shifts[pm_sensitivity][BIT(abs_pm, 0, 3)];
+ s32 abs_pm = (lfo_raw_pm < 0) ? -lfo_raw_pm : lfo_raw_pm;
+ u32 const shifts = s_lfo_pm_shifts[pm_sensitivity][BIT(abs_pm, 0, 3)];
// compute the adjustment
- s16 adjust = (fnum_bits >> BIT(shifts, 0, 4)) + (fnum_bits >> BIT(shifts, 4, 4));
+ s32 adjust = (fnum_bits >> BIT(shifts, 0, 4)) + (fnum_bits >> BIT(shifts, 4, 4));
if (pm_sensitivity > 5)
adjust <<= pm_sensitivity - 5;
adjust >>= 2;
@@ -307,13 +556,13 @@ inline s16 opn_lfo_pm_phase_adjustment(u8 fnum_bits, u8 pm_sensitivity, s8 lfo_r
//-------------------------------------------------
-// opm_keycode_to_phase_step - converts an
+// opm_key_code_to_phase_step - converts an
// OPM concatenated block (3 bits), keycode
// (4 bits) and key fraction (6 bits) to a 0.10
// phase step, after applying the given delta
//-------------------------------------------------
-inline u32 opm_keycode_to_phase_step(u16 block_freq, s16 delta)
+inline u32 opm_key_code_to_phase_step(u32 block_freq, s32 delta)
{
// The phase step is essentially the fnum in OPN-speak. To compute this table,
// we used the standard formula for computing the frequency of a note, and
@@ -323,11 +572,11 @@ inline u32 opm_keycode_to_phase_step(u16 block_freq, s16 delta)
// However, the YM2608 manual describes everything in terms of a nominal 8MHz
// clock, which produces an FM clock of:
//
- // 8000000 / 6(channels) / 4(operators) / 6(prescale) = 55555Hz FM clock
+ // 8000000 / 24(operators) / 6(prescale) = 55555Hz FM clock
//
// Whereas the descriptions for the YM2151 use a nominal 3.579545MHz clock:
//
- // 3579545 / 8(channels) / 4(operators) / 2(prescale) = 55930Hz FM clock
+ // 3579545 / 32(operators) / 2(prescale) = 55930Hz FM clock
//
// To correct for this, the YM2608 formula was adjusted to use a clock of
// 8053920Hz, giving this equation for the fnum:
@@ -390,22 +639,22 @@ inline u32 opm_keycode_to_phase_step(u16 block_freq, s16 delta)
};
// extract the block (octave) first
- u8 block = BIT(block_freq, 10, 3);
+ u32 block = BIT(block_freq, 10, 3);
// the keycode (bits 6-9) is "gappy", mapping 12 values over 16 in each
// octave; to correct for this, we multiply the 4-bit value by 3/4 (or
// rather subtract 1/4); note that a (invalid) value of 15 will bleed into
// the next octave -- this is confirmed
- u8 adjusted_code = BIT(block_freq, 6, 4) - BIT(block_freq, 8, 2);
+ u32 adjusted_code = BIT(block_freq, 6, 4) - BIT(block_freq, 8, 2);
// now re-insert the 6-bit fraction
- s16 eff_freq = (adjusted_code << 6) | BIT(block_freq, 0, 6);
+ s32 eff_freq = (adjusted_code << 6) | BIT(block_freq, 0, 6);
// now that the gaps are removed, add the delta
eff_freq += delta;
// handle over/underflow by adjusting the block:
- if (u16(eff_freq) >= 768)
+ if (u32(eff_freq) >= 768)
{
// minimum delta is -512 (PM), so we can only underflow by 1 octave
if (eff_freq < 0)
@@ -431,6 +680,1397 @@ inline u32 opm_keycode_to_phase_step(u16 block_freq, s16 delta)
}
+//-------------------------------------------------
+// opl_key_scale_atten - converts an
+// OPL concatenated block (3 bits) and fnum
+// (10 bits) into an attenuation offset; values
+// here are for 6dB/octave, in 0.75dB units
+// (matching total level LSB)
+//-------------------------------------------------
+
+inline u32 opl_key_scale_atten(u32 block, u32 fnum_4msb)
+{
+ // this table uses the top 4 bits of FNUM and are the maximal values
+ // (for when block == 7). Values for other blocks can be computed by
+ // subtracting 8 for each block below 7.
+ static u8 const fnum_to_atten[16] = { 0,24,32,37,40,43,45,47,48,50,51,52,53,54,55,56 };
+ s32 result = fnum_to_atten[fnum_4msb] - 8 * (block ^ 7);
+ return std::max<s32>(0, result);
+}
+
+
+
+//*********************************************************
+// OPM SPECIFICS
+//*********************************************************
+
+//-------------------------------------------------
+// ymopm_registers - constructor
+//-------------------------------------------------
+
+ymopm_registers::ymopm_registers() :
+ m_lfo_counter(0),
+ m_noise_lfsr(1),
+ m_noise_counter(0),
+ m_noise_state(0),
+ m_noise_lfo(0),
+ m_lfo_am(0)
+{
+ // create the waveforms
+ for (int index = 0; index < WAVEFORM_LENGTH; index++)
+ m_waveform[0][index] = abs_sin_attenuation(index) | (BIT(index, 9) << 15);
+
+ // create the LFO waveforms; AM in the low 8 bits, PM in the upper 8
+ // waveforms are adjusted to match the pictures in the application manual
+ for (int index = 0; index < LFO_WAVEFORM_LENGTH; index++)
+ {
+ // waveform 0 is a sawtooth
+ u8 am = index ^ 0xff;
+ s8 pm = s8(index);
+ m_lfo_waveform[0][index] = am | (pm << 8);
+
+ // waveform 1 is a square wave
+ am = BIT(index, 7) ? 0 : 0xff;
+ pm = s8(am ^ 0x80);
+ m_lfo_waveform[1][index] = am | (pm << 8);
+
+ // waveform 2 is a triangle wave
+ am = BIT(index, 7) ? (index << 1) : ((index ^ 0xff) << 1);
+ pm = s8(BIT(index, 6) ? am : ~am);
+ m_lfo_waveform[2][index] = am | (pm << 8);
+
+ // waveform 3 is noise; it is filled in dynamically
+ }
+}
+
+
+//-------------------------------------------------
+// save - register for save states
+//-------------------------------------------------
+
+void ymopm_registers::save(device_t &device)
+{
+ device.save_item(YMFM_NAME(m_lfo_counter));
+ device.save_item(YMFM_NAME(m_lfo_am));
+ device.save_item(YMFM_NAME(m_noise_lfsr));
+ device.save_item(YMFM_NAME(m_noise_counter));
+ device.save_item(YMFM_NAME(m_noise_state));
+ device.save_item(YMFM_NAME(m_noise_lfo));
+ device.save_item(YMFM_NAME(m_regdata));
+}
+
+
+//-------------------------------------------------
+// reset - reset to initial state
+//-------------------------------------------------
+
+void ymopm_registers::reset()
+{
+ std::fill_n(&m_regdata[0], REGISTERS, 0);
+
+ // enable output on both channels by default
+ m_regdata[0x20] = m_regdata[0x21] = m_regdata[0x22] = m_regdata[0x23] = 0xc0;
+ m_regdata[0x24] = m_regdata[0x25] = m_regdata[0x26] = m_regdata[0x27] = 0xc0;
+}
+
+
+//-------------------------------------------------
+// operator_map - return an array of operator
+// indices for each channel; for OPM this is fixed
+//-------------------------------------------------
+
+void ymopm_registers::operator_map(operator_mapping &dest) const
+{
+ // Note that the channel index order is 0,2,1,3, so we bitswap the index.
+ //
+ // This is because the order in the map is:
+ // carrier 1, carrier 2, modulator 1, modulator 2
+ //
+ // But when wiring up the connections, the more natural order is:
+ // carrier 1, modulator 1, carrier 2, modulator 2
+ static const operator_mapping s_fixed_map =
+ { {
+ operator_list( 0, 16, 8, 24 ), // Channel 0 operators
+ operator_list( 1, 17, 9, 25 ), // Channel 1 operators
+ operator_list( 2, 18, 10, 26 ), // Channel 2 operators
+ operator_list( 3, 19, 11, 27 ), // Channel 3 operators
+ operator_list( 4, 20, 12, 28 ), // Channel 4 operators
+ operator_list( 5, 21, 13, 29 ), // Channel 5 operators
+ operator_list( 6, 22, 14, 30 ), // Channel 6 operators
+ operator_list( 7, 23, 15, 31 ), // Channel 7 operators
+ } };
+ dest = s_fixed_map;
+}
+
+
+//-------------------------------------------------
+// write - handle writes to the register array
+//-------------------------------------------------
+
+bool ymopm_registers::write(u16 index, u8 data, u32 &channel, u32 &opmask)
+{
+ assert(index < REGISTERS);
+
+ // LFO AM/PM depth are written to the same register (0x19);
+ // redirect the PM depth to an unused neighbor (0x1a)
+ if (index == 0x19)
+ m_regdata[index + BIT(data, 7)] = data;
+ else if (index != 0x1a)
+ m_regdata[index] = data;
+
+ // handle writes to the key on index
+ if (index == 0x08)
+ {
+ channel = BIT(data, 0, 3);
+ opmask = BIT(data, 3, 4);
+ return true;
+ }
+ return false;
+}
+
+
+//-------------------------------------------------
+// clock_noise_and_lfo - clock the noise and LFO,
+// handling clock division, depth, and waveform
+// computations
+//-------------------------------------------------
+
+s32 ymopm_registers::clock_noise_and_lfo()
+{
+ // base noise frequency is measured at 2x 1/2 FM frequency; this
+ // means each tick counts as two steps against the noise counter
+ u32 freq = noise_frequency();
+ for (int rep = 0; rep < 2; rep++)
+ {
+ // evidence seems to suggest the LFSR is clocked continually and just
+ // sampled at the noise frequency for output purposes; note that the
+ // low 8 bits are the most recent 8 bits of history while bits 8-24
+ // contain the 17 bit LFSR state
+ m_noise_lfsr <<= 1;
+ m_noise_lfsr |= BIT(m_noise_lfsr, 17) ^ BIT(m_noise_lfsr, 14) ^ 1;
+
+ // compare against the frequency and latch when we exceed it
+ if (m_noise_counter++ >= freq)
+ {
+ m_noise_counter = 0;
+ m_noise_state = BIT(m_noise_lfsr, 17);
+ }
+ }
+
+ // treat the rate as a 4.4 floating-point step value with implied
+ // leading 1; this matches exactly the frequencies in the application
+ // manual, though it might not be implemented exactly this way on chip
+ u32 rate = lfo_rate();
+ m_lfo_counter += (0x10 | BIT(rate, 0, 4)) << BIT(rate, 4, 4);
+ u32 lfo = BIT(m_lfo_counter, 22, 8);
+
+ // fill in the noise entry 1 ahead of our current position; this
+ // ensures the current value remains stable for a full LFO clock
+ // and effectively latches the running value when the LFO advances
+ u32 lfo_noise = BIT(m_noise_lfsr, 17, 8);
+ m_lfo_waveform[3][(lfo + 1) & 0xff] = lfo_noise | (lfo_noise << 8);
+
+ // fetch the AM/PM values based on the waveform; AM is unsigned and
+ // encoded in the low 8 bits, while PM signed and encoded in the upper
+ // 8 bits
+ s32 ampm = m_lfo_waveform[lfo_waveform()][lfo];
+
+ // apply depth to the AM value and store for later
+ m_lfo_am = ((ampm & 0xff) * lfo_am_depth()) >> 7;
+
+ // apply depth to the PM value and return it
+ return ((ampm >> 8) * s32(lfo_pm_depth())) >> 7;
+}
+
+
+//-------------------------------------------------
+// lfo_am_offset - return the AM offset from LFO
+// for the given channel
+//-------------------------------------------------
+
+u32 ymopm_registers::lfo_am_offset(u32 choffs) const
+{
+ // OPM maps AM quite differently from OPN
+
+ // shift value for AM sensitivity is [*, 0, 1, 2],
+ // mapping to values of [0, 23.9, 47.8, and 95.6dB]
+ u32 am_sensitivity = ch_lfo_am_sens(choffs);
+ if (am_sensitivity == 0)
+ return 0;
+
+ // QUESTION: see OPN note below for the dB range mapping; it applies
+ // here as well
+
+ // raw LFO AM value on OPM is 0-FF, which is already a factor of 2
+ // larger than the OPN below, putting our staring point at 2x theirs;
+ // this works out since our minimum is 2x their maximum
+ return m_lfo_am << (am_sensitivity - 1);
+}
+
+
+//-------------------------------------------------
+// cache_operator_data - fill the operator cache
+// with prefetched data
+//-------------------------------------------------
+
+void ymopm_registers::cache_operator_data(u32 choffs, u32 opoffs, ymfm_opdata_cache &cache)
+{
+ // set up the easy stuff
+ cache.waveform = &m_waveform[0][0];
+
+ // get frequency from the channel
+ u32 block_freq = cache.block_freq = ch_block_freq(choffs);
+
+ // compute the keycode: block_freq is:
+ //
+ // BBBCCCCFFFFFF
+ // ^^^^^
+ //
+ // the 5-bit keycode is just the top 5 bits (block + top 2 bits
+ // of the key code)
+ u32 keycode = BIT(block_freq, 8, 5);
+
+ // detune adjustment
+ cache.detune = detune_adjustment(op_detune(opoffs), keycode);
+
+ // multiple value, as an x.1 value (0 means 0.5)
+ cache.multiple = op_multiple(opoffs) * 2;
+ if (cache.multiple == 0)
+ cache.multiple = 1;
+
+ // phase step, or PHASE_STEP_DYNAMIC if PM is active; this depends on
+ // block_freq, detune, and multiple, so compute it after we've done those
+ if (lfo_pm_depth() == 0 || ch_lfo_pm_sens(choffs) == 0)
+ cache.phase_step = compute_phase_step(choffs, opoffs, cache, 0);
+ else
+ cache.phase_step = ymfm_opdata_cache::PHASE_STEP_DYNAMIC;
+
+ // total level, scaled by 8
+ cache.total_level = op_total_level(opoffs) << 3;
+
+ // 4-bit sustain level, but 15 means 31 so effectively 5 bits
+ cache.eg_sustain = op_sustain_level(opoffs);
+ cache.eg_sustain |= (cache.eg_sustain + 1) & 0x10;
+ cache.eg_sustain <<= 5;
+
+ // determine KSR adjustment for enevlope rates
+ u32 ksrval = keycode >> (op_ksr(opoffs) ^ 3);
+ cache.eg_rate[YMFM_ENV_ATTACK] = effective_rate(op_attack_rate(opoffs) * 2, ksrval);
+ cache.eg_rate[YMFM_ENV_DECAY] = effective_rate(op_decay_rate(opoffs) * 2, ksrval);
+ cache.eg_rate[YMFM_ENV_SUSTAIN] = effective_rate(op_sustain_rate(opoffs) * 2, ksrval);
+ cache.eg_rate[YMFM_ENV_RELEASE] = effective_rate(op_release_rate(opoffs) * 4 + 2, ksrval);
+ cache.eg_rate[YMFM_ENV_DEPRESS] = 0x3f;
+}
+
+
+//-------------------------------------------------
+// compute_phase_step - compute the phase step
+//-------------------------------------------------
+
+u32 ymopm_registers::compute_phase_step(u32 choffs, u32 opoffs, ymfm_opdata_cache const &cache, s32 lfo_raw_pm)
+{
+ // OPM logic is rather unique here, due to extra detune
+ // and the use of key codes (not to be confused with keycode)
+
+ // start with coarse detune delta; table uses cents value from
+ // manual, converted into 1/64ths
+ static const s16 s_detune2_delta[4] = { 0, (600*64+50)/100, (781*64+50)/100, (950*64+50)/100 };
+ s32 delta = s_detune2_delta[op_detune2(opoffs)];
+
+ // add in the PM delta
+ u32 pm_sensitivity = ch_lfo_pm_sens(choffs);
+ if (pm_sensitivity != 0)
+ {
+ // raw PM value is -127..128 which is +/- 200 cents
+ // manual gives these magnitudes in cents:
+ // 0, +/-5, +/-10, +/-20, +/-50, +/-100, +/-400, +/-700
+ // this roughly corresponds to shifting the 200-cent value:
+ // 0 >> 5, >> 4, >> 3, >> 2, >> 1, << 1, << 2
+ if (pm_sensitivity < 6)
+ delta += lfo_raw_pm >> (6 - pm_sensitivity);
+ else
+ delta += lfo_raw_pm << (pm_sensitivity - 5);
+ }
+
+ // apply delta and convert to a frequency number
+ u32 phase_step = opm_key_code_to_phase_step(cache.block_freq, delta);
+
+ // apply detune based on the keycode
+ phase_step += cache.detune;
+
+ // apply frequency multiplier (which is cached as an x.1 value)
+ return (phase_step * cache.multiple) >> 1;
+}
+
+
+//-------------------------------------------------
+// log_keyon - log a key-on event
+//-------------------------------------------------
+
+void ymopm_registers::log_keyon(u32 choffs, u32 opoffs)
+{
+ u32 chnum = choffs;
+ u32 opnum = opoffs;
+
+ LOG("%d.%02d freq=%04X dt2=%d dt=%d fb=%d alg=%X mul=%X tl=%02X ksr=%d adsr=%02X/%02X/%02X/%X sl=%X out=%c%c",
+ chnum, opnum,
+ ch_block_freq(choffs),
+ op_detune2(opoffs),
+ op_detune(opoffs),
+ ch_feedback(choffs),
+ ch_algorithm(choffs),
+ op_multiple(opoffs),
+ op_total_level(opoffs),
+ op_ksr(opoffs),
+ op_attack_rate(opoffs),
+ op_decay_rate(opoffs),
+ op_sustain_rate(opoffs),
+ op_release_rate(opoffs),
+ op_sustain_level(opoffs),
+ ch_output_0(choffs) ? 'L' : '-',
+ ch_output_1(choffs) ? 'R' : '-');
+
+ bool am = (lfo_am_depth() != 0 && ch_lfo_am_sens(choffs) != 0 && op_lfo_am_enable(opoffs) != 0);
+ if (am)
+ LOG(" am=%d/%02X", ch_lfo_am_sens(choffs), lfo_am_depth());
+ bool pm = (lfo_pm_depth() != 0 && ch_lfo_pm_sens(choffs) != 0);
+ if (pm)
+ LOG(" pm=%d/%02X", ch_lfo_pm_sens(choffs), lfo_pm_depth());
+ if (am || pm)
+ LOG(" lfo=%02X/%c", lfo_rate(), "WQTN"[lfo_waveform()]);
+ if (noise_enable() && opoffs == 31)
+ LOG(" noise=1");
+}
+
+
+//*********************************************************
+// OPN/OPNA SPECIFICS
+//*********************************************************
+
+//-------------------------------------------------
+// ymopn_registers_base - constructor
+//-------------------------------------------------
+
+template<bool IsOpnA>
+ymopn_registers_base<IsOpnA>::ymopn_registers_base() :
+ m_lfo_counter(0),
+ m_lfo_am(0)
+{
+ // create the waveforms
+ for (int index = 0; index < WAVEFORM_LENGTH; index++)
+ m_waveform[0][index] = abs_sin_attenuation(index) | (BIT(index, 9) << 15);
+}
+
+
+//-------------------------------------------------
+// save - register for save states
+//-------------------------------------------------
+
+template<bool IsOpnA>
+void ymopn_registers_base<IsOpnA>::save(device_t &device)
+{
+ if (IsOpnA)
+ {
+ device.save_item(YMFM_NAME(m_lfo_counter));
+ device.save_item(YMFM_NAME(m_lfo_am));
+ }
+ device.save_item(YMFM_NAME(m_regdata));
+}
+
+
+//-------------------------------------------------
+// reset - reset to initial state
+//-------------------------------------------------
+
+template<bool IsOpnA>
+void ymopn_registers_base<IsOpnA>::reset()
+{
+ std::fill_n(&m_regdata[0], REGISTERS, 0);
+ if (IsOpnA)
+ {
+ // enable output on both channels by default
+ m_regdata[0xb4] = m_regdata[0xb5] = m_regdata[0xb6] = 0xc0;
+ m_regdata[0x1b4] = m_regdata[0x1b5] = m_regdata[0x1b6] = 0xc0;
+ }
+}
+
+
+//-------------------------------------------------
+// operator_map - return an array of operator
+// indices for each channel; for OPN this is fixed
+//-------------------------------------------------
+
+template<>
+void ymopn_registers_base<false>::operator_map(operator_mapping &dest) const
+{
+ // Note that the channel index order is 0,2,1,3, so we bitswap the index.
+ //
+ // This is because the order in the map is:
+ // carrier 1, carrier 2, modulator 1, modulator 2
+ //
+ // But when wiring up the connections, the more natural order is:
+ // carrier 1, modulator 1, carrier 2, modulator 2
+ static const operator_mapping s_fixed_map =
+ { {
+ operator_list( 0, 6, 3, 9 ), // Channel 0 operators
+ operator_list( 1, 7, 4, 10 ), // Channel 1 operators
+ operator_list( 2, 8, 5, 11 ), // Channel 2 operators
+ } };
+ dest = s_fixed_map;
+}
+
+template<>
+void ymopn_registers_base<true>::operator_map(operator_mapping &dest) const
+{
+ // Note that the channel index order is 0,2,1,3, so we bitswap the index.
+ //
+ // This is because the order in the map is:
+ // carrier 1, carrier 2, modulator 1, modulator 2
+ //
+ // But when wiring up the connections, the more natural order is:
+ // carrier 1, modulator 1, carrier 2, modulator 2
+ static const operator_mapping s_fixed_map =
+ { {
+ operator_list( 0, 6, 3, 9 ), // Channel 0 operators
+ operator_list( 1, 7, 4, 10 ), // Channel 1 operators
+ operator_list( 2, 8, 5, 11 ), // Channel 2 operators
+ operator_list( 12, 18, 15, 21 ), // Channel 3 operators
+ operator_list( 13, 19, 16, 22 ), // Channel 4 operators
+ operator_list( 14, 20, 17, 23 ), // Channel 5 operators
+ } };
+ dest = s_fixed_map;
+}
+
+
+//-------------------------------------------------
+// write - handle writes to the register array
+//-------------------------------------------------
+
+template<bool IsOpnA>
+bool ymopn_registers_base<IsOpnA>::write(u16 index, u8 data, u32 &channel, u32 &opmask)
+{
+ assert(index < REGISTERS);
+
+ // writes in the 0xa0-af/0x1a0-af region are handled as latched pairs
+ // borrow unused registers 0xb8-bf/0x1b8-bf as temporary holding locations
+ if ((index & 0xf0) == 0xa0)
+ {
+ u32 latchindex = 0xb8 | (BIT(index, 3) << 2) | BIT(index, 0, 2);
+ if (IsOpnA)
+ latchindex |= index & 0x100;
+
+ // writes to the upper half just latch (only low 6 bits matter)
+ if (BIT(index, 2))
+ m_regdata[latchindex] = data | 0x80;
+
+ // writes to the lower half only commit if the latch is there
+ else if (BIT(m_regdata[latchindex], 7))
+ {
+ m_regdata[index | 4] = m_regdata[latchindex] & 0x3f;
+ m_regdata[latchindex] = 0;
+ }
+ }
+
+ // everything else is normal
+ m_regdata[index] = data;
+
+ // handle writes to the key on index
+ if (index == 0x28)
+ {
+ channel = BIT(data, 0, 2);
+ if (channel == 3)
+ return false;
+ if (IsOpnA)
+ channel += BIT(data, 2, 1) * 3;
+ opmask = BIT(data, 4, 4);
+ return true;
+ }
+ return false;
+}
+
+
+//-------------------------------------------------
+// clock_noise_and_lfo - clock the noise and LFO,
+// handling clock division, depth, and waveform
+// computations
+//-------------------------------------------------
+
+template<bool IsOpnA>
+s32 ymopn_registers_base<IsOpnA>::clock_noise_and_lfo()
+{
+ // OPN has no noise generation
+
+ // if LFO not enabled (not present on OPN), quick exit with 0s
+ if (!IsOpnA || !lfo_enable())
+ {
+ m_lfo_counter = 0;
+ m_lfo_am = 0;
+ return 0;
+ }
+
+ // this table is based on converting the frequencies in the applications
+ // manual to clock dividers, based on the assumption of a 7-bit LFO value
+ static u8 const lfo_max_count[8] = { 109, 78, 72, 68, 63, 45, 9, 6 };
+ u32 subcount = u8(m_lfo_counter++);
+
+ // when we cross the divider count, add enough to zero it and cause an
+ // increment at bit 8; the 7-bit value lives from bits 8-14
+ if (subcount >= lfo_max_count[lfo_rate()])
+ m_lfo_counter += subcount ^ 0xff;
+
+ // AM value is 7 bits, staring at bit 8; grab the low 6 directly
+ m_lfo_am = BIT(m_lfo_counter, 8, 6);
+
+ // first half of the AM period (bit 6 == 0) is inverted
+ if (BIT(m_lfo_counter, 8+6) == 0)
+ m_lfo_am ^= 0x3f;
+
+ // PM value is 5 bits, starting at bit 10; grab the low 3 directly
+ s32 pm = BIT(m_lfo_counter, 10, 3);
+
+ // PM is reflected based on bit 3
+ if (BIT(m_lfo_counter, 10+3))
+ pm ^= 7;
+
+ // PM is negated based on bit 4
+ return BIT(m_lfo_counter, 10+4) ? -pm : pm;
+}
+
+
+//-------------------------------------------------
+// lfo_am_offset - return the AM offset from LFO
+// for the given channel
+//-------------------------------------------------
+
+template<bool IsOpnA>
+u32 ymopn_registers_base<IsOpnA>::lfo_am_offset(u32 choffs) const
+{
+ // shift value for AM sensitivity is [7, 3, 1, 0],
+ // mapping to values of [0, 1.4, 5.9, and 11.8dB]
+ u32 am_shift = (1 << (ch_lfo_am_sens(choffs) ^ 3)) - 1;
+
+ // QUESTION: max sensitivity should give 11.8dB range, but this value
+ // is directly added to an x.8 attenuation value, which will only give
+ // 126/256 or ~4.9dB range -- what am I missing? The calculation below
+ // matches several other emulators, including the Nuked implemenation.
+
+ // raw LFO AM value on OPN is 0-3F, scale that up by a factor of 2
+ // (giving 7 bits) before applying the final shift
+ return (m_lfo_am << 1) >> am_shift;
+}
+
+
+//-------------------------------------------------
+// cache_operator_data - fill the operator cache
+// with prefetched data
+//-------------------------------------------------
+
+template<bool IsOpnA>
+void ymopn_registers_base<IsOpnA>::cache_operator_data(u32 choffs, u32 opoffs, ymfm_opdata_cache &cache)
+{
+ // set up the easy stuff
+ cache.waveform = &m_waveform[0][0];
+
+ // get frequency from the channel
+ u32 block_freq = cache.block_freq = ch_block_freq(choffs);
+
+ // if multi-frequency mode is enabled and this is channel 2,
+ // fetch one of the special frequencies
+ if (multi_freq() && choffs == 2)
+ {
+ if (opoffs == 2)
+ block_freq = cache.block_freq = multi_block_freq(1);
+ else if (opoffs == 10)
+ block_freq = cache.block_freq = multi_block_freq(2);
+ else if (opoffs == 6)
+ block_freq = cache.block_freq = multi_block_freq(0);
+ }
+
+ // compute the keycode: block_freq is:
+ //
+ // BBBFFFFFFFFFFF
+ // ^^^^???
+ //
+ // the 5-bit keycode uses the top 4 bits plus a magic formula
+ // for the final bit
+ u32 keycode = BIT(block_freq, 10, 4) << 1;
+
+ // lowest bit is determined by a mix of next lower FNUM bits
+ // according to this equation from the YM2608 manual:
+ //
+ // (F11 & (F10 | F9 | F8)) | (!F11 & F10 & F9 & F8)
+ //
+ // for speed, we just look it up in a 16-bit constant
+ keycode |= BIT(0xfe80, BIT(block_freq, 7, 4));
+
+ // detune adjustment
+ cache.detune = detune_adjustment(op_detune(opoffs), keycode);
+
+ // multiple value, as an x.1 value (0 means 0.5)
+ cache.multiple = op_multiple(opoffs) * 2;
+ if (cache.multiple == 0)
+ cache.multiple = 1;
+
+ // phase step, or PHASE_STEP_DYNAMIC if PM is active; this depends on
+ // block_freq, detune, and multiple, so compute it after we've done those
+ if (!IsOpnA || lfo_enable() == 0 || ch_lfo_pm_sens(choffs) == 0)
+ cache.phase_step = compute_phase_step(choffs, opoffs, cache, 0);
+ else
+ cache.phase_step = ymfm_opdata_cache::PHASE_STEP_DYNAMIC;
+
+ // total level, scaled by 8
+ cache.total_level = op_total_level(opoffs) << 3;
+
+ // 4-bit sustain level, but 15 means 31 so effectively 5 bits
+ cache.eg_sustain = op_sustain_level(opoffs);
+ cache.eg_sustain |= (cache.eg_sustain + 1) & 0x10;
+ cache.eg_sustain <<= 5;
+
+ // determine KSR adjustment for enevlope rates
+ u32 ksrval = keycode >> (op_ksr(opoffs) ^ 3);
+ cache.eg_rate[YMFM_ENV_ATTACK] = effective_rate(op_attack_rate(opoffs) * 2, ksrval);
+ cache.eg_rate[YMFM_ENV_DECAY] = effective_rate(op_decay_rate(opoffs) * 2, ksrval);
+ cache.eg_rate[YMFM_ENV_SUSTAIN] = effective_rate(op_sustain_rate(opoffs) * 2, ksrval);
+ cache.eg_rate[YMFM_ENV_RELEASE] = effective_rate(op_release_rate(opoffs) * 4 + 2, ksrval);
+ cache.eg_rate[YMFM_ENV_DEPRESS] = 0x3f;
+}
+
+
+//-------------------------------------------------
+// compute_phase_step - compute the phase step
+//-------------------------------------------------
+
+template<bool IsOpnA>
+u32 ymopn_registers_base<IsOpnA>::compute_phase_step(u32 choffs, u32 opoffs, ymfm_opdata_cache const &cache, s32 lfo_raw_pm)
+{
+ // OPN phase calculation has only a single detune parameter
+ // and uses FNUMs instead of keycodes
+
+ // extract frequency number (low 11 bits of block_freq)
+ u32 fnum = BIT(cache.block_freq, 0, 11) << 1;
+
+ // if there's a non-zero PM sensitivity, compute the adjustment
+ u32 pm_sensitivity = ch_lfo_pm_sens(choffs);
+ if (pm_sensitivity != 0)
+ {
+ // apply the phase adjustment based on the upper 7 bits
+ // of FNUM and the PM depth parameters
+ fnum += opn_lfo_pm_phase_adjustment(BIT(cache.block_freq, 4, 7), pm_sensitivity, lfo_raw_pm);
+
+ // keep fnum to 12 bits
+ fnum &= 0xfff;
+ }
+
+ // apply block shift to compute phase step
+ u32 block = BIT(cache.block_freq, 11, 3);
+ u32 phase_step = (fnum << block) >> 2;
+
+ // apply detune based on the keycode
+ phase_step += cache.detune;
+
+ // clamp to 17 bits in case detune overflows
+ // QUESTION: is this specific to the YM2612/3438?
+ phase_step &= 0x1ffff;
+
+ // apply frequency multiplier (which is cached as an x.1 value)
+ return (phase_step * cache.multiple) >> 1;
+}
+
+
+//-------------------------------------------------
+// log_keyon - log a key-on event
+//-------------------------------------------------
+
+template<bool IsOpnA>
+void ymopn_registers_base<IsOpnA>::log_keyon(u32 choffs, u32 opoffs)
+{
+ u32 chnum = (choffs & 3) + 3 * BIT(choffs, 8);
+ u32 opnum = (opoffs & 15) - ((opoffs & 15) / 4) + 12 * BIT(opoffs, 8);
+
+ u32 block_freq = ch_block_freq(choffs);
+ if (multi_freq() && choffs == 2)
+ {
+ if (opoffs == 2)
+ block_freq = multi_block_freq(1);
+ else if (opoffs == 10)
+ block_freq = multi_block_freq(2);
+ else if (opoffs == 6)
+ block_freq = multi_block_freq(0);
+ }
+
+ LOG("%d.%02d freq=%04X dt=%d fb=%d alg=%X mul=%X tl=%02X ksr=%d adsr=%02X/%02X/%02X/%X sl=%X",
+ chnum, opnum,
+ block_freq,
+ op_detune(opoffs),
+ ch_feedback(choffs),
+ ch_algorithm(choffs),
+ op_multiple(opoffs),
+ op_total_level(opoffs),
+ op_ksr(opoffs),
+ op_attack_rate(opoffs),
+ op_decay_rate(opoffs),
+ op_sustain_rate(opoffs),
+ op_release_rate(opoffs),
+ op_sustain_level(opoffs));
+
+ if (OUTPUTS > 1)
+ LOG(" out=%c%c",
+ ch_output_0(choffs) ? 'L' : '-',
+ ch_output_1(choffs) ? 'R' : '-');
+ if (op_ssg_eg_enable(opoffs))
+ LOG(" ssg=%X", op_ssg_eg_mode(opoffs));
+ bool am = (lfo_enable() && op_lfo_am_enable(opoffs) && ch_lfo_am_sens(choffs) != 0);
+ if (am)
+ LOG(" am=%d", ch_lfo_am_sens(choffs));
+ bool pm = (lfo_enable() && ch_lfo_pm_sens(choffs) != 0);
+ if (pm)
+ LOG(" pm=%d", ch_lfo_pm_sens(choffs));
+ if (am || pm)
+ LOG(" lfo=%02X", lfo_rate());
+ if (multi_freq() && choffs == 2)
+ LOG(" multi=1");
+}
+
+
+//*********************************************************
+// OPL SPECIFICS
+//*********************************************************
+
+//-------------------------------------------------
+// ymopl_registers_base - constructor
+//-------------------------------------------------
+
+template<int Revision>
+ymopl_registers_base<Revision>::ymopl_registers_base() :
+ m_lfo_am_counter(0),
+ m_lfo_pm_counter(0),
+ m_noise_lfsr(1),
+ m_lfo_am(0)
+{
+ // create the waveforms
+ for (int index = 0; index < WAVEFORM_LENGTH; index++)
+ m_waveform[0][index] = abs_sin_attenuation(index) | (BIT(index, 9) << 15);
+
+ if (WAVEFORMS >= 4)
+ {
+ u16 zeroval = m_waveform[0][0];
+ for (int index = 0; index < WAVEFORM_LENGTH; index++)
+ {
+ m_waveform[1][index] = BIT(index, 9) ? zeroval : m_waveform[0][index];
+ m_waveform[2][index] = m_waveform[0][index] & 0x7fff;
+ m_waveform[3][index] = BIT(index, 8) ? zeroval : (m_waveform[0][index] & 0x7fff);
+ if (WAVEFORMS >= 8)
+ {
+ m_waveform[4][index] = BIT(index, 9) ? zeroval : m_waveform[0][index * 2];
+ m_waveform[5][index] = BIT(index, 9) ? zeroval : m_waveform[0][(index * 2) & 0x1ff];
+ m_waveform[6][index] = BIT(index, 9) << 15;
+ m_waveform[7][index] = (zeroval - m_waveform[0][(index / 2)]) | (BIT(index, 9) << 15);
+ }
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// save - register for save states
+//-------------------------------------------------
+
+template<int Revision>
+void ymopl_registers_base<Revision>::save(device_t &device)
+{
+ device.save_item(YMFM_NAME(m_lfo_am_counter));
+ device.save_item(YMFM_NAME(m_lfo_pm_counter));
+ device.save_item(YMFM_NAME(m_lfo_am));
+ device.save_item(YMFM_NAME(m_noise_lfsr));
+ device.save_item(YMFM_NAME(m_regdata));
+}
+
+
+//-------------------------------------------------
+// reset - reset to initial state
+//-------------------------------------------------
+
+template<int Revision>
+void ymopl_registers_base<Revision>::reset()
+{
+ std::fill_n(&m_regdata[0], REGISTERS, 0);
+}
+
+
+//-------------------------------------------------
+// operator_map - return an array of operator
+// indices for each channel; for OPL this is fixed
+//-------------------------------------------------
+
+template<int Revision>
+void ymopl_registers_base<Revision>::operator_map(operator_mapping &dest) const
+{
+ if (Revision <= 2)
+ {
+ // OPL/OPL2 has a fixed map, all 2 operators
+ static const operator_mapping s_fixed_map =
+ { {
+ operator_list( 0, 3 ), // Channel 0 operators
+ operator_list( 1, 4 ), // Channel 1 operators
+ operator_list( 2, 5 ), // Channel 2 operators
+ operator_list( 6, 9 ), // Channel 3 operators
+ operator_list( 7, 10 ), // Channel 4 operators
+ operator_list( 8, 11 ), // Channel 5 operators
+ operator_list( 12, 15 ), // Channel 6 operators
+ operator_list( 13, 16 ), // Channel 7 operators
+ operator_list( 14, 17 ), // Channel 8 operators
+ } };
+ dest = s_fixed_map;
+ }
+ else
+ {
+ // OPL3/OPL4 can be configured for 2 or 4 operators
+ u32 fourop = fourop_enable();
+
+ dest.chan[ 0] = BIT(fourop, 0) ? operator_list( 0, 3, 6, 9 ) : operator_list( 0, 3 );
+ dest.chan[ 1] = BIT(fourop, 1) ? operator_list( 1, 4, 7, 10 ) : operator_list( 1, 4 );
+ dest.chan[ 2] = BIT(fourop, 2) ? operator_list( 2, 5, 8, 11 ) : operator_list( 2, 5 );
+ dest.chan[ 3] = BIT(fourop, 0) ? operator_list() : operator_list( 6, 9 );
+ dest.chan[ 4] = BIT(fourop, 1) ? operator_list() : operator_list( 7, 10 );
+ dest.chan[ 5] = BIT(fourop, 2) ? operator_list() : operator_list( 8, 11 );
+ dest.chan[ 6] = operator_list( 12, 15 );
+ dest.chan[ 7] = operator_list( 13, 16 );
+ dest.chan[ 8] = operator_list( 14, 17 );
+
+ dest.chan[ 9] = BIT(fourop, 3) ? operator_list( 18, 21, 24, 27 ) : operator_list( 18, 21 );
+ dest.chan[10] = BIT(fourop, 4) ? operator_list( 19, 22, 25, 28 ) : operator_list( 19, 22 );
+ dest.chan[11] = BIT(fourop, 5) ? operator_list( 20, 23, 26, 29 ) : operator_list( 20, 23 );
+ dest.chan[12] = BIT(fourop, 3) ? operator_list() : operator_list( 24, 27 );
+ dest.chan[13] = BIT(fourop, 4) ? operator_list() : operator_list( 25, 28 );
+ dest.chan[14] = BIT(fourop, 5) ? operator_list() : operator_list( 26, 29 );
+ dest.chan[15] = operator_list( 30, 33 );
+ dest.chan[16] = operator_list( 31, 34 );
+ dest.chan[17] = operator_list( 32, 35 );
+ }
+}
+
+
+//-------------------------------------------------
+// write - handle writes to the register array
+//-------------------------------------------------
+
+template<int Revision>
+bool ymopl_registers_base<Revision>::write(u16 index, u8 data, u32 &channel, u32 &opmask)
+{
+ assert(index < REGISTERS);
+
+ // writes to the mode register with high bit set ignore the low bits
+ if (index == REG_MODE && BIT(data, 7) != 0)
+ m_regdata[index] |= 0x80;
+ else
+ m_regdata[index] = data;
+
+ // handle writes to the rhythm keyons
+ if (index == 0xbd)
+ {
+ channel = YMFM_RHYTHM_CHANNEL;
+ opmask = BIT(data, 5) ? BIT(data, 0, 5) : 0;
+ return true;
+ }
+
+ // handle writes to the channel keyons
+ if ((index & 0xf0) == 0xb0)
+ {
+ channel = index & 0x0f;
+ if (channel < 9)
+ {
+ if (IsOpl3Plus)
+ channel += 9 * BIT(index, 8);
+ opmask = BIT(data, 5) ? 15 : 0;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+//-------------------------------------------------
+// clock_noise_and_lfo - clock the noise and LFO,
+// handling clock division, depth, and waveform
+// computations
+//-------------------------------------------------
+
+static s32 opl_clock_noise_and_lfo(u32 &noise_lfsr, u16 &lfo_am_counter, u16 &lfo_pm_counter, u8 &lfo_am, u32 am_depth, u32 pm_depth)
+{
+ // OPL has a 23-bit noise generator for the rhythm section, running at
+ // a constant rate, used only for percussion input
+ noise_lfsr <<= 1;
+ noise_lfsr |= BIT(noise_lfsr, 23) ^ BIT(noise_lfsr, 9) ^ BIT(noise_lfsr, 8) ^ BIT(noise_lfsr, 1);
+
+ // OPL has two fixed-frequency LFOs, one for AM, one for PM
+
+ // the AM LFO has 210*64 steps; at a nominal 50kHz output,
+ // this equates to a period of 50000/(210*64) = 3.72Hz
+ u32 am_counter = lfo_am_counter++;
+ if (am_counter >= 210*64 - 1)
+ lfo_am_counter = 0;
+
+ // low 8 bits are fractional; depth 0 is divided by 2, while depth 1 is times 2
+ int shift = 9 - 2 * am_depth;
+
+ // AM value is the upper bits of the value, inverted across the midpoint
+ // to produce a triangle
+ lfo_am = ((am_counter < 105*64) ? am_counter : (210*64+63 - am_counter)) >> shift;
+
+ // the PM LFO has 8192 steps, or a nominal period of 6.1Hz
+ u32 pm_counter = lfo_pm_counter++;
+
+ // PM LFO is broken into 8 chunks, each lasting 1024 steps; the PM value
+ // depends on the upper bits of FNUM, so this value is a fraction and
+ // sign to apply to that value, as a 1.3 value
+ static s8 const pm_scale[8] = { 8, 4, 0, -4, -8, -4, 0, 4 };
+ return pm_scale[BIT(pm_counter, 10, 3)] >> (pm_depth ^ 1);
+}
+
+template<int Revision>
+s32 ymopl_registers_base<Revision>::clock_noise_and_lfo()
+{
+ return opl_clock_noise_and_lfo(m_noise_lfsr, m_lfo_am_counter, m_lfo_pm_counter, m_lfo_am, lfo_am_depth(), lfo_pm_depth());
+}
+
+
+//-------------------------------------------------
+// cache_operator_data - fill the operator cache
+// with prefetched data; note that this code is
+// also used by ymopna_registers, so it must
+// handle upper channels cleanly
+//-------------------------------------------------
+
+template<int Revision>
+void ymopl_registers_base<Revision>::cache_operator_data(u32 choffs, u32 opoffs, ymfm_opdata_cache &cache)
+{
+ // set up the easy stuff
+ cache.waveform = &m_waveform[op_waveform(opoffs) % WAVEFORMS][0];
+
+ // get frequency from the channel
+ u32 block_freq = cache.block_freq = ch_block_freq(choffs);
+
+ // compute the keycode: block_freq is:
+ //
+ // 111 |
+ // 21098|76543210
+ // BBBFF|FFFFFFFF
+ // ^^^??
+ //
+ // the 4-bit keycode uses the top 3 bits plus one of the next two bits
+ u32 keycode = BIT(block_freq, 10, 3) << 1;
+
+ // lowest bit is determined by note_select(); note that it is
+ // actually reversed from what the manual says, however
+ keycode |= BIT(block_freq, 9 - note_select(), 1);
+
+ // no detune adjustment on OPL
+ cache.detune = 0;
+
+ // multiple value, as an x.1 value (0 means 0.5)
+ // replace the low bit with a table lookup to give 0,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15
+ u32 multiple = op_multiple(opoffs);
+ cache.multiple = ((multiple & 0xe) | BIT(0xc2aa, multiple)) * 2;
+ if (cache.multiple == 0)
+ cache.multiple = 1;
+
+ // phase step, or PHASE_STEP_DYNAMIC if PM is active; this depends on block_freq, detune,
+ // and multiple, so compute it after we've done those
+ if (op_lfo_pm_enable(opoffs) == 0)
+ cache.phase_step = compute_phase_step(choffs, opoffs, cache, 0);
+ else
+ cache.phase_step = ymfm_opdata_cache::PHASE_STEP_DYNAMIC;
+
+ // total level, scaled by 8
+ cache.total_level = op_total_level(opoffs) << 3;
+
+ // pre-add key scale level
+ u32 ksl = op_ksl(opoffs);
+ if (ksl != 0)
+ cache.total_level += opl_key_scale_atten(BIT(block_freq, 10, 3), BIT(block_freq, 6, 4)) << ksl;
+
+ // 4-bit sustain level, but 15 means 31 so effectively 5 bits
+ cache.eg_sustain = op_sustain_level(opoffs);
+ cache.eg_sustain |= (cache.eg_sustain + 1) & 0x10;
+ cache.eg_sustain <<= 5;
+
+ // determine KSR adjustment for enevlope rates
+ u32 ksrval = keycode >> (2 * (op_ksr(opoffs) ^ 1));
+ cache.eg_rate[YMFM_ENV_ATTACK] = effective_rate(op_attack_rate(opoffs) * 4, ksrval);
+ cache.eg_rate[YMFM_ENV_DECAY] = effective_rate(op_decay_rate(opoffs) * 4, ksrval);
+ cache.eg_rate[YMFM_ENV_SUSTAIN] = op_eg_sustain(opoffs) ? 0 : effective_rate(op_release_rate(opoffs) * 4, ksrval);
+ cache.eg_rate[YMFM_ENV_RELEASE] = effective_rate(op_release_rate(opoffs) * 4, ksrval);
+ cache.eg_rate[YMFM_ENV_DEPRESS] = 0x3f;
+}
+
+
+//-------------------------------------------------
+// compute_phase_step - compute the phase step
+//-------------------------------------------------
+
+static u32 opl_compute_phase_step(u32 block_freq, u32 multiple, s32 lfo_raw_pm)
+{
+ // OPL phase calculation has no detuning, but uses FNUMs like
+ // the OPN version, and computes PM a bit differently
+
+ // extract frequency number as a 12-bit fraction
+ u32 fnum = BIT(block_freq, 0, 10) << 2;
+
+ // apply the phase adjustment based on the upper 3 bits
+ // of FNUM and the PM depth parameters
+ fnum += (lfo_raw_pm * BIT(block_freq, 7, 3)) >> 1;
+
+ // keep fnum to 12 bits
+ fnum &= 0xfff;
+
+ // apply block shift to compute phase step
+ u32 block = BIT(block_freq, 10, 3);
+ u32 phase_step = (fnum << block) >> 2;
+
+ // apply frequency multiplier (which is cached as an x.1 value)
+ return (phase_step * multiple) >> 1;
+}
+
+template<int Revision>
+u32 ymopl_registers_base<Revision>::compute_phase_step(u32 choffs, u32 opoffs, ymfm_opdata_cache const &cache, s32 lfo_raw_pm)
+{
+ return opl_compute_phase_step(cache.block_freq, cache.multiple, op_lfo_pm_enable(opoffs) ? lfo_raw_pm : 0);
+}
+
+
+//-------------------------------------------------
+// log_keyon - log a key-on event
+//-------------------------------------------------
+
+template<int Revision>
+void ymopl_registers_base<Revision>::log_keyon(u32 choffs, u32 opoffs)
+{
+ u32 chnum = (choffs & 15) + 9 * BIT(choffs, 8);
+ u32 opnum = (opoffs & 31) - 2 * ((opoffs & 31) / 8) + 18 * BIT(opoffs, 8);
+
+ LOG("%2d.%02d freq=%04X fb=%d alg=%X mul=%X tl=%02X ksr=%d ns=%d ksl=%d adr=%X/%X/%X sl=%X sus=%d",
+ chnum, opnum,
+ ch_block_freq(choffs),
+ ch_feedback(choffs),
+ ch_algorithm(choffs),
+ op_multiple(opoffs),
+ op_total_level(opoffs),
+ op_ksr(opoffs),
+ note_select(),
+ op_ksl(opoffs),
+ op_attack_rate(opoffs),
+ op_decay_rate(opoffs),
+ op_release_rate(opoffs),
+ op_sustain_level(opoffs),
+ op_eg_sustain(opoffs));
+
+ if (OUTPUTS > 1)
+ LOG(" out=%c%c%c%c",
+ ch_output_0(choffs) ? 'L' : '-',
+ ch_output_1(choffs) ? 'R' : '-',
+ ch_output_2(choffs) ? '0' : '-',
+ ch_output_3(choffs) ? '1' : '-');
+ if (op_lfo_am_enable(opoffs) != 0)
+ LOG(" am=%d", lfo_am_depth());
+ if (op_lfo_pm_enable(opoffs) != 0)
+ LOG(" pm=%d", lfo_pm_depth());
+ if (waveform_enable() && op_waveform(opoffs) != 0)
+ LOG(" wf=%d", op_waveform(opoffs));
+ if (is_rhythm(choffs))
+ LOG(" rhy=1");
+ if (DYNAMIC_OPS)
+ {
+ operator_mapping map;
+ operator_map(map);
+ if (BIT(map.chan[chnum], 16, 8) != 0xff)
+ LOG(" 4op");
+ }
+}
+
+
+//*********************************************************
+// OPLL SPECIFICS
+//*********************************************************
+
+//-------------------------------------------------
+// ymopll_registers - constructor
+//-------------------------------------------------
+
+ymopll_registers::ymopll_registers() :
+ m_lfo_am_counter(0),
+ m_lfo_pm_counter(0),
+ m_noise_lfsr(1),
+ m_lfo_am(0)
+{
+ // create the waveforms
+ for (int index = 0; index < WAVEFORM_LENGTH; index++)
+ m_waveform[0][index] = abs_sin_attenuation(index) | (BIT(index, 9) << 15);
+
+ u16 zeroval = m_waveform[0][0];
+ for (int index = 0; index < WAVEFORM_LENGTH; index++)
+ m_waveform[1][index] = BIT(index, 9) ? zeroval : m_waveform[0][index];
+
+ // initialize the instruments to something sane
+ for (int choffs = 0; choffs < CHANNELS; choffs++)
+ m_chinst[choffs] = &m_regdata[0];
+ for (int opoffs = 0; opoffs < OPERATORS; opoffs++)
+ m_opinst[opoffs] = &m_regdata[BIT(opoffs, 0)];
+}
+
+
+//-------------------------------------------------
+// save - register for save states
+//-------------------------------------------------
+
+void ymopll_registers::save(device_t &device)
+{
+ device.save_item(YMFM_NAME(m_lfo_am_counter));
+ device.save_item(YMFM_NAME(m_lfo_pm_counter));
+ device.save_item(YMFM_NAME(m_lfo_am));
+ device.save_item(YMFM_NAME(m_noise_lfsr));
+ device.save_item(YMFM_NAME(m_regdata));
+}
+
+
+//-------------------------------------------------
+// reset - reset to initial state
+//-------------------------------------------------
+
+void ymopll_registers::reset()
+{
+ std::fill_n(&m_regdata[0], REGISTERS, 0);
+}
+
+
+//-------------------------------------------------
+// operator_map - return an array of operator
+// indices for each channel; for OPLL this is fixed
+//-------------------------------------------------
+
+void ymopll_registers::operator_map(operator_mapping &dest) const
+{
+ static const operator_mapping s_fixed_map =
+ { {
+ operator_list( 0, 1 ), // Channel 0 operators
+ operator_list( 2, 3 ), // Channel 1 operators
+ operator_list( 4, 5 ), // Channel 2 operators
+ operator_list( 6, 7 ), // Channel 3 operators
+ operator_list( 8, 9 ), // Channel 4 operators
+ operator_list( 10, 11 ), // Channel 5 operators
+ operator_list( 12, 13 ), // Channel 6 operators
+ operator_list( 14, 15 ), // Channel 7 operators
+ operator_list( 16, 17 ), // Channel 8 operators
+ } };
+ dest = s_fixed_map;
+}
+
+
+//-------------------------------------------------
+// write - handle writes to the register array;
+// note that this code is also used by
+// ymopl3_registers, so it must handle upper
+// channels cleanly
+//-------------------------------------------------
+
+bool ymopll_registers::write(u16 index, u8 data, u32 &channel, u32 &opmask)
+{
+ assert(index < REGISTERS);
+
+ // write the new data
+ m_regdata[index] = data;
+
+ // handle writes to the rhythm keyons
+ if (index == 0x0e)
+ {
+ channel = YMFM_RHYTHM_CHANNEL;
+ opmask = BIT(data, 5) ? BIT(data, 0, 5) : 0;
+ return true;
+ }
+
+ // handle writes to the channel keyons
+ if ((index & 0xf0) == 0x20)
+ {
+ channel = index & 0x0f;
+ if (channel < CHANNELS)
+ {
+ opmask = BIT(data, 4) ? 3 : 0;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+//-------------------------------------------------
+// clock_noise_and_lfo - clock the noise and LFO,
+// handling clock division, depth, and waveform
+// computations
+//-------------------------------------------------
+
+s32 ymopll_registers::clock_noise_and_lfo()
+{
+ // implementation is the same as OPL with fixed depths
+ return opl_clock_noise_and_lfo(m_noise_lfsr, m_lfo_am_counter, m_lfo_pm_counter, m_lfo_am, 1, 1);
+}
+
+
+//-------------------------------------------------
+// cache_operator_data - fill the operator cache
+// with prefetched data; note that this code is
+// also used by ymopna_registers, so it must
+// handle upper channels cleanly
+//-------------------------------------------------
+
+void ymopll_registers::cache_operator_data(u32 choffs, u32 opoffs, ymfm_opdata_cache &cache)
+{
+ // first set up the instrument data
+ u32 instrument = ch_instrument(choffs);
+ if (rhythm_enable() && choffs >= 6)
+ m_chinst[choffs] = &m_instdata[8 * (15 + (choffs - 6))];
+ else
+ m_chinst[choffs] = (instrument == 0) ? &m_regdata[0] : &m_instdata[8 * (instrument - 1)];
+ m_opinst[opoffs] = m_chinst[choffs] + BIT(opoffs, 0);
+
+ // set up the easy stuff
+ cache.waveform = &m_waveform[op_waveform(opoffs) % WAVEFORMS][0];
+
+ // get frequency from the channel
+ u32 block_freq = cache.block_freq = ch_block_freq(choffs);
+
+ // compute the keycode: block_freq is:
+ //
+ // 11 |
+ // 1098|76543210
+ // BBBF|FFFFFFFF
+ // ^^^^
+ //
+ // the 4-bit keycode uses the top 4 bits
+ u32 keycode = BIT(block_freq, 8, 4);
+
+ // no detune adjustment on OPLL
+ cache.detune = 0;
+
+ // multiple value, as an x.1 value (0 means 0.5)
+ // replace the low bit with a table lookup to give 0,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15
+ u32 multiple = op_multiple(opoffs);
+ cache.multiple = ((multiple & 0xe) | BIT(0xc2aa, multiple)) * 2;
+ if (cache.multiple == 0)
+ cache.multiple = 1;
+
+ // phase step, or PHASE_STEP_DYNAMIC if PM is active; this depends on
+ // block_freq, detune, and multiple, so compute it after we've done those
+ if (op_lfo_pm_enable(opoffs) == 0)
+ cache.phase_step = compute_phase_step(choffs, opoffs, cache, 0);
+ else
+ cache.phase_step = ymfm_opdata_cache::PHASE_STEP_DYNAMIC;
+
+ // total level, scaled by 8; for non-rhythm operator 0, this is the total
+ // level from the instrument data; for other operators it is 4*volume
+ if (BIT(opoffs, 0) == 1 || (rhythm_enable() && choffs >= 7))
+ cache.total_level = op_volume(opoffs) * 4;
+ else
+ cache.total_level = ch_total_level(choffs);
+ cache.total_level <<= 3;
+
+ // pre-add key scale level
+ u32 ksl = op_ksl(opoffs);
+ if (ksl != 0)
+ cache.total_level += opl_key_scale_atten(BIT(block_freq, 9, 3), BIT(block_freq, 5, 4)) << ksl;
+
+ // 4-bit sustain level, but 15 means 31 so effectively 5 bits
+ cache.eg_sustain = op_sustain_level(opoffs);
+ cache.eg_sustain |= (cache.eg_sustain + 1) & 0x10;
+ cache.eg_sustain <<= 5;
+
+ // The envelope diagram in the YM2413 datasheet gives values for these
+ // in ms from 0->48dB. The attack/decay tables give values in ms from
+ // 0->96dB, so to pick an equivalent decay rate, we want to find the
+ // closest match that is 2x the 0->48dB value:
+ //
+ // DP = 10ms (0->48db) -> 20ms (0->96db); decay of 12 gives 19.20ms
+ // RR = 310ms (0->48db) -> 620ms (0->96db); decay of 7 gives 613.76ms
+ // RS = 1200ms (0->48db) -> 2400ms (0->96db); decay of 5 gives 2455.04ms
+ //
+ // The envelope diagram for percussive sounds (eg_sustain() == 0) also uses
+ // "RR" to mean both the constant RR above and the Release Rate specified in
+ // the instrument data. In this case, Relief Pitcher's credit sound bears out
+ // that the Release Rate is used during sustain, and that the constant RR
+ // (or RS) is used during the release phase.
+ constexpr u8 DP = 12 * 4;
+ constexpr u8 RR = 7 * 4;
+ constexpr u8 RS = 5 * 4;
+
+ // determine KSR adjustment for envelope rates
+ u32 ksrval = keycode >> (2 * (op_ksr(opoffs) ^ 1));
+ cache.eg_rate[YMFM_ENV_DEPRESS] = DP;
+ cache.eg_rate[YMFM_ENV_ATTACK] = effective_rate(op_attack_rate(opoffs) * 4, ksrval);
+ cache.eg_rate[YMFM_ENV_DECAY] = effective_rate(op_decay_rate(opoffs) * 4, ksrval);
+ if (op_eg_sustain(opoffs))
+ {
+ cache.eg_rate[YMFM_ENV_SUSTAIN] = 0;
+ cache.eg_rate[YMFM_ENV_RELEASE] = ch_sustain(choffs) ? RS : effective_rate(op_release_rate(opoffs) * 4, ksrval);
+ }
+ else
+ {
+ cache.eg_rate[YMFM_ENV_SUSTAIN] = effective_rate(op_release_rate(opoffs) * 4, ksrval);
+ cache.eg_rate[YMFM_ENV_RELEASE] = ch_sustain(choffs) ? RS : RR;
+ }
+}
+
+
+//-------------------------------------------------
+// compute_phase_step - compute the phase step
+//-------------------------------------------------
+
+u32 ymopll_registers::compute_phase_step(u32 choffs, u32 opoffs, ymfm_opdata_cache const &cache, s32 lfo_raw_pm)
+{
+ // phase step computation is the same as OPL but the block_freq has one
+ // more bit, which we shift in
+ return opl_compute_phase_step(cache.block_freq << 1, cache.multiple, op_lfo_pm_enable(opoffs) ? lfo_raw_pm : 0);
+}
+
+
+//-------------------------------------------------
+// log_keyon - log a key-on event
+//-------------------------------------------------
+
+void ymopll_registers::log_keyon(u32 choffs, u32 opoffs)
+{
+ u32 chnum = choffs;
+ u32 opnum = opoffs;
+
+ LOG("%d.%02d freq=%04X inst=%X fb=%d mul=%X",
+ chnum, opnum,
+ ch_block_freq(choffs),
+ ch_instrument(choffs),
+ ch_feedback(choffs),
+ op_multiple(opoffs));
+
+ if (BIT(opoffs, 0) == 1 || (is_rhythm(choffs) && choffs >= 6))
+ LOG(" vol=%X", op_volume(opoffs));
+ else
+ LOG(" tl=%02X", ch_total_level(choffs));
+
+ LOG(" ksr=%d ksl=%d adr=%X/%X/%X sl=%X sus=%d/%d",
+ op_ksr(opoffs),
+ op_ksl(opoffs),
+ op_attack_rate(opoffs),
+ op_decay_rate(opoffs),
+ op_release_rate(opoffs),
+ op_sustain_level(opoffs),
+ op_eg_sustain(opoffs),
+ ch_sustain(choffs));
+
+ if (op_lfo_am_enable(opoffs))
+ LOG(" am=1");
+ if (op_lfo_pm_enable(opoffs))
+ LOG(" pm=1");
+ if (op_waveform(opoffs) != 0)
+ LOG(" wf=1");
+ if (is_rhythm(choffs))
+ LOG(" rhy=1");
+}
+
//*********************************************************
// YMFM OPERATOR
@@ -441,15 +2081,17 @@ inline u32 opm_keycode_to_phase_step(u16 block_freq, s16 delta)
//-------------------------------------------------
template<class RegisterType>
-ymfm_operator<RegisterType>::ymfm_operator(RegisterType regs) :
+ymfm_operator<RegisterType>::ymfm_operator(ymfm_engine_base<RegisterType> &owner, u32 opoffs) :
+ m_choffs(0),
+ m_opoffs(opoffs),
m_phase(0),
m_env_attenuation(0x3ff),
- m_env_state(ENV_RELEASE),
+ m_env_state(YMFM_ENV_RELEASE),
m_ssg_inverted(false),
m_key_state(0),
- m_keyon(0),
- m_csm_triggered(0),
- m_regs(regs)
+ m_keyon_live(0),
+ m_regs(owner.regs()),
+ m_owner(owner)
{
}
@@ -458,12 +2100,10 @@ ymfm_operator<RegisterType>::ymfm_operator(RegisterType regs) :
// save - register for save states
//-------------------------------------------------
-ALLOW_SAVE_TYPE(ymfm_operator<ymopm_registers>::envelope_state);
-ALLOW_SAVE_TYPE(ymfm_operator<ymopn_registers>::envelope_state);
-ALLOW_SAVE_TYPE(ymfm_operator<ymopna_registers>::envelope_state);
+ALLOW_SAVE_TYPE(ymfm_envelope_state);
template<class RegisterType>
-void ymfm_operator<RegisterType>::save(device_t &device, u8 index)
+void ymfm_operator<RegisterType>::save(device_t &device, u32 index)
{
// save our data
device.save_item(YMFM_NAME(m_phase), index);
@@ -471,8 +2111,7 @@ void ymfm_operator<RegisterType>::save(device_t &device, u8 index)
device.save_item(YMFM_NAME(m_env_state), index);
device.save_item(YMFM_NAME(m_ssg_inverted), index);
device.save_item(YMFM_NAME(m_key_state), index);
- device.save_item(YMFM_NAME(m_keyon), index);
- device.save_item(YMFM_NAME(m_csm_triggered), index);
+ device.save_item(YMFM_NAME(m_keyon_live), index);
}
@@ -486,36 +2125,49 @@ void ymfm_operator<RegisterType>::reset()
// reset our data
m_phase = 0;
m_env_attenuation = 0x3ff;
- m_env_state = ENV_RELEASE;
+ m_env_state = YMFM_ENV_RELEASE;
m_ssg_inverted = 0;
m_key_state = 0;
- m_keyon = 0;
- m_csm_triggered = 0;
+ m_keyon_live = 0;
}
//-------------------------------------------------
-// clock - master clocking function
+// prepare - prepare for clocking
//-------------------------------------------------
template<class RegisterType>
-void ymfm_operator<RegisterType>::clock(u32 env_counter, s8 lfo_raw_pm, u16 block_freq)
+bool ymfm_operator<RegisterType>::prepare()
{
+ // cache the data
+ m_regs.cache_operator_data(m_choffs, m_opoffs, m_cache);
+
// clock the key state
- u8 keycode = block_freq_to_keycode(block_freq);
- clock_keystate(m_keyon | m_csm_triggered, keycode);
- m_csm_triggered = 0;
+ clock_keystate(u32(m_keyon_live != 0));
+ m_keyon_live &= ~(1 << YMFM_KEYON_CSM);
+
+ // we're active until we're quiet after the release
+ return (m_env_state != YMFM_ENV_RELEASE || m_env_attenuation < ENV_QUIET);
+}
+
+//-------------------------------------------------
+// clock - master clocking function
+//-------------------------------------------------
+
+template<class RegisterType>
+void ymfm_operator<RegisterType>::clock(u32 env_counter, s32 lfo_raw_pm)
+{
// clock the SSG-EG state (OPN/OPNA)
- if (m_regs.ssg_eg_enabled())
- clock_ssg_eg_state(keycode);
+ if (m_regs.op_ssg_eg_enable(m_opoffs))
+ clock_ssg_eg_state();
- // clock the envelope if on an envelope cycle
+ // clock the envelope if on an envelope cycle; env_counter is a x.2 value
if (BIT(env_counter, 0, 2) == 0)
- clock_envelope(env_counter >> 2, keycode);
+ clock_envelope(env_counter >> 2);
// clock the phase
- clock_phase(lfo_raw_pm, block_freq);
+ clock_phase(lfo_raw_pm);
}
@@ -526,28 +2178,26 @@ void ymfm_operator<RegisterType>::clock(u32 env_counter, s8 lfo_raw_pm, u16 bloc
//-------------------------------------------------
template<class RegisterType>
-s16 ymfm_operator<RegisterType>::compute_volume(u16 modulation, u16 am_offset) const
+s32 ymfm_operator<RegisterType>::compute_volume(u32 phase, u32 am_offset) const
{
+ // the low 10 bits of phase represents a full 2*PI period over
+ // the full sin wave
+
// early out if the envelope is effectively off
if (m_env_attenuation > ENV_QUIET)
return 0;
- // start with the upper 10 bits of the phase value plus modulation
- // the low 10 bits of this result represents a full 2*PI period over
- // the full sin wave
- u16 phase = (m_phase >> 10) + modulation;
-
// get the absolute value of the sin, as attenuation, as a 4.8 fixed point value
- u16 sin_attenuation = abs_sin_attenuation(phase);
+ u32 sin_attenuation = m_cache.waveform[phase & (RegisterType::WAVEFORM_LENGTH - 1)];
// get the attenuation from the evelope generator as a 4.6 value, shifted up to 4.8
- u16 env_attenuation = envelope_attenuation(am_offset) << 2;
+ u32 env_attenuation = envelope_attenuation(am_offset) << 2;
// combine into a 5.8 value, then convert from attenuation to 13-bit linear volume
- s16 result = attenuation_to_volume(sin_attenuation + env_attenuation);
+ s32 result = attenuation_to_volume((sin_attenuation & 0x7fff) + env_attenuation);
// negate if in the negative part of the sin wave (sign bit gives 14 bits)
- return BIT(phase, 9) ? -result : result;
+ return BIT(sin_attenuation, 15) ? -result : result;
}
@@ -558,64 +2208,28 @@ s16 ymfm_operator<RegisterType>::compute_volume(u16 modulation, u16 am_offset) c
//-------------------------------------------------
template<class RegisterType>
-s16 ymfm_operator<RegisterType>::compute_noise_volume(u8 noise_state, u16 am_offset) const
+s32 ymfm_operator<RegisterType>::compute_noise_volume(u32 am_offset) const
{
// application manual says the logarithmic transform is not applied here, so we
// just use the raw envelope attenuation, inverted (since 0 attenuation should be
// maximum), and shift it up from a 10-bit value to an 11-bit value
- u16 result = (envelope_attenuation(am_offset) ^ 0x3ff) << 1;
+ u32 result = (envelope_attenuation(am_offset) ^ 0x3ff) << 1;
// QUESTION: is AM applied still?
// negate based on the noise state
- return BIT(noise_state, 0) ? -result : result;
+ return BIT(m_regs.noise_state(), 0) ? -result : result;
}
//-------------------------------------------------
-// block_freq_to_keycode - given a concatenated
-// block+frequency value, return the 5-bit keycode
+// keyonoff - signal a key on/off event
//-------------------------------------------------
-// OPM version
-template<>
-u8 ymfm_operator<ymopm_registers>::block_freq_to_keycode(u16 block_freq)
-{
- // block_freq is block(3b):keycode(4b):keyfrac(6b); the 5-bit keycode
- // we want is just the top 5 bits here
- return BIT(block_freq, 8, 5);
-}
-
-// OPN/OPNA version
template<class RegisterType>
-u8 ymfm_operator<RegisterType>::block_freq_to_keycode(u16 block_freq)
+void ymfm_operator<RegisterType>::keyonoff(u32 on, ymfm_keyon_type type)
{
- // block_freq is block(3b):fnum(11b); the 5-bit keycode uses the top
- // 4 bits plus a magic formula for the final bit
- u8 keycode = BIT(block_freq, 10, 4) << 1;
-
- // lowest bit is determined by a mix of next lower FNUM bits
- // according to this equation from the YM2608 manual:
- //
- // (F11 & (F10 | F9 | F8)) | (!F11 & F10 & F9 & F8)
- //
- // for speed, we just look it up in a 16-bit constant
- return keycode | BIT(0xfe80, BIT(block_freq, 7, 4));
-}
-
-
-//-------------------------------------------------
-// effective_rate - return the effective 6-bit
-// ADSR rate value after adjusting for keycode
-//-------------------------------------------------
-
-template<class RegisterType>
-u8 ymfm_operator<RegisterType>::effective_rate(u8 rawrate, u8 keycode)
-{
- if (rawrate == 0)
- return 0;
- u8 rate = rawrate * 2 + (keycode >> (m_regs.ksr() ^ 3));
- return (rate < 64) ? rate : 63;
+ m_keyon_live = (m_keyon_live & ~(1 << int(type))) | (BIT(on, 0) << int(type));
}
@@ -626,52 +2240,24 @@ u8 ymfm_operator<RegisterType>::effective_rate(u8 rawrate, u8 keycode)
//-------------------------------------------------
template<class RegisterType>
-void ymfm_operator<RegisterType>::start_attack(u8 keycode)
+void ymfm_operator<RegisterType>::start_attack()
{
// don't change anything if already in attack state
- if (m_env_state == ENV_ATTACK)
+ if (m_env_state == YMFM_ENV_ATTACK)
return;
- m_env_state = ENV_ATTACK;
+ m_env_state = YMFM_ENV_ATTACK;
// generally not inverted at start, except if SSG-EG is
// enabled and one of the inverted modes is specified
- m_ssg_inverted = m_regs.ssg_eg_enabled() & BIT(m_regs.ssg_eg_mode(), 2);
+ if (RegisterType::EG_HAS_SSG)
+ m_ssg_inverted = m_regs.op_ssg_eg_enable(m_opoffs) & BIT(m_regs.op_ssg_eg_mode(m_opoffs), 2);
// reset the phase when we start an attack
m_phase = 0;
// if the attack rate >= 62 then immediately go to max attenuation
- if (effective_rate(m_regs.attack_rate(), keycode) >= 62)
+ if (m_cache.eg_rate[YMFM_ENV_ATTACK] >= 62)
m_env_attenuation = 0;
-
- // log key on events under certain conditions
- if (m_regs.lfo_waveform() == 3 && m_regs.lfo_enabled() && ((m_regs.lfo_am_enabled() && m_regs.lfo_am_sensitivity() != 0) || m_regs.lfo_pm_sensitivity() != 0))
- {
- LOG("KeyOn %d.%d: freq=%04X dt2=%d fb=%d alg=%d dt=%d mul=%X tl=%02X ksr=%d adsr=%02X/%02X/%02X/%X sl=%X pan=%c%c",
- m_regs.chnum(), m_regs.opnum(),
- m_regs.block_freq(),
- m_regs.detune2(),
- m_regs.feedback(),
- m_regs.algorithm(),
- m_regs.detune(),
- m_regs.multiple(),
- m_regs.total_level(),
- m_regs.ksr(),
- m_regs.attack_rate(),
- m_regs.decay_rate(),
- m_regs.sustain_rate(),
- m_regs.release_rate(),
- m_regs.sustain_level(),
- m_regs.pan_left() ? 'L' : '-',
- m_regs.pan_right() ? 'R' : '-');
- if (m_regs.ssg_eg_enabled())
- LOG(" ssg=%X", m_regs.ssg_eg_mode());
- if (m_regs.lfo_enabled() && ((m_regs.lfo_am_enabled() && m_regs.lfo_am_sensitivity() != 0) || m_regs.lfo_pm_sensitivity() != 0))
- LOG(" am=%d pm=%d w=%d", m_regs.lfo_am_enabled() ? m_regs.lfo_am_sensitivity() : 0, m_regs.lfo_pm_sensitivity(), m_regs.lfo_waveform());
- if (m_regs.noise_enabled() && m_regs.opnum() == 3 && m_regs.chnum() == 7)
- LOG(" noise=1");
- LOG("\n");
- }
}
@@ -684,12 +2270,12 @@ template<class RegisterType>
void ymfm_operator<RegisterType>::start_release()
{
// don't change anything if already in release state
- if (m_env_state == ENV_RELEASE)
+ if (m_env_state == YMFM_ENV_RELEASE)
return;
- m_env_state = ENV_RELEASE;
+ m_env_state = YMFM_ENV_RELEASE;
// adjust attenuation if inverted due to SSG-EG
- if (m_ssg_inverted)
+ if (RegisterType::EG_HAS_SSG && m_ssg_inverted)
m_env_attenuation = 0x200 - m_env_attenuation;
}
@@ -700,7 +2286,7 @@ void ymfm_operator<RegisterType>::start_release()
//-------------------------------------------------
template<class RegisterType>
-void ymfm_operator<RegisterType>::clock_keystate(u8 keystate, u8 keycode)
+void ymfm_operator<RegisterType>::clock_keystate(u32 keystate)
{
assert(keystate == 0 || keystate == 1);
@@ -711,7 +2297,24 @@ void ymfm_operator<RegisterType>::clock_keystate(u8 keystate, u8 keycode)
// if the key has turned on, start the attack
if (keystate != 0)
- start_attack(keycode);
+ {
+ // log key on events under certain conditions
+ // if (m_regs.lfo_waveform() == 3 && m_regs.lfo_enable() && ((m_regs.lfo_am_enable() && m_regs.lfo_am_sensitivity() != 0) || m_regs.lfo_pm_sensitivity() != 0))
+ // if ((m_regs.rhythm_enable() && m_regs.chnum() >= 6) ||
+ // (m_regs.waveform_enable() && m_regs.waveform() != 0))
+ {
+ LOG("%s: ", m_owner.device().tag(), m_opoffs);
+ m_regs.log_keyon(m_choffs, m_opoffs);
+ LOG("\n");
+ }
+
+ // OPLL has a DP ("depress"?) state to bring the volume
+ // down before starting the attack
+ if (RegisterType::EG_HAS_DEPRESS && m_env_attenuation < 0x200)
+ m_env_state = YMFM_ENV_DEPRESS;
+ else
+ start_attack();
+ }
// otherwise, start the release
else
@@ -726,7 +2329,7 @@ void ymfm_operator<RegisterType>::clock_keystate(u8 keystate, u8 keycode)
//-------------------------------------------------
template<class RegisterType>
-void ymfm_operator<RegisterType>::clock_ssg_eg_state(u8 keycode)
+void ymfm_operator<RegisterType>::clock_ssg_eg_state()
{
// work only happens once the attenuation crosses above 0x200
if (!BIT(m_env_attenuation, 9))
@@ -741,7 +2344,7 @@ void ymfm_operator<RegisterType>::clock_ssg_eg_state(u8 keycode)
// 101: inverted run once, hold low
// 110: inverted repeat, alternating between inverted/non-inverted
// 111: inverted run once, hold high
- u8 mode = m_regs.ssg_eg_mode();
+ u32 mode = m_regs.op_ssg_eg_mode(m_opoffs);
// hold modes (1/3/5/7)
if (BIT(mode, 0))
@@ -751,7 +2354,7 @@ void ymfm_operator<RegisterType>::clock_ssg_eg_state(u8 keycode)
// if holding low (modes 1/5), force the attenuation to maximum
// once we're past the attack phase
- if (m_env_state != ENV_ATTACK && BIT(mode, 1) == 0)
+ if (m_env_state != YMFM_ENV_ATTACK && BIT(mode, 1) == 0)
m_env_attenuation = 0x3ff;
}
@@ -762,8 +2365,8 @@ void ymfm_operator<RegisterType>::clock_ssg_eg_state(u8 keycode)
m_ssg_inverted ^= BIT(mode, 1);
// restart attack if in decay/sustain states
- if (m_env_state == ENV_DECAY || m_env_state == ENV_SUSTAIN)
- start_attack(keycode);
+ if (m_env_state == YMFM_ENV_DECAY || m_env_state == YMFM_ENV_SUSTAIN)
+ start_attack();
// phase is reset to 0 regardless in modes 0/4
if (BIT(mode, 1) == 0)
@@ -771,7 +2374,7 @@ void ymfm_operator<RegisterType>::clock_ssg_eg_state(u8 keycode)
}
// in all modes, once we hit release state, attenuation is forced to maximum
- if (m_env_state == ENV_RELEASE)
+ if (m_env_state == YMFM_ENV_RELEASE)
m_env_attenuation = 0x3ff;
}
@@ -782,31 +2385,21 @@ void ymfm_operator<RegisterType>::clock_ssg_eg_state(u8 keycode)
//-------------------------------------------------
template<class RegisterType>
-void ymfm_operator<RegisterType>::clock_envelope(u16 env_counter, u8 keycode)
+void ymfm_operator<RegisterType>::clock_envelope(u32 env_counter)
{
- // if in attack state, see if we hit minimum attenuation
- if (m_env_state == ENV_ATTACK && m_env_attenuation == 0)
- m_env_state = ENV_DECAY;
+ // handle attack->decay and decay->sustain transitions
+ if (m_env_state == YMFM_ENV_ATTACK && m_env_attenuation == 0)
+ m_env_state = YMFM_ENV_DECAY;
+ else if (m_env_state == YMFM_ENV_DECAY && m_env_attenuation >= m_cache.eg_sustain)
+ m_env_state = YMFM_ENV_SUSTAIN;
- // if in decay state, see if we hit the sustain level
- else if (m_env_state == ENV_DECAY)
- {
- // 4-bit sustain level, but 15 means 31 so effectively 5 bits
- u8 target = m_regs.sustain_level();
- target |= (target + 1) & 0x10;
-
- // bring current attenuation down to 5 bits and compare
- if ((m_env_attenuation >> 5) >= target)
- m_env_state = ENV_SUSTAIN;
- }
-
- // determine our raw 5-bit rate value
- u8 rate = effective_rate(m_regs.adsr_rate(m_env_state), keycode);
+ // fetch the appropriate 6-bit rate value from the cache
+ u32 rate = m_cache.eg_rate[m_env_state];
// compute the rate shift value; this is the shift needed to
// apply to the env_counter such that it becomes a 5.11 fixed
// point number
- u8 rate_shift = rate >> 2;
+ u32 rate_shift = rate >> 2;
env_counter <<= rate_shift;
// see if the fractional part is 0; if not, it's not time to clock
@@ -814,10 +2407,10 @@ void ymfm_operator<RegisterType>::clock_envelope(u16 env_counter, u8 keycode)
return;
// determine the increment based on the non-fractional part of env_counter
- u8 increment = attenuation_increment(rate, BIT(env_counter, 11, 3));
+ u32 increment = attenuation_increment(rate, BIT(env_counter, 11, 3));
// attack is the only one that increases
- if (m_env_state == ENV_ATTACK)
+ if (m_env_state == YMFM_ENV_ATTACK)
{
// glitch means that attack rates of 62/63 don't increment if
// changed after the initial key on (where they are handled
@@ -834,7 +2427,7 @@ void ymfm_operator<RegisterType>::clock_envelope(u16 env_counter, u8 keycode)
else
{
// non-SSG-EG cases just apply the increment
- if (!m_regs.ssg_eg_enabled())
+ if (!m_regs.op_ssg_eg_enable(m_opoffs))
m_env_attenuation += increment;
// SSG-EG only applies if less than mid-point, and then at 4x
@@ -844,6 +2437,10 @@ void ymfm_operator<RegisterType>::clock_envelope(u16 env_counter, u8 keycode)
// clamp the final attenuation
if (m_env_attenuation >= 0x400)
m_env_attenuation = 0x3ff;
+
+ // transition from depress to attack
+ if (RegisterType::EG_HAS_DEPRESS && m_env_state == YMFM_ENV_DEPRESS && m_env_attenuation >= 0x200)
+ start_attack();
}
}
@@ -854,84 +2451,13 @@ void ymfm_operator<RegisterType>::clock_envelope(u16 env_counter, u8 keycode)
// against the Nuked phase generator
//-------------------------------------------------
-// OPM version
-template<>
-void ymfm_operator<ymopm_registers>::clock_phase(s8 lfo_raw_pm, u16 block_freq)
-{
- // start with coarse detune delta; table uses cents value from
- // manual, converted into 1/64ths
- static const s16 s_detune2_delta[4] = { 0, (600*64+50)/100, (781*64+50)/100, (950*64+50)/100 };
- s16 delta = s_detune2_delta[m_regs.detune2()];
-
- // add in the PM delta
- u8 pm_sensitivity = m_regs.lfo_pm_sensitivity();
- if (pm_sensitivity != 0)
- {
- // raw PM value is -127..128 which is +/- 200 cents
- // manual gives these magnitudes in cents:
- // 0, +/-5, +/-10, +/-20, +/-50, +/-100, +/-400, +/-700
- // this roughly corresponds to shifting the 200-cent value:
- // 0 >> 5, >> 4, >> 3, >> 2, >> 1, << 1, << 2
- if (pm_sensitivity < 6)
- delta += lfo_raw_pm >> (6 - pm_sensitivity);
- else
- delta += lfo_raw_pm << (pm_sensitivity - 5);
- }
-
- // apply delta and convert to a frequency number
- u32 phase_step = opm_keycode_to_phase_step(block_freq, delta);
-
- // apply detune based on the keycode
- phase_step += detune_adjustment(m_regs.detune(), block_freq_to_keycode(block_freq));
-
- // QUESTION: do we clamp to 17 bits like YM2612?
-
- // apply frequency multiplier (0 means 0.5, other values are as-is)
- u8 multiple = m_regs.multiple();
- if (multiple == 0)
- phase_step >>= 1;
- else
- phase_step *= multiple;
-
- // finally apply the step to the current phase value
- m_phase += phase_step;
-}
-
template<class RegisterType>
-void ymfm_operator<RegisterType>::clock_phase(s8 lfo_raw_pm, u16 block_freq)
+void ymfm_operator<RegisterType>::clock_phase(s32 lfo_raw_pm)
{
- // extract frequency number (low 11 bits of block_freq)
- u16 fnum = BIT(block_freq, 0, 11) << 1;
-
- // if there's a non-zero PM sensitivity, compute the adjustment
- u8 pm_sensitivity = m_regs.lfo_pm_sensitivity();
- if (pm_sensitivity != 0)
- {
- // apply the phase adjustment based on the upper 7 bits
- // of FNUM and the PM depth parameters
- fnum += opn_lfo_pm_phase_adjustment(BIT(block_freq, 4, 7), pm_sensitivity, lfo_raw_pm);
-
- // keep fnum to 12 bits
- fnum &= 0xfff;
- }
-
- // apply block shift to compute phase step
- u8 block = BIT(block_freq, 11, 3);
- u32 phase_step = (fnum << block) >> 2;
-
- // apply detune based on the keycode
- phase_step += detune_adjustment(m_regs.detune(), block_freq_to_keycode(block_freq));
-
- // clamp to 17 bits in case detune overflows
- // QUESTION: is this specific to the YM2612/3438?
- phase_step &= 0x1ffff;
-
- // apply frequency multiplier (0 means 0.5, other values are as-is)
- u8 multiple = m_regs.multiple();
- if (multiple == 0)
- phase_step >>= 1;
- else
- phase_step *= multiple;
+ // read from the cache, or recalculate if PM active
+ u32 phase_step = m_cache.phase_step;
+ if (phase_step == ymfm_opdata_cache::PHASE_STEP_DYNAMIC)
+ phase_step = m_regs.compute_phase_step(m_choffs, m_opoffs, m_cache, lfo_raw_pm);
// finally apply the step to the current phase value
m_phase += phase_step;
@@ -944,20 +2470,20 @@ void ymfm_operator<RegisterType>::clock_phase(s8 lfo_raw_pm, u16 block_freq)
//-------------------------------------------------
template<class RegisterType>
-u16 ymfm_operator<RegisterType>::envelope_attenuation(u8 am_offset) const
+u32 ymfm_operator<RegisterType>::envelope_attenuation(u32 am_offset) const
{
- u16 result = m_env_attenuation;
+ u32 result = m_env_attenuation;
// invert if necessary due to SSG-EG
- if (m_ssg_inverted)
+ if (RegisterType::EG_HAS_SSG && m_ssg_inverted)
result = (0x200 - result) & 0x3ff;
// add in LFO AM modulation
- if (m_regs.lfo_am_enabled())
+ if (m_regs.op_lfo_am_enable(m_opoffs))
result += am_offset;
- // add in total level
- result += m_regs.total_level() << 3;
+ // add in total level and KSL from the cache
+ result += m_cache.total_level;
// clamp to max and return
return (result < 0x400) ? result : 0x3ff;
@@ -974,14 +2500,13 @@ u16 ymfm_operator<RegisterType>::envelope_attenuation(u8 am_offset) const
//-------------------------------------------------
template<class RegisterType>
-ymfm_channel<RegisterType>::ymfm_channel(RegisterType regs) :
+ymfm_channel<RegisterType>::ymfm_channel(ymfm_engine_base<RegisterType> &owner, u32 choffs) :
+ m_choffs(choffs),
m_feedback{ 0, 0 },
m_feedback_in(0),
- m_op1(regs.operator_registers(0)),
- m_op2(regs.operator_registers(1)),
- m_op3(regs.operator_registers(2)),
- m_op4(regs.operator_registers(3)),
- m_regs(regs)
+ m_op{ nullptr, nullptr, nullptr, nullptr },
+ m_regs(owner.regs()),
+ m_owner(owner)
{
}
@@ -991,17 +2516,11 @@ ymfm_channel<RegisterType>::ymfm_channel(RegisterType regs) :
//-------------------------------------------------
template<class RegisterType>
-void ymfm_channel<RegisterType>::save(device_t &device, u8 index)
+void ymfm_channel<RegisterType>::save(device_t &device, u32 index)
{
// save our data
device.save_item(YMFM_NAME(m_feedback), index);
device.save_item(YMFM_NAME(m_feedback_in), index);
-
- // save operator data
- m_op1.save(device, index * 4 + 0);
- m_op2.save(device, index * 4 + 1);
- m_op3.save(device, index * 4 + 2);
- m_op4.save(device, index * 4 + 3);
}
@@ -1015,12 +2534,6 @@ void ymfm_channel<RegisterType>::reset()
// reset our data
m_feedback[0] = m_feedback[1] = 0;
m_feedback_in = 0;
-
- // reset the operators
- m_op1.reset();
- m_op2.reset();
- m_op3.reset();
- m_op4.reset();
}
@@ -1029,26 +2542,30 @@ void ymfm_channel<RegisterType>::reset()
//-------------------------------------------------
template<class RegisterType>
-void ymfm_channel<RegisterType>::keyonoff(u8 states)
+void ymfm_channel<RegisterType>::keyonoff(u32 states, ymfm_keyon_type type)
{
- m_op1.keyonoff(BIT(states, 0));
- m_op2.keyonoff(BIT(states, 1));
- m_op3.keyonoff(BIT(states, 2));
- m_op4.keyonoff(BIT(states, 3));
+ for (int opnum = 0; opnum < std::size(m_op); opnum++)
+ if (m_op[opnum] != nullptr)
+ m_op[opnum]->keyonoff(BIT(states, opnum), type);
}
//-------------------------------------------------
-// keyon_csm - signal CSM key on to our operators
+// prepare - prepare for clocking
//-------------------------------------------------
template<class RegisterType>
-void ymfm_channel<RegisterType>::keyon_csm()
+bool ymfm_channel<RegisterType>::prepare()
{
- m_op1.keyon_csm();
- m_op2.keyon_csm();
- m_op3.keyon_csm();
- m_op4.keyon_csm();
+ u32 active_mask = 0;
+
+ // prepare all operators and determine if they are active
+ for (int opnum = 0; opnum < std::size(m_op); opnum++)
+ if (m_op[opnum] != nullptr)
+ if (m_op[opnum]->prepare())
+ active_mask |= 1 << opnum;
+
+ return (active_mask != 0);
}
@@ -1057,192 +2574,278 @@ void ymfm_channel<RegisterType>::keyon_csm()
//-------------------------------------------------
template<class RegisterType>
-void ymfm_channel<RegisterType>::clock(u32 env_counter, s8 lfo_raw_pm, bool is_multi_freq)
+void ymfm_channel<RegisterType>::clock(u32 env_counter, s32 lfo_raw_pm)
{
- // grab common block/fnum values
- u16 block_freq = m_regs.block_freq();
-
// clock the feedback through
m_feedback[0] = m_feedback[1];
m_feedback[1] = m_feedback_in;
- // in multi-frequency mode, the first 3 channels use independent block/fnum values
- if (is_multi_freq)
+ for (int opnum = 0; opnum < std::size(m_op); opnum++)
+ if (m_op[opnum] != nullptr)
+ m_op[opnum]->clock(env_counter, lfo_raw_pm);
+}
+
+
+//-------------------------------------------------
+// output_2op - combine 4 operators according to
+// the specified algorithm, returning a sum
+// according to the rshift and clipmax parameters,
+// which vary between different implementations
+//-------------------------------------------------
+
+template<class RegisterType>
+void ymfm_channel<RegisterType>::output_2op(s32 outputs[RegisterType::OUTPUTS], u32 rshift, s32 clipmax) const
+{
+ // The first 2 operators should be populated
+ assert(m_op[0] != nullptr);
+ assert(m_op[1] != nullptr);
+
+ // AM amount is the same across all operators; compute it once
+ u32 am_offset = m_regs.lfo_am_offset(m_choffs);
+
+ // operator 1 has optional self-feedback
+ s32 opmod = 0;
+ u32 feedback = m_regs.ch_feedback(m_choffs);
+ if (feedback != 0)
+ opmod = (m_feedback[0] + m_feedback[1]) >> (10 - feedback);
+
+ // compute the 14-bit volume/value of operator 1 and update the feedback
+ s32 op1value = m_feedback_in = m_op[0]->compute_volume(m_op[0]->phase() + opmod, am_offset);
+
+ // now that the feedback has been computed, skip the rest if all volumes
+ // are clear; no need to do all this work for nothing
+ if (m_regs.ch_output_any(m_choffs) == 0)
+ return;
+
+ // Algorithms for two-operator case:
+ // 0: O1 -> O2 -> out
+ // 1: (O1 + O2) -> out
+ s32 result;
+ if (BIT(m_regs.ch_algorithm(m_choffs), 0) == 0)
{
- m_op1.clock(env_counter, lfo_raw_pm, m_regs.multi_block_freq1());
- m_op2.clock(env_counter, lfo_raw_pm, m_regs.multi_block_freq2());
- m_op3.clock(env_counter, lfo_raw_pm, m_regs.multi_block_freq0());
+ // some OPL chips use the previous sample for modulation instead of
+ // the current sample
+ opmod = (RegisterType::MODULATOR_DELAY ? m_feedback[1] : op1value) >> 1;
+ result = m_op[1]->compute_volume(m_op[1]->phase() + opmod, am_offset) >> rshift;
}
-
- // otherwise, all channels use the common block/fnum
else
{
- m_op1.clock(env_counter, lfo_raw_pm, block_freq);
- m_op2.clock(env_counter, lfo_raw_pm, block_freq);
- m_op3.clock(env_counter, lfo_raw_pm, block_freq);
+ result = op1value + (m_op[1]->compute_volume(m_op[1]->phase(), am_offset) >> rshift);
+ s32 clipmin = -clipmax - 1;
+ result = std::clamp(result, clipmin, clipmax);
}
- // operator 3 uses the common values in all cases
- m_op4.clock(env_counter, lfo_raw_pm, block_freq);
+ // add to the output
+ add_to_output(m_choffs, outputs, result);
}
//-------------------------------------------------
-// output - combine the operators according to the
-// specified algorithm, returning a sum according
-// to the rshift and clipmax parameters, which
-// vary between different OPN implementations
+// output_4op - combine 4 operators according to
+// the specified algorithm, returning a sum
+// according to the rshift and clipmax parameters,
+// which vary between different implementations
//-------------------------------------------------
template<class RegisterType>
-void ymfm_channel<RegisterType>::output(u8 lfo_raw_am, u8 noise_state, s32 &lsum, s32 &rsum, u8 rshift, s16 clipmax) const
+void ymfm_channel<RegisterType>::output_4op(s32 outputs[RegisterType::OUTPUTS], u32 rshift, s32 clipmax) const
{
+ // all 4 operators should be populated
+ assert(m_op[0] != nullptr);
+ assert(m_op[1] != nullptr);
+ assert(m_op[2] != nullptr);
+ assert(m_op[3] != nullptr);
+
// AM amount is the same across all operators; compute it once
- u16 am_offset = lfo_am_offset(lfo_raw_am);
-
- // Algorithms:
- // 0: O1 -> O2 -> O3 -> O4 -> out
- // 1: (O1 + O2) -> O3 -> O4 -> out
- // 2: (O1 + (O2 -> O3)) -> O4 -> out
- // 3: ((O1 -> O2) + O3) -> O4 -> out
- // 4: ((O1 -> O2) + (O3 -> O4)) -> out
- // 5: ((O1 -> O2) + (O1 -> O3) + (O1 -> O4)) -> out
- // 6: ((O1 -> O2) + O3 + O4) -> out
- // 7: (O1 + O2 + O3 + O4) -> out
+ u32 am_offset = m_regs.lfo_am_offset(m_choffs);
+
+ // operator 1 has optional self-feedback
+ s32 opmod = 0;
+ u32 feedback = m_regs.ch_feedback(m_choffs);
+ if (feedback != 0)
+ opmod = (m_feedback[0] + m_feedback[1]) >> (10 - feedback);
+
+ // compute the 14-bit volume/value of operator 1 and update the feedback
+ s32 op1value = m_feedback_in = m_op[0]->compute_volume(m_op[0]->phase() + opmod, am_offset);
+
+ // now that the feedback has been computed, skip the rest if all volumes
+ // are clear; no need to do all this work for nothing
+ if (m_regs.ch_output_any(m_choffs) == 0)
+ return;
+
+ // OPM/OPN offer 8 different connection algorithms for 4 operators,
+ // and OPL3 offers 4 more, which we designate here as 8-11.
//
// The operators are computed in order, with the inputs pulled from
- // an array of values that is populated as we go:
+ // an array of values (opout) that is populated as we go:
// 0 = 0
// 1 = O1
// 2 = O2
// 3 = O3
- // 4 = O4
+ // 4 = (O4)
// 5 = O1+O2
// 6 = O1+O3
// 7 = O2+O3
//
- // This table encodes for operators 2-4 which of the 8 input values
- // above is used: 1 bit for O2 and 3 bits for O3 and O4
- static u8 const s_algorithm_inputs[8] =
+ // The s_algorithm_ops table describes the inputs and outputs of each
+ // algorithm as follows:
+ //
+ // ---------x use opout[x] as operator 2 input
+ // ------xxx- use opout[x] as operator 3 input
+ // ---xxx---- use opout[x] as operator 4 input
+ // --x------- include opout[1] in final sum
+ // -x-------- include opout[2] in final sum
+ // x--------- include opout[3] in final sum
+ #define ALGORITHM(op2in, op3in, op4in, op1out, op2out, op3out) \
+ (op2in | (op3in << 1) | (op4in << 4) | (op1out << 7) | (op2out << 8) | (op3out << 9))
+ static u16 const s_algorithm_ops[8+4] =
{
- // OP2 OP3 OP4
- 1 | (2 << 1) | (3 << 4),
- 0 | (5 << 1) | (3 << 4),
- 0 | (2 << 1) | (6 << 4),
- 1 | (0 << 1) | (7 << 4),
- 1 | (0 << 1) | (3 << 4),
- 1 | (1 << 1) | (1 << 4),
- 1 | (0 << 1) | (0 << 4),
- 0 | (0 << 1) | (0 << 4)
+ ALGORITHM(1,2,3, 0,0,0), // 0: O1 -> O2 -> O3 -> O4 -> out (O4)
+ ALGORITHM(0,5,3, 0,0,0), // 1: (O1 + O2) -> O3 -> O4 -> out (O4)
+ ALGORITHM(0,2,6, 0,0,0), // 2: (O1 + (O2 -> O3)) -> O4 -> out (O4)
+ ALGORITHM(1,0,7, 0,0,0), // 3: ((O1 -> O2) + O3) -> O4 -> out (O4)
+ ALGORITHM(1,0,3, 0,1,0), // 4: ((O1 -> O2) + (O3 -> O4)) -> out (O2+O4)
+ ALGORITHM(1,1,1, 0,1,1), // 5: ((O1 -> O2) + (O1 -> O3) + (O1 -> O4)) -> out (O2+O3+O4)
+ ALGORITHM(1,0,0, 0,1,1), // 6: ((O1 -> O2) + O3 + O4) -> out (O2+O3+O4)
+ ALGORITHM(0,0,0, 1,1,1), // 7: (O1 + O2 + O3 + O4) -> out (O1+O2+O3+O4)
+ ALGORITHM(1,2,3, 0,0,0), // 8: O1 -> O2 -> O3 -> O4 -> out (O4) [same as 0]
+ ALGORITHM(0,2,3, 1,0,0), // 9: (O1 + (O2 -> O3 -> O4)) -> out (O1+O4) [unique]
+ ALGORITHM(1,0,3, 0,1,0), // 10: ((O1 -> O2) + (O3 -> O4)) -> out (O2+O4) [same as 4]
+ ALGORITHM(0,2,0, 1,0,1) // 11: (O1 + (O2 -> O3) + O4) -> out (O1+O3+O4) [unique]
};
- u8 algorithm = m_regs.algorithm();
- u8 algorithm_inputs = s_algorithm_inputs[algorithm];
+ u32 algorithm_ops = s_algorithm_ops[m_regs.ch_algorithm(m_choffs)];
+
+ // populate the opout table
s16 opout[8];
opout[0] = 0;
-
- // operator 1 has optional self-feedback
- s16 modulation = 0;
- u8 feedback = m_regs.feedback();
- if (feedback != 0)
- modulation = (m_feedback[0] + m_feedback[1]) >> (10 - feedback);
-
- // compute the 14-bit volume/value of operator 1 and update the feedback
- opout[1] = m_feedback_in = m_op1.compute_volume(modulation, am_offset);
-
- // no that the feedback has been computed, skip the rest if both pans are clear;
- // no need to do all this work for nothing
- if (m_regs.pan_left() == 0 && m_regs.pan_right() == 0)
- return;
+ opout[1] = op1value;
// compute the 14-bit volume/value of operator 2
- opout[2] = m_op2.compute_volume(opout[BIT(algorithm_inputs, 0, 1)] >> 1, am_offset);
+ opmod = opout[BIT(algorithm_ops, 0, 1)] >> 1;
+ opout[2] = m_op[1]->compute_volume(m_op[1]->phase() + opmod, am_offset);
opout[5] = opout[1] + opout[2];
// compute the 14-bit volume/value of operator 3
- opout[3] = m_op3.compute_volume(opout[BIT(algorithm_inputs, 1, 3)] >> 1, am_offset);
+ opmod = opout[BIT(algorithm_ops, 1, 3)] >> 1;
+ opout[3] = m_op[2]->compute_volume(m_op[2]->phase() + opmod, am_offset);
opout[6] = opout[1] + opout[3];
opout[7] = opout[2] + opout[3];
// compute the 14-bit volume/value of operator 4; this could be a noise
- // value on the OPM
- if (noise_state != 0)
- opout[4] = m_op4.compute_noise_volume(noise_state, am_offset);
+ // value on the OPM; all algorithms consume OP4 output at a minimum
+ s32 result;
+ if (m_regs.noise_enable() && m_choffs == 7)
+ result = m_op[3]->compute_noise_volume(am_offset);
else
- opout[4] = m_op4.compute_volume(opout[BIT(algorithm_inputs, 4, 3)] >> 1, am_offset);
+ {
+ opmod = opout[BIT(algorithm_ops, 4, 3)] >> 1;
+ result = m_op[3]->compute_volume(m_op[3]->phase() + opmod, am_offset);
+ }
+ result >>= rshift;
- // all algorithms consume OP4 output
- s16 result = opout[4] >> rshift;
+ // optionally add OP1, OP2, OP3
+ s32 clipmin = -clipmax - 1;
+ if (BIT(algorithm_ops, 7) != 0)
+ result = std::clamp(result + (opout[1] >> rshift), clipmin, clipmax);
+ if (BIT(algorithm_ops, 8) != 0)
+ result = std::clamp(result + (opout[2] >> rshift), clipmin, clipmax);
+ if (BIT(algorithm_ops, 9) != 0)
+ result = std::clamp(result + (opout[3] >> rshift), clipmin, clipmax);
- // algorithms 4-7 add in OP2 output
- if (algorithm >= 4)
- {
- s16 clipmin = -clipmax - 1;
- result += opout[2] >> rshift;
- result = std::clamp(result, clipmin, clipmax);
+ // add to the output
+ add_to_output(m_choffs, outputs, result);
+}
- // agorithms 5-7 add in OP3 output
- if (algorithm >= 5)
- {
- result += opout[3] >> rshift;
- result = std::clamp(result, clipmin, clipmax);
- // algorithm 7 adds in OP1 output
- if (algorithm == 7)
- {
- result += opout[1] >> rshift;
- result = std::clamp(result, clipmin, clipmax);
- }
- }
- }
+//-------------------------------------------------
+// output_rhythm_ch6 - special case output
+// computation for OPL channel 6 in rhythm mode,
+// which outputs a Bass Drum instrument
+//-------------------------------------------------
+
+template<class RegisterType>
+void ymfm_channel<RegisterType>::output_rhythm_ch6(s32 outputs[RegisterType::OUTPUTS], u32 rshift, s32 clipmax) const
+{
+ // AM amount is the same across all operators; compute it once
+ u32 am_offset = m_regs.lfo_am_offset(m_choffs);
+
+ // Bass Drum: this uses operators 12 and 15 (i.e., channel 6)
+ // in an almost-normal way, except that if the algorithm is 1,
+ // the first operator is ignored instead of added in
+
+ // operator 1 has optional self-feedback
+ s32 opmod = 0;
+ u32 feedback = m_regs.ch_feedback(m_choffs);
+ if (feedback != 0)
+ opmod = (m_feedback[0] + m_feedback[1]) >> (10 - feedback);
+
+ // compute the 14-bit volume/value of operator 1 and update the feedback
+ s32 opout1 = m_feedback_in = m_op[0]->compute_volume(m_op[0]->phase() + opmod, am_offset);
+
+ // compute the 14-bit volume/value of operator 2, which is the result
+ opmod = BIT(m_regs.ch_algorithm(m_choffs), 0) ? 0 : (opout1 >> 1);
+ s32 result = m_op[1]->compute_volume(m_op[1]->phase() + opmod, am_offset) >> rshift;
// add to the output
- if (m_regs.pan_left())
- lsum += result;
- if (m_regs.pan_right())
- rsum += result;
+ add_to_output(m_choffs, outputs, result * 2);
}
//-------------------------------------------------
-// lfo_am_offset - convert a 6/8-bit raw AM value
-// into an amplitude offset based on sensitivity
+// output_rhythm_ch7 - special case output
+// computation for OPL channel 7 in rhythm mode,
+// which outputs High Hat and Snare Drum
+// instruments
//-------------------------------------------------
-// OPM version
-template<>
-u16 ymfm_channel<ymopm_registers>::lfo_am_offset(u8 lfo_raw_am) const
+template<class RegisterType>
+void ymfm_channel<RegisterType>::output_rhythm_ch7(u32 phase_select, s32 outputs[RegisterType::OUTPUTS], u32 rshift, s32 clipmax) const
{
- // shift value for AM sensitivity is [*, 0, 1, 2],
- // mapping to values of [0, 23.9, 47.8, and 95.6dB]
- u8 am_sensitivity = m_regs.lfo_am_sensitivity();
- if (am_sensitivity == 0)
- return 0;
-
- // QUESTION: see OPN note below for the dB range mapping; it applies
- // here as well
+ // AM amount is the same across all operators; compute it once
+ u32 am_offset = m_regs.lfo_am_offset(m_choffs);
+ u32 noise_state = BIT(m_regs.noise_state(), 0);
+
+ // High Hat: this uses the envelope from operator 13 (channel 7),
+ // and a combination of noise and the operator 13/17 phase select
+ // to compute the phase
+ u32 phase = (phase_select << 9) | (0xd0 >> (2 * (noise_state ^ phase_select)));
+ s32 result = m_op[0]->compute_volume(phase, am_offset) >> rshift;
+
+ // Snare Drum: this uses the envelope from operator 16 (channel 7),
+ // and a combination of noise and operator 13 phase to pick a phase
+ u32 op13phase = m_op[0]->phase();
+ phase = (0x100 << BIT(op13phase, 8)) ^ (noise_state << 8);
+ result += m_op[1]->compute_volume(phase, am_offset) >> rshift;
+ result = std::clamp<s32>(result, -clipmax - 1, clipmax);
- // raw LFO AM value on OPM is 0-FF, which is already a factor of 2
- // larger than the OPN below, putting our staring point at 2x theirs;
- // this works out since our minimum is 2x their maximum
- return lfo_raw_am << (am_sensitivity - 1);
+ // add to the output
+ add_to_output(m_choffs, outputs, result * 2);
}
-// OPN/OPNA version
+
+//-------------------------------------------------
+// output_rhythm_ch8 - special case output
+// computation for OPL channel 8 in rhythm mode,
+// which outputs Tom Tom and Top Cymbal instruments
+//-------------------------------------------------
+
template<class RegisterType>
-u16 ymfm_channel<RegisterType>::lfo_am_offset(u8 lfo_raw_am) const
+void ymfm_channel<RegisterType>::output_rhythm_ch8(u32 phase_select, s32 outputs[RegisterType::OUTPUTS], u32 rshift, s32 clipmax) const
{
- // shift value for AM sensitivity is [7, 3, 1, 0],
- // mapping to values of [0, 1.4, 5.9, and 11.8dB]
- u8 am_shift = (1 << (m_regs.lfo_am_sensitivity() ^ 3)) - 1;
+ // AM amount is the same across all operators; compute it once
+ u32 am_offset = m_regs.lfo_am_offset(m_choffs);
- // QUESTION: max sensitivity should give 11.8dB range, but this value
- // is directly added to an x.8 attenuation value, which will only give
- // 126/256 or ~4.9dB range -- what am I missing? The calculation below
- // matches several other emulators, including the Nuked implemenation.
+ // Tom Tom: this is just a single operator processed normally
+ s32 result = m_op[0]->compute_volume(m_op[0]->phase(), am_offset) >> rshift;
- // raw LFO AM value on OPN is 0-3F, scale that up by a factor of 2
- // (giving 7 bits) before applying the final shift
- return (lfo_raw_am << 1) >> am_shift;
+ // Top Cymbal: this uses the envelope from operator 17 (channel 8),
+ // and the operator 13/17 phase select to compute the phase
+ u32 phase = 0x100 | (phase_select << 9);
+ result += m_op[1]->compute_volume(phase, am_offset) >> rshift;
+ result = std::clamp<s32>(result, -clipmax - 1, clipmax);
+
+ // add to the output
+ add_to_output(m_choffs, outputs, result * 2);
}
@@ -1259,28 +2862,27 @@ template<class RegisterType>
ymfm_engine_base<RegisterType>::ymfm_engine_base(device_t &device) :
m_device(device),
m_env_counter(0),
- m_lfo_counter(0),
- m_noise_lfsr(0),
- m_noise_counter(0),
- m_noise_state(0),
- m_noise_lfo(0),
- m_lfo_am(0),
m_status(0),
m_clock_prescale(RegisterType::DEFAULT_PRESCALE),
m_irq_mask(STATUS_TIMERA | STATUS_TIMERB),
m_irq_state(0),
- m_active_channels(0xffffffff),
- m_modified_channels(0xffffffff),
+ m_active_channels(ALL_CHANNELS),
+ m_modified_channels(ALL_CHANNELS),
m_prepare_count(0),
m_busy_end(attotime::zero),
m_timer{ nullptr, nullptr },
- m_irq_handler(device),
- m_regdata(RegisterType::REGISTERS),
- m_regs(m_regdata)
+ m_irq_handler(device)
{
// create the channels
- for (int chnum = 0; chnum < RegisterType::CHANNELS; chnum++)
- m_channel[chnum] = std::make_unique<ymfm_channel<RegisterType>>(m_regs.channel_registers(chnum));
+ for (int chnum = 0; chnum < CHANNELS; chnum++)
+ m_channel[chnum] = std::make_unique<ymfm_channel<RegisterType>>(*this, RegisterType::channel_offset(chnum));
+
+ // create the operators
+ for (int opnum = 0; opnum < OPERATORS; opnum++)
+ m_operator[opnum] = std::make_unique<ymfm_operator<RegisterType>>(*this, RegisterType::operator_offset(opnum));
+
+ // do the initial operator assignment
+ assign_operators();
}
@@ -1300,22 +2902,22 @@ void ymfm_engine_base<RegisterType>::save(device_t &device)
// save our data
device.save_item(YMFM_NAME(m_env_counter));
- device.save_item(YMFM_NAME(m_lfo_counter));
- device.save_item(YMFM_NAME(m_noise_lfsr));
- device.save_item(YMFM_NAME(m_noise_counter));
- device.save_item(YMFM_NAME(m_noise_state));
- device.save_item(YMFM_NAME(m_noise_lfo));
- device.save_item(YMFM_NAME(m_lfo_am));
device.save_item(YMFM_NAME(m_status));
device.save_item(YMFM_NAME(m_clock_prescale));
device.save_item(YMFM_NAME(m_irq_mask));
device.save_item(YMFM_NAME(m_irq_state));
device.save_item(YMFM_NAME(m_busy_end));
- device.save_item(YMFM_NAME(m_regdata));
+
+ // save the register/family data
+ m_regs.save(device);
// save channel data
- for (int chnum = 0; chnum < RegisterType::CHANNELS; chnum++)
+ for (int chnum = 0; chnum < CHANNELS; chnum++)
m_channel[chnum]->save(device, chnum);
+
+ // save operator data
+ for (int opnum = 0; opnum < OPERATORS; opnum++)
+ m_operator[opnum]->save(device, opnum);
}
@@ -1329,19 +2931,20 @@ void ymfm_engine_base<RegisterType>::reset()
// reset all status bits
set_reset_status(0, 0xff);
- // clear all registers
- std::fill_n(&m_regdata[0], m_regdata.size(), 0);
+ // register type-specific initialization
+ m_regs.reset();
// explicitly write to the mode register since it has side-effects
// QUESTION: old cores initialize this to 0x30 -- who is right?
write(RegisterType::REG_MODE, 0);
- // register type-specific initialization
- m_regs.reset();
-
// reset the channels
for (auto &chan : m_channel)
chan->reset();
+
+ // reset the operators
+ for (auto &op : m_operator)
+ op->reset();
}
@@ -1351,39 +2954,41 @@ void ymfm_engine_base<RegisterType>::reset()
//-------------------------------------------------
template<class RegisterType>
-u32 ymfm_engine_base<RegisterType>::clock(u8 chanmask)
+u32 ymfm_engine_base<RegisterType>::clock(u32 chanmask)
{
// if something was modified, prepare
// also prepare every 4k samples to catch ending notes
if (m_modified_channels != 0 || m_prepare_count++ >= 4096)
{
+ // reassign operators to channels if dynamic
+ if (RegisterType::DYNAMIC_OPS)
+ assign_operators();
+
// call each channel to prepare
m_active_channels = 0;
- for (int chnum = 0; chnum < RegisterType::CHANNELS; chnum++)
+ for (int chnum = 0; chnum < CHANNELS; chnum++)
if (BIT(chanmask, chnum))
- if (m_channel[chnum]->active())
+ if (m_channel[chnum]->prepare())
m_active_channels |= 1 << chnum;
// reset the modified channels and prepare count
m_modified_channels = m_prepare_count = 0;
}
- // increment the envelope count; low two bits are the subcount, which
- // only counts to 3, so if it reaches 3, count one more time
- m_env_counter++;
- if (BIT(m_env_counter, 0, 2) == 3)
- m_env_counter++;
+ // if the envelope clock divider is 1, just increment by 4;
+ // otherwise, increment by 1 and manually wrap when we reach the divide count
+ if (RegisterType::EG_CLOCK_DIVIDER == 1)
+ m_env_counter += 4;
+ else if (BIT(++m_env_counter, 0, 2) == RegisterType::EG_CLOCK_DIVIDER)
+ m_env_counter += 4 - RegisterType::EG_CLOCK_DIVIDER;
// clock the noise generator
- clock_noise();
-
- // clock the LFO
- s8 lfo_raw_pm = clock_lfo();
+ s32 lfo_raw_pm = m_regs.clock_noise_and_lfo();
// now update the state of all the channels and operators
- for (int chnum = 0; chnum < RegisterType::CHANNELS; chnum++)
+ for (int chnum = 0; chnum < CHANNELS; chnum++)
if (BIT(chanmask, chnum))
- m_channel[chnum]->clock(m_env_counter, lfo_raw_pm, chnum == 2 && m_regs.multi_freq());
+ m_channel[chnum]->clock(m_env_counter, lfo_raw_pm);
// return the envelope counter as it is used to clock ADPCM-A
return m_env_counter;
@@ -1396,20 +3001,51 @@ u32 ymfm_engine_base<RegisterType>::clock(u8 chanmask)
//-------------------------------------------------
template<class RegisterType>
-void ymfm_engine_base<RegisterType>::output(s32 &lsum, s32 &rsum, u8 rshift, s16 clipmax, u8 chanmask) const
+void ymfm_engine_base<RegisterType>::output(s32 outputs[RegisterType::OUTPUTS], u32 rshift, s32 clipmax, u32 chanmask) const
{
// mask out inactive channels
chanmask &= m_active_channels;
- // sum over all the desired channels
- for (int chnum = 0; chnum < RegisterType::CHANNELS; chnum++)
- if (BIT(chanmask, chnum))
- {
- // noise must be non-zero to use noise on OP4, so if it is enabled,
- // OR with 2 (since only the LSB is actually checked for the noise state)
- u8 noise = (chnum == 7 && m_regs.noise_enabled()) ? (m_noise_state | 2) : 0;
- m_channel[chnum]->output(m_lfo_am, noise, lsum, rsum, rshift, clipmax);
- }
+ // handle the rhythm case, where some of the operators are dedicated
+ // to percussion (this is an OPL-specific feature)
+ if (m_regs.rhythm_enable())
+ {
+ // we don't support the OPM noise channel here; ensure it is off
+ assert(m_regs.noise_enable() == 0);
+
+ // precompute the operator 13+17 phase selection value
+ u32 op13phase = m_operator[13]->phase();
+ u32 op17phase = m_operator[17]->phase();
+ u32 phase_select = (BIT(op13phase, 2) ^ BIT(op13phase, 7)) | BIT(op13phase, 3) | (BIT(op17phase, 5) ^ BIT(op17phase, 3));
+
+ // sum over all the desired channels
+ for (int chnum = 0; chnum < CHANNELS; chnum++)
+ if (BIT(chanmask, chnum))
+ {
+ if (chnum == 6)
+ m_channel[chnum]->output_rhythm_ch6(outputs, rshift, clipmax);
+ else if (chnum == 7)
+ m_channel[chnum]->output_rhythm_ch7(phase_select, outputs, rshift, clipmax);
+ else if (chnum == 8)
+ m_channel[chnum]->output_rhythm_ch8(phase_select, outputs, rshift, clipmax);
+ else if (m_channel[chnum]->is4op())
+ m_channel[chnum]->output_4op(outputs, rshift, clipmax);
+ else
+ m_channel[chnum]->output_2op(outputs, rshift, clipmax);
+ }
+ }
+ else
+ {
+ // sum over all the desired channels
+ for (int chnum = 0; chnum < CHANNELS; chnum++)
+ if (BIT(chanmask, chnum))
+ {
+ if (m_channel[chnum]->is4op())
+ m_channel[chnum]->output_4op(outputs, rshift, clipmax);
+ else
+ m_channel[chnum]->output_2op(outputs, rshift, clipmax);
+ }
+ }
}
@@ -1428,18 +3064,27 @@ void ymfm_engine_base<RegisterType>::write(u16 regnum, u8 data)
return;
}
- // most writes are passive, consumed only when needed
- m_regs.write(regnum, data);
-
// for now just mark all channels as modified
- m_modified_channels = 0xffffffff;
+ m_modified_channels = ALL_CHANNELS;
- // handle writes to the keyon registers
- if (regnum == RegisterType::REG_KEYON)
+ // most writes are passive, consumed only when needed
+ u32 keyon_channel;
+ u32 keyon_opmask;
+ if (m_regs.write(regnum, data, keyon_channel, keyon_opmask))
{
- u8 chnum = m_regs.keyon_channel();
- if (chnum < RegisterType::CHANNELS)
- m_channel[chnum]->keyonoff(m_regs.keyon_states());
+ // handle writes to the keyon register(s)
+ if (keyon_channel < CHANNELS)
+ {
+ // normal channel on/off
+ m_channel[keyon_channel]->keyonoff(keyon_opmask, YMFM_KEYON_NORMAL);
+ }
+ else if (CHANNELS >= 9 && keyon_channel == RegisterType::YMFM_RHYTHM_CHANNEL)
+ {
+ // special case for the OPL rhythm channels
+ m_channel[6]->keyonoff(BIT(keyon_opmask, 4) ? 3 : 0, YMFM_KEYON_RHYTHM);
+ m_channel[7]->keyonoff(BIT(keyon_opmask, 0) | (BIT(keyon_opmask, 3) << 1), YMFM_KEYON_RHYTHM);
+ m_channel[8]->keyonoff(BIT(keyon_opmask, 2) | (BIT(keyon_opmask, 1) << 1), YMFM_KEYON_RHYTHM);
+ }
}
}
@@ -1453,8 +3098,6 @@ template<class RegisterType>
u8 ymfm_engine_base<RegisterType>::status() const
{
u8 result = m_status & ~STATUS_BUSY;
-
- // synthesize the busy flag if we're still busy
if (m_device.machine().time() < m_busy_end)
result |= STATUS_BUSY;
return result;
@@ -1462,142 +3105,22 @@ u8 ymfm_engine_base<RegisterType>::status() const
//-------------------------------------------------
-// clock_lfo - clock the LFO, handling clock
-// division, depth, and waveform computations
+// assign_operators - get the current mapping of
+// operators to channels and assign them all
//-------------------------------------------------
-// OPM implementation
-template<>
-s8 ymfm_engine_base<ymopm_registers>::clock_lfo()
-{
- // treat the rate as a 4.4 floating-point step value with implied
- // leading 1; this matches exactly the frequencies in the application
- // manual, though it might not be implemented exactly this way on chip
- u8 rate = m_regs.lfo_rate();
- u32 prev_counter = m_lfo_counter;
- m_lfo_counter += (0x10 | BIT(rate, 0, 4)) << BIT(rate, 4, 4);
- u8 lfo = BIT(m_lfo_counter, 22, 8);
-
- // compute the AM and PM values based on the waveform
- // AM is 8-bit unsigned; PM is 8-bit signed; waveforms are adjusted
- // to match the pictures in the application manual
- u8 am;
- s8 pm;
- switch (m_regs.lfo_waveform())
- {
- // sawtooth
- default:
- case 0:
- am = lfo ^ 0xff;
- pm = lfo;
- break;
-
- // square wave
- case 1:
- am = BIT(lfo, 7) ? 0 : 0xff;
- pm = am ^ 0x80;
- break;
-
- // triangle wave
- case 2:
- am = BIT(lfo, 7) ? (lfo << 1) : (~lfo << 1);
- pm = BIT(lfo, 6) ? am : ~am;
- break;
-
- // noise:
- case 3:
- // QUESTION: this behavior is surmised but not yet verified:
- // LFO noise value is accumulated over 8 bits of LFSR and
- // clocked as the LFO value transitions
- if (BIT(m_lfo_counter ^ prev_counter, 22, 8) != 0)
- m_noise_lfo = m_noise_lfsr & 0xff;
- am = m_noise_lfo;
- pm = am ^ 0x80;
- break;
- }
-
- // apply depth to the AM value and store for later
- m_lfo_am = (am * m_regs.lfo_am_depth()) >> 7;
-
- // apply depth to the PM value and return it
- return (pm * m_regs.lfo_pm_depth()) >> 7;
-}
-
-// OPN/OPNA implementation
template<class RegisterType>
-s8 ymfm_engine_base<RegisterType>::clock_lfo()
-{
- // if not enabled, quick exit with 0s
- if (!m_regs.lfo_enabled())
- {
- m_lfo_counter = 0;
- m_lfo_am = 0;
- return 0;
- }
-
- // this table is based on converting the frequencies in the applications
- // manual to clock dividers, based on the assumption of a 7-bit LFO value
- static u8 const lfo_max_count[8] = { 109, 78, 72, 68, 63, 45, 9, 6 };
- u8 subcount = u8(m_lfo_counter++);
-
- // when we cross the divider count, add enough to zero it and cause an
- // increment at bit 8; the 7-bit value lives from bits 8-14
- if (subcount >= lfo_max_count[m_regs.lfo_rate()])
- m_lfo_counter += subcount ^ 0xff;
-
- // AM value is 7 bits, staring at bit 8; grab the low 6 directly
- m_lfo_am = BIT(m_lfo_counter, 8, 6);
-
- // first half of the AM period (bit 6 == 0) is inverted
- if (BIT(m_lfo_counter, 8+6) == 0)
- m_lfo_am ^= 0x3f;
-
- // PM value is 5 bits, starting at bit 10; grab the low 3 directly
- s8 pm = BIT(m_lfo_counter, 10, 3);
-
- // PM is reflected based on bit 3
- if (BIT(m_lfo_counter, 10+3))
- pm ^= 7;
-
- // PM is negated based on bit 4
- return BIT(m_lfo_counter, 10+4) ? -pm : pm;
-}
-
-
-//-------------------------------------------------
-// clock_noise - clock the noise generator
-//-------------------------------------------------
-
-// OPM implementation
-template<>
-void ymfm_engine_base<ymopm_registers>::clock_noise()
+void ymfm_engine_base<RegisterType>::assign_operators()
{
- // base noise frequency is measured at 2x 1/2 FM frequency; this means
- // each tick counts as two steps against the noise counter
- u8 freq = m_regs.noise_frequency();
- for (int rep = 0; rep < 2; rep++)
- {
- // evidence seems to suggest the LFSR is clocked continually and just
- // sampled at the noise frequency for output purposes; clock it here
- // twice; note that the low 8 bits are the most recent 8 bits of history
- // while bits 8-24 contain the 17 bit LFSR state
- m_noise_lfsr >>= 1;
- m_noise_lfsr |= (BIT(m_noise_lfsr, 7) ^ BIT(m_noise_lfsr, 10) ^ 1) << 24;
+ typename RegisterType::operator_mapping map;
+ m_regs.operator_map(map);
- // compare against the frequency and latch when we exceed it
- if (m_noise_counter++ >= freq)
+ for (int chnum = 0; chnum < CHANNELS; chnum++)
+ for (int index = 0; index < 4; index++)
{
- m_noise_counter = 0;
- m_noise_state = BIT(m_noise_lfsr, 7);
+ u32 opnum = BIT(map.chan[chnum], 8 * index, 8);
+ m_channel[chnum]->assign(index, (opnum == 0xff) ? nullptr : m_operator[opnum].get());
}
- }
-}
-
-// OPN/OPNA implementation
-template<class RegisterType>
-void ymfm_engine_base<RegisterType>::clock_noise()
-{
- // OPN does not have a noise generator
}
@@ -1607,13 +3130,13 @@ void ymfm_engine_base<RegisterType>::clock_noise()
//-------------------------------------------------
template<class RegisterType>
-void ymfm_engine_base<RegisterType>::update_timer(u8 tnum, u8 enable)
+void ymfm_engine_base<RegisterType>::update_timer(u32 tnum, u32 enable)
{
// if the timer is live, but not currently enabled, set the timer
- if (enable && !m_timer[tnum]->enabled())
+ if (enable && !m_timer[tnum]->enable())
{
- // each timer clock is n channels * 4 operators * prescale factor (2/3/6)
- u32 clockscale = RegisterType::CHANNELS * 4 * m_clock_prescale;
+ // each timer clock is n operators * prescale factor (2/3/6)
+ u32 clockscale = OPERATORS * m_clock_prescale;
// period comes from the registers, and is different for each
u32 period = (tnum == 0) ? (1024 - m_regs.timer_a_value()) : 16 * (256 - m_regs.timer_b_value());
@@ -1644,9 +3167,9 @@ TIMER_CALLBACK_MEMBER(ymfm_engine_base<RegisterType>::timer_handler)
// if timer A fired in CSM mode, trigger CSM on all relevant channels
if (param == 0 && m_regs.csm())
- for (int chnum = 0; chnum < RegisterType::CHANNELS; chnum++)
+ for (int chnum = 0; chnum < CHANNELS; chnum++)
if (BIT(RegisterType::CSM_TRIGGER_MASK, chnum))
- m_channel[chnum]->keyon_csm();
+ m_channel[chnum]->keyonoff(1, YMFM_KEYON_CSM);
// reset
update_timer(param, 1);
@@ -1683,6 +3206,12 @@ TIMER_CALLBACK_MEMBER(ymfm_engine_base<RegisterType>::check_interrupts)
u8 old_state = m_irq_state;
m_irq_state = ((m_status & m_irq_mask) != 0);
+ // set the IRQ status bit
+ if (m_irq_state)
+ m_status |= STATUS_IRQ;
+ else
+ m_status &= ~STATUS_IRQ;
+
// if changed, signal the new state
if (old_state != m_irq_state && !m_irq_handler.isnull())
m_irq_handler(m_irq_state ? ASSERT_LINE : CLEAR_LINE);
@@ -1698,21 +3227,41 @@ template<class RegisterType>
TIMER_CALLBACK_MEMBER(ymfm_engine_base<RegisterType>::synced_mode_w)
{
// actually write the mode register now
- m_regs.write(RegisterType::REG_MODE, param);
+ u32 dummy1, dummy2;
+ m_regs.write(RegisterType::REG_MODE, param, dummy1, dummy2);
+
+ // reset IRQ status -- when written, all other bits are ignored
+ // QUESTION: should this maybe just reset the IRQ bit and not all the bits?
+ // That is, check_interrupts would only set, this would only clear?
+ if (m_regs.irq_reset())
+ set_reset_status(0, 0x78);
+ else
+ {
+ // reset timer status
+ u8 reset_mask = 0;
+ if (m_regs.reset_timer_b())
+ reset_mask |= RegisterType::STATUS_TIMERB;
+ if (m_regs.reset_timer_a())
+ reset_mask |= RegisterType::STATUS_TIMERA;
+ set_reset_status(0, reset_mask);
+
+ // load timers
+ update_timer(1, m_regs.load_timer_b());
+ update_timer(0, m_regs.load_timer_a());
+ }
+}
- // reset timer status
- if (m_regs.reset_timer_b())
- set_reset_status(0, STATUS_TIMERB);
- if (m_regs.reset_timer_a())
- set_reset_status(0, STATUS_TIMERA);
- // load timers
- update_timer(1, m_regs.load_timer_b());
- update_timer(0, m_regs.load_timer_a());
-}
+//*********************************************************
+// EXPLICIT TEMPLATE INSTANTIATION
+//*********************************************************
-// Explicit template instantiation
template class ymfm_engine_base<ymopm_registers>;
template class ymfm_engine_base<ymopn_registers>;
template class ymfm_engine_base<ymopna_registers>;
+template class ymfm_engine_base<ymopl_registers>;
+template class ymfm_engine_base<ymopl2_registers>;
+template class ymfm_engine_base<ymopll_registers>;
+template class ymfm_engine_base<ymopl3_registers>;
+template class ymfm_engine_base<ymopl4_registers>;