From 4fb577f65d761504249d358c752e67751c34c2c8 Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Sat, 28 Mar 2020 22:51:28 +0100 Subject: [PATCH 01/26] Implementing auto scroll --- sources/CPU/CPU.hpp | 4 +-- sources/Debugger/CPUDebug.cpp | 47 ++++++++++++++++++++++++----------- sources/Debugger/CPUDebug.hpp | 5 +++- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index 3e45f77..1504737 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -476,7 +476,7 @@ namespace ComSquare::CPU {&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::BRK, 7, "rtl #-#", AddressingMode::Implied, 1}, // 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 @@ -487,7 +487,7 @@ namespace ComSquare::CPU {&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::BRK, 7, "rts #-#", AddressingMode::Implied, 1}, // 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 diff --git a/sources/Debugger/CPUDebug.cpp b/sources/Debugger/CPUDebug.cpp index 170fb23..1eb696d 100644 --- a/sources/Debugger/CPUDebug.cpp +++ b/sources/Debugger/CPUDebug.cpp @@ -29,7 +29,7 @@ namespace ComSquare::Debugger 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->_updateDisassembly(this->_cartridgeHeader.emulationInterrupts.reset, 0xFFFF - this->_cartridgeHeader.emulationInterrupts.reset); //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(); @@ -44,6 +44,7 @@ namespace ComSquare::Debugger QMainWindow::connect(this->_ui.disassembly->verticalHeader(), &QHeaderView::sectionClicked, this, &CPUDebug::toggleBreakpoint); this->_window->show(); this->_updateRegistersPanel(); + this->_updateDisassembly(this->_registers.pac, 0); } bool CPUDebug::isDebugger() @@ -65,7 +66,7 @@ namespace ComSquare::Debugger return 0xFF; if (this->_isStepping) { cycles = this->_executeInstruction(this->readPC()); - this->_updateDisassembly(); + this->_updateDisassembly(this->_registers.pac); return cycles; } @@ -115,7 +116,7 @@ namespace ComSquare::Debugger this->_ui.actionPause->setText("Resume"); else this->_ui.actionPause->setText("Pause"); - this->_updateDisassembly(); + this->_updateDisassembly(this->_registers.pac); } void CPUDebug::step() @@ -190,25 +191,28 @@ namespace ComSquare::Debugger this->_ui.logger->clear(); } - void CPUDebug::_updateDisassembly(uint24_t refreshSize) + void CPUDebug::_updateDisassembly(uint24_t start, uint24_t refreshSize) { - auto first = std::find_if(this->disassembledInstructions.begin(), this->disassembledInstructions.end(), [this](DisassembledInstruction &i) { - return i.address >= this->_registers.pac; + auto first = std::find_if(this->disassembledInstructions.begin(), this->disassembledInstructions.end(), [start](DisassembledInstruction &i) { + return i.address >= start; }); - auto end = std::find_if(this->disassembledInstructions.begin(), this->disassembledInstructions.end(),[this, refreshSize](DisassembledInstruction &i) { - return i.address >= this->_registers.pac + refreshSize; + auto end = std::find_if(this->disassembledInstructions.begin(), this->disassembledInstructions.end(),[start, refreshSize](DisassembledInstruction &i) { + return i.address >= start + 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; + auto next = std::find_if(this->disassembledInstructions.begin(), this->disassembledInstructions.end(), [start](DisassembledInstruction &i) { + return i.address >= start; }); - int row = next - this->disassembledInstructions.begin(); DisassemblyContext ctx = this->_getDisassemblyContext(); - std::vector nextInstructions = this->_disassemble(this->_registers.pac, refreshSize, ctx); + std::vector nextInstructions = this->_disassemble(start, 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); + + int row = next - this->disassembledInstructions.begin(); + if (this->_ui.disassembly->rowAt(0) > row || this->_ui.disassembly->rowAt(this->_ui.disassembly->height()) < row) { + auto index = this->_model.index(row, 0); + this->_ui.disassembly->scrollTo(index, QAbstractItemView::PositionAtCenter); + } this->_ui.disassembly->viewport()->repaint(); } @@ -347,6 +351,8 @@ namespace ComSquare::Debugger int CPUDebug::RESB() { CPU::RESB(); + this->disassembledInstructions.clear(); + this->_updateDisassembly(0xFFFF - this->_cartridgeHeader.emulationInterrupts.reset); this->_updateRegistersPanel(); return (0); } @@ -362,7 +368,7 @@ namespace ComSquare::Debugger } 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) {} + : CPU::Instruction(instruction), address(addr), argument(std::move(arg)), opcode(op), level(Safe) {} std::string DisassembledInstruction::toString() { @@ -414,6 +420,17 @@ QVariant DisassemblyModel::headerData(int section, Qt::Orientation orientation, return QString(ComSquare::Utility::to_hex(instruction.address, ComSquare::Utility::HexString::NoPrefix).c_str()); } +void DisassemblyModel::beginReset() +{ + this->beginResetModel(); +} + +void DisassemblyModel::endReset() +{ + this->endResetModel(); + emit this->dataChanged(this->index(0, 0), this->index(this->_cpu.disassembledInstructions.size(), this->columnCount(QModelIndex()))); +} + RowPainter::RowPainter(ComSquare::Debugger::CPUDebug &cpu, QObject *parent) : QStyledItemDelegate(parent), _cpu(cpu) { } void RowPainter::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const diff --git a/sources/Debugger/CPUDebug.hpp b/sources/Debugger/CPUDebug.hpp index fb04acb..6d7ab99 100644 --- a/sources/Debugger/CPUDebug.hpp +++ b/sources/Debugger/CPUDebug.hpp @@ -37,6 +37,9 @@ public: 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; + + void beginReset(); + void endReset(); }; //! @brief The qt class that highlight breakpoints and the PC's position @@ -128,7 +131,7 @@ namespace ComSquare::Debugger //! @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); + void _updateDisassembly(uint24_t start, 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. From b090a7d036a1e625a76dbac5126bfa85051d1e17 Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Fri, 3 Apr 2020 13:51:05 +0200 Subject: [PATCH 02/26] Implementing the absolute indirect long addressing mode --- sources/CPU/AddressingModes.cpp | 10 ++++++++++ sources/CPU/CPU.cpp | 10 +++------- sources/CPU/CPU.hpp | 2 ++ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/sources/CPU/AddressingModes.cpp b/sources/CPU/AddressingModes.cpp index 0d39b18..4cba032 100644 --- a/sources/CPU/AddressingModes.cpp +++ b/sources/CPU/AddressingModes.cpp @@ -151,6 +151,16 @@ namespace ComSquare::CPU return effective; } + uint24_t CPU::_getAbsoluteIndirectLongAddr() + { + uint16_t abs = this->readPC(); + abs += this->readPC() << 8u; + uint24_t effective = this->_bus->read(abs); + effective += this->_bus->read(abs + 1) << 8u; + effective += this->_bus->read(abs + 2) << 16u; + return effective; + } + uint24_t CPU::_getAbsoluteIndirectIndexedByXAddr() { uint24_t abs = this->readPC(); diff --git a/sources/CPU/CPU.cpp b/sources/CPU/CPU.cpp index a31bf3f..ccb315e 100644 --- a/sources/CPU/CPU.cpp +++ b/sources/CPU/CPU.cpp @@ -231,9 +231,6 @@ namespace ComSquare::CPU valueAddr = this->_getImmediateAddrForX(); break; - // TODO implement the relative addressing mode - // TODO implement the relative long addressing mode - case Absolute: valueAddr = this->_getAbsoluteAddr(); break; @@ -243,8 +240,9 @@ namespace ComSquare::CPU case AbsoluteIndirect: valueAddr = this->_getAbsoluteIndirectAddr(); break; - - //TODO implement absolute indirect long addressing mode + case AbsoluteIndirectLong: + valueAddr = this->_getAbsoluteIndirectLongAddr(); + break; case DirectPage: valueAddr = this->_getDirectAddr(); @@ -298,8 +296,6 @@ namespace ComSquare::CPU case AbsoluteIndirectIndexedByX: valueAddr = this->_getAbsoluteIndirectIndexedByXAddr(); break; - default: - break; } return instruction.cycleCount + (this->*instruction.call)(valueAddr, instruction.addressingMode); diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index 1504737..e9159ba 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -232,6 +232,8 @@ namespace ComSquare::CPU uint24_t _getProgramCounterRelativeLongAddr(); //! @brief 2 bytes are pulled from the to form the effective address. uint24_t _getAbsoluteIndirectAddr(); + //! @brief 3 bytes are pulled from the to form the effective address. + uint24_t _getAbsoluteIndirectLongAddr(); //! @brief The is added with X, then 2 bytes are pulled from that address to form the new location. uint24_t _getAbsoluteIndirectIndexedByXAddr(); //! @brief 2 bytes are pulled from the direct page address to form the 16-bit address. It is combined with DBR to form a 24-bit effective address. From 7407c3cfaaeefc6896160269fea6a6dab8e466c4 Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Fri, 3 Apr 2020 14:32:18 +0200 Subject: [PATCH 03/26] Implementing missing addressings modes inside the debugger --- CMakeLists.txt | 6 +- sources/CPU/AddressingModes.cpp | 16 -- sources/CPU/CPU.cpp | 6 - sources/CPU/CPU.hpp | 4 - sources/CPU/Instruction.hpp | 3 - sources/Debugger/{ => CPU}/CPUDebug.cpp | 152 +----------- sources/Debugger/{ => CPU}/CPUDebug.hpp | 45 +++- sources/Debugger/CPU/Disassembly.cpp | 272 +++++++++++++++++++++ sources/SNES.cpp | 2 +- tests/CPU/testAddressingMode.cpp | 78 +++--- ui/ui_cpu.h | 306 ++++++++++++++++++++++++ 11 files changed, 659 insertions(+), 231 deletions(-) rename sources/Debugger/{ => CPU}/CPUDebug.cpp (71%) rename sources/Debugger/{ => CPU}/CPUDebug.hpp (77%) create mode 100644 sources/Debugger/CPU/Disassembly.cpp create mode 100644 ui/ui_cpu.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 17a4bc3..4f38620 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -159,8 +159,8 @@ add_executable(ComSquare sources/CPU/Instructions/InternalInstruction.cpp sources/Ram/ExtendedRam.cpp sources/Ram/ExtendedRam.hpp - sources/Debugger/CPUDebug.cpp - sources/Debugger/CPUDebug.hpp + sources/Debugger/CPU/CPUDebug.cpp + sources/Debugger/CPU/CPUDebug.hpp sources/Renderer/QtRenderer/QtSFML.cpp sources/Renderer/QtRenderer/QtSFML.hpp sources/Renderer/QtRenderer/QtWidgetSFML.cpp @@ -205,7 +205,7 @@ add_executable(ComSquare sources/Models/Components.hpp sources/CPU/Instruction.hpp sources/Exceptions/DebuggableError.hpp -) + sources/Debugger/CPU/Disassembly.cpp) target_compile_definitions(ComSquare PUBLIC DEBUGGER_ENABLED) diff --git a/sources/CPU/AddressingModes.cpp b/sources/CPU/AddressingModes.cpp index 4cba032..d19a985 100644 --- a/sources/CPU/AddressingModes.cpp +++ b/sources/CPU/AddressingModes.cpp @@ -126,22 +126,6 @@ namespace ComSquare::CPU return lng + this->_registers.x; } - uint24_t CPU::_getProgramCounterRelativeAddr() - { - uint24_t pc = 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->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->readPC(); diff --git a/sources/CPU/CPU.cpp b/sources/CPU/CPU.cpp index ccb315e..9fdb770 100644 --- a/sources/CPU/CPU.cpp +++ b/sources/CPU/CPU.cpp @@ -287,12 +287,6 @@ namespace ComSquare::CPU valueAddr = this->_getStackRelativeIndirectIndexedYAddr(); break; - case ProgramCounterRelative: - valueAddr = this->_getProgramCounterRelativeAddr(); - break; - case ProgramCounterRelativeLong: - valueAddr = this->_getProgramCounterRelativeLongAddr(); - break; case AbsoluteIndirectIndexedByX: valueAddr = this->_getAbsoluteIndirectIndexedByXAddr(); break; diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index e9159ba..6c60e09 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -226,10 +226,6 @@ namespace ComSquare::CPU uint24_t _getAbsoluteIndexedByYAddr(); //! @brief The effective address is formed by adding the with X. uint24_t _getAbsoluteIndexedByXLongAddr(); - //! @brief The <8-bit signed exp> is added to PC (program counter) to form the new location. - uint24_t _getProgramCounterRelativeAddr(); - //! @brief The <16-bit signed exp> is added to PC (program counter) to form the new location. - uint24_t _getProgramCounterRelativeLongAddr(); //! @brief 2 bytes are pulled from the to form the effective address. uint24_t _getAbsoluteIndirectAddr(); //! @brief 3 bytes are pulled from the to form the effective address. diff --git a/sources/CPU/Instruction.hpp b/sources/CPU/Instruction.hpp index 5af02f4..8061d1e 100644 --- a/sources/CPU/Instruction.hpp +++ b/sources/CPU/Instruction.hpp @@ -41,9 +41,6 @@ namespace ComSquare::CPU StackRelativeIndirectIndexedByY, - ProgramCounterRelative, - ProgramCounterRelativeLong, - AbsoluteIndirect, AbsoluteIndirectIndexedByX, diff --git a/sources/Debugger/CPUDebug.cpp b/sources/Debugger/CPU/CPUDebug.cpp similarity index 71% rename from sources/Debugger/CPUDebug.cpp rename to sources/Debugger/CPU/CPUDebug.cpp index 1eb696d..098c434 100644 --- a/sources/Debugger/CPUDebug.cpp +++ b/sources/Debugger/CPU/CPUDebug.cpp @@ -3,9 +3,9 @@ // #include "CPUDebug.hpp" -#include "../Utility/Utility.hpp" -#include "../Exceptions/InvalidOpcode.hpp" -#include "../CPU/CPU.hpp" +#include "../../Utility/Utility.hpp" +#include "../../Exceptions/InvalidOpcode.hpp" +#include "../../CPU/CPU.hpp" #include #include #include @@ -221,133 +221,6 @@ namespace ComSquare::Debugger 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 (dual) - 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)); - return ss.str(); - } - - std::string CPUDebug::_getAbsoluteValue(uint24_t pc) - { - std::stringstream ss; - ss << "$" << std::hex << (this->_bus->read(pc) + (this->_bus->read(pc + 1, true) << 8u)); - return ss.str(); - } - - std::string CPUDebug::_getAbsoluteLongValue(uint24_t pc) - { - unsigned value = this->_bus->read(pc++, true); - value += this->_bus->read(pc++, true) << 8u; - value += this->_bus->read(pc, true) << 16u; - - std::stringstream ss; - ss << "$" << std::hex << value; - return ss.str(); - } - - std::string CPUDebug::_getDirectIndexedByXValue(uint24_t pc) - { - unsigned value = this->_bus->read(pc, true); - - std::stringstream ss; - ss << "$" << std::hex << value << ", x"; - return ss.str(); - } - int CPUDebug::RESB() { CPU::RESB(); @@ -366,14 +239,6 @@ namespace ComSquare::Debugger { 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), level(Safe) {} - - std::string DisassembledInstruction::toString() - { - return this->name + " " + this->argument; - } } DisassemblyModel::DisassemblyModel(ComSquare::Debugger::CPUDebug &cpu) : QAbstractTableModel(), _cpu(cpu){ } @@ -420,17 +285,6 @@ QVariant DisassemblyModel::headerData(int section, Qt::Orientation orientation, return QString(ComSquare::Utility::to_hex(instruction.address, ComSquare::Utility::HexString::NoPrefix).c_str()); } -void DisassemblyModel::beginReset() -{ - this->beginResetModel(); -} - -void DisassemblyModel::endReset() -{ - this->endResetModel(); - emit this->dataChanged(this->index(0, 0), this->index(this->_cpu.disassembledInstructions.size(), this->columnCount(QModelIndex()))); -} - RowPainter::RowPainter(ComSquare::Debugger::CPUDebug &cpu, QObject *parent) : QStyledItemDelegate(parent), _cpu(cpu) { } void RowPainter::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const diff --git a/sources/Debugger/CPUDebug.hpp b/sources/Debugger/CPU/CPUDebug.hpp similarity index 77% rename from sources/Debugger/CPUDebug.hpp rename to sources/Debugger/CPU/CPUDebug.hpp index 6d7ab99..469ffe1 100644 --- a/sources/Debugger/CPUDebug.hpp +++ b/sources/Debugger/CPU/CPUDebug.hpp @@ -6,11 +6,11 @@ #define COMSQUARE_CPUDEBUG_HPP #include -#include "../CPU/CPU.hpp" -#include "../Renderer/SFRenderer.hpp" -#include "../SNES.hpp" -#include "../../ui/ui_cpu.h" -#include "ClosableWindow.hpp" +#include "../../CPU/CPU.hpp" +#include "../../Renderer/SFRenderer.hpp" +#include "../../SNES.hpp" +#include "../../../ui/ui_cpu.h" +#include "../ClosableWindow.hpp" namespace ComSquare::Debugger { @@ -37,9 +37,6 @@ public: 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; - - void beginReset(); - void endReset(); }; //! @brief The qt class that highlight breakpoints and the PC's position @@ -144,14 +141,42 @@ namespace ComSquare::Debugger //! @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. std::string _getAbsoluteValue(uint24_t pc); //! @brief Return a printable string corresponding to the value of an absolute long addressing mode. std::string _getAbsoluteLongValue(uint24_t pc); + //! @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 a direct indirect addressing mode. + std::string _getDirectIndirectValue(uint24_t pc); + //! @brief Return a printable string corresponding to the value of a direct indirect long addressing mode. + std::string _getDirectIndirectLongValue(uint24_t pc); + //! @brief Return a printable string corresponding to the value of a absolute indexed by x addressing mode. + std::string _getAbsoluteIndexByXValue(uint24_t pc); + //! @brief Return a printable string corresponding to the value of a absolute indexed by x long addressing mode. + std::string _getAbsoluteIndexByXLongValue(uint24_t pc); + //! @brief Return a printable string corresponding to the value of a absolute indexed by y addressing mode. + std::string _getAbsoluteIndexByYValue(uint24_t pc); //! @brief Return a printable string corresponding to the value of a direct index by x addressing mode. std::string _getDirectIndexedByXValue(uint24_t pc); + //! @brief Return a printable string corresponding to the value of a direct index by y addressing mode. + std::string _getDirectIndexedByYValue(uint24_t pc); + //! @brief Return a printable string corresponding to the value of a direct indirect index by x addressing mode. + std::string _getDirectIndexedByXIndirectValue(uint24_t pc); + //! @brief Return a printable string corresponding to the value of a direct indirect index by y addressing mode. + std::string _getDirectIndirectIndexedByYValue(uint24_t pc); + //! @brief Return a printable string corresponding to the value of a direct indirect index by y long addressing mode. + std::string _getDirectIndirectIndexedByYLongValue(uint24_t pc); + //! @brief Return a printable string corresponding to the value of a stack relative addressing mode. + std::string _getStackRelativeValue(uint24_t pc); + //! @brief Return a printable string corresponding to the value of a stack relative indirect indexed by y addressing mode. + std::string _getStackRelativeIndiretIndexdeByYValue(uint24_t pc); + //! @brief Return a printable string corresponding to the value of a absolute indirect addressing mode. + std::string _getAbsoluteIndirectValue(uint24_t pc); + //! @brief Return a printable string corresponding to the value of a absolute indirect indexed by x addressing mode. + std::string _getAbsoluteIndirectIndexedByXValue(uint24_t pc); + //! @brief Return a printable string corresponding to the value of a absolute indirect long addressing mode. + std::string _getAbsoluteIndirectLongValue(uint24_t pc); public: //! @brief Pause/Resume the CPU. diff --git a/sources/Debugger/CPU/Disassembly.cpp b/sources/Debugger/CPU/Disassembly.cpp new file mode 100644 index 0000000..7541dd7 --- /dev/null +++ b/sources/Debugger/CPU/Disassembly.cpp @@ -0,0 +1,272 @@ +// +// Created by anonymus-raccoon on 4/3/20. +// + +#include +#include "CPUDebug.hpp" +#include "../../Utility/Utility.hpp" + +using namespace ComSquare::CPU; + +namespace ComSquare::Debugger +{ + 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), level(Safe) {} + + std::string DisassembledInstruction::toString() + { + return this->name + " " + this->argument; + } + + 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 Immediate8bits: + return this->_getImmediateValue(pc, false); + case ImmediateForA: + return this->_getImmediateValue(pc, !ctx.mFlag); + case ImmediateForX: + return this->_getImmediateValue(pc, !ctx.xFlag); + + case Absolute: + return this->_getAbsoluteValue(pc); + case AbsoluteLong: + return this->_getAbsoluteLongValue(pc); + + case DirectPage: + return this->_getDirectValue(pc); + case DirectPageIndexedByX: + return this->_getDirectIndexedByXValue(pc); + + case DirectPageIndirect: + return this->_getDirectIndirectValue(pc); + case DirectPageIndirectLong: + return this->_getDirectIndirectLongValue(pc); + case AbsoluteIndexedByX: + return this->_getAbsoluteIndexByXValue(pc); + case AbsoluteIndexedByXLong: + return this->_getAbsoluteIndexByXLongValue(pc); + case AbsoluteIndexedByY: + return this->_getAbsoluteIndexByYValue(pc); + case DirectPageIndexedByY: + return this->_getDirectIndexedByYValue(pc); + case DirectPageIndirectIndexedByX: + return this->_getDirectIndexedByXIndirectValue(pc); + case DirectPageIndirectIndexedByY: + return this->_getDirectIndirectIndexedByYValue(pc); + case DirectPageIndirectIndexedByYLong: + return this->_getDirectIndirectIndexedByYLongValue(pc); + case StackRelative: + return this->_getStackRelativeValue(pc); + case StackRelativeIndirectIndexedByY: + return this->_getStackRelativeIndiretIndexdeByYValue(pc); + case AbsoluteIndirect: + return this->_getAbsoluteIndirectValue(pc); + case AbsoluteIndirectIndexedByX: + return this->_getAbsoluteIndirectIndexedByXValue(pc); + case AbsoluteIndirectLong: + return this->_getAbsoluteIndirectLongValue(pc); + + default: + return "???"; + } + } + + std::string CPUDebug::_getImmediateValue(uint24_t pc, bool dual) + { + unsigned value = this->_bus->read(pc, true); + + if (dual) + 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) + { + return Utility::to_hex(this->_bus->read(pc, true), Utility::HexString::AsmPrefix); + } + + std::string CPUDebug::_getAbsoluteValue(uint24_t pc) + { + uint24_t value = this->_bus->read(pc) + (this->_bus->read(pc + 1, true) << 8u); + return Utility::to_hex(value, Utility::HexString::AsmPrefix); + } + + std::string CPUDebug::_getAbsoluteLongValue(uint24_t pc) + { + unsigned value = this->_bus->read(pc++, true); + value += this->_bus->read(pc++, true) << 8u; + value += this->_bus->read(pc, true) << 16u; + + return Utility::to_hex(value, Utility::HexString::AsmPrefix); + } + + std::string CPUDebug::_getDirectIndexedByXValue(uint24_t pc) + { + unsigned value = this->_bus->read(pc, true); + + std::stringstream ss; + ss << "$" << std::hex << value << ", x"; + return ss.str(); + } + + std::string CPUDebug::_getDirectIndexedByYValue(uint24_t pc) + { + unsigned value = this->_bus->read(pc, true); + + std::stringstream ss; + ss << "$" << std::hex << value << ", y"; + return ss.str(); + } + + std::string CPUDebug::_getDirectIndirectValue(uint24_t pc) + { + return "(" + Utility::to_hex(this->_bus->read(pc, true), Utility::AsmPrefix) + ")"; + } + + std::string CPUDebug::_getDirectIndirectLongValue(uint24_t pc) + { + return "[" + Utility::to_hex(this->_bus->read(pc, true), Utility::AsmPrefix) + "]"; + } + + std::string CPUDebug::_getAbsoluteIndexByXValue(uint24_t pc) + { + uint24_t value = this->_bus->read(pc) + (this->_bus->read(pc + 1, true) << 8u); + return Utility::to_hex(value, Utility::HexString::AsmPrefix) + ", x"; + } + + std::string CPUDebug::_getAbsoluteIndexByYValue(uint24_t pc) + { + uint24_t value = this->_bus->read(pc) + (this->_bus->read(pc + 1, true) << 8u); + return Utility::to_hex(value, Utility::HexString::AsmPrefix) + ", y"; + } + + std::string CPUDebug::_getAbsoluteIndexByXLongValue(uint24_t pc) + { + unsigned value = this->_bus->read(pc++, true); + value += this->_bus->read(pc++, true) << 8u; + value += this->_bus->read(pc, true) << 16u; + + return Utility::to_hex(value, Utility::HexString::AsmPrefix) + ", x"; + } + + std::string CPUDebug::_getDirectIndexedByXIndirectValue(uint24_t pc) + { + unsigned value = this->_bus->read(pc, true); + + std::stringstream ss; + ss << "($" << std::hex << value << ", x)"; + return ss.str(); + } + + std::string CPUDebug::_getDirectIndirectIndexedByYValue(uint24_t pc) + { + unsigned value = this->_bus->read(pc, true); + + std::stringstream ss; + ss << "($" << std::hex << value << "), y"; + return ss.str(); + } + + std::string CPUDebug::_getDirectIndirectIndexedByYLongValue(uint24_t pc) + { + unsigned value = this->_bus->read(pc, true); + + std::stringstream ss; + ss << "[$" << std::hex << value << "], y"; + return ss.str(); + } + + std::string CPUDebug::_getStackRelativeValue(uint24_t pc) + { + return Utility::to_hex(this->_bus->read(pc, true), Utility::AsmPrefix) + ", s"; + } + + std::string CPUDebug::_getStackRelativeIndiretIndexdeByYValue(uint24_t pc) + { + return "(" + Utility::to_hex(this->_bus->read(pc, true), Utility::AsmPrefix) + ", s), y"; + } + + std::string CPUDebug::_getAbsoluteIndirectValue(uint24_t pc) + { + uint24_t value = this->_bus->read(pc) + (this->_bus->read(pc + 1, true) << 8u); + return "(" + Utility::to_hex(value, Utility::HexString::AsmPrefix) + ")"; + } + + std::string CPUDebug::_getAbsoluteIndirectLongValue(uint24_t pc) + { + unsigned value = this->_bus->read(pc++, true); + value += this->_bus->read(pc++, true) << 8u; + value += this->_bus->read(pc, true) << 16u; + + return "(" + Utility::to_hex(value, Utility::HexString::AsmPrefix) + ")"; + } + + std::string CPUDebug::_getAbsoluteIndirectIndexedByXValue(uint24_t pc) + { + uint24_t value = this->_bus->read(pc) + (this->_bus->read(pc + 1, true) << 8u); + return "(" + Utility::to_hex(value, Utility::HexString::AsmPrefix) + ", x)"; + } +} \ No newline at end of file diff --git a/sources/SNES.cpp b/sources/SNES.cpp index bcbce3c..0687305 100644 --- a/sources/SNES.cpp +++ b/sources/SNES.cpp @@ -6,7 +6,7 @@ #include #include "SNES.hpp" #ifdef DEBUGGER_ENABLED -#include "Debugger/CPUDebug.hpp" +#include "Debugger/CPU/CPUDebug.hpp" #include "Debugger/APUDebug.hpp" #include "Debugger/MemoryBusDebug.hpp" diff --git a/tests/CPU/testAddressingMode.cpp b/tests/CPU/testAddressingMode.cpp index 485366e..f4ed3e4 100644 --- a/tests/CPU/testAddressingMode.cpp +++ b/tests/CPU/testAddressingMode.cpp @@ -181,45 +181,45 @@ Test(AddrMode, AbsoluteLongIndexByX) cr_assert_eq(snes.cpu->_registers.pac, 0x808003); } -Test(AddrMode, ProgramCounterRelativePositive) -{ - Init() - snes.cpu->_registers.pac = 0x808010; - snes.cartridge->_data[0x10] = 0x15; - cr_assert_eq(snes.cpu->_getProgramCounterRelativeAddr(), 0x808025, "Returned address was %x but was expecting 0x808025.", snes.cpu->_getProgramCounterRelativeAddr()); - cr_assert_eq(snes.cpu->_registers.pac, 0x808011); -} - -Test(AddrMode, ProgramCounterRelativeNegative) -{ - Init() - snes.cpu->_registers.pac = 0x808010; - snes.cartridge->_data[0x10] = -0x15; - cr_assert_eq(snes.cpu->_getProgramCounterRelativeAddr(), 0x807FFB, "Returned address was %x but was expecting 0x807FFB.", snes.cpu->_getProgramCounterRelativeAddr()); - cr_assert_eq(snes.cpu->_registers.pac, 0x808011); -} - -Test(AddrMode, ProgramCounterRelativeLongPositive) -{ - Init() - snes.cpu->_registers.pac = 0x808010; - snes.cartridge->_data[0x10] = 0x15; - snes.cartridge->_data[0x11] = 0x10; - auto addr = snes.cpu->_getProgramCounterRelativeLongAddr(); - cr_assert_eq(addr, 0x809025, "Returned address was %x but was expecting 0x809025.", addr); - cr_assert_eq(snes.cpu->_registers.pac, 0x808012); -} - -Test(AddrMode, ProgramCounterRelativeLongNegative) -{ - Init() - snes.cpu->_registers.pac = 0x808010; - snes.cartridge->_data[0x10] = 0x10; - snes.cartridge->_data[0x11] = -0x15; - auto addr = snes.cpu->_getProgramCounterRelativeLongAddr(); - cr_assert_eq(addr, 0x806B00, "Returned address was %x but was expecting 0x806B00.", addr); - cr_assert_eq(snes.cpu->_registers.pac, 0x808012); -} +//Test(AddrMode, ProgramCounterRelativePositive) +//{ +// Init() +// snes.cpu->_registers.pac = 0x808010; +// snes.cartridge->_data[0x10] = 0x15; +// cr_assert_eq(snes.cpu->_getProgramCounterRelativeAddr(), 0x808025, "Returned address was %x but was expecting 0x808025.", snes.cpu->_getProgramCounterRelativeAddr()); +// cr_assert_eq(snes.cpu->_registers.pac, 0x808011); +//} +// +//Test(AddrMode, ProgramCounterRelativeNegative) +//{ +// Init() +// snes.cpu->_registers.pac = 0x808010; +// snes.cartridge->_data[0x10] = -0x15; +// cr_assert_eq(snes.cpu->_getProgramCounterRelativeAddr(), 0x807FFB, "Returned address was %x but was expecting 0x807FFB.", snes.cpu->_getProgramCounterRelativeAddr()); +// cr_assert_eq(snes.cpu->_registers.pac, 0x808011); +//} +// +//Test(AddrMode, ProgramCounterRelativeLongPositive) +//{ +// Init() +// snes.cpu->_registers.pac = 0x808010; +// snes.cartridge->_data[0x10] = 0x15; +// snes.cartridge->_data[0x11] = 0x10; +// auto addr = snes.cpu->_getProgramCounterRelativeLongAddr(); +// cr_assert_eq(addr, 0x809025, "Returned address was %x but was expecting 0x809025.", addr); +// cr_assert_eq(snes.cpu->_registers.pac, 0x808012); +//} +// +//Test(AddrMode, ProgramCounterRelativeLongNegative) +//{ +// Init() +// snes.cpu->_registers.pac = 0x808010; +// snes.cartridge->_data[0x10] = 0x10; +// snes.cartridge->_data[0x11] = -0x15; +// auto addr = snes.cpu->_getProgramCounterRelativeLongAddr(); +// cr_assert_eq(addr, 0x806B00, "Returned address was %x but was expecting 0x806B00.", addr); +// cr_assert_eq(snes.cpu->_registers.pac, 0x808012); +//} Test(AddrMode, AbsoluteIndirect) { diff --git a/ui/ui_cpu.h b/ui/ui_cpu.h new file mode 100644 index 0000000..50bfe74 --- /dev/null +++ b/ui/ui_cpu.h @@ -0,0 +1,306 @@ +/******************************************************************************** +** Form generated from reading UI file 'cpu.ui' +** +** Created by: Qt User Interface Compiler version 5.14.1 +** +** WARNING! All changes made in this file will be lost when recompiling UI file! +********************************************************************************/ + +#ifndef UI_CPU_H +#define UI_CPU_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Ui_CPUView +{ +public: + QAction *actionPause; + QAction *actionStep; + QAction *actionNext; + QWidget *centralwidget; + QGridLayout *gridLayout_2; + QTableView *disassembly; + QFormLayout *formLayout; + QLabel *accumulatorLabel; + QLineEdit *accumulatorLineEdit; + QLabel *programBankRegisterLabel; + QLineEdit *programBankRegisterLineEdit; + QLabel *programCounterLabel; + QLineEdit *programCounterLineEdit; + QLabel *directBankLabel; + QLineEdit *directBankLineEdit; + QLabel *directPageLabel; + QLineEdit *directPageLineEdit; + QLabel *stackPointerLabel; + QLineEdit *stackPointerLineEdit; + QLabel *xIndexLabel; + QLineEdit *xIndexLineEdit; + QLabel *yIndexLabel; + QLineEdit *yIndexLineEdit; + QLabel *flagsLabel; + QLineEdit *flagsLineEdit; + QLabel *emulationModeLabel; + QCheckBox *emulationModeCheckBox; + QGridLayout *gridLayout; + QPushButton *clear; + QLabel *loggerLabel; + QTextBrowser *logger; + QToolBar *toolBar; + + void setupUi(QMainWindow *CPUView) + { + if (CPUView->objectName().isEmpty()) + CPUView->setObjectName(QString::fromUtf8("CPUView")); + CPUView->resize(971, 709); + QIcon icon; + icon.addFile(QString::fromUtf8(":/resources/Logo.png"), QSize(), QIcon::Normal, QIcon::Off); + CPUView->setWindowIcon(icon); + CPUView->setAutoFillBackground(false); + actionPause = new QAction(CPUView); + actionPause->setObjectName(QString::fromUtf8("actionPause")); + QIcon icon1; + icon1.addFile(QString::fromUtf8(":/resources/icons/play.svg"), QSize(), QIcon::Normal, QIcon::Off); + actionPause->setIcon(icon1); + actionStep = new QAction(CPUView); + actionStep->setObjectName(QString::fromUtf8("actionStep")); + QIcon icon2; + icon2.addFile(QString::fromUtf8(":/resources/icons/step.svg"), QSize(), QIcon::Normal, QIcon::Off); + actionStep->setIcon(icon2); + actionNext = new QAction(CPUView); + actionNext->setObjectName(QString::fromUtf8("actionNext")); + QIcon icon3; + icon3.addFile(QString::fromUtf8(":/resources/icons/continue.svg"), QSize(), QIcon::Normal, QIcon::Off); + actionNext->setIcon(icon3); + centralwidget = new QWidget(CPUView); + centralwidget->setObjectName(QString::fromUtf8("centralwidget")); + gridLayout_2 = new QGridLayout(centralwidget); + gridLayout_2->setObjectName(QString::fromUtf8("gridLayout_2")); + disassembly = new QTableView(centralwidget); + disassembly->setObjectName(QString::fromUtf8("disassembly")); + QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + sizePolicy.setHorizontalStretch(1); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(disassembly->sizePolicy().hasHeightForWidth()); + disassembly->setSizePolicy(sizePolicy); + disassembly->setStyleSheet(QString::fromUtf8("")); + disassembly->setSelectionMode(QAbstractItemView::ExtendedSelection); + disassembly->setSelectionBehavior(QAbstractItemView::SelectRows); + disassembly->setShowGrid(false); + disassembly->setGridStyle(Qt::NoPen); + disassembly->horizontalHeader()->setVisible(false); + disassembly->horizontalHeader()->setHighlightSections(false); + + gridLayout_2->addWidget(disassembly, 0, 0, 2, 1); + + formLayout = new QFormLayout(); + formLayout->setObjectName(QString::fromUtf8("formLayout")); + accumulatorLabel = new QLabel(centralwidget); + accumulatorLabel->setObjectName(QString::fromUtf8("accumulatorLabel")); + + formLayout->setWidget(0, QFormLayout::LabelRole, accumulatorLabel); + + accumulatorLineEdit = new QLineEdit(centralwidget); + accumulatorLineEdit->setObjectName(QString::fromUtf8("accumulatorLineEdit")); + + formLayout->setWidget(0, QFormLayout::FieldRole, accumulatorLineEdit); + + programBankRegisterLabel = new QLabel(centralwidget); + programBankRegisterLabel->setObjectName(QString::fromUtf8("programBankRegisterLabel")); + + formLayout->setWidget(1, QFormLayout::LabelRole, programBankRegisterLabel); + + programBankRegisterLineEdit = new QLineEdit(centralwidget); + programBankRegisterLineEdit->setObjectName(QString::fromUtf8("programBankRegisterLineEdit")); + + formLayout->setWidget(1, QFormLayout::FieldRole, programBankRegisterLineEdit); + + programCounterLabel = new QLabel(centralwidget); + programCounterLabel->setObjectName(QString::fromUtf8("programCounterLabel")); + + formLayout->setWidget(2, QFormLayout::LabelRole, programCounterLabel); + + programCounterLineEdit = new QLineEdit(centralwidget); + programCounterLineEdit->setObjectName(QString::fromUtf8("programCounterLineEdit")); + + formLayout->setWidget(2, QFormLayout::FieldRole, programCounterLineEdit); + + directBankLabel = new QLabel(centralwidget); + directBankLabel->setObjectName(QString::fromUtf8("directBankLabel")); + + formLayout->setWidget(3, QFormLayout::LabelRole, directBankLabel); + + directBankLineEdit = new QLineEdit(centralwidget); + directBankLineEdit->setObjectName(QString::fromUtf8("directBankLineEdit")); + + formLayout->setWidget(3, QFormLayout::FieldRole, directBankLineEdit); + + directPageLabel = new QLabel(centralwidget); + directPageLabel->setObjectName(QString::fromUtf8("directPageLabel")); + + formLayout->setWidget(4, QFormLayout::LabelRole, directPageLabel); + + directPageLineEdit = new QLineEdit(centralwidget); + directPageLineEdit->setObjectName(QString::fromUtf8("directPageLineEdit")); + + formLayout->setWidget(4, QFormLayout::FieldRole, directPageLineEdit); + + stackPointerLabel = new QLabel(centralwidget); + stackPointerLabel->setObjectName(QString::fromUtf8("stackPointerLabel")); + + formLayout->setWidget(5, QFormLayout::LabelRole, stackPointerLabel); + + stackPointerLineEdit = new QLineEdit(centralwidget); + stackPointerLineEdit->setObjectName(QString::fromUtf8("stackPointerLineEdit")); + + formLayout->setWidget(5, QFormLayout::FieldRole, stackPointerLineEdit); + + xIndexLabel = new QLabel(centralwidget); + xIndexLabel->setObjectName(QString::fromUtf8("xIndexLabel")); + + formLayout->setWidget(6, QFormLayout::LabelRole, xIndexLabel); + + xIndexLineEdit = new QLineEdit(centralwidget); + xIndexLineEdit->setObjectName(QString::fromUtf8("xIndexLineEdit")); + + formLayout->setWidget(6, QFormLayout::FieldRole, xIndexLineEdit); + + yIndexLabel = new QLabel(centralwidget); + yIndexLabel->setObjectName(QString::fromUtf8("yIndexLabel")); + + formLayout->setWidget(7, QFormLayout::LabelRole, yIndexLabel); + + yIndexLineEdit = new QLineEdit(centralwidget); + yIndexLineEdit->setObjectName(QString::fromUtf8("yIndexLineEdit")); + + formLayout->setWidget(7, QFormLayout::FieldRole, yIndexLineEdit); + + flagsLabel = new QLabel(centralwidget); + flagsLabel->setObjectName(QString::fromUtf8("flagsLabel")); + + formLayout->setWidget(8, QFormLayout::LabelRole, flagsLabel); + + flagsLineEdit = new QLineEdit(centralwidget); + flagsLineEdit->setObjectName(QString::fromUtf8("flagsLineEdit")); + + formLayout->setWidget(8, QFormLayout::FieldRole, flagsLineEdit); + + emulationModeLabel = new QLabel(centralwidget); + emulationModeLabel->setObjectName(QString::fromUtf8("emulationModeLabel")); + + formLayout->setWidget(9, QFormLayout::LabelRole, emulationModeLabel); + + emulationModeCheckBox = new QCheckBox(centralwidget); + emulationModeCheckBox->setObjectName(QString::fromUtf8("emulationModeCheckBox")); + emulationModeCheckBox->setLayoutDirection(Qt::RightToLeft); + emulationModeCheckBox->setCheckable(true); + + formLayout->setWidget(9, QFormLayout::FieldRole, emulationModeCheckBox); + + + gridLayout_2->addLayout(formLayout, 0, 1, 1, 1); + + gridLayout = new QGridLayout(); + gridLayout->setObjectName(QString::fromUtf8("gridLayout")); + clear = new QPushButton(centralwidget); + clear->setObjectName(QString::fromUtf8("clear")); + + gridLayout->addWidget(clear, 2, 0, 1, 1); + + loggerLabel = new QLabel(centralwidget); + loggerLabel->setObjectName(QString::fromUtf8("loggerLabel")); + loggerLabel->setAlignment(Qt::AlignCenter); + + gridLayout->addWidget(loggerLabel, 0, 0, 1, 1); + + logger = new QTextBrowser(centralwidget); + logger->setObjectName(QString::fromUtf8("logger")); + + gridLayout->addWidget(logger, 1, 0, 1, 1); + + + gridLayout_2->addLayout(gridLayout, 1, 1, 1, 1); + + CPUView->setCentralWidget(centralwidget); + toolBar = new QToolBar(CPUView); + toolBar->setObjectName(QString::fromUtf8("toolBar")); + toolBar->setMinimumSize(QSize(0, 0)); + toolBar->setMovable(false); + toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + toolBar->setFloatable(true); + CPUView->addToolBar(Qt::TopToolBarArea, toolBar); + + toolBar->addAction(actionPause); + toolBar->addAction(actionNext); + toolBar->addAction(actionStep); + + retranslateUi(CPUView); + + QMetaObject::connectSlotsByName(CPUView); + } // setupUi + + void retranslateUi(QMainWindow *CPUView) + { + CPUView->setWindowTitle(QCoreApplication::translate("CPUView", "CPU's Debugger", nullptr)); + actionPause->setText(QCoreApplication::translate("CPUView", "Continue", nullptr)); +#if QT_CONFIG(tooltip) + actionPause->setToolTip(QCoreApplication::translate("CPUView", "Pause or Resume instruction execution.", nullptr)); +#endif // QT_CONFIG(tooltip) +#if QT_CONFIG(shortcut) + actionPause->setShortcut(QCoreApplication::translate("CPUView", "C", nullptr)); +#endif // QT_CONFIG(shortcut) + actionStep->setText(QCoreApplication::translate("CPUView", "Step", nullptr)); +#if QT_CONFIG(tooltip) + actionStep->setToolTip(QCoreApplication::translate("CPUView", "Execute a single instruction", nullptr)); +#endif // QT_CONFIG(tooltip) +#if QT_CONFIG(shortcut) + actionStep->setShortcut(QCoreApplication::translate("CPUView", "S", nullptr)); +#endif // QT_CONFIG(shortcut) + actionNext->setText(QCoreApplication::translate("CPUView", "Next", nullptr)); +#if QT_CONFIG(tooltip) + actionNext->setToolTip(QCoreApplication::translate("CPUView", "Continue execution to the next line.", nullptr)); +#endif // QT_CONFIG(tooltip) +#if QT_CONFIG(shortcut) + actionNext->setShortcut(QCoreApplication::translate("CPUView", "N", nullptr)); +#endif // QT_CONFIG(shortcut) + accumulatorLabel->setText(QCoreApplication::translate("CPUView", "Accumulator", nullptr)); + accumulatorLineEdit->setText(QString()); + programBankRegisterLabel->setText(QCoreApplication::translate("CPUView", "Program Bank", nullptr)); + programCounterLabel->setText(QCoreApplication::translate("CPUView", "Program Counter", nullptr)); + directBankLabel->setText(QCoreApplication::translate("CPUView", "Direct Bank", nullptr)); + directPageLabel->setText(QCoreApplication::translate("CPUView", "Direct Page", nullptr)); + stackPointerLabel->setText(QCoreApplication::translate("CPUView", "Stack Pointer", nullptr)); + xIndexLabel->setText(QCoreApplication::translate("CPUView", "X Index", nullptr)); + yIndexLabel->setText(QCoreApplication::translate("CPUView", "Y Index", nullptr)); + flagsLabel->setText(QCoreApplication::translate("CPUView", "Flags", nullptr)); + emulationModeLabel->setText(QCoreApplication::translate("CPUView", "Emulation mode", nullptr)); + clear->setText(QCoreApplication::translate("CPUView", "Clear History", nullptr)); + loggerLabel->setText(QCoreApplication::translate("CPUView", "Instructions History", nullptr)); + toolBar->setWindowTitle(QCoreApplication::translate("CPUView", "toolBar", nullptr)); + } // retranslateUi + +}; + +namespace Ui { + class CPUView: public Ui_CPUView {}; +} // namespace Ui + +QT_END_NAMESPACE + +#endif // UI_CPU_H From 9f8ae6cb087439bf83cca922768cb988dda9827e Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Fri, 3 Apr 2020 15:14:19 +0200 Subject: [PATCH 04/26] Implementing DEX & DEY --- sources/CPU/CPU.hpp | 8 +++-- .../Instructions/MathematicalOperations.cpp | 34 ++++++++++++++++--- tests/CPU/Math/testADC.cpp | 6 ++-- tests/CPU/Math/testSBC.cpp | 2 +- 4 files changed, 39 insertions(+), 11 deletions(-) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index 6c60e09..96930b9 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -374,6 +374,10 @@ namespace ComSquare::CPU int JML(uint24_t valueAddr, AddressingMode); //! @brief No OP. int NOP(uint24_t, AddressingMode); + //! @brief Decrement the X register. + int DEX(uint24_t, AddressingMode); + //! @brief Decrement the Y register. + int DEY(uint24_t, AddressingMode); //! @brief All the instructions of the CPU. //! @info Instructions are indexed by their opcode @@ -514,7 +518,7 @@ namespace ComSquare::CPU {&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::DEY, 2, "dey", AddressingMode::Implied, 1}, // 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 @@ -580,7 +584,7 @@ namespace ComSquare::CPU {&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::DEX, 2, "dex", AddressingMode::Implied, 1}, // 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 diff --git a/sources/CPU/Instructions/MathematicalOperations.cpp b/sources/CPU/Instructions/MathematicalOperations.cpp index 5b6e9e1..2b16056 100644 --- a/sources/CPU/Instructions/MathematicalOperations.cpp +++ b/sources/CPU/Instructions/MathematicalOperations.cpp @@ -12,8 +12,8 @@ namespace ComSquare::CPU unsigned value = this->_bus->read(valueAddr) + this->_registers.p.c; if (!this->_registers.p.m) value += this->_bus->read(valueAddr + 1) << 8u; - unsigned negativeMask = this->_isEmulationMode ? 0x80u : 0x8000u; - unsigned maxValue = this->_isEmulationMode ? UINT8_MAX : UINT16_MAX; + unsigned negativeMask = this->_registers.p.m ? 0x80u : 0x8000u; + unsigned maxValue = this->_registers.p.m ? UINT8_MAX : UINT16_MAX; this->_registers.p.c = static_cast(this->_registers.a) + value > maxValue; if ((this->_registers.a & negativeMask) == (value & negativeMask)) @@ -21,7 +21,7 @@ namespace ComSquare::CPU else this->_registers.p.v = false; this->_registers.a += value; - if (this->_isEmulationMode) + if (this->_registers.p.m) this->_registers.a %= 0x100; this->_registers.p.z = this->_registers.a == 0; this->_registers.p.n = this->_registers.a & negativeMask; @@ -51,7 +51,7 @@ namespace ComSquare::CPU int CPU::SBC(uint24_t valueAddr, AddressingMode mode) { - unsigned negativeMask = this->_isEmulationMode ? 0x80u : 0x8000u; + unsigned negativeMask = this->_registers.p.m ? 0x80u : 0x8000u; unsigned value = this->_bus->read(valueAddr); if (!this->_registers.p.m) value += this->_bus->read(valueAddr + 1) << 8u; @@ -63,7 +63,7 @@ namespace ComSquare::CPU else this->_registers.p.v = false; this->_registers.a += ~value + oldCarry; - if (this->_isEmulationMode) + if (this->_registers.p.m) this->_registers.a %= 0x100; this->_registers.p.z = this->_registers.a == 0; this->_registers.p.n = this->_registers.a & negativeMask; @@ -90,4 +90,28 @@ namespace ComSquare::CPU } return cycles; } + + int CPU::DEX(uint24_t, AddressingMode) + { + unsigned negativeMask = this->_registers.p.x_b ? UINT8_MAX : UINT16_MAX; + + this->_registers.x--; + if (this->_registers.p.x_b) + this->_registers.xh = 0; + this->_registers.p.z = this->_registers.x == 0; + this->_registers.p.n = this->_registers.x & negativeMask; + return 0; + } + + int CPU::DEY(uint24_t, AddressingMode) + { + unsigned negativeMask = this->_registers.p.x_b ? UINT8_MAX : UINT16_MAX; + + this->_registers.y--; + if (this->_registers.p.x_b) + this->_registers.yh = 0; + this->_registers.p.z = this->_registers.y == 0; + this->_registers.p.n = this->_registers.y & negativeMask; + return 0; + } } \ No newline at end of file diff --git a/tests/CPU/Math/testADC.cpp b/tests/CPU/Math/testADC.cpp index 00a5106..3d4f193 100644 --- a/tests/CPU/Math/testADC.cpp +++ b/tests/CPU/Math/testADC.cpp @@ -67,7 +67,7 @@ Test(ADC, overflowEmulation) Test(ADC, signedOverflow) { Init() - snes.cpu->_isEmulationMode = false; + snes.cpu->_registers.p.m = false; snes.cpu->_registers.a = 0x7FFF; snes.wram->_data[0] = 0x1; snes.cpu->ADC(0x0, ComSquare::CPU::AddressingMode::Implied); @@ -81,7 +81,7 @@ Test(ADC, signedOverflow) Test(ADC, signedOverflowEmulated) { Init() - snes.cpu->_isEmulationMode = true; + snes.cpu->_registers.p.m = true; snes.cpu->_registers.a = 0x007F; snes.wram->_data[0] = 0x1; snes.cpu->ADC(0x0, ComSquare::CPU::AddressingMode::Implied); @@ -95,7 +95,7 @@ Test(ADC, signedOverflowEmulated) Test(ADC, negative) { Init() - snes.cpu->_isEmulationMode = false; + snes.cpu->_registers.p.m = false; snes.cpu->_registers.a = 0x8FFF; snes.wram->_data[0] = 0x1; snes.cpu->ADC(0x0, ComSquare::CPU::AddressingMode::Implied); diff --git a/tests/CPU/Math/testSBC.cpp b/tests/CPU/Math/testSBC.cpp index 52b0186..e784481 100644 --- a/tests/CPU/Math/testSBC.cpp +++ b/tests/CPU/Math/testSBC.cpp @@ -62,7 +62,7 @@ Test(SBC, overflowEmulation) Init() snes.cpu->_isEmulationMode = true; snes.cpu->_registers.a = 0x1; - snes.cpu->_registers.p.m = false; + snes.cpu->_registers.p.m = true; snes.cpu->_registers.p.c = false; snes.wram->_data[0] = 0x02; snes.cpu->SBC(0x0, ComSquare::CPU::AddressingMode::Implied); From 8e7d28dc4dca23b94ab30196cef390f6da5e3bfa Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Fri, 3 Apr 2020 15:24:47 +0200 Subject: [PATCH 05/26] Testing DEX & DEY --- CMakeLists.txt | 2 +- .../Instructions/MathematicalOperations.cpp | 4 +- tests/CPU/Math/testOthersMath.cpp | 119 ++++++++++++++++++ 3 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 tests/CPU/Math/testOthersMath.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f38620..068a71a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,7 +95,7 @@ add_executable(unit_tests sources/Models/Components.hpp sources/CPU/Instruction.hpp sources/Exceptions/DebuggableError.hpp -) + tests/CPU/Math/testOthersMath.cpp) # include criterion & coverage target_link_libraries(unit_tests criterion -lgcov) diff --git a/sources/CPU/Instructions/MathematicalOperations.cpp b/sources/CPU/Instructions/MathematicalOperations.cpp index 2b16056..54fa345 100644 --- a/sources/CPU/Instructions/MathematicalOperations.cpp +++ b/sources/CPU/Instructions/MathematicalOperations.cpp @@ -93,7 +93,7 @@ namespace ComSquare::CPU int CPU::DEX(uint24_t, AddressingMode) { - unsigned negativeMask = this->_registers.p.x_b ? UINT8_MAX : UINT16_MAX; + unsigned negativeMask = this->_registers.p.x_b ? 0x80 : 0x8000; this->_registers.x--; if (this->_registers.p.x_b) @@ -105,7 +105,7 @@ namespace ComSquare::CPU int CPU::DEY(uint24_t, AddressingMode) { - unsigned negativeMask = this->_registers.p.x_b ? UINT8_MAX : UINT16_MAX; + unsigned negativeMask = this->_registers.p.x_b ? 0x80 : 0x8000; this->_registers.y--; if (this->_registers.p.x_b) diff --git a/tests/CPU/Math/testOthersMath.cpp b/tests/CPU/Math/testOthersMath.cpp new file mode 100644 index 0000000..1d8ed65 --- /dev/null +++ b/tests/CPU/Math/testOthersMath.cpp @@ -0,0 +1,119 @@ +// +// Created by anonymus-raccoon on 4/3/20. +// + +#include +#include +#include "../../tests.hpp" +#include "../../../sources/SNES.hpp" +using namespace ComSquare; + +Test(DEX, simple) +{ + Init() + snes.cpu->_registers.p.x_b = true; + snes.cpu->_registers.x = 0x57; + snes.cpu->DEX(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.x, 0x56, "The x index value should be 0x56 but it was 0x%x.", snes.cpu->_registers.x); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flags should not be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(DEX, overflowEmul) +{ + Init() + snes.cpu->_registers.p.x_b = true; + snes.cpu->_registers.x = 0; + snes.cpu->DEX(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.x, 0xFF, "The x index value should be 0xFF but it was 0x%x.", snes.cpu->_registers.x); + cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flags should be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(DEX, fakeOverflowNonEmul) +{ + Init() + snes.cpu->_registers.p.x_b = false; + snes.cpu->_registers.x = 0xFF00; + snes.cpu->DEX(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.x, 0xFEFF, "The x index value should be 0xFEFF but it was 0x%x.", snes.cpu->_registers.x); + cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flags should be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(DEX, nonNegative) +{ + Init() + snes.cpu->_registers.p.x_b = true; + snes.cpu->_registers.x = 0x80; + snes.cpu->DEX(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.x, 0x7F, "The x index value should be 0x7F but it was 0x%x.", snes.cpu->_registers.x); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flags should not be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(DEX, zero) +{ + Init() + snes.cpu->_registers.p.x_b = true; + snes.cpu->_registers.x = 1; + snes.cpu->DEX(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.x, 0, "The x index value should be 0 but it was 0x%x.", snes.cpu->_registers.x); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flags should not be set."); + cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flags should be set."); +} + +Test(DEY, simple) +{ + Init() + snes.cpu->_registers.p.x_b = true; + snes.cpu->_registers.y = 0x57; + snes.cpu->DEY(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.y, 0x56, "The x index value should be 0x56 but it was 0x%x.", snes.cpu->_registers.y); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flags should not be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(DEY, overflowEmul) +{ + Init() + snes.cpu->_registers.p.x_b = true; + snes.cpu->_registers.y = 0; + snes.cpu->DEY(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.y, 0xFF, "The x index value should be 0xFF but it was 0x%x.", snes.cpu->_registers.y); + cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flags should be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(DEY, fakeOverflowNonEmul) +{ + Init() + snes.cpu->_registers.p.x_b = false; + snes.cpu->_registers.y = 0xFF00; + snes.cpu->DEY(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.y, 0xFEFF, "The x index value should be 0xFEFF but it was 0x%x.", snes.cpu->_registers.y); + cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flags should be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(DEY, nonNegative) +{ + Init() + snes.cpu->_registers.p.x_b = true; + snes.cpu->_registers.y = 0x80; + snes.cpu->DEY(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.y, 0x7F, "The x index value should be 0x7F but it was 0x%x.", snes.cpu->_registers.y); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flags should not be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(DEY, zero) +{ + Init() + snes.cpu->_registers.p.x_b = true; + snes.cpu->_registers.y = 1; + snes.cpu->DEY(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.y, 0, "The x index value should be 0 but it was 0x%x.", snes.cpu->_registers.y); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flags should not be set."); + cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flags should be set."); +} \ No newline at end of file From d73d570139892678e6e64fa7767732e136bd0d31 Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Fri, 3 Apr 2020 15:51:35 +0200 Subject: [PATCH 06/26] Implementing the ORA --- sources/CPU/CPU.hpp | 32 +++++++++--------- .../Instructions/MathematicalOperations.cpp | 33 +++++++++++++++++++ 2 files changed, 50 insertions(+), 15 deletions(-) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index 96930b9..ec96690 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -378,42 +378,44 @@ namespace ComSquare::CPU int DEX(uint24_t, AddressingMode); //! @brief Decrement the Y register. int DEY(uint24_t, AddressingMode); + //! @brief Or accumulator with memory. + int ORA(uint24_t valueAddr, AddressingMode mode); //! @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::ORA, 6, "ora", AddressingMode::DirectPageIndirectIndexedByX, 2}, // 01 {&CPU::COP, 7, "cop", AddressingMode::Implied, 2}, // 02 - {&CPU::BRK, 7, "ora #-#", AddressingMode::Implied, 2}, // 03 + {&CPU::ORA, 4, "ora", AddressingMode::StackRelative, 2}, // 03 {&CPU::BRK, 7, "tsb #-#", AddressingMode::Implied, 2}, // 04 - {&CPU::BRK, 7, "ora #-#", AddressingMode::Implied, 2}, // 05 + {&CPU::ORA, 3, "ora", AddressingMode::DirectPage, 2}, // 05 {&CPU::BRK, 7, "asl #-#", AddressingMode::Implied, 2}, // 06 - {&CPU::BRK, 7, "ora #-#", AddressingMode::Implied, 2}, // 07 + {&CPU::ORA, 6, "ora", AddressingMode::DirectPageIndirectLong, 2}, // 07 {&CPU::PHP, 3, "php", AddressingMode::Implied, 3}, // 08 - {&CPU::BRK, 7, "ora #-#", AddressingMode::Implied, 2}, // 09 + {&CPU::ORA, 2, "ora", AddressingMode::ImmediateForA, 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::ORA, 3, "ora", AddressingMode::Absolute, 4}, // 0D {&CPU::BRK, 7, "asl #-#", AddressingMode::Implied, 2}, // 0E - {&CPU::BRK, 7, "ora #-#", AddressingMode::Implied, 2}, // 0F + {&CPU::ORA, 5, "ora", AddressingMode::AbsoluteLong, 5}, // 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::ORA, 5, "ora", AddressingMode::DirectPageIndirectIndexedByY, 2}, // 11 + {&CPU::ORA, 5, "ora", AddressingMode::DirectPageIndirect, 2}, // 12 + {&CPU::ORA, 7, "ora", AddressingMode::StackRelativeIndirectIndexedByY, 2}, // 13 {&CPU::BRK, 7, "trb #-#", AddressingMode::Implied, 2}, // 14 - {&CPU::BRK, 7, "ora #-#", AddressingMode::Implied, 2}, // 15 + {&CPU::ORA, 4, "ora", AddressingMode::DirectPageIndexedByX, 2}, // 15 {&CPU::BRK, 7, "asl #-#", AddressingMode::Implied, 2}, // 16 - {&CPU::BRK, 7, "ora #-#", AddressingMode::Implied, 2}, // 17 + {&CPU::ORA, 6, "ora", AddressingMode::DirectPageIndirectIndexedByYLong, 2}, // 17 {&CPU::CLC, 2, "clc", AddressingMode::Implied, 1}, // 18 - {&CPU::BRK, 7, "ora #-#", AddressingMode::Implied, 2}, // 19 + {&CPU::ORA, 4, "ora", AddressingMode::AbsoluteIndexedByY, 3}, // 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::ORA, 4, "ora", AddressingMode::AbsoluteIndexedByX, 3}, // 1D {&CPU::BRK, 7, "asl #-#", AddressingMode::Implied, 2}, // 1E - {&CPU::BRK, 7, "ora #-#", AddressingMode::Implied, 2}, // 1F + {&CPU::ORA, 5, "ora", AddressingMode::AbsoluteIndexedByXLong, 4}, // 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 diff --git a/sources/CPU/Instructions/MathematicalOperations.cpp b/sources/CPU/Instructions/MathematicalOperations.cpp index 54fa345..4872cbb 100644 --- a/sources/CPU/Instructions/MathematicalOperations.cpp +++ b/sources/CPU/Instructions/MathematicalOperations.cpp @@ -91,6 +91,39 @@ namespace ComSquare::CPU return cycles; } + int CPU::ORA(uint24_t valueAddr, AddressingMode mode) + { + unsigned negativeMask = this->_registers.p.m ? 0x80u : 0x8000u; + unsigned value = this->_bus->read(valueAddr); + if (!this->_registers.p.m) + value += this->_bus->read(valueAddr + 1) << 8u; + this->_registers.a |= value; + 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; + } + int CPU::DEX(uint24_t, AddressingMode) { unsigned negativeMask = this->_registers.p.x_b ? 0x80 : 0x8000; From 21724fd18982f90764e57b05fcac731fbd57e7e7 Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Fri, 3 Apr 2020 15:54:55 +0200 Subject: [PATCH 07/26] Testing the ORA --- tests/CPU/Math/testOthersMath.cpp | 36 +++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/CPU/Math/testOthersMath.cpp b/tests/CPU/Math/testOthersMath.cpp index 1d8ed65..3a65f92 100644 --- a/tests/CPU/Math/testOthersMath.cpp +++ b/tests/CPU/Math/testOthersMath.cpp @@ -116,4 +116,40 @@ Test(DEY, zero) cr_assert_eq(snes.cpu->_registers.y, 0, "The x index value should be 0 but it was 0x%x.", snes.cpu->_registers.y); cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flags should not be set."); cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flags should be set."); +} + +Test(ORA, simple) +{ + Init() + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.a = 0x80; + snes.wram->_data[0] = 0x0F; + snes.cpu->ORA(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0x8F, "The accumulator's value should be 0x8F but it was 0x%x.", snes.cpu->_registers.a); + cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flags should be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(ORA, simple2) +{ + Init() + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.a = 0x80; + snes.wram->_data[0] = 0xF0; + snes.cpu->ORA(0x00, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0xF0, "The accumulator's value should be 0xF0 but it was 0x%x.", snes.cpu->_registers.a); + cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flags should be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(ORA, zero) +{ + Init() + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.a = 0x00; + snes.wram->_data[0] = 0x00; + snes.cpu->ORA(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0x00, "The accumulator's value should be 0x00 but it was 0x%x.", snes.cpu->_registers.a); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flags should not be set."); + cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flags should be set."); } \ No newline at end of file From 6bd920b65be97b78408169ce7c4eae5c0f25d7e7 Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Fri, 3 Apr 2020 17:37:57 +0200 Subject: [PATCH 08/26] Reworking a bit the UI to add room for a stack viewer --- sources/Debugger/CPU/CPUDebug.cpp | 10 +- ui/cpu.ui | 184 ++++++++++++++++++++++++++--- ui/ui_cpu.h | 189 ++++++++++++++++++++++++++---- 3 files changed, 340 insertions(+), 43 deletions(-) diff --git a/sources/Debugger/CPU/CPUDebug.cpp b/sources/Debugger/CPU/CPUDebug.cpp index 098c434..4a7f544 100644 --- a/sources/Debugger/CPU/CPUDebug.cpp +++ b/sources/Debugger/CPU/CPUDebug.cpp @@ -165,8 +165,16 @@ namespace ComSquare::Debugger this->_ui.xIndexLineEdit->setText(Utility::to_hex(this->_registers.xl).c_str()); this->_ui.yIndexLineEdit->setText(Utility::to_hex(this->_registers.yl).c_str()); } - this->_ui.flagsLineEdit->setText(this->_getFlagsString().c_str()); this->_ui.emulationModeCheckBox->setCheckState(this->_isEmulationMode ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + + this->_ui.mCheckbox->setCheckState(this->_registers.p.m ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + this->_ui.xCheckbox->setCheckState(this->_registers.p.x_b ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + this->_ui.iCheckbox->setCheckState(this->_registers.p.i ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + this->_ui.vCheckbox->setCheckState(this->_registers.p.v ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + this->_ui.dCheckbox->setCheckState(this->_registers.p.d ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + this->_ui.cCheckbox->setCheckState(this->_registers.p.c ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + this->_ui.zCheckbox->setCheckState(this->_registers.p.z ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + this->_ui.nCheckbox->setCheckState(this->_registers.p.n ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); } std::string CPUDebug::_getFlagsString() diff --git a/ui/cpu.ui b/ui/cpu.ui index bbaf550..1373c20 100644 --- a/ui/cpu.ui +++ b/ui/cpu.ui @@ -7,7 +7,7 @@ 0 0 971 - 709 + 673 @@ -21,7 +21,7 @@ false - + @@ -54,6 +54,23 @@ + + + + + + + + Stack Viewer + + + Qt::AlignCenter + + + + + + @@ -140,23 +157,13 @@ - - - Flags - - - - - - - Emulation mode - + Qt::RightToLeft @@ -170,6 +177,9 @@ + + + @@ -187,11 +197,153 @@ - - - + + + + Flags + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + false + + + false + + + + QLayout::SetDefaultConstraint + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + 26 + + + 7 + + + + + Memory/Accumulator Select (M) + + + + + + + Qt::RightToLeft + + + + + + + Index Select (X) / Break (B) + + + + + + + Qt::RightToLeft + + + + + + + Interupt Request Disable (I) + + + + + + + Qt::RightToLeft + + + + + + + Overflow (V) + + + + + + + Qt::RightToLeft + + + + + + + Decimal (D) + + + + + + + Qt::RightToLeft + + + + + + + Carry (C) + + + + + + + Qt::RightToLeft + + + + + + + Zero (Z) + + + + + + + Negative (N) + + + + + + + Qt::RightToLeft + + + + + + + Qt::RightToLeft + + + + + + diff --git a/ui/ui_cpu.h b/ui/ui_cpu.h index 50bfe74..13d1ef0 100644 --- a/ui/ui_cpu.h +++ b/ui/ui_cpu.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -35,8 +36,11 @@ public: QAction *actionStep; QAction *actionNext; QWidget *centralwidget; - QGridLayout *gridLayout_2; + QGridLayout *gridLayout_3; QTableView *disassembly; + QGridLayout *gridLayout_2; + QTableView *stackView; + QLabel *label; QFormLayout *formLayout; QLabel *accumulatorLabel; QLineEdit *accumulatorLineEdit; @@ -54,21 +58,37 @@ public: QLineEdit *xIndexLineEdit; QLabel *yIndexLabel; QLineEdit *yIndexLineEdit; - QLabel *flagsLabel; - QLineEdit *flagsLineEdit; QLabel *emulationModeLabel; QCheckBox *emulationModeCheckBox; QGridLayout *gridLayout; + QTextBrowser *logger; QPushButton *clear; QLabel *loggerLabel; - QTextBrowser *logger; + QGroupBox *formGroupBox; + QFormLayout *formLayout_2; + QLabel *negativeLabel; + QCheckBox *mCheckbox; + QLabel *zeroLabel; + QCheckBox *xCheckbox; + QLabel *carryLabel; + QCheckBox *iCheckbox; + QLabel *Overflow; + QCheckBox *vCheckbox; + QLabel *decimalLabel; + QCheckBox *dCheckbox; + QLabel *memoryAccumulatorSelectLabel; + QCheckBox *cCheckbox; + QLabel *indeXSelectLabel; + QLabel *irqDisableLabel; + QCheckBox *nCheckbox; + QCheckBox *zCheckbox; QToolBar *toolBar; void setupUi(QMainWindow *CPUView) { if (CPUView->objectName().isEmpty()) CPUView->setObjectName(QString::fromUtf8("CPUView")); - CPUView->resize(971, 709); + CPUView->resize(971, 673); QIcon icon; icon.addFile(QString::fromUtf8(":/resources/Logo.png"), QSize(), QIcon::Normal, QIcon::Off); CPUView->setWindowIcon(icon); @@ -90,8 +110,8 @@ public: actionNext->setIcon(icon3); centralwidget = new QWidget(CPUView); centralwidget->setObjectName(QString::fromUtf8("centralwidget")); - gridLayout_2 = new QGridLayout(centralwidget); - gridLayout_2->setObjectName(QString::fromUtf8("gridLayout_2")); + gridLayout_3 = new QGridLayout(centralwidget); + gridLayout_3->setObjectName(QString::fromUtf8("gridLayout_3")); disassembly = new QTableView(centralwidget); disassembly->setObjectName(QString::fromUtf8("disassembly")); QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); @@ -107,7 +127,23 @@ public: disassembly->horizontalHeader()->setVisible(false); disassembly->horizontalHeader()->setHighlightSections(false); - gridLayout_2->addWidget(disassembly, 0, 0, 2, 1); + gridLayout_3->addWidget(disassembly, 0, 0, 2, 1); + + gridLayout_2 = new QGridLayout(); + gridLayout_2->setObjectName(QString::fromUtf8("gridLayout_2")); + stackView = new QTableView(centralwidget); + stackView->setObjectName(QString::fromUtf8("stackView")); + + gridLayout_2->addWidget(stackView, 1, 0, 1, 1); + + label = new QLabel(centralwidget); + label->setObjectName(QString::fromUtf8("label")); + label->setAlignment(Qt::AlignCenter); + + gridLayout_2->addWidget(label, 0, 0, 1, 1); + + + gridLayout_3->addLayout(gridLayout_2, 0, 1, 1, 1); formLayout = new QFormLayout(); formLayout->setObjectName(QString::fromUtf8("formLayout")); @@ -191,33 +227,28 @@ public: formLayout->setWidget(7, QFormLayout::FieldRole, yIndexLineEdit); - flagsLabel = new QLabel(centralwidget); - flagsLabel->setObjectName(QString::fromUtf8("flagsLabel")); - - formLayout->setWidget(8, QFormLayout::LabelRole, flagsLabel); - - flagsLineEdit = new QLineEdit(centralwidget); - flagsLineEdit->setObjectName(QString::fromUtf8("flagsLineEdit")); - - formLayout->setWidget(8, QFormLayout::FieldRole, flagsLineEdit); - emulationModeLabel = new QLabel(centralwidget); emulationModeLabel->setObjectName(QString::fromUtf8("emulationModeLabel")); - formLayout->setWidget(9, QFormLayout::LabelRole, emulationModeLabel); + formLayout->setWidget(8, QFormLayout::LabelRole, emulationModeLabel); emulationModeCheckBox = new QCheckBox(centralwidget); emulationModeCheckBox->setObjectName(QString::fromUtf8("emulationModeCheckBox")); emulationModeCheckBox->setLayoutDirection(Qt::RightToLeft); emulationModeCheckBox->setCheckable(true); - formLayout->setWidget(9, QFormLayout::FieldRole, emulationModeCheckBox); + formLayout->setWidget(8, QFormLayout::FieldRole, emulationModeCheckBox); - gridLayout_2->addLayout(formLayout, 0, 1, 1, 1); + gridLayout_3->addLayout(formLayout, 0, 2, 1, 1); gridLayout = new QGridLayout(); gridLayout->setObjectName(QString::fromUtf8("gridLayout")); + logger = new QTextBrowser(centralwidget); + logger->setObjectName(QString::fromUtf8("logger")); + + gridLayout->addWidget(logger, 1, 0, 1, 1); + clear = new QPushButton(centralwidget); clear->setObjectName(QString::fromUtf8("clear")); @@ -229,13 +260,110 @@ public: gridLayout->addWidget(loggerLabel, 0, 0, 1, 1); - logger = new QTextBrowser(centralwidget); - logger->setObjectName(QString::fromUtf8("logger")); - gridLayout->addWidget(logger, 1, 0, 1, 1); + gridLayout_3->addLayout(gridLayout, 1, 1, 1, 1); + + formGroupBox = new QGroupBox(centralwidget); + formGroupBox->setObjectName(QString::fromUtf8("formGroupBox")); + formGroupBox->setAlignment(Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter); + formGroupBox->setFlat(false); + formGroupBox->setCheckable(false); + formLayout_2 = new QFormLayout(formGroupBox); + formLayout_2->setObjectName(QString::fromUtf8("formLayout_2")); + formLayout_2->setSizeConstraint(QLayout::SetDefaultConstraint); + formLayout_2->setLabelAlignment(Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter); + formLayout_2->setFormAlignment(Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop); + formLayout_2->setContentsMargins(26, 7, -1, -1); + negativeLabel = new QLabel(formGroupBox); + negativeLabel->setObjectName(QString::fromUtf8("negativeLabel")); + + formLayout_2->setWidget(0, QFormLayout::LabelRole, negativeLabel); + + mCheckbox = new QCheckBox(formGroupBox); + mCheckbox->setObjectName(QString::fromUtf8("mCheckbox")); + mCheckbox->setLayoutDirection(Qt::RightToLeft); + + formLayout_2->setWidget(0, QFormLayout::FieldRole, mCheckbox); + + zeroLabel = new QLabel(formGroupBox); + zeroLabel->setObjectName(QString::fromUtf8("zeroLabel")); + + formLayout_2->setWidget(1, QFormLayout::LabelRole, zeroLabel); + + xCheckbox = new QCheckBox(formGroupBox); + xCheckbox->setObjectName(QString::fromUtf8("xCheckbox")); + xCheckbox->setLayoutDirection(Qt::RightToLeft); + + formLayout_2->setWidget(1, QFormLayout::FieldRole, xCheckbox); + + carryLabel = new QLabel(formGroupBox); + carryLabel->setObjectName(QString::fromUtf8("carryLabel")); + + formLayout_2->setWidget(2, QFormLayout::LabelRole, carryLabel); + + iCheckbox = new QCheckBox(formGroupBox); + iCheckbox->setObjectName(QString::fromUtf8("iCheckbox")); + iCheckbox->setLayoutDirection(Qt::RightToLeft); + + formLayout_2->setWidget(2, QFormLayout::FieldRole, iCheckbox); + + Overflow = new QLabel(formGroupBox); + Overflow->setObjectName(QString::fromUtf8("Overflow")); + + formLayout_2->setWidget(3, QFormLayout::LabelRole, Overflow); + + vCheckbox = new QCheckBox(formGroupBox); + vCheckbox->setObjectName(QString::fromUtf8("vCheckbox")); + vCheckbox->setLayoutDirection(Qt::RightToLeft); + + formLayout_2->setWidget(3, QFormLayout::FieldRole, vCheckbox); + + decimalLabel = new QLabel(formGroupBox); + decimalLabel->setObjectName(QString::fromUtf8("decimalLabel")); + + formLayout_2->setWidget(4, QFormLayout::LabelRole, decimalLabel); + + dCheckbox = new QCheckBox(formGroupBox); + dCheckbox->setObjectName(QString::fromUtf8("dCheckbox")); + dCheckbox->setLayoutDirection(Qt::RightToLeft); + + formLayout_2->setWidget(4, QFormLayout::FieldRole, dCheckbox); + + memoryAccumulatorSelectLabel = new QLabel(formGroupBox); + memoryAccumulatorSelectLabel->setObjectName(QString::fromUtf8("memoryAccumulatorSelectLabel")); + + formLayout_2->setWidget(5, QFormLayout::LabelRole, memoryAccumulatorSelectLabel); + + cCheckbox = new QCheckBox(formGroupBox); + cCheckbox->setObjectName(QString::fromUtf8("cCheckbox")); + cCheckbox->setLayoutDirection(Qt::RightToLeft); + + formLayout_2->setWidget(5, QFormLayout::FieldRole, cCheckbox); + + indeXSelectLabel = new QLabel(formGroupBox); + indeXSelectLabel->setObjectName(QString::fromUtf8("indeXSelectLabel")); + + formLayout_2->setWidget(6, QFormLayout::LabelRole, indeXSelectLabel); + + irqDisableLabel = new QLabel(formGroupBox); + irqDisableLabel->setObjectName(QString::fromUtf8("irqDisableLabel")); + + formLayout_2->setWidget(7, QFormLayout::LabelRole, irqDisableLabel); + + nCheckbox = new QCheckBox(formGroupBox); + nCheckbox->setObjectName(QString::fromUtf8("nCheckbox")); + nCheckbox->setLayoutDirection(Qt::RightToLeft); + + formLayout_2->setWidget(7, QFormLayout::FieldRole, nCheckbox); + + zCheckbox = new QCheckBox(formGroupBox); + zCheckbox->setObjectName(QString::fromUtf8("zCheckbox")); + zCheckbox->setLayoutDirection(Qt::RightToLeft); + + formLayout_2->setWidget(6, QFormLayout::FieldRole, zCheckbox); - gridLayout_2->addLayout(gridLayout, 1, 1, 1, 1); + gridLayout_3->addWidget(formGroupBox, 1, 2, 1, 1); CPUView->setCentralWidget(centralwidget); toolBar = new QToolBar(CPUView); @@ -279,6 +407,7 @@ public: #if QT_CONFIG(shortcut) actionNext->setShortcut(QCoreApplication::translate("CPUView", "N", nullptr)); #endif // QT_CONFIG(shortcut) + label->setText(QCoreApplication::translate("CPUView", "Stack Viewer", nullptr)); accumulatorLabel->setText(QCoreApplication::translate("CPUView", "Accumulator", nullptr)); accumulatorLineEdit->setText(QString()); programBankRegisterLabel->setText(QCoreApplication::translate("CPUView", "Program Bank", nullptr)); @@ -288,10 +417,18 @@ public: stackPointerLabel->setText(QCoreApplication::translate("CPUView", "Stack Pointer", nullptr)); xIndexLabel->setText(QCoreApplication::translate("CPUView", "X Index", nullptr)); yIndexLabel->setText(QCoreApplication::translate("CPUView", "Y Index", nullptr)); - flagsLabel->setText(QCoreApplication::translate("CPUView", "Flags", nullptr)); emulationModeLabel->setText(QCoreApplication::translate("CPUView", "Emulation mode", nullptr)); clear->setText(QCoreApplication::translate("CPUView", "Clear History", nullptr)); loggerLabel->setText(QCoreApplication::translate("CPUView", "Instructions History", nullptr)); + formGroupBox->setTitle(QCoreApplication::translate("CPUView", "Flags", nullptr)); + negativeLabel->setText(QCoreApplication::translate("CPUView", "Memory/Accumulator Select (M)", nullptr)); + zeroLabel->setText(QCoreApplication::translate("CPUView", "Index Select (X) / Break (B)", nullptr)); + carryLabel->setText(QCoreApplication::translate("CPUView", "Interupt Request Disable (I)", nullptr)); + Overflow->setText(QCoreApplication::translate("CPUView", "Overflow (V)", nullptr)); + decimalLabel->setText(QCoreApplication::translate("CPUView", "Decimal (D)", nullptr)); + memoryAccumulatorSelectLabel->setText(QCoreApplication::translate("CPUView", "Carry (C)", nullptr)); + indeXSelectLabel->setText(QCoreApplication::translate("CPUView", "Zero (Z)", nullptr)); + irqDisableLabel->setText(QCoreApplication::translate("CPUView", "Negative (N)", nullptr)); toolBar->setWindowTitle(QCoreApplication::translate("CPUView", "toolBar", nullptr)); } // retranslateUi From 4ac1169dc26fa775552781619b790a3b5cea7a23 Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Fri, 3 Apr 2020 21:54:05 +0200 Subject: [PATCH 09/26] Implementing the stack viewer --- sources/Debugger/CPU/CPUDebug.cpp | 62 +++++++++++++++++++++++++++++-- sources/Debugger/CPU/CPUDebug.hpp | 29 +++++++++++++++ ui/cpu.ui | 6 ++- ui/ui_cpu.h | 1 + 4 files changed, 94 insertions(+), 4 deletions(-) diff --git a/sources/Debugger/CPU/CPUDebug.cpp b/sources/Debugger/CPU/CPUDebug.cpp index 4a7f544..9a08dd8 100644 --- a/sources/Debugger/CPU/CPUDebug.cpp +++ b/sources/Debugger/CPU/CPUDebug.cpp @@ -21,6 +21,7 @@ namespace ComSquare::Debugger _ui(), _model(*this), _painter(*this), + _stackModel(*this->_bus, *this), _snes(snes) { this->_window->setContextMenuPolicy(Qt::NoContextMenu); @@ -31,12 +32,16 @@ namespace ComSquare::Debugger this->_updateDisassembly(this->_cartridgeHeader.emulationInterrupts.reset, 0xFFFF - this->_cartridgeHeader.emulationInterrupts.reset); //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->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + this->_ui.disassembly->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); this->_ui.disassembly->verticalHeader()->setHighlightSections(false); this->_ui.disassembly->setItemDelegate(&this->_painter); + this->_ui.stackView->setModel(&this->_stackModel); + this->_ui.stackView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + this->_ui.stackView->verticalHeader()->setSectionResizeMode (QHeaderView::Fixed); + this->_ui.stackView->verticalHeader()->setHighlightSections(false); + 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); @@ -175,6 +180,9 @@ namespace ComSquare::Debugger this->_ui.cCheckbox->setCheckState(this->_registers.p.c ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); this->_ui.zCheckbox->setCheckState(this->_registers.p.z ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); this->_ui.nCheckbox->setCheckState(this->_registers.p.n ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + + auto index = this->_stackModel.index(this->_registers.s / 2, 0); + this->_ui.stackView->scrollTo(index, QAbstractItemView::PositionAtCenter); } std::string CPUDebug::_getFlagsString() @@ -247,6 +255,11 @@ namespace ComSquare::Debugger { return this->_registers.pac; } + + uint16_t CPUDebug::getStackPointer() + { + return this->_registers.s; + } } DisassemblyModel::DisassemblyModel(ComSquare::Debugger::CPUDebug &cpu) : QAbstractTableModel(), _cpu(cpu){ } @@ -321,3 +334,46 @@ QSize RowPainter::sizeHint(const QStyleOptionViewItem &, const QModelIndex &) co { return QSize(); } + +StackModel::StackModel(ComSquare::Memory::MemoryBus &bus, ComSquare::Debugger::CPUDebug &cpu) : _bus(bus), _cpu(cpu) { } + +int StackModel::rowCount(const QModelIndex &) const +{ + return 0x10000 / 2; +} + +int StackModel::columnCount(const QModelIndex &) const +{ + return 2; +} + +QVariant StackModel::data(const QModelIndex &index, int role) const +{ + if (role == Qt::BackgroundRole) { + if (index.row() * 2 + index.column() == this->_cpu.getStackPointer()) + return QColor(Qt::darkBlue); + if (index.row() * 2 + index.column() == this->_cpu.initialStackPointer) + return QColor(Qt::darkCyan); + } + if (role == Qt::TextAlignmentRole) + return Qt::AlignCenter; + if (role != Qt::DisplayRole) + return QVariant(); + uint16_t addr = index.row() * 2 + index.column(); + try { + uint8_t value = this->_bus.read(addr); + return (ComSquare::Utility::to_hex(value, ComSquare::Utility::NoPrefix).c_str()); + } catch (std::exception &) { + return "??"; + } +} + +QVariant StackModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal) + return QVariant(); + if (role != Qt::DisplayRole) + return QVariant(); + uint16_t addr = section * 2; + return QString(ComSquare::Utility::to_hex(addr, ComSquare::Utility::HexString::NoPrefix).c_str()); +} diff --git a/sources/Debugger/CPU/CPUDebug.hpp b/sources/Debugger/CPU/CPUDebug.hpp index 469ffe1..67dd93e 100644 --- a/sources/Debugger/CPU/CPUDebug.hpp +++ b/sources/Debugger/CPU/CPUDebug.hpp @@ -17,6 +17,29 @@ namespace ComSquare::Debugger class CPUDebug; } +//! @brief The qt model that show the stack. +class StackModel : public QAbstractTableModel +{ +Q_OBJECT +private: + ComSquare::Memory::MemoryBus &_bus; + ComSquare::Debugger::CPUDebug &_cpu; +public: + explicit StackModel(ComSquare::Memory::MemoryBus &bus, ComSquare::Debugger::CPUDebug &cpu); + StackModel(const StackModel &) = delete; + const StackModel &operator=(const StackModel &) = delete; + ~StackModel() 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 model that show the disassembly. class DisassemblyModel : public QAbstractTableModel { @@ -114,6 +137,8 @@ namespace ComSquare::Debugger DisassemblyModel _model; //! @brief A custom painter that highlight breakpoints and the PC's position. RowPainter _painter; + //! @brief The stack viewer's model. + StackModel _stackModel; //! @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. @@ -197,6 +222,10 @@ namespace ComSquare::Debugger std::vector breakpoints; //! @brief Return the current program counter of this CPU. uint24_t getPC(); + //! @brief Return the current stack pointer. + uint16_t getStackPointer(); + //! @brief The stack pointer before the execution of any instructions. + uint16_t initialStackPointer = this->_registers.s; //! @brief Update the UI when resetting the CPU. int RESB() override; //! @brief Convert a basic CPU to a debugging CPU. diff --git a/ui/cpu.ui b/ui/cpu.ui index 1373c20..817b1d2 100644 --- a/ui/cpu.ui +++ b/ui/cpu.ui @@ -56,7 +56,11 @@ - + + + false + + diff --git a/ui/ui_cpu.h b/ui/ui_cpu.h index 13d1ef0..7680dc1 100644 --- a/ui/ui_cpu.h +++ b/ui/ui_cpu.h @@ -133,6 +133,7 @@ public: gridLayout_2->setObjectName(QString::fromUtf8("gridLayout_2")); stackView = new QTableView(centralwidget); stackView->setObjectName(QString::fromUtf8("stackView")); + stackView->horizontalHeader()->setVisible(false); gridLayout_2->addWidget(stackView, 1, 0, 1, 1); From 1feab12fece5d71c6e23ea6f03a9f59869229b08 Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Fri, 3 Apr 2020 22:23:54 +0200 Subject: [PATCH 10/26] Fixing the BRK/COP --- sources/CPU/CPU.hpp | 4 ++-- sources/CPU/Instructions/Interrupts.cpp | 8 ++------ sources/Debugger/CPU/CPUDebug.cpp | 2 +- tests/CPU/testInterupts.cpp | 12 ++++++------ 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index ec96690..856b5b7 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -384,9 +384,9 @@ namespace ComSquare::CPU //! @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, "brk", AddressingMode::Immediate8bits, 2}, // 00 {&CPU::ORA, 6, "ora", AddressingMode::DirectPageIndirectIndexedByX, 2}, // 01 - {&CPU::COP, 7, "cop", AddressingMode::Implied, 2}, // 02 + {&CPU::COP, 7, "cop", AddressingMode::Immediate8bits, 2}, // 02 {&CPU::ORA, 4, "ora", AddressingMode::StackRelative, 2}, // 03 {&CPU::BRK, 7, "tsb #-#", AddressingMode::Implied, 2}, // 04 {&CPU::ORA, 3, "ora", AddressingMode::DirectPage, 2}, // 05 diff --git a/sources/CPU/Instructions/Interrupts.cpp b/sources/CPU/Instructions/Interrupts.cpp index 1155da4..2e09647 100644 --- a/sources/CPU/Instructions/Interrupts.cpp +++ b/sources/CPU/Instructions/Interrupts.cpp @@ -24,16 +24,14 @@ namespace ComSquare::CPU int CPU::BRK(uint24_t, AddressingMode) { if (this->_isEmulationMode) { - this->_registers.pc += 2; this->_push(this->_registers.pc); - this->_registers.p.x_b = true; this->_push(this->_registers.p.flags); this->_registers.p.i = true; this->_registers.p.d = false; + this->_registers.pbr = 0x0; this->_registers.pc = this->_cartridgeHeader.emulationInterrupts.brk; } else { this->_push(this->_registers.pbr); - this->_registers.pc += 2; this->_push(this->_registers.pc); this->_push(this->_registers.p.flags); this->_registers.p.i = true; @@ -47,16 +45,14 @@ namespace ComSquare::CPU int CPU::COP(uint24_t, AddressingMode) { if (this->_isEmulationMode) { - this->_registers.pc += 2; this->_push(this->_registers.pc); this->_push(this->_registers.p.flags); this->_registers.p.i = true; this->_registers.p.d = false; + this->_registers.pbr = 0x0; this->_registers.pc = this->_cartridgeHeader.emulationInterrupts.cop; - } else { this->_push(this->_registers.pbr); - this->_registers.pc += 2; this->_push(this->_registers.pc); this->_push(this->_registers.p.flags); this->_registers.p.i = true; diff --git a/sources/Debugger/CPU/CPUDebug.cpp b/sources/Debugger/CPU/CPUDebug.cpp index 9a08dd8..b2de823 100644 --- a/sources/Debugger/CPU/CPUDebug.cpp +++ b/sources/Debugger/CPU/CPUDebug.cpp @@ -234,7 +234,7 @@ namespace ComSquare::Debugger DisassemblyContext CPUDebug::_getDisassemblyContext() { - return {this->_registers.p.m, this->_registers.p.x_b, false}; + return {this->_registers.p.m, this->_registers.p.x_b, this->_isEmulationMode}; } int CPUDebug::RESB() diff --git a/tests/CPU/testInterupts.cpp b/tests/CPU/testInterupts.cpp index cfb7a77..1a3b3e6 100644 --- a/tests/CPU/testInterupts.cpp +++ b/tests/CPU/testInterupts.cpp @@ -20,14 +20,14 @@ Test(CPU_emulated, BRK) snes.cpu->_registers.pbr = 0x15; 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.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."); cr_assert_eq(snes.cpu->_registers.p.i, true, "The Interrupt disable flag should be set."); cr_assert_eq(snes.cpu->_registers.p.x_b, true, "The break flag should be set."); int data = snes.cpu->_pop(); cr_assert_eq(data, 0xF1, "The Status Registers should be pushed into the stack with the value 0xF1 but it was 0x%X (expected 0xF1).", data); data = snes.cpu->_pop16(); - cr_assert_eq(data, 0x158u, "The program counter should be incremented by two and pushed on the stack but it was 0x%X (expected 0x158).", data); + cr_assert_eq(data, 0x156u, "The program counter should be incremented by two and pushed on the stack but it was 0x%X (expected 0x158).", data); } Test(CPU_native, BRK) @@ -46,7 +46,7 @@ Test(CPU_native, BRK) int data = snes.cpu->_pop(); cr_assert_eq(data, 0xF1, "The Status Registers should be pushed into the stack with the value 0xF1 but it was 0x%X (expected 0xF1).", data); data = snes.cpu->_pop16(); - cr_assert_eq(data, 0x158u, "The program counter should be incremented by two and pushed on the stack but it was 0x%X (expected 0x158).", data); + cr_assert_eq(data, 0x156u, "The program counter should be incremented by two and pushed on the stack but it was 0x%X (expected 0x156).", data); data = snes.cpu->_pop(); cr_assert_eq(data, 0x15, "The program bank register should be pushed on the stack but it was 0x%X (expected 0x15).", data); } @@ -61,14 +61,14 @@ Test(CPU_emulated, COP) snes.cpu->_registers.pbr = 0x15; 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.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."); cr_assert_eq(snes.cpu->_registers.p.i, true, "The Interrupt disable flag should be set."); cr_assert_eq(snes.cpu->_registers.p.x_b, false, "The break flag should not be set."); int data = snes.cpu->_pop(); cr_assert_eq(data, 0x0F, "The Status Registers should be pushed into the stack with the value 0x0F but it was 0x%X (expected 0xF1).", data); data = snes.cpu->_pop16(); - cr_assert_eq(data, 0x158u, "The program counter should be incremented by two and pushed on the stack but it was 0x%X (expected 0x158).", data); + cr_assert_eq(data, 0x156u, "The program counter should be incremented by two and pushed on the stack but it was 0x%X (expected 0x158).", data); } Test(CPU_native, COP) @@ -87,7 +87,7 @@ Test(CPU_native, COP) int data = snes.cpu->_pop(); cr_assert_eq(data, 0xF1, "The Status Registers should be pushed into the stack with the value 0xF1 but it was 0x%X (expected 0xF1).", data); data = snes.cpu->_pop16(); - cr_assert_eq(data, 0x158u, "The program counter should be incremented by two and pushed on the stack but it was 0x%X (expected 0x158).", data); + cr_assert_eq(data, 0x156u, "The program counter should be incremented by two and pushed on the stack but it was 0x%X (expected 0x156).", data); data = snes.cpu->_pop(); cr_assert_eq(data, 0x15, "The program bank register should be pushed on the stack but it was 0x%X (expected 0x15).", data); } \ No newline at end of file From 84f964111c64e84e3e1aeb83238b9799f07c7deb Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Fri, 3 Apr 2020 22:32:39 +0200 Subject: [PATCH 11/26] Implementing the RTS/RTL --- sources/CPU/CPU.hpp | 8 ++++++-- sources/CPU/Instructions/InternalInstruction.cpp | 13 +++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index 856b5b7..7338a21 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -380,6 +380,10 @@ namespace ComSquare::CPU int DEY(uint24_t, AddressingMode); //! @brief Or accumulator with memory. int ORA(uint24_t valueAddr, AddressingMode mode); + //! @brief Return from subroutine. + int RTS(uint24_t, AddressingMode); + //! @brief Return from subroutine long. + int RTL(uint24_t, AddressingMode); //! @brief All the instructions of the CPU. //! @info Instructions are indexed by their opcode @@ -480,7 +484,7 @@ namespace ComSquare::CPU {&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, 1}, // 60 + {&CPU::RTL, 6, "rtl", AddressingMode::Implied, 1}, // 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 @@ -491,7 +495,7 @@ namespace ComSquare::CPU {&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, 1}, // 6B + {&CPU::RTS, 6, "rts", AddressingMode::Implied, 1}, // 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 diff --git a/sources/CPU/Instructions/InternalInstruction.cpp b/sources/CPU/Instructions/InternalInstruction.cpp index 782bfbb..d553621 100644 --- a/sources/CPU/Instructions/InternalInstruction.cpp +++ b/sources/CPU/Instructions/InternalInstruction.cpp @@ -352,4 +352,17 @@ namespace ComSquare::CPU { return 0; } + + int CPU::RTS(uint24_t, AddressingMode) + { + this->_registers.pc = this->_pop16() + 1; + return 0; + } + + int CPU::RTL(uint24_t, AddressingMode) + { + this->_registers.pc = this->_pop16() + 1; + this->_registers.dbr = this->_pop(); + return 0; + } } \ No newline at end of file From 218935b5bbd4845dbffd4ad71b5e26d9ebd7b819 Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Sat, 4 Apr 2020 23:17:31 +0200 Subject: [PATCH 12/26] Reworking the instruction history --- sources/CPU/CPU.cpp | 135 ++++++++++++---------------- sources/CPU/CPU.hpp | 5 ++ sources/Debugger/CPU/CPUDebug.cpp | 123 ++++++++++++++++++++++--- sources/Debugger/CPU/CPUDebug.hpp | 43 +++++++++ sources/Debugger/MemoryBusDebug.cpp | 13 ++- sources/Memory/MemoryBus.hpp | 10 ++- ui/cpu.ui | 20 ++--- ui/ui_cpu.h | 21 +++-- 8 files changed, 252 insertions(+), 118 deletions(-) diff --git a/sources/CPU/CPU.cpp b/sources/CPU/CPU.cpp index 9fdb770..8116408 100644 --- a/sources/CPU/CPU.cpp +++ b/sources/CPU/CPU.cpp @@ -211,86 +211,69 @@ namespace ComSquare::CPU return cycles; } + uint24_t CPU::_getValueAddr(Instruction &instruction) + { + switch (instruction.addressingMode) { + case Implied: + return 0; + case Immediate8bits: + return this->_getImmediateAddr8Bits(); + case ImmediateForA: + return this->_getImmediateAddrForA(); + case ImmediateForX: + return this->_getImmediateAddrForX(); + + case Absolute: + return this->_getAbsoluteAddr(); + case AbsoluteLong: + return this->_getAbsoluteLongAddr(); + case AbsoluteIndirect: + return this->_getAbsoluteIndirectAddr(); + case AbsoluteIndirectLong: + return this->_getAbsoluteIndirectLongAddr(); + + case DirectPage: + return this->_getDirectAddr(); + case DirectPageIndirect: + return this->_getDirectIndirectAddr(); + case DirectPageIndirectLong: + return this->_getDirectIndirectLongAddr(); + + case DirectPageIndexedByX: + return this->_getDirectIndexedByXAddr(); + case DirectPageIndexedByY: + return this->_getDirectIndexedByYAddr(); + case DirectPageIndirectIndexedByX: + return this->_getDirectIndirectIndexedXAddr(); + case DirectPageIndirectIndexedByY: + return this->_getDirectIndirectIndexedYAddr(); + case DirectPageIndirectIndexedByYLong: + return this->_getDirectIndirectIndexedYLongAddr(); + + case AbsoluteIndexedByX: + return this->_getAbsoluteIndexedByXAddr(); + case AbsoluteIndexedByXLong: + return this->_getAbsoluteIndexedByXLongAddr(); + case AbsoluteIndexedByY: + return this->_getAbsoluteIndexedByYAddr(); + + case StackRelative: + return this->_getStackRelativeAddr(); + case StackRelativeIndirectIndexedByY: + return this->_getStackRelativeIndirectIndexedYAddr(); + + case AbsoluteIndirectIndexedByX: + return this->_getAbsoluteIndirectIndexedByXAddr(); + default: + return 0; + } + } + unsigned CPU::_executeInstruction(uint8_t opcode) { Instruction instruction = this->_instructions[opcode]; - uint24_t valueAddr = 0; - this->_hasIndexCrossedPageBoundary = false; - - 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 Absolute: - valueAddr = this->_getAbsoluteAddr(); - break; - case AbsoluteLong: - valueAddr = this->_getAbsoluteLongAddr(); - break; - case AbsoluteIndirect: - valueAddr = this->_getAbsoluteIndirectAddr(); - break; - case AbsoluteIndirectLong: - valueAddr = this->_getAbsoluteIndirectLongAddr(); - break; - - case DirectPage: - valueAddr = this->_getDirectAddr(); - break; - case DirectPageIndirect: - valueAddr = this->_getDirectIndirectAddr(); - break; - case DirectPageIndirectLong: - valueAddr = this->_getDirectIndirectLongAddr(); - break; - - 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 AbsoluteIndexedByX: - valueAddr = this->_getAbsoluteIndexedByXAddr(); - break; - case AbsoluteIndexedByXLong: - valueAddr = this->_getAbsoluteIndexedByXLongAddr(); - break; - case AbsoluteIndexedByY: - valueAddr = this->_getAbsoluteIndexedByYAddr(); - break; - - case StackRelative: - valueAddr = this->_getStackRelativeAddr(); - break; - case StackRelativeIndirectIndexedByY: - valueAddr = this->_getStackRelativeIndirectIndexedYAddr(); - break; - - case AbsoluteIndirectIndexedByX: - valueAddr = this->_getAbsoluteIndirectIndexedByXAddr(); - break; - } + uint24_t valueAddr = this->_getValueAddr(instruction); return instruction.cycleCount + (this->*instruction.call)(valueAddr, instruction.addressingMode); } diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index 7338a21..e498095 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -258,6 +258,11 @@ namespace ComSquare::CPU //! @return The number of CPU cycles that the instruction took. virtual unsigned _executeInstruction(uint8_t opcode); + //! @brief Get the parameter address of an instruction from it's addressing mode. + //! @info The current program counter should point to the instruction's opcode + 1. + //! @return The address of the data to read on the instruction. + uint24_t _getValueAddr(Instruction &instruction); + //! @brief Break instruction - Causes a software break. The PC is loaded from a vector table. int BRK(uint24_t, AddressingMode); //! @brief Co-Processor Enable instruction - Causes a software break. The PC is loaded from a vector table. diff --git a/sources/Debugger/CPU/CPUDebug.cpp b/sources/Debugger/CPU/CPUDebug.cpp index b2de823..9463824 100644 --- a/sources/Debugger/CPU/CPUDebug.cpp +++ b/sources/Debugger/CPU/CPUDebug.cpp @@ -42,6 +42,12 @@ namespace ComSquare::Debugger this->_ui.stackView->verticalHeader()->setSectionResizeMode (QHeaderView::Fixed); this->_ui.stackView->verticalHeader()->setHighlightSections(false); + + this->_ui.history->setModel(&this->_historyModel); + this->_ui.history->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + this->_ui.history->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); + this->_ui.history->verticalHeader()->hide(); + 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); @@ -108,7 +114,10 @@ namespace ComSquare::Debugger 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()); + this->_registers.pc--; + this->_historyModel.log({opcode, instruction.name, instruction.argument, this->getProceededParameters()}); + this->_ui.history->scrollToBottom(); + this->_registers.pc++; unsigned ret = CPU::_executeInstruction(opcode); this->_updateRegistersPanel(); return ret; @@ -204,7 +213,7 @@ namespace ComSquare::Debugger void CPUDebug::clearHistory() { - this->_ui.logger->clear(); + this->_historyModel.clear(); } void CPUDebug::_updateDisassembly(uint24_t start, uint24_t refreshSize) @@ -260,6 +269,19 @@ namespace ComSquare::Debugger { return this->_registers.s; } + + std::string CPUDebug::getProceededParameters() + { + uint24_t pac = this->_registers.pac; + this->_bus->forceSilence = true; + Instruction instruction = this->_instructions[this->readPC()]; + uint24_t valueAddr = this->_getValueAddr(instruction); + this->_registers.pac = pac; + this->_bus->forceSilence = false; + if (instruction.size == 1) + return ""; + return "[" + Utility::to_hex(valueAddr, Utility::AsmPrefix) + "]"; + } } DisassemblyModel::DisassemblyModel(ComSquare::Debugger::CPUDebug &cpu) : QAbstractTableModel(), _cpu(cpu){ } @@ -276,21 +298,28 @@ int DisassemblyModel::rowCount(const QModelIndex &) const 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) + + switch (role) { + case Qt::DecorationRole: + if (index.column() == 2 && instruction.level == ComSquare::Debugger::TrustLevel::Unsafe) return QColor(Qt::yellow); - if (index.column() == 3 && instruction.level == ComSquare::Debugger::TrustLevel::Compromised) + if (index.column() == 2 && 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()); + case Qt::DisplayRole: + switch (index.column()) { + case 0: + return QString(instruction.name.c_str()); + case 1: + return QString(instruction.argument.c_str()); + case 3: + if (instruction.address != this->_cpu.getPC()) + return QVariant(); + return QString(this->_cpu.getProceededParameters().c_str()); + default: + return QVariant(); + } default: return QVariant(); } @@ -377,3 +406,71 @@ QVariant StackModel::headerData(int section, Qt::Orientation orientation, int ro uint16_t addr = section * 2; return QString(ComSquare::Utility::to_hex(addr, ComSquare::Utility::HexString::NoPrefix).c_str()); } + +HistoryModel::HistoryModel() = default; + +int HistoryModel::rowCount(const QModelIndex &) const +{ + return this->_instructions.size(); +} + +int HistoryModel::columnCount(const QModelIndex &) const +{ + return 4; +} + +QVariant HistoryModel::data(const QModelIndex &index, int role) const +{ + if (role == Qt::TextAlignmentRole) + return Qt::AlignCenter; + if (role != Qt::DisplayRole) + return QVariant(); + + ComSquare::Debugger::ExecutedInstruction instruction = this->_instructions[index.row()]; + switch (index.column()) { + case 0: + return QString(ComSquare::Utility::to_hex(instruction.opcode, ComSquare::Utility::NoPrefix).c_str()); + case 1: + return QString(instruction.name.c_str()); + case 2: + return QString(instruction.params.c_str()); + case 3: + return QString(instruction.proceededParams.c_str()); + default: + return QVariant(); + } +} + +QVariant HistoryModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Vertical || role != Qt::DisplayRole) + return QVariant(); + switch (section) { + case 0: + return QString("OP"); + case 1: + return QString("INST"); + case 2: + return QString("Parameter"); + case 3: + return QString("Data Addr"); + default: + return QVariant(); + } +} + +void HistoryModel::log(const ComSquare::Debugger::ExecutedInstruction& instruction) +{ + int row = this->_instructions.size(); + this->beginInsertRows(QModelIndex(), row, row); + this->_instructions.push_back(instruction); + this->insertRow(row); + this->endInsertRows(); +} + +void HistoryModel::clear() +{ + this->beginResetModel(); + this->_instructions.clear(); + this->endResetModel(); +} \ No newline at end of file diff --git a/sources/Debugger/CPU/CPUDebug.hpp b/sources/Debugger/CPU/CPUDebug.hpp index 67dd93e..ab596d0 100644 --- a/sources/Debugger/CPU/CPUDebug.hpp +++ b/sources/Debugger/CPU/CPUDebug.hpp @@ -15,6 +15,18 @@ namespace ComSquare::Debugger { class CPUDebug; + + //! @brief An instruction that has already been executed. Used for the history viewer + struct ExecutedInstruction { + //! @brief Opcode of the instruction + uint8_t opcode; + //! @brief The name of the instruction + std::string name; + //! @brief Readable parameters (disassembly style) + std::string params; + //! @brief The address to read from after processing the parameter. + std::string proceededParams; + }; } //! @brief The qt model that show the stack. @@ -40,6 +52,33 @@ public: QVariant headerData(int section, Qt::Orientation orientation, int role) const override; }; +//! @brief The qt model that show the history. +class HistoryModel : public QAbstractTableModel +{ +Q_OBJECT +private: + std::vector _instructions = {}; +public: + HistoryModel(); + HistoryModel(const HistoryModel &) = delete; + const HistoryModel &operator=(const HistoryModel &) = delete; + ~HistoryModel() override = default; + + //! @brief Log a new instruction + void log(const ComSquare::Debugger::ExecutedInstruction &); + //! @brief Remove every instructions of the history. + void clear(); + + //! @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 model that show the disassembly. class DisassemblyModel : public QAbstractTableModel { @@ -139,6 +178,8 @@ namespace ComSquare::Debugger RowPainter _painter; //! @brief The stack viewer's model. StackModel _stackModel; + //! @brief The history model. + HistoryModel _historyModel; //! @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. @@ -220,6 +261,8 @@ namespace ComSquare::Debugger std::vector disassembledInstructions; //! @brief The list of breakpoints the user has set. std::vector breakpoints; + //! @brief Get a string representing the actual value of the arguments of the next instruction to execute. + std::string getProceededParameters(); //! @brief Return the current program counter of this CPU. uint24_t getPC(); //! @brief Return the current stack pointer. diff --git a/sources/Debugger/MemoryBusDebug.cpp b/sources/Debugger/MemoryBusDebug.cpp index e1548e5..a02533c 100644 --- a/sources/Debugger/MemoryBusDebug.cpp +++ b/sources/Debugger/MemoryBusDebug.cpp @@ -150,10 +150,14 @@ namespace ComSquare::Debugger uint8_t MemoryBusDebug::read(uint24_t addr, bool silence) { - if (!silence) { + if (!silence && !forceSilence) { auto accessor = this->getAccessor(addr); - uint8_t value = accessor->read(addr - accessor->getStart()); - this->_model.log(BusLog(false, addr, accessor, value, value)); + if (!accessor) { + this->_model.log(BusLog(true, addr, accessor, this->_openBus, this->_openBus)); + } else { + uint8_t value = accessor->read(addr - accessor->getStart()); + this->_model.log(BusLog(false, addr, accessor, value, value)); + } } return MemoryBus::read(addr); } @@ -167,7 +171,8 @@ namespace ComSquare::Debugger } catch (InvalidAddress &) { value = 0; } - this->_model.log(BusLog(true, addr, accessor, value, data)); + if (!forceSilence) + this->_model.log(BusLog(true, addr, accessor, value, data)); MemoryBus::write(addr, data); } diff --git a/sources/Memory/MemoryBus.hpp b/sources/Memory/MemoryBus.hpp index 983ebfd..044462d 100644 --- a/sources/Memory/MemoryBus.hpp +++ b/sources/Memory/MemoryBus.hpp @@ -22,20 +22,22 @@ namespace ComSquare //! @brief The list of components registered inside the bus. Every components that can read/write to a public address should be in this vector. std::vector> _memoryAccessors; - //! @brief The last value read via the memory bus. - uint8_t _openBus = 0; - //! @brief WRam, CPU, PPU & APU registers are mirrored to all banks of Q1 & Q3. This function is used for the mirroring. //! @param console All the components. //! @param i Base address for the mirrors. void _mirrorComponents(SNES &console, unsigned i); - + protected: + //! @brief The last value read via the memory bus. + uint8_t _openBus = 0; public: MemoryBus() = default; MemoryBus(const MemoryBus &) = default; MemoryBus &operator=(const MemoryBus &) = default; ~MemoryBus() = default; + //! @brief Force silencing read to the bus. + bool forceSilence = false; + //! @brief Read data at a global address. //! @param addr The address to read from. //! @param silence Disable login to the memory bus's debugger (if enabled). Should only be used by other debuggers. diff --git a/ui/cpu.ui b/ui/cpu.ui index 817b1d2..220c344 100644 --- a/ui/cpu.ui +++ b/ui/cpu.ui @@ -181,16 +181,6 @@ - - - - - - - Clear History - - - @@ -201,6 +191,16 @@ + + + + Clear History + + + + + + diff --git a/ui/ui_cpu.h b/ui/ui_cpu.h index 7680dc1..f99cb91 100644 --- a/ui/ui_cpu.h +++ b/ui/ui_cpu.h @@ -23,7 +23,6 @@ #include #include #include -#include #include #include @@ -61,9 +60,9 @@ public: QLabel *emulationModeLabel; QCheckBox *emulationModeCheckBox; QGridLayout *gridLayout; - QTextBrowser *logger; - QPushButton *clear; QLabel *loggerLabel; + QPushButton *clear; + QTableView *history; QGroupBox *formGroupBox; QFormLayout *formLayout_2; QLabel *negativeLabel; @@ -245,21 +244,21 @@ public: gridLayout = new QGridLayout(); gridLayout->setObjectName(QString::fromUtf8("gridLayout")); - logger = new QTextBrowser(centralwidget); - logger->setObjectName(QString::fromUtf8("logger")); + loggerLabel = new QLabel(centralwidget); + loggerLabel->setObjectName(QString::fromUtf8("loggerLabel")); + loggerLabel->setAlignment(Qt::AlignCenter); - gridLayout->addWidget(logger, 1, 0, 1, 1); + gridLayout->addWidget(loggerLabel, 0, 0, 1, 1); clear = new QPushButton(centralwidget); clear->setObjectName(QString::fromUtf8("clear")); gridLayout->addWidget(clear, 2, 0, 1, 1); - loggerLabel = new QLabel(centralwidget); - loggerLabel->setObjectName(QString::fromUtf8("loggerLabel")); - loggerLabel->setAlignment(Qt::AlignCenter); + history = new QTableView(centralwidget); + history->setObjectName(QString::fromUtf8("history")); - gridLayout->addWidget(loggerLabel, 0, 0, 1, 1); + gridLayout->addWidget(history, 1, 0, 1, 1); gridLayout_3->addLayout(gridLayout, 1, 1, 1, 1); @@ -419,8 +418,8 @@ public: xIndexLabel->setText(QCoreApplication::translate("CPUView", "X Index", nullptr)); yIndexLabel->setText(QCoreApplication::translate("CPUView", "Y Index", nullptr)); emulationModeLabel->setText(QCoreApplication::translate("CPUView", "Emulation mode", nullptr)); - clear->setText(QCoreApplication::translate("CPUView", "Clear History", nullptr)); loggerLabel->setText(QCoreApplication::translate("CPUView", "Instructions History", nullptr)); + clear->setText(QCoreApplication::translate("CPUView", "Clear History", nullptr)); formGroupBox->setTitle(QCoreApplication::translate("CPUView", "Flags", nullptr)); negativeLabel->setText(QCoreApplication::translate("CPUView", "Memory/Accumulator Select (M)", nullptr)); zeroLabel->setText(QCoreApplication::translate("CPUView", "Index Select (X) / Break (B)", nullptr)); From 9d5cbb4ed24d8af2c0458265ae8965fc9caafec7 Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Mon, 6 Apr 2020 14:10:27 +0200 Subject: [PATCH 13/26] Cleaning up --- sources/Debugger/CPU/CPUDebug.cpp | 25 +++++++++++--- ui/cpu.ui | 55 ++++++++++++++++++++----------- ui/ui_cpu.h | 54 +++++++++++++++++++----------- 3 files changed, 90 insertions(+), 44 deletions(-) diff --git a/sources/Debugger/CPU/CPUDebug.cpp b/sources/Debugger/CPU/CPUDebug.cpp index 9463824..5be3734 100644 --- a/sources/Debugger/CPU/CPUDebug.cpp +++ b/sources/Debugger/CPU/CPUDebug.cpp @@ -33,6 +33,7 @@ namespace ComSquare::Debugger this->_updateDisassembly(this->_cartridgeHeader.emulationInterrupts.reset, 0xFFFF - this->_cartridgeHeader.emulationInterrupts.reset); //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()->setSectionResizeMode(QHeaderView::Stretch); + this->_ui.disassembly->horizontalHeader()->setStretchLastSection(true); this->_ui.disassembly->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); this->_ui.disassembly->verticalHeader()->setHighlightSections(false); this->_ui.disassembly->setItemDelegate(&this->_painter); @@ -45,6 +46,7 @@ namespace ComSquare::Debugger this->_ui.history->setModel(&this->_historyModel); this->_ui.history->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + this->_ui.history->horizontalHeader()->setStretchLastSection(true); this->_ui.history->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); this->_ui.history->verticalHeader()->hide(); @@ -183,6 +185,7 @@ namespace ComSquare::Debugger this->_ui.mCheckbox->setCheckState(this->_registers.p.m ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); this->_ui.xCheckbox->setCheckState(this->_registers.p.x_b ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + this->_ui.bCheckbox->setCheckState(this->_registers.p.x_b ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); this->_ui.iCheckbox->setCheckState(this->_registers.p.i ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); this->_ui.vCheckbox->setCheckState(this->_registers.p.v ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); this->_ui.dCheckbox->setCheckState(this->_registers.p.d ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); @@ -327,8 +330,20 @@ QVariant DisassemblyModel::data(const QModelIndex &index, int role) const QVariant DisassemblyModel::headerData(int section, Qt::Orientation orientation, int role) const { - if (orientation == Qt::Horizontal) - return QVariant(); + if (orientation == Qt::Horizontal) { + switch (section) { + case 0: + return QString("INST"); + case 1: + return QString("Parameter"); + case 2: + return QString("Thrust"); + case 3: + return QString("Data Address"); + default: + return QVariant(); + } + } if (role != Qt::DisplayRole) return QVariant(); ComSquare::Debugger::DisassembledInstruction instruction = this->_cpu.disassembledInstructions[section]; @@ -447,13 +462,13 @@ QVariant HistoryModel::headerData(int section, Qt::Orientation orientation, int return QVariant(); switch (section) { case 0: - return QString("OP"); + return QString("op"); case 1: - return QString("INST"); + return QString("ins"); case 2: return QString("Parameter"); case 3: - return QString("Data Addr"); + return QString("Pointer"); default: return QVariant(); } diff --git a/ui/cpu.ui b/ui/cpu.ui index 220c344..0f8d2f2 100644 --- a/ui/cpu.ui +++ b/ui/cpu.ui @@ -6,7 +6,7 @@ 0 0 - 971 + 1058 673 @@ -22,7 +22,7 @@ - + @@ -74,7 +74,7 @@ - + @@ -179,7 +179,7 @@ - + @@ -203,7 +203,7 @@ - + Flags @@ -233,10 +233,13 @@ 7 + + 0 + - Memory/Accumulator Select (M) + Memory Select (M) @@ -250,7 +253,7 @@ - Index Select (X) / Break (B) + Index Select (X) @@ -261,90 +264,104 @@ - + Interupt Request Disable (I) - + Qt::RightToLeft - + Overflow (V) - + Qt::RightToLeft - + Decimal (D) - + Qt::RightToLeft - + Carry (C) - + Qt::RightToLeft - + Zero (Z) - + Negative (N) - + Qt::RightToLeft - + Qt::RightToLeft + + + + Break (B) + + + + + + + Qt::RightToLeft + + + diff --git a/ui/ui_cpu.h b/ui/ui_cpu.h index f99cb91..97769dd 100644 --- a/ui/ui_cpu.h +++ b/ui/ui_cpu.h @@ -81,13 +81,15 @@ public: QLabel *irqDisableLabel; QCheckBox *nCheckbox; QCheckBox *zCheckbox; + QLabel *breakBLabel; + QCheckBox *bCheckbox; QToolBar *toolBar; void setupUi(QMainWindow *CPUView) { if (CPUView->objectName().isEmpty()) CPUView->setObjectName(QString::fromUtf8("CPUView")); - CPUView->resize(971, 673); + CPUView->resize(1058, 673); QIcon icon; icon.addFile(QString::fromUtf8(":/resources/Logo.png"), QSize(), QIcon::Normal, QIcon::Off); CPUView->setWindowIcon(icon); @@ -126,7 +128,7 @@ public: disassembly->horizontalHeader()->setVisible(false); disassembly->horizontalHeader()->setHighlightSections(false); - gridLayout_3->addWidget(disassembly, 0, 0, 2, 1); + gridLayout_3->addWidget(disassembly, 0, 0, 3, 1); gridLayout_2 = new QGridLayout(); gridLayout_2->setObjectName(QString::fromUtf8("gridLayout_2")); @@ -240,7 +242,7 @@ public: formLayout->setWidget(8, QFormLayout::FieldRole, emulationModeCheckBox); - gridLayout_3->addLayout(formLayout, 0, 2, 1, 1); + gridLayout_3->addLayout(formLayout, 0, 2, 2, 1); gridLayout = new QGridLayout(); gridLayout->setObjectName(QString::fromUtf8("gridLayout")); @@ -261,7 +263,7 @@ public: gridLayout->addWidget(history, 1, 0, 1, 1); - gridLayout_3->addLayout(gridLayout, 1, 1, 1, 1); + gridLayout_3->addLayout(gridLayout, 1, 1, 2, 1); formGroupBox = new QGroupBox(centralwidget); formGroupBox->setObjectName(QString::fromUtf8("formGroupBox")); @@ -273,7 +275,7 @@ public: formLayout_2->setSizeConstraint(QLayout::SetDefaultConstraint); formLayout_2->setLabelAlignment(Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter); formLayout_2->setFormAlignment(Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop); - formLayout_2->setContentsMargins(26, 7, -1, -1); + formLayout_2->setContentsMargins(26, 7, 0, -1); negativeLabel = new QLabel(formGroupBox); negativeLabel->setObjectName(QString::fromUtf8("negativeLabel")); @@ -299,71 +301,82 @@ public: carryLabel = new QLabel(formGroupBox); carryLabel->setObjectName(QString::fromUtf8("carryLabel")); - formLayout_2->setWidget(2, QFormLayout::LabelRole, carryLabel); + formLayout_2->setWidget(3, QFormLayout::LabelRole, carryLabel); iCheckbox = new QCheckBox(formGroupBox); iCheckbox->setObjectName(QString::fromUtf8("iCheckbox")); iCheckbox->setLayoutDirection(Qt::RightToLeft); - formLayout_2->setWidget(2, QFormLayout::FieldRole, iCheckbox); + formLayout_2->setWidget(3, QFormLayout::FieldRole, iCheckbox); Overflow = new QLabel(formGroupBox); Overflow->setObjectName(QString::fromUtf8("Overflow")); - formLayout_2->setWidget(3, QFormLayout::LabelRole, Overflow); + formLayout_2->setWidget(4, QFormLayout::LabelRole, Overflow); vCheckbox = new QCheckBox(formGroupBox); vCheckbox->setObjectName(QString::fromUtf8("vCheckbox")); vCheckbox->setLayoutDirection(Qt::RightToLeft); - formLayout_2->setWidget(3, QFormLayout::FieldRole, vCheckbox); + formLayout_2->setWidget(4, QFormLayout::FieldRole, vCheckbox); decimalLabel = new QLabel(formGroupBox); decimalLabel->setObjectName(QString::fromUtf8("decimalLabel")); - formLayout_2->setWidget(4, QFormLayout::LabelRole, decimalLabel); + formLayout_2->setWidget(5, QFormLayout::LabelRole, decimalLabel); dCheckbox = new QCheckBox(formGroupBox); dCheckbox->setObjectName(QString::fromUtf8("dCheckbox")); dCheckbox->setLayoutDirection(Qt::RightToLeft); - formLayout_2->setWidget(4, QFormLayout::FieldRole, dCheckbox); + formLayout_2->setWidget(5, QFormLayout::FieldRole, dCheckbox); memoryAccumulatorSelectLabel = new QLabel(formGroupBox); memoryAccumulatorSelectLabel->setObjectName(QString::fromUtf8("memoryAccumulatorSelectLabel")); - formLayout_2->setWidget(5, QFormLayout::LabelRole, memoryAccumulatorSelectLabel); + formLayout_2->setWidget(6, QFormLayout::LabelRole, memoryAccumulatorSelectLabel); cCheckbox = new QCheckBox(formGroupBox); cCheckbox->setObjectName(QString::fromUtf8("cCheckbox")); cCheckbox->setLayoutDirection(Qt::RightToLeft); - formLayout_2->setWidget(5, QFormLayout::FieldRole, cCheckbox); + formLayout_2->setWidget(6, QFormLayout::FieldRole, cCheckbox); indeXSelectLabel = new QLabel(formGroupBox); indeXSelectLabel->setObjectName(QString::fromUtf8("indeXSelectLabel")); - formLayout_2->setWidget(6, QFormLayout::LabelRole, indeXSelectLabel); + formLayout_2->setWidget(7, QFormLayout::LabelRole, indeXSelectLabel); irqDisableLabel = new QLabel(formGroupBox); irqDisableLabel->setObjectName(QString::fromUtf8("irqDisableLabel")); - formLayout_2->setWidget(7, QFormLayout::LabelRole, irqDisableLabel); + formLayout_2->setWidget(8, QFormLayout::LabelRole, irqDisableLabel); nCheckbox = new QCheckBox(formGroupBox); nCheckbox->setObjectName(QString::fromUtf8("nCheckbox")); nCheckbox->setLayoutDirection(Qt::RightToLeft); - formLayout_2->setWidget(7, QFormLayout::FieldRole, nCheckbox); + formLayout_2->setWidget(8, QFormLayout::FieldRole, nCheckbox); zCheckbox = new QCheckBox(formGroupBox); zCheckbox->setObjectName(QString::fromUtf8("zCheckbox")); zCheckbox->setLayoutDirection(Qt::RightToLeft); - formLayout_2->setWidget(6, QFormLayout::FieldRole, zCheckbox); + formLayout_2->setWidget(7, QFormLayout::FieldRole, zCheckbox); + + breakBLabel = new QLabel(formGroupBox); + breakBLabel->setObjectName(QString::fromUtf8("breakBLabel")); + + formLayout_2->setWidget(2, QFormLayout::LabelRole, breakBLabel); + + bCheckbox = new QCheckBox(formGroupBox); + bCheckbox->setObjectName(QString::fromUtf8("bCheckbox")); + bCheckbox->setLayoutDirection(Qt::RightToLeft); + + formLayout_2->setWidget(2, QFormLayout::FieldRole, bCheckbox); - gridLayout_3->addWidget(formGroupBox, 1, 2, 1, 1); + gridLayout_3->addWidget(formGroupBox, 2, 2, 1, 1); CPUView->setCentralWidget(centralwidget); toolBar = new QToolBar(CPUView); @@ -421,14 +434,15 @@ public: loggerLabel->setText(QCoreApplication::translate("CPUView", "Instructions History", nullptr)); clear->setText(QCoreApplication::translate("CPUView", "Clear History", nullptr)); formGroupBox->setTitle(QCoreApplication::translate("CPUView", "Flags", nullptr)); - negativeLabel->setText(QCoreApplication::translate("CPUView", "Memory/Accumulator Select (M)", nullptr)); - zeroLabel->setText(QCoreApplication::translate("CPUView", "Index Select (X) / Break (B)", nullptr)); + negativeLabel->setText(QCoreApplication::translate("CPUView", "Memory Select (M)", nullptr)); + zeroLabel->setText(QCoreApplication::translate("CPUView", "Index Select (X)", nullptr)); carryLabel->setText(QCoreApplication::translate("CPUView", "Interupt Request Disable (I)", nullptr)); Overflow->setText(QCoreApplication::translate("CPUView", "Overflow (V)", nullptr)); decimalLabel->setText(QCoreApplication::translate("CPUView", "Decimal (D)", nullptr)); memoryAccumulatorSelectLabel->setText(QCoreApplication::translate("CPUView", "Carry (C)", nullptr)); indeXSelectLabel->setText(QCoreApplication::translate("CPUView", "Zero (Z)", nullptr)); irqDisableLabel->setText(QCoreApplication::translate("CPUView", "Negative (N)", nullptr)); + breakBLabel->setText(QCoreApplication::translate("CPUView", "Break (B)", nullptr)); toolBar->setWindowTitle(QCoreApplication::translate("CPUView", "toolBar", nullptr)); } // retranslateUi From e60c6d7bd76dba694f6280d7fd94f6561590907e Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Mon, 6 Apr 2020 15:49:46 +0200 Subject: [PATCH 14/26] Fixing memory shadow's bank offset --- CMakeLists.txt | 4 +- sources/Memory/ARectangleMemory.cpp | 1 + sources/Memory/ARectangleMemory.hpp | 2 +- sources/Memory/RectangleShadow.cpp | 5 +- sources/Memory/RectangleShadow.hpp | 2 +- tests/testRectangleMemory.cpp | 89 +++++++++++++++++++++++++++++ 6 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 tests/testRectangleMemory.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 068a71a..5bdd37c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,7 +95,9 @@ add_executable(unit_tests sources/Models/Components.hpp sources/CPU/Instruction.hpp sources/Exceptions/DebuggableError.hpp - tests/CPU/Math/testOthersMath.cpp) + tests/CPU/Math/testOthersMath.cpp + tests/testRectangleMemory.cpp +) # include criterion & coverage target_link_libraries(unit_tests criterion -lgcov) diff --git a/sources/Memory/ARectangleMemory.cpp b/sources/Memory/ARectangleMemory.cpp index b60aa66..e7e9ff4 100644 --- a/sources/Memory/ARectangleMemory.cpp +++ b/sources/Memory/ARectangleMemory.cpp @@ -5,6 +5,7 @@ #include #include "ARectangleMemory.hpp" #include "../Exceptions/InvalidAddress.hpp" +#include "../Utility/Utility.hpp" namespace ComSquare::Memory { diff --git a/sources/Memory/ARectangleMemory.hpp b/sources/Memory/ARectangleMemory.hpp index f3adb8f..0a8f028 100644 --- a/sources/Memory/ARectangleMemory.hpp +++ b/sources/Memory/ARectangleMemory.hpp @@ -12,7 +12,7 @@ namespace ComSquare::Memory { //! @brief Superset of the AMemory to map non continuous rectangle to the memory. (A rectangle that spam across more than one bank but that does not start at 0000 or end at FFFF). class ARectangleMemory : public AMemory { - private: + protected: //! @brief The first bank to map to. uint8_t _startBank = 0; //! @brief The last bank to map to. diff --git a/sources/Memory/RectangleShadow.cpp b/sources/Memory/RectangleShadow.cpp index 9d924d4..424724a 100644 --- a/sources/Memory/RectangleShadow.cpp +++ b/sources/Memory/RectangleShadow.cpp @@ -3,6 +3,7 @@ // #include "RectangleShadow.hpp" +#include "../Utility/Utility.hpp" #include #include @@ -17,13 +18,13 @@ namespace ComSquare::Memory uint8_t RectangleShadow::read_internal(uint24_t addr) { - addr += this->_bankOffset << 16u; + addr += this->_bankOffset * (this->_endPage - this->_startPage); return this->_initial->read_internal(addr); } void RectangleShadow::write_internal(uint24_t addr, uint8_t data) { - addr += this->_bankOffset << 16u; + addr += this->_bankOffset * (this->_endPage - this->_startPage); this->_initial->write_internal(addr, data); } diff --git a/sources/Memory/RectangleShadow.hpp b/sources/Memory/RectangleShadow.hpp index d3130d9..365c85f 100644 --- a/sources/Memory/RectangleShadow.hpp +++ b/sources/Memory/RectangleShadow.hpp @@ -22,7 +22,7 @@ namespace ComSquare::Memory explicit RectangleShadow(std::shared_ptr initial, uint8_t startBank, uint8_t endBank, uint16_t startPage, uint16_t endPage); RectangleShadow(const RectangleShadow &) = default; RectangleShadow &operator=(const RectangleShadow &) = default; - ~RectangleShadow() = default; + ~RectangleShadow() override = default; //! @brief Internal component read. Implement this as you would implement a basic AMemory's read. //! @param addr The local address to read from. 0x0 refer to the first byte of your data and the address is in the component's space. That means that you can consider this address as continuous diff --git a/tests/testRectangleMemory.cpp b/tests/testRectangleMemory.cpp new file mode 100644 index 0000000..71edf33 --- /dev/null +++ b/tests/testRectangleMemory.cpp @@ -0,0 +1,89 @@ +// +// Created by anonymus-raccoon on 4/6/20. +// + +#include +#include +#include "tests.hpp" +#include "../sources/SNES.hpp" +#include "../sources/Renderer/NoRenderer.hpp" +#include "../sources/PPU/PPU.hpp" +#include "../sources/Memory/RectangleShadow.hpp" +#include "../sources/Utility/Utility.hpp" + +using namespace ComSquare; + +Test(RectangleMemory, HorizontalRamRead) +{ + Ram::Ram ram(0xFF, Component::Rom, "Rom"); + ram.setMemoryRegion(0x00, 0xFF, 0x0000, 0x0001); + for (int i = 0x00; i < 0xFF; i++) + ram._data[i] = i; + for (uint24_t i = 0x000000; i < 0xFF0000; i += 0x010000) + cr_assert_eq(ram.read(i), i >> 16u, "The ram's read returned 0x%x but the internal ram value was: 0x%x (addr: 0x%06x)", ram.read(i), i >> 16, i); +} + +Test(RectangleMemory, HorizontalRamWrite) +{ + Ram::Ram ram(0xFF, Component::Rom, "Rom"); + ram.setMemoryRegion(0x00, 0xFF, 0x0000, 0x0001); + for (uint24_t i = 0x000000; i < 0xFF0000; i += 0x010000) + ram.write(i, i >> 16u); + for (int i = 0x00; i < 0xFF; i++) + cr_assert_eq(ram._data[i], i, "The ram's write put 0x%x but it should had put: 0x%x (addr: 0x%06x)", ram._data[i], i, i << 16u); +} + +Test(RectangleMemory, DualLineRamRead) +{ + Ram::Ram ram(0xFF * 2, Component::Rom, "Rom"); + ram.setMemoryRegion(0x00, 0xFF, 0x0000, 0x0002); + for (int i = 0x00; i < 0xFF * 2; i++) + ram._data[i] = i; + for (uint24_t i = 0x000000, v = 0; v < 0xFF * 2; i += 0x010000, v += 2) { + cr_assert_eq(ram.read(i), (uint8_t)(v), "The ram's read returned 0x%x but the internal ram value was: 0x%x (addr: 0x%06x)", ram.read(i), (uint8_t)(v), i); + cr_assert_eq(ram.read(i + 1), (uint8_t)(v + 1), "The ram's read returned 0x%x but the internal ram value was: 0x%x (addr: 0x%06x)", ram.read(i + 1), (uint8_t)(v + 1), i + 1); + } +} + +Test(RectangleMemory, HorizontalRamShadowRead) +{ + std::shared_ptr ram = std::make_shared(0xFF, Component::Rom, "Rom"); + ram->setMemoryRegion(0x00, 0xFF, 0x0000, 0x0001); + Memory::RectangleShadow shadow(ram, 0x00, 0xFF, 0x8000, 0x8001); + for (int i = 0x00; i < 0xFF; i++) + ram->_data[i] = i; + for (uint24_t i = 0x008000; i < 0xFF8000; i += 0x010000) { + uint8_t v = shadow.read(i - shadow.getStart()); + cr_assert_eq(v, i >> 16u, "The ram's read returned 0x%x but the internal ram value was: 0x%x (addr: 0x%06x)", v, i >> 16, i); + }} + +Test(RectangleMemory, HorizontalRamShadowReadWithBankOffset) +{ + std::shared_ptr ram = std::make_shared(0xFF, Component::Rom, "Rom"); + ram->setMemoryRegion(0x00, 0xFF, 0x0000, 0x0001); + Memory::RectangleShadow shadow(ram, 0x80, 0xFF, 0x8000, 0x8001); + for (int i = 0x00; i < 0xFF; i++) + ram->_data[i] = i; + shadow.setBankOffset(0x80); + for (uint24_t i = 0x808000; i < 0xFF8000; i += 0x010000) { + uint8_t v = shadow.read(i - shadow.getStart()); + cr_assert_eq(v, i >> 16u, "The ram's read returned 0x%x but the internal ram value was: 0x%x (addr: 0x%06x)", v, i >> 16, i); + } +} + +Test(RectangleMemory, ShadowOffsetCartridge) +{ + std::shared_ptr ram = std::make_shared(0x3fff80, Component::Rom, "Rom"); + ram->setMemoryRegion(0x80, 0xFF, 0x8000, 0xFFFF); + Memory::RectangleShadow shadow(ram, 0xC0, 0xEF, 0x0000, 0x7FFF); + for (int i = 0x00; i < 0x3fff80; i++) + ram->_data[i] = i; + shadow.setBankOffset(0x40); + for (uint24_t i = 0xC00000; i < 0xEF7FFF; i += 0x1) { + if ((uint16_t)i > 0x7FFFu) + i += 0x010000 - 0x8000; + uint8_t v = shadow.read(i - shadow.getStart()); + uint8_t r = ram->read(i + 0x8000 - ram->getStart()); + cr_assert_eq(v, r, "The ram's read returned 0x%x but the internal ram value was: 0x%x (addr: 0x%06x)", v, r, i); + } +} \ No newline at end of file From 83b8c83e6e044550f1d5198a4a8f0851d2a5e03f Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Mon, 6 Apr 2020 17:12:12 +0200 Subject: [PATCH 15/26] Implementing the CMP --- CMakeLists.txt | 2 +- sources/CPU/CPU.hpp | 34 +++++---- .../Instructions/MathematicalOperations.cpp | 37 ++++++++++ tests/CPU/Math/testCMP.cpp | 73 +++++++++++++++++++ 4 files changed, 129 insertions(+), 17 deletions(-) create mode 100644 tests/CPU/Math/testCMP.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5bdd37c..18c98a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,7 +97,7 @@ add_executable(unit_tests sources/Exceptions/DebuggableError.hpp tests/CPU/Math/testOthersMath.cpp tests/testRectangleMemory.cpp -) + tests/CPU/Math/testCMP.cpp) # include criterion & coverage target_link_libraries(unit_tests criterion -lgcov) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index e498095..a0598b6 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -389,6 +389,8 @@ namespace ComSquare::CPU int RTS(uint24_t, AddressingMode); //! @brief Return from subroutine long. int RTL(uint24_t, AddressingMode); + //! @brief Compare Accumulator with Memory. + int CMP(uint24_t, AddressingMode); //! @brief All the instructions of the CPU. //! @info Instructions are indexed by their opcode @@ -586,37 +588,37 @@ namespace ComSquare::CPU {&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::CMP, 6, "cmp", AddressingMode::DirectPageIndirectIndexedByX, 2}, // C1 {&CPU::REP, 3, "rep", AddressingMode::Immediate8bits, 2}, // C2 - {&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // C3 + {&CPU::CMP, 4, "cmp", AddressingMode::StackRelative, 2}, // C3 {&CPU::BRK, 7, "cpy #-#", AddressingMode::Implied, 2}, // C4 - {&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // C5 + {&CPU::CMP, 3, "cmp", AddressingMode::DirectPage, 2}, // C5 {&CPU::BRK, 7, "dec #-#", AddressingMode::Implied, 2}, // C6 - {&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // C7 + {&CPU::CMP, 6, "cmp", AddressingMode::DirectPageIndirectLong, 2}, // C7 {&CPU::INY, 2, "iny", AddressingMode::Implied, 1}, // C8 - {&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // C9 + {&CPU::CMP, 2, "cmp", AddressingMode::Implied, 2}, // C9 {&CPU::DEX, 2, "dex", AddressingMode::Implied, 1}, // 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::CMP, 4, "cmp", AddressingMode::Absolute, 3}, // CD {&CPU::BRK, 7, "dec #-#", AddressingMode::Implied, 2}, // CE - {&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // CF + {&CPU::CMP, 6, "cmp", AddressingMode::AbsoluteLong, 4}, // 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::CMP, 5, "cmp", AddressingMode::DirectPageIndirectIndexedByY, 2}, // D1 + {&CPU::CMP, 5, "cmp", AddressingMode::DirectPageIndirect, 2}, // D2 + {&CPU::CMP, 7, "cmp", AddressingMode::StackRelativeIndirectIndexedByY, 2}, // D3 {&CPU::BRK, 7, "pei #-#", AddressingMode::Implied, 2}, // D4 - {&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // D5 + {&CPU::CMP, 4, "cmp", AddressingMode::DirectPageIndexedByX, 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::CMP, 6, "cmp", AddressingMode::DirectPageIndirectIndexedByYLong, 2}, // D7 + {&CPU::CLD, 2, "cld", AddressingMode::Implied, 2}, // D8 + {&CPU::CMP, 4, "cmp", AddressingMode::AbsoluteIndexedByY, 3}, // 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::CMP, 4, "cmp", AddressingMode::AbsoluteIndexedByX, 3}, // DD {&CPU::BRK, 7, "dec #-#", AddressingMode::Implied, 2}, // DE - {&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // DF + {&CPU::CMP, 5, "cmp", AddressingMode::AbsoluteIndexedByXLong, 4}, // 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 diff --git a/sources/CPU/Instructions/MathematicalOperations.cpp b/sources/CPU/Instructions/MathematicalOperations.cpp index 4872cbb..776a810 100644 --- a/sources/CPU/Instructions/MathematicalOperations.cpp +++ b/sources/CPU/Instructions/MathematicalOperations.cpp @@ -147,4 +147,41 @@ namespace ComSquare::CPU this->_registers.p.n = this->_registers.y & negativeMask; return 0; } + + int CPU::CMP(uint24_t valueAddr, AddressingMode mode) + { + unsigned negativeMask = this->_registers.p.m ? 0x80u : 0x8000u; + unsigned value = this->_bus->read(valueAddr); + if (!this->_registers.p.m) + value += this->_bus->read(valueAddr + 1) << 8u; + unsigned result = this->_registers.a - value; + if (this->_registers.p.m) + result %= 0x100; + + this->_registers.p.n = result & negativeMask; + this->_registers.p.z = result == 0; + this->_registers.p.c = this->_registers.a >= result; + + 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/tests/CPU/Math/testCMP.cpp b/tests/CPU/Math/testCMP.cpp new file mode 100644 index 0000000..47968d4 --- /dev/null +++ b/tests/CPU/Math/testCMP.cpp @@ -0,0 +1,73 @@ +// +// Created by anonymus-raccoon on 4/6/20. +// + +#include +#include +#include "../../tests.hpp" +#include "../../../sources/SNES.hpp" +using namespace ComSquare; + +Test(CMP, underflow) +{ + Init() + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.a = 0; + snes.wram->_data[0] = 0x1; + snes.cpu->CMP(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.p.c, false, "The carry flags should not be set."); + cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flags should be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(CMP, zero) +{ + Init() + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.a = 0x5; + snes.wram->_data[0] = 0x5; + snes.cpu->CMP(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flags should be set."); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flags should not be set."); + cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flags should be set."); +} + +Test(CMP, nativeModeZero) +{ + Init() + snes.cpu->_registers.p.m = false; + snes.cpu->_registers.a = 0x5000; + snes.wram->_data[0] = 0x00; + snes.wram->_data[1] = 0x50; + snes.cpu->CMP(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flags should be set."); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flags should not be set."); + cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flags should be set."); +} + +Test(CMP, nativeModeNothing) +{ + Init() + snes.cpu->_registers.p.m = false; + snes.cpu->_registers.a = 0x8000; + snes.wram->_data[0] = 0x00; + snes.wram->_data[1] = 0x50; + snes.cpu->CMP(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flags should be set."); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flags should not be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(CMP, negative) +{ + Init() + snes.cpu->_registers.p.m = false; + snes.cpu->_registers.a = 0xB000; + snes.wram->_data[0] = 0x00; + snes.wram->_data[1] = 0x10; + snes.cpu->CMP(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flags should be set."); + cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flags should be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + From 055e7d29bdfc3359494b5a785b9cf628c9bb8e31 Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Mon, 6 Apr 2020 17:19:04 +0200 Subject: [PATCH 16/26] Cleaning up the CPX/CPY/INX/INY --- sources/CPU/CPU.hpp | 14 ++-- .../CPU/Instructions/InternalInstruction.cpp | 66 ------------------ .../Instructions/MathematicalOperations.cpp | 67 +++++++++++++++++++ 3 files changed, 74 insertions(+), 73 deletions(-) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index a0598b6..a00e72b 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -587,19 +587,19 @@ namespace ComSquare::CPU {&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::CPY, 2, "cpy", AddressingMode::ImmediateForX, 2}, // C0 {&CPU::CMP, 6, "cmp", AddressingMode::DirectPageIndirectIndexedByX, 2}, // C1 {&CPU::REP, 3, "rep", AddressingMode::Immediate8bits, 2}, // C2 {&CPU::CMP, 4, "cmp", AddressingMode::StackRelative, 2}, // C3 - {&CPU::BRK, 7, "cpy #-#", AddressingMode::Implied, 2}, // C4 + {&CPU::CPY, 3, "cpy", AddressingMode::DirectPage, 2}, // C4 {&CPU::CMP, 3, "cmp", AddressingMode::DirectPage, 2}, // C5 {&CPU::BRK, 7, "dec #-#", AddressingMode::Implied, 2}, // C6 {&CPU::CMP, 6, "cmp", AddressingMode::DirectPageIndirectLong, 2}, // C7 {&CPU::INY, 2, "iny", AddressingMode::Implied, 1}, // C8 - {&CPU::CMP, 2, "cmp", AddressingMode::Implied, 2}, // C9 + {&CPU::CMP, 2, "cmp", AddressingMode::ImmediateForA, 2}, // C9 {&CPU::DEX, 2, "dex", AddressingMode::Implied, 1}, // CA {&CPU::BRK, 7, "wai #-#", AddressingMode::Implied, 2}, // CB - {&CPU::BRK, 7, "cpy #-#", AddressingMode::Implied, 2}, // CC + {&CPU::CPY, 4, "cpy", AddressingMode::Absolute, 3}, // CC {&CPU::CMP, 4, "cmp", AddressingMode::Absolute, 3}, // CD {&CPU::BRK, 7, "dec #-#", AddressingMode::Implied, 2}, // CE {&CPU::CMP, 6, "cmp", AddressingMode::AbsoluteLong, 4}, // CF @@ -619,11 +619,11 @@ namespace ComSquare::CPU {&CPU::CMP, 4, "cmp", AddressingMode::AbsoluteIndexedByX, 3}, // DD {&CPU::BRK, 7, "dec #-#", AddressingMode::Implied, 2}, // DE {&CPU::CMP, 5, "cmp", AddressingMode::AbsoluteIndexedByXLong, 4}, // DF - {&CPU::BRK, 7, "cpx #-#", AddressingMode::Implied, 2}, // E0 + {&CPU::CPX, 2, "cpx", AddressingMode::ImmediateForX, 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::CPX, 3, "cpx", AddressingMode::DirectPage, 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 @@ -631,7 +631,7 @@ namespace ComSquare::CPU {&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::CPX, 4, "cpx", AddressingMode::Absolute, 3}, // 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 diff --git a/sources/CPU/Instructions/InternalInstruction.cpp b/sources/CPU/Instructions/InternalInstruction.cpp index d553621..1fc020a 100644 --- a/sources/CPU/Instructions/InternalInstruction.cpp +++ b/sources/CPU/Instructions/InternalInstruction.cpp @@ -199,72 +199,6 @@ namespace ComSquare::CPU return 0; } - int CPU::INX(uint24_t, AddressingMode) - { - this->_registers.x++; - - if (this->_registers.p.x_b) - this->_registers.x %= 0x100; - - 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; - } - - int CPU::INY(uint24_t, AddressingMode) - { - this->_registers.y++; - - if (this->_registers.p.x_b) - this->_registers.y %= 0x100; - - 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; - } - - int CPU::CPX(uint24_t valueAddr, AddressingMode mode) - { - unsigned value = this->_bus->read(valueAddr++); - - if (this->_registers.p.x_b) { - uint8_t x = this->_registers.x; - x -= value; - this->_registers.p.z = x == 0; - this->_registers.p.n = x & 0x80u; - } else { - value += this->_bus->read(valueAddr) << 8u; - uint16_t x = this->_registers.x; - x -= value; - this->_registers.p.z = x == 0; - 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); - } - - int CPU::CPY(uint24_t valueAddr, AddressingMode mode) - { - unsigned value = this->_bus->read(valueAddr++); - - this->_registers.p.c = this->_registers.y >= value; - if (this->_registers.p.x_b) { - uint8_t y = this->_registers.y; - y -= value; - this->_registers.p.z = y == 0; - this->_registers.p.n = y & 0x80u; - } else { - value += this->_bus->read(valueAddr) << 8u; - uint16_t y = this->_registers.y; - y -= value; - this->_registers.p.z = y == 0; - this->_registers.p.n = y & 0x8000u; - } - return !this->_registers.p.x_b + (mode == DirectPage && this->_registers.dl != 0); - } - int CPU::BCC(uint24_t valueAddr, AddressingMode) { if (!this->_registers.p.c) diff --git a/sources/CPU/Instructions/MathematicalOperations.cpp b/sources/CPU/Instructions/MathematicalOperations.cpp index 776a810..070229b 100644 --- a/sources/CPU/Instructions/MathematicalOperations.cpp +++ b/sources/CPU/Instructions/MathematicalOperations.cpp @@ -184,4 +184,71 @@ namespace ComSquare::CPU } return cycles; } + + + int CPU::INX(uint24_t, AddressingMode) + { + this->_registers.x++; + + if (this->_registers.p.x_b) + this->_registers.x %= 0x100; + + 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; + } + + int CPU::INY(uint24_t, AddressingMode) + { + this->_registers.y++; + + if (this->_registers.p.x_b) + this->_registers.y %= 0x100; + + 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; + } + + int CPU::CPX(uint24_t valueAddr, AddressingMode mode) + { + unsigned value = this->_bus->read(valueAddr++); + + if (this->_registers.p.x_b) { + uint8_t x = this->_registers.x; + x -= value; + this->_registers.p.z = x == 0; + this->_registers.p.n = x & 0x80u; + } else { + value += this->_bus->read(valueAddr) << 8u; + uint16_t x = this->_registers.x; + x -= value; + this->_registers.p.z = x == 0; + 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); + } + + int CPU::CPY(uint24_t valueAddr, AddressingMode mode) + { + unsigned value = this->_bus->read(valueAddr++); + + this->_registers.p.c = this->_registers.y >= value; + if (this->_registers.p.x_b) { + uint8_t y = this->_registers.y; + y -= value; + this->_registers.p.z = y == 0; + this->_registers.p.n = y & 0x80u; + } else { + value += this->_bus->read(valueAddr) << 8u; + uint16_t y = this->_registers.y; + y -= value; + this->_registers.p.z = y == 0; + this->_registers.p.n = y & 0x8000u; + } + return !this->_registers.p.x_b + (mode == DirectPage && this->_registers.dl != 0); + } } \ No newline at end of file From 724a2ca6169ee7c3a8505cb3a2561a98df91543c Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Mon, 6 Apr 2020 17:29:06 +0200 Subject: [PATCH 17/26] Implementing the AND --- sources/CPU/CPU.hpp | 30 ++++++++-------- sources/CPU/Instructions/BitsInstructions.cpp | 32 ----------------- .../Instructions/MathematicalOperations.cpp | 34 +++++++++++++++++++ 3 files changed, 49 insertions(+), 47 deletions(-) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index a00e72b..1f6dd1c 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -428,37 +428,37 @@ namespace ComSquare::CPU {&CPU::BRK, 7, "asl #-#", AddressingMode::Implied, 2}, // 1E {&CPU::ORA, 5, "ora", AddressingMode::AbsoluteIndexedByXLong, 4}, // 1F {&CPU::JSR, 6, "jsr", AddressingMode::Absolute, 3}, // 20 - {&CPU::BRK, 7, "and #-#", AddressingMode::Implied, 2}, // 21 + {&CPU::AND, 6, "and", AddressingMode::DirectPageIndirectIndexedByX, 2}, // 21 {&CPU::JSL, 8, "jsl", AddressingMode::AbsoluteLong, 4}, // 22 - {&CPU::BRK, 7, "and #-#", AddressingMode::Implied, 2}, // 23 + {&CPU::AND, 4, "and", AddressingMode::StackRelative, 2}, // 23 {&CPU::BRK, 7, "bit #-#", AddressingMode::Implied, 2}, // 24 - {&CPU::BRK, 7, "and #-#", AddressingMode::Implied, 2}, // 25 + {&CPU::AND, 3, "and", AddressingMode::DirectPage, 2}, // 25 {&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 26 - {&CPU::BRK, 7, "and #-#", AddressingMode::Implied, 2}, // 27 + {&CPU::AND, 6, "and", AddressingMode::DirectPageIndirectLong, 2}, // 27 {&CPU::PLP, 4, "plp", AddressingMode::Implied, 1}, // 28 - {&CPU::BRK, 7, "and #-#", AddressingMode::Implied, 2}, // 29 + {&CPU::AND, 2, "and", AddressingMode::ImmediateForA, 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::AND, 4, "and", AddressingMode::Absolute, 3}, // 2D {&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 2E - {&CPU::BRK, 7, "and #-#", AddressingMode::Implied, 2}, // 2F + {&CPU::AND, 5, "and", AddressingMode::AbsoluteLong, 4}, // 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::AND, 5, "and", AddressingMode::DirectPageIndirectIndexedByY, 2}, // 31 + {&CPU::AND, 5, "and", AddressingMode::DirectPageIndirect, 2}, // 32 + {&CPU::AND, 7, "and", AddressingMode::StackRelativeIndirectIndexedByY, 2}, // 33 {&CPU::BRK, 7, "bit #-#", AddressingMode::Implied, 2}, // 34 - {&CPU::BRK, 7, "and #-#", AddressingMode::Implied, 2}, // 35 + {&CPU::AND, 4, "and", AddressingMode::DirectPageIndexedByX, 2}, // 35 {&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 36 - {&CPU::BRK, 7, "and #-#", AddressingMode::Implied, 2}, // 37 + {&CPU::AND, 6, "and", AddressingMode::DirectPageIndirectIndexedByYLong, 2}, // 37 {&CPU::SEC, 2, "sec", AddressingMode::Implied, 1}, // 38 - {&CPU::BRK, 7, "and #-#", AddressingMode::Implied, 2}, // 39 + {&CPU::AND, 4, "and", AddressingMode::AbsoluteIndexedByY, 3}, // 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::AND, 4, "and", AddressingMode::AbsoluteIndexedByX, 3}, // 3D {&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 3E - {&CPU::BRK, 7, "and #-#", AddressingMode::Implied, 2}, // 3F + {&CPU::AND, 5, "and", AddressingMode::AbsoluteIndexedByXLong, 4}, // 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 diff --git a/sources/CPU/Instructions/BitsInstructions.cpp b/sources/CPU/Instructions/BitsInstructions.cpp index d3ecefa..cf6cd8a 100644 --- a/sources/CPU/Instructions/BitsInstructions.cpp +++ b/sources/CPU/Instructions/BitsInstructions.cpp @@ -7,37 +7,5 @@ namespace ComSquare::CPU { - int CPU::AND(uint24_t valueAddr, AddressingMode mode) - { - unsigned negativeMask = this->_isEmulationMode ? 0x80u : 0x8000u; - unsigned value = this->_bus->read(valueAddr); - if (!this->_registers.p.m) - value += this->_bus->read(valueAddr + 1) << 8u; - 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/MathematicalOperations.cpp b/sources/CPU/Instructions/MathematicalOperations.cpp index 070229b..35af999 100644 --- a/sources/CPU/Instructions/MathematicalOperations.cpp +++ b/sources/CPU/Instructions/MathematicalOperations.cpp @@ -251,4 +251,38 @@ namespace ComSquare::CPU } return !this->_registers.p.x_b + (mode == DirectPage && this->_registers.dl != 0); } + + int CPU::AND(uint24_t valueAddr, AddressingMode mode) + { + unsigned negativeMask = this->_isEmulationMode ? 0x80u : 0x8000u; + unsigned value = this->_bus->read(valueAddr); + if (!this->_registers.p.m) + value += this->_bus->read(valueAddr + 1) << 8u; + + 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 From 5ded0b44e8741c5dc567babef834331f3ea3a81e Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Mon, 6 Apr 2020 17:59:25 +0200 Subject: [PATCH 18/26] Implementing the INC --- sources/CPU/CPU.hpp | 12 ++-- .../Instructions/MathematicalOperations.cpp | 42 +++++++++++- tests/CPU/Math/testOthersMath.cpp | 66 +++++++++++++++++++ 3 files changed, 114 insertions(+), 6 deletions(-) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index 1f6dd1c..7159174 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -391,6 +391,8 @@ namespace ComSquare::CPU int RTL(uint24_t, AddressingMode); //! @brief Compare Accumulator with Memory. int CMP(uint24_t, AddressingMode); + //! @brief Increment + int INC(uint24_t, AddressingMode); //! @brief All the instructions of the CPU. //! @info Instructions are indexed by their opcode @@ -421,7 +423,7 @@ namespace ComSquare::CPU {&CPU::ORA, 6, "ora", AddressingMode::DirectPageIndirectIndexedByYLong, 2}, // 17 {&CPU::CLC, 2, "clc", AddressingMode::Implied, 1}, // 18 {&CPU::ORA, 4, "ora", AddressingMode::AbsoluteIndexedByY, 3}, // 19 - {&CPU::BRK, 7, "inc #-#", AddressingMode::Implied, 2}, // 1A + {&CPU::INC, 2, "inc", AddressingMode::Implied, 1}, // 1A {&CPU::BRK, 7, "tcs #-#", AddressingMode::Implied, 2}, // 1B {&CPU::BRK, 7, "trb #-#", AddressingMode::Implied, 2}, // 1C {&CPU::ORA, 4, "ora", AddressingMode::AbsoluteIndexedByX, 3}, // 1D @@ -625,7 +627,7 @@ namespace ComSquare::CPU {&CPU::SBC, 4, "sbc", AddressingMode::StackRelative, 2}, // E3 {&CPU::CPX, 3, "cpx", AddressingMode::DirectPage, 2}, // E4 {&CPU::SBC, 3, "sbc", AddressingMode::DirectPage, 2}, // E5 - {&CPU::BRK, 7, "inc #-#", AddressingMode::Implied, 2}, // E6 + {&CPU::INC, 5, "inc", AddressingMode::DirectPage, 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 @@ -633,7 +635,7 @@ namespace ComSquare::CPU {&CPU::BRK, 7, "xba #-#", AddressingMode::Implied, 2}, // EB {&CPU::CPX, 4, "cpx", AddressingMode::Absolute, 3}, // EC {&CPU::SBC, 4, "sbc", AddressingMode::Absolute, 3}, // ED - {&CPU::BRK, 7, "inc #-#", AddressingMode::Implied, 2}, // EE + {&CPU::INC, 6, "inc", AddressingMode::Absolute, 3}, // 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 @@ -641,7 +643,7 @@ namespace ComSquare::CPU {&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::INC, 6, "inc", AddressingMode::DirectPageIndexedByX, 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 @@ -649,7 +651,7 @@ namespace ComSquare::CPU {&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::INC, 7, "inc", AddressingMode::AbsoluteIndexedByX, 3}, // FE {&CPU::SBC, 5, "sbc", AddressingMode::AbsoluteIndexedByXLong, 4}, // FF }; public: diff --git a/sources/CPU/Instructions/MathematicalOperations.cpp b/sources/CPU/Instructions/MathematicalOperations.cpp index 35af999..df64f01 100644 --- a/sources/CPU/Instructions/MathematicalOperations.cpp +++ b/sources/CPU/Instructions/MathematicalOperations.cpp @@ -254,7 +254,7 @@ namespace ComSquare::CPU int CPU::AND(uint24_t valueAddr, AddressingMode mode) { - unsigned negativeMask = this->_isEmulationMode ? 0x80u : 0x8000u; + unsigned negativeMask = this->_registers.p.m ? 0x80u : 0x8000u; unsigned value = this->_bus->read(valueAddr); if (!this->_registers.p.m) value += this->_bus->read(valueAddr + 1) << 8u; @@ -285,4 +285,44 @@ namespace ComSquare::CPU } return cycles; } + + int CPU::INC(uint24_t valueAddr, AddressingMode mode) + { + unsigned negativeMask = this->_registers.p.m ? 0x80u : 0x8000u; + + unsigned result; + if (mode == Implied) { + this->_registers.a++; + if (this->_registers.p.m) + this->_registers.ah = 0; + result = this->_registers.a; + } else if (!this->_registers.p.m) { + result = this->_bus->read(valueAddr); + result += this->_bus->read(valueAddr + 1) << 8u; + result = (uint16_t)(result + 1); + this->_bus->write(valueAddr, result); + this->_bus->write(valueAddr + 1, result << 8u); + } else { + result = this->_bus->read(valueAddr); + result = (uint8_t)(result + 1); + this->_bus->write(valueAddr, result); + } + + this->_registers.p.z = result == 0; + this->_registers.p.n = result & negativeMask; + + switch (mode) { + case Implied: + return 0; + case Absolute: + return this->_registers.p.m == 0 ? 2 : 0; + case DirectPage: + case DirectPageIndexedByX: + return (this->_registers.p.m == 0 ? 2 : 0) + this->_registers.dl != 0 ; + case AbsoluteIndexedByX: + return (this->_registers.p.m == 0 ? 2 : 0) + this->_hasIndexCrossedPageBoundary; + default: + return 0; + } + } } \ No newline at end of file diff --git a/tests/CPU/Math/testOthersMath.cpp b/tests/CPU/Math/testOthersMath.cpp index 3a65f92..48364f2 100644 --- a/tests/CPU/Math/testOthersMath.cpp +++ b/tests/CPU/Math/testOthersMath.cpp @@ -152,4 +152,70 @@ Test(ORA, zero) cr_assert_eq(snes.cpu->_registers.a, 0x00, "The accumulator's value should be 0x00 but it was 0x%x.", snes.cpu->_registers.a); cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flags should not be set."); cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flags should be set."); +} + +Test(INC, simple) +{ + Init() + snes.cpu->_registers.p.m = true; + snes.wram->_data[0] = 0x56; + snes.cpu->INC(0x0, ComSquare::CPU::AddressingMode::Absolute); + cr_assert_eq(snes.wram->_data[0], 0x57, "The incremented value should be 0x57 but it was 0x%x.", snes.wram->_data[0]); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flags should not be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(INC, negative) +{ + Init() + snes.cpu->_registers.p.m = true; + snes.wram->_data[0] = 0x7F; + snes.cpu->INC(0x0, ComSquare::CPU::AddressingMode::Absolute); + cr_assert_eq(snes.wram->_data[0], 0x80, "The incremented value should be 0x80 but it was 0x%x.", snes.wram->_data[0]); + cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flags should be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(INC, accumulator) +{ + Init() + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.a = 0x56; + snes.cpu->INC(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0x57, "The incremented value should be 0x57 but it was 0x%x.", snes.cpu->_registers.a); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flags should not be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(INC, negativeAccumulator) +{ + Init() + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.a = 0x7F; + snes.cpu->INC(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0x80, "The incremented value should be 0x80 but it was 0x%x.", snes.cpu->_registers.a); + cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flags should be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(INC, nativeAccumulator) +{ + Init() + snes.cpu->_registers.p.m = false; + snes.cpu->_registers.a = 0x5600; + snes.cpu->INC(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0x5601, "The incremented value should be 0x5601 but it was 0x%x.", snes.cpu->_registers.a); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flags should not be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(INC, negativeNativeAccumulator) +{ + Init() + snes.cpu->_registers.p.m = false; + snes.cpu->_registers.a = 0x8FFF; + snes.cpu->INC(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0x9000, "The incremented value should be 0x9000 but it was 0x%x.", snes.cpu->_registers.a); + cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flags should be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); } \ No newline at end of file From f675e453858184b7e4c10e7d27c6d81eefd07fc8 Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Mon, 6 Apr 2020 18:05:00 +0200 Subject: [PATCH 19/26] Implementing DEC --- sources/CPU/CPU.hpp | 12 ++-- .../Instructions/MathematicalOperations.cpp | 40 +++++++++++ tests/CPU/Math/testOthersMath.cpp | 66 +++++++++++++++++++ 3 files changed, 113 insertions(+), 5 deletions(-) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index 7159174..a478d35 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -393,6 +393,8 @@ namespace ComSquare::CPU int CMP(uint24_t, AddressingMode); //! @brief Increment int INC(uint24_t, AddressingMode); + //! @brief Decrement + int DEC(uint24_t, AddressingMode); //! @brief All the instructions of the CPU. //! @info Instructions are indexed by their opcode @@ -455,7 +457,7 @@ namespace ComSquare::CPU {&CPU::AND, 6, "and", AddressingMode::DirectPageIndirectIndexedByYLong, 2}, // 37 {&CPU::SEC, 2, "sec", AddressingMode::Implied, 1}, // 38 {&CPU::AND, 4, "and", AddressingMode::AbsoluteIndexedByY, 3}, // 39 - {&CPU::BRK, 7, "dec #-#", AddressingMode::Implied, 2}, // 3A + {&CPU::DEC, 2, "dec", AddressingMode::Implied, 1}, // 3A {&CPU::BRK, 7, "tsc #-#", AddressingMode::Implied, 2}, // 3B {&CPU::BRK, 7, "bit #-#", AddressingMode::Implied, 2}, // 3C {&CPU::AND, 4, "and", AddressingMode::AbsoluteIndexedByX, 3}, // 3D @@ -595,7 +597,7 @@ namespace ComSquare::CPU {&CPU::CMP, 4, "cmp", AddressingMode::StackRelative, 2}, // C3 {&CPU::CPY, 3, "cpy", AddressingMode::DirectPage, 2}, // C4 {&CPU::CMP, 3, "cmp", AddressingMode::DirectPage, 2}, // C5 - {&CPU::BRK, 7, "dec #-#", AddressingMode::Implied, 2}, // C6 + {&CPU::DEC, 5, "dec", AddressingMode::DirectPage, 2}, // C6 {&CPU::CMP, 6, "cmp", AddressingMode::DirectPageIndirectLong, 2}, // C7 {&CPU::INY, 2, "iny", AddressingMode::Implied, 1}, // C8 {&CPU::CMP, 2, "cmp", AddressingMode::ImmediateForA, 2}, // C9 @@ -603,7 +605,7 @@ namespace ComSquare::CPU {&CPU::BRK, 7, "wai #-#", AddressingMode::Implied, 2}, // CB {&CPU::CPY, 4, "cpy", AddressingMode::Absolute, 3}, // CC {&CPU::CMP, 4, "cmp", AddressingMode::Absolute, 3}, // CD - {&CPU::BRK, 7, "dec #-#", AddressingMode::Implied, 2}, // CE + {&CPU::DEC, 6, "dec", AddressingMode::Absolute, 3}, // CE {&CPU::CMP, 6, "cmp", AddressingMode::AbsoluteLong, 4}, // CF {&CPU::BNE, 2, "bne", AddressingMode::Implied, 2}, // D0 {&CPU::CMP, 5, "cmp", AddressingMode::DirectPageIndirectIndexedByY, 2}, // D1 @@ -611,7 +613,7 @@ namespace ComSquare::CPU {&CPU::CMP, 7, "cmp", AddressingMode::StackRelativeIndirectIndexedByY, 2}, // D3 {&CPU::BRK, 7, "pei #-#", AddressingMode::Implied, 2}, // D4 {&CPU::CMP, 4, "cmp", AddressingMode::DirectPageIndexedByX, 2}, // D5 - {&CPU::BRK, 7, "dec #-#", AddressingMode::Implied, 2}, // D6 + {&CPU::DEC, 6, "dec", AddressingMode::DirectPageIndexedByX, 2}, // D6 {&CPU::CMP, 6, "cmp", AddressingMode::DirectPageIndirectIndexedByYLong, 2}, // D7 {&CPU::CLD, 2, "cld", AddressingMode::Implied, 2}, // D8 {&CPU::CMP, 4, "cmp", AddressingMode::AbsoluteIndexedByY, 3}, // D9 @@ -619,7 +621,7 @@ namespace ComSquare::CPU {&CPU::BRK, 7, "stp #-#", AddressingMode::Implied, 2}, // DB {&CPU::JML, 7, "jml", AddressingMode::AbsoluteIndirectLong, 2}, // DC {&CPU::CMP, 4, "cmp", AddressingMode::AbsoluteIndexedByX, 3}, // DD - {&CPU::BRK, 7, "dec #-#", AddressingMode::Implied, 2}, // DE + {&CPU::DEC, 7, "dec", AddressingMode::AbsoluteIndexedByX, 3}, // DE {&CPU::CMP, 5, "cmp", AddressingMode::AbsoluteIndexedByXLong, 4}, // DF {&CPU::CPX, 2, "cpx", AddressingMode::ImmediateForX, 2}, // E0 {&CPU::SBC, 6, "sbc", AddressingMode::DirectPageIndirectIndexedByX, 2}, // E1 diff --git a/sources/CPU/Instructions/MathematicalOperations.cpp b/sources/CPU/Instructions/MathematicalOperations.cpp index df64f01..ff2d63e 100644 --- a/sources/CPU/Instructions/MathematicalOperations.cpp +++ b/sources/CPU/Instructions/MathematicalOperations.cpp @@ -325,4 +325,44 @@ namespace ComSquare::CPU return 0; } } + + int CPU::DEC(uint24_t valueAddr, AddressingMode mode) + { + unsigned negativeMask = this->_registers.p.m ? 0x80u : 0x8000u; + + unsigned result; + if (mode == Implied) { + this->_registers.a--; + if (this->_registers.p.m) + this->_registers.ah = 0; + result = this->_registers.a; + } else if (!this->_registers.p.m) { + result = this->_bus->read(valueAddr); + result += this->_bus->read(valueAddr + 1) << 8u; + result = (uint16_t)(result - 1); + this->_bus->write(valueAddr, result); + this->_bus->write(valueAddr + 1, result << 8u); + } else { + result = this->_bus->read(valueAddr); + result = (uint8_t)(result - 1); + this->_bus->write(valueAddr, result); + } + + this->_registers.p.z = result == 0; + this->_registers.p.n = result & negativeMask; + + switch (mode) { + case Implied: + return 0; + case Absolute: + return this->_registers.p.m == 0 ? 2 : 0; + case DirectPage: + case DirectPageIndexedByX: + return (this->_registers.p.m == 0 ? 2 : 0) + this->_registers.dl != 0 ; + case AbsoluteIndexedByX: + return (this->_registers.p.m == 0 ? 2 : 0) + this->_hasIndexCrossedPageBoundary; + default: + return 0; + } + } } \ No newline at end of file diff --git a/tests/CPU/Math/testOthersMath.cpp b/tests/CPU/Math/testOthersMath.cpp index 48364f2..1f293aa 100644 --- a/tests/CPU/Math/testOthersMath.cpp +++ b/tests/CPU/Math/testOthersMath.cpp @@ -218,4 +218,70 @@ Test(INC, negativeNativeAccumulator) cr_assert_eq(snes.cpu->_registers.a, 0x9000, "The incremented value should be 0x9000 but it was 0x%x.", snes.cpu->_registers.a); cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flags should be set."); cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(DEC, simple) +{ + Init() + snes.cpu->_registers.p.m = true; + snes.wram->_data[0] = 0x58; + snes.cpu->DEC(0x0, ComSquare::CPU::AddressingMode::Absolute); + cr_assert_eq(snes.wram->_data[0], 0x57, "The incremented value should be 0x57 but it was 0x%x.", snes.wram->_data[0]); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flags should not be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(DEC, negative) +{ + Init() + snes.cpu->_registers.p.m = true; + snes.wram->_data[0] = 0x81; + snes.cpu->DEC(0x0, ComSquare::CPU::AddressingMode::Absolute); + cr_assert_eq(snes.wram->_data[0], 0x80, "The incremented value should be 0x80 but it was 0x%x.", snes.wram->_data[0]); + cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flags should be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(DEC, accumulator) +{ + Init() + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.a = 0x58; + snes.cpu->DEC(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0x57, "The incremented value should be 0x57 but it was 0x%x.", snes.cpu->_registers.a); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flags should not be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(DEC, negativeAccumulator) +{ + Init() + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.a = 0x81; + snes.cpu->DEC(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0x80, "The incremented value should be 0x80 but it was 0x%x.", snes.cpu->_registers.a); + cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flags should be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(DEC, nativeAccumulator) +{ + Init() + snes.cpu->_registers.p.m = false; + snes.cpu->_registers.a = 0x5602; + snes.cpu->DEC(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0x5601, "The incremented value should be 0x5601 but it was 0x%x.", snes.cpu->_registers.a); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flags should not be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(DEC, negativeNativeAccumulator) +{ + Init() + snes.cpu->_registers.p.m = false; + snes.cpu->_registers.a = 0x9001; + snes.cpu->DEC(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0x9000, "The incremented value should be 0x9000 but it was 0x%x.", snes.cpu->_registers.a); + cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flags should be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); } \ No newline at end of file From a40bfd1c3c840029fa9da18ee74729c39e64ec85 Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Mon, 6 Apr 2020 18:37:11 +0200 Subject: [PATCH 20/26] Implementing the EOR --- sources/CPU/CPU.hpp | 32 ++++++++-------- .../Instructions/MathematicalOperations.cpp | 33 ++++++++++++++++ tests/CPU/Math/testOthersMath.cpp | 38 ++++++++++++++++++- 3 files changed, 87 insertions(+), 16 deletions(-) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index a478d35..9a61baa 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -395,6 +395,8 @@ namespace ComSquare::CPU int INC(uint24_t, AddressingMode); //! @brief Decrement int DEC(uint24_t, AddressingMode); + //! @brief XOR, Exclusive OR accumulator with memory. + int EOR(uint24_t, AddressingMode); //! @brief All the instructions of the CPU. //! @info Instructions are indexed by their opcode @@ -464,37 +466,37 @@ namespace ComSquare::CPU {&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 3E {&CPU::AND, 5, "and", AddressingMode::AbsoluteIndexedByXLong, 4}, // 3F {&CPU::RTI, 6, "rti", AddressingMode::Implied, 1}, // 40 - {&CPU::BRK, 7, "eor #-#", AddressingMode::Implied, 2}, // 41 + {&CPU::EOR, 6, "eor", AddressingMode::DirectPageIndirectIndexedByX, 2}, // 41 {&CPU::BRK, 7, "wdm #-#", AddressingMode::Implied, 2}, // 42 - {&CPU::BRK, 7, "eor #-#", AddressingMode::Implied, 2}, // 43 + {&CPU::EOR, 4, "eor", AddressingMode::StackRelative, 2}, // 43 {&CPU::BRK, 7, "mvp #-#", AddressingMode::Implied, 2}, // 44 - {&CPU::BRK, 7, "eor #-#", AddressingMode::Implied, 2}, // 45 + {&CPU::EOR, 3, "eor", AddressingMode::DirectPage, 2}, // 45 {&CPU::BRK, 7, "lsr #-#", AddressingMode::Implied, 2}, // 46 - {&CPU::BRK, 7, "eor #-#", AddressingMode::Implied, 2}, // 47 + {&CPU::EOR, 6, "eor", AddressingMode::DirectPageIndirectLong, 2}, // 47 {&CPU::PHA, 3, "pha", AddressingMode::Implied, 1}, // 48 - {&CPU::BRK, 7, "eor #-#", AddressingMode::Implied, 2}, // 49 + {&CPU::EOR, 2, "eor", AddressingMode::ImmediateForA, 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::EOR, 4, "eor", AddressingMode::Absolute, 3}, // 4D {&CPU::BRK, 7, "lsr #-#", AddressingMode::Implied, 2}, // 4E - {&CPU::BRK, 7, "eor #-#", AddressingMode::Implied, 2}, // 4F + {&CPU::EOR, 5, "eor", AddressingMode::AbsoluteLong, 4}, // 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::EOR, 5, "eor", AddressingMode::DirectPageIndirectIndexedByY, 2}, // 51 + {&CPU::EOR, 5, "eor", AddressingMode::DirectPageIndirect, 2}, // 52 + {&CPU::EOR, 4, "eor", AddressingMode::StackRelativeIndirectIndexedByY, 2}, // 53 {&CPU::BRK, 7, "mvn #-#", AddressingMode::Implied, 2}, // 54 - {&CPU::BRK, 7, "eor #-#", AddressingMode::Implied, 2}, // 55 + {&CPU::EOR, 4, "eor", AddressingMode::DirectPageIndexedByX, 2}, // 55 {&CPU::BRK, 7, "lsr #-#", AddressingMode::Implied, 2}, // 56 - {&CPU::BRK, 7, "eor #-#", AddressingMode::Implied, 2}, // 57 + {&CPU::EOR, 6, "eor", AddressingMode::DirectPageIndirectIndexedByYLong, 2}, // 57 {&CPU::CLI, 2, "cli", AddressingMode::Implied, 1}, // 58 - {&CPU::BRK, 7, "eor #-#", AddressingMode::Implied, 2}, // 59 + {&CPU::EOR, 4, "eor", AddressingMode::AbsoluteIndexedByY, 3}, // 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::EOR, 4, "eor", AddressingMode::AbsoluteIndexedByX, 3}, // 5D {&CPU::BRK, 7, "lsr #-#", AddressingMode::Implied, 2}, // 5E - {&CPU::BRK, 7, "eor #-#", AddressingMode::Implied, 2}, // 5F + {&CPU::EOR, 5, "eor", AddressingMode::AbsoluteIndexedByXLong, 4}, // 5F {&CPU::RTL, 6, "rtl", AddressingMode::Implied, 1}, // 60 {&CPU::ADC, 6, "adc", AddressingMode::DirectPageIndirectIndexedByX, 2}, // 61 {&CPU::BRK, 7, "per #-#", AddressingMode::Implied, 2}, // 62 diff --git a/sources/CPU/Instructions/MathematicalOperations.cpp b/sources/CPU/Instructions/MathematicalOperations.cpp index ff2d63e..f723978 100644 --- a/sources/CPU/Instructions/MathematicalOperations.cpp +++ b/sources/CPU/Instructions/MathematicalOperations.cpp @@ -365,4 +365,37 @@ namespace ComSquare::CPU return 0; } } + + int CPU::EOR(uint24_t valueAddr, AddressingMode mode) + { + unsigned negativeMask = this->_registers.p.m ? 0x80u : 0x8000u; + unsigned value = this->_bus->read(valueAddr); + if (!this->_registers.p.m) + value += this->_bus->read(valueAddr + 1) << 8u; + this->_registers.a ^= value; + 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/tests/CPU/Math/testOthersMath.cpp b/tests/CPU/Math/testOthersMath.cpp index 1f293aa..294f4f9 100644 --- a/tests/CPU/Math/testOthersMath.cpp +++ b/tests/CPU/Math/testOthersMath.cpp @@ -284,4 +284,40 @@ Test(DEC, negativeNativeAccumulator) cr_assert_eq(snes.cpu->_registers.a, 0x9000, "The incremented value should be 0x9000 but it was 0x%x.", snes.cpu->_registers.a); cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flags should be set."); cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); -} \ No newline at end of file +} + +Test(EOR, simple) +{ + Init() + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.a = 0x80; + snes.wram->_data[0] = 0x0F; + snes.cpu->EOR(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0x8F, "The accumulator's value should be 0x8F but it was 0x%x.", snes.cpu->_registers.a); + cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flags should be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(EOR, simple2) +{ + Init() + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.a = 0x80; + snes.wram->_data[0] = 0xF0; + snes.cpu->EOR(0x00, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0x70, "The accumulator's value should be 0x70 but it was 0x%x.", snes.cpu->_registers.a); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flags should not be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(EOR, zero) +{ + Init() + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.a = 0x00; + snes.wram->_data[0] = 0x00; + snes.cpu->EOR(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0x00, "The accumulator's value should be 0x00 but it was 0x%x.", snes.cpu->_registers.a); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flags should not be set."); + cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flags should be set."); +} From f35427f28ac32d3ef0c81b848a357e711b6936cb Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Tue, 7 Apr 2020 22:40:53 +0200 Subject: [PATCH 21/26] Adding most of the transfer instructions --- sources/CPU/CPU.hpp | 28 +- .../CPU/Instructions/TransferRegisters.cpp | 76 ++++++ tests/CPU/TransferRegisters.cpp | 241 +++++++++++++++++- 3 files changed, 337 insertions(+), 8 deletions(-) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index 9a61baa..f928561 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -397,6 +397,20 @@ namespace ComSquare::CPU int DEC(uint24_t, AddressingMode); //! @brief XOR, Exclusive OR accumulator with memory. int EOR(uint24_t, AddressingMode); + //! @brief Transfer 16 bit A to DP + int TCD(uint24_t, AddressingMode); + //! @brief Transfer 16 bit A to SP + int TCS(uint24_t, AddressingMode); + //! @brief Transfer DP to 16 bit A + int TDC(uint24_t, AddressingMode); + //! @brief Transfer DP to 16 bit A + int TSC(uint24_t, AddressingMode); + //! @brief Transfer SP to X + int TSX(uint24_t, AddressingMode); + //! @brief Transfer X to A + int TXA(uint24_t, AddressingMode); + //! @brief Transfer Y to A + int TYA(uint24_t, AddressingMode); //! @brief All the instructions of the CPU. //! @info Instructions are indexed by their opcode @@ -428,7 +442,7 @@ namespace ComSquare::CPU {&CPU::CLC, 2, "clc", AddressingMode::Implied, 1}, // 18 {&CPU::ORA, 4, "ora", AddressingMode::AbsoluteIndexedByY, 3}, // 19 {&CPU::INC, 2, "inc", AddressingMode::Implied, 1}, // 1A - {&CPU::BRK, 7, "tcs #-#", AddressingMode::Implied, 2}, // 1B + {&CPU::TCS, 2, "tcs", AddressingMode::Implied, 1}, // 1B {&CPU::BRK, 7, "trb #-#", AddressingMode::Implied, 2}, // 1C {&CPU::ORA, 4, "ora", AddressingMode::AbsoluteIndexedByX, 3}, // 1D {&CPU::BRK, 7, "asl #-#", AddressingMode::Implied, 2}, // 1E @@ -460,7 +474,7 @@ namespace ComSquare::CPU {&CPU::SEC, 2, "sec", AddressingMode::Implied, 1}, // 38 {&CPU::AND, 4, "and", AddressingMode::AbsoluteIndexedByY, 3}, // 39 {&CPU::DEC, 2, "dec", AddressingMode::Implied, 1}, // 3A - {&CPU::BRK, 7, "tsc #-#", AddressingMode::Implied, 2}, // 3B + {&CPU::TSC, 2, "tsc", AddressingMode::Implied, 1}, // 3B {&CPU::BRK, 7, "bit #-#", AddressingMode::Implied, 2}, // 3C {&CPU::AND, 4, "and", AddressingMode::AbsoluteIndexedByX, 3}, // 3D {&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 3E @@ -492,7 +506,7 @@ namespace ComSquare::CPU {&CPU::CLI, 2, "cli", AddressingMode::Implied, 1}, // 58 {&CPU::EOR, 4, "eor", AddressingMode::AbsoluteIndexedByY, 3}, // 59 {&CPU::PHY, 3, "phy", AddressingMode::Implied, 1}, // 5A - {&CPU::BRK, 7, "tcd #-#", AddressingMode::Implied, 2}, // 5B + {&CPU::TCD, 2, "tcd", AddressingMode::Implied, 1}, // 5B {&CPU::JML, 4, "jml", AddressingMode::Implied, 4}, // 5C {&CPU::EOR, 4, "eor", AddressingMode::AbsoluteIndexedByX, 3}, // 5D {&CPU::BRK, 7, "lsr #-#", AddressingMode::Implied, 2}, // 5E @@ -524,7 +538,7 @@ namespace ComSquare::CPU {&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::TDC, 2, "tdc", AddressingMode::Implied, 1}, // 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 @@ -539,7 +553,7 @@ namespace ComSquare::CPU {&CPU::STA, 6, "sta", AddressingMode::DirectPageIndirectLong, 2}, // 87 {&CPU::DEY, 2, "dey", AddressingMode::Implied, 1}, // 88 {&CPU::BRK, 7, "bit #-#", AddressingMode::Implied, 2}, // 89 - {&CPU::BRK, 7, "txa #-#", AddressingMode::Implied, 2}, // 8A + {&CPU::TXA, 2, "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 @@ -553,7 +567,7 @@ namespace ComSquare::CPU {&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::TYA, 2, "tya", AddressingMode::Implied, 1}, // 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 @@ -587,7 +601,7 @@ namespace ComSquare::CPU {&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::TSX, 2, "tsx", AddressingMode::Implied, 1}, // 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 diff --git a/sources/CPU/Instructions/TransferRegisters.cpp b/sources/CPU/Instructions/TransferRegisters.cpp index 4905071..6135ce3 100644 --- a/sources/CPU/Instructions/TransferRegisters.cpp +++ b/sources/CPU/Instructions/TransferRegisters.cpp @@ -48,4 +48,80 @@ namespace ComSquare::CPU } return 0; } + + int CPU::TCD(uint24_t, AddressingMode) + { + this->_registers.d = this->_registers.a; + this->_registers.p.n = this->_registers.d & 0x8000u; + this->_registers.p.z = this->_registers.d == 0; + return 0; + } + + int CPU::TCS(uint24_t, AddressingMode) + { + this->_registers.s = this->_registers.a; + if (this->_isEmulationMode) + this->_registers.sh = 1; + return 0; + } + + int CPU::TDC(uint24_t, AddressingMode) + { + this->_registers.a = this->_registers.d; + this->_registers.p.n = this->_registers.a & 0x8000u; + this->_registers.p.z = this->_registers.a == 0; + return 0; + } + + int CPU::TSC(uint24_t, AddressingMode) + { + this->_registers.a = this->_registers.s; + this->_registers.p.n = this->_registers.a & 0x8000u; + this->_registers.p.z = this->_registers.a == 0; + return 0; + } + + int CPU::TSX(uint24_t, AddressingMode) + { + unsigned negativeFlag = this->_registers.p.x_b ? 0x80u : 0x8000u; + + this->_registers.x = this->_registers.s; + if (this->_registers.p.x_b) + this->_registers.xh = 0; + this->_registers.p.n = this->_registers.x & negativeFlag; + this->_registers.p.z = this->_registers.x == 0; + return 0; + } + + int CPU::TXA(uint24_t, AddressingMode) + { + unsigned negativeFlag = this->_registers.p.m ? 0x80u : 0x8000u; + + if (this->_registers.p.m) + this->_registers.al = this->_registers.xl; + else { + this->_registers.a = this->_registers.x; + if (this->_registers.p.x_b) + this->_registers.ah = 0; + } + this->_registers.p.n = this->_registers.a & negativeFlag; + this->_registers.p.z = this->_registers.a == 0; + return 0; + } + + int CPU::TYA(uint24_t, AddressingMode) + { + unsigned negativeFlag = this->_registers.p.m ? 0x80u : 0x8000u; + + if (this->_registers.p.m) + this->_registers.al = this->_registers.yl; + else { + this->_registers.a = this->_registers.y; + if (this->_registers.p.x_b) + this->_registers.ah = 0; + } + this->_registers.p.n = this->_registers.a & negativeFlag; + this->_registers.p.z = this->_registers.a == 0; + return 0; + } } \ No newline at end of file diff --git a/tests/CPU/TransferRegisters.cpp b/tests/CPU/TransferRegisters.cpp index c840e2a..07cabcc 100644 --- a/tests/CPU/TransferRegisters.cpp +++ b/tests/CPU/TransferRegisters.cpp @@ -144,4 +144,243 @@ Test(TXS, 8bitsIndex) 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."); -} \ No newline at end of file +} + +Test(TCD, emulationMode) +{ + Init() + snes.cpu->_isEmulationMode = true; + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.d = 0x5656; + snes.cpu->_registers.a = 0xABCD; + snes.cpu->TCD(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.d, 0xABCD, "The direct page should be 0xABCD but it was %x", snes.cpu->_registers.d); + 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."); +} + +Test(TCD, zero) +{ + Init() + snes.cpu->_isEmulationMode = true; + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.d = 0x5656; + snes.cpu->_registers.a = 0x0; + snes.cpu->TCD(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.d, 0, "The direct page should be 0x0 but it was %x", snes.cpu->_registers.d); + 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."); +} + +Test(TCS, emulationMode) +{ + Init() + snes.cpu->_isEmulationMode = true; + snes.cpu->_registers.p.m = false; + snes.cpu->_registers.s = 0x0156; + snes.cpu->_registers.a = 0xABCD; + snes.cpu->TCS(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.s, 0x01CD, "The stack pointer should be 0x01CD but it was %x", snes.cpu->_registers.s); +} + +Test(TCS, native) +{ + Init() + snes.cpu->_isEmulationMode = false; + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.s = 0x0156; + snes.cpu->_registers.a = 0xABCD; + snes.cpu->TCS(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); +} + +Test(TDC, emulationMode) +{ + Init() + snes.cpu->_isEmulationMode = true; + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.d = 0xABCD; + snes.cpu->_registers.a = 0x5656; + snes.cpu->TDC(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0xABCD, "The accumulator should be 0xABCD but it was %x", snes.cpu->_registers.a); + 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."); +} + +Test(TDC, zero) +{ + Init() + snes.cpu->_isEmulationMode = true; + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.d = 0x0; + snes.cpu->_registers.a = 0x5656; + snes.cpu->TDC(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0, "The accumulator should be 0x0 but it was %x", snes.cpu->_registers.a); + 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."); +} + +Test(TSC, emulationMode) +{ + Init() + snes.cpu->_isEmulationMode = true; + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.s = 0xABCD; + snes.cpu->_registers.a = 0x5656; + snes.cpu->TSC(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0xABCD, "The accumulator should be 0xABCD but it was %x", snes.cpu->_registers.a); + 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."); +} + +Test(TSC, zero) +{ + Init() + snes.cpu->_isEmulationMode = true; + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.s = 0x0; + snes.cpu->_registers.a = 0x5656; + snes.cpu->TSC(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0, "The accumulator should be 0x0 but it was %x", snes.cpu->_registers.a); + 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."); +} + +Test(TSX, emulationMode) +{ + Init() + snes.cpu->_isEmulationMode = true; + snes.cpu->_registers.p.x_b = true; + snes.cpu->_registers.s = 0xABCD; + snes.cpu->_registers.x = 0x5656; + snes.cpu->TSX(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.x, 0x00CD, "The x index should be 0x00CD 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."); +} + +Test(TSX, native) +{ + Init() + snes.cpu->_isEmulationMode = false; + snes.cpu->_registers.p.x_b = false; + snes.cpu->_registers.s = 0x8F00; + snes.cpu->_registers.x = 0x5656; + snes.cpu->TSX(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.x, 0x8F00, "The x index should be 0x8F00 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."); +} + +Test(TXA, double8bits) +{ + Init() + snes.cpu->_isEmulationMode = true; + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.p.x_b = true; + snes.cpu->_registers.x = 0xABCD; + snes.cpu->_registers.a = 0x5656; + snes.cpu->TXA(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0x56CD, "The accumulator should be 0x56CD but it was %x", snes.cpu->_registers.a); + 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."); +} + +Test(TXA, index8bits) +{ + Init() + snes.cpu->_isEmulationMode = false; + snes.cpu->_registers.p.m = false; + snes.cpu->_registers.p.x_b = true; + snes.cpu->_registers.x = 0x0BCD; + snes.cpu->_registers.a = 0x5656; + snes.cpu->TXA(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0x00CD, "The accumulator should be 0x00CD but it was %x", snes.cpu->_registers.a); + 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 be not set."); +} + +Test(TXA, accumulator8bits) +{ + Init() + snes.cpu->_isEmulationMode = false; + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.p.x_b = false; + snes.cpu->_registers.x = 0x0BCD; + snes.cpu->_registers.a = 0x5656; + snes.cpu->TXA(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0x56CD, "The accumulator should be 0x56CD but it was %x", snes.cpu->_registers.a); + 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."); +} + +Test(TXA, double16bits) +{ + Init() + snes.cpu->_isEmulationMode = false; + snes.cpu->_registers.p.m = false; + snes.cpu->_registers.p.x_b = false; + snes.cpu->_registers.x = 0xAB0D; + snes.cpu->_registers.a = 0x5656; + snes.cpu->TXA(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0xAB0D, "The accumulator should be 0xAB0D but it was %x", snes.cpu->_registers.a); + 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."); +} + + +Test(TYA, double8bits) +{ + Init() + snes.cpu->_isEmulationMode = true; + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.p.x_b = true; + snes.cpu->_registers.y = 0xABCD; + snes.cpu->_registers.a = 0x5656; + snes.cpu->TYA(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0x56CD, "The accumulator should be 0x56CD but it was %x", snes.cpu->_registers.a); + 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."); +} + +Test(TYA, index8bits) +{ + Init() + snes.cpu->_isEmulationMode = false; + snes.cpu->_registers.p.m = false; + snes.cpu->_registers.p.x_b = true; + snes.cpu->_registers.y = 0x0BCD; + snes.cpu->_registers.a = 0x5656; + snes.cpu->TYA(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0x00CD, "The accumulator should be 0x00CD but it was %x", snes.cpu->_registers.a); + 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 be not set."); +} + +Test(TYA, accumulator8bits) +{ + Init() + snes.cpu->_isEmulationMode = false; + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.p.x_b = false; + snes.cpu->_registers.y = 0x0BCD; + snes.cpu->_registers.a = 0x5656; + snes.cpu->TYA(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0x56CD, "The accumulator should be 0x56CD but it was %x", snes.cpu->_registers.a); + 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."); +} + +Test(TYA, double16bits) +{ + Init() + snes.cpu->_isEmulationMode = false; + snes.cpu->_registers.p.m = false; + snes.cpu->_registers.p.x_b = false; + snes.cpu->_registers.y = 0xAB0D; + snes.cpu->_registers.a = 0x5656; + snes.cpu->TYA(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0xAB0D, "The accumulator should be 0xAB0D but it was %x", snes.cpu->_registers.a); + 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."); +} From a9eca0f039160e6623cfada1269249f251708d87 Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Wed, 8 Apr 2020 11:12:45 +0200 Subject: [PATCH 22/26] Implementing TXY and TYX --- sources/CPU/CPU.hpp | 8 ++- .../CPU/Instructions/TransferRegisters.cpp | 26 ++++++++++ tests/CPU/TransferRegisters.cpp | 52 +++++++++++++++++++ 3 files changed, 84 insertions(+), 2 deletions(-) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index f928561..ee25dfc 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -411,6 +411,10 @@ namespace ComSquare::CPU int TXA(uint24_t, AddressingMode); //! @brief Transfer Y to A int TYA(uint24_t, AddressingMode); + //! @brief Transfer X to Y + int TXY(uint24_t, AddressingMode); + //! @brief Transfer Y to X + int TYX(uint24_t, AddressingMode); //! @brief All the instructions of the CPU. //! @info Instructions are indexed by their opcode @@ -570,7 +574,7 @@ namespace ComSquare::CPU {&CPU::TYA, 2, "tya", AddressingMode::Implied, 1}, // 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::TXY, 2, "txy", AddressingMode::Implied, 1}, // 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 @@ -602,7 +606,7 @@ namespace ComSquare::CPU {&CPU::CLV, 7, "clv", AddressingMode::Implied, 1}, // B8 {&CPU::LDA, 4, "lda", AddressingMode::AbsoluteIndexedByY, 3}, // B9 {&CPU::TSX, 2, "tsx", AddressingMode::Implied, 1}, // BA - {&CPU::BRK, 7, "tyx #-#", AddressingMode::Implied, 2}, // BB + {&CPU::TYX, 2, "tyx", AddressingMode::Implied, 1}, // 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 diff --git a/sources/CPU/Instructions/TransferRegisters.cpp b/sources/CPU/Instructions/TransferRegisters.cpp index 6135ce3..18efae8 100644 --- a/sources/CPU/Instructions/TransferRegisters.cpp +++ b/sources/CPU/Instructions/TransferRegisters.cpp @@ -124,4 +124,30 @@ namespace ComSquare::CPU this->_registers.p.z = this->_registers.a == 0; return 0; } + + int CPU::TXY(uint24_t, AddressingMode) + { + unsigned negativeFlag = this->_registers.p.x_b ? 0x80u : 0x8000u; + + if (this->_registers.p.x_b) + this->_registers.yl = this->_registers.xl; + else + this->_registers.y = this->_registers.x; + this->_registers.p.n = this->_registers.y & negativeFlag; + this->_registers.p.z = this->_registers.y == 0; + return 0; + } + + int CPU::TYX(uint24_t, AddressingMode) + { + unsigned negativeFlag = this->_registers.p.x_b ? 0x80u : 0x8000u; + + if (this->_registers.p.x_b) + this->_registers.xl = this->_registers.yl; + else + this->_registers.x = this->_registers.y; + this->_registers.p.n = this->_registers.y & negativeFlag; + this->_registers.p.z = this->_registers.y == 0; + return 0; + } } \ No newline at end of file diff --git a/tests/CPU/TransferRegisters.cpp b/tests/CPU/TransferRegisters.cpp index 07cabcc..91ab304 100644 --- a/tests/CPU/TransferRegisters.cpp +++ b/tests/CPU/TransferRegisters.cpp @@ -384,3 +384,55 @@ Test(TYA, double16bits) 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."); } + +Test(TXY, emulationMode) +{ + Init() + snes.cpu->_isEmulationMode = true; + snes.cpu->_registers.p.x_b = true; + snes.cpu->_registers.x = 0x0BCD; + snes.cpu->_registers.y = 0x5656; + snes.cpu->TXY(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.y, 0x56CD, "The y index should be 0x56CD 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."); +} + +Test(TXY, nativeMode) +{ + Init() + snes.cpu->_isEmulationMode = false; + snes.cpu->_registers.p.x_b = false; + snes.cpu->_registers.x = 0xAB0D; + snes.cpu->_registers.y = 0x5656; + snes.cpu->TXY(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.y, 0xAB0D, "The y index should be 0xAB0D 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."); +} + +Test(TYX, emulationMode) +{ + Init() + snes.cpu->_isEmulationMode = true; + snes.cpu->_registers.p.x_b = true; + snes.cpu->_registers.y = 0x0BCD; + snes.cpu->_registers.x = 0x5656; + snes.cpu->TYX(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.x, 0x56CD, "The x index should be 0x56CD 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."); +} + +Test(TYX, nativeMode) +{ + Init() + snes.cpu->_isEmulationMode = false; + snes.cpu->_registers.p.x_b = false; + snes.cpu->_registers.y = 0xAB0D; + snes.cpu->_registers.x = 0x5656; + snes.cpu->TYX(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.x, 0xAB0D, "The x index should be 0xAB0D 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."); +} \ No newline at end of file From 548553400606556adacbdeff249713590e365e8f Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Wed, 8 Apr 2020 11:17:47 +0200 Subject: [PATCH 23/26] Adding TSB --- sources/CPU/CPU.hpp | 6 ++++-- sources/CPU/Instructions/BitsInstructions.cpp | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index ee25dfc..a174e70 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -415,6 +415,8 @@ namespace ComSquare::CPU int TXY(uint24_t, AddressingMode); //! @brief Transfer Y to X int TYX(uint24_t, AddressingMode); + //! @brief Test and Set Memory Bits Against Accumulator + int TSB(uint24_t, AddressingMode); //! @brief All the instructions of the CPU. //! @info Instructions are indexed by their opcode @@ -423,7 +425,7 @@ namespace ComSquare::CPU {&CPU::ORA, 6, "ora", AddressingMode::DirectPageIndirectIndexedByX, 2}, // 01 {&CPU::COP, 7, "cop", AddressingMode::Immediate8bits, 2}, // 02 {&CPU::ORA, 4, "ora", AddressingMode::StackRelative, 2}, // 03 - {&CPU::BRK, 7, "tsb #-#", AddressingMode::Implied, 2}, // 04 + {&CPU::TSB, 5, "tsb", AddressingMode::DirectPage, 2}, // 04 {&CPU::ORA, 3, "ora", AddressingMode::DirectPage, 2}, // 05 {&CPU::BRK, 7, "asl #-#", AddressingMode::Implied, 2}, // 06 {&CPU::ORA, 6, "ora", AddressingMode::DirectPageIndirectLong, 2}, // 07 @@ -431,7 +433,7 @@ namespace ComSquare::CPU {&CPU::ORA, 2, "ora", AddressingMode::ImmediateForA, 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::TSB, 6, "tsb", AddressingMode::Absolute, 3}, // 0C {&CPU::ORA, 3, "ora", AddressingMode::Absolute, 4}, // 0D {&CPU::BRK, 7, "asl #-#", AddressingMode::Implied, 2}, // 0E {&CPU::ORA, 5, "ora", AddressingMode::AbsoluteLong, 5}, // 0F diff --git a/sources/CPU/Instructions/BitsInstructions.cpp b/sources/CPU/Instructions/BitsInstructions.cpp index cf6cd8a..95b91eb 100644 --- a/sources/CPU/Instructions/BitsInstructions.cpp +++ b/sources/CPU/Instructions/BitsInstructions.cpp @@ -7,5 +7,19 @@ namespace ComSquare::CPU { + int CPU::TSB(uint24_t valueAddr, AddressingMode mode) + { + uint8_t value = this->_bus->read(valueAddr); + value |= this->_registers.a; + this->_bus->write(valueAddr, value); + this->_registers.p.z = value == 0; + + int cycles = 0; + if (!this->_registers.p.m) + cycles += 2; + if (mode == DirectPage) + cycles += this->_registers.dl != 0; + return cycles; + } } \ No newline at end of file From b9ff47584e40a6c813c712734680b2e67d7c11e9 Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Wed, 8 Apr 2020 11:39:43 +0200 Subject: [PATCH 24/26] Finishing the TSB --- sources/CPU/Instructions/BitsInstructions.cpp | 7 +++++- tests/CPU/testBits.cpp | 25 ++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/sources/CPU/Instructions/BitsInstructions.cpp b/sources/CPU/Instructions/BitsInstructions.cpp index 95b91eb..737bdc9 100644 --- a/sources/CPU/Instructions/BitsInstructions.cpp +++ b/sources/CPU/Instructions/BitsInstructions.cpp @@ -2,6 +2,7 @@ // Created by anonymus-raccoon on 2/20/20. // +#include #include "../../Models/Int24.hpp" #include "../CPU.hpp" @@ -9,9 +10,13 @@ namespace ComSquare::CPU { int CPU::TSB(uint24_t valueAddr, AddressingMode mode) { - uint8_t value = this->_bus->read(valueAddr); + uint16_t value = this->_bus->read(valueAddr); + if (!this->_registers.p.m) + value += this->_bus->read(valueAddr + 1) << 8u; value |= this->_registers.a; this->_bus->write(valueAddr, value); + if (!this->_registers.p.m) + this->_bus->write(valueAddr + 1, value >> 8u); this->_registers.p.z = value == 0; diff --git a/tests/CPU/testBits.cpp b/tests/CPU/testBits.cpp index a06c7af..5c4b334 100644 --- a/tests/CPU/testBits.cpp +++ b/tests/CPU/testBits.cpp @@ -35,7 +35,6 @@ Test(AND, nativeNegative) cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flag should be set."); } - Test(AND, emulationTest) { Init() @@ -48,3 +47,27 @@ Test(AND, emulationTest) cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set."); } +Test(TSB, emulationTest) +{ + Init() + snes.wram->_data[0] = 0b00110011; + snes.cpu->_registers.a = 0b00110111; + snes.cpu->_registers.p.m = true; + snes.cpu->TSB(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.wram->_data[0], 0b00110111, "The data in ram should be 0b00110111 but it was %x", snes.wram->_data[0]); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); +} + +Test(TSB, nativeTest) +{ + Init() + snes.wram->_data[0] = 0xF0; + snes.wram->_data[1] = 0x0F; + snes.cpu->_registers.a = 0x8008; + snes.cpu->_registers.p.m = false; + snes.cpu->TSB(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.wram->_data[0], 0xF8, "The first data in ram should be 0xF8 but it was %x", snes.wram->_data[0]); + cr_assert_eq(snes.wram->_data[1], 0x8F, "The second data in ram should be 0x8F but it was %x", snes.wram->_data[1]); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); +} + From d0455b46e29ab44ccd8322d8b50801ed80a6fc60 Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Wed, 8 Apr 2020 11:49:37 +0200 Subject: [PATCH 25/26] Solving branching instructions addressing mode --- sources/CPU/CPU.hpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index a174e70..1b0cd41 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -437,7 +437,7 @@ namespace ComSquare::CPU {&CPU::ORA, 3, "ora", AddressingMode::Absolute, 4}, // 0D {&CPU::BRK, 7, "asl #-#", AddressingMode::Implied, 2}, // 0E {&CPU::ORA, 5, "ora", AddressingMode::AbsoluteLong, 5}, // 0F - {&CPU::BPL, 7, "bpl", AddressingMode::Implied, 2}, // 10 + {&CPU::BPL, 7, "bpl", AddressingMode::Immediate8bits, 2}, // 10 {&CPU::ORA, 5, "ora", AddressingMode::DirectPageIndirectIndexedByY, 2}, // 11 {&CPU::ORA, 5, "ora", AddressingMode::DirectPageIndirect, 2}, // 12 {&CPU::ORA, 7, "ora", AddressingMode::StackRelativeIndirectIndexedByY, 2}, // 13 @@ -469,7 +469,7 @@ namespace ComSquare::CPU {&CPU::AND, 4, "and", AddressingMode::Absolute, 3}, // 2D {&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 2E {&CPU::AND, 5, "and", AddressingMode::AbsoluteLong, 4}, // 2F - {&CPU::BMI, 2, "bmi", AddressingMode::Implied, 2}, // 30 + {&CPU::BMI, 2, "bmi", AddressingMode::Immediate8bits, 2}, // 30 {&CPU::AND, 5, "and", AddressingMode::DirectPageIndirectIndexedByY, 2}, // 31 {&CPU::AND, 5, "and", AddressingMode::DirectPageIndirect, 2}, // 32 {&CPU::AND, 7, "and", AddressingMode::StackRelativeIndirectIndexedByY, 2}, // 33 @@ -501,7 +501,7 @@ namespace ComSquare::CPU {&CPU::EOR, 4, "eor", AddressingMode::Absolute, 3}, // 4D {&CPU::BRK, 7, "lsr #-#", AddressingMode::Implied, 2}, // 4E {&CPU::EOR, 5, "eor", AddressingMode::AbsoluteLong, 4}, // 4F - {&CPU::BVC, 2, "bvc", AddressingMode::Implied, 2}, // 50 + {&CPU::BVC, 2, "bvc", AddressingMode::Immediate8bits, 2}, // 50 {&CPU::EOR, 5, "eor", AddressingMode::DirectPageIndirectIndexedByY, 2}, // 51 {&CPU::EOR, 5, "eor", AddressingMode::DirectPageIndirect, 2}, // 52 {&CPU::EOR, 4, "eor", AddressingMode::StackRelativeIndirectIndexedByY, 2}, // 53 @@ -533,7 +533,7 @@ namespace ComSquare::CPU {&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::BVS, 2, "bvs", AddressingMode::Immediate8bits, 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 @@ -549,9 +549,9 @@ namespace ComSquare::CPU {&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::BRA, 3, "bra", AddressingMode::Immediate8bits, 2}, // 80 {&CPU::STA, 6, "sta", AddressingMode::DirectPageIndexedByX, 2}, // 81 - {&CPU::BRL, 4, "brl", AddressingMode::Implied, 3}, // 82 + {&CPU::BRL, 4, "brl", AddressingMode::Absolute, 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 @@ -565,7 +565,7 @@ namespace ComSquare::CPU {&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::BCC, 2, "bcc", AddressingMode::Immediate8bits, 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 @@ -597,7 +597,7 @@ namespace ComSquare::CPU {&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::BCS, 2, "bcs", AddressingMode::Immediate8bits, 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 @@ -629,7 +629,7 @@ namespace ComSquare::CPU {&CPU::CMP, 4, "cmp", AddressingMode::Absolute, 3}, // CD {&CPU::DEC, 6, "dec", AddressingMode::Absolute, 3}, // CE {&CPU::CMP, 6, "cmp", AddressingMode::AbsoluteLong, 4}, // CF - {&CPU::BNE, 2, "bne", AddressingMode::Implied, 2}, // D0 + {&CPU::BNE, 2, "bne", AddressingMode::Immediate8bits, 2}, // D0 {&CPU::CMP, 5, "cmp", AddressingMode::DirectPageIndirectIndexedByY, 2}, // D1 {&CPU::CMP, 5, "cmp", AddressingMode::DirectPageIndirect, 2}, // D2 {&CPU::CMP, 7, "cmp", AddressingMode::StackRelativeIndirectIndexedByY, 2}, // D3 @@ -661,7 +661,7 @@ namespace ComSquare::CPU {&CPU::SBC, 4, "sbc", AddressingMode::Absolute, 3}, // ED {&CPU::INC, 6, "inc", AddressingMode::Absolute, 3}, // EE {&CPU::SBC, 5, "sbc", AddressingMode::AbsoluteLong, 4}, // EF - {&CPU::BEQ, 2, "beq", AddressingMode::Implied, 2}, // F0 + {&CPU::BEQ, 2, "beq", AddressingMode::Immediate8bits, 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 From 26ea447f24200c2ba4159819d1c6a9ce7992770b Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Wed, 8 Apr 2020 17:13:00 +0200 Subject: [PATCH 26/26] Implementing the XBA --- sources/CPU/CPU.hpp | 4 +++- .../Instructions/MathematicalOperations.cpp | 10 ++++++++++ tests/CPU/Math/testOthersMath.cpp | 20 +++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index 1b0cd41..2d72b91 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -417,6 +417,8 @@ namespace ComSquare::CPU int TYX(uint24_t, AddressingMode); //! @brief Test and Set Memory Bits Against Accumulator int TSB(uint24_t, AddressingMode); + //! @brief Exchange the B and A Accumulators + int XBA(uint24_t, AddressingMode); //! @brief All the instructions of the CPU. //! @info Instructions are indexed by their opcode @@ -656,7 +658,7 @@ namespace ComSquare::CPU {&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::XBA, 3, "xba", AddressingMode::Implied, 1}, // EB {&CPU::CPX, 4, "cpx", AddressingMode::Absolute, 3}, // EC {&CPU::SBC, 4, "sbc", AddressingMode::Absolute, 3}, // ED {&CPU::INC, 6, "inc", AddressingMode::Absolute, 3}, // EE diff --git a/sources/CPU/Instructions/MathematicalOperations.cpp b/sources/CPU/Instructions/MathematicalOperations.cpp index f723978..7d95161 100644 --- a/sources/CPU/Instructions/MathematicalOperations.cpp +++ b/sources/CPU/Instructions/MathematicalOperations.cpp @@ -398,4 +398,14 @@ namespace ComSquare::CPU } return cycles; } + + int CPU::XBA(uint24_t, AddressingMode) + { + int tmp = this->_registers.ah; + this->_registers.ah = this->_registers.al; + this->_registers.al = tmp; + this->_registers.p.n = this->_registers.al & 0x80u; + this->_registers.p.z = this->_registers.al == 0; + return 0; + } } \ No newline at end of file diff --git a/tests/CPU/Math/testOthersMath.cpp b/tests/CPU/Math/testOthersMath.cpp index 294f4f9..a8a6093 100644 --- a/tests/CPU/Math/testOthersMath.cpp +++ b/tests/CPU/Math/testOthersMath.cpp @@ -321,3 +321,23 @@ Test(EOR, zero) cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flags should not be set."); cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flags should be set."); } + +Test(XBA, zero) +{ + Init() + snes.cpu->_registers.a = 0x0001; + snes.cpu->XBA(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0x0100, "The accumulator's value should be 0x0100 but it was 0x%x.", snes.cpu->_registers.a); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flags should not be set."); + cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flags should be set."); +} + +Test(XBA, negative) +{ + Init() + snes.cpu->_registers.a = 0x8001; + snes.cpu->XBA(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.a, 0x0180, "The accumulator's value should be 0x0180 but it was 0x%x.", snes.cpu->_registers.a); + cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flags should be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should be not set."); +}