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);