/*
** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
**
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of version 2 of the GNU Library General 
** Public License as published by the Free Software Foundation.
**
** 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 
** Library General Public License for more details.  To obtain a 
** copy of the GNU Library General Public License, write to the Free 
** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**
** Any permitted reproduction of these routines, in whole or in part,
** must bear this legend.
**
**
** nes_apu.h
**
** NES APU emulation header file
** nes_apu.h,v 1.5 2002/03/24 15:32:03 tekezo Exp
*/

#ifndef _NES_APU_H_
#define _NES_APU_H_

#include <kos.h>

/* define this for realtime generated noise */
#define  REALTIME_NOISE

#define  APU_WRA0       0x4000
#define  APU_WRA1       0x4001
#define  APU_WRA2       0x4002
#define  APU_WRA3       0x4003
#define  APU_WRB0       0x4004
#define  APU_WRB1       0x4005
#define  APU_WRB2       0x4006
#define  APU_WRB3       0x4007
#define  APU_WRC0       0x4008
#define  APU_WRC2       0x400A
#define  APU_WRC3       0x400B
#define  APU_WRD0       0x400C
#define  APU_WRD2       0x400E
#define  APU_WRD3       0x400F
#define  APU_WRE0       0x4010
#define  APU_WRE1       0x4011
#define  APU_WRE2       0x4012
#define  APU_WRE3       0x4013

#define  APU_SMASK      0x4015

/* length of generated noise */
#define  APU_NOISE_32K  0x7FFF
#define  APU_NOISE_93   93

/*#define  APU_BASEFREQ   1789772.7272727272727272 */
#define  APU_BASEFREQ   1789772.5

/* to/from 16.16 fixed point */
#define  APU_TO_FIXED(x)    ((x) << 16)
#define  APU_FROM_FIXED(x)  ((x) >> 16)

#define boolean int
#define true 1
#define false 0

/* ============================================================ */
/* APU Sound struct */

/* channel structures */
/* As much data as possible is precalculated,
** to keep the sample processing as lean as possible
*/
 
typedef struct __attribute__ ((packed, aligned(4))) rectangle_s {
    uint8 regs[4];

    boolean enabled;

    int32 phaseacc;
    int32 freq;
    int32 output_vol;
    boolean fixed_envelope;
    boolean holdnote;
    uint8 volume;

    int32 sweep_phase;
    int32 sweep_delay;
    boolean sweep_on;
    uint8 sweep_shifts;
    uint8 sweep_length;
    boolean sweep_inc;

    /* this may not be necessary in the future */
    int32 freq_limit;

    /* rectangle 0 uses a complement addition for sweep
    ** increases, while rectangle 1 uses subtraction
    */
    boolean sweep_complement;

    int32 env_phase;
    int32 env_delay;
    uint8 env_vol;

    int vbl_length;
    uint8 adder;
    int duty_flip;

    /* for sync read $4105 */
    boolean enabled_cur;
    boolean holdnote_cur;
    int vbl_length_cur;
} rectangle_t;

typedef struct __attribute__ ((packed, aligned(4))) triangle_s {
    uint8 regs[3];

    boolean enabled;

    int32 freq;
    int32 phaseacc;
    int32 output_vol;

    uint8 adder;

    boolean holdnote;
    boolean counter_started;
    /* quasi-hack */
    int write_latency;

    int vbl_length;
    int linear_length;

    /* for sync read $4105 */
    boolean enabled_cur;
    boolean holdnote_cur;
    boolean counter_started_cur;
    int vbl_length_cur;

#ifdef APU_YANO
    /* less compatibility, clearer sound if enabled */
    boolean ideal_triangle;
#endif
} triangle_t;

typedef struct __attribute__ ((packed, aligned(4))) noise_s {
    uint8 regs[3];

    boolean enabled;

    int32 freq;
    int32 phaseacc;
    int32 output_vol;

    int32 env_phase;
    int32 env_delay;
    uint8 env_vol;
    boolean fixed_envelope;
    boolean holdnote;

    uint8 volume;

    int vbl_length;

#ifdef REALTIME_NOISE
    uint8 xor_tap;
#else
    boolean short_sample;
    int cur_pos;
#endif /* REALTIME_NOISE */

    /* for sync read $4105 */
    boolean enabled_cur;
    boolean holdnote_cur;
    int vbl_length_cur;
} noise_t;

typedef struct __attribute__ ((packed, aligned(4))) dmc_s {
    uint8 regs[4];

    /* bodge for timestamp queue */
    boolean enabled;
    
    int32 freq;
    int32 phaseacc;
    int32 output_vol;

    uint32 address;
    uint32 cached_addr;
    int dma_length;
    int cached_dmalength;
    uint8 cur_byte;

    boolean looping;
    boolean irq_gen;
    boolean irq_occurred;

    /* for sync read $4105 and DPCM IRQ */
    int32 freq_cur;
    int32 phaseacc_cur;
    int dma_length_cur;
    int cached_dmalength_cur;
    boolean enabled_cur;
    boolean looping_cur;
    boolean irq_gen_cur;
    boolean irq_occurred_cur;
} dmc_t;

