Add lots of tests, mainly covering addition and subtraction. Shows quite a few problems...

Signed-off-by: Adrian Conlon <Adrian.conlon@gmail.com>
This commit is contained in:
Adrian Conlon 2018-10-14 10:06:47 +01:00
parent 7e527ff093
commit 12dc90c064

View File

@ -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 twos 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 twos 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);
}
}