1591 lines
58 KiB
Text
1591 lines
58 KiB
Text
|
// BSD 3-Clause License
|
||
|
//
|
||
|
// Copyright (c) 2021, Aaron Giles
|
||
|
// All rights reserved.
|
||
|
//
|
||
|
// Redistribution and use in source and binary forms, with or without
|
||
|
// modification, are permitted provided that the following conditions are met:
|
||
|
//
|
||
|
// 1. Redistributions of source code must retain the above copyright notice, this
|
||
|
// list of conditions and the following disclaimer.
|
||
|
//
|
||
|
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||
|
// this list of conditions and the following disclaimer in the documentation
|
||
|
// and/or other materials provided with the distribution.
|
||
|
//
|
||
|
// 3. Neither the name of the copyright holder nor the names of its
|
||
|
// contributors may be used to endorse or promote products derived from
|
||
|
// this software without specific prior written permission.
|
||
|
//
|
||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||
|
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||
|
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
|
||
|
namespace ymfm
|
||
|
{
|
||
|
|
||
|
//*********************************************************
|
||
|
// GLOBAL TABLE LOOKUPS
|
||
|
//*********************************************************
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// abs_sin_attenuation - given a sin (phase) input
|
||
|
// where the range 0-2*PI is mapped onto 10 bits,
|
||
|
// return the absolute value of sin(input),
|
||
|
// logarithmically-adjusted and treated as an
|
||
|
// attenuation value, in 4.8 fixed point format
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
inline uint32_t abs_sin_attenuation(uint32_t 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
|
||
|
static uint16_t const s_sin_table[256] =
|
||
|
{
|
||
|
0x859,0x6c3,0x607,0x58b,0x52e,0x4e4,0x4a6,0x471,0x443,0x41a,0x3f5,0x3d3,0x3b5,0x398,0x37e,0x365,
|
||
|
0x34e,0x339,0x324,0x311,0x2ff,0x2ed,0x2dc,0x2cd,0x2bd,0x2af,0x2a0,0x293,0x286,0x279,0x26d,0x261,
|
||
|
0x256,0x24b,0x240,0x236,0x22c,0x222,0x218,0x20f,0x206,0x1fd,0x1f5,0x1ec,0x1e4,0x1dc,0x1d4,0x1cd,
|
||
|
0x1c5,0x1be,0x1b7,0x1b0,0x1a9,0x1a2,0x19b,0x195,0x18f,0x188,0x182,0x17c,0x177,0x171,0x16b,0x166,
|
||
|
0x160,0x15b,0x155,0x150,0x14b,0x146,0x141,0x13c,0x137,0x133,0x12e,0x129,0x125,0x121,0x11c,0x118,
|
||
|
0x114,0x10f,0x10b,0x107,0x103,0x0ff,0x0fb,0x0f8,0x0f4,0x0f0,0x0ec,0x0e9,0x0e5,0x0e2,0x0de,0x0db,
|
||
|
0x0d7,0x0d4,0x0d1,0x0cd,0x0ca,0x0c7,0x0c4,0x0c1,0x0be,0x0bb,0x0b8,0x0b5,0x0b2,0x0af,0x0ac,0x0a9,
|
||
|
0x0a7,0x0a4,0x0a1,0x09f,0x09c,0x099,0x097,0x094,0x092,0x08f,0x08d,0x08a,0x088,0x086,0x083,0x081,
|
||
|
0x07f,0x07d,0x07a,0x078,0x076,0x074,0x072,0x070,0x06e,0x06c,0x06a,0x068,0x066,0x064,0x062,0x060,
|
||
|
0x05e,0x05c,0x05b,0x059,0x057,0x055,0x053,0x052,0x050,0x04e,0x04d,0x04b,0x04a,0x048,0x046,0x045,
|
||
|
0x043,0x042,0x040,0x03f,0x03e,0x03c,0x03b,0x039,0x038,0x037,0x035,0x034,0x033,0x031,0x030,0x02f,
|
||
|
0x02e,0x02d,0x02b,0x02a,0x029,0x028,0x027,0x026,0x025,0x024,0x023,0x022,0x021,0x020,0x01f,0x01e,
|
||
|
0x01d,0x01c,0x01b,0x01a,0x019,0x018,0x017,0x017,0x016,0x015,0x014,0x014,0x013,0x012,0x011,0x011,
|
||
|
0x010,0x00f,0x00f,0x00e,0x00d,0x00d,0x00c,0x00c,0x00b,0x00a,0x00a,0x009,0x009,0x008,0x008,0x007,
|
||
|
0x007,0x007,0x006,0x006,0x005,0x005,0x005,0x004,0x004,0x004,0x003,0x003,0x003,0x002,0x002,0x002,
|
||
|
0x002,0x001,0x001,0x001,0x001,0x001,0x001,0x001,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000
|
||
|
};
|
||
|
|
||
|
// if the top bit is set, we're in the second half of the curve
|
||
|
// which is a mirror image, so invert the index
|
||
|
if (bitfield(input, 8))
|
||
|
input = ~input;
|
||
|
|
||
|
// return the value from the table
|
||
|
return s_sin_table[input & 0xff];
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// attenuation_to_volume - given a 5.8 fixed point
|
||
|
// logarithmic attenuation value, return a 13-bit
|
||
|
// linear volume
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
inline uint32_t attenuation_to_volume(uint32_t 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 uint16_t const s_power_table[256] =
|
||
|
{
|
||
|
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] >> (input >> 8);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// attenuation_increment - given a 6-bit ADSR
|
||
|
// rate value and a 3-bit stepping index,
|
||
|
// return a 4-bit increment to the attenutaion
|
||
|
// for this step (or for the attack case, the
|
||
|
// fractional scale factor to decrease by)
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
inline uint32_t attenuation_increment(uint32_t rate, uint32_t index)
|
||
|
{
|
||
|
static uint32_t const s_increment_table[64] =
|
||
|
{
|
||
|
0x00000000, 0x00000000, 0x10101010, 0x10101010, // 0-3 (0x00-0x03)
|
||
|
0x10101010, 0x10101010, 0x11101110, 0x11101110, // 4-7 (0x04-0x07)
|
||
|
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 8-11 (0x08-0x0B)
|
||
|
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 12-15 (0x0C-0x0F)
|
||
|
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 16-19 (0x10-0x13)
|
||
|
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 20-23 (0x14-0x17)
|
||
|
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 24-27 (0x18-0x1B)
|
||
|
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 28-31 (0x1C-0x1F)
|
||
|
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 32-35 (0x20-0x23)
|
||
|
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 36-39 (0x24-0x27)
|
||
|
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 40-43 (0x28-0x2B)
|
||
|
0x10101010, 0x10111010, 0x11101110, 0x11111110, // 44-47 (0x2C-0x2F)
|
||
|
0x11111111, 0x21112111, 0x21212121, 0x22212221, // 48-51 (0x30-0x33)
|
||
|
0x22222222, 0x42224222, 0x42424242, 0x44424442, // 52-55 (0x34-0x37)
|
||
|
0x44444444, 0x84448444, 0x84848484, 0x88848884, // 56-59 (0x38-0x3B)
|
||
|
0x88888888, 0x88888888, 0x88888888, 0x88888888 // 60-63 (0x3C-0x3F)
|
||
|
};
|
||
|
return bitfield(s_increment_table[rate], 4*index, 4);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// detune_adjustment - given a 5-bit key code
|
||
|
// value and a 3-bit detune parameter, return a
|
||
|
// 6-bit signed phase displacement; this table
|
||
|
// has been verified against Nuked's equations,
|
||
|
// but the equations are rather complicated, so
|
||
|
// we'll keep the simplicity of the table
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
inline int32_t detune_adjustment(uint32_t detune, uint32_t keycode)
|
||
|
{
|
||
|
static uint8_t const s_detune_adjustment[32][4] =
|
||
|
{
|
||
|
{ 0, 0, 1, 2 }, { 0, 0, 1, 2 }, { 0, 0, 1, 2 }, { 0, 0, 1, 2 },
|
||
|
{ 0, 1, 2, 2 }, { 0, 1, 2, 3 }, { 0, 1, 2, 3 }, { 0, 1, 2, 3 },
|
||
|
{ 0, 1, 2, 4 }, { 0, 1, 3, 4 }, { 0, 1, 3, 4 }, { 0, 1, 3, 5 },
|
||
|
{ 0, 2, 4, 5 }, { 0, 2, 4, 6 }, { 0, 2, 4, 6 }, { 0, 2, 5, 7 },
|
||
|
{ 0, 2, 5, 8 }, { 0, 3, 6, 8 }, { 0, 3, 6, 9 }, { 0, 3, 7, 10 },
|
||
|
{ 0, 4, 8, 11 }, { 0, 4, 8, 12 }, { 0, 4, 9, 13 }, { 0, 5, 10, 14 },
|
||
|
{ 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 }
|
||
|
};
|
||
|
int32_t result = s_detune_adjustment[keycode][detune & 3];
|
||
|
return bitfield(detune, 2) ? -result : result;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// 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;
|
||
|
// this applies to OPM and OPZ, so it lives here
|
||
|
// in a central location
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
inline uint32_t opm_key_code_to_phase_step(uint32_t block_freq, int32_t 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
|
||
|
// then converted that frequency to fnum using the formula documented in the
|
||
|
// YM2608 manual.
|
||
|
//
|
||
|
// However, the YM2608 manual describes everything in terms of a nominal 8MHz
|
||
|
// clock, which produces an FM clock of:
|
||
|
//
|
||
|
// 8000000 / 24(operators) / 6(prescale) = 55555Hz FM clock
|
||
|
//
|
||
|
// Whereas the descriptions for the YM2151 use a nominal 3.579545MHz 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:
|
||
|
//
|
||
|
// fnum = (double(144) * freq * (1 << 20)) / double(8053920) / 4;
|
||
|
//
|
||
|
// Unfortunately, the computed table differs in a few spots from the data
|
||
|
// verified from an actual chip. The table below comes from David Viens'
|
||
|
// analysis, used with his permission.
|
||
|
static const uint32_t s_phase_step[12*64] =
|
||
|
{
|
||
|
41568,41600,41632,41664,41696,41728,41760,41792,41856,41888,41920,41952,42016,42048,42080,42112,
|
||
|
42176,42208,42240,42272,42304,42336,42368,42400,42464,42496,42528,42560,42624,42656,42688,42720,
|
||
|
42784,42816,42848,42880,42912,42944,42976,43008,43072,43104,43136,43168,43232,43264,43296,43328,
|
||
|
43392,43424,43456,43488,43552,43584,43616,43648,43712,43744,43776,43808,43872,43904,43936,43968,
|
||
|
44032,44064,44096,44128,44192,44224,44256,44288,44352,44384,44416,44448,44512,44544,44576,44608,
|
||
|
44672,44704,44736,44768,44832,44864,44896,44928,44992,45024,45056,45088,45152,45184,45216,45248,
|
||
|
45312,45344,45376,45408,45472,45504,45536,45568,45632,45664,45728,45760,45792,45824,45888,45920,
|
||
|
45984,46016,46048,46080,46144,46176,46208,46240,46304,46336,46368,46400,46464,46496,46528,46560,
|
||
|
46656,46688,46720,46752,46816,46848,46880,46912,46976,47008,47072,47104,47136,47168,47232,47264,
|
||
|
47328,47360,47392,47424,47488,47520,47552,47584,47648,47680,47744,47776,47808,47840,47904,47936,
|
||
|
48032,48064,48096,48128,48192,48224,48288,48320,48384,48416,48448,48480,48544,48576,48640,48672,
|
||
|
48736,48768,48800,48832,48896,48928,48992,49024,49088,49120,49152,49184,49248,49280,49344,49376,
|
||
|
49440,49472,49504,49536,49600,49632,49696,49728,49792,49824,49856,49888,49952,49984,50048,50080,
|
||
|
50144,50176,50208,50240,50304,50336,50400,50432,50496,50528,50560,50592,50656,50688,50752,50784,
|
||
|
50880,50912,50944,50976,51040,51072,51136,51168,51232,51264,51328,51360,51424,51456,51488,51520,
|
||
|
51616,51648,51680,51712,51776,51808,51872,51904,51968,52000,52064,52096,52160,52192,52224,52256,
|
||
|
52384,52416,52448,52480,52544,52576,52640,52672,52736,52768,52832,52864,52928,52960,52992,53024,
|
||
|
53120,53152,53216,53248,53312,53344,53408,53440,53504,53536,53600,53632,53696,53728,53792,53824,
|
||
|
53920,53952,54016,54048,54112,54144,54208,54240,54304,54336,54400,54432,54496,54528,54592,54624,
|
||
|
54688,54720,54784,54816,54880,54912,54976,55008,55072,55104,55168,55200,55264,55296,55360,55392,
|
||
|
55488,55520,55584,55616,55680,55712,55776,55808,55872,55936,55968,56032,56064,56128,56160,56224,
|
||
|
56288,56320,56384,56416,56480,56512,56576,56608,56672,56736,56768,56832,56864,56928,56960,57024,
|
||
|
57120,57152,57216,57248,57312,57376,57408,57472,57536,57568,57632,57664,57728,57792,57824,57888,
|
||
|
57952,57984,58048,58080,58144,58208,58240,58304,58368,58400,58464,58496,58560,58624,58656,58720,
|
||
|
58784,58816,58880,58912,58976,59040,59072,59136,59200,59232,59296,59328,59392,59456,59488,59552,
|
||
|
59648,59680,59744,59776,59840,59904,59936,60000,60064,60128,60160,60224,60288,60320,60384,60416,
|
||
|
60512,60544,60608,60640,60704,60768,60800,60864,60928,60992,61024,61088,61152,61184,61248,61280,
|
||
|
61376,61408,61472,61536,61600,61632,61696,61760,61824,61856,61920,61984,62048,62080,62144,62208,
|
||
|
62272,62304,62368,62432,62496,62528,62592,62656,62720,62752,62816,62880,62944,62976,63040,63104,
|
||
|
63200,63232,63296,63360,63424,63456,63520,63584,63648,63680,63744,63808,63872,63904,63968,64032,
|
||
|
64096,64128,64192,64256,64320,64352,64416,64480,64544,64608,64672,64704,64768,64832,64896,64928,
|
||
|
65024,65056,65120,65184,65248,65312,65376,65408,65504,65536,65600,65664,65728,65792,65856,65888,
|
||
|
65984,66016,66080,66144,66208,66272,66336,66368,66464,66496,66560,66624,66688,66752,66816,66848,
|
||
|
66944,66976,67040,67104,67168,67232,67296,67328,67424,67456,67520,67584,67648,67712,67776,67808,
|
||
|
67904,67936,68000,68064,68128,68192,68256,68288,68384,68448,68512,68544,68640,68672,68736,68800,
|
||
|
68896,68928,68992,69056,69120,69184,69248,69280,69376,69440,69504,69536,69632,69664,69728,69792,
|
||
|
69920,69952,70016,70080,70144,70208,70272,70304,70400,70464,70528,70560,70656,70688,70752,70816,
|
||
|
70912,70976,71040,71104,71136,71232,71264,71360,71424,71488,71552,71616,71648,71744,71776,71872,
|
||
|
71968,72032,72096,72160,72192,72288,72320,72416,72480,72544,72608,72672,72704,72800,72832,72928,
|
||
|
72992,73056,73120,73184,73216,73312,73344,73440,73504,73568,73632,73696,73728,73824,73856,73952,
|
||
|
74080,74144,74208,74272,74304,74400,74432,74528,74592,74656,74720,74784,74816,74912,74944,75040,
|
||
|
75136,75200,75264,75328,75360,75456,75488,75584,75648,75712,75776,75840,75872,75968,76000,76096,
|
||
|
76224,76288,76352,76416,76448,76544,76576,76672,76736,76800,76864,76928,77024,77120,77152,77248,
|
||
|
77344,77408,77472,77536,77568,77664,77696,77792,77856,77920,77984,78048,78144,78240,78272,78368,
|
||
|
78464,78528,78592,78656,78688,78784,78816,78912,78976,79040,79104,79168,79264,79360,79392,79488,
|
||
|
79616,79680,79744,79808,79840,79936,79968,80064,80128,80192,80256,80320,80416,80512,80544,80640,
|
||
|
80768,80832,80896,80960,80992,81088,81120,81216,81280,81344,81408,81472,81568,81664,81696,81792,
|
||
|
81952,82016,82080,82144,82176,82272,82304,82400,82464,82528,82592,82656,82752,82848,82880,82976
|
||
|
};
|
||
|
|
||
|
// extract the block (octave) first
|
||
|
uint32_t block = bitfield(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
|
||
|
uint32_t adjusted_code = bitfield(block_freq, 6, 4) - bitfield(block_freq, 8, 2);
|
||
|
|
||
|
// now re-insert the 6-bit fraction
|
||
|
int32_t eff_freq = (adjusted_code << 6) | bitfield(block_freq, 0, 6);
|
||
|
|
||
|
// now that the gaps are removed, add the delta
|
||
|
eff_freq += delta;
|
||
|
|
||
|
// handle over/underflow by adjusting the block:
|
||
|
if (uint32_t(eff_freq) >= 768)
|
||
|
{
|
||
|
// minimum delta is -512 (PM), so we can only underflow by 1 octave
|
||
|
if (eff_freq < 0)
|
||
|
{
|
||
|
eff_freq += 768;
|
||
|
if (block-- == 0)
|
||
|
return s_phase_step[0] >> 7;
|
||
|
}
|
||
|
|
||
|
// maximum delta is +512+608 (PM+detune), so we can overflow by up to 2 octaves
|
||
|
else
|
||
|
{
|
||
|
eff_freq -= 768;
|
||
|
if (eff_freq >= 768)
|
||
|
block++, eff_freq -= 768;
|
||
|
if (block++ >= 7)
|
||
|
return s_phase_step[767];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// look up the phase shift for the key code, then shift by octave
|
||
|
return s_phase_step[eff_freq] >> (block ^ 7);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// opn_lfo_pm_phase_adjustment - given the 7 most
|
||
|
// significant frequency number bits, plus a 3-bit
|
||
|
// PM depth value and a signed 5-bit raw PM value,
|
||
|
// return a signed PM adjustment to the frequency;
|
||
|
// algorithm written to match Nuked behavior
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
inline int32_t opn_lfo_pm_phase_adjustment(uint32_t fnum_bits, uint32_t pm_sensitivity, int32_t 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
|
||
|
// value containing 0-2 bits
|
||
|
static uint8_t const s_lfo_pm_shifts[8][8] =
|
||
|
{
|
||
|
{ 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77 },
|
||
|
{ 0x77, 0x77, 0x77, 0x77, 0x72, 0x72, 0x72, 0x72 },
|
||
|
{ 0x77, 0x77, 0x77, 0x72, 0x72, 0x72, 0x17, 0x17 },
|
||
|
{ 0x77, 0x77, 0x72, 0x72, 0x17, 0x17, 0x12, 0x12 },
|
||
|
{ 0x77, 0x77, 0x72, 0x17, 0x17, 0x17, 0x12, 0x07 },
|
||
|
{ 0x77, 0x77, 0x17, 0x12, 0x07, 0x07, 0x02, 0x01 },
|
||
|
{ 0x77, 0x77, 0x17, 0x12, 0x07, 0x07, 0x02, 0x01 },
|
||
|
{ 0x77, 0x77, 0x17, 0x12, 0x07, 0x07, 0x02, 0x01 }
|
||
|
};
|
||
|
|
||
|
// look up the relevant shifts
|
||
|
int32_t abs_pm = (lfo_raw_pm < 0) ? -lfo_raw_pm : lfo_raw_pm;
|
||
|
uint32_t const shifts = s_lfo_pm_shifts[pm_sensitivity][bitfield(abs_pm, 0, 3)];
|
||
|
|
||
|
// compute the adjustment
|
||
|
int32_t adjust = (fnum_bits >> bitfield(shifts, 0, 4)) + (fnum_bits >> bitfield(shifts, 4, 4));
|
||
|
if (pm_sensitivity > 5)
|
||
|
adjust <<= pm_sensitivity - 5;
|
||
|
adjust >>= 2;
|
||
|
|
||
|
// every 16 cycles it inverts sign
|
||
|
return (lfo_raw_pm < 0) ? -adjust : adjust;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//*********************************************************
|
||
|
// FM OPERATOR
|
||
|
//*********************************************************
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// fm_operator - constructor
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
fm_operator<RegisterType>::fm_operator(fm_engine_base<RegisterType> &owner, uint32_t opoffs) :
|
||
|
m_choffs(0),
|
||
|
m_opoffs(opoffs),
|
||
|
m_phase(0),
|
||
|
m_env_attenuation(0x3ff),
|
||
|
m_env_state(EG_RELEASE),
|
||
|
m_ssg_inverted(false),
|
||
|
m_key_state(0),
|
||
|
m_keyon_live(0),
|
||
|
m_regs(owner.regs()),
|
||
|
m_owner(owner)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// reset - reset the channel state
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
void fm_operator<RegisterType>::reset()
|
||
|
{
|
||
|
// reset our data
|
||
|
m_phase = 0;
|
||
|
m_env_attenuation = 0x3ff;
|
||
|
m_env_state = EG_RELEASE;
|
||
|
m_ssg_inverted = 0;
|
||
|
m_key_state = 0;
|
||
|
m_keyon_live = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// save_restore - save or restore the data
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
void fm_operator<RegisterType>::save_restore(ymfm_saved_state &state)
|
||
|
{
|
||
|
state.save_restore(m_phase);
|
||
|
state.save_restore(m_env_attenuation);
|
||
|
state.save_restore(m_env_state);
|
||
|
state.save_restore(m_ssg_inverted);
|
||
|
state.save_restore(m_key_state);
|
||
|
state.save_restore(m_keyon_live);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// prepare - prepare for clocking
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
bool fm_operator<RegisterType>::prepare()
|
||
|
{
|
||
|
// cache the data
|
||
|
m_regs.cache_operator_data(m_choffs, m_opoffs, m_cache);
|
||
|
|
||
|
// clock the key state
|
||
|
clock_keystate(uint32_t(m_keyon_live != 0));
|
||
|
m_keyon_live &= ~(1 << KEYON_CSM);
|
||
|
|
||
|
// we're active until we're quiet after the release
|
||
|
return (m_env_state != (RegisterType::EG_HAS_REVERB ? EG_REVERB : EG_RELEASE) || m_env_attenuation < EG_QUIET);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// clock - master clocking function
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
void fm_operator<RegisterType>::clock(uint32_t env_counter, int32_t lfo_raw_pm)
|
||
|
{
|
||
|
// clock the SSG-EG state (OPN/OPNA)
|
||
|
if (m_regs.op_ssg_eg_enable(m_opoffs))
|
||
|
clock_ssg_eg_state();
|
||
|
else
|
||
|
m_ssg_inverted = false;
|
||
|
|
||
|
// clock the envelope if on an envelope cycle; env_counter is a x.2 value
|
||
|
if (bitfield(env_counter, 0, 2) == 0)
|
||
|
clock_envelope(env_counter >> 2);
|
||
|
|
||
|
// clock the phase
|
||
|
clock_phase(lfo_raw_pm);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// compute_volume - compute the 14-bit signed
|
||
|
// volume of this operator, given a phase
|
||
|
// modulation and an AM LFO offset
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
int32_t fm_operator<RegisterType>::compute_volume(uint32_t phase, uint32_t 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 > EG_QUIET)
|
||
|
return 0;
|
||
|
|
||
|
// get the absolute value of the sin, as attenuation, as a 4.8 fixed point value
|
||
|
uint32_t 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
|
||
|
uint32_t env_attenuation = envelope_attenuation(am_offset) << 2;
|
||
|
|
||
|
// combine into a 5.8 value, then convert from attenuation to 13-bit linear volume
|
||
|
int32_t 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 bitfield(sin_attenuation, 15) ? -result : result;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// compute_noise_volume - compute the 14-bit
|
||
|
// signed noise volume of this operator, given a
|
||
|
// noise input value and an AM offset
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
int32_t fm_operator<RegisterType>::compute_noise_volume(uint32_t 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
|
||
|
int32_t result = (envelope_attenuation(am_offset) ^ 0x3ff) << 1;
|
||
|
|
||
|
// QUESTION: is AM applied still?
|
||
|
|
||
|
// negate based on the noise state
|
||
|
return bitfield(m_regs.noise_state(), 0) ? -result : result;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// keyonoff - signal a key on/off event
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
void fm_operator<RegisterType>::keyonoff(uint32_t on, keyon_type type)
|
||
|
{
|
||
|
m_keyon_live = (m_keyon_live & ~(1 << int(type))) | (bitfield(on, 0) << int(type));
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// start_attack - start the attack phase; called
|
||
|
// when a keyon happens or when an SSG-EG cycle
|
||
|
// is complete and restarts
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
void fm_operator<RegisterType>::start_attack(bool is_restart)
|
||
|
{
|
||
|
// don't change anything if already in attack state
|
||
|
if (m_env_state == EG_ATTACK)
|
||
|
return;
|
||
|
m_env_state = EG_ATTACK;
|
||
|
|
||
|
// generally not inverted at start, except if SSG-EG is enabled and
|
||
|
// one of the inverted modes is specified; leave this alone on a
|
||
|
// restart, as it is managed by the clock_ssg_eg_state() code
|
||
|
if (RegisterType::EG_HAS_SSG && !is_restart)
|
||
|
m_ssg_inverted = m_regs.op_ssg_eg_enable(m_opoffs) & bitfield(m_regs.op_ssg_eg_mode(m_opoffs), 2);
|
||
|
|
||
|
// reset the phase when we start an attack due to a key on
|
||
|
// (but not when due to an SSG-EG restart except in certain cases
|
||
|
// managed directly by the SSG-EG code)
|
||
|
if (!is_restart)
|
||
|
m_phase = 0;
|
||
|
|
||
|
// if the attack rate >= 62 then immediately go to max attenuation
|
||
|
if (m_cache.eg_rate[EG_ATTACK] >= 62)
|
||
|
m_env_attenuation = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// start_release - start the release phase;
|
||
|
// called when a keyoff happens
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
void fm_operator<RegisterType>::start_release()
|
||
|
{
|
||
|
// don't change anything if already in release state
|
||
|
if (m_env_state >= EG_RELEASE)
|
||
|
return;
|
||
|
m_env_state = EG_RELEASE;
|
||
|
|
||
|
// if attenuation if inverted due to SSG-EG, snap the inverted attenuation
|
||
|
// as the starting point
|
||
|
if (RegisterType::EG_HAS_SSG && m_ssg_inverted)
|
||
|
{
|
||
|
m_env_attenuation = (0x200 - m_env_attenuation) & 0x3ff;
|
||
|
m_ssg_inverted = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// clock_keystate - clock the keystate to match
|
||
|
// the incoming keystate
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
void fm_operator<RegisterType>::clock_keystate(uint32_t keystate)
|
||
|
{
|
||
|
assert(keystate == 0 || keystate == 1);
|
||
|
|
||
|
// has the key changed?
|
||
|
if ((keystate ^ m_key_state) != 0)
|
||
|
{
|
||
|
m_key_state = keystate;
|
||
|
|
||
|
// if the key has turned on, start the attack
|
||
|
if (keystate != 0)
|
||
|
{
|
||
|
// 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 = EG_DEPRESS;
|
||
|
else
|
||
|
start_attack();
|
||
|
}
|
||
|
|
||
|
// otherwise, start the release
|
||
|
else
|
||
|
start_release();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// clock_ssg_eg_state - clock the SSG-EG state;
|
||
|
// should only be called if SSG-EG is enabled
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
void fm_operator<RegisterType>::clock_ssg_eg_state()
|
||
|
{
|
||
|
// work only happens once the attenuation crosses above 0x200
|
||
|
if (!bitfield(m_env_attenuation, 9))
|
||
|
return;
|
||
|
|
||
|
// 8 SSG-EG modes:
|
||
|
// 000: repeat normally
|
||
|
// 001: run once, hold low
|
||
|
// 010: repeat, alternating between inverted/non-inverted
|
||
|
// 011: run once, hold high
|
||
|
// 100: inverted repeat normally
|
||
|
// 101: inverted run once, hold low
|
||
|
// 110: inverted repeat, alternating between inverted/non-inverted
|
||
|
// 111: inverted run once, hold high
|
||
|
uint32_t mode = m_regs.op_ssg_eg_mode(m_opoffs);
|
||
|
|
||
|
// hold modes (1/3/5/7)
|
||
|
if (bitfield(mode, 0))
|
||
|
{
|
||
|
// set the inverted flag to the end state (0 for modes 1/7, 1 for modes 3/5)
|
||
|
m_ssg_inverted = bitfield(mode, 2) ^ bitfield(mode, 1);
|
||
|
|
||
|
// if holding, force the attenuation to the expected value once we're
|
||
|
// past the attack phase
|
||
|
if (m_env_state != EG_ATTACK)
|
||
|
m_env_attenuation = m_ssg_inverted ? 0x200 : 0x3ff;
|
||
|
}
|
||
|
|
||
|
// continuous modes (0/2/4/6)
|
||
|
else
|
||
|
{
|
||
|
// toggle invert in alternating mode (even in attack state)
|
||
|
m_ssg_inverted ^= bitfield(mode, 1);
|
||
|
|
||
|
// restart attack if in decay/sustain states
|
||
|
if (m_env_state == EG_DECAY || m_env_state == EG_SUSTAIN)
|
||
|
start_attack(true);
|
||
|
|
||
|
// phase is reset to 0 in modes 0/4
|
||
|
if (bitfield(mode, 1) == 0)
|
||
|
m_phase = 0;
|
||
|
}
|
||
|
|
||
|
// in all modes, once we hit release state, attenuation is forced to maximum
|
||
|
if (m_env_state == EG_RELEASE)
|
||
|
m_env_attenuation = 0x3ff;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// clock_envelope - clock the envelope state
|
||
|
// according to the given count
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
void fm_operator<RegisterType>::clock_envelope(uint32_t env_counter)
|
||
|
{
|
||
|
// handle attack->decay transitions
|
||
|
if (m_env_state == EG_ATTACK && m_env_attenuation == 0)
|
||
|
m_env_state = EG_DECAY;
|
||
|
|
||
|
// handle decay->sustain transitions; it is important to do this immediately
|
||
|
// after the attack->decay transition above in the event that the sustain level
|
||
|
// is set to 0 (in which case we will skip right to sustain without doing any
|
||
|
// decay); as an example where this can be heard, check the cymbals sound
|
||
|
// in channel 0 of shinobi's test mode sound #5
|
||
|
if (m_env_state == EG_DECAY && m_env_attenuation >= m_cache.eg_sustain)
|
||
|
m_env_state = EG_SUSTAIN;
|
||
|
|
||
|
// fetch the appropriate 6-bit rate value from the cache
|
||
|
uint32_t 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
|
||
|
uint32_t rate_shift = rate >> 2;
|
||
|
env_counter <<= rate_shift;
|
||
|
|
||
|
// see if the fractional part is 0; if not, it's not time to clock
|
||
|
if (bitfield(env_counter, 0, 11) != 0)
|
||
|
return;
|
||
|
|
||
|
// determine the increment based on the non-fractional part of env_counter
|
||
|
uint32_t relevant_bits = bitfield(env_counter, (rate_shift <= 11) ? 11 : rate_shift, 3);
|
||
|
uint32_t increment = attenuation_increment(rate, relevant_bits);
|
||
|
|
||
|
// attack is the only one that increases
|
||
|
if (m_env_state == EG_ATTACK)
|
||
|
{
|
||
|
// glitch means that attack rates of 62/63 don't increment if
|
||
|
// changed after the initial key on (where they are handled
|
||
|
// specially); nukeykt confirms this happens on OPM, OPN, OPL/OPLL
|
||
|
// at least so assuming it is true for everyone
|
||
|
if (rate < 62)
|
||
|
m_env_attenuation += (~m_env_attenuation * increment) >> 4;
|
||
|
}
|
||
|
|
||
|
// all other cases are similar
|
||
|
else
|
||
|
{
|
||
|
// non-SSG-EG cases just apply the increment
|
||
|
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
|
||
|
else if (m_env_attenuation < 0x200)
|
||
|
m_env_attenuation += 4 * increment;
|
||
|
|
||
|
// 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 == EG_DEPRESS && m_env_attenuation >= 0x200)
|
||
|
start_attack();
|
||
|
|
||
|
// transition from release to reverb, should switch at -18dB
|
||
|
if (RegisterType::EG_HAS_REVERB && m_env_state == EG_RELEASE && m_env_attenuation >= 0xc0)
|
||
|
m_env_state = EG_REVERB;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// clock_phase - clock the 10.10 phase value; the
|
||
|
// OPN version of the logic has been verified
|
||
|
// against the Nuked phase generator
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
void fm_operator<RegisterType>::clock_phase(int32_t lfo_raw_pm)
|
||
|
{
|
||
|
// read from the cache, or recalculate if PM active
|
||
|
uint32_t phase_step = m_cache.phase_step;
|
||
|
if (phase_step == 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;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// envelope_attenuation - return the effective
|
||
|
// attenuation of the envelope
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
uint32_t fm_operator<RegisterType>::envelope_attenuation(uint32_t am_offset) const
|
||
|
{
|
||
|
uint32_t result = m_env_attenuation >> m_cache.eg_shift;
|
||
|
|
||
|
// invert if necessary due to SSG-EG
|
||
|
if (RegisterType::EG_HAS_SSG && m_ssg_inverted)
|
||
|
result = (0x200 - result) & 0x3ff;
|
||
|
|
||
|
// add in LFO AM modulation
|
||
|
if (m_regs.op_lfo_am_enable(m_opoffs))
|
||
|
result += am_offset;
|
||
|
|
||
|
// add in total level and KSL from the cache
|
||
|
result += m_cache.total_level;
|
||
|
|
||
|
// clamp to max, apply shift, and return
|
||
|
return std::min<uint32_t>(result, 0x3ff);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//*********************************************************
|
||
|
// FM CHANNEL
|
||
|
//*********************************************************
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// fm_channel - constructor
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
fm_channel<RegisterType>::fm_channel(fm_engine_base<RegisterType> &owner, uint32_t choffs) :
|
||
|
m_choffs(choffs),
|
||
|
m_feedback{ 0, 0 },
|
||
|
m_feedback_in(0),
|
||
|
m_op{ nullptr, nullptr, nullptr, nullptr },
|
||
|
m_regs(owner.regs()),
|
||
|
m_owner(owner)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// reset - reset the channel state
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
void fm_channel<RegisterType>::reset()
|
||
|
{
|
||
|
// reset our data
|
||
|
m_feedback[0] = m_feedback[1] = 0;
|
||
|
m_feedback_in = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// save_restore - save or restore the data
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
void fm_channel<RegisterType>::save_restore(ymfm_saved_state &state)
|
||
|
{
|
||
|
state.save_restore(m_feedback[0]);
|
||
|
state.save_restore(m_feedback[1]);
|
||
|
state.save_restore(m_feedback_in);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// keyonoff - signal key on/off to our operators
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
void fm_channel<RegisterType>::keyonoff(uint32_t states, keyon_type type, uint32_t chnum)
|
||
|
{
|
||
|
for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++)
|
||
|
if (m_op[opnum] != nullptr)
|
||
|
m_op[opnum]->keyonoff(bitfield(states, opnum), type);
|
||
|
|
||
|
if (debug::LOG_KEYON_EVENTS && ((debug::GLOBAL_FM_CHANNEL_MASK >> chnum) & 1) != 0)
|
||
|
for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++)
|
||
|
if (m_op[opnum] != nullptr)
|
||
|
debug::log_keyon("%c%s\n", bitfield(states, opnum) ? '+' : '-', m_regs.log_keyon(m_choffs, m_op[opnum]->opoffs()).c_str());
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// prepare - prepare for clocking
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
bool fm_channel<RegisterType>::prepare()
|
||
|
{
|
||
|
uint32_t active_mask = 0;
|
||
|
|
||
|
// prepare all operators and determine if they are active
|
||
|
for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++)
|
||
|
if (m_op[opnum] != nullptr)
|
||
|
if (m_op[opnum]->prepare())
|
||
|
active_mask |= 1 << opnum;
|
||
|
|
||
|
return (active_mask != 0);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// clock - master clock of all operators
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
void fm_channel<RegisterType>::clock(uint32_t env_counter, int32_t lfo_raw_pm)
|
||
|
{
|
||
|
// clock the feedback through
|
||
|
m_feedback[0] = m_feedback[1];
|
||
|
m_feedback[1] = m_feedback_in;
|
||
|
|
||
|
for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++)
|
||
|
if (m_op[opnum] != nullptr)
|
||
|
m_op[opnum]->clock(env_counter, lfo_raw_pm);
|
||
|
|
||
|
/*
|
||
|
useful temporary code for envelope debugging
|
||
|
if (m_choffs == 0x101)
|
||
|
{
|
||
|
for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++)
|
||
|
{
|
||
|
auto &op = *m_op[((opnum & 1) << 1) | ((opnum >> 1) & 1)];
|
||
|
printf(" %c%03X%c%c ",
|
||
|
"PADSRV"[op.debug_eg_state()],
|
||
|
op.debug_eg_attenuation(),
|
||
|
op.debug_ssg_inverted() ? '-' : '+',
|
||
|
m_regs.op_ssg_eg_enable(op.opoffs()) ? '0' + m_regs.op_ssg_eg_mode(op.opoffs()) : ' ');
|
||
|
}
|
||
|
printf(" -- ");
|
||
|
}
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// 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 fm_channel<RegisterType>::output_2op(output_data &output, uint32_t rshift, int32_t 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
|
||
|
uint32_t am_offset = m_regs.lfo_am_offset(m_choffs);
|
||
|
|
||
|
// operator 1 has optional self-feedback
|
||
|
int32_t opmod = 0;
|
||
|
uint32_t 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
|
||
|
int32_t 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
|
||
|
int32_t result;
|
||
|
if (bitfield(m_regs.ch_algorithm(m_choffs), 0) == 0)
|
||
|
{
|
||
|
// 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;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
result = (RegisterType::MODULATOR_DELAY ? m_feedback[1] : op1value) >> rshift;
|
||
|
result += m_op[1]->compute_volume(m_op[1]->phase(), am_offset) >> rshift;
|
||
|
int32_t clipmin = -clipmax - 1;
|
||
|
result = clamp(result, clipmin, clipmax);
|
||
|
}
|
||
|
|
||
|
// add to the output
|
||
|
add_to_output(m_choffs, output, result);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// 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 fm_channel<RegisterType>::output_4op(output_data &output, uint32_t rshift, int32_t 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
|
||
|
uint32_t am_offset = m_regs.lfo_am_offset(m_choffs);
|
||
|
|
||
|
// operator 1 has optional self-feedback
|
||
|
int32_t opmod = 0;
|
||
|
uint32_t 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
|
||
|
int32_t 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 (opout) that is populated as we go:
|
||
|
// 0 = 0
|
||
|
// 1 = O1
|
||
|
// 2 = O2
|
||
|
// 3 = O3
|
||
|
// 4 = (O4)
|
||
|
// 5 = O1+O2
|
||
|
// 6 = O1+O3
|
||
|
// 7 = O2+O3
|
||
|
//
|
||
|
// 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 uint16_t const s_algorithm_ops[8+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]
|
||
|
};
|
||
|
uint32_t algorithm_ops = s_algorithm_ops[m_regs.ch_algorithm(m_choffs)];
|
||
|
|
||
|
// populate the opout table
|
||
|
int16_t opout[8];
|
||
|
opout[0] = 0;
|
||
|
opout[1] = op1value;
|
||
|
|
||
|
// compute the 14-bit volume/value of operator 2
|
||
|
opmod = opout[bitfield(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
|
||
|
opmod = opout[bitfield(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; all algorithms consume OP4 output at a minimum
|
||
|
int32_t result;
|
||
|
if (m_regs.noise_enable() && m_choffs == 7)
|
||
|
result = m_op[3]->compute_noise_volume(am_offset);
|
||
|
else
|
||
|
{
|
||
|
opmod = opout[bitfield(algorithm_ops, 4, 3)] >> 1;
|
||
|
result = m_op[3]->compute_volume(m_op[3]->phase() + opmod, am_offset);
|
||
|
}
|
||
|
result >>= rshift;
|
||
|
|
||
|
// optionally add OP1, OP2, OP3
|
||
|
int32_t clipmin = -clipmax - 1;
|
||
|
if (bitfield(algorithm_ops, 7) != 0)
|
||
|
result = clamp(result + (opout[1] >> rshift), clipmin, clipmax);
|
||
|
if (bitfield(algorithm_ops, 8) != 0)
|
||
|
result = clamp(result + (opout[2] >> rshift), clipmin, clipmax);
|
||
|
if (bitfield(algorithm_ops, 9) != 0)
|
||
|
result = clamp(result + (opout[3] >> rshift), clipmin, clipmax);
|
||
|
|
||
|
// add to the output
|
||
|
add_to_output(m_choffs, output, result);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// output_rhythm_ch6 - special case output
|
||
|
// computation for OPL channel 6 in rhythm mode,
|
||
|
// which outputs a Bass Drum instrument
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
void fm_channel<RegisterType>::output_rhythm_ch6(output_data &output, uint32_t rshift, int32_t clipmax) const
|
||
|
{
|
||
|
// AM amount is the same across all operators; compute it once
|
||
|
uint32_t 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
|
||
|
int32_t opmod = 0;
|
||
|
uint32_t 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
|
||
|
int32_t 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 = bitfield(m_regs.ch_algorithm(m_choffs), 0) ? 0 : (opout1 >> 1);
|
||
|
int32_t result = m_op[1]->compute_volume(m_op[1]->phase() + opmod, am_offset) >> rshift;
|
||
|
|
||
|
// add to the output
|
||
|
add_to_output(m_choffs, output, result * 2);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// output_rhythm_ch7 - special case output
|
||
|
// computation for OPL channel 7 in rhythm mode,
|
||
|
// which outputs High Hat and Snare Drum
|
||
|
// instruments
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
void fm_channel<RegisterType>::output_rhythm_ch7(uint32_t phase_select, output_data &output, uint32_t rshift, int32_t clipmax) const
|
||
|
{
|
||
|
// AM amount is the same across all operators; compute it once
|
||
|
uint32_t am_offset = m_regs.lfo_am_offset(m_choffs);
|
||
|
uint32_t noise_state = bitfield(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
|
||
|
uint32_t phase = (phase_select << 9) | (0xd0 >> (2 * (noise_state ^ phase_select)));
|
||
|
int32_t 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
|
||
|
uint32_t op13phase = m_op[0]->phase();
|
||
|
phase = (0x100 << bitfield(op13phase, 8)) ^ (noise_state << 8);
|
||
|
result += m_op[1]->compute_volume(phase, am_offset) >> rshift;
|
||
|
result = clamp(result, -clipmax - 1, clipmax);
|
||
|
|
||
|
// add to the output
|
||
|
add_to_output(m_choffs, output, result * 2);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// 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>
|
||
|
void fm_channel<RegisterType>::output_rhythm_ch8(uint32_t phase_select, output_data &output, uint32_t rshift, int32_t clipmax) const
|
||
|
{
|
||
|
// AM amount is the same across all operators; compute it once
|
||
|
uint32_t am_offset = m_regs.lfo_am_offset(m_choffs);
|
||
|
|
||
|
// Tom Tom: this is just a single operator processed normally
|
||
|
int32_t result = m_op[0]->compute_volume(m_op[0]->phase(), am_offset) >> rshift;
|
||
|
|
||
|
// Top Cymbal: this uses the envelope from operator 17 (channel 8),
|
||
|
// and the operator 13/17 phase select to compute the phase
|
||
|
uint32_t phase = 0x100 | (phase_select << 9);
|
||
|
result += m_op[1]->compute_volume(phase, am_offset) >> rshift;
|
||
|
result = clamp(result, -clipmax - 1, clipmax);
|
||
|
|
||
|
// add to the output
|
||
|
add_to_output(m_choffs, output, result * 2);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//*********************************************************
|
||
|
// FM ENGINE BASE
|
||
|
//*********************************************************
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// fm_engine_base - constructor
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
fm_engine_base<RegisterType>::fm_engine_base(ymfm_interface &intf) :
|
||
|
m_intf(intf),
|
||
|
m_env_counter(0),
|
||
|
m_status(0),
|
||
|
m_clock_prescale(RegisterType::DEFAULT_PRESCALE),
|
||
|
m_irq_mask(STATUS_TIMERA | STATUS_TIMERB),
|
||
|
m_irq_state(0),
|
||
|
m_timer_running{0,0},
|
||
|
m_total_clocks(0),
|
||
|
m_active_channels(ALL_CHANNELS),
|
||
|
m_modified_channels(ALL_CHANNELS),
|
||
|
m_prepare_count(0)
|
||
|
{
|
||
|
// inform the interface of their engine
|
||
|
m_intf.m_engine = this;
|
||
|
|
||
|
// create the channels
|
||
|
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
|
||
|
m_channel[chnum] = std::make_unique<fm_channel<RegisterType>>(*this, RegisterType::channel_offset(chnum));
|
||
|
|
||
|
// create the operators
|
||
|
for (uint32_t opnum = 0; opnum < OPERATORS; opnum++)
|
||
|
m_operator[opnum] = std::make_unique<fm_operator<RegisterType>>(*this, RegisterType::operator_offset(opnum));
|
||
|
|
||
|
#if (YMFM_DEBUG_LOG_WAVFILES)
|
||
|
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
|
||
|
m_wavfile[chnum].set_index(chnum);
|
||
|
#endif
|
||
|
|
||
|
// do the initial operator assignment
|
||
|
assign_operators();
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// reset - reset the overall state
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
void fm_engine_base<RegisterType>::reset()
|
||
|
{
|
||
|
// reset all status bits
|
||
|
set_reset_status(0, 0xff);
|
||
|
|
||
|
// 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);
|
||
|
|
||
|
// reset the channels
|
||
|
for (auto &chan : m_channel)
|
||
|
chan->reset();
|
||
|
|
||
|
// reset the operators
|
||
|
for (auto &op : m_operator)
|
||
|
op->reset();
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// save_restore - save or restore the data
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
void fm_engine_base<RegisterType>::save_restore(ymfm_saved_state &state)
|
||
|
{
|
||
|
// save our data
|
||
|
state.save_restore(m_env_counter);
|
||
|
state.save_restore(m_status);
|
||
|
state.save_restore(m_clock_prescale);
|
||
|
state.save_restore(m_irq_mask);
|
||
|
state.save_restore(m_irq_state);
|
||
|
state.save_restore(m_timer_running[0]);
|
||
|
state.save_restore(m_timer_running[1]);
|
||
|
state.save_restore(m_total_clocks);
|
||
|
|
||
|
// save the register/family data
|
||
|
m_regs.save_restore(state);
|
||
|
|
||
|
// save channel data
|
||
|
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
|
||
|
m_channel[chnum]->save_restore(state);
|
||
|
|
||
|
// save operator data
|
||
|
for (uint32_t opnum = 0; opnum < OPERATORS; opnum++)
|
||
|
m_operator[opnum]->save_restore(state);
|
||
|
|
||
|
// invalidate any caches
|
||
|
invalidate_caches();
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// clock - iterate over all channels, clocking
|
||
|
// them forward one step
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
uint32_t fm_engine_base<RegisterType>::clock(uint32_t chanmask)
|
||
|
{
|
||
|
// update the clock counter
|
||
|
m_total_clocks++;
|
||
|
|
||
|
// 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 (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
|
||
|
if (bitfield(chanmask, chnum))
|
||
|
if (m_channel[chnum]->prepare())
|
||
|
m_active_channels |= 1 << chnum;
|
||
|
|
||
|
// reset the modified channels and prepare count
|
||
|
m_modified_channels = m_prepare_count = 0;
|
||
|
}
|
||
|
|
||
|
// 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 (bitfield(++m_env_counter, 0, 2) == RegisterType::EG_CLOCK_DIVIDER)
|
||
|
m_env_counter += 4 - RegisterType::EG_CLOCK_DIVIDER;
|
||
|
|
||
|
// clock the noise generator
|
||
|
int32_t lfo_raw_pm = m_regs.clock_noise_and_lfo();
|
||
|
|
||
|
// now update the state of all the channels and operators
|
||
|
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
|
||
|
if (bitfield(chanmask, chnum))
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// output - compute a sum over the relevant
|
||
|
// channels
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
void fm_engine_base<RegisterType>::output(output_data &output, uint32_t rshift, int32_t clipmax, uint32_t chanmask) const
|
||
|
{
|
||
|
// mask out some channels for debug purposes
|
||
|
chanmask &= debug::GLOBAL_FM_CHANNEL_MASK;
|
||
|
|
||
|
// mask out inactive channels
|
||
|
if (!YMFM_DEBUG_LOG_WAVFILES)
|
||
|
chanmask &= m_active_channels;
|
||
|
|
||
|
// 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
|
||
|
uint32_t op13phase = m_operator[13]->phase();
|
||
|
uint32_t op17phase = m_operator[17]->phase();
|
||
|
uint32_t phase_select = (bitfield(op13phase, 2) ^ bitfield(op13phase, 7)) | bitfield(op13phase, 3) | (bitfield(op17phase, 5) ^ bitfield(op17phase, 3));
|
||
|
|
||
|
// sum over all the desired channels
|
||
|
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
|
||
|
if (bitfield(chanmask, chnum))
|
||
|
{
|
||
|
#if (YMFM_DEBUG_LOG_WAVFILES)
|
||
|
auto reference = output;
|
||
|
#endif
|
||
|
if (chnum == 6)
|
||
|
m_channel[chnum]->output_rhythm_ch6(output, rshift, clipmax);
|
||
|
else if (chnum == 7)
|
||
|
m_channel[chnum]->output_rhythm_ch7(phase_select, output, rshift, clipmax);
|
||
|
else if (chnum == 8)
|
||
|
m_channel[chnum]->output_rhythm_ch8(phase_select, output, rshift, clipmax);
|
||
|
else if (m_channel[chnum]->is4op())
|
||
|
m_channel[chnum]->output_4op(output, rshift, clipmax);
|
||
|
else
|
||
|
m_channel[chnum]->output_2op(output, rshift, clipmax);
|
||
|
#if (YMFM_DEBUG_LOG_WAVFILES)
|
||
|
m_wavfile[chnum].add(output, reference);
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// sum over all the desired channels
|
||
|
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
|
||
|
if (bitfield(chanmask, chnum))
|
||
|
{
|
||
|
#if (YMFM_DEBUG_LOG_WAVFILES)
|
||
|
auto reference = output;
|
||
|
#endif
|
||
|
if (m_channel[chnum]->is4op())
|
||
|
m_channel[chnum]->output_4op(output, rshift, clipmax);
|
||
|
else
|
||
|
m_channel[chnum]->output_2op(output, rshift, clipmax);
|
||
|
#if (YMFM_DEBUG_LOG_WAVFILES)
|
||
|
m_wavfile[chnum].add(output, reference);
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// write - handle writes to the OPN registers
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
void fm_engine_base<RegisterType>::write(uint16_t regnum, uint8_t data)
|
||
|
{
|
||
|
debug::log_fm_write("%03X = %02X\n", regnum, data);
|
||
|
|
||
|
// special case: writes to the mode register can impact IRQs;
|
||
|
// schedule these writes to ensure ordering with timers
|
||
|
if (regnum == RegisterType::REG_MODE)
|
||
|
{
|
||
|
m_intf.ymfm_sync_mode_write(data);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// for now just mark all channels as modified
|
||
|
m_modified_channels = ALL_CHANNELS;
|
||
|
|
||
|
// most writes are passive, consumed only when needed
|
||
|
uint32_t keyon_channel;
|
||
|
uint32_t keyon_opmask;
|
||
|
if (m_regs.write(regnum, data, keyon_channel, keyon_opmask))
|
||
|
{
|
||
|
// handle writes to the keyon register(s)
|
||
|
if (keyon_channel < CHANNELS)
|
||
|
{
|
||
|
// normal channel on/off
|
||
|
m_channel[keyon_channel]->keyonoff(keyon_opmask, KEYON_NORMAL, keyon_channel);
|
||
|
}
|
||
|
else if (CHANNELS >= 9 && keyon_channel == RegisterType::RHYTHM_CHANNEL)
|
||
|
{
|
||
|
// special case for the OPL rhythm channels
|
||
|
m_channel[6]->keyonoff(bitfield(keyon_opmask, 4) ? 3 : 0, KEYON_RHYTHM, 6);
|
||
|
m_channel[7]->keyonoff(bitfield(keyon_opmask, 0) | (bitfield(keyon_opmask, 3) << 1), KEYON_RHYTHM, 7);
|
||
|
m_channel[8]->keyonoff(bitfield(keyon_opmask, 2) | (bitfield(keyon_opmask, 1) << 1), KEYON_RHYTHM, 8);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// status - return the current state of the
|
||
|
// status flags
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
uint8_t fm_engine_base<RegisterType>::status() const
|
||
|
{
|
||
|
return m_status & ~STATUS_BUSY & ~m_regs.status_mask();
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// assign_operators - get the current mapping of
|
||
|
// operators to channels and assign them all
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
void fm_engine_base<RegisterType>::assign_operators()
|
||
|
{
|
||
|
typename RegisterType::operator_mapping map;
|
||
|
m_regs.operator_map(map);
|
||
|
|
||
|
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
|
||
|
for (uint32_t index = 0; index < 4; index++)
|
||
|
{
|
||
|
uint32_t opnum = bitfield(map.chan[chnum], 8 * index, 8);
|
||
|
m_channel[chnum]->assign(index, (opnum == 0xff) ? nullptr : m_operator[opnum].get());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// update_timer - update the state of the given
|
||
|
// timer
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
void fm_engine_base<RegisterType>::update_timer(uint32_t tnum, uint32_t enable, int32_t delta_clocks)
|
||
|
{
|
||
|
// if the timer is live, but not currently enabled, set the timer
|
||
|
if (enable && !m_timer_running[tnum])
|
||
|
{
|
||
|
// period comes from the registers, and is different for each
|
||
|
uint32_t period = (tnum == 0) ? (1024 - m_regs.timer_a_value()) : 16 * (256 - m_regs.timer_b_value());
|
||
|
|
||
|
// caller can also specify a delta to account for other effects
|
||
|
period += delta_clocks;
|
||
|
|
||
|
// reset it
|
||
|
m_intf.ymfm_set_timer(tnum, period * OPERATORS * m_clock_prescale);
|
||
|
m_timer_running[tnum] = 1;
|
||
|
}
|
||
|
|
||
|
// if the timer is not live, ensure it is not enabled
|
||
|
else if (!enable)
|
||
|
{
|
||
|
m_intf.ymfm_set_timer(tnum, -1);
|
||
|
m_timer_running[tnum] = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// engine_timer_expired - timer has expired - signal
|
||
|
// status and possibly IRQs
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
void fm_engine_base<RegisterType>::engine_timer_expired(uint32_t tnum)
|
||
|
{
|
||
|
// update status
|
||
|
if (tnum == 0 && m_regs.enable_timer_a())
|
||
|
set_reset_status(STATUS_TIMERA, 0);
|
||
|
else if (tnum == 1 && m_regs.enable_timer_b())
|
||
|
set_reset_status(STATUS_TIMERB, 0);
|
||
|
|
||
|
// if timer A fired in CSM mode, trigger CSM on all relevant channels
|
||
|
if (tnum == 0 && m_regs.csm())
|
||
|
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
|
||
|
if (bitfield(RegisterType::CSM_TRIGGER_MASK, chnum))
|
||
|
{
|
||
|
m_channel[chnum]->keyonoff(1, KEYON_CSM, chnum);
|
||
|
m_modified_channels |= 1 << chnum;
|
||
|
}
|
||
|
|
||
|
// reset
|
||
|
m_timer_running[tnum] = false;
|
||
|
update_timer(tnum, 1, 0);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// check_interrupts - check the interrupt sources
|
||
|
// for interrupts
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
void fm_engine_base<RegisterType>::engine_check_interrupts()
|
||
|
{
|
||
|
// update the state
|
||
|
uint8_t old_state = m_irq_state;
|
||
|
m_irq_state = ((m_status & m_irq_mask & ~m_regs.status_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_intf.ymfm_update_irq(m_irq_state ? true : false);
|
||
|
}
|
||
|
|
||
|
|
||
|
//-------------------------------------------------
|
||
|
// engine_mode_write - handle a mode register write
|
||
|
// via timer callback
|
||
|
//-------------------------------------------------
|
||
|
|
||
|
template<class RegisterType>
|
||
|
void fm_engine_base<RegisterType>::engine_mode_write(uint8_t data)
|
||
|
{
|
||
|
// mark all channels as modified
|
||
|
m_modified_channels = ALL_CHANNELS;
|
||
|
|
||
|
// actually write the mode register now
|
||
|
uint32_t dummy1, dummy2;
|
||
|
m_regs.write(RegisterType::REG_MODE, data, 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
|
||
|
uint8_t 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; note that timer B gets a small negative adjustment because
|
||
|
// the *16 multiplier is free-running, so the first tick of the clock
|
||
|
// is a bit shorter
|
||
|
update_timer(1, m_regs.load_timer_b(), -(m_total_clocks & 15));
|
||
|
update_timer(0, m_regs.load_timer_a(), 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|