mips1: emulate instruction and data caches

This commit is contained in:
Patrick Mackinlay 2023-07-18 16:58:38 +07:00
parent cede97e6ae
commit 4af341e49b
4 changed files with 424 additions and 260 deletions

View File

@ -6,9 +6,10 @@
* IDT devices come in two variations: those with an "E" suffix include a TLB,
* while those without have hard-wired address translation.
*
* TODO
* - R3041 features
* - cache emulation
* TODO:
* - multi-word cache line sizes
* - verify lwl/lwr and swl/swr cache logic
* - R3041 features
*
*/
#include "emu.h"
@ -24,6 +25,167 @@
#include "logmacro.h"
enum registers : unsigned
{
MIPS1_R0 = 0,
MIPS1_COP0 = 32,
MIPS1_F0 = 64,
MIPS1_PC = 80,
MIPS1_HI,
MIPS1_LO,
MIPS1_FCR30,
MIPS1_FCR31,
};
enum exception : u32
{
EXCEPTION_INTERRUPT = 0x00000000,
EXCEPTION_TLBMOD = 0x00000004,
EXCEPTION_TLBLOAD = 0x00000008,
EXCEPTION_TLBSTORE = 0x0000000c,
EXCEPTION_ADDRLOAD = 0x00000010,
EXCEPTION_ADDRSTORE = 0x00000014,
EXCEPTION_BUSINST = 0x00000018,
EXCEPTION_BUSDATA = 0x0000001c,
EXCEPTION_SYSCALL = 0x00000020,
EXCEPTION_BREAK = 0x00000024,
EXCEPTION_INVALIDOP = 0x00000028,
EXCEPTION_BADCOP = 0x0000002c,
EXCEPTION_OVERFLOW = 0x00000030,
EXCEPTION_TRAP = 0x00000034,
EXCEPTION_BADCOP0 = 0x0000002c,
EXCEPTION_BADCOP1 = 0x1000002c,
EXCEPTION_BADCOP2 = 0x2000002c,
EXCEPTION_BADCOP3 = 0x3000002c,
};
enum cop0_reg : u8
{
COP0_Index = 0,
COP0_Random = 1,
COP0_EntryLo = 2,
COP0_BusCtrl = 2, // r3041 only
COP0_Config = 3, // r3041/r3071/r3081 only
COP0_Context = 4,
COP0_BadVAddr = 8,
COP0_Count = 9, // r3041 only
COP0_EntryHi = 10,
COP0_PortSize = 10, // r3041 only
COP0_Compare = 11, // r3041 only
COP0_Status = 12,
COP0_Cause = 13,
COP0_EPC = 14,
COP0_PRId = 15,
};
enum sr_mask : u32
{
SR_IEc = 0x00000001, // interrupt enable (current)
SR_KUc = 0x00000002, // user mode (current)
SR_IEp = 0x00000004, // interrupt enable (previous)
SR_KUp = 0x00000008, // user mode (previous)
SR_IEo = 0x00000010, // interrupt enable (old)
SR_KUo = 0x00000020, // user mode (old)
SR_IMSW0 = 0x00000100, // software interrupt 0 enable
SR_IMSW1 = 0x00000200, // software interrupt 1 enable
SR_IMEX0 = 0x00000400, // external interrupt 0 enable
SR_IMEX1 = 0x00000800, // external interrupt 1 enable
SR_IMEX2 = 0x00001000, // external interrupt 2 enable
SR_IMEX3 = 0x00002000, // external interrupt 3 enable
SR_IMEX4 = 0x00004000, // external interrupt 4 enable
SR_IMEX5 = 0x00008000, // external interrupt 5 enable
SR_IsC = 0x00010000, // isolate (data) cache
SR_SwC = 0x00020000, // swap caches
SR_PZ = 0x00040000, // cache parity zero
SR_CM = 0x00080000, // cache miss
SR_PE = 0x00100000, // cache parity error
SR_TS = 0x00200000, // tlb shutdown
SR_BEV = 0x00400000, // boot exception vectors
SR_RE = 0x02000000, // reverse endianness in user mode
SR_COP0 = 0x10000000, // coprocessor 0 usable
SR_COP1 = 0x20000000, // coprocessor 1 usable
SR_COP2 = 0x40000000, // coprocessor 2 usable
SR_COP3 = 0x80000000, // coprocessor 3 usable
SR_KUIE = 0x0000003f, // all interrupt enable and user mode bits
SR_KUIEpc = 0x0000000f, // previous and current interrupt enable and user mode bits
SR_KUIEop = 0x0000003c, // old and previous interrupt enable and user mode bits
SR_IM = 0x0000ff00, // all interrupt mask bits
};
enum cause_mask : u32
{
CAUSE_EXCCODE = 0x0000007c, // exception code
CAUSE_IPSW0 = 0x00000100, // software interrupt 0 pending
CAUSE_IPSW1 = 0x00000200, // software interrupt 1 pending
CAUSE_IPEX0 = 0x00000400, // external interrupt 0 pending
CAUSE_IPEX1 = 0x00000800, // external interrupt 1 pending
CAUSE_IPEX2 = 0x00001000, // external interrupt 2 pending
CAUSE_IPEX3 = 0x00002000, // external interrupt 3 pending
CAUSE_IPEX4 = 0x00004000, // external interrupt 4 pending
CAUSE_IPEX5 = 0x00008000, // external interrupt 5 pending
CAUSE_IP = 0x0000ff00, // interrupt pending
CAUSE_CE = 0x30000000, // co-processor error
CAUSE_BD = 0x80000000, // branch delay
CAUSE_IPEX = 0x0000fc00, // external interrupt pending
};
enum entryhi_mask : u32
{
EH_VPN = 0xfffff000, // virtual page number
EH_ASID = 0x00000fc0, // address space identifier
EH_WM = 0xffffffc0, // write mask
};
enum entrylo_mask : u32
{
EL_PFN = 0xfffff000, // physical frame
EL_N = 0x00000800, // noncacheable
EL_D = 0x00000400, // dirty
EL_V = 0x00000200, // valid
EL_G = 0x00000100, // global
EL_WM = 0xffffff00, // write mask
};
enum context_mask : u32
{
PTE_BASE = 0xffe00000, // base address of page table
BAD_VPN = 0x001ffffc, // virtual address bits 30..12
};
enum cp1_fcr31_mask : u32
{
FCR31_RM = 0x00000003, // rounding mode
FCR31_FI = 0x00000004, // inexact operation flag
FCR31_FU = 0x00000008, // underflow flag
FCR31_FO = 0x00000010, // overflow flag
FCR31_FZ = 0x00000020, // divide by zero flag
FCR31_FV = 0x00000040, // invalid operation flag
FCR31_EI = 0x00000080, // inexact operation enable
FCR31_EU = 0x00000100, // underflow enable
FCR31_EO = 0x00000200, // overflow enable
FCR31_EZ = 0x00000400, // divide by zero enable
FCR31_EV = 0x00000800, // invalid operation enable
FCR31_CI = 0x00001000, // inexact operation cause
FCR31_CU = 0x00002000, // underflow cause
FCR31_CO = 0x00004000, // overflow cause
FCR31_CZ = 0x00008000, // divide by zero cause
FCR31_CV = 0x00010000, // invalid operation cause
FCR31_CE = 0x00020000, // unimplemented operation cause
FCR31_C = 0x00800000, // condition
FCR31_FM = 0x0000007c, // flag mask
FCR31_EM = 0x00000f80, // enable mask
FCR31_CM = 0x0001f000, // cause mask (except unimplemented)
};
#define RSREG ((op >> 21) & 31)
#define RTREG ((op >> 16) & 31)
#define RDREG ((op >> 11) & 31)
@ -58,13 +220,12 @@ mips1core_device_base::mips1core_device_base(machine_config const &mconfig, devi
: cpu_device(mconfig, type, tag, owner, clock)
, m_program_config_be("program", ENDIANNESS_BIG, 32, 32)
, m_program_config_le("program", ENDIANNESS_LITTLE, 32, 32)
, m_icache_config("icache", ENDIANNESS_BIG, 32, 32)
, m_dcache_config("dcache", ENDIANNESS_BIG, 32, 32)
, m_cpurev(cpurev)
, m_endianness(ENDIANNESS_BIG)
, m_icount(0)
, m_icache_size(icache_size)
, m_dcache_size(dcache_size)
, m_icache(icache_size)
, m_dcache(dcache_size)
, m_cache((icache_size && dcache_size) ? CACHED : UNCACHED)
, m_in_brcond(*this, 0)
{
}
@ -132,17 +293,6 @@ iop_device::iop_device(machine_config const &mconfig, char const *tag, device_t
m_endianness = ENDIANNESS_LITTLE;
}
/*
* Two additional address spaces are defined to represent the instruction and
* data caches. These are only used to simulate cache isolation functionality
* at this point, but could simulate other behaviour as needed in future.
*/
void mips1core_device_base::device_add_mconfig(machine_config &config)
{
set_addrmap(1, &mips1core_device_base::icache_map);
set_addrmap(2, &mips1core_device_base::dcache_map);
}
void mips1core_device_base::device_start()
{
// set our instruction counter
@ -181,6 +331,14 @@ void mips1core_device_base::device_start()
m_cop0[COP0_Cause] = 0;
m_r[0] = 0;
m_icache.start();
m_dcache.start();
save_pointer(STRUCT_MEMBER(m_icache.line, tag), m_icache.lines());
save_pointer(STRUCT_MEMBER(m_icache.line, data), m_icache.lines());
save_pointer(STRUCT_MEMBER(m_dcache.line, tag), m_dcache.lines());
save_pointer(STRUCT_MEMBER(m_dcache.line, data), m_dcache.lines());
}
void r3041_device::device_start()
@ -208,7 +366,6 @@ void mips1core_device_base::device_reset()
// non-tlb devices have tlb shut down
m_cop0[COP0_Status] = SR_BEV | SR_TS;
m_data_spacenum = 0;
m_bus_error = false;
}
@ -632,21 +789,21 @@ void mips1core_device_base::execute_set_input(int irqline, int state)
device_memory_interface::space_config_vector mips1core_device_base::memory_space_config() const
{
return space_config_vector {
std::make_pair(AS_PROGRAM, (m_endianness == ENDIANNESS_BIG) ? &m_program_config_be : &m_program_config_le),
std::make_pair(1, &m_icache_config),
std::make_pair(2, &m_dcache_config)
std::make_pair(AS_PROGRAM, (m_endianness == ENDIANNESS_BIG) ? &m_program_config_be : &m_program_config_le)
};
}
bool mips1core_device_base::memory_translate(int spacenum, int intention, offs_t &address, address_space *&target_space)
{
target_space = &space(spacenum);
if (spacenum != AS_PROGRAM)
return true;
return translate(intention, address, true);
return translate(intention, address, true) != ERROR;
}
bool mips1core_device_base::translate(int intention, offs_t &address, bool debug)
mips1core_device_base::translate_result mips1core_device_base::translate(int intention, offs_t &address, bool debug)
{
// check for kernel memory address
if (BIT(address, 31))
@ -657,9 +814,12 @@ bool mips1core_device_base::translate(int intention, offs_t &address, bool debug
switch (address & 0xe0000000)
{
case 0x80000000: // kseg0: unmapped, cached, privileged
address &= ~0xe0000000;
return m_cache;
case 0xa0000000: // kseg1: unmapped, uncached, privileged
address &= ~0xe0000000;
break;
return UNCACHED;
case 0xc0000000: // kseg2: mapped, cached, privileged
case 0xe0000000:
@ -670,14 +830,14 @@ bool mips1core_device_base::translate(int intention, offs_t &address, bool debug
{
address_error(intention, address);
return false;
return ERROR;
}
}
else
// kuseg physical addresses have a 1GB offset
address += 0x40000000;
return true;
return m_cache;
}
std::unique_ptr<util::disasm_interface> mips1core_device_base::create_disassembler()
@ -685,18 +845,6 @@ std::unique_ptr<util::disasm_interface> mips1core_device_base::create_disassembl
return std::make_unique<mips1_disassembler>();
}
void mips1core_device_base::icache_map(address_map &map)
{
if (m_icache_size)
map(0, m_icache_size - 1).ram().mirror(~(m_icache_size - 1));
}
void mips1core_device_base::dcache_map(address_map &map)
{
if (m_dcache_size)
map(0, m_dcache_size - 1).ram().mirror(~(m_dcache_size - 1));
}
void mips1core_device_base::generate_exception(u32 exception, bool refill)
{
if ((VERBOSE & LOG_RISCOS) && (exception == EXCEPTION_SYSCALL))
@ -954,10 +1102,10 @@ void mips1core_device_base::set_cop0_reg(unsigned const reg, u32 const data)
{
u32 const delta = SR ^ data;
m_cop0[COP0_Status] = data;
if ((delta & SR_IsC) && (m_cache == UNCACHED))
fatalerror("mips1: cannot isolate non-existent cache (%s)\n", machine().describe_context());
// handle cache isolation and swap
m_data_spacenum = (data & SR_IsC) ? ((data & SR_SwC) ? 1 : 2) : 0;
m_cop0[COP0_Status] = data;
if ((delta & SR_KUc) && (m_branch_state != EXCEPTION))
debugger_privilege_hook();
@ -1074,7 +1222,7 @@ void mips1core_device_base::lwr(u32 const op)
offs_t const offset = SIMMVAL + m_r[RSREG];
load<u32, false>(offset, [this, op, offset](u32 temp)
{
unsigned const shift = ((offset & 0x3) ^ (m_endianness == ENDIANNESS_LITTLE ? 0 : 3)) << 3;
unsigned const shift = ((offset & 3) ^ (m_endianness == ENDIANNESS_LITTLE ? 0 : 3)) << 3;
m_r[RTREG] = (m_r[RTREG] & ~u32(0xffffffffU >> shift)) | (temp >> shift);
});
@ -1096,6 +1244,53 @@ void mips1core_device_base::swr(u32 const op)
store<u32, false>(offset, m_r[RTREG] << shift, 0xffffffffU << shift);
}
/*
* This function determines the active cache (instruction or data) depending on
* the icache parameter and the status register SwC (swap caches) flag. A line
* within the cache is then selected based upon the low address bits. The upper
* address bits are compared with the line tag to identify whether the lookup
* is a hit or a miss.
*
* If the cache lookup misses and the invalidate parameter evaluates to true,
* the cache line tag is updated to match the input address and invalidated.
*
* The function returns the selected line and the miss state.
*
* TODO: multiple-word cache lines
*/
std::tuple<struct mips1core_device_base::cache::line &, bool> mips1core_device_base::cache_lookup(u32 address, bool invalidate, bool icache)
{
// cache line data is word-addressed
address &= ~3;
// select instruction or data cache
struct cache const &c = (icache ^ bool(SR & SR_SwC)) ? m_icache : m_dcache;
// select line within cache based on low address bits
struct cache::line &l = c.line[(address & (c.size - 1)) >> 2];
// compare cache line tag against upper address bits and line valid bit
bool const miss = (l.tag ^ address) & (-c.size | cache::line::INV);
// on cache miss, optionally update the line tag and invalidate (cache
// miss is usually followed by line replacement)
if (miss && invalidate)
l.tag = (address & -c.size) | cache::line::INV;
return std::tie(l, miss);
}
// compute bit position of sub-unit within a word given endianness and address
template <typename T> unsigned mips1core_device_base::shift_factor(u32 address) const
{
if constexpr (sizeof(T) == 1)
return ((m_endianness == ENDIANNESS_BIG) ? (address & 3) ^ 3 : (address & 3)) * 8;
else if constexpr (sizeof(T) == 2)
return ((m_endianness == ENDIANNESS_BIG) ? (address & 2) ^ 2 : (address & 2)) * 8;
else
return 0;
}
template <typename T, bool Aligned, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, void> mips1core_device_base::load(u32 address, U &&apply)
{
// alignment error
@ -1105,28 +1300,75 @@ template <typename T, bool Aligned, typename U> std::enable_if_t<std::is_convert
return;
}
if (translate(TR_READ, address, false))
T data;
if (!(SR & SR_IsC))
{
translate_result const t = translate(TR_READ, address, false);
if (t == ERROR)
return;
// align address for ld[lr] instructions
if (!Aligned)
address &= ~(sizeof(T) - 1);
T const data
= (sizeof(T) == 1) ? space(m_data_spacenum).read_byte(address)
: (sizeof(T) == 2) ? space(m_data_spacenum).read_word(address)
: space(m_data_spacenum).read_dword(address);
if (m_bus_error)
if (t == CACHED)
{
m_bus_error = false;
generate_exception(EXCEPTION_BUSDATA);
auto [l, miss] = cache_lookup(address, true);
if (miss)
{
// load
u32 const data = space(AS_PROGRAM).read_dword(address);
if (m_bus_error)
{
m_bus_error = false;
generate_exception(EXCEPTION_BUSDATA);
return;
}
// replace cache line data and mark valid
l.update(data);
}
data = l.data >> shift_factor<T>(address);
}
else
apply(data);
{
if constexpr (sizeof(T) == 4)
data = space(AS_PROGRAM).read_dword(address);
else if constexpr (sizeof(T) == 2)
data = space(AS_PROGRAM).read_word(address);
else if constexpr (sizeof(T) == 1)
data = space(AS_PROGRAM).read_byte(address);
if (m_bus_error)
{
m_bus_error = false;
generate_exception(EXCEPTION_BUSDATA);
return;
}
}
}
else
{
// when isolated, loads always hit the cache and the status register
// CM flag reflects the actual hit/miss state
auto [l, miss] = cache_lookup(address, false);
if (miss)
SR |= SR_CM;
else
SR &= ~SR_CM;
data = l.data >> shift_factor<T>(address);
}
apply(data);
}
template <typename T, bool Aligned, typename U> std::enable_if_t<std::is_convertible<U, T>::value, void> mips1core_device_base::store(u32 address, U data, T mem_mask)
template <typename T, bool Aligned> void mips1core_device_base::store(u32 address, T data, T mem_mask)
{
// alignment error
if (Aligned && (address & (sizeof(T) - 1)))
@ -1135,47 +1377,99 @@ template <typename T, bool Aligned, typename U> std::enable_if_t<std::is_convert
return;
}
if (translate(TR_WRITE, address, false))
if (!(SR & SR_IsC))
{
translate_result const t = translate(TR_WRITE, address, false);
if (t == ERROR)
return;
// align address for sd[lr] instructions
if (!Aligned)
address &= ~(sizeof(T) - 1);
switch (sizeof(T))
if (t == CACHED)
{
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;
auto [l, miss] = cache_lookup(address, sizeof(T) == 4);
if (!miss)
{
unsigned const shift = shift_factor<T>(address);
l.update(u32(data) << shift, u32(mem_mask) << shift);
}
else if constexpr (sizeof(T) == 4)
// only full word stores update the cache after a miss
l.update(data, mem_mask);
}
// uncached or write-through store
if constexpr (sizeof(T) == 4)
space(AS_PROGRAM).write_dword(address, T(data), mem_mask);
else if constexpr (sizeof(T) == 2)
space(AS_PROGRAM).write_word(address, T(data), mem_mask);
else if constexpr (sizeof(T) == 1)
space(AS_PROGRAM).write_byte(address, T(data));
}
else
{
// when isolated, full word stores update the cache, while partial word
// stores invalidate the cache line
auto [l, miss] = cache_lookup(address, true);
if constexpr (sizeof(T) == 4)
l.update(data, mem_mask);
else
l.invalidate();
}
}
bool mips1core_device_base::fetch(u32 address, std::function<void(u32)> &&apply)
void mips1core_device_base::fetch(u32 address, std::function<void(u32)> &&apply)
{
// alignment error
if (address & 3)
{
address_error(TR_FETCH, address);
return false;
}
if (translate(TR_FETCH, address, false))
translate_result const t = translate(TR_FETCH, address, false);
if (t == ERROR)
return;
u32 data;
if (t == CACHED)
{
u32 const data = space(AS_PROGRAM).read_dword(address);
auto [l, miss] = cache_lookup(address, true, true);
if (miss)
{
// fetch
u32 const data = space(AS_PROGRAM).read_dword(address);
if (m_bus_error)
{
m_bus_error = false;
generate_exception(EXCEPTION_BUSINST);
return;
}
// replace cache line data and mark valid
l.update(data);
}
data = l.data;
}
else
{
data = space(AS_PROGRAM).read_dword(address);
if (m_bus_error)
{
m_bus_error = false;
generate_exception(EXCEPTION_BUSINST);
return false;
return;
}
apply(data);
return true;
}
else
return false;
apply(data);
}
std::string mips1core_device_base::debug_string(u32 string_pointer, unsigned const limit)
@ -1949,7 +2243,7 @@ template <typename T> void mips1_device_base::set_cop1_reg(unsigned const reg, T
m_f[reg] = data;
}
bool mips1_device_base::translate(int intention, offs_t &address, bool debug)
mips1core_device_base::translate_result mips1_device_base::translate(int intention, offs_t &address, bool debug)
{
// check for kernel memory address
if (BIT(address, 31))
@ -1960,9 +2254,12 @@ bool mips1_device_base::translate(int intention, offs_t &address, bool debug)
switch (address & 0xe0000000)
{
case 0x80000000: // kseg0: unmapped, cached, privileged
address &= ~0xe0000000;
return m_cache;
case 0xa0000000: // kseg1: unmapped, uncached, privileged
address &= ~0xe0000000;
return true;
return UNCACHED;
case 0xc0000000: // kseg2: mapped, cached, privileged
case 0xe0000000:
@ -1973,7 +2270,7 @@ bool mips1_device_base::translate(int intention, offs_t &address, bool debug)
{
address_error(intention, address);
return false;
return ERROR;
}
}
@ -2018,7 +2315,7 @@ bool mips1_device_base::translate(int intention, offs_t &address, bool debug)
if (i > 0)
std::swap(mru[i - 1], mru[i]);
return true;
return (entry[1] & EL_N) ? UNCACHED : m_cache;
}
if (!machine().side_effects_disabled() && !debug)
@ -2041,5 +2338,5 @@ bool mips1_device_base::translate(int intention, offs_t &address, bool debug)
generate_exception(modify ? EXCEPTION_TLBMOD : (intention == TR_WRITE) ? EXCEPTION_TLBSTORE : EXCEPTION_TLBLOAD, refill);
}
return false;
return ERROR;
}

