// license:GPL-2.0+
// copyright-holders:Matthew Conte
/*****************************************************************************

  MAME/MESS NES APU CORE

  Based on the Nofrendo/Nosefart NES RP2A03 sound emulation core written by
  Matthew Conte (matt@conte.com) and redesigned for use in MAME/MESS by
  Who Wants to Know? (wwtk@mail.com)

  This core is written with the advise and consent of Matthew Conte and is
  released under the GNU Public License.

 *****************************************************************************

   NES_DEFS.H

   NES APU internal type definitions and constants.

 *****************************************************************************/

#ifndef MAME_SOUND_NES_DEFS_H
#define MAME_SOUND_NES_DEFS_H

#pragma once


/* APU type */
struct apu_t
{
	/* CHANNEL TYPE DEFINITIONS */

	/* Square Wave */
	struct square_t
	{
		square_t()
		{
			for (auto & elem : regs)
				elem = 0;
		}

		u8 regs[4];
		s32 vbl_length = 0;
		s32 freq = 0;
		float phaseacc = 0.0;
		float env_phase = 0.0;
		float sweep_phase = 0.0;
		u8 adder = 0;
		u8 env_vol = 0;
		bool enabled = false;
		u8 output = 0;
	};

	/* Triangle Wave */
	struct triangle_t
	{
		triangle_t()
		{
			for (auto & elem : regs)
				elem = 0;
		}

		u8 regs[4]; /* regs[1] unused */
		s32 linear_length = 0;
		bool linear_reload = false;
		s32 vbl_length = 0;
		s32 write_latency = 0;
		float phaseacc = 0.0;
		u8 adder = 0;
		bool counter_started = false;
		bool enabled = false;
		u8 output = 0;
	};

	/* Noise Wave */
	struct noise_t
	{
		noise_t()
		{
			for (auto & elem : regs)
				elem = 0;
		}

		u8 regs[4]; /* regs[1] unused */
		u16 lfsr = 1;
		s32 vbl_length = 0;
		float phaseacc = 0.0;
		float env_phase = 0.0;
		u8 env_vol = 0;
		bool enabled = false;
		u8 output = 0;
	};

	/* DPCM Wave */
	struct dpcm_t
	{
		dpcm_t()
		{
			for (auto & elem : regs)
				elem = 0;
		}

		u8 regs[4];
		u32 address = 0;
		u32 length = 0;
		s32 bits_left = 0;
		float phaseacc = 0.0;
		u8 cur_byte = 0;
		bool enabled = false;
		bool irq_occurred = false;
		s16 vol = 0;
		u8 output = 0;
	};


	/* REGISTER DEFINITIONS */
	static constexpr unsigned WRA0    = 0x00;
	static constexpr unsigned WRA1    = 0x01;
	static constexpr unsigned WRA2    = 0x02;
	static constexpr unsigned WRA3    = 0x03;
	static constexpr unsigned WRB0    = 0x04;
	static constexpr unsigned WRB1    = 0x05;
	static constexpr unsigned WRB2    = 0x06;
	static constexpr unsigned WRB3    = 0x07;
	static constexpr unsigned WRC0    = 0x08;
	static constexpr unsigned WRC2    = 0x0A;
	static constexpr unsigned WRC3    = 0x0B;
	static constexpr unsigned WRD0    = 0x0C;
	static constexpr unsigned WRD2    = 0x0E;
	static constexpr unsigned WRD3    = 0x0F;
	static constexpr unsigned WRE0    = 0x10;
	static constexpr unsigned WRE1    = 0x11;
	static constexpr unsigned WRE2    = 0x12;
	static constexpr unsigned WRE3    = 0x13;
	static constexpr unsigned SMASK   = 0x15;
	static constexpr unsigned IRQCTRL = 0x17;

	/* Sound channels */
	square_t   squ[2];
	triangle_t tri;
	noise_t    noi;
	dpcm_t     dpcm;

	u8 step_mode = 0;
	bool frame_irq_enabled = false;
	bool frame_irq_occurred = false;
};


struct mmc5_sound_t
{
	/* CHANNEL TYPE DEFINITIONS */

	/* Square Wave */
	struct square_t
	{
		square_t()
		{
			for (auto & elem : regs)
				elem = 0;
		}

		u8 regs[4];
		s32 vbl_length = 0;
		s32 freq = 0;
		float phaseacc = 0.0;
		float env_phase = 0.0;
		u8 adder = 0;
		u8 env_vol = 0;
		bool enabled = false;
		u8 output = 0;
	};

	/* DPCM Wave */
	struct pcm_t
	{
		pcm_t()
		{
			for (auto & elem : regs)
				elem = 0;
		}

		u8 regs[2];
		bool irq_enabled = false;
		bool irq_line = false;
		u8 output = 0;
	};


	/* REGISTER DEFINITIONS */
	static constexpr unsigned WRA0    = 0x00;
	static constexpr unsigned WRA1    = 0x01;
	static constexpr unsigned WRA2    = 0x02;
	static constexpr unsigned WRA3    = 0x03;
	static constexpr unsigned WRB0    = 0x04;
	static constexpr unsigned WRB1    = 0x05;
	static constexpr unsigned WRB2    = 0x06;
	static constexpr unsigned WRB3    = 0x07;
	static constexpr unsigned WRE0    = 0x10;
	static constexpr unsigned WRE1    = 0x11;
	static constexpr unsigned SMASK   = 0x15;

	/* Sound channels */
	square_t   squ[2];
	pcm_t      pcm;
};


/* CONSTANTS */

/* vblank length table used for squares, triangle, noise */
static const u8 vbl_length[32] =
{
	10, 254, 20,  2, 40,  4, 80,  6, 160,  8, 60, 10, 14, 12, 26, 14,
	12,  16, 24, 18, 48, 20, 96, 22, 192, 24, 72, 26, 16, 28, 32, 30
};

/* frequency limit of square channels */
static const int freq_limit[8] =
{
	0x3FF, 0x555, 0x666, 0x71C, 0x787, 0x7C1, 0x7E0, 0x7F0,
};

// table of noise period
// each fundamental is determined as: freq = master / period / 93
static const int noise_freq[2][16] =
{
	{ 4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068 }, // NTSC
	{ 4, 8, 14, 30, 60, 88, 118, 148, 188, 236, 354, 472, 708,  944, 1890, 3778 }  // PAL
};

// dpcm (cpu) cycle period
// each frequency is determined as: freq = master / period
static const int dpcm_clocks[2][16] =
{
	{ 428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54 }, // NTSC
	{ 398, 354, 316, 298, 276, 236, 210, 198, 176, 148, 132, 118,  98, 78, 66, 50 }  // PAL
};

/* ratios of pos/neg pulse for square waves */
/* 2/16 = 12.5%, 4/16 = 25%, 8/16 = 50%, 12/16 = 75% */
static const int duty_lut[4] =
{
	0b01000000, // 01000000 (12.5%)
	0b01100000, // 01100000 (25%)
	0b01111000, // 01111000 (50%)
	0b10011111, // 10011111 (25% negated)
};

#endif // MAME_SOUND_NES_DEFS_H
