From e606429219688c372f06d041805a4bae2f3e0f0c Mon Sep 17 00:00:00 2001 From: adelikat Date: Fri, 15 Nov 2013 01:52:03 +0000 Subject: [PATCH] more misc cleanup and removing some usings that should have been removed when moving things from one project to another --- .../CPUs/68000/Diassembler.cs | 202 +- .../CPUs/CP1610/CP1610.cs | 4 +- .../CPUs/CP1610/Execute.cs | 2 +- .../CPUs/HuC6280/HuC6280.cs | 698 +++--- .../CPUs/MOS 6502X/MOS6502X.cs | 38 +- .../CPUs/MOS 6502X/MOS6502XDouble.cs | 4 +- .../CPUs/MOS 6502X/MOS6502XNative.cs | 18 +- .../Interfaces/CoreComms.cs | 15 +- BizHawk.Emulation.Common/MemoryDomain.cs | 2 +- BizHawk.Emulation.Common/Sound/HuC6280PSG.cs | 13 +- BizHawk.Emulation.Common/Sound/MMC5Audio.cs | 58 +- BizHawk.Emulation.Common/Sound/SN76489.cs | 6 +- .../Sound/Sunsoft5BAudio.cs | 35 +- .../Sound/Utilities/BlipBuffer.cs | 5 +- .../Sound/Utilities/BufferedAsync.cs | 16 +- .../Sound/Utilities/DCFilter.cs | 9 +- .../Sound/Utilities/Equalizer.cs | 3 - .../Sound/Utilities/Metaspu.cs | 12 +- .../Sound/Utilities/SoundMixer.cs | 5 +- .../Sound/Utilities/SpeexResampler.cs | 26 +- BizHawk.Emulation.Common/Sound/VRC6Alt.cs | 4 - BizHawk.Emulation.Common/Sound/YM2413.cs | 1 - BizHawk.Emulation.Common/Sound/YM2612.cs | 2227 ++++++++--------- 23 files changed, 1687 insertions(+), 1716 deletions(-) diff --git a/BizHawk.Emulation.Common/CPUs/68000/Diassembler.cs b/BizHawk.Emulation.Common/CPUs/68000/Diassembler.cs index 34384c369d..0da11ee8bb 100644 --- a/BizHawk.Emulation.Common/CPUs/68000/Diassembler.cs +++ b/BizHawk.Emulation.Common/CPUs/68000/Diassembler.cs @@ -2,115 +2,115 @@ namespace BizHawk.Emulation.Common.Components.M68000 { - public sealed class DisassemblyInfo - { - public int PC; - public string Mnemonic; - public string Args; - public string RawBytes; - public int Length; + public sealed class DisassemblyInfo + { + public int PC; + public string Mnemonic; + public string Args; + public string RawBytes; + public int Length; - public override string ToString() - { - return string.Format("{0:X6}: {3,-20} {1,-8} {2}", PC, Mnemonic, Args, RawBytes); - } - } + public override string ToString() + { + return string.Format("{0:X6}: {3,-20} {1,-8} {2}", PC, Mnemonic, Args, RawBytes); + } + } - partial class MC68000 - { - public DisassemblyInfo Disassemble(int pc) - { - var info = new DisassemblyInfo { Mnemonic = "UNKNOWN", PC = pc, Length = 2 }; - op = (ushort)ReadWord(pc); + partial class MC68000 + { + public DisassemblyInfo Disassemble(int pc) + { + var info = new DisassemblyInfo { Mnemonic = "UNKNOWN", PC = pc, Length = 2 }; + op = (ushort)ReadWord(pc); - if (Opcodes[op] == MOVE) MOVE_Disasm(info); - else if (Opcodes[op] == MOVEA) MOVEA_Disasm(info); - else if (Opcodes[op] == MOVEQ) MOVEQ_Disasm(info); - else if (Opcodes[op] == MOVEM0) MOVEM0_Disasm(info); - else if (Opcodes[op] == MOVEM1) MOVEM1_Disasm(info); - else if (Opcodes[op] == LEA) LEA_Disasm(info); - else if (Opcodes[op] == CLR) CLR_Disasm(info); - else if (Opcodes[op] == EXT) EXT_Disasm(info); - else if (Opcodes[op] == PEA) PEA_Disasm(info); + if (Opcodes[op] == MOVE) MOVE_Disasm(info); + else if (Opcodes[op] == MOVEA) MOVEA_Disasm(info); + else if (Opcodes[op] == MOVEQ) MOVEQ_Disasm(info); + else if (Opcodes[op] == MOVEM0) MOVEM0_Disasm(info); + else if (Opcodes[op] == MOVEM1) MOVEM1_Disasm(info); + else if (Opcodes[op] == LEA) LEA_Disasm(info); + else if (Opcodes[op] == CLR) CLR_Disasm(info); + else if (Opcodes[op] == EXT) EXT_Disasm(info); + else if (Opcodes[op] == PEA) PEA_Disasm(info); - else if (Opcodes[op] == ANDI) ANDI_Disasm(info); - else if (Opcodes[op] == EORI) EORI_Disasm(info); - else if (Opcodes[op] == ORI) ORI_Disasm(info); - else if (Opcodes[op] == ASLd) ASLd_Disasm(info); - else if (Opcodes[op] == ASRd) ASRd_Disasm(info); - else if (Opcodes[op] == LSLd) LSLd_Disasm(info); - else if (Opcodes[op] == LSRd) LSRd_Disasm(info); - else if (Opcodes[op] == ROXLd) ROXLd_Disasm(info); - else if (Opcodes[op] == ROXRd) ROXRd_Disasm(info); - else if (Opcodes[op] == ROLd) ROLd_Disasm(info); - else if (Opcodes[op] == RORd) RORd_Disasm(info); - else if (Opcodes[op] == SWAP) SWAP_Disasm(info); - else if (Opcodes[op] == AND0) AND0_Disasm(info); - else if (Opcodes[op] == AND1) AND1_Disasm(info); - else if (Opcodes[op] == EOR) EOR_Disasm(info); - else if (Opcodes[op] == OR0) OR0_Disasm(info); - else if (Opcodes[op] == OR1) OR1_Disasm(info); - else if (Opcodes[op] == NOT) NOT_Disasm(info); - else if (Opcodes[op] == NEG) NEG_Disasm(info); + else if (Opcodes[op] == ANDI) ANDI_Disasm(info); + else if (Opcodes[op] == EORI) EORI_Disasm(info); + else if (Opcodes[op] == ORI) ORI_Disasm(info); + else if (Opcodes[op] == ASLd) ASLd_Disasm(info); + else if (Opcodes[op] == ASRd) ASRd_Disasm(info); + else if (Opcodes[op] == LSLd) LSLd_Disasm(info); + else if (Opcodes[op] == LSRd) LSRd_Disasm(info); + else if (Opcodes[op] == ROXLd) ROXLd_Disasm(info); + else if (Opcodes[op] == ROXRd) ROXRd_Disasm(info); + else if (Opcodes[op] == ROLd) ROLd_Disasm(info); + else if (Opcodes[op] == RORd) RORd_Disasm(info); + else if (Opcodes[op] == SWAP) SWAP_Disasm(info); + else if (Opcodes[op] == AND0) AND0_Disasm(info); + else if (Opcodes[op] == AND1) AND1_Disasm(info); + else if (Opcodes[op] == EOR) EOR_Disasm(info); + else if (Opcodes[op] == OR0) OR0_Disasm(info); + else if (Opcodes[op] == OR1) OR1_Disasm(info); + else if (Opcodes[op] == NOT) NOT_Disasm(info); + else if (Opcodes[op] == NEG) NEG_Disasm(info); - else if (Opcodes[op] == JMP) JMP_Disasm(info); - else if (Opcodes[op] == JSR) JSR_Disasm(info); - else if (Opcodes[op] == Bcc) Bcc_Disasm(info); - else if (Opcodes[op] == BRA) BRA_Disasm(info); - else if (Opcodes[op] == BSR) BSR_Disasm(info); - else if (Opcodes[op] == DBcc) DBcc_Disasm(info); - else if (Opcodes[op] == Scc) Scc_Disasm(info); - else if (Opcodes[op] == RTE) RTE_Disasm(info); - else if (Opcodes[op] == RTS) RTS_Disasm(info); - else if (Opcodes[op] == RTR) RTR_Disasm(info); - else if (Opcodes[op] == TST) TST_Disasm(info); - else if (Opcodes[op] == BTSTi) BTSTi_Disasm(info); - else if (Opcodes[op] == BTSTr) BTSTr_Disasm(info); - else if (Opcodes[op] == BCHGi) BCHGi_Disasm(info); - else if (Opcodes[op] == BCHGr) BCHGr_Disasm(info); - else if (Opcodes[op] == BCLRi) BCLRi_Disasm(info); - else if (Opcodes[op] == BCLRr) BCLRr_Disasm(info); - else if (Opcodes[op] == BSETi) BSETi_Disasm(info); - else if (Opcodes[op] == BSETr) BSETr_Disasm(info); - else if (Opcodes[op] == LINK) LINK_Disasm(info); - else if (Opcodes[op] == UNLK) UNLK_Disasm(info); - else if (Opcodes[op] == NOP) NOP_Disasm(info); + else if (Opcodes[op] == JMP) JMP_Disasm(info); + else if (Opcodes[op] == JSR) JSR_Disasm(info); + else if (Opcodes[op] == Bcc) Bcc_Disasm(info); + else if (Opcodes[op] == BRA) BRA_Disasm(info); + else if (Opcodes[op] == BSR) BSR_Disasm(info); + else if (Opcodes[op] == DBcc) DBcc_Disasm(info); + else if (Opcodes[op] == Scc) Scc_Disasm(info); + else if (Opcodes[op] == RTE) RTE_Disasm(info); + else if (Opcodes[op] == RTS) RTS_Disasm(info); + else if (Opcodes[op] == RTR) RTR_Disasm(info); + else if (Opcodes[op] == TST) TST_Disasm(info); + else if (Opcodes[op] == BTSTi) BTSTi_Disasm(info); + else if (Opcodes[op] == BTSTr) BTSTr_Disasm(info); + else if (Opcodes[op] == BCHGi) BCHGi_Disasm(info); + else if (Opcodes[op] == BCHGr) BCHGr_Disasm(info); + else if (Opcodes[op] == BCLRi) BCLRi_Disasm(info); + else if (Opcodes[op] == BCLRr) BCLRr_Disasm(info); + else if (Opcodes[op] == BSETi) BSETi_Disasm(info); + else if (Opcodes[op] == BSETr) BSETr_Disasm(info); + else if (Opcodes[op] == LINK) LINK_Disasm(info); + else if (Opcodes[op] == UNLK) UNLK_Disasm(info); + else if (Opcodes[op] == NOP) NOP_Disasm(info); - else if (Opcodes[op] == ADD0) ADD_Disasm(info); - else if (Opcodes[op] == ADD1) ADD_Disasm(info); - else if (Opcodes[op] == ADDA) ADDA_Disasm(info); - else if (Opcodes[op] == ADDI) ADDI_Disasm(info); - else if (Opcodes[op] == ADDQ) ADDQ_Disasm(info); - else if (Opcodes[op] == SUB0) SUB_Disasm(info); - else if (Opcodes[op] == SUB1) SUB_Disasm(info); - else if (Opcodes[op] == SUBA) SUBA_Disasm(info); - else if (Opcodes[op] == SUBI) SUBI_Disasm(info); - else if (Opcodes[op] == SUBQ) SUBQ_Disasm(info); - else if (Opcodes[op] == CMP) CMP_Disasm(info); - else if (Opcodes[op] == CMPM) CMPM_Disasm(info); - else if (Opcodes[op] == CMPA) CMPA_Disasm(info); + else if (Opcodes[op] == ADD0) ADD_Disasm(info); + else if (Opcodes[op] == ADD1) ADD_Disasm(info); + else if (Opcodes[op] == ADDA) ADDA_Disasm(info); + else if (Opcodes[op] == ADDI) ADDI_Disasm(info); + else if (Opcodes[op] == ADDQ) ADDQ_Disasm(info); + else if (Opcodes[op] == SUB0) SUB_Disasm(info); + else if (Opcodes[op] == SUB1) SUB_Disasm(info); + else if (Opcodes[op] == SUBA) SUBA_Disasm(info); + else if (Opcodes[op] == SUBI) SUBI_Disasm(info); + else if (Opcodes[op] == SUBQ) SUBQ_Disasm(info); + else if (Opcodes[op] == CMP) CMP_Disasm(info); + else if (Opcodes[op] == CMPM) CMPM_Disasm(info); + else if (Opcodes[op] == CMPA) CMPA_Disasm(info); - else if (Opcodes[op] == CMPI) CMPI_Disasm(info); - else if (Opcodes[op] == MULU) MULU_Disasm(info); - else if (Opcodes[op] == MULS) MULS_Disasm(info); - else if (Opcodes[op] == DIVU) DIVU_Disasm(info); - else if (Opcodes[op] == DIVS) DIVS_Disasm(info); + else if (Opcodes[op] == CMPI) CMPI_Disasm(info); + else if (Opcodes[op] == MULU) MULU_Disasm(info); + else if (Opcodes[op] == MULS) MULS_Disasm(info); + else if (Opcodes[op] == DIVU) DIVU_Disasm(info); + else if (Opcodes[op] == DIVS) DIVS_Disasm(info); - else if (Opcodes[op] == MOVEtSR) MOVEtSR_Disasm(info); - else if (Opcodes[op] == MOVEfSR) MOVEfSR_Disasm(info); - else if (Opcodes[op] == MOVEUSP) MOVEUSP_Disasm(info); - else if (Opcodes[op] == ANDI_SR) ANDI_SR_Disasm(info); - else if (Opcodes[op] == EORI_SR) EORI_SR_Disasm(info); - else if (Opcodes[op] == ORI_SR) ORI_SR_Disasm(info); - else if (Opcodes[op] == MOVECCR) MOVECCR_Disasm(info); - else if (Opcodes[op] == TRAP) TRAP_Disasm(info); + else if (Opcodes[op] == MOVEtSR) MOVEtSR_Disasm(info); + else if (Opcodes[op] == MOVEfSR) MOVEfSR_Disasm(info); + else if (Opcodes[op] == MOVEUSP) MOVEUSP_Disasm(info); + else if (Opcodes[op] == ANDI_SR) ANDI_SR_Disasm(info); + else if (Opcodes[op] == EORI_SR) EORI_SR_Disasm(info); + else if (Opcodes[op] == ORI_SR) ORI_SR_Disasm(info); + else if (Opcodes[op] == MOVECCR) MOVECCR_Disasm(info); + else if (Opcodes[op] == TRAP) TRAP_Disasm(info); - var sb = new StringBuilder(); - for (int p = info.PC; p < info.PC + info.Length; p++) - sb.AppendFormat("{0:X2}", ReadByte(p)); - info.RawBytes = sb.ToString(); + var sb = new StringBuilder(); + for (int p = info.PC; p < info.PC + info.Length; p++) + sb.AppendFormat("{0:X2}", ReadByte(p)); + info.RawBytes = sb.ToString(); - return info; - } - } + return info; + } + } } diff --git a/BizHawk.Emulation.Common/CPUs/CP1610/CP1610.cs b/BizHawk.Emulation.Common/CPUs/CP1610/CP1610.cs index b995e748cb..d5645840ad 100644 --- a/BizHawk.Emulation.Common/CPUs/CP1610/CP1610.cs +++ b/BizHawk.Emulation.Common/CPUs/CP1610/CP1610.cs @@ -10,7 +10,7 @@ namespace BizHawk.Emulation.Common.Components.CP1610 private bool FlagS, FlagC, FlagZ, FlagO, FlagI, FlagD, IntRM, BusRq, BusAk, Interruptible, Interrupted; //private bool MSync; - private ushort[] Register = new ushort[8]; + private readonly ushort[] Register = new ushort[8]; private ushort RegisterSP { get { return Register[6]; } set { Register[6] = value; } } private ushort RegisterPC { get { return Register[7]; } set { Register[7] = value; } } @@ -21,7 +21,7 @@ namespace BizHawk.Emulation.Common.Components.CP1610 public Func WriteMemory; private static bool Logging = true; - private static StreamWriter Log; + private static readonly StreamWriter Log; static CP1610() { diff --git a/BizHawk.Emulation.Common/CPUs/CP1610/Execute.cs b/BizHawk.Emulation.Common/CPUs/CP1610/Execute.cs index 6ac14990e8..73e8c5b18a 100644 --- a/BizHawk.Emulation.Common/CPUs/CP1610/Execute.cs +++ b/BizHawk.Emulation.Common/CPUs/CP1610/Execute.cs @@ -185,7 +185,7 @@ namespace BizHawk.Emulation.Common.Components.CP1610 // Unknown opcode. throw new ArgumentException(); } - RegisterPC = (ushort)addr; + RegisterPC = addr; cycles = 12; Interruptible = true; break; diff --git a/BizHawk.Emulation.Common/CPUs/HuC6280/HuC6280.cs b/BizHawk.Emulation.Common/CPUs/HuC6280/HuC6280.cs index 3f635e63c8..9dafaca5a6 100644 --- a/BizHawk.Emulation.Common/CPUs/HuC6280/HuC6280.cs +++ b/BizHawk.Emulation.Common/CPUs/HuC6280/HuC6280.cs @@ -6,398 +6,398 @@ using BizHawk.Common; namespace BizHawk.Emulation.Common.Components.H6280 { - public sealed partial class HuC6280 - { - public HuC6280() - { - Reset(); - } + public sealed partial class HuC6280 + { + public HuC6280() + { + Reset(); + } - public void Reset() - { - A = 0; - X = 0; - Y = 0; - //P = 0x14; // Set I and B - P = 0x04; // Set I - S = 0; - PC = 0; - PendingCycles = 0; - TotalExecutedCycles = 0; - LagIFlag = true; - LowSpeed = true; - } + public void Reset() + { + A = 0; + X = 0; + Y = 0; + //P = 0x14; // Set I and B + P = 0x04; // Set I + S = 0; + PC = 0; + PendingCycles = 0; + TotalExecutedCycles = 0; + LagIFlag = true; + LowSpeed = true; + } - public void ResetPC() - { - PC = ReadWord(ResetVector); - } + public void ResetPC() + { + PC = ReadWord(ResetVector); + } - // ==== CPU State ==== + // ==== CPU State ==== - public byte A; - public byte X; - public byte Y; - public byte P; - public ushort PC; - public byte S; - public byte[] MPR = new byte[8]; + public byte A; + public byte X; + public byte Y; + public byte P; + public ushort PC; + public byte S; + public byte[] MPR = new byte[8]; - public bool LagIFlag; - public bool IRQ1Assert; - public bool IRQ2Assert; - public bool TimerAssert; - public byte IRQControlByte, IRQNextControlByte; + public bool LagIFlag; + public bool IRQ1Assert; + public bool IRQ2Assert; + public bool TimerAssert; + public byte IRQControlByte, IRQNextControlByte; - public long TotalExecutedCycles; - public int PendingCycles; - public bool LowSpeed; + public long TotalExecutedCycles; + public int PendingCycles; + public bool LowSpeed; - private bool InBlockTransfer = false; - private ushort btFrom; - private ushort btTo; - private ushort btLen; - private int btAlternator; + private bool InBlockTransfer = false; + private ushort btFrom; + private ushort btTo; + private ushort btLen; + private int btAlternator; - // -- Timer Support -- + // -- Timer Support -- - public int TimerTickCounter; - public byte TimerReloadValue; - public byte TimerValue; - public bool TimerEnabled; + public int TimerTickCounter; + public byte TimerReloadValue; + public byte TimerValue; + public bool TimerEnabled; - public void SaveStateText(TextWriter writer) - { - writer.WriteLine("[HuC6280]"); - writer.WriteLine("A {0:X2}", A); - writer.WriteLine("X {0:X2}", X); - writer.WriteLine("Y {0:X2}", Y); - writer.WriteLine("P {0:X2}", P); - writer.WriteLine("PC {0:X4}", PC); - writer.WriteLine("S {0:X2}", S); - writer.Write("MPR "); - MPR.SaveAsHex(writer); - writer.WriteLine("LagIFlag {0}", LagIFlag); - writer.WriteLine("IRQ1Assert {0}", IRQ1Assert); - writer.WriteLine("IRQ2Assert {0}", IRQ2Assert); - writer.WriteLine("TimerAssert {0}", TimerAssert); - writer.WriteLine("IRQControlByte {0:X2}", IRQControlByte); - writer.WriteLine("IRQNextControlByte {0:X2}", IRQNextControlByte); - writer.WriteLine("ExecutedCycles {0}", TotalExecutedCycles); - writer.WriteLine("PendingCycles {0}", PendingCycles); - writer.WriteLine("LowSpeed {0}", LowSpeed); - writer.WriteLine("TimerTickCounter {0}", TimerTickCounter); - writer.WriteLine("TimerReloadValue {0}", TimerReloadValue); - writer.WriteLine("TimerValue {0}", TimerValue); - writer.WriteLine("TimerEnabled {0}", TimerEnabled); - writer.WriteLine("InBlockTransfer {0}", InBlockTransfer); - writer.WriteLine("BTFrom {0}", btFrom); - writer.WriteLine("BTTo {0}", btTo); - writer.WriteLine("BTLen {0}", btLen); - writer.WriteLine("BTAlternator {0}", btAlternator); - writer.WriteLine("[/HuC6280]\n"); - } + public void SaveStateText(TextWriter writer) + { + writer.WriteLine("[HuC6280]"); + writer.WriteLine("A {0:X2}", A); + writer.WriteLine("X {0:X2}", X); + writer.WriteLine("Y {0:X2}", Y); + writer.WriteLine("P {0:X2}", P); + writer.WriteLine("PC {0:X4}", PC); + writer.WriteLine("S {0:X2}", S); + writer.Write("MPR "); + MPR.SaveAsHex(writer); + writer.WriteLine("LagIFlag {0}", LagIFlag); + writer.WriteLine("IRQ1Assert {0}", IRQ1Assert); + writer.WriteLine("IRQ2Assert {0}", IRQ2Assert); + writer.WriteLine("TimerAssert {0}", TimerAssert); + writer.WriteLine("IRQControlByte {0:X2}", IRQControlByte); + writer.WriteLine("IRQNextControlByte {0:X2}", IRQNextControlByte); + writer.WriteLine("ExecutedCycles {0}", TotalExecutedCycles); + writer.WriteLine("PendingCycles {0}", PendingCycles); + writer.WriteLine("LowSpeed {0}", LowSpeed); + writer.WriteLine("TimerTickCounter {0}", TimerTickCounter); + writer.WriteLine("TimerReloadValue {0}", TimerReloadValue); + writer.WriteLine("TimerValue {0}", TimerValue); + writer.WriteLine("TimerEnabled {0}", TimerEnabled); + writer.WriteLine("InBlockTransfer {0}", InBlockTransfer); + writer.WriteLine("BTFrom {0}", btFrom); + writer.WriteLine("BTTo {0}", btTo); + writer.WriteLine("BTLen {0}", btLen); + writer.WriteLine("BTAlternator {0}", btAlternator); + writer.WriteLine("[/HuC6280]\n"); + } - public void LoadStateText(TextReader reader) - { - while (true) - { - string[] args = reader.ReadLine().Split(' '); - if (args[0].Trim() == "") continue; - if (args[0] == "[/HuC6280]") break; - if (args[0] == "A") - A = byte.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "X") - X = byte.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "Y") - Y = byte.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "P") - P = byte.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "PC") - PC = ushort.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "S") - S = byte.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "MPR") - MPR.ReadFromHex(args[1]); - else if (args[0] == "LagIFlag") - LagIFlag = bool.Parse(args[1]); - else if (args[0] == "IRQ1Assert") - IRQ1Assert = bool.Parse(args[1]); - else if (args[0] == "IRQ2Assert") - IRQ2Assert = bool.Parse(args[1]); - else if (args[0] == "TimerAssert") - TimerAssert = bool.Parse(args[1]); - else if (args[0] == "IRQControlByte") - IRQControlByte = byte.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "IRQNextControlByte") - IRQNextControlByte = byte.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "ExecutedCycles") - TotalExecutedCycles = long.Parse(args[1]); - else if (args[0] == "PendingCycles") - PendingCycles = int.Parse(args[1]); - else if (args[0] == "LowSpeed") - LowSpeed = bool.Parse(args[1]); - else if (args[0] == "TimerTickCounter") - TimerTickCounter = int.Parse(args[1]); - else if (args[0] == "TimerReloadValue") - TimerReloadValue = byte.Parse(args[1]); - else if (args[0] == "TimerValue") - TimerValue = byte.Parse(args[1]); - else if (args[0] == "TimerEnabled") - TimerEnabled = bool.Parse(args[1]); - else if (args[0] == "InBlockTransfer") - InBlockTransfer = bool.Parse(args[1]); - else if (args[0] == "BTFrom") - btFrom = ushort.Parse(args[1]); - else if (args[0] == "BTTo") - btTo = ushort.Parse(args[1]); - else if (args[0] == "BTLen") - btLen = ushort.Parse(args[1]); - else if (args[0] == "BTAlternator") - btAlternator = int.Parse(args[1]); - else - Console.WriteLine("Skipping unrecognized identifier " + args[0]); - } - } + public void LoadStateText(TextReader reader) + { + while (true) + { + string[] args = reader.ReadLine().Split(' '); + if (args[0].Trim() == "") continue; + if (args[0] == "[/HuC6280]") break; + if (args[0] == "A") + A = byte.Parse(args[1], NumberStyles.HexNumber); + else if (args[0] == "X") + X = byte.Parse(args[1], NumberStyles.HexNumber); + else if (args[0] == "Y") + Y = byte.Parse(args[1], NumberStyles.HexNumber); + else if (args[0] == "P") + P = byte.Parse(args[1], NumberStyles.HexNumber); + else if (args[0] == "PC") + PC = ushort.Parse(args[1], NumberStyles.HexNumber); + else if (args[0] == "S") + S = byte.Parse(args[1], NumberStyles.HexNumber); + else if (args[0] == "MPR") + MPR.ReadFromHex(args[1]); + else if (args[0] == "LagIFlag") + LagIFlag = bool.Parse(args[1]); + else if (args[0] == "IRQ1Assert") + IRQ1Assert = bool.Parse(args[1]); + else if (args[0] == "IRQ2Assert") + IRQ2Assert = bool.Parse(args[1]); + else if (args[0] == "TimerAssert") + TimerAssert = bool.Parse(args[1]); + else if (args[0] == "IRQControlByte") + IRQControlByte = byte.Parse(args[1], NumberStyles.HexNumber); + else if (args[0] == "IRQNextControlByte") + IRQNextControlByte = byte.Parse(args[1], NumberStyles.HexNumber); + else if (args[0] == "ExecutedCycles") + TotalExecutedCycles = long.Parse(args[1]); + else if (args[0] == "PendingCycles") + PendingCycles = int.Parse(args[1]); + else if (args[0] == "LowSpeed") + LowSpeed = bool.Parse(args[1]); + else if (args[0] == "TimerTickCounter") + TimerTickCounter = int.Parse(args[1]); + else if (args[0] == "TimerReloadValue") + TimerReloadValue = byte.Parse(args[1]); + else if (args[0] == "TimerValue") + TimerValue = byte.Parse(args[1]); + else if (args[0] == "TimerEnabled") + TimerEnabled = bool.Parse(args[1]); + else if (args[0] == "InBlockTransfer") + InBlockTransfer = bool.Parse(args[1]); + else if (args[0] == "BTFrom") + btFrom = ushort.Parse(args[1]); + else if (args[0] == "BTTo") + btTo = ushort.Parse(args[1]); + else if (args[0] == "BTLen") + btLen = ushort.Parse(args[1]); + else if (args[0] == "BTAlternator") + btAlternator = int.Parse(args[1]); + else + Console.WriteLine("Skipping unrecognized identifier " + args[0]); + } + } - public void SaveStateBinary(BinaryWriter writer) - { - writer.Write(A); - writer.Write(X); - writer.Write(Y); - writer.Write(P); - writer.Write(PC); - writer.Write(S); - writer.Write(MPR); - writer.Write(LagIFlag); - writer.Write(IRQ1Assert); - writer.Write(IRQ2Assert); - writer.Write(TimerAssert); - writer.Write(IRQControlByte); - writer.Write(IRQNextControlByte); - writer.Write(TotalExecutedCycles); - writer.Write(PendingCycles); - writer.Write(LowSpeed); + public void SaveStateBinary(BinaryWriter writer) + { + writer.Write(A); + writer.Write(X); + writer.Write(Y); + writer.Write(P); + writer.Write(PC); + writer.Write(S); + writer.Write(MPR); + writer.Write(LagIFlag); + writer.Write(IRQ1Assert); + writer.Write(IRQ2Assert); + writer.Write(TimerAssert); + writer.Write(IRQControlByte); + writer.Write(IRQNextControlByte); + writer.Write(TotalExecutedCycles); + writer.Write(PendingCycles); + writer.Write(LowSpeed); - writer.Write(TimerTickCounter); - writer.Write(TimerReloadValue); - writer.Write(TimerValue); - writer.Write(TimerEnabled); + writer.Write(TimerTickCounter); + writer.Write(TimerReloadValue); + writer.Write(TimerValue); + writer.Write(TimerEnabled); - writer.Write(InBlockTransfer); - writer.Write(btFrom); - writer.Write(btTo); - writer.Write(btLen); - writer.Write((byte)btAlternator); - } + writer.Write(InBlockTransfer); + writer.Write(btFrom); + writer.Write(btTo); + writer.Write(btLen); + writer.Write((byte)btAlternator); + } - public void LoadStateBinary(BinaryReader reader) - { - A = reader.ReadByte(); - X = reader.ReadByte(); - Y = reader.ReadByte(); - P = reader.ReadByte(); - PC = reader.ReadUInt16(); - S = reader.ReadByte(); - MPR = reader.ReadBytes(8); - LagIFlag = reader.ReadBoolean(); - IRQ1Assert = reader.ReadBoolean(); - IRQ2Assert = reader.ReadBoolean(); - TimerAssert = reader.ReadBoolean(); - IRQControlByte = reader.ReadByte(); - IRQNextControlByte = reader.ReadByte(); - TotalExecutedCycles = reader.ReadInt64(); - PendingCycles = reader.ReadInt32(); - LowSpeed = reader.ReadBoolean(); + public void LoadStateBinary(BinaryReader reader) + { + A = reader.ReadByte(); + X = reader.ReadByte(); + Y = reader.ReadByte(); + P = reader.ReadByte(); + PC = reader.ReadUInt16(); + S = reader.ReadByte(); + MPR = reader.ReadBytes(8); + LagIFlag = reader.ReadBoolean(); + IRQ1Assert = reader.ReadBoolean(); + IRQ2Assert = reader.ReadBoolean(); + TimerAssert = reader.ReadBoolean(); + IRQControlByte = reader.ReadByte(); + IRQNextControlByte = reader.ReadByte(); + TotalExecutedCycles = reader.ReadInt64(); + PendingCycles = reader.ReadInt32(); + LowSpeed = reader.ReadBoolean(); - TimerTickCounter = reader.ReadInt32(); - TimerReloadValue = reader.ReadByte(); - TimerValue = reader.ReadByte(); - TimerEnabled = reader.ReadBoolean(); + TimerTickCounter = reader.ReadInt32(); + TimerReloadValue = reader.ReadByte(); + TimerValue = reader.ReadByte(); + TimerEnabled = reader.ReadBoolean(); - InBlockTransfer = reader.ReadBoolean(); - btFrom = reader.ReadUInt16(); - btTo = reader.ReadUInt16(); - btLen = reader.ReadUInt16(); - btAlternator = reader.ReadByte(); - } + InBlockTransfer = reader.ReadBoolean(); + btFrom = reader.ReadUInt16(); + btTo = reader.ReadUInt16(); + btLen = reader.ReadUInt16(); + btAlternator = reader.ReadByte(); + } - // ==== Interrupts ==== + // ==== Interrupts ==== - private const ushort ResetVector = 0xFFFE; - private const ushort NMIVector = 0xFFFC; - private const ushort TimerVector = 0xFFFA; - private const ushort IRQ1Vector = 0xFFF8; - private const ushort IRQ2Vector = 0xFFF6; + private const ushort ResetVector = 0xFFFE; + private const ushort NMIVector = 0xFFFC; + private const ushort TimerVector = 0xFFFA; + private const ushort IRQ1Vector = 0xFFF8; + private const ushort IRQ2Vector = 0xFFF6; - private const byte IRQ2Selector = 0x01; - private const byte IRQ1Selector = 0x02; - private const byte TimerSelector = 0x04; + private const byte IRQ2Selector = 0x01; + private const byte IRQ1Selector = 0x02; + private const byte TimerSelector = 0x04; - public void WriteIrqControl(byte value) - { - // There is a single-instruction delay before writes to the IRQ Control Byte take effect. - value &= 7; - IRQNextControlByte = value; - } + public void WriteIrqControl(byte value) + { + // There is a single-instruction delay before writes to the IRQ Control Byte take effect. + value &= 7; + IRQNextControlByte = value; + } - public void WriteIrqStatus() - { - TimerAssert = false; - } + public void WriteIrqStatus() + { + TimerAssert = false; + } - public byte ReadIrqStatus() - { - byte status = 0; - if (IRQ2Assert) status |= 1; - if (IRQ1Assert) status |= 2; - if (TimerAssert) status |= 4; - return status; - } + public byte ReadIrqStatus() + { + byte status = 0; + if (IRQ2Assert) status |= 1; + if (IRQ1Assert) status |= 2; + if (TimerAssert) status |= 4; + return status; + } - public void WriteTimer(byte value) - { - value &= 0x7F; - TimerReloadValue = value; - } + public void WriteTimer(byte value) + { + value &= 0x7F; + TimerReloadValue = value; + } - public void WriteTimerEnable(byte value) - { - if (TimerEnabled == false && (value & 1) == 1) - { - TimerValue = TimerReloadValue; // timer value is reset when toggled from off to on - TimerTickCounter = 0; - } - TimerEnabled = (value & 1) == 1; - } + public void WriteTimerEnable(byte value) + { + if (TimerEnabled == false && (value & 1) == 1) + { + TimerValue = TimerReloadValue; // timer value is reset when toggled from off to on + TimerTickCounter = 0; + } + TimerEnabled = (value & 1) == 1; + } - public byte ReadTimerValue() - { - if (TimerTickCounter + 5 > 1024) - { - // There exists a slight delay between when the timer counter is decremented and when - // the interrupt fires; games can detect it, so we hack it this way. - return (byte) ((TimerValue - 1) & 0x7F); - } - return TimerValue; - } + public byte ReadTimerValue() + { + if (TimerTickCounter + 5 > 1024) + { + // There exists a slight delay between when the timer counter is decremented and when + // the interrupt fires; games can detect it, so we hack it this way. + return (byte)((TimerValue - 1) & 0x7F); + } + return TimerValue; + } - // ==== Flags ==== + // ==== Flags ==== - /// Carry Flag - private bool FlagC - { - get { return (P & 0x01) != 0; } - set { P = (byte)((P & ~0x01) | (value ? 0x01 : 0x00)); } - } + /// Carry Flag + private bool FlagC + { + get { return (P & 0x01) != 0; } + set { P = (byte)((P & ~0x01) | (value ? 0x01 : 0x00)); } + } - /// Zero Flag - private bool FlagZ - { - get { return (P & 0x02) != 0; } - set { P = (byte)((P & ~0x02) | (value ? 0x02 : 0x00)); } - } + /// Zero Flag + private bool FlagZ + { + get { return (P & 0x02) != 0; } + set { P = (byte)((P & ~0x02) | (value ? 0x02 : 0x00)); } + } - /// Interrupt Disable Flag - private bool FlagI - { - get { return (P & 0x04) != 0; } - set { P = (byte)((P & ~0x04) | (value ? 0x04 : 0x00)); } - } + /// Interrupt Disable Flag + private bool FlagI + { + get { return (P & 0x04) != 0; } + set { P = (byte)((P & ~0x04) | (value ? 0x04 : 0x00)); } + } - /// Decimal Mode Flag - private bool FlagD - { - get { return (P & 0x08) != 0; } - set { P = (byte)((P & ~0x08) | (value ? 0x08 : 0x00)); } - } + /// Decimal Mode Flag + private bool FlagD + { + get { return (P & 0x08) != 0; } + set { P = (byte)((P & ~0x08) | (value ? 0x08 : 0x00)); } + } - /// Break Flag - private bool FlagB - { - get { return (P & 0x10) != 0; } - set { P = (byte)((P & ~0x10) | (value ? 0x10 : 0x00)); } - } + /// Break Flag + private bool FlagB + { + get { return (P & 0x10) != 0; } + set { P = (byte)((P & ~0x10) | (value ? 0x10 : 0x00)); } + } - /// T... Flag - private bool FlagT - { - get { return (P & 0x20) != 0; } - set { P = (byte)((P & ~0x20) | (value ? 0x20 : 0x00)); } - } + /// T... Flag + private bool FlagT + { + get { return (P & 0x20) != 0; } + set { P = (byte)((P & ~0x20) | (value ? 0x20 : 0x00)); } + } - /// Overflow Flag - private bool FlagV - { - get { return (P & 0x40) != 0; } - set { P = (byte)((P & ~0x40) | (value ? 0x40 : 0x00)); } - } + /// Overflow Flag + private bool FlagV + { + get { return (P & 0x40) != 0; } + set { P = (byte)((P & ~0x40) | (value ? 0x40 : 0x00)); } + } - /// Negative Flag - private bool FlagN - { - get { return (P & 0x80) != 0; } - set { P = (byte)((P & ~0x80) | (value ? 0x80 : 0x00)); } - } + /// Negative Flag + private bool FlagN + { + get { return (P & 0x80) != 0; } + set { P = (byte)((P & ~0x80) | (value ? 0x80 : 0x00)); } + } - // ==== Memory ==== + // ==== Memory ==== - public Func ReadMemory21; - public Action WriteMemory21; - public Action WriteVDC; - public Action ThinkAction = delegate { }; + public Func ReadMemory21; + public Action WriteMemory21; + public Action WriteVDC; + public Action ThinkAction = delegate { }; - public byte ReadMemory(ushort address) - { - byte page = MPR[address >> 13]; - return ReadMemory21((page << 13) | (address & 0x1FFF)); - } + public byte ReadMemory(ushort address) + { + byte page = MPR[address >> 13]; + return ReadMemory21((page << 13) | (address & 0x1FFF)); + } - public void WriteMemory(ushort address, byte value) - { - byte page = MPR[address >> 13]; - WriteMemory21((page << 13) | (address & 0x1FFF), value); - } + public void WriteMemory(ushort address, byte value) + { + byte page = MPR[address >> 13]; + WriteMemory21((page << 13) | (address & 0x1FFF), value); + } - private ushort ReadWord(ushort address) - { - byte l = ReadMemory(address); - byte h = ReadMemory(++address); - return (ushort)((h << 8) | l); - } + private ushort ReadWord(ushort address) + { + byte l = ReadMemory(address); + byte h = ReadMemory(++address); + return (ushort)((h << 8) | l); + } - private void WriteWord(ushort address, ushort value) - { - byte l = (byte)(value & 0xFF); - byte h = (byte)(value >> 8); - WriteMemory(address, l); - WriteMemory(++address, h); - } + private void WriteWord(ushort address, ushort value) + { + byte l = (byte)(value & 0xFF); + byte h = (byte)(value >> 8); + WriteMemory(address, l); + WriteMemory(++address, h); + } - private ushort ReadWordPageWrap(ushort address) - { - ushort highAddress = (ushort)((address & 0xFF00) + ((address + 1) & 0xFF)); - return (ushort)(ReadMemory(address) | (ReadMemory(highAddress) << 8)); - } + private ushort ReadWordPageWrap(ushort address) + { + ushort highAddress = (ushort)((address & 0xFF00) + ((address + 1) & 0xFF)); + return (ushort)(ReadMemory(address) | (ReadMemory(highAddress) << 8)); + } - public string State() - { - int notused; - string a = string.Format("{0:X4} {1:X2} {2} ", PC, ReadMemory(PC), Disassemble(PC, out notused)).PadRight(41); - string b = string.Format("A:{0:X2} X:{1:X2} Y:{2:X2} P:{3:X2} SP:{4:X2} Cy:{5}", A, X, Y, P, S, TotalExecutedCycles); - string val = a + b + " "; - if (FlagN) val = val + "N"; - if (FlagV) val = val + "V"; - if (FlagT) val = val + "T"; - if (FlagB) val = val + "B"; - if (FlagD) val = val + "D"; - if (FlagI) val = val + "I"; - if (FlagZ) val = val + "Z"; - if (FlagC) val = val + "C"; - return val; - } + public string State() + { + int notused; + string a = string.Format("{0:X4} {1:X2} {2} ", PC, ReadMemory(PC), Disassemble(PC, out notused)).PadRight(41); + string b = string.Format("A:{0:X2} X:{1:X2} Y:{2:X2} P:{3:X2} SP:{4:X2} Cy:{5}", A, X, Y, P, S, TotalExecutedCycles); + string val = a + b + " "; + if (FlagN) val = val + "N"; + if (FlagV) val = val + "V"; + if (FlagT) val = val + "T"; + if (FlagB) val = val + "B"; + if (FlagD) val = val + "D"; + if (FlagI) val = val + "I"; + if (FlagZ) val = val + "Z"; + if (FlagC) val = val + "C"; + return val; + } - private static readonly byte[] TableNZ = + private static readonly byte[] TableNZ = { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -416,5 +416,5 @@ namespace BizHawk.Emulation.Common.Components.H6280 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }; - } + } } \ No newline at end of file diff --git a/BizHawk.Emulation.Common/CPUs/MOS 6502X/MOS6502X.cs b/BizHawk.Emulation.Common/CPUs/MOS 6502X/MOS6502X.cs index 08bb2f02f5..89dc29371f 100644 --- a/BizHawk.Emulation.Common/CPUs/MOS 6502X/MOS6502X.cs +++ b/BizHawk.Emulation.Common/CPUs/MOS 6502X/MOS6502X.cs @@ -29,7 +29,7 @@ namespace BizHawk.Emulation.Common.Components.M6502 mi = 0; opcode = 256; iflag_pending = true; - RDY = true; + RDY = true; } public void NESSoftReset() @@ -220,23 +220,23 @@ namespace BizHawk.Emulation.Common.Components.M6502 } private static readonly byte[] TableNZ = - { - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 - }; + { + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 + }; } } \ No newline at end of file diff --git a/BizHawk.Emulation.Common/CPUs/MOS 6502X/MOS6502XDouble.cs b/BizHawk.Emulation.Common/CPUs/MOS 6502X/MOS6502XDouble.cs index 90004aa9bc..f9469469a0 100644 --- a/BizHawk.Emulation.Common/CPUs/MOS 6502X/MOS6502XDouble.cs +++ b/BizHawk.Emulation.Common/CPUs/MOS 6502X/MOS6502XDouble.cs @@ -12,8 +12,8 @@ namespace BizHawk.Emulation.Common.Components.M6502 /// public class MOS6502XDouble { - MOS6502X m; - MOS6502X_CPP n; + readonly MOS6502X m; + readonly MOS6502X_CPP n; public MOS6502XDouble(Action DisposeBuilder) { diff --git a/BizHawk.Emulation.Common/CPUs/MOS 6502X/MOS6502XNative.cs b/BizHawk.Emulation.Common/CPUs/MOS 6502X/MOS6502XNative.cs index 774116d2fa..ca4cb32d68 100644 --- a/BizHawk.Emulation.Common/CPUs/MOS 6502X/MOS6502XNative.cs +++ b/BizHawk.Emulation.Common/CPUs/MOS 6502X/MOS6502XNative.cs @@ -7,27 +7,27 @@ namespace BizHawk.Emulation.Common.Components.M6502 { public static class MOS6502X_DLL { - [UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.Cdecl)] + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate byte ReadMemoryD(ushort addr); - [UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.Cdecl)] + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void WriteMemoryD(ushort addr, byte value); [DllImport("MOS6502XNative.dll", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr Create(); + internal static extern IntPtr Create(); [DllImport("MOS6502XNative.dll", CallingConvention = CallingConvention.Cdecl)] - public static extern void Destroy(IntPtr ptr); + internal static extern void Destroy(IntPtr ptr); [DllImport("MOS6502XNative.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint = "?Reset@MOS6502X@@QAEXXZ")] - public static extern void Reset(IntPtr ptr); + internal static extern void Reset(IntPtr ptr); [DllImport("MOS6502XNative.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint = "?NESSoftReset@MOS6502X@@QAEXXZ")] - public static extern void NESSoftReset(IntPtr ptr); + internal static extern void NESSoftReset(IntPtr ptr); [DllImport("MOS6502XNative.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint = "?ExecuteOne@MOS6502X@@QAEXXZ")] - public static extern void ExecuteOne(IntPtr ptr); + internal static extern void ExecuteOne(IntPtr ptr); [DllImport("MOS6502XNative.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint = "?SetTrampolines@MOS6502X@@QAEXP6AEG@Z0P6AXGE@Z@Z")] - public static extern void SetTrampolines(IntPtr ptr, ReadMemoryD Read, ReadMemoryD DummyRead, WriteMemoryD Write); + internal static extern void SetTrampolines(IntPtr ptr, ReadMemoryD Read, ReadMemoryD DummyRead, WriteMemoryD Write); } /// @@ -213,8 +213,6 @@ namespace BizHawk.Emulation.Common.Components.M6502 DisposeBuilder(h2); DisposeBuilder(h3); } - - } } diff --git a/BizHawk.Emulation.Common/Interfaces/CoreComms.cs b/BizHawk.Emulation.Common/Interfaces/CoreComms.cs index 0a1a689d55..d8113525b8 100644 --- a/BizHawk.Emulation.Common/Interfaces/CoreComms.cs +++ b/BizHawk.Emulation.Common/Interfaces/CoreComms.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reflection; using System.Text; namespace BizHawk.Emulation.Common @@ -123,7 +122,7 @@ namespace BizHawk.Emulation.Common public class InputCallbackSystem { - private List _list = new List(); + private readonly List _list = new List(); public void Add(Action action) { @@ -159,14 +158,14 @@ namespace BizHawk.Emulation.Common public class MemoryCallbackSystem { - private List _reads = new List(); - private List _readAddrs = new List(); + private readonly List _reads = new List(); + private readonly List _readAddrs = new List(); - private List _writes = new List(); - private List _writeAddrs = new List(); + private readonly List _writes = new List(); + private readonly List _writeAddrs = new List(); - private List _executes = new List(); - private List _execAddrs = new List(); + private readonly List _executes = new List(); + private readonly List _execAddrs = new List(); public void AddRead(Action function, uint? addr) { diff --git a/BizHawk.Emulation.Common/MemoryDomain.cs b/BizHawk.Emulation.Common/MemoryDomain.cs index 5bcdd280a0..5c965355e6 100644 --- a/BizHawk.Emulation.Common/MemoryDomain.cs +++ b/BizHawk.Emulation.Common/MemoryDomain.cs @@ -105,7 +105,7 @@ namespace BizHawk.Emulation.Common public class MemoryDomainList : ReadOnlyCollection { - private int _mainMemoryIndex = 0; + private readonly int _mainMemoryIndex; public MemoryDomainList(IList domains) : base(domains) diff --git a/BizHawk.Emulation.Common/Sound/HuC6280PSG.cs b/BizHawk.Emulation.Common/Sound/HuC6280PSG.cs index 4f50e41be1..a245d3939e 100644 --- a/BizHawk.Emulation.Common/Sound/HuC6280PSG.cs +++ b/BizHawk.Emulation.Common/Sound/HuC6280PSG.cs @@ -4,7 +4,6 @@ using System.Globalization; using System.IO; using BizHawk.Common; -using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Common.Components { @@ -30,15 +29,15 @@ namespace BizHawk.Emulation.Common.Components public PSGChannel[] Channels = new PSGChannel[8]; public byte VoiceLatch; - byte WaveTableWriteOffset; + private byte WaveTableWriteOffset; - Queue commands = new Queue(256); - long frameStartTime, frameStopTime; + private readonly Queue commands = new Queue(256); + private long frameStartTime, frameStopTime; const int SampleRate = 44100; const int PsgBase = 3580000; - static byte[] LogScale = { 0, 0, 10, 10, 13, 13, 16, 16, 20, 20, 26, 26, 32, 32, 40, 40, 51, 51, 64, 64, 81, 81, 102, 102, 128, 128, 161, 161, 203, 203, 255, 255 }; - static byte[] VolumeReductionTable = { 0x1F, 0x1D, 0x1B, 0x19, 0x17, 0x15, 0x13, 0x10, 0x0F, 0x0D, 0x0B, 0x09, 0x07, 0x05, 0x03, 0x00 }; + static readonly byte[] LogScale = { 0, 0, 10, 10, 13, 13, 16, 16, 20, 20, 26, 26, 32, 32, 40, 40, 51, 51, 64, 64, 81, 81, 102, 102, 128, 128, 161, 161, 203, 203, 255, 255 }; + static readonly byte[] VolumeReductionTable = { 0x1F, 0x1D, 0x1B, 0x19, 0x17, 0x15, 0x13, 0x10, 0x0F, 0x0D, 0x0B, 0x09, 0x07, 0x05, 0x03, 0x00 }; public byte MainVolumeLeft; public byte MainVolumeRight; @@ -49,7 +48,9 @@ namespace BizHawk.Emulation.Common.Components MaxVolume = short.MaxValue; Waves.InitWaves(); for (int i = 0; i < 8; i++) + { Channels[i] = new PSGChannel(); + } } public void BeginFrame(long cycles) diff --git a/BizHawk.Emulation.Common/Sound/MMC5Audio.cs b/BizHawk.Emulation.Common/Sound/MMC5Audio.cs index d6b8883761..b773e56bfd 100644 --- a/BizHawk.Emulation.Common/Sound/MMC5Audio.cs +++ b/BizHawk.Emulation.Common/Sound/MMC5Audio.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - using BizHawk.Common; namespace BizHawk.Emulation.Common.Components @@ -12,19 +8,19 @@ namespace BizHawk.Emulation.Common.Components class Pulse { // regs - int V; - int T; - int L; - int D; - bool LenCntDisable; - bool ConstantVolume; - bool Enable; + private int V; + private int T; + private int L; + private int D; + private bool LenCntDisable; + private bool ConstantVolume; + private bool Enable; // envelope - bool estart; - int etime; - int ecount; + private bool estart; + private int etime; + private int ecount; // length - static int[] lenlookup = + private static readonly int[] lenlookup = { 10,254, 20, 2, 40, 4, 80, 6, 160, 8, 60, 10, 14, 12, 26, 14, 12, 16, 24, 18, 48, 20, 96, 22, 192, 24, 72, 26, 16, 28, 32, 30 @@ -32,18 +28,18 @@ namespace BizHawk.Emulation.Common.Components int length; // pulse - int sequence; - static int[,] sequencelookup = + private int sequence; + private static readonly int[,] sequencelookup = { {0,0,0,0,0,0,0,1}, {0,0,0,0,0,0,1,1}, {0,0,0,0,1,1,1,1}, {1,1,1,1,1,1,0,0} }; - int clock; - int output; + private int clock; + private int output; - public Action SendDiff; + private readonly Action SendDiff; public Pulse(Action SendDiff) { @@ -165,7 +161,7 @@ namespace BizHawk.Emulation.Common.Components } } - Pulse[] pulse = new Pulse[2]; + private readonly Pulse[] pulse = new Pulse[2]; /// /// @@ -245,15 +241,15 @@ namespace BizHawk.Emulation.Common.Components RaiseIRQ(PCMEnableIRQ && PCMIRQTriggered); } - Action RaiseIRQ; + private readonly Action RaiseIRQ; const int framereload = 7458; // ??? - int frame = 0; - bool PCMRead; - bool PCMEnableIRQ; - bool PCMIRQTriggered; - byte PCMVal; - byte PCMNextVal; + private int frame; + private bool PCMRead; + private bool PCMEnableIRQ; + private bool PCMIRQTriggered; + private byte PCMVal; + private byte PCMNextVal; public void SyncState(Serializer ser) { @@ -288,14 +284,14 @@ namespace BizHawk.Emulation.Common.Components } if (PCMNextVal != PCMVal) { - enqueuer(20 * (int)(PCMVal - PCMNextVal)); + enqueuer(20 * (PCMVal - PCMNextVal)); PCMVal = PCMNextVal; } } - Action enqueuer; + private readonly Action enqueuer; - void PulseAddDiff(int value) + private void PulseAddDiff(int value) { enqueuer(value * 370); //Console.WriteLine(value); diff --git a/BizHawk.Emulation.Common/Sound/SN76489.cs b/BizHawk.Emulation.Common/Sound/SN76489.cs index b0b4cd1e9d..067e5731ec 100644 --- a/BizHawk.Emulation.Common/Sound/SN76489.cs +++ b/BizHawk.Emulation.Common/Sound/SN76489.cs @@ -3,8 +3,6 @@ using System.Collections.Generic; using System.Globalization; using System.IO; -using BizHawk.Emulation.Common; - // TODO the freq->note translation should be moved to a separate utility class. namespace BizHawk.Emulation.Common.Components @@ -26,7 +24,7 @@ namespace BizHawk.Emulation.Common.Components public bool Right = true; const int SampleRate = 44100; - static byte[] LogScale = { 0, 10, 13, 16, 20, 26, 32, 40, 51, 64, 81, 102, 128, 161, 203, 255 }; + private static readonly byte[] LogScale = { 0, 10, 13, 16, 20, 26, 32, 40, 51, 64, 81, 102, 128, 161, 203, 255 }; public void Mix(short[] samples, int start, int len, int maxVolume) { @@ -52,7 +50,7 @@ namespace BizHawk.Emulation.Common.Components public Channel[] Channels = new Channel[4]; public byte PsgLatch; - Queue commands = new Queue(256); + private readonly Queue commands = new Queue(256); int frameStartTime, frameStopTime; const int PsgBase = 111861; diff --git a/BizHawk.Emulation.Common/Sound/Sunsoft5BAudio.cs b/BizHawk.Emulation.Common/Sound/Sunsoft5BAudio.cs index 5e7ac5912d..0e0d8d238d 100644 --- a/BizHawk.Emulation.Common/Sound/Sunsoft5BAudio.cs +++ b/BizHawk.Emulation.Common/Sound/Sunsoft5BAudio.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - using BizHawk.Common; namespace BizHawk.Emulation.Common.Components @@ -16,15 +12,15 @@ namespace BizHawk.Emulation.Common.Components { class Pulse { - Action SendDiff; + private readonly Action SendDiff; // regs - int Period; - bool Disable; - int Volume; + private int Period; + private bool Disable; + private int Volume; // state - int clock; - int sequence; - int output; + private int clock; + private int sequence; + private int output; public void SyncState(Serializer ser) { @@ -45,7 +41,10 @@ namespace BizHawk.Emulation.Common.Components { int newout = 1; if (!Disable) + { newout = sequence; + } + newout *= Volume; if (newout != output) { @@ -87,7 +86,7 @@ namespace BizHawk.Emulation.Common.Components } int RegNum; - Pulse[] pulse = new Pulse[3]; + readonly Pulse[] pulse = new Pulse[3]; public void RegSelect(byte val) { @@ -132,8 +131,8 @@ namespace BizHawk.Emulation.Common.Components ser.EndSection(); } - Action enqueuer; - void PulseAddDiff(int val) + private readonly Action enqueuer; + private void PulseAddDiff(int val) { enqueuer(val * 250); } @@ -142,13 +141,17 @@ namespace BizHawk.Emulation.Common.Components { 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(); + foreach (Pulse p in pulse) + { + p.Clock(); + } } } } diff --git a/BizHawk.Emulation.Common/Sound/Utilities/BlipBuffer.cs b/BizHawk.Emulation.Common/Sound/Utilities/BlipBuffer.cs index dde87a94ee..b5bf6d0bd2 100644 --- a/BizHawk.Emulation.Common/Sound/Utilities/BlipBuffer.cs +++ b/BizHawk.Emulation.Common/Sound/Utilities/BlipBuffer.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Runtime.InteropServices; namespace BizHawk.Emulation.Common @@ -9,7 +6,7 @@ namespace BizHawk.Emulation.Common /// /// wrapper around blargg's unmanaged blip_buf /// - public class BlipBuffer : IDisposable + public sealed class BlipBuffer : IDisposable { // this is transitional only. if the band-limited synthesis idea works out, i'll // make a managed MIT implementation diff --git a/BizHawk.Emulation.Common/Sound/Utilities/BufferedAsync.cs b/BizHawk.Emulation.Common/Sound/Utilities/BufferedAsync.cs index 169b3883f6..dc9c453dad 100644 --- a/BizHawk.Emulation.Common/Sound/Utilities/BufferedAsync.cs +++ b/BizHawk.Emulation.Common/Sound/Utilities/BufferedAsync.cs @@ -1,7 +1,5 @@ using System.Collections.Generic; -using BizHawk.Emulation.Common; - namespace BizHawk.Emulation.Common { // Generates SEMI-synchronous sound, or "buffered asynchronous" sound. @@ -34,10 +32,10 @@ namespace BizHawk.Emulation.Common { public ISoundProvider BaseSoundProvider; - Queue buffer = new Queue(4096); + readonly Queue buffer = new Queue(4096); - int SamplesInOneFrame = 1470; - int TargetExtraSamples = 882; + private int SamplesInOneFrame = 1470; + private int TargetExtraSamples = 882; const int MaxExcessSamples = 4096; /// @@ -72,11 +70,15 @@ namespace BizHawk.Emulation.Common BaseSoundProvider.GetSamples(mySamples); - for (int i = 0; i < mySamples.Length; i++) - buffer.Enqueue(mySamples[i]); + foreach (short s in mySamples) + { + buffer.Enqueue(s); + } for (int i = 0; i < samples.Length; i++) + { samples[i] = buffer.Dequeue(); + } } } } \ No newline at end of file diff --git a/BizHawk.Emulation.Common/Sound/Utilities/DCFilter.cs b/BizHawk.Emulation.Common/Sound/Utilities/DCFilter.cs index 7675bd63cd..b1fa405502 100644 --- a/BizHawk.Emulation.Common/Sound/Utilities/DCFilter.cs +++ b/BizHawk.Emulation.Common/Sound/Utilities/DCFilter.cs @@ -1,16 +1,11 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Common { /// /// implements a DC block filter on top of an ISoundProvider. rather simple. /// - public class DCFilter : ISoundProvider, ISyncSoundProvider + sealed public class DCFilter : ISoundProvider, ISyncSoundProvider { /* * A note about accuracy: @@ -78,7 +73,7 @@ namespace BizHawk.Emulation.Common PushThroughSamples(samples, samples, length); } - void PushThroughSamples(short[] samplesin, short[] samplesout, int length) + private void PushThroughSamples(short[] samplesin, short[] samplesout, int length) { for (int i = 0; i < length; i += 2) { diff --git a/BizHawk.Emulation.Common/Sound/Utilities/Equalizer.cs b/BizHawk.Emulation.Common/Sound/Utilities/Equalizer.cs index 0a0c1c6618..8683c65bab 100644 --- a/BizHawk.Emulation.Common/Sound/Utilities/Equalizer.cs +++ b/BizHawk.Emulation.Common/Sound/Utilities/Equalizer.cs @@ -1,9 +1,6 @@ // C# port of C-based 3-band equalizer (C) Neil C / Etanza Systems / 2006 using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace BizHawk.Emulation.Common { diff --git a/BizHawk.Emulation.Common/Sound/Utilities/Metaspu.cs b/BizHawk.Emulation.Common/Sound/Utilities/Metaspu.cs index 41830fe1ff..493a962f6b 100644 --- a/BizHawk.Emulation.Common/Sound/Utilities/Metaspu.cs +++ b/BizHawk.Emulation.Common/Sound/Utilities/Metaspu.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using BizHawk.Emulation.Common; - namespace BizHawk.Emulation.Common { /// @@ -10,8 +8,8 @@ namespace BizHawk.Emulation.Common /// public class MetaspuAsync : ISoundProvider { - ISynchronizingAudioBuffer buffer; - ISyncSoundProvider input; + private readonly ISynchronizingAudioBuffer buffer; + private readonly ISyncSoundProvider input; public MetaspuAsync(ISyncSoundProvider input, ESynchMethod method) { buffer = Metaspu.metaspu_construct(method); @@ -49,7 +47,7 @@ namespace BizHawk.Emulation.Common { } - short[] pullBuffer = new short[1470]; + private readonly short[] pullBuffer = new short[1470]; public void PullSamples(ISoundProvider source) { Array.Clear(pullBuffer, 0, 1470); @@ -124,7 +122,7 @@ namespace BizHawk.Emulation.Common } //adjustobuf(200,1000) - bool mixqueue_go = false; + bool mixqueue_go; public void clear() { @@ -177,7 +175,7 @@ namespace BizHawk.Emulation.Common return done; } - Adjustobuf adjustobuf; + private readonly Adjustobuf adjustobuf; class Adjustobuf { public Adjustobuf(int _minLatency, int _maxLatency) diff --git a/BizHawk.Emulation.Common/Sound/Utilities/SoundMixer.cs b/BizHawk.Emulation.Common/Sound/Utilities/SoundMixer.cs index 6a8197f730..a6adb30b43 100644 --- a/BizHawk.Emulation.Common/Sound/Utilities/SoundMixer.cs +++ b/BizHawk.Emulation.Common/Sound/Utilities/SoundMixer.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Common { @@ -7,7 +6,7 @@ namespace BizHawk.Emulation.Common public sealed class SoundMixer : ISoundProvider { - List SoundProviders; + private readonly List SoundProviders; public SoundMixer(params ISoundProvider[] soundProviders) { @@ -41,7 +40,9 @@ namespace BizHawk.Emulation.Common { int eachVolume = short.MaxValue / SoundProviders.Count; foreach (var source in SoundProviders) + { source.MaxVolume = eachVolume; + } } // Not actually supported on mixer. diff --git a/BizHawk.Emulation.Common/Sound/Utilities/SpeexResampler.cs b/BizHawk.Emulation.Common/Sound/Utilities/SpeexResampler.cs index dae26171bc..127c98caf2 100644 --- a/BizHawk.Emulation.Common/Sound/Utilities/SpeexResampler.cs +++ b/BizHawk.Emulation.Common/Sound/Utilities/SpeexResampler.cs @@ -1,11 +1,6 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Runtime.InteropServices; -using BizHawk.Emulation.Common; - namespace BizHawk.Emulation.Common { /// @@ -259,29 +254,29 @@ namespace BizHawk.Emulation.Common /// /// opaque pointer to state /// - IntPtr st = IntPtr.Zero; + private IntPtr st = IntPtr.Zero; /// /// function to call to dispatch output /// - Action drainer; + private readonly Action drainer; // TODO: this size is roughly based on how big you can make the buffer before the snes resampling (32040.5 -> 44100) gets screwed up - short[] inbuf = new short[512]; //[8192]; // [512]; + private short[] inbuf = new short[512]; //[8192]; // [512]; - short[] outbuf; + private short[] outbuf; // for ISyncSoundProvider - short[] outbuf2 = new short[16]; - int outbuf2pos = 0; + private short[] outbuf2 = new short[16]; + private int outbuf2pos = 0; // to accept an ISyncSoundProvder input - ISyncSoundProvider input; + private readonly ISyncSoundProvider input; /// /// in buffer position in samples (not sample pairs) /// - int inbufpos = 0; + private int inbufpos = 0; /// /// throw an exception based on error state @@ -327,10 +322,7 @@ namespace BizHawk.Emulation.Common if (drainer != null && input != null) throw new ArgumentException("Can't autofetch without being an ISyncSoundProvider?"); - if (drainer != null) - this.drainer = drainer; - else - this.drainer = InternalDrain; + this.drainer = drainer ?? InternalDrain; this.input = input; outbuf = new short[inbuf.Length * ratioden / rationum / 2 * 2 + 128]; diff --git a/BizHawk.Emulation.Common/Sound/VRC6Alt.cs b/BizHawk.Emulation.Common/Sound/VRC6Alt.cs index 4051088425..5ad960cec2 100644 --- a/BizHawk.Emulation.Common/Sound/VRC6Alt.cs +++ b/BizHawk.Emulation.Common/Sound/VRC6Alt.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - using BizHawk.Common; namespace BizHawk.Emulation.Common.Components diff --git a/BizHawk.Emulation.Common/Sound/YM2413.cs b/BizHawk.Emulation.Common/Sound/YM2413.cs index 6c4ce230bd..6c1298325c 100644 --- a/BizHawk.Emulation.Common/Sound/YM2413.cs +++ b/BizHawk.Emulation.Common/Sound/YM2413.cs @@ -7,7 +7,6 @@ using System; using BizHawk.Common; -using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Common.Components { diff --git a/BizHawk.Emulation.Common/Sound/YM2612.cs b/BizHawk.Emulation.Common/Sound/YM2612.cs index 838f41a112..480eb00732 100644 --- a/BizHawk.Emulation.Common/Sound/YM2612.cs +++ b/BizHawk.Emulation.Common/Sound/YM2612.cs @@ -1,532 +1,529 @@ using System; -using System.Diagnostics; using System.Collections.Generic; using System.IO; -using BizHawk.Emulation.Common; - namespace BizHawk.Emulation.Common.Components { - // ====================================================================== - // Yamaha YM2612 Emulation Core - // Primarily sourced from Nemesis's documentation on Sprite's Mind forums: - // http://gendev.spritesmind.net/forum/viewtopic.php?t=386 - // - // Notes: - // - In order to facilitate asynchronous sound generation, timer commands - // and reads are emulated immediately, while all other commands are - // queued together with a timestamp and processed at the end of the frame. - // - Commands are stretched in time to match the number of samples requested - // for the frame. For accurate, synchronous sound, simply request the correct - // number of samples for each frame. - // - Output is emulated at native output rate and downsampled (badly) to 44100hz. - // ====================================================================== - - // TODO: Finish testing Envelope generator - // TODO: maybe add guards when changing envelope parameters to immediately change envelope state (ie dont wait for EG cycle?) - // TODO: Detune - // TODO: LFO - // TODO: Switch from Perfect Operator to Accurate Operator. - // TODO: Operator1 Self-Feedback - // TODO: MEM delayed samples - // TODO: CSM mode - // TODO: SSG-EG - // TODO: Seriously, I think we need better resampling code. - // TODO: Experiment with low-pass filters, etc. - - public sealed class YM2612 : ISoundProvider - { - public readonly Channel[] Channels = { new Channel(), new Channel(), new Channel(), new Channel(), new Channel(), new Channel() }; - - public YM2612() - { - InitTimers(); - MaxVolume = short.MaxValue; - } - - // ==================================================================================== - - int frameStartClock; - int frameEndClock; - - public void BeginFrame(int clock) - { - frameStartClock = clock; - while (commands.Count > 0) - { - var cmd = commands.Dequeue(); - WriteCommand(cmd); - } - } - - public void EndFrame(int clock) - { - frameEndClock = clock; - } - - // ==================================================================================== - // YM2612 I/O - // ==================================================================================== - - public class QueuedCommand - { - public byte Part; - public byte Register; - public byte Data; - public int Clock; - } - - byte PartSelect; - byte RegisterSelect; - bool DacEnable; - byte DacValue; - - Queue commands = new Queue(); - - const int Slot1 = 0; - const int Slot2 = 2; - const int Slot3 = 1; - const int Slot4 = 3; - - public byte ReadStatus(int clock) - { - UpdateTimers(clock); - - byte retval = 0; - if (TimerATripped) retval |= 1; - if (TimerBTripped) retval |= 2; - return retval; - } - - public void Write(int addr, byte value, int clock) - { - UpdateTimers(clock); - - if (addr == 0) - { - PartSelect = 1; - RegisterSelect = value; - return; - } - else if (addr == 2) - { - PartSelect = 2; - RegisterSelect = value; - return; - } - - if (PartSelect == 1) - { - if (RegisterSelect == 0x24) { WriteTimerA_MSB_24(value, clock); return; } - if (RegisterSelect == 0x25) { WriteTimerA_LSB_25(value, clock); return; } - if (RegisterSelect == 0x26) { WriteTimerB_26(value, clock); return; } - if (RegisterSelect == 0x27) { WriteTimerControl_27(value, clock); } // we process immediately AND enqueue command for port $27. Allows accurate tracking of CH3 special modes. - } - - // If its not timer related just queue the command write - var cmd = new QueuedCommand { Part = PartSelect, Register = RegisterSelect, Data = value, Clock = clock - frameStartClock }; - commands.Enqueue(cmd); - } - - void WriteCommand(QueuedCommand cmd) - { - if (cmd.Part == 1) - Part1_WriteRegister(cmd.Register, cmd.Data); - else - Part2_WriteRegister(cmd.Register, cmd.Data); - } - - static void GetChanOpP1(byte value, out int channel, out int oper) - { - value &= 15; - switch (value) - { - case 0: channel = 0; oper = 0; return; - case 4: channel = 0; oper = 2; return; - case 8: channel = 0; oper = 1; return; - case 12: channel = 0; oper = 3; return; - - case 1: channel = 1; oper = 0; return; - case 5: channel = 1; oper = 2; return; - case 9: channel = 1; oper = 1; return; - case 13: channel = 1; oper = 3; return; - - case 2: channel = 2; oper = 0; return; - case 6: channel = 2; oper = 2; return; - case 10: channel = 2; oper = 1; return; - case 14: channel = 2; oper = 3; return; - - default: channel = -1; oper = -1; return; - } - } - - static void GetChanOpP2(byte value, out int channel, out int oper) - { - value &= 15; - switch (value) - { - case 0: channel = 3; oper = 0; return; - case 4: channel = 3; oper = 2; return; - case 8: channel = 3; oper = 1; return; - case 12: channel = 3; oper = 3; return; - - case 1: channel = 4; oper = 0; return; - case 5: channel = 4; oper = 2; return; - case 9: channel = 4; oper = 1; return; - case 13: channel = 4; oper = 3; return; - - case 2: channel = 5; oper = 0; return; - case 6: channel = 5; oper = 2; return; - case 10: channel = 5; oper = 1; return; - case 14: channel = 5; oper = 3; return; - - default: channel = -1; oper = -1; return; - } - } - - void Part1_WriteRegister(byte register, byte value) - { - int chan, oper; - GetChanOpP1(register, out chan, out oper); - - switch (register & 0xF0) - { - case 0x20: WriteLowBlock(register, value); break; - case 0x30: Write_MUL_DT(chan, oper, value); break; - case 0x40: Write_TL(chan, oper, value); break; - case 0x50: Write_AR_KS(chan, oper, value); break; - case 0x60: Write_DR_AM(chan, oper, value); break; - case 0x70: Write_SR(chan, oper, value); break; - case 0x80: Write_RR_SL(chan, oper, value); break; - case 0x90: Write_SSGEG(chan, oper, value); break; - case 0xA0: - case 0xB0: WriteHighBlockP1(register, value); break; - } - } - - void Part2_WriteRegister(byte register, byte value) - { - int chan, oper; - GetChanOpP2(register, out chan, out oper); - - switch (register & 0xF0) - { - case 0x30: Write_MUL_DT(chan, oper, value); break; - case 0x40: Write_TL(chan, oper, value); break; - case 0x50: Write_AR_KS(chan, oper, value); break; - case 0x60: Write_DR_AM(chan, oper, value); break; - case 0x70: Write_SR(chan, oper, value); break; - case 0x80: Write_RR_SL(chan, oper, value); break; - case 0x90: Write_SSGEG(chan, oper, value); break; - case 0xA0: - case 0xB0: WriteHighBlockP2(register, value); break; - } - } - - void WriteLowBlock(byte register, byte value) - { - switch (register) - { - //case 0x22: Console.WriteLine("LFO Control {0:X2}", value); break; - case 0x24: break; // Timer A MSB, handled immediately - case 0x25: break; // Timer A LSB, handled immediately - case 0x26: break; // Timer B, handled immediately - //case 0x27: Console.WriteLine("$27: Ch3 Mode / Timer Control {0:X2}", value); break; // determines if CH3 has 1 frequency or 4 frequencies. - case 0x28: KeyOnOff(value); break; - case 0x2A: DacValue = value; break; - case 0x2B: DacEnable = (value & 0x80) != 0; break; - case 0x2C: throw new Exception("something wrote to ym2612 port $2C!"); //http://forums.sonicretro.org/index.php?showtopic=28589 - } - } - - void WriteHighBlockP1(byte register, byte value) - { - switch (register) - { - case 0xA0: WriteFrequencyLow(Channels[0], value); break; - case 0xA1: WriteFrequencyLow(Channels[1], value); break; - case 0xA2: WriteFrequencyLow(Channels[2], value); break; - - case 0xA4: WriteFrequencyHigh(Channels[0], value); break; - case 0xA5: WriteFrequencyHigh(Channels[1], value); break; - case 0xA6: WriteFrequencyHigh(Channels[2], value); break; - - case 0xB0: Write_Feedback_Algorithm(Channels[0], value); break; - case 0xB1: Write_Feedback_Algorithm(Channels[1], value); break; - case 0xB2: Write_Feedback_Algorithm(Channels[2], value); break; - - case 0xB4: Write_Stereo_LfoSensitivy(Channels[0], value); break; - case 0xB5: Write_Stereo_LfoSensitivy(Channels[1], value); break; - case 0xB6: Write_Stereo_LfoSensitivy(Channels[2], value); break; - } - } - - void WriteHighBlockP2(byte register, byte value) - { - switch (register) - { - case 0xA0: WriteFrequencyLow(Channels[3], value); break; - case 0xA1: WriteFrequencyLow(Channels[4], value); break; - case 0xA2: WriteFrequencyLow(Channels[5], value); break; - - case 0xA4: WriteFrequencyHigh(Channels[3], value); break; - case 0xA5: WriteFrequencyHigh(Channels[4], value); break; - case 0xA6: WriteFrequencyHigh(Channels[5], value); break; - - case 0xB0: Write_Feedback_Algorithm(Channels[3], value); break; - case 0xB1: Write_Feedback_Algorithm(Channels[4], value); break; - case 0xB2: Write_Feedback_Algorithm(Channels[5], value); break; - - case 0xB4: Write_Stereo_LfoSensitivy(Channels[3], value); break; - case 0xB5: Write_Stereo_LfoSensitivy(Channels[4], value); break; - case 0xB6: Write_Stereo_LfoSensitivy(Channels[5], value); break; - } - } - - void KeyOnOff(byte value) - { - int channel = value & 3; - if (channel == 3) return; // illegal channel number, abort - if ((value & 4) != 0) channel += 3; // select part 2 - - var chan = Channels[channel]; - - //Console.WriteLine("KeyOnOff for channel {0}", channel); - - if ((value & 0x10) != 0) KeyOn(chan.Operators[Slot1]); else KeyOff(chan.Operators[Slot1]); - if ((value & 0x20) != 0) KeyOn(chan.Operators[Slot2]); else KeyOff(chan.Operators[Slot2]); - if ((value & 0x40) != 0) KeyOn(chan.Operators[Slot3]); else KeyOff(chan.Operators[Slot3]); - if ((value & 0x80) != 0) KeyOn(chan.Operators[Slot4]); else KeyOff(chan.Operators[Slot4]); - } - - static void WriteFrequencyLow(Channel channel, byte value) - { - channel.FrequencyNumber &= 0x700; - channel.FrequencyNumber |= value; - - // TODO maybe its 4-frequency mode - // TODO is this right, only reflect change when writing LSB? - - Operator op; - op = channel.Operators[0]; op.FrequencyNumber = channel.FrequencyNumber; op.Block = channel.Block; CalcKeyCode(op); CalcRks(op); - op = channel.Operators[1]; op.FrequencyNumber = channel.FrequencyNumber; op.Block = channel.Block; CalcKeyCode(op); CalcRks(op); - op = channel.Operators[2]; op.FrequencyNumber = channel.FrequencyNumber; op.Block = channel.Block; CalcKeyCode(op); CalcRks(op); - op = channel.Operators[3]; op.FrequencyNumber = channel.FrequencyNumber; op.Block = channel.Block; CalcKeyCode(op); CalcRks(op); - } - - static void WriteFrequencyHigh(Channel channel, byte value) - { - channel.FrequencyNumber &= 0x0FF; - channel.FrequencyNumber |= (value & 15) << 8; - channel.Block = (value >> 3) & 7; - } - - static void CalcKeyCode(Operator op) - { - int freq = op.FrequencyNumber; - bool f11 = ((freq >> 10) & 1) != 0; - bool f10 = ((freq >> 9) & 1) != 0; - bool f09 = ((freq >> 8) & 1) != 0; - bool f08 = ((freq >> 7) & 1) != 0; - - bool n3a = f11 & (f10 | f09 | f08); - bool n3b = !f11 & f10 & f09 & f08; - bool n3 = n3a | n3b; - - op.KeyCode = (op.Block << 2) | (f11 ? 2 : 0) | (n3 ? 1 :0); - } - - static void CalcRks(Operator op) - { - int shiftVal = 3 - op.KS_KeyScale; - op.Rks = op.KeyCode >> shiftVal; - } - - static void Write_Feedback_Algorithm(Channel channel, byte value) - { - channel.Algorithm = value & 7; - channel.Feedback = (value >> 3) & 7; - } - - static void Write_Stereo_LfoSensitivy(Channel channel, byte value) - { - channel.FMS_FrequencyModulationSensitivity = value & 3; - channel.AMS_AmplitudeModulationSensitivity = (value >> 3) & 7; - channel.RightOutput = (value & 0x40) != 0; - channel.LeftOutput = (value & 0x80) != 0; - } - - void Write_MUL_DT(int chan, int op, byte value) - { - if (chan < 0) return; - var oper = Channels[chan].Operators[op]; - oper.MUL_Multiple = value & 15; - oper.DT_Detune = (value >> 4) & 7; - } - - public void Write_TL(int chan, int op, byte value) - { - if (chan < 0) return; - var oper = Channels[chan].Operators[op]; - oper.TL_TotalLevel = value & 127; - } - - public void Write_AR_KS(int chan, int op, byte value) - { - if (chan < 0) return; - var oper = Channels[chan].Operators[op]; - oper.AR_AttackRate = value & 31; - oper.KS_KeyScale = value >> 6; - CalcRks(oper); - } - - public void Write_DR_AM(int chan, int op, byte value) - { - if (chan < 0) return; - var oper = Channels[chan].Operators[op]; - oper.DR_DecayRate = value & 31; - oper.AM_AmplitudeModulation = (value & 128) != 0; - } - - public void Write_SR(int chan, int op, byte value) - { - if (chan < 0) return; - var oper = Channels[chan].Operators[op]; - oper.SR_SustainRate = value & 31; - } - - public void Write_RR_SL(int chan, int op, byte value) - { - if (chan < 0) return; - var oper = Channels[chan].Operators[op]; - oper.RR_ReleaseRate = value & 15; - oper.SL_SustainLevel = value >> 4; - } - - public void Write_SSGEG(int chan, int op, byte value) - { - if (chan < 0) return; - var oper = Channels[chan].Operators[op]; - oper.SSG_EG = value & 15; - } - - // ==================================================================================== - // Timers - // ==================================================================================== - - // Assuming this is connected to a Genesis/MegaDrive: - - // The master clock on the Genesis is 53,693,175 MCLK / sec (NTSC) - // 53,203,424 MCLK / sec (PAL) - // 7,670,454 68K cycles / sec (7 MCLK divisor) (NTSC) - // 3,579,545 Z80 cycles / sec (15 MCLK divisor) (NTSC) - - // YM2612 is fed by 68000 Clock: 7,670,454 ECLK / sec (NTSC) - // 7,600,489 ECLK / sec (PAL) - - // YM2612 has /6 divisor on the EXT CLOCK. - // YM2612 takes 24 cycles to generate a sample. 6*24 = 144. This is where the /144 divisor comes from. - // YM2612 native output rate is 7670454 / 144 = 53267 hz (NTSC), 52781 hz (PAL) - - // Timer A ticks at the native output rate (53267 times per second for NTSC). - // Timer B ticks down with a /16 divisor. (3329 times per second for NTSC). - - // Ergo, Timer A ticks every 67.2 Z80 cycles. Timer B ticks every 1075.2 Z80 cycles. - - // TODO: make this not hardcoded to Genesis timing. - - const float timerAZ80Factor = 67.2f; - const float timerBZ80Factor = 1075.2f; - - const int ntscOutputRate = 53267; - const int palOutputRate = 52781; - - const float ntsc44100Factor = 1.20786848f; - const float pal44100Factor = 1.19684807f; - - int TimerAPeriod, TimerBPeriod; - bool TimerATripped, TimerBTripped; - int TimerAResetClock, TimerBResetClock; - int TimerALastReset, TimerBLastReset; - - byte TimerControl27; - bool TimerALoad { get { return (TimerControl27 & 1) != 0; } } - bool TimerBLoad { get { return (TimerControl27 & 2) != 0; } } - bool TimerAEnable { get { return (TimerControl27 & 4) != 0; } } - bool TimerBEnable { get { return (TimerControl27 & 8) != 0; } } - bool TimerAReset { get { return (TimerControl27 & 16) != 0; } } - bool TimerBReset { get { return (TimerControl27 & 32) != 0; } } - - void InitTimers() - { - TimerAResetClock = 68812; - TimerBResetClock = 275200; - } - - void UpdateTimers(int clock) - { - int elapsedCyclesSinceLastTimerAReset = clock - TimerALastReset; - if (elapsedCyclesSinceLastTimerAReset > TimerAResetClock) - { - if (TimerAEnable) - TimerATripped = true; - - int numTimesTripped = elapsedCyclesSinceLastTimerAReset / TimerAResetClock; - TimerALastReset += (TimerAResetClock * numTimesTripped); - } - - int elapsedCyclesSinceLastTimerBReset = clock - TimerBLastReset; - if (elapsedCyclesSinceLastTimerBReset > TimerBResetClock) - { - if (TimerBEnable) - TimerBTripped = true; - - int numTimesTripped = elapsedCyclesSinceLastTimerBReset / TimerBResetClock; - TimerBLastReset += (TimerBResetClock * numTimesTripped); - } - } - - void WriteTimerA_MSB_24(byte value, int clock) - { - TimerAPeriod = (value << 2) | (TimerAPeriod & 3); - TimerAResetClock = (int)((1024 - TimerAPeriod) * timerAZ80Factor); - } - - void WriteTimerA_LSB_25(byte value, int clock) - { - TimerAPeriod = (TimerAPeriod & 0x3FC) | (value & 3); - TimerAResetClock = (int)((1024 - TimerAPeriod) * timerAZ80Factor); - } - - void WriteTimerB_26(byte value, int clock) - { - TimerBPeriod = value; - TimerBResetClock = (int)((256 - TimerBPeriod) * timerBZ80Factor); - } - - void WriteTimerControl_27(byte value, int clock) - { - bool lagALoad = TimerALoad; - bool lagBLoad = TimerBLoad; - - TimerControl27 = value; - - if (!lagALoad && TimerALoad) - TimerALastReset = clock; - - if (!lagBLoad && TimerBLoad) - TimerBLastReset = clock; - - if (TimerAReset) TimerATripped = false; - if (TimerBReset) TimerBTripped = false; - } - - // ==================================================================================== - // Support Tables - // ==================================================================================== - - #region tables - static readonly byte[] egRateCounterShiftValues = + // ====================================================================== + // Yamaha YM2612 Emulation Core + // Primarily sourced from Nemesis's documentation on Sprite's Mind forums: + // http://gendev.spritesmind.net/forum/viewtopic.php?t=386 + // + // Notes: + // - In order to facilitate asynchronous sound generation, timer commands + // and reads are emulated immediately, while all other commands are + // queued together with a timestamp and processed at the end of the frame. + // - Commands are stretched in time to match the number of samples requested + // for the frame. For accurate, synchronous sound, simply request the correct + // number of samples for each frame. + // - Output is emulated at native output rate and downsampled (badly) to 44100hz. + // ====================================================================== + + // TODO: Finish testing Envelope generator + // TODO: maybe add guards when changing envelope parameters to immediately change envelope state (ie dont wait for EG cycle?) + // TODO: Detune + // TODO: LFO + // TODO: Switch from Perfect Operator to Accurate Operator. + // TODO: Operator1 Self-Feedback + // TODO: MEM delayed samples + // TODO: CSM mode + // TODO: SSG-EG + // TODO: Seriously, I think we need better resampling code. + // TODO: Experiment with low-pass filters, etc. + + public sealed class YM2612 : ISoundProvider + { + public readonly Channel[] Channels = { new Channel(), new Channel(), new Channel(), new Channel(), new Channel(), new Channel() }; + + public YM2612() + { + InitTimers(); + MaxVolume = short.MaxValue; + } + + // ==================================================================================== + + int frameStartClock; + int frameEndClock; + + public void BeginFrame(int clock) + { + frameStartClock = clock; + while (commands.Count > 0) + { + var cmd = commands.Dequeue(); + WriteCommand(cmd); + } + } + + public void EndFrame(int clock) + { + frameEndClock = clock; + } + + // ==================================================================================== + // YM2612 I/O + // ==================================================================================== + + public class QueuedCommand + { + public byte Part; + public byte Register; + public byte Data; + public int Clock; + } + + byte PartSelect; + byte RegisterSelect; + bool DacEnable; + byte DacValue; + + readonly Queue commands = new Queue(); + + const int Slot1 = 0; + const int Slot2 = 2; + const int Slot3 = 1; + const int Slot4 = 3; + + public byte ReadStatus(int clock) + { + UpdateTimers(clock); + + byte retval = 0; + if (TimerATripped) retval |= 1; + if (TimerBTripped) retval |= 2; + return retval; + } + + public void Write(int addr, byte value, int clock) + { + UpdateTimers(clock); + + if (addr == 0) + { + PartSelect = 1; + RegisterSelect = value; + return; + } + else if (addr == 2) + { + PartSelect = 2; + RegisterSelect = value; + return; + } + + if (PartSelect == 1) + { + if (RegisterSelect == 0x24) { WriteTimerA_MSB_24(value, clock); return; } + if (RegisterSelect == 0x25) { WriteTimerA_LSB_25(value, clock); return; } + if (RegisterSelect == 0x26) { WriteTimerB_26(value, clock); return; } + if (RegisterSelect == 0x27) { WriteTimerControl_27(value, clock); } // we process immediately AND enqueue command for port $27. Allows accurate tracking of CH3 special modes. + } + + // If its not timer related just queue the command write + var cmd = new QueuedCommand { Part = PartSelect, Register = RegisterSelect, Data = value, Clock = clock - frameStartClock }; + commands.Enqueue(cmd); + } + + void WriteCommand(QueuedCommand cmd) + { + if (cmd.Part == 1) + Part1_WriteRegister(cmd.Register, cmd.Data); + else + Part2_WriteRegister(cmd.Register, cmd.Data); + } + + static void GetChanOpP1(byte value, out int channel, out int oper) + { + value &= 15; + switch (value) + { + case 0: channel = 0; oper = 0; return; + case 4: channel = 0; oper = 2; return; + case 8: channel = 0; oper = 1; return; + case 12: channel = 0; oper = 3; return; + + case 1: channel = 1; oper = 0; return; + case 5: channel = 1; oper = 2; return; + case 9: channel = 1; oper = 1; return; + case 13: channel = 1; oper = 3; return; + + case 2: channel = 2; oper = 0; return; + case 6: channel = 2; oper = 2; return; + case 10: channel = 2; oper = 1; return; + case 14: channel = 2; oper = 3; return; + + default: channel = -1; oper = -1; return; + } + } + + static void GetChanOpP2(byte value, out int channel, out int oper) + { + value &= 15; + switch (value) + { + case 0: channel = 3; oper = 0; return; + case 4: channel = 3; oper = 2; return; + case 8: channel = 3; oper = 1; return; + case 12: channel = 3; oper = 3; return; + + case 1: channel = 4; oper = 0; return; + case 5: channel = 4; oper = 2; return; + case 9: channel = 4; oper = 1; return; + case 13: channel = 4; oper = 3; return; + + case 2: channel = 5; oper = 0; return; + case 6: channel = 5; oper = 2; return; + case 10: channel = 5; oper = 1; return; + case 14: channel = 5; oper = 3; return; + + default: channel = -1; oper = -1; return; + } + } + + void Part1_WriteRegister(byte register, byte value) + { + int chan, oper; + GetChanOpP1(register, out chan, out oper); + + switch (register & 0xF0) + { + case 0x20: WriteLowBlock(register, value); break; + case 0x30: Write_MUL_DT(chan, oper, value); break; + case 0x40: Write_TL(chan, oper, value); break; + case 0x50: Write_AR_KS(chan, oper, value); break; + case 0x60: Write_DR_AM(chan, oper, value); break; + case 0x70: Write_SR(chan, oper, value); break; + case 0x80: Write_RR_SL(chan, oper, value); break; + case 0x90: Write_SSGEG(chan, oper, value); break; + case 0xA0: + case 0xB0: WriteHighBlockP1(register, value); break; + } + } + + void Part2_WriteRegister(byte register, byte value) + { + int chan, oper; + GetChanOpP2(register, out chan, out oper); + + switch (register & 0xF0) + { + case 0x30: Write_MUL_DT(chan, oper, value); break; + case 0x40: Write_TL(chan, oper, value); break; + case 0x50: Write_AR_KS(chan, oper, value); break; + case 0x60: Write_DR_AM(chan, oper, value); break; + case 0x70: Write_SR(chan, oper, value); break; + case 0x80: Write_RR_SL(chan, oper, value); break; + case 0x90: Write_SSGEG(chan, oper, value); break; + case 0xA0: + case 0xB0: WriteHighBlockP2(register, value); break; + } + } + + void WriteLowBlock(byte register, byte value) + { + switch (register) + { + //case 0x22: Console.WriteLine("LFO Control {0:X2}", value); break; + case 0x24: break; // Timer A MSB, handled immediately + case 0x25: break; // Timer A LSB, handled immediately + case 0x26: break; // Timer B, handled immediately + //case 0x27: Console.WriteLine("$27: Ch3 Mode / Timer Control {0:X2}", value); break; // determines if CH3 has 1 frequency or 4 frequencies. + case 0x28: KeyOnOff(value); break; + case 0x2A: DacValue = value; break; + case 0x2B: DacEnable = (value & 0x80) != 0; break; + case 0x2C: throw new Exception("something wrote to ym2612 port $2C!"); //http://forums.sonicretro.org/index.php?showtopic=28589 + } + } + + void WriteHighBlockP1(byte register, byte value) + { + switch (register) + { + case 0xA0: WriteFrequencyLow(Channels[0], value); break; + case 0xA1: WriteFrequencyLow(Channels[1], value); break; + case 0xA2: WriteFrequencyLow(Channels[2], value); break; + + case 0xA4: WriteFrequencyHigh(Channels[0], value); break; + case 0xA5: WriteFrequencyHigh(Channels[1], value); break; + case 0xA6: WriteFrequencyHigh(Channels[2], value); break; + + case 0xB0: Write_Feedback_Algorithm(Channels[0], value); break; + case 0xB1: Write_Feedback_Algorithm(Channels[1], value); break; + case 0xB2: Write_Feedback_Algorithm(Channels[2], value); break; + + case 0xB4: Write_Stereo_LfoSensitivy(Channels[0], value); break; + case 0xB5: Write_Stereo_LfoSensitivy(Channels[1], value); break; + case 0xB6: Write_Stereo_LfoSensitivy(Channels[2], value); break; + } + } + + void WriteHighBlockP2(byte register, byte value) + { + switch (register) + { + case 0xA0: WriteFrequencyLow(Channels[3], value); break; + case 0xA1: WriteFrequencyLow(Channels[4], value); break; + case 0xA2: WriteFrequencyLow(Channels[5], value); break; + + case 0xA4: WriteFrequencyHigh(Channels[3], value); break; + case 0xA5: WriteFrequencyHigh(Channels[4], value); break; + case 0xA6: WriteFrequencyHigh(Channels[5], value); break; + + case 0xB0: Write_Feedback_Algorithm(Channels[3], value); break; + case 0xB1: Write_Feedback_Algorithm(Channels[4], value); break; + case 0xB2: Write_Feedback_Algorithm(Channels[5], value); break; + + case 0xB4: Write_Stereo_LfoSensitivy(Channels[3], value); break; + case 0xB5: Write_Stereo_LfoSensitivy(Channels[4], value); break; + case 0xB6: Write_Stereo_LfoSensitivy(Channels[5], value); break; + } + } + + void KeyOnOff(byte value) + { + int channel = value & 3; + if (channel == 3) return; // illegal channel number, abort + if ((value & 4) != 0) channel += 3; // select part 2 + + var chan = Channels[channel]; + + //Console.WriteLine("KeyOnOff for channel {0}", channel); + + if ((value & 0x10) != 0) KeyOn(chan.Operators[Slot1]); else KeyOff(chan.Operators[Slot1]); + if ((value & 0x20) != 0) KeyOn(chan.Operators[Slot2]); else KeyOff(chan.Operators[Slot2]); + if ((value & 0x40) != 0) KeyOn(chan.Operators[Slot3]); else KeyOff(chan.Operators[Slot3]); + if ((value & 0x80) != 0) KeyOn(chan.Operators[Slot4]); else KeyOff(chan.Operators[Slot4]); + } + + static void WriteFrequencyLow(Channel channel, byte value) + { + channel.FrequencyNumber &= 0x700; + channel.FrequencyNumber |= value; + + // TODO maybe its 4-frequency mode + // TODO is this right, only reflect change when writing LSB? + + Operator op; + op = channel.Operators[0]; op.FrequencyNumber = channel.FrequencyNumber; op.Block = channel.Block; CalcKeyCode(op); CalcRks(op); + op = channel.Operators[1]; op.FrequencyNumber = channel.FrequencyNumber; op.Block = channel.Block; CalcKeyCode(op); CalcRks(op); + op = channel.Operators[2]; op.FrequencyNumber = channel.FrequencyNumber; op.Block = channel.Block; CalcKeyCode(op); CalcRks(op); + op = channel.Operators[3]; op.FrequencyNumber = channel.FrequencyNumber; op.Block = channel.Block; CalcKeyCode(op); CalcRks(op); + } + + static void WriteFrequencyHigh(Channel channel, byte value) + { + channel.FrequencyNumber &= 0x0FF; + channel.FrequencyNumber |= (value & 15) << 8; + channel.Block = (value >> 3) & 7; + } + + static void CalcKeyCode(Operator op) + { + int freq = op.FrequencyNumber; + bool f11 = ((freq >> 10) & 1) != 0; + bool f10 = ((freq >> 9) & 1) != 0; + bool f09 = ((freq >> 8) & 1) != 0; + bool f08 = ((freq >> 7) & 1) != 0; + + bool n3a = f11 & (f10 | f09 | f08); + bool n3b = !f11 & f10 & f09 & f08; + bool n3 = n3a | n3b; + + op.KeyCode = (op.Block << 2) | (f11 ? 2 : 0) | (n3 ? 1 : 0); + } + + static void CalcRks(Operator op) + { + int shiftVal = 3 - op.KS_KeyScale; + op.Rks = op.KeyCode >> shiftVal; + } + + static void Write_Feedback_Algorithm(Channel channel, byte value) + { + channel.Algorithm = value & 7; + channel.Feedback = (value >> 3) & 7; + } + + static void Write_Stereo_LfoSensitivy(Channel channel, byte value) + { + channel.FMS_FrequencyModulationSensitivity = value & 3; + channel.AMS_AmplitudeModulationSensitivity = (value >> 3) & 7; + channel.RightOutput = (value & 0x40) != 0; + channel.LeftOutput = (value & 0x80) != 0; + } + + void Write_MUL_DT(int chan, int op, byte value) + { + if (chan < 0) return; + var oper = Channels[chan].Operators[op]; + oper.MUL_Multiple = value & 15; + oper.DT_Detune = (value >> 4) & 7; + } + + public void Write_TL(int chan, int op, byte value) + { + if (chan < 0) return; + var oper = Channels[chan].Operators[op]; + oper.TL_TotalLevel = value & 127; + } + + public void Write_AR_KS(int chan, int op, byte value) + { + if (chan < 0) return; + var oper = Channels[chan].Operators[op]; + oper.AR_AttackRate = value & 31; + oper.KS_KeyScale = value >> 6; + CalcRks(oper); + } + + public void Write_DR_AM(int chan, int op, byte value) + { + if (chan < 0) return; + var oper = Channels[chan].Operators[op]; + oper.DR_DecayRate = value & 31; + oper.AM_AmplitudeModulation = (value & 128) != 0; + } + + public void Write_SR(int chan, int op, byte value) + { + if (chan < 0) return; + var oper = Channels[chan].Operators[op]; + oper.SR_SustainRate = value & 31; + } + + public void Write_RR_SL(int chan, int op, byte value) + { + if (chan < 0) return; + var oper = Channels[chan].Operators[op]; + oper.RR_ReleaseRate = value & 15; + oper.SL_SustainLevel = value >> 4; + } + + public void Write_SSGEG(int chan, int op, byte value) + { + if (chan < 0) return; + var oper = Channels[chan].Operators[op]; + oper.SSG_EG = value & 15; + } + + // ==================================================================================== + // Timers + // ==================================================================================== + + // Assuming this is connected to a Genesis/MegaDrive: + + // The master clock on the Genesis is 53,693,175 MCLK / sec (NTSC) + // 53,203,424 MCLK / sec (PAL) + // 7,670,454 68K cycles / sec (7 MCLK divisor) (NTSC) + // 3,579,545 Z80 cycles / sec (15 MCLK divisor) (NTSC) + + // YM2612 is fed by 68000 Clock: 7,670,454 ECLK / sec (NTSC) + // 7,600,489 ECLK / sec (PAL) + + // YM2612 has /6 divisor on the EXT CLOCK. + // YM2612 takes 24 cycles to generate a sample. 6*24 = 144. This is where the /144 divisor comes from. + // YM2612 native output rate is 7670454 / 144 = 53267 hz (NTSC), 52781 hz (PAL) + + // Timer A ticks at the native output rate (53267 times per second for NTSC). + // Timer B ticks down with a /16 divisor. (3329 times per second for NTSC). + + // Ergo, Timer A ticks every 67.2 Z80 cycles. Timer B ticks every 1075.2 Z80 cycles. + + // TODO: make this not hardcoded to Genesis timing. + + const float timerAZ80Factor = 67.2f; + const float timerBZ80Factor = 1075.2f; + + const int ntscOutputRate = 53267; + const int palOutputRate = 52781; + + const float ntsc44100Factor = 1.20786848f; + const float pal44100Factor = 1.19684807f; + + int TimerAPeriod, TimerBPeriod; + bool TimerATripped, TimerBTripped; + int TimerAResetClock, TimerBResetClock; + int TimerALastReset, TimerBLastReset; + + byte TimerControl27; + bool TimerALoad { get { return (TimerControl27 & 1) != 0; } } + bool TimerBLoad { get { return (TimerControl27 & 2) != 0; } } + bool TimerAEnable { get { return (TimerControl27 & 4) != 0; } } + bool TimerBEnable { get { return (TimerControl27 & 8) != 0; } } + bool TimerAReset { get { return (TimerControl27 & 16) != 0; } } + bool TimerBReset { get { return (TimerControl27 & 32) != 0; } } + + void InitTimers() + { + TimerAResetClock = 68812; + TimerBResetClock = 275200; + } + + void UpdateTimers(int clock) + { + int elapsedCyclesSinceLastTimerAReset = clock - TimerALastReset; + if (elapsedCyclesSinceLastTimerAReset > TimerAResetClock) + { + if (TimerAEnable) + TimerATripped = true; + + int numTimesTripped = elapsedCyclesSinceLastTimerAReset / TimerAResetClock; + TimerALastReset += (TimerAResetClock * numTimesTripped); + } + + int elapsedCyclesSinceLastTimerBReset = clock - TimerBLastReset; + if (elapsedCyclesSinceLastTimerBReset > TimerBResetClock) + { + if (TimerBEnable) + TimerBTripped = true; + + int numTimesTripped = elapsedCyclesSinceLastTimerBReset / TimerBResetClock; + TimerBLastReset += (TimerBResetClock * numTimesTripped); + } + } + + void WriteTimerA_MSB_24(byte value, int clock) + { + TimerAPeriod = (value << 2) | (TimerAPeriod & 3); + TimerAResetClock = (int)((1024 - TimerAPeriod) * timerAZ80Factor); + } + + void WriteTimerA_LSB_25(byte value, int clock) + { + TimerAPeriod = (TimerAPeriod & 0x3FC) | (value & 3); + TimerAResetClock = (int)((1024 - TimerAPeriod) * timerAZ80Factor); + } + + void WriteTimerB_26(byte value, int clock) + { + TimerBPeriod = value; + TimerBResetClock = (int)((256 - TimerBPeriod) * timerBZ80Factor); + } + + void WriteTimerControl_27(byte value, int clock) + { + bool lagALoad = TimerALoad; + bool lagBLoad = TimerBLoad; + + TimerControl27 = value; + + if (!lagALoad && TimerALoad) + TimerALastReset = clock; + + if (!lagBLoad && TimerBLoad) + TimerBLastReset = clock; + + if (TimerAReset) TimerATripped = false; + if (TimerBReset) TimerBTripped = false; + } + + // ==================================================================================== + // Support Tables + // ==================================================================================== + + #region tables + static readonly byte[] egRateCounterShiftValues = { 11, 11, 11, 11, // Rates 0-3 10, 10, 10, 10, // Rates 4-7 @@ -546,7 +543,7 @@ namespace BizHawk.Emulation.Common.Components 0, 0, 0, 0 // Rates 60-63 }; - static readonly byte[] egRateIncrementValues = + static readonly byte[] egRateIncrementValues = { 0,0,0,0,0,0,0,0, // Rate 0 0,0,0,0,0,0,0,0, // Rate 1 @@ -614,13 +611,13 @@ namespace BizHawk.Emulation.Common.Components 8,8,8,8,8,8,8,8 // Rate 63 }; - static readonly int[] slTable = // translates a 4-bit SL value into a 10-bit attenuation value + static readonly int[] slTable = // translates a 4-bit SL value into a 10-bit attenuation value { 0x000, 0x020, 0x040, 0x060, 0x080, 0x0A0, 0x0C0, 0x0E0, 0x100, 0x120, 0x140, 0x160, 0x180, 0x1A0, 0x1C0, 0x3FF }; - static readonly int[] detuneTable = + static readonly int[] detuneTable = { 0, 0, 1, 2, // Key-Code 0 0, 0, 1, 2, // Key-Code 1 @@ -655,406 +652,408 @@ namespace BizHawk.Emulation.Common.Components 0, 8, 16, 22, // Key-Code 30 0, 8, 16, 22 // Key-Code 31 }; - #endregion - - // ==================================================================================== - // Envelope Generator - // ==================================================================================== - - int egDivisorCounter; // This provides the /3 divisor to run the envelope generator once for every 3 FM sample output ticks. - int egCycleCounter; // This provides a rolling counter of the envelope generator update ticks. (/3 divisor already applied) - - const int MaxAttenuation = 1023; - - void MaybeRunEnvelopeGenerator() - { - if (egDivisorCounter == 0) - { - for (int c = 0; c < 6; c++) - for (int o = 0; o < 4; o++) - EnvelopeGeneratorTick(Channels[c].Operators[o]); - - egCycleCounter++; - } - - egDivisorCounter++; - if (egDivisorCounter == 3) - egDivisorCounter = 0; - } - - void EnvelopeGeneratorTick(Operator op) - { - // First, let's handle envelope generator phase transitions. - - if (op.EnvelopeState == EnvelopeState.Off) - return; - - if (op.EnvelopeState != EnvelopeState.Attack && op.EgAttenuation == MaxAttenuation) - { - op.EnvelopeState = EnvelopeState.Off; - return; - } - - if (op.EnvelopeState == EnvelopeState.Attack && op.EgAttenuation == 0) - { - op.EnvelopeState = EnvelopeState.Decay; - if (op.SL_SustainLevel == 0) // If Sustain Level is 0, we skip Decay and go straight to Sustain phase. - op.EnvelopeState = EnvelopeState.Sustain; - } - - if (op.EnvelopeState == EnvelopeState.Decay && op.EgAttenuation >= op.Normalized10BitSL) - { - op.EnvelopeState = EnvelopeState.Sustain; - } - - // At this point, we've determined what envelope phase we're in. Lets do the update. - // Start by calculating Rate. - - int rate = 0; - switch (op.EnvelopeState) - { - case EnvelopeState.Attack: rate = op.AR_AttackRate; break; - case EnvelopeState.Decay: rate = op.DR_DecayRate; break; - case EnvelopeState.Sustain: rate = op.SR_SustainRate; break; - case EnvelopeState.Release: rate = (op.RR_ReleaseRate << 1) + 1; break; - } - - if (rate != 0) // rate=0 is 0 no matter the value of Rks. - rate = Math.Min((rate * 2) + op.Rks, 63); - - // Now we have rate. figure out shift value and cycle offset - int shiftValue = egRateCounterShiftValues[rate]; - - if (egCycleCounter % (1 << shiftValue) == 0) - { - // Update attenuation value this tick - int updateCycleOffset = (egCycleCounter >> shiftValue) & 7; // gives the offset within the 8-step cycle - int attenuationAdjustment = egRateIncrementValues[(rate * 8) + updateCycleOffset]; - - if (op.EnvelopeState == EnvelopeState.Attack) - op.EgAttenuation += (~op.EgAttenuation * attenuationAdjustment) >> 4; - else // One of the decay phases - op.EgAttenuation += attenuationAdjustment; - } - } - - static void KeyOn(Operator op) - { - op.PhaseCounter = 0; // Reset Phase Generator - - if (op.AR_AttackRate >= 30) - { - - // AR of 30 or 31 skips attack phase - op.EgAttenuation = 0; // Force minimum attenuation - - op.EnvelopeState = EnvelopeState.Decay; - if (op.SL_SustainLevel == 0) // If Sustain Level is 0, we skip Decay and go straight to Sustain phase. - op.EnvelopeState = EnvelopeState.Sustain; - - } else { - - // Regular Key-On - op.EnvelopeState = EnvelopeState.Attack; - - } - } - - static void KeyOff(Operator op) - { - op.EnvelopeState = EnvelopeState.Release; - } - - // ==================================================================================== - // Operator Unit - // ==================================================================================== - - int GetOperatorOutput(Operator op, int phaseModulationInput14) - { - if (op.EgAttenuation == MaxAttenuation) - return 0; - - RunPhaseGenerator(op); - int phase10 = op.PhaseCounter >> 10; - - // operators return a 14-bit output, but take a 10-bit input. What 4 bits are discarded? not the obvious ones... - // the input is shifted right by one; the least significant bit and the 3 most significant bits are discarded. - int phaseModulationInput10 = (phaseModulationInput14 >> 1) & 0x3FF; - - phase10 += phaseModulationInput10; - phase10 &= 0x3FF; - - return OperatorCalc(phase10, op.AdjustedEGOutput); - } - - static void RunPhaseGenerator(Operator op) - { - // Take the Frequency Number & shift based on Block - int phaseIncrement = op.FrequencyNumber; - switch (op.Block) - { - case 0: phaseIncrement >>= 1; break; - case 1: break; - default: phaseIncrement <<= op.Block - 1; break; - } - - // Apply Detune - int detuneAdjustment = detuneTable[(op.KeyCode * 4) + (op.DT_Detune & 3)]; - if ((op.DT_Detune & 4) != 0) - detuneAdjustment = -detuneAdjustment; - phaseIncrement += detuneAdjustment; - phaseIncrement &= 0x1FFFF; // mask to 17-bits, which is the current size of the register at this point in the calculation. This allows proper detune overflow. - - // Apply MUL - switch (op.MUL_Multiple) - { - case 0: phaseIncrement /= 2; break; - default: phaseIncrement *= op.MUL_Multiple; break; - } - - op.PhaseCounter += phaseIncrement; - op.PhaseCounter &= 0xFFFFF; - } - - static int OperatorCalc(int phase10, int attenuation) - { - // calculate sin - double phaseNormalized = (phase10 / 1023d); - double sinResult = Math.Sin(phaseNormalized * Math.PI * 2); - - // convert attenuation into linear power representation - const double attenuationIndividualBitWeighting = 48.0 / 1024.0; - double attenuationInBels = (((double)attenuation * attenuationIndividualBitWeighting) / 10.0); - double powerLinear = Math.Pow(10.0, -attenuationInBels); - - // attenuate result - double resultNormalized = sinResult * powerLinear; - - // calculate 14-bit operator output - const int maxOperatorOutput = 8191; - return (int)(resultNormalized * maxOperatorOutput); - } - - // ==================================================================================== - // Channel Unit - // ==================================================================================== - - const int max14bitValue = 0x1FFF; // maximum signed value - - int GetChannelOutput(Channel channel, int maxVolume) - { - int outc = 0; - - switch (channel.Algorithm) - { - case 0: - { - int out1; - out1 = GetOperatorOutput(channel.Operators[0], 0); - out1 = GetOperatorOutput(channel.Operators[1], out1); - out1 = GetOperatorOutput(channel.Operators[2], out1); - outc = GetOperatorOutput(channel.Operators[3], out1); - break; - } - - case 1: - { - int out1, out2; - out1 = GetOperatorOutput(channel.Operators[0], 0); - out2 = GetOperatorOutput(channel.Operators[1], 0); - outc = GetOperatorOutput(channel.Operators[2], Limit(out1 + out2, -8191, 8191)); // TODO test whether these Limit calls are actually correct. technically I expect it to be overflowing in a 10-bit space. - outc = GetOperatorOutput(channel.Operators[3], outc); - break; - } - - case 2: - { - int out1, out2; - out1 = GetOperatorOutput(channel.Operators[0], 0); - out2 = GetOperatorOutput(channel.Operators[1], 0); - out2 = GetOperatorOutput(channel.Operators[2], out2); - outc = GetOperatorOutput(channel.Operators[3], Limit(out1 + out2, -8191, 8191)); - break; - } - - case 3: - { - int out1, out2; - out1 = GetOperatorOutput(channel.Operators[0], 0); - out1 = GetOperatorOutput(channel.Operators[1], out1); - out2 = GetOperatorOutput(channel.Operators[2], 0); - outc = GetOperatorOutput(channel.Operators[3], Limit(out1 + out2, -8191, 8191)); - break; - } - - case 4: - { - int out1, out2; - out1 = GetOperatorOutput(channel.Operators[0], 0); - out1 = GetOperatorOutput(channel.Operators[1], out1); - out2 = GetOperatorOutput(channel.Operators[2], 0); - out2 = GetOperatorOutput(channel.Operators[3], out2); - outc = Limit(out1 + out2, -8191, 8191); - break; - } - - case 5: - { - int out1, out2, out3, out4; - out1 = GetOperatorOutput(channel.Operators[0], 0); - out2 = GetOperatorOutput(channel.Operators[1], out1); - out3 = GetOperatorOutput(channel.Operators[2], out1); - out4 = GetOperatorOutput(channel.Operators[3], out1); - outc = Limit(out2 + out3 + out4, -8191, 8191); - break; - } - - case 6: - { - int out1, out2, out3, out4; - out1 = GetOperatorOutput(channel.Operators[0], 0); - out2 = GetOperatorOutput(channel.Operators[1], out1); - out3 = GetOperatorOutput(channel.Operators[2], out1); - out4 = GetOperatorOutput(channel.Operators[3], out1); - outc = Limit(out2 + out3 + out4, -8191, 8191); - break; - } - - case 7: - { - int out1, out2, out3, out4; - out1 = GetOperatorOutput(channel.Operators[0], 0); - out2 = GetOperatorOutput(channel.Operators[1], out1); - out3 = GetOperatorOutput(channel.Operators[2], 0); - out4 = GetOperatorOutput(channel.Operators[3], 0); - outc = Limit(out2 + out3 + out4, -8191, 8191); - break; - } - } - - return outc * maxVolume / max14bitValue; - } - - static int Limit(int value, int min, int max) - { - if (value < min) return min; - if (value > max) return max; - return value; - } - - // ==================================================================================== - // Support Classes/Structs/Enums - // ==================================================================================== - - public enum EnvelopeState - { - Attack, - Decay, - Sustain, - Release, - Off - } - - public sealed class Operator - { - // External Settings - - public int TL_TotalLevel; // 7 bits - public int SL_SustainLevel; // 4 bits - public int AR_AttackRate; // 5 bits - public int DR_DecayRate; // 5 bits - public int SR_SustainRate; // 5 bits - public int RR_ReleaseRate; // 4 bits - public int KS_KeyScale; // 2 bits - public int SSG_EG; // 4 bits - - public int DT_Detune; // 3 bits - public int MUL_Multiple; // 4 bits - - public bool AM_AmplitudeModulation; // 1 bit - - public int FrequencyNumber; // 11 bits - public int Block; // 3 bits - - public int KeyCode; // 5 bits (described on pg 25 of YM2608 docs) - public int Rks; // 5 bits (described on pg 29 of YM2608 docs) - - // Internal State - - public int PhaseCounter; // 20 bits, where the 10 most significant bits are output to the operator. - - public EnvelopeState EnvelopeState = EnvelopeState.Off; - - private int egAttenuation = MaxAttenuation; // 10-bit attenuation value output from envelope generator - public int EgAttenuation - { - get { return egAttenuation; } - set - { - egAttenuation = value; - if (egAttenuation < 0) egAttenuation = 0; - if (egAttenuation > 1023) egAttenuation = 1023; - } - } - - public int Normalized10BitSL { get { return slTable[SL_SustainLevel]; } } - public int Normalized10BitTL { get { return TL_TotalLevel << 3; } } - public int AdjustedEGOutput { get { return Math.Min(egAttenuation + Normalized10BitTL, 1023); } } - } - - public sealed class Channel - { - public readonly Operator[] Operators = { new Operator(), new Operator(), new Operator(), new Operator() }; - - public int FrequencyNumber; // 11 bits - public int Block; // 3 bits - public int Feedback; // 3 bits - public int Algorithm; // 3 bits (algorithms 0 - 7) - - public bool SpecialMode; // TODO, there are 2 special modes, a bool is not going to do the trick. - public bool LeftOutput = true; // These apparently need to be initialized on. - public bool RightOutput = true; // These apparently need to be initialized on. - - public int AMS_AmplitudeModulationSensitivity; // 3 bits - public int FMS_FrequencyModulationSensitivity; // 2 bits - } - - // ==================================================================================== - // ISoundProvider - // ==================================================================================== - - public void GetSamples(short[] samples) - { - // Generate raw samples at native sampling rate (~53hz) - int numStereoSamples = samples.Length / 2; - int nativeStereoSamples = (int)(numStereoSamples * ntsc44100Factor) * 2; - short[] nativeSamples = new short[nativeStereoSamples]; - GetSamplesNative(nativeSamples); - - // downsample from native output rate to 44100. - //CrappyNaiveResampler(nativeSamples, samples); - MaybeBetterDownsampler(nativeSamples, samples); + #endregion + + // ==================================================================================== + // Envelope Generator + // ==================================================================================== + + int egDivisorCounter; // This provides the /3 divisor to run the envelope generator once for every 3 FM sample output ticks. + int egCycleCounter; // This provides a rolling counter of the envelope generator update ticks. (/3 divisor already applied) + + const int MaxAttenuation = 1023; + + void MaybeRunEnvelopeGenerator() + { + if (egDivisorCounter == 0) + { + for (int c = 0; c < 6; c++) + for (int o = 0; o < 4; o++) + EnvelopeGeneratorTick(Channels[c].Operators[o]); + + egCycleCounter++; + } + + egDivisorCounter++; + if (egDivisorCounter == 3) + egDivisorCounter = 0; + } + + void EnvelopeGeneratorTick(Operator op) + { + // First, let's handle envelope generator phase transitions. + + if (op.EnvelopeState == EnvelopeState.Off) + return; + + if (op.EnvelopeState != EnvelopeState.Attack && op.EgAttenuation == MaxAttenuation) + { + op.EnvelopeState = EnvelopeState.Off; + return; + } + + if (op.EnvelopeState == EnvelopeState.Attack && op.EgAttenuation == 0) + { + op.EnvelopeState = EnvelopeState.Decay; + if (op.SL_SustainLevel == 0) // If Sustain Level is 0, we skip Decay and go straight to Sustain phase. + op.EnvelopeState = EnvelopeState.Sustain; + } + + if (op.EnvelopeState == EnvelopeState.Decay && op.EgAttenuation >= op.Normalized10BitSL) + { + op.EnvelopeState = EnvelopeState.Sustain; + } + + // At this point, we've determined what envelope phase we're in. Lets do the update. + // Start by calculating Rate. + + int rate = 0; + switch (op.EnvelopeState) + { + case EnvelopeState.Attack: rate = op.AR_AttackRate; break; + case EnvelopeState.Decay: rate = op.DR_DecayRate; break; + case EnvelopeState.Sustain: rate = op.SR_SustainRate; break; + case EnvelopeState.Release: rate = (op.RR_ReleaseRate << 1) + 1; break; + } + + if (rate != 0) // rate=0 is 0 no matter the value of Rks. + rate = Math.Min((rate * 2) + op.Rks, 63); + + // Now we have rate. figure out shift value and cycle offset + int shiftValue = egRateCounterShiftValues[rate]; + + if (egCycleCounter % (1 << shiftValue) == 0) + { + // Update attenuation value this tick + int updateCycleOffset = (egCycleCounter >> shiftValue) & 7; // gives the offset within the 8-step cycle + int attenuationAdjustment = egRateIncrementValues[(rate * 8) + updateCycleOffset]; + + if (op.EnvelopeState == EnvelopeState.Attack) + op.EgAttenuation += (~op.EgAttenuation * attenuationAdjustment) >> 4; + else // One of the decay phases + op.EgAttenuation += attenuationAdjustment; + } + } + + static void KeyOn(Operator op) + { + op.PhaseCounter = 0; // Reset Phase Generator + + if (op.AR_AttackRate >= 30) + { + + // AR of 30 or 31 skips attack phase + op.EgAttenuation = 0; // Force minimum attenuation + + op.EnvelopeState = EnvelopeState.Decay; + if (op.SL_SustainLevel == 0) // If Sustain Level is 0, we skip Decay and go straight to Sustain phase. + op.EnvelopeState = EnvelopeState.Sustain; + + } + else + { + + // Regular Key-On + op.EnvelopeState = EnvelopeState.Attack; + + } + } + + static void KeyOff(Operator op) + { + op.EnvelopeState = EnvelopeState.Release; + } + + // ==================================================================================== + // Operator Unit + // ==================================================================================== + + int GetOperatorOutput(Operator op, int phaseModulationInput14) + { + if (op.EgAttenuation == MaxAttenuation) + return 0; + + RunPhaseGenerator(op); + int phase10 = op.PhaseCounter >> 10; + + // operators return a 14-bit output, but take a 10-bit input. What 4 bits are discarded? not the obvious ones... + // the input is shifted right by one; the least significant bit and the 3 most significant bits are discarded. + int phaseModulationInput10 = (phaseModulationInput14 >> 1) & 0x3FF; + + phase10 += phaseModulationInput10; + phase10 &= 0x3FF; + + return OperatorCalc(phase10, op.AdjustedEGOutput); + } + + static void RunPhaseGenerator(Operator op) + { + // Take the Frequency Number & shift based on Block + int phaseIncrement = op.FrequencyNumber; + switch (op.Block) + { + case 0: phaseIncrement >>= 1; break; + case 1: break; + default: phaseIncrement <<= op.Block - 1; break; + } + + // Apply Detune + int detuneAdjustment = detuneTable[(op.KeyCode * 4) + (op.DT_Detune & 3)]; + if ((op.DT_Detune & 4) != 0) + detuneAdjustment = -detuneAdjustment; + phaseIncrement += detuneAdjustment; + phaseIncrement &= 0x1FFFF; // mask to 17-bits, which is the current size of the register at this point in the calculation. This allows proper detune overflow. + + // Apply MUL + switch (op.MUL_Multiple) + { + case 0: phaseIncrement /= 2; break; + default: phaseIncrement *= op.MUL_Multiple; break; + } + + op.PhaseCounter += phaseIncrement; + op.PhaseCounter &= 0xFFFFF; + } + + static int OperatorCalc(int phase10, int attenuation) + { + // calculate sin + double phaseNormalized = (phase10 / 1023d); + double sinResult = Math.Sin(phaseNormalized * Math.PI * 2); + + // convert attenuation into linear power representation + const double attenuationIndividualBitWeighting = 48.0 / 1024.0; + double attenuationInBels = (((double)attenuation * attenuationIndividualBitWeighting) / 10.0); + double powerLinear = Math.Pow(10.0, -attenuationInBels); + + // attenuate result + double resultNormalized = sinResult * powerLinear; + + // calculate 14-bit operator output + const int maxOperatorOutput = 8191; + return (int)(resultNormalized * maxOperatorOutput); + } + + // ==================================================================================== + // Channel Unit + // ==================================================================================== + + const int max14bitValue = 0x1FFF; // maximum signed value + + int GetChannelOutput(Channel channel, int maxVolume) + { + int outc = 0; + + switch (channel.Algorithm) + { + case 0: + { + int out1; + out1 = GetOperatorOutput(channel.Operators[0], 0); + out1 = GetOperatorOutput(channel.Operators[1], out1); + out1 = GetOperatorOutput(channel.Operators[2], out1); + outc = GetOperatorOutput(channel.Operators[3], out1); + break; + } + + case 1: + { + int out1, out2; + out1 = GetOperatorOutput(channel.Operators[0], 0); + out2 = GetOperatorOutput(channel.Operators[1], 0); + outc = GetOperatorOutput(channel.Operators[2], Limit(out1 + out2, -8191, 8191)); // TODO test whether these Limit calls are actually correct. technically I expect it to be overflowing in a 10-bit space. + outc = GetOperatorOutput(channel.Operators[3], outc); + break; + } + + case 2: + { + int out1, out2; + out1 = GetOperatorOutput(channel.Operators[0], 0); + out2 = GetOperatorOutput(channel.Operators[1], 0); + out2 = GetOperatorOutput(channel.Operators[2], out2); + outc = GetOperatorOutput(channel.Operators[3], Limit(out1 + out2, -8191, 8191)); + break; + } + + case 3: + { + int out1, out2; + out1 = GetOperatorOutput(channel.Operators[0], 0); + out1 = GetOperatorOutput(channel.Operators[1], out1); + out2 = GetOperatorOutput(channel.Operators[2], 0); + outc = GetOperatorOutput(channel.Operators[3], Limit(out1 + out2, -8191, 8191)); + break; + } + + case 4: + { + int out1, out2; + out1 = GetOperatorOutput(channel.Operators[0], 0); + out1 = GetOperatorOutput(channel.Operators[1], out1); + out2 = GetOperatorOutput(channel.Operators[2], 0); + out2 = GetOperatorOutput(channel.Operators[3], out2); + outc = Limit(out1 + out2, -8191, 8191); + break; + } + + case 5: + { + int out1, out2, out3, out4; + out1 = GetOperatorOutput(channel.Operators[0], 0); + out2 = GetOperatorOutput(channel.Operators[1], out1); + out3 = GetOperatorOutput(channel.Operators[2], out1); + out4 = GetOperatorOutput(channel.Operators[3], out1); + outc = Limit(out2 + out3 + out4, -8191, 8191); + break; + } + + case 6: + { + int out1, out2, out3, out4; + out1 = GetOperatorOutput(channel.Operators[0], 0); + out2 = GetOperatorOutput(channel.Operators[1], out1); + out3 = GetOperatorOutput(channel.Operators[2], out1); + out4 = GetOperatorOutput(channel.Operators[3], out1); + outc = Limit(out2 + out3 + out4, -8191, 8191); + break; + } + + case 7: + { + int out1, out2, out3, out4; + out1 = GetOperatorOutput(channel.Operators[0], 0); + out2 = GetOperatorOutput(channel.Operators[1], out1); + out3 = GetOperatorOutput(channel.Operators[2], 0); + out4 = GetOperatorOutput(channel.Operators[3], 0); + outc = Limit(out2 + out3 + out4, -8191, 8191); + break; + } + } + + return outc * maxVolume / max14bitValue; + } + + static int Limit(int value, int min, int max) + { + if (value < min) return min; + if (value > max) return max; + return value; + } + + // ==================================================================================== + // Support Classes/Structs/Enums + // ==================================================================================== + + public enum EnvelopeState + { + Attack, + Decay, + Sustain, + Release, + Off + } + + public sealed class Operator + { + // External Settings + + public int TL_TotalLevel; // 7 bits + public int SL_SustainLevel; // 4 bits + public int AR_AttackRate; // 5 bits + public int DR_DecayRate; // 5 bits + public int SR_SustainRate; // 5 bits + public int RR_ReleaseRate; // 4 bits + public int KS_KeyScale; // 2 bits + public int SSG_EG; // 4 bits + + public int DT_Detune; // 3 bits + public int MUL_Multiple; // 4 bits + + public bool AM_AmplitudeModulation; // 1 bit + + public int FrequencyNumber; // 11 bits + public int Block; // 3 bits + + public int KeyCode; // 5 bits (described on pg 25 of YM2608 docs) + public int Rks; // 5 bits (described on pg 29 of YM2608 docs) + + // Internal State + + public int PhaseCounter; // 20 bits, where the 10 most significant bits are output to the operator. + + public EnvelopeState EnvelopeState = EnvelopeState.Off; + + private int egAttenuation = MaxAttenuation; // 10-bit attenuation value output from envelope generator + public int EgAttenuation + { + get { return egAttenuation; } + set + { + egAttenuation = value; + if (egAttenuation < 0) egAttenuation = 0; + if (egAttenuation > 1023) egAttenuation = 1023; + } + } + + public int Normalized10BitSL { get { return slTable[SL_SustainLevel]; } } + public int Normalized10BitTL { get { return TL_TotalLevel << 3; } } + public int AdjustedEGOutput { get { return Math.Min(egAttenuation + Normalized10BitTL, 1023); } } + } + + public sealed class Channel + { + public readonly Operator[] Operators = { new Operator(), new Operator(), new Operator(), new Operator() }; + + public int FrequencyNumber; // 11 bits + public int Block; // 3 bits + public int Feedback; // 3 bits + public int Algorithm; // 3 bits (algorithms 0 - 7) + + public bool SpecialMode; // TODO, there are 2 special modes, a bool is not going to do the trick. + public bool LeftOutput = true; // These apparently need to be initialized on. + public bool RightOutput = true; // These apparently need to be initialized on. + + public int AMS_AmplitudeModulationSensitivity; // 3 bits + public int FMS_FrequencyModulationSensitivity; // 2 bits + } + + // ==================================================================================== + // ISoundProvider + // ==================================================================================== + + public void GetSamples(short[] samples) + { + // Generate raw samples at native sampling rate (~53hz) + int numStereoSamples = samples.Length / 2; + int nativeStereoSamples = (int)(numStereoSamples * ntsc44100Factor) * 2; + short[] nativeSamples = new short[nativeStereoSamples]; + GetSamplesNative(nativeSamples); + + // downsample from native output rate to 44100. + //CrappyNaiveResampler(nativeSamples, samples); + MaybeBetterDownsampler(nativeSamples, samples); //LinearDownsampler(nativeSamples, samples); - } + } - static void CrappyNaiveResampler(short[] input, short[] output) - { - // this is not good resampling code. - int numStereoSamples = output.Length / 2; - - int offset = 0; - for (int i = 0; i < numStereoSamples; i++) - { - int nativeOffset = ((i * ntscOutputRate) / 44100) * 2; - output[offset++] += input[nativeOffset++]; // left - output[offset++] += input[nativeOffset]; // right - } - } + static void CrappyNaiveResampler(short[] input, short[] output) + { + // this is not good resampling code. + int numStereoSamples = output.Length / 2; - static double Fraction(double value) - { - return value - Math.Floor(value); - } + int offset = 0; + for (int i = 0; i < numStereoSamples; i++) + { + int nativeOffset = ((i * ntscOutputRate) / 44100) * 2; + output[offset++] += input[nativeOffset++]; // left + output[offset++] += input[nativeOffset]; // right + } + } + + static double Fraction(double value) + { + return value - Math.Floor(value); + } static void LinearDownsampler(short[] input, short[] output) { @@ -1075,219 +1074,219 @@ namespace BizHawk.Emulation.Common.Components } - static void MaybeBetterDownsampler(short[] input, short[] output) - { - // This is still not a good resampler. But it's better than the other one. Unsure how much difference it makes. - // The difference with this one is that all source samples will be sampled and weighted, none skipped over. - double nativeSamplesPerOutputSample = (double) input.Length / (double) output.Length; - int outputStereoSamples = output.Length / 2; - int inputStereoSamples = input.Length / 2; + static void MaybeBetterDownsampler(short[] input, short[] output) + { + // This is still not a good resampler. But it's better than the other one. Unsure how much difference it makes. + // The difference with this one is that all source samples will be sampled and weighted, none skipped over. + double nativeSamplesPerOutputSample = (double)input.Length / (double)output.Length; + int outputStereoSamples = output.Length / 2; + int inputStereoSamples = input.Length / 2; - int offset = 0; - for (int i = 0; i < outputStereoSamples; i++) - { - - double startSample = nativeSamplesPerOutputSample * i; - double endSample = nativeSamplesPerOutputSample * (i+1); + int offset = 0; + for (int i = 0; i < outputStereoSamples; i++) + { - int iStartSample = (int) Math.Floor(startSample); - int iEndSample = (int) Math.Floor(endSample); - double leftSample = 0; - double rightSample = 0; - for (int j = iStartSample; j <= iEndSample; j++) - { - if (j == inputStereoSamples) - break; + double startSample = nativeSamplesPerOutputSample * i; + double endSample = nativeSamplesPerOutputSample * (i + 1); - double weight = 1.0; + int iStartSample = (int)Math.Floor(startSample); + int iEndSample = (int)Math.Floor(endSample); + double leftSample = 0; + double rightSample = 0; + for (int j = iStartSample; j <= iEndSample; j++) + { + if (j == inputStereoSamples) + break; - if (j == iStartSample) - weight = 1.0 - Fraction(startSample); - else if (j == iEndSample) - weight = Fraction(endSample); + double weight = 1.0; - leftSample += ((double) input[(j * 2) + 0] * weight); - rightSample += ((double) input[(j * 2) + 1] * weight); - } - output[offset++] = (short) leftSample; - output[offset++] = (short) rightSample; - } - } + if (j == iStartSample) + weight = 1.0 - Fraction(startSample); + else if (j == iEndSample) + weight = Fraction(endSample); - void GetSamplesNative(short[] samples) - { - int elapsedCycles = frameEndClock - frameStartClock; - int start = 0; - while (commands.Count > 0) - { - var cmd = commands.Dequeue(); - int pos = ((cmd.Clock * samples.Length) / elapsedCycles) & ~1; - GetSamplesImmediate(samples, start, pos - start); - start = pos; - WriteCommand(cmd); - } - GetSamplesImmediate(samples, start, samples.Length - start); - } + leftSample += ((double)input[(j * 2) + 0] * weight); + rightSample += ((double)input[(j * 2) + 1] * weight); + } + output[offset++] = (short)leftSample; + output[offset++] = (short)rightSample; + } + } - void GetSamplesImmediate(short[] samples, int pos, int length) - { - int channelVolume = MaxVolume / 6; + void GetSamplesNative(short[] samples) + { + int elapsedCycles = frameEndClock - frameStartClock; + int start = 0; + while (commands.Count > 0) + { + var cmd = commands.Dequeue(); + int pos = ((cmd.Clock * samples.Length) / elapsedCycles) & ~1; + GetSamplesImmediate(samples, start, pos - start); + start = pos; + WriteCommand(cmd); + } + GetSamplesImmediate(samples, start, samples.Length - start); + } - for (int i = 0; i < length / 2; i++) - { - MaybeRunEnvelopeGenerator(); + void GetSamplesImmediate(short[] samples, int pos, int length) + { + int channelVolume = MaxVolume / 6; - // Generate FM output - for (int ch = 0; ch < 6; ch++) - { - short sample = (short)GetChannelOutput(Channels[ch], channelVolume); + for (int i = 0; i < length / 2; i++) + { + MaybeRunEnvelopeGenerator(); - if (ch < 5 || DacEnable == false) - { - if (Channels[ch].LeftOutput) samples[pos] += sample; - if (Channels[ch].RightOutput) samples[pos + 1] += sample; - } - else - { - short dacValue = (short)(((DacValue - 80) * channelVolume) / 80); - if (Channels[5].LeftOutput) samples[pos] += dacValue; - if (Channels[5].RightOutput) samples[pos + 1] += dacValue; - } - } - pos += 2; - } - } + // Generate FM output + for (int ch = 0; ch < 6; ch++) + { + short sample = (short)GetChannelOutput(Channels[ch], channelVolume); - public void DiscardSamples() { } - public int MaxVolume { get; set; } + if (ch < 5 || DacEnable == false) + { + if (Channels[ch].LeftOutput) samples[pos] += sample; + if (Channels[ch].RightOutput) samples[pos + 1] += sample; + } + else + { + short dacValue = (short)(((DacValue - 80) * channelVolume) / 80); + if (Channels[5].LeftOutput) samples[pos] += dacValue; + if (Channels[5].RightOutput) samples[pos + 1] += dacValue; + } + } + pos += 2; + } + } - // ==================================================================================== - // Save States - // ==================================================================================== + public void DiscardSamples() { } + public int MaxVolume { get; set; } - public void SaveStateBinary(BinaryWriter writer) - { - writer.Write(TimerAPeriod); - writer.Write(TimerBPeriod); - writer.Write(TimerATripped); - writer.Write(TimerBTripped); - writer.Write(TimerAResetClock); - writer.Write(TimerBResetClock); - writer.Write(TimerALastReset); - writer.Write(TimerBLastReset); - writer.Write(TimerControl27); - writer.Write(egDivisorCounter); - writer.Write(egCycleCounter); - - writer.Write(PartSelect); - writer.Write(RegisterSelect); - writer.Write(DacEnable); - writer.Write(DacValue); + // ==================================================================================== + // Save States + // ==================================================================================== - for (int i = 0; i < 6; i++) - ChannelSaveStateBinary(writer, Channels[i]); - } + public void SaveStateBinary(BinaryWriter writer) + { + writer.Write(TimerAPeriod); + writer.Write(TimerBPeriod); + writer.Write(TimerATripped); + writer.Write(TimerBTripped); + writer.Write(TimerAResetClock); + writer.Write(TimerBResetClock); + writer.Write(TimerALastReset); + writer.Write(TimerBLastReset); + writer.Write(TimerControl27); + writer.Write(egDivisorCounter); + writer.Write(egCycleCounter); - public void LoadStateBinary(BinaryReader reader) - { - TimerAPeriod = reader.ReadInt32(); - TimerBPeriod = reader.ReadInt32(); - TimerATripped = reader.ReadBoolean(); - TimerBTripped = reader.ReadBoolean(); - TimerAResetClock = reader.ReadInt32(); - TimerBResetClock = reader.ReadInt32(); - TimerALastReset = reader.ReadInt32(); - TimerBLastReset = reader.ReadInt32(); - TimerControl27 = reader.ReadByte(); - egDivisorCounter = reader.ReadInt32(); - egCycleCounter = reader.ReadInt32(); + writer.Write(PartSelect); + writer.Write(RegisterSelect); + writer.Write(DacEnable); + writer.Write(DacValue); - PartSelect = reader.ReadByte(); - RegisterSelect = reader.ReadByte(); - DacEnable = reader.ReadBoolean(); - DacValue = reader.ReadByte(); + for (int i = 0; i < 6; i++) + ChannelSaveStateBinary(writer, Channels[i]); + } - for (int i = 0; i < 6; i++) - ChannelLoadStateBinary(reader, Channels[i]); - } + public void LoadStateBinary(BinaryReader reader) + { + TimerAPeriod = reader.ReadInt32(); + TimerBPeriod = reader.ReadInt32(); + TimerATripped = reader.ReadBoolean(); + TimerBTripped = reader.ReadBoolean(); + TimerAResetClock = reader.ReadInt32(); + TimerBResetClock = reader.ReadInt32(); + TimerALastReset = reader.ReadInt32(); + TimerBLastReset = reader.ReadInt32(); + TimerControl27 = reader.ReadByte(); + egDivisorCounter = reader.ReadInt32(); + egCycleCounter = reader.ReadInt32(); - void ChannelSaveStateBinary(BinaryWriter writer, Channel c) - { - // TODO reduce size of state via casting - writer.Write(c.FrequencyNumber); - writer.Write(c.Block); - writer.Write(c.Feedback); - writer.Write(c.Algorithm); - writer.Write(c.SpecialMode); - writer.Write(c.LeftOutput); - writer.Write(c.RightOutput); - writer.Write(c.AMS_AmplitudeModulationSensitivity); - writer.Write(c.FMS_FrequencyModulationSensitivity); + PartSelect = reader.ReadByte(); + RegisterSelect = reader.ReadByte(); + DacEnable = reader.ReadBoolean(); + DacValue = reader.ReadByte(); - for (int i = 0; i < 4; i++) - OperatorSaveStateBinary(writer, c.Operators[i]); - } + for (int i = 0; i < 6; i++) + ChannelLoadStateBinary(reader, Channels[i]); + } - void ChannelLoadStateBinary(BinaryReader reader, Channel c) - { - c.FrequencyNumber = reader.ReadInt32(); - c.Block = reader.ReadInt32(); - c.Feedback = reader.ReadInt32(); - c.Algorithm = reader.ReadInt32(); - c.SpecialMode = reader.ReadBoolean(); - c.LeftOutput = reader.ReadBoolean(); - c.RightOutput = reader.ReadBoolean(); - c.AMS_AmplitudeModulationSensitivity = reader.ReadInt32(); - c.FMS_FrequencyModulationSensitivity = reader.ReadInt32(); + void ChannelSaveStateBinary(BinaryWriter writer, Channel c) + { + // TODO reduce size of state via casting + writer.Write(c.FrequencyNumber); + writer.Write(c.Block); + writer.Write(c.Feedback); + writer.Write(c.Algorithm); + writer.Write(c.SpecialMode); + writer.Write(c.LeftOutput); + writer.Write(c.RightOutput); + writer.Write(c.AMS_AmplitudeModulationSensitivity); + writer.Write(c.FMS_FrequencyModulationSensitivity); - for (int i = 0; i < 4; i++) - OperatorLoadStateBinary(reader, c.Operators[i]); - } + for (int i = 0; i < 4; i++) + OperatorSaveStateBinary(writer, c.Operators[i]); + } - void OperatorSaveStateBinary(BinaryWriter writer, Operator op) - { - // TODO, size of states could be shrunken by using casts. - writer.Write(op.TL_TotalLevel); - writer.Write(op.SL_SustainLevel); - writer.Write(op.AR_AttackRate); - writer.Write(op.DR_DecayRate); - writer.Write(op.SR_SustainRate); - writer.Write(op.RR_ReleaseRate); - writer.Write(op.KS_KeyScale); - writer.Write(op.SSG_EG); - writer.Write(op.DT_Detune); - writer.Write(op.MUL_Multiple); - writer.Write(op.AM_AmplitudeModulation); - writer.Write(op.FrequencyNumber); - writer.Write(op.Block); - writer.Write(op.KeyCode); - writer.Write(op.Rks); - writer.Write(op.PhaseCounter); - writer.Write((byte)op.EnvelopeState); - writer.Write(op.EgAttenuation); - } + void ChannelLoadStateBinary(BinaryReader reader, Channel c) + { + c.FrequencyNumber = reader.ReadInt32(); + c.Block = reader.ReadInt32(); + c.Feedback = reader.ReadInt32(); + c.Algorithm = reader.ReadInt32(); + c.SpecialMode = reader.ReadBoolean(); + c.LeftOutput = reader.ReadBoolean(); + c.RightOutput = reader.ReadBoolean(); + c.AMS_AmplitudeModulationSensitivity = reader.ReadInt32(); + c.FMS_FrequencyModulationSensitivity = reader.ReadInt32(); - void OperatorLoadStateBinary(BinaryReader reader, Operator op) - { - op.TL_TotalLevel = reader.ReadInt32(); - op.SL_SustainLevel = reader.ReadInt32(); - op.AR_AttackRate = reader.ReadInt32(); - op.DR_DecayRate = reader.ReadInt32(); - op.SR_SustainRate = reader.ReadInt32(); - op.RR_ReleaseRate = reader.ReadInt32(); - op.KS_KeyScale = reader.ReadInt32(); - op.SSG_EG = reader.ReadInt32(); - op.DT_Detune = reader.ReadInt32(); - op.MUL_Multiple = reader.ReadInt32(); - op.AM_AmplitudeModulation = reader.ReadBoolean(); + for (int i = 0; i < 4; i++) + OperatorLoadStateBinary(reader, c.Operators[i]); + } - op.FrequencyNumber = reader.ReadInt32(); - op.Block = reader.ReadInt32(); - op.KeyCode = reader.ReadInt32(); - op.Rks = reader.ReadInt32(); - op.PhaseCounter = reader.ReadInt32(); - op.EnvelopeState = (EnvelopeState)Enum.ToObject(typeof(EnvelopeState), reader.ReadByte()); - op.EgAttenuation = reader.ReadInt32(); - } - } + void OperatorSaveStateBinary(BinaryWriter writer, Operator op) + { + // TODO, size of states could be shrunken by using casts. + writer.Write(op.TL_TotalLevel); + writer.Write(op.SL_SustainLevel); + writer.Write(op.AR_AttackRate); + writer.Write(op.DR_DecayRate); + writer.Write(op.SR_SustainRate); + writer.Write(op.RR_ReleaseRate); + writer.Write(op.KS_KeyScale); + writer.Write(op.SSG_EG); + writer.Write(op.DT_Detune); + writer.Write(op.MUL_Multiple); + writer.Write(op.AM_AmplitudeModulation); + writer.Write(op.FrequencyNumber); + writer.Write(op.Block); + writer.Write(op.KeyCode); + writer.Write(op.Rks); + writer.Write(op.PhaseCounter); + writer.Write((byte)op.EnvelopeState); + writer.Write(op.EgAttenuation); + } + + void OperatorLoadStateBinary(BinaryReader reader, Operator op) + { + op.TL_TotalLevel = reader.ReadInt32(); + op.SL_SustainLevel = reader.ReadInt32(); + op.AR_AttackRate = reader.ReadInt32(); + op.DR_DecayRate = reader.ReadInt32(); + op.SR_SustainRate = reader.ReadInt32(); + op.RR_ReleaseRate = reader.ReadInt32(); + op.KS_KeyScale = reader.ReadInt32(); + op.SSG_EG = reader.ReadInt32(); + op.DT_Detune = reader.ReadInt32(); + op.MUL_Multiple = reader.ReadInt32(); + op.AM_AmplitudeModulation = reader.ReadBoolean(); + + op.FrequencyNumber = reader.ReadInt32(); + op.Block = reader.ReadInt32(); + op.KeyCode = reader.ReadInt32(); + op.Rks = reader.ReadInt32(); + op.PhaseCounter = reader.ReadInt32(); + op.EnvelopeState = (EnvelopeState)Enum.ToObject(typeof(EnvelopeState), reader.ReadByte()); + op.EgAttenuation = reader.ReadInt32(); + } + } } \ No newline at end of file