mirror of
https://github.com/ApfelTeeSaft/Yaroze.NET.git
synced 2026-08-26 19:43:27 +00:00
315 lines
9.4 KiB
C#
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);
|
|
}
|
|
}
|