// license:BSD-3-Clause
// copyright-holders:Vas Crabb

#include "emu.h"
#include "upd777.h"

#include "upd777dasm.h"


#define LOG_UNHANDLED_OPS       (1U << 1)

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

DEFINE_DEVICE_TYPE(UPD777,    upd777_cpu_device,    "upd777",    "uPD777")

upd777_cpu_device::upd777_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, address_map_constructor data)
	: cpu_device(mconfig, type, tag, owner, clock)
	, m_datamem(*this, "datamem")
	, m_space_config("program", ENDIANNESS_BIG, 16, 11, -1, address_map_constructor(FUNC(upd777_cpu_device::internal_map), this))
	, m_data_config("data", ENDIANNESS_BIG, 8, 7, 0, data)
	, m_gfxdecode(*this, "gfxdecode")
	, m_palette(*this, "palette")
	, m_screen(*this, "screen")
	, m_prgregion(*this, "prg")
	, m_patregion(*this, "patterns")
	, m_port_in(*this, 0xff)
{
}

upd777_cpu_device::upd777_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
	: upd777_cpu_device(mconfig, UPD777, tag, owner, clock, address_map_constructor(FUNC(upd777_cpu_device::internal_data_map), this))
{
}

std::unique_ptr<util::disasm_interface> upd777_cpu_device::create_disassembler()
{
	return std::make_unique<upd777_disassembler>();
}

device_memory_interface::space_config_vector upd777_cpu_device::memory_space_config() const
{
	return space_config_vector{
		std::make_pair(AS_PROGRAM, &m_space_config),
		std::make_pair(AS_DATA, &m_data_config)
	};
}

void upd777_cpu_device::internal_map(address_map &map)
{
	map(0x000, 0x7ff).rom().region("prg", 0);
}

void upd777_cpu_device::internal_data_map(address_map &map)
{
	// 0x20 groups of 4 7-bit values
	// groups 0x00-0x18 are used for sprite/pattern entries with the format

	//     6543210
	// 00  yyyyyyp   (y = ypos, p = PRIO)
	// 01  xxxxxxx   (x = xpos)
	// 02  ttttttt   (t = pattern)
	// 03  YYYRGBS   (Y = , RGB = color, S=ySUB)

	map(0x00, 0x7f).ram().share("datamem");
}

void upd777_cpu_device::increment_pc()
{
	u16 lowpc = m_pc & 0x07f;
	u16 highpc = m_pc & 0x780;

	const u16 xnor = BIT(lowpc, 6) ^ BIT(lowpc, 5) ^ 1;
	lowpc = ((lowpc << 1) | xnor) & 0x7f;

	// is returning to the start of the page the correct behavior?
	if (lowpc == 0x00)
	{
		logerror("overflowing PC, returning to start of current page %03x\n", highpc);
		// pakpak runs better if you do this, but that is clearly by chance as
		// most other cases show this isn't meant to happen
		//highpc += 0x80;
		//highpc &= 0x7ff;
	}

	m_pc = highpc | lowpc;
}

void upd777_cpu_device::device_start()
{
	space(AS_PROGRAM).specific(m_space);
	space(AS_DATA).specific(m_data);

	set_icountptr(m_icount);

	state_add(UPD777_PC, "PC", m_pc);
	state_add(STATE_GENPC, "GENPC", m_pc).noshow();
	state_add(STATE_GENPCBASE, "CURPC", m_pc).noshow();

	state_add(UPD777_A1, "A1", m_a[0]);
	state_add(UPD777_A2, "A2", m_a[1]);
	state_add(UPD777_A3, "A3", m_a[2]);
	state_add(UPD777_A4, "A4", m_a[3]);

	state_add(UPD777_L, "L", m_l);
	state_add(UPD777_H, "H", m_h);

	state_add(UPD777_ADDR_STACK0, "ADDR_STACK0", m_stack[0]);
	state_add(UPD777_ADDR_STACK1, "ADDR_STACK1", m_stack[1]);
	state_add(UPD777_ADDR_STACK2, "ADDR_STACK2", m_stack[2]);
	state_add(UPD777_ADDR_STACK_POS, "ADDR_STACK_POS", m_stackpos);

	//state_add(UPD777_SKIP, "SKIP", m_skip); // will always be showing 0 as debugger doesn't hook on skipped opcodes

	save_item(NAME(m_ppc));
	save_item(NAME(m_pc));
	save_item(NAME(m_skip));
	save_item(NAME(m_a));
	save_item(NAME(m_l));
	save_item(NAME(m_ldash));
	save_item(NAME(m_x4));
	save_item(NAME(m_h));

	save_item(NAME(m_frs));
	save_item(NAME(m_fls));

	save_item(NAME(m_mode));
	save_item(NAME(m_stb));

	save_item(NAME(m_stack));
	save_item(NAME(m_stackpos));

	save_item(NAME(m_disp));
	save_item(NAME(m_gpe));
	save_item(NAME(m_kie));
	save_item(NAME(m_sme));
}

void upd777_cpu_device::device_reset()
{
	m_ppc = 0;
	m_pc = 0;

	m_a[0] = m_a[1] = m_a[2] = m_a[3] = 0;
	m_l = 0;
	m_ldash = 0;
	m_x4 = 0;
	m_h = 0;

	m_frs = 0;
	m_fls = 0;

	m_skip = 1; // the first opcode is always 'NOP' so maybe skip is 1 on startup?

	m_stack[0] = m_stack[1] = m_stack[2] = 0;
	m_stackpos = 0;

	m_disp = 0;
	m_gpe = 0;
	m_kie = 0;
	m_sme = 0;
}

inline u16 upd777_cpu_device::fetch()
{
	u16 opcode = m_space.read_word(m_pc);
	m_ppc = m_pc;
	increment_pc();
	return opcode;
}


inline void upd777_cpu_device::set_a11(int a11)
{
	m_pc = (m_pc & 0x3ff) | (a11 & 1) << 10;
}

inline void upd777_cpu_device::set_new_pc(int newpc)
{
	m_pc = newpc;
}

// L reg (lower memory pointer) is 2 bit
inline void upd777_cpu_device::set_l(int l)
{
	m_l = l & 0x3;
}

inline u8 upd777_cpu_device::get_l() const
{
	return m_l & 0x3;
}

// H reg (upper memory pointer) is 5-bit
inline void upd777_cpu_device::set_h(int h)
{
	m_h = h & 0x1f;
}

