// license:BSD-3-Clause
// copyright-holders:Philip Bennett
/***************************************************************************

    cubeqcpu.c

    Implementation of the Cube Quest AM2901-based CPUs
    Copyright Philip J Bennett

    TODO:

    * Tidy up diassembly (split into different files?)

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

#include "emu.h"
#include "debugger.h"
#include "cubeqcpu.h"
#include "cubedasm.h"


/***************************************************************************
    CONSTANTS
***************************************************************************/

/* Am2901 Instruction Fields */
enum alu_src
{
	AQ = 0,
	AB = 1,
	ZQ = 2,
	ZB = 3,
	ZA = 4,
	DA = 5,
	DQ = 6,
	DZ = 7
};

enum alu_ins
{
	ADD   = 0,
	SUBR  = 1,
	SUBS  = 2,
	OR    = 3,
	AND   = 4,
	NOTRS = 5,
	EXOR  = 6,
	EXNOR = 7
};

enum alu_dst
{
	QREG  = 0,
	NOP   = 1,
	RAMA  = 2,
	RAMF  = 3,
	RAMQD = 4,
	RAMD  = 5,
	RAMQU = 6,
	RAMU  = 7
};

/***************************************************************************
    MACROS
***************************************************************************/

#define _BIT(x, n)          ((x) & (1 << (n)))

/***************************************************************************
    STRUCTURES & TYPEDEFS
***************************************************************************/


DEFINE_DEVICE_TYPE(CQUESTSND, cquestsnd_cpu_device, "cquestsnd", "Cube Quest Sound CPU")
DEFINE_DEVICE_TYPE(CQUESTROT, cquestrot_cpu_device, "cquestrot", "Cube Quest Rotate CPU")
DEFINE_DEVICE_TYPE(CQUESTLIN, cquestlin_cpu_device, "cquestlin", "Cube Quest Line CPU")


cquestsnd_cpu_device::cquestsnd_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
	: cpu_device(mconfig, CQUESTSND, tag, owner, clock)
	, m_program_config("program", ENDIANNESS_BIG, 64, 8, -3)
	, m_dac_w(*this)
	, m_sound_region_tag(nullptr)
{
}

device_memory_interface::space_config_vector cquestsnd_cpu_device::memory_space_config() const
{
	return space_config_vector {
		std::make_pair(AS_PROGRAM, &m_program_config)
	};
}


std::unique_ptr<util::disasm_interface> cquestsnd_cpu_device::create_disassembler()
{
	return std::make_unique<cquestsnd_disassembler>();
}


cquestrot_cpu_device::cquestrot_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
	: cpu_device(mconfig, CQUESTROT, tag, owner, clock)
	, m_program_config("program", ENDIANNESS_BIG, 64, 9, -3)
	, m_linedata_w(*this)
{
}


u16 cquestrot_cpu_device::linedata_r()
{
	return m_linedata;
}


std::unique_ptr<util::disasm_interface> cquestrot_cpu_device::create_disassembler()
{
	return std::make_unique<cquestsnd_disassembler>();
}


cquestlin_cpu_device::cquestlin_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
	: cpu_device(mconfig, CQUESTLIN, tag, owner, clock)
	, m_program_config("program", ENDIANNESS_BIG, 64, 8, -3)
	, m_linedata_r(*this)
	, m_flags(0)
	, m_curpc(0)
{
}

device_memory_interface::space_config_vector cquestlin_cpu_device::memory_space_config() const
{
	return space_config_vector {
		std::make_pair(AS_PROGRAM, &m_program_config)
	};
}

std::unique_ptr<util::disasm_interface> cquestlin_cpu_device::create_disassembler()
{
	return std::make_unique<cquestlin_disassembler>();
}


void cquestlin_cpu_device::linedata_w(offs_t offset, u16 data)
{
	m_sram[offset] = data;
}


/***************************************************************************
    MEMORY ACCESSORS FOR 68000
***************************************************************************/

void cquestsnd_cpu_device::sndram_w(offs_t offset, u16 data, u16 mem_mask)
{
	COMBINE_DATA(&m_sram[offset]);
}

u16 cquestsnd_cpu_device::sndram_r(offs_t offset)
{
	return m_sram[offset];
}


void cquestrot_cpu_device::rotram_w(offs_t offset, u16 data, u16 mem_mask)
{
	COMBINE_DATA(&m_dram[offset]);
}

u16 cquestrot_cpu_device::rotram_r(offs_t offset)
{
	return m_dram[offset];
}


/***************************************************************************
    SOUND INITIALIZATION AND SHUTDOWN
***************************************************************************/

void cquestsnd_cpu_device::device_start()
{
	m_dac_w.resolve_safe();
	assert(m_sound_region_tag != nullptr);
	m_sound_data = (u16*)machine().root_device().memregion(m_sound_region_tag)->base();

	space(AS_PROGRAM).cache(m_cache);
	space(AS_PROGRAM).specific(m_program);

	memset(m_ram, 0, sizeof(m_ram));
	m_q = 0;
	m_f = 0;
	m_y = 0;
	m_cflag = 0;
	m_vflag = 0;
	m_pc = 0;
	m_platch = 0;
	m_rtnlatch = 0;
	m_adrcntr = 0;
	m_dinlatch = 0;
	m_ramwlatch = 0;
	m_prev_ipram = 0;
	m_prev_ipwrt = 0;
	m_adrlatch = 0;

	save_item(NAME(m_ram));
	save_item(NAME(m_q));
	save_item(NAME(m_f));
	save_item(NAME(m_y));
	save_item(NAME(m_cflag));
	save_item(NAME(m_vflag));

	save_item(NAME(m_pc));
	save_item(NAME(m_platch));
	save_item(NAME(m_rtnlatch));
	save_item(NAME(m_adrcntr));
	save_item(NAME(m_adrlatch));
	save_item(NAME(m_dinlatch));
	save_item(NAME(m_ramwlatch));
	save_item(NAME(m_prev_ipram));
	save_item(NAME(m_prev_ipwrt));

	state_add(CQUESTSND_PC,       "PC",     m_pc).formatstr("%02X");
	state_add(CQUESTSND_Q,        "Q",      m_q).formatstr("%04X");
	state_add(CQUESTSND_RTNLATCH, "RTN",    m_rtnlatch).formatstr("%02X");
	state_add(CQUESTSND_ADRCNTR,  "CNT",    m_adrcntr).formatstr("%02X");
	state_add(CQUESTSND_DINLATCH, "DINX",   m_dinlatch).formatstr("%04X");
	state_add(CQUESTSND_RAM0,     "RAM[0]", m_ram[0x0]).formatstr("%04X");
	state_add(CQUESTSND_RAM1,     "RAM[1]", m_ram[0x1]).formatstr("%04X");
	state_add(CQUESTSND_RAM2,     "RAM[2]", m_ram[0x2]).formatstr("%04X");
	state_add(CQUESTSND_RAM3,     "RAM[3]", m_ram[0x3]).formatstr("%04X");
	state_add(CQUESTSND_RAM4,     "RAM[4]", m_ram[0x4]).formatstr("%04X");
	state_add(CQUESTSND_RAM5,     "RAM[5]", m_ram[0x5]).formatstr("%04X");
	state_add(CQUESTSND_RAM6,     "RAM[6]", m_ram[0x6]).formatstr("%04X");
	state_add(CQUESTSND_RAM7,     "RAM[7]", m_ram[0x7]).formatstr("%04X");
	state_add(CQUESTSND_RAM8,     "RAM[8]", m_ram[0x8]).formatstr("%04X");
	state_add(CQUESTSND_RAM9,     "RAM[9]", m_ram[0x9]).formatstr("%04X");
	state_add(CQUESTSND_RAMA,     "RAM[A]", m_ram[0xa]).formatstr("%04X");
	state_add(CQUESTSND_RAMB,     "RAM[B]", m_ram[0xb]).formatstr("%04X");
	state_add(CQUESTSND_RAMC,     "RAM[C]", m_ram[0xc]).formatstr("%04X");
	state_add(CQUESTSND_RAMD,     "RAM[D]", m_ram[0xd]).formatstr("%04X");
	state_add(CQUESTSND_RAME,     "RAM[E]", m_ram[0xe]).formatstr("%04X");
	state_add(CQUESTSND_RAMF,     "RAM[F]", m_ram[0xf]).formatstr("%04X");

	state_add(STATE_GENPC, "GENPC", m_pc).formatstr("%02X").noshow();
	state_add(STATE_GENPCBASE, "CURPC", m_pc).formatstr("%02X").noshow();

	set_icountptr(m_icount);
}


