/*
**  software implementation of YM2413
**  FM sound generator type OPLL
**
** Copyright (C) 2002 Jarek Burczynski
**
** Version 1.0


to do:

- make sure of the sinus amplitude bits

- make sure of the EG resolution bits (looks like the biggest
  modulation index generated by the modulator is 123, 124 = no modulation)
- find proper algorithm for attack phase of EG

- tune up instruments ROM

- support sample replay in test mode (it is NOT as simple as setting bit 0
  in register 0x0f and using register 0x10 for sample data).
  Which games use this feature ?

*/

/* EkeEke (2011): removed multiple chips support, cleaned code & added FM board interface for Genesis Plus GX */
/* PiCiJi: put it in class */

#ifndef YM2413CORE_H
#define YM2413CORE_H

#define SLOT1 0
#define SLOT2 1

#define EG_SH   16  /* 16.16 fixed point (EG timing)         */
#define LFO_SH  24  /*  8.24 fixed point (LFO calculations)  */

#define TL_RES_LEN      (256)
#define TL_TAB_LEN      (11*2*TL_RES_LEN)

#define SIN_BITS        10
#define SIN_LEN         (1<<SIN_BITS)
#define SIN_MASK        (SIN_LEN-1)

#define ENV_QUIET       (TL_TAB_LEN>>5)
#define ENV_BITS        10
#define ENV_LEN         (1<<ENV_BITS)
#define ENV_STEP        (128.0/ENV_LEN)

#define FREQ_SH         16  /* 16.16 fixed point (frequency calculations) */
#define FREQ_MASK       ((1<<FREQ_SH)-1)

#define RATE_STEPS (8)
#define LFO_AM_TAB_ELEMENTS 210
#define RATE_STEPS (8)

#define MAX_ATT_INDEX  ((1<<(ENV_BITS-2))-1) /*255*/
#define MIN_ATT_INDEX  (0)

#define EG_DMP      5
#define EG_ATT      4
#define EG_DEC      3
#define EG_SUS      2
#define EG_REL      1
#define EG_OFF      0

#include <dataTypes.h>

class YM2413CORE
{
    typedef struct {
        u32  ar;       /* attack rate: AR<<2           */
        u32  dr;       /* decay rate:  DR<<2           */
        u32  rr;       /* release rate:RR<<2           */
        u8  KSR;       /* key scale rate               */
        u8  ksl;       /* keyscale level               */
        u8  ksr;       /* key scale rate: kcode>>KSR   */
        u8  mul;       /* multiple: mul_tab[ML]        */

        /* Phase Generator */
        u32 phase;     /* frequency counter            */
        u32 freq;      /* frequency counter step       */
        u8 fb_shift;   /* feedback shift value         */
        i32 op1_out[2]; /* slot1 output for feedback    */

        /* Envelope Generator */
        u8  eg_type;   /* percussive/nonpercussive mode  */
        u8  state;     /* phase type                     */
        u32  TL;       /* total level: TL << 2           */
        i32  TLL;       /* adjusted now TL                */
        i32  volume;    /* envelope counter               */
        u32  sl;       /* sustain level: sl_tab[SL]      */

        u8  eg_sh_dp;  /* (dump state)                   */
        u8  eg_sel_dp; /* (dump state)                   */
        u8  eg_sh_ar;  /* (attack state)                 */
        u8  eg_sel_ar; /* (attack state)                 */
        u8  eg_sh_dr;  /* (decay state)                  */
        u8  eg_sel_dr; /* (decay state)                  */
        u8  eg_sh_rr;  /* (release state for non-perc.)  */
        u8  eg_sel_rr; /* (release state for non-perc.)  */
        u8  eg_sh_rs;  /* (release state for perc.mode)  */
        u8  eg_sel_rs; /* (release state for perc.mode)  */

        u32  key;      /* 0 = KEY OFF, >0 = KEY ON */

        /* LFO */
        u32  AMmask;   /* LFO Amplitude Modulation enable mask */
        u8  vib;       /* LFO Phase Modulation enable flag (active high)*/

        unsigned int wavetable;
    } YM2413_OPLL_SLOT;

