diff --git a/CMakeLists.txt b/CMakeLists.txt index 58d8cf6..502dc01 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,8 +31,8 @@ add_executable(unit_tests sources/APU/APU.cpp sources/Exceptions/InvalidAddress.hpp sources/Exceptions/InvalidRom.hpp - sources/Models/Ints.hpp - sources/Models/Ints.hpp + sources/Models/Int24.hpp + sources/Models/Int24.hpp sources/Ram/Ram.cpp sources/Ram/Ram.hpp sources/Memory/MemoryShadow.cpp @@ -64,6 +64,7 @@ add_executable(unit_tests tests/PPU/testPpuWrite.cpp tests/PPU/testPpuWriteFromVmain.cpp sources/CPU/Instructions/MathematicalOperations.cpp + sources/CPU/Instructions/MemoryInstructions.cpp tests/CPU/Math/testADC.cpp tests/CPU/testStore.cpp sources/APU/Instructions/Bit.cpp @@ -99,8 +100,8 @@ add_executable(ComSquare sources/APU/APU.cpp sources/Exceptions/InvalidAddress.hpp sources/Exceptions/InvalidRom.hpp - sources/Models/Ints.hpp - sources/Models/Ints.hpp + sources/Models/Int24.hpp + sources/Models/Int24.hpp sources/Ram/Ram.cpp sources/Ram/Ram.hpp sources/Memory/MemoryShadow.cpp @@ -124,7 +125,8 @@ add_executable(ComSquare sources/APU/Instructions/Standbys.cpp sources/APU/Instructions/ProgramStatusWord.cpp sources/APU/Instructions/Bit.cpp -) + sources/CPU/Instructions/MathematicalOperations.cpp + sources/CPU/Instructions/MemoryInstructions.cpp) target_link_libraries(ComSquare sfml-graphics diff --git a/sources/APU/APU.cpp b/sources/APU/APU.cpp index bf221af..8538868 100644 --- a/sources/APU/APU.cpp +++ b/sources/APU/APU.cpp @@ -12,17 +12,13 @@ namespace ComSquare::APU { APU::APU() : _dsp(new DSP::DSP) { - this->_map.Page0 = std::make_shared(Ram::Ram(0x00F0)); - this->_map.Page1 = std::make_shared(Ram::Ram(0x0100)); - this->_map.Memory = std::make_shared(Ram::Ram(0xFDC0)); - this->_map.IPL = std::make_shared(Ram::Ram(0x0040)); this->reset(); } uint8_t APU::_internalRead(uint24_t addr) { switch (addr) { case 0x0000 ... 0x00EF: - return this->_map.Page0->read_internal(addr); + return this->_map.Page0.read_internal(addr); case 0xF0: return this->_registers.unknown; case 0xF2: @@ -48,11 +44,11 @@ namespace ComSquare::APU case 0xFF: return this->_registers.counter2; case 0x0100 ... 0x01FF: - return this->_map.Page1->read_internal(addr - 0x0100); + return this->_map.Page1.read_internal(addr - 0x0100); case 0x0200 ... 0xFFBF: - return this->_map.Memory->read_internal(addr - 0x200); + return this->_map.Memory.read_internal(addr - 0x200); case 0xFFC0 ... 0xFFFF: - return this->_map.IPL->read_internal(addr - 0xFFC0); + return this->_map.IPL.read_internal(addr - 0xFFC0); default: throw InvalidAddress("APU Registers read", addr); } @@ -61,7 +57,7 @@ namespace ComSquare::APU void APU::_internalWrite(uint24_t addr, uint8_t data) { switch (addr) { case 0x0000 ... 0x00EF: - this->_map.Page0->write_internal(addr, data); + this->_map.Page0.write_internal(addr, data); break; case 0xF0: this->_registers.unknown = data; @@ -103,13 +99,13 @@ namespace ComSquare::APU this->_registers.timer2 = data; break; case 0x0100 ... 0x01FF: - this->_map.Page1->write_internal(addr - 0x0100, data); + this->_map.Page1.write_internal(addr - 0x0100, data); break; case 0x0200 ... 0xFFBF: - this->_map.Memory->write_internal(addr - 0x200, data); + this->_map.Memory.write_internal(addr - 0x200, data); break; case 0xFFC0 ... 0xFFFF: - this->_map.IPL->write_internal(addr - 0xFFC0, data); + this->_map.IPL.write_internal(addr - 0xFFC0, data); break; default: throw InvalidAddress("APU Registers write", addr); @@ -247,4 +243,13 @@ namespace ComSquare::APU return (addr2 << 8u) | addr1; } + + MemoryMap::MemoryMap() : + Page0(0x00F0), + Page1(0x0100), + Memory(0xFDC0), + IPL(0x0040) + { + + } } \ No newline at end of file diff --git a/sources/APU/APU.hpp b/sources/APU/APU.hpp index 88107dd..47d7e67 100644 --- a/sources/APU/APU.hpp +++ b/sources/APU/APU.hpp @@ -117,13 +117,15 @@ namespace ComSquare::APU struct MemoryMap { //! @brief Zero page memory - std::shared_ptr Page0; + Ram::Ram Page0; //! @brief Stack space memory - std::shared_ptr Page1; + Ram::Ram Page1; //! @brief Any-use memory - std::shared_ptr Memory; + Ram::Ram Memory; //! @brief IPL ROM - std::shared_ptr IPL; + Ram::Ram IPL; + + MemoryMap(); }; class APU : public Memory::IMemory { @@ -197,6 +199,9 @@ namespace ComSquare::APU int TSET1(uint24_t abs); public: explicit APU(); + APU(const APU &) = default; + APU &operator=(const APU &) = default; + ~APU() = default; //! @brief Read from the internal APU register. //! @param addr The address to read from. The address 0x00 should refer to the first byte of the register. diff --git a/sources/APU/DSP/DSP.hpp b/sources/APU/DSP/DSP.hpp index 084abca..07f2b8f 100644 --- a/sources/APU/DSP/DSP.hpp +++ b/sources/APU/DSP/DSP.hpp @@ -112,6 +112,9 @@ namespace ComSquare::APU::DSP std::array _channels{}; public: explicit DSP(); + DSP(const DSP &) = default; + DSP &operator=(const DSP &) = default; + ~DSP() = default; //! @brief Read from the internal DSP register. //! @param addr The address to read from. The address 0x0 should refer to the first byte of the register. diff --git a/sources/CPU/CPU.cpp b/sources/CPU/CPU.cpp index 40246b7..0b503a3 100644 --- a/sources/CPU/CPU.cpp +++ b/sources/CPU/CPU.cpp @@ -190,59 +190,103 @@ namespace ComSquare::CPU unsigned cycles = 0; for (int i = 0; i < 0xFF; i++) - cycles += this->executeInstruction(); + cycles += this->_executeInstruction(); return cycles; } - unsigned CPU::executeInstruction() + unsigned CPU::_executeInstruction() { uint8_t opcode = this->_bus->read(this->_registers.pc); - this->_extraMemoryCycles = 0; + this->_hasIndexCrossedPageBoundary = false; switch (opcode) { - case Instructions::BRK: return 7 + this->BRK(); + case Instructions::BRK: this->BRK(); return 7 + !this->_isEmulationMode; - case Instructions::RTI: return 6 + this->RTI(); + case Instructions::RTI: this->RTI(); return 6 + !this->_isEmulationMode; - case Instructions::ADC_IM: return 2 + this->ADC(this->_getImmediateAddr()); - case Instructions::ADC_ABS: return 4 + this->ADC(this->_getAbsoluteAddr()); - case Instructions::ADC_ABSl: return 5 + this->ADC(this->_getAbsoluteLongAddr()); - case Instructions::ADC_DP: return 3 + this->ADC(this->_getDirectAddr()); - case Instructions::ADC_DPi: return 5 + this->ADC(this->_getDirectIndirectAddr()); - case Instructions::ADC_DPil: return 6 + this->ADC(this->_getDirectIndirectLongAddr()); - case Instructions::ADC_ABSX: return 4 + this->ADC(this->_getAbsoluteIndexedByXAddr()); - case Instructions::ADC_ABSXl:return 5 + this->ADC(this->_getAbsoluteIndexedByXLongAddr()); - case Instructions::ADC_ABSY: return 4 + this->ADC(this->_getAbsoluteIndexedByYAddr()); - case Instructions::ADC_DPX: return 4 + this->ADC(this->_getDirectIndexedByXAddr()); - case Instructions::ADC_DPXi: return 6 + this->ADC(this->_getDirectIndirectIndexedXAddr()); - case Instructions::ADC_DPYi: return 5 + this->ADC(this->_getDirectIndirectIndexedYAddr()); - case Instructions::ADC_DPYil:return 6 + this->ADC(this->_getDirectIndirectIndexedYLongAddr()); - case Instructions::ADC_SR: return 4 + this->ADC(this->_getStackRelativeAddr()); - case Instructions::ADC_SRYi: return 7 + this->ADC(this->_getStackRelativeIndirectIndexedYAddr()); + case Instructions::ADC_IM: this->ADC(this->_getImmediateAddr()); return 2 + !this->_registers.p.m; + case Instructions::ADC_ABS: this->ADC(this->_getAbsoluteAddr()); return 4 + !this->_registers.p.m; + case Instructions::ADC_ABSl: this->ADC(this->_getAbsoluteLongAddr()); return 5 + !this->_registers.p.m; + case Instructions::ADC_DP: this->ADC(this->_getDirectAddr()); return 3 + !this->_registers.p.m + this->_registers.dl != 0; + case Instructions::ADC_DPi: this->ADC(this->_getDirectIndirectAddr()); return 5 + !this->_registers.p.m + this->_registers.dl != 0; + case Instructions::ADC_DPil: this->ADC(this->_getDirectIndirectLongAddr()); return 6 + !this->_registers.p.m + this->_registers.dl != 0; + case Instructions::ADC_ABSX: this->ADC(this->_getAbsoluteIndexedByXAddr()); return 4 + !this->_registers.p.m + this->_hasIndexCrossedPageBoundary; + case Instructions::ADC_ABSXl:this->ADC(this->_getAbsoluteIndexedByXLongAddr()); return 5 + !this->_registers.p.m; + case Instructions::ADC_ABSY: this->ADC(this->_getAbsoluteIndexedByYAddr()); return 4 + !this->_registers.p.m + this->_hasIndexCrossedPageBoundary; + case Instructions::ADC_DPX: this->ADC(this->_getDirectIndexedByXAddr()); return 4 + !this->_registers.p.m + this->_registers.dl != 0; + case Instructions::ADC_DPXi: this->ADC(this->_getDirectIndirectIndexedXAddr()); return 6 + !this->_registers.p.m + this->_registers.dl != 0; + case Instructions::ADC_DPYi: this->ADC(this->_getDirectIndirectIndexedYAddr()); return 5 + !this->_registers.p.m + this->_registers.dl != 0 + this->_hasIndexCrossedPageBoundary; + case Instructions::ADC_DPYil:this->ADC(this->_getDirectIndirectIndexedYLongAddr()); return 6 + !this->_registers.p.m + this->_registers.dl != 0; + case Instructions::ADC_SR: this->ADC(this->_getStackRelativeAddr()); return 4 + !this->_registers.p.m; + case Instructions::ADC_SRYi: this->ADC(this->_getStackRelativeIndirectIndexedYAddr()); return 7 + !this->_registers.p.m; + + case Instructions::STA_ABS: this->STA(this->_getAbsoluteAddr()); return 4 + !this->_registers.p.m; + case Instructions::STA_ABSl: this->STA(this->_getAbsoluteLongAddr()); return 5 + !this->_registers.p.m; + case Instructions::STA_DP: this->STA(this->_getDirectAddr()); return 3 + !this->_registers.p.m + this->_registers.dl != 0; + case Instructions::STA_DPi: this->STA(this->_getDirectIndirectAddr()); return 5 + !this->_registers.p.m + this->_registers.dl != 0; + case Instructions::STA_DPil: this->STA(this->_getDirectIndirectLongAddr()); return 6 + !this->_registers.p.m + this->_registers.dl != 0; + case Instructions::STA_ABSX: this->STA(this->_getAbsoluteIndexedByXAddr()); return 5 + !this->_registers.p.m; + case Instructions::STA_ABSXl:this->STA(this->_getAbsoluteIndexedByXLongAddr()); return 5 + !this->_registers.p.m; + case Instructions::STA_ABSY: this->STA(this->_getAbsoluteIndexedByYAddr()); return 5 + !this->_registers.p.m; + case Instructions::STA_DPX: this->STA(this->_getDirectIndexedByXAddr()); return 4 + !this->_registers.p.m + this->_registers.dl != 0; + case Instructions::STA_DPXi: this->STA(this->_getDirectIndirectIndexedXAddr()); return 6 + !this->_registers.p.m + this->_registers.dl != 0; + case Instructions::STA_DPYi: this->STA(this->_getDirectIndirectIndexedYAddr()); return 6 + !this->_registers.p.m + this->_registers.dl != 0; + case Instructions::STA_DPYil:this->STA(this->_getDirectIndirectIndexedYLongAddr()); return 6 + !this->_registers.p.m + this->_registers.dl != 0; + case Instructions::STA_SR: this->STA(this->_getStackRelativeAddr()); return 4 + !this->_registers.p.m; + case Instructions::STA_SRYi: this->STA(this->_getStackRelativeIndirectIndexedYAddr()); return 7 + !this->_registers.p.m; + + case Instructions::STX_ABS: this->STX(this->_getAbsoluteAddr()); return 4 + !this->_registers.p.m; + case Instructions::STX_DP: this->STX(this->_getDirectAddr()); return 3 + !this->_registers.p.m + this->_registers.dl != 0; + case Instructions::STX_DPY: this->STX(this->_getDirectIndexedByYAddr()); return 4 + !this->_registers.p.m + this->_registers.dl != 0; + + case Instructions::STY_ABS: this->STX(this->_getAbsoluteAddr()); return 4 + !this->_registers.p.m; + case Instructions::STY_DP: this->STX(this->_getDirectAddr()); return 3 + !this->_registers.p.m + this->_registers.dl != 0; + case Instructions::STY_DPX: this->STX(this->_getDirectIndexedByXAddr()); return 4 + !this->_registers.p.m + this->_registers.dl != 0; + + case Instructions::STZ_ABS: this->STX(this->_getAbsoluteAddr()); return 4 + !this->_registers.p.m; + case Instructions::STZ_DP: this->STX(this->_getDirectAddr()); return 3 + !this->_registers.p.m + this->_registers.dl != 0; + case Instructions::STZ_ABSX: this->STX(this->_getAbsoluteIndexedByXAddr()); return 3 + !this->_registers.p.m + this->_registers.dl != 0; + case Instructions::STZ_DPX: this->STX(this->_getDirectIndexedByXAddr()); return 4 + !this->_registers.p.m + this->_registers.dl != 0; + + case Instructions::LDA_IM: this->LDA(this->_getImmediateAddr()); return 2 + !this->_registers.p.m; + case Instructions::LDA_ABS: this->LDA(this->_getAbsoluteAddr()); return 4 + !this->_registers.p.m; + case Instructions::LDA_ABSl: this->LDA(this->_getAbsoluteLongAddr()); return 5 + !this->_registers.p.m; + case Instructions::LDA_DP: this->LDA(this->_getDirectAddr()); return 3 + !this->_registers.p.m + this->_registers.dl != 0; + case Instructions::LDA_DPi: this->LDA(this->_getDirectIndirectAddr()); return 5 + !this->_registers.p.m + this->_registers.dl != 0; + case Instructions::LDA_DPil: this->LDA(this->_getDirectIndirectLongAddr()); return 6 + !this->_registers.p.m + this->_registers.dl != 0; + case Instructions::LDA_ABSX: this->LDA(this->_getAbsoluteIndexedByXAddr()); return 4 + !this->_registers.p.m + this->_hasIndexCrossedPageBoundary; + case Instructions::LDA_ABSXl:this->LDA(this->_getAbsoluteIndexedByXLongAddr()); return 5 + !this->_registers.p.m; + case Instructions::LDA_ABSY: this->LDA(this->_getAbsoluteIndexedByYAddr()); return 4 + !this->_registers.p.m + this->_hasIndexCrossedPageBoundary; + case Instructions::LDA_DPX: this->LDA(this->_getDirectIndexedByXAddr()); return 4 + !this->_registers.p.m + this->_registers.dl != 0; + case Instructions::LDA_DPXi: this->LDA(this->_getDirectIndirectIndexedXAddr()); return 6 + !this->_registers.p.m + this->_registers.dl != 0; + case Instructions::LDA_DPYi: this->LDA(this->_getDirectIndirectIndexedYAddr()); return 5 + !this->_registers.p.m + this->_registers.dl != 0 + this->_hasIndexCrossedPageBoundary; + case Instructions::LDA_DPYil:this->LDA(this->_getDirectIndirectIndexedYLongAddr()); return 6 + !this->_registers.p.m + this->_registers.dl != 0; + case Instructions::LDA_SR: this->LDA(this->_getStackRelativeAddr()); return 4 + !this->_registers.p.m; + case Instructions::LDA_SRYi: this->LDA(this->_getStackRelativeIndirectIndexedYAddr()); return 7 + !this->_registers.p.m; default: throw InvalidOpcode("CPU", opcode); } } - void CPU::push(uint8_t data) + void CPU::_push(uint8_t data) { this->_bus->write(this->_registers.s--, data); } - void CPU::push(uint16_t data) + void CPU::_push(uint16_t data) { this->_bus->write(this->_registers.s--, data); this->_bus->write(this->_registers.s--, data << 8u); } - uint8_t CPU::pop() + uint8_t CPU::_pop() { return this->_bus->read(this->_registers.s++); } - uint16_t CPU::pop16() + uint16_t CPU::_pop16() { return this->_bus->read(this->_registers.s++) + (this->_bus->read(this->_registers.s++) << 8u); } @@ -261,9 +305,6 @@ namespace ComSquare::CPU uint24_t CPU::_getDirectAddr() { - if (this->_registers.dl != 0) - this->_extraMemoryCycles++; - uint8_t addr = this->_bus->read(this->_registers.pac++); return this->_registers.d + addr; } @@ -286,23 +327,17 @@ namespace ComSquare::CPU uint24_t CPU::_getDirectIndirectIndexedYAddr() { - if (this->_registers.dl != 0) - this->_extraMemoryCycles++; - uint16_t dp = this->_bus->read(this->_registers.pac++) + this->_registers.d; 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))) - this->_extraMemoryCycles++; + this->_hasIndexCrossedPageBoundary = true; return base + this->_registers.y; } uint24_t CPU::_getDirectIndirectIndexedYLongAddr() { - if (this->_registers.dl != 0) - this->_extraMemoryCycles++; - uint16_t dp = this->_bus->read(this->_registers.pac++) + this->_registers.d; uint24_t base = this->_bus->read(dp); base += this->_bus->read(dp + 1) << 8u; @@ -312,9 +347,6 @@ namespace ComSquare::CPU uint24_t CPU::_getDirectIndirectIndexedXAddr() { - if (this->_registers.dl != 0) - this->_extraMemoryCycles++; - uint16_t dp = this->_bus->read(this->_registers.pac++) + this->_registers.d; dp += this->_registers.x; uint24_t base = this->_bus->read(dp); @@ -325,9 +357,6 @@ namespace ComSquare::CPU uint24_t CPU::_getDirectIndexedByXAddr() { - if (this->_registers.dl != 0) - this->_extraMemoryCycles++; - uint16_t dp = this->_bus->read(this->_registers.pac++) + this->_registers.d; dp += this->_registers.x; return dp; @@ -335,9 +364,6 @@ namespace ComSquare::CPU uint24_t CPU::_getDirectIndexedByYAddr() { - if (this->_registers.dl != 0) - this->_extraMemoryCycles++; - uint16_t dp = this->_bus->read(this->_registers.pac++) + this->_registers.d; dp += this->_registers.y; return dp; @@ -349,7 +375,7 @@ namespace ComSquare::CPU abs += this->_bus->read(this->_registers.pac++) << 8u; uint24_t effective = abs + (this->_registers.dbr << 16u); if ((effective & 0xF0000000u) == (((effective + this->_registers.x) & 0xF0000000u))) - this->_extraMemoryCycles++; + this->_hasIndexCrossedPageBoundary = true; return effective + this->_registers.x; } @@ -359,7 +385,7 @@ namespace ComSquare::CPU abs += this->_bus->read(this->_registers.pac++) << 8u; uint24_t effective = abs + (this->_registers.dbr << 16u); if ((effective & 0xF0000000u) == (((effective + this->_registers.y) & 0xF0000000u))) - this->_extraMemoryCycles++; + this->_hasIndexCrossedPageBoundary = true; return effective + this->_registers.y; } @@ -408,9 +434,6 @@ namespace ComSquare::CPU uint24_t CPU::_getDirectIndirectAddr() { - if (this->_registers.dl != 0) - this->_extraMemoryCycles++; - uint16_t dp = this->_bus->read(this->_registers.pac++) + this->_registers.d; uint24_t effective = this->_bus->read(dp); effective += this->_bus->read(dp + 1) << 8u; @@ -420,9 +443,6 @@ namespace ComSquare::CPU uint24_t CPU::_getDirectIndirectLongAddr() { - if (this->_registers.dl != 0) - this->_extraMemoryCycles++; - uint16_t dp = this->_bus->read(this->_registers.pac++) + this->_registers.d; uint24_t effective = this->_bus->read(dp); effective += this->_bus->read(++dp) << 8u; @@ -441,15 +461,4 @@ namespace ComSquare::CPU base += this->_registers.dbr << 16u; return base + this->_registers.y; } - - unsigned CPU::STA(uint24_t addr) - { - if (this->_registers.p.m) - this->_bus->write(addr, this->_registers.al); - else { - this->_bus->write(addr, this->_registers.al); - this->_bus->write(addr + 1, this->_registers.ah); - } - return 0; - } } \ No newline at end of file diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index f74a8bf..63352da 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -7,7 +7,7 @@ #include "../Memory/IMemory.hpp" #include "../Memory/MemoryBus.hpp" -#include "../Models/Ints.hpp" +#include "../Models/Int24.hpp" #include "Instructions/CommonInstructions.hpp" #include "../Cartridge/Cartridge.hpp" @@ -18,8 +18,8 @@ namespace ComSquare::CPU //! @brief The Accumulator union { struct { - uint8_t ah; uint8_t al; + uint8_t ah; }; uint16_t a; }; @@ -28,8 +28,8 @@ namespace ComSquare::CPU //! @brief The Direct Page register; union { struct { - uint8_t dh; uint8_t dl; + uint8_t dh; }; uint16_t d; }; @@ -40,8 +40,8 @@ namespace ComSquare::CPU //! @brief The Program Counter; union { struct { - uint8_t pch; uint8_t pcl; + uint8_t pch; }; uint16_t pc; }; @@ -52,24 +52,24 @@ namespace ComSquare::CPU //! @brief The Stack pointer union { struct { - uint8_t sh; uint8_t sl; + uint8_t sh; }; uint16_t s; }; //! @brief The X index register union { struct { - uint8_t xh; uint8_t xl; + uint8_t xh; }; uint16_t x; }; //! @brief The Y index register union { struct { - uint8_t yh; uint8_t yl; + uint8_t yh; }; uint16_t y; }; @@ -180,7 +180,7 @@ namespace ComSquare::CPU uint8_t joy4h; }; - //! @brief All the instructions opcode of the main CPI. + //! @brief All the instructions opcode of the main CPU. //! @info The name of the instruction followed by their parameters (after an underscore) if any. //! @info Addr mode with an i at the end means indirect. //! @info Addr mode with an l at the end means long. @@ -204,6 +204,50 @@ namespace ComSquare::CPU ADC_ABSY = 0x79, ADC_ABSX = 0x7D, ADC_ABSXl = 0x7F, + + STA_ABS = 0x8D, + STA_ABSl = 0x8F, + STA_DP = 0x85, + STA_DPi = 0x92, + STA_DPil = 0x87, + STA_ABSX = 0x9D, + STA_ABSXl = 0x9F, + STA_ABSY = 0x99, + STA_DPX = 0x95, + STA_DPXi = 0x81, + STA_DPYi = 0x91, + STA_DPYil = 0x97, + STA_SR = 0x83, + STA_SRYi = 0x93, + + STX_ABS = 0x8E, + STX_DP = 0x86, + STX_DPY = 0x96, + + STY_ABS = 0x8C, + STY_DP = 0x84, + STY_DPX = 0x94, + + STZ_ABS = 0x9C, + STZ_DP = 0x64, + STZ_ABSX = 0x9E, + STZ_DPX = 0x74, + + LDA_IM = 0xA9, + LDA_ABS = 0xAD, + LDA_ABSl = 0xAF, + LDA_DP = 0xA5, + LDA_DPi = 0xB2, + LDA_DPil = 0xA7, + LDA_ABSX = 0xBD, + LDA_ABSXl = 0xBF, + LDA_ABSY = 0xB9, + LDA_DPX = 0xB5, + LDA_DPXi = 0xA1, + LDA_DPYi = 0xB1, + LDA_DPYil = 0xB7, + LDA_SR = 0xA3, + LDA_SRYi = 0xB3 }; //! @brief The main CPU @@ -220,8 +264,8 @@ namespace ComSquare::CPU //! @brief The cartridge header (stored for interrupt vectors.. Cartridge::Header &_cartridgeHeader; - //! @brief An additional number of cycles that the current running instruction took to run. (Used for address modes that take longer to run than others). - unsigned _extraMemoryCycles = 0; + //! @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. (This functions returns the 24bit space address of the value). uint24_t _getImmediateAddr(); @@ -266,32 +310,43 @@ namespace ComSquare::CPU //! @brief Push 8 bits of data to the stack. - void push(uint8_t data); + void _push(uint8_t data); //! @brief Push 16 bits of data to the stack. - void push(uint16_t data); + void _push(uint16_t data); //! @brief Pop 8 bits of data from the stack. - uint8_t pop(); + uint8_t _pop(); //! @brief Pop 16 bits of data from the stack. - uint16_t pop16(); + uint16_t _pop16(); //! @brief Execute a single instruction. //! @return The number of CPU cycles that the instruction took. - unsigned executeInstruction(); + unsigned _executeInstruction(); //! @brief Reset interrupt - Called on boot and when the reset button is pressed. - unsigned RESB(); + void RESB(); //! @brief Break instruction - Causes a software break. The PC is loaded from a vector table. - unsigned BRK(); + void BRK(); //! @brief Return from Interrupt - Used to return from a interrupt handler. - unsigned RTI(); + void RTI(); //! @brief Add with carry - Adds operand to the Accumulator; adds an additional 1 if carry is set. //! @return The number of extra cycles that this operation took. - unsigned ADC(uint24_t valueAddr); + void ADC(uint24_t valueAddr); //! @brief Store the accumulator to memory. - unsigned STA(uint24_t addr); + void STA(uint24_t addr); + //! @brief Store the index register X to memory. + void STX(uint24_t addr); + //! @brief Store the index register Y to memory. + void STY(uint24_t addr); + //! @brief Store zero to the memory. + void STZ(uint24_t addr); + //! @brief Load the accumulator from memory. + void LDA(uint24_t addr); public: explicit CPU(std::shared_ptr bus, Cartridge::Header &cartridgeHeader); + CPU(const CPU &) = default; + CPU &operator=(const CPU &) = delete; + ~CPU() = default; //! @brief This function continue to execute the Cartridge code. //! @return The number of CPU cycles that elapsed unsigned update(); diff --git a/sources/CPU/Instructions/Interrupts.cpp b/sources/CPU/Instructions/Interrupts.cpp index b175048..c439e9f 100644 --- a/sources/CPU/Instructions/Interrupts.cpp +++ b/sources/CPU/Instructions/Interrupts.cpp @@ -6,7 +6,7 @@ namespace ComSquare::CPU { - unsigned CPU::RESB() + void CPU::RESB() { this->_registers.p.i = true; this->_registers.p.d = false; @@ -18,10 +18,9 @@ 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; - return 0; } - unsigned CPU::BRK() + void CPU::BRK() { // TODO rework this. The PC should be pushed to the stack. // Info here: http://softpixel.com/~cwright/sianse/docs/65816NFO.HTM at BRK Software Break @@ -33,18 +32,14 @@ namespace ComSquare::CPU else this->_registers.pc = this->_cartridgeHeader.nativeInterrupts.brk; this->_registers.p.d = false; - return !this->_isEmulationMode; } - unsigned CPU::RTI() + void CPU::RTI() { - this->_registers.p.flags = this->pop(); - this->_registers.pc = this->pop16(); + this->_registers.p.flags = this->_pop(); + this->_registers.pc = this->_pop16(); - if (!this->_isEmulationMode) { - this->_registers.pbr = this->pop16(); - return 1; - } - return 0; + if (!this->_isEmulationMode) + this->_registers.pbr = this->_pop16(); } } \ No newline at end of file diff --git a/sources/CPU/Instructions/MathematicalOperations.cpp b/sources/CPU/Instructions/MathematicalOperations.cpp index 78a8dc1..37a38e6 100644 --- a/sources/CPU/Instructions/MathematicalOperations.cpp +++ b/sources/CPU/Instructions/MathematicalOperations.cpp @@ -7,7 +7,7 @@ namespace ComSquare::CPU { - unsigned CPU::ADC(uint24_t valueAddr) + void CPU::ADC(uint24_t valueAddr) { unsigned value = this->_bus->read(valueAddr) + this->_registers.p.c; if (this->_registers.p.m) @@ -25,6 +25,5 @@ namespace ComSquare::CPU this->_registers.a %= 0x100; this->_registers.p.z = this->_registers.a == 0; this->_registers.p.n = this->_registers.a & negativeMask; - return this->_extraMemoryCycles + !this->_registers.p.m; } } \ No newline at end of file diff --git a/sources/CPU/Instructions/MemoryInstructions.cpp b/sources/CPU/Instructions/MemoryInstructions.cpp new file mode 100644 index 0000000..ac24496 --- /dev/null +++ b/sources/CPU/Instructions/MemoryInstructions.cpp @@ -0,0 +1,58 @@ +// +// Created by anonymus-raccoon on 2/13/20. +// + +#include "../CPU.hpp" + +namespace ComSquare::CPU +{ + void CPU::STA(uint24_t addr) + { + if (this->_registers.p.m) + this->_bus->write(addr, this->_registers.al); + else { + this->_bus->write(addr, this->_registers.al); + this->_bus->write(addr + 1, this->_registers.ah); + } + } + + void CPU::STX(uint24_t addr) + { + if (this->_registers.p.x_b) + this->_bus->write(addr, this->_registers.xl); + else { + this->_bus->write(addr, this->_registers.xl); + this->_bus->write(addr + 1, this->_registers.xh); + } + } + + void CPU::STY(uint24_t addr) + { + if (this->_registers.p.x_b) + this->_bus->write(addr, this->_registers.yl); + else { + this->_bus->write(addr, this->_registers.yl); + this->_bus->write(addr + 1, this->_registers.yh); + } + } + + void CPU::STZ(uint24_t addr) + { + this->_bus->write(addr, 0x00); + if (!this->_registers.p.m) + this->_bus->write(addr + 1, 0x00); + } + + void CPU::LDA(uint24_t addr) + { + if (this->_registers.p.m) { + this->_registers.a = this->_bus->read(addr); + this->_registers.p.n = this->_registers.al & 0xF0u; + } else { + this->_registers.al = this->_bus->read(addr); + this->_registers.ah = this->_bus->read(addr + 1); + this->_registers.p.n = this->_registers.a & 0xF000u; + } + this->_registers.p.z = this->_registers.a == 0x0; + } +} \ No newline at end of file diff --git a/sources/Cartridge/Cartridge.hpp b/sources/Cartridge/Cartridge.hpp index 3b6abb2..113619b 100644 --- a/sources/Cartridge/Cartridge.hpp +++ b/sources/Cartridge/Cartridge.hpp @@ -7,7 +7,7 @@ #include #include "../Memory/IMemory.hpp" -#include "../Models/Ints.hpp" +#include "../Models/Int24.hpp" #include "../Memory/IRectangleMemory.hpp" #include "InterruptVectors.hpp" @@ -57,6 +57,9 @@ namespace ComSquare::Cartridge InterruptVectors emulationInterrupts{}; Header() = default; + Header(const Header &) = default; + Header &operator=(const Header &) = default; + ~Header() = default; }; //! @brief Contains the rom's memory/instructions. @@ -86,6 +89,10 @@ namespace ComSquare::Cartridge public: //! @brief Load a rom from it's path. explicit Cartridge(const std::string &romPath); + //! @brief The cartridge can't be copied. + Cartridge(const Cartridge &) = delete; + //! @brief The cartridge can't be assigned. + Cartridge &operator=(const Cartridge &) = delete; //! @brief Destructor that free the cartridge data. ~Cartridge(); diff --git a/sources/Memory/IMemory.hpp b/sources/Memory/IMemory.hpp index 694469d..b0a8e64 100644 --- a/sources/Memory/IMemory.hpp +++ b/sources/Memory/IMemory.hpp @@ -8,7 +8,7 @@ #include #include -#include "../Models/Ints.hpp" +#include "../Models/Int24.hpp" namespace ComSquare::Memory { diff --git a/sources/Memory/MemoryBus.hpp b/sources/Memory/MemoryBus.hpp index b7689eb..e1db314 100644 --- a/sources/Memory/MemoryBus.hpp +++ b/sources/Memory/MemoryBus.hpp @@ -36,6 +36,11 @@ namespace ComSquare inline void _mirrorComponents(SNES &console, unsigned i); public: + MemoryBus() = default; + MemoryBus(const MemoryBus &) = default; + MemoryBus &operator=(const MemoryBus &) = default; + ~MemoryBus() = default; + //! @brief Read data at a global address. //! @param addr The address to read from. //! @return The value that the component returned for this address. If the address was mapped to ram, it simply returned the value. If the address was mapped to a register the component returned the register. diff --git a/sources/Memory/MemoryShadow.hpp b/sources/Memory/MemoryShadow.hpp index e6f4db7..ce80ef8 100644 --- a/sources/Memory/MemoryShadow.hpp +++ b/sources/Memory/MemoryShadow.hpp @@ -17,6 +17,9 @@ namespace ComSquare::Memory public: //! @brief Create a shadow for the memory given as parameter. explicit MemoryShadow(std::shared_ptr initial, uint24_t start, uint24_t end); + MemoryShadow(const MemoryShadow &) = default; + MemoryShadow &operator=(const MemoryShadow &) = default; + ~MemoryShadow() = default; //! @brief Read from the initial IMemory given. //! @param addr The address to read from. The address 0x0 should refer to the first byte of the initial IMemory. diff --git a/sources/Memory/RectangleShadow.hpp b/sources/Memory/RectangleShadow.hpp index c418f24..7f43ad6 100644 --- a/sources/Memory/RectangleShadow.hpp +++ b/sources/Memory/RectangleShadow.hpp @@ -20,6 +20,9 @@ namespace ComSquare::Memory public: //! @brief Create a shadow for the memory given as parameter. explicit RectangleShadow(std::shared_ptr initial, uint8_t startBank, uint8_t endBank, uint16_t startPage, uint16_t endPage); + RectangleShadow(const RectangleShadow &) = default; + RectangleShadow &operator=(const RectangleShadow &) = default; + ~RectangleShadow() = default; //! @brief Internal component read. Implement this as you would implement a basic IMemory's read. //! @param addr The local address to read from. 0x0 refer to the first byte of your data and the address is in the component's space. That means that you can consider this address as continuous diff --git a/sources/Models/Int24.hpp b/sources/Models/Int24.hpp new file mode 100644 index 0000000..6d0f7bd --- /dev/null +++ b/sources/Models/Int24.hpp @@ -0,0 +1,10 @@ +// +// Created by anonymus-raccoon on 1/28/20. +// + +#ifndef COMSQUARE_INT24_HPP +#define COMSQUARE_INT24_HPP + +typedef unsigned uint24_t; + +#endif //COMSQUARE_INT24_HPP diff --git a/sources/Models/Ints.hpp b/sources/Models/Ints.hpp deleted file mode 100644 index 58f6c01..0000000 --- a/sources/Models/Ints.hpp +++ /dev/null @@ -1,10 +0,0 @@ -// -// Created by anonymus-raccoon on 1/28/20. -// - -#ifndef COMSQUARE_INTS_HPP -#define COMSQUARE_INTS_HPP - -typedef unsigned uint24_t; - -#endif //COMSQUARE_INTS_HPP diff --git a/sources/PPU/PPU.cpp b/sources/PPU/PPU.cpp index 1e59f57..0bdc94b 100644 --- a/sources/PPU/PPU.cpp +++ b/sources/PPU/PPU.cpp @@ -198,8 +198,8 @@ namespace ComSquare::PPU } PPU::PPU(const std::shared_ptr &bus, Renderer::IRenderer &renderer): - _bus(std::move(bus)), - _renderer(renderer) + _renderer(renderer), + _bus(std::move(bus)) { this->_isLowByte = true; //_vram = new uint16_t[32000]; diff --git a/sources/PPU/PPU.hpp b/sources/PPU/PPU.hpp index 41020da..b9d1f84 100644 --- a/sources/PPU/PPU.hpp +++ b/sources/PPU/PPU.hpp @@ -521,6 +521,10 @@ namespace ComSquare::PPU uint16_t *_vram; public: PPU(const std::shared_ptr &bus, Renderer::IRenderer &renderer); + PPU(const PPU &) = default; + PPU &operator=(const PPU &) = delete; + ~PPU() = default; + //! @brief Read data from the component. //! @param addr The local address to read from (0x0 should refer to the first byte of this component). //! @throw This function should thrown an InvalidAddress for address that are not mapped to the component. diff --git a/sources/Ram/Ram.hpp b/sources/Ram/Ram.hpp index a6fea5d..b659b2a 100644 --- a/sources/Ram/Ram.hpp +++ b/sources/Ram/Ram.hpp @@ -18,6 +18,10 @@ namespace ComSquare::Ram public: //! @brief Load a rom from it's path. explicit Ram(size_t size); + //! @brief The ram can't be copied. + Ram(const Ram &) = delete; + //! @brief The ram can't be assigned. + Ram &operator=(Ram &) = delete; //! @brief Destructor that free the ram. ~Ram(); //! @brief Read from the ram. diff --git a/sources/Renderer/NoRenderer.hpp b/sources/Renderer/NoRenderer.hpp index 21269e8..d06d5b1 100644 --- a/sources/Renderer/NoRenderer.hpp +++ b/sources/Renderer/NoRenderer.hpp @@ -29,6 +29,9 @@ namespace ComSquare::Renderer //! @param width width of the window. //! @param maxFPS the number of maximum FPS for the window. NoRenderer(unsigned int height, unsigned int width, int maxFPS); + NoRenderer(const NoRenderer &) = default; + NoRenderer &operator=(const NoRenderer &) = default; + ~NoRenderer() = default; }; } diff --git a/sources/Renderer/SFRenderer.hpp b/sources/Renderer/SFRenderer.hpp index f7b403c..e259fdd 100644 --- a/sources/Renderer/SFRenderer.hpp +++ b/sources/Renderer/SFRenderer.hpp @@ -53,6 +53,9 @@ namespace ComSquare::Renderer //! @param width width of the window. //! @param maxFPS the number of maximum FPS for the window. SFRenderer(unsigned int height, unsigned int width, int maxFPS); + SFRenderer(const SFRenderer &) = delete; + SFRenderer &operator=(const SFRenderer &) = delete; + ~SFRenderer() = default; }; } diff --git a/sources/SNES.hpp b/sources/SNES.hpp index 996cb8a..c863d88 100644 --- a/sources/SNES.hpp +++ b/sources/SNES.hpp @@ -32,6 +32,9 @@ namespace ComSquare std::shared_ptr sram; //! @brief Create all the components using a common memory bus for all of them. SNES(const std::shared_ptr &bus, const std::string &ramPath, Renderer::IRenderer &renderer); + SNES(const SNES &) = default; + SNES &operator=(const SNES &) = default; + ~SNES() = default; }; } diff --git a/tests/APU/testAPU.cpp b/tests/APU/testAPU.cpp index 4601db8..c7aa1ce 100644 --- a/tests/APU/testAPU.cpp +++ b/tests/APU/testAPU.cpp @@ -32,7 +32,7 @@ Test(_internalRead, Page0) auto apu = Init().second.apu; uint8_t result = 0; - apu->_map.Page0->_data[0x0010] = 123; + apu->_map.Page0._data[0x0010] = 123; result = apu->_internalRead(0x0010); cr_assert_eq(result, 123); } @@ -42,7 +42,7 @@ Test(_internalRead, Page1) auto apu = Init().second.apu; uint8_t result = 0; - apu->_map.Page1->_data[0x0042] = 123; + apu->_map.Page1._data[0x0042] = 123; result = apu->_internalRead(0x0142); cr_assert_eq(result, 123); } @@ -52,7 +52,7 @@ Test(_internalRead, Memory) auto apu = Init().second.apu; uint8_t result = 0; - apu->_map.Memory->_data[0xFCDC] = 123; + apu->_map.Memory._data[0xFCDC] = 123; result = apu->_internalRead(0xFEDC); cr_assert_eq(result, 123); } @@ -62,7 +62,7 @@ Test(_internalRead, IPL) auto apu = Init().second.apu; uint8_t result = 0; - apu->_map.IPL->_data[0x001F] = 123; + apu->_map.IPL._data[0x001F] = 123; result = apu->_internalRead(0xFFDF); cr_assert_eq(result, 123); } @@ -85,7 +85,7 @@ Test(_internalWrite, Page0) auto apu = Init().second.apu; apu->_internalWrite(0x0001, 123); - cr_assert_eq(apu->_map.Page0->_data[0x0001], 123); + cr_assert_eq(apu->_map.Page0._data[0x0001], 123); } Test(_internalWrite, register) @@ -101,7 +101,7 @@ Test(_internalWrite, Page1) auto apu = Init().second.apu; apu->_internalWrite(0x01FF, 123); - cr_assert_eq(apu->_map.Page1->_data[0x00FF], 123); + cr_assert_eq(apu->_map.Page1._data[0x00FF], 123); } Test(_internalWrite, Memory) @@ -109,7 +109,7 @@ Test(_internalWrite, Memory) auto apu = Init().second.apu; apu->_internalWrite(0x0789, 123); - cr_assert_eq(apu->_map.Memory->_data[0x0589], 123); + cr_assert_eq(apu->_map.Memory._data[0x0589], 123); } Test(_internalWrite, IPL) @@ -117,7 +117,7 @@ Test(_internalWrite, IPL) auto apu = Init().second.apu; apu->_internalWrite(0xFFF0, 123); - cr_assert_eq(apu->_map.Memory->_data[0x0030], 123); + cr_assert_eq(apu->_map.IPL._data[0x0030], 123); } Test(_internalWrite, Invalid) diff --git a/tests/CPU/testInterupts.cpp b/tests/CPU/testInterupts.cpp index dd6339b..2856cb3 100644 --- a/tests/CPU/testInterupts.cpp +++ b/tests/CPU/testInterupts.cpp @@ -16,7 +16,7 @@ Test(CPU_emulated, BRK) pair.second.cartridge->header.emulationInterrupts.brk = 0x123u; pair.second.cpu->_registers.p.d = true; pair.second.cpu->_registers.pc = 0x156u; - cr_assert_eq(pair.second.cpu->BRK(), 0); + pair.second.cpu->BRK(); cr_assert_eq(pair.second.cpu->_registers.pc, 0x123u); cr_assert_eq(pair.second.cpu->_registers.p.i, 1, "pair.second.cpu->_registers.p.i mmust be equal to 1 but it was %d", pair.second.cpu->_registers.p.i); cr_assert_eq(pair.second.cpu->_registers.p.d, false); @@ -28,7 +28,7 @@ Test(CPU_native, BRK) pair.second.cpu->_isEmulationMode = false; pair.second.cartridge->header.nativeInterrupts.brk = 0x123u; pair.second.cpu->_registers.pc = 0x156u; - cr_assert_eq(pair.second.cpu->BRK(), 1); + pair.second.cpu->BRK(); cr_assert_eq(pair.second.cpu->_registers.pc, 0x123u); cr_assert_eq(pair.second.cpu->_registers.p.i, true); cr_assert_eq(pair.second.cpu->_registers.p.d, false); diff --git a/tests/CPU/testStore.cpp b/tests/CPU/testStore.cpp index 914ecfc..f7dbc5f 100644 --- a/tests/CPU/testStore.cpp +++ b/tests/CPU/testStore.cpp @@ -8,13 +8,160 @@ #include "../tests.hpp" #include "../../sources/SNES.hpp" using namespace ComSquare; -// -//Test(STA, 8bits) -//{ -// auto pair = Init(); -// pair.second.cpu->_registers.p.m = false; -// pair.second.cpu->_registers.a = 0x11; -// pair.second.cpu->STA(0x0); -// auto data = pair.second.wram->_data[0]; -// cr_assert_eq(data, 0x11, "The stored value should be 0x11 but it was 0x%x.", data); -//} \ No newline at end of file + +Test(STA, 8bits) +{ + auto pair = Init(); + pair.second.cpu->_registers.p.m = true; + pair.second.cpu->_registers.a = 0x11; + pair.second.cpu->STA(0x0); + auto data = pair.second.wram->_data[0]; + cr_assert_eq(data, 0x11, "The stored value should be 0x11 but it was 0x%x.", data); +} + +Test(STA, 16bits) +{ + auto pair = Init(); + pair.second.cpu->_registers.p.m = false; + pair.second.cpu->_registers.a = 0x11AB; + pair.second.cpu->STA(0x0); + auto data = pair.second.wram->_data[0] + (pair.second.wram->_data[1] << 8u); + cr_assert_eq(data, 0x11AB, "The stored value should be 0x11AB but it was 0x%x.", data); +} + +Test(STX, 8bits) +{ + auto pair = Init(); + pair.second.cpu->_registers.p.x_b = true; + pair.second.cpu->_registers.x = 0x11; + pair.second.cpu->STX(0x0); + auto data = pair.second.wram->_data[0]; + cr_assert_eq(data, 0x11, "The stored value should be 0x11 but it was 0x%x.", data); +} + +Test(STX, 16bits) +{ + auto pair = Init(); + pair.second.cpu->_registers.p.x_b = false; + pair.second.cpu->_registers.x = 0x11AB; + pair.second.cpu->STX(0x0); + auto data = pair.second.wram->_data[0] + (pair.second.wram->_data[1] << 8u); + cr_assert_eq(data, 0x11AB, "The stored value should be 0x11AB but it was 0x%x.", data); +} + +Test(STY, 8bits) +{ + auto pair = Init(); + pair.second.cpu->_registers.p.x_b = true; + pair.second.cpu->_registers.y = 0x11; + pair.second.cpu->STY(0x0); + auto data = pair.second.wram->_data[0]; + cr_assert_eq(data, 0x11, "The stored value should be 0x11 but it was 0x%x.", data); +} + +Test(STY, 16bits) +{ + auto pair = Init(); + pair.second.cpu->_registers.p.x_b = false; + pair.second.cpu->_registers.y = 0x11AB; + pair.second.cpu->STY(0x0); + auto data = pair.second.wram->_data[0] + (pair.second.wram->_data[1] << 8u); + cr_assert_eq(data, 0x11AB, "The stored value should be 0x11AB but it was 0x%x.", data); +} + +Test(STZ, 8bits) +{ + auto pair = Init(); + pair.second.cpu->_registers.p.m = true; + pair.second.wram->_data[0] = 0x11; + pair.second.cpu->STZ(0x0); + auto data = pair.second.wram->_data[0]; + cr_assert_eq(data, 0x00, "The stored value should be 0x00 but it was 0x%x.", data); +} + +Test(STZ, 16bits) +{ + auto pair = Init(); + pair.second.cpu->_registers.p.m = false; + pair.second.wram->_data[0] = 0x11; + pair.second.wram->_data[1] = 0x11; + pair.second.cpu->STZ(0x0); + auto data = pair.second.wram->_data[0] + (pair.second.wram->_data[1] << 8u); + cr_assert_eq(data, 0x00, "The stored value should be 0x00 but it was 0x%x.", data); +} + +Test(LDA, 8bits) +{ + auto pair = Init(); + pair.second.cpu->_registers.p.m = true; + pair.second.wram->_data[0] = 0x01; + pair.second.cpu->LDA(0x0); + auto data = pair.second.cpu->_registers.a; + cr_assert_eq(data, 0x01, "The stored value should be 0x01 but it was 0x%x.", data); + cr_assert_eq(pair.second.cpu->_registers.p.z, false, "The zero flag register should not be set."); + cr_assert_eq(pair.second.cpu->_registers.p.n, false, "The negative flag register should not be set."); +} + +Test(LDA, 8bitsNegative) +{ + auto pair = Init(); + pair.second.cpu->_registers.p.m = true; + pair.second.wram->_data[0] = 0x11; + pair.second.cpu->LDA(0x0); + auto data = pair.second.cpu->_registers.a; + cr_assert_eq(data, 0x11, "The stored value should be 0x11 but it was 0x%x.", data); + cr_assert_eq(pair.second.cpu->_registers.p.z, false, "The zero flag register should not be set."); + cr_assert_eq(pair.second.cpu->_registers.p.n, true, "The negative flag register should be set."); +} + +Test(LDA, 8bitsZero) +{ + auto pair = Init(); + pair.second.cpu->_registers.p.m = true; + pair.second.wram->_data[0] = 0x00; + pair.second.wram->_data[1] = 0x11; + pair.second.cpu->LDA(0x0); + auto data = pair.second.cpu->_registers.a; + cr_assert_eq(data, 0x00, "The stored value should be 0x00 but it was 0x%x.", data); + cr_assert_eq(pair.second.cpu->_registers.p.z, true, "The zero flag register should be set."); + cr_assert_eq(pair.second.cpu->_registers.p.n, false, "The negative flag register should not be set."); +} + +Test(LDA, 16bits) +{ + auto pair = Init(); + pair.second.cpu->_registers.p.m = false; + pair.second.wram->_data[0] = 0xAB; + pair.second.wram->_data[1] = 001; + pair.second.cpu->LDA(0x0); + auto data = pair.second.cpu->_registers.a; + cr_assert_eq(data, 0x01AB, "The stored value should be 0x01AB but it was 0x%x.", data); + cr_assert_eq(pair.second.cpu->_registers.p.z, false, "The zero flag register should not be set."); + cr_assert_eq(pair.second.cpu->_registers.p.n, false, "The negative flag register should not be set."); +} + +Test(LDA, 16bitsNegative) +{ + auto pair = Init(); + pair.second.cpu->_registers.p.m = false; + pair.second.wram->_data[0] = 0xAB; + pair.second.wram->_data[1] = 0x11; + pair.second.cpu->LDA(0x0); + auto data = pair.second.cpu->_registers.a; + cr_assert_eq(data, 0x11AB, "The stored value should be 0x11AB but it was 0x%x.", data); + cr_assert_eq(pair.second.cpu->_registers.p.z, false, "The zero flag register should not be set."); + cr_assert_eq(pair.second.cpu->_registers.p.n, true, "The negative flag register should be set."); +} + +Test(LDA, 16bitsZero) +{ + auto pair = Init(); + pair.second.cpu->_registers.p.m = false; + pair.second.wram->_data[0] = 0x00; + pair.second.wram->_data[1] = 0x00; + pair.second.cpu->LDA(0x0); + auto data = pair.second.cpu->_registers.a; + cr_assert_eq(data, 0x0000, "The stored value should be 0x0000 but it was 0x%x.", data); + cr_assert_eq(pair.second.cpu->_registers.p.z, true, "The zero flag register should be set."); + cr_assert_eq(pair.second.cpu->_registers.p.n, false, "The negative flag register should not be set."); +}