/*
	License: BSD-3-Clause
	see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details

	Copyright holder(s): cam900
	Modifiers and Contributors for Furnace: cam900, tildearrow
	Konami VRC VI sound emulation core

	It's one of NES mapper with built-in sound chip, and also one of 2 Konami VRCs with this feature. (rest one has OPLL derivatives.)

	It's also DACless like other sound chip and mapper-with-sound manufactured by konami,
	the Chips 6 bit digital sound output is needs converted to analog sound output when you it want to make some sounds, or send to sound mixer.

	Its are used for Akumajou Densetsu (Japan release of Castlevania III), Madara, Esper Dream 2.

	The chip is installed in 351951 PCB and 351949A PCB.

	351951 PCB is used exclusivly for Akumajou Densetsu, Small board has VRC VI, PRG and CHR ROM.
	- It's configuration also calls VRC6a, iNES mapper 024.

	351949A PCB is for Last 2 titles with VRC VI, Bigger board has VRC VI, PRG and CHR ROM, and Battery Backed 8K x 8 bit SRAM.
	- Additionally, It's PRG A0 and A1 bit to VRC VI input is swapped, compare to above.
	- It's configuration also calls VRC6b, iNES mapper 026.

	The chip itself has 053328, 053329, 053330 Revision, but Its difference between revision is unknown.

	Like other mappers for NES, It has internal timer - Its timer can be sync with scanline like other Konami mapper in this era.

	Register layout (Sound and Timer only; 351951 PCB case, 351949A swaps xxx1 and xxx2):

	Address Bits      Description
	        7654 3210

	9000-9002 Pulse 1

	9000    x--- ---- Pulse 1 Duty ignore
	        -xxx ---- Pulse 1 Duty cycle
	        ---- xxxx Pulse 1 Volume
	9001    xxxx xxxx Pulse 1 Pitch bit 0-7
	9002    x--- ---- Pulse 1 Enable
	        ---- xxxx Pulse 1 Pitch bit 8-11

	9003 Sound control

	9003    ---- -x-- 4 bit Frequency mode
	        ---- -0x- 8 bit Frequency mode
	        ---- ---x Halt

	a000-a002 Pulse 2

	a000    x--- ---- Pulse 2 Duty ignore
	        -xxx ---- Pulse 2 Duty cycle
	        ---- xxxx Pulse 2 Volume
	a001    xxxx xxxx Pulse 2 Pitch bit 0-7
	a002    x--- ---- Pulse 2 Enable
	        ---- xxxx Pulse 2 Pitch bit 8-11

	b000-b002 Sawtooth

	b000    --xx xxxx Sawtooth Accumulate Rate
	b001    xxxx xxxx Sawtooth Pitch bit 0-7
	b002    x--- ---- Sawtooth Enable
	        ---- xxxx Sawtooth Pitch bit 8-11

	f000-f002 IRQ Timer

	f000    xxxx xxxx IRQ Timer latch
	f001    ---- -0-- Sync with scanline
	        ---- --x- Enable timer
	        ---- ---x Enable timer after IRQ Acknowledge
	f002    ---- ---- IRQ Acknowledge

	Frequency calculations:

	if 4 bit Frequency Mode then
		Frequency: Input clock / (bit 8 to 11 of Pitch + 1)
	end else if 8 bit Frequency Mode then
		Frequency: Input clock / (bit 4 to 11 of Pitch + 1)
	end else then
		Frequency: Input clock / (Pitch + 1)
	end
*/

#include "vrcvi.hpp"
#include <string.h>

void vrcvi_core::tick()
{
	m_out = 0;
	if (!m_control.m_halt) // Halt flag
	{
		// tick per each clock
    int elemIndex=0;
		for (auto & elem : m_pulse)
		{
			if (elem.tick()) {
				m_out += elem.m_control.m_volume; // add 4 bit pulse output
        m_ch_out[elemIndex]=elem.m_control.m_volume;
      } else {
        m_ch_out[elemIndex]=0;
      }
      elemIndex++;
		}
		if (m_sawtooth.tick()) {
			m_out += bitfield(m_sawtooth.m_accum, 3, 5); // add 5 bit sawtooth output
      m_ch_out[2]=bitfield(m_sawtooth.m_accum, 3, 5);
    } else {
      m_ch_out[2]=0;
    }
	}
	if (m_timer.tick())
		m_timer.counter_tick();
}

void vrcvi_core::reset()
{
	for (auto & elem : m_pulse)
		elem.reset();

	m_sawtooth.reset();
	m_timer.reset();
	m_control.reset();
	m_out = 0;
        memset(m_ch_out,0,sizeof(m_ch_out));
}

bool vrcvi_core::alu_t::tick()
{
	if (m_divider.m_enable)
	{
		const u16 temp = m_counter;
		// post decrement
		if (bitfield(m_host.m_control.m_shift, 1))
		{
			m_counter = (m_counter & 0x0ff) | (bitfield(bitfield(m_counter, 8, 4) - 1, 0, 4) << 8);
			m_counter = (m_counter & 0xf00) | (bitfield(bitfield(m_counter, 0, 8) - 1, 0, 8) << 0);
		}
		else if (bitfield(m_host.m_control.m_shift, 0))
		{
			m_counter = (m_counter & 0x00f) | (bitfield(bitfield(m_counter, 4, 8) - 1, 0, 8) << 4);
			m_counter = (m_counter & 0xff0) | (bitfield(bitfield(m_counter, 0, 4) - 1, 0, 4) << 0);
		}
		else
			m_counter = bitfield(bitfield(m_counter, 0, 12) - 1, 0, 12);

		// carry handling
		bool carry = bitfield(m_host.m_control.m_shift, 1) ? (bitfield(temp, 8, 4) == 0) : 
		            (bitfield(m_host.m_control.m_shift, 0) ? (bitfield(temp, 4, 8) == 0) :
								(bitfield(temp, 0, 12) == 0));
		if (carry)
			m_counter = m_divider.m_divider;

		return carry;
	}
	return false;
}

