From e02a5cf1f526385363d874fa6fbe3ffacb217e4e Mon Sep 17 00:00:00 2001 From: Patrick Mackinlay Date: Tue, 26 Feb 2019 18:54:04 +0700 Subject: mips1: fpu emulation Code refactoring makes the changes hard to isolate, but the main improvements are: * implemented fpu instructions and exceptions * corrected swl/swr implementation * tlb mru lookup optimization * interrupt and privilege debugger breakpoints --- src/devices/cpu/mips/mips1.cpp | 2170 +++++++++++++++++++++++++-------------- src/devices/cpu/mips/mips1.h | 152 +-- src/mame/drivers/decstation.cpp | 6 +- src/mame/drivers/mips.cpp | 5 +- 4 files changed, 1480 insertions(+), 853 deletions(-) diff --git a/src/devices/cpu/mips/mips1.cpp b/src/devices/cpu/mips/mips1.cpp index ff629e04bcc..cc6fd58343f 100644 --- a/src/devices/cpu/mips/mips1.cpp +++ b/src/devices/cpu/mips/mips1.cpp @@ -7,8 +7,6 @@ * while those without have hard-wired address translation. * * TODO - * - FPU support - * - further cleanup on coprocessors * - R3041 features * - cache emulation * @@ -17,6 +15,7 @@ #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) @@ -32,23 +31,16 @@ #define RDREG ((op >> 11) & 31) #define SHIFT ((op >> 6) & 31) -#define RSVAL m_r[RSREG] -#define RTVAL m_r[RTREG] -#define RDVAL m_r[RDREG] +#define FTREG ((op >> 16) & 31) +#define FSREG ((op >> 11) & 31) +#define FDREG ((op >> 6) & 31) #define SIMMVAL s16(op) #define UIMMVAL u16(op) #define LIMMVAL (op & 0x03ffffff) -#define ADDPC(x) do { m_branch_state = BRANCH; m_branch_target = m_pc + 4 + ((x) << 2); } while (0) -#define ADDPCL(x,l) do { m_branch_state = BRANCH; m_branch_target = m_pc + 4 + ((x) << 2); m_r[l] = m_pc + 8; } while (0) -#define ABSPC(x) do { m_branch_state = BRANCH; m_branch_target = ((m_pc + 4) & 0xf0000000) | ((x) << 2); } while (0) -#define ABSPCL(x,l) do { m_branch_state = BRANCH; m_branch_target = ((m_pc + 4) & 0xf0000000) | ((x) << 2); m_r[l] = m_pc + 8; } while (0) -#define SETPC(x) do { m_branch_state = BRANCH; m_branch_target = (x); } while (0) -#define SETPCL(x,l) do { m_branch_state = BRANCH; m_branch_target = (x); m_r[l] = m_pc + 8; } while (0) - -#define SR m_cpr[0][COP0_Status] -#define CAUSE m_cpr[0][COP0_Cause] +#define SR m_cop0[COP0_Status] +#define CAUSE m_cop0[COP0_Cause] DEFINE_DEVICE_TYPE(R2000, r2000_device, "r2000", "MIPS R2000") DEFINE_DEVICE_TYPE(R2000A, r2000a_device, "r2000a", "MIPS R2000A") @@ -71,8 +63,6 @@ mips1core_device_base::mips1core_device_base(const machine_config &mconfig, devi , m_icache_config("icache", ENDIANNESS_BIG, 32, 32) , m_dcache_config("dcache", ENDIANNESS_BIG, 32, 32) , m_cpurev(cpurev) - , m_hasfpu(false) - , m_fpurev(0) , m_endianness(ENDIANNESS_BIG) , m_icount(0) , m_icache_size(icache_size) @@ -83,6 +73,7 @@ mips1core_device_base::mips1core_device_base(const machine_config &mconfig, devi 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) + , m_fcr0(0) { } @@ -127,14 +118,14 @@ r3052e_device::r3052e_device(const machine_config &mconfig, const char *tag, dev } r3071_device::r3071_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, size_t icache_size, size_t dcache_size) - : mips1core_device_base(mconfig, R3071, tag, owner, clock, 0x0200, icache_size, dcache_size) + : mips1_device_base(mconfig, R3071, tag, owner, clock, 0x0200, icache_size, dcache_size) { } r3081_device::r3081_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, size_t icache_size, size_t dcache_size) - : mips1core_device_base(mconfig, R3081, tag, owner, clock, 0x0200, icache_size, dcache_size) + : mips1_device_base(mconfig, R3081, tag, owner, clock, 0x0200, icache_size, dcache_size) { - set_fpurev(0x0300); + set_fpu(0x0300); } iop_device::iop_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) @@ -154,18 +145,6 @@ void mips1core_device_base::device_add_mconfig(machine_config &config) set_addrmap(2, &mips1core_device_base::dcache_map); } -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::device_start() { // set our instruction counter @@ -178,49 +157,32 @@ void mips1core_device_base::device_start() // register our state for the debugger state_add(STATE_GENPC, "GENPC", m_pc).noshow(); state_add(STATE_GENPCBASE, "CURPC", m_pc).noshow(); - state_add(STATE_GENSP, "GENSP", m_r[31]).noshow(); - state_add(STATE_GENFLAGS, "GENFLAGS", m_cpr[0][COP0_Status]).noshow(); - state_add(MIPS1_PC, "PC", m_pc); - state_add(MIPS1_COP0_SR, "SR", m_cpr[0][COP0_Status]); + state_add(MIPS1_PC, "PC", m_pc); + state_add(MIPS1_COP0 + COP0_Status, "SR", m_cop0[COP0_Status]); - for (int i = 0; i < 32; i++) + for (unsigned i = 0; i < ARRAY_LENGTH(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); state_add(MIPS1_LO, "LO", m_lo); - state_add(MIPS1_COP0_BADVADDR, "BadVAddr", m_cpr[0][COP0_BadVAddr]); - state_add(MIPS1_COP0_CAUSE, "Cause", m_cpr[0][COP0_Cause]); - state_add(MIPS1_COP0_EPC, "EPC", m_cpr[0][COP0_EPC]); + + // cop0 exception registers + state_add(MIPS1_COP0 + COP0_BadVAddr, "BadVAddr", m_cop0[COP0_BadVAddr]); + state_add(MIPS1_COP0 + COP0_Cause, "Cause", m_cop0[COP0_Cause]); + state_add(MIPS1_COP0 + COP0_EPC, "EPC", m_cop0[COP0_EPC]); // register our state for saving save_item(NAME(m_pc)); save_item(NAME(m_hi)); save_item(NAME(m_lo)); save_item(NAME(m_r)); - save_item(NAME(m_cpr)); - save_item(NAME(m_ccr)); + save_item(NAME(m_cop0)); save_item(NAME(m_branch_state)); save_item(NAME(m_branch_target)); - // initialise cpu and fpu id registers - m_cpr[0][COP0_PRId] = m_cpurev; - m_ccr[1][0] = m_fpurev; -} - -void mips1_device_base::device_start() -{ - mips1core_device_base::device_start(); - - // cop0 tlb registers - state_add(MIPS1_COP0_INDEX, "Index", m_cpr[0][COP0_Index]); - state_add(MIPS1_COP0_RANDOM, "Random", m_cpr[0][COP0_Random]); - state_add(MIPS1_COP0_ENTRYLO, "EntryLo", m_cpr[0][COP0_EntryLo]); - state_add(MIPS1_COP0_ENTRYHI, "EntryHi", m_cpr[0][COP0_EntryHi]); - state_add(MIPS1_COP0_CONTEXT, "Context", m_cpr[0][COP0_Context]); - - save_item(NAME(m_reset_time)); - save_item(NAME(m_tlb)); + // initialise cpu id register + m_cop0[COP0_PRId] = m_cpurev; } void r3041_device::device_start() @@ -228,11 +190,11 @@ void r3041_device::device_start() mips1core_device_base::device_start(); // cop0 r3041 registers - state_add(MIPS1_COP0_BUSCTRL, "BusCtrl", m_cpr[0][COP0_BusCtrl]); - state_add(MIPS1_COP0_CONFIG, "Config", m_cpr[0][COP0_Config]); - state_add(MIPS1_COP0_COUNT, "Count", m_cpr[0][COP0_Count]); - state_add(MIPS1_COP0_PORTSIZE, "PortSize", m_cpr[0][COP0_PortSize]); - state_add(MIPS1_COP0_COMPARE, "Compare", m_cpr[0][COP0_Compare]); + state_add(MIPS1_COP0 + COP0_BusCtrl, "BusCtrl", m_cop0[COP0_BusCtrl]); + state_add(MIPS1_COP0 + COP0_Config, "Config", m_cop0[COP0_Config]); + 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]); } void mips1core_device_base::device_reset() @@ -242,151 +204,585 @@ void mips1core_device_base::device_reset() m_branch_state = NONE; // non-tlb devices have tlb shut down - m_cpr[0][COP0_Status] = SR_BEV | SR_TS; + m_cop0[COP0_Status] = SR_BEV | SR_TS; m_data_spacenum = 0; } -void mips1_device_base::device_reset() -{ - mips1core_device_base::device_reset(); - - // tlb is not shut down - m_cpr[0][COP0_Status] &= ~SR_TS; - - m_reset_time = total_cycles(); -} - -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::unique_ptr mips1core_device_base::create_disassembler() +void mips1core_device_base::execute_run() { - return std::make_unique(); -} + // check for interrupts + check_irqs(); -void mips1core_device_base::generate_exception(u32 exception, bool refill) -{ - if ((VERBOSE & LOG_RISCOS) && (exception == EXCEPTION_SYSCALL)) + // core execution loop + while (m_icount-- > 0) { - 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" - }; + // debugging + debugger_instruction_hook(m_pc); - static char const *const bsd_syscalls[] = + if (VERBOSE & LOG_IOP) { - "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" }; + 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(ptr, [](char c) { printf("%c", c); }); + ptr++; + length--; + } + fflush(stdout); + } + } - int const asid = (m_cpr[0][COP0_EntryHi] & EH_ASID) >> 6; - switch (m_r[2]) + // fetch and execute instruction + fetch(m_pc, [this](u32 const op) { - case 1000: // indirect - switch (m_r[4]) + // parse the instruction + switch (op >> 26) { - 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 0x00: // SPECIAL + switch (op & 63) + { + case 0x00: // SLL + m_r[RDREG] = m_r[RTREG] << SHIFT; + break; + case 0x02: // SRL + m_r[RDREG] = m_r[RTREG] >> SHIFT; + break; + case 0x03: // SRA + m_r[RDREG] = s32(m_r[RTREG]) >> SHIFT; + break; + case 0x04: // SLLV + m_r[RDREG] = m_r[RTREG] << (m_r[RSREG] & 31); + break; + case 0x06: // SRLV + m_r[RDREG] = m_r[RTREG] >> (m_r[RSREG] & 31); + break; + case 0x07: // SRAV + m_r[RDREG] = s32(m_r[RTREG]) >> (m_r[RSREG] & 31); + break; + case 0x08: // JR + m_branch_state = BRANCH; + m_branch_target = m_r[RSREG]; + break; + case 0x09: // JALR + m_branch_state = BRANCH; + m_branch_target = m_r[RSREG]; + m_r[RDREG] = m_pc + 8; + break; + case 0x0c: // SYSCALL + generate_exception(EXCEPTION_SYSCALL); + break; + case 0x0d: // BREAK + generate_exception(EXCEPTION_BREAK); + break; + case 0x10: // MFHI + m_r[RDREG] = m_hi; + break; + case 0x11: // MTHI + m_hi = m_r[RSREG]; + break; + case 0x12: // MFLO + m_r[RDREG] = m_lo; + break; + case 0x13: // MTLO + m_lo = m_r[RSREG]; + break; + case 0x18: // MULT + { + u64 product = mul_32x32(m_r[RSREG], m_r[RTREG]); - 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; + m_lo = product; + m_hi = product >> 32; + m_icount -= 11; + } + break; + case 0x19: // MULTU + { + u64 product = mulu_32x32(m_r[RSREG], m_r[RTREG]); - 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; + m_lo = product; + m_hi = product >> 32; + m_icount -= 11; + } + break; + case 0x1a: // DIV + if (m_r[RTREG]) + { + m_lo = s32(m_r[RSREG]) / s32(m_r[RTREG]); + m_hi = s32(m_r[RSREG]) % s32(m_r[RTREG]); + } + m_icount -= 34; + break; + case 0x1b: // DIVU + if (m_r[RTREG]) + { + m_lo = m_r[RSREG] / m_r[RTREG]; + m_hi = m_r[RSREG] % m_r[RTREG]; + } + m_icount -= 34; + break; + case 0x20: // ADD + { + u32 const sum = m_r[RSREG] + m_r[RTREG]; - 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()); + // overflow: (sign(addend0) == sign(addend1)) && (sign(addend0) != sign(sum)) + if (!BIT(m_r[RSREG] ^ m_r[RTREG], 31) && BIT(m_r[RSREG] ^ sum, 31)) + generate_exception(EXCEPTION_OVERFLOW); + else + m_r[RDREG] = sum; + } break; + case 0x21: // ADDU + m_r[RDREG] = m_r[RSREG] + m_r[RTREG]; + break; + case 0x22: // SUB + { + u32 const difference = m_r[RSREG] - m_r[RTREG]; + // overflow: (sign(minuend) != sign(subtrahend)) && (sign(minuend) != sign(difference)) + if (BIT(m_r[RSREG] ^ m_r[RTREG], 31) && BIT(m_r[RSREG] ^ difference, 31)) + generate_exception(EXCEPTION_OVERFLOW); + else + m_r[RDREG] = difference; + } + break; + case 0x23: // SUBU + m_r[RDREG] = m_r[RSREG] - m_r[RTREG]; + break; + case 0x24: // AND + m_r[RDREG] = m_r[RSREG] & m_r[RTREG]; + break; + case 0x25: // OR + m_r[RDREG] = m_r[RSREG] | m_r[RTREG]; + break; + case 0x26: // XOR + m_r[RDREG] = m_r[RSREG] ^ m_r[RTREG]; + break; + case 0x27: // NOR + m_r[RDREG] = ~(m_r[RSREG] | m_r[RTREG]); + break; + case 0x2a: // SLT + m_r[RDREG] = s32(m_r[RSREG]) < s32(m_r[RTREG]); + break; + case 0x2b: // SLTU + m_r[RDREG] = u32(m_r[RSREG]) < u32(m_r[RTREG]); + 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()); + generate_exception(EXCEPTION_INVALIDOP); break; } break; + case 0x01: // REGIMM + switch (RTREG) + { + case 0x00: // BLTZ + if (s32(m_r[RSREG]) < 0) + { + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + } + break; + case 0x01: // BGEZ + if (s32(m_r[RSREG]) >= 0) + { + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + } + break; + case 0x10: // BLTZAL + if (s32(m_r[RSREG]) < 0) + { + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + m_r[31] = m_pc + 8; + } + break; + case 0x11: // BGEZAL + if (s32(m_r[RSREG]) >= 0) + { + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + m_r[31] = m_pc + 8; + } + break; + default: + generate_exception(EXCEPTION_INVALIDOP); + break; + } + break; + case 0x02: // J + m_branch_state = BRANCH; + m_branch_target = ((m_pc + 4) & 0xf0000000) | (LIMMVAL << 2); + break; + case 0x03: // JAL + m_branch_state = BRANCH; + m_branch_target = ((m_pc + 4) & 0xf0000000) | (LIMMVAL << 2); + m_r[31] = m_pc + 8; + break; + case 0x04: // BEQ + if (m_r[RSREG] == m_r[RTREG]) + { + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + } + break; + case 0x05: // BNE + if (m_r[RSREG] != m_r[RTREG]) + { + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + } + break; + case 0x06: // BLEZ + if (s32(m_r[RSREG]) <= 0) + { + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + } + break; + case 0x07: // BGTZ + if (s32(m_r[RSREG]) > 0) + { + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + } + break; + case 0x08: // ADDI + { + u32 const sum = m_r[RSREG] + SIMMVAL; - 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()); + // overflow: (sign(addend0) == sign(addend1)) && (sign(addend0) != sign(sum)) + if (!BIT(m_r[RSREG] ^ s32(SIMMVAL), 31) && BIT(m_r[RSREG] ^ sum, 31)) + generate_exception(EXCEPTION_OVERFLOW); + else + m_r[RTREG] = sum; + } + break; + case 0x09: // ADDIU + m_r[RTREG] = m_r[RSREG] + SIMMVAL; + break; + case 0x0a: // SLTI + m_r[RTREG] = s32(m_r[RSREG]) < s32(SIMMVAL); + break; + case 0x0b: // SLTIU + m_r[RTREG] = u32(m_r[RSREG]) < u32(SIMMVAL); + break; + case 0x0c: // ANDI + m_r[RTREG] = m_r[RSREG] & UIMMVAL; + break; + case 0x0d: // ORI + m_r[RTREG] = m_r[RSREG] | UIMMVAL; + break; + case 0x0e: // XORI + m_r[RTREG] = m_r[RSREG] ^ UIMMVAL; + break; + case 0x0f: // LUI + m_r[RTREG] = UIMMVAL << 16; + break; + case 0x10: // COP0 + if (!(SR & SR_KUc) || (SR & SR_COP0)) + handle_cop0(op); else - LOGMASKED(LOG_RISCOS, "asid %d syscall indirect:unknown %d (%s)\n", - asid, m_r[4], machine().describe_context()); + generate_exception(EXCEPTION_BADCOP0); 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 + case 0x11: // COP1 + handle_cop1(op); + break; + case 0x12: // COP2 + handle_cop2(op); + break; + case 0x13: // COP3 + handle_cop3(op); + break; + case 0x20: // LB + load(SIMMVAL + m_r[RSREG], [this, op](s8 temp) { m_r[RTREG] = temp; }); + break; + case 0x21: // LH + load(SIMMVAL + m_r[RSREG], [this, op](s16 temp) { m_r[RTREG] = temp; }); + break; + case 0x22: // LWL + lwl(op); + break; + case 0x23: // LW + load(SIMMVAL + m_r[RSREG], [this, op](u32 temp) { m_r[RTREG] = temp; }); + break; + case 0x24: // LBU + load(SIMMVAL + m_r[RSREG], [this, op](u8 temp) { m_r[RTREG] = temp; }); + break; + case 0x25: // LHU + load(SIMMVAL + m_r[RSREG], [this, op](u16 temp) { m_r[RTREG] = temp; }); + break; + case 0x26: // LWR + lwr(op); + break; + case 0x28: // SB + store(SIMMVAL + m_r[RSREG], m_r[RTREG]); + break; + case 0x29: // SH + store(SIMMVAL + m_r[RSREG], m_r[RTREG]); + break; + case 0x2a: // SWL + swl(op); + break; + case 0x2b: // SW + store(SIMMVAL + m_r[RSREG], m_r[RTREG]); + break; + case 0x2e: // SWR + swr(op); + break; + case 0x31: // LWC1 + handle_cop1(op); + break; + case 0x32: // LWC2 + handle_cop2(op); + break; + case 0x33: // LWC3 + handle_cop3(op); + break; + case 0x39: // SWC1 + handle_cop1(op); + break; + case 0x3a: // SWC2 + handle_cop2(op); + break; + case 0x3b: // SWC3 + handle_cop3(op); + break; + default: + generate_exception(EXCEPTION_INVALIDOP); + break; + } + + // update pc and branch state + switch (m_branch_state) + { + case NONE: + m_pc += 4; + break; + + case DELAY: + m_branch_state = NONE; + m_pc = m_branch_target; + break; + + case BRANCH: + m_branch_state = DELAY; + m_pc += 4; + break; + + case EXCEPTION: + m_branch_state = NONE; + break; + } + + // clear register 0 + m_r[0] = 0; + }); + } +} + +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); + + check_irqs(); +} + +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) + }; +} + +bool mips1core_device_base::memory_translate(int spacenum, int intention, offs_t &address) +{ + // check for kernel memory address + if (BIT(address, 31)) + { + // check debug or kernel mode + if ((intention & TRANSLATE_DEBUG_MASK) || !(SR & SR_KUc)) + { + switch (address & 0xe0000000) + { + case 0x80000000: // kseg0: unmapped, cached, privileged + case 0xa0000000: // kseg1: unmapped, uncached, privileged + address &= ~0xe0000000; + break; + + case 0xc0000000: // kseg2: mapped, cached, privileged + case 0xe0000000: + break; + } + } + else if (SR & SR_KUc) + { + if (!machine().side_effects_disabled()) + { + // exception + m_cop0[COP0_BadVAddr] = address; + + generate_exception((intention & TRANSLATE_WRITE) ? EXCEPTION_ADDRSTORE : EXCEPTION_ADDRLOAD); + } + return false; + } + } + else + // kuseg physical addresses have a 1GB offset + address += 0x40000000; + + return true; +} + +std::unique_ptr mips1core_device_base::create_disassembler() +{ + return std::make_unique(); +} + +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; @@ -422,7 +818,7 @@ void mips1core_device_base::generate_exception(u32 exception, bool refill) } // set the exception PC - m_cpr[0][COP0_EPC] = m_pc; + m_cop0[COP0_EPC] = m_pc; // load the cause register CAUSE = (CAUSE & CAUSE_IP) | exception; @@ -430,7 +826,7 @@ void mips1core_device_base::generate_exception(u32 exception, bool refill) // if in a branch delay slot, restart the branch if (m_branch_state == DELAY) { - m_cpr[0][COP0_EPC] -= 4; + m_cop0[COP0_EPC] -= 4; CAUSE |= CAUSE_BD; } m_branch_state = EXCEPTION; @@ -444,109 +840,307 @@ void mips1core_device_base::generate_exception(u32 exception, bool refill) m_pc = (SR & SR_BEV) ? 0xbfc00180 : 0x80000080; debugger_exception_hook(exception); + + if (SR & SR_KUp) + debugger_privilege_hook(); +} + +void mips1core_device_base::check_irqs() +{ + if ((CAUSE & SR & SR_IM) && (SR & SR_IEc)) + generate_exception(EXCEPTION_INTERRUPT); +} + +void mips1core_device_base::handle_cop0(u32 const op) +{ + switch (RSREG) + { + case 0x00: // MFC0 + m_r[RTREG] = get_cop0_reg(RDREG); + break; + case 0x04: // MTC0 + set_cop0_reg(RDREG, m_r[RTREG]); + break; + case 0x08: // BC0 + switch (RTREG) + { + case 0x00: // BC0F + if (!m_in_brcond[0]()) + { + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + } + break; + case 0x01: // BC0T + if (m_in_brcond[0]()) + { + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + } + break; + default: + generate_exception(EXCEPTION_INVALIDOP); + break; + } + break; + case 0x10: // COP0 + switch (op & 31) + { + case 0x10: // RFE + SR = (SR & ~SR_KUIEpc) | ((SR >> 2) & SR_KUIEpc); + if (bool(SR & SR_KUc) ^ bool(SR & SR_KUp)) + debugger_privilege_hook(); + break; + default: + generate_exception(EXCEPTION_INVALIDOP); + break; + } + break; + default: + generate_exception(EXCEPTION_INVALIDOP); + break; + } +} + +u32 mips1core_device_base::get_cop0_reg(unsigned const reg) +{ + return m_cop0[reg]; +} + +void mips1core_device_base::set_cop0_reg(unsigned const reg, u32 const data) +{ + switch (reg) + { + case COP0_Context: + m_cop0[COP0_Context] = (m_cop0[COP0_Context] & ~PTE_BASE) | (data & PTE_BASE); + break; + case COP0_Status: + { + u32 const delta = SR ^ data; + + m_cop0[COP0_Status] = data; + + // handle cache isolation and swap + m_data_spacenum = (data & SR_IsC) ? ((data & SR_SwC) ? 1 : 2) : 0; + + // update interrupts + if (delta & (SR_IEc | SR_IM)) + check_irqs(); + + if ((delta & SR_KUc) && (m_branch_state != EXCEPTION)) + debugger_privilege_hook(); + } + break; + case COP0_Cause: + CAUSE = (CAUSE & CAUSE_IPEX) | (data & ~CAUSE_IPEX); + + // update interrupts -- software ints can occur this way + check_irqs(); + break; + case COP0_PRId: + // read-only register + break; + + default: + m_cop0[reg] = data; + break; + } +} + +void mips1core_device_base::handle_cop1(u32 const op) +{ + if (!(SR & SR_COP1)) + generate_exception(EXCEPTION_BADCOP1); +} + +void mips1core_device_base::handle_cop2(u32 const op) +{ + if (!(SR & SR_COP2)) + generate_exception(EXCEPTION_BADCOP2); +} + +void mips1core_device_base::handle_cop3(u32 const op) +{ + if (!(SR & SR_COP3)) + generate_exception(EXCEPTION_BADCOP3); +} + +void mips1core_device_base::lwl(u32 const op) +{ + offs_t const offset = SIMMVAL + m_r[RSREG]; + load(offset & ~3, [this, op, offset](u32 temp) + { + unsigned const shift = ((offset & 3) ^ ENDIAN_VALUE_LE_BE(m_endianness, 3, 0)) << 3; + + m_r[RTREG] = (m_r[RTREG] & ~u32(0xffffffffU << shift)) | (temp << shift); + }); } -void mips1core_device_base::check_irqs() +void mips1core_device_base::lwr(u32 const op) { - if ((CAUSE & SR & SR_IM) && (SR & SR_IEc)) - generate_exception(EXCEPTION_INTERRUPT); + offs_t const offset = SIMMVAL + m_r[RSREG]; + load(offset & ~3, [this, op, offset](u32 temp) + { + unsigned const shift = ((offset & 0x3) ^ ENDIAN_VALUE_LE_BE(m_endianness, 0, 3)) << 3; + + m_r[RTREG] = (m_r[RTREG] & ~u32(0xffffffffU >> shift)) | (temp >> shift); + }); } -void mips1core_device_base::set_irq_line(int irqline, int state) +void mips1core_device_base::swl(u32 const op) { - if (state != CLEAR_LINE) - CAUSE |= CAUSE_IPEX0 << irqline; - else - CAUSE &= ~(CAUSE_IPEX0 << irqline); + offs_t const offset = SIMMVAL + m_r[RSREG]; + unsigned const shift = ((offset & 3) ^ ENDIAN_VALUE_LE_BE(m_endianness, 3, 0)) << 3; - check_irqs(); + store(offset & ~3, m_r[RTREG] >> shift, 0xffffffffU >> shift); } -u32 mips1core_device_base::get_cop0_reg(int const index) +void mips1core_device_base::swr(u32 const op) { - return m_cpr[0][index]; + offs_t const offset = SIMMVAL + m_r[RSREG]; + unsigned const shift = ((offset & 3) ^ ENDIAN_VALUE_LE_BE(m_endianness, 0, 3)) << 3; + + store(offset & ~3, m_r[RTREG] << shift, 0xffffffffU << shift); } -u32 mips1_device_base::get_cop0_reg(int const index) +template std::enable_if_t>::value, void> mips1core_device_base::load(u32 address, U &&apply) { - // assume 64-entry tlb with 8 wired entries - if (index == COP0_Random) - m_cpr[0][index] = (63 - ((total_cycles() - m_reset_time) % 56)) << 8; - - return m_cpr[0][index]; + if (memory_translate(m_data_spacenum, TRANSLATE_READ, address)) + { + switch (sizeof(T)) + { + case 1: apply(T(space(m_data_spacenum).read_byte(address))); break; + case 2: apply(T(space(m_data_spacenum).read_word(address))); break; + case 4: apply(T(space(m_data_spacenum).read_dword(address))); break; + } + } } -void mips1core_device_base::set_cop0_reg(int const index, u32 const data) +template std::enable_if_t::value, void> mips1core_device_base::store(u32 address, U data, T mem_mask) { - if (index == COP0_Cause) + if (memory_translate(m_data_spacenum, TRANSLATE_WRITE, address)) { - CAUSE = (CAUSE & CAUSE_IPEX) | (data & ~CAUSE_IPEX); - - // update interrupts -- software ints can occur this way - check_irqs(); + 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; + } } - else if (index == COP0_Status) - { - m_cpr[0][index] = data; +} - // handle cache isolation and swap - m_data_spacenum = (data & SR_IsC) ? ((data & SR_SwC) ? 1 : 2) : 0; +bool mips1core_device_base::fetch(u32 address, std::function &&apply) +{ + if (memory_translate(0, TRANSLATE_FETCH, address)) + { + apply(space(0).read_dword(address)); - // update interrupts - check_irqs(); + return true; } - else if (index == COP0_Context) - m_cpr[0][index] = (m_cpr[0][index] & ~PTE_BASE) | (data & PTE_BASE); - else if (index != COP0_PRId) - m_cpr[0][index] = data; + else + return false; } -void mips1core_device_base::handle_cop0(u32 const op) +std::string mips1core_device_base::debug_string(u32 string_pointer, unsigned const limit) { - switch (RSREG) + auto const suppressor(machine().disable_side_effects()); + + bool done = false; + bool mapped = false; + std::string result(""); + + while (!done) { - case 0x00: /* MFCz */ if (RTREG) RTVAL = get_cop0_reg(RDREG); break; - case 0x02: /* CFCz */ if (RTREG) RTVAL = get_cop_creg<0>(RDREG); break; - case 0x04: /* MTCz */ set_cop0_reg(RDREG, RTVAL); break; - case 0x06: /* CTCz */ set_cop_creg<0>(RDREG, RTVAL); break; - case 0x08: /* BC */ - switch (RTREG) + done = true; + load(string_pointer++, [limit, &done, &mapped, &result](u8 byte) + { + mapped = true; + if (byte != 0) { - case 0x00: /* BCzF */ if (!m_in_brcond[0]()) ADDPC(SIMMVAL); break; - case 0x01: /* BCzT */ if (m_in_brcond[0]()) ADDPC(SIMMVAL); break; - case 0x02: /* BCzFL */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x03: /* BCzTL */ generate_exception(EXCEPTION_INVALIDOP); break; - default: generate_exception(EXCEPTION_INVALIDOP); break; + result += byte; + + done = result.length() == limit; } - break; - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1a: - case 0x1b: - case 0x1c: - case 0x1d: - case 0x1e: - case 0x1f: /* COP */ - switch (op & 0x01ffffff) + }); + } + + if (!mapped) + result.assign("[unmapped]"); + + return result; +} + +std::string mips1core_device_base::debug_string_array(u32 array_pointer) +{ + auto const suppressor(machine().disable_side_effects()); + + bool done = false; + std::string result(""); + + while (!done) + { + done = true; + load(array_pointer, [this, &done, &result](u32 string_pointer) + { + if (string_pointer != 0) { - case 0x01: /* TLBR */ break; - case 0x02: /* TLBWI */ break; - case 0x06: /* TLBWR */ break; - case 0x08: /* TLBP */ break; - case 0x10: /* RFE */ SR = (SR & ~SR_KUIEpc) | ((SR >> 2) & SR_KUIEpc); break; - case 0x18: /* ERET */ generate_exception(EXCEPTION_INVALIDOP); break; - default: generate_exception(EXCEPTION_INVALIDOP); break; + if (!result.empty()) + result += ", "; + + result += '\"' + debug_string(string_pointer) + '\"'; + + done = false; } - break; - default: generate_exception(EXCEPTION_INVALIDOP); break; + }); + + array_pointer += 4; + } + + return result; +} + +void mips1_device_base::device_start() +{ + mips1core_device_base::device_start(); + + // cop0 tlb registers + state_add(MIPS1_COP0 + COP0_Index, "Index", m_cop0[COP0_Index]); + state_add(MIPS1_COP0 + COP0_Random, "Random", m_cop0[COP0_Random]); + state_add(MIPS1_COP0 + COP0_EntryLo, "EntryLo", m_cop0[COP0_EntryLo]); + state_add(MIPS1_COP0 + COP0_EntryHi, "EntryHi", m_cop0[COP0_EntryHi]); + state_add(MIPS1_COP0 + COP0_Context, "Context", m_cop0[COP0_Context]); + + // cop1 registers + if (m_fcr0) + { + state_add(MIPS1_FCR31, "FCSR", m_fcr31); + for (unsigned i = 0; i < ARRAY_LENGTH(m_f); i++) + state_add(MIPS1_F0 + i, util::string_format("F%d", i).c_str(), m_f[i]); + } + + save_item(NAME(m_reset_time)); + save_item(NAME(m_tlb)); + + save_item(NAME(m_fcr30)); + save_item(NAME(m_fcr31)); + save_item(NAME(m_f)); +} + +void mips1_device_base::device_reset() +{ + mips1core_device_base::device_reset(); + + // tlb is not shut down + m_cop0[COP0_Status] &= ~SR_TS; + + m_reset_time = total_cycles(); + + // initialize tlb mru index with identity mapping + for (unsigned i = 0; i < ARRAY_LENGTH(m_tlb); i++) + { + m_tlb_mru[TRANSLATE_READ][i] = i; + m_tlb_mru[TRANSLATE_WRITE][i] = i; + m_tlb_mru[TRANSLATE_FETCH][i] = i; } } @@ -556,26 +1150,26 @@ void mips1_device_base::handle_cop0(u32 const op) { case 0x42000001: // TLBR - read tlb { - u8 const index = (m_cpr[0][COP0_Index] >> 8) & 0x3f; + u8 const index = (m_cop0[COP0_Index] >> 8) & 0x3f; - m_cpr[0][COP0_EntryHi] = m_tlb[index][0]; - m_cpr[0][COP0_EntryLo] = m_tlb[index][1]; + m_cop0[COP0_EntryHi] = m_tlb[index][0]; + m_cop0[COP0_EntryLo] = m_tlb[index][1]; } break; case 0x42000002: // TLBWI - write tlb (indexed) { - u8 const index = (m_cpr[0][COP0_Index] >> 8) & 0x3f; + u8 const index = (m_cop0[COP0_Index] >> 8) & 0x3f; - m_tlb[index][0] = m_cpr[0][COP0_EntryHi]; - m_tlb[index][1] = m_cpr[0][COP0_EntryLo]; + m_tlb[index][0] = m_cop0[COP0_EntryHi]; + m_tlb[index][1] = m_cop0[COP0_EntryLo]; LOGMASKED(LOG_TLB, "tlb write index %d asid %d vpn 0x%08x pfn 0x%08x %c%c%c%c (%s)\n", - index, (m_cpr[0][COP0_EntryHi] & EH_ASID) >> 6, m_cpr[0][COP0_EntryHi] & EH_VPN, m_cpr[0][COP0_EntryLo] & EL_PFN, - m_cpr[0][COP0_EntryLo] & EL_N ? 'N' : '-', - m_cpr[0][COP0_EntryLo] & EL_D ? 'D' : '-', - m_cpr[0][COP0_EntryLo] & EL_V ? 'V' : '-', - m_cpr[0][COP0_EntryLo] & EL_G ? 'G' : '-', + index, (m_cop0[COP0_EntryHi] & EH_ASID) >> 6, m_cop0[COP0_EntryHi] & EH_VPN, m_cop0[COP0_EntryLo] & EL_PFN, + m_cop0[COP0_EntryLo] & EL_N ? 'N' : '-', + m_cop0[COP0_EntryLo] & EL_D ? 'D' : '-', + m_cop0[COP0_EntryLo] & EL_V ? 'V' : '-', + m_cop0[COP0_EntryLo] & EL_G ? 'G' : '-', machine().describe_context()); } break; @@ -584,37 +1178,37 @@ void mips1_device_base::handle_cop0(u32 const op) { u8 const random = get_cop0_reg(COP0_Random) >> 8; - m_tlb[random][0] = m_cpr[0][COP0_EntryHi]; - m_tlb[random][1] = m_cpr[0][COP0_EntryLo]; + m_tlb[random][0] = m_cop0[COP0_EntryHi]; + m_tlb[random][1] = m_cop0[COP0_EntryLo]; LOGMASKED(LOG_TLB, "tlb write random %d asid %d vpn 0x%08x pfn 0x%08x %c%c%c%c (%s)\n", - random, (m_cpr[0][COP0_EntryHi] & EH_ASID) >> 6, m_cpr[0][COP0_EntryHi] & EH_VPN, m_cpr[0][COP0_EntryLo] & EL_PFN, - m_cpr[0][COP0_EntryLo] & EL_N ? 'N' : '-', - m_cpr[0][COP0_EntryLo] & EL_D ? 'D' : '-', - m_cpr[0][COP0_EntryLo] & EL_V ? 'V' : '-', - m_cpr[0][COP0_EntryLo] & EL_G ? 'G' : '-', + random, (m_cop0[COP0_EntryHi] & EH_ASID) >> 6, m_cop0[COP0_EntryHi] & EH_VPN, m_cop0[COP0_EntryLo] & EL_PFN, + m_cop0[COP0_EntryLo] & EL_N ? 'N' : '-', + m_cop0[COP0_EntryLo] & EL_D ? 'D' : '-', + m_cop0[COP0_EntryLo] & EL_V ? 'V' : '-', + m_cop0[COP0_EntryLo] & EL_G ? 'G' : '-', machine().describe_context()); } break; case 0x42000008: // TLBP - probe tlb - m_cpr[0][COP0_Index] = 0x80000000; + m_cop0[COP0_Index] = 0x80000000; for (u8 index = 0; index < 64; index++) { // test vpn and optionally asid u32 const mask = (m_tlb[index][1] & EL_G) ? EH_VPN : EH_VPN | EH_ASID; - if ((m_tlb[index][0] & mask) == (m_cpr[0][COP0_EntryHi] & mask)) + if ((m_tlb[index][0] & mask) == (m_cop0[COP0_EntryHi] & mask)) { LOGMASKED(LOG_TLB, "tlb probe hit vpn 0x%08x index %d (%s)\n", - m_cpr[0][COP0_EntryHi] & mask, index, machine().describe_context()); + m_cop0[COP0_EntryHi] & mask, index, machine().describe_context()); - m_cpr[0][COP0_Index] = index << 8; + m_cop0[COP0_Index] = index << 8; break; } } - if ((VERBOSE & LOG_TLB) && BIT(m_cpr[0][COP0_Index], 31)) + if ((VERBOSE & LOG_TLB) && BIT(m_cop0[COP0_Index], 31)) LOGMASKED(LOG_TLB, "tlb probe miss asid %d vpn 0x%08x(%s)\n", - (m_cpr[0][COP0_EntryHi] & EH_ASID) >> 6, m_cpr[0][COP0_EntryHi] & EH_VPN, machine().describe_context()); + (m_cop0[COP0_EntryHi] & EH_ASID) >> 6, m_cop0[COP0_EntryHi] & EH_VPN, machine().describe_context()); break; default: @@ -622,495 +1216,558 @@ void mips1_device_base::handle_cop0(u32 const op) } } -void mips1core_device_base::set_cop1_creg(int idx, u32 val) +u32 mips1_device_base::get_cop0_reg(unsigned const reg) { - // fpu revision register is read-only - if (idx) - m_ccr[1][idx] = val; + // assume 64-entry tlb with 8 wired entries + if (reg == COP0_Random) + m_cop0[reg] = (63 - ((total_cycles() - m_reset_time) % 56)) << 8; + + return m_cop0[reg]; } -void mips1core_device_base::handle_cop1(u32 const op) +void mips1_device_base::handle_cop1(u32 const op) { - if (!m_hasfpu) - return; - - switch (RSREG) + if (!(SR & SR_COP1)) { - case 0x00: /* MFCz */ if (RTREG) RTVAL = get_cop_reg<1>(RDREG); break; - case 0x02: /* CFCz */ if (RTREG) RTVAL = get_cop_creg<1>(RDREG); break; - case 0x04: /* MTCz */ set_cop_reg<1>(RDREG, RTVAL); break; - case 0x06: /* CTCz */ set_cop1_creg(RDREG, RTVAL); break; - case 0x08: /* BC */ - switch (RTREG) - { - case 0x00: /* BCzF */ if (!m_in_brcond[1]()) ADDPC(SIMMVAL); break; - case 0x01: /* BCzT */ if (m_in_brcond[1]()) ADDPC(SIMMVAL); break; - case 0x02: /* BCzFL */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x03: /* BCzTL */ generate_exception(EXCEPTION_INVALIDOP); break; - default: generate_exception(EXCEPTION_INVALIDOP); break; - } - break; - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1a: - case 0x1b: - case 0x1c: - case 0x1d: - case 0x1e: - case 0x1f: /* COP */ generate_exception(EXCEPTION_INVALIDOP); break; - default: generate_exception(EXCEPTION_INVALIDOP); break; + generate_exception(EXCEPTION_BADCOP1); + return; } -} -template void mips1core_device_base::handle_cop(u32 const op) -{ - switch (RSREG) + if (!m_fcr0) + return; + + switch (op >> 26) { - case 0x00: /* MFCz */ if (RTREG) RTVAL = get_cop_reg(RDREG); break; - case 0x02: /* CFCz */ if (RTREG) RTVAL = get_cop_creg(RDREG); break; - case 0x04: /* MTCz */ set_cop_reg(RDREG, RTVAL); break; - case 0x06: /* CTCz */ set_cop_creg(RDREG, RTVAL); break; - case 0x08: /* BC */ - switch (RTREG) + case 0x11: // COP1 + switch ((op >> 21) & 0x1f) + { + case 0x00: // MFC1 + if (FSREG & 1) + // move the high half of the floating point register + m_r[RTREG] = m_f[FSREG >> 1] >> 32; + else + // move the low half of the floating point register + m_r[RTREG] = m_f[FSREG >> 1] >> 0; + break; + case 0x02: // CFC1 + switch (FSREG) { - case 0x00: /* BCzF */ if (!m_in_brcond[Coprocessor]()) ADDPC(SIMMVAL); break; - case 0x01: /* BCzT */ if (m_in_brcond[Coprocessor]()) ADDPC(SIMMVAL); break; - case 0x02: /* BCzFL */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x03: /* BCzTL */ generate_exception(EXCEPTION_INVALIDOP); break; - default: generate_exception(EXCEPTION_INVALIDOP); break; + case 0: m_r[RTREG] = m_fcr0; break; + case 30: m_r[RTREG] = m_fcr30; break; + case 31: m_r[RTREG] = m_fcr31; break; + break; + + default: + logerror("cfc1 undefined fpu control register %d (%s)\n", FSREG, machine().describe_context()); + break; } break; - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1a: - case 0x1b: - case 0x1c: - case 0x1d: - case 0x1e: - case 0x1f: /* COP */ generate_exception(EXCEPTION_INVALIDOP); break; - default: generate_exception(EXCEPTION_INVALIDOP); break; - } -} + case 0x04: // MTC1 + if (FSREG & 1) + // load the high half of the floating point register + m_f[FSREG >> 1] = (u64(m_r[RTREG]) << 32) | u32(m_f[FSREG >> 1]); + else + // load the low half of the floating point register + m_f[FSREG >> 1] = (m_f[FSREG >> 1] & ~0xffffffffULL) | m_r[RTREG]; + break; + case 0x06: // CTC1 + switch (RDREG) + { + case 0: // register is read-only + break; -void mips1core_device_base::execute_run() -{ - // check for IRQs - check_irqs(); + case 30: + m_fcr30 = m_r[RTREG]; + break; - // core execution loop - do - { - // debugging - debugger_instruction_hook(m_pc); + case 31: + m_fcr31 = m_r[RTREG]; - 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) + // update rounding mode + switch (m_fcr31 & FCR31_RM) { - load(ptr, [](char c) { printf("%c", c); }); - ptr++; - length--; + case 0: softfloat_roundingMode = softfloat_round_near_even; break; + case 1: softfloat_roundingMode = softfloat_round_minMag; break; + case 2: softfloat_roundingMode = softfloat_round_max; break; + case 3: softfloat_roundingMode = softfloat_round_min; break; } - fflush(stdout); - } - } - // fetch and execute instruction - fetch(m_pc, [this](u32 const op) - { - // parse the instruction - switch (op >> 26) + // exception check + if ((m_fcr31 & FCR31_CE) || ((m_fcr31 & FCR31_CM) >> 5) & (m_fcr31 & FCR31_EM)) + execute_set_input(m_fpu_irq, ASSERT_LINE); + break; + + default: + logerror("ctc1 undefined fpu control register %d (%s)\n", RDREG, machine().describe_context()); + break; + } + break; + case 0x08: // BC + switch ((op >> 16) & 0x1f) + { + case 0x00: // BC1F + if (!(m_fcr31 & FCR31_C)) { - case 0x00: /* SPECIAL */ - switch (op & 63) - { - case 0x00: /* SLL */ if (RDREG) RDVAL = RTVAL << SHIFT; break; - case 0x02: /* SRL */ if (RDREG) RDVAL = RTVAL >> SHIFT; break; - case 0x03: /* SRA */ if (RDREG) RDVAL = s32(RTVAL) >> SHIFT; break; - case 0x04: /* SLLV */ if (RDREG) RDVAL = RTVAL << (RSVAL & 31); break; - case 0x06: /* SRLV */ if (RDREG) RDVAL = RTVAL >> (RSVAL & 31); break; - case 0x07: /* SRAV */ if (RDREG) RDVAL = s32(RTVAL) >> (RSVAL & 31); break; - case 0x08: /* JR */ SETPC(RSVAL); break; - case 0x09: /* JALR */ SETPCL(RSVAL, RDREG); break; - case 0x0c: /* SYSCALL */ generate_exception(EXCEPTION_SYSCALL); break; - case 0x0d: /* BREAK */ generate_exception(EXCEPTION_BREAK); break; - case 0x0f: /* SYNC */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x10: /* MFHI */ if (RDREG) RDVAL = m_hi; break; - case 0x11: /* MTHI */ m_hi = RSVAL; break; - case 0x12: /* MFLO */ if (RDREG) RDVAL = m_lo; break; - case 0x13: /* MTLO */ m_lo = RSVAL; break; - case 0x18: /* MULT */ - { - u64 product = mul_32x32(RSVAL, RTVAL); + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + } + break; + case 0x01: // BC1T + if (m_fcr31 & FCR31_C) + { + m_branch_state = BRANCH; + m_branch_target = m_pc + 4 + (s32(SIMMVAL) << 2); + } + break; - m_lo = product; - m_hi = product >> 32; - m_icount -= 11; - } - break; - case 0x19: /* MULTU */ - { - u64 product = mulu_32x32(RSVAL, RTVAL); + default: + // unimplemented operation + m_fcr31 |= FCR31_CE; + execute_set_input(m_fpu_irq, ASSERT_LINE); + break; + } + break; + case 0x10: // S + switch (op & 0x3f) + { + case 0x00: // ADD.S + set_cop1_reg(FDREG >> 1, f32_add(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) }).v); + break; + case 0x01: // SUB.S + set_cop1_reg(FDREG >> 1, f32_sub(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) }).v); + break; + case 0x02: // MUL.S + set_cop1_reg(FDREG >> 1, f32_mul(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) }).v); + break; + case 0x03: // DIV.S + set_cop1_reg(FDREG >> 1, f32_div(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) }).v); + break; + case 0x05: // ABS.S + if (f32_lt(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ 0 })) + set_cop1_reg(FDREG >> 1, f32_mul(float32_t{ u32(m_f[FSREG >> 1]) }, i32_to_f32(-1)).v); + else + set_cop1_reg(FDREG >> 1, m_f[FSREG >> 1]); + break; + case 0x06: // MOV.S + m_f[FDREG >> 1] = m_f[FSREG >> 1]; + break; + case 0x07: // NEG.S + set_cop1_reg(FDREG >> 1, f32_mul(float32_t{ u32(m_f[FSREG >> 1]) }, i32_to_f32(-1)).v); + break; - m_lo = product; - m_hi = product >> 32; - m_icount -= 11; - } - break; - case 0x1a: /* DIV */ - if (RTVAL) - { - m_lo = s32(RSVAL) / s32(RTVAL); - m_hi = s32(RSVAL) % s32(RTVAL); - } - m_icount -= 34; - break; - case 0x1b: /* DIVU */ - if (RTVAL) - { - m_lo = RSVAL / RTVAL; - m_hi = RSVAL % RTVAL; - } - m_icount -= 34; - break; - case 0x20: /* ADD */ - { - u32 const sum = RSVAL + RTVAL; - - // overflow: (sign(addend0) == sign(addend1)) && (sign(addend0) != sign(sum)) - if (!BIT(RSVAL ^ RTVAL, 31) && BIT(RSVAL ^ sum, 31)) - generate_exception(EXCEPTION_OVERFLOW); - else if (RDREG) - RDVAL = sum; - } - break; - case 0x21: /* ADDU */ if (RDREG) RDVAL = RSVAL + RTVAL; break; - case 0x22: /* SUB */ - { - u32 const difference = RSVAL - RTVAL; - - // overflow: (sign(minuend) != sign(subtrahend)) && (sign(minuend) != sign(difference)) - if (BIT(RSVAL ^ RTVAL, 31) && BIT(RSVAL ^ difference, 31)) - generate_exception(EXCEPTION_OVERFLOW); - else if (RDREG) - RDVAL = difference; - } - break; - case 0x23: /* SUBU */ if (RDREG) RDVAL = RSVAL - RTVAL; break; - case 0x24: /* AND */ if (RDREG) RDVAL = RSVAL & RTVAL; break; - case 0x25: /* OR */ if (RDREG) RDVAL = RSVAL | RTVAL; break; - case 0x26: /* XOR */ if (RDREG) RDVAL = RSVAL ^ RTVAL; break; - case 0x27: /* NOR */ if (RDREG) RDVAL = ~(RSVAL | RTVAL); break; - case 0x2a: /* SLT */ if (RDREG) RDVAL = s32(RSVAL) < s32(RTVAL); break; - case 0x2b: /* SLTU */ if (RDREG) RDVAL = u32(RSVAL) < u32(RTVAL); break; - case 0x30: /* TEQ */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x31: /* TGEU */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x32: /* TLT */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x33: /* TLTU */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x34: /* TGE */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x36: /* TNE */ generate_exception(EXCEPTION_INVALIDOP); break; - default: /* ??? */ generate_exception(EXCEPTION_INVALIDOP); break; - } - break; + case 0x21: // CVT.D.S + 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)); + break; - case 0x01: /* REGIMM */ - switch (RTREG) - { - case 0x00: /* BLTZ */ if (s32(RSVAL) < 0) ADDPC(SIMMVAL); break; - case 0x01: /* BGEZ */ if (s32(RSVAL) >= 0) ADDPC(SIMMVAL); break; - case 0x02: /* BLTZL */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x03: /* BGEZL */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x08: /* TGEI */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x09: /* TGEIU */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x0a: /* TLTI */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x0b: /* TLTIU */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x0c: /* TEQI */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x0e: /* TNEI */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x10: /* BLTZAL */ if (s32(RSVAL) < 0) ADDPCL(SIMMVAL, 31); break; - case 0x11: /* BGEZAL */ if (s32(RSVAL) >= 0) ADDPCL(SIMMVAL, 31); break; - case 0x12: /* BLTZALL */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x13: /* BGEZALL */ generate_exception(EXCEPTION_INVALIDOP); break; - default: /* ??? */ generate_exception(EXCEPTION_INVALIDOP); break; - } - break; + case 0x30: // C.F.S (false) + m_fcr31 &= ~FCR31_C; + break; + case 0x31: // C.UN.S (unordered) + f32_eq(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) }); + if (softfloat_exceptionFlags & softfloat_flag_invalid) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; + case 0x32: // C.EQ.S (equal) + if (f32_eq(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; + case 0x33: // C.UEQ.S (unordered equal) + if (f32_eq(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; + case 0x34: // C.OLT.S (less than) + if (f32_lt(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; + case 0x35: // C.ULT.S (unordered less than) + if (f32_lt(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; + case 0x36: // C.OLE.S (less than or equal) + if (f32_le(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; + case 0x37: // C.ULE.S (unordered less than or equal) + if (f32_le(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; - case 0x02: /* J */ ABSPC(LIMMVAL); break; - case 0x03: /* JAL */ ABSPCL(LIMMVAL, 31); break; - case 0x04: /* BEQ */ if (RSVAL == RTVAL) ADDPC(SIMMVAL); break; - case 0x05: /* BNE */ if (RSVAL != RTVAL) ADDPC(SIMMVAL); break; - case 0x06: /* BLEZ */ if (s32(RSVAL) <= 0) ADDPC(SIMMVAL); break; - case 0x07: /* BGTZ */ if (s32(RSVAL) > 0) ADDPC(SIMMVAL); break; - case 0x08: /* ADDI */ - { - u32 const sum = RSVAL + SIMMVAL; + case 0x38: // C.SF.S (signalling false) + f32_eq(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) }); - // overflow: (sign(addend0) == sign(addend1)) && (sign(addend0) != sign(sum)) - if (!BIT(RSVAL ^ s32(SIMMVAL), 31) && BIT(RSVAL ^ sum, 31)) - generate_exception(EXCEPTION_OVERFLOW); - else if (RTREG) - RTVAL = sum; - } - break; - case 0x09: /* ADDIU */ if (RTREG) RTVAL = RSVAL + SIMMVAL; break; - case 0x0a: /* SLTI */ if (RTREG) RTVAL = s32(RSVAL) < s32(SIMMVAL); break; - case 0x0b: /* SLTIU */ if (RTREG) RTVAL = u32(RSVAL) < u32(SIMMVAL); break; - case 0x0c: /* ANDI */ if (RTREG) RTVAL = RSVAL & UIMMVAL; break; - case 0x0d: /* ORI */ if (RTREG) RTVAL = RSVAL | UIMMVAL; break; - case 0x0e: /* XORI */ if (RTREG) RTVAL = RSVAL ^ UIMMVAL; break; - case 0x0f: /* LUI */ if (RTREG) RTVAL = UIMMVAL << 16; break; - case 0x10: /* COP0 */ - if (!(SR & SR_KUc) || (SR & SR_COP0)) - handle_cop0(op); - else - generate_exception(EXCEPTION_BADCOP0); - break; - case 0x11: // COP1 - if (SR & SR_COP1) - handle_cop1(op); - else - generate_exception(EXCEPTION_BADCOP1); - break; - case 0x12: // COP2 - if (SR & SR_COP2) - handle_cop<2>(op); - else - generate_exception(EXCEPTION_BADCOP2); - break; - case 0x13: // COP3 - if (SR & SR_COP3) - handle_cop<3>(op); - else - generate_exception(EXCEPTION_BADCOP3); - break; - case 0x14: /* BEQL */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x15: /* BNEL */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x16: /* BLEZL */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x17: /* BGTZL */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x20: /* LB */ load(SIMMVAL + RSVAL, [this, op](s8 temp) { if (RTREG) RTVAL = temp; }); break; - case 0x21: /* LH */ load(SIMMVAL + RSVAL, [this, op](s16 temp) { if (RTREG) RTVAL = temp; }); break; - case 0x22: /* LWL */ lwl(op); break; - case 0x23: /* LW */ load(SIMMVAL + RSVAL, [this, op](u32 temp) { if (RTREG) RTVAL = temp; }); break; - case 0x24: /* LBU */ load(SIMMVAL + RSVAL, [this, op](u8 temp) { if (RTREG) RTVAL = temp; }); break; - case 0x25: /* LHU */ load(SIMMVAL + RSVAL, [this, op](u16 temp) { if (RTREG) RTVAL = temp; }); break; - case 0x26: /* LWR */ lwr(op); break; - case 0x28: /* SB */ store(SIMMVAL + RSVAL, RTVAL); break; - case 0x29: /* SH */ store(SIMMVAL + RSVAL, RTVAL); break; - case 0x2a: /* SWL */ swl(op); break; - case 0x2b: /* SW */ store(SIMMVAL + RSVAL, RTVAL); break; - case 0x2e: /* SWR */ swr(op); break; - case 0x2f: /* CACHE */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x30: /* LL */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x31: /* LWC1 */ load(SIMMVAL + RSVAL, [this, op](u32 temp) { set_cop_reg<1>(RTREG, temp); }); break; - case 0x32: /* LWC2 */ load(SIMMVAL + RSVAL, [this, op](u32 temp) { set_cop_reg<2>(RTREG, temp); }); break; - case 0x33: /* LWC3 */ load(SIMMVAL + RSVAL, [this, op](u32 temp) { set_cop_reg<3>(RTREG, temp); }); break; - case 0x34: /* LDC0 */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x35: /* LDC1 */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x36: /* LDC2 */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x37: /* LDC3 */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x38: /* SC */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x39: /* LWC1 */ store(SIMMVAL + RSVAL, get_cop_reg<1>(RTREG)); break; - case 0x3a: /* LWC2 */ store(SIMMVAL + RSVAL, get_cop_reg<2>(RTREG)); break; - case 0x3b: /* LWC3 */ store(SIMMVAL + RSVAL, get_cop_reg<3>(RTREG)); break; - case 0x3c: /* SDC0 */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x3d: /* SDC1 */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x3e: /* SDC2 */ generate_exception(EXCEPTION_INVALIDOP); break; - case 0x3f: /* SDC3 */ generate_exception(EXCEPTION_INVALIDOP); break; - default: /* ??? */ generate_exception(EXCEPTION_INVALIDOP); break; - } + m_fcr31 &= ~FCR31_C; - // update pc and branch state - switch (m_branch_state) - { - case NONE: - m_pc += 4; + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } + break; + case 0x39: // C.NGLE.S (not greater, less than or equal) + f32_eq(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) }); + + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_C | FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } + else + m_fcr31 &= ~FCR31_C; break; + case 0x3a: // C.SEQ.S (signalling equal) + if (f32_eq(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; - case DELAY: - m_branch_state = NONE; - m_pc = m_branch_target; + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } break; + case 0x3b: // C.NGL.S (not greater or less than) + if (f32_eq(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; - case BRANCH: - m_branch_state = DELAY; - m_pc += 4; + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } break; + case 0x3c: // C.LT.S (less than) + if (f32_lt(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; - case EXCEPTION: - m_branch_state = NONE; + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } break; - } - }); - m_icount--; + case 0x3d: // C.NGE.S (not greater or equal) + if (f32_lt(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; - } while (m_icount > 0 || m_branch_state); -} + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } + break; + case 0x3e: // C.LE.S (less than or equal) + if (f32_le(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; -void mips1core_device_base::lwl(u32 const op) -{ - offs_t const offset = SIMMVAL + RSVAL; - load(offset & ~3, [this, op, offset](u32 temp) - { - if (RTREG) - { - unsigned const shift = ((offset & 3) ^ ENDIAN_VALUE_LE_BE(m_endianness, 3, 0)) << 3; + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } + break; + case 0x3f: // C.NGT.S (not greater than) + if (f32_le(float32_t{ u32(m_f[FSREG >> 1]) }, float32_t{ u32(m_f[FTREG >> 1]) }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; - RTVAL = (RTVAL & ~u32(0xffffffffU << shift)) | (temp << shift); - } - }); -} + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } + break; -void mips1core_device_base::lwr(u32 const op) -{ - offs_t const offset = SIMMVAL + RSVAL; - load(offset & ~3, [this, op, offset](u32 temp) - { - if (RTREG) - { - unsigned const shift = ((offset & 0x3) ^ ENDIAN_VALUE_LE_BE(m_endianness, 0, 3)) << 3; + default: // unimplemented operation + m_fcr31 |= FCR31_CE; + execute_set_input(m_fpu_irq, ASSERT_LINE); + break; + } + break; + case 0x11: // D + switch (op & 0x3f) + { + case 0x00: // ADD.D + set_cop1_reg(FDREG >> 1, f64_add(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] }).v); + break; + case 0x01: // SUB.D + set_cop1_reg(FDREG >> 1, f64_sub(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] }).v); + break; + case 0x02: // MUL.D + set_cop1_reg(FDREG >> 1, f64_mul(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] }).v); + break; + case 0x03: // DIV.D + set_cop1_reg(FDREG >> 1, f64_div(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] }).v); + break; - RTVAL = (RTVAL & ~u32(0xffffffffU >> shift)) | (temp >> shift); - } - }); -} + case 0x05: // ABS.D + if (f64_lt(float64_t{ m_f[FSREG >> 1] }, float64_t{ 0 })) + set_cop1_reg(FDREG >> 1, f64_mul(float64_t{ m_f[FSREG >> 1] }, i32_to_f64(-1)).v); + else + set_cop1_reg(FDREG >> 1, m_f[FSREG >> 1]); + break; + case 0x06: // MOV.D + m_f[FDREG >> 1] = m_f[FSREG >> 1]; + break; + case 0x07: // NEG.D + set_cop1_reg(FDREG >> 1, f64_mul(float64_t{ m_f[FSREG >> 1] }, i32_to_f64(-1)).v); + break; -void mips1core_device_base::swl(u32 const op) -{ - offs_t const offset = SIMMVAL + RSVAL; - unsigned const shift = ((offset & 3) ^ ENDIAN_VALUE_LE_BE(m_endianness, 3, 0)) << 3; + case 0x20: // CVT.S.D + 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)); + break; - // only load if necessary - if (shift) - { - load(offset & ~3, [this, op, offset, shift](u32 temp) - { - store(offset & ~3, (temp & ~u32(0xffffffffU >> shift)) | (RTVAL >> shift)); - }); - } - else - store(offset & ~3, RTVAL); -} + case 0x30: // C.F.D (false) + m_fcr31 &= ~FCR31_C; + break; + case 0x31: // C.UN.D (unordered) + f64_eq(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] }); + if (softfloat_exceptionFlags & softfloat_flag_invalid) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; + case 0x32: // C.EQ.D (equal) + if (f64_eq(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; + case 0x33: // C.UEQ.D (unordered equal) + if (f64_eq(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; + case 0x34: // C.OLT.D (less than) + if (f64_lt(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; + case 0x35: // C.ULT.D (unordered less than) + if (f64_lt(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; + case 0x36: // C.OLE.D (less than or equal) + if (f64_le(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; + case 0x37: // C.ULE.D (unordered less than or equal) + if (f64_le(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; + break; + case 0x38: // C.SF.D (signalling false) + f64_eq(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] }); -void mips1core_device_base::swr(u32 const op) -{ - offs_t const offset = SIMMVAL + RSVAL; - unsigned const shift = ((offset & 3) ^ ENDIAN_VALUE_LE_BE(m_endianness, 0, 3)) << 3; + m_fcr31 &= ~FCR31_C; - // only load if necessary - if (shift) - { - load(offset & ~3, [this, op, offset, shift](u32 temp) - { - store(offset & ~3, (temp & ~u32(0xffffffffU << shift)) | (RTVAL << shift)); - }); - } - else - store(offset & ~3, RTVAL); -} + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } + break; + case 0x39: // C.NGLE.D (not greater, less than or equal) + f64_eq(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] }); -template std::enable_if_t>::value, void> mips1core_device_base::load(u32 program_address, U &&apply) -{ - offs_t translated_address = program_address; + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_C | FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } + else + m_fcr31 &= ~FCR31_C; + break; + case 0x3a: // C.SEQ.D (signalling equal) + if (f64_eq(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; - if (memory_translate(m_data_spacenum, TRANSLATE_READ, translated_address)) - { - switch (sizeof(T)) - { - case 1: apply(T(space(m_data_spacenum).read_byte(translated_address))); break; - case 2: apply(T(space(m_data_spacenum).read_word(translated_address))); break; - case 4: apply(T(space(m_data_spacenum).read_dword(translated_address))); break; - } - } -} + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } + break; + case 0x3b: // C.NGL.D (not greater or less than) + if (f64_eq(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; -template std::enable_if_t::value, void> mips1core_device_base::store(u32 program_address, U data) -{ - offs_t translated_address = program_address; + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } + break; + case 0x3c: // C.LT.D (less than) + if (f64_lt(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; - if (memory_translate(m_data_spacenum, TRANSLATE_WRITE, translated_address)) - { - switch (sizeof(T)) - { - case 1: space(m_data_spacenum).write_byte(translated_address, T(data)); break; - case 2: space(m_data_spacenum).write_word(translated_address, T(data)); break; - case 4: space(m_data_spacenum).write_dword(translated_address, T(data)); break; - } - } -} + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } + break; + case 0x3d: // C.NGE.D (not greater or equal) + if (f64_lt(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; -bool mips1core_device_base::fetch(u32 program_address, std::function &&apply) -{ - offs_t translated_address = program_address; + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } + break; + case 0x3e: // C.LE.D (less than or equal) + if (f64_le(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] })) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; - if (memory_translate(0, TRANSLATE_FETCH, translated_address)) - { - apply(space(0).read_dword(translated_address)); + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } + break; + case 0x3f: // C.NGT.D (not greater than) + if (f64_le(float64_t{ m_f[FSREG >> 1] }, float64_t{ m_f[FTREG >> 1] }) || (softfloat_exceptionFlags & softfloat_flag_invalid)) + m_fcr31 |= FCR31_C; + else + m_fcr31 &= ~FCR31_C; - return true; - } - else - return false; -} + if (softfloat_exceptionFlags & softfloat_flag_invalid) + { + m_fcr31 |= FCR31_CV; + execute_set_input(m_fpu_irq, ASSERT_LINE); + } + break; -bool mips1core_device_base::memory_translate(int spacenum, int intention, offs_t &address) -{ - // check for kernel memory address - if (BIT(address, 31)) - { - // check debug or kernel mode - if ((intention & TRANSLATE_DEBUG_MASK) || !(SR & SR_KUc)) - { - switch (address & 0xe0000000) + default: // unimplemented operation + m_fcr31 |= FCR31_CE; + execute_set_input(m_fpu_irq, ASSERT_LINE); + break; + } + break; + case 0x14: // W + switch (op & 0x3f) { - case 0x80000000: // kseg0: unmapped, cached, privileged - case 0xa0000000: // kseg1: unmapped, uncached, privileged - address &= ~0xe0000000; + case 0x20: // CVT.S.W + set_cop1_reg(FDREG >> 1, i32_to_f32(s32(m_f[FSREG >> 1])).v); + break; + case 0x21: // CVT.D.W + set_cop1_reg(FDREG >> 1, i32_to_f64(s32(m_f[FSREG >> 1])).v); break; - case 0xc0000000: // kseg2: mapped, cached, privileged - case 0xe0000000: + default: // unimplemented operation + m_fcr31 |= FCR31_CE; + execute_set_input(m_fpu_irq, ASSERT_LINE); break; } + break; + + default: // unimplemented operation + m_fcr31 |= FCR31_CE; + execute_set_input(m_fpu_irq, ASSERT_LINE); + break; } - else if (SR & SR_KUc) + break; + case 0x31: // LWC1 + load(SIMMVAL + m_r[RSREG], + [this, op](u32 data) { - if (!machine().side_effects_disabled()) - { - // exception - m_cpr[0][COP0_BadVAddr] = address; + if (FTREG & 1) + // load the high half of the floating point register + m_f[FTREG >> 1] = (u64(data) << 32) | u32(m_f[FTREG >> 1]); + else + // load the low half of the floating point register + m_f[FTREG >> 1] = (m_f[FTREG >> 1] & ~0xffffffffULL) | data; + }); + break; + case 0x39: // SWC1 + if (FTREG & 1) + // store the high half of the floating point register + store(SIMMVAL + m_r[RSREG], m_f[FTREG >> 1] >> 32); + else + // store the low half of the floating point register + store(SIMMVAL + m_r[RSREG], m_f[FTREG >> 1]); + break; + } +} - generate_exception((intention & TRANSLATE_WRITE) ? EXCEPTION_ADDRSTORE : EXCEPTION_ADDRLOAD); - } - return false; +void mips1_device_base::set_cop1_reg(unsigned const reg, u64 const data) +{ + // translate softfloat exception flags to cause register + if (softfloat_exceptionFlags) + { + if (softfloat_exceptionFlags & softfloat_flag_inexact) + m_fcr31 |= FCR31_CI; + if (softfloat_exceptionFlags & softfloat_flag_underflow) + m_fcr31 |= FCR31_CU; + if (softfloat_exceptionFlags & softfloat_flag_overflow) + m_fcr31 |= FCR31_CO; + if (softfloat_exceptionFlags & softfloat_flag_infinite) + m_fcr31 |= FCR31_CZ; + if (softfloat_exceptionFlags & softfloat_flag_invalid) + m_fcr31 |= FCR31_CV; + + // check if exception is enabled + if (((m_fcr31 & FCR31_CM) >> 5) & (m_fcr31 & FCR31_EM)) + { + execute_set_input(m_fpu_irq, ASSERT_LINE); + return; } + + // set flags + m_fcr31 |= ((m_fcr31 & FCR31_CM) >> 10); } - else - // kuseg physical addresses have a 1GB offset - address += 0x40000000; - return true; + m_f[reg] = data; } bool mips1_device_base::memory_translate(int spacenum, int intention, offs_t &address) @@ -1138,7 +1795,7 @@ bool mips1_device_base::memory_translate(int spacenum, int intention, offs_t &ad if (!machine().side_effects_disabled()) { // exception - m_cpr[0][COP0_BadVAddr] = address; + m_cop0[COP0_BadVAddr] = address; generate_exception((intention & TRANSLATE_WRITE) ? EXCEPTION_ADDRSTORE : EXCEPTION_ADDRLOAD); } @@ -1147,12 +1804,18 @@ bool mips1_device_base::memory_translate(int spacenum, int intention, offs_t &ad } // key is a combination of VPN and ASID - u32 const key = (address & EH_VPN) | (m_cpr[0][COP0_EntryHi] & EH_ASID); + u32 const key = (address & EH_VPN) | (m_cop0[COP0_EntryHi] & EH_ASID); + + unsigned *mru = m_tlb_mru[intention & TRANSLATE_TYPE_MASK]; + bool refill = !BIT(address, 31); bool modify = false; - for (u32 const *entry : m_tlb) + for (unsigned i = 0; i < ARRAY_LENGTH(m_tlb); i++) { + unsigned const index = mru[i]; + u32 const *const entry = m_tlb[index]; + // test vpn and optionally asid u32 const mask = (entry[1] & EL_G) ? EH_VPN : EH_VPN | EH_ASID; if ((entry[0] & mask) != (key & mask)) @@ -1176,6 +1839,11 @@ bool mips1_device_base::memory_translate(int spacenum, int intention, offs_t &ad // translate the address address &= ~EH_VPN; address |= (entry[1] & EL_PFN); + + // promote the entry in the mru index + if (i > 0) + std::swap(mru[i - 1], mru[i]); + return true; } @@ -1185,77 +1853,19 @@ bool mips1_device_base::memory_translate(int spacenum, int intention, offs_t &ad { if (modify) LOGMASKED(LOG_TLB, "tlb modify asid %d address 0x%08x (%s)\n", - (m_cpr[0][COP0_EntryHi] & EH_ASID) >> 6, address, machine().describe_context()); + (m_cop0[COP0_EntryHi] & EH_ASID) >> 6, address, machine().describe_context()); else LOGMASKED(LOG_TLB, "tlb miss %c asid %d address 0x%08x (%s)\n", - (intention & TRANSLATE_WRITE) ? 'w' : 'r', (m_cpr[0][COP0_EntryHi] & EH_ASID) >> 6, address, machine().describe_context()); + (intention & TRANSLATE_WRITE) ? 'w' : 'r', (m_cop0[COP0_EntryHi] & EH_ASID) >> 6, address, machine().describe_context()); } // load tlb exception registers - m_cpr[0][COP0_BadVAddr] = address; - m_cpr[0][COP0_EntryHi] = key; - m_cpr[0][COP0_Context] = (m_cpr[0][COP0_Context] & PTE_BASE) | ((address >> 10) & BAD_VPN); + m_cop0[COP0_BadVAddr] = address; + 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); } return false; } - -std::string mips1core_device_base::debug_string(u32 string_pointer, int const limit) -{ - auto const suppressor(machine().disable_side_effects()); - - bool done = false; - bool mapped = false; - std::string result(""); - - while (!done) - { - done = true; - load(string_pointer++, [limit, &done, &mapped, &result](u8 byte) - { - mapped = true; - if (byte != 0) - { - result += byte; - - done = result.length() == limit; - } - }); - } - - if (!mapped) - result.assign("[unmapped]"); - - return result; -} - -std::string mips1core_device_base::debug_string_array(u32 array_pointer) -{ - auto const suppressor(machine().disable_side_effects()); - - bool done = false; - std::string result(""); - - while (!done) - { - done = true; - load(array_pointer, [this, &done, &result](u32 string_pointer) - { - if (string_pointer != 0) - { - if (!result.empty()) - result += ", "; - - result += '\"' + debug_string(string_pointer) + '\"'; - - done = false; - } - }); - - array_pointer += 4; - } - - return result; -} diff --git a/src/devices/cpu/mips/mips1.h b/src/devices/cpu/mips/mips1.h index 3b1f6126e20..3ae34e4b69f 100644 --- a/src/devices/cpu/mips/mips1.h +++ b/src/devices/cpu/mips/mips1.h @@ -9,21 +9,8 @@ class mips1core_device_base : public cpu_device { public: - // floating point coprocessor revision numbers recognised by RISC/os 4.52 and IRIX - enum fpu_rev_t : u32 - { - MIPS_R2360 = 0x0100, // MIPS R2360 Floating Point Board - MIPS_R2010 = 0x0200, // MIPS R2010 VLSI Floating Point Chip - MIPS_R2010A = 0x0310, // MIPS R2010A VLSI Floating Point Chip - MIPS_R3010 = 0x0320, // MIPS R3010 VLSI Floating Point Chip - MIPS_R3010A = 0x0330, // MIPS R3010A VLSI Floating Point Chip - MIPS_R3010Av4 = 0x0340, // MIPS R3010A VLSI Floating Point Chip - MIPS_R6010 = 0x0400, // MIPS R6010 Floating Point Chip - }; - // device configuration void set_endianness(endianness_t endianness) { m_endianness = endianness; } - void set_fpurev(u32 revision) { m_hasfpu = true; m_fpurev = revision; } // input lines template auto in_brcond() { return m_in_brcond[Coprocessor].bind(); } @@ -31,35 +18,17 @@ public: 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); - enum registers + enum registers : unsigned { - // general purpose cpu registers - MIPS1_R0, MIPS1_R1, MIPS1_R2, MIPS1_R3, MIPS1_R4, MIPS1_R5, MIPS1_R6, MIPS1_R7, - MIPS1_R8, MIPS1_R9, MIPS1_R10, MIPS1_R11, MIPS1_R12, MIPS1_R13, MIPS1_R14, MIPS1_R15, - MIPS1_R16, MIPS1_R17, MIPS1_R18, MIPS1_R19, MIPS1_R20, MIPS1_R21, MIPS1_R22, MIPS1_R23, - MIPS1_R24, MIPS1_R25, MIPS1_R26, MIPS1_R27, MIPS1_R28, MIPS1_R29, MIPS1_R30, MIPS1_R31, + MIPS1_R0 = 0, + MIPS1_COP0 = 32, + MIPS1_F0 = 64, - // other cpu registers + MIPS1_PC = 80, MIPS1_HI, MIPS1_LO, - MIPS1_PC, - - // coprocessor 0 registers - MIPS1_COP0_INDEX, // reg 0, tlb only - MIPS1_COP0_RANDOM, // reg 1, tlb only - MIPS1_COP0_ENTRYLO, // reg 2, tlb only - MIPS1_COP0_BUSCTRL, // reg 2, r3041 only - MIPS1_COP0_CONFIG, // reg 3, r3041/r3071/r3081 only - MIPS1_COP0_CONTEXT, // reg 4, tlb only - MIPS1_COP0_BADVADDR, // reg 8 - MIPS1_COP0_COUNT, // reg 9, r3041 only - MIPS1_COP0_ENTRYHI, // reg 10, tlb only - MIPS1_COP0_PORTSIZE, // reg 10, r3041 only - MIPS1_COP0_COMPARE, // reg 11, r3041 only - MIPS1_COP0_SR, // reg 12 - MIPS1_COP0_CAUSE, // reg 13 - MIPS1_COP0_EPC, // reg 14 - MIPS1_COP0_PRID, // reg 15 + MIPS1_FCR30, + MIPS1_FCR31, }; enum exception : u32 @@ -75,12 +44,14 @@ protected: 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, - EXCEPTION_OVERFLOW = 0x00000030, - EXCEPTION_TRAP = 0x00000034, }; enum cop0_reg : u8 @@ -184,7 +155,7 @@ protected: virtual u32 execute_max_cycles() const override { return 40; } virtual u32 execute_input_lines() const override { return 6; } virtual void execute_run() override; - virtual void execute_set_input(int inputnum, int state) override { set_irq_line(inputnum, state); } + virtual void execute_set_input(int inputnum, int state) override; // device_memory_interface overrides virtual space_config_vector memory_space_config() const override; @@ -199,23 +170,16 @@ protected: // interrupts void generate_exception(u32 exception, bool refill = false); void check_irqs(); - void set_irq_line(int irqline, int state); // cop0 - virtual u32 get_cop0_reg(int const index); - void set_cop0_reg(int const index, u32 const data); virtual void handle_cop0(u32 const op); + virtual u32 get_cop0_reg(unsigned const reg); + virtual void set_cop0_reg(unsigned const reg, u32 const data); - // cop1 - void set_cop1_creg(int const index, u32 const data); - void handle_cop1(u32 const op); - - // generic coprocessor implementation - template void handle_cop(u32 const op); - template u32 get_cop_reg(int const index) { return m_cpr[Coprocessor][index]; } - template void set_cop_reg(int const index, u32 const data) { m_cpr[Coprocessor][index] = data; } - template u32 get_cop_creg(int const index) { return m_ccr[Coprocessor][index]; } - template void set_cop_creg(int const index, u32 const data) { m_ccr[Coprocessor][index] = data; } + // other coprocessors + virtual void handle_cop1(u32 const op); + virtual void handle_cop2(u32 const op); + virtual void handle_cop3(u32 const op); // load/store left/right opcodes void lwl(u32 const op); @@ -224,12 +188,12 @@ protected: void swr(u32 const op); // memory accessors - template std::enable_if_t>::value, void> load(u32 program_address, U &&apply); - template std::enable_if_t::value, void> store(u32 program_address, U data); - bool fetch(u32 program_address, std::function &&apply); + template std::enable_if_t>::value, void> load(u32 address, U &&apply); + template std::enable_if_t::value, void> store(u32 address, U data, T mem_mask = ~T(0)); + bool fetch(u32 address, std::function &&apply); // debug helpers - std::string debug_string(u32 string_pointer, int const limit = 0); + std::string debug_string(u32 string_pointer, unsigned const limit = 0); std::string debug_string_array(u32 array_pointer); // address spaces @@ -242,19 +206,16 @@ protected: // configuration u32 m_cpurev; - bool m_hasfpu; - u32 m_fpurev; endianness_t m_endianness; // core registers u32 m_pc; + u32 m_r[32]; u32 m_hi; u32 m_lo; - u32 m_r[32]; - // COP registers - u32 m_cpr[4][32]; - u32 m_ccr[4][32]; + // cop0 registers + u32 m_cop0[32]; // internal stuff int m_icount; @@ -278,9 +239,54 @@ protected: 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 + { + MIPS_R2360 = 0x0100, // MIPS R2360 Floating Point Board + MIPS_R2010 = 0x0200, // MIPS R2010 VLSI Floating Point Chip + MIPS_R2010A = 0x0310, // MIPS R2010A VLSI Floating Point Chip + MIPS_R3010 = 0x0320, // MIPS R3010 VLSI Floating Point Chip + MIPS_R3010A = 0x0330, // MIPS R3010A VLSI Floating Point Chip + MIPS_R3010Av4 = 0x0340, // MIPS R3010A VLSI Floating Point Chip + MIPS_R6010 = 0x0400, // MIPS R6010 Floating Point Chip + }; + + 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) + }; + // device_t overrides virtual void device_start() override; virtual void device_reset() override; @@ -288,12 +294,24 @@ protected: // device_memory_interface overrides virtual bool memory_translate(int spacenum, int intention, offs_t &address) override; - virtual u32 get_cop0_reg(int idx) override; virtual void handle_cop0(u32 const op) override; + virtual u32 get_cop0_reg(unsigned const reg) override; + + virtual void handle_cop1(u32 const op) override; + virtual void set_cop1_reg(unsigned const reg, u64 const data); private: u64 m_reset_time; u32 m_tlb[64][2]; // 0 is hi, 1 is lo + unsigned m_tlb_mru[3][64]; + + // cop1 registers + u64 m_f[16]; + u32 m_fcr0; + u32 m_fcr30; + u32 m_fcr31; + + unsigned m_fpu_irq; }; class r2000_device : public mips1_device_base @@ -347,13 +365,13 @@ public: r3052e_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); }; -class r3071_device : public mips1core_device_base +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); }; -class r3081_device : public mips1core_device_base +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); diff --git a/src/mame/drivers/decstation.cpp b/src/mame/drivers/decstation.cpp index 322f94cd352..93e23d9fb0c 100644 --- a/src/mame/drivers/decstation.cpp +++ b/src/mame/drivers/decstation.cpp @@ -133,7 +133,7 @@ private: uint32_t kn01_screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); - required_device m_maincpu; + required_device m_maincpu; required_device m_screen; optional_device m_scantimer; optional_device m_sfb; @@ -546,7 +546,7 @@ static void dec_scsi_devices(device_slot_interface &device) MACHINE_CONFIG_START(decstation_state::kn01) R2000(config, m_maincpu, 16.67_MHz_XTAL, 65536, 131072); m_maincpu->set_endianness(ENDIANNESS_LITTLE); - m_maincpu->set_fpurev(0x340); + m_maincpu->set_fpu(mips1_device_base::MIPS_R3010Av4); m_maincpu->in_brcond<0>().set(FUNC(decstation_state::brcond0_r)); m_maincpu->set_addrmap(AS_PROGRAM, &decstation_state::kn01_map); @@ -570,7 +570,7 @@ MACHINE_CONFIG_END MACHINE_CONFIG_START(decstation_state::kn02ba) R3000A(config, m_maincpu, 33.333_MHz_XTAL, 65536, 131072); m_maincpu->set_endianness(ENDIANNESS_LITTLE); - m_maincpu->set_fpurev(0x340); // should be R3010A v4.0 + m_maincpu->set_fpu(mips1_device_base::MIPS_R3010Av4); m_maincpu->in_brcond<0>().set(FUNC(decstation_state::brcond0_r)); m_maincpu->set_addrmap(AS_PROGRAM, &decstation_state::threemin_map); diff --git a/src/mame/drivers/mips.cpp b/src/mame/drivers/mips.cpp index dd3a8b4f3e6..7b02b9161db 100644 --- a/src/mame/drivers/mips.cpp +++ b/src/mame/drivers/mips.cpp @@ -499,8 +499,7 @@ static void mips_scsi_devices(device_slot_interface &device) void rx2030_state::rx2030(machine_config &config) { R2000A(config, m_cpu, 33.333_MHz_XTAL / 2, 32768, 32768); - // TODO: FPU disabled until properly emulated - //m_cpu->set_fpurev(mips1_device_base::MIPS_R2010A); + m_cpu->set_fpu(mips1_device_base::MIPS_R2010A); m_cpu->in_brcond<0>().set([]() { return 1; }); // writeback complete V50(config, m_iop, 20_MHz_XTAL / 2); @@ -772,7 +771,7 @@ void rx3230_state::rx3230(machine_config &config) { R3000A(config, m_cpu, 50_MHz_XTAL / 2, 32768, 32768); m_cpu->set_addrmap(AS_PROGRAM, &rx3230_state::rx3230_map); - //m_cpu->set_fpurev(mips1_device_base::MIPS_R3010A); + m_cpu->set_fpu(mips1_device_base::MIPS_R3010A); m_cpu->in_brcond<0>().set([]() { return 1; }); // writeback complete // 32 SIMM slots, 8-128MB memory, banks of 8 1MB or 4MB SIMMs -- cgit v1.2.3