Merge branch 'master' into gif-impl

This commit is contained in:
Marco Satti 2018-09-08 14:51:34 +08:00
commit 30ad98be49
14 changed files with 783 additions and 413 deletions

View file

@ -81,6 +81,7 @@ set(COMMON_SRC_FILES
"${CMAKE_SOURCE_DIR}/liborbum/src/Controller/Ee/Timers/CEeTimers.hpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Controller/Ee/Vpu/Vif/CVif.cpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Controller/Ee/Vpu/Vif/CVif.hpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Controller/Ee/Vpu/Vu/VuBranchDelaySlot.hpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Controller/Ee/Vpu/Vu/Interpreter/CVuInterpreter.cpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Controller/Ee/Vpu/Vu/Interpreter/CVuInterpreter.hpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Controller/Ee/Vpu/Vu/Interpreter/CVuInterpreter_CONVERT.cpp"

View file

@ -177,6 +177,9 @@ struct Constants
struct VPU
{
// A VU Instruction is 8 bytes long
static constexpr const int SIZE_VU_INSTRUCTION = 8;
struct VIF
{
// Number of instructions is based off the CMD field in the VIFcode, without the leading interrupt bit and the m bit in the UNPACK instructions. See EE Users Manual page 87.

View file

@ -76,7 +76,7 @@ public:
return current_slot > 0;
}
private:
protected:
size_t current_slot;
uptr branch_pc;

View file

@ -3,6 +3,14 @@
#include "Controller/Ee/Vpu/Vu/Interpreter/CVuInterpreter.hpp"
#include "Core.hpp"
// All instructions here are related to the conversion between floating-points and
// fixed-points.
//
// FTOIx are the instructions for converting a float to a fixed with (32-x) bits
// as the integer part, and (x) bits as the fractional part.
//
// ITOFx are the instructions for converting a fixed to a float.
void CVuInterpreter::FTOI0(VuUnit_Base* unit, const VuInstruction inst)
{
SizedQwordRegister& ft = unit->vf[inst.ft()];

View file

@ -1,134 +1,190 @@
#include <boost/format.hpp>
#include <cmath>
#include "Controller/Ee/Vpu/Vu/Interpreter/CVuInterpreter.hpp"
#include "Core.hpp"
// All instructions here are related to the EFU (Elementary Functio Unit).
// All results produced by those instructions are stored in the P register.
void CVuInterpreter::ESADD(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) ESADD: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("ESADD: Not implemented.");
#endif
// P = VF[fs](x)^2 + VF[fs](y)^2 + VF[fs](z)^2
SizedQwordRegister& reg_source = unit->vf[inst.fs()];
SizedWordRegister& reg_dest = unit->p;
const f32 a = reg_source.read_float(VuVectorField::X);
const f32 b = reg_source.read_float(VuVectorField::Y);
const f32 c = reg_source.read_float(VuVectorField::Z);
const f32 result = (a * a) + (b * b) + (c * c);
reg_dest.write_float(result);
}
void CVuInterpreter::ERSADD(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) ERSADD: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("ERSADD: Not implemented.");
#endif
// P = 1 / (VF[fs](x)^2 + VF[fs](y)^2 + VF[fs](z)^2)
SizedQwordRegister& reg_source = unit->vf[inst.fs()];
SizedWordRegister& reg_dest = unit->p;
const f32 a = reg_source.read_float(VuVectorField::X);
const f32 b = reg_source.read_float(VuVectorField::Y);
const f32 c = reg_source.read_float(VuVectorField::Z);
const f32 result = 1.0f / ((a * a) + (b * b) + (c * c));
reg_dest.write_float(result);
}
void CVuInterpreter::ELENG(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) ELENG: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("ELENG: Not implemented.");
#endif
// P = sqrt(VF[fs](x)^2 + VF[fs](y)^2 + VF[fs](z)^2)
SizedQwordRegister& reg_source = unit->vf[inst.fs()];
SizedWordRegister& reg_dest = unit->p;
const f32 a = reg_source.read_float(VuVectorField::X);
const f32 b = reg_source.read_float(VuVectorField::Y);
const f32 c = reg_source.read_float(VuVectorField::Z);
const f32 result = std::sqrt((a * a) + (b * b) + (c * c));
reg_dest.write_float(result);
}
void CVuInterpreter::ERLENG(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) ERLENG: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("ERLENG: Not implemented.");
#endif
// P = 1 / sqrt(VF[fs](x)^2 + VF[fs](y)^2 + VF[fs](z)^2)
SizedQwordRegister& reg_source = unit->vf[inst.fs()];
SizedWordRegister& reg_dest = unit->p;
const f32 a = reg_source.read_float(VuVectorField::X);
const f32 b = reg_source.read_float(VuVectorField::Y);
const f32 c = reg_source.read_float(VuVectorField::Z);
const f32 result = 1.0f / std::sqrt((a * a) + (b * b) + (c * c));
reg_dest.write_float(result);
}
void CVuInterpreter::EATANxy(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) EATANxy: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("EATANxy: Not implemented.");
#endif
// P = arctan(VF[fs](y) / VF[fs](x))
SizedQwordRegister& reg_source = unit->vf[inst.fs()];
SizedWordRegister& reg_dest = unit->p;
const f32 a = reg_source.read_float(VuVectorField::X);
const f32 b = reg_source.read_float(VuVectorField::Y);
const f32 result = std::atan(b / a);
reg_dest.write_float(result);
}
void CVuInterpreter::EATANxz(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) EATANxz: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("EATANxz: Not implemented.");
#endif
// P = arctan(VF[fs](y) / VF[fs](x))
SizedQwordRegister& reg_source = unit->vf[inst.fs()];
SizedWordRegister& reg_dest = unit->p;
const f32 a = reg_source.read_float(VuVectorField::X);
const f32 b = reg_source.read_float(VuVectorField::Z);
const f32 result = std::atan(b / a);
reg_dest.write_float(result);
}
void CVuInterpreter::ESUM(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) ESUM: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("ESUM: Not implemented.");
#endif
// P = VF[fs](w) + VF[fs](x) + VF[fs](y) + VF[fs](z)
SizedQwordRegister& reg_source = unit->vf[inst.fs()];
SizedWordRegister& reg_dest = unit->p;
const f32 a = reg_source.read_float(VuVectorField::X);
const f32 b = reg_source.read_float(VuVectorField::Y);
const f32 c = reg_source.read_float(VuVectorField::Z);
const f32 d = reg_source.read_float(VuVectorField::W);
const f32 result = a + b + c + d;
reg_dest.write_float(result);
}
void CVuInterpreter::ERCPR(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) ERCPR: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("ERCPR: Not implemented.");
#endif
// P = 1 / VF[fs](fsf)
SizedQwordRegister& reg_source = unit->vf[inst.fs()];
SizedWordRegister& reg_dest = unit->p;
const f32 a = reg_source.read_float(inst.fsf());
const f32 result = 1.0f / a;
reg_dest.write_float(result);
}
void CVuInterpreter::ESQRT(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) ESQRT: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("ESQRT: Not implemented.");
#endif
// P = sqrt(VF[fs](fsf))
SizedQwordRegister& reg_source = unit->vf[inst.fs()];
SizedWordRegister& reg_dest = unit->p;
const f32 a = reg_source.read_float(inst.fsf());
const f32 result = std::sqrt(a);
reg_dest.write_float(result);
}
void CVuInterpreter::ERSQRT(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) ERSQRT: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("ERSQRT: Not implemented.");
#endif
// P = 1 / sqrt(VF[fs](fsf))
SizedQwordRegister& reg_source = unit->vf[inst.fs()];
SizedWordRegister& reg_dest = unit->p;
const f32 a = reg_source.read_float(inst.fsf());
const f32 result = 1.0f / std::sqrt(a);
reg_dest.write_float(result);
}
void CVuInterpreter::ESIN(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) ESIN: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("ESIN: Not implemented.");
#endif
// P = sin(VF[fs](fsf))
SizedQwordRegister& reg_source = unit->vf[inst.fs()];
SizedWordRegister& reg_dest = unit->p;
const f32 a = reg_source.read_float(inst.fsf());
const f32 result = std::sin(a);
reg_dest.write_float(result);
}
void CVuInterpreter::EATAN(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) EATAN: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("EATAN: Not implemented.");
#endif
// P = arctan(VF[fs](fsf))
SizedQwordRegister& reg_source = unit->vf[inst.fs()];
SizedWordRegister& reg_dest = unit->p;
const f32 a = reg_source.read_float(inst.fsf());
const f32 result = std::atan(a);
reg_dest.write_float(result);
}
void CVuInterpreter::EEXP(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) EEXP: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("EEXP: Not implemented.");
#endif
// P = exp(-VF[fs](fsf))
SizedQwordRegister& reg_source = unit->vf[inst.fs()];
SizedWordRegister& reg_dest = unit->p;
const f32 a = reg_source.read_float(inst.fsf());
const f32 result = std::exp(-a);
reg_dest.write_float(result);
}