    typedef struct {
        YM2413_OPLL_SLOT SLOT[2];

        /* phase generator state */
        u32  block_fnum;   /* block+fnum */
        u32  fc;           /* Freq. freqement base */
        u32  ksl_base;     /* KeyScaleLevel Base step  */
        u8   kcode;        /* key code (for key scaling) */
        u8   sus;          /* sus on/off (release speed in percussive mode)  */
    } YM2413_OPLL_CH;

    typedef struct {
        YM2413_OPLL_CH P_CH[9];   /* OPLL chips have 9 channels */
        u8  instvol_r[9];        /* instrument/volume (or volume/volume in percussive mode)  */

        u32  eg_cnt;             /* global envelope generator counter  */
        u32  eg_timer;           /* global envelope generator counter works at frequency = chipclock/72 */
        u32  eg_timer_add;       /* step of eg_timer */
        u32  eg_timer_overflow;  /* envelope generator timer overlfows every 1 sample (on real chip) */

        u8  rhythm;              /* Rhythm mode  */

        /* LFO */
        u32  lfo_am_cnt;
        u32  lfo_am_inc;
        u32  lfo_pm_cnt;
        u32  lfo_pm_inc;

        u32  noise_rng;      /* 23 bit noise shift register  */
        u32  noise_p;        /* current noise 'phase'  */
        u32  noise_f;        /* current noise period */

        /* instrument settings
        0-user instrument
        1-15 - fixed instruments
        16 -bass drum settings
        17,18 - other percussion instruments
        */
        u8 inst_tab[19][8];
        u32 fn_tab[1024];     /* fnumber->increment counter  */
        bool state;
    } Chip;

    Chip ym2413;
    signed int output[2];
    u32  LFO_AM;
    i32  LFO_PM;

    signed int tl_tab[TL_TAB_LEN];
    unsigned int sin_tab[SIN_LEN * 2];
    static const double ksl_tab[8*16];
    static const unsigned char eg_rate_shift[16+64+16];
    static const unsigned char eg_rate_select[16+64+16];
    static const u8 lfo_am_table[LFO_AM_TAB_ELEMENTS];
    static const i8 lfo_pm_table[8*8];
    static unsigned char table[19][8];
    static const unsigned char eg_inc[15*RATE_STEPS];
    static const u32 sl_tab[16];
    static const double mul_tab[16];

    unsigned int getKsl(unsigned pos) {
        return static_cast<unsigned int>(ksl_tab[pos]);
    }

    u8 getMul(unsigned pos) {
        return static_cast<u8>(mul_tab[pos]);
    }

    void initTlTable();
    void initSinTable();
    signed int op_calc(u32 phase, unsigned int env, signed int pm, unsigned int wave_tab, bool pmShift);

    void CALC_FCSLOT( YM2413_OPLL_CH* CH, YM2413_OPLL_SLOT* SLOT );
    void set_mul(int slot,int v);
    void set_ksl_tl(int chan,int v);
    void set_ar_dr(int slot, int v);
    void set_sl_rr(int slot,int v);
    void set_ksl_wave_fb(int chan,int v);
    void update_instrument_zero(u8 r);
    void load_instrument(u32 chan, u32 slot, u8* inst );
    void KEY_ON(YM2413_OPLL_SLOT* SLOT, u32 key_set);
    void KEY_OFF(YM2413_OPLL_SLOT *SLOT, u32 key_clr);
    void chan_calc( YM2413_OPLL_CH *CH );
    void rhythm_calc( YM2413_OPLL_CH *CH, unsigned int noise );
    void advance_lfo(void);
    void advance(void);
    void OPLL_initalize(void);

public:
    YM2413CORE();
protected:
    int YM2413Update();
    void reset();
    void writeReg(int r, int v);
    bool isActivated() { return ym2413.state; }
    void activate(bool state) { ym2413.state = state; }
};

#endif // YM2413_H