// H reg is used as upper bits of memory address
inline u8 upd777_cpu_device::get_h_shifted() const
{
	return (m_h & 0x1f) << 2;
}

inline u8 upd777_cpu_device::get_h() const
{
	return m_h & 0x1f;
}

// M is the content of memory address pointed to by H and L
inline u8 upd777_cpu_device::get_m_data()
{
	u8 addr = get_h_shifted() | get_l();
	return read_data_mem(addr & 0x7f) & 0x7f;
}

inline void upd777_cpu_device::set_m_data(u8 data)
{
	u8 addr = get_h_shifted() | get_l();
	write_data_mem(addr & 0x7f, data & 0x7f);
}

// 'A' regs are 7-bit
inline void upd777_cpu_device::set_a1(u8 data) { m_a[0] = data & 0x7f; }
inline void upd777_cpu_device::set_a2(u8 data) { m_a[1] = data & 0x7f; }
inline void upd777_cpu_device::set_a3(u8 data) { m_a[2] = data & 0x7f; }
inline void upd777_cpu_device::set_a4(u8 data) { m_a[3] = data & 0x7f; }

inline void upd777_cpu_device::set_a1_or_a2(int reg, u8 value)
{
	if (reg == 0)
		set_a1(value);
	else
		set_a2(value);
}

// 'A' regs are 7-bit
inline u8 upd777_cpu_device::get_a1() const { return m_a[0] & 0x7f; }
inline u8 upd777_cpu_device::get_a2() const { return m_a[1] & 0x7f; }
inline u8 upd777_cpu_device::get_a3() const { return m_a[2] & 0x7f; }
inline u8 upd777_cpu_device::get_a4() const { return m_a[3] & 0x7f; }

inline u8 upd777_cpu_device::get_a1_or_a2(int reg) const
{
	if (reg == 0)
		return get_a1();
	else
		return get_a2();
}

// FRS/FLS are the 2 7-bit sound registers
inline void upd777_cpu_device::set_frs(u8 frs) { m_frs = frs & 0x7f; }
inline void upd777_cpu_device::set_fls(u8 fls) { m_fls = fls & 0x7f; }

// MODE is a 7-bit register with the following format
// 6543210
// rbhpRGB (r = reverberate sound effect, b = brightness, h = hue, p = black/prio, RGB = color)
inline void upd777_cpu_device::set_mode(u8 mode) { m_mode = mode & 0x7f; }

// single bit enable registers, although they have an important effect on the K->M opcode
inline void upd777_cpu_device::set_disp(u8 data) { m_disp = data & 1; }
inline void upd777_cpu_device::set_gpe(u8 data) { m_gpe = data & 1; }
inline void upd777_cpu_device::set_kie(u8 data) { m_kie = data & 1; }
inline void upd777_cpu_device::set_sme(u8 data) { m_sme = data & 1; }

inline u8 upd777_cpu_device::get_kie() const { return m_kie & 1; }
inline u8 upd777_cpu_device::get_sme() const { return m_sme & 1; }


inline u8 upd777_cpu_device::read_data_mem(u8 addr)
{
	// data memory is 7-bit
	return m_data.read_byte(addr) & 0x7f;
}

inline void upd777_cpu_device::write_data_mem(u8 addr, u8 data)
{
	// data memory is 7-bit
	m_data.write_byte(addr, data & 0x7f);
}

inline void upd777_cpu_device::push_to_stack(u16 addr)
{
	if (m_stackpos < 3)
	{
		m_stack[m_stackpos] = addr;
		m_stackpos++;
	}
	else
	{
		logerror("attempting to push to full address stack\n");
	}
}

inline u16 upd777_cpu_device::pull_from_stack()
{
	if (m_stackpos > 0)
	{
		m_stackpos--;
		return m_stack[m_stackpos];
	}
	else
	{
		logerror("attempting to pull from empty address stack\n");
		return 0;
	}
}

