From 4d30a3562096e1b499b0be8fa5fbc38d29183bba Mon Sep 17 00:00:00 2001 From: AnonymusRaccoon Date: Thu, 13 Feb 2020 13:55:01 +0100 Subject: [PATCH] Changing timing management --- CMakeLists.txt | 4 +- sources/CPU/CPU.cpp | 104 +++++++----------- sources/CPU/CPU.hpp | 41 ++++--- sources/CPU/Instructions/Interrupts.cpp | 19 ++-- .../Instructions/MathematicalOperations.cpp | 3 +- .../CPU/Instructions/MemoryInstructions.cpp | 18 +++ tests/CPU/testInterupts.cpp | 4 +- 7 files changed, 101 insertions(+), 92 deletions(-) create mode 100644 sources/CPU/Instructions/MemoryInstructions.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6243844..d3937f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,6 +58,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 ) @@ -113,7 +114,8 @@ add_executable(ComSquare sources/CPU/Instructions/CommonInstructions.hpp sources/Exceptions/InvalidOpcode.hpp sources/CPU/Instructions/Interrupts.cpp - sources/CPU/Instructions/MathematicalOperations.cpp) + sources/CPU/Instructions/MathematicalOperations.cpp + sources/CPU/Instructions/MemoryInstructions.cpp) target_link_libraries(ComSquare sfml-graphics diff --git a/sources/CPU/CPU.cpp b/sources/CPU/CPU.cpp index 40246b7..54403c0 100644 --- a/sources/CPU/CPU.cpp +++ b/sources/CPU/CPU.cpp @@ -190,59 +190,74 @@ 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; 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 +276,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 +298,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 +318,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 +328,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 +335,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 +346,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 +356,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 +405,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 +414,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 +432,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 6980eea..b8d46c9 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -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,21 @@ 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 }; //! @brief The main CPU @@ -220,8 +235,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,30 +281,30 @@ 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); public: explicit CPU(std::shared_ptr bus, Cartridge::Header &cartridgeHeader); //! @brief This function continue to execute the Cartridge code. 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..56bba9a --- /dev/null +++ b/sources/CPU/Instructions/MemoryInstructions.cpp @@ -0,0 +1,18 @@ +// +// 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); + } + } +} \ No newline at end of file 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);