Skip to content

Commit 89fb3ad

Browse files
committed
DMA: Hardwire reverse direction for OTC
Prevent register writes from clearing the bit, which means a OTC at the end of memory would trigger a DMA error even though the actual clear is forced to be negative. Fixes missing every-other-frame/flicker in Prism Land Story.
1 parent 8a96948 commit 89fb3ad

1 file changed

Lines changed: 22 additions & 16 deletions

File tree

src/core/dma.cpp

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,14 @@ struct ChannelState
8888

8989
static constexpr u32 WRITE_MASK = 0b01110001'01110111'00000111'00000011;
9090

91-
// Only bits 24, 28 and 30 can be set in OTC. All other bits are hardwired to zero.
92-
static constexpr u32 OTC_MASK = 0b01010001'00000000'00000000'00000000;
91+
// Only bits 24, 28 and 30 can be set in OTC. All other bits are hardwired to zero, except bit 1 which is 1.
92+
static constexpr u32 OTC_WRITE_MASK = 0b01010001'00000000'00000000'00000000;
93+
static constexpr u32 OTC_FIXED_BITS = 0b00000000'00000000'00000000'00000010;
9394
} channel_control = {};
9495

9596
bool request = false;
9697
};
98+
static_assert(std::is_trivially_copyable_v<ChannelState>, "ChannelState is trivially copyable");
9799

98100
union DPCRRegister
99101
{
@@ -122,6 +124,7 @@ union DPCRRegister
122124
return ConvertToBoolUnchecked((bits >> (static_cast<u8>(channel) * 4 + 3)) & u32(1));
123125
}
124126
};
127+
static_assert(std::is_trivially_copyable_v<DPCRRegister>, "DPCRRegister is trivially copyable");
125128

126129
static constexpr u32 DICR_WRITE_MASK = 0b00000000'11111111'10000000'01111111;
127130
static constexpr u32 DICR_RESET_MASK = 0b01111111'00000000'00000000'00000000;
@@ -172,6 +175,8 @@ union DICRRegister
172175
(((bits & (1u << 23)) != 0u) != 0u && (bits & (0b1111111u << 24)) != 0u)); // master enable + irq on any channel
173176
}
174177
};
178+
static_assert(std::is_trivially_copyable_v<DICRRegister>, "DICRRegister is trivially copyable");
179+
175180
} // namespace
176181

177182
static void ClearState();
@@ -262,14 +267,8 @@ void DMA::Reset()
262267

263268
void DMA::ClearState()
264269
{
265-
for (u32 i = 0; i < NUM_CHANNELS; i++)
266-
{
267-
ChannelState& cs = s_state.channels[i];
268-
cs.base_address = 0;
269-
cs.block_control.bits = 0;
270-
cs.channel_control.bits = 0;
271-
cs.request = false;
272-
}
270+
s_state.channels = {};
271+
s_state.channels[static_cast<u32>(Channel::OTC)].channel_control.bits = ChannelState::ChannelControl::OTC_FIXED_BITS;
273272

274273
s_state.DPCR.bits = 0x07654321;
275274
s_state.DICR.bits = 0;
@@ -295,6 +294,15 @@ bool DMA::DoState(StateWrapper& sw)
295294

296295
if (sw.IsReading())
297296
{
297+
// Fix up missing OTC bits which old save states were not forcing to 1.
298+
if (sw.GetVersion() < 85) [[unlikely]]
299+
{
300+
s_state.channels[static_cast<u32>(Channel::OTC)].channel_control.bits =
301+
(s_state.channels[static_cast<u32>(Channel::OTC)].channel_control.bits &
302+
ChannelState::ChannelControl::OTC_WRITE_MASK) |
303+
ChannelState::ChannelControl::OTC_FIXED_BITS;
304+
}
305+
298306
if (s_state.halt_ticks_remaining > 0)
299307
s_state.unhalt_event.SetIntervalAndSchedule(s_state.halt_ticks_remaining);
300308
else
@@ -379,12 +387,10 @@ void DMA::WriteRegister(u32 offset, u32 value)
379387
// transfer is happening, and the SPU transfer gets delayed until the GPU transfer unhalts and finishes, and
380388
// breaks the interrupt.
381389
const bool ignore_halt = !state.channel_control.enable_busy && (value & (1u << 24));
382-
383-
state.channel_control.bits = (state.channel_control.bits & ~ChannelState::ChannelControl::WRITE_MASK) |
384-
(value & ChannelState::ChannelControl::WRITE_MASK);
385-
state.channel_control.bits = (static_cast<Channel>(channel_index) == Channel::OTC) ?
386-
(state.channel_control.bits & ChannelState::ChannelControl::OTC_MASK) :
387-
state.channel_control.bits;
390+
const u32 write_mask = (static_cast<Channel>(channel_index) != Channel::OTC) ?
391+
ChannelState::ChannelControl::WRITE_MASK :
392+
ChannelState::ChannelControl::OTC_WRITE_MASK;
393+
state.channel_control.bits = (state.channel_control.bits & ~write_mask) | (value & write_mask);
388394
TRACE_LOG("DMA channel {} channel control <- 0x{:08X}", static_cast<Channel>(channel_index),
389395
state.channel_control.bits);
390396

0 commit comments

Comments
 (0)