void cquestsnd_cpu_device::device_reset()
{
	m_pc = 0;
}


/***************************************************************************
    ROTATE INITIALIZATION AND SHUTDOWN
***************************************************************************/

void cquestrot_cpu_device::device_start()
{
	m_linedata_w.resolve_safe();

	space(AS_PROGRAM).cache(m_cache);
	space(AS_PROGRAM).specific(m_program);

	memset(m_ram, 0, sizeof(m_ram));
	m_q = 0;
	m_f = 0;
	m_y = 0;
	m_cflag = 0;
	m_vflag = 0;
	m_pc = 0;
	m_seqcnt = 0;
	m_dsrclatch = 0;
	m_rsrclatch = 0;
	m_dynaddr = 0;
	m_dyndata = 0;
	m_yrlatch = 0;
	m_ydlatch = 0;
	m_dinlatch = 0;
	m_divreg = 0;
	m_linedata = 0;
	m_lineaddr = 0;
	m_prev_dred = 0;
	m_prev_dwrt = 0;
	m_wc = 0;
	m_rc = 0;
	m_clkcnt = 0;

	save_item(NAME(m_ram));
	save_item(NAME(m_q));
	save_item(NAME(m_f));
	save_item(NAME(m_y));
	save_item(NAME(m_cflag));
	save_item(NAME(m_vflag));

	save_item(NAME(m_pc));
	save_item(NAME(m_seqcnt));
	save_item(NAME(m_dsrclatch));
	save_item(NAME(m_rsrclatch));
	save_item(NAME(m_dynaddr));
	save_item(NAME(m_dyndata));
	save_item(NAME(m_yrlatch));
	save_item(NAME(m_ydlatch));
	save_item(NAME(m_dinlatch));
	save_item(NAME(m_divreg));
	save_item(NAME(m_linedata));
	save_item(NAME(m_lineaddr));
	save_item(NAME(m_prev_dred));
	save_item(NAME(m_prev_dwrt));
	save_item(NAME(m_wc));

	save_pointer(NAME(m_dram), 16384);
	save_pointer(NAME(m_sram), 2048);

	state_add(CQUESTROT_PC,        "PC",        m_pc).formatstr("%02X");
	state_add(CQUESTROT_Q,         "Q",         m_q).formatstr("%04X");
	state_add(CQUESTROT_RAM0,      "RAM[0]",    m_ram[0x0]).formatstr("%04X");
	state_add(CQUESTROT_RAM1,      "RAM[1]",    m_ram[0x1]).formatstr("%04X");
	state_add(CQUESTROT_RAM2,      "RAM[2]",    m_ram[0x2]).formatstr("%04X");
	state_add(CQUESTROT_RAM3,      "RAM[3]",    m_ram[0x3]).formatstr("%04X");
	state_add(CQUESTROT_RAM4,      "RAM[4]",    m_ram[0x4]).formatstr("%04X");
	state_add(CQUESTROT_RAM5,      "RAM[5]",    m_ram[0x5]).formatstr("%04X");
	state_add(CQUESTROT_RAM6,      "RAM[6]",    m_ram[0x6]).formatstr("%04X");
	state_add(CQUESTROT_RAM7,      "RAM[7]",    m_ram[0x7]).formatstr("%04X");
	state_add(CQUESTROT_RAM8,      "RAM[8]",    m_ram[0x8]).formatstr("%04X");
	state_add(CQUESTROT_RAM9,      "RAM[9]",    m_ram[0x9]).formatstr("%04X");
	state_add(CQUESTROT_RAMA,      "RAM[A]",    m_ram[0xa]).formatstr("%04X");
	state_add(CQUESTROT_RAMB,      "RAM[B]",    m_ram[0xb]).formatstr("%04X");
	state_add(CQUESTROT_RAMC,      "RAM[C]",    m_ram[0xc]).formatstr("%04X");
	state_add(CQUESTROT_RAMD,      "RAM[D]",    m_ram[0xd]).formatstr("%04X");
	state_add(CQUESTROT_RAME,      "RAM[E]",    m_ram[0xe]).formatstr("%04X");
	state_add(CQUESTROT_RAMF,      "RAM[F]",    m_ram[0xf]).formatstr("%04X");
	state_add(CQUESTROT_SEQCNT,    "SEQCNT",    m_seqcnt).formatstr("%01X");
	state_add(CQUESTROT_DYNADDR,   "DYNADDR",   m_dynaddr).formatstr("%04X");
	state_add(CQUESTROT_DYNDATA,   "DYNDATA",   m_dyndata).formatstr("%04X");
	state_add(CQUESTROT_YRLATCH,   "YRLATCH",   m_yrlatch).formatstr("%04X");
	state_add(CQUESTROT_YDLATCH,   "YDLATCH",   m_ydlatch).formatstr("%04X");
	state_add(CQUESTROT_DINLATCH,  "DINLATCH",  m_dinlatch).formatstr("%04X");
	state_add(CQUESTROT_DSRCLATCH, "DSRCLATCH", m_dsrclatch).formatstr("%04X");
	state_add(CQUESTROT_RSRCLATCH, "RSRCLATCH", m_rsrclatch).formatstr("%04X");
	state_add(CQUESTROT_LDADDR,    "LDADDR",    m_lineaddr).formatstr("%04X");
	state_add(CQUESTROT_LDDATA,    "LDDATA",    m_linedata).formatstr("%04X");

	state_add(STATE_GENPC, "GENPC", m_pc).formatstr("%02X").noshow();
	state_add(STATE_GENPCBASE, "CURPC", m_pc).formatstr("%02X").noshow();
	state_add(STATE_GENFLAGS, "GENFLAGS", m_flags).formatstr("%3s").noshow();

	set_icountptr(m_icount);
}


