diff --git a/MC6809/MC6809.UnitTest/SchwotzerArithmeticTests.cs b/MC6809/MC6809.UnitTest/SchwotzerArithmeticTests.cs
new file mode 100644
index 0000000..92ade1e
--- /dev/null
+++ b/MC6809/MC6809.UnitTest/SchwotzerArithmeticTests.cs
@@ -0,0 +1,267 @@
+//
+// Copyright (c) Adrian Conlon. All rights reserved.
+//
+
+// Test data from W. Schwotzer's MC6809 CPU Emulation Validation suite,
+// verified on an SGS Thomson EF6809P processor.
+
+namespace MC6809.UnitTest
+{
+ [TestClass]
+ public class SchwotzerArithmeticTests
+ {
+ private readonly Board board = new();
+ private readonly MC6809 cpu;
+
+ public SchwotzerArithmeticTests() => this.cpu = this.board.CPU;
+
+ [TestInitialize]
+ public void Initialise() { this.board.RaisePOWER(); this.cpu.Step(); }
+
+ [TestCleanup]
+ public void Cleanup() => this.board.LowerPOWER();
+
+ // ADD (ADDB immediate): checks HNZVC ($2F)
+ [DataTestMethod]
+ [DataRow(0x00, 0x00, 0x00, 0x04)]
+ [DataRow(0x00, 0x01, 0x01, 0x00)]
+ [DataRow(0x40, 0x40, 0x80, 0x0A)]
+ [DataRow(0x00, 0x80, 0x80, 0x08)]
+ [DataRow(0x80, 0x80, 0x00, 0x07)]
+ [DataRow(0x40, 0x80, 0xC0, 0x08)]
+ [DataRow(0x7F, 0x7F, 0xFE, 0x2A)]
+ [DataRow(0x7F, 0x80, 0xFF, 0x08)]
+ [DataRow(0x80, 0x7F, 0xFF, 0x08)]
+ [DataRow(0x7F, 0x01, 0x80, 0x2A)]
+ [DataRow(0x80, 0x01, 0x81, 0x08)]
+ [DataRow(0x40, 0xC0, 0x00, 0x05)]
+ [DataRow(0x40, 0xC1, 0x01, 0x01)]
+ [DataRow(0xFF, 0xFF, 0xFE, 0x29)]
+ [DataRow(0x81, 0x80, 0x01, 0x03)]
+ public void TestADDB(int operand, int data, int expected, int flags)
+ {
+ this.board.Poke(0, 0xCB);
+ this.board.Poke(1, (byte)data);
+ this.cpu.CC = 0;
+ this.cpu.B = (byte)operand;
+ this.cpu.Step();
+ Assert.AreEqual((byte)expected, this.cpu.B);
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x2F));
+ }
+
+ // ADDD (immediate): checks NZVC ($0F)
+ [DataTestMethod]
+ [DataRow(0x0000, 0x0000, 0x0000, 0x04)]
+ [DataRow(0x0000, 0x0001, 0x0001, 0x00)]
+ [DataRow(0x4000, 0x4000, 0x8000, 0x0A)]
+ [DataRow(0x0000, 0x8000, 0x8000, 0x08)]
+ [DataRow(0x8000, 0x8000, 0x0000, 0x07)]
+ [DataRow(0x4000, 0x8000, 0xC000, 0x08)]
+ [DataRow(0x7FFF, 0x7FFF, 0xFFFE, 0x0A)]
+ [DataRow(0x7FFF, 0x8000, 0xFFFF, 0x08)]
+ [DataRow(0x7FFF, 0x0001, 0x8000, 0x0A)]
+ [DataRow(0x8000, 0x0001, 0x8001, 0x08)]
+ [DataRow(0x4000, 0xC000, 0x0000, 0x05)]
+ [DataRow(0x4000, 0xC001, 0x0001, 0x01)]
+ [DataRow(0xFFFF, 0xFFFF, 0xFFFE, 0x09)]
+ [DataRow(0x8001, 0x8000, 0x0001, 0x03)]
+ public void TestADDD(int operand, int data, int expected, int flags)
+ {
+ this.board.Poke(0, 0xC3);
+ this.board.Poke(1, (byte)(data >> 8));
+ this.board.Poke(2, (byte)(data & 0xFF));
+ this.cpu.CC = 0;
+ this.cpu.D.Word = (ushort)operand;
+ this.cpu.Step();
+ Assert.AreEqual((ushort)expected, this.cpu.D.Word);
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x0F));
+ }
+
+ // ADC (ADCA immediate): carry-in from carryIn; checks HNZVC ($2F)
+ [DataTestMethod]
+ [DataRow(0x00, 0, 0x00, 0x00, 0x04)]
+ [DataRow(0x00, 0, 0x01, 0x01, 0x00)]
+ [DataRow(0x40, 0, 0x40, 0x80, 0x0A)]
+ [DataRow(0x00, 0, 0x80, 0x80, 0x08)]
+ [DataRow(0x80, 0, 0x80, 0x00, 0x07)]
+ [DataRow(0x40, 0, 0x80, 0xC0, 0x08)]
+ [DataRow(0x7F, 0, 0x7F, 0xFE, 0x2A)]
+ [DataRow(0x7F, 0, 0x80, 0xFF, 0x08)]
+ [DataRow(0x80, 0, 0x7F, 0xFF, 0x08)]
+ [DataRow(0x7F, 0, 0x01, 0x80, 0x2A)]
+ [DataRow(0x80, 0, 0x01, 0x81, 0x08)]
+ [DataRow(0x40, 0, 0xC0, 0x00, 0x05)]
+ [DataRow(0x40, 0, 0xC1, 0x01, 0x01)]
+ [DataRow(0xFF, 0, 0xFF, 0xFE, 0x29)]
+ [DataRow(0x81, 0, 0x80, 0x01, 0x03)]
+ [DataRow(0x00, 1, 0x00, 0x01, 0x00)]
+ [DataRow(0x00, 1, 0x01, 0x02, 0x00)]
+ [DataRow(0x40, 1, 0x40, 0x81, 0x0A)]
+ [DataRow(0x00, 1, 0x80, 0x81, 0x08)]
+ [DataRow(0x80, 1, 0x80, 0x01, 0x03)]
+ [DataRow(0x40, 1, 0x80, 0xC1, 0x08)]
+ [DataRow(0x7F, 1, 0x7F, 0xFF, 0x2A)]
+ [DataRow(0x7F, 1, 0x80, 0x00, 0x25)]
+ [DataRow(0x80, 1, 0x7F, 0x00, 0x25)]
+ [DataRow(0x7F, 1, 0x01, 0x81, 0x2A)]
+ [DataRow(0x80, 1, 0x01, 0x82, 0x08)]
+ [DataRow(0x40, 1, 0xC0, 0x01, 0x01)]
+ [DataRow(0x40, 1, 0xC1, 0x02, 0x01)]
+ [DataRow(0xFF, 1, 0xFF, 0xFF, 0x29)]
+ [DataRow(0x81, 1, 0x80, 0x02, 0x03)]
+ public void TestADCA(int operand, int carryIn, int data, int expected, int flags)
+ {
+ this.board.Poke(0, 0x89);
+ this.board.Poke(1, (byte)data);
+ this.cpu.CC = (byte)carryIn;
+ this.cpu.A = (byte)operand;
+ this.cpu.Step();
+ Assert.AreEqual((byte)expected, this.cpu.A);
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x2F));
+ }
+
+ // SUB (SUBB immediate): checks NZVC ($0F)
+ [DataTestMethod]
+ [DataRow(0x00, 0x00, 0x00, 0x04)]
+ [DataRow(0x00, 0x01, 0xFF, 0x09)]
+ [DataRow(0x00, 0x7F, 0x81, 0x09)]
+ [DataRow(0x00, 0x80, 0x80, 0x0B)]
+ [DataRow(0x00, 0xFF, 0x01, 0x01)]
+ [DataRow(0x01, 0x00, 0x01, 0x00)]
+ [DataRow(0x01, 0x7F, 0x82, 0x09)]
+ [DataRow(0x40, 0x41, 0xFF, 0x09)]
+ [DataRow(0x40, 0x80, 0xC0, 0x0B)]
+ [DataRow(0x40, 0xC0, 0x80, 0x0B)]
+ [DataRow(0x40, 0xC1, 0x7F, 0x01)]
+ [DataRow(0x7F, 0x00, 0x7F, 0x00)]
+ [DataRow(0x7F, 0x01, 0x7E, 0x00)]
+ [DataRow(0x7F, 0x7E, 0x01, 0x00)]
+ [DataRow(0x7F, 0x7F, 0x00, 0x04)]
+ [DataRow(0x7F, 0x80, 0xFF, 0x0B)]
+ [DataRow(0x7F, 0xFF, 0x80, 0x0B)]
+ [DataRow(0x80, 0x01, 0x7F, 0x02)]
+ [DataRow(0x80, 0x40, 0x40, 0x02)]
+ [DataRow(0x80, 0x7F, 0x01, 0x02)]
+ [DataRow(0x80, 0x80, 0x00, 0x04)]
+ [DataRow(0x80, 0x81, 0xFF, 0x09)]
+ [DataRow(0x81, 0x80, 0x01, 0x00)]
+ [DataRow(0xFF, 0xFF, 0x00, 0x04)]
+ [DataRow(0xFF, 0xFE, 0x01, 0x00)]
+ [DataRow(0xFF, 0x00, 0xFF, 0x08)]
+ public void TestSUBB(int operand, int data, int expected, int flags)
+ {
+ this.board.Poke(0, 0xC0);
+ this.board.Poke(1, (byte)data);
+ this.cpu.CC = 0;
+ this.cpu.B = (byte)operand;
+ this.cpu.Step();
+ Assert.AreEqual((byte)expected, this.cpu.B);
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x0F));
+ }
+
+ // SUBD (immediate): checks NZVC ($0F)
+ [DataTestMethod]
+ [DataRow(0x0000, 0x0000, 0x0000, 0x04)]
+ [DataRow(0x0000, 0x0001, 0xFFFF, 0x09)]
+ [DataRow(0x0000, 0x7FFF, 0x8001, 0x09)]
+ [DataRow(0x0000, 0x8000, 0x8000, 0x0B)]
+ [DataRow(0x0000, 0xFFFF, 0x0001, 0x01)]
+ [DataRow(0x0001, 0x0000, 0x0001, 0x00)]
+ [DataRow(0x0001, 0x7FFF, 0x8002, 0x09)]
+ [DataRow(0x4000, 0x4001, 0xFFFF, 0x09)]
+ [DataRow(0x4000, 0x8000, 0xC000, 0x0B)]
+ [DataRow(0x4000, 0xC000, 0x8000, 0x0B)]
+ [DataRow(0x4000, 0xC001, 0x7FFF, 0x01)]
+ [DataRow(0x7FFF, 0x0000, 0x7FFF, 0x00)]
+ [DataRow(0x7FFF, 0x0001, 0x7FFE, 0x00)]
+ [DataRow(0x7FFF, 0x7FFE, 0x0001, 0x00)]
+ [DataRow(0x7FFF, 0x7FFF, 0x0000, 0x04)]
+ [DataRow(0x7FFF, 0x8000, 0xFFFF, 0x0B)]
+ [DataRow(0x7FFF, 0xFFFF, 0x8000, 0x0B)]
+ [DataRow(0x8000, 0x0001, 0x7FFF, 0x02)]
+ [DataRow(0x8000, 0x4000, 0x4000, 0x02)]
+ [DataRow(0x8000, 0x7FFF, 0x0001, 0x02)]
+ [DataRow(0x8000, 0x8000, 0x0000, 0x04)]
+ [DataRow(0x8000, 0x8001, 0xFFFF, 0x09)]
+ [DataRow(0x8001, 0x8000, 0x0001, 0x00)]
+ [DataRow(0xFFFF, 0xFFFF, 0x0000, 0x04)]
+ [DataRow(0xFFFF, 0xFFFE, 0x0001, 0x00)]
+ [DataRow(0xFFFF, 0x0000, 0xFFFF, 0x08)]
+ public void TestSUBD(int operand, int data, int expected, int flags)
+ {
+ this.board.Poke(0, 0x83);
+ this.board.Poke(1, (byte)(data >> 8));
+ this.board.Poke(2, (byte)(data & 0xFF));
+ this.cpu.CC = 0;
+ this.cpu.D.Word = (ushort)operand;
+ this.cpu.Step();
+ Assert.AreEqual((ushort)expected, this.cpu.D.Word);
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x0F));
+ }
+
+ // SBC (SBCA immediate): carry-in from carryIn; checks NZVC ($0F)
+ [DataTestMethod]
+ [DataRow(0x00, 0, 0x00, 0x00, 0x04)]
+ [DataRow(0x00, 0, 0x01, 0xFF, 0x09)]
+ [DataRow(0x00, 0, 0x7F, 0x81, 0x09)]
+ [DataRow(0x00, 0, 0x80, 0x80, 0x0B)]
+ [DataRow(0x00, 0, 0xFF, 0x01, 0x01)]
+ [DataRow(0x01, 0, 0x00, 0x01, 0x00)]
+ [DataRow(0x01, 0, 0x7F, 0x82, 0x09)]
+ [DataRow(0x40, 0, 0x41, 0xFF, 0x09)]
+ [DataRow(0x40, 0, 0x80, 0xC0, 0x0B)]
+ [DataRow(0x40, 0, 0xC0, 0x80, 0x0B)]
+ [DataRow(0x40, 0, 0xC1, 0x7F, 0x01)]
+ [DataRow(0x7F, 0, 0x00, 0x7F, 0x00)]
+ [DataRow(0x7F, 0, 0x01, 0x7E, 0x00)]
+ [DataRow(0x7F, 0, 0x7E, 0x01, 0x00)]
+ [DataRow(0x7F, 0, 0x7F, 0x00, 0x04)]
+ [DataRow(0x7F, 0, 0x80, 0xFF, 0x0B)]
+ [DataRow(0x7F, 0, 0xFF, 0x80, 0x0B)]
+ [DataRow(0x80, 0, 0x01, 0x7F, 0x02)]
+ [DataRow(0x80, 0, 0x40, 0x40, 0x02)]
+ [DataRow(0x80, 0, 0x7F, 0x01, 0x02)]
+ [DataRow(0x80, 0, 0x80, 0x00, 0x04)]
+ [DataRow(0x80, 0, 0x81, 0xFF, 0x09)]
+ [DataRow(0x81, 0, 0x80, 0x01, 0x00)]
+ [DataRow(0xFF, 0, 0xFF, 0x00, 0x04)]
+ [DataRow(0xFF, 0, 0xFE, 0x01, 0x00)]
+ [DataRow(0xFF, 0, 0x00, 0xFF, 0x08)]
+ [DataRow(0x00, 1, 0x00, 0xFF, 0x09)]
+ [DataRow(0x00, 1, 0x01, 0xFE, 0x09)]
+ [DataRow(0x00, 1, 0x7F, 0x80, 0x09)]
+ [DataRow(0x00, 1, 0x80, 0x7F, 0x01)]
+ [DataRow(0x00, 1, 0xFF, 0x00, 0x05)]
+ [DataRow(0x01, 1, 0x00, 0x00, 0x04)]
+ [DataRow(0x01, 1, 0x7F, 0x81, 0x09)]
+ [DataRow(0x40, 1, 0x41, 0xFE, 0x09)]
+ [DataRow(0x40, 1, 0x80, 0xBF, 0x0B)]
+ [DataRow(0x40, 1, 0xC0, 0x7F, 0x01)]
+ [DataRow(0x40, 1, 0xC1, 0x7E, 0x01)]
+ [DataRow(0x7F, 1, 0x00, 0x7E, 0x00)]
+ [DataRow(0x7F, 1, 0x01, 0x7D, 0x00)]
+ [DataRow(0x7F, 1, 0x7E, 0x00, 0x04)]
+ [DataRow(0x7F, 1, 0x7F, 0xFF, 0x09)]
+ [DataRow(0x7F, 1, 0x80, 0xFE, 0x0B)]
+ [DataRow(0x7F, 1, 0xFF, 0x7F, 0x01)]
+ [DataRow(0x80, 1, 0x01, 0x7E, 0x02)]
+ [DataRow(0x80, 1, 0x40, 0x3F, 0x02)]
+ [DataRow(0x80, 1, 0x7F, 0x00, 0x06)]
+ [DataRow(0x80, 1, 0x80, 0xFF, 0x09)]
+ [DataRow(0x80, 1, 0x81, 0xFE, 0x09)]
+ [DataRow(0x81, 1, 0x80, 0x00, 0x04)]
+ [DataRow(0xFF, 1, 0xFF, 0xFF, 0x09)]
+ [DataRow(0xFF, 1, 0xFE, 0x00, 0x04)]
+ [DataRow(0xFF, 1, 0x00, 0xFE, 0x08)]
+ public void TestSBCA(int operand, int carryIn, int data, int expected, int flags)
+ {
+ this.board.Poke(0, 0x82);
+ this.board.Poke(1, (byte)data);
+ this.cpu.CC = (byte)carryIn;
+ this.cpu.A = (byte)operand;
+ this.cpu.Step();
+ Assert.AreEqual((byte)expected, this.cpu.A);
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x0F));
+ }
+ }
+}
diff --git a/MC6809/MC6809.UnitTest/SchwotzerCompareLoadTests.cs b/MC6809/MC6809.UnitTest/SchwotzerCompareLoadTests.cs
new file mode 100644
index 0000000..ccabe07
--- /dev/null
+++ b/MC6809/MC6809.UnitTest/SchwotzerCompareLoadTests.cs
@@ -0,0 +1,285 @@
+//
+// Copyright (c) Adrian Conlon. All rights reserved.
+//
+
+// Test data from W. Schwotzer's MC6809 CPU Emulation Validation suite,
+// verified on an SGS Thomson EF6809P processor.
+
+namespace MC6809.UnitTest
+{
+ [TestClass]
+ public class SchwotzerCompareLoadTests
+ {
+ private readonly Board board = new();
+ private readonly MC6809 cpu;
+
+ public SchwotzerCompareLoadTests() => this.cpu = this.board.CPU;
+
+ [TestInitialize]
+ public void Initialise() { this.board.RaisePOWER(); this.cpu.Step(); }
+
+ [TestCleanup]
+ public void Cleanup() => this.board.LowerPOWER();
+
+ // CMPB immediate ($C1): same data as SUBB but B is not modified; checks NZVC ($0F)
+ [DataTestMethod]
+ [DataRow(0x00, 0x00, 0x04)]
+ [DataRow(0x00, 0x01, 0x09)]
+ [DataRow(0x00, 0x7F, 0x09)]
+ [DataRow(0x00, 0x80, 0x0B)]
+ [DataRow(0x00, 0xFF, 0x01)]
+ [DataRow(0x01, 0x00, 0x00)]
+ [DataRow(0x01, 0x7F, 0x09)]
+ [DataRow(0x40, 0x41, 0x09)]
+ [DataRow(0x40, 0x80, 0x0B)]
+ [DataRow(0x40, 0xC0, 0x0B)]
+ [DataRow(0x40, 0xC1, 0x01)]
+ [DataRow(0x7F, 0x00, 0x00)]
+ [DataRow(0x7F, 0x01, 0x00)]
+ [DataRow(0x7F, 0x7E, 0x00)]
+ [DataRow(0x7F, 0x7F, 0x04)]
+ [DataRow(0x7F, 0x80, 0x0B)]
+ [DataRow(0x7F, 0xFF, 0x0B)]
+ [DataRow(0x80, 0x01, 0x02)]
+ [DataRow(0x80, 0x40, 0x02)]
+ [DataRow(0x80, 0x7F, 0x02)]
+ [DataRow(0x80, 0x80, 0x04)]
+ [DataRow(0x80, 0x81, 0x09)]
+ [DataRow(0x81, 0x80, 0x00)]
+ [DataRow(0xFF, 0xFF, 0x04)]
+ [DataRow(0xFF, 0xFE, 0x00)]
+ [DataRow(0xFF, 0x00, 0x08)]
+ public void TestCMPB(int operand, int data, int flags)
+ {
+ this.board.Poke(0, 0xC1);
+ this.board.Poke(1, (byte)data);
+ this.cpu.CC = 0;
+ this.cpu.B = (byte)operand;
+ this.cpu.Step();
+ Assert.AreEqual((byte)operand, this.cpu.B); // B must not be modified
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x0F));
+ }
+
+ // CMPD immediate ($10,$83): same data as SUBD but D is not modified; checks NZVC ($0F)
+ [DataTestMethod]
+ [DataRow(0x0000, 0x0000, 0x04)]
+ [DataRow(0x0000, 0x0001, 0x09)]
+ [DataRow(0x0000, 0x7FFF, 0x09)]
+ [DataRow(0x0000, 0x8000, 0x0B)]
+ [DataRow(0x0000, 0xFFFF, 0x01)]
+ [DataRow(0x0001, 0x0000, 0x00)]
+ [DataRow(0x0001, 0x7FFF, 0x09)]
+ [DataRow(0x4000, 0x4001, 0x09)]
+ [DataRow(0x4000, 0x8000, 0x0B)]
+ [DataRow(0x4000, 0xC000, 0x0B)]
+ [DataRow(0x4000, 0xC001, 0x01)]
+ [DataRow(0x7FFF, 0x0000, 0x00)]
+ [DataRow(0x7FFF, 0x0001, 0x00)]
+ [DataRow(0x7FFF, 0x7FFE, 0x00)]
+ [DataRow(0x7FFF, 0x7FFF, 0x04)]
+ [DataRow(0x7FFF, 0x8000, 0x0B)]
+ [DataRow(0x7FFF, 0xFFFF, 0x0B)]
+ [DataRow(0x8000, 0x0001, 0x02)]
+ [DataRow(0x8000, 0x4000, 0x02)]
+ [DataRow(0x8000, 0x7FFF, 0x02)]
+ [DataRow(0x8000, 0x8000, 0x04)]
+ [DataRow(0x8000, 0x8001, 0x09)]
+ [DataRow(0x8001, 0x8000, 0x00)]
+ [DataRow(0xFFFF, 0xFFFF, 0x04)]
+ [DataRow(0xFFFF, 0xFFFE, 0x00)]
+ [DataRow(0xFFFF, 0x0000, 0x08)]
+ public void TestCMPD(int operand, int data, int flags)
+ {
+ this.board.Poke(0, 0x10);
+ this.board.Poke(1, 0x83);
+ this.board.Poke(2, (byte)(data >> 8));
+ this.board.Poke(3, (byte)(data & 0xFF));
+ this.cpu.CC = 0;
+ this.cpu.D.Word = (ushort)operand;
+ this.cpu.Step();
+ Assert.AreEqual((ushort)operand, this.cpu.D.Word); // D must not be modified
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x0F));
+ }
+
+ // LDA immediate ($86): checks NZV ($0E) — V always cleared
+ [DataTestMethod]
+ [DataRow(0x00, 0x04)]
+ [DataRow(0x01, 0x00)]
+ [DataRow(0x7F, 0x00)]
+ [DataRow(0x80, 0x08)]
+ [DataRow(0xFF, 0x08)]
+ [DataRow(0xAA, 0x08)]
+ [DataRow(0x55, 0x00)]
+ [DataRow(0x10, 0x00)]
+ public void TestLDA(int data, int flags)
+ {
+ this.board.Poke(0, 0x86);
+ this.board.Poke(1, (byte)data);
+ this.cpu.CC = 0;
+ this.cpu.A = 0xFF; // pre-load a different value
+ this.cpu.Step();
+ Assert.AreEqual((byte)data, this.cpu.A);
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x0E));
+ }
+
+ // LDD immediate ($CC): checks NZV ($0E) — V always cleared
+ [DataTestMethod]
+ [DataRow(0x0000, 0x04)]
+ [DataRow(0x0001, 0x00)]
+ [DataRow(0x7FFF, 0x00)]
+ [DataRow(0x8000, 0x08)]
+ [DataRow(0xFFFF, 0x08)]
+ [DataRow(0xAAAA, 0x08)]
+ [DataRow(0x5555, 0x00)]
+ [DataRow(0x1000, 0x00)]
+ public void TestLDD(int data, int flags)
+ {
+ this.board.Poke(0, 0xCC);
+ this.board.Poke(1, (byte)(data >> 8));
+ this.board.Poke(2, (byte)(data & 0xFF));
+ this.cpu.CC = 0;
+ this.cpu.D.Word = 0x1234; // pre-load a different value
+ this.cpu.Step();
+ Assert.AreEqual((ushort)data, this.cpu.D.Word);
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x0E));
+ }
+
+ // STA extended ($B7 addr): store A to memory, checks NZV ($0E) — V always cleared
+ // Stores to $0100 so it doesn't overwrite the instruction at $0000
+ [DataTestMethod]
+ [DataRow(0x00, 0x04)]
+ [DataRow(0x01, 0x00)]
+ [DataRow(0x7F, 0x00)]
+ [DataRow(0x80, 0x08)]
+ [DataRow(0xFF, 0x08)]
+ [DataRow(0xAA, 0x08)]
+ [DataRow(0x55, 0x00)]
+ [DataRow(0x10, 0x00)]
+ public void TestSTA(int data, int flags)
+ {
+ this.board.Poke(0, 0xB7);
+ this.board.Poke(1, 0x01);
+ this.board.Poke(2, 0x00);
+ this.cpu.CC = 0;
+ this.cpu.A = (byte)data;
+ this.cpu.Step();
+ Assert.AreEqual((byte)data, this.board.Peek(0x0100));
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x0E));
+ }
+
+ // STD extended ($FD addr): store D to memory, checks NZV ($0E) — V always cleared
+ // Stores to $0100 so it doesn't overwrite the instruction
+ [DataTestMethod]
+ [DataRow(0x0000, 0x04)]
+ [DataRow(0x0001, 0x00)]
+ [DataRow(0x7FFF, 0x00)]
+ [DataRow(0x8000, 0x08)]
+ [DataRow(0xFFFF, 0x08)]
+ [DataRow(0xAAAA, 0x08)]
+ [DataRow(0x5555, 0x00)]
+ [DataRow(0x1000, 0x00)]
+ public void TestSTD(int data, int flags)
+ {
+ this.board.Poke(0, 0xFD);
+ this.board.Poke(1, 0x01);
+ this.board.Poke(2, 0x00);
+ this.cpu.CC = 0;
+ this.cpu.D.Word = (ushort)data;
+ this.cpu.Step();
+ var stored = (ushort)((this.board.Peek(0x0100) << 8) | this.board.Peek(0x0101));
+ Assert.AreEqual((ushort)data, stored);
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x0E));
+ }
+
+ // LEAY ,Y ($31,$A4): load Y with effective address of ,Y (i.e. Y itself); checks Z ($04)
+ // Z is set if result is zero, cleared otherwise. No other CC flags affected.
+ [DataTestMethod]
+ [DataRow(0x0000, 0x04)]
+ [DataRow(0x0001, 0x00)]
+ [DataRow(0x7FFF, 0x00)]
+ [DataRow(0x8000, 0x00)]
+ [DataRow(0xFFFF, 0x00)]
+ [DataRow(0x0100, 0x00)]
+ public void TestLEAY(int yValue, int flags)
+ {
+ this.board.Poke(0, 0x31);
+ this.board.Poke(1, 0xA4); // ,Y (no offset, indirect=0)
+ this.cpu.CC = 0;
+ this.cpu.Y.Word = (ushort)yValue;
+ this.cpu.Step();
+ Assert.AreEqual((ushort)yValue, this.cpu.Y.Word);
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x04));
+ }
+
+ // DAA ($19): decimal adjust A after BCD addition; checks NZC ($0D)
+ // initialCC encodes H (bit5) and C (bit0) before the instruction
+ // 64 rows: all combinations of initialCC in {0x00,0x01,0x20,0x21} × A in {0x00..0x0F, plus selected values}
+ // Data from Schwotzer TBDAA table, hardware-verified on EF6809P
+ [DataTestMethod]
+ // initialCC=0x00 (H=0, C=0)
+ [DataRow(0x00, 0x00, 0x00, 0x04)]
+ [DataRow(0x00, 0x09, 0x09, 0x00)]
+ [DataRow(0x00, 0x0A, 0x10, 0x00)]
+ [DataRow(0x00, 0x0F, 0x15, 0x00)]
+ [DataRow(0x00, 0x10, 0x10, 0x00)]
+ [DataRow(0x00, 0x19, 0x19, 0x00)]
+ [DataRow(0x00, 0x1A, 0x20, 0x00)]
+ [DataRow(0x00, 0x90, 0x90, 0x08)]
+ [DataRow(0x00, 0x99, 0x99, 0x08)]
+ [DataRow(0x00, 0x9A, 0x00, 0x05)]
+ [DataRow(0x00, 0xA0, 0x00, 0x05)]
+ [DataRow(0x00, 0xF9, 0x59, 0x01)]
+ [DataRow(0x00, 0xFF, 0x65, 0x01)]
+ // initialCC=0x01 (H=0, C=1)
+ [DataRow(0x01, 0x00, 0x60, 0x01)]
+ [DataRow(0x01, 0x09, 0x69, 0x01)]
+ [DataRow(0x01, 0x0A, 0x70, 0x01)]
+ [DataRow(0x01, 0x0F, 0x75, 0x01)]
+ [DataRow(0x01, 0x10, 0x70, 0x01)]
+ [DataRow(0x01, 0x19, 0x79, 0x01)]
+ [DataRow(0x01, 0x1A, 0x80, 0x09)]
+ [DataRow(0x01, 0x90, 0xF0, 0x09)]
+ [DataRow(0x01, 0x99, 0xF9, 0x09)]
+ [DataRow(0x01, 0x9A, 0x00, 0x05)]
+ [DataRow(0x01, 0xA0, 0x00, 0x05)]
+ [DataRow(0x01, 0xF9, 0x59, 0x01)]
+ [DataRow(0x01, 0xFF, 0x65, 0x01)]
+ // initialCC=0x20 (H=1, C=0)
+ [DataRow(0x20, 0x00, 0x06, 0x00)]
+ [DataRow(0x20, 0x09, 0x0F, 0x00)]
+ [DataRow(0x20, 0x0A, 0x10, 0x00)]
+ [DataRow(0x20, 0x0F, 0x15, 0x00)]
+ [DataRow(0x20, 0x10, 0x16, 0x00)]
+ [DataRow(0x20, 0x19, 0x1F, 0x00)]
+ [DataRow(0x20, 0x1A, 0x20, 0x00)]
+ [DataRow(0x20, 0x90, 0x96, 0x08)]
+ [DataRow(0x20, 0x99, 0x9F, 0x08)]
+ [DataRow(0x20, 0x9A, 0x00, 0x05)]
+ [DataRow(0x20, 0xA0, 0x06, 0x01)]
+ [DataRow(0x20, 0xF9, 0x5F, 0x01)]
+ [DataRow(0x20, 0xFF, 0x65, 0x01)]
+ // initialCC=0x21 (H=1, C=1)
+ [DataRow(0x21, 0x00, 0x66, 0x01)]
+ [DataRow(0x21, 0x09, 0x6F, 0x01)]
+ [DataRow(0x21, 0x0A, 0x70, 0x01)]
+ [DataRow(0x21, 0x0F, 0x75, 0x01)]
+ [DataRow(0x21, 0x10, 0x76, 0x01)]
+ [DataRow(0x21, 0x19, 0x7F, 0x01)]
+ [DataRow(0x21, 0x1A, 0x80, 0x09)]
+ [DataRow(0x21, 0x90, 0xF6, 0x09)]
+ [DataRow(0x21, 0x99, 0xFF, 0x09)]
+ [DataRow(0x21, 0x9A, 0x00, 0x05)]
+ [DataRow(0x21, 0xA0, 0x06, 0x01)]
+ [DataRow(0x21, 0xF9, 0x5F, 0x01)]
+ [DataRow(0x21, 0xFF, 0x65, 0x01)]
+ public void TestDAA(int initialCC, int input, int expected, int flags)
+ {
+ this.board.Poke(0, 0x19);
+ this.cpu.CC = (byte)initialCC;
+ this.cpu.A = (byte)input;
+ this.cpu.Step();
+ Assert.AreEqual((byte)expected, this.cpu.A);
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x0D));
+ }
+ }
+}
diff --git a/MC6809/MC6809.UnitTest/SchwotzerShiftTests.cs b/MC6809/MC6809.UnitTest/SchwotzerShiftTests.cs
new file mode 100644
index 0000000..d15b612
--- /dev/null
+++ b/MC6809/MC6809.UnitTest/SchwotzerShiftTests.cs
@@ -0,0 +1,139 @@
+//
+// Copyright (c) Adrian Conlon. All rights reserved.
+//
+
+// Test data from W. Schwotzer's MC6809 CPU Emulation Validation suite,
+// verified on an SGS Thomson EF6809P processor.
+//
+// Note: the Schwotzer LSL flag table was demonstrably incorrect on several
+// entries (the original LBNE OUTERR for flags was commented out). Result
+// values are hardware-verified; expected flags are from the MC6809 spec:
+// C = old bit 7, N = result bit 7, Z = result==0, V = old_bit7 XOR old_bit6.
+
+namespace MC6809.UnitTest
+{
+ [TestClass]
+ public class SchwotzerShiftTests
+ {
+ private readonly Board board = new();
+ private readonly MC6809 cpu;
+
+ public SchwotzerShiftTests() => this.cpu = this.board.CPU;
+
+ [TestInitialize]
+ public void Initialise() { this.board.RaisePOWER(); this.cpu.Step(); }
+
+ [TestCleanup]
+ public void Cleanup() => this.board.LowerPOWER();
+
+ // LSL: result from hardware; flags from MC6809 spec; checks NZVC ($0F)
+ [DataTestMethod]
+ [DataRow(0x00, 0x00, 0x04)] // C=0 N=0 Z=1 V=0
+ [DataRow(0xFF, 0xFE, 0x09)] // C=1 N=1 Z=0 V=0 (bit7^bit6 = 1^1 = 0)
+ [DataRow(0x7F, 0xFE, 0x0A)] // C=0 N=1 Z=0 V=1 (0^1=1)
+ [DataRow(0x3F, 0x7E, 0x00)] // C=0 N=0 Z=0 V=0 (0^0=0)
+ [DataRow(0x1F, 0x3E, 0x00)]
+ [DataRow(0x0F, 0x1E, 0x00)]
+ [DataRow(0x07, 0x0E, 0x00)]
+ [DataRow(0x03, 0x06, 0x00)]
+ [DataRow(0x01, 0x02, 0x00)]
+ [DataRow(0x55, 0xAA, 0x0A)] // C=0 N=1 Z=0 V=1 (0^1=1)
+ [DataRow(0xAA, 0x54, 0x03)] // C=1 N=0 Z=0 V=1 (1^0=1)
+ [DataRow(0x80, 0x00, 0x07)] // C=1 N=0 Z=1 V=1 (1^0=1)
+ [DataRow(0x10, 0x20, 0x00)] // C=0 N=0 Z=0 V=0 (0^0=0)
+ [DataRow(0xC0, 0x80, 0x09)] // C=1 N=1 Z=0 V=0 (1^1=0)
+ [DataRow(0xE0, 0xC0, 0x09)]
+ [DataRow(0xF0, 0xE0, 0x09)]
+ [DataRow(0xF8, 0xF0, 0x09)]
+ [DataRow(0xFC, 0xF8, 0x09)]
+ [DataRow(0xFE, 0xFC, 0x09)]
+ public void TestLSLA(int input, int expected, int flags)
+ {
+ this.board.Poke(0, 0x48);
+ this.cpu.CC = 0;
+ this.cpu.A = (byte)input;
+ this.cpu.Step();
+ Assert.AreEqual((byte)expected, this.cpu.A);
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x0F));
+ }
+
+ // ROR: carry-in from carryIn parameter; checks NZC ($0D)
+ [DataTestMethod]
+ [DataRow(0x00, 0, 0x00, 0x04)]
+ [DataRow(0x01, 0, 0x00, 0x05)]
+ [DataRow(0x0F, 0, 0x07, 0x01)]
+ [DataRow(0x10, 0, 0x08, 0x00)]
+ [DataRow(0x7F, 0, 0x3F, 0x01)]
+ [DataRow(0x80, 0, 0x40, 0x00)]
+ [DataRow(0xFE, 0, 0x7F, 0x00)]
+ [DataRow(0xFF, 0, 0x7F, 0x01)]
+ [DataRow(0x00, 1, 0x80, 0x08)]
+ [DataRow(0x01, 1, 0x80, 0x09)]
+ [DataRow(0x0F, 1, 0x87, 0x09)]
+ [DataRow(0x10, 1, 0x88, 0x08)]
+ [DataRow(0x7F, 1, 0xBF, 0x09)]
+ [DataRow(0x80, 1, 0xC0, 0x08)]
+ [DataRow(0xFE, 1, 0xFF, 0x08)]
+ [DataRow(0xFF, 1, 0xFF, 0x09)]
+ public void TestRORA(int input, int carryIn, int expected, int flags)
+ {
+ this.board.Poke(0, 0x46);
+ this.cpu.CC = (byte)carryIn;
+ this.cpu.A = (byte)input;
+ this.cpu.Step();
+ Assert.AreEqual((byte)expected, this.cpu.A);
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x0D));
+ }
+
+ // ROL: carry-in from carryIn parameter; checks NZC ($0D)
+ [DataTestMethod]
+ [DataRow(0x00, 0, 0x00, 0x04)]
+ [DataRow(0x01, 0, 0x02, 0x00)]
+ [DataRow(0x0F, 0, 0x1E, 0x00)]
+ [DataRow(0x08, 0, 0x10, 0x00)]
+ [DataRow(0x7F, 0, 0xFE, 0x08)]
+ [DataRow(0x80, 0, 0x00, 0x05)]
+ [DataRow(0xFE, 0, 0xFC, 0x09)]
+ [DataRow(0xFF, 0, 0xFE, 0x09)]
+ [DataRow(0x00, 1, 0x01, 0x00)]
+ [DataRow(0x01, 1, 0x03, 0x00)]
+ [DataRow(0x0F, 1, 0x1F, 0x00)]
+ [DataRow(0x08, 1, 0x11, 0x00)]
+ [DataRow(0x7F, 1, 0xFF, 0x08)]
+ [DataRow(0x80, 1, 0x01, 0x01)]
+ [DataRow(0xFE, 1, 0xFD, 0x09)]
+ [DataRow(0xFF, 1, 0xFF, 0x09)]
+ public void TestROLA(int input, int carryIn, int expected, int flags)
+ {
+ this.board.Poke(0, 0x49);
+ this.cpu.CC = (byte)carryIn;
+ this.cpu.A = (byte)input;
+ this.cpu.Step();
+ Assert.AreEqual((byte)expected, this.cpu.A);
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x0D));
+ }
+
+ // BIT: AND without storing result; checks NZV ($0E)
+ [DataTestMethod]
+ [DataRow(0x00, 0x00, 0x04)]
+ [DataRow(0xAA, 0x55, 0x04)]
+ [DataRow(0xAA, 0xAA, 0x08)]
+ [DataRow(0x55, 0x55, 0x00)]
+ [DataRow(0xFF, 0xFF, 0x08)]
+ [DataRow(0xFF, 0x80, 0x08)]
+ [DataRow(0x81, 0x80, 0x08)]
+ [DataRow(0xFF, 0x7F, 0x00)]
+ [DataRow(0xFF, 0x01, 0x00)]
+ [DataRow(0xF0, 0x0F, 0x04)]
+ public void TestBITA(int operand, int data, int flags)
+ {
+ this.board.Poke(0, 0x85);
+ this.board.Poke(1, (byte)data);
+ this.cpu.CC = 0;
+ this.cpu.A = (byte)operand;
+ this.cpu.Step();
+ Assert.AreEqual((byte)operand, this.cpu.A); // A must not be modified
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x0E));
+ }
+ }
+}
diff --git a/MC6809/MC6809.UnitTest/SchwotzerUnaryTests.cs b/MC6809/MC6809.UnitTest/SchwotzerUnaryTests.cs
new file mode 100644
index 0000000..c3d5a1e
--- /dev/null
+++ b/MC6809/MC6809.UnitTest/SchwotzerUnaryTests.cs
@@ -0,0 +1,249 @@
+//
+// Copyright (c) Adrian Conlon. All rights reserved.
+//
+
+// Test data from W. Schwotzer's MC6809 CPU Emulation Validation suite,
+// verified on an SGS Thomson EF6809P processor.
+
+namespace MC6809.UnitTest
+{
+ [TestClass]
+ public class SchwotzerUnaryTests
+ {
+ private readonly Board board = new();
+ private readonly MC6809 cpu;
+
+ public SchwotzerUnaryTests() => this.cpu = this.board.CPU;
+
+ [TestInitialize]
+ public void Initialise() { this.board.RaisePOWER(); this.cpu.Step(); }
+
+ [TestCleanup]
+ public void Cleanup() => this.board.LowerPOWER();
+
+ // NEG: checks NZVC ($0F)
+ [DataTestMethod]
+ [DataRow(0x00, 0x00, 0x04)]
+ [DataRow(0x80, 0x80, 0x0B)]
+ [DataRow(0x01, 0xFF, 0x09)]
+ [DataRow(0xFF, 0x01, 0x01)]
+ [DataRow(0x7F, 0x81, 0x09)]
+ [DataRow(0x81, 0x7F, 0x01)]
+ [DataRow(0xC0, 0x40, 0x01)]
+ [DataRow(0x40, 0xC0, 0x09)]
+ public void TestNEGA(int input, int expected, int flags)
+ {
+ this.board.Poke(0, 0x40);
+ this.cpu.CC = 0;
+ this.cpu.A = (byte)input;
+ this.cpu.Step();
+ Assert.AreEqual((byte)expected, this.cpu.A);
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x0F));
+ }
+
+ // COM: checks NZVC ($0F)
+ [DataTestMethod]
+ [DataRow(0x00, 0xFF, 0x09)]
+ [DataRow(0xFF, 0x00, 0x05)]
+ [DataRow(0xF0, 0x0F, 0x01)]
+ [DataRow(0x0F, 0xF0, 0x09)]
+ [DataRow(0x55, 0xAA, 0x09)]
+ [DataRow(0xAA, 0x55, 0x01)]
+ [DataRow(0x01, 0xFE, 0x09)]
+ [DataRow(0xFE, 0x01, 0x01)]
+ [DataRow(0x80, 0x7F, 0x01)]
+ [DataRow(0x7F, 0x80, 0x09)]
+ public void TestCOMA(int input, int expected, int flags)
+ {
+ this.board.Poke(0, 0x43);
+ this.cpu.CC = 0;
+ this.cpu.A = (byte)input;
+ this.cpu.Step();
+ Assert.AreEqual((byte)expected, this.cpu.A);
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x0F));
+ }
+
+ // CLR: checks NZVC ($0F) — always clears to zero, Z=1
+ [DataTestMethod]
+ [DataRow(0x01, 0x04)]
+ [DataRow(0x00, 0x04)]
+ [DataRow(0xFF, 0x04)]
+ [DataRow(0x80, 0x04)]
+ [DataRow(0x7F, 0x04)]
+ [DataRow(0x10, 0x04)]
+ [DataRow(0x0F, 0x04)]
+ public void TestCLRA(int input, int flags)
+ {
+ this.board.Poke(0, 0x4F);
+ this.cpu.CC = 0;
+ this.cpu.A = (byte)input;
+ this.cpu.Step();
+ Assert.AreEqual(0, this.cpu.A);
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x0F));
+ }
+
+ // DEC: checks NZV ($0E)
+ [DataTestMethod]
+ [DataRow(0x01, 0x00, 0x04)]
+ [DataRow(0x00, 0xFF, 0x08)]
+ [DataRow(0xFF, 0xFE, 0x08)]
+ [DataRow(0x80, 0x7F, 0x02)]
+ [DataRow(0x7F, 0x7E, 0x00)]
+ [DataRow(0x10, 0x0F, 0x00)]
+ [DataRow(0x0F, 0x0E, 0x00)]
+ public void TestDECA(int input, int expected, int flags)
+ {
+ this.board.Poke(0, 0x4A);
+ this.cpu.CC = 0;
+ this.cpu.A = (byte)input;
+ this.cpu.Step();
+ Assert.AreEqual((byte)expected, this.cpu.A);
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x0E));
+ }
+
+ // INC: checks NZV ($0E)
+ [DataTestMethod]
+ [DataRow(0x00, 0x01, 0x00)]
+ [DataRow(0xFE, 0xFF, 0x08)]
+ [DataRow(0xFF, 0x00, 0x04)]
+ [DataRow(0x7F, 0x80, 0x0A)]
+ [DataRow(0x80, 0x81, 0x08)]
+ [DataRow(0x0F, 0x10, 0x00)]
+ [DataRow(0x10, 0x11, 0x00)]
+ public void TestINCA(int input, int expected, int flags)
+ {
+ this.board.Poke(0, 0x4C);
+ this.cpu.CC = 0;
+ this.cpu.A = (byte)input;
+ this.cpu.Step();
+ Assert.AreEqual((byte)expected, this.cpu.A);
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x0E));
+ }
+
+ // TST: checks NZV ($0E) — does not modify A
+ [DataTestMethod]
+ [DataRow(0x00, 0x04)]
+ [DataRow(0x80, 0x08)]
+ [DataRow(0x01, 0x00)]
+ [DataRow(0xFF, 0x08)]
+ [DataRow(0x7F, 0x00)]
+ [DataRow(0x81, 0x08)]
+ [DataRow(0xC0, 0x08)]
+ [DataRow(0x40, 0x00)]
+ public void TestTSTA(int input, int flags)
+ {
+ this.board.Poke(0, 0x4D);
+ this.cpu.CC = 0;
+ this.cpu.A = (byte)input;
+ this.cpu.Step();
+ Assert.AreEqual((byte)input, this.cpu.A);
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x0E));
+ }
+
+ // LSR: checks NZVC ($0F)
+ [DataTestMethod]
+ [DataRow(0x00, 0x00, 0x04)]
+ [DataRow(0xFF, 0x7F, 0x01)]
+ [DataRow(0x7F, 0x3F, 0x01)]
+ [DataRow(0x3F, 0x1F, 0x01)]
+ [DataRow(0x1F, 0x0F, 0x01)]
+ [DataRow(0x0F, 0x07, 0x01)]
+ [DataRow(0x07, 0x03, 0x01)]
+ [DataRow(0x03, 0x01, 0x01)]
+ [DataRow(0x01, 0x00, 0x05)]
+ [DataRow(0x55, 0x2A, 0x01)]
+ [DataRow(0xAA, 0x55, 0x00)]
+ [DataRow(0x80, 0x40, 0x00)]
+ [DataRow(0x10, 0x08, 0x00)]
+ [DataRow(0xC0, 0x60, 0x00)]
+ [DataRow(0xE0, 0x70, 0x00)]
+ [DataRow(0xF0, 0x78, 0x00)]
+ [DataRow(0xF8, 0x7C, 0x00)]
+ [DataRow(0xFC, 0x7E, 0x00)]
+ [DataRow(0xFE, 0x7F, 0x00)]
+ public void TestLSRA(int input, int expected, int flags)
+ {
+ this.board.Poke(0, 0x44);
+ this.cpu.CC = 0;
+ this.cpu.A = (byte)input;
+ this.cpu.Step();
+ Assert.AreEqual((byte)expected, this.cpu.A);
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x0F));
+ }
+
+ // ASR: checks NZC ($0D)
+ [DataTestMethod]
+ [DataRow(0x00, 0x00, 0x04)]
+ [DataRow(0xFF, 0xFF, 0x09)]
+ [DataRow(0x7F, 0x3F, 0x01)]
+ [DataRow(0x3F, 0x1F, 0x01)]
+ [DataRow(0x1F, 0x0F, 0x01)]
+ [DataRow(0x0F, 0x07, 0x01)]
+ [DataRow(0x07, 0x03, 0x01)]
+ [DataRow(0x03, 0x01, 0x01)]
+ [DataRow(0x01, 0x00, 0x05)]
+ [DataRow(0x55, 0x2A, 0x01)]
+ [DataRow(0xAA, 0xD5, 0x08)]
+ [DataRow(0x80, 0xC0, 0x08)]
+ [DataRow(0x10, 0x08, 0x00)]
+ [DataRow(0xC0, 0xE0, 0x08)]
+ [DataRow(0xE0, 0xF0, 0x08)]
+ [DataRow(0xF0, 0xF8, 0x08)]
+ [DataRow(0xF8, 0xFC, 0x08)]
+ [DataRow(0xFC, 0xFE, 0x08)]
+ [DataRow(0xFE, 0xFF, 0x08)]
+ public void TestASRA(int input, int expected, int flags)
+ {
+ this.board.Poke(0, 0x47);
+ this.cpu.CC = 0;
+ this.cpu.A = (byte)input;
+ this.cpu.Step();
+ Assert.AreEqual((byte)expected, this.cpu.A);
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x0D));
+ }
+
+ // SEX: input in B, sign-extended into D; checks NZ ($0C)
+ [DataTestMethod]
+ [DataRow(0x00, 0x00, 0x00, 0x04)]
+ [DataRow(0x01, 0x00, 0x01, 0x00)]
+ [DataRow(0x7F, 0x00, 0x7F, 0x00)]
+ [DataRow(0x80, 0xFF, 0x80, 0x08)]
+ [DataRow(0xFF, 0xFF, 0xFF, 0x08)]
+ public void TestSEX(int inputB, int expectedA, int expectedB, int flags)
+ {
+ this.board.Poke(0, 0x1D);
+ this.cpu.CC = 0;
+ this.cpu.B = (byte)inputB;
+ this.cpu.Step();
+ Assert.AreEqual((byte)expectedA, this.cpu.A);
+ Assert.AreEqual((byte)expectedB, this.cpu.B);
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x0C));
+ }
+
+ // MUL: A × B → D (unsigned); checks ZC ($05)
+ // C is set if bit 7 of the result's low byte (B) is set
+ [DataTestMethod]
+ [DataRow(0x00, 0x00, 0x00, 0x00, 0x04)]
+ [DataRow(0x80, 0x80, 0x40, 0x00, 0x00)]
+ [DataRow(0x01, 0xFF, 0x00, 0xFF, 0x01)]
+ [DataRow(0xFF, 0x01, 0x00, 0xFF, 0x01)]
+ [DataRow(0x7F, 0x81, 0x3F, 0xFF, 0x01)]
+ [DataRow(0x81, 0x7F, 0x3F, 0xFF, 0x01)]
+ [DataRow(0xC0, 0x40, 0x30, 0x00, 0x00)]
+ [DataRow(0x40, 0xC0, 0x30, 0x00, 0x00)]
+ [DataRow(0xFF, 0xFF, 0xFE, 0x01, 0x00)]
+ [DataRow(0x7F, 0x7F, 0x3F, 0x01, 0x00)]
+ [DataRow(0x01, 0x01, 0x00, 0x01, 0x00)]
+ public void TestMUL(int inputA, int inputB, int expectedA, int expectedB, int flags)
+ {
+ this.board.Poke(0, 0x3D);
+ this.cpu.CC = 0;
+ this.cpu.A = (byte)inputA;
+ this.cpu.B = (byte)inputB;
+ this.cpu.Step();
+ Assert.AreEqual((byte)expectedA, this.cpu.A);
+ Assert.AreEqual((byte)expectedB, this.cpu.B);
+ Assert.AreEqual((byte)flags, (byte)(this.cpu.CC & 0x05));
+ }
+ }
+}
diff --git a/MC6809/MC6809.cs b/MC6809/MC6809.cs
index b63969e..c73730d 100644
--- a/MC6809/MC6809.cs
+++ b/MC6809/MC6809.cs
@@ -2165,17 +2165,27 @@ namespace MC6809
{
this.LEA(this.X);
this.CC = this.AdjustZero(this.X);
+ this.SwallowRead();
}
private void LEAY()
{
this.LEA(this.Y);
this.CC = this.AdjustZero(this.Y);
+ this.SwallowRead();
}
- private void LEAS() => this.LEA(this.S);
+ private void LEAS()
+ {
+ this.LEA(this.S);
+ this.SwallowRead();
+ }
- private void LEAU() => this.LEA(this.U);
+ private void LEAU()
+ {
+ this.LEA(this.U);
+ this.SwallowRead();
+ }
private void LEA(Register16 register) => register.Assign(this.EA);