typedef struct __attribute__ ((packed, aligned(4))) APUSOUND_s {
    rectangle_t rectangle[2];
    triangle_t triangle;
    noise_t noise;
    dmc_t dmc;
} APUSOUND;

/* ============================================================ */
/* ex-sound */
typedef enum {
    NES_APU_EXSOUND_NONE,
    NES_APU_EXSOUND_VRC6,
    NES_APU_EXSOUND_VRC7,
    NES_APU_EXSOUND_FDS,
    NES_APU_EXSOUND_MMC5,
    NES_APU_EXSOUND_N106,
    NES_APU_EXSOUND_FME7,
} nes_apu_exsound_type_t;

typedef struct __attribute__ ((packed, aligned(4))) nes_apu_exsound_s {
    int32 (*render_func)();
    void (*write_func)(uint32 address, uint8 value);
} nes_apu_exsound_t;

/* VRC6 Sound struct */

typedef struct __attribute__ ((packed, aligned(4))) VRC6_SQUARE_s{
    uint32 cps;
    int32 cycles;
    uint32 spd;
    uint8 regs[3];
    uint8 update;
    uint8 adr;
} VRC6_SQUARE;

typedef struct __attribute__ ((packed, aligned(4))) VRC6_SAW_s {
    uint32 cps;
    int32 cycles;
    uint32 spd;
    uint32 output;
    uint8 regs[3];
    uint8 update;
    uint8 adr;
} VRC6_SAW;

typedef struct __attribute__ ((packed, aligned(4))) VRC6SOUND_s {
    VRC6_SQUARE square[2];
    VRC6_SAW saw;
    uint32 mastervolume;
} VRC6SOUND;

/* ------------------------------------------------------------ */
/* VRC7 Sound struct */

typedef struct __attribute__ ((packed, aligned(4))) OPLL_OP_s {
    uint32	pg_phase;
    uint32	pg_spd;
    int32	vib_cycles;
    uint32	input;
    uint32	eg_phase;
    uint32	eg_sl;
    uint32	eg_arr;
    uint32	eg_drr;
    uint32	eg_rrr;
    uint8	pg_vib;
    uint32	*sintblp;
    uint8	tl;
    uint8	eg_mode;
    uint8	eg_type;
    uint8	su_type;
    uint8	eg_ar;
    uint8	eg_dr;
    uint8	eg_rr;
    uint8	eg_ks;
    uint8	eg_am;
} OPLL_OP;

typedef struct __attribute__ ((packed, aligned(4))) OPLL_LFO_s {
    uint32	cps;
    uint32	spd;
    int32	cycles;
    uint32	adr;
    uint32	adrmask;
    uint32	*table;
    uint32	output;
} OPLL_LFO;

typedef struct __attribute__ ((packed, aligned(4))) OPLL_CH_s {
    uint32 cps;
    int32 cycles;
    uint32 fbbuf[2];
    uint32 output;
    OPLL_OP op[2];
    uint32 mastervolume;
    uint8 tone[8];
    uint8 key;
    uint8 toneno;
    uint8 freql;
    uint8 freqh;
    uint8 fb;
    uint8 update;
} OPLL_CH;

typedef struct __attribute__ ((packed, aligned(4))) OPLLSOUND_s {
    OPLL_CH ch[6];
    OPLL_LFO lfo[2];
    uint32 mastervolume;
    uint8 usertone[8];
    uint8 adr;
    uint8 rhythmc;
    uint8 toneupdate;
} OPLLSOUND;

/* ------------------------------------------------------------ */
/* FDS Sound struct */

typedef struct __attribute__ ((packed, aligned(4))) FDS_FMOP_s {
    uint32 wave[0x40];
    uint32 envspd;
    int32 envphase;
    uint32 envout;
    uint32 outlvl;

    uint32 phase;
    uint32 spd;
    uint32 volume;
    int32 sweep;

    uint8 enable;
    uint8 envmode;

    int32 timer;
    uint32 last_spd;
} FDS_FMOP;

typedef struct __attribute__ ((packed, aligned(4))) FDSSOUND_s {
    uint32 cps;
    int32 cycles;
    uint32 mastervolume;
    int32 output;
    int32 fade;

    FDS_FMOP op[2];

    uint32 waveaddr;
    uint8 mute;
    uint8 reg[0x10];
    uint8 reg_cur[0x10];
} FDSSOUND;

/* ------------------------------------------------------------ */
/* MMC5 Sound struct */

typedef struct __attribute__ ((packed, aligned(4))) MMC5_SQUARE_s {
    uint32 cps;
    int32 cycles;
    int32 sweepphase;
    int32 envphase;

    uint32 spd;
    uint32 envspd;
    uint32 sweepspd;

    uint32 length;
    uint32 freq;
    uint32 mastervolume;
    uint32 release;

    uint8 regs[4];
    uint8 update;
    uint8 key;
    uint8 adr;
    uint8 envadr;
    uint8 duty;
} MMC5_SQUARE;

typedef struct __attribute__ ((packed, aligned(4))) MMC5_DA_s {
    int32 output;
    uint8 key;
} MMC5_DA;

