From abe445d202ed19f5d3a6ded9babb2b0af36c9df8 Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Tue, 28 Apr 2020 23:12:43 +0200 Subject: [PATCH 01/12] Adding the BIT instruction --- sources/CPU/CPU.hpp | 12 ++--- sources/CPU/Instructions/BitsInstructions.cpp | 28 ++++++++++++ tests/CPU/testBits.cpp | 44 +++++++++++++++++++ 3 files changed, 79 insertions(+), 5 deletions(-) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index 2d72b91..1bbe317 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -419,6 +419,8 @@ namespace ComSquare::CPU int TSB(uint24_t, AddressingMode); //! @brief Exchange the B and A Accumulators int XBA(uint24_t, AddressingMode); + //! @brief Test Memory Bits against Accumulator + int BIT(uint24_t, AddressingMode); //! @brief All the instructions of the CPU. //! @info Instructions are indexed by their opcode @@ -459,7 +461,7 @@ namespace ComSquare::CPU {&CPU::AND, 6, "and", AddressingMode::DirectPageIndirectIndexedByX, 2}, // 21 {&CPU::JSL, 8, "jsl", AddressingMode::AbsoluteLong, 4}, // 22 {&CPU::AND, 4, "and", AddressingMode::StackRelative, 2}, // 23 - {&CPU::BRK, 7, "bit #-#", AddressingMode::Implied, 2}, // 24 + {&CPU::BIT, 3, "bit", AddressingMode::DirectPage, 2}, // 24 {&CPU::AND, 3, "and", AddressingMode::DirectPage, 2}, // 25 {&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 26 {&CPU::AND, 6, "and", AddressingMode::DirectPageIndirectLong, 2}, // 27 @@ -467,7 +469,7 @@ namespace ComSquare::CPU {&CPU::AND, 2, "and", AddressingMode::ImmediateForA, 2}, // 29 {&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 2A {&CPU::PLD, 5, "pld", AddressingMode::Implied, 1}, // 2B - {&CPU::BRK, 7, "bit #-#", AddressingMode::Implied, 2}, // 2C + {&CPU::BIT, 4, "bit", AddressingMode::Absolute, 3}, // 2C {&CPU::AND, 4, "and", AddressingMode::Absolute, 3}, // 2D {&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 2E {&CPU::AND, 5, "and", AddressingMode::AbsoluteLong, 4}, // 2F @@ -475,7 +477,7 @@ namespace ComSquare::CPU {&CPU::AND, 5, "and", AddressingMode::DirectPageIndirectIndexedByY, 2}, // 31 {&CPU::AND, 5, "and", AddressingMode::DirectPageIndirect, 2}, // 32 {&CPU::AND, 7, "and", AddressingMode::StackRelativeIndirectIndexedByY, 2}, // 33 - {&CPU::BRK, 7, "bit #-#", AddressingMode::Implied, 2}, // 34 + {&CPU::BIT, 4, "bit", AddressingMode::DirectPageIndexedByX, 2}, // 34 {&CPU::AND, 4, "and", AddressingMode::DirectPageIndexedByX, 2}, // 35 {&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 36 {&CPU::AND, 6, "and", AddressingMode::DirectPageIndirectIndexedByYLong, 2}, // 37 @@ -483,7 +485,7 @@ namespace ComSquare::CPU {&CPU::AND, 4, "and", AddressingMode::AbsoluteIndexedByY, 3}, // 39 {&CPU::DEC, 2, "dec", AddressingMode::Implied, 1}, // 3A {&CPU::TSC, 2, "tsc", AddressingMode::Implied, 1}, // 3B - {&CPU::BRK, 7, "bit #-#", AddressingMode::Implied, 2}, // 3C + {&CPU::BIT, 4, "bit", AddressingMode::AbsoluteIndexedByX, 3}, // 3C {&CPU::AND, 4, "and", AddressingMode::AbsoluteIndexedByX, 3}, // 3D {&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 3E {&CPU::AND, 5, "and", AddressingMode::AbsoluteIndexedByXLong, 4}, // 3F @@ -560,7 +562,7 @@ namespace ComSquare::CPU {&CPU::STX, 3, "stx", AddressingMode::DirectPage, 2}, // 86 {&CPU::STA, 6, "sta", AddressingMode::DirectPageIndirectLong, 2}, // 87 {&CPU::DEY, 2, "dey", AddressingMode::Implied, 1}, // 88 - {&CPU::BRK, 7, "bit #-#", AddressingMode::Implied, 2}, // 89 + {&CPU::BIT, 2, "bit", AddressingMode::ImmediateForA, 2}, // 89 {&CPU::TXA, 2, "txa", AddressingMode::Implied, 2}, // 8A {&CPU::PHB, 3, "phb", AddressingMode::Implied, 1}, // 8B {&CPU::STY, 4, "sty", AddressingMode::Absolute, 3}, // 8C diff --git a/sources/CPU/Instructions/BitsInstructions.cpp b/sources/CPU/Instructions/BitsInstructions.cpp index 737bdc9..41848f6 100644 --- a/sources/CPU/Instructions/BitsInstructions.cpp +++ b/sources/CPU/Instructions/BitsInstructions.cpp @@ -27,4 +27,32 @@ namespace ComSquare::CPU cycles += this->_registers.dl != 0; return cycles; } + + int CPU::BIT(uint24_t valueAddr, AddressingMode mode) + { + unsigned negativeMask = this->_registers.p.m ? 0x80u : 0x8000u; + unsigned value = this->_bus->read(valueAddr); + if (!this->_registers.p.m) + value += this->_bus->read(valueAddr + 1) << 8u; + + if (mode != ImmediateForA) { + this->_registers.p.n = value & negativeMask; + this->_registers.p.v = value & (negativeMask >> 1u); + } + this->_registers.p.z = (value & this->_registers.a) == 0; + + int cycles = !this->_registers.p.m; + switch (mode) { + case DirectPage: + case DirectPageIndexedByX: + cycles += this->_registers.dl != 0; + break; + case AbsoluteIndexedByX: + cycles += this->_hasIndexCrossedPageBoundary; + break; + default: + break; + } + return cycles; + } } \ No newline at end of file diff --git a/tests/CPU/testBits.cpp b/tests/CPU/testBits.cpp index 5c4b334..9e8e5dd 100644 --- a/tests/CPU/testBits.cpp +++ b/tests/CPU/testBits.cpp @@ -71,3 +71,47 @@ Test(TSB, nativeTest) cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); } +Test(BIT, immediate) +{ + Init() + snes.wram->_data[0] = 0xFF; + snes.wram->_data[1] = 0x00; + snes.cpu->_registers.p.m = false; + snes.cpu->_registers.a = 0x8008; + snes.cpu->_registers.p.v = false; + snes.cpu->_registers.p.n = false; + snes.cpu->BIT(0x0, ComSquare::CPU::AddressingMode::ImmediateForA); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); + cr_assert_eq(snes.cpu->_registers.p.v, false, "The overflow flag should not be set."); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set."); +} + +Test(BIT, immediateZero) +{ + Init() + snes.wram->_data[0] = 0x00; + snes.wram->_data[1] = 0xFF; + snes.cpu->_registers.p.m = false; + snes.cpu->_registers.a = 0x0008; + snes.cpu->_registers.p.v = true; + snes.cpu->_registers.p.n = true; + snes.cpu->BIT(0x0, ComSquare::CPU::AddressingMode::ImmediateForA); + cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flag should be set."); + cr_assert_eq(snes.cpu->_registers.p.v, true, "The overflow flag should be set."); + cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flag should be set."); +} + +Test(BIT, other) +{ + Init() + snes.wram->_data[0] = 0x00; + snes.wram->_data[1] = 0xFF; + snes.cpu->_registers.p.m = false; + snes.cpu->_registers.a = 0x8008; + snes.cpu->_registers.p.v = false; + snes.cpu->_registers.p.n = false; + snes.cpu->BIT(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); + cr_assert_eq(snes.cpu->_registers.p.v, true, "The overflow flag should be set."); + cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flag should be set."); +} \ No newline at end of file From 66f82dc5f0582c71db4be570ff2d4719e2ca2e1f Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Wed, 13 May 2020 14:11:58 +0200 Subject: [PATCH 02/12] Implementing ASL --- sources/CPU/CPU.hpp | 12 +++--- sources/CPU/Instructions/BitsInstructions.cpp | 40 +++++++++++++++++++ tests/CPU/testBits.cpp | 38 ++++++++++++++++++ 3 files changed, 85 insertions(+), 5 deletions(-) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index 1bbe317..a65944c 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -421,6 +421,8 @@ namespace ComSquare::CPU int XBA(uint24_t, AddressingMode); //! @brief Test Memory Bits against Accumulator int BIT(uint24_t, AddressingMode); + //! @brief Arithmetic Shift Left + int ASL(uint24_t, AddressingMode); //! @brief All the instructions of the CPU. //! @info Instructions are indexed by their opcode @@ -431,15 +433,15 @@ namespace ComSquare::CPU {&CPU::ORA, 4, "ora", AddressingMode::StackRelative, 2}, // 03 {&CPU::TSB, 5, "tsb", AddressingMode::DirectPage, 2}, // 04 {&CPU::ORA, 3, "ora", AddressingMode::DirectPage, 2}, // 05 - {&CPU::BRK, 7, "asl #-#", AddressingMode::Implied, 2}, // 06 + {&CPU::ASL, 5, "asl", AddressingMode::DirectPage, 2}, // 06 {&CPU::ORA, 6, "ora", AddressingMode::DirectPageIndirectLong, 2}, // 07 {&CPU::PHP, 3, "php", AddressingMode::Implied, 3}, // 08 {&CPU::ORA, 2, "ora", AddressingMode::ImmediateForA, 2}, // 09 - {&CPU::BRK, 7, "asl #-#", AddressingMode::Implied, 2}, // 0A + {&CPU::ASL, 2, "asl", AddressingMode::Implied, 1}, // 0A {&CPU::PHD, 4, "phd", AddressingMode::Implied, 1}, // 0B {&CPU::TSB, 6, "tsb", AddressingMode::Absolute, 3}, // 0C {&CPU::ORA, 3, "ora", AddressingMode::Absolute, 4}, // 0D - {&CPU::BRK, 7, "asl #-#", AddressingMode::Implied, 2}, // 0E + {&CPU::ASL, 6, "asl", AddressingMode::Absolute, 3}, // 0E {&CPU::ORA, 5, "ora", AddressingMode::AbsoluteLong, 5}, // 0F {&CPU::BPL, 7, "bpl", AddressingMode::Immediate8bits, 2}, // 10 {&CPU::ORA, 5, "ora", AddressingMode::DirectPageIndirectIndexedByY, 2}, // 11 @@ -447,7 +449,7 @@ namespace ComSquare::CPU {&CPU::ORA, 7, "ora", AddressingMode::StackRelativeIndirectIndexedByY, 2}, // 13 {&CPU::BRK, 7, "trb #-#", AddressingMode::Implied, 2}, // 14 {&CPU::ORA, 4, "ora", AddressingMode::DirectPageIndexedByX, 2}, // 15 - {&CPU::BRK, 7, "asl #-#", AddressingMode::Implied, 2}, // 16 + {&CPU::ASL, 6, "asl", AddressingMode::DirectPageIndexedByX, 2}, // 16 {&CPU::ORA, 6, "ora", AddressingMode::DirectPageIndirectIndexedByYLong, 2}, // 17 {&CPU::CLC, 2, "clc", AddressingMode::Implied, 1}, // 18 {&CPU::ORA, 4, "ora", AddressingMode::AbsoluteIndexedByY, 3}, // 19 @@ -455,7 +457,7 @@ namespace ComSquare::CPU {&CPU::TCS, 2, "tcs", AddressingMode::Implied, 1}, // 1B {&CPU::BRK, 7, "trb #-#", AddressingMode::Implied, 2}, // 1C {&CPU::ORA, 4, "ora", AddressingMode::AbsoluteIndexedByX, 3}, // 1D - {&CPU::BRK, 7, "asl #-#", AddressingMode::Implied, 2}, // 1E + {&CPU::ASL, 7, "asl", AddressingMode::AbsoluteIndexedByX, 3}, // 1E {&CPU::ORA, 5, "ora", AddressingMode::AbsoluteIndexedByXLong, 4}, // 1F {&CPU::JSR, 6, "jsr", AddressingMode::Absolute, 3}, // 20 {&CPU::AND, 6, "and", AddressingMode::DirectPageIndirectIndexedByX, 2}, // 21 diff --git a/sources/CPU/Instructions/BitsInstructions.cpp b/sources/CPU/Instructions/BitsInstructions.cpp index 41848f6..7ad9f83 100644 --- a/sources/CPU/Instructions/BitsInstructions.cpp +++ b/sources/CPU/Instructions/BitsInstructions.cpp @@ -55,4 +55,44 @@ namespace ComSquare::CPU } return cycles; } + + int CPU::ASL(uint24_t valueAddr, AddressingMode mode) + { + unsigned highByte = this->_registers.p.m ? 0x80u : 0x8000u; + + if (mode == Implied) { + this->_registers.a <<= 1u; + this->_registers.p.n = this->_registers.a & highByte; + this->_registers.p.z = this->_registers.a == 0; + return 0; + } + + uint16_t value = this->_bus->read(valueAddr); + if (!this->_registers.p.m) + value += this->_bus->read(valueAddr + 1) << 8u; + + this->_registers.p.c = value & highByte; + + value <<= 1u; + this->_bus->write(valueAddr, value); + if (!this->_registers.p.m) + this->_bus->write(valueAddr + 1, value >> 8u); + + this->_registers.p.n = value & highByte; + this->_registers.p.z = value == 0; + + int cycles = 2 * !this->_registers.p.m; + switch (mode) { + case DirectPage: + case DirectPageIndexedByX: + cycles += this->_registers.dl != 0; + break; + case AbsoluteIndexedByX: + cycles += this->_hasIndexCrossedPageBoundary; + break; + default: + break; + } + return cycles; + } } \ No newline at end of file diff --git a/tests/CPU/testBits.cpp b/tests/CPU/testBits.cpp index 9e8e5dd..1e94a0a 100644 --- a/tests/CPU/testBits.cpp +++ b/tests/CPU/testBits.cpp @@ -114,4 +114,42 @@ Test(BIT, other) cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); cr_assert_eq(snes.cpu->_registers.p.v, true, "The overflow flag should be set."); cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flag should be set."); +} + +Test(ASL, emulationTest) +{ + Init() + snes.wram->_data[0] = 0b10110011; + snes.cpu->_registers.p.m = true; + snes.cpu->ASL(0x0, ComSquare::CPU::AddressingMode::Absolute); + cr_assert_eq(snes.wram->_data[0], 0b01100110, "The data in ram should be 0b01100110 but it was %x", snes.wram->_data[0]); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); + cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flag should be set."); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set."); +} + +Test(ASL, nativeTest) +{ + Init() + snes.wram->_data[0] = 0b10110011; + snes.wram->_data[1] = 0b10000011; + snes.cpu->_registers.p.m = false; + snes.cpu->ASL(0x0, ComSquare::CPU::AddressingMode::Absolute); + cr_assert_eq(snes.wram->_data[0], 0b01100110, "The data in ram should be 0b01100110 but it was %x", snes.wram->_data[0]); + cr_assert_eq(snes.wram->_data[1], 0b00000111, "The data in ram should be 0b00000111 but it was %x", snes.wram->_data[1]); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); + cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flag should be set."); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set."); +} + +Test(ASL, accumulator) +{ + Init() + snes.cpu->_registers.a = 0b10110011; + snes.cpu->_registers.p.m = true; + snes.cpu->ASL(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.al, 0b01100110, "The accumulator should be 0b01100110 but it was %x", snes.cpu->_registers.al); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); + cr_assert_eq(snes.cpu->_registers.p.c, false, "The carry flag should not be set."); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set."); } \ No newline at end of file From 475986580d2b737877e0b8df1fa9dbdc4675f7f0 Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Wed, 13 May 2020 14:39:46 +0200 Subject: [PATCH 03/12] Implementing LSR --- sources/CPU/CPU.hpp | 12 +++--- sources/CPU/Instructions/BitsInstructions.cpp | 42 ++++++++++++++++++- tests/CPU/testBits.cpp | 38 +++++++++++++++++ 3 files changed, 85 insertions(+), 7 deletions(-) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index a65944c..a4c69be 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -423,6 +423,8 @@ namespace ComSquare::CPU int BIT(uint24_t, AddressingMode); //! @brief Arithmetic Shift Left int ASL(uint24_t, AddressingMode); + // !@brief Logical Shift Right + int LSR(uint24_t, AddressingMode); //! @brief All the instructions of the CPU. //! @info Instructions are indexed by their opcode @@ -497,15 +499,15 @@ namespace ComSquare::CPU {&CPU::EOR, 4, "eor", AddressingMode::StackRelative, 2}, // 43 {&CPU::BRK, 7, "mvp #-#", AddressingMode::Implied, 2}, // 44 {&CPU::EOR, 3, "eor", AddressingMode::DirectPage, 2}, // 45 - {&CPU::BRK, 7, "lsr #-#", AddressingMode::Implied, 2}, // 46 + {&CPU::LSR, 5, "lsr", AddressingMode::DirectPage, 2}, // 46 {&CPU::EOR, 6, "eor", AddressingMode::DirectPageIndirectLong, 2}, // 47 {&CPU::PHA, 3, "pha", AddressingMode::Implied, 1}, // 48 {&CPU::EOR, 2, "eor", AddressingMode::ImmediateForA, 2}, // 49 - {&CPU::BRK, 7, "lsr #-#", AddressingMode::Implied, 2}, // 4A + {&CPU::LSR, 2, "lsr", AddressingMode::Implied, 1}, // 4A {&CPU::PHK, 3, "phk", AddressingMode::Implied, 1}, // 4B {&CPU::JMP, 3, "jmp", AddressingMode::Absolute, 3}, // 4C {&CPU::EOR, 4, "eor", AddressingMode::Absolute, 3}, // 4D - {&CPU::BRK, 7, "lsr #-#", AddressingMode::Implied, 2}, // 4E + {&CPU::LSR, 6, "lsr", AddressingMode::Absolute, 3}, // 4E {&CPU::EOR, 5, "eor", AddressingMode::AbsoluteLong, 4}, // 4F {&CPU::BVC, 2, "bvc", AddressingMode::Immediate8bits, 2}, // 50 {&CPU::EOR, 5, "eor", AddressingMode::DirectPageIndirectIndexedByY, 2}, // 51 @@ -513,7 +515,7 @@ namespace ComSquare::CPU {&CPU::EOR, 4, "eor", AddressingMode::StackRelativeIndirectIndexedByY, 2}, // 53 {&CPU::BRK, 7, "mvn #-#", AddressingMode::Implied, 2}, // 54 {&CPU::EOR, 4, "eor", AddressingMode::DirectPageIndexedByX, 2}, // 55 - {&CPU::BRK, 7, "lsr #-#", AddressingMode::Implied, 2}, // 56 + {&CPU::LSR, 6, "lsr", AddressingMode::DirectPageIndexedByX, 2}, // 56 {&CPU::EOR, 6, "eor", AddressingMode::DirectPageIndirectIndexedByYLong, 2}, // 57 {&CPU::CLI, 2, "cli", AddressingMode::Implied, 1}, // 58 {&CPU::EOR, 4, "eor", AddressingMode::AbsoluteIndexedByY, 3}, // 59 @@ -521,7 +523,7 @@ namespace ComSquare::CPU {&CPU::TCD, 2, "tcd", AddressingMode::Implied, 1}, // 5B {&CPU::JML, 4, "jml", AddressingMode::Implied, 4}, // 5C {&CPU::EOR, 4, "eor", AddressingMode::AbsoluteIndexedByX, 3}, // 5D - {&CPU::BRK, 7, "lsr #-#", AddressingMode::Implied, 2}, // 5E + {&CPU::LSR, 7, "lsr", AddressingMode::AbsoluteIndexedByX, 3}, // 5E {&CPU::EOR, 5, "eor", AddressingMode::AbsoluteIndexedByXLong, 4}, // 5F {&CPU::RTL, 6, "rtl", AddressingMode::Implied, 1}, // 60 {&CPU::ADC, 6, "adc", AddressingMode::DirectPageIndirectIndexedByX, 2}, // 61 diff --git a/sources/CPU/Instructions/BitsInstructions.cpp b/sources/CPU/Instructions/BitsInstructions.cpp index 7ad9f83..629655e 100644 --- a/sources/CPU/Instructions/BitsInstructions.cpp +++ b/sources/CPU/Instructions/BitsInstructions.cpp @@ -61,6 +61,7 @@ namespace ComSquare::CPU unsigned highByte = this->_registers.p.m ? 0x80u : 0x8000u; if (mode == Implied) { + this->_registers.p.c = this->_registers.a & highByte; this->_registers.a <<= 1u; this->_registers.p.n = this->_registers.a & highByte; this->_registers.p.z = this->_registers.a == 0; @@ -72,15 +73,52 @@ namespace ComSquare::CPU value += this->_bus->read(valueAddr + 1) << 8u; this->_registers.p.c = value & highByte; - value <<= 1u; + this->_registers.p.n = value & highByte; + this->_registers.p.z = value == 0; + this->_bus->write(valueAddr, value); if (!this->_registers.p.m) this->_bus->write(valueAddr + 1, value >> 8u); - this->_registers.p.n = value & highByte; + int cycles = 2 * !this->_registers.p.m; + switch (mode) { + case DirectPage: + case DirectPageIndexedByX: + cycles += this->_registers.dl != 0; + break; + case AbsoluteIndexedByX: + cycles += this->_hasIndexCrossedPageBoundary; + break; + default: + break; + } + return cycles; + } + + int CPU::LSR(uint24_t valueAddr, AddressingMode mode) + { + this->_registers.p.n = false; + + if (mode == Implied) { + this->_registers.p.c = this->_registers.a & 1u; + this->_registers.a >>= 1u; + this->_registers.p.z = this->_registers.a == 0; + return 0; + } + + uint16_t value = this->_bus->read(valueAddr); + if (!this->_registers.p.m) + value += this->_bus->read(valueAddr + 1) << 8u; + + this->_registers.p.c = value & 1u; + value >>= 1u; this->_registers.p.z = value == 0; + this->_bus->write(valueAddr, value); + if (!this->_registers.p.m) + this->_bus->write(valueAddr + 1, value >> 8u); + int cycles = 2 * !this->_registers.p.m; switch (mode) { case DirectPage: diff --git a/tests/CPU/testBits.cpp b/tests/CPU/testBits.cpp index 1e94a0a..c21757a 100644 --- a/tests/CPU/testBits.cpp +++ b/tests/CPU/testBits.cpp @@ -150,6 +150,44 @@ Test(ASL, accumulator) snes.cpu->ASL(0x0, ComSquare::CPU::AddressingMode::Implied); cr_assert_eq(snes.cpu->_registers.al, 0b01100110, "The accumulator should be 0b01100110 but it was %x", snes.cpu->_registers.al); cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); + cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flag should be set."); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set."); +} + +Test(LSR, emulationTest) +{ + Init() + snes.wram->_data[0] = 0b01100110; + snes.cpu->_registers.p.m = true; + snes.cpu->LSR(0x0, ComSquare::CPU::AddressingMode::Absolute); + cr_assert_eq(snes.wram->_data[0], 0b00110011, "The data in ram should be 0b00110011 but it was %x", snes.wram->_data[0]); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); cr_assert_eq(snes.cpu->_registers.p.c, false, "The carry flag should not be set."); cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set."); +} + +Test(LSR, nativeTest) +{ + Init() + snes.wram->_data[0] = 0b10110011; + snes.wram->_data[1] = 0b10000011; + snes.cpu->_registers.p.m = false; + snes.cpu->LSR(0x0, ComSquare::CPU::AddressingMode::Absolute); + cr_assert_eq(snes.wram->_data[0], 0b11011001, "The data in ram should be 0b11011001 but it was %x", snes.wram->_data[0]); + cr_assert_eq(snes.wram->_data[1], 0b01000001, "The data in ram should be 0b01000001 but it was %x", snes.wram->_data[1]); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); + cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flag should be set."); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set."); +} + +Test(LSR, accumulator) +{ + Init() + snes.cpu->_registers.a = 0b10110011; + snes.cpu->_registers.p.m = true; + snes.cpu->LSR(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.al, 0b01011001, "The accumulator should be 0b01011001 but it was %x", snes.cpu->_registers.al); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); + cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flag should be set."); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set."); } \ No newline at end of file From deab98dac03f342cd7e92bef8f8f9301709d0492 Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Wed, 13 May 2020 14:58:37 +0200 Subject: [PATCH 04/12] Implementing ROL --- sources/CPU/CPU.hpp | 12 +++-- sources/CPU/Instructions/BitsInstructions.cpp | 43 +++++++++++++++ tests/CPU/testBits.cpp | 53 +++++++++++++++++++ 3 files changed, 103 insertions(+), 5 deletions(-) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index a4c69be..92cf17e 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -425,6 +425,8 @@ namespace ComSquare::CPU int ASL(uint24_t, AddressingMode); // !@brief Logical Shift Right int LSR(uint24_t, AddressingMode); + // !@brief Rotate Left + int ROL(uint24_t, AddressingMode); //! @brief All the instructions of the CPU. //! @info Instructions are indexed by their opcode @@ -467,15 +469,15 @@ namespace ComSquare::CPU {&CPU::AND, 4, "and", AddressingMode::StackRelative, 2}, // 23 {&CPU::BIT, 3, "bit", AddressingMode::DirectPage, 2}, // 24 {&CPU::AND, 3, "and", AddressingMode::DirectPage, 2}, // 25 - {&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 26 + {&CPU::ROL, 5, "rol", AddressingMode::DirectPage, 2}, // 26 {&CPU::AND, 6, "and", AddressingMode::DirectPageIndirectLong, 2}, // 27 {&CPU::PLP, 4, "plp", AddressingMode::Implied, 1}, // 28 {&CPU::AND, 2, "and", AddressingMode::ImmediateForA, 2}, // 29 - {&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 2A + {&CPU::ROL, 2, "rol", AddressingMode::Implied, 1}, // 2A {&CPU::PLD, 5, "pld", AddressingMode::Implied, 1}, // 2B {&CPU::BIT, 4, "bit", AddressingMode::Absolute, 3}, // 2C {&CPU::AND, 4, "and", AddressingMode::Absolute, 3}, // 2D - {&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 2E + {&CPU::ROL, 6, "rol", AddressingMode::Absolute, 3}, // 2E {&CPU::AND, 5, "and", AddressingMode::AbsoluteLong, 4}, // 2F {&CPU::BMI, 2, "bmi", AddressingMode::Immediate8bits, 2}, // 30 {&CPU::AND, 5, "and", AddressingMode::DirectPageIndirectIndexedByY, 2}, // 31 @@ -483,7 +485,7 @@ namespace ComSquare::CPU {&CPU::AND, 7, "and", AddressingMode::StackRelativeIndirectIndexedByY, 2}, // 33 {&CPU::BIT, 4, "bit", AddressingMode::DirectPageIndexedByX, 2}, // 34 {&CPU::AND, 4, "and", AddressingMode::DirectPageIndexedByX, 2}, // 35 - {&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 36 + {&CPU::ROL, 6, "rol", AddressingMode::DirectPageIndexedByX, 2}, // 36 {&CPU::AND, 6, "and", AddressingMode::DirectPageIndirectIndexedByYLong, 2}, // 37 {&CPU::SEC, 2, "sec", AddressingMode::Implied, 1}, // 38 {&CPU::AND, 4, "and", AddressingMode::AbsoluteIndexedByY, 3}, // 39 @@ -491,7 +493,7 @@ namespace ComSquare::CPU {&CPU::TSC, 2, "tsc", AddressingMode::Implied, 1}, // 3B {&CPU::BIT, 4, "bit", AddressingMode::AbsoluteIndexedByX, 3}, // 3C {&CPU::AND, 4, "and", AddressingMode::AbsoluteIndexedByX, 3}, // 3D - {&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 3E + {&CPU::ROL, 7, "rol", AddressingMode::AbsoluteIndexedByX, 3}, // 3E {&CPU::AND, 5, "and", AddressingMode::AbsoluteIndexedByXLong, 4}, // 3F {&CPU::RTI, 6, "rti", AddressingMode::Implied, 1}, // 40 {&CPU::EOR, 6, "eor", AddressingMode::DirectPageIndirectIndexedByX, 2}, // 41 diff --git a/sources/CPU/Instructions/BitsInstructions.cpp b/sources/CPU/Instructions/BitsInstructions.cpp index 629655e..db67ab0 100644 --- a/sources/CPU/Instructions/BitsInstructions.cpp +++ b/sources/CPU/Instructions/BitsInstructions.cpp @@ -133,4 +133,47 @@ namespace ComSquare::CPU } return cycles; } + + int CPU::ROL(uint24_t valueAddr, AddressingMode mode) + { + unsigned highByte = this->_registers.p.m ? 0x80u : 0x8000u; + bool oldCarry = this->_registers.p.c; + + if (mode == Implied) { + this->_registers.p.c = this->_registers.a & highByte; + this->_registers.a <<= 1u; + this->_registers.a |= oldCarry; + this->_registers.p.n = this->_registers.a & highByte; + this->_registers.p.z = this->_registers.a == 0; + return 0; + } + + uint16_t value = this->_bus->read(valueAddr); + if (!this->_registers.p.m) + value += this->_bus->read(valueAddr + 1) << 8u; + + this->_registers.p.c = value & highByte; + value <<= 1u; + value |= oldCarry; + this->_registers.p.n = value & highByte; + this->_registers.p.z = value == 0; + + this->_bus->write(valueAddr, value); + if (!this->_registers.p.m) + this->_bus->write(valueAddr + 1, value >> 8u); + + int cycles = 2 * !this->_registers.p.m; + switch (mode) { + case DirectPage: + case DirectPageIndexedByX: + cycles += this->_registers.dl != 0; + break; + case AbsoluteIndexedByX: + cycles += this->_hasIndexCrossedPageBoundary; + break; + default: + break; + } + return cycles; + } } \ No newline at end of file diff --git a/tests/CPU/testBits.cpp b/tests/CPU/testBits.cpp index c21757a..ae59ee6 100644 --- a/tests/CPU/testBits.cpp +++ b/tests/CPU/testBits.cpp @@ -190,4 +190,57 @@ Test(LSR, accumulator) cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flag should be set."); cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set."); +} + +Test(ROL, emulationTest) +{ + Init() + snes.wram->_data[0] = 0b10110011; + snes.cpu->_registers.p.m = true; + snes.cpu->ROL(0x0, ComSquare::CPU::AddressingMode::Absolute); + cr_assert_eq(snes.wram->_data[0], 0b01100110, "The data in ram should be 0b01100110 but it was %x", snes.wram->_data[0]); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); + cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flag should be set."); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set."); +} + +Test(ROL, emulationTestWithCarry) +{ + Init() + snes.wram->_data[0] = 0b10110011; + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.p.c = true; + snes.cpu->ROL(0x0, ComSquare::CPU::AddressingMode::Absolute); + cr_assert_eq(snes.wram->_data[0], 0b01100111, "The data in ram should be 0b01100111 but it was %x", snes.wram->_data[0]); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); + cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flag should be set."); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set."); +} + +Test(ROL, nativeTest) +{ + Init() + snes.wram->_data[0] = 0b10110011; + snes.wram->_data[1] = 0b10000011; + snes.cpu->_registers.p.m = false; + snes.cpu->_registers.p.c = true; + snes.cpu->ROL(0x0, ComSquare::CPU::AddressingMode::Absolute); + cr_assert_eq(snes.wram->_data[0], 0b01100111, "The data in ram should be 0b01100111 but it was %x", snes.wram->_data[0]); + cr_assert_eq(snes.wram->_data[1], 0b00000111, "The data in ram should be 0b00000111 but it was %x", snes.wram->_data[1]); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); + cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flag should be set."); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set."); +} + +Test(ROL, accumulator) +{ + Init() + snes.cpu->_registers.a = 0b10110011; + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.p.c = true; + snes.cpu->ROL(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.al, 0b01100111, "The accumulator should be 0b01100111 but it was %x", snes.cpu->_registers.al); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); + cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flag should be set."); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set."); } \ No newline at end of file From 4c4cc6b655a6a25f344e3e3c834dbb9ed57f16cb Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Wed, 13 May 2020 16:35:13 +0200 Subject: [PATCH 05/12] Implementing ROR --- sources/CPU/CPU.hpp | 12 +++-- sources/CPU/Instructions/BitsInstructions.cpp | 42 +++++++++++++++ tests/CPU/testBits.cpp | 54 +++++++++++++++++++ 3 files changed, 103 insertions(+), 5 deletions(-) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index 92cf17e..b87704e 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -427,6 +427,8 @@ namespace ComSquare::CPU int LSR(uint24_t, AddressingMode); // !@brief Rotate Left int ROL(uint24_t, AddressingMode); + // !@brief Rotate Right + int ROR(uint24_t, AddressingMode); //! @brief All the instructions of the CPU. //! @info Instructions are indexed by their opcode @@ -533,15 +535,15 @@ namespace ComSquare::CPU {&CPU::ADC, 4, "adc", AddressingMode::StackRelative, 2}, // 63 {&CPU::STZ, 3, "stz", AddressingMode::DirectPage, 2}, // 64 {&CPU::ADC, 3, "adc", AddressingMode::DirectPage, 2}, // 65 - {&CPU::BRK, 7, "ror #-#", AddressingMode::Implied, 2}, // 66 + {&CPU::ROR, 5, "ror", AddressingMode::DirectPage, 2}, // 66 {&CPU::ADC, 6, "adc", AddressingMode::DirectPageIndirectLong, 2}, // 67 {&CPU::PLA, 4, "pla", AddressingMode::Implied, 1}, // 68 {&CPU::ADC, 2, "adc", AddressingMode::ImmediateForA, 2}, // 69 - {&CPU::BRK, 7, "ror #-#", AddressingMode::Implied, 2}, // 6A + {&CPU::ROR, 2, "ror", AddressingMode::Implied, 1}, // 6A {&CPU::RTS, 6, "rts", AddressingMode::Implied, 1}, // 6B {&CPU::JMP, 5, "jmp", AddressingMode::AbsoluteIndirect, 3}, // 6C {&CPU::ADC, 4, "adc", AddressingMode::Absolute, 3}, // 6D - {&CPU::BRK, 7, "ror #-#", AddressingMode::Implied, 2}, // 6E + {&CPU::ROR, 6, "ror", AddressingMode::Absolute, 3}, // 6E {&CPU::ADC, 5, "adc", AddressingMode::AbsoluteLong, 4}, // 6F {&CPU::BVS, 2, "bvs", AddressingMode::Immediate8bits, 2}, // 70 {&CPU::ADC, 5, "adc", AddressingMode::DirectPageIndirectIndexedByY, 2}, // 71 @@ -549,7 +551,7 @@ namespace ComSquare::CPU {&CPU::ADC, 7, "adc", AddressingMode::StackRelativeIndirectIndexedByY, 2}, // 73 {&CPU::STZ, 4, "stz", AddressingMode::DirectPageIndexedByX, 2}, // 74 {&CPU::ADC, 4, "adc", AddressingMode::DirectPageIndexedByX, 2}, // 75 - {&CPU::BRK, 7, "ror #-#", AddressingMode::Implied, 2}, // 76 + {&CPU::ROR, 6, "ror", AddressingMode::DirectPageIndexedByX, 2}, // 76 {&CPU::ADC, 6, "adc", AddressingMode::DirectPageIndirectIndexedByY, 2}, // 77 {&CPU::SEI, 2, "sei", AddressingMode::Implied, 1}, // 78 {&CPU::ADC, 4, "adc", AddressingMode::AbsoluteIndexedByY, 2}, // 79 @@ -557,7 +559,7 @@ namespace ComSquare::CPU {&CPU::TDC, 2, "tdc", AddressingMode::Implied, 1}, // 7B {&CPU::JMP, 6, "jmp", AddressingMode::AbsoluteIndirectIndexedByX, 3}, // 7C {&CPU::ADC, 4, "adc", AddressingMode::AbsoluteIndexedByX, 3}, // 7D - {&CPU::BRK, 7, "ror #-#", AddressingMode::Implied, 2}, // 7E + {&CPU::ROR, 7, "ror", AddressingMode::AbsoluteIndexedByX, 3}, // 7E {&CPU::ADC, 5, "adc", AddressingMode::AbsoluteIndexedByXLong, 4}, // 7F {&CPU::BRA, 3, "bra", AddressingMode::Immediate8bits, 2}, // 80 {&CPU::STA, 6, "sta", AddressingMode::DirectPageIndexedByX, 2}, // 81 diff --git a/sources/CPU/Instructions/BitsInstructions.cpp b/sources/CPU/Instructions/BitsInstructions.cpp index db67ab0..81205b7 100644 --- a/sources/CPU/Instructions/BitsInstructions.cpp +++ b/sources/CPU/Instructions/BitsInstructions.cpp @@ -176,4 +176,46 @@ namespace ComSquare::CPU } return cycles; } + + int CPU::ROR(uint24_t valueAddr, AddressingMode mode) + { + this->_registers.p.n = false; + bool oldCarry = this->_registers.p.c; + unsigned highByteIndex = this->_registers.p.m ? 7 : 15; + + if (mode == Implied) { + this->_registers.p.c = this->_registers.a & 1u; + this->_registers.a >>= 1u; + this->_registers.a |= oldCarry << highByteIndex; + this->_registers.p.z = this->_registers.a == 0; + return 0; + } + + uint16_t value = this->_bus->read(valueAddr); + if (!this->_registers.p.m) + value += this->_bus->read(valueAddr + 1) << 8u; + + this->_registers.p.c = value & 1u; + value >>= 1u; + value |= oldCarry << highByteIndex; + this->_registers.p.z = value == 0; + + this->_bus->write(valueAddr, value); + if (!this->_registers.p.m) + this->_bus->write(valueAddr + 1, value >> 8u); + + int cycles = 2 * !this->_registers.p.m; + switch (mode) { + case DirectPage: + case DirectPageIndexedByX: + cycles += this->_registers.dl != 0; + break; + case AbsoluteIndexedByX: + cycles += this->_hasIndexCrossedPageBoundary; + break; + default: + break; + } + return cycles; + } } \ No newline at end of file diff --git a/tests/CPU/testBits.cpp b/tests/CPU/testBits.cpp index ae59ee6..b8ef723 100644 --- a/tests/CPU/testBits.cpp +++ b/tests/CPU/testBits.cpp @@ -243,4 +243,58 @@ Test(ROL, accumulator) cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flag should be set."); cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set."); +} + +Test(ROR, emulationTestWithtoutCarry) +{ + Init() + snes.wram->_data[0] = 0b01100110; + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.p.c = false; + snes.cpu->ROR(0x0, ComSquare::CPU::AddressingMode::Absolute); + cr_assert_eq(snes.wram->_data[0], 0b00110011, "The data in ram should be 0b00110011 but it was %x", snes.wram->_data[0]); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); + cr_assert_eq(snes.cpu->_registers.p.c, false, "The carry flag should not be set."); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set."); +} + +Test(ROR, emulationTest) +{ + Init() + snes.wram->_data[0] = 0b01100110; + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.p.c = true; + snes.cpu->ROR(0x0, ComSquare::CPU::AddressingMode::Absolute); + cr_assert_eq(snes.wram->_data[0], 0b10110011, "The data in ram should be 0b10110011 but it was %x", snes.wram->_data[0]); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); + cr_assert_eq(snes.cpu->_registers.p.c, false, "The carry flag should not be set."); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set."); +} + +Test(ROR, nativeTest) +{ + Init() + snes.wram->_data[0] = 0b10110011; + snes.wram->_data[1] = 0b10000011; + snes.cpu->_registers.p.m = false; + snes.cpu->_registers.p.c = true; + snes.cpu->ROR(0x0, ComSquare::CPU::AddressingMode::Absolute); + cr_assert_eq(snes.wram->_data[0], 0b11011001, "The data in ram should be 0b11011001 but it was %x", snes.wram->_data[0]); + cr_assert_eq(snes.wram->_data[1], 0b11000001, "The data in ram should be 0b11000001 but it was %x", snes.wram->_data[1]); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); + cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flag should be set."); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set."); +} + +Test(ROR, accumulator) +{ + Init() + snes.cpu->_registers.a = 0b10110011; + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.p.c = true; + snes.cpu->ROR(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.al, 0b11011001, "The accumulator should be 0b11011001 but it was %x", snes.cpu->_registers.al); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); + cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flag should be set."); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set."); } \ No newline at end of file From b29d1c4216751bbcd9e7ad69f13a53d2e227584a Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Wed, 13 May 2020 17:04:14 +0200 Subject: [PATCH 06/12] Implementing TRB --- sources/CPU/CPU.hpp | 6 ++-- sources/CPU/Instructions/BitsInstructions.cpp | 21 +++++++++++ tests/CPU/testBits.cpp | 35 +++++++++++++++++++ 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index b87704e..1c6be7d 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -417,6 +417,8 @@ namespace ComSquare::CPU int TYX(uint24_t, AddressingMode); //! @brief Test and Set Memory Bits Against Accumulator int TSB(uint24_t, AddressingMode); + //! @brief Test and Reset Memory Bits Against Accumulator + int TRB(uint24_t, AddressingMode); //! @brief Exchange the B and A Accumulators int XBA(uint24_t, AddressingMode); //! @brief Test Memory Bits against Accumulator @@ -453,7 +455,7 @@ namespace ComSquare::CPU {&CPU::ORA, 5, "ora", AddressingMode::DirectPageIndirectIndexedByY, 2}, // 11 {&CPU::ORA, 5, "ora", AddressingMode::DirectPageIndirect, 2}, // 12 {&CPU::ORA, 7, "ora", AddressingMode::StackRelativeIndirectIndexedByY, 2}, // 13 - {&CPU::BRK, 7, "trb #-#", AddressingMode::Implied, 2}, // 14 + {&CPU::TRB, 5, "trb", AddressingMode::DirectPage, 2}, // 14 {&CPU::ORA, 4, "ora", AddressingMode::DirectPageIndexedByX, 2}, // 15 {&CPU::ASL, 6, "asl", AddressingMode::DirectPageIndexedByX, 2}, // 16 {&CPU::ORA, 6, "ora", AddressingMode::DirectPageIndirectIndexedByYLong, 2}, // 17 @@ -461,7 +463,7 @@ namespace ComSquare::CPU {&CPU::ORA, 4, "ora", AddressingMode::AbsoluteIndexedByY, 3}, // 19 {&CPU::INC, 2, "inc", AddressingMode::Implied, 1}, // 1A {&CPU::TCS, 2, "tcs", AddressingMode::Implied, 1}, // 1B - {&CPU::BRK, 7, "trb #-#", AddressingMode::Implied, 2}, // 1C + {&CPU::TRB, 6, "trb", AddressingMode::Absolute, 3}, // 1C {&CPU::ORA, 4, "ora", AddressingMode::AbsoluteIndexedByX, 3}, // 1D {&CPU::ASL, 7, "asl", AddressingMode::AbsoluteIndexedByX, 3}, // 1E {&CPU::ORA, 5, "ora", AddressingMode::AbsoluteIndexedByXLong, 4}, // 1F diff --git a/sources/CPU/Instructions/BitsInstructions.cpp b/sources/CPU/Instructions/BitsInstructions.cpp index 81205b7..2332114 100644 --- a/sources/CPU/Instructions/BitsInstructions.cpp +++ b/sources/CPU/Instructions/BitsInstructions.cpp @@ -28,6 +28,27 @@ namespace ComSquare::CPU return cycles; } + int CPU::TRB(uint24_t valueAddr, AddressingMode mode) + { + uint16_t value = this->_bus->read(valueAddr); + if (!this->_registers.p.m) + value += this->_bus->read(valueAddr + 1) << 8u; + + uint16_t newValue = value & ~this->_registers.a; + this->_bus->write(valueAddr, newValue); + if (!this->_registers.p.m) + this->_bus->write(valueAddr + 1, newValue >> 8u); + + this->_registers.p.z = (value & this->_registers.a) == 0; + + int cycles = 0; + if (!this->_registers.p.m) + cycles += 2; + if (mode == DirectPage) + cycles += this->_registers.dl != 0; + return cycles; + } + int CPU::BIT(uint24_t valueAddr, AddressingMode mode) { unsigned negativeMask = this->_registers.p.m ? 0x80u : 0x8000u; diff --git a/tests/CPU/testBits.cpp b/tests/CPU/testBits.cpp index b8ef723..a4bd5de 100644 --- a/tests/CPU/testBits.cpp +++ b/tests/CPU/testBits.cpp @@ -297,4 +297,39 @@ Test(ROR, accumulator) cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flag should be set."); cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set."); +} + +Test(TRB, emulationTest) +{ + Init() + snes.wram->_data[0] = 0xFF; + snes.cpu->_registers.a = 0b00110111; + snes.cpu->_registers.p.m = true; + snes.cpu->TRB(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.wram->_data[0], 0b11001000, "The data in ram should be 0b11001000 but it was %x", snes.wram->_data[0]); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); +} + +Test(TRB, nativeTest) +{ + Init() + snes.wram->_data[0] = 0xF0; + snes.wram->_data[1] = 0x0F; + snes.cpu->_registers.a = 0x0F0F; + snes.cpu->_registers.p.m = false; + snes.cpu->TRB(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.wram->_data[0], 0xF0, "The first data in ram should be 0xF0 but it was %x", snes.wram->_data[0]); + cr_assert_eq(snes.wram->_data[1], 0x00, "The second data in ram should be 0x00 but it was %x", snes.wram->_data[1]); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); +} + +Test(TRB, zero) +{ + Init() + snes.wram->_data[0] = 0xFF; + snes.cpu->_registers.a = 0b0; + snes.cpu->_registers.p.m = true; + snes.cpu->TRB(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.wram->_data[0], 0xFF, "The data in ram should be 0xFF but it was %x", snes.wram->_data[0]); + cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flag should be set."); } \ No newline at end of file From 876b78a5c9ca3c75fc2f8a5a8fb5ce688703bf78 Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Wed, 13 May 2020 17:52:47 +0200 Subject: [PATCH 07/12] Implementing PER --- sources/CPU/AddressingModes.cpp | 7 +++++++ sources/CPU/CPU.cpp | 6 ++---- sources/CPU/CPU.hpp | 10 +++++++--- sources/CPU/Instruction.hpp | 1 + sources/CPU/Instructions/InternalInstruction.cpp | 9 +++++++++ tests/CPU/testInternal.cpp | 13 +++++++++++++ 6 files changed, 39 insertions(+), 7 deletions(-) diff --git a/sources/CPU/AddressingModes.cpp b/sources/CPU/AddressingModes.cpp index d19a985..3f649c2 100644 --- a/sources/CPU/AddressingModes.cpp +++ b/sources/CPU/AddressingModes.cpp @@ -14,6 +14,13 @@ namespace ComSquare::CPU return ret; } + uint24_t CPU::_getImmediateAddr16Bits() + { + uint24_t ret = this->_registers.pac; + this->_registers.pc += 2; + return ret; + } + uint24_t CPU::_getImmediateAddrForA() { uint24_t effective = this->_registers.pac; diff --git a/sources/CPU/CPU.cpp b/sources/CPU/CPU.cpp index 8116408..34a01ae 100644 --- a/sources/CPU/CPU.cpp +++ b/sources/CPU/CPU.cpp @@ -6,9 +6,7 @@ #include #include -#include "../Exceptions/NotImplementedException.hpp" #include "../Exceptions/InvalidAddress.hpp" -#include "../Exceptions/InvalidOpcode.hpp" namespace ComSquare::CPU { @@ -218,6 +216,8 @@ namespace ComSquare::CPU return 0; case Immediate8bits: return this->_getImmediateAddr8Bits(); + case Immediate16bits: + return this->_getImmediateAddr16Bits(); case ImmediateForA: return this->_getImmediateAddrForA(); case ImmediateForX: @@ -264,8 +264,6 @@ namespace ComSquare::CPU case AbsoluteIndirectIndexedByX: return this->_getAbsoluteIndirectIndexedByXAddr(); - default: - return 0; } } diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index 1c6be7d..d0b0d47 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -198,8 +198,10 @@ namespace ComSquare::CPU //! @brief True if an addressing mode with an iterator (x, y) has crossed the page. (Used because crossing the page boundary take one more cycle to run certain instructions). bool _hasIndexCrossedPageBoundary = false; - //! @brief Immediate address mode is specified with a value in 8. (This functions returns the 24bit space address of the value). + //! @brief Immediate address mode is specified with a value in 8 bits. (This functions returns the 24bit space address of the value). uint24_t _getImmediateAddr8Bits(); + //! @brief Immediate address mode is specified with a value in 16 bits. (This functions returns the 24bit space address of the value). + uint24_t _getImmediateAddr16Bits(); //! @brief Immediate address mode is specified with a value in 8 or 16 bits. The value is 16 bits if the m flag is unset. (This functions returns the 24bit space address of the value). uint24_t _getImmediateAddrForA(); //! @brief Immediate address mode is specified with a value in 8 or 16 bits. The value is 16 bits if the x flag is unset. (This functions returns the 24bit space address of the value). @@ -431,6 +433,8 @@ namespace ComSquare::CPU int ROL(uint24_t, AddressingMode); // !@brief Rotate Right int ROR(uint24_t, AddressingMode); + //! @brief Push Effective PC Relative Indirect Address + int PER(uint24_t, AddressingMode); //! @brief All the instructions of the CPU. //! @info Instructions are indexed by their opcode @@ -533,7 +537,7 @@ namespace ComSquare::CPU {&CPU::EOR, 5, "eor", AddressingMode::AbsoluteIndexedByXLong, 4}, // 5F {&CPU::RTL, 6, "rtl", AddressingMode::Implied, 1}, // 60 {&CPU::ADC, 6, "adc", AddressingMode::DirectPageIndirectIndexedByX, 2}, // 61 - {&CPU::BRK, 7, "per #-#", AddressingMode::Implied, 2}, // 62 + {&CPU::PER, 6, "per", AddressingMode::Immediate16bits, 3}, // 62 {&CPU::ADC, 4, "adc", AddressingMode::StackRelative, 2}, // 63 {&CPU::STZ, 3, "stz", AddressingMode::DirectPage, 2}, // 64 {&CPU::ADC, 3, "adc", AddressingMode::DirectPage, 2}, // 65 @@ -606,7 +610,7 @@ namespace ComSquare::CPU {&CPU::TAY, 2, "tay", AddressingMode::Implied, 1}, // A8 {&CPU::LDA, 2, "lda", AddressingMode::ImmediateForA, 2}, // A9 {&CPU::TAX, 2, "tax", AddressingMode::Implied, 1}, // AA - {&CPU::BRK, 7, "trb #-#", AddressingMode::Implied, 2}, // AB + {&CPU::BRK, 7, "plb #-#", AddressingMode::Implied, 2}, // AB {&CPU::LDY, 4, "ldy", AddressingMode::Absolute, 4}, // AC {&CPU::LDA, 4, "lda", AddressingMode::Absolute, 3}, // AD {&CPU::LDX, 4, "ldx", AddressingMode::Absolute, 3}, // AE diff --git a/sources/CPU/Instruction.hpp b/sources/CPU/Instruction.hpp index 8061d1e..f6ef811 100644 --- a/sources/CPU/Instruction.hpp +++ b/sources/CPU/Instruction.hpp @@ -17,6 +17,7 @@ namespace ComSquare::CPU Implied, Immediate8bits, + Immediate16bits, ImmediateForA, ImmediateForX, diff --git a/sources/CPU/Instructions/InternalInstruction.cpp b/sources/CPU/Instructions/InternalInstruction.cpp index 1fc020a..d3ee972 100644 --- a/sources/CPU/Instructions/InternalInstruction.cpp +++ b/sources/CPU/Instructions/InternalInstruction.cpp @@ -184,6 +184,15 @@ namespace ComSquare::CPU return !this->_registers.p.x_b; } + int CPU::PER(uint24_t valueAddr, AddressingMode) + { + uint16_t value = this->_bus->read(valueAddr); + value += this->_bus->read(valueAddr + 1) << 8u; + value += this->_registers.pc; + this->_push(value); + return 0; + } + int CPU::XCE(uint24_t, AddressingMode) { bool oldCarry = this->_registers.p.c; diff --git a/tests/CPU/testInternal.cpp b/tests/CPU/testInternal.cpp index 0619ece..6710426 100644 --- a/tests/CPU/testInternal.cpp +++ b/tests/CPU/testInternal.cpp @@ -914,4 +914,17 @@ Test(JML, simpleJump) snes.cpu->_registers.pc = 0x8000; snes.cpu->JML(0x10AB00, ComSquare::CPU::AddressingMode::Implied); cr_assert_eq(snes.cpu->_registers.pac, 0x10AB00, "The program counter should be equal to 0x10AB00 but it was 0x%x.", snes.cpu->_registers.pac); +} + +Test(PER, simple) +{ + Init() + snes.cpu->_registers.pac = 0x008005; + snes.cpu->_registers.s = 0x1FFF; + snes.wram->_data[0x0] = 0xFF; + snes.wram->_data[0x1] = 0xFF; + snes.cpu->PER(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.s, 0x1FFD, "The stack pointer should be equal to 0x1FFD but it was 0x%x.", snes.cpu->_registers.s); + uint16_t value = snes.cpu->_pop16(); + cr_assert_eq(value, 0x8004, "The pushed value should be equal to 0x8004 but it was 0x%x.", value); } \ No newline at end of file From fea20fee703fc86ce4a17a1deaa6cf54bdbcbe2c Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Wed, 13 May 2020 18:10:56 +0200 Subject: [PATCH 08/12] Implemnting PEI --- sources/CPU/CPU.cpp | 2 ++ sources/CPU/CPU.hpp | 4 +++- sources/CPU/Instructions/InternalInstruction.cpp | 6 ++++++ sources/Exceptions/InvalidOpcode.hpp | 5 +++++ tests/CPU/testInternal.cpp | 12 ++++++++++++ 5 files changed, 28 insertions(+), 1 deletion(-) diff --git a/sources/CPU/CPU.cpp b/sources/CPU/CPU.cpp index 34a01ae..7e3ba69 100644 --- a/sources/CPU/CPU.cpp +++ b/sources/CPU/CPU.cpp @@ -7,6 +7,7 @@ #include #include #include "../Exceptions/InvalidAddress.hpp" +#include "../Exceptions/InvalidOpcode.hpp" namespace ComSquare::CPU { @@ -265,6 +266,7 @@ namespace ComSquare::CPU case AbsoluteIndirectIndexedByX: return this->_getAbsoluteIndirectIndexedByXAddr(); } + throw InvalidOpcode("Unknown addressing mode for."); } unsigned CPU::_executeInstruction(uint8_t opcode) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index d0b0d47..364fc81 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -435,6 +435,8 @@ namespace ComSquare::CPU int ROR(uint24_t, AddressingMode); //! @brief Push Effective PC Relative Indirect Address int PER(uint24_t, AddressingMode); + //! @brief Push Effective Indirect Address + int PEI(uint24_t, AddressingMode); //! @brief All the instructions of the CPU. //! @info Instructions are indexed by their opcode @@ -651,7 +653,7 @@ namespace ComSquare::CPU {&CPU::CMP, 5, "cmp", AddressingMode::DirectPageIndirectIndexedByY, 2}, // D1 {&CPU::CMP, 5, "cmp", AddressingMode::DirectPageIndirect, 2}, // D2 {&CPU::CMP, 7, "cmp", AddressingMode::StackRelativeIndirectIndexedByY, 2}, // D3 - {&CPU::BRK, 7, "pei #-#", AddressingMode::Implied, 2}, // D4 + {&CPU::PEI, 6, "pei", AddressingMode::DirectPage, 2}, // D4 {&CPU::CMP, 4, "cmp", AddressingMode::DirectPageIndexedByX, 2}, // D5 {&CPU::DEC, 6, "dec", AddressingMode::DirectPageIndexedByX, 2}, // D6 {&CPU::CMP, 6, "cmp", AddressingMode::DirectPageIndirectIndexedByYLong, 2}, // D7 diff --git a/sources/CPU/Instructions/InternalInstruction.cpp b/sources/CPU/Instructions/InternalInstruction.cpp index d3ee972..0f01df9 100644 --- a/sources/CPU/Instructions/InternalInstruction.cpp +++ b/sources/CPU/Instructions/InternalInstruction.cpp @@ -193,6 +193,12 @@ namespace ComSquare::CPU return 0; } + int CPU::PEI(uint24_t value, AddressingMode) + { + this->_push(static_cast(value)); + return 0; + } + int CPU::XCE(uint24_t, AddressingMode) { bool oldCarry = this->_registers.p.c; diff --git a/sources/Exceptions/InvalidOpcode.hpp b/sources/Exceptions/InvalidOpcode.hpp index 57e990d..b4b1ae7 100644 --- a/sources/Exceptions/InvalidOpcode.hpp +++ b/sources/Exceptions/InvalidOpcode.hpp @@ -17,6 +17,11 @@ namespace ComSquare private: std::string _msg; public: + explicit InvalidOpcode(const std::string &what) + { + this->_msg = what; + } + explicit InvalidOpcode(const std::string &pu, unsigned opcode) { std::stringstream stream; diff --git a/tests/CPU/testInternal.cpp b/tests/CPU/testInternal.cpp index 6710426..3eabc41 100644 --- a/tests/CPU/testInternal.cpp +++ b/tests/CPU/testInternal.cpp @@ -927,4 +927,16 @@ Test(PER, simple) cr_assert_eq(snes.cpu->_registers.s, 0x1FFD, "The stack pointer should be equal to 0x1FFD but it was 0x%x.", snes.cpu->_registers.s); uint16_t value = snes.cpu->_pop16(); cr_assert_eq(value, 0x8004, "The pushed value should be equal to 0x8004 but it was 0x%x.", value); +} + +Test(PEI, simple) +{ + Init() + snes.cpu->_registers.s = 0x1FFF; + snes.wram->_data[0x0] = 0xFF; + snes.wram->_data[0x1] = 0xFF; + snes.cpu->PER(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.s, 0x1FFD, "The stack pointer should be equal to 0x1FFD but it was 0x%x.", snes.cpu->_registers.s); + uint16_t value = snes.cpu->_pop16(); + cr_assert_eq(value, 0xFFFF, "The pushed value should be equal to 0xFFFF but it was 0x%x.", value); } \ No newline at end of file From 051d1d7596a006ed71e79aa62b55bf7e594c45fc Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Wed, 13 May 2020 18:19:21 +0200 Subject: [PATCH 09/12] Adding PEA --- sources/CPU/CPU.hpp | 4 +++- sources/CPU/Instructions/InternalInstruction.cpp | 6 ++++++ tests/CPU/testInternal.cpp | 12 +++++++++++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index 364fc81..a4746a3 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -437,6 +437,8 @@ namespace ComSquare::CPU int PER(uint24_t, AddressingMode); //! @brief Push Effective Indirect Address int PEI(uint24_t, AddressingMode); + //! @brief Push Effective Absolute Address + int PEA(uint24_t, AddressingMode); //! @brief All the instructions of the CPU. //! @info Instructions are indexed by their opcode @@ -685,7 +687,7 @@ namespace ComSquare::CPU {&CPU::SBC, 5, "sbc", AddressingMode::DirectPageIndirectIndexedByY, 2}, // F1 {&CPU::SBC, 5, "sbc", AddressingMode::DirectPageIndirect, 2}, // F2 {&CPU::SBC, 7, "sbc", AddressingMode::StackRelativeIndirectIndexedByY, 2}, // F3 - {&CPU::BRK, 7, "pea #-#", AddressingMode::Implied, 2}, // F4 + {&CPU::PEA, 5, "pea", AddressingMode::Immediate16bits, 3}, // F4 {&CPU::SBC, 4, "sbc", AddressingMode::DirectPageIndexedByX, 2}, // F5 {&CPU::INC, 6, "inc", AddressingMode::DirectPageIndexedByX, 2}, // F6 {&CPU::SBC, 6, "sbc", AddressingMode::DirectPageIndirectIndexedByYLong, 2}, // F7 diff --git a/sources/CPU/Instructions/InternalInstruction.cpp b/sources/CPU/Instructions/InternalInstruction.cpp index 0f01df9..bb06b87 100644 --- a/sources/CPU/Instructions/InternalInstruction.cpp +++ b/sources/CPU/Instructions/InternalInstruction.cpp @@ -193,6 +193,12 @@ namespace ComSquare::CPU return 0; } + int CPU::PEA(uint24_t value, AddressingMode) + { + this->_push(static_cast(value)); + return 0; + } + int CPU::PEI(uint24_t value, AddressingMode) { this->_push(static_cast(value)); diff --git a/tests/CPU/testInternal.cpp b/tests/CPU/testInternal.cpp index 3eabc41..63f6f5c 100644 --- a/tests/CPU/testInternal.cpp +++ b/tests/CPU/testInternal.cpp @@ -930,12 +930,22 @@ Test(PER, simple) } Test(PEI, simple) +{ + Init() + snes.cpu->_registers.s = 0x1FFF; + snes.cpu->PEI(0xFFFF, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.s, 0x1FFD, "The stack pointer should be equal to 0x1FFD but it was 0x%x.", snes.cpu->_registers.s); + uint16_t value = snes.cpu->_pop16(); + cr_assert_eq(value, 0xFFFF, "The pushed value should be equal to 0xFFFF but it was 0x%x.", value); +} + +Test(PEA, simple) { Init() snes.cpu->_registers.s = 0x1FFF; snes.wram->_data[0x0] = 0xFF; snes.wram->_data[0x1] = 0xFF; - snes.cpu->PER(0x0, ComSquare::CPU::AddressingMode::Implied); + snes.cpu->PEA(0xFFFF, ComSquare::CPU::AddressingMode::Implied); cr_assert_eq(snes.cpu->_registers.s, 0x1FFD, "The stack pointer should be equal to 0x1FFD but it was 0x%x.", snes.cpu->_registers.s); uint16_t value = snes.cpu->_pop16(); cr_assert_eq(value, 0xFFFF, "The pushed value should be equal to 0xFFFF but it was 0x%x.", value); From 7fe28a8bcc75ba62dcb2d33f3d6216e3680dbb21 Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Wed, 13 May 2020 19:14:53 +0200 Subject: [PATCH 10/12] Implemnting the STP --- sources/CPU/CPU.cpp | 2 ++ sources/CPU/CPU.hpp | 6 +++++- sources/CPU/Instructions/InternalInstruction.cpp | 6 ++++++ sources/CPU/Instructions/Interrupts.cpp | 1 + 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/sources/CPU/CPU.cpp b/sources/CPU/CPU.cpp index 7e3ba69..4f2a727 100644 --- a/sources/CPU/CPU.cpp +++ b/sources/CPU/CPU.cpp @@ -205,6 +205,8 @@ namespace ComSquare::CPU { unsigned cycles = 0; + if (this->_isStopped) + return 0xFF; for (int i = 0; i < 0xFF; i++) cycles += this->_executeInstruction(this->readPC()); return cycles; diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index a4746a3..1baa090 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -188,6 +188,8 @@ namespace ComSquare::CPU Registers _registers{}; //! @brief Is the CPU running in emulation mode (in 8bits) bool _isEmulationMode = true; + //! @brief If the processor is stopped (using an STP instruction), the clock is stopped and no instruction will be run until a manual reset. + bool _isStopped = false; //! @brief Internal registers of the CPU (accessible from the bus via addr $4200 to $421F). InternalRegisters _internalRegisters{}; //! @brief The memory bus to use for read/write. @@ -439,6 +441,8 @@ namespace ComSquare::CPU int PEI(uint24_t, AddressingMode); //! @brief Push Effective Absolute Address int PEA(uint24_t, AddressingMode); + //! @brief Stop the processor + int STP(uint24_t, AddressingMode); //! @brief All the instructions of the CPU. //! @info Instructions are indexed by their opcode @@ -662,7 +666,7 @@ namespace ComSquare::CPU {&CPU::CLD, 2, "cld", AddressingMode::Implied, 2}, // D8 {&CPU::CMP, 4, "cmp", AddressingMode::AbsoluteIndexedByY, 3}, // D9 {&CPU::PHX, 3, "phx", AddressingMode::Implied, 1}, // DA - {&CPU::BRK, 7, "stp #-#", AddressingMode::Implied, 2}, // DB + {&CPU::STP, 3, "stp", AddressingMode::Implied, 1}, // DB {&CPU::JML, 7, "jml", AddressingMode::AbsoluteIndirectLong, 2}, // DC {&CPU::CMP, 4, "cmp", AddressingMode::AbsoluteIndexedByX, 3}, // DD {&CPU::DEC, 7, "dec", AddressingMode::AbsoluteIndexedByX, 3}, // DE diff --git a/sources/CPU/Instructions/InternalInstruction.cpp b/sources/CPU/Instructions/InternalInstruction.cpp index bb06b87..59eddbc 100644 --- a/sources/CPU/Instructions/InternalInstruction.cpp +++ b/sources/CPU/Instructions/InternalInstruction.cpp @@ -320,4 +320,10 @@ namespace ComSquare::CPU this->_registers.dbr = this->_pop(); return 0; } + + int CPU::STP(uint24_t, AddressingMode) + { + this->_isStopped = true; + return 0; + } } \ No newline at end of file diff --git a/sources/CPU/Instructions/Interrupts.cpp b/sources/CPU/Instructions/Interrupts.cpp index 2e09647..8f8d515 100644 --- a/sources/CPU/Instructions/Interrupts.cpp +++ b/sources/CPU/Instructions/Interrupts.cpp @@ -18,6 +18,7 @@ namespace ComSquare::CPU this->_registers.d = 0x0000; this->_registers.sh = 0x01; // the low bit of the stack pointer is undefined on reset. this->_registers.pc = this->_cartridgeHeader.emulationInterrupts.reset; + this->_isStopped = false; return 0; } From d0e4caf12e35185feaadeded54233025cef1a1e4 Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Wed, 13 May 2020 23:12:31 +0200 Subject: [PATCH 11/12] Implementing MVN --- CMakeLists.txt | 2 +- sources/CPU/CPU.hpp | 13 ++++++++++--- .../CPU/Instructions/InternalInstruction.cpp | 5 +++++ .../CPU/Instructions/TransferRegisters.cpp | 18 ++++++++++++++++++ ...ransferRegisters.cpp => testTransfers.cpp} | 19 +++++++++++++++++++ 5 files changed, 53 insertions(+), 4 deletions(-) rename tests/CPU/{TransferRegisters.cpp => testTransfers.cpp} (94%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 18c98a8..302d68a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,7 +90,7 @@ add_executable(unit_tests sources/APU/IPL/IPL.cpp tests/CPU/Math/testSBC.cpp sources/CPU/Instructions/TransferRegisters.cpp - tests/CPU/TransferRegisters.cpp + tests/CPU/testTransfers.cpp sources/CPU/AddressingModes.cpp sources/Models/Components.hpp sources/CPU/Instruction.hpp diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index 1baa090..39e483f 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -443,6 +443,13 @@ namespace ComSquare::CPU int PEA(uint24_t, AddressingMode); //! @brief Stop the processor int STP(uint24_t, AddressingMode); + //! @brief WDM Reserved for Future Expansion (used as a code breakpoint) + int WDM(uint24_t, AddressingMode); + //! @brief Block Move Next. This instruction is special: it takes parameter in the registers + //! @param X_register Source address + //! @param Y_register Destination address + //! @param C_register (16 bits accumulator) Length -1 + int MVN(uint24_t, AddressingMode); //! @brief All the instructions of the CPU. //! @info Instructions are indexed by their opcode @@ -513,7 +520,7 @@ namespace ComSquare::CPU {&CPU::AND, 5, "and", AddressingMode::AbsoluteIndexedByXLong, 4}, // 3F {&CPU::RTI, 6, "rti", AddressingMode::Implied, 1}, // 40 {&CPU::EOR, 6, "eor", AddressingMode::DirectPageIndirectIndexedByX, 2}, // 41 - {&CPU::BRK, 7, "wdm #-#", AddressingMode::Implied, 2}, // 42 + {&CPU::WDM, 2, "wdm", AddressingMode::Immediate8bits, 2}, // 42 {&CPU::EOR, 4, "eor", AddressingMode::StackRelative, 2}, // 43 {&CPU::BRK, 7, "mvp #-#", AddressingMode::Implied, 2}, // 44 {&CPU::EOR, 3, "eor", AddressingMode::DirectPage, 2}, // 45 @@ -531,7 +538,7 @@ namespace ComSquare::CPU {&CPU::EOR, 5, "eor", AddressingMode::DirectPageIndirectIndexedByY, 2}, // 51 {&CPU::EOR, 5, "eor", AddressingMode::DirectPageIndirect, 2}, // 52 {&CPU::EOR, 4, "eor", AddressingMode::StackRelativeIndirectIndexedByY, 2}, // 53 - {&CPU::BRK, 7, "mvn #-#", AddressingMode::Implied, 2}, // 54 + {&CPU::MVN, 0, "mvn", AddressingMode::Immediate16bits, 2}, // 54 {&CPU::EOR, 4, "eor", AddressingMode::DirectPageIndexedByX, 2}, // 55 {&CPU::LSR, 6, "lsr", AddressingMode::DirectPageIndexedByX, 2}, // 56 {&CPU::EOR, 6, "eor", AddressingMode::DirectPageIndirectIndexedByYLong, 2}, // 57 @@ -618,7 +625,7 @@ namespace ComSquare::CPU {&CPU::TAY, 2, "tay", AddressingMode::Implied, 1}, // A8 {&CPU::LDA, 2, "lda", AddressingMode::ImmediateForA, 2}, // A9 {&CPU::TAX, 2, "tax", AddressingMode::Implied, 1}, // AA - {&CPU::BRK, 7, "plb #-#", AddressingMode::Implied, 2}, // AB + {&CPU::PLB, 4, "plb", AddressingMode::Implied, 1}, // AB {&CPU::LDY, 4, "ldy", AddressingMode::Absolute, 4}, // AC {&CPU::LDA, 4, "lda", AddressingMode::Absolute, 3}, // AD {&CPU::LDX, 4, "ldx", AddressingMode::Absolute, 3}, // AE diff --git a/sources/CPU/Instructions/InternalInstruction.cpp b/sources/CPU/Instructions/InternalInstruction.cpp index 59eddbc..8430522 100644 --- a/sources/CPU/Instructions/InternalInstruction.cpp +++ b/sources/CPU/Instructions/InternalInstruction.cpp @@ -326,4 +326,9 @@ namespace ComSquare::CPU this->_isStopped = true; return 0; } + + int CPU::WDM(uint24_t, AddressingMode) + { + return 0; + } } \ No newline at end of file diff --git a/sources/CPU/Instructions/TransferRegisters.cpp b/sources/CPU/Instructions/TransferRegisters.cpp index 18efae8..64821e3 100644 --- a/sources/CPU/Instructions/TransferRegisters.cpp +++ b/sources/CPU/Instructions/TransferRegisters.cpp @@ -2,6 +2,7 @@ // Created by anonymus-raccoon on 2/28/20. // +#include #include "../CPU.hpp" namespace ComSquare::CPU @@ -150,4 +151,21 @@ namespace ComSquare::CPU this->_registers.p.z = this->_registers.y == 0; return 0; } + + int CPU::MVN(uint24_t params, AddressingMode) + { + uint8_t srcBank = params; + uint8_t destBank = params >> 8u; + int length = this->_registers.a + 1; + + this->_registers.dbr = destBank; + while (this->_registers.a != 0xFFFF) { + uint8_t data = this->_bus->read(srcBank << 24u | this->_registers.x); + this->_bus->write(destBank << 24u | this->_registers.y, data); + this->_registers.x++; + this->_registers.y++; + this->_registers.a--; + } + return 7 * length; + } } \ No newline at end of file diff --git a/tests/CPU/TransferRegisters.cpp b/tests/CPU/testTransfers.cpp similarity index 94% rename from tests/CPU/TransferRegisters.cpp rename to tests/CPU/testTransfers.cpp index 91ab304..6550cc5 100644 --- a/tests/CPU/TransferRegisters.cpp +++ b/tests/CPU/testTransfers.cpp @@ -435,4 +435,23 @@ Test(TYX, nativeMode) cr_assert_eq(snes.cpu->_registers.x, 0xAB0D, "The x index should be 0xAB0D but it was %x", snes.cpu->_registers.x); cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flag should be set."); cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should be not set."); +} + +Test(MVN, hardCase) +{ + Init() + snes.cpu->_registers.a = 0x10; + snes.cpu->_registers.x = 0x0000; + snes.cpu->_registers.y = 0x1000; + for (int i = 0; i <= snes.cpu->_registers.a; i++) + snes.wram->_data[i] = i; + + int cycles = snes.cpu->MVN(0x2010, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(cycles, 0x77, "The MVN should take 0x77 cycles but it took %x.", cycles); + cr_assert_eq(snes.cpu->_registers.dbr, 0x20, "The data bank register should be 0x20 but it was %x", snes.cpu->_registers.dbr); + cr_assert_eq(snes.cpu->_registers.a, 0xFFFF, "The c accumulator should be 0xFFFF but it was %x", snes.cpu->_registers.a); + cr_assert_eq(snes.cpu->_registers.x, 0x0011, "The x index should be 0x0011 but it was %x", snes.cpu->_registers.x); + cr_assert_eq(snes.cpu->_registers.y, 0x1011, "The y index should be 0x1011 but it was %x", snes.cpu->_registers.y); + for (int i = 0; i < 0x11; i++) + cr_assert_eq(snes.wram->_data[i + 0x1000], i, "The data in ram should be %x but it was %x", i, snes.wram->_data[i + 0x1000]); } \ No newline at end of file From 8240fbd54c4808393a6b4272acbae822962d2c2d Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Thu, 14 May 2020 01:06:29 +0200 Subject: [PATCH 12/12] Implementing MVP --- sources/CPU/CPU.hpp | 7 ++++++- .../CPU/Instructions/TransferRegisters.cpp | 17 +++++++++++++++++ tests/CPU/testTransfers.cpp | 19 +++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index 39e483f..2b95861 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -450,6 +450,11 @@ namespace ComSquare::CPU //! @param Y_register Destination address //! @param C_register (16 bits accumulator) Length -1 int MVN(uint24_t, AddressingMode); + //! @brief Block Move Previous. This instruction is special: it takes parameter in the registers + //! @param X_register Source address (last byte) + //! @param Y_register Destination address (last byte) + //! @param C_register (16 bits accumulator) Length -1 + int MVP(uint24_t, AddressingMode); //! @brief All the instructions of the CPU. //! @info Instructions are indexed by their opcode @@ -522,7 +527,7 @@ namespace ComSquare::CPU {&CPU::EOR, 6, "eor", AddressingMode::DirectPageIndirectIndexedByX, 2}, // 41 {&CPU::WDM, 2, "wdm", AddressingMode::Immediate8bits, 2}, // 42 {&CPU::EOR, 4, "eor", AddressingMode::StackRelative, 2}, // 43 - {&CPU::BRK, 7, "mvp #-#", AddressingMode::Implied, 2}, // 44 + {&CPU::MVP, 0, "mvp", AddressingMode::Immediate16bits, 3}, // 44 {&CPU::EOR, 3, "eor", AddressingMode::DirectPage, 2}, // 45 {&CPU::LSR, 5, "lsr", AddressingMode::DirectPage, 2}, // 46 {&CPU::EOR, 6, "eor", AddressingMode::DirectPageIndirectLong, 2}, // 47 diff --git a/sources/CPU/Instructions/TransferRegisters.cpp b/sources/CPU/Instructions/TransferRegisters.cpp index 64821e3..496a5d6 100644 --- a/sources/CPU/Instructions/TransferRegisters.cpp +++ b/sources/CPU/Instructions/TransferRegisters.cpp @@ -168,4 +168,21 @@ namespace ComSquare::CPU } return 7 * length; } + + int CPU::MVP(uint24_t params, AddressingMode) + { + uint8_t srcBank = params; + uint8_t destBank = params >> 8u; + int length = this->_registers.a + 1; + + this->_registers.dbr = destBank; + while (this->_registers.a != 0xFFFF) { + uint8_t data = this->_bus->read(srcBank << 24u | this->_registers.x); + this->_bus->write(destBank << 24u | this->_registers.y, data); + this->_registers.x--; + this->_registers.y--; + this->_registers.a--; + } + return 7 * length; + } } \ No newline at end of file diff --git a/tests/CPU/testTransfers.cpp b/tests/CPU/testTransfers.cpp index 6550cc5..87497b6 100644 --- a/tests/CPU/testTransfers.cpp +++ b/tests/CPU/testTransfers.cpp @@ -454,4 +454,23 @@ Test(MVN, hardCase) cr_assert_eq(snes.cpu->_registers.y, 0x1011, "The y index should be 0x1011 but it was %x", snes.cpu->_registers.y); for (int i = 0; i < 0x11; i++) cr_assert_eq(snes.wram->_data[i + 0x1000], i, "The data in ram should be %x but it was %x", i, snes.wram->_data[i + 0x1000]); +} + +Test(MVP, hardCase) +{ + Init() + snes.cpu->_registers.a = 0x10; + snes.cpu->_registers.x = 0x0010; + snes.cpu->_registers.y = 0x1010; + for (int i = 0; i <= snes.cpu->_registers.a; i++) + snes.wram->_data[i] = i; + + int cycles = snes.cpu->MVP(0x2010, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(cycles, 0x77, "The MVN should take 0x77 cycles but it took %x.", cycles); + cr_assert_eq(snes.cpu->_registers.dbr, 0x20, "The data bank register should be 0x20 but it was %x", snes.cpu->_registers.dbr); + cr_assert_eq(snes.cpu->_registers.a, 0xFFFF, "The c accumulator should be 0xFFFF but it was %x", snes.cpu->_registers.a); + cr_assert_eq(snes.cpu->_registers.x, 0xFFFF, "The x index should be 0xFFFF but it was %x", snes.cpu->_registers.x); + cr_assert_eq(snes.cpu->_registers.y, 0x0FFF, "The y index should be 0x0FFF but it was %x", snes.cpu->_registers.y); + for (int i = 0; i < 0x11; i++) + cr_assert_eq(snes.wram->_data[i + 0x1000], i, "The data in ram should be %x but it was %x", i, snes.wram->_data[i + 0x1000]); } \ No newline at end of file