Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Core/SNES/DmaControllerTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ struct DmaChannelConfig
uint8_t HdmaBank;
uint8_t HdmaLineCounterAndRepeat;
bool DoTransfer;
bool HdmaFinished;

bool UnusedControlFlag;

Expand Down
53 changes: 31 additions & 22 deletions Core/SNES/SnesDmaController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
#include "SNES/SnesDmaController.h"
#include "SNES/DmaControllerTypes.h"
#include "SNES/SnesMemoryManager.h"
#include "Shared/MessageManager.h"
#include "Utilities/Serializer.h"

static constexpr uint8_t _transferByteCount[8] = { 1, 2, 2, 4, 4, 4, 2, 4 };
Expand Down Expand Up @@ -43,6 +42,7 @@ void SnesDmaController::Reset()
_dmaStartDelay = false;
_dmaPending = false;
_needToProcess = false;
_stoppedHdmaChannels = 0;

for(int i = 0; i < 8; i++) {
_state.Channel[i].DmaActive = false;
Expand Down Expand Up @@ -110,11 +110,11 @@ bool SnesDmaController::InitHdmaChannels()
{
_hdmaInitPending = false;

//Reset internal flags on every frame, whether or not the channels are enabled
for(int i = 0; i < 8; i++) {
//Reset internal flags on every frame, whether or not the channels are enabled
_state.Channel[i].HdmaFinished = false;
_state.Channel[i].DoTransfer = false; //not resetting this causes graphical glitches in some games (Aladdin, Super Ghouls and Ghosts)
}
_stoppedHdmaChannels = 0;

if(!_state.HdmaChannels) {
//No channels are enabled, no more processing needs to be done
Expand Down Expand Up @@ -146,8 +146,9 @@ bool SnesDmaController::InitHdmaChannels()
_dmaClockCounter += 8;

ch.HdmaTableAddress++;
if(ch.HdmaLineCounterAndRepeat == 0) {
ch.HdmaFinished = true;
bool stopped = ch.HdmaLineCounterAndRepeat == 0;
if(stopped) {
StopHdmaChannel(i);
}

//3. Load Indirect Address, if necessary.
Expand All @@ -156,7 +157,7 @@ bool SnesDmaController::InitHdmaChannels()
_memoryManager->IncMasterClock4();
_dmaClockCounter += 8;

if(!ch.HdmaFinished) {
if(!stopped) {
uint8_t msb = _memoryManager->ReadDma((ch.SrcBank << 16) | ch.HdmaTableAddress++, true);
_memoryManager->IncMasterClock4();
_dmaClockCounter += 8;
Expand Down Expand Up @@ -230,11 +231,26 @@ bool SnesDmaController::HasActiveDmaChannel()
return false;
}

uint8_t SnesDmaController::GetActiveHdmaChannels()
{
return _state.HdmaChannels & ~_stoppedHdmaChannels;
}

bool SnesDmaController::IsHdmaChannelActive(int i)
{
return GetActiveHdmaChannels() & (1 << i);
}

void SnesDmaController::StopHdmaChannel(int i)
{
_stoppedHdmaChannels |= (1 << i);
}

bool SnesDmaController::ProcessHdmaChannels()
{
_hdmaPending = false;

if(!_state.HdmaChannels) {
if(!GetActiveHdmaChannels()) {
UpdateNeedToProcessFlag();
return false;
}
Expand All @@ -251,16 +267,12 @@ bool SnesDmaController::ProcessHdmaChannels()
//Run all the DMA transfers for each channel first, before fetching data for the next scanline
for(int i = 0; i < 8; i++) {
DmaChannelConfig& ch = _state.Channel[i];
if((_state.HdmaChannels & (1 << i)) == 0) {
if(!IsHdmaChannelActive(i)) {
continue;
}

ch.DmaActive = false;

if(ch.HdmaFinished) {
continue;
}

//1. If DoTransfer is false, skip to step 3.
if(ch.DoTransfer) {
//2. For the number of bytes (1, 2, or 4) required for this Transfer Mode...
Expand All @@ -272,7 +284,7 @@ bool SnesDmaController::ProcessHdmaChannels()
//Update the channel's state & fetch data for the next scanline
for(int i = 0; i < 8; i++) {
DmaChannelConfig& ch = _state.Channel[i];
if((_state.HdmaChannels & (1 << i)) == 0 || ch.HdmaFinished) {
if(!IsHdmaChannelActive(i)) {
continue;
}

Expand Down Expand Up @@ -318,7 +330,7 @@ bool SnesDmaController::ProcessHdmaChannels()

//"c. If $43xA is zero, terminate this HDMA channel for this frame. The bit in $420c is not cleared, though, so it may be automatically restarted next frame."
if(ch.HdmaLineCounterAndRepeat == 0) {
ch.HdmaFinished = true;
StopHdmaChannel(i);
}

//"d. Set DoTransfer to true."
Expand All @@ -339,12 +351,8 @@ bool SnesDmaController::ProcessHdmaChannels()

bool SnesDmaController::IsLastActiveHdmaChannel(uint8_t channel)
{
for(int i = channel + 1; i < 8; i++) {
if((_state.HdmaChannels & (1 << i)) && !_state.Channel[i].HdmaFinished) {
return false;
}
}
return true;
uint8_t mask = (1 << (channel + 1)) - 1;
return (GetActiveHdmaChannels() & ~mask) == 0;
}

void SnesDmaController::UpdateNeedToProcessFlag()
Expand All @@ -355,7 +363,7 @@ void SnesDmaController::UpdateNeedToProcessFlag()

void SnesDmaController::BeginHdmaTransfer()
{
if(_state.HdmaChannels) {
if(GetActiveHdmaChannels()) {
_hdmaPending = true;
_dmaStartDelay = true;
UpdateNeedToProcessFlag();
Expand Down Expand Up @@ -804,13 +812,14 @@ void SnesDmaController::Serialize(Serializer& s)
SV(_hdmaInitPending);
SV(_dmaStartDelay);
SV(_needToProcess);
SV(_stoppedHdmaChannels);

for(int i = 0; i < 8; i++) {
SVI(_state.Channel[i].Decrement);
SVI(_state.Channel[i].DestAddress);
SVI(_state.Channel[i].DoTransfer);
SVI(_state.Channel[i].FixedTransfer);
SVI(_state.Channel[i].HdmaBank);
SVI(_state.Channel[i].HdmaFinished);
SVI(_state.Channel[i].HdmaIndirectAddressing);
SVI(_state.Channel[i].HdmaLineCounterAndRepeat);
SVI(_state.Channel[i].HdmaTableAddress);
Expand Down
5 changes: 4 additions & 1 deletion Core/SNES/SnesDmaController.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#pragma once
#include "pch.h"
#include "SNES/SnesCpuTypes.h"
#include "SNES/DmaControllerTypes.h"
#include "Utilities/ISerializable.h"

Expand All @@ -19,6 +18,7 @@ class SnesDmaController final : public ISerializable
bool _dmaStartDelay = false;
bool _dmaPending = false;
uint32_t _dmaClockCounter = 0;
uint8_t _stoppedHdmaChannels = 0;

uint8_t _activeChannel = 0; //Used by debugger's event viewer

Expand All @@ -38,6 +38,9 @@ class SnesDmaController final : public ISerializable
void UpdateNeedToProcessFlag();

bool HasActiveDmaChannel();
uint8_t GetActiveHdmaChannels();
bool IsHdmaChannelActive(int i);
void StopHdmaChannel(int i);

public:
SnesDmaController(SnesMemoryManager* memoryManager);
Expand Down
1 change: 0 additions & 1 deletion UI/Interop/DebugApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -760,7 +760,6 @@ public struct DmaChannelConfig
public byte HdmaLineCounterAndRepeat;

[MarshalAs(UnmanagedType.I1)] public bool DoTransfer;
[MarshalAs(UnmanagedType.I1)] public bool HdmaFinished;
[MarshalAs(UnmanagedType.I1)] public bool UnusedControlFlag;

public byte UnusedRegister;
Expand Down
Loading