diff --git a/BizHawk.Emulation/BizHawk.Emulation.csproj b/BizHawk.Emulation/BizHawk.Emulation.csproj
index 5b8f4d6330..61eb806bc8 100644
--- a/BizHawk.Emulation/BizHawk.Emulation.csproj
+++ b/BizHawk.Emulation/BizHawk.Emulation.csproj
@@ -442,6 +442,7 @@
+
diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/ExROM.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/ExROM.cs
index 2b6b403c48..f4e4557bcf 100644
--- a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/ExROM.cs
+++ b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/ExROM.cs
@@ -315,9 +315,15 @@ namespace BizHawk.Emulation.Consoles.Nintendo
public override byte ReadPRG(int addr)
{
bool ram;
- addr = MapPRG(addr, out ram);
- if (ram) return WRAM[addr];
- else return ROM[addr];
+ byte ret;
+ int mapaddr = MapPRG(addr, out ram);
+ if (ram)
+ ret = WRAM[mapaddr];
+ else
+ ret = ROM[mapaddr];
+ if (addr < 0x4000)
+ audio.ReadROMTrigger(ret);
+ return ret;
}
public override void WritePRG(int addr, byte value)
diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/Mapper069.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/Mapper069.cs
index 0ffec31197..87590b70de 100644
--- a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/Mapper069.cs
+++ b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/Mapper069.cs
@@ -9,6 +9,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo
class Sunsoft_5 : Sunsoft_FME7
{
+ Sound.Sunsoft5BAudio audio;
+
public override bool Configure(NES.EDetectionOrigin origin)
{
//configure
@@ -22,14 +24,33 @@ namespace BizHawk.Emulation.Consoles.Nintendo
}
BaseConfigure();
+ if (NES.apu != null)
+ audio = new Sound.Sunsoft5BAudio(NES.apu.ExternalQueue);
return true;
}
public override void WritePRG(int addr, byte value)
{
- //TODO - sound
- base.WritePRG(addr, value);
+ int a = addr & 0xe000;
+ if (a == 0x4000)
+ audio.RegSelect(value);
+ else if (a == 0x6000)
+ audio.RegWrite(value);
+ else
+ base.WritePRG(addr, value);
+ }
+
+ public override void SyncState(Serializer ser)
+ {
+ base.SyncState(ser);
+ audio.SyncState(ser);
+ }
+
+ public override void ClockCPU()
+ {
+ audio.Clock();
+ base.ClockCPU();
}
}
diff --git a/BizHawk.Emulation/Sound/Sunsoft5BAudio.cs b/BizHawk.Emulation/Sound/Sunsoft5BAudio.cs
new file mode 100644
index 0000000000..2ad2a055eb
--- /dev/null
+++ b/BizHawk.Emulation/Sound/Sunsoft5BAudio.cs
@@ -0,0 +1,146 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace BizHawk.Emulation.Sound
+{
+ // YM2149F variant
+ // this implementation is quite incomplete
+ // http://wiki.nesdev.com/w/index.php/Sunsoft_5B_audio
+ public class Sunsoft5BAudio
+ {
+ class Pulse
+ {
+ Action SendDiff;
+ // regs
+ int Period;
+ bool Disable;
+ int Volume;
+ // state
+ int clock;
+ int sequence;
+ int output;
+
+ public void SyncState(Serializer ser)
+ {
+ ser.Sync("Period", ref Period);
+ ser.Sync("Disable", ref Disable);
+ ser.Sync("Volume", ref Volume);
+ ser.Sync("clock", ref clock);
+ ser.Sync("sequence", ref sequence);
+ ser.Sync("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;
+ 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("Sunsoft5BAudio");
+ ser.Sync("RegNum", ref RegNum);
+ for (int i = 0; i < pulse.Length; i++)
+ pulse[i].SyncState(ser);
+ ser.EndSection();
+ }
+
+ Action enqueuer;
+ 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()
+ {
+ for (int i = 0; i < pulse.Length; i++)
+ pulse[i].Clock();
+ }
+ }
+}