diff options
Diffstat (limited to 'src/devices/sound/ymfm.cpp')
-rw-r--r-- | src/devices/sound/ymfm.cpp | 2931 |
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>; |