blueMSX-libretro/Src/SoundChips/OpenMsxYMF262.cpp
2014-07-07 23:38:08 +02:00

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);
}