// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/*********************************************************************

    saa7191.cpp

    Philips SAA7191B Digital Multistandard Colour Decoder (DMSD)

    TODO:
    - Actual functionality

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

#include "emu.h"
#include "saa7191.h"

#define LOG_UNKNOWN     (1 << 0)
#define LOG_READS       (1 << 1)
#define LOG_WRITES      (1 << 2)
#define LOG_ERRORS      (1 << 3)
#define LOG_I2C_IGNORES (1 << 4)
#define LOG_DEFAULT     (LOG_READS | LOG_WRITES | LOG_ERRORS | LOG_I2C_IGNORES | LOG_UNKNOWN)

#define VERBOSE         (LOG_DEFAULT)
#include "logmacro.h"

DEFINE_DEVICE_TYPE(SAA7191, saa7191_device, "saa7191", "Philips SAA7191 DMSD")

saa7191_device::saa7191_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, SAA7191, tag, owner, clock)
	, m_chr_in(*this)
	, m_cvbs_in(*this)
	, m_y_out(*this)
	, m_uv_out(*this)
	, m_hs_out(*this)
	, m_vs_out(*this)
{
}

void saa7191_device::device_start()
{
	save_item(NAME(m_status));
	save_item(NAME(m_regs));
	save_item(NAME(m_i2c_write_addr));
	save_item(NAME(m_i2c_read_addr));
	save_item(NAME(m_i2c_subaddr));
	save_item(NAME(m_i2c_state));

	m_chr_in.resolve_safe(0);
	m_cvbs_in.resolve_safe(0);

	m_y_out.resolve_safe();
	m_uv_out.resolve_safe();
	m_hs_out.resolve_safe();
	m_vs_out.resolve_safe();

	m_input_clock = timer_alloc(TIMER_INPUT_CLOCK);
	m_input_clock->adjust(attotime::never);
}

void saa7191_device::device_reset()
{
	m_status = 0;
	memset(m_regs, 0, sizeof(uint8_t) * REG_COUNT);

	m_i2c_write_addr = 0x8a;
	m_i2c_read_addr = 0x8b;
	m_i2c_subaddr = 0x00;
	m_i2c_state = I2C_STATE_IDLE;

	m_input_clock->adjust(attotime::never);
}

void saa7191_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
}

void saa7191_device::i2c_data_w(uint8_t data)
{
	switch (m_i2c_state)
	{
	case I2C_STATE_IDLE:
		if (data == m_i2c_write_addr)
			m_i2c_state = I2C_STATE_SUBADDR_WRITE;
		else if (data == m_i2c_read_addr)
			m_i2c_state = I2C_STATE_SUBADDR_READ;
		else
			LOGMASKED(LOG_I2C_IGNORES, "I2C idle, address %02x ignored (mine are R:%02x/W:%02x)\n", data, m_i2c_read_addr, m_i2c_write_addr);
		break;
	case I2C_STATE_SUBADDR_WRITE:
		m_i2c_subaddr = data;
		m_i2c_state = I2C_STATE_DATA_WRITE;
		break;
	case I2C_STATE_SUBADDR_READ:
		m_i2c_subaddr = data;
		m_i2c_state = I2C_STATE_DATA_READ;
		break;
	case I2C_STATE_DATA_WRITE:
		reg_w(data);
		m_i2c_subaddr = (m_i2c_subaddr + 1) % REG_COUNT;
		break;
	case I2C_STATE_DATA_READ:
		LOGMASKED(LOG_ERRORS, "I2C is expecting a data read, but data was written, returning to idle\n");
		m_i2c_state = I2C_STATE_IDLE;
		break;
	default:
		LOGMASKED(LOG_ERRORS, "Unknown I2C state %d, returning to idle\n", m_i2c_state);
		m_i2c_state = I2C_STATE_IDLE;
		break;
	}
}

uint8_t saa7191_device::i2c_data_r()
{
	if (m_i2c_state != I2C_STATE_DATA_READ)
	{
		LOGMASKED(LOG_ERRORS, "i2c_data_r called, but we are in state %d and not expecting a data read, returning to idle\n", m_i2c_state);
		m_i2c_state = I2C_STATE_IDLE;
		return 0;
	}
	if (m_i2c_subaddr == 0x01)
	{
		LOGMASKED(LOG_READS, "i2c_data_r: Status = %02x\n", m_status);
		m_i2c_subaddr = (m_i2c_subaddr + 1) % REG_COUNT;
		return m_status;
	}
	LOGMASKED(LOG_UNKNOWN, "i2c_data_r: Unknown Sub-Address %02x, returning 0\n", m_i2c_subaddr);
	m_i2c_subaddr = (m_i2c_subaddr + 1) % REG_COUNT;
	return 0;
}

