summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Patrick Mackinlay <pmackinlay@hotmail.com>2019-02-26 18:54:04 +0700
committer Patrick Mackinlay <pmackinlay@hotmail.com>2019-02-26 18:54:04 +0700
commite02a5cf1f526385363d874fa6fbe3ffacb217e4e (patch)
tree24688348450f8ab1b97178bbca5176ca9825f902
parent330c498e5d5e6f592551db2b4c07c753f2d1a29f (diff)
mips1: fpu emulation
Code refactoring makes the changes hard to isolate, but the main improvements are: * implemented fpu instructions and exceptions * corrected swl/swr implementation * tlb mru lookup optimization * interrupt and privilege debugger breakpoints
-rw-r--r--src/devices/cpu/mips/mips1.cpp1928
-rw-r--r--src/devices/cpu/mips/mips1.h152
-rw-r--r--src/mame/drivers/decstation.cpp6
-rw-r--r--src/mame/drivers/mips.cpp5
4 files changed, 1359 insertions, 732 deletions
diff --git a/src/devices/cpu/mips/mips1.cpp b/src/devices/cpu/mips/mips1.cpp
index ff629e04bcc..cc6fd58343f 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,6 +15,7 @@
#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)
@@ -32,23 +31,16 @@
#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")
@@ -71,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)
@@ -83,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)
{
}
@@ -127,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)
@@ -154,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
@@ -178,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()
@@ -228,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()
@@ -242,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
@@ -266,11 +611,62 @@ 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::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))
@@ -323,7 +719,7 @@ void mips1core_device_base::generate_exception(u32 exception, bool refill)
static char const *const sem_syscalls[] = { "semctl", "semget", "semop" };
static char const *const mips_syscalls[] = { "mipskopt", "mipshwconf", "mipsgetrusage", "mipswait", "mipscacheflush", "mipscachectl" };
- int const asid = (m_cpr[0][COP0_EntryHi] & EH_ASID) >> 6;
+ unsigned const asid = (m_cop0[COP0_EntryHi] & EH_ASID) >> 6;
switch (m_r[2])
{
case 1000: // indirect
@@ -422,7 +818,7 @@ void mips1core_device_base::generate_exception(u32 exception, bool refill)
}
// set the exception PC
- m_cpr[0][COP0_EPC] = m_pc;
+ m_cop0[COP0_EPC] = m_pc;
// load the cause register
CAUSE = (CAUSE & CAUSE_IP) | exception;
@@ -430,7 +826,7 @@ void mips1core_device_base::generate_exception(u32 exception, bool refill)
// if in a branch delay slot, restart the branch
if (m_branch_state == DELAY)
{
- m_cpr[0][COP0_EPC] -= 4;
+ m_cop0[COP0_EPC] -= 4;
CAUSE |= CAUSE_BD;
}
m_branch_state = EXCEPTION;
@@ -444,6 +840,9 @@ void mips1core_device_base::generate_exception(u32 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()
@@ -452,101 +851,296 @@ void mips1core_device_base::check_irqs()
generate_exception(EXCEPTION_INTERRUPT);
}
-void mips1core_device_base::set_irq_line(int irqline, int state)
+void mips1core_device_base::handle_cop0(u32 const op)
{
- if (state != CLEAR_LINE)
- CAUSE |= CAUSE_IPEX0 << irqline;
- else
- CAUSE &= ~(CAUSE_IPEX0 << irqline);
+ 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;
+ }
+}
- check_irqs();
+u32 mips1core_device_base::get_cop0_reg(unsigned const reg)
+{
+ return m_cop0[reg];
+}
+
+void mips1core_device_base::set_cop0_reg(unsigned const reg, u32 const data)
+{
+ switch (reg)
+ {
+ case COP0_Context:
+ m_cop0[COP0_Context] = (m_cop0[COP0_Context] & ~PTE_BASE) | (data & PTE_BASE);
+ break;
+ case COP0_Status:
+ {
+ 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;
+ }
}
-u32 mips1core_device_base::get_cop0_reg(int const index)
+void mips1core_device_base::handle_cop1(u32 const op)
{
- return m_cpr[0][index];
+ if (!(SR & SR_COP1))
+ generate_exception(EXCEPTION_BADCOP1);
}
-u32 mips1_device_base::get_cop0_reg(int const index)
+void mips1core_device_base::handle_cop2(u32 const op)
{
- // assume 64-entry tlb with 8 wired entries
- if (index == COP0_Random)
- m_cpr[0][index] = (63 - ((total_cycles() - m_reset_time) % 56)) << 8;
+ if (!(SR & SR_COP2))
+ generate_exception(EXCEPTION_BADCOP2);
+}
- return m_cpr[0][index];
+void mips1core_device_base::handle_cop3(u32 const op)
+{
+ if (!(SR & SR_COP3))
+ generate_exception(EXCEPTION_BADCOP3);
}
-void mips1core_device_base::set_cop0_reg(int const index, u32 const data)
+void mips1core_device_base::lwl(u32 const op)
{
- if (index == COP0_Cause)
+ offs_t const offset = SIMMVAL + m_r[RSREG];
+ load<u32>(offset & ~3, [this, op, offset](u32 temp)
{
- CAUSE = (CAUSE & CAUSE_IPEX) | (data & ~CAUSE_IPEX);
+ unsigned const shift = ((offset & 3) ^ ENDIAN_VALUE_LE_BE(m_endianness, 3, 0)) << 3;
- // update interrupts -- software ints can occur this way
- check_irqs();
+ 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;
+ }
}
- else if (index == COP0_Status)
+}
+
+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))
{
- m_cpr[0][index] = data;
+ 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;
+ }
+ }
+}
- // handle cache isolation and swap
- m_data_spacenum = (data & SR_IsC) ? ((data & SR_SwC) ? 1 : 2) : 0;
+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));
- // update interrupts
- check_irqs();
+ return true;
}
- 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;
+ else
+ return false;
}
-void mips1core_device_base::handle_cop0(u32 const op)
+std::string mips1core_device_base::debug_string(u32 string_pointer, unsigned const limit)
{
- switch (RSREG)
+ auto const suppressor(machine().disable_side_effects());
+
+ bool done = false;
+ bool mapped = false;
+ std::string result("");
+
+ while (!done)
{
- 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 (RTREG)
+ done = true;
+ load<u8>(string_pointer++, [limit, &done, &mapped, &result](u8 byte)
+ {
+ mapped = true;
+ if (byte != 0)
{
- 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;
+ result += byte;
+
+ done = result.length() == limit;
}
- 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)
+ });
+ }
+
+ 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)
{
- case 0x01: /* TLBR */ break;
- case 0x02: /* TLBWI */ break;
- case 0x06: /* TLBWR */ break;
- case 0x08: /* TLBP */ break;
- case 0x10: /* RFE */ SR = (SR & ~SR_KUIEpc) | ((SR >> 2) & SR_KUIEpc); break;
- case 0x18: /* ERET */ generate_exception(EXCEPTION_INVALIDOP); break;
- default: generate_exception(EXCEPTION_INVALIDOP); break;
+ if (!result.empty())
+ result += ", ";
+
+ result += '\"' + debug_string(string_pointer) + '\"';
+
+ done = false;
}
- break;
- default: generate_exception(EXCEPTION_INVALIDOP); break;
+ });
+
+ 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;
}
}
@@ -556,26 +1150,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;
@@ -584,37 +1178,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:
@@ -622,495 +1216,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 (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)
+ // update rounding mode
+ switch (m_fcr31 & FCR31_RM)
{
- load<u8>(ptr, [](char c) { printf("%c", c); });
- ptr++;
- length--;
+ 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;
}
- fflush(stdout);
- }
- }
- // 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_BADCOP0);
- break;
- case 0x11: // COP1
- if (SR & SR_COP1)
- handle_cop1(op);
- else
- generate_exception(EXCEPTION_BADCOP1);
- break;
- case 0x12: // COP2
- if (SR & SR_COP2)
- handle_cop<2>(op);
- else
- generate_exception(EXCEPTION_BADCOP2);
- break;
- case 0x13: // COP3
- if (SR & SR_COP3)
- handle_cop<3>(op);
- else
- generate_exception(EXCEPTION_BADCOP3);
- 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)
{
- if (!machine().side_effects_disabled())
- {
- // 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)
@@ -1138,7 +1795,7 @@ bool mips1_device_base::memory_translate(int spacenum, int intention, offs_t &ad
if (!machine().side_effects_disabled())
{
// exception
- m_cpr[0][COP0_BadVAddr] = address;
+ m_cop0[COP0_BadVAddr] = address;
generate_exception((intention & TRANSLATE_WRITE) ? EXCEPTION_ADDRSTORE : EXCEPTION_ADDRLOAD);
}
@@ -1147,12 +1804,18 @@ bool mips1_device_base::memory_translate(int spacenum, int intention, offs_t &ad
}
// 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 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))
@@ -1176,6 +1839,11 @@ bool mips1_device_base::memory_translate(int spacenum, int intention, offs_t &ad
// 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;
}
@@ -1185,77 +1853,19 @@ bool mips1_device_base::memory_translate(int spacenum, int intention, offs_t &ad
{
if (modify)
LOGMASKED(LOG_TLB, "tlb modify asid %d address 0x%08x (%s)\n",
- (m_cpr[0][COP0_EntryHi] & EH_ASID) >> 6, address, machine().describe_context());
+ (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_cpr[0][COP0_EntryHi] & EH_ASID) >> 6, address, machine().describe_context());
+ (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(modify ? EXCEPTION_TLBMOD : (intention & TRANSLATE_WRITE) ? EXCEPTION_TLBSTORE : EXCEPTION_TLBLOAD, refill);
}
return false;
}
-
-std::string mips1core_device_base::debug_string(u32 string_pointer, int 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;
-}
diff --git a/src/devices/cpu/mips/mips1.h b/src/devices/cpu/mips/mips1.h
index 3b1f6126e20..3ae34e4b69f 100644
--- a/src/devices/cpu/mips/mips1.h
+++ b/src/devices/cpu/mips/mips1.h
@@ -9,21 +9,8 @@
class mips1core_device_base : public cpu_device
{
public:
- // floating point coprocessor revision numbers recognised by RISC/os 4.52 and IRIX
- enum fpu_rev_t : u32
- {
- MIPS_R2360 = 0x0100, // MIPS R2360 Floating Point Board
- MIPS_R2010 = 0x0200, // MIPS R2010 VLSI Floating Point Chip
- MIPS_R2010A = 0x0310, // MIPS R2010A VLSI Floating Point Chip
- MIPS_R3010 = 0x0320, // MIPS R3010 VLSI Floating Point Chip
- MIPS_R3010A = 0x0330, // MIPS R3010A VLSI Floating Point Chip
- MIPS_R3010Av4 = 0x0340, // MIPS R3010A VLSI Floating Point Chip
- MIPS_R6010 = 0x0400, // MIPS R6010 Floating Point Chip
- };
-
// device configuration
void set_endianness(endianness_t endianness) { m_endianness = endianness; }
- void set_fpurev(u32 revision) { m_hasfpu = true; m_fpurev = revision; }
// input lines
template <unsigned Coprocessor> auto in_brcond() { return m_in_brcond[Coprocessor].bind(); }
@@ -31,35 +18,17 @@ public:
protected:
mips1core_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u32 cpurev, size_t icache_size, size_t dcache_size);
- enum registers
+ enum registers : unsigned
{
- // general purpose cpu registers
- MIPS1_R0, MIPS1_R1, MIPS1_R2, MIPS1_R3, MIPS1_R4, MIPS1_R5, MIPS1_R6, MIPS1_R7,
- MIPS1_R8, MIPS1_R9, MIPS1_R10, MIPS1_R11, MIPS1_R12, MIPS1_R13, MIPS1_R14, MIPS1_R15,
- MIPS1_R16, MIPS1_R17, MIPS1_R18, MIPS1_R19, MIPS1_R20, MIPS1_R21, MIPS1_R22, MIPS1_R23,
- MIPS1_R24, MIPS1_R25, MIPS1_R26, MIPS1_R27, MIPS1_R28, MIPS1_R29, MIPS1_R30, MIPS1_R31,
+ MIPS1_R0 = 0,
+ MIPS1_COP0 = 32,
+ MIPS1_F0 = 64,
- // other cpu registers
+ MIPS1_PC = 80,
MIPS1_HI,
MIPS1_LO,
- MIPS1_PC,
-
- // coprocessor 0 registers
- MIPS1_COP0_INDEX, // reg 0, tlb only
- MIPS1_COP0_RANDOM, // reg 1, tlb only
- MIPS1_COP0_ENTRYLO, // reg 2, tlb only
- MIPS1_COP0_BUSCTRL, // reg 2, r3041 only
- MIPS1_COP0_CONFIG, // reg 3, r3041/r3071/r3081 only
- MIPS1_COP0_CONTEXT, // reg 4, tlb only
- MIPS1_COP0_BADVADDR, // reg 8
- MIPS1_COP0_COUNT, // reg 9, r3041 only
- MIPS1_COP0_ENTRYHI, // reg 10, tlb only
- MIPS1_COP0_PORTSIZE, // reg 10, r3041 only
- MIPS1_COP0_COMPARE, // reg 11, r3041 only
- MIPS1_COP0_SR, // reg 12
- MIPS1_COP0_CAUSE, // reg 13
- MIPS1_COP0_EPC, // reg 14
- MIPS1_COP0_PRID, // reg 15
+ MIPS1_FCR30,
+ MIPS1_FCR31,
};
enum exception : u32
@@ -75,12 +44,14 @@ protected:
EXCEPTION_SYSCALL = 0x00000020,
EXCEPTION_BREAK = 0x00000024,
EXCEPTION_INVALIDOP = 0x00000028,
+ EXCEPTION_BADCOP = 0x0000002c,
+ EXCEPTION_OVERFLOW = 0x00000030,
+ EXCEPTION_TRAP = 0x00000034,
+
EXCEPTION_BADCOP0 = 0x0000002c,
EXCEPTION_BADCOP1 = 0x1000002c,
EXCEPTION_BADCOP2 = 0x2000002c,
EXCEPTION_BADCOP3 = 0x3000002c,
- EXCEPTION_OVERFLOW = 0x00000030,
- EXCEPTION_TRAP = 0x00000034,
};
enum cop0_reg : u8
@@ -184,7 +155,7 @@ protected:
virtual u32 execute_max_cycles() const override { return 40; }
virtual u32 execute_input_lines() const override { return 6; }
virtual void execute_run() override;
- virtual void execute_set_input(int inputnum, int state) override { set_irq_line(inputnum, state); }
+ virtual void execute_set_input(int inputnum, int state) override;
// device_memory_interface overrides
virtual space_config_vector memory_space_config() const override;
@@ -199,23 +170,16 @@ protected:
// interrupts
void generate_exception(u32 exception, bool refill = false);
void check_irqs();
- void set_irq_line(int irqline, int state);
// cop0
- virtual u32 get_cop0_reg(int const index);
- void set_cop0_reg(int const index, u32 const data);
virtual void handle_cop0(u32 const op);
+ virtual u32 get_cop0_reg(unsigned const reg);
+ virtual void set_cop0_reg(unsigned const reg, u32 const data);
- // cop1
- void set_cop1_creg(int const index, u32 const data);
- void handle_cop1(u32 const op);
-
- // generic coprocessor implementation
- template <unsigned Coprocessor> void handle_cop(u32 const op);
- template <unsigned Coprocessor> u32 get_cop_reg(int const index) { return m_cpr[Coprocessor][index]; }
- template <unsigned Coprocessor> void set_cop_reg(int const index, u32 const data) { m_cpr[Coprocessor][index] = data; }
- template <unsigned Coprocessor> u32 get_cop_creg(int const index) { return m_ccr[Coprocessor][index]; }
- template <unsigned Coprocessor> void set_cop_creg(int const index, u32 const data) { m_ccr[Coprocessor][index] = data; }
+ // other coprocessors
+ virtual void handle_cop1(u32 const op);
+ virtual void handle_cop2(u32 const op);
+ virtual void handle_cop3(u32 const op);
// load/store left/right opcodes
void lwl(u32 const op);
@@ -224,12 +188,12 @@ protected:
void swr(u32 const op);
// memory accessors
- template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, void> load(u32 program_address, U &&apply);
- template <typename T, typename U> std::enable_if_t<std::is_convertible<U, T>::value, void> store(u32 program_address, U data);
- bool fetch(u32 program_address, std::function<void(u32)> &&apply);
+ template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, void> load(u32 address, U &&apply);
+ template <typename T, typename U> std::enable_if_t<std::is_convertible<U, T>::value, void> store(u32 address, U data, T mem_mask = ~T(0));
+ bool fetch(u32 address, std::function<void(u32)> &&apply);
// debug helpers
- std::string debug_string(u32 string_pointer, int const limit = 0);
+ std::string debug_string(u32 string_pointer, unsigned const limit = 0);
std::string debug_string_array(u32 array_pointer);
// address spaces
@@ -242,19 +206,16 @@ protected:
// configuration
u32 m_cpurev;
- bool m_hasfpu;
- u32 m_fpurev;
endianness_t m_endianness;
// core registers
u32 m_pc;
+ u32 m_r[32];
u32 m_hi;
u32 m_lo;
- u32 m_r[32];
- // COP registers
- u32 m_cpr[4][32];
- u32 m_ccr[4][32];
+ // cop0 registers
+ u32 m_cop0[32];
// internal stuff
int m_icount;
@@ -278,9 +239,54 @@ protected:
class mips1_device_base : public mips1core_device_base
{
+public:
+ // floating point coprocessor revision numbers recognised by RISC/os 4.52 and IRIX
+ enum fpu_rev_t : u32
+ {
+ MIPS_R2360 = 0x0100, // MIPS R2360 Floating Point Board
+ MIPS_R2010 = 0x0200, // MIPS R2010 VLSI Floating Point Chip
+ MIPS_R2010A = 0x0310, // MIPS R2010A VLSI Floating Point Chip
+ MIPS_R3010 = 0x0320, // MIPS R3010 VLSI Floating Point Chip
+ MIPS_R3010A = 0x0330, // MIPS R3010A VLSI Floating Point Chip
+ MIPS_R3010Av4 = 0x0340, // MIPS R3010A VLSI Floating Point Chip
+ MIPS_R6010 = 0x0400, // MIPS R6010 Floating Point Chip
+ };
+
+ void set_fpu(u32 revision, unsigned interrupt = 3) { m_fcr0 = revision; m_fpu_irq = interrupt; }
+
protected:
mips1_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u32 cpurev, size_t icache_size, size_t dcache_size);
+ enum cp1_fcr31_mask : u32
+ {
+ FCR31_RM = 0x00000003, // rounding mode
+
+ FCR31_FI = 0x00000004, // inexact operation flag
+ FCR31_FU = 0x00000008, // underflow flag
+ FCR31_FO = 0x00000010, // overflow flag
+ FCR31_FZ = 0x00000020, // divide by zero flag
+ FCR31_FV = 0x00000040, // invalid operation flag
+
+ FCR31_EI = 0x00000080, // inexact operation enable
+ FCR31_EU = 0x00000100, // underflow enable
+ FCR31_EO = 0x00000200, // overflow enable
+ FCR31_EZ = 0x00000400, // divide by zero enable
+ FCR31_EV = 0x00000800, // invalid operation enable
+
+ FCR31_CI = 0x00001000, // inexact operation cause
+ FCR31_CU = 0x00002000, // underflow cause
+ FCR31_CO = 0x00004000, // overflow cause
+ FCR31_CZ = 0x00008000, // divide by zero cause
+ FCR31_CV = 0x00010000, // invalid operation cause
+ FCR31_CE = 0x00020000, // unimplemented operation cause
+
+ FCR31_C = 0x00800000, // condition
+
+ FCR31_FM = 0x0000007c, // flag mask
+ FCR31_EM = 0x00000f80, // enable mask
+ FCR31_CM = 0x0001f000, // cause mask (except unimplemented)
+ };
+
// device_t overrides
virtual void device_start() override;
virtual void device_reset() override;
@@ -288,12 +294,24 @@ protected:
// device_memory_interface overrides
virtual bool memory_translate(int spacenum, int intention, offs_t &address) override;
- virtual u32 get_cop0_reg(int idx) override;
virtual void handle_cop0(u32 const op) override;
+ virtual u32 get_cop0_reg(unsigned const reg) override;
+
+ virtual void handle_cop1(u32 const op) override;
+ virtual void set_cop1_reg(unsigned const reg, u64 const data);
private:
u64 m_reset_time;
u32 m_tlb[64][2]; // 0 is hi, 1 is lo
+ unsigned m_tlb_mru[3][64];
+
+ // cop1 registers
+ u64 m_f[16];
+ u32 m_fcr0;
+ u32 m_fcr30;
+ u32 m_fcr31;
+
+ unsigned m_fpu_irq;
};
class r2000_device : public mips1_device_base
@@ -347,13 +365,13 @@ public:
r3052e_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
};
-class r3071_device : public mips1core_device_base
+class r3071_device : public mips1_device_base
{
public:
r3071_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, size_t icache_size = 16384, size_t dcache_size = 4096);
};
-class r3081_device : public mips1core_device_base
+class r3081_device : public mips1_device_base
{
public:
r3081_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, size_t icache_size = 16384, size_t dcache_size = 4096);
diff --git a/src/mame/drivers/decstation.cpp b/src/mame/drivers/decstation.cpp
index 322f94cd352..93e23d9fb0c 100644
--- a/src/mame/drivers/decstation.cpp
+++ b/src/mame/drivers/decstation.cpp
@@ -133,7 +133,7 @@ private:
uint32_t kn01_screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
- required_device<mips1core_device_base> m_maincpu;
+ required_device<mips1_device_base> m_maincpu;
required_device<screen_device> m_screen;
optional_device<timer_device> m_scantimer;
optional_device<decsfb_device> m_sfb;
@@ -546,7 +546,7 @@ static void dec_scsi_devices(device_slot_interface &device)
MACHINE_CONFIG_START(decstation_state::kn01)
R2000(config, m_maincpu, 16.67_MHz_XTAL, 65536, 131072);
m_maincpu->set_endianness(ENDIANNESS_LITTLE);
- m_maincpu->set_fpurev(0x340);
+ m_maincpu->set_fpu(mips1_device_base::MIPS_R3010Av4);
m_maincpu->in_brcond<0>().set(FUNC(decstation_state::brcond0_r));
m_maincpu->set_addrmap(AS_PROGRAM, &decstation_state::kn01_map);
@@ -570,7 +570,7 @@ MACHINE_CONFIG_END
MACHINE_CONFIG_START(decstation_state::kn02ba)
R3000A(config, m_maincpu, 33.333_MHz_XTAL, 65536, 131072);
m_maincpu->set_endianness(ENDIANNESS_LITTLE);
- m_maincpu->set_fpurev(0x340); // should be R3010A v4.0
+ m_maincpu->set_fpu(mips1_device_base::MIPS_R3010Av4);
m_maincpu->in_brcond<0>().set(FUNC(decstation_state::brcond0_r));
m_maincpu->set_addrmap(AS_PROGRAM, &decstation_state::threemin_map);
diff --git a/src/mame/drivers/mips.cpp b/src/mame/drivers/mips.cpp
index dd3a8b4f3e6..7b02b9161db 100644
--- a/src/mame/drivers/mips.cpp
+++ b/src/mame/drivers/mips.cpp
@@ -499,8 +499,7 @@ static void mips_scsi_devices(device_slot_interface &device)
void rx2030_state::rx2030(machine_config &config)
{
R2000A(config, m_cpu, 33.333_MHz_XTAL / 2, 32768, 32768);
- // TODO: FPU disabled until properly emulated
- //m_cpu->set_fpurev(mips1_device_base::MIPS_R2010A);
+ m_cpu->set_fpu(mips1_device_base::MIPS_R2010A);
m_cpu->in_brcond<0>().set([]() { return 1; }); // writeback complete
V50(config, m_iop, 20_MHz_XTAL / 2);
@@ -772,7 +771,7 @@ void rx3230_state::rx3230(machine_config &config)
{
R3000A(config, m_cpu, 50_MHz_XTAL / 2, 32768, 32768);
m_cpu->set_addrmap(AS_PROGRAM, &rx3230_state::rx3230_map);
- //m_cpu->set_fpurev(mips1_device_base::MIPS_R3010A);
+ m_cpu->set_fpu(mips1_device_base::MIPS_R3010A);
m_cpu->in_brcond<0>().set([]() { return 1; }); // writeback complete
// 32 SIMM slots, 8-128MB memory, banks of 8 1MB or 4MB SIMMs