void cquestrot_cpu_device::device_reset()
{
	m_pc = 0;
	m_wc = 0;
	m_prev_dred = 1;
	m_prev_dwrt = 1;
}


void cquestrot_cpu_device::state_string_export(const device_state_entry &entry, std::string &str) const
{
	switch (entry.index())
	{
		case STATE_GENFLAGS:
			str = string_format("%c%c%c",
					m_cflag ? 'C' : '.',
					m_vflag ? 'V' : '.',
					m_f ? '.' : 'Z');
			break;
	}
}

device_memory_interface::space_config_vector cquestrot_cpu_device::memory_space_config() const
{
	return space_config_vector {
		std::make_pair(AS_PROGRAM, &m_program_config)
	};
}

/***************************************************************************
    LINE DRAWER INITIALIZATION AND SHUTDOWN
***************************************************************************/
#define FOREGROUND      0
#define BACKGROUND      1
#define ODD_FIELD       0
#define EVEN_FIELD      1

void cquestlin_cpu_device::device_start()
{
	m_linedata_r.resolve_safe(0);

	space(AS_PROGRAM).cache(m_cache);
	space(AS_PROGRAM).specific(m_program);

	memset(m_ram, 0, sizeof(m_ram));
	m_q = 0;
	m_f = 0;
	m_y = 0;
	m_cflag = 0;
	m_vflag = 0;
	m_pc[0] = m_pc[1] = 0;
	m_seqcnt = 0;
	m_clatch = 0;
	m_zlatch = 0;
	m_xcnt = 0;
	m_ycnt = 0;
	m_sreg = 0;
	m_fadlatch = 0;
	m_badlatch = 0;
	m_sramdlatch = 0;
	m_fglatch = 0;
	m_bglatch = 0;
	m_gt0reg = 0;
	m_fdxreg = 0;
	m_field = 0;
	m_clkcnt = 0;

	save_item(NAME(m_ram));
	save_item(NAME(m_q));
	save_item(NAME(m_f));
	save_item(NAME(m_y));
	save_item(NAME(m_cflag));
	save_item(NAME(m_vflag));

	save_item(NAME(m_pc[0]));
	save_item(NAME(m_pc[1]));
	save_item(NAME(m_seqcnt));
	save_item(NAME(m_clatch));
	save_item(NAME(m_zlatch));
	save_item(NAME(m_xcnt));
	save_item(NAME(m_ycnt));
	save_item(NAME(m_sreg));
	save_item(NAME(m_fadlatch));
	save_item(NAME(m_badlatch));
	save_item(NAME(m_sramdlatch));
	save_item(NAME(m_fglatch));
	save_item(NAME(m_bglatch));
	save_item(NAME(m_gt0reg));
	save_item(NAME(m_fdxreg));
	save_item(NAME(m_field));
	save_item(NAME(m_clkcnt));

	save_pointer(NAME(m_sram), 4096);
	save_pointer(NAME(m_ptr_ram), 1024);
	save_pointer(NAME(m_e_stack), 32768);
	save_pointer(NAME(m_o_stack), 32768);

	state_add(CQUESTLIN_FGPC,     "FPC", m_pc[FOREGROUND]).formatstr("%02X");
	state_add(CQUESTLIN_BGPC,     "BPC", m_pc[BACKGROUND]).formatstr("%02X");
	state_add(CQUESTLIN_Q,        "Q", m_q).formatstr("%04X");
	state_add(CQUESTLIN_RAM0,     "RAM[0]", m_ram[0x0]).formatstr("%04X");
	state_add(CQUESTLIN_RAM1,     "RAM[1]", m_ram[0x1]).formatstr("%04X");
	state_add(CQUESTLIN_RAM2,     "RAM[2]", m_ram[0x2]).formatstr("%04X");
	state_add(CQUESTLIN_RAM3,     "RAM[3]", m_ram[0x3]).formatstr("%04X");
	state_add(CQUESTLIN_RAM4,     "RAM[4]", m_ram[0x4]).formatstr("%04X");
	state_add(CQUESTLIN_RAM5,     "RAM[5]", m_ram[0x5]).formatstr("%04X");
	state_add(CQUESTLIN_RAM6,     "RAM[6]", m_ram[0x6]).formatstr("%04X");
	state_add(CQUESTLIN_RAM7,     "RAM[7]", m_ram[0x7]).formatstr("%04X");
	state_add(CQUESTLIN_RAM8,     "RAM[8]", m_ram[0x8]).formatstr("%04X");
	state_add(CQUESTLIN_RAM9,     "RAM[9]", m_ram[0x9]).formatstr("%04X");
	state_add(CQUESTLIN_RAMA,     "RAM[A]", m_ram[0xa]).formatstr("%04X");
	state_add(CQUESTLIN_RAMB,     "RAM[B]", m_ram[0xb]).formatstr("%04X");
	state_add(CQUESTLIN_RAMC,     "RAM[C]", m_ram[0xc]).formatstr("%04X");
	state_add(CQUESTLIN_RAMD,     "RAM[D]", m_ram[0xd]).formatstr("%04X");
	state_add(CQUESTLIN_RAME,     "RAM[E]", m_ram[0xe]).formatstr("%04X");
	state_add(CQUESTLIN_RAMF,     "RAM[F]", m_ram[0xf]).formatstr("%04X");

	state_add(CQUESTLIN_FADLATCH, "FADDR", m_fadlatch).formatstr("%04X");
	state_add(CQUESTLIN_BADLATCH, "BADDR", m_badlatch).formatstr("%04X");
	state_add(CQUESTLIN_SREG,     "SREG", m_sreg).formatstr("%04X");
	state_add(CQUESTLIN_XCNT,     "XCNT", m_xcnt).formatstr("%03X");
	state_add(CQUESTLIN_YCNT,     "YCNT", m_ycnt).formatstr("%03X");
	state_add(CQUESTLIN_CLATCH,   "CLATCH", m_clatch).formatstr("%04X");
	state_add(CQUESTLIN_ZLATCH,   "ZLATCH", m_zlatch).formatstr("%04X");

	state_add(STATE_GENPC, "GENPC", m_curpc).formatstr("%02X").noshow();
	state_add(STATE_GENPCBASE, "CURPC", m_curpc).formatstr("%02X").noshow();
	state_add(STATE_GENFLAGS, "GENFLAGS", m_flags).formatstr("%6s").noshow();

	set_icountptr(m_icount);
}