View file

@ -3,122 +3,92 @@
#include "Controller/Ee/Vpu/Vu/Interpreter/CVuInterpreter.hpp"
#include "Core.hpp"
// All instructions here are related to the flags.
// Mostly straightforward.
void CVuInterpreter::FSAND(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) FSAND: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("FSAND: Not implemented.");
#endif
const uword status = unit->status.read_uword();
SizedHwordRegister& vi = unit->vi[inst.it()];
vi.write_uhword(status & inst.imm12());
}
void CVuInterpreter::FSEQ(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) FSEQ: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("FSEQ: Not implemented.");
#endif
const uword status = unit->status.read_uword();
SizedHwordRegister& vi = unit->vi[inst.it()];
vi.write_uhword((uhword)(status == inst.imm12()));
}
void CVuInterpreter::FSOR(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) FSOR: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("FSOR: Not implemented.");
#endif
const uword status = unit->status.read_uword();
SizedHwordRegister& vi = unit->vi[inst.it()];
vi.write_uhword(status | inst.imm12());
}
void CVuInterpreter::FSSET(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) FSSET: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("FSSET: Not implemented.");
#endif
SizedWordRegister& status = unit->status;
// Set the sticky flags
// 0xFC0 == 0b111111000000
status.write_uhword(0, status.read_uhword(0) | (inst.imm12() & 0xFC0));
}
void CVuInterpreter::FMAND(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) FMAND: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("FMAND: Not implemented.");
#endif
SizedHwordRegister& it = unit->vi[inst.it()];
const uword mac = unit->mac.read_uword();
const uhword is = unit->vi[inst.is()].read_uhword();
it.write_uhword(mac & is);
}
void CVuInterpreter::FMEQ(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) FMEQ: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("FMEQ: Not implemented.");
#endif
SizedHwordRegister& it = unit->vi[inst.it()];
const uword mac = unit->mac.read_uword();
const uhword is = unit->vi[inst.is()].read_uhword();
it.write_uhword(mac == is);
}
void CVuInterpreter::FMOR(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) FMOR: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("FMOR: Not implemented.");
#endif
SizedHwordRegister& it = unit->vi[inst.it()];
const uword mac = unit->mac.read_uword();
const uhword is = unit->vi[inst.is()].read_uhword();
it.write_uhword(mac | is);
}
void CVuInterpreter::FCAND(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) FCAND: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("FCAND: Not implemented.");
#endif
SizedHwordRegister& vi01 = unit->vi[1];
const uword clipping = unit->clipping.read_uword();
vi01.write_uhword((clipping & inst.imm24()) > 0);
}
void CVuInterpreter::FCEQ(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) FCEQ: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("FCEQ: Not implemented.");
#endif
SizedHwordRegister& vi01 = unit->vi[1];
const uword clipping = unit->clipping.read_uword();
vi01.write_uhword(clipping == inst.imm24());
}
void CVuInterpreter::FCOR(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) FCOR: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("FCOR: Not implemented.");
#endif
SizedHwordRegister& vi01 = unit->vi[1];
const uword clipping = unit->clipping.read_uword();
vi01.write_uhword((clipping | inst.imm24()) == 0xFFFFFF);
}
void CVuInterpreter::FCSET(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) FCSET: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("FCSET: Not implemented.");
#endif
SizedWordRegister& clipping = unit->clipping;
clipping.write_uword(inst.imm24());
}
void CVuInterpreter::FCGET(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) FCGET: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("FCGET: Not implemented.");
#endif
SizedHwordRegister& it = unit->vi[inst.it()];
const uhword clipping = unit->clipping.read_uword();
it.write_uhword(clipping & 0xFFF);
}

