160 lines
3.5 KiB
C#
160 lines
3.5 KiB
C#
using System;
|
|
|
|
using BizHawk.Common;
|
|
using BizHawk.Common.NumberExtensions;
|
|
|
|
namespace BizHawk.Emulation.Cores.Components
|
|
{
|
|
/// <summary>
|
|
/// YM2149F variant
|
|
/// this implementation is quite incomplete
|
|
/// http://wiki.nesdev.com/w/index.php/Sunsoft_5B_audio
|
|
/// </summary>
|
|
public class Sunsoft5BAudio
|
|
{
|
|
class Pulse
|
|
{
|
|
private readonly Action<int> SendDiff;
|
|
// regs
|
|
private int Period;
|
|
private bool Disable;
|
|
private int Volume;
|
|
// state
|
|
private int clock;
|
|
private int sequence;
|
|
private int output;
|
|
|
|
public void SyncState(Serializer ser)
|
|
{
|
|
ser.Sync(nameof(Period), ref Period);
|
|
ser.Sync(nameof(Disable), ref Disable);
|
|
ser.Sync(nameof(Volume), ref Volume);
|
|
ser.Sync(nameof(clock), ref clock);
|
|
ser.Sync(nameof(sequence), ref sequence);
|
|
ser.Sync(nameof(output), ref output);
|
|
}
|
|
|
|
public Pulse(Action<int> SendDiff)
|
|
{
|
|
this.SendDiff = SendDiff;
|
|
}
|
|
|
|
void CalcOut()
|
|
{
|
|
int newout = 1;
|
|
if (!Disable)
|
|
{
|
|
newout = sequence;
|
|
}
|
|
|
|
newout *= Volume;
|
|
if (newout != output)
|
|
{
|
|
SendDiff(newout - output);
|
|
output = newout;
|
|
}
|
|
}
|
|
|
|
public void Clock()
|
|
{
|
|
clock--;
|
|
if (clock < 0)
|
|
{
|
|
clock = Period * 16;
|
|
sequence ^= 1;
|
|
CalcOut();
|
|
}
|
|
}
|
|
public void WritePeriodLow(byte val)
|
|
{
|
|
Period &= 0xf00;
|
|
Period |= val;
|
|
}
|
|
public void WritePeriodHigh(byte val)
|
|
{
|
|
Period &= 0x0ff;
|
|
Period |= val << 8 & 0xf00;
|
|
}
|
|
public void SetToneDisable(bool disable)
|
|
{
|
|
Disable = disable;
|
|
CalcOut();
|
|
}
|
|
public void SetVolume(byte val)
|
|
{
|
|
Volume = val & 15;
|
|
CalcOut();
|
|
}
|
|
}
|
|
|
|
int RegNum;
|
|
readonly Pulse[] pulse = new Pulse[3];
|
|
|
|
public void RegSelect(byte val)
|
|
{
|
|
RegNum = val & 15;
|
|
}
|
|
public void RegWrite(byte val)
|
|
{
|
|
switch (RegNum)
|
|
{
|
|
case 0: pulse[0].WritePeriodLow(val); break;
|
|
case 1: pulse[0].WritePeriodHigh(val); break;
|
|
case 2: pulse[1].WritePeriodLow(val); break;
|
|
case 3: pulse[1].WritePeriodHigh(val); break;
|
|
case 4: pulse[2].WritePeriodLow(val); break;
|
|
case 5: pulse[2].WritePeriodHigh(val); break;
|
|
case 6: break; // noise period
|
|
case 7: // also noise disable
|
|
pulse[0].SetToneDisable(val.Bit(0));
|
|
pulse[1].SetToneDisable(val.Bit(1));
|
|
pulse[2].SetToneDisable(val.Bit(2));
|
|
break;
|
|
case 8: pulse[0].SetVolume(val); break; // also envelope enable
|
|
case 9: pulse[1].SetVolume(val); break; // also envelope enable
|
|
case 10: pulse[2].SetVolume(val); break; // also envelope enable
|
|
case 11: break; // envelope low
|
|
case 12: break; // envelope high
|
|
case 13: break; // envelope params
|
|
// ports 14 and 15 are not hooked up on Sunsoft 5B
|
|
}
|
|
}
|
|
|
|
public void SyncState(Serializer ser)
|
|
{
|
|
ser.BeginSection(nameof(Sunsoft5BAudio));
|
|
ser.Sync(nameof(RegNum), ref RegNum);
|
|
for (int i = 0; i < pulse.Length; i++)
|
|
{
|
|
ser.BeginSection("Pulse" + i);
|
|
pulse[i].SyncState(ser);
|
|
ser.EndSection();
|
|
}
|
|
ser.EndSection();
|
|
}
|
|
|
|
private readonly Action<int> enqueuer;
|
|
private void PulseAddDiff(int val)
|
|
{
|
|
enqueuer(val * 250);
|
|
}
|
|
|
|
public Sunsoft5BAudio(Action<int> enqueuer)
|
|
{
|
|
this.enqueuer = enqueuer;
|
|
for (int i = 0; i < pulse.Length; i++)
|
|
{
|
|
pulse[i] = new Pulse(PulseAddDiff);
|
|
}
|
|
}
|
|
|
|
public void Clock()
|
|
{
|
|
foreach (Pulse p in pulse)
|
|
{
|
|
p.Clock();
|
|
}
|
|
}
|
|
}
|
|
}
|