View File

@ -19,139 +19,7 @@ public:
protected:
mips1core_device_base(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock, u32 cpurev, size_t icache_size, size_t dcache_size);
enum registers : unsigned
{
MIPS1_R0 = 0,
MIPS1_COP0 = 32,
MIPS1_F0 = 64,
MIPS1_PC = 80,
MIPS1_HI,
MIPS1_LO,
MIPS1_FCR30,
MIPS1_FCR31,
};
enum exception : u32
{
EXCEPTION_INTERRUPT = 0x00000000,
EXCEPTION_TLBMOD = 0x00000004,
EXCEPTION_TLBLOAD = 0x00000008,
EXCEPTION_TLBSTORE = 0x0000000c,
EXCEPTION_ADDRLOAD = 0x00000010,
EXCEPTION_ADDRSTORE = 0x00000014,
EXCEPTION_BUSINST = 0x00000018,
EXCEPTION_BUSDATA = 0x0000001c,
EXCEPTION_SYSCALL = 0x00000020,
EXCEPTION_BREAK = 0x00000024,
EXCEPTION_INVALIDOP = 0x00000028,
EXCEPTION_BADCOP = 0x0000002c,
EXCEPTION_OVERFLOW = 0x00000030,
EXCEPTION_TRAP = 0x00000034,
EXCEPTION_BADCOP0 = 0x0000002c,
EXCEPTION_BADCOP1 = 0x1000002c,
EXCEPTION_BADCOP2 = 0x2000002c,
EXCEPTION_BADCOP3 = 0x3000002c,
};
enum cop0_reg : u8
{
COP0_Index = 0,
COP0_Random = 1,
COP0_EntryLo = 2,
COP0_BusCtrl = 2, // r3041 only
COP0_Config = 3, // r3041/r3071/r3081 only
COP0_Context = 4,
COP0_BadVAddr = 8,
COP0_Count = 9, // r3041 only
COP0_EntryHi = 10,
COP0_PortSize = 10, // r3041 only
COP0_Compare = 11, // r3041 only
COP0_Status = 12,
COP0_Cause = 13,
COP0_EPC = 14,
COP0_PRId = 15,
};
enum sr_mask : u32
{
SR_IEc = 0x00000001, // interrupt enable (current)
SR_KUc = 0x00000002, // user mode (current)
SR_IEp = 0x00000004, // interrupt enable (previous)
SR_KUp = 0x00000008, // user mode (previous)
SR_IEo = 0x00000010, // interrupt enable (old)
SR_KUo = 0x00000020, // user mode (old)
SR_IMSW0 = 0x00000100, // software interrupt 0 enable
SR_IMSW1 = 0x00000200, // software interrupt 1 enable
SR_IMEX0 = 0x00000400, // external interrupt 0 enable
SR_IMEX1 = 0x00000800, // external interrupt 1 enable
SR_IMEX2 = 0x00001000, // external interrupt 2 enable
SR_IMEX3 = 0x00002000, // external interrupt 3 enable
SR_IMEX4 = 0x00004000, // external interrupt 4 enable
SR_IMEX5 = 0x00008000, // external interrupt 5 enable
SR_IsC = 0x00010000, // isolate (data) cache
SR_SwC = 0x00020000, // swap caches
SR_PZ = 0x00040000, // cache parity zero
SR_CM = 0x00080000, // cache match
SR_PE = 0x00100000, // cache parity error
SR_TS = 0x00200000, // tlb shutdown
SR_BEV = 0x00400000, // boot exception vectors
SR_RE = 0x02000000, // reverse endianness in user mode
SR_COP0 = 0x10000000, // coprocessor 0 usable
SR_COP1 = 0x20000000, // coprocessor 1 usable
SR_COP2 = 0x40000000, // coprocessor 2 usable
SR_COP3 = 0x80000000, // coprocessor 3 usable
SR_KUIE = 0x0000003f, // all interrupt enable and user mode bits
SR_KUIEpc = 0x0000000f, // previous and current interrupt enable and user mode bits
SR_KUIEop = 0x0000003c, // old and previous interrupt enable and user mode bits
SR_IM = 0x0000ff00, // all interrupt mask bits
};
enum cause_mask : u32
{
CAUSE_EXCCODE = 0x0000007c, // exception code
CAUSE_IPSW0 = 0x00000100, // software interrupt 0 pending
CAUSE_IPSW1 = 0x00000200, // software interrupt 1 pending
CAUSE_IPEX0 = 0x00000400, // external interrupt 0 pending
CAUSE_IPEX1 = 0x00000800, // external interrupt 1 pending
CAUSE_IPEX2 = 0x00001000, // external interrupt 2 pending
CAUSE_IPEX3 = 0x00002000, // external interrupt 3 pending
CAUSE_IPEX4 = 0x00004000, // external interrupt 4 pending
CAUSE_IPEX5 = 0x00008000, // external interrupt 5 pending
CAUSE_IP = 0x0000ff00, // interrupt pending
CAUSE_CE = 0x30000000, // co-processor error
CAUSE_BD = 0x80000000, // branch delay
CAUSE_IPEX = 0x0000fc00, // external interrupt pending
};
enum entryhi_mask : u32
{
EH_VPN = 0xfffff000, // virtual page number
EH_ASID = 0x00000fc0, // address space identifier
EH_WM = 0xffffffc0, // write mask
};
enum entrylo_mask : u32
{
EL_PFN = 0xfffff000, // physical frame
EL_N = 0x00000800, // noncacheable
EL_D = 0x00000400, // dirty
EL_V = 0x00000200, // valid
EL_G = 0x00000100, // global
EL_WM = 0xffffff00, // write mask
};
enum context_mask : u32
{
PTE_BASE = 0xffe00000, // base address of page table
BAD_VPN = 0x001ffffc, // virtual address bits 30..12
};
// device_t implementation
virtual void device_add_mconfig(machine_config &config) override;
virtual void device_start() override;
virtual void device_reset() override;
@ -169,11 +37,6 @@ protected:
// device_disasm_interface implementation
virtual std::unique_ptr<util::disasm_interface> create_disassembler() override;
virtual bool translate(int intention, offs_t &address, bool debug);
void icache_map(address_map &map);
void dcache_map(address_map &map);
// exceptions
void generate_exception(u32 exception, bool refill = false);
void address_error(int intention, u32 const address);
@ -194,10 +57,47 @@ protected:
void swl(u32 const op);
void swr(u32 const op);
// memory accessors
// memory
enum translate_result : unsigned { ERROR, UNCACHED, CACHED };
virtual translate_result translate(int intention, offs_t &address, bool debug);
template <typename T, bool Aligned = true, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, void> load(u32 address, U &&apply);
template <typename T, bool Aligned = true, typename U> std::enable_if_t<std::is_convertible<U, T>::value, void> store(u32 address, U data, T mem_mask = ~T(0));
bool fetch(u32 address, std::function<void(u32)> &&apply);
template <typename T, bool Aligned = true> void store(u32 address, T data, T mem_mask = ~T(0));
void fetch(u32 address, std::function<void(u32)> &&apply);
// cache
template <typename T> unsigned shift_factor(u32 address) const;
struct cache
{
cache(size_t size)
: size(size)
{
}
struct line
{
enum tag_mask : u32
{
INV = 0x0000'0001, // cache line invalid
};
void invalidate() { tag = INV; }
void update(u32 data, u32 mask = 0xffff'ffffU)
{
this->tag &= ~INV;
this->data = (this->data & ~mask) | (data & mask);
}
u32 tag;
u32 data;
};
size_t lines() const { return size / 4; }
void start() { line = std::make_unique<struct line[]>(lines()); }
size_t const size;
std::unique_ptr<struct line[]> line;
};
std::tuple<struct cache::line &, bool> cache_lookup(u32 address, bool invalidate, bool icache = false);
// debug helpers
std::string debug_string(u32 string_pointer, unsigned const limit = 0);
@ -206,10 +106,6 @@ protected:
// address spaces
address_space_config const m_program_config_be;
address_space_config const m_program_config_le;
address_space_config const m_icache_config;
address_space_config const m_dcache_config;
int m_data_spacenum;
// configuration
u32 const m_cpurev;
@ -237,8 +133,9 @@ protected:
u32 m_branch_target;
// cache memory
size_t const m_icache_size;
size_t const m_dcache_size;
struct cache m_icache;
struct cache m_dcache;
translate_result const m_cache;
// I/O
devcb_read_line::array<4> m_in_brcond;
@ -265,41 +162,11 @@ public:
protected:
mips1_device_base(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock, u32 cpurev, size_t icache_size, size_t dcache_size);
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 implementation
virtual void device_start() override;
virtual void device_reset() override;
virtual bool translate(int intention, offs_t &address, bool debug) override;
virtual translate_result translate(int intention, offs_t &address, bool debug) override;
virtual void handle_cop0(u32 const op) override;
virtual u32 get_cop0_reg(unsigned const reg) override;

View File

@ -38,11 +38,11 @@ public:
protected:
seeq8003_device(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock = 0);
// device_t overrides
// device_t implementation
virtual void device_start() override;
virtual void device_reset() override;
// device_network_interface overrides
// device_network_interface implementation
virtual int recv_start_cb(u8 *buf, int length) override;
// register read handlers

View File

@ -180,20 +180,20 @@
* Keyboard controller output port
* 4: select 1M/4M SIMMs?
*
* rs3230 -window -nomax -ui_active -tty1 terminal -numscreens 2
* PON failures
* kseg0/kseg1 cache
* instruction cache functionality (skipped)
* instruction cache functionality (failed)
* instruction cache mats+ (skipped)
* data cache block refill
* data cache block refill (failed)
* instruction cache block refill (skipped)
* scc - requires z80scc zero count interrupt
* id prom (failed)
* tod - loop <1 second real time?
* color frame buffer (skipped)
* dma controller chip
* scsi controller chip
* tlb (skipped) - all pass except tlb_n (requires cpu data cache)
* exception (skipped)
* parity
* parity (failed)
* dma parity (skipped)
* at serial board (skipped)
*/