From 12dc90c0643f4974920231d994e87289678134be Mon Sep 17 00:00:00 2001 From: Adrian Conlon Date: Sun, 14 Oct 2018 10:06:47 +0100 Subject: [PATCH] Add lots of tests, mainly covering addition and subtraction. Shows quite a few problems... Signed-off-by: Adrian Conlon --- MC6809/unittest/mc6809_tests.cpp | 689 ++++++++++++++++++++++++++++++- 1 file changed, 687 insertions(+), 2 deletions(-) diff --git a/MC6809/unittest/mc6809_tests.cpp b/MC6809/unittest/mc6809_tests.cpp index 869a77a..3a66071 100644 --- a/MC6809/unittest/mc6809_tests.cpp +++ b/MC6809/unittest/mc6809_tests.cpp @@ -1,8 +1,10 @@ #include "pch.h" #include "Board.h" -// Using examples from 6809 Assembly Language Programming, by Lance A. Leventhal -// Just test the basics... +// Some tests from: +// 6809 Assembly Language Programming, by Lance A. Leventhal +// https://github.com/sorenroug/osnine-java/blob/master/core/cputests/ +// void setRegs(a, b, x, y, u) TEST_CASE("Add Accumulator B to Index Register X Unsigned", "[ABX]") { @@ -19,6 +21,50 @@ TEST_CASE("Add Accumulator B to Index Register X Unsigned", "[ABX]") { REQUIRE(cpu.X() == 0x111b); REQUIRE(cpu.cycles() == 3); } + + SECTION("Inherent test (ABX1)") { + cpu.A() = 0; + cpu.B() = 0xce; + cpu.X() = 0x8006; + cpu.Y() = 0; + cpu.U() = 0; + cpu.CC() = 0; + board.poke(0, 0x3a); + cpu.step(); + REQUIRE(cpu.A() == 0); + REQUIRE(cpu.B() == 0xce); + REQUIRE(cpu.X() == 0x80d4); + REQUIRE(cpu.Y() == 0); + REQUIRE(cpu.U() == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::HF) == 0); + REQUIRE(cpu.cycles() == 3); + } + + SECTION("Inherent test (ABX2)") { + cpu.A() = 0; + cpu.B() = 0xd6; + cpu.X() = 0x7ffe; + cpu.Y() = 0; + cpu.U() = 0; + cpu.CC() = EightBit::mc6809::CF | EightBit::mc6809::VF | EightBit::mc6809::ZF; + board.poke(0, 0x3a); + cpu.step(); + REQUIRE(cpu.A() == 0); + REQUIRE(cpu.B() == 0xd6); + REQUIRE(cpu.X() == 0x80d4); + REQUIRE(cpu.Y() == 0); + REQUIRE(cpu.U() == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::HF) == 0); + REQUIRE(cpu.cycles() == 3); + } } TEST_CASE("Add Memory Plus Carry to Accumulator A", "[ADC][ADCA]") { @@ -42,6 +88,66 @@ TEST_CASE("Add Memory Plus Carry to Accumulator A", "[ADC][ADCA]") { REQUIRE((cpu.CC() & EightBit::mc6809::CF) == 0); REQUIRE(cpu.cycles() == 2); } + + SECTION("Immediate (byte) ADCANoC1") { + cpu.A() = 0x5; + cpu.CC() = 0; + board.poke(0, 0x89); + board.poke(1, 0x02); + cpu.step(); + REQUIRE(cpu.A() == 7); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::HF) == 0); + REQUIRE(cpu.cycles() == 2); + } + + /* Test half-carry $E + $2 = $10 */ + SECTION("Immediate (byte) ADCANoC2") { + cpu.A() = 0xe; + cpu.CC() = 0; + board.poke(0, 0x89); + board.poke(1, 0x02); + cpu.step(); + REQUIRE(cpu.A() == 0x10); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::HF) != 0); + REQUIRE(cpu.cycles() == 2); + } + + /* Add $22 and carry to register A ($14) */ + SECTION("Immediate (byte) ADCAWiC") { + cpu.A() = 0x14; + cpu.CC() = EightBit::mc6809::CF | EightBit::mc6809::HF; + board.poke(0, 0x89); + board.poke(1, 0x22); + cpu.step(); + REQUIRE(cpu.A() == 0x37); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::HF) == 0); + REQUIRE(cpu.cycles() == 2); + } + + /* Test that half-carry is set when adding with a carry */ + SECTION("Immediate (byte) ADCAWiHC") { + cpu.A() = 0x14; + cpu.CC() = EightBit::mc6809::CF; + board.poke(0, 0x89); + board.poke(1, 0x2B); + cpu.step(); + REQUIRE(cpu.A() == 0x40); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::HF) != 0); + REQUIRE(cpu.cycles() == 2); + } } TEST_CASE("Add Memory to Accumulator A", "[ADD][ADDA]") { @@ -64,6 +170,197 @@ TEST_CASE("Add Memory to Accumulator A", "[ADD][ADDA]") { REQUIRE((cpu.CC() & EightBit::mc6809::CF) == 0); REQUIRE(cpu.cycles() == 2); } + + // Add 0x02 to A=0x04. + SECTION("Immediate (byte) (ADDANoC)") { + board.poke(0, 0x8b); + board.poke(1, 0x02); + cpu.CC() = 0; + cpu.A() = 4; + cpu.B() = 5; + cpu.step(); + REQUIRE(cpu.A() == 6); + REQUIRE(cpu.B() == 5); + REQUIRE((cpu.CC() & EightBit::mc6809::HF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) == 0); + REQUIRE(cpu.cycles() == 2); + } + + // The overflow (V) bit indicates signed two’s complement overflow, which occurs when the + // sign bit differs from the carry bit after an arithmetic operation. + // A=0x03 + 0xFF becomes 0x02 + SECTION("Immediate (byte) (ADDAWiC)") { + board.poke(0, 0x8b); + board.poke(1, 0xff); + cpu.CC() = 0; + cpu.A() = 3; + cpu.step(); + REQUIRE(cpu.A() == 2); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) != 0); + REQUIRE(cpu.cycles() == 2); + } +} + +TEST_CASE("Add Memory to Accumulator B", "[ADD][ADDB]") { + + Board board; + board.initialise(); + auto& cpu = board.CPU(); + cpu.step(); // Step over the reset + + // positive + positive with overflow. + // B=0x40 + 0x41 becomes 0x81 or -127 + SECTION("Immediate (byte) ADDB1") { + board.poke(0, 0xcb); + board.poke(1, 0x41); + cpu.B() = 0x40; + cpu.CC() = 0; + cpu.step(); + REQUIRE(cpu.B() == 0x81); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::HF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) == 0); + REQUIRE(cpu.cycles() == 2); + } + + // negative + negative. + // B=0xFF + 0xFF becomes 0xFE or -2 + SECTION("Immediate (byte) ADDB2") { + board.poke(0, 0xcb); + board.poke(1, 0xff); + cpu.B() = 0xff; + cpu.CC() = 0; + cpu.step(); + REQUIRE(cpu.B() == 0xfe); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) != 0); + REQUIRE(cpu.cycles() == 2); + } + + // negative + negative with overflow. + // B=0xC0 + 0xBF becomes 0x7F or 127 + SECTION("Immediate (byte) ADDB3") { + board.poke(0, 0xcb); + board.poke(1, 0xbf); + cpu.B() = 0xc0; + cpu.CC() = 0; + cpu.step(); + REQUIRE(cpu.B() == 0x7f); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) != 0); + REQUIRE(cpu.cycles() == 2); + } + + // positive + negative with negative result. + // B=0x02 + 0xFC becomes 0xFE or -2 + SECTION("Immediate (byte) ADDB4") { + board.poke(0, 0xcb); + board.poke(1, 0xfc); + cpu.B() = 0x02; + cpu.CC() = 0; + cpu.step(); + REQUIRE(cpu.B() == 0xfe); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) == 0); + REQUIRE(cpu.cycles() == 2); + } +} + +TEST_CASE("Add Memory to Accumulator D", "[ADD][ADDD]") { + + Board board; + board.initialise(); + auto& cpu = board.CPU(); + cpu.step(); // Step over the reset + + // Add 0x02B0 to D=0x0405 becomes 0x6B5. + // positive + positive = positive + SECTION("Immediate (word) (ADDDNoC)") { + board.poke(0, 0xc3); + board.poke(1, 0x02); + board.poke(2, 0xb0); + cpu.CC() = 0; + cpu.A() = 4; + cpu.B() = 5; + cpu.step(); + REQUIRE(cpu.A() == 0x06); + REQUIRE(cpu.B() == 0xb5); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) == 0); + REQUIRE(cpu.cycles() == 4); + } + + // Add 0xE2B0 to D=0x8405 becomes 0x66B5. + // negative + negative = positive + overflow + SECTION("Immediate (word) (ADDD1)") { + board.poke(0, 0xc3); + board.poke(1, 0xe2); + board.poke(2, 0xb0); + cpu.CC() = 0; + cpu.A() = 0x84; + cpu.B() = 5; + cpu.step(); + REQUIRE(cpu.A() == 0x66); + REQUIRE(cpu.B() == 0xb5); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) != 0); + REQUIRE(cpu.cycles() == 4); + } + + // negative + negative = negative. + // Add 0xE000 to D=0xD000 becomes 0xB000 + SECTION("Immediate (word) (ADDD2)") { + board.poke(0, 0xc3); + board.poke(1, 0xe0); + board.poke(2, 0); + cpu.CC() = 0; + cpu.A() = 0xd0; + cpu.B() = 0; + cpu.step(); + REQUIRE(cpu.A() == 0xb0); + REQUIRE(cpu.B() == 0x00); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) != 0); + REQUIRE(cpu.cycles() == 4); + } + + // positive + positive = negative + overflow. + // Add 0x7000 to D=0x7000 becomes 0xE000 + SECTION("Immediate (word) (ADDD3)") { + board.poke(0, 0xc3); + board.poke(1, 0x70); + board.poke(2, 0); + cpu.CC() = 0; + cpu.A() = 0x70; + cpu.B() = 0; + cpu.step(); + REQUIRE(cpu.A() == 0xe0); + REQUIRE(cpu.B() == 0x00); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) == 0); + REQUIRE(cpu.cycles() == 4); + } } TEST_CASE("Logical AND Accumulator", "[AND][ANDA]") { @@ -162,3 +459,391 @@ TEST_CASE("Clear Accumulator or Memory", "[CLR][CLRA]") { REQUIRE(cpu.cycles() == 2); } } + +TEST_CASE("Compare Memory with a Register (8-bit)", "[CMP][CMPA][CMPB]") { + + Board board; + board.initialise(); + auto& cpu = board.CPU(); + cpu.step(); // Step over the reset + + SECTION("Immediate (byte)") { + board.poke(0, 0x81); + board.poke(1, 0x18); + cpu.A() = 0xf6; + cpu.step(); + REQUIRE(cpu.A() == 0xf6); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) == 0); + REQUIRE(cpu.cycles() == 2); + } + + SECTION("Indirect mode: CMPA ,Y+ (CMP1)") { + board.poke(0, 0xa1); + board.poke(1, 0xa0); + board.poke(0x205, 0xff); + cpu.CC() = 0; + cpu.A() = 0xff; + cpu.B() = 0; + cpu.X() = 0; + cpu.Y() = 0x205; + cpu.U() = 0; + cpu.step(); + REQUIRE(cpu.A() == 0xff); + REQUIRE(cpu.B() == 0x00); + REQUIRE(cpu.X() == 0x00); + REQUIRE(cpu.Y() == 0x206); + REQUIRE(cpu.U() == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) != 0); + REQUIRE(cpu.cycles() == 6); + } + + // B = 0xA0, CMPB with 0xA0 + SECTION("Immediate (CMP2)") { + board.poke(0, 0xc1); + board.poke(1, 0xa0); + cpu.CC() = EightBit::mc6809::NF; + cpu.B() = 0xa0; + cpu.step(); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) == 0); + REQUIRE(cpu.cycles() == 2); + } + + // B = 0x70, CMPB with 0xA0 + SECTION("Immediate (CMP3)") { + board.poke(0, 0xc1); + board.poke(1, 0xa0); + cpu.CC() = EightBit::mc6809::NF; + cpu.B() = 0x70; + cpu.step(); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) != 0); + REQUIRE(cpu.cycles() == 2); + } + + // Compare 0x5410 with 0x5410 + SECTION("Immediate (word) (CMP16)") { + board.poke(0, 0xc1); + board.poke(1, 0xa0); + cpu.CC() = EightBit::mc6809::NF; + cpu.B() = 0x70; + cpu.step(); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) != 0); + REQUIRE(cpu.cycles() == 2); + } +} + +TEST_CASE("Compare Memory with a Register (16-bit)", "[CMP][CMPX]") { + + Board board; + board.initialise(); + auto& cpu = board.CPU(); + cpu.step(); // Step over the reset + + SECTION("Immediate (word)") { + board.poke(0, 0x8c); + board.poke(1, 0x1b); + board.poke(2, 0xb0); + cpu.X() = 0x1ab0; + cpu.step(); + REQUIRE(cpu.X() == 0x1ab0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) != 0); + REQUIRE(cpu.cycles() == 4); + } +} + +TEST_CASE("Decrement Accumulator or Memory", "[DEC][DECA]") { + + Board board; + board.initialise(); + auto& cpu = board.CPU(); + cpu.step(); // Step over the reset + + SECTION("Inherent (DECA0x32)") { + board.poke(0, 0x4a); + cpu.CC() = 0; + cpu.A() = 0x32; + cpu.step(); + REQUIRE(cpu.A() == 0x31); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) == 0); + REQUIRE(cpu.cycles() == 2); + } + + // Test 0x80 - special case + SECTION("Inherent (DECA0x80)") { + board.poke(0, 0x4a); + cpu.CC() = 0; + cpu.A() = 0x80; + cpu.step(); + REQUIRE(cpu.A() == 0x7f); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) == 0); + REQUIRE(cpu.cycles() == 2); + } + + // Test 0x00 - special case + SECTION("Inherent (DECA0x00)") { + board.poke(0, 0x4a); + cpu.CC() = 0; + cpu.A() = 0x00; + cpu.step(); + REQUIRE(cpu.A() == 0xff); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) != 0); + REQUIRE(cpu.cycles() == 2); + } +} + +TEST_CASE("Increment Accumulator or Memory Location by 1", "[INC][INCA]") { + + Board board; + board.initialise(); + auto& cpu = board.CPU(); + cpu.step(); // Step over the reset + + SECTION("Inherent (INCA1)") { + board.poke(0, 0x4c); + cpu.CC() = 0; + cpu.A() = 0x32; + cpu.step(); + REQUIRE(cpu.A() == 0x33); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) == 0); + REQUIRE(cpu.cycles() == 2); + } + + SECTION("Inherent (INCA2)") { + board.poke(0, 0x4c); + cpu.CC() = 0; + cpu.A() = 0x7f; + cpu.step(); + REQUIRE(cpu.A() == 0x80); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) == 0); + REQUIRE(cpu.cycles() == 2); + } + + SECTION("Inherent (INCA3)") { + board.poke(0, 0x4c); + cpu.CC() = 0; + cpu.A() = 0xff; + cpu.step(); + REQUIRE(cpu.A() == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) == 0); + REQUIRE(cpu.cycles() == 2); + } +} + + +TEST_CASE("Subtract Memory from Accumulator with Borrow (8-bit)", "[SBC][SBCA][SBCB]") { + + Board board; + board.initialise(); + auto& cpu = board.CPU(); + cpu.step(); // Step over the reset + + SECTION("Immediate (byte)") { + board.poke(0, 0x82); + board.poke(1, 0x34); + cpu.A() = 0x14; + cpu.setFlag(cpu.CC(), EightBit::mc6809::CF); + cpu.step(); + REQUIRE(cpu.A() == 0xdf); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) != 0); + REQUIRE(cpu.cycles() == 4); + } + + // Test the subtraction with carry instruction. + // B=0x35 - addr(0x503)=0x3 - C=1 becomes 0x31 + // SBCB dp+03 + SECTION("Direct (SBCB)") { + board.poke(0, 0xd2); + board.poke(1, 0x03); + board.poke(0x503, 0x03); + cpu.DP() = 5; + cpu.B() = 0x35; + cpu.CC() = EightBit::mc6809::CF; + cpu.step(); + REQUIRE(cpu.B() == 0x31); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) == 0); + REQUIRE(cpu.cycles() == 4); + } + + // Test the SBCA instruction. + // A=0xFF - 0xFE - C=1 becomes 0x00 + SECTION("Immediate (SBCA) (SBCA1)") { + board.poke(0, 0x82); + board.poke(1, 0xfe); + board.poke(0x503, 0x03); + cpu.CC() = EightBit::mc6809::CF | EightBit::mc6809::NF; + cpu.A() = 0xff; + cpu.step(); + REQUIRE(cpu.A() == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) == 0); + REQUIRE(cpu.cycles() == 4); + } + + // Test the SBCA instruction. + // A=0x00 - 0xFF - C=0 becomes 0x01 + SECTION("Immediate (SBCA) (SBCA2)") { + board.poke(0, 0x82); + board.poke(1, 0xff); + cpu.CC() = EightBit::mc6809::NF | EightBit::mc6809::VF; + cpu.A() = 0; + cpu.step(); + REQUIRE(cpu.A() == 1); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) == 0); + REQUIRE(cpu.cycles() == 4); + } + + // Test the SBCA instruction. + // A=0x00 - 0x01 - C=0 becomes 0xFF + SECTION("Immediate (SBCA) (SBCA3)") { + board.poke(0, 0x82); + board.poke(1, 0x01); + cpu.CC() = EightBit::mc6809::NF | EightBit::mc6809::VF; + cpu.A() = 0; + cpu.step(); + REQUIRE(cpu.A() == 0xff); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) != 0); + REQUIRE(cpu.cycles() == 4); + } +} + +TEST_CASE("Subtract Memory from Register ", "[SUB][SUBA]") { + + Board board; + board.initialise(); + auto& cpu = board.CPU(); + cpu.step(); // Step over the reset + + // Test the SUBA instruction. + // The overflow (V) bit indicates signed two’s complement overflow, which + // occurs when the sign bit differs from the carry bit after an arithmetic + // operation. + // A=0x00 - 0xFF becomes 0x01 + // positive - negative = positive + SECTION("Immediate (SUBA) (SUBA1)") { + board.poke(0, 0x80); + board.poke(1, 0xff); + cpu.CC() = EightBit::mc6809::CF | EightBit::mc6809::NF | EightBit::mc6809::VF; + cpu.A() = 0; + cpu.step(); + REQUIRE(cpu.A() == 1); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) == 0); + REQUIRE(cpu.cycles() == 2); + } + + // A=0x00 - 0x01 becomes 0xFF + // positive - positive = negative + SECTION("Immediate (SUBA) (SUBA2)") { + board.poke(0, 0x80); + board.poke(1, 1); + cpu.CC() = EightBit::mc6809::CF | EightBit::mc6809::NF | EightBit::mc6809::VF; + cpu.A() = 0; + cpu.step(); + REQUIRE(cpu.A() == 0xff); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) != 0); + REQUIRE(cpu.cycles() == 2); + } + + // Test the subtraction instruction. + // IMMEDIATE mode: B=0x02 - 0xB3 becomes 0x4F + // positive - negative = positive + SECTION("Immediate (SUBB) (SUBB1)") { + board.poke(0, 0xc0); + board.poke(1, 0xb3); + cpu.CC() = 0; + cpu.B() = 2; + cpu.step(); + REQUIRE(cpu.B() == 0x4f); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) != 0); + REQUIRE(cpu.cycles() == 2); + } + + // Test the subtraction instruction. + // IMMEDIATE mode: B=0x02 - 0x81 becomes 0x81 + // positive - negative = negative + overflow + SECTION("Immediate (SUBB) (SUBB2)") { + board.poke(0, 0xc0); + board.poke(1, 0x81); + cpu.CC() = 0; + cpu.B() = 2; + cpu.step(); + REQUIRE(cpu.B() == 0x81); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) != 0); + REQUIRE(cpu.cycles() == 2); + } + + // Example from Programming the 6809. + // 0x03 - 0x21 = 0xE2 + // positive - positive = negative + SECTION("Immediate (SUBB) (SUBBY)") { + board.poke(0, 0xe0); + board.poke(1, 0xa4); + board.poke(0x21, 0x21); + cpu.CC() = EightBit::mc6809::ZF; + cpu.B() = 3; + cpu.Y() = 0x21; + cpu.step(); + REQUIRE(cpu.B() == 0xe2); + REQUIRE((cpu.CC() & EightBit::mc6809::NF) != 0); + REQUIRE((cpu.CC() & EightBit::mc6809::ZF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::VF) == 0); + REQUIRE((cpu.CC() & EightBit::mc6809::CF) != 0); + REQUIRE(cpu.cycles() == 4); + } +}