pica: Fix irq request register behaviour (#1216)

This commit is contained in:
PabloMK7 2025-07-10 23:01:53 +01:00 committed by GitHub
parent df134acefe
commit d8bef418a7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 46 additions and 11 deletions

View File

@ -54,6 +54,10 @@ PicaCore::PicaCore(Memory::MemorySystem& memory_, std::shared_ptr<DebugContext>
PicaCore::~PicaCore() = default;
void PicaCore::InitializeRegs() {
// Values initialized by GSP
regs.internal.irq_autostop = 1;
regs.internal.irq_mask = 0xFFFFFFF0;
auto& framebuffer_top = regs.framebuffer_config[0];
auto& framebuffer_sub = regs.framebuffer_config[1];
@ -100,7 +104,11 @@ void PicaCore::ProcessCmdList(PAddr list, u32 size, bool ignore_list) {
const u8* head = memory.GetPhysicalPointer(list);
cmd_list.Reset(list, head, size);
bool stop_requested = false;
while (cmd_list.current_index < cmd_list.length) {
if (stop_requested) [[unlikely]] {
break;
}
// Align read pointer to 8 bytes
if (cmd_list.current_index % 2 != 0) {
cmd_list.current_index++;
@ -111,18 +119,26 @@ void PicaCore::ProcessCmdList(PAddr list, u32 size, bool ignore_list) {
const CommandHeader header{cmd_list.head[cmd_list.current_index++]};
// Write to the requested PICA register.
WriteInternalReg(header.cmd_id, value, header.parameter_mask);
WriteInternalReg(header.cmd_id, value, header.parameter_mask, stop_requested);
// Write any extra paramters as well.
for (u32 i = 0; i < header.extra_data_length; ++i) {
if (stop_requested) [[unlikely]] {
break;
}
const u32 cmd = header.cmd_id + (header.group_commands ? i + 1 : 0);
const u32 extra_value = cmd_list.head[cmd_list.current_index++];
WriteInternalReg(cmd, extra_value, header.parameter_mask);
WriteInternalReg(cmd, extra_value, header.parameter_mask, stop_requested);
}
}
}
void PicaCore::WriteInternalReg(u32 id, u32 value, u32 mask) {
static bool any_byte_match(u32 a, u32 b) {
return ((a & 0xFF) == (b & 0xFF)) || (((a >> 8) & 0xFF) == ((b >> 8) & 0xFF)) ||
(((a >> 16) & 0xFF) == ((b >> 16) & 0xFF)) || (((a >> 24) & 0xFF) == ((b >> 24) & 0xFF));
}
void PicaCore::WriteInternalReg(u32 id, u32 value, u32 mask, bool& stop_requested) {
if (id >= RegsInternal::NUM_REGS) {
LOG_ERROR(
HW_GPU,
@ -153,8 +169,15 @@ void PicaCore::WriteInternalReg(u32 id, u32 value, u32 mask) {
switch (id) {
// Trigger IRQ
case PICA_REG_INDEX(trigger_irq):
signal_interrupt(Service::GSP::InterruptId::P3D);
case PICA_REG_INDEX(irq_request):
// TODO(PabloMK7): This logic is not fully accurate, but close enough:
// https://problemkaputt.de/gbatek-3ds-gpu-internal-registers-finalize-interrupt-registers.htm
if (any_byte_match(regs.internal.reg_array[id], regs.internal.irq_compare)) [[likely]] {
signal_interrupt(Service::GSP::InterruptId::P3D);
if (regs.internal.irq_autostop) [[likely]] {
stop_requested = true;
}
}
break;
case PICA_REG_INDEX(pipeline.triangle_topology):

View File

@ -43,7 +43,7 @@ public:
private:
void InitializeRegs();
void WriteInternalReg(u32 id, u32 value, u32 mask);
void WriteInternalReg(u32 id, u32 value, u32 mask, bool& stop_requested);
void SubmitImmediate(u32 data);

View File

@ -1,4 +1,4 @@
// Copyright 2015 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -13,6 +13,9 @@ namespace Pica {
static constexpr std::pair<u16, const char*> register_names[] = {
{0x010, "GPUREG_FINALIZE"},
{0x020, "GPUREG_IRQ_CMP"},
{0x020, "GPUREG_IRQ_MASK"},
{0x034, "GPUREG_IRQ_AUTOSTOP"},
{0x040, "GPUREG_FACECULLING_CONFIG"},
{0x041, "GPUREG_VIEWPORT_WIDTH"},

View File

@ -1,4 +1,4 @@
// Copyright 2017 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -21,8 +21,14 @@ struct RegsInternal {
union {
struct {
INSERT_PADDING_WORDS(0x10);
u32 trigger_irq;
INSERT_PADDING_WORDS(0x2f);
u32 irq_request;
INSERT_PADDING_WORDS(0xf);
u32 irq_compare;
INSERT_PADDING_WORDS(0xf);
u32 irq_mask;
INSERT_PADDING_WORDS(0x3);
u32 irq_autostop;
INSERT_PADDING_WORDS(0xb);
RasterizerRegs rasterizer;
TexturingRegs texturing;
FramebufferRegs framebuffer;
@ -46,7 +52,10 @@ static_assert(sizeof(RegsInternal) == RegsInternal::NUM_REGS * sizeof(u32),
static_assert(offsetof(RegsInternal, field_name) == position * 4, \
"Field " #field_name " has invalid position")
ASSERT_REG_POSITION(trigger_irq, 0x10);
ASSERT_REG_POSITION(irq_request, 0x10);
ASSERT_REG_POSITION(irq_compare, 0x20);
ASSERT_REG_POSITION(irq_mask, 0x30);
ASSERT_REG_POSITION(irq_autostop, 0x34);
ASSERT_REG_POSITION(rasterizer, 0x40);
ASSERT_REG_POSITION(rasterizer.cull_mode, 0x40);