Add 6809 Schwotzer tests

This commit is contained in:
Adrian Conlon committed 2026-05-12 22:18:51 +01:00
1 parent b228332e27
commit f1fb442f08
5 files changed
+952 -2

No files matched your search

@@ -0,0 +1,267 @@
// <copyright file="SchwotzerArithmeticTests.cs" company="Adrian Conlon">
// Copyright (c) Adrian Conlon. All rights reserved.
// </copyright>
// 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));
}
}
}
@@ -0,0 +1,285 @@
// <copyright file="SchwotzerCompareLoadTests.cs" company="Adrian Conlon">
// Copyright (c) Adrian Conlon. All rights reserved.
// </copyright>
// 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));
}
}
}
@@ -0,0 +1,139 @@
// <copyright file="SchwotzerShiftTests.cs" company="Adrian Conlon">
// Copyright (c) Adrian Conlon. All rights reserved.
// </copyright>
// 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));
}
}
}
@@ -0,0 +1,249 @@
// <copyright file="SchwotzerUnaryTests.cs" company="Adrian Conlon">
// Copyright (c) Adrian Conlon. All rights reserved.
// </copyright>
// 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));
}
}
}
+12 -2
View File
@@ -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);