void cquestlin_cpu_device::device_reset()
{
	m_clkcnt = 0;
	m_pc[FOREGROUND] = 0;
	m_pc[BACKGROUND] = 0x80;
}


void cquestlin_cpu_device::state_string_export(const device_state_entry &entry, std::string &str) const
{
	switch (entry.index())
	{
		case STATE_GENFLAGS:
			str = string_format("%c%c%c|%cG",
					m_cflag ? 'C' : '.',
					m_vflag ? 'V' : '.',
					m_f ? '.' : 'Z',
					(m_clkcnt & 3) ? 'B' : 'F');
			break;
	}
}


/***************************************************************************
    SOUND CORE EXECUTION LOOP
***************************************************************************/

#define SND_PC          (m_pc)
#define SND_DATA_IN     (_ramen ? m_sound_data[m_platch] : m_dinlatch)

enum snd_latch_type
{
	PLTCH = 0,
	DAC = 1,
	ADLATCH = 2
};

bool cquestsnd_cpu_device::do_sndjmp(u8 jmp)
{
	switch (jmp)
	{
		/* JUMP */ case 0: return true;
		/* MSB  */ case 2: return m_f & 0x8000 ? false : true;
		/* !MSB */ case 3: return m_f & 0x8000 ? true : false;
		/* ZERO */ case 5: return m_f == 0 ? false : true;
		/* OVR  */ case 6: return m_vflag ? false : true;
		/* LOOP */ case 7: return m_adrcntr & 0x80 ? false: true;
	}

	return false;
}

void cquestsnd_cpu_device::execute_run()
{
	/* Core execution loop */
	do
	{
		/* Decode the instruction */
		u64 inst = m_cache.read_qword(SND_PC);
		u32 inslow = inst & 0xffffffff;
		u32 inshig = inst >> 32;

		u8 t               = (inshig >> 24) & 0xff;
		const u8 b         = (inshig >> 20) & 0xf;
		const u8 a         = (inshig >> 16) & 0xf;
		const u8 ci        = (inshig >> 15) & 1;
		const u8 i5_3      = (inshig >> 12) & 7;
		const bool _ramen  = (inshig >> 11) & 1;
		const u8 i2_0      = (inshig >> 8) & 7;
		const bool rtnltch = (inshig >> 7) & 1;
		const u8 jmp       = (inshig >> 4) & 7;
		const bool inca    = (inshig >> 3) & 1;
		const u8 i8_6      = (inshig >> 0) & 7;
		const bool _ipram  = (inslow >> 31) & 1;
		const bool _ipwrt  = (inslow >> 30) & 1;
		const u8 latch     = (inslow >> 28) & 3;
		const bool rtn     = (inslow >> 27) & 1;
		const bool _rin    = (inslow >> 26) & 1;

		debugger_instruction_hook(m_pc);

		/* Don't think this matters, but just in case */
		if (rtn)
			t = m_rtnlatch;

		/* Handle the AM2901 ALU instruction */
		{
			u16 r = 0;
			u16 s = 0;

			u32 res = 0;
			u32 cflag = 0;
			u32 vflag = 0;

			/* Determine the ALU sources */
			switch (i2_0)
			{
				case AQ: r = m_ram[a]; s = m_q;      break;
				case AB: r = m_ram[a]; s = m_ram[b]; break;
				case ZQ: r = 0;                s = m_q;      break;
				case ZB: r = 0;                s = m_ram[b]; break;
				case ZA: r = 0;                s = m_ram[a]; break;
				case DA: r = SND_DATA_IN;      s = m_ram[a]; break;
				case DQ: r = SND_DATA_IN;      s = m_q;      break;
				case DZ: r = SND_DATA_IN;      s = 0;                break;
			}

			/* Perform the ALU operation */
			switch (i5_3)
			{
				case ADD:
					res = r + s + ci;
					cflag = (res >> 16) & 1;
					vflag = (((r & 0x7fff) + (s & 0x7fff) + ci) >> 15) ^ cflag;
					break;
				case SUBR:
					res = ~r + s + ci;
					cflag = (res >> 16) & 1;
					vflag = (((s & 0x7fff) + (~r & 0x7fff) + ci) >> 15) ^ cflag;
					break;
				case SUBS:
					res = r + ~s + ci;
					cflag = (res >> 16) & 1;
					vflag = (((r & 0x7fff) + (~s & 0x7fff) + ci) >> 15) ^ cflag;
					break;
				case OR:
					res = r | s;
					break;
				case AND:
					res = r & s;
					break;
				case NOTRS:
					res = ~r & s;
					break;
				case EXOR:
					res = r ^ s;
					break;
				case EXNOR:
					res = ~(r ^ s);
					break;
			}

			m_f = res;
			m_cflag = cflag;
			m_vflag = vflag;

			switch (i8_6)
			{
				case QREG:
					m_q = m_f;
					m_y = m_f;
					break;
				case NOP:
					m_y = m_f;
					break;
				case RAMA:
					m_y = m_ram[a];
					m_ram[b] = m_f;
					break;
				case RAMF:
					m_ram[b] = m_f;
					m_y = m_f;
					break;
				case RAMQD:
				{
					u16 qin;

					m_ram[b] = (_rin ? 0 : 0x8000) | (m_f >> 1);
					m_q >>= 1;
					m_y = m_f;

					/* When right shifting Q, we need to OR in a value */
					qin = (((m_y >> 15) ^ (m_y >> 1)) & 1) ? 0 : 0x8000;

					m_q |= qin;
					break;
				}
				case RAMD:
					m_ram[b] = (_rin ? 0 : 0x8000) | (m_f >> 1);
					m_y = m_f;
					break;
				case RAMQU:
					m_ram[b] = (m_f << 1) | (_rin ? 0 : 0x0001);
					m_q <<= 1;
					m_y = m_f;
					break;
				case RAMU:
					m_ram[b] = (m_f << 1) | (_rin ? 0 : 0x0001);
					m_y = m_f;
					break;
			}
		}

		/* Now handle any SRAM accesses from the previous cycle */
		if (!m_prev_ipram)
		{
			u16 addr = m_adrlatch | (m_adrcntr & 0x7f);

			if (!m_prev_ipwrt)
			m_sram[addr] = m_ramwlatch;
			else
			m_dinlatch = m_sram[addr];
		}

		/* Handle latches */
		if (latch == PLTCH)
		{
			m_platch = ((t & 3) << 9) | ((m_y >> 6) & 0x1ff);
		}
		else if (latch == DAC)
		{
			m_dac_w((m_y & 0xfff0) | ((m_adrcntr >> 3) & 0xf));
		}
		else if (latch == ADLATCH)
		{
			/* Load the SRAM address counter - this value is instantly loaded */
			m_adrcntr = m_y & 0x7f;

			/* Also load the SRAM address latch */
			m_adrlatch = m_y & 0x780;
		}

		/* Check for jump/return */
		if (do_sndjmp(jmp))
			m_pc = rtn ? m_rtnlatch : t;
		else
			m_pc++;

		/* Load the return latch? (Obviously a load and a ret in the same cycle are invalid) */
		if (rtnltch)
			m_rtnlatch = t;

		/* Only increment the sound counter if not loading */
		if (inca && latch != ADLATCH)
			m_adrcntr++;

		/* Latch data for a RAM write (do actual write on the next cycle) */
		if (!_ipwrt)
			m_ramwlatch = m_y;

		/* Save level sensitive bits */
		m_prev_ipram = _ipram;
		m_prev_ipwrt = _ipwrt;

		m_icount--;
	} while (m_icount > 0);
}


