diff --git a/CMakeLists.txt b/CMakeLists.txt index 4513dc8..e6dd858 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,6 +88,8 @@ add_executable(unit_tests tests/APU/testOperand.cpp sources/APU/Instructions/8bitArithmetic.cpp sources/APU/Instructions/8bitDataTransmission.cpp + sources/APU/IPL/IPL.hpp + sources/APU/IPL/IPL.cpp ) # include criterion & coverage @@ -187,6 +189,8 @@ add_executable(ComSquare sources/APU/Instructions/8bitLogical.cpp sources/APU/Instructions/8bitArithmetic.cpp sources/APU/Instructions/8bitDataTransmission.cpp + sources/APU/IPL/IPL.hpp + sources/APU/IPL/IPL.cpp ) target_compile_definitions(ComSquare PUBLIC DEBUGGER_ENABLED) diff --git a/main.cpp b/main.cpp index a2bb748..9176d81 100644 --- a/main.cpp +++ b/main.cpp @@ -22,12 +22,13 @@ void parseArguments(int argc, char **argv, SNES &snes) int option_index = 0; static struct option long_options[] = { {"cpu", no_argument, 0, 'c' }, + {"apu", no_argument, 0, 'a' }, {"memory", no_argument, 0, 'm' }, {"header", no_argument, 0, 'h' }, {0, 0, 0, 0 } }; - char c = getopt_long(argc, argv, "cmh", long_options, &option_index); + char c = getopt_long(argc, argv, "camh", long_options, &option_index); if (c == -1) break; switch (c) { @@ -37,6 +38,9 @@ void parseArguments(int argc, char **argv, SNES &snes) case 'c': snes.enableCPUDebugging(); break; + case 'a': + snes.enableAPUDebugging(); + break; case 'm': snes.enableRamViewer(); break; diff --git a/sources/APU/APU.cpp b/sources/APU/APU.cpp index 85f9707..ef60c82 100644 --- a/sources/APU/APU.cpp +++ b/sources/APU/APU.cpp @@ -7,6 +7,7 @@ #include "../Exceptions/NotImplementedException.hpp" #include "../Exceptions/InvalidAddress.hpp" #include "../Exceptions/InvalidOpcode.hpp" +#include "../Utility/Utility.hpp" namespace ComSquare::APU { @@ -50,7 +51,7 @@ namespace ComSquare::APU case 0x0200 ... 0xFFBF: return this->_map->Memory.read_internal(addr - 0x200); case 0xFFC0 ... 0xFFFF: - return this->_map->IPL.read_internal(addr - 0xFFC0); + return this->_map->IPL.read(addr - 0xFFC0); default: throw InvalidAddress("APU Registers read", addr); } @@ -107,7 +108,7 @@ namespace ComSquare::APU this->_map->Memory.write_internal(addr - 0x200, data); break; case 0xFFC0 ... 0xFFFF: - this->_map->IPL.write_internal(addr - 0xFFC0, data); + this->_map->IPL.write(addr - 0xFFC0, data); break; default: throw InvalidAddress("APU Registers write", addr); @@ -152,11 +153,21 @@ namespace ComSquare::APU void APU::reset() { + this->_registers.port0 = 0x00; + this->_registers.port1 = 0x00; + this->_registers.port2 = 0x00; + this->_registers.port3 = 0x00; + + this->_paddingCycles = 0; + this->_internalRegisters.ya = 0x0000; + this->_internalRegisters.x = 0x00; + this->_internalRegisters.sp = 0xEF; + this->_internalRegisters.pc = 0xFFC0; } int APU::_executeInstruction() { - uint8_t opcode = this->_internalRead(this->_internalRegisters.pc); + uint8_t opcode = this->_internalRead(this->_internalRegisters.pc++); switch (opcode) { case 0x00: @@ -697,6 +708,6 @@ namespace ComSquare::APU Page0(0x00F0), Page1(0x0100), Memory(0xFDC0), - IPL(0x0040) + IPL() { } } diff --git a/sources/APU/APU.hpp b/sources/APU/APU.hpp index 825b955..fbe7555 100644 --- a/sources/APU/APU.hpp +++ b/sources/APU/APU.hpp @@ -9,6 +9,7 @@ #include "DSP/DSP.hpp" #include "../Memory/IMemory.hpp" #include "../Ram/Ram.hpp" +#include "IPL/IPL.hpp" namespace ComSquare::APU { @@ -123,7 +124,7 @@ namespace ComSquare::APU //! @brief Any-use memory Ram::Ram Memory; //! @brief IPL ROM - Ram::Ram IPL; + IPL::IPL IPL; MemoryMap(); MemoryMap(const MemoryMap &) = delete; diff --git a/sources/APU/IPL/IPL.cpp b/sources/APU/IPL/IPL.cpp new file mode 100644 index 0000000..ff36344 --- /dev/null +++ b/sources/APU/IPL/IPL.cpp @@ -0,0 +1,29 @@ +// +// Created by Melefo on 27/02/2020. +// + +#include "IPL.hpp" +#include "../../Exceptions/InvalidAddress.hpp" + +namespace ComSquare::APU::IPL +{ + IPL::IPL() + { } + + IPL::~IPL() + { } + + uint8_t IPL::read(uint24_t addr) + { + if (addr >= this->_size) + throw InvalidAddress("IPL read", addr); + return this->_data[addr]; + } + + void IPL::write(uint24_t addr, uint8_t data) + { + if (addr >= this->_size) + throw InvalidAddress("IPL write", addr); + this->_data[addr] = data; + } +} \ No newline at end of file diff --git a/sources/APU/IPL/IPL.hpp b/sources/APU/IPL/IPL.hpp new file mode 100644 index 0000000..44089f7 --- /dev/null +++ b/sources/APU/IPL/IPL.hpp @@ -0,0 +1,53 @@ +// +// Created by Melefo on 27/02/2020. +// + +#ifndef COMSQUARE_IPL_HPP +#define COMSQUARE_IPL_HPP + +#include "../../Memory/IRectangleMemory.hpp" + +namespace ComSquare::APU::IPL +{ + class IPL : public Memory::IMemory { + protected: + //! @brief The Rom. + std::array _data = { + 0xCD, 0xEF, 0xBD, 0xE8, 0x00, 0xC6, 0x1D, 0xD0, + 0xFC, 0x8F, 0xAA, 0xF4, 0x8F, 0xBB, 0xF5, 0x78, + 0xCC, 0xF4, 0xD0, 0xFB, 0x2F, 0x19, 0xEB, 0xF4, + 0xD0, 0xFC, 0x7E, 0xF4, 0xD0, 0x0B, 0xE4, 0xF5, + 0xCB, 0xF4, 0xD7, 0x00, 0xFC, 0xD0, 0xF3, 0xAB, + 0x01, 0x10, 0xEF, 0x7E, 0xF4, 0x10, 0xEB, 0xBA, + 0xF6, 0xDA, 0x00, 0xBA, 0xF4, 0xC4, 0xF4, 0xDD, + 0x5D, 0xD0, 0xDB, 0x1F, 0x00, 0x00, 0xC0, 0xFF + }; + size_t _size = 64; + public: + //! @brief Create the rom with its value. + explicit IPL(); + + //! @brief The rom can't be copied. + IPL(const IPL &) = delete; + + //! @brief The rom can't be assigned. + IPL &operator=(IPL &) = delete; + + //! @brief Destructor that free the rom. + ~IPL(); + + //! @brief Read data from the component using the same method as the basic IMemory. + //! @param addr The global 24 bits address. This method is responsible of mapping to the component's read. + //! @throw InvalidAddress if the address is not mapped to the component. + //! @return Return the data at the address given as parameter. + uint8_t read(uint24_t addr) override; + + //! @brief Write data to this component using the same method as the basic IMemory. + //! @param addr The global 24 bits address. This method is responsible of mapping to the component's write. + //! @param data The new data to write. + //! @throw InvalidAddress if the address is not mapped to the component. + void write(uint24_t addr, uint8_t data) override; + }; +} + +#endif //COMSQUARE_IPL_HPP \ No newline at end of file diff --git a/sources/Debugger/APUDebug.cpp b/sources/Debugger/APUDebug.cpp index 443334e..177f7a7 100644 --- a/sources/Debugger/APUDebug.cpp +++ b/sources/Debugger/APUDebug.cpp @@ -1,10 +1,10 @@ -// //! @brief Convert a basic CPU to a debugging CPU. - +// // Created by Melefo on 19/02/2020. // #include "APUDebug.hpp" #include "../Utility/Utility.hpp" +#include "../Exceptions/InvalidOpcode.hpp" using namespace ComSquare::APU; @@ -20,6 +20,8 @@ namespace ComSquare::Debugger this->setAttribute(Qt::WA_QuitOnClose, false); this->_ui.setupUi(this); + //QMainWindow::connect(this->_ui.resumeButton, SIGNAL(clicked()), this, SLOT(APUDebug::pause())); + //QMainWindow::connect(this->_ui.stepButton, SIGNAL(clicked()), this, SLOT(APUDebug::step())); this->show(); this->_updatePanel(); } @@ -472,6 +474,12 @@ namespace ComSquare::Debugger int APUDebug::_executeInstruction() { + if (this->_isPaused) + return 0; + if (this->_isStepping) { + this->_isStepping = false; + this->_isPaused = true; + } this->_ui.logger->append(APUDebug::_getInstructionString().c_str()); this->_updatePanel(); return APU::_executeInstruction(); @@ -479,10 +487,32 @@ namespace ComSquare::Debugger void APUDebug::update(unsigned cycles) { - if (!this->isVisible()) { - this->_snes.disableAPUDebugging(); - return; + try { + if (!this->isVisible()) { + this->_snes.disableAPUDebugging(); + return; + } + if (this->_isPaused) + return; + return APU::update(cycles); + } catch (InvalidOpcode &e) { + this->pause(); + this->_ui.logger->append(e.what()); } - return APU::update(cycles); + } + + void APUDebug::step() + { + this->_isStepping = true; + this->_isPaused = false; + } + + void APUDebug::pause() + { + this->_isPaused = !this->_isPaused; + if (this->_isPaused) + this->_ui.resumeButton->setText("Resume"); + else + this->_ui.resumeButton->setText("Pause"); } } \ No newline at end of file diff --git a/sources/Debugger/APUDebug.hpp b/sources/Debugger/APUDebug.hpp index 41d66e1..0e015a6 100644 --- a/sources/Debugger/APUDebug.hpp +++ b/sources/Debugger/APUDebug.hpp @@ -16,6 +16,11 @@ namespace ComSquare::Debugger //! @brief A widget that contain the whole UI. Ui::APUView _ui; + //! @brief If this is set to true, the execution of the APU will be paused. + bool _isPaused = true; + //! @brief If this is set to true, the APU will execute one instruction and pause itself. + bool _isStepping = false; + //! @brief A reference to the snes (to disable the debugger). SNES &_snes; @@ -30,6 +35,11 @@ namespace ComSquare::Debugger //! @brief return the mnemonic of the current instruction done. std::string _getInstructionString(); + public slots: + //! @brief Pause/Resume the APU. + void pause(); + //! @brief Step - Execute a single instruction. + void step(); public: //! @brief Convert a basic APU to a debugging APU. explicit APUDebug(ComSquare::APU::APU &apu, SNES &snes); diff --git a/sources/SNES.cpp b/sources/SNES.cpp index 73d4450..488b26a 100644 --- a/sources/SNES.cpp +++ b/sources/SNES.cpp @@ -78,9 +78,9 @@ namespace ComSquare void SNES::enableAPUDebugging() { #ifdef DEBUGGER_ENABLED - this->apu = std::make_shared(*this->apu, *this); + this->apu = std::make_shared(*this->apu, *this); #else - std::cerr << "Debugging features are not enabled. You can't enable the debugger." << std::endl; + std::cerr << "Debugging features are not enabled. You can't enable the debugger." << std::endl; #endif } diff --git a/tests/APU/testAPUInstructions.cpp b/tests/APU/testAPUInstructions.cpp index 7fa0aed..1f253de 100644 --- a/tests/APU/testAPUInstructions.cpp +++ b/tests/APU/testAPUInstructions.cpp @@ -380,6 +380,7 @@ Test(Subroutine, CALL) auto apu = Init().second.apu; int result = 0; + apu->_internalRegisters.pc = 0; apu->_internalWrite(apu->_getAbsoluteAddr(), 23); apu->_internalRegisters.pc -= 2; result = apu->CALL(apu->_getAbsoluteAddr()); @@ -855,6 +856,7 @@ Test(XVIbitDataTransmission, MOVW) auto apu = Init().second.apu; int result = 0; + apu->_internalRegisters.pc = 0; apu->_internalRegisters.ya = 0x2211; apu->_internalWrite(apu->_internalRegisters.pc, 0x55); result = apu->MOVW(apu->_getDirectAddr()); diff --git a/ui/apuView.ui b/ui/apuView.ui index 03d2908..71a242d 100644 --- a/ui/apuView.ui +++ b/ui/apuView.ui @@ -31,7 +31,7 @@ QTabWidget::Rounded - 2 + 0 @@ -329,6 +329,45 @@ CPU + + + + + + + Instructions History + + + Qt::AlignCenter + + + + + + + + + Resume + + + + :/resources/icons/play.svg:/resources/icons/play.svg + + + + + + + Step + + + + :/resources/icons/step.svg:/resources/icons/step.svg + + + + + @@ -393,19 +432,6 @@ - - - - Instructions History - - - Qt::AlignCenter - - - - - - @@ -543,7 +569,7 @@ - 7 + 0