View file

@ -8,6 +8,8 @@
#include "Resources/Ee/Vpu/Vu/VuUnits.hpp"
#include "Utilities/Utilities.hpp"
// All instructions here are related to float arithmetic.
//
// Explaination for the comments:
// VF[x] - the x-th register of VF
// VF[x](f) - the f field of the x-th register of VF, if not specified

View file

@ -3,73 +3,113 @@
#include "Controller/Ee/Vpu/Vu/Interpreter/CVuInterpreter.hpp"
#include "Core.hpp"
#include "Resources/Ee/Vpu/Vu/VuUnits.hpp"
#include "Utilities/Utilities.hpp"
// All instructions here are related to integer arithmetics.
void CVuInterpreter::IADD(VuUnit_Base* unit, const VuInstruction inst)
{
// ID = IS + IT.
auto& reg_source1 = unit->vi[inst.fs()]; // IS.
auto& reg_source2 = unit->vi[inst.ft()]; // IT.
auto& reg_dest = unit->vi[inst.fd()]; // ID.
// VI[id] = VI[is] + VI[it]
reg_dest.write_uhword(reg_source1.read_uhword() + reg_source2.read_uhword());
SizedHwordRegister& reg_source_1 = unit->vi[inst.is()];
SizedHwordRegister& reg_source_2 = unit->vi[inst.it()];
SizedHwordRegister& reg_dest = unit->vi[inst.id()];
const shword a = reg_source_1.read_uhword();
const shword b = reg_source_2.read_uhword();
const shword result = a + b;
// The behaviour is standard-defined and thus cross-platform. (Phew!)
reg_dest.write_uhword(static_cast<uhword>(result));
}
void CVuInterpreter::IADDI(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) IADDI: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("IADDI: Not implemented.");
#endif
// VI[it] = VI[is] + Imm
SizedHwordRegister& reg_source_1 = unit->vi[inst.is()];
const ubyte reg_source_2 = inst.imm5();
SizedHwordRegister& reg_dest = unit->vi[inst.it()];
// Imm5 is a five-bit signed integer, so convert it
const shword a = static_cast<shword>(reg_source_1.read_uhword());
const shword b = extend_integer<shword, ubyte, 5>(reg_source_2);
shword result = a + b;
reg_dest.write_uhword(static_cast<uhword>(result));
}
void CVuInterpreter::IADDIU(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) IADDIU: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("IADDIU: Not implemented.");
#endif
// VI[it] = VI[is] + Imm
SizedHwordRegister& reg_source_1 = unit->vi[inst.is()];
const uhword reg_source_2 = inst.imm15();
SizedHwordRegister& reg_dest = unit->vi[inst.it()];
const shword a = static_cast<shword>(reg_source_1.read_uhword());
const uhword b = reg_source_2;
shword result = a + b;
reg_dest.write_uhword(static_cast<uhword>(result));
}
void CVuInterpreter::IAND(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) IAND: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("IAND: Not implemented.");
#endif
// VI[id] = VI[is] & VI[it]
SizedHwordRegister& reg_source_1 = unit->vi[inst.is()];
SizedHwordRegister& reg_source_2 = unit->vi[inst.it()];
SizedHwordRegister& reg_dest = unit->vi[inst.id()];
const shword a = reg_source_1.read_uhword();
const shword b = reg_source_2.read_uhword();
const shword result = a & b;
reg_dest.write_uhword(static_cast<uhword>(result));
}
void CVuInterpreter::IOR(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) IOR: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("IOR: Not implemented.");
#endif
// VI[id] = VI[is] | VI[it]
SizedHwordRegister& reg_source_1 = unit->vi[inst.is()];
SizedHwordRegister& reg_source_2 = unit->vi[inst.it()];
SizedHwordRegister& reg_dest = unit->vi[inst.id()];
const shword a = reg_source_1.read_uhword();
const shword b = reg_source_2.read_uhword();
const shword result = a | b;
reg_dest.write_uhword(static_cast<uhword>(result));
}
void CVuInterpreter::ISUB(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) ISUB: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("ISUB: Not implemented.");
#endif
// VI[id] = VI[is] - VI[it]
SizedHwordRegister& reg_source_1 = unit->vi[inst.is()];
SizedHwordRegister& reg_source_2 = unit->vi[inst.it()];
SizedHwordRegister& reg_dest = unit->vi[inst.id()];
const shword a = reg_source_1.read_uhword();
const shword b = reg_source_2.read_uhword();
const shword result = a - b;
reg_dest.write_uhword(static_cast<uhword>(result));
}
void CVuInterpreter::ISUBIU(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) ISUBIU: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("ISUBIU: Not implemented.");
#endif
// VI[it] = VI[is] - Imm
SizedHwordRegister& reg_source_1 = unit->vi[inst.is()];
const uhword reg_source_2 = inst.imm15();
SizedHwordRegister& reg_dest = unit->vi[inst.it()];
const shword a = static_cast<shword>(reg_source_1.read_uhword());
const uhword b = reg_source_2;
shword result = a - b;
reg_dest.write_uhword(static_cast<uhword>(result));
}