/***************************************************************************
    ROTATE CORE EXECUTION LOOP
***************************************************************************/

#define ROT_PC          (m_pc & 0x1ff)

enum rot_spf
{
	SPF_UNUSED0 = 0,
	SPF_UNUSED1 = 1,
	SPF_OP      = 2,
	SPF_RET     = 3,
	SPF_SQLTCH  = 4,
	SPF_SWRT    = 5,
	SPF_DIV     = 6,
	SPF_MULT    = 7,
	SPF_DRED    = 8,
	SPF_DWRT    = 9
};

enum rot_yout
{
	YOUT_UNUSED0 = 0,
	YOUT_UNUSED1 = 1,
	YOUT_Y2LDA   = 2,
	YOUT_Y2LDD   = 3,
	YOUT_Y2DAD   = 4,
	YOUT_Y2DYN   = 5,
	YOUT_Y2R     = 6,
	YOUT_Y2D     = 7
};

/* Sync is asserted for the duration of every fourth cycle */
/* The Dynamic RAM latch clocks in a value at the end of this cycle */
/* So CPU waits for sync before reading from DRAM */

int cquestrot_cpu_device::do_rotjmp(u8 jmp)
{
	int ret = 0;

	switch (jmp & 7)
	{
		/*        */ case 0: ret = 0;                         break;
		/* SEQ    */ case 1: ret = (m_seqcnt == 0xf); break;
		/* CAROUT */ case 2: ret = m_cflag;           break;
		/* SYNC   */ case 3: ret = !(m_clkcnt & 0x3); break;
		/* LDWAIT */ case 4: ret = 0;                         break;
		/* MSB    */ case 5: ret = BIT(m_f, 15);      break;
		/* >=1    */ case 6: ret = (!_BIT(m_f, 15) && !(m_f == 0)); break;
		/* ZERO   */ case 7: ret = (m_f == 0);        break;
	}

	return !(!ret ^ BIT(jmp, 3));
}


#define ROT_SRAM_ADDRESS    ((m_dsrclatch & 2) ? m_yrlatch : (m_rsrclatch | 0x700))


