// license:BSD-3-Clause
// copyright-holders:Vas Crabb, Wilbert Pol
/***************************************************************************

 TAMA5 Mapper (Used by Tamagotchi 3)
 ============

 Cartridge contains the following chips:
 * TAMA5 Game Boy bus interface
 * TAMA6 Toshiba TMP47C243M TLCS-47 microcontroller
 * TAMA7 mask ROM
 * Toshiba TC8521AM real-time clock
 * Toshiba M62021P system reset IC with switch for memory backup

 The MCU acts as an interface between the TAMA5 and the RTC.  The MCU can
 drive a piezo speaker (in response to alarms from the RTC).  There is a
 mechanical switch that disconnects power from the piezo speaker.  The RTC,
 MCU and piezo speaker have backup power supplied by a user-replaceable
 CR2016 coin cell.

 The TAMA5 chip only responds to A15, A14, A0, and D3-D0, i.e. addresses are
 effectively masked with 0xC001, and data is effectively masked with 0x0F.

 Status: partially supported.

 The TAMA5 mapper includes a special RTC chip which communicates through the
 RAM area (0xA000-0xBFFF); most notably addresses 0xA000 and 0xA001 seem to
 be used. In this setup 0xA001 acts like a control register and 0xA000 like
 a data register.

 Accepted values by the TAMA5 control register:
 0x00 - Writing to 0xA000 will set bits 3-0 for ROM bank selection.
 0x01 - Writing to 0xA000 will set bits (7-?)4 for ROM bank selection.

 0x04 - Bits 3-0 of the value to write
 0x05 - Bits 4-7 of the value to write
 0x06 - Address control hi
        bit 0 - Bit 4 for the address
        bit 3-1 - 000 - Write a byte to the 32 byte memory. The data to be
                        written must be set in registers 0x04 (lo nibble) and
                        0x05 (hi nibble).
                - 001 - Read a byte from the 32 byte memory. The data read
                        will be available in registers 0x0C (lo nibble) and
                        0x0D (hi nibble).
                - 010 - Unknown (occurs just after having started a game and
                        entered a date) (execution at address 1A19)
                - 011 - Unknown (not encountered yet)
                - 100 - Unknown (occurs during booting a game; appears to be
                        some kind of read command as it is followed by a read
                        of the 0x0C register) (execution at address 1B5B)
                - 101 - Unknown (not encountered yet)
                - 110 - Unknown (not encountered yet)
                - 111 - Unknown (not encountered yet)
 0x07 - Address control lo
        bit 3-0 - bits 3-0 for the address

 0x0A - After writing this the lowest 2 bits of A000 determine whether the
        TAMA5 chip is ready to accept the next command. If the lowest 2 bits
        hold the value 01 then the TAMA5 chip is ready for the next command.

 0x0C - Reading from A000 will return bits 3-0 of the data
 0x0D - Reading from A000 will return bits 7-4 of the data

 0x04 - RTC controls? -> RTC/memory?
 0x05 - Write time/memomry?
 0x06 - RTC controls?
 0x07 - RTC controls?

 Unknown sequences:
 During booting a game (1B5B:
 04 <- 00, 06 <- 08, 07 <- 01, followed by read 0C
 when value read from 0C equals 00 followed by the sequence:
 04 <- 01, 06 <- 08, 07 <- 01, followed by read 0C
 the value read from 0C is checked for non-zero, don't know the consequences for either
 yet.

 Initialization after starting a game:
 At address 1A19:
 06 <- 05, 07 <- 02, followed by read 0C, if != 0F => OK, otherwise do something.

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

#include "emu.h"
#include "tama5.h"

#include "cartbase.h"

//#define VERBOSE 1
//#define LOG_OUTPUT_FUNC osd_printf_info
#include "logmacro.h"


namespace bus::gameboy {

namespace {

class tama5_device : public mbc_device_base
{
public:
	static constexpr feature_type unemulated_features() { return feature::SOUND; }

	tama5_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);

	virtual std::error_condition load(std::string &message) override ATTR_COLD;

protected:
	virtual void device_start() override ATTR_COLD;
	virtual void device_reset() override ATTR_COLD;

private:
	u8 data_r(address_space &space);
	void data_w(u8 data);
	void command_w(u8 data);

	u8 m_bank_sel_rom;

	u8 m_registers[0x20];
	u8 m_response;
	u8 m_command;
	u8 m_data;
	u8 m_address;
};


tama5_device::tama5_device(
		machine_config const &mconfig,
		char const *tag,
		device_t *owner,
		u32 clock) :
	mbc_device_base(mconfig, GB_ROM_TAMA5, tag, owner, clock),
	m_bank_sel_rom(0U),
	m_response(0U),
	m_command(0U),
	m_data(0U),
	m_address(0U)
{
}


std::error_condition tama5_device::load(std::string &message)
{
	// set up ROM
	set_bank_bits_rom(5);
	if (!check_rom(message))
		return image_error::BADSOFTWARE;
	install_rom();

	// install I/O
	cart_space()->install_read_handler(0xa000, 0xa000, 0x0000, 0x1fff, 0x0000, emu::rw_delegate(*this, FUNC(tama5_device::data_r)));
	cart_space()->install_write_handler(0xa000, 0xa000, 0x0000, 0x1ffe, 0x0000, emu::rw_delegate(*this, FUNC(tama5_device::data_w)));
	cart_space()->install_write_handler(0xa001, 0xa001, 0x0000, 0x1ffe, 0x0000, emu::rw_delegate(*this, FUNC(tama5_device::command_w)));

	// all good
	return std::error_condition();
}


void tama5_device::device_start()
{
	mbc_device_base::device_start();

	save_item(NAME(m_bank_sel_rom));
	save_item(NAME(m_registers));
	save_item(NAME(m_response));
	save_item(NAME(m_command));
	save_item(NAME(m_data));
	save_item(NAME(m_address));
}


void tama5_device::device_reset()
{
	mbc_device_base::device_reset();

	m_bank_sel_rom = 1;

	std::fill(std::begin(m_registers), std::end(m_registers), 0xffU);
	m_response = 0xffU;
	m_command = 0x00U;
	m_data = 0x00U;
	m_address = 0x00U;

	set_bank_rom(m_bank_sel_rom);
}


u8 tama5_device::data_r(address_space &space)
{
	return (space.unmap() & 0xf0) | (m_response & 0x0f);
}


void tama5_device::data_w(u8 data)
{
	switch (m_command)
	{
	case 0x00:      // ROM bank low
		m_bank_sel_rom = (m_bank_sel_rom & 0x10) | (data & 0x0f);
		set_bank_rom(m_bank_sel_rom);
		break;
	case 0x01:      // ROM bank high
		m_bank_sel_rom = (m_bank_sel_rom & 0x0f) | ((data & 0x01) << 4);
		set_bank_rom(m_bank_sel_rom);
		break;
	case 0x04:      // value low
		m_data = (m_data & 0xf0) | (data & 0x0f);
		break;
	case 0x05:      // value high
		m_data = (m_data & 0x0f) | (data << 4);
		break;
	case 0x06:      // address high
		m_address = (m_address & 0x0f) | (data << 4);
		break;
	case 0x07:      // address low/trigger
		m_address = (m_address & 0xf0) | (data & 0x0f);
		switch (m_address & 0xe0)
		{
		case 0x00:
			LOG("%s: write register 0x%02X = 0x%02X\n", machine().describe_context(), m_address & 0x1f, m_data);
			m_registers[m_address & 0x1f] = m_data;
			break;
		case 0x20:
			LOG("%s: read register 0x%02X\n", machine().describe_context(), m_address & 0x1f);
			m_data = m_registers[m_address & 0x1f];
			break;
		case 0x40:  // unknown - some kind of read
			if ((m_address & 0x1f) == 0x12)
				m_data = 0xff;
			[[fallthrough]];
		case 0x80:  // unknown - some kind of read when 0x07=0x01, or write when 0x07=0x00 or 0x07=0x02
		default:
			logerror("%s: Unknown access 0x%02X = 0x%02X\n", machine().describe_context(), m_address, m_data);
		}
	}
}


void tama5_device::command_w(u8 data)
{
	switch (data)
	{
	case 0x00:      // ROM bank low
	case 0x01:      // ROM bank high
	case 0x04:      // value low
	case 0x05:      // value high
	case 0x06:      // address high
	case 0x07:      // address low/trigger
		break;
	case 0x0a:      // ready status in least significant bit
		m_response = 0x01U;
		break;
	case 0x0c:      // read value low
		m_response = m_data & 0x0f;
		break;
	case 0x0d:      // read value high
		m_response = m_data >> 4;
		break;
	default:
		logerror("%s: Unknown command 0x%02X\n", machine().describe_context(), data);
	}
	m_command = data;
}

} // anonymous namespace

} // namespace bus::gameboy


DEFINE_DEVICE_TYPE_PRIVATE(GB_ROM_TAMA5, device_gb_cart_interface, bus::gameboy::tama5_device, "gb_rom_tama5", "Game Boy Bandai Tamagotchi Cartridge")
