mips1: handle exceptions in delay slot instructions (nw)

I introduced this error when adding tlb support and modifying the exception code previously. This resolves the error, cleans up delayed branch handling and fixes a panic in the mips rx2030 and rx3230 driver.
This commit is contained in:
Patrick Mackinlay 2018-12-18 18:21:19 +07:00
parent f74ad668aa
commit 2af742e9d1
2 changed files with 70 additions and 58 deletions

View File

@ -39,12 +39,12 @@
#define UIMMVAL u16(op) #define UIMMVAL u16(op)
#define LIMMVAL (op & 0x03ffffff) #define LIMMVAL (op & 0x03ffffff)
#define ADDPC(x) do { m_nextpc = m_pc + ((x) << 2); } while (0) #define ADDPC(x) do { m_branch_state = BRANCH; m_branch_target = m_pc + 4 + ((x) << 2); } while (0)
#define ADDPCL(x,l) do { m_nextpc = m_pc + ((x) << 2); m_r[l] = m_pc + 4; } while (0) #define ADDPCL(x,l) do { m_branch_state = BRANCH; m_branch_target = m_pc + 4 + ((x) << 2); m_r[l] = m_pc + 8; } while (0)
#define ABSPC(x) do { m_nextpc = (m_pc & 0xf0000000) | ((x) << 2); } while (0) #define ABSPC(x) do { m_branch_state = BRANCH; m_branch_target = ((m_pc + 4) & 0xf0000000) | ((x) << 2); } while (0)
#define ABSPCL(x,l) do { m_nextpc = (m_pc & 0xf0000000) | ((x) << 2); m_r[l] = m_pc + 4; } while (0) #define ABSPCL(x,l) do { m_branch_state = BRANCH; m_branch_target = ((m_pc + 4) & 0xf0000000) | ((x) << 2); m_r[l] = m_pc + 8; } while (0)
#define SETPC(x) do { m_nextpc = (x); } while (0) #define SETPC(x) do { m_branch_state = BRANCH; m_branch_target = (x); } while (0)
#define SETPCL(x,l) do { m_nextpc = (x); m_r[l] = m_pc + 4; } while (0) #define SETPCL(x,l) do { m_branch_state = BRANCH; m_branch_target = (x); m_r[l] = m_pc + 8; } while (0)
#define SR m_cpr[0][COP0_Status] #define SR m_cpr[0][COP0_Status]
#define CAUSE m_cpr[0][COP0_Cause] #define CAUSE m_cpr[0][COP0_Cause]
@ -61,6 +61,8 @@ DEFINE_DEVICE_TYPE(R3071, r3071_device, "r3071", "IDT R3071")
DEFINE_DEVICE_TYPE(R3081, r3081_device, "r3081", "IDT R3081") DEFINE_DEVICE_TYPE(R3081, r3081_device, "r3081", "IDT R3081")
DEFINE_DEVICE_TYPE(SONYPS2_IOP, iop_device, "sonyiop", "Sony Playstation 2 IOP") DEFINE_DEVICE_TYPE(SONYPS2_IOP, iop_device, "sonyiop", "Sony Playstation 2 IOP")
ALLOW_SAVE_TYPE(mips1core_device_base::branch_state_t);
mips1core_device_base::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) mips1core_device_base::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)
: cpu_device(mconfig, type, tag, owner, clock) : cpu_device(mconfig, type, tag, owner, clock)
, m_program_config_be("program", ENDIANNESS_BIG, 32, 32) , m_program_config_be("program", ENDIANNESS_BIG, 32, 32)
@ -71,9 +73,6 @@ mips1core_device_base::mips1core_device_base(const machine_config &mconfig, devi
, m_hasfpu(false) , m_hasfpu(false)
, m_fpurev(0) , m_fpurev(0)
, m_endianness(ENDIANNESS_BIG) , m_endianness(ENDIANNESS_BIG)
, m_pc(0)
, m_nextpc(0)
, m_ppc(0)
, m_icount(0) , m_icount(0)
, m_icache_size(icache_size) , m_icache_size(icache_size)
, m_dcache_size(dcache_size) , m_dcache_size(dcache_size)
@ -177,7 +176,7 @@ void mips1core_device_base::device_start()
// register our state for the debugger // register our state for the debugger
state_add(STATE_GENPC, "GENPC", m_pc).noshow(); state_add(STATE_GENPC, "GENPC", m_pc).noshow();
state_add(STATE_GENPCBASE, "CURPC", m_ppc).noshow(); state_add(STATE_GENPCBASE, "CURPC", m_pc).noshow();
state_add(STATE_GENSP, "GENSP", m_r[31]).noshow(); state_add(STATE_GENSP, "GENSP", m_r[31]).noshow();
state_add(STATE_GENFLAGS, "GENFLAGS", m_cpr[0][COP0_Status]).noshow(); state_add(STATE_GENFLAGS, "GENFLAGS", m_cpr[0][COP0_Status]).noshow();
@ -195,13 +194,13 @@ void mips1core_device_base::device_start()
// register our state for saving // register our state for saving
save_item(NAME(m_pc)); save_item(NAME(m_pc));
save_item(NAME(m_nextpc));
save_item(NAME(m_hi)); save_item(NAME(m_hi));
save_item(NAME(m_lo)); save_item(NAME(m_lo));
save_item(NAME(m_r)); save_item(NAME(m_r));
save_item(NAME(m_cpr)); save_item(NAME(m_cpr));
save_item(NAME(m_ccr)); save_item(NAME(m_ccr));
save_item(NAME(m_ppc)); save_item(NAME(m_branch_state));
save_item(NAME(m_branch_target));
// initialise cpu and fpu id registers // initialise cpu and fpu id registers
m_cpr[0][COP0_PRId] = m_cpurev; m_cpr[0][COP0_PRId] = m_cpurev;
@ -239,7 +238,7 @@ void mips1core_device_base::device_reset()
{ {
// initialize the state // initialize the state
m_pc = 0xbfc00000; m_pc = 0xbfc00000;
m_nextpc = ~0; m_branch_state = NONE;
// non-tlb devices have tlb shut down // non-tlb devices have tlb shut down
m_cpr[0][COP0_Status] = SR_BEV | SR_TS; m_cpr[0][COP0_Status] = SR_BEV | SR_TS;
@ -274,18 +273,18 @@ std::unique_ptr<util::disasm_interface> mips1core_device_base::create_disassembl
void mips1core_device_base::generate_exception(int exception) void mips1core_device_base::generate_exception(int exception)
{ {
// set the exception PC // set the exception PC
m_cpr[0][COP0_EPC] = (exception == EXCEPTION_INTERRUPT) ? m_pc : m_ppc; m_cpr[0][COP0_EPC] = m_pc;
// put the cause in the low 8 bits and clear the branch delay flag // put the cause in the low 8 bits and clear the branch delay flag
CAUSE = (CAUSE & ~0x800000ff) | (exception << 2); CAUSE = (CAUSE & ~0x800000ff) | (exception << 2);
// if we were in a branch delay slot, adjust // if in a branch delay slot, restart the branch
if (m_nextpc != ~0) if (m_branch_state == DELAY)
{ {
m_nextpc = ~0;
m_cpr[0][COP0_EPC] -= 4; m_cpr[0][COP0_EPC] -= 4;
CAUSE |= 0x80000000; CAUSE |= 0x80000000;
} }
m_branch_state = NONE;
// shift the exception bits // shift the exception bits
SR = (SR & 0xffffffc0) | ((SR << 2) & 0x3c); SR = (SR & 0xffffffc0) | ((SR << 2) & 0x3c);
@ -552,7 +551,6 @@ void mips1core_device_base::execute_run()
do do
{ {
// debugging // debugging
m_ppc = m_pc;
debugger_instruction_hook(m_pc); debugger_instruction_hook(m_pc);
#if ENABLE_IOP_KPUTS #if ENABLE_IOP_KPUTS
@ -575,16 +573,6 @@ void mips1core_device_base::execute_run()
// fetch and execute instruction // fetch and execute instruction
fetch(m_pc, [this](u32 const op) fetch(m_pc, [this](u32 const op)
{ {
{
// adjust for next PC
if (m_nextpc != ~0)
{
m_pc = m_nextpc;
m_nextpc = ~0;
}
else
m_pc += 4;
// parse the instruction // parse the instruction
switch (op >> 26) switch (op >> 26)
{ {
@ -784,11 +772,28 @@ void mips1core_device_base::execute_run()
case 0x3f: /* SDC3 */ generate_exception(EXCEPTION_INVALIDOP); break; case 0x3f: /* SDC3 */ generate_exception(EXCEPTION_INVALIDOP); break;
default: /* ??? */ generate_exception(EXCEPTION_INVALIDOP); break; default: /* ??? */ generate_exception(EXCEPTION_INVALIDOP); break;
} }
// update pc and branch state
switch (m_branch_state)
{
case NONE:
m_pc += 4;
break;
case DELAY:
m_branch_state = NONE;
m_pc = m_branch_target;
break;
case BRANCH:
m_branch_state = DELAY;
m_pc += 4;
break;
} }
}); });
m_icount--; m_icount--;
} while (m_icount > 0 || m_nextpc != ~0); } while (m_icount > 0 || m_branch_state);
} }
void mips1core_device_base::lwl(u32 const op) void mips1core_device_base::lwl(u32 const op)
@ -995,7 +1000,8 @@ bool mips1_device_base::memory_translate(int spacenum, int intention, offs_t &ad
if (!(intention & TRANSLATE_DEBUG_MASK)) if (!(intention & TRANSLATE_DEBUG_MASK))
{ {
if ((VERBOSE & LOG_TLB) && !dirty) if ((VERBOSE & LOG_TLB) && !dirty)
LOGMASKED(LOG_TLB, "tlb miss address 0x%08x\n", address); LOGMASKED(LOG_TLB, "tlb miss %s address 0x%08x (%s)\n",
(intention & TRANSLATE_WRITE) ? "store" : "load", address, machine().describe_context());
// exception // exception
m_cpr[0][COP0_BadVAddr] = address; m_cpr[0][COP0_BadVAddr] = address;

View File

@ -101,32 +101,32 @@ protected:
enum sr_mask : u32 enum sr_mask : u32
{ {
SR_IEc = 0x00000001, SR_IEc = 0x00000001, // interrupt enable
SR_KUc = 0x00000002, SR_KUc = 0x00000002, // kernel mode
SR_IEp = 0x00000004, SR_IEp = 0x00000004, // interrupt enable (previous)
SR_KUp = 0x00000008, SR_KUp = 0x00000008, // kernel mode (previous)
SR_IEo = 0x00000010, SR_IEo = 0x00000010, // interrupt enable (old)
SR_KUo = 0x00000020, SR_KUo = 0x00000020, // kernel mode (old)
SR_IMSW0 = 0x00000100, SR_IMSW0 = 0x00000100, // software interrupt 0 enable
SR_IMSW1 = 0x00000200, SR_IMSW1 = 0x00000200, // software interrupt 1 enable
SR_IMEX0 = 0x00000400, SR_IMEX0 = 0x00000400, // external interrupt 0 enable
SR_IMEX1 = 0x00000800, SR_IMEX1 = 0x00000800, // external interrupt 1 enable
SR_IMEX2 = 0x00001000, SR_IMEX2 = 0x00001000, // external interrupt 2 enable
SR_IMEX3 = 0x00002000, SR_IMEX3 = 0x00002000, // external interrupt 3 enable
SR_IMEX4 = 0x00004000, SR_IMEX4 = 0x00004000, // external interrupt 4 enable
SR_IMEX5 = 0x00008000, SR_IMEX5 = 0x00008000, // external interrupt 5 enable
SR_IsC = 0x00010000, SR_IsC = 0x00010000, // isolate (data) cache
SR_SwC = 0x00020000, SR_SwC = 0x00020000, // swap caches
SR_PZ = 0x00040000, SR_PZ = 0x00040000, // cache parity zero
SR_CM = 0x00080000, SR_CM = 0x00080000, // cache match
SR_PE = 0x00100000, SR_PE = 0x00100000, // cache parity error
SR_TS = 0x00200000, SR_TS = 0x00200000, // tlb shutdown
SR_BEV = 0x00400000, SR_BEV = 0x00400000, // boot exception vectors
SR_RE = 0x02000000, SR_RE = 0x02000000, // reverse endianness in user mode
SR_COP0 = 0x10000000, SR_COP0 = 0x10000000, // coprocessor 0 usable
SR_COP1 = 0x20000000, SR_COP1 = 0x20000000, // coprocessor 1 usable
SR_COP2 = 0x40000000, SR_COP2 = 0x40000000, // coprocessor 2 usable
SR_COP3 = 0x80000000, SR_COP3 = 0x80000000, // coprocessor 3 usable
}; };
enum entryhi_mask : u32 enum entryhi_mask : u32
@ -213,7 +213,6 @@ protected:
// core registers // core registers
u32 m_pc; u32 m_pc;
u32 m_nextpc;
u32 m_hi; u32 m_hi;
u32 m_lo; u32 m_lo;
u32 m_r[32]; u32 m_r[32];
@ -223,8 +222,15 @@ protected:
u32 m_ccr[4][32]; u32 m_ccr[4][32];
// internal stuff // internal stuff
u32 m_ppc;
int m_icount; int m_icount;
enum branch_state_t : unsigned
{
NONE = 0,
DELAY = 1, // delay slot instruction active
BRANCH = 2, // branch instruction active
}
m_branch_state;
u32 m_branch_target;
// cache memory // cache memory
size_t const m_icache_size; size_t const m_icache_size;