void cquestrot_cpu_device::execute_run()
{
	/* Core execution loop */
	do
	{
		/* Decode the instruction */
		u64 inst = m_cache.read_qword(ROT_PC);

		u32 inslow = inst & 0xffffffff;
		u32 inshig = inst >> 32;

		u16 t              = (inshig >> 20) & 0xfff;
		const u8 jmp       = (inshig >> 16) & 0xf;
		const u8 spf       = (inshig >> 12) & 0xf;
		const bool rsrc    = (inshig >> 11) & 0x1;
		const u8 yout      = (inshig >> 8) & 0x7;
		const u8 sel       = (inshig >> 6) & 0x3;
		const u8 dsrc      = (inshig >> 4) & 0x3;
		const u8 b         = (inshig >> 0) & 0xf;
		const u8 a         = (inslow >> 28) & 0xf;
		const u8 i8_6      = (inslow >> 24) & 0x7;
		u8 ci              = (inslow >> 23) & 0x1;
		u8 i5_3            = (inslow >> 20) & 0x7;
		const bool _sex    = (inslow >> 19) & 0x1;
		u8 i2_0            = (inslow >> 16) & 0x7;

		int dsrclatch;
		u16 data_in = 0xffff;

		debugger_instruction_hook(ROT_PC);

		/* Handle DRAM accesses - I ought to check this... */
		if (!(m_clkcnt & 3))
		{
			if (m_wc)
			{
				m_wc = 0;
				m_dram[m_dynaddr & 0x3fff] = m_dyndata;
			}
			if (m_rc)
			{
				m_rc = 0;
				m_dinlatch = m_dram[m_dynaddr & 0x3fff];
			}
		}

		/* Flag pending DRAM accesses */
		if (!m_prev_dwrt)
			m_wc = 1;
		else if (!m_prev_dred)
			m_rc = 1;

		/* What's on the D-Bus? */
		if (~m_dsrclatch & 0x10)
			data_in = m_dinlatch;
		else if (~m_dsrclatch & 0x20)
			data_in = m_sram[ROT_SRAM_ADDRESS];
		else if (~m_dsrclatch & 0x40)
			data_in = m_ydlatch;
		else if (~m_dsrclatch & 0x80)
			data_in = t & 0xfff;

		/* What's on the T-Bus? */
		if ((spf == SPF_RET) && (m_dsrclatch & 0x80))
			t = data_in;
		else if (spf == SPF_OP)
			t = (t & ~0xf) | (data_in >> 12);


		if (~m_dsrclatch & 1)
			m_sram[ROT_SRAM_ADDRESS] = data_in;


		/* Sign extend ALU input? */
		if (!_sex)
			data_in = (data_in & ~0xf000) | ((data_in & 0x800) ? 0xf000 : 0);

		/* No do the ALU operation */
		{
			u16 r = 0;
			u16 s = 0;

			u32 res = 0;
			u32 cflag = 0;
			u32 vflag = 0;

			/* First, determine correct I1 bit */
			if ((spf == SPF_MULT) && !_BIT(m_q, 0))
				i2_0 |= 2;

			/* Determine the ALU sources */
			switch (i2_0)
			{
				case 0: r = m_ram[a]; s = m_q;      break;
				case 1: r = m_ram[a]; s = m_ram[b]; break;
				case 2: r = 0;                s = m_q;      break;
				case 3: r = 0;                s = m_ram[b]; break;
				case 4: r = 0;                s = m_ram[a]; break;
				case 5: r = data_in;          s = m_ram[a]; break;
				case 6: r = data_in;          s = m_q;      break;
				case 7: r = data_in;          s = 0;                break;
			}

			/* Next, determine the I3 and carry bits */
			if ((spf == SPF_DIV) && m_divreg)
			{
				i5_3 |= 1;
				ci = 1;
			}

			/* Perform the ALU operation */
			switch (i5_3)
			{
				case ADD:
					res = r + s + ci;
					cflag = (res >> 16) & 1;
					vflag = (((r & 0x7fff) + (s & 0x7fff) + ci) >> 15) ^ cflag;
					break;
				case SUBR:
					res = ~r + s + ci;
					cflag = (res >> 16) & 1;
					vflag = (((s & 0x7fff) + (~r & 0x7fff) + ci) >> 15) ^ cflag;
					break;
				case SUBS:
					res = r + ~s + ci;
					cflag = (res >> 16) & 1;
					vflag = (((r & 0x7fff) + (~s & 0x7fff) + ci) >> 15) ^ cflag;
					break;
				case OR:
					res = r | s;
					break;
				case AND:
					res = r & s;
					break;
				case NOTRS:
					res = ~r & s;
					break;
				case EXOR:
					res = r ^ s;
					break;
				case EXNOR:
					res = ~(r ^ s);
					break;
			}

			m_f = res;
			m_cflag = cflag;
			m_vflag = vflag;

			switch (i8_6)
			{
				case QREG:
					m_q = m_f;
					m_y = m_f;
					break;
				case NOP:
					m_y = m_f;
					break;
				case RAMA:
					m_y = m_ram[a];
					m_ram[b] = m_f;
					break;
				case RAMF:
					m_ram[b] = m_f;
					m_y = m_f;
					break;
				case RAMQD:
				{
					u16 q0 = m_q & 1;
					u16 r0 = m_f & 1;
					u16 q15 = 0;
					u16 r15 = 0;

					/* Determine Q15 and RAM15 */
					switch (sel)
					{
						case 0: q15 = r15 = 0;
								break;
						case 1: q15 = r15 = 0x8000;
								break;
						case 2: q15 = q0 << 15;
								r15 = r0 << 15;
								break;
						case 3: q15 = r0 << 15;
								r15 = (m_vflag ^ BIT(m_f, 15)) << 15;
								break;
					}

					m_ram[b] = r15 | (m_f >> 1);
					m_q = q15 | (m_q >> 1);
					m_y = m_f;
					break;
				}
				case RAMD:
				{
					u16 r0 = m_f & 1;
					u16 r15 = 0;

					switch (sel)
					{
						case 0: r15 = 0;        break;
						case 1: r15 = 0x8000;   break;
						case 2: r15 = r0 << 15; break;
						case 3:
							r15 = (m_vflag ^ BIT(m_f, 15)) << 15;
							break;
					}

					m_ram[b] = r15 | (m_f >> 1);
					m_y = m_f;
					break;
				}
				case RAMQU:
				{
					u16 q15 = BIT(m_q, 15);
					u16 r15 = BIT(m_f, 15);
					u16 q0 = 0;
					u16 r0 = 0;

					switch (sel)
					{
						case 0: q0 = 0; r0 = 0;     break;
						case 1: q0 = 1; r0 = 1;     break;
						case 2: q0 = q15; r0 = r15; break;
						case 3:
						{
							q0 = (spf == SPF_DIV) && !BIT(m_f, 15);
							r0 = q15;
							break;
						}
					}

					m_ram[b] = (m_f << 1) | r0;
					m_q = (m_q << 1) | q0;
					m_y = m_f;
					break;
				}
				case RAMU:
				{
					u16 q15 = BIT(m_q, 15);
					u16 r15 = BIT(m_f, 15);
					u16 r0 = 0;

					switch (sel)
					{
						case 0: r0 = 0;     break;
						case 1: r0 = 1;     break;
						case 2: r0 = r15;   break;
						case 3: r0 = q15;   break;
					}

					m_ram[b] = (m_f << 1) | r0;
					m_y = m_f;
					break;
				}
			}
		}

		/* Check for jump */
		if (do_rotjmp(jmp))
			m_pc = t;
		else
			m_pc = (m_pc + 1) & 0xfff;

		/* Rising edge; update the sequence counter */
		if (spf == SPF_SQLTCH)
			m_seqcnt = t & 0xf;
		else if ((spf == SPF_MULT) || (spf == SPF_DIV))
			m_seqcnt = (m_seqcnt + 1) & 0xf;

		/* Rising edge; write data source reg */
		dsrclatch =
				(~(0x10 << dsrc) & 0xf0)
				| (rsrc ? 0x04 : 0x02)
				| (spf == SPF_SWRT ? 0 : 1);

		/* R-latch is written on rising edge of dsrclatch bit 2 */
		if (!_BIT(m_dsrclatch, 2) && _BIT(dsrclatch, 2))
			m_rsrclatch = t & 0xff;

		m_dsrclatch = dsrclatch;

		/* Handle latching on rising edge */
		switch (yout)
		{
			case YOUT_Y2LDA:
			{
				m_lineaddr = m_y & 0xfff;
				break;
			}
			case YOUT_Y2LDD:
			{
				m_linedata = ((t & 0xf) << 12) | (m_y & 0xfff);
				m_linedata_w(m_lineaddr, m_linedata);
				break;
			}
			case YOUT_Y2DAD: m_dynaddr = m_y & 0x3fff;  break;
			case YOUT_Y2DYN: m_dyndata = m_y & 0xffff;  break;
			case YOUT_Y2R:   m_yrlatch = m_y & 0x7ff;   break;
			case YOUT_Y2D:   m_ydlatch = m_y;           break;
		}

		/* Clock in the divide register */
		m_divreg = (spf == SPF_DIV) && !_BIT(m_f, 15);

		/* DRAM accessing */
		m_prev_dred = !(spf == SPF_DRED);
		m_prev_dwrt = !(spf == SPF_DWRT);

		m_clkcnt++;
		m_icount--;
	} while (m_icount > 0);
}


/***************************************************************************
    LINE DRAWER CORE EXECUTION LOOP
***************************************************************************/

enum line_spf
{
	LSPF_UNUSED  = 0,
	LSPF_FSTOP   = 1,
	LSPF_SREG    = 2,
	LSPF_FSTRT   = 3,
	LSPF_PWRT    = 4,
	LSPF_MULT    = 5,
	LSPF_LSTOP   = 6,
	LSPF_BRES    = 7
};

enum line_latch
{
	LLATCH_UNUSED   = 0,
	LLATCH_SEQLATCH = 1,
	LLATCH_XLATCH   = 2,
	LLATCH_YLATCH   = 3,
	LLATCH_BADLATCH = 4,
	LLATCH_FADLATCH = 5,
	LLATCH_CLATCH   = 6,
	LLATCH_ZLATCH   = 7
};

enum sreg_bits
{
	SREG_E0     = 0,
	SREG_DX_DY  = 1,
	SREG_DY     = 2,
	SREG_DX     = 3,
	SREG_LE0    = 4,
	SREG_LDX_DY = 5,
	SREG_LDY    = 6,
	SREG_LDX    = 7
};

