diff --git a/CPU_emu/CPU_emu.CMD_Methods.cs b/CPU_emu/CPU_emu.CMD_Methods.cs index 1f64848..d70669a 100644 --- a/CPU_emu/CPU_emu.CMD_Methods.cs +++ b/CPU_emu/CPU_emu.CMD_Methods.cs @@ -98,7 +98,7 @@ public void Cmd_B4() #region stack push pull - // Push Accumulator on Stack + // Push Accumulator on Stack PHA public void Cmd_48() { PushByteToStack(A,ref _CpuCycle); diff --git a/CPU_emu/Class_CPU.cs b/CPU_emu/Class_CPU.cs index bc8f359..068594f 100644 --- a/CPU_emu/Class_CPU.cs +++ b/CPU_emu/Class_CPU.cs @@ -1,4 +1,5 @@ -using CPU_emulator; +using CPU_emu; +using CPU_emulator; using System; using System.Collections.Generic; using System.ComponentModel; @@ -31,6 +32,8 @@ public partial class CPU private const uint MAX_MEM = 1024 * 64; private byte[] Data = new byte[MAX_MEM]; + MemoryBus _memoryBus; + private byte NegativeFlagBit = 0b10000000, OverflowFlagBit = 0b01000000, @@ -42,6 +45,8 @@ private byte public bool SteppingMode { get => _SteppingMode; set => _SteppingMode = value; } public bool ExitRequested { get => _ExitRequested; set => _ExitRequested = value; } public ulong InterruptPeriod { get => _InterruptPeriod; set => _InterruptPeriod = value; } + + public ulong CpuCycle { get => _CpuCycle; } public byte[] Memory { get @@ -70,6 +75,7 @@ public byte[] Memory public CPU() { + _memoryBus = new MemoryBus(ref Data); SetVectors(); } @@ -91,7 +97,7 @@ public void Reset() SetRegister("A", 0); SetRegister("X", 0); SetRegister("Y", 0); - InterruptPeriod = 1000; + ExitRequested = false; // set all status flags to false @@ -105,51 +111,7 @@ public void Reset() } - private void LoadInlineTestProg() - { - Data[PC] = 0xB5; - Data[PC + 1] = 0x0b; - Data[PC + 2] = 0x48; - Data[PC + 3] = 0xA9; - Data[PC + 4] = 0xac; - Data[PC + 5] = 0x48; - Data[PC + 6] = 0xA9; - Data[PC + 7] = 0x00; - Data[PC + 8] = 0x48; - Data[PC + 9] = 0xA9; - Data[PC + 10] = 0xae; - Data[PC + 11] = 0x48; - - Data[PC + 12] = 0x48; - Data[PC + 13] = 0x48; - Data[PC + 14] = 0x48; - Data[PC + 15] = 0x48; - - Data[PC + 16] = 0x48; - Data[PC + 17] = 0x48; - Data[PC + 18] = 0x48; - Data[PC + 19] = 0x48; - - Data[PC + 20] = 0x48; - Data[PC + 21] = 0x48; - Data[PC + 22] = 0x48; - Data[PC + 23] = 0x48; - Data[PC + 24] = 0x48; - Data[PC + 25] = 0x48; - Data[PC + 26] = 0x48; - Data[PC + 27] = 0x48; - - Data[PC + 28] = 0x48; - Data[PC + 29] = 0x68; - Data[PC + 30] = 0x68; - Data[PC + 31] = 0x68; - Data[PC + 32] = 0x68; - Data[PC + 33] = 0x68; - Data[PC + 34] = 0x68; - Data[PC + 35] = 0x68; - - OnMemoryUpdate?.Invoke(this, new CPUEventArgs(this)); - } + public void Start() { @@ -163,8 +125,7 @@ public void Start() private void CpuRunner_DoWork(object sender, DoWorkEventArgs e) { - //_CpuCycle = InterruptPeriod; - + Type thisType = this.GetType(); @@ -172,28 +133,8 @@ private void CpuRunner_DoWork(object sender, DoWorkEventArgs e) { byte instruction = FetchByte(); - // Build method name from 'Cmd' + opcode - string cmd = "Cmd_" + instruction.ToString("X2").ToUpper(); - - MethodInfo theMethod2Call = thisType.GetMethod(cmd); - // Check if method Cmd_ exists - if (theMethod2Call != null) - { - // check for method attribute - OpcodeAttribute? attribute = theMethod2Call.GetCustomAttribute(); - - theMethod2Call.Invoke(this, new object[] { }); + CallInstruction(thisType, instruction); - if (attribute != null) - { - IncrementCpuCycle((ulong)attribute.Cycles); - } - } - else - { - MessageBox.Show(string.Format("Method {0} not found!", cmd),"Error!",MessageBoxButtons.OK,MessageBoxIcon.Error); - } - if (_SteppingMode) { break; @@ -208,13 +149,38 @@ private void CpuRunner_DoWork(object sender, DoWorkEventArgs e) break; } - + if (SlowDown && !_SteppingMode) { System.Threading.Thread.Sleep(SlowDownTime); } - + + } + } + + private void CallInstruction(Type thisType, byte instruction) + { + // Build method name from 'Cmd' + opcode + string cmd = "Cmd_" + instruction.ToString("X2").ToUpper(); + + MethodInfo theMethod2Call = thisType.GetMethod(cmd); + // Check if method Cmd_ exists + if (theMethod2Call != null) + { + // check for method attribute + OpcodeAttribute? attribute = theMethod2Call.GetCustomAttribute(); + + theMethod2Call.Invoke(this, new object[] { }); + + if (attribute != null) + { + IncrementCpuCycle((ulong)attribute.Cycles); + } + } + else + { + MessageBox.Show(string.Format("Method {0} not found!", cmd), "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error); } } @@ -256,7 +222,8 @@ private void SetZeroAndNegativeFlags(byte register) private byte FetchByte() { - byte data = Data[PC]; + //byte data = Data[PC]; + byte data = _memoryBus.Read((ushort)PC); IncrementPC(); IncrementCpuCycle(1); return data; @@ -275,13 +242,15 @@ private ushort FetchWord(ref ulong CpuCycle) public void WriteByteToMemory(byte b,ushort address) { - Data[address] = b; + //Data[address] = b; + _memoryBus.Write(address, b); OnMemoryUpdate?.Invoke(this, new CPUEventArgs(this)); } private byte ReadByteFromMemory(ushort address) { - return Data[address]; + //return Data[address]; + return _memoryBus.Read(address); } private ushort ReadWordFromMemory(ushort address) diff --git a/CPU_emu/Forms/CPU_emu.Designer.cs b/CPU_emu/Forms/CPU_emu.Designer.cs index 7bc2c42..2b8164a 100644 --- a/CPU_emu/Forms/CPU_emu.Designer.cs +++ b/CPU_emu/Forms/CPU_emu.Designer.cs @@ -329,7 +329,7 @@ private void InitializeComponent() toolStripMenuItemStyleTXT.CheckOnClick = true; toolStripMenuItemStyleTXT.CheckState = System.Windows.Forms.CheckState.Checked; toolStripMenuItemStyleTXT.Name = "toolStripMenuItemStyleTXT"; - toolStripMenuItemStyleTXT.Size = new System.Drawing.Size(95, 22); + toolStripMenuItemStyleTXT.Size = new System.Drawing.Size(94, 22); toolStripMenuItemStyleTXT.Text = "TXT"; toolStripMenuItemStyleTXT.Click += toolStripMenuItemStyleTXT_Click; // @@ -337,7 +337,7 @@ private void InitializeComponent() // toolStripMenuItemStyleLED.CheckOnClick = true; toolStripMenuItemStyleLED.Name = "toolStripMenuItemStyleLED"; - toolStripMenuItemStyleLED.Size = new System.Drawing.Size(95, 22); + toolStripMenuItemStyleLED.Size = new System.Drawing.Size(94, 22); toolStripMenuItemStyleLED.Text = "LED"; toolStripMenuItemStyleLED.Click += toolStripMenuItemStyleLED_Click; // @@ -711,7 +711,7 @@ private void InitializeComponent() // toolStripProgressBar1 // toolStripProgressBar1.Name = "toolStripProgressBar1"; - toolStripProgressBar1.Size = new System.Drawing.Size(233, 18); + toolStripProgressBar1.Size = new System.Drawing.Size(300, 18); toolStripProgressBar1.Step = 1; toolStripProgressBar1.Style = System.Windows.Forms.ProgressBarStyle.Continuous; toolStripProgressBar1.Visible = false; @@ -725,7 +725,7 @@ private void InitializeComponent() // toolStripStatusLabelKernal // toolStripStatusLabelKernal.Name = "toolStripStatusLabelKernal"; - toolStripStatusLabelKernal.Size = new System.Drawing.Size(24, 19); + toolStripStatusLabelKernal.Size = new System.Drawing.Size(25, 19); toolStripStatusLabelKernal.Text = "0x0"; // // toolStripStatusLabel2 @@ -737,7 +737,7 @@ private void InitializeComponent() // toolStripStatusLabelBasic // toolStripStatusLabelBasic.Name = "toolStripStatusLabelBasic"; - toolStripStatusLabelBasic.Size = new System.Drawing.Size(24, 19); + toolStripStatusLabelBasic.Size = new System.Drawing.Size(25, 19); toolStripStatusLabelBasic.Text = "0x0"; // // toolStripStatusLabel3 diff --git a/CPU_emu/Interfaces/IMemoryBus.cs b/CPU_emu/Interfaces/IMemoryBus.cs new file mode 100644 index 0000000..7149b5f --- /dev/null +++ b/CPU_emu/Interfaces/IMemoryBus.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CPU_emu; + +internal interface IMemoryBus +{ + byte Read(ushort address); + void Write(ushort address, byte value); + void Load(ushort address, params byte[] data); + byte[] Dump(ushort address, int length); + + +} diff --git a/CPU_emu/MemoryBus.cs b/CPU_emu/MemoryBus.cs new file mode 100644 index 0000000..83c6312 --- /dev/null +++ b/CPU_emu/MemoryBus.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CPU_emu +{ + internal class MemoryBus : IMemoryBus + { + private readonly byte[] _memory; + + public MemoryBus(int memSize = 1024 * 64) + { + _memory = new byte[memSize]; + } + public MemoryBus(ref byte[] memory) + { + _memory = memory; + } + public byte Read(ushort address) + { + return _memory[address]; + } + + public void Write(ushort address, byte value) + { + _memory[address] = value; + } + + public void Load(ushort address, params byte[] data) + { + Array.Copy(data, 0, _memory, address, data.Length); + } + + public byte[] Dump(ushort address, int length) + { + return _memory.Skip(address).Take(length).ToArray(); + } + + + } +} diff --git a/CPU_emuTests/CPU_Tests.cs b/CPU_emuTests/CPU_Tests.cs index 519819b..8745d51 100644 --- a/CPU_emuTests/CPU_Tests.cs +++ b/CPU_emuTests/CPU_Tests.cs @@ -1,554 +1,572 @@ +using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.DataCollection; using System.Reflection; -namespace CPU_emulator +namespace CPU_emulator; + +[TestFixture] +public class CPU_Tests { - [TestFixture] - public class CPU_Tests + private CPU cpu; + + [SetUp] + public void Setup() { - private CPU cpu; - - [SetUp] - public void Setup() - { - cpu = new CPU(); - } - #region set registers - [Test] - public void SetRegister_A_Test() - { - byte value = 0xff; - cpu.SetRegister("A", value); - Assert.That(cpu.A, Is.EqualTo(0xff)); + cpu = new CPU(); + } + + #region set registers - } + [Test] + public void SetRegister_A_Test() + { + byte value = 0xff; + cpu.SetRegister("A", value); + Assert.That(cpu.A, Is.EqualTo(0xff)); - [Test] - public void SetRegister_X_Test() - { - byte value = 0xff; - cpu.SetRegister("X", value); - Assert.That(cpu.X, Is.EqualTo(0xff)); + } - } + [Test] + public void SetRegister_X_Test() + { + byte value = 0xff; + cpu.SetRegister("X", value); + Assert.That(cpu.X, Is.EqualTo(0xff)); - [Test] - public void SetRegister_Y_Test() - { - byte value = 0xff; - cpu.SetRegister("Y", value); - Assert.That(cpu.Y, Is.EqualTo(0xff)); + } - } - #endregion + [Test] + public void SetRegister_Y_Test() + { + byte value = 0xff; + cpu.SetRegister("Y", value); + Assert.That(cpu.Y, Is.EqualTo(0xff)); - #region misc - [Test] - public void Reset_Test() - { - - cpu.Reset(); - - - Assert.That(cpu.PC, Is.EqualTo(0x200)); - Assert.That(cpu.SP, Is.EqualTo(0x1FF)); - Assert.That(cpu.A, Is.EqualTo(0)); - Assert.That(cpu.X, Is.EqualTo(0)); - Assert.That(cpu.A, Is.EqualTo(0)); - - Assert.That(cpu.flags["C"], Is.False); - Assert.That(cpu.flags["Z"], Is.False); - Assert.That(cpu.flags["I"], Is.False); - Assert.That(cpu.flags["D"], Is.False); - Assert.That(cpu.flags["B"], Is.False); - Assert.That(cpu.flags["V"], Is.False); - Assert.That(cpu.flags["N"], Is.False); - - // TODO: Check events from PC and flags update - } + } + + #endregion set registers - [Test] - public void SetVectors_Test() - { - GetPrivateMethod("SetVectors", cpu).Invoke(cpu, null); - byte[] memory = cpu.ReadMemory(); + #region misc + [Test] + public void Reset_Test() + { + bool eventSetPCRaised = false; + bool eventFlagsUpdateRaised = false; + + cpu.OnProgramCounterUpdate += (object? sender, CPUEventArgs e) => { eventSetPCRaised = true; }; + cpu.OnFlagsUpdate += (object? sender, CPUEventArgs e) => { eventFlagsUpdateRaised = true; }; + + cpu.Reset(); + + Assert.IsTrue(eventSetPCRaised, "Event for PC update not raised"); + Assert.IsTrue(eventFlagsUpdateRaised, "Event for flags-update not raised"); + + Assert.That(cpu.PC, Is.EqualTo(0x200)); + Assert.That(cpu.SP, Is.EqualTo(0x1FF)); + Assert.That(cpu.A, Is.EqualTo(0)); + Assert.That(cpu.X, Is.EqualTo(0)); + Assert.That(cpu.A, Is.EqualTo(0)); + + Assert.That(cpu.flags["C"], Is.False); + Assert.That(cpu.flags["Z"], Is.False); + Assert.That(cpu.flags["I"], Is.False); + Assert.That(cpu.flags["D"], Is.False); + Assert.That(cpu.flags["B"], Is.False); + Assert.That(cpu.flags["V"], Is.False); + Assert.That(cpu.flags["N"], Is.False); - Assert.That(memory[0xFFFC], Is.EqualTo(0x00)); - Assert.That(memory[0xFFFD], Is.EqualTo(0x02)); - } + } + + [Test] + public void SetVectors_Test() + { + TestHelper.GetPrivateMethod("SetVectors", cpu).Invoke(cpu, null); + byte[] memory = cpu.ReadMemory(); - #endregion + Assert.That(memory[0xFFFC], Is.EqualTo(0x00)); + Assert.That(memory[0xFFFD], Is.EqualTo(0x02)); + } - #region set flags - [TestCase(0xff, ExpectedResult = new bool[] { false, true })] - [TestCase(0x00, ExpectedResult = new bool[] { true, false })] - public bool[] SetZeroAndNegativeFlags_Test(byte b) - { - CPUEventArgs? cpuea = null; + #endregion misc - cpu.OnFlagsUpdate += delegate (object? sender, CPUEventArgs e) - { - cpuea = e; - }; + #region set flags - bool[] result = new bool[2]; - GetPrivateMethod("SetZeroAndNegativeFlags",cpu).Invoke(cpu,new object[] {b}); + [TestCase(0xff, ExpectedResult = new bool[] { false, true })] + [TestCase(0x00, ExpectedResult = new bool[] { true, false })] + public bool[] SetZeroAndNegativeFlags_Test(byte b) + { + CPUEventArgs? cpuea = null; - if (cpuea != null) - { - switch (b) - { - case 0xff: - Assert.That(cpuea.Flags["Z"], Is.EqualTo(false)); - Assert.That(cpuea.Flags["N"], Is.EqualTo(true)); - break; - case 0x00: - Assert.That(cpuea.Flags["Z"], Is.EqualTo(true)); - Assert.That(cpuea.Flags["N"], Is.EqualTo(false)); - break; - - default: - break; - } - - - } - else + cpu.OnFlagsUpdate += delegate (object? sender, CPUEventArgs e) + { + cpuea = e; + }; + + bool[] result = new bool[2]; + TestHelper.GetPrivateMethod("SetZeroAndNegativeFlags",cpu).Invoke(cpu,new object[] {b}); + + if (cpuea != null) + { + switch (b) { - Assert.Fail("Event not triggered"); + case 0xff: + Assert.That(cpuea.Flags["Z"], Is.EqualTo(false)); + Assert.That(cpuea.Flags["N"], Is.EqualTo(true)); + break; + case 0x00: + Assert.That(cpuea.Flags["Z"], Is.EqualTo(true)); + Assert.That(cpuea.Flags["N"], Is.EqualTo(false)); + break; + + default: + break; } - result[0] = cpu.flags["Z"]; - result[1] = cpu.flags["N"]; - - return result; + } - #endregion - - #region fetch data - [Test] - public void FetchByte_Test() + else { - byte value = 0xff; - ulong cyc = 100; + Assert.Fail("Event not triggered"); + } - cpu.ResetMemory(); - cpu.WriteByteToMemory(value, 200); - cpu.SetPC(200); + result[0] = cpu.flags["Z"]; + result[1] = cpu.flags["N"]; - Assert.That(GetPrivateMethod("FetchByte", cpu).Invoke(cpu,null), Is.EqualTo(0xff)); - Assert.That(cpu.PC, Is.EqualTo(201)); + return result; + } + + #endregion set flags - } + #region fetch data + [Test] + public void FetchByte_Test() + { + byte value = 0xff; + ulong cyc = 100; - #endregion + cpu.ResetMemory(); + cpu.WriteByteToMemory(value, 200); + cpu.SetPC(200); - #region ProgramCounterTest - [Test] - public void SetPC_Test() - { - CPUEventArgs? cpuea = null; + Assert.That(TestHelper.GetPrivateMethod("FetchByte", cpu).Invoke(cpu,null), Is.EqualTo(0xff)); + Assert.That(cpu.PC, Is.EqualTo(201)); - cpu.OnProgramCounterUpdate += delegate (object? sender, CPUEventArgs e) - { - cpuea = e; - }; + } - cpu.SetPC(0x200); - Assert.That(cpu.PC, Is.EqualTo(0x200)); + #endregion fetch data - if (cpuea != null) - { - Assert.That(cpuea.PC, Is.EqualTo(0x200)); - } - else - { - Assert.Fail("Event not triggered"); - } - } + #region ProgramCounterTest + + [Test] + public void SetPC_Test() + { + CPUEventArgs? cpuea = null; - [Test] - public void IncrementPC_Test() + cpu.OnProgramCounterUpdate += delegate (object? sender, CPUEventArgs e) { - cpu.SetPC(0x200); + cpuea = e; + }; - CPUEventArgs? cpuea = null; + cpu.SetPC(0x200); + Assert.That(cpu.PC, Is.EqualTo(0x200)); - cpu.OnProgramCounterUpdate += delegate (object? sender, CPUEventArgs e) - { - cpuea = e; - }; + if (cpuea != null) + { + Assert.That(cpuea.PC, Is.EqualTo(0x200)); + } + else + { + Assert.Fail("Event not triggered"); + } + } - cpu.IncrementPC(); - Assert.That(cpu.PC, Is.EqualTo(0x201)); + [Test] + public void IncrementPC_Test() + { + cpu.SetPC(0x200); - if (cpuea != null) - { - Assert.That(cpuea.PC, Is.EqualTo(0x201)); - } - else - { - Assert.Fail("Event not triggered"); - } + CPUEventArgs? cpuea = null; - } + cpu.OnProgramCounterUpdate += delegate (object? sender, CPUEventArgs e) + { + cpuea = e; + }; + + cpu.IncrementPC(); + Assert.That(cpu.PC, Is.EqualTo(0x201)); - [Test] - public void DecrementPC_Test() + if (cpuea != null) + { + Assert.That(cpuea.PC, Is.EqualTo(0x201)); + } + else { - cpu.SetPC(0x201); + Assert.Fail("Event not triggered"); + } - CPUEventArgs? cpuea = null; + } - cpu.OnProgramCounterUpdate += delegate (object? sender, CPUEventArgs e) - { - cpuea = e; - }; + [Test] + public void DecrementPC_Test() + { + cpu.SetPC(0x201); - cpu.DecrementPC(); - Assert.That(cpu.PC, Is.EqualTo(0x200)); + CPUEventArgs? cpuea = null; - if (cpuea != null) - { - Assert.That(cpuea.PC, Is.EqualTo(0x200)); - } - else - { - Assert.Fail("Event not triggered"); - } + cpu.OnProgramCounterUpdate += delegate (object? sender, CPUEventArgs e) + { + cpuea = e; + }; + cpu.DecrementPC(); + Assert.That(cpu.PC, Is.EqualTo(0x200)); + + if (cpuea != null) + { + Assert.That(cpuea.PC, Is.EqualTo(0x200)); + } + else + { + Assert.Fail("Event not triggered"); } - #endregion + } - #region StackPointerTest - [Test] - public void SetSP_Test() - { - CPUEventArgs? cpuea = null; + #endregion ProgramCounterTest - cpu.OnStackPointerUpdate += delegate (object? sender, CPUEventArgs e) - { - cpuea = e; - }; + #region StackPointerTest + [Test] + public void SetSP_Test() + { + CPUEventArgs? cpuea = null; - GetPrivateMethod("SetSP", cpu).Invoke(cpu, new object[] { (ushort)0x01e0 }); - Assert.That(cpu.SP, Is.EqualTo(0x01e0)); - - if (cpuea != null) - { - Assert.That(cpuea.SP, Is.EqualTo(0x01e0)); - } - else - { - Assert.Fail("Event not triggered"); - } - - } + cpu.OnStackPointerUpdate += delegate (object? sender, CPUEventArgs e) + { + cpuea = e; + }; - [Test] - public void IncrementSP_Test() + TestHelper.GetPrivateMethod("SetSP", cpu).Invoke(cpu, new object[] { (ushort)0x01e0 }); + Assert.That(cpu.SP, Is.EqualTo(0x01e0)); + + if (cpuea != null) + { + Assert.That(cpuea.SP, Is.EqualTo(0x01e0)); + } + else { - ushort? expectedSPfromEventargs = null; + Assert.Fail("Event not triggered"); + } + + } - GetPrivateMethod("SetSP", cpu).Invoke(cpu, new object[] { (ushort)0x01fc }); + [Test] + public void IncrementSP_Test() + { + ushort? expectedSPfromEventargs = null; - cpu.OnStackPointerUpdate += delegate (object? sender, CPUEventArgs e) - { - expectedSPfromEventargs = e.SP; - }; - - GetPrivateMethod("IncrementSP", cpu).Invoke(cpu, null); - Assert.That(cpu.SP, Is.EqualTo(0x01fd)); + TestHelper.GetPrivateMethod("SetSP", cpu).Invoke(cpu, new object[] { (ushort)0x01fc }); - if (expectedSPfromEventargs != null) - { - Assert.That(expectedSPfromEventargs, Is.EqualTo(0x01fd)); - } - else - { - Assert.Fail("Event not triggered"); - } + cpu.OnStackPointerUpdate += delegate (object? sender, CPUEventArgs e) + { + expectedSPfromEventargs = e.SP; + }; + + TestHelper.GetPrivateMethod("IncrementSP", cpu).Invoke(cpu, null); + Assert.That(cpu.SP, Is.EqualTo(0x01fd)); + if (expectedSPfromEventargs != null) + { + Assert.That(expectedSPfromEventargs, Is.EqualTo(0x01fd)); } - - [Test] - public void DecrementSP_Test() + else { - ushort? expectedSPfromEventargs = null; + Assert.Fail("Event not triggered"); + } - GetPrivateMethod("SetSP", cpu).Invoke(cpu, new object[] { (ushort)0x01ff }); + } - cpu.OnStackPointerUpdate += delegate (object? sender, CPUEventArgs e) - { - expectedSPfromEventargs = e.SP; - }; + [Test] + public void DecrementSP_Test() + { + ushort? expectedSPfromEventargs = null; - GetPrivateMethod("DecrementSP", cpu).Invoke(cpu, null); - Assert.That(cpu.SP, Is.EqualTo(0x01fe)); + TestHelper.GetPrivateMethod("SetSP", cpu).Invoke(cpu, new object[] { (ushort)0x01ff }); - if (expectedSPfromEventargs != null) - { - Assert.That(expectedSPfromEventargs, Is.EqualTo(0x01fe)); - } - else - { - Assert.Fail("Event not triggered"); - } + cpu.OnStackPointerUpdate += delegate (object? sender, CPUEventArgs e) + { + expectedSPfromEventargs = e.SP; + }; + + TestHelper.GetPrivateMethod("DecrementSP", cpu).Invoke(cpu, null); + + Assert.That(cpu.SP, Is.EqualTo(0x01fe)); + if (expectedSPfromEventargs != null) + { + Assert.That(expectedSPfromEventargs, Is.EqualTo(0x01fe)); + } + else + { + Assert.Fail("Event not triggered"); } - #endregion + } - #region helper - private MethodInfo GetPrivateMethod(string methodName,object objectUnderTest) - { - if (string.IsNullOrWhiteSpace(methodName)) - Assert.Fail("methodName cannot be null or whitespace"); + #endregion StackPointerTest - var method = objectUnderTest.GetType() - .GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance); - if (method == null) - Assert.Fail(string.Format("{0} method not found", methodName)); +} - return method; - } +public class CPU_Command_Tests +{ + private CPU cpu; - #endregion + [SetUp] + public void SetUp() + { + cpu = new CPU(); } - public class CPU_Command_Tests + #region LDA + // Test Load Accumulator immidiate A9 + [TestCase(0xff, ExpectedResult = new object[] { false, true, 0xff, 3 })] + [TestCase(0x00, ExpectedResult = new object[] { true, false, 0x00, 3 })] + public object[] Cmd_A9_Test(byte b) { - private CPU cpu; + object[] result = new object[4]; - [SetUp] - public void SetUp() - { - cpu = new CPU(); - } - #region LDA - // Test Load Accumulator immidiate A9 - [TestCase(0xff, ExpectedResult = new object[] { false, true, 0xff })] - [TestCase(0x00, ExpectedResult = new object[] { true, false, 0x00 })] - public object[] Cmd_A9_Test(byte b) - { - object[] result = new object[3]; + cpu.WriteByteToMemory(b, 0x200); + cpu.SetPC(0x200); + + TestHelper.GetPrivateMethod("CallInstruction", cpu).Invoke(cpu, new object[] { cpu.GetType() , (byte)0xA9 }); - cpu.WriteByteToMemory(b, 0x200); - cpu.SetPC(0x200); - cpu.Cmd_A9(); + result[0] = cpu.flags["Z"]; + result[1] = cpu.flags["N"]; + result[2] = cpu.A; + result[3] = cpu.CpuCycle; - result[0] = cpu.flags["Z"]; - result[1] = cpu.flags["N"]; - result[2] = cpu.A; + return result; - return result; + } + + // Test Load Accumulator zeropage A5 + [TestCase(0xff, ExpectedResult = new object[] { false, true, 0xff })] + [TestCase(0x00, ExpectedResult = new object[] { true, false, 0x00 })] + public object[] Cmd_A5_Test(byte b) + { + object[] result = new object[3]; - } - - // Test Load Accumulator zeropage A5 - [TestCase(0xff, ExpectedResult = new object[] { false, true, 0xff })] - [TestCase(0x00, ExpectedResult = new object[] { true, false, 0x00 })] - public object[] Cmd_A5_Test(byte b) - { - object[] result = new object[3]; + cpu.WriteByteToMemory(b, 0x01); // set byte on zeropage adr. 0x01 + cpu.WriteByteToMemory(0x01, 0x200); + cpu.SetPC(0x200); + cpu.Cmd_A5(); - cpu.WriteByteToMemory(b, 0x01); // set byte on zeropage adr. 0x01 - cpu.WriteByteToMemory(0x01, 0x200); - cpu.SetPC(0x200); - cpu.Cmd_A5(); + result[0] = cpu.flags["Z"]; + result[1] = cpu.flags["N"]; + result[2] = cpu.A; - result[0] = cpu.flags["Z"]; - result[1] = cpu.flags["N"]; - result[2] = cpu.A; + return result; - return result; + } - } + // Load Accumulator zeropage X B5 + [TestCase(0xff, ExpectedResult = new object[] { false, true, 0xff })] + [TestCase(0x00, ExpectedResult = new object[] { true, false, 0x00 })] + public object[] Cmd_B5_Test(byte b) + { + object[] result = new object[3]; + cpu.SetRegister("X",0x06); + cpu.WriteByteToMemory(b, 0x07); // set byte on zeropage adr. 0x01 + cpu.WriteByteToMemory(0x01, 0x200); + cpu.SetPC(0x200); + cpu.Cmd_B5(); - // Load Accumulator zeropage X B5 - [TestCase(0xff, ExpectedResult = new object[] { false, true, 0xff })] - [TestCase(0x00, ExpectedResult = new object[] { true, false, 0x00 })] - public object[] Cmd_B5_Test(byte b) - { - object[] result = new object[3]; - cpu.SetRegister("X",0x06); - cpu.WriteByteToMemory(b, 0x07); // set byte on zeropage adr. 0x01 - cpu.WriteByteToMemory(0x01, 0x200); - cpu.SetPC(0x200); - cpu.Cmd_B5(); + result[0] = cpu.flags["Z"]; + result[1] = cpu.flags["N"]; + result[2] = cpu.A; - result[0] = cpu.flags["Z"]; - result[1] = cpu.flags["N"]; - result[2] = cpu.A; + return result; - return result; + } - } + #endregion - #endregion + #region LDX - #region LDX + // Test Load X immidiate A2 + [TestCase(0xff, ExpectedResult = new object[] { false, true, 0xff })] + [TestCase(0x00, ExpectedResult = new object[] { true, false, 0x00 })] + public object[] Cmd_A2_Test(byte b) + { + object[] result = new object[3]; - // Test Load X immidiate A2 - [TestCase(0xff, ExpectedResult = new object[] { false, true, 0xff })] - [TestCase(0x00, ExpectedResult = new object[] { true, false, 0x00 })] - public object[] Cmd_A2_Test(byte b) - { - object[] result = new object[3]; + cpu.WriteByteToMemory(b, 0x200); + cpu.SetPC(0x200); + cpu.Cmd_A2(); - cpu.WriteByteToMemory(b, 0x200); - cpu.SetPC(0x200); - cpu.Cmd_A2(); + result[0] = cpu.flags["Z"]; + result[1] = cpu.flags["N"]; + result[2] = cpu.X; - result[0] = cpu.flags["Z"]; - result[1] = cpu.flags["N"]; - result[2] = cpu.X; + return result; - return result; + } - } + // Test Load X zeropage A6 + [TestCase(0xff, ExpectedResult = new object[] { false, true, 0xff })] + [TestCase(0x00, ExpectedResult = new object[] { true, false, 0x00 })] + public object[] Cmd_A6_Test(byte b) + { + object[] result = new object[3]; - // Test Load X zeropage A6 - [TestCase(0xff, ExpectedResult = new object[] { false, true, 0xff })] - [TestCase(0x00, ExpectedResult = new object[] { true, false, 0x00 })] - public object[] Cmd_A6_Test(byte b) - { - object[] result = new object[3]; + cpu.WriteByteToMemory(b, 0x01); // set byte on zeropage adr. 0x01 + cpu.WriteByteToMemory(0x01, 0x200); + cpu.SetPC(0x200); + cpu.Cmd_A6(); - cpu.WriteByteToMemory(b, 0x01); // set byte on zeropage adr. 0x01 - cpu.WriteByteToMemory(0x01, 0x200); - cpu.SetPC(0x200); - cpu.Cmd_A6(); + result[0] = cpu.flags["Z"]; + result[1] = cpu.flags["N"]; + result[2] = cpu.X; - result[0] = cpu.flags["Z"]; - result[1] = cpu.flags["N"]; - result[2] = cpu.X; + return result; - return result; + } - } + // Load X zeropage Y B6 + [TestCase(0xff, ExpectedResult = new object[] { false, true, 0xff })] + [TestCase(0x00, ExpectedResult = new object[] { true, false, 0x00 })] + public object[] Cmd_B6_Test(byte b) + { + object[] result = new object[3]; + cpu.SetRegister("Y", 0x06); + cpu.WriteByteToMemory(b, 0x07); // set byte on zeropage adr. 0x01 + cpu.WriteByteToMemory(0x01, 0x200); + cpu.SetPC(0x200); + cpu.Cmd_B6(); - // Load X zeropage Y B6 - [TestCase(0xff, ExpectedResult = new object[] { false, true, 0xff })] - [TestCase(0x00, ExpectedResult = new object[] { true, false, 0x00 })] - public object[] Cmd_B6_Test(byte b) - { - object[] result = new object[3]; - cpu.SetRegister("Y", 0x06); - cpu.WriteByteToMemory(b, 0x07); // set byte on zeropage adr. 0x01 - cpu.WriteByteToMemory(0x01, 0x200); - cpu.SetPC(0x200); - cpu.Cmd_B6(); + result[0] = cpu.flags["Z"]; + result[1] = cpu.flags["N"]; + result[2] = cpu.X; - result[0] = cpu.flags["Z"]; - result[1] = cpu.flags["N"]; - result[2] = cpu.X; + return result; - return result; + } - } + #endregion - #endregion + #region LDY - #region LDY + // Test Load Y immidiate A0 + [TestCase(0xff, ExpectedResult = new object[] { false, true, 0xff })] + [TestCase(0x00, ExpectedResult = new object[] { true, false, 0x00 })] + public object[] Cmd_A0_Test(byte b) + { + object[] result = new object[3]; - // Test Load Y immidiate A0 - [TestCase(0xff, ExpectedResult = new object[] { false, true, 0xff })] - [TestCase(0x00, ExpectedResult = new object[] { true, false, 0x00 })] - public object[] Cmd_A0_Test(byte b) - { - object[] result = new object[3]; + cpu.WriteByteToMemory(b, 0x200); + cpu.SetPC(0x200); + cpu.Cmd_A0(); - cpu.WriteByteToMemory(b, 0x200); - cpu.SetPC(0x200); - cpu.Cmd_A0(); + result[0] = cpu.flags["Z"]; + result[1] = cpu.flags["N"]; + result[2] = cpu.Y; - result[0] = cpu.flags["Z"]; - result[1] = cpu.flags["N"]; - result[2] = cpu.Y; + return result; - return result; + } - } + // Test Load Y zeropage A4 + [TestCase(0xff, ExpectedResult = new object[] { false, true, 0xff })] + [TestCase(0x00, ExpectedResult = new object[] { true, false, 0x00 })] + public object[] Cmd_A4_Test(byte b) + { + object[] result = new object[3]; - // Test Load Y zeropage A4 - [TestCase(0xff, ExpectedResult = new object[] { false, true, 0xff })] - [TestCase(0x00, ExpectedResult = new object[] { true, false, 0x00 })] - public object[] Cmd_A4_Test(byte b) - { - object[] result = new object[3]; + cpu.WriteByteToMemory(b, 0x01); // set byte on zeropage adr. 0x01 + cpu.WriteByteToMemory(0x01, 0x200); + cpu.SetPC(0x200); + cpu.Cmd_A4(); - cpu.WriteByteToMemory(b, 0x01); // set byte on zeropage adr. 0x01 - cpu.WriteByteToMemory(0x01, 0x200); - cpu.SetPC(0x200); - cpu.Cmd_A4(); + result[0] = cpu.flags["Z"]; + result[1] = cpu.flags["N"]; + result[2] = cpu.Y; - result[0] = cpu.flags["Z"]; - result[1] = cpu.flags["N"]; - result[2] = cpu.Y; + return result; - return result; + } - } + // Load Y zeropage X B4 + [TestCase(0xff, ExpectedResult = new object[] { false, true, 0xff })] + [TestCase(0x00, ExpectedResult = new object[] { true, false, 0x00 })] + public object[] Cmd_B4_Test(byte b) + { + object[] result = new object[3]; + cpu.SetRegister("X", 0x06); + cpu.WriteByteToMemory(b, 0x07); // set byte on zeropage adr. 0x01 + cpu.WriteByteToMemory(0x01, 0x200); + cpu.SetPC(0x200); + cpu.Cmd_B4(); - // Load Y zeropage X B4 - [TestCase(0xff, ExpectedResult = new object[] { false, true, 0xff })] - [TestCase(0x00, ExpectedResult = new object[] { true, false, 0x00 })] - public object[] Cmd_B4_Test(byte b) - { - object[] result = new object[3]; - cpu.SetRegister("X", 0x06); - cpu.WriteByteToMemory(b, 0x07); // set byte on zeropage adr. 0x01 - cpu.WriteByteToMemory(0x01, 0x200); - cpu.SetPC(0x200); - cpu.Cmd_B4(); + result[0] = cpu.flags["Z"]; + result[1] = cpu.flags["N"]; + result[2] = cpu.Y; - result[0] = cpu.flags["Z"]; - result[1] = cpu.flags["N"]; - result[2] = cpu.Y; + return result; - return result; + } - } + #endregion - #endregion + #region push pull - #region push pull + // Test Push Accumulator on Stack + [Test] + public void Cmd_48_Test() + { + CPUEventArgs? cpuea = null; - // Test Push Accumulator on Stack - [Test] - public void Cmd_48_Test() + cpu.OnStackPointerUpdate += delegate (object? sender, CPUEventArgs e) { - CPUEventArgs? cpuea = null; + cpuea = e; + }; - cpu.OnStackPointerUpdate += delegate (object? sender, CPUEventArgs e) - { - cpuea = e; - }; + cpu.Reset(); + cpu.SetRegister("A", 0x42); + cpu.Cmd_48(); + + Assert.That(cpu.SP, Is.EqualTo(0x01fe)); + + if (cpuea != null) + { + Assert.That(cpuea.SP, Is.EqualTo(0x01fe)); + } + else + { + Assert.Fail("Event not triggered"); + } - cpu.Reset(); - cpu.SetRegister("A", 0x42); - cpu.Cmd_48(); + Assert.That(cpu.Memory[0x01ff], Is.EqualTo(0x42)); - Assert.That(cpu.SP, Is.EqualTo(0x01fe)); + } - if (cpuea != null) - { - Assert.That(cpuea.SP, Is.EqualTo(0x01fe)); - } - else - { - Assert.Fail("Event not triggered"); - } + #endregion +} - Assert.That(cpu.Memory[0x01ff], Is.EqualTo(0x42)); +static class TestHelper +{ + public static MethodInfo GetPrivateMethod(string methodName, object objectUnderTest) + { + if (string.IsNullOrWhiteSpace(methodName)) + Assert.Fail("methodName cannot be null or whitespace"); - } + MethodInfo? method = objectUnderTest.GetType() + .GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance); + + if (method == null) + Assert.Fail(string.Format("{0} method not found", methodName)); - #endregion + return method; } } \ No newline at end of file