// license:BSD-3-Clause // copyright-holders:Ryan Holtz #include "emu.h" #include "unsp.h" #include "unspfe.h" #include "unspdefs.h" void unsp_device::invalidate_cache() { m_cache_dirty = true; } void unsp_device::execute_run_drc() { int execute_result; /* reset the cache if dirty */ if (m_cache_dirty) { code_flush_cache(); m_cache_dirty = false; } /* execute */ do { /* run as much as we can */ execute_result = m_drcuml->execute(*m_entry); /* if we need to recompile, do it */ if (execute_result == EXECUTE_MISSING_CODE) { code_compile_block(UNSP_LPC); } else if (execute_result == EXECUTE_UNMAPPED_CODE) { fatalerror("Attempted to execute unmapped code at PC=%08X\n", UNSP_LPC); } else if (execute_result == EXECUTE_RESET_CACHE) { code_flush_cache(); } } while (execute_result != EXECUTE_OUT_OF_CYCLES); } /*************************************************************************** C FUNCTION CALLBACKS ***************************************************************************/ /*------------------------------------------------- cfunc_unimplemented - handler for unimplemented opcdes -------------------------------------------------*/ inline void unsp_device::ccfunc_unimplemented() { fatalerror("PC=%08X: Unimplemented op %04x\n", UNSP_LPC, (uint16_t)m_core->m_arg0); } static void cfunc_unimplemented(void *param) { ((unsp_device *)param)->ccfunc_unimplemented(); } #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 void unsp_device::cfunc_muls() { const uint32_t op = m_core->m_arg0; const uint16_t size = ((op >> 3) & 15) ? ((op >> 3) & 15) : 16; const uint16_t rd = (op >> 9) & 7; const uint16_t rs = op & 7; execute_muls_ss(rd, rs, size); } static void ccfunc_muls(void *param) { ((unsp_device *)param)->cfunc_muls(); } /*************************************************************************** CACHE MANAGEMENT ***************************************************************************/ /*------------------------------------------------- flush_drc_cache - outward-facing accessor to code_flush_cache -------------------------------------------------*/ void unsp_device::flush_drc_cache() { if (!m_enable_drc) return; m_cache_dirty = true; } /*------------------------------------------------- code_flush_cache - flush the cache and regenerate static code -------------------------------------------------*/ void unsp_device::code_flush_cache() { /* empty the transient cache contents */ m_drcuml->reset(); try { /* generate the entry point and out-of-cycles handlers */ static_generate_entry_point(); static_generate_nocode_handler(); static_generate_out_of_cycles(); static_generate_memory_accessor(false, "read", m_mem_read); static_generate_memory_accessor(true, "write", m_mem_write); static_generate_trigger_fiq(); static_generate_trigger_irq(); static_generate_check_interrupts(); } catch (drcuml_block::abort_compilation &) { fatalerror("Unable to generate static u'nSP code\n"); fflush(stdout); } } /*------------------------------------------------- code_compile_block - compile a block of the given mode at the specified pc -------------------------------------------------*/ void unsp_device::code_compile_block(offs_t pc) { compiler_state compiler = { 0 }; const opcode_desc *seqhead, *seqlast; bool override = false; g_profiler.start(PROFILER_DRC_COMPILE); /* get a description of this sequence */ const opcode_desc *desclist = m_drcfe->describe_code(pc); bool succeeded = false; while (!succeeded) { try { /* start the block */ 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()) { const opcode_desc *curdesc; uint32_t nextpc; /* add a code log entry */ if (m_drcuml->logging()) block.append_comment("-------------------------"); /* determine the last instruction in this sequence */ for (seqlast = seqhead; seqlast != nullptr; seqlast = seqlast->next()) if (seqlast->flags & OPFLAG_END_SEQUENCE) break; assert(seqlast != nullptr); /* if we don't have a hash for this mode/pc, or if we are overriding all, add one */ if (override || !m_drcuml->hash_exists(0, seqhead->pc)) UML_HASH(block, 0, seqhead->pc); /* if we already have a hash, and this is the first sequence, assume that we */ /* are recompiling due to being out of sync and allow future overrides */ else if (seqhead == desclist) { override = true; UML_HASH(block, 0, seqhead->pc); } /* otherwise, redispatch to that fixed PC and skip the rest of the processing */ else { UML_LABEL(block, seqhead->pc | 0x80000000); UML_HASHJMP(block, 0, seqhead->pc, *m_nocode); continue; } /* validate this code block if we're not pointing into ROM */ if (m_program.space().get_write_ptr(seqhead->physpc) != nullptr) generate_checksum_block(block, compiler, seqhead, seqlast); /* label this instruction, if it may be jumped to locally */ if (seqhead->flags & OPFLAG_IS_BRANCH_TARGET) UML_LABEL(block, seqhead->pc | 0x80000000); /* 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); } 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) { UML_HASHJMP(block, 0, nextpc, *m_nocode); // hashjmp ,nextpc,nocode } } /* end the sequence */ block.end(); g_profiler.stop(); succeeded = true; } catch (drcuml_block::abort_compilation &) { code_flush_cache(); } } } /*************************************************************************** STATIC CODEGEN ***************************************************************************/ /*------------------------------------------------- alloc_handle - allocate a handle if not already allocated -------------------------------------------------*/ static inline void alloc_handle(drcuml_state &drcuml, uml::code_handle *&handleptr, const char *name) { if (!handleptr) handleptr = drcuml.handle_alloc(name); } /*------------------------------------------------- generate_entry_point - generate a static entry point -------------------------------------------------*/ void unsp_device::static_generate_entry_point() { /* begin generating */ drcuml_block &block(m_drcuml->begin_block(32)); /* forward references */ alloc_handle(*m_drcuml, m_nocode, "nocode"); alloc_handle(*m_drcuml, m_entry, "entry"); UML_HANDLE(block, *m_entry); /* load fast integer registers */ //load_fast_iregs(block); /* generate a hash jump via the current mode and PC */ UML_ROLAND(block, I1, mem(&m_core->m_r[REG_SR]), 16, 0x003f0000); UML_OR(block, I1, I1, mem(&m_core->m_r[REG_PC])); UML_HASHJMP(block, 0, I1, *m_nocode); block.end(); } /*------------------------------------------------- static_generate_nocode_handler - generate an exception handler for "out of code" -------------------------------------------------*/ void unsp_device::static_generate_nocode_handler() { /* begin generating */ drcuml_block &block(m_drcuml->begin_block(10)); /* generate a hash jump via the current mode and PC */ alloc_handle(*m_drcuml, m_nocode, "nocode"); UML_HANDLE(block, *m_nocode); UML_GETEXP(block, I0); UML_ROLINS(block, mem(&m_core->m_r[REG_SR]), I0, 16, 0x3f); UML_AND(block, mem(&m_core->m_r[REG_PC]), I0, 0x0000ffff); //save_fast_iregs(block); UML_EXIT(block, EXECUTE_MISSING_CODE); block.end(); } /*------------------------------------------------- static_generate_out_of_cycles - generate an out of cycles exception handler -------------------------------------------------*/ void unsp_device::static_generate_out_of_cycles() { /* begin generating */ drcuml_block &block(m_drcuml->begin_block(10)); /* generate a hash jump via the current mode and PC */ alloc_handle(*m_drcuml, m_out_of_cycles, "out_of_cycles"); UML_HANDLE(block, *m_out_of_cycles); //save_fast_iregs(block); UML_EXIT(block, EXECUTE_OUT_OF_CYCLES); block.end(); } /*------------------------------------------------------------------ static_generate_memory_accessor ------------------------------------------------------------------*/ void unsp_device::static_generate_memory_accessor(bool iswrite, const char *name, uml::code_handle *&handleptr) { /* on entry, address is in I0; data for writes is in I1 */ /* on exit, read result is in I1 */ /* begin generating */ drcuml_block &block(m_drcuml->begin_block(32)); /* add a global entry for this */ alloc_handle(*m_drcuml, handleptr, 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); block.end(); } /*************************************************************************** CODE GENERATION ***************************************************************************/ void unsp_device::static_generate_check_interrupts() { uml::code_label test_loop = 1; uml::code_label found = 2; uml::code_label do_irq = 3; /* begin generating */ drcuml_block &block(m_drcuml->begin_block(256)); /* generate a hash jump via the current mode and PC */ alloc_handle(*m_drcuml, m_check_interrupts, "check_interrupts"); UML_HANDLE(block, *m_check_interrupts); UML_CMP(block, mem(&m_core->m_sirq), 0); UML_RETc(block, uml::COND_Z); UML_MOV(block, I2, 0); UML_MOV(block, I0, 1); UML_MOV(block, I1, mem(&m_core->m_sirq)); UML_LABEL(block, test_loop); UML_TEST(block, I1, I0); UML_JMPc(block, uml::COND_NZ, found); UML_SHL(block, I0, I0, 1); UML_ADD(block, I2, I2, 1); UML_CMP(block, I0, 1 << 9); UML_JMPc(block, uml::COND_NE, test_loop); UML_RET(block); UML_LABEL(block, found); UML_CMP(block, I0, 1); UML_JMPc(block, uml::COND_NE, do_irq); UML_CALLH(block, *m_trigger_fiq); UML_RET(block); UML_LABEL(block, do_irq); UML_CALLH(block, *m_trigger_irq); UML_RET(block); block.end(); } void unsp_device::static_generate_trigger_fiq() { /* begin generating */ drcuml_block &block(m_drcuml->begin_block(256)); /* generate a hash jump via the current mode and PC */ alloc_handle(*m_drcuml, m_trigger_fiq, "trigger_fiq"); UML_HANDLE(block, *m_trigger_fiq); UML_TEST(block, mem(&m_core->m_enable_fiq), 1); UML_RETc(block, uml::COND_Z); UML_TEST(block, mem(&m_core->m_fiq), 1); UML_RETc(block, uml::COND_NZ); UML_MOV(block, mem(&m_core->m_fiq), 1); UML_MOV(block, I0, mem(&m_core->m_r[REG_SP])); UML_MOV(block, I1, mem(&m_core->m_r[REG_PC])); UML_CALLH(block, *m_mem_write); UML_SUB(block, I0, I0, 1); UML_MOV(block, I1, mem(&m_core->m_r[REG_SR])); UML_CALLH(block, *m_mem_write); UML_SUB(block, I0, I0, 1); UML_AND(block, mem(&m_core->m_r[REG_SP]), I0, 0x0000ffff); UML_MOV(block, I0, 0x0000fff6); UML_CALLH(block, *m_mem_read); UML_MOV(block, mem(&m_core->m_r[REG_PC]), I1); UML_MOV(block, mem(&m_core->m_r[REG_SR]), 0); UML_HASHJMP(block, 0, I1, *m_nocode); UML_RET(block); block.end(); } void unsp_device::static_generate_trigger_irq() { /* begin generating */ drcuml_block &block(m_drcuml->begin_block(256)); uml::code_label skip_ine = 1; /* generate a hash jump via the current mode and PC */ alloc_handle(*m_drcuml, m_trigger_irq, "trigger_irq"); UML_HANDLE(block, *m_trigger_irq); // If INE is 0 and IRQ is 1, abort UML_XOR(block, I1, mem(&m_core->m_irq), 1); UML_AND(block, I1, I1, mem(&m_core->m_ine)); UML_RETc(block, uml::COND_NZ); // If INE is 0 and IRQ is 0, we have a valid IRQ UML_TEST(block, mem(&m_core->m_ine), 1); UML_JMPc(block, uml::COND_Z, skip_ine); // If INE is 1 and IRQ line is < PRI, abort UML_CMP(block, I0, mem(&m_core->m_pri)); UML_RETc(block, uml::COND_LE); // Update our current interrupt priority UML_MOV(block, mem(&m_core->m_pri), I0); UML_LABEL(block, skip_ine); UML_MOV(block, mem(&m_core->m_irq), 1); UML_TEST(block, mem(&m_core->m_enable_irq), 1); UML_RETc(block, uml::COND_Z); UML_MOV(block, I0, mem(&m_core->m_r[REG_SP])); UML_MOV(block, I1, mem(&m_core->m_r[REG_PC])); UML_CALLH(block, *m_mem_write); UML_SUB(block, I0, I0, 1); UML_MOV(block, I1, mem(&m_core->m_r[REG_SR])); UML_CALLH(block, *m_mem_write); UML_SUB(block, I0, I0, 1); UML_AND(block, mem(&m_core->m_r[REG_SP]), I0, 0x0000ffff); UML_ADD(block, I0, I2, 0x0000fff7); UML_CALLH(block, *m_mem_read); UML_MOV(block, mem(&m_core->m_r[REG_PC]), I1); UML_MOV(block, mem(&m_core->m_r[REG_SR]), 0); UML_HASHJMP(block, 0, I1, *m_nocode); UML_RET(block); block.end(); } /*------------------------------------------------- generate_check_cycles - generate code to generate an exception if cycles are out -------------------------------------------------*/ void unsp_device::generate_check_cycles(drcuml_block &block, compiler_state &compiler, uml::parameter param) { UML_CMP(block, mem(&m_core->m_icount), 0); UML_EXHc(block, uml::COND_L, *m_out_of_cycles, param); } /*------------------------------------------------- generate_checksum_block - generate code to validate a sequence of opcodes -------------------------------------------------*/ void unsp_device::generate_checksum_block(drcuml_block &block, compiler_state &compiler, const opcode_desc *seqhead, const opcode_desc *seqlast) { const opcode_desc *curdesc; if (m_drcuml->logging()) { block.append_comment("[Validation for %08X]", seqhead->pc); } /* full verification; sum up everything */ void *memptr = m_program.space().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++) { UML_LOAD(block, I1, memptr, i, SIZE_WORD, SCALE_x2); UML_ADD(block, I0, I0, I1); sum += ((uint16_t*)memptr)[i]; } for (curdesc = seqhead->next(); curdesc != seqlast->next(); curdesc = curdesc->next()) { if (!(curdesc->flags & OPFLAG_VIRTUAL_NOOP)) { memptr = m_program.space().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_LOAD(block, I1, memptr, i, SIZE_WORD, SCALE_x2); UML_ADD(block, I0, I0, I1); sum += ((uint16_t*)memptr)[i]; } } } UML_CMP(block, I0, sum); UML_EXHc(block, COND_NE, *m_nocode, seqhead->pc); } /*------------------------------------------------- log_add_disasm_comment - add a comment including disassembly of a MIPS instruction -------------------------------------------------*/ void unsp_device::log_add_disasm_comment(drcuml_block &block, uint32_t pc, uint32_t op) { if (m_drcuml->logging()) { block.append_comment("%08X: %08x", pc, op); } } /*------------------------------------------------------------------ generate_branch ------------------------------------------------------------------*/ void unsp_device::generate_branch(drcuml_block &block, compiler_state &compiler, const opcode_desc *desc) { if (desc->targetpc == BRANCH_TARGET_DYNAMIC) { UML_ROLAND(block, I0, mem(&m_core->m_r[REG_SR]), 16, 0x3f0000); UML_OR(block, mem(&m_core->m_jmpdest), I0, mem(&m_core->m_r[REG_PC])); } /* update the cycles and jump through the hash table to the target */ if (desc->targetpc != BRANCH_TARGET_DYNAMIC) { UML_CALLH(block, *m_check_interrupts); UML_HASHJMP(block, 0, desc->targetpc, *m_nocode); } else { UML_CALLH(block, *m_check_interrupts); UML_HASHJMP(block, 0, mem(&m_core->m_jmpdest), *m_nocode); } } /*------------------------------------------------- generate_sequence_instruction - generate code for a single instruction in a sequence -------------------------------------------------*/ void unsp_device::generate_sequence_instruction(drcuml_block &block, compiler_state &compiler, const opcode_desc *desc) { /* add an entry for the log */ if (m_drcuml->logging() && !(desc->flags & OPFLAG_VIRTUAL_NOOP)) log_add_disasm_comment(block, desc->pc, desc->opptr.w[0]); /* set the PC map variable */ UML_MAPVAR(block, MAPVAR_PC, desc->pc); /* if we are debugging, call the debugger */ if ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0) { //save_fast_iregs(block); UML_DEBUG(block, desc->pc); } #if UNSP_LOG_REGS UML_CALLC(block, cfunc_log_regs, this); #endif if (!(desc->flags & OPFLAG_VIRTUAL_NOOP)) { /* compile the instruction */ if (!generate_opcode(block, compiler, desc)) { UML_ROLINS(block, mem(&m_core->m_r[REG_SR]), desc->pc, 16, 0x3f); UML_AND(block, mem(&m_core->m_r[REG_PC]), desc->pc, 0x0000ffff); UML_MOV(block, mem(&m_core->m_arg0), desc->opptr.w[0]); UML_CALLC(block, cfunc_unimplemented, this); } } } /*------------------------------------------------------------------ generate_add_lpc - adds an offset to the long program counter comprised of PC and the low 6 bits of SR ------------------------------------------------------------------*/ void unsp_device::generate_add_lpc(drcuml_block &block, int32_t offset) { UML_ROLAND(block, I0, mem(&m_core->m_r[REG_SR]), 16, 0x3f0000); UML_OR(block, I0, I0, mem(&m_core->m_r[REG_PC])); UML_ADD(block, I0, I0, offset); UML_ROLINS(block, mem(&m_core->m_r[REG_SR]), I0, 16, 0x3f); UML_AND(block, mem(&m_core->m_r[REG_PC]), I0, 0x0000ffff); } /*------------------------------------------------------------------ generate_update_nzsc - perform a full flag update ------------------------------------------------------------------*/ void unsp_device::generate_update_nzsc(drcuml_block &block) { UML_XOR(block, I1, I1, I2); UML_TEST(block, I1, 0x8000); UML_SETc(block, uml::COND_NZ, I2); UML_TEST(block, I3, 0x8000); UML_SETc(block, uml::COND_NZ, I1); UML_ROLINS(block, mem(&m_core->m_r[REG_SR]), I1, UNSP_N_SHIFT, UNSP_N); UML_TEST(block, I3, 0x10000); UML_SETc(block, uml::COND_NZ, I1); UML_ROLINS(block, mem(&m_core->m_r[REG_SR]), I1, UNSP_C_SHIFT, UNSP_C); UML_CMP(block, I2, I1); UML_SETc(block, uml::COND_NE, I1); UML_ROLINS(block, mem(&m_core->m_r[REG_SR]), I1, UNSP_S_SHIFT, UNSP_S); UML_TEST(block, I3, 0x0000ffff); UML_SETc(block, uml::COND_Z, I1); UML_ROLINS(block, mem(&m_core->m_r[REG_SR]), I1, UNSP_Z_SHIFT, UNSP_Z); } /*------------------------------------------------------------------ generate_update_nz - perform a partial flag update ------------------------------------------------------------------*/ void unsp_device::generate_update_nz(drcuml_block &block) { UML_TEST(block, I3, 0x8000); UML_SETc(block, uml::COND_NZ, I1); UML_ROLINS(block, mem(&m_core->m_r[REG_SR]), I1, UNSP_N_SHIFT, UNSP_N); UML_AND(block, I2, I3, 0x0000ffff); UML_CMP(block, I2, 0); UML_SETc(block, uml::COND_E, I1); UML_ROLINS(block, mem(&m_core->m_r[REG_SR]), I1, UNSP_Z_SHIFT, UNSP_Z); } /*------------------------------------------------------------------ generate_opcode - main handler which generates the UML for a single opcode ------------------------------------------------------------------*/ bool unsp_device::generate_opcode(drcuml_block &block, compiler_state &compiler, const opcode_desc *desc) { uint32_t op = (uint32_t)desc->opptr.w[0]; generate_add_lpc(block, 1); const uint16_t op0 = (op >> 12) & 15; const uint16_t opa = (op >> 9) & 7; const uint16_t op1 = (op >> 6) & 7; const uint16_t opn = (op >> 3) & 7; const uint16_t opb = op & 7; const uint8_t lower_op = (op1 << 4) | op0; uml::code_label skip_branch = compiler.m_labelnum++; uml::code_label reti_done = compiler.m_labelnum++; uml::code_label mul_opa_nohi = compiler.m_labelnum++; uml::code_label mul_opb_nohi = compiler.m_labelnum++; uml::code_label shift_no_sign = compiler.m_labelnum++; uml::code_label no_carry = compiler.m_labelnum++; if(op0 < 0xf && opa == 0x7 && op1 < 2) { const uint32_t opimm = op & 0x3f; switch(op0) { case 0: // JB UML_TEST(block, mem(&m_core->m_r[REG_SR]), UNSP_C); UML_JMPc(block, uml::COND_NZ, skip_branch); break; case 1: // JAE UML_TEST(block, mem(&m_core->m_r[REG_SR]), UNSP_C); UML_JMPc(block, uml::COND_Z, skip_branch); break; case 2: // JGE UML_TEST(block, mem(&m_core->m_r[REG_SR]), UNSP_S); UML_JMPc(block, uml::COND_NZ, skip_branch); break; case 3: // JL UML_TEST(block, mem(&m_core->m_r[REG_SR]), UNSP_S); UML_JMPc(block, uml::COND_Z, skip_branch); break; case 4: // JNE UML_TEST(block, mem(&m_core->m_r[REG_SR]), UNSP_Z); UML_JMPc(block, uml::COND_NZ, skip_branch); break; case 5: // JE UML_TEST(block, mem(&m_core->m_r[REG_SR]), UNSP_Z); UML_JMPc(block, uml::COND_Z, skip_branch); break; case 6: // JPL UML_TEST(block, mem(&m_core->m_r[REG_SR]), UNSP_N); UML_JMPc(block, uml::COND_NZ, skip_branch); break; case 7: // JMI UML_TEST(block, mem(&m_core->m_r[REG_SR]), UNSP_N); UML_JMPc(block, uml::COND_Z, skip_branch); break; case 8: // JBE UML_AND(block, I0, mem(&m_core->m_r[REG_SR]), UNSP_Z | UNSP_C); UML_CMP(block, I0, UNSP_C); UML_JMPc(block, uml::COND_E, skip_branch); break; case 9: // JA UML_AND(block, I0, mem(&m_core->m_r[REG_SR]), UNSP_Z | UNSP_C); UML_CMP(block, I0, UNSP_C); UML_JMPc(block, uml::COND_NE, skip_branch); break; case 10: // JLE UML_TEST(block, mem(&m_core->m_r[REG_SR]), UNSP_Z | UNSP_S); UML_JMPc(block, uml::COND_Z, skip_branch); break; case 11: // JG UML_TEST(block, mem(&m_core->m_r[REG_SR]), UNSP_Z); UML_JMPc(block, uml::COND_NZ, skip_branch); UML_TEST(block, mem(&m_core->m_r[REG_SR]), UNSP_S); UML_JMPc(block, uml::COND_NZ, skip_branch); break; case 12: // JVC UML_ROLAND(block, I0, mem(&m_core->m_r[REG_SR]), 32-UNSP_S_SHIFT, 1); UML_ROLAND(block, I1, mem(&m_core->m_r[REG_SR]), 32-UNSP_N_SHIFT, 1); UML_CMP(block, I0, I1); UML_JMPc(block, uml::COND_NE, skip_branch); break; case 13: // JVS UML_ROLAND(block, I0, mem(&m_core->m_r[REG_SR]), 32-UNSP_S_SHIFT, 1); UML_ROLAND(block, I1, mem(&m_core->m_r[REG_SR]), 32-UNSP_N_SHIFT, 1); UML_CMP(block, I0, I1); UML_JMPc(block, uml::COND_E, skip_branch); break; case 14: // JMP 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); return true; default: return false; } 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; } 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; UML_MOV(block, I0, mem(&m_core->m_r[opb])); while (r0) { 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--; } return true; } else if (lower_op == 0x29) { if (op == 0x9a98) // reti { UML_ADD(block, I0, mem(&m_core->m_r[REG_SP]), 1); UML_CALLH(block, *m_mem_read); UML_MOV(block, mem(&m_core->m_r[REG_SR]), I1); UML_ADD(block, I0, I0, 1); UML_CALLH(block, *m_mem_read); UML_MOV(block, mem(&m_core->m_r[REG_PC]), I1); UML_AND(block, mem(&m_core->m_r[REG_SP]), I0, 0x0000ffff); UML_TEST(block, mem(&m_core->m_fiq), 1); UML_MOVc(block, uml::COND_NZ, mem(&m_core->m_fiq), 0); UML_JMPc(block, uml::COND_NZ, reti_done); UML_TEST(block, mem(&m_core->m_irq), 1); UML_MOVc(block, uml::COND_NZ, mem(&m_core->m_irq), 0); UML_LABEL(block, reti_done); generate_branch(block, compiler, desc); } else // pop { uint32_t r0 = opn; uint32_t r1 = opa; bool do_branch = false; UML_MOV(block, I0, mem(&m_core->m_r[opb])); while (r0) { 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--; } if (do_branch) generate_branch(block, compiler, desc); } return true; } else if (op0 == 0xf) { switch (op1) { case 0x00: // Multiply, Unsigned * Signed if(opn == 1 && opa != 7) { UML_MOV(block, I0, mem(&m_core->m_r[opa])); UML_MOV(block, I1, mem(&m_core->m_r[opb])); UML_MULU(block, I2, I2, I0, I1); UML_TEST(block, I1, 0x00008000); UML_JMPc(block, uml::COND_Z, mul_opb_nohi); UML_SHL(block, I0, I0, 16); UML_SUB(block, I2, I2, I0); UML_LABEL(block, mul_opb_nohi); UML_SHR(block, mem(&m_core->m_r[REG_R4]), I2, 16); UML_AND(block, mem(&m_core->m_r[REG_R3]), I2, 0x0000ffff); return true; } return false; case 0x01: // Call if(!(opa & 1)) { generate_add_lpc(block, 1); UML_MOV(block, I0, mem(&m_core->m_r[REG_SP])); UML_MOV(block, I1, mem(&m_core->m_r[REG_PC])); UML_CALLH(block, *m_mem_write); UML_SUB(block, I0, I0, 1); UML_MOV(block, I1, mem(&m_core->m_r[REG_SR])); UML_CALLH(block, *m_mem_write); UML_SUB(block, I0, I0, 1); UML_AND(block, mem(&m_core->m_r[REG_SP]), I0, 0x0000ffff); UML_MOV(block, I0, desc->targetpc); UML_AND(block, mem(&m_core->m_r[REG_PC]), I0, 0x0000ffff); UML_ROLINS(block, mem(&m_core->m_r[REG_SR]), I0, 16, 0x3f); generate_branch(block, compiler, desc); return true; } return false; case 0x02: // Far Jump if (opa == 7) { UML_MOV(block, I0, desc->targetpc); UML_AND(block, mem(&m_core->m_r[REG_PC]), I0, 0x0000ffff); UML_ROLINS(block, mem(&m_core->m_r[REG_SR]), I0, 16, 0x3f); generate_branch(block, compiler, desc); return true; } return false; case 0x04: // Multiply, Signed * Signed if(opn == 1 && opa != 7) { UML_MOV(block, I0, mem(&m_core->m_r[opa])); UML_MOV(block, I1, mem(&m_core->m_r[opb])); UML_MULU(block, I2, I2, I0, I1); UML_TEST(block, I1, 0x00008000); UML_JMPc(block, uml::COND_Z, mul_opb_nohi); UML_SHL(block, I3, I0, 16); UML_SUB(block, I2, I2, I3); UML_LABEL(block, mul_opb_nohi); UML_TEST(block, I0, 0x00008000); UML_JMPc(block, uml::COND_Z, mul_opa_nohi); UML_SHL(block, I3, I1, 16); UML_SUB(block, I2, I2, I3); UML_LABEL(block, mul_opa_nohi); UML_SHR(block, mem(&m_core->m_r[REG_R4]), I2, 16); UML_AND(block, mem(&m_core->m_r[REG_R3]), I2, 0x0000ffff); return true; } return false; case 0x05: // Interrupt flags switch(op & 0x3f) { case 0: UML_MOV(block, mem(&m_core->m_enable_irq), 0); UML_MOV(block, mem(&m_core->m_enable_fiq), 0); break; case 1: UML_MOV(block, mem(&m_core->m_enable_irq), 1); UML_MOV(block, mem(&m_core->m_enable_fiq), 0); break; case 2: UML_MOV(block, mem(&m_core->m_enable_irq), 0); UML_MOV(block, mem(&m_core->m_enable_fiq), 1); break; case 3: UML_MOV(block, mem(&m_core->m_enable_irq), 1); UML_MOV(block, mem(&m_core->m_enable_fiq), 1); break; case 4: UML_MOV(block, mem(&m_core->m_fir_move), 1); break; case 5: UML_MOV(block, mem(&m_core->m_fir_move), 0); break; case 8: UML_MOV(block, mem(&m_core->m_enable_irq), 0); break; case 9: UML_MOV(block, mem(&m_core->m_enable_irq), 1); break; case 12: UML_MOV(block, mem(&m_core->m_enable_fiq), 0); break; case 14: UML_MOV(block, mem(&m_core->m_enable_fiq), 1); break; case 37: // nop break; default: logerror("unsp drc interrupt flags %02x\n", op & 0x3f); return false; } return true; case 0x06: case 0x07: if (opa == 7) return false; // MULS UML_MOV(block, mem(&m_core->m_arg0), desc->opptr.w[0]); UML_CALLC(block, ccfunc_muls, this); return true; default: return false; } } // At this point, we should be dealing solely with ALU ops. UML_MOV(block, I2, mem(&m_core->m_r[opa])); switch (op1) { case 0x00: // r, [bp+imm6] UML_ADD(block, I0, mem(&m_core->m_r[REG_BP]), op & 0x3f); UML_AND(block, I0, I0, 0x0000ffff); if (op0 != 0x0d) UML_CALLH(block, *m_mem_read); break; case 0x01: // r, imm6 UML_MOV(block, I1, op & 0x3f); break; case 0x03: // Indirect { const uint8_t lsbits = opn & 3; if (opn & 4) { switch (lsbits) { case 0: // r, [r] UML_ROLAND(block, I0, mem(&m_core->m_r[REG_SR]), 6, 0x3f0000); UML_OR(block, I0, I0, mem(&m_core->m_r[opb])); if (op0 != 0x0d) UML_CALLH(block, *m_mem_read); break; case 1: // r, [r--] UML_ROLAND(block, I0, mem(&m_core->m_r[REG_SR]), 6, 0x3f0000); UML_OR(block, I0, I0, mem(&m_core->m_r[opb])); if (op0 != 0x0d) UML_CALLH(block, *m_mem_read); UML_SUB(block, I3, I0, 1); UML_ROLINS(block, mem(&m_core->m_r[REG_SR]), I3, 32-6, 0x0000fc00); UML_AND(block, mem(&m_core->m_r[opb]), I3, 0x0000ffff); break; case 2: // r, [r++] UML_ROLAND(block, I0, mem(&m_core->m_r[REG_SR]), 6, 0x3f0000); UML_OR(block, I0, I0, mem(&m_core->m_r[opb])); if (op0 != 0x0d) UML_CALLH(block, *m_mem_read); UML_ADD(block, I3, I0, 1); UML_ROLINS(block, mem(&m_core->m_r[REG_SR]), I3, 32-6, 0x0000fc00); UML_AND(block, mem(&m_core->m_r[opb]), I3, 0x0000ffff); break; case 3: // r, [++r] UML_ROLAND(block, I0, mem(&m_core->m_r[REG_SR]), 6, 0x3f0000); UML_OR(block, I0, I0, mem(&m_core->m_r[opb])); UML_ADD(block, I0, I0, 1); UML_ROLINS(block, mem(&m_core->m_r[REG_SR]), I0, 32-6, 0x0000fc00); UML_AND(block, mem(&m_core->m_r[opb]), I0, 0x0000ffff); if (op0 != 0x0d) UML_CALLH(block, *m_mem_read); break; default: // can't happen break; } } else { switch (lsbits) { case 0: // r, [r] UML_MOV(block, I0, mem(&m_core->m_r[opb])); if (op0 != 0x0d) UML_CALLH(block, *m_mem_read); break; case 1: // r, [r--] UML_MOV(block, I0, mem(&m_core->m_r[opb])); if (op0 != 0x0d) UML_CALLH(block, *m_mem_read); UML_SUB(block, I3, I0, 1); UML_AND(block, mem(&m_core->m_r[opb]), I3, 0x0000ffff); break; case 2: // r, [r++] UML_MOV(block, I0, mem(&m_core->m_r[opb])); if (op0 != 0x0d) UML_CALLH(block, *m_mem_read); UML_ADD(block, I3, I0, 1); UML_AND(block, mem(&m_core->m_r[opb]), I3, 0x0000ffff); break; case 3: // r, [++r] UML_ADD(block, I0, mem(&m_core->m_r[opb]), 1); UML_AND(block, I0, I0, 0x0000ffff); UML_MOV(block, mem(&m_core->m_r[opb]), I0); if (op0 != 0x0d) UML_CALLH(block, *m_mem_read); break; default: // can't happen break; } } break; } case 0x04: // 16-bit ops switch (opn) { case 0x00: // r UML_MOV(block, I1, mem(&m_core->m_r[opb])); break; case 0x01: // imm16 { UML_MOV(block, I2, mem(&m_core->m_r[opb])); const uint16_t r1 = m_cache.read_word(desc->pc + 1); generate_add_lpc(block, 1); UML_MOV(block, I1, r1); break; } case 0x02: // [imm16] { UML_MOV(block, I2, mem(&m_core->m_r[opb])); const uint16_t r1 = m_cache.read_word(desc->pc + 1); generate_add_lpc(block, 1); UML_MOV(block, I0, r1); if (op0 != 0x0d) UML_CALLH(block, *m_mem_read); break; } case 0x03: // store [imm16], r { UML_MOV(block, I1, I2); UML_MOV(block, I2, mem(&m_core->m_r[opb])); const uint16_t r2 = m_cache.read_word(desc->pc + 1); generate_add_lpc(block, 1); UML_MOV(block, I0, r2); break; } default: // Shifted ops { UML_SHL(block, I0, mem(&m_core->m_r[opb]), 4); UML_OR(block, I0, I0, mem(&m_core->m_sb)); UML_TEST(block, I0, 0x80000); UML_JMPc(block, uml::COND_Z, shift_no_sign); UML_OR(block, I0, I0, 0xf00000); UML_LABEL(block, shift_no_sign); UML_SHR(block, I0, I0, opn - 3); UML_AND(block, mem(&m_core->m_sb), I0, 0xf); UML_SHR(block, I1, I0, 4); UML_AND(block, I1, I1, 0x0000ffff); break; } } break; case 0x05: // More shifted ops if (opn & 4) // Shift right { UML_SHL(block, I0, mem(&m_core->m_r[opb]), 4); UML_OR(block, I0, I0, mem(&m_core->m_sb)); UML_SHR(block, I0, I0, opn - 3); UML_AND(block, mem(&m_core->m_sb), I0, 0xf); UML_SHR(block, I1, I0, 4); UML_AND(block, I1, I1, 0x0000ffff); } else // Shift left { UML_SHL(block, I0, mem(&m_core->m_sb), 16); UML_OR(block, I0, I0, mem(&m_core->m_r[opb])); UML_SHL(block, I0, I0, opn + 1); UML_SHR(block, I1, I0, 16); UML_AND(block, mem(&m_core->m_sb), I1, 0xf); UML_AND(block, I1, I0, 0x0000ffff); } break; case 0x06: // Rotated ops { UML_SHL(block, I0, mem(&m_core->m_sb), 16); UML_OR(block, I0, I0, mem(&m_core->m_r[opb])); UML_SHL(block, I0, I0, 4); UML_OR(block, I0, I0, mem(&m_core->m_sb)); if (opn & 4) // Rotate right { UML_SHR(block, I0, I0, opn - 3); UML_AND(block, mem(&m_core->m_sb), I0, 0xf); } else // Rotate left { UML_SHL(block, I0, I0, opn + 1); UML_SHR(block, I1, I0, 20); UML_AND(block, mem(&m_core->m_sb), I1, 0xf); } UML_SHR(block, I1, I0, 4); UML_AND(block, I1, I1, 0x0000ffff); break; } case 0x07: // Direct 8 UML_MOV(block, I0, op & 0x3f); UML_CALLH(block, *m_mem_read); break; default: break; } switch (op0) { case 0x00: // Add UML_ADD(block, I3, I2, I1); if (opa != 7) generate_update_nzsc(block); break; case 0x01: // Add w/ carry UML_ROLAND(block, I3, mem(&m_core->m_r[REG_SR]), 32-UNSP_C_SHIFT, 1); UML_ADD(block, I3, I3, I2); UML_ADD(block, I3, I3, I1); if (opa != 7) generate_update_nzsc(block); break; case 0x02: // Subtract UML_XOR(block, I1, I1, 0x0000ffff); UML_ADD(block, I3, I1, I2); UML_ADD(block, I3, I3, 1); if (opa != 7) generate_update_nzsc(block); break; case 0x03: // Subtract w/ carry UML_XOR(block, I1, I1, 0x0000ffff); UML_ADD(block, I3, I1, I2); UML_TEST(block, mem(&m_core->m_r[REG_SR]), UNSP_C); UML_JMPc(block, uml::COND_Z, no_carry); UML_ADD(block, I3, I3, 1); UML_LABEL(block, no_carry); if (opa != 7) generate_update_nzsc(block); break; case 0x04: // Compare UML_XOR(block, I1, I1, 0x0000ffff); UML_ADD(block, I3, I1, I2); UML_ADD(block, I3, I3, 1); if (opa != 7) generate_update_nzsc(block); return true; case 0x06: // Negate UML_SUB(block, I3, 0, I1); if (opa != 7) generate_update_nz(block); break; case 0x08: // XOR UML_XOR(block, I3, I2, I1); if (opa != 7) generate_update_nz(block); break; case 0x09: // Load UML_MOV(block, I3, I1); if (opa != 7) generate_update_nz(block); break; case 0x0a: // OR UML_OR(block, I3, I2, I1); if (opa != 7) generate_update_nz(block); break; case 0x0b: // AND UML_AND(block, I3, I2, I1); if (opa != 7) generate_update_nz(block); break; case 0x0c: // Test UML_AND(block, I3, I2, I1); if (opa != 7) generate_update_nz(block); return true; case 0x0d: // Store UML_MOV(block, I1, I2); UML_CALLH(block, *m_mem_write); return true; default: return false; } if (op1 == 0x04 && opn == 0x03) { UML_MOV(block, I1, I3); UML_CALLH(block, *m_mem_write); return true; } UML_AND(block, mem(&m_core->m_r[opa]), I3, 0x0000ffff); if (opa == REG_PC) generate_branch(block, compiler, desc); return true; }