int cquestlin_cpu_device::do_linjmp(u8 jmp)
{
	int ret = 0;

	switch (jmp & 7)
	{
		/*        */ case 0: ret = 0; break;
		/* MSB    */ case 1: ret = BIT(m_f, 11); break;
		/* SEQ    */ case 2: ret = (m_seqcnt == 0xfff); break;
		/* >0     */ case 3: ret = !(m_f == 0) && !_BIT(m_f, 11); break;
		/* CAROUT */ case 4: ret = (m_cflag); break;
		/* ZERO   */ case 5: ret = (m_f == 0); break;
	}

	return !(!ret ^ BIT(jmp, 3));
}


void cquestlin_cpu_device::cubeqcpu_swap_line_banks()
{
	m_field = m_field ^ 1;
}


void cquestlin_cpu_device::cubeqcpu_clear_stack()
{
	memset(&m_ptr_ram[m_field * 256], 0, 256);
}


u8 cquestlin_cpu_device::cubeqcpu_get_ptr_ram_val(int i)
{
	return m_ptr_ram[((m_field^1) * 256) + i];
}


u32* cquestlin_cpu_device::cubeqcpu_get_stack_ram()
{
	if (m_field != ODD_FIELD)
		return m_o_stack;
	else
		return m_e_stack;
}


