summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices
diff options
context:
space:
mode:
author MooglyGuy <therealmogminer@gmail.com>2020-03-04 14:25:55 +0100
committer MooglyGuy <therealmogminer@gmail.com>2020-03-04 14:26:10 +0100
commitc0d388dc09ca069af955ec260b1bdbc7a9bf00a7 (patch)
treeac0a3e02408ae0d09d5f796164f7cf8656d1fd60 /src/devices
parentedc2024fc0e657b6be9c590199afeb748d4b13a3 (diff)
-r4000: Added basic R5000 support. [Ryan Holtz]
Diffstat (limited to 'src/devices')
-rw-r--r--src/devices/cpu/mips/r4000.cpp219
-rw-r--r--src/devices/cpu/mips/r4000.h18
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