// 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) DECLARE_DEVICE_TYPE(R5000, r5000_device) class r4000_base_device : public cpu_device { public: enum config_mask : u32 { CONFIG_K0 = 0x00000007, // kseg0 cache coherency CONFIG_CU = 0x00000008, // 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]); } void bus_error() { m_bus_error = true; } protected: enum cache_size { 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(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, const XTAL &clock, u32 prid, u32 fcr, cache_size icache_size, cache_size dcache_size, unsigned m32, unsigned m64, unsigned d32, unsigned d64, bool timer_interrupt_disabled); 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) EH_WM = 0xc000'00ff'ffff'e0ff, // write mask }; 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 EL_WM = 0x0000'0000'3fff'fffe, // write mask }; 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 TAGLO_CS = 0x00001c00, // scache status TAGLO_STAG = 0xffffe000, // scache tag }; 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 }; enum scache_mask : u32 { SCACHE_CS = 0x01c00000, // cache state SCACHE_STAG = 0x0007ffff, // physical tag SCACHE_PIDX = 0x00380000, // primary cache index }; // 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 create_disassembler() override; // device_execute_interface overrides virtual u64 execute_clocks_to_cycles(u64 clocks) const noexcept override { return (clocks * 2); } virtual u64 execute_cycles_to_clocks(u64 cycles) const noexcept override { return (cycles + 1) / 2; } virtual u32 execute_min_cycles() const noexcept override { return 1; } virtual u32 execute_max_cycles() const noexcept override { return *std::max_element(std::begin(m_hilo_cycles), std::end(m_hilo_cycles)); } virtual u32 execute_input_lines() const noexcept override { return 6; } virtual void execute_run() override; 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); 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_cache(u32 const op); 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_unimplemented(); template bool cp1_op(T op); virtual void cp1_execute(u32 const op); virtual void cp1x_execute(u32 const op); template void cp1_set(unsigned const reg, T const data); void cp1_mov_s(u32 const op); void cp1_mov_d(u32 const op); // cp2 implementation void cp2_execute(u32 const op); // address and memory handling enum translate_result : unsigned { ERROR, MISS, UNCACHED, CACHED }; translate_result translate(int intention, u64 &address); void address_error(int intention, u64 const address); template void accessors(T &m); template std::enable_if_t>::value, bool> load(u64 program_address, U &&apply); template std::enable_if_t>::value, bool> load_linked(u64 program_address, U &&apply); template std::enable_if_t::value, bool> store(u64 program_address, U data, T mem_mask = ~T(0)); bool fetch(u64 address, std::function &&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); // configuration helpers void configure_scache(); // device configuration state address_space_config m_program_config_le; address_space_config m_program_config_be; // memory access helpers memory_access<36, 3, 0, ENDIANNESS_LITTLE>::cache m_le; memory_access<36, 3, 0, ENDIANNESS_BIG>::cache m_be; std::function read_byte; std::function read_word; std::function read_dword; std::function read_qword; std::function write_byte; std::function write_word; std::function write_dword; std::function write_qword; enum branch_state : u64 { STATE = 0x00000000'00000003, TARGET = 0xffffffff'fffffffc, NONE = 0, BRANCH = 1, // retire delayed branch DELAY = 2, // delay slot instruction NULLIFY = 3, // next instruction nullified }; // runtime state int m_icount; // integer multiple/divide state unsigned const m_hilo_cycles[4]; unsigned m_hilo_delay; // cpu state u64 m_pc; u64 m_r[32]; u64 m_hi; u64 m_lo; u64 m_branch_state; // cp0 state u64 m_cp0[32]; u64 m_cp0_timer_zero; emu_timer *m_cp0_timer; bool m_hard_reset; bool m_ll_active; bool m_bus_error; bool m_timer_interrupt_disabled; struct tlb_entry { 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 m_icache_tag; std::unique_ptr m_icache_data; // experimental scache state u32 m_scache_size; // Size in bytes u8 m_scache_line_size; u32 m_scache_line_index; // Secondary cache line shift u32 m_scache_tag_mask; // Mask for extracting the tag from a physical address u32 m_scache_tag_size; std::unique_ptr m_scache_tag; // 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, const XTAL &clock, bool timer_interrupt_disabled) : r4000_base_device(mconfig, R4000, tag, owner, clock, 0x0430, 0x0500, CACHE_8K, CACHE_8K, 10, 20, 69, 133, timer_interrupt_disabled) { // no secondary cache m_cp0[CP0_Config] |= CONFIG_SC; } r4000_device(const machine_config &mconfig, const char *tag, device_t *owner, const XTAL &clock) : r4000_device(mconfig, tag, owner, clock, false) { } }; class r4400_device : public r4000_base_device { public: r4400_device(const machine_config &mconfig, const char *tag, device_t *owner, const XTAL &clock, bool timer_interrupt_disabled, u32 scache_size, u8 scache_line_size) : r4000_base_device(mconfig, R4400, tag, owner, clock, 0x0440, 0x0500, CACHE_16K, CACHE_16K, 10, 20, 69, 133, timer_interrupt_disabled) { m_scache_size = scache_size; m_scache_line_size = scache_line_size; configure_scache(); } r4400_device(const machine_config &mconfig, const char *tag, device_t *owner, const XTAL &clock) : r4400_device(mconfig, tag, owner, clock, false, 0, 0) { } }; class r4600_device : public r4000_base_device { public: r4600_device(const machine_config &mconfig, const char *tag, device_t *owner, const XTAL &clock, bool timer_interrupt_disabled) : r4000_base_device(mconfig, R4600, tag, owner, clock, 0x2020, 0x2020, CACHE_16K, CACHE_16K, 10, 12, 42, 74, timer_interrupt_disabled) { // no secondary cache m_cp0[CP0_Config] |= CONFIG_SC; } r4600_device(const machine_config &mconfig, const char *tag, device_t *owner, const XTAL &clock) : r4600_device(mconfig, tag, owner, clock, false) { } }; class r5000_device : public r4000_base_device { public: r5000_device(const machine_config &mconfig, const char *tag, device_t *owner, const XTAL &clock, bool timer_interrupt_disabled) : r4000_base_device(mconfig, R5000, tag, owner, clock, 0x2320, 0x2320, CACHE_32K, CACHE_32K, 5, 9, 36, 68, timer_interrupt_disabled) { // no secondary cache m_cp0[CP0_Config] |= CONFIG_SC; } r5000_device(const machine_config &mconfig, const char *tag, device_t *owner, const XTAL &clock) : r5000_device(mconfig, tag, owner, clock, false) { } private: virtual void handle_reserved_instruction(u32 const op) override; virtual void cp1_execute(u32 const op) override; virtual void cp1x_execute(u32 const op) override; static u32 const s_fcc_masks[8]; static u32 const s_fcc_shifts[8]; }; #endif // MAME_CPU_MIPS_R4000_H