mirror of
https://github.com/emu-russia/pureikyubu.git
synced 2025-04-02 10:42:15 -04:00
315 lines
10 KiB
C++
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);
|
|
}
|
|
|
|
}
|