r4000: experimental mips3 implementation (nw)

WIP checkpoint: while I believe it's largely accurate (and very slow), neither jazz nor sgi systems can fully boot yet using this device, so it remains experimental.

This implementation should go away when it has helped identify the improvements required for mips3.
This commit is contained in:
Patrick Mackinlay 2019-02-01 15:27:13 +07:00
parent b952577370
commit ba8d7b1c8f
4 changed files with 3537 additions and 0 deletions

View File

@ -1297,6 +1297,7 @@ end
--------------------------------------------------
-- MIPS R4000 (MIPS III/IV) series
--@src/devices/cpu/mips/mips3.h,CPUS["MIPS3"] = true
--@src/devices/cpu/mips/r4000.h,CPUS["MIPS3"] = true
--------------------------------------------------
if (CPUS["MIPS3"]~=null) then
@ -1313,6 +1314,8 @@ if (CPUS["MIPS3"]~=null) then
MAME_DIR .. "src/devices/cpu/mips/ps2vu.h",
MAME_DIR .. "src/devices/cpu/mips/ps2vif1.cpp",
MAME_DIR .. "src/devices/cpu/mips/ps2vif1.h",
MAME_DIR .. "src/devices/cpu/mips/r4000.cpp",
MAME_DIR .. "src/devices/cpu/mips/r4000.h",
}
end

View File