void upd777_cpu_device::do_op()
{
	const u16 inst = fetch();

	if (inst >= 0b0000'1000'0000 && inst <= 0b0000'1111'1111)
	{
		// 080 - 0ff Skip if (M[H[5:1],L[2:1]][7:1]-K[7:1]) makes borrow
		const int k = inst & 0x7f;
		u8 m = get_m_data();
		m = m - k;
		if (m & 0x80)
			m_skip = 1;
	}
	else if (inst >= 0b0001'0000'0000 && inst <= 0b0001'0111'1111)
	{
		// 100-17f M[H[5:1],L[2:1]][7:1]+K[7:1]->M[H[5:1],L[2:1]][7:1], Skip if carry, N->L[2:1]
		const int k = inst & 0x1f;
		const int n = (inst >> 5) & 0x3;
		u8 m = get_m_data();
		m = m + k;
		set_m_data(m & 0x7f);

		if (m & 0x80)
			m_skip = 1;

		// TODO: prevents infinitely scrolling screen, and allows pakpak to boot, but this does't make logical
		//       sense, unless the 'Skip if carry' in the text above is specifically meaning this only applies
		//       when skipping, however if you apply the same to the next opcode it breaks text rendering
		//       eg 'score' text in the same game.
		if (m_skip)
			set_l(n);
	}
	else if (inst >= 0b0001'1000'0000 && inst <= 0b0001'1111'1111)
	{
		// 180-1ff M[H[5:1],L[2:1]][7:1]-K[7:1]->M[H[5:1],L[2:1]][7:1], Skip if borrow, N->L[2:1]
		const int k = inst & 0x1f;
		const int n = (inst >> 5) & 0x3;
		u8 m = get_m_data();
		m = m - k;
		set_m_data(m & 0x7f);

		if (m & 0x80)
			m_skip = 1;

		set_l(n);
	}
	else if (inst >= 0b0100'1000'0000 && inst <= 0b0100'1011'1111)
	{
		// 480-4bf H[5:1]-K[5:1]->H[5:1], Skip if borrow
		const int k = inst & 0x1f;
		u8 h = get_h();
		h = h - k;
		if (h & 0x20)
			m_skip = 1;
		set_h(h & 0x1f);
	}
	else if (inst >= 0b0100'1100'0000 && inst <= 0b0100'1111'1111)
	{
		// 4c0 - 4ff H[5:1]+K[5:1]->H[5:1], Skip if carry
		const int k = inst & 0x1f;
		u8 h = get_h();
		h = h + k;
		if (h & 0x20)
			m_skip = 1;
		set_h(h & 0x1f);
	}
	else if (inst >= 0b0101'0000'0000 && inst <= 0b0101'0111'1111)
	{
		// 500 - 57f
		// When (KIE=0)&(SME=0), Store K[7:1] to M[H[5:1],L[2:1]][7:1]
		// When (KIE=1), Store KIN[7:1] to M[H[5:1],L[2:1]][7:1]
		// When (SME=1), Store HCL[7:1] to M[H[5:1],L[2:1]][7:1]
		const int k = inst & 0x7f;

		if (get_kie()) // documentation does not state if KIE or SME has priority
		{
			LOGMASKED(LOG_UNHANDLED_OPS, "KIE->M\n", k);
			// Inputs for Cassette Vision appear to be read by this
			// (selected based on the STB output value?)
			set_m_data(m_port_in(m_stb));
		}
		else if (get_sme())
		{
			LOGMASKED(LOG_UNHANDLED_OPS, "SME->M\n", k);
			set_m_data(0);
		}
		else
		{
			set_m_data(k & 0x7f);
		}
	}
	else if (inst >= 0b0101'1000'0000 && inst <= 0b0101'1111'1111)
	{
		// 580 - 5ff Store K[7:6] to L[2:1] and K[5:1] to H[5:1]
		const int k = inst & 0x7f;
		set_l(k >> 5);
		set_h(k & 0x1f);
	}
	else if (inst >= 0b0110'0000'0000 && inst <= 0b0111'1111'1111)
	{
		// 600-67f Store K[7:1] to A1[7:1]
		// 680-6ff Store K[7:1] to A2[7:1]
		// 700-77f Store K[7:1] to A3[7:1]
		// 780-7ff Store K[7:1] to A4[7:1]
		const int reg = (inst & 0x180) >> 7;
		const int k = inst & 0x7f;
		switch (reg)
		{
		case 0: set_a1(k); break;
		case 1: set_a2(k); break;
		case 2: set_a3(k); break;
		case 3: set_a4(k); break;
		}
	}
	else if (inst >= 0b1000'0000'0000 && inst <= 0b1011'1111'1111)
	{
		// 800 - bff Move K[10:1] to A[10:1], Jump to A[11:1]
		u16 fulladdress = (m_ppc & 0x400) | (inst & 0x3ff);
		set_new_pc(fulladdress);
	}
	else if (inst >= 0b1100'0000'0000 && inst <= 0b1111'1111'1111)
	{
		// c00 - fff Move K[10:1] to A[10:1], 0 to A11, Jump to A[11:1], Push next A[11:1] up to ROM address stack
		const int k = inst & 0x3ff;
		push_to_stack(m_pc);
		set_new_pc(k);
	}
	else if (((inst & 0b1111'0000'0000) == 0b0010'0000'0000) && ((inst & 0b0000'0000'1100) != 0b0000'0000'0100))
	{
		// 0b0010'rrnR'oonn where rr = reg1 (A1, A2, M or H), n = invert condition, R = reg2 (A1 or A2) and oo = optype (only 0,2,3 are valid, no cases here for 1) nn = next l value

		// optype · (AND)
		// 200 Skip if (A1[7:1]·A1[7:1]) makes zero, N->L[2:1]
		// 220 Skip if (A1[7:1]·A1[7:1]) makes non zero, N->L[2:1]
		// 210 Skip if (A1[7:1]·A2[7:1]) makes zero, N->L[2:1]
		// 230 Skip if (A1[7:1]·A2[7:1]) makes non zero, N->L[2:1]
		// 240 Skip if (A2[7:1]·A1[7:1]) makes zero, N->L[2:1]
		// 260 Skip if (A2[7:1]·A1[7:1]) makes non zero, N->L[2:1]
		// 250 Skip if (A2[7:1]·A2[7:1]) makes zero, N->L[2:1]
		// 270 Skip if (A2[7:1]·A2[7:1]) makes non zero, N->L[2:1]
		// 280 Skip if (M[H[5:1],L[2:1]][7:1]·A1[7:1]) makes zero, N->L[2:1]
		// 2a0 Skip if (M[H[5:1],L[2:1]][7:1]·A1[7:1]) makes non zero, N->L[2:1]
		// 290 Skip if (M[H[5:1],L[2:1]][7:1]·A2[7:1]) makes zero, N->L[2:1]
		// 2b0 Skip if (M[H[5:1],L[2:1]][7:1]·A2[7:1]) makes non zero, N->L[2:1]
		// 2c0 Skip if (H[5:1]·A1[5:1]) makes zero, N->L[2:1]
		// 2e0 Skip if (H[5:1]·A1[5:1]) makes non zero, N->L[2:1]
		// 2d0 Skip if (H[5:1]·A2[5:1]) makes zero, N->L[2:1]
		// 2f0 Skip if (H[5:1]·A2[5:1]) makes non zero, N->L[2:1]

		// optype = (these are expressed as x=y in the opcopde syntax, but x-y in the description, in reality it seems to act as 'CMP' so x-y = 0)
		// 208 Skip if (A1[7:1]-A1[7:1]) makes zero, N->L[2:1]
		// 228 Skip if (A1[7:1]-A1[7:1]) makes non zero, N->L[2:1]
		// 218 Skip if (A1[7:1]-A2[7:1]) makes zero, N->L[2:1]
		// 238 Skip if (A1[7:1]-A2[7:1]) makes non zero, N->L[2:1]
		// 248 Skip if (A2[7:1]-A1[7:1]) makes zero, N->L[2:1]
		// 268 Skip if (A2[7:1]-A1[7:1]) makes non zero, N->L[2:1]
		// 258 Skip if (A2[7:1]-A2[7:1]) makes zero, N->L[2:1]
		// 278 Skip if (A2[7:1]-A2[7:1]) makes non zero, N->L[2:1]
		// 288 Skip if (M[H[5:1],L[2:1]][7:1]-A1[7:1]) makes zero, N->L[2:1]
		// 2a8 Skip if (M[H[5:1],L[2:1]][7:1]-A1[7:1]) makes non zero, N->L[2:1]
		// 298 Skip if (M[H[5:1],L[2:1]][7:1]-A2[7:1]) makes zero, N->L[2:1]
		// 2b8 Skip if (M[H[5:1],L[2:1]][7:1]-A2[7:1]) makes non zero, N->L[2:1]
		// 2c8 Skip if (H[5:1]-A1[5:1]) makes zero, N->L[2:1]
		// 2e8 Skip if (H[5:1]-A1[5:1]) makes non zero, N->L[2:1]
		// 2d8 Skip if (H[5:1]-A2[5:1]) makes zero, N->L[2:1]
		// 2f8 Skip if (H[5:1]-A2[5:1]) makes non zero, N->L[2:1]

		// optype -
		// 20c Skip if (A1[7:1]-A1[7:1]) makes borrow, N->L[2:1]
		// 22c Skip if (A1[7:1]-A1[7:1]) makes non borrow, N->L[2:1]
		// 21c Skip if (A1[7:1]-A2[7:1]) makes borrow, N->L[2:1]
		// 23c Skip if (A1[7:1]-A2[7:1]) makes non borrow, N->L[2:1]
		// 24c Skip if (A2[7:1]-A1[7:1]) makes borrow, N->L[2:1]
		// 26c Skip if (A2[7:1]-A1[7:1]) makes non borrow, N->L[2:1]
		// 25c Skip if (A2[7:1]-A2[7:1]) makes borrow, N->L[2:1]
		// 27c Skip if (A2[7:1]-A2[7:1]) makes non borrow, N->L[2:1]
		// 28c Skip if (M[H[5:1],L[2:1]][7:1]-A1[7:1]) makes borrow, N->L[2:1]
		// 2ac Skip if (M[H[5:1],L[2:1]][7:1]-A1[7:1]) makes non borrow, N->L[2:1]
		// 29c Skip if (M[H[5:1],L[2:1]][7:1]-A2[7:1]) makes borrow, N->L[2:1]
		// 2bc Skip if (M[H[5:1],L[2:1]][7:1]-A2[7:1]) makes non borrow, N->L[2:1]
		// 2cc Skip if (H[5:1]-A1[5:1]) makes borrow, N->L[2:1]
		// 2ec Skip if (H[5:1]-A1[5:1]) makes non borrow, N->L[2:1]
		// 2dc Skip if (H[5:1]-A2[5:1]) makes borrow, N->L[2:1]
		// 2fc Skip if (H[5:1]-A2[5:1]) makes non borrow, N->L[2:1]
		const int non = inst & 0x20;
		const int optype = (inst & 0x0c) >> 2;
		const int reg1 = (inst & 0xc0) >> 6;
		const int reg2 = (inst & 0x10) >> 4;
		const int n = inst & 0x3;
		u8 srcreg2 = get_a1_or_a2(reg2);
		u8 srcreg1 = 0;
		switch (reg1)
		{
		case 0: srcreg1 = get_a1(); break;
		case 1: srcreg1 = get_a2(); break;
		case 2: srcreg1 = get_m_data(); break;
		case 3:
		{
			srcreg1 = get_h();
			srcreg2 &= 0x1f;
			break;
		}
		}

		switch (optype)
		{
		case 0: // AND
		{
			if (!non)
			{
				if ((srcreg1 & srcreg2) == 0) // skip if (x·y) makes zero, N->L[2:1]
					m_skip = 1;
			}
			else
			{
				if ((srcreg1 & srcreg2) != 0) // skip if (x·y) makes non zero, N->L[2:1]
					m_skip = 1;
			}
			break;
		}
		case 1: // invalid
		{
			// can't happen, no switch case leads here
			break;
		}
		case 2: // =
		{
			if (!non)
			{
				if (srcreg1 == srcreg2) // skip if (x-y) makes zero, N->L[2:1]
					m_skip = 1;
			}
			else
			{
				if (srcreg1 != srcreg2) // skip if (x-y) makes non zero, N->L[2:1]
					m_skip = 1;
			}
			break;
		}
		case 3: // -
		{
			u8 result = srcreg1 - srcreg2;

			if (!non)
			{
				if (result & 0x80) // skip if (x-y) makes borrow, N->L[2:1]
					m_skip = 1;
			}
			else
			{
				if ((result & 0x80) == 0) // skip if (x-y) makes non borrow, N->L[2:1]
					m_skip = 1;
			}

			break;
		}
		}

		set_l(n);
	}
	else if ((inst & 0b1111'1010'0000) == 0b0011'0010'0000)
	{
		//   0b0011'0r1R'oonn (where r = reg1, R = reg2, o = optype, and n = next l value)
		// 320 AND A1[7:1] and A1[7:1], store to A1[7:1], N->L[2:1]
		// 324 Add A1[7:1] and A1[7:1], store to A1[7:1], N->L[2:1]
		// 328 OR A1[7:1] and A1[7:1], store to A1[7:1], N->L[2:1]
		// 32c Subtract A1[7:1] and A1[7:1], store to A1[7:1], Skip if borrow, N->L[2:1]
		// 330 AND A1[7:1] and A2[7:1], store to A1[7:1], N->L[2:1]
		// 334 Add A1[7:1] and A2[7:1], store to A1[7:1], N->L[2:1]
		// 338 OR A1[7:1] and A2[7:1], store to A1[7:1], N->L[2:1]
		// 33c Subtract A1[7:1] and A2[7:1], store to A1[7:1], Skip if borrow, N->L[2:1]
		// 360 AND A2[7:1] and A1[7:1], store to A2[7:1], N->L[2:1]
		// 364 Add A2[7:1] and A1[7:1], store to A2[7:1], N->L[2:1]
		// 368 OR A2[7:1] and A1[7:1], store to A2[7:1], N->L[2:1]
		// 36c Subtract A2[7:1] and A1[7:1], store to A2[7:1], Skip if borrow, N->L[2:1]
		// 370 AND A2[7:1] and A2[7:1], store to A2[7:1], N->L[2:1]
		// 374 Add A2[7:1] and A2[7:1], store to A2[7:1], N->L[2:1]
		// 378 OR A2[7:1] and A2[7:1], store to A2[7:1], N->L[2:1]
		// 37c Subtract A2[7:1] and A2[7:1], store to A2[7:1], Skip if borrow, N->L[2:1]
		const int optype = (inst & 0x0c) >> 2;
		const int reg2 = (inst & 0x10) >> 4;
		const int reg1 = (inst & 0x40) >> 6;
		const int n = inst & 0x3;

		u8 src1 = get_a1_or_a2(reg1);
		u8 src2 = get_a1_or_a2(reg2);

		switch (optype)
		{
		case 0: // AND
		{
			src1 = src1 & src2;
			break;
		}
		case 1: // ADD
		{
			src1 = src1 + src2;
			// not in this case?
			//if (src1 & 0x80)
			//  m_skip = 1;
			break;
		}
		case 2: // OR
		{
			src1 = src1 | src2;
			break;
		}
		case 3: // MINUS
		{
			src1 = src1 - src2;
			if (src1 & 0x80)
				m_skip = 1;
			break;
		}
		}
		set_a1_or_a2(reg1, src1);
		set_l(n);
	}
	else if ((inst & 0b1111'1110'0000) == 0b0011'1010'0000)
	{
		//   0b0011'101r'oonn (where r = reg, o = optype, n = next l value)
		// 3a0 AND M[H[5:1],L[2:1]][7:1] and A1[7:1], store to M[H[5:1],L[2:1]][7:1], N->L[2:1]
		// 3a4 Add M[H[5:1],L[2:1]][7:1] and A1[7:1], store to M[H[5:1],L[2:1]][7:1], N->L[2:1] Skip if carry
		// 3a8 OR M[H[5:1],L[2:1]][7:1] and A1[7:1], store to M[H[5:1],L[2:1]][7:1], N->L[2:1]
		// 3ac Subtract M[H[5:1],L[2:1]][7:1] and A1[7:1], store to M[H[5:1],L[2:1]][7:1], N->L[2:1] Skip if borrow
		// 3b0 AND M[H[5:1],L[2:1]][7:1] and A2[7:1], store to M[H[5:1],L[2:1]][7:1], N->L[2:1]
		// 3b4 Add M[H[5:1],L[2:1]][7:1] and A2[7:1], store to M[H[5:1],L[2:1]][7:1], N->L[2:1] Skip if carry
		// 3b8 OR M[H[5:1],L[2:1]][7:1] and A2[7:1], store to M[H[5:1],L[2:1]][7:1], N->L[2:1]
		// 3bc Subtract M[H[5:1],L[2:1]][7:1] and A2[7:1], store to M[H[5:1],L[2:1]][7:1], N->L[2:1] Skip if borrow
		const int optype = (inst & 0x0c) >> 2;
		const int reg2 = (inst & 0x10) >> 4;
		const int n = inst & 0x3;
		u8 src2 = get_a1_or_a2(reg2);
		u8 m = get_m_data();

		switch (optype)
		{
		case 0: // AND
		{
			m = m & src2;
			break;
		}
		case 1: // ADD
		{
			m = m + src2;
			if (m & 0x80)
				m_skip = 1;
			break;
		}
		case 2: // OR
		{
			m = m | src2;
			break;
		}
		case 3: // MINUS
		{
			m = m - src2;
			if (m & 0x80)
				m_skip = 1;
			break;
		}
		}
		set_m_data(m);
		set_l(n);
	}
	else if ((inst & 0b1111'1110'0000) == 0b0011'1110'0000)
	{
		//   0b0011'111r'oonn (where r = reg, o = optype, n = next l value)
		// 3e0 AND H[5:1] and A1[5:1], store to H[5:1], N->L[2:1]
		// 3e4 Add H[5:1] and A1[5:1], store to H[5:1], N->L[2:1]
		// 3e8 OR H[5:1] and A1[5:1], store to H[5:1], N->L[2:1]
		// 3ec Subtract H[5:1] and A1[5:1], store to H[5:1], Skip if borrow, N->L[2:1]
		// 3f0 AND H[5:1] and A2[5:1], store to H[5:1], N->L[2:1]
		// 3f4 Add H[5:1] and A2[5:1], store to H[5:1], N->L[2:1]
		// 3f8 OR H[5:1] and A2[5:1], store to H[5:1], N->L[2:1]
		// 3fc Subtract H[5:1] and A2[5:1], store to H[5:1], Skip if borrow, N->L[2:1]
		const int optype = (inst & 0x0c) >> 2;
		const int reg2 = (inst & 0x10) >> 4;
		const int n = inst & 0x3;
		u8 src2 = get_a1_or_a2(reg2) & 0x1f;
		u8 h = get_h();

		switch (optype)
		{
		case 0: // AND
		{
			h = h & src2;
			break;
		}
		case 1: // ADD
		{
			h = h + src2;
			break;
		}
		case 2: // OR
		{
			h = h | src2;
			break;
		}
		case 3: // MINUS
		{
			h = h - src2;
			if (h & 0x20)
				m_skip = 1;
			break;
		}
		}
		set_h(h & 0x1f);
		set_l(n);
	}
	else if ((inst & 0b1111'1100'0010) == 0b0100'0100'0000)
	{
		//   0b0100'01dg'ks0n (where  d = DISP, G = GPE, K = KIE, S = SME, n = A11)
		// 440 Set D to DISP, G to GPE, K to KIE, S to SME, N->A[11]
		const int d = (inst >> 5) & 0x1;
		const int g = (inst >> 4) & 0x1;
		const int k = (inst >> 3) & 0x1;
		const int s = (inst >> 2) & 0x1;
		const int n = inst & 0x1;
		set_disp(d);
		set_gpe(g);
		set_kie(k);
		set_sme(s);
		set_a11(n);
	}
	else if (inst == 0b0000'0000'0000)
	{
		// 000 No Operation
		// nothing
	}
	else if (inst == 0b0000'0000'0100)
	{
		// 004 Skip if (Gun Port Latch) = 1
		LOGMASKED(LOG_UNHANDLED_OPS, "GPL\n");
	}
	else if (inst == 0b0000'0000'1000)
	{
		// 008 Move H[5:1] to Line Buffer Register[5:1]
		u8 h = get_h();
		//LOGMASKED(LOG_UNHANDLED_OPS, "H(%02x)->NRM\n", h);

		// this seems to push a value from RAM into the line buffer for the current 4(?) scanlines
		u8 m1 = read_data_mem(get_h_shifted() | 0);
		u8 m2 = read_data_mem(get_h_shifted() | 1);
		u8 m3 = read_data_mem(get_h_shifted() | 2);
		u8 m4 = read_data_mem(get_h_shifted() | 3);
		push_to_line_buffer(h, m1,m2,m3,m4);
	}
	else if (inst == 0b0000'0001'1000)
	{
		// 018 H[5:1]<->X4[5:1], 0->X4[7:6], 0->X3[7:1], 0->X1'[1], 0->A1'[1], L[2:1]<->L'[2:1]
		LOGMASKED(LOG_UNHANDLED_OPS, "H<->X\n");
#if 1
		// this opcode is not well explained! but X4 etc. are referenced on Data RAM & Register Files which makes
		// it even more confusing (while L' isn't referenced anywhere else at all!)
		u8 temp;

		temp = m_x4;
		m_x4 = get_h();
		m_h = temp;

		temp = m_ldash;
		m_ldash = get_l();
		set_l(temp);
#endif
	}
	else if (inst == 0b0000'0010'0000)
	{
		// 020 Subroutine End, Pop down address stack
		u16 addr = pull_from_stack();
		set_new_pc(addr);
	}
	else if (inst == 0b0000'0100'1001)
	{
		// 049 Skip if (4H Horizontal Blank) = 1
		//LOGMASKED(LOG_UNHANDLED_OPS, "4H BLK\n");
		if (get_hbl_4_state())
			m_skip = 1;
	}
	else if (inst == 0b0000'0100'1010)
	{
		// 04a Skip if (Vertical Blank) = 1, 0->M[[18:00],[3]][1]
		LOGMASKED(LOG_UNHANDLED_OPS, "VBLK\n");
		if (get_vbl_state())
			m_skip = 1;

		// TODO:
		// need to do the 0->M[[18:00],[3]][1] bit
	}
	else if (inst == 0b0000'0100'1100)
	{
		// 04c Skip if (GP&SW/ input) = 1
		LOGMASKED(LOG_UNHANDLED_OPS, "GPSW/\n");
	}
	else if (inst == 0b0000'0101'0100)
	{
		// 054 Move (A4[7:1],A3[7:1],A2[7:1],A1[7:1]) to M[H[5:1]][28:1]
		write_data_mem(get_h_shifted() | 0, get_a1());
		write_data_mem(get_h_shifted() | 1, get_a2());
		write_data_mem(get_h_shifted() | 2, get_a3());
		write_data_mem(get_h_shifted() | 3, get_a4());
	}
	else if (inst == 0b0000'0101'1000)
	{
		// 058 Move M[H[5:1]][28:1] to (A4[7:1],A3[7:1],A2[7:1],A1[7:1])
		u8 m1 = read_data_mem(get_h_shifted() | 0);
		u8 m2 = read_data_mem(get_h_shifted() | 1);
		u8 m3 = read_data_mem(get_h_shifted() | 2);
		u8 m4 = read_data_mem(get_h_shifted() | 3);
		set_a1(m1);
		set_a2(m2);
		set_a3(m3);
		set_a4(m4);
	}
	else if (inst == 0b0000'0101'1100)
	{
		// 05c Exchange (A4[7:1],A3[7:1],A2[7:1],A1[7:1]) and M[H[5:1]][28:1]
		u8 m1 = read_data_mem(get_h_shifted() | 0);
		u8 m2 = read_data_mem(get_h_shifted() | 1);
		u8 m3 = read_data_mem(get_h_shifted() | 2);
		u8 m4 = read_data_mem(get_h_shifted() | 3);
		write_data_mem(get_h_shifted() | 0, get_a1());
		write_data_mem(get_h_shifted() | 1, get_a2());
		write_data_mem(get_h_shifted() | 2, get_a3());
		write_data_mem(get_h_shifted() | 3, get_a4());
		set_a1(m1);
		set_a2(m2);
		set_a3(m3);
		set_a4(m4);
	}
	else if (inst == 0b0000'0110'0000)
	{
		// 060 Subroutine End, Pop down address stack, Skip
		u16 addr = pull_from_stack();
		set_new_pc(addr);
		m_skip = 1;
	}
	else if (inst == 0b0011'0000'1000)
	{
		// 308 Move A1[7:1] to FLS[7:1], 0->L[2:1]
		u8 a1 = get_a1();
		set_fls(a1);
		set_l(0);
	}
	else if (inst == 0b0011'0100'1000)
	{
		// 348 Move A2[7:1] to FLS[7:1], 0->L[2:1]
		u8 a2 = get_a2();
		set_fls(a2);
		set_l(0);
	}
	else if (inst == 0b0011'1000'1000)
	{
		// 388 Move M[H[5:1],L[2:1]][7:1] to FLS[7:1], 0->L[2:1]
		u8 m = get_m_data();
		set_fls(m);
		set_l(0);
	}
	else if (inst == 0b0011'0000'1001)
	{
		// 309 Move A1[7:1] to FRS[7:1], 1->L[2:1]
		u8 a1 = get_a1();
		set_frs(a1);
		set_l(1);
	}
	else if (inst == 0b0011'0100'1001)
	{
		// 349 Move A2[7:1] to FRS[7:1], 1->L[2:1]
		u8 a2 = get_a2();
		set_frs(a2);
		set_l(1);
	}
	else if (inst == 0b0011'1000'1001)
	{
		// 389 Move M[H[5:1],L[2:1]][7:1] to FRS[7:1], 1->L[2:1]
		u8 m = get_m_data();
		set_frs(m);
		set_l(1);
	}
	else if ((inst == 0b0000'0010'1000) || (inst == 0b0000'0010'1001))
	{
		// 028 Shift STB[4:1], N->STB[1]
		// STB is an input strobe / shifter
		const int n = inst & 1;
		LOGMASKED(LOG_UNHANDLED_OPS, "0x%d->STB\n", n);

		m_stb = (m_stb << 1) | n;
		m_stb &= 0xf;
	}

	else if ((inst == 0b0011'0000'1010) || (inst == 0b0011'0000'1011))
	{
		// 30a Move A1[7:1] to MODE[7:1], 1N->L[2:1]
		const int n = (inst & 0x1) + 2;
		u8 a1 = get_a1();
		set_mode(a1);
		set_l(n);
	}
	else if ((inst == 0b0011'01001010) || (inst == 0b0011'0100'1011))
	{
		// 34a Move A2[7:1] to MODE[7:1], 1N->L[2:1]
		const int n = (inst & 0x1) + 2;
		u8 a2 = get_a2();
		set_mode(a2);
		set_l(n);
	}
	else if ((inst == 0b0011'1000'1010) || (inst == 0b00111000'1011))
	{
		// 38a Move M[H[5:1],L[2:1]][7:1] to MODE[7:1], 1N->L[2:1]
		const int n = (inst & 0x1) + 2;
		u8 m = get_m_data();
		set_mode(m);
		set_l(n);
	}
	else if ((inst == 0b0100'0000'0000) || (inst == 0b0100'0000'0001))
	{
		// 400 N->A[11]
		const int n = inst & 0x1;
		set_a11(n);
	}
	else if ((inst == 0b0100'0000'0010) || (inst == 0b0100'0000'0011))
	{
		// 402 Jump to (000,M[H[5:1],L[2:1]][5:1],1N), 0->L[2:1], N->A[11]
		const int n = inst & 0x1;
		LOGMASKED(LOG_UNHANDLED_OPS, "JPM, 0->L, %d->A11\n", n);
		set_a11(n);
	}
	else if ((inst & 0b1111'1111'1100) == 0b0011'0000'0000)
	{
		// 300 N->L[2:1]
		const int n = inst & 0x3;
		set_l(n);
	}
	else if ((inst & 0b1111'1111'1100) == 0b0011'0001'0000)
	{
		// 310 Move A2[7:1] to A1[7:1], N->L[2:1]
		const int n = inst & 0x3;
		u8 a2 = get_a2();
		set_a1(a2);
		set_l(n);
	}
	else if ((inst & 0b1111'1111'1100) == 0b0011'0100'0000)
	{
		// 340 Move A1[7:1] to A2[7:1], N->L[2:1]
		const int n = inst & 0x3;
		u8 a1 = get_a1();
		set_a2(a1);
		set_l(n);
	}
	else if ((inst & 0b1111'1111'1100) == 0b0011'0001'1000)
	{
		// 318 Right shift A1[7:1], 0->A1[7], N->L[2:1]
		const int n = inst & 0x3;
		u8 a1 = get_a1();
		a1 = a1 >> 1;
		set_a1(a1);
		set_l(n);
	}
	else if ((inst & 0b1111'1111'1100) == 0b0011'0101'1000)
	{
		// 358 Right shift A2[7:1], 0->A2[7], N->L[2:1]
		const int n = inst & 0x3;
		u8 a2 = get_a2();
		a2 = a2 >> 1;
		set_a2(a2);
		set_l(n);
	}
	else if ((inst & 0b1111'1111'1100) == 0b0011'1001'1000)
	{
		// 398 Right shift M[H[5:1],L[2:1]][7:1], 0->M[H[5:1],L[2:1]][7], N->L[2:1]
		const int n = inst & 0x3;
		u8 m = get_m_data();
		m = m >> 1;
		set_m_data(m);
		set_l(n);
	}
	else if ((inst & 0b1111'1111'1100) == 0b0011'0001'1100)
	{
		// 31c Subtract A1[7:1] and A2[7:1], store to A2[7:1], Skip if borrow, N->L[2:1]
		const int n = inst & 0x3;
		u8 a1 = get_a1();
		u8 a2 = get_a2();
		a2 = a1 - a2;
		if (a2 & 0x80)
			m_skip = 1;
		set_a2(a2);
		set_l(n);
	}
	else if ((inst & 0b1111'1111'1100) == 0b0011'0100'1100)
	{
		// 34c Subtract A2[7:1] and A1[7:1], store to A1[7:1], Skip if borrow, N->L[2:1]
		const int n = inst & 0x3;
		u8 a1 = get_a1();
		u8 a2 = get_a2();
		a1 = a2 - a1;
		if (a1 & 0x80)
			m_skip = 1;
		set_a1(a1);
		set_l(n);
	}
	else if ((inst & 0b1111'1110'1100) == 0b0011'1000'0000)
	{
		// 380 Move A1[7:1] to M[H[5:1],L[2:1]][7:1], N->L[2:1]
		// 390 Move A2[7:1] to M[H[5:1],L[2:1]][7:1], N->L[2:1]
		const int reg = (inst & 0x10) >> 4;
		const int n = inst & 0x3;
		u8 src = get_a1_or_a2(reg);
		set_m_data(src);
		set_l(n);
	}
	else if ((inst & 0b1111'1110'1100) == 0b0011'1000'0100)
	{
		// 384 Exchange M[H[5:1],L[2:1]][7:1] and A1[7:1], N->L[2:1]
		// 394 Exchange M[H[5:1],L[2:1]][7:1] and A2[7:1], N->L[2:1]
		const int reg = (inst & 0x10) >> 4;
		const int n = inst & 0x3;
		u8 src = get_a1_or_a2(reg);
		u8 m = get_m_data();
		set_m_data(src);
		set_a1_or_a2(reg,m);
		set_l(n);
	}
	else if ((inst & 0b1111'1110'1100) == 0b0011'1000'1100)
	{
		// 38c Move M[H[5:1],L[2:1]][7:1] to A1[7:1], N->L[2:1]
		// 39c Move M[H[5:1],L[2:1]][7:1] to A2[7:1], N->L[2:1]
		const int reg = (inst & 0x10) >> 4;
		const int n = inst & 0x3;
		u8 m = get_m_data();
		set_a1_or_a2(reg,m);
		set_l(n);
	}
	else if ((inst & 0b1111'1110'1100) == 0b0011'1100'0000)
	{
		// 3c0 Move A1[5:1] to H[5:1], N->L[2:1]
		// 3d0 Move A2[5:1] to H[5:1], N->L[2:1]
		const int reg = (inst & 0x10) >> 4;
		const int n = inst & 0x3;
		u8 src = get_a1_or_a2(reg);
		set_h(src);
		set_l(n);
	}
	else if ((inst & 0b1111'1110'1100) == 0b0011'1100'1100)
	{
		// 3cc Move H[5:1] to A1[5:1], 0->A1[7:6], N->L[2:1]
		// 3dc Move H[5:1] to A2[5:1], 0->A2[7:6], N->L[2:1]
		const int reg = (inst & 0x10) >> 4;
		const int n = inst & 0x3;
		u8 h = get_h() & 0x1f;
		set_a1_or_a2(reg,h);
		set_l(n);
	}
	else if ((inst & 0b1111'1011'0011) == 0b0000'0011'0000)
	{
		// 30 Skip if (PD1 input) = 1
		// 34 Skip if (PD2 input) = 1
		// 38 Skip if (PD3 input) = 1
		// 3c Skip if (PD4 input) = 1
		// 70 Skip if (PD1 input) = 0
		// 74 Skip if (PD2 input) = 0
		// 78 Skip if (PD3 input) = 0
		// 7c Skip if (PD4 input) = 0
		const int which = (inst & 0x00c) >> 2;
		const int inv = inst & 0x40;
		LOGMASKED(LOG_UNHANDLED_OPS, "PD%d %sJ\n", which + 1, inv ? "/" : "");
	}
	else
	{
		LOGMASKED(LOG_UNHANDLED_OPS, "%04x <ILLEGAL>\n", inst);
	}
}

void upd777_cpu_device::execute_run()
{
	while (m_icount > 0)
	{
		if (m_skip)
		{
			fetch();
			m_skip = 0;
		}

		debugger_instruction_hook(m_pc);
		do_op();
		m_icount--;
	}
}

void upd777_cpu_device::execute_set_input(int inputnum, int state)
{
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


bool upd777_cpu_device::get_vbl_state()
{
	int vpos = m_screen->vpos();
	if (vpos > 240)
		return true;

	return false;
}

bool upd777_cpu_device::get_hbl_4_state()
{
	// I *think* this is Hblank for every 4th line (so a new display list can be written?)
	int vpos = m_screen->vpos();
	if ((vpos % 4) == 0)
	{
		int hpos = m_screen->hpos();
		if (hpos > 200)
			return true;
	}

	return false;
}

uint32_t upd777_cpu_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	// this needs to be scanline based, drawing whatever has been pushed to the linebuffer for the current
	// group of scanlines!

	bitmap.fill(0, cliprect);
	gfx_element *gfx = m_gfxdecode->gfx(0);
	gfx_element *gfx2 = m_gfxdecode->gfx(1);

	for (int i = 0; i <= 0x18; i++)
	{
		u8 s0 = m_datamem[(i * 4) + 0];
		u8 s1 = m_datamem[(i * 4) + 1];
		u8 s2 = m_datamem[(i * 4) + 2];
		u8 s3 = m_datamem[(i * 4) + 3];

		int ypos = (s0 & 0x7e) >> 1;
		//int prio = (s0 & 0x01);
		int xpos = (s1 & 0x7f);
		int patn = (s2 & 0x7f);
		//int ylow = (s3 & 0x70);
		int pal = (s3 & 0x0e) >> 1;
		//int ysub = (s3 & 0x01);

		if (patn<0x70)
			gfx->zoom_transpen(bitmap, cliprect, patn, pal, 0, 0, xpos * 4, ypos * 4, 0x40000, 0x40000, 0);
		else
			gfx2->zoom_transpen(bitmap, cliprect, patn-0x70, pal, 0, 0, xpos * 4, ypos * 4, 0x40000, 0x40000, 0);
	}

	return 0;
}

// documentation says patterns 0x00 - 0x6e are 7x7
// and patterns 0x70 - 0x7e are 8x7
// but they all seem to be stored at 11x7, just with some columns blank?
// this is probably because of how they were read out, over 11 data lines

// 0x00-0x2f are 'Normal (7x7)
// 0x30-0x67 are 'Bent' (7x7)
// 0x68-0x6f are 'Y Repeat' (7x7)
// 0x70-0x77 are 'XY Repeat' (8x7)
// 0x78-0x7f are 'X Repeat' (8x7)
//
// NOTE, sprite patterns *7 and *f are unused so documentation expresses these ranges as to 66, 6e etc. rather than 67 6f
//
// it isn't clear how the 'Bent' effect etc. is enabled, as clearly not all patterns in this range should use it?

static const gfx_layout test_layout =
{
	7,7,
	0x70,
	1,
	{ 0 },
	{ 4,5,6,7,8,9,10 },
	{ 0*11,1*11,2*11,3*11,4*11,5*11,6*11 },
	7*11
};

static const gfx_layout test2_layout =
{
	8,7,
	0x10,
	1,
	{ 0 },
	{ 3,4,5,6,7,8,9,10 },
	{ 0*11,1*11,2*11,3*11,4*11,5*11,6*11 },
	7*11
};

static GFXDECODE_START( gfx_ud777 )
	GFXDECODE_ENTRY( "patterns", 0x000, test_layout,  0, 8 )
	GFXDECODE_ENTRY( "patterns", 0x436, test2_layout, 0, 8 )
GFXDECODE_END


void upd777_cpu_device::palette_init(palette_device &palette) const
{
	// just a fake palette for now
	for (int i = 0; i < palette.entries(); i++)
	{
		if (i & 1)
		{
			palette.set_pen_color(i, rgb_t(((i >> 1) & 1) ? 0xff : 0x7f, ((i >> 2) & 1) ? 0xff : 0x7f, ((i >> 3) & 1) ? 0xff : 0x7f));
		}
		else
		{
			palette.set_pen_color(i, rgb_t(0, 0, 0));
		}
	}
}

void upd777_cpu_device::push_to_line_buffer(u8 h, u8 m1, u8 m2, u8 m3, u8 m4)
{
	logerror("sprite %02x pushed to line buffer at scanline %d hpos %d: details %02x %02x %02x %02x\n", h, m_screen->vpos(), m_screen->hpos(), m1, m2, m3, m4);
}

TIMER_DEVICE_CALLBACK_MEMBER(upd777_cpu_device::scanline_timer)
{
	int scanline = param;

	logerror("scanline %d\n", scanline);
}

void upd777_cpu_device::device_add_mconfig(machine_config &config)
{
	// or pass the screen from the driver?
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2000));
	m_screen->set_size(512, 256);
	m_screen->set_visarea(0, 512-1, 0, 256-0-1);
	m_screen->set_screen_update(FUNC(upd777_cpu_device::screen_update));
	m_screen->set_palette(m_palette);

	TIMER(config, "scantimer").configure_scanline(FUNC(upd777_cpu_device::scanline_timer), "screen", 0, 1);

	GFXDECODE(config, m_gfxdecode, m_palette, gfx_ud777);
	PALETTE(config, m_palette, FUNC(upd777_cpu_device::palette_init), 32 * 3).set_entries(0x10);
}

ROM_START( upd777 )
	ROM_REGION16_BE( 0x1000, "prg", ROMREGION_ERASEFF )
	ROM_REGION( 0x4d0, "patterns", ROMREGION_ERASEFF )
ROM_END

const tiny_rom_entry *upd777_cpu_device::device_rom_region() const
{
	return ROM_NAME(upd777);
}

