// license:BSD-3-Clause
// copyright-holders:Fabio Priuli
/***********************************************************************************************************


 PC-Engine & Turbografx-16 HuCard emulation


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


#include "emu.h"
#include "pce_rom.h"



//-------------------------------------------------
//  pce_rom_device - constructor
//-------------------------------------------------

DEFINE_DEVICE_TYPE(PCE_ROM_STD,       pce_rom_device,       "pce_rom",       "PCE/TG16 HuCards")
DEFINE_DEVICE_TYPE(PCE_ROM_POPULOUS,  pce_populous_device,  "pce_populous",  "PCE Populous HuCard")
DEFINE_DEVICE_TYPE(PCE_ROM_SF2,       pce_sf2_device,       "pce_sf2",       "PCE Street Fighter 2 CE HuCard")
DEFINE_DEVICE_TYPE(PCE_ROM_TENNOKOE,  pce_tennokoe_device,  "pce_tennokoe",  "PCE Tennokoe Bank HuCard")


pce_rom_device::pce_rom_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, type, tag, owner, clock)
	, device_pce_cart_interface(mconfig, *this)
{
}

pce_rom_device::pce_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: pce_rom_device(mconfig, PCE_ROM_STD, tag, owner, clock)
{
}

pce_populous_device::pce_populous_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: pce_rom_device(mconfig, PCE_ROM_POPULOUS, tag, owner, clock)
{
}

pce_sf2_device::pce_sf2_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: pce_rom_device(mconfig, PCE_ROM_SF2, tag, owner, clock)
	, m_rom_bank(*this, "rom_bank")
{
}

pce_tennokoe_device::pce_tennokoe_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: pce_rom_device(mconfig, PCE_ROM_TENNOKOE, tag, owner, clock)
	, device_nvram_interface(mconfig, *this)
{
}


//-------------------------------------------------
//  mapper specific start/reset
//-------------------------------------------------


void pce_sf2_device::device_reset()
{
	m_rom_bank->set_entry(0);
}

void pce_tennokoe_device::device_start()
{
	save_item(NAME(m_bram));
	save_item(NAME(m_bram_locked));
}

void pce_tennokoe_device::device_reset()
{
	m_bram_locked = 1;
}

// Tennokoe Bank is a special HuCard containing x4 Backup RAM banks,
// the software can transfer
void pce_tennokoe_device::nvram_default()
{
	memset(m_bram, 0xff, m_bram_size);
	// easter egg: load copy of a BRAM (debug leftover?) inside bank 4.
	// Contains 14 save blocks, mostly at the end of the various games.
	// Not entirely correct but not incorrect as well to just unlock these saves for public use,
	// for testing reasons and for a slim chance of actually be supposed as default data in the bank(s)
	// File list:
	// 001 NEUTOPIA1
	// 002 MOTURBO-1
	// 003 MOMODE2-1
	// 004 MOMOKATSU.
	// 005 BOMBERMAN1
	// 006 POPULOUS.
	// 007 ADVENTURE1 (Starts in HuMan form at the village with 983040 gold, basically before the last dungeon);
	// 008 TENGAI1
	// 009 COBRA1
	// 010 YS.DATA.01 (right before the switch between Ys 1 and 2, read book to move on);
	// 011 YS.DATA.02
	// 012 YS3.DAT.01 (right before the end, go to the right bridge for the ending);
	// 013 URUSEI1
	// 014 MITUBATI1
	// TODO: document the other saves.
	memcpy(m_bram + 0x1800, m_rom + 0x8800, 0x800);
}

bool pce_tennokoe_device::nvram_read(util::read_stream &file)
{
	auto const [err, actual] = read(file, m_bram, m_bram_size);
	return !err && (actual == m_bram_size);
}

bool pce_tennokoe_device::nvram_write(util::write_stream &file)
{
	auto const [err, actual] = write(file, m_bram, m_bram_size);
	return !err;
}

/*-------------------------------------------------
 mapper specific handlers
 -------------------------------------------------*/

uint8_t pce_rom_device::rom_r(offs_t offset)
{
	int bank = offset / 0x20000;
	return m_rom[rom_bank_map[bank] * 0x20000 + (offset & 0x1ffff)];
}

void pce_rom_device::install_memory_handlers(address_space &space)
{
	space.install_read_handler(0x00000, 0xfffff, emu::rw_delegate(*this, FUNC(pce_rom_device::rom_r)));
}


void pce_populous_device::install_memory_handlers(address_space &space)
{
	pce_rom_device::install_memory_handlers(space);
	space.install_ram(0x80000, 0x87fff, &m_ram[0]);
}


void pce_sf2_device::bank_w(offs_t offset, uint8_t data)
{
	m_rom_bank->set_entry(offset & 3);
}


void pce_sf2_device::install_memory_handlers(address_space &space)
{
	m_rom_bank->configure_entries(0, 4, m_rom + 0x80000, 0x80000);
	space.install_rom(0x000000, 0x07ffff, m_rom);
	space.install_read_bank(0x080000, 0x0fffff, m_rom_bank);
	space.install_write_handler(0x001ff0, 0x001ff3, emu::rw_delegate(*this, FUNC(pce_sf2_device::bank_w)));
}


uint8_t pce_tennokoe_device::bram_r(offs_t offset)
{
	if (m_bram_locked)
		return 0xff;
	else
		return m_bram[offset & (m_bram_size-1)];
}

void pce_tennokoe_device::bram_w(offs_t offset, uint8_t data)
{
	if (!m_bram_locked)
		m_bram[offset & (m_bram_size-1)] = data;
}

void pce_tennokoe_device::bram_lock_w(offs_t offset, uint8_t data)
{
	// TODO: lock/unlock mechanism is a complete guess, needs real HW study
	m_bram_locked = (data == 0);
}

void pce_tennokoe_device::install_memory_handlers(address_space &space)
{
	space.install_rom(0x000000, 0x07ffff, m_rom);
	space.install_readwrite_handler(0x080000, 0x081fff, 0, 0x00e000, 0, emu::rw_delegate(*this, FUNC(pce_tennokoe_device::bram_r)), emu::rw_delegate(*this, FUNC(pce_tennokoe_device::bram_w)));
	// (writes to ports $c0000, $d0000, $f0000)
	space.install_write_handler(0x0f0000, 0x0f0000, 0, 0x00ffff, 0, emu::rw_delegate(*this, FUNC(pce_tennokoe_device::bram_lock_w)));
}

