/*
 * Copyright (C) 2019 Nuke.YKT
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 *
 *  Yamaha YM2413 emulator
 *  Thanks:
 *      siliconpr0n.org(digshadow, John McMaster):
 *          VRC VII decap and die shot.
 *
 *  version: 1.0.1
 */

#ifndef OPLL_H
#define OPLL_H

#include <cstdint>

enum : uint8_t {
    opll_type_ym2413 = 0x00,    /* Yamaha YM2413  */
    opll_type_ds1001,           /* Konami VRC VII */
    opll_type_ym2413b,          /* Yamaha YM2413B */
    opll_type_ymf281,           /* Yamaha YMF281  */
    opll_type_ymf281b,          /* Yamaha YMF281B */
    opll_type_ym2420,           /* Yamaha YM2420  */
    opll_type_ym2423,           /* Yamaha YM2423  */
};

enum : uint8_t {
    opll_patch_1 = 0x00,
    opll_patch_2,
    opll_patch_3,
    opll_patch_4,
    opll_patch_5,
    opll_patch_6,
    opll_patch_7,
    opll_patch_8,
    opll_patch_9,
    opll_patch_10,
    opll_patch_11,
    opll_patch_12,
    opll_patch_13,
    opll_patch_14,
    opll_patch_15,
    opll_patch_drum_0,
    opll_patch_drum_1,
    opll_patch_drum_2,
    opll_patch_drum_3,
    opll_patch_drum_4,
    opll_patch_drum_5,
    opll_patch_max
};

struct opll_patch_t {
    uint8_t tl;
    uint8_t dc;
    uint8_t dm;
    uint8_t fb;
    uint8_t am[2];
    uint8_t vib[2];
    uint8_t et[2];
    uint8_t ksr[2];
    uint8_t multi[2];
    uint8_t ksl[2];
    uint8_t ar[2];
    uint8_t dr[2];
    uint8_t sl[2];
    uint8_t rr[2];
};

struct opll_t {
    uint32_t chip_type;
    uint32_t cycles;
    uint32_t slot;
    const opll_patch_t *patchrom;
    /* IO */
    uint8_t write_data;
    uint8_t write_a;
    uint8_t write_d;
    uint8_t write_a_en;
    uint8_t write_d_en;
    uint8_t write_fm_address;
    uint8_t write_fm_data;
    uint8_t write_mode_address;
    uint8_t address;
    uint8_t data;
    /* Envelope generator */
    uint8_t eg_counter_state;
    uint8_t eg_counter_state_prev;
    uint32_t eg_timer;
    uint8_t eg_timer_low_lock;
    uint8_t eg_timer_carry;
    uint8_t eg_timer_shift;
    uint8_t eg_timer_shift_lock;
    uint8_t eg_timer_shift_stop;
    uint8_t eg_state[18];
    uint8_t eg_level[18];
    uint8_t eg_kon;
    uint32_t eg_dokon;
    uint8_t eg_off;
    uint8_t eg_rate;
    uint8_t eg_maxrate;
    uint8_t eg_zerorate;
    uint8_t eg_inc_lo;
    uint8_t eg_inc_hi;
    uint8_t eg_rate_hi;
    uint16_t eg_sl;
    uint16_t eg_ksltl;
    uint8_t eg_out;
    uint8_t eg_silent;
    /* Phase generator */
    uint16_t pg_fnum;
    uint8_t pg_block;
    uint16_t pg_out;
    uint32_t pg_inc;
    uint32_t pg_phase[18];
    uint32_t pg_phase_next;
    /* Operator */
    int16_t op_fb1[9];
    int16_t op_fb2[9];
    int16_t op_fbsum;
    int16_t op_mod;
    uint8_t op_neg;
    uint16_t op_logsin;
    uint16_t op_exp_m;
    uint16_t op_exp_s;
    /* Channel */
    int16_t ch_out;
    int16_t ch_out_hh;
    int16_t ch_out_tm;
    int16_t ch_out_bd;
    int16_t ch_out_sd;
    int16_t ch_out_tc;
    /* LFO */
    uint16_t lfo_counter;
    uint8_t lfo_vib_counter;
    uint16_t lfo_am_counter;
    uint8_t lfo_am_step;
    uint8_t lfo_am_dir;
    uint8_t lfo_am_car;
    uint8_t lfo_am_out;
    /* Register set */
    uint16_t fnum[9];
    uint8_t block[9];
    uint8_t kon[9];
    uint8_t son[9];
    uint8_t vol[9];
    uint8_t inst[9];
    uint8_t rhythm;
    uint8_t testmode;
    opll_patch_t patch;
    uint8_t c_instr;
    uint8_t c_op;
    uint8_t c_tl;
    uint8_t c_dc;
    uint8_t c_dm;
    uint8_t c_fb;
    uint8_t c_am;
    uint8_t c_vib;
    uint8_t c_et;
    uint8_t c_ksr;
    uint8_t c_ksr_freq;
    uint8_t c_ksl_freq;
    uint8_t c_ksl_block;
    uint8_t c_multi;
    uint8_t c_ksl;
    uint8_t c_adrr[3];
    uint8_t c_sl;
    uint16_t c_fnum;
    uint16_t c_block;
    /* Rhythm mode */
    int8_t rm_enable;
    uint32_t rm_noise;
    uint32_t rm_select;
    uint8_t rm_hh_bit2;
    uint8_t rm_hh_bit3;
    uint8_t rm_hh_bit7;
    uint8_t rm_hh_bit8;
    uint8_t rm_tc_bit3;
    uint8_t rm_tc_bit5;

    int16_t output_m;
    int16_t output_r;
};

void OPLL_Reset(opll_t *chip, uint32_t chip_type);
void OPLL_Clock(opll_t *chip, int32_t *buffer);
void OPLL_Write(opll_t *chip, uint32_t port, uint8_t data);

#endif
