// license:BSD-3-Clause
// copyright-holders:Antoine Mine
/**********************************************************************

  Copyright (C) Antoine Mine' 2008

   Hewlett Packard HP48 S/SX & G/GX/G+ and HP49 G

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

#include "emu.h"
#include "hp48_port.h"
#include "hp48.h"

#include "logmacro.h"


/* ----- card images ------ */
DEFINE_DEVICE_TYPE(HP48_PORT, hp48_port_image_device, "hp48_port_image", "HP48 memory card")

/* helper for load and create */
void hp48_port_image_device::fill_port()
{
	int size = m_port_size;
	LOG("hp48_port_image_device::fill_port: %s module=%i size=%i rw=%i\n", tag(), m_module, size, m_port_write);
	m_port_data = make_unique_clear<uint8_t[]>(2 * size);
	m_hp48->m_modules[m_module].off_mask = 2 * (( size > 128 * 1024 ) ? 128 * 1024 : size) - 1;
	m_hp48->m_modules[m_module].read     = read8sm_delegate(*m_hp48);
	m_hp48->m_modules[m_module].write    = write8sm_delegate(*m_hp48);
	m_hp48->m_modules[m_module].isnop    = m_port_write ? 0 : 1;
	m_hp48->m_modules[m_module].data     = (void*)m_port_data.get();
	m_hp48->apply_modules();
}

/* helper for start and unload */
void hp48_port_image_device::unfill_port()
{
	LOG("hp48_port_image_device::unfill_port\n");
	m_hp48->m_modules[m_module].off_mask = 0x00fff;  /* 2 KB */
	m_hp48->m_modules[m_module].read     = read8sm_delegate(*m_hp48);
	m_hp48->m_modules[m_module].write    = write8sm_delegate(*m_hp48);
	m_hp48->m_modules[m_module].data     = nullptr;
	m_hp48->m_modules[m_module].isnop    = 1;
	m_port_size                          = 0;
}


std::pair<std::error_condition, std::string> hp48_port_image_device::call_load()
{
	int size = length();
	if (size == 0) size = m_max_size; /* default size */
	LOG("hp48_port_image load: size=%i\n", size);

	/* check size */
	if ((size < 32*1024) || (size > m_max_size) || (size & (size-1)))
	{
		return std::make_pair(
				image_error::INVALIDLENGTH,
				util::string_format("Image size must be a power of two between %i and %i", 32*1024, m_max_size));
	}

	m_port_size = size;
	m_port_write = !is_readonly();
	fill_port();
	fread(m_port_data.get(), m_port_size);
	m_hp48->decode_nibble(m_port_data.get(), m_port_data.get(), m_port_size);
	return std::make_pair(std::error_condition(), std::string());
}

std::pair<std::error_condition, std::string> hp48_port_image_device::call_create(int format_type, util::option_resolution *format_options)
{
	int size = m_max_size;
	LOG("hp48_port_image create: size=%i\n", size);
	/* XXX defaults to max_size; get user-specified size instead */

	/* check size */
	/* size must be a power of 2 between 32K and max_size */
	if ((size < 32*1024) || (size > m_max_size) || (size & (size-1)))
	{
		return std::make_pair(
				image_error::INVALIDLENGTH,
				util::string_format("Image size must be a power of two between %i and %i", 32*1024, m_max_size));
	}

	m_port_size = size;
	m_port_write = true;
	fill_port();
	return std::make_pair(std::error_condition(), std::string());
}

void hp48_port_image_device::call_unload()
{
	LOG("hp48_port image unload: %s size=%i rw=%i\n", tag(), m_port_size, m_port_write);
	if (m_port_write)
	{
		m_hp48->encode_nibble(m_port_data.get(), m_port_data.get(), m_port_size);
		fseek(0, SEEK_SET);
		fwrite(m_port_data.get(), m_port_size);
	}
	m_port_data = nullptr;
	unfill_port();
	m_hp48->apply_modules();
}

void hp48_port_image_device::device_start()
{
	LOG("hp48_port_image start\n");
	unfill_port();
}

hp48_port_image_device::hp48_port_image_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, HP48_PORT, tag, owner, clock)
	, device_image_interface(mconfig, *this)
	, m_port_size(0)
	, m_port_write(false)
	, m_hp48(*this, DEVICE_SELF_OWNER)
{
}
