// license:BSD-3-Clause
// copyright-holders:Angelo Salese
/***************************************************************************

    Atari Jaguar "Blitter" device

    TODO:
    - Stub device, port/rewrite from jagblit;
    - actual codename/chip part number;
    - has different revs, encapsulate;

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

#include "emu.h"
#include "jag_blitter.h"



//**************************************************************************
//  GLOBAL VARIABLES
//**************************************************************************

// device type definition
DEFINE_DEVICE_TYPE(JAG_BLITTER, jag_blitter_device, "jag_blitter", "Atari Jaguar Blitter")


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

//-------------------------------------------------
//  jag_blitter_device - constructor
//-------------------------------------------------

jag_blitter_device::jag_blitter_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, JAG_BLITTER, tag, owner, clock)
	, device_memory_interface(mconfig, *this)
	, m_space_config("regs", ENDIANNESS_BIG, 32, 8, 0, address_map_constructor(FUNC(jag_blitter_device::regs_map), this))
	, m_command_latch(0)
	, m_status_idle(true)
	, m_count_lines(0)
	, m_count_pixels(0)
{
	m_a1.base = 0;
	m_a1.xstep = 0;
	m_a1.ystep = 0;
}

device_memory_interface::space_config_vector jag_blitter_device::memory_space_config() const
{
	return space_config_vector {
		std::make_pair(0, &m_space_config)
	};
}


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

void jag_blitter_device::device_start()
{
	save_item(NAME(m_status_idle));
	save_item(NAME(m_command_latch));
	save_item(NAME(m_count_lines));
	save_item(NAME(m_count_pixels));
	save_item(NAME(m_a1.base));
	save_item(NAME(m_a1.xstep));
	save_item(NAME(m_a1.ystep));

	m_command_timer = timer_alloc(0);
}


//-------------------------------------------------
//  device_reset - device-specific reset
//-------------------------------------------------

void jag_blitter_device::device_reset()
{
	m_command_latch = 0;
	m_status_idle = true;

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

//--------------------------------------------------
// core workings
//--------------------------------------------------

/***************************************************************************
    FUNCTION TABLES
***************************************************************************/

const jag_blitter_device::op_func jag_blitter_device::upda_ops[8] =
{
	&jag_blitter_device::op_nop,
	&jag_blitter_device::op_unemulated, // upda1f
	&jag_blitter_device::op_upda1,
	&jag_blitter_device::op_unemulated, // upda1 + upda1f

	&jag_blitter_device::op_unemulated, // upda2
	&jag_blitter_device::op_unemulated, // upda1f + upda2
	&jag_blitter_device::op_unemulated, // upda1 + upda2
	&jag_blitter_device::op_unemulated  // upda1 + upda1f + upda2
};

void jag_blitter_device::op_nop()
{
	// do nothing for this step
}

// common cases for unhandled, will be removed once everything is set
void jag_blitter_device::op_unemulated()
{
	throw emu_fatalerror("%s: unhandled step with command latch %08x",m_command_latch);
}

void jag_blitter_device::op_upda1()
{
	// ...

}

inline void jag_blitter_device::command_start()
{
	if (m_status_idle == false)
		throw emu_fatalerror("%s: inflight blitter trigger %08x", this->tag(), m_command_latch);
	m_status_idle = false;
	// TODO: to be removed, see below
	m_command_timer->adjust(attotime::from_ticks(m_count_lines * m_count_pixels, this->clock()));
}

inline void jag_blitter_device::command_run()
{
	// TODO: need to single step, have different timings between pixel and phrase modes,
	// calculate collision detection, delay a bit the kickoff due of bus chain requests,
	// take more time by virtue of using additional steps, be a civilian or not depending
	// of BUSHI setting

	printf("%08x\n",m_command_latch);
	// init
	m_a1.ptr = m_a1.base;

	// ...

	command_done();
}

inline void jag_blitter_device::command_done()
{
	// m_status_idle = true;
	// ...
}

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

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

void jag_blitter_device::regs_map(address_map &map)
{
	// $f02200
	map(0x00, 0x03).w(FUNC(jag_blitter_device::a1_base_w));
	map(0x10, 0x13).w(FUNC(jag_blitter_device::a1_ystep_w)).umask32(0xffff0000);
	map(0x10, 0x13).w(FUNC(jag_blitter_device::a1_xstep_w)).umask32(0x0000ffff);
	map(0x38, 0x3b).rw(FUNC(jag_blitter_device::status_r), FUNC(jag_blitter_device::command_w));
	map(0x3c, 0x3f).w(FUNC(jag_blitter_device::count_outer_w)).umask32(0xffff0000);
	map(0x3c, 0x3f).w(FUNC(jag_blitter_device::count_inner_w)).umask32(0x0000ffff);
}

void jag_blitter_device::a1_base_w(offs_t offset, u32 data, u32 mem_mask)
{
	COMBINE_DATA(&m_a1.base);
	// align to phrase
	m_a1.base &= ~7;
}

void jag_blitter_device::a1_xstep_w(offs_t offset, u16 data, u16 mem_mask)
{
	COMBINE_DATA(&m_a1.xstep);
}

void jag_blitter_device::a1_ystep_w(offs_t offset, u16 data, u16 mem_mask)
{
	COMBINE_DATA(&m_a1.ystep);
}

u32 jag_blitter_device::status_r()
{
	// TODO: stopped bit
	// TODO: diag bits 2-31
	return m_status_idle;
}

void jag_blitter_device::command_w(offs_t offset, u32 data, u32 mem_mask)
{
	COMBINE_DATA(&m_command_latch);
	// TODO: is it possible from 68k to write on this in byte units?
	// We may just do so in order to take endianness into account, or just delegate to the overlying bus framework,
	// may be a common problem with ALL video regs for that matter.
	if (ACCESSING_BITS_0_15)
		command_start();
}

void jag_blitter_device::count_outer_w(offs_t offset, u16 data, u16 mem_mask)
{
	COMBINE_DATA(&m_count_lines);
	if (m_count_lines == 0)
	{
		// according to documentation, log it out
		m_count_lines = 0x10000;
		popmessage("Blitter: line count set to max, contact MAMEdev");
	}
}

void jag_blitter_device::count_inner_w(offs_t offset, u16 data, u16 mem_mask)
{
	COMBINE_DATA(&m_count_pixels);
	if (m_count_pixels == 0)
	{
		// according to documentation, log it out
		m_count_pixels = 0x10000;
		popmessage("Blitter: pixel count set to max, contact MAMEdev");
	}
}

u32 jag_blitter_device::iobus_r(offs_t offset, u32 mem_mask)
{
	return space().read_dword(offset*4, mem_mask);
}

void jag_blitter_device::iobus_w(offs_t offset, u32 data, u32 mem_mask)
{
	space().write_dword(offset*4, data, mem_mask);
}