View file

@ -1,208 +1,271 @@
#include <boost/format.hpp>
#include <cmath>
#include "Controller/Ee/Vpu/Vu/Interpreter/CVuInterpreter.hpp"
#include "Core.hpp"
#include "Resources/RResources.hpp"
#include "Utilities/Utilities.hpp"
// Miscellaneous VU instructions.
// Includes:-
// - random (R* instructions)
// - branching & jumps
// - GIF/VIF interaction
// - WAIT* instruciotns (synchronization between different VU execution units)
void CVuInterpreter::NOP(VuUnit_Base* unit, const VuInstruction inst)
{
return;
}
void CVuInterpreter::CLIP(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) CLIP: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("CLIP: Not implemented.");
#endif
// Left-shifts CLIP by 6 bits, compares the fields of FS with w field of FT,
// and sets the corresponding flags
SizedQwordRegister& reg_source_1 = unit->vf[inst.fs()];
SizedQwordRegister& reg_source_2 = unit->vf[inst.ft()];
VuUnitRegister_Clipping& clip = unit->clipping;
const f32 ft = std::abs(reg_source_2.read_float(VuVectorField::W));
clip.shift_judgement();
clip.insert_field(VuUnitRegister_Clipping::POSX_0, reg_source_1.read_float(VuVectorField::X) > ft);
clip.insert_field(VuUnitRegister_Clipping::NEGX_0, reg_source_1.read_float(VuVectorField::X) < -ft);
clip.insert_field(VuUnitRegister_Clipping::POSY_0, reg_source_1.read_float(VuVectorField::Y) > ft);
clip.insert_field(VuUnitRegister_Clipping::NEGY_0, reg_source_1.read_float(VuVectorField::Y) < -ft);
clip.insert_field(VuUnitRegister_Clipping::POSZ_0, reg_source_1.read_float(VuVectorField::Z) > ft);
clip.insert_field(VuUnitRegister_Clipping::NEGZ_0, reg_source_1.read_float(VuVectorField::Z) < -ft);
}
void CVuInterpreter::RINIT(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) RINIT: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("RINIT: Not implemented.");
#endif
SizedQwordRegister& reg_source = unit->vf[inst.fs()];
SizedWordRegister& reg_dest = unit->r;
// Writes a float consisting 23 bits of R as mantissa and 001111111 as exp+sign.
constexpr uword append = 0b001111111 << 23;
const uword fsf = (reg_source.read_uword(inst.fsf()) & 0x7FFFFF) | append;
reg_dest.write_uword(fsf);
}
void CVuInterpreter::RGET(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) RGET: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("RGET: Not implemented.");
#endif
SizedWordRegister& reg_source = unit->r;
SizedQwordRegister& reg_dest = unit->vf[inst.ft()];
for (auto field : VuVectorField::VECTOR_FIELDS)
{
if (inst.test_dest_field(field))
{
reg_dest.write_uword(field, reg_source.read_uword());
}
}
}
void CVuInterpreter::RNEXT(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) RNEXT: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("RNEXT: Not implemented.");
#endif
SizedWordRegister& reg_R = unit->r;
SizedQwordRegister& reg_dest = unit->vf[inst.ft()];
// A Galois form M-series LFSR adapted from PCSX2 (advance_r() in pcsx2/vu.cpp)
const int x = (reg_R.read_uword() >> 4) & 1;
const int y = (reg_R.read_uword() >> 22) & 1;
reg_R.write_uword(((reg_R.read_uword() << 1) ^ (x ^ y)) & 0x7FFFFF);
// Append the exp+sign to R
reg_R.write_uword(reg_R.read_uword() | 0b001111111 << 23);
}
void CVuInterpreter::RXOR(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) RXOR: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("RXOR: Not implemented.");
#endif
SizedWordRegister& reg_source_1 = unit->r;
SizedQwordRegister& reg_source_2 = unit->vf[inst.ft()];
SizedWordRegister& reg_dest = unit->r;
const uword a = reg_source_1.read_uword();
const uword b = reg_source_2.read_uword(inst.fsf()) & 0x7FFFFF;
const uword result = a ^ b;
reg_dest.write_uword(result);
}
void CVuInterpreter::WAITQ(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
// TODO:
// Actual synchronization.
// The VU Interpreter is synchronous, I imagine, so synchronization is actually unneeded.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) WAITQ: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("WAITQ: Not implemented.");
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) WAITQ is called!") % __FILENAME__ % __LINE__;
#endif
return;
}
void CVuInterpreter::IBEQ(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) IBEQ: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("IBEQ: Not implemented.");
#endif
SizedHwordRegister& reg_source_1 = unit->vi[inst.is()];
SizedHwordRegister& reg_source_2 = unit->vi[inst.it()];
if (reg_source_1.read_uhword() == reg_source_2.read_uhword())
{
shword offset_by = extend_integer<shword, uhword, 11>(inst.imm11());
unit->bdelay.set_branch_itype(unit->pc, offset_by);
}
}
void CVuInterpreter::IBGEZ(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) IBGEZ: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("IBGEZ: Not implemented.");
#endif
SizedHwordRegister& reg_source_1 = unit->vi[inst.is()];
if (reg_source_1.read_uhword() >= 0)
{
shword offset_by = extend_integer<shword, uhword, 11>(inst.imm11());
unit->bdelay.set_branch_itype(unit->pc, offset_by);
}
}
void CVuInterpreter::IBGTZ(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) IBGTZ: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("IBGTZ: Not implemented.");
#endif
SizedHwordRegister& reg_source_1 = unit->vi[inst.is()];
if (reg_source_1.read_uhword() > 0)
{
shword offset_by = extend_integer<shword, uhword, 11>(inst.imm11());
unit->bdelay.set_branch_itype(unit->pc, offset_by);
}
}
void CVuInterpreter::IBLEZ(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) IBLEZ: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("IBLEZ: Not implemented.");
#endif
SizedHwordRegister& reg_source_1 = unit->vi[inst.is()];
if (reg_source_1.read_uhword() <= 0)
{
shword offset_by = extend_integer<shword, uhword, 11>(inst.imm11());
unit->bdelay.set_branch_itype(unit->pc, offset_by);
}
}
void CVuInterpreter::IBLTZ(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) IBLTZ: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("IBLTZ: Not implemented.");
#endif
SizedHwordRegister& reg_source_1 = unit->vi[inst.is()];
if (reg_source_1.read_uhword() < 0)
{
shword offset_by = extend_integer<shword, uhword, 11>(inst.imm11());
unit->bdelay.set_branch_itype(unit->pc, offset_by);
}
}
void CVuInterpreter::IBNE(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) IBNE: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("IBNE: Not implemented.");
#endif
SizedHwordRegister& reg_source_1 = unit->vi[inst.is()];
SizedHwordRegister& reg_source_2 = unit->vi[inst.it()];
if (reg_source_1.read_uhword() != reg_source_2.read_uhword())
{
shword offset_by = extend_integer<shword, uhword, 11>(inst.imm11());
unit->bdelay.set_branch_itype(unit->pc, offset_by);
}
}
void CVuInterpreter::B(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) B: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("B: Not implemented.");
#endif
shword offset_by = extend_integer<shword, uhword, 11>(inst.imm11());
unit->bdelay.set_branch_itype(unit->pc, offset_by);
}
void CVuInterpreter::BAL(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) BAL: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("BAL: Not implemented.");
#endif
SizedHwordRegister& reg_dest = unit->vi[inst.it()];
if (unit->bdelay.is_branch_pending())
{
// If there is a pending branch, the linked register becomes the
// second instruction of said branch
reg_dest.write_uhword((unit->bdelay.get_branch_pc() + 8) / 8);
}
else
{
// Otherwise, the linked register is the instruction next to the
// branch delay slot
reg_dest.write_uhword((unit->pc.read_uword() + 2 * 8) / 8);
}
shword offset_by = extend_integer<shword, uhword, 11>(inst.imm11());
unit->bdelay.set_branch_itype(unit->pc, offset_by);
}
void CVuInterpreter::JR(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) JR: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("JR: Not implemented.");
#endif
SizedHwordRegister& reg_source_1 = unit->vi[inst.is()];
unit->bdelay.set_branch_jtype(unit->pc, reg_source_1.read_uhword());
}
void CVuInterpreter::JALR(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) JALR: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("JALR: Not implemented.");
#endif
SizedHwordRegister& reg_source_1 = unit->vi[inst.is()];
SizedHwordRegister& reg_dest = unit->vi[inst.it()];
if (unit->bdelay.is_branch_pending())
{
// If there is a pending branch, the linked register becomes the
// second instruction of said branch
reg_dest.write_uhword((unit->bdelay.get_branch_pc() + 8) / 8);
}
else
{
// Otherwise, the linked register is the instruction next to the
// branch delay slot
reg_dest.write_uhword((unit->pc.read_uword() + 2 * 8) / 8);
}
unit->bdelay.set_branch_jtype(unit->pc, reg_source_1.read_uhword());
}
void CVuInterpreter::WAITP(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
// TODO:
// Actual synchronization.
// The VU Interpreter is synchronous, I imagine, so synchronization is actually unneeded.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) WAITP: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("WAITP: Not implemented.");
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) WAITP is called!") % __FILENAME__ % __LINE__;
#endif
return;
}
void CVuInterpreter::XGKICK(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
// TODO: Wait for GIF to be implemented
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) XGKICK: Not implemented.") % __FILENAME__ % __LINE__;
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) XGKICK: Not implemented. (GIF not implemented)") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("XGKICK: Not implemented.");
throw std::runtime_error("XGKICK: Not implemented. (GIF not implemented)");
#endif
}
void CVuInterpreter::XTOP(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) XTOP: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("XTOP: Not implemented.");
#endif
SizedHwordRegister& reg_dest = unit->vi[inst.it()];
const RResources& r = core->get_resources();
VifUnit_Base* vif = r.ee.vpu.vif.units[unit->core_id];
reg_dest.write_uhword(vif->top.read_uword());
}
void CVuInterpreter::XITOP(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) XITOP: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("XITOP: Not implemented.");
#endif
SizedHwordRegister& reg_dest = unit->vi[inst.it()];
const RResources& r = core->get_resources();
VifUnit_Base* vif = r.ee.vpu.vif.units[unit->core_id];
reg_dest.write_uhword(vif->itop.read_uword());
}

