Files
Yaroze.NET/tests/Yaroze.Tests/DMA/DmaTests.cs
T
2026-01-24 17:23:50 +01:00

315 lines
9.4 KiB
C#

using Xunit;
using Yaroze.Core;
using Yaroze.Core.DMA;
namespace Yaroze.Tests.DMA;
public class DmaTests
{
private readonly Emulator _emu;
private readonly DmaController _dma;
public DmaTests()
{
_emu = new Emulator();
_dma = _emu.Dma;
}
[Fact]
public void Dma_Reset_ClearsChannels()
{
// Arrange
_emu.Bus.Write32(0x1F8010A0, 0x12345678); // Channel 2 MADR
// Act
_dma.Reset();
// Assert
Assert.Equal(0u, _dma.GetChannel(2).MADR);
}
[Fact]
public void Dma_DPCR_ReadWrite()
{
// Act
_emu.Bus.Write32(0x1F8010F0, 0x12345678);
uint result = _emu.Bus.Read32(0x1F8010F0);
// Assert
Assert.Equal(0x12345678u, result);
}
[Fact]
public void Dma_DICR_ReadWrite()
{
// Act
_emu.Bus.Write32(0x1F8010F4, 0x00FF00FF);
uint result = _emu.Bus.Read32(0x1F8010F4);
// Assert - Check that written value is retained
Assert.Equal(0x00FF00FFu, result & 0x00FFFFFF);
}
[Fact]
public void DmaChannel_MADR_MaskedTo24Bits()
{
// Act
_emu.Bus.Write32(0x1F8010A0, 0xFFFFFFFF); // Channel 2 MADR
// Assert - Should be masked to 24 bits
Assert.Equal(0x00FFFFFFu, _dma.GetChannel(2).MADR);
}
[Fact]
public void DmaChannel_BCR_ReadWrite()
{
// Act
_emu.Bus.Write32(0x1F8010A4, 0x12345678); // Channel 2 BCR
uint result = _emu.Bus.Read32(0x1F8010A4);
// Assert
Assert.Equal(0x12345678u, result);
}
[Fact]
public void DmaChannel_CHCR_ReadWrite()
{
// Act
_emu.Bus.Write32(0x1F8010A8, 0x01000201); // Channel 2 CHCR
uint result = _emu.Bus.Read32(0x1F8010A8);
// Assert
Assert.Equal(0x01000201u, result);
}
[Fact]
public void DmaChannel_IsActive_ReflectsBusyBit()
{
// Arrange - Set CHCR with start/busy bit
_emu.Bus.Write32(0x1F8010A8, 0x01000000); // Channel 2 CHCR with busy bit
// Act
bool isActive = _dma.GetChannel(2).IsActive;
// Assert
Assert.True(isActive);
}
[Fact]
public void DmaChannel_SyncMode_ExtractsCorrectly()
{
// Arrange - Set sync mode to 2 (linked list) in bits 9-10
_emu.Bus.Write32(0x1F8010A8, 0x00000400); // Sync mode 2
// Act
int syncMode = _dma.GetChannel(2).SyncMode;
// Assert
Assert.Equal(2, syncMode);
}
[Fact]
public void DmaChannel_FromRam_ExtractsCorrectly()
{
// Arrange - Set direction bit
_emu.Bus.Write32(0x1F8010A8, 0x00000001); // From RAM
// Act
bool fromRam = _dma.GetChannel(2).FromRam;
// Assert
Assert.True(fromRam);
}
[Fact]
public void Dma_AllChannelsAccessible()
{
// Test that all 7 channels have distinct addresses
for (int i = 0; i < 7; i++)
{
uint baseAddr = (uint)(0x1F801080 + i * 0x10);
// Write unique value to each channel's MADR
_emu.Bus.Write32(baseAddr, (uint)(0x100 * (i + 1)));
}
// Verify
for (int i = 0; i < 7; i++)
{
Assert.Equal((uint)(0x100 * (i + 1)), _dma.GetChannel(i).MADR);
}
}
[Fact]
public void Dma_BurstTransfer_TransfersData()
{
// Arrange - Set up a burst transfer from RAM to GPU
// Channel 2 (GPU), sync mode 0 (burst), from RAM
// Write test data to RAM
for (int i = 0; i < 10; i++)
{
_emu.Bus.Ram.Write32((uint)(0x1000 + i * 4), (uint)(0xA0 + i));
}
// Configure DMA channel 2
_emu.Bus.Write32(0x1F8010A0, 0x00001000); // MADR = 0x1000
_emu.Bus.Write32(0x1F8010A4, 0x0000000A); // BCR = 10 words
_emu.Bus.Write32(0x1F8010F0, 0x00000800); // DPCR = Enable channel 2 (bit 11)
// Act - Trigger transfer with CHCR (sync mode 0, from RAM, start)
_emu.Bus.Write32(0x1F8010A8, 0x01000001);
// Assert - Channel should no longer be busy
Assert.False(_dma.GetChannel(2).IsActive);
}
[Fact]
public void Dma_BurstTransfer_UpdatesMADR()
{
// Arrange
_emu.Bus.Ram.Write32(0x1000, 0x12345678);
_emu.Bus.Write32(0x1F8010A0, 0x00001000); // MADR = 0x1000
_emu.Bus.Write32(0x1F8010A4, 0x00000005); // BCR = 5 words
_emu.Bus.Write32(0x1F8010F0, 0x00000800); // DPCR = Enable channel 2
// Act
_emu.Bus.Write32(0x1F8010A8, 0x01000001); // Start transfer
// Assert - MADR should have advanced by 5*4 = 20 bytes
Assert.Equal(0x00001014u, _dma.GetChannel(2).MADR);
}
[Fact]
public void Dma_BurstTransfer_BackwardDirection()
{
// Arrange - Transfer with backward step (bit 1 set)
_emu.Bus.Ram.Write32(0x1000, 0x12345678);
_emu.Bus.Write32(0x1F8010A0, 0x00001000); // MADR = 0x1000
_emu.Bus.Write32(0x1F8010A4, 0x00000005); // BCR = 5 words
_emu.Bus.Write32(0x1F8010F0, 0x00000800); // DPCR = Enable channel 2
// Act - Trigger with backward step (bit 1)
_emu.Bus.Write32(0x1F8010A8, 0x01000003); // From RAM + backward
// Assert - MADR should have decremented by 5*4 = 20 bytes
Assert.Equal(0x00000FECu, _dma.GetChannel(2).MADR);
}
[Fact]
public void Dma_SliceTransfer_MultipleBlocks()
{
// Arrange - Sync mode 1 (slice): block size in BCR[15:0], count in BCR[31:16]
_emu.Bus.Ram.Write32(0x1000, 0x12345678);
_emu.Bus.Write32(0x1F8010A0, 0x00001000); // MADR = 0x1000
_emu.Bus.Write32(0x1F8010A4, 0x00030004); // BCR = 3 blocks of 4 words
_emu.Bus.Write32(0x1F8010F0, 0x00000800); // DPCR = Enable channel 2
// Act - Trigger with sync mode 1 (bits 9-10 = 01)
_emu.Bus.Write32(0x1F8010A8, 0x01000201); // Sync mode 1, from RAM
// Assert - MADR should have advanced by 3*4*4 = 48 bytes
Assert.Equal(0x00001030u, _dma.GetChannel(2).MADR);
}
[Fact]
public void Dma_InterruptFlag_SetOnTransfer()
{
// Arrange - Enable interrupt for channel 2 (bit 18)
_emu.Bus.Write32(0x1F8010F4, 0x00040000); // DICR = Enable IRQ for channel 2
_emu.Bus.Write32(0x1F8010F0, 0x00000800); // DPCR = Enable channel 2
_emu.Bus.Write32(0x1F8010A0, 0x00001000); // MADR
_emu.Bus.Write32(0x1F8010A4, 0x00000001); // BCR = 1 word
// Act - Trigger transfer
_emu.Bus.Write32(0x1F8010A8, 0x01000001);
// Assert - Interrupt flag for channel 2 (bit 26) should be set
uint dicr = _emu.Bus.Read32(0x1F8010F4);
Assert.NotEqual(0u, dicr & (1u << 26));
}
[Fact]
public void Dma_MasterFlag_SetWhenEnabled()
{
// Arrange - Enable master IRQ (bit 23) and channel 2 IRQ (bit 18)
_emu.Bus.Write32(0x1F8010F4, 0x00840000); // Master enable + Channel 2 enable
_emu.Bus.Write32(0x1F8010F0, 0x00000800); // DPCR = Enable channel 2
_emu.Bus.Write32(0x1F8010A0, 0x00001000); // MADR
_emu.Bus.Write32(0x1F8010A4, 0x00000001); // BCR = 1 word
// Act - Trigger transfer
_emu.Bus.Write32(0x1F8010A8, 0x01000001);
// Assert - Master flag (bit 31) should be set
uint dicr = _emu.Bus.Read32(0x1F8010F4);
Assert.NotEqual(0u, dicr & (1u << 31));
}
[Fact]
public void Dma_HasPendingInterrupt_ReflectsMasterFlag()
{
// Arrange
_emu.Bus.Write32(0x1F8010F4, 0x00840000); // Enable master + channel 2
_emu.Bus.Write32(0x1F8010F0, 0x00000800); // Enable channel 2
_emu.Bus.Write32(0x1F8010A0, 0x00001000);
_emu.Bus.Write32(0x1F8010A4, 0x00000001);
// Act
_emu.Bus.Write32(0x1F8010A8, 0x01000001); // Trigger
// Assert
Assert.True(_dma.HasPendingInterrupt());
}
[Fact]
public void Dma_ClearInterruptFlag_WritingOne()
{
// Arrange - Set interrupt flag
_emu.Bus.Write32(0x1F8010F4, 0x00840000);
_emu.Bus.Write32(0x1F8010F0, 0x00000800);
_emu.Bus.Write32(0x1F8010A0, 0x00001000);
_emu.Bus.Write32(0x1F8010A4, 0x00000001);
_emu.Bus.Write32(0x1F8010A8, 0x01000001); // Trigger
// Verify flag is set
uint dicr = _emu.Bus.Read32(0x1F8010F4);
Assert.NotEqual(0u, dicr & (1u << 26));
// Act - Clear by writing 1 to the flag bit
_emu.Bus.Write32(0x1F8010F4, (1u << 26));
// Assert - Flag should be cleared
dicr = _emu.Bus.Read32(0x1F8010F4);
Assert.Equal(0u, dicr & (1u << 26));
}
[Fact]
public void Dma_DisabledChannel_NoTransfer()
{
// Arrange - Channel 2 disabled in DPCR (bit 11 = 0)
_emu.Bus.Write32(0x1F8010F0, 0x00000000); // DPCR = All disabled
_emu.Bus.Write32(0x1F8010A0, 0x00001000); // MADR
_emu.Bus.Write32(0x1F8010A4, 0x00000010); // BCR = 16 words
// Act - Try to trigger transfer
_emu.Bus.Write32(0x1F8010A8, 0x01000001);
// Assert - MADR should not have changed (transfer didn't execute)
Assert.Equal(0x00001000u, _dma.GetChannel(2).MADR);
}
[Fact]
public void Dma_Channel6_OTC_IsAccessible()
{
// Channel 6 (OTC - Ordering Table Clear) is special
// Act
_emu.Bus.Write32(0x1F8010E0, 0x00002000); // MADR
_emu.Bus.Write32(0x1F8010E4, 0x00000010); // BCR
// Assert
Assert.Equal(0x00002000u, _dma.GetChannel(6).MADR);
Assert.Equal(0x00000010u, _dma.GetChannel(6).BCR);
}
}