summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/mips/r4000.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/mips/r4000.cpp')
-rw-r--r--src/devices/cpu/mips/r4000.cpp1416
1 files changed, 1123 insertions, 293 deletions
diff --git a/src/devices/cpu/mips/r4000.cpp b/src/devices/cpu/mips/r4000.cpp
index 7b26493f61c..47e2cd784a7 100644
--- a/src/devices/cpu/mips/r4000.cpp
+++ b/src/devices/cpu/mips/r4000.cpp
@@ -27,13 +27,17 @@
*/
#include "emu.h"
-#include "debugger.h"
#include "r4000.h"
+
#include "mips3dsm.h"
+#include "divtlb.h"
+#include "debug/debugcpu.h"
+
+#include "unicode.h"
+
#include "softfloat3/source/include/softfloat.h"
-#define LOG_GENERAL (1U << 0)
#define LOG_TLB (1U << 1)
#define LOG_CACHE (1U << 2)
#define LOG_EXCEPTION (1U << 3)
@@ -41,6 +45,7 @@
#define LOG_STATS (1U << 5)
#define VERBOSE (LOG_GENERAL)
+#include "logmacro.h"
// operating system specific system call logging
#define SYSCALL_IRIX53 (1U << 0)
@@ -54,11 +59,11 @@
// experimental primary instruction cache
#define ICACHE 0
-#include "logmacro.h"
+#define SCACHE !(m_cp0[CP0_Config] & CONFIG_SC)
#define USE_ABI_REG_NAMES 1
-// cpu instruction fiels
+// cpu instruction fields
#define RSREG ((op >> 21) & 31)
#define RTREG ((op >> 16) & 31)
#define RDREG ((op >> 11) & 31)
@@ -84,11 +89,20 @@
DEFINE_DEVICE_TYPE(R4000, r4000_device, "r4000", "MIPS R4000")
DEFINE_DEVICE_TYPE(R4400, r4400_device, "r4400", "MIPS R4400")
DEFINE_DEVICE_TYPE(R4600, r4600_device, "r4600", "QED R4600")
+DEFINE_DEVICE_TYPE(R5000, r5000_device, "r5000", "MIPS R5000")
+
+u32 const r5000_device::s_fcc_masks[8] = { (1U << 23), (1U << 25), (1U << 26), (1U << 27), (1U << 28), (1U << 29), (1U << 30), (1U << 31) };
+u32 const r5000_device::s_fcc_shifts[8] = { 23, 25, 26, 27, 28, 29, 30, 31 };
-r4000_base_device::r4000_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u32 prid, u32 fcr, cache_size_t icache_size, cache_size_t dcache_size)
+r4000_base_device::r4000_base_device(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock, u32 prid, u32 fcr, cache_size icache_size, cache_size dcache_size, unsigned m32, unsigned m64, unsigned d32, unsigned d64, bool timer_interrupt_disabled)
: cpu_device(mconfig, type, tag, owner, clock)
, m_program_config_le("program", ENDIANNESS_LITTLE, 64, 32)
, m_program_config_be("program", ENDIANNESS_BIG, 64, 32)
+ , m_hilo_cycles{ m32, m64, d32, d64 }
+ , m_r{}
+ , m_cp0{}
+ , m_timer_interrupt_disabled(timer_interrupt_disabled)
+ , m_f{}
, m_fcr0(fcr)
{
m_cp0[CP0_PRId] = prid;
@@ -175,7 +189,9 @@ void r4000_base_device::device_start()
set_icountptr(m_icount);
- m_cp0_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(r4000_base_device::cp0_timer_callback), this));
+ m_hard_reset = true;
+
+ m_cp0_timer = timer_alloc(FUNC(r4000_base_device::cp0_timer_callback), this);
// compute icache line selection mask and allocate tag and data
unsigned const config_ic = (m_cp0[CP0_Config] & CONFIG_IC) >> 9;
@@ -183,32 +199,49 @@ void r4000_base_device::device_start()
m_icache_mask_hi = (0x1000U << config_ic) - 1;
m_icache_tag = std::make_unique<u32[]>(0x100U << config_ic);
m_icache_data = std::make_unique<u32 []>((0x1000U << config_ic) >> 2);
+
+ R4000_ENDIAN_LE_BE(accessors(m_le), accessors(m_be));
+
+ if (SCACHE)
+ save_pointer(NAME(m_scache_tag), m_scache_tag_size);
}
void r4000_base_device::device_reset()
{
+ if (!m_hard_reset)
+ {
+ m_cp0[CP0_Status] = SR_BEV | SR_ERL | SR_SR;
+ m_cp0[CP0_ErrorEPC] = m_pc;
+ }
+ else
+ m_cp0[CP0_Status] = SR_BEV | SR_ERL;
+
+ m_hilo_delay = 0;
m_branch_state = NONE;
m_pc = s64(s32(0xbfc00000));
+ m_r[0] = 0;
- m_cp0[CP0_Status] = SR_BEV | SR_ERL;
m_cp0[CP0_Wired] = 0;
m_cp0[CP0_Compare] = 0;
m_cp0[CP0_Count] = 0;
m_cp0_timer_zero = total_cycles();
+ m_hard_reset = false;
m_ll_active = false;
m_bus_error = false;
+ m_cp0[CP0_Cause] = 0;
+
m_cp0[CP0_WatchLo] = 0;
m_cp0[CP0_WatchHi] = 0;
// initialize tlb mru index with identity mapping
- for (unsigned i = 0; i < ARRAY_LENGTH(m_tlb); i++)
+ for (unsigned i = 0; i < std::size(m_tlb); i++)
{
- m_tlb_mru[TRANSLATE_READ][i] = i;
- m_tlb_mru[TRANSLATE_WRITE][i] = i;
- m_tlb_mru[TRANSLATE_FETCH][i] = i;
+ m_tlb_mru[TR_READ][i] = i;
+ m_tlb_mru[TR_WRITE][i] = i;
+ m_tlb_mru[TR_FETCH][i] = i;
}
// initialize statistics
@@ -235,12 +268,13 @@ device_memory_interface::space_config_vector r4000_base_device::memory_space_con
};
}
-bool r4000_base_device::memory_translate(int spacenum, int intention, offs_t &address)
+bool r4000_base_device::memory_translate(int spacenum, int intention, offs_t &address, address_space *&target_space)
{
+ target_space = &space(spacenum);
// FIXME: address truncation
u64 placeholder = s32(address);
- translate_t const t = translate(intention, placeholder);
+ translate_result const t = translate(intention, true, placeholder);
if (t == ERROR || t == MISS)
return false;
@@ -256,7 +290,7 @@ std::unique_ptr<util::disasm_interface> r4000_base_device::create_disassembler()
void r4000_base_device::execute_run()
{
- while (m_icount-- > 0)
+ while (m_icount > 0)
{
debugger_instruction_hook(m_pc);
@@ -274,31 +308,32 @@ void r4000_base_device::execute_run()
});
// update pc and branch state
- switch (m_branch_state)
+ switch (m_branch_state & STATE)
{
case NONE:
m_pc += 4;
break;
- case DELAY:
+ case BRANCH:
+ m_pc = m_branch_state & TARGET;
m_branch_state = NONE;
- m_pc = m_branch_target;
break;
- case BRANCH:
- m_branch_state = DELAY;
+ case DELAY:
m_pc += 4;
- break;
-
- case EXCEPTION:
- m_branch_state = NONE;
+ m_branch_state = (m_branch_state & TARGET) | BRANCH;
break;
case NULLIFY:
- m_branch_state = NONE;
m_pc += 8;
+ m_branch_state = NONE;
break;
}
+
+ if (m_hilo_delay)
+ m_hilo_delay--;
+
+ m_icount--;
}
}
@@ -338,12 +373,12 @@ void r4000_base_device::cpu_execute(u32 const op)
m_r[RDREG] = s64(s32(m_r[RTREG]) >> (m_r[RSREG] & 31));
break;
case 0x08: // JR
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_r[RSREG], 0);
+ // FIXME: address error if low bits are set
+ m_branch_state = ADDR(m_r[RSREG], 0) | DELAY;
break;
case 0x09: // JALR
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_r[RSREG], 0);
+ // FIXME: address error if low bits are set
+ m_branch_state = ADDR(m_r[RSREG], 0) | DELAY;
m_r[RDREG] = ADDR(m_pc, 8);
break;
//case 0x0a: // *
@@ -407,12 +442,22 @@ void r4000_base_device::cpu_execute(u32 const op)
break;
case 0x10: // MFHI
m_r[RDREG] = m_hi;
+ if (m_hilo_delay)
+ {
+ m_icount -= m_hilo_delay;
+ m_hilo_delay = 0;
+ }
break;
case 0x11: // MTHI
m_hi = m_r[RSREG];
break;
case 0x12: // MFLO
m_r[RDREG] = m_lo;
+ if (m_hilo_delay)
+ {
+ m_icount -= m_hilo_delay;
+ m_hilo_delay = 0;
+ }
break;
case 0x13: // MTLO
m_lo = m_r[RSREG];
@@ -433,6 +478,7 @@ void r4000_base_device::cpu_execute(u32 const op)
m_lo = s64(s32(product));
m_hi = s64(s32(product >> 32));
+ m_hilo_delay = m_hilo_cycles[0];
}
break;
case 0x19: // MULTU
@@ -441,6 +487,7 @@ void r4000_base_device::cpu_execute(u32 const op)
m_lo = s64(s32(product));
m_hi = s64(s32(product >> 32));
+ m_hilo_delay = m_hilo_cycles[0];
}
break;
case 0x1a: // DIV
@@ -448,6 +495,7 @@ void r4000_base_device::cpu_execute(u32 const op)
{
m_lo = s64(s32(m_r[RSREG]) / s32(m_r[RTREG]));
m_hi = s64(s32(m_r[RSREG]) % s32(m_r[RTREG]));
+ m_hilo_delay = m_hilo_cycles[2];
}
break;
case 0x1b: // DIVU
@@ -455,19 +503,23 @@ void r4000_base_device::cpu_execute(u32 const op)
{
m_lo = s64(s32(u32(m_r[RSREG]) / u32(m_r[RTREG])));
m_hi = s64(s32(u32(m_r[RSREG]) % u32(m_r[RTREG])));
+ m_hilo_delay = m_hilo_cycles[2];
}
break;
case 0x1c: // DMULT
- m_lo = mul_64x64(m_r[RSREG], m_r[RTREG], reinterpret_cast<s64 *>(&m_hi));
+ m_lo = mul_64x64(m_r[RSREG], m_r[RTREG], *reinterpret_cast<s64 *>(&m_hi));
+ m_hilo_delay = m_hilo_cycles[1];
break;
case 0x1d: // DMULTU
- m_lo = mulu_64x64(m_r[RSREG], m_r[RTREG], &m_hi);
+ m_lo = mulu_64x64(m_r[RSREG], m_r[RTREG], m_hi);
+ m_hilo_delay = m_hilo_cycles[1];
break;
case 0x1e: // DDIV
if (m_r[RTREG])
{
m_lo = s64(m_r[RSREG]) / s64(m_r[RTREG]);
m_hi = s64(m_r[RSREG]) % s64(m_r[RTREG]);
+ m_hilo_delay = m_hilo_cycles[3];
}
break;
case 0x1f: // DDIVU
@@ -475,6 +527,7 @@ void r4000_base_device::cpu_execute(u32 const op)
{
m_lo = m_r[RSREG] / m_r[RTREG];
m_hi = m_r[RSREG] % m_r[RTREG];
+ m_hilo_delay = m_hilo_cycles[3];
}
break;
case 0x20: // ADD
@@ -601,10 +654,7 @@ void r4000_base_device::cpu_execute(u32 const op)
break;
default:
- // * Operation codes marked with an asterisk cause reserved
- // instruction exceptions in all current implementations and are
- // reserved for future versions of the architecture.
- cpu_exception(EXCEPTION_RI);
+ handle_reserved_instruction(op);
break;
}
break;
@@ -613,33 +663,21 @@ void r4000_base_device::cpu_execute(u32 const op)
{
case 0x00: // BLTZ
if (s64(m_r[RSREG]) < 0)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
+ m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY;
break;
case 0x01: // BGEZ
if (s64(m_r[RSREG]) >= 0)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
+ m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY;
break;
case 0x02: // BLTZL
if (s64(m_r[RSREG]) < 0)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
+ m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY;
else
m_branch_state = NULLIFY;
break;
case 0x03: // BGEZL
if (s64(m_r[RSREG]) >= 0)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
+ m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY;
else
m_branch_state = NULLIFY;
break;
@@ -675,36 +713,24 @@ void r4000_base_device::cpu_execute(u32 const op)
//case 0x0f: // *
case 0x10: // BLTZAL
if (s64(m_r[RSREG]) < 0)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
+ m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY;
m_r[31] = ADDR(m_pc, 8);
break;
case 0x11: // BGEZAL
if (s64(m_r[RSREG]) >= 0)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
+ m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY;
m_r[31] = ADDR(m_pc, 8);
break;
case 0x12: // BLTZALL
if (s64(m_r[RSREG]) < 0)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
+ m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY;
else
m_branch_state = NULLIFY;
m_r[31] = ADDR(m_pc, 8);
break;
case 0x13: // BGEZALL
if (s64(m_r[RSREG]) >= 0)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
+ m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY;
else
m_branch_state = NULLIFY;
m_r[31] = ADDR(m_pc, 8);
@@ -726,46 +752,32 @@ void r4000_base_device::cpu_execute(u32 const op)
// * Operation codes marked with an asterisk cause reserved
// instruction exceptions in all current implementations and are
// reserved for future versions of the architecture.
- cpu_exception(EXCEPTION_RI);
+ handle_reserved_instruction(op);
break;
}
break;
case 0x02: // J
- m_branch_state = BRANCH;
- m_branch_target = (ADDR(m_pc, 4) & ~0x0fffffffULL) | ((op & 0x03ffffffU) << 2);
+ m_branch_state = (ADDR(m_pc, 4) & ~0x0fffffffULL) | ((op & 0x03ffffffU) << 2) | DELAY;
break;
case 0x03: // JAL
- m_branch_state = BRANCH;
- m_branch_target = (ADDR(m_pc, 4) & ~0x0fffffffULL) | ((op & 0x03ffffffU) << 2);
+ m_branch_state = (ADDR(m_pc, 4) & ~0x0fffffffULL) | ((op & 0x03ffffffU) << 2) | DELAY;
m_r[31] = ADDR(m_pc, 8);
break;
case 0x04: // BEQ
if (m_r[RSREG] == m_r[RTREG])
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
+ m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY;
break;
case 0x05: // BNE
if (m_r[RSREG] != m_r[RTREG])
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
+ m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY;
break;
case 0x06: // BLEZ
if (s64(m_r[RSREG]) <= 0)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
+ m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY;
break;
case 0x07: // BGTZ
if (s64(m_r[RSREG]) > 0)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
+ m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY;
break;
case 0x08: // ADDI
{
@@ -808,40 +820,30 @@ void r4000_base_device::cpu_execute(u32 const op)
case 0x12: // COP2
cp2_execute(op);
break;
- //case 0x13: // *
+ case 0x13: // COP1X
+ cp1x_execute(op);
+ break;
case 0x14: // BEQL
if (m_r[RSREG] == m_r[RTREG])
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
+ m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY;
else
m_branch_state = NULLIFY;
break;
case 0x15: // BNEL
if (m_r[RSREG] != m_r[RTREG])
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
+ m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY;
else
m_branch_state = NULLIFY;
break;
case 0x16: // BLEZL
if (s64(m_r[RSREG]) <= 0)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
+ m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY;
else
m_branch_state = NULLIFY;
break;
case 0x17: // BGTZL
if (s64(m_r[RSREG]) > 0)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
+ m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY;
else
m_branch_state = NULLIFY;
break;
@@ -951,75 +953,7 @@ void r4000_base_device::cpu_execute(u32 const op)
cpu_swr(op);
break;
case 0x2f: // CACHE
- if ((SR & SR_KSU) && !(SR & SR_CU0) && !(SR & (SR_EXL | SR_ERL)))
- {
- cpu_exception(EXCEPTION_CP0);
- break;
- }
-
- switch ((op >> 16) & 0x1f)
- {
- case 0x00: // index invalidate (I)
- if (ICACHE)
- {
- m_icache_tag[(ADDR(m_r[RSREG], s16(op)) & m_icache_mask_hi) >> m_icache_shift] &= ~ICACHE_V;
- break;
- }
-
- case 0x04: // index load tag (I)
- if (ICACHE)
- {
- u32 const tag = m_icache_tag[(ADDR(m_r[RSREG], s16(op)) & m_icache_mask_hi) >> m_icache_shift];
-
- m_cp0[CP0_TagLo] = ((tag & ICACHE_PTAG) << 8) | ((tag & ICACHE_V) >> 18) | ((tag & ICACHE_P) >> 25);
- m_cp0[CP0_ECC] = 0; // data ecc or parity
-
- break;
- }
-
- case 0x08: // index store tag (I)
- if (ICACHE)
- {
- // FIXME: compute parity
- m_icache_tag[(ADDR(m_r[RSREG], s16(op)) & m_icache_mask_hi) >> m_icache_shift] =
- (m_cp0[CP0_TagLo] & TAGLO_PTAGLO) >> 8 | (m_cp0[CP0_TagLo] & TAGLO_PSTATE) << 18;
-
- break;
- }
-
- case 0x01: // index writeback invalidate (D)
- case 0x02: // index invalidate (SI)
- case 0x03: // index writeback invalidate (SD)
-
- case 0x05: // index load tag (D)
- case 0x06: // index load tag (SI)
- case 0x07: // index load tag (SI)
-
- case 0x09: // index store tag (D)
- case 0x0a: // index store tag (SI)
- case 0x0b: // index store tag (SD)
-
- case 0x0d: // create dirty exclusive (D)
- case 0x0f: // create dirty exclusive (SD)
-
- case 0x10: // hit invalidate (I)
- case 0x11: // hit invalidate (D)
- case 0x12: // hit invalidate (SI)
- case 0x13: // hit invalidate (SD)
-
- case 0x14: // fill (I)
- case 0x15: // hit writeback invalidate (D)
- case 0x17: // hit writeback invalidate (SD)
-
- case 0x18: // hit writeback (I)
- case 0x19: // hit writeback (D)
- case 0x1b: // hit writeback (SD)
-
- case 0x1e: // hit set virtual (SI)
- case 0x1f: // hit set virtual (SD)
- //LOGMASKED(LOG_CACHE, "cache 0x%08x unimplemented (%s)\n", op, machine().describe_context());
- break;
- }
+ cp0_cache(op);
break;
case 0x30: // LL
load_linked<u32>(ADDR(m_r[RSREG], s16(op)),
@@ -1036,7 +970,6 @@ void r4000_base_device::cpu_execute(u32 const op)
case 0x32: // LWC2
cp2_execute(op);
break;
- //case 0x33: // *
case 0x34: // LLD
load_linked<u64>(ADDR(m_r[RSREG], s16(op)),
[this, op](u64 address, u64 data)
@@ -1106,11 +1039,50 @@ void r4000_base_device::cpu_execute(u32 const op)
// * Operation codes marked with an asterisk cause reserved instruction
// exceptions in all current implementations and are reserved for future
// versions of the architecture.
- cpu_exception(EXCEPTION_RI);
+ handle_reserved_instruction(op);
break;
}
}
+void r4000_base_device::handle_reserved_instruction(u32 const op)
+{
+ // Unhandled operation codes cause reserved instruction
+ // exceptions in all current implementations and are
+ // reserved for future versions of the architecture.
+ cpu_exception(EXCEPTION_RI);
+}
+
+void r5000_device::handle_reserved_instruction(u32 const op)
+{
+ switch (op >> 26)
+ {
+ case 0x00: // SPECIAL
+ switch (op & 0x3f)
+ {
+ case 0x01: // MOVT / MOVF
+ if (((m_fcr31 >> s_fcc_shifts[(op >> 18) & 7]) & 1) == ((op >> 16) & 1))
+ {
+ if (RDREG)
+ {
+ m_r[RDREG] = m_r[RSREG];
+ }
+ }
+ return;
+ case 0x0a: // MOVZ
+ if (m_r[RTREG] == 0) { if (RDREG) m_r[RDREG] = m_r[RSREG]; }
+ return;
+ case 0x0b: // MOVN
+ if (m_r[RTREG] != 0) { if (RDREG) m_r[RDREG] = m_r[RSREG]; }
+ return;
+ }
+ break;
+ case 0x33: // PREF (effective no-op)
+ return;
+ }
+
+ r4000_base_device::handle_reserved_instruction(op);
+}
+
void r4000_base_device::cpu_exception(u32 exception, u16 const vector)
{
if (exception != EXCEPTION_INT)
@@ -1122,8 +1094,8 @@ void r4000_base_device::cpu_exception(u32 exception, u16 const vector)
CAUSE = (CAUSE & CAUSE_IP) | exception;
- // if in a branch delay slot, restart at the branch instruction
- if (m_branch_state == DELAY)
+ // if executing a delay slot instruction, restart from the branch
+ if ((m_branch_state & STATE) == BRANCH)
{
m_cp0[CP0_EPC] -= 4;
CAUSE |= CAUSE_BD;
@@ -1134,10 +1106,20 @@ void r4000_base_device::cpu_exception(u32 exception, u16 const vector)
else
CAUSE = (CAUSE & (CAUSE_BD | CAUSE_IP)) | exception;
- m_branch_state = EXCEPTION;
- m_pc = ((SR & SR_BEV) ? s64(s32(0xbfc00200)) : s64(s32(0x80000000))) + vector;
+ m_branch_state = (((SR & SR_BEV) ? s64(s32(0xbfc00200)) : s64(s32(0x80000000))) + vector) | BRANCH;
- if (exception != EXCEPTION_INT)
+ if (exception == EXCEPTION_INT)
+ {
+ if (debugger_enabled())
+ {
+ // notify the debugger of the first pending hardware interrupt
+ u32 const iphw = CAUSE & SR & CAUSE_IPHW;
+
+ if (iphw)
+ debug()->interrupt_hook(22 - count_leading_zeros_32((iphw - 1) & ~iphw), m_pc);
+ }
+ }
+ else
debugger_exception_hook(exception);
}
@@ -1146,7 +1128,7 @@ void r4000_base_device::cpu_lwl(u32 const op)
u64 const offset = ADDR(m_r[RSREG], s16(op));
unsigned const shift = ((offset & 3) ^ R4000_ENDIAN_LE_BE(3, 0)) << 3;
- load<u32>(offset & ~3,
+ load<u32, false>(offset,
[this, op, shift](u32 const data)
{
m_r[RTREG] = s32((m_r[RTREG] & ~u32(~u32(0) << shift)) | (data << shift));
@@ -1158,7 +1140,7 @@ void r4000_base_device::cpu_lwr(u32 const op)
u64 const offset = ADDR(m_r[RSREG], s16(op));
unsigned const shift = ((offset & 0x3) ^ R4000_ENDIAN_LE_BE(0, 3)) << 3;
- load<u32>(offset & ~3,
+ load<u32, false>(offset,
[this, op, shift](u32 const data)
{
m_r[RTREG] = s32((m_r[RTREG] & ~u32(~u32(0) >> shift)) | (data >> shift));
@@ -1170,7 +1152,7 @@ void r4000_base_device::cpu_swl(u32 const op)
u64 const offset = ADDR(m_r[RSREG], s16(op));
unsigned const shift = ((offset & 3) ^ R4000_ENDIAN_LE_BE(3, 0)) << 3;
- store<u32>(offset & ~3, u32(m_r[RTREG]) >> shift, ~u32(0) >> shift);
+ store<u32, false>(offset, u32(m_r[RTREG]) >> shift, ~u32(0) >> shift);
}
void r4000_base_device::cpu_swr(u32 const op)
@@ -1178,7 +1160,7 @@ void r4000_base_device::cpu_swr(u32 const op)
u64 const offset = ADDR(m_r[RSREG], s16(op));
unsigned const shift = ((offset & 3) ^ R4000_ENDIAN_LE_BE(0, 3)) << 3;
- store<u32>(offset & ~3, u32(m_r[RTREG]) << shift, ~u32(0) << shift);
+ store<u32, false>(offset, u32(m_r[RTREG]) << shift, ~u32(0) << shift);
}
void r4000_base_device::cpu_ldl(u32 const op)
@@ -1186,7 +1168,7 @@ void r4000_base_device::cpu_ldl(u32 const op)
u64 const offset = ADDR(m_r[RSREG], s16(op));
unsigned const shift = ((offset & 7) ^ R4000_ENDIAN_LE_BE(7, 0)) << 3;
- load<u64>(offset & ~7,
+ load<u64, false>(offset,
[this, op, shift](u64 const data)
{
m_r[RTREG] = (m_r[RTREG] & ~u64(~u64(0) << shift)) | (data << shift);
@@ -1198,7 +1180,7 @@ void r4000_base_device::cpu_ldr(u32 const op)
u64 const offset = ADDR(m_r[RSREG], s16(op));
unsigned const shift = ((offset & 7) ^ R4000_ENDIAN_LE_BE(0, 7)) << 3;
- load<u64>(offset & ~7,
+ load<u64, false>(offset,
[this, op, shift](u64 const data)
{
m_r[RTREG] = (m_r[RTREG] & ~u64(~u64(0) >> shift)) | (data >> shift);
@@ -1210,7 +1192,7 @@ void r4000_base_device::cpu_sdl(u32 const op)
u64 const offset = ADDR(m_r[RSREG], s16(op));
unsigned const shift = ((offset & 7) ^ R4000_ENDIAN_LE_BE(7, 0)) << 3;
- store<u64>(offset & ~7, m_r[RTREG] >> shift, ~u64(0) >> shift);
+ store<u64, false>(offset, m_r[RTREG] >> shift, ~u64(0) >> shift);
}
void r4000_base_device::cpu_sdr(u32 const op)
@@ -1218,7 +1200,129 @@ void r4000_base_device::cpu_sdr(u32 const op)
u64 const offset = ADDR(m_r[RSREG], s16(op));
unsigned const shift = ((offset & 7) ^ R4000_ENDIAN_LE_BE(0, 7)) << 3;
- store<u64>(offset & ~7, m_r[RTREG] << shift, ~u64(0) << shift);
+ store<u64, false>(offset, m_r[RTREG] << shift, ~u64(0) << shift);
+}
+
+void r4000_base_device::cp0_cache(u32 const op)
+{
+ if ((SR & SR_KSU) && !(SR & SR_CU0) && !(SR & (SR_EXL | SR_ERL)))
+ {
+ cpu_exception(EXCEPTION_CP0);
+ return;
+ }
+
+ switch ((op >> 16) & 0x1f)
+ {
+ case 0x00: // index invalidate (I)
+ if (ICACHE)
+ {
+ m_icache_tag[(ADDR(m_r[RSREG], s16(op)) & m_icache_mask_hi) >> m_icache_shift] &= ~ICACHE_V;
+ break;
+ }
+ [[fallthrough]];
+ case 0x04: // index load tag (I)
+ if (ICACHE)
+ {
+ u32 const tag = m_icache_tag[(ADDR(m_r[RSREG], s16(op)) & m_icache_mask_hi) >> m_icache_shift];
+
+ m_cp0[CP0_TagLo] = ((tag & ICACHE_PTAG) << 8) | ((tag & ICACHE_V) >> 18) | ((tag & ICACHE_P) >> 25);
+ m_cp0[CP0_ECC] = 0; // data ecc or parity
+
+ break;
+ }
+ [[fallthrough]];
+ case 0x08: // index store tag (I)
+ if (ICACHE)
+ {
+ // FIXME: compute parity
+ m_icache_tag[(ADDR(m_r[RSREG], s16(op)) & m_icache_mask_hi) >> m_icache_shift] =
+ (m_cp0[CP0_TagLo] & TAGLO_PTAGLO) >> 8 | (m_cp0[CP0_TagLo] & TAGLO_PSTATE) << 18;
+ break;
+ }
+ [[fallthrough]];
+ case 0x01: // index writeback invalidate (D)
+ case 0x02: // index invalidate (SI)
+ case 0x03: // index writeback invalidate (SD)
+ case 0x05: // index load tag (D)
+ //LOGMASKED(LOG_CACHE, "cache 0x%08x unimplemented (%s)\n", op, machine().describe_context());
+ break;
+ case 0x06: // index load tag (SI)
+ case 0x07: // index load tag (SD)
+ if (SCACHE)
+ {
+ // TODO: translation type for CACHE instruction? Read seems reasonable since only the tag is changing here
+ u64 physical_address = ADDR(m_r[RSREG], s16(op));
+ translate_result const t = translate(TR_READ, false, physical_address);
+ if (t == ERROR || t == MISS)
+ return;
+
+ u32 const index = (physical_address & m_scache_tag_mask) >> m_scache_line_index;
+ if (index < m_scache_tag_size)
+ {
+ // TODO: Load the ECC register here
+ u32 const tag = m_scache_tag[index];
+ u32 const cs = (tag & SCACHE_CS) >> 22;
+ u32 const stag = tag & SCACHE_STAG;
+ u32 const pidx = (tag & SCACHE_PIDX) >> 19;
+ m_cp0[CP0_TagLo] = (stag << 13) | (cs << 10) | (pidx << 7);
+ }
+ else
+ fatalerror("r4000 scache load tag index out of range!");
+ }
+ else
+ LOGMASKED(LOG_CACHE, "cache 0x%08x called without scache enabled (%s)\n", op, machine().describe_context());
+ break;
+ case 0x09: // index store tag (D)
+ //LOGMASKED(LOG_CACHE, "cache 0x%08x unimplemented (%s)\n", op, machine().describe_context());
+ break;
+ case 0x0a: // index store tag (SI)
+ case 0x0b: // index store tag (SD)
+ if (SCACHE)
+ {
+ // TODO: translation type for CACHE instruction? Read seems reasonable since only the tag is changing here
+ u64 const virtual_address = ADDR(m_r[RSREG], s16(op));
+ u64 physical_address = virtual_address;
+ translate_result const t = translate(TR_READ, false, physical_address);
+ if (t == ERROR || t == MISS)
+ return;
+
+ u64 const index = (physical_address & m_scache_tag_mask) >> m_scache_line_index;
+ if (index < m_scache_tag_size)
+ {
+ // TODO: Calculate ECC bits here
+ u64 const tag_lo = m_cp0[CP0_TagLo];
+ u32 const cs = (tag_lo & TAGLO_CS) >> 10;
+ u32 const stag = (tag_lo & TAGLO_STAG) >> 13;
+ u32 const pidx = (virtual_address & 0x7000) >> 12;
+ m_scache_tag[index] = cs << 22 | pidx << 19 | stag;
+ }
+ else
+ fatalerror("r4000 scache store tag index out of range!");
+ }
+ else
+ LOGMASKED(LOG_CACHE, "cache 0x%08x called without scache enabled (%s)\n", op, machine().describe_context());
+ break;
+ case 0x0d: // create dirty exclusive (D)
+ case 0x0f: // create dirty exclusive (SD)
+
+ case 0x10: // hit invalidate (I)
+ case 0x11: // hit invalidate (D)
+ case 0x12: // hit invalidate (SI)
+ case 0x13: // hit invalidate (SD)
+
+ case 0x14: // fill (I)
+ case 0x15: // hit writeback invalidate (D)
+ case 0x17: // hit writeback invalidate (SD)
+
+ case 0x18: // hit writeback (I)
+ case 0x19: // hit writeback (D)
+ case 0x1b: // hit writeback (SD)
+
+ case 0x1e: // hit set virtual (SI)
+ case 0x1f: // hit set virtual (SD)
+ //LOGMASKED(LOG_CACHE, "cache 0x%08x unimplemented (%s)\n", op, machine().describe_context());
+ break;
+ }
}
void r4000_base_device::cp0_execute(u32 const op)
@@ -1312,14 +1416,12 @@ void r4000_base_device::cp0_execute(u32 const op)
case 0x18: // ERET
if (SR & SR_ERL)
{
- m_branch_state = EXCEPTION;
- m_pc = m_cp0[CP0_ErrorEPC];
+ m_branch_state = m_cp0[CP0_ErrorEPC] | BRANCH;
SR &= ~SR_ERL;
}
else
{
- m_branch_state = EXCEPTION;
- m_pc = m_cp0[CP0_EPC];
+ m_branch_state = m_cp0[CP0_EPC] | BRANCH;
SR &= ~SR_EXL;
}
@@ -1353,10 +1455,10 @@ u64 r4000_base_device::cp0_get(unsigned const reg)
{
u8 const wired = m_cp0[CP0_Wired] & 0x3f;
- if (wired < ARRAY_LENGTH(m_tlb))
- return ((total_cycles() - m_cp0_timer_zero) % (ARRAY_LENGTH(m_tlb) - wired) + wired) & 0x3f;
+ if (wired < std::size(m_tlb))
+ return ((total_cycles() - m_cp0_timer_zero) % (std::size(m_tlb) - wired) + wired) & 0x3f;
else
- return ARRAY_LENGTH(m_tlb) - 1;
+ return std::size(m_tlb) - 1;
}
break;
@@ -1398,7 +1500,8 @@ void r4000_base_device::cp0_set(unsigned const reg, u64 const data)
break;
case CP0_Compare:
m_cp0[CP0_Compare] = u32(data);
- CAUSE &= ~CAUSE_IPEX5;
+ if (!m_timer_interrupt_disabled)
+ CAUSE &= ~CAUSE_IPEX5;
cp0_update_timer(true);
break;
@@ -1467,22 +1570,23 @@ void r4000_base_device::cp0_tlbr()
{
u8 const index = m_cp0[CP0_Index] & 0x3f;
- if (index < ARRAY_LENGTH(m_tlb))
+ if (index < std::size(m_tlb))
{
- tlb_entry_t const &entry = m_tlb[index];
+ tlb_entry const &entry = m_tlb[index];
m_cp0[CP0_PageMask] = entry.mask;
m_cp0[CP0_EntryHi] = entry.vpn;
- m_cp0[CP0_EntryLo0] = entry.pfn[0];
- m_cp0[CP0_EntryLo1] = entry.pfn[1];
+ u64 const global = (entry.vpn & EH_G) ? EL_G : 0x0;
+ m_cp0[CP0_EntryLo0] = entry.pfn[0] | global;
+ m_cp0[CP0_EntryLo1] = entry.pfn[1] | global;
}
}
void r4000_base_device::cp0_tlbwi(u8 const index)
{
- if (index < ARRAY_LENGTH(m_tlb))
+ if (index < std::size(m_tlb))
{
- tlb_entry_t &entry = m_tlb[index];
+ tlb_entry &entry = m_tlb[index];
entry.mask = m_cp0[CP0_PageMask];
entry.vpn = m_cp0[CP0_EntryHi] & EH_WM;
@@ -1491,7 +1595,7 @@ void r4000_base_device::cp0_tlbwi(u8 const index)
entry.pfn[0] = m_cp0[CP0_EntryLo0] & EL_WM;
entry.pfn[1] = m_cp0[CP0_EntryLo1] & EL_WM;
- entry.low_bit = 32 - count_leading_zeros((entry.mask >> 1) | 0xfff);
+ entry.low_bit = 32 - count_leading_zeros_32((entry.mask >> 1) | 0xfff);
LOGMASKED(LOG_TLB, "tlb write index %02d mask 0x%016x vpn2 0x%016x %c asid 0x%02x pfn0 0x%016x %c%c pfn1 0x%016x %c%c (%s)\n",
index, entry.mask,
@@ -1505,9 +1609,9 @@ void r4000_base_device::cp0_tlbwi(u8 const index)
void r4000_base_device::cp0_tlbwr()
{
u8 const wired = m_cp0[CP0_Wired] & 0x3f;
- u8 const unwired = ARRAY_LENGTH(m_tlb) - wired;
+ u8 const unwired = std::size(m_tlb) - wired;
- u8 const index = (unwired > 0) ? ((total_cycles() - m_cp0_timer_zero) % unwired + wired) & 0x3f : (ARRAY_LENGTH(m_tlb) - 1);
+ u8 const index = (unwired > 0) ? ((total_cycles() - m_cp0_timer_zero) % unwired + wired) & 0x3f : (std::size(m_tlb) - 1);
cp0_tlbwi(index);
}
@@ -1515,9 +1619,9 @@ void r4000_base_device::cp0_tlbwr()
void r4000_base_device::cp0_tlbp()
{
m_cp0[CP0_Index] = 0x80000000;
- for (u8 index = 0; index < ARRAY_LENGTH(m_tlb); index++)
+ for (u8 index = 0; index < std::size(m_tlb); index++)
{
- tlb_entry_t const &entry = m_tlb[index];
+ tlb_entry const &entry = m_tlb[index];
u64 const mask = (cp0_64() ? EH_R | (EH_VPN2_64 & ~entry.mask) : (EH_VPN2_32 & ~entry.mask))
| ((entry.vpn & EH_G) ? 0 : EH_ASID);
@@ -1530,13 +1634,16 @@ void r4000_base_device::cp0_tlbp()
}
if (m_cp0[CP0_Index] == 0x80000000)
- LOGMASKED(LOG_TLB, "tlbp miss 0x%08x\n", m_cp0[CP0_EntryHi]);
+ LOGMASKED(LOG_TLB, "tlbp miss 0x%08x (%s)\n", m_cp0[CP0_EntryHi], machine().describe_context());
else
- LOGMASKED(LOG_TLB, "tlbp hit 0x%08x index %02d\n", m_cp0[CP0_EntryHi], m_cp0[CP0_Index]);
+ LOGMASKED(LOG_TLB, "tlbp hit 0x%08x index %02d (%s)\n", m_cp0[CP0_EntryHi], m_cp0[CP0_Index], machine().describe_context());
}
void r4000_base_device::cp0_update_timer(bool start)
{
+ if (m_timer_interrupt_disabled)
+ return;
+
if (start || m_cp0_timer->enabled())
{
u32 const count = (total_cycles() - m_cp0_timer_zero) / 2;
@@ -1596,6 +1703,486 @@ template <> bool r4000_base_device::cp1_op<float64_t>(float64_t op)
return true;
}
+void r5000_device::cp1_execute(u32 const op)
+{
+ if (!(SR & SR_CU1))
+ {
+ cpu_exception(EXCEPTION_CP1);
+ return;
+ }
+
+ softfloat_exceptionFlags = 0;
+ switch (op >> 26)
+ {
+ case 0x11: // COP1
+ switch ((op >> 21) & 0x1f)
+ {
+ case 0x10: // S
+ {
+ switch (op & 0x3f)
+ {
+ case 0x11: // MOVF.S / MOVT.S
+ if (((m_fcr31 >> s_fcc_shifts[(op >> 18) & 7]) & 1) == ((op >> 16) & 1))
+ cp1_mov_s(op);
+ return;
+ case 0x12: // MOVZ.S
+ if (m_r[RTREG] == 0)
+ cp1_mov_s(op);
+ return;
+ case 0x13: // MOVN.S
+ if (m_r[RTREG] != 0)
+ cp1_mov_s(op);
+ return;
+ case 0x15: // RECIP.S
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ float32_t const fs = float32_t{ u32(m_f[FSREG]) };
+
+ if (cp1_op(fs))
+ {
+ cp1_set(FDREG, f32_div(i32_to_f32(1), fs).v);
+ }
+ }
+ return;
+ case 0x16: // RSQRT.S
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ float32_t const fs = float32_t{ u32(m_f[FSREG]) };
+
+ if (cp1_op(fs))
+ {
+ cp1_set(FDREG, f32_div(i32_to_f32(1), f32_sqrt(fs)).v);
+ }
+ }
+ return;
+ case 0x30: // C.F.S (false)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+ return;
+ case 0x31: // C.UN.S (unordered)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ // detect unordered
+ f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) });
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7];
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+ }
+ return;
+ case 0x32: // C.EQ.S (equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }))
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7];
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+ }
+ return;
+ case 0x33: // C.UEQ.S (unordered equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7];
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+ }
+ return;
+ case 0x34: // C.OLT.S (less than)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f32_lt(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }))
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7];
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+ }
+ return;
+ case 0x35: // C.ULT.S (unordered less than)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f32_lt(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7];
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+ }
+ return;
+ case 0x36: // C.OLE.S (less than or equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f32_le(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }))
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7];
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+ }
+ return;
+ case 0x37: // C.ULE.S (unordered less than or equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f32_le(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7];
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+ }
+ return;
+
+ case 0x38: // C.SF.S (signalling false)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ // detect unordered
+ f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) });
+
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ return;
+ case 0x39: // C.NGLE.S (not greater, less than or equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ // detect unordered
+ f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) });
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7] | FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+ }
+ return;
+ case 0x3a: // C.SEQ.S (signalling equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }))
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7];
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ return;
+ case 0x3b: // C.NGL.S (not greater or less than)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7];
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ return;
+ case 0x3c: // C.LT.S (less than)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f32_lt(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }))
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7];
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ return;
+ case 0x3d: // C.NGE.S (not greater or equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f32_lt(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7];
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ return;
+ case 0x3e: // C.LE.S (less than or equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f32_le(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }))
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7];
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ return;
+ case 0x3f: // C.NGT.S (not greater than)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f32_le(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7];
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ return;
+ }
+ }
+ [[fallthrough]];
+ case 0x11: // D
+ switch (op & 0x3f)
+ {
+ case 0x11: // MOVF.D / MOVT.D
+ if (((m_fcr31 >> s_fcc_shifts[(op >> 18) & 7]) & 1) == ((op >> 16) & 1))
+ cp1_mov_d(op);
+ return;
+ case 0x12: // MOVZ.D
+ if (m_r[RTREG] == 0)
+ cp1_mov_d(op);
+ return;
+ case 0x13: // MOVN.D
+ if (m_r[RTREG] != 0)
+ cp1_mov_d(op);
+ return;
+ case 0x15: // RECIP.D
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ float64_t const fs = float64_t{ m_f[FSREG] };
+
+ if (cp1_op(fs))
+ {
+ cp1_set(FDREG, f64_div(i32_to_f64(1), fs).v);
+ }
+ }
+ return;
+ case 0x16: // RSQRT.D
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ float64_t const fs = float64_t{ m_f[FSREG] };
+
+ if (cp1_op(fs))
+ {
+ cp1_set(FDREG, f64_div(i32_to_f64(1), f64_sqrt(fs)).v);
+ }
+ }
+ return;
+ case 0x30: // C.F.D (false)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+ return;
+ case 0x31: // C.UN.D (unordered)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ // detect unordered
+ f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] });
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7];
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+ }
+ return;
+ case 0x32: // C.EQ.D (equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }))
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7];
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+ }
+ return;
+ case 0x33: // C.UEQ.D (unordered equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7];
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+ }
+ return;
+ case 0x34: // C.OLT.D (less than)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f64_lt(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }))
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7];
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+ }
+ return;
+ case 0x35: // C.ULT.D (unordered less than)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f64_lt(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7];
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+ }
+ return;
+ case 0x36: // C.OLE.D (less than or equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f64_le(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }))
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7];
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+ }
+ return;
+ case 0x37: // C.ULE.D (unordered less than or equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f64_le(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7];
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+ }
+ return;
+
+ case 0x38: // C.SF.D (signalling false)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ // detect unordered
+ f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] });
+
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ return;
+ case 0x39: // C.NGLE.D (not greater, less than or equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ // detect unordered
+ f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] });
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7] | FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+ }
+ return;
+ case 0x3a: // C.SEQ.D (signalling equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }))
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7];
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ return;
+ case 0x3b: // C.NGL.D (not greater or less than)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7];
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ return;
+ case 0x3c: // C.LT.D (less than)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f64_lt(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }))
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7];
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ return;
+ case 0x3d: // C.NGE.D (not greater or equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f64_lt(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7];
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ return;
+ case 0x3e: // C.LE.D (less than or equal)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f64_le(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }))
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7];
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ return;
+ case 0x3f: // C.NGT.D (not greater than)
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ if (f64_le(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
+ m_fcr31 |= s_fcc_masks[(op >> 18) & 7];
+ else
+ m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7];
+
+ if (softfloat_exceptionFlags & softfloat_flag_invalid)
+ {
+ m_fcr31 |= FCR31_CV;
+ cpu_exception(EXCEPTION_FPE);
+ }
+ }
+ return;
+ }
+ return;
+ }
+ }
+
+ r4000_base_device::cp1_execute(op);
+}
+
void r4000_base_device::cp1_execute(u32 const op)
{
if (!(SR & SR_CU1))
@@ -1692,33 +2279,21 @@ void r4000_base_device::cp1_execute(u32 const op)
{
case 0x00: // BC1F
if (!(m_fcr31 & FCR31_C))
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
+ m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY;
break;
case 0x01: // BC1T
if (m_fcr31 & FCR31_C)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
+ m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY;
break;
case 0x02: // BC1FL
if (!(m_fcr31 & FCR31_C))
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
+ m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY;
else
m_branch_state = NULLIFY;
break;
case 0x03: // BC1TL
if (m_fcr31 & FCR31_C)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
+ m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY;
else
m_branch_state = NULLIFY;
break;
@@ -1797,23 +2372,7 @@ void r4000_base_device::cp1_execute(u32 const op)
}
break;
case 0x06: // MOV.S
- if (SR & SR_FR)
- m_f[FDREG] = (m_f[FDREG] & ~0xffffffffULL) | u32(m_f[FSREG]);
- else
- if (FDREG & 1)
- if (FSREG & 1)
- // move high half to high half
- m_f[FDREG & ~1] = (m_f[FSREG & ~1] & ~0xffffffffULL) | u32(m_f[FDREG & ~1]);
- else
- // move low half to high half
- m_f[FDREG & ~1] = (m_f[FSREG & ~1] << 32) | u32(m_f[FDREG & ~1]);
- else
- if (FSREG & 1)
- // move high half to low half
- m_f[FDREG & ~1] = (m_f[FDREG & ~1] & ~0xffffffffULL) | (m_f[FSREG & ~1] >> 32);
- else
- // move low half to low half
- m_f[FDREG & ~1] = (m_f[FDREG & ~1] & ~0xffffffffULL) | u32(m_f[FSREG & ~1]);
+ cp1_mov_s(op);
break;
case 0x07: // NEG.S
if ((SR & SR_FR) || !(op & ODD_REGS))
@@ -2193,8 +2752,7 @@ void r4000_base_device::cp1_execute(u32 const op)
}
break;
case 0x06: // MOV.D
- if ((SR & SR_FR) || !(op & ODD_REGS))
- m_f[FDREG] = m_f[FSREG];
+ cp1_mov_d(op);
break;
case 0x07: // NEG.D
if ((SR & SR_FR) || !(op & ODD_REGS))
@@ -2528,11 +3086,11 @@ void r4000_base_device::cp1_execute(u32 const op)
// TODO: MIPS3 only
switch (op & 0x3f)
{
- case 0x02a00020: // CVT.S.L
+ case 0x20: // CVT.S.L
if ((SR & SR_FR) || !(op & ODD_REGS))
cp1_set(FDREG, i64_to_f32(s64(m_f[FSREG])).v);
break;
- case 0x02a00021: // CVT.D.L
+ case 0x21: // CVT.D.L
if ((SR & SR_FR) || !(op & ODD_REGS))
cp1_set(FDREG, i64_to_f64(s64(m_f[FSREG])).v);
break;
@@ -2587,8 +3145,214 @@ void r4000_base_device::cp1_execute(u32 const op)
break;
case 0x3d: // SDC1
+ store<u64>(ADDR(m_r[RSREG], s16(op)), m_f[RTREG]);
+ break;
+ }
+}
+
+void r4000_base_device::cp1_mov_s(u32 const op)
+{
+ if (SR & SR_FR)
+ m_f[FDREG] = (m_f[FDREG] & ~0xffffffffULL) | u32(m_f[FSREG]);
+ else
+ if (FDREG & 1)
+ if (FSREG & 1)
+ // move high half to high half
+ m_f[FDREG & ~1] = (m_f[FSREG & ~1] & ~0xffffffffULL) | u32(m_f[FDREG & ~1]);
+ else
+ // move low half to high half
+ m_f[FDREG & ~1] = (m_f[FSREG & ~1] << 32) | u32(m_f[FDREG & ~1]);
+ else
+ if (FSREG & 1)
+ // move high half to low half
+ m_f[FDREG & ~1] = (m_f[FDREG & ~1] & ~0xffffffffULL) | (m_f[FSREG & ~1] >> 32);
+ else
+ // move low half to low half
+ m_f[FDREG & ~1] = (m_f[FDREG & ~1] & ~0xffffffffULL) | u32(m_f[FSREG & ~1]);
+}
+
+void r4000_base_device::cp1_mov_d(u32 const op)
+{
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ m_f[FDREG] = m_f[FSREG];
+}
+
+void r4000_base_device::cp1x_execute(u32 const op)
+{
+ if (!(SR & SR_CU1))
+ {
+ cpu_exception(EXCEPTION_CP1);
+ return;
+ }
+
+ logerror("cp1x not supported < R5000 (%s)\n", machine().describe_context());
+}
+
+void r5000_device::cp1x_execute(u32 const op)
+{
+ if (!(SR & SR_CU1))
+ {
+ cpu_exception(EXCEPTION_CP1);
+ return;
+ }
+
+ switch (op & 0x3f)
+ {
+ case 0x00: // LWXC1
+ load<u32>(s64(s32(u32(m_r[RSREG]) + u32(m_r[RTREG]))),
+ [this, op](u32 data)
+ {
+ if (SR & SR_FR)
+ m_f[RTREG] = (m_f[RTREG] & ~0xffffffffULL) | data;
+ else
+ if (RTREG & 1)
+ // load the high half of the even floating point register
+ m_f[RTREG & ~1] = (u64(data) << 32) | u32(m_f[RTREG & ~1]);
+ else
+ // load the low half of the even floating point register
+ m_f[RTREG & ~1] = (m_f[RTREG & ~1] & ~0xffffffffULL) | data;
+ });
+ break;
+ case 0x01: // LDXC1
+ load<u64>(s64(s32(u32(m_r[RSREG]) + u32(m_r[RTREG]))),
+ [this, op](u64 data)
+ {
+ if ((SR & SR_FR) || !(RTREG & 1))
+ m_f[RTREG] = data;
+ });
+ break;
+ case 0x08: // SWXC1
+ if (SR & SR_FR)
+ store<u32>(s64(s32(u32(m_r[RSREG]) + u32(m_r[RTREG]))), u32(m_f[RTREG]));
+ else
+ if (RTREG & 1)
+ // store the high half of the even floating point register
+ store<u32>(s64(s32(u32(m_r[RSREG]) + u32(m_r[RTREG]))), u32(m_f[RTREG & ~1] >> 32));
+ else
+ // store the low half of the even floating point register
+ store<u32>(s64(s32(u32(m_r[RSREG]) + u32(m_r[RTREG]))), u32(m_f[RTREG & ~1]));
+ break;
+ case 0x09: // SDXC1
if ((SR & SR_FR) || !(RTREG & 1))
- store<u64>(ADDR(m_r[RSREG], s16(op)), m_f[RTREG]);
+ store<u64>(s64(s32(u32(m_r[RSREG]) + u32(m_r[RTREG]))), m_f[RTREG]);
+ break;
+ case 0x0f: // PREFX
+ // Do nothing for now (implementations are permitted to do this)
+ break;
+ case 0x20: // MADD.S
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ float32_t const fr = float32_t{ u32(m_f[FRREG]) };
+ float32_t const fs = float32_t{ u32(m_f[FSREG]) };
+ float32_t const ft = float32_t{ u32(m_f[FTREG]) };
+
+ if (cp1_op(fr) && cp1_op(fs) && cp1_op(ft))
+ cp1_set(FDREG, f32_add(f32_mul(fs, ft), fr).v);
+ }
+ break;
+ case 0x21: // MADD.D
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ float64_t const fr = float64_t{ m_f[FRREG] };
+ float64_t const fs = float64_t{ m_f[FSREG] };
+ float64_t const ft = float64_t{ m_f[FTREG] };
+
+ if (cp1_op(fr) && cp1_op(fs) && cp1_op(ft))
+ cp1_set(FDREG, f64_add(f64_mul(fs, ft), fr).v);
+ }
+ break;
+ case 0x28: // MSUB.S
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ float32_t const fr = float32_t{ u32(m_f[FRREG]) };
+ float32_t const fs = float32_t{ u32(m_f[FSREG]) };
+ float32_t const ft = float32_t{ u32(m_f[FTREG]) };
+
+ if (cp1_op(fr) && cp1_op(fs) && cp1_op(ft))
+ cp1_set(FDREG, f32_sub(f32_mul(fs, ft), fr).v);
+ }
+ break;
+ case 0x29: // MSUB.D
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ float64_t const fr = float64_t{ m_f[FRREG] };
+ float64_t const fs = float64_t{ m_f[FSREG] };
+ float64_t const ft = float64_t{ m_f[FTREG] };
+
+ if (cp1_op(fr) && cp1_op(fs) && cp1_op(ft))
+ cp1_set(FDREG, f64_sub(f64_mul(fs, ft), fr).v);
+ }
+ break;
+ case 0x30: // NMADD.S
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ float32_t const fr = float32_t{ u32(m_f[FRREG]) };
+ float32_t const fs = float32_t{ u32(m_f[FSREG]) };
+ float32_t const ft = float32_t{ u32(m_f[FTREG]) };
+
+ if (cp1_op(fr) && cp1_op(fs) && cp1_op(ft))
+ cp1_set(FDREG, f32_mul(f32_add(f32_mul(fs, ft), fr), i32_to_f32(-1)).v);
+ }
+ break;
+ case 0x31: // NMADD.D
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ float64_t const fr = float64_t{ m_f[FRREG] };
+ float64_t const fs = float64_t{ m_f[FSREG] };
+ float64_t const ft = float64_t{ m_f[FTREG] };
+
+ if (cp1_op(fr) && cp1_op(fs) && cp1_op(ft))
+ cp1_set(FDREG, f64_mul(f64_add(f64_mul(fs, ft), fr), i32_to_f64(-1)).v);
+ }
+ break;
+ case 0x38: // NMSUB.S
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ float32_t const fr = float32_t{ u32(m_f[FRREG]) };
+ float32_t const fs = float32_t{ u32(m_f[FSREG]) };
+ float32_t const ft = float32_t{ u32(m_f[FTREG]) };
+
+ if (cp1_op(fr) && cp1_op(fs) && cp1_op(ft))
+ cp1_set(FDREG, f32_mul(f32_sub(f32_mul(fs, ft), fr), i32_to_f32(-1)).v);
+ }
+ break;
+ case 0x39: // NMSUB.D
+ if ((SR & SR_FR) || !(op & ODD_REGS))
+ {
+ float64_t const fr = float64_t{ m_f[FRREG] };
+ float64_t const fs = float64_t{ m_f[FSREG] };
+ float64_t const ft = float64_t{ m_f[FTREG] };
+
+ if (cp1_op(fr) && cp1_op(fs) && cp1_op(ft))
+ cp1_set(FDREG, f64_mul(f64_sub(f64_mul(fs, ft), fr), i32_to_f64(-1)).v);
+ }
+ break;
+ case 0x24: /* MADD.W */
+ logerror("cp1x unsupported op (%s): MADD.W\n", machine().describe_context());
+ break;
+ case 0x25: /* MADD.L */
+ logerror("cp1x unsupported op (%s): MADD.L\n", machine().describe_context());
+ break;
+ case 0x2c: /* MSUB.W */
+ logerror("cp1x unsupported op (%s): MSUB.W\n", machine().describe_context());
+ break;
+ case 0x2d: /* MSUB.L */
+ logerror("cp1x unsupported op (%s): MSUB.L\n", machine().describe_context());
+ break;
+ case 0x34: /* NMADD.W */
+ logerror("cp1x unsupported op (%s): NMADD.W\n", machine().describe_context());
+ break;
+ case 0x35: /* NMADD.L */
+ logerror("cp1x unsupported op (%s): NMADD.L\n", machine().describe_context());
+ break;
+ case 0x3c: /* NMSUB.W */
+ logerror("cp1x unsupported op (%s): NMSUB.W\n", machine().describe_context());
+ break;
+ case 0x3d: /* NMSUB.L */
+ logerror("cp1x unsupported op (%s): NMSUB.L\n", machine().describe_context());
+ break;
+ default:
+ logerror("cp1x unsupported op (%s): [unknown]\n", machine().describe_context());
break;
}
}
@@ -2727,7 +3491,7 @@ void r4000_base_device::cp2_execute(u32 const op)
}
}
-r4000_base_device::translate_t r4000_base_device::translate(int intention, u64 &address)
+r4000_base_device::translate_result r4000_base_device::translate(int intention, bool debug, u64 &address)
{
/*
* Decode the program address into one of the following ranges depending on
@@ -2881,16 +3645,16 @@ r4000_base_device::translate_t r4000_base_device::translate(int intention, u64 &
// address needs translation, using a combination of VPN2 and ASID
u64 const key = (address & (extended ? (EH_R | EH_VPN2_64) : EH_VPN2_32)) | (m_cp0[CP0_EntryHi] & EH_ASID);
- unsigned *mru = m_tlb_mru[intention & TRANSLATE_TYPE_MASK];
- if (LOG_STATS)
+ unsigned *mru = m_tlb_mru[intention];
+ if (VERBOSE & LOG_STATS)
m_tlb_scans++;
bool invalid = false;
bool modify = false;
- for (unsigned i = 0; i < ARRAY_LENGTH(m_tlb); i++)
+ for (unsigned i = 0; i < std::size(m_tlb); i++)
{
unsigned const index = mru[i];
- tlb_entry_t const &entry = m_tlb[index];
+ tlb_entry const &entry = m_tlb[index];
// test vpn and asid
u64 const mask = (extended ? EH_R | (EH_VPN2_64 & ~entry.mask) : (EH_VPN2_32 & ~entry.mask))
@@ -2899,7 +3663,7 @@ r4000_base_device::translate_t r4000_base_device::translate(int intention, u64 &
if ((entry.vpn & mask) != (key & mask))
continue;
- if (LOG_STATS)
+ if (VERBOSE & LOG_STATS)
m_tlb_loops += i + 1;
u64 const pfn = entry.pfn[BIT(address, entry.low_bit)];
@@ -2912,7 +3676,7 @@ r4000_base_device::translate_t r4000_base_device::translate(int intention, u64 &
}
// test dirty
- if ((intention & TRANSLATE_WRITE) && !(pfn & EL_D))
+ if ((intention == TR_WRITE) && !(pfn & EL_D))
{
modify = true;
break;
@@ -2930,18 +3694,18 @@ r4000_base_device::translate_t r4000_base_device::translate(int intention, u64 &
}
// tlb miss, invalid entry, or a store to a non-dirty entry
- if (!machine().side_effects_disabled() && !(intention & TRANSLATE_DEBUG_MASK))
+ if (!machine().side_effects_disabled() && !debug)
{
if (VERBOSE & LOG_TLB)
{
- char const mode[] = { 'r', 'w', 'x' };
+ static char const mode[] = { 'r', 'w', 'x' };
if (modify)
- LOGMASKED(LOG_TLB, "tlb modify asid %d address 0x%016x (%s)\n",
- m_cp0[CP0_EntryHi] & EH_ASID, address, machine().describe_context());
+ LOGMASKED(LOG_TLB, "tlb modify asid 0x%02x address 0x%016x (%s)\n",
+ m_cp0[CP0_EntryHi] & EH_ASID, address, machine().describe_context());
else
- LOGMASKED(LOG_TLB, "tlb miss %c asid %d address 0x%016x (%s)\n",
- mode[intention & TRANSLATE_TYPE_MASK], m_cp0[CP0_EntryHi] & EH_ASID, address, machine().describe_context());
+ LOGMASKED(LOG_TLB, "tlb miss %c asid 0x%02x address 0x%016x (%s)\n",
+ mode[intention], m_cp0[CP0_EntryHi] & EH_ASID, address, machine().describe_context());
}
// load tlb exception registers
@@ -2951,9 +3715,9 @@ r4000_base_device::translate_t r4000_base_device::translate(int intention, u64 &
m_cp0[CP0_XContext] = (m_cp0[CP0_XContext] & XCONTEXT_PTEBASE) | ((address >> 31) & XCONTEXT_R) | ((address >> 9) & XCONTEXT_BADVPN2);
if (invalid || modify || (SR & SR_EXL))
- cpu_exception(modify ? EXCEPTION_MOD : (intention & TRANSLATE_WRITE) ? EXCEPTION_TLBS : EXCEPTION_TLBL);
+ cpu_exception(modify ? EXCEPTION_MOD : (intention == TR_WRITE) ? EXCEPTION_TLBS : EXCEPTION_TLBL);
else
- cpu_exception((intention & TRANSLATE_WRITE) ? EXCEPTION_TLBS : EXCEPTION_TLBL, extended ? 0x080 : 0x000);
+ cpu_exception((intention == TR_WRITE) ? EXCEPTION_TLBS : EXCEPTION_TLBL, extended ? 0x080 : 0x000);
}
return MISS;
@@ -2961,7 +3725,7 @@ r4000_base_device::translate_t r4000_base_device::translate(int intention, u64 &
void r4000_base_device::address_error(int intention, u64 const address)
{
- if (!machine().side_effects_disabled() && !(intention & TRANSLATE_DEBUG_MASK))
+ if (!machine().side_effects_disabled())
{
logerror("address_error 0x%016x (%s)\n", address, machine().describe_context());
@@ -2969,28 +3733,43 @@ void r4000_base_device::address_error(int intention, u64 const address)
if (!(SR & SR_EXL))
m_cp0[CP0_BadVAddr] = address;
- cpu_exception((intention & TRANSLATE_WRITE) ? EXCEPTION_ADES : EXCEPTION_ADEL);
+ cpu_exception((intention == TR_WRITE) ? EXCEPTION_ADES : EXCEPTION_ADEL);
// address errors shouldn't typically occur, so a breakpoint is handy
machine().debug_break();
}
}
-template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, bool> r4000_base_device::load(u64 address, U &&apply)
+template <typename T> void r4000_base_device::accessors(T &m)
+{
+ space(AS_PROGRAM).specific(m);
+
+ read_byte = [&m](offs_t offset) { return m.read_byte(offset); };
+ read_word = [&m](offs_t offset) { return m.read_word(offset); };
+ read_dword = [&m](offs_t offset) { return m.read_dword(offset); };
+ read_qword = [&m](offs_t offset) { return m.read_qword(offset); };
+
+ write_byte = [&m](offs_t offset, u8 data) { m.write_byte(offset, data); };
+ write_word = [&m](offs_t offset, u16 data, u16 mem_mask) { m.write_word(offset, data, mem_mask); };
+ write_dword = [&m](offs_t offset, u32 data, u32 mem_mask) { m.write_dword(offset, data, mem_mask); };
+ write_qword = [&m](offs_t offset, u64 data, u64 mem_mask) { m.write_qword(offset, data, mem_mask); };
+}
+
+template <typename T, bool Aligned, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, bool> r4000_base_device::load(u64 address, U &&apply)
{
// alignment error
- if (address & (sizeof(T) - 1))
+ if (Aligned && (address & (sizeof(T) - 1)))
{
- address_error(TRANSLATE_READ, address);
+ address_error(TR_READ, address);
return false;
}
- translate_t const t = translate(TRANSLATE_READ, address);
+ translate_result const t = translate(TR_READ, false, address);
// address error
if (t == ERROR)
{
- address_error(TRANSLATE_READ, address);
+ address_error(TR_READ, address);
return false;
}
@@ -3011,15 +3790,19 @@ template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::f
}
}
+ // align address for l[dw][lr] instructions
+ if (!Aligned)
+ address &= ~(sizeof(T) - 1);
+
// TODO: cache lookup
T value = 0;
switch (sizeof(T))
{
- case 1: value = T(space(0).read_byte(address)); break;
- case 2: value = T(space(0).read_word(address)); break;
- case 4: value = T(space(0).read_dword(address)); break;
- case 8: value = T(space(0).read_qword(address)); break;
+ case 1: value = T(read_byte(address)); break;
+ case 2: value = T(read_word(address)); break;
+ case 4: value = T(read_dword(address)); break;
+ case 8: value = T(read_qword(address)); break;
}
if (m_bus_error)
@@ -3040,16 +3823,16 @@ template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::f
// alignment error
if (address & (sizeof(T) - 1))
{
- address_error(TRANSLATE_READ, address);
+ address_error(TR_READ, address);
return false;
}
- translate_t const t = translate(TRANSLATE_READ, address);
+ translate_result const t = translate(TR_READ, false, address);
// address error
if (t == ERROR)
{
- address_error(TRANSLATE_READ, address);
+ address_error(TR_READ, address);
return false;
}
@@ -3073,28 +3856,28 @@ template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::f
switch (sizeof(T))
{
- case 4: apply(address, T(space(0).read_dword(address))); break;
- case 8: apply(address, T(space(0).read_qword(address))); break;
+ case 4: apply(address, T(read_dword(address))); break;
+ case 8: apply(address, T(read_qword(address))); break;
}
return true;
}
-template <typename T, typename U> std::enable_if_t<std::is_convertible<U, T>::value, bool> r4000_base_device::store(u64 address, U data, T mem_mask)
+template <typename T, bool Aligned, typename U> std::enable_if_t<std::is_convertible<U, T>::value, bool> r4000_base_device::store(u64 address, U data, T mem_mask)
{
// alignment error
- if (address & (sizeof(T) - 1))
+ if (Aligned && (address & (sizeof(T) - 1)))
{
- address_error(TRANSLATE_WRITE, address);
+ address_error(TR_WRITE, address);
return false;
}
- translate_t const t = translate(TRANSLATE_WRITE, address);
+ translate_result const t = translate(TR_WRITE, false, address);
// address error
if (t == ERROR)
{
- address_error(TRANSLATE_WRITE, address);
+ address_error(TR_WRITE, address);
return false;
}
@@ -3114,14 +3897,18 @@ template <typename T, typename U> std::enable_if_t<std::is_convertible<U, T>::va
}
}
+ // align address for s[dw][lr] instructions
+ if (!Aligned)
+ address &= ~(sizeof(T) - 1);
+
// TODO: cache lookup
switch (sizeof(T))
{
- case 1: space(0).write_byte(address, T(data)); break;
- case 2: space(0).write_word(address, T(data), mem_mask); break;
- case 4: space(0).write_dword(address, T(data), mem_mask); break;
- case 8: space(0).write_qword(address, T(data), mem_mask); break;
+ case 1: write_byte(address, T(data)); break;
+ case 2: write_word(address, T(data), mem_mask); break;
+ case 4: write_dword(address, T(data), mem_mask); break;
+ case 8: write_qword(address, T(data), mem_mask); break;
}
return true;
@@ -3134,17 +3921,17 @@ bool r4000_base_device::fetch(u64 address, std::function<void(u32)> &&apply)
// alignment error
if (address & 3)
{
- address_error(TRANSLATE_FETCH, address);
+ address_error(TR_FETCH, address);
return false;
}
- translate_t const t = translate(TRANSLATE_FETCH, address);
+ translate_result const t = translate(TR_FETCH, false, address);
// address error
if (t == ERROR)
{
- address_error(TRANSLATE_FETCH, address);
+ address_error(TR_FETCH, address);
return false;
}
@@ -3157,7 +3944,7 @@ bool r4000_base_device::fetch(u64 address, std::function<void(u32)> &&apply)
{
if (t == UNCACHED)
{
- const u32 insn = space(0).read_dword(address);
+ const u32 insn = read_dword(address);
if (m_bus_error)
{
@@ -3186,7 +3973,7 @@ bool r4000_base_device::fetch(u64 address, std::function<void(u32)> &&apply)
tag = ICACHE_V | (address >> 12);
for (unsigned i = 0; i < m_icache_line_size; i += 8)
{
- u64 const data = space(0).read_qword((address & m_icache_mask_lo) | i);
+ u64 const data = read_qword((address & m_icache_mask_lo) | i);
m_icache_data[(((cache_address & m_icache_mask_lo) | i) >> 2) + 0] = u32(data);
m_icache_data[(((cache_address & m_icache_mask_lo) | i) >> 2) + 1] = data >> 32;
@@ -3200,7 +3987,7 @@ bool r4000_base_device::fetch(u64 address, std::function<void(u32)> &&apply)
}
else
{
- const u32 insn = space(0).read_dword(address);
+ const u32 insn = read_dword(address);
if (m_bus_error)
{
@@ -3296,3 +4083,46 @@ std::string r4000_base_device::debug_unicode_string(u64 unicode_string_pointer)
return utf8_from_wstring(result);
}
+
+void r4000_base_device::configure_scache()
+{
+ if (m_scache_size > 0)
+ {
+ /*
+ * Secondary cache tag size depends on the cache line size
+ * (how many bytes are transferred with one cache operation) and the
+ * size of the cache itself.
+ * For example, the Sony NEWS NWS-5000X has a 1MB secondary cache
+ * and a cache line size of 16 words. So, the slice of the physical
+ * address used to index into the cache is bits 19:6.
+ * See chapter 11 of the R4000 user manual for more details.
+ */
+ if (m_scache_line_size == 0)
+ fatalerror("SCACHE size set but line size was not set!");
+
+ if (m_scache_line_size <= 0x10)
+ m_scache_line_index = 4;
+ else if (m_scache_line_size <= 0x20)
+ {
+ m_scache_line_index = 5;
+ m_cp0[CP0_Config] |= 1 << 22;
+ }
+ else if (m_scache_line_size <= 0x40)
+ {
+ m_scache_line_index = 6;
+ m_cp0[CP0_Config] |= 2 << 22;
+ }
+ else
+ {
+ m_scache_line_index = 7;
+ m_cp0[CP0_Config] |= 3 << 22;
+ }
+
+ m_scache_tag_size = m_scache_size >> m_scache_line_index;
+ m_scache_tag_mask = m_scache_size - 1;
+
+ m_scache_tag = std::make_unique<u32[]>(m_scache_tag_size);
+ }
+ else
+ m_cp0[CP0_Config] |= CONFIG_SC;
+}