diff --git a/sources/CPU/CPU.cpp b/sources/CPU/CPU.cpp index d7b8492..3c438e1 100644 --- a/sources/CPU/CPU.cpp +++ b/sources/CPU/CPU.cpp @@ -281,6 +281,19 @@ namespace ComSquare::CPU case Instructions::REP: this->REP(this->_getImmediateAddr()); return 3; + case Instructions::PHA: this->PHA(); return 3 + !this->_registers.p.m; + case Instructions::PHB: this->PHB(); return 3; + case Instructions::PHD: this->PHD(); return 4; + case Instructions::PHK: this->PHK(); return 3; + case Instructions::PHP: this->PHP(); return 3; + case Instructions::PHX: this->PHX(); return 3 + !this->_registers.p.x_b; + case Instructions::PHY: this->PHY(); return 3 + !this->_registers.p.x_b; + + case Instructions::JSR_ABS: this->JSR(this->_getAbsoluteAddr()); return 6; + case Instructions::JSR_ABSXi: this->JSR(this->_getAbsoluteIndirectIndexedByXAddr()); return 8; + + case Instructions::JSL: this->JSR(this->_getAbsoluteLongAddr()); return 8; + default: throw InvalidOpcode("CPU", opcode); } @@ -293,18 +306,18 @@ namespace ComSquare::CPU void CPU::_push(uint16_t data) { + this->_bus->write(this->_registers.s--, data >> 8u); this->_bus->write(this->_registers.s--, data); - this->_bus->write(this->_registers.s--, data << 8u); } uint8_t CPU::_pop() { - return this->_bus->read(this->_registers.s++); + return this->_bus->read(++this->_registers.s); } uint16_t CPU::_pop16() { - return this->_bus->read(this->_registers.s++) + (this->_bus->read(this->_registers.s++) << 8u); + return this->_bus->read(++this->_registers.s) + (this->_bus->read(++this->_registers.s) << 8u); } //////////////////////////////////////////////////////////////////// @@ -347,7 +360,7 @@ namespace ComSquare::CPU uint24_t base = this->_bus->read(dp); base += this->_bus->read(dp + 1) << 8u; base += this->_registers.dbr << 16u; - if ((base & 0xF0000000u) == (((base + this->_registers.y) & 0xF0000000u))) + if ((base & 0x80000000u) == (((base + this->_registers.y) & 0x80000000u))) this->_hasIndexCrossedPageBoundary = true; return base + this->_registers.y; } @@ -390,7 +403,7 @@ namespace ComSquare::CPU uint16_t abs = this->_bus->read(this->_registers.pac++); abs += this->_bus->read(this->_registers.pac++) << 8u; uint24_t effective = abs + (this->_registers.dbr << 16u); - if ((effective & 0xF0000000u) == (((effective + this->_registers.x) & 0xF0000000u))) + if ((effective & 0x80000000u) == (((effective + this->_registers.x) & 0x80000000u))) this->_hasIndexCrossedPageBoundary = true; return effective + this->_registers.x; } @@ -400,7 +413,7 @@ namespace ComSquare::CPU uint16_t abs = this->_bus->read(this->_registers.pac++); abs += this->_bus->read(this->_registers.pac++) << 8u; uint24_t effective = abs + (this->_registers.dbr << 16u); - if ((effective & 0xF0000000u) == (((effective + this->_registers.y) & 0xF0000000u))) + if ((effective & 0x80000000u) == (((effective + this->_registers.y) & 0x80000000u))) this->_hasIndexCrossedPageBoundary = true; return effective + this->_registers.y; } @@ -438,7 +451,7 @@ namespace ComSquare::CPU return effective; } - uint24_t CPU::_getAbsoluteIndexedIndirectAddr() + uint24_t CPU::_getAbsoluteIndirectIndexedByXAddr() { uint24_t abs = this->_bus->read(this->_registers.pac++); abs += this->_bus->read(this->_registers.pac++) << 8u; diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index f094de5..4953bb4 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -34,8 +34,6 @@ namespace ComSquare::CPU }; union { struct { - //! @brief The Program Bank Register; - uint8_t pbr; //! @brief The Program Counter; union { struct { @@ -44,6 +42,8 @@ namespace ComSquare::CPU }; uint16_t pc; }; + //! @brief The Program Bank Register; + uint8_t pbr; }; //! @brief The current Program Address Counter (does not exist in a snes but is useful here). uint24_t pac; @@ -262,7 +262,19 @@ namespace ComSquare::CPU SEP = 0xE2, - REP = 0xC2 + REP = 0xC2, + + PHA = 0x48, + PHB = 0x8B, + PHD = 0x0B, + PHK = 0x4B, + PHP = 0x08, + PHX = 0xDA, + PHY = 0x5A, + + JSR_ABS = 0x20, + JSR_ABSXi = 0xFC, + JSL = 0x22 }; //! @brief The main CPU @@ -313,7 +325,7 @@ namespace ComSquare::CPU //! @brief 2 bytes are pulled from the to form the effective address. uint24_t _getAbsoluteIndirectAddr(); //! @brief The is added with X, then 2 bytes are pulled from that address to form the new location. - uint24_t _getAbsoluteIndexedIndirectAddr(); + uint24_t _getAbsoluteIndirectIndexedByXAddr(); //! @brief 2 bytes are pulled from the direct page address to form the 16-bit address. It is combined with DBR to form a 24-bit effective address. uint24_t _getDirectIndirectAddr(); //! @brief 3 bytes are pulled from the direct page address to form an effective address. @@ -365,6 +377,38 @@ namespace ComSquare::CPU void SEP(uint24_t valueAddr); //! @brief Reset status bits. void REP(uint24_t valueAddr); + //! @brief Jump to subroutine + void JSR(uint24_t addr); + //! @brief Jump to subroutine (long) + void JSL(uint24_t addr); + //! @brief Push the accumulator to the stack. + void PHA(); + //! @brief Push the data bank register to the stack. + void PHB(); + //! @brief Push the direct page register to the stack. + void PHD(); + //! @brief Push the program bank register to the stack. + void PHK(); + //! @brief Push the processor status register to the stack. + void PHP(); + //! @brief Push the x index register to the stack. + void PHX(); + //! @brief Push the y index register to the stack. + void PHY(); + //! @brief Pull the accumulator to the stack. + void PLA(); + //! @brief Pull the data bank register to the stack. + void PLB(); + //! @brief Pull the direct page register to the stack. + void PLD(); + //! @brief Pull the program bank register to the stack. + void PLK(); + //! @brief Pull the processor status register to the stack. + void PLP(); + //! @brief Pull the x index register to the stack. + void PLX(); + //! @brief Pull the y index register to the stack. + void PLY(); public: explicit CPU(std::shared_ptr bus, Cartridge::Header &cartridgeHeader); CPU(const CPU &) = default; diff --git a/sources/CPU/Instructions/InternalInstruction.cpp b/sources/CPU/Instructions/InternalInstruction.cpp index 652b82e..b2ba2fc 100644 --- a/sources/CPU/Instructions/InternalInstruction.cpp +++ b/sources/CPU/Instructions/InternalInstruction.cpp @@ -19,4 +19,90 @@ namespace ComSquare::CPU this->_registers.p.m = true; } } + + void CPU::JSR(uint24_t valueAddr) + { + this->_push(--this->_registers.pc); + this->_registers.pc = this->_bus->read(valueAddr) + (this->_bus->read(valueAddr + 1) << 8u); + } + + void CPU::JSL(uint24_t valueAddr) + { + this->_registers.pac--; + this->_push(this->_registers.pbr); + this->_push(this->_registers.pc); + this->_registers.pc = this->_bus->read(valueAddr) + (this->_bus->read(valueAddr + 1) << 8u); + } + + void CPU::PHA() + { + this->_push(this->_registers.a); + } + + void CPU::PHB() + { + this->_push(this->_registers.dbr); + } + + void CPU::PHD() + { + this->_push(this->_registers.d); + } + + void CPU::PHK() + { + this->_push(this->_registers.pbr); + } + + void CPU::PHP() + { + this->_push(this->_registers.p.flags); + } + + void CPU::PHX() + { + this->_push(this->_registers.x); + } + + void CPU::PHY() + { + this->_push(this->_registers.y); + } + + void CPU::PLA() + { + this->_registers.a = this->_pop16(); + this->_registers.p.z = this->_registers.a == 0; + this->_registers.p.n = this->_registers.a & 0x8000u; + } + + void CPU::PLB() + { + + } + + void CPU::PLD() + { + + } + + void CPU::PLK() + { + + } + + void CPU::PLP() + { + + } + + void CPU::PLX() + { + + } + + void CPU::PLY() + { + + } } \ No newline at end of file diff --git a/tests/CPU/testAddressingMode.cpp b/tests/CPU/testAddressingMode.cpp index 3d09508..3492fa2 100644 --- a/tests/CPU/testAddressingMode.cpp +++ b/tests/CPU/testAddressingMode.cpp @@ -243,7 +243,7 @@ Test(AddrMode, AbsoluteIndexedIndirect) pair.second.cpu->_registers.x = 2; pair.second.wram->_data[0x01AD] = 0xEF; pair.second.wram->_data[0x01AE] = 0x01; - auto addr = pair.second.cpu->_getAbsoluteIndexedIndirectAddr(); + auto addr = pair.second.cpu->_getAbsoluteIndirectIndexedByXAddr(); cr_assert_eq(addr, 0x01EF, "Returned address was %x but was expecting 0x01EF.", addr); cr_assert_eq(pair.second.cpu->_registers.pac, 0x808002); } diff --git a/tests/CPU/testInternal.cpp b/tests/CPU/testInternal.cpp index 85a2e6c..61404a9 100644 --- a/tests/CPU/testInternal.cpp +++ b/tests/CPU/testInternal.cpp @@ -69,4 +69,151 @@ Test(REP, resetsomeEmulation) pair.second.cpu->REP(0x0); auto data = pair.second.cpu->_registers.p.flags; cr_assert_eq(data, 0b00110100, "The flag should be 0b00110100 but it was %x", data); +} + +Test(JSR, jump) +{ + auto pair = Init(); + pair.second.cpu->_registers.pc = 0xABCD; + pair.second.cpu->_registers.s = 0x0123; + pair.second.wram->_data[0] = 0xFF; + pair.second.wram->_data[1] = 0xAB; + pair.second.cpu->JSR(0x0); + auto pc = pair.second.cpu->_registers.pc; + cr_assert_eq(pc, 0xABFF, "The PC should be 0xABFF but it was %x", pc); + cr_assert_eq(pair.second.cpu->_registers.s, 0x0121, "The stack pointer should be 0x0121 but it was %x", pair.second.cpu->_registers.s); + auto pushed = pair.second.cpu->_pop16(); + cr_assert_eq(pushed, 0xABCC, "The value pushed to the stack should be 0xABCC but it was %x", pushed); +} + +Test(JSL, jump) +{ + auto pair = Init(); + pair.second.cpu->_registers.pbr = 0xFF; + pair.second.cpu->_registers.pc = 0xABCD; + pair.second.cpu->_registers.s = 0x0123; + pair.second.wram->_data[0] = 0xFF; + pair.second.wram->_data[1] = 0xAB; + pair.second.cpu->JSL(0x0); + auto pc = pair.second.cpu->_registers.pc; + cr_assert_eq(pc, 0xABFF, "The PC should be 0xABFF but it was %x", pc); + cr_assert_eq(pair.second.cpu->_registers.s, 0x0120, "The stack pointer should be 0x0120 but it was %x", pair.second.cpu->_registers.s); + auto pushed = pair.second.cpu->_pop16() + (pair.second.cpu->_pop() << 16u); + cr_assert_eq(pushed, 0xFFABCC, "The value pushed to the stack should be 0xFFABCD but it was %x", pushed); +} + +Test(PHA, basic) +{ + auto pair = Init(); + pair.second.cpu->_registers.a = 0xABCD; + pair.second.cpu->_registers.s = 0x02; + pair.second.cpu->PHA(); + cr_assert_eq(pair.second.wram->_data[1], 0xCD, "The second value pushed to the stack should be 0xCD but it was %x", pair.second.wram->_data[1]); + cr_assert_eq(pair.second.wram->_data[2], 0xAB, "The first value pushed to the stack should be 0xAB but it was %x", pair.second.wram->_data[2]); + cr_assert_eq(pair.second.cpu->_registers.s, 0x0, "The Stack pointer should be equal to 0x0 but it was %x", pair.second.cpu->_registers.s); +} + +Test(PHB, basic) +{ + auto pair = Init(); + pair.second.cpu->_registers.dbr = 0xFF; + pair.second.cpu->_registers.s = 0x02; + pair.second.cpu->PHB(); + cr_assert_eq(pair.second.wram->_data[2], 0xFF, "The first value pushed to the stack should be 0xFF but it was %x", pair.second.wram->_data[2]); + cr_assert_eq(pair.second.cpu->_registers.s, 0x1, "The Stack pointer should be equal to 0x1 but it was %x", pair.second.cpu->_registers.s); +} + +Test(PHD, basic) +{ + auto pair = Init(); + pair.second.cpu->_registers.d = 0xABCD; + pair.second.cpu->_registers.s = 0x02; + pair.second.cpu->PHD(); + cr_assert_eq(pair.second.wram->_data[1], 0xCD, "The second value pushed to the stack should be 0xCD but it was %x", pair.second.wram->_data[1]); + cr_assert_eq(pair.second.wram->_data[2], 0xAB, "The first value pushed to the stack should be 0xAB but it was %x", pair.second.wram->_data[2]); + cr_assert_eq(pair.second.cpu->_registers.s, 0x0, "The Stack pointer should be equal to 0x0 but it was %x", pair.second.cpu->_registers.s); +} + +Test(PHK, basic) +{ + auto pair = Init(); + pair.second.cpu->_registers.pbr = 0xFF; + pair.second.cpu->_registers.s = 0x02; + pair.second.cpu->PHK(); + cr_assert_eq(pair.second.wram->_data[2], 0xFF, "The first value pushed to the stack should be 0xFF but it was %x", pair.second.wram->_data[2]); + cr_assert_eq(pair.second.cpu->_registers.s, 0x1, "The Stack pointer should be equal to 0x1 but it was %x", pair.second.cpu->_registers.s); +} + +Test(PHP, basic) +{ + auto pair = Init(); + pair.second.cpu->_registers.p.flags = 0xFF; + pair.second.cpu->_registers.s = 0x02; + pair.second.cpu->PHP(); + cr_assert_eq(pair.second.wram->_data[2], 0xFF, "The first value pushed to the stack should be 0xFF but it was %x", pair.second.wram->_data[2]); + cr_assert_eq(pair.second.cpu->_registers.s, 0x1, "The Stack pointer should be equal to 0x1 but it was %x", pair.second.cpu->_registers.s); +} + +Test(PHX, basic) +{ + auto pair = Init(); + pair.second.cpu->_registers.x = 0xABCD; + pair.second.cpu->_registers.s = 0x02; + pair.second.cpu->PHX(); + cr_assert_eq(pair.second.wram->_data[1], 0xCD, "The second value pushed to the stack should be 0xCD but it was %x", pair.second.wram->_data[1]); + cr_assert_eq(pair.second.wram->_data[2], 0xAB, "The first value pushed to the stack should be 0xAB but it was %x", pair.second.wram->_data[2]); + cr_assert_eq(pair.second.cpu->_registers.s, 0x0, "The Stack pointer should be equal to 0x0 but it was %x", pair.second.cpu->_registers.s); +} + +Test(PHY, basic) +{ + auto pair = Init(); + pair.second.cpu->_registers.y = 0xABCD; + pair.second.cpu->_registers.s = 0x02; + pair.second.cpu->PHY(); + cr_assert_eq(pair.second.wram->_data[1], 0xCD, "The second value pushed to the stack should be 0xCD but it was %x", pair.second.wram->_data[1]); + cr_assert_eq(pair.second.wram->_data[2], 0xAB, "The first value pushed to the stack should be 0xAB but it was %x", pair.second.wram->_data[2]); + cr_assert_eq(pair.second.cpu->_registers.s, 0x0, "The Stack pointer should be equal to 0x0 but it was %x", pair.second.cpu->_registers.s); +} + +Test(PLA, basic) +{ + auto pair = Init(); + pair.second.wram->_data[1] = 0xCD; + pair.second.wram->_data[2] = 0x7B; + pair.second.cpu->_registers.s = 0x00; + pair.second.cpu->PLA(); + auto data = pair.second.cpu->_registers.a; + cr_assert_eq(data, 0x7BCD, "The accumulator should be 0x7BCD but it was %x", data); + cr_assert_eq(pair.second.cpu->_registers.p.z, false, "The zero flag should not be set.", pair.second.cpu->_registers.p.z); + cr_assert_eq(pair.second.cpu->_registers.p.n, false, "The negative flag should not be set.", pair.second.cpu->_registers.p.n); + cr_assert_eq(pair.second.cpu->_registers.s, 0x2, "The Stack pointer should be equal to 0x2 but it was %x", pair.second.cpu->_registers.s); +} + +Test(PLA, zero) +{ + auto pair = Init(); + pair.second.wram->_data[1] = 0x00; + pair.second.wram->_data[2] = 0x00; + pair.second.cpu->_registers.s = 0x00; + pair.second.cpu->PLA(); + auto data = pair.second.cpu->_registers.a; + cr_assert_eq(data, 0x0000, "The accumulator should be 0x0000 but it was %x", data); + cr_assert_eq(pair.second.cpu->_registers.p.z, true, "The zero flag should be set.", pair.second.cpu->_registers.p.z); + cr_assert_eq(pair.second.cpu->_registers.p.n, false, "The negative flag should not be set.", pair.second.cpu->_registers.p.n); + cr_assert_eq(pair.second.cpu->_registers.s, 0x2, "The Stack pointer should be equal to 0x2 but it was %x", pair.second.cpu->_registers.s); +} + +Test(PLA, negative) +{ + auto pair = Init(); + pair.second.wram->_data[1] = 0x00; + pair.second.wram->_data[2] = 0xA0; + pair.second.cpu->_registers.s = 0x00; + pair.second.cpu->PLA(); + auto data = pair.second.cpu->_registers.a; + cr_assert_eq(data, 0xA000, "The accumulator should be 0xA000 but it was %x", data); + cr_assert_eq(pair.second.cpu->_registers.p.z, false, "The zero flag not should be set.", pair.second.cpu->_registers.p.z); + cr_assert_eq(pair.second.cpu->_registers.p.n, true, "The negative flag should be set.", pair.second.cpu->_registers.p.n); + cr_assert_eq(pair.second.cpu->_registers.s, 0x2, "The Stack pointer should be equal to 0x2 but it was %x", pair.second.cpu->_registers.s); } \ No newline at end of file