summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/mips/mips1.cpp
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/mips1.cpp
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/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;