summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/mips
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2019-03-26 11:13:37 +1100
committer Vas Crabb <vas@vastheman.com>2019-03-26 11:13:37 +1100
commit97b67170277437131adf6ed4d60139c172529e4f (patch)
tree7a5cbf608f191075f1612b1af15832c206a3fe2d /src/devices/cpu/mips
parentb380514764cf857469bae61c11143a19f79a74c5 (diff)
(nw) Clean up the mess on master
This effectively reverts b380514764cf857469bae61c11143a19f79a74c5 and c24473ddff715ecec2e258a6eb38960cf8c8e98e, restoring the state at 598cd5227223c3b04ca31f0dbc1981256d9ea3ff. Before pushing, please check that what you're about to push is sane. Check your local commit log and ensure there isn't anything out-of-place before pushing to mainline. When things like this happen, it wastes everyone's time. I really don't need this in a week when real work™ is busting my balls and I'm behind where I want to be with preparing for MAME release.
Diffstat (limited to 'src/devices/cpu/mips')
-rw-r--r--src/devices/cpu/mips/mips1.cpp2118
-rw-r--r--src/devices/cpu/mips/mips1.h208
-rw-r--r--src/devices/cpu/mips/mips3.cpp74
-rw-r--r--src/devices/cpu/mips/mips3.h3
-rw-r--r--src/devices/cpu/mips/mips3com.cpp7
-rw-r--r--src/devices/cpu/mips/mips3drc.cpp59
-rw-r--r--src/devices/cpu/mips/mips3fe.cpp4
-rw-r--r--src/devices/cpu/mips/r4000.cpp3067
-rw-r--r--src/devices/cpu/mips/r4000.h463
9 files changed, 5237 insertions, 766 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;
diff --git a/src/devices/cpu/mips/mips1.h b/src/devices/cpu/mips/mips1.h
index d77bdef3cdb..3ae34e4b69f 100644
--- a/src/devices/cpu/mips/mips1.h
+++ b/src/devices/cpu/mips/mips1.h
@@ -9,21 +9,8 @@
class mips1core_device_base : public cpu_device
{
public:
- // floating point coprocessor revision numbers recognised by RISC/os 4.52 and IRIX
- enum fpu_rev_t : u32
- {
- MIPS_R2360 = 0x0100, // MIPS R2360 Floating Point Board
- MIPS_R2010 = 0x0200, // MIPS R2010 VLSI Floating Point Chip
- MIPS_R2010A = 0x0310, // MIPS R2010A VLSI Floating Point Chip
- MIPS_R3010 = 0x0320, // MIPS R3010 VLSI Floating Point Chip
- MIPS_R3010A = 0x0330, // MIPS R3010A VLSI Floating Point Chip
- MIPS_R3010Av4 = 0x0340, // MIPS R3010A VLSI Floating Point Chip
- MIPS_R6010 = 0x0400, // MIPS R6010 Floating Point Chip
- };
-
// device configuration
void set_endianness(endianness_t endianness) { m_endianness = endianness; }
- void set_fpurev(u32 revision) { m_hasfpu = true; m_fpurev = revision; }
// input lines
template <unsigned Coprocessor> auto in_brcond() { return m_in_brcond[Coprocessor].bind(); }
@@ -31,53 +18,40 @@ public:
protected:
mips1core_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);
- enum registers
+ enum registers : unsigned
{
- // general purpose cpu registers
- MIPS1_R0, MIPS1_R1, MIPS1_R2, MIPS1_R3, MIPS1_R4, MIPS1_R5, MIPS1_R6, MIPS1_R7,
- MIPS1_R8, MIPS1_R9, MIPS1_R10, MIPS1_R11, MIPS1_R12, MIPS1_R13, MIPS1_R14, MIPS1_R15,
- MIPS1_R16, MIPS1_R17, MIPS1_R18, MIPS1_R19, MIPS1_R20, MIPS1_R21, MIPS1_R22, MIPS1_R23,
- MIPS1_R24, MIPS1_R25, MIPS1_R26, MIPS1_R27, MIPS1_R28, MIPS1_R29, MIPS1_R30, MIPS1_R31,
+ MIPS1_R0 = 0,
+ MIPS1_COP0 = 32,
+ MIPS1_F0 = 64,
- // other cpu registers
+ MIPS1_PC = 80,
MIPS1_HI,
MIPS1_LO,
- MIPS1_PC,
-
- // coprocessor 0 registers
- MIPS1_COP0_INDEX, // reg 0, tlb only
- MIPS1_COP0_RANDOM, // reg 1, tlb only
- MIPS1_COP0_ENTRYLO, // reg 2, tlb only
- MIPS1_COP0_BUSCTRL, // reg 2, r3041 only
- MIPS1_COP0_CONFIG, // reg 3, r3041/r3071/r3081 only
- MIPS1_COP0_CONTEXT, // reg 4, tlb only
- MIPS1_COP0_BADVADDR, // reg 8
- MIPS1_COP0_COUNT, // reg 9, r3041 only
- MIPS1_COP0_ENTRYHI, // reg 10, tlb only
- MIPS1_COP0_PORTSIZE, // reg 10, r3041 only
- MIPS1_COP0_COMPARE, // reg 11, r3041 only
- MIPS1_COP0_SR, // reg 12
- MIPS1_COP0_CAUSE, // reg 13
- MIPS1_COP0_EPC, // reg 14
- MIPS1_COP0_PRID, // reg 15
+ MIPS1_FCR30,
+ MIPS1_FCR31,
};
- enum exception : int
+ enum exception : u32
{
- EXCEPTION_INTERRUPT = 0,
- EXCEPTION_TLBMOD = 1,
- EXCEPTION_TLBLOAD = 2,
- EXCEPTION_TLBSTORE = 3,
- EXCEPTION_ADDRLOAD = 4,
- EXCEPTION_ADDRSTORE = 5,
- EXCEPTION_BUSINST = 6,
- EXCEPTION_BUSDATA = 7,
- EXCEPTION_SYSCALL = 8,
- EXCEPTION_BREAK = 9,
- EXCEPTION_INVALIDOP = 10,
- EXCEPTION_BADCOP = 11,
- EXCEPTION_OVERFLOW = 12,
- EXCEPTION_TRAP = 13,
+ EXCEPTION_INTERRUPT = 0x00000000,
+ EXCEPTION_TLBMOD = 0x00000004,
+ EXCEPTION_TLBLOAD = 0x00000008,
+ EXCEPTION_TLBSTORE = 0x0000000c,
+ EXCEPTION_ADDRLOAD = 0x00000010,
+ EXCEPTION_ADDRSTORE = 0x00000014,
+ EXCEPTION_BUSINST = 0x00000018,
+ EXCEPTION_BUSDATA = 0x0000001c,
+ EXCEPTION_SYSCALL = 0x00000020,
+ EXCEPTION_BREAK = 0x00000024,
+ EXCEPTION_INVALIDOP = 0x00000028,
+ EXCEPTION_BADCOP = 0x0000002c,
+ EXCEPTION_OVERFLOW = 0x00000030,
+ EXCEPTION_TRAP = 0x00000034,
+
+ EXCEPTION_BADCOP0 = 0x0000002c,
+ EXCEPTION_BADCOP1 = 0x1000002c,
+ EXCEPTION_BADCOP2 = 0x2000002c,
+ EXCEPTION_BADCOP3 = 0x3000002c,
};
enum cop0_reg : u8
@@ -127,6 +101,29 @@ protected:
SR_COP1 = 0x20000000, // coprocessor 1 usable
SR_COP2 = 0x40000000, // coprocessor 2 usable
SR_COP3 = 0x80000000, // coprocessor 3 usable
+
+ SR_KUIE = 0x0000003f, // all interrupt enable and user mode bits
+ SR_KUIEpc = 0x0000000f, // previous and current interrupt enable and user mode bits
+ SR_KUIEop = 0x0000003c, // old and previous interrupt enable and user mode bits
+ SR_IM = 0x0000ff00, // all interrupt mask bits
+ };
+
+ enum cause_mask : u32
+ {
+ CAUSE_EXCCODE = 0x0000007c, // exception code
+ CAUSE_IPSW0 = 0x00000100, // software interrupt 0 pending
+ CAUSE_IPSW1 = 0x00000200, // software interrupt 1 pending
+ CAUSE_IPEX0 = 0x00000400, // external interrupt 0 pending
+ CAUSE_IPEX1 = 0x00000800, // external interrupt 1 pending
+ CAUSE_IPEX2 = 0x00001000, // external interrupt 2 pending
+ CAUSE_IPEX3 = 0x00002000, // external interrupt 3 pending
+ CAUSE_IPEX4 = 0x00004000, // external interrupt 4 pending
+ CAUSE_IPEX5 = 0x00008000, // external interrupt 5 pending
+ CAUSE_IP = 0x0000ff00, // interrupt pending
+ CAUSE_CE = 0x30000000, // co-processor error
+ CAUSE_BD = 0x80000000, // branch delay
+
+ CAUSE_IPEX = 0x0000fc00, // external interrupt pending
};
enum entryhi_mask : u32
@@ -158,7 +155,7 @@ protected:
virtual u32 execute_max_cycles() const override { return 40; }
virtual u32 execute_input_lines() const override { return 6; }
virtual void execute_run() override;
- virtual void execute_set_input(int inputnum, int state) override { set_irq_line(inputnum, state); }
+ virtual void execute_set_input(int inputnum, int state) override;
// device_memory_interface overrides
virtual space_config_vector memory_space_config() const override;
@@ -171,25 +168,18 @@ protected:
void dcache_map(address_map &map);
// interrupts
- void generate_exception(int exception, bool refill = false);
+ void generate_exception(u32 exception, bool refill = false);
void check_irqs();
- void set_irq_line(int irqline, int state);
// cop0
- virtual u32 get_cop0_reg(int const index);
- void set_cop0_reg(int const index, u32 const data);
virtual void handle_cop0(u32 const op);
+ virtual u32 get_cop0_reg(unsigned const reg);
+ virtual void set_cop0_reg(unsigned const reg, u32 const data);
- // cop1
- void set_cop1_creg(int const index, u32 const data);
- void handle_cop1(u32 const op);
-
- // generic coprocessor implementation
- template <unsigned Coprocessor> void handle_cop(u32 const op);
- template <unsigned Coprocessor> u32 get_cop_reg(int const index) { return m_cpr[Coprocessor][index]; }
- template <unsigned Coprocessor> void set_cop_reg(int const index, u32 const data) { m_cpr[Coprocessor][index] = data; }
- template <unsigned Coprocessor> u32 get_cop_creg(int const index) { return m_ccr[Coprocessor][index]; }
- template <unsigned Coprocessor> void set_cop_creg(int const index, u32 const data) { m_ccr[Coprocessor][index] = data; }
+ // other coprocessors
+ virtual void handle_cop1(u32 const op);
+ virtual void handle_cop2(u32 const op);
+ virtual void handle_cop3(u32 const op);
// load/store left/right opcodes
void lwl(u32 const op);
@@ -198,9 +188,13 @@ protected:
void swr(u32 const op);
// memory accessors
- template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, void> load(u32 program_address, U &&apply);
- template <typename T, typename U> std::enable_if_t<std::is_convertible<U, T>::value, void> store(u32 program_address, U data);
- bool fetch(u32 program_address, std::function<void(u32)> &&apply);
+ template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, void> load(u32 address, U &&apply);
+ template <typename T, typename U> std::enable_if_t<std::is_convertible<U, T>::value, void> store(u32 address, U data, T mem_mask = ~T(0));
+ bool fetch(u32 address, std::function<void(u32)> &&apply);
+
+ // debug helpers
+ std::string debug_string(u32 string_pointer, unsigned const limit = 0);
+ std::string debug_string_array(u32 array_pointer);
// address spaces
const address_space_config m_program_config_be;
@@ -212,19 +206,16 @@ protected:
// configuration
u32 m_cpurev;
- bool m_hasfpu;
- u32 m_fpurev;
endianness_t m_endianness;
// core registers
u32 m_pc;
+ u32 m_r[32];
u32 m_hi;
u32 m_lo;
- u32 m_r[32];
- // COP registers
- u32 m_cpr[4][32];
- u32 m_ccr[4][32];
+ // cop0 registers
+ u32 m_cop0[32];
// internal stuff
int m_icount;
@@ -248,9 +239,54 @@ protected:
class mips1_device_base : public mips1core_device_base
{
+public:
+ // floating point coprocessor revision numbers recognised by RISC/os 4.52 and IRIX
+ enum fpu_rev_t : u32
+ {
+ MIPS_R2360 = 0x0100, // MIPS R2360 Floating Point Board
+ MIPS_R2010 = 0x0200, // MIPS R2010 VLSI Floating Point Chip
+ MIPS_R2010A = 0x0310, // MIPS R2010A VLSI Floating Point Chip
+ MIPS_R3010 = 0x0320, // MIPS R3010 VLSI Floating Point Chip
+ MIPS_R3010A = 0x0330, // MIPS R3010A VLSI Floating Point Chip
+ MIPS_R3010Av4 = 0x0340, // MIPS R3010A VLSI Floating Point Chip
+ MIPS_R6010 = 0x0400, // MIPS R6010 Floating Point Chip
+ };
+
+ void set_fpu(u32 revision, unsigned interrupt = 3) { m_fcr0 = revision; m_fpu_irq = interrupt; }
+
protected:
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);
+ enum cp1_fcr31_mask : u32
+ {
+ FCR31_RM = 0x00000003, // rounding mode
+
+ FCR31_FI = 0x00000004, // inexact operation flag
+ FCR31_FU = 0x00000008, // underflow flag
+ FCR31_FO = 0x00000010, // overflow flag
+ FCR31_FZ = 0x00000020, // divide by zero flag
+ FCR31_FV = 0x00000040, // invalid operation flag
+
+ FCR31_EI = 0x00000080, // inexact operation enable
+ FCR31_EU = 0x00000100, // underflow enable
+ FCR31_EO = 0x00000200, // overflow enable
+ FCR31_EZ = 0x00000400, // divide by zero enable
+ FCR31_EV = 0x00000800, // invalid operation enable
+
+ FCR31_CI = 0x00001000, // inexact operation cause
+ FCR31_CU = 0x00002000, // underflow cause
+ FCR31_CO = 0x00004000, // overflow cause
+ FCR31_CZ = 0x00008000, // divide by zero cause
+ FCR31_CV = 0x00010000, // invalid operation cause
+ FCR31_CE = 0x00020000, // unimplemented operation cause
+
+ FCR31_C = 0x00800000, // condition
+
+ FCR31_FM = 0x0000007c, // flag mask
+ FCR31_EM = 0x00000f80, // enable mask
+ FCR31_CM = 0x0001f000, // cause mask (except unimplemented)
+ };
+
// device_t overrides
virtual void device_start() override;
virtual void device_reset() override;
@@ -258,12 +294,24 @@ protected:
// device_memory_interface overrides
virtual bool memory_translate(int spacenum, int intention, offs_t &address) override;
- virtual u32 get_cop0_reg(int idx) override;
virtual void handle_cop0(u32 const op) override;
+ virtual u32 get_cop0_reg(unsigned const reg) override;
+
+ virtual void handle_cop1(u32 const op) override;
+ virtual void set_cop1_reg(unsigned const reg, u64 const data);
private:
u64 m_reset_time;
u32 m_tlb[64][2]; // 0 is hi, 1 is lo
+ unsigned m_tlb_mru[3][64];
+
+ // cop1 registers
+ u64 m_f[16];
+ u32 m_fcr0;
+ u32 m_fcr30;
+ u32 m_fcr31;
+
+ unsigned m_fpu_irq;
};
class r2000_device : public mips1_device_base
@@ -317,13 +365,13 @@ public:
r3052e_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
};
-class r3071_device : public mips1core_device_base
+class r3071_device : public mips1_device_base
{
public:
r3071_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, size_t icache_size = 16384, size_t dcache_size = 4096);
};
-class r3081_device : public mips1core_device_base
+class r3081_device : public mips1_device_base
{
public:
r3081_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, size_t icache_size = 16384, size_t dcache_size = 4096);
diff --git a/src/devices/cpu/mips/mips3.cpp b/src/devices/cpu/mips/mips3.cpp
index 8c8816b8e23..178d81fadbd 100644
--- a/src/devices/cpu/mips/mips3.cpp
+++ b/src/devices/cpu/mips/mips3.cpp
@@ -16,9 +16,10 @@
#include "ps2vu.h"
#include <cmath>
-#define ENABLE_OVERFLOWS (0)
-#define ENABLE_EE_ELF_LOADER (0)
-#define ENABLE_EE_DECI2 (0)
+#define ENABLE_OVERFLOWS (0)
+#define ENABLE_EE_ELF_LOADER (0)
+#define ENABLE_EE_DECI2 (0)
+#define DELAY_SLOT_EXCEPTION_HACK (0)
/***************************************************************************
HELPER MACROS
@@ -160,13 +161,15 @@ mips3_device::mips3_device(const machine_config &mconfig, device_type type, cons
, m_pfnmask(flavor == MIPS3_TYPE_VR4300 ? 0x000fffff : 0x00ffffff)
, m_tlbentries(flavor == MIPS3_TYPE_VR4300 ? 32 : MIPS3_MAX_TLB_ENTRIES)
, m_bigendian(endianness == ENDIANNESS_BIG)
- , m_byte_xor(m_bigendian ? BYTE4_XOR_BE(0) : BYTE4_XOR_LE(0))
- , m_word_xor(m_bigendian ? WORD_XOR_BE(0) : WORD_XOR_LE(0))
+ , m_byte_xor(data_bits == 64 ? (m_bigendian ? BYTE8_XOR_BE(0) : BYTE8_XOR_LE(0)) : (m_bigendian ? BYTE4_XOR_BE(0) : BYTE4_XOR_LE(0)))
+ , m_word_xor(data_bits == 64 ? (m_bigendian ? WORD2_XOR_BE(0) : WORD2_XOR_LE(0)) : (m_bigendian ? WORD_XOR_BE(0) : WORD_XOR_LE(0)))
+ , m_dword_xor(data_bits == 64 ? (m_bigendian ? DWORD_XOR_BE(0) : DWORD_XOR_LE(0)) : 0)
, c_icache_size(0)
, c_dcache_size(0)
+ , c_secondary_cache_line_size(0)
, m_fastram_select(0)
, m_debugger_temp(0)
- , m_drc_cache(DRC_CACHE_SIZE + sizeof(internal_mips3_state) + 0x80000)
+ , m_drc_cache(DRC_CACHE_SIZE + sizeof(internal_mips3_state) + 0x800000)
, m_drcuml(nullptr)
, m_drcfe(nullptr)
, m_drcoptions(0)
@@ -313,7 +316,11 @@ void mips3_device::generate_exception(int exception, int backup)
if (!(SR & SR_EXL))
{
/* if we were in a branch delay slot, adjust */
+#if DELAY_SLOT_EXCEPTION_HACK
+ if (((m_nextpc != ~0) || (m_delayslot)) && (m_ppc == m_core->pc - 4))
+#else
if ((m_nextpc != ~0) || (m_delayslot))
+#endif
{
m_delayslot = false;
m_nextpc = ~0;
@@ -336,6 +343,7 @@ void mips3_device::generate_exception(int exception, int backup)
if ((CAUSE & 0x7f) == 0)
logerror("Took interrupt -- Cause = %08X, PC = %08X\n", (uint32_t)CAUSE, m_core->pc);
*/
+ debugger_exception_hook(exception);
}
@@ -1233,7 +1241,7 @@ inline bool mips3_device::RWORD(offs_t address, uint32_t *result, bool insn)
{
continue;
}
- *result = m_fastram[ramnum].offset_base32[tlbaddress >> 2];
+ *result = m_fastram[ramnum].offset_base32[(tlbaddress ^ m_dword_xor) >> 2];
return true;
}
*result = (*m_memory.read_dword)(*m_program, tlbaddress);
@@ -1403,7 +1411,7 @@ inline void mips3_device::WWORD(offs_t address, uint32_t data)
{
continue;
}
- m_fastram[ramnum].offset_base32[tlbaddress >> 2] = data;
+ m_fastram[ramnum].offset_base32[(tlbaddress ^ m_dword_xor) >> 2] = data;
return;
}
(*m_memory.write_dword)(*m_program, tlbaddress, data);
@@ -1772,7 +1780,7 @@ inline void mips3_device::set_cop0_creg(int idx, uint64_t val)
void mips3_device::handle_cop0(uint32_t op)
{
- if ((SR & SR_KSU_MASK) != SR_KSU_KERNEL && !(SR & SR_COP0))
+ if ((SR & SR_KSU_MASK) != SR_KSU_KERNEL && !(SR & SR_COP0) && !(SR & (SR_EXL | SR_ERL)))
{
m_badcop_value = 0;
generate_exception(EXCEPTION_BADCOP, 1);
@@ -1832,7 +1840,13 @@ void mips3_device::handle_cop0(uint32_t op)
break;
case 0x10: /* RFE */ invalid_instruction(op); break;
- case 0x18: /* ERET */ m_core->pc = m_core->cpr[0][COP0_EPC]; SR &= ~SR_EXL; check_irqs(); m_lld_value ^= 0xffffffff; m_ll_value ^= 0xffffffff; break;
+ case 0x18: /* ERET */
+ m_core->pc = m_core->cpr[0][COP0_EPC];
+ SR &= ~SR_EXL;
+ check_irqs();
+ m_lld_value ^= 0xffffffff;
+ m_ll_value ^= 0xffffffff;
+ break;
case 0x20: /* WAIT */ break;
default: handle_extra_cop0(op); break;
}
@@ -3516,47 +3530,13 @@ void mips3_device::handle_special(uint32_t op)
m_core->icount -= 35;
break;
case 0x1c: /* DMULT */
- {
- uint64_t a_hi = (uint32_t)(RSVAL64 >> 32);
- uint64_t b_hi = (uint32_t)(RTVAL64 >> 32);
- uint64_t a_lo = (uint32_t)RSVAL64;
- uint64_t b_lo = (uint32_t)RTVAL64;
- uint64_t p1 = a_lo * b_lo;
- uint64_t p2 = a_hi * b_lo;
- uint64_t p3 = a_lo * b_hi;
- uint64_t p4 = a_hi * b_hi;
- uint64_t carry = (uint32_t)(((p1 >> 32) + (uint32_t)p2 + (uint32_t)p3) >> 32);
-
- LOVAL64 = p1 + (p2 << 32) + (p3 << 32);
- HIVAL64 = p4 + (p2 >> 32) + (p3 >> 32) + carry;
-
- // Adjust for sign
- if (RSVAL64 < 0)
- HIVAL64 -= RTVAL64;
- if (RTVAL64 < 0)
- HIVAL64 -= RSVAL64;
-
+ LOVAL64 = mul_64x64(RSVAL64, RTVAL64, reinterpret_cast<s64 *>(&HIVAL64));
m_core->icount -= 7;
break;
- }
case 0x1d: /* DMULTU */
- {
- uint64_t a_hi = (uint32_t)(RSVAL64 >> 32);
- uint64_t b_hi = (uint32_t)(RTVAL64 >> 32);
- uint64_t a_lo = (uint32_t)RSVAL64;
- uint64_t b_lo = (uint32_t)RTVAL64;
- uint64_t p1 = a_lo * b_lo;
- uint64_t p2 = a_hi * b_lo;
- uint64_t p3 = a_lo * b_hi;
- uint64_t p4 = a_hi * b_hi;
- uint64_t carry = (uint32_t)(((p1 >> 32) + (uint32_t)p2 + (uint32_t)p3) >> 32);
-
- LOVAL64 = p1 + (p2 << 32) + (p3 << 32);
- HIVAL64 = p4 + (p2 >> 32) + (p3 >> 32) + carry;
-
+ LOVAL64 = mulu_64x64(RSVAL64, RTVAL64, &HIVAL64);
m_core->icount -= 7;
break;
- }
case 0x1e: /* DDIV */
if (RTVAL64)
{
@@ -5500,7 +5480,7 @@ void mips3_device::load_elf()
void r5000be_device::handle_cache(uint32_t op)
{
- if ((SR & SR_KSU_MASK) != SR_KSU_KERNEL && !(SR & SR_COP0))
+ if ((SR & SR_KSU_MASK) != SR_KSU_KERNEL && !(SR & SR_COP0) && !(SR & (SR_EXL | SR_ERL)))
{
m_badcop_value = 0;
generate_exception(EXCEPTION_BADCOP, 1);
diff --git a/src/devices/cpu/mips/mips3.h b/src/devices/cpu/mips/mips3.h
index 8d16c6cbd84..0afa4ef610b 100644
--- a/src/devices/cpu/mips/mips3.h
+++ b/src/devices/cpu/mips/mips3.h
@@ -297,6 +297,7 @@ public:
void set_icache_size(size_t icache_size) { c_icache_size = icache_size; }
void set_dcache_size(size_t dcache_size) { c_dcache_size = dcache_size; }
+ void set_secondary_cache_line_size(uint8_t secondary_cache_line_size) { c_secondary_cache_line_size = secondary_cache_line_size; }
void set_system_clock(uint32_t system_clock) { c_system_clock = system_clock; }
TIMER_CALLBACK_MEMBER(compare_int_callback);
@@ -436,11 +437,13 @@ protected:
bool m_bigendian;
uint32_t m_byte_xor;
uint32_t m_word_xor;
+ uint32_t m_dword_xor;
data_accessors m_memory;
/* cache memory */
size_t c_icache_size;
size_t c_dcache_size;
+ uint8_t c_secondary_cache_line_size;
/* MMU */
mips3_tlb_entry m_tlb[MIPS3_MAX_TLB_ENTRIES];
diff --git a/src/devices/cpu/mips/mips3com.cpp b/src/devices/cpu/mips/mips3com.cpp
index 0cab50ca894..04225d86da7 100644
--- a/src/devices/cpu/mips/mips3com.cpp
+++ b/src/devices/cpu/mips/mips3com.cpp
@@ -273,6 +273,13 @@ uint32_t mips3_device::compute_config_register()
else if (c_icache_size <= 0x40000) configreg |= 6 << 9;
else configreg |= 7 << 9;
+ if (c_secondary_cache_line_size != 0) {
+ configreg &= ~((0xf << 20) | (1 << 17));
+ if (c_secondary_cache_line_size <= 0x10) configreg |= 0 << 22;
+ else if (c_secondary_cache_line_size <= 0x20) configreg |= 1 << 22;
+ else if (c_secondary_cache_line_size <= 0x40) configreg |= 2 << 22;
+ else configreg |= 3 << 22;
+ }
/* set the system clock divider */
int divisor = 2;
if (c_system_clock != 0)
diff --git a/src/devices/cpu/mips/mips3drc.cpp b/src/devices/cpu/mips/mips3drc.cpp
index abedfd59a8d..02439727608 100644
--- a/src/devices/cpu/mips/mips3drc.cpp
+++ b/src/devices/cpu/mips/mips3drc.cpp
@@ -318,8 +318,9 @@ void mips3_device::code_compile_block(uint8_t mode, offs_t pc)
/* 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(mode, seqhead->pc))
+ {
UML_HASH(block, mode, seqhead->pc); // hash mode,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)
@@ -332,8 +333,7 @@ void mips3_device::code_compile_block(uint8_t mode, offs_t pc)
else
{
UML_LABEL(block, seqhead->pc | 0x80000000); // label seqhead->pc | 0x80000000
- UML_HASHJMP(block, m_core->mode, seqhead->pc, *m_nocode);
- // hashjmp <mode>,seqhead->pc,nocode
+ UML_HASHJMP(block, m_core->mode, seqhead->pc, *m_nocode); // hashjmp <mode>,seqhead->pc,nocode
continue;
}
@@ -343,11 +343,15 @@ void mips3_device::code_compile_block(uint8_t mode, offs_t pc)
/* label this instruction, if it may be jumped to locally */
if (seqhead->flags & OPFLAG_IS_BRANCH_TARGET)
+ {
UML_LABEL(block, seqhead->pc | 0x80000000); // label seqhead->pc | 0x80000000
+ }
/* iterate over instructions in the sequence and compile them */
for (curdesc = seqhead; curdesc != seqlast->next(); curdesc = curdesc->next())
+ {
generate_sequence_instruction(block, compiler, curdesc);
+ }
/* if we need to return to the start, do it */
if (seqlast->flags & OPFLAG_RETURN_TO_START)
@@ -362,11 +366,13 @@ void mips3_device::code_compile_block(uint8_t mode, offs_t pc)
/* if the last instruction can change modes, use a variable mode; otherwise, assume the same mode */
if (seqlast->flags & OPFLAG_CAN_CHANGE_MODES)
- UML_HASHJMP(block, mem(&m_core->mode), nextpc, *m_nocode);
- // hashjmp <mode>,nextpc,nocode
+ {
+ UML_HASHJMP(block, mem(&m_core->mode), nextpc, *m_nocode); // hashjmp <mode>,nextpc,nocode
+ }
else if (seqlast->next() == nullptr || seqlast->next()->pc != nextpc)
- UML_HASHJMP(block, m_core->mode, nextpc, *m_nocode);
- // hashjmp <mode>,nextpc,nocode
+ {
+ UML_HASHJMP(block, m_core->mode, nextpc, *m_nocode); // hashjmp <mode>,nextpc,nocode
+ }
}
/* end the sequence */
@@ -1058,7 +1064,6 @@ void mips3_device::generate_update_cycles(drcuml_block &block, compiler_state &c
compiler.cycles = 0;
}
-
/*-------------------------------------------------
generate_checksum_block - generate code to
validate a sequence of opcodes
@@ -1077,15 +1082,17 @@ void mips3_device::generate_checksum_block(drcuml_block &block, compiler_state &
{
uint32_t sum = seqhead->opptr.l[0];
const void *base = m_prptr(seqhead->physpc);
- UML_LOAD(block, I0, base, 0, SIZE_DWORD, SCALE_x4); // load i0,base,0,dword
+ uint32_t low_bits = (seqhead->physpc & (m_data_bits == 64 ? 4 : 0)) ^ m_dword_xor;
+ UML_LOAD(block, I0, base, low_bits, SIZE_DWORD, SCALE_x1); // load i0,base,0,dword
if (seqhead->delay.first() != nullptr
&& !(seqhead->delay.first()->flags & OPFLAG_VIRTUAL_NOOP)
&& seqhead->physpc != seqhead->delay.first()->physpc)
{
+ uint32_t low_bits = (seqhead->delay.first()->physpc & (m_data_bits == 64 ? 4 : 0)) ^ m_dword_xor;
base = m_prptr(seqhead->delay.first()->physpc);
assert(base != nullptr);
- UML_LOAD(block, I1, base, 0, SIZE_DWORD, SCALE_x4); // load i1,base,dword
+ UML_LOAD(block, I1, base, low_bits, SIZE_DWORD, SCALE_x1); // load i1,base,dword
UML_ADD(block, I0, I0, I1); // add i0,i0,i1
sum += seqhead->delay.first()->opptr.l[0];
@@ -1104,21 +1111,24 @@ void mips3_device::generate_checksum_block(drcuml_block &block, compiler_state &
if (!(curdesc->flags & OPFLAG_VIRTUAL_NOOP))
{
const void *base = m_prptr(seqhead->physpc);
- UML_LOAD(block, I0, base, 0, SIZE_DWORD, SCALE_x4); // load i0,base,0,dword
+ UML_LOAD(block, I0, base, m_dword_xor, SIZE_DWORD, SCALE_x1); // load i0,base,0,dword
UML_CMP(block, I0, curdesc->opptr.l[0]); // cmp i0,opptr[0]
UML_EXHc(block, COND_NE, *m_nocode, epc(seqhead)); // exne nocode,seqhead->pc
}
#else
uint32_t sum = 0;
const void *base = m_prptr(seqhead->physpc);
- UML_LOAD(block, I0, base, 0, SIZE_DWORD, SCALE_x4); // load i0,base,0,dword
+ uint32_t low_bits = (seqhead->physpc & (m_data_bits == 64 ? 4 : 0)) ^ m_dword_xor;
+ UML_LOAD(block, I0, base, low_bits, SIZE_DWORD, SCALE_x1); // load i0,base,0,dword
sum += seqhead->opptr.l[0];
for (curdesc = seqhead->next(); curdesc != seqlast->next(); curdesc = curdesc->next())
+ {
if (!(curdesc->flags & OPFLAG_VIRTUAL_NOOP))
{
base = m_prptr(curdesc->physpc);
assert(base != nullptr);
- UML_LOAD(block, I1, base, 0, SIZE_DWORD, SCALE_x4); // load i1,base,dword
+ uint32_t low_bits = (curdesc->physpc & (m_data_bits == 64 ? 4 : 0)) ^ m_dword_xor;
+ UML_LOAD(block, I1, base, low_bits, SIZE_DWORD, SCALE_x1); // load i1,base,dword
UML_ADD(block, I0, I0, I1); // add i0,i0,i1
sum += curdesc->opptr.l[0];
@@ -1128,11 +1138,13 @@ void mips3_device::generate_checksum_block(drcuml_block &block, compiler_state &
{
base = m_prptr(curdesc->delay.first()->physpc);
assert(base != nullptr);
- UML_LOAD(block, I1, base, 0, SIZE_DWORD, SCALE_x4); // load i1,base,dword
+ uint32_t low_bits = (curdesc->delay.first()->physpc & (m_data_bits == 64 ? 4 : 0)) ^ m_dword_xor;
+ UML_LOAD(block, I1, base, low_bits, SIZE_DWORD, SCALE_x1); // load i1,base,dword
UML_ADD(block, I0, I0, I1); // add i0,i0,i1
sum += curdesc->delay.first()->opptr.l[0];
}
}
+ }
UML_CMP(block, I0, sum); // cmp i0,sum
UML_EXHc(block, COND_NE, *m_nocode, epc(seqhead)); // exne nocode,seqhead->pc
#endif
@@ -1291,17 +1303,18 @@ void mips3_device::generate_delay_slot_and_branch(drcuml_block &block, compiler_
{
generate_update_cycles(block, compiler_temp, desc->targetpc, true); // <subtract cycles>
if (desc->flags & OPFLAG_INTRABLOCK_BRANCH)
+ {
UML_JMP(block, desc->targetpc | 0x80000000); // jmp desc->targetpc | 0x80000000
+ }
else
- UML_HASHJMP(block, m_core->mode, desc->targetpc, *m_nocode);
- // hashjmp <mode>,desc->targetpc,nocode
+ {
+ UML_HASHJMP(block, m_core->mode, desc->targetpc, *m_nocode); // hashjmp <mode>,desc->targetpc,nocode
+ }
}
else
{
- generate_update_cycles(block, compiler_temp, uml::mem(&m_core->jmpdest), true);
- // <subtract cycles>
- UML_HASHJMP(block, m_core->mode, mem(&m_core->jmpdest), *m_nocode);
- // hashjmp <mode>,<rsreg>,nocode
+ generate_update_cycles(block, compiler_temp, uml::mem(&m_core->jmpdest), true); // <subtract cycles>
+ UML_HASHJMP(block, m_core->mode, mem(&m_core->jmpdest), *m_nocode); // hashjmp <mode>,<rsreg>,nocode
}
/* update the label */
@@ -2615,10 +2628,8 @@ bool mips3_device::generate_cop0(drcuml_block &block, compiler_state &compiler,
UML_MOV(block, CPR032(COP0_Status), I0); // mov [Status],i0
generate_update_mode(block);
compiler.checkints = true;
- generate_update_cycles(block, compiler, CPR032(COP0_ErrorPC), true);
- // <subtract cycles>
- UML_HASHJMP(block, mem(&m_core->mode), CPR032(COP0_ErrorPC), *m_nocode);
- // hashjmp <mode>,[EPC],nocode
+ generate_update_cycles(block, compiler, CPR032(COP0_ErrorPC), true); // <subtract cycles>
+ UML_HASHJMP(block, mem(&m_core->mode), CPR032(COP0_ErrorPC), *m_nocode); // hashjmp <mode>,[EPC],nocode
return true;
case 0x20: /* WAIT */
diff --git a/src/devices/cpu/mips/mips3fe.cpp b/src/devices/cpu/mips/mips3fe.cpp
index ff0c59797d9..77b72c9b48b 100644
--- a/src/devices/cpu/mips/mips3fe.cpp
+++ b/src/devices/cpu/mips/mips3fe.cpp
@@ -316,13 +316,13 @@ bool mips3_frontend::describe_special(uint32_t op, opcode_desc &desc)
return true;
case 0x10: // MFHI
- desc.regin[0] |= REGFLAG_HI;
+ desc.regin[2] |= REGFLAG_HI;
desc.regout[0] |= REGFLAG_R(RDREG);
return true;
case 0x11: // MTHI
desc.regin[0] |= REGFLAG_R(RSREG);
- desc.regout[0] |= REGFLAG_HI;
+ desc.regout[2] |= REGFLAG_HI;
return true;
case 0x12: // MFLO
diff --git a/src/devices/cpu/mips/r4000.cpp b/src/devices/cpu/mips/r4000.cpp
new file mode 100644
index 00000000000..ca95b107c65
--- /dev/null
+++ b/src/devices/cpu/mips/r4000.cpp
@@ -0,0 +1,3067 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+/*
+ * This is a stripped-down MIPS-III CPU derived from the main mips3 code. Its
+ * primary purpose is to act as a test-bed to aid in debugging MIPS-based
+ * systems, after which the changes/improvements from here are expected to
+ * be back-ported and incorporated into the original mips3 device.
+ *
+ * Because of this specific approach, no attempt is made to support many of the
+ * current features of the mips3 device at this time. Key differences bewteen
+ * this implementation and mips3 include:
+ *
+ * - only supports MIPS R4000/R4400 and QED R4600
+ * - no dynamic recompilation
+ * - reworked address translation logic, including 64-bit modes
+ * - reworked softfloat3-based floating point
+ * - experimental primary instruction cache
+ * - memory tap based ll/sc
+ * - configurable endianness
+ * - it's very very very slow
+ *
+ * TODO
+ * - try to eliminate mode check in address calculations
+ * - find a better way to deal with software interrupts
+ * - enforce mode checks for cp1
+ * - cache instructions
+ * - check/improve instruction timing
+ *
+ */
+
+#include "emu.h"
+#include "debugger.h"
+#include "r4000.h"
+#include "mips3dsm.h"
+
+#include "softfloat3/source/include/softfloat.h"
+
+#define LOG_GENERAL (1U << 0)
+#define LOG_TLB (1U << 1)
+#define LOG_CACHE (1U << 2)
+#define LOG_EXCEPTION (1U << 3)
+#define LOG_SYSCALL (1U << 4)
+#define LOG_STATS (1U << 5)
+
+#define VERBOSE (LOG_GENERAL)
+
+// operating system specific system call logging
+#define SYSCALL_IRIX53 (1U << 0)
+#define SYSCALL_WINNT4 (1U << 1)
+#if VERBOSE & LOG_SYSCALL
+#define SYSCALL_MASK (SYSCALL_IRIX53)
+#else
+#define SYSCALL_MASK (0)
+#endif
+
+// experimental primary instruction cache
+#define ICACHE 0
+
+#include "logmacro.h"
+
+#define USE_ABI_REG_NAMES 1
+
+// cpu instruction fiels
+#define RSREG ((op >> 21) & 31)
+#define RTREG ((op >> 16) & 31)
+#define RDREG ((op >> 11) & 31)
+#define SHIFT ((op >> 6) & 31)
+
+// cop1 instruction fields
+#define FRREG ((op >> 21) & 31)
+#define FTREG ((op >> 16) & 31)
+#define FSREG ((op >> 11) & 31)
+#define FDREG ((op >> 6) & 31)
+
+#define R4000_ENDIAN_LE_BE(le, be) ((m_cp0[CP0_Config] & CONFIG_BE) ? (be) : (le))
+
+// identify odd-numbered cop1 registers
+#define ODD_REGS 0x00010840U
+
+// address computation
+#define ADDR(r, o) (cp0_64() ? ((r) + (o)) : s64(s32((r) + (o))))
+
+#define SR m_cp0[CP0_Status]
+#define CAUSE m_cp0[CP0_Cause]
+
+DEFINE_DEVICE_TYPE(R4000, r4000_device, "r4000", "MIPS R4000")
+DEFINE_DEVICE_TYPE(R4400, r4400_device, "r4400", "MIPS R4400")
+DEFINE_DEVICE_TYPE(R4600, r4600_device, "r4600", "QED R4600")
+
+r4000_base_device::r4000_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u32 prid, cache_size_t icache_size, cache_size_t dcache_size)
+ : cpu_device(mconfig, type, tag, owner, clock)
+ , m_program_config_le("program", ENDIANNESS_LITTLE, 64, 32)
+ , m_program_config_be("program", ENDIANNESS_BIG, 64, 32)
+ , m_ll_watch(nullptr)
+ , m_fcr0(0x00000500U)
+{
+ m_cp0[CP0_PRId] = prid;
+
+ // default configuration
+ m_cp0[CP0_Config] = CONFIG_BE | (icache_size << 9) | (dcache_size << 6);
+}
+
+void r4000_base_device::device_start()
+{
+ // TODO: save state
+
+ state_add(STATE_GENPC, "GENPC", m_pc).noshow();
+ state_add(STATE_GENPCBASE, "CURPC", m_pc).noshow();
+ state_add(MIPS3_PC, "PC", m_pc).formatstr("%016X");
+
+ // exception processing
+ state_add(MIPS3_CP0 + CP0_Status, "SR", m_cp0[CP0_Status]).formatstr("%08X");
+ state_add(MIPS3_CP0 + CP0_EPC, "EPC", m_cp0[CP0_EPC]).formatstr("%016X");
+ state_add(MIPS3_CP0 + CP0_Cause, "Cause", m_cp0[CP0_Cause]).formatstr("%08X");
+ state_add(MIPS3_CP0 + CP0_Context, "Context", m_cp0[CP0_Context]).formatstr("%016X");
+ state_add(MIPS3_CP0 + CP0_BadVAddr, "BadVAddr", m_cp0[CP0_BadVAddr]).formatstr("%016X");
+ state_add(MIPS3_CP0 + CP0_Compare, "Compare", m_cp0[CP0_Compare]).formatstr("%08X");
+ state_add(MIPS3_CP0 + CP0_WatchLo, "WatchLo", m_cp0[CP0_WatchLo]).formatstr("%08X");
+ state_add(MIPS3_CP0 + CP0_WatchHi, "WatchHi", m_cp0[CP0_WatchHi]).formatstr("%08X");
+ state_add(MIPS3_CP0 + CP0_XContext, "XContext", m_cp0[CP0_XContext]).formatstr("%016X");
+
+ // memory management
+ state_add(MIPS3_CP0 + CP0_Index, "Index", m_cp0[CP0_Index]).formatstr("%08X");
+ state_add(MIPS3_CP0 + CP0_EntryLo0, "EntryLo0", m_cp0[CP0_EntryLo0]).formatstr("%016X");
+ state_add(MIPS3_CP0 + CP0_EntryLo1, "EntryLo1", m_cp0[CP0_EntryLo1]).formatstr("%016X");
+ state_add(MIPS3_CP0 + CP0_PageMask, "PageMask", m_cp0[CP0_PageMask]).formatstr("%08X");
+ state_add(MIPS3_CP0 + CP0_Wired, "Wired", m_cp0[CP0_Wired]).formatstr("%08X");
+ state_add(MIPS3_CP0 + CP0_EntryHi, "EntryHi", m_cp0[CP0_EntryHi]).formatstr("%016X");
+ state_add(MIPS3_CP0 + CP0_LLAddr, "LLAddr", m_cp0[CP0_LLAddr]).formatstr("%08X");
+
+#if USE_ABI_REG_NAMES
+ state_add(MIPS3_R0 + 0, "zero", m_r[0]).callimport().formatstr("%016X"); // Can't change R0
+ state_add(MIPS3_R0 + 1, "at", m_r[1]).formatstr("%016X");
+ state_add(MIPS3_R0 + 2, "v0", m_r[2]).formatstr("%016X");
+ state_add(MIPS3_R0 + 3, "v1", m_r[3]).formatstr("%016X");
+ state_add(MIPS3_R0 + 4, "a0", m_r[4]).formatstr("%016X");
+ state_add(MIPS3_R0 + 5, "a1", m_r[5]).formatstr("%016X");
+ state_add(MIPS3_R0 + 6, "a2", m_r[6]).formatstr("%016X");
+ state_add(MIPS3_R0 + 7, "a3", m_r[7]).formatstr("%016X");
+ state_add(MIPS3_R0 + 8, "t0", m_r[8]).formatstr("%016X");
+ state_add(MIPS3_R0 + 9, "t1", m_r[9]).formatstr("%016X");
+ state_add(MIPS3_R0 + 10, "t2", m_r[10]).formatstr("%016X");
+ state_add(MIPS3_R0 + 11, "t3", m_r[11]).formatstr("%016X");
+ state_add(MIPS3_R0 + 12, "t4", m_r[12]).formatstr("%016X");
+ state_add(MIPS3_R0 + 13, "t5", m_r[13]).formatstr("%016X");
+ state_add(MIPS3_R0 + 14, "t6", m_r[14]).formatstr("%016X");
+ state_add(MIPS3_R0 + 15, "t7", m_r[15]).formatstr("%016X");
+ state_add(MIPS3_R0 + 16, "s0", m_r[16]).formatstr("%016X");
+ state_add(MIPS3_R0 + 17, "s1", m_r[17]).formatstr("%016X");
+ state_add(MIPS3_R0 + 18, "s2", m_r[18]).formatstr("%016X");
+ state_add(MIPS3_R0 + 19, "s3", m_r[19]).formatstr("%016X");
+ state_add(MIPS3_R0 + 20, "s4", m_r[20]).formatstr("%016X");
+ state_add(MIPS3_R0 + 21, "s5", m_r[21]).formatstr("%016X");
+ state_add(MIPS3_R0 + 22, "s6", m_r[22]).formatstr("%016X");
+ state_add(MIPS3_R0 + 23, "s7", m_r[23]).formatstr("%016X");
+ state_add(MIPS3_R0 + 24, "t8", m_r[24]).formatstr("%016X");
+ state_add(MIPS3_R0 + 25, "t9", m_r[25]).formatstr("%016X");
+ state_add(MIPS3_R0 + 26, "k0", m_r[26]).formatstr("%016X");
+ state_add(MIPS3_R0 + 27, "k1", m_r[27]).formatstr("%016X");
+ state_add(MIPS3_R0 + 28, "gp", m_r[28]).formatstr("%016X");
+ state_add(MIPS3_R0 + 29, "sp", m_r[29]).formatstr("%016X");
+ state_add(MIPS3_R0 + 30, "fp", m_r[30]).formatstr("%016X");
+ state_add(MIPS3_R0 + 31, "ra", m_r[31]).formatstr("%016X");
+#else
+ state_add(MIPS3_R0, "R0", m_r[0]).callimport().formatstr("%016X");
+ for (unsigned i = 1; i < 32; i++)
+ state_add(MIPS3_R0 + i, util::string_format("R%d", i).c_str(), m_r[i]);
+#endif
+
+ state_add(MIPS3_HI, "HI", m_hi).formatstr("%016X");
+ state_add(MIPS3_LO, "LO", m_lo).formatstr("%016X");
+
+ // floating point registers
+ state_add(MIPS3_FCR30, "FCR30", m_fcr30).formatstr("%08X");
+ state_add(MIPS3_FCR31, "FCR31", m_fcr31).formatstr("%08X");
+ for (unsigned i = 0; i < 32; i++)
+ state_add(MIPS3_F0 + i, util::string_format("F%d", i).c_str(), m_f[i]);
+
+ set_icountptr(m_icount);
+
+ m_cp0_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(r4000_base_device::cp0_timer_callback), this));
+
+ // compute icache line selection mask and allocate tag and data
+ unsigned const config_ic = (m_cp0[CP0_Config] & CONFIG_IC) >> 9;
+
+ m_icache_mask_hi = (0x1000U << config_ic) - 1;
+ m_icache_tag = std::make_unique<u32[]>(0x100U << config_ic);
+ m_icache_data = std::make_unique<u32 []>((0x1000U << config_ic) >> 2);
+}
+
+void r4000_base_device::device_reset()
+{
+ m_branch_state = NONE;
+ m_pc = s64(s32(0xbfc00000));
+
+ m_cp0[CP0_Status] = SR_BEV | SR_ERL;
+ m_cp0[CP0_Wired] = 0;
+ m_cp0[CP0_Compare] = 0;
+ m_cp0[CP0_Count] = 0;
+
+ m_cp0_timer_zero = total_cycles();
+
+ if (m_ll_watch)
+ {
+ m_ll_watch->remove();
+ m_ll_watch = nullptr;
+ }
+
+ m_cp0[CP0_WatchLo] = 0;
+ m_cp0[CP0_WatchHi] = 0;
+
+ // 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;
+ }
+
+ // initialize statistics
+ m_tlb_scans = 0;
+ m_tlb_loops = 0;
+ m_icache_hits = 0;
+ m_icache_misses = 0;
+}
+
+void r4000_base_device::device_stop()
+{
+ if ((m_icache_hits + m_icache_misses) > 0)
+ LOGMASKED(LOG_STATS, "icache hit ratio %.3f%% (%d hits %d misses)\n",
+ double(m_icache_hits) / double(m_icache_hits + m_icache_misses) * 100.0, m_icache_hits, m_icache_misses);
+
+ if (m_tlb_scans > 0)
+ LOGMASKED(LOG_STATS, "tlb scans %d loops %d average %.3f loops per scan\n", m_tlb_scans, m_tlb_loops, double(m_tlb_loops) / double(m_tlb_scans));
+}
+
+device_memory_interface::space_config_vector r4000_base_device::memory_space_config() const
+{
+ return space_config_vector{
+ std::make_pair(AS_PROGRAM, R4000_ENDIAN_LE_BE(&m_program_config_le, &m_program_config_be))
+ };
+}
+
+bool r4000_base_device::memory_translate(int spacenum, int intention, offs_t &address)
+{
+ // FIXME: address truncation
+ u64 placeholder = s32(address);
+
+ translate_t const t = translate(intention, placeholder);
+
+ if (t == ERROR || t == MISS)
+ return false;
+
+ address = placeholder;
+ return true;
+}
+
+std::unique_ptr<util::disasm_interface> r4000_base_device::create_disassembler()
+{
+ return std::make_unique<mips3_disassembler>();
+}
+
+void r4000_base_device::execute_run()
+{
+ // check interrupts
+ if ((CAUSE & SR & CAUSE_IP) && (SR & SR_IE) && !(SR & (SR_EXL | SR_ERL)))
+ cpu_exception(EXCEPTION_INT);
+
+ while (m_icount > 0)
+ {
+ debugger_instruction_hook(m_pc);
+
+ fetch(m_pc,
+ [this](u32 const op)
+ {
+ cpu_execute(op);
+
+ // zero register zero
+ m_r[0] = 0;
+ });
+
+ // 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;
+
+ case NULLIFY:
+ m_branch_state = NONE;
+ m_pc += 8;
+ break;
+ }
+
+ m_icount--;
+ }
+}
+
+void r4000_base_device::execute_set_input(int inputnum, int state)
+{
+ if (state)
+ m_cp0[CP0_Cause] |= (CAUSE_IPEX0 << inputnum);
+ else
+ m_cp0[CP0_Cause] &= ~(CAUSE_IPEX0 << inputnum);
+}
+
+void r4000_base_device::cpu_execute(u32 const op)
+{
+ switch (op >> 26)
+ {
+ case 0x00: // SPECIAL
+ switch (op & 0x3f)
+ {
+ case 0x00: // SLL
+ m_r[RDREG] = s64(s32(m_r[RTREG] << SHIFT));
+ break;
+ //case 0x01: // *
+ case 0x02: // SRL
+ m_r[RDREG] = s64(s32(u32(m_r[RTREG]) >> SHIFT));
+ break;
+ case 0x03: // SRA
+ m_r[RDREG] = s64(s32(m_r[RTREG]) >> SHIFT);
+ break;
+ case 0x04: // SLLV
+ m_r[RDREG] = s64(s32(m_r[RTREG] << (m_r[RSREG] & 31)));
+ break;
+ //case 0x05: // *
+ case 0x06: // SRLV
+ m_r[RDREG] = s64(s32(u32(m_r[RTREG]) >> (m_r[RSREG] & 31)));
+ break;
+ case 0x07: // SRAV
+ m_r[RDREG] = s64(s32(m_r[RTREG]) >> (m_r[RSREG] & 31));
+ break;
+ case 0x08: // JR
+ m_branch_state = BRANCH;
+ m_branch_target = ADDR(m_r[RSREG], 0);
+ break;
+ case 0x09: // JALR
+ m_branch_state = BRANCH;
+ m_branch_target = ADDR(m_r[RSREG], 0);
+ m_r[RDREG] = ADDR(m_pc, 8);
+ break;
+ //case 0x0a: // *
+ //case 0x0b: // *
+ case 0x0c: // SYSCALL
+ if (VERBOSE & LOG_SYSCALL)
+ {
+ if (SYSCALL_MASK & SYSCALL_IRIX53)
+ {
+ switch (m_r[2])
+ {
+ case 0x3e9: // 1001 = exit
+ LOGMASKED(LOG_SYSCALL, "exit(%d) (%s)\n", m_r[4], machine().describe_context());
+ break;
+
+ case 0x3ea: // 1002 = fork
+ LOGMASKED(LOG_SYSCALL, "fork() (%s)\n", machine().describe_context());
+ break;
+
+ case 0x3eb: // 1003 = read
+ LOGMASKED(LOG_SYSCALL, "read(%d, 0x%x, %d) (%s)\n", m_r[4], m_r[5], m_r[6], machine().describe_context());
+ break;
+
+ case 0x3ec: // 1004 = write
+ LOGMASKED(LOG_SYSCALL, "write(%d, 0x%x, %d) (%s)\n", m_r[4], m_r[5], m_r[6], machine().describe_context());
+ if (m_r[4] == 1 || m_r[4] == 2)
+ printf("%s", debug_string(m_r[5], m_r[6]).c_str());
+ break;
+
+ case 0x3ed: // 1005 = open
+ LOGMASKED(LOG_SYSCALL, "open(\"%s\", %#o) (%s)\n", debug_string(m_r[4]), m_r[5], machine().describe_context());
+ break;
+
+ case 0x3ee: // 1006 = close
+ LOGMASKED(LOG_SYSCALL, "close(%d) (%s)\n", m_r[4], machine().describe_context());
+ break;
+
+ case 0x3ef: // 1007 = creat
+ LOGMASKED(LOG_SYSCALL, "creat(\"%s\", %#o) (%s)\n", debug_string(m_r[4]), m_r[5], machine().describe_context());
+ break;
+
+ case 0x423: // 1059 = exece
+ LOGMASKED(LOG_SYSCALL, "exece(\"%s\", [ %s ], [ %s ]) (%s)\n", debug_string(m_r[4]), debug_string_array(m_r[5]), debug_string_array(m_r[6]), machine().describe_context());
+ break;
+
+ default:
+ LOGMASKED(LOG_SYSCALL, "syscall 0x%x (%s)\n", m_r[2], machine().describe_context());
+ break;
+ }
+ }
+ else if (SYSCALL_MASK & SYSCALL_WINNT4)
+ {
+ switch (m_r[2])
+ {
+ case 0x4f:
+ load<s32>(m_r[7] + 8,
+ [this](s64 string_pointer)
+ {
+ LOGMASKED(LOG_SYSCALL, "NtOpenFile(%s) (%s)\n", debug_string(string_pointer), machine().describe_context());
+ });
+ break;
+
+ default:
+ LOGMASKED(LOG_SYSCALL, "syscall 0x%x (%s)\n", m_r[2], machine().describe_context());
+ break;
+ }
+ }
+ }
+ cpu_exception(EXCEPTION_SYS);
+ break;
+ case 0x0d: // BREAK
+ cpu_exception(EXCEPTION_BP);
+ break;
+ //case 0x0e: // *
+ case 0x0f: // SYNC
+ 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 0x14: // DSLLV
+ m_r[RDREG] = m_r[RTREG] << (m_r[RSREG] & 63);
+ break;
+ //case 0x15: // *
+ case 0x16: // DSRLV
+ m_r[RDREG] = m_r[RTREG] >> (m_r[RSREG] & 63);
+ break;
+ case 0x17: // DSRAV
+ m_r[RDREG] = s64(m_r[RTREG]) >> (m_r[RSREG] & 63);
+ break;
+ case 0x18: // MULT
+ {
+ u64 const product = mul_32x32(s32(m_r[RSREG]), s32(m_r[RTREG]));
+
+ m_lo = s64(s32(product));
+ m_hi = s64(s32(product >> 32));
+ m_icount -= 3;
+ }
+ break;
+ case 0x19: // MULTU
+ {
+ u64 const product = mulu_32x32(u32(m_r[RSREG]), u32(m_r[RTREG]));
+
+ m_lo = s64(s32(product));
+ m_hi = s64(s32(product >> 32));
+ m_icount -= 3;
+ }
+ break;
+ case 0x1a: // DIV
+ if (m_r[RTREG])
+ {
+ m_lo = s64(s32(m_r[RSREG]) / s32(m_r[RTREG]));
+ m_hi = s64(s32(m_r[RSREG]) % s32(m_r[RTREG]));
+ }
+ m_icount -= 35;
+ break;
+ case 0x1b: // DIVU
+ if (m_r[RTREG])
+ {
+ m_lo = s64(s32(u32(m_r[RSREG]) / u32(m_r[RTREG])));
+ m_hi = s64(s32(u32(m_r[RSREG]) % u32(m_r[RTREG])));
+ }
+ m_icount -= 35;
+ break;
+ case 0x1c: // DMULT
+ m_lo = mul_64x64(m_r[RSREG], m_r[RTREG], reinterpret_cast<s64 *>(&m_hi));
+ m_icount -= 7;
+ break;
+ case 0x1d: // DMULTU
+ m_lo = mulu_64x64(m_r[RSREG], m_r[RTREG], &m_hi);
+ m_icount -= 7;
+ break;
+ case 0x1e: // DDIV
+ if (m_r[RTREG])
+ {
+ m_lo = s64(m_r[RSREG]) / s64(m_r[RTREG]);
+ m_hi = s64(m_r[RSREG]) % s64(m_r[RTREG]);
+ }
+ m_icount -= 67;
+ break;
+ case 0x1f: // DDIVU
+ if (m_r[RTREG])
+ {
+ m_lo = m_r[RSREG] / m_r[RTREG];
+ m_hi = m_r[RSREG] % m_r[RTREG];
+ }
+ m_icount -= 67;
+ break;
+ case 0x20: // ADD
+ {
+ u32 const sum = u32(m_r[RSREG]) + u32(m_r[RTREG]);
+
+ // overflow: (sign(addend0) == sign(addend1)) && (sign(addend0) != sign(sum))
+ if (!BIT(u32(m_r[RSREG]) ^ u32(m_r[RTREG]), 31) && BIT(u32(m_r[RSREG]) ^ sum, 31))
+ cpu_exception(EXCEPTION_OV);
+ else
+ m_r[RDREG] = s64(s32(sum));
+ }
+ break;
+ case 0x21: // ADDU
+ m_r[RDREG] = s64(s32(u32(m_r[RSREG]) + u32(m_r[RTREG])));
+ break;
+ case 0x22: // SUB
+ {
+ u32 const difference = u32(m_r[RSREG]) - u32(m_r[RTREG]);
+
+ // overflow: (sign(minuend) != sign(subtrahend)) && (sign(minuend) != sign(difference))
+ if (BIT(u32(m_r[RSREG]) ^ u32(m_r[RTREG]), 31) && BIT(u32(m_r[RSREG]) ^ difference, 31))
+ cpu_exception(EXCEPTION_OV);
+ else
+ m_r[RDREG] = s64(s32(difference));
+ }
+ break;
+ case 0x23: // SUBU
+ m_r[RDREG] = s64(s32(u32(m_r[RSREG]) - u32(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 0x28: // *
+ //case 0x29: // *
+ case 0x2a: // SLT
+ m_r[RDREG] = s64(m_r[RSREG]) < s64(m_r[RTREG]);
+ break;
+ case 0x2b: // SLTU
+ m_r[RDREG] = m_r[RSREG] < m_r[RTREG];
+ break;
+ case 0x2c: // DADD
+ {
+ u64 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], 63) && BIT(m_r[RSREG] ^ sum, 63))
+ cpu_exception(EXCEPTION_OV);
+ else
+ m_r[RDREG] = sum;
+ }
+ break;
+ case 0x2d: // DADDU
+ m_r[RDREG] = m_r[RSREG] + m_r[RTREG];
+ break;
+ case 0x2e: // DSUB
+ {
+ u64 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], 63) && BIT(m_r[RSREG] ^ difference, 63))
+ cpu_exception(EXCEPTION_OV);
+ else
+ m_r[RDREG] = difference;
+ }
+ break;
+ case 0x2f: // DSUBU
+ m_r[RDREG] = m_r[RSREG] - m_r[RTREG];
+ break;
+ case 0x30: // TGE
+ if (s64(m_r[RSREG]) >= s64(m_r[RTREG]))
+ cpu_exception(EXCEPTION_TR);
+ break;
+ case 0x31: // TGEU
+ if (m_r[RSREG] >= m_r[RTREG])
+ cpu_exception(EXCEPTION_TR);
+ break;
+ case 0x32: // TLT
+ if (s64(m_r[RSREG]) < s64(m_r[RTREG]))
+ cpu_exception(EXCEPTION_TR);
+ break;
+ case 0x33: // TLTU
+ if (m_r[RSREG] < m_r[RTREG])
+ cpu_exception(EXCEPTION_TR);
+ break;
+ case 0x34: // TEQ
+ if (m_r[RSREG] == m_r[RTREG])
+ cpu_exception(EXCEPTION_TR);
+ break;
+ //case 0x35: // *
+ case 0x36: // TNE
+ if (m_r[RSREG] != m_r[RTREG])
+ cpu_exception(EXCEPTION_TR);
+ break;
+ //case 0x37: // *
+ case 0x38: // DSLL
+ m_r[RDREG] = m_r[RTREG] << SHIFT;
+ break;
+ //case 0x39: // *
+ case 0x3a: // DSRL
+ m_r[RDREG] = m_r[RTREG] >> SHIFT;
+ break;
+ case 0x3b: // DSRA
+ m_r[RDREG] = s64(m_r[RTREG]) >> SHIFT;
+ break;
+ case 0x3c: // DSLL32
+ m_r[RDREG] = m_r[RTREG] << (SHIFT + 32);
+ break;
+ //case 0x3d: // *
+ case 0x3e: // DSRL32
+ m_r[RDREG] = m_r[RTREG] >> (SHIFT + 32);
+ break;
+ case 0x3f: // DSRA32
+ m_r[RDREG] = s64(m_r[RTREG]) >> (SHIFT + 32);
+ break;
+
+ default:
+ // * Operation codes marked with an asterisk cause reserved
+ // instruction exceptions in all current implementations and are
+ // reserved for future versions of the architecture.
+ cpu_exception(EXCEPTION_RI);
+ break;
+ }
+ break;
+ case 0x01: // REGIMM
+ switch ((op >> 16) & 0x1f)
+ {
+ case 0x00: // BLTZ
+ if (s64(m_r[RSREG]) < 0)
+ {
+ m_branch_state = BRANCH;
+ m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
+ }
+ break;
+ case 0x01: // BGEZ
+ if (s64(m_r[RSREG]) >= 0)
+ {
+ m_branch_state = BRANCH;
+ m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
+ }
+ break;
+ case 0x02: // BLTZL
+ if (s64(m_r[RSREG]) < 0)
+ {
+ m_branch_state = BRANCH;
+ m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
+ }
+ else
+ m_branch_state = NULLIFY;
+ break;
+ case 0x03: // BGEZL
+ if (s64(m_r[RSREG]) >= 0)
+ {
+ m_branch_state = BRANCH;
+ m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
+ }
+ else
+ m_branch_state = NULLIFY;
+ break;
+ //case 0x04: // *
+ //case 0x05: // *
+ //case 0x06: // *
+ //case 0x07: // *
+ case 0x08: // TGEI
+ if (s64(m_r[RSREG]) >= s16(op))
+ cpu_exception(EXCEPTION_TR);
+ break;
+ case 0x09: // TGEIU
+ if (m_r[RSREG] >= u64(s64(s16(op))))
+ cpu_exception(EXCEPTION_TR);
+ break;
+ case 0x0a: // TLTI
+ if (s64(m_r[RSREG]) < s16(op))
+ cpu_exception(EXCEPTION_TR);
+ break;
+ case 0x0b: // TLTIU
+ if (m_r[RSREG] >= u64(s64(s16(op))))
+ cpu_exception(EXCEPTION_TR);
+ break;
+ case 0x0c: // TEQI
+ if (m_r[RSREG] == u64(s64(s16(op))))
+ cpu_exception(EXCEPTION_TR);
+ break;
+ //case 0x0d: // *
+ case 0x0e: // TNEI
+ if (m_r[RSREG] != u64(s64(s16(op))))
+ cpu_exception(EXCEPTION_TR);
+ break;
+ //case 0x0f: // *
+ case 0x10: // BLTZAL
+ if (s64(m_r[RSREG]) < 0)
+ {
+ m_branch_state = BRANCH;
+ m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
+ }
+ m_r[31] = ADDR(m_pc, 8);
+ break;
+ case 0x11: // BGEZAL
+ if (s64(m_r[RSREG]) >= 0)
+ {
+ m_branch_state = BRANCH;
+ m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
+ }
+ m_r[31] = ADDR(m_pc, 8);
+ break;
+ case 0x12: // BLTZALL
+ if (s64(m_r[RSREG]) < 0)
+ {
+ m_branch_state = BRANCH;
+ m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
+ }
+ else
+ m_branch_state = NULLIFY;
+ m_r[31] = ADDR(m_pc, 8);
+ break;
+ case 0x13: // BGEZALL
+ if (s64(m_r[RSREG]) >= 0)
+ {
+ m_branch_state = BRANCH;
+ m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
+ }
+ else
+ m_branch_state = NULLIFY;
+ m_r[31] = ADDR(m_pc, 8);
+ break;
+ //case 0x14: // *
+ //case 0x15: // *
+ //case 0x16: // *
+ //case 0x17: // *
+ //case 0x18: // *
+ //case 0x19: // *
+ //case 0x1a: // *
+ //case 0x1b: // *
+ //case 0x1c: // *
+ //case 0x1d: // *
+ //case 0x1e: // *
+ //case 0x1f: // *
+
+ default:
+ // * Operation codes marked with an asterisk cause reserved
+ // instruction exceptions in all current implementations and are
+ // reserved for future versions of the architecture.
+ cpu_exception(EXCEPTION_RI);
+ break;
+ }
+ break;
+ case 0x02: // J
+ m_branch_state = BRANCH;
+ m_branch_target = (ADDR(m_pc, 4) & ~0x0fffffffULL) | ((op & 0x03ffffffU) << 2);
+ break;
+ case 0x03: // JAL
+ m_branch_state = BRANCH;
+ m_branch_target = (ADDR(m_pc, 4) & ~0x0fffffffULL) | ((op & 0x03ffffffU) << 2);
+ m_r[31] = ADDR(m_pc, 8);
+ break;
+ case 0x04: // BEQ
+ if (m_r[RSREG] == m_r[RTREG])
+ {
+ m_branch_state = BRANCH;
+ m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
+ }
+ break;
+ case 0x05: // BNE
+ if (m_r[RSREG] != m_r[RTREG])
+ {
+ m_branch_state = BRANCH;
+ m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
+ }
+ break;
+ case 0x06: // BLEZ
+ if (s64(m_r[RSREG]) <= 0)
+ {
+ m_branch_state = BRANCH;
+ m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
+ }
+ break;
+ case 0x07: // BGTZ
+ if (s64(m_r[RSREG]) > 0)
+ {
+ m_branch_state = BRANCH;
+ m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
+ }
+ break;
+ case 0x08: // ADDI
+ {
+ u32 const sum = u32(m_r[RSREG]) + s16(op);
+
+ // overflow: (sign(addend0) == sign(addend1)) && (sign(addend0) != sign(sum))
+ if (!BIT(u32(m_r[RSREG]) ^ s32(s16(op)), 31) && BIT(u32(m_r[RSREG]) ^ sum, 31))
+ cpu_exception(EXCEPTION_OV);
+ else
+ m_r[RTREG] = s64(s32(sum));
+ }
+ break;
+ case 0x09: // ADDIU
+ m_r[RTREG] = s64(s32(u32(m_r[RSREG]) + s16(op)));
+ break;
+ case 0x0a: // SLTI
+ m_r[RTREG] = s64(m_r[RSREG]) < s64(s16(op));
+ break;
+ case 0x0b: // SLTIU
+ m_r[RTREG] = m_r[RSREG] < u64(s64(s16(op)));
+ break;
+ case 0x0c: // ANDI
+ m_r[RTREG] = m_r[RSREG] & u16(op);
+ break;
+ case 0x0d: // ORI
+ m_r[RTREG] = m_r[RSREG] | u16(op);
+ break;
+ case 0x0e: // XORI
+ m_r[RTREG] = m_r[RSREG] ^ u16(op);
+ break;
+ case 0x0f: // LUI
+ m_r[RTREG] = s64(s16(op)) << 16;
+ break;
+ case 0x10: // COP0
+ cp0_execute(op);
+ break;
+ case 0x11: // COP1
+ cp1_execute(op);
+ break;
+ case 0x12: // COP2
+ cp2_execute(op);
+ break;
+ //case 0x13: // *
+ case 0x14: // BEQL
+ if (m_r[RSREG] == m_r[RTREG])
+ {
+ m_branch_state = BRANCH;
+ m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
+ }
+ else
+ m_branch_state = NULLIFY;
+ break;
+ case 0x15: // BNEL
+ if (m_r[RSREG] != m_r[RTREG])
+ {
+ m_branch_state = BRANCH;
+ m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
+ }
+ else
+ m_branch_state = NULLIFY;
+ break;
+ case 0x16: // BLEZL
+ if (s64(m_r[RSREG]) <= 0)
+ {
+ m_branch_state = BRANCH;
+ m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
+ }
+ else
+ m_branch_state = NULLIFY;
+ break;
+ case 0x17: // BGTZL
+ if (s64(m_r[RSREG]) > 0)
+ {
+ m_branch_state = BRANCH;
+ m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
+ }
+ else
+ m_branch_state = NULLIFY;
+ break;
+ case 0x18: // DADDI
+ {
+ u64 const sum = m_r[RSREG] + s64(s16(op));
+
+ // overflow: (sign(addend0) == sign(addend1)) && (sign(addend0) != sign(sum))
+ if (!BIT(m_r[RSREG] ^ s64(s16(op)), 63) && BIT(m_r[RSREG] ^ sum, 63))
+ cpu_exception(EXCEPTION_OV);
+ else
+ m_r[RTREG] = sum;
+ }
+ break;
+ case 0x19: // DADDIU
+ m_r[RTREG] = m_r[RSREG] + s64(s16(op));
+ break;
+ case 0x1a: // LDL
+ if (!(SR & SR_KSU) || (SR & (SR_EXL | SR_ERL)) || cp0_64())
+ cpu_ldl(op);
+ else
+ cpu_exception(EXCEPTION_RI);
+ break;
+ case 0x1b: // LDR
+ if (!(SR & SR_KSU) || (SR & (SR_EXL | SR_ERL)) || cp0_64())
+ cpu_ldr(op);
+ else
+ cpu_exception(EXCEPTION_RI);
+ break;
+ //case 0x1c: // *
+ //case 0x1d: // *
+ //case 0x1e: // *
+ //case 0x1f: // *
+ case 0x20: // LB
+ load<s8>(ADDR(m_r[RSREG], s16(op)),
+ [this, op](s8 data)
+ {
+ m_r[RTREG] = data;
+ });
+ break;
+ case 0x21: // LH
+ load<s16>(ADDR(m_r[RSREG], s16(op)),
+ [this, op](s16 data)
+ {
+ m_r[RTREG] = data;
+ });
+ break;
+ case 0x22: // LWL
+ cpu_lwl(op);
+ break;
+ case 0x23: // LW
+ load<s32>(ADDR(m_r[RSREG], s16(op)),
+ [this, op](s32 data)
+ {
+ m_r[RTREG] = data;
+ });
+ break;
+ case 0x24: // LBU
+ load<s8>(ADDR(m_r[RSREG], s16(op)),
+ [this, op](u8 data)
+ {
+ m_r[RTREG] = data;
+ });
+ break;
+ case 0x25: // LHU
+ load<u16>(ADDR(m_r[RSREG], s16(op)),
+ [this, op](u16 data)
+ {
+ m_r[RTREG] = data;
+ });
+ break;
+ case 0x26: // LWR
+ cpu_lwr(op);
+ break;
+ case 0x27: // LWU
+ load<u32>(ADDR(m_r[RSREG], s16(op)),
+ [this, op](u32 data)
+ {
+ m_r[RTREG] = data;
+ });
+ break;
+ case 0x28: // SB
+ store<u8>(ADDR(m_r[RSREG], s16(op)), u8(m_r[RTREG]));
+ break;
+ case 0x29: // SH
+ store<u16>(ADDR(m_r[RSREG], s16(op)), u16(m_r[RTREG]));
+ break;
+ case 0x2a: // SWL
+ cpu_swl(op);
+ break;
+ case 0x2b: // SW
+ store<u32>(ADDR(m_r[RSREG], s16(op)), u32(m_r[RTREG]));
+ break;
+ case 0x2c: // SDL
+ if (!(SR & SR_KSU) || (SR & (SR_EXL | SR_ERL)) || cp0_64())
+ cpu_sdl(op);
+ else
+ cpu_exception(EXCEPTION_RI);
+ break;
+ case 0x2d: // SDR
+ if (!(SR & SR_KSU) || (SR & (SR_EXL | SR_ERL)) || cp0_64())
+ cpu_sdr(op);
+ else
+ cpu_exception(EXCEPTION_RI);
+ break;
+ case 0x2e: // SWR
+ cpu_swr(op);
+ break;
+ case 0x2f: // CACHE
+ if ((SR & SR_KSU) && !(SR & SR_CU0) && !(SR & (SR_EXL | SR_ERL)))
+ {
+ cpu_exception(EXCEPTION_CP0);
+ break;
+ }
+
+ switch ((op >> 16) & 0x1f)
+ {
+ case 0x00: // index invalidate (I)
+ if (ICACHE)
+ {
+ m_icache_tag[(ADDR(m_r[RSREG], s16(op)) & m_icache_mask_hi) >> m_icache_shift] &= ~ICACHE_V;
+ break;
+ }
+
+ case 0x04: // index load tag (I)
+ if (ICACHE)
+ {
+ u32 const tag = m_icache_tag[(ADDR(m_r[RSREG], s16(op)) & m_icache_mask_hi) >> m_icache_shift];
+
+ m_cp0[CP0_TagLo] = ((tag & ICACHE_PTAG) << 8) | ((tag & ICACHE_V) >> 18) | ((tag & ICACHE_P) >> 25);
+ m_cp0[CP0_ECC] = 0; // data ecc or parity
+
+ break;
+ }
+
+ case 0x08: // index store tag (I)
+ if (ICACHE)
+ {
+ // FIXME: compute parity
+ m_icache_tag[(ADDR(m_r[RSREG], s16(op)) & m_icache_mask_hi) >> m_icache_shift] =
+ (m_cp0[CP0_TagLo] & TAGLO_PTAGLO) >> 8 | (m_cp0[CP0_TagLo] & TAGLO_PSTATE) << 18;
+
+ break;
+ }
+
+ case 0x01: // index writeback invalidate (D)
+ case 0x02: // index invalidate (SI)
+ case 0x03: // index writeback invalidate (SD)
+
+ case 0x05: // index load tag (D)
+ case 0x06: // index load tag (SI)
+ case 0x07: // index load tag (SI)
+
+ case 0x09: // index store tag (D)
+ case 0x0a: // index store tag (SI)
+ case 0x0b: // index store tag (SD)
+
+ case 0x0d: // create dirty exclusive (D)
+ case 0x0f: // create dirty exclusive (SD)
+
+ case 0x10: // hit invalidate (I)
+ case 0x11: // hit invalidate (D)
+ case 0x12: // hit invalidate (SI)
+ case 0x13: // hit invalidate (SD)
+
+ case 0x14: // fill (I)
+ case 0x15: // hit writeback invalidate (D)
+ case 0x17: // hit writeback invalidate (SD)
+
+ case 0x18: // hit writeback (I)
+ case 0x19: // hit writeback (D)
+ case 0x1b: // hit writeback (SD)
+
+ case 0x1e: // hit set virtual (SI)
+ case 0x1f: // hit set virtual (SD)
+ //LOGMASKED(LOG_CACHE, "cache 0x%08x unimplemented (%s)\n", op, machine().describe_context());
+ break;
+ }
+ break;
+ case 0x30: // LL
+ load_linked<s32>(ADDR(m_r[RSREG], s16(op)),
+ [this, op](u64 address, s32 data)
+ {
+ // remove existing tap
+ if (m_ll_watch)
+ m_ll_watch->remove();
+
+ m_r[RTREG] = data;
+ m_cp0[CP0_LLAddr] = u32(address >> 4);
+
+ // install write tap
+ // FIXME: physical address truncation
+ m_ll_watch = space(0).install_write_tap(offs_t(address & ~7), offs_t(address | 7), "ll",
+ [this, hi(bool(BIT(address, 2)))](offs_t offset, u64 &data, u64 mem_mask)
+ {
+ if (hi ? ACCESSING_BITS_32_63 : ACCESSING_BITS_0_31)
+ {
+ m_ll_watch->remove();
+ m_ll_watch = nullptr;
+ }
+ });
+ });
+ break;
+ case 0x31: // LWC1
+ cp1_execute(op);
+ break;
+ case 0x32: // LWC2
+ cp2_execute(op);
+ break;
+ //case 0x33: // *
+ case 0x34: // LLD
+ load_linked<u64>(ADDR(m_r[RSREG], s16(op)),
+ [this, op](u64 address, u64 data)
+ {
+ // remove existing tap
+ if (m_ll_watch)
+ m_ll_watch->remove();
+
+ m_r[RTREG] = data;
+ m_cp0[CP0_LLAddr] = u32(address >> 4);
+
+ // install write tap
+ // FIXME: address truncation
+ m_ll_watch = space(0).install_write_tap(offs_t(address & ~7), offs_t(address | 7), "lld",
+ [this](offs_t offset, u64 &data, u64 mem_mask)
+ {
+ m_ll_watch->remove();
+ m_ll_watch = nullptr;
+ });
+ });
+ break;
+ case 0x35: // LDC1
+ cp1_execute(op);
+ break;
+ case 0x36: // LDC2
+ cp2_execute(op);
+ break;
+ case 0x37: // LD
+ load<u64>(ADDR(m_r[RSREG], s16(op)),
+ [this, op](u64 data)
+ {
+ m_r[RTREG] = data;
+ });
+ break;
+ case 0x38: // SC
+ if (m_ll_watch)
+ {
+ m_ll_watch->remove();
+ m_ll_watch = nullptr;
+
+ store<u32>(ADDR(m_r[RSREG], s16(op)), u32(m_r[RTREG]));
+ m_r[RTREG] = 1;
+ }
+ else
+ m_r[RTREG] = 0;
+ break;
+ case 0x39: // SWC1
+ cp1_execute(op);
+ break;
+ case 0x3a: // SWC2
+ cp2_execute(op);
+ break;
+ //case 0x3b: // *
+ case 0x3c: // SCD
+ if (m_ll_watch)
+ {
+ m_ll_watch->remove();
+ m_ll_watch = nullptr;
+
+ store<u64>(ADDR(m_r[RSREG], s16(op)), m_r[RTREG]);
+ m_r[RTREG] = 1;
+ }
+ else
+ m_r[RTREG] = 0;
+ break;
+ case 0x3d: // SDC1
+ cp1_execute(op);
+ break;
+ case 0x3e: // SDC2
+ cp2_execute(op);
+ break;
+ case 0x3f: // SD
+ store<u64>(ADDR(m_r[RSREG], s16(op)), m_r[RTREG]);
+ break;
+
+ default:
+ // * Operation codes marked with an asterisk cause reserved instruction
+ // exceptions in all current implementations and are reserved for future
+ // versions of the architecture.
+ cpu_exception(EXCEPTION_RI);
+ break;
+ }
+}
+
+void r4000_base_device::cpu_exception(u32 exception, u16 const vector)
+{
+ if (exception != EXCEPTION_INT)
+ LOGMASKED(LOG_EXCEPTION, "exception 0x%08x\n", exception);
+
+ if (!(SR & SR_EXL))
+ {
+ m_cp0[CP0_EPC] = m_pc;
+
+ CAUSE = (CAUSE & CAUSE_IP) | exception;
+
+ // if in a branch delay slot, restart at the branch instruction
+ if (m_branch_state == DELAY)
+ {
+ m_cp0[CP0_EPC] -= 4;
+ CAUSE |= CAUSE_BD;
+ }
+
+ SR |= SR_EXL;
+ }
+ else
+ CAUSE = (CAUSE & (CAUSE_BD | CAUSE_IP)) | exception;
+
+ m_branch_state = EXCEPTION;
+ m_pc = ((SR & SR_BEV) ? s64(s32(0xbfc00200)) : s64(s32(0x80000000))) + vector;
+
+ if (exception != EXCEPTION_INT)
+ debugger_exception_hook(exception);
+}
+
+void r4000_base_device::cpu_lwl(u32 const op)
+{
+ u64 const offset = ADDR(m_r[RSREG], s16(op));
+ unsigned const shift = ((offset & 3) ^ R4000_ENDIAN_LE_BE(3, 0)) << 3;
+
+ load<u32>(offset & ~3,
+ [this, op, shift](u32 const data)
+ {
+ m_r[RTREG] = s32((m_r[RTREG] & ~u32(~u32(0) << shift)) | (data << shift));
+ });
+}
+
+void r4000_base_device::cpu_lwr(u32 const op)
+{
+ u64 const offset = ADDR(m_r[RSREG], s16(op));
+ unsigned const shift = ((offset & 0x3) ^ R4000_ENDIAN_LE_BE(0, 3)) << 3;
+
+ load<u32>(offset & ~3,
+ [this, op, shift](u32 const data)
+ {
+ m_r[RTREG] = s32((m_r[RTREG] & ~u32(~u32(0) >> shift)) | (data >> shift));
+ });
+}
+
+void r4000_base_device::cpu_swl(u32 const op)
+{
+ u64 const offset = ADDR(m_r[RSREG], s16(op));
+ unsigned const shift = ((offset & 3) ^ R4000_ENDIAN_LE_BE(3, 0)) << 3;
+
+ store<u32>(offset & ~3, u32(m_r[RTREG]) >> shift, ~u32(0) >> shift);
+}
+
+void r4000_base_device::cpu_swr(u32 const op)
+{
+ u64 const offset = ADDR(m_r[RSREG], s16(op));
+ unsigned const shift = ((offset & 3) ^ R4000_ENDIAN_LE_BE(0, 3)) << 3;
+
+ store<u32>(offset & ~3, u32(m_r[RTREG]) << shift, ~u32(0) << shift);
+}
+
+void r4000_base_device::cpu_ldl(u32 const op)
+{
+ u64 const offset = ADDR(m_r[RSREG], s16(op));
+ unsigned const shift = ((offset & 7) ^ R4000_ENDIAN_LE_BE(7, 0)) << 3;
+
+ load<u64>(offset & ~7,
+ [this, op, shift](u64 const data)
+ {
+ m_r[RTREG] = (m_r[RTREG] & ~u64(~u64(0) << shift)) | (data << shift);
+ });
+}
+
+void r4000_base_device::cpu_ldr(u32 const op)
+{
+ u64 const offset = ADDR(m_r[RSREG], s16(op));
+ unsigned const shift = ((offset & 7) ^ R4000_ENDIAN_LE_BE(0, 7)) << 3;
+
+ load<u64>(offset & ~7,
+ [this, op, shift](u64 const data)
+ {
+ m_r[RTREG] = (m_r[RTREG] & ~u64(~u64(0) >> shift)) | (data >> shift);
+ });
+}
+
+void r4000_base_device::cpu_sdl(u32 const op)
+{
+ u64 const offset = ADDR(m_r[RSREG], s16(op));
+ unsigned const shift = ((offset & 7) ^ R4000_ENDIAN_LE_BE(7, 0)) << 3;
+
+ store<u64>(offset & ~7, m_r[RTREG] >> shift, ~u64(0) >> shift);
+}
+
+void r4000_base_device::cpu_sdr(u32 const op)
+{
+ u64 const offset = ADDR(m_r[RSREG], s16(op));
+ unsigned const shift = ((offset & 7) ^ R4000_ENDIAN_LE_BE(0, 7)) << 3;
+
+ store<u64>(offset & ~7, m_r[RTREG] << shift, ~u64(0) << shift);
+}
+
+void r4000_base_device::cp0_execute(u32 const op)
+{
+ if ((SR & SR_KSU) && !(SR & SR_CU0) && !(SR & (SR_EXL | SR_ERL)))
+ {
+ cpu_exception(EXCEPTION_CP0);
+ return;
+ }
+
+ switch ((op >> 21) & 0x1f)
+ {
+ case 0x00: // MFC0
+ m_r[RTREG] = s32(cp0_get(RDREG));
+ break;
+ case 0x01: // DMFC0
+ // ε Operation codes marked with epsilon are valid when the
+ // processor is operating either in the Kernel mode or in the
+ // 64-bit non-Kernel (User or Supervisor) mode. These instructions
+ // cause a reserved instruction exception if 64-bit operation is
+ // not enabled in User or Supervisor mode.
+ if (!(SR & SR_KSU) || (SR & (SR_EXL | SR_ERL)) || cp0_64())
+ m_r[RTREG] = cp0_get(RDREG);
+ else
+ cpu_exception(EXCEPTION_RI);
+ break;
+ case 0x02: // CFC0
+ break;
+
+ case 0x04: // MTC0
+ cp0_set(RDREG, s64(s32(m_r[RTREG])));
+ break;
+ case 0x05: // DMTC0
+ // ε Operation codes marked with epsilon are valid when the
+ // processor is operating either in the Kernel mode or in the
+ // 64-bit non-Kernel (User or Supervisor) mode. These instructions
+ // cause a reserved instruction exception if 64-bit operation is
+ // not enabled in User or Supervisor mode.
+ if (!(SR & SR_KSU) || (SR & (SR_EXL | SR_ERL)) || cp0_64())
+ cp0_set(RDREG, m_r[RTREG]);
+ else
+ cpu_exception(EXCEPTION_RI);
+ break;
+ case 0x06: // CTC0
+ break;
+
+ case 0x08: // BC0
+ switch ((op >> 16) & 0x1f)
+ {
+ case 0x00: // BC0F
+ case 0x01: // BC0T
+ case 0x02: // BC0FL
+ case 0x03: // BC0TL
+ // fall through
+
+ default:
+ // γ Operation codes marked with a gamma cause a reserved
+ // instruction exception. They are reserved for future versions
+ // of the architecture.
+ cpu_exception(EXCEPTION_RI);
+ 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:
+ // CP0 function
+ switch (op & 0x3f)
+ {
+ case 0x01: // TLBR
+ cp0_tlbr();
+ break;
+ case 0x02: // TLBWI
+ cp0_tlbwi(m_cp0[CP0_Index] & 0x3f);
+ break;
+
+ case 0x06: // TLBWR
+ cp0_tlbwr();
+ break;
+
+ case 0x08: // TLBP
+ cp0_tlbp();
+ break;
+
+ case 0x10: // RFE
+ // ξ Operation codes marked with a xi cause a reserved
+ // instruction exception on R4000 processors.
+ cpu_exception(EXCEPTION_RI);
+ break;
+
+ case 0x18: // ERET
+ if (SR & SR_ERL)
+ {
+ logerror("eret from error\n");
+ m_branch_state = EXCEPTION;
+ m_pc = m_cp0[CP0_ErrorEPC];
+ SR &= ~SR_ERL;
+ }
+ else
+ {
+ m_branch_state = EXCEPTION;
+ m_pc = m_cp0[CP0_EPC];
+ SR &= ~SR_EXL;
+ }
+
+ if (m_ll_watch)
+ {
+ m_ll_watch->remove();
+ m_ll_watch = nullptr;
+ }
+ break;
+
+ default:
+ // Φ Operation codes marked with a phi are invalid but do not
+ // cause reserved instruction exceptions in R4000 implementations.
+ break;
+ }
+ break;
+
+ default:
+ // γ Operation codes marked with a gamma cause a reserved
+ // instruction exception. They are reserved for future versions
+ // of the architecture.
+ cpu_exception(EXCEPTION_RI);
+ break;
+ }
+}
+
+u64 r4000_base_device::cp0_get(unsigned const reg)
+{
+ switch (reg)
+ {
+ case CP0_Count:
+ return u32((total_cycles() - m_cp0_timer_zero) / 2);
+
+ case CP0_Random:
+ {
+ u8 const wired = m_cp0[CP0_Wired] & 0x3f;
+
+ if (wired < ARRAY_LENGTH(m_tlb))
+ return ((total_cycles() - m_cp0_timer_zero) % (ARRAY_LENGTH(m_tlb) - wired) + wired) & 0x3f;
+ else
+ return ARRAY_LENGTH(m_tlb) - 1;
+ }
+ break;
+
+ default:
+ return m_cp0[reg];
+ }
+}
+
+void r4000_base_device::cp0_set(unsigned const reg, u64 const data)
+{
+ switch (reg)
+ {
+ case CP0_Count:
+ m_cp0[CP0_Count] = u32(data);
+ m_cp0_timer_zero = total_cycles() - m_cp0[CP0_Count] * 2;
+
+ cp0_update_timer();
+ break;
+
+ case CP0_EntryHi:
+ m_cp0[CP0_EntryHi] = data & (EH_R | EH_VPN2_64 | EH_ASID);
+ break;
+
+ case CP0_Compare:
+ m_cp0[CP0_Compare] = u32(data);
+ CAUSE &= ~CAUSE_IPEX5;
+
+ cp0_update_timer(true);
+ break;
+
+ case CP0_Status:
+ m_cp0[CP0_Status] = u32(data);
+
+ // FIXME: software interrupt check
+ if (CAUSE & SR & SR_IMSW)
+ m_icount = 0;
+
+ if (data & SR_RE)
+ fatalerror("unimplemented reverse endian mode enabled (%s)\n", machine().describe_context().c_str());
+ break;
+
+ case CP0_Cause:
+ CAUSE = (CAUSE & ~CAUSE_IPSW) | (data & CAUSE_IPSW);
+
+ // FIXME: software interrupt check
+ if (CAUSE & SR & SR_IMSW)
+ m_icount = 0;
+ break;
+
+ case CP0_PRId:
+ // read-only register
+ break;
+
+ case CP0_Config:
+ m_cp0[CP0_Config] = (m_cp0[CP0_Config] & ~CONFIG_WM) | (data & CONFIG_WM);
+
+ if (m_cp0[CP0_Config] & CONFIG_IB)
+ {
+ m_icache_line_size = 32;
+ m_icache_shift = 5;
+ m_icache_mask_lo = ~u32(0x1f);
+ }
+ else
+ {
+ m_icache_line_size = 16;
+ m_icache_shift = 4;
+ m_icache_mask_lo = ~u32(0xf);
+ }
+
+ LOGMASKED(LOG_CACHE, "icache/dcache line sizes %d/%d bytes\n",
+ m_icache_line_size, m_cp0[CP0_Config] & CONFIG_DB ? 32 : 16);
+ break;
+
+ default:
+ m_cp0[reg] = data;
+ break;
+ }
+}
+
+void r4000_base_device::cp0_tlbr()
+{
+ u8 const index = m_cp0[CP0_Index] & 0x3f;
+
+ if (index < ARRAY_LENGTH(m_tlb))
+ {
+ tlb_entry_t const &entry = m_tlb[index];
+
+ m_cp0[CP0_PageMask] = entry.mask;
+ m_cp0[CP0_EntryHi] = entry.vpn;
+ m_cp0[CP0_EntryLo0] = entry.pfn[0];
+ m_cp0[CP0_EntryLo1] = entry.pfn[1];
+ }
+}
+
+void r4000_base_device::cp0_tlbwi(u8 const index)
+{
+ if (index < ARRAY_LENGTH(m_tlb))
+ {
+ tlb_entry_t &entry = m_tlb[index];
+
+ entry.mask = m_cp0[CP0_PageMask];
+ entry.vpn = m_cp0[CP0_EntryHi];
+ if ((m_cp0[CP0_EntryLo0] & EL_G) && (m_cp0[CP0_EntryLo1] & EL_G))
+ entry.vpn |= EH_G;
+ entry.pfn[0] = m_cp0[CP0_EntryLo0];
+ entry.pfn[1] = m_cp0[CP0_EntryLo1];
+
+ entry.low_bit = 32 - count_leading_zeros((entry.mask >> 1) | 0xfff);
+
+ LOGMASKED(LOG_TLB, "tlb write index %02d mask 0x%016x vpn2 0x%016x %c asid 0x%02x pfn0 0x%016x %c%c pfn1 0x%016x %c%c (%s)\n",
+ index, entry.mask,
+ entry.vpn, entry.vpn & EH_G ? 'G' : '-', entry.vpn & EH_ASID,
+ entry.pfn[0] & EL_PFN, entry.pfn[0] & EL_D ? 'D' : '-', entry.pfn[0] & EL_V ? 'V' : '-',
+ entry.pfn[1] & EL_PFN, entry.pfn[1] & EL_D ? 'D' : '-', entry.pfn[1] & EL_V ? 'V' : '-',
+ machine().describe_context());
+ }
+}
+
+void r4000_base_device::cp0_tlbwr()
+{
+ u8 const wired = m_cp0[CP0_Wired] & 0x3f;
+ u8 const unwired = ARRAY_LENGTH(m_tlb) - wired;
+
+ u8 const index = (unwired > 0) ? ((total_cycles() - m_cp0_timer_zero) % unwired + wired) & 0x3f : (ARRAY_LENGTH(m_tlb) - 1);
+
+ cp0_tlbwi(index);
+}
+
+void r4000_base_device::cp0_tlbp()
+{
+ m_cp0[CP0_Index] = 0x80000000;
+ for (u8 index = 0; index < ARRAY_LENGTH(m_tlb); index++)
+ {
+ tlb_entry_t const &entry = m_tlb[index];
+
+ u64 const mask = (cp0_64() ? EH_R | (EH_VPN2_64 & ~entry.mask) : (EH_VPN2_32 & ~entry.mask))
+ | ((entry.vpn & EH_G) ? 0 : EH_ASID);
+
+ if ((entry.vpn & mask) == (m_cp0[CP0_EntryHi] & mask))
+ {
+ m_cp0[CP0_Index] = index;
+ break;
+ }
+ }
+
+ if (m_cp0[CP0_Index] == 0x80000000)
+ LOGMASKED(LOG_TLB, "tlbp miss 0x%08x\n", m_cp0[CP0_EntryHi]);
+ else
+ LOGMASKED(LOG_TLB, "tlbp hit 0x%08x index %02d\n", m_cp0[CP0_EntryHi], m_cp0[CP0_Index]);
+}
+
+void r4000_base_device::cp0_update_timer(bool start)
+{
+ if (start || m_cp0_timer->enabled())
+ {
+ u32 const count = (total_cycles() - m_cp0_timer_zero) / 2;
+ u32 const delta = m_cp0[CP0_Compare] - count;
+
+ m_cp0_timer->adjust(cycles_to_attotime(u64(delta) * 2));
+ }
+}
+
+TIMER_CALLBACK_MEMBER(r4000_base_device::cp0_timer_callback)
+{
+ m_cp0[CP0_Cause] |= CAUSE_IPEX5;
+}
+
+bool r4000_base_device::cp0_64() const
+{
+ switch (SR & (SR_KSU | SR_ERL | SR_EXL))
+ {
+ case SR_KSU_U: return bool(SR & SR_UX);
+ case SR_KSU_S: return bool(SR & SR_SX);
+
+ default:
+ return bool(SR & SR_KX);
+ }
+}
+
+void r4000_base_device::cp1_execute(u32 const op)
+{
+ if (!(SR & SR_CU1))
+ {
+ cpu_exception(EXCEPTION_CP1);
+ return;
+ }
+
+ softfloat_exceptionFlags = 0;
+ switch (op >> 26)
+ {
+ case 0x11: // COP1
+ switch ((op >> 21) & 0x1f)
+ {
+ case 0x00: // MFC1
+ if (SR & SR_FR)
+ m_r[RTREG] = s64(s32(m_f[RDREG]));
+ else
+ if (RDREG & 1)
+ // move the high half of the even floating point register
+ m_r[RTREG] = s64(s32(m_f[RDREG & ~1] >> 32));
+ else
+ // move the low half of the even floating point register
+ m_r[RTREG] = s64(s32(m_f[RDREG & ~1] >> 0));
+ break;
+ case 0x01: // DMFC1
+ // TODO: MIPS3 only
+ if ((SR & SR_FR) || !(RDREG & 1))
+ m_r[RTREG] = m_f[RDREG];
+ break;
+ case 0x02: // CFC1
+ switch (RDREG)
+ {
+ case 0: m_r[RTREG] = m_fcr0; break;
+ case 30: m_r[RTREG] = m_fcr30; break;
+ case 31: m_r[RTREG] = m_fcr31; break;
+
+ default:
+ logerror("cfc1 undefined fpu control register %d (%s)\n", RDREG, machine().describe_context());
+ break;
+ }
+ break;
+ case 0x04: // MTC1
+ if (SR & SR_FR)
+ m_f[RDREG] = u32(m_r[RTREG]);
+ else
+ if (RDREG & 1)
+ // load the high half of the even floating point register
+ m_f[RDREG & ~1] = (m_r[RTREG] << 32) | u32(m_f[RDREG & ~1]);
+ else
+ // load the low half of the even floating point register
+ m_f[RDREG & ~1] = (m_f[RDREG & ~1] & ~0xffffffffULL) | u32(m_r[RTREG]);
+ break;
+ case 0x05: // DMTC1
+ // TODO: MIPS3 only
+ if ((SR & SR_FR) || !(RDREG & 1))
+ m_f[RDREG] = m_r[RTREG];
+ break;
+ case 0x06: // CTC1
+ switch (RDREG)
+ {
+ case 0: // register is read-only
+ break;
+
+ case 30: // unknown
+ m_fcr30 = u32(m_r[RTREG]);
+ break;
+
+ case 31:
+ m_fcr31 = u32(m_r[RTREG]);
+
+ // 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;
+ }
+
+ // exception check
+ if ((m_fcr31 & FCR31_CE) || ((m_fcr31 & FCR31_CM) >> 5) & (m_fcr31 & FCR31_EM))
+ cpu_exception(EXCEPTION_FPE);
+
+ 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))
+ {
+ m_branch_state = BRANCH;
+ m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
+ }
+ break;
+ case 0x01: // BC1T
+ if (m_fcr31 & FCR31_C)
+ {
+ m_branch_state = BRANCH;
+ m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
+ }
+ break;
+ case 0x02: // BC1FL
+ if (!(m_fcr31 & FCR31_C))
+ {
+ m_branch_state = BRANCH;
+ m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
+ }
+ else
+ m_branch_state = NULLIFY;
+ break;
+ case 0x03: // BC1TL
+ if (m_fcr31 & FCR31_C)
+ {
+ m_branch_state = BRANCH;
+ m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
+ }
+ else
+ m_branch_state = NULLIFY;
+ break;
+
+ default:
+ // reserved instructions
+ cpu_exception(EXCEPTION_RI);
+ break;
+ }
+ break;
+
+ case 0x10: // S
+ switch (op & 0x3f)
+ {
+ case 0x00: // ADD.S
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f32_add(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }).v);
+ break;
+ case 0x01: // SUB.S
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f32_sub(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }).v);
+ break;
+ case 0x02: // MUL.S
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f32_mul(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }).v);
+ break;
+ case 0x03: // DIV.S
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f32_div(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }).v);
+ break;
+ case 0x04: // SQRT.S
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f32_sqrt(float32_t{ u32(m_f[FSREG]) }).v);
+ break;
+ case 0x05: // ABS.S
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f32_lt(float32_t{ u32(m_f[FSREG]) }, float32_t{ 0 }))
+ cp1_set(FDREG, f32_mul(float32_t{ u32(m_f[FSREG]) }, i32_to_f32(-1)).v);
+ else
+ cp1_set(FDREG, m_f[FSREG]);
+ }
+ break;
+ case 0x06: // MOV.S
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ m_f[FDREG] = m_f[FSREG];
+ break;
+ case 0x07: // NEG.S
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f32_mul(float32_t{ u32(m_f[FSREG]) }, i32_to_f32(-1)).v);
+ break;
+ case 0x08: // ROUND.L.S
+ // TODO: MIPS3 only
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f32_to_i64(float32_t{ u32(m_f[FSREG]) }, softfloat_round_near_even, true));
+ break;
+ case 0x09: // TRUNC.L.S
+ // TODO: MIPS3 only
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f32_to_i64(float32_t{ u32(m_f[FSREG]) }, softfloat_round_minMag, true));
+ break;
+ case 0x0a: // CEIL.L.S
+ // TODO: MIPS3 only
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f32_to_i64(float32_t{ u32(m_f[FSREG]) }, softfloat_round_max, true));
+ break;
+ case 0x0b: // FLOOR.L.S
+ // TODO: MIPS3 only
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f32_to_i64(float32_t{ u32(m_f[FSREG]) }, softfloat_round_min, true));
+ break;
+ case 0x0c: // ROUND.W.S
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f32_to_i32(float32_t{ u32(m_f[FSREG]) }, softfloat_round_near_even, true));
+ break;
+ case 0x0d: // TRUNC.W.S
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f32_to_i32(float32_t{ u32(m_f[FSREG]) }, softfloat_round_minMag, true));
+ break;
+ case 0x0e: // CEIL.W.S
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f32_to_i32(float32_t{ u32(m_f[FSREG]) }, softfloat_round_max, true));
+ break;
+ case 0x0f: // FLOOR.W.S
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f32_to_i32(float32_t{ u32(m_f[FSREG]) }, softfloat_round_min, true));
+ break;
+
+ case 0x21: // CVT.D.S
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f32_to_f64(float32_t{ u32(m_f[FSREG]) }).v);
+ break;
+ case 0x24: // CVT.W.S
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f32_to_i32(float32_t{ u32(m_f[FSREG]) }, softfloat_roundingMode, true));
+ break;
+ case 0x25: // CVT.L.S
+ // TODO: MIPS3 only
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f32_to_i64(float32_t{ u32(m_f[FSREG]) }, softfloat_roundingMode, true));
+ break;
+
+ case 0x30: // C.F.S (false)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ m_fcr31 &= ~FCR31_C;
+ break;
+ case 0x31: // C.UN.S (unordered)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ // detect unordered
+ f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) });
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ m_fcr31 |= FCR31_C;
+ else
+ m_fcr31 &= ~FCR31_C;
+ }
+ break;
+ case 0x32: // C.EQ.S (equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }))
+ m_fcr31 |= FCR31_C;
+ else
+ m_fcr31 &= ~FCR31_C;
+ }
+ break;
+ case 0x33: // C.UEQ.S (unordered equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
+ m_fcr31 |= FCR31_C;
+ else
+ m_fcr31 &= ~FCR31_C;
+ }
+ break;
+ case 0x34: // C.OLT.S (less than)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f32_lt(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }))
+ m_fcr31 |= FCR31_C;
+ else
+ m_fcr31 &= ~FCR31_C;
+ }
+ break;
+ case 0x35: // C.ULT.S (unordered less than)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f32_lt(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (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 ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f32_le(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }))
+ m_fcr31 |= FCR31_C;
+ else
+ m_fcr31 &= ~FCR31_C;
+ }
+ break;
+ case 0x37: // C.ULE.S (unordered less than or equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f32_le(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
+ m_fcr31 |= FCR31_C;
+ else
+ m_fcr31 &= ~FCR31_C;
+ }
+ break;
+
+ case 0x38: // C.SF.S (signalling false)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ // detect unordered
+ f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) });
+
+ m_fcr31 &= ~FCR31_C;
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ break;
+ case 0x39: // C.NGLE.S (not greater, less than or equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ // detect unordered
+ f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) });
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_C | FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ else
+ m_fcr31 &= ~FCR31_C;
+ }
+ break;
+ case 0x3a: // C.SEQ.S (signalling equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }))
+ m_fcr31 |= FCR31_C;
+ else
+ m_fcr31 &= ~FCR31_C;
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ break;
+ case 0x3b: // C.NGL.S (not greater or less than)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
+ m_fcr31 |= FCR31_C;
+ else
+ m_fcr31 &= ~FCR31_C;
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ break;
+ case 0x3c: // C.LT.S (less than)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f32_lt(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }))
+ m_fcr31 |= FCR31_C;
+ else
+ m_fcr31 &= ~FCR31_C;
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ break;
+ case 0x3d: // C.NGE.S (not greater or equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f32_lt(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
+ m_fcr31 |= FCR31_C;
+ else
+ m_fcr31 &= ~FCR31_C;
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ break;
+ case 0x3e: // C.LE.S (less than or equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f32_le(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }))
+ m_fcr31 |= FCR31_C;
+ else
+ m_fcr31 &= ~FCR31_C;
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ break;
+ case 0x3f: // C.NGT.S (not greater than)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f32_le(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
+ m_fcr31 |= FCR31_C;
+ else
+ m_fcr31 &= ~FCR31_C;
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ break;
+
+ default: // unimplemented operations
+ m_fcr31 |= FCR31_CE;
+ cpu_exception(EXCEPTION_FPE);
+ break;
+ }
+ break;
+ case 0x11: // D
+ switch (op & 0x3f)
+ {
+ case 0x00: // ADD.D
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f64_add(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }).v);
+ break;
+ case 0x01: // SUB.D
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f64_sub(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }).v);
+ break;
+ case 0x02: // MUL.D
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f64_mul(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }).v);
+ break;
+ case 0x03: // DIV.D
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f64_div(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }).v);
+ break;
+ case 0x04: // SQRT.D
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f64_sqrt(float64_t{ m_f[FSREG] }).v);
+ break;
+ case 0x05: // ABS.D
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f64_lt(float64_t{ m_f[FSREG] }, float64_t{ 0 }))
+ cp1_set(FDREG, f64_mul(float64_t{ m_f[FSREG] }, i32_to_f64(-1)).v);
+ else
+ cp1_set(FDREG, m_f[FSREG]);
+ }
+ break;
+ case 0x06: // MOV.D
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ m_f[FDREG] = m_f[FSREG];
+ break;
+ case 0x07: // NEG.D
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f64_mul(float64_t{ m_f[FSREG] }, i32_to_f64(-1)).v);
+ break;
+ case 0x08: // ROUND.L.D
+ // TODO: MIPS3 only
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f64_to_i64(float64_t{ m_f[FSREG] }, softfloat_round_near_even, true));
+ break;
+ case 0x09: // TRUNC.L.D
+ // TODO: MIPS3 only
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f64_to_i64(float64_t{ m_f[FSREG] }, softfloat_round_minMag, true));
+ break;
+ case 0x0a: // CEIL.L.D
+ // TODO: MIPS3 only
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f64_to_i64(float64_t{ m_f[FSREG] }, softfloat_round_max, true));
+ break;
+ case 0x0b: // FLOOR.L.D
+ // TODO: MIPS3 only
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f64_to_i64(float64_t{ m_f[FSREG] }, softfloat_round_min, true));
+ break;
+ case 0x0c: // ROUND.W.D
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f64_to_i32(float64_t{ m_f[FSREG] }, softfloat_round_near_even, true));
+ break;
+ case 0x0d: // TRUNC.W.D
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f64_to_i32(float64_t{ m_f[FSREG] }, softfloat_round_minMag, true));
+ break;
+ case 0x0e: // CEIL.W.D
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f64_to_i32(float64_t{ m_f[FSREG] }, softfloat_round_max, true));
+ break;
+ case 0x0f: // FLOOR.W.D
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f64_to_i32(float64_t{ m_f[FSREG] }, softfloat_round_min, true));
+ break;
+
+ case 0x20: // CVT.S.D
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f64_to_f32(float64_t{ m_f[FSREG] }).v);
+ break;
+ case 0x24: // CVT.W.D
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f64_to_i32(float64_t{ m_f[FSREG] }, softfloat_roundingMode, true));
+ break;
+ case 0x25: // CVT.L.D
+ // TODO: MIPS3 only
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, f64_to_i64(float64_t{ m_f[FSREG] }, softfloat_roundingMode, true));
+ break;
+
+ case 0x30: // C.F.D (false)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ m_fcr31 &= ~FCR31_C;
+ break;
+ case 0x31: // C.UN.D (unordered)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ // detect unordered
+ f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] });
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ m_fcr31 |= FCR31_C;
+ else
+ m_fcr31 &= ~FCR31_C;
+ }
+ break;
+ case 0x32: // C.EQ.D (equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }))
+ m_fcr31 |= FCR31_C;
+ else
+ m_fcr31 &= ~FCR31_C;
+ }
+ break;
+ case 0x33: // C.UEQ.D (unordered equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
+ m_fcr31 |= FCR31_C;
+ else
+ m_fcr31 &= ~FCR31_C;
+ }
+ break;
+ case 0x34: // C.OLT.D (less than)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f64_lt(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }))
+ m_fcr31 |= FCR31_C;
+ else
+ m_fcr31 &= ~FCR31_C;
+ }
+ break;
+ case 0x35: // C.ULT.D (unordered less than)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f64_lt(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (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 ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f64_le(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }))
+ m_fcr31 |= FCR31_C;
+ else
+ m_fcr31 &= ~FCR31_C;
+ }
+ break;
+ case 0x37: // C.ULE.D (unordered less than or equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f64_le(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
+ m_fcr31 |= FCR31_C;
+ else
+ m_fcr31 &= ~FCR31_C;
+ }
+ break;
+
+ case 0x38: // C.SF.D (signalling false)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ // detect unordered
+ f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] });
+
+ m_fcr31 &= ~FCR31_C;
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ break;
+ case 0x39: // C.NGLE.D (not greater, less than or equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ // detect unordered
+ f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] });
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_C | FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ else
+ m_fcr31 &= ~FCR31_C;
+ }
+ break;
+ case 0x3a: // C.SEQ.D (signalling equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }))
+ m_fcr31 |= FCR31_C;
+ else
+ m_fcr31 &= ~FCR31_C;
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ break;
+ case 0x3b: // C.NGL.D (not greater or less than)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
+ m_fcr31 |= FCR31_C;
+ else
+ m_fcr31 &= ~FCR31_C;
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ break;
+ case 0x3c: // C.LT.D (less than)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f64_lt(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }))
+ m_fcr31 |= FCR31_C;
+ else
+ m_fcr31 &= ~FCR31_C;
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ break;
+ case 0x3d: // C.NGE.D (not greater or equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f64_lt(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
+ m_fcr31 |= FCR31_C;
+ else
+ m_fcr31 &= ~FCR31_C;
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ break;
+ case 0x3e: // C.LE.D (less than or equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f64_le(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }))
+ m_fcr31 |= FCR31_C;
+ else
+ m_fcr31 &= ~FCR31_C;
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ break;
+ case 0x3f: // C.NGT.D (not greater than)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f64_le(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
+ m_fcr31 |= FCR31_C;
+ else
+ m_fcr31 &= ~FCR31_C;
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ break;
+
+ default: // unimplemented operations
+ m_fcr31 |= FCR31_CE;
+ cpu_exception(EXCEPTION_FPE);
+ break;
+ }
+ break;
+ case 0x14: // W
+ switch (op & 0x3f)
+ {
+ case 0x20: // CVT.S.W
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, i32_to_f32(s32(m_f[FSREG])).v);
+ break;
+ case 0x21: // CVT.D.W
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, i32_to_f64(s32(m_f[FSREG])).v);
+ break;
+
+ default: // unimplemented operations
+ m_fcr31 |= FCR31_CE;
+ cpu_exception(EXCEPTION_FPE);
+ break;
+ }
+ break;
+ case 0x15: // L
+ // TODO: MIPS3 only
+ switch (op & 0x3f)
+ {
+ case 0x02a00020: // CVT.S.L
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, i64_to_f32(s64(m_f[FSREG])).v);
+ break;
+ case 0x02a00021: // CVT.D.L
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ cp1_set(FDREG, i64_to_f64(s64(m_f[FSREG])).v);
+ break;
+
+ default: // unimplemented operations
+ m_fcr31 |= FCR31_CE;
+ cpu_exception(EXCEPTION_FPE);
+ break;
+ }
+ break;
+
+ default: // unimplemented operations
+ m_fcr31 |= FCR31_CE;
+ cpu_exception(EXCEPTION_FPE);
+ break;
+ }
+ break;
+
+ case 0x31: // LWC1
+ load<u32>(ADDR(m_r[RSREG], s16(op)),
+ [this, op](u32 data)
+ {
+ if (SR & SR_FR)
+ m_f[RTREG] = data;
+ else
+ if (RTREG & 1)
+ // load the high half of the even floating point register
+ m_f[RTREG & ~1] = (u64(data) << 32) | u32(m_f[RTREG & ~1]);
+ else
+ // load the low half of the even floating point register
+ m_f[RTREG & ~1] = (m_f[RTREG & ~1] & ~0xffffffffULL) | data;
+ });
+ break;
+
+ case 0x35: // LDC1
+ load<u64>(ADDR(m_r[RSREG], s16(op)),
+ [this, op](u64 data)
+ {
+ if ((SR & SR_FR) || !(RTREG & 1))
+ m_f[RTREG] = data;
+ });
+ break;
+
+ case 0x39: // SWC1
+ if (SR & SR_FR)
+ store<u32>(ADDR(m_r[RSREG], s16(op)), u32(m_f[RTREG]));
+ else
+ if (RTREG & 1)
+ // store the high half of the even floating point register
+ store<u32>(ADDR(m_r[RSREG], s16(op)), u32(m_f[RTREG & ~1] >> 32));
+ else
+ // store the low half of the even floating point register
+ store<u32>(ADDR(m_r[RSREG], s16(op)), u32(m_f[RTREG & ~1]));
+ break;
+
+ case 0x3d: // SDC1
+ if ((SR & SR_FR) || !(RTREG & 1))
+ store<u64>(ADDR(m_r[RSREG], s16(op)), m_f[RTREG]);
+ break;
+ }
+}
+
+void r4000_base_device::cp1_set(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))
+ {
+ cpu_exception(EXCEPTION_FPE);
+ return;
+ }
+
+ // set flags
+ m_fcr31 |= ((m_fcr31 & FCR31_CM) >> 10);
+ }
+
+ m_f[reg] = data;
+}
+
+void r4000_base_device::cp2_execute(u32 const op)
+{
+ if (!(SR & SR_CU2))
+ {
+ cpu_exception(EXCEPTION_CP2);
+ return;
+ }
+
+ switch (op >> 26)
+ {
+ case 0x12: // COP2
+ switch ((op >> 21) & 0x1f)
+ {
+ case 0x00: // MFC2
+ logerror("mfc2 unimplemented (%s)\n", machine().describe_context());
+ break;
+ case 0x01: // DMFC2
+ // ε Operation codes marked with epsilon are valid when the
+ // processor is operating either in the Kernel mode or in the
+ // 64-bit non-Kernel (User or Supervisor) mode. These instructions
+ // cause a reserved instruction exception if 64-bit operation is
+ // not enabled in User or Supervisor mode.
+ if (!(SR & SR_KSU) || (SR & (SR_EXL | SR_ERL)) || cp0_64())
+ logerror("dmfc2 unimplemented (%s)\n", machine().describe_context());
+ else
+ cpu_exception(EXCEPTION_RI);
+ break;
+ case 0x02: // CFC2
+ logerror("cfc2 unimplemented (%s)\n", machine().describe_context());
+ break;
+
+ case 0x04: // MTC2
+ logerror("mtc2 unimplemented (%s)\n", machine().describe_context());
+ break;
+ case 0x05: // DMTC2
+ // ε Operation codes marked with epsilon are valid when the
+ // processor is operating either in the Kernel mode or in the
+ // 64-bit non-Kernel (User or Supervisor) mode. These instructions
+ // cause a reserved instruction exception if 64-bit operation is
+ // not enabled in User or Supervisor mode.
+ if (!(SR & SR_KSU) || (SR & (SR_EXL | SR_ERL)) || cp0_64())
+ logerror("dmtc2 unimplemented (%s)\n", machine().describe_context());
+ else
+ cpu_exception(EXCEPTION_RI);
+ break;
+ case 0x06: // CTC2
+ logerror("ctc2 unimplemented (%s)\n", machine().describe_context());
+ break;
+
+ case 0x08: // BC2
+ switch ((op >> 16) & 0x1f)
+ {
+ case 0x00: // BC2F
+ case 0x01: // BC2F
+ case 0x02: // BC2FL
+ case 0x03: // BC2TL
+ logerror("bc2 unimplemented (%s)\n", machine().describe_context());
+ break;
+
+ default:
+ // γ Operation codes marked with a gamma cause a reserved
+ // instruction exception. They are reserved for future versions
+ // of the architecture.
+ cpu_exception(EXCEPTION_RI);
+ 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:
+ // CP2 function
+ logerror("function unimplemented (%s)\n", machine().describe_context());
+ break;
+
+ default:
+ // γ Operation codes marked with a gamma cause a reserved
+ // instruction exception. They are reserved for future versions
+ // of the architecture.
+ cpu_exception(EXCEPTION_RI);
+ break;
+ }
+ break;
+
+ case 0x32: // LWC2
+ logerror("lwc2 unimplemented (%s)\n", machine().describe_context());
+ break;
+
+ case 0x36: // LDC2
+ logerror("ldc2 unimplemented (%s)\n", machine().describe_context());
+ break;
+
+ case 0x3a: // SWC2
+ logerror("swc2 unimplemented (%s)\n", machine().describe_context());
+ break;
+
+ case 0x3e: // SDC2
+ logerror("sdc2 unimplemented (%s)\n", machine().describe_context());
+ break;
+ }
+}
+
+r4000_base_device::translate_t r4000_base_device::translate(int intention, u64 &address)
+{
+ /*
+ * Decode the program address into one of the following ranges depending on
+ * the active status register bits.
+ *
+ * 32-bit modes
+ * user: 0x0000'0000-0x7fff'ffff (useg, mapped)
+ *
+ * super: 0x0000'0000-0x7fff'ffff (suseg, mapped)
+ * 0xc000'0000-0xdfff'ffff (ssseg, mapped)
+ *
+ * kernel: 0x0000'0000-0x7fff'ffff (kuseg, mapped)
+ * 0x8000'0000-0x9fff'ffff (kseg0, unmapped, cached)
+ * 0xa000'0000-0xbfff'ffff (kseg1, unmapped, uncached)
+ * 0xc000'0000-0xdfff'ffff (ksseg, mapped)
+ * 0xe000'0000-0xffff'ffff (kseg3, mapped)
+ *
+ * 64-bit modes
+ * user: 0x0000'0000'0000'0000-0x0000'00ff'ffff'ffff (xuseg, mapped)
+ *
+ * super: 0x0000'0000'0000'0000-0x0000'00ff'ffff'ffff (xsuseg, mapped)
+ * 0x4000'0000'0000'0000-0x4000'00ff'ffff'ffff (xsseg, mapped)
+ * 0xffff'ffff'c000'0000-0xffff'ffff'dfff'ffff (csseg, mapped)
+ *
+ * kernel: 0x0000'0000'0000'0000-0x0000'00ff'ffff'ffff (xkuseg, mapped)
+ * 0x4000'0000'0000'0000-0x4000'00ff'ffff'ffff (xksseg, mapped)
+ * 0x8000'0000'0000'0000-0xbfff'ffff'ffff'ffff (xkphys, unmapped)
+ * 0xc000'0000'0000'0000-0xc000'00ff'7fff'ffff (xkseg, mapped)
+ * 0xffff'ffff'8000'0000-0xffff'ffff'9fff'ffff (ckseg0, unmapped, cached)
+ * 0xffff'ffff'a000'0000-0xffff'ffff'bfff'ffff (ckseg1, unmapped, uncached)
+ * 0xffff'ffff'c000'0000-0xffff'ffff'dfff'ffff (cksseg, mapped)
+ * 0xffff'ffff'e000'0000-0xffff'ffff'ffff'ffff (ckseg3, mapped)
+ */
+
+ bool extended = false;
+
+ switch (SR & (SR_KSU | SR_ERL | SR_EXL))
+ {
+ case SR_KSU_U:
+ // user mode
+ if (SR & SR_UX)
+ {
+ // 64-bit user mode
+ if (address & 0xffff'ff00'0000'0000)
+ return ERROR; // exception
+ else
+ extended = true; // xuseg
+ }
+ else
+ {
+ // 32-bit user mode
+ if (address & 0xffff'ffff'8000'0000)
+ return ERROR; // exception
+ else
+ extended = false; // useg
+ }
+ break;
+
+ case SR_KSU_S:
+ // supervisor mode
+ if (SR & SR_SX)
+ {
+ // 64-bit supervisor mode
+ if (address & 0xffff'ff00'0000'0000)
+ if ((address & 0xffff'ff00'0000'0000) == 0x4000'0000'0000'0000)
+ extended = true; // xsseg
+ else
+ if ((address & 0xffff'ffff'e000'0000) == 0xffff'ffff'c000'0000)
+ extended = true; // csseg
+ else
+ return ERROR; // exception
+ else
+ extended = true; // xsuseg
+ }
+ else
+ {
+ // 32-bit supervisor mode
+ if (address & 0xffff'ffff'8000'0000)
+ if ((address & 0xffff'ffff'e000'0000) == 0xffff'ffff'c000'0000)
+ extended = false; // sseg
+ else
+ return ERROR; // exception
+ else
+ extended = false; // suseg
+ }
+ break;
+
+ case SR_KSU_U | SR_KSU_S:
+ fatalerror("invalid ksu bits 0x%08x (%s)\n", u32(SR), machine().describe_context().c_str());
+ break;
+
+ default:
+ // kernel mode
+ if (SR & SR_KX)
+ {
+ // 64-bit kernel mode
+ if (address & 0xffff'ff00'0000'0000)
+ if ((address & 0xffff'ff00'0000'0000) == 0x4000'0000'0000'0000)
+ extended = true; // xksseg
+ else
+ if ((address & 0xc000'0000'0000'0000) == 0x8000'0000'0000'0000)
+ {
+ address &= 0x0000'000f'ffff'ffff; // xkphys
+
+ // FIXME: caching depends on top three bits
+ return CACHED;
+ }
+ else
+ if ((address & 0xffff'ff00'0000'0000) == 0xc000'0000'0000'0000)
+ if ((address & 0x0000'00ff'8000'0000) == 0x0000'00ff'8000'0000)
+ return ERROR; // exception
+ else
+ extended = true; // xkseg
+ else
+ // FIXME: ckseg0 caching depends on config regiter
+ switch (address & 0xffff'ffff'e000'0000)
+ {
+ case 0xffff'ffff'8000'0000: address &= 0x7fff'ffff; return CACHED; // ckseg0
+ case 0xffff'ffff'a000'0000: address &= 0x1fff'ffff; return UNCACHED; // ckseg1
+ case 0xffff'ffff'c000'0000: extended = true; break; // cksseg
+ case 0xffff'ffff'e000'0000: extended = true; break; // ckseg3
+ default: return ERROR; // exception
+ }
+ else
+ if (SR & SR_ERL)
+ // FIXME: documentation says 2^31, but assume it should be 2^40
+ return UNCACHED; // xkuseg (unmapped, uncached)
+ else
+ extended = true; // xkuseg
+ }
+ else
+ {
+ // 32-bit kernel mode
+ if (address & 0xffff'ffff'8000'0000)
+ switch (address & 0xffff'ffff'e000'0000)
+ {
+ case 0xffff'ffff'8000'0000: address &= 0x7fff'ffff; return CACHED; // kseg0
+ case 0xffff'ffff'a000'0000: address &= 0x1fff'ffff; return UNCACHED; // kseg1
+ case 0xffff'ffff'c000'0000: extended = false; break; // ksseg
+ case 0xffff'ffff'e000'0000: extended = false; break; // kseg3
+ default: return ERROR; // exception
+ }
+ else
+ if (SR & SR_ERL)
+ return UNCACHED; // kuseg (unmapped, uncached)
+ else
+ extended = false; // kuseg
+ }
+ break;
+ }
+
+ // address needs translation, using a combination of VPN2 and ASID
+ u64 const key = (address & (extended ? (EH_R | EH_VPN2_64) : EH_VPN2_32)) | (m_cp0[CP0_EntryHi] & EH_ASID);
+
+ unsigned *mru = m_tlb_mru[intention & TRANSLATE_TYPE_MASK];
+ if (LOG_STATS)
+ m_tlb_scans++;
+
+ bool invalid = false;
+ bool modify = false;
+ for (unsigned i = 0; i < ARRAY_LENGTH(m_tlb); i++)
+ {
+ unsigned const index = mru[i];
+ tlb_entry_t const &entry = m_tlb[index];
+
+ // test vpn and asid
+ u64 const mask = (extended ? EH_R | (EH_VPN2_64 & ~entry.mask) : (EH_VPN2_32 & ~entry.mask))
+ | ((entry.vpn & EH_G) ? 0 : EH_ASID);
+
+ if ((entry.vpn & mask) != (key & mask))
+ continue;
+
+ if (LOG_STATS)
+ m_tlb_loops += i + 1;
+
+ u64 const pfn = entry.pfn[BIT(address, entry.low_bit)];
+
+ // test valid
+ if (!(pfn & EL_V))
+ {
+ invalid = true;
+ break;
+ }
+
+ // test dirty
+ if ((intention & TRANSLATE_WRITE) && !(pfn & EL_D))
+ {
+ modify = true;
+ break;
+ }
+
+ // translate the address
+ address &= (entry.mask >> 1) | 0xfff;
+ address |= ((pfn & EL_PFN) << 6) & ~(entry.mask >> 1);
+
+ // promote the entry in the mru index
+ if (i > 0)
+ std::swap(mru[i - 1], mru[i]);
+
+ return ((pfn & EL_C) == C_2) ? UNCACHED : CACHED;
+ }
+
+ // tlb miss, invalid entry, or a store to a non-dirty entry
+ if (!machine().side_effects_disabled() && !(intention & TRANSLATE_DEBUG_MASK))
+ {
+ if (VERBOSE & LOG_TLB)
+ {
+ char const mode[] = { 'r', 'w', 'x' };
+
+ if (modify)
+ LOGMASKED(LOG_TLB, "tlb modify asid %d address 0x%016x (%s)\n",
+ m_cp0[CP0_EntryHi] & EH_ASID, address, machine().describe_context());
+ else
+ LOGMASKED(LOG_TLB, "tlb miss %c asid %d address 0x%016x (%s)\n",
+ mode[intention & TRANSLATE_TYPE_MASK], m_cp0[CP0_EntryHi] & EH_ASID, address, machine().describe_context());
+ }
+
+ // load tlb exception registers
+ m_cp0[CP0_BadVAddr] = address;
+ m_cp0[CP0_EntryHi] = key;
+ m_cp0[CP0_Context] = (m_cp0[CP0_Context] & CONTEXT_PTEBASE) | ((address >> 9) & CONTEXT_BADVPN2);
+ m_cp0[CP0_XContext] = (m_cp0[CP0_XContext] & XCONTEXT_PTEBASE) | ((address >> 31) & XCONTEXT_R) | ((address >> 9) & XCONTEXT_BADVPN2);
+
+ if (invalid || modify || (SR & SR_EXL))
+ cpu_exception(modify ? EXCEPTION_MOD : (intention & TRANSLATE_WRITE) ? EXCEPTION_TLBS : EXCEPTION_TLBL);
+ else
+ cpu_exception((intention & TRANSLATE_WRITE) ? EXCEPTION_TLBS : EXCEPTION_TLBL, extended ? 0x000 : 0x080);
+ }
+
+ return MISS;
+}
+
+void r4000_base_device::address_error(int intention, u64 const address)
+{
+ if (!machine().side_effects_disabled() && !(intention & TRANSLATE_DEBUG_MASK))
+ {
+ logerror("address_error 0x%016x (%s)\n", address, machine().describe_context());
+
+ // TODO: check this
+ if (!(SR & SR_EXL))
+ m_cp0[CP0_BadVAddr] = address;
+
+ cpu_exception((intention & TRANSLATE_WRITE) ? EXCEPTION_ADES : EXCEPTION_ADEL);
+
+ // address errors shouldn't typically occur, so a breakpoint is handy
+ machine().debug_break();
+ }
+}
+
+template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, bool> r4000_base_device::load(u64 address, U &&apply)
+{
+ // alignment error
+ if (address & (sizeof(T) - 1))
+ {
+ address_error(TRANSLATE_READ, address);
+ return false;
+ }
+
+ translate_t const t = translate(TRANSLATE_READ, address);
+
+ // address error
+ if (t == ERROR)
+ {
+ address_error(TRANSLATE_READ, address);
+
+ return false;
+ }
+
+ // tlb miss
+ if (t == MISS)
+ return false;
+
+ // watchpoint
+ if ((m_cp0[CP0_WatchLo] & WATCHLO_R) && !(SR & SR_EXL))
+ {
+ u64 const watch_address = ((m_cp0[CP0_WatchHi] & WATCHHI_PADDR1) << 32) | (m_cp0[CP0_WatchLo] & WATCHLO_PADDR0);
+
+ if ((address & ~7) == watch_address)
+ {
+ cpu_exception(EXCEPTION_WATCH);
+ return false;
+ }
+ }
+
+ // TODO: cache lookup
+
+ switch (sizeof(T))
+ {
+ case 1: apply(T(space(0).read_byte(address))); break;
+ case 2: apply(T(space(0).read_word(address))); break;
+ case 4: apply(T(space(0).read_dword(address))); break;
+ case 8: apply(T(space(0).read_qword(address))); break;
+ }
+
+ return true;
+}
+
+template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(u64, T)>>::value, bool> r4000_base_device::load_linked(u64 address, U &&apply)
+{
+ // alignment error
+ if (address & (sizeof(T) - 1))
+ {
+ address_error(TRANSLATE_READ, address);
+ return false;
+ }
+
+ translate_t const t = translate(TRANSLATE_READ, address);
+
+ // address error
+ if (t == ERROR)
+ {
+ address_error(TRANSLATE_READ, address);
+ return false;
+ }
+
+ // tlb miss
+ if (t == MISS)
+ return false;
+
+ // watchpoint
+ if ((m_cp0[CP0_WatchLo] & WATCHLO_R) && !(SR & SR_EXL))
+ {
+ u64 const watch_address = ((m_cp0[CP0_WatchHi] & WATCHHI_PADDR1) << 32) | (m_cp0[CP0_WatchLo] & WATCHLO_PADDR0);
+
+ if ((address & ~7) == watch_address)
+ {
+ cpu_exception(EXCEPTION_WATCH);
+ return false;
+ }
+ }
+
+ // TODO: cache lookup
+
+ switch (sizeof(T))
+ {
+ case 4: apply(address, T(space(0).read_dword(address))); break;
+ case 8: apply(address, T(space(0).read_qword(address))); break;
+ }
+
+ return true;
+}
+
+template <typename T, typename U> std::enable_if_t<std::is_convertible<U, T>::value, bool> r4000_base_device::store(u64 address, U data, T mem_mask)
+{
+ // alignment error
+ if (address & (sizeof(T) - 1))
+ {
+ address_error(TRANSLATE_READ, address);
+ return false;
+ }
+
+ translate_t const t = translate(TRANSLATE_WRITE, address);
+
+ // address error
+ if (t == ERROR)
+ {
+ address_error(TRANSLATE_WRITE, address);
+ return false;
+ }
+
+ // tlb miss
+ if (t == MISS)
+ return false;
+
+ // watchpoint
+ if ((m_cp0[CP0_WatchLo] & WATCHLO_W) && !(SR & SR_EXL))
+ {
+ u64 const watch_address = ((m_cp0[CP0_WatchHi] & WATCHHI_PADDR1) << 32) | (m_cp0[CP0_WatchLo] & WATCHLO_PADDR0);
+
+ if ((address & ~7) == watch_address)
+ {
+ cpu_exception(EXCEPTION_WATCH);
+ return false;
+ }
+ }
+
+ // TODO: cache lookup
+
+ switch (sizeof(T))
+ {
+ case 1: space(0).write_byte(address, T(data)); break;
+ case 2: space(0).write_word(address, T(data), mem_mask); break;
+ case 4: space(0).write_dword(address, T(data), mem_mask); break;
+ case 8: space(0).write_qword(address, T(data), mem_mask); break;
+ }
+
+ return true;
+}
+
+bool r4000_base_device::fetch(u64 address, std::function<void(u32)> &&apply)
+{
+ u64 const program_address = address;
+
+ // alignment error
+ if (address & 3)
+ {
+ address_error(TRANSLATE_FETCH, address);
+
+ return false;
+ }
+
+ translate_t const t = translate(TRANSLATE_FETCH, address);
+
+ // address error
+ if (t == ERROR)
+ {
+ address_error(TRANSLATE_FETCH, address);
+
+ return false;
+ }
+
+ // tlb miss
+ if (t == MISS)
+ return false;
+
+ if (ICACHE)
+ {
+ if (t == UNCACHED)
+ {
+ apply(space(0).read_dword(address));
+
+ return true;
+ }
+
+ // look up the tag
+ u32 const cache_address = (program_address & m_icache_mask_hi);
+ u32 &tag = m_icache_tag[cache_address >> m_icache_shift];
+
+ // check for cache miss
+ if (!(tag & ICACHE_V) || (tag & ICACHE_PTAG) != (address >> 12))
+ {
+ // cache miss
+ m_icache_misses++;
+
+ // reload the cache line
+ tag = ICACHE_V | (address >> 12);
+ for (unsigned i = 0; i < m_icache_line_size; i += 8)
+ {
+ u64 const data = space(0).read_qword((address & m_icache_mask_lo) | i);
+
+ m_icache_data[(((cache_address & m_icache_mask_lo) | i) >> 2) + 0] = u32(data);
+ m_icache_data[(((cache_address & m_icache_mask_lo) | i) >> 2) + 1] = data >> 32;
+ }
+ }
+ else
+ m_icache_hits++;
+
+ // apply the result
+ apply(m_icache_data[cache_address >> 2]);
+ }
+ else
+ apply(space(0).read_dword(address));
+
+ return true;
+}
+
+std::string r4000_base_device::debug_string(u64 string_pointer, unsigned 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 r4000_base_device::debug_string_array(u64 array_pointer)
+{
+ auto const suppressor(machine().disable_side_effects());
+
+ bool done = false;
+ std::string result("");
+
+ while (!done)
+ {
+ done = true;
+ load<s32>(array_pointer, [this, &done, &result](u64 string_pointer)
+ {
+ if (string_pointer != 0)
+ {
+ if (!result.empty())
+ result += ", ";
+
+ result += '\"' + debug_string(string_pointer) + '\"';
+
+ done = false;
+ }
+ });
+
+ array_pointer += 4;
+ }
+
+ return result;
+}
+
+std::string r4000_base_device::debug_unicode_string(u64 unicode_string_pointer)
+{
+ auto const suppressor(machine().disable_side_effects());
+
+ std::wstring result(L"");
+
+ if (!load<u16>(unicode_string_pointer,
+ [this, unicode_string_pointer, &result](u16 const length)
+ {
+ if (length)
+ if (!load<u32>(unicode_string_pointer + 4,
+ [this, length, &result](s32 buffer)
+ {
+ for (int i = 0; i < length; i += 2)
+ load<u16>(buffer + i, [&result](wchar_t const character) { result += character; });
+ }))
+ result.assign(L"[unmapped]");
+ }))
+ result.assign(L"[unmapped]");
+
+ return utf8_from_wstring(result);
+}
diff --git a/src/devices/cpu/mips/r4000.h b/src/devices/cpu/mips/r4000.h
new file mode 100644
index 00000000000..8c3a1760ba3
--- /dev/null
+++ b/src/devices/cpu/mips/r4000.h
@@ -0,0 +1,463 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+#ifndef MAME_CPU_MIPS_R4000_H
+#define MAME_CPU_MIPS_R4000_H
+
+#pragma once
+
+DECLARE_DEVICE_TYPE(R4000, r4000_device)
+DECLARE_DEVICE_TYPE(R4400, r4400_device)
+DECLARE_DEVICE_TYPE(R4600, r4600_device)
+
+class r4000_base_device : public cpu_device
+{
+public:
+ enum config_mask : u32
+ {
+ CONFIG_K0 = 0x00000007, // kseg0 cache coherency
+ CONFIG_CU = 0x00000080, // store conditional cache coherent
+ CONFIG_DB = 0x00000010, // primary d-cache line 32 bytes
+ CONFIG_IB = 0x00000020, // primary i-cache line 32 bytes
+ CONFIG_DC = 0x000001c0, // primary d-cache size
+ CONFIG_IC = 0x00000e00, // primary i-cache size
+ CONFIG_EB = 0x00002000, // sub-block ordering
+ CONFIG_EM = 0x00004000, // parity mode enable
+ CONFIG_BE = 0x00008000, // big endian
+ CONFIG_SM = 0x00010000, // dirty shared disable
+ CONFIG_SC = 0x00020000, // secondary cache absent
+ CONFIG_EW = 0x000c0000, // system port width
+ CONFIG_SW = 0x00100000, // secondary cache port width
+ CONFIG_SS = 0x00200000, // split secondary cache mode
+ CONFIG_SB = 0x00c00000, // secondary cache line size
+ CONFIG_EP = 0x0f000000, // transmit data pattern
+ CONFIG_EC = 0x70000000, // system clock ratio
+ CONFIG_CM = 0x80000000, // master/checker enable
+
+ CONFIG_WM = 0x0000003f, // runtime-writable bits
+ };
+ void set_config(u32 data, u32 mem_mask = ~u32(0))
+ {
+ if (!configured())
+ COMBINE_DATA(&m_cp0[CP0_Config]);
+ }
+
+protected:
+ enum cache_size_t
+ {
+ CACHE_4K = 0,
+ CACHE_8K = 1,
+ CACHE_16K = 2,
+ CACHE_32K = 3,
+ CACHE_64K = 4,
+ CACHE_128K = 5,
+ CACHE_256K = 6,
+ CACHE_512K = 7,
+ };
+ r4000_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u32 prid, cache_size_t icache_size, cache_size_t dcache_size);
+
+ enum cp0_reg : int
+ {
+ CP0_Index = 0,
+ CP0_Random = 1,
+ CP0_EntryLo0 = 2,
+ CP0_EntryLo1 = 3,
+ CP0_Context = 4,
+ CP0_PageMask = 5,
+ CP0_Wired = 6,
+ CP0_BadVAddr = 8,
+ CP0_Count = 9,
+ CP0_EntryHi = 10,
+ CP0_Compare = 11,
+ CP0_Status = 12,
+ CP0_Cause = 13,
+ CP0_EPC = 14,
+ CP0_PRId = 15,
+ CP0_Config = 16,
+ CP0_LLAddr = 17,
+ CP0_WatchLo = 18,
+ CP0_WatchHi = 19,
+ CP0_XContext = 20,
+ CP0_ECC = 26,
+ CP0_CacheErr = 27,
+ CP0_TagLo = 28,
+ CP0_TagHi = 29,
+ CP0_ErrorEPC = 30,
+ };
+
+ enum cp0_sr_mask : u32
+ {
+ SR_IE = 0x00000001, // interrupt enable
+ SR_EXL = 0x00000002, // exception level
+ SR_ERL = 0x00000004, // error level
+ SR_KSU = 0x00000018, // kernel/supervisor/user mode
+ SR_UX = 0x00000020, // 64-bit addressing user mode
+ SR_SX = 0x00000040, // 64-bit addressing supervisor mode
+ SR_KX = 0x00000080, // 64-bit addressing kernel mode
+ SR_IMSW0 = 0x00000100, // software interrupt 0 enable
+ SR_IMSW1 = 0x00000200, // software interrupt 1 enable
+ SR_IMEX0 = 0x00000400, // external interrupt 0 enable
+ SR_IMEX1 = 0x00000800, // external interrupt 1 enable
+ SR_IMEX2 = 0x00001000, // external interrupt 2 enable
+ SR_IMEX3 = 0x00002000, // external interrupt 3 enable
+ SR_IMEX4 = 0x00004000, // external interrupt 4 enable
+ SR_IMEX5 = 0x00008000, // external interrupt 5 enable
+ SR_DE = 0x00010000, // disable cache parity/ecc exceptions
+ SR_CE = 0x00020000, // cache ecc check enable
+ SR_CH = 0x00040000, // cache hit
+ SR_SR = 0x00100000, // soft reset
+ SR_TS = 0x00200000, // tlb shutdown (read only)
+ SR_BEV = 0x00400000, // bootstrap exception vectors
+ SR_RE = 0x02000000, // reverse endian
+ SR_FR = 0x04000000, // enable additional floating-point registers
+ SU_RP = 0x08000000, // reduced power
+ SR_CU0 = 0x10000000, // coprocessor usable 0
+ SR_CU1 = 0x20000000, // coprocessor usable 1
+ SR_CU2 = 0x40000000, // coprocessor usable 2
+ SR_CU3 = 0x80000000, // coprocessor usable 3
+
+ SR_IMSW = 0x00000300,
+ SR_IM = 0x0000ff00, // interrupt mask
+ SR_DS = 0x01ff0000, // diagnostic status
+ };
+
+ enum cp0_sr_ksu_mode : u32
+ {
+ SR_KSU_K = 0x00000000, // kernel mode
+ SR_KSU_S = 0x00000008, // supervisor mode
+ SR_KSU_U = 0x00000010, // user mode
+ };
+
+ enum cp0_cause_mask : u32
+ {
+ CAUSE_EXCCODE = 0x0000007c, // exception code
+ CAUSE_IPSW0 = 0x00000100, // software interrupt 0 pending
+ CAUSE_IPSW1 = 0x00000200, // software interrupt 1 pending
+ CAUSE_IPEX0 = 0x00000400, // external interrupt 0 pending
+ CAUSE_IPEX1 = 0x00000800, // external interrupt 1 pending
+ CAUSE_IPEX2 = 0x00001000, // external interrupt 2 pending
+ CAUSE_IPEX3 = 0x00002000, // external interrupt 3 pending
+ CAUSE_IPEX4 = 0x00004000, // external interrupt 4 pending
+ CAUSE_IPEX5 = 0x00008000, // external interrupt 5 pending
+ CAUSE_CE = 0x30000000, // coprocessor unit
+ CAUSE_BD = 0x80000000, // branch delay slot
+
+ CAUSE_IPSW = 0x00000300,
+ CAUSE_IPHW = 0x0000fc00,
+ CAUSE_IP = 0x0000ff00,
+ };
+
+ enum cp0_watchlo_mask : u32
+ {
+ WATCHLO_W = 0x00000001, // trap on store
+ WATCHLO_R = 0x00000002, // trap on load
+ WATCHLO_PADDR0 = 0xfffffff8, // physical address bits 31:3
+ };
+ enum cp0_watchhi_mask : u32
+ {
+ WATCHHI_PADDR1 = 0x0000000f, // physical address bits 35:32
+ };
+
+ enum cp0_tlb_mask : u64
+ {
+ TLB_MASK = 0x0000'0000'01ff'e000,
+ };
+ enum cp0_tlb_eh : u64
+ {
+ EH_ASID = 0x0000'0000'0000'00ff, // address space id
+ EH_G = 0x0000'0000'0000'1000, // global (tlb only)
+ EH_VPN2_32 = 0x0000'0000'ffff'e000, // virtual page number (32-bit mode)
+ EH_VPN2_64 = 0x0000'00ff'ffff'e000, // virtual page number (64-bit mode)
+ EH_R = 0xc000'0000'0000'0000, // region (64-bit mode)
+ };
+ enum cp0_tlb_el : u64
+ {
+ EL_G = 0x0000'0000'0000'0001, // global (entrylo only)
+ EL_V = 0x0000'0000'0000'0002, // valid
+ EL_D = 0x0000'0000'0000'0004, // dirty
+ EL_C = 0x0000'0000'0000'0038, // coherency
+ EL_PFN = 0x0000'0000'3fff'ffc0, // page frame number
+ };
+ enum cp0_tlb_el_c : u64
+ {
+ C_0 = 0x00, // reserved
+ C_1 = 0x08, // reserved
+ C_2 = 0x10, // uncached
+ C_3 = 0x18, // cacheable noncoherent (noncoherent)
+ C_4 = 0x20, // cacheable coherent exclusive (exclusive)
+ C_5 = 0x28, // cacheable coherent exclusive on write (sharable)
+ C_6 = 0x30, // cacheable coherent update on write (update)
+ C_7 = 0x38, // reserved
+ };
+
+ enum cp0_context_mask : u64
+ {
+ CONTEXT_PTEBASE = 0xffff'ffff'ff80'0000,
+ CONTEXT_BADVPN2 = 0x0000'0000'007f'fff0,
+ };
+
+ enum cp0_xcontext_mask : u64
+ {
+ XCONTEXT_PTEBASE = 0xffff'fffe'0000'0000, // page table entry base
+ XCONTEXT_R = 0x0000'0001'8000'0000, // region
+ XCONTEXT_BADVPN2 = 0x0000'0000'7fff'fff0, // bad virtual page number / 2
+ };
+
+ enum cp0_pagemask_mask : u32
+ {
+ PAGEMASK = 0x01ff'e000,
+ };
+
+ enum exception_mask : u32
+ {
+ EXCEPTION_INT = 0x00000000, // interrupt
+ EXCEPTION_MOD = 0x00000004, // tlb modification
+ EXCEPTION_TLBL = 0x00000008, // tlb load
+ EXCEPTION_TLBS = 0x0000000c, // tlb store
+ EXCEPTION_ADEL = 0x00000010, // address error load
+ EXCEPTION_ADES = 0x00000014, // address error store
+ EXCEPTION_IBE = 0x00000018, // bus error (instruction fetch)
+ EXCEPTION_DBE = 0x0000001c, // bus error (data reference: load or store)
+ EXCEPTION_SYS = 0x00000020, // syscall
+ EXCEPTION_BP = 0x00000024, // breakpoint
+ EXCEPTION_RI = 0x00000028, // reserved instruction
+ EXCEPTION_CPU = 0x0000002c, // coprocessor unusable
+ EXCEPTION_OV = 0x00000030, // arithmetic overflow
+ EXCEPTION_TR = 0x00000034, // trap
+ EXCEPTION_VCEI = 0x00000038, // virtual coherency exception instruction
+ EXCEPTION_FPE = 0x0000003c, // floating point
+ EXCEPTION_WATCH = 0x0000005c, // reference to watchhi/watchlo address
+ EXCEPTION_VCED = 0x0000007c, // virtual coherency exception data
+
+ EXCEPTION_CP0 = 0x0000002c, // coprocessor 0 unusable
+ EXCEPTION_CP1 = 0x1000002c, // coprocessor 1 unusable
+ EXCEPTION_CP2 = 0x2000002c, // coprocessor 2 unusable
+ EXCEPTION_CP3 = 0x3000002c, // coprocessor 3 unusable
+ };
+
+ enum cp1_fcr31_mask : u32
+ {
+ FCR31_RM = 0x00000003, // rounding mode
+
+ FCR31_FI = 0x00000004, // inexact operation flag
+ FCR31_FU = 0x00000008, // underflow flag
+ FCR31_FO = 0x00000010, // overflow flag
+ FCR31_FZ = 0x00000020, // divide by zero flag
+ FCR31_FV = 0x00000040, // invalid operation flag
+
+ FCR31_EI = 0x00000080, // inexact operation enable
+ FCR31_EU = 0x00000100, // underflow enable
+ FCR31_EO = 0x00000200, // overflow enable
+ FCR31_EZ = 0x00000400, // divide by zero enable
+ FCR31_EV = 0x00000800, // invalid operation enable
+
+ FCR31_CI = 0x00001000, // inexact operation cause
+ FCR31_CU = 0x00002000, // underflow cause
+ FCR31_CO = 0x00004000, // overflow cause
+ FCR31_CZ = 0x00008000, // divide by zero cause
+ FCR31_CV = 0x00010000, // invalid operation cause
+ FCR31_CE = 0x00020000, // unimplemented operation cause
+
+ FCR31_C = 0x00800000, // condition
+ FCR31_FS = 0x01000000, // flush denormalized results
+
+ FCR31_FM = 0x0000007c, // flag mask
+ FCR31_EM = 0x00000f80, // enable mask
+ FCR31_CM = 0x0001f000, // cause mask (except unimplemented)
+ };
+
+ enum mips3_registers : unsigned
+ {
+ MIPS3_R0 = 0,
+ MIPS3_CP0 = 32,
+ MIPS3_F0 = 64,
+
+ MIPS3_PC = 96,
+ MIPS3_HI,
+ MIPS3_LO,
+ MIPS3_FCR30,
+ MIPS3_FCR31,
+ };
+
+ enum cp0_taglo_mask : u32
+ {
+ TAGLO_PTAGLO = 0xffffff00, // physical adddress bits 35:12
+ TAGLO_PSTATE = 0x000000c0, // primary cache state
+ TAGLO_P = 0x00000001, // primary tag even parity
+ };
+ enum icache_mask : u32
+ {
+ ICACHE_PTAG = 0x00ffffff, // physical tag
+ ICACHE_V = 0x01000000, // valid
+ ICACHE_P = 0x02000000, // even parity
+ };
+ enum dcache_mask : u32
+ {
+ DCACHE_PTAG = 0x00ffffff, // physical tag
+ DCACHE_CS = 0x01000000, // primary cache state
+ DCACHE_P = 0x02000000, // even parity for ptag and cs
+ DCACHE_W = 0x02000000, // write-back
+ DCACHE_WP = 0x02000000, // even parity for write-back
+ };
+
+ // device_t overrides
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_stop() override;
+
+ // device_memory_interface overrides
+ virtual space_config_vector memory_space_config() const override;
+ virtual bool memory_translate(int spacenum, int intention, offs_t &address) override;
+
+ // device_disasm_interface overrides
+ virtual std::unique_ptr<util::disasm_interface> create_disassembler() override;
+
+ // device_execute_interface overrides
+ virtual u32 execute_min_cycles() const override { return 1; }
+ virtual u32 execute_max_cycles() const override { return 40; }
+ virtual u32 execute_input_lines() const override { return 6; }
+ virtual void execute_run() override;
+ virtual void execute_set_input(int inputnum, int state) override;
+
+ // cpu implementation
+ void cpu_execute(u32 const op);
+ void cpu_exception(u32 exception, u16 const vector = 0x180);
+ void cpu_lwl(u32 const op);
+ void cpu_lwr(u32 const op);
+ void cpu_ldl(u32 const op);
+ void cpu_ldr(u32 const op);
+ void cpu_swl(u32 const op);
+ void cpu_swr(u32 const op);
+ void cpu_sdl(u32 const op);
+ void cpu_sdr(u32 const op);
+
+ // cp0 implementation
+ void cp0_execute(u32 const op);
+ u64 cp0_get(unsigned const reg);
+ void cp0_set(unsigned const reg, u64 const data);
+ void cp0_tlbr();
+ void cp0_tlbwi(u8 const index);
+ void cp0_tlbwr();
+ void cp0_tlbp();
+
+ // cp0 helpers
+ TIMER_CALLBACK_MEMBER(cp0_timer_callback);
+ void cp0_update_timer(bool start = false);
+ bool cp0_64() const;
+
+ // cp1 implementation
+ void cp1_execute(u32 const op);
+ void cp1_set(unsigned const reg, u64 const data);
+
+ // cp2 implementation
+ void cp2_execute(u32 const op);
+
+ // address and memory handling
+ enum translate_t { ERROR, MISS, UNCACHED, CACHED };
+ translate_t translate(int intention, u64 &address);
+ void address_error(int intention, u64 const address);
+
+ template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, bool> load(u64 program_address, U &&apply);
+ template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(u64, T)>>::value, bool> load_linked(u64 program_address, U &&apply);
+ template <typename T, typename U> std::enable_if_t<std::is_convertible<U, T>::value, bool> store(u64 program_address, U data, T mem_mask = ~T(0));
+ bool fetch(u64 address, std::function<void(u32)> &&apply);
+
+ // debugging helpers
+ std::string debug_string(u64 string_pointer, unsigned limit = 0);
+ std::string debug_string_array(u64 array_pointer);
+ std::string debug_unicode_string(u64 unicode_string_pointer);
+
+ // device configuration state
+ address_space_config m_program_config_le;
+ address_space_config m_program_config_be;
+
+ // runtime state
+ int m_icount;
+
+ // cpu state
+ u64 m_pc;
+ u64 m_r[32];
+ u64 m_hi;
+ u64 m_lo;
+ enum branch_state_t : unsigned
+ {
+ NONE = 0,
+ DELAY = 1, // delay slot instruction active
+ BRANCH = 2, // branch instruction active
+ EXCEPTION = 3, // exception triggered
+ NULLIFY = 4, // next instruction nullified
+ }
+ m_branch_state;
+ u64 m_branch_target;
+
+ // cp0 state
+ u64 m_cp0[32];
+ u64 m_cp0_timer_zero;
+ emu_timer *m_cp0_timer;
+ memory_passthrough_handler *m_ll_watch;
+ struct tlb_entry_t
+ {
+ u64 mask;
+ u64 vpn;
+ u64 pfn[2];
+
+ unsigned low_bit;
+ }
+ m_tlb[48];
+ unsigned m_tlb_mru[3][48];
+
+ // cp1 state
+ u64 m_f[32]; // floating point registers
+ u32 m_fcr0; // implementation and revision register
+ u32 m_fcr30; // unknown
+ u32 m_fcr31; // control/status register
+
+ // experimental icache state
+ u32 m_icache_mask_hi;
+ u32 m_icache_mask_lo;
+ unsigned m_icache_line_size;
+ unsigned m_icache_shift;
+ std::unique_ptr<u32[]> m_icache_tag;
+ std::unique_ptr<u32[]> m_icache_data;
+
+ // statistics
+ u64 m_tlb_scans;
+ u64 m_tlb_loops;
+ u64 m_icache_hits;
+ u64 m_icache_misses;
+};
+
+class r4000_device : public r4000_base_device
+{
+public:
+ // NOTE: R4000 chips prior to 3.0 have an xtlb bug
+ r4000_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : r4000_base_device(mconfig, R4000, tag, owner, clock, 0x0430, CACHE_8K, CACHE_8K)
+ {
+ // no secondary cache
+ m_cp0[CP0_Config] |= CONFIG_SC;
+ }
+};
+
+class r4400_device : public r4000_base_device
+{
+public:
+ r4400_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : r4000_base_device(mconfig, R4400, tag, owner, clock, 0x0440, CACHE_16K, CACHE_16K)
+ {
+ // no secondary cache
+ m_cp0[CP0_Config] |= CONFIG_SC;
+ }
+};
+
+class r4600_device : public r4000_base_device
+{
+public:
+ r4600_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : r4000_base_device(mconfig, R4600, tag, owner, clock, 0x2000, CACHE_16K, CACHE_16K)
+ {
+ // no secondary cache
+ m_cp0[CP0_Config] |= CONFIG_SC;
+ }
+};
+#endif // MAME_CPU_MIPS_R4000_H