StoicGoose.Unity/Assets/Plugins/StoicGooseUnity/StoicGoose.Core/DMA/SphinxSoundDMAController.cs

174 lines
3.6 KiB
C#

using StoicGoose.Core.Interfaces;
using static StoicGoose.Common.Utilities.BitHandling;
namespace StoicGoose.Core.DMA
{
public class SphinxSoundDMAController : IPortAccessComponent
{
readonly static int[] cycleCounts = { 768, 512, 256, 128 };
readonly static ushort destinationPortChannel2Volume = 0x089;
readonly static ushort destinationPortHyperVoice = 0x095;
readonly IMachine machine = default;
/* REG_DMA_SRC(_HI) */
uint dmaSource;
/* REG_DMA_LEN(_HI) */
uint dmaLength;
/* REG_DMA_CTRL */
byte dmaControl;
public bool IsActive => IsBitSet(dmaControl, 7);
bool isDecrementMode => IsBitSet(dmaControl, 6);
bool isDestinationHyperVoice => IsBitSet(dmaControl, 4);
bool isLoopingMode => IsBitSet(dmaControl, 3);
int dmaRate => dmaControl & 0b11;
uint initialSource, initialLength;
int cycleCount;
public SphinxSoundDMAController(IMachine machine)
{
this.machine = machine;
}
public void Reset()
{
initialSource = initialLength = 0;
cycleCount = 0;
ResetRegisters();
}
private void ResetRegisters()
{
dmaSource = dmaLength = dmaControl = 0;
}
public void Shutdown()
{
//
}
public void Step(int clockCyclesInStep)
{
cycleCount += clockCyclesInStep;
if (cycleCount >= cycleCounts[dmaRate])
{
machine.WritePort(isDestinationHyperVoice ? destinationPortHyperVoice : destinationPortChannel2Volume, machine.ReadMemory(dmaSource));
dmaSource += (uint)(isDecrementMode ? -1 : 1);
dmaLength--;
if (dmaLength == 0)
{
if (isLoopingMode)
{
dmaSource = initialSource;
dmaLength = initialLength;
}
else
ChangeBit(ref dmaControl, 7, false);
}
cycleCount = 0;
}
}
public byte ReadPort(ushort port)
{
var retVal = (byte)0;
switch (port)
{
case 0x4A:
/* REG_SDMA_SRC (low) */
retVal |= (byte)((dmaSource >> 0) & 0xFF);
break;
case 0x4B:
/* REG_SDMA_SRC (mid) */
retVal |= (byte)((dmaSource >> 8) & 0xFF);
break;
case 0x4C:
/* REG_SDMA_SRC_HI */
retVal |= (byte)((dmaSource >> 16) & 0x0F);
break;
case 0x4E:
/* REG_SDMA_LEN (low) */
retVal |= (byte)((dmaLength >> 0) & 0xFE);
break;
case 0x4F:
/* REG_SDMA_LEN (mid) */
retVal |= (byte)((dmaLength >> 8) & 0xFF);
break;
case 0x50:
/* REG_SDMA_LEN_HI */
retVal |= (byte)((dmaLength >> 16) & 0x0F);
break;
case 0x52:
/* REG_SDMA_CTRL */
retVal |= (byte)(dmaControl & 0b11011111);
break;
}
return retVal;
}
public void WritePort(ushort port, byte value)
{
switch (port)
{
case 0x4A:
/* REG_SDMA_SRC (low) */
dmaSource &= 0xFFF00;
dmaSource |= (uint)((value << 0) & 0x000FF);
break;
case 0x4B:
/* REG_SDMA_SRC (mid) */
dmaSource &= 0xF00FF;
dmaSource |= (uint)((value << 8) & 0x0FF00);
break;
case 0x4C:
/* REG_SDMA_SRC_HI */
dmaSource &= 0x0FFFF;
dmaSource |= (uint)((value << 16) & 0xF0000);
break;
case 0x4E:
/* REG_SDMA_LEN (low) */
dmaLength &= 0xFFF00;
dmaLength |= (ushort)((value << 0) & 0x00FF);
break;
case 0x4F:
/* REG_SDMA_LEN (mid) */
dmaLength &= 0xF00FF;
dmaLength |= (ushort)((value << 8) & 0xFF00);
break;
case 0x50:
/* REG_SDMA_SRC_HI */
dmaLength &= 0x0FFFF;
dmaLength |= (uint)((value << 16) & 0xF0000);
break;
case 0x52:
/* REG_SDMA_CTRL */
if (!IsActive && IsBitSet(value, 7))
{
initialSource = dmaSource;
initialLength = dmaLength;
}
dmaControl = (byte)(value & 0b11011111);
break;
}
}
}
}