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(); + } + } +}