typedef struct __attribute__ ((packed, aligned(4))) MMC5SOUND_s {
    MMC5_SQUARE square[2];
    MMC5_DA da;
} MMC5SOUND;

/* ------------------------------------------------------------ */
/* N106 Sound struct */

typedef struct __attribute__ ((packed, aligned(4))) N106_WM_s {
    uint32 logvol;
    int32 cycles;
    uint32 spd;
    uint32 phase;
    uint32 tlen;
    uint8 tadr;
} N106_WM;

typedef struct __attribute__ ((packed, aligned(4))) N106SOUND_s {
    uint32 cps;
    uint32 mastervolume;

    N106_WM ch[8];

    uint8 addressauto;
    uint8 address;
    uint8 chinuse;

    uint32 tone[0x100];	/* TONE DATA */
    uint8 data[0x80];
} N106SOUND;

/* ------------------------------------------------------------ */
/* FME7 Sound struct */

typedef struct __attribute__ ((packed, aligned(4))) PSG_SQUARE_s {
    uint32 cps;
    int32 cycles;
    uint32 spd;
    uint8 regs[3];
    uint8 update;
    uint8 adr;
    uint8 key;
} PSG_SQUARE;

typedef struct __attribute__ ((packed, aligned(4))) PSG_NOISE_s {
    uint32 cps;
    int32 cycles;
    uint32 spd;
    uint32 noiserng;
    uint8 regs[1];
    uint8 update;
    uint8 noiseout;
} PSG_NOISE;

typedef struct __attribute__ ((packed, aligned(4))) PSG_ENVELOPE_s {
    uint32 cps;
    int32 cycles;
    uint32 spd;
    uint32 envout;
    int8 *adr;
    uint8 regs[3];
    uint8 update;
} PSG_ENVELOPE;

typedef struct __attribute__ ((packed, aligned(4))) PSGSOUND_s {
    PSG_SQUARE square[3];
    PSG_ENVELOPE envelope;
    PSG_NOISE noise;
    uint32 mastervolume;
    uint32 adr;
} PSGSOUND;

/* ------------------------------------------------------------ */
/* APU Sound struct */

enum {
    APU_FILTER_NONE,
    APU_FILTER_LOWPASS,
    APU_FILTER_WEIGHTED
};

#ifdef APU_YANO
enum {
    APUMODE_IDEAL_TRIANGLE,
    APUMODE_SMOOTH_ENVELOPE,
    APUMODE_SMOOTH_SWEEP
};
#endif /* APU_YANO */

#if 0
typedef struct {
    uint32 min_range, max_range;
    uint8 (*read_func)(uint32 address);
} apu_memread;

typedef struct {
    uint32 min_range, max_range;
    void (*write_func)(uint32 address, uint8 value);
} apu_memwrite;
#else
typedef uint8 (*apu_memread)(uint32 address);
typedef void (*apu_memwrite)(uint32 address, uint8 value);
#endif

/* APU queue structure */
#define  APUQUEUE_SIZE  8192
#define  APUQUEUE_MASK  (APUQUEUE_SIZE - 1)

/* apu ring buffer member */
typedef struct __attribute__ ((packed, aligned(4))) apudata_s {
    uint32 timestamp, address;
    uint8 value;
} apudata_t;

typedef struct __attribute__ ((packed, aligned(4))) apu_s {
    nes_apu_exsound_type_t exsound_type;
    nes_apu_exsound_t exsound;    
    APUSOUND apus;
    VRC6SOUND vrc6s;
    OPLLSOUND ym2413s;
    FDSSOUND fdssound;
    MMC5SOUND mmc5;
    N106SOUND n106s;
    PSGSOUND psg;
    
    uint8 enable_reg;
    uint8 enable_reg_cur;
    
    apudata_t queue[APUQUEUE_SIZE];
    int q_head, q_tail;
    
    /* for ExSound */
    apudata_t ex_queue[APUQUEUE_SIZE];
    int ex_q_head, ex_q_tail;
    int exsound_enable;
    
    uint32 elapsed_cycles;
    
    int32 cycle_rate;
    int sample_rate;
    int sample_bits;
    int refresh_rate;
} apu_t;

extern apu_t *apu;

/* Function prototypes */
apu_t *apu_getcontext(void);
void apu_setcontext(apu_t *src_apu);

apu_t *apu_create(int sample_rate, int refresh_rate, int frag_size,
                         int sample_bits);
void apu_destroy(apu_t **apu);
void apu_setparams(int sample_rate, int refresh_rate, int sample_bits);
void apu_set_exsound(nes_apu_exsound_type_t ex_type);
void apu_set_exsound_enable(int enable);

void apu_process(void *buffer, int num_samples);
void apu_reset(void);

#ifdef APU_YANO
extern void apu_setmode(int item, int mode);
#endif /* APU_YANO */

uint8 apu_read(uint32 address);
void apu_write(uint32 address, uint8 value);

uint8 ex_read(uint32 address);
void ex_write(uint32 address, uint8 value);

void apu_write_cur(uint32 address, uint8 value);
void sync_apu_register();
boolean sync_dmc_register(uint32 cpu_cycles);

#endif /* _NES_APU_H_ */