pureikyubu/SRC/DSP/DspInterpreter.cpp
2020-09-02 17:52:34 +03:00

315 lines
10 KiB
C++

// GameCube DSP interpreter
#include "pch.h"
using namespace Debug;
namespace DSP
{
DspInterpreter::DspInterpreter(DspCore* parent)
{
core = parent;
}
DspInterpreter::~DspInterpreter()
{
}
void DspInterpreter::FetchMpyParams(DspParameter s1p, DspParameter s2p, int64_t& s1, int64_t& s2, bool checkDp)
{
switch (s1p)
{
case DspParameter::x0:
s1 = core->regs.x.l;
break;
case DspParameter::x1:
s1 = core->regs.x.h;
break;
case DspParameter::y1:
s1 = core->regs.y.h;
break;
case DspParameter::a1:
s1 = core->regs.a.m;
break;
case DspParameter::b1:
s1 = core->regs.b.m;
break;
}
switch (s2p)
{
case DspParameter::x0:
s2 = core->regs.x.l;
break;
case DspParameter::x1:
s2 = core->regs.x.h;
break;
case DspParameter::y0:
s2 = core->regs.y.l;
break;
case DspParameter::y1:
s2 = core->regs.y.h;
break;
}
if (core->regs.psr.dp && checkDp)
{
if (s1p == DspParameter::x0 && s2p == DspParameter::y1)
{
s2 = DspCore::SignExtend16((uint16_t)s2);
}
else if (s1p == DspParameter::x1 && s2p == DspParameter::y0)
{
s1 = DspCore::SignExtend16((uint16_t)s1);
}
else if (s1p == DspParameter::x1 && s2p == DspParameter::y1)
{
s1 = DspCore::SignExtend16((uint16_t)s1);
s2 = DspCore::SignExtend16((uint16_t)s2);
}
}
if (core->regs.psr.im == 0)
{
s2 *= 2;
}
}
void DspInterpreter::AdvanceAddress(int r, DspParameter param)
{
switch (param)
{
case DspParameter::mod_none:
break;
case DspParameter::mod_dec:
core->ArAdvance(r, -1);
break;
case DspParameter::mod_inc:
core->ArAdvance(r, +1);
break;
case DspParameter::mod_plus_m0:
core->ArAdvance(r, core->regs.m[0]);
break;
case DspParameter::mod_plus_m1:
core->ArAdvance(r, core->regs.m[1]);
break;
case DspParameter::mod_plus_m2:
core->ArAdvance(r, core->regs.m[2]);
break;
case DspParameter::mod_plus_m3:
core->ArAdvance(r, core->regs.m[3]);
break;
case DspParameter::mod_minus_m:
core->ArAdvance(r, -core->regs.m[r]);
break;
case DspParameter::mod_plus_m:
core->ArAdvance(r, core->regs.m[r]);
break;
}
}
bool DspInterpreter::ConditionTrue(ConditionCode cc)
{
switch (cc)
{
case ConditionCode::ge: return (core->regs.psr.n ^ core->regs.psr.v) == 0;
case ConditionCode::lt: return (core->regs.psr.n ^ core->regs.psr.v) != 0;
case ConditionCode::gt: return (core->regs.psr.z | (core->regs.psr.n ^ core->regs.psr.v)) == 0;
case ConditionCode::le: return (core->regs.psr.z | (core->regs.psr.n ^ core->regs.psr.v)) != 0;
case ConditionCode::nz: return core->regs.psr.z == 0;
case ConditionCode::z: return core->regs.psr.z != 0;
case ConditionCode::nc: return core->regs.psr.c == 0;
case ConditionCode::c: return core->regs.psr.c != 0;
case ConditionCode::ne: return core->regs.psr.e == 0;
case ConditionCode::e: return core->regs.psr.e != 0;
case ConditionCode::nm: return (core->regs.psr.z | (~core->regs.psr.u & ~core->regs.psr.e)) == 0;
case ConditionCode::m: return (core->regs.psr.z | (~core->regs.psr.u & ~core->regs.psr.e)) != 0;
case ConditionCode::nt: return core->regs.psr.tb == 0;
case ConditionCode::t: return core->regs.psr.tb != 0;
case ConditionCode::v: return core->regs.psr.v != 0;
case ConditionCode::always: return true;
}
return false;
}
void DspInterpreter::Dispatch(AnalyzeInfo& info)
{
// A non-flowControl instruction can change the interpreter's internal flag (for example, when trying to access the stack registers with overflow and generating an Error interrupt).
flowControl = info.flowControl;
if (!info.parallel)
{
switch (info.instr)
{
case DspRegularInstruction::jmp: jmp(info); break;
case DspRegularInstruction::call: call(info); break;
case DspRegularInstruction::rets: rets(info); break;
case DspRegularInstruction::reti: reti(info); break;
case DspRegularInstruction::trap: trap(info); break;
case DspRegularInstruction::wait: wait(info); break;
case DspRegularInstruction::exec: exec(info); break;
case DspRegularInstruction::loop: loop(info); break;
case DspRegularInstruction::rep: rep(info); break;
case DspRegularInstruction::pld: pld(info); break;
case DspRegularInstruction::nop:
break;
case DspRegularInstruction::mr: mr(info); break;
case DspRegularInstruction::adsi: adsi(info); break;
case DspRegularInstruction::adli: adli(info); break;
case DspRegularInstruction::cmpsi: cmpsi(info); break;
case DspRegularInstruction::cmpli: cmpli(info); break;
case DspRegularInstruction::lsfi: lsfi(info); break;
case DspRegularInstruction::asfi: asfi(info); break;
case DspRegularInstruction::xorli: xorli(info); break;
case DspRegularInstruction::anli: anli(info); break;
case DspRegularInstruction::orli: orli(info); break;
case DspRegularInstruction::norm: norm(info); break;
case DspRegularInstruction::div: div(info); break;
case DspRegularInstruction::addc: addc(info); break;
case DspRegularInstruction::subc: subc(info); break;
case DspRegularInstruction::negc: negc(info); break;
case DspRegularInstruction::max: _max(info); break;
case DspRegularInstruction::lsf: lsf(info); break;
case DspRegularInstruction::asf: asf(info); break;
case DspRegularInstruction::ld: ld(info); break;
case DspRegularInstruction::st: st(info); break;
case DspRegularInstruction::ldsa: ldsa(info); break;
case DspRegularInstruction::stsa: stsa(info); break;
case DspRegularInstruction::ldla: ldla(info); break;
case DspRegularInstruction::stla: stla(info); break;
case DspRegularInstruction::mv: mv(info); break;
case DspRegularInstruction::mvsi: mvsi(info); break;
case DspRegularInstruction::mvli: mvli(info); break;
case DspRegularInstruction::stli: stli(info); break;
case DspRegularInstruction::clr: clr(info); break;
case DspRegularInstruction::set: set(info); break;
case DspRegularInstruction::btstl: btstl(info); break;
case DspRegularInstruction::btsth: btsth(info); break;
}
}
else
{
switch (info.parallelInstr)
{
case DspParallelInstruction::add: p_add(info); break;
case DspParallelInstruction::addl: p_addl(info); break;
case DspParallelInstruction::sub: p_sub(info); break;
case DspParallelInstruction::amv: p_amv(info); break;
case DspParallelInstruction::cmp: p_cmp(info); break;
case DspParallelInstruction::inc: p_inc(info); break;
case DspParallelInstruction::dec: p_dec(info); break;
case DspParallelInstruction::abs: p_abs(info); break;
case DspParallelInstruction::neg: p_neg(info); break;
case DspParallelInstruction::clr: p_clr(info); break;
case DspParallelInstruction::rnd: p_rnd(info); break;
case DspParallelInstruction::rndp: p_rndp(info); break;
case DspParallelInstruction::tst: p_tst(info); break;
case DspParallelInstruction::lsl16: p_lsl16(info); break;
case DspParallelInstruction::lsr16: p_lsr16(info); break;
case DspParallelInstruction::asr16: p_asr16(info); break;
case DspParallelInstruction::addp: p_addp(info); break;
case DspParallelInstruction::nop:
break;
case DspParallelInstruction::set: p_set(info); break;
case DspParallelInstruction::mpy: p_mpy(info); break;
case DspParallelInstruction::mac: p_mac(info); break;
case DspParallelInstruction::macn: p_macn(info); break;
case DspParallelInstruction::mvmpy: p_mvmpy(info); break;
case DspParallelInstruction::rnmpy: p_rnmpy(info); break;
case DspParallelInstruction::admpy: p_admpy(info); break;
case DspParallelInstruction::_not: p_not(info); break;
case DspParallelInstruction::_xor: p_xor(info); break;
case DspParallelInstruction::_and: p_and(info); break;
case DspParallelInstruction::_or: p_or(info); break;
case DspParallelInstruction::lsf: p_lsf(info); break;
case DspParallelInstruction::asf: p_asf(info); break;
}
switch (info.parallelMemInstr)
{
case DspParallelMemInstruction::ldd: p_ldd(info); break;
case DspParallelMemInstruction::ls: p_ls(info); break;
case DspParallelMemInstruction::ld: p_ld(info); break;
case DspParallelMemInstruction::st: p_st(info); break;
case DspParallelMemInstruction::mv: p_mv(info); break;
case DspParallelMemInstruction::mr: p_mr(info); break;
case DspParallelMemInstruction::nop:
break;
}
}
// If there were no control transfers, increase pc by the instruction size
if (!flowControl)
{
// Checking the logic of the `rep` instruction.
// If the value of the repeat register is not equal to 0, then instead of the usual PC increment, it is not performed.
if (core->repeatCount)
{
core->repeatCount--;
}
if (core->repeatCount == 0)
{
// Checking the current pc for loop is done only if the eas/lcs stack is not empty
if (core->regs.pc == core->regs.eas->top() && !core->regs.lcs->empty())
{
// If pc is equal to eas then lcs = lcs - 1.
uint16_t lc;
core->regs.lcs->pop(lc);
core->regs.lcs->push(lc - 1);
// If after that lcs is not equal to zero, then pc = pcs. Otherwise pop pcs/eas/lcs and pc = pc + 1 (exit the loop)
if (core->regs.lcs->top() != 0)
{
core->regs.pc = core->regs.pcs->top();
}
else
{
uint16_t dummy;
core->regs.pcs->pop(dummy);
core->regs.eas->pop(dummy);
core->regs.lcs->pop(dummy);
// The DSP behaves strangely when the last loop instruction is a branch instruction.
// The exact work of the DSP in this case is on the verge of unpredictable behavior, so we will not bother and complicate the code.
// All the same, microcode developers are adequate people and will never deal with placing branch instructions at the end of a loop.
core->regs.pc += 1;
}
}
else
{
core->regs.pc += (DspAddress)(info.sizeInBytes >> 1);
}
}
}
}
void DspInterpreter::ExecuteInstr()
{
AnalyzeInfo info;
// Fetch, analyze and dispatch instruction at pc addr
DspAddress imemAddr = core->regs.pc;
uint8_t* imemPtr = core->dsp->TranslateIMem(imemAddr);
if (imemPtr == nullptr)
{
Halt("DSP TranslateIMem failed on dsp addr: 0x%04X\n", imemAddr);
core->dsp->Suspend();
return;
}
Analyzer::Analyze(imemPtr, DspCore::MaxInstructionSizeInBytes, info);
Dispatch(info);
}
}