diff options
Diffstat (limited to 'src/devices/cpu/mips/mips1.cpp')
-rw-r--r-- | src/devices/cpu/mips/mips1.cpp | 2118 |
1 files changed, 1505 insertions, 613 deletions
diff --git a/src/devices/cpu/mips/mips1.cpp b/src/devices/cpu/mips/mips1.cpp index 72479a6065d..1ed202c0d63 100644 --- a/src/devices/cpu/mips/mips1.cpp +++ b/src/devices/cpu/mips/mips1.cpp @@ -7,8 +7,6 @@ * while those without have hard-wired address translation. * * TODO - * - FPU support - * - further cleanup on coprocessors * - R3041 features * - cache emulation * @@ -17,37 +15,32 @@ #include "mips1.h" #include "mips1dsm.h" #include "debugger.h" +#include "softfloat3/source/include/softfloat.h" #define LOG_GENERAL (1U << 0) #define LOG_TLB (1U << 1) +#define LOG_IOP (1U << 2) +#define LOG_RISCOS (1U << 3) //#define VERBOSE (LOG_GENERAL|LOG_TLB) -#include "logmacro.h" -#define ENABLE_IOP_KPUTS (0) +#include "logmacro.h" #define RSREG ((op >> 21) & 31) #define RTREG ((op >> 16) & 31) #define RDREG ((op >> 11) & 31) #define SHIFT ((op >> 6) & 31) -#define RSVAL m_r[RSREG] -#define RTVAL m_r[RTREG] -#define RDVAL m_r[RDREG] +#define FTREG ((op >> 16) & 31) +#define FSREG ((op >> 11) & 31) +#define FDREG ((op >> 6) & 31) #define SIMMVAL s16(op) #define UIMMVAL u16(op) #define LIMMVAL (op & 0x03ffffff) -#define ADDPC(x) do { m_branch_state = BRANCH; m_branch_target = m_pc + 4 + ((x) << 2); } while (0) -#define ADDPCL(x,l) do { m_branch_state = BRANCH; m_branch_target = m_pc + 4 + ((x) << 2); m_r[l] = m_pc + 8; } while (0) -#define ABSPC(x) do { m_branch_state = BRANCH; m_branch_target = ((m_pc + 4) & 0xf0000000) | ((x) << 2); } while (0) -#define ABSPCL(x,l) do { m_branch_state = BRANCH; m_branch_target = ((m_pc + 4) & 0xf0000000) | ((x) << 2); m_r[l] = m_pc + 8; } while (0) -#define SETPC(x) do { m_branch_state = BRANCH; m_branch_target = (x); } while (0) -#define SETPCL(x,l) do { m_branch_state = BRANCH; m_branch_target = (x); m_r[l] = m_pc + 8; } while (0) - -#define SR m_cpr[0][COP0_Status] -#define CAUSE m_cpr[0][COP0_Cause] +#define SR m_cop0[COP0_Status] +#define CAUSE m_cop0[COP0_Cause] DEFINE_DEVICE_TYPE(R2000, r2000_device, "r2000", "MIPS R2000") DEFINE_DEVICE_TYPE(R2000A, r2000a_device, "r2000a", "MIPS R2000A") @@ -70,8 +63,6 @@ mips1core_device_base::mips1core_device_base(const machine_config &mconfig, devi , m_icache_config("icache", ENDIANNESS_BIG, 32, 32) , m_dcache_config("dcache", ENDIANNESS_BIG, 32, 32) , m_cpurev(cpurev) - , m_hasfpu(false) - , m_fpurev(0) , m_endianness(ENDIANNESS_BIG) , m_icount(0) , m_icache_size(icache_size) @@ -82,6 +73,7 @@ mips1core_device_base::mips1core_device_base(const machine_config &mconfig, devi mips1_device_base::mips1_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u32 cpurev, size_t icache_size, size_t dcache_size) : mips1core_device_base(mconfig, type, tag, owner, clock, cpurev, icache_size, dcache_size) + , m_fcr0(0) { } @@ -126,14 +118,14 @@ r3052e_device::r3052e_device(const machine_config &mconfig, const char *tag, dev } r3071_device::r3071_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, size_t icache_size, size_t dcache_size) - : mips1core_device_base(mconfig, R3071, tag, owner, clock, 0x0200, icache_size, dcache_size) + : mips1_device_base(mconfig, R3071, tag, owner, clock, 0x0200, icache_size, dcache_size) { } r3081_device::r3081_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, size_t icache_size, size_t dcache_size) - : mips1core_device_base(mconfig, R3081, tag, owner, clock, 0x0200, icache_size, dcache_size) + : mips1_device_base(mconfig, R3081, tag, owner, clock, 0x0200, icache_size, dcache_size) { - set_fpurev(0x0300); + set_fpu(0x0300); } iop_device::iop_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) @@ -153,18 +145,6 @@ void mips1core_device_base::device_add_mconfig(machine_config &config) set_addrmap(2, &mips1core_device_base::dcache_map); } -void mips1core_device_base::icache_map(address_map &map) -{ - if (m_icache_size) - map(0, m_icache_size - 1).ram().mirror(~(m_icache_size - 1)); -} - -void mips1core_device_base::dcache_map(address_map &map) -{ - if (m_dcache_size) - map(0, m_dcache_size - 1).ram().mirror(~(m_dcache_size - 1)); -} - void mips1core_device_base::device_start() { // set our instruction counter @@ -177,49 +157,32 @@ void mips1core_device_base::device_start() // register our state for the debugger state_add(STATE_GENPC, "GENPC", m_pc).noshow(); state_add(STATE_GENPCBASE, "CURPC", m_pc).noshow(); - state_add(STATE_GENSP, "GENSP", m_r[31]).noshow(); - state_add(STATE_GENFLAGS, "GENFLAGS", m_cpr[0][COP0_Status]).noshow(); - state_add(MIPS1_PC, "PC", m_pc); - state_add(MIPS1_COP0_SR, "SR", m_cpr[0][COP0_Status]); + state_add(MIPS1_PC, "PC", m_pc); + state_add(MIPS1_COP0 + COP0_Status, "SR", m_cop0[COP0_Status]); - for (int i = 0; i < 32; i++) + for (unsigned i = 0; i < ARRAY_LENGTH(m_r); i++) state_add(MIPS1_R0 + i, util::string_format("R%d", i).c_str(), m_r[i]); state_add(MIPS1_HI, "HI", m_hi); state_add(MIPS1_LO, "LO", m_lo); - state_add(MIPS1_COP0_BADVADDR, "BadVAddr", m_cpr[0][COP0_BadVAddr]); - state_add(MIPS1_COP0_CAUSE, "Cause", m_cpr[0][COP0_Cause]); - state_add(MIPS1_COP0_EPC, "EPC", m_cpr[0][COP0_EPC]); + + // cop0 exception registers + state_add(MIPS1_COP0 + COP0_BadVAddr, "BadVAddr", m_cop0[COP0_BadVAddr]); + state_add(MIPS1_COP0 + COP0_Cause, "Cause", m_cop0[COP0_Cause]); + state_add(MIPS1_COP0 + COP0_EPC, "EPC", m_cop0[COP0_EPC]); // register our state for saving save_item(NAME(m_pc)); save_item(NAME(m_hi)); save_item(NAME(m_lo)); save_item(NAME(m_r)); - save_item(NAME(m_cpr)); - save_item(NAME(m_ccr)); + save_item(NAME(m_cop0)); save_item(NAME(m_branch_state)); save_item(NAME(m_branch_target)); - // initialise cpu and fpu id registers - m_cpr[0][COP0_PRId] = m_cpurev; - m_ccr[1][0] = m_fpurev; -} - -void mips1_device_base::device_start() -{ - mips1core_device_base::device_start(); - - // cop0 tlb registers - state_add(MIPS1_COP0_INDEX, "Index", m_cpr[0][COP0_Index]); - state_add(MIPS1_COP0_RANDOM, "Random", m_cpr[0][COP0_Random]); - state_add(MIPS1_COP0_ENTRYLO, "EntryLo", m_cpr[0][COP0_EntryLo]); - state_add(MIPS1_COP0_ENTRYHI, "EntryHi", m_cpr[0][COP0_EntryHi]); - state_add(MIPS1_COP0_CONTEXT, "Context", m_cpr[0][COP0_Context]); - - save_item(NAME(m_reset_time)); - save_item(NAME(m_tlb)); + // initialise cpu id register + m_cop0[COP0_PRId] = m_cpurev; } void r3041_device::device_start() @@ -227,11 +190,11 @@ void r3041_device::device_start() mips1core_device_base::device_start(); // cop0 r3041 registers - state_add(MIPS1_COP0_BUSCTRL, "BusCtrl", m_cpr[0][COP0_BusCtrl]); - state_add(MIPS1_COP0_CONFIG, "Config", m_cpr[0][COP0_Config]); - state_add(MIPS1_COP0_COUNT, "Count", m_cpr[0][COP0_Count]); - state_add(MIPS1_COP0_PORTSIZE, "PortSize", m_cpr[0][COP0_PortSize]); - state_add(MIPS1_COP0_COMPARE, "Compare", m_cpr[0][COP0_Compare]); + state_add(MIPS1_COP0 + COP0_BusCtrl, "BusCtrl", m_cop0[COP0_BusCtrl]); + state_add(MIPS1_COP0 + COP0_Config, "Config", m_cop0[COP0_Config]); + state_add(MIPS1_COP0 + COP0_Count, "Count", m_cop0[COP0_Count]); + state_add(MIPS1_COP0 + COP0_PortSize, "PortSize", m_cop0[COP0_PortSize]); + state_add(MIPS1_COP0 + COP0_Compare, "Compare", m_cop0[COP0_Compare]); } void mips1core_device_base::device_reset() @@ -241,19 +204,402 @@ void mips1core_device_base::device_reset() m_branch_state = NONE; // non-tlb devices have tlb shut down - m_cpr[0][COP0_Status] = SR_BEV | SR_TS; + m_cop0[COP0_Status] = SR_BEV | SR_TS; m_data_spacenum = 0; } -void mips1_device_base::device_reset() +void mips1core_device_base::execute_run() { - mips1core_device_base::device_reset(); + // check for interrupts + check_irqs(); - // tlb is not shut down - m_cpr[0][COP0_Status] &= ~SR_TS; + // core execution loop + while (m_icount-- > 0) + { + // debugging + debugger_instruction_hook(m_pc); - m_reset_time = total_cycles(); + if (VERBOSE & LOG_IOP) + { + if ((m_pc & 0x1fffffff) == 0x00012C48 || (m_pc & 0x1fffffff) == 0x0001420C || (m_pc & 0x1fffffff) == 0x0001430C) + { + u32 ptr = m_r[5]; + u32 length = m_r[6]; + if (length >= 4096) + length = 4095; + while (length) + { + load<u8>(ptr, [](char c) { printf("%c", c); }); + ptr++; + length--; + } + fflush(stdout); + } + } + + // fetch and execute instruction + fetch(m_pc, [this](u32 const op) + { + // parse the instruction + switch (op >> 26) + { + case 0x00: // SPECIAL + switch (op & 63) + { + case 0x00: // SLL + m_r[RDREG] = m_r[RTREG] << SHIFT; + break; + case 0x02: // SRL + m_r[RDREG] = m_r[RTREG] >> SHIFT; + break; + case 0x03: // SRA + m_r[RDREG] = s32(m_r[RTREG]) >> SHIFT; + break; + case 0x04: // SLLV + m_r[RDREG] = m_r[RTREG] << (m_r[RSREG] & 31); + break; + case 0x06: // SRLV + m_r[RDREG] = m_r[RTREG] >> (m_r[RSREG] & 31); + break; + case 0x07: // SRAV + m_r[RDREG] = s32(m_r[RTREG]) >> (m_r[RSREG] & 31); + break; + case 0x08: // JR + m_branch_state = BRANCH; + m_branch_target = m_r[RSREG]; + break; + case 0x09: // JALR + m_branch_state = BRANCH; + m_branch_target = m_r[RSREG]; + m_r[RDREG] = m_pc + 8; + break; + case 0x0c: // SYSCALL + generate_exception(EXCEPTION_SYSCALL); + break; + case 0x0d: // BREAK + generate_exception(EXCEPTION_BREAK); + break; + case 0x10: // MFHI + m_r[RDREG] = m_hi; + break; + case 0x11: // MTHI + m_hi = m_r[RSREG]; + break; + case 0x12: // MFLO + m_r[RDREG] = m_lo; + break; + case 0x13: // MTLO + m_lo = m_r[RSREG]; + break; + case 0x18: // MULT + { + u64 product = mul_32x32(m_r[RSREG], m_r[RTREG]); + + m_lo = product; + m_hi = product >> 32; + m_icount -= 11; + } + break; + case 0x19: // MULTU + { + u64 product = mulu_32x32(m_r[RSREG], m_r[RTREG]); + + m_lo = product; + m_hi = product >> 32; + m_icount -= 11; + } + break; + case 0x1a: // DIV + if (m_r[RTREG]) + { + m_lo = s32(m_r[RSREG]) / s32(m_r[RTREG]); + m_hi = s32(m_r[RSREG]) % s32(m_r[RTREG]); + } + m_icount -= 34; + break; + case 0x1b: // DIVU + if (m_r[RTREG]) + { + m_lo = m_r[RSREG] / m_r[RTREG]; + m_hi = m_r[RSREG] % m_r[RTREG]; + } + m_icount -= 34; + break; + case 0x20: // ADD + { + u32 const sum = m_r[RSREG] + m_r[RTREG]; + + // overflow: (sign(addend0) == sign(addend1)) && (sign(addend0) != sign(sum)) + if (!BIT(m_r[RSREG] ^ m_r[RTREG], 31) && BIT(m_r[RSREG] ^ sum, 31)) + generate_exception(EXCEPTION_OVERFLOW); + else + m_r[RDREG] = sum; + } + break; + case 0x21: // ADDU + m_r[RDREG] = m_r[RSREG] + m_r[RTREG]; + break; + case 0x22: // SUB + { + u32 const difference = m_r[RSREG] - m_r[RTREG]; + + // overflow: (sign(minuend) != sign(subtrahend)) && (sign(minuend) != sign(difference)) + if (BIT(m_r[RSREG] ^ m_r[RTREG], 31) && BIT(m_r[RSREG] ^ difference, 31)) + generate_exception(EXCEPTION_OVERFLOW); + else + m_r[RDREG] = difference; + } + break; + case 0x23: // SUBU + m_r[RDREG] = m_r[RSREG] - m_r[RTREG]; + break; + case 0x24: // AND + m_r[RDREG] = m_r[RSREG] & m_r[RTREG]; + break; + case 0x25: // OR + m_r[RDREG] = m_r[RSREG] | m_r[RTREG]; + break; + case 0x26: // XOR + m_r[RDREG] = m_r[RSREG] ^ m_r[RTREG]; + break; + case 0x27: // NOR + m_r[RDREG] = ~(m_r[RSREG] | m_r[RTREG]); + break; + case 0x2a: // SLT + m_r[RDREG] = s32(m_r[RSREG]) < s32(m_r[RTREG]); + break; + case 0x2b: // SLTU + m_r[RDREG] = u32(m_r[RSREG]) < u32(m_r[RTREG]); + break; + default: + generate_exception(EXCEPTION_INVALIDOP); + break; + } + break; + case 0x01: // REGIMM + switch (RTREG) + { + case 0x00: // BLTZ + if (s32(m_r[RSREG]) < 0) + { + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + } + break; + case 0x01: // BGEZ + if (s32(m_r[RSREG]) >= 0) + { + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + } + break; + case 0x10: // BLTZAL + if (s32(m_r[RSREG]) < 0) + { + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + m_r[31] = m_pc + 8; + } + break; + case 0x11: // BGEZAL + if (s32(m_r[RSREG]) >= 0) + { + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + m_r[31] = m_pc + 8; + } + break; + default: + generate_exception(EXCEPTION_INVALIDOP); + break; + } + break; + case 0x02: // J + m_branch_state = BRANCH; + m_branch_target = ((m_pc + 4) & 0xf0000000) | (LIMMVAL << 2); + break; + case 0x03: // JAL + m_branch_state = BRANCH; + m_branch_target = ((m_pc + 4) & 0xf0000000) | (LIMMVAL << 2); + m_r[31] = m_pc + 8; + break; + case 0x04: // BEQ + if (m_r[RSREG] == m_r[RTREG]) + { + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + } + break; + case 0x05: // BNE + if (m_r[RSREG] != m_r[RTREG]) + { + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + } + break; + case 0x06: // BLEZ + if (s32(m_r[RSREG]) <= 0) + { + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + } + break; + case 0x07: // BGTZ + if (s32(m_r[RSREG]) > 0) + { + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + } + break; + case 0x08: // ADDI + { + u32 const sum = m_r[RSREG] + SIMMVAL; + + // overflow: (sign(addend0) == sign(addend1)) && (sign(addend0) != sign(sum)) + if (!BIT(m_r[RSREG] ^ s32(SIMMVAL), 31) && BIT(m_r[RSREG] ^ sum, 31)) + generate_exception(EXCEPTION_OVERFLOW); + else + m_r[RTREG] = sum; + } + break; + case 0x09: // ADDIU + m_r[RTREG] = m_r[RSREG] + SIMMVAL; + break; + case 0x0a: // SLTI + m_r[RTREG] = s32(m_r[RSREG]) < s32(SIMMVAL); + break; + case 0x0b: // SLTIU + m_r[RTREG] = u32(m_r[RSREG]) < u32(SIMMVAL); + break; + case 0x0c: // ANDI + m_r[RTREG] = m_r[RSREG] & UIMMVAL; + break; + case 0x0d: // ORI + m_r[RTREG] = m_r[RSREG] | UIMMVAL; + break; + case 0x0e: // XORI + m_r[RTREG] = m_r[RSREG] ^ UIMMVAL; + break; + case 0x0f: // LUI + m_r[RTREG] = UIMMVAL << 16; + break; + case 0x10: // COP0 + if (!(SR & SR_KUc) || (SR & SR_COP0)) + handle_cop0(op); + else + generate_exception(EXCEPTION_BADCOP0); + break; + case 0x11: // COP1 + handle_cop1(op); + break; + case 0x12: // COP2 + handle_cop2(op); + break; + case 0x13: // COP3 + handle_cop3(op); + break; + case 0x20: // LB + load<u8>(SIMMVAL + m_r[RSREG], [this, op](s8 temp) { m_r[RTREG] = temp; }); + break; + case 0x21: // LH + load<u16>(SIMMVAL + m_r[RSREG], [this, op](s16 temp) { m_r[RTREG] = temp; }); + break; + case 0x22: // LWL + lwl(op); + break; + case 0x23: // LW + load<u32>(SIMMVAL + m_r[RSREG], [this, op](u32 temp) { m_r[RTREG] = temp; }); + break; + case 0x24: // LBU + load<u8>(SIMMVAL + m_r[RSREG], [this, op](u8 temp) { m_r[RTREG] = temp; }); + break; + case 0x25: // LHU + load<u16>(SIMMVAL + m_r[RSREG], [this, op](u16 temp) { m_r[RTREG] = temp; }); + break; + case 0x26: // LWR + lwr(op); + break; + case 0x28: // SB + store<u8>(SIMMVAL + m_r[RSREG], m_r[RTREG]); + break; + case 0x29: // SH + store<u16>(SIMMVAL + m_r[RSREG], m_r[RTREG]); + break; + case 0x2a: // SWL + swl(op); + break; + case 0x2b: // SW + store<u32>(SIMMVAL + m_r[RSREG], m_r[RTREG]); + break; + case 0x2e: // SWR + swr(op); + break; + case 0x31: // LWC1 + handle_cop1(op); + break; + case 0x32: // LWC2 + handle_cop2(op); + break; + case 0x33: // LWC3 + handle_cop3(op); + break; + case 0x39: // SWC1 + handle_cop1(op); + break; + case 0x3a: // SWC2 + handle_cop2(op); + break; + case 0x3b: // SWC3 + handle_cop3(op); + break; + default: + generate_exception(EXCEPTION_INVALIDOP); + break; + } + + // update pc and branch state + switch (m_branch_state) + { + case NONE: + m_pc += 4; + break; + + case DELAY: + m_branch_state = NONE; + m_pc = m_branch_target; + break; + + case BRANCH: + m_branch_state = DELAY; + m_pc += 4; + break; + + case EXCEPTION: + m_branch_state = NONE; + break; + } + + // clear register 0 + m_r[0] = 0; + }); + } +} + +void mips1core_device_base::execute_set_input(int irqline, int state) +{ + if (state != CLEAR_LINE) + { + CAUSE |= CAUSE_IPEX0 << irqline; + + // enable debugger interrupt breakpoints + if ((SR & SR_IEc) && (SR & (SR_IMEX0 << irqline))) + standard_irq_callback(irqline); + } + else + CAUSE &= ~(CAUSE_IPEX0 << irqline); + + check_irqs(); } device_memory_interface::space_config_vector mips1core_device_base::memory_space_config() const @@ -265,29 +611,228 @@ device_memory_interface::space_config_vector mips1core_device_base::memory_space }; } +bool mips1core_device_base::memory_translate(int spacenum, int intention, offs_t &address) +{ + // check for kernel memory address + if (BIT(address, 31)) + { + // check debug or kernel mode + if ((intention & TRANSLATE_DEBUG_MASK) || !(SR & SR_KUc)) + { + switch (address & 0xe0000000) + { + case 0x80000000: // kseg0: unmapped, cached, privileged + case 0xa0000000: // kseg1: unmapped, uncached, privileged + address &= ~0xe0000000; + break; + + case 0xc0000000: // kseg2: mapped, cached, privileged + case 0xe0000000: + break; + } + } + else if (SR & SR_KUc) + { + if (!machine().side_effects_disabled()) + { + // exception + m_cop0[COP0_BadVAddr] = address; + + generate_exception((intention & TRANSLATE_WRITE) ? EXCEPTION_ADDRSTORE : EXCEPTION_ADDRLOAD); + } + return false; + } + } + else + // kuseg physical addresses have a 1GB offset + address += 0x40000000; + + return true; +} + std::unique_ptr<util::disasm_interface> mips1core_device_base::create_disassembler() { return std::make_unique<mips1_disassembler>(); } -void mips1core_device_base::generate_exception(int exception, bool refill) +void mips1core_device_base::icache_map(address_map &map) +{ + if (m_icache_size) + map(0, m_icache_size - 1).ram().mirror(~(m_icache_size - 1)); +} + +void mips1core_device_base::dcache_map(address_map &map) +{ + if (m_dcache_size) + map(0, m_dcache_size - 1).ram().mirror(~(m_dcache_size - 1)); +} + +void mips1core_device_base::generate_exception(u32 exception, bool refill) { + if ((VERBOSE & LOG_RISCOS) && (exception == EXCEPTION_SYSCALL)) + { + static char const *const sysv_syscalls[] = + { + "syscall", "exit", "fork", "read", "write", "open", "close", "wait", "creat", "link", + "unlink", "execv", "chdir", "time", "mknod", "chmod", "chown", "brk", "stat", "lseek", + "getpid", "mount", "umount", "setuid", "getuid", "stime", "ptrace", "alarm", "fstat", "pause", + "utime", "stty", "gtty", "access", "nice", "statfs", "sync", "kill", "fstatfs", "setpgrp", + nullptr, "dup", "pipe", "times", "profil", "plock", "setgid", "getgid", "signal", "msgsys", + "sysmips", "acct", "shmsys", "semsys", "ioctl", "uadmin", nullptr, "utssys", nullptr, "execve", + "umask", "chroot", "ofcntl", "ulimit", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + "advfs", "unadvfs", "rmount", "rumount", "rfstart", nullptr, "rdebug", "rfstop", "rfsys", "rmdir", + "mkdir", "getdents", nullptr, nullptr, "sysfs", "getmsg", "putmsg", "poll", "sigreturn", "accept", + "bind", "connect", "gethostid", "getpeername", "getsockname", "getsockopt", "listen", "recv", "recvfrom", "recvmsg", + "select", "send", "sendmsg", "sendto", "sethostid", "setsockopt", "shutdown", "socket", "gethostname", "sethostname", + "getdomainname","setdomainname","truncate", "ftruncate", "rename", "symlink", "readlink", "lstat", "nfsmount", "nfssvc", + "getfh", "async_daemon", "old_exportfs", "mmap", "munmap", "getitimer", "setitimer", nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + "cacheflush", "cachectl", "fchown", "fchmod", "wait3", "mmap", "munmap", "madvise", "getpagesize", "setreuid", + "setregid", "setpgid", "getgroups", "setgroups", "gettimeofday", "getrusage", "getrlimit", "setrlimit", "exportfs", "fcntl" + }; + + static char const *const bsd_syscalls[] = + { + "syscall", "exit", "fork", "read", "write", "open", "close", nullptr, "creat", "link", + "unlink", "execv", "chdir", nullptr, "mknod", "chmod", "chown", "brk", nullptr, "lseek", + "getpid", "omount", "oumount", nullptr, "getuid", nullptr, "ptrace", nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, "access", nullptr, nullptr, "sync", "kill", "stat", nullptr, + "lstat", "dup", "pipe", nullptr, "profil", nullptr, nullptr, "getgid", nullptr, nullptr, + nullptr, "acct", nullptr, nullptr, "ioctl", "reboot", nullptr, "symlink", "readlink", "execve", + "umask", "chroot", "fstat", nullptr, "getpagesize", "mremap", "vfork", nullptr, nullptr, "sbrk", + "sstk", "mmap", "vadvise", "munmap", "mprotec", "madvise", "vhangup", nullptr, "mincore", "getgroups", + "setgroups", "getpgrp", "setpgrp", "setitimer", "wait3", "swapon", "getitimer", "gethostname", "sethostname", "getdtablesize", + "dup2", "getdopt", "fcntl", "select", "setdopt", "fsync", "setpriority", "socket", "connect", "accept", + "getpriority", "send", "recv", "sigreturn", "bind", "setsockopt", "listen", nullptr, "sigvec", "sigblock", + "sigsetmask", "sigpause", "sigstack", "recvmsg", "sendmsg", nullptr, "gettimeofday", "getrusage", "getsockopt", nullptr, + "readv", "writev", "settimeofday", "fchown", "fchmod", "recvfrom", "setreuid", "setregid", "rename", "truncate", + "ftruncate", "flock", nullptr, "sendto", "shutdown", "socketpair", "mkdir", "rmdir", "utimes", "sigcleanup", + "adjtime", "getpeername", "gethostid", "sethostid", "getrlimit", "setrlimit", "killpg", nullptr, "setquota", "quota", + "getsockname", "sysmips", "cacheflush", "cachectl", "debug", nullptr, nullptr, nullptr, "nfssvc", "getdirentries", + "statfs", "fstatfs", "unmount", "async_daemon", "getfh", "getdomainname","setdomainname",nullptr, "quotactl", "old_exportfs", + "mount", "hdwconf", "exportfs", "nfsfh_open", "libattach", "libdetach" + }; + + static char const *const msg_syscalls[] = { "msgget", "msgctl", "msgrcv", "msgsnd" }; + static char const *const shm_syscalls[] = { "shmat", "shmctl", "shmdt", "shmget" }; + static char const *const sem_syscalls[] = { "semctl", "semget", "semop" }; + static char const *const mips_syscalls[] = { "mipskopt", "mipshwconf", "mipsgetrusage", "mipswait", "mipscacheflush", "mipscachectl" }; + + unsigned const asid = (m_cop0[COP0_EntryHi] & EH_ASID) >> 6; + switch (m_r[2]) + { + case 1000: // indirect + switch (m_r[4]) + { + case 1049: // msgsys + LOGMASKED(LOG_RISCOS, "asid %d syscall msgsys:%s() (%s)\n", + asid, (m_r[5] < ARRAY_LENGTH(msg_syscalls)) ? msg_syscalls[m_r[5]] : "unknown", machine().describe_context()); + break; + + case 1052: // shmsys + LOGMASKED(LOG_RISCOS, "asid %d syscall shmsys:%s() (%s)\n", + asid, (m_r[5] < ARRAY_LENGTH(shm_syscalls)) ? shm_syscalls[m_r[5]] : "unknown", machine().describe_context()); + break; + + case 1053: // semsys + LOGMASKED(LOG_RISCOS, "asid %d syscall semsys:%s() (%s)\n", + asid, (m_r[5] < ARRAY_LENGTH(sem_syscalls)) ? sem_syscalls[m_r[5]] : "unknown", machine().describe_context()); + break; + + case 2151: // bsd_sysmips + switch (m_r[5]) + { + case 0x100: // mipskopt + LOGMASKED(LOG_RISCOS, "asid %d syscall bsd_sysmips:mipskopt(\"%s\") (%s)\n", + asid, debug_string(m_r[6]), machine().describe_context()); + break; + + default: + if ((m_r[5] > 0x100) && (m_r[5] - 0x100) < ARRAY_LENGTH(mips_syscalls)) + LOGMASKED(LOG_RISCOS, "asid %d syscall bsd_sysmips:%s() (%s)\n", + asid, mips_syscalls[m_r[5] - 0x100], machine().describe_context()); + else + LOGMASKED(LOG_RISCOS, "asid %d syscall bsd_sysmips:unknown %d (%s)\n", + asid, m_r[5], machine().describe_context()); + break; + } + break; + + default: + if ((m_r[4] > 2000) && (m_r[4] - 2000 < ARRAY_LENGTH(bsd_syscalls)) && bsd_syscalls[m_r[4] - 2000]) + LOGMASKED(LOG_RISCOS, "asid %d syscall bsd_%s() (%s)\n", + asid, bsd_syscalls[m_r[4] - 2000], machine().describe_context()); + else + LOGMASKED(LOG_RISCOS, "asid %d syscall indirect:unknown %d (%s)\n", + asid, m_r[4], machine().describe_context()); + break; + } + break; + + case 1003: // read + case 1006: // close + case 1054: // ioctl + case 1169: // fcntl + LOGMASKED(LOG_RISCOS, "asid %d syscall %s(%d) (%s)\n", + asid, sysv_syscalls[m_r[2] - 1000], m_r[4], machine().describe_context()); + break; + + case 1004: // write + if (m_r[4] == 1 || m_r[4] == 2) + LOGMASKED(LOG_RISCOS, "asid %d syscall %s(%d, \"%s\") (%s)\n", + asid, sysv_syscalls[m_r[2] - 1000], m_r[4], debug_string(m_r[5], m_r[6]), machine().describe_context()); + else + LOGMASKED(LOG_RISCOS, "asid %d syscall %s(%d) (%s)\n", + asid, sysv_syscalls[m_r[2] - 1000], m_r[4], machine().describe_context()); + break; + + case 1005: // open + case 1008: // creat + case 1009: // link + case 1010: // unlink + case 1012: // chdir + case 1018: // stat + case 1033: // access + LOGMASKED(LOG_RISCOS, "asid %d syscall %s(\"%s\") (%s)\n", + asid, sysv_syscalls[m_r[2] - 1000], debug_string(m_r[4]), machine().describe_context()); + break; + + case 1059: // execve + LOGMASKED(LOG_RISCOS, "asid %d syscall execve(\"%s\", [ %s ], [ %s ]) (%s)\n", + asid, debug_string(m_r[4]), debug_string_array(m_r[5]), debug_string_array(m_r[6]), machine().describe_context()); + break; + + case 1060: // umask + LOGMASKED(LOG_RISCOS, "asid %d syscall umask(%#o) (%s)\n", + asid, m_r[4] & 0777, machine().describe_context()); + break; + + default: + if ((m_r[2] > 1000) && (m_r[2] - 1000 < ARRAY_LENGTH(sysv_syscalls)) && sysv_syscalls[m_r[2] - 1000]) + LOGMASKED(LOG_RISCOS, "asid %d syscall %s() (%s)\n", asid, sysv_syscalls[m_r[2] - 1000], machine().describe_context()); + else + LOGMASKED(LOG_RISCOS, "asid %d syscall unknown %d (%s)\n", asid, m_r[2], machine().describe_context()); + break; + } + } + // set the exception PC - m_cpr[0][COP0_EPC] = m_pc; + m_cop0[COP0_EPC] = m_pc; - // put the cause in the low 8 bits and clear the branch delay flag - CAUSE = (CAUSE & ~0x800000ff) | (exception << 2); + // load the cause register + CAUSE = (CAUSE & CAUSE_IP) | exception; // if in a branch delay slot, restart the branch if (m_branch_state == DELAY) { - m_cpr[0][COP0_EPC] -= 4; - CAUSE |= 0x80000000; + m_cop0[COP0_EPC] -= 4; + CAUSE |= CAUSE_BD; } m_branch_state = EXCEPTION; // shift the exception bits - SR = (SR & 0xffffffc0) | ((SR << 2) & 0x3c); + SR = (SR & ~SR_KUIE) | ((SR << 2) & SR_KUIEop); if (refill) m_pc = (SR & SR_BEV) ? 0xbfc00100 : 0x80000000; @@ -295,109 +840,369 @@ void mips1core_device_base::generate_exception(int exception, bool refill) m_pc = (SR & SR_BEV) ? 0xbfc00180 : 0x80000080; debugger_exception_hook(exception); + + if (SR & SR_KUp) + debugger_privilege_hook(); } void mips1core_device_base::check_irqs() { - if ((CAUSE & SR & 0xff00) && (SR & SR_IEc)) + if ((CAUSE & SR & SR_IM) && (SR & SR_IEc)) generate_exception(EXCEPTION_INTERRUPT); } -void mips1core_device_base::set_irq_line(int irqline, int state) -{ - if (state != CLEAR_LINE) - CAUSE |= 0x400 << irqline; - else - CAUSE &= ~(0x400 << irqline); - - check_irqs(); -} - -u32 mips1core_device_base::get_cop0_reg(int const index) +void mips1core_device_base::handle_cop0(u32 const op) { - return m_cpr[0][index]; + switch (RSREG) + { + case 0x00: // MFC0 + m_r[RTREG] = get_cop0_reg(RDREG); + break; + case 0x04: // MTC0 + set_cop0_reg(RDREG, m_r[RTREG]); + break; + case 0x08: // BC0 + switch (RTREG) + { + case 0x00: // BC0F + if (!m_in_brcond[0]()) + { + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + } + break; + case 0x01: // BC0T + if (m_in_brcond[0]()) + { + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + } + break; + default: + generate_exception(EXCEPTION_INVALIDOP); + break; + } + break; + case 0x10: // COP0 + switch (op & 31) + { + case 0x10: // RFE + SR = (SR & ~SR_KUIEpc) | ((SR >> 2) & SR_KUIEpc); + if (bool(SR & SR_KUc) ^ bool(SR & SR_KUp)) + debugger_privilege_hook(); + break; + default: + generate_exception(EXCEPTION_INVALIDOP); + break; + } + break; + default: + generate_exception(EXCEPTION_INVALIDOP); + break; + } } -u32 mips1_device_base::get_cop0_reg(int const index) +u32 mips1core_device_base::get_cop0_reg(unsigned const reg) { - // assume 64-entry tlb with 8 wired entries - if (index == COP0_Random) - m_cpr[0][index] = (63 - ((total_cycles() - m_reset_time) % 56)) << 8; - - return m_cpr[0][index]; + return m_cop0[reg]; } -void mips1core_device_base::set_cop0_reg(int const index, u32 const data) +void mips1core_device_base::set_cop0_reg(unsigned const reg, u32 const data) { - if (index == COP0_Cause) + switch (reg) { - CAUSE = (CAUSE & 0xfc00) | (data & ~0xfc00); - - // update interrupts -- software ints can occur this way - check_irqs(); - } - else if (index == COP0_Status) + case COP0_Context: + m_cop0[COP0_Context] = (m_cop0[COP0_Context] & ~PTE_BASE) | (data & PTE_BASE); + break; + case COP0_Status: { - m_cpr[0][index] = data; + u32 const delta = SR ^ data; + + m_cop0[COP0_Status] = data; // handle cache isolation and swap m_data_spacenum = (data & SR_IsC) ? ((data & SR_SwC) ? 1 : 2) : 0; // update interrupts + if (delta & (SR_IEc | SR_IM)) + check_irqs(); + + if ((delta & SR_KUc) && (m_branch_state != EXCEPTION)) + debugger_privilege_hook(); + } + break; + case COP0_Cause: + CAUSE = (CAUSE & CAUSE_IPEX) | (data & ~CAUSE_IPEX); + + // update interrupts -- software ints can occur this way check_irqs(); + break; + case COP0_PRId: + // read-only register + break; + + default: + m_cop0[reg] = data; + break; } - else if (index == COP0_Context) - m_cpr[0][index] = (m_cpr[0][index] & ~PTE_BASE) | (data & PTE_BASE); - else if (index != COP0_PRId) - m_cpr[0][index] = data; } -void mips1core_device_base::handle_cop0(u32 const op) +void mips1core_device_base::handle_cop1(u32 const op) { - switch (RSREG) + if (!(SR & SR_COP1)) + generate_exception(EXCEPTION_BADCOP1); +} + +void mips1core_device_base::handle_cop2(u32 const op) +{ + if (SR & SR_COP2) { - case 0x00: /* MFCz */ if (RTREG) RTVAL = get_cop0_reg(RDREG); break; - case 0x02: /* CFCz */ if (RTREG) RTVAL = get_cop_creg<0>(RDREG); break; - case 0x04: /* MTCz */ set_cop0_reg(RDREG, RTVAL); break; - case 0x06: /* CTCz */ set_cop_creg<0>(RDREG, RTVAL); break; - case 0x08: /* BC */ + switch (RSREG) + { + case 0x08: // BC2 switch (RTREG) { - case 0x00: /* BCzF */ if (!m_in_brcond[0]()) ADDPC(SIMMVAL); break; - case 0x01: /* BCzT */ if (m_in_brcond[0]()) ADDPC(SIMMVAL); break; - case 0x02: /* BCzFL */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x03: /* BCzTL */ generate_exception(EXCEPTION_INVALIDOP); break; - default: generate_exception(EXCEPTION_INVALIDOP); break; + case 0x00: // BC2F + if (!m_in_brcond[2]()) + { + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + } + break; + case 0x01: // BC2T + if (m_in_brcond[2]()) + { + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + } + break; + default: + generate_exception(EXCEPTION_INVALIDOP); + break; } break; - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1a: - case 0x1b: - case 0x1c: - case 0x1d: - case 0x1e: - case 0x1f: /* COP */ - switch (op & 0x01ffffff) + default: + generate_exception(EXCEPTION_INVALIDOP); + break; + } + } + else + generate_exception(EXCEPTION_BADCOP2); +} + +void mips1core_device_base::handle_cop3(u32 const op) +{ + if (SR & SR_COP3) + { + switch (RSREG) + { + case 0x08: // BC3 + switch (RTREG) { - case 0x01: /* TLBR */ break; - case 0x02: /* TLBWI */ break; - case 0x06: /* TLBWR */ break; - case 0x08: /* TLBP */ break; - case 0x10: /* RFE */ SR = (SR & 0xfffffff0) | ((SR >> 2) & 0x0f); break; - case 0x18: /* ERET */ generate_exception(EXCEPTION_INVALIDOP); break; - default: generate_exception(EXCEPTION_INVALIDOP); break; + case 0x00: // BC3F + if (!m_in_brcond[3]()) + { + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + } + break; + case 0x01: // BC3T + if (m_in_brcond[3]()) + { + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + } + break; + default: + generate_exception(EXCEPTION_INVALIDOP); + break; } break; - default: generate_exception(EXCEPTION_INVALIDOP); break; + default: + generate_exception(EXCEPTION_INVALIDOP); + break; + } + } + else + generate_exception(EXCEPTION_BADCOP3); +} + +void mips1core_device_base::lwl(u32 const op) +{ + offs_t const offset = SIMMVAL + m_r[RSREG]; + load<u32>(offset & ~3, [this, op, offset](u32 temp) + { + unsigned const shift = ((offset & 3) ^ ENDIAN_VALUE_LE_BE(m_endianness, 3, 0)) << 3; + + m_r[RTREG] = (m_r[RTREG] & ~u32(0xffffffffU << shift)) | (temp << shift); + }); +} + +void mips1core_device_base::lwr(u32 const op) +{ + offs_t const offset = SIMMVAL + m_r[RSREG]; + load<u32>(offset & ~3, [this, op, offset](u32 temp) + { + unsigned const shift = ((offset & 0x3) ^ ENDIAN_VALUE_LE_BE(m_endianness, 0, 3)) << 3; + + m_r[RTREG] = (m_r[RTREG] & ~u32(0xffffffffU >> shift)) | (temp >> shift); + }); +} + +void mips1core_device_base::swl(u32 const op) +{ + offs_t const offset = SIMMVAL + m_r[RSREG]; + unsigned const shift = ((offset & 3) ^ ENDIAN_VALUE_LE_BE(m_endianness, 3, 0)) << 3; + + store<u32>(offset & ~3, m_r[RTREG] >> shift, 0xffffffffU >> shift); +} + +void mips1core_device_base::swr(u32 const op) +{ + offs_t const offset = SIMMVAL + m_r[RSREG]; + unsigned const shift = ((offset & 3) ^ ENDIAN_VALUE_LE_BE(m_endianness, 0, 3)) << 3; + + store<u32>(offset & ~3, m_r[RTREG] << shift, 0xffffffffU << shift); +} + +template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, void> mips1core_device_base::load(u32 address, U &&apply) +{ + if (memory_translate(m_data_spacenum, TRANSLATE_READ, address)) + { + switch (sizeof(T)) + { + case 1: apply(T(space(m_data_spacenum).read_byte(address))); break; + case 2: apply(T(space(m_data_spacenum).read_word(address))); break; + case 4: apply(T(space(m_data_spacenum).read_dword(address))); break; + } + } +} + +template <typename T, typename U> std::enable_if_t<std::is_convertible<U, T>::value, void> mips1core_device_base::store(u32 address, U data, T mem_mask) +{ + if (memory_translate(m_data_spacenum, TRANSLATE_WRITE, address)) + { + switch (sizeof(T)) + { + case 1: space(m_data_spacenum).write_byte(address, T(data)); break; + case 2: space(m_data_spacenum).write_word(address, T(data), mem_mask); break; + case 4: space(m_data_spacenum).write_dword(address, T(data), mem_mask); break; + } + } +} + +bool mips1core_device_base::fetch(u32 address, std::function<void(u32)> &&apply) +{ + if (memory_translate(0, TRANSLATE_FETCH, address)) + { + apply(space(0).read_dword(address)); + + return true; + } + else + return false; +} + +std::string mips1core_device_base::debug_string(u32 string_pointer, unsigned const limit) +{ + auto const suppressor(machine().disable_side_effects()); + + bool done = false; + bool mapped = false; + std::string result(""); + + while (!done) + { + done = true; + load<u8>(string_pointer++, [limit, &done, &mapped, &result](u8 byte) + { + mapped = true; + if (byte != 0) + { + result += byte; + + done = result.length() == limit; + } + }); + } + + if (!mapped) + result.assign("[unmapped]"); + + return result; +} + +std::string mips1core_device_base::debug_string_array(u32 array_pointer) +{ + auto const suppressor(machine().disable_side_effects()); + + bool done = false; + std::string result(""); + + while (!done) + { + done = true; + load<u32>(array_pointer, [this, &done, &result](u32 string_pointer) + { + if (string_pointer != 0) + { + if (!result.empty()) + result += ", "; + + result += '\"' + debug_string(string_pointer) + '\"'; + + done = false; + } + }); + + array_pointer += 4; + } + + return result; +} + +void mips1_device_base::device_start() +{ + mips1core_device_base::device_start(); + + // cop0 tlb registers + state_add(MIPS1_COP0 + COP0_Index, "Index", m_cop0[COP0_Index]); + state_add(MIPS1_COP0 + COP0_Random, "Random", m_cop0[COP0_Random]); + state_add(MIPS1_COP0 + COP0_EntryLo, "EntryLo", m_cop0[COP0_EntryLo]); + state_add(MIPS1_COP0 + COP0_EntryHi, "EntryHi", m_cop0[COP0_EntryHi]); + state_add(MIPS1_COP0 + COP0_Context, "Context", m_cop0[COP0_Context]); + + // cop1 registers + if (m_fcr0) + { + state_add(MIPS1_FCR31, "FCSR", m_fcr31); + for (unsigned i = 0; i < ARRAY_LENGTH(m_f); i++) + state_add(MIPS1_F0 + i, util::string_format("F%d", i).c_str(), m_f[i]); + } + + save_item(NAME(m_reset_time)); + save_item(NAME(m_tlb)); + + save_item(NAME(m_fcr30)); + save_item(NAME(m_fcr31)); + save_item(NAME(m_f)); +} + +void mips1_device_base::device_reset() +{ + mips1core_device_base::device_reset(); + + // tlb is not shut down + m_cop0[COP0_Status] &= ~SR_TS; + + m_reset_time = total_cycles(); + + // initialize tlb mru index with identity mapping + for (unsigned i = 0; i < ARRAY_LENGTH(m_tlb); i++) + { + m_tlb_mru[TRANSLATE_READ][i] = i; + m_tlb_mru[TRANSLATE_WRITE][i] = i; + m_tlb_mru[TRANSLATE_FETCH][i] = i; } } @@ -407,26 +1212,26 @@ void mips1_device_base::handle_cop0(u32 const op) { case 0x42000001: // TLBR - read tlb { - u8 const index = (m_cpr[0][COP0_Index] >> 8) & 0x3f; + u8 const index = (m_cop0[COP0_Index] >> 8) & 0x3f; - m_cpr[0][COP0_EntryHi] = m_tlb[index][0]; - m_cpr[0][COP0_EntryLo] = m_tlb[index][1]; + m_cop0[COP0_EntryHi] = m_tlb[index][0]; + m_cop0[COP0_EntryLo] = m_tlb[index][1]; } break; case 0x42000002: // TLBWI - write tlb (indexed) { - u8 const index = (m_cpr[0][COP0_Index] >> 8) & 0x3f; + u8 const index = (m_cop0[COP0_Index] >> 8) & 0x3f; - m_tlb[index][0] = m_cpr[0][COP0_EntryHi]; - m_tlb[index][1] = m_cpr[0][COP0_EntryLo]; + m_tlb[index][0] = m_cop0[COP0_EntryHi]; + m_tlb[index][1] = m_cop0[COP0_EntryLo]; LOGMASKED(LOG_TLB, "tlb write index %d asid %d vpn 0x%08x pfn 0x%08x %c%c%c%c (%s)\n", - index, (m_cpr[0][COP0_EntryHi] & EH_ASID) >> 6, m_cpr[0][COP0_EntryHi] & EH_VPN, m_cpr[0][COP0_EntryLo] & EL_PFN, - m_cpr[0][COP0_EntryLo] & EL_N ? 'N' : '-', - m_cpr[0][COP0_EntryLo] & EL_D ? 'D' : '-', - m_cpr[0][COP0_EntryLo] & EL_V ? 'V' : '-', - m_cpr[0][COP0_EntryLo] & EL_G ? 'G' : '-', + index, (m_cop0[COP0_EntryHi] & EH_ASID) >> 6, m_cop0[COP0_EntryHi] & EH_VPN, m_cop0[COP0_EntryLo] & EL_PFN, + m_cop0[COP0_EntryLo] & EL_N ? 'N' : '-', + m_cop0[COP0_EntryLo] & EL_D ? 'D' : '-', + m_cop0[COP0_EntryLo] & EL_V ? 'V' : '-', + m_cop0[COP0_EntryLo] & EL_G ? 'G' : '-', machine().describe_context()); } break; @@ -435,37 +1240,37 @@ void mips1_device_base::handle_cop0(u32 const op) { u8 const random = get_cop0_reg(COP0_Random) >> 8; - m_tlb[random][0] = m_cpr[0][COP0_EntryHi]; - m_tlb[random][1] = m_cpr[0][COP0_EntryLo]; + m_tlb[random][0] = m_cop0[COP0_EntryHi]; + m_tlb[random][1] = m_cop0[COP0_EntryLo]; LOGMASKED(LOG_TLB, "tlb write random %d asid %d vpn 0x%08x pfn 0x%08x %c%c%c%c (%s)\n", - random, (m_cpr[0][COP0_EntryHi] & EH_ASID) >> 6, m_cpr[0][COP0_EntryHi] & EH_VPN, m_cpr[0][COP0_EntryLo] & EL_PFN, - m_cpr[0][COP0_EntryLo] & EL_N ? 'N' : '-', - m_cpr[0][COP0_EntryLo] & EL_D ? 'D' : '-', - m_cpr[0][COP0_EntryLo] & EL_V ? 'V' : '-', - m_cpr[0][COP0_EntryLo] & EL_G ? 'G' : '-', + random, (m_cop0[COP0_EntryHi] & EH_ASID) >> 6, m_cop0[COP0_EntryHi] & EH_VPN, m_cop0[COP0_EntryLo] & EL_PFN, + m_cop0[COP0_EntryLo] & EL_N ? 'N' : '-', + m_cop0[COP0_EntryLo] & EL_D ? 'D' : '-', + m_cop0[COP0_EntryLo] & EL_V ? 'V' : '-', + m_cop0[COP0_EntryLo] & EL_G ? 'G' : '-', machine().describe_context()); } break; case 0x42000008: // TLBP - probe tlb - m_cpr[0][COP0_Index] = 0x80000000; + m_cop0[COP0_Index] = 0x80000000; for (u8 index = 0; index < 64; index++) { // test vpn and optionally asid u32 const mask = (m_tlb[index][1] & EL_G) ? EH_VPN : EH_VPN | EH_ASID; - if ((m_tlb[index][0] & mask) == (m_cpr[0][COP0_EntryHi] & mask)) + if ((m_tlb[index][0] & mask) == (m_cop0[COP0_EntryHi] & mask)) { LOGMASKED(LOG_TLB, "tlb probe hit vpn 0x%08x index %d (%s)\n", - m_cpr[0][COP0_EntryHi] & mask, index, machine().describe_context()); + m_cop0[COP0_EntryHi] & mask, index, machine().describe_context()); - m_cpr[0][COP0_Index] = index << 8; + m_cop0[COP0_Index] = index << 8; break; } } - if ((VERBOSE & LOG_TLB) && BIT(m_cpr[0][COP0_Index], 31)) + if ((VERBOSE & LOG_TLB) && BIT(m_cop0[COP0_Index], 31)) LOGMASKED(LOG_TLB, "tlb probe miss asid %d vpn 0x%08x(%s)\n", - (m_cpr[0][COP0_EntryHi] & EH_ASID) >> 6, m_cpr[0][COP0_EntryHi] & EH_VPN, machine().describe_context()); + (m_cop0[COP0_EntryHi] & EH_ASID) >> 6, m_cop0[COP0_EntryHi] & EH_VPN, machine().describe_context()); break; default: @@ -473,491 +1278,558 @@ void mips1_device_base::handle_cop0(u32 const op) } } -void mips1core_device_base::set_cop1_creg(int idx, u32 val) +u32 mips1_device_base::get_cop0_reg(unsigned const reg) { - // fpu revision register is read-only - if (idx) - m_ccr[1][idx] = val; + // assume 64-entry tlb with 8 wired entries + if (reg == COP0_Random) + m_cop0[reg] = (63 - ((total_cycles() - m_reset_time) % 56)) << 8; + + return m_cop0[reg]; } -void mips1core_device_base::handle_cop1(u32 const op) +void mips1_device_base::handle_cop1(u32 const op) { - if (!m_hasfpu) - return; - - switch (RSREG) + if (!(SR & SR_COP1)) { - case 0x00: /* MFCz */ if (RTREG) RTVAL = get_cop_reg<1>(RDREG); break; - case 0x02: /* CFCz */ if (RTREG) RTVAL = get_cop_creg<1>(RDREG); break; - case 0x04: /* MTCz */ set_cop_reg<1>(RDREG, RTVAL); break; - case 0x06: /* CTCz */ set_cop1_creg(RDREG, RTVAL); break; - case 0x08: /* BC */ - switch (RTREG) - { - case 0x00: /* BCzF */ if (!m_in_brcond[1]()) ADDPC(SIMMVAL); break; - case 0x01: /* BCzT */ if (m_in_brcond[1]()) ADDPC(SIMMVAL); break; - case 0x02: /* BCzFL */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x03: /* BCzTL */ generate_exception(EXCEPTION_INVALIDOP); break; - default: generate_exception(EXCEPTION_INVALIDOP); break; - } - break; - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1a: - case 0x1b: - case 0x1c: - case 0x1d: - case 0x1e: - case 0x1f: /* COP */ generate_exception(EXCEPTION_INVALIDOP); break; - default: generate_exception(EXCEPTION_INVALIDOP); break; + generate_exception(EXCEPTION_BADCOP1); + return; } -} -template <unsigned Coprocessor> void mips1core_device_base::handle_cop(u32 const op) -{ - switch (RSREG) + if (!m_fcr0) + return; + + switch (op >> 26) { - case 0x00: /* MFCz */ if (RTREG) RTVAL = get_cop_reg<Coprocessor>(RDREG); break; - case 0x02: /* CFCz */ if (RTREG) RTVAL = get_cop_creg<Coprocessor>(RDREG); break; - case 0x04: /* MTCz */ set_cop_reg<Coprocessor>(RDREG, RTVAL); break; - case 0x06: /* CTCz */ set_cop_creg<Coprocessor>(RDREG, RTVAL); break; - case 0x08: /* BC */ - switch (RTREG) + case 0x11: // COP1 + switch ((op >> 21) & 0x1f) + { + case 0x00: // MFC1 + if (FSREG & 1) + // move the high half of the floating point register + m_r[RTREG] = m_f[FSREG >> 1] >> 32; + else + // move the low half of the floating point register + m_r[RTREG] = m_f[FSREG >> 1] >> 0; + break; + case 0x02: // CFC1 + switch (FSREG) { - case 0x00: /* BCzF */ if (!m_in_brcond[Coprocessor]()) ADDPC(SIMMVAL); break; - case 0x01: /* BCzT */ if (m_in_brcond[Coprocessor]()) ADDPC(SIMMVAL); break; - case 0x02: /* BCzFL */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x03: /* BCzTL */ generate_exception(EXCEPTION_INVALIDOP); break; - default: generate_exception(EXCEPTION_INVALIDOP); break; + case 0: m_r[RTREG] = m_fcr0; break; + case 30: m_r[RTREG] = m_fcr30; break; + case 31: m_r[RTREG] = m_fcr31; break; + break; + + default: + logerror("cfc1 undefined fpu control register %d (%s)\n", FSREG, machine().describe_context()); + break; } break; - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1a: - case 0x1b: - case 0x1c: - case 0x1d: - case 0x1e: - case 0x1f: /* COP */ generate_exception(EXCEPTION_INVALIDOP); break; - default: generate_exception(EXCEPTION_INVALIDOP); break; - } -} + case 0x04: // MTC1 + if (FSREG & 1) + // load the high half of the floating point register + m_f[FSREG >> 1] = (u64(m_r[RTREG]) << 32) | u32(m_f[FSREG >> 1]); + else + // load the low half of the floating point register + m_f[FSREG >> 1] = (m_f[FSREG >> 1] & ~0xffffffffULL) | m_r[RTREG]; + break; + case 0x06: // CTC1 + switch (RDREG) + { + case 0: // register is read-only + break; -void mips1core_device_base::execute_run() -{ - // check for IRQs - check_irqs(); + case 30: + m_fcr30 = m_r[RTREG]; + break; - // core execution loop - do - { - // debugging - debugger_instruction_hook(m_pc); + case 31: + m_fcr31 = m_r[RTREG]; -#if ENABLE_IOP_KPUTS - if ((m_pc & 0x1fffffff) == 0x00012C48 || (m_pc & 0x1fffffff) == 0x0001420C || (m_pc & 0x1fffffff) == 0x0001430C) - { - u32 ptr = m_r[5]; - u32 length = m_r[6]; - if (length >= 4096) - length = 4095; - while (length) - { - load<u8>(ptr, [this](char c) { printf("%c", c); }); - ptr++; - length--; - } - fflush(stdout); - } -#endif + // update rounding mode + switch (m_fcr31 & FCR31_RM) + { + case 0: softfloat_roundingMode = softfloat_round_near_even; break; + case 1: softfloat_roundingMode = softfloat_round_minMag; break; + case 2: softfloat_roundingMode = softfloat_round_max; break; + case 3: softfloat_roundingMode = softfloat_round_min; break; + } - // fetch and execute instruction - fetch(m_pc, [this](u32 const op) - { - // parse the instruction - switch (op >> 26) + // exception check + if ((m_fcr31 & FCR31_CE) || ((m_fcr31 & FCR31_CM) >> 5) & (m_fcr31 & FCR31_EM)) + execute_set_input(m_fpu_irq, ASSERT_LINE); + break; + + default: + logerror("ctc1 undefined fpu control register %d (%s)\n", RDREG, machine().describe_context()); + break; + } + break; + case 0x08: // BC + switch ((op >> 16) & 0x1f) + { + case 0x00: // BC1F + if (!(m_fcr31 & FCR31_C)) { - case 0x00: /* SPECIAL */ - switch (op & 63) - { - case 0x00: /* SLL */ if (RDREG) RDVAL = RTVAL << SHIFT; break; - case 0x02: /* SRL */ if (RDREG) RDVAL = RTVAL >> SHIFT; break; - case 0x03: /* SRA */ if (RDREG) RDVAL = s32(RTVAL) >> SHIFT; break; - case 0x04: /* SLLV */ if (RDREG) RDVAL = RTVAL << (RSVAL & 31); break; - case 0x06: /* SRLV */ if (RDREG) RDVAL = RTVAL >> (RSVAL & 31); break; - case 0x07: /* SRAV */ if (RDREG) RDVAL = s32(RTVAL) >> (RSVAL & 31); break; - case 0x08: /* JR */ SETPC(RSVAL); break; - case 0x09: /* JALR */ SETPCL(RSVAL, RDREG); break; - case 0x0c: /* SYSCALL */ generate_exception(EXCEPTION_SYSCALL); break; - case 0x0d: /* BREAK */ generate_exception(EXCEPTION_BREAK); break; - case 0x0f: /* SYNC */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x10: /* MFHI */ if (RDREG) RDVAL = m_hi; break; - case 0x11: /* MTHI */ m_hi = RSVAL; break; - case 0x12: /* MFLO */ if (RDREG) RDVAL = m_lo; break; - case 0x13: /* MTLO */ m_lo = RSVAL; break; - case 0x18: /* MULT */ - { - u64 product = mul_32x32(RSVAL, RTVAL); + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + } + break; + case 0x01: // BC1T + if (m_fcr31 & FCR31_C) + { + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + } + break; - m_lo = product; - m_hi = product >> 32; - m_icount -= 11; - } - break; - case 0x19: /* MULTU */ - { - u64 product = mulu_32x32(RSVAL, RTVAL); + default: + // unimplemented operation + m_fcr31 |= FCR31_CE; + execute_set_input(m_fpu_irq, ASSERT_LINE); + break; + } + break; + case 0x10: // S + switch (op & 0x3f) + { + case 0x00: // ADD.S + set_cop1_reg(FDREG >> 1, f32_add(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) }).v); + break; + case 0x01: // SUB.S + set_cop1_reg(FDREG >> 1, f32_sub(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) }).v); + break; + case 0x02: // MUL.S + set_cop1_reg(FDREG >> 1, f32_mul(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) }).v); + break; + case 0x03: // DIV.S + set_cop1_reg(FDREG >> 1, f32_div(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) }).v); + break; + case 0x05: // ABS.S + if (f32_lt(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ 0 })) + set_cop1_reg(FDREG >> 1, f32_mul(float32_t{ u32(m_f[FSREG >> 1]) }, i32_to_f32(-1)).v); + else + set_cop1_reg(FDREG >> 1, m_f[FSREG >> 1]); + break; + case 0x06: // MOV.S + m_f[FDREG >> 1] = m_f[FSREG >> 1]; + break; + case 0x07: // NEG.S + set_cop1_reg(FDREG >> 1, f32_mul(float32_t{ u32(m_f[FSREG >> 1]) }, i32_to_f32(-1)).v); + break; - m_lo = product; - m_hi = product >> 32; - m_icount -= 11; - } - break; - case 0x1a: /* DIV */ - if (RTVAL) - { - m_lo = s32(RSVAL) / s32(RTVAL); - m_hi = s32(RSVAL) % s32(RTVAL); - } - m_icount -= 34; - break; - case 0x1b: /* DIVU */ - if (RTVAL) - { - m_lo = RSVAL / RTVAL; - m_hi = RSVAL % RTVAL; - } - m_icount -= 34; - break; - case 0x20: /* ADD */ - { - u32 const sum = RSVAL + RTVAL; - - // overflow: (sign(addend0) == sign(addend1)) && (sign(addend0) != sign(sum)) - if (!BIT(RSVAL ^ RTVAL, 31) && BIT(RSVAL ^ sum, 31)) - generate_exception(EXCEPTION_OVERFLOW); - else if (RDREG) - RDVAL = sum; - } - break; - case 0x21: /* ADDU */ if (RDREG) RDVAL = RSVAL + RTVAL; break; - case 0x22: /* SUB */ - { - u32 const difference = RSVAL - RTVAL; - - // overflow: (sign(minuend) != sign(subtrahend)) && (sign(minuend) != sign(difference)) - if (BIT(RSVAL ^ RTVAL, 31) && BIT(RSVAL ^ difference, 31)) - generate_exception(EXCEPTION_OVERFLOW); - else if (RDREG) - RDVAL = difference; - } - break; - case 0x23: /* SUBU */ if (RDREG) RDVAL = RSVAL - RTVAL; break; - case 0x24: /* AND */ if (RDREG) RDVAL = RSVAL & RTVAL; break; - case 0x25: /* OR */ if (RDREG) RDVAL = RSVAL | RTVAL; break; - case 0x26: /* XOR */ if (RDREG) RDVAL = RSVAL ^ RTVAL; break; - case 0x27: /* NOR */ if (RDREG) RDVAL = ~(RSVAL | RTVAL); break; - case 0x2a: /* SLT */ if (RDREG) RDVAL = s32(RSVAL) < s32(RTVAL); break; - case 0x2b: /* SLTU */ if (RDREG) RDVAL = u32(RSVAL) < u32(RTVAL); break; - case 0x30: /* TEQ */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x31: /* TGEU */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x32: /* TLT */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x33: /* TLTU */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x34: /* TGE */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x36: /* TNE */ generate_exception(EXCEPTION_INVALIDOP); break; - default: /* ??? */ generate_exception(EXCEPTION_INVALIDOP); break; - } - break; + case 0x21: // CVT.D.S + set_cop1_reg(FDREG >> 1, f32_to_f64(float32_t{ u32(m_f[FSREG >> 1]) }).v); + break; + case 0x24: // CVT.W.S + set_cop1_reg(FDREG >> 1, f32_to_i32(float32_t{ u32(m_f[FSREG >> 1]) }, softfloat_roundingMode, true)); + break; - case 0x01: /* REGIMM */ - switch (RTREG) - { - case 0x00: /* BLTZ */ if (s32(RSVAL) < 0) ADDPC(SIMMVAL); break; - case 0x01: /* BGEZ */ if (s32(RSVAL) >= 0) ADDPC(SIMMVAL); break; - case 0x02: /* BLTZL */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x03: /* BGEZL */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x08: /* TGEI */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x09: /* TGEIU */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x0a: /* TLTI */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x0b: /* TLTIU */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x0c: /* TEQI */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x0e: /* TNEI */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x10: /* BLTZAL */ if (s32(RSVAL) < 0) ADDPCL(SIMMVAL, 31); break; - case 0x11: /* BGEZAL */ if (s32(RSVAL) >= 0) ADDPCL(SIMMVAL, 31); break; - case 0x12: /* BLTZALL */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x13: /* BGEZALL */ generate_exception(EXCEPTION_INVALIDOP); break; - default: /* ??? */ generate_exception(EXCEPTION_INVALIDOP); break; - } - break; + case 0x30: // C.F.S (false) + m_fcr31 &= ~FCR31_C; + break; + case 0x31: // C.UN.S (unordered) + f32_eq(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) }); + if (softfloat_exceptionFlags & softfloat_flag_invalid) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; + case 0x32: // C.EQ.S (equal) + if (f32_eq(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; + case 0x33: // C.UEQ.S (unordered equal) + if (f32_eq(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; + case 0x34: // C.OLT.S (less than) + if (f32_lt(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; + case 0x35: // C.ULT.S (unordered less than) + if (f32_lt(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; + case 0x36: // C.OLE.S (less than or equal) + if (f32_le(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; + case 0x37: // C.ULE.S (unordered less than or equal) + if (f32_le(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; - case 0x02: /* J */ ABSPC(LIMMVAL); break; - case 0x03: /* JAL */ ABSPCL(LIMMVAL, 31); break; - case 0x04: /* BEQ */ if (RSVAL == RTVAL) ADDPC(SIMMVAL); break; - case 0x05: /* BNE */ if (RSVAL != RTVAL) ADDPC(SIMMVAL); break; - case 0x06: /* BLEZ */ if (s32(RSVAL) <= 0) ADDPC(SIMMVAL); break; - case 0x07: /* BGTZ */ if (s32(RSVAL) > 0) ADDPC(SIMMVAL); break; - case 0x08: /* ADDI */ - { - u32 const sum = RSVAL + SIMMVAL; + case 0x38: // C.SF.S (signalling false) + f32_eq(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) }); - // overflow: (sign(addend0) == sign(addend1)) && (sign(addend0) != sign(sum)) - if (!BIT(RSVAL ^ s32(SIMMVAL), 31) && BIT(RSVAL ^ sum, 31)) - generate_exception(EXCEPTION_OVERFLOW); - else if (RTREG) - RTVAL = sum; - } - break; - case 0x09: /* ADDIU */ if (RTREG) RTVAL = RSVAL + SIMMVAL; break; - case 0x0a: /* SLTI */ if (RTREG) RTVAL = s32(RSVAL) < s32(SIMMVAL); break; - case 0x0b: /* SLTIU */ if (RTREG) RTVAL = u32(RSVAL) < u32(SIMMVAL); break; - case 0x0c: /* ANDI */ if (RTREG) RTVAL = RSVAL & UIMMVAL; break; - case 0x0d: /* ORI */ if (RTREG) RTVAL = RSVAL | UIMMVAL; break; - case 0x0e: /* XORI */ if (RTREG) RTVAL = RSVAL ^ UIMMVAL; break; - case 0x0f: /* LUI */ if (RTREG) RTVAL = UIMMVAL << 16; break; - case 0x10: /* COP0 */ - if (!(SR & SR_KUc) || (SR & SR_COP0)) - handle_cop0(op); - else - generate_exception(EXCEPTION_BADCOP); - break; - case 0x11: // COP1 - if (SR & SR_COP1) - handle_cop1(op); - else - generate_exception(EXCEPTION_BADCOP); - break; - case 0x12: // COP2 - if (SR & SR_COP2) - handle_cop<2>(op); - else - generate_exception(EXCEPTION_BADCOP); - break; - case 0x13: // COP3 - if (SR & SR_COP3) - handle_cop<3>(op); - else - generate_exception(EXCEPTION_BADCOP); - break; - case 0x14: /* BEQL */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x15: /* BNEL */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x16: /* BLEZL */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x17: /* BGTZL */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x20: /* LB */ load<u8>(SIMMVAL + RSVAL, [this, op](s8 temp) { if (RTREG) RTVAL = temp; }); break; - case 0x21: /* LH */ load<u16>(SIMMVAL + RSVAL, [this, op](s16 temp) { if (RTREG) RTVAL = temp; }); break; - case 0x22: /* LWL */ lwl(op); break; - case 0x23: /* LW */ load<u32>(SIMMVAL + RSVAL, [this, op](u32 temp) { if (RTREG) RTVAL = temp; }); break; - case 0x24: /* LBU */ load<u8>(SIMMVAL + RSVAL, [this, op](u8 temp) { if (RTREG) RTVAL = temp; }); break; - case 0x25: /* LHU */ load<u16>(SIMMVAL + RSVAL, [this, op](u16 temp) { if (RTREG) RTVAL = temp; }); break; - case 0x26: /* LWR */ lwr(op); break; - case 0x28: /* SB */ store<u8>(SIMMVAL + RSVAL, RTVAL); break; - case 0x29: /* SH */ store<u16>(SIMMVAL + RSVAL, RTVAL); break; - case 0x2a: /* SWL */ swl(op); break; - case 0x2b: /* SW */ store<u32>(SIMMVAL + RSVAL, RTVAL); break; - case 0x2e: /* SWR */ swr(op); break; - case 0x2f: /* CACHE */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x30: /* LL */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x31: /* LWC1 */ load<u32>(SIMMVAL + RSVAL, [this, op](u32 temp) { set_cop_reg<1>(RTREG, temp); }); break; - case 0x32: /* LWC2 */ load<u32>(SIMMVAL + RSVAL, [this, op](u32 temp) { set_cop_reg<2>(RTREG, temp); }); break; - case 0x33: /* LWC3 */ load<u32>(SIMMVAL + RSVAL, [this, op](u32 temp) { set_cop_reg<3>(RTREG, temp); }); break; - case 0x34: /* LDC0 */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x35: /* LDC1 */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x36: /* LDC2 */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x37: /* LDC3 */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x38: /* SC */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x39: /* LWC1 */ store<u32>(SIMMVAL + RSVAL, get_cop_reg<1>(RTREG)); break; - case 0x3a: /* LWC2 */ store<u32>(SIMMVAL + RSVAL, get_cop_reg<2>(RTREG)); break; - case 0x3b: /* LWC3 */ store<u32>(SIMMVAL + RSVAL, get_cop_reg<3>(RTREG)); break; - case 0x3c: /* SDC0 */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x3d: /* SDC1 */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x3e: /* SDC2 */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x3f: /* SDC3 */ generate_exception(EXCEPTION_INVALIDOP); break; - default: /* ??? */ generate_exception(EXCEPTION_INVALIDOP); break; + m_fcr31 &= ~FCR31_C; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); } + break; + case 0x39: // C.NGLE.S (not greater, less than or equal) + f32_eq(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) }); - // update pc and branch state - switch (m_branch_state) - { - case NONE: - m_pc += 4; + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_C | FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } + else + m_fcr31 &= ~FCR31_C; break; + case 0x3a: // C.SEQ.S (signalling equal) + if (f32_eq(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; - case DELAY: - m_branch_state = NONE; - m_pc = m_branch_target; + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } break; + case 0x3b: // C.NGL.S (not greater or less than) + if (f32_eq(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; - case BRANCH: - m_branch_state = DELAY; - m_pc += 4; + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } break; + case 0x3c: // C.LT.S (less than) + if (f32_lt(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; - case EXCEPTION: - m_branch_state = NONE; + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } break; - } - }); - m_icount--; + case 0x3d: // C.NGE.S (not greater or equal) + if (f32_lt(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; - } while (m_icount > 0 || m_branch_state); -} + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } + break; + case 0x3e: // C.LE.S (less than or equal) + if (f32_le(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; -void mips1core_device_base::lwl(u32 const op) -{ - offs_t const offset = SIMMVAL + RSVAL; - load<u32>(offset & ~3, [this, op, offset](u32 temp) - { - if (RTREG) - { - unsigned const shift = ((offset & 3) ^ ENDIAN_VALUE_LE_BE(m_endianness, 3, 0)) << 3; + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } + break; + case 0x3f: // C.NGT.S (not greater than) + if (f32_le(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; - RTVAL = (RTVAL & ~u32(0xffffffffU << shift)) | (temp << shift); - } - }); -} + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } + break; -void mips1core_device_base::lwr(u32 const op) -{ - offs_t const offset = SIMMVAL + RSVAL; - load<u32>(offset & ~3, [this, op, offset](u32 temp) - { - if (RTREG) - { - unsigned const shift = ((offset & 0x3) ^ ENDIAN_VALUE_LE_BE(m_endianness, 0, 3)) << 3; + default: // unimplemented operation + m_fcr31 |= FCR31_CE; + execute_set_input(m_fpu_irq, ASSERT_LINE); + break; + } + break; + case 0x11: // D + switch (op & 0x3f) + { + case 0x00: // ADD.D + set_cop1_reg(FDREG >> 1, f64_add(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] }).v); + break; + case 0x01: // SUB.D + set_cop1_reg(FDREG >> 1, f64_sub(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] }).v); + break; + case 0x02: // MUL.D + set_cop1_reg(FDREG >> 1, f64_mul(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] }).v); + break; + case 0x03: // DIV.D + set_cop1_reg(FDREG >> 1, f64_div(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] }).v); + break; - RTVAL = (RTVAL & ~u32(0xffffffffU >> shift)) | (temp >> shift); - } - }); -} + case 0x05: // ABS.D + if (f64_lt(float64_t{ m_f[FSREG >> 1] }, float64_t{ 0 })) + set_cop1_reg(FDREG >> 1, f64_mul(float64_t{ m_f[FSREG >> 1] }, i32_to_f64(-1)).v); + else + set_cop1_reg(FDREG >> 1, m_f[FSREG >> 1]); + break; + case 0x06: // MOV.D + m_f[FDREG >> 1] = m_f[FSREG >> 1]; + break; + case 0x07: // NEG.D + set_cop1_reg(FDREG >> 1, f64_mul(float64_t{ m_f[FSREG >> 1] }, i32_to_f64(-1)).v); + break; -void mips1core_device_base::swl(u32 const op) -{ - offs_t const offset = SIMMVAL + RSVAL; - unsigned const shift = ((offset & 3) ^ ENDIAN_VALUE_LE_BE(m_endianness, 3, 0)) << 3; + case 0x20: // CVT.S.D + set_cop1_reg(FDREG >> 1, f64_to_f32(float64_t{ m_f[FSREG >> 1] }).v); + break; + case 0x24: // CVT.W.D + set_cop1_reg(FDREG >> 1, f64_to_i32(float64_t{ m_f[FSREG >> 1] }, softfloat_roundingMode, true)); + break; - // only load if necessary - if (shift) - { - load<u32>(offset & ~3, [this, op, offset, shift](u32 temp) - { - store<u32>(offset & ~3, (temp & ~u32(0xffffffffU >> shift)) | (RTVAL >> shift)); - }); - } - else - store<u32>(offset & ~3, RTVAL); -} + case 0x30: // C.F.D (false) + m_fcr31 &= ~FCR31_C; + break; + case 0x31: // C.UN.D (unordered) + f64_eq(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] }); + if (softfloat_exceptionFlags & softfloat_flag_invalid) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; + case 0x32: // C.EQ.D (equal) + if (f64_eq(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; + case 0x33: // C.UEQ.D (unordered equal) + if (f64_eq(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; + case 0x34: // C.OLT.D (less than) + if (f64_lt(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; + case 0x35: // C.ULT.D (unordered less than) + if (f64_lt(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; + case 0x36: // C.OLE.D (less than or equal) + if (f64_le(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; + case 0x37: // C.ULE.D (unordered less than or equal) + if (f64_le(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; + case 0x38: // C.SF.D (signalling false) + f64_eq(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] }); -void mips1core_device_base::swr(u32 const op) -{ - offs_t const offset = SIMMVAL + RSVAL; - unsigned const shift = ((offset & 3) ^ ENDIAN_VALUE_LE_BE(m_endianness, 0, 3)) << 3; + m_fcr31 &= ~FCR31_C; - // only load if necessary - if (shift) - { - load<u32>(offset & ~3, [this, op, offset, shift](u32 temp) - { - store<u32>(offset & ~3, (temp & ~u32(0xffffffffU << shift)) | (RTVAL << shift)); - }); - } - else - store<u32>(offset & ~3, RTVAL); -} + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } + break; + case 0x39: // C.NGLE.D (not greater, less than or equal) + f64_eq(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] }); -template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, void> mips1core_device_base::load(u32 program_address, U &&apply) -{ - offs_t translated_address = program_address; + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_C | FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } + else + m_fcr31 &= ~FCR31_C; + break; + case 0x3a: // C.SEQ.D (signalling equal) + if (f64_eq(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; - if (memory_translate(m_data_spacenum, TRANSLATE_READ, translated_address)) - { - switch (sizeof(T)) - { - case 1: apply(T(space(m_data_spacenum).read_byte(translated_address))); break; - case 2: apply(T(space(m_data_spacenum).read_word(translated_address))); break; - case 4: apply(T(space(m_data_spacenum).read_dword(translated_address))); break; - } - } -} + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } + break; + case 0x3b: // C.NGL.D (not greater or less than) + if (f64_eq(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; -template <typename T, typename U> std::enable_if_t<std::is_convertible<U, T>::value, void> mips1core_device_base::store(u32 program_address, U data) -{ - offs_t translated_address = program_address; + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } + break; + case 0x3c: // C.LT.D (less than) + if (f64_lt(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; - if (memory_translate(m_data_spacenum, TRANSLATE_WRITE, translated_address)) - { - switch (sizeof(T)) - { - case 1: space(m_data_spacenum).write_byte(translated_address, T(data)); break; - case 2: space(m_data_spacenum).write_word(translated_address, T(data)); break; - case 4: space(m_data_spacenum).write_dword(translated_address, T(data)); break; - } - } -} + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } + break; + case 0x3d: // C.NGE.D (not greater or equal) + if (f64_lt(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; -bool mips1core_device_base::fetch(u32 program_address, std::function<void(u32)> &&apply) -{ - offs_t translated_address = program_address; + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } + break; + case 0x3e: // C.LE.D (less than or equal) + if (f64_le(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; - if (memory_translate(0, TRANSLATE_FETCH, translated_address)) - { - apply(space(0).read_dword(translated_address)); + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } + break; + case 0x3f: // C.NGT.D (not greater than) + if (f64_le(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; - return true; - } - else - return false; -} + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } + break; -bool mips1core_device_base::memory_translate(int spacenum, int intention, offs_t &address) -{ - // check for kernel memory address - if (BIT(address, 31)) - { - // check debug or kernel mode - if ((intention & TRANSLATE_DEBUG_MASK) || !(SR & SR_KUc)) - { - switch (address & 0xe0000000) + default: // unimplemented operation + m_fcr31 |= FCR31_CE; + execute_set_input(m_fpu_irq, ASSERT_LINE); + break; + } + break; + case 0x14: // W + switch (op & 0x3f) { - case 0x80000000: // kseg0: unmapped, cached, privileged - case 0xa0000000: // kseg1: unmapped, uncached, privileged - address &= ~0xe0000000; + case 0x20: // CVT.S.W + set_cop1_reg(FDREG >> 1, i32_to_f32(s32(m_f[FSREG >> 1])).v); + break; + case 0x21: // CVT.D.W + set_cop1_reg(FDREG >> 1, i32_to_f64(s32(m_f[FSREG >> 1])).v); break; - case 0xc0000000: // kseg2: mapped, cached, privileged - case 0xe0000000: + default: // unimplemented operation + m_fcr31 |= FCR31_CE; + execute_set_input(m_fpu_irq, ASSERT_LINE); break; } + break; + + default: // unimplemented operation + m_fcr31 |= FCR31_CE; + execute_set_input(m_fpu_irq, ASSERT_LINE); + break; } - else if (SR & SR_KUc) + break; + case 0x31: // LWC1 + load<u32>(SIMMVAL + m_r[RSREG], + [this, op](u32 data) { - // exception - m_cpr[0][COP0_BadVAddr] = address; + if (FTREG & 1) + // load the high half of the floating point register + m_f[FTREG >> 1] = (u64(data) << 32) | u32(m_f[FTREG >> 1]); + else + // load the low half of the floating point register + m_f[FTREG >> 1] = (m_f[FTREG >> 1] & ~0xffffffffULL) | data; + }); + break; + case 0x39: // SWC1 + if (FTREG & 1) + // store the high half of the floating point register + store<u32>(SIMMVAL + m_r[RSREG], m_f[FTREG >> 1] >> 32); + else + // store the low half of the floating point register + store<u32>(SIMMVAL + m_r[RSREG], m_f[FTREG >> 1]); + break; + } +} - generate_exception((intention & TRANSLATE_WRITE) ? EXCEPTION_ADDRSTORE : EXCEPTION_ADDRLOAD); - return false; +void mips1_device_base::set_cop1_reg(unsigned const reg, u64 const data) +{ + // translate softfloat exception flags to cause register + if (softfloat_exceptionFlags) + { + if (softfloat_exceptionFlags & softfloat_flag_inexact) + m_fcr31 |= FCR31_CI; + if (softfloat_exceptionFlags & softfloat_flag_underflow) + m_fcr31 |= FCR31_CU; + if (softfloat_exceptionFlags & softfloat_flag_overflow) + m_fcr31 |= FCR31_CO; + if (softfloat_exceptionFlags & softfloat_flag_infinite) + m_fcr31 |= FCR31_CZ; + if (softfloat_exceptionFlags & softfloat_flag_invalid) + m_fcr31 |= FCR31_CV; + + // check if exception is enabled + if (((m_fcr31 & FCR31_CM) >> 5) & (m_fcr31 & FCR31_EM)) + { + execute_set_input(m_fpu_irq, ASSERT_LINE); + return; } + + // set flags + m_fcr31 |= ((m_fcr31 & FCR31_CM) >> 10); } - else - // kuseg physical addresses have a 1GB offset - address += 0x40000000; - return true; + m_f[reg] = data; } bool mips1_device_base::memory_translate(int spacenum, int intention, offs_t &address) @@ -982,21 +1854,30 @@ bool mips1_device_base::memory_translate(int spacenum, int intention, offs_t &ad } else if (SR & SR_KUc) { - // exception - m_cpr[0][COP0_BadVAddr] = address; + if (!machine().side_effects_disabled()) + { + // exception + m_cop0[COP0_BadVAddr] = address; - generate_exception((intention & TRANSLATE_WRITE) ? EXCEPTION_ADDRSTORE : EXCEPTION_ADDRLOAD); + generate_exception((intention & TRANSLATE_WRITE) ? EXCEPTION_ADDRSTORE : EXCEPTION_ADDRLOAD); + } return false; } } // key is a combination of VPN and ASID - u32 const key = (address & EH_VPN) | (m_cpr[0][COP0_EntryHi] & EH_ASID); + u32 const key = (address & EH_VPN) | (m_cop0[COP0_EntryHi] & EH_ASID); + + unsigned *mru = m_tlb_mru[intention & TRANSLATE_TYPE_MASK]; + bool refill = !BIT(address, 31); - bool dirty = false; + bool modify = false; - for (u32 const *entry : m_tlb) + for (unsigned i = 0; i < ARRAY_LENGTH(m_tlb); i++) { + unsigned const index = mru[i]; + u32 const *const entry = m_tlb[index]; + // test vpn and optionally asid u32 const mask = (entry[1] & EL_G) ? EH_VPN : EH_VPN | EH_ASID; if ((entry[0] & mask) != (key & mask)) @@ -1013,28 +1894,39 @@ bool mips1_device_base::memory_translate(int spacenum, int intention, offs_t &ad if ((intention & TRANSLATE_WRITE) && !(entry[1] & EL_D)) { refill = false; - dirty = true; + modify = true; break; } // translate the address address &= ~EH_VPN; address |= (entry[1] & EL_PFN); + + // promote the entry in the mru index + if (i > 0) + std::swap(mru[i - 1], mru[i]); + return true; } - if (!(intention & TRANSLATE_DEBUG_MASK)) + if (!machine().side_effects_disabled() && !(intention & TRANSLATE_DEBUG_MASK)) { - if ((VERBOSE & LOG_TLB) && !dirty) - LOGMASKED(LOG_TLB, "tlb miss %c asid %d address 0x%08x (%s)\n", - (intention & TRANSLATE_WRITE) ? 'w' : 'r', (m_cpr[0][COP0_EntryHi] & EH_ASID) >> 6, address, machine().describe_context()); + if (VERBOSE & LOG_TLB) + { + if (modify) + LOGMASKED(LOG_TLB, "tlb modify asid %d address 0x%08x (%s)\n", + (m_cop0[COP0_EntryHi] & EH_ASID) >> 6, address, machine().describe_context()); + else + LOGMASKED(LOG_TLB, "tlb miss %c asid %d address 0x%08x (%s)\n", + (intention & TRANSLATE_WRITE) ? 'w' : 'r', (m_cop0[COP0_EntryHi] & EH_ASID) >> 6, address, machine().describe_context()); + } // load tlb exception registers - m_cpr[0][COP0_BadVAddr] = address; - m_cpr[0][COP0_EntryHi] = key; - m_cpr[0][COP0_Context] = (m_cpr[0][COP0_Context] & PTE_BASE) | ((address >> 10) & BAD_VPN); + m_cop0[COP0_BadVAddr] = address; + m_cop0[COP0_EntryHi] = key; + m_cop0[COP0_Context] = (m_cop0[COP0_Context] & PTE_BASE) | ((address >> 10) & BAD_VPN); - generate_exception(dirty ? EXCEPTION_TLBMOD : (intention & TRANSLATE_WRITE) ? EXCEPTION_TLBSTORE : EXCEPTION_TLBLOAD, refill); + generate_exception(modify ? EXCEPTION_TLBMOD : (intention & TRANSLATE_WRITE) ? EXCEPTION_TLBSTORE : EXCEPTION_TLBLOAD, refill); } return false; |