View file

@ -4,185 +4,248 @@
#include "Core.hpp"
#include "Resources/Ee/Vpu/Vu/VuUnits.hpp"
// All instructions here are related to registers.
// Particularly load/store
void CVuInterpreter::MOVE(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) MOVE: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("MOVE: Not implemented.");
#endif
SizedQwordRegister& reg_source = unit->vf[inst.fs()];
SizedQwordRegister& reg_dest = unit->vf[inst.ft()];
for (auto field : VuVectorField::VECTOR_FIELDS)
{
if (inst.test_dest_field(field))
{
reg_dest.write_uword(reg_source.read_uword(field), field);
}
}
}
void CVuInterpreter::MFIR(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) MFIR: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("MFIR: Not implemented.");
#endif
SizedHwordRegister& reg_source = unit->vi[inst.is()];
SizedQwordRegister& reg_dest = unit->vf[inst.ft()];
for (auto field : VuVectorField::VECTOR_FIELDS)
{
if (inst.test_dest_field(field))
{
reg_dest.write_uword(reg_source.read_uhword(), field);
}
}
}
void CVuInterpreter::MTIR(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) MTIR: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("MTIR: Not implemented.");
#endif
SizedQwordRegister& reg_source = unit->vf[inst.fs()];
SizedHwordRegister& reg_dest = unit->vi[inst.it()];
reg_dest.write_uhword(reg_source.read_uword(inst.fsf()) & 0xFFFF);
}
void CVuInterpreter::MR32(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) MR32: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("MR32: Not implemented.");
#endif
SizedQwordRegister& reg_source = unit->vf[inst.fs()];
SizedQwordRegister& reg_dest = unit->vf[inst.ft()];
reg_dest.write_uword(reg_source.read_uword(VuVectorField::W), VuVectorField::X);
reg_dest.write_uword(reg_source.read_uword(VuVectorField::X), VuVectorField::Y);
reg_dest.write_uword(reg_source.read_uword(VuVectorField::Y), VuVectorField::Z);
reg_dest.write_uword(reg_source.read_uword(VuVectorField::Z), VuVectorField::W);
}
void CVuInterpreter::LQ(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) LQ: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("LQ: Not implemented.");
#endif
SizedHwordRegister& reg_source = unit->vi[inst.is()];
SizedQwordRegister& reg_dest = unit->vf[inst.ft()];
const uword address = (inst.imm11() + reg_source.read_uhword()) * NUMBER_BYTES_IN_QWORD;
const uqword source = unit->bus.read_uqword(BusContext::Vu, address);
for (auto field : VuVectorField::VECTOR_FIELDS)
{
if (inst.test_dest_field(field))
{
// Investigate?: Endianness scares me
reg_dest.write_uword(field, source.uw[field]);
}
}
}
void CVuInterpreter::LQD(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) LQD: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("LQD: Not implemented.");
#endif
SizedHwordRegister& reg_source = unit->vi[inst.is()];
SizedQwordRegister& reg_dest = unit->vf[inst.ft()];
// Pre-decrement VI first
reg_source.write_uhword(reg_source.read_uhword() - 1);
const uword address = reg_source.read_uhword() * NUMBER_BYTES_IN_QWORD;
const uqword source = unit->bus.read_uqword(BusContext::Vu, address);
for (auto field : VuVectorField::VECTOR_FIELDS)
{
if (inst.test_dest_field(field))
{
// Investigate?: Endianness scares me
reg_dest.write_uword(field, source.uw[field]);
}
}
}
void CVuInterpreter::LQI(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) LQI: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("LQI: Not implemented.");
#endif
SizedHwordRegister& reg_source = unit->vi[inst.is()];
SizedQwordRegister& reg_dest = unit->vf[inst.ft()];
const uword address = reg_source.read_uhword() * NUMBER_BYTES_IN_QWORD;
const uqword source = unit->bus.read_uqword(BusContext::Vu, address);
for (auto field : VuVectorField::VECTOR_FIELDS)
{
if (inst.test_dest_field(field))
{
// Investigate?: Endianness scares me
reg_dest.write_uword(field, source.uw[field]);
}
}
// Post-increment the VI
reg_source.write_uhword(reg_source.read_uhword() + 1);
}
void CVuInterpreter::SQ(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) SQ: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("SQ: Not implemented.");
#endif
SizedQwordRegister& reg_source_1 = unit->vf[inst.fs()];
SizedHwordRegister& reg_source_2 = unit->vi[inst.it()];
const uword address = (inst.imm11() + reg_source_2.read_uhword()) * NUMBER_BYTES_IN_QWORD;
for (auto field : VuVectorField::VECTOR_FIELDS)
{
if (inst.test_dest_field(field))
{
unit->bus.write_uword(BusContext::Vu, address + NUMBER_BYTES_IN_WORD * field, reg_source_1.read_uword(field));
}
}
}
void CVuInterpreter::SQD(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) SQD: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("SQD: Not implemented.");
#endif
SizedQwordRegister& reg_source_1 = unit->vf[inst.fs()];
SizedHwordRegister& reg_source_2 = unit->vi[inst.it()];
reg_source_2.write_uhword(reg_source_2.read_uhword() - 1);
const uword address = reg_source_2.read_uhword() * NUMBER_BYTES_IN_QWORD;
for (auto field : VuVectorField::VECTOR_FIELDS)
{
if (inst.test_dest_field(field))
{
unit->bus.write_uword(BusContext::Vu, address + NUMBER_BYTES_IN_WORD * field, reg_source_1.read_uword(field));
}
}
}
void CVuInterpreter::SQI(VuUnit_Base* unit, const VuInstruction inst)
{
// MEM(Ft) = Fs
auto& reg_source1 = unit->vf[inst.fs()];
auto& reg_source2 = unit->vi[inst.ft()]; // Mem Addr.
SizedQwordRegister& reg_source_1 = unit->vf[inst.fs()];
SizedHwordRegister& reg_source_2 = unit->vi[inst.ft()]; // Mem Addr.
// Real address obtained by VI * 16 (qword addressing).
uword address = reg_source2.read_uhword() * NUMBER_BYTES_IN_QWORD;
const uword address = reg_source_2.read_uhword() * NUMBER_BYTES_IN_QWORD;
// 32-bit write for each dest subfield.
if (inst.test_dest_x())
unit->bus.write_uword(BusContext::Vu, address, reg_source1.read_uword(0));
if (inst.test_dest_y())
unit->bus.write_uword(BusContext::Vu, address + 4, reg_source1.read_uword(1));
if (inst.test_dest_z())
unit->bus.write_uword(BusContext::Vu, address + 8, reg_source1.read_uword(2));
if (inst.test_dest_w())
unit->bus.write_uword(BusContext::Vu, address + 12, reg_source1.read_uword(3));
for (auto field : VuVectorField::VECTOR_FIELDS)
{
if (inst.test_dest_field(field))
{
unit->bus.write_uword(BusContext::Vu, address + NUMBER_BYTES_IN_WORD * field, reg_source_1.read_uword(field));
}
}
// Post increment.
reg_source2.write_uhword(reg_source2.read_uhword() + 1);
reg_source_2.write_uhword(reg_source_2.read_uhword() + 1);
}
void CVuInterpreter::ILW(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) ILW: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("ILW: Not implemented.");
#endif
SizedHwordRegister& reg_source = unit->vi[inst.is()];
SizedHwordRegister& reg_dest = unit->vi[inst.it()];
const uword address = (inst.imm15() + reg_source.read_uhword()) * NUMBER_BYTES_IN_QWORD;
const uword source = unit->bus.read_uqword(BusContext::Vu, address).uw[inst.dest()];
reg_dest.write_uhword(static_cast<uhword>(source));
}
void CVuInterpreter::ISW(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) ISW: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("ISW: Not implemented.");
#endif
SizedHwordRegister& reg_source_1 = unit->vi[inst.it()];
SizedHwordRegister& reg_source_2 = unit->vi[inst.is()];
const uword address = (inst.imm15() + reg_source_2.read_uhword()) * NUMBER_BYTES_IN_QWORD;
for (auto field : VuVectorField::VECTOR_FIELDS)
{
if (inst.test_dest_field(field))
{
unit->bus.write_uword(BusContext::Vu, address + NUMBER_BYTES_IN_WORD * field, reg_source_1.read_uhword());
}
}
}
void CVuInterpreter::ILWR(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) ILWR: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("ILWR: Not implemented.");
#endif
SizedHwordRegister& reg_source = unit->vi[inst.is()];
SizedHwordRegister& reg_dest = unit->vi[inst.it()];
const uword address = reg_source.read_uhword() * NUMBER_BYTES_IN_QWORD;
const uword source = unit->bus.read_uqword(BusContext::Vu, address).uw[inst.dest()];
reg_dest.write_uhword(static_cast<uhword>(source));
}
void CVuInterpreter::ISWR(VuUnit_Base* unit, const VuInstruction inst)
{
// MEM(Fs) = Ft.
auto& reg_source1 = unit->vi[inst.ft()]; // Data.
auto& reg_source2 = unit->vi[inst.fs()]; // Mem Addr.
SizedHwordRegister& reg_source_1 = unit->vi[inst.it()]; // Data.
SizedHwordRegister& reg_source_2 = unit->vi[inst.is()]; // Mem Addr.
// Real address obtained by VI * 16.
uword address = reg_source2.read_uhword() * NUMBER_BYTES_IN_QWORD;
const uword address = reg_source_2.read_uhword() * NUMBER_BYTES_IN_QWORD;
// 32-bit write for each dest subfield. Upper 16-bits of VI[Ft] value is set to 0.
if (inst.test_dest_x())
unit->bus.write_uword(BusContext::Vu, address, static_cast<uword>(reg_source1.read_uhword()));
if (inst.test_dest_y())
unit->bus.write_uword(BusContext::Vu, address + 4, static_cast<uword>(reg_source1.read_uhword()));
if (inst.test_dest_z())
unit->bus.write_uword(BusContext::Vu, address + 8, static_cast<uword>(reg_source1.read_uhword()));
if (inst.test_dest_w())
unit->bus.write_uword(BusContext::Vu, address + 12, static_cast<uword>(reg_source1.read_uhword()));
for (auto field : VuVectorField::VECTOR_FIELDS)
{
if (inst.test_dest_field(field))
{
unit->bus.write_uword(BusContext::Vu, address + NUMBER_BYTES_IN_WORD * field, reg_source_1.read_uhword());
}
}
}
void CVuInterpreter::LOI(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) LOI: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("LOI: Not implemented.");
#endif
// Call this instruction when the I bit of the upper instruction is set
// And pass the lower instruction in
unit->i.write_uword(inst.value);
// (In case you are wondering... LOI is a pseudo-instruction, it's
// not supposed to be referenced)
}
void CVuInterpreter::MFP(VuUnit_Base* unit, const VuInstruction inst)
{
// TODO: Implement.
#if defined(BUILD_DEBUG)
BOOST_LOG(Core::get_logger()) << boost::format("(%s, %d) MFP: Not implemented.") % __FILENAME__ % __LINE__;
#else
throw std::runtime_error("MFP: Not implemented.");
#endif
SizedWordRegister& reg_source = unit->p;
SizedQwordRegister& reg_dest = unit->vf[inst.ft()];
for (auto field : VuVectorField::VECTOR_FIELDS)
{
if (inst.test_dest_field(field))
{
reg_dest.write_uword(reg_source.read_uword(), field);
}
}
}

