Top secrets sources NedoPC pentevo

Rev

Blame | Last modification | View Log | Download | RSS feed | ?url?

// 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.4 2005/09/24 00:09:50 dvik Exp $

/*
 *
 * File: ymf262.c - software implementation of YMF262
 *                  FM sound generator type OPL3
 *
 * Copyright (C) 2003 Jarek Burczynski
 *
 * Version 0.1
 *
 *
 * 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 "../std.h"

#include "ymf262.h"
#include <cmath>

#ifdef _MSC_VER
#pragma warning( disable : 4355 )
#endif

const double 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 double 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 u8 SLOT1 = 0;
const u8 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 u8 RATE_STEPS = 8;
const u8 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 u8 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 u8 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) (u8)(2 * x)
const u8 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 u8 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 char 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(u8 flag)
{
        setStatus(flag);
}

// status set and IRQ handling
void YMF262::setStatus(u8 flag)
{
        // set status flag masking out disabled IRQs
        status |= flag;
        if (status & statusMask) {
                status |= 0x80;
//              IRQHelper::set();
        }
}

// status reset and IRQ handling
void YMF262::resetStatus(u8 flag)
{
        // reset status flag
        status &= ~flag;
        if (!(status & statusMask)) {
                status &= 0x7F;
//              IRQHelper::reset();
        }
}

// IRQ mask set
void YMF262::changeStatusMask(u8 flag)
{
        statusMask = flag;
        status &= statusMask;
        if (status) {
                status |= 0x80;
//              IRQHelper::set();
        } else {
                status &= 0x7F;
//              IRQHelper::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);
        }

        u8 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) {
                        u8 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(u8 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(u8 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(u8 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++) {
                double m = (1 << 16) / pow((double)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 double LOG2 = ::log((double)2);
        for (i = 0; i < SIN_LEN; i++) {
                // non-standard sinus
                double m = sin(((i * 2) + 1) * PI / SIN_LEN); // checked against the real chip
                // we never reach zero here due to ((i * 2) + 1)
                double 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;
        double freqbase  = ((double)CLCK_FREQ / (8.0 * 36)) / (double)(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)( (double)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(u8 key_set)
{
        if (!key) {
                // restart Phase Generator
                Cnt = 0;
                // phase -> Attack
                state = EG_ATT;
        }
        key |= key_set;
}

void YMF262Slot::FM_KEYOFF(u8 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(u8 sl, u8 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(u8 sl, u8 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(u8 sl, u8 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(u8 sl, u8 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
        }
}

u8 YMF262::peekReg(int r)
{
        return reg[r];
}

u8 YMF262::readReg(int r)
{
        return reg[r];
}

void YMF262::writeReg(int r, u8 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, u8 v, const EmuTime &time)
{
        reg[r] = v;

        u8 ch_offset = 0;
        if (r & 0x100) {
                switch(r) {
                case 0x101:     // test register
                        return;
               
                case 0x104: { // 6 channels enable
                        YMF262Channel &ch0 = channels[0];
                        u8 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);
                                timer1.resetFlags();
                                timer2.resetFlags();
                        } 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) {
                        u8 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];
                                        u8 conn = (ch.slots[SLOT1].CON << 1) || (ch3.slots[SLOT1].CON);
                                        switch(conn) {
                                        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) {
                                        u8 conn = (ch3.slots[SLOT1].CON << 1) || (ch.slots[SLOT1].CON);
                                        switch(conn) {
                                        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, unsigned tStatesPerSecond)
{
        timer1.init(tStatesPerSecond);
        timer2.init(tStatesPerSecond);

        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)
{
    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, 0);
}

YMF262::~YMF262()
{
}

u8 YMF262::peekStatus(const EmuTime& time)
{
        return status | status2 | timer1.poll(time) | timer2.poll(time);
}

u8 YMF262::readStatus(const EmuTime& time)
{
        u8 result = peekStatus(time);
        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[5].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;
}