169 lines
3.5 KiB
C#
169 lines
3.5 KiB
C#
using StoicGoose.Core.Interfaces;
|
|
|
|
using static StoicGoose.Common.Utilities.BitHandling;
|
|
|
|
namespace StoicGoose.Core.DMA
|
|
{
|
|
public class SphinxGeneralDMAController : IPortAccessComponent
|
|
{
|
|
// TODO: verify behavior!
|
|
|
|
readonly IMachine machine = default;
|
|
|
|
/* REG_DMA_SRC(_HI) */
|
|
uint dmaSource;
|
|
/* REG_DMA_DST */
|
|
ushort dmaDestination;
|
|
/* REG_DMA_LEN */
|
|
ushort dmaLength;
|
|
/* REG_DMA_CTRL */
|
|
byte dmaControl;
|
|
|
|
public bool IsActive => IsBitSet(dmaControl, 7);
|
|
|
|
bool isDecrementMode => IsBitSet(dmaControl, 6);
|
|
|
|
public SphinxGeneralDMAController(IMachine machine)
|
|
{
|
|
this.machine = machine;
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
//
|
|
|
|
ResetRegisters();
|
|
}
|
|
|
|
private void ResetRegisters()
|
|
{
|
|
dmaSource = dmaDestination = dmaLength = dmaControl = 0;
|
|
}
|
|
|
|
public void Shutdown()
|
|
{
|
|
//
|
|
}
|
|
|
|
public int Step()
|
|
{
|
|
if (dmaLength == 0 || ((dmaSource >> 16) & 0x0F) == 0x01)
|
|
{
|
|
/* Disable DMA if length is zero OR source is SRAM */
|
|
ChangeBit(ref dmaControl, 7, false);
|
|
return 5;
|
|
}
|
|
else
|
|
{
|
|
if (((dmaSource >> 16) & 0x0F) != 0x01)
|
|
{
|
|
/* Perform DMA if source is not SRAM */
|
|
machine.WriteMemory((uint)(dmaDestination + 0), machine.ReadMemory(dmaSource + 0));
|
|
machine.WriteMemory((uint)(dmaDestination + 1), machine.ReadMemory(dmaSource + 1));
|
|
}
|
|
|
|
dmaSource += (uint)(isDecrementMode ? -2 : 2);
|
|
dmaDestination += (ushort)(isDecrementMode ? -2 : 2);
|
|
dmaLength -= 2;
|
|
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
public byte ReadPort(ushort port)
|
|
{
|
|
var retVal = (byte)0;
|
|
|
|
switch (port)
|
|
{
|
|
case 0x40:
|
|
/* REG_DMA_SRC (low) */
|
|
retVal |= (byte)((dmaSource >> 0) & 0xFE);
|
|
break;
|
|
case 0x41:
|
|
/* REG_DMA_SRC (mid) */
|
|
retVal |= (byte)((dmaSource >> 8) & 0xFF);
|
|
break;
|
|
case 0x42:
|
|
/* REG_DMA_SRC_HI */
|
|
retVal |= (byte)((dmaSource >> 16) & 0x0F);
|
|
break;
|
|
|
|
case 0x44:
|
|
/* REG_DMA_DST (low) */
|
|
retVal |= (byte)((dmaDestination >> 0) & 0xFE);
|
|
break;
|
|
case 0x45:
|
|
/* REG_DMA_DST (high) */
|
|
retVal |= (byte)((dmaDestination >> 8) & 0xFF);
|
|
break;
|
|
|
|
case 0x46:
|
|
/* REG_DMA_LEN */
|
|
retVal |= (byte)((dmaLength >> 0) & 0xFE);
|
|
break;
|
|
case 0x47:
|
|
/* REG_DMA_LEN */
|
|
retVal |= (byte)((dmaLength >> 8) & 0xFF);
|
|
break;
|
|
|
|
case 0x48:
|
|
/* REG_DMA_CTRL */
|
|
retVal |= (byte)(dmaControl & 0b11000000);
|
|
break;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
public void WritePort(ushort port, byte value)
|
|
{
|
|
switch (port)
|
|
{
|
|
case 0x40:
|
|
/* REG_DMA_SRC (low) */
|
|
dmaSource &= 0xFFF00;
|
|
dmaSource |= (uint)((value << 0) & 0x000FE);
|
|
break;
|
|
case 0x41:
|
|
/* REG_DMA_SRC (high) */
|
|
dmaSource &= 0xF00FE;
|
|
dmaSource |= (uint)((value << 8) & 0x0FF00);
|
|
break;
|
|
case 0x42:
|
|
/* REG_DMA_SRC_HI */
|
|
dmaSource &= 0x0FFFE;
|
|
dmaSource |= (uint)((value << 16) & 0xF0000);
|
|
break;
|
|
|
|
case 0x44:
|
|
/* REG_DMA_DST (low) */
|
|
dmaDestination &= 0xFF00;
|
|
dmaDestination |= (ushort)((value << 0) & 0x00FE);
|
|
break;
|
|
case 0x45:
|
|
/* REG_DMA_DST (high) */
|
|
dmaDestination &= 0x00FE;
|
|
dmaDestination |= (ushort)((value << 8) & 0xFF00);
|
|
break;
|
|
|
|
case 0x46:
|
|
/* REG_DMA_LEN (low) */
|
|
dmaLength &= 0xFF00;
|
|
dmaLength |= (ushort)((value << 0) & 0x00FE);
|
|
break;
|
|
case 0x47:
|
|
/* REG_DMA_LEN (high) */
|
|
dmaLength &= 0x00FE;
|
|
dmaLength |= (ushort)((value << 8) & 0xFF00);
|
|
break;
|
|
|
|
case 0x48:
|
|
/* REG_DMA_CTRL */
|
|
dmaControl = (byte)(value & 0b11000000);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|