mirror of
https://github.com/libretro/blueMSX-libretro.git
synced 2024-11-27 02:30:41 +00:00
2343 lines
68 KiB
C++
Executable File
2343 lines
68 KiB
C++
Executable File
// This file is taken from the openMSX project.
|
|
// The file has been modified to be built in the blueMSX environment.
|
|
|
|
// $Id: OpenMsxYMF262.cpp,v 1.8 2009-07-18 15:08:35 dvik Exp $
|
|
|
|
/*
|
|
*
|
|
* File: ymf262.c - software implementation of YMF262
|
|
* FM sound generator type OPL3
|
|
*
|
|
* Copyright (C) 2003 Jarek Burczynski
|
|
*
|
|
* Version 0.2
|
|
*
|
|
*
|
|
* Revision History:
|
|
*
|
|
* 03-03-2003: initial release
|
|
* - thanks to Olivier Galibert and Chris Hardy for YMF262 and YAC512 chips
|
|
* - thanks to Stiletto for the datasheets
|
|
*
|
|
*
|
|
*
|
|
* differences between OPL2 and OPL3 not documented in Yamaha datahasheets:
|
|
* - sinus table is a little different: the negative part is off by one...
|
|
*
|
|
* - in order to enable selection of four different waveforms on OPL2
|
|
* one must set bit 5 in register 0x01(test).
|
|
* on OPL3 this bit is ignored and 4-waveform select works *always*.
|
|
* (Don't confuse this with OPL3's 8-waveform select.)
|
|
*
|
|
* - Envelope Generator: all 15 x rates take zero time on OPL3
|
|
* (on OPL2 15 0 and 15 1 rates take some time while 15 2 and 15 3 rates
|
|
* take zero time)
|
|
*
|
|
* - channel calculations: output of operator 1 is in perfect sync with
|
|
* output of operator 2 on OPL3; on OPL and OPL2 output of operator 1
|
|
* is always delayed by one sample compared to output of operator 2
|
|
*
|
|
*
|
|
* differences between OPL2 and OPL3 shown in datasheets:
|
|
* - YMF262 does not support CSM mode
|
|
*/
|
|
|
|
#include "OpenMsxYMF262.h"
|
|
#include <cmath>
|
|
#include <cstring>
|
|
|
|
|
|
extern "C" {
|
|
#include "SaveState.h"
|
|
#include "math.h"
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning( disable : 4355 )
|
|
#endif
|
|
|
|
const DoubleT PI = 3.14159265358979323846;
|
|
|
|
const int FREQ_SH = 16; // 16.16 fixed point (frequency calculations)
|
|
const int EG_SH = 16; // 16.16 fixed point (EG timing)
|
|
const int LFO_SH = 24; // 8.24 fixed point (LFO calculations)
|
|
const int TIMER_SH = 16; // 16.16 fixed point (timers calculations)
|
|
const int FREQ_MASK = (1 << FREQ_SH) - 1;
|
|
const unsigned int EG_TIMER_OVERFLOW = 1 << EG_SH;
|
|
|
|
// envelope output entries
|
|
const int ENV_BITS = 10;
|
|
const int ENV_LEN = 1 << ENV_BITS;
|
|
const DoubleT ENV_STEP = 128.0 / ENV_LEN;
|
|
|
|
const int MAX_ATT_INDEX = (1 << (ENV_BITS - 1)) - 1; //511
|
|
const int MIN_ATT_INDEX = 0;
|
|
|
|
// sinwave entries
|
|
const int SIN_BITS = 10;
|
|
const int SIN_LEN = 1 << SIN_BITS;
|
|
const int SIN_MASK = SIN_LEN - 1;
|
|
|
|
const int TL_RES_LEN = 256; // 8 bits addressing (real chip)
|
|
|
|
// register number to channel number , slot offset
|
|
const byte SLOT1 = 0;
|
|
const byte SLOT2 = 1;
|
|
|
|
// Envelope Generator phases
|
|
const int EG_ATT = 4;
|
|
const int EG_DEC = 3;
|
|
const int EG_SUS = 2;
|
|
const int EG_REL = 1;
|
|
const int EG_OFF = 0;
|
|
|
|
|
|
// mapping of register number (offset) to slot number used by the emulator
|
|
const int slot_array[32] =
|
|
{
|
|
0, 2, 4, 1, 3, 5, -1, -1,
|
|
6, 8, 10, 7, 9, 11, -1, -1,
|
|
12, 14, 16, 13, 15, 17, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1
|
|
};
|
|
|
|
// key scale level
|
|
// table is 3dB/octave , DV converts this into 6dB/octave
|
|
// 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale
|
|
#define DV(x) (int)(x / (0.1875/2.0))
|
|
const unsigned ksl_tab[8 * 16] =
|
|
{
|
|
// OCT 0
|
|
DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000),
|
|
DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000),
|
|
DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000),
|
|
DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000),
|
|
// OCT 1
|
|
DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000),
|
|
DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000),
|
|
DV( 0.000), DV( 0.750), DV( 1.125), DV( 1.500),
|
|
DV( 1.875), DV( 2.250), DV( 2.625), DV( 3.000),
|
|
// OCT 2
|
|
DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000),
|
|
DV( 0.000), DV( 1.125), DV( 1.875), DV( 2.625),
|
|
DV( 3.000), DV( 3.750), DV( 4.125), DV( 4.500),
|
|
DV( 4.875), DV( 5.250), DV( 5.625), DV( 6.000),
|
|
// OCT 3
|
|
DV( 0.000), DV( 0.000), DV( 0.000), DV( 1.875),
|
|
DV( 3.000), DV( 4.125), DV( 4.875), DV( 5.625),
|
|
DV( 6.000), DV( 6.750), DV( 7.125), DV( 7.500),
|
|
DV( 7.875), DV( 8.250), DV( 8.625), DV( 9.000),
|
|
// OCT 4
|
|
DV( 0.000), DV( 0.000), DV( 3.000), DV( 4.875),
|
|
DV( 6.000), DV( 7.125), DV( 7.875), DV( 8.625),
|
|
DV( 9.000), DV( 9.750), DV(10.125), DV(10.500),
|
|
DV(10.875), DV(11.250), DV(11.625), DV(12.000),
|
|
// OCT 5
|
|
DV( 0.000), DV( 3.000), DV( 6.000), DV( 7.875),
|
|
DV( 9.000), DV(10.125), DV(10.875), DV(11.625),
|
|
DV(12.000), DV(12.750), DV(13.125), DV(13.500),
|
|
DV(13.875), DV(14.250), DV(14.625), DV(15.000),
|
|
// OCT 6
|
|
DV( 0.000), DV( 6.000), DV( 9.000), DV(10.875),
|
|
DV(12.000), DV(13.125), DV(13.875), DV(14.625),
|
|
DV(15.000), DV(15.750), DV(16.125), DV(16.500),
|
|
DV(16.875), DV(17.250), DV(17.625), DV(18.000),
|
|
// OCT 7
|
|
DV( 0.000), DV( 9.000), DV(12.000), DV(13.875),
|
|
DV(15.000), DV(16.125), DV(16.875), DV(17.625),
|
|
DV(18.000), DV(18.750), DV(19.125), DV(19.500),
|
|
DV(19.875), DV(20.250), DV(20.625), DV(21.000)
|
|
};
|
|
#undef DV
|
|
|
|
// sustain level table (3dB per step)
|
|
// 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)
|
|
#define SC(db) (unsigned) (db * (2.0/ENV_STEP))
|
|
const unsigned sl_tab[16] = {
|
|
SC( 0), SC( 1), SC( 2), SC(3 ), SC(4 ), SC(5 ), SC(6 ), SC( 7),
|
|
SC( 8), SC( 9), SC(10), SC(11), SC(12), SC(13), SC(14), SC(31)
|
|
};
|
|
#undef SC
|
|
|
|
|
|
const byte RATE_STEPS = 8;
|
|
const byte eg_inc[15 * RATE_STEPS] =
|
|
{
|
|
//cycle:0 1 2 3 4 5 6 7
|
|
0,1, 0,1, 0,1, 0,1, // 0 rates 00..12 0 (increment by 0 or 1)
|
|
0,1, 0,1, 1,1, 0,1, // 1 rates 00..12 1
|
|
0,1, 1,1, 0,1, 1,1, // 2 rates 00..12 2
|
|
0,1, 1,1, 1,1, 1,1, // 3 rates 00..12 3
|
|
|
|
1,1, 1,1, 1,1, 1,1, // 4 rate 13 0 (increment by 1)
|
|
1,1, 1,2, 1,1, 1,2, // 5 rate 13 1
|
|
1,2, 1,2, 1,2, 1,2, // 6 rate 13 2
|
|
1,2, 2,2, 1,2, 2,2, // 7 rate 13 3
|
|
|
|
2,2, 2,2, 2,2, 2,2, // 8 rate 14 0 (increment by 2)
|
|
2,2, 2,4, 2,2, 2,4, // 9 rate 14 1
|
|
2,4, 2,4, 2,4, 2,4, // 10 rate 14 2
|
|
2,4, 4,4, 2,4, 4,4, // 11 rate 14 3
|
|
|
|
4,4, 4,4, 4,4, 4,4, // 12 rates 15 0, 15 1, 15 2, 15 3 for decay
|
|
8,8, 8,8, 8,8, 8,8, // 13 rates 15 0, 15 1, 15 2, 15 3 for attack (zero time)
|
|
0,0, 0,0, 0,0, 0,0, // 14 infinity rates for attack and decay(s)
|
|
};
|
|
|
|
|
|
#define O(a) (a*RATE_STEPS)
|
|
// note that there is no O(13) in this table - it's directly in the code
|
|
const byte eg_rate_select[16 + 64 + 16] =
|
|
{
|
|
// Envelope Generator rates (16 + 64 rates + 16 RKS)
|
|
// 16 infinite time rates
|
|
O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),
|
|
O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),
|
|
|
|
// rates 00-12
|
|
O( 0),O( 1),O( 2),O( 3),
|
|
O( 0),O( 1),O( 2),O( 3),
|
|
O( 0),O( 1),O( 2),O( 3),
|
|
O( 0),O( 1),O( 2),O( 3),
|
|
O( 0),O( 1),O( 2),O( 3),
|
|
O( 0),O( 1),O( 2),O( 3),
|
|
O( 0),O( 1),O( 2),O( 3),
|
|
O( 0),O( 1),O( 2),O( 3),
|
|
O( 0),O( 1),O( 2),O( 3),
|
|
O( 0),O( 1),O( 2),O( 3),
|
|
O( 0),O( 1),O( 2),O( 3),
|
|
O( 0),O( 1),O( 2),O( 3),
|
|
O( 0),O( 1),O( 2),O( 3),
|
|
|
|
// rate 13
|
|
O( 4),O( 5),O( 6),O( 7),
|
|
|
|
// rate 14
|
|
O( 8),O( 9),O(10),O(11),
|
|
|
|
// rate 15
|
|
O(12),O(12),O(12),O(12),
|
|
|
|
// 16 dummy rates (same as 15 3)
|
|
O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),
|
|
O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),
|
|
};
|
|
#undef O
|
|
|
|
//rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
|
|
//shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0
|
|
//mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0
|
|
#define O(a) (a*1)
|
|
const byte eg_rate_shift[16 + 64 + 16] =
|
|
{
|
|
// Envelope Generator counter shifts (16 + 64 rates + 16 RKS)
|
|
// 16 infinite time rates
|
|
O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
|
|
O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
|
|
|
|
// rates 00-15
|
|
O(12),O(12),O(12),O(12),
|
|
O(11),O(11),O(11),O(11),
|
|
O(10),O(10),O(10),O(10),
|
|
O( 9),O( 9),O( 9),O( 9),
|
|
O( 8),O( 8),O( 8),O( 8),
|
|
O( 7),O( 7),O( 7),O( 7),
|
|
O( 6),O( 6),O( 6),O( 6),
|
|
O( 5),O( 5),O( 5),O( 5),
|
|
O( 4),O( 4),O( 4),O( 4),
|
|
O( 3),O( 3),O( 3),O( 3),
|
|
O( 2),O( 2),O( 2),O( 2),
|
|
O( 1),O( 1),O( 1),O( 1),
|
|
O( 0),O( 0),O( 0),O( 0),
|
|
O( 0),O( 0),O( 0),O( 0),
|
|
O( 0),O( 0),O( 0),O( 0),
|
|
O( 0),O( 0),O( 0),O( 0),
|
|
|
|
// 16 dummy rates (same as 15 3)
|
|
O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
|
|
O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
|
|
};
|
|
#undef O
|
|
|
|
|
|
// multiple table
|
|
#define ML(x) (byte)(2 * x)
|
|
const byte mul_tab[16] =
|
|
{
|
|
// 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15
|
|
ML( 0.5),ML( 1.0),ML( 2.0),ML( 3.0),ML( 4.0),ML( 5.0),ML( 6.0),ML( 7.0),
|
|
ML( 8.0),ML( 9.0),ML(10.0),ML(10.0),ML(12.0),ML(12.0),ML(15.0),ML(15.0)
|
|
};
|
|
#undef ML
|
|
|
|
// TL_TAB_LEN is calculated as:
|
|
// (12+1)=13 - sinus amplitude bits (Y axis)
|
|
// additional 1: to compensate for calculations of negative part of waveform
|
|
// (if we don't add it then the greatest possible _negative_ value would be -2
|
|
// and we really need -1 for waveform #7)
|
|
// 2 - sinus sign bit (Y axis)
|
|
// TL_RES_LEN - sinus resolution (X axis)
|
|
|
|
const int TL_TAB_LEN = 13 * 2 * TL_RES_LEN;
|
|
static int tl_tab[TL_TAB_LEN];
|
|
const int ENV_QUIET = TL_TAB_LEN >> 4;
|
|
|
|
// sin waveform table in 'decibel' scale
|
|
// there are eight waveforms on OPL3 chips
|
|
static unsigned int sin_tab[SIN_LEN * 8];
|
|
|
|
|
|
// LFO Amplitude Modulation table (verified on real YM3812)
|
|
// 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples
|
|
//
|
|
// Length: 210 elements
|
|
//
|
|
// Each of the elements has to be repeated
|
|
// exactly 64 times (on 64 consecutive samples).
|
|
// The whole table takes: 64 * 210 = 13440 samples.
|
|
//
|
|
// When AM = 1 data is used directly
|
|
// When AM = 0 data is divided by 4 before being used (loosing precision is important)
|
|
|
|
const unsigned int LFO_AM_TAB_ELEMENTS = 210;
|
|
const byte lfo_am_table[LFO_AM_TAB_ELEMENTS] =
|
|
{
|
|
0,0,0,0,0,0,0,
|
|
1,1,1,1,
|
|
2,2,2,2,
|
|
3,3,3,3,
|
|
4,4,4,4,
|
|
5,5,5,5,
|
|
6,6,6,6,
|
|
7,7,7,7,
|
|
8,8,8,8,
|
|
9,9,9,9,
|
|
10,10,10,10,
|
|
11,11,11,11,
|
|
12,12,12,12,
|
|
13,13,13,13,
|
|
14,14,14,14,
|
|
15,15,15,15,
|
|
16,16,16,16,
|
|
17,17,17,17,
|
|
18,18,18,18,
|
|
19,19,19,19,
|
|
20,20,20,20,
|
|
21,21,21,21,
|
|
22,22,22,22,
|
|
23,23,23,23,
|
|
24,24,24,24,
|
|
25,25,25,25,
|
|
26,26,26,
|
|
25,25,25,25,
|
|
24,24,24,24,
|
|
23,23,23,23,
|
|
22,22,22,22,
|
|
21,21,21,21,
|
|
20,20,20,20,
|
|
19,19,19,19,
|
|
18,18,18,18,
|
|
17,17,17,17,
|
|
16,16,16,16,
|
|
15,15,15,15,
|
|
14,14,14,14,
|
|
13,13,13,13,
|
|
12,12,12,12,
|
|
11,11,11,11,
|
|
10,10,10,10,
|
|
9,9,9,9,
|
|
8,8,8,8,
|
|
7,7,7,7,
|
|
6,6,6,6,
|
|
5,5,5,5,
|
|
4,4,4,4,
|
|
3,3,3,3,
|
|
2,2,2,2,
|
|
1,1,1,1
|
|
};
|
|
|
|
// LFO Phase Modulation table (verified on real YM3812)
|
|
const int lfo_pm_table[8 * 8 * 2] =
|
|
{
|
|
// FNUM2/FNUM = 00 0xxxxxxx (0x0000)
|
|
0, 0, 0, 0, 0, 0, 0, 0, //LFO PM depth = 0
|
|
0, 0, 0, 0, 0, 0, 0, 0, //LFO PM depth = 1
|
|
|
|
// FNUM2/FNUM = 00 1xxxxxxx (0x0080)
|
|
0, 0, 0, 0, 0, 0, 0, 0, //LFO PM depth = 0
|
|
1, 0, 0, 0,-1, 0, 0, 0, //LFO PM depth = 1
|
|
|
|
// FNUM2/FNUM = 01 0xxxxxxx (0x0100)
|
|
1, 0, 0, 0,-1, 0, 0, 0, //LFO PM depth = 0
|
|
2, 1, 0,-1,-2,-1, 0, 1, //LFO PM depth = 1
|
|
|
|
// FNUM2/FNUM = 01 1xxxxxxx (0x0180)
|
|
1, 0, 0, 0,-1, 0, 0, 0, //LFO PM depth = 0
|
|
3, 1, 0,-1,-3,-1, 0, 1, //LFO PM depth = 1
|
|
|
|
// FNUM2/FNUM = 10 0xxxxxxx (0x0200)
|
|
2, 1, 0,-1,-2,-1, 0, 1, //LFO PM depth = 0
|
|
4, 2, 0,-2,-4,-2, 0, 2, //LFO PM depth = 1
|
|
|
|
// FNUM2/FNUM = 10 1xxxxxxx (0x0280)
|
|
2, 1, 0,-1,-2,-1, 0, 1, //LFO PM depth = 0
|
|
5, 2, 0,-2,-5,-2, 0, 2, //LFO PM depth = 1
|
|
|
|
// FNUM2/FNUM = 11 0xxxxxxx (0x0300)
|
|
3, 1, 0,-1,-3,-1, 0, 1, //LFO PM depth = 0
|
|
6, 3, 0,-3,-6,-3, 0, 3, //LFO PM depth = 1
|
|
|
|
// FNUM2/FNUM = 11 1xxxxxxx (0x0380)
|
|
3, 1, 0,-1,-3,-1, 0, 1, //LFO PM depth = 0
|
|
7, 3, 0,-3,-7,-3, 0, 3 //LFO PM depth = 1
|
|
};
|
|
|
|
|
|
static int* chanOut;
|
|
#define PHASE_MOD1 18
|
|
#define PHASE_MOD2 19
|
|
|
|
|
|
|
|
YMF262Slot::YMF262Slot()
|
|
{
|
|
ar = dr = rr = KSR = ksl = ksr = mul = 0;
|
|
Cnt = Incr = FB = op1_out[0] = op1_out[1] = CON = 0;
|
|
connect = 0;
|
|
eg_type = state = TL = TLL = volume = sl = 0;
|
|
eg_m_ar = eg_sh_ar = eg_sel_ar = eg_m_dr = eg_sh_dr = 0;
|
|
eg_sel_dr = eg_m_rr = eg_sh_rr = eg_sel_rr = 0;
|
|
key = AMmask = vib = waveform_number = wavetable = 0;
|
|
}
|
|
|
|
YMF262Channel::YMF262Channel()
|
|
{
|
|
block_fnum = fc = ksl_base = kcode = extended = 0;
|
|
}
|
|
|
|
|
|
void YMF262::callback(byte flag)
|
|
{
|
|
setStatus(flag);
|
|
}
|
|
|
|
// status set and IRQ handling
|
|
void YMF262::setStatus(byte flag)
|
|
{
|
|
// set status flag masking out disabled IRQs
|
|
status |= flag;
|
|
if (status & statusMask) {
|
|
status |= 0x80;
|
|
irq.set();
|
|
}
|
|
}
|
|
|
|
// status reset and IRQ handling
|
|
void YMF262::resetStatus(byte flag)
|
|
{
|
|
// reset status flag
|
|
status &= ~flag;
|
|
if (!(status & statusMask)) {
|
|
status &= 0x7F;
|
|
irq.reset();
|
|
}
|
|
}
|
|
|
|
// IRQ mask set
|
|
void YMF262::changeStatusMask(byte flag)
|
|
{
|
|
statusMask = flag;
|
|
status &= statusMask;
|
|
if (status) {
|
|
status |= 0x80;
|
|
irq.set();
|
|
} else {
|
|
status &= 0x7F;
|
|
irq.reset();
|
|
}
|
|
}
|
|
|
|
|
|
// advance LFO to next sample
|
|
void YMF262::advance_lfo()
|
|
{
|
|
// LFO
|
|
lfo_am_cnt += lfo_am_inc;
|
|
if (lfo_am_cnt >= (LFO_AM_TAB_ELEMENTS << LFO_SH)) {
|
|
// lfo_am_table is 210 elements long
|
|
lfo_am_cnt -= (LFO_AM_TAB_ELEMENTS << LFO_SH);
|
|
}
|
|
|
|
byte tmp = lfo_am_table[lfo_am_cnt >> LFO_SH];
|
|
if (lfo_am_depth) {
|
|
LFO_AM = tmp;
|
|
} else {
|
|
LFO_AM = tmp >> 2;
|
|
}
|
|
lfo_pm_cnt += lfo_pm_inc;
|
|
LFO_PM = ((lfo_pm_cnt >> LFO_SH) & 7) | lfo_pm_depth_range;
|
|
}
|
|
|
|
// advance to next sample
|
|
void YMF262::advance()
|
|
{
|
|
eg_timer += eg_timer_add;
|
|
|
|
if (eg_timer > 4 * EG_TIMER_OVERFLOW) {
|
|
eg_timer = EG_TIMER_OVERFLOW;
|
|
}
|
|
while (eg_timer >= EG_TIMER_OVERFLOW) {
|
|
eg_timer -= EG_TIMER_OVERFLOW;
|
|
eg_cnt++;
|
|
|
|
for (int i = 0; i < 18 * 2; i++) {
|
|
YMF262Channel &ch = channels[i / 2];
|
|
YMF262Slot &op = ch.slots[i & 1];
|
|
// Envelope Generator
|
|
switch(op.state) {
|
|
case EG_ATT: // attack phase
|
|
if (!(eg_cnt & op.eg_m_ar)) {
|
|
op.volume += (~op.volume * eg_inc[op.eg_sel_ar + ((eg_cnt >> op.eg_sh_ar) & 7)]) >> 3;
|
|
if (op.volume <= MIN_ATT_INDEX) {
|
|
op.volume = MIN_ATT_INDEX;
|
|
op.state = EG_DEC;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EG_DEC: // decay phase
|
|
if (!(eg_cnt & op.eg_m_dr)) {
|
|
op.volume += eg_inc[op.eg_sel_dr + ((eg_cnt >> op.eg_sh_dr) & 7)];
|
|
if (op.volume >= op.sl) {
|
|
op.state = EG_SUS;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EG_SUS: // sustain phase
|
|
// this is important behaviour:
|
|
// one can change percusive/non-percussive
|
|
// modes on the fly and the chip will remain
|
|
// in sustain phase - verified on real YM3812
|
|
if (op.eg_type) {
|
|
// non-percussive mode
|
|
// do nothing
|
|
} else {
|
|
// percussive mode
|
|
// during sustain phase chip adds Release Rate (in percussive mode)
|
|
if (!(eg_cnt & op.eg_m_rr)) {
|
|
op.volume += eg_inc[op.eg_sel_rr + ((eg_cnt>>op.eg_sh_rr) & 7)];
|
|
if (op.volume >= MAX_ATT_INDEX) {
|
|
op.volume = MAX_ATT_INDEX;
|
|
}
|
|
} else {
|
|
// do nothing in sustain phase
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EG_REL: // release phase
|
|
if (!(eg_cnt & op.eg_m_rr)) {
|
|
op.volume += eg_inc[op.eg_sel_rr + ((eg_cnt>>op.eg_sh_rr) & 7)];
|
|
if (op.volume >= MAX_ATT_INDEX) {
|
|
op.volume = MAX_ATT_INDEX;
|
|
op.state = EG_OFF;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
int i;
|
|
for (i = 0; i < 18 * 2; i++) {
|
|
YMF262Channel &ch = channels[i / 2];
|
|
YMF262Slot &op = ch.slots[i & 1];
|
|
|
|
// Phase Generator
|
|
if (op.vib) {
|
|
byte block;
|
|
unsigned int block_fnum = ch.block_fnum;
|
|
unsigned int fnum_lfo = (block_fnum & 0x0380) >> 7;
|
|
signed int lfo_fn_table_index_offset = lfo_pm_table[LFO_PM + 16 * fnum_lfo];
|
|
|
|
if (lfo_fn_table_index_offset) {
|
|
// LFO phase modulation active
|
|
block_fnum += lfo_fn_table_index_offset;
|
|
block = (block_fnum & 0x1c00) >> 10;
|
|
op.Cnt += (fn_tab[block_fnum & 0x03ff] >> (7 - block)) * op.mul;
|
|
} else {
|
|
// LFO phase modulation = zero
|
|
op.Cnt += op.Incr;
|
|
}
|
|
} else {
|
|
// LFO phase modulation disabled for this operator
|
|
op.Cnt += op.Incr;
|
|
}
|
|
}
|
|
|
|
// The Noise Generator of the YM3812 is 23-bit shift register.
|
|
// Period is equal to 2^23-2 samples.
|
|
// Register works at sampling frequency of the chip, so output
|
|
// can change on every sample.
|
|
//
|
|
// Output of the register and input to the bit 22 is:
|
|
// bit0 XOR bit14 XOR bit15 XOR bit22
|
|
//
|
|
// Simply use bit 22 as the noise output.
|
|
noise_p += noise_f;
|
|
i = (noise_p >> FREQ_SH) & 0x1f; // number of events (shifts of the shift register)
|
|
noise_p &= FREQ_MASK;
|
|
while (i--) {
|
|
// unsigned j = ( (noise_rng) ^ (noise_rng>>14) ^ (noise_rng>>15) ^ (noise_rng>>22) ) & 1;
|
|
// noise_rng = (j<<22) | (noise_rng>>1);
|
|
//
|
|
// Instead of doing all the logic operations above, we
|
|
// use a trick here (and use bit 0 as the noise output).
|
|
// The difference is only that the noise bit changes one
|
|
// step ahead. This doesn't matter since we don't know
|
|
// what is real state of the noise_rng after the reset.
|
|
|
|
if (noise_rng & 1) {
|
|
noise_rng ^= 0x800302;
|
|
}
|
|
noise_rng >>= 1;
|
|
}
|
|
}
|
|
|
|
|
|
signed int op_calc(unsigned phase, unsigned env, signed int pm, unsigned int wave_tab)
|
|
{
|
|
int i = (phase & ~FREQ_MASK) + (pm << 16);
|
|
int p = (env << 4) + sin_tab[wave_tab + ((i >> FREQ_SH ) & SIN_MASK)];
|
|
if (p >= TL_TAB_LEN) {
|
|
return 0;
|
|
}
|
|
return tl_tab[p];
|
|
}
|
|
|
|
signed int op_calc1(unsigned phase, unsigned int env, signed int pm, unsigned int wave_tab)
|
|
{
|
|
int i = (phase & ~FREQ_MASK) + pm;
|
|
int p = (env << 4) + sin_tab[wave_tab + ((i >> FREQ_SH) & SIN_MASK)];
|
|
if (p >= TL_TAB_LEN) {
|
|
return 0;
|
|
}
|
|
return tl_tab[p];
|
|
}
|
|
|
|
inline int YMF262Slot::volume_calc(byte LFO_AM)
|
|
{
|
|
return TLL + volume + (LFO_AM & AMmask);
|
|
}
|
|
|
|
// calculate output of a standard 2 operator channel
|
|
// (or 1st part of a 4-op channel)
|
|
void YMF262Channel::chan_calc(byte LFO_AM)
|
|
{
|
|
chanOut[PHASE_MOD1] = 0;
|
|
chanOut[PHASE_MOD2] = 0;
|
|
chanOut[PHASE_MOD1] = 0;
|
|
chanOut[PHASE_MOD2] = 0;
|
|
|
|
// SLOT 1
|
|
int env = slots[SLOT1].volume_calc(LFO_AM);
|
|
int out = slots[SLOT1].op1_out[0] + slots[SLOT1].op1_out[1];
|
|
slots[SLOT1].op1_out[0] = slots[SLOT1].op1_out[1];
|
|
slots[SLOT1].op1_out[1] = 0;
|
|
if (env < ENV_QUIET) {
|
|
if (!slots[SLOT1].FB) {
|
|
out = 0;
|
|
}
|
|
slots[SLOT1].op1_out[1] = op_calc1(slots[SLOT1].Cnt, env, (out<<slots[SLOT1].FB), slots[SLOT1].wavetable);
|
|
}
|
|
chanOut[slots[SLOT1].connect] += slots[SLOT1].op1_out[1];
|
|
|
|
// SLOT 2
|
|
env = slots[SLOT2].volume_calc(LFO_AM);
|
|
if (env < ENV_QUIET) {
|
|
chanOut[slots[SLOT2].connect] += op_calc(slots[SLOT2].Cnt, env, chanOut[PHASE_MOD1], slots[SLOT2].wavetable);
|
|
}
|
|
}
|
|
|
|
// calculate output of a 2nd part of 4-op channel
|
|
void YMF262Channel::chan_calc_ext(byte LFO_AM)
|
|
{
|
|
chanOut[PHASE_MOD1] = 0;
|
|
|
|
// SLOT 1
|
|
int env = slots[SLOT1].volume_calc(LFO_AM);
|
|
if (env < ENV_QUIET) {
|
|
chanOut[slots[SLOT1].connect] += op_calc(slots[SLOT1].Cnt, env, chanOut[PHASE_MOD2], slots[SLOT1].wavetable );
|
|
}
|
|
|
|
// SLOT 2
|
|
env = slots[SLOT2].volume_calc(LFO_AM);
|
|
if (env < ENV_QUIET) {
|
|
chanOut[slots[SLOT2].connect] += op_calc(slots[SLOT2].Cnt, env, chanOut[PHASE_MOD1], slots[SLOT2].wavetable);
|
|
}
|
|
}
|
|
|
|
// operators used in the rhythm sounds generation process:
|
|
//
|
|
// Envelope Generator:
|
|
//
|
|
// channel operator register number Bass High Snare Tom Top
|
|
// / slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal
|
|
// 6 / 0 12 50 70 90 f0 +
|
|
// 6 / 1 15 53 73 93 f3 +
|
|
// 7 / 0 13 51 71 91 f1 +
|
|
// 7 / 1 16 54 74 94 f4 +
|
|
// 8 / 0 14 52 72 92 f2 +
|
|
// 8 / 1 17 55 75 95 f5 +
|
|
//
|
|
// Phase Generator:
|
|
//
|
|
// channel operator register number Bass High Snare Tom Top
|
|
// / slot number MULTIPLE Drum Hat Drum Tom Cymbal
|
|
// 6 / 0 12 30 +
|
|
// 6 / 1 15 33 +
|
|
// 7 / 0 13 31 + + +
|
|
// 7 / 1 16 34 ----- n o t u s e d -----
|
|
// 8 / 0 14 32 +
|
|
// 8 / 1 17 35 + +
|
|
//
|
|
// channel operator register number Bass High Snare Tom Top
|
|
// number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal
|
|
// 6 12,15 B6 A6 +
|
|
//
|
|
// 7 13,16 B7 A7 + + +
|
|
//
|
|
// 8 14,17 B8 A8 + + +
|
|
|
|
// calculate rhythm
|
|
void YMF262::chan_calc_rhythm(bool noise)
|
|
{
|
|
YMF262Slot& SLOT6_1 = channels[6].slots[SLOT1];
|
|
YMF262Slot& SLOT6_2 = channels[6].slots[SLOT2];
|
|
YMF262Slot& SLOT7_1 = channels[7].slots[SLOT1];
|
|
YMF262Slot& SLOT7_2 = channels[7].slots[SLOT2];
|
|
YMF262Slot& SLOT8_1 = channels[8].slots[SLOT1];
|
|
YMF262Slot& SLOT8_2 = channels[8].slots[SLOT2];
|
|
|
|
// Bass Drum (verified on real YM3812):
|
|
// - depends on the channel 6 'connect' register:
|
|
// when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out)
|
|
// when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored
|
|
// - output sample always is multiplied by 2
|
|
|
|
chanOut[PHASE_MOD1] = 0;
|
|
|
|
// SLOT 1
|
|
int env = SLOT6_1.volume_calc(LFO_AM);
|
|
int out = SLOT6_1.op1_out[0] + SLOT6_1.op1_out[1];
|
|
SLOT6_1.op1_out[0] = SLOT6_1.op1_out[1];
|
|
|
|
if (!SLOT6_1.CON) {
|
|
chanOut[PHASE_MOD1] = SLOT6_1.op1_out[0];
|
|
} else {
|
|
// ignore output of operator 1
|
|
}
|
|
|
|
SLOT6_1.op1_out[1] = 0;
|
|
if (env < ENV_QUIET) {
|
|
if (!SLOT6_1.FB) {
|
|
out = 0;
|
|
}
|
|
SLOT6_1.op1_out[1] = op_calc1(SLOT6_1.Cnt, env, (out << SLOT6_1.FB), SLOT6_1.wavetable);
|
|
}
|
|
|
|
// SLOT 2
|
|
env = SLOT6_2.volume_calc(LFO_AM);
|
|
if (env < ENV_QUIET) {
|
|
chanout[6] += op_calc(SLOT6_2.Cnt, env, chanOut[PHASE_MOD1], SLOT6_2.wavetable) * 2;
|
|
}
|
|
|
|
// Phase generation is based on:
|
|
// HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases)
|
|
// SD (16) channel 7->slot 1
|
|
// TOM (14) channel 8->slot 1
|
|
// TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases)
|
|
|
|
// Envelope generation based on:
|
|
// HH channel 7->slot1
|
|
// SD channel 7->slot2
|
|
// TOM channel 8->slot1
|
|
// TOP channel 8->slot2
|
|
|
|
// The following formulas can be well optimized.
|
|
// I leave them in direct form for now (in case I've missed something).
|
|
|
|
// High Hat (verified on real YM3812)
|
|
env = SLOT7_1.volume_calc(LFO_AM);
|
|
if (env < ENV_QUIET) {
|
|
// high hat phase generation:
|
|
// phase = d0 or 234 (based on frequency only)
|
|
// phase = 34 or 2d0 (based on noise)
|
|
|
|
// base frequency derived from operator 1 in channel 7
|
|
bool bit7 = ((SLOT7_1.Cnt >> FREQ_SH) & 0x80) != 0;
|
|
bool bit3 = ((SLOT7_1.Cnt >> FREQ_SH) & 0x08) != 0;
|
|
bool bit2 = ((SLOT7_1.Cnt >> FREQ_SH) & 0x04) != 0;
|
|
bool res1 = ((bit2 ^ bit7) | bit3) != 0;
|
|
// when res1 = 0 phase = 0x000 | 0xd0;
|
|
// when res1 = 1 phase = 0x200 | (0xd0>>2);
|
|
unsigned phase = res1 ? (0x200|(0xd0>>2)) : 0xd0;
|
|
|
|
// enable gate based on frequency of operator 2 in channel 8
|
|
bool bit5e= ((SLOT8_2.Cnt>>FREQ_SH) & 0x20) != 0;
|
|
bool bit3e= ((SLOT8_2.Cnt>>FREQ_SH) & 0x08) != 0;
|
|
bool res2 = (bit3e ^ bit5e) != 0;
|
|
// when res2 = 0 pass the phase from calculation above (res1);
|
|
// when res2 = 1 phase = 0x200 | (0xd0>>2);
|
|
if (res2) {
|
|
phase = (0x200|(0xd0>>2));
|
|
}
|
|
|
|
// when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0
|
|
// when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change
|
|
if (phase&0x200) {
|
|
if (noise) {
|
|
phase = 0x200|0xd0;
|
|
}
|
|
} else {
|
|
// when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2
|
|
// when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change
|
|
if (noise) {
|
|
phase = 0xd0>>2;
|
|
}
|
|
}
|
|
chanout[7] += op_calc(phase<<FREQ_SH, env, 0, SLOT7_1.wavetable) * 2;
|
|
}
|
|
|
|
// Snare Drum (verified on real YM3812)
|
|
env = SLOT7_2.volume_calc(LFO_AM);
|
|
if (env < ENV_QUIET) {
|
|
// base frequency derived from operator 1 in channel 7
|
|
bool bit8 = ((SLOT7_1.Cnt>>FREQ_SH) & 0x100) != 0;
|
|
// when bit8 = 0 phase = 0x100;
|
|
// when bit8 = 1 phase = 0x200;
|
|
unsigned phase = bit8 ? 0x200 : 0x100;
|
|
|
|
// Noise bit XOR'es phase by 0x100
|
|
// when noisebit = 0 pass the phase from calculation above
|
|
// when noisebit = 1 phase ^= 0x100;
|
|
// in other words: phase ^= (noisebit<<8);
|
|
if (noise) {
|
|
phase ^= 0x100;
|
|
}
|
|
chanout[7] += op_calc(phase<<FREQ_SH, env, 0, SLOT7_2.wavetable) * 2;
|
|
}
|
|
|
|
// Tom Tom (verified on real YM3812)
|
|
env = SLOT8_1.volume_calc(LFO_AM);
|
|
if (env < ENV_QUIET) {
|
|
chanout[8] += op_calc(SLOT8_1.Cnt, env, 0, SLOT8_1.wavetable) * 2;
|
|
}
|
|
|
|
// Top Cymbal (verified on real YM3812)
|
|
env = SLOT8_2.volume_calc(LFO_AM);
|
|
if (env < ENV_QUIET) {
|
|
// base frequency derived from operator 1 in channel 7
|
|
bool bit7 = ((SLOT7_1.Cnt>>FREQ_SH) & 0x80) != 0;
|
|
bool bit3 = ((SLOT7_1.Cnt>>FREQ_SH) & 0x08) != 0;
|
|
bool bit2 = ((SLOT7_1.Cnt>>FREQ_SH) & 0x04) != 0;
|
|
bool res1 = ((bit2 ^ bit7) | bit3) != 0;
|
|
// when res1 = 0 phase = 0x000 | 0x100;
|
|
// when res1 = 1 phase = 0x200 | 0x100;
|
|
unsigned phase = res1 ? 0x300 : 0x100;
|
|
|
|
// enable gate based on frequency of operator 2 in channel 8
|
|
bool bit5e= ((SLOT8_2.Cnt>>FREQ_SH) & 0x20) != 0;
|
|
bool bit3e= ((SLOT8_2.Cnt>>FREQ_SH) & 0x08) != 0;
|
|
bool res2 = (bit3e ^ bit5e) != 0;
|
|
// when res2 = 0 pass the phase from calculation above (res1);
|
|
// when res2 = 1 phase = 0x200 | 0x100;
|
|
if (res2) {
|
|
phase = 0x300;
|
|
}
|
|
chanout[8] += op_calc(phase<<FREQ_SH, env, 0, SLOT8_2.wavetable) * 2;
|
|
}
|
|
}
|
|
|
|
|
|
// generic table initialize
|
|
void YMF262::init_tables(void)
|
|
{
|
|
int i;
|
|
static bool alreadyInit = false;
|
|
if (alreadyInit) {
|
|
return;
|
|
}
|
|
alreadyInit = true;
|
|
|
|
for (int x = 0; x < TL_RES_LEN; x++) {
|
|
DoubleT m = (1 << 16) / powf((DoubleT)2, (x + 1) * (ENV_STEP / 4.0) / 8.0);
|
|
m = floor(m);
|
|
|
|
// we never reach (1<<16) here due to the (x+1)
|
|
// result fits within 16 bits at maximum
|
|
int n = (int)m; // 16 bits here
|
|
n >>= 4; // 12 bits here
|
|
if (n & 1) { // round to nearest
|
|
n = (n >> 1) + 1;
|
|
} else {
|
|
n = n >> 1;
|
|
}
|
|
// 11 bits here (rounded)
|
|
n <<= 1; // 12 bits here (as in real chip)
|
|
tl_tab[x * 2 + 0] = n;
|
|
tl_tab[x * 2 + 1] = ~tl_tab[x * 2 + 0]; // this _is_ different from OPL2 (verified on real YMF262)
|
|
|
|
for (i = 1; i < 13; i++) {
|
|
tl_tab[x * 2 + 0 + i * 2 * TL_RES_LEN] = tl_tab[x * 2 + 0] >> i;
|
|
tl_tab[x * 2 + 1 + i * 2 * TL_RES_LEN] = ~tl_tab[x * 2 + 0 + i * 2 * TL_RES_LEN]; // this _is_ different from OPL2 (verified on real YMF262)
|
|
}
|
|
}
|
|
|
|
const DoubleT LOG2 = ::log((DoubleT)2);
|
|
for (i = 0; i < SIN_LEN; i++) {
|
|
// non-standard sinus
|
|
DoubleT m = sin(((i * 2) + 1) * PI / SIN_LEN); // checked against the real chip
|
|
// we never reach zero here due to ((i * 2) + 1)
|
|
DoubleT o = (m > 0.0) ?
|
|
8 * ::log( 1.0 / m) / LOG2: // convert to 'decibels'
|
|
8 * ::log(-1.0 / m) / LOG2; // convert to 'decibels'
|
|
o = o / (ENV_STEP / 4);
|
|
|
|
int n = (int)(2 * o);
|
|
if (n & 1) {// round to nearest
|
|
n = (n>>1)+1;
|
|
} else {
|
|
n = n>>1;
|
|
}
|
|
sin_tab[i] = n * 2 + (m >=0.0 ? 0 : 1);
|
|
}
|
|
|
|
for (i = 0; i < SIN_LEN; i++) {
|
|
// these 'pictures' represent _two_ cycles
|
|
// waveform 1: __ __
|
|
// / \____/ \____
|
|
// output only first half of the sinus waveform (positive one)
|
|
if (i & (1 << (SIN_BITS - 1))) {
|
|
sin_tab[1*SIN_LEN+i] = TL_TAB_LEN;
|
|
} else {
|
|
sin_tab[1*SIN_LEN+i] = sin_tab[i];
|
|
}
|
|
|
|
// waveform 2: __ __ __ __
|
|
// / \/ \/ \/ \.
|
|
// abs(sin)
|
|
sin_tab[2 * SIN_LEN + i] = sin_tab[i & (SIN_MASK >> 1)];
|
|
|
|
// waveform 3: _ _ _ _
|
|
// / |_/ |_/ |_/ |_
|
|
// abs(output only first quarter of the sinus waveform)
|
|
if (i & (1<<(SIN_BITS-2))) {
|
|
sin_tab[3*SIN_LEN+i] = TL_TAB_LEN;
|
|
} else {
|
|
sin_tab[3*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>2)];
|
|
}
|
|
|
|
// waveform 4:
|
|
// /\ ____/\ ____
|
|
// \/ \/
|
|
// output whole sinus waveform in half the cycle(step=2) and output 0 on the other half of cycle
|
|
if (i & (1 << (SIN_BITS-1))) {
|
|
sin_tab[4*SIN_LEN+i] = TL_TAB_LEN;
|
|
} else {
|
|
sin_tab[4*SIN_LEN+i] = sin_tab[i*2];
|
|
}
|
|
|
|
// waveform 5:
|
|
// /\/\____/\/\____
|
|
//
|
|
// output abs(whole sinus) waveform in half the cycle(step=2) and output 0 on the other half of cycle
|
|
if (i & (1 << (SIN_BITS-1))) {
|
|
sin_tab[5*SIN_LEN+i] = TL_TAB_LEN;
|
|
} else {
|
|
sin_tab[5*SIN_LEN+i] = sin_tab[(i*2) & (SIN_MASK>>1)];
|
|
}
|
|
|
|
// waveform 6: ____ ____
|
|
//
|
|
// ____ ____
|
|
// output maximum in half the cycle and output minimum on the other half of cycle
|
|
if (i & (1 << (SIN_BITS - 1))) {
|
|
sin_tab[6*SIN_LEN+i] = 1; // negative
|
|
} else {
|
|
sin_tab[6*SIN_LEN+i] = 0; // positive
|
|
}
|
|
|
|
// waveform 7:
|
|
// |\____ |\____
|
|
// \| \|
|
|
// output sawtooth waveform
|
|
int x = (i & (1 << (SIN_BITS - 1))) ?
|
|
((SIN_LEN - 1) - i) * 16 + 1 : // negative: from 8177 to 1
|
|
i * 16; //positive: from 0 to 8176
|
|
if (x > TL_TAB_LEN) {
|
|
x = TL_TAB_LEN; // clip to the allowed range
|
|
}
|
|
sin_tab[7 * SIN_LEN+i] = x;
|
|
}
|
|
}
|
|
|
|
|
|
void YMF262::setSampleRate(int sampleRate, int Oversampling)
|
|
{
|
|
oplOversampling = Oversampling;
|
|
const int CLCK_FREQ = 14318180;
|
|
DoubleT freqbase = ((DoubleT)CLCK_FREQ / (8.0 * 36)) / (DoubleT)(sampleRate * oplOversampling);
|
|
|
|
// make fnumber -> increment counter table
|
|
for (int i = 0; i < 1024; i++) {
|
|
// opn phase increment counter = 20bit
|
|
// -10 because chip works with 10.10 fixed point, while we use 16.16
|
|
fn_tab[i] = (unsigned)( (DoubleT)i * 64 * freqbase * (1<<(FREQ_SH - 10)));
|
|
}
|
|
|
|
// Amplitude modulation: 27 output levels (triangle waveform);
|
|
// 1 level takes one of: 192, 256 or 448 samples
|
|
// One entry from LFO_AM_TABLE lasts for 64 samples
|
|
lfo_am_inc = (unsigned)((1 << LFO_SH) * freqbase / 64.0);
|
|
|
|
// Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples
|
|
lfo_pm_inc = (unsigned)((1 << LFO_SH) * freqbase / 1024.0);
|
|
|
|
// Noise generator: a step takes 1 sample
|
|
noise_f = (unsigned)((1 << FREQ_SH) * freqbase);
|
|
|
|
eg_timer_add = (unsigned)((1 << EG_SH) * freqbase);
|
|
}
|
|
|
|
void YMF262Slot::FM_KEYON(byte key_set)
|
|
{
|
|
if (!key) {
|
|
// restart Phase Generator
|
|
Cnt = 0;
|
|
// phase -> Attack
|
|
state = EG_ATT;
|
|
}
|
|
key |= key_set;
|
|
}
|
|
|
|
void YMF262Slot::FM_KEYOFF(byte key_clr)
|
|
{
|
|
if (key) {
|
|
key &= key_clr;
|
|
if (!key) {
|
|
// phase -> Release
|
|
if (state > EG_REL) {
|
|
state = EG_REL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// update phase increment counter of operator (also update the EG rates if necessary)
|
|
void YMF262Channel::CALC_FCSLOT(YMF262Slot &slot)
|
|
{
|
|
// (frequency) phase increment counter
|
|
slot.Incr = fc * slot.mul;
|
|
int ksr = kcode >> slot.KSR;
|
|
|
|
if (slot.ksr != ksr) {
|
|
slot.ksr = ksr;
|
|
|
|
// calculate envelope generator rates
|
|
if ((slot.ar + slot.ksr) < 16+60) {
|
|
slot.eg_sh_ar = eg_rate_shift [slot.ar + slot.ksr ];
|
|
slot.eg_m_ar = (1 << slot.eg_sh_ar) - 1;
|
|
slot.eg_sel_ar = eg_rate_select[slot.ar + slot.ksr ];
|
|
} else {
|
|
slot.eg_sh_ar = 0;
|
|
slot.eg_m_ar = (1 << slot.eg_sh_ar) - 1;
|
|
slot.eg_sel_ar = 13 * RATE_STEPS;
|
|
}
|
|
slot.eg_sh_dr = eg_rate_shift [slot.dr + slot.ksr ];
|
|
slot.eg_m_dr = (1 << slot.eg_sh_dr) - 1;
|
|
slot.eg_sel_dr = eg_rate_select[slot.dr + slot.ksr ];
|
|
slot.eg_sh_rr = eg_rate_shift [slot.rr + slot.ksr ];
|
|
slot.eg_m_rr = (1 << slot.eg_sh_rr) - 1;
|
|
slot.eg_sel_rr = eg_rate_select[slot.rr + slot.ksr ];
|
|
}
|
|
}
|
|
|
|
// set multi,am,vib,EG-TYP,KSR,mul
|
|
void YMF262::set_mul(byte sl, byte v)
|
|
{
|
|
int chan_no = sl / 2;
|
|
YMF262Channel &ch = channels[chan_no];
|
|
YMF262Slot &slot = ch.slots[sl & 1];
|
|
|
|
slot.mul = mul_tab[v & 0x0f];
|
|
slot.KSR = (v & 0x10) ? 0 : 2;
|
|
slot.eg_type = (v & 0x20);
|
|
slot.vib = (v & 0x40);
|
|
slot.AMmask = (v & 0x80) ? ~0 : 0;
|
|
|
|
if (OPL3_mode) {
|
|
// in OPL3 mode
|
|
// DO THIS:
|
|
// if this is one of the slots of 1st channel forming up a 4-op channel
|
|
// do normal operation
|
|
// else normal 2 operator function
|
|
// OR THIS:
|
|
// if this is one of the slots of 2nd channel forming up a 4-op channel
|
|
// update it using channel data of 1st channel of a pair
|
|
// else normal 2 operator function
|
|
switch(chan_no) {
|
|
case 0: case 1: case 2:
|
|
case 9: case 10: case 11:
|
|
if (ch.extended) {
|
|
// normal
|
|
ch.CALC_FCSLOT(slot);
|
|
} else {
|
|
// normal
|
|
ch.CALC_FCSLOT(slot);
|
|
}
|
|
break;
|
|
case 3: case 4: case 5:
|
|
case 12: case 13: case 14: {
|
|
YMF262Channel &ch3 = channels[chan_no - 3];
|
|
if (ch3.extended) {
|
|
// update this slot using frequency data for 1st channel of a pair
|
|
ch3.CALC_FCSLOT(slot);
|
|
} else {
|
|
// normal
|
|
ch.CALC_FCSLOT(slot);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
// normal
|
|
ch.CALC_FCSLOT(slot);
|
|
break;
|
|
}
|
|
} else {
|
|
// in OPL2 mode
|
|
ch.CALC_FCSLOT(slot);
|
|
}
|
|
}
|
|
|
|
// set ksl & tl
|
|
void YMF262::set_ksl_tl(byte sl, byte v)
|
|
{
|
|
int chan_no = sl/2;
|
|
YMF262Channel &ch = channels[chan_no];
|
|
YMF262Slot &slot = ch.slots[sl & 1];
|
|
|
|
int ksl = v >> 6; // 0 / 1.5 / 3.0 / 6.0 dB/OCT
|
|
|
|
slot.ksl = ksl ? 3 - ksl : 31;
|
|
slot.TL = (v & 0x3F) << (ENV_BITS - 1 - 7); // 7 bits TL (bit 6 = always 0)
|
|
|
|
if (OPL3_mode) {
|
|
|
|
// in OPL3 mode
|
|
//DO THIS:
|
|
//if this is one of the slots of 1st channel forming up a 4-op channel
|
|
//do normal operation
|
|
//else normal 2 operator function
|
|
//OR THIS:
|
|
//if this is one of the slots of 2nd channel forming up a 4-op channel
|
|
//update it using channel data of 1st channel of a pair
|
|
//else normal 2 operator function
|
|
switch(chan_no) {
|
|
case 0: case 1: case 2:
|
|
case 9: case 10: case 11:
|
|
if (ch.extended) {
|
|
// normal
|
|
slot.TLL = slot.TL + (ch.ksl_base >> slot.ksl);
|
|
} else {
|
|
// normal
|
|
slot.TLL = slot.TL + (ch.ksl_base >> slot.ksl);
|
|
}
|
|
break;
|
|
case 3: case 4: case 5:
|
|
case 12: case 13: case 14: {
|
|
YMF262Channel &ch3 = channels[chan_no - 3];
|
|
if (ch3.extended) {
|
|
// update this slot using frequency data for 1st channel of a pair
|
|
slot.TLL = slot.TL + (ch3.ksl_base >> slot.ksl);
|
|
} else {
|
|
// normal
|
|
slot.TLL = slot.TL + (ch.ksl_base >> slot.ksl);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
// normal
|
|
slot.TLL = slot.TL + (ch.ksl_base >> slot.ksl);
|
|
break;
|
|
}
|
|
} else {
|
|
// in OPL2 mode
|
|
slot.TLL = slot.TL + (ch.ksl_base >> slot.ksl);
|
|
}
|
|
}
|
|
|
|
// set attack rate & decay rate
|
|
void YMF262::set_ar_dr(byte sl, byte v)
|
|
{
|
|
YMF262Channel &ch = channels[sl / 2];
|
|
YMF262Slot &slot = ch.slots[sl & 1];
|
|
|
|
slot.ar = (v >> 4) ? 16 + ((v >> 4) << 2) : 0;
|
|
|
|
if ((slot.ar + slot.ksr) < 16 + 60) {
|
|
// verified on real YMF262 - all 15 x rates take "zero" time
|
|
slot.eg_sh_ar = eg_rate_shift [slot.ar + slot.ksr];
|
|
slot.eg_m_ar = (1 << slot.eg_sh_ar) - 1;
|
|
slot.eg_sel_ar = eg_rate_select[slot.ar + slot.ksr];
|
|
} else {
|
|
slot.eg_sh_ar = 0;
|
|
slot.eg_m_ar = (1 << slot.eg_sh_ar) - 1;
|
|
slot.eg_sel_ar = 13 * RATE_STEPS;
|
|
}
|
|
|
|
slot.dr = (v & 0x0F) ? 16 + ((v & 0x0F) << 2) : 0;
|
|
slot.eg_sh_dr = eg_rate_shift [slot.dr + slot.ksr];
|
|
slot.eg_m_dr = (1 << slot.eg_sh_dr) - 1;
|
|
slot.eg_sel_dr = eg_rate_select[slot.dr + slot.ksr];
|
|
}
|
|
|
|
// set sustain level & release rate
|
|
void YMF262::set_sl_rr(byte sl, byte v)
|
|
{
|
|
YMF262Channel &ch = channels[sl / 2];
|
|
YMF262Slot &slot = ch.slots[sl & 1];
|
|
|
|
slot.sl = sl_tab[v >> 4];
|
|
slot.rr = (v & 0x0F) ? 16 + ((v & 0x0F) << 2) : 0;
|
|
slot.eg_sh_rr = eg_rate_shift [slot.rr + slot.ksr];
|
|
slot.eg_m_rr = (1 << slot.eg_sh_rr) - 1;
|
|
slot.eg_sel_rr = eg_rate_select[slot.rr + slot.ksr];
|
|
}
|
|
|
|
void YMF262::update_channels(YMF262Channel &ch)
|
|
{
|
|
// update channel passed as a parameter and a channel at CH+=3;
|
|
if (ch.extended) {
|
|
// we've just switched to combined 4 operator mode
|
|
} else {
|
|
// we've just switched to normal 2 operator mode
|
|
}
|
|
}
|
|
|
|
byte YMF262::peekReg(int r)
|
|
{
|
|
return reg[r];
|
|
}
|
|
|
|
byte YMF262::readReg(int r)
|
|
{
|
|
return reg[r];
|
|
}
|
|
|
|
void YMF262::writeReg(int r, byte v, const EmuTime &time)
|
|
{
|
|
if (!OPL3_mode && (r != 0x105)) {
|
|
// in OPL2 mode the only accessible in set #2 is register 0x05
|
|
r &= ~0x100;
|
|
}
|
|
writeRegForce(r, v, time);
|
|
checkMute();
|
|
}
|
|
void YMF262::writeRegForce(int r, byte v, const EmuTime &time)
|
|
{
|
|
reg[r] = v;
|
|
|
|
byte ch_offset = 0;
|
|
if (r & 0x100) {
|
|
switch(r) {
|
|
case 0x101: // test register
|
|
return;
|
|
|
|
case 0x104: { // 6 channels enable
|
|
YMF262Channel &ch0 = channels[0];
|
|
byte prev = ch0.extended;
|
|
ch0.extended = (v >> 0) & 1;
|
|
if (prev != ch0.extended) {
|
|
update_channels(ch0);
|
|
}
|
|
YMF262Channel &ch1 = channels[1];
|
|
prev = ch1.extended;
|
|
ch1.extended = (v >> 1) & 1;
|
|
if (prev != ch1.extended) {
|
|
update_channels(ch1);
|
|
}
|
|
YMF262Channel &ch2 = channels[2];
|
|
prev = ch2.extended;
|
|
ch2.extended = (v >> 2) & 1;
|
|
if (prev != ch2.extended) {
|
|
update_channels(ch2);
|
|
}
|
|
YMF262Channel &ch9 = channels[9];
|
|
prev = ch9.extended;
|
|
ch9.extended = (v >> 3) & 1;
|
|
if (prev != ch9.extended) {
|
|
update_channels(ch9);
|
|
}
|
|
YMF262Channel &ch10 = channels[10];
|
|
prev = ch10.extended;
|
|
ch10.extended = (v >> 4) & 1;
|
|
if (prev != ch10.extended) {
|
|
update_channels(ch10);
|
|
}
|
|
YMF262Channel &ch11 = channels[11];
|
|
prev = ch11.extended;
|
|
ch11.extended = (v >> 5) & 1;
|
|
if (prev != ch11.extended) {
|
|
update_channels(ch11);
|
|
}
|
|
return;
|
|
}
|
|
case 0x105: // OPL3 extensions enable register
|
|
// OPL3 mode when bit0=1 otherwise it is OPL2 mode
|
|
OPL3_mode = v & 0x01;
|
|
if (OPL3_mode) {
|
|
status2 = 0x02;
|
|
}
|
|
|
|
// following behaviour was tested on real YMF262,
|
|
// switching OPL3/OPL2 modes on the fly:
|
|
// - does not change the waveform previously selected
|
|
// (unless when ....)
|
|
// - does not update CH.A, CH.B, CH.C and CH.D output
|
|
// selectors (registers c0-c8) (unless when ....)
|
|
// - does not disable channels 9-17 on OPL3->OPL2 switch
|
|
// - does not switch 4 operator channels back to 2
|
|
// operator channels
|
|
return;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
ch_offset = 9; // register page #2 starts from channel 9
|
|
}
|
|
|
|
r &= 0xFF;
|
|
switch(r & 0xE0) {
|
|
case 0x00: // 00-1F:control
|
|
switch(r & 0x1F) {
|
|
case 0x01: // test register
|
|
break;
|
|
|
|
case 0x02: // Timer 1
|
|
timer1.setValue(v);
|
|
break;
|
|
|
|
case 0x03: // Timer 2
|
|
timer2.setValue(v);
|
|
break;
|
|
|
|
case 0x04: // IRQ clear / mask and Timer enable
|
|
if (v & 0x80) {
|
|
// IRQ flags clear
|
|
resetStatus(0x60);
|
|
} else {
|
|
changeStatusMask((~v) & 0x60);
|
|
timer1.setStart((v & R04_ST1) != 0, time);
|
|
timer2.setStart((v & R04_ST2) != 0, time);
|
|
}
|
|
break;
|
|
|
|
case 0x08: // x,NTS,x,x, x,x,x,x
|
|
nts = v;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 0x20: { // am ON, vib ON, ksr, eg_type, mul
|
|
int slot = slot_array[r & 0x1F];
|
|
if (slot < 0) return;
|
|
set_mul(slot + ch_offset * 2, v);
|
|
break;
|
|
}
|
|
case 0x40: {
|
|
int slot = slot_array[r & 0x1F];
|
|
if (slot < 0) return;
|
|
set_ksl_tl(slot + ch_offset * 2, v);
|
|
break;
|
|
}
|
|
case 0x60: {
|
|
int slot = slot_array[r & 0x1F];
|
|
if (slot < 0) return;
|
|
set_ar_dr(slot + ch_offset * 2, v);
|
|
break;
|
|
}
|
|
case 0x80: {
|
|
int slot = slot_array[r & 0x1F];
|
|
if (slot < 0) return;
|
|
set_sl_rr(slot + ch_offset * 2, v);
|
|
break;
|
|
}
|
|
case 0xA0: {
|
|
if (r == 0xBD) {
|
|
// am depth, vibrato depth, r,bd,sd,tom,tc,hh
|
|
if (ch_offset != 0) {
|
|
// 0xbd register is present in set #1 only
|
|
return;
|
|
}
|
|
lfo_am_depth = v & 0x80;
|
|
lfo_pm_depth_range = (v & 0x40) ? 8 : 0;
|
|
rhythm = v & 0x3F;
|
|
|
|
if (rhythm & 0x20) {
|
|
// BD key on/off
|
|
if (v & 0x10) {
|
|
channels[6].slots[SLOT1].FM_KEYON ( 2);
|
|
channels[6].slots[SLOT2].FM_KEYON ( 2);
|
|
} else {
|
|
channels[6].slots[SLOT1].FM_KEYOFF(~2);
|
|
channels[6].slots[SLOT2].FM_KEYOFF(~2);
|
|
}
|
|
// HH key on/off
|
|
if (v & 0x01) {
|
|
channels[7].slots[SLOT1].FM_KEYON ( 2);
|
|
} else {
|
|
channels[7].slots[SLOT1].FM_KEYOFF(~2);
|
|
}
|
|
// SD key on/off
|
|
if (v & 0x08) {
|
|
channels[7].slots[SLOT2].FM_KEYON ( 2);
|
|
} else {
|
|
channels[7].slots[SLOT2].FM_KEYOFF(~2);
|
|
}
|
|
// TOM key on/off
|
|
if (v & 0x04) {
|
|
channels[8].slots[SLOT1].FM_KEYON ( 2);
|
|
} else {
|
|
channels[8].slots[SLOT1].FM_KEYOFF(~2);
|
|
}
|
|
// TOP-CY key on/off
|
|
if (v & 0x02) {
|
|
channels[8].slots[SLOT2].FM_KEYON ( 2);
|
|
} else {
|
|
channels[8].slots[SLOT2].FM_KEYOFF(~2);
|
|
}
|
|
} else {
|
|
// BD key off
|
|
channels[6].slots[SLOT1].FM_KEYOFF(~2);
|
|
channels[6].slots[SLOT2].FM_KEYOFF(~2);
|
|
// HH key off
|
|
channels[7].slots[SLOT1].FM_KEYOFF(~2);
|
|
// SD key off
|
|
channels[7].slots[SLOT2].FM_KEYOFF(~2);
|
|
// TOM key off
|
|
channels[8].slots[SLOT1].FM_KEYOFF(~2);
|
|
// TOP-CY off
|
|
channels[8].slots[SLOT2].FM_KEYOFF(~2);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// keyon,block,fnum
|
|
if ((r & 0x0F) > 8) {
|
|
return;
|
|
}
|
|
int chan_no = (r & 0x0F) + ch_offset;
|
|
YMF262Channel &ch = channels[chan_no];
|
|
YMF262Channel &ch3 = channels[chan_no + 3];
|
|
int block_fnum;
|
|
if (!(r & 0x10)) {
|
|
// a0-a8
|
|
block_fnum = (ch.block_fnum&0x1F00) | v;
|
|
} else {
|
|
// b0-b8
|
|
block_fnum = ((v & 0x1F) << 8) | (ch.block_fnum & 0xFF);
|
|
if (OPL3_mode) {
|
|
// in OPL3 mode
|
|
// DO THIS:
|
|
// if this is 1st channel forming up a 4-op channel
|
|
// ALSO keyon/off slots of 2nd channel forming up 4-op channel
|
|
// else normal 2 operator function keyon/off
|
|
// OR THIS:
|
|
// if this is 2nd channel forming up 4-op channel just do nothing
|
|
// else normal 2 operator function keyon/off
|
|
switch(chan_no) {
|
|
case 0: case 1: case 2:
|
|
case 9: case 10: case 11:
|
|
if (ch.extended) {
|
|
//if this is 1st channel forming up a 4-op channel
|
|
//ALSO keyon/off slots of 2nd channel forming up 4-op channel
|
|
if (v & 0x20) {
|
|
ch.slots[SLOT1].FM_KEYON ( 1);
|
|
ch.slots[SLOT2].FM_KEYON ( 1);
|
|
ch3.slots[SLOT1].FM_KEYON( 1);
|
|
ch3.slots[SLOT2].FM_KEYON( 1);
|
|
} else {
|
|
ch.slots[SLOT1].FM_KEYOFF (~1);
|
|
ch.slots[SLOT2].FM_KEYOFF (~1);
|
|
ch3.slots[SLOT1].FM_KEYOFF(~1);
|
|
ch3.slots[SLOT2].FM_KEYOFF(~1);
|
|
}
|
|
} else {
|
|
//else normal 2 operator function keyon/off
|
|
if (v & 0x20) {
|
|
ch.slots[SLOT1].FM_KEYON ( 1);
|
|
ch.slots[SLOT2].FM_KEYON ( 1);
|
|
} else {
|
|
ch.slots[SLOT1].FM_KEYOFF(~1);
|
|
ch.slots[SLOT2].FM_KEYOFF(~1);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 3: case 4: case 5:
|
|
case 12: case 13: case 14: {
|
|
YMF262Channel &ch_3 = channels[chan_no - 3];
|
|
if (ch_3.extended) {
|
|
//if this is 2nd channel forming up 4-op channel just do nothing
|
|
} else {
|
|
//else normal 2 operator function keyon/off
|
|
if (v & 0x20) {
|
|
ch.slots[SLOT1].FM_KEYON ( 1);
|
|
ch.slots[SLOT2].FM_KEYON ( 1);
|
|
} else {
|
|
ch.slots[SLOT1].FM_KEYOFF(~1);
|
|
ch.slots[SLOT2].FM_KEYOFF(~1);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
if (v & 0x20) {
|
|
ch.slots[SLOT1].FM_KEYON ( 1);
|
|
ch.slots[SLOT2].FM_KEYON ( 1);
|
|
} else {
|
|
ch.slots[SLOT1].FM_KEYOFF(~1);
|
|
ch.slots[SLOT2].FM_KEYOFF(~1);
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
if (v & 0x20) {
|
|
ch.slots[SLOT1].FM_KEYON ( 1);
|
|
ch.slots[SLOT2].FM_KEYON ( 1);
|
|
} else {
|
|
ch.slots[SLOT1].FM_KEYOFF(~1);
|
|
ch.slots[SLOT2].FM_KEYOFF(~1);
|
|
}
|
|
}
|
|
}
|
|
// update
|
|
if (ch.block_fnum != block_fnum) {
|
|
byte block = block_fnum >> 10;
|
|
ch.block_fnum = block_fnum;
|
|
ch.ksl_base = ksl_tab[block_fnum >> 6];
|
|
ch.fc = fn_tab[block_fnum & 0x03FF] >> (7 - block);
|
|
|
|
// BLK 2,1,0 bits -> bits 3,2,1 of kcode
|
|
ch.kcode = (ch.block_fnum & 0x1C00) >> 9;
|
|
|
|
// the info below is actually opposite to what is stated
|
|
// in the Manuals (verifed on real YMF262)
|
|
// if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum
|
|
// if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum
|
|
if (nts & 0x40) {
|
|
ch.kcode |= (ch.block_fnum & 0x100) >> 8; // notesel == 1
|
|
} else {
|
|
ch.kcode |= (ch.block_fnum & 0x200) >> 9; // notesel == 0
|
|
}
|
|
if (OPL3_mode) {
|
|
int chan_no = (r & 0x0F) + ch_offset;
|
|
// in OPL3 mode
|
|
//DO THIS:
|
|
//if this is 1st channel forming up a 4-op channel
|
|
//ALSO update slots of 2nd channel forming up 4-op channel
|
|
//else normal 2 operator function keyon/off
|
|
//OR THIS:
|
|
//if this is 2nd channel forming up 4-op channel just do nothing
|
|
//else normal 2 operator function keyon/off
|
|
switch(chan_no) {
|
|
case 0: case 1: case 2:
|
|
case 9: case 10: case 11:
|
|
if (ch.extended) {
|
|
//if this is 1st channel forming up a 4-op channel
|
|
//ALSO update slots of 2nd channel forming up 4-op channel
|
|
|
|
// refresh Total Level in FOUR SLOTs of this channel and channel+3 using data from THIS channel
|
|
ch.slots[SLOT1].TLL = ch.slots[SLOT1].TL + (ch.ksl_base >> ch.slots[SLOT1].ksl);
|
|
ch.slots[SLOT2].TLL = ch.slots[SLOT2].TL + (ch.ksl_base >> ch.slots[SLOT2].ksl);
|
|
ch3.slots[SLOT1].TLL = ch3.slots[SLOT1].TL + (ch.ksl_base >> ch3.slots[SLOT1].ksl);
|
|
ch3.slots[SLOT2].TLL = ch3.slots[SLOT2].TL + (ch.ksl_base >> ch3.slots[SLOT2].ksl);
|
|
|
|
// refresh frequency counter in FOUR SLOTs of this channel and channel+3 using data from THIS channel
|
|
ch.CALC_FCSLOT(ch.slots[SLOT1]);
|
|
ch.CALC_FCSLOT(ch.slots[SLOT2]);
|
|
ch.CALC_FCSLOT(ch3.slots[SLOT1]);
|
|
ch.CALC_FCSLOT(ch3.slots[SLOT2]);
|
|
} else {
|
|
//else normal 2 operator function
|
|
// refresh Total Level in both SLOTs of this channel
|
|
ch.slots[SLOT1].TLL = ch.slots[SLOT1].TL + (ch.ksl_base >> ch.slots[SLOT1].ksl);
|
|
ch.slots[SLOT2].TLL = ch.slots[SLOT2].TL + (ch.ksl_base >> ch.slots[SLOT2].ksl);
|
|
|
|
// refresh frequency counter in both SLOTs of this channel
|
|
ch.CALC_FCSLOT(ch.slots[SLOT1]);
|
|
ch.CALC_FCSLOT(ch.slots[SLOT2]);
|
|
}
|
|
break;
|
|
|
|
case 3: case 4: case 5:
|
|
case 12: case 13: case 14: {
|
|
YMF262Channel &ch_3 = channels[chan_no - 3];
|
|
if (ch_3.extended) {
|
|
//if this is 2nd channel forming up 4-op channel just do nothing
|
|
} else {
|
|
//else normal 2 operator function
|
|
// refresh Total Level in both SLOTs of this channel
|
|
ch.slots[SLOT1].TLL = ch.slots[SLOT1].TL + (ch.ksl_base >> ch.slots[SLOT1].ksl);
|
|
ch.slots[SLOT2].TLL = ch.slots[SLOT2].TL + (ch.ksl_base >> ch.slots[SLOT2].ksl);
|
|
|
|
// refresh frequency counter in both SLOTs of this channel
|
|
ch.CALC_FCSLOT(ch.slots[SLOT1]);
|
|
ch.CALC_FCSLOT(ch.slots[SLOT2]);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
// refresh Total Level in both SLOTs of this channel
|
|
ch.slots[SLOT1].TLL = ch.slots[SLOT1].TL + (ch.ksl_base >> ch.slots[SLOT1].ksl);
|
|
ch.slots[SLOT2].TLL = ch.slots[SLOT2].TL + (ch.ksl_base >> ch.slots[SLOT2].ksl);
|
|
|
|
// refresh frequency counter in both SLOTs of this channel
|
|
ch.CALC_FCSLOT(ch.slots[SLOT1]);
|
|
ch.CALC_FCSLOT(ch.slots[SLOT2]);
|
|
break;
|
|
}
|
|
} else {
|
|
// in OPL2 mode
|
|
// refresh Total Level in both SLOTs of this channel
|
|
ch.slots[SLOT1].TLL = ch.slots[SLOT1].TL + (ch.ksl_base >> ch.slots[SLOT1].ksl);
|
|
ch.slots[SLOT2].TLL = ch.slots[SLOT2].TL + (ch.ksl_base >> ch.slots[SLOT2].ksl);
|
|
|
|
// refresh frequency counter in both SLOTs of this channel
|
|
ch.CALC_FCSLOT(ch.slots[SLOT1]);
|
|
ch.CALC_FCSLOT(ch.slots[SLOT2]);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 0xC0: {
|
|
// CH.D, CH.C, CH.B, CH.A, FB(3bits), C
|
|
if ((r & 0xF) > 8) {
|
|
return;
|
|
}
|
|
int chan_no = (r & 0x0F) + ch_offset;
|
|
YMF262Channel &ch = channels[chan_no];
|
|
|
|
int base = chan_no * 4;
|
|
if (OPL3_mode) {
|
|
// OPL3 mode
|
|
pan[base + 0] = (v & 0x10) ? ~0 : 0; // ch.A
|
|
pan[base + 1] = (v & 0x20) ? ~0 : 0; // ch.B
|
|
pan[base + 2] = (v & 0x40) ? ~0 : 0; // ch.C
|
|
pan[base + 3] = (v & 0x80) ? ~0 : 0; // ch.D
|
|
} else {
|
|
// OPL2 mode - always enabled
|
|
pan[base + 0] = ~0; // ch.A
|
|
pan[base + 1] = ~0; // ch.B
|
|
pan[base + 2] = ~0; // ch.C
|
|
pan[base + 3] = ~0; // ch.D
|
|
}
|
|
|
|
ch.slots[SLOT1].FB = (v >> 1) & 7 ? ((v >> 1) & 7) + 7 : 0;
|
|
ch.slots[SLOT1].CON = v & 1;
|
|
|
|
if (OPL3_mode) {
|
|
switch(chan_no) {
|
|
case 0: case 1: case 2:
|
|
case 9: case 10: case 11:
|
|
if (ch.extended) {
|
|
YMF262Channel &ch3 = channels[chan_no + 3];
|
|
switch((ch.slots[SLOT1].CON << 1) | ch3.slots[SLOT1].CON) {
|
|
case 0:
|
|
// 1 -> 2 -> 3 -> 4 - out
|
|
ch.slots[SLOT1].connect = PHASE_MOD1;
|
|
ch.slots[SLOT2].connect = PHASE_MOD2;
|
|
ch3.slots[SLOT1].connect = PHASE_MOD1;
|
|
ch3.slots[SLOT2].connect = chan_no + 3;
|
|
break;
|
|
|
|
case 1:
|
|
// 1 -> 2 -\.
|
|
// 3 -> 4 -+- out
|
|
ch.slots[SLOT1].connect = PHASE_MOD1;
|
|
ch.slots[SLOT2].connect = chan_no;
|
|
ch3.slots[SLOT1].connect = PHASE_MOD1;
|
|
ch3.slots[SLOT2].connect = chan_no + 3;
|
|
break;
|
|
|
|
case 2:
|
|
// 1 -----------\.
|
|
// 2 -> 3 -> 4 -+- out
|
|
ch.slots[SLOT1].connect = chan_no;
|
|
ch.slots[SLOT2].connect = PHASE_MOD2;
|
|
ch3.slots[SLOT1].connect = PHASE_MOD1;
|
|
ch3.slots[SLOT2].connect = chan_no + 3;
|
|
break;
|
|
|
|
case 3:
|
|
// 1 ------\.
|
|
// 2 -> 3 -+- out
|
|
// 4 ------/
|
|
ch.slots[SLOT1].connect = chan_no;
|
|
ch.slots[SLOT2].connect = PHASE_MOD2;
|
|
ch3.slots[SLOT1].connect = chan_no + 3;
|
|
ch3.slots[SLOT2].connect = chan_no + 3;
|
|
break;
|
|
}
|
|
} else {
|
|
// 2 operators mode
|
|
ch.slots[SLOT1].connect = ch.slots[SLOT1].CON ? chan_no : PHASE_MOD1;
|
|
ch.slots[SLOT2].connect = chan_no;
|
|
}
|
|
break;
|
|
|
|
case 3: case 4: case 5:
|
|
case 12: case 13: case 14: {
|
|
YMF262Channel &ch3 = channels[chan_no - 3];
|
|
if (ch3.extended) {
|
|
switch((ch3.slots[SLOT1].CON << 1) | ch.slots[SLOT1].CON) {
|
|
case 0:
|
|
// 1 -> 2 -> 3 -> 4 - out
|
|
ch3.slots[SLOT1].connect = PHASE_MOD1;
|
|
ch3.slots[SLOT2].connect = PHASE_MOD2;
|
|
ch.slots[SLOT1].connect = PHASE_MOD1;
|
|
ch.slots[SLOT2].connect = chan_no;
|
|
break;
|
|
|
|
case 1:
|
|
// 1 -> 2 -\.
|
|
// 3 -> 4 -+- out
|
|
ch3.slots[SLOT1].connect = PHASE_MOD1;
|
|
ch3.slots[SLOT2].connect = chan_no - 3;
|
|
ch.slots[SLOT1].connect = PHASE_MOD1;
|
|
ch.slots[SLOT2].connect = chan_no;
|
|
break;
|
|
|
|
case 2:
|
|
// 1 -----------\.
|
|
// 2 -> 3 -> 4 -+- out
|
|
ch3.slots[SLOT1].connect = chan_no - 3;
|
|
ch3.slots[SLOT2].connect = PHASE_MOD2;
|
|
ch.slots[SLOT1].connect = PHASE_MOD1;
|
|
ch.slots[SLOT2].connect = chan_no;
|
|
break;
|
|
|
|
case 3:
|
|
// 1 ------\.
|
|
// 2 -> 3 -+- out
|
|
// 4 ------/
|
|
ch3.slots[SLOT1].connect = chan_no - 3;
|
|
ch3.slots[SLOT2].connect = PHASE_MOD2;
|
|
ch.slots[SLOT1].connect = chan_no;
|
|
ch.slots[SLOT2].connect = chan_no;
|
|
break;
|
|
}
|
|
} else {
|
|
// 2 operators mode
|
|
ch.slots[SLOT1].connect = ch.slots[SLOT1].CON ? chan_no : PHASE_MOD1;
|
|
ch.slots[SLOT2].connect = chan_no;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
// 2 operators mode
|
|
ch.slots[SLOT1].connect = ch.slots[SLOT1].CON ? chan_no : PHASE_MOD1;
|
|
ch.slots[SLOT2].connect = chan_no;
|
|
break;
|
|
}
|
|
} else {
|
|
// OPL2 mode - always 2 operators mode
|
|
ch.slots[SLOT1].connect = ch.slots[SLOT1].CON ? chan_no : PHASE_MOD1;
|
|
ch.slots[SLOT2].connect = chan_no;
|
|
}
|
|
break;
|
|
}
|
|
case 0xE0: {
|
|
// waveform select
|
|
int slot = slot_array[r & 0x1f];
|
|
if (slot < 0) return;
|
|
slot += ch_offset * 2;
|
|
YMF262Channel &ch = channels[slot / 2];
|
|
|
|
// store 3-bit value written regardless of current OPL2 or OPL3
|
|
// mode... (verified on real YMF262)
|
|
v &= 7;
|
|
ch.slots[slot & 1].waveform_number = v;
|
|
// ... but select only waveforms 0-3 in OPL2 mode
|
|
if (!OPL3_mode) {
|
|
v &= 3;
|
|
}
|
|
ch.slots[slot & 1].wavetable = v * SIN_LEN;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void YMF262::reset(const EmuTime &time)
|
|
{
|
|
eg_timer = 0;
|
|
eg_cnt = 0;
|
|
|
|
noise_rng = 1; // noise shift register
|
|
nts = 0; // note split
|
|
resetStatus(0x60);
|
|
|
|
// reset with register write
|
|
writeRegForce(0x01, 0, time); // test register
|
|
writeRegForce(0x02, 0, time); // Timer1
|
|
writeRegForce(0x03, 0, time); // Timer2
|
|
writeRegForce(0x04, 0, time); // IRQ mask clear
|
|
|
|
//FIX IT registers 101, 104 and 105
|
|
//FIX IT (dont change CH.D, CH.C, CH.B and CH.A in C0-C8 registers)
|
|
int c;
|
|
for (c = 0xFF; c >= 0x20; c--) {
|
|
writeRegForce(c, 0, time);
|
|
}
|
|
//FIX IT (dont change CH.D, CH.C, CH.B and CH.A in C0-C8 registers)
|
|
for (c = 0x1FF; c >= 0x120; c--) {
|
|
writeRegForce(c, 0, time);
|
|
}
|
|
|
|
// reset operator parameters
|
|
for (c = 0; c < 9 * 2; c++) {
|
|
YMF262Channel &ch = channels[c];
|
|
for (int s = 0; s < 2; s++) {
|
|
ch.slots[s].state = EG_OFF;
|
|
ch.slots[s].volume = MAX_ATT_INDEX;
|
|
}
|
|
}
|
|
setInternalMute(true);
|
|
}
|
|
|
|
YMF262::YMF262(short volume, const EmuTime &time, void* ref)
|
|
: timer1(this, ref), timer2(this, ref)
|
|
{
|
|
chanOut = chanout;
|
|
|
|
LFO_AM = LFO_PM = 0;
|
|
lfo_am_depth = lfo_pm_depth_range = lfo_am_cnt = lfo_pm_cnt = 0;
|
|
noise_rng = noise_p = 0;
|
|
rhythm = nts = 0;
|
|
OPL3_mode = false;
|
|
status = status2 = statusMask = 0;
|
|
|
|
oplOversampling = 1;
|
|
|
|
init_tables();
|
|
|
|
reset(time);
|
|
}
|
|
|
|
YMF262::~YMF262()
|
|
{
|
|
}
|
|
|
|
byte YMF262::peekStatus()
|
|
{
|
|
return status | status2;
|
|
}
|
|
|
|
byte YMF262::readStatus()
|
|
{
|
|
byte result = status | status2;
|
|
status2 = 0;
|
|
return result;
|
|
}
|
|
|
|
void YMF262::checkMute()
|
|
{
|
|
bool mute = checkMuteHelper();
|
|
//PRT_DEBUG("YMF262: muted " << mute);
|
|
setInternalMute(mute);
|
|
}
|
|
bool YMF262::checkMuteHelper()
|
|
{
|
|
// TODO this doesn't always mute when possible
|
|
for (int i = 0; i < 18; i++) {
|
|
for (int j = 0; j < 2; j++) {
|
|
YMF262Slot &sl = channels[i].slots[j];
|
|
if (!((sl.state == EG_OFF) ||
|
|
((sl.state == EG_REL) &&
|
|
((sl.TLL + sl.volume) >= ENV_QUIET)))) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int* YMF262::updateBuffer(int length)
|
|
{
|
|
if (isInternalMuted()) {
|
|
return NULL;
|
|
}
|
|
|
|
bool rhythmEnabled = (rhythm & 0x20) != 0;
|
|
|
|
int* buf = buffer;
|
|
while (length--) {
|
|
int a = 0;
|
|
int b = 0;
|
|
int c = 0;
|
|
int d = 0;
|
|
int count = oplOversampling;
|
|
while (count--) {
|
|
advance_lfo();
|
|
|
|
// clear channel outputs
|
|
memset(chanout, 0, sizeof(int) * 18);
|
|
|
|
// register set #1
|
|
// extended 4op ch#0 part 1 or 2op ch#0
|
|
channels[0].chan_calc(LFO_AM);
|
|
if (channels[0].extended) {
|
|
// extended 4op ch#0 part 2
|
|
channels[3].chan_calc_ext(LFO_AM);
|
|
} else {
|
|
// standard 2op ch#3
|
|
channels[3].chan_calc(LFO_AM);
|
|
}
|
|
|
|
// extended 4op ch#1 part 1 or 2op ch#1
|
|
channels[1].chan_calc(LFO_AM);
|
|
if (channels[1].extended) {
|
|
// extended 4op ch#1 part 2
|
|
channels[4].chan_calc_ext(LFO_AM);
|
|
} else {
|
|
// standard 2op ch#4
|
|
channels[4].chan_calc(LFO_AM);
|
|
}
|
|
|
|
// extended 4op ch#2 part 1 or 2op ch#2
|
|
channels[2].chan_calc(LFO_AM);
|
|
if (channels[2].extended) {
|
|
// extended 4op ch#2 part 2
|
|
channels[5].chan_calc_ext(LFO_AM);
|
|
} else {
|
|
// standard 2op ch#5
|
|
channels[5].chan_calc(LFO_AM);
|
|
}
|
|
|
|
if (!rhythmEnabled) {
|
|
channels[6].chan_calc(LFO_AM);
|
|
channels[7].chan_calc(LFO_AM);
|
|
channels[8].chan_calc(LFO_AM);
|
|
} else {
|
|
// Rhythm part
|
|
chan_calc_rhythm(noise_rng & 1);
|
|
}
|
|
|
|
// register set #2
|
|
channels[9].chan_calc(LFO_AM);
|
|
if (channels[9].extended) {
|
|
channels[12].chan_calc_ext(LFO_AM);
|
|
} else {
|
|
channels[12].chan_calc(LFO_AM);
|
|
}
|
|
|
|
channels[10].chan_calc(LFO_AM);
|
|
if (channels[10].extended) {
|
|
channels[13].chan_calc_ext(LFO_AM);
|
|
} else {
|
|
channels[13].chan_calc(LFO_AM);
|
|
}
|
|
|
|
channels[11].chan_calc(LFO_AM);
|
|
if (channels[11].extended) {
|
|
channels[14].chan_calc_ext(LFO_AM);
|
|
} else {
|
|
channels[14].chan_calc(LFO_AM);
|
|
}
|
|
|
|
// channels 15,16,17 are fixed 2-operator channels only
|
|
channels[15].chan_calc(LFO_AM);
|
|
channels[16].chan_calc(LFO_AM);
|
|
channels[17].chan_calc(LFO_AM);
|
|
|
|
for (int i = 0; i < 18; i++) {
|
|
a += chanout[i] & pan[4 * i + 0];
|
|
b += chanout[i] & pan[4 * i + 1];
|
|
c += chanout[i] & pan[4 * i + 2];
|
|
d += chanout[i] & pan[4 * i + 3];
|
|
}
|
|
|
|
advance();
|
|
}
|
|
*(buf++) = (a << 3) / oplOversampling;
|
|
*(buf++) = (b << 3) / oplOversampling;
|
|
}
|
|
|
|
checkMute();
|
|
return buffer;
|
|
}
|
|
|
|
void YMF262::setInternalVolume(short newVolume)
|
|
{
|
|
maxVolume = newVolume;
|
|
}
|
|
|
|
void YMF262::loadState()
|
|
{
|
|
SaveState* state = saveStateOpenForRead("ymf262");
|
|
char tag[32];
|
|
int i;
|
|
|
|
saveStateGetBuffer(state, "reg", reg, sizeof(reg));
|
|
|
|
for (i = 0; i < sizeof(fn_tab) / sizeof(fn_tab[0]); i++) {
|
|
sprintf(tag, "fn_tab%.4d", i);
|
|
fn_tab[i] = saveStateGet(state, tag, 0);
|
|
}
|
|
|
|
for (i = 0; i < sizeof(pan) / sizeof(pan[0]); i++) {
|
|
sprintf(tag, "pan%.4d", i);
|
|
pan[i] = saveStateGet(state, tag, 0);
|
|
}
|
|
|
|
for (i = 0; i < sizeof(chanout) / sizeof(chanout[0]); i++) {
|
|
sprintf(tag, "chanout%.4d", i);
|
|
chanout[i] = saveStateGet(state, tag, 0);
|
|
}
|
|
|
|
|
|
eg_cnt = saveStateGet(state, "eg_cnt", 0);
|
|
eg_timer = saveStateGet(state, "eg_timer", 0);
|
|
eg_timer_add = saveStateGet(state, "eg_timer_add", 0);
|
|
|
|
LFO_AM = (byte)saveStateGet(state, "LFO_AM", 0);
|
|
LFO_PM = (byte)saveStateGet(state, "LFO_PM", 0);
|
|
|
|
lfo_am_depth = (byte)saveStateGet(state, "lfo_am_depth", 0);
|
|
lfo_pm_depth_range = (byte)saveStateGet(state, "lfo_pm_depth_range", 0);
|
|
lfo_am_cnt = saveStateGet(state, "lfo_am_cnt", 0);
|
|
lfo_am_inc = saveStateGet(state, "lfo_am_inc", 0);
|
|
lfo_pm_cnt = saveStateGet(state, "lfo_pm_cnt", 0);
|
|
lfo_pm_inc = saveStateGet(state, "lfo_pm_inc", 0);
|
|
|
|
noise_rng = saveStateGet(state, "noise_rng", 0);
|
|
noise_p = saveStateGet(state, "noise_p", 0);
|
|
noise_f = saveStateGet(state, "noise_f", 0);
|
|
|
|
OPL3_mode = saveStateGet(state, "OPL3_mode", 0) != 0;
|
|
rhythm = (byte)saveStateGet(state, "rhythm", 0);
|
|
nts = (byte)saveStateGet(state, "nts", 0);
|
|
|
|
status = (byte)saveStateGet(state, "status", 0);
|
|
status2 = (byte)saveStateGet(state, "status2", 0);
|
|
statusMask = (byte)saveStateGet(state, "statusMask", 0);
|
|
maxVolume = (short)saveStateGet(state, "maxVolume", 0);
|
|
|
|
for (int i = 0; i < 18; i++) {
|
|
sprintf(tag, "block_fnum%d", i);
|
|
channels[i].block_fnum = saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "fc%d", i);
|
|
channels[i].fc = saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "ksl_base%d", i);
|
|
channels[i].ksl_base = saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "kcode%d", i);
|
|
channels[i].kcode = (byte)saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "extended%d", i);
|
|
channels[i].extended = (byte)saveStateGet(state, tag, 0);
|
|
|
|
for (int j = 0; j < 2; j++) {
|
|
sprintf(tag, "ar%d_%d", i, j);
|
|
channels[i].slots[j].ar = (byte)saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "dr%d_%d", i, j);
|
|
channels[i].slots[j].dr = (byte)saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "rr%d_%d", i, j);
|
|
channels[i].slots[j].rr = (byte)saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "KSR%d_%d", i, j);
|
|
channels[i].slots[j].KSR = (byte)saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "ksl%d_%d", i, j);
|
|
channels[i].slots[j].ksl = (byte)saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "ksr%d_%d", i, j);
|
|
channels[i].slots[j].ksr = (byte)saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "mul%d_%d", i, j);
|
|
channels[i].slots[j].mul = (byte)saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "Cnt%d_%d", i, j);
|
|
channels[i].slots[j].Cnt = saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "Incr%d_%d", i, j);
|
|
channels[i].slots[j].Incr = saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "FB%d_%d", i, j);
|
|
channels[i].slots[j].FB = (byte)saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "op1_out%d_%d_0", i, j);
|
|
channels[i].slots[j].op1_out[0] = saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "op1_out%d_%d_1", i, j);
|
|
channels[i].slots[j].op1_out[1] = saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "CON%d_%d", i, j);
|
|
channels[i].slots[j].CON = (byte)saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "eg_type%d_%d", i, j);
|
|
channels[i].slots[j].eg_type = (byte)saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "state%d_%d", i, j);
|
|
channels[i].slots[j].state = (byte)saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "TL%d_%d", i, j);
|
|
channels[i].slots[j].TL = saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "TLL%d_%d", i, j);
|
|
channels[i].slots[j].TLL = saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "volume%d_%d", i, j);
|
|
channels[i].slots[j].volume = saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "sl%d_%d", i, j);
|
|
channels[i].slots[j].sl = saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "eg_m_ar%d_%d", i, j);
|
|
channels[i].slots[j].eg_m_ar = saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "eg_sh_ar%d_%d", i, j);
|
|
channels[i].slots[j].eg_sh_ar = (byte)saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "eg_sel_ar%d_%d", i, j);
|
|
channels[i].slots[j].eg_sel_ar = (byte)saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "eg_m_dr%d_%d", i, j);
|
|
channels[i].slots[j].eg_m_dr = saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "eg_sh_dr%d_%d", i, j);
|
|
channels[i].slots[j].eg_sh_dr = (byte)saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "eg_sel_dr%d_%d", i, j);
|
|
channels[i].slots[j].eg_sel_dr = (byte)saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "eg_m_rr%d_%d", i, j);
|
|
channels[i].slots[j].eg_m_rr = saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "eg_sh_rr%d_%d", i, j);
|
|
channels[i].slots[j].eg_sh_rr = (byte)saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "eg_sel_rr%d_%d", i, j);
|
|
channels[i].slots[j].eg_sel_rr = (byte)saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "key%d_%d", i, j);
|
|
channels[i].slots[j].key = (byte)saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "AMmask%d_%d", i, j);
|
|
channels[i].slots[j].AMmask = (byte)saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "vib%d_%d", i, j);
|
|
channels[i].slots[j].vib = (byte)saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "waveform_number%d_%d", i, j);
|
|
channels[i].slots[j].waveform_number = (byte)saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "wavetable%d_%d", i, j);
|
|
channels[i].slots[j].wavetable = saveStateGet(state, tag, 0);
|
|
|
|
sprintf(tag, "connect%d_%d", i, j);
|
|
channels[i].slots[j].connect = saveStateGet(state, tag, 0);
|
|
}
|
|
}
|
|
|
|
saveStateClose(state);
|
|
}
|
|
|
|
void YMF262::saveState()
|
|
{
|
|
SaveState* state = saveStateOpenForWrite("ymf262");
|
|
char tag[32];
|
|
int i;
|
|
|
|
saveStateSetBuffer(state, "reg", reg, sizeof(reg));
|
|
|
|
for (i = 0; i < sizeof(fn_tab) / sizeof(fn_tab[0]); i++) {
|
|
sprintf(tag, "fn_tab%.4d", i);
|
|
saveStateSet(state, tag, fn_tab[i]);
|
|
}
|
|
|
|
for (i = 0; i < sizeof(pan) / sizeof(pan[0]); i++) {
|
|
sprintf(tag, "pan%.4d", i);
|
|
saveStateSet(state, tag, pan[i]);
|
|
}
|
|
|
|
for (i = 0; i < sizeof(chanout) / sizeof(chanout[0]); i++) {
|
|
sprintf(tag, "chanout%.4d", i);
|
|
saveStateSet(state, tag, chanout[i]);
|
|
}
|
|
|
|
|
|
saveStateSet(state, "eg_cnt", eg_cnt);
|
|
saveStateSet(state, "eg_timer", eg_timer);
|
|
saveStateSet(state, "eg_timer_add", eg_timer_add);
|
|
|
|
saveStateSet(state, "LFO_AM", LFO_AM);
|
|
saveStateSet(state, "LFO_PM", LFO_PM);
|
|
|
|
saveStateSet(state, "lfo_am_depth", lfo_am_depth);
|
|
saveStateSet(state, "lfo_pm_depth_range", lfo_pm_depth_range);
|
|
saveStateSet(state, "lfo_am_cnt", lfo_am_cnt);
|
|
saveStateSet(state, "lfo_am_inc", lfo_am_inc);
|
|
saveStateSet(state, "lfo_pm_cnt", lfo_pm_cnt);
|
|
saveStateSet(state, "lfo_pm_inc", lfo_pm_inc);
|
|
|
|
saveStateSet(state, "noise_rng", noise_rng);
|
|
saveStateSet(state, "noise_p", noise_p);
|
|
saveStateSet(state, "noise_f", noise_f);
|
|
|
|
saveStateSet(state, "OPL3_mode", OPL3_mode);
|
|
saveStateSet(state, "rhythm", rhythm);
|
|
saveStateSet(state, "nts", nts);
|
|
|
|
saveStateSet(state, "status", status);
|
|
saveStateSet(state, "status2", status2);
|
|
saveStateSet(state, "statusMask", statusMask);
|
|
saveStateSet(state, "maxVolume", maxVolume);
|
|
|
|
for (int i = 0; i < 18; i++) {
|
|
sprintf(tag, "block_fnum%d", i);
|
|
saveStateSet(state, tag, channels[i].block_fnum);
|
|
|
|
sprintf(tag, "fc%d", i);
|
|
saveStateSet(state, tag, channels[i].fc);
|
|
|
|
sprintf(tag, "ksl_base%d", i);
|
|
saveStateSet(state, tag, channels[i].ksl_base);
|
|
|
|
sprintf(tag, "kcode%d", i);
|
|
saveStateSet(state, tag, channels[i].kcode);
|
|
|
|
sprintf(tag, "extended%d", i);
|
|
saveStateSet(state, tag, channels[i].extended);
|
|
|
|
for (int j = 0; j < 2; j++) {
|
|
sprintf(tag, "ar%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].ar);
|
|
|
|
sprintf(tag, "dr%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].dr);
|
|
|
|
sprintf(tag, "rr%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].rr);
|
|
|
|
sprintf(tag, "KSR%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].KSR);
|
|
|
|
sprintf(tag, "ksl%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].ksl);
|
|
|
|
sprintf(tag, "ksr%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].ksr);
|
|
|
|
sprintf(tag, "mul%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].mul);
|
|
|
|
sprintf(tag, "Cnt%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].Cnt);
|
|
|
|
sprintf(tag, "Incr%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].Incr);
|
|
|
|
sprintf(tag, "FB%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].FB);
|
|
|
|
sprintf(tag, "op1_out%d_%d_0", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].op1_out[0]);
|
|
|
|
sprintf(tag, "op1_out%d_%d_1", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].op1_out[1]);
|
|
|
|
sprintf(tag, "CON%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].CON);
|
|
|
|
sprintf(tag, "eg_type%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].eg_type);
|
|
|
|
sprintf(tag, "state%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].state);
|
|
|
|
sprintf(tag, "TL%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].TL);
|
|
|
|
sprintf(tag, "TLL%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].TLL);
|
|
|
|
sprintf(tag, "volume%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].volume);
|
|
|
|
sprintf(tag, "sl%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].sl);
|
|
|
|
sprintf(tag, "eg_m_ar%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].eg_m_ar);
|
|
|
|
sprintf(tag, "eg_sh_ar%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].eg_sh_ar);
|
|
|
|
sprintf(tag, "eg_sel_ar%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].eg_sel_ar);
|
|
|
|
sprintf(tag, "eg_m_dr%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].eg_m_dr);
|
|
|
|
sprintf(tag, "eg_sh_dr%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].eg_sh_dr);
|
|
|
|
sprintf(tag, "eg_sel_dr%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].eg_sel_dr);
|
|
|
|
sprintf(tag, "eg_m_rr%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].eg_m_rr);
|
|
|
|
sprintf(tag, "eg_sh_rr%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].eg_sh_rr);
|
|
|
|
sprintf(tag, "eg_sel_rr%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].eg_sel_rr);
|
|
|
|
sprintf(tag, "key%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].key);
|
|
|
|
sprintf(tag, "AMmask%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].AMmask);
|
|
|
|
sprintf(tag, "vib%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].vib);
|
|
|
|
sprintf(tag, "waveform_number%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].waveform_number);
|
|
|
|
sprintf(tag, "wavetable%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].wavetable);
|
|
|
|
sprintf(tag, "connect%d_%d", i, j);
|
|
saveStateSet(state, tag, channels[i].slots[j].connect);
|
|
}
|
|
}
|
|
|
|
saveStateClose(state);
|
|
}
|