mirror of
https://github.com/hch12907/orbum.git
synced 2024-05-20 13:07:53 -04:00
Implement VU interpreter
This commit is contained in:
parent
5286c1ad7a
commit
1d1212e57e
|
@ -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"
|
||||
|
|
44
liborbum/src/Controller/Ee/Vpu/Vu/CVu.cpp
Normal file
44
liborbum/src/Controller/Ee/Vpu/Vu/CVu.cpp
Normal 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;
|
||||
}
|
22
liborbum/src/Controller/Ee/Vpu/Vu/CVu.hpp
Normal file
22
liborbum/src/Controller/Ee/Vpu/Vu/CVu.hpp
Normal 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);
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 //
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
8
liborbum/src/Resources/Ee/Vpu/Vu/VuState.hpp
Normal file
8
liborbum/src/Resources/Ee/Vpu/Vu/VuState.hpp
Normal file
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
enum class VuOperationState
|
||||
{
|
||||
Ready,
|
||||
Run,
|
||||
Stop
|
||||
};
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue