diff options
author | MooglyGuy <therealmogminer@gmail.com> | 2020-03-04 14:25:55 +0100 |
---|---|---|
committer | MooglyGuy <therealmogminer@gmail.com> | 2020-03-04 14:26:10 +0100 |
commit | c0d388dc09ca069af955ec260b1bdbc7a9bf00a7 (patch) | |
tree | ac0a3e02408ae0d09d5f796164f7cf8656d1fd60 /src/devices | |
parent | edc2024fc0e657b6be9c590199afeb748d4b13a3 (diff) |
-r4000: Added basic R5000 support. [Ryan Holtz]
Diffstat (limited to 'src/devices')
-rw-r--r-- | src/devices/cpu/mips/r4000.cpp | 219 | ||||
-rw-r--r-- | src/devices/cpu/mips/r4000.h | 18 |
2 files changed, 231 insertions, 6 deletions
diff --git a/src/devices/cpu/mips/r4000.cpp b/src/devices/cpu/mips/r4000.cpp index a5e19d7afe8..dea5bbb9915 100644 --- a/src/devices/cpu/mips/r4000.cpp +++ b/src/devices/cpu/mips/r4000.cpp @@ -84,6 +84,7 @@ 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") 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) : cpu_device(mconfig, type, tag, owner, clock) @@ -609,10 +610,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; @@ -816,7 +814,9 @@ 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]) { @@ -1044,7 +1044,7 @@ void r4000_base_device::cpu_execute(u32 const op) case 0x32: // LWC2 cp2_execute(op); break; - //case 0x33: // * + // case 0x33: * case 0x34: // LLD load_linked<u64>(ADDR(m_r[RSREG], s16(op)), [this, op](u64 address, u64 data) @@ -1119,6 +1119,33 @@ void r4000_base_device::cpu_execute(u32 const op) } } +void r4000_base_device::handle_reserved_instruction(u32 const op) +{ + // Unahdnled 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 & 0x3f) + { + case 0x33: // PREF (effective no-op) + break; + default: + switch (op >> 26) + { + case 0x0a: // MOVN + if (m_r[RTREG] == 0) { if (RDREG) m_r[RDREG] = m_r[RSREG]; } + break; + case 0x0b: // MOVZ + if (m_r[RTREG] != 0) { if (RDREG) m_r[RDREG] = m_r[RSREG]; } + break; + } + } +} + void r4000_base_device::cpu_exception(u32 exception, u16 const vector) { if (exception != EXCEPTION_INT) @@ -2601,6 +2628,186 @@ void r4000_base_device::cp1_execute(u32 const op) } } +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>(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; + } +} + template <typename T> void r4000_base_device::cp1_set(unsigned const reg, T const data) { // translate softfloat exception flags to cause register diff --git a/src/devices/cpu/mips/r4000.h b/src/devices/cpu/mips/r4000.h index f349c6c6752..ed4b5efa58b 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 { @@ -326,6 +327,7 @@ protected: 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); @@ -355,6 +357,7 @@ protected: void cp1_unimplemented(); template <typename T> bool cp1_op(T op); 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); // cp2 implementation @@ -470,4 +473,19 @@ public: m_cp0[CP0_Config] |= CONFIG_SC; } }; + +class r5000_device : public r4000_base_device +{ +public: + r5000_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : r4000_base_device(mconfig, R5000, tag, owner, clock, 0x2320, 0x2320, CACHE_32K, CACHE_32K) + { + // no secondary cache + m_cp0[CP0_Config] |= CONFIG_SC; + } + +private: + virtual void handle_reserved_instruction(u32 const op) override; + virtual void cp1x_execute(u32 const op) override; +}; #endif // MAME_CPU_MIPS_R4000_H |