diff options
Diffstat (limited to 'src/devices/cpu/mips/r4000.h')
-rw-r--r-- | src/devices/cpu/mips/r4000.h | 463 |
1 files changed, 463 insertions, 0 deletions
diff --git a/src/devices/cpu/mips/r4000.h b/src/devices/cpu/mips/r4000.h new file mode 100644 index 00000000000..8c3a1760ba3 --- /dev/null +++ b/src/devices/cpu/mips/r4000.h @@ -0,0 +1,463 @@ +// 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_FCR30, + 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); + bool cp0_64() const; + + // 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]; + + unsigned low_bit; + } + m_tlb[48]; + unsigned m_tlb_mru[3][48]; + + // cp1 state + u64 m_f[32]; // floating point registers + u32 m_fcr0; // implementation and revision register + u32 m_fcr30; // unknown + 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; + + // statistics + u64 m_tlb_scans; + u64 m_tlb_loops; + u64 m_icache_hits; + u64 m_icache_misses; +}; + +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 |