void cquestlin_cpu_device::execute_run()
{
#define LINE_PC ((m_pc[prog] & 0x7f) | ((prog == BACKGROUND) ? 0x80 : 0))

	u32  *stack_ram;
	u8   *ptr_ram;

	/* Check the field and set the stack/pointer RAM pointers appropriately */
	if (m_field == ODD_FIELD)
	{
		stack_ram = m_o_stack;
		ptr_ram = &m_ptr_ram[0];
	}
	else
	{
		stack_ram = m_e_stack;
		ptr_ram = &m_ptr_ram[0x100];
	}

	/* Core execution loop */
	do
	{
		/* Are we executing the foreground or backgroud program? */
		int prog = (m_clkcnt & 3) ? BACKGROUND : FOREGROUND;

		m_curpc = LINE_PC;
		u64 inst = m_cache.read_qword(LINE_PC);

		u32 inslow = inst & 0xffffffff;
		u32 inshig = inst >> 32;

		u8 t               = (inshig >> 24) & 0xff;
		const u8 jmp       = (inshig >> 20) & 0xf;
		const u8 latch     = (inshig >> 16) & 0x7;
		const bool op      = (inshig >> 15) & 0x1;
		const u8 spf       = (inshig >> 12) & 0x7;
		const u8 b         = (inshig >> 8) & 0xf;
		u8 a               = (inshig >> 4) & 0xf;
		const u8 i8_6      = (inshig >> 0) & 0x7;
		const u8 ci        = (inslow >> 31) & 0x1;
		const u8 i5_3      = (inslow >> 28) & 0x7;
		const u8 _pbcs     = (inslow >> 27) & 0x1;
		u8 i2_0            = (inslow >> 24) & 0x7;

		u16  data_in = 0;

		debugger_instruction_hook(m_pc[prog]);

		/* Handle accesses to and from shared SRAM */
		if (prog == FOREGROUND)
		{
			if (!_BIT(m_fglatch, 5))
				data_in = m_sram[m_fadlatch];
			else
				data_in = m_linedata_r();
		}
		else
		{
			if (!_BIT(m_bglatch, 4))
				m_sram[m_badlatch] = m_sramdlatch;
			else if (_BIT(m_bglatch, 2))
				data_in = m_sram[m_badlatch];
			else
				data_in = m_linedata_r();
		}

		/* Handle a write to stack RAM (/DOWRT) */
		if ((m_clkcnt & 3) == 1)
		{
			if (_BIT(m_fglatch, 4) && (m_ycnt < 256))
			{
				/* 20-bit words */
				u32 data;
				u16 h = m_xcnt;
				u8 v = m_ycnt & 0xff;

				/* Clamp H between 0 and 319 */
				if (h >= 320)
					h = (h & 0x800) ? 0 : 319;

				/* Stack word type depends on STOP/#START bit */
				if (_BIT(m_fglatch, 3))
					data = (0 << 19) | (h << 8) | m_zlatch;
				else
					data = (1 << 19) | ((m_clatch & 0x100) << 9) | (h << 8) | (m_clatch & 0xff);

				stack_ram[(v << 7) | (ptr_ram[v] & 0x7f)] = data;

				/* Also increment the pointer RAM entry. Note that it cannot exceed 128 */
				ptr_ram[v] = (ptr_ram[v] + 1) & 0x7f;
			}
		}

		/* Override T3-0? */
		if (op)
			t = (t & ~0xf) | (data_in >> 12);

		/* Determine the correct I1 bit  */
		if ((spf == LSPF_MULT) && !_BIT(m_q, 0))
			i2_0 |= 2;

		/* Determine A0 (BRESA0) */
		if ((prog == FOREGROUND) && !_BIT(m_fglatch, 2))
			a |= m_gt0reg;

		/* Now do the ALU operation */
		{
			u16 r = 0;
			u16 s = 0;

			u16 res = 0;
			u32 cflag = 0;
			u32 vflag = 0;

			/* Determine the ALU sources */
			switch (i2_0)
			{
				case 0: r = m_ram[a];   s = m_q;      break;
				case 1: r = m_ram[a];   s = m_ram[b]; break;
				case 2: r = 0;                  s = m_q;      break;
				case 3: r = 0;                  s = m_ram[b]; break;
				case 4: r = 0;                  s = m_ram[a]; break;
				case 5: r = data_in;            s = m_ram[a]; break;
				case 6: r = data_in;            s = m_q;      break;
				case 7: r = data_in;            s = 0;                break;
			}

			/* 12-bits */
			r &= 0xfff;
			s &= 0xfff;

			/* Perform the 12-bit ALU operation */
			switch (i5_3)
			{
				case ADD:
					res = r + s + ci;
					cflag = (res >> 12) & 1;
					vflag = (((r & 0x7ff) + (s & 0x7ff) + ci) >> 11) ^ cflag;
					break;
				case SUBR:
					res = (r ^ 0x0FFF) + s + ci;
					cflag = (res >> 12) & 1;
					vflag = (((s & 0x7ff) + (~r & 0x7ff) + ci) >> 11) ^ cflag;
					break;
				case SUBS:
					res = r + (s ^ 0x0FFF) + ci;
					cflag = (res >> 12) & 1;
					vflag = (((r & 0x7ff) + (~s & 0x7ff) + ci) >> 11) ^ cflag;
					break;
				case OR:
					res = r | s;
					break;
				case AND:
					res = r & s;
					break;
				case NOTRS:
					res = ~r & s;
					break;
				case EXOR:
					res = r ^ s;
					break;
				case EXNOR:
					res = ~(r ^ s);
					break;
			}

			m_f = res & 0xfff;
			m_cflag = cflag;
			m_vflag = vflag;

			switch (i8_6)
			{
				case QREG:
					m_q = m_f;
					m_y = m_f;
					break;
				case NOP:
					m_y = m_f;
					break;
				case RAMA:
					m_y = m_ram[a];
					m_ram[b] = m_f;
					break;
				case RAMF:
					m_ram[b] = m_f;
					m_y = m_f;
					break;
				case RAMQD:
				{
					u16 r11 = (BIT(m_f, 11) ^ m_vflag) ? 0x800 : 0;
					u16 q11 = (prog == BACKGROUND) ? 0x800 : 0;

					m_ram[b] = r11 | (m_f >> 1);
					m_q = q11 | (m_q >> 1);
					m_y = m_f;
					break;
				}
				case RAMD:
				{
					u16 r11 = (BIT(m_f, 11) ^ m_vflag) ? 0x800 : 0;

					m_ram[b] = r11 | (m_f >> 1);
					m_y = m_f;
					break;
				}
				case RAMQU:
				{
					/* Determine shift inputs */
					u16 r0 = (prog == BACKGROUND);

					/* This should never happen - Q0 will be invalid */
					m_ram[b] = (m_f << 1) | r0;
					m_q = (m_q << 1) | 0;
					m_y = m_f;
					break;
				}
				case RAMU:
				{
					u16 r0 = (prog == BACKGROUND);

					m_ram[b] = (m_f << 1) | r0;
					m_y = m_f;
					break;
				}
			}
		}

		/* Adjust program counter */
		if (do_linjmp(jmp))
			m_pc[prog] = t & 0x7f;
		else
			m_pc[prog] = (m_pc[prog] + 1) & 0x7f;

		if (prog == BACKGROUND)
			m_pc[prog] |= 0x80;
		else
		{
			/* Handle events that happen during FG execution */
			if (latch == LLATCH_XLATCH)
				m_xcnt = m_y & 0xfff;
			else
			{
				int _xcet;
				int mux_sel = (BIT(m_sreg, SREG_DX_DY) << 1) | (BIT(m_sreg, SREG_DX) ^ BIT(m_sreg, SREG_DY));

				if (mux_sel == 0)
					_xcet = !(spf == LSPF_BRES);
				else if (mux_sel == 1)
					_xcet = _BIT(m_fglatch, 1);
				else if (mux_sel == 2)
					_xcet = !(m_gt0reg && (spf == LSPF_BRES));
				else
					_xcet = _BIT(m_fglatch, 0);

				if (!_xcet)
					m_xcnt = (m_xcnt + (_BIT(m_sreg, SREG_DX) ? 1 : -1)) & 0xfff;
			}

			if (latch == LLATCH_YLATCH)
				m_ycnt = m_y & 0xfff;
			else
			{
				int _ycet;
				int mux_sel = (BIT(m_sreg, SREG_DX_DY) << 1) | (BIT(m_sreg, SREG_DX) ^ BIT(m_sreg, SREG_DY));

				if (mux_sel == 0)
					_ycet = !(m_gt0reg && (spf == LSPF_BRES));
				else if (mux_sel == 1)
					_ycet = _BIT(m_fglatch, 0);
				else if (mux_sel == 2)
					_ycet = !(spf == LSPF_BRES);
				else
					_ycet = _BIT(m_fglatch, 1);

				if (!_ycet)
					m_ycnt = (m_ycnt + (_BIT(m_sreg, SREG_DY) ? 1 : -1)) & 0xfff;
			}
		}

		if (latch == LLATCH_CLATCH)
			m_clatch = m_y & 0x1ff;
		else if (latch == LLATCH_ZLATCH)
			m_zlatch = m_y & 0xff;
		else if (latch == LLATCH_FADLATCH)
			m_fadlatch = m_y & 0xfff;
		else if (latch == LLATCH_BADLATCH)
			m_badlatch = m_y & 0xfff;

		/* What about the SRAM dlatch? */
		if (!_BIT(m_bglatch, 5))
			m_sramdlatch = ((t & 0xf) << 12) | (m_y & 0x0fff);

		/* BG and FG latches */
		if (prog == FOREGROUND)
		{
			int mux_sel = (!(spf == LSPF_FSTOP) << 1) | !(spf == LSPF_LSTOP);
			int dowrt;
			int start_stop;

			/* Handle the stack write and start/stop mux */
			if (mux_sel == 0)
			{
				dowrt = 0;
				start_stop = 0;
			}
			else if (mux_sel == 1)
			{
				dowrt = m_fdxreg ^ BIT(m_sreg, SREG_DX);
				start_stop = m_fdxreg;
			}
			else if (mux_sel == 2)
			{
				dowrt = BIT(m_sreg, SREG_LDX) ^ BIT(m_sreg, SREG_DX);
				start_stop = BIT(m_sreg, SREG_DX);
			}
			else
			{
				dowrt = (spf == LSPF_BRES) && (_BIT(m_sreg, SREG_DX_DY) || m_gt0reg);
				start_stop = BIT(m_sreg, SREG_DY);
			}

			m_fglatch =
					(latch == LLATCH_FADLATCH ? 0 : (1 << 5))
					| (dowrt << 4)
					| (start_stop << 3)
					| (_pbcs << 2)
					| (spf == LSPF_BRES ? 0 : (1 << 1))
					| (m_gt0reg && (spf == LSPF_BRES) ? 0 : 1);
		}
		else
		{
			int _lpwrt = BIT(m_bglatch, 5);

			m_bglatch =
					(!(spf == LSPF_PWRT) << 5)
					| (_lpwrt << 4)
					| ((!_lpwrt || (!(spf == LSPF_PWRT) && (latch == LLATCH_BADLATCH))) << 2);
		}

		/* Clock-in another bit into the sign bit shifter? */
		if (spf == LSPF_SREG)
		{
			/* The sign bit is inverted */
			m_sreg = (m_sreg << 1) | !BIT(m_f, 11);

			/* Also latch the >0 reg */
			m_gt0reg = !(m_f == 0) && !_BIT(m_f, 11);
		}
		else if (spf == LSPF_FSTRT)
		{
			m_fdxreg = BIT(m_sreg, 3);
		}

		/* Load or increment sequence counter? */
		if (latch == LLATCH_SEQLATCH)
		{
			m_seqcnt = m_y & 0xfff;
		}
		else if (spf == LSPF_BRES)
		{
			m_seqcnt = (m_seqcnt + 1) & 0xfff;

			/* Also latch the >0 reg */
			m_gt0reg = !(m_f == 0) && !_BIT(m_f, 11);
		}

		m_icount--;
		m_clkcnt++;
	} while (m_icount > 0);
}
