diff options
Diffstat (limited to 'src/devices/cpu/mips/mips1.cpp')
-rw-r--r-- | src/devices/cpu/mips/mips1.cpp | 883 |
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; } |