summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/mips/mips1.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/mips/mips1.cpp')
-rw-r--r--src/devices/cpu/mips/mips1.cpp883
1 files changed, 522 insertions, 361 deletions
diff --git a/src/devices/cpu/mips/mips1.cpp b/src/devices/cpu/mips/mips1.cpp
index 6c491cb04ca..2316da4b25d 100644
--- a/src/devices/cpu/mips/mips1.cpp
+++ b/src/devices/cpu/mips/mips1.cpp
@@ -6,26 +6,183 @@
* IDT devices come in two variations: those with an "E" suffix include a TLB,
* while those without have hard-wired address translation.
*
- * TODO
- * - R3041 features
- * - cache emulation
+ * TODO:
+ * - multi-word cache line sizes
+ * - R3041 features
*
*/
#include "emu.h"
#include "mips1.h"
#include "mips1dsm.h"
-#include "debugger.h"
#include "softfloat3/source/include/softfloat.h"
-#define LOG_GENERAL (1U << 0)
#define LOG_TLB (1U << 1)
-#define LOG_IOP (1U << 2)
-#define LOG_RISCOS (1U << 3)
//#define VERBOSE (LOG_GENERAL|LOG_TLB)
#include "logmacro.h"
+enum registers : unsigned
+{
+ MIPS1_R0 = 0,
+ MIPS1_COP0 = 32,
+ MIPS1_F0 = 64,
+
+ MIPS1_PC = 80,
+ MIPS1_HI,
+ MIPS1_LO,
+ MIPS1_FCR30,
+ MIPS1_FCR31,
+};
+
+enum exception : u32
+{
+ EXCEPTION_INTERRUPT = 0x00000000,
+ EXCEPTION_TLBMOD = 0x00000004,
+ EXCEPTION_TLBLOAD = 0x00000008,
+ EXCEPTION_TLBSTORE = 0x0000000c,
+ EXCEPTION_ADDRLOAD = 0x00000010,
+ EXCEPTION_ADDRSTORE = 0x00000014,
+ EXCEPTION_BUSINST = 0x00000018,
+ EXCEPTION_BUSDATA = 0x0000001c,
+ EXCEPTION_SYSCALL = 0x00000020,
+ EXCEPTION_BREAK = 0x00000024,
+ EXCEPTION_INVALIDOP = 0x00000028,
+ EXCEPTION_BADCOP = 0x0000002c,
+ EXCEPTION_OVERFLOW = 0x00000030,
+ EXCEPTION_TRAP = 0x00000034,
+
+ EXCEPTION_BADCOP0 = 0x0000002c,
+ EXCEPTION_BADCOP1 = 0x1000002c,
+ EXCEPTION_BADCOP2 = 0x2000002c,
+ EXCEPTION_BADCOP3 = 0x3000002c,
+};
+
+enum cop0_reg : u8
+{
+ COP0_Index = 0,
+ COP0_Random = 1,
+ COP0_EntryLo = 2,
+ COP0_BusCtrl = 2, // r3041 only
+ COP0_Config = 3, // r3041/r3071/r3081 only
+ COP0_Context = 4,
+ COP0_BadVAddr = 8,
+ COP0_Count = 9, // r3041 only
+ COP0_EntryHi = 10,
+ COP0_PortSize = 10, // r3041 only
+ COP0_Compare = 11, // r3041 only
+ COP0_Status = 12,
+ COP0_Cause = 13,
+ COP0_EPC = 14,
+ COP0_PRId = 15,
+};
+
+enum sr_mask : u32
+{
+ SR_IEc = 0x00000001, // interrupt enable (current)
+ SR_KUc = 0x00000002, // user mode (current)
+ SR_IEp = 0x00000004, // interrupt enable (previous)
+ SR_KUp = 0x00000008, // user mode (previous)
+ SR_IEo = 0x00000010, // interrupt enable (old)
+ SR_KUo = 0x00000020, // user mode (old)
+ SR_IMSW0 = 0x00000100, // software interrupt 0 enable
+ SR_IMSW1 = 0x00000200, // software interrupt 1 enable
+ SR_IMEX0 = 0x00000400, // external interrupt 0 enable
+ SR_IMEX1 = 0x00000800, // external interrupt 1 enable
+ SR_IMEX2 = 0x00001000, // external interrupt 2 enable
+ SR_IMEX3 = 0x00002000, // external interrupt 3 enable
+ SR_IMEX4 = 0x00004000, // external interrupt 4 enable
+ SR_IMEX5 = 0x00008000, // external interrupt 5 enable
+ SR_IsC = 0x00010000, // isolate (data) cache
+ SR_SwC = 0x00020000, // swap caches
+ SR_PZ = 0x00040000, // cache parity zero
+ SR_CM = 0x00080000, // cache miss
+ SR_PE = 0x00100000, // cache parity error
+ SR_TS = 0x00200000, // tlb shutdown
+ SR_BEV = 0x00400000, // boot exception vectors
+ SR_RE = 0x02000000, // reverse endianness in user mode
+ SR_COP0 = 0x10000000, // coprocessor 0 usable
+ SR_COP1 = 0x20000000, // coprocessor 1 usable
+ SR_COP2 = 0x40000000, // coprocessor 2 usable
+ SR_COP3 = 0x80000000, // coprocessor 3 usable
+
+ SR_KUIE = 0x0000003f, // all interrupt enable and user mode bits
+ SR_KUIEpc = 0x0000000f, // previous and current interrupt enable and user mode bits
+ SR_KUIEop = 0x0000003c, // old and previous interrupt enable and user mode bits
+ SR_IM = 0x0000ff00, // all interrupt mask bits
+};
+
+enum cause_mask : u32
+{
+ CAUSE_EXCCODE = 0x0000007c, // exception code
+ CAUSE_IPSW0 = 0x00000100, // software interrupt 0 pending
+ CAUSE_IPSW1 = 0x00000200, // software interrupt 1 pending
+ CAUSE_IPEX0 = 0x00000400, // external interrupt 0 pending
+ CAUSE_IPEX1 = 0x00000800, // external interrupt 1 pending
+ CAUSE_IPEX2 = 0x00001000, // external interrupt 2 pending
+ CAUSE_IPEX3 = 0x00002000, // external interrupt 3 pending
+ CAUSE_IPEX4 = 0x00004000, // external interrupt 4 pending
+ CAUSE_IPEX5 = 0x00008000, // external interrupt 5 pending
+ CAUSE_IP = 0x0000ff00, // interrupt pending
+ CAUSE_CE = 0x30000000, // co-processor error
+ CAUSE_BD = 0x80000000, // branch delay
+
+ CAUSE_IPEX = 0x0000fc00, // external interrupt pending
+};
+
+enum entryhi_mask : u32
+{
+ EH_VPN = 0xfffff000, // virtual page number
+ EH_ASID = 0x00000fc0, // address space identifier
+
+ EH_WM = 0xffffffc0, // write mask
+};
+enum entrylo_mask : u32
+{
+ EL_PFN = 0xfffff000, // physical frame
+ EL_N = 0x00000800, // noncacheable
+ EL_D = 0x00000400, // dirty
+ EL_V = 0x00000200, // valid
+ EL_G = 0x00000100, // global
+
+ EL_WM = 0xffffff00, // write mask
+};
+enum context_mask : u32
+{
+ PTE_BASE = 0xffe00000, // base address of page table
+ BAD_VPN = 0x001ffffc, // virtual address bits 30..12
+};
+
+enum cp1_fcr31_mask : u32
+{
+ FCR31_RM = 0x00000003, // rounding mode
+
+ FCR31_FI = 0x00000004, // inexact operation flag
+ FCR31_FU = 0x00000008, // underflow flag
+ FCR31_FO = 0x00000010, // overflow flag
+ FCR31_FZ = 0x00000020, // divide by zero flag
+ FCR31_FV = 0x00000040, // invalid operation flag
+
+ FCR31_EI = 0x00000080, // inexact operation enable
+ FCR31_EU = 0x00000100, // underflow enable
+ FCR31_EO = 0x00000200, // overflow enable
+ FCR31_EZ = 0x00000400, // divide by zero enable
+ FCR31_EV = 0x00000800, // invalid operation enable
+
+ FCR31_CI = 0x00001000, // inexact operation cause
+ FCR31_CU = 0x00002000, // underflow cause
+ FCR31_CO = 0x00004000, // overflow cause
+ FCR31_CZ = 0x00008000, // divide by zero cause
+ FCR31_CV = 0x00010000, // invalid operation cause
+ FCR31_CE = 0x00020000, // unimplemented operation cause
+
+ FCR31_C = 0x00800000, // condition
+
+ FCR31_FM = 0x0000007c, // flag mask
+ FCR31_EM = 0x00000f80, // enable mask
+ FCR31_CM = 0x0001f000, // cause mask (except unimplemented)
+};
+
#define RSREG ((op >> 21) & 31)
#define RTREG ((op >> 16) & 31)
#define RDREG ((op >> 11) & 31)
@@ -54,106 +211,91 @@ DEFINE_DEVICE_TYPE(R3071, r3071_device, "r3071", "IDT R3071")
DEFINE_DEVICE_TYPE(R3081, r3081_device, "r3081", "IDT R3081")
DEFINE_DEVICE_TYPE(SONYPS2_IOP, iop_device, "sonyiop", "Sony Playstation 2 IOP")
-ALLOW_SAVE_TYPE(mips1core_device_base::branch_state_t);
+ALLOW_SAVE_TYPE(mips1core_device_base::branch_state);
-mips1core_device_base::mips1core_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u32 cpurev, size_t icache_size, size_t dcache_size)
+mips1core_device_base::mips1core_device_base(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock, u32 cpurev, size_t icache_size, size_t dcache_size, bool cache_pws)
: cpu_device(mconfig, type, tag, owner, clock)
, m_program_config_be("program", ENDIANNESS_BIG, 32, 32)
, m_program_config_le("program", ENDIANNESS_LITTLE, 32, 32)
- , m_icache_config("icache", ENDIANNESS_BIG, 32, 32)
- , m_dcache_config("dcache", ENDIANNESS_BIG, 32, 32)
, m_cpurev(cpurev)
, m_endianness(ENDIANNESS_BIG)
, m_icount(0)
- , m_icache_size(icache_size)
- , m_dcache_size(dcache_size)
- , m_in_brcond{ *this, *this, *this, *this }
+ , m_icache(icache_size)
+ , m_dcache(dcache_size)
+ , m_cache((icache_size && dcache_size) ? CACHED : UNCACHED)
+ , m_cache_pws(cache_pws)
+ , m_in_brcond(*this, 0)
{
}
-mips1_device_base::mips1_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u32 cpurev, size_t icache_size, size_t dcache_size)
- : mips1core_device_base(mconfig, type, tag, owner, clock, cpurev, icache_size, dcache_size)
+mips1_device_base::mips1_device_base(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock, u32 cpurev, size_t icache_size, size_t dcache_size, bool cache_pws)
+ : mips1core_device_base(mconfig, type, tag, owner, clock, cpurev, icache_size, dcache_size, cache_pws)
, m_fcr0(0)
{
}
-r2000_device::r2000_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, size_t icache_size, size_t dcache_size)
- : mips1_device_base(mconfig, R2000, tag, owner, clock, 0x0100, icache_size, dcache_size)
+r2000_device::r2000_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock, size_t icache_size, size_t dcache_size)
+ : mips1_device_base(mconfig, R2000, tag, owner, clock, 0x0120, icache_size, dcache_size, false)
{
}
-r2000a_device::r2000a_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, size_t icache_size, size_t dcache_size)
- : mips1_device_base(mconfig, R2000A, tag, owner, clock, 0x0210, icache_size, dcache_size)
+r2000a_device::r2000a_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock, size_t icache_size, size_t dcache_size)
+ : mips1_device_base(mconfig, R2000A, tag, owner, clock, 0x0210, icache_size, dcache_size, false)
{
}
-r3000_device::r3000_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, size_t icache_size, size_t dcache_size)
- : mips1_device_base(mconfig, R3000, tag, owner, clock, 0x0220, icache_size, dcache_size)
+r3000_device::r3000_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock, size_t icache_size, size_t dcache_size)
+ : mips1_device_base(mconfig, R3000, tag, owner, clock, 0x0220, icache_size, dcache_size, false)
{
}
-r3000a_device::r3000a_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, size_t icache_size, size_t dcache_size)
- : mips1_device_base(mconfig, R3000A, tag, owner, clock, 0x0230, icache_size, dcache_size)
+r3000a_device::r3000a_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock, size_t icache_size, size_t dcache_size)
+ : mips1_device_base(mconfig, R3000A, tag, owner, clock, 0x0230, icache_size, dcache_size, false)
{
}
-r3041_device::r3041_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
- : mips1core_device_base(mconfig, R3041, tag, owner, clock, 0x0700, 2048, 512)
+r3041_device::r3041_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
+ : mips1core_device_base(mconfig, R3041, tag, owner, clock, 0x0700, 2048, 512, true)
{
}
-r3051_device::r3051_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
- : mips1core_device_base(mconfig, R3051, tag, owner, clock, 0x0200, 4096, 2048)
+r3051_device::r3051_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
+ : mips1core_device_base(mconfig, R3051, tag, owner, clock, 0x0200, 4096, 2048, true)
{
}
-r3052_device::r3052_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
- : mips1core_device_base(mconfig, R3052, tag, owner, clock, 0x0200, 8192, 2048)
+r3052_device::r3052_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
+ : mips1core_device_base(mconfig, R3052, tag, owner, clock, 0x0200, 8192, 2048, true)
{
}
-r3052e_device::r3052e_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
- : mips1_device_base(mconfig, R3052E, tag, owner, clock, 0x0200, 8192, 2048)
+r3052e_device::r3052e_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
+ : mips1_device_base(mconfig, R3052E, tag, owner, clock, 0x0200, 8192, 2048, true)
{
}
-r3071_device::r3071_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, size_t icache_size, size_t dcache_size)
- : mips1_device_base(mconfig, R3071, tag, owner, clock, 0x0200, icache_size, dcache_size)
+r3071_device::r3071_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock, size_t icache_size, size_t dcache_size)
+ : mips1_device_base(mconfig, R3071, tag, owner, clock, 0x0200, icache_size, dcache_size, true)
{
}
-r3081_device::r3081_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, size_t icache_size, size_t dcache_size)
- : mips1_device_base(mconfig, R3081, tag, owner, clock, 0x0200, icache_size, dcache_size)
+r3081_device::r3081_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock, size_t icache_size, size_t dcache_size)
+ : mips1_device_base(mconfig, R3081, tag, owner, clock, 0x0200, icache_size, dcache_size, true)
{
set_fpu(0x0300);
}
-iop_device::iop_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
- : mips1core_device_base(mconfig, SONYPS2_IOP, tag, owner, clock, 0x001f, 4096, 1024)
+iop_device::iop_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
+ : mips1core_device_base(mconfig, SONYPS2_IOP, tag, owner, clock, 0x001f, 4096, 1024, false)
{
m_endianness = ENDIANNESS_LITTLE;
}
-/*
- * Two additional address spaces are defined to represent the instruction and
- * data caches. These are only used to simulate cache isolation functionality
- * at this point, but could simulate other behaviour as needed in future.
- */
-void mips1core_device_base::device_add_mconfig(machine_config &config)
-{
- set_addrmap(1, &mips1core_device_base::icache_map);
- set_addrmap(2, &mips1core_device_base::dcache_map);
-}
-
void mips1core_device_base::device_start()
{
// set our instruction counter
set_icountptr(m_icount);
- // resolve conditional branch input handlers
- for (devcb_read_line &cb : m_in_brcond)
- cb.resolve_safe(0);
-
// register our state for the debugger
state_add(STATE_GENPC, "GENPC", m_pc).noshow();
state_add(STATE_GENPCBASE, "CURPC", m_pc).noshow();
@@ -161,7 +303,7 @@ void mips1core_device_base::device_start()
state_add(MIPS1_PC, "PC", m_pc);
state_add(MIPS1_COP0 + COP0_Status, "SR", m_cop0[COP0_Status]);
- for (unsigned i = 0; i < ARRAY_LENGTH(m_r); i++)
+ for (unsigned i = 0; i < std::size(m_r); i++)
state_add(MIPS1_R0 + i, util::string_format("R%d", i).c_str(), m_r[i]);
state_add(MIPS1_HI, "HI", m_hi);
@@ -183,6 +325,18 @@ void mips1core_device_base::device_start()
// initialise cpu id register
m_cop0[COP0_PRId] = m_cpurev;
+
+ m_cop0[COP0_Cause] = 0;
+
+ m_r[0] = 0;
+
+ m_icache.start();
+ m_dcache.start();
+
+ save_pointer(STRUCT_MEMBER(m_icache.line, tag), m_icache.lines());
+ save_pointer(STRUCT_MEMBER(m_icache.line, data), m_icache.lines());
+ save_pointer(STRUCT_MEMBER(m_dcache.line, tag), m_dcache.lines());
+ save_pointer(STRUCT_MEMBER(m_dcache.line, data), m_dcache.lines());
}
void r3041_device::device_start()
@@ -195,6 +349,10 @@ void r3041_device::device_start()
state_add(MIPS1_COP0 + COP0_Count, "Count", m_cop0[COP0_Count]);
state_add(MIPS1_COP0 + COP0_PortSize, "PortSize", m_cop0[COP0_PortSize]);
state_add(MIPS1_COP0 + COP0_Compare, "Compare", m_cop0[COP0_Compare]);
+
+ m_cop0[COP0_BusCtrl] = 0x20130b00U;
+ m_cop0[COP0_Config] = 0x40000000U;
+ m_cop0[COP0_PortSize] = 0;
}
void mips1core_device_base::device_reset()
@@ -206,10 +364,17 @@ void mips1core_device_base::device_reset()
// non-tlb devices have tlb shut down
m_cop0[COP0_Status] = SR_BEV | SR_TS;
- m_data_spacenum = 0;
m_bus_error = false;
}
+void r3041_device::device_reset()
+{
+ mips1core_device_base::device_reset();
+
+ m_cop0[COP0_Count] = 0;
+ m_cop0[COP0_Compare] = 0x00ffffffU;
+}
+
void mips1core_device_base::execute_run()
{
// core execution loop
@@ -218,30 +383,21 @@ void mips1core_device_base::execute_run()
// debugging
debugger_instruction_hook(m_pc);
- if (VERBOSE & LOG_IOP)
- {
- if ((m_pc & 0x1fffffff) == 0x00012C48 || (m_pc & 0x1fffffff) == 0x0001420C || (m_pc & 0x1fffffff) == 0x0001430C)
- {
- u32 ptr = m_r[5];
- u32 length = m_r[6];
- if (length >= 4096)
- length = 4095;
- while (length)
- {
- load<u8>(ptr, [](char c) { printf("%c", c); });
- ptr++;
- length--;
- }
- fflush(stdout);
- }
- }
-
// fetch instruction
fetch(m_pc, [this](u32 const op)
{
// check for interrupts
if ((CAUSE & SR & SR_IM) && (SR & SR_IEc))
{
+ // enable debugger interrupt breakpoints
+ for (int irqline = 0; irqline < 6; irqline++)
+ {
+ if (CAUSE & SR & (CAUSE_IPEX0 << irqline))
+ {
+ standard_irq_callback(irqline, m_pc);
+ break;
+ }
+ }
generate_exception(EXCEPTION_INTERRUPT);
return;
}
@@ -605,13 +761,7 @@ void mips1core_device_base::execute_run()
void mips1core_device_base::execute_set_input(int irqline, int state)
{
if (state != CLEAR_LINE)
- {
CAUSE |= CAUSE_IPEX0 << irqline;
-
- // enable debugger interrupt breakpoints
- if ((SR & SR_IEc) && (SR & (SR_IMEX0 << irqline)))
- standard_irq_callback(irqline);
- }
else
CAUSE &= ~(CAUSE_IPEX0 << irqline);
}
@@ -619,26 +769,37 @@ void mips1core_device_base::execute_set_input(int irqline, int state)
device_memory_interface::space_config_vector mips1core_device_base::memory_space_config() const
{
return space_config_vector {
- std::make_pair(AS_PROGRAM, (m_endianness == ENDIANNESS_BIG) ? &m_program_config_be : &m_program_config_le),
- std::make_pair(1, &m_icache_config),
- std::make_pair(2, &m_dcache_config)
+ std::make_pair(AS_PROGRAM, (m_endianness == ENDIANNESS_BIG) ? &m_program_config_be : &m_program_config_le)
};
}
-bool mips1core_device_base::memory_translate(int spacenum, int intention, offs_t &address)
+bool mips1core_device_base::memory_translate(int spacenum, int intention, offs_t &address, address_space *&target_space)
+{
+ target_space = &space(spacenum);
+
+ if (spacenum != AS_PROGRAM)
+ return true;
+
+ return translate(intention, address, true) != ERROR;
+}
+
+mips1core_device_base::translate_result mips1core_device_base::translate(int intention, offs_t &address, bool debug)
{
// check for kernel memory address
if (BIT(address, 31))
{
// check debug or kernel mode
- if ((intention & TRANSLATE_DEBUG_MASK) || !(SR & SR_KUc))
+ if (debug || !(SR & SR_KUc))
{
switch (address & 0xe0000000)
{
case 0x80000000: // kseg0: unmapped, cached, privileged
+ address &= ~0xe0000000;
+ return m_cache;
+
case 0xa0000000: // kseg1: unmapped, uncached, privileged
address &= ~0xe0000000;
- break;
+ return UNCACHED;
case 0xc0000000: // kseg2: mapped, cached, privileged
case 0xe0000000:
@@ -647,21 +808,16 @@ bool mips1core_device_base::memory_translate(int spacenum, int intention, offs_t
}
else if (SR & SR_KUc)
{
- if (!machine().side_effects_disabled())
- {
- // exception
- m_cop0[COP0_BadVAddr] = address;
+ address_error(intention, address);
- generate_exception((intention & TRANSLATE_WRITE) ? EXCEPTION_ADDRSTORE : EXCEPTION_ADDRLOAD);
- }
- return false;
+ return ERROR;
}
}
else
// kuseg physical addresses have a 1GB offset
address += 0x40000000;
- return true;
+ return m_cache;
}
std::unique_ptr<util::disasm_interface> mips1core_device_base::create_disassembler()
@@ -669,168 +825,8 @@ std::unique_ptr<util::disasm_interface> mips1core_device_base::create_disassembl
return std::make_unique<mips1_disassembler>();
}
-void mips1core_device_base::icache_map(address_map &map)
-{
- if (m_icache_size)
- map(0, m_icache_size - 1).ram().mirror(~(m_icache_size - 1));
-}
-
-void mips1core_device_base::dcache_map(address_map &map)
-{
- if (m_dcache_size)
- map(0, m_dcache_size - 1).ram().mirror(~(m_dcache_size - 1));
-}
-
void mips1core_device_base::generate_exception(u32 exception, bool refill)
{
- if ((VERBOSE & LOG_RISCOS) && (exception == EXCEPTION_SYSCALL))
- {
- static char const *const sysv_syscalls[] =
- {
- "syscall", "exit", "fork", "read", "write", "open", "close", "wait", "creat", "link",
- "unlink", "execv", "chdir", "time", "mknod", "chmod", "chown", "brk", "stat", "lseek",
- "getpid", "mount", "umount", "setuid", "getuid", "stime", "ptrace", "alarm", "fstat", "pause",
- "utime", "stty", "gtty", "access", "nice", "statfs", "sync", "kill", "fstatfs", "setpgrp",
- nullptr, "dup", "pipe", "times", "profil", "plock", "setgid", "getgid", "signal", "msgsys",
- "sysmips", "acct", "shmsys", "semsys", "ioctl", "uadmin", nullptr, "utssys", nullptr, "execve",
- "umask", "chroot", "ofcntl", "ulimit", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
- "advfs", "unadvfs", "rmount", "rumount", "rfstart", nullptr, "rdebug", "rfstop", "rfsys", "rmdir",
- "mkdir", "getdents", nullptr, nullptr, "sysfs", "getmsg", "putmsg", "poll", "sigreturn", "accept",
- "bind", "connect", "gethostid", "getpeername", "getsockname", "getsockopt", "listen", "recv", "recvfrom", "recvmsg",
- "select", "send", "sendmsg", "sendto", "sethostid", "setsockopt", "shutdown", "socket", "gethostname", "sethostname",
- "getdomainname","setdomainname","truncate", "ftruncate", "rename", "symlink", "readlink", "lstat", "nfsmount", "nfssvc",
- "getfh", "async_daemon", "old_exportfs", "mmap", "munmap", "getitimer", "setitimer", nullptr, nullptr, nullptr,
- nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
- nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
- "cacheflush", "cachectl", "fchown", "fchmod", "wait3", "mmap", "munmap", "madvise", "getpagesize", "setreuid",
- "setregid", "setpgid", "getgroups", "setgroups", "gettimeofday", "getrusage", "getrlimit", "setrlimit", "exportfs", "fcntl"
- };
-
- static char const *const bsd_syscalls[] =
- {
- "syscall", "exit", "fork", "read", "write", "open", "close", nullptr, "creat", "link",
- "unlink", "execv", "chdir", nullptr, "mknod", "chmod", "chown", "brk", nullptr, "lseek",
- "getpid", "omount", "oumount", nullptr, "getuid", nullptr, "ptrace", nullptr, nullptr, nullptr,
- nullptr, nullptr, nullptr, "access", nullptr, nullptr, "sync", "kill", "stat", nullptr,
- "lstat", "dup", "pipe", nullptr, "profil", nullptr, nullptr, "getgid", nullptr, nullptr,
- nullptr, "acct", nullptr, nullptr, "ioctl", "reboot", nullptr, "symlink", "readlink", "execve",
- "umask", "chroot", "fstat", nullptr, "getpagesize", "mremap", "vfork", nullptr, nullptr, "sbrk",
- "sstk", "mmap", "vadvise", "munmap", "mprotec", "madvise", "vhangup", nullptr, "mincore", "getgroups",
- "setgroups", "getpgrp", "setpgrp", "setitimer", "wait3", "swapon", "getitimer", "gethostname", "sethostname", "getdtablesize",
- "dup2", "getdopt", "fcntl", "select", "setdopt", "fsync", "setpriority", "socket", "connect", "accept",
- "getpriority", "send", "recv", "sigreturn", "bind", "setsockopt", "listen", nullptr, "sigvec", "sigblock",
- "sigsetmask", "sigpause", "sigstack", "recvmsg", "sendmsg", nullptr, "gettimeofday", "getrusage", "getsockopt", nullptr,
- "readv", "writev", "settimeofday", "fchown", "fchmod", "recvfrom", "setreuid", "setregid", "rename", "truncate",
- "ftruncate", "flock", nullptr, "sendto", "shutdown", "socketpair", "mkdir", "rmdir", "utimes", "sigcleanup",
- "adjtime", "getpeername", "gethostid", "sethostid", "getrlimit", "setrlimit", "killpg", nullptr, "setquota", "quota",
- "getsockname", "sysmips", "cacheflush", "cachectl", "debug", nullptr, nullptr, nullptr, "nfssvc", "getdirentries",
- "statfs", "fstatfs", "unmount", "async_daemon", "getfh", "getdomainname","setdomainname",nullptr, "quotactl", "old_exportfs",
- "mount", "hdwconf", "exportfs", "nfsfh_open", "libattach", "libdetach"
- };
-
- static char const *const msg_syscalls[] = { "msgget", "msgctl", "msgrcv", "msgsnd" };
- static char const *const shm_syscalls[] = { "shmat", "shmctl", "shmdt", "shmget" };
- static char const *const sem_syscalls[] = { "semctl", "semget", "semop" };
- static char const *const mips_syscalls[] = { "mipskopt", "mipshwconf", "mipsgetrusage", "mipswait", "mipscacheflush", "mipscachectl" };
-
- unsigned const asid = (m_cop0[COP0_EntryHi] & EH_ASID) >> 6;
- switch (m_r[2])
- {
- case 1000: // indirect
- switch (m_r[4])
- {
- case 1049: // msgsys
- LOGMASKED(LOG_RISCOS, "asid %d syscall msgsys:%s() (%s)\n",
- asid, (m_r[5] < ARRAY_LENGTH(msg_syscalls)) ? msg_syscalls[m_r[5]] : "unknown", machine().describe_context());
- break;
-
- case 1052: // shmsys
- LOGMASKED(LOG_RISCOS, "asid %d syscall shmsys:%s() (%s)\n",
- asid, (m_r[5] < ARRAY_LENGTH(shm_syscalls)) ? shm_syscalls[m_r[5]] : "unknown", machine().describe_context());
- break;
-
- case 1053: // semsys
- LOGMASKED(LOG_RISCOS, "asid %d syscall semsys:%s() (%s)\n",
- asid, (m_r[5] < ARRAY_LENGTH(sem_syscalls)) ? sem_syscalls[m_r[5]] : "unknown", machine().describe_context());
- break;
-
- case 2151: // bsd_sysmips
- switch (m_r[5])
- {
- case 0x100: // mipskopt
- LOGMASKED(LOG_RISCOS, "asid %d syscall bsd_sysmips:mipskopt(\"%s\") (%s)\n",
- asid, debug_string(m_r[6]), machine().describe_context());
- break;
-
- default:
- if ((m_r[5] > 0x100) && (m_r[5] - 0x100) < ARRAY_LENGTH(mips_syscalls))
- LOGMASKED(LOG_RISCOS, "asid %d syscall bsd_sysmips:%s() (%s)\n",
- asid, mips_syscalls[m_r[5] - 0x100], machine().describe_context());
- else
- LOGMASKED(LOG_RISCOS, "asid %d syscall bsd_sysmips:unknown %d (%s)\n",
- asid, m_r[5], machine().describe_context());
- break;
- }
- break;
-
- default:
- if ((m_r[4] > 2000) && (m_r[4] - 2000 < ARRAY_LENGTH(bsd_syscalls)) && bsd_syscalls[m_r[4] - 2000])
- LOGMASKED(LOG_RISCOS, "asid %d syscall bsd_%s() (%s)\n",
- asid, bsd_syscalls[m_r[4] - 2000], machine().describe_context());
- else
- LOGMASKED(LOG_RISCOS, "asid %d syscall indirect:unknown %d (%s)\n",
- asid, m_r[4], machine().describe_context());
- break;
- }
- break;
-
- case 1003: // read
- case 1006: // close
- case 1054: // ioctl
- case 1169: // fcntl
- LOGMASKED(LOG_RISCOS, "asid %d syscall %s(%d) (%s)\n",
- asid, sysv_syscalls[m_r[2] - 1000], m_r[4], machine().describe_context());
- break;
-
- case 1004: // write
- if (m_r[4] == 1 || m_r[4] == 2)
- LOGMASKED(LOG_RISCOS, "asid %d syscall %s(%d, \"%s\") (%s)\n",
- asid, sysv_syscalls[m_r[2] - 1000], m_r[4], debug_string(m_r[5], m_r[6]), machine().describe_context());
- else
- LOGMASKED(LOG_RISCOS, "asid %d syscall %s(%d) (%s)\n",
- asid, sysv_syscalls[m_r[2] - 1000], m_r[4], machine().describe_context());
- break;
-
- case 1005: // open
- case 1008: // creat
- case 1009: // link
- case 1010: // unlink
- case 1012: // chdir
- case 1018: // stat
- case 1033: // access
- LOGMASKED(LOG_RISCOS, "asid %d syscall %s(\"%s\") (%s)\n",
- asid, sysv_syscalls[m_r[2] - 1000], debug_string(m_r[4]), machine().describe_context());
- break;
-
- case 1059: // execve
- LOGMASKED(LOG_RISCOS, "asid %d syscall execve(\"%s\", [ %s ], [ %s ]) (%s)\n",
- asid, debug_string(m_r[4]), debug_string_array(m_r[5]), debug_string_array(m_r[6]), machine().describe_context());
- break;
-
- case 1060: // umask
- LOGMASKED(LOG_RISCOS, "asid %d syscall umask(%#o) (%s)\n",
- asid, m_r[4] & 0777, machine().describe_context());
- break;
-
- default:
- if ((m_r[2] > 1000) && (m_r[2] - 1000 < ARRAY_LENGTH(sysv_syscalls)) && sysv_syscalls[m_r[2] - 1000])
- LOGMASKED(LOG_RISCOS, "asid %d syscall %s() (%s)\n", asid, sysv_syscalls[m_r[2] - 1000], machine().describe_context());
- else
- LOGMASKED(LOG_RISCOS, "asid %d syscall unknown %d (%s)\n", asid, m_r[2], machine().describe_context());
- break;
- }
- }
-
// set the exception PC
m_cop0[COP0_EPC] = m_pc;
@@ -845,20 +841,36 @@ void mips1core_device_base::generate_exception(u32 exception, bool refill)
}
m_branch_state = EXCEPTION;
- // shift the exception bits
- SR = (SR & ~SR_KUIE) | ((SR << 2) & SR_KUIEop);
-
if (refill)
m_pc = (SR & SR_BEV) ? 0xbfc00100 : 0x80000000;
else
m_pc = (SR & SR_BEV) ? 0xbfc00180 : 0x80000080;
+ // hook exception in caller context enabling debugger access to memory parameters
debugger_exception_hook(exception);
+ // shift the exception bits
+ SR = (SR & ~SR_KUIE) | ((SR << 2) & SR_KUIEop);
+
if (SR & SR_KUp)
debugger_privilege_hook();
}
+void mips1core_device_base::address_error(int intention, u32 const address)
+{
+ if (!machine().side_effects_disabled())
+ {
+ logerror("address_error 0x%08x (%s)\n", address, machine().describe_context());
+
+ m_cop0[COP0_BadVAddr] = address;
+
+ generate_exception((intention == TR_WRITE) ? EXCEPTION_ADDRSTORE : EXCEPTION_ADDRLOAD);
+
+ // address errors shouldn't typically occur, so a breakpoint is handy
+ machine().debug_break();
+ }
+}
+
void mips1core_device_base::handle_cop0(u32 const op)
{
switch (RSREG)
@@ -923,10 +935,10 @@ void mips1core_device_base::set_cop0_reg(unsigned const reg, u32 const data)
{
u32 const delta = SR ^ data;
- m_cop0[COP0_Status] = data;
+ if ((delta & SR_IsC) && (m_cache == UNCACHED))
+ fatalerror("mips1: cannot isolate non-existent cache (%s)\n", machine().describe_context());
- // handle cache isolation and swap
- m_data_spacenum = (data & SR_IsC) ? ((data & SR_SwC) ? 1 : 2) : 0;
+ m_cop0[COP0_Status] = data;
if ((delta & SR_KUc) && (m_branch_state != EXCEPTION))
debugger_privilege_hook();
@@ -1030,9 +1042,9 @@ void mips1core_device_base::handle_cop3(u32 const op)
void mips1core_device_base::lwl(u32 const op)
{
offs_t const offset = SIMMVAL + m_r[RSREG];
- load<u32>(offset & ~3, [this, op, offset](u32 temp)
+ load<u32, false>(offset, [this, op, offset](u32 temp)
{
- unsigned const shift = ((offset & 3) ^ ENDIAN_VALUE_LE_BE(m_endianness, 3, 0)) << 3;
+ unsigned const shift = ((offset & 3) ^ (m_endianness == ENDIANNESS_LITTLE ? 3 : 0)) << 3;
m_r[RTREG] = (m_r[RTREG] & ~u32(0xffffffffU << shift)) | (temp << shift);
});
@@ -1041,9 +1053,9 @@ void mips1core_device_base::lwl(u32 const op)
void mips1core_device_base::lwr(u32 const op)
{
offs_t const offset = SIMMVAL + m_r[RSREG];
- load<u32>(offset & ~3, [this, op, offset](u32 temp)
+ load<u32, false>(offset, [this, op, offset](u32 temp)
{
- unsigned const shift = ((offset & 0x3) ^ ENDIAN_VALUE_LE_BE(m_endianness, 0, 3)) << 3;
+ unsigned const shift = ((offset & 3) ^ (m_endianness == ENDIANNESS_LITTLE ? 0 : 3)) << 3;
m_r[RTREG] = (m_r[RTREG] & ~u32(0xffffffffU >> shift)) | (temp >> shift);
});
@@ -1052,128 +1064,263 @@ void mips1core_device_base::lwr(u32 const op)
void mips1core_device_base::swl(u32 const op)
{
offs_t const offset = SIMMVAL + m_r[RSREG];
- unsigned const shift = ((offset & 3) ^ ENDIAN_VALUE_LE_BE(m_endianness, 3, 0)) << 3;
+ unsigned const shift = ((offset & 3) ^ (m_endianness == ENDIANNESS_LITTLE ? 3 : 0)) << 3;
- store<u32>(offset & ~3, m_r[RTREG] >> shift, 0xffffffffU >> shift);
+ store<u32, false>(offset, m_r[RTREG] >> shift, 0xffffffffU >> shift);
}
void mips1core_device_base::swr(u32 const op)
{
offs_t const offset = SIMMVAL + m_r[RSREG];
- unsigned const shift = ((offset & 3) ^ ENDIAN_VALUE_LE_BE(m_endianness, 0, 3)) << 3;
+ unsigned const shift = ((offset & 3) ^ (m_endianness == ENDIANNESS_LITTLE ? 0 : 3)) << 3;
- store<u32>(offset & ~3, m_r[RTREG] << shift, 0xffffffffU << shift);
+ store<u32, false>(offset, m_r[RTREG] << shift, 0xffffffffU << shift);
}
-template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, void> mips1core_device_base::load(u32 address, U &&apply)
+/*
+ * This function determines the active cache (instruction or data) depending on
+ * the icache parameter and the status register SwC (swap caches) flag. A line
+ * within the cache is then selected based upon the low address bits. The upper
+ * address bits are compared with the line tag to identify whether the lookup
+ * is a hit or a miss.
+ *
+ * If the cache lookup misses and the invalidate parameter evaluates to true,
+ * the cache line tag is updated to match the input address and invalidated.
+ *
+ * The function returns the selected line and the miss state.
+ *
+ * TODO: multiple-word cache lines
+ */
+std::tuple<struct mips1core_device_base::cache::line &, bool> mips1core_device_base::cache_lookup(u32 address, bool invalidate, bool icache)
{
- if (memory_translate(m_data_spacenum, TRANSLATE_READ, address))
- {
- T const data
- = (sizeof(T) == 1) ? space(m_data_spacenum).read_byte(address)
- : (sizeof(T) == 2) ? space(m_data_spacenum).read_word(address)
- : space(m_data_spacenum).read_dword(address);
+ // cache line data is word-addressed
+ address &= ~3;
- if (m_bus_error)
- {
- m_bus_error = false;
- generate_exception(EXCEPTION_BUSDATA);
- }
- else
- apply(data);
- }
+ // select instruction or data cache
+ struct cache const &c = (icache ^ bool(SR & SR_SwC)) ? m_icache : m_dcache;
+
+ // select line within cache based on low address bits
+ struct cache::line &l = c.line[(address & (c.size - 1)) >> 2];
+
+ // clear cache parity error
+ SR &= ~SR_PE;
+
+ // compare cache line tag against upper address bits and line valid bit
+ bool const miss = (l.tag ^ address) & (-c.size | cache::line::INV);
+
+ // on cache miss, optionally update the line tag and invalidate (cache
+ // miss is usually followed by line replacement)
+ if (miss && invalidate)
+ l.tag = (address & -c.size) | cache::line::INV;
+
+ return std::tie(l, miss);
}
-template <typename T, typename U> std::enable_if_t<std::is_convertible<U, T>::value, void> mips1core_device_base::store(u32 address, U data, T mem_mask)
+// compute bit position of sub-unit within a word given endianness and address
+template <typename T> unsigned mips1core_device_base::shift_factor(u32 address) const
{
- if (memory_translate(m_data_spacenum, TRANSLATE_WRITE, address))
- {
- switch (sizeof(T))
- {
- case 1: space(m_data_spacenum).write_byte(address, T(data)); break;
- case 2: space(m_data_spacenum).write_word(address, T(data), mem_mask); break;
- case 4: space(m_data_spacenum).write_dword(address, T(data), mem_mask); break;
- }
- }
+ if constexpr (sizeof(T) == 1)
+ return ((m_endianness == ENDIANNESS_BIG) ? (address & 3) ^ 3 : (address & 3)) * 8;
+ else if constexpr (sizeof(T) == 2)
+ return ((m_endianness == ENDIANNESS_BIG) ? (address & 2) ^ 2 : (address & 2)) * 8;
+ else
+ return 0;
}
-bool mips1core_device_base::fetch(u32 address, std::function<void(u32)> &&apply)
+template <typename T, bool Aligned, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, void> mips1core_device_base::load(u32 address, U &&apply)
{
- if (memory_translate(0, TRANSLATE_FETCH, address))
+ // alignment error
+ if (Aligned && (address & (sizeof(T) - 1)))
{
- u32 const data = space(0).read_dword(address);
+ address_error(TR_READ, address);
+ return;
+ }
- if (m_bus_error)
+ T data;
+ if (!(SR & SR_IsC))
+ {
+ translate_result const t = translate(TR_READ, address, false);
+ if (t == ERROR)
+ return;
+
+ // align address for ld[lr] instructions
+ if (!Aligned)
+ address &= ~(sizeof(T) - 1);
+
+ if (t == CACHED)
{
- m_bus_error = false;
- generate_exception(EXCEPTION_BUSINST);
+ auto [l, miss] = cache_lookup(address, true);
+
+ if (miss)
+ {
+ // load
+ u32 const data = space(AS_PROGRAM).read_dword(address);
+ if (m_bus_error)
+ {
+ m_bus_error = false;
+ generate_exception(EXCEPTION_BUSDATA);
+
+ return;
+ }
+
+ // replace cache line data and mark valid
+ l.update(data);
+ }
- return false;
+ data = l.data >> shift_factor<T>(address);
}
+ else
+ {
+ if constexpr (sizeof(T) == 4)
+ data = space(AS_PROGRAM).read_dword(address);
+ else if constexpr (sizeof(T) == 2)
+ data = space(AS_PROGRAM).read_word(address);
+ else if constexpr (sizeof(T) == 1)
+ data = space(AS_PROGRAM).read_byte(address);
+
+ if (m_bus_error)
+ {
+ m_bus_error = false;
+ generate_exception(EXCEPTION_BUSDATA);
- apply(data);
- return true;
+ return;
+ }
+ }
}
else
- return false;
+ {
+ // when isolated, loads always hit the cache and the status register
+ // CM flag reflects the actual hit/miss state
+ auto [l, miss] = cache_lookup(address & ~0xe000'0000, false);
+
+ if (miss)
+ SR |= SR_CM;
+ else
+ SR &= ~SR_CM;
+
+ data = l.data >> shift_factor<T>(address);
+ }
+
+ apply(data);
}
-std::string mips1core_device_base::debug_string(u32 string_pointer, unsigned const limit)
+template <typename T, bool Aligned> void mips1core_device_base::store(u32 address, T data, T mem_mask)
{
- auto const suppressor(machine().disable_side_effects());
-
- bool done = false;
- bool mapped = false;
- std::string result("");
+ // alignment error
+ if (Aligned && (address & (sizeof(T) - 1)))
+ {
+ address_error(TR_WRITE, address);
+ return;
+ }
- while (!done)
+ if (!(SR & SR_IsC))
{
- done = true;
- load<u8>(string_pointer++, [limit, &done, &mapped, &result](u8 byte)
+ translate_result const t = translate(TR_WRITE, address, false);
+ if (t == ERROR)
+ return;
+
+ // align address for sd[lr] instructions
+ if (!Aligned)
+ address &= ~(sizeof(T) - 1);
+
+ if (t == CACHED)
{
- mapped = true;
- if (byte != 0)
+ auto [l, miss] = cache_lookup(address, sizeof(T) == 4);
+
+ // cached full word stores always update the cache
+ if constexpr (Aligned && sizeof(T) == 4)
+ l.update(data);
+ else if (!miss)
{
- result += byte;
+ if (!m_cache_pws)
+ {
+ // reload the cache line from memory
+ u32 const data = space(AS_PROGRAM).read_dword(address);
+ if (m_bus_error)
+ {
+ m_bus_error = false;
+ generate_exception(EXCEPTION_BUSDATA);
+
+ return;
+ }
- done = result.length() == limit;
+ l.update(data);
+ }
+
+ // merge data into the cache
+ unsigned const shift = shift_factor<T>(address);
+ l.update(u32(data) << shift, u32(mem_mask) << shift);
}
- });
- }
+ }
- if (!mapped)
- result.assign("[unmapped]");
+ // uncached or write-through store
+ if constexpr (sizeof(T) == 4)
+ space(AS_PROGRAM).write_dword(address, T(data), mem_mask);
+ else if constexpr (sizeof(T) == 2)
+ space(AS_PROGRAM).write_word(address, T(data), mem_mask);
+ else if constexpr (sizeof(T) == 1)
+ space(AS_PROGRAM).write_byte(address, T(data));
+ }
+ else
+ {
+ // when isolated, full word stores update the cache, while partial word
+ // stores invalidate the cache line
+ auto [l, miss] = cache_lookup(address & ~0xe000'0000, true);
- return result;
+ if constexpr (Aligned && sizeof(T) == 4)
+ l.update(data, mem_mask);
+ else
+ l.invalidate();
+ }
}
-std::string mips1core_device_base::debug_string_array(u32 array_pointer)
+void mips1core_device_base::fetch(u32 address, std::function<void(u32)> &&apply)
{
- auto const suppressor(machine().disable_side_effects());
+ // alignment error
+ if (address & 3)
+ address_error(TR_FETCH, address);
- bool done = false;
- std::string result("");
+ translate_result const t = translate(TR_FETCH, address, false);
+ if (t == ERROR)
+ return;
- while (!done)
+ u32 data;
+ if (t == CACHED)
{
- done = true;
- load<u32>(array_pointer, [this, &done, &result](u32 string_pointer)
+ auto [l, miss] = cache_lookup(address, true, true);
+
+ if (miss)
{
- if (string_pointer != 0)
+ // fetch
+ u32 const data = space(AS_PROGRAM).read_dword(address);
+ if (m_bus_error)
{
- if (!result.empty())
- result += ", ";
-
- result += '\"' + debug_string(string_pointer) + '\"';
+ m_bus_error = false;
+ generate_exception(EXCEPTION_BUSINST);
- done = false;
+ return;
}
- });
- array_pointer += 4;
+ // replace cache line data and mark valid
+ l.update(data);
+ }
+
+ data = l.data;
+ }
+ else
+ {
+ data = space(AS_PROGRAM).read_dword(address);
+
+ if (m_bus_error)
+ {
+ m_bus_error = false;
+ generate_exception(EXCEPTION_BUSINST);
+
+ return;
+ }
}
- return result;
+ apply(data);
}
void mips1_device_base::device_start()
@@ -1191,7 +1338,7 @@ void mips1_device_base::device_start()
if (m_fcr0)
{
state_add(MIPS1_FCR31, "FCSR", m_fcr31);
- for (unsigned i = 0; i < ARRAY_LENGTH(m_f); i++)
+ for (unsigned i = 0; i < std::size(m_f); i++)
state_add(MIPS1_F0 + i, util::string_format("F%d", i * 2).c_str(), m_f[i]);
}
@@ -1213,11 +1360,11 @@ void mips1_device_base::device_reset()
m_reset_time = total_cycles();
// 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;
}
}
@@ -1475,7 +1622,15 @@ void mips1_device_base::handle_cop1(u32 const op)
set_cop1_reg(FDREG >> 1, f32_to_f64(float32_t{ u32(m_f[FSREG >> 1]) }).v);
break;
case 0x24: // CVT.W.S
- set_cop1_reg(FDREG >> 1, f32_to_i32(float32_t{ u32(m_f[FSREG >> 1]) }, softfloat_roundingMode, true));
+ if (BIT(m_f[FSREG >> 1], 23, 8) == 0xff)
+ {
+ // +/- infinity or NaN
+ m_fcr31 &= ~FCR31_CM;
+ m_fcr31 |= FCR31_CE;
+ execute_set_input(m_fpu_irq, ASSERT_LINE);
+ }
+ else
+ set_cop1_reg(FDREG >> 1, f32_to_i32(float32_t{ u32(m_f[FSREG >> 1]) }, softfloat_roundingMode, true));
break;
case 0x30: // C.F.S (false)
@@ -1659,7 +1814,15 @@ void mips1_device_base::handle_cop1(u32 const op)
set_cop1_reg(FDREG >> 1, f64_to_f32(float64_t{ m_f[FSREG >> 1] }).v);
break;
case 0x24: // CVT.W.D
- set_cop1_reg(FDREG >> 1, f64_to_i32(float64_t{ m_f[FSREG >> 1] }, softfloat_roundingMode, true));
+ if (BIT(m_f[FSREG >> 1], 52, 11) == 0x7ff)
+ {
+ // +/- infinity or NaN
+ m_fcr31 &= ~FCR31_CM;
+ m_fcr31 |= FCR31_CE;
+ execute_set_input(m_fpu_irq, ASSERT_LINE);
+ }
+ else
+ set_cop1_reg(FDREG >> 1, f64_to_i32(float64_t{ m_f[FSREG >> 1] }, softfloat_roundingMode, true));
break;
case 0x30: // C.F.D (false)
@@ -1889,20 +2052,23 @@ template <typename T> void mips1_device_base::set_cop1_reg(unsigned const reg, T
m_f[reg] = data;
}
-bool mips1_device_base::memory_translate(int spacenum, int intention, offs_t &address)
+mips1core_device_base::translate_result mips1_device_base::translate(int intention, offs_t &address, bool debug)
{
// check for kernel memory address
if (BIT(address, 31))
{
// check debug or kernel mode
- if ((intention & TRANSLATE_DEBUG_MASK) || !(SR & SR_KUc))
+ if (debug || !(SR & SR_KUc))
{
switch (address & 0xe0000000)
{
case 0x80000000: // kseg0: unmapped, cached, privileged
+ address &= ~0xe0000000;
+ return m_cache;
+
case 0xa0000000: // kseg1: unmapped, uncached, privileged
address &= ~0xe0000000;
- return true;
+ return UNCACHED;
case 0xc0000000: // kseg2: mapped, cached, privileged
case 0xe0000000:
@@ -1911,26 +2077,21 @@ bool mips1_device_base::memory_translate(int spacenum, int intention, offs_t &ad
}
else if (SR & SR_KUc)
{
- if (!machine().side_effects_disabled())
- {
- // exception
- m_cop0[COP0_BadVAddr] = address;
+ address_error(intention, address);
- generate_exception((intention & TRANSLATE_WRITE) ? EXCEPTION_ADDRSTORE : EXCEPTION_ADDRLOAD);
- }
- return false;
+ return ERROR;
}
}
// key is a combination of VPN and ASID
u32 const key = (address & EH_VPN) | (m_cop0[COP0_EntryHi] & EH_ASID);
- unsigned *mru = m_tlb_mru[intention & TRANSLATE_TYPE_MASK];
+ unsigned *mru = m_tlb_mru[intention];
bool refill = !BIT(address, 31);
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];
u32 const *const entry = m_tlb[index];
@@ -1948,7 +2109,7 @@ bool mips1_device_base::memory_translate(int spacenum, int intention, offs_t &ad
}
// test dirty
- if ((intention & TRANSLATE_WRITE) && !(entry[1] & EL_D))
+ if ((intention == TR_WRITE) && !(entry[1] & EL_D))
{
refill = false;
modify = true;
@@ -1963,10 +2124,10 @@ bool mips1_device_base::memory_translate(int spacenum, int intention, offs_t &ad
if (i > 0)
std::swap(mru[i - 1], mru[i]);
- return true;
+ return (entry[1] & EL_N) ? UNCACHED : m_cache;
}
- if (!machine().side_effects_disabled() && !(intention & TRANSLATE_DEBUG_MASK))
+ if (!machine().side_effects_disabled() && !debug)
{
if (VERBOSE & LOG_TLB)
{
@@ -1975,7 +2136,7 @@ bool mips1_device_base::memory_translate(int spacenum, int intention, offs_t &ad
(m_cop0[COP0_EntryHi] & EH_ASID) >> 6, address, machine().describe_context());
else
LOGMASKED(LOG_TLB, "asid %2d tlb miss %c address 0x%08x (%s)\n",
- (m_cop0[COP0_EntryHi] & EH_ASID) >> 6, (intention & TRANSLATE_WRITE) ? 'w' : 'r', address, machine().describe_context());
+ (m_cop0[COP0_EntryHi] & EH_ASID) >> 6, (intention == TR_WRITE) ? 'w' : 'r', address, machine().describe_context());
}
// load tlb exception registers
@@ -1983,8 +2144,8 @@ bool mips1_device_base::memory_translate(int spacenum, int intention, offs_t &ad
m_cop0[COP0_EntryHi] = key;
m_cop0[COP0_Context] = (m_cop0[COP0_Context] & PTE_BASE) | ((address >> 10) & BAD_VPN);
- generate_exception(modify ? EXCEPTION_TLBMOD : (intention & TRANSLATE_WRITE) ? EXCEPTION_TLBSTORE : EXCEPTION_TLBLOAD, refill);
+ generate_exception(modify ? EXCEPTION_TLBMOD : (intention == TR_WRITE) ? EXCEPTION_TLBSTORE : EXCEPTION_TLBLOAD, refill);
}
- return false;
+ return ERROR;
}