mips1: fpu emulation

Code refactoring makes the changes hard to isolate, but the main improvements are:
* implemented fpu instructions and exceptions
* corrected swl/swr implementation
* tlb mru lookup optimization
* interrupt and privilege debugger breakpoints
This commit is contained in:
Patrick Mackinlay 2019-02-26 18:54:04 +07:00
parent 330c498e5d
commit e02a5cf1f5
4 changed files with 1444 additions and 817 deletions

File diff suppressed because it is too large Load Diff

View File

@ -9,21 +9,8 @@
class mips1core_device_base : public cpu_device
{
public:
// floating point coprocessor revision numbers recognised by RISC/os 4.52 and IRIX
enum fpu_rev_t : u32
{
MIPS_R2360 = 0x0100, // MIPS R2360 Floating Point Board
MIPS_R2010 = 0x0200, // MIPS R2010 VLSI Floating Point Chip
MIPS_R2010A = 0x0310, // MIPS R2010A VLSI Floating Point Chip
MIPS_R3010 = 0x0320, // MIPS R3010 VLSI Floating Point Chip
MIPS_R3010A = 0x0330, // MIPS R3010A VLSI Floating Point Chip
MIPS_R3010Av4 = 0x0340, // MIPS R3010A VLSI Floating Point Chip
MIPS_R6010 = 0x0400, // MIPS R6010 Floating Point Chip
};
// device configuration
void set_endianness(endianness_t endianness) { m_endianness = endianness; }
void set_fpurev(u32 revision) { m_hasfpu = true; m_fpurev = revision; }
// input lines
template <unsigned Coprocessor> auto in_brcond() { return m_in_brcond[Coprocessor].bind(); }
@ -31,35 +18,17 @@ public:
protected:
mips1core_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u32 cpurev, size_t icache_size, size_t dcache_size);
enum registers
enum registers : unsigned
{
// general purpose cpu registers
MIPS1_R0, MIPS1_R1, MIPS1_R2, MIPS1_R3, MIPS1_R4, MIPS1_R5, MIPS1_R6, MIPS1_R7,
MIPS1_R8, MIPS1_R9, MIPS1_R10, MIPS1_R11, MIPS1_R12, MIPS1_R13, MIPS1_R14, MIPS1_R15,
MIPS1_R16, MIPS1_R17, MIPS1_R18, MIPS1_R19, MIPS1_R20, MIPS1_R21, MIPS1_R22, MIPS1_R23,
MIPS1_R24, MIPS1_R25, MIPS1_R26, MIPS1_R27, MIPS1_R28, MIPS1_R29, MIPS1_R30, MIPS1_R31,
MIPS1_R0 = 0,
MIPS1_COP0 = 32,
MIPS1_F0 = 64,
// other cpu registers
MIPS1_PC = 80,
MIPS1_HI,
MIPS1_LO,
MIPS1_PC,
// coprocessor 0 registers
MIPS1_COP0_INDEX, // reg 0, tlb only
MIPS1_COP0_RANDOM, // reg 1, tlb only
MIPS1_COP0_ENTRYLO, // reg 2, tlb only
MIPS1_COP0_BUSCTRL, // reg 2, r3041 only
MIPS1_COP0_CONFIG, // reg 3, r3041/r3071/r3081 only
MIPS1_COP0_CONTEXT, // reg 4, tlb only
MIPS1_COP0_BADVADDR, // reg 8
MIPS1_COP0_COUNT, // reg 9, r3041 only
MIPS1_COP0_ENTRYHI, // reg 10, tlb only
MIPS1_COP0_PORTSIZE, // reg 10, r3041 only
MIPS1_COP0_COMPARE, // reg 11, r3041 only
MIPS1_COP0_SR, // reg 12
MIPS1_COP0_CAUSE, // reg 13
MIPS1_COP0_EPC, // reg 14
MIPS1_COP0_PRID, // reg 15
MIPS1_FCR30,
MIPS1_FCR31,
};
enum exception : u32
@ -75,12 +44,14 @@ protected:
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,
EXCEPTION_OVERFLOW = 0x00000030,
EXCEPTION_TRAP = 0x00000034,
};
enum cop0_reg : u8
@ -184,7 +155,7 @@ protected:
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 { set_irq_line(inputnum, state); }
virtual void execute_set_input(int inputnum, int state) override;
// device_memory_interface overrides
virtual space_config_vector memory_space_config() const override;
@ -199,23 +170,16 @@ protected:
// interrupts
void generate_exception(u32 exception, bool refill = false);
void check_irqs();
void set_irq_line(int irqline, int state);
// cop0
virtual u32 get_cop0_reg(int const index);
void set_cop0_reg(int const index, u32 const data);
virtual void handle_cop0(u32 const op);
virtual u32 get_cop0_reg(unsigned const reg);
virtual void set_cop0_reg(unsigned const reg, u32 const data);
// cop1
void set_cop1_creg(int const index, u32 const data);
void handle_cop1(u32 const op);
// generic coprocessor implementation
template <unsigned Coprocessor> void handle_cop(u32 const op);
template <unsigned Coprocessor> u32 get_cop_reg(int const index) { return m_cpr[Coprocessor][index]; }
template <unsigned Coprocessor> void set_cop_reg(int const index, u32 const data) { m_cpr[Coprocessor][index] = data; }
template <unsigned Coprocessor> u32 get_cop_creg(int const index) { return m_ccr[Coprocessor][index]; }
template <unsigned Coprocessor> void set_cop_creg(int const index, u32 const data) { m_ccr[Coprocessor][index] = data; }
// other coprocessors
virtual void handle_cop1(u32 const op);
virtual void handle_cop2(u32 const op);
virtual void handle_cop3(u32 const op);
// load/store left/right opcodes
void lwl(u32 const op);
@ -224,12 +188,12 @@ protected:
void swr(u32 const op);
// memory accessors
template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, void> load(u32 program_address, U &&apply);
template <typename T, typename U> std::enable_if_t<std::is_convertible<U, T>::value, void> store(u32 program_address, U data);
bool fetch(u32 program_address, std::function<void(u32)> &&apply);
template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, void> load(u32 address, U &&apply);
template <typename T, 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);
// debug helpers
std::string debug_string(u32 string_pointer, int const limit = 0);
std::string debug_string(u32 string_pointer, unsigned const limit = 0);
std::string debug_string_array(u32 array_pointer);
// address spaces
@ -242,19 +206,16 @@ protected:
// configuration
u32 m_cpurev;
bool m_hasfpu;
u32 m_fpurev;
endianness_t m_endianness;
// core registers
u32 m_pc;
u32 m_r[32];
u32 m_hi;
u32 m_lo;
u32 m_r[32];
// COP registers
u32 m_cpr[4][32];
u32 m_ccr[4][32];
// cop0 registers
u32 m_cop0[32];
// internal stuff
int m_icount;
@ -278,9 +239,54 @@ protected:
class mips1_device_base : public mips1core_device_base
{
public:
// floating point coprocessor revision numbers recognised by RISC/os 4.52 and IRIX
enum fpu_rev_t : u32
{
MIPS_R2360 = 0x0100, // MIPS R2360 Floating Point Board
MIPS_R2010 = 0x0200, // MIPS R2010 VLSI Floating Point Chip
MIPS_R2010A = 0x0310, // MIPS R2010A VLSI Floating Point Chip
MIPS_R3010 = 0x0320, // MIPS R3010 VLSI Floating Point Chip
MIPS_R3010A = 0x0330, // MIPS R3010A VLSI Floating Point Chip
MIPS_R3010Av4 = 0x0340, // MIPS R3010A VLSI Floating Point Chip
MIPS_R6010 = 0x0400, // MIPS R6010 Floating Point Chip
};
void set_fpu(u32 revision, unsigned interrupt = 3) { m_fcr0 = revision; m_fpu_irq = interrupt; }
protected:
mips1_device_base(const machine_config &mconfig, device_type type, const char *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 overrides
virtual void device_start() override;
virtual void device_reset() override;
@ -288,12 +294,24 @@ protected:
// device_memory_interface overrides
virtual bool memory_translate(int spacenum, int intention, offs_t &address) override;
virtual u32 get_cop0_reg(int idx) override;
virtual void handle_cop0(u32 const op) override;
virtual u32 get_cop0_reg(unsigned const reg) override;
virtual void handle_cop1(u32 const op) override;
virtual void set_cop1_reg(unsigned const reg, u64 const data);
private:
u64 m_reset_time;
u32 m_tlb[64][2]; // 0 is hi, 1 is lo
unsigned m_tlb_mru[3][64];
// cop1 registers
u64 m_f[16];
u32 m_fcr0;
u32 m_fcr30;
u32 m_fcr31;
unsigned m_fpu_irq;
};
class r2000_device : public mips1_device_base
@ -347,13 +365,13 @@ public:
r3052e_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
};
class r3071_device : public mips1core_device_base
class r3071_device : public mips1_device_base
{
public:
r3071_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, size_t icache_size = 16384, size_t dcache_size = 4096);
};
class r3081_device : public mips1core_device_base
class r3081_device : public mips1_device_base
{
public:
r3081_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, size_t icache_size = 16384, size_t dcache_size = 4096);

