diff options
Diffstat (limited to 'src/devices/cpu/mips')
-rw-r--r-- | src/devices/cpu/mips/mips1.cpp | 883 | ||||
-rw-r--r-- | src/devices/cpu/mips/mips1.h | 294 | ||||
-rw-r--r-- | src/devices/cpu/mips/mips1dsm.cpp | 161 | ||||
-rw-r--r-- | src/devices/cpu/mips/mips3.cpp | 786 | ||||
-rw-r--r-- | src/devices/cpu/mips/mips3.h | 161 | ||||
-rw-r--r-- | src/devices/cpu/mips/mips3com.cpp | 18 | ||||
-rw-r--r-- | src/devices/cpu/mips/mips3com.h | 4 | ||||
-rw-r--r-- | src/devices/cpu/mips/mips3drc.cpp | 565 | ||||
-rw-r--r-- | src/devices/cpu/mips/mips3dsm.cpp | 216 | ||||
-rw-r--r-- | src/devices/cpu/mips/mips3fe.cpp | 8 | ||||
-rw-r--r-- | src/devices/cpu/mips/o2dprintf.hxx | 32 | ||||
-rw-r--r-- | src/devices/cpu/mips/ps2vif1.cpp | 12 | ||||
-rw-r--r-- | src/devices/cpu/mips/ps2vif1.h | 12 | ||||
-rw-r--r-- | src/devices/cpu/mips/ps2vu.cpp | 15 | ||||
-rw-r--r-- | src/devices/cpu/mips/ps2vu.h | 35 | ||||
-rw-r--r-- | src/devices/cpu/mips/r4000.cpp | 1416 | ||||
-rw-r--r-- | src/devices/cpu/mips/r4000.h | 152 |
17 files changed, 3151 insertions, 1619 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; } diff --git a/src/devices/cpu/mips/mips1.h b/src/devices/cpu/mips/mips1.h index 306944d358f..5de7238281c 100644 --- a/src/devices/cpu/mips/mips1.h +++ b/src/devices/cpu/mips/mips1.h @@ -17,163 +17,28 @@ public: void berr_w(int state) { m_bus_error = bool(state); } protected: - mips1core_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u32 cpurev, size_t icache_size, size_t dcache_size); + 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); - 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 match - 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 + // device_t implementation + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; - 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 - }; - - // device_t overrides - virtual void device_add_mconfig(machine_config &config) override; - virtual void device_start() override; - virtual void device_reset() override; - - // device_execute_interface overrides - virtual u32 execute_min_cycles() const override { return 1; } - virtual u32 execute_max_cycles() const override { return 40; } - virtual u32 execute_input_lines() const override { return 6; } + // device_execute_interface implementation + virtual u32 execute_min_cycles() const noexcept override { return 1; } + virtual u32 execute_max_cycles() const noexcept override { return 40; } virtual void execute_run() override; virtual void execute_set_input(int inputnum, int state) override; - // device_memory_interface overrides + // device_memory_interface implementation virtual space_config_vector memory_space_config() const override; - virtual bool memory_translate(int spacenum, int intention, offs_t &address) override; + virtual bool memory_translate(int spacenum, int intention, offs_t &address, address_space *&target_space) override; - // device_disasm_interface overrides + // device_disasm_interface implementation virtual std::unique_ptr<util::disasm_interface> create_disassembler() override; - void icache_map(address_map &map); - void dcache_map(address_map &map); - - // interrupts + // exceptions void generate_exception(u32 exception, bool refill = false); + void address_error(int intention, u32 const address); // cop0 virtual void handle_cop0(u32 const op); @@ -191,25 +56,54 @@ protected: void swl(u32 const op); void swr(u32 const op); - // memory accessors - template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, void> load(u32 address, U &&apply); - template <typename T, typename U> std::enable_if_t<std::is_convertible<U, T>::value, void> store(u32 address, U data, T mem_mask = ~T(0)); - bool fetch(u32 address, std::function<void(u32)> &&apply); + // memory + enum translate_result : unsigned { ERROR, UNCACHED, CACHED }; + virtual translate_result translate(int intention, offs_t &address, bool debug); + template <typename T, bool Aligned = true, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, void> load(u32 address, U &&apply); + template <typename T, bool Aligned = true> void store(u32 address, T data, T mem_mask = ~T(0)); + void fetch(u32 address, std::function<void(u32)> &&apply); - // debug helpers - std::string debug_string(u32 string_pointer, unsigned const limit = 0); - std::string debug_string_array(u32 array_pointer); + // cache + template <typename T> unsigned shift_factor(u32 address) const; + struct cache + { + cache(size_t size) + : size(size) + { + } + + struct line + { + enum tag_mask : u32 + { + INV = 0x0000'0001, // cache line invalid + }; + + void invalidate() { tag = INV; } + void update(u32 data, u32 mask = 0xffff'ffffU) + { + this->tag &= ~INV; + this->data = (this->data & ~mask) | (data & mask); + } + + u32 tag; + u32 data; + }; + + size_t lines() const { return size / 4; } + void start() { line = std::make_unique<struct line[]>(lines()); } + + size_t const size; + std::unique_ptr<struct line[]> line; + }; + std::tuple<struct cache::line &, bool> cache_lookup(u32 address, bool invalidate, bool icache = false); // address spaces - const address_space_config m_program_config_be; - const address_space_config m_program_config_le; - const address_space_config m_icache_config; - const address_space_config m_dcache_config; - - int m_data_spacenum; + address_space_config const m_program_config_be; + address_space_config const m_program_config_le; // configuration - u32 m_cpurev; + u32 const m_cpurev; endianness_t m_endianness; // core registers @@ -223,7 +117,7 @@ protected: // internal stuff int m_icount; - enum branch_state_t : unsigned + enum branch_state : unsigned { NONE = 0, DELAY = 1, // delay slot instruction active @@ -234,11 +128,13 @@ protected: u32 m_branch_target; // cache memory - size_t const m_icache_size; - size_t const m_dcache_size; + struct cache m_icache; + struct cache m_dcache; + translate_result const m_cache; + bool const m_cache_pws; // cache supports partial-word store // I/O - devcb_read_line m_in_brcond[4]; + devcb_read_line::array<4> m_in_brcond; bool m_bus_error; }; @@ -246,7 +142,7 @@ class mips1_device_base : public mips1core_device_base { public: // floating point coprocessor revision numbers recognised by RISC/os 4.52 and IRIX - enum fpu_rev_t : u32 + enum fpu_rev : u32 { MIPS_R2360 = 0x0100, // MIPS R2360 Floating Point Board MIPS_R2010 = 0x0200, // MIPS R2010 VLSI Floating Point Chip @@ -260,44 +156,13 @@ public: void set_fpu(u32 revision, unsigned interrupt = 3) { m_fcr0 = revision; m_fpu_irq = interrupt; } protected: - mips1_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u32 cpurev, size_t icache_size, size_t dcache_size); - - enum cp1_fcr31_mask : u32 - { - FCR31_RM = 0x00000003, // rounding mode - - FCR31_FI = 0x00000004, // inexact operation flag - FCR31_FU = 0x00000008, // underflow flag - FCR31_FO = 0x00000010, // overflow flag - FCR31_FZ = 0x00000020, // divide by zero flag - FCR31_FV = 0x00000040, // invalid operation flag - - FCR31_EI = 0x00000080, // inexact operation enable - FCR31_EU = 0x00000100, // underflow enable - FCR31_EO = 0x00000200, // overflow enable - FCR31_EZ = 0x00000400, // divide by zero enable - FCR31_EV = 0x00000800, // invalid operation enable - - FCR31_CI = 0x00001000, // inexact operation cause - FCR31_CU = 0x00002000, // underflow cause - FCR31_CO = 0x00004000, // overflow cause - FCR31_CZ = 0x00008000, // divide by zero cause - FCR31_CV = 0x00010000, // invalid operation cause - FCR31_CE = 0x00020000, // unimplemented operation cause - - FCR31_C = 0x00800000, // condition - - FCR31_FM = 0x0000007c, // flag mask - FCR31_EM = 0x00000f80, // enable mask - FCR31_CM = 0x0001f000, // cause mask (except unimplemented) - }; + 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); - // device_t overrides - virtual void device_start() override; - virtual void device_reset() override; + // device_t implementation + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; - // device_memory_interface overrides - virtual bool memory_translate(int spacenum, int intention, offs_t &address) override; + virtual translate_result translate(int intention, offs_t &address, bool debug) override; virtual void handle_cop0(u32 const op) override; virtual u32 get_cop0_reg(unsigned const reg) override; @@ -323,70 +188,71 @@ private: class r2000_device : public mips1_device_base { public: - r2000_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, size_t icache_size = 0, size_t dcache_size = 0); + r2000_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock, size_t icache_size = 0, size_t dcache_size = 0); }; class r2000a_device : public mips1_device_base { public: - r2000a_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, size_t icache_size = 0, size_t dcache_size = 0); + r2000a_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock, size_t icache_size = 0, size_t dcache_size = 0); }; class r3000_device : public mips1_device_base { public: - r3000_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, size_t icache_size = 0, size_t dcache_size = 0); + r3000_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock, size_t icache_size = 0, size_t dcache_size = 0); }; class r3000a_device : public mips1_device_base { public: - r3000a_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, size_t icache_size = 0, size_t dcache_size = 0); + r3000a_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock, size_t icache_size = 0, size_t dcache_size = 0); }; class r3041_device : public mips1core_device_base { public: - r3041_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + r3041_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock); protected: - virtual void device_start() override; + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; }; class r3051_device : public mips1core_device_base { public: - r3051_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + r3051_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock); }; class r3052_device : public mips1core_device_base { public: - r3052_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + r3052_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock); }; class r3052e_device : public mips1_device_base { public: - r3052e_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + r3052e_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock); }; class r3071_device : public mips1_device_base { public: - r3071_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, size_t icache_size = 16384, size_t dcache_size = 4096); + r3071_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock, size_t icache_size = 16384, size_t dcache_size = 4096); }; class r3081_device : public mips1_device_base { public: - r3081_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, size_t icache_size = 16384, size_t dcache_size = 4096); + r3081_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock, size_t icache_size = 16384, size_t dcache_size = 4096); }; class iop_device : public mips1core_device_base { public: - iop_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + iop_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock); }; DECLARE_DEVICE_TYPE(R2000, r2000_device) diff --git a/src/devices/cpu/mips/mips1dsm.cpp b/src/devices/cpu/mips/mips1dsm.cpp index 72429ca8c03..563e9e1715a 100644 --- a/src/devices/cpu/mips/mips1dsm.cpp +++ b/src/devices/cpu/mips/mips1dsm.cpp @@ -84,10 +84,12 @@ const char *const mips1_disassembler::ccreg[4][32] = std::string mips1_disassembler::signed_16bit(int16_t val) { - if (val < 0) - return util::string_format("-$%x", -val); + if (val >= -9 && val <= 9) + return util::string_format("%d", val); + else if (val < 0) + return util::string_format("-0x%x", -val); else - return util::string_format("$%x", val); + return util::string_format("0x%x", val); } uint32_t mips1_disassembler::dasm_cop(uint32_t pc, int cop, uint32_t op, std::ostream &stream) @@ -105,11 +107,9 @@ uint32_t mips1_disassembler::dasm_cop(uint32_t pc, int cop, uint32_t op, std::os case 0x08: /* BC */ switch (rt) { - case 0x00: util::stream_format(stream, "bc%df $%08x", cop, pc + 4 + ((int16_t)op << 2)); break; - case 0x01: util::stream_format(stream, "bc%dt $%08x", cop, pc + 4 + ((int16_t)op << 2)); break; - case 0x02: util::stream_format(stream, "bc%dfl [invalid]", cop); break; - case 0x03: util::stream_format(stream, "bc%dtl [invalid]", cop); break; - default: util::stream_format(stream, "dc.l $%08x [invalid]", op); break; + case 0x00: util::stream_format(stream, "bc%df 0x%08x", cop, pc + 4 + ((int16_t)op << 2)); flags = STEP_COND | step_over_extra(1); break; + case 0x01: util::stream_format(stream, "bc%dt 0x%08x", cop, pc + 4 + ((int16_t)op << 2)); flags = STEP_COND | step_over_extra(1); break; + default: util::stream_format(stream, ".word 0x%08x /*invalid*/", op); break; } break; case 0x10: @@ -137,14 +137,13 @@ uint32_t mips1_disassembler::dasm_cop(uint32_t pc, int cop, uint32_t op, std::os case 0x06: util::stream_format(stream, "tlbwr"); break; case 0x08: util::stream_format(stream, "tlbp"); break; case 0x10: util::stream_format(stream, "rfe"); break; - case 0x18: util::stream_format(stream, "eret [invalid]"); break; - default: util::stream_format(stream, "cop%d $%07x", cop, op & 0x01ffffff); break; + default: util::stream_format(stream, "cop%d 0x%07x", cop, op & 0x01ffffff); break; } } else - util::stream_format(stream, "cop%d $%07x", cop, op & 0x01ffffff); + util::stream_format(stream, "cop%d 0x%07x", cop, op & 0x01ffffff); break; - default: util::stream_format(stream, "dc.l $%08x [invalid]", op); break; + default: util::stream_format(stream, ".word 0x%08x /*invalid*/", op); break; } return flags; @@ -176,10 +175,10 @@ uint32_t mips1_disassembler::dasm_cop1(uint32_t pc, uint32_t op, std::ostream &s case 0x08: /* BC */ switch (rt & 3) { - case 0x00: util::stream_format(stream, "bc1f $%08x,%d", pc + 4 + ((int16_t)op << 2), (op >> 18) & 7); break; - case 0x01: util::stream_format(stream, "bc1t $%08x,%d", pc + 4 + ((int16_t)op << 2), (op >> 18) & 7); break; - case 0x02: util::stream_format(stream, "bc1fl $%08x,%d", pc + 4 + ((int16_t)op << 2), (op >> 18) & 7); flags = STEP_OVER | step_over_extra(1); break; - case 0x03: util::stream_format(stream, "bc1tl $%08x,%d", pc + 4 + ((int16_t)op << 2), (op >> 18) & 7); flags = STEP_OVER | step_over_extra(1); break; + case 0x00: util::stream_format(stream, "bc1f 0x%08x,%d", pc + 4 + ((int16_t)op << 2), (op >> 18) & 7); flags = STEP_COND | step_over_extra(1); break; + case 0x01: util::stream_format(stream, "bc1t 0x%08x,%d", pc + 4 + ((int16_t)op << 2), (op >> 18) & 7); flags = STEP_COND | step_over_extra(1); break; + case 0x02: util::stream_format(stream, "bc1fl 0x%08x,%d", pc + 4 + ((int16_t)op << 2), (op >> 18) & 7); flags = STEP_COND | step_over_extra(1); break; + case 0x03: util::stream_format(stream, "bc1tl 0x%08x,%d", pc + 4 + ((int16_t)op << 2), (op >> 18) & 7); flags = STEP_COND | step_over_extra(1); break; } break; default: /* COP */ @@ -226,7 +225,7 @@ uint32_t mips1_disassembler::dasm_cop1(uint32_t pc, uint32_t op, std::ostream &s case 0x3d: util::stream_format(stream, "c.nge.%s %s,%s,%d", fmt, cpreg[1][fs], cpreg[1][ft], (op >> 8) & 7); break; case 0x3e: util::stream_format(stream, "c.le.%s %s,%s,%d", fmt, cpreg[1][fs], cpreg[1][ft], (op >> 8) & 7); break; case 0x3f: util::stream_format(stream, "c.ngt.%s %s,%s,%d", fmt, cpreg[1][fs], cpreg[1][ft], (op >> 8) & 7); break; - default: util::stream_format(stream, "cop1 $%07x", op & 0x01ffffff); break; + default: util::stream_format(stream, "cop1 0x%07x", op & 0x01ffffff); break; } break; } @@ -257,7 +256,7 @@ offs_t mips1_disassembler::disassemble(std::ostream &stream, offs_t pc, const da case 0x04: util::stream_format(stream, "sllv %s,%s,%s", reg[rd], reg[rt], reg[rs]); break; case 0x06: util::stream_format(stream, "srlv %s,%s,%s", reg[rd], reg[rt], reg[rs]); break; case 0x07: util::stream_format(stream, "srav %s,%s,%s", reg[rd], reg[rt], reg[rs]); break; - case 0x08: util::stream_format(stream, "jr %s", reg[rs]); if (rs == 31) flags = STEP_OUT; break; + case 0x08: util::stream_format(stream, "jr %s", reg[rs]); if (rs == 31) flags = STEP_OUT | step_over_extra(1); break; case 0x09: if (rd == 31) util::stream_format(stream, "jalr %s", reg[rs]); else @@ -266,7 +265,6 @@ offs_t mips1_disassembler::disassemble(std::ostream &stream, offs_t pc, const da break; case 0x0c: util::stream_format(stream, "syscall"); flags = STEP_OVER; break; case 0x0d: util::stream_format(stream, "break"); flags = STEP_OVER; break; - case 0x0f: util::stream_format(stream, "sync [invalid]"); break; case 0x10: util::stream_format(stream, "mfhi %s", reg[rd]); break; case 0x11: util::stream_format(stream, "mthi %s", reg[rs]); break; case 0x12: util::stream_format(stream, "mflo %s", reg[rd]); break; @@ -276,72 +274,96 @@ offs_t mips1_disassembler::disassemble(std::ostream &stream, offs_t pc, const da case 0x1a: util::stream_format(stream, "div %s,%s", reg[rs], reg[rt]); break; case 0x1b: util::stream_format(stream, "divu %s,%s", reg[rs], reg[rt]); break; case 0x20: util::stream_format(stream, "add %s,%s,%s", reg[rd], reg[rs], reg[rt]); break; - case 0x21: util::stream_format(stream, "addu %s,%s,%s", reg[rd], reg[rs], reg[rt]); break; - case 0x22: util::stream_format(stream, "sub %s,%s,%s", reg[rd], reg[rs], reg[rt]); break; - case 0x23: util::stream_format(stream, "subu %s,%s,%s", reg[rd], reg[rs], reg[rt]); break; + case 0x21: if (rs == 0 || rt == 0) + util::stream_format(stream, "move %s,%s", reg[rd], reg[rs == 0 ? rt : rs]); + else + util::stream_format(stream, "addu %s,%s,%s", reg[rd], reg[rs], reg[rt]); + break; + case 0x22: if (rs == 0) + util::stream_format(stream, "neg %s,%s", reg[rd], reg[rt]); + else + util::stream_format(stream, "sub %s,%s,%s", reg[rd], reg[rs], reg[rt]); + break; + case 0x23: if (rs == 0) + util::stream_format(stream, "negu %s,%s", reg[rd], reg[rt]); + else + util::stream_format(stream, "subu %s,%s,%s", reg[rd], reg[rs], reg[rt]); + break; case 0x24: util::stream_format(stream, "and %s,%s,%s", reg[rd], reg[rs], reg[rt]); break; - case 0x25: util::stream_format(stream, "or %s,%s,%s", reg[rd], reg[rs], reg[rt]); break; + case 0x25: if (rs == 0 || rt == 0) + util::stream_format(stream, "move %s,%s", reg[rd], reg[rs == 0 ? rt : rs]); + else + util::stream_format(stream, "or %s,%s,%s", reg[rd], reg[rs], reg[rt]); + break; case 0x26: util::stream_format(stream, "xor %s,%s,%s", reg[rd], reg[rs], reg[rt]); break; - case 0x27: util::stream_format(stream, "nor %s,%s,%s", reg[rd], reg[rs], reg[rt]); break; + case 0x27: if (rs == 0) + util::stream_format(stream, "not %s,%s", reg[rd], reg[rt]); + else + util::stream_format(stream, "nor %s,%s,%s", reg[rd], reg[rs], reg[rt]); + break; case 0x2a: util::stream_format(stream, "slt %s,%s,%s", reg[rd], reg[rs], reg[rt]); break; case 0x2b: util::stream_format(stream, "sltu %s,%s,%s", reg[rd], reg[rs], reg[rt]); break; - case 0x30: util::stream_format(stream, "teq [invalid]"); break; - case 0x31: util::stream_format(stream, "tgeu [invalid]"); break; - case 0x32: util::stream_format(stream, "tlt [invalid]"); break; - case 0x33: util::stream_format(stream, "tltu [invalid]"); break; - case 0x34: util::stream_format(stream, "tge [invalid]"); break; - case 0x36: util::stream_format(stream, "tne [invalid]"); break; - default: util::stream_format(stream, "dc.l $%08x [invalid]", op); break; + default: util::stream_format(stream, ".word 0x%08x /*invalid*/", op); break; } break; case 0x01: /* REGIMM */ switch ((op >> 16) & 31) { - case 0x00: util::stream_format(stream, "bltz %s,$%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); break; - case 0x01: util::stream_format(stream, "bgez %s,$%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); break; - case 0x02: util::stream_format(stream, "bltzl [invalid]"); break; - case 0x03: util::stream_format(stream, "bgezl [invalid]"); break; - case 0x08: util::stream_format(stream, "tgei [invalid]"); break; - case 0x09: util::stream_format(stream, "tgeiu [invalid]"); break; - case 0x0a: util::stream_format(stream, "tlti [invalid]"); break; - case 0x0b: util::stream_format(stream, "tltiu [invalid]"); break; - case 0x0c: util::stream_format(stream, "teqi [invalid]"); break; - case 0x0e: util::stream_format(stream, "tnei [invalid]"); break; - case 0x10: util::stream_format(stream, "bltzal %s,$%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); flags = STEP_OVER | step_over_extra(1); break; - case 0x11: util::stream_format(stream, "bgezal %s,$%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); flags = STEP_OVER | step_over_extra(1); break; - case 0x12: util::stream_format(stream, "bltzall [invalid]"); break; - case 0x13: util::stream_format(stream, "bgezall [invalid]"); break; - default: util::stream_format(stream, "dc.l $%08x [invalid]", op); break; + case 0x00: util::stream_format(stream, "bltz %s,0x%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); if (rs != 0) flags = STEP_COND | step_over_extra(1); break; + case 0x01: util::stream_format(stream, "bgez %s,0x%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); if (rs != 0) flags = STEP_COND | step_over_extra(1); break; + case 0x10: util::stream_format(stream, "bltzal %s,0x%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); if (rs != 0) flags = STEP_OVER | STEP_COND | step_over_extra(1); break; + case 0x11: if (rs == 0) + util::stream_format(stream, "bal 0x%08x", pc + 4 + ((int16_t)op << 2)); + else + util::stream_format(stream, "bgezal %s,0x%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); + flags = STEP_OVER | (rs != 0 ? STEP_COND : 0) | step_over_extra(1); + break; + default: util::stream_format(stream, ".word 0x%08x /*invalid*/", op); break; } break; - case 0x02: util::stream_format(stream, "j $%08x", (pc & 0xf0000000) | ((op & 0x03ffffff) << 2)); break; - case 0x03: util::stream_format(stream, "jal $%08x", (pc & 0xf0000000) | ((op & 0x03ffffff) << 2)); flags = STEP_OVER | step_over_extra(1); break; + case 0x02: util::stream_format(stream, "j 0x%08x", (pc & 0xf0000000) | ((op & 0x03ffffff) << 2)); break; + case 0x03: util::stream_format(stream, "jal 0x%08x", (pc & 0xf0000000) | ((op & 0x03ffffff) << 2)); flags = STEP_OVER | step_over_extra(1); break; case 0x04: if (rs == 0 && rt == 0) - util::stream_format(stream, "b $%08x", pc + 4 + ((int16_t)op << 2)); + util::stream_format(stream, "b 0x%08x", pc + 4 + ((int16_t)op << 2)); else - util::stream_format(stream, "beq %s,%s,$%08x", reg[rs], reg[rt], pc + 4 + ((int16_t)op << 2)); - break; - case 0x05: util::stream_format(stream, "bne %s,%s,$%08x", reg[rs], reg[rt], pc + 4 + ((int16_t)op << 2));break; - case 0x06: util::stream_format(stream, "blez %s,$%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); break; - case 0x07: util::stream_format(stream, "bgtz %s,$%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); break; + { + if (rt == 0) + util::stream_format(stream, "beqz %s,0x%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); + else + util::stream_format(stream, "beq %s,%s,0x%08x", reg[rs], reg[rt], pc + 4 + ((int16_t)op << 2)); + flags = STEP_COND | step_over_extra(1); + } + break; + case 0x05: if (rt == 0) + util::stream_format(stream, "bnez %s,0x%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); + else + util::stream_format(stream, "bne %s,%s,0x%08x", reg[rs], reg[rt], pc + 4 + ((int16_t)op << 2)); + flags = STEP_COND | step_over_extra(1); + break; + case 0x06: util::stream_format(stream, "blez %s,0x%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); flags = STEP_COND | step_over_extra(1); break; + case 0x07: util::stream_format(stream, "bgtz %s,0x%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); flags = STEP_COND | step_over_extra(1); break; case 0x08: util::stream_format(stream, "addi %s,%s,%s", reg[rt], reg[rs], signed_16bit(op)); break; - case 0x09: util::stream_format(stream, "addiu %s,%s,%s", reg[rt], reg[rs], signed_16bit(op)); break; + case 0x09: if (rs == 0) + util::stream_format(stream, "li %s,%s", reg[rt], signed_16bit(op)); + else + util::stream_format(stream, "addiu %s,%s,%s", reg[rt], reg[rs], signed_16bit(op)); + break; case 0x0a: util::stream_format(stream, "slti %s,%s,%s", reg[rt], reg[rs], signed_16bit(op)); break; case 0x0b: util::stream_format(stream, "sltiu %s,%s,%s", reg[rt], reg[rs], signed_16bit(op)); break; - case 0x0c: util::stream_format(stream, "andi %s,%s,$%04x", reg[rt], reg[rs], (uint16_t)op); break; - case 0x0d: util::stream_format(stream, "ori %s,%s,$%04x", reg[rt], reg[rs], (uint16_t)op); break; - case 0x0e: util::stream_format(stream, "xori %s,%s,$%04x", reg[rt], reg[rs], (uint16_t)op); break; - case 0x0f: util::stream_format(stream, "lui %s,$%04x", reg[rt], (uint16_t)op); break; + case 0x0c: util::stream_format(stream, "andi %s,%s,0x%04x", reg[rt], reg[rs], (uint16_t)op); break; + case 0x0d: if (rs == 0) + util::stream_format(stream, "li %s,0x%04x", reg[rt], (uint16_t)op); + else + util::stream_format(stream, "ori %s,%s,0x%04x", reg[rt], reg[rs], (uint16_t)op); + break; + case 0x0e: util::stream_format(stream, "xori %s,%s,0x%04x", reg[rt], reg[rs], (uint16_t)op); break; + case 0x0f: util::stream_format(stream, "lui %s,0x%04x", reg[rt], (uint16_t)op); break; case 0x10: flags = dasm_cop(pc, 0, op, stream); break; case 0x11: flags = dasm_cop1(pc, op, stream); break; case 0x12: flags = dasm_cop(pc, 2, op, stream); break; case 0x13: flags = dasm_cop(pc, 3, op, stream); break; - case 0x14: util::stream_format(stream, "beql [invalid]"); break; - case 0x15: util::stream_format(stream, "bnel [invalid]"); break; - case 0x16: util::stream_format(stream, "blezl [invalid]"); break; - case 0x17: util::stream_format(stream, "bgtzl [invalid]"); break; case 0x20: util::stream_format(stream, "lb %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]); break; case 0x21: util::stream_format(stream, "lh %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]); break; case 0x22: util::stream_format(stream, "lwl %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]); break; @@ -354,24 +376,13 @@ offs_t mips1_disassembler::disassemble(std::ostream &stream, offs_t pc, const da case 0x2a: util::stream_format(stream, "swl %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]); break; case 0x2b: util::stream_format(stream, "sw %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]); break; case 0x2e: util::stream_format(stream, "swr %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]); break; - case 0x2f: util::stream_format(stream, "cache [invalid]"); break; - case 0x30: util::stream_format(stream, "ll [invalid]"); break; case 0x31: util::stream_format(stream, "lwc1 %s,%s(%s)", cpreg[1][rt], signed_16bit(op), reg[rs]); break; case 0x32: util::stream_format(stream, "lwc2 %s,%s(%s)", cpreg[2][rt], signed_16bit(op), reg[rs]); break; case 0x33: util::stream_format(stream, "lwc3 %s,%s(%s)", cpreg[3][rt], signed_16bit(op), reg[rs]); break; - case 0x34: util::stream_format(stream, "ldc0 [invalid]"); break; - case 0x35: util::stream_format(stream, "ldc1 [invalid]"); break; - case 0x36: util::stream_format(stream, "ldc2 [invalid]"); break; - case 0x37: util::stream_format(stream, "ldc3 [invalid]"); break; - case 0x38: util::stream_format(stream, "sc [invalid]"); break; case 0x39: util::stream_format(stream, "swc1 %s,%s(%s)", cpreg[1][rt], signed_16bit(op), reg[rs]); break; case 0x3a: util::stream_format(stream, "swc2 %s,%s(%s)", cpreg[2][rt], signed_16bit(op), reg[rs]); break; case 0x3b: util::stream_format(stream, "swc3 %s,%s(%s)", cpreg[3][rt], signed_16bit(op), reg[rs]); break; - case 0x3c: util::stream_format(stream, "sdc0 [invalid]"); break; - case 0x3d: util::stream_format(stream, "sdc1 [invalid]"); break; - case 0x3e: util::stream_format(stream, "sdc2 [invalid]"); break; - case 0x3f: util::stream_format(stream, "sdc3 [invalid]"); break; - default: util::stream_format(stream, "dc.l $%08x [invalid]", op); break; + default: util::stream_format(stream, ".word 0x%08x /*invalid*/", op); break; } return 4 | flags | SUPPORTED; } diff --git a/src/devices/cpu/mips/mips3.cpp b/src/devices/cpu/mips/mips3.cpp index bd50fb22df7..060c5acfc2f 100644 --- a/src/devices/cpu/mips/mips3.cpp +++ b/src/devices/cpu/mips/mips3.cpp @@ -9,7 +9,6 @@ ***************************************************************************/ #include "emu.h" -#include "debugger.h" #include "mips3.h" #include "mips3com.h" #include "mips3dsm.h" @@ -19,6 +18,9 @@ #define ENABLE_OVERFLOWS (0) #define ENABLE_EE_ELF_LOADER (0) #define ENABLE_EE_DECI2 (0) +#define ENABLE_O2_DPRINTF (0) + +#include "o2dprintf.hxx" /*************************************************************************** HELPER MACROS @@ -124,7 +126,8 @@ DEFINE_DEVICE_TYPE(R5000BE, r5000be_device, "r5000be", "MIPS R5000 (big)") DEFINE_DEVICE_TYPE(R5000LE, r5000le_device, "r5000le", "MIPS R5000 (little)") DEFINE_DEVICE_TYPE(VR5500BE, vr5500be_device, "vr5500be", "NEC VR5500 (big)") DEFINE_DEVICE_TYPE(VR5500LE, vr5500le_device, "vr5500le", "NEC VR5500 (little)") -DEFINE_DEVICE_TYPE(R5900LE, r5900le_device, "r5900le", "Emotion Engine Core") +DEFINE_DEVICE_TYPE(R5900BE, r5900be_device, "r5900be", "Emotion Engine Core (big)") +DEFINE_DEVICE_TYPE(R5900LE, r5900le_device, "r5900le", "Emotion Engine Core (little)") DEFINE_DEVICE_TYPE(QED5271BE, qed5271be_device, "qed5271be", "MIPS QED5271 (big)") DEFINE_DEVICE_TYPE(QED5271LE, qed5271le_device, "qed5271le", "MIPS QED5271 (little)") DEFINE_DEVICE_TYPE(RM7000BE, rm7000be_device, "rm7000be", "MIPS RM7000 (big)") @@ -244,7 +247,7 @@ void mips3_device::generate_exception(int exception, int backup) if (exception != 0) { - fprintf(stderr, "Exception: PC=%08X, PPC=%08X\n", m_core->pc, m_ppc); + fprintf(stderr, "Exception type %d: PC=%08X, PPC=%08X\n", exception, m_core->pc, m_ppc); machine().debug_break(); } */ @@ -345,11 +348,9 @@ void mips3_device::generate_exception(int exception, int backup) void mips3_device::generate_tlb_exception(int exception, offs_t address) { m_core->cpr[0][COP0_BadVAddr] = address; - if(exception == EXCEPTION_TLBLOAD || exception == EXCEPTION_TLBSTORE || exception == EXCEPTION_TLBLOAD_FILL || exception == EXCEPTION_TLBSTORE_FILL) - { - m_core->cpr[0][COP0_Context] = (m_core->cpr[0][COP0_Context] & 0xff800000) | ((address >> 9) & 0x007ffff0); - m_core->cpr[0][COP0_EntryHi] = (address & 0xffffe000) | (m_core->cpr[0][COP0_EntryHi] & 0xff); - } + m_core->cpr[0][COP0_Context] = (m_core->cpr[0][COP0_Context] & 0xff800000) | ((address >> 9) & 0x007ffff0); + m_core->cpr[0][COP0_EntryHi] = (address & 0xffffe000) | (m_core->cpr[0][COP0_EntryHi] & 0xff); + generate_exception(exception, 1); } @@ -372,7 +373,7 @@ void mips3_device::check_irqs() generate_exception(EXCEPTION_INTERRUPT, 0); } -void r5900le_device::check_irqs() +void r5900_device::check_irqs() { if ((CAUSE & SR & 0xfc00) && (SR & SR_IE) && (SR & SR_EIE) && !(SR & (SR_EXL | SR_ERL))) generate_exception(EXCEPTION_INTERRUPT, 0); @@ -401,38 +402,35 @@ void mips3_device::device_start() { if (m_data_bits == 32) { - auto cache = m_program->cache<2, 0, ENDIANNESS_LITTLE>(); - m_pr32 = [cache](offs_t address) -> u32 { return cache->read_dword(address); }; - m_prptr = [cache](offs_t address) -> const void * { return cache->read_ptr(address); }; + m_program->cache(m_cache32le); + m_pr32 = delegate<u32 (offs_t)>(&memory_access<32, 2, 0, ENDIANNESS_LITTLE>::cache::read_dword, &m_cache32le); + m_prptr = [this] (offs_t address) -> const void * { return m_cache32le.read_ptr(address); }; } else { - auto cache = m_program->cache<3, 0, ENDIANNESS_LITTLE>(); - m_pr32 = [cache](offs_t address) -> u32 { return cache->read_dword(address); }; - m_prptr = [cache](offs_t address) -> const void * { return cache->read_ptr(address); }; + m_program->cache(m_cache64le); + m_pr32 = delegate<u32 (offs_t)>(&memory_access<32, 3, 0, ENDIANNESS_LITTLE>::cache::read_dword, &m_cache64le); + m_prptr = [this] (offs_t address) -> const void * { return m_cache64le.read_ptr(address); }; } } else { if (m_data_bits == 32) { - auto cache = m_program->cache<2, 0, ENDIANNESS_BIG>(); - m_pr32 = [cache](offs_t address) -> u32 { return cache->read_dword(address); }; - m_prptr = [cache](offs_t address) -> const void * { return cache->read_ptr(address); }; + m_program->cache(m_cache32be); + m_pr32 = delegate<u32 (offs_t)>(&memory_access<32, 2, 0, ENDIANNESS_BIG>::cache::read_dword, &m_cache32be); + m_prptr = [this] (offs_t address) -> const void * { return m_cache32be.read_ptr(address); }; } else { - auto cache = m_program->cache<3, 0, ENDIANNESS_BIG>(); - m_pr32 = [cache](offs_t address) -> u32 { return cache->read_dword(address); }; - m_prptr = [cache](offs_t address) -> const void * { return cache->read_ptr(address); }; + m_program->cache(m_cache64be); + m_pr32 = delegate<u32 (offs_t)>(&memory_access<32, 3, 0, ENDIANNESS_BIG>::cache::read_dword, &m_cache64be); + m_prptr = [this] (offs_t address) -> const void * { return m_cache64be.read_ptr(address); }; } } - /* set up the endianness */ - m_program->accessors(m_memory); - /* allocate a timer for the compare interrupt */ - m_compare_int_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(mips3_device::compare_int_callback), this)); + m_compare_int_timer = timer_alloc(FUNC(mips3_device::compare_int_callback), this); uint32_t flags = 0; /* initialize the UML generator */ @@ -444,9 +442,9 @@ void mips3_device::device_start() for (int regnum = 0; regnum < 32; regnum++) { char buf[10]; - sprintf(buf, "r%d", regnum); + snprintf(buf, 10, "r%d", regnum); m_drcuml->symbol_add(&m_core->r[regnum], sizeof(m_core->r[regnum]), buf); - sprintf(buf, "f%d", regnum); + snprintf(buf, 10, "f%d", regnum); m_drcuml->symbol_add(&m_core->cpr[1][regnum], sizeof(m_core->cpr[1][regnum]), buf); } m_drcuml->symbol_add(&m_core->r[REG_LO], sizeof(m_core->r[REG_LO]), "lo"); @@ -474,7 +472,8 @@ void mips3_device::device_start() m_drcuml->symbol_add(&m_core->cpr[0][COP0_TagLo], sizeof(m_core->cpr[0][COP0_TagLo]), "TagLo"); m_drcuml->symbol_add(&m_core->cpr[0][COP0_TagHi], sizeof(m_core->cpr[0][COP0_TagHi]), "TagHi"); m_drcuml->symbol_add(&m_core->cpr[0][COP0_ErrorPC], sizeof(m_core->cpr[0][COP0_ErrorPC]), "ErrorPC"); - m_drcuml->symbol_add(&m_core->ccr[1][31], sizeof(m_core->cpr[1][31]), "fcr31"); + m_drcuml->symbol_add(&m_core->ccr[1][0], sizeof(m_core->ccr[1][0]), "fcr0"); + m_drcuml->symbol_add(&m_core->ccr[1][31], sizeof(m_core->ccr[1][31]), "fcr31"); m_drcuml->symbol_add(&m_core->mode, sizeof(m_core->mode), "mode"); m_drcuml->symbol_add(&m_core->arg0, sizeof(m_core->arg0), "arg0"); m_drcuml->symbol_add(&m_core->arg1, sizeof(m_core->arg1), "arg1"); @@ -545,73 +544,73 @@ void mips3_device::device_start() #if USE_ABI_REG_NAMES state_add( MIPS3_R0, "zero", m_core->r[0]).callimport().formatstr("%016X"); // Can't change R0 - state_add( MIPS3_R1, "at", m_core->r[1]).formatstr("%016X"); - state_add( MIPS3_R2, "v0", m_core->r[2]).formatstr("%016X"); - state_add( MIPS3_R3, "v1", m_core->r[3]).formatstr("%016X"); - state_add( MIPS3_R4, "a0", m_core->r[4]).formatstr("%016X"); - state_add( MIPS3_R5, "a1", m_core->r[5]).formatstr("%016X"); - state_add( MIPS3_R6, "a2", m_core->r[6]).formatstr("%016X"); - state_add( MIPS3_R7, "a3", m_core->r[7]).formatstr("%016X"); - state_add( MIPS3_R8, "t0", m_core->r[8]).formatstr("%016X"); - state_add( MIPS3_R9, "t1", m_core->r[9]).formatstr("%016X"); - state_add( MIPS3_R10, "t2", m_core->r[10]).formatstr("%016X"); - state_add( MIPS3_R11, "t3", m_core->r[11]).formatstr("%016X"); - state_add( MIPS3_R12, "t4", m_core->r[12]).formatstr("%016X"); - state_add( MIPS3_R13, "t5", m_core->r[13]).formatstr("%016X"); - state_add( MIPS3_R14, "t6", m_core->r[14]).formatstr("%016X"); - state_add( MIPS3_R15, "t7", m_core->r[15]).formatstr("%016X"); - state_add( MIPS3_R16, "s0", m_core->r[16]).formatstr("%016X"); - state_add( MIPS3_R17, "s1", m_core->r[17]).formatstr("%016X"); - state_add( MIPS3_R18, "s2", m_core->r[18]).formatstr("%016X"); - state_add( MIPS3_R19, "s3", m_core->r[19]).formatstr("%016X"); - state_add( MIPS3_R20, "s4", m_core->r[20]).formatstr("%016X"); - state_add( MIPS3_R21, "s5", m_core->r[21]).formatstr("%016X"); - state_add( MIPS3_R22, "s6", m_core->r[22]).formatstr("%016X"); - state_add( MIPS3_R23, "s7", m_core->r[23]).formatstr("%016X"); - state_add( MIPS3_R24, "t8", m_core->r[24]).formatstr("%016X"); - state_add( MIPS3_R25, "t9", m_core->r[25]).formatstr("%016X"); - state_add( MIPS3_R26, "k0", m_core->r[26]).formatstr("%016X"); - state_add( MIPS3_R27, "k1", m_core->r[27]).formatstr("%016X"); - state_add( MIPS3_R28, "gp", m_core->r[28]).formatstr("%016X"); - state_add( MIPS3_R29, "sp", m_core->r[29]).formatstr("%016X"); - state_add( MIPS3_R30, "fp", m_core->r[30]).formatstr("%016X"); - state_add( MIPS3_R31, "ra", m_core->r[31]).formatstr("%016X"); + state_add( MIPS3_R1, "at", m_core->r[1]).formatstr("%016X").callimport(); + state_add( MIPS3_R2, "v0", m_core->r[2]).formatstr("%016X").callimport(); + state_add( MIPS3_R3, "v1", m_core->r[3]).formatstr("%016X").callimport(); + state_add( MIPS3_R4, "a0", m_core->r[4]).formatstr("%016X").callimport(); + state_add( MIPS3_R5, "a1", m_core->r[5]).formatstr("%016X").callimport(); + state_add( MIPS3_R6, "a2", m_core->r[6]).formatstr("%016X").callimport(); + state_add( MIPS3_R7, "a3", m_core->r[7]).formatstr("%016X").callimport(); + state_add( MIPS3_R8, "t0", m_core->r[8]).formatstr("%016X").callimport(); + state_add( MIPS3_R9, "t1", m_core->r[9]).formatstr("%016X").callimport(); + state_add( MIPS3_R10, "t2", m_core->r[10]).formatstr("%016X").callimport(); + state_add( MIPS3_R11, "t3", m_core->r[11]).formatstr("%016X").callimport(); + state_add( MIPS3_R12, "t4", m_core->r[12]).formatstr("%016X").callimport(); + state_add( MIPS3_R13, "t5", m_core->r[13]).formatstr("%016X").callimport(); + state_add( MIPS3_R14, "t6", m_core->r[14]).formatstr("%016X").callimport(); + state_add( MIPS3_R15, "t7", m_core->r[15]).formatstr("%016X").callimport(); + state_add( MIPS3_R16, "s0", m_core->r[16]).formatstr("%016X").callimport(); + state_add( MIPS3_R17, "s1", m_core->r[17]).formatstr("%016X").callimport(); + state_add( MIPS3_R18, "s2", m_core->r[18]).formatstr("%016X").callimport(); + state_add( MIPS3_R19, "s3", m_core->r[19]).formatstr("%016X").callimport(); + state_add( MIPS3_R20, "s4", m_core->r[20]).formatstr("%016X").callimport(); + state_add( MIPS3_R21, "s5", m_core->r[21]).formatstr("%016X").callimport(); + state_add( MIPS3_R22, "s6", m_core->r[22]).formatstr("%016X").callimport(); + state_add( MIPS3_R23, "s7", m_core->r[23]).formatstr("%016X").callimport(); + state_add( MIPS3_R24, "t8", m_core->r[24]).formatstr("%016X").callimport(); + state_add( MIPS3_R25, "t9", m_core->r[25]).formatstr("%016X").callimport(); + state_add( MIPS3_R26, "k0", m_core->r[26]).formatstr("%016X").callimport(); + state_add( MIPS3_R27, "k1", m_core->r[27]).formatstr("%016X").callimport(); + state_add( MIPS3_R28, "gp", m_core->r[28]).formatstr("%016X").callimport(); + state_add( MIPS3_R29, "sp", m_core->r[29]).formatstr("%016X").callimport(); + state_add( MIPS3_R30, "fp", m_core->r[30]).formatstr("%016X").callimport(); + state_add( MIPS3_R31, "ra", m_core->r[31]).formatstr("%016X").callimport(); #else state_add( MIPS3_R0, "R0", m_core->r[0]).callimport().formatstr("%016X"); // Can't change R0 - state_add( MIPS3_R1, "R1", m_core->r[1]).formatstr("%016X"); - state_add( MIPS3_R2, "R2", m_core->r[2]).formatstr("%016X"); - state_add( MIPS3_R3, "R3", m_core->r[3]).formatstr("%016X"); - state_add( MIPS3_R4, "R4", m_core->r[4]).formatstr("%016X"); - state_add( MIPS3_R5, "R5", m_core->r[5]).formatstr("%016X"); - state_add( MIPS3_R6, "R6", m_core->r[6]).formatstr("%016X"); - state_add( MIPS3_R7, "R7", m_core->r[7]).formatstr("%016X"); - state_add( MIPS3_R8, "R8", m_core->r[8]).formatstr("%016X"); - state_add( MIPS3_R9, "R9", m_core->r[9]).formatstr("%016X"); - state_add( MIPS3_R10, "R10", m_core->r[10]).formatstr("%016X"); - state_add( MIPS3_R11, "R11", m_core->r[11]).formatstr("%016X"); - state_add( MIPS3_R12, "R12", m_core->r[12]).formatstr("%016X"); - state_add( MIPS3_R13, "R13", m_core->r[13]).formatstr("%016X"); - state_add( MIPS3_R14, "R14", m_core->r[14]).formatstr("%016X"); - state_add( MIPS3_R15, "R15", m_core->r[15]).formatstr("%016X"); - state_add( MIPS3_R16, "R16", m_core->r[16]).formatstr("%016X"); - state_add( MIPS3_R17, "R17", m_core->r[17]).formatstr("%016X"); - state_add( MIPS3_R18, "R18", m_core->r[18]).formatstr("%016X"); - state_add( MIPS3_R19, "R19", m_core->r[19]).formatstr("%016X"); - state_add( MIPS3_R20, "R20", m_core->r[20]).formatstr("%016X"); - state_add( MIPS3_R21, "R21", m_core->r[21]).formatstr("%016X"); - state_add( MIPS3_R22, "R22", m_core->r[22]).formatstr("%016X"); - state_add( MIPS3_R23, "R23", m_core->r[23]).formatstr("%016X"); - state_add( MIPS3_R24, "R24", m_core->r[24]).formatstr("%016X"); - state_add( MIPS3_R25, "R25", m_core->r[25]).formatstr("%016X"); - state_add( MIPS3_R26, "R26", m_core->r[26]).formatstr("%016X"); - state_add( MIPS3_R27, "R27", m_core->r[27]).formatstr("%016X"); - state_add( MIPS3_R28, "R28", m_core->r[28]).formatstr("%016X"); - state_add( MIPS3_R29, "R29", m_core->r[29]).formatstr("%016X"); - state_add( MIPS3_R30, "R30", m_core->r[30]).formatstr("%016X"); - state_add( MIPS3_R31, "R31", m_core->r[31]).formatstr("%016X"); + state_add( MIPS3_R1, "R1", m_core->r[1]).formatstr("%016X").callimport(); + state_add( MIPS3_R2, "R2", m_core->r[2]).formatstr("%016X").callimport(); + state_add( MIPS3_R3, "R3", m_core->r[3]).formatstr("%016X").callimport(); + state_add( MIPS3_R4, "R4", m_core->r[4]).formatstr("%016X").callimport(); + state_add( MIPS3_R5, "R5", m_core->r[5]).formatstr("%016X").callimport(); + state_add( MIPS3_R6, "R6", m_core->r[6]).formatstr("%016X").callimport(); + state_add( MIPS3_R7, "R7", m_core->r[7]).formatstr("%016X").callimport(); + state_add( MIPS3_R8, "R8", m_core->r[8]).formatstr("%016X").callimport(); + state_add( MIPS3_R9, "R9", m_core->r[9]).formatstr("%016X").callimport(); + state_add( MIPS3_R10, "R10", m_core->r[10]).formatstr("%016X").callimport(); + state_add( MIPS3_R11, "R11", m_core->r[11]).formatstr("%016X").callimport(); + state_add( MIPS3_R12, "R12", m_core->r[12]).formatstr("%016X").callimport(); + state_add( MIPS3_R13, "R13", m_core->r[13]).formatstr("%016X").callimport(); + state_add( MIPS3_R14, "R14", m_core->r[14]).formatstr("%016X").callimport(); + state_add( MIPS3_R15, "R15", m_core->r[15]).formatstr("%016X").callimport(); + state_add( MIPS3_R16, "R16", m_core->r[16]).formatstr("%016X").callimport(); + state_add( MIPS3_R17, "R17", m_core->r[17]).formatstr("%016X").callimport(); + state_add( MIPS3_R18, "R18", m_core->r[18]).formatstr("%016X").callimport(); + state_add( MIPS3_R19, "R19", m_core->r[19]).formatstr("%016X").callimport(); + state_add( MIPS3_R20, "R20", m_core->r[20]).formatstr("%016X").callimport(); + state_add( MIPS3_R21, "R21", m_core->r[21]).formatstr("%016X").callimport(); + state_add( MIPS3_R22, "R22", m_core->r[22]).formatstr("%016X").callimport(); + state_add( MIPS3_R23, "R23", m_core->r[23]).formatstr("%016X").callimport(); + state_add( MIPS3_R24, "R24", m_core->r[24]).formatstr("%016X").callimport(); + state_add( MIPS3_R25, "R25", m_core->r[25]).formatstr("%016X").callimport(); + state_add( MIPS3_R26, "R26", m_core->r[26]).formatstr("%016X").callimport(); + state_add( MIPS3_R27, "R27", m_core->r[27]).formatstr("%016X").callimport(); + state_add( MIPS3_R28, "R28", m_core->r[28]).formatstr("%016X").callimport(); + state_add( MIPS3_R29, "R29", m_core->r[29]).formatstr("%016X").callimport(); + state_add( MIPS3_R30, "R30", m_core->r[30]).formatstr("%016X").callimport(); + state_add( MIPS3_R31, "R31", m_core->r[31]).formatstr("%016X").callimport(); #endif - state_add( MIPS3_HI, "HI", m_core->r[REG_HI]).formatstr("%016X"); - state_add( MIPS3_LO, "LO", m_core->r[REG_LO]).formatstr("%016X"); + state_add( MIPS3_HI, "HI", m_core->r[REG_HI]).formatstr("%016X").callimport(); + state_add( MIPS3_LO, "LO", m_core->r[REG_LO]).formatstr("%016X").callimport(); state_add( MIPS3_CCR1_31, "CCR31", m_core->ccr[1][31]).formatstr("%08X"); @@ -728,13 +727,12 @@ void mips3_device::device_start() state_add( MIPS3_LLADDR, "LLAddr", m_core->cpr[0][COP0_LLAddr]).formatstr("%08X"); state_add( STATE_GENPCBASE, "CURPC", m_core->pc).noshow(); - state_add( STATE_GENSP, "CURSP", m_core->r[31]).noshow(); state_add( STATE_GENFLAGS, "CURFLAGS", m_debugger_temp).formatstr("%1s").noshow(); set_icountptr(m_core->icount); } -void r5900le_device::device_start() +void r5900_device::device_start() { mips3_device::device_start(); #if USE_ABI_REG_NAMES @@ -816,6 +814,16 @@ void mips3_device::state_export(const device_state_entry &entry) } } +void mips3_device::state_import(const device_state_entry &entry) +{ + if (m_isdrc && (entry.index() >= MIPS3_R1) && (entry.index() <= MIPS3_LO)) + { + // this refers to HI as R32 and LO as R33 because I'm lazy + const unsigned regnum = entry.index() - MIPS3_R0; + if (m_regmap[regnum].is_int_register()) + logerror("debugger R%u = %08X, must update UML I%u\n", regnum, m_core->r[regnum], m_regmap[regnum].ireg() - uml::REG_I0); + } +} void mips3_device::state_string_export(const device_state_entry &entry, std::string &str) const { @@ -1102,6 +1110,9 @@ void mips3_device::device_reset() m_core->llbit = 0; m_core->count_zero_time = total_cycles(); + /* initialize the FPU state */ + m_core->ccr[1][0] = compute_fpu_prid_register(); + /* initialize the TLB state */ for (int tlbindex = 0; tlbindex < m_tlbentries; tlbindex++) { @@ -1117,11 +1128,11 @@ void mips3_device::device_reset() } /* load the fixed TLB range */ - vtlb_load(2 * m_tlbentries + 0, (0xa0000000 - 0x80000000) >> MIPS3_MIN_PAGE_SHIFT, 0x80000000, 0x00000000 | VTLB_READ_ALLOWED | VTLB_WRITE_ALLOWED | VTLB_FETCH_ALLOWED | VTLB_FLAG_VALID); - vtlb_load(2 * m_tlbentries + 1, (0xc0000000 - 0xa0000000) >> MIPS3_MIN_PAGE_SHIFT, 0xa0000000, 0x00000000 | VTLB_READ_ALLOWED | VTLB_WRITE_ALLOWED | VTLB_FETCH_ALLOWED | VTLB_FLAG_VALID); + vtlb_load(2 * m_tlbentries + 0, (0xa0000000 - 0x80000000) >> MIPS3_MIN_PAGE_SHIFT, 0x80000000, 0x00000000 | READ_ALLOWED | WRITE_ALLOWED | FETCH_ALLOWED | FLAG_VALID); + vtlb_load(2 * m_tlbentries + 1, (0xc0000000 - 0xa0000000) >> MIPS3_MIN_PAGE_SHIFT, 0xa0000000, 0x00000000 | READ_ALLOWED | WRITE_ALLOWED | FETCH_ALLOWED | FLAG_VALID); // TX4925 on-board peripherals pass-through if (m_flavor == MIPS3_TYPE_TX4925) - vtlb_load(2 * m_tlbentries + 2, (0xff200000 - 0xff1f0000) >> MIPS3_MIN_PAGE_SHIFT, 0xff1f0000, 0xff1f0000 | VTLB_READ_ALLOWED | VTLB_WRITE_ALLOWED | VTLB_FETCH_ALLOWED | VTLB_FLAG_VALID); + vtlb_load(2 * m_tlbentries + 2, (0xff200000 - 0xff1f0000) >> MIPS3_MIN_PAGE_SHIFT, 0xff1f0000, 0xff1f0000 | READ_ALLOWED | WRITE_ALLOWED | FETCH_ALLOWED | FLAG_VALID); m_tlb_seed = 0; m_core->mode = (MODE_KERNEL << 1) | 0; @@ -1137,26 +1148,34 @@ void mips3_device::device_reset() } -bool mips3_device::memory_translate(int spacenum, int intention, offs_t &address) +bool mips3_device::memory_translate(int spacenum, int intention, offs_t &address, address_space *&target_space) { + target_space = &space(spacenum); + /* only applies to the program address space */ if (spacenum == AS_PROGRAM) { const vtlb_entry *table = vtlb_table(); vtlb_entry entry = table[address >> MIPS3_MIN_PAGE_SHIFT]; - if ((entry & (1 << (intention & (TRANSLATE_TYPE_MASK | TRANSLATE_USER_MASK)))) == 0) + if ((entry & (1 << intention)) == 0) return false; address = (entry & ~MIPS3_MIN_PAGE_MASK) | (address & MIPS3_MIN_PAGE_MASK); } return true; } +bool r4650_device::memory_translate(int spacenum, int intention, offs_t &address, address_space *&target_space) +{ + target_space = &space(spacenum); + return true; +} + std::unique_ptr<util::disasm_interface> mips3_device::create_disassembler() { return std::make_unique<mips3_disassembler>(); } -std::unique_ptr<util::disasm_interface> r5900le_device::create_disassembler() +std::unique_ptr<util::disasm_interface> r5900_device::create_disassembler() { return std::make_unique<ee_disassembler>(); } @@ -1170,7 +1189,7 @@ std::unique_ptr<util::disasm_interface> r5900le_device::create_disassembler() inline bool mips3_device::RBYTE(offs_t address, uint32_t *result) { const uint32_t tlbval = vtlb_table()[address >> 12]; - if (tlbval & VTLB_READ_ALLOWED) + if (tlbval & READ_ALLOWED) { const uint32_t tlbaddress = (tlbval & ~0xfff) | (address & 0xfff); for (int ramnum = 0; ramnum < m_fastram_select; ramnum++) @@ -1182,11 +1201,11 @@ inline bool mips3_device::RBYTE(offs_t address, uint32_t *result) *result = m_fastram[ramnum].offset_base8[tlbaddress ^ m_byte_xor]; return true; } - *result = (*m_memory.read_byte)(*m_program, tlbaddress); + *result = m_program->read_byte(tlbaddress); } else { - if(tlbval & VTLB_FLAG_FIXED) + if (tlbval & FLAG_FIXED) { generate_tlb_exception(EXCEPTION_TLBLOAD, address); } @@ -1203,7 +1222,7 @@ inline bool mips3_device::RBYTE(offs_t address, uint32_t *result) inline bool mips3_device::RHALF(offs_t address, uint32_t *result) { const uint32_t tlbval = vtlb_table()[address >> 12]; - if (tlbval & VTLB_READ_ALLOWED) + if (tlbval & READ_ALLOWED) { const uint32_t tlbaddress = (tlbval & ~0xfff) | (address & 0xfff); for (int ramnum = 0; ramnum < m_fastram_select; ramnum++) @@ -1215,11 +1234,11 @@ inline bool mips3_device::RHALF(offs_t address, uint32_t *result) *result = m_fastram[ramnum].offset_base16[(tlbaddress ^ m_word_xor) >> 1]; return true; } - *result = (*m_memory.read_word)(*m_program, tlbaddress); + *result = m_program->read_word(tlbaddress); } else { - if(tlbval & VTLB_FLAG_FIXED) + if (tlbval & FLAG_FIXED) { generate_tlb_exception(EXCEPTION_TLBLOAD, address); } @@ -1236,7 +1255,7 @@ inline bool mips3_device::RHALF(offs_t address, uint32_t *result) inline bool mips3_device::RWORD(offs_t address, uint32_t *result, bool insn) { const uint32_t tlbval = vtlb_table()[address >> 12]; - if (tlbval & VTLB_READ_ALLOWED) + if (tlbval & READ_ALLOWED) { const uint32_t tlbaddress = (tlbval & ~0xfff) | (address & 0xfff); for (int ramnum = 0; ramnum < m_fastram_select; ramnum++) @@ -1248,11 +1267,11 @@ inline bool mips3_device::RWORD(offs_t address, uint32_t *result, bool insn) *result = m_fastram[ramnum].offset_base32[(tlbaddress ^ m_dword_xor) >> 2]; return true; } - *result = (*m_memory.read_dword)(*m_program, tlbaddress); + *result = m_program->read_dword(tlbaddress); } else { - if(tlbval & VTLB_FLAG_FIXED) + if (tlbval & FLAG_FIXED) { generate_tlb_exception(EXCEPTION_TLBLOAD, address); } @@ -1269,13 +1288,13 @@ inline bool mips3_device::RWORD(offs_t address, uint32_t *result, bool insn) inline bool mips3_device::RWORD_MASKED(offs_t address, uint32_t *result, uint32_t mem_mask) { const uint32_t tlbval = vtlb_table()[address >> 12]; - if (tlbval & VTLB_READ_ALLOWED) + if (tlbval & READ_ALLOWED) { - *result = (*m_memory.read_dword_masked)(*m_program, (tlbval & ~0xfff) | (address & 0xfff), mem_mask); + *result = m_program->read_dword((tlbval & ~0xfff) | (address & 0xfff), mem_mask); } else { - if(tlbval & VTLB_FLAG_FIXED) + if (tlbval & FLAG_FIXED) { generate_tlb_exception(EXCEPTION_TLBLOAD, address); } @@ -1292,13 +1311,13 @@ inline bool mips3_device::RWORD_MASKED(offs_t address, uint32_t *result, uint32_ inline bool mips3_device::RDOUBLE(offs_t address, uint64_t *result) { const uint32_t tlbval = vtlb_table()[address >> 12]; - if (tlbval & VTLB_READ_ALLOWED) + if (tlbval & READ_ALLOWED) { - *result = (*m_memory.read_qword)(*m_program, (tlbval & ~0xfff) | (address & 0xfff)); + *result = m_program->read_qword((tlbval & ~0xfff) | (address & 0xfff)); } else { - if(tlbval & VTLB_FLAG_FIXED) + if (tlbval & FLAG_FIXED) { generate_tlb_exception(EXCEPTION_TLBLOAD, address); } @@ -1315,13 +1334,13 @@ inline bool mips3_device::RDOUBLE(offs_t address, uint64_t *result) inline bool mips3_device::RDOUBLE_MASKED(offs_t address, uint64_t *result, uint64_t mem_mask) { const uint32_t tlbval = vtlb_table()[address >> 12]; - if (tlbval & VTLB_READ_ALLOWED) + if (tlbval & READ_ALLOWED) { - *result = (*m_memory.read_qword_masked)(*m_program, (tlbval & ~0xfff) | (address & 0xfff), mem_mask); + *result = m_program->read_qword((tlbval & ~0xfff) | (address & 0xfff), mem_mask); } else { - if(tlbval & VTLB_FLAG_FIXED) + if (tlbval & FLAG_FIXED) { generate_tlb_exception(EXCEPTION_TLBLOAD, address); } @@ -1338,7 +1357,7 @@ inline bool mips3_device::RDOUBLE_MASKED(offs_t address, uint64_t *result, uint6 inline void mips3_device::WBYTE(offs_t address, uint8_t data) { const uint32_t tlbval = vtlb_table()[address >> 12]; - if (tlbval & VTLB_WRITE_ALLOWED) + if (tlbval & WRITE_ALLOWED) { const uint32_t tlbaddress = (tlbval & ~0xfff) | (address & 0xfff); for (int ramnum = 0; ramnum < m_fastram_select; ramnum++) @@ -1350,15 +1369,15 @@ inline void mips3_device::WBYTE(offs_t address, uint8_t data) m_fastram[ramnum].offset_base8[tlbaddress ^ m_byte_xor] = data; return; } - (*m_memory.write_byte)(*m_program, tlbaddress, data); + m_program->write_byte(tlbaddress, data); } else { - if(tlbval & VTLB_READ_ALLOWED) + if (tlbval & READ_ALLOWED) { generate_tlb_exception(EXCEPTION_TLBMOD, address); } - else if(tlbval & VTLB_FLAG_FIXED) + else if (tlbval & FLAG_FIXED) { generate_tlb_exception(EXCEPTION_TLBSTORE, address); } @@ -1372,7 +1391,7 @@ inline void mips3_device::WBYTE(offs_t address, uint8_t data) inline void mips3_device::WHALF(offs_t address, uint16_t data) { const uint32_t tlbval = vtlb_table()[address >> 12]; - if (tlbval & VTLB_WRITE_ALLOWED) + if (tlbval & WRITE_ALLOWED) { const uint32_t tlbaddress = (tlbval & ~0xfff) | (address & 0xfff); for (int ramnum = 0; ramnum < m_fastram_select; ramnum++) @@ -1384,15 +1403,15 @@ inline void mips3_device::WHALF(offs_t address, uint16_t data) m_fastram[ramnum].offset_base16[(tlbaddress ^ m_word_xor) >> 1] = data; return; } - (*m_memory.write_word)(*m_program, tlbaddress, data); + m_program->write_word(tlbaddress, data); } else { - if(tlbval & VTLB_READ_ALLOWED) + if (tlbval & READ_ALLOWED) { generate_tlb_exception(EXCEPTION_TLBMOD, address); } - else if(tlbval & VTLB_FLAG_FIXED) + else if (tlbval & FLAG_FIXED) { generate_tlb_exception(EXCEPTION_TLBSTORE, address); } @@ -1406,7 +1425,7 @@ inline void mips3_device::WHALF(offs_t address, uint16_t data) inline void mips3_device::WWORD(offs_t address, uint32_t data) { const uint32_t tlbval = vtlb_table()[address >> 12]; - if (tlbval & VTLB_WRITE_ALLOWED) + if (tlbval & WRITE_ALLOWED) { const uint32_t tlbaddress = (tlbval & ~0xfff) | (address & 0xfff); for (int ramnum = 0; ramnum < m_fastram_select; ramnum++) @@ -1418,15 +1437,15 @@ inline void mips3_device::WWORD(offs_t address, uint32_t data) m_fastram[ramnum].offset_base32[(tlbaddress ^ m_dword_xor) >> 2] = data; return; } - (*m_memory.write_dword)(*m_program, tlbaddress, data); + m_program->write_dword(tlbaddress, data); } else { - if(tlbval & VTLB_READ_ALLOWED) + if (tlbval & READ_ALLOWED) { generate_tlb_exception(EXCEPTION_TLBMOD, address); } - else if(tlbval & VTLB_FLAG_FIXED) + else if (tlbval & FLAG_FIXED) { generate_tlb_exception(EXCEPTION_TLBSTORE, address); } @@ -1440,17 +1459,17 @@ inline void mips3_device::WWORD(offs_t address, uint32_t data) inline void mips3_device::WWORD_MASKED(offs_t address, uint32_t data, uint32_t mem_mask) { const uint32_t tlbval = vtlb_table()[address >> 12]; - if (tlbval & VTLB_WRITE_ALLOWED) + if (tlbval & WRITE_ALLOWED) { - (*m_memory.write_dword_masked)(*m_program, (tlbval & ~0xfff) | (address & 0xfff), data, mem_mask); + m_program->write_dword((tlbval & ~0xfff) | (address & 0xfff), data, mem_mask); } else { - if(tlbval & VTLB_READ_ALLOWED) + if (tlbval & READ_ALLOWED) { generate_tlb_exception(EXCEPTION_TLBMOD, address); } - else if(tlbval & VTLB_FLAG_FIXED) + else if (tlbval & FLAG_FIXED) { generate_tlb_exception(EXCEPTION_TLBSTORE, address); } @@ -1464,17 +1483,17 @@ inline void mips3_device::WWORD_MASKED(offs_t address, uint32_t data, uint32_t m inline void mips3_device::WDOUBLE(offs_t address, uint64_t data) { const uint32_t tlbval = vtlb_table()[address >> 12]; - if (tlbval & VTLB_WRITE_ALLOWED) + if (tlbval & WRITE_ALLOWED) { - (*m_memory.write_qword)(*m_program, (tlbval & ~0xfff) | (address & 0xfff), data); + m_program->write_qword((tlbval & ~0xfff) | (address & 0xfff), data); } else { - if(tlbval & VTLB_READ_ALLOWED) + if (tlbval & READ_ALLOWED) { generate_tlb_exception(EXCEPTION_TLBMOD, address); } - else if(tlbval & VTLB_FLAG_FIXED) + else if (tlbval & FLAG_FIXED) { generate_tlb_exception(EXCEPTION_TLBSTORE, address); } @@ -1488,17 +1507,17 @@ inline void mips3_device::WDOUBLE(offs_t address, uint64_t data) inline void mips3_device::WDOUBLE_MASKED(offs_t address, uint64_t data, uint64_t mem_mask) { const uint32_t tlbval = vtlb_table()[address >> 12]; - if (tlbval & VTLB_WRITE_ALLOWED) + if (tlbval & WRITE_ALLOWED) { - (*m_memory.write_qword_masked)(*m_program, (tlbval & ~0xfff) | (address & 0xfff), data, mem_mask); + m_program->write_qword((tlbval & ~0xfff) | (address & 0xfff), data, mem_mask); } else { - if(tlbval & VTLB_READ_ALLOWED) + if (tlbval & READ_ALLOWED) { generate_tlb_exception(EXCEPTION_TLBMOD, address); } - else if(tlbval & VTLB_FLAG_FIXED) + else if (tlbval & FLAG_FIXED) { generate_tlb_exception(EXCEPTION_TLBSTORE, address); } @@ -1509,38 +1528,211 @@ inline void mips3_device::WDOUBLE_MASKED(offs_t address, uint64_t data, uint64_t } } -inline void r5900le_device::WBYTE(offs_t address, uint8_t data) +inline bool r4650_device::RBYTE(offs_t address, uint32_t *result) +{ + if ((SR & SR_KSU_USER) == SR_KSU_KERNEL) + { + *result = m_program->read_byte(address); + return true; + } + + if ((address & 0xfffff000) > m_core->cpr[0][COP0_R4650_DBound]) + { + generate_tlb_exception(EXCEPTION_ADDRLOAD, address); + *result = 0; + return false; + } + *result = m_program->read_byte(address + m_core->cpr[0][COP0_R4650_DBase]); + return true; +} + +inline bool r4650_device::RHALF(offs_t address, uint32_t *result) +{ + if ((SR & SR_KSU_USER) == SR_KSU_KERNEL) + { + *result = m_program->read_word(address); + return true; + } + + if ((address & 0xfffff000) > m_core->cpr[0][COP0_R4650_DBound]) + { + generate_tlb_exception(EXCEPTION_ADDRLOAD, address); + *result = 0; + return false; + } + *result = m_program->read_word(address + m_core->cpr[0][COP0_R4650_DBase]); + return true; +} + +inline bool r4650_device::RWORD(offs_t address, uint32_t *result, bool insn) +{ + if ((SR & SR_KSU_USER) == SR_KSU_KERNEL) + { + *result = m_program->read_dword(address); + return true; + } + + static const uint32_t BASE_INDICES[2] = { COP0_R4650_DBase, COP0_R4650_IBase }; + static const uint32_t BOUND_INDICES[2] = { COP0_R4650_DBound, COP0_R4650_IBound }; + const uint32_t base = m_core->cpr[0][BASE_INDICES[insn]]; + const uint32_t bound = m_core->cpr[0][BOUND_INDICES[insn]]; + if ((address & 0xfffff000) > bound) + { + generate_tlb_exception(EXCEPTION_ADDRLOAD, address); + *result = 0; + return false; + } + *result = m_program->read_dword(address + base); + return true; +} + +inline bool r4650_device::RWORD_MASKED(offs_t address, uint32_t *result, uint32_t mem_mask) +{ + if ((SR & SR_KSU_USER) == SR_KSU_KERNEL) + { + *result = m_program->read_dword(address, mem_mask); + return true; + } + + if ((address & 0xfffff000) > m_core->cpr[0][COP0_R4650_DBound]) + { + generate_tlb_exception(EXCEPTION_ADDRLOAD, address); + *result = 0; + return false; + } + *result = m_program->read_dword(address + m_core->cpr[0][COP0_R4650_DBase], mem_mask); + return true; +} + +inline bool r4650_device::RDOUBLE(offs_t address, uint64_t *result) +{ + if ((SR & SR_KSU_USER) == SR_KSU_KERNEL) + { + *result = m_program->read_qword(address); + return true; + } + + if ((address & 0xfffff000) > m_core->cpr[0][COP0_R4650_DBound]) + { + generate_tlb_exception(EXCEPTION_ADDRLOAD, address); + *result = 0; + return false; + } + *result = m_program->read_qword(address + m_core->cpr[0][COP0_R4650_DBase]); + return true; +} + +inline bool r4650_device::RDOUBLE_MASKED(offs_t address, uint64_t *result, uint64_t mem_mask) +{ + if ((SR & SR_KSU_USER) == SR_KSU_KERNEL) + { + *result = m_program->read_qword(address, mem_mask); + return true; + } + + if ((address & 0xfffff000) > m_core->cpr[0][COP0_R4650_DBound]) + { + generate_tlb_exception(EXCEPTION_ADDRLOAD, address); + *result = 0; + return false; + } + *result = m_program->read_qword(address + m_core->cpr[0][COP0_R4650_DBase], mem_mask); + return true; +} + +inline void r4650_device::WBYTE(offs_t address, uint8_t data) +{ + if ((SR & SR_KSU_USER) == SR_KSU_KERNEL) + m_program->write_byte(address, data); + else if ((address & 0xfffff000) > m_core->cpr[0][COP0_R4650_DBound]) + generate_tlb_exception(EXCEPTION_ADDRSTORE, address); + else + m_program->write_byte(address + m_core->cpr[0][COP0_R4650_DBound], data); +} + +inline void r4650_device::WHALF(offs_t address, uint16_t data) +{ + if ((SR & SR_KSU_USER) == SR_KSU_KERNEL) + m_program->write_word(address, data); + else if ((address & 0xfffff000) > m_core->cpr[0][COP0_R4650_DBound]) + generate_tlb_exception(EXCEPTION_ADDRSTORE, address); + else + m_program->write_word(address + m_core->cpr[0][COP0_R4650_DBound], data); +} + +inline void r4650_device::WWORD(offs_t address, uint32_t data) +{ + if ((SR & SR_KSU_USER) == SR_KSU_KERNEL) + m_program->write_dword(address, data); + else if ((address & 0xfffff000) > m_core->cpr[0][COP0_R4650_DBound]) + generate_tlb_exception(EXCEPTION_ADDRSTORE, address); + else + m_program->write_dword(address + m_core->cpr[0][COP0_R4650_DBound], data); +} + +inline void r4650_device::WWORD_MASKED(offs_t address, uint32_t data, uint32_t mem_mask) +{ + if ((SR & SR_KSU_USER) == SR_KSU_KERNEL) + m_program->write_dword(address, data, mem_mask); + else if ((address & 0xfffff000) > m_core->cpr[0][COP0_R4650_DBound]) + generate_tlb_exception(EXCEPTION_ADDRSTORE, address); + else + m_program->write_dword(address + m_core->cpr[0][COP0_R4650_DBound], data, mem_mask); +} + +inline void r4650_device::WDOUBLE(offs_t address, uint64_t data) +{ + if ((SR & SR_KSU_USER) == SR_KSU_KERNEL) + m_program->write_qword(address, data); + else if ((address & 0xfffff000) > m_core->cpr[0][COP0_R4650_DBound]) + generate_tlb_exception(EXCEPTION_ADDRSTORE, address); + else + m_program->write_qword(address + m_core->cpr[0][COP0_R4650_DBound], data); +} + +inline void r4650_device::WDOUBLE_MASKED(offs_t address, uint64_t data, uint64_t mem_mask) { - if (address >= 0x70000000 && address < 0x70004000) (*m_memory.write_byte)(*m_program, address, data); + if ((SR & SR_KSU_USER) == SR_KSU_KERNEL) + m_program->write_qword(address, data, mem_mask); + else if ((address & 0xfffff000) > m_core->cpr[0][COP0_R4650_DBound]) + generate_tlb_exception(EXCEPTION_ADDRSTORE, address); + else + m_program->write_qword(address + m_core->cpr[0][COP0_R4650_DBound], data, mem_mask); +} + +inline void r5900_device::WBYTE(offs_t address, uint8_t data) +{ + if (address >= 0x70000000 && address < 0x70004000) m_program->write_byte(address, data); else mips3_device::WBYTE(address, data); } -inline void r5900le_device::WHALF(offs_t address, uint16_t data) +inline void r5900_device::WHALF(offs_t address, uint16_t data) { - if (address >= 0x70000000 && address < 0x70004000) (*m_memory.write_word)(*m_program, address, data); + if (address >= 0x70000000 && address < 0x70004000) m_program->write_word(address, data); else mips3_device::WHALF(address, data); } -inline void r5900le_device::WWORD(offs_t address, uint32_t data) +inline void r5900_device::WWORD(offs_t address, uint32_t data) { - if (address >= 0x70000000 && address < 0x70004000) (*m_memory.write_dword)(*m_program, address, data); + if (address >= 0x70000000 && address < 0x70004000) m_program->write_dword(address, data); else mips3_device::WWORD(address, data); } -inline void r5900le_device::WWORD_MASKED(offs_t address, uint32_t data, uint32_t mem_mask) +inline void r5900_device::WWORD_MASKED(offs_t address, uint32_t data, uint32_t mem_mask) { - if (address >= 0x70000000 && address < 0x70004000) (*m_memory.write_dword_masked)(*m_program, address, data, mem_mask); + if (address >= 0x70000000 && address < 0x70004000) m_program->write_dword(address, data, mem_mask); else mips3_device::WWORD_MASKED(address, data, mem_mask); } -inline void r5900le_device::WDOUBLE(offs_t address, uint64_t data) { - if (address >= 0x70000000 && address < 0x70004000) (*m_memory.write_qword)(*m_program, address, data); +inline void r5900_device::WDOUBLE(offs_t address, uint64_t data) +{ + if (address >= 0x70000000 && address < 0x70004000) m_program->write_qword(address, data); else mips3_device::WDOUBLE(address, data); } -inline void r5900le_device::WDOUBLE_MASKED(offs_t address, uint64_t data, uint64_t mem_mask) +inline void r5900_device::WDOUBLE_MASKED(offs_t address, uint64_t data, uint64_t mem_mask) { - if (address >= 0x70000000 && address < 0x70004000) (*m_memory.write_qword_masked)(*m_program, address, data, mem_mask); + if (address >= 0x70000000 && address < 0x70004000) m_program->write_qword(address, data, mem_mask); else mips3_device::WDOUBLE_MASKED(address, data, mem_mask); } @@ -1548,24 +1740,56 @@ inline void r5900le_device::WQUAD(offs_t address, uint64_t data_hi, uint64_t dat { if (address >= 0x70000000 && address < 0x70004000) { - (*m_memory.write_qword)(*m_program, address, data_lo); - (*m_memory.write_qword)(*m_program, address + 8, data_hi); + m_program->write_qword(address, data_lo); + m_program->write_qword(address + 8, data_hi); + return; + } + + const uint32_t tlbval = vtlb_table()[address >> 12]; + if (tlbval & WRITE_ALLOWED) + { + m_program->write_qword((tlbval & ~0xfff) | (address & 0xfff), data_lo); + m_program->write_qword((tlbval & ~0xfff) | ((address + 8) & 0xfff), data_hi); + } + else + { + if (tlbval & READ_ALLOWED) + { + generate_tlb_exception(EXCEPTION_TLBMOD, address); + } + else if (tlbval & FLAG_FIXED) + { + generate_tlb_exception(EXCEPTION_TLBSTORE, address); + } + else + { + generate_tlb_exception(EXCEPTION_TLBSTORE_FILL, address); + } + } +} + +inline void r5900be_device::WQUAD(offs_t address, uint64_t data_hi, uint64_t data_lo) +{ + if (address >= 0x70000000 && address < 0x70004000) + { + m_program->write_qword(address, data_hi); + m_program->write_qword(address + 8, data_lo); return; } const uint32_t tlbval = vtlb_table()[address >> 12]; - if (tlbval & VTLB_WRITE_ALLOWED) + if (tlbval & WRITE_ALLOWED) { - (*m_memory.write_qword)(*m_program, (tlbval & ~0xfff) | (address & 0xfff), data_lo); - (*m_memory.write_qword)(*m_program, (tlbval & ~0xfff) | ((address + 8) & 0xfff), data_hi); + m_program->write_qword((tlbval & ~0xfff) | (address & 0xfff), data_hi); + m_program->write_qword((tlbval & ~0xfff) | ((address + 8) & 0xfff), data_lo); } else { - if(tlbval & VTLB_READ_ALLOWED) + if (tlbval & READ_ALLOWED) { generate_tlb_exception(EXCEPTION_TLBMOD, address); } - else if(tlbval & VTLB_FLAG_FIXED) + else if (tlbval & FLAG_FIXED) { generate_tlb_exception(EXCEPTION_TLBSTORE, address); } @@ -1576,59 +1800,61 @@ inline void r5900le_device::WQUAD(offs_t address, uint64_t data_hi, uint64_t dat } } -inline bool r5900le_device::RBYTE(offs_t address, uint32_t *result) { - if (address >= 0x70000000 && address < 0x70004000) { - *result = (*m_memory.read_byte)(*m_program, address); +inline bool r5900_device::RBYTE(offs_t address, uint32_t *result) +{ + if (address >= 0x70000000 && address < 0x70004000) + { + *result = m_program->read_byte(address); return true; } return mips3_device::RBYTE(address, result); } -inline bool r5900le_device::RHALF(offs_t address, uint32_t *result) +inline bool r5900_device::RHALF(offs_t address, uint32_t *result) { if (address >= 0x70000000 && address < 0x70004000) { - *result = (*m_memory.read_word)(*m_program, address); + *result = m_program->read_word(address); return true; } return mips3_device::RHALF(address, result); } -inline bool r5900le_device::RWORD(offs_t address, uint32_t *result, bool insn) +inline bool r5900_device::RWORD(offs_t address, uint32_t *result, bool insn) { if (address >= 0x70000000 && address < 0x70004000) { - *result = (*m_memory.read_dword)(*m_program, address); + *result = m_program->read_dword(address); return true; } return mips3_device::RWORD(address, result, insn); } -inline bool r5900le_device::RWORD_MASKED(offs_t address, uint32_t *result, uint32_t mem_mask) +inline bool r5900_device::RWORD_MASKED(offs_t address, uint32_t *result, uint32_t mem_mask) { if (address >= 0x70000000 && address < 0x70004000) { - *result = (*m_memory.read_dword_masked)(*m_program, address, mem_mask); + *result = m_program->read_dword(address, mem_mask); return true; } return mips3_device::RWORD_MASKED(address, result, mem_mask); } -inline bool r5900le_device::RDOUBLE(offs_t address, uint64_t *result) +inline bool r5900_device::RDOUBLE(offs_t address, uint64_t *result) { if (address >= 0x70000000 && address < 0x70004000) { - *result = (*m_memory.read_qword)(*m_program, address); + *result = m_program->read_qword(address); return true; } return mips3_device::RDOUBLE(address, result); } -inline bool r5900le_device::RDOUBLE_MASKED(offs_t address, uint64_t *result, uint64_t mem_mask) +inline bool r5900_device::RDOUBLE_MASKED(offs_t address, uint64_t *result, uint64_t mem_mask) { if (address >= 0x70000000 && address < 0x70004000) { - *result = (*m_memory.read_qword_masked)(*m_program, address, mem_mask); + *result = m_program->read_qword(address, mem_mask); return true; } return mips3_device::RDOUBLE_MASKED(address, result, mem_mask); @@ -1638,20 +1864,52 @@ inline bool r5900le_device::RQUAD(offs_t address, uint64_t *result_hi, uint64_t { if (address >= 0x70000000 && address < 0x70004000) { - *result_lo = (*m_memory.read_qword)(*m_program, address); - *result_hi = (*m_memory.read_qword)(*m_program, address + 8); + *result_lo = m_program->read_qword(address); + *result_hi = m_program->read_qword(address + 8); return true; } const uint32_t tlbval = vtlb_table()[address >> 12]; - if (tlbval & VTLB_READ_ALLOWED) + if (tlbval & READ_ALLOWED) { - *result_lo = (*m_memory.read_qword)(*m_program, (tlbval & ~0xfff) | (address & 0xfff)); - *result_hi = (*m_memory.read_qword)(*m_program, (tlbval & ~0xfff) | ((address + 8) & 0xfff)); + *result_lo = m_program->read_qword((tlbval & ~0xfff) | (address & 0xfff)); + *result_hi = m_program->read_qword((tlbval & ~0xfff) | ((address + 8) & 0xfff)); } else { - if(tlbval & VTLB_FLAG_FIXED) + if (tlbval & FLAG_FIXED) + { + generate_tlb_exception(EXCEPTION_TLBLOAD, address); + } + else + { + generate_tlb_exception(EXCEPTION_TLBLOAD_FILL, address); + } + *result_hi = 0; + *result_lo = 0; + return false; + } + return true; +} + +inline bool r5900be_device::RQUAD(offs_t address, uint64_t *result_hi, uint64_t *result_lo) +{ + if (address >= 0x70000000 && address < 0x70004000) + { + *result_hi = m_program->read_qword(address); + *result_lo = m_program->read_qword(address + 8); + return true; + } + + const uint32_t tlbval = vtlb_table()[address >> 12]; + if (tlbval & READ_ALLOWED) + { + *result_hi = m_program->read_qword((tlbval & ~0xfff) | (address & 0xfff)); + *result_lo = m_program->read_qword((tlbval & ~0xfff) | ((address + 8) & 0xfff)); + } + else + { + if (tlbval & FLAG_FIXED) { generate_tlb_exception(EXCEPTION_TLBLOAD, address); } @@ -1695,23 +1953,40 @@ uint64_t mips3_device::get_cop0_reg(int idx) } else if (idx == COP0_Random) { - int wired = m_core->cpr[0][COP0_Wired] & 0x3f; - int range = 48 - wired; - if (range > 0) - return ((total_cycles() - m_core->count_zero_time) % range + wired) & 0x3f; - else - return 47; + uint32_t wired = m_core->cpr[0][COP0_Wired] & 0x3f; + uint32_t unwired = m_tlbentries - wired; + + if (unwired == 0) + return m_tlbentries - 1; + + return (generate_tlb_index() % unwired) + wired; } return m_core->cpr[0][idx]; } +void r4650_device::set_cop0_reg(int idx, uint64_t val) +{ + switch (idx) + { + case COP0_R4650_IBase: + case COP0_R4650_IBound: + case COP0_R4650_DBase: + case COP0_R4650_DBound: + m_core->cpr[0][idx] = val; + break; + default: + mips3_device::set_cop0_reg(idx, val); + break; + } +} + void mips3_device::set_cop0_reg(int idx, uint64_t val) { switch (idx) { case COP0_Cause: CAUSE = (CAUSE & 0xfc00) | (val & ~0xfc00); - if (CAUSE & 0x300) + if ((CAUSE & SR & 0x300) && (SR & SR_IE) && !(SR & (SR_EXL | SR_ERL))) { /* if we're in a delay slot, propogate the target PC before generating the exception */ if (m_nextpc != ~0) @@ -1802,8 +2077,8 @@ void mips3_device::handle_cop0(uint32_t op) case 0x08: /* BC */ switch (RTREG) { - case 0x00: /* BCzF */ if (!m_cf[0]) ADDPC(SIMMVAL); break; - case 0x01: /* BCzF */ if (m_cf[0]) ADDPC(SIMMVAL); break; + case 0x00: /* BCzF */ if (!m_cf[0][0]) ADDPC(SIMMVAL); break; + case 0x01: /* BCzF */ if (m_cf[0][0]) ADDPC(SIMMVAL); break; case 0x02: /* BCzFL */ invalid_instruction(op); break; case 0x03: /* BCzTL */ invalid_instruction(op); break; default: invalid_instruction(op); break; @@ -1980,25 +2255,31 @@ void mips3_device::handle_cop1_fr0(uint32_t op) break; case 0x03: - if (IS_SINGLE(op)) { /* DIV.S */ - if (FTVALW_FR0 == 0 && (COP1_FCR31 & (1 << (FCR31_ENABLE + FPE_DIV0)))) { + if (IS_SINGLE(op)) /* DIV.S */ + { + if (FTVALW_FR0 == 0 && (COP1_FCR31 & (1 << (FCR31_ENABLE + FPE_DIV0)))) + { COP1_FCR31 |= (1 << (FCR31_FLAGS + FPE_DIV0)); // Set flag COP1_FCR31 |= (1 << (FCR31_CAUSE + FPE_DIV0)); // Set cause generate_exception(EXCEPTION_FPE, 1); //machine().debug_break(); } - else { + else + { FDVALS_FR0 = FSVALS_FR0 / FTVALS_FR0; } } - else { /* DIV.D */ - if (FTVALL_FR0 == 0ull && (COP1_FCR31 & (1 << (FCR31_ENABLE + FPE_DIV0)))) { + else /* DIV.D */ + { + if (FTVALL_FR0 == 0ull && (COP1_FCR31 & (1 << (FCR31_ENABLE + FPE_DIV0)))) + { COP1_FCR31 |= (1 << (FCR31_FLAGS + FPE_DIV0)); // Set flag COP1_FCR31 |= (1 << (FCR31_CAUSE + FPE_DIV0)); // Set cause generate_exception(EXCEPTION_FPE, 1); //machine().debug_break(); } - else { + else + { FDVALD_FR0 = FSVALD_FR0 / FTVALD_FR0; } } @@ -2866,7 +3147,7 @@ inline void mips3_device::set_cop2_creg(int idx, uint64_t val) m_core->vfr[idx][0] = val; } -inline void r5900le_device::handle_dmfc2(uint32_t op) +inline void r5900_device::handle_dmfc2(uint32_t op) { // QMFC2 if (!RTREG) @@ -2884,7 +3165,7 @@ inline void r5900le_device::handle_dmfc2(uint32_t op) m_core->rh[rt] = ((uint64_t)rtval[3] << 32) | rtval[2]; } -inline void r5900le_device::handle_dmtc2(uint32_t op) +inline void r5900_device::handle_dmtc2(uint32_t op) { // QMTC2 uint32_t rt = RTREG; @@ -2896,23 +3177,23 @@ inline void r5900le_device::handle_dmtc2(uint32_t op) } } -inline uint64_t r5900le_device::get_cop2_reg(int idx) +inline uint64_t r5900_device::get_cop2_reg(int idx) { return reinterpret_cast<uint32_t*>(m_core->vfr[idx])[0]; } -inline void r5900le_device::set_cop2_reg(int idx, uint64_t val) +inline void r5900_device::set_cop2_reg(int idx, uint64_t val) { reinterpret_cast<uint32_t*>(m_core->vfr[idx])[0] = (uint32_t)val; } -inline uint64_t r5900le_device::get_cop2_creg(int idx) +inline uint64_t r5900_device::get_cop2_creg(int idx) { logerror("%s: CFC2: Getting ccr[%d] (%08x)\n", machine().describe_context(), idx, m_core->vcr[idx]); return m_core->vcr[idx]; } -inline void r5900le_device::set_cop2_creg(int idx, uint64_t val) +inline void r5900_device::set_cop2_creg(int idx, uint64_t val) { if (idx < 16) { @@ -3003,8 +3284,8 @@ void mips3_device::handle_cop2(uint32_t op) case 0x08: /* BC */ switch (RTREG) { - case 0x00: /* BCzF */ if (!m_cf[2]) ADDPC(SIMMVAL); break; - case 0x01: /* BCzT */ if (m_cf[2]) ADDPC(SIMMVAL); break; + case 0x00: /* BCzF */ if (!m_cf[2][0]) ADDPC(SIMMVAL); break; + case 0x01: /* BCzT */ if (m_cf[2][0]) ADDPC(SIMMVAL); break; case 0x02: /* BCzFL */ invalid_instruction(op); break; case 0x03: /* BCzTL */ invalid_instruction(op); break; default: invalid_instruction(op); break; @@ -3024,7 +3305,7 @@ void mips3_device::handle_extra_cop2(uint32_t op) VU0/1 (COP2) EXECUTION HANDLING (R5900) ***************************************************************************/ -void r5900le_device::handle_extra_cop2(uint32_t op) +void r5900_device::handle_extra_cop2(uint32_t op) { // TODO: Flags, rounding... const int rd = (op >> 6) & 31; @@ -3481,13 +3762,13 @@ void mips3_device::handle_regimm(uint32_t op) void mips3_device::handle_mult(uint32_t op) { - uint64_t temp64 = (int64_t)(int32_t)RSVAL32 * (int64_t)(int32_t)RTVAL32; + uint64_t temp64 = mul_32x32(RSVAL32, RTVAL32); LOVAL64 = (int32_t)temp64; HIVAL64 = (int32_t)(temp64 >> 32); m_core->icount -= 3; } -void r5900le_device::handle_mult(uint32_t op) +void r5900_device::handle_mult(uint32_t op) { mips3_device::handle_mult(op); if (RDREG) RDVAL64 = LOVAL64; @@ -3495,13 +3776,13 @@ void r5900le_device::handle_mult(uint32_t op) void mips3_device::handle_multu(uint32_t op) { - uint64_t temp64 = (uint64_t)RSVAL32 * (uint64_t)RTVAL32; + uint64_t temp64 = mulu_32x32(RSVAL32, RTVAL32); LOVAL64 = (int32_t)temp64; HIVAL64 = (int32_t)(temp64 >> 32); m_core->icount -= 3; } -void r5900le_device::handle_multu(uint32_t op) +void r5900_device::handle_multu(uint32_t op) { mips3_device::handle_multu(op); if (RDREG) RDVAL64 = LOVAL64; @@ -3551,11 +3832,11 @@ void mips3_device::handle_special(uint32_t op) m_core->icount -= 35; break; case 0x1c: /* DMULT */ - LOVAL64 = mul_64x64(RSVAL64, RTVAL64, reinterpret_cast<s64 *>(&HIVAL64)); + LOVAL64 = mul_64x64(RSVAL64, RTVAL64, *reinterpret_cast<s64 *>(&HIVAL64)); m_core->icount -= 7; break; case 0x1d: /* DMULTU */ - LOVAL64 = mulu_64x64(RSVAL64, RTVAL64, &HIVAL64); + LOVAL64 = mulu_64x64(RSVAL64, RTVAL64, HIVAL64); m_core->icount -= 7; break; case 0x1e: /* DDIV */ @@ -3631,8 +3912,28 @@ void mips3_device::handle_idt(uint32_t op) { switch (op & 0x1f) { + case 0: /* MAD */ + if (RSREG != 0 && RTREG != 0) + { + int64_t temp64 = mul_32x32(RSVAL32, RTVAL32); + temp64 += ((int64_t)m_core->r[REG_HI] << 32) | m_core->r[REG_LO]; + m_core->r[REG_LO] = (int32_t)temp64; + m_core->r[REG_HI] = (int32_t)(temp64 >> 32); + } + m_core->icount -= 3; + break; + case 1: /* MADU */ + if (RSREG != 0 && RTREG != 0) + { + uint64_t temp64 = mulu_32x32(RSVAL32, RTVAL32); + temp64 += ((uint64_t)m_core->r[REG_HI] << 32) | m_core->r[REG_LO]; + m_core->r[REG_LO] = (uint32_t)temp64; + m_core->r[REG_HI] = (uint32_t)(temp64 >> 32); + } + m_core->icount -= 3; + break; case 2: /* MUL */ - RDVAL64 = (int32_t)((int32_t)RSVAL32 * (int32_t)RTVAL32); + if (RDREG) RDVAL64 = (int32_t)((int32_t)RSVAL32 * (int32_t)RTVAL32); m_core->icount -= 3; break; default: @@ -3641,7 +3942,7 @@ void mips3_device::handle_idt(uint32_t op) } } -void r5900le_device::handle_extra_base(uint32_t op) +void r5900_device::handle_extra_base(uint32_t op) { const int rs = (op >> 21) & 31; const int rt = (op >> 16) & 31; @@ -3670,7 +3971,7 @@ void r5900le_device::handle_extra_base(uint32_t op) } } -void r5900le_device::handle_extra_special(uint32_t op) +void r5900_device::handle_extra_special(uint32_t op) { const int rs = (op >> 21) & 31; const int rd = (op >> 11) & 31; @@ -3689,7 +3990,7 @@ void r5900le_device::handle_extra_special(uint32_t op) } } -void r5900le_device::handle_extra_regimm(uint32_t op) +void r5900_device::handle_extra_regimm(uint32_t op) { switch (op & 63) { @@ -3705,7 +4006,7 @@ void r5900le_device::handle_extra_regimm(uint32_t op) } } -void r5900le_device::handle_extra_cop0(uint32_t op) +void r5900_device::handle_extra_cop0(uint32_t op) { switch (op & 0x01ffffff) { @@ -3723,7 +4024,7 @@ void r5900le_device::handle_extra_cop0(uint32_t op) } } -void r5900le_device::handle_extra_cop1(uint32_t op) +void r5900_device::handle_extra_cop1(uint32_t op) { switch (op & 0x3f) { @@ -3738,7 +4039,7 @@ void r5900le_device::handle_extra_cop1(uint32_t op) } } -void r5900le_device::handle_idt(uint32_t op) +void r5900_device::handle_idt(uint32_t op) { const int rs = (op >> 21) & 31; const int rt = (op >> 16) & 31; @@ -3749,7 +4050,7 @@ void r5900le_device::handle_idt(uint32_t op) { case 0x00: /* MADD */ { - uint64_t temp64 = (int64_t)(int32_t)RSVAL32 * (int64_t)(int32_t)RTVAL32; + uint64_t temp64 = mul_32x32(RSVAL32, RTVAL32); m_core->r[REG_LO] += (int32_t)temp64; m_core->r[REG_HI] += (int32_t)(temp64 >> 32); if (rd) @@ -3803,7 +4104,7 @@ void r5900le_device::handle_idt(uint32_t op) break; case 0x18: /* MULT1 */ { - uint64_t temp64 = (int64_t)(int32_t)RSVAL32 * (int64_t)(int32_t)RTVAL32; + uint64_t temp64 = mul_32x32(RSVAL32, RTVAL32); m_core->rh[REG_LO] = (int32_t)temp64; m_core->rh[REG_HI] = (int32_t)(temp64 >> 32); if (rd) @@ -3962,7 +4263,7 @@ void r5900le_device::handle_idt(uint32_t op) } } -void r5900le_device::handle_mmi0(uint32_t op) +void r5900_device::handle_mmi0(uint32_t op) { const int rs = (op >> 21) & 31; const int rt = (op >> 16) & 31; @@ -4421,7 +4722,7 @@ void r5900le_device::handle_mmi0(uint32_t op) } } -void r5900le_device::handle_mmi1(uint32_t op) +void r5900_device::handle_mmi1(uint32_t op) { const int rs = (op >> 21) & 31; const int rt = (op >> 16) & 31; @@ -4785,7 +5086,7 @@ void r5900le_device::handle_mmi1(uint32_t op) } } -void r5900le_device::handle_mmi2(uint32_t op) +void r5900_device::handle_mmi2(uint32_t op) { const int rs = (op >> 21) & 31; const int rt = (op >> 16) & 31; @@ -4912,7 +5213,7 @@ void r5900le_device::handle_mmi2(uint32_t op) } } -void r5900le_device::handle_mmi3(uint32_t op) +void r5900_device::handle_mmi3(uint32_t op) { const int rs = (op >> 21) & 31; const int rt = (op >> 16) & 31; @@ -5011,7 +5312,7 @@ void mips3_device::handle_sdc2(uint32_t op) WDOUBLE(SIMMVAL+RSVAL32, get_cop2_reg(RTREG)); } -void r5900le_device::handle_ldc2(uint32_t op) +void r5900_device::handle_ldc2(uint32_t op) { /* LQC2 */ const uint32_t base = SIMMVAL + RSVAL32; @@ -5023,7 +5324,7 @@ void r5900le_device::handle_ldc2(uint32_t op) } } -void r5900le_device::handle_sdc2(uint32_t op) +void r5900_device::handle_sdc2(uint32_t op) { /* SQC2 */ const uint32_t base = SIMMVAL + RSVAL32; @@ -5034,15 +5335,6 @@ void r5900le_device::handle_sdc2(uint32_t op) } } -void mips3_device::burn_cycles(int32_t cycles) -{ - execute_burn(cycles); -} - -#if ENABLE_O2_DPRINTF -#include "o2dprintf.hxx" -#endif - void mips3_device::execute_run() { if (m_isdrc) @@ -5200,12 +5492,12 @@ void mips3_device::execute_run() { // Should actually use physical address m_core->cpr[0][COP0_LLAddr] = SIMMVAL + RSVAL32; - RTVAL64 = temp; + RTVAL64 = int64_t(int32_t(temp)); m_core->llbit = 1; if LL_BREAK machine().debug_break(); - break; } + break; case 0x31: /* LWC1 */ if (!(SR & SR_COP1)) { @@ -5226,18 +5518,18 @@ void mips3_device::execute_run() m_core->llbit = 1; if LL_BREAK machine().debug_break(); - break; } + break; case 0x35: /* LDC1 */ - if (!(SR & SR_COP1)) - { - m_badcop_value = 1; - generate_exception(EXCEPTION_BADCOP, 1); + if (!(SR & SR_COP1)) + { + m_badcop_value = 1; + generate_exception(EXCEPTION_BADCOP, 1); + break; + } + if (RDOUBLE(SIMMVAL+RSVAL32, &temp64)) + set_cop1_reg64(RTREG, temp64); break; - } - if (RDOUBLE(SIMMVAL+RSVAL32, &temp64)) - set_cop1_reg64(RTREG, temp64); - break; case 0x36: handle_ldc2(op); break; case 0x37: /* LD */ if (RDOUBLE(SIMMVAL+RSVAL32, &temp64) && RTREG) RTVAL64 = temp64; break; case 0x38: /* SC */ @@ -5293,12 +5585,10 @@ void mips3_device::execute_run() m_delayslot = false; m_core->icount--; -#if ENABLE_O2_DPRINTF - if (m_core->pc == 0xbfc04d74) + if (ENABLE_O2_DPRINTF && m_core->pc == 0xbfc04d74) { do_o2_dprintf((uint32_t)m_core->r[4], (uint32_t)m_core->r[5], (uint32_t)m_core->r[6], (uint32_t)m_core->r[7], (uint32_t)m_core->r[29] + 16); } -#endif #if ENABLE_EE_ELF_LOADER static bool elf_loaded = false; diff --git a/src/devices/cpu/mips/mips3.h b/src/devices/cpu/mips/mips3.h index 147e9213620..73105cb0ad3 100644 --- a/src/devices/cpu/mips/mips3.h +++ b/src/devices/cpu/mips/mips3.h @@ -20,8 +20,6 @@ MIPS III/IV emulator. #include "cpu/drcuml.h" #include "ps2vu.h" -#define ENABLE_O2_DPRINTF (0) - DECLARE_DEVICE_TYPE(R4000BE, r4000be_device) DECLARE_DEVICE_TYPE(R4000LE, r4000le_device) DECLARE_DEVICE_TYPE(R4400BE, r4400be_device) @@ -49,6 +47,7 @@ DECLARE_DEVICE_TYPE(QED5271LE, qed5271le_device) DECLARE_DEVICE_TYPE(RM7000BE, rm7000be_device) DECLARE_DEVICE_TYPE(RM7000LE, rm7000le_device) DECLARE_DEVICE_TYPE(R5900LE, r5900le_device) +DECLARE_DEVICE_TYPE(R5900BE, r5900be_device) /*************************************************************************** @@ -309,29 +308,28 @@ public: void add_fastram(offs_t start, offs_t end, uint8_t readonly, void *base); void clear_fastram(uint32_t select_start); void mips3drc_set_options(uint32_t options); + uint32_t mips3drc_get_options(); void mips3drc_add_hotspot(offs_t pc, uint32_t opcode, uint32_t cycles); - void burn_cycles(int32_t cycles); protected: // device-level overrides - virtual void device_start() override; - virtual void device_reset() override; - virtual void device_stop() override; + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; + virtual void device_stop() override ATTR_COLD; // device_execute_interface overrides - virtual uint32_t execute_min_cycles() const override { return 1; } - virtual uint32_t execute_max_cycles() const override { return 40; } - virtual uint32_t execute_input_lines() const override { return 6; } + virtual uint32_t execute_min_cycles() const noexcept override { return 1; } + virtual uint32_t execute_max_cycles() const noexcept override { return 40; } virtual void execute_run() override; virtual void execute_set_input(int inputnum, int state) override; - virtual void execute_burn(int32_t cycles) override { m_totalcycles += cycles; } // device_memory_interface overrides virtual space_config_vector memory_space_config() const override; - virtual bool memory_translate(int spacenum, int intention, offs_t &address) override; + virtual bool memory_translate(int spacenum, int intention, offs_t &address, address_space *&target_space) override; // device_state_interface overrides virtual void state_export(const device_state_entry &entry) override; + virtual void state_import(const device_state_entry &entry) override; virtual void state_string_export(const device_state_entry &entry, std::string &str) const override; // device_disasm_interface overrides @@ -352,6 +350,8 @@ protected: virtual void WDOUBLE(offs_t address, uint64_t data); virtual void WDOUBLE_MASKED(offs_t address, uint64_t data, uint64_t mem_mask); + virtual void set_cop0_reg(int idx, uint64_t val); + struct internal_mips3_state { /* core registers */ uint32_t pc; @@ -400,6 +400,11 @@ protected: uint8_t *m_icache; address_space_config m_program_config; + memory_access<32, 2, 0, ENDIANNESS_LITTLE>::cache m_cache32le; + memory_access<32, 3, 0, ENDIANNESS_LITTLE>::cache m_cache64le; + memory_access<32, 2, 0, ENDIANNESS_BIG>::cache m_cache32be; + memory_access<32, 3, 0, ENDIANNESS_BIG>::cache m_cache64be; + mips3_flavor m_flavor; /* internal stuff */ @@ -427,7 +432,7 @@ protected: address_space * m_program; uint32_t m_data_bits; - std::function<u32(offs_t)> m_pr32; + delegate<u32 (offs_t)> m_pr32; std::function<const void * (offs_t)> m_prptr; uint32_t c_system_clock; uint32_t m_cpu_clock; @@ -443,7 +448,6 @@ protected: uint32_t m_byte_xor; uint32_t m_word_xor; uint32_t m_dword_xor; - data_accessors m_memory; /* cache memory */ size_t c_icache_size; @@ -514,6 +518,12 @@ protected: void generate_exception(int exception, int backup); void generate_tlb_exception(int exception, offs_t address); + + void static_generate_memory_mode_checks(drcuml_block &block, uml::code_handle &exception_addrerr, int &label, int mode); + void static_generate_fastram_accessor(drcuml_block &block, int &label, int size, bool iswrite, bool ismasked); + void static_generate_memory_rw(drcuml_block &block, int size, bool iswrite, bool ismasked); + virtual void static_generate_memory_accessor(drcuml_block &block, int &label, int mode, int size, bool iswrite, bool ismasked, const char *name, uml::code_handle *&handleptr); + virtual void check_irqs(); virtual void handle_mult(uint32_t op); virtual void handle_multu(uint32_t op); @@ -528,13 +538,13 @@ public: private: uint32_t compute_config_register(); uint32_t compute_prid_register(); + uint32_t compute_fpu_prid_register(); uint32_t generate_tlb_index(); void tlb_map_entry(int tlbindex); void tlb_write_common(int tlbindex); uint64_t get_cop0_reg(int idx); - void set_cop0_reg(int idx, uint64_t val); uint64_t get_cop0_creg(int idx); void set_cop0_creg(int idx, uint64_t val); void handle_cop0(uint32_t op); @@ -610,16 +620,15 @@ private: uml::code_label labelnum; /* index for local labels */ }; - void static_generate_entry_point(); - void static_generate_nocode_handler(); - void static_generate_out_of_cycles(); - void static_generate_tlb_mismatch(); - void static_generate_exception(uint8_t exception, int recover, const char *name); - void static_generate_memory_accessor(int mode, int size, int iswrite, int ismasked, const char *name, uml::code_handle *&handleptr); + void static_generate_entry_point(drcuml_block &block, int &label); + void static_generate_nocode_handler(drcuml_block &block, int &label); + void static_generate_out_of_cycles(drcuml_block &block, int &label); + void static_generate_tlb_mismatch(drcuml_block &block, int &label); + void static_generate_exception(drcuml_block &block, int &label, uint8_t exception, int recover, const char *name); void generate_update_mode(drcuml_block &block); void generate_update_cycles(drcuml_block &block, compiler_state &compiler, uml::parameter param, bool allow_exception); - void generate_checksum_block(drcuml_block &block, compiler_state &compiler, const opcode_desc *seqhead, const opcode_desc *seqlast); + void generate_checksum_block(drcuml_block &block, compiler_state &compiler, const opcode_desc *seqhead, const opcode_desc *seqlast, const opcode_desc *codelast); void generate_sequence_instruction(drcuml_block &block, compiler_state &compiler, const opcode_desc *desc); void generate_delay_slot_and_branch(drcuml_block &block, compiler_state &compiler, const opcode_desc *desc, uint8_t linkreg); @@ -644,9 +653,7 @@ private: void log_opcode_desc(const opcode_desc *desclist, int indent); void load_elf(); -#if ENABLE_O2_DPRINTF - void do_o2_dprintf(uint32_t fmt_addr, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t stack); -#endif + [[maybe_unused]] void do_o2_dprintf(uint32_t fmt_addr, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t stack); }; @@ -740,20 +747,49 @@ public: } }; -class r4650be_device : public mips3_device { +class r4650_device : public mips3_device { +public: + // construction/destruction + r4650_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, endianness_t endianness) + : mips3_device(mconfig, type, tag, owner, clock, MIPS3_TYPE_R4650, endianness, 32) // Should be 64 bits + { + } + +protected: + virtual bool memory_translate(int spacenum, int intention, offs_t &address, address_space *&target_space) override; + + virtual void static_generate_memory_accessor(drcuml_block &block, int &label, int mode, int size, bool iswrite, bool ismasked, const char *name, uml::code_handle *&handleptr) override; + + virtual bool RBYTE(offs_t address, uint32_t *result) override; + virtual bool RHALF(offs_t address, uint32_t *result) override; + virtual bool RWORD(offs_t address, uint32_t *result, bool insn = false) override; + virtual bool RWORD_MASKED(offs_t address, uint32_t *result, uint32_t mem_mask) override; + virtual bool RDOUBLE(offs_t address, uint64_t *result) override; + virtual bool RDOUBLE_MASKED(offs_t address, uint64_t *result, uint64_t mem_mask) override; + virtual void WBYTE(offs_t address, uint8_t data) override; + virtual void WHALF(offs_t address, uint16_t data) override; + virtual void WWORD(offs_t address, uint32_t data) override; + virtual void WWORD_MASKED(offs_t address, uint32_t data, uint32_t mem_mask) override; + virtual void WDOUBLE(offs_t address, uint64_t data) override; + virtual void WDOUBLE_MASKED(offs_t address, uint64_t data, uint64_t mem_mask) override; + + virtual void set_cop0_reg(int idx, uint64_t val) override; +}; + +class r4650be_device : public r4650_device { public: // construction/destruction r4650be_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : mips3_device(mconfig, R4650BE, tag, owner, clock, MIPS3_TYPE_R4650, ENDIANNESS_BIG, 32) // Should be 64 bits + : r4650_device(mconfig, R4650BE, tag, owner, clock, ENDIANNESS_BIG) { } }; -class r4650le_device : public mips3_device { +class r4650le_device : public r4650_device { public: // construction/destruction r4650le_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : mips3_device(mconfig, R4650LE, tag, owner, clock, MIPS3_TYPE_R4650, ENDIANNESS_LITTLE, 32) // Should be 64 bits + : r4650_device(mconfig, R4650LE, tag, owner, clock, ENDIANNESS_LITTLE) { } }; @@ -833,24 +869,16 @@ public: } }; -class r5900le_device : public mips3_device { -public: +class r5900_device : public mips3_device { +protected: // construction/destruction - template <typename T> - r5900le_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, T &&vu0_tag) - : r5900le_device(mconfig, tag, owner, clock) - { - m_vu0.set_tag(std::forward<T>(vu0_tag)); - } - - r5900le_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : mips3_device(mconfig, R5900LE, tag, owner, clock, MIPS3_TYPE_R5900, ENDIANNESS_LITTLE, 64) + r5900_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, endianness_t endianness) + : mips3_device(mconfig, type, tag, owner, clock, MIPS3_TYPE_R5900, endianness, 64) , m_vu0(*this, finder_base::DUMMY_TAG) { } -protected: - virtual void device_start() override; + virtual void device_start() override ATTR_COLD; // device_disasm_interface overrides virtual std::unique_ptr<util::disasm_interface> create_disassembler() override; @@ -868,8 +896,8 @@ protected: void WDOUBLE(offs_t address, uint64_t data) override; void WDOUBLE_MASKED(offs_t address, uint64_t data, uint64_t mem_mask) override; - bool RQUAD(offs_t address, uint64_t *result_hi, uint64_t *result_lo); - void WQUAD(offs_t address, uint64_t data_hi, uint64_t data_lo); + virtual bool RQUAD(offs_t address, uint64_t *result_hi, uint64_t *result_lo) = 0; + virtual void WQUAD(offs_t address, uint64_t data_hi, uint64_t data_lo) = 0; uint64_t get_cop2_reg(int idx) override; void set_cop2_reg(int idx, uint64_t val) override; @@ -899,6 +927,46 @@ protected: required_device<sonyvu0_device> m_vu0; }; +class r5900le_device : public r5900_device { +public: + // construction/destruction + template <typename T> + r5900le_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, T &&vu0_tag) + : r5900le_device(mconfig, tag, owner, clock) + { + m_vu0.set_tag(std::forward<T>(vu0_tag)); + } + + r5900le_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : r5900_device(mconfig, R5900LE, tag, owner, clock, ENDIANNESS_LITTLE) + { + } + +protected: + bool RQUAD(offs_t address, uint64_t *result_hi, uint64_t *result_lo) override; + void WQUAD(offs_t address, uint64_t data_hi, uint64_t data_lo) override; +}; + +class r5900be_device : public r5900_device { +public: + // construction/destruction + template <typename T> + r5900be_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, T &&vu0_tag) + : r5900be_device(mconfig, tag, owner, clock) + { + m_vu0.set_tag(std::forward<T>(vu0_tag)); + } + + r5900be_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : r5900_device(mconfig, R5900BE, tag, owner, clock, ENDIANNESS_BIG) + { + } + +protected: + bool RQUAD(offs_t address, uint64_t *result_hi, uint64_t *result_lo) override; + void WQUAD(offs_t address, uint64_t data_hi, uint64_t data_lo) override; +}; + class qed5271be_device : public mips3_device { public: // construction/destruction @@ -965,17 +1033,16 @@ private: COMPILER-SPECIFIC OPTIONS ***************************************************************************/ -/* fix me -- how do we make this work?? */ #define MIPS3DRC_STRICT_VERIFY 0x0001 /* verify all instructions */ #define MIPS3DRC_STRICT_COP0 0x0002 /* validate all COP0 instructions */ #define MIPS3DRC_STRICT_COP1 0x0004 /* validate all COP1 instructions */ #define MIPS3DRC_STRICT_COP2 0x0008 /* validate all COP2 instructions */ -#define MIPS3DRC_FLUSH_PC 0x0010 /* flush the PC value before each memory access */ +#define MIPS3DRC_DISABLE_INTRABLOCK 0x0010 /* disable intrablock branching */ #define MIPS3DRC_CHECK_OVERFLOWS 0x0020 /* actually check overflows on add/sub instructions */ #define MIPS3DRC_ACCURATE_DIVZERO 0x0040 /* load correct values into HI/LO on integer divide-by-zero */ +#define MIPS3DRC_EXTRA_INSTR_CHECK 0x0080 /* adds the last instruction value to all validation entry locations, used with STRICT_VERIFY */ -#define MIPS3DRC_COMPATIBLE_OPTIONS (MIPS3DRC_STRICT_VERIFY | MIPS3DRC_STRICT_COP1 | MIPS3DRC_STRICT_COP0 | MIPS3DRC_STRICT_COP2 | MIPS3DRC_FLUSH_PC) +#define MIPS3DRC_COMPATIBLE_OPTIONS (MIPS3DRC_STRICT_VERIFY | MIPS3DRC_STRICT_COP1 | MIPS3DRC_STRICT_COP0 | MIPS3DRC_STRICT_COP2) #define MIPS3DRC_FASTEST_OPTIONS (0) - #endif // MAME_CPU_MIPS_MIPS3_H diff --git a/src/devices/cpu/mips/mips3com.cpp b/src/devices/cpu/mips/mips3com.cpp index f671ba2c335..2a9a933ef87 100644 --- a/src/devices/cpu/mips/mips3com.cpp +++ b/src/devices/cpu/mips/mips3com.cpp @@ -143,7 +143,7 @@ generate_tlb_index - generate a random tlb index uint32_t mips3_device::generate_tlb_index() { // Actual hardware uses a free running counter to generate the index. - // This impementation uses a linear congruential generator so that DRC and non-DRC code sequences match. + // This implementation uses a linear congruential generator so that DRC and non-DRC code sequences match. m_tlb_seed = 214013 * m_tlb_seed + 2531011; return (m_tlb_seed >> 16) & 0x3f; } @@ -336,8 +336,10 @@ uint32_t mips3_device::compute_prid_register() return 0x5500; case MIPS3_TYPE_R4600: + return 0x2020; + case MIPS3_TYPE_R4650: - return 0x2000; + return 0x2200; case MIPS3_TYPE_R4700: return 0x2100; @@ -364,6 +366,12 @@ uint32_t mips3_device::compute_prid_register() //return 0x2000; } +uint32_t mips3_device::compute_fpu_prid_register() +{ + /* For most CPUs they are same */ + return compute_prid_register(); +} + /*------------------------------------------------- tlb_map_entry - map a single TLB entry @@ -414,15 +422,15 @@ void mips3_device::tlb_map_entry(int tlbindex) /* valid? */ if ((lo & 2) != 0) { - flags |= VTLB_FLAG_VALID | VTLB_READ_ALLOWED | VTLB_FETCH_ALLOWED; + flags |= FLAG_VALID | READ_ALLOWED | FETCH_ALLOWED; /* writable? */ if ((lo & 4) != 0) - flags |= VTLB_WRITE_ALLOWED; + flags |= WRITE_ALLOWED; /* mirror the flags for user mode if the VPN is in user space */ if (effvpn < (0x80000000 >> MIPS3_MIN_PAGE_SHIFT)) - flags |= (flags << 4) & (VTLB_USER_READ_ALLOWED | VTLB_USER_WRITE_ALLOWED | VTLB_USER_FETCH_ALLOWED); + flags |= (flags << 4) & (USER_READ_ALLOWED | USER_WRITE_ALLOWED | USER_FETCH_ALLOWED); } /* load the virtual TLB with the corresponding entries */ diff --git a/src/devices/cpu/mips/mips3com.h b/src/devices/cpu/mips/mips3com.h index 343e161ee68..8976f42575f 100644 --- a/src/devices/cpu/mips/mips3com.h +++ b/src/devices/cpu/mips/mips3com.h @@ -107,6 +107,10 @@ #define COP0_TagLo 28 #define COP0_TagHi 29 #define COP0_ErrorPC 30 +#define COP0_R4650_IBase 0 +#define COP0_R4650_IBound 1 +#define COP0_R4650_DBase 2 +#define COP0_R4650_DBound 3 /* Status register bits */ #define SR_IE 0x00000001 diff --git a/src/devices/cpu/mips/mips3drc.cpp b/src/devices/cpu/mips/mips3drc.cpp index 0f1cd0a29d4..f682da6b212 100644 --- a/src/devices/cpu/mips/mips3drc.cpp +++ b/src/devices/cpu/mips/mips3drc.cpp @@ -24,7 +24,6 @@ ***************************************************************************/ #include "emu.h" -#include "debugger.h" #include "mips3com.h" #include "mips3fe.h" #include "mips3dsm.h" @@ -34,6 +33,11 @@ #include "cpu/drcumlsh.h" +/* Use with STRICT_VERIFY to print debug info to console for extra validation checks */ +/* Set to 1 to activate and use MIPS3DRC_STRICT_VERIFY in the drc options */ +#define DEBUG_STRICT_VERIFY 0 + + /*************************************************************************** MACROS ***************************************************************************/ @@ -120,7 +124,7 @@ inline void mips3_device::load_fast_iregs(drcuml_block &block) { int regnum; - for (regnum = 0; regnum < ARRAY_LENGTH(m_regmap); regnum++) + for (regnum = 0; regnum < std::size(m_regmap); regnum++) if (m_regmap[regnum].is_int_register()) UML_DMOV(block, ireg(m_regmap[regnum].ireg() - REG_I0), mem(&m_core->r[regnum])); } @@ -135,7 +139,7 @@ inline void mips3_device::save_fast_iregs(drcuml_block &block) { int regnum; - for (regnum = 0; regnum < ARRAY_LENGTH(m_regmap); regnum++) + for (regnum = 0; regnum < std::size(m_regmap); regnum++) if (m_regmap[regnum].is_int_register()) UML_DMOV(block, mem(&m_core->r[regnum]), ireg(m_regmap[regnum].ireg() - REG_I0)); } @@ -157,6 +161,15 @@ void mips3_device::mips3drc_set_options(uint32_t options) } /*------------------------------------------------- + mips3drc_get_options - return current DRC options +-------------------------------------------------*/ + +uint32_t mips3_device::mips3drc_get_options() +{ + return m_drcoptions; +} + +/*------------------------------------------------- mips3drc_clears_fastram - clears fastram region starting at index select_start -------------------------------------------------*/ @@ -174,7 +187,7 @@ void mips3_device::clear_fastram(uint32_t select_start) void mips3_device::add_fastram(offs_t start, offs_t end, uint8_t readonly, void *base) { - if (m_fastram_select < ARRAY_LENGTH(m_fastram)) + if (m_fastram_select < std::size(m_fastram)) { m_fastram[m_fastram_select].start = start; m_fastram[m_fastram_select].end = end; @@ -197,7 +210,7 @@ void mips3_device::add_fastram(offs_t start, offs_t end, uint8_t readonly, void void mips3_device::mips3drc_add_hotspot(offs_t pc, uint32_t opcode, uint32_t cycles) { if (!allow_drc()) return; - if (m_hotspot_select < ARRAY_LENGTH(m_hotspot)) + if (m_hotspot_select < std::size(m_hotspot)) { m_hotspot[m_hotspot_select].pc = pc; m_hotspot[m_hotspot_select].opcode = opcode; @@ -226,45 +239,55 @@ void mips3_device::code_flush_cache() try { - /* generate the entry point and out-of-cycles handlers */ - static_generate_entry_point(); - static_generate_nocode_handler(); - static_generate_out_of_cycles(); - static_generate_tlb_mismatch(); - - /* append exception handlers for various types */ - static_generate_exception(EXCEPTION_INTERRUPT, true, "exception_interrupt"); - static_generate_exception(EXCEPTION_INTERRUPT, false, "exception_interrupt_norecover"); - static_generate_exception(EXCEPTION_TLBMOD, true, "exception_tlbmod"); - static_generate_exception(EXCEPTION_TLBLOAD, true, "exception_tlbload"); - static_generate_exception(EXCEPTION_TLBSTORE, true, "exception_tlbstore"); - static_generate_exception(EXCEPTION_TLBLOAD_FILL, true, "exception_tlbload_fill"); - static_generate_exception(EXCEPTION_TLBSTORE_FILL, true, "exception_tlbstore_fill"); - static_generate_exception(EXCEPTION_ADDRLOAD, true, "exception_addrload"); - static_generate_exception(EXCEPTION_ADDRSTORE, true, "exception_addrstore"); - static_generate_exception(EXCEPTION_SYSCALL, true, "exception_syscall"); - static_generate_exception(EXCEPTION_BREAK, true, "exception_break"); - static_generate_exception(EXCEPTION_INVALIDOP, true, "exception_invalidop"); - static_generate_exception(EXCEPTION_BADCOP, true, "exception_badcop"); - static_generate_exception(EXCEPTION_OVERFLOW, true, "exception_overflow"); - static_generate_exception(EXCEPTION_TRAP, true, "exception_trap"); - static_generate_exception(EXCEPTION_FPE, true, "exception_fpe"); + { + drcuml_block &block(m_drcuml->begin_block(1024)); + int label = 1; + + /* generate the entry point and out-of-cycles handlers */ + static_generate_entry_point(block, label); + static_generate_nocode_handler(block, label); + static_generate_out_of_cycles(block, label); + static_generate_tlb_mismatch(block, label); + + /* append exception handlers for various types */ + static_generate_exception(block, label, EXCEPTION_INTERRUPT, true, "exception_interrupt"); + static_generate_exception(block, label, EXCEPTION_INTERRUPT, false, "exception_interrupt_norecover"); + static_generate_exception(block, label, EXCEPTION_TLBMOD, true, "exception_tlbmod"); + static_generate_exception(block, label, EXCEPTION_TLBLOAD, true, "exception_tlbload"); + static_generate_exception(block, label, EXCEPTION_TLBSTORE, true, "exception_tlbstore"); + static_generate_exception(block, label, EXCEPTION_TLBLOAD_FILL, true, "exception_tlbload_fill"); + static_generate_exception(block, label, EXCEPTION_TLBSTORE_FILL, true, "exception_tlbstore_fill"); + static_generate_exception(block, label, EXCEPTION_ADDRLOAD, true, "exception_addrload"); + static_generate_exception(block, label, EXCEPTION_ADDRSTORE, true, "exception_addrstore"); + static_generate_exception(block, label, EXCEPTION_SYSCALL, true, "exception_syscall"); + static_generate_exception(block, label, EXCEPTION_BREAK, true, "exception_break"); + static_generate_exception(block, label, EXCEPTION_INVALIDOP, true, "exception_invalidop"); + static_generate_exception(block, label, EXCEPTION_BADCOP, true, "exception_badcop"); + static_generate_exception(block, label, EXCEPTION_OVERFLOW, true, "exception_overflow"); + static_generate_exception(block, label, EXCEPTION_TRAP, true, "exception_trap"); + static_generate_exception(block, label, EXCEPTION_FPE, true, "exception_fpe"); + + block.end(); + } /* add subroutines for memory accesses */ for (mode = 0; mode < 3; mode++) { - static_generate_memory_accessor(mode, 1, false, false, "read8", m_read8[mode]); - static_generate_memory_accessor(mode, 1, true, false, "write8", m_write8[mode]); - static_generate_memory_accessor(mode, 2, false, false, "read16", m_read16[mode]); - static_generate_memory_accessor(mode, 2, true, false, "write16", m_write16[mode]); - static_generate_memory_accessor(mode, 4, false, false, "read32", m_read32[mode]); - static_generate_memory_accessor(mode, 4, false, true, "read32mask", m_read32mask[mode]); - static_generate_memory_accessor(mode, 4, true, false, "write32", m_write32[mode]); - static_generate_memory_accessor(mode, 4, true, true, "write32mask", m_write32mask[mode]); - static_generate_memory_accessor(mode, 8, false, false, "read64", m_read64[mode]); - static_generate_memory_accessor(mode, 8, false, true, "read64mask", m_read64mask[mode]); - static_generate_memory_accessor(mode, 8, true, false, "write64", m_write64[mode]); - static_generate_memory_accessor(mode, 8, true, true, "write64mask", m_write64mask[mode]); + drcuml_block &block(m_drcuml->begin_block(1024)); + int label = 1; + static_generate_memory_accessor(block, label, mode, 1, false, false, "read8", m_read8[mode]); + static_generate_memory_accessor(block, label, mode, 1, true, false, "write8", m_write8[mode]); + static_generate_memory_accessor(block, label, mode, 2, false, false, "read16", m_read16[mode]); + static_generate_memory_accessor(block, label, mode, 2, true, false, "write16", m_write16[mode]); + static_generate_memory_accessor(block, label, mode, 4, false, false, "read32", m_read32[mode]); + static_generate_memory_accessor(block, label, mode, 4, false, true, "read32mask", m_read32mask[mode]); + static_generate_memory_accessor(block, label, mode, 4, true, false, "write32", m_write32[mode]); + static_generate_memory_accessor(block, label, mode, 4, true, true, "write32mask", m_write32mask[mode]); + static_generate_memory_accessor(block, label, mode, 8, false, false, "read64", m_read64[mode]); + static_generate_memory_accessor(block, label, mode, 8, false, true, "read64mask", m_read64mask[mode]); + static_generate_memory_accessor(block, label, mode, 8, true, false, "write64", m_write64[mode]); + static_generate_memory_accessor(block, label, mode, 8, true, true, "write64mask", m_write64mask[mode]); + block.end(); } } catch (drcuml_block::abort_compilation &) @@ -283,13 +306,16 @@ void mips3_device::code_compile_block(uint8_t mode, offs_t pc) { compiler_state compiler = { 0 }; const opcode_desc *seqhead, *seqlast; + const opcode_desc *codelast; const opcode_desc *desclist; bool override = false; - g_profiler.start(PROFILER_DRC_COMPILE); + auto profile = g_profiler.start(PROFILER_DRC_COMPILE); /* get a description of this sequence */ desclist = m_drcfe->describe_code(pc); + /* get last instruction of the code (potentially used in generate_checksum) */ + codelast = m_drcfe->get_last(); if (m_drcuml->logging() || m_drcuml->logging_native()) log_opcode_desc(desclist, 0); @@ -341,7 +367,7 @@ void mips3_device::code_compile_block(uint8_t mode, offs_t pc) /* validate this code block if we're not pointing into ROM */ if (m_program->get_write_ptr(seqhead->physpc) != nullptr) - generate_checksum_block(block, compiler, seqhead, seqlast); + generate_checksum_block(block, compiler, seqhead, seqlast, codelast); /* label this instruction, if it may be jumped to locally */ if (seqhead->flags & OPFLAG_IS_BRANCH_TARGET) @@ -379,7 +405,6 @@ void mips3_device::code_compile_block(uint8_t mode, offs_t pc) /* end the sequence */ block.end(); - g_profiler.stop(); succeeded = true; } catch (drcuml_block::abort_compilation &) @@ -570,44 +595,40 @@ static void cfunc_unimplemented(void *param) static entry point -------------------------------------------------*/ -void mips3_device::static_generate_entry_point() +void mips3_device::static_generate_entry_point(drcuml_block &block, int &label) { - uml::code_label const skip = 1; - - drcuml_block &block(m_drcuml->begin_block(20)); + uml::code_label const skip = label++; /* forward references */ alloc_handle(*m_drcuml, m_exception_norecover[EXCEPTION_INTERRUPT], "interrupt_norecover"); alloc_handle(*m_drcuml, m_nocode, "nocode"); alloc_handle(*m_drcuml, m_entry, "entry"); - UML_HANDLE(block, *m_entry); // handle entry + UML_HANDLE(block, *m_entry); // handle entry /* reset the FPU mode */ - UML_AND(block, I0, CCR132(31), 3); // and i0,ccr1[31],3 - UML_LOAD(block, I0, &m_fpmode[0], I0, SIZE_BYTE, SCALE_x1);// load i0,fpmode,i0,byte - UML_SETFMOD(block, I0); // setfmod i0 + UML_AND(block, I0, CCR132(31), 3); // and i0,ccr1[31],3 + UML_LOAD(block, I0, &m_fpmode[0], I0, SIZE_BYTE, SCALE_x1); // load i0,fpmode,i0,byte + UML_SETFMOD(block, I0); // setfmod i0 /* load fast integer registers */ load_fast_iregs(block); /* check for interrupts */ UML_AND(block, I0, CPR032(COP0_Cause), CPR032(COP0_Status)); // and i0,[Cause],[Status] - UML_AND(block, I0, I0, 0xfc00); // and i0,i0,0xfc00,Z - UML_JMPc(block, COND_Z, skip); // jmp skip,Z + UML_AND(block, I0, I0, 0xfc00); // and i0,i0,0xfc00,Z + UML_JMPc(block, COND_Z, skip); // jmp skip,Z UML_TEST(block, CPR032(COP0_Status), SR_IE); // test [Status],SR_IE - UML_JMPc(block, COND_Z, skip); // jmp skip,Z + UML_JMPc(block, COND_Z, skip); // jmp skip,Z UML_TEST(block, CPR032(COP0_Status), SR_EXL | SR_ERL); // test [Status],SR_EXL | SR_ERL - UML_JMPc(block, COND_NZ, skip); // jmp skip,NZ - UML_MOV(block, I0, mem(&m_core->pc)); // mov i0,pc - UML_MOV(block, I1, 0); // mov i1,0 - UML_CALLH(block, *m_exception_norecover[EXCEPTION_INTERRUPT]); // callh exception_norecover + UML_JMPc(block, COND_NZ, skip); // jmp skip,NZ + UML_MOV(block, I0, mem(&m_core->pc)); // mov i0,pc + UML_MOV(block, I1, 0); // mov i1,0 + UML_CALLH(block, *m_exception_norecover[EXCEPTION_INTERRUPT]); // callh exception_norecover UML_LABEL(block, skip); // skip: /* generate a hash jump via the current mode and PC */ - UML_HASHJMP(block, mem(&m_core->mode), mem(&m_core->pc), *m_nocode); - // hashjmp <mode>,<pc>,nocode - block.end(); + UML_HASHJMP(block, mem(&m_core->mode), mem(&m_core->pc), *m_nocode); // hashjmp <mode>,<pc>,nocode } @@ -616,11 +637,8 @@ void mips3_device::static_generate_entry_point() exception handler for "out of code" -------------------------------------------------*/ -void mips3_device::static_generate_nocode_handler() +void mips3_device::static_generate_nocode_handler(drcuml_block &block, int &label) { - /* begin generating */ - drcuml_block &block(m_drcuml->begin_block(10)); - /* generate a hash jump via the current mode and PC */ alloc_handle(*m_drcuml, m_nocode, "nocode"); UML_HANDLE(block, *m_nocode); // handle nocode @@ -628,8 +646,6 @@ void mips3_device::static_generate_nocode_handler() UML_MOV(block, mem(&m_core->pc), I0); // mov [pc],i0 save_fast_iregs(block); UML_EXIT(block, EXECUTE_MISSING_CODE); // exit EXECUTE_MISSING_CODE - - block.end(); } @@ -638,11 +654,8 @@ void mips3_device::static_generate_nocode_handler() out of cycles exception handler -------------------------------------------------*/ -void mips3_device::static_generate_out_of_cycles() +void mips3_device::static_generate_out_of_cycles(drcuml_block &block, int &label) { - /* begin generating */ - drcuml_block &block(m_drcuml->begin_block(10)); - /* generate a hash jump via the current mode and PC */ alloc_handle(*m_drcuml, m_out_of_cycles, "out_of_cycles"); UML_HANDLE(block, *m_out_of_cycles); // handle out_of_cycles @@ -650,8 +663,6 @@ void mips3_device::static_generate_out_of_cycles() UML_MOV(block, mem(&m_core->pc), I0); // mov <pc>,i0 save_fast_iregs(block); UML_EXIT(block, EXECUTE_OUT_OF_CYCLES); // exit EXECUTE_OUT_OF_CYCLES - - block.end(); } @@ -660,15 +671,14 @@ void mips3_device::static_generate_out_of_cycles() TLB mismatch handler -------------------------------------------------*/ -void mips3_device::static_generate_tlb_mismatch() +void mips3_device::static_generate_tlb_mismatch(drcuml_block &block, int &label) { + uml::code_label const skip = label++; + /* forward references */ alloc_handle(*m_drcuml, m_exception[EXCEPTION_TLBLOAD], "exception_tlbload"); alloc_handle(*m_drcuml, m_exception[EXCEPTION_TLBLOAD_FILL], "exception_tlbload_fill"); - /* begin generating */ - drcuml_block &block(m_drcuml->begin_block(20)); - /* generate a hash jump via the current mode and PC */ alloc_handle(*m_drcuml, m_tlb_mismatch, "tlb_mismatch"); UML_HANDLE(block, *m_tlb_mismatch); // handle tlb_mismatch @@ -687,12 +697,12 @@ void mips3_device::static_generate_tlb_mismatch() UML_MOV(block, mem(&m_core->arg1), I1); // mov [arg1],i1 UML_CALLC(block, cfunc_printf_debug, this); // callc printf_debug } - UML_TEST(block, I1, VTLB_FETCH_ALLOWED); // test i1,VTLB_FETCH_ALLOWED - UML_JMPc(block, COND_NZ, 1); // jmp 1,nz - UML_TEST(block, I1, VTLB_FLAG_FIXED); // test i1,VTLB_FLAG_FIXED + UML_TEST(block, I1, FETCH_ALLOWED); // test i1,FETCH_ALLOWED + UML_JMPc(block, COND_NZ, skip); // jmp skip,nz + UML_TEST(block, I1, FLAG_FIXED); // test i1,FLAG_FIXED UML_EXHc(block, COND_NZ, *m_exception[EXCEPTION_TLBLOAD], I0); // exh exception[TLBLOAD],i0,nz UML_EXH(block, *m_exception[EXCEPTION_TLBLOAD_FILL], I0); // exh exception[TLBLOAD_FILL],i0 - UML_LABEL(block, 1); // 1: + UML_LABEL(block, skip); // skip: save_fast_iregs(block); // the saved PC may be set 1 instruction back with the low bit set to indicate @@ -702,8 +712,6 @@ void mips3_device::static_generate_tlb_mismatch() UML_MOV(block, mem(&m_core->pc), I0); // mov i0,[pc] UML_EXIT(block, EXECUTE_MISSING_CODE); // exit EXECUTE_MISSING_CODE - - block.end(); } @@ -712,12 +720,12 @@ void mips3_device::static_generate_tlb_mismatch() exception handler -------------------------------------------------*/ -void mips3_device::static_generate_exception(uint8_t exception, int recover, const char *name) +void mips3_device::static_generate_exception(drcuml_block &block, int &label, uint8_t exception, int recover, const char *name) { uml::code_handle *&exception_handle = recover ? m_exception[exception] : m_exception_norecover[exception]; uint32_t offset = 0x180; - uml::code_label const next = 1; - uml::code_label const skip = 2; + uml::code_label const next = label++; + uml::code_label const skip = label++; /* translate our fake fill exceptions into real exceptions */ if (exception == EXCEPTION_TLBLOAD_FILL || exception == EXCEPTION_TLBSTORE_FILL) @@ -726,9 +734,6 @@ void mips3_device::static_generate_exception(uint8_t exception, int recover, con exception = (exception - EXCEPTION_TLBLOAD_FILL) + EXCEPTION_TLBLOAD; } - /* begin generating */ - drcuml_block &block(m_drcuml->begin_block(1024)); - /* add a global entry for this */ alloc_handle(*m_drcuml, exception_handle, name); UML_HANDLE(block, *exception_handle); // handle name @@ -741,7 +746,7 @@ void mips3_device::static_generate_exception(uint8_t exception, int recover, con UML_MOV(block, CPR032(COP0_BadVAddr), I0); // mov [BadVAddr],i0 } - if (exception == EXCEPTION_TLBLOAD || exception == EXCEPTION_TLBSTORE) + if (exception == EXCEPTION_TLBMOD || exception == EXCEPTION_TLBLOAD || exception == EXCEPTION_TLBSTORE) { /* set the upper bits of EntryHi and the lower bits of Context to the fault page */ UML_ROLINS(block, CPR032(COP0_EntryHi), I0, 0, 0xffffe000); // rolins [EntryHi],i0,0,0xffffe000 @@ -783,7 +788,7 @@ void mips3_device::static_generate_exception(uint8_t exception, int recover, con } /* set EXL in the SR */ - UML_OR(block, I0, CPR032(COP0_Status), SR_EXL); // or i0,[Status],SR_EXL + UML_OR(block, I0, CPR032(COP0_Status), SR_EXL); // or i0,[Status],SR_EXL UML_MOV(block, CPR032(COP0_Status), I0); // mov [Status],i0 generate_update_mode(block); @@ -791,142 +796,126 @@ void mips3_device::static_generate_exception(uint8_t exception, int recover, con if ((PRINTF_EXCEPTIONS && exception != EXCEPTION_INTERRUPT && exception != EXCEPTION_SYSCALL) || (PRINTF_MMU && (exception == EXCEPTION_TLBLOAD || exception == EXCEPTION_TLBSTORE))) { - UML_CALLC(block, cfunc_printf_exception, this); // callc cfunc_printf_exception,nullptr + UML_CALLC(block, cfunc_printf_exception, this); // callc cfunc_printf_exception,nullptr //UML_CALLC(block, cfunc_debug_break, this); // callc cfunc_debug_break,nullptr } /* choose our target PC */ - UML_ADD(block, I0, I3, 0xbfc00200); // add i0,i3,0xbfc00200 - UML_TEST(block, CPR032(COP0_Status), SR_BEV); // test CPR032(COP0_Status),SR_BEV - UML_JMPc(block, COND_NZ, skip); // jnz <skip> - UML_ADD(block, I0, I3, 0x80000000); // add i0,i3,0x80000000,z + UML_ADD(block, I0, I3, 0xbfc00200); // add i0,i3,0xbfc00200 + UML_TEST(block, CPR032(COP0_Status), SR_BEV); // test CPR032(COP0_Status),SR_BEV + UML_JMPc(block, COND_NZ, skip); // jnz <skip> + UML_ADD(block, I0, I3, 0x80000000); // add i0,i3,0x80000000,z UML_LABEL(block, skip); // <skip>: /* adjust cycles */ - UML_SUB(block, mem(&m_core->icount), mem(&m_core->icount), I1); // sub icount,icount,cycles,S - UML_EXHc(block, COND_S, *m_out_of_cycles, I0); // exh out_of_cycles,i0 - - UML_HASHJMP(block, mem(&m_core->mode), I0, *m_nocode);// hashjmp <mode>,i0,nocode + UML_SUB(block, mem(&m_core->icount), mem(&m_core->icount), I1); // sub icount,icount,cycles,S + UML_EXHc(block, COND_S, *m_out_of_cycles, I0); // exh out_of_cycles,i0 - block.end(); + UML_HASHJMP(block, mem(&m_core->mode), I0, *m_nocode); // hashjmp <mode>,i0,nocode } /*------------------------------------------------------------------ - static_generate_memory_accessor + static_generate_memory_mode_checks ------------------------------------------------------------------*/ -void mips3_device::static_generate_memory_accessor(int mode, int size, int iswrite, int ismasked, const char *name, uml::code_handle *&handleptr) +void mips3_device::static_generate_memory_mode_checks(drcuml_block &block, uml::code_handle &exception_addrerr, int &label, int mode) { - /* on entry, address is in I0; data for writes is in I1; mask for accesses is in I2 */ - /* on exit, read result is in I0 */ - /* routine trashes I0-I3 */ - uml::code_handle &exception_tlb = *m_exception[iswrite ? EXCEPTION_TLBSTORE : EXCEPTION_TLBLOAD]; - uml::code_handle &exception_tlbfill = *m_exception[iswrite ? EXCEPTION_TLBSTORE_FILL : EXCEPTION_TLBLOAD_FILL]; - uml::code_handle &exception_addrerr = *m_exception[iswrite ? EXCEPTION_ADDRSTORE : EXCEPTION_ADDRLOAD]; - int tlbmiss = 0; - int label = 1; - int ramnum; - - /* begin generating */ - drcuml_block &block(m_drcuml->begin_block(1024)); - - /* add a global entry for this */ - alloc_handle(*m_drcuml, handleptr, name); - UML_HANDLE(block, *handleptr); // handle handleptr - /* user mode? generate address exception if top bit is set */ if (mode == MODE_USER) { - UML_TEST(block, I0, 0x80000000); // test i0,0x80000000 - UML_EXHc(block, COND_NZ, exception_addrerr, I0); // exh addrerr,i0,nz + UML_TEST(block, I0, 0x80000000); // test i0,0x80000000 + UML_EXHc(block, COND_NZ, exception_addrerr, I0); // exh addrerr,i0,nz } /* supervisor mode? generate address exception if not in user space or in $C0000000-DFFFFFFF */ if (mode == MODE_SUPER) { int addrok; - UML_TEST(block, I0, 0x80000000); // test i0,0x80000000 + UML_TEST(block, I0, 0x80000000); // test i0,0x80000000 UML_JMPc(block, COND_Z, addrok = label++); // jz addrok - UML_SHR(block, I3, I0, 29); // shr i3,i0,29 - UML_CMP(block, I3, 6); // cmp i3,6 + UML_SHR(block, I3, I0, 29); // shr i3,i0,29 + UML_CMP(block, I3, 6); // cmp i3,6 UML_EXHc(block, COND_NE, exception_addrerr, I0); // exh addrerr,i0,ne - UML_LABEL(block, addrok); // addrok: + UML_LABEL(block, addrok); // addrok: } +} - /* general case: assume paging and perform a translation */ - UML_SHR(block, I3, I0, 12); // shr i3,i0,12 - UML_LOAD(block, I3, (void *)vtlb_table(), I3, SIZE_DWORD, SCALE_x4);// load i3,[vtlb_table],i3,dword - UML_TEST(block, I3, iswrite ? VTLB_WRITE_ALLOWED : VTLB_READ_ALLOWED);// test i3,iswrite ? VTLB_WRITE_ALLOWED : VTLB_READ_ALLOWED - UML_JMPc(block, COND_Z, tlbmiss = label++); // jmp tlbmiss,z - UML_ROLINS(block, I0, I3, 0, 0xfffff000); // rolins i0,i3,0,0xfffff000 - if ((machine().debug_flags & DEBUG_FLAG_ENABLED) == 0) - for (ramnum = 0; ramnum < m_fastram_select; ramnum++) +/*------------------------------------------------------------------ + static_generate_fastram_accessor +------------------------------------------------------------------*/ + +void mips3_device::static_generate_fastram_accessor(drcuml_block &block, int &label, int size, bool iswrite, bool ismasked) +{ + if (!debugger_enabled()) + { + for (int ramnum = 0; ramnum < m_fastram_select; ramnum++) + { if (!(iswrite && m_fastram[ramnum].readonly)) { void *fastbase = (uint8_t *)m_fastram[ramnum].base - m_fastram[ramnum].start; uint32_t skip = label++; if (m_fastram[ramnum].end != 0xffffffff) { - UML_CMP(block, I0, m_fastram[ramnum].end); // cmp i0,end - UML_JMPc(block, COND_A, skip); // ja skip + UML_CMP(block, I0, m_fastram[ramnum].end); // cmp i0,end + UML_JMPc(block, COND_A, skip); // ja skip } if (m_fastram[ramnum].start != 0x00000000) { - UML_CMP(block, I0, m_fastram[ramnum].start);// cmp i0,fastram_start - UML_JMPc(block, COND_B, skip); // jb skip + UML_CMP(block, I0, m_fastram[ramnum].start); // cmp i0,fastram_start + UML_JMPc(block, COND_B, skip); // jb skip } if (!iswrite) { if (size == 1) { UML_XOR(block, I0, I0, m_bigendian ? BYTE4_XOR_BE(0) : BYTE4_XOR_LE(0)); - // xor i0,i0,bytexor - UML_LOAD(block, I0, fastbase, I0, SIZE_BYTE, SCALE_x1); // load i0,fastbase,i0,byte + // xor i0,i0,bytexor + UML_LOAD(block, I0, fastbase, I0, SIZE_BYTE, SCALE_x1); // load i0,fastbase,i0,byte } else if (size == 2) { UML_XOR(block, I0, I0, m_bigendian ? WORD_XOR_BE(0) : WORD_XOR_LE(0)); - // xor i0,i0,wordxor - UML_LOAD(block, I0, fastbase, I0, SIZE_WORD, SCALE_x1); // load i0,fastbase,i0,word_x1 + // xor i0,i0,wordxor + UML_LOAD(block, I0, fastbase, I0, SIZE_WORD, SCALE_x1); // load i0,fastbase,i0,word_x1 } else if (size == 4) { - UML_LOAD(block, I0, fastbase, I0, SIZE_DWORD, SCALE_x1); // load i0,fastbase,i0,dword_x1 + UML_LOAD(block, I0, fastbase, I0, SIZE_DWORD, SCALE_x1); // load i0,fastbase,i0,dword_x1 } else if (size == 8) { - UML_DLOAD(block, I0, fastbase, I0, SIZE_QWORD, SCALE_x1); // dload i0,fastbase,i0,qword_x1 + UML_DLOAD(block, I0, fastbase, I0, SIZE_QWORD, SCALE_x1); // dload i0,fastbase,i0,qword_x1 UML_DROR(block, I0, I0, 32 * (m_bigendian ? BYTE_XOR_BE(0) : BYTE_XOR_LE(0))); - // dror i0,i0,32*bytexor + // dror i0,i0,32*bytexor } - UML_RET(block); // ret + UML_RET(block); // ret } else { if (size == 1) { UML_XOR(block, I0, I0, m_bigendian ? BYTE4_XOR_BE(0) : BYTE4_XOR_LE(0)); - // xor i0,i0,bytexor - UML_STORE(block, fastbase, I0, I1, SIZE_BYTE, SCALE_x1);// store fastbase,i0,i1,byte + // xor i0,i0,bytexor + UML_STORE(block, fastbase, I0, I1, SIZE_BYTE, SCALE_x1); // store fastbase,i0,i1,byte } else if (size == 2) { UML_XOR(block, I0, I0, m_bigendian ? WORD_XOR_BE(0) : WORD_XOR_LE(0)); - // xor i0,i0,wordxor - UML_STORE(block, fastbase, I0, I1, SIZE_WORD, SCALE_x1);// store fastbase,i0,i1,word_x1 + // xor i0,i0,wordxor + UML_STORE(block, fastbase, I0, I1, SIZE_WORD, SCALE_x1); // store fastbase,i0,i1,word_x1 } else if (size == 4) { if (ismasked) { - UML_LOAD(block, I3, fastbase, I0, SIZE_DWORD, SCALE_x1); // load i3,fastbase,i0,dword_x1 - UML_ROLINS(block, I3, I1, 0, I2); // rolins i3,i1,0,i2 - UML_STORE(block, fastbase, I0, I3, SIZE_DWORD, SCALE_x1); // store fastbase,i0,i3,dword_x1 + UML_LOAD(block, I3, fastbase, I0, SIZE_DWORD, SCALE_x1); // load i3,fastbase,i0,dword_x1 + UML_ROLINS(block, I3, I1, 0, I2); // rolins i3,i1,0,i2 + UML_STORE(block, fastbase, I0, I3, SIZE_DWORD, SCALE_x1); // store fastbase,i0,i3,dword_x1 } else - UML_STORE(block, fastbase, I0, I1, SIZE_DWORD, SCALE_x1); // store fastbase,i0,i1,dword_x1 + UML_STORE(block, fastbase, I0, I1, SIZE_DWORD, SCALE_x1); // store fastbase,i0,i1,dword_x1 } else if (size == 8) { @@ -936,8 +925,8 @@ void mips3_device::static_generate_memory_accessor(int mode, int size, int iswri { UML_DROR(block, I2, I2, 32 * (m_bigendian ? BYTE_XOR_BE(0) : BYTE_XOR_LE(0))); // dror i2,i2,32*bytexor - UML_DLOAD(block, I3, fastbase, I0, SIZE_QWORD, SCALE_x1); // dload i3,fastbase,i0,qword_x1 - UML_DROLINS(block, I3, I1, 0, I2); // drolins i3,i1,0,i2 + UML_DLOAD(block, I3, fastbase, I0, SIZE_QWORD, SCALE_x1); // dload i3,fastbase,i0,qword_x1 + UML_DROLINS(block, I3, I1, 0, I2); // drolins i3,i1,0,i2 UML_DSTORE(block, fastbase, I0, I3, SIZE_QWORD, SCALE_x1); // dstore fastbase,i0,i3,qword_x1 } else @@ -946,9 +935,19 @@ void mips3_device::static_generate_memory_accessor(int mode, int size, int iswri UML_RET(block); // ret } - UML_LABEL(block, skip); // skip: + UML_LABEL(block, skip); // skip: } + } + } +} + + +/*------------------------------------------------------------------ + static_generate_memory_rw +------------------------------------------------------------------*/ +void mips3_device::static_generate_memory_rw(drcuml_block &block, int size, bool iswrite, bool ismasked) +{ switch (size) { case 1: @@ -969,9 +968,9 @@ void mips3_device::static_generate_memory_accessor(int mode, int size, int iswri if (iswrite) { if (!ismasked) - UML_WRITE(block, I0, I1, SIZE_DWORD, SPACE_PROGRAM); // write i0,i1,program_dword + UML_WRITE(block, I0, I1, SIZE_DWORD, SPACE_PROGRAM); // write i0,i1,program_dword else - UML_WRITEM(block, I0, I1, I2, SIZE_DWORD, SPACE_PROGRAM); // writem i0,i1,i2,program_dword + UML_WRITEM(block, I0, I1, I2, SIZE_DWORD, SPACE_PROGRAM); // writem i0,i1,i2,program_dword } else { @@ -986,38 +985,90 @@ void mips3_device::static_generate_memory_accessor(int mode, int size, int iswri if (iswrite) { if (!ismasked) - UML_DWRITE(block, I0, I1, SIZE_QWORD, SPACE_PROGRAM); // dwrite i0,i1,program_qword + UML_DWRITE(block, I0, I1, SIZE_QWORD, SPACE_PROGRAM); // dwrite i0,i1,program_qword else - UML_DWRITEM(block, I0, I1, I2, SIZE_QWORD, SPACE_PROGRAM); // dwritem i0,i1,i2,program_qword + UML_DWRITEM(block, I0, I1, I2, SIZE_QWORD, SPACE_PROGRAM); // dwritem i0,i1,i2,program_qword } else { if (!ismasked) - UML_DREAD(block, I0, I0, SIZE_QWORD, SPACE_PROGRAM); // dread i0,i0,program_qword + UML_DREAD(block, I0, I0, SIZE_QWORD, SPACE_PROGRAM); // dread i0,i0,program_qword else - UML_DREADM(block, I0, I0, I2, SIZE_QWORD, SPACE_PROGRAM); // dreadm i0,i0,i2,program_qword + UML_DREADM(block, I0, I0, I2, SIZE_QWORD, SPACE_PROGRAM); // dreadm i0,i0,i2,program_qword } break; } UML_RET(block); // ret +} - if (tlbmiss != 0) + +/*------------------------------------------------------------------ + static_generate_memory_accessor +------------------------------------------------------------------*/ + +void mips3_device::static_generate_memory_accessor(drcuml_block &block, int &label, int mode, int size, bool iswrite, bool ismasked, const char *name, uml::code_handle *&handleptr) +{ + /* on entry, address is in I0; data for writes is in I1; mask for accesses is in I2 */ + /* on exit, read result is in I0 */ + /* routine trashes I0-I3 */ + uml::code_handle &exception_tlb = *m_exception[iswrite ? EXCEPTION_TLBSTORE : EXCEPTION_TLBLOAD]; + uml::code_handle &exception_tlbfill = *m_exception[iswrite ? EXCEPTION_TLBSTORE_FILL : EXCEPTION_TLBLOAD_FILL]; + uml::code_handle &exception_addrerr = *m_exception[iswrite ? EXCEPTION_ADDRSTORE : EXCEPTION_ADDRLOAD]; + int tlbmiss = 0; + + /* add a global entry for this */ + alloc_handle(*m_drcuml, handleptr, name); + UML_HANDLE(block, *handleptr); // handle handleptr + + static_generate_memory_mode_checks(block, exception_addrerr, label, mode); + + /* general case: assume paging and perform a translation */ + UML_SHR(block, I3, I0, 12); // shr i3,i0,12 + UML_LOAD(block, I3, (void *)vtlb_table(), I3, SIZE_DWORD, SCALE_x4); // load i3,[vtlb_table],i3,dword + UML_TEST(block, I3, iswrite ? WRITE_ALLOWED : READ_ALLOWED); // test i3,iswrite ? WRITE_ALLOWED : READ_ALLOWED + UML_JMPc(block, COND_Z, tlbmiss = label++); // jmp tlbmiss,z + UML_ROLINS(block, I0, I3, 0, 0xfffff000); // rolins i0,i3,0,0xfffff000 + + static_generate_fastram_accessor(block, label, size, iswrite, ismasked); + static_generate_memory_rw(block, size, iswrite, ismasked); + + UML_LABEL(block, tlbmiss); // tlbmiss: + if (iswrite) { - UML_LABEL(block, tlbmiss); // tlbmiss: - if (iswrite) - { - UML_TEST(block, I3, VTLB_READ_ALLOWED); // test i3,VTLB_READ_ALLOWED - UML_EXHc(block, COND_NZ, *m_exception[EXCEPTION_TLBMOD], I0); - // exh tlbmod,i0,nz - } - UML_TEST(block, I3, VTLB_FLAG_FIXED); // test i3,VTLB_FLAG_FIXED - UML_EXHc(block, COND_NZ, exception_tlb, I0); // exh tlb,i0,nz - UML_EXH(block, exception_tlbfill, I0); // exh tlbfill,i0 + UML_TEST(block, I3, READ_ALLOWED); // test i3,READ_ALLOWED + UML_EXHc(block, COND_NZ, *m_exception[EXCEPTION_TLBMOD], I0); // exh tlbmod,i0,nz } - - block.end(); + UML_TEST(block, I3, FLAG_FIXED); // test i3,FLAG_FIXED + UML_EXHc(block, COND_NZ, exception_tlb, I0); // exh tlb,i0,nz + UML_EXH(block, exception_tlbfill, I0); // exh tlbfill,i0 } +void r4650_device::static_generate_memory_accessor(drcuml_block &block, int &label, int mode, int size, bool iswrite, bool ismasked, const char *name, uml::code_handle *&handleptr) +{ + /* on entry, address is in I0; data for writes is in I1; mask for accesses is in I2 */ + /* on exit, read result is in I0 */ + /* routine trashes I0-I3 */ + uml::code_handle &exception_addrerr = *m_exception[iswrite ? EXCEPTION_ADDRSTORE : EXCEPTION_ADDRLOAD]; + + /* add a global entry for this */ + alloc_handle(*m_drcuml, handleptr, name); + UML_HANDLE(block, *handleptr); // handle handleptr + + static_generate_memory_mode_checks(block, exception_addrerr, label, mode); + + if (mode == MODE_USER) + { + int addrok; + UML_CMP(block, I0, CPR032(COP0_R4650_DBound)); // cmp i0,CPR0[DBound] + UML_JMPc(block, COND_LE, addrok = label++); // jle addrok + UML_EXHc(block, COND_G, exception_addrerr, I0); // exh addrerr,i0,ne + UML_LABEL(block, addrok); // addrok: + UML_ADD(block, I0, I0, CPR032(COP0_R4650_DBase)); // add i0,i0,CPR0[DBase] + } + + static_generate_fastram_accessor(block, label, size, iswrite, ismasked); + static_generate_memory_rw(block, size, iswrite, ismasked); +} /*************************************************************************** @@ -1098,7 +1149,7 @@ void mips3_device::generate_update_cycles(drcuml_block &block, compiler_state &c validate a sequence of opcodes -------------------------------------------------*/ -void mips3_device::generate_checksum_block(drcuml_block &block, compiler_state &compiler, const opcode_desc *seqhead, const opcode_desc *seqlast) +void mips3_device::generate_checksum_block(drcuml_block &block, compiler_state &compiler, const opcode_desc *seqhead, const opcode_desc *seqlast, const opcode_desc *codelast) { const opcode_desc *curdesc; if (m_drcuml->logging()) @@ -1147,35 +1198,130 @@ void mips3_device::generate_checksum_block(drcuml_block &block, compiler_state & #else uint32_t sum = 0; const void *base = m_prptr(seqhead->physpc); - uint32_t low_bits = (seqhead->physpc & (m_data_bits == 64 ? 4 : 0)) ^ m_dword_xor; + const uint32_t data_bits_mask = (m_data_bits == 64 ? 4 : 0); + const uint32_t last_physpc = codelast->physpc; + uint32_t low_bits = (seqhead->physpc & data_bits_mask) ^ m_dword_xor; UML_LOAD(block, I0, base, low_bits, SIZE_DWORD, SCALE_x1); // load i0,base,0,dword sum += seqhead->opptr.l[0]; - for (curdesc = seqhead->next(); curdesc != seqlast->next(); curdesc = curdesc->next()) + if ((m_drcoptions & MIPS3DRC_EXTRA_INSTR_CHECK) && !(codelast->flags & OPFLAG_VIRTUAL_NOOP) && last_physpc != seqhead->physpc) { - if (!(curdesc->flags & OPFLAG_VIRTUAL_NOOP)) + base = m_prptr(last_physpc); + assert(base != nullptr); + low_bits = (last_physpc & data_bits_mask) ^ m_dword_xor; + UML_LOAD(block, I1, base, low_bits, SIZE_DWORD, SCALE_x1); // load i1,base,dword + UML_ADD(block, I0, I0, I1); // add i0,i0,i1 + sum += codelast->opptr.l[0]; + } + if (!(m_drcoptions & MIPS3DRC_EXTRA_INSTR_CHECK)) + { + for (curdesc = seqhead->next(); curdesc != seqlast->next(); curdesc = curdesc->next()) { - base = m_prptr(curdesc->physpc); - assert(base != nullptr); - uint32_t low_bits = (curdesc->physpc & (m_data_bits == 64 ? 4 : 0)) ^ m_dword_xor; - UML_LOAD(block, I1, base, low_bits, SIZE_DWORD, SCALE_x1); // load i1,base,dword - UML_ADD(block, I0, I0, I1); // add i0,i0,i1 - sum += curdesc->opptr.l[0]; - - if (curdesc->delay.first() != nullptr - && !(curdesc->delay.first()->flags & OPFLAG_VIRTUAL_NOOP) - && (curdesc == seqlast || (curdesc->next() != nullptr && curdesc->next()->physpc != curdesc->delay.first()->physpc))) + if (!(curdesc->flags & OPFLAG_VIRTUAL_NOOP)) { - base = m_prptr(curdesc->delay.first()->physpc); + base = m_prptr(curdesc->physpc); assert(base != nullptr); - uint32_t low_bits = (curdesc->delay.first()->physpc & (m_data_bits == 64 ? 4 : 0)) ^ m_dword_xor; - UML_LOAD(block, I1, base, low_bits, SIZE_DWORD, SCALE_x1); // load i1,base,dword - UML_ADD(block, I0, I0, I1); // add i0,i0,i1 - sum += curdesc->delay.first()->opptr.l[0]; + low_bits = (curdesc->physpc & data_bits_mask) ^ m_dword_xor; + UML_LOAD(block, I1, base, low_bits, SIZE_DWORD, SCALE_x1); // load i1,base,dword + UML_ADD(block, I0, I0, I1); // add i0,i0,i1 + sum += curdesc->opptr.l[0]; + + if (curdesc->delay.first() != nullptr + && !(curdesc->delay.first()->flags & OPFLAG_VIRTUAL_NOOP) + && (curdesc == seqlast || (curdesc->next() != nullptr && curdesc->next()->physpc != curdesc->delay.first()->physpc))) + { + base = m_prptr(curdesc->delay.first()->physpc); + assert(base != nullptr); + low_bits = (curdesc->delay.first()->physpc & data_bits_mask) ^ m_dword_xor; + UML_LOAD(block, I1, base, low_bits, SIZE_DWORD, SCALE_x1); // load i1,base,dword + UML_ADD(block, I0, I0, I1); // add i0,i0,i1 + sum += curdesc->delay.first()->opptr.l[0]; + } + } + } + } + else + { + for (curdesc = seqhead->next(); curdesc != seqlast->next(); curdesc = curdesc->next()) + { + if (!(curdesc->flags & OPFLAG_VIRTUAL_NOOP)) + { + // Skip the last if it was already included above + if (curdesc->physpc != last_physpc) + { + base = m_prptr(curdesc->physpc); + assert(base != nullptr); + low_bits = (curdesc->physpc & data_bits_mask) ^ m_dword_xor; + UML_LOAD(block, I1, base, low_bits, SIZE_DWORD, SCALE_x1); // load i1,base,dword + UML_ADD(block, I0, I0, I1); // add i0,i0,i1 + sum += curdesc->opptr.l[0]; + } + + if (curdesc->delay.first() != nullptr + && !(curdesc->delay.first()->flags & OPFLAG_VIRTUAL_NOOP) + && (curdesc == seqlast || (curdesc->next() != nullptr && curdesc->next()->physpc != curdesc->delay.first()->physpc))) + { + base = m_prptr(curdesc->delay.first()->physpc); + assert(base != nullptr); + low_bits = (curdesc->delay.first()->physpc & data_bits_mask) ^ m_dword_xor; + UML_LOAD(block, I1, base, low_bits, SIZE_DWORD, SCALE_x1); // load i1,base,dword + UML_ADD(block, I0, I0, I1); // add i0,i0,i1 + sum += curdesc->delay.first()->opptr.l[0]; + } } } } UML_CMP(block, I0, sum); // cmp i0,sum UML_EXHc(block, COND_NE, *m_nocode, epc(seqhead)); // exne nocode,seqhead->pc + if (DEBUG_STRICT_VERIFY) + { + // This code will do additional checks on the last instruction and last delay slot and indicate if the check failed + uml::code_label check_second = compiler.labelnum++; + uml::code_label check_failed = compiler.labelnum++; + uml::code_label check_passed = compiler.labelnum++; + // Check the last instruction + if (!(codelast->flags & OPFLAG_VIRTUAL_NOOP) && last_physpc != seqhead->physpc) + { + base = m_prptr(last_physpc); + assert(base != nullptr); + low_bits = (last_physpc & (m_data_bits == 64 ? 4 : 0)) ^ m_dword_xor; + UML_LOAD(block, I0, base, low_bits, SIZE_DWORD, SCALE_x1); // load i1,base,dword + sum = codelast->opptr.l[0]; + UML_CMP(block, I0, sum); // cmp i0,sum + UML_JMPc(block, COND_E, check_second); + static const char text[] = "Last instr validation fail seq: %08X end: %08x\n"; + UML_DMOV(block, mem(&m_core->format), (uintptr_t)text); // mov [format],text + UML_MOV(block, mem(&m_core->arg0), seqhead->pc); + UML_MOV(block, mem(&m_core->arg1), last_physpc); // mov [arg0],desc->pc + UML_CALLC(block, cfunc_printf_debug, this); // callc printf_debug + //UML_CALLC(block, cfunc_debug_break, this); + // Skip delay slot check + UML_JMP(block, check_failed); + // Check the last instruction delay slot + UML_LABEL(block, check_second); + if (codelast->delay.first() != nullptr && !(codelast->delay.first()->flags & OPFLAG_VIRTUAL_NOOP) + && last_physpc != seqhead->physpc) + { + base = m_prptr(codelast->delay.first()->physpc); + assert(base != nullptr); + low_bits = (codelast->delay.first()->physpc & (m_data_bits == 64 ? 4 : 0)) ^ m_dword_xor; + UML_LOAD(block, I0, base, low_bits, SIZE_DWORD, SCALE_x1); // load i1,base,dword + sum = codelast->delay.first()->opptr.l[0]; + UML_CMP(block, I0, sum); // cmp i0,sum + UML_JMPc(block, COND_E, check_passed); + static const char text[] = "Last delay slot validation fail seq: %08X end: %08x\n"; + UML_DMOV(block, mem(&m_core->format), (uintptr_t)text); // mov [format],text + UML_MOV(block, mem(&m_core->arg0), seqhead->pc); + UML_MOV(block, mem(&m_core->arg1), last_physpc); // mov [arg0],desc->pc + UML_CALLC(block, cfunc_printf_debug, this); // callc printf_debug + //UML_CALLC(block, cfunc_debug_break, this); + UML_JMP(block, check_failed); + } + } + UML_JMP(block, check_passed); + UML_LABEL(block, check_failed); + UML_EXH(block, *m_nocode, epc(seqhead)); // exne nocode,seqhead->pc + UML_LABEL(block, check_passed); + } #endif } } @@ -1188,22 +1334,19 @@ void mips3_device::generate_checksum_block(drcuml_block &block, compiler_state & void mips3_device::generate_sequence_instruction(drcuml_block &block, compiler_state &compiler, const opcode_desc *desc) { - offs_t expc; - int hotnum; - /* add an entry for the log */ if (m_drcuml->logging() && !(desc->flags & OPFLAG_VIRTUAL_NOOP)) log_add_disasm_comment(block, desc->pc, desc->opptr.l[0]); /* set the PC map variable */ - expc = (desc->flags & OPFLAG_IN_DELAY_SLOT) ? desc->pc - 3 : desc->pc; + offs_t expc = (desc->flags & OPFLAG_IN_DELAY_SLOT) ? desc->pc - 3 : desc->pc; UML_MAPVAR(block, MAPVAR_PC, expc); // mapvar PC,expc /* accumulate total cycles */ compiler.cycles += desc->cycles; /* is this a hotspot? */ - for (hotnum = 0; hotnum < MIPS3_MAX_HOTSPOTS; hotnum++) + for (int hotnum = 0; hotnum < MIPS3_MAX_HOTSPOTS; hotnum++) if (m_hotspot[hotnum].pc != 0 && desc->pc == m_hotspot[hotnum].pc && desc->opptr.l[0] == m_hotspot[hotnum].opcode) { compiler.cycles += m_hotspot[hotnum].cycles; @@ -1221,7 +1364,7 @@ void mips3_device::generate_sequence_instruction(drcuml_block &block, compiler_s } /* if we are debugging, call the debugger */ - if ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0) + if (debugger_enabled()) { UML_MOV(block, mem(&m_core->pc), desc->pc); // mov [pc],desc->pc save_fast_iregs(block); @@ -1247,6 +1390,8 @@ void mips3_device::generate_sequence_instruction(drcuml_block &block, compiler_s UML_CALLC(block, cfunc_printf_debug, this); // callc printf_debug } UML_EXH(block, *m_tlb_mismatch, 0); // exh tlb_mismatch,0 + // Unconditional tlb exception, no point going further + return; } /* validate our TLB entry at this PC; if we fail, we need to handle it */ @@ -1255,7 +1400,7 @@ void mips3_device::generate_sequence_instruction(drcuml_block &block, compiler_s const vtlb_entry *tlbtable = vtlb_table(); /* if we currently have a valid TLB read entry, we just verify */ - if (tlbtable[desc->pc >> 12] & VTLB_FETCH_ALLOWED) + if (tlbtable[desc->pc >> 12] & FETCH_ALLOWED) { if (PRINTF_MMU) { @@ -1280,6 +1425,8 @@ void mips3_device::generate_sequence_instruction(drcuml_block &block, compiler_s UML_CALLC(block, cfunc_printf_debug, this); // callc printf_debug } UML_EXH(block, *m_tlb_mismatch, 0); // exh tlb_mismatch,0 + // Unconditional tlb exception, no point going further + return; } } @@ -1331,7 +1478,7 @@ void mips3_device::generate_delay_slot_and_branch(drcuml_block &block, compiler_ if (desc->targetpc != BRANCH_TARGET_DYNAMIC) { generate_update_cycles(block, compiler_temp, desc->targetpc, true); // <subtract cycles> - if (desc->flags & OPFLAG_INTRABLOCK_BRANCH) + if (!(m_drcoptions & MIPS3DRC_DISABLE_INTRABLOCK) && (desc->flags & OPFLAG_INTRABLOCK_BRANCH)) { UML_JMP(block, desc->targetpc | 0x80000000); // jmp desc->targetpc | 0x80000000 } @@ -2456,6 +2603,11 @@ bool mips3_device::generate_set_cop0_reg(drcuml_block &block, compiler_state &co return true; case COP0_Count: + // don't count the cycle for this instruction yet; we need a non-zero cycle + // count in case we are in a delay slot, otherwise the test for negative cycles + // won't be generated (due to compiler.cycles == 0); see the loop during early + // boot of gauntdl, @BFC01A24 + compiler.cycles--; generate_update_cycles(block, compiler, desc->pc, !in_delay_slot); // <subtract cycles> UML_MOV(block, CPR032(COP0_Count), I0); // mov [Count],i0 UML_CALLC(block, cfunc_get_cycles, this); // callc cfunc_get_cycles,mips3 @@ -2464,6 +2616,7 @@ bool mips3_device::generate_set_cop0_reg(drcuml_block &block, compiler_state &co UML_DSUB(block, mem(&m_core->count_zero_time), mem(&m_core->numcycles), I0); // dsub [count_zero_time],[m_numcycles],i0 UML_CALLC(block, cfunc_mips3com_update_cycle_counting, this); // callc mips3com_update_cycle_counting,mips.core + compiler.cycles++; return true; case COP0_Compare: @@ -2509,12 +2662,18 @@ bool mips3_device::generate_get_cop0_reg(drcuml_block &block, compiler_state &co switch (reg) { case COP0_Count: + // don't count the cycle for this instruction yet; we need a non-zero cycle + // count in case we are in a delay slot, otherwise the test for negative cycles + // won't be generated (due to compiler.cycles == 0); see the loop during early + // boot of gauntdl, @BFC01A24 + compiler.cycles--; generate_update_cycles(block, compiler, desc->pc, false); // <subtract cycles> UML_CALLC(block, cfunc_get_cycles, this); // callc cfunc_get_cycles,mips3 UML_DSUB(block, I0, mem(&m_core->numcycles), mem(&m_core->count_zero_time)); // dsub i0,[numcycles],[count_zero_time] UML_DSHR(block, I0, I0, 1); // dshr i0,i0,1 UML_DSEXT(block, I0, I0, SIZE_DWORD); // dsext i0,i0,dword + compiler.cycles++; return true; case COP0_Random: diff --git a/src/devices/cpu/mips/mips3dsm.cpp b/src/devices/cpu/mips/mips3dsm.cpp index 9e1113819b7..cdad3375ae7 100644 --- a/src/devices/cpu/mips/mips3dsm.cpp +++ b/src/devices/cpu/mips/mips3dsm.cpp @@ -124,10 +124,12 @@ const char *const mips3_disassembler::ccreg[4][32] = inline std::string mips3_disassembler::signed_16bit(int16_t val) { - if (val < 0) - return util::string_format("-$%x", -val); + if (val >= -9 && val <= 9) + return util::string_format("%d", val); + else if (val < 0) + return util::string_format("-0x%x", -val); else - return util::string_format("$%x", val); + return util::string_format("0x%x", val); } uint32_t mips3_disassembler::dasm_cop0(uint32_t pc, uint32_t op, std::ostream &stream) @@ -147,11 +149,9 @@ uint32_t mips3_disassembler::dasm_cop0(uint32_t pc, uint32_t op, std::ostream &s case 0x08: /* BC */ switch (rt) { - case 0x00: util::stream_format(stream, "bc0f $%08x", pc + 4 + ((int16_t)op << 2)); break; - case 0x01: util::stream_format(stream, "bc0t $%08x", pc + 4 + ((int16_t)op << 2)); break; - case 0x02: util::stream_format(stream, "bc0fl [invalid]"); break; - case 0x03: util::stream_format(stream, "bc0tl [invalid]"); break; - default: util::stream_format(stream, "dc.l $%08x [invalid]", op); break; + case 0x00: util::stream_format(stream, "bc0f 0x%08x", pc + 4 + ((int16_t)op << 2)); flags = STEP_COND | step_over_extra(1); break; + case 0x01: util::stream_format(stream, "bc0t 0x%08x", pc + 4 + ((int16_t)op << 2)); flags = STEP_COND | step_over_extra(1); break; + default: util::stream_format(stream, ".word 0x%08x /*invalid*/", op); break; } break; case 0x10: @@ -181,14 +181,14 @@ uint32_t mips3_disassembler::dasm_cop0(uint32_t pc, uint32_t op, std::ostream &s default: dasm_extra_cop0(pc, op, stream); break; } break; - default: util::stream_format(stream, "dc.l $%08x [invalid]", op); break; + default: util::stream_format(stream, ".word 0x%08x /*invalid*/", op); break; } return flags; } uint32_t mips3_disassembler::dasm_extra_cop0(uint32_t pc, uint32_t op, std::ostream &stream) { - util::stream_format(stream, "cop0 $%07x", op & 0x01ffffff); + util::stream_format(stream, "cop0 0x%07x", op & 0x01ffffff); return 0; } @@ -218,10 +218,10 @@ uint32_t mips3_disassembler::dasm_cop1(uint32_t pc, uint32_t op, std::ostream &s case 0x08: /* BC */ switch (rt & 3) { - case 0x00: util::stream_format(stream, "bc1f $%08x,%d", pc + 4 + ((int16_t)op << 2), (op >> 18) & 7); break; - case 0x01: util::stream_format(stream, "bc1t $%08x,%d", pc + 4 + ((int16_t)op << 2), (op >> 18) & 7); break; - case 0x02: util::stream_format(stream, "bc1fl $%08x,%d", pc + 4 + ((int16_t)op << 2), (op >> 18) & 7); flags = STEP_OVER | step_over_extra(1); break; - case 0x03: util::stream_format(stream, "bc1tl $%08x,%d", pc + 4 + ((int16_t)op << 2), (op >> 18) & 7); flags = STEP_OVER | step_over_extra(1); break; + case 0x00: util::stream_format(stream, "bc1f 0x%08x,%d", pc + 4 + ((int16_t)op << 2), (op >> 18) & 7); flags = STEP_COND | step_over_extra(1); break; + case 0x01: util::stream_format(stream, "bc1t 0x%08x,%d", pc + 4 + ((int16_t)op << 2), (op >> 18) & 7); flags = STEP_COND | step_over_extra(1); break; + case 0x02: util::stream_format(stream, "bc1fl 0x%08x,%d", pc + 4 + ((int16_t)op << 2), (op >> 18) & 7); flags = STEP_COND | step_over_extra(1); break; + case 0x03: util::stream_format(stream, "bc1tl 0x%08x,%d", pc + 4 + ((int16_t)op << 2), (op >> 18) & 7); flags = STEP_COND | step_over_extra(1); break; } break; default: /* COP */ @@ -277,7 +277,7 @@ uint32_t mips3_disassembler::dasm_cop1(uint32_t pc, uint32_t op, std::ostream &s uint32_t mips3_disassembler::dasm_extra_cop1(uint32_t pc, uint32_t op, std::ostream &stream) { - util::stream_format(stream, "cop1 $%07x", op & 0x01ffffff); + util::stream_format(stream, "cop1 0x%07x", op & 0x01ffffff); return 0; } @@ -336,7 +336,7 @@ uint32_t mips3_disassembler::dasm_cop1x(uint32_t pc, uint32_t op, std::ostream & case 0x3d: case 0x3e: case 0x3f: util::stream_format(stream, "nmsub.%s %s,%s,%s,%s", fmt3, cpreg[1][fd], cpreg[1][fr], cpreg[1][fs], cpreg[1][ft]); break; - default: util::stream_format(stream, "cop1 $%07x", op & 0x01ffffff); break; + default: util::stream_format(stream, "cop1 0x%07x", op & 0x01ffffff); break; } return flags; } @@ -358,11 +358,9 @@ uint32_t mips3_disassembler::dasm_cop2(uint32_t pc, uint32_t op, std::ostream &s case 0x08: /* BC */ switch (rt) { - case 0x00: util::stream_format(stream, "bc2f $%08x", pc + 4 + ((int16_t)op << 2)); break; - case 0x01: util::stream_format(stream, "bc2t $%08x", pc + 4 + ((int16_t)op << 2)); break; - case 0x02: util::stream_format(stream, "bc2fl [invalid]"); break; - case 0x03: util::stream_format(stream, "bc2tl [invalid]"); break; - default: util::stream_format(stream, "dc.l $%08x [invalid]", op); break; + case 0x00: util::stream_format(stream, "bc2f 0x%08x", pc + 4 + ((int16_t)op << 2)); flags = STEP_COND | step_over_extra(1); break; + case 0x01: util::stream_format(stream, "bc2t 0x%08x", pc + 4 + ((int16_t)op << 2)); flags = STEP_COND | step_over_extra(1); break; + default: util::stream_format(stream, ".word 0x%08x /*invalid*/", op); break; } break; case 0x10: @@ -383,14 +381,14 @@ uint32_t mips3_disassembler::dasm_cop2(uint32_t pc, uint32_t op, std::ostream &s case 0x1f: /* COP */ flags = dasm_extra_cop2(pc, op, stream); break; - default: util::stream_format(stream, "dc.l $%08x [invalid]", op); break; + default: util::stream_format(stream, ".word 0x%08x /*invalid*/", op); break; } return flags; } uint32_t mips3_disassembler::dasm_extra_cop2(uint32_t pc, uint32_t op, std::ostream &stream) { - util::stream_format(stream, "cop2 $%07x", op & 0x01ffffff); + util::stream_format(stream, "cop2 0x%07x", op & 0x01ffffff); return 0; } @@ -406,9 +404,9 @@ uint32_t mips3_disassembler::dasm_idt(uint32_t pc, uint32_t op, std::ostream &st { case 0: util::stream_format(stream, "mad %s,%s", reg[rs], reg[rt]); break; case 1: util::stream_format(stream, "madu %s,%s", reg[rs], reg[rt]); break; - case 2: util::stream_format(stream, "mul %s,%s,%s", reg[rs], reg[rt], reg[rd]); break; + case 2: util::stream_format(stream, "mul %s,%s,%s", reg[rd], reg[rs], reg[rt]); break; case 4: util::stream_format(stream, "msub %s,%s", reg[rs], reg[rt]); break; - default:util::stream_format(stream, "dc.l $%08x [invalid]", op); break; + default:util::stream_format(stream, ".word 0x%08x /*invalid*/", op); break; } return flags; @@ -416,19 +414,19 @@ uint32_t mips3_disassembler::dasm_idt(uint32_t pc, uint32_t op, std::ostream &st uint32_t mips3_disassembler::dasm_extra_base(uint32_t pc, uint32_t op, std::ostream &stream) { - util::stream_format(stream, "dc.l $%08x [invalid]", op); + util::stream_format(stream, ".word 0x%08x /*invalid*/", op); return 0; } uint32_t mips3_disassembler::dasm_extra_regimm(uint32_t pc, uint32_t op, std::ostream &stream) { - util::stream_format(stream, "dc.l $%08x [invalid]", op); + util::stream_format(stream, ".word 0x%08x /*invalid*/", op); return 0; } uint32_t mips3_disassembler::dasm_extra_special(uint32_t pc, uint32_t op, std::ostream &stream) { - util::stream_format(stream, "dc.l $%08x [invalid]", op); + util::stream_format(stream, ".word 0x%08x /*invalid*/", op); return 0; } @@ -438,7 +436,7 @@ uint32_t ee_disassembler::dasm_extra_cop0(uint32_t pc, uint32_t op, std::ostream { case 0x38: util::stream_format(stream, "ei"); break; case 0x39: util::stream_format(stream, "di"); break; - default: util::stream_format(stream, "cop1 $%07x", op & 0x01ffffff); break; + default: util::stream_format(stream, "cop1 0x%07x", op & 0x01ffffff); break; } return 0; } @@ -453,7 +451,7 @@ uint32_t ee_disassembler::dasm_extra_cop1(uint32_t pc, uint32_t op, std::ostream { case 0x18: util::stream_format(stream, "adda.s %s,%s", cpreg[1][fs], cpreg[1][ft]); break; case 0x1c: util::stream_format(stream, "madd.s %s,%s,%s", cpreg[1][fd], cpreg[1][fs], cpreg[1][ft]); break; - default: util::stream_format(stream, "dc.l $%08x [invalid]", op); break; + default: util::stream_format(stream, ".word 0x%08x /*invalid*/", op); break; } return 0; } @@ -522,10 +520,10 @@ uint32_t ee_disassembler::dasm_extra_cop2(uint32_t pc, uint32_t op, std::ostream case 0x2f: util::stream_format(stream, "vmini.%s %s%s %s%s %s%s", dest, vfreg[rd], destc, vfreg[rs], destc, vfreg[rt], dest); break; case 0x30: util::stream_format(stream, "viadd %s, %s, %s", vireg[rd], vireg[rs], vireg[rt]); break; case 0x31: util::stream_format(stream, "visub %s, %s, %s", vireg[rd], vireg[rs], vireg[rt]); break; - case 0x32: util::stream_format(stream, "viaddi %s, %s, $%x", vireg[rt], vireg[rs], imm5); break; + case 0x32: util::stream_format(stream, "viaddi %s, %s, 0x%x", vireg[rt], vireg[rs], imm5); break; case 0x34: util::stream_format(stream, "viand %s, %s, %s", vireg[rd], vireg[rs], vireg[rt]); break; case 0x35: util::stream_format(stream, "vior %s, %s, %s", vireg[rd], vireg[rs], vireg[rt]); break; - case 0x38: util::stream_format(stream, "vcallms $06x", (op & 0x001fffc0) >> 3); break; + case 0x38: util::stream_format(stream, "vcallms 0x%06x", (op & 0x001fffc0) >> 3); break; case 0x39: util::stream_format(stream, "vcallmsr $vi27"); break; case 0x3c: case 0x3d: case 0x3e: case 0x3f: switch (ext) @@ -588,10 +586,10 @@ uint32_t ee_disassembler::dasm_extra_cop2(uint32_t pc, uint32_t op, std::ostream case 0x41: util::stream_format(stream, "vrget.%s %s%s R", dest, vfreg[rt], destc); break; case 0x42: util::stream_format(stream, "vrinit R, %s%s", vfreg[rs], fsf); break; case 0x43: util::stream_format(stream, "vrxor R, %s%s", vfreg[rs], fsf); break; - default: util::stream_format(stream, "dc.l $%08x [invalid]", op); break; + default: util::stream_format(stream, ".word 0x%08x /*invalid*/", op); break; } break; - default: util::stream_format(stream, "dc.l $%08x [invalid]", op); break; + default: util::stream_format(stream, ".word 0x%08x /*invalid*/", op); break; } return 0; @@ -663,14 +661,14 @@ uint32_t ee_disassembler::dasm_idt(uint32_t pc, uint32_t op, std::ostream &strea case 2: util::stream_format(stream, "pmfhl.slw %s", reg[rd]); break; case 3: util::stream_format(stream, "pmfhl.lh %s", reg[rd]); break; case 4: util::stream_format(stream, "pmfhl.sh %s", reg[rd]); break; - default: util::stream_format(stream, "dc.l $%08x [invalid]", op); break; + default: util::stream_format(stream, ".word 0x%08x /*invalid*/", op); break; } break; case 0x31: switch (fmt) { case 0: util::stream_format(stream, "pmthl.lw %s", reg[rd]); break; - default: util::stream_format(stream, "dc.l $%08x [invalid]", op); break; + default: util::stream_format(stream, ".word 0x%08x /*invalid*/", op); break; } break; case 0x34: util::stream_format(stream, "psllh %s,%s,%d", reg[rd], reg[rt], shift); break; @@ -679,7 +677,7 @@ uint32_t ee_disassembler::dasm_idt(uint32_t pc, uint32_t op, std::ostream &strea case 0x3c: util::stream_format(stream, "psllw %s,%s,%d", reg[rd], reg[rt], shift); break; case 0x3e: util::stream_format(stream, "psrlw %s,%s,%d", reg[rd], reg[rt], shift); break; case 0x3f: util::stream_format(stream, "psraw %s,%s,%d", reg[rd], reg[rt], shift); break; - default: util::stream_format(stream, "dc.l $%08x [invalid]", op); break; + default: util::stream_format(stream, ".word 0x%08x /*invalid*/", op); break; } return flags; @@ -719,7 +717,7 @@ uint32_t ee_disassembler::dasm_mmi0(uint32_t pc, uint32_t op, std::ostream &stre case 0x1b: util::stream_format(stream, "ppacb %s,%s,%s", reg[rd], reg[rs], reg[rt]); break; case 0x1e: util::stream_format(stream, "pext5 %s,%s", reg[rd], reg[rt]); break; case 0x1f: util::stream_format(stream, "ppac5 %s,%s", reg[rd], reg[rt]); break; - default: util::stream_format(stream, "dc.l $%08x [invalid]", op); break; + default: util::stream_format(stream, ".word 0x%08x /*invalid*/", op); break; } return 0; @@ -752,7 +750,7 @@ uint32_t ee_disassembler::dasm_mmi1(uint32_t pc, uint32_t op, std::ostream &stre case 0x19: util::stream_format(stream, "psubub %s,%s,%s", reg[rd], reg[rs], reg[rt]); break; case 0x1a: util::stream_format(stream, "pextub %s,%s,%s", reg[rd], reg[rs], reg[rt]); break; case 0x1b: util::stream_format(stream, "qfsrv %s,%s,%s", reg[rd], reg[rs], reg[rt]); break; - default: util::stream_format(stream, "dc.l $%08x [invalid]", op); break; + default: util::stream_format(stream, ".word 0x%08x /*invalid*/", op); break; } return 0; @@ -789,7 +787,7 @@ uint32_t ee_disassembler::dasm_mmi2(uint32_t pc, uint32_t op, std::ostream &stre case 0x1d: util::stream_format(stream, "pdivbw %s,%s", reg[rs], reg[rt]); break; case 0x1e: util::stream_format(stream, "pexew %s,%s", reg[rd], reg[rt]); break; case 0x1f: util::stream_format(stream, "prot3w %s,%s", reg[rd], reg[rt]); break; - default: util::stream_format(stream, "dc.l $%08x [invalid]", op); break; + default: util::stream_format(stream, ".word 0x%08x /*invalid*/", op); break; } return 0; @@ -817,7 +815,7 @@ uint32_t ee_disassembler::dasm_mmi3(uint32_t pc, uint32_t op, std::ostream &stre case 0x1a: util::stream_format(stream, "pexch %s,%s", reg[rd], reg[rt]); break; case 0x1b: util::stream_format(stream, "pcpyh %s,%s", reg[rd], reg[rt]); break; case 0x1e: util::stream_format(stream, "pexcw %s,%s", reg[rd], reg[rt]); break; - default: util::stream_format(stream, "dc.l $%08x [invalid]", op); break; + default: util::stream_format(stream, ".word 0x%08x /*invalid*/", op); break; } return 0; @@ -832,7 +830,7 @@ uint32_t ee_disassembler::dasm_extra_base(uint32_t pc, uint32_t op, std::ostream { case 0x1e: util::stream_format(stream, "lq %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]); break; case 0x1f: util::stream_format(stream, "sq %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]); break; - default: util::stream_format(stream, "dc.l $%08x [invalid]", op); break; + default: util::stream_format(stream, ".word 0x%08x /*invalid*/", op); break; } return 0; @@ -847,7 +845,7 @@ uint32_t ee_disassembler::dasm_extra_special(uint32_t pc, uint32_t op, std::ostr { case 0x28: util::stream_format(stream, "mfsa %s", reg[rd]); break; case 0x29: util::stream_format(stream, "mtsa %s", reg[rs]); break; - default: util::stream_format(stream, "dc.l $%08x [invalid]", op); break; + default: util::stream_format(stream, ".word 0x%08x /*invalid*/", op); break; } return 0; } @@ -860,7 +858,7 @@ uint32_t ee_disassembler::dasm_extra_regimm(uint32_t pc, uint32_t op, std::ostre { case 0x18: util::stream_format(stream, "mtsab %s,%s", reg[rs], signed_16bit(op)); break; case 0x19: util::stream_format(stream, "mtsah %s,%s", reg[rs], signed_16bit(op)); break; - default: util::stream_format(stream, "dc.l $%08x [invalid]", op); break; + default: util::stream_format(stream, ".word 0x%08x /*invalid*/", op); break; } return 0; } @@ -894,7 +892,7 @@ offs_t mips3_disassembler::dasm_one(std::ostream &stream, offs_t pc, u32 op) case 0x04: util::stream_format(stream, "sllv %s,%s,%s", reg[rd], reg[rt], reg[rs]); break; case 0x06: util::stream_format(stream, "srlv %s,%s,%s", reg[rd], reg[rt], reg[rs]); break; case 0x07: util::stream_format(stream, "srav %s,%s,%s", reg[rd], reg[rt], reg[rs]); break; - case 0x08: util::stream_format(stream, "jr %s", reg[rs]); if (rs == 31) flags = STEP_OUT; break; + case 0x08: util::stream_format(stream, "jr %s", reg[rs]); if (rs == 31) flags = STEP_OUT | step_over_extra(1); break; case 0x09: if (rd == 31) util::stream_format(stream, "jalr %s", reg[rs]); else @@ -922,13 +920,33 @@ offs_t mips3_disassembler::dasm_one(std::ostream &stream, offs_t pc, u32 op) case 0x1e: util::stream_format(stream, "ddiv %s,%s", reg[rs], reg[rt]); break; case 0x1f: util::stream_format(stream, "ddivu %s,%s", reg[rs], reg[rt]); break; case 0x20: util::stream_format(stream, "add %s,%s,%s", reg[rd], reg[rs], reg[rt]); break; - case 0x21: util::stream_format(stream, "addu %s,%s,%s", reg[rd], reg[rs], reg[rt]); break; - case 0x22: util::stream_format(stream, "sub %s,%s,%s", reg[rd], reg[rs], reg[rt]); break; - case 0x23: util::stream_format(stream, "subu %s,%s,%s", reg[rd], reg[rs], reg[rt]); break; + case 0x21: if (rs == 0 || rt == 0) + util::stream_format(stream, "move %s,%s", reg[rd], reg[rs == 0 ? rt : rs]); + else + util::stream_format(stream, "addu %s,%s,%s", reg[rd], reg[rs], reg[rt]); + break; + case 0x22: if (rs == 0) + util::stream_format(stream, "neg %s,%s", reg[rd], reg[rt]); + else + util::stream_format(stream, "sub %s,%s,%s", reg[rd], reg[rs], reg[rt]); + break; + case 0x23: if (rs == 0) + util::stream_format(stream, "negu %s,%s", reg[rd], reg[rt]); + else + util::stream_format(stream, "subu %s,%s,%s", reg[rd], reg[rs], reg[rt]); + break; case 0x24: util::stream_format(stream, "and %s,%s,%s", reg[rd], reg[rs], reg[rt]); break; - case 0x25: util::stream_format(stream, "or %s,%s,%s", reg[rd], reg[rs], reg[rt]); break; + case 0x25: if (rs == 0 || rt == 0) + util::stream_format(stream, "move %s,%s", reg[rd], reg[rs == 0 ? rt : rs]); + else + util::stream_format(stream, "or %s,%s,%s", reg[rd], reg[rs], reg[rt]); + break; case 0x26: util::stream_format(stream, "xor %s,%s,%s", reg[rd], reg[rs], reg[rt]); break; - case 0x27: util::stream_format(stream, "nor %s,%s,%s", reg[rd], reg[rs], reg[rt]); break; + case 0x27: if (rs == 0) + util::stream_format(stream, "not %s,%s", reg[rd], reg[rt]); + else + util::stream_format(stream, "nor %s,%s,%s", reg[rd], reg[rs], reg[rt]); + break; case 0x2a: util::stream_format(stream, "slt %s,%s,%s", reg[rd], reg[rs], reg[rt]); break; case 0x2b: util::stream_format(stream, "sltu %s,%s,%s", reg[rd], reg[rs], reg[rt]); break; case 0x2c: util::stream_format(stream, "dadd %s,%s,%s", reg[rd], reg[rs], reg[rt]); break; @@ -954,50 +972,84 @@ offs_t mips3_disassembler::dasm_one(std::ostream &stream, offs_t pc, u32 op) case 0x01: /* REGIMM */ switch ((op >> 16) & 31) { - case 0x00: util::stream_format(stream, "bltz %s,$%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); break; - case 0x01: util::stream_format(stream, "bgez %s,$%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); break; - case 0x02: util::stream_format(stream, "bltzl %s,$%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); break; - case 0x03: util::stream_format(stream, "bgezl %s,$%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); break; - case 0x08: util::stream_format(stream, "tgei %s,%s", reg[rs], signed_16bit(op)); flags = STEP_OVER; break; - case 0x09: util::stream_format(stream, "tgeiu %s,%s", reg[rs], signed_16bit(op)); flags = STEP_OVER; break; - case 0x0a: util::stream_format(stream, "tlti %s,%s", reg[rs], signed_16bit(op)); flags = STEP_OVER; break; - case 0x0b: util::stream_format(stream, "tltiu %s,%s", reg[rs], signed_16bit(op)); flags = STEP_OVER; break; - case 0x0c: util::stream_format(stream, "teqi %s,%s", reg[rs], signed_16bit(op)); flags = STEP_OVER; break; - case 0x0e: util::stream_format(stream, "tnei %s,%s", reg[rs], signed_16bit(op)); flags = STEP_OVER; break; - case 0x10: util::stream_format(stream, "bltzal %s,$%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); flags = STEP_OVER | step_over_extra(1); break; - case 0x11: util::stream_format(stream, "bgezal %s,$%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); flags = STEP_OVER | step_over_extra(1); break; - case 0x12: util::stream_format(stream, "bltzall %s,$%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); flags = STEP_OVER | step_over_extra(1); break; - case 0x13: util::stream_format(stream, "bgezall %s,$%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); flags = STEP_OVER | step_over_extra(1); break; + case 0x00: util::stream_format(stream, "bltz %s,0x%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); if (rs != 0) flags = STEP_COND | step_over_extra(1); break; + case 0x01: util::stream_format(stream, "bgez %s,0x%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); if (rs != 0) flags = STEP_COND | step_over_extra(1); break; + case 0x02: util::stream_format(stream, "bltzl %s,0x%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); if (rs != 0) flags = STEP_COND | step_over_extra(1); break; + case 0x03: util::stream_format(stream, "bgezl %s,0x%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); if (rs != 0) flags = STEP_COND | step_over_extra(1); break; + case 0x08: util::stream_format(stream, "tgei %s,%s", reg[rs], signed_16bit(op)); flags = STEP_OVER | STEP_COND; break; + case 0x09: util::stream_format(stream, "tgeiu %s,%s", reg[rs], signed_16bit(op)); flags = STEP_OVER | STEP_COND; break; + case 0x0a: util::stream_format(stream, "tlti %s,%s", reg[rs], signed_16bit(op)); flags = STEP_OVER | STEP_COND; break; + case 0x0b: util::stream_format(stream, "tltiu %s,%s", reg[rs], signed_16bit(op)); flags = STEP_OVER | STEP_COND; break; + case 0x0c: util::stream_format(stream, "teqi %s,%s", reg[rs], signed_16bit(op)); flags = STEP_OVER | STEP_COND; break; + case 0x0e: util::stream_format(stream, "tnei %s,%s", reg[rs], signed_16bit(op)); flags = STEP_OVER | STEP_COND; break; + case 0x10: util::stream_format(stream, "bltzal %s,0x%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); if (rs != 0) flags = STEP_OVER | STEP_COND | step_over_extra(1); break; + case 0x11: if (rs == 0) + util::stream_format(stream, "bal 0x%08x", pc + 4 + ((int16_t)op << 2)); + else + util::stream_format(stream, "bgezal %s,0x%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); + flags = STEP_OVER | (rs != 0 ? STEP_COND : 0) | step_over_extra(1); + break; + case 0x12: util::stream_format(stream, "bltzall %s,0x%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); if (rs != 0) flags = STEP_OVER | STEP_COND | step_over_extra(1); break; + case 0x13: util::stream_format(stream, "bgezall %s,0x%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); flags = STEP_OVER | (rs != 0 ? STEP_COND : 0) | step_over_extra(1); break; default: flags = dasm_extra_regimm(pc, op, stream); break; } break; - case 0x02: util::stream_format(stream, "j $%08x", (pc & 0xf0000000) | ((op & 0x03ffffff) << 2)); break; - case 0x03: util::stream_format(stream, "jal $%08x", (pc & 0xf0000000) | ((op & 0x03ffffff) << 2)); flags = STEP_OVER | step_over_extra(1); break; + case 0x02: util::stream_format(stream, "j 0x%08x", (pc & 0xf0000000) | ((op & 0x03ffffff) << 2)); break; + case 0x03: util::stream_format(stream, "jal 0x%08x", (pc & 0xf0000000) | ((op & 0x03ffffff) << 2)); flags = STEP_OVER | step_over_extra(1); break; case 0x04: if (rs == 0 && rt == 0) - util::stream_format(stream, "b $%08x", pc + 4 + ((int16_t)op << 2)); + util::stream_format(stream, "b 0x%08x", pc + 4 + ((int16_t)op << 2)); else - util::stream_format(stream, "beq %s,%s,$%08x", reg[rs], reg[rt], pc + 4 + ((int16_t)op << 2)); - break; - case 0x05: util::stream_format(stream, "bne %s,%s,$%08x", reg[rs], reg[rt], pc + 4 + ((int16_t)op << 2));break; - case 0x06: util::stream_format(stream, "blez %s,$%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); break; - case 0x07: util::stream_format(stream, "bgtz %s,$%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); break; + { + if (rt == 0) + util::stream_format(stream, "beqz %s,0x%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); + else + util::stream_format(stream, "beq %s,%s,0x%08x", reg[rs], reg[rt], pc + 4 + ((int16_t)op << 2)); + flags = STEP_COND | step_over_extra(1); + } + break; + case 0x05: if (rt == 0) + util::stream_format(stream, "bnez %s,0x%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); + else + util::stream_format(stream, "bne %s,%s,0x%08x", reg[rs], reg[rt], pc + 4 + ((int16_t)op << 2)); + flags = STEP_COND | step_over_extra(1); + break; + case 0x06: util::stream_format(stream, "blez %s,0x%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); if (rs != 0) flags = STEP_COND | step_over_extra(1); break; + case 0x07: util::stream_format(stream, "bgtz %s,0x%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); flags = STEP_COND | step_over_extra(1); break; case 0x08: util::stream_format(stream, "addi %s,%s,%s", reg[rt], reg[rs], signed_16bit(op)); break; - case 0x09: util::stream_format(stream, "addiu %s,%s,%s", reg[rt], reg[rs], signed_16bit(op)); break; + case 0x09: if (rs == 0) + util::stream_format(stream, "li %s,%s", reg[rt], signed_16bit(op)); + else + util::stream_format(stream, "addiu %s,%s,%s", reg[rt], reg[rs], signed_16bit(op)); + break; case 0x0a: util::stream_format(stream, "slti %s,%s,%s", reg[rt], reg[rs], signed_16bit(op)); break; case 0x0b: util::stream_format(stream, "sltiu %s,%s,%s", reg[rt], reg[rs], signed_16bit(op)); break; - case 0x0c: util::stream_format(stream, "andi %s,%s,$%04x", reg[rt], reg[rs], (uint16_t)op); break; - case 0x0d: util::stream_format(stream, "ori %s,%s,$%04x", reg[rt], reg[rs], (uint16_t)op); break; - case 0x0e: util::stream_format(stream, "xori %s,%s,$%04x", reg[rt], reg[rs], (uint16_t)op); break; - case 0x0f: util::stream_format(stream, "lui %s,$%04x", reg[rt], (uint16_t)op); break; + case 0x0c: util::stream_format(stream, "andi %s,%s,0x%04x", reg[rt], reg[rs], (uint16_t)op); break; + case 0x0d: if (rs == 0) + util::stream_format(stream, "li %s,0x%04x", reg[rt], (uint16_t)op); + else + util::stream_format(stream, "ori %s,%s,0x%04x", reg[rt], reg[rs], (uint16_t)op); + break; + case 0x0e: util::stream_format(stream, "xori %s,%s,0x%04x", reg[rt], reg[rs], (uint16_t)op); break; + case 0x0f: util::stream_format(stream, "lui %s,0x%04x", reg[rt], (uint16_t)op); break; case 0x10: flags = dasm_cop0(pc, op, stream); break; case 0x11: flags = dasm_cop1(pc, op, stream); break; case 0x12: flags = dasm_cop2(pc, op, stream); break; case 0x13: flags = dasm_cop1x(pc, op, stream); break; - case 0x14: util::stream_format(stream, "beql %s,%s,$%08x", reg[rs], reg[rt], pc + 4 + ((int16_t)op << 2));break; - case 0x15: util::stream_format(stream, "bnel %s,%s,$%08x", reg[rs], reg[rt], pc + 4 + ((int16_t)op << 2));break; - case 0x16: util::stream_format(stream, "blezl %s,%s,$%08x", reg[rs], reg[rt], pc + 4 + ((int16_t)op << 2));break; - case 0x17: util::stream_format(stream, "bgtzl %s,%s,$%08x", reg[rs], reg[rt], pc + 4 + ((int16_t)op << 2));break; + case 0x14: if (rt == 0) + util::stream_format(stream, "beqzl %s,0x%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); + else + util::stream_format(stream, "beql %s,%s,0x%08x", reg[rs], reg[rt], pc + 4 + ((int16_t)op << 2)); + if (rs != 0 || rt != 0) flags = STEP_COND | step_over_extra(1); + break; + case 0x15: if (rt == 0) + util::stream_format(stream, "bnezl %s,0x%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); + else + util::stream_format(stream, "bnel %s,%s,0x%08x", reg[rs], reg[rt], pc + 4 + ((int16_t)op << 2)); + flags = STEP_COND | step_over_extra(1); + break; + case 0x16: util::stream_format(stream, "blezl %s,0x%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); if (rs != 0) flags = STEP_COND | step_over_extra(1); break; + case 0x17: util::stream_format(stream, "bgtzl %s,0x%08x", reg[rs], pc + 4 + ((int16_t)op << 2)); flags = STEP_COND | step_over_extra(1); break; case 0x18: util::stream_format(stream, "daddi %s,%s,%s", reg[rt], reg[rs], signed_16bit(op)); break; case 0x19: util::stream_format(stream, "daddiu %s,%s,%s", reg[rt], reg[rs], signed_16bit(op)); break; case 0x1a: util::stream_format(stream, "ldl %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]); break; @@ -1022,7 +1074,7 @@ offs_t mips3_disassembler::dasm_one(std::ostream &stream, offs_t pc, u32 op) case 0x30: util::stream_format(stream, "ll %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]); break; case 0x31: util::stream_format(stream, "lwc1 %s,%s(%s)", cpreg[1][rt], signed_16bit(op), reg[rs]); break; case 0x32: util::stream_format(stream, "lwc2 %s,%s(%s)", cpreg[2][rt], signed_16bit(op), reg[rs]); break; - case 0x33: util::stream_format(stream, "pref $%x,%s(%s)", rt, signed_16bit(op), reg[rs]); break; + case 0x33: util::stream_format(stream, "pref 0x%x,%s(%s)", rt, signed_16bit(op), reg[rs]); break; case 0x34: util::stream_format(stream, "lld %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]); break; case 0x35: util::stream_format(stream, "ldc1 %s,%s(%s)", cpreg[1][rt], signed_16bit(op), reg[rs]); break; case 0x36: util::stream_format(stream, "ldc2 %s,%s(%s)", cpreg[2][rt], signed_16bit(op), reg[rs]); break; diff --git a/src/devices/cpu/mips/mips3fe.cpp b/src/devices/cpu/mips/mips3fe.cpp index 77b72c9b48b..49b86be681b 100644 --- a/src/devices/cpu/mips/mips3fe.cpp +++ b/src/devices/cpu/mips/mips3fe.cpp @@ -40,7 +40,8 @@ bool mips3_frontend::describe(opcode_desc &desc, const opcode_desc *prev) // compute the physical PC assert((desc.physpc & 3) == 0); - if (!m_mips3->memory_translate(AS_PROGRAM, TRANSLATE_FETCH, desc.physpc)) + address_space *tspace; + if (!m_mips3->memory_translate(AS_PROGRAM, device_memory_interface::TR_FETCH, desc.physpc, tspace)) { // uh-oh: a page fault; leave the description empty and just if this is the first instruction, leave it empty and // mark as needing to validate; otherwise, just end the sequence here @@ -155,6 +156,7 @@ bool mips3_frontend::describe(opcode_desc &desc, const opcode_desc *prev) case 0x22: // LWL case 0x26: // LWR desc.regin[0] |= REGFLAG_R(RTREG); + [[fallthrough]]; case 0x20: // LB case 0x21: // LH case 0x23: // LW @@ -217,6 +219,7 @@ bool mips3_frontend::describe(opcode_desc &desc, const opcode_desc *prev) case 0x33: // PREF if (m_mips3->m_flavor < mips3_device::MIPS3_TYPE_MIPS_IV) return false; + [[fallthrough]]; case 0x2f: // CACHE // effective no-op return true; @@ -253,6 +256,7 @@ bool mips3_frontend::describe_special(uint32_t op, opcode_desc &desc) if (m_mips3->m_flavor < mips3_device::MIPS3_TYPE_MIPS_IV) return false; desc.regin[0] |= REGFLAG_R(RDREG); + [[fallthrough]]; case 0x04: // SLLV case 0x06: // SRLV case 0x07: // SRAV @@ -596,6 +600,7 @@ bool mips3_frontend::describe_cop1(uint32_t op, opcode_desc &desc) case 0x13: // MOVN - MIPS IV if (m_mips3->m_flavor < mips3_device::MIPS3_TYPE_MIPS_IV) return false; + [[fallthrough]]; case 0x00: // ADD case 0x01: // SUB case 0x02: // MUL @@ -608,6 +613,7 @@ bool mips3_frontend::describe_cop1(uint32_t op, opcode_desc &desc) case 0x16: // RSQRT - MIPS IV if (m_mips3->m_flavor < mips3_device::MIPS3_TYPE_MIPS_IV) return false; + [[fallthrough]]; case 0x04: // SQRT case 0x05: // ABS case 0x06: // MOV diff --git a/src/devices/cpu/mips/o2dprintf.hxx b/src/devices/cpu/mips/o2dprintf.hxx index 83915aed0b4..6c5f0af5ac6 100644 --- a/src/devices/cpu/mips/o2dprintf.hxx +++ b/src/devices/cpu/mips/o2dprintf.hxx @@ -1,6 +1,8 @@ // license:BSD-3-Clause // copyright-holders:Ryan Holtz +#include "multibyte.h" + static char digit[] = "0123456789abcdef"; static void dprintdec(int64_t val, bool zeropad, int size) @@ -175,32 +177,31 @@ void dprintoct(uint64_t val, bool zeropad, int pos) #define Ioct 30 #define Loct 63 -static uint64_t dprintf_get_arg64(uint8_t *buf, uint32_t &curr) +static uint64_t dprintf_get_arg64(const uint8_t *buf, uint32_t &curr) { curr = (curr + 3) & ~3; - const uint64_t ret = ((uint64_t)buf[curr+0] << 56) | ((uint64_t)buf[curr+1] << 48) | ((uint64_t)buf[curr+2] << 40) | ((uint64_t)buf[curr+3] << 32) | - ((uint64_t)buf[curr+4] << 24) | ((uint64_t)buf[curr+5] << 16) | ((uint64_t)buf[curr+6] << 8) | buf[curr+7]; + const uint64_t ret = get_u64be(&buf[curr]); curr += 8; return ret; } -static uint32_t dprintf_get_arg32(uint8_t *buf, uint32_t &curr) +static uint32_t dprintf_get_arg32(const uint8_t *buf, uint32_t &curr) { curr = (curr + 3) & ~3; - const uint32_t ret = ((uint32_t)buf[curr+0] << 24) | ((uint32_t)buf[curr+1] << 16) | ((uint32_t)buf[curr+2] << 8) | buf[curr+3]; + const uint32_t ret = get_u32be(&buf[curr]); curr += 4; return ret; } -static uint16_t dprintf_get_arg16(uint8_t *buf, uint32_t &curr) +static uint16_t dprintf_get_arg16(const uint8_t *buf, uint32_t &curr) { curr = (curr + 1) & ~1; - const uint16_t ret = ((uint16_t)buf[curr+0] << 8) | buf[curr+1]; + const uint16_t ret = get_u16be(&buf[curr]); curr += 2; return ret; } -static uint8_t dprintf_get_arg8(uint8_t *buf, uint32_t &curr) +static uint8_t dprintf_get_arg8(const uint8_t *buf, uint32_t &curr) { const uint8_t ret = buf[curr++]; return ret; @@ -213,18 +214,9 @@ void mips3_device::do_o2_dprintf(uint32_t fmt_addr, uint32_t a1, uint32_t a2, ui int idx = 0; uint8_t byte_val = 0; fmt_addr &= 0x1fffffff; - argbuf[0] = (uint8_t)(a1 >> 24); - argbuf[1] = (uint8_t)(a1 >> 16); - argbuf[2] = (uint8_t)(a1 >> 8); - argbuf[3] = (uint8_t)a1; - argbuf[4] = (uint8_t)(a2 >> 24); - argbuf[5] = (uint8_t)(a2 >> 16); - argbuf[6] = (uint8_t)(a2 >> 8); - argbuf[7] = (uint8_t)a2; - argbuf[8] = (uint8_t)(a3 >> 24); - argbuf[9] = (uint8_t)(a3 >> 16); - argbuf[10] = (uint8_t)(a3 >> 8); - argbuf[11] = (uint8_t)a3; + put_u32be(&argbuf[0], a1); + put_u32be(&argbuf[4], a2); + put_u32be(&argbuf[8], a3); stack &= 0x1fffffff; for (int i = 0; i < 4096-12; i++) { diff --git a/src/devices/cpu/mips/ps2vif1.cpp b/src/devices/cpu/mips/ps2vif1.cpp index 30f7aadc0bc..43fc71a2267 100644 --- a/src/devices/cpu/mips/ps2vif1.cpp +++ b/src/devices/cpu/mips/ps2vif1.cpp @@ -131,7 +131,7 @@ void ps2_vif1_device::device_reset() m_unpack_format = FMT_S32; } -READ32_MEMBER(ps2_vif1_device::regs_r) +uint32_t ps2_vif1_device::regs_r(offs_t offset) { uint32_t ret = 0; switch (offset) @@ -235,12 +235,12 @@ READ32_MEMBER(ps2_vif1_device::regs_r) return ret; } -WRITE32_MEMBER(ps2_vif1_device::regs_w) +void ps2_vif1_device::regs_w(offs_t offset, uint32_t data) { logerror("%s: Write: Unknown %08x = %08x\n", machine().describe_context(), 0x10003c00 + (offset << 2), data); } -READ64_MEMBER(ps2_vif1_device::mmio_r) +uint64_t ps2_vif1_device::mmio_r(offs_t offset) { uint64_t ret = 0ULL; if (offset) @@ -254,7 +254,7 @@ READ64_MEMBER(ps2_vif1_device::mmio_r) return ret; } -WRITE64_MEMBER(ps2_vif1_device::mmio_w) +void ps2_vif1_device::mmio_w(offs_t offset, uint64_t data) { if (offset) { @@ -349,7 +349,7 @@ void ps2_vif1_device::execute_run() m_icount = 0; break; } - // Intentional fall-through + [[fallthrough]]; case STAT_MODE_DECODE: decode_vifcode(); break; @@ -359,7 +359,7 @@ void ps2_vif1_device::execute_run() m_icount = 0; break; } - // Intentional fall-through + [[fallthrough]]; case STAT_MODE_DATA: transfer_vifcode_data(); break; diff --git a/src/devices/cpu/mips/ps2vif1.h b/src/devices/cpu/mips/ps2vif1.h index 90ec1417ded..e054b448852 100644 --- a/src/devices/cpu/mips/ps2vif1.h +++ b/src/devices/cpu/mips/ps2vif1.h @@ -33,19 +33,19 @@ public: ps2_vif1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); virtual ~ps2_vif1_device() override; - DECLARE_READ64_MEMBER(mmio_r); - DECLARE_WRITE64_MEMBER(mmio_w); + uint64_t mmio_r(offs_t offset); + void mmio_w(offs_t offset, uint64_t data); - DECLARE_READ32_MEMBER(regs_r); - DECLARE_WRITE32_MEMBER(regs_w); + uint32_t regs_r(offs_t offset); + void regs_w(offs_t offset, uint32_t data); void dma_write(const uint64_t hi, const uint64_t lo); void tag_write(uint32_t *data); bool fifo_available(uint32_t count) const { return (BUFFER_SIZE - m_end) >= count; } protected: - virtual void device_start() override; - virtual void device_reset() override; + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; virtual void execute_run() override; uint32_t calculate_unpack_count(); diff --git a/src/devices/cpu/mips/ps2vu.cpp b/src/devices/cpu/mips/ps2vu.cpp index 1587c92dd3a..8f0b125314d 100644 --- a/src/devices/cpu/mips/ps2vu.cpp +++ b/src/devices/cpu/mips/ps2vu.cpp @@ -14,7 +14,6 @@ #include "video/ps2gif.h" #include "vudasm.h" -#include "debugger.h" DEFINE_DEVICE_TYPE(SONYPS2_VU0, sonyvu0_device, "sonyvu0", "Sony PlayStation 2 VU0") DEFINE_DEVICE_TYPE(SONYPS2_VU1, sonyvu1_device, "sonyvu1", "Sony PlayStation 2 VU1") @@ -78,14 +77,14 @@ ps2_vif1_device* sonyvu1_device::interface() return m_vif.target(); } -READ64_MEMBER(sonyvu1_device::vif_r) +uint64_t sonyvu1_device::vif_r(offs_t offset) { - return m_vif->mmio_r(space, offset, mem_mask); + return m_vif->mmio_r(offset); } -WRITE64_MEMBER(sonyvu1_device::vif_w) +void sonyvu1_device::vif_w(offs_t offset, uint64_t data) { - m_vif->mmio_w(space, offset, data, mem_mask); + m_vif->mmio_w(offset, data); } void sonyvu_device::device_start() @@ -975,12 +974,12 @@ void sonyvu0_device::device_reset() m_cmsar1 = 0; } -READ32_MEMBER(sonyvu0_device::vu1_reg_r) +uint32_t sonyvu0_device::vu1_reg_r(offs_t offset) { return m_vu1_regs[offset]; } -WRITE32_MEMBER(sonyvu0_device::vu1_reg_w) +void sonyvu0_device::vu1_reg_w(offs_t offset, uint32_t data) { m_vu1_regs[offset] = data; } @@ -996,7 +995,7 @@ void sonyvu1_device::device_start() save_item(NAME(m_p)); - state_add(SONYVU1_P, "P", *(uint32_t*)&m_p).formatstr("%17s");; + state_add(SONYVU1_P, "P", *(uint32_t*)&m_p).formatstr("%17s"); } void sonyvu1_device::device_reset() diff --git a/src/devices/cpu/mips/ps2vu.h b/src/devices/cpu/mips/ps2vu.h index c2e99668ffd..245c623ebfa 100644 --- a/src/devices/cpu/mips/ps2vu.h +++ b/src/devices/cpu/mips/ps2vu.h @@ -110,13 +110,12 @@ protected: }; // device-level overrides - virtual void device_start() override; - virtual void device_reset() override; + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; // device_execute_interface overrides - virtual uint32_t execute_min_cycles() const override { return 1; } - virtual uint32_t execute_max_cycles() const override { return 1; } - virtual uint32_t execute_input_lines() const override { return 0; } + virtual uint32_t execute_min_cycles() const noexcept override { return 1; } + virtual uint32_t execute_max_cycles() const noexcept override { return 1; } virtual void execute_run() override; virtual void execute_set_input(int inputnum, int state) override { } @@ -188,18 +187,18 @@ public: ps2_vif1_device* interface(); - DECLARE_READ64_MEMBER(vif_r); - DECLARE_WRITE64_MEMBER(vif_w); + uint64_t vif_r(offs_t offset); + void vif_w(offs_t offset, uint64_t data); protected: - virtual void device_start() override; - virtual void device_reset() override; - virtual void device_add_mconfig(machine_config &config) override; + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; + virtual void device_add_mconfig(machine_config &config) override ATTR_COLD; virtual void state_string_export(const device_state_entry &entry, std::string &str) const override; - void micro_map(address_map &map); - void vu_map(address_map &map); + void micro_map(address_map &map) ATTR_COLD; + void vu_map(address_map &map) ATTR_COLD; void execute_xgkick(uint32_t rs) override; @@ -222,16 +221,16 @@ public: sonyvu0_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); protected: - virtual void device_start() override; - virtual void device_reset() override; + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; - void micro_map(address_map &map); - void vu_map(address_map &map); + void micro_map(address_map &map) ATTR_COLD; + void vu_map(address_map &map) ATTR_COLD; void execute_xgkick(uint32_t rs) override; - DECLARE_READ32_MEMBER(vu1_reg_r); - DECLARE_WRITE32_MEMBER(vu1_reg_w); + uint32_t vu1_reg_r(offs_t offset); + void vu1_reg_w(offs_t offset, uint32_t data); required_device<sonyvu1_device> m_vu1; diff --git a/src/devices/cpu/mips/r4000.cpp b/src/devices/cpu/mips/r4000.cpp index 7b26493f61c..47e2cd784a7 100644 --- a/src/devices/cpu/mips/r4000.cpp +++ b/src/devices/cpu/mips/r4000.cpp @@ -27,13 +27,17 @@ */ #include "emu.h" -#include "debugger.h" #include "r4000.h" + #include "mips3dsm.h" +#include "divtlb.h" +#include "debug/debugcpu.h" + +#include "unicode.h" + #include "softfloat3/source/include/softfloat.h" -#define LOG_GENERAL (1U << 0) #define LOG_TLB (1U << 1) #define LOG_CACHE (1U << 2) #define LOG_EXCEPTION (1U << 3) @@ -41,6 +45,7 @@ #define LOG_STATS (1U << 5) #define VERBOSE (LOG_GENERAL) +#include "logmacro.h" // operating system specific system call logging #define SYSCALL_IRIX53 (1U << 0) @@ -54,11 +59,11 @@ // experimental primary instruction cache #define ICACHE 0 -#include "logmacro.h" +#define SCACHE !(m_cp0[CP0_Config] & CONFIG_SC) #define USE_ABI_REG_NAMES 1 -// cpu instruction fiels +// cpu instruction fields #define RSREG ((op >> 21) & 31) #define RTREG ((op >> 16) & 31) #define RDREG ((op >> 11) & 31) @@ -84,11 +89,20 @@ DEFINE_DEVICE_TYPE(R4000, r4000_device, "r4000", "MIPS R4000") DEFINE_DEVICE_TYPE(R4400, r4400_device, "r4400", "MIPS R4400") DEFINE_DEVICE_TYPE(R4600, r4600_device, "r4600", "QED R4600") +DEFINE_DEVICE_TYPE(R5000, r5000_device, "r5000", "MIPS R5000") + +u32 const r5000_device::s_fcc_masks[8] = { (1U << 23), (1U << 25), (1U << 26), (1U << 27), (1U << 28), (1U << 29), (1U << 30), (1U << 31) }; +u32 const r5000_device::s_fcc_shifts[8] = { 23, 25, 26, 27, 28, 29, 30, 31 }; -r4000_base_device::r4000_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u32 prid, u32 fcr, cache_size_t icache_size, cache_size_t dcache_size) +r4000_base_device::r4000_base_device(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock, u32 prid, u32 fcr, cache_size icache_size, cache_size dcache_size, unsigned m32, unsigned m64, unsigned d32, unsigned d64, bool timer_interrupt_disabled) : cpu_device(mconfig, type, tag, owner, clock) , m_program_config_le("program", ENDIANNESS_LITTLE, 64, 32) , m_program_config_be("program", ENDIANNESS_BIG, 64, 32) + , m_hilo_cycles{ m32, m64, d32, d64 } + , m_r{} + , m_cp0{} + , m_timer_interrupt_disabled(timer_interrupt_disabled) + , m_f{} , m_fcr0(fcr) { m_cp0[CP0_PRId] = prid; @@ -175,7 +189,9 @@ void r4000_base_device::device_start() set_icountptr(m_icount); - m_cp0_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(r4000_base_device::cp0_timer_callback), this)); + m_hard_reset = true; + + m_cp0_timer = timer_alloc(FUNC(r4000_base_device::cp0_timer_callback), this); // compute icache line selection mask and allocate tag and data unsigned const config_ic = (m_cp0[CP0_Config] & CONFIG_IC) >> 9; @@ -183,32 +199,49 @@ void r4000_base_device::device_start() m_icache_mask_hi = (0x1000U << config_ic) - 1; m_icache_tag = std::make_unique<u32[]>(0x100U << config_ic); m_icache_data = std::make_unique<u32 []>((0x1000U << config_ic) >> 2); + + R4000_ENDIAN_LE_BE(accessors(m_le), accessors(m_be)); + + if (SCACHE) + save_pointer(NAME(m_scache_tag), m_scache_tag_size); } void r4000_base_device::device_reset() { + if (!m_hard_reset) + { + m_cp0[CP0_Status] = SR_BEV | SR_ERL | SR_SR; + m_cp0[CP0_ErrorEPC] = m_pc; + } + else + m_cp0[CP0_Status] = SR_BEV | SR_ERL; + + m_hilo_delay = 0; m_branch_state = NONE; m_pc = s64(s32(0xbfc00000)); + m_r[0] = 0; - m_cp0[CP0_Status] = SR_BEV | SR_ERL; m_cp0[CP0_Wired] = 0; m_cp0[CP0_Compare] = 0; m_cp0[CP0_Count] = 0; m_cp0_timer_zero = total_cycles(); + m_hard_reset = false; m_ll_active = false; m_bus_error = false; + m_cp0[CP0_Cause] = 0; + m_cp0[CP0_WatchLo] = 0; m_cp0[CP0_WatchHi] = 0; // initialize tlb mru index with identity mapping - for (unsigned i = 0; i < ARRAY_LENGTH(m_tlb); i++) + for (unsigned i = 0; i < std::size(m_tlb); i++) { - m_tlb_mru[TRANSLATE_READ][i] = i; - m_tlb_mru[TRANSLATE_WRITE][i] = i; - m_tlb_mru[TRANSLATE_FETCH][i] = i; + m_tlb_mru[TR_READ][i] = i; + m_tlb_mru[TR_WRITE][i] = i; + m_tlb_mru[TR_FETCH][i] = i; } // initialize statistics @@ -235,12 +268,13 @@ device_memory_interface::space_config_vector r4000_base_device::memory_space_con }; } -bool r4000_base_device::memory_translate(int spacenum, int intention, offs_t &address) +bool r4000_base_device::memory_translate(int spacenum, int intention, offs_t &address, address_space *&target_space) { + target_space = &space(spacenum); // FIXME: address truncation u64 placeholder = s32(address); - translate_t const t = translate(intention, placeholder); + translate_result const t = translate(intention, true, placeholder); if (t == ERROR || t == MISS) return false; @@ -256,7 +290,7 @@ std::unique_ptr<util::disasm_interface> r4000_base_device::create_disassembler() void r4000_base_device::execute_run() { - while (m_icount-- > 0) + while (m_icount > 0) { debugger_instruction_hook(m_pc); @@ -274,31 +308,32 @@ void r4000_base_device::execute_run() }); // update pc and branch state - switch (m_branch_state) + switch (m_branch_state & STATE) { case NONE: m_pc += 4; break; - case DELAY: + case BRANCH: + m_pc = m_branch_state & TARGET; m_branch_state = NONE; - m_pc = m_branch_target; break; - case BRANCH: - m_branch_state = DELAY; + case DELAY: m_pc += 4; - break; - - case EXCEPTION: - m_branch_state = NONE; + m_branch_state = (m_branch_state & TARGET) | BRANCH; break; case NULLIFY: - m_branch_state = NONE; m_pc += 8; + m_branch_state = NONE; break; } + + if (m_hilo_delay) + m_hilo_delay--; + + m_icount--; } } @@ -338,12 +373,12 @@ void r4000_base_device::cpu_execute(u32 const op) m_r[RDREG] = s64(s32(m_r[RTREG]) >> (m_r[RSREG] & 31)); break; case 0x08: // JR - m_branch_state = BRANCH; - m_branch_target = ADDR(m_r[RSREG], 0); + // FIXME: address error if low bits are set + m_branch_state = ADDR(m_r[RSREG], 0) | DELAY; break; case 0x09: // JALR - m_branch_state = BRANCH; - m_branch_target = ADDR(m_r[RSREG], 0); + // FIXME: address error if low bits are set + m_branch_state = ADDR(m_r[RSREG], 0) | DELAY; m_r[RDREG] = ADDR(m_pc, 8); break; //case 0x0a: // * @@ -407,12 +442,22 @@ void r4000_base_device::cpu_execute(u32 const op) break; case 0x10: // MFHI m_r[RDREG] = m_hi; + if (m_hilo_delay) + { + m_icount -= m_hilo_delay; + m_hilo_delay = 0; + } break; case 0x11: // MTHI m_hi = m_r[RSREG]; break; case 0x12: // MFLO m_r[RDREG] = m_lo; + if (m_hilo_delay) + { + m_icount -= m_hilo_delay; + m_hilo_delay = 0; + } break; case 0x13: // MTLO m_lo = m_r[RSREG]; @@ -433,6 +478,7 @@ void r4000_base_device::cpu_execute(u32 const op) m_lo = s64(s32(product)); m_hi = s64(s32(product >> 32)); + m_hilo_delay = m_hilo_cycles[0]; } break; case 0x19: // MULTU @@ -441,6 +487,7 @@ void r4000_base_device::cpu_execute(u32 const op) m_lo = s64(s32(product)); m_hi = s64(s32(product >> 32)); + m_hilo_delay = m_hilo_cycles[0]; } break; case 0x1a: // DIV @@ -448,6 +495,7 @@ void r4000_base_device::cpu_execute(u32 const op) { m_lo = s64(s32(m_r[RSREG]) / s32(m_r[RTREG])); m_hi = s64(s32(m_r[RSREG]) % s32(m_r[RTREG])); + m_hilo_delay = m_hilo_cycles[2]; } break; case 0x1b: // DIVU @@ -455,19 +503,23 @@ void r4000_base_device::cpu_execute(u32 const op) { m_lo = s64(s32(u32(m_r[RSREG]) / u32(m_r[RTREG]))); m_hi = s64(s32(u32(m_r[RSREG]) % u32(m_r[RTREG]))); + m_hilo_delay = m_hilo_cycles[2]; } break; case 0x1c: // DMULT - m_lo = mul_64x64(m_r[RSREG], m_r[RTREG], reinterpret_cast<s64 *>(&m_hi)); + m_lo = mul_64x64(m_r[RSREG], m_r[RTREG], *reinterpret_cast<s64 *>(&m_hi)); + m_hilo_delay = m_hilo_cycles[1]; break; case 0x1d: // DMULTU - m_lo = mulu_64x64(m_r[RSREG], m_r[RTREG], &m_hi); + m_lo = mulu_64x64(m_r[RSREG], m_r[RTREG], m_hi); + m_hilo_delay = m_hilo_cycles[1]; break; case 0x1e: // DDIV if (m_r[RTREG]) { m_lo = s64(m_r[RSREG]) / s64(m_r[RTREG]); m_hi = s64(m_r[RSREG]) % s64(m_r[RTREG]); + m_hilo_delay = m_hilo_cycles[3]; } break; case 0x1f: // DDIVU @@ -475,6 +527,7 @@ void r4000_base_device::cpu_execute(u32 const op) { m_lo = m_r[RSREG] / m_r[RTREG]; m_hi = m_r[RSREG] % m_r[RTREG]; + m_hilo_delay = m_hilo_cycles[3]; } break; case 0x20: // ADD @@ -601,10 +654,7 @@ void r4000_base_device::cpu_execute(u32 const op) break; default: - // * Operation codes marked with an asterisk cause reserved - // instruction exceptions in all current implementations and are - // reserved for future versions of the architecture. - cpu_exception(EXCEPTION_RI); + handle_reserved_instruction(op); break; } break; @@ -613,33 +663,21 @@ void r4000_base_device::cpu_execute(u32 const op) { case 0x00: // BLTZ if (s64(m_r[RSREG]) < 0) - { - m_branch_state = BRANCH; - m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2); - } + m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY; break; case 0x01: // BGEZ if (s64(m_r[RSREG]) >= 0) - { - m_branch_state = BRANCH; - m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2); - } + m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY; break; case 0x02: // BLTZL if (s64(m_r[RSREG]) < 0) - { - m_branch_state = BRANCH; - m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2); - } + m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY; else m_branch_state = NULLIFY; break; case 0x03: // BGEZL if (s64(m_r[RSREG]) >= 0) - { - m_branch_state = BRANCH; - m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2); - } + m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY; else m_branch_state = NULLIFY; break; @@ -675,36 +713,24 @@ void r4000_base_device::cpu_execute(u32 const op) //case 0x0f: // * case 0x10: // BLTZAL if (s64(m_r[RSREG]) < 0) - { - m_branch_state = BRANCH; - m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2); - } + m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY; m_r[31] = ADDR(m_pc, 8); break; case 0x11: // BGEZAL if (s64(m_r[RSREG]) >= 0) - { - m_branch_state = BRANCH; - m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2); - } + m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY; m_r[31] = ADDR(m_pc, 8); break; case 0x12: // BLTZALL if (s64(m_r[RSREG]) < 0) - { - m_branch_state = BRANCH; - m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2); - } + m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY; else m_branch_state = NULLIFY; m_r[31] = ADDR(m_pc, 8); break; case 0x13: // BGEZALL if (s64(m_r[RSREG]) >= 0) - { - m_branch_state = BRANCH; - m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2); - } + m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY; else m_branch_state = NULLIFY; m_r[31] = ADDR(m_pc, 8); @@ -726,46 +752,32 @@ void r4000_base_device::cpu_execute(u32 const op) // * Operation codes marked with an asterisk cause reserved // instruction exceptions in all current implementations and are // reserved for future versions of the architecture. - cpu_exception(EXCEPTION_RI); + handle_reserved_instruction(op); break; } break; case 0x02: // J - m_branch_state = BRANCH; - m_branch_target = (ADDR(m_pc, 4) & ~0x0fffffffULL) | ((op & 0x03ffffffU) << 2); + m_branch_state = (ADDR(m_pc, 4) & ~0x0fffffffULL) | ((op & 0x03ffffffU) << 2) | DELAY; break; case 0x03: // JAL - m_branch_state = BRANCH; - m_branch_target = (ADDR(m_pc, 4) & ~0x0fffffffULL) | ((op & 0x03ffffffU) << 2); + m_branch_state = (ADDR(m_pc, 4) & ~0x0fffffffULL) | ((op & 0x03ffffffU) << 2) | DELAY; m_r[31] = ADDR(m_pc, 8); break; case 0x04: // BEQ if (m_r[RSREG] == m_r[RTREG]) - { - m_branch_state = BRANCH; - m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2); - } + m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY; break; case 0x05: // BNE if (m_r[RSREG] != m_r[RTREG]) - { - m_branch_state = BRANCH; - m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2); - } + m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY; break; case 0x06: // BLEZ if (s64(m_r[RSREG]) <= 0) - { - m_branch_state = BRANCH; - m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2); - } + m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY; break; case 0x07: // BGTZ if (s64(m_r[RSREG]) > 0) - { - m_branch_state = BRANCH; - m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2); - } + m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY; break; case 0x08: // ADDI { @@ -808,40 +820,30 @@ void r4000_base_device::cpu_execute(u32 const op) case 0x12: // COP2 cp2_execute(op); break; - //case 0x13: // * + case 0x13: // COP1X + cp1x_execute(op); + break; case 0x14: // BEQL if (m_r[RSREG] == m_r[RTREG]) - { - m_branch_state = BRANCH; - m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2); - } + m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY; else m_branch_state = NULLIFY; break; case 0x15: // BNEL if (m_r[RSREG] != m_r[RTREG]) - { - m_branch_state = BRANCH; - m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2); - } + m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY; else m_branch_state = NULLIFY; break; case 0x16: // BLEZL if (s64(m_r[RSREG]) <= 0) - { - m_branch_state = BRANCH; - m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2); - } + m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY; else m_branch_state = NULLIFY; break; case 0x17: // BGTZL if (s64(m_r[RSREG]) > 0) - { - m_branch_state = BRANCH; - m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2); - } + m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY; else m_branch_state = NULLIFY; break; @@ -951,75 +953,7 @@ void r4000_base_device::cpu_execute(u32 const op) cpu_swr(op); break; case 0x2f: // CACHE - if ((SR & SR_KSU) && !(SR & SR_CU0) && !(SR & (SR_EXL | SR_ERL))) - { - cpu_exception(EXCEPTION_CP0); - break; - } - - switch ((op >> 16) & 0x1f) - { - case 0x00: // index invalidate (I) - if (ICACHE) - { - m_icache_tag[(ADDR(m_r[RSREG], s16(op)) & m_icache_mask_hi) >> m_icache_shift] &= ~ICACHE_V; - break; - } - - case 0x04: // index load tag (I) - if (ICACHE) - { - u32 const tag = m_icache_tag[(ADDR(m_r[RSREG], s16(op)) & m_icache_mask_hi) >> m_icache_shift]; - - m_cp0[CP0_TagLo] = ((tag & ICACHE_PTAG) << 8) | ((tag & ICACHE_V) >> 18) | ((tag & ICACHE_P) >> 25); - m_cp0[CP0_ECC] = 0; // data ecc or parity - - break; - } - - case 0x08: // index store tag (I) - if (ICACHE) - { - // FIXME: compute parity - m_icache_tag[(ADDR(m_r[RSREG], s16(op)) & m_icache_mask_hi) >> m_icache_shift] = - (m_cp0[CP0_TagLo] & TAGLO_PTAGLO) >> 8 | (m_cp0[CP0_TagLo] & TAGLO_PSTATE) << 18; - - break; - } - - case 0x01: // index writeback invalidate (D) - case 0x02: // index invalidate (SI) - case 0x03: // index writeback invalidate (SD) - - case 0x05: // index load tag (D) - case 0x06: // index load tag (SI) - case 0x07: // index load tag (SI) - - case 0x09: // index store tag (D) - case 0x0a: // index store tag (SI) - case 0x0b: // index store tag (SD) - - case 0x0d: // create dirty exclusive (D) - case 0x0f: // create dirty exclusive (SD) - - case 0x10: // hit invalidate (I) - case 0x11: // hit invalidate (D) - case 0x12: // hit invalidate (SI) - case 0x13: // hit invalidate (SD) - - case 0x14: // fill (I) - case 0x15: // hit writeback invalidate (D) - case 0x17: // hit writeback invalidate (SD) - - case 0x18: // hit writeback (I) - case 0x19: // hit writeback (D) - case 0x1b: // hit writeback (SD) - - case 0x1e: // hit set virtual (SI) - case 0x1f: // hit set virtual (SD) - //LOGMASKED(LOG_CACHE, "cache 0x%08x unimplemented (%s)\n", op, machine().describe_context()); - break; - } + cp0_cache(op); break; case 0x30: // LL load_linked<u32>(ADDR(m_r[RSREG], s16(op)), @@ -1036,7 +970,6 @@ void r4000_base_device::cpu_execute(u32 const op) case 0x32: // LWC2 cp2_execute(op); break; - //case 0x33: // * case 0x34: // LLD load_linked<u64>(ADDR(m_r[RSREG], s16(op)), [this, op](u64 address, u64 data) @@ -1106,11 +1039,50 @@ void r4000_base_device::cpu_execute(u32 const op) // * Operation codes marked with an asterisk cause reserved instruction // exceptions in all current implementations and are reserved for future // versions of the architecture. - cpu_exception(EXCEPTION_RI); + handle_reserved_instruction(op); break; } } +void r4000_base_device::handle_reserved_instruction(u32 const op) +{ + // Unhandled operation codes cause reserved instruction + // exceptions in all current implementations and are + // reserved for future versions of the architecture. + cpu_exception(EXCEPTION_RI); +} + +void r5000_device::handle_reserved_instruction(u32 const op) +{ + switch (op >> 26) + { + case 0x00: // SPECIAL + switch (op & 0x3f) + { + case 0x01: // MOVT / MOVF + if (((m_fcr31 >> s_fcc_shifts[(op >> 18) & 7]) & 1) == ((op >> 16) & 1)) + { + if (RDREG) + { + m_r[RDREG] = m_r[RSREG]; + } + } + return; + case 0x0a: // MOVZ + if (m_r[RTREG] == 0) { if (RDREG) m_r[RDREG] = m_r[RSREG]; } + return; + case 0x0b: // MOVN + if (m_r[RTREG] != 0) { if (RDREG) m_r[RDREG] = m_r[RSREG]; } + return; + } + break; + case 0x33: // PREF (effective no-op) + return; + } + + r4000_base_device::handle_reserved_instruction(op); +} + void r4000_base_device::cpu_exception(u32 exception, u16 const vector) { if (exception != EXCEPTION_INT) @@ -1122,8 +1094,8 @@ void r4000_base_device::cpu_exception(u32 exception, u16 const vector) CAUSE = (CAUSE & CAUSE_IP) | exception; - // if in a branch delay slot, restart at the branch instruction - if (m_branch_state == DELAY) + // if executing a delay slot instruction, restart from the branch + if ((m_branch_state & STATE) == BRANCH) { m_cp0[CP0_EPC] -= 4; CAUSE |= CAUSE_BD; @@ -1134,10 +1106,20 @@ void r4000_base_device::cpu_exception(u32 exception, u16 const vector) else CAUSE = (CAUSE & (CAUSE_BD | CAUSE_IP)) | exception; - m_branch_state = EXCEPTION; - m_pc = ((SR & SR_BEV) ? s64(s32(0xbfc00200)) : s64(s32(0x80000000))) + vector; + m_branch_state = (((SR & SR_BEV) ? s64(s32(0xbfc00200)) : s64(s32(0x80000000))) + vector) | BRANCH; - if (exception != EXCEPTION_INT) + if (exception == EXCEPTION_INT) + { + if (debugger_enabled()) + { + // notify the debugger of the first pending hardware interrupt + u32 const iphw = CAUSE & SR & CAUSE_IPHW; + + if (iphw) + debug()->interrupt_hook(22 - count_leading_zeros_32((iphw - 1) & ~iphw), m_pc); + } + } + else debugger_exception_hook(exception); } @@ -1146,7 +1128,7 @@ void r4000_base_device::cpu_lwl(u32 const op) u64 const offset = ADDR(m_r[RSREG], s16(op)); unsigned const shift = ((offset & 3) ^ R4000_ENDIAN_LE_BE(3, 0)) << 3; - load<u32>(offset & ~3, + load<u32, false>(offset, [this, op, shift](u32 const data) { m_r[RTREG] = s32((m_r[RTREG] & ~u32(~u32(0) << shift)) | (data << shift)); @@ -1158,7 +1140,7 @@ void r4000_base_device::cpu_lwr(u32 const op) u64 const offset = ADDR(m_r[RSREG], s16(op)); unsigned const shift = ((offset & 0x3) ^ R4000_ENDIAN_LE_BE(0, 3)) << 3; - load<u32>(offset & ~3, + load<u32, false>(offset, [this, op, shift](u32 const data) { m_r[RTREG] = s32((m_r[RTREG] & ~u32(~u32(0) >> shift)) | (data >> shift)); @@ -1170,7 +1152,7 @@ void r4000_base_device::cpu_swl(u32 const op) u64 const offset = ADDR(m_r[RSREG], s16(op)); unsigned const shift = ((offset & 3) ^ R4000_ENDIAN_LE_BE(3, 0)) << 3; - store<u32>(offset & ~3, u32(m_r[RTREG]) >> shift, ~u32(0) >> shift); + store<u32, false>(offset, u32(m_r[RTREG]) >> shift, ~u32(0) >> shift); } void r4000_base_device::cpu_swr(u32 const op) @@ -1178,7 +1160,7 @@ void r4000_base_device::cpu_swr(u32 const op) u64 const offset = ADDR(m_r[RSREG], s16(op)); unsigned const shift = ((offset & 3) ^ R4000_ENDIAN_LE_BE(0, 3)) << 3; - store<u32>(offset & ~3, u32(m_r[RTREG]) << shift, ~u32(0) << shift); + store<u32, false>(offset, u32(m_r[RTREG]) << shift, ~u32(0) << shift); } void r4000_base_device::cpu_ldl(u32 const op) @@ -1186,7 +1168,7 @@ void r4000_base_device::cpu_ldl(u32 const op) u64 const offset = ADDR(m_r[RSREG], s16(op)); unsigned const shift = ((offset & 7) ^ R4000_ENDIAN_LE_BE(7, 0)) << 3; - load<u64>(offset & ~7, + load<u64, false>(offset, [this, op, shift](u64 const data) { m_r[RTREG] = (m_r[RTREG] & ~u64(~u64(0) << shift)) | (data << shift); @@ -1198,7 +1180,7 @@ void r4000_base_device::cpu_ldr(u32 const op) u64 const offset = ADDR(m_r[RSREG], s16(op)); unsigned const shift = ((offset & 7) ^ R4000_ENDIAN_LE_BE(0, 7)) << 3; - load<u64>(offset & ~7, + load<u64, false>(offset, [this, op, shift](u64 const data) { m_r[RTREG] = (m_r[RTREG] & ~u64(~u64(0) >> shift)) | (data >> shift); @@ -1210,7 +1192,7 @@ void r4000_base_device::cpu_sdl(u32 const op) u64 const offset = ADDR(m_r[RSREG], s16(op)); unsigned const shift = ((offset & 7) ^ R4000_ENDIAN_LE_BE(7, 0)) << 3; - store<u64>(offset & ~7, m_r[RTREG] >> shift, ~u64(0) >> shift); + store<u64, false>(offset, m_r[RTREG] >> shift, ~u64(0) >> shift); } void r4000_base_device::cpu_sdr(u32 const op) @@ -1218,7 +1200,129 @@ void r4000_base_device::cpu_sdr(u32 const op) u64 const offset = ADDR(m_r[RSREG], s16(op)); unsigned const shift = ((offset & 7) ^ R4000_ENDIAN_LE_BE(0, 7)) << 3; - store<u64>(offset & ~7, m_r[RTREG] << shift, ~u64(0) << shift); + store<u64, false>(offset, m_r[RTREG] << shift, ~u64(0) << shift); +} + +void r4000_base_device::cp0_cache(u32 const op) +{ + if ((SR & SR_KSU) && !(SR & SR_CU0) && !(SR & (SR_EXL | SR_ERL))) + { + cpu_exception(EXCEPTION_CP0); + return; + } + + switch ((op >> 16) & 0x1f) + { + case 0x00: // index invalidate (I) + if (ICACHE) + { + m_icache_tag[(ADDR(m_r[RSREG], s16(op)) & m_icache_mask_hi) >> m_icache_shift] &= ~ICACHE_V; + break; + } + [[fallthrough]]; + case 0x04: // index load tag (I) + if (ICACHE) + { + u32 const tag = m_icache_tag[(ADDR(m_r[RSREG], s16(op)) & m_icache_mask_hi) >> m_icache_shift]; + + m_cp0[CP0_TagLo] = ((tag & ICACHE_PTAG) << 8) | ((tag & ICACHE_V) >> 18) | ((tag & ICACHE_P) >> 25); + m_cp0[CP0_ECC] = 0; // data ecc or parity + + break; + } + [[fallthrough]]; + case 0x08: // index store tag (I) + if (ICACHE) + { + // FIXME: compute parity + m_icache_tag[(ADDR(m_r[RSREG], s16(op)) & m_icache_mask_hi) >> m_icache_shift] = + (m_cp0[CP0_TagLo] & TAGLO_PTAGLO) >> 8 | (m_cp0[CP0_TagLo] & TAGLO_PSTATE) << 18; + break; + } + [[fallthrough]]; + case 0x01: // index writeback invalidate (D) + case 0x02: // index invalidate (SI) + case 0x03: // index writeback invalidate (SD) + case 0x05: // index load tag (D) + //LOGMASKED(LOG_CACHE, "cache 0x%08x unimplemented (%s)\n", op, machine().describe_context()); + break; + case 0x06: // index load tag (SI) + case 0x07: // index load tag (SD) + if (SCACHE) + { + // TODO: translation type for CACHE instruction? Read seems reasonable since only the tag is changing here + u64 physical_address = ADDR(m_r[RSREG], s16(op)); + translate_result const t = translate(TR_READ, false, physical_address); + if (t == ERROR || t == MISS) + return; + + u32 const index = (physical_address & m_scache_tag_mask) >> m_scache_line_index; + if (index < m_scache_tag_size) + { + // TODO: Load the ECC register here + u32 const tag = m_scache_tag[index]; + u32 const cs = (tag & SCACHE_CS) >> 22; + u32 const stag = tag & SCACHE_STAG; + u32 const pidx = (tag & SCACHE_PIDX) >> 19; + m_cp0[CP0_TagLo] = (stag << 13) | (cs << 10) | (pidx << 7); + } + else + fatalerror("r4000 scache load tag index out of range!"); + } + else + LOGMASKED(LOG_CACHE, "cache 0x%08x called without scache enabled (%s)\n", op, machine().describe_context()); + break; + case 0x09: // index store tag (D) + //LOGMASKED(LOG_CACHE, "cache 0x%08x unimplemented (%s)\n", op, machine().describe_context()); + break; + case 0x0a: // index store tag (SI) + case 0x0b: // index store tag (SD) + if (SCACHE) + { + // TODO: translation type for CACHE instruction? Read seems reasonable since only the tag is changing here + u64 const virtual_address = ADDR(m_r[RSREG], s16(op)); + u64 physical_address = virtual_address; + translate_result const t = translate(TR_READ, false, physical_address); + if (t == ERROR || t == MISS) + return; + + u64 const index = (physical_address & m_scache_tag_mask) >> m_scache_line_index; + if (index < m_scache_tag_size) + { + // TODO: Calculate ECC bits here + u64 const tag_lo = m_cp0[CP0_TagLo]; + u32 const cs = (tag_lo & TAGLO_CS) >> 10; + u32 const stag = (tag_lo & TAGLO_STAG) >> 13; + u32 const pidx = (virtual_address & 0x7000) >> 12; + m_scache_tag[index] = cs << 22 | pidx << 19 | stag; + } + else + fatalerror("r4000 scache store tag index out of range!"); + } + else + LOGMASKED(LOG_CACHE, "cache 0x%08x called without scache enabled (%s)\n", op, machine().describe_context()); + break; + case 0x0d: // create dirty exclusive (D) + case 0x0f: // create dirty exclusive (SD) + + case 0x10: // hit invalidate (I) + case 0x11: // hit invalidate (D) + case 0x12: // hit invalidate (SI) + case 0x13: // hit invalidate (SD) + + case 0x14: // fill (I) + case 0x15: // hit writeback invalidate (D) + case 0x17: // hit writeback invalidate (SD) + + case 0x18: // hit writeback (I) + case 0x19: // hit writeback (D) + case 0x1b: // hit writeback (SD) + + case 0x1e: // hit set virtual (SI) + case 0x1f: // hit set virtual (SD) + //LOGMASKED(LOG_CACHE, "cache 0x%08x unimplemented (%s)\n", op, machine().describe_context()); + break; + } } void r4000_base_device::cp0_execute(u32 const op) @@ -1312,14 +1416,12 @@ void r4000_base_device::cp0_execute(u32 const op) case 0x18: // ERET if (SR & SR_ERL) { - m_branch_state = EXCEPTION; - m_pc = m_cp0[CP0_ErrorEPC]; + m_branch_state = m_cp0[CP0_ErrorEPC] | BRANCH; SR &= ~SR_ERL; } else { - m_branch_state = EXCEPTION; - m_pc = m_cp0[CP0_EPC]; + m_branch_state = m_cp0[CP0_EPC] | BRANCH; SR &= ~SR_EXL; } @@ -1353,10 +1455,10 @@ u64 r4000_base_device::cp0_get(unsigned const reg) { u8 const wired = m_cp0[CP0_Wired] & 0x3f; - if (wired < ARRAY_LENGTH(m_tlb)) - return ((total_cycles() - m_cp0_timer_zero) % (ARRAY_LENGTH(m_tlb) - wired) + wired) & 0x3f; + if (wired < std::size(m_tlb)) + return ((total_cycles() - m_cp0_timer_zero) % (std::size(m_tlb) - wired) + wired) & 0x3f; else - return ARRAY_LENGTH(m_tlb) - 1; + return std::size(m_tlb) - 1; } break; @@ -1398,7 +1500,8 @@ void r4000_base_device::cp0_set(unsigned const reg, u64 const data) break; case CP0_Compare: m_cp0[CP0_Compare] = u32(data); - CAUSE &= ~CAUSE_IPEX5; + if (!m_timer_interrupt_disabled) + CAUSE &= ~CAUSE_IPEX5; cp0_update_timer(true); break; @@ -1467,22 +1570,23 @@ void r4000_base_device::cp0_tlbr() { u8 const index = m_cp0[CP0_Index] & 0x3f; - if (index < ARRAY_LENGTH(m_tlb)) + if (index < std::size(m_tlb)) { - tlb_entry_t const &entry = m_tlb[index]; + tlb_entry const &entry = m_tlb[index]; m_cp0[CP0_PageMask] = entry.mask; m_cp0[CP0_EntryHi] = entry.vpn; - m_cp0[CP0_EntryLo0] = entry.pfn[0]; - m_cp0[CP0_EntryLo1] = entry.pfn[1]; + u64 const global = (entry.vpn & EH_G) ? EL_G : 0x0; + m_cp0[CP0_EntryLo0] = entry.pfn[0] | global; + m_cp0[CP0_EntryLo1] = entry.pfn[1] | global; } } void r4000_base_device::cp0_tlbwi(u8 const index) { - if (index < ARRAY_LENGTH(m_tlb)) + if (index < std::size(m_tlb)) { - tlb_entry_t &entry = m_tlb[index]; + tlb_entry &entry = m_tlb[index]; entry.mask = m_cp0[CP0_PageMask]; entry.vpn = m_cp0[CP0_EntryHi] & EH_WM; @@ -1491,7 +1595,7 @@ void r4000_base_device::cp0_tlbwi(u8 const index) entry.pfn[0] = m_cp0[CP0_EntryLo0] & EL_WM; entry.pfn[1] = m_cp0[CP0_EntryLo1] & EL_WM; - entry.low_bit = 32 - count_leading_zeros((entry.mask >> 1) | 0xfff); + entry.low_bit = 32 - count_leading_zeros_32((entry.mask >> 1) | 0xfff); LOGMASKED(LOG_TLB, "tlb write index %02d mask 0x%016x vpn2 0x%016x %c asid 0x%02x pfn0 0x%016x %c%c pfn1 0x%016x %c%c (%s)\n", index, entry.mask, @@ -1505,9 +1609,9 @@ void r4000_base_device::cp0_tlbwi(u8 const index) void r4000_base_device::cp0_tlbwr() { u8 const wired = m_cp0[CP0_Wired] & 0x3f; - u8 const unwired = ARRAY_LENGTH(m_tlb) - wired; + u8 const unwired = std::size(m_tlb) - wired; - u8 const index = (unwired > 0) ? ((total_cycles() - m_cp0_timer_zero) % unwired + wired) & 0x3f : (ARRAY_LENGTH(m_tlb) - 1); + u8 const index = (unwired > 0) ? ((total_cycles() - m_cp0_timer_zero) % unwired + wired) & 0x3f : (std::size(m_tlb) - 1); cp0_tlbwi(index); } @@ -1515,9 +1619,9 @@ void r4000_base_device::cp0_tlbwr() void r4000_base_device::cp0_tlbp() { m_cp0[CP0_Index] = 0x80000000; - for (u8 index = 0; index < ARRAY_LENGTH(m_tlb); index++) + for (u8 index = 0; index < std::size(m_tlb); index++) { - tlb_entry_t const &entry = m_tlb[index]; + tlb_entry const &entry = m_tlb[index]; u64 const mask = (cp0_64() ? EH_R | (EH_VPN2_64 & ~entry.mask) : (EH_VPN2_32 & ~entry.mask)) | ((entry.vpn & EH_G) ? 0 : EH_ASID); @@ -1530,13 +1634,16 @@ void r4000_base_device::cp0_tlbp() } if (m_cp0[CP0_Index] == 0x80000000) - LOGMASKED(LOG_TLB, "tlbp miss 0x%08x\n", m_cp0[CP0_EntryHi]); + LOGMASKED(LOG_TLB, "tlbp miss 0x%08x (%s)\n", m_cp0[CP0_EntryHi], machine().describe_context()); else - LOGMASKED(LOG_TLB, "tlbp hit 0x%08x index %02d\n", m_cp0[CP0_EntryHi], m_cp0[CP0_Index]); + LOGMASKED(LOG_TLB, "tlbp hit 0x%08x index %02d (%s)\n", m_cp0[CP0_EntryHi], m_cp0[CP0_Index], machine().describe_context()); } void r4000_base_device::cp0_update_timer(bool start) { + if (m_timer_interrupt_disabled) + return; + if (start || m_cp0_timer->enabled()) { u32 const count = (total_cycles() - m_cp0_timer_zero) / 2; @@ -1596,6 +1703,486 @@ template <> bool r4000_base_device::cp1_op<float64_t>(float64_t op) return true; } +void r5000_device::cp1_execute(u32 const op) +{ + if (!(SR & SR_CU1)) + { + cpu_exception(EXCEPTION_CP1); + return; + } + + softfloat_exceptionFlags = 0; + switch (op >> 26) + { + case 0x11: // COP1 + switch ((op >> 21) & 0x1f) + { + case 0x10: // S + { + switch (op & 0x3f) + { + case 0x11: // MOVF.S / MOVT.S + if (((m_fcr31 >> s_fcc_shifts[(op >> 18) & 7]) & 1) == ((op >> 16) & 1)) + cp1_mov_s(op); + return; + case 0x12: // MOVZ.S + if (m_r[RTREG] == 0) + cp1_mov_s(op); + return; + case 0x13: // MOVN.S + if (m_r[RTREG] != 0) + cp1_mov_s(op); + return; + case 0x15: // RECIP.S + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + float32_t const fs = float32_t{ u32(m_f[FSREG]) }; + + if (cp1_op(fs)) + { + cp1_set(FDREG, f32_div(i32_to_f32(1), fs).v); + } + } + return; + case 0x16: // RSQRT.S + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + float32_t const fs = float32_t{ u32(m_f[FSREG]) }; + + if (cp1_op(fs)) + { + cp1_set(FDREG, f32_div(i32_to_f32(1), f32_sqrt(fs)).v); + } + } + return; + case 0x30: // C.F.S (false) + if ((SR & SR_FR) || !(op & ODD_REGS)) + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + return; + case 0x31: // C.UN.S (unordered) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + // detect unordered + f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }); + if (softfloat_exceptionFlags & softfloat_flag_invalid) + m_fcr31 |= s_fcc_masks[(op >> 18) & 7]; + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + } + return; + case 0x32: // C.EQ.S (equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) })) + m_fcr31 |= s_fcc_masks[(op >> 18) & 7]; + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + } + return; + case 0x33: // C.UEQ.S (unordered equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= s_fcc_masks[(op >> 18) & 7]; + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + } + return; + case 0x34: // C.OLT.S (less than) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f32_lt(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) })) + m_fcr31 |= s_fcc_masks[(op >> 18) & 7]; + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + } + return; + case 0x35: // C.ULT.S (unordered less than) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f32_lt(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= s_fcc_masks[(op >> 18) & 7]; + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + } + return; + case 0x36: // C.OLE.S (less than or equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f32_le(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) })) + m_fcr31 |= s_fcc_masks[(op >> 18) & 7]; + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + } + return; + case 0x37: // C.ULE.S (unordered less than or equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f32_le(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= s_fcc_masks[(op >> 18) & 7]; + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + } + return; + + case 0x38: // C.SF.S (signalling false) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + // detect unordered + f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }); + + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + return; + case 0x39: // C.NGLE.S (not greater, less than or equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + // detect unordered + f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }); + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= s_fcc_masks[(op >> 18) & 7] | FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + } + return; + case 0x3a: // C.SEQ.S (signalling equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) })) + m_fcr31 |= s_fcc_masks[(op >> 18) & 7]; + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + return; + case 0x3b: // C.NGL.S (not greater or less than) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f32_eq(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= s_fcc_masks[(op >> 18) & 7]; + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + return; + case 0x3c: // C.LT.S (less than) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f32_lt(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) })) + m_fcr31 |= s_fcc_masks[(op >> 18) & 7]; + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + return; + case 0x3d: // C.NGE.S (not greater or equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f32_lt(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= s_fcc_masks[(op >> 18) & 7]; + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + return; + case 0x3e: // C.LE.S (less than or equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f32_le(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) })) + m_fcr31 |= s_fcc_masks[(op >> 18) & 7]; + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + return; + case 0x3f: // C.NGT.S (not greater than) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f32_le(float32_t{ u32(m_f[FSREG]) }, float32_t{ u32(m_f[FTREG]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= s_fcc_masks[(op >> 18) & 7]; + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + return; + } + } + [[fallthrough]]; + case 0x11: // D + switch (op & 0x3f) + { + case 0x11: // MOVF.D / MOVT.D + if (((m_fcr31 >> s_fcc_shifts[(op >> 18) & 7]) & 1) == ((op >> 16) & 1)) + cp1_mov_d(op); + return; + case 0x12: // MOVZ.D + if (m_r[RTREG] == 0) + cp1_mov_d(op); + return; + case 0x13: // MOVN.D + if (m_r[RTREG] != 0) + cp1_mov_d(op); + return; + case 0x15: // RECIP.D + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + float64_t const fs = float64_t{ m_f[FSREG] }; + + if (cp1_op(fs)) + { + cp1_set(FDREG, f64_div(i32_to_f64(1), fs).v); + } + } + return; + case 0x16: // RSQRT.D + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + float64_t const fs = float64_t{ m_f[FSREG] }; + + if (cp1_op(fs)) + { + cp1_set(FDREG, f64_div(i32_to_f64(1), f64_sqrt(fs)).v); + } + } + return; + case 0x30: // C.F.D (false) + if ((SR & SR_FR) || !(op & ODD_REGS)) + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + return; + case 0x31: // C.UN.D (unordered) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + // detect unordered + f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }); + if (softfloat_exceptionFlags & softfloat_flag_invalid) + m_fcr31 |= s_fcc_masks[(op >> 18) & 7]; + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + } + return; + case 0x32: // C.EQ.D (equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] })) + m_fcr31 |= s_fcc_masks[(op >> 18) & 7]; + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + } + return; + case 0x33: // C.UEQ.D (unordered equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= s_fcc_masks[(op >> 18) & 7]; + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + } + return; + case 0x34: // C.OLT.D (less than) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f64_lt(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] })) + m_fcr31 |= s_fcc_masks[(op >> 18) & 7]; + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + } + return; + case 0x35: // C.ULT.D (unordered less than) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f64_lt(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= s_fcc_masks[(op >> 18) & 7]; + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + } + return; + case 0x36: // C.OLE.D (less than or equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f64_le(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] })) + m_fcr31 |= s_fcc_masks[(op >> 18) & 7]; + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + } + return; + case 0x37: // C.ULE.D (unordered less than or equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f64_le(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= s_fcc_masks[(op >> 18) & 7]; + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + } + return; + + case 0x38: // C.SF.D (signalling false) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + // detect unordered + f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }); + + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + return; + case 0x39: // C.NGLE.D (not greater, less than or equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + // detect unordered + f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }); + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= s_fcc_masks[(op >> 18) & 7] | FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + } + return; + case 0x3a: // C.SEQ.D (signalling equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] })) + m_fcr31 |= s_fcc_masks[(op >> 18) & 7]; + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + return; + case 0x3b: // C.NGL.D (not greater or less than) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f64_eq(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= s_fcc_masks[(op >> 18) & 7]; + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + return; + case 0x3c: // C.LT.D (less than) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f64_lt(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] })) + m_fcr31 |= s_fcc_masks[(op >> 18) & 7]; + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + return; + case 0x3d: // C.NGE.D (not greater or equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f64_lt(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= s_fcc_masks[(op >> 18) & 7]; + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + return; + case 0x3e: // C.LE.D (less than or equal) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f64_le(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] })) + m_fcr31 |= s_fcc_masks[(op >> 18) & 7]; + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + return; + case 0x3f: // C.NGT.D (not greater than) + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + if (f64_le(float64_t{ m_f[FSREG] }, float64_t{ m_f[FTREG] }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= s_fcc_masks[(op >> 18) & 7]; + else + m_fcr31 &= ~s_fcc_masks[(op >> 18) & 7]; + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + cpu_exception(EXCEPTION_FPE); + } + } + return; + } + return; + } + } + + r4000_base_device::cp1_execute(op); +} + void r4000_base_device::cp1_execute(u32 const op) { if (!(SR & SR_CU1)) @@ -1692,33 +2279,21 @@ void r4000_base_device::cp1_execute(u32 const op) { case 0x00: // BC1F if (!(m_fcr31 & FCR31_C)) - { - m_branch_state = BRANCH; - m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2); - } + m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY; break; case 0x01: // BC1T if (m_fcr31 & FCR31_C) - { - m_branch_state = BRANCH; - m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2); - } + m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY; break; case 0x02: // BC1FL if (!(m_fcr31 & FCR31_C)) - { - m_branch_state = BRANCH; - m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2); - } + m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY; else m_branch_state = NULLIFY; break; case 0x03: // BC1TL if (m_fcr31 & FCR31_C) - { - m_branch_state = BRANCH; - m_branch_target = ADDR(m_pc + 4, s32(s16(op)) << 2); - } + m_branch_state = ADDR(m_pc + 4, s32(s16(op)) << 2) | DELAY; else m_branch_state = NULLIFY; break; @@ -1797,23 +2372,7 @@ void r4000_base_device::cp1_execute(u32 const op) } break; case 0x06: // MOV.S - if (SR & SR_FR) - m_f[FDREG] = (m_f[FDREG] & ~0xffffffffULL) | u32(m_f[FSREG]); - else - if (FDREG & 1) - if (FSREG & 1) - // move high half to high half - m_f[FDREG & ~1] = (m_f[FSREG & ~1] & ~0xffffffffULL) | u32(m_f[FDREG & ~1]); - else - // move low half to high half - m_f[FDREG & ~1] = (m_f[FSREG & ~1] << 32) | u32(m_f[FDREG & ~1]); - else - if (FSREG & 1) - // move high half to low half - m_f[FDREG & ~1] = (m_f[FDREG & ~1] & ~0xffffffffULL) | (m_f[FSREG & ~1] >> 32); - else - // move low half to low half - m_f[FDREG & ~1] = (m_f[FDREG & ~1] & ~0xffffffffULL) | u32(m_f[FSREG & ~1]); + cp1_mov_s(op); break; case 0x07: // NEG.S if ((SR & SR_FR) || !(op & ODD_REGS)) @@ -2193,8 +2752,7 @@ void r4000_base_device::cp1_execute(u32 const op) } break; case 0x06: // MOV.D - if ((SR & SR_FR) || !(op & ODD_REGS)) - m_f[FDREG] = m_f[FSREG]; + cp1_mov_d(op); break; case 0x07: // NEG.D if ((SR & SR_FR) || !(op & ODD_REGS)) @@ -2528,11 +3086,11 @@ void r4000_base_device::cp1_execute(u32 const op) // TODO: MIPS3 only switch (op & 0x3f) { - case 0x02a00020: // CVT.S.L + case 0x20: // CVT.S.L if ((SR & SR_FR) || !(op & ODD_REGS)) cp1_set(FDREG, i64_to_f32(s64(m_f[FSREG])).v); break; - case 0x02a00021: // CVT.D.L + case 0x21: // CVT.D.L if ((SR & SR_FR) || !(op & ODD_REGS)) cp1_set(FDREG, i64_to_f64(s64(m_f[FSREG])).v); break; @@ -2587,8 +3145,214 @@ void r4000_base_device::cp1_execute(u32 const op) break; case 0x3d: // SDC1 + store<u64>(ADDR(m_r[RSREG], s16(op)), m_f[RTREG]); + break; + } +} + +void r4000_base_device::cp1_mov_s(u32 const op) +{ + if (SR & SR_FR) + m_f[FDREG] = (m_f[FDREG] & ~0xffffffffULL) | u32(m_f[FSREG]); + else + if (FDREG & 1) + if (FSREG & 1) + // move high half to high half + m_f[FDREG & ~1] = (m_f[FSREG & ~1] & ~0xffffffffULL) | u32(m_f[FDREG & ~1]); + else + // move low half to high half + m_f[FDREG & ~1] = (m_f[FSREG & ~1] << 32) | u32(m_f[FDREG & ~1]); + else + if (FSREG & 1) + // move high half to low half + m_f[FDREG & ~1] = (m_f[FDREG & ~1] & ~0xffffffffULL) | (m_f[FSREG & ~1] >> 32); + else + // move low half to low half + m_f[FDREG & ~1] = (m_f[FDREG & ~1] & ~0xffffffffULL) | u32(m_f[FSREG & ~1]); +} + +void r4000_base_device::cp1_mov_d(u32 const op) +{ + if ((SR & SR_FR) || !(op & ODD_REGS)) + m_f[FDREG] = m_f[FSREG]; +} + +void r4000_base_device::cp1x_execute(u32 const op) +{ + if (!(SR & SR_CU1)) + { + cpu_exception(EXCEPTION_CP1); + return; + } + + logerror("cp1x not supported < R5000 (%s)\n", machine().describe_context()); +} + +void r5000_device::cp1x_execute(u32 const op) +{ + if (!(SR & SR_CU1)) + { + cpu_exception(EXCEPTION_CP1); + return; + } + + switch (op & 0x3f) + { + case 0x00: // LWXC1 + load<u32>(s64(s32(u32(m_r[RSREG]) + u32(m_r[RTREG]))), + [this, op](u32 data) + { + if (SR & SR_FR) + m_f[RTREG] = (m_f[RTREG] & ~0xffffffffULL) | data; + else + if (RTREG & 1) + // load the high half of the even floating point register + m_f[RTREG & ~1] = (u64(data) << 32) | u32(m_f[RTREG & ~1]); + else + // load the low half of the even floating point register + m_f[RTREG & ~1] = (m_f[RTREG & ~1] & ~0xffffffffULL) | data; + }); + break; + case 0x01: // LDXC1 + load<u64>(s64(s32(u32(m_r[RSREG]) + u32(m_r[RTREG]))), + [this, op](u64 data) + { + if ((SR & SR_FR) || !(RTREG & 1)) + m_f[RTREG] = data; + }); + break; + case 0x08: // SWXC1 + if (SR & SR_FR) + store<u32>(s64(s32(u32(m_r[RSREG]) + u32(m_r[RTREG]))), u32(m_f[RTREG])); + else + if (RTREG & 1) + // store the high half of the even floating point register + store<u32>(s64(s32(u32(m_r[RSREG]) + u32(m_r[RTREG]))), u32(m_f[RTREG & ~1] >> 32)); + else + // store the low half of the even floating point register + store<u32>(s64(s32(u32(m_r[RSREG]) + u32(m_r[RTREG]))), u32(m_f[RTREG & ~1])); + break; + case 0x09: // SDXC1 if ((SR & SR_FR) || !(RTREG & 1)) - store<u64>(ADDR(m_r[RSREG], s16(op)), m_f[RTREG]); + store<u64>(s64(s32(u32(m_r[RSREG]) + u32(m_r[RTREG]))), m_f[RTREG]); + break; + case 0x0f: // PREFX + // Do nothing for now (implementations are permitted to do this) + break; + case 0x20: // MADD.S + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + float32_t const fr = float32_t{ u32(m_f[FRREG]) }; + float32_t const fs = float32_t{ u32(m_f[FSREG]) }; + float32_t const ft = float32_t{ u32(m_f[FTREG]) }; + + if (cp1_op(fr) && cp1_op(fs) && cp1_op(ft)) + cp1_set(FDREG, f32_add(f32_mul(fs, ft), fr).v); + } + break; + case 0x21: // MADD.D + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + float64_t const fr = float64_t{ m_f[FRREG] }; + float64_t const fs = float64_t{ m_f[FSREG] }; + float64_t const ft = float64_t{ m_f[FTREG] }; + + if (cp1_op(fr) && cp1_op(fs) && cp1_op(ft)) + cp1_set(FDREG, f64_add(f64_mul(fs, ft), fr).v); + } + break; + case 0x28: // MSUB.S + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + float32_t const fr = float32_t{ u32(m_f[FRREG]) }; + float32_t const fs = float32_t{ u32(m_f[FSREG]) }; + float32_t const ft = float32_t{ u32(m_f[FTREG]) }; + + if (cp1_op(fr) && cp1_op(fs) && cp1_op(ft)) + cp1_set(FDREG, f32_sub(f32_mul(fs, ft), fr).v); + } + break; + case 0x29: // MSUB.D + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + float64_t const fr = float64_t{ m_f[FRREG] }; + float64_t const fs = float64_t{ m_f[FSREG] }; + float64_t const ft = float64_t{ m_f[FTREG] }; + + if (cp1_op(fr) && cp1_op(fs) && cp1_op(ft)) + cp1_set(FDREG, f64_sub(f64_mul(fs, ft), fr).v); + } + break; + case 0x30: // NMADD.S + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + float32_t const fr = float32_t{ u32(m_f[FRREG]) }; + float32_t const fs = float32_t{ u32(m_f[FSREG]) }; + float32_t const ft = float32_t{ u32(m_f[FTREG]) }; + + if (cp1_op(fr) && cp1_op(fs) && cp1_op(ft)) + cp1_set(FDREG, f32_mul(f32_add(f32_mul(fs, ft), fr), i32_to_f32(-1)).v); + } + break; + case 0x31: // NMADD.D + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + float64_t const fr = float64_t{ m_f[FRREG] }; + float64_t const fs = float64_t{ m_f[FSREG] }; + float64_t const ft = float64_t{ m_f[FTREG] }; + + if (cp1_op(fr) && cp1_op(fs) && cp1_op(ft)) + cp1_set(FDREG, f64_mul(f64_add(f64_mul(fs, ft), fr), i32_to_f64(-1)).v); + } + break; + case 0x38: // NMSUB.S + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + float32_t const fr = float32_t{ u32(m_f[FRREG]) }; + float32_t const fs = float32_t{ u32(m_f[FSREG]) }; + float32_t const ft = float32_t{ u32(m_f[FTREG]) }; + + if (cp1_op(fr) && cp1_op(fs) && cp1_op(ft)) + cp1_set(FDREG, f32_mul(f32_sub(f32_mul(fs, ft), fr), i32_to_f32(-1)).v); + } + break; + case 0x39: // NMSUB.D + if ((SR & SR_FR) || !(op & ODD_REGS)) + { + float64_t const fr = float64_t{ m_f[FRREG] }; + float64_t const fs = float64_t{ m_f[FSREG] }; + float64_t const ft = float64_t{ m_f[FTREG] }; + + if (cp1_op(fr) && cp1_op(fs) && cp1_op(ft)) + cp1_set(FDREG, f64_mul(f64_sub(f64_mul(fs, ft), fr), i32_to_f64(-1)).v); + } + break; + case 0x24: /* MADD.W */ + logerror("cp1x unsupported op (%s): MADD.W\n", machine().describe_context()); + break; + case 0x25: /* MADD.L */ + logerror("cp1x unsupported op (%s): MADD.L\n", machine().describe_context()); + break; + case 0x2c: /* MSUB.W */ + logerror("cp1x unsupported op (%s): MSUB.W\n", machine().describe_context()); + break; + case 0x2d: /* MSUB.L */ + logerror("cp1x unsupported op (%s): MSUB.L\n", machine().describe_context()); + break; + case 0x34: /* NMADD.W */ + logerror("cp1x unsupported op (%s): NMADD.W\n", machine().describe_context()); + break; + case 0x35: /* NMADD.L */ + logerror("cp1x unsupported op (%s): NMADD.L\n", machine().describe_context()); + break; + case 0x3c: /* NMSUB.W */ + logerror("cp1x unsupported op (%s): NMSUB.W\n", machine().describe_context()); + break; + case 0x3d: /* NMSUB.L */ + logerror("cp1x unsupported op (%s): NMSUB.L\n", machine().describe_context()); + break; + default: + logerror("cp1x unsupported op (%s): [unknown]\n", machine().describe_context()); break; } } @@ -2727,7 +3491,7 @@ void r4000_base_device::cp2_execute(u32 const op) } } -r4000_base_device::translate_t r4000_base_device::translate(int intention, u64 &address) +r4000_base_device::translate_result r4000_base_device::translate(int intention, bool debug, u64 &address) { /* * Decode the program address into one of the following ranges depending on @@ -2881,16 +3645,16 @@ r4000_base_device::translate_t r4000_base_device::translate(int intention, u64 & // address needs translation, using a combination of VPN2 and ASID u64 const key = (address & (extended ? (EH_R | EH_VPN2_64) : EH_VPN2_32)) | (m_cp0[CP0_EntryHi] & EH_ASID); - unsigned *mru = m_tlb_mru[intention & TRANSLATE_TYPE_MASK]; - if (LOG_STATS) + unsigned *mru = m_tlb_mru[intention]; + if (VERBOSE & LOG_STATS) m_tlb_scans++; bool invalid = false; bool modify = false; - for (unsigned i = 0; i < ARRAY_LENGTH(m_tlb); i++) + for (unsigned i = 0; i < std::size(m_tlb); i++) { unsigned const index = mru[i]; - tlb_entry_t const &entry = m_tlb[index]; + tlb_entry const &entry = m_tlb[index]; // test vpn and asid u64 const mask = (extended ? EH_R | (EH_VPN2_64 & ~entry.mask) : (EH_VPN2_32 & ~entry.mask)) @@ -2899,7 +3663,7 @@ r4000_base_device::translate_t r4000_base_device::translate(int intention, u64 & if ((entry.vpn & mask) != (key & mask)) continue; - if (LOG_STATS) + if (VERBOSE & LOG_STATS) m_tlb_loops += i + 1; u64 const pfn = entry.pfn[BIT(address, entry.low_bit)]; @@ -2912,7 +3676,7 @@ r4000_base_device::translate_t r4000_base_device::translate(int intention, u64 & } // test dirty - if ((intention & TRANSLATE_WRITE) && !(pfn & EL_D)) + if ((intention == TR_WRITE) && !(pfn & EL_D)) { modify = true; break; @@ -2930,18 +3694,18 @@ r4000_base_device::translate_t r4000_base_device::translate(int intention, u64 & } // tlb miss, invalid entry, or a store to a non-dirty entry - if (!machine().side_effects_disabled() && !(intention & TRANSLATE_DEBUG_MASK)) + if (!machine().side_effects_disabled() && !debug) { if (VERBOSE & LOG_TLB) { - char const mode[] = { 'r', 'w', 'x' }; + static char const mode[] = { 'r', 'w', 'x' }; if (modify) - LOGMASKED(LOG_TLB, "tlb modify asid %d address 0x%016x (%s)\n", - m_cp0[CP0_EntryHi] & EH_ASID, address, machine().describe_context()); + LOGMASKED(LOG_TLB, "tlb modify asid 0x%02x address 0x%016x (%s)\n", + m_cp0[CP0_EntryHi] & EH_ASID, address, machine().describe_context()); else - LOGMASKED(LOG_TLB, "tlb miss %c asid %d address 0x%016x (%s)\n", - mode[intention & TRANSLATE_TYPE_MASK], m_cp0[CP0_EntryHi] & EH_ASID, address, machine().describe_context()); + LOGMASKED(LOG_TLB, "tlb miss %c asid 0x%02x address 0x%016x (%s)\n", + mode[intention], m_cp0[CP0_EntryHi] & EH_ASID, address, machine().describe_context()); } // load tlb exception registers @@ -2951,9 +3715,9 @@ r4000_base_device::translate_t r4000_base_device::translate(int intention, u64 & m_cp0[CP0_XContext] = (m_cp0[CP0_XContext] & XCONTEXT_PTEBASE) | ((address >> 31) & XCONTEXT_R) | ((address >> 9) & XCONTEXT_BADVPN2); if (invalid || modify || (SR & SR_EXL)) - cpu_exception(modify ? EXCEPTION_MOD : (intention & TRANSLATE_WRITE) ? EXCEPTION_TLBS : EXCEPTION_TLBL); + cpu_exception(modify ? EXCEPTION_MOD : (intention == TR_WRITE) ? EXCEPTION_TLBS : EXCEPTION_TLBL); else - cpu_exception((intention & TRANSLATE_WRITE) ? EXCEPTION_TLBS : EXCEPTION_TLBL, extended ? 0x080 : 0x000); + cpu_exception((intention == TR_WRITE) ? EXCEPTION_TLBS : EXCEPTION_TLBL, extended ? 0x080 : 0x000); } return MISS; @@ -2961,7 +3725,7 @@ r4000_base_device::translate_t r4000_base_device::translate(int intention, u64 & void r4000_base_device::address_error(int intention, u64 const address) { - if (!machine().side_effects_disabled() && !(intention & TRANSLATE_DEBUG_MASK)) + if (!machine().side_effects_disabled()) { logerror("address_error 0x%016x (%s)\n", address, machine().describe_context()); @@ -2969,28 +3733,43 @@ void r4000_base_device::address_error(int intention, u64 const address) if (!(SR & SR_EXL)) m_cp0[CP0_BadVAddr] = address; - cpu_exception((intention & TRANSLATE_WRITE) ? EXCEPTION_ADES : EXCEPTION_ADEL); + cpu_exception((intention == TR_WRITE) ? EXCEPTION_ADES : EXCEPTION_ADEL); // address errors shouldn't typically occur, so a breakpoint is handy machine().debug_break(); } } -template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, bool> r4000_base_device::load(u64 address, U &&apply) +template <typename T> void r4000_base_device::accessors(T &m) +{ + space(AS_PROGRAM).specific(m); + + read_byte = [&m](offs_t offset) { return m.read_byte(offset); }; + read_word = [&m](offs_t offset) { return m.read_word(offset); }; + read_dword = [&m](offs_t offset) { return m.read_dword(offset); }; + read_qword = [&m](offs_t offset) { return m.read_qword(offset); }; + + write_byte = [&m](offs_t offset, u8 data) { m.write_byte(offset, data); }; + write_word = [&m](offs_t offset, u16 data, u16 mem_mask) { m.write_word(offset, data, mem_mask); }; + write_dword = [&m](offs_t offset, u32 data, u32 mem_mask) { m.write_dword(offset, data, mem_mask); }; + write_qword = [&m](offs_t offset, u64 data, u64 mem_mask) { m.write_qword(offset, data, mem_mask); }; +} + +template <typename T, bool Aligned, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, bool> r4000_base_device::load(u64 address, U &&apply) { // alignment error - if (address & (sizeof(T) - 1)) + if (Aligned && (address & (sizeof(T) - 1))) { - address_error(TRANSLATE_READ, address); + address_error(TR_READ, address); return false; } - translate_t const t = translate(TRANSLATE_READ, address); + translate_result const t = translate(TR_READ, false, address); // address error if (t == ERROR) { - address_error(TRANSLATE_READ, address); + address_error(TR_READ, address); return false; } @@ -3011,15 +3790,19 @@ template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::f } } + // align address for l[dw][lr] instructions + if (!Aligned) + address &= ~(sizeof(T) - 1); + // TODO: cache lookup T value = 0; switch (sizeof(T)) { - case 1: value = T(space(0).read_byte(address)); break; - case 2: value = T(space(0).read_word(address)); break; - case 4: value = T(space(0).read_dword(address)); break; - case 8: value = T(space(0).read_qword(address)); break; + case 1: value = T(read_byte(address)); break; + case 2: value = T(read_word(address)); break; + case 4: value = T(read_dword(address)); break; + case 8: value = T(read_qword(address)); break; } if (m_bus_error) @@ -3040,16 +3823,16 @@ template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::f // alignment error if (address & (sizeof(T) - 1)) { - address_error(TRANSLATE_READ, address); + address_error(TR_READ, address); return false; } - translate_t const t = translate(TRANSLATE_READ, address); + translate_result const t = translate(TR_READ, false, address); // address error if (t == ERROR) { - address_error(TRANSLATE_READ, address); + address_error(TR_READ, address); return false; } @@ -3073,28 +3856,28 @@ template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::f switch (sizeof(T)) { - case 4: apply(address, T(space(0).read_dword(address))); break; - case 8: apply(address, T(space(0).read_qword(address))); break; + case 4: apply(address, T(read_dword(address))); break; + case 8: apply(address, T(read_qword(address))); break; } return true; } -template <typename T, typename U> std::enable_if_t<std::is_convertible<U, T>::value, bool> r4000_base_device::store(u64 address, U data, T mem_mask) +template <typename T, bool Aligned, typename U> std::enable_if_t<std::is_convertible<U, T>::value, bool> r4000_base_device::store(u64 address, U data, T mem_mask) { // alignment error - if (address & (sizeof(T) - 1)) + if (Aligned && (address & (sizeof(T) - 1))) { - address_error(TRANSLATE_WRITE, address); + address_error(TR_WRITE, address); return false; } - translate_t const t = translate(TRANSLATE_WRITE, address); + translate_result const t = translate(TR_WRITE, false, address); // address error if (t == ERROR) { - address_error(TRANSLATE_WRITE, address); + address_error(TR_WRITE, address); return false; } @@ -3114,14 +3897,18 @@ template <typename T, typename U> std::enable_if_t<std::is_convertible<U, T>::va } } + // align address for s[dw][lr] instructions + if (!Aligned) + address &= ~(sizeof(T) - 1); + // TODO: cache lookup switch (sizeof(T)) { - case 1: space(0).write_byte(address, T(data)); break; - case 2: space(0).write_word(address, T(data), mem_mask); break; - case 4: space(0).write_dword(address, T(data), mem_mask); break; - case 8: space(0).write_qword(address, T(data), mem_mask); break; + case 1: write_byte(address, T(data)); break; + case 2: write_word(address, T(data), mem_mask); break; + case 4: write_dword(address, T(data), mem_mask); break; + case 8: write_qword(address, T(data), mem_mask); break; } return true; @@ -3134,17 +3921,17 @@ bool r4000_base_device::fetch(u64 address, std::function<void(u32)> &&apply) // alignment error if (address & 3) { - address_error(TRANSLATE_FETCH, address); + address_error(TR_FETCH, address); return false; } - translate_t const t = translate(TRANSLATE_FETCH, address); + translate_result const t = translate(TR_FETCH, false, address); // address error if (t == ERROR) { - address_error(TRANSLATE_FETCH, address); + address_error(TR_FETCH, address); return false; } @@ -3157,7 +3944,7 @@ bool r4000_base_device::fetch(u64 address, std::function<void(u32)> &&apply) { if (t == UNCACHED) { - const u32 insn = space(0).read_dword(address); + const u32 insn = read_dword(address); if (m_bus_error) { @@ -3186,7 +3973,7 @@ bool r4000_base_device::fetch(u64 address, std::function<void(u32)> &&apply) tag = ICACHE_V | (address >> 12); for (unsigned i = 0; i < m_icache_line_size; i += 8) { - u64 const data = space(0).read_qword((address & m_icache_mask_lo) | i); + u64 const data = read_qword((address & m_icache_mask_lo) | i); m_icache_data[(((cache_address & m_icache_mask_lo) | i) >> 2) + 0] = u32(data); m_icache_data[(((cache_address & m_icache_mask_lo) | i) >> 2) + 1] = data >> 32; @@ -3200,7 +3987,7 @@ bool r4000_base_device::fetch(u64 address, std::function<void(u32)> &&apply) } else { - const u32 insn = space(0).read_dword(address); + const u32 insn = read_dword(address); if (m_bus_error) { @@ -3296,3 +4083,46 @@ std::string r4000_base_device::debug_unicode_string(u64 unicode_string_pointer) return utf8_from_wstring(result); } + +void r4000_base_device::configure_scache() +{ + if (m_scache_size > 0) + { + /* + * Secondary cache tag size depends on the cache line size + * (how many bytes are transferred with one cache operation) and the + * size of the cache itself. + * For example, the Sony NEWS NWS-5000X has a 1MB secondary cache + * and a cache line size of 16 words. So, the slice of the physical + * address used to index into the cache is bits 19:6. + * See chapter 11 of the R4000 user manual for more details. + */ + if (m_scache_line_size == 0) + fatalerror("SCACHE size set but line size was not set!"); + + if (m_scache_line_size <= 0x10) + m_scache_line_index = 4; + else if (m_scache_line_size <= 0x20) + { + m_scache_line_index = 5; + m_cp0[CP0_Config] |= 1 << 22; + } + else if (m_scache_line_size <= 0x40) + { + m_scache_line_index = 6; + m_cp0[CP0_Config] |= 2 << 22; + } + else + { + m_scache_line_index = 7; + m_cp0[CP0_Config] |= 3 << 22; + } + + m_scache_tag_size = m_scache_size >> m_scache_line_index; + m_scache_tag_mask = m_scache_size - 1; + + m_scache_tag = std::make_unique<u32[]>(m_scache_tag_size); + } + else + m_cp0[CP0_Config] |= CONFIG_SC; +} diff --git a/src/devices/cpu/mips/r4000.h b/src/devices/cpu/mips/r4000.h index 2430812919c..a6e0f816d9a 100644 --- a/src/devices/cpu/mips/r4000.h +++ b/src/devices/cpu/mips/r4000.h @@ -9,6 +9,7 @@ DECLARE_DEVICE_TYPE(R4000, r4000_device) DECLARE_DEVICE_TYPE(R4400, r4400_device) DECLARE_DEVICE_TYPE(R4600, r4600_device) +DECLARE_DEVICE_TYPE(R5000, r5000_device) class r4000_base_device : public cpu_device { @@ -16,7 +17,7 @@ public: enum config_mask : u32 { CONFIG_K0 = 0x00000007, // kseg0 cache coherency - CONFIG_CU = 0x00000080, // store conditional cache coherent + CONFIG_CU = 0x00000008, // store conditional cache coherent CONFIG_DB = 0x00000010, // primary d-cache line 32 bytes CONFIG_IB = 0x00000020, // primary i-cache line 32 bytes CONFIG_DC = 0x000001c0, // primary d-cache size @@ -45,7 +46,7 @@ public: void bus_error() { m_bus_error = true; } protected: - enum cache_size_t + enum cache_size { CACHE_4K = 0, CACHE_8K = 1, @@ -56,7 +57,7 @@ protected: CACHE_256K = 6, CACHE_512K = 7, }; - r4000_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u32 prid, u32 fcr, cache_size_t icache_size, cache_size_t dcache_size); + r4000_base_device(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock, u32 prid, u32 fcr, cache_size icache_size, cache_size dcache_size, unsigned m32, unsigned m64, unsigned d32, unsigned d64, bool timer_interrupt_disabled); enum cp0_reg : int { @@ -290,6 +291,8 @@ protected: TAGLO_PTAGLO = 0xffffff00, // physical adddress bits 35:12 TAGLO_PSTATE = 0x000000c0, // primary cache state TAGLO_P = 0x00000001, // primary tag even parity + TAGLO_CS = 0x00001c00, // scache status + TAGLO_STAG = 0xffffe000, // scache tag }; enum icache_mask : u32 { @@ -305,27 +308,35 @@ protected: DCACHE_W = 0x02000000, // write-back DCACHE_WP = 0x02000000, // even parity for write-back }; + enum scache_mask : u32 + { + SCACHE_CS = 0x01c00000, // cache state + SCACHE_STAG = 0x0007ffff, // physical tag + SCACHE_PIDX = 0x00380000, // primary cache index + }; // device_t overrides - virtual void device_start() override; - virtual void device_reset() override; - virtual void device_stop() override; + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; + virtual void device_stop() override ATTR_COLD; // device_memory_interface overrides virtual space_config_vector memory_space_config() const override; - virtual bool memory_translate(int spacenum, int intention, offs_t &address) override; + virtual bool memory_translate(int spacenum, int intention, offs_t &address, address_space *&target_space) override; // device_disasm_interface overrides virtual std::unique_ptr<util::disasm_interface> create_disassembler() override; // device_execute_interface overrides - virtual u32 execute_min_cycles() const override { return 1; } - virtual u32 execute_max_cycles() const override { return 40; } - virtual u32 execute_input_lines() const override { return 6; } + virtual u64 execute_clocks_to_cycles(u64 clocks) const noexcept override { return (clocks * 2); } + virtual u64 execute_cycles_to_clocks(u64 cycles) const noexcept override { return (cycles + 1) / 2; } + virtual u32 execute_min_cycles() const noexcept override { return 1; } + virtual u32 execute_max_cycles() const noexcept override { return *std::max_element(std::begin(m_hilo_cycles), std::end(m_hilo_cycles)); } virtual void execute_run() override; virtual void execute_set_input(int inputnum, int state) override; // cpu implementation + virtual void handle_reserved_instruction(u32 const op); void cpu_execute(u32 const op); void cpu_exception(u32 exception, u16 const vector = 0x180); void cpu_lwl(u32 const op); @@ -338,6 +349,7 @@ protected: void cpu_sdr(u32 const op); // cp0 implementation + void cp0_cache(u32 const op); void cp0_execute(u32 const op); u64 cp0_get(unsigned const reg); void cp0_set(unsigned const reg, u64 const data); @@ -354,20 +366,24 @@ protected: // cp1 implementation void cp1_unimplemented(); template <typename T> bool cp1_op(T op); - void cp1_execute(u32 const op); + virtual void cp1_execute(u32 const op); + virtual void cp1x_execute(u32 const op); template <typename T> void cp1_set(unsigned const reg, T const data); + void cp1_mov_s(u32 const op); + void cp1_mov_d(u32 const op); // cp2 implementation void cp2_execute(u32 const op); // address and memory handling - enum translate_t { ERROR, MISS, UNCACHED, CACHED }; - translate_t translate(int intention, u64 &address); + enum translate_result : unsigned { ERROR, MISS, UNCACHED, CACHED }; + translate_result translate(int intention, bool debug, u64 &address); void address_error(int intention, u64 const address); - template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, bool> load(u64 program_address, U &&apply); + template <typename T> void accessors(T &m); + template <typename T, bool Aligned = true, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, bool> load(u64 program_address, U &&apply); template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(u64, T)>>::value, bool> load_linked(u64 program_address, U &&apply); - template <typename T, typename U> std::enable_if_t<std::is_convertible<U, T>::value, bool> store(u64 program_address, U data, T mem_mask = ~T(0)); + template <typename T, bool Aligned = true, typename U> std::enable_if_t<std::is_convertible<U, T>::value, bool> store(u64 program_address, U data, T mem_mask = ~T(0)); bool fetch(u64 address, std::function<void(u32)> &&apply); // debugging helpers @@ -375,36 +391,60 @@ protected: std::string debug_string_array(u64 array_pointer); std::string debug_unicode_string(u64 unicode_string_pointer); + // configuration helpers + void configure_scache(); + // device configuration state address_space_config m_program_config_le; address_space_config m_program_config_be; + // memory access helpers + memory_access<32, 3, 0, ENDIANNESS_LITTLE>::specific m_le; + memory_access<32, 3, 0, ENDIANNESS_BIG>::specific m_be; + + std::function<u8(offs_t offset)> read_byte; + std::function<u16(offs_t offset)> read_word; + std::function<u32(offs_t offset)> read_dword; + std::function<u64(offs_t offset)> read_qword; + std::function<void(offs_t offset, u8 data)> write_byte; + std::function<void(offs_t offset, u16 data, u16 mem_mask)> write_word; + std::function<void(offs_t offset, u32 data, u32 mem_mask)> write_dword; + std::function<void(offs_t offset, u64 data, u64 mem_mask)> write_qword; + + enum branch_state : u64 + { + STATE = 0x00000000'00000003, + TARGET = 0xffffffff'fffffffc, + + NONE = 0, + BRANCH = 1, // retire delayed branch + DELAY = 2, // delay slot instruction + NULLIFY = 3, // next instruction nullified + }; + // runtime state int m_icount; + // integer multiple/divide state + unsigned const m_hilo_cycles[4]; + unsigned m_hilo_delay; + // cpu state u64 m_pc; u64 m_r[32]; u64 m_hi; u64 m_lo; - enum branch_state_t : unsigned - { - NONE = 0, - DELAY = 1, // delay slot instruction active - BRANCH = 2, // branch instruction active - EXCEPTION = 3, // exception triggered - NULLIFY = 4, // next instruction nullified - } - m_branch_state; - u64 m_branch_target; + u64 m_branch_state; // cp0 state u64 m_cp0[32]; u64 m_cp0_timer_zero; emu_timer *m_cp0_timer; + bool m_hard_reset; bool m_ll_active; bool m_bus_error; - struct tlb_entry_t + bool m_timer_interrupt_disabled; + struct tlb_entry { u64 mask; u64 vpn; @@ -429,6 +469,14 @@ protected: std::unique_ptr<u32[]> m_icache_tag; std::unique_ptr<u32[]> m_icache_data; + // experimental scache state + u32 m_scache_size; // Size in bytes + u8 m_scache_line_size; + u32 m_scache_line_index; // Secondary cache line shift + u32 m_scache_tag_mask; // Mask for extracting the tag from a physical address + u32 m_scache_tag_size; + std::unique_ptr<u32[]> m_scache_tag; + // statistics u64 m_tlb_scans; u64 m_tlb_loops; @@ -440,33 +488,73 @@ class r4000_device : public r4000_base_device { public: // NOTE: R4000 chips prior to 3.0 have an xtlb bug - r4000_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) - : r4000_base_device(mconfig, R4000, tag, owner, clock, 0x0430, 0x0500, CACHE_8K, CACHE_8K) + r4000_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, bool timer_interrupt_disabled) + : r4000_base_device(mconfig, R4000, tag, owner, clock, 0x0430, 0x0500, CACHE_8K, CACHE_8K, 10, 20, 69, 133, timer_interrupt_disabled) { // no secondary cache m_cp0[CP0_Config] |= CONFIG_SC; } + + r4000_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : r4000_device(mconfig, tag, owner, clock, false) + { + } }; class r4400_device : public r4000_base_device { public: + r4400_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, bool timer_interrupt_disabled, u32 scache_size, u8 scache_line_size) + : r4000_base_device(mconfig, R4400, tag, owner, clock, 0x0440, 0x0500, CACHE_16K, CACHE_16K, 10, 20, 69, 133, timer_interrupt_disabled) + { + m_scache_size = scache_size; + m_scache_line_size = scache_line_size; + configure_scache(); + } + r4400_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) - : r4000_base_device(mconfig, R4400, tag, owner, clock, 0x0440, 0x0500, CACHE_16K, CACHE_16K) + : r4400_device(mconfig, tag, owner, clock, false, 0, 0) { - // no secondary cache - m_cp0[CP0_Config] |= CONFIG_SC; } }; class r4600_device : public r4000_base_device { public: + r4600_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, bool timer_interrupt_disabled) + : r4000_base_device(mconfig, R4600, tag, owner, clock, 0x2020, 0x2020, CACHE_16K, CACHE_16K, 10, 12, 42, 74, timer_interrupt_disabled) + { + // no secondary cache + m_cp0[CP0_Config] |= CONFIG_SC; + } + r4600_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) - : r4000_base_device(mconfig, R4600, tag, owner, clock, 0x2020, 0x2020, CACHE_16K, CACHE_16K) + : r4600_device(mconfig, tag, owner, clock, false) + { + } +}; + +class r5000_device : public r4000_base_device +{ +public: + r5000_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, bool timer_interrupt_disabled) + : r4000_base_device(mconfig, R5000, tag, owner, clock, 0x2320, 0x2320, CACHE_32K, CACHE_32K, 5, 9, 36, 68, timer_interrupt_disabled) { // no secondary cache m_cp0[CP0_Config] |= CONFIG_SC; } + + r5000_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : r5000_device(mconfig, tag, owner, clock, false) + { + } + +private: + virtual void handle_reserved_instruction(u32 const op) override; + virtual void cp1_execute(u32 const op) override; + virtual void cp1x_execute(u32 const op) override; + + static u32 const s_fcc_masks[8]; + static u32 const s_fcc_shifts[8]; }; #endif // MAME_CPU_MIPS_R4000_H |