@ -256,6 +256,7 @@ end
"utils",
ext_lib("expat"),
"softfloat",
"softfloat3",
ext_lib("jpeg"),
"7z",
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,460 @@
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay
#ifndef MAME_CPU_MIPS_R4000_H
#define MAME_CPU_MIPS_R4000_H
#pragma once
DECLARE_DEVICE_TYPE(R4000, r4000_device)
DECLARE_DEVICE_TYPE(R4400, r4400_device)
DECLARE_DEVICE_TYPE(R4600, r4600_device)
class r4000_base_device : public cpu_device
{
public:
enum config_mask : u32
{
CONFIG_K0 = 0x00000007, // kseg0 cache coherency
CONFIG_CU = 0x00000080, // store conditional cache coherent
CONFIG_DB = 0x00000010, // primary d-cache line 32 bytes
CONFIG_IB = 0x00000020, // primary i-cache line 32 bytes
CONFIG_DC = 0x000001c0, // primary d-cache size
CONFIG_IC = 0x00000e00, // primary i-cache size
CONFIG_EB = 0x00002000, // sub-block ordering
CONFIG_EM = 0x00004000, // parity mode enable
CONFIG_BE = 0x00008000, // big endian
CONFIG_SM = 0x00010000, // dirty shared disable
CONFIG_SC = 0x00020000, // secondary cache absent
CONFIG_EW = 0x000c0000, // system port width
CONFIG_SW = 0x00100000, // secondary cache port width
CONFIG_SS = 0x00200000, // split secondary cache mode
CONFIG_SB = 0x00c00000, // secondary cache line size
CONFIG_EP = 0x0f000000, // transmit data pattern
CONFIG_EC = 0x70000000, // system clock ratio
CONFIG_CM = 0x80000000, // master/checker enable
CONFIG_WM = 0x0000003f, // runtime-writable bits
};
void set_config(u32 data, u32 mem_mask = ~u32(0))
{
if (!configured())
COMBINE_DATA(&m_cp0[CP0_Config]);
}
protected:
enum cache_size_t
{
CACHE_4K = 0,
CACHE_8K = 1,
CACHE_16K = 2,
CACHE_32K = 3,
CACHE_64K = 4,
CACHE_128K = 5,
CACHE_256K = 6,
CACHE_512K = 7,
};
r4000_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u32 prid, cache_size_t icache_size, cache_size_t dcache_size);
enum cp0_reg : int
{
CP0_Index = 0,
CP0_Random = 1,
CP0_EntryLo0 = 2,
CP0_EntryLo1 = 3,
CP0_Context = 4,
CP0_PageMask = 5,
CP0_Wired = 6,
CP0_BadVAddr = 8,
CP0_Count = 9,
CP0_EntryHi = 10,
CP0_Compare = 11,
CP0_Status = 12,
CP0_Cause = 13,
CP0_EPC = 14,
CP0_PRId = 15,
CP0_Config = 16,
CP0_LLAddr = 17,
CP0_WatchLo = 18,
CP0_WatchHi = 19,
CP0_XContext = 20,
CP0_ECC = 26,
CP0_CacheErr = 27,
CP0_TagLo = 28,
CP0_TagHi = 29,
CP0_ErrorEPC = 30,
};
enum cp0_sr_mask : u32
{
SR_IE = 0x00000001, // interrupt enable
SR_EXL = 0x00000002, // exception level
SR_ERL = 0x00000004, // error level
SR_KSU = 0x00000018, // kernel/supervisor/user mode
SR_UX = 0x00000020, // 64-bit addressing user mode
SR_SX = 0x00000040, // 64-bit addressing supervisor mode
SR_KX = 0x00000080, // 64-bit addressing kernel mode
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_DE = 0x00010000, // disable cache parity/ecc exceptions
SR_CE = 0x00020000, // cache ecc check enable
SR_CH = 0x00040000, // cache hit
SR_SR = 0x00100000, // soft reset
SR_TS = 0x00200000, // tlb shutdown (read only)
SR_BEV = 0x00400000, // bootstrap exception vectors
SR_RE = 0x02000000, // reverse endian
SR_FR = 0x04000000, // enable additional floating-point registers
SU_RP = 0x08000000, // reduced power
SR_CU0 = 0x10000000, // coprocessor usable 0
SR_CU1 = 0x20000000, // coprocessor usable 1
SR_CU2 = 0x40000000, // coprocessor usable 2
SR_CU3 = 0x80000000, // coprocessor usable 3
SR_IMSW = 0x00000300,
SR_IM = 0x0000ff00, // interrupt mask
SR_DS = 0x01ff0000, // diagnostic status
};
enum cp0_sr_ksu_mode : u32
{
SR_KSU_K = 0x00000000, // kernel mode
SR_KSU_S = 0x00000008, // supervisor mode
SR_KSU_U = 0x00000010, // user mode
};
enum cp0_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_CE = 0x30000000, // coprocessor unit
CAUSE_BD = 0x80000000, // branch delay slot
CAUSE_IPSW = 0x00000300,
CAUSE_IPHW = 0x0000fc00,
CAUSE_IP = 0x0000ff00,
};
enum cp0_watchlo_mask : u32
{
WATCHLO_W = 0x00000001, // trap on store
WATCHLO_R = 0x00000002, // trap on load
WATCHLO_PADDR0 = 0xfffffff8, // physical address bits 31:3
};
enum cp0_watchhi_mask : u32
{
WATCHHI_PADDR1 = 0x0000000f, // physical address bits 35:32
};
enum cp0_tlb_mask : u64
{
TLB_MASK = 0x0000'0000'01ff'e000,
};
enum cp0_tlb_eh : u64
{
EH_ASID = 0x0000'0000'0000'00ff, // address space id
EH_G = 0x0000'0000'0000'1000, // global (tlb only)
EH_VPN2_32 = 0x0000'0000'ffff'e000, // virtual page number (32-bit mode)
EH_VPN2_64 = 0x0000'00ff'ffff'e000, // virtual page number (64-bit mode)
EH_R = 0xc000'0000'0000'0000, // region (64-bit mode)
};
enum cp0_tlb_el : u64
{
EL_G = 0x0000'0000'0000'0001, // global (entrylo only)
EL_V = 0x0000'0000'0000'0002, // valid
EL_D = 0x0000'0000'0000'0004, // dirty
EL_C = 0x0000'0000'0000'0038, // coherency
EL_PFN = 0x0000'0000'3fff'ffc0, // page frame number
};
enum cp0_tlb_el_c : u64
{
C_0 = 0x00, // reserved
C_1 = 0x08, // reserved
C_2 = 0x10, // uncached
C_3 = 0x18, // cacheable noncoherent (noncoherent)
C_4 = 0x20, // cacheable coherent exclusive (exclusive)
C_5 = 0x28, // cacheable coherent exclusive on write (sharable)
C_6 = 0x30, // cacheable coherent update on write (update)
C_7 = 0x38, // reserved
};
enum cp0_context_mask : u64
{
CONTEXT_PTEBASE = 0xffff'ffff'ff80'0000,
CONTEXT_BADVPN2 = 0x0000'0000'007f'fff0,
};
enum cp0_xcontext_mask : u64
{
XCONTEXT_PTEBASE = 0xffff'fffe'0000'0000, // page table entry base
XCONTEXT_R = 0x0000'0001'8000'0000, // region
XCONTEXT_BADVPN2 = 0x0000'0000'7fff'fff0, // bad virtual page number / 2
};
enum cp0_pagemask_mask : u32
{
PAGEMASK = 0x01ff'e000,
};
enum exception_mask : u32
{
EXCEPTION_INT = 0x00000000, // interrupt
EXCEPTION_MOD = 0x00000004, // tlb modification
EXCEPTION_TLBL = 0x00000008, // tlb load
EXCEPTION_TLBS = 0x0000000c, // tlb store
EXCEPTION_ADEL = 0x00000010, // address error load
EXCEPTION_ADES = 0x00000014, // address error store
EXCEPTION_IBE = 0x00000018, // bus error (instruction fetch)
EXCEPTION_DBE = 0x0000001c, // bus error (data reference: load or store)
EXCEPTION_SYS = 0x00000020, // syscall
EXCEPTION_BP = 0x00000024, // breakpoint
EXCEPTION_RI = 0x00000028, // reserved instruction
EXCEPTION_CPU = 0x0000002c, // coprocessor unusable
EXCEPTION_OV = 0x00000030, // arithmetic overflow
EXCEPTION_TR = 0x00000034, // trap
EXCEPTION_VCEI = 0x00000038, // virtual coherency exception instruction
EXCEPTION_FPE = 0x0000003c, // floating point
EXCEPTION_WATCH = 0x0000005c, // reference to watchhi/watchlo address
EXCEPTION_VCED = 0x0000007c, // virtual coherency exception data
EXCEPTION_CP0 = 0x0000002c, // coprocessor 0 unusable
EXCEPTION_CP1 = 0x1000002c, // coprocessor 1 unusable
EXCEPTION_CP2 = 0x2000002c, // coprocessor 2 unusable
EXCEPTION_CP3 = 0x3000002c, // coprocessor 3 unusable
};
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_FS = 0x01000000, // flush denormalized results
FCR31_FM = 0x0000007c, // flag mask
FCR31_EM = 0x00000f80, // enable mask
FCR31_CM = 0x0001f000, // cause mask (except unimplemented)
};
enum mips3_registers : unsigned
{
MIPS3_R0 = 0,
MIPS3_CP0 = 32,
MIPS3_F0 = 64,
MIPS3_PC = 96,
MIPS3_HI,
MIPS3_LO,
MIPS3_FCR31,
};
enum cp0_taglo_mask : u32
{
TAGLO_PTAGLO = 0xffffff00, // physical adddress bits 35:12
TAGLO_PSTATE = 0x000000c0, // primary cache state
TAGLO_P = 0x00000001, // primary tag even parity
};
enum icache_mask : u32
{
ICACHE_PTAG = 0x00ffffff, // physical tag
ICACHE_V = 0x01000000, // valid
ICACHE_P = 0x02000000, // even parity
};
enum dcache_mask : u32
{
DCACHE_PTAG = 0x00ffffff, // physical tag
DCACHE_CS = 0x01000000, // primary cache state
DCACHE_P = 0x02000000, // even parity for ptag and cs
DCACHE_W = 0x02000000, // write-back
DCACHE_WP = 0x02000000, // even parity for write-back
};
// device_t overrides
virtual void device_start() override;
virtual void device_reset() override;
virtual void device_stop() override;
// device_memory_interface overrides
virtual space_config_vector memory_space_config() const override;
virtual bool memory_translate(int spacenum, int intention, offs_t &address) override;
// device_disasm_interface overrides
virtual std::unique_ptr<util::disasm_interface> create_disassembler() override;
// device_execute_interface overrides
virtual u32 execute_min_cycles() const override { return 1; }
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;
// cpu implementation
void cpu_execute(u32 const op);
void cpu_exception(u32 exception, u16 const vector = 0x180);
void cpu_lwl(u32 const op);
void cpu_lwr(u32 const op);
void cpu_ldl(u32 const op);
void cpu_ldr(u32 const op);
void cpu_swl(u32 const op);
void cpu_swr(u32 const op);
void cpu_sdl(u32 const op);
void cpu_sdr(u32 const op);
// cp0 implementation
void cp0_execute(u32 const op);
u64 cp0_get(unsigned const reg);
void cp0_set(unsigned const reg, u64 const data);
void cp0_tlbr();
void cp0_tlbwi(u8 const index);
void cp0_tlbwr();
void cp0_tlbp();
// cp0 helpers
TIMER_CALLBACK_MEMBER(cp0_timer_callback);
void cp0_update_timer(bool start = false);
void cp0_mode_check();
// cp1 implementation
void cp1_execute(u32 const op);
void cp1_set(unsigned const reg, u64 const data);
// cp2 implementation
void cp2_execute(u32 const op);
// address and memory handling
enum translate_t { ERROR, MISS, UNCACHED, CACHED };
translate_t translate(int intention, u64 &address);
void address_error(int intention, u64 const address);
template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, bool> load(u64 program_address, U &&apply);
template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(u64, T)>>::value, bool> load_linked(u64 program_address, U &&apply);
template <typename T, typename U> std::enable_if_t<std::is_convertible<U, T>::value, bool> store(u64 program_address, U data, T mem_mask = ~T(0));
bool fetch(u64 address, std::function<void(u32)> &&apply);
// debugging helpers
std::string debug_string(u64 string_pointer, unsigned limit = 0);
std::string debug_string_array(u64 array_pointer);
std::string debug_unicode_string(u64 unicode_string_pointer);
// device configuration state
address_space_config m_program_config_le;
address_space_config m_program_config_be;
// runtime state
int m_icount;
// cpu state
u64 m_pc;
u64 m_r[32];
u64 m_hi;
u64 m_lo;
enum branch_state_t : unsigned
{
NONE = 0,
DELAY = 1, // delay slot instruction active
BRANCH = 2, // branch instruction active
EXCEPTION = 3, // exception triggered
NULLIFY = 4, // next instruction nullified
}
m_branch_state;
u64 m_branch_target;
// cp0 state
u64 m_cp0[32];
u64 m_cp0_timer_zero;
emu_timer *m_cp0_timer;
memory_passthrough_handler *m_ll_watch;
struct tlb_entry_t
{
u64 mask;
u64 vpn;
u64 pfn[2];
u8 low_bit;
}
m_tlb[48];
unsigned m_last[3];
bool m_64;
// cp1 state
u64 m_f[32]; // floating point registers
u32 m_fcr0; // implementation and revision register
u32 m_fcr31; // control/status register
// experimental icache state
u32 m_icache_mask_hi;
u32 m_icache_mask_lo;
unsigned m_icache_line_size;
unsigned m_icache_shift;
std::unique_ptr<u32[]> m_icache_tag;
std::unique_ptr<u32[]> m_icache_data;
// experimental icache statistics
u64 m_icache_hit;
u64 m_icache_miss;
};
class r4000_device : public r4000_base_device
{
public:
// NOTE: R4000 chips prior to 3.0 have an xtlb bug
r4000_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: r4000_base_device(mconfig, R4000, tag, owner, clock, 0x0430, CACHE_8K, CACHE_8K)
{
// no secondary cache
m_cp0[CP0_Config] |= CONFIG_SC;
}
};
class r4400_device : public r4000_base_device
{
public:
r4400_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: r4000_base_device(mconfig, R4400, tag, owner, clock, 0x0440, CACHE_16K, CACHE_16K)
{
// no secondary cache
m_cp0[CP0_Config] |= CONFIG_SC;
}
};
class r4600_device : public r4000_base_device
{
public:
r4600_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: r4000_base_device(mconfig, R4600, tag, owner, clock, 0x2000, CACHE_16K, CACHE_16K)
{
// no secondary cache
m_cp0[CP0_Config] |= CONFIG_SC;
}
};
#endif // MAME_CPU_MIPS_R4000_H