Implement VU interpreter

This commit is contained in:
hch12907 2018-09-16 16:19:11 +08:00
parent 5286c1ad7a
commit 1d1212e57e
10 changed files with 205 additions and 47 deletions

View file

@ -81,7 +81,8 @@ 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/CVu.cpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Controller/Ee/Vpu/Vu/CVu.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"
@ -188,9 +189,11 @@ set(COMMON_SRC_FILES
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Vpu/VpuRegisters.hpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Vpu/Vu/RVu.cpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Vpu/Vu/RVu.hpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Vpu/Vu/VuBranchDelaySlot.hpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Vpu/Vu/VuInstruction.cpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Vpu/Vu/VuInstruction.hpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Vpu/Vu/VuRegisters.hpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Vpu/Vu/VuState.hpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Vpu/Vu/VuUnitRegisters.cpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Vpu/Vu/VuUnitRegisters.hpp"
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Vpu/Vu/VuUnits.cpp"

View file

@ -0,0 +1,44 @@
#include <boost/format.hpp>
#include "Controller/Ee/Vpu/Vu/CVu.hpp"
#include "Resources/RResources.hpp"
CVu::CVu(Core *core) :
CController(core)
{
}
void CVu::handle_event(const ControllerEvent& event)
{
switch (event.type)
{
case ControllerEvent::Type::Time:
{
int ticks_remaining = time_to_ticks(event.data.time_us);
while (ticks_remaining > 0)
ticks_remaining -= time_step(ticks_remaining);
break;
}
default:
{
throw std::runtime_error("CVu event handler not implemented - please fix!");
}
}
}
int CVu::time_to_ticks(const double time_us)
{
int ticks = static_cast<int>(time_us / 1.0e6 * Constants::EE::VPU::VU::VU_CLK_SPEED * core->get_options().system_bias_vu);
if (ticks < 10)
{
static bool warned = false;
if (!warned)
{
BOOST_LOG(Core::get_logger()) << "VU ticks too low - increase time delta";
warned = true;
}
}
return ticks;
}

View file

@ -0,0 +1,22 @@
#pragma once
#include "Core.hpp"
#include "Controller/CController.hpp"
#include "Controller/ControllerEvent.hpp"
class Core;
class CVu : public CController
{
public:
CVu(Core* core);
void handle_event(const ControllerEvent& event) override;
/// Steps through the VU core state, executing one macro and one micro instruction.
virtual int time_step(const int ticks_available) = 0;
private:
/// Converts a time duration into the number of ticks that would have occurred.
int time_to_ticks(const double time_us);
};

View file

@ -1,53 +1,109 @@
#include <boost/format.hpp>
#include "Controller/Ee/Vpu/Vu/Interpreter/CVuInterpreter.hpp"
#include "Core.hpp"
#include "Resources/RResources.hpp"
CVuInterpreter::CVuInterpreter(Core* core) :
CController(core)
CVu(core)
{
}
void CVuInterpreter::handle_event(const ControllerEvent& event)
{
switch (event.type)
{
case ControllerEvent::Type::Time:
{
int ticks_remaining = time_to_ticks(event.data.time_us);
while (ticks_remaining > 0)
ticks_remaining -= time_step(ticks_remaining);
break;
}
default:
{
throw std::runtime_error("CVuInterpreter event handler not implemented - please fix!");
}
}
}
int CVuInterpreter::time_to_ticks(const double time_us)
{
int ticks = static_cast<int>(time_us / 1.0e6 * Constants::EE::VPU::VU::VU_CLK_SPEED * core->get_options().system_bias_vu);
if (ticks < 10)
{
static bool warned = false;
if (!warned)
{
BOOST_LOG(Core::get_logger()) << "Vu ticks too low - increase time delta";
warned = true;
}
}
return ticks;
}
int CVuInterpreter::time_step(const int ticks_available)
{
RResources& r = core->get_resources();
// TODO: Not yet implemented.
#if defined(BUILD_DEBUG)
DEBUG_LOOP_COUNTER++;
#endif
// The priority of register writing is: COP2 Transfer > Upper Inst > Lower Inst
for (auto* unit : r.ee.vpu.vu.units)
{
// Move on if the unit is not running and the delay slot is empty
if (unit->operation_state != VuOperationState::Run && !unit->bdelay.is_branch_pending())
{
continue;
}
// PC & Instructions stuff...
const uword pc = unit->pc.read_uword() & 0x0FFF;
const udword raw_inst = r.ee.bus.read_uword(BusContext::Vu, 0x11000000 + 0x8000 * unit->core_id + pc);
const uword upper_raw_inst = (raw_inst >> 31) & 0xFFFFFFFF;
const VuInstruction upper_inst = VuInstruction(upper_raw_inst);
const MipsInstructionInfo upper_info = upper_inst.upper_lookup();
const uword lower_raw_inst = raw_inst & 0xFFFFFFFF;
const VuInstruction lower_inst = VuInstruction(lower_raw_inst);
const MipsInstructionInfo lower_info = lower_inst.lower_lookup();
// If I (bit 63) is set, execute UpperInst and LOI (using LowerInst as an immediate)
if ((raw_inst >> 63) & 1)
{
(this->*VU_INSTRUCTION_TABLE[upper_inst.upper_lookup().impl_index])(unit, upper_inst);
this->LOI(unit, lower_inst);
// Onto the next unit
continue;
}
// If E (bit 62) is set, execute current and next instruction, and
// terminate the current micro subroutine
// Here we setup a delay slot for the next instruction
if ((raw_inst >> 62) & 1)
{
if (unit->bdelay.is_branch_pending())
{
BOOST_LOG(Core::get_logger()) << "Found E-bit in branch delay slot";
}
// Don't actually branch
unit->bdelay.set_branch_itype(unit->pc, 0);
// Change the state of the VU
unit->operation_state = VuOperationState::Ready;
}
// If M (bit 61) is set, then execute QMTC2 or CTC2 without interlocking
// (VU0 only)
if (((raw_inst >> 61) & 1) && unit->core_id == 0)
{
// TODO
}
// If D (bit 60) and DE (in FBRST) is set, terminate the micro subroutine and interrupt
if ((raw_inst >> 60) & 1)
{
if (r.ee.vpu.vu.fbrst.de(unit->core_id))
{
auto _lock = r.ee.intc.stat.scope_lock();
r.ee.intc.stat.insert_field(EeIntcRegister_Stat::VU_KEYS[unit->core_id], 1);
unit->operation_state = VuOperationState::Stop;
}
}
// If T (bit 59) and TE (in FBRST) is set, terminate the micro subroutine and interrupt
if ((raw_inst >> 59) & 1)
{
if (r.ee.vpu.vu.fbrst.te(unit->core_id))
{
auto _lock = r.ee.intc.stat.scope_lock();
r.ee.intc.stat.insert_field(EeIntcRegister_Stat::VU_KEYS[unit->core_id], 1);
unit->operation_state = VuOperationState::Stop;
}
}
(this->*VU_INSTRUCTION_TABLE[lower_inst.lower_lookup().impl_index])(unit, lower_inst);
(this->*VU_INSTRUCTION_TABLE[upper_inst.upper_lookup().impl_index])(unit, upper_inst);
// Advance the PC
unit->bdelay.advance_pc(unit->pc);
}
// TODO: Correct CPI
return 1;
}

View file

@ -1,24 +1,19 @@
#pragma once
#include "Controller/CController.hpp"
#include "Controller/Ee/Vpu/Vu/CVu.hpp"
#include "Resources/Ee/Vpu/Vu/VuInstruction.hpp"
#include "Resources/Ee/Vpu/Vu/VuUnits.hpp"
class Core;
/// The VU0/1 interpreter.
class CVuInterpreter : public CController
class CVuInterpreter : public CVu
{
public:
CVuInterpreter(Core* core);
void handle_event(const ControllerEvent& event) override;
/// Converts a time duration into the number of ticks that would have occurred.
int time_to_ticks(const double time_us);
/// Steps through the VU core state, executing one macro and one micro instruction.
int time_step(const int ticks_available);
int time_step(const int ticks_available) override;
//////////////////////////
// Common Functionality //

View file

@ -15,4 +15,29 @@ public:
static constexpr Bitfield RS1 = Bitfield(9, 1);
static constexpr Bitfield DE1 = Bitfield(10, 1);
static constexpr Bitfield TE1 = Bitfield(11, 1);
// Field extraction methods
ubyte fb(uword core_id)
{
// Small explanation: core_id << 3 = core_id * 8
// Thus for VU0 (core_id of which = 0), we obtain bit 0
// and for VU1 we obtain bit 8
return (read_uword() << (0 + (core_id << 3))) & 1;
}
ubyte rs(uword core_id)
{
return (read_uword() << (1 + (core_id << 3))) & 1;
}
ubyte de(uword core_id)
{
return (read_uword() << (2 + (core_id << 3))) & 1;
}
ubyte te(uword core_id)
{
return (read_uword() << (3 + (core_id << 3))) & 1;
}
};

View file

@ -0,0 +1,8 @@
#pragma once
enum class VuOperationState
{
Ready,
Run,
Stop
};

View file

@ -15,7 +15,8 @@ VuUnit_Base::VuUnit_Base(const int core_id) :
SizedHwordRegister(), SizedHwordRegister(), SizedHwordRegister(), SizedHwordRegister(),
SizedHwordRegister(), SizedHwordRegister(), SizedHwordRegister(), SizedHwordRegister(),
SizedHwordRegister(), SizedHwordRegister(), SizedHwordRegister(), SizedHwordRegister()},
bus(8) // TODO: fine tune.
bus(8), // TODO: fine tune.
operation_state(VuOperationState::Ready)
{
}

View file

@ -12,7 +12,8 @@
#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/VuBranchDelaySlot.hpp"
#include "Resources/Ee/Vpu/Vu/VuState.hpp"
#include "Resources/Ee/Vpu/Vu/VuUnitRegisters.hpp"
class EeCoreCop0;
@ -78,6 +79,9 @@ public:
/// Used by different things, eg: ccr registers for VU0 and bus mappings for VU1.
MapperHwordWordRegister vi_32[Constants::EE::VPU::VU::NUMBER_VI_REGISTERS];
/// VU operation state. The VU has 3 operation states: Ready, Run, Stop.
VuOperationState operation_state;
public:
template<class Archive>
void serialize(Archive & archive)