Marge master into PPU

This commit is contained in:
Clément Le Bihan
2020-03-28 23:35:38 +01:00
38 changed files with 1512 additions and 971 deletions

View File

@@ -93,6 +93,8 @@ add_executable(unit_tests
tests/CPU/TransferRegisters.cpp
sources/CPU/AddressingModes.cpp
sources/Models/Components.hpp
sources/CPU/Instruction.hpp
sources/Exceptions/DebuggableError.hpp
)
# include criterion & coverage
@@ -200,7 +202,13 @@ add_executable(ComSquare
sources/Debugger/MemoryBusDebug.cpp
sources/Debugger/MemoryBusDebug.hpp
sources/Debugger/ClosableWindow.hpp
sources/Models/Components.hpp sources/Debugger/CGramDebug.cpp sources/Debugger/CGramDebug.hpp)
sources/Models/Components.hpp
sources/CPU/Instruction.hpp
sources/Exceptions/DebuggableError.hpp
sources/Models/Components.hpp
sources/Debugger/CGramDebug.cpp
sources/Debugger/CGramDebug.hpp
)
target_compile_definitions(ComSquare PUBLIC DEBUGGER_ENABLED)

View File

@@ -1,5 +1,6 @@
<RCC>
<qresource prefix="resources">
<file>icons/continue.svg</file>
<file>icons/step.svg</file>
<file>Logo.png</file>
<file>icons/play.svg</file>

View File

@@ -0,0 +1 @@
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="level-down-alt" class="svg-inline--fa fa-level-down-alt fa-w-10" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M313.553 392.331L209.587 504.334c-9.485 10.214-25.676 10.229-35.174 0L70.438 392.331C56.232 377.031 67.062 352 88.025 352H152V80H68.024a11.996 11.996 0 0 1-8.485-3.515l-56-56C-4.021 12.926 1.333 0 12.024 0H208c13.255 0 24 10.745 24 24v328h63.966c20.878 0 31.851 24.969 17.587 40.331z"></path></svg>

After

Width:  |  Height:  |  Size: 531 B

View File