View file

@ -0,0 +1,113 @@
#pragma once
#include <cereal/cereal.hpp>
#include "Common/Types/Mips/BranchDelaySlot.hpp"
#include "Common/Types/Primitive.hpp"
#include "Common/Types/Register/PcRegisters.hpp"
/// BranchDelaySlot, modified slightly for the VUs.
/// See BranchDelaySlot for more documentation.
template <size_t slots = 1>
class VuBranchDelaySlot : public BranchDelaySlot<slots>
{
public:
VuBranchDelaySlot() :
second_branch_pc(0),
second_branch_pending(false),
BranchDelaySlot<slots>()
{
}
/// is_branch_pending() in VuBranchDelaySlot has the same behaviour as the
/// one in BranchDelaySlot.
using BranchDelaySlot<slots>::is_branch_pending;
/// Obtains the PC of which the VU is branching to.
/// This is used by the *AL* (link register) branching/jumping instructions.
uptr get_branch_pc()
{
return branch_pc;
}
/// Sets the offset of the PC address of the VUs, with a delay slot.
/// See BranchDelaySlot::set_branch_itype for more documentation.
/// The only difference is the size of the instruction - VU instructions are 8 bytes long
/// whereas EE Core instructions are only 4.
void set_branch_itype(WordPcRegister& pc, const shword imm)
{
// If we are branching in the delay slot, the original branch runs for only one cycle
if (is_branch_pending())
{
second_branch_pending = true;
second_branch_pc = (pc.read_uword() + Constants::EE::VPU::SIZE_VU_INSTRUCTION + imm * 8) & 0x3FFF;
}
else
{
current_slot = slots + 1;
// VU can hold 16KB of instructions only (and 4KB in VU0), so AND it with 0x3FFF
branch_pc = (pc.read_uword() + Constants::EE::VPU::SIZE_VU_INSTRUCTION + imm * 8) & 0x3FFF;
}
}
/// Sets the PC address of the VUs, with a delay slot.
/// The VUs' jumping range is much smaller than the EE Core's
/// (just 16KB is sufficient for the VUs), and so this method behaves
/// differently from the original set_branch_jtype.
void set_branch_jtype(WordPcRegister& pc, const uptr jump_to)
{
if (is_branch_pending())
{
second_branch_pending = true;
second_branch_pc = (jump_to * 8) & 0x3FFF;
}
else
{
current_slot = slots + 1;
branch_pc = (jump_to * 8) & 0x3FFF;
}
}
void advance_pc(WordPcRegister& pc)
{
if (current_slot)
{
current_slot--;
if (!current_slot)
{
pc.write_uword(branch_pc);
if (second_branch_pending)
{
second_branch_pending = false;
branch_pc = second_branch_pc;
current_slot = 1;
}
return;
}
}
pc.offset(Constants::EE::VPU::SIZE_VU_INSTRUCTION);
}
private:
// introduces the base class variables to this class
using BranchDelaySlot<slots>::branch_pc;
using BranchDelaySlot<slots>::current_slot;
uptr second_branch_pc;
bool second_branch_pending;
// Serialization
public:
template<class Archive>
void serialize(Archive & archive)
{
archive(
CEREAL_NVP(current_slot),
CEREAL_NVP(branch_pc),
CEREAL_NVP(second_branch_pc),
CEREAL_NVP(second_branch_pending)
);
}
};

