diff --git a/CMakeLists.txt b/CMakeLists.txt index 0edbfec..1d92bce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/resources/appResources.qrc b/resources/appResources.qrc index 8e8574c..52163e6 100644 --- a/resources/appResources.qrc +++ b/resources/appResources.qrc @@ -1,5 +1,6 @@ + icons/continue.svg icons/step.svg Logo.png icons/play.svg diff --git a/resources/icons/continue.svg b/resources/icons/continue.svg new file mode 100644 index 0000000..9af8782 --- /dev/null +++ b/resources/icons/continue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sources/CPU/AddressingModes.cpp b/sources/CPU/AddressingModes.cpp index edcb35f..0d39b18 100644 --- a/sources/CPU/AddressingModes.cpp +++ b/sources/CPU/AddressingModes.cpp @@ -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(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; } diff --git a/sources/CPU/CPU.cpp b/sources/CPU/CPU.cpp index 1dfe1cb..a31bf3f 100644 --- a/sources/CPU/CPU.cpp +++ b/sources/CPU/CPU.cpp @@ -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) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index 84d613f..3e45f77 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -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 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(); diff --git a/sources/CPU/Instruction.hpp b/sources/CPU/Instruction.hpp new file mode 100644 index 0000000..5af02f4 --- /dev/null +++ b/sources/CPU/Instruction.hpp @@ -0,0 +1,67 @@ +// +// Created by anonymus-raccoon on 3/25/20. +// + +#ifndef COMSQUARE_INSTRUCTION_HPP +#define COMSQUARE_INSTRUCTION_HPP + +#include +#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 diff --git a/sources/CPU/Instructions/BitsInstructions.cpp b/sources/CPU/Instructions/BitsInstructions.cpp index 46b2b6a..d3ecefa 100644 --- a/sources/CPU/Instructions/BitsInstructions.cpp +++ b/sources/CPU/Instructions/BitsInstructions.cpp @@ -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; } } \ No newline at end of file diff --git a/sources/CPU/Instructions/InternalInstruction.cpp b/sources/CPU/Instructions/InternalInstruction.cpp index 6c16c0c..782bfbb 100644 --- a/sources/CPU/Instructions/InternalInstruction.cpp +++ b/sources/CPU/Instructions/InternalInstruction.cpp @@ -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 - this->_registers.a = this->_pop16(); + 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 - this->_registers.x = this->_pop16(); + 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 - this->_registers.y = this->_pop16(); + 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(this->_bus->read(valueAddr)); - return !this->_registers.p.c; + this->_registers.pc += static_cast(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(this->_bus->read(valueAddr)); - return this->_registers.p.c; + this->_registers.pc += static_cast(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(this->_bus->read(valueAddr)); - return this->_registers.p.z; + this->_registers.pc += static_cast(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(this->_bus->read(valueAddr)); - return !this->_registers.p.z; + this->_registers.pc += static_cast(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(this->_bus->read(valueAddr)); - return this->_registers.p.n; + this->_registers.pc += static_cast(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(this->_bus->read(valueAddr)); - return !this->_registers.p.n; + this->_registers.pc += static_cast(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(this->_bus->read(valueAddr)); - return true; + this->_registers.pc += static_cast(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(value); - return true; + this->_registers.pc += static_cast(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(this->_bus->read(valueAddr)); - return !this->_registers.p.v; + this->_registers.pc += static_cast(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(this->_bus->read(valueAddr)); - return this->_registers.p.v; + this->_registers.pc += static_cast(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; } } \ No newline at end of file diff --git a/sources/CPU/Instructions/Interrupts.cpp b/sources/CPU/Instructions/Interrupts.cpp index 1c5d021..1155da4 100644 --- a/sources/CPU/Instructions/Interrupts.cpp +++ b/sources/CPU/Instructions/Interrupts.cpp @@ -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; } } \ No newline at end of file diff --git a/sources/CPU/Instructions/MathematicalOperations.cpp b/sources/CPU/Instructions/MathematicalOperations.cpp index 75835ea..5b6e9e1 100644 --- a/sources/CPU/Instructions/MathematicalOperations.cpp +++ b/sources/CPU/Instructions/MathematicalOperations.cpp @@ -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; } } \ No newline at end of file diff --git a/sources/CPU/Instructions/MemoryInstructions.cpp b/sources/CPU/Instructions/MemoryInstructions.cpp index 8a7b310..f102d90 100644 --- a/sources/CPU/Instructions/MemoryInstructions.cpp +++ b/sources/CPU/Instructions/MemoryInstructions.cpp @@ -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; } } \ No newline at end of file diff --git a/sources/CPU/Instructions/TransferRegisters.cpp b/sources/CPU/Instructions/TransferRegisters.cpp index 4b95092..4905071 100644 --- a/sources/CPU/Instructions/TransferRegisters.cpp +++ b/sources/CPU/Instructions/TransferRegisters.cpp @@ -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; } } \ No newline at end of file diff --git a/sources/Cartridge/Cartridge.cpp b/sources/Cartridge/Cartridge.cpp index 227903a..91a8036 100644 --- a/sources/Cartridge/Cartridge.cpp +++ b/sources/Cartridge/Cartridge.cpp @@ -15,22 +15,18 @@ 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); - FILE *rom = fopen(romPath.c_str(), "rb"); + if (romPath.empty()) + throw InvalidRomException("Path is empty."); + size_t size = Cartridge::getRomSize(romPath); + FILE *rom = fopen(romPath.c_str(), "rb"); - if (!rom) - throw InvalidRomException("Could not open the rom file at " + romPath + ". " + strerror(errno)); - this->_size = size; - this->_data = new uint8_t[size]; - 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; - } + if (!rom) + throw InvalidRomException("Could not open the rom file at " + romPath + ". " + strerror(errno)); + this->_size = size; + this->_data = new uint8_t[size]; + std::memset(this->_data, 0, size); + fread(this->_data, 1, size, rom); + this->_loadHeader(); } size_t Cartridge::getRomSize(const std::string &romPath) diff --git a/sources/Debugger/CGramDebug.cpp b/sources/Debugger/CGramDebug.cpp index d2923a7..7d46c6f 100644 --- a/sources/Debugger/CGramDebug.cpp +++ b/sources/Debugger/CGramDebug.cpp @@ -5,8 +5,6 @@ #include "CGramDebug.hpp" #include "../SNES.hpp" #include -#include -#include #include #include "../Utility/Utility.hpp" diff --git a/sources/Debugger/CPUDebug.cpp b/sources/Debugger/CPUDebug.cpp index 80a4c07..170fb23 100644 --- a/sources/Debugger/CPUDebug.cpp +++ b/sources/Debugger/CPUDebug.cpp @@ -5,8 +5,11 @@ #include "CPUDebug.hpp" #include "../Utility/Utility.hpp" #include "../Exceptions/InvalidOpcode.hpp" +#include "../CPU/CPU.hpp" #include +#include #include +#include using namespace ComSquare::CPU; @@ -16,6 +19,8 @@ namespace ComSquare::Debugger : CPU(basicCPU), _window(new ClosableWindow(*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 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 CPUDebug::_disassemble(uint24_t pc, uint24_t length, DisassemblyContext &ctx) + { + std::vector 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(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(this->_bus->read(pc), true); + ss << "$" << std::hex << static_cast(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(); } -} \ No newline at end of file + + 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(); +} diff --git a/sources/Debugger/CPUDebug.hpp b/sources/Debugger/CPUDebug.hpp index 15fbb7c..fb04acb 100644 --- a/sources/Debugger/CPUDebug.hpp +++ b/sources/Debugger/CPUDebug.hpp @@ -5,6 +5,7 @@ #ifndef COMSQUARE_CPUDEBUG_HPP #define COMSQUARE_CPUDEBUG_HPP +#include #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 *_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 _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 disassembledInstructions; + //! @brief The list of breakpoints the user has set. + std::vector 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; diff --git a/sources/Debugger/MemoryBusDebug.cpp b/sources/Debugger/MemoryBusDebug.cpp index 2caafab..e1548e5 100644 --- a/sources/Debugger/MemoryBusDebug.cpp +++ b/sources/Debugger/MemoryBusDebug.cpp @@ -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); diff --git a/sources/Exceptions/DebuggableError.hpp b/sources/Exceptions/DebuggableError.hpp new file mode 100644 index 0000000..fcc9180 --- /dev/null +++ b/sources/Exceptions/DebuggableError.hpp @@ -0,0 +1,15 @@ +// +// Created by anonymus-raccoon on 3/26/20. +// + +#ifndef COMSQUARE_DEBUGGABLEERROR_HPP +#define COMSQUARE_DEBUGGABLEERROR_HPP + +#include + +namespace ComSquare +{ + class DebuggableError : public std::exception {}; +} + +#endif //COMSQUARE_DEBUGGABLEERROR_HPP diff --git a/sources/Exceptions/InvalidAction.hpp b/sources/Exceptions/InvalidAction.hpp index 8b9c06a..e74d73d 100644 --- a/sources/Exceptions/InvalidAction.hpp +++ b/sources/Exceptions/InvalidAction.hpp @@ -7,11 +7,12 @@ #include #include +#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: diff --git a/sources/Exceptions/InvalidAddress.hpp b/sources/Exceptions/InvalidAddress.hpp index 09724ae..b91ce6e 100644 --- a/sources/Exceptions/InvalidAddress.hpp +++ b/sources/Exceptions/InvalidAddress.hpp @@ -9,11 +9,12 @@ #include #include #include +#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: diff --git a/sources/Exceptions/InvalidOpcode.hpp b/sources/Exceptions/InvalidOpcode.hpp index 00447c4..57e990d 100644 --- a/sources/Exceptions/InvalidOpcode.hpp +++ b/sources/Exceptions/InvalidOpcode.hpp @@ -8,11 +8,12 @@ #include #include #include +#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: diff --git a/sources/Exceptions/InvalidRom.hpp b/sources/Exceptions/InvalidRom.hpp index 433c478..f0f331a 100644 --- a/sources/Exceptions/InvalidRom.hpp +++ b/sources/Exceptions/InvalidRom.hpp @@ -7,11 +7,12 @@ #include #include +#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: diff --git a/sources/Renderer/QtRenderer/QtSFML.cpp b/sources/Renderer/QtRenderer/QtSFML.cpp index d90a581..760bfd8 100644 --- a/sources/Renderer/QtRenderer/QtSFML.cpp +++ b/sources/Renderer/QtRenderer/QtSFML.cpp @@ -8,6 +8,9 @@ #include #include #include "QtSFML.hpp" +#include "../../Exceptions/InvalidOpcode.hpp" +#include "../../Exceptions/InvalidAddress.hpp" +#include "../../Exceptions/InvalidAction.hpp" #ifdef Q_WS_X11 #include @@ -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(); diff --git a/sources/SNES.cpp b/sources/SNES.cpp index 88e796d..0686da6 100644 --- a/sources/SNES.cpp +++ b/sources/SNES.cpp @@ -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(this->cpu)->focus(); - else + if (this->cpu->isDebugger()) { + auto cpuDebug = std::static_pointer_cast(this->cpu); + cpuDebug->focus(); + if (pause) + cpuDebug->pause(); + } else this->cpu = std::make_shared(*this->cpu, *this); #else std::cerr << "Debugging features are not enabled. You can't enable the debugger." << std::endl; + (void)pause; #endif } diff --git a/sources/SNES.hpp b/sources/SNES.hpp index 19dcbb4..5babc05 100644 --- a/sources/SNES.hpp +++ b/sources/SNES.hpp @@ -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. diff --git a/sources/Utility/Utility.cpp b/sources/Utility/Utility.cpp index c66efbc..66500b7 100644 --- a/sources/Utility/Utility.cpp +++ b/sources/Utility/Utility.cpp @@ -3,29 +3,42 @@ // #include +#include #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(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) diff --git a/sources/Utility/Utility.hpp b/sources/Utility/Utility.hpp index 2fbc8bb..489afc3 100644 --- a/sources/Utility/Utility.hpp +++ b/sources/Utility/Utility.hpp @@ -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); diff --git a/sources/main.cpp b/sources/main.cpp index 9c4de9b..b0f0afe 100644 --- a/sources/main.cpp +++ b/sources/main.cpp @@ -77,8 +77,14 @@ int main(int argc, char **argv) QApplication app(argc, argv); QApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton); Renderer::QtSFML renderer(600, 800); - SNES snes(argv[1], renderer); - renderer.createWindow(snes, 60); - parseArguments(argc, argv, snes); - return QApplication::exec(); + 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; + } } \ No newline at end of file diff --git a/tests/CPU/Math/testADC.cpp b/tests/CPU/Math/testADC.cpp index 67222fd..00a5106 100644 --- a/tests/CPU/Math/testADC.cpp +++ b/tests/CPU/Math/testADC.cpp @@ -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."); diff --git a/tests/CPU/Math/testSBC.cpp b/tests/CPU/Math/testSBC.cpp index 22c2a9e..52b0186 100644 --- a/tests/CPU/Math/testSBC.cpp +++ b/tests/CPU/Math/testSBC.cpp @@ -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."); diff --git a/tests/CPU/TransferRegisters.cpp b/tests/CPU/TransferRegisters.cpp index 7ee8e76..c840e2a 100644 --- a/tests/CPU/TransferRegisters.cpp +++ b/tests/CPU/TransferRegisters.cpp @@ -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."); diff --git a/tests/CPU/testAddressingMode.cpp b/tests/CPU/testAddressingMode.cpp index 0ae047f..485366e 100644 --- a/tests/CPU/testAddressingMode.cpp +++ b/tests/CPU/testAddressingMode.cpp @@ -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) diff --git a/tests/CPU/testBits.cpp b/tests/CPU/testBits.cpp index 04b3fe6..a06c7af 100644 --- a/tests/CPU/testBits.cpp +++ b/tests/CPU/testBits.cpp @@ -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."); diff --git a/tests/CPU/testInternal.cpp b/tests/CPU/testInternal.cpp index 7caa8ea..0619ece 100644 --- a/tests/CPU/testInternal.cpp +++ b/tests/CPU/testInternal.cpp @@ -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); } \ No newline at end of file diff --git a/tests/CPU/testInterupts.cpp b/tests/CPU/testInterupts.cpp index 9d26dd2..cfb7a77 100644 --- a/tests/CPU/testInterupts.cpp +++ b/tests/CPU/testInterupts.cpp @@ -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."); diff --git a/tests/CPU/testStore.cpp b/tests/CPU/testStore.cpp index 3bd0a97..4f0af93 100644 --- a/tests/CPU/testStore.cpp +++ b/tests/CPU/testStore.cpp @@ -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."); diff --git a/ui/cpu.ui b/ui/cpu.ui index 09dd3ed..bbaf550 100644 --- a/ui/cpu.ui +++ b/ui/cpu.ui @@ -6,8 +6,8 @@ 0 0 - 420 - 438 + 971 + 709 @@ -21,28 +21,39 @@ false - + + + + + + 1 + 0 + + + + + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + false + + + Qt::NoPen + + + false + + + false + + + - - - Instructions History - - - Qt::AlignCenter - - - - - - - - - - Clear History - - - - @@ -150,10 +161,37 @@ Qt::RightToLeft + + true + + + + + + + Clear History + + + + + + + Instructions History + + + Qt::AlignCenter + + + + + + + + @@ -182,6 +220,7 @@ false + @@ -190,13 +229,13 @@ :/resources/icons/play.svg:/resources/icons/play.svg - Resume + Continue Pause or Resume instruction execution. - P + C @@ -214,6 +253,21 @@ S + + + + :/resources/icons/continue.svg:/resources/icons/continue.svg + + + Next + + + Continue execution to the next line. + + + N + +