summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/mips/r4000.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/mips/r4000.cpp')
-rw-r--r--src/devices/cpu/mips/r4000.cpp68
1 files changed, 54 insertions, 14 deletions
diff --git a/src/devices/cpu/mips/r4000.cpp b/src/devices/cpu/mips/r4000.cpp
index d88210c9d79..059dfb47540 100644
--- a/src/devices/cpu/mips/r4000.cpp
+++ b/src/devices/cpu/mips/r4000.cpp
@@ -90,10 +90,11 @@ DEFINE_DEVICE_TYPE(R5000, r5000_device, "r5000", "MIPS R5000")
u32 const r5000_device::s_fcc_masks[8] = { (1U << 23), (1U << 25), (1U << 26), (1U << 27), (1U << 28), (1U << 29), (1U << 30), (1U << 31) };
u32 const r5000_device::s_fcc_shifts[8] = { 23, 25, 26, 27, 28, 29, 30, 31 };
-r4000_base_device::r4000_base_device(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)
+r4000_base_device::r4000_base_device(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock, u32 prid, u32 fcr, cache_size icache_size, cache_size dcache_size, unsigned m32, unsigned m64, unsigned d32, unsigned d64)
: cpu_device(mconfig, type, tag, owner, clock)
, m_program_config_le("program", ENDIANNESS_LITTLE, 64, 32)
, m_program_config_be("program", ENDIANNESS_BIG, 64, 32)
+ , m_hilo_cycles{ m32, m64, d32, d64 }
, m_r{}
, m_cp0{}
, m_f{}
@@ -193,6 +194,8 @@ void r4000_base_device::device_start()
m_icache_mask_hi = (0x1000U << config_ic) - 1;
m_icache_tag = std::make_unique<u32[]>(0x100U << config_ic);
m_icache_data = std::make_unique<u32 []>((0x1000U << config_ic) >> 2);
+
+ R4000_ENDIAN_LE_BE(accessors(m_le), accessors(m_be));
}
void r4000_base_device::device_reset()
@@ -205,6 +208,7 @@ void r4000_base_device::device_reset()
else
m_cp0[CP0_Status] = SR_BEV | SR_ERL;
+ m_hilo_delay = 0;
m_branch_state = NONE;
m_pc = s64(s32(0xbfc00000));
m_r[0] = 0;
@@ -294,6 +298,9 @@ void r4000_base_device::execute_run()
m_r[0] = 0;
});
+ if (m_hilo_delay)
+ m_hilo_delay--;
+
// update pc and branch state
switch (m_branch_state)
{
@@ -428,12 +435,22 @@ void r4000_base_device::cpu_execute(u32 const op)
break;
case 0x10: // MFHI
m_r[RDREG] = m_hi;
+ if (m_hilo_delay)
+ {
+ m_icount -= m_hilo_delay;
+ m_hilo_delay = 0;
+ }
break;
case 0x11: // MTHI
m_hi = m_r[RSREG];
break;
case 0x12: // MFLO
m_r[RDREG] = m_lo;
+ if (m_hilo_delay)
+ {
+ m_icount -= m_hilo_delay;
+ m_hilo_delay = 0;
+ }
break;
case 0x13: // MTLO
m_lo = m_r[RSREG];
@@ -454,6 +471,7 @@ void r4000_base_device::cpu_execute(u32 const op)
m_lo = s64(s32(product));
m_hi = s64(s32(product >> 32));
+ m_hilo_delay = m_hilo_cycles[0];
}
break;
case 0x19: // MULTU
@@ -462,6 +480,7 @@ void r4000_base_device::cpu_execute(u32 const op)
m_lo = s64(s32(product));
m_hi = s64(s32(product >> 32));
+ m_hilo_delay = m_hilo_cycles[0];
}
break;
case 0x1a: // DIV
@@ -469,6 +488,7 @@ void r4000_base_device::cpu_execute(u32 const op)
{
m_lo = s64(s32(m_r[RSREG]) / s32(m_r[RTREG]));
m_hi = s64(s32(m_r[RSREG]) % s32(m_r[RTREG]));
+ m_hilo_delay = m_hilo_cycles[2];
}
break;
case 0x1b: // DIVU
@@ -476,19 +496,23 @@ void r4000_base_device::cpu_execute(u32 const op)
{
m_lo = s64(s32(u32(m_r[RSREG]) / u32(m_r[RTREG])));
m_hi = s64(s32(u32(m_r[RSREG]) % u32(m_r[RTREG])));
+ m_hilo_delay = m_hilo_cycles[2];
}
break;
case 0x1c: // DMULT
m_lo = mul_64x64(m_r[RSREG], m_r[RTREG], *reinterpret_cast<s64 *>(&m_hi));
+ m_hilo_delay = m_hilo_cycles[1];
break;
case 0x1d: // DMULTU
m_lo = mulu_64x64(m_r[RSREG], m_r[RTREG], m_hi);
+ m_hilo_delay = m_hilo_cycles[1];
break;
case 0x1e: // DDIV
if (m_r[RTREG])
{
m_lo = s64(m_r[RSREG]) / s64(m_r[RTREG]);
m_hi = s64(m_r[RSREG]) % s64(m_r[RTREG]);
+ m_hilo_delay = m_hilo_cycles[3];
}
break;
case 0x1f: // DDIVU
@@ -496,6 +520,7 @@ void r4000_base_device::cpu_execute(u32 const op)
{
m_lo = m_r[RSREG] / m_r[RTREG];
m_hi = m_r[RSREG] % m_r[RTREG];
+ m_hilo_delay = m_hilo_cycles[3];
}
break;
case 0x20: // ADD
@@ -3704,6 +3729,21 @@ void r4000_base_device::address_error(int intention, u64 const address)
}
}
+template <typename T> void r4000_base_device::accessors(T &m)
+{
+ space(AS_PROGRAM).cache(m);
+
+ read_byte = [&m](offs_t offset) { return m.read_byte(offset); };
+ read_word = [&m](offs_t offset) { return m.read_word(offset); };
+ read_dword = [&m](offs_t offset) { return m.read_dword(offset); };
+ read_qword = [&m](offs_t offset) { return m.read_qword(offset); };
+
+ write_byte = [&m](offs_t offset, u8 data) { m.write_byte(offset, data); };
+ write_word = [&m](offs_t offset, u16 data, u16 mem_mask) { m.write_word(offset, data, mem_mask); };
+ write_dword = [&m](offs_t offset, u32 data, u32 mem_mask) { m.write_dword(offset, data, mem_mask); };
+ write_qword = [&m](offs_t offset, u64 data, u64 mem_mask) { m.write_qword(offset, data, mem_mask); };
+}
+
template <typename T, bool Aligned, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, bool> r4000_base_device::load(u64 address, U &&apply)
{
// alignment error
@@ -3748,10 +3788,10 @@ template <typename T, bool Aligned, typename U> std::enable_if_t<std::is_convert
T value = 0;
switch (sizeof(T))
{
- case 1: value = T(space(0).read_byte(address)); break;
- case 2: value = T(space(0).read_word(address)); break;
- case 4: value = T(space(0).read_dword(address)); break;
- case 8: value = T(space(0).read_qword(address)); break;
+ case 1: value = T(read_byte(address)); break;
+ case 2: value = T(read_word(address)); break;
+ case 4: value = T(read_dword(address)); break;
+ case 8: value = T(read_qword(address)); break;
}
if (m_bus_error)
@@ -3805,8 +3845,8 @@ template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::f
switch (sizeof(T))
{
- case 4: apply(address, T(space(0).read_dword(address))); break;
- case 8: apply(address, T(space(0).read_qword(address))); break;
+ case 4: apply(address, T(read_dword(address))); break;
+ case 8: apply(address, T(read_qword(address))); break;
}
return true;
@@ -3854,10 +3894,10 @@ template <typename T, bool Aligned, typename U> std::enable_if_t<std::is_convert
switch (sizeof(T))
{
- case 1: space(0).write_byte(address, T(data)); break;
- case 2: space(0).write_word(address, T(data), mem_mask); break;
- case 4: space(0).write_dword(address, T(data), mem_mask); break;
- case 8: space(0).write_qword(address, T(data), mem_mask); break;
+ case 1: write_byte(address, T(data)); break;
+ case 2: write_word(address, T(data), mem_mask); break;
+ case 4: write_dword(address, T(data), mem_mask); break;
+ case 8: write_qword(address, T(data), mem_mask); break;
}
return true;
@@ -3893,7 +3933,7 @@ bool r4000_base_device::fetch(u64 address, std::function<void(u32)> &&apply)
{
if (t == UNCACHED)
{
- const u32 insn = space(0).read_dword(address);
+ const u32 insn = read_dword(address);
if (m_bus_error)
{
@@ -3922,7 +3962,7 @@ bool r4000_base_device::fetch(u64 address, std::function<void(u32)> &&apply)
tag = ICACHE_V | (address >> 12);
for (unsigned i = 0; i < m_icache_line_size; i += 8)
{
- u64 const data = space(0).read_qword((address & m_icache_mask_lo) | i);
+ u64 const data = read_qword((address & m_icache_mask_lo) | i);
m_icache_data[(((cache_address & m_icache_mask_lo) | i) >> 2) + 0] = u32(data);
m_icache_data[(((cache_address & m_icache_mask_lo) | i) >> 2) + 1] = data >> 32;
@@ -3936,7 +3976,7 @@ bool r4000_base_device::fetch(u64 address, std::function<void(u32)> &&apply)
}
else
{
- const u32 insn = space(0).read_dword(address);
+ const u32 insn = read_dword(address);
if (m_bus_error)
{