View file

@ -1,6 +1,7 @@
#pragma once
#include "Common/Types/Mips/MipsInstruction.hpp"
#include "Common/Types/Mips/MipsInstructionInfo.hpp"
#include "Resources/Ee/Vpu/Vu/VuVectorField.hpp"
/// A VU instruction type, which is used to extract information out of the parsed 32-bit value.
@ -50,16 +51,35 @@ struct VuInstruction : public MipsInstruction
return static_cast<ubyte>(FT.extract_from(value));
}
inline ubyte it() const
{
// FT and IT differ in name only
return ft();
}
ubyte fs() const
{
return static_cast<ubyte>(FS.extract_from(value));
}
inline ubyte is() const
{
// FS and IS differ in name only
return fs();
}
ubyte fd() const
{
return static_cast<ubyte>(FD.extract_from(value));
}
inline ubyte id() const
{
// FD and ID differ in name only
return fs();
}
ubyte opcode() const
{
return static_cast<ubyte>(OPCODE.extract_from(value));
@ -80,11 +100,26 @@ struct VuInstruction : public MipsInstruction
return static_cast<ubyte>(FSF.extract_from(value));
}
ubyte imm5() const {
// FD is IMM5 in some instructions
return static_cast<ubyte>(FD.extract_from(value));
}
uhword imm11() const
{
return static_cast<uhword>(IMM11.extract_from(value));
}
uhword imm12() const
{
return static_cast<uhword>(IMM11.extract_from(value) | (DEST.extract_from(value) & 1) << 11);
}
uhword imm15() const
{
return static_cast<uhword>(IMM11.extract_from(value) | (DEST.extract_from(value) << 11));
}
uword imm24() const
{
return static_cast<uword>(IMM24.extract_from(value));

View file

@ -12,6 +12,7 @@
#include "Common/Types/Register/PcRegisters.hpp"
#include "Common/Types/Register/SizedHwordRegister.hpp"
#include "Common/Types/Register/SizedQwordRegister.hpp"
#include "Controller/Ee/Vpu/Vu/VuBranchDelaySlot.hpp"
#include "Resources/Ee/Vpu/Vu/VuUnitRegisters.hpp"
class EeCoreCop0;
@ -62,6 +63,7 @@ public:
/// Also known as the TPC (termination PC), treated as the same thing.
/// Made to be 32-bit even though only 16-bits are used (bus maps easier).
WordPcRegister pc;
VuBranchDelaySlot<> bdelay;
/// The CMSAR register used for micro subroutine execution.
/// See VU Users Manual page 202.

View file

@ -30,6 +30,20 @@ int count_leading_bits(const sword value);
/// Example: in 0b...0100, the answer is 2.
int count_trailing_zeros(const uword value);
/// Parses `source` of type U as a N-bit integer, converting it to an int of type T.
template<typename T, typename U, unsigned int N>
constexpr T extend_integer(const U source)
{
// For a 5 bit integer, it's the first 4 bits (0b01111)
constexpr T value_mask = (1 << (N - 1)) - 1;
// For converting a 5 bit to a 32 bit integer, it's the last (32-4) bits
constexpr T non_value_mask = (-1) ^ value_mask;
const T sign = ((T)source >> (N - 1)) & 1;
const T value = (T)source & value_mask;
return (sign == 0) ? value : non_value_mask | value;
}
/// Saturates values to the next smallest size, if above the maximum value allowed.
/// Eg: for 0x02345678 to hword, this becomes 0x7FFF;
shword saturate_word_to_hword(const sword value);