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.cpp3067
1 files changed, 0 insertions, 3067 deletions
diff --git a/src/devices/cpu/mips/r4000.cpp b/src/devices/cpu/mips/r4000.cpp
deleted file mode 100644
index ca95b107c65..00000000000
--- a/src/devices/cpu/mips/r4000.cpp
+++ /dev/null
@@ -1,3067 +0,0 @@
-// license:BSD-3-Clause
-// copyright-holders:Patrick Mackinlay
-
-/*
- * This is a stripped-down MIPS-III CPU derived from the main mips3 code. Its
- * primary purpose is to act as a test-bed to aid in debugging MIPS-based
- * systems, after which the changes/improvements from here are expected to
- * be back-ported and incorporated into the original mips3 device.
- *
- * Because of this specific approach, no attempt is made to support many of the
- * current features of the mips3 device at this time. Key differences bewteen
- * this implementation and mips3 include:
- *
- * - only supports MIPS R4000/R4400 and QED R4600
- * - no dynamic recompilation
- * - reworked address translation logic, including 64-bit modes
- * - reworked softfloat3-based floating point
- * - experimental primary instruction cache
- * - memory tap based ll/sc
- * - configurable endianness
- * - it's very very very slow
- *
- * TODO
- * - try to eliminate mode check in address calculations
- * - find a better way to deal with software interrupts
- * - enforce mode checks for cp1
- * - cache instructions
- * - check/improve instruction timing
- *
- */
-
-#include "emu.h"
-#include "debugger.h"
-#include "r4000.h"
-#include "mips3dsm.h"
-
-#include "softfloat3/source/include/softfloat.h"
-
-#define LOG_GENERAL (1U << 0)
-#define LOG_TLB (1U << 1)
-#define LOG_CACHE (1U << 2)
-#define LOG_EXCEPTION (1U << 3)
-#define LOG_SYSCALL (1U << 4)
-#define LOG_STATS (1U << 5)
-
-#define VERBOSE (LOG_GENERAL)
-
-// operating system specific system call logging
-#define SYSCALL_IRIX53 (1U << 0)
-#define SYSCALL_WINNT4 (1U << 1)
-#if VERBOSE & LOG_SYSCALL
-#define SYSCALL_MASK (SYSCALL_IRIX53)
-#else
-#define SYSCALL_MASK (0)
-#endif
-
-// experimental primary instruction cache
-#define ICACHE 0
-
-#include "logmacro.h"
-
-#define USE_ABI_REG_NAMES 1
-
-// cpu instruction fiels
-#define RSREG ((op >> 21) & 31)
-#define RTREG ((op >> 16) & 31)
-#define RDREG ((op >> 11) & 31)
-#define SHIFT ((op >> 6) & 31)
-
-// cop1 instruction fields
-#define FRREG ((op >> 21) & 31)
-#define FTREG ((op >> 16) & 31)
-#define FSREG ((op >> 11) & 31)
-#define FDREG ((op >> 6) & 31)
-
-#define R4000_ENDIAN_LE_BE(le, be) ((m_cp0[CP0_Config] & CONFIG_BE) ? (be) : (le))
-
-// identify odd-numbered cop1 registers
-#define ODD_REGS 0x00010840U
-
-// address computation
-#define ADDR(r, o) (cp0_64() ? ((r) + (o)) : s64(s32((r) + (o))))
-
-#define SR m_cp0[CP0_Status]
-#define CAUSE m_cp0[CP0_Cause]
-
-DEFINE_DEVICE_TYPE(R4000, r4000_device, "r4000", "MIPS R4000")
-DEFINE_DEVICE_TYPE(R4400, r4400_device, "r4400", "MIPS R4400")
-DEFINE_DEVICE_TYPE(R4600, r4600_device, "r4600", "QED R4600")
-
-r4000_base_device::r4000_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u32 prid, cache_size_t icache_size, cache_size_t dcache_size)
- : cpu_device(mconfig, type, tag, owner, clock)
- , m_program_config_le("program", ENDIANNESS_LITTLE, 64, 32)
- , m_program_config_be("program", ENDIANNESS_BIG, 64, 32)
- , m_ll_watch(nullptr)
- , m_fcr0(0x00000500U)
-{
- m_cp0[CP0_PRId] = prid;
-
- // default configuration
- m_cp0[CP0_Config] = CONFIG_BE | (icache_size << 9) | (dcache_size << 6);
-}
-
-void r4000_base_device::device_start()
-{
- // TODO: save state
-
- state_add(STATE_GENPC, "GENPC", m_pc).noshow();
- state_add(STATE_GENPCBASE, "CURPC", m_pc).noshow();
- state_add(MIPS3_PC, "PC", m_pc).formatstr("%016X");
-
- // exception processing
- state_add(MIPS3_CP0 + CP0_Status, "SR", m_cp0[CP0_Status]).formatstr("%08X");
- state_add(MIPS3_CP0 + CP0_EPC, "EPC", m_cp0[CP0_EPC]).formatstr("%016X");
- state_add(MIPS3_CP0 + CP0_Cause, "Cause", m_cp0[CP0_Cause]).formatstr("%08X");
- state_add(MIPS3_CP0 + CP0_Context, "Context", m_cp0[CP0_Context]).formatstr("%016X");
- state_add(MIPS3_CP0 + CP0_BadVAddr, "BadVAddr", m_cp0[CP0_BadVAddr]).formatstr("%016X");
- state_add(MIPS3_CP0 + CP0_Compare, "Compare", m_cp0[CP0_Compare]).formatstr("%08X");
- state_add(MIPS3_CP0 + CP0_WatchLo, "WatchLo", m_cp0[CP0_WatchLo]).formatstr("%08X");
- state_add(MIPS3_CP0 + CP0_WatchHi, "WatchHi", m_cp0[CP0_WatchHi]).formatstr("%08X");
- state_add(MIPS3_CP0 + CP0_XContext, "XContext", m_cp0[CP0_XContext]).formatstr("%016X");
-
- // memory management
- state_add(MIPS3_CP0 + CP0_Index, "Index", m_cp0[CP0_Index]).formatstr("%08X");
- state_add(MIPS3_CP0 + CP0_EntryLo0, "EntryLo0", m_cp0[CP0_EntryLo0]).formatstr("%016X");
- state_add(MIPS3_CP0 + CP0_EntryLo1, "EntryLo1", m_cp0[CP0_EntryLo1]).formatstr("%016X");
- state_add(MIPS3_CP0 + CP0_PageMask, "PageMask", m_cp0[CP0_PageMask]).formatstr("%08X");
- state_add(MIPS3_CP0 + CP0_Wired, "Wired", m_cp0[CP0_Wired]).formatstr("%08X");
- state_add(MIPS3_CP0 + CP0_EntryHi, "EntryHi", m_cp0[CP0_EntryHi]).formatstr("%016X");
- state_add(MIPS3_CP0 + CP0_LLAddr, "LLAddr", m_cp0[CP0_LLAddr]).formatstr("%08X");
-
-#if USE_ABI_REG_NAMES
- state_add(MIPS3_R0 + 0, "zero", m_r[0]).callimport().formatstr("%016X"); // Can't change R0
- state_add(MIPS3_R0 + 1, "at", m_r[1]).formatstr("%016X");
- state_add(MIPS3_R0 + 2, "v0", m_r[2]).formatstr("%016X");
- state_add(MIPS3_R0 + 3, "v1", m_r[3]).formatstr("%016X");
- state_add(MIPS3_R0 + 4, "a0", m_r[4]).formatstr("%016X");
- state_add(MIPS3_R0 + 5, "a1", m_r[5]).formatstr("%016X");
- state_add(MIPS3_R0 + 6, "a2", m_r[6]).formatstr("%016X");
- state_add(MIPS3_R0 + 7, "a3", m_r[7]).formatstr("%016X");
- state_add(MIPS3_R0 + 8, "t0", m_r[8]).formatstr("%016X");
- state_add(MIPS3_R0 + 9, "t1", m_r[9]).formatstr("%016X");
- state_add(MIPS3_R0 + 10, "t2", m_r[10]).formatstr("%016X");
- state_add(MIPS3_R0 + 11, "t3", m_r[11]).formatstr("%016X");
- state_add(MIPS3_R0 + 12, "t4", m_r[12]).formatstr("%016X");
- state_add(MIPS3_R0 + 13, "t5", m_r[13]).formatstr("%016X");
- state_add(MIPS3_R0 + 14, "t6", m_r[14]).formatstr("%016X");
- state_add(MIPS3_R0 + 15, "t7", m_r[15]).formatstr("%016X");
- state_add(MIPS3_R0 + 16, "s0", m_r[16]).formatstr("%016X");
- state_add(MIPS3_R0 + 17, "s1", m_r[17]).formatstr("%016X");
- state_add(MIPS3_R0 + 18, "s2", m_r[18]).formatstr("%016X");
- state_add(MIPS3_R0 + 19, "s3", m_r[19]).formatstr("%016X");
- state_add(MIPS3_R0 + 20, "s4", m_r[20]).formatstr("%016X");
- state_add(MIPS3_R0 + 21, "s5", m_r[21]).formatstr("%016X");
- state_add(MIPS3_R0 + 22, "s6", m_r[22]).formatstr("%016X");
- state_add(MIPS3_R0 + 23, "s7", m_r[23]).formatstr("%016X");
- state_add(MIPS3_R0 + 24, "t8", m_r[24]).formatstr("%016X");
- state_add(MIPS3_R0 + 25, "t9", m_r[25]).formatstr("%016X");
- state_add(MIPS3_R0 + 26, "k0", m_r[26]).formatstr("%016X");
- state_add(MIPS3_R0 + 27, "k1", m_r[27]).formatstr("%016X");
- state_add(MIPS3_R0 + 28, "gp", m_r[28]).formatstr("%016X");
- state_add(MIPS3_R0 + 29, "sp", m_r[29]).formatstr("%016X");
- state_add(MIPS3_R0 + 30, "fp", m_r[30]).formatstr("%016X");
- state_add(MIPS3_R0 + 31, "ra", m_r[31]).formatstr("%016X");
-#else
- state_add(MIPS3_R0, "R0", m_r[0]).callimport().formatstr("%016X");
- for (unsigned i = 1; i < 32; i++)
- state_add(MIPS3_R0 + i, util::string_format("R%d", i).c_str(), m_r[i]);
-#endif
-
- state_add(MIPS3_HI, "HI", m_hi).formatstr("%016X");
- state_add(MIPS3_LO, "LO", m_lo).formatstr("%016X");
-
- // floating point registers
- state_add(MIPS3_FCR30, "FCR30", m_fcr30).formatstr("%08X");
- state_add(MIPS3_FCR31, "FCR31", m_fcr31).formatstr("%08X");
- for (unsigned i = 0; i < 32; i++)
- state_add(MIPS3_F0 + i, util::string_format("F%d", i).c_str(), m_f[i]);
-
- set_icountptr(m_icount);
-
- m_cp0_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(r4000_base_device::cp0_timer_callback), this));
-
- // compute icache line selection mask and allocate tag and data
- unsigned const config_ic = (m_cp0[CP0_Config] & CONFIG_IC) >> 9;
-
- m_icache_mask_hi = (0x1000U << config_ic) - 1;
- m_icache_tag = std::make_unique<u32[]>(0x100U << config_ic);
- m_icache_data = std::make_unique<u32 []>((0x1000U << config_ic) >> 2);
-}
-
-void r4000_base_device::device_reset()
-{
- m_branch_state = NONE;
- m_pc = s64(s32(0xbfc00000));
-
- m_cp0[CP0_Status] = SR_BEV | SR_ERL;
- m_cp0[CP0_Wired] = 0;
- m_cp0[CP0_Compare] = 0;
- m_cp0[CP0_Count] = 0;
-
- m_cp0_timer_zero = total_cycles();
-
- if (m_ll_watch)
- {
- m_ll_watch->remove();
- m_ll_watch = nullptr;
- }
-
- m_cp0[CP0_WatchLo] = 0;
- m_cp0[CP0_WatchHi] = 0;
-
- // initialize tlb mru index with identity mapping
- for (unsigned i = 0; i < ARRAY_LENGTH(m_tlb); i++)
- {
- m_tlb_mru[TRANSLATE_READ][i] = i;
- m_tlb_mru[TRANSLATE_WRITE][i] = i;
- m_tlb_mru[TRANSLATE_FETCH][i] = i;
- }
-
- // initialize statistics
- m_tlb_scans = 0;
- m_tlb_loops = 0;
- m_icache_hits = 0;
- m_icache_misses = 0;
-}
-
-void r4000_base_device::device_stop()
-{
- if ((m_icache_hits + m_icache_misses) > 0)
- LOGMASKED(LOG_STATS, "icache hit ratio %.3f%% (%d hits %d misses)\n",
- double(m_icache_hits) / double(m_icache_hits + m_icache_misses) * 100.0, m_icache_hits, m_icache_misses);
-
- if (m_tlb_scans > 0)
- LOGMASKED(LOG_STATS, "tlb scans %d loops %d average %.3f loops per scan\n", m_tlb_scans, m_tlb_loops, double(m_tlb_loops) / double(m_tlb_scans));
-}
-
-device_memory_interface::space_config_vector r4000_base_device::memory_space_config() const
-{
- return space_config_vector{
- std::make_pair(AS_PROGRAM, R4000_ENDIAN_LE_BE(&m_program_config_le, &m_program_config_be))
- };
-}
-
-bool r4000_base_device::memory_translate(int spacenum, int intention, offs_t &address)
-{
- // FIXME: address truncation
- u64 placeholder = s32(address);
-
- translate_t const t = translate(intention, placeholder);
-
- if (t == ERROR || t == MISS)
- return false;
-
- address = placeholder;
- return true;
-}
-
-std::unique_ptr<util::disasm_interface> r4000_base_device::create_disassembler()
-{
- return std::make_unique<mips3_disassembler>();
-}
-
-void r4000_base_device::execute_run()
-{
- // check interrupts
- if ((CAUSE & SR & CAUSE_IP) && (SR & SR_IE) && !(SR & (SR_EXL | SR_ERL)))
- cpu_exception(EXCEPTION_INT);
-
- while (m_icount > 0)
- {
- debugger_instruction_hook(m_pc);
-
- fetch(m_pc,
- [this](u32 const op)
- {
- cpu_execute(op);
-
- // zero register zero
- m_r[0] = 0;
- });
-
- // update pc and branch state
- switch (m_branch_state)
- {
- case NONE:
- m_pc += 4;
- break;
-
- case DELAY:
- m_branch_state = NONE;
- m_pc = m_branch_target;
- break;
-
- case BRANCH:
- m_branch_state = DELAY;
- m_pc += 4;
- break;
-
- case EXCEPTION:
- m_branch_state = NONE;
- break;
-
- case NULLIFY:
- m_branch_state = NONE;
- m_pc += 8;
- break;
- }
-
- m_icount--;
- }
-}
-
-void r4000_base_device::execute_set_input(int inputnum, int state)
-{
- if (state)
- m_cp0[CP0_Cause] |= (CAUSE_IPEX0 << inputnum);
- else
- m_cp0[CP0_Cause] &= ~(CAUSE_IPEX0 << inputnum);
-}
-
-void r4000_base_device::cpu_execute(u32 const op)
-{
- switch (op >> 26)
- {
- case 0x00: // SPECIAL
- switch (op & 0x3f)
- {
- case 0x00: // SLL
- m_r[RDREG] = s64(s32(m_r[RTREG] << SHIFT));
- break;
- //case 0x01: // *
- case 0x02: // SRL
- m_r[RDREG] = s64(s32(u32(m_r[RTREG]) >> SHIFT));
- break;
- case 0x03: // SRA
- m_r[RDREG] = s64(s32(m_r[RTREG]) >> SHIFT);
- break;
- case 0x04: // SLLV
- m_r[RDREG] = s64(s32(m_r[RTREG] << (m_r[RSREG] & 31)));
- break;
- //case 0x05: // *
- case 0x06: // SRLV
- m_r[RDREG] = s64(s32(u32(m_r[RTREG]) >> (m_r[RSREG] & 31)));
- break;
- case 0x07: // SRAV
- m_r[RDREG] = s64(s32(m_r[RTREG]) >> (m_r[RSREG] & 31));
- break;
- case 0x08: // JR
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_r[RSREG], 0);
- break;
- case 0x09: // JALR
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_r[RSREG], 0);
- m_r[RDREG] = ADDR(m_pc, 8);
- break;
- //case 0x0a: // *
- //case 0x0b: // *
- case 0x0c: // SYSCALL
- if (VERBOSE & LOG_SYSCALL)
- {
- if (SYSCALL_MASK & SYSCALL_IRIX53)
- {
- switch (m_r[2])
- {
- case 0x3e9: // 1001 = exit
- LOGMASKED(LOG_SYSCALL, "exit(%d) (%s)\n", m_r[4], machine().describe_context());
- break;
-
- case 0x3ea: // 1002 = fork
- LOGMASKED(LOG_SYSCALL, "fork() (%s)\n", machine().describe_context());
- break;
-
- case 0x3eb: // 1003 = read
- LOGMASKED(LOG_SYSCALL, "read(%d, 0x%x, %d) (%s)\n", m_r[4], m_r[5], m_r[6], machine().describe_context());
- break;
-
- case 0x3ec: // 1004 = write
- LOGMASKED(LOG_SYSCALL, "write(%d, 0x%x, %d) (%s)\n", m_r[4], m_r[5], m_r[6], machine().describe_context());
- if (m_r[4] == 1 || m_r[4] == 2)
- printf("%s", debug_string(m_r[5], m_r[6]).c_str());
- break;
-
- case 0x3ed: // 1005 = open
- LOGMASKED(LOG_SYSCALL, "open(\"%s\", %#o) (%s)\n", debug_string(m_r[4]), m_r[5], machine().describe_context());
- break;
-
- case 0x3ee: // 1006 = close
- LOGMASKED(LOG_SYSCALL, "close(%d) (%s)\n", m_r[4], machine().describe_context());
- break;
-
- case 0x3ef: // 1007 = creat
- LOGMASKED(LOG_SYSCALL, "creat(\"%s\", %#o) (%s)\n", debug_string(m_r[4]), m_r[5], machine().describe_context());
- break;
-
- case 0x423: // 1059 = exece
- LOGMASKED(LOG_SYSCALL, "exece(\"%s\", [ %s ], [ %s ]) (%s)\n", debug_string(m_r[4]), debug_string_array(m_r[5]), debug_string_array(m_r[6]), machine().describe_context());
- break;
-
- default:
- LOGMASKED(LOG_SYSCALL, "syscall 0x%x (%s)\n", m_r[2], machine().describe_context());
- break;
- }
- }
- else if (SYSCALL_MASK & SYSCALL_WINNT4)
- {
- switch (m_r[2])
- {
- case 0x4f:
- load<s32>(m_r[7] + 8,
- [this](s64 string_pointer)
- {
- LOGMASKED(LOG_SYSCALL, "NtOpenFile(%s) (%s)\n", debug_string(string_pointer), machine().describe_context());
- });
- break;
-
- default:
- LOGMASKED(LOG_SYSCALL, "syscall 0x%x (%s)\n", m_r[2], machine().describe_context());
- break;
- }
- }
- }
- cpu_exception(EXCEPTION_SYS);
- break;
- case 0x0d: // BREAK
- cpu_exception(EXCEPTION_BP);
- break;
- //case 0x0e: // *
- case 0x0f: // SYNC
- break;
- case 0x10: // MFHI
- m_r[RDREG] = m_hi;
- break;
- case 0x11: // MTHI
- m_hi = m_r[RSREG];
- break;
- case 0x12: // MFLO
- m_r[RDREG] = m_lo;
- break;
- case 0x13: // MTLO
- m_lo = m_r[RSREG];
- break;
- case 0x14: // DSLLV
- m_r[RDREG] = m_r[RTREG] << (m_r[RSREG] & 63);
- break;
- //case 0x15: // *
- case 0x16: // DSRLV
- m_r[RDREG] = m_r[RTREG] >> (m_r[RSREG] & 63);
- break;
- case 0x17: // DSRAV
- m_r[RDREG] = s64(m_r[RTREG]) >> (m_r[RSREG] & 63);
- break;
- case 0x18: // MULT
- {
- u64 const product = mul_32x32(s32(m_r[RSREG]), s32(m_r[RTREG]));
-
- m_lo = s64(s32(product));
- m_hi = s64(s32(product >> 32));
- m_icount -= 3;
- }
- break;
- case 0x19: // MULTU
- {
- u64 const product = mulu_32x32(u32(m_r[RSREG]), u32(m_r[RTREG]));
-
- m_lo = s64(s32(product));
- m_hi = s64(s32(product >> 32));
- m_icount -= 3;
- }
- break;
- case 0x1a: // DIV
- if (m_r[RTREG])
- {
- m_lo = s64(s32(m_r[RSREG]) / s32(m_r[RTREG]));
- m_hi = s64(s32(m_r[RSREG]) % s32(m_r[RTREG]));
- }
- m_icount -= 35;
- break;
- case 0x1b: // DIVU
- if (m_r[RTREG])
- {
- m_lo = s64(s32(u32(m_r[RSREG]) / u32(m_r[RTREG])));
- m_hi = s64(s32(u32(m_r[RSREG]) % u32(m_r[RTREG])));
- }
- m_icount -= 35;
- break;
- case 0x1c: // DMULT
- m_lo = mul_64x64(m_r[RSREG], m_r[RTREG], reinterpret_cast<s64 *>(&m_hi));
- m_icount -= 7;
- break;
- case 0x1d: // DMULTU
- m_lo = mulu_64x64(m_r[RSREG], m_r[RTREG], &m_hi);
- m_icount -= 7;
- break;
- case 0x1e: // DDIV
- if (m_r[RTREG])
- {
- m_lo = s64(m_r[RSREG]) / s64(m_r[RTREG]);
- m_hi = s64(m_r[RSREG]) % s64(m_r[RTREG]);
- }
- m_icount -= 67;
- break;
- case 0x1f: // DDIVU
- if (m_r[RTREG])
- {
- m_lo = m_r[RSREG] / m_r[RTREG];
- m_hi = m_r[RSREG] % m_r[RTREG];
- }
- m_icount -= 67;
- break;
- case 0x20: // ADD
- {
- u32 const sum = u32(m_r[RSREG]) + u32(m_r[RTREG]);
-
- // overflow: (sign(addend0) == sign(addend1)) && (sign(addend0) != sign(sum))
- if (!BIT(u32(m_r[RSREG]) ^ u32(m_r[RTREG]), 31) && BIT(u32(m_r[RSREG]) ^ sum, 31))
- cpu_exception(EXCEPTION_OV);
- else
- m_r[RDREG] = s64(s32(sum));
- }
- break;
- case 0x21: // ADDU
- m_r[RDREG] = s64(s32(u32(m_r[RSREG]) + u32(m_r[RTREG])));
- break;
- case 0x22: // SUB
- {
- u32 const difference = u32(m_r[RSREG]) - u32(m_r[RTREG]);
-
- // overflow: (sign(minuend) != sign(subtrahend)) && (sign(minuend) != sign(difference))
- if (BIT(u32(m_r[RSREG]) ^ u32(m_r[RTREG]), 31) && BIT(u32(m_r[RSREG]) ^ difference, 31))
- cpu_exception(EXCEPTION_OV);
- else
- m_r[RDREG] = s64(s32(difference));
- }
- break;
- case 0x23: // SUBU
- m_r[RDREG] = s64(s32(u32(m_r[RSREG]) - u32(m_r[RTREG])));
- break;
- case 0x24: // AND
- m_r[RDREG] = m_r[RSREG] & m_r[RTREG];
- break;
- case 0x25: // OR
- m_r[RDREG] = m_r[RSREG] | m_r[RTREG];
- break;
- case 0x26: // XOR
- m_r[RDREG] = m_r[RSREG] ^ m_r[RTREG];
- break;
- case 0x27: // NOR
- m_r[RDREG] = ~(m_r[RSREG] | m_r[RTREG]);
- break;
- //case 0x28: // *
- //case 0x29: // *
- case 0x2a: // SLT
- m_r[RDREG] = s64(m_r[RSREG]) < s64(m_r[RTREG]);
- break;
- case 0x2b: // SLTU
- m_r[RDREG] = m_r[RSREG] < m_r[RTREG];
- break;
- case 0x2c: // DADD
- {
- u64 const sum = m_r[RSREG] + m_r[RTREG];
-
- // overflow: (sign(addend0) == sign(addend1)) && (sign(addend0) != sign(sum))
- if (!BIT(m_r[RSREG] ^ m_r[RTREG], 63) && BIT(m_r[RSREG] ^ sum, 63))
- cpu_exception(EXCEPTION_OV);
- else
- m_r[RDREG] = sum;
- }
- break;
- case 0x2d: // DADDU
- m_r[RDREG] = m_r[RSREG] + m_r[RTREG];
- break;
- case 0x2e: // DSUB
- {
- u64 const difference = m_r[RSREG] - m_r[RTREG];
-
- // overflow: (sign(minuend) != sign(subtrahend)) && (sign(minuend) != sign(difference))
- if (BIT(m_r[RSREG] ^ m_r[RTREG], 63) && BIT(m_r[RSREG] ^ difference, 63))
- cpu_exception(EXCEPTION_OV);
- else
- m_r[RDREG] = difference;
- }
- break;
- case 0x2f: // DSUBU
- m_r[RDREG] = m_r[RSREG] - m_r[RTREG];
- break;
- case 0x30: // TGE
- if (s64(m_r[RSREG]) >= s64(m_r[RTREG]))
- cpu_exception(EXCEPTION_TR);
- break;
- case 0x31: // TGEU
- if (m_r[RSREG] >= m_r[RTREG])
- cpu_exception(EXCEPTION_TR);
- break;
- case 0x32: // TLT
- if (s64(m_r[RSREG]) < s64(m_r[RTREG]))
- cpu_exception(EXCEPTION_TR);
- break;
- case 0x33: // TLTU
- if (m_r[RSREG] < m_r[RTREG])
- cpu_exception(EXCEPTION_TR);
- break;
- case 0x34: // TEQ
- if (m_r[RSREG] == m_r[RTREG])
- cpu_exception(EXCEPTION_TR);
- break;
- //case 0x35: // *
- case 0x36: // TNE
- if (m_r[RSREG] != m_r[RTREG])
- cpu_exception(EXCEPTION_TR);
- break;
- //case 0x37: // *
- case 0x38: // DSLL
- m_r[RDREG] = m_r[RTREG] << SHIFT;
- break;
- //case 0x39: // *
- case 0x3a: // DSRL
- m_r[RDREG] = m_r[RTREG] >> SHIFT;
- break;
- case 0x3b: // DSRA
- m_r[RDREG] = s64(m_r[RTREG]) >> SHIFT;
- break;
- case 0x3c: // DSLL32
- m_r[RDREG] = m_r[RTREG] << (SHIFT + 32);
- break;
- //case 0x3d: // *
- case 0x3e: // DSRL32
- m_r[RDREG] = m_r[RTREG] >> (SHIFT + 32);
- break;
- case 0x3f: // DSRA32
- m_r[RDREG] = s64(m_r[RTREG]) >> (SHIFT + 32);
- break;
-
- default:
- // * Operation codes marked with an asterisk cause reserved
- // instruction exceptions in all current implementations and are
- // reserved for future versions of the architecture.
- cpu_exception(EXCEPTION_RI);
- break;
- }
- break;
- case 0x01: // REGIMM
- switch ((op >> 16) & 0x1f)
- {
- case 0x00: // BLTZ
- if (s64(m_r[RSREG]) < 0)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
- break;
- case 0x01: // BGEZ
- if (s64(m_r[RSREG]) >= 0)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
- break;
- case 0x02: // BLTZL
- if (s64(m_r[RSREG]) < 0)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
- else
- m_branch_state = NULLIFY;
- break;
- case 0x03: // BGEZL
- if (s64(m_r[RSREG]) >= 0)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
- else
- m_branch_state = NULLIFY;
- break;
- //case 0x04: // *
- //case 0x05: // *
- //case 0x06: // *
- //case 0x07: // *
- case 0x08: // TGEI
- if (s64(m_r[RSREG]) >= s16(op))
- cpu_exception(EXCEPTION_TR);
- break;
- case 0x09: // TGEIU
- if (m_r[RSREG] >= u64(s64(s16(op))))
- cpu_exception(EXCEPTION_TR);
- break;
- case 0x0a: // TLTI
- if (s64(m_r[RSREG]) < s16(op))
- cpu_exception(EXCEPTION_TR);
- break;
- case 0x0b: // TLTIU
- if (m_r[RSREG] >= u64(s64(s16(op))))
- cpu_exception(EXCEPTION_TR);
- break;
- case 0x0c: // TEQI
- if (m_r[RSREG] == u64(s64(s16(op))))
- cpu_exception(EXCEPTION_TR);
- break;
- //case 0x0d: // *
- case 0x0e: // TNEI
- if (m_r[RSREG] != u64(s64(s16(op))))
- cpu_exception(EXCEPTION_TR);
- break;
- //case 0x0f: // *
- case 0x10: // BLTZAL
- if (s64(m_r[RSREG]) < 0)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
- m_r[31] = ADDR(m_pc, 8);
- break;
- case 0x11: // BGEZAL
- if (s64(m_r[RSREG]) >= 0)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
- m_r[31] = ADDR(m_pc, 8);
- break;
- case 0x12: // BLTZALL
- if (s64(m_r[RSREG]) < 0)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
- else
- m_branch_state = NULLIFY;
- m_r[31] = ADDR(m_pc, 8);
- break;
- case 0x13: // BGEZALL
- if (s64(m_r[RSREG]) >= 0)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
- else
- m_branch_state = NULLIFY;
- m_r[31] = ADDR(m_pc, 8);
- break;
- //case 0x14: // *
- //case 0x15: // *
- //case 0x16: // *
- //case 0x17: // *
- //case 0x18: // *
- //case 0x19: // *
- //case 0x1a: // *
- //case 0x1b: // *
- //case 0x1c: // *
- //case 0x1d: // *
- //case 0x1e: // *
- //case 0x1f: // *
-
- default:
- // * Operation codes marked with an asterisk cause reserved
- // instruction exceptions in all current implementations and are
- // reserved for future versions of the architecture.
- cpu_exception(EXCEPTION_RI);
- break;
- }
- break;
- case 0x02: // J
- m_branch_state = BRANCH;
- m_branch_target = (ADDR(m_pc, 4) & ~0x0fffffffULL) | ((op & 0x03ffffffU) << 2);
- break;
- case 0x03: // JAL
- m_branch_state = BRANCH;
- m_branch_target = (ADDR(m_pc, 4) & ~0x0fffffffULL) | ((op & 0x03ffffffU) << 2);
- m_r[31] = ADDR(m_pc, 8);
- break;
- case 0x04: // BEQ
- if (m_r[RSREG] == m_r[RTREG])
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
- break;
- case 0x05: // BNE
- if (m_r[RSREG] != m_r[RTREG])
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
- break;
- case 0x06: // BLEZ
- if (s64(m_r[RSREG]) <= 0)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
- break;
- case 0x07: // BGTZ
- if (s64(m_r[RSREG]) > 0)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
- break;
- case 0x08: // ADDI
- {
- u32 const sum = u32(m_r[RSREG]) + s16(op);
-
- // overflow: (sign(addend0) == sign(addend1)) && (sign(addend0) != sign(sum))
- if (!BIT(u32(m_r[RSREG]) ^ s32(s16(op)), 31) && BIT(u32(m_r[RSREG]) ^ sum, 31))
- cpu_exception(EXCEPTION_OV);
- else
- m_r[RTREG] = s64(s32(sum));
- }
- break;
- case 0x09: // ADDIU
- m_r[RTREG] = s64(s32(u32(m_r[RSREG]) + s16(op)));
- break;
- case 0x0a: // SLTI
- m_r[RTREG] = s64(m_r[RSREG]) < s64(s16(op));
- break;
- case 0x0b: // SLTIU
- m_r[RTREG] = m_r[RSREG] < u64(s64(s16(op)));
- break;
- case 0x0c: // ANDI
- m_r[RTREG] = m_r[RSREG] & u16(op);
- break;
- case 0x0d: // ORI
- m_r[RTREG] = m_r[RSREG] | u16(op);
- break;
- case 0x0e: // XORI
- m_r[RTREG] = m_r[RSREG] ^ u16(op);
- break;
- case 0x0f: // LUI
- m_r[RTREG] = s64(s16(op)) << 16;
- break;
- case 0x10: // COP0
- cp0_execute(op);
- break;
- case 0x11: // COP1
- cp1_execute(op);
- break;
- case 0x12: // COP2
- cp2_execute(op);
- break;
- //case 0x13: // *
- case 0x14: // BEQL
- if (m_r[RSREG] == m_r[RTREG])
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
- else
- m_branch_state = NULLIFY;
- break;
- case 0x15: // BNEL
- if (m_r[RSREG] != m_r[RTREG])
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
- else
- m_branch_state = NULLIFY;
- break;
- case 0x16: // BLEZL
- if (s64(m_r[RSREG]) <= 0)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
- else
- m_branch_state = NULLIFY;
- break;
- case 0x17: // BGTZL
- if (s64(m_r[RSREG]) > 0)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
- else
- m_branch_state = NULLIFY;
- break;
- case 0x18: // DADDI
- {
- u64 const sum = m_r[RSREG] + s64(s16(op));
-
- // overflow: (sign(addend0) == sign(addend1)) && (sign(addend0) != sign(sum))
- if (!BIT(m_r[RSREG] ^ s64(s16(op)), 63) && BIT(m_r[RSREG] ^ sum, 63))
- cpu_exception(EXCEPTION_OV);
- else
- m_r[RTREG] = sum;
- }
- break;
- case 0x19: // DADDIU
- m_r[RTREG] = m_r[RSREG] + s64(s16(op));
- break;
- case 0x1a: // LDL
- if (!(SR & SR_KSU) || (SR & (SR_EXL | SR_ERL)) || cp0_64())
- cpu_ldl(op);
- else
- cpu_exception(EXCEPTION_RI);
- break;
- case 0x1b: // LDR
- if (!(SR & SR_KSU) || (SR & (SR_EXL | SR_ERL)) || cp0_64())
- cpu_ldr(op);
- else
- cpu_exception(EXCEPTION_RI);
- break;
- //case 0x1c: // *
- //case 0x1d: // *
- //case 0x1e: // *
- //case 0x1f: // *
- case 0x20: // LB
- load<s8>(ADDR(m_r[RSREG], s16(op)),
- [this, op](s8 data)
- {
- m_r[RTREG] = data;
- });
- break;
- case 0x21: // LH
- load<s16>(ADDR(m_r[RSREG], s16(op)),
- [this, op](s16 data)
- {
- m_r[RTREG] = data;
- });
- break;
- case 0x22: // LWL
- cpu_lwl(op);
- break;
- case 0x23: // LW
- load<s32>(ADDR(m_r[RSREG], s16(op)),
- [this, op](s32 data)
- {
- m_r[RTREG] = data;
- });
- break;
- case 0x24: // LBU
- load<s8>(ADDR(m_r[RSREG], s16(op)),
- [this, op](u8 data)
- {
- m_r[RTREG] = data;
- });
- break;
- case 0x25: // LHU
- load<u16>(ADDR(m_r[RSREG], s16(op)),
- [this, op](u16 data)
- {
- m_r[RTREG] = data;
- });
- break;
- case 0x26: // LWR
- cpu_lwr(op);
- break;
- case 0x27: // LWU
- load<u32>(ADDR(m_r[RSREG], s16(op)),
- [this, op](u32 data)
- {
- m_r[RTREG] = data;
- });
- break;
- case 0x28: // SB
- store<u8>(ADDR(m_r[RSREG], s16(op)), u8(m_r[RTREG]));
- break;
- case 0x29: // SH
- store<u16>(ADDR(m_r[RSREG], s16(op)), u16(m_r[RTREG]));
- break;
- case 0x2a: // SWL
- cpu_swl(op);
- break;
- case 0x2b: // SW
- store<u32>(ADDR(m_r[RSREG], s16(op)), u32(m_r[RTREG]));
- break;
- case 0x2c: // SDL
- if (!(SR & SR_KSU) || (SR & (SR_EXL | SR_ERL)) || cp0_64())
- cpu_sdl(op);
- else
- cpu_exception(EXCEPTION_RI);
- break;
- case 0x2d: // SDR
- if (!(SR & SR_KSU) || (SR & (SR_EXL | SR_ERL)) || cp0_64())
- cpu_sdr(op);
- else
- cpu_exception(EXCEPTION_RI);
- break;
- case 0x2e: // SWR
- cpu_swr(op);
- break;
- case 0x2f: // CACHE
- if ((SR & SR_KSU) && !(SR & SR_CU0) && !(SR & (SR_EXL | SR_ERL)))
- {
- cpu_exception(EXCEPTION_CP0);
- break;
- }
-
- switch ((op >> 16) & 0x1f)
- {
- case 0x00: // index invalidate (I)
- if (ICACHE)
- {
- m_icache_tag[(ADDR(m_r[RSREG], s16(op)) & m_icache_mask_hi) >> m_icache_shift] &= ~ICACHE_V;
- break;
- }
-
- case 0x04: // index load tag (I)
- if (ICACHE)
- {
- u32 const tag = m_icache_tag[(ADDR(m_r[RSREG], s16(op)) & m_icache_mask_hi) >> m_icache_shift];
-
- m_cp0[CP0_TagLo] = ((tag & ICACHE_PTAG) << 8) | ((tag & ICACHE_V) >> 18) | ((tag & ICACHE_P) >> 25);
- m_cp0[CP0_ECC] = 0; // data ecc or parity
-
- break;
- }
-
- case 0x08: // index store tag (I)
- if (ICACHE)
- {
- // FIXME: compute parity
- m_icache_tag[(ADDR(m_r[RSREG], s16(op)) & m_icache_mask_hi) >> m_icache_shift] =
- (m_cp0[CP0_TagLo] & TAGLO_PTAGLO) >> 8 | (m_cp0[CP0_TagLo] & TAGLO_PSTATE) << 18;
-
- break;
- }
-
- case 0x01: // index writeback invalidate (D)
- case 0x02: // index invalidate (SI)
- case 0x03: // index writeback invalidate (SD)
-
- case 0x05: // index load tag (D)
- case 0x06: // index load tag (SI)
- case 0x07: // index load tag (SI)
-
- case 0x09: // index store tag (D)
- case 0x0a: // index store tag (SI)
- case 0x0b: // index store tag (SD)
-
- case 0x0d: // create dirty exclusive (D)
- case 0x0f: // create dirty exclusive (SD)
-
- case 0x10: // hit invalidate (I)
- case 0x11: // hit invalidate (D)
- case 0x12: // hit invalidate (SI)
- case 0x13: // hit invalidate (SD)
-
- case 0x14: // fill (I)
- case 0x15: // hit writeback invalidate (D)
- case 0x17: // hit writeback invalidate (SD)
-
- case 0x18: // hit writeback (I)
- case 0x19: // hit writeback (D)
- case 0x1b: // hit writeback (SD)
-
- case 0x1e: // hit set virtual (SI)
- case 0x1f: // hit set virtual (SD)
- //LOGMASKED(LOG_CACHE, "cache 0x%08x unimplemented (%s)\n", op, machine().describe_context());
- break;
- }
- break;
- case 0x30: // LL
- load_linked<s32>(ADDR(m_r[RSREG], s16(op)),
- [this, op](u64 address, s32 data)
- {
- // remove existing tap
- if (m_ll_watch)
- m_ll_watch->remove();
-
- m_r[RTREG] = data;
- m_cp0[CP0_LLAddr] = u32(address >> 4);
-
- // install write tap
- // FIXME: physical address truncation
- m_ll_watch = space(0).install_write_tap(offs_t(address & ~7), offs_t(address | 7), "ll",
- [this, hi(bool(BIT(address, 2)))](offs_t offset, u64 &data, u64 mem_mask)
- {
- if (hi ? ACCESSING_BITS_32_63 : ACCESSING_BITS_0_31)
- {
- m_ll_watch->remove();
- m_ll_watch = nullptr;
- }
- });
- });
- break;
- case 0x31: // LWC1
- cp1_execute(op);
- break;
- case 0x32: // LWC2
- cp2_execute(op);
- break;
- //case 0x33: // *
- case 0x34: // LLD
- load_linked<u64>(ADDR(m_r[RSREG], s16(op)),
- [this, op](u64 address, u64 data)
- {
- // remove existing tap
- if (m_ll_watch)
- m_ll_watch->remove();
-
- m_r[RTREG] = data;
- m_cp0[CP0_LLAddr] = u32(address >> 4);
-
- // install write tap
- // FIXME: address truncation
- m_ll_watch = space(0).install_write_tap(offs_t(address & ~7), offs_t(address | 7), "lld",
- [this](offs_t offset, u64 &data, u64 mem_mask)
- {
- m_ll_watch->remove();
- m_ll_watch = nullptr;
- });
- });
- break;
- case 0x35: // LDC1
- cp1_execute(op);
- break;
- case 0x36: // LDC2
- cp2_execute(op);
- break;
- case 0x37: // LD
- load<u64>(ADDR(m_r[RSREG], s16(op)),
- [this, op](u64 data)
- {
- m_r[RTREG] = data;
- });
- break;
- case 0x38: // SC
- if (m_ll_watch)
- {
- m_ll_watch->remove();
- m_ll_watch = nullptr;
-
- store<u32>(ADDR(m_r[RSREG], s16(op)), u32(m_r[RTREG]));
- m_r[RTREG] = 1;
- }
- else
- m_r[RTREG] = 0;
- break;
- case 0x39: // SWC1
- cp1_execute(op);
- break;
- case 0x3a: // SWC2
- cp2_execute(op);
- break;
- //case 0x3b: // *
- case 0x3c: // SCD
- if (m_ll_watch)
- {
- m_ll_watch->remove();
- m_ll_watch = nullptr;
-
- store<u64>(ADDR(m_r[RSREG], s16(op)), m_r[RTREG]);
- m_r[RTREG] = 1;
- }
- else
- m_r[RTREG] = 0;
- break;
- case 0x3d: // SDC1
- cp1_execute(op);
- break;
- case 0x3e: // SDC2
- cp2_execute(op);
- break;
- case 0x3f: // SD
- store<u64>(ADDR(m_r[RSREG], s16(op)), m_r[RTREG]);
- break;
-
- default:
- // * Operation codes marked with an asterisk cause reserved instruction
- // exceptions in all current implementations and are reserved for future
- // versions of the architecture.
- cpu_exception(EXCEPTION_RI);
- break;
- }
-}
-
-void r4000_base_device::cpu_exception(u32 exception, u16 const vector)
-{
- if (exception != EXCEPTION_INT)
- LOGMASKED(LOG_EXCEPTION, "exception 0x%08x\n", exception);
-
- if (!(SR & SR_EXL))
- {
- m_cp0[CP0_EPC] = m_pc;
-
- CAUSE = (CAUSE & CAUSE_IP) | exception;
-
- // if in a branch delay slot, restart at the branch instruction
- if (m_branch_state == DELAY)
- {
- m_cp0[CP0_EPC] -= 4;
- CAUSE |= CAUSE_BD;
- }
-
- SR |= SR_EXL;
- }
- else
- CAUSE = (CAUSE & (CAUSE_BD | CAUSE_IP)) | exception;
-
- m_branch_state = EXCEPTION;
- m_pc = ((SR & SR_BEV) ? s64(s32(0xbfc00200)) : s64(s32(0x80000000))) + vector;
-
- if (exception != EXCEPTION_INT)
- debugger_exception_hook(exception);
-}
-
-void r4000_base_device::cpu_lwl(u32 const op)
-{
- u64 const offset = ADDR(m_r[RSREG], s16(op));
- unsigned const shift = ((offset & 3) ^ R4000_ENDIAN_LE_BE(3, 0)) << 3;
-
- load<u32>(offset & ~3,
- [this, op, shift](u32 const data)
- {
- m_r[RTREG] = s32((m_r[RTREG] & ~u32(~u32(0) << shift)) | (data << shift));
- });
-}
-
-void r4000_base_device::cpu_lwr(u32 const op)
-{
- u64 const offset = ADDR(m_r[RSREG], s16(op));
- unsigned const shift = ((offset & 0x3) ^ R4000_ENDIAN_LE_BE(0, 3)) << 3;
-
- load<u32>(offset & ~3,
- [this, op, shift](u32 const data)
- {
- m_r[RTREG] = s32((m_r[RTREG] & ~u32(~u32(0) >> shift)) | (data >> shift));
- });
-}
-
-void r4000_base_device::cpu_swl(u32 const op)
-{
- u64 const offset = ADDR(m_r[RSREG], s16(op));
- unsigned const shift = ((offset & 3) ^ R4000_ENDIAN_LE_BE(3, 0)) << 3;
-
- store<u32>(offset & ~3, u32(m_r[RTREG]) >> shift, ~u32(0) >> shift);
-}
-
-void r4000_base_device::cpu_swr(u32 const op)
-{
- u64 const offset = ADDR(m_r[RSREG], s16(op));
- unsigned const shift = ((offset & 3) ^ R4000_ENDIAN_LE_BE(0, 3)) << 3;
-
- store<u32>(offset & ~3, u32(m_r[RTREG]) << shift, ~u32(0) << shift);
-}
-
-void r4000_base_device::cpu_ldl(u32 const op)
-{
- u64 const offset = ADDR(m_r[RSREG], s16(op));
- unsigned const shift = ((offset & 7) ^ R4000_ENDIAN_LE_BE(7, 0)) << 3;
-
- load<u64>(offset & ~7,
- [this, op, shift](u64 const data)
- {
- m_r[RTREG] = (m_r[RTREG] & ~u64(~u64(0) << shift)) | (data << shift);
- });
-}
-
-void r4000_base_device::cpu_ldr(u32 const op)
-{
- u64 const offset = ADDR(m_r[RSREG], s16(op));
- unsigned const shift = ((offset & 7) ^ R4000_ENDIAN_LE_BE(0, 7)) << 3;
-
- load<u64>(offset & ~7,
- [this, op, shift](u64 const data)
- {
- m_r[RTREG] = (m_r[RTREG] & ~u64(~u64(0) >> shift)) | (data >> shift);
- });
-}
-
-void r4000_base_device::cpu_sdl(u32 const op)
-{
- u64 const offset = ADDR(m_r[RSREG], s16(op));
- unsigned const shift = ((offset & 7) ^ R4000_ENDIAN_LE_BE(7, 0)) << 3;
-
- store<u64>(offset & ~7, m_r[RTREG] >> shift, ~u64(0) >> shift);
-}
-
-void r4000_base_device::cpu_sdr(u32 const op)
-{
- u64 const offset = ADDR(m_r[RSREG], s16(op));
- unsigned const shift = ((offset & 7) ^ R4000_ENDIAN_LE_BE(0, 7)) << 3;
-
- store<u64>(offset & ~7, m_r[RTREG] << shift, ~u64(0) << shift);
-}
-
-void r4000_base_device::cp0_execute(u32 const op)
-{
- if ((SR & SR_KSU) && !(SR & SR_CU0) && !(SR & (SR_EXL | SR_ERL)))
- {
- cpu_exception(EXCEPTION_CP0);
- return;
- }
-
- switch ((op >> 21) & 0x1f)
- {
- case 0x00: // MFC0
- m_r[RTREG] = s32(cp0_get(RDREG));
- break;
- case 0x01: // DMFC0
- // ε Operation codes marked with epsilon are valid when the
- // processor is operating either in the Kernel mode or in the
- // 64-bit non-Kernel (User or Supervisor) mode. These instructions
- // cause a reserved instruction exception if 64-bit operation is
- // not enabled in User or Supervisor mode.
- if (!(SR & SR_KSU) || (SR & (SR_EXL | SR_ERL)) || cp0_64())
- m_r[RTREG] = cp0_get(RDREG);
- else
- cpu_exception(EXCEPTION_RI);
- break;
- case 0x02: // CFC0
- break;
-
- case 0x04: // MTC0
- cp0_set(RDREG, s64(s32(m_r[RTREG])));
- break;
- case 0x05: // DMTC0
- // ε Operation codes marked with epsilon are valid when the
- // processor is operating either in the Kernel mode or in the
- // 64-bit non-Kernel (User or Supervisor) mode. These instructions
- // cause a reserved instruction exception if 64-bit operation is
- // not enabled in User or Supervisor mode.
- if (!(SR & SR_KSU) || (SR & (SR_EXL | SR_ERL)) || cp0_64())
- cp0_set(RDREG, m_r[RTREG]);
- else
- cpu_exception(EXCEPTION_RI);
- break;
- case 0x06: // CTC0
- break;
-
- case 0x08: // BC0
- switch ((op >> 16) & 0x1f)
- {
- case 0x00: // BC0F
- case 0x01: // BC0T
- case 0x02: // BC0FL
- case 0x03: // BC0TL
- // fall through
-
- default:
- // γ Operation codes marked with a gamma cause a reserved
- // instruction exception. They are reserved for future versions
- // of the architecture.
- cpu_exception(EXCEPTION_RI);
- break;
- }
- break;
-
- case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17:
- case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f:
- // CP0 function
- switch (op & 0x3f)
- {
- case 0x01: // TLBR
- cp0_tlbr();
- break;
- case 0x02: // TLBWI
- cp0_tlbwi(m_cp0[CP0_Index] & 0x3f);
- break;
-
- case 0x06: // TLBWR
- cp0_tlbwr();
- break;
-
- case 0x08: // TLBP
- cp0_tlbp();
- break;
-
- case 0x10: // RFE
- // ξ Operation codes marked with a xi cause a reserved
- // instruction exception on R4000 processors.
- cpu_exception(EXCEPTION_RI);
- break;
-
- case 0x18: // ERET
- if (SR & SR_ERL)
- {
- logerror("eret from error\n");
- m_branch_state = EXCEPTION;
- m_pc = m_cp0[CP0_ErrorEPC];
- SR &= ~SR_ERL;
- }
- else
- {
- m_branch_state = EXCEPTION;
- m_pc = m_cp0[CP0_EPC];
- SR &= ~SR_EXL;
- }
-
- if (m_ll_watch)
- {
- m_ll_watch->remove();
- m_ll_watch = nullptr;
- }
- break;
-
- default:
- // Φ Operation codes marked with a phi are invalid but do not
- // cause reserved instruction exceptions in R4000 implementations.
- break;
- }
- break;
-
- default:
- // γ Operation codes marked with a gamma cause a reserved
- // instruction exception. They are reserved for future versions
- // of the architecture.
- cpu_exception(EXCEPTION_RI);
- break;
- }
-}
-
-u64 r4000_base_device::cp0_get(unsigned const reg)
-{
- switch (reg)
- {
- case CP0_Count:
- return u32((total_cycles() - m_cp0_timer_zero) / 2);
-
- case CP0_Random:
- {
- u8 const wired = m_cp0[CP0_Wired] & 0x3f;
-
- if (wired < ARRAY_LENGTH(m_tlb))
- return ((total_cycles() - m_cp0_timer_zero) % (ARRAY_LENGTH(m_tlb) - wired) + wired) & 0x3f;
- else
- return ARRAY_LENGTH(m_tlb) - 1;
- }
- break;
-
- default:
- return m_cp0[reg];
- }
-}
-
-void r4000_base_device::cp0_set(unsigned const reg, u64 const data)
-{
- switch (reg)
- {
- case CP0_Count:
- m_cp0[CP0_Count] = u32(data);
- m_cp0_timer_zero = total_cycles() - m_cp0[CP0_Count] * 2;
-
- cp0_update_timer();
- break;
-
- case CP0_EntryHi:
- m_cp0[CP0_EntryHi] = data & (EH_R | EH_VPN2_64 | EH_ASID);
- break;
-
- case CP0_Compare:
- m_cp0[CP0_Compare] = u32(data);
- CAUSE &= ~CAUSE_IPEX5;
-
- cp0_update_timer(true);
- break;
-
- case CP0_Status:
- m_cp0[CP0_Status] = u32(data);
-
- // FIXME: software interrupt check
- if (CAUSE & SR & SR_IMSW)
- m_icount = 0;
-
- if (data & SR_RE)
- fatalerror("unimplemented reverse endian mode enabled (%s)\n", machine().describe_context().c_str());
- break;
-
- case CP0_Cause:
- CAUSE = (CAUSE & ~CAUSE_IPSW) | (data & CAUSE_IPSW);
-
- // FIXME: software interrupt check
- if (CAUSE & SR & SR_IMSW)
- m_icount = 0;
- break;
-
- case CP0_PRId:
- // read-only register
- break;
-
- case CP0_Config:
- m_cp0[CP0_Config] = (m_cp0[CP0_Config] & ~CONFIG_WM) | (data & CONFIG_WM);
-
- if (m_cp0[CP0_Config] & CONFIG_IB)
- {
- m_icache_line_size = 32;
- m_icache_shift = 5;
- m_icache_mask_lo = ~u32(0x1f);
- }
- else
- {
- m_icache_line_size = 16;
- m_icache_shift = 4;
- m_icache_mask_lo = ~u32(0xf);
- }
-
- LOGMASKED(LOG_CACHE, "icache/dcache line sizes %d/%d bytes\n",
- m_icache_line_size, m_cp0[CP0_Config] & CONFIG_DB ? 32 : 16);
- break;
-
- default:
- m_cp0[reg] = data;
- break;
- }
-}
-
-void r4000_base_device::cp0_tlbr()
-{
- u8 const index = m_cp0[CP0_Index] & 0x3f;
-
- if (index < ARRAY_LENGTH(m_tlb))
- {
- tlb_entry_t const &entry = m_tlb[index];
-
- m_cp0[CP0_PageMask] = entry.mask;
- m_cp0[CP0_EntryHi] = entry.vpn;
- m_cp0[CP0_EntryLo0] = entry.pfn[0];
- m_cp0[CP0_EntryLo1] = entry.pfn[1];
- }
-}
-
-void r4000_base_device::cp0_tlbwi(u8 const index)
-{
- if (index < ARRAY_LENGTH(m_tlb))
- {
- tlb_entry_t &entry = m_tlb[index];
-
- entry.mask = m_cp0[CP0_PageMask];
- entry.vpn = m_cp0[CP0_EntryHi];
- if ((m_cp0[CP0_EntryLo0] & EL_G) && (m_cp0[CP0_EntryLo1] & EL_G))
- entry.vpn |= EH_G;
- entry.pfn[0] = m_cp0[CP0_EntryLo0];
- entry.pfn[1] = m_cp0[CP0_EntryLo1];
-
- entry.low_bit = 32 - count_leading_zeros((entry.mask >> 1) | 0xfff);
-
- LOGMASKED(LOG_TLB, "tlb write index %02d mask 0x%016x vpn2 0x%016x %c asid 0x%02x pfn0 0x%016x %c%c pfn1 0x%016x %c%c (%s)\n",
- index, entry.mask,
- entry.vpn, entry.vpn & EH_G ? 'G' : '-', entry.vpn & EH_ASID,
- entry.pfn[0] & EL_PFN, entry.pfn[0] & EL_D ? 'D' : '-', entry.pfn[0] & EL_V ? 'V' : '-',
- entry.pfn[1] & EL_PFN, entry.pfn[1] & EL_D ? 'D' : '-', entry.pfn[1] & EL_V ? 'V' : '-',
- machine().describe_context());
- }
-}
-
-void r4000_base_device::cp0_tlbwr()
-{
- u8 const wired = m_cp0[CP0_Wired] & 0x3f;
- u8 const unwired = ARRAY_LENGTH(m_tlb) - wired;
-
- u8 const index = (unwired > 0) ? ((total_cycles() - m_cp0_timer_zero) % unwired + wired) & 0x3f : (ARRAY_LENGTH(m_tlb) - 1);
-
- cp0_tlbwi(index);
-}
-
-void r4000_base_device::cp0_tlbp()
-{
- m_cp0[CP0_Index] = 0x80000000;
- for (u8 index = 0; index < ARRAY_LENGTH(m_tlb); index++)
- {
- tlb_entry_t const &entry = m_tlb[index];
-
- u64 const mask = (cp0_64() ? EH_R | (EH_VPN2_64 & ~entry.mask) : (EH_VPN2_32 & ~entry.mask))
- | ((entry.vpn & EH_G) ? 0 : EH_ASID);
-
- if ((entry.vpn & mask) == (m_cp0[CP0_EntryHi] & mask))
- {
- m_cp0[CP0_Index] = index;
- break;
- }
- }
-
- if (m_cp0[CP0_Index] == 0x80000000)
- LOGMASKED(LOG_TLB, "tlbp miss 0x%08x\n", m_cp0[CP0_EntryHi]);
- else
- LOGMASKED(LOG_TLB, "tlbp hit 0x%08x index %02d\n", m_cp0[CP0_EntryHi], m_cp0[CP0_Index]);
-}
-
-void r4000_base_device::cp0_update_timer(bool start)
-{
- if (start || m_cp0_timer->enabled())
- {
- u32 const count = (total_cycles() - m_cp0_timer_zero) / 2;
- u32 const delta = m_cp0[CP0_Compare] - count;
-
- m_cp0_timer->adjust(cycles_to_attotime(u64(delta) * 2));
- }
-}
-
-TIMER_CALLBACK_MEMBER(r4000_base_device::cp0_timer_callback)
-{
- m_cp0[CP0_Cause] |= CAUSE_IPEX5;
-}
-
-bool r4000_base_device::cp0_64() const
-{
- switch (SR & (SR_KSU | SR_ERL | SR_EXL))
- {
- case SR_KSU_U: return bool(SR & SR_UX);
- case SR_KSU_S: return bool(SR & SR_SX);
-
- default:
- return bool(SR & SR_KX);
- }
-}
-
-void r4000_base_device::cp1_execute(u32 const op)
-{
- if (!(SR & SR_CU1))
- {
- cpu_exception(EXCEPTION_CP1);
- return;
- }
-
- softfloat_exceptionFlags = 0;
- switch (op >> 26)
- {
- case 0x11: // COP1
- switch ((op >> 21) & 0x1f)
- {
- case 0x00: // MFC1
- if (SR & SR_FR)
- m_r[RTREG] = s64(s32(m_f[RDREG]));
- else
- if (RDREG & 1)
- // move the high half of the even floating point register
- m_r[RTREG] = s64(s32(m_f[RDREG & ~1] >> 32));
- else
- // move the low half of the even floating point register
- m_r[RTREG] = s64(s32(m_f[RDREG & ~1] >> 0));
- break;
- case 0x01: // DMFC1
- // TODO: MIPS3 only
- if ((SR & SR_FR) || !(RDREG & 1))
- m_r[RTREG] = m_f[RDREG];
- break;
- case 0x02: // CFC1
- switch (RDREG)
- {
- case 0: m_r[RTREG] = m_fcr0; break;
- case 30: m_r[RTREG] = m_fcr30; break;
- case 31: m_r[RTREG] = m_fcr31; break;
-
- default:
- logerror("cfc1 undefined fpu control register %d (%s)\n", RDREG, machine().describe_context());
- break;
- }
- break;
- case 0x04: // MTC1
- if (SR & SR_FR)
- m_f[RDREG] = u32(m_r[RTREG]);
- else
- if (RDREG & 1)
- // load the high half of the even floating point register
- m_f[RDREG & ~1] = (m_r[RTREG] << 32) | u32(m_f[RDREG & ~1]);
- else
- // load the low half of the even floating point register
- m_f[RDREG & ~1] = (m_f[RDREG & ~1] & ~0xffffffffULL) | u32(m_r[RTREG]);
- break;
- case 0x05: // DMTC1
- // TODO: MIPS3 only
- if ((SR & SR_FR) || !(RDREG & 1))
- m_f[RDREG] = m_r[RTREG];
- break;
- case 0x06: // CTC1
- switch (RDREG)
- {
- case 0: // register is read-only
- break;
-
- case 30: // unknown
- m_fcr30 = u32(m_r[RTREG]);
- break;
-
- case 31:
- m_fcr31 = u32(m_r[RTREG]);
-
- // update rounding mode
- switch (m_fcr31 & FCR31_RM)
- {
- case 0: softfloat_roundingMode = softfloat_round_near_even; break;
- case 1: softfloat_roundingMode = softfloat_round_minMag; break;
- case 2: softfloat_roundingMode = softfloat_round_max; break;
- case 3: softfloat_roundingMode = softfloat_round_min; break;
- }
-
- // exception check
- if ((m_fcr31 & FCR31_CE) || ((m_fcr31 & FCR31_CM) >> 5) & (m_fcr31 & FCR31_EM))
- cpu_exception(EXCEPTION_FPE);
-
- break;
-
- default:
- logerror("ctc1 undefined fpu control register %d (%s)\n", RDREG, machine().describe_context());
- break;
- }
- break;
- case 0x08: // BC
- switch ((op >> 16) & 0x1f)
- {
- case 0x00: // BC1F
- if (!(m_fcr31 & FCR31_C))
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
- break;
- case 0x01: // BC1T
- if (m_fcr31 & FCR31_C)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
- break;
- case 0x02: // BC1FL
- if (!(m_fcr31 & FCR31_C))
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
- else
- m_branch_state = NULLIFY;
- break;
- case 0x03: // BC1TL
- if (m_fcr31 & FCR31_C)
- {
- m_branch_state = BRANCH;
- m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2);
- }
- else
- m_branch_state = NULLIFY;
- break;
-
- default:
- // reserved instructions
- cpu_exception(EXCEPTION_RI);
- break;
- }
- break;
-
- case 0x10: // S
- switch (op & 0x3f)
- {
- case 0x00: // ADD.S
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f32_add(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }).v);
- break;
- case 0x01: // SUB.S
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f32_sub(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }).v);
- break;
- case 0x02: // MUL.S
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f32_mul(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }).v);
- break;
- case 0x03: // DIV.S
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f32_div(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }).v);
- break;
- case 0x04: // SQRT.S
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f32_sqrt(float32_t{ u32(m_f[FSREG]) }).v);
- break;
- case 0x05: // ABS.S
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- if (f32_lt(float32_t{ u32(m_f[FSREG]) }, float32_t{ 0 }))
- cp1_set(FDREG, f32_mul(float32_t{ u32(m_f[FSREG]) }, i32_to_f32(-1)).v);
- else
- cp1_set(FDREG, m_f[FSREG]);
- }
- break;
- case 0x06: // MOV.S
- if ((SR & SR_FR) || !(op & ODD_REGS))
- m_f[FDREG] = m_f[FSREG];
- break;
- case 0x07: // NEG.S
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f32_mul(float32_t{ u32(m_f[FSREG]) }, i32_to_f32(-1)).v);
- break;
- case 0x08: // ROUND.L.S
- // TODO: MIPS3 only
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f32_to_i64(float32_t{ u32(m_f[FSREG]) }, softfloat_round_near_even, true));
- break;
- case 0x09: // TRUNC.L.S
- // TODO: MIPS3 only
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f32_to_i64(float32_t{ u32(m_f[FSREG]) }, softfloat_round_minMag, true));
- break;
- case 0x0a: // CEIL.L.S
- // TODO: MIPS3 only
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f32_to_i64(float32_t{ u32(m_f[FSREG]) }, softfloat_round_max, true));
- break;
- case 0x0b: // FLOOR.L.S
- // TODO: MIPS3 only
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f32_to_i64(float32_t{ u32(m_f[FSREG]) }, softfloat_round_min, true));
- break;
- case 0x0c: // ROUND.W.S
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f32_to_i32(float32_t{ u32(m_f[FSREG]) }, softfloat_round_near_even, true));
- break;
- case 0x0d: // TRUNC.W.S
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f32_to_i32(float32_t{ u32(m_f[FSREG]) }, softfloat_round_minMag, true));
- break;
- case 0x0e: // CEIL.W.S
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f32_to_i32(float32_t{ u32(m_f[FSREG]) }, softfloat_round_max, true));
- break;
- case 0x0f: // FLOOR.W.S
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f32_to_i32(float32_t{ u32(m_f[FSREG]) }, softfloat_round_min, true));
- break;
-
- case 0x21: // CVT.D.S
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f32_to_f64(float32_t{ u32(m_f[FSREG]) }).v);
- break;
- case 0x24: // CVT.W.S
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f32_to_i32(float32_t{ u32(m_f[FSREG]) }, softfloat_roundingMode, true));
- break;
- case 0x25: // CVT.L.S
- // TODO: MIPS3 only
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f32_to_i64(float32_t{ u32(m_f[FSREG]) }, softfloat_roundingMode, true));
- break;
-
- case 0x30: // C.F.S (false)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- m_fcr31 &= ~FCR31_C;
- break;
- case 0x31: // C.UN.S (unordered)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- // detect unordered
- f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) });
- if (softfloat_exceptionFlags & softfloat_flag_invalid)
- m_fcr31 |= FCR31_C;
- else
- m_fcr31 &= ~FCR31_C;
- }
- break;
- case 0x32: // C.EQ.S (equal)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- if (f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }))
- m_fcr31 |= FCR31_C;
- else
- m_fcr31 &= ~FCR31_C;
- }
- break;
- case 0x33: // C.UEQ.S (unordered equal)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- if (f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
- m_fcr31 |= FCR31_C;
- else
- m_fcr31 &= ~FCR31_C;
- }
- break;
- case 0x34: // C.OLT.S (less than)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- if (f32_lt(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }))
- m_fcr31 |= FCR31_C;
- else
- m_fcr31 &= ~FCR31_C;
- }
- break;
- case 0x35: // C.ULT.S (unordered less than)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- if (f32_lt(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
- m_fcr31 |= FCR31_C;
- else
- m_fcr31 &= ~FCR31_C;
- }
- break;
- case 0x36: // C.OLE.S (less than or equal)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- if (f32_le(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }))
- m_fcr31 |= FCR31_C;
- else
- m_fcr31 &= ~FCR31_C;
- }
- break;
- case 0x37: // C.ULE.S (unordered less than or equal)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- if (f32_le(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
- m_fcr31 |= FCR31_C;
- else
- m_fcr31 &= ~FCR31_C;
- }
- break;
-
- case 0x38: // C.SF.S (signalling false)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- // detect unordered
- f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) });
-
- m_fcr31 &= ~FCR31_C;
-
- if (softfloat_exceptionFlags & softfloat_flag_invalid)
- {
- m_fcr31 |= FCR31_CV;
- cpu_exception(EXCEPTION_FPE);
- }
- }
- break;
- case 0x39: // C.NGLE.S (not greater, less than or equal)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- // detect unordered
- f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) });
-
- if (softfloat_exceptionFlags & softfloat_flag_invalid)
- {
- m_fcr31 |= FCR31_C | FCR31_CV;
- cpu_exception(EXCEPTION_FPE);
- }
- else
- m_fcr31 &= ~FCR31_C;
- }
- break;
- case 0x3a: // C.SEQ.S (signalling equal)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- if (f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }))
- m_fcr31 |= FCR31_C;
- else
- m_fcr31 &= ~FCR31_C;
-
- if (softfloat_exceptionFlags & softfloat_flag_invalid)
- {
- m_fcr31 |= FCR31_CV;
- cpu_exception(EXCEPTION_FPE);
- }
- }
- break;
- case 0x3b: // C.NGL.S (not greater or less than)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- if (f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
- m_fcr31 |= FCR31_C;
- else
- m_fcr31 &= ~FCR31_C;
-
- if (softfloat_exceptionFlags & softfloat_flag_invalid)
- {
- m_fcr31 |= FCR31_CV;
- cpu_exception(EXCEPTION_FPE);
- }
- }
- break;
- case 0x3c: // C.LT.S (less than)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- if (f32_lt(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }))
- m_fcr31 |= FCR31_C;
- else
- m_fcr31 &= ~FCR31_C;
-
- if (softfloat_exceptionFlags & softfloat_flag_invalid)
- {
- m_fcr31 |= FCR31_CV;
- cpu_exception(EXCEPTION_FPE);
- }
- }
- break;
- case 0x3d: // C.NGE.S (not greater or equal)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- if (f32_lt(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
- m_fcr31 |= FCR31_C;
- else
- m_fcr31 &= ~FCR31_C;
-
- if (softfloat_exceptionFlags & softfloat_flag_invalid)
- {
- m_fcr31 |= FCR31_CV;
- cpu_exception(EXCEPTION_FPE);
- }
- }
- break;
- case 0x3e: // C.LE.S (less than or equal)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- if (f32_le(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }))
- m_fcr31 |= FCR31_C;
- else
- m_fcr31 &= ~FCR31_C;
-
- if (softfloat_exceptionFlags & softfloat_flag_invalid)
- {
- m_fcr31 |= FCR31_CV;
- cpu_exception(EXCEPTION_FPE);
- }
- }
- break;
- case 0x3f: // C.NGT.S (not greater than)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- if (f32_le(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
- m_fcr31 |= FCR31_C;
- else
- m_fcr31 &= ~FCR31_C;
-
- if (softfloat_exceptionFlags & softfloat_flag_invalid)
- {
- m_fcr31 |= FCR31_CV;
- cpu_exception(EXCEPTION_FPE);
- }
- }
- break;
-
- default: // unimplemented operations
- m_fcr31 |= FCR31_CE;
- cpu_exception(EXCEPTION_FPE);
- break;
- }
- break;
- case 0x11: // D
- switch (op & 0x3f)
- {
- case 0x00: // ADD.D
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f64_add(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }).v);
- break;
- case 0x01: // SUB.D
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f64_sub(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }).v);
- break;
- case 0x02: // MUL.D
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f64_mul(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }).v);
- break;
- case 0x03: // DIV.D
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f64_div(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }).v);
- break;
- case 0x04: // SQRT.D
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f64_sqrt(float64_t{ m_f[FSREG] }).v);
- break;
- case 0x05: // ABS.D
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- if (f64_lt(float64_t{ m_f[FSREG] }, float64_t{ 0 }))
- cp1_set(FDREG, f64_mul(float64_t{ m_f[FSREG] }, i32_to_f64(-1)).v);
- else
- cp1_set(FDREG, m_f[FSREG]);
- }
- break;
- case 0x06: // MOV.D
- if ((SR & SR_FR) || !(op & ODD_REGS))
- m_f[FDREG] = m_f[FSREG];
- break;
- case 0x07: // NEG.D
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f64_mul(float64_t{ m_f[FSREG] }, i32_to_f64(-1)).v);
- break;
- case 0x08: // ROUND.L.D
- // TODO: MIPS3 only
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f64_to_i64(float64_t{ m_f[FSREG] }, softfloat_round_near_even, true));
- break;
- case 0x09: // TRUNC.L.D
- // TODO: MIPS3 only
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f64_to_i64(float64_t{ m_f[FSREG] }, softfloat_round_minMag, true));
- break;
- case 0x0a: // CEIL.L.D
- // TODO: MIPS3 only
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f64_to_i64(float64_t{ m_f[FSREG] }, softfloat_round_max, true));
- break;
- case 0x0b: // FLOOR.L.D
- // TODO: MIPS3 only
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f64_to_i64(float64_t{ m_f[FSREG] }, softfloat_round_min, true));
- break;
- case 0x0c: // ROUND.W.D
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f64_to_i32(float64_t{ m_f[FSREG] }, softfloat_round_near_even, true));
- break;
- case 0x0d: // TRUNC.W.D
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f64_to_i32(float64_t{ m_f[FSREG] }, softfloat_round_minMag, true));
- break;
- case 0x0e: // CEIL.W.D
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f64_to_i32(float64_t{ m_f[FSREG] }, softfloat_round_max, true));
- break;
- case 0x0f: // FLOOR.W.D
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f64_to_i32(float64_t{ m_f[FSREG] }, softfloat_round_min, true));
- break;
-
- case 0x20: // CVT.S.D
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f64_to_f32(float64_t{ m_f[FSREG] }).v);
- break;
- case 0x24: // CVT.W.D
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f64_to_i32(float64_t{ m_f[FSREG] }, softfloat_roundingMode, true));
- break;
- case 0x25: // CVT.L.D
- // TODO: MIPS3 only
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, f64_to_i64(float64_t{ m_f[FSREG] }, softfloat_roundingMode, true));
- break;
-
- case 0x30: // C.F.D (false)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- m_fcr31 &= ~FCR31_C;
- break;
- case 0x31: // C.UN.D (unordered)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- // detect unordered
- f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] });
- if (softfloat_exceptionFlags & softfloat_flag_invalid)
- m_fcr31 |= FCR31_C;
- else
- m_fcr31 &= ~FCR31_C;
- }
- break;
- case 0x32: // C.EQ.D (equal)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- if (f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }))
- m_fcr31 |= FCR31_C;
- else
- m_fcr31 &= ~FCR31_C;
- }
- break;
- case 0x33: // C.UEQ.D (unordered equal)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- if (f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
- m_fcr31 |= FCR31_C;
- else
- m_fcr31 &= ~FCR31_C;
- }
- break;
- case 0x34: // C.OLT.D (less than)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- if (f64_lt(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }))
- m_fcr31 |= FCR31_C;
- else
- m_fcr31 &= ~FCR31_C;
- }
- break;
- case 0x35: // C.ULT.D (unordered less than)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- if (f64_lt(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
- m_fcr31 |= FCR31_C;
- else
- m_fcr31 &= ~FCR31_C;
- }
- break;
- case 0x36: // C.OLE.D (less than or equal)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- if (f64_le(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }))
- m_fcr31 |= FCR31_C;
- else
- m_fcr31 &= ~FCR31_C;
- }
- break;
- case 0x37: // C.ULE.D (unordered less than or equal)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- if (f64_le(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
- m_fcr31 |= FCR31_C;
- else
- m_fcr31 &= ~FCR31_C;
- }
- break;
-
- case 0x38: // C.SF.D (signalling false)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- // detect unordered
- f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] });
-
- m_fcr31 &= ~FCR31_C;
-
- if (softfloat_exceptionFlags & softfloat_flag_invalid)
- {
- m_fcr31 |= FCR31_CV;
- cpu_exception(EXCEPTION_FPE);
- }
- }
- break;
- case 0x39: // C.NGLE.D (not greater, less than or equal)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- // detect unordered
- f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] });
-
- if (softfloat_exceptionFlags & softfloat_flag_invalid)
- {
- m_fcr31 |= FCR31_C | FCR31_CV;
- cpu_exception(EXCEPTION_FPE);
- }
- else
- m_fcr31 &= ~FCR31_C;
- }
- break;
- case 0x3a: // C.SEQ.D (signalling equal)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- if (f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }))
- m_fcr31 |= FCR31_C;
- else
- m_fcr31 &= ~FCR31_C;
-
- if (softfloat_exceptionFlags & softfloat_flag_invalid)
- {
- m_fcr31 |= FCR31_CV;
- cpu_exception(EXCEPTION_FPE);
- }
- }
- break;
- case 0x3b: // C.NGL.D (not greater or less than)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- if (f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
- m_fcr31 |= FCR31_C;
- else
- m_fcr31 &= ~FCR31_C;
-
- if (softfloat_exceptionFlags & softfloat_flag_invalid)
- {
- m_fcr31 |= FCR31_CV;
- cpu_exception(EXCEPTION_FPE);
- }
- }
- break;
- case 0x3c: // C.LT.D (less than)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- if (f64_lt(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }))
- m_fcr31 |= FCR31_C;
- else
- m_fcr31 &= ~FCR31_C;
-
- if (softfloat_exceptionFlags & softfloat_flag_invalid)
- {
- m_fcr31 |= FCR31_CV;
- cpu_exception(EXCEPTION_FPE);
- }
- }
- break;
- case 0x3d: // C.NGE.D (not greater or equal)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- if (f64_lt(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
- m_fcr31 |= FCR31_C;
- else
- m_fcr31 &= ~FCR31_C;
-
- if (softfloat_exceptionFlags & softfloat_flag_invalid)
- {
- m_fcr31 |= FCR31_CV;
- cpu_exception(EXCEPTION_FPE);
- }
- }
- break;
- case 0x3e: // C.LE.D (less than or equal)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- if (f64_le(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }))
- m_fcr31 |= FCR31_C;
- else
- m_fcr31 &= ~FCR31_C;
-
- if (softfloat_exceptionFlags & softfloat_flag_invalid)
- {
- m_fcr31 |= FCR31_CV;
- cpu_exception(EXCEPTION_FPE);
- }
- }
- break;
- case 0x3f: // C.NGT.D (not greater than)
- if ((SR & SR_FR) || !(op & ODD_REGS))
- {
- if (f64_le(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid))
- m_fcr31 |= FCR31_C;
- else
- m_fcr31 &= ~FCR31_C;
-
- if (softfloat_exceptionFlags & softfloat_flag_invalid)
- {
- m_fcr31 |= FCR31_CV;
- cpu_exception(EXCEPTION_FPE);
- }
- }
- break;
-
- default: // unimplemented operations
- m_fcr31 |= FCR31_CE;
- cpu_exception(EXCEPTION_FPE);
- break;
- }
- break;
- case 0x14: // W
- switch (op & 0x3f)
- {
- case 0x20: // CVT.S.W
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, i32_to_f32(s32(m_f[FSREG])).v);
- break;
- case 0x21: // CVT.D.W
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, i32_to_f64(s32(m_f[FSREG])).v);
- break;
-
- default: // unimplemented operations
- m_fcr31 |= FCR31_CE;
- cpu_exception(EXCEPTION_FPE);
- break;
- }
- break;
- case 0x15: // L
- // TODO: MIPS3 only
- switch (op & 0x3f)
- {
- case 0x02a00020: // CVT.S.L
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, i64_to_f32(s64(m_f[FSREG])).v);
- break;
- case 0x02a00021: // CVT.D.L
- if ((SR & SR_FR) || !(op & ODD_REGS))
- cp1_set(FDREG, i64_to_f64(s64(m_f[FSREG])).v);
- break;
-
- default: // unimplemented operations
- m_fcr31 |= FCR31_CE;
- cpu_exception(EXCEPTION_FPE);
- break;
- }
- break;
-
- default: // unimplemented operations
- m_fcr31 |= FCR31_CE;
- cpu_exception(EXCEPTION_FPE);
- break;
- }
- break;
-
- case 0x31: // LWC1
- load<u32>(ADDR(m_r[RSREG], s16(op)),
- [this, op](u32 data)
- {
- if (SR & SR_FR)
- m_f[RTREG] = data;
- else
- if (RTREG & 1)
- // load the high half of the even floating point register
- m_f[RTREG & ~1] = (u64(data) << 32) | u32(m_f[RTREG & ~1]);
- else
- // load the low half of the even floating point register
- m_f[RTREG & ~1] = (m_f[RTREG & ~1] & ~0xffffffffULL) | data;
- });
- break;
-
- case 0x35: // LDC1
- load<u64>(ADDR(m_r[RSREG], s16(op)),
- [this, op](u64 data)
- {
- if ((SR & SR_FR) || !(RTREG & 1))
- m_f[RTREG] = data;
- });
- break;
-
- case 0x39: // SWC1
- if (SR & SR_FR)
- store<u32>(ADDR(m_r[RSREG], s16(op)), u32(m_f[RTREG]));
- else
- if (RTREG & 1)
- // store the high half of the even floating point register
- store<u32>(ADDR(m_r[RSREG], s16(op)), u32(m_f[RTREG & ~1] >> 32));
- else
- // store the low half of the even floating point register
- store<u32>(ADDR(m_r[RSREG], s16(op)), u32(m_f[RTREG & ~1]));
- break;
-
- case 0x3d: // SDC1
- if ((SR & SR_FR) || !(RTREG & 1))
- store<u64>(ADDR(m_r[RSREG], s16(op)), m_f[RTREG]);
- break;
- }
-}
-
-void r4000_base_device::cp1_set(unsigned const reg, u64 const data)
-{
- // translate softfloat exception flags to cause register
- if (softfloat_exceptionFlags)
- {
- if (softfloat_exceptionFlags & softfloat_flag_inexact)
- m_fcr31 |= FCR31_CI;
- if (softfloat_exceptionFlags & softfloat_flag_underflow)
- m_fcr31 |= FCR31_CU;
- if (softfloat_exceptionFlags & softfloat_flag_overflow)
- m_fcr31 |= FCR31_CO;
- if (softfloat_exceptionFlags & softfloat_flag_infinite)
- m_fcr31 |= FCR31_CZ;
- if (softfloat_exceptionFlags & softfloat_flag_invalid)
- m_fcr31 |= FCR31_CV;
-
- // check if exception is enabled
- if (((m_fcr31 & FCR31_CM) >> 5) & (m_fcr31 & FCR31_EM))
- {
- cpu_exception(EXCEPTION_FPE);
- return;
- }
-
- // set flags
- m_fcr31 |= ((m_fcr31 & FCR31_CM) >> 10);
- }
-
- m_f[reg] = data;
-}
-
-void r4000_base_device::cp2_execute(u32 const op)
-{
- if (!(SR & SR_CU2))
- {
- cpu_exception(EXCEPTION_CP2);
- return;
- }
-
- switch (op >> 26)
- {
- case 0x12: // COP2
- switch ((op >> 21) & 0x1f)
- {
- case 0x00: // MFC2
- logerror("mfc2 unimplemented (%s)\n", machine().describe_context());
- break;
- case 0x01: // DMFC2
- // ε Operation codes marked with epsilon are valid when the
- // processor is operating either in the Kernel mode or in the
- // 64-bit non-Kernel (User or Supervisor) mode. These instructions
- // cause a reserved instruction exception if 64-bit operation is
- // not enabled in User or Supervisor mode.
- if (!(SR & SR_KSU) || (SR & (SR_EXL | SR_ERL)) || cp0_64())
- logerror("dmfc2 unimplemented (%s)\n", machine().describe_context());
- else
- cpu_exception(EXCEPTION_RI);
- break;
- case 0x02: // CFC2
- logerror("cfc2 unimplemented (%s)\n", machine().describe_context());
- break;
-
- case 0x04: // MTC2
- logerror("mtc2 unimplemented (%s)\n", machine().describe_context());
- break;
- case 0x05: // DMTC2
- // ε Operation codes marked with epsilon are valid when the
- // processor is operating either in the Kernel mode or in the
- // 64-bit non-Kernel (User or Supervisor) mode. These instructions
- // cause a reserved instruction exception if 64-bit operation is
- // not enabled in User or Supervisor mode.
- if (!(SR & SR_KSU) || (SR & (SR_EXL | SR_ERL)) || cp0_64())
- logerror("dmtc2 unimplemented (%s)\n", machine().describe_context());
- else
- cpu_exception(EXCEPTION_RI);
- break;
- case 0x06: // CTC2
- logerror("ctc2 unimplemented (%s)\n", machine().describe_context());
- break;
-
- case 0x08: // BC2
- switch ((op >> 16) & 0x1f)
- {
- case 0x00: // BC2F
- case 0x01: // BC2F
- case 0x02: // BC2FL
- case 0x03: // BC2TL
- logerror("bc2 unimplemented (%s)\n", machine().describe_context());
- break;
-
- default:
- // γ Operation codes marked with a gamma cause a reserved
- // instruction exception. They are reserved for future versions
- // of the architecture.
- cpu_exception(EXCEPTION_RI);
- break;
- }
- break;
-
- case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17:
- case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f:
- // CP2 function
- logerror("function unimplemented (%s)\n", machine().describe_context());
- break;
-
- default:
- // γ Operation codes marked with a gamma cause a reserved
- // instruction exception. They are reserved for future versions
- // of the architecture.
- cpu_exception(EXCEPTION_RI);
- break;
- }
- break;
-
- case 0x32: // LWC2
- logerror("lwc2 unimplemented (%s)\n", machine().describe_context());
- break;
-
- case 0x36: // LDC2
- logerror("ldc2 unimplemented (%s)\n", machine().describe_context());
- break;
-
- case 0x3a: // SWC2
- logerror("swc2 unimplemented (%s)\n", machine().describe_context());
- break;
-
- case 0x3e: // SDC2
- logerror("sdc2 unimplemented (%s)\n", machine().describe_context());
- break;
- }
-}
-
-r4000_base_device::translate_t r4000_base_device::translate(int intention, u64 &address)
-{
- /*
- * Decode the program address into one of the following ranges depending on
- * the active status register bits.
- *
- * 32-bit modes
- * user: 0x0000'0000-0x7fff'ffff (useg, mapped)
- *
- * super: 0x0000'0000-0x7fff'ffff (suseg, mapped)
- * 0xc000'0000-0xdfff'ffff (ssseg, mapped)
- *
- * kernel: 0x0000'0000-0x7fff'ffff (kuseg, mapped)
- * 0x8000'0000-0x9fff'ffff (kseg0, unmapped, cached)
- * 0xa000'0000-0xbfff'ffff (kseg1, unmapped, uncached)
- * 0xc000'0000-0xdfff'ffff (ksseg, mapped)
- * 0xe000'0000-0xffff'ffff (kseg3, mapped)
- *
- * 64-bit modes
- * user: 0x0000'0000'0000'0000-0x0000'00ff'ffff'ffff (xuseg, mapped)
- *
- * super: 0x0000'0000'0000'0000-0x0000'00ff'ffff'ffff (xsuseg, mapped)
- * 0x4000'0000'0000'0000-0x4000'00ff'ffff'ffff (xsseg, mapped)
- * 0xffff'ffff'c000'0000-0xffff'ffff'dfff'ffff (csseg, mapped)
- *
- * kernel: 0x0000'0000'0000'0000-0x0000'00ff'ffff'ffff (xkuseg, mapped)
- * 0x4000'0000'0000'0000-0x4000'00ff'ffff'ffff (xksseg, mapped)
- * 0x8000'0000'0000'0000-0xbfff'ffff'ffff'ffff (xkphys, unmapped)
- * 0xc000'0000'0000'0000-0xc000'00ff'7fff'ffff (xkseg, mapped)
- * 0xffff'ffff'8000'0000-0xffff'ffff'9fff'ffff (ckseg0, unmapped, cached)
- * 0xffff'ffff'a000'0000-0xffff'ffff'bfff'ffff (ckseg1, unmapped, uncached)
- * 0xffff'ffff'c000'0000-0xffff'ffff'dfff'ffff (cksseg, mapped)
- * 0xffff'ffff'e000'0000-0xffff'ffff'ffff'ffff (ckseg3, mapped)
- */
-
- bool extended = false;
-
- switch (SR & (SR_KSU | SR_ERL | SR_EXL))
- {
- case SR_KSU_U:
- // user mode
- if (SR & SR_UX)
- {
- // 64-bit user mode
- if (address & 0xffff'ff00'0000'0000)
- return ERROR; // exception
- else
- extended = true; // xuseg
- }
- else
- {
- // 32-bit user mode
- if (address & 0xffff'ffff'8000'0000)
- return ERROR; // exception
- else
- extended = false; // useg
- }
- break;
-
- case SR_KSU_S:
- // supervisor mode
- if (SR & SR_SX)
- {
- // 64-bit supervisor mode
- if (address & 0xffff'ff00'0000'0000)
- if ((address & 0xffff'ff00'0000'0000) == 0x4000'0000'0000'0000)
- extended = true; // xsseg
- else
- if ((address & 0xffff'ffff'e000'0000) == 0xffff'ffff'c000'0000)
- extended = true; // csseg
- else
- return ERROR; // exception
- else
- extended = true; // xsuseg
- }
- else
- {
- // 32-bit supervisor mode
- if (address & 0xffff'ffff'8000'0000)
- if ((address & 0xffff'ffff'e000'0000) == 0xffff'ffff'c000'0000)
- extended = false; // sseg
- else
- return ERROR; // exception
- else
- extended = false; // suseg
- }
- break;
-
- case SR_KSU_U | SR_KSU_S:
- fatalerror("invalid ksu bits 0x%08x (%s)\n", u32(SR), machine().describe_context().c_str());
- break;
-
- default:
- // kernel mode
- if (SR & SR_KX)
- {
- // 64-bit kernel mode
- if (address & 0xffff'ff00'0000'0000)
- if ((address & 0xffff'ff00'0000'0000) == 0x4000'0000'0000'0000)
- extended = true; // xksseg
- else
- if ((address & 0xc000'0000'0000'0000) == 0x8000'0000'0000'0000)
- {
- address &= 0x0000'000f'ffff'ffff; // xkphys
-
- // FIXME: caching depends on top three bits
- return CACHED;
- }
- else
- if ((address & 0xffff'ff00'0000'0000) == 0xc000'0000'0000'0000)
- if ((address & 0x0000'00ff'8000'0000) == 0x0000'00ff'8000'0000)
- return ERROR; // exception
- else
- extended = true; // xkseg
- else
- // FIXME: ckseg0 caching depends on config regiter
- switch (address & 0xffff'ffff'e000'0000)
- {
- case 0xffff'ffff'8000'0000: address &= 0x7fff'ffff; return CACHED; // ckseg0
- case 0xffff'ffff'a000'0000: address &= 0x1fff'ffff; return UNCACHED; // ckseg1
- case 0xffff'ffff'c000'0000: extended = true; break; // cksseg
- case 0xffff'ffff'e000'0000: extended = true; break; // ckseg3
- default: return ERROR; // exception
- }
- else
- if (SR & SR_ERL)
- // FIXME: documentation says 2^31, but assume it should be 2^40
- return UNCACHED; // xkuseg (unmapped, uncached)
- else
- extended = true; // xkuseg
- }
- else
- {
- // 32-bit kernel mode
- if (address & 0xffff'ffff'8000'0000)
- switch (address & 0xffff'ffff'e000'0000)
- {
- case 0xffff'ffff'8000'0000: address &= 0x7fff'ffff; return CACHED; // kseg0
- case 0xffff'ffff'a000'0000: address &= 0x1fff'ffff; return UNCACHED; // kseg1
- case 0xffff'ffff'c000'0000: extended = false; break; // ksseg
- case 0xffff'ffff'e000'0000: extended = false; break; // kseg3
- default: return ERROR; // exception
- }
- else
- if (SR & SR_ERL)
- return UNCACHED; // kuseg (unmapped, uncached)
- else
- extended = false; // kuseg
- }
- break;
- }
-
- // address needs translation, using a combination of VPN2 and ASID
- u64 const key = (address & (extended ? (EH_R | EH_VPN2_64) : EH_VPN2_32)) | (m_cp0[CP0_EntryHi] & EH_ASID);
-
- unsigned *mru = m_tlb_mru[intention & TRANSLATE_TYPE_MASK];
- if (LOG_STATS)
- m_tlb_scans++;
-
- bool invalid = false;
- bool modify = false;
- for (unsigned i = 0; i < ARRAY_LENGTH(m_tlb); i++)
- {
- unsigned const index = mru[i];
- tlb_entry_t const &entry = m_tlb[index];
-
- // test vpn and asid
- u64 const mask = (extended ? EH_R | (EH_VPN2_64 & ~entry.mask) : (EH_VPN2_32 & ~entry.mask))
- | ((entry.vpn & EH_G) ? 0 : EH_ASID);
-
- if ((entry.vpn & mask) != (key & mask))
- continue;
-
- if (LOG_STATS)
- m_tlb_loops += i + 1;
-
- u64 const pfn = entry.pfn[BIT(address, entry.low_bit)];
-
- // test valid
- if (!(pfn & EL_V))
- {
- invalid = true;
- break;
- }
-
- // test dirty
- if ((intention & TRANSLATE_WRITE) && !(pfn & EL_D))
- {
- modify = true;
- break;
- }
-
- // translate the address
- address &= (entry.mask >> 1) | 0xfff;
- address |= ((pfn & EL_PFN) << 6) & ~(entry.mask >> 1);
-
- // promote the entry in the mru index
- if (i > 0)
- std::swap(mru[i - 1], mru[i]);
-
- return ((pfn & EL_C) == C_2) ? UNCACHED : CACHED;
- }
-
- // tlb miss, invalid entry, or a store to a non-dirty entry
- if (!machine().side_effects_disabled() && !(intention & TRANSLATE_DEBUG_MASK))
- {
- if (VERBOSE & LOG_TLB)
- {
- char const mode[] = { 'r', 'w', 'x' };
-
- if (modify)
- LOGMASKED(LOG_TLB, "tlb modify asid %d address 0x%016x (%s)\n",
- m_cp0[CP0_EntryHi] & EH_ASID, address, machine().describe_context());
- else
- LOGMASKED(LOG_TLB, "tlb miss %c asid %d address 0x%016x (%s)\n",
- mode[intention & TRANSLATE_TYPE_MASK], m_cp0[CP0_EntryHi] & EH_ASID, address, machine().describe_context());
- }
-
- // load tlb exception registers
- m_cp0[CP0_BadVAddr] = address;
- m_cp0[CP0_EntryHi] = key;
- m_cp0[CP0_Context] = (m_cp0[CP0_Context] & CONTEXT_PTEBASE) | ((address >> 9) & CONTEXT_BADVPN2);
- m_cp0[CP0_XContext] = (m_cp0[CP0_XContext] & XCONTEXT_PTEBASE) | ((address >> 31) & XCONTEXT_R) | ((address >> 9) & XCONTEXT_BADVPN2);
-
- if (invalid || modify || (SR & SR_EXL))
- cpu_exception(modify ? EXCEPTION_MOD : (intention & TRANSLATE_WRITE) ? EXCEPTION_TLBS : EXCEPTION_TLBL);
- else
- cpu_exception((intention & TRANSLATE_WRITE) ? EXCEPTION_TLBS : EXCEPTION_TLBL, extended ? 0x000 : 0x080);
- }
-
- return MISS;
-}
-
-void r4000_base_device::address_error(int intention, u64 const address)
-{
- if (!machine().side_effects_disabled() && !(intention & TRANSLATE_DEBUG_MASK))
- {
- logerror("address_error 0x%016x (%s)\n", address, machine().describe_context());
-
- // TODO: check this
- if (!(SR & SR_EXL))
- m_cp0[CP0_BadVAddr] = address;
-
- cpu_exception((intention & TRANSLATE_WRITE) ? EXCEPTION_ADES : EXCEPTION_ADEL);
-
- // address errors shouldn't typically occur, so a breakpoint is handy
- machine().debug_break();
- }
-}
-
-template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, bool> r4000_base_device::load(u64 address, U &&apply)
-{
- // alignment error
- if (address & (sizeof(T) - 1))
- {
- address_error(TRANSLATE_READ, address);
- return false;
- }
-
- translate_t const t = translate(TRANSLATE_READ, address);
-
- // address error
- if (t == ERROR)
- {
- address_error(TRANSLATE_READ, address);
-
- return false;
- }
-
- // tlb miss
- if (t == MISS)
- return false;
-
- // watchpoint
- if ((m_cp0[CP0_WatchLo] & WATCHLO_R) && !(SR & SR_EXL))
- {
- u64 const watch_address = ((m_cp0[CP0_WatchHi] & WATCHHI_PADDR1) << 32) | (m_cp0[CP0_WatchLo] & WATCHLO_PADDR0);
-
- if ((address & ~7) == watch_address)
- {
- cpu_exception(EXCEPTION_WATCH);
- return false;
- }
- }
-
- // TODO: cache lookup
-
- switch (sizeof(T))
- {
- case 1: apply(T(space(0).read_byte(address))); break;
- case 2: apply(T(space(0).read_word(address))); break;
- case 4: apply(T(space(0).read_dword(address))); break;
- case 8: apply(T(space(0).read_qword(address))); break;
- }
-
- return true;
-}
-
-template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(u64, T)>>::value, bool> r4000_base_device::load_linked(u64 address, U &&apply)
-{
- // alignment error
- if (address & (sizeof(T) - 1))
- {
- address_error(TRANSLATE_READ, address);
- return false;
- }
-
- translate_t const t = translate(TRANSLATE_READ, address);
-
- // address error
- if (t == ERROR)
- {
- address_error(TRANSLATE_READ, address);
- return false;
- }
-
- // tlb miss
- if (t == MISS)
- return false;
-
- // watchpoint
- if ((m_cp0[CP0_WatchLo] & WATCHLO_R) && !(SR & SR_EXL))
- {
- u64 const watch_address = ((m_cp0[CP0_WatchHi] & WATCHHI_PADDR1) << 32) | (m_cp0[CP0_WatchLo] & WATCHLO_PADDR0);
-
- if ((address & ~7) == watch_address)
- {
- cpu_exception(EXCEPTION_WATCH);
- return false;
- }
- }
-
- // TODO: cache lookup
-
- switch (sizeof(T))
- {
- case 4: apply(address, T(space(0).read_dword(address))); break;
- case 8: apply(address, T(space(0).read_qword(address))); break;
- }
-
- return true;
-}
-
-template <typename T, typename U> std::enable_if_t<std::is_convertible<U, T>::value, bool> r4000_base_device::store(u64 address, U data, T mem_mask)
-{
- // alignment error
- if (address & (sizeof(T) - 1))
- {
- address_error(TRANSLATE_READ, address);
- return false;
- }
-
- translate_t const t = translate(TRANSLATE_WRITE, address);
-
- // address error
- if (t == ERROR)
- {
- address_error(TRANSLATE_WRITE, address);
- return false;
- }
-
- // tlb miss
- if (t == MISS)
- return false;
-
- // watchpoint
- if ((m_cp0[CP0_WatchLo] & WATCHLO_W) && !(SR & SR_EXL))
- {
- u64 const watch_address = ((m_cp0[CP0_WatchHi] & WATCHHI_PADDR1) << 32) | (m_cp0[CP0_WatchLo] & WATCHLO_PADDR0);
-
- if ((address & ~7) == watch_address)
- {
- cpu_exception(EXCEPTION_WATCH);
- return false;
- }
- }
-
- // TODO: cache lookup
-
- switch (sizeof(T))
- {
- case 1: space(0).write_byte(address, T(data)); break;
- case 2: space(0).write_word(address, T(data), mem_mask); break;
- case 4: space(0).write_dword(address, T(data), mem_mask); break;
- case 8: space(0).write_qword(address, T(data), mem_mask); break;
- }
-
- return true;
-}
-
-bool r4000_base_device::fetch(u64 address, std::function<void(u32)> &&apply)
-{
- u64 const program_address = address;
-
- // alignment error
- if (address & 3)
- {
- address_error(TRANSLATE_FETCH, address);
-
- return false;
- }
-
- translate_t const t = translate(TRANSLATE_FETCH, address);
-
- // address error
- if (t == ERROR)
- {
- address_error(TRANSLATE_FETCH, address);
-
- return false;
- }
-
- // tlb miss
- if (t == MISS)
- return false;
-
- if (ICACHE)
- {
- if (t == UNCACHED)
- {
- apply(space(0).read_dword(address));
-
- return true;
- }
-
- // look up the tag
- u32 const cache_address = (program_address & m_icache_mask_hi);
- u32 &tag = m_icache_tag[cache_address >> m_icache_shift];
-
- // check for cache miss
- if (!(tag & ICACHE_V) || (tag & ICACHE_PTAG) != (address >> 12))
- {
- // cache miss
- m_icache_misses++;
-
- // reload the cache line
- tag = ICACHE_V | (address >> 12);
- for (unsigned i = 0; i < m_icache_line_size; i += 8)
- {
- u64 const data = space(0).read_qword((address & m_icache_mask_lo) | i);
-
- m_icache_data[(((cache_address & m_icache_mask_lo) | i) >> 2) + 0] = u32(data);
- m_icache_data[(((cache_address & m_icache_mask_lo) | i) >> 2) + 1] = data >> 32;
- }
- }
- else
- m_icache_hits++;
-
- // apply the result
- apply(m_icache_data[cache_address >> 2]);
- }
- else
- apply(space(0).read_dword(address));
-
- return true;
-}
-
-std::string r4000_base_device::debug_string(u64 string_pointer, unsigned limit)
-{
- auto const suppressor(machine().disable_side_effects());
-
- bool done = false;
- bool mapped = false;
- std::string result("");
-
- while (!done)
- {
- done = true;
- load<u8>(string_pointer++, [limit, &done, &mapped, &result](u8 byte)
- {
- mapped = true;
- if (byte != 0)
- {
- result += byte;
-
- done = result.length() == limit;
- }
- });
- }
-
- if (!mapped)
- result.assign("[unmapped]");
-
- return result;
-}
-
-std::string r4000_base_device::debug_string_array(u64 array_pointer)
-{
- auto const suppressor(machine().disable_side_effects());
-
- bool done = false;
- std::string result("");
-
- while (!done)
- {
- done = true;
- load<s32>(array_pointer, [this, &done, &result](u64 string_pointer)
- {
- if (string_pointer != 0)
- {
- if (!result.empty())
- result += ", ";
-
- result += '\"' + debug_string(string_pointer) + '\"';
-
- done = false;
- }
- });
-
- array_pointer += 4;
- }
-
- return result;
-}
-
-std::string r4000_base_device::debug_unicode_string(u64 unicode_string_pointer)
-{
- auto const suppressor(machine().disable_side_effects());
-
- std::wstring result(L"");
-
- if (!load<u16>(unicode_string_pointer,
- [this, unicode_string_pointer, &result](u16 const length)
- {
- if (length)
- if (!load<u32>(unicode_string_pointer + 4,
- [this, length, &result](s32 buffer)
- {
- for (int i = 0; i < length; i += 2)
- load<u16>(buffer + i, [&result](wchar_t const character) { result += character; });
- }))
- result.assign(L"[unmapped]");
- }))
- result.assign(L"[unmapped]");
-
- return utf8_from_wstring(result);
-}