-devices/cpu/unsp: Various fixes: [Ryan Holtz]

* Fixed push and pop instructions in the DRC. Fixes Wall-E.
 * Eliminated the use of intermediate register I7 in the DRC.
 * Reworked DRC timing to be fully in line with the interpreter.
 * Reworked DRC block checksumming to not route through the memory system.
 * Fixed DRC block checksumming to include all words in an opcode. Fixes V.Smile intro graphical glitches.
 * Increased cache size and reduced block size to reduce frequency of cache flushes and associated recompiles.
 * Added more verbose optional register and write logging.
 * Added a direct setter for IRQ lines to avoid a scheduler sync, pending merging the spg2xx_device into a unsp_device subclass.
 * Added direct setters/getters for the data segment as well.
This commit is contained in:
Ryan Holtz 2019-01-14 10:06:52 +01:00
parent b5e2e78671
commit 8f330896e2
4 changed files with 128 additions and 113 deletions

View File

@ -24,7 +24,7 @@
DEFINE_DEVICE_TYPE(UNSP, unsp_device, "unsp", "SunPlus u'nSP")
/* size of the execution code cache */
#define CACHE_SIZE (32 * 1024 * 1024)
#define CACHE_SIZE (64 * 1024 * 1024)
unsp_device::unsp_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: cpu_device(mconfig, UNSP, tag, owner, clock)
@ -32,13 +32,13 @@ unsp_device::unsp_device(const machine_config &mconfig, const char *tag, device_
, m_program(nullptr)
, m_core(nullptr)
, m_debugger_temp(0)
#if UNSP_LOG_OPCODES
#if UNSP_LOG_OPCODES || UNSP_LOG_REGS
, m_log_ops(0)
#endif
, m_cache(CACHE_SIZE + sizeof(unsp_device))
, m_drcuml(nullptr)
, m_drcfe(nullptr)
, m_drcoptions(UNSP_STRICT_VERIFY)
, m_drcoptions(0)
, m_cache_dirty(0)
, m_entry(nullptr)
, m_nocode(nullptr)
@ -78,6 +78,9 @@ uint16_t unsp_device::read16(uint32_t address)
void unsp_device::write16(uint32_t address, uint16_t data)
{
#if UNSP_LOG_REGS
log_write(address, data);
#endif
m_program->write_word(address, data);
}
@ -148,7 +151,7 @@ void unsp_device::device_start()
state_add(UNSP_IRQ, "IRQ", m_core->m_irq).formatstr("%1u");
state_add(UNSP_FIQ, "FIQ", m_core->m_fiq).formatstr("%1u");
state_add(UNSP_SB, "SB", m_core->m_sb).formatstr("%1u");
#if UNSP_LOG_OPCODES
#if UNSP_LOG_OPCODES || UNSP_LOG_REGS
state_add(UNSP_LOG_OPS,"LOG", m_log_ops).formatstr("%1u");
#endif
@ -197,10 +200,20 @@ void unsp_device::device_stop()
#if UNSP_LOG_REGS
void unsp_device::log_regs()
{
if (m_log_ops == 0)
return;
fwrite(m_core->m_r, sizeof(uint32_t), 8, m_log_file);
fwrite(&m_core->m_sb, sizeof(uint32_t), 1, m_log_file);
fflush(m_log_file);
fwrite(&m_core->m_icount, sizeof(uint32_t), 1, m_log_file);
}
void unsp_device::log_write(uint32_t addr, uint32_t data)
{
addr |= 0x80000000;
fwrite(&addr, sizeof(uint32_t), 1, m_log_file);
fwrite(&data, sizeof(uint32_t), 1, m_log_file);
}
#endif
void unsp_device::state_string_export(const device_state_entry &entry, std::string &str) const
@ -948,11 +961,15 @@ void unsp_device::execute_run()
unsp_disassembler dasm;
#endif
while (m_core->m_icount > 0)
while (m_core->m_icount >= 0)
{
debugger_instruction_hook(UNSP_LPC);
const uint32_t op = read16(UNSP_LPC);
#if UNSP_LOG_REGS
log_regs();
#endif
#if UNSP_LOG_OPCODES
if (m_log_ops)
{
@ -962,10 +979,6 @@ void unsp_device::execute_run()
}
#endif
#if UNSP_LOG_REGS
log_regs();
#endif
add_lpc(1);
execute_one(op);
@ -977,16 +990,26 @@ void unsp_device::execute_run()
/*****************************************************************************/
void unsp_device::execute_set_input(int irqline, int state)
void unsp_device::execute_set_input(int inputnum, int state)
{
m_core->m_sirq &= ~(1 << irqline);
set_state_unsynced(inputnum, state);
}
uint8_t unsp_device::get_csb()
{
return 1 << ((UNSP_LPC >> 20) & 3);
}
void unsp_device::set_state_unsynced(int inputnum, int state)
{
m_core->m_sirq &= ~(1 << inputnum);
if(!state)
{
return;
}
switch (irqline)
switch (inputnum)
{
case UNSP_IRQ0_LINE:
case UNSP_IRQ1_LINE:
@ -997,14 +1020,20 @@ void unsp_device::execute_set_input(int irqline, int state)
case UNSP_IRQ6_LINE:
case UNSP_IRQ7_LINE:
case UNSP_FIQ_LINE:
m_core->m_sirq |= (1 << irqline);
m_core->m_sirq |= (1 << inputnum);
break;
case UNSP_BRK_LINE:
break;
}
}
uint8_t unsp_device::get_csb()
uint16_t unsp_device::get_ds()
{
return 1 << ((UNSP_LPC >> 20) & 3);
return (m_core->m_r[REG_SR] >> 10) & 0x3f;
}
void unsp_device::set_ds(uint16_t ds)
{
m_core->m_r[REG_SR] &= 0x03ff;
m_core->m_r[REG_SR] |= (ds & 0x3f) << 10;
}

View File

@ -29,8 +29,6 @@
#define MAPVAR_PC M0
#define MAPVAR_CYCLES M1
#define UNSP_STRICT_VERIFY 0x0001 /* verify all instructions */
#define SINGLE_INSTRUCTION_MODE (0)
#define ENABLE_UNSP_DRC (1)
@ -63,7 +61,7 @@ enum
UNSP_FIQ_EN,
UNSP_IRQ,
UNSP_FIQ,
#if UNSP_LOG_OPCODES
#if UNSP_LOG_OPCODES || UNSP_LOG_REGS
UNSP_SB,
UNSP_LOG_OPS
#else
@ -95,12 +93,25 @@ public:
// construction/destruction
unsp_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
// HACK: IRQ line state can only be modified directly by hardware on-board the SPG SoC itself.
// Therefore, to avoid an unnecessary scheduler sync when the external spg2xx_device sets or
// clears an interrupt line, we provide this direct accessor.
// A more correct but longer-term solution will be to move spg2xx_device to be internal to
// a subclass of unsp_device rather than its own standalone device.
void set_state_unsynced(int inputnum, int state);
uint8_t get_csb();
void set_ds(uint16_t ds);
uint16_t get_ds();
inline void ccfunc_unimplemented();
void invalidate_cache();
#if UNSP_LOG_REGS
void log_regs();
void log_write(uint32_t addr, uint32_t data);
void cfunc_log_write();
#endif
protected:
@ -176,7 +187,6 @@ private:
uint32_t m_arg0;
uint32_t m_arg1;
uint32_t m_arg2;
uint32_t m_jmpdest;
int m_icount;
@ -191,7 +201,7 @@ private:
internal_unsp_state *m_core;
uint32_t m_debugger_temp;
#if UNSP_LOG_OPCODES
#if UNSP_LOG_OPCODES || UNSP_LOG_REGS
uint32_t m_log_ops;
#endif
@ -251,11 +261,9 @@ private:
void static_generate_memory_accessor(bool iswrite, const char *name, uml::code_handle *&handleptr);
void generate_branch(drcuml_block &block, compiler_state &compiler, const opcode_desc *desc);
void generate_update_cycles(drcuml_block &block, compiler_state &compiler, uml::parameter param);
void generate_check_cycles(drcuml_block &block, compiler_state &compiler, uml::parameter param);
void generate_checksum_block(drcuml_block &block, compiler_state &compiler, const opcode_desc *seqhead, const opcode_desc *seqlast);
void generate_sequence_instruction(drcuml_block &block, compiler_state &compiler, const opcode_desc *desc);
void generate_push(drcuml_block &block, uint32_t sp);
void generate_pop(drcuml_block &block, uint32_t sp);
void generate_add_lpc(drcuml_block &block, int32_t offset);
void generate_update_nzsc(drcuml_block &block);
void generate_update_nz(drcuml_block &block);

View File

@ -66,10 +66,20 @@ static void cfunc_unimplemented(void *param)
}
#if UNSP_LOG_REGS
void unsp_device::cfunc_log_write()
{
log_write(m_core->m_arg0, m_core->m_arg1);
}
static void cfunc_log_regs(void *param)
{
((unsp_device *)param)->log_regs();
}
static void ccfunc_log_write(void *param)
{
((unsp_device *)param)->cfunc_log_write();
}
#endif
/***************************************************************************
@ -140,7 +150,7 @@ void unsp_device::code_compile_block(offs_t pc)
try
{
/* start the block */
drcuml_block &block(m_drcuml->begin_block(32768));
drcuml_block &block(m_drcuml->begin_block(1024*8));
/* loop until we get through all instruction sequences */
for (seqhead = desclist; seqhead != nullptr; seqhead = seqlast->next())
@ -186,22 +196,15 @@ void unsp_device::code_compile_block(offs_t pc)
if (seqhead->flags & OPFLAG_IS_BRANCH_TARGET)
UML_LABEL(block, seqhead->pc | 0x80000000);
UML_MOV(block, I7, 0);
UML_CALLH(block, *m_check_interrupts);
/* iterate over instructions in the sequence and compile them */
for (curdesc = seqhead; curdesc != seqlast->next(); curdesc = curdesc->next())
{
generate_check_cycles(block, compiler, curdesc->pc + curdesc->length);
generate_sequence_instruction(block, compiler, curdesc);
UML_CALLH(block, *m_check_interrupts);
}
if (seqlast->flags & OPFLAG_RETURN_TO_START) /* if we need to return to the start, do it */
nextpc = pc;
else /* otherwise we just go to the next instruction */
nextpc = seqlast->pc + seqlast->length;
generate_update_cycles(block, compiler, nextpc);
nextpc = seqlast->pc + seqlast->length;
/* if the last instruction can change modes, use a variable mode; otherwise, assume the same mode */
if (seqlast->next() == nullptr || seqlast->next()->pc != nextpc)
@ -324,7 +327,14 @@ void unsp_device::static_generate_memory_accessor(bool iswrite, const char *name
UML_HANDLE(block, *handleptr);
if (iswrite)
{
#if UNSP_LOG_REGS
UML_MOV(block, mem(&m_core->m_arg0), I0);
UML_MOV(block, mem(&m_core->m_arg1), I1);
UML_CALLC(block, ccfunc_log_write, this);
#endif
UML_WRITE(block, I0, I1, SIZE_WORD, SPACE_PROGRAM);
}
else
UML_READ(block, I1, I0, SIZE_WORD, SPACE_PROGRAM);
UML_RET(block);
@ -470,17 +480,14 @@ void unsp_device::static_generate_trigger_irq()
}
/*-------------------------------------------------
generate_update_cycles - generate code to
subtract cycles from the icount and generate
an exception if out
generate_check_cycles - generate code to
generate an exception if cycles are out
-------------------------------------------------*/
void unsp_device::generate_update_cycles(drcuml_block &block, compiler_state &compiler, uml::parameter param)
void unsp_device::generate_check_cycles(drcuml_block &block, compiler_state &compiler, uml::parameter param)
{
UML_SUB(block, mem(&m_core->m_icount), mem(&m_core->m_icount), I7);
UML_EXHc(block, uml::COND_Z, *m_out_of_cycles, param);
UML_EXHc(block, uml::COND_S, *m_out_of_cycles, param);
UML_MOV(block, I7, 0);
UML_CMP(block, mem(&m_core->m_icount), 0);
UML_EXHc(block, uml::COND_L, *m_out_of_cycles, param);
}
/*-------------------------------------------------
@ -495,33 +502,35 @@ void unsp_device::generate_checksum_block(drcuml_block &block, compiler_state &c
{
block.append_comment("[Validation for %08X]", seqhead->pc);
}
/* loose verify or single instruction: just compare and fail */
if (!(m_drcoptions & UNSP_STRICT_VERIFY) || seqhead->next() == nullptr)
/* full verification; sum up everything */
void *memptr = m_program->get_write_ptr(seqhead->physpc);
UML_LOAD(block, I0, memptr, 0, SIZE_WORD, SCALE_x2);
uint32_t sum = seqhead->opptr.w[0];
for (int i = 1; i < seqhead->length; i++)
{
if (!(seqhead->flags & OPFLAG_VIRTUAL_NOOP))
{
uint32_t sum = seqhead->opptr.w[0];
UML_READ(block, I0, seqhead->physpc, SIZE_WORD, SPACE_PROGRAM);
UML_CMP(block, I0, sum);
UML_EXHc(block, COND_NE, *m_nocode, seqhead->pc);
}
UML_LOAD(block, I1, memptr, i, SIZE_WORD, SCALE_x2);
UML_ADD(block, I0, I0, I1);
sum += ((uint16_t*)memptr)[i];
}
else /* full verification; sum up everything */
for (curdesc = seqhead->next(); curdesc != seqlast->next(); curdesc = curdesc->next())
{
UML_READ(block, I0, seqhead->physpc, SIZE_WORD, SPACE_PROGRAM);
uint32_t sum = seqhead->opptr.w[0];
for (curdesc = seqhead->next(); curdesc != seqlast->next(); curdesc = curdesc->next())
if (!(curdesc->flags & OPFLAG_VIRTUAL_NOOP))
{
if (!(curdesc->flags & OPFLAG_VIRTUAL_NOOP))
memptr = m_program->get_write_ptr(curdesc->physpc);
UML_LOAD(block, I1, memptr, 0, SIZE_WORD, SCALE_x2);
UML_ADD(block, I0, I0, I1);
sum += curdesc->opptr.w[0];
for (int i = 1; i < curdesc->length; i++)
{
UML_READ(block, I1, curdesc->physpc, SIZE_WORD, SPACE_PROGRAM);
UML_LOAD(block, I1, memptr, i, SIZE_WORD, SCALE_x2);
UML_ADD(block, I0, I0, I1);
sum += curdesc->opptr.w[0];
sum += ((uint16_t*)memptr)[i];
}
}
UML_CMP(block, I0, sum);
UML_EXHc(block, COND_NE, *m_nocode, seqhead->pc);
}
UML_CMP(block, I0, sum);
UML_EXHc(block, COND_NE, *m_nocode, seqhead->pc);
}
@ -554,47 +563,17 @@ void unsp_device::generate_branch(drcuml_block &block, compiler_state &compiler,
/* update the cycles and jump through the hash table to the target */
if (desc->targetpc != BRANCH_TARGET_DYNAMIC)
{
generate_update_cycles(block, compiler, desc->targetpc);
UML_CALLH(block, *m_check_interrupts);
UML_HASHJMP(block, 0, desc->targetpc, *m_nocode);
}
else
{
generate_update_cycles(block, compiler, uml::mem(&m_core->m_jmpdest));
UML_CALLH(block, *m_check_interrupts);
UML_HASHJMP(block, 0, mem(&m_core->m_jmpdest), *m_nocode);
}
}
/*------------------------------------------------------------------
generate_push - pushes a register onto a stack addressed by
the specified register
------------------------------------------------------------------*/
void unsp_device::generate_push(drcuml_block &block, uint32_t sp)
{
// value to push is in I1
UML_MOV(block, I0, mem(&m_core->m_r[sp]));
UML_CALLH(block, *m_mem_write);
UML_SUB(block, I0, I0, 1);
UML_AND(block, mem(&m_core->m_r[sp]), I0, 0x0000ffff);
}
/*------------------------------------------------------------------
generate_pop - pops a value off a stack addressed by the
specified register
------------------------------------------------------------------*/
void unsp_device::generate_pop(drcuml_block &block, uint32_t sp)
{
// popped value is in I1
UML_MOV(block, I0, mem(&m_core->m_r[sp]));
UML_ADD(block, I0, I0, 1);
UML_CALLH(block, *m_mem_read);
UML_AND(block, mem(&m_core->m_r[sp]), I0, 0x0000ffff);
}
/*-------------------------------------------------
generate_sequence_instruction - generate code
for a single instruction in a sequence
@ -721,8 +700,6 @@ bool unsp_device::generate_opcode(drcuml_block &block, compiler_state &compiler,
uml::code_label shift_no_sign = compiler.m_labelnum++;
uml::code_label no_carry = compiler.m_labelnum++;
UML_ADD(block, I7, I7, desc->cycles);
if(op0 < 0xf && opa == 0x7 && op1 < 2)
{
const uint32_t opimm = op & 0x3f;
@ -793,7 +770,7 @@ bool unsp_device::generate_opcode(drcuml_block &block, compiler_state &compiler,
break;
case 14: // JMP
UML_ADD(block, I7, I7, 2);
UML_SUB(block, mem(&m_core->m_icount), mem(&m_core->m_icount), 4);
UML_MOV(block, I0, desc->targetpc);
UML_AND(block, mem(&m_core->m_r[REG_PC]), I0, 0x0000ffff);
generate_branch(block, compiler, desc);
@ -802,13 +779,17 @@ bool unsp_device::generate_opcode(drcuml_block &block, compiler_state &compiler,
default:
return false;
}
UML_ADD(block, I7, I7, 2);
UML_SUB(block, mem(&m_core->m_icount), mem(&m_core->m_icount), 4);
generate_add_lpc(block, (op1 == 0) ? opimm : (0 - opimm));
generate_branch(block, compiler, desc);
UML_LABEL(block, skip_branch);
UML_SUB(block, mem(&m_core->m_icount), mem(&m_core->m_icount), 2);
return true;
}
else if (lower_op == 0x2d) // Push
UML_SUB(block, mem(&m_core->m_icount), mem(&m_core->m_icount), desc->cycles);
if (lower_op == 0x2d) // Push
{
uint32_t r0 = opn;
uint32_t r1 = opa;
@ -818,10 +799,10 @@ bool unsp_device::generate_opcode(drcuml_block &block, compiler_state &compiler,
UML_MOV(block, I1, mem(&m_core->m_r[r1]));
UML_CALLH(block, *m_mem_write);
UML_SUB(block, I0, I0, 1);
UML_AND(block, mem(&m_core->m_r[opb]), I0, 0x0000ffff);
r0--;
r1--;
}
UML_AND(block, mem(&m_core->m_r[opb]), I0, 0x0000ffff);
return true;
}
else if (lower_op == 0x29)
@ -870,13 +851,13 @@ bool unsp_device::generate_opcode(drcuml_block &block, compiler_state &compiler,
{
r1++;
UML_ADD(block, I0, I0, 1);
UML_AND(block, mem(&m_core->m_r[opb]), I0, 0x0000ffff);
UML_CALLH(block, *m_mem_read);
UML_MOV(block, mem(&m_core->m_r[r1]), I1);
if (r1 == REG_PC)
do_branch = true;
r0--;
}
UML_AND(block, mem(&m_core->m_r[opb]), I0, 0x0000ffff);
if (do_branch)
generate_branch(block, compiler, desc);
}

View File

@ -945,7 +945,7 @@ WRITE_LINE_MEMBER(spg2xx_device::vblank)
void spg2xx_device::check_video_irq()
{
m_cpu->set_input_line(UNSP_IRQ0_LINE, (VIDEO_IRQ_STATUS & VIDEO_IRQ_ENABLE) ? ASSERT_LINE : CLEAR_LINE);
m_cpu->set_state_unsynced(UNSP_IRQ0_LINE, (VIDEO_IRQ_STATUS & VIDEO_IRQ_ENABLE) ? ASSERT_LINE : CLEAR_LINE);
}
@ -1041,7 +1041,7 @@ READ16_MEMBER(spg2xx_device::io_r)
break;
case 0x2f: // Data Segment
val = m_cpu->state_int(UNSP_SR) >> 10;
val = m_cpu->get_ds();
LOGMASKED(LOG_SEGMENT, "io_r: Data Segment = %04x\n", val);
break;
@ -1428,12 +1428,9 @@ WRITE16_MEMBER(spg2xx_device::io_w)
}
case 0x2f: // Data Segment
{
uint16_t ds = m_cpu->state_int(UNSP_SR);
m_cpu->set_state_int(UNSP_SR, (ds & 0x03ff) | ((data & 0x3f) << 10));
m_cpu->set_ds(data & 0x3f);
LOGMASKED(LOG_SEGMENT, "io_w: Data Segment = %04x\n", data);
break;
}
case 0x30: // UART Control
{
@ -1762,19 +1759,19 @@ void spg2xx_device::extint_w(int channel, bool state)
void spg2xx_device::check_irqs(const uint16_t changed)
{
// {
// m_cpu->set_input_line(UNSP_IRQ1_LINE, ASSERT_LINE);
// m_cpu->set_state_unsynced(UNSP_IRQ1_LINE, ASSERT_LINE);
// }
if (changed & 0x0c00) // Timer A, Timer B IRQ
{
LOGMASKED(LOG_IRQS, "%ssserting IRQ2 (%04x)\n", (IO_IRQ_ENABLE & IO_IRQ_STATUS & 0x0c00) ? "A" : "Dea", (IO_IRQ_ENABLE & IO_IRQ_STATUS & 0x0c00));
m_cpu->set_input_line(UNSP_IRQ2_LINE, (IO_IRQ_ENABLE & IO_IRQ_STATUS & 0x0c00) ? ASSERT_LINE : CLEAR_LINE);
m_cpu->set_state_unsynced(UNSP_IRQ2_LINE, (IO_IRQ_ENABLE & IO_IRQ_STATUS & 0x0c00) ? ASSERT_LINE : CLEAR_LINE);
}
if (changed & 0x2100) // UART, ADC IRQ
{
LOGMASKED(LOG_UART, "%ssserting IRQ3 (%04x)\n", (IO_IRQ_ENABLE & IO_IRQ_STATUS & 0x2100) ? "A" : "Dea", (IO_IRQ_ENABLE & IO_IRQ_STATUS & 0x2100));
m_cpu->set_input_line(UNSP_IRQ3_LINE, (IO_IRQ_ENABLE & IO_IRQ_STATUS & 0x2100) ? ASSERT_LINE : CLEAR_LINE);
m_cpu->set_state_unsynced(UNSP_IRQ3_LINE, (IO_IRQ_ENABLE & IO_IRQ_STATUS & 0x2100) ? ASSERT_LINE : CLEAR_LINE);
}
if (changed & (AUDIO_BIS_MASK | AUDIO_BIE_MASK)) // Beat IRQ
@ -1782,31 +1779,31 @@ void spg2xx_device::check_irqs(const uint16_t changed)
if ((m_audio_regs[AUDIO_BEAT_COUNT] & (AUDIO_BIS_MASK | AUDIO_BIE_MASK)) == (AUDIO_BIS_MASK | AUDIO_BIE_MASK))
{
LOGMASKED(LOG_BEAT, "Asserting beat IRQ\n");
m_cpu->set_input_line(UNSP_IRQ4_LINE, ASSERT_LINE);
m_cpu->set_state_unsynced(UNSP_IRQ4_LINE, ASSERT_LINE);
}
else
{
LOGMASKED(LOG_BEAT, "Clearing beat IRQ\n");
m_cpu->set_input_line(UNSP_IRQ4_LINE, CLEAR_LINE);
m_cpu->set_state_unsynced(UNSP_IRQ4_LINE, CLEAR_LINE);
}
}
if (changed & 0x1200) // External IRQ
{
LOGMASKED(LOG_UART, "%ssserting IRQ5 (%04x)\n", (IO_IRQ_ENABLE & IO_IRQ_STATUS & 0x1200) ? "A" : "Dea", (IO_IRQ_ENABLE & IO_IRQ_STATUS & 0x1200));
m_cpu->set_input_line(UNSP_IRQ5_LINE, (IO_IRQ_ENABLE & IO_IRQ_STATUS & 0x1200) ? ASSERT_LINE : CLEAR_LINE);
m_cpu->set_state_unsynced(UNSP_IRQ5_LINE, (IO_IRQ_ENABLE & IO_IRQ_STATUS & 0x1200) ? ASSERT_LINE : CLEAR_LINE);
}
if (changed & 0x0070) // 1024Hz, 2048Hz, 4096Hz IRQ
{
LOGMASKED(LOG_IRQS, "%ssserting IRQ6 (%04x)\n", (IO_IRQ_ENABLE & IO_IRQ_STATUS & 0x0070) ? "A" : "Dea", (IO_IRQ_ENABLE & IO_IRQ_STATUS & 0x0070));
m_cpu->set_input_line(UNSP_IRQ6_LINE, (IO_IRQ_ENABLE & IO_IRQ_STATUS & 0x0070) ? ASSERT_LINE : CLEAR_LINE);
m_cpu->set_state_unsynced(UNSP_IRQ6_LINE, (IO_IRQ_ENABLE & IO_IRQ_STATUS & 0x0070) ? ASSERT_LINE : CLEAR_LINE);
}
if (changed & 0x008b) // TMB1, TMB2, 4Hz, key change IRQ
{
LOGMASKED(LOG_IRQS, "%ssserting IRQ7 (%04x)\n", (IO_IRQ_ENABLE & IO_IRQ_STATUS & 0x008b) ? "A" : "Dea", (IO_IRQ_ENABLE & IO_IRQ_STATUS & 0x008b));
m_cpu->set_input_line(UNSP_IRQ7_LINE, (IO_IRQ_ENABLE & IO_IRQ_STATUS & 0x008b) ? ASSERT_LINE : CLEAR_LINE);
m_cpu->set_state_unsynced(UNSP_IRQ7_LINE, (IO_IRQ_ENABLE & IO_IRQ_STATUS & 0x008b) ? ASSERT_LINE : CLEAR_LINE);
}
}