// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/*********************************************************************

    Acorn Battery Management Unit

    The BMU is actually a 4-bit Hitachi HD404304F MCU, marked as
    BMU 0290032.

    TODO:
    - configurable status
    - callback when battery low

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

#include "emu.h"
#include "acorn_bmu.h"

#define LOG_DATA (1U << 1)
#define LOG_LINE (1U << 2)

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


DEFINE_DEVICE_TYPE(ACORN_BMU, acorn_bmu_device, "acorn_bmu", "Acorn Battery Management Unit")


//**************************************************************************
//  LIVE DEVICE
//**************************************************************************

//-------------------------------------------------
//  acorn_bmu_device - constructor
//-------------------------------------------------

acorn_bmu_device::acorn_bmu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, ACORN_BMU, tag, owner, clock)
	, m_slave_address(BMU_SLAVE_ADDRESS)
	, m_scl(0)
	, m_sdaw(0)
	, m_sdar(1)
	, m_state(STATE_IDLE)
	, m_bits(0)
	, m_shift(0)
	, m_devsel(0)
	, m_register(0)
	, m_estimate(0)
{
}

//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void acorn_bmu_device::device_start()
{
	save_item(NAME(m_scl));
	save_item(NAME(m_sdaw));
	save_item(NAME(m_sdar));
	save_item(NAME(m_state));
	save_item(NAME(m_bits));
	save_item(NAME(m_shift));
	save_item(NAME(m_devsel));
	save_item(NAME(m_register));
	save_item(NAME(m_slave_address));
	save_item(NAME(m_estimate));
}


//**************************************************************************
//  READ/WRITE HANDLERS
//**************************************************************************

void acorn_bmu_device::scl_w(int state)
{
	if (m_scl != state)
	{
		m_scl = state;
		LOGMASKED(LOG_LINE, "set_scl_line %d\n", m_scl);

		switch (m_state)
		{
		case STATE_DEVSEL:
		case STATE_REGISTER:
		case STATE_DATAIN:
			if (m_bits < 8)
			{
				if (m_scl)
				{
					m_shift = ((m_shift << 1) | m_sdaw) & 0xff;
					m_bits++;
				}
			}
			else
			{
				if (m_scl)
				{
					m_bits++;
				}
				else
				{
					if (m_bits == 8)
					{
						switch (m_state)
						{
						case STATE_DEVSEL:
							m_devsel = m_shift;

							if ((m_devsel & 0xfe) != m_slave_address)
							{
								LOGMASKED(LOG_DATA, "devsel %02x: not this device\n", m_devsel);
								m_state = STATE_IDLE;
							}
							else if ((m_devsel & 1) == 0)
							{
								LOGMASKED(LOG_DATA, "devsel %02x: write\n", m_devsel);
								m_state = STATE_REGISTER;
							}
							else
							{
								LOGMASKED(LOG_DATA, "devsel %02x: read\n", m_devsel);
								m_state = STATE_READSELACK;
							}
							break;

						case STATE_REGISTER:
							m_register = m_shift;

							LOGMASKED(LOG_DATA, "register %02x\n", m_register);

							m_state = STATE_DATAIN;
							break;

						case STATE_DATAIN:
							LOGMASKED(LOG_DATA, "register[ %02x ] <- %02x\n", m_register, m_shift);

							switch (m_register)
							{
							case BMU_CHARGE_ESTIMATE:
								m_estimate = m_shift;
								break;
							}

							m_register++;
							break;
						}

						if( m_state != STATE_IDLE )
						{
							m_sdar = 0 ;
						}
					}
					else
					{
						m_bits = 0;
						m_sdar = 1;
					}
				}
			}
			break;

		case STATE_READSELACK:
			m_bits = 0;
			m_state = STATE_DATAOUT;
			break;

		case STATE_DATAOUT:
			if (m_bits < 8)
			{
				if (m_scl)
				{
					m_bits++;
				}
				else
				{
					if (m_bits == 0)
					{
						switch (m_register)
						{
						case BMU_VERSION:
							m_shift = 0x03;
							break;

						case BMU_STATUS:
							m_shift = STATUS_CHARGER_PRESENT | STATUS_BATTERY_PRESENT | STATUS_CHARGE_STATE_KNOWN | STATUS_LID_OPEN;
							break;

						case BMU_CAPACITY_USED:
							m_shift = 0x40;
							break;

						case BMU_CHARGE_ESTIMATE:
							m_shift = m_estimate;
							break;

						case BMU_COMMAND:
							m_shift = 0;
							break;
						}

						LOGMASKED(LOG_DATA, "register[ %02x ] -> %02x\n", m_register, m_shift);
						m_register++;
					}

					m_sdar = (m_shift >> 7) & 1;

					m_shift = (m_shift << 1) & 0xff;
				}
			}
			else
			{
				if (m_scl)
				{
					if (m_sdaw)
					{
						LOGMASKED(LOG_DATA, "nack\n");
						m_state = STATE_IDLE;
					}

					m_bits = 0;
				}
				else
				{
					m_sdar = 1;
				}
			}
			break;
		}
	}
}

void acorn_bmu_device::sda_w(int state)
{
	state &= 1;
	if (m_sdaw != state)
	{
		LOGMASKED(LOG_LINE, "set sda %d\n", state);
		m_sdaw = state;

		if (m_scl)
		{
			if (m_sdaw)
			{
				LOGMASKED(LOG_DATA, "stop\n");
				m_state = STATE_IDLE;
			}
			else
			{
				LOGMASKED(LOG_DATA, "start\n");
				m_state = STATE_DEVSEL;
				m_bits = 0;
			}

			m_sdar = 1;
		}
	}
}

int acorn_bmu_device::sda_r()
{
	int res = m_sdar & 1;

	LOGMASKED(LOG_LINE, "read sda %d\n", res);

	return res;
}
