205 lines
4.3 KiB
C++
205 lines
4.3 KiB
C++
/*
|
|
Highly inefficient and boring interpreter. Nothing special here
|
|
*/
|
|
|
|
#include "types.h"
|
|
|
|
#include "../sh4_interpreter.h"
|
|
#include "../sh4_opcode_list.h"
|
|
#include "../sh4_core.h"
|
|
#include "../sh4_interrupts.h"
|
|
#include "hw/sh4/sh4_mem.h"
|
|
#include "../sh4_sched.h"
|
|
#include "../sh4_cache.h"
|
|
#include "debug/gdb_server.h"
|
|
#include "../sh4_cycles.h"
|
|
|
|
Sh4ICache icache;
|
|
Sh4OCache ocache;
|
|
Sh4Interpreter *Sh4Interpreter::Instance;
|
|
|
|
void Sh4Interpreter::ExecuteOpcode(u16 op)
|
|
{
|
|
if (ctx->sr.FD == 1 && OpDesc[op]->IsFloatingPoint())
|
|
throw SH4ThrownException(ctx->pc - 2, Sh4Ex_FpuDisabled);
|
|
OpPtr[op](ctx, op);
|
|
sh4cycles.executeCycles(op);
|
|
}
|
|
|
|
u16 Sh4Interpreter::ReadNexOp()
|
|
{
|
|
u32 addr = ctx->pc;
|
|
if (!mmu_enabled() && (addr & 1))
|
|
// address error
|
|
throw SH4ThrownException(addr, Sh4Ex_AddressErrorRead);
|
|
|
|
ctx->pc = addr + 2;
|
|
|
|
return IReadMem16(addr);
|
|
}
|
|
|
|
void Sh4Interpreter::Run()
|
|
{
|
|
Instance = this;
|
|
ctx->restoreHostRoundingMode();
|
|
|
|
try {
|
|
do
|
|
{
|
|
try {
|
|
do
|
|
{
|
|
u32 op = ReadNexOp();
|
|
|
|
ExecuteOpcode(op);
|
|
} while (ctx->cycle_counter > 0);
|
|
ctx->cycle_counter += SH4_TIMESLICE;
|
|
UpdateSystem_INTC();
|
|
} catch (const SH4ThrownException& ex) {
|
|
Do_Exception(ex.epc, ex.expEvn);
|
|
// an exception requires the instruction pipeline to drain, so approx 5 cycles
|
|
sh4cycles.addCycles(5 * CPU_RATIO);
|
|
}
|
|
} while (ctx->CpuRunning);
|
|
} catch (const debugger::Stop&) {
|
|
}
|
|
|
|
ctx->CpuRunning = false;
|
|
Instance = nullptr;
|
|
}
|
|
|
|
void Sh4Interpreter::Start()
|
|
{
|
|
ctx->CpuRunning = true;
|
|
}
|
|
|
|
void Sh4Interpreter::Stop()
|
|
{
|
|
ctx->CpuRunning = false;
|
|
}
|
|
|
|
void Sh4Interpreter::Step()
|
|
{
|
|
verify(!ctx->CpuRunning);
|
|
Instance = this;
|
|
|
|
ctx->restoreHostRoundingMode();
|
|
try {
|
|
u32 op = ReadNexOp();
|
|
ExecuteOpcode(op);
|
|
} catch (const SH4ThrownException& ex) {
|
|
Do_Exception(ex.epc, ex.expEvn);
|
|
// an exception requires the instruction pipeline to drain, so approx 5 cycles
|
|
sh4cycles.addCycles(5 * CPU_RATIO);
|
|
} catch (const debugger::Stop&) {
|
|
}
|
|
Instance = nullptr;
|
|
}
|
|
|
|
void Sh4Interpreter::Reset(bool hard)
|
|
{
|
|
verify(!ctx->CpuRunning);
|
|
|
|
if (hard)
|
|
{
|
|
int schedNext = ctx->sh4_sched_next;
|
|
memset(ctx, 0, sizeof(*ctx));
|
|
ctx->sh4_sched_next = schedNext;
|
|
}
|
|
ctx->pc = 0xA0000000;
|
|
|
|
memset(ctx->r, 0, sizeof(ctx->r));
|
|
memset(ctx->r_bank, 0, sizeof(ctx->r_bank));
|
|
|
|
ctx->gbr = ctx->ssr = ctx->spc = ctx->sgr = ctx->dbr = ctx->vbr = 0;
|
|
ctx->mac.full = ctx->pr = ctx->fpul = 0;
|
|
|
|
ctx->sr.setFull(0x700000F0);
|
|
ctx->old_sr.status = ctx->sr.status;
|
|
UpdateSR();
|
|
|
|
ctx->fpscr.full = 0x00040001;
|
|
ctx->old_fpscr = ctx->fpscr;
|
|
|
|
icache.Reset(hard);
|
|
ocache.Reset(hard);
|
|
sh4cycles.reset();
|
|
ctx->cycle_counter = SH4_TIMESLICE;
|
|
|
|
INFO_LOG(INTERPRETER, "Sh4 Reset");
|
|
}
|
|
|
|
bool Sh4Interpreter::IsCpuRunning()
|
|
{
|
|
return ctx->CpuRunning;
|
|
}
|
|
|
|
//TODO : Check for valid delayslot instruction
|
|
void Sh4Interpreter::ExecuteDelayslot()
|
|
{
|
|
try {
|
|
u32 op = ReadNexOp();
|
|
|
|
ExecuteOpcode(op);
|
|
} catch (SH4ThrownException& ex) {
|
|
AdjustDelaySlotException(ex);
|
|
throw ex;
|
|
} catch (const debugger::Stop& e) {
|
|
ctx->pc -= 2; // break on previous instruction
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
void Sh4Interpreter::ExecuteDelayslot_RTE()
|
|
{
|
|
try {
|
|
// In an RTE delay slot, status register (SR) bits are referenced as follows.
|
|
// In instruction access, the MD bit is used before modification, and in data access,
|
|
// the MD bit is accessed after modification.
|
|
// The other bits—S, T, M, Q, FD, BL, and RB—after modification are used for delay slot
|
|
// instruction execution. The STC and STC.L SR instructions access all SR bits after modification.
|
|
u32 op = ReadNexOp();
|
|
// Now restore all SR bits
|
|
ctx->sr.setFull(ctx->ssr);
|
|
// And execute
|
|
ExecuteOpcode(op);
|
|
} catch (const SH4ThrownException&) {
|
|
throw FlycastException("Fatal: SH4 exception in RTE delay slot");
|
|
} catch (const debugger::Stop& e) {
|
|
ctx->pc -= 2; // break on previous instruction
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
// every SH4_TIMESLICE cycles
|
|
int UpdateSystem_INTC()
|
|
{
|
|
Sh4cntx.sh4_sched_next -= SH4_TIMESLICE;
|
|
if (Sh4cntx.sh4_sched_next < 0)
|
|
sh4_sched_tick(SH4_TIMESLICE);
|
|
if (Sh4cntx.interrupt_pend)
|
|
return UpdateINTC();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
void Sh4Interpreter::Init()
|
|
{
|
|
ctx = &p_sh4rcb->cntx;
|
|
memset(ctx, 0, sizeof(*ctx));
|
|
sh4cycles.init(ctx);
|
|
icache.init(ctx);
|
|
ocache.init(ctx);
|
|
}
|
|
|
|
void Sh4Interpreter::Term()
|
|
{
|
|
Stop();
|
|
INFO_LOG(INTERPRETER, "Sh4 Term");
|
|
}
|
|
|
|
Sh4Executor *Get_Sh4Interpreter()
|
|
{
|
|
return new Sh4Interpreter();
|
|
}
|