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