@@ -7,47 +7,56 @@
namespace ComSquare::CPU
{
uint24_t CPU::_getImmediateAddr8Bits()
{
uint24_t ret = this->_registers.pac;
this->_registers.pc++;
return ret;
}
uint24_t CPU::_getImmediateAddrForA()
{
uint24_t effective = this->_registers.pac++;
uint24_t effective = this->_registers.pac;
this->_registers.pc++;
if (!this->_registers.p.m)
this->_registers.pac++;
this->_registers.pc++;
return effective;
}
uint24_t CPU::_getImmediateAddrForX()
{
uint24_t effective = this->_registers.pac++;
uint24_t effective = this->_registers.pac;
this->_registers.pc++;
if (!this->_registers.p.x_b)
this->_registers.pac++;
this->_registers.pc++;
return effective;
}
uint24_t CPU::_getDirectAddr()
{
uint8_t addr = this->_bus->read(this->_registers.pac++);
uint8_t addr = this->readPC();
return this->_registers.d + addr;
}
uint24_t CPU::_getAbsoluteAddr()
{
uint24_t addr = this->_registers.dbr << 16u;
addr += this->_bus->read(this->_registers.pac++);
addr += this->_bus->read(this->_registers.pac++) << 8u;
addr += this->readPC();
addr += this->readPC() << 8u;
return addr;
}
uint24_t CPU::_getAbsoluteLongAddr()
{
uint24_t addr = this->_bus->read(this->_registers.pac++);
addr += this->_bus->read(this->_registers.pac++) << 8u;
addr += this->_bus->read(this->_registers.pac++) << 16u;
uint24_t addr = this->readPC();
addr += this->readPC() << 8u;
addr += this->readPC() << 16u;
return addr;
}
uint24_t CPU::_getDirectIndirectIndexedYAddr()
{
uint16_t dp = this->_bus->read(this->_registers.pac++) + this->_registers.d;
uint16_t dp = this->readPC() + this->_registers.d;
uint24_t base = this->_bus->read(dp);
base += this->_bus->read(dp + 1) << 8u;
base += this->_registers.dbr << 16u;
@@ -58,7 +67,7 @@ namespace ComSquare::CPU
uint24_t CPU::_getDirectIndirectIndexedYLongAddr()
{
uint16_t dp = this->_bus->read(this->_registers.pac++) + this->_registers.d;
uint16_t dp = this->readPC() + this->_registers.d;
uint24_t base = this->_bus->read(dp);
base += this->_bus->read(dp + 1) << 8u;
base += this->_bus->read(dp + 2) << 16u;
@@ -67,7 +76,7 @@ namespace ComSquare::CPU
uint24_t CPU::_getDirectIndirectIndexedXAddr()
{
uint16_t dp = this->_bus->read(this->_registers.pac++) + this->_registers.d;
uint16_t dp = this->readPC() + this->_registers.d;
dp += this->_registers.x;
uint24_t base = this->_bus->read(dp);
base += this->_bus->read(dp + 1) << 8u;
@@ -77,22 +86,22 @@ namespace ComSquare::CPU
uint24_t CPU::_getDirectIndexedByXAddr()
{
uint16_t dp = this->_bus->read(this->_registers.pac++) + this->_registers.d;
uint16_t dp = this->readPC() + this->_registers.d;
dp += this->_registers.x;
return dp;
}
uint24_t CPU::_getDirectIndexedByYAddr()
{
uint16_t dp = this->_bus->read(this->_registers.pac++) + this->_registers.d;
uint16_t dp = this->readPC() + this->_registers.d;
dp += this->_registers.y;
return dp;
}
uint24_t CPU::_getAbsoluteIndexedByXAddr()
{
uint16_t abs = this->_bus->read(this->_registers.pac++);
abs += this->_bus->read(this->_registers.pac++) << 8u;
uint16_t abs = this->readPC();
abs += this->readPC() << 8u;
uint24_t effective = abs + (this->_registers.dbr << 16u);
if ((effective & 0x80000000u) == (((effective + this->_registers.x) & 0x80000000u)))
this->_hasIndexCrossedPageBoundary = true;
@@ -101,8 +110,8 @@ namespace ComSquare::CPU
uint24_t CPU::_getAbsoluteIndexedByYAddr()
{
uint16_t abs = this->_bus->read(this->_registers.pac++);
abs += this->_bus->read(this->_registers.pac++) << 8u;
uint16_t abs = this->readPC();
abs += this->readPC() << 8u;
uint24_t effective = abs + (this->_registers.dbr << 16u);
if ((effective & 0x80000000u) == (((effective + this->_registers.y) & 0x80000000u)))
this->_hasIndexCrossedPageBoundary = true;
@@ -111,32 +120,32 @@ namespace ComSquare::CPU
uint24_t CPU::_getAbsoluteIndexedByXLongAddr()
{
uint24_t lng = this->_bus->read(this->_registers.pac++);
lng += this->_bus->read(this->_registers.pac++) << 8u;
lng += this->_bus->read(this->_registers.pac++) << 16u;
uint24_t lng = this->readPC();
lng += this->readPC() << 8u;
lng += this->readPC() << 16u;
return lng + this->_registers.x;
}
uint24_t CPU::_getProgramCounterRelativeAddr()
{
uint24_t pc = this->_registers.pac;
int8_t mod = this->_bus->read(this->_registers.pac++);
int8_t mod = this->readPC();
return pc + mod;
}
uint24_t CPU::_getProgramCounterRelativeLongAddr()
{
uint24_t pc = this->_registers.pac;
uint8_t val1 = this->_bus->read(this->_registers.pac++);
uint8_t val2 = this->_bus->read(this->_registers.pac++);
uint8_t val1 = this->readPC();
uint8_t val2 = this->readPC();
int16_t mod = val2 > 0x7F ? (static_cast<char>(val2) * 256 - val1) : (val1 | val2 << 8u);
return pc + mod;
}
uint24_t CPU::_getAbsoluteIndirectAddr()
{
uint16_t abs = this->_bus->read(this->_registers.pac++);
abs += this->_bus->read(this->_registers.pac++) << 8u;
uint16_t abs = this->readPC();
abs += this->readPC() << 8u;
uint24_t effective = this->_bus->read(abs);
effective += this->_bus->read(abs + 1) << 8u;
return effective;
@@ -144,8 +153,8 @@ namespace ComSquare::CPU
uint24_t CPU::_getAbsoluteIndirectIndexedByXAddr()
{
uint24_t abs = this->_bus->read(this->_registers.pac++);
abs += this->_bus->read(this->_registers.pac++) << 8u;
uint24_t abs = this->readPC();
abs += this->readPC() << 8u;
abs += this->_registers.x;
uint24_t effective = this->_bus->read(abs);
effective += this->_bus->read(abs + 1) << 8u;
@@ -154,7 +163,7 @@ namespace ComSquare::CPU
uint24_t CPU::_getDirectIndirectAddr()
{
uint16_t dp = this->_bus->read(this->_registers.pac++) + this->_registers.d;
uint16_t dp = this->readPC() + this->_registers.d;
uint24_t effective = this->_bus->read(dp);
effective += this->_bus->read(dp + 1) << 8u;
effective += this->_registers.dbr << 16u;
@@ -163,7 +172,7 @@ namespace ComSquare::CPU
uint24_t CPU::_getDirectIndirectLongAddr()
{
uint16_t dp = this->_bus->read(this->_registers.pac++) + this->_registers.d;
uint16_t dp = this->readPC() + this->_registers.d;
uint24_t effective = this->_bus->read(dp);
effective += this->_bus->read(++dp) << 8u;
effective += this->_bus->read(++dp) << 16u;
@@ -172,12 +181,12 @@ namespace ComSquare::CPU
uint24_t CPU::_getStackRelativeAddr()
{
return this->_bus->read(this->_registers.pac++) + this->_registers.s;
return this->readPC() + this->_registers.s;
}
uint24_t CPU::_getStackRelativeIndirectIndexedYAddr()
{
uint24_t base = this->_bus->read(this->_registers.pac++) + this->_registers.s;
uint24_t base = this->readPC() + this->_registers.s;
base += this->_registers.dbr << 16u;
return base + this->_registers.y;
}

View File

@@ -195,202 +195,114 @@ namespace ComSquare::CPU
}
}
uint8_t CPU::readPC()
{
uint8_t ret = this->_bus->read(this->_registers.pac);
this->_registers.pc++;
return ret;
}
unsigned CPU::update()
{
unsigned cycles = 0;
for (int i = 0; i < 0xFF; i++)
cycles += this->_executeInstruction(this->_bus->read(this->_registers.pac++));
cycles += this->_executeInstruction(this->readPC());
return cycles;
}
unsigned CPU::_executeInstruction(uint8_t opcode)
{
Instruction instruction = this->_instructions[opcode];
uint24_t valueAddr = 0;
this->_hasIndexCrossedPageBoundary = false;
uint24_t addr;
switch (opcode) {
case Instructions::BRK: this->BRK(); return 7 + !this->_isEmulationMode;
switch (instruction.addressingMode) {
case Implied:
break;
case Immediate8bits:
valueAddr = this->_getImmediateAddr8Bits();
break;
case ImmediateForA:
valueAddr = this->_getImmediateAddrForA();
break;
case ImmediateForX:
valueAddr = this->_getImmediateAddrForX();
break;
case Instructions::COP: this->COP(); return 7 + !this->_isEmulationMode;
// TODO implement the relative addressing mode
// TODO implement the relative long addressing mode
case Instructions::RTI: this->RTI(); return 6 + !this->_isEmulationMode;
case Absolute:
valueAddr = this->_getAbsoluteAddr();
break;
case AbsoluteLong:
valueAddr = this->_getAbsoluteLongAddr();
break;
case AbsoluteIndirect:
valueAddr = this->_getAbsoluteIndirectAddr();
break;
case Instructions::ADC_IM: this->ADC(this->_getImmediateAddrForA()); 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;
//TODO implement absolute indirect long addressing mode
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 DirectPage:
valueAddr = this->_getDirectAddr();
break;
case DirectPageIndirect:
valueAddr = this->_getDirectIndirectAddr();
break;
case DirectPageIndirectLong:
valueAddr = this->_getDirectIndirectLongAddr();
break;
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 DirectPageIndexedByX:
valueAddr = this->_getDirectIndexedByXAddr();
break;
case DirectPageIndexedByY:
valueAddr = this->_getDirectIndexedByYAddr();
break;
case DirectPageIndirectIndexedByX:
valueAddr = this->_getDirectIndirectIndexedXAddr();
break;
case DirectPageIndirectIndexedByY:
valueAddr = this->_getDirectIndirectIndexedYAddr();
break;
case DirectPageIndirectIndexedByYLong:
valueAddr = this->_getDirectIndirectIndexedYLongAddr();
break;
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 AbsoluteIndexedByX:
valueAddr = this->_getAbsoluteIndexedByXAddr();
break;
case AbsoluteIndexedByXLong:
valueAddr = this->_getAbsoluteIndexedByXLongAddr();
break;
case AbsoluteIndexedByY:
valueAddr = this->_getAbsoluteIndexedByYAddr();
break;
case Instructions::STZ_ABS: this->STZ(this->_getAbsoluteAddr()); return 4 + !this->_registers.p.m;
case Instructions::STZ_DP: this->STZ(this->_getDirectAddr()); return 3 + !this->_registers.p.m + this->_registers.dl != 0;
case Instructions::STZ_ABSX: this->STZ(this->_getAbsoluteIndexedByXAddr()); return 3 + !this->_registers.p.m + this->_registers.dl != 0;
case Instructions::STZ_DPX: this->STZ(this->_getDirectIndexedByXAddr()); return 4 + !this->_registers.p.m + this->_registers.dl != 0;
case Instructions::LDA_IM: this->LDA(this->_getImmediateAddrForA()); 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;
case Instructions::LDX_IM: this->LDX(this->_getImmediateAddrForX()); return 2 + !this->_registers.p.m;
case Instructions::LDX_ABS: this->LDX(this->_getAbsoluteAddr()); return 4 + !this->_registers.p.m;
case Instructions::LDX_DP: this->LDX(this->_getDirectAddr()); return 3 + !this->_registers.p.m + this->_registers.dl != 0;
case Instructions::LDX_ABSY: this->LDX(this->_getAbsoluteIndexedByYAddr()); return 4 + !this->_registers.p.m + this->_hasIndexCrossedPageBoundary;
case Instructions::LDX_DPY: this->LDX(this->_getDirectIndexedByYAddr()); return 4 + !this->_registers.p.m + this->_registers.dl != 0;
case Instructions::LDY_IM: this->LDY(this->_getImmediateAddrForX()); return 2 + !this->_registers.p.m;
case Instructions::LDY_ABS: this->LDY(this->_getAbsoluteAddr()); return 4 + !this->_registers.p.m;
case Instructions::LDY_DP: this->LDY(this->_getDirectAddr()); return 3 + !this->_registers.p.m + this->_registers.dl != 0;
case Instructions::LDY_ABSY: this->LDY(this->_getAbsoluteIndexedByYAddr()); return 4 + !this->_registers.p.m + this->_hasIndexCrossedPageBoundary;
case Instructions::LDY_DPY: this->LDY(this->_getDirectIndexedByYAddr()); return 4 + !this->_registers.p.m + this->_registers.dl != 0;
case Instructions::SEP: this->SEP(this->_bus->read(this->_registers.pc++)); return 3;
case Instructions::REP: this->REP(this->_bus->read(this->_registers.pc++)); 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::PLA: this->PLA(); return 4 + !this->_registers.p.m;
case Instructions::PLB: this->PLB(); return 4;
case Instructions::PLD: this->PLD(); return 5;
case Instructions::PLP: this->PLP(); return 4;
case Instructions::PLX: this->PLX(); return 4 + !this->_registers.p.x_b;
case Instructions::PLY: this->PLY(); return 4 + !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->JSL(this->_getAbsoluteLongAddr()); return 8;
case Instructions::CLC: this->CLC(); return 2;
case Instructions::CLI: this->CLI(); return 2;
case Instructions::CLD: this->CLD(); return 2;
case Instructions::CLV: this->CLV(); return 2;
case Instructions::SEC: this->SEC(); return 2;
case Instructions::SED: this->SED(); return 2;
case Instructions::SEI: this->SEI(); return 2;
case Instructions::AND_IM: this->AND(this->_getImmediateAddrForA()); return 2 + !this->_registers.p.m;
case Instructions::AND_ABS: this->AND(this->_getAbsoluteAddr()); return 4 + !this->_registers.p.m;
case Instructions::AND_ABSl: this->AND(this->_getAbsoluteLongAddr()); return 5 + !this->_registers.p.m;
case Instructions::AND_DP: this->AND(this->_getDirectAddr()); return 3 + !this->_registers.p.m + this->_registers.dl != 0;
case Instructions::AND_DPi: this->AND(this->_getDirectIndirectAddr()); return 5 + !this->_registers.p.m + this->_registers.dl != 0;
case Instructions::AND_DPil: this->AND(this->_getDirectIndirectLongAddr()); return 6 + !this->_registers.p.m + this->_registers.dl != 0;
case Instructions::AND_ABSX: this->AND(this->_getAbsoluteIndexedByXAddr()); return 4 + !this->_registers.p.m + this->_hasIndexCrossedPageBoundary;
case Instructions::AND_ABSXl:this->AND(this->_getAbsoluteIndexedByXLongAddr()); return 5 + !this->_registers.p.m;
case Instructions::AND_ABSY: this->AND(this->_getAbsoluteIndexedByYAddr()); return 4 + !this->_registers.p.m + this->_hasIndexCrossedPageBoundary;
case Instructions::AND_DPX: this->AND(this->_getDirectIndexedByXAddr()); return 4 + !this->_registers.p.m + this->_registers.dl != 0;
case Instructions::AND_DPXi: this->AND(this->_getDirectIndirectIndexedXAddr()); return 6 + !this->_registers.p.m + this->_registers.dl != 0;
case Instructions::AND_DPYi: this->AND(this->_getDirectIndirectIndexedYAddr()); return 5 + !this->_registers.p.m + this->_registers.dl != 0 + this->_hasIndexCrossedPageBoundary;
case Instructions::AND_DPYil:this->AND(this->_getDirectIndirectIndexedYLongAddr()); return 6 + !this->_registers.p.m + this->_registers.dl != 0;
case Instructions::AND_SR: this->AND(this->_getStackRelativeAddr()); return 4 + !this->_registers.p.m;
case Instructions::AND_SRYi: this->AND(this->_getStackRelativeIndirectIndexedYAddr()); return 7 + !this->_registers.p.m;
case Instructions::XCE: this->XCE(); return 2;
case Instructions::SBC_IM: this->SBC(this->_getImmediateAddrForA()); return 2 + !this->_registers.p.m;
case Instructions::SBC_ABS: this->SBC(this->_getAbsoluteAddr()); return 4 + !this->_registers.p.m;
case Instructions::SBC_ABSl: this->SBC(this->_getAbsoluteLongAddr()); return 5 + !this->_registers.p.m;
case Instructions::SBC_DP: this->SBC(this->_getDirectAddr()); return 3 + !this->_registers.p.m + this->_registers.dl != 0;
case Instructions::SBC_DPi: this->SBC(this->_getDirectIndirectAddr()); return 5 + !this->_registers.p.m + this->_registers.dl != 0;
case Instructions::SBC_DPil: this->SBC(this->_getDirectIndirectLongAddr()); return 6 + !this->_registers.p.m + this->_registers.dl != 0;
case Instructions::SBC_ABSX: this->SBC(this->_getAbsoluteIndexedByXAddr()); return 4 + !this->_registers.p.m + this->_hasIndexCrossedPageBoundary;
case Instructions::SBC_ABSXl:this->SBC(this->_getAbsoluteIndexedByXLongAddr()); return 5 + !this->_registers.p.m;
case Instructions::SBC_ABSY: this->SBC(this->_getAbsoluteIndexedByYAddr()); return 4 + !this->_registers.p.m + this->_hasIndexCrossedPageBoundary;
case Instructions::SBC_DPX: this->SBC(this->_getDirectIndexedByXAddr()); return 4 + !this->_registers.p.m + this->_registers.dl != 0;
case Instructions::SBC_DPXi: this->SBC(this->_getDirectIndirectIndexedXAddr()); return 6 + !this->_registers.p.m + this->_registers.dl != 0;
case Instructions::SBC_DPYi: this->SBC(this->_getDirectIndirectIndexedYAddr()); return 5 + !this->_registers.p.m + this->_registers.dl != 0 + this->_hasIndexCrossedPageBoundary;
case Instructions::SBC_DPYil:this->SBC(this->_getDirectIndirectIndexedYLongAddr()); return 6 + !this->_registers.p.m + this->_registers.dl != 0;
case Instructions::SBC_SR: this->SBC(this->_getStackRelativeAddr()); return 4 + !this->_registers.p.m;
case Instructions::SBC_SRYi: this->SBC(this->_getStackRelativeIndirectIndexedYAddr()); return 7 + !this->_registers.p.m;
case Instructions::TAX: this->TAX(); return 2;
case Instructions::TAY: this->TAY(); return 2;
case Instructions::TXS: this->TXS(); return 2;
case Instructions::INX: this->INX(); return 2;
case Instructions::INY: this->INY(); return 2;
case Instructions::CPX_IM: this->CPX(this->_getImmediateAddrForX()); return 2 + !this->_registers.p.m;
case Instructions::CPX_ABS: this->CPX(this->_getAbsoluteAddr()); return 4 + !this->_registers.p.m;
case Instructions::CPX_DP: this->CPX(this->_getDirectAddr()); return 3 + !this->_registers.p.m + this->_registers.dl != 0;
case Instructions::CPY_IM: this->CPY(this->_getImmediateAddrForX()); return 2 + !this->_registers.p.m;
case Instructions::CPY_ABS: this->CPY(this->_getAbsoluteAddr()); return 4 + !this->_registers.p.m;
case Instructions::CPY_DP: this->CPY(this->_getDirectAddr()); return 3 + !this->_registers.p.m + this->_registers.dl != 0;
case Instructions::BCC: return this->BCC(this->_registers.pc++) + 2 + this->_isEmulationMode;
case Instructions::BCS: return this->BCS(this->_registers.pc++) + 2 + this->_isEmulationMode;
case Instructions::BEQ: return this->BEQ(this->_registers.pc++) + 2 + this->_isEmulationMode;
case Instructions::BNE: return this->BNE(this->_registers.pc++) + 2 + this->_isEmulationMode;
case Instructions::BMI: return this->BMI(this->_registers.pc++) + 2 + this->_isEmulationMode;
case Instructions::BPL: return this->BPL(this->_registers.pc++) + 2 + this->_isEmulationMode;
case Instructions::BVC: return this->BVC(this->_registers.pc++) + 2 + this->_isEmulationMode;
case Instructions::BVS: return this->BVS(this->_registers.pc++) + 2 + this->_isEmulationMode;
case Instructions::BRA: this->BRA(this->_registers.pc++); return 3 + this->_isEmulationMode;
case Instructions::BRL: this->BRL(this->_registers.pc); this->_registers.pc += 2; return 4;
case Instructions::JMP_ABS: addr = this->_getAbsoluteAddr(); this->JMP(addr); return 3;
case Instructions::JMP_ABSi: addr = this->_getAbsoluteIndirectAddr(); this->JMP(addr); return 3;
case Instructions::JMP_ABSXi: addr = this->_getAbsoluteIndexedByXAddr(); this->JMP(addr); return 3;
case Instructions::JML_ABSl: addr = this->_getAbsoluteLongAddr(); this->JML(addr); return 3;
//case Instructions::JML_ABSil: this->JML(this->_getAbsoluteLong()); return 3;
case StackRelative:
valueAddr = this->_getStackRelativeAddr();
break;
case StackRelativeIndirectIndexedByY:
valueAddr = this->_getStackRelativeIndirectIndexedYAddr();
break;
case ProgramCounterRelative:
valueAddr = this->_getProgramCounterRelativeAddr();
break;
case ProgramCounterRelativeLong:
valueAddr = this->_getProgramCounterRelativeLongAddr();
break;
case AbsoluteIndirectIndexedByX:
valueAddr = this->_getAbsoluteIndirectIndexedByXAddr();
break;
default:
throw InvalidOpcode("CPU", opcode);
break;
}
return instruction.cycleCount + (this->*instruction.call)(valueAddr, instruction.addressingMode);
}
void CPU::_push(uint8_t data)

View File

@@ -10,6 +10,7 @@
#include "../Models/Int24.hpp"
#include "../Cartridge/Cartridge.hpp"
#include "../Memory/AMemory.hpp"
#include "Instruction.hpp"
namespace ComSquare::CPU
{
@@ -180,189 +181,6 @@ namespace ComSquare::CPU
uint8_t joy4h;
};
//! @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.
enum Instructions
{
BRK = 0x00,
COP = 0x02,
RTI = 0x40,
ADC_DPXi = 0x61,
ADC_SR = 0x63,
ADC_DP = 0x65,
ADC_DPil = 0x67,
ADC_IM = 0x69,
ADC_ABS = 0x6D,
ADC_ABSl = 0x6F,
ADC_DPYi = 0x71,
ADC_DPi = 0x72,
ADC_SRYi = 0x73,
ADC_DPX = 0x75,
ADC_DPYil = 0x77,
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,
LDX_IM = 0xA2,
LDX_ABS = 0xAE,
LDX_DP = 0xA6,
LDX_ABSY = 0xBE,
LDX_DPY = 0xB6,
LDY_IM = 0xA0,
LDY_ABS = 0xAC,
LDY_DP = 0xA4,
LDY_ABSY = 0xBC,
LDY_DPY = 0xB4,
SEP = 0xE2,
REP = 0xC2,
PHA = 0x48,
PHB = 0x8B,
PHD = 0x0B,
PHK = 0x4B,
PHP = 0x08,
PHX = 0xDA,
PHY = 0x5A,
PLA = 0x68,
PLB = 0xAB,
PLD = 0x2B,
PLP = 0x28,
PLX = 0xFA,
PLY = 0x7A,
JSR_ABS = 0x20,
JSR_ABSXi = 0xFC,
JSL = 0x22,
CLC = 0x18,
CLI = 0x58,
CLD = 0xD8,
CLV = 0xB8,
SEC = 0x38,
SEI = 0x78,
SED = 0xF8,
AND_IM = 0x29,
AND_ABS = 0x2D,
AND_ABSl = 0x2F,
AND_DP = 0x25,
AND_DPi = 0x32,
AND_DPil = 0x27,
AND_ABSX = 0x3D,
AND_ABSXl = 0x3F,
AND_ABSY = 0x39,
AND_DPX = 0x35,
AND_DPXi = 0x21,
AND_DPYi = 0x31,
AND_DPYil = 0x37,
AND_SR = 0x23,
AND_SRYi = 0x33,
XCE = 0xFB,
SBC_IM = 0xE9,
SBC_ABS = 0xED,
SBC_ABSl = 0xEF,
SBC_DP = 0xE5,
SBC_DPi = 0xF2,
SBC_DPil = 0xE7,
SBC_ABSX = 0xFD,
SBC_ABSXl = 0xFF,
SBC_ABSY = 0xF9,
SBC_DPX = 0xF5,
SBC_DPXi = 0xE1,
SBC_DPYi = 0xF1,
SBC_DPYil = 0xF7,
SBC_SR = 0xE3,
SBC_SRYi = 0xF3,
TAX = 0xAA,
TAY = 0xA8,
TXS = 0x9A,
INX = 0xE8,
INY = 0xC8,
CPX_IM = 0xE0,
CPX_ABS = 0xEC,
CPX_DP = 0xE4,
CPY_IM = 0xC0,
CPY_ABS = 0xCC,
CPY_DP = 0xC4,
BCC = 0x90,
BCS = 0xB0,
BEQ = 0xF0,
BNE = 0xD0,
BMI = 0x30,
BPL = 0x10,
BVC = 0x50,
BVS = 0x70,
BRA = 0x80,
BRL = 0x82,
JMP_ABS = 0x4C,
JMP_ABSi = 0x6C,
JMP_ABSXi = 0x7C,
JML_ABSl = 0x5C,
JML_ABSil = 0xDC
};
//! @brief The main CPU
class CPU : public Memory::AMemory {
protected:
@@ -380,6 +198,8 @@ namespace ComSquare::CPU
//! @brief True if an addressing mode with an iterator (x, y) has crossed the page. (Used because crossing the page boundary take one more cycle to run certain instructions).
bool _hasIndexCrossedPageBoundary = false;
//! @brief Immediate address mode is specified with a value in 8. (This functions returns the 24bit space address of the value).
uint24_t _getImmediateAddr8Bits();
//! @brief Immediate address mode is specified with a value in 8 or 16 bits. The value is 16 bits if the m flag is unset. (This functions returns the 24bit space address of the value).
uint24_t _getImmediateAddrForA();
//! @brief Immediate address mode is specified with a value in 8 or 16 bits. The value is 16 bits if the x flag is unset. (This functions returns the 24bit space address of the value).
@@ -433,125 +253,390 @@ namespace ComSquare::CPU
//! @brief Pop 16 bits of data from the stack.
uint16_t _pop16();
//! @brief Return the data at the program bank concatenated with the program counter. It also increment the program counter (the program bank is not incremented on overflows).
uint8_t readPC();
//! @brief Execute a single instruction.
//! @return The number of CPU cycles that the instruction took.
virtual unsigned _executeInstruction(uint8_t opcode);
//! @brief Break instruction - Causes a software break. The PC is loaded from a vector table.
void BRK();
int BRK(uint24_t, AddressingMode);
//! @brief Co-Processor Enable instruction - Causes a software break. The PC is loaded from a vector table.
void COP();
int COP(uint24_t, AddressingMode);
//! @brief Return from Interrupt - Used to return from a interrupt handler.
void RTI();
int RTI(uint24_t, AddressingMode);
//! @brief Add with carry - Adds operand to the Accumulator; adds an additional 1 if carry is set.
void ADC(uint24_t valueAddr);
int ADC(uint24_t valueAddr, AddressingMode);
//! @brief Store the accumulator to memory.
void STA(uint24_t addr);
int STA(uint24_t addr, AddressingMode);
//! @brief Store the index register X to memory.
void STX(uint24_t addr);
int STX(uint24_t addr, AddressingMode);
//! @brief Store the index register Y to memory.
void STY(uint24_t addr);
int STY(uint24_t addr, AddressingMode);
//! @brief Store zero to the memory.
void STZ(uint24_t addr);
int STZ(uint24_t addr, AddressingMode);
//! @brief Load the accumulator from memory.
void LDA(uint24_t addr);
int LDA(uint24_t addr, AddressingMode);
//! @brief Load the X index register from memory.
void LDX(uint24_t addr);
int LDX(uint24_t addr, AddressingMode);
//! @brief Load the Y index register from memory.
void LDY(uint24_t addr);
int LDY(uint24_t addr, AddressingMode);
//! @brief Set status bits.
void SEP(uint24_t valueAddr);
int SEP(uint24_t valueAddr, AddressingMode);
//! @brief Reset status bits.
void REP(uint24_t valueAddr);
int REP(uint24_t valueAddr, AddressingMode);
//! @brief Jump to subroutine
void JSR(uint24_t addr);
int JSR(uint24_t addr, AddressingMode);
//! @brief Jump to subroutine (long)
void JSL(uint24_t addr);
int JSL(uint24_t addr, AddressingMode);
//! @brief Push the accumulator to the stack.
void PHA();
int PHA(uint24_t, AddressingMode);
//! @brief Push the data bank register to the stack.
void PHB();
int PHB(uint24_t, AddressingMode);
//! @brief Push the direct page register to the stack.
void PHD();
int PHD(uint24_t, AddressingMode);
//! @brief Push the program bank register to the stack.
void PHK();
int PHK(uint24_t, AddressingMode);
//! @brief Push the processor status register to the stack.
void PHP();
int PHP(uint24_t, AddressingMode);
//! @brief Push the x index register to the stack.
void PHX();
int PHX(uint24_t, AddressingMode);
//! @brief Push the y index register to the stack.
void PHY();
int PHY(uint24_t, AddressingMode);
//! @brief Pull the accumulator to the stack.
void PLA();
int PLA(uint24_t, AddressingMode);
//! @brief Pull the data bank register to the stack.
void PLB();
int PLB(uint24_t, AddressingMode);
//! @brief Pull the direct page register to the stack.
void PLD();
int PLD(uint24_t, AddressingMode);
//! @brief Pull the processor status register to the stack.
void PLP();
int PLP(uint24_t, AddressingMode);
//! @brief Pull the x index register to the stack.
void PLX();
int PLX(uint24_t, AddressingMode);
//! @brief Pull the y index register to the stack.
void PLY();
int PLY(uint24_t, AddressingMode);
//! @brief Clear the carry flag.
void CLC();
int CLC(uint24_t, AddressingMode);
//! @brief Clear the Interrupt Disable flag.
void CLI();
int CLI(uint24_t, AddressingMode);
//! @brief Clear the decimal flag.
void CLD();
int CLD(uint24_t, AddressingMode);
//! @brief Clear the overflow flag.
void CLV();
int CLV(uint24_t, AddressingMode);
//! @brief Set the carry Flag.
void SEC();
int SEC(uint24_t, AddressingMode);
//! @brief Set the decimal flag.
void SED();
int SED(uint24_t, AddressingMode);
//! @brief Set the Interrupt Disable flag.
void SEI();
int SEI(uint24_t, AddressingMode);
//! @brief Exchange Carry and Emulation Flags
void XCE();
int XCE(uint24_t, AddressingMode);
//! @brief And accumulator with memory.
void AND(uint24_t valueAddr);
int AND(uint24_t valueAddr, AddressingMode);
//! @brief Subtract with Borrow from Accumulator.
void SBC(uint24_t valueAddr);
int SBC(uint24_t valueAddr, AddressingMode);
//! @brief Transfer A to X
void TAX();
int TAX(uint24_t, AddressingMode);
//! @brief Transfer A to Y
void TAY();
int TAY(uint24_t, AddressingMode);
//! @brief Transfer X to SP
void TXS();
int TXS(uint24_t, AddressingMode);
//! @brief Increment the X register
void INX();
int INX(uint24_t, AddressingMode);
//! @brief Increment the Y register
void INY();
int INY(uint24_t, AddressingMode);
//! @brief Compare the X register with the memory
void CPX(uint24_t valueAddr);
int CPX(uint24_t valueAddr, AddressingMode);
//! @brief Compare the Y register with the memory
void CPY(uint24_t valueAddr);
int CPY(uint24_t valueAddr, AddressingMode);
//! @brief Branch if carry clear
bool BCC(uint24_t valueAddr);
int BCC(uint24_t valueAddr, AddressingMode);
//! @brief Branch if carry set
bool BCS(uint24_t valueAddr);
int BCS(uint24_t valueAddr, AddressingMode);
//! @brief Branch if equal
bool BEQ(uint24_t valueAddr);
int BEQ(uint24_t valueAddr, AddressingMode);
//! @brief Branch if not equal
bool BNE(uint24_t valueAddr);
int BNE(uint24_t valueAddr, AddressingMode);
//! @brief Branch if minus
bool BMI(uint24_t valueAddr);
int BMI(uint24_t valueAddr, AddressingMode);
//! @brief Branch if plus
bool BPL(uint24_t valueAddr);
int BPL(uint24_t valueAddr, AddressingMode);
//! @brief Branch if Overflow Clear
bool BVC(uint24_t valueAddr);
int BVC(uint24_t valueAddr, AddressingMode);
//! @brief Branch if Overflow Set
bool BVS(uint24_t valueAddr);
int BVS(uint24_t valueAddr, AddressingMode);
//! @brief Branch always
bool BRA(uint24_t valueAddr);
int BRA(uint24_t valueAddr, AddressingMode);
//! @brief Branch always long
bool BRL(uint24_t valueAddr);
int BRL(uint24_t valueAddr, AddressingMode);
//! @brief Jump.
void JMP(uint24_t valueAddr);
int JMP(uint24_t valueAddr, AddressingMode);
//! @brief Long jump.
void JML(uint24_t valueAddr);
int JML(uint24_t valueAddr, AddressingMode);
//! @brief No OP.
int NOP(uint24_t, AddressingMode);
//! @brief All the instructions of the CPU.
//! @info Instructions are indexed by their opcode
Instruction _instructions[0x100] = {
{&CPU::BRK, 7, "brk", AddressingMode::Implied, 2}, // 00
{&CPU::BRK, 7, "ora #-#", AddressingMode::Implied, 2}, // 01
{&CPU::COP, 7, "cop", AddressingMode::Implied, 2}, // 02
{&CPU::BRK, 7, "ora #-#", AddressingMode::Implied, 2}, // 03
{&CPU::BRK, 7, "tsb #-#", AddressingMode::Implied, 2}, // 04
{&CPU::BRK, 7, "ora #-#", AddressingMode::Implied, 2}, // 05
{&CPU::BRK, 7, "asl #-#", AddressingMode::Implied, 2}, // 06
{&CPU::BRK, 7, "ora #-#", AddressingMode::Implied, 2}, // 07
{&CPU::PHP, 3, "php", AddressingMode::Implied, 3}, // 08
{&CPU::BRK, 7, "ora #-#", AddressingMode::Implied, 2}, // 09
{&CPU::BRK, 7, "asl #-#", AddressingMode::Implied, 2}, // 0A
{&CPU::PHD, 4, "phd", AddressingMode::Implied, 1}, // 0B
{&CPU::BRK, 7, "tsb #-#", AddressingMode::Implied, 2}, // 0C
{&CPU::BRK, 7, "ora #-#", AddressingMode::Implied, 2}, // 0D
{&CPU::BRK, 7, "asl #-#", AddressingMode::Implied, 2}, // 0E
{&CPU::BRK, 7, "ora #-#", AddressingMode::Implied, 2}, // 0F
{&CPU::BPL, 7, "bpl", AddressingMode::Implied, 2}, // 10
{&CPU::BRK, 7, "ora #-#", AddressingMode::Implied, 2}, // 11
{&CPU::BRK, 7, "ora #-#", AddressingMode::Implied, 2}, // 12
{&CPU::BRK, 7, "ora #-#", AddressingMode::Implied, 2}, // 13
{&CPU::BRK, 7, "trb #-#", AddressingMode::Implied, 2}, // 14
{&CPU::BRK, 7, "ora #-#", AddressingMode::Implied, 2}, // 15
{&CPU::BRK, 7, "asl #-#", AddressingMode::Implied, 2}, // 16
{&CPU::BRK, 7, "ora #-#", AddressingMode::Implied, 2}, // 17
{&CPU::CLC, 2, "clc", AddressingMode::Implied, 1}, // 18
{&CPU::BRK, 7, "ora #-#", AddressingMode::Implied, 2}, // 19
{&CPU::BRK, 7, "inc #-#", AddressingMode::Implied, 2}, // 1A
{&CPU::BRK, 7, "tcs #-#", AddressingMode::Implied, 2}, // 1B
{&CPU::BRK, 7, "trb #-#", AddressingMode::Implied, 2}, // 1C
{&CPU::BRK, 7, "ora #-#", AddressingMode::Implied, 2}, // 1D
{&CPU::BRK, 7, "asl #-#", AddressingMode::Implied, 2}, // 1E
{&CPU::BRK, 7, "ora #-#", AddressingMode::Implied, 2}, // 1F
{&CPU::JSR, 6, "jsr", AddressingMode::Absolute, 3}, // 20
{&CPU::BRK, 7, "and #-#", AddressingMode::Implied, 2}, // 21
{&CPU::JSL, 8, "jsl", AddressingMode::AbsoluteLong, 4}, // 22
{&CPU::BRK, 7, "and #-#", AddressingMode::Implied, 2}, // 23
{&CPU::BRK, 7, "bit #-#", AddressingMode::Implied, 2}, // 24
{&CPU::BRK, 7, "and #-#", AddressingMode::Implied, 2}, // 25
{&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 26
{&CPU::BRK, 7, "and #-#", AddressingMode::Implied, 2}, // 27
{&CPU::PLP, 4, "plp", AddressingMode::Implied, 1}, // 28
{&CPU::BRK, 7, "and #-#", AddressingMode::Implied, 2}, // 29
{&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 2A
{&CPU::PLD, 5, "pld", AddressingMode::Implied, 1}, // 2B
{&CPU::BRK, 7, "bit #-#", AddressingMode::Implied, 2}, // 2C
{&CPU::BRK, 7, "and #-#", AddressingMode::Implied, 2}, // 2D
{&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 2E
{&CPU::BRK, 7, "and #-#", AddressingMode::Implied, 2}, // 2F
{&CPU::BMI, 2, "bmi", AddressingMode::Implied, 2}, // 30
{&CPU::BRK, 7, "and #-#", AddressingMode::Implied, 2}, // 31
{&CPU::BRK, 7, "and #-#", AddressingMode::Implied, 2}, // 32
{&CPU::BRK, 7, "and #-#", AddressingMode::Implied, 2}, // 33
{&CPU::BRK, 7, "bit #-#", AddressingMode::Implied, 2}, // 34
{&CPU::BRK, 7, "and #-#", AddressingMode::Implied, 2}, // 35
{&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 36
{&CPU::BRK, 7, "and #-#", AddressingMode::Implied, 2}, // 37
{&CPU::SEC, 2, "sec", AddressingMode::Implied, 1}, // 38
{&CPU::BRK, 7, "and #-#", AddressingMode::Implied, 2}, // 39
{&CPU::BRK, 7, "dec #-#", AddressingMode::Implied, 2}, // 3A
{&CPU::BRK, 7, "tsc #-#", AddressingMode::Implied, 2}, // 3B
{&CPU::BRK, 7, "bit #-#", AddressingMode::Implied, 2}, // 3C
{&CPU::BRK, 7, "and #-#", AddressingMode::Implied, 2}, // 3D
{&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 3E
{&CPU::BRK, 7, "and #-#", AddressingMode::Implied, 2}, // 3F
{&CPU::RTI, 6, "rti", AddressingMode::Implied, 1}, // 40
{&CPU::BRK, 7, "eor #-#", AddressingMode::Implied, 2}, // 41
{&CPU::BRK, 7, "wdm #-#", AddressingMode::Implied, 2}, // 42
{&CPU::BRK, 7, "eor #-#", AddressingMode::Implied, 2}, // 43
{&CPU::BRK, 7, "mvp #-#", AddressingMode::Implied, 2}, // 44
{&CPU::BRK, 7, "eor #-#", AddressingMode::Implied, 2}, // 45
{&CPU::BRK, 7, "lsr #-#", AddressingMode::Implied, 2}, // 46
{&CPU::BRK, 7, "eor #-#", AddressingMode::Implied, 2}, // 47
{&CPU::PHA, 3, "pha", AddressingMode::Implied, 1}, // 48
{&CPU::BRK, 7, "eor #-#", AddressingMode::Implied, 2}, // 49
{&CPU::BRK, 7, "lsr #-#", AddressingMode::Implied, 2}, // 4A
{&CPU::PHK, 3, "phk", AddressingMode::Implied, 1}, // 4B
{&CPU::JMP, 3, "jmp", AddressingMode::Absolute, 3}, // 4C
{&CPU::BRK, 7, "eor #-#", AddressingMode::Implied, 2}, // 4D
{&CPU::BRK, 7, "lsr #-#", AddressingMode::Implied, 2}, // 4E
{&CPU::BRK, 7, "eor #-#", AddressingMode::Implied, 2}, // 4F
{&CPU::BVC, 2, "bvc", AddressingMode::Implied, 2}, // 50
{&CPU::BRK, 7, "eor #-#", AddressingMode::Implied, 2}, // 51
{&CPU::BRK, 7, "eor #-#", AddressingMode::Implied, 2}, // 52
{&CPU::BRK, 7, "eor #-#", AddressingMode::Implied, 2}, // 53
{&CPU::BRK, 7, "mvn #-#", AddressingMode::Implied, 2}, // 54
{&CPU::BRK, 7, "eor #-#", AddressingMode::Implied, 2}, // 55
{&CPU::BRK, 7, "lsr #-#", AddressingMode::Implied, 2}, // 56
{&CPU::BRK, 7, "eor #-#", AddressingMode::Implied, 2}, // 57
{&CPU::CLI, 2, "cli", AddressingMode::Implied, 1}, // 58
{&CPU::BRK, 7, "eor #-#", AddressingMode::Implied, 2}, // 59
{&CPU::PHY, 3, "phy", AddressingMode::Implied, 1}, // 5A
{&CPU::BRK, 7, "tcd #-#", AddressingMode::Implied, 2}, // 5B
{&CPU::JML, 4, "jml", AddressingMode::Implied, 4}, // 5C
{&CPU::BRK, 7, "eor #-#", AddressingMode::Implied, 2}, // 5D
{&CPU::BRK, 7, "lsr #-#", AddressingMode::Implied, 2}, // 5E
{&CPU::BRK, 7, "eor #-#", AddressingMode::Implied, 2}, // 5F
{&CPU::BRK, 7, "rtl #-#", AddressingMode::Implied, 2}, // 60
{&CPU::ADC, 6, "adc", AddressingMode::DirectPageIndirectIndexedByX, 2}, // 61
{&CPU::BRK, 7, "per #-#", AddressingMode::Implied, 2}, // 62
{&CPU::ADC, 4, "adc", AddressingMode::StackRelative, 2}, // 63
{&CPU::STZ, 3, "stz", AddressingMode::DirectPage, 2}, // 64
{&CPU::ADC, 3, "adc", AddressingMode::DirectPage, 2}, // 65
{&CPU::BRK, 7, "ror #-#", AddressingMode::Implied, 2}, // 66
{&CPU::ADC, 6, "adc", AddressingMode::DirectPageIndirectLong, 2}, // 67
{&CPU::PLA, 4, "pla", AddressingMode::Implied, 1}, // 68
{&CPU::ADC, 2, "adc", AddressingMode::ImmediateForA, 2}, // 69
{&CPU::BRK, 7, "ror #-#", AddressingMode::Implied, 2}, // 6A
{&CPU::BRK, 7, "rts #-#", AddressingMode::Implied, 2}, // 6B
{&CPU::JMP, 5, "jmp", AddressingMode::AbsoluteIndirect, 3}, // 6C
{&CPU::ADC, 4, "adc", AddressingMode::Absolute, 3}, // 6D
{&CPU::BRK, 7, "ror #-#", AddressingMode::Implied, 2}, // 6E
{&CPU::ADC, 5, "adc", AddressingMode::AbsoluteLong, 4}, // 6F
{&CPU::BVS, 2, "bvs", AddressingMode::Implied, 2}, // 70
{&CPU::ADC, 5, "adc", AddressingMode::DirectPageIndirectIndexedByY, 2}, // 71
{&CPU::ADC, 5, "adc", AddressingMode::DirectPageIndirect, 2}, // 72
{&CPU::ADC, 7, "adc", AddressingMode::StackRelativeIndirectIndexedByY, 2}, // 73
{&CPU::STZ, 4, "stz", AddressingMode::DirectPageIndexedByX, 2}, // 74
{&CPU::ADC, 4, "adc", AddressingMode::DirectPageIndexedByX, 2}, // 75
{&CPU::BRK, 7, "ror #-#", AddressingMode::Implied, 2}, // 76
{&CPU::ADC, 6, "adc", AddressingMode::DirectPageIndirectIndexedByY, 2}, // 77
{&CPU::SEI, 2, "sei", AddressingMode::Implied, 1}, // 78
{&CPU::ADC, 4, "adc", AddressingMode::AbsoluteIndexedByY, 2}, // 79
{&CPU::PLY, 4, "ply", AddressingMode::Implied, 1}, // 7A
{&CPU::BRK, 7, "tdc #-#", AddressingMode::Implied, 2}, // 7B
{&CPU::JMP, 6, "jmp", AddressingMode::AbsoluteIndirectIndexedByX, 3}, // 7C
{&CPU::ADC, 4, "adc", AddressingMode::AbsoluteIndexedByX, 3}, // 7D
{&CPU::BRK, 7, "ror #-#", AddressingMode::Implied, 2}, // 7E
{&CPU::ADC, 5, "adc", AddressingMode::AbsoluteIndexedByXLong, 4}, // 7F
{&CPU::BRA, 3, "bra", AddressingMode::Implied, 2}, // 80
{&CPU::STA, 6, "sta", AddressingMode::DirectPageIndexedByX, 2}, // 81
{&CPU::BRL, 4, "brl", AddressingMode::Implied, 3}, // 82
{&CPU::STA, 4, "sta", AddressingMode::StackRelative, 2}, // 83
{&CPU::STY, 3, "sty", AddressingMode::DirectPage, 2}, // 84
{&CPU::STA, 3, "sta", AddressingMode::DirectPage, 2}, // 85
{&CPU::STX, 3, "stx", AddressingMode::DirectPage, 2}, // 86
{&CPU::STA, 6, "sta", AddressingMode::DirectPageIndirectLong, 2}, // 87
{&CPU::BRK, 7, "dey #-#", AddressingMode::Implied, 2}, // 88
{&CPU::BRK, 7, "bit #-#", AddressingMode::Implied, 2}, // 89
{&CPU::BRK, 7, "txa #-#", AddressingMode::Implied, 2}, // 8A
{&CPU::PHB, 3, "phb", AddressingMode::Implied, 1}, // 8B
{&CPU::STY, 4, "sty", AddressingMode::Absolute, 3}, // 8C
{&CPU::STA, 4, "sta", AddressingMode::Absolute, 3}, // 8D
{&CPU::STX, 4, "stx", AddressingMode::Absolute, 3}, // 8E
{&CPU::STA, 5, "sta", AddressingMode::AbsoluteLong, 4}, // 8F
{&CPU::BCC, 2, "bcc", AddressingMode::Implied, 2}, // 90
{&CPU::STA, 6, "sta", AddressingMode::DirectPageIndirectIndexedByY, 2}, // 91
{&CPU::STA, 5, "sta", AddressingMode::DirectPageIndirect, 2}, // 92
{&CPU::STA, 7, "sta", AddressingMode::StackRelativeIndirectIndexedByY, 2}, // 93
{&CPU::STY, 4, "sty", AddressingMode::DirectPageIndirectIndexedByX, 2}, // 94
{&CPU::STA, 4, "sta", AddressingMode::DirectPageIndexedByX, 2}, // 95
{&CPU::STX, 4, "stx", AddressingMode::DirectPageIndexedByY, 2}, // 96
{&CPU::STA, 6, "sta", AddressingMode::DirectPageIndirectIndexedByYLong, 2}, // 97
{&CPU::BRK, 7, "tya #-#", AddressingMode::Implied, 2}, // 98
{&CPU::STA, 5, "sta", AddressingMode::AbsoluteIndexedByY, 3}, // 99
{&CPU::TXS, 2, "txs", AddressingMode::Implied, 1}, // 9A
{&CPU::BRK, 7, "txy #-#", AddressingMode::Implied, 2}, // 9B
{&CPU::STZ, 4, "stz", AddressingMode::Absolute, 3}, // 9C
{&CPU::STA, 5, "sta", AddressingMode::AbsoluteIndexedByX, 3}, // 9D
{&CPU::STZ, 5, "stz", AddressingMode::AbsoluteIndexedByX, 3}, // 9E
{&CPU::STA, 5, "sta", AddressingMode::AbsoluteIndexedByXLong, 4}, // 9F
{&CPU::LDY, 2, "ldy", AddressingMode::ImmediateForX, 2}, // A0
{&CPU::LDA, 6, "lda", AddressingMode::DirectPageIndirectIndexedByX, 2}, // A1
{&CPU::LDX, 2, "ldx", AddressingMode::ImmediateForX, 2}, // A2
{&CPU::LDA, 4, "lda", AddressingMode::StackRelative, 2}, // A3
{&CPU::LDY, 3, "ldy", AddressingMode::DirectPage, 2}, // A4
{&CPU::LDA, 3, "lda", AddressingMode::DirectPage, 2}, // A5
{&CPU::LDX, 3, "ldx", AddressingMode::DirectPage, 2}, // A6
{&CPU::LDA, 6, "lda", AddressingMode::DirectPageIndirectLong, 2}, // A7
{&CPU::TAY, 2, "tay", AddressingMode::Implied, 1}, // A8
{&CPU::LDA, 2, "lda", AddressingMode::ImmediateForA, 2}, // A9
{&CPU::TAX, 2, "tax", AddressingMode::Implied, 1}, // AA
{&CPU::BRK, 7, "trb #-#", AddressingMode::Implied, 2}, // AB
{&CPU::LDY, 4, "ldy", AddressingMode::Absolute, 4}, // AC
{&CPU::LDA, 4, "lda", AddressingMode::Absolute, 3}, // AD
{&CPU::LDX, 4, "ldx", AddressingMode::Absolute, 3}, // AE
{&CPU::LDA, 5, "lda", AddressingMode::AbsoluteLong, 4}, // AF
{&CPU::BCS, 2, "bcs", AddressingMode::Implied, 2}, // B0
{&CPU::LDA, 5, "lda", AddressingMode::DirectPageIndirectIndexedByY, 2}, // B1
{&CPU::LDA, 5, "lda", AddressingMode::DirectPageIndirect, 2}, // B2
{&CPU::LDA, 7, "lda", AddressingMode::StackRelativeIndirectIndexedByY, 2}, // B3
{&CPU::LDY, 4, "ldy", AddressingMode::DirectPageIndexedByX, 2}, // B4
{&CPU::LDA, 4, "lda", AddressingMode::DirectPageIndexedByX, 2}, // B5
{&CPU::LDX, 4, "ldx", AddressingMode::DirectPageIndexedByY, 2}, // B6
{&CPU::LDA, 6, "lda", AddressingMode::DirectPageIndirectIndexedByYLong, 2}, // B7
{&CPU::CLV, 7, "clv", AddressingMode::Implied, 1}, // B8
{&CPU::LDA, 4, "lda", AddressingMode::AbsoluteIndexedByY, 3}, // B9
{&CPU::BRK, 7, "mvn #-#", AddressingMode::Implied, 2}, // BA
{&CPU::BRK, 7, "tyx #-#", AddressingMode::Implied, 2}, // BB
{&CPU::LDY, 4, "ldy", AddressingMode::AbsoluteIndexedByX, 3}, // BC
{&CPU::LDA, 4, "lda", AddressingMode::AbsoluteIndexedByX, 3}, // BD
{&CPU::LDX, 4, "ldx", AddressingMode::AbsoluteIndexedByY, 3}, // BE
{&CPU::LDA, 5, "lda", AddressingMode::AbsoluteIndexedByXLong, 4}, // BF
{&CPU::BRK, 7, "cpy #-#", AddressingMode::Implied, 2}, // C0
{&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // C1
{&CPU::REP, 3, "rep", AddressingMode::Immediate8bits, 2}, // C2
{&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // C3
{&CPU::BRK, 7, "cpy #-#", AddressingMode::Implied, 2}, // C4
{&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // C5
{&CPU::BRK, 7, "dec #-#", AddressingMode::Implied, 2}, // C6
{&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // C7
{&CPU::INY, 2, "iny", AddressingMode::Implied, 1}, // C8
{&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // C9
{&CPU::BRK, 7, "dex #-#", AddressingMode::Implied, 2}, // CA
{&CPU::BRK, 7, "wai #-#", AddressingMode::Implied, 2}, // CB
{&CPU::BRK, 7, "cpy #-#", AddressingMode::Implied, 2}, // CC
{&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // CD
{&CPU::BRK, 7, "dec #-#", AddressingMode::Implied, 2}, // CE
{&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // CF
{&CPU::BNE, 2, "bne", AddressingMode::Implied, 2}, // D0
{&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // D1
{&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // D2
{&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // D3
{&CPU::BRK, 7, "pei #-#", AddressingMode::Implied, 2}, // D4
{&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // D5
{&CPU::BRK, 7, "dec #-#", AddressingMode::Implied, 2}, // D6
{&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // D7
{&CPU::CLD, 27, "cld", AddressingMode::Implied, 2}, // D8
{&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // D9
{&CPU::PHX, 3, "phx", AddressingMode::Implied, 1}, // DA
{&CPU::BRK, 7, "stp #-#", AddressingMode::Implied, 2}, // DB
{&CPU::JML, 7, "jml", AddressingMode::AbsoluteIndirectLong, 2}, // DC
{&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // DD
{&CPU::BRK, 7, "dec #-#", AddressingMode::Implied, 2}, // DE
{&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // DF
{&CPU::BRK, 7, "cpx #-#", AddressingMode::Implied, 2}, // E0
{&CPU::SBC, 6, "sbc", AddressingMode::DirectPageIndirectIndexedByX, 2}, // E1
{&CPU::SEP, 3, "sep", AddressingMode::Immediate8bits, 2}, // E2
{&CPU::SBC, 4, "sbc", AddressingMode::StackRelative, 2}, // E3
{&CPU::BRK, 7, "cpx #-#", AddressingMode::Implied, 2}, // E4
{&CPU::SBC, 3, "sbc", AddressingMode::DirectPage, 2}, // E5
{&CPU::BRK, 7, "inc #-#", AddressingMode::Implied, 2}, // E6
{&CPU::SBC, 6, "sbc", AddressingMode::DirectPageIndirectLong, 2}, // E7
{&CPU::INX, 2, "inx", AddressingMode::Implied, 1}, // E8
{&CPU::SBC, 2, "sbc", AddressingMode::ImmediateForA, 2}, // E9
{&CPU::NOP, 2, "nop", AddressingMode::Implied, 1}, // EA
{&CPU::BRK, 7, "xba #-#", AddressingMode::Implied, 2}, // EB
{&CPU::BRK, 7, "cpx #-#", AddressingMode::Implied, 2}, // EC
{&CPU::SBC, 4, "sbc", AddressingMode::Absolute, 3}, // ED
{&CPU::BRK, 7, "inc #-#", AddressingMode::Implied, 2}, // EE
{&CPU::SBC, 5, "sbc", AddressingMode::AbsoluteLong, 4}, // EF
{&CPU::BEQ, 2, "beq", AddressingMode::Implied, 2}, // F0
{&CPU::SBC, 5, "sbc", AddressingMode::DirectPageIndirectIndexedByY, 2}, // F1
{&CPU::SBC, 5, "sbc", AddressingMode::DirectPageIndirect, 2}, // F2
{&CPU::SBC, 7, "sbc", AddressingMode::StackRelativeIndirectIndexedByY, 2}, // F3
{&CPU::BRK, 7, "pea #-#", AddressingMode::Implied, 2}, // F4
{&CPU::SBC, 4, "sbc", AddressingMode::DirectPageIndexedByX, 2}, // F5
{&CPU::BRK, 7, "inc #-#", AddressingMode::Implied, 2}, // F6
{&CPU::SBC, 6, "sbc", AddressingMode::DirectPageIndirectIndexedByYLong, 2}, // F7
{&CPU::SED, 2, "sed", AddressingMode::Implied, 1}, // F8
{&CPU::SBC, 4, "sbc", AddressingMode::AbsoluteIndexedByY, 3}, // F9
{&CPU::PLX, 4, "plx", AddressingMode::Implied, 1}, // FA
{&CPU::XCE, 2, "xce", AddressingMode::Implied, 1}, // FB
{&CPU::JSR, 8, "jsr", AddressingMode::AbsoluteIndirectIndexedByX, 3}, // FC
{&CPU::SBC, 4, "sbc", AddressingMode::AbsoluteIndexedByX, 3}, // FD
{&CPU::BRK, 7, "inc #-#", AddressingMode::Implied, 2}, // FE
{&CPU::SBC, 5, "sbc", AddressingMode::AbsoluteIndexedByXLong, 4}, // FF
};
public:
explicit CPU(std::shared_ptr<Memory::MemoryBus> bus, Cartridge::Header &cartridgeHeader);
CPU(const CPU &) = default;
@@ -578,7 +663,7 @@ namespace ComSquare::CPU
Component getComponent() override;
//! @brief Reset interrupt - Called on boot and when the reset button is pressed.
virtual void RESB();
virtual int RESB();
//! @brief Return true if the CPU is overloaded with debugging features.
virtual bool isDebugger();

View File

@@ -0,0 +1,67 @@
//
// Created by anonymus-raccoon on 3/25/20.
//
#ifndef COMSQUARE_INSTRUCTION_HPP
#define COMSQUARE_INSTRUCTION_HPP
#include <string>
#include "../Models/Int24.hpp"
namespace ComSquare::CPU
{
class CPU;
//! @brief Different addressing modes that instructions can use for the main CPU.
enum AddressingMode {
Implied,
Immediate8bits,
ImmediateForA,
ImmediateForX,
Absolute,
AbsoluteLong,
DirectPage,
DirectPageIndirect,
DirectPageIndirectLong,
AbsoluteIndexedByX,
AbsoluteIndexedByXLong,
AbsoluteIndexedByY,
DirectPageIndexedByX,
DirectPageIndexedByY,
DirectPageIndirectIndexedByX,
DirectPageIndirectIndexedByY,
DirectPageIndirectIndexedByYLong,
StackRelative,
StackRelativeIndirectIndexedByY,
ProgramCounterRelative,
ProgramCounterRelativeLong,
AbsoluteIndirect,
AbsoluteIndirectIndexedByX,
AbsoluteIndirectLong
};
//! @brief Struct containing basic information about instructions.
struct Instruction {
int (CPU::*call)(uint24_t valueAddr, AddressingMode mode) = nullptr;
int cycleCount = 0;
std::string name = "";
AddressingMode addressingMode = Implied;
int size = 0;
Instruction() = default;
Instruction(const Instruction &) = default;
Instruction &operator=(const Instruction &) = default;
~Instruction() = default;
};
}
#endif //COMSQUARE_INSTRUCTION_HPP

View File

@@ -7,7 +7,7 @@
namespace ComSquare::CPU
{
void CPU::AND(uint24_t valueAddr)
int CPU::AND(uint24_t valueAddr, AddressingMode mode)
{
unsigned negativeMask = this->_isEmulationMode ? 0x80u : 0x8000u;
unsigned value = this->_bus->read(valueAddr);
@@ -17,5 +17,27 @@ namespace ComSquare::CPU
this->_registers.a &= value;
this->_registers.p.n = this->_registers.a & negativeMask;
this->_registers.p.z = this->_registers.a == 0;
int cycles = !this->_registers.p.m;
switch (mode) {
case DirectPage:
case DirectPageIndirect:
case DirectPageIndirectLong:
case DirectPageIndexedByX:
case DirectPageIndirectIndexedByX:
case DirectPageIndirectIndexedByYLong:
cycles += this->_registers.dl != 0;
break;
case AbsoluteIndexedByX:
case AbsoluteIndexedByY:
cycles += this->_hasIndexCrossedPageBoundary;
break;
case DirectPageIndirectIndexedByY:
cycles += this->_registers.dl != 0 + this->_hasIndexCrossedPageBoundary;
break;
default:
break;
}
return cycles;
}
}

View File

@@ -6,152 +6,185 @@
namespace ComSquare::CPU
{
void CPU::SEC()
int CPU::SEC(uint24_t, AddressingMode)
{
this->_registers.p.c = true;
return 0;
}
void CPU::SED()
int CPU::SED(uint24_t, AddressingMode)
{
this->_registers.p.d = true;
return 0;
}
void CPU::SEI()
int CPU::SEI(uint24_t, AddressingMode)
{
this->_registers.p.i = true;
return 0;
}
void CPU::CLC()
int CPU::CLC(uint24_t, AddressingMode)
{
this->_registers.p.c = false;
return 0;
}
void CPU::CLI()
int CPU::CLI(uint24_t, AddressingMode)
{
this->_registers.p.i = false;
return 0;
}
void CPU::CLD()
int CPU::CLD(uint24_t, AddressingMode)
{
this->_registers.p.d = false;
return 0;
}
void CPU::CLV()
int CPU::CLV(uint24_t, AddressingMode)
{
this->_registers.p.v = false;
return 0;
}
void CPU::SEP(uint24_t value)
int CPU::SEP(uint24_t valueAddr, AddressingMode)
{
this->_registers.p.flags |= value;
this->_registers.p.flags |= this->_bus->read(valueAddr);
return 0;
}
void CPU::REP(uint24_t value)
int CPU::REP(uint24_t valueAddr, AddressingMode)
{
this->_registers.p.flags &= ~value;
this->_registers.p.flags &= ~this->_bus->read(valueAddr);
if (this->_isEmulationMode) {
this->_registers.p.x_b = true;
this->_registers.p.m = true;
}
return 0;
}
void CPU::JSR(uint24_t value)
int CPU::JSR(uint24_t valueAddr, AddressingMode)
{
this->_push(--this->_registers.pc);
this->_registers.pc = value;
this->_registers.pc = valueAddr;
return 0;
}
void CPU::JSL(uint24_t value)
int CPU::JSL(uint24_t valueAddr, AddressingMode)
{
this->_registers.pac--;
this->_push(this->_registers.pbr);
this->_push(this->_registers.pc);
this->_registers.pac = value;
this->_registers.pac = valueAddr;
return 0;
}
void CPU::PHA()
int CPU::PHA(uint24_t, AddressingMode)
{
this->_push(this->_registers.a);
return !this->_registers.p.m;
}
void CPU::PHB()
int CPU::PHB(uint24_t, AddressingMode)
{
this->_push(this->_registers.dbr);
return 0;
}
void CPU::PHD()
int CPU::PHD(uint24_t, AddressingMode)
{
this->_push(this->_registers.d);
return 0;
}
void CPU::PHK()
int CPU::PHK(uint24_t, AddressingMode)
{
this->_push(this->_registers.pbr);
return 0;
}
void CPU::PHP()
int CPU::PHP(uint24_t, AddressingMode)
{
this->_push(this->_registers.p.flags);
return 0;
}
void CPU::PHX()
int CPU::PHX(uint24_t, AddressingMode)
{
this->_push(this->_registers.x);
return !this->_registers.p.x_b;
}
void CPU::PHY()
int CPU::PHY(uint24_t, AddressingMode)
{
this->_push(this->_registers.y);
return !this->_registers.p.x_b;
}
void CPU::PLA()
int CPU::PLA(uint24_t, AddressingMode)
{
// TODO this register should be poped by 8 if the m flag is 1
if (this->_registers.p.m) {
this->_registers.a = this->_pop();
this->_registers.ah = 0;
} else
this->_registers.a = this->_pop16();
this->_registers.p.z = this->_registers.a == 0;
this->_registers.p.n = this->_registers.a & 0x8000u;
return !this->_registers.p.m;
}
void CPU::PLB()
int CPU::PLB(uint24_t, AddressingMode)
{
this->_registers.dbr = this->_pop();
this->_registers.p.z = this->_registers.dbr == 0;
this->_registers.p.n = this->_registers.dbr & 0x80u;
return 0;
}
void CPU::PLD()
int CPU::PLD(uint24_t, AddressingMode)
{
this->_registers.d = this->_pop16();
this->_registers.p.z = this->_registers.d == 0;
this->_registers.p.n = this->_registers.d & 0x8000u;
return 0;
}
void CPU::PLP()
int CPU::PLP(uint24_t, AddressingMode)
{
this->_registers.p.flags = this->_pop();
if (this->_isEmulationMode) {
this->_registers.p.m = true;
this->_registers.p.x_b = true;
}
return 0;
}
void CPU::PLX()
int CPU::PLX(uint24_t, AddressingMode)
{
// TODO this register should be poped by 8 if the x_b is 1
if (this->_registers.p.x_b) {
this->_registers.x = this->_pop();
this->_registers.xh = 0;
} else
this->_registers.x = this->_pop16();
this->_registers.p.z = this->_registers.x == 0;
this->_registers.p.n = this->_registers.x & 0x8000u;
return !this->_registers.p.x_b;
}
void CPU::PLY()
int CPU::PLY(uint24_t, AddressingMode)
{
// TODO this register should be poped by 8 if the x_b is 1
if (this->_registers.p.x_b) {
this->_registers.y = this->_pop();
this->_registers.yh = 0;
} else
this->_registers.y = this->_pop16();
this->_registers.p.z = this->_registers.y == 0;
this->_registers.p.n = this->_registers.y & 0x8000u;
return !this->_registers.p.x_b;
}
void CPU::XCE()
int CPU::XCE(uint24_t, AddressingMode)
{
bool oldCarry = this->_registers.p.c;
this->_registers.p.c = this->_isEmulationMode;
@@ -163,9 +196,10 @@ namespace ComSquare::CPU
this->_registers.xh = 0;
this->_registers.yh = 0;
}
return 0;
}
void CPU::INX()
int CPU::INX(uint24_t, AddressingMode)
{
this->_registers.x++;
@@ -175,9 +209,10 @@ namespace ComSquare::CPU
unsigned negativeFlag = this->_registers.p.x_b ? 0x80u : 0x8000u;
this->_registers.p.z = this->_registers.x == 0;
this->_registers.p.n = this->_registers.x & negativeFlag;
return 0;
}
void CPU::INY()
int CPU::INY(uint24_t, AddressingMode)
{
this->_registers.y++;
@@ -187,9 +222,10 @@ namespace ComSquare::CPU
unsigned negativeFlag = this->_registers.p.x_b ? 0x80u : 0x8000u;
this->_registers.p.z = this->_registers.y == 0;
this->_registers.p.n = this->_registers.y & negativeFlag;
return 0;
}
void CPU::CPX(uint24_t valueAddr)
int CPU::CPX(uint24_t valueAddr, AddressingMode mode)
{
unsigned value = this->_bus->read(valueAddr++);
@@ -206,9 +242,10 @@ namespace ComSquare::CPU
this->_registers.p.n = x & 0x8000u;
}
this->_registers.p.c = this->_registers.x >= value;
return !this->_registers.p.x_b + (mode == DirectPage && this->_registers.dl != 0);
}
void CPU::CPY(uint24_t valueAddr)
int CPU::CPY(uint24_t valueAddr, AddressingMode mode)
{
unsigned value = this->_bus->read(valueAddr++);
@@ -225,86 +262,94 @@ namespace ComSquare::CPU
this->_registers.p.z = y == 0;
this->_registers.p.n = y & 0x8000u;
}
return !this->_registers.p.x_b + (mode == DirectPage && this->_registers.dl != 0);
}
bool CPU::BCC(uint24_t valueAddr)
int CPU::BCC(uint24_t valueAddr, AddressingMode)
{
if (!this->_registers.p.c)
this->_registers.pac += static_cast<int8_t>(this->_bus->read(valueAddr));
return !this->_registers.p.c;
this->_registers.pc += static_cast<int8_t>(this->_bus->read(valueAddr));
return !this->_registers.p.c + this->_isEmulationMode;
}
bool CPU::BCS(uint24_t valueAddr)
int CPU::BCS(uint24_t valueAddr, AddressingMode)
{
if (this->_registers.p.c)
this->_registers.pac += static_cast<int8_t>(this->_bus->read(valueAddr));
return this->_registers.p.c;
this->_registers.pc += static_cast<int8_t>(this->_bus->read(valueAddr));
return this->_registers.p.c + this->_isEmulationMode;
}
bool CPU::BEQ(uint24_t valueAddr)
int CPU::BEQ(uint24_t valueAddr, AddressingMode)
{
if (this->_registers.p.z)
this->_registers.pac += static_cast<int8_t>(this->_bus->read(valueAddr));
return this->_registers.p.z;
this->_registers.pc += static_cast<int8_t>(this->_bus->read(valueAddr));
return this->_registers.p.z + this->_isEmulationMode;
}
bool CPU::BNE(uint24_t valueAddr)
int CPU::BNE(uint24_t valueAddr, AddressingMode)
{
if (!this->_registers.p.z)
this->_registers.pac += static_cast<int8_t>(this->_bus->read(valueAddr));
return !this->_registers.p.z;
this->_registers.pc += static_cast<int8_t>(this->_bus->read(valueAddr));
return !this->_registers.p.z + this->_isEmulationMode;
}
bool CPU::BMI(uint24_t valueAddr)
int CPU::BMI(uint24_t valueAddr, AddressingMode)
{
if (this->_registers.p.n)
this->_registers.pac += static_cast<int8_t>(this->_bus->read(valueAddr));
return this->_registers.p.n;
this->_registers.pc += static_cast<int8_t>(this->_bus->read(valueAddr));
return this->_registers.p.n + this->_isEmulationMode;
}
bool CPU::BPL(uint24_t valueAddr)
int CPU::BPL(uint24_t valueAddr, AddressingMode)
{
if (!this->_registers.p.n)
this->_registers.pac += static_cast<int8_t>(this->_bus->read(valueAddr));
return !this->_registers.p.n;
this->_registers.pc += static_cast<int8_t>(this->_bus->read(valueAddr));
return !this->_registers.p.n + this->_isEmulationMode;
}
bool CPU::BRA(uint24_t valueAddr)
int CPU::BRA(uint24_t valueAddr, AddressingMode)
{
this->_registers.pac += static_cast<int8_t>(this->_bus->read(valueAddr));
return true;
this->_registers.pc += static_cast<int8_t>(this->_bus->read(valueAddr));
return this->_isEmulationMode;
}
bool CPU::BRL(uint24_t valueAddr)
int CPU::BRL(uint24_t valueAddr, AddressingMode)
{
unsigned value = this->_bus->read(valueAddr);
value += this->_bus->read(valueAddr + 1) << 8u;
this->_registers.pac += static_cast<int16_t>(value);
return true;
this->_registers.pc += static_cast<int16_t>(value);
return 0;
}
bool CPU::BVC(uint24_t valueAddr)
int CPU::BVC(uint24_t valueAddr, AddressingMode)
{
if (!this->_registers.p.v)
this->_registers.pac += static_cast<int8_t>(this->_bus->read(valueAddr));
return !this->_registers.p.v;
this->_registers.pc += static_cast<int8_t>(this->_bus->read(valueAddr));
return !this->_registers.p.v + this->_isEmulationMode;
}
bool CPU::BVS(uint24_t valueAddr)
int CPU::BVS(uint24_t valueAddr, AddressingMode)
{
if (this->_registers.p.v)
this->_registers.pac += static_cast<int8_t>(this->_bus->read(valueAddr));
return this->_registers.p.v;
this->_registers.pc += static_cast<int8_t>(this->_bus->read(valueAddr));
return this->_registers.p.v + this->_isEmulationMode;
}
void CPU::JMP(uint24_t value)
int CPU::JMP(uint24_t value, AddressingMode)
{
this->_registers.pc = value;
return 0;
}
void CPU::JML(uint24_t value)
int CPU::JML(uint24_t value, AddressingMode)
{
this->_registers.pac = value;
return 0;
}
int CPU::NOP(uint24_t, AddressingMode)
{
return 0;
}
}

View File

@@ -6,7 +6,7 @@
namespace ComSquare::CPU
{
void CPU::RESB()
int CPU::RESB()
{
this->_registers.p.i = true;
this->_registers.p.d = false;
@@ -18,9 +18,10 @@ 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;
}
void CPU::BRK()
int CPU::BRK(uint24_t, AddressingMode)
{
if (this->_isEmulationMode) {
this->_registers.pc += 2;
@@ -30,7 +31,6 @@ namespace ComSquare::CPU
this->_registers.p.i = true;
this->_registers.p.d = false;
this->_registers.pc = this->_cartridgeHeader.emulationInterrupts.brk;
} else {
this->_push(this->_registers.pbr);
this->_registers.pc += 2;
@@ -41,9 +41,10 @@ namespace ComSquare::CPU
this->_registers.pbr = 0x0;
this->_registers.pc = this->_cartridgeHeader.nativeInterrupts.brk;
}
return !this->_isEmulationMode;
}
void CPU::COP()
int CPU::COP(uint24_t, AddressingMode)
{
if (this->_isEmulationMode) {
this->_registers.pc += 2;
@@ -63,14 +64,16 @@ namespace ComSquare::CPU
this->_registers.pbr = 0x0;
this->_registers.pc = this->_cartridgeHeader.nativeInterrupts.cop;
}
return !this->_isEmulationMode;
}
void CPU::RTI()
int CPU::RTI(uint24_t, AddressingMode)
{
this->_registers.p.flags = this->_pop();
this->_registers.pc = this->_pop16();
if (!this->_isEmulationMode)
this->_registers.pbr = this->_pop16();
return !this->_isEmulationMode;
}
}

View File

@@ -7,7 +7,7 @@
namespace ComSquare::CPU
{
void CPU::ADC(uint24_t valueAddr)
int CPU::ADC(uint24_t valueAddr, AddressingMode mode)
{
unsigned value = this->_bus->read(valueAddr) + this->_registers.p.c;
if (!this->_registers.p.m)
@@ -25,9 +25,31 @@ namespace ComSquare::CPU
this->_registers.a %= 0x100;
this->_registers.p.z = this->_registers.a == 0;
this->_registers.p.n = this->_registers.a & negativeMask;
int cycles = !this->_registers.p.m;
switch (mode) {
case DirectPage:
case DirectPageIndirect:
case DirectPageIndirectLong:
case DirectPageIndexedByX:
case DirectPageIndirectIndexedByX:
case DirectPageIndirectIndexedByYLong:
cycles += this->_registers.dl != 0;
break;
case AbsoluteIndexedByX:
case AbsoluteIndexedByY:
cycles += this->_hasIndexCrossedPageBoundary;
break;
case DirectPageIndirectIndexedByY:
cycles += this->_registers.dl != 0 + this->_hasIndexCrossedPageBoundary;
break;
default:
break;
}
return cycles;
}
void CPU::SBC(uint24_t valueAddr)
int CPU::SBC(uint24_t valueAddr, AddressingMode mode)
{
unsigned negativeMask = this->_isEmulationMode ? 0x80u : 0x8000u;
unsigned value = this->_bus->read(valueAddr);
@@ -45,5 +67,27 @@ namespace ComSquare::CPU
this->_registers.a %= 0x100;
this->_registers.p.z = this->_registers.a == 0;
this->_registers.p.n = this->_registers.a & negativeMask;
int cycles = !this->_registers.p.m;
switch (mode) {
case DirectPage:
case DirectPageIndirect:
case DirectPageIndirectLong:
case DirectPageIndexedByX:
case DirectPageIndirectIndexedByX:
case DirectPageIndirectIndexedByYLong:
cycles += this->_registers.dl != 0;
break;
case AbsoluteIndexedByX:
case AbsoluteIndexedByY:
cycles += this->_hasIndexCrossedPageBoundary;
break;
case DirectPageIndirectIndexedByY:
cycles += this->_registers.dl != 0 + this->_hasIndexCrossedPageBoundary;
break;
default:
break;
}
return cycles;
}
}

View File

@@ -6,7 +6,7 @@
namespace ComSquare::CPU
{
void CPU::STA(uint24_t addr)
int CPU::STA(uint24_t addr, AddressingMode mode)
{
if (this->_registers.p.m)
this->_bus->write(addr, this->_registers.al);
@@ -14,9 +14,24 @@ namespace ComSquare::CPU
this->_bus->write(addr, this->_registers.al);
this->_bus->write(addr + 1, this->_registers.ah);
}
int cycles = !this->_registers.p.m;
switch (mode) {
case DirectPage:
case DirectPageIndirect:
case DirectPageIndirectLong:
case DirectPageIndexedByX:
case DirectPageIndirectIndexedByX:
case DirectPageIndirectIndexedByY:
case DirectPageIndirectIndexedByYLong:
cycles += this->_registers.dl != 0;
default:
break;
}
return cycles;
}
void CPU::STX(uint24_t addr)
int CPU::STX(uint24_t addr, AddressingMode mode)
{
if (this->_registers.p.x_b)
this->_bus->write(addr, this->_registers.xl);
@@ -24,9 +39,10 @@ namespace ComSquare::CPU
this->_bus->write(addr, this->_registers.xl);
this->_bus->write(addr + 1, this->_registers.xh);
}
return !this->_registers.p.x_b + (mode != Absolute && this->_registers.dl != 0);
}
void CPU::STY(uint24_t addr)
int CPU::STY(uint24_t addr, AddressingMode mode)
{
if (this->_registers.p.x_b)
this->_bus->write(addr, this->_registers.yl);
@@ -34,16 +50,20 @@ namespace ComSquare::CPU
this->_bus->write(addr, this->_registers.yl);
this->_bus->write(addr + 1, this->_registers.yh);
}
return !this->_registers.p.x_b + (mode != Absolute && this->_registers.dl != 0);
}
void CPU::STZ(uint24_t addr)
int CPU::STZ(uint24_t addr, AddressingMode mode)
{
this->_bus->write(addr, 0x00);
if (!this->_registers.p.m)
this->_bus->write(addr + 1, 0x00);
if (mode == Absolute || mode == AbsoluteIndexedByX)
return !this->_registers.p.m;
return !this->_registers.p.m + this->_registers.dl != 0;
}
void CPU::LDA(uint24_t addr)
int CPU::LDA(uint24_t addr, AddressingMode mode)
{
if (this->_registers.p.m) {
this->_registers.a = this->_bus->read(addr);
@@ -54,9 +74,31 @@ namespace ComSquare::CPU
this->_registers.p.n = this->_registers.a & 0xF000u;
}
this->_registers.p.z = this->_registers.a == 0x0;
int cycles = !this->_registers.p.m;
switch (mode) {
case DirectPage:
case DirectPageIndirect:
case DirectPageIndirectLong:
case DirectPageIndexedByX:
case DirectPageIndirectIndexedByX:
case DirectPageIndirectIndexedByYLong:
cycles += this->_registers.dl != 0;
break;
case AbsoluteIndexedByX:
case AbsoluteIndexedByY:
cycles += this->_hasIndexCrossedPageBoundary;
break;
case DirectPageIndirectIndexedByY:
cycles += this->_registers.dl != 0 + this->_hasIndexCrossedPageBoundary;
break;
default:
break;
}
return cycles;
}
void CPU::LDX(uint24_t addr)
int CPU::LDX(uint24_t addr, AddressingMode mode)
{
if (this->_registers.p.x_b) {
this->_registers.x = this->_bus->read(addr);
@@ -67,9 +109,23 @@ namespace ComSquare::CPU
this->_registers.p.n = this->_registers.x & 0xF000u;
}
this->_registers.p.z = this->_registers.x == 0x0;
int cycles = !this->_registers.p.x_b;
switch (mode) {
case DirectPage:
case DirectPageIndexedByY:
cycles += this->_registers.dl != 0;
break;
case AbsoluteIndexedByY:
cycles += this->_hasIndexCrossedPageBoundary;
break;
default:
break;
}
return cycles;
}
void CPU::LDY(uint24_t addr)
int CPU::LDY(uint24_t addr, AddressingMode mode)
{
if (this->_registers.p.x_b) {
this->_registers.y = this->_bus->read(addr);
@@ -80,5 +136,19 @@ namespace ComSquare::CPU
this->_registers.p.n = this->_registers.y & 0xF000u;
}
this->_registers.p.z = this->_registers.y == 0x0;
int cycles = !this->_registers.p.x_b;
switch (mode) {
case DirectPage:
case DirectPageIndexedByY:
cycles += this->_registers.dl != 0;
break;
case AbsoluteIndexedByY:
cycles += this->_hasIndexCrossedPageBoundary;
break;
default:
break;
}
return cycles;
}
}

View File

@@ -6,7 +6,7 @@
namespace ComSquare::CPU
{
void CPU::TAX()
int CPU::TAX(uint24_t, AddressingMode)
{
if (this->_registers.p.x_b) {
this->_registers.xl = this->_registers.al;
@@ -17,9 +17,10 @@ namespace ComSquare::CPU
this->_registers.p.z = this->_registers.x == 0;
this->_registers.p.n = this->_registers.x & 0x8000u;
}
return 0;
}
void CPU::TAY()
int CPU::TAY(uint24_t, AddressingMode)
{
if (this->_registers.p.x_b) {
this->_registers.yl = this->_registers.al;
@@ -30,9 +31,10 @@ namespace ComSquare::CPU
this->_registers.p.z = this->_registers.y == 0;
this->_registers.p.n = this->_registers.y & 0x8000u;
}
return 0;
}
void CPU::TXS()
int CPU::TXS(uint24_t, AddressingMode)
{
if (this->_registers.p.x_b) {
this->_registers.sh = 0;
@@ -44,5 +46,6 @@ namespace ComSquare::CPU
this->_registers.p.z = this->_registers.s == 0;
this->_registers.p.n = this->_registers.s & 0x8000u;
}
return 0;
}
}

View File

@@ -15,7 +15,6 @@ namespace ComSquare::Cartridge
Cartridge::Cartridge(const std::string &romPath)
: Ram::Ram(0, Rom, "Cartridge")
{
try {
if (romPath.empty())
throw InvalidRomException("Path is empty.");
size_t size = Cartridge::getRomSize(romPath);
@@ -28,9 +27,6 @@ namespace ComSquare::Cartridge
std::memset(this->_data, 0, size);
fread(this->_data, 1, size, rom);
this->_loadHeader();
} catch (InvalidRomException &ex) {
std::cerr << "Invalid Rom Error: " << ex.what() << std::endl;
}
}
size_t Cartridge::getRomSize(const std::string &romPath)

View File

@@ -5,8 +5,6 @@
#include "CGramDebug.hpp"
#include "../SNES.hpp"
#include <QColor>
#include <iostream>
#include <bitset>
#include <string>
#include "../Utility/Utility.hpp"

View File

@@ -5,8 +5,11 @@
#include "CPUDebug.hpp"
#include "../Utility/Utility.hpp"
#include "../Exceptions/InvalidOpcode.hpp"
#include "../CPU/CPU.hpp"
#include <QtEvents>
#include <QPainter>
#include <iostream>
#include <utility>
using namespace ComSquare::CPU;
@@ -16,6 +19,8 @@ namespace ComSquare::Debugger
: CPU(basicCPU),
_window(new ClosableWindow<CPUDebug>(*this, &CPUDebug::disableDebugger)),
_ui(),
_model(*this),
_painter(*this),
_snes(snes)
{
this->_window->setContextMenuPolicy(Qt::NoContextMenu);
@@ -23,9 +28,20 @@ namespace ComSquare::Debugger
this->_window->setAttribute(Qt::WA_DeleteOnClose);
this->_ui.setupUi(this->_window);
this->_updateDisassembly(0xFFFF - this->_registers.pc); //Parse the first page of the ROM (the code can't reach the second page without a jump).
this->_ui.disassembly->setModel(&this->_model);
this->_ui.disassembly->horizontalHeader()->setStretchLastSection(true);
this->_ui.disassembly->resizeColumnsToContents();
this->_ui.disassembly->verticalHeader()->setSectionResizeMode (QHeaderView::Fixed);
this->_ui.disassembly->verticalHeader()->setHighlightSections(false);
this->_ui.disassembly->setItemDelegate(&this->_painter);
QMainWindow::connect(this->_ui.actionPause, &QAction::triggered, this, &CPUDebug::pause);
QMainWindow::connect(this->_ui.actionStep, &QAction::triggered, this, &CPUDebug::step);
QMainWindow::connect(this->_ui.actionNext, &QAction::triggered, this, &CPUDebug::next);
QMainWindow::connect(this->_ui.clear, &QPushButton::released, this, &CPUDebug::clearHistory);
QMainWindow::connect(this->_ui.disassembly->verticalHeader(), &QHeaderView::sectionClicked, this, &CPUDebug::toggleBreakpoint);
this->_window->show();
this->_updateRegistersPanel();
}
@@ -43,25 +59,50 @@ namespace ComSquare::Debugger
unsigned CPUDebug::update()
{
try {
unsigned cycles = 0;
if (this->_isPaused)
return 0xFF;
if (this->_isStepping)
return this->_executeInstruction(this->_bus->read(this->_registers.pac++));
return CPU::update();
if (this->_isStepping) {
cycles = this->_executeInstruction(this->readPC());
this->_updateDisassembly();
return cycles;
}
for (int i = 0; i < 0xFF; i++) {
auto breakpoint = std::find_if(this->breakpoints.begin(), this->breakpoints.end(), [this](Breakpoint &brk) {
return brk.address == this->_registers.pac;
});
if (i != 0 && breakpoint != this->breakpoints.end()) {
if (breakpoint->oneTime)
this->breakpoints.erase(breakpoint);
this->_isPaused = false;
this->pause();
return cycles;
}
cycles += this->_executeInstruction(this->readPC());
}
return cycles;
} catch (InvalidOpcode &e) {
if (!this->_isPaused)
this->pause();
std::cout << "Invalid Opcode: " << e.what() << std::endl;
return 0xFF;
}
}
unsigned CPUDebug::_executeInstruction(uint8_t opcode)
{
if (this->_isPaused)
return 0;
if (this->_isStepping) {
this->_isStepping = false;
this->_isPaused = true;
}
this->_ui.logger->append((CPUDebug::_getInstructionString(this->_registers.pac - 1) + " - " + Utility::to_hex(opcode)).c_str());
uint24_t pc = (this->_registers.pbr << 16u) | (this->_registers.pc - 1u);
DisassemblyContext ctx = this->_getDisassemblyContext();
DisassembledInstruction instruction = this->_parseInstruction(pc, ctx);
this->_ui.logger->append((instruction.toString() + " - " + Utility::to_hex(opcode)).c_str());
unsigned ret = CPU::_executeInstruction(opcode);
this->_updateRegistersPanel();
return ret;
@@ -74,6 +115,7 @@ namespace ComSquare::Debugger
this->_ui.actionPause->setText("Resume");
else
this->_ui.actionPause->setText("Pause");
this->_updateDisassembly();
}
void CPUDebug::step()
@@ -82,6 +124,28 @@ namespace ComSquare::Debugger
this->_isPaused = false;
}
void CPUDebug::next()
{
auto next = std::find_if(this->disassembledInstructions.begin(), this->disassembledInstructions.end(), [this](DisassembledInstruction &i) {
return i.address > this->_registers.pac;
});
this->breakpoints.push_back({next->address, true});
this->_isPaused = false;
}
void CPUDebug::toggleBreakpoint(int logicalIndex)
{
DisassembledInstruction instruction = this->disassembledInstructions[logicalIndex];
auto existing = std::find_if(this->breakpoints.begin(), this->breakpoints.end(), [instruction](Breakpoint &i) {
return i.address == instruction.address;
});
if (existing == this->breakpoints.end())
this->breakpoints.push_back({instruction.address, false});
else
this->breakpoints.erase(existing);
this->_ui.disassembly->viewport()->repaint();
}
void CPUDebug::_updateRegistersPanel()
{
if (!this->_registers.p.m)
@@ -126,49 +190,130 @@ namespace ComSquare::Debugger
this->_ui.logger->clear();
}
std::string CPUDebug::_getImmediateValueForA(uint24_t pc)
void CPUDebug::_updateDisassembly(uint24_t refreshSize)
{
auto first = std::find_if(this->disassembledInstructions.begin(), this->disassembledInstructions.end(), [this](DisassembledInstruction &i) {
return i.address >= this->_registers.pac;
});
auto end = std::find_if(this->disassembledInstructions.begin(), this->disassembledInstructions.end(),[this, refreshSize](DisassembledInstruction &i) {
return i.address >= this->_registers.pac + refreshSize;
});
this->disassembledInstructions.erase(first, end);
auto next = std::find_if(this->disassembledInstructions.begin(), this->disassembledInstructions.end(), [this](DisassembledInstruction &i) {
return i.address >= this->_registers.pac;
});
int row = next - this->disassembledInstructions.begin();
DisassemblyContext ctx = this->_getDisassemblyContext();
std::vector<DisassembledInstruction> nextInstructions = this->_disassemble(this->_registers.pac, refreshSize, ctx);
this->disassembledInstructions.insert(next, nextInstructions.begin(), nextInstructions.end());
if (this->_ui.disassembly->rowAt(0) > row || this->_ui.disassembly->rowAt(this->_ui.disassembly->height()) < row)
this->_ui.disassembly->scrollTo(this->_model.index(row, 0), QAbstractItemView::PositionAtCenter);
this->_ui.disassembly->viewport()->repaint();
}
DisassemblyContext CPUDebug::_getDisassemblyContext()
{
return {this->_registers.p.m, this->_registers.p.x_b, false};
}
std::vector<DisassembledInstruction> CPUDebug::_disassemble(uint24_t pc, uint24_t length, DisassemblyContext &ctx)
{
std::vector<DisassembledInstruction> map;
uint24_t endAddr = pc + length;
while (pc < endAddr) {
DisassembledInstruction instruction = this->_parseInstruction(pc, ctx);
instruction.level = ctx.level;
map.push_back(instruction);
pc += instruction.size;
if (instruction.addressingMode == ImmediateForA && !ctx.mFlag)
pc++;
if (instruction.addressingMode == ImmediateForX && !ctx.xFlag)
pc++;
if (instruction.opcode == 0x40 && ctx.isEmulationMode) { // RTI
ctx.mFlag = true;
ctx.xFlag = true;
}
if (instruction.opcode == 0xC2) { // REP
if (ctx.isEmulationMode) {
ctx.mFlag = true;
ctx.xFlag = true;
} else {
uint8_t m = this->_bus->read(pc - 1);
ctx.mFlag &= ~m & 0b00100000u;
ctx.xFlag &= ~m & 0b00010000u;
}
}
if (instruction.opcode == 0xE2) { // SEP
uint8_t m = this->_bus->read(pc - 1);
ctx.mFlag |= m & 0b00100000u;
ctx.xFlag |= m & 0b00010000u;
}
if (instruction.opcode == 0x28) { // PLP
if (ctx.isEmulationMode) {
ctx.mFlag = true;
ctx.xFlag = true;
} else
ctx.level = Compromised;
}
if (instruction.opcode == 0xFB) {// XCE
ctx.level = Unsafe;
ctx.isEmulationMode = false; // The most common use of the XCE is to enable native mode at the start of the ROM so we guess that it has done that.
}
}
return map;
}
DisassembledInstruction CPUDebug::_parseInstruction(uint24_t pc, DisassemblyContext &ctx)
{
uint24_t opcode = this->_bus->read(pc, true);
Instruction instruction = this->_instructions[opcode];
std::string argument = this->_getInstructionParameter(instruction, pc + 1, ctx);
return DisassembledInstruction(instruction, pc, argument, opcode);
}
std::string CPUDebug::_getInstructionParameter(Instruction &instruction, uint24_t pc, DisassemblyContext &ctx)
{
switch (instruction.addressingMode) {
case Implied:
return "";
case ImmediateForA:
return this->_getImmediateValue(pc, !ctx.mFlag);
case ImmediateForX:
return this->_getImmediateValue(pc, !ctx.xFlag);
case Immediate8bits:
return this->_getImmediateValue(pc, false);
case Absolute:
return this->_getAbsoluteValue(pc);
case AbsoluteLong:
return this->_getAbsoluteLongValue(pc);
case DirectPage:
return this->_getDirectValue(pc);
case DirectPageIndexedByX:
return this->_getDirectIndexedByXValue(pc);
default:
return "???";
}
}
std::string CPUDebug::_getImmediateValue(uint24_t pc, bool dual)
{
unsigned value = this->_bus->read(pc, true);
if (!this->_registers.p.m)
if (dual)
value += this->_bus->read(pc + 1, true) << 8u;
std::stringstream ss;
ss << "#$" << std::hex << value;
return ss.str();
}
std::string CPUDebug::_getImmediateValueForX(uint24_t pc)
{
unsigned value = this->_bus->read(pc, true);
if (!this->_registers.p.x_b)
value += this->_bus->read(pc + 1, true) << 8u;
std::stringstream ss;
ss << "#$" << std::hex << value;
return ss.str();
}
std::string CPUDebug::_getImmediateValue8Bits(uint24_t pc)
{
std::stringstream ss;
ss << "#$" << std::hex << static_cast<int>(this->_bus->read(pc), true);
return ss.str();
}
std::string CPUDebug::_getImmediateValue16Bits(uint24_t pc)
{
unsigned value = this->_bus->read(pc, true);
value += this->_bus->read(pc + 1, true) << 8u;
std::stringstream ss;
ss << "#$" << std::hex << value;
return ss.str();
}
std::string CPUDebug::_getDirectValue(uint24_t pc)
{
std::stringstream ss;
ss << "$" << std::hex << static_cast<int>(this->_bus->read(pc), true);
ss << "$" << std::hex << static_cast<int>(this->_bus->read(pc, true));
return ss.str();
}
@@ -199,201 +344,101 @@ namespace ComSquare::Debugger
return ss.str();
}
std::string CPUDebug::_getInstructionString(uint24_t pc)
{
uint8_t opcode = this->_bus->read(pc++, true);
switch (opcode) {
case Instructions::BRK: return "BRK";
case Instructions::COP: return "COP";
case Instructions::RTI: return "RTI";
case Instructions::ADC_IM: return "ADC " + this->_getImmediateValueForA(pc);
case Instructions::ADC_ABS: return "ADC " + this->_getAbsoluteValue(pc);
case Instructions::ADC_ABSl: return "ADC " + this->_getAbsoluteLongValue(pc);
case Instructions::ADC_DP: return "ADC " + this->_getDirectValue(pc);
case Instructions::ADC_DPi: return "ADC";
case Instructions::ADC_DPil: return "ADC";
case Instructions::ADC_ABSX: return "ADC";
case Instructions::ADC_ABSXl:return "ADC";
case Instructions::ADC_ABSY: return "ADC";
case Instructions::ADC_DPX: return "ADC " + this->_getDirectIndexedByXValue(pc);
case Instructions::ADC_DPXi: return "ADC";
case Instructions::ADC_DPYi: return "ADC";
case Instructions::ADC_DPYil:return "ADC";
case Instructions::ADC_SR: return "ADC";
case Instructions::ADC_SRYi: return "ADC";
case Instructions::STA_ABS: return "STA " + this->_getAbsoluteValue(pc);
case Instructions::STA_ABSl: return "STA " + this->_getAbsoluteLongValue(pc);
case Instructions::STA_DP: return "STA " + this->_getDirectValue(pc);
case Instructions::STA_DPi: return "STA";
case Instructions::STA_DPil: return "STA";
case Instructions::STA_ABSX: return "STA";
case Instructions::STA_ABSXl:return "STA";
case Instructions::STA_ABSY: return "STA";
case Instructions::STA_DPX: return "STA " + this->_getDirectIndexedByXValue(pc);
case Instructions::STA_DPXi: return "STA";
case Instructions::STA_DPYi: return "STA";
case Instructions::STA_DPYil:return "STA";
case Instructions::STA_SR: return "STA";
case Instructions::STA_SRYi: return "STA";
case Instructions::STX_ABS: return "STX " + this->_getAbsoluteValue(pc);
case Instructions::STX_DP: return "STX " + this->_getDirectValue(pc);
case Instructions::STX_DPY: return "STX";
case Instructions::STY_ABS: return "STY " + this->_getAbsoluteValue(pc);
case Instructions::STY_DP: return "STY " + this->_getDirectValue(pc);
case Instructions::STY_DPX: return "STY " + this->_getDirectIndexedByXValue(pc);
case Instructions::STZ_ABS: return "STZ " + this->_getAbsoluteValue(pc);
case Instructions::STZ_DP: return "STZ " + this->_getDirectValue(pc);
case Instructions::STZ_ABSX: return "STZ";
case Instructions::STZ_DPX: return "STZ " + this->_getDirectIndexedByXValue(pc);
case Instructions::LDA_IM: return "LDA " + this->_getImmediateValueForA(pc);
case Instructions::LDA_ABS: return "LDA " + this->_getAbsoluteValue(pc);
case Instructions::LDA_ABSl: return "LDA " + this->_getAbsoluteLongValue(pc);
case Instructions::LDA_DP: return "LDA " + this->_getDirectValue(pc);
case Instructions::LDA_DPi: return "LDA";
case Instructions::LDA_DPil: return "LDA";
case Instructions::LDA_ABSX: return "LDA";
case Instructions::LDA_ABSXl:return "LDA";
case Instructions::LDA_ABSY: return "LDA";
case Instructions::LDA_DPX: return "LDA " + this->_getDirectIndexedByXValue(pc);
case Instructions::LDA_DPXi: return "LDA";
case Instructions::LDA_DPYi: return "LDA";
case Instructions::LDA_DPYil:return "LDA";
case Instructions::LDA_SR: return "LDA";
case Instructions::LDA_SRYi: return "LDA";
case Instructions::LDX_IM: return "LDX " + this->_getImmediateValueForX(pc);
case Instructions::LDX_ABS: return "LDX " + this->_getAbsoluteValue(pc);
case Instructions::LDX_DP: return "LDX " + this->_getDirectValue(pc);
case Instructions::LDX_ABSY: return "LDX";
case Instructions::LDX_DPY: return "LDX";
case Instructions::LDY_IM: return "LDY " + this->_getImmediateValueForX(pc);
case Instructions::LDY_ABS: return "LDY " + this->_getAbsoluteValue(pc);
case Instructions::LDY_DP: return "LDY " + this->_getDirectValue(pc);
case Instructions::LDY_ABSY: return "LDY";
case Instructions::LDY_DPY: return "LDY";
case Instructions::SEP: return "SEP " + this->_getImmediateValue8Bits(pc);
case Instructions::REP: return "REP " + this->_getImmediateValue8Bits(pc);
case Instructions::PHA: return "PHA";
case Instructions::PHB: return "PHB";
case Instructions::PHD: return "PHD";
case Instructions::PHK: return "PHK";
case Instructions::PHP: return "PHP";
case Instructions::PHX: return "PHX";
case Instructions::PHY: return "PHY";
case Instructions::PLA: return "PLA";
case Instructions::PLB: return "PLB";
case Instructions::PLD: return "PLD";
case Instructions::PLP: return "PLP";
case Instructions::PLX: return "PLX";
case Instructions::PLY: return "PLY";
case Instructions::JSR_ABS: return "JSR " + this->_getAbsoluteValue(pc);
case Instructions::JSR_ABSXi: return "JSR";
case Instructions::JSL: return "JSL " + this->_getAbsoluteLongValue(pc);
case Instructions::CLC: return "CLC";
case Instructions::CLI: return "CLI";
case Instructions::CLD: return "CLD";
case Instructions::CLV: return "CLV";
case Instructions::SEC: return "SEC";
case Instructions::SED: return "SED";
case Instructions::SEI: return "SEI";
case Instructions::AND_IM: return "AND " + this->_getImmediateValueForA(pc);
case Instructions::AND_ABS: return "AND " + this->_getAbsoluteValue(pc);
case Instructions::AND_ABSl: return "AND " + this->_getAbsoluteLongValue(pc);
case Instructions::AND_DP: return "AND " + this->_getDirectValue(pc);
case Instructions::AND_DPi: return "AND";
case Instructions::AND_DPil: return "AND";
case Instructions::AND_ABSX: return "AND";
case Instructions::AND_ABSXl:return "AND";
case Instructions::AND_ABSY: return "AND";
case Instructions::AND_DPX: return "AND " + this->_getDirectIndexedByXValue(pc);
case Instructions::AND_DPXi: return "AND";
case Instructions::AND_DPYi: return "AND";
case Instructions::AND_DPYil:return "AND";
case Instructions::AND_SR: return "AND";
case Instructions::AND_SRYi: return "AND";
case Instructions::XCE: return "XCE";
case Instructions::SBC_IM: return "SBC " + this->_getImmediateValueForA(pc);
case Instructions::SBC_ABS: return "SBC " + this->_getAbsoluteValue(pc);
case Instructions::SBC_ABSl: return "SBC " + this->_getAbsoluteLongValue(pc);
case Instructions::SBC_DP: return "SBC " + this->_getDirectValue(pc);
case Instructions::SBC_DPi: return "SBC";
case Instructions::SBC_DPil: return "SBC";
case Instructions::SBC_ABSX: return "SBC";
case Instructions::SBC_ABSXl:return "SBC";
case Instructions::SBC_ABSY: return "SBC";
case Instructions::SBC_DPX: return "SBC " + this->_getDirectIndexedByXValue(pc);
case Instructions::SBC_DPXi: return "SBC";
case Instructions::SBC_DPYi: return "SBC";
case Instructions::SBC_DPYil:return "SBC";
case Instructions::SBC_SR: return "SBC";
case Instructions::SBC_SRYi: return "SBC";
case Instructions::TAX: return "TAX";
case Instructions::TAY: return "TAY";
case Instructions::TXS: return "TXS";
case Instructions::INX: return "INX";
case Instructions::INY: return "INY";
case Instructions::CPX_IM: return "CPX " + this->_getImmediateValueForX(pc);
case Instructions::CPX_ABS: return "CPX " + this->_getAbsoluteValue(pc);
case Instructions::CPX_DP: return "CPX";
case Instructions::CPY_IM: return "CPY " + this->_getImmediateValueForX(pc);
case Instructions::CPY_ABS: return "CPY " + this->_getAbsoluteValue(pc);
case Instructions::CPY_DP: return "CPY";
case Instructions::BCC: return "BCC " + this->_getImmediateValue8Bits(pc);
case Instructions::BCS: return "BCS " + this->_getImmediateValue8Bits(pc);
case Instructions::BEQ: return "BEQ " + this->_getImmediateValue8Bits(pc);
case Instructions::BNE: return "BNE " + this->_getImmediateValue8Bits(pc);
case Instructions::BMI: return "BMI " + this->_getImmediateValue8Bits(pc);
case Instructions::BPL: return "BPL " + this->_getImmediateValue8Bits(pc);
case Instructions::BVC: return "BVC " + this->_getImmediateValue8Bits(pc);
case Instructions::BVS: return "BVS " + this->_getImmediateValue8Bits(pc);
case Instructions::BRA: return "BRA " + this->_getImmediateValue8Bits(pc);
case Instructions::BRL: return "BRL " + this->_getImmediateValue16Bits(pc);
case Instructions::JMP_ABS: return "JMP " + this->_getAbsoluteValue(pc);
case Instructions::JMP_ABSi: return "JMP "; //+ this->_getAbsoluteIndire(pc);
case Instructions::JMP_ABSXi: return "JMP "; //+ this->_getAbsoluteValue(pc);
case Instructions::JML_ABSl: return "JML";
case Instructions::JML_ABSil: return "JML";
default: return "Unknown";
}
}
void CPUDebug::RESB()
int CPUDebug::RESB()
{
CPU::RESB();
this->_updateRegistersPanel();
return (0);
}
void CPUDebug::focus()
{
this->_window->activateWindow();
}
uint24_t CPUDebug::getPC()
{
return this->_registers.pac;
}
DisassembledInstruction::DisassembledInstruction(const CPU::Instruction &instruction, uint24_t addr, std::string arg, uint8_t op)
: CPU::Instruction(instruction), address(addr), argument(std::move(arg)), opcode(op) {}
std::string DisassembledInstruction::toString()
{
return this->name + " " + this->argument;
}
}
DisassemblyModel::DisassemblyModel(ComSquare::Debugger::CPUDebug &cpu) : QAbstractTableModel(), _cpu(cpu){ }
int DisassemblyModel::columnCount(const QModelIndex &) const
{
return 4;
}
int DisassemblyModel::rowCount(const QModelIndex &) const
{
return this->_cpu.disassembledInstructions.size();
}
QVariant DisassemblyModel::data(const QModelIndex &index, int role) const
{
if (role != Qt::DisplayRole && role != Qt::DecorationRole)
return QVariant();
ComSquare::Debugger::DisassembledInstruction instruction = this->_cpu.disassembledInstructions[index.row()];
if (role == Qt::DecorationRole) {
if (index.column() == 3 && instruction.level == ComSquare::Debugger::TrustLevel::Unsafe)
return QColor(Qt::yellow);
if (index.column() == 3 && instruction.level == ComSquare::Debugger::TrustLevel::Compromised)
return QColor(Qt::red);
return QVariant();
}
switch (index.column()) {
case 0:
return QString(instruction.name.c_str());
case 1:
return QString(instruction.argument.c_str());
default:
return QVariant();
}
}
QVariant DisassemblyModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal)
return QVariant();
if (role != Qt::DisplayRole)
return QVariant();
ComSquare::Debugger::DisassembledInstruction instruction = this->_cpu.disassembledInstructions[section];
return QString(ComSquare::Utility::to_hex(instruction.address, ComSquare::Utility::HexString::NoPrefix).c_str());
}
RowPainter::RowPainter(ComSquare::Debugger::CPUDebug &cpu, QObject *parent) : QStyledItemDelegate(parent), _cpu(cpu) { }
void RowPainter::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
ComSquare::Debugger::DisassembledInstruction instruction = this->_cpu.disassembledInstructions[index.row()];
bool isBreakpoint = false;
auto breakpoint = std::find_if(this->_cpu.breakpoints.begin(), this->_cpu.breakpoints.end(), [instruction](ComSquare::Debugger::Breakpoint brk) {
return brk.address == instruction.address;
});
if (breakpoint != this->_cpu.breakpoints.end())
isBreakpoint = true;
QStyleOptionViewItem style = option;
if (instruction.address == this->_cpu.getPC()) {
painter->fillRect(option.rect, QColor(Qt::darkGreen));
style.state &= ~QStyle::State_Selected;
} else if (isBreakpoint && !breakpoint->oneTime) {
painter->fillRect(option.rect,QColor(Qt::darkRed));
style.state &= ~QStyle::State_Selected;
}
QStyledItemDelegate::paint(painter, style, index);
}
QSize RowPainter::sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const
{
return QSize();
}

View File

@@ -5,6 +5,7 @@
#ifndef COMSQUARE_CPUDEBUG_HPP
#define COMSQUARE_CPUDEBUG_HPP
#include <QtWidgets/QStyledItemDelegate>
#include "../CPU/CPU.hpp"
#include "../Renderer/SFRenderer.hpp"
#include "../SNES.hpp"
@@ -13,6 +14,95 @@
namespace ComSquare::Debugger
{
class CPUDebug;
}
//! @brief The qt model that show the disassembly.
class DisassemblyModel : public QAbstractTableModel
{
Q_OBJECT
private:
ComSquare::Debugger::CPUDebug &_cpu;
public:
explicit DisassemblyModel(ComSquare::Debugger::CPUDebug &cpu);
DisassemblyModel(const DisassemblyModel &) = delete;
const DisassemblyModel &operator=(const DisassemblyModel &) = delete;
~DisassemblyModel() override = default;
//! @brief The number of row the table has.
int rowCount(const QModelIndex &parent) const override;
//! @brief The number of column the table has.
int columnCount(const QModelIndex &parent) const override;
//! @brief Return a data representing the table cell.
QVariant data(const QModelIndex &index, int role) const override;
//! @brief Override the headers to use hex values.
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
};
//! @brief The qt class that highlight breakpoints and the PC's position
class RowPainter : public QStyledItemDelegate {
Q_OBJECT
private:
//! @brief The CPU to get PC and breakpoints from.
ComSquare::Debugger::CPUDebug &_cpu;
public:
explicit RowPainter(ComSquare::Debugger::CPUDebug &cpu, QObject *parent = nullptr);
RowPainter &operator=(const RowPainter &) = delete;
~RowPainter() override = default;
protected:
QSize sizeHint(const QStyleOptionViewItem &options, const QModelIndex &index) const override;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
};
namespace ComSquare::Debugger
{
enum TrustLevel {
Safe,
Unsafe,
Compromised
};
//! @brief Struct used to emulate the state of the processor during the disassembly since instructions take a different amount of space depending on some flags.
struct DisassemblyContext {
//! @brief The accumulator and Memory width flag (in native mode only) - 0 = 16 bits mode, 1 = 8 bits mode.
//! @info If this flag is set to false, instructions that have the ImmediateByA addressing mode take 1 more bit of space.
bool mFlag = true;
//! @brief The indeX register width flag (in native mode only) - 0 = 16 bits mode, 1 = 8 bits mode OR the Break flag (in emulation mode only)
//! @info If this flag is set to false, instructions that have the ImmediateByX addressing mode take 1 more bit of space.
bool xFlag = true;
//! @brief Is the CPU emulating a 6502? If yes, some instructions don't change flags the same way.
bool isEmulationMode = true;
//! @brief Sometimes, the flags can't be tracked correctly after an instruction so the next instructions may not be correctly disassembled.
TrustLevel level = Safe;
};
//! @brief Struct representing an instruction in an human readable way (created by disassembling the rom).
struct DisassembledInstruction : public CPU::Instruction {
//! @brief The address of the instruction
uint24_t address;
//! @brief A string representing the argument with the right addressing mode.
std::string argument;
//! @brief The opcode of the instruction
uint8_t opcode;
//! @brief Are we sure that this instruction has been correctly disassembled?
TrustLevel level;
DisassembledInstruction(const CPU::Instruction &instruction, uint24_t address, std::string argument, uint8_t opcode);
DisassembledInstruction(const DisassembledInstruction &) = default;
DisassembledInstruction &operator=(const DisassembledInstruction &) = default;
~DisassembledInstruction() = default;
std::string toString();
};
//! @brief Struct representing a breakpoint set by the user or by the app
struct Breakpoint {
//! @brief The address of the breakpoint
uint24_t address;
//! @brief If this is true, the breakpoint will be deleted on first hit and won't be shown on the disassembly view.
bool oneTime;
};
//! @brief A custom CPU with a window that show it's registers and the disassembly.
class CPUDebug : public CPU::CPU, public QObject {
private:
@@ -20,6 +110,10 @@ namespace ComSquare::Debugger
ClosableWindow<CPUDebug> *_window;
//! @brief A widget that contain the whole UI.
Ui::CPUView _ui;
//! @brief The disassembly viewer's model.
DisassemblyModel _model;
//! @brief A custom painter that highlight breakpoints and the PC's position.
RowPainter _painter;
//! @brief If this is set to true, the execution of the CPU will be paused.
bool _isPaused = true;
//! @brief If this is set to true, the CPU will execute one instruction and pause itself.
@@ -28,21 +122,25 @@ namespace ComSquare::Debugger
SNES &_snes;
//! @brief Reimplement the basic instruction execution method to log instructions inside the logger view.
unsigned _executeInstruction(uint8_t opcode) override;
//! @brief Get a printable string representing an instruction at the program counter given as parameter.
std::string _getInstructionString(uint24_t pc);
//! @brief Return a disassembly context representing the current state of the processor.
DisassemblyContext _getDisassemblyContext();
//! @brief Disassemble part of the memory (using the bus) and parse it to a map of address and disassembled instruction.
//! @param ctx The initial context of the processor before the disassembly begin.
std::vector<DisassembledInstruction> _disassemble(uint24_t startAddr, uint24_t size, DisassemblyContext &ctx);
//! @brief Update disassembly with the new state of the processor.
void _updateDisassembly(uint24_t refreshSize = 0xFF);
//! @brief Parse the instruction at the program counter given to have human readable information.
DisassembledInstruction _parseInstruction(uint24_t pc, DisassemblyContext &ctx);
//! @brief Get the parameter of the instruction as an hexadecimal string.
std::string _getInstructionParameter(ComSquare::CPU::Instruction &instruction, uint24_t pc, DisassemblyContext &ctx);
//! @brief Get a printable string representing the flags.
std::string _getFlagsString();
//! @brief Update the register's panel (accumulator, stack pointer...)
void _updateRegistersPanel();
//! @brief Return a printable string corresponding to the value of an immediate addressing mode for a.
std::string _getImmediateValueForA(uint24_t pc);
//! @brief Return a printable string corresponding to the value of an immediate addressing mode for x.
std::string _getImmediateValueForX(uint24_t pc);
//! @brief Return a printable string corresponding to the value of a 8bits immediate addressing mode (used only with SEP and REP).
std::string _getImmediateValue8Bits(uint24_t pc);
//! @brief Return a printable string corresponding to the value of a 16bits immediate addressing mode.
std::string _getImmediateValue16Bits(uint24_t pc);
//! @brief Return a printable string corresponding to the value of a 8 or 16 bits immediate addressing mode.
//! @param dual Set this to true if the instruction take 16bits and not 8. (used for the immediate by a when the flag m is not set or the immediate by x if the flag x is not set).
std::string _getImmediateValue(uint24_t pc, bool dual);
//! @brief Return a printable string corresponding to the value of a direct addressing mode.
std::string _getDirectValue(uint24_t pc);
//! @brief Return a printable string corresponding to the value of an absolute addressing mode.
@@ -52,18 +150,27 @@ namespace ComSquare::Debugger
//! @brief Return a printable string corresponding to the value of a direct index by x addressing mode.
std::string _getDirectIndexedByXValue(uint24_t pc);
public slots:
public:
//! @brief Pause/Resume the CPU.
void pause();
//! @brief Step - Execute a single instruction.
void step();
//! @brief Next - Continue running instructions until the next line is reached.
void next();
//! @brief Clear the history panel.
void clearHistory();
//! @brief Called when the user clicks on a section header. It enable/disable a breakpoint for this address.
void toggleBreakpoint(int logicalIndex);
//! @brief Called when the window is closed. Turn off the debugger and revert to a basic CPU.
void disableDebugger();
public:
//! @brief Update the UI when reseting the CPU.
void RESB() override;
//! @brief The list of disassembled instructions to show on the debugger.
std::vector<DisassembledInstruction> disassembledInstructions;
//! @brief The list of breakpoints the user has set.
std::vector<Breakpoint> breakpoints;
//! @brief Return the current program counter of this CPU.
uint24_t getPC();
//! @brief Update the UI when resetting the CPU.
int RESB() override;
//! @brief Convert a basic CPU to a debugging CPU.
explicit CPUDebug(ComSquare::CPU::CPU &cpu, SNES &snes);
CPUDebug(const CPUDebug &) = delete;

View File

@@ -25,6 +25,8 @@ namespace ComSquare::Debugger
this->_ui.setupUi(this->_window);
this->_proxy.setSourceModel(&this->_model);
this->_ui.log->setModel(&this->_proxy);
this->_ui.log->setAlternatingRowColors(true);
this->_ui.log->verticalHeader()->hide();
this->_ui.log->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
this->_ui.log->horizontalHeader()->setStretchLastSection(true);
this->_ui.log->horizontalHeader()->setSectionsMovable(true);

View File

@@ -0,0 +1,15 @@
//
// Created by anonymus-raccoon on 3/26/20.
//
#ifndef COMSQUARE_DEBUGGABLEERROR_HPP
#define COMSQUARE_DEBUGGABLEERROR_HPP
#include <bits/exception.h>
namespace ComSquare
{
class DebuggableError : public std::exception {};
}
#endif //COMSQUARE_DEBUGGABLEERROR_HPP

View File

@@ -7,11 +7,12 @@
#include <exception>
#include <string>
#include "DebuggableError.hpp"
namespace ComSquare
{
//! @brief Exception thrown when someone tries to load an invalid rom.
class InvalidAction : public std::exception {
class InvalidAction : public DebuggableError {
private:
std::string _msg;
public:

View File

@@ -9,11 +9,12 @@
#include <string>
#include <ios>
#include <sstream>
#include "DebuggableError.hpp"
namespace ComSquare
{
//! @brief Exception thrown when trying to read/write to an invalid address.
class InvalidAddress : public std::exception {
class InvalidAddress : public DebuggableError {
private:
std::string _msg;
public:

View File

@@ -8,11 +8,12 @@
#include <exception>
#include <string>
#include <sstream>
#include "DebuggableError.hpp"
namespace ComSquare
{
//! @brief Exception thrown when someone tries to load an invalid rom.
class InvalidOpcode : public std::exception {
class InvalidOpcode : public DebuggableError {
private:
std::string _msg;
public:

View File

@@ -7,11 +7,12 @@
#include <exception>
#include <string>
#include "DebuggableError.hpp"
namespace ComSquare
{
//! @brief Exception thrown when someone tries to load an invalid rom.
class InvalidRomException : public std::exception {
class InvalidRomException : public DebuggableError {
private:
std::string _msg;
public:

View File

@@ -8,6 +8,9 @@
#include <QMenuBar>
#include <iostream>
#include "QtSFML.hpp"
#include "../../Exceptions/InvalidOpcode.hpp"
#include "../../Exceptions/InvalidAddress.hpp"
#include "../../Exceptions/InvalidAction.hpp"
#ifdef Q_WS_X11
#include <Qt/qx11info_x11.h>
@@ -88,6 +91,9 @@ namespace ComSquare::Renderer
{
try {
this->_snes.update();
} catch (DebuggableError &e) {
std::cout << "Invalid rom's instruction: " << e.what() << std::endl;
this->_snes.enableCPUDebugging(true);
} catch (std::exception &e) {
std::cerr << "An error occurred: " << e.what() << std::endl;
QApplication::quit();

View File

@@ -35,15 +35,19 @@ namespace ComSquare
this->apu->update(cycleCount);
}
void SNES::enableCPUDebugging()
void SNES::enableCPUDebugging(bool pause)
{
#ifdef DEBUGGER_ENABLED
if (this->cpu->isDebugger())
std::static_pointer_cast<Debugger::CPUDebug>(this->cpu)->focus();
else
if (this->cpu->isDebugger()) {
auto cpuDebug = std::static_pointer_cast<Debugger::CPUDebug>(this->cpu);
cpuDebug->focus();
if (pause)
cpuDebug->pause();
} else
this->cpu = std::make_shared<Debugger::CPUDebug>(*this->cpu, *this);
#else
std::cerr << "Debugging features are not enabled. You can't enable the debugger." << std::endl;
(void)pause;
#endif
}

View File

@@ -56,7 +56,7 @@ namespace ComSquare
//! @brief Disable the CPU's debugging window.
void disableCPUDebugging();
//! @brief Enable the CPU's debugging window.
void enableCPUDebugging();
void enableCPUDebugging(bool pause = false);
//! @brief Disable the Ram's debugging window.
void disableRamViewer();
//! @brief Enable the Ram's debugging window.

View File

@@ -3,29 +3,42 @@
//
#include <bitset>
#include <iomanip>
#include "Utility.hpp"
namespace ComSquare::Utility
{
std::string to_hex(uint8_t i)
std::string to_hex(uint8_t i, HexString prefix)
{
char buf[5];
sprintf(buf, "0x%02X", i);
return buf;
std::stringstream ss;
if (prefix == AsmPrefix)
ss << "$";
else if (prefix == StandardPrefix)
ss << "0x";
ss << std::hex << std::setfill('0') << std::setw(2) << static_cast<unsigned>(i);
return ss.str();
}
std::string to_hex(uint16_t i)
std::string to_hex(uint16_t i, HexString prefix)
{
char buf[7];
sprintf(buf, "0x%04X", i);
return buf;
std::stringstream ss;
if (prefix == AsmPrefix)
ss << "$";
else if (prefix == StandardPrefix)
ss << "0x";
ss << std::hex << std::setfill('0') << std::setw(4) << i;
return ss.str();
}
std::string to_hex(uint24_t i)
std::string to_hex(uint24_t i, HexString prefix)
{
char buf[9];
sprintf(buf, "0x%06X", i);
return buf;
std::stringstream ss;
if (prefix == AsmPrefix)
ss << "$";
else if (prefix == StandardPrefix)
ss << "0x";
ss << std::hex << std::setfill('0') << std::setw(6) << i;
return ss.str();
}
std::string to_binary(uint8_t i)

View File

@@ -12,11 +12,17 @@
namespace ComSquare::Utility
{
std::string to_hex(uint8_t i);
enum HexString {
NoPrefix,
AsmPrefix,
StandardPrefix
};
std::string to_hex(uint16_t i);
std::string to_hex(uint8_t i, HexString prefix = StandardPrefix);
std::string to_hex(uint24_t i);
std::string to_hex(uint16_t i, HexString prefix = StandardPrefix);
std::string to_hex(uint24_t i, HexString prefix = StandardPrefix);
std::string to_binary(uint8_t i);

View File

@@ -77,8 +77,14 @@ int main(int argc, char **argv)
QApplication app(argc, argv);
QApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton);
Renderer::QtSFML renderer(600, 800);
try {
SNES snes(argv[1], renderer);
renderer.createWindow(snes, 60);
parseArguments(argc, argv, snes);
return QApplication::exec();
}
catch(std::exception &ex) {
std::cerr << ex.what() << std::endl;
return 1;
}
}

View File

@@ -14,7 +14,7 @@ Test(ADC, addingOne)
snes.cpu->_isEmulationMode = false;
snes.cpu->_registers.a = 0;
snes.wram->_data[0] = 0x1;
snes.cpu->ADC(0x0);
snes.cpu->ADC(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.a, 1, "The accumulator's value should be 0x1 but it was 0x%x.", snes.cpu->_registers.a);
cr_assert_eq(snes.cpu->_registers.p.c, false, "The carry flags should not be set.");
cr_assert_eq(snes.cpu->_registers.p.v, false, "The overflow flags should not be set.");
@@ -28,7 +28,7 @@ Test(ADC, addingOneEmulation)
snes.cpu->_isEmulationMode = true;
snes.cpu->_registers.a = 0;
snes.wram->_data[0] = 0x1;
snes.cpu->ADC(0x0);
snes.cpu->ADC(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.a, 1, "The accumulator's value should be 0x1 but it was 0x%x.", snes.cpu->_registers.a);
cr_assert_eq(snes.cpu->_registers.p.c, false, "The carry flags should not be set.");
cr_assert_eq(snes.cpu->_registers.p.v, false, "The overflow flags should not be set.");
@@ -42,7 +42,7 @@ Test(ADC, overflow)
snes.cpu->_isEmulationMode = false;
snes.cpu->_registers.a = 0xFFFF;
snes.wram->_data[0] = 0x1;
snes.cpu->ADC(0x0);
snes.cpu->ADC(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.a, 0, "The accumulator's value should be 0x0 but it was 0x%x.", snes.cpu->_registers.a);
cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flags should be set.");
cr_assert_eq(snes.cpu->_registers.p.v, false, "The overflow flags should not be set.");
@@ -56,7 +56,7 @@ Test(ADC, overflowEmulation)
snes.cpu->_isEmulationMode = true;
snes.cpu->_registers.a = 0xFF;
snes.wram->_data[0] = 0x1;
snes.cpu->ADC(0x0);
snes.cpu->ADC(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.a, 0, "The accumulator's value should be 0x0 but it was 0x%x.", snes.cpu->_registers.a);
cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flags should be set.");
cr_assert_eq(snes.cpu->_registers.p.v, false, "The overflow flags should not be set.");
@@ -70,7 +70,7 @@ Test(ADC, signedOverflow)
snes.cpu->_isEmulationMode = false;
snes.cpu->_registers.a = 0x7FFF;
snes.wram->_data[0] = 0x1;
snes.cpu->ADC(0x0);
snes.cpu->ADC(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.a, 0x8000, "The accumulator's value should be 0x8000 but it was 0x%x.", snes.cpu->_registers.a);
cr_assert_eq(snes.cpu->_registers.p.c, false, "The carry flags should not be set.");
cr_assert_eq(snes.cpu->_registers.p.v, true, "The overflow flags should be set.");
@@ -84,7 +84,7 @@ Test(ADC, signedOverflowEmulated)
snes.cpu->_isEmulationMode = true;
snes.cpu->_registers.a = 0x007F;
snes.wram->_data[0] = 0x1;
snes.cpu->ADC(0x0);
snes.cpu->ADC(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.a, 0x0080, "The accumulator's value should be 0x0080 but it was 0x%x.", snes.cpu->_registers.a);
cr_assert_eq(snes.cpu->_registers.p.c, false, "The carry flags should not be set.");
cr_assert_eq(snes.cpu->_registers.p.v, true, "The overflow flags should be set.");
@@ -98,7 +98,7 @@ Test(ADC, negative)
snes.cpu->_isEmulationMode = false;
snes.cpu->_registers.a = 0x8FFF;
snes.wram->_data[0] = 0x1;
snes.cpu->ADC(0x0);
snes.cpu->ADC(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.a, 0x9000, "The accumulator's value should be 0x9000 but it was 0x%x.", snes.cpu->_registers.a);
cr_assert_eq(snes.cpu->_registers.p.c, false, "The carry flags should not be set.");
cr_assert_eq(snes.cpu->_registers.p.v, false, "The overflow flags should be set.");
@@ -114,7 +114,7 @@ Test(ADC, memoryTwoBytes)
snes.cpu->_registers.a = 0x000F;
snes.wram->_data[0] = 0x01;
snes.wram->_data[1] = 0x04;
snes.cpu->ADC(0x0);
snes.cpu->ADC(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.a, 0x0410, "The accumulator's value should be 0x0410 but it was 0x%x.", snes.cpu->_registers.a);
cr_assert_eq(snes.cpu->_registers.p.c, false, "The carry flags should not be set.");
cr_assert_eq(snes.cpu->_registers.p.v, false, "The overflow flags should not be set.");

View File

@@ -15,7 +15,7 @@ Test(SBC, removingOne)
snes.cpu->_registers.p.c = true;
snes.cpu->_registers.a = 0x1;
snes.wram->_data[0] = 0x1;
snes.cpu->SBC(0x0);
snes.cpu->SBC(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.a, 0, "The accumulator's value should be 0x0 but it was 0x%x.", snes.cpu->_registers.a);
cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flags should be set.");
cr_assert_eq(snes.cpu->_registers.p.v, false, "The overflow flags should not be set.");
@@ -32,7 +32,7 @@ Test(SBC, legitOverflowWithCarry)
snes.cpu->_registers.p.c = true;
snes.wram->_data[0] = 0x03;
snes.wram->_data[1] = 0x20;
snes.cpu->SBC(0x0);
snes.cpu->SBC(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.a, 0xDFFE, "The accumulator's value should be 0xDFFE but it was 0x%x.", snes.cpu->_registers.a);
cr_assert_eq(snes.cpu->_registers.p.c, false, "The carry flags should not be set.");
cr_assert_eq(snes.cpu->_registers.p.v, false, "The overflow flags should not be set.");
@@ -49,7 +49,7 @@ Test(SBC, overflowWithCarry)
snes.cpu->_registers.p.c = true;
snes.wram->_data[0] = 0x03;
snes.wram->_data[1] = 0x20;
snes.cpu->SBC(0x0);
snes.cpu->SBC(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.a, 0xDFFE, "The accumulator's value should be 0xDFFE but it was 0x%x.", snes.cpu->_registers.a);
cr_assert_eq(snes.cpu->_registers.p.c, false, "The carry flags should not be set.");
cr_assert_eq(snes.cpu->_registers.p.v, false, "The overflow flags should be not set.");
@@ -65,7 +65,7 @@ Test(SBC, overflowEmulation)
snes.cpu->_registers.p.m = false;
snes.cpu->_registers.p.c = false;
snes.wram->_data[0] = 0x02;
snes.cpu->SBC(0x0);
snes.cpu->SBC(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.a, 0xFE, "The accumulator's value should be 0xFE but it was 0x%x.", snes.cpu->_registers.a);
cr_assert_eq(snes.cpu->_registers.p.c, false, "The carry flags should not be set.");
cr_assert_eq(snes.cpu->_registers.p.v, false, "The overflow flags should not be set.");
@@ -83,7 +83,7 @@ Test(SBC, overflowEmulation)
// snes.cpu->_registers.p.m = false;
// snes.wram->_data[0] = 0x03;
// snes.wram->_data[1] = 0x20;
// snes.cpu->SBC(0x0);
// snes.cpu->SBC(0x0, ComSquare::CPU::AddressingMode::Implied);
// cr_assert_eq(snes.cpu->_registers.a, 0x7998, "The accumulator's value should be 0x7998 but it was 0x%x.", snes.cpu->_registers.a);
// cr_assert_eq(snes.cpu->_registers.p.c, false, "The carry flags should not be set.");
// cr_assert_eq(snes.cpu->_registers.p.v, false, "The overflow flags should not be set.");

View File

@@ -17,7 +17,7 @@ Test(TAX, 16bitsTo16Bits)
snes.cpu->_registers.p.m = false;
snes.cpu->_registers.x = 0xABCD;
snes.cpu->_registers.a = 0xFEDC;
snes.cpu->TAX();
snes.cpu->TAX(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.x, 0xFEDC, "The flags should be 0xFEDC but it was %x", snes.cpu->_registers.x);
cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flag should be set.");
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set.");
@@ -31,7 +31,7 @@ Test(TAX, 16bitsTo8Bits)
snes.cpu->_registers.p.m = false;
snes.cpu->_registers.x = 0xFEDC;
snes.cpu->_registers.a = 0xAB00;
snes.cpu->TAX();
snes.cpu->TAX(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.x, 0xFE00, "The flags should be 0xFE00 but it was %x", snes.cpu->_registers.x);
cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set.");
cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flag should be set.");
@@ -45,7 +45,7 @@ Test(TAX, 8bitsTo16Bits)
snes.cpu->_registers.p.m = true;
snes.cpu->_registers.x = 0xFEDC;
snes.cpu->_registers.a = 0xAB;
snes.cpu->TAX();
snes.cpu->TAX(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.x, 0x00AB, "The flags should be 0x00AB but it was %x", snes.cpu->_registers.x);
cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set.");
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set.");
@@ -59,7 +59,7 @@ Test(TAX, 8bitsTo8Bits)
snes.cpu->_registers.p.m = true;
snes.cpu->_registers.x = 0xFE;
snes.cpu->_registers.a = 0xAB;
snes.cpu->TAX();
snes.cpu->TAX(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.x, 0xAB, "The flags should be 0xAB but it was %x", snes.cpu->_registers.x);
cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flag should be set.");
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should be not set.");
@@ -74,7 +74,7 @@ Test(TAY, 16bitsTo16Bits)
snes.cpu->_registers.p.m = false;
snes.cpu->_registers.y = 0xABCD;
snes.cpu->_registers.a = 0xFEDC;
snes.cpu->TAY();
snes.cpu->TAY(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.y, 0xFEDC, "The y register should be 0xFEDC but it was %x", snes.cpu->_registers.y);
cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flag should be set.");
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set.");
@@ -88,7 +88,7 @@ Test(TAY, 16bitsTo8Bits)
snes.cpu->_registers.p.m = false;
snes.cpu->_registers.y = 0xFEDC;
snes.cpu->_registers.a = 0xAB00;
snes.cpu->TAY();
snes.cpu->TAY(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.y, 0xFE00, "The y register should be 0xFE00 but it was %x", snes.cpu->_registers.y);
cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set.");
cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flag should be set.");
@@ -102,7 +102,7 @@ Test(TAY, 8bitsTo16Bits)
snes.cpu->_registers.p.m = true;
snes.cpu->_registers.y = 0xFEDC;
snes.cpu->_registers.a = 0xAB;
snes.cpu->TAY();
snes.cpu->TAY(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.y, 0x00AB, "The y register should be 0x00AB but it was %x", snes.cpu->_registers.y);
cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set.");
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set.");
@@ -116,7 +116,7 @@ Test(TAY, 8bitsTo8Bits)
snes.cpu->_registers.p.m = true;
snes.cpu->_registers.y = 0xFE;
snes.cpu->_registers.a = 0xAB;
snes.cpu->TAY();
snes.cpu->TAY(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.y, 0xAB, "The y register should be 0xAB but it was %x", snes.cpu->_registers.y);
cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flag should be set.");
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should be not set.");
@@ -128,7 +128,7 @@ Test(TXS, 16bitsIndex)
snes.cpu->_isEmulationMode = false;
snes.cpu->_registers.p.x_b = false;
snes.cpu->_registers.x = 0xABCD;
snes.cpu->TXS();
snes.cpu->TXS(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.s, 0xABCD, "The stack pointer should be 0xABCD but it was %x", snes.cpu->_registers.s);
cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flag should be set.");
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should be not set.");
@@ -140,7 +140,7 @@ Test(TXS, 8bitsIndex)
snes.cpu->_isEmulationMode = false;
snes.cpu->_registers.p.x_b = true;
snes.cpu->_registers.x = 0xABCD;
snes.cpu->TXS();
snes.cpu->TXS(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.s, 0x00CD, "The stack pointer should be 0x00CD but it was %x", snes.cpu->_registers.s);
cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flag should be set.");
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should be not set.");

View File

@@ -42,7 +42,7 @@ Test(AddrMode, ImmediateBankChange)
snes.cpu->_registers.pac = 0x00FFFF;
snes.cpu->_registers.p.m = true;
cr_assert_eq(snes.cpu->_getImmediateAddrForA(), 0x00FFFF);
cr_assert_eq(snes.cpu->_registers.pac, 0x010000);
cr_assert_eq(snes.cpu->_registers.pac, 0x000000);
}
Test(AddrMode, Direct)

View File

@@ -15,7 +15,7 @@ Test(AND, emulation)
snes.wram->_data[0] = 0x00;
snes.cpu->_registers.a = 0xFF;
snes.cpu->_isEmulationMode = true;
snes.cpu->AND(0x0);
snes.cpu->AND(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.a, 0x00, "The flags should be 0x00 but it was %x", snes.cpu->_registers.a);
cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flag should be set.");
cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set.");
@@ -29,7 +29,7 @@ Test(AND, nativeNegative)
snes.cpu->_registers.p.m = false;
snes.cpu->_registers.a = 0xFF00;
snes.cpu->_isEmulationMode = false;
snes.cpu->AND(0x0);
snes.cpu->AND(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.a, 0xF000, "The flags should be 0xF000 but it was %x", snes.cpu->_registers.a);
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set.");
cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flag should be set.");
@@ -42,7 +42,7 @@ Test(AND, emulationTest)
snes.wram->_data[0] = 0b00110011;
snes.cpu->_registers.a = 0b00110111;
snes.cpu->_isEmulationMode = true;
snes.cpu->AND(0x0);
snes.cpu->AND(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.a, 0b00110011, "The flags should be 0b00110011 but it was %x", snes.cpu->_registers.a);
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set.");
cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set.");

View File

@@ -15,7 +15,8 @@ using namespace ComSquare;
Test(SEP, setall)
{
Init()
snes.cpu->SEP(0xFF);
snes.wram->_data[0] = 0xFF;
snes.cpu->SEP(0x00, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.p.flags;
cr_assert_eq(data, 0xFF, "The flag should be 0xFF but it was %x", data);
}
@@ -24,7 +25,8 @@ Test(SEP, setsome)
{
Init()
snes.cpu->_registers.p.flags = 0b01000000;
snes.cpu->SEP(0b10110101);
snes.wram->_data[0] = 0b10110101;
snes.cpu->SEP(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.p.flags;
cr_assert_eq(data, 0b11110101, "The flag should be 245 but it was %i", data);
}
@@ -33,7 +35,8 @@ Test(REP, resetall)
{
Init()
snes.cpu->_isEmulationMode = false;
snes.cpu->REP(0xFF);
snes.wram->_data[0] = 0xFF;
snes.cpu->REP(0x00, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.p.flags;
cr_assert_eq(data, 0x00, "The flag should be 0x00 but it was %x", data);
}
@@ -43,7 +46,8 @@ Test(REP, resetsome)
Init()
snes.cpu->_isEmulationMode = false;
snes.cpu->_registers.p.flags = 0b01000000;
snes.cpu->REP(0b01000000);
snes.wram->_data[0] = 0b01000000;
snes.cpu->REP(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.p.flags;
cr_assert_eq(data, 0x0, "The flag should be 0 but it was %x", data);
}
@@ -52,7 +56,8 @@ Test(REP, resetallEmulation)
{
Init()
snes.cpu->_isEmulationMode = true;
snes.cpu->REP(0xFF);
snes.wram->_data[0] = 0xFF;
snes.cpu->REP(0x00, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.p.flags;
cr_assert_eq(data, 0b00110000, "The flag should be 0b00110000 but it was %x", data);
}
@@ -62,7 +67,8 @@ Test(REP, resetsomeEmulation)
Init()
snes.cpu->_isEmulationMode = true;
snes.cpu->_registers.p.flags = 0b01000101;
snes.cpu->REP(0b01000001);
snes.wram->_data[0] = 0b01000001;
snes.cpu->REP(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.p.flags;
cr_assert_eq(data, 0b00110100, "The flag should be 0b00110100 but it was %x", data);
}
@@ -72,7 +78,7 @@ Test(JSR, jump)
Init()
snes.cpu->_registers.pc = 0xABCD;
snes.cpu->_registers.s = 0x0123;
snes.cpu->JSR(0xABFF);
snes.cpu->JSR(0xABFF, ComSquare::CPU::AddressingMode::Implied);
auto pc = snes.cpu->_registers.pc;
cr_assert_eq(pc, 0xABFF, "The PC should be 0xABFF but it was %x", pc);
cr_assert_eq(snes.cpu->_registers.s, 0x0121, "The stack pointer should be 0x0121 but it was %x", snes.cpu->_registers.s);
@@ -86,7 +92,7 @@ Test(JSL, jump)
snes.cpu->_registers.pbr = 0xFF;
snes.cpu->_registers.pc = 0xABCD;
snes.cpu->_registers.s = 0x0123;
snes.cpu->JSL(0xCDABFF);
snes.cpu->JSL(0xCDABFF, ComSquare::CPU::AddressingMode::Implied);
auto pac = snes.cpu->_registers.pac;
cr_assert_eq(pac, 0xCDABFF, "The PC should be 0xCDABFF but it was %x", pac);
cr_assert_eq(snes.cpu->_registers.s, 0x0120, "The stack pointer should be 0x0120 but it was %x", snes.cpu->_registers.s);
@@ -99,7 +105,7 @@ Test(PHA, basic)
Init()
snes.cpu->_registers.a = 0xABCD;
snes.cpu->_registers.s = 0x02;
snes.cpu->PHA();
snes.cpu->PHA(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.wram->_data[1], 0xCD, "The second value pushed to the stack should be 0xCD but it was %x", snes.wram->_data[1]);
cr_assert_eq(snes.wram->_data[2], 0xAB, "The first value pushed to the stack should be 0xAB but it was %x", snes.wram->_data[2]);
cr_assert_eq(snes.cpu->_registers.s, 0x0, "The Stack pointer should be equal to 0x0 but it was %x", snes.cpu->_registers.s);
@@ -110,7 +116,7 @@ Test(PHB, basic)
Init()
snes.cpu->_registers.dbr = 0xFF;
snes.cpu->_registers.s = 0x02;
snes.cpu->PHB();
snes.cpu->PHB(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.wram->_data[2], 0xFF, "The first value pushed to the stack should be 0xFF but it was %x", snes.wram->_data[2]);
cr_assert_eq(snes.cpu->_registers.s, 0x1, "The Stack pointer should be equal to 0x1 but it was %x", snes.cpu->_registers.s);
}
@@ -120,7 +126,7 @@ Test(PHD, basic)
Init()
snes.cpu->_registers.d = 0xABCD;
snes.cpu->_registers.s = 0x02;
snes.cpu->PHD();
snes.cpu->PHD(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.wram->_data[1], 0xCD, "The second value pushed to the stack should be 0xCD but it was %x", snes.wram->_data[1]);
cr_assert_eq(snes.wram->_data[2], 0xAB, "The first value pushed to the stack should be 0xAB but it was %x", snes.wram->_data[2]);
cr_assert_eq(snes.cpu->_registers.s, 0x0, "The Stack pointer should be equal to 0x0 but it was %x", snes.cpu->_registers.s);
@@ -131,7 +137,7 @@ Test(PHK, basic)
Init()
snes.cpu->_registers.pbr = 0xFF;
snes.cpu->_registers.s = 0x02;
snes.cpu->PHK();
snes.cpu->PHK(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.wram->_data[2], 0xFF, "The first value pushed to the stack should be 0xFF but it was %x", snes.wram->_data[2]);
cr_assert_eq(snes.cpu->_registers.s, 0x1, "The Stack pointer should be equal to 0x1 but it was %x", snes.cpu->_registers.s);
}
@@ -141,7 +147,7 @@ Test(PHP, basic)
Init()
snes.cpu->_registers.p.flags = 0xFF;
snes.cpu->_registers.s = 0x02;
snes.cpu->PHP();
snes.cpu->PHP(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.wram->_data[2], 0xFF, "The first value pushed to the stack should be 0xFF but it was %x", snes.wram->_data[2]);
cr_assert_eq(snes.cpu->_registers.s, 0x1, "The Stack pointer should be equal to 0x1 but it was %x", snes.cpu->_registers.s);
}
@@ -151,7 +157,7 @@ Test(PHX, basic)
Init()
snes.cpu->_registers.x = 0xABCD;
snes.cpu->_registers.s = 0x02;
snes.cpu->PHX();
snes.cpu->PHX(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.wram->_data[1], 0xCD, "The second value pushed to the stack should be 0xCD but it was %x", snes.wram->_data[1]);
cr_assert_eq(snes.wram->_data[2], 0xAB, "The first value pushed to the stack should be 0xAB but it was %x", snes.wram->_data[2]);
cr_assert_eq(snes.cpu->_registers.s, 0x0, "The Stack pointer should be equal to 0x0 but it was %x", snes.cpu->_registers.s);
@@ -162,7 +168,7 @@ Test(PHY, basic)
Init()
snes.cpu->_registers.y = 0xABCD;
snes.cpu->_registers.s = 0x02;
snes.cpu->PHY();
snes.cpu->PHY(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.wram->_data[1], 0xCD, "The second value pushed to the stack should be 0xCD but it was %x", snes.wram->_data[1]);
cr_assert_eq(snes.wram->_data[2], 0xAB, "The first value pushed to the stack should be 0xAB but it was %x", snes.wram->_data[2]);
cr_assert_eq(snes.cpu->_registers.s, 0x0, "The Stack pointer should be equal to 0x0 but it was %x", snes.cpu->_registers.s);
@@ -174,7 +180,8 @@ Test(PLA, basic)
snes.wram->_data[1] = 0xCD;
snes.wram->_data[2] = 0x7B;
snes.cpu->_registers.s = 0x00;
snes.cpu->PLA();
snes.cpu->_registers.p.m = false;
snes.cpu->PLA(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.a;
cr_assert_eq(data, 0x7BCD, "The accumulator should be 0x7BCD but it was %x", data);
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set.", snes.cpu->_registers.p.z);
@@ -188,7 +195,8 @@ Test(PLA, zero)
snes.wram->_data[1] = 0x00;
snes.wram->_data[2] = 0x00;
snes.cpu->_registers.s = 0x00;
snes.cpu->PLA();
snes.cpu->_registers.p.m = false;
snes.cpu->PLA(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.a;
cr_assert_eq(data, 0x0000, "The accumulator should be 0x0000 but it was %x", data);
cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flag should be set.", snes.cpu->_registers.p.z);
@@ -202,7 +210,8 @@ Test(PLA, negative)
snes.wram->_data[1] = 0x00;
snes.wram->_data[2] = 0xA0;
snes.cpu->_registers.s = 0x00;
snes.cpu->PLA();
snes.cpu->_registers.p.m = false;
snes.cpu->PLA(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.a;
cr_assert_eq(data, 0xA000, "The accumulator should be 0xA000 but it was %x", data);
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag not should be set.", snes.cpu->_registers.p.z);
@@ -216,7 +225,8 @@ Test(PLX, basic)
snes.wram->_data[1] = 0xCD;
snes.wram->_data[2] = 0x7B;
snes.cpu->_registers.s = 0x00;
snes.cpu->PLX();
snes.cpu->_registers.p.x_b = false;
snes.cpu->PLX(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.x;
cr_assert_eq(data, 0x7BCD, "The X register should be 0x7BCD but it was %x", data);
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set.", snes.cpu->_registers.p.z);
@@ -230,7 +240,8 @@ Test(PLX, zero)
snes.wram->_data[1] = 0x00;
snes.wram->_data[2] = 0x00;
snes.cpu->_registers.s = 0x00;
snes.cpu->PLX();
snes.cpu->_registers.p.x_b = false;
snes.cpu->PLX(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.x;
cr_assert_eq(data, 0x0000, "The x register should be 0x0000 but it was %x", data);
cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flag should be set.", snes.cpu->_registers.p.z);
@@ -244,7 +255,8 @@ Test(PLX, negative)
snes.wram->_data[1] = 0x00;
snes.wram->_data[2] = 0xA0;
snes.cpu->_registers.s = 0x00;
snes.cpu->PLX();
snes.cpu->_registers.p.x_b = false;
snes.cpu->PLX(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.x;
cr_assert_eq(data, 0xA000, "The x register should be 0xA000 but it was %x", data);
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag not should be set.", snes.cpu->_registers.p.z);
@@ -258,7 +270,8 @@ Test(PLY, basic)
snes.wram->_data[1] = 0xCD;
snes.wram->_data[2] = 0x7B;
snes.cpu->_registers.s = 0x00;
snes.cpu->PLY();
snes.cpu->_registers.p.x_b = false;
snes.cpu->PLY(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.y;
cr_assert_eq(data, 0x7BCD, "The Y register should be 0x7BCD but it was %x", data);
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set.", snes.cpu->_registers.p.z);
@@ -272,7 +285,8 @@ Test(PLY, zero)
snes.wram->_data[1] = 0x00;
snes.wram->_data[2] = 0x00;
snes.cpu->_registers.s = 0x00;
snes.cpu->PLY();
snes.cpu->_registers.p.x_b = false;
snes.cpu->PLY(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.y;
cr_assert_eq(data, 0x0000, "The y register should be 0x0000 but it was %x", data);
cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flag should be set.", snes.cpu->_registers.p.z);
@@ -286,7 +300,8 @@ Test(PLY, negative)
snes.wram->_data[1] = 0x00;
snes.wram->_data[2] = 0xA0;
snes.cpu->_registers.s = 0x00;
snes.cpu->PLY();
snes.cpu->_registers.p.x_b = false;
snes.cpu->PLY(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.y;
cr_assert_eq(data, 0xA000, "The y register should be 0xA000 but it was %x", data);
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag not should be set.", snes.cpu->_registers.p.z);
@@ -300,7 +315,7 @@ Test(PLD, basic)
snes.wram->_data[1] = 0xCD;
snes.wram->_data[2] = 0x7B;
snes.cpu->_registers.s = 0x00;
snes.cpu->PLD();
snes.cpu->PLD(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.d;
cr_assert_eq(data, 0x7BCD, "The D register should be 0x7BCD but it was %x", data);
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set.", snes.cpu->_registers.p.z);
@@ -314,7 +329,7 @@ Test(PLD, zero)
snes.wram->_data[1] = 0x00;
snes.wram->_data[2] = 0x00;
snes.cpu->_registers.s = 0x00;
snes.cpu->PLD();
snes.cpu->PLD(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.d;
cr_assert_eq(data, 0x0000, "The d register should be 0x0000 but it was %x", data);
cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flag should be set.", snes.cpu->_registers.p.z);
@@ -328,7 +343,7 @@ Test(PLD, negative)
snes.wram->_data[1] = 0x00;
snes.wram->_data[2] = 0xA0;
snes.cpu->_registers.s = 0x00;
snes.cpu->PLD();
snes.cpu->PLD(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.d;
cr_assert_eq(data, 0xA000, "The D register should be 0xA000 but it was %x", data);
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag not should be set.", snes.cpu->_registers.p.z);
@@ -341,7 +356,7 @@ Test(PLB, basic)
Init()
snes.wram->_data[1] = 0x7D;
snes.cpu->_registers.s = 0x00;
snes.cpu->PLB();
snes.cpu->PLB(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.dbr;
cr_assert_eq(data, 0x7D, "The DBR should be 0x7D but it was %x", data);
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set.", snes.cpu->_registers.p.z);
@@ -354,7 +369,7 @@ Test(PLB, zero)
Init()
snes.wram->_data[1] = 0x00;
snes.cpu->_registers.s = 0x00;
snes.cpu->PLB();
snes.cpu->PLB(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.dbr;
cr_assert_eq(data, 0x00, "The dbr should be 0x00 but it was %x", data);
cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flag should be set.", snes.cpu->_registers.p.z);
@@ -367,7 +382,7 @@ Test(PLB, negative)
Init()
snes.wram->_data[1] = 0xA0;
snes.cpu->_registers.s = 0x00;
snes.cpu->PLB();
snes.cpu->PLB(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.dbr;
cr_assert_eq(data, 0xA0, "The D register should be 0xA0 but it was %x", data);
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag not should be set.", snes.cpu->_registers.p.z);
@@ -381,7 +396,7 @@ Test(PLP, basic)
snes.wram->_data[1] = 0x7D;
snes.cpu->_registers.s = 0x00;
snes.cpu->_isEmulationMode = false;
snes.cpu->PLP();
snes.cpu->PLP(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.p.flags;
cr_assert_eq(data, 0x7D, "The flags should be 0x7D but it was %x", data);
cr_assert_eq(snes.cpu->_registers.s, 0x1, "The Stack pointer should be equal to 0x1 but it was %x", snes.cpu->_registers.s);
@@ -393,7 +408,7 @@ Test(PLP, emulation)
snes.wram->_data[1] = 0x00;
snes.cpu->_registers.s = 0x00;
snes.cpu->_isEmulationMode = true;
snes.cpu->PLP();
snes.cpu->PLP(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.p.flags;
cr_assert_eq(data, 0b00110000, "The flags should be 0b00110000 but it was %x", data);
cr_assert_eq(snes.cpu->_registers.s, 0x1, "The Stack pointer should be equal to 0x1 but it was %x", snes.cpu->_registers.s);
@@ -403,7 +418,7 @@ Test(CLC, clear)
{
Init()
snes.cpu->_registers.p.flags = 0xFF;
snes.cpu->CLC();
snes.cpu->CLC(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.p.c, false, "The carry flag should not be set");
}
@@ -411,7 +426,7 @@ Test(CLI, clear)
{
Init()
snes.cpu->_registers.p.flags = 0xFF;
snes.cpu->CLI();
snes.cpu->CLI(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.p.i, false, "The interrupt flag should not be set");
}
@@ -419,7 +434,7 @@ Test(CLD, clear)
{
Init()
snes.cpu->_registers.p.flags = 0xFF;
snes.cpu->CLD();
snes.cpu->CLD(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.p.d, false, "The decimal flag should not be set");
}
@@ -427,7 +442,7 @@ Test(CLV, clear)
{
Init()
snes.cpu->_registers.p.flags = 0xFF;
snes.cpu->CLV();
snes.cpu->CLV(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.p.v, false, "The overflow flag should not be set");
}
@@ -435,7 +450,7 @@ Test(SEC, set)
{
Init()
snes.cpu->_registers.p.flags = 0x00;
snes.cpu->SEC();
snes.cpu->SEC(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flag should be set");
}
@@ -443,7 +458,7 @@ Test(SEI, set)
{
Init()
snes.cpu->_registers.p.flags = 0x00;
snes.cpu->SEI();
snes.cpu->SEI(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.p.i, true, "The interrupt disabled flag should be set");
}
@@ -451,7 +466,7 @@ Test(SED, set)
{
Init()
snes.cpu->_registers.p.flags = 0x00;
snes.cpu->SED();
snes.cpu->SED(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.p.d, true, "The decimal flag should be set");
}
@@ -463,7 +478,7 @@ Test(XCE, enableEmulation)
snes.cpu->_registers.p.c = true;
snes.cpu->_registers.xh = 0xFF;
snes.cpu->_registers.yh = 0xFF;
snes.cpu->XCE();
snes.cpu->XCE(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_isEmulationMode, true, "The e flag should be set");
cr_assert_eq(snes.cpu->_registers.p.c, false, "The carry flag should not be set");
cr_assert_eq(snes.cpu->_registers.p.m, false, "The memory width flag should be untouched (unset)");
@@ -479,7 +494,7 @@ Test(XCE, enableNative)
snes.cpu->_registers.p.flags = 0;
snes.cpu->_registers.xh = 0xFF;
snes.cpu->_registers.yh = 0xFF;
snes.cpu->XCE();
snes.cpu->XCE(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_isEmulationMode, false, "The e flag should be not set");
cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flag should be set");
cr_assert_eq(snes.cpu->_registers.p.m, true, "The memory width flag should be set");
@@ -495,7 +510,7 @@ Test(INX, basic)
snes.cpu->_registers.p.flags = 0;
snes.cpu->_registers.p.x_b = false;
snes.cpu->_registers.x = 0xFF;
snes.cpu->INX();
snes.cpu->INX(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.x, 0x0100, "The x register should be equal to 0x0100 but it was 0x%x.", snes.cpu->_registers.x);
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set");
cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set");
@@ -508,7 +523,7 @@ Test(INX, 8bits)
snes.cpu->_registers.p.flags = 0;
snes.cpu->_registers.p.x_b = true;
snes.cpu->_registers.x = 0xFF;
snes.cpu->INX();
snes.cpu->INX(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.x, 0x00, "The x register should be equal to 0x00 but it was 0x%x.", snes.cpu->_registers.x);
cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flag should be set");
cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set");
@@ -521,7 +536,7 @@ Test(INY, basic)
snes.cpu->_registers.p.flags = 0;
snes.cpu->_registers.p.x_b = false;
snes.cpu->_registers.y = 0xFF;
snes.cpu->INY();
snes.cpu->INY(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.y, 0x0100, "The y register should be equal to 0x0100 but it was 0x%x.", snes.cpu->_registers.y);
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set");
cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set");
@@ -534,7 +549,7 @@ Test(INY, 8bits)
snes.cpu->_registers.p.flags = 0;
snes.cpu->_registers.p.x_b = true;
snes.cpu->_registers.y = 0xFF;
snes.cpu->INY();
snes.cpu->INY(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.y, 0x00, "The y register should be equal to 0x00 but it was 0x%x.", snes.cpu->_registers.y);
cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flag should be set");
cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set");
@@ -547,7 +562,7 @@ Test(CPX, basic)
snes.cpu->_registers.p.flags = 0;
snes.cpu->_registers.x = 0xFF;
snes.wram->_data[0] = 0xFF;
snes.cpu->CPX(0x0);
snes.cpu->CPX(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flag should be set");
cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set");
cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flag should be set");
@@ -560,7 +575,7 @@ Test(CPX, negative)
snes.cpu->_registers.p.flags = 0;
snes.cpu->_registers.x = 0x80;
snes.wram->_data[0] = 0xFF;
snes.cpu->CPX(0x0);
snes.cpu->CPX(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set");
cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flag should be set");
cr_assert_eq(snes.cpu->_registers.p.c, false, "The carry flag should not be set");
@@ -574,7 +589,7 @@ Test(CPX, 16bits)
snes.cpu->_registers.x = 0x8888;
snes.wram->_data[0] = 0x88;
snes.wram->_data[1] = 0x98;
snes.cpu->CPX(0x0);
snes.cpu->CPX(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set");
cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flag should be set");
cr_assert_eq(snes.cpu->_registers.p.c, false, "The carry flag should not be set");
@@ -587,7 +602,7 @@ Test(CPY, basic)
snes.cpu->_registers.p.flags = 0;
snes.cpu->_registers.y = 0xFF;
snes.wram->_data[0] = 0xFF;
snes.cpu->CPY(0x0);
snes.cpu->CPY(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flag should be set");
cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set");
cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flag should be set");
@@ -600,7 +615,7 @@ Test(CPY, negative)
snes.cpu->_registers.p.flags = 0;
snes.cpu->_registers.y = 0x80;
snes.wram->_data[0] = 0xFF;
snes.cpu->CPY(0x0);
snes.cpu->CPY(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set");
cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flag should be set");
cr_assert_eq(snes.cpu->_registers.p.c, false, "The carry flag should not be set");
@@ -612,7 +627,7 @@ Test(BCC, basic)
snes.cpu->_registers.p.c = false;
snes.cpu->_registers.pc = 0x80;
snes.wram->_data[0] = 0x50;
snes.cpu->BCC(0x0);
snes.cpu->BCC(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0xD0, "The program counter should be equal to 0xD0 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -622,7 +637,7 @@ Test(BCC, negativeJump)
snes.cpu->_registers.p.c = false;
snes.cpu->_registers.pc = 0x80;
snes.wram->_data[0] = 0xF0;
snes.cpu->BCC(0x0);
snes.cpu->BCC(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0x70, "The program counter should be equal to 0x70 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -632,7 +647,7 @@ Test(BCC, noJump)
snes.cpu->_registers.p.c = true;
snes.cpu->_registers.pc = 0x80;
snes.wram->_data[0] = 0x90;
snes.cpu->BCC(0x0);
snes.cpu->BCC(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0x80, "The program counter should be equal to 0x80 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -642,7 +657,7 @@ Test(BCS, basic)
snes.cpu->_registers.p.c = true;
snes.cpu->_registers.pc = 0x80;
snes.wram->_data[0] = 0x50;
snes.cpu->BCS(0x0);
snes.cpu->BCS(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0xD0, "The program counter should be equal to 0xD0 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -652,7 +667,7 @@ Test(BCS, negativeJump)
snes.cpu->_registers.p.c = true;
snes.cpu->_registers.pc = 0x80;
snes.wram->_data[0] = 0xF0;
snes.cpu->BCS(0x0);
snes.cpu->BCS(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0x70, "The program counter should be equal to 0x70 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -662,7 +677,7 @@ Test(BCS, noJump)
snes.cpu->_registers.p.c = false;
snes.cpu->_registers.pc = 0x80;
snes.wram->_data[0] = 0x90;
snes.cpu->BCS(0x0);
snes.cpu->BCS(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0x80, "The program counter should be equal to 0x80 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -672,7 +687,7 @@ Test(BEQ, basic)
snes.cpu->_registers.p.z = true;
snes.cpu->_registers.pc = 0x80;
snes.wram->_data[0] = 0x50;
snes.cpu->BEQ(0x0);
snes.cpu->BEQ(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0xD0, "The program counter should be equal to 0xD0 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -682,7 +697,7 @@ Test(BEQ, negativeJump)
snes.cpu->_registers.p.z = true;
snes.cpu->_registers.pc = 0x80;
snes.wram->_data[0] = 0xF0;
snes.cpu->BEQ(0x0);
snes.cpu->BEQ(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0x70, "The program counter should be equal to 0x70 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -692,7 +707,7 @@ Test(BEQ, noJump)
snes.cpu->_registers.p.z = false;
snes.cpu->_registers.pc = 0x80;
snes.wram->_data[0] = 0x90;
snes.cpu->BEQ(0x0);
snes.cpu->BEQ(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0x80, "The program counter should be equal to 0x80 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -702,7 +717,7 @@ Test(BNE, basic)
snes.cpu->_registers.p.z = false;
snes.cpu->_registers.pc = 0x80;
snes.wram->_data[0] = 0x50;
snes.cpu->BNE(0x0);
snes.cpu->BNE(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0xD0, "The program counter should be equal to 0xD0 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -712,7 +727,7 @@ Test(BNE, negativeJump)
snes.cpu->_registers.p.z = false;
snes.cpu->_registers.pc = 0x80;
snes.wram->_data[0] = 0xF0;
snes.cpu->BNE(0x0);
snes.cpu->BNE(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0x70, "The program counter should be equal to 0x70 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -722,7 +737,7 @@ Test(BNE, noJump)
snes.cpu->_registers.p.z = true;
snes.cpu->_registers.pc = 0x80;
snes.wram->_data[0] = 0x90;
snes.cpu->BNE(0x0);
snes.cpu->BNE(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0x80, "The program counter should be equal to 0x80 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -732,7 +747,7 @@ Test(BMI, basic)
snes.cpu->_registers.p.n = true;
snes.cpu->_registers.pc = 0x80;
snes.wram->_data[0] = 0x50;
snes.cpu->BMI(0x0);
snes.cpu->BMI(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0xD0, "The program counter should be equal to 0xD0 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -742,7 +757,7 @@ Test(BMI, negativeJump)
snes.cpu->_registers.p.n = true;
snes.cpu->_registers.pc = 0x80;
snes.wram->_data[0] = 0xF0;
snes.cpu->BMI(0x0);
snes.cpu->BMI(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0x70, "The program counter should be equal to 0x70 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -752,7 +767,7 @@ Test(BMI, noJump)
snes.cpu->_registers.p.n = false;
snes.cpu->_registers.pc = 0x80;
snes.wram->_data[0] = 0x90;
snes.cpu->BMI(0x0);
snes.cpu->BMI(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0x80, "The program counter should be equal to 0x80 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -762,7 +777,7 @@ Test(BPL, basic)
snes.cpu->_registers.p.n = false;
snes.cpu->_registers.pc = 0x80;
snes.wram->_data[0] = 0x50;
snes.cpu->BPL(0x0);
snes.cpu->BPL(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0xD0, "The program counter should be equal to 0xD0 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -772,7 +787,7 @@ Test(BPL, negativeJump)
snes.cpu->_registers.p.n = false;
snes.cpu->_registers.pc = 0x80;
snes.wram->_data[0] = 0xF0;
snes.cpu->BPL(0x0);
snes.cpu->BPL(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0x70, "The program counter should be equal to 0x70 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -782,7 +797,7 @@ Test(BPL, noJump)
snes.cpu->_registers.p.n = true;
snes.cpu->_registers.pc = 0x80;
snes.wram->_data[0] = 0x90;
snes.cpu->BPL(0x0);
snes.cpu->BPL(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0x80, "The program counter should be equal to 0x80 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -791,7 +806,7 @@ Test(BRA, basic)
Init()
snes.cpu->_registers.pc = 0x80;
snes.wram->_data[0] = 0x50;
snes.cpu->BRA(0x0);
snes.cpu->BRA(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0xD0, "The program counter should be equal to 0xD0 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -800,7 +815,7 @@ Test(BRA, negativeJump)
Init()
snes.cpu->_registers.pc = 0x80;
snes.wram->_data[0] = 0xF0;
snes.cpu->BRA(0x0);
snes.cpu->BRA(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0x70, "The program counter should be equal to 0x70 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -810,7 +825,7 @@ Test(BRL, basic)
snes.cpu->_registers.pc = 0x8080;
snes.wram->_data[0] = 0x00;
snes.wram->_data[1] = 0x10;
snes.cpu->BRL(0x0);
snes.cpu->BRL(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0x9080, "The program counter should be equal to 0x9080 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -820,7 +835,7 @@ Test(BRL, negativeJump)
snes.cpu->_registers.pc = 0x8080;
snes.wram->_data[0] = 0x00;
snes.wram->_data[1] = 0xF0;
snes.cpu->BRL(0x0);
snes.cpu->BRL(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0x7080, "The program counter should be equal to 0x7080 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -830,7 +845,7 @@ Test(BVC, basic)
snes.cpu->_registers.p.v = false;
snes.cpu->_registers.pc = 0x80;
snes.wram->_data[0] = 0x50;
snes.cpu->BVC(0x0);
snes.cpu->BVC(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0xD0, "The program counter should be equal to 0xD0 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -840,7 +855,7 @@ Test(BVC, negativeJump)
snes.cpu->_registers.p.v = false;
snes.cpu->_registers.pc = 0x80;
snes.wram->_data[0] = 0xF0;
snes.cpu->BVC(0x0);
snes.cpu->BVC(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0x70, "The program counter should be equal to 0x70 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -850,7 +865,7 @@ Test(BVC, noJump)
snes.cpu->_registers.p.v = true;
snes.cpu->_registers.pc = 0x80;
snes.wram->_data[0] = 0x90;
snes.cpu->BVC(0x0);
snes.cpu->BVC(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0x80, "The program counter should be equal to 0x80 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -861,7 +876,7 @@ Test(BVS, basic)
snes.cpu->_registers.p.v = true;
snes.cpu->_registers.pc = 0x80;
snes.wram->_data[0] = 0x50;
snes.cpu->BVS(0x0);
snes.cpu->BVS(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0xD0, "The program counter should be equal to 0xD0 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -871,7 +886,7 @@ Test(BVS, negativeJump)
snes.cpu->_registers.p.v = true;
snes.cpu->_registers.pc = 0x80;
snes.wram->_data[0] = 0xF0;
snes.cpu->BVS(0x0);
snes.cpu->BVS(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0x70, "The program counter should be equal to 0x70 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -881,7 +896,7 @@ Test(BVS, noJump)
snes.cpu->_registers.p.v = false;
snes.cpu->_registers.pc = 0x80;
snes.wram->_data[0] = 0x90;
snes.cpu->BVS(0x0);
snes.cpu->BVS(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0x80, "The program counter should be equal to 0x80 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -889,7 +904,7 @@ Test(JMP, simpleJump)
{
Init()
snes.cpu->_registers.pc = 0x8000;
snes.cpu->JMP(0x1000);
snes.cpu->JMP(0x1000, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0x1000, "The program counter should be equal to 0x9000 but it was 0x%x.", snes.cpu->_registers.pc);
}
@@ -897,6 +912,6 @@ Test(JML, simpleJump)
{
Init()
snes.cpu->_registers.pc = 0x8000;
snes.cpu->JML(0x10AB00);
snes.cpu->JML(0x10AB00, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pac, 0x10AB00, "The program counter should be equal to 0x10AB00 but it was 0x%x.", snes.cpu->_registers.pac);
}

View File

@@ -18,7 +18,7 @@ Test(CPU_emulated, BRK)
snes.cpu->_registers.p.flags = 0xF1;
snes.cpu->_registers.pc = 0x156u;
snes.cpu->_registers.pbr = 0x15;
snes.cpu->BRK();
snes.cpu->BRK(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0x123u, "The program counter should be 0x123u but it was 0x%X", snes.cpu->_registers.pc);
cr_assert_eq(snes.cpu->_registers.pbr, 0x15, "The PBR should be 0x15 but it was 0x%X", snes.cpu->_registers.pbr);
cr_assert_eq(snes.cpu->_registers.p.d, false, "The decimal flag should not be set.");
@@ -38,7 +38,7 @@ Test(CPU_native, BRK)
snes.cpu->_registers.p.flags = 0xF1;
snes.cpu->_registers.pc = 0x156u;
snes.cpu->_registers.pbr = 0x15;
snes.cpu->BRK();
snes.cpu->BRK(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0x123u, "The program counter should be 0x123u but it was 0x%X", snes.cpu->_registers.pc);
cr_assert_eq(snes.cpu->_registers.pbr, 0x0, "The PBR should be 0x0 but it was 0x%X", snes.cpu->_registers.pbr);
cr_assert_eq(snes.cpu->_registers.p.d, false, "The decimal flag should not be set.");
@@ -59,7 +59,7 @@ Test(CPU_emulated, COP)
snes.cpu->_registers.p.flags = 0x0F;
snes.cpu->_registers.pc = 0x156u;
snes.cpu->_registers.pbr = 0x15;
snes.cpu->COP();
snes.cpu->COP(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0x123u, "The program counter should be 0x123u but it was 0x%X", snes.cpu->_registers.pc);
cr_assert_eq(snes.cpu->_registers.pbr, 0x15, "The PBR should be 0x15 but it was 0x%X", snes.cpu->_registers.pbr);
cr_assert_eq(snes.cpu->_registers.p.d, false, "The decimal flag should not be set.");
@@ -79,7 +79,7 @@ Test(CPU_native, COP)
snes.cpu->_registers.p.flags = 0xF1;
snes.cpu->_registers.pc = 0x156u;
snes.cpu->_registers.pbr = 0x15;
snes.cpu->COP();
snes.cpu->COP(0x0, ComSquare::CPU::AddressingMode::Implied);
cr_assert_eq(snes.cpu->_registers.pc, 0x123u, "The program counter should be 0x123u but it was 0x%X", snes.cpu->_registers.pc);
cr_assert_eq(snes.cpu->_registers.pbr, 0x0, "The PBR should be 0x0 but it was 0x%X", snes.cpu->_registers.pbr);
cr_assert_eq(snes.cpu->_registers.p.d, false, "The decimal flag should not be set.");

View File

@@ -14,7 +14,7 @@ Test(STA, 8bits)
Init()
snes.cpu->_registers.p.m = true;
snes.cpu->_registers.a = 0x11;
snes.cpu->STA(0x0);
snes.cpu->STA(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.wram->_data[0];
cr_assert_eq(data, 0x11, "The stored value should be 0x11 but it was 0x%x.", data);
}
@@ -24,7 +24,7 @@ Test(STA, 16bits)
Init()
snes.cpu->_registers.p.m = false;
snes.cpu->_registers.a = 0x11AB;
snes.cpu->STA(0x0);
snes.cpu->STA(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.wram->_data[0] + (snes.wram->_data[1] << 8u);
cr_assert_eq(data, 0x11AB, "The stored value should be 0x11AB but it was 0x%x.", data);
}
@@ -34,7 +34,7 @@ Test(STX, 8bits)
Init()
snes.cpu->_registers.p.x_b = true;
snes.cpu->_registers.x = 0x11;
snes.cpu->STX(0x0);
snes.cpu->STX(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.wram->_data[0];
cr_assert_eq(data, 0x11, "The stored value should be 0x11 but it was 0x%x.", data);
}
@@ -44,7 +44,7 @@ Test(STX, 16bits)
Init()
snes.cpu->_registers.p.x_b = false;
snes.cpu->_registers.x = 0x11AB;
snes.cpu->STX(0x0);
snes.cpu->STX(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.wram->_data[0] + (snes.wram->_data[1] << 8u);
cr_assert_eq(data, 0x11AB, "The stored value should be 0x11AB but it was 0x%x.", data);
}
@@ -54,7 +54,7 @@ Test(STY, 8bits)
Init()
snes.cpu->_registers.p.x_b = true;
snes.cpu->_registers.y = 0x11;
snes.cpu->STY(0x0);
snes.cpu->STY(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.wram->_data[0];
cr_assert_eq(data, 0x11, "The stored value should be 0x11 but it was 0x%x.", data);
}
@@ -64,7 +64,7 @@ Test(STY, 16bits)
Init()
snes.cpu->_registers.p.x_b = false;
snes.cpu->_registers.y = 0x11AB;
snes.cpu->STY(0x0);
snes.cpu->STY(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.wram->_data[0] + (snes.wram->_data[1] << 8u);
cr_assert_eq(data, 0x11AB, "The stored value should be 0x11AB but it was 0x%x.", data);
}
@@ -74,7 +74,7 @@ Test(STZ, 8bits)
Init()
snes.cpu->_registers.p.m = true;
snes.wram->_data[0] = 0x11;
snes.cpu->STZ(0x0);
snes.cpu->STZ(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.wram->_data[0];
cr_assert_eq(data, 0x00, "The stored value should be 0x00 but it was 0x%x.", data);
}
@@ -85,7 +85,7 @@ Test(STZ, 16bits)
snes.cpu->_registers.p.m = false;
snes.wram->_data[0] = 0x11;
snes.wram->_data[1] = 0x11;
snes.cpu->STZ(0x0);
snes.cpu->STZ(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.wram->_data[0] + (snes.wram->_data[1] << 8u);
cr_assert_eq(data, 0x00, "The stored value should be 0x00 but it was 0x%x.", data);
}
@@ -95,7 +95,7 @@ Test(LDX, 8bits)
Init()
snes.cpu->_registers.p.x_b = true;
snes.wram->_data[0] = 0x01;
snes.cpu->LDX(0x0);
snes.cpu->LDX(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.x;
cr_assert_eq(data, 0x01, "The stored value should be 0x01 but it was 0x%x.", data);
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag register should not be set.");
@@ -107,7 +107,7 @@ Test(LDX, 8bitsNegative)
Init()
snes.cpu->_registers.p.x_b = true;
snes.wram->_data[0] = 0x11;
snes.cpu->LDX(0x0);
snes.cpu->LDX(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.x;
cr_assert_eq(data, 0x11, "The stored value should be 0x11 but it was 0x%x.", data);
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag register should not be set.");
@@ -120,7 +120,7 @@ Test(LDX, 8bitsZero)
snes.cpu->_registers.p.x_b = true;
snes.wram->_data[0] = 0x00;
snes.wram->_data[1] = 0x11;
snes.cpu->LDX(0x0);
snes.cpu->LDX(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.x;
cr_assert_eq(data, 0x00, "The stored value should be 0x00 but it was 0x%x.", data);
cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flag register should be set.");
@@ -133,7 +133,7 @@ Test(LDX, 16bits)
snes.cpu->_registers.p.x_b = false;
snes.wram->_data[0] = 0xAB;
snes.wram->_data[1] = 001;
snes.cpu->LDX(0x0);
snes.cpu->LDX(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.x;
cr_assert_eq(data, 0x01AB, "The stored value should be 0x01AB but it was 0x%x.", data);
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag register should not be set.");
@@ -146,7 +146,7 @@ Test(LDX, 16bitsNegative)
snes.cpu->_registers.p.x_b = false;
snes.wram->_data[0] = 0xAB;
snes.wram->_data[1] = 0x11;
snes.cpu->LDX(0x0);
snes.cpu->LDX(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.x;
cr_assert_eq(data, 0x11AB, "The stored value should be 0x11AB but it was 0x%x.", data);
cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag register should not be set.");
@@ -159,7 +159,7 @@ Test(LDX, 16bitsZero)
snes.cpu->_registers.p.x_b = false;
snes.wram->_data[0] = 0x00;
snes.wram->_data[1] = 0x00;
snes.cpu->LDX(0x0);
snes.cpu->LDX(0x0, ComSquare::CPU::AddressingMode::Implied);
auto data = snes.cpu->_registers.x;
cr_assert_eq(data, 0x0000, "The stored value should be 0x0000 but it was 0x%x.", data);
cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flag register should be set.");

104
ui/cpu.ui
View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>420</width>
<height>438</height>
<width>971</width>
<height>709</height>
</rect>
</property>
<property name="windowTitle">
@@ -21,28 +21,39 @@
<bool>false</bool>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0" rowspan="2">
<widget class="QTableView" name="disassembly">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="gridStyle">
<enum>Qt::NoPen</enum>
</property>
<attribute name="horizontalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="loggerLabel">
<property name="text">
<string>Instructions History</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QTextBrowser" name="logger"/>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="clear">
<property name="text">
<string>Clear History</string>
</property>
</widget>
</item>
<item row="0" column="0" rowspan="3">
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="accumulatorLabel">
@@ -150,10 +161,37 @@
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="1">
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<widget class="QPushButton" name="clear">
<property name="text">
<string>Clear History</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="loggerLabel">
<property name="text">
<string>Instructions History</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QTextBrowser" name="logger"/>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QToolBar" name="toolBar">
@@ -182,6 +220,7 @@
<bool>false</bool>
</attribute>
<addaction name="actionPause"/>
<addaction name="actionNext"/>
<addaction name="actionStep"/>
</widget>
<action name="actionPause">
@@ -190,13 +229,13 @@
<normaloff>:/resources/icons/play.svg</normaloff>:/resources/icons/play.svg</iconset>
</property>
<property name="text">
<string>Resume</string>
<string>Continue</string>
</property>
<property name="toolTip">
<string>Pause or Resume instruction execution.</string>
</property>
<property name="shortcut">
<string>P</string>
<string>C</string>
</property>
</action>
<action name="actionStep">
@@ -214,6 +253,21 @@
<string>S</string>
</property>
</action>
<action name="actionNext">
<property name="icon">
<iconset resource="../resources/appResources.qrc">
<normaloff>:/resources/icons/continue.svg</normaloff>:/resources/icons/continue.svg</iconset>
</property>
<property name="text">
<string>Next</string>
</property>
<property name="toolTip">
<string>Continue execution to the next line.</string>
</property>
<property name="shortcut">
<string>N</string>
</property>
</action>
</widget>
<resources>
<include location="../resources/appResources.qrc"/>