bool vrcvi_core::pulse_t::tick()
{
	if (!m_divider.m_enable)
		return false;

	if (vrcvi_core::alu_t::tick())
		m_cycle = bitfield(m_cycle + 1, 0, 4);

	return m_control.m_mode ? true : ((m_cycle > m_control.m_duty) ? true : false);
}

bool vrcvi_core::sawtooth_t::tick()
{
	if (!m_divider.m_enable)
		return false;

	if (vrcvi_core::alu_t::tick())
	{
		if (bitfield(m_cycle++, 0)) // Even step only
			m_accum += m_rate;
		if (m_cycle >= 14) // Reset accumulator at every 14 cycles
		{
			m_accum = 0;
			m_cycle = 0;
		}
	}
	return (m_accum == 0) ? false : true;
}

void vrcvi_core::alu_t::reset()
{
	m_divider.reset();
	m_counter = 0;
	m_cycle = 0;
}

void vrcvi_core::pulse_t::reset()
{
	vrcvi_core::alu_t::reset();
	m_control.reset();
}

void vrcvi_core::sawtooth_t::reset()
{
	vrcvi_core::alu_t::reset();
	m_rate = 0;
	m_accum = 0;
}

bool vrcvi_core::timer_t::tick()
{
	if (m_timer_control.m_enable)
	{
		if (!m_timer_control.m_sync) // scanline sync mode
		{
			m_prescaler -= 3;
			if (m_prescaler <= 0)
			{
				m_prescaler += 341;
				return true;
			}
		}
	}
	return (m_timer_control.m_enable && m_timer_control.m_sync) ? true : false;
}

void vrcvi_core::timer_t::counter_tick()
{
	if (bitfield(++m_counter, 0, 8) == 0)
	{
		m_counter = m_counter_latch;
		irq_set();
	}
}

void vrcvi_core::timer_t::reset()
{
	m_timer_control.reset();
	m_prescaler = 341;
	m_counter = m_counter_latch = 0;
	irq_clear();
}

// Accessors

void vrcvi_core::alu_t::divider_t::write(bool msb, u8 data)
{
	if (msb)
	{
		m_divider = (m_divider & ~0xf00) | (bitfield<u32>(data, 0, 4) << 8);
		m_enable = bitfield(data, 7);
	}
	else
		m_divider = (m_divider & ~0x0ff) | data;
}


void vrcvi_core::pulse_w(u8 voice, u8 address, u8 data)
{
	pulse_t &v = m_pulse[voice];
	switch (address)
	{
		case 0x00: // Control - 0x9000 (Pulse 1), 0xa000 (Pulse 2)
			v.m_control.m_mode = bitfield(data, 7);
			v.m_control.m_duty = bitfield(data, 4, 3);
			v.m_control.m_volume = bitfield(data, 0, 4);
			break;
		case 0x01: // Pitch LSB - 0x9001/0x9002 (Pulse 1), 0xa001/0xa002 (Pulse 2)
			v.m_divider.write(false, data);
			break;
		case 0x02: // Pitch MSB, Enable/Disable - 0x9002/0x9001 (Pulse 1), 0xa002/0xa001 (Pulse 2)
			v.m_divider.write(true, data);
			if (!v.m_divider.m_enable) // Reset duty cycle
				v.m_cycle = 0;
			break;
	}
}

void vrcvi_core::saw_w(u8 address, u8 data)
{
	switch (address)
	{
		case 0x00: // Sawtooth Accumulate - 0xb000
			m_sawtooth.m_rate = bitfield(data, 0, 6);
			break;
		case 0x01: // Pitch LSB - 0xb001/0xb002 (Sawtooth)
			m_sawtooth.m_divider.write(false, data);
			break;
		case 0x02: // Pitch MSB, Enable/Disable - 0xb002/0xb001 (Sawtooth)
			m_sawtooth.m_divider.write(true, data);
			if (!m_sawtooth.m_divider.m_enable) // Reset accumulator
				m_sawtooth.m_accum = 0;
			break;
	}
}

void vrcvi_core::timer_w(u8 address, u8 data)
{
	switch (address)
	{
		case 0x00: // Timer latch - 0xf000
			m_timer.m_counter_latch = data;
			break;
		case 0x01: // Timer control - 0xf001/0xf002
			m_timer.m_timer_control.m_sync = bitfield(data, 2);
			m_timer.m_timer_control.m_enable = bitfield(data, 1);
			m_timer.m_timer_control.m_enable_ack = bitfield(data, 0);
			if (m_timer.m_timer_control.m_enable)
			{
				m_timer.m_counter = m_timer.m_counter_latch;
				m_timer.m_prescaler = 341;
			}
			m_timer.irq_clear();
			break;
		case 0x02: // IRQ Acknowledge - 0xf002/0xf001
			m_timer.irq_clear();
			m_timer.m_timer_control.m_enable = m_timer.m_timer_control.m_enable_ack;
			break;
	}
}

void vrcvi_core::control_w(u8 data)
{
	// Global control - 0x9003
	m_control.m_halt = bitfield(data, 0);
	m_control.m_shift = bitfield(data, 1, 2);
}