View File

@ -133,7 +133,7 @@ private:
uint32_t kn01_screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
required_device<mips1core_device_base> m_maincpu;
required_device<mips1_device_base> m_maincpu;
required_device<screen_device> m_screen;
optional_device<timer_device> m_scantimer;
optional_device<decsfb_device> m_sfb;
@ -546,7 +546,7 @@ static void dec_scsi_devices(device_slot_interface &device)
MACHINE_CONFIG_START(decstation_state::kn01)
R2000(config, m_maincpu, 16.67_MHz_XTAL, 65536, 131072);
m_maincpu->set_endianness(ENDIANNESS_LITTLE);
m_maincpu->set_fpurev(0x340);
m_maincpu->set_fpu(mips1_device_base::MIPS_R3010Av4);
m_maincpu->in_brcond<0>().set(FUNC(decstation_state::brcond0_r));
m_maincpu->set_addrmap(AS_PROGRAM, &decstation_state::kn01_map);
@ -570,7 +570,7 @@ MACHINE_CONFIG_END
MACHINE_CONFIG_START(decstation_state::kn02ba)
R3000A(config, m_maincpu, 33.333_MHz_XTAL, 65536, 131072);
m_maincpu->set_endianness(ENDIANNESS_LITTLE);
m_maincpu->set_fpurev(0x340); // should be R3010A v4.0
m_maincpu->set_fpu(mips1_device_base::MIPS_R3010Av4);
m_maincpu->in_brcond<0>().set(FUNC(decstation_state::brcond0_r));
m_maincpu->set_addrmap(AS_PROGRAM, &decstation_state::threemin_map);

View File

@ -499,8 +499,7 @@ static void mips_scsi_devices(device_slot_interface &device)
void rx2030_state::rx2030(machine_config &config)
{
R2000A(config, m_cpu, 33.333_MHz_XTAL / 2, 32768, 32768);
// TODO: FPU disabled until properly emulated
//m_cpu->set_fpurev(mips1_device_base::MIPS_R2010A);
m_cpu->set_fpu(mips1_device_base::MIPS_R2010A);
m_cpu->in_brcond<0>().set([]() { return 1; }); // writeback complete
V50(config, m_iop, 20_MHz_XTAL / 2);
@ -772,7 +771,7 @@ void rx3230_state::rx3230(machine_config &config)
{
R3000A(config, m_cpu, 50_MHz_XTAL / 2, 32768, 32768);
m_cpu->set_addrmap(AS_PROGRAM, &rx3230_state::rx3230_map);
//m_cpu->set_fpurev(mips1_device_base::MIPS_R3010A);
m_cpu->set_fpu(mips1_device_base::MIPS_R3010A);
m_cpu->in_brcond<0>().set([]() { return 1; }); // writeback complete
// 32 SIMM slots, 8-128MB memory, banks of 8 1MB or 4MB SIMMs