using System; using BizHawk.Common; using BizHawk.Common.NumberExtensions; namespace BizHawk.Emulation.Cores.Components { /// /// YM2149F variant /// this implementation is quite incomplete /// http://wiki.nesdev.com/w/index.php/Sunsoft_5B_audio /// public class Sunsoft5BAudio { class Pulse { private readonly Action 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 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 enqueuer; private void PulseAddDiff(int val) { enqueuer(val * 250); } public Sunsoft5BAudio(Action 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(); } } } }