WRITE_LINE_MEMBER(saa7191_device::i2c_stop_w)
{
	if (state)
		m_i2c_state = I2C_STATE_IDLE;
}

WRITE_LINE_MEMBER(saa7191_device::iicsa_w)
{
	m_i2c_write_addr = state ? 0x8e : 0x8a;
	m_i2c_read_addr = m_i2c_write_addr | 1;
}

void saa7191_device::reg_w(uint8_t data)
{
	if (m_i2c_subaddr < REG_COUNT)
	{
		m_regs[m_i2c_subaddr] = data;
	}

	switch (m_i2c_subaddr)
	{
	case REG_IDEL:
		LOGMASKED(LOG_WRITES, "i2c_data_w: Increment delay = %02x\n", data);
		break;
	case REG_HSYB:
		LOGMASKED(LOG_WRITES, "i2c_data_w: H sync begin, 50Hz = %02x\n", data);
		break;
	case REG_HSYS:
		LOGMASKED(LOG_WRITES, "i2c_data_w: H sync stop, 50Hz = %02x\n", data);
		break;
	case REG_HCLB:
		LOGMASKED(LOG_WRITES, "i2c_data_w: H clamp begin, 50Hz = %02x\n", data);
		break;
	case REG_HCLS:
		LOGMASKED(LOG_WRITES, "i2c_data_w: H clamp stop, 50Hz = %02x\n", data);
		break;
	case REG_HPHI:
		LOGMASKED(LOG_WRITES, "i2c_data_w: H sync after PHI1, 50Hz = %02x\n", data);
		break;
	case REG_LUMC:
		LOGMASKED(LOG_WRITES, "i2c_data_w: Luminance control = %02x\n", data);
		break;
	case REG_HUEC:
		LOGMASKED(LOG_WRITES, "i2c_data_w: Hue control = %02x\n", data);
		break;
	case REG_CKTQ:
		LOGMASKED(LOG_WRITES, "i2c_data_w: Colour killer threshold QAM = %02x\n", data);
		break;
	case REG_CKTS:
		LOGMASKED(LOG_WRITES, "i2c_data_w: Colour killer threshold SECAM = %02x\n", data);
		break;
	case REG_PLSE:
		LOGMASKED(LOG_WRITES, "i2c_data_w: PAL switch sensitivity = %02x\n", data);
		break;
	case REG_SESE:
		LOGMASKED(LOG_WRITES, "i2c_data_w: SECAM switch sensitivity = %02x\n", data);
		break;
	case REG_GAIN:
		LOGMASKED(LOG_WRITES, "i2c_data_w: Chroma gain control settings = %02x\n", data);
		break;
	case REG_STDC:
		LOGMASKED(LOG_WRITES, "i2c_data_w: Standard/mode control = %02x\n", data);
		break;
	case REG_IOCK:
		LOGMASKED(LOG_WRITES, "i2c_data_w: I/O and clock control = %02x\n", data);
		break;
	case REG_CTL1:
		LOGMASKED(LOG_WRITES, "i2c_data_w: Control #1 = %02x\n", data);
		break;
	case REG_CTL2:
		LOGMASKED(LOG_WRITES, "i2c_data_w: Control #2 = %02x\n", data);
		break;
	case REG_CHCV:
		LOGMASKED(LOG_WRITES, "i2c_data_w: Chroma gain reference = %02x\n", data);
		break;
	case REG_HS6B:
		LOGMASKED(LOG_WRITES, "i2c_data_w: H sync begin, 60Hz = %02x\n", data);
		break;
	case REG_HS6S:
		LOGMASKED(LOG_WRITES, "i2c_data_w: H sync stop, 60Hz = %02x\n", data);
		break;
	case REG_HC6B:
		LOGMASKED(LOG_WRITES, "i2c_data_w: H clamp begin, 60Hz = %02x\n", data);
		break;
	case REG_HC6S:
		LOGMASKED(LOG_WRITES, "i2c_data_w: H clamp stop, 60Hz = %02x\n", data);
		break;
	case REG_HP6I:
		LOGMASKED(LOG_WRITES, "i2c_data_w: H sync after PHI1, 60Hz = %02x\n", data);
		break;
	default:
		LOGMASKED(LOG_WRITES, "i2c_data_w: Unknown Register %02x = %02x (ignored)\n", m_i2c_subaddr, data);
		if (m_i2c_subaddr < REG_COUNT)
		{
			m_regs[m_i2c_subaddr] = 0;
		}
		break;
	}
}

