mirror of
https://github.com/hch12907/orbum.git
synced 2024-06-01 19:08:05 -04:00
Merge marco9999/gif-impl.
This commit is contained in:
commit
62bb9925ab
|
@ -161,6 +161,9 @@ set(COMMON_SRC_FILES
|
|||
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/EeRegisters.hpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Gif/RGif.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Gif/RGif.hpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Gif/Giftag.hpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Gif/GifRegisters.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Gif/GifRegisters.hpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Intc/EeIntcConstants.hpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Intc/EeIntcRegisters.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Ee/Intc/EeIntcRegisters.hpp"
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "Common/Types/Primitive.hpp"
|
||||
|
||||
/// Describes a bitfield within a primitive value.
|
||||
struct Bitfield
|
||||
{
|
||||
|
@ -50,6 +52,35 @@ struct Bitfield
|
|||
return cleaned_value | cleaned_field_value;
|
||||
}
|
||||
|
||||
template <typename To, typename From>
|
||||
constexpr To extract_from(const From value) const
|
||||
{
|
||||
return static_cast<To>((value & shifted_mask<From>()) >> start);
|
||||
}
|
||||
|
||||
template <typename To, typename From>
|
||||
constexpr To insert_into(const From value, const From field_value) const
|
||||
{
|
||||
From cleaned_value = value & (~shifted_mask<From>());
|
||||
From cleaned_field_value = (field_value & unshifted_mask<From>()) << start;
|
||||
return static_cast<To>(cleaned_value | cleaned_field_value);
|
||||
}
|
||||
|
||||
/// qword template specialization.
|
||||
template <typename To>
|
||||
constexpr To extract_from(const uqword value) const
|
||||
{
|
||||
// Most (all?) bitfields within uqword types are aligned to 64 bit
|
||||
// boundaries... But this should be easy to implement if needed.
|
||||
if ((start < 64) && ((length + start) >= 64))
|
||||
throw std::logic_error("Cannot extract bitfield over 64 bit boundary from a uqword [not implemented]");
|
||||
|
||||
if (start < 64)
|
||||
return Bitfield(start, length).extract_from<To>(value.lo);
|
||||
else
|
||||
return Bitfield(start - 64, length).extract_from<To>(value.hi);
|
||||
}
|
||||
|
||||
constexpr bool operator==(const Bitfield& rhs) const
|
||||
{
|
||||
return (start == rhs.start) && (length == rhs.length);
|
||||
|
@ -62,4 +93,4 @@ struct Bitfield
|
|||
|
||||
int start;
|
||||
int length;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -119,36 +119,6 @@ public:
|
|||
finished:; // Done.
|
||||
}
|
||||
|
||||
/// Given the address properties, performs a lookup in the page
|
||||
/// table and returns the mapped entry. On a nullptr object being
|
||||
/// retrieved, a runtime_error is thrown (debug builds only).
|
||||
const Page& get_page(const AddressTy address) const
|
||||
{
|
||||
const size_t vdn = get_vdn(address);
|
||||
|
||||
#if DEBUG_BYTEBUS_RUNTIME_LOOKUP_CHECKS
|
||||
if (vdn >= table.size())
|
||||
throw std::runtime_error(str(boost::format("ByteBus lookup: no directory exists, address = 0x%08X.") % address));
|
||||
#endif
|
||||
|
||||
const auto& directory = table[vdn];
|
||||
const size_t page_index = directory.page_mask.extract_from(address);
|
||||
|
||||
#if DEBUG_BYTEBUS_RUNTIME_LOOKUP_CHECKS
|
||||
if (page_index >= directory.page_table.size())
|
||||
throw std::runtime_error(str(boost::format("ByteBus lookup: no page exists (probably culled), address = 0x%08X.") % address));
|
||||
#endif
|
||||
|
||||
const auto& page = directory.page_table[page_index];
|
||||
|
||||
#if DEBUG_BYTEBUS_RUNTIME_LOOKUP_CHECKS
|
||||
if (page.object == nullptr)
|
||||
throw std::runtime_error(str(boost::format("ByteBus lookup: nullptr object, address = 0x%08X.") % address));
|
||||
#endif
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
/// Read or write to a mapped object.
|
||||
ubyte read_ubyte(const BusContext context, const AddressTy address) const
|
||||
{
|
||||
|
@ -292,6 +262,36 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
/// Given the address properties, performs a lookup in the page
|
||||
/// table and returns the mapped entry. On a nullptr object being
|
||||
/// retrieved, a runtime_error is thrown (debug builds only).
|
||||
const Page& get_page(const AddressTy address) const
|
||||
{
|
||||
const size_t vdn = get_vdn(address);
|
||||
|
||||
#if DEBUG_BYTEBUS_RUNTIME_LOOKUP_CHECKS
|
||||
if (vdn >= table.size())
|
||||
throw std::runtime_error(str(boost::format("ByteBus lookup: no directory exists, address = 0x%08X.") % address));
|
||||
#endif
|
||||
|
||||
const auto& directory = table[vdn];
|
||||
const size_t page_index = directory.page_mask.extract_from(address);
|
||||
|
||||
#if DEBUG_BYTEBUS_RUNTIME_LOOKUP_CHECKS
|
||||
if (page_index >= directory.page_table.size())
|
||||
throw std::runtime_error(str(boost::format("ByteBus lookup: no page exists (probably culled), address = 0x%08X.") % address));
|
||||
#endif
|
||||
|
||||
const auto& page = directory.page_table[page_index];
|
||||
|
||||
#if DEBUG_BYTEBUS_RUNTIME_LOOKUP_CHECKS
|
||||
if (page.object == nullptr)
|
||||
throw std::runtime_error(str(boost::format("ByteBus lookup: nullptr object, address = 0x%08X.") % address));
|
||||
#endif
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
/// Returns the VDN (virtual directory number) for the address
|
||||
/// given using this Bus' context.
|
||||
size_t get_vdn(const AddressTy address) const
|
||||
|
|
|
@ -43,6 +43,29 @@ static_assert(sizeof(f32) * CHAR_BIT == 32, "f32 is not 32-bits long!");
|
|||
/// TODO: Look into boost/multiprecision (uint128_t) for endianess and compiler ordering?
|
||||
struct uqword
|
||||
{
|
||||
constexpr uqword() :
|
||||
lo(0),
|
||||
hi(0)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr uqword(const udword ud) :
|
||||
lo(ud),
|
||||
hi(ud)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr uqword(const udword lo, const udword hi) :
|
||||
lo(lo),
|
||||
hi(hi)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr uqword(const uword uw0, const uword uw1, const uword uw2, const uword uw3) :
|
||||
uw{uw0, uw1, uw2, uw3}
|
||||
{
|
||||
}
|
||||
|
||||
union {
|
||||
struct
|
||||
{
|
||||
|
@ -56,31 +79,8 @@ struct uqword
|
|||
ubyte ub[16];
|
||||
};
|
||||
|
||||
uqword() :
|
||||
lo(0),
|
||||
hi(0)
|
||||
{
|
||||
}
|
||||
|
||||
uqword(const udword ud) :
|
||||
lo(ud),
|
||||
hi(ud)
|
||||
{
|
||||
}
|
||||
|
||||
uqword(const udword lo, const udword hi) :
|
||||
lo(lo),
|
||||
hi(hi)
|
||||
{
|
||||
}
|
||||
|
||||
uqword(const uword uw0, const uword uw1, const uword uw2, const uword uw3) :
|
||||
uw{uw0, uw1, uw2, uw3}
|
||||
{
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& archive)
|
||||
void serialize(Archive & archive)
|
||||
{
|
||||
archive(
|
||||
CEREAL_NVP(hi),
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#include <algorithm>
|
||||
|
||||
#include "Controller/Ee/Gif/CGif.hpp"
|
||||
|
||||
#include "Core.hpp"
|
||||
|
@ -28,7 +30,6 @@ void CGif::handle_event(const ControllerEvent& event)
|
|||
|
||||
int CGif::time_to_ticks(const double time_us)
|
||||
{
|
||||
// TODO: find out for sure.
|
||||
int ticks = static_cast<int>(time_us / 1.0e6 * Constants::EE::EEBUS_CLK_SPEED * core->get_options().system_bias_gif);
|
||||
|
||||
if (ticks < 10)
|
||||
|
@ -46,6 +47,509 @@ int CGif::time_to_ticks(const double time_us)
|
|||
|
||||
int CGif::time_step(const int ticks_available)
|
||||
{
|
||||
// Not yet implemented.
|
||||
return ticks_available;
|
||||
}
|
||||
auto& r = core->get_resources();
|
||||
auto& ctrl = r.ee.gif.ctrl;
|
||||
auto& fifo_gif_path1 = r.fifo_gif_path1;
|
||||
auto& fifo_gif_path2 = r.fifo_gif_path2;
|
||||
auto& fifo_gif_path3 = r.fifo_gif_path3;
|
||||
|
||||
int cycles_consumed = 0;
|
||||
|
||||
// Prioritise paths by 1 -> 2 -> 3.
|
||||
// TODO: GIF code does not do path arbitration currently.
|
||||
// See EE Users Manual page 149.
|
||||
DmaFifoQueue<>* paths[3] = {&fifo_gif_path1, &fifo_gif_path2, &fifo_gif_path3};
|
||||
|
||||
for (auto& fifo : paths)
|
||||
{
|
||||
// A GIFtag is always read before processing data.
|
||||
if (!ctrl.transfer_started)
|
||||
{
|
||||
if (!fifo->has_read_available(NUMBER_BYTES_IN_QWORD))
|
||||
continue;
|
||||
|
||||
uqword data;
|
||||
fifo->read(reinterpret_cast<ubyte*>(&data), NUMBER_BYTES_IN_QWORD);
|
||||
|
||||
ctrl.transfer_started = true;
|
||||
|
||||
cycles_consumed = handle_tag(Giftag(data));
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (ctrl.tag.flg())
|
||||
{
|
||||
case Giftag::DataFormat::Packed:
|
||||
{
|
||||
if (!fifo->has_read_available(NUMBER_BYTES_IN_QWORD))
|
||||
continue;
|
||||
|
||||
uqword data;
|
||||
fifo->read(reinterpret_cast<ubyte*>(&data), NUMBER_BYTES_IN_QWORD);
|
||||
|
||||
cycles_consumed = handle_data_packed(data);
|
||||
}
|
||||
case Giftag::DataFormat::Reglist:
|
||||
{
|
||||
// TODO: maybe a weird situation can occur where there is only
|
||||
// 1 dword available which should be processed first before
|
||||
// attempting processing of other paths... Probably should make
|
||||
// this proper at some stage. Manual doesn't really say what to do...
|
||||
if (!fifo->has_read_available(NUMBER_BYTES_IN_QWORD))
|
||||
continue;
|
||||
|
||||
uqword data;
|
||||
fifo->read(reinterpret_cast<ubyte*>(&data), NUMBER_BYTES_IN_QWORD);
|
||||
|
||||
cycles_consumed = handle_data_reglist(data);
|
||||
}
|
||||
case Giftag::DataFormat::Image:
|
||||
case Giftag::DataFormat::Disabled:
|
||||
{
|
||||
if (!fifo->has_read_available(NUMBER_BYTES_IN_QWORD))
|
||||
continue;
|
||||
|
||||
uqword data;
|
||||
fifo->read(reinterpret_cast<ubyte*>(&data), NUMBER_BYTES_IN_QWORD);
|
||||
|
||||
cycles_consumed = handle_data_image(data);
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw std::runtime_error("Unknown GIF data processing method");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ctrl.tag.eop())
|
||||
{
|
||||
throw std::runtime_error("End of GS packet reached - what do we do?");
|
||||
}
|
||||
|
||||
// Do not process other paths if at least one path was successfully processed.
|
||||
if (cycles_consumed)
|
||||
break;
|
||||
}
|
||||
|
||||
// At least 1 cycle is consumed always if no paths had data available for processing.
|
||||
return std::max(cycles_consumed, 1);
|
||||
}
|
||||
|
||||
int CGif::handle_tag(const Giftag tag)
|
||||
{
|
||||
auto& r = core->get_resources();
|
||||
auto& ctrl = r.ee.gif.ctrl;
|
||||
|
||||
ctrl.tag = tag;
|
||||
ctrl.transfer_register_count = 0;
|
||||
ctrl.transfer_loop_count = 0;
|
||||
|
||||
// Initialise the RGBAQ.Q value on every tag read.
|
||||
// See EE Users manual page 153.
|
||||
ctrl.rgbaq_q = 1.0f;
|
||||
|
||||
// Some special processing when the NLOOP field is non-zero
|
||||
// "Values other than the EOP field are disregarded."
|
||||
// See EE Users Manual page 151.
|
||||
if (tag.nloop())
|
||||
{
|
||||
if (tag.flg() == Giftag::DataFormat::Packed)
|
||||
{
|
||||
// Output the packed PRIM data to GS if the PRE bit is set.
|
||||
if (tag.pre())
|
||||
{
|
||||
throw std::runtime_error("Sending GIFtag packed PRIM data not implemented yet");
|
||||
//uhword prim_value = tag.prim();
|
||||
//auto& prim = r.gs.prim;
|
||||
//prim.write_udword(prim_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1 cycle consumed for the tag, and 1 cycle for the handling of the PRIM data
|
||||
// (always done even if it's not used). See the descriptions of the transfer
|
||||
// modes in the EE Users Manual page 153, 159, 160.
|
||||
return 2;
|
||||
}
|
||||
|
||||
int CGif::handle_data_packed(const uqword data)
|
||||
{
|
||||
auto& r = core->get_resources();
|
||||
auto& ctrl = r.ee.gif.ctrl;
|
||||
|
||||
// Get the register descirptor.
|
||||
size_t register_descirptor = ctrl.tag.regs(ctrl.transfer_register_count);
|
||||
|
||||
// Process the data given, and write to the GS register.
|
||||
// See EE Users Manual page 152 onwards for processing details.
|
||||
switch (register_descirptor)
|
||||
{
|
||||
case 0x0:
|
||||
{
|
||||
// PRIM
|
||||
constexpr Bitfield PRIM = Bitfield(0, 11);
|
||||
|
||||
udword prim_value = PRIM.extract_from<udword>(data);
|
||||
|
||||
//auto& prim = r.gs.prim;
|
||||
//prim.write_udword(prim_value);
|
||||
break;
|
||||
}
|
||||
case 0x1:
|
||||
{
|
||||
// RGBAQ
|
||||
constexpr Bitfield R = Bitfield(0, 8);
|
||||
constexpr Bitfield G = Bitfield(32, 8);
|
||||
constexpr Bitfield B = Bitfield(64, 8);
|
||||
constexpr Bitfield A = Bitfield(96, 8);
|
||||
|
||||
udword r_value = R.extract_from<udword>(data);
|
||||
udword g_value = G.extract_from<udword>(data);
|
||||
udword b_value = B.extract_from<udword>(data);
|
||||
udword a_value = A.extract_from<udword>(data);
|
||||
udword q_value = *reinterpret_cast<udword*>(&ctrl.rgbaq_q);
|
||||
|
||||
//auto& rgbaq = r.gs.rgbaq;
|
||||
//rgbaq.write_field(GsRegister_Rgbaq::R, r_value);
|
||||
//rgbaq.write_field(GsRegister_Rgbaq::G, g_value);
|
||||
//rgbaq.write_field(GsRegister_Rgbaq::B, b_value);
|
||||
//rgbaq.write_field(GsRegister_Rgbaq::A, a_value);
|
||||
//rgbaq.write_field(GsRegister_Rgbaq::Q, q_value);
|
||||
break;
|
||||
}
|
||||
case 0x2:
|
||||
{
|
||||
// ST
|
||||
constexpr Bitfield S = Bitfield(0, 32);
|
||||
constexpr Bitfield T = Bitfield(32, 32);
|
||||
constexpr Bitfield Q = Bitfield(64, 32);
|
||||
|
||||
// Store the Q value internally, used when handling RGBAQ register descriptor (see above).
|
||||
uword q_value = Q.extract_from<uword>(data);
|
||||
ctrl.rgbaq_q = *reinterpret_cast<f32*>(&q_value);
|
||||
|
||||
udword s_value = S.extract_from<udword>(data);
|
||||
udword t_value = T.extract_from<udword>(data);
|
||||
|
||||
//auto& st = r.gs.st;
|
||||
//st.write_field(GsRegister_St::S, s_value);
|
||||
//st.write_field(GsRegister_St::T, t_value);
|
||||
break;
|
||||
}
|
||||
case 0x3:
|
||||
{
|
||||
// UV
|
||||
constexpr Bitfield U = Bitfield(0, 14);
|
||||
constexpr Bitfield V = Bitfield(32, 14);
|
||||
|
||||
udword u_value = U.extract_from<udword>(data);
|
||||
udword v_value = V.extract_from<udword>(data);
|
||||
|
||||
//auto& uv = r.gs.uv;
|
||||
//uv.write_field(GsRegister_Uv::U, u_value);
|
||||
//uv.write_field(GsRegister_Uv::V, v_value);
|
||||
break;
|
||||
}
|
||||
case 0x4:
|
||||
{
|
||||
// XYZF2/3
|
||||
constexpr Bitfield X = Bitfield(0, 16);
|
||||
constexpr Bitfield Y = Bitfield(32, 16);
|
||||
constexpr Bitfield Z = Bitfield(68, 24);
|
||||
constexpr Bitfield F = Bitfield(100, 8);
|
||||
constexpr Bitfield ADC = Bitfield(111, 1);
|
||||
|
||||
udword x_value = X.extract_from<udword>(data);
|
||||
udword y_value = Y.extract_from<udword>(data);
|
||||
udword z_value = Z.extract_from<udword>(data);
|
||||
udword f_value = F.extract_from<udword>(data);
|
||||
udword adc_value = ADC.extract_from<udword>(data);
|
||||
|
||||
//GsRegister_Xyzf * xyzf = adc_value ? &r.gs.xyzf2 : &r.gs.xyzf3;
|
||||
//xyzf->write_field(GsRegister_Xyzf::X, x_value);
|
||||
//xyzf->write_field(GsRegister_Xyzf::Y, y_value);
|
||||
//xyzf->write_field(GsRegister_Xyzf::Z, z_value);
|
||||
//xyzf->write_field(GsRegister_Xyzf::F, f_value);
|
||||
break;
|
||||
}
|
||||
case 0x5:
|
||||
{
|
||||
// XYZ2/3
|
||||
constexpr Bitfield X = Bitfield(0, 16);
|
||||
constexpr Bitfield Y = Bitfield(32, 16);
|
||||
constexpr Bitfield Z = Bitfield(64, 32);
|
||||
constexpr Bitfield ADC = Bitfield(111, 1);
|
||||
|
||||
udword x_value = X.extract_from<udword>(data);
|
||||
udword y_value = Y.extract_from<udword>(data);
|
||||
udword z_value = Z.extract_from<udword>(data);
|
||||
udword adc_value = ADC.extract_from<udword>(data);
|
||||
|
||||
//GsRegister_Xyzf * xyz = adc_value ? &r.gs.xyz2 : &r.gs.xyz3;
|
||||
//xyz->write_field(GsRegister_Xyzf::X, x_value);
|
||||
//xyz->write_field(GsRegister_Xyzf::Y, y_value);
|
||||
//xyz->write_field(GsRegister_Xyzf::Z, z_value);
|
||||
break;
|
||||
}
|
||||
case 0x6:
|
||||
{
|
||||
// TEX0_1
|
||||
//auto& tex0_1 = r.gs.tex0_1;
|
||||
//tex0_1.write_udword(data.lo);
|
||||
break;
|
||||
}
|
||||
case 0x7:
|
||||
{
|
||||
// TEX0_2
|
||||
//auto& tex0_2 = r.gs.tex0_2;
|
||||
//tex0_2.write_udword(data.lo);
|
||||
break;
|
||||
}
|
||||
case 0x8:
|
||||
{
|
||||
// CLAMP_1
|
||||
//auto& clamp_1 = r.gs.clamp_1;
|
||||
//clamp_1.write_udword(data.lo);
|
||||
break;
|
||||
}
|
||||
case 0x9:
|
||||
{
|
||||
// CLAMP_2
|
||||
//auto& clamp_2 = r.gs.clamp_2;
|
||||
//clamp_2.write_udword(data.lo);
|
||||
break;
|
||||
}
|
||||
case 0xA:
|
||||
{
|
||||
// FOG
|
||||
// TODO: Not sure if we need to extract the integral part? Wording a bit unclear.
|
||||
// See EE Users Manual page 155.
|
||||
constexpr Bitfield F = Bitfield(100, 8);
|
||||
|
||||
udword f_value = F.extract_from<udword>(data);
|
||||
|
||||
//auto& fog = r.gs.fog;
|
||||
//fog.write_field(GsRegister_Fog::F, f_value);
|
||||
break;
|
||||
}
|
||||
case 0xB:
|
||||
{
|
||||
// Reserved
|
||||
throw std::runtime_error("Reserved register descriptor read from GIFtag");
|
||||
}
|
||||
case 0xC:
|
||||
{
|
||||
// XYZF3
|
||||
//auto& xyzf3 = r.gs.xyzf3;
|
||||
//xyzf3.write_udword(data.lo);
|
||||
break;
|
||||
}
|
||||
case 0xD:
|
||||
{
|
||||
// XYZ3
|
||||
//auto& xyz3 = r.gs.xyz3;
|
||||
//xyz3.write_udword(data.lo);
|
||||
break;
|
||||
}
|
||||
case 0xE:
|
||||
{
|
||||
// A+D (packed address + data)
|
||||
constexpr Bitfield ADDR = Bitfield(64, 8);
|
||||
|
||||
uptr addr_value = ADDR.extract_from<uptr>(data);
|
||||
udword data_value = data.lo;
|
||||
|
||||
//r.gs.bus.write_udword(BusContext::Gif, addr_value, data_value);
|
||||
break;
|
||||
}
|
||||
case 0xF:
|
||||
{
|
||||
// NOP
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw std::runtime_error("Unknown register descriptor given");
|
||||
}
|
||||
}
|
||||
|
||||
ctrl.transfer_register_count += 1;
|
||||
if (ctrl.transfer_register_count == ctrl.tag.nreg())
|
||||
{
|
||||
ctrl.transfer_register_count = 0;
|
||||
ctrl.transfer_loop_count += 1;
|
||||
}
|
||||
|
||||
if (ctrl.transfer_loop_count == ctrl.tag.nloop())
|
||||
ctrl.transfer_started = false;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int CGif::handle_data_reglist(const uqword data)
|
||||
{
|
||||
auto& r = core->get_resources();
|
||||
auto& ctrl = r.ee.gif.ctrl;
|
||||
|
||||
udword datas[] = { data.lo, data.hi };
|
||||
for (auto& data : datas)
|
||||
{
|
||||
// Get the register descirptor.
|
||||
size_t register_descirptor = ctrl.tag.regs(ctrl.transfer_register_count);
|
||||
|
||||
// Write data directly to the GS register.
|
||||
// See EE Users Manual page 159 onwards for processing details.
|
||||
switch (register_descirptor)
|
||||
{
|
||||
case 0x0:
|
||||
{
|
||||
// PRIM
|
||||
//auto& prim = r.gs.prim;
|
||||
//prim.write_udword(data);
|
||||
break;
|
||||
}
|
||||
case 0x1:
|
||||
{
|
||||
// RGBAQ
|
||||
//auto& rgbaq = r.gs.rgbaq;
|
||||
//rgbaq.write_udword(data);
|
||||
break;
|
||||
}
|
||||
case 0x2:
|
||||
{
|
||||
// ST
|
||||
//auto& st = r.gs.st;
|
||||
//st.write_udword(data);
|
||||
break;
|
||||
}
|
||||
case 0x3:
|
||||
{
|
||||
// UV
|
||||
//auto& uv = r.gs.uv;
|
||||
//uv.write_udword(data);
|
||||
break;
|
||||
}
|
||||
case 0x4:
|
||||
{
|
||||
// XYZF2
|
||||
//auto& xyzf2 = r.gs.xyzf2;
|
||||
//xyzf2.write_udword(data);
|
||||
break;
|
||||
}
|
||||
case 0x5:
|
||||
{
|
||||
// XYZ2
|
||||
//auto& xyz2 = r.gs.xyz2;
|
||||
//xyz2.write_udword(data);
|
||||
break;
|
||||
}
|
||||
case 0x6:
|
||||
{
|
||||
// TEX0_1
|
||||
//auto& tex0_1 = r.gs.tex0_1;
|
||||
//tex0_1.write_udword(data);
|
||||
break;
|
||||
}
|
||||
case 0x7:
|
||||
{
|
||||
// TEX0_2
|
||||
//auto& tex0_2 = r.gs.tex0_2;
|
||||
//tex0_2.write_udword(data);
|
||||
break;
|
||||
}
|
||||
case 0x8:
|
||||
{
|
||||
// CLAMP_1
|
||||
//auto& clamp_1 = r.gs.clamp_1;
|
||||
//clamp_1.write_udword(data);
|
||||
break;
|
||||
}
|
||||
case 0x9:
|
||||
{
|
||||
// CLAMP_2
|
||||
//auto& clamp_2 = r.gs.clamp_2;
|
||||
//clamp_2.write_udword(data);
|
||||
break;
|
||||
}
|
||||
case 0xA:
|
||||
{
|
||||
// FOG
|
||||
//auto& fog = r.gs.fog;
|
||||
//fog.write_udword(data);
|
||||
break;
|
||||
}
|
||||
case 0xB:
|
||||
{
|
||||
// Reserved
|
||||
throw std::runtime_error("Reserved register descriptor read from GIFtag");
|
||||
}
|
||||
case 0xC:
|
||||
{
|
||||
// XYZF3
|
||||
//auto& xyzf3 = r.gs.xyzf3;
|
||||
//xyzf3.write_udword(data);
|
||||
break;
|
||||
}
|
||||
case 0xD:
|
||||
{
|
||||
// XYZ3
|
||||
//auto& xyz3 = r.gs.xyz3;
|
||||
//xyz3.write_udword(data);
|
||||
break;
|
||||
}
|
||||
case 0xE:
|
||||
{
|
||||
// A+D (packed address + data)
|
||||
// No-op for REGLIST mode. See EE Users Manual page 159.
|
||||
break;
|
||||
}
|
||||
case 0xF:
|
||||
{
|
||||
// NOP
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw std::runtime_error("Unknown register descriptor given");
|
||||
}
|
||||
}
|
||||
|
||||
ctrl.transfer_register_count += 1;
|
||||
if (ctrl.transfer_register_count == ctrl.tag.nreg())
|
||||
{
|
||||
ctrl.transfer_register_count = 0;
|
||||
ctrl.transfer_loop_count += 1;
|
||||
}
|
||||
|
||||
if (ctrl.transfer_loop_count == ctrl.tag.nloop())
|
||||
{
|
||||
ctrl.transfer_started = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Although not stated, I'm gussing the GIF consumes an extra cycle even when
|
||||
// NREGS is odd (ie: upper 64-bits of last qword of data discarded).
|
||||
return 2;
|
||||
}
|
||||
|
||||
int CGif::handle_data_image(const uqword data)
|
||||
{
|
||||
auto& r = core->get_resources();
|
||||
auto& ctrl = r.ee.gif.ctrl;
|
||||
//auto& hwreg = r.gs.hwreg;
|
||||
|
||||
udword datas[] = { data.lo, data.hi };
|
||||
for (auto& data : datas)
|
||||
{
|
||||
// Data is output sequentially to the HWREG transfer register of the GS.
|
||||
// See EE Users manual page 160.
|
||||
//hwreg.write_udword(data);
|
||||
}
|
||||
|
||||
ctrl.transfer_loop_count += 1;
|
||||
if (ctrl.transfer_loop_count == ctrl.tag.nloop())
|
||||
ctrl.transfer_started = false;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "Controller/CController.hpp"
|
||||
#include <utility>
|
||||
|
||||
#include "Common/Types/Primitive.hpp"
|
||||
#include "Controller/CController.hpp"
|
||||
#include "Resources/Ee/Gif/Giftag.hpp"
|
||||
|
||||
/// GIF interface, controls data between the EE (EE Core, VPU1) to the GS.
|
||||
class CGif : public CController
|
||||
{
|
||||
public:
|
||||
|
@ -13,4 +18,21 @@ public:
|
|||
int time_to_ticks(const double time_us);
|
||||
|
||||
int time_step(const int ticks_available);
|
||||
|
||||
/// Handle the initial processing of a GIFtag. For tag details, see EE Users
|
||||
/// manual page 151.
|
||||
/// Note: consumes 2 cycles when the PRE bit is set and FLG is set to packed
|
||||
/// mode. (PRE handling is considered a single cycle.) See EE Users manual
|
||||
/// page 153 about this.
|
||||
int handle_tag(const Giftag tag);
|
||||
|
||||
int handle_data_packed(const uqword data);
|
||||
|
||||
/// Handle a GS primitive using the REGLIST processing method.
|
||||
/// Note: consumes 2 cycles (processes both 64-bit data strings at the same time).
|
||||
int handle_data_reglist(const uqword data);
|
||||
|
||||
int handle_data_image(const uqword data);
|
||||
|
||||
std::pair<size_t, SizedDwordRegister*> get_register_descriptor() const;
|
||||
};
|
||||
|
|
10
liborbum/src/Resources/Ee/Gif/GifRegisters.cpp
Normal file
10
liborbum/src/Resources/Ee/Gif/GifRegisters.cpp
Normal file
|
@ -0,0 +1,10 @@
|
|||
#include "Resources/Ee/Gif/GifRegisters.hpp"
|
||||
|
||||
GifRegister_Ctrl::GifRegister_Ctrl() :
|
||||
transfer_started(false),
|
||||
tag(),
|
||||
transfer_register_count(0),
|
||||
transfer_loop_count(0),
|
||||
rgbaq_q(1.0f)
|
||||
{
|
||||
}
|
113
liborbum/src/Resources/Ee/Gif/GifRegisters.hpp
Normal file
113
liborbum/src/Resources/Ee/Gif/GifRegisters.hpp
Normal file
|
@ -0,0 +1,113 @@
|
|||
#pragma once
|
||||
|
||||
#include <cereal/cereal.hpp>
|
||||
#include <cereal/types/polymorphic.hpp>
|
||||
|
||||
#include "Common/Types/Bitfield.hpp"
|
||||
#include "Common/Types/Register/SizedWordRegister.hpp"
|
||||
|
||||
#include "Resources/Ee/Gif/Giftag.hpp"
|
||||
|
||||
class GifRegister_Ctrl : public SizedWordRegister
|
||||
{
|
||||
public:
|
||||
static constexpr Bitfield RST = Bitfield(0, 1);
|
||||
static constexpr Bitfield PSE = Bitfield(3, 1);
|
||||
|
||||
GifRegister_Ctrl();
|
||||
|
||||
/// Indicates whether the GIF is currently processing GS primitive
|
||||
/// data (otherwise read a tag). Reset upon finishing a GS primitive.
|
||||
bool transfer_started;
|
||||
|
||||
/// Current transfer tag being processed.
|
||||
Giftag tag;
|
||||
|
||||
/// Number of registers / loops processed for the current tag.
|
||||
size_t transfer_register_count;
|
||||
size_t transfer_loop_count;
|
||||
|
||||
/// Special holding area for the RGBAQ.Q value, set when the ST
|
||||
/// register descriptor is written to. See EE Users manual page 153/154.
|
||||
f32 rgbaq_q;
|
||||
|
||||
public:
|
||||
template<class Archive>
|
||||
void serialize(Archive & archive)
|
||||
{
|
||||
archive(
|
||||
cereal::base_class<SizedWordRegister>(this),
|
||||
CEREAL_NVP(transfer_started),
|
||||
CEREAL_NVP(tag),
|
||||
CEREAL_NVP(transfer_register_count),
|
||||
CEREAL_NVP(transfer_loop_count),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
class GifRegister_Mode : public SizedWordRegister
|
||||
{
|
||||
public:
|
||||
static constexpr Bitfield M3R = Bitfield(0, 1);
|
||||
static constexpr Bitfield IMT = Bitfield(2, 1);
|
||||
};
|
||||
|
||||
class GifRegister_Stat : public SizedWordRegister
|
||||
{
|
||||
public:
|
||||
static constexpr Bitfield M3R = Bitfield(0, 1);
|
||||
static constexpr Bitfield M3P = Bitfield(1, 1);
|
||||
static constexpr Bitfield IMT = Bitfield(2, 1);
|
||||
static constexpr Bitfield PSE = Bitfield(3, 1);
|
||||
static constexpr Bitfield IP3 = Bitfield(5, 1);
|
||||
static constexpr Bitfield P3Q = Bitfield(6, 1);
|
||||
static constexpr Bitfield P2Q = Bitfield(7, 1);
|
||||
static constexpr Bitfield P1Q = Bitfield(8, 1);
|
||||
static constexpr Bitfield OPH = Bitfield(9, 1);
|
||||
static constexpr Bitfield APATH = Bitfield(10, 2);
|
||||
static constexpr Bitfield DIR = Bitfield(12, 1);
|
||||
static constexpr Bitfield FQC = Bitfield(24, 5);
|
||||
};
|
||||
|
||||
class GifRegister_Tag0 : public SizedWordRegister
|
||||
{
|
||||
public:
|
||||
static constexpr Bitfield NLOOP = Bitfield(0, 15);
|
||||
static constexpr Bitfield EOP = Bitfield(15, 1);
|
||||
static constexpr Bitfield TAG = Bitfield(16, 16);
|
||||
};
|
||||
|
||||
class GifRegister_Tag1 : public SizedWordRegister
|
||||
{
|
||||
public:
|
||||
static constexpr Bitfield TAG = Bitfield(0, 14);
|
||||
static constexpr Bitfield PRE = Bitfield(14, 1);
|
||||
static constexpr Bitfield PRIM = Bitfield(15, 11);
|
||||
static constexpr Bitfield FLG = Bitfield(26, 2);
|
||||
static constexpr Bitfield NREG = Bitfield(28, 4);
|
||||
};
|
||||
|
||||
class GifRegister_Tag2 : public SizedWordRegister
|
||||
{
|
||||
public:
|
||||
static constexpr Bitfield TAG = Bitfield(0, 32);
|
||||
};
|
||||
|
||||
class GifRegister_Tag3 : public SizedWordRegister
|
||||
{
|
||||
public:
|
||||
static constexpr Bitfield TAG = Bitfield(0, 32);
|
||||
};
|
||||
|
||||
class GifRegister_Cnt : public SizedWordRegister
|
||||
{
|
||||
public:
|
||||
static constexpr Bitfield P3CNT = Bitfield(0, 15);
|
||||
};
|
||||
|
||||
class GifRegister_P3tag : public SizedWordRegister
|
||||
{
|
||||
public:
|
||||
static constexpr Bitfield LOOPCNT = Bitfield(0, 15);
|
||||
static constexpr Bitfield EOP = Bitfield(15, 1);
|
||||
};
|
193
liborbum/src/Resources/Ee/Gif/Giftag.hpp
Normal file
193
liborbum/src/Resources/Ee/Gif/Giftag.hpp
Normal file
|
@ -0,0 +1,193 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "Common/Types/Bitfield.hpp"
|
||||
#include "Common/Types/Primitive.hpp"
|
||||
|
||||
class Giftag
|
||||
{
|
||||
public:
|
||||
static constexpr Bitfield NLOOP = Bitfield(0, 15);
|
||||
static constexpr Bitfield EOP = Bitfield(15, 1);
|
||||
static constexpr Bitfield PRE = Bitfield(46, 1);
|
||||
static constexpr Bitfield PRIM = Bitfield(47, 11);
|
||||
static constexpr Bitfield FLG = Bitfield(58, 2);
|
||||
static constexpr Bitfield NREG = Bitfield(60, 4);
|
||||
static constexpr Bitfield REGS0 = Bitfield(64, 4);
|
||||
static constexpr Bitfield REGS1 = Bitfield(68, 4);
|
||||
static constexpr Bitfield REGS2 = Bitfield(72, 4);
|
||||
static constexpr Bitfield REGS3 = Bitfield(76, 4);
|
||||
static constexpr Bitfield REGS4 = Bitfield(80, 4);
|
||||
static constexpr Bitfield REGS5 = Bitfield(84, 4);
|
||||
static constexpr Bitfield REGS6 = Bitfield(88, 4);
|
||||
static constexpr Bitfield REGS7 = Bitfield(92, 4);
|
||||
static constexpr Bitfield REGS8 = Bitfield(96, 4);
|
||||
static constexpr Bitfield REGS9 = Bitfield(100, 4);
|
||||
static constexpr Bitfield REGS10 = Bitfield(104, 4);
|
||||
static constexpr Bitfield REGS11 = Bitfield(108, 4);
|
||||
static constexpr Bitfield REGS12 = Bitfield(112, 4);
|
||||
static constexpr Bitfield REGS13 = Bitfield(116, 4);
|
||||
static constexpr Bitfield REGS14 = Bitfield(120, 4);
|
||||
static constexpr Bitfield REGS15 = Bitfield(124, 4);
|
||||
|
||||
enum DataFormat
|
||||
{
|
||||
Packed,
|
||||
Reglist,
|
||||
Image,
|
||||
Disabled // Disabled means the same operation mode as Image.
|
||||
};
|
||||
|
||||
Giftag() = default;
|
||||
|
||||
Giftag(const uqword tag) :
|
||||
tag(tag)
|
||||
{
|
||||
}
|
||||
|
||||
size_t nloop() const
|
||||
{
|
||||
return NLOOP.extract_from<size_t>(tag);
|
||||
}
|
||||
|
||||
bool eop() const
|
||||
{
|
||||
return EOP.extract_from<int>(tag) > 0;
|
||||
}
|
||||
|
||||
bool pre() const
|
||||
{
|
||||
return EOP.extract_from<int>(tag) > 0;
|
||||
}
|
||||
|
||||
udword prim() const
|
||||
{
|
||||
// The PRIM register is dword in size.
|
||||
return PRIM.extract_from<udword>(tag);
|
||||
}
|
||||
|
||||
DataFormat flg() const
|
||||
{
|
||||
switch (FLG.extract_from<int>(tag))
|
||||
{
|
||||
case 0:
|
||||
return Packed;
|
||||
case 1:
|
||||
return Reglist;
|
||||
case 2:
|
||||
return Image;
|
||||
case 3:
|
||||
return Disabled;
|
||||
default:
|
||||
throw std::runtime_error("Unknown Giftag data format");
|
||||
}
|
||||
}
|
||||
|
||||
size_t nreg() const
|
||||
{
|
||||
size_t nreg = NREG.extract_from<size_t>(tag);
|
||||
|
||||
if (nreg >= 16)
|
||||
throw std::runtime_error("Unknown nreg value");
|
||||
|
||||
return (nreg == 0) ? 16 : nreg;
|
||||
}
|
||||
|
||||
size_t regs0() const
|
||||
{
|
||||
return REGS0.extract_from<size_t>(tag);
|
||||
}
|
||||
|
||||
size_t regs1() const
|
||||
{
|
||||
return REGS1.extract_from<size_t>(tag);
|
||||
}
|
||||
|
||||
size_t regs2() const
|
||||
{
|
||||
return REGS2.extract_from<size_t>(tag);
|
||||
}
|
||||
|
||||
size_t regs3() const
|
||||
{
|
||||
return REGS3.extract_from<size_t>(tag);
|
||||
}
|
||||
|
||||
size_t regs4() const
|
||||
{
|
||||
return REGS4.extract_from<size_t>(tag);
|
||||
}
|
||||
|
||||
size_t regs5() const
|
||||
{
|
||||
return REGS5.extract_from<size_t>(tag);
|
||||
}
|
||||
|
||||
size_t regs6() const
|
||||
{
|
||||
return REGS6.extract_from<size_t>(tag);
|
||||
}
|
||||
|
||||
size_t regs7() const
|
||||
{
|
||||
return REGS7.extract_from<size_t>(tag);
|
||||
}
|
||||
|
||||
size_t regs8() const
|
||||
{
|
||||
return REGS8.extract_from<size_t>(tag);
|
||||
}
|
||||
|
||||
size_t regs9() const
|
||||
{
|
||||
return REGS9.extract_from<size_t>(tag);
|
||||
}
|
||||
|
||||
size_t regs10() const
|
||||
{
|
||||
return REGS10.extract_from<size_t>(tag);
|
||||
}
|
||||
|
||||
size_t regs11() const
|
||||
{
|
||||
return REGS11.extract_from<size_t>(tag);
|
||||
}
|
||||
|
||||
size_t regs12() const
|
||||
{
|
||||
return REGS12.extract_from<size_t>(tag);
|
||||
}
|
||||
|
||||
size_t regs13() const
|
||||
{
|
||||
return REGS13.extract_from<size_t>(tag);
|
||||
}
|
||||
|
||||
size_t regs14() const
|
||||
{
|
||||
return REGS14.extract_from<size_t>(tag);
|
||||
}
|
||||
|
||||
size_t regs15() const
|
||||
{
|
||||
return REGS15.extract_from<size_t>(tag);
|
||||
}
|
||||
|
||||
size_t regs(const size_t descriptor_index) const
|
||||
{
|
||||
using RegisterDescriptor = size_t(Giftag::*)() const;
|
||||
static const RegisterDescriptor register_descriptors[] = {
|
||||
&Giftag::regs0, &Giftag::regs1, &Giftag::regs2, &Giftag::regs3,
|
||||
&Giftag::regs4, &Giftag::regs5, &Giftag::regs6, &Giftag::regs7,
|
||||
&Giftag::regs8, &Giftag::regs9, &Giftag::regs10, &Giftag::regs11,
|
||||
&Giftag::regs12, &Giftag::regs13, &Giftag::regs14, &Giftag::regs15
|
||||
};
|
||||
|
||||
auto fn = register_descriptors[descriptor_index];
|
||||
return (this->*fn)();
|
||||
}
|
||||
|
||||
private:
|
||||
uqword tag;
|
||||
};
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "Common/Types/Memory/ArrayByteMemory.hpp"
|
||||
#include "Common/Types/Register/SizedWordRegister.hpp"
|
||||
#include "Resources/Ee/Gif/GifRegisters.hpp"
|
||||
|
||||
class RGif
|
||||
{
|
||||
|
@ -11,7 +12,7 @@ public:
|
|||
RGif();
|
||||
|
||||
/// GIF memory mapped registers. See page 21 of EE Users Manual.
|
||||
SizedWordRegister ctrl;
|
||||
GifRegister_Ctrl ctrl;
|
||||
SizedWordRegister mode;
|
||||
SizedWordRegister stat;
|
||||
ArrayByteMemory memory_3030;
|
||||
|
|
|
@ -119,7 +119,7 @@ void initialise_ee_dmac(RResources* r)
|
|||
// Init DMA FIFO queues.
|
||||
r->ee.dmac.channels[0].dma_fifo_queue = &r->fifo_vif0;
|
||||
r->ee.dmac.channels[1].dma_fifo_queue = &r->fifo_vif1;
|
||||
r->ee.dmac.channels[2].dma_fifo_queue = &r->fifo_gif;
|
||||
r->ee.dmac.channels[2].dma_fifo_queue = &r->fifo_gif_path3;
|
||||
r->ee.dmac.channels[3].dma_fifo_queue = &r->fifo_fromipu;
|
||||
r->ee.dmac.channels[4].dma_fifo_queue = &r->fifo_toipu;
|
||||
r->ee.dmac.channels[5].dma_fifo_queue = &r->fifo_sif0;
|
||||
|
@ -419,7 +419,7 @@ void initialise_ee(RResources* r)
|
|||
// FIFO Registers.
|
||||
r->ee.bus.map(0x10004000, &r->fifo_vif0);
|
||||
r->ee.bus.map(0x10005000, &r->fifo_vif1);
|
||||
r->ee.bus.map(0x10006000, &r->fifo_gif);
|
||||
r->ee.bus.map(0x10006000, &r->fifo_gif_path3);
|
||||
r->ee.bus.map(0x10007000, &r->fifo_fromipu);
|
||||
r->ee.bus.map(0x10007010, &r->fifo_toipu);
|
||||
|
||||
|
|
|
@ -54,10 +54,10 @@ public:
|
|||
SbusRegister_F300 sbus_f300; // TODO: related to psx sif2/gpu? Investigate (see PCSX2).
|
||||
SizedWordRegister sbus_f380;
|
||||
|
||||
/// FIFO Queue registers, attached to both the EE and IOP DMAC channels.
|
||||
/// DMA FIFO queues, attached to both the EE and IOP DMAC channels.
|
||||
DmaFifoQueue<> fifo_vif0;
|
||||
DmaFifoQueue<> fifo_vif1;
|
||||
DmaFifoQueue<> fifo_gif;
|
||||
DmaFifoQueue<> fifo_gif_path3;
|
||||
DmaFifoQueue<> fifo_fromipu;
|
||||
DmaFifoQueue<> fifo_toipu;
|
||||
DmaFifoQueue<> fifo_sif0;
|
||||
|
@ -74,6 +74,10 @@ public:
|
|||
DmaFifoQueue<> fifo_fromsio2;
|
||||
DmaFifoQueue<> fifo_tosio2;
|
||||
|
||||
// GIF FIFO queues (alternate paths).
|
||||
DmaFifoQueue<> fifo_gif_path1;
|
||||
DmaFifoQueue<> fifo_gif_path2;
|
||||
|
||||
public:
|
||||
template <class Archive>
|
||||
void serialize(Archive& archive)
|
||||
|
@ -99,7 +103,7 @@ public:
|
|||
CEREAL_NVP(sbus_f380),
|
||||
CEREAL_NVP(fifo_vif0),
|
||||
CEREAL_NVP(fifo_vif1),
|
||||
CEREAL_NVP(fifo_gif),
|
||||
CEREAL_NVP(fifo_gif_path3),
|
||||
CEREAL_NVP(fifo_fromipu),
|
||||
CEREAL_NVP(fifo_toipu),
|
||||
CEREAL_NVP(fifo_sif0),
|
||||
|
|
Loading…
Reference in a new issue