diff --git a/CMakeLists.txt b/CMakeLists.txt index 73b2e7e..1b5558d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -144,11 +144,17 @@ add_executable(ComSquare ui/cpu.ui ui/ramView.ui ui/cartridgeView.ui + ui/apuView.ui resources/appResources.qrc sources/Utility/Utility.hpp sources/Debugger/MemoryViewer.cpp sources/Debugger/MemoryViewer.hpp - sources/Utility/Utility.cpp sources/Debugger/HeaderViewer.cpp sources/Debugger/HeaderViewer.hpp) + sources/Utility/Utility.cpp + sources/Debugger/HeaderViewer.cpp + sources/Debugger/HeaderViewer.hpp + sources/Debugger/APUDebug.hpp + sources/Debugger/APUDebug.cpp +) target_compile_definitions(ComSquare PUBLIC DEBUGGER_ENABLED) diff --git a/sources/APU/APU.cpp b/sources/APU/APU.cpp index 8538868..95ebb8f 100644 --- a/sources/APU/APU.cpp +++ b/sources/APU/APU.cpp @@ -3,6 +3,7 @@ // #include +#include #include "APU.hpp" #include "../Exceptions/NotImplementedException.hpp" #include "../Exceptions/InvalidAddress.hpp" @@ -10,7 +11,9 @@ namespace ComSquare::APU { - APU::APU() : _dsp(new DSP::DSP) + APU::APU(std::shared_ptr &map) : + _map(map), + _dsp(new DSP::DSP) { this->reset(); } @@ -18,7 +21,7 @@ namespace ComSquare::APU uint8_t APU::_internalRead(uint24_t addr) { switch (addr) { case 0x0000 ... 0x00EF: - return this->_map.Page0.read_internal(addr); + return this->_map->Page0.read_internal(addr); case 0xF0: return this->_registers.unknown; case 0xF2: @@ -44,11 +47,11 @@ namespace ComSquare::APU case 0xFF: return this->_registers.counter2; case 0x0100 ... 0x01FF: - return this->_map.Page1.read_internal(addr - 0x0100); + return this->_map->Page1.read_internal(addr - 0x0100); case 0x0200 ... 0xFFBF: - return this->_map.Memory.read_internal(addr - 0x200); + return this->_map->Memory.read_internal(addr - 0x200); case 0xFFC0 ... 0xFFFF: - return this->_map.IPL.read_internal(addr - 0xFFC0); + return this->_map->IPL.read_internal(addr - 0xFFC0); default: throw InvalidAddress("APU Registers read", addr); } @@ -57,7 +60,7 @@ namespace ComSquare::APU void APU::_internalWrite(uint24_t addr, uint8_t data) { switch (addr) { case 0x0000 ... 0x00EF: - this->_map.Page0.write_internal(addr, data); + this->_map->Page0.write_internal(addr, data); break; case 0xF0: this->_registers.unknown = data; @@ -99,13 +102,13 @@ namespace ComSquare::APU this->_registers.timer2 = data; break; case 0x0100 ... 0x01FF: - this->_map.Page1.write_internal(addr - 0x0100, data); + this->_map->Page1.write_internal(addr - 0x0100, data); break; case 0x0200 ... 0xFFBF: - this->_map.Memory.write_internal(addr - 0x200, data); + this->_map->Memory.write_internal(addr - 0x200, data); break; case 0xFFC0 ... 0xFFFF: - this->_map.IPL.write_internal(addr - 0xFFC0, data); + this->_map->IPL.write_internal(addr - 0xFFC0, data); break; default: throw InvalidAddress("APU Registers write", addr); @@ -152,9 +155,9 @@ namespace ComSquare::APU { } - int APU::executeInstruction() + int APU::_executeInstruction() { - uint8_t opcode = this->_internalRead(this->_internalRegisters.pc++); + uint8_t opcode = this->_internalRead(this->_internalRegisters.pc); switch (opcode) { case 0x00: @@ -222,7 +225,7 @@ namespace ComSquare::APU cycles -= this->_paddingCycles; while (total < cycles && this->_state == Running) - total += this->executeInstruction(); + total += this->_executeInstruction(); if (this->_state == Running) this->_paddingCycles = total - cycles; } diff --git a/sources/APU/APU.hpp b/sources/APU/APU.hpp index 47d7e67..3d6d4f8 100644 --- a/sources/APU/APU.hpp +++ b/sources/APU/APU.hpp @@ -126,17 +126,20 @@ namespace ComSquare::APU Ram::Ram IPL; MemoryMap(); + MemoryMap(const MemoryMap &) = delete; + MemoryMap &operator=(const MemoryMap &) = delete; + ~MemoryMap() = default; }; class APU : public Memory::IMemory { - private: + protected: //! @brief All the registers of the APU CPU Registers _registers{}; //! @brief Internal registers of the CPU (accessible from the bus via addr $4200 to $421F). InternalRegisters _internalRegisters{}; //! @brief Internal APU memory separated according to their utility - MemoryMap _map; + std::shared_ptr _map; //! @brief The DSP component used to produce sound std::shared_ptr _dsp; @@ -165,7 +168,7 @@ namespace ComSquare::APU //! @brief Execute a single instruction. //! @return The number of cycles that the instruction took. - int executeInstruction(); + virtual int _executeInstruction(); //! @brief No Operation instruction, do nothing than delay int NOP(); @@ -198,7 +201,7 @@ namespace ComSquare::APU //! @brief test set 1-bit instruction, Test and set bits with absolute address int TSET1(uint24_t abs); public: - explicit APU(); + explicit APU(std::shared_ptr &map); APU(const APU &) = default; APU &operator=(const APU &) = default; ~APU() = default; @@ -215,7 +218,7 @@ namespace ComSquare::APU void write(uint24_t addr, uint8_t data) override; //! @brief This function execute the instructions received until the maximum number of cycles is reached. //! @return The number of cycles that elapsed. - void update(unsigned cycles); + virtual void update(unsigned cycles); //! @brief This function is executed when the SNES is powered on or the reset button is pushed. void reset(); diff --git a/sources/APU/DSP/DSP.cpp b/sources/APU/DSP/DSP.cpp index 7775b40..0f926ce 100644 --- a/sources/APU/DSP/DSP.cpp +++ b/sources/APU/DSP/DSP.cpp @@ -581,4 +581,14 @@ namespace ComSquare::APU::DSP throw InvalidAddress("DSP Registers write", addr); } } + + Registers DSP::getRegisters() + { + return this->_registers; + } + + std::array DSP::getChannels() + { + return this->_channels; + } } \ No newline at end of file diff --git a/sources/APU/DSP/DSP.hpp b/sources/APU/DSP/DSP.hpp index 838db19..603a0db 100644 --- a/sources/APU/DSP/DSP.hpp +++ b/sources/APU/DSP/DSP.hpp @@ -64,10 +64,15 @@ namespace ComSquare::APU::DSP //! @brief Left channel volume register uint8_t volR; - //! @brief Lower 8 bits of pitch register - uint8_t pitchL; - //! @brief Higher 8 bits of pitch register - uint8_t pitchH; + union { + struct { + //! @brief Lower 8 bits of pitch register + uint8_t pitchL; + //! @brief Higher 8 bits of pitch register + uint8_t pitchH; + }; + uint16_t pitch; + }; //! @brief Key On register bool kon : 1; @@ -88,15 +93,20 @@ namespace ComSquare::APU::DSP //! @brief Source number register uint8_t srcn; - //! @brief Envelope register - uint8_t adsr1; - //! @brief Envelope controllers register - uint8_t adsr2; + union { + struct { + //! @brief Envelope register + uint8_t adsr1; + //! @brief Envelope controllers register + uint8_t adsr2; + }; + uint16_t envelope; + }; //! @brief Gain register uint8_t gain; //! @brief Envelope value register uint8_t envx; - //! @brief Wave _height register + //! @brief Wave height register uint8_t outx; //! @brief Echo FIR filter coefficients @@ -116,6 +126,10 @@ namespace ComSquare::APU::DSP DSP &operator=(const DSP &) = default; ~DSP() = default; + Registers getRegisters(); + + std::array getChannels(); + //! @brief Read from the internal DSP register. //! @param addr The address to read from. The address 0x0 should refer to the first byte of the register. //! @throw InvalidAddress will be thrown if the address is more than $7F (the number of register). diff --git a/sources/Debugger/APUDebug.cpp b/sources/Debugger/APUDebug.cpp new file mode 100644 index 0000000..443334e --- /dev/null +++ b/sources/Debugger/APUDebug.cpp @@ -0,0 +1,488 @@ +// //! @brief Convert a basic CPU to a debugging CPU. + +// Created by Melefo on 19/02/2020. +// + +#include "APUDebug.hpp" +#include "../Utility/Utility.hpp" + +using namespace ComSquare::APU; + +namespace ComSquare::Debugger +{ + APUDebug::APUDebug(APU &apu, SNES &snes) : + APU(apu), + QMainWindow(), + _ui(), + _snes(snes) + { + this->setContextMenuPolicy(Qt::NoContextMenu); + this->setAttribute(Qt::WA_QuitOnClose, false); + + this->_ui.setupUi(this); + this->show(); + this->_updatePanel(); + } + + void APUDebug::_updatePanel() + { + this->_ui.port0hexaLineEdit->setText(Utility::to_hex(this->_registers.port0).c_str()); + this->_ui.port0LineEdit->setText(Utility::to_binary(this->_registers.port0).c_str()); + + this->_ui.port1hexaLineEdit->setText(Utility::to_hex(this->_registers.port1).c_str()); + this->_ui.port1LineEdit->setText(Utility::to_binary(this->_registers.port1).c_str()); + + this->_ui.port2hexaLineEdit->setText(Utility::to_hex(this->_registers.port1).c_str()); + this->_ui.port2LineEdit->setText(Utility::to_binary(this->_registers.port1).c_str()); + + this->_ui.port3hexaLineEdit->setText(Utility::to_hex(this->_registers.port1).c_str()); + this->_ui.port3LineEdit->setText(Utility::to_binary(this->_registers.port1).c_str()); + + this->_ui.controlhexaLineEdit->setText(Utility::to_hex(this->_registers.ctrlreg).c_str()); + this->_ui.controlLineEdit->setText(Utility::to_binary(this->_registers.ctrlreg).c_str()); + + this->_ui.dSPRegAddresshexaLineEdit->setText(Utility::to_hex(this->_registers.dspregAddr).c_str()); + this->_ui.dSPRegAddressLineEdit->setText(Utility::to_binary(this->_registers.dspregAddr).c_str()); + + this->_ui.dSPRegDatahexaLineEdit->setText(Utility::to_hex(this->_registers.dspregData).c_str()); + this->_ui.dSPRegDataLineEdit->setText(Utility::to_binary(this->_registers.dspregData).c_str()); + + this->_ui.timer0hexaLineEdit->setText(Utility::to_hex(this->_registers.timer0).c_str()); + this->_ui.timer0LineEdit->setText(Utility::to_binary(this->_registers.timer0).c_str()); + + this->_ui.timer1hexaLineEdit->setText(Utility::to_hex(this->_registers.timer1).c_str()); + this->_ui.timer1LineEdit->setText(Utility::to_binary(this->_registers.timer1).c_str()); + + this->_ui.timer2hexaLineEdit->setText(Utility::to_hex(this->_registers.timer2).c_str()); + this->_ui.timer2LineEdit->setText(Utility::to_binary(this->_registers.timer2).c_str()); + + this->_ui.counter0hexaLineEdit->setText(Utility::to_hex(this->_registers.counter0).c_str()); + this->_ui.counter0LineEdit->setText(Utility::to_binary(this->_registers.counter0).c_str()); + + this->_ui.counter1hexaLineEdit->setText(Utility::to_hex(this->_registers.counter1).c_str()); + this->_ui.counter1LineEdit->setText(Utility::to_binary(this->_registers.counter1).c_str()); + + this->_ui.counter2hexaLineEdit->setText(Utility::to_hex(this->_registers.counter2).c_str()); + this->_ui.counter2LineEdit->setText(Utility::to_binary(this->_registers.counter2).c_str()); + + this->_ui.regMemhexaLineEdit->setText(Utility::to_hex(this->_registers.regmem1).c_str()); + this->_ui.regMemLineEdit->setText(Utility::to_binary(this->_registers.regmem1).c_str()); + + this->_ui.regMemhexaLineEdit_2->setText(Utility::to_hex(this->_registers.regmem2).c_str()); + this->_ui.regMemLineEdit_2->setText(Utility::to_binary(this->_registers.regmem2).c_str()); + + this->_ui.unknownhexaLineEdit->setText(Utility::to_hex(this->_registers.unknown).c_str()); + this->_ui.unknownLineEdit->setText(Utility::to_binary(this->_registers.unknown).c_str()); + + this->_ui.stackPointerLineEdit->setText(Utility::to_hex(this->_internalRegisters.sp).c_str()); + this->_ui.xIndexLineEdit->setText(Utility::to_hex(this->_internalRegisters.x).c_str()); + this->_ui.yIndexLineEdit->setText(Utility::to_hex(this->_internalRegisters.y).c_str()); + this->_ui.accumlatorLineEdit->setText(Utility::to_hex(this->_internalRegisters.a).c_str()); + this->_ui.programCounterLineEdit->setText(Utility::to_hex(this->_internalRegisters.pc).c_str()); + this->_ui.programStatusWordLineEdit->setText(this->_getPSWString().c_str()); + + this->_ui.mvolLprogressBar->setValue(this->_dsp->getRegisters().mvolL); + this->_ui.mvolRprogressBar->setValue(this->_dsp->getRegisters().mvolR); + this->_ui.evolLprogressBar->setValue(this->_dsp->getRegisters().evolL); + this->_ui.evolRprogressBar->setValue(this->_dsp->getRegisters().evolR); + this->_ui.echoprogressBar->setValue(this->_dsp->getRegisters().efb); + this->_ui.flagslineEdit->setText(Utility::to_binary(this->_dsp->getRegisters().flg).c_str()); + this->_ui.sourceDirectoryLineEdit->setText(Utility::to_hex(this->_dsp->getRegisters().dir).c_str()); + this->_ui.echoBufferOffsetLineEdit->setText(Utility::to_hex(this->_dsp->getRegisters().esa).c_str()); + this->_ui.echoDelayLineEdit->setText(Utility::to_hex(this->_dsp->getRegisters().edl).c_str()); + + this->_ui.VolumeLprogressBar->setValue(this->_dsp->getChannels()[0].volL); + this->_ui.VolumeRprogressBar->setValue(this->_dsp->getChannels()[0].volR); + this->_ui.WaveHeightprogressBar->setValue(this->_dsp->getChannels()[0].outx); + this->_ui.EchoFIRCoeffprogressBar->setValue(this->_dsp->getChannels()[0].coeff); + this->_ui.PitchlineEdit->setText(Utility::to_hex(this->_dsp->getChannels()[0].pitch).c_str()); + this->_ui.sourceNumberLineEdit->setText(Utility::to_hex(this->_dsp->getChannels()[0].srcn).c_str()); + this->_ui.GainlineEdit->setText(Utility::to_hex(this->_dsp->getChannels()[0].gain).c_str()); + this->_ui.EnvelopelineEdit->setText(Utility::to_hex(this->_dsp->getChannels()[0].envelope).c_str()); + this->_ui.EnvelopeValueLineEdit->setText(Utility::to_hex(this->_dsp->getChannels()[0].envx).c_str()); + this->_ui.KeyOncheckBox->setChecked(this->_dsp->getChannels()[0].kon); + this->_ui.KeyOffcheckBox->setChecked(this->_dsp->getChannels()[0].kof); + this->_ui.NoisecheckBox->setChecked(this->_dsp->getChannels()[0].non); + this->_ui.EchocheckBox->setChecked(this->_dsp->getChannels()[0].eon); + this->_ui.SampleEndcheckBox->setChecked(this->_dsp->getChannels()[0].endx); + this->_ui.PitchModulationcheckBox->setChecked(this->_dsp->getChannels()[0].pmon); + + this->_ui.VolumeLprogressBar_2->setValue(this->_dsp->getChannels()[1].volL); + this->_ui.VolumeRprogressBar_2->setValue(this->_dsp->getChannels()[1].volR); + this->_ui.WaveHeightprogressBar_2->setValue(this->_dsp->getChannels()[1].outx); + this->_ui.EchoFIRCoeffprogressBar_2->setValue(this->_dsp->getChannels()[1].coeff); + this->_ui.PitchlineEdit_2->setText(Utility::to_hex(this->_dsp->getChannels()[1].pitch).c_str()); + this->_ui.sourceNumberLineEdit_2->setText(Utility::to_hex(this->_dsp->getChannels()[1].srcn).c_str()); + this->_ui.GainlineEdit_2->setText(Utility::to_hex(this->_dsp->getChannels()[1].gain).c_str()); + this->_ui.EnvelopelineEdit_2->setText(Utility::to_hex(this->_dsp->getChannels()[1].envelope).c_str()); + this->_ui.EnvelopeValueLineEdit_2->setText(Utility::to_hex(this->_dsp->getChannels()[1].envx).c_str()); + this->_ui.KeyOncheckBox_2->setChecked(this->_dsp->getChannels()[1].kon); + this->_ui.KeyOffcheckBox_2->setChecked(this->_dsp->getChannels()[1].kof); + this->_ui.NoisecheckBox_2->setChecked(this->_dsp->getChannels()[1].non); + this->_ui.EchocheckBox_2->setChecked(this->_dsp->getChannels()[1].eon); + this->_ui.SampleEndcheckBox_2->setChecked(this->_dsp->getChannels()[1].endx); + this->_ui.PitchModulationcheckBox_2->setChecked(this->_dsp->getChannels()[1].pmon); + + this->_ui.VolumeLprogressBar_3->setValue(this->_dsp->getChannels()[2].volL); + this->_ui.VolumeRprogressBar_3->setValue(this->_dsp->getChannels()[2].volR); + this->_ui.WaveHeightprogressBar_3->setValue(this->_dsp->getChannels()[2].outx); + this->_ui.EchoFIRCoeffprogressBar_3->setValue(this->_dsp->getChannels()[2].coeff); + this->_ui.PitchlineEdit_3->setText(Utility::to_hex(this->_dsp->getChannels()[2].pitch).c_str()); + this->_ui.sourceNumberLineEdit_3->setText(Utility::to_hex(this->_dsp->getChannels()[2].srcn).c_str()); + this->_ui.GainlineEdit_3->setText(Utility::to_hex(this->_dsp->getChannels()[2].gain).c_str()); + this->_ui.EnvelopelineEdit_3->setText(Utility::to_hex(this->_dsp->getChannels()[2].envelope).c_str()); + this->_ui.EnvelopeValueLineEdit_3->setText(Utility::to_hex(this->_dsp->getChannels()[2].envx).c_str()); + this->_ui.KeyOncheckBox_3->setChecked(this->_dsp->getChannels()[2].kon); + this->_ui.KeyOffcheckBox_3->setChecked(this->_dsp->getChannels()[2].kof); + this->_ui.NoisecheckBox_3->setChecked(this->_dsp->getChannels()[2].non); + this->_ui.EchocheckBox_3->setChecked(this->_dsp->getChannels()[2].eon); + this->_ui.SampleEndcheckBox_3->setChecked(this->_dsp->getChannels()[2].endx); + this->_ui.PitchModulationcheckBox_3->setChecked(this->_dsp->getChannels()[2].pmon); + + this->_ui.VolumeLprogressBar_4->setValue(this->_dsp->getChannels()[3].volL); + this->_ui.VolumeRprogressBar_4->setValue(this->_dsp->getChannels()[3].volR); + this->_ui.WaveHeightprogressBar_4->setValue(this->_dsp->getChannels()[3].outx); + this->_ui.EchoFIRCoeffprogressBar_4->setValue(this->_dsp->getChannels()[3].coeff); + this->_ui.PitchlineEdit_4->setText(Utility::to_hex(this->_dsp->getChannels()[3].pitch).c_str()); + this->_ui.sourceNumberLineEdit_4->setText(Utility::to_hex(this->_dsp->getChannels()[3].srcn).c_str()); + this->_ui.GainlineEdit_4->setText(Utility::to_hex(this->_dsp->getChannels()[3].gain).c_str()); + this->_ui.EnvelopelineEdit_4->setText(Utility::to_hex(this->_dsp->getChannels()[3].envelope).c_str()); + this->_ui.EnvelopeValueLineEdit_4->setText(Utility::to_hex(this->_dsp->getChannels()[3].envx).c_str()); + this->_ui.KeyOncheckBox_4->setChecked(this->_dsp->getChannels()[3].kon); + this->_ui.KeyOffcheckBox_4->setChecked(this->_dsp->getChannels()[3].kof); + this->_ui.NoisecheckBox_4->setChecked(this->_dsp->getChannels()[3].non); + this->_ui.EchocheckBox_4->setChecked(this->_dsp->getChannels()[3].eon); + this->_ui.SampleEndcheckBox_4->setChecked(this->_dsp->getChannels()[3].endx); + this->_ui.PitchModulationcheckBox_4->setChecked(this->_dsp->getChannels()[3].pmon); + + this->_ui.VolumeLprogressBar_5->setValue(this->_dsp->getChannels()[4].volL); + this->_ui.VolumeRprogressBar_5->setValue(this->_dsp->getChannels()[4].volR); + this->_ui.WaveHeightprogressBar_5->setValue(this->_dsp->getChannels()[4].outx); + this->_ui.EchoFIRCoeffprogressBar_5->setValue(this->_dsp->getChannels()[4].coeff); + this->_ui.PitchlineEdit_5->setText(Utility::to_hex(this->_dsp->getChannels()[4].pitch).c_str()); + this->_ui.sourceNumberLineEdit_5->setText(Utility::to_hex(this->_dsp->getChannels()[4].srcn).c_str()); + this->_ui.GainlineEdit_5->setText(Utility::to_hex(this->_dsp->getChannels()[4].gain).c_str()); + this->_ui.EnvelopelineEdit_5->setText(Utility::to_hex(this->_dsp->getChannels()[4].envelope).c_str()); + this->_ui.EnvelopeValueLineEdit_5->setText(Utility::to_hex(this->_dsp->getChannels()[4].envx).c_str()); + this->_ui.KeyOncheckBox_5->setChecked(this->_dsp->getChannels()[4].kon); + this->_ui.KeyOffcheckBox_5->setChecked(this->_dsp->getChannels()[4].kof); + this->_ui.NoisecheckBox_5->setChecked(this->_dsp->getChannels()[4].non); + this->_ui.EchocheckBox_5->setChecked(this->_dsp->getChannels()[4].eon); + this->_ui.SampleEndcheckBox_5->setChecked(this->_dsp->getChannels()[4].endx); + this->_ui.PitchModulationcheckBox_5->setChecked(this->_dsp->getChannels()[4].pmon); + + this->_ui.VolumeLprogressBar_6->setValue(this->_dsp->getChannels()[5].volL); + this->_ui.VolumeRprogressBar_6->setValue(this->_dsp->getChannels()[5].volR); + this->_ui.WaveHeightprogressBar_6->setValue(this->_dsp->getChannels()[5].outx); + this->_ui.EchoFIRCoeffprogressBar_6->setValue(this->_dsp->getChannels()[5].coeff); + this->_ui.PitchlineEdit_6->setText(Utility::to_hex(this->_dsp->getChannels()[5].pitch).c_str()); + this->_ui.sourceNumberLineEdit_6->setText(Utility::to_hex(this->_dsp->getChannels()[5].srcn).c_str()); + this->_ui.GainlineEdit_6->setText(Utility::to_hex(this->_dsp->getChannels()[5].gain).c_str()); + this->_ui.EnvelopelineEdit_6->setText(Utility::to_hex(this->_dsp->getChannels()[5].envelope).c_str()); + this->_ui.EnvelopeValueLineEdit_6->setText(Utility::to_hex(this->_dsp->getChannels()[5].envx).c_str()); + this->_ui.KeyOncheckBox_6->setChecked(this->_dsp->getChannels()[5].kon); + this->_ui.KeyOffcheckBox_6->setChecked(this->_dsp->getChannels()[5].kof); + this->_ui.NoisecheckBox_6->setChecked(this->_dsp->getChannels()[5].non); + this->_ui.EchocheckBox_6->setChecked(this->_dsp->getChannels()[5].eon); + this->_ui.SampleEndcheckBox_6->setChecked(this->_dsp->getChannels()[5].endx); + this->_ui.PitchModulationcheckBox_6->setChecked(this->_dsp->getChannels()[5].pmon); + + this->_ui.VolumeLprogressBar_7->setValue(this->_dsp->getChannels()[6].volL); + this->_ui.VolumeRprogressBar_7->setValue(this->_dsp->getChannels()[6].volR); + this->_ui.WaveHeightprogressBar_7->setValue(this->_dsp->getChannels()[6].outx); + this->_ui.EchoFIRCoeffprogressBar_7->setValue(this->_dsp->getChannels()[6].coeff); + this->_ui.PitchlineEdit_7->setText(Utility::to_hex(this->_dsp->getChannels()[6].pitch).c_str()); + this->_ui.sourceNumberLineEdit_7->setText(Utility::to_hex(this->_dsp->getChannels()[6].srcn).c_str()); + this->_ui.GainlineEdit_7->setText(Utility::to_hex(this->_dsp->getChannels()[6].gain).c_str()); + this->_ui.EnvelopelineEdit_7->setText(Utility::to_hex(this->_dsp->getChannels()[6].envelope).c_str()); + this->_ui.EnvelopeValueLineEdit_7->setText(Utility::to_hex(this->_dsp->getChannels()[6].envx).c_str()); + this->_ui.KeyOncheckBox_7->setChecked(this->_dsp->getChannels()[6].kon); + this->_ui.KeyOffcheckBox_7->setChecked(this->_dsp->getChannels()[6].kof); + this->_ui.NoisecheckBox_7->setChecked(this->_dsp->getChannels()[6].non); + this->_ui.EchocheckBox_7->setChecked(this->_dsp->getChannels()[6].eon); + this->_ui.SampleEndcheckBox_7->setChecked(this->_dsp->getChannels()[6].endx); + this->_ui.PitchModulationcheckBox_7->setChecked(this->_dsp->getChannels()[6].pmon); + + this->_ui.VolumeLprogressBar_8->setValue(this->_dsp->getChannels()[7].volL); + this->_ui.VolumeRprogressBar_8->setValue(this->_dsp->getChannels()[7].volR); + this->_ui.WaveHeightprogressBar_8->setValue(this->_dsp->getChannels()[7].outx); + this->_ui.EchoFIRCoeffprogressBar_8->setValue(this->_dsp->getChannels()[7].coeff); + this->_ui.PitchlineEdit_8->setText(Utility::to_hex(this->_dsp->getChannels()[7].pitch).c_str()); + this->_ui.sourceNumberLineEdit_8->setText(Utility::to_hex(this->_dsp->getChannels()[7].srcn).c_str()); + this->_ui.GainlineEdit_8->setText(Utility::to_hex(this->_dsp->getChannels()[7].gain).c_str()); + this->_ui.EnvelopelineEdit_8->setText(Utility::to_hex(this->_dsp->getChannels()[7].envelope).c_str()); + this->_ui.EnvelopeValueLineEdit_8->setText(Utility::to_hex(this->_dsp->getChannels()[7].envx).c_str()); + this->_ui.KeyOncheckBox_8->setChecked(this->_dsp->getChannels()[7].kon); + this->_ui.KeyOffcheckBox_8->setChecked(this->_dsp->getChannels()[7].kof); + this->_ui.NoisecheckBox_8->setChecked(this->_dsp->getChannels()[7].non); + this->_ui.EchocheckBox_8->setChecked(this->_dsp->getChannels()[7].eon); + this->_ui.SampleEndcheckBox_8->setChecked(this->_dsp->getChannels()[7].endx); + this->_ui.PitchModulationcheckBox_8->setChecked(this->_dsp->getChannels()[7].pmon); + } + + std::string APUDebug::_getPSWString() + { + std::string str; + str += this->_internalRegisters.n ? 'n' : '-'; + str += this->_internalRegisters.v ? 'v' : '-'; + str += this->_internalRegisters.p ? 'p' : '-'; + str += this->_internalRegisters.b ? 'b' : '-'; + str += this->_internalRegisters.h ? 'h' : '-'; + str += this->_internalRegisters.i ? 'i' : '-'; + str += this->_internalRegisters.z ? 'z' : '-'; + str += this->_internalRegisters.c ? 'c' : '-'; + return str; + } + + std::string APUDebug::_getInstructionString() + { + uint8_t opcode = this->_internalRead(this->_internalRegisters.pc); + + switch (opcode) { + case 0x00: + return "NOP"; + case 0x10: + return "BPL"; + case 0x20: + return "CLRP"; + case 0x30: + return "BMI"; + case 0x40: + return "SETP"; + case 0x50: + return "BVC"; + case 0x60: + return "CLRC"; + case 0x70: + return "BVS"; + case 0x80: + return "SETC"; + case 0x90: + return "BCC"; + case 0xA0: + return "EI"; + case 0xB0: + return "BCS"; + case 0xC0: + return "DI"; + case 0xD0: + return "BNE"; + case 0xE0: + return "CLRV"; + case 0xF0: + return "BEQ"; + case 0x01: + case 0x11: + case 0x21: + case 0x31: + case 0x41: + case 0x51: + case 0x61: + case 0x71: + case 0x81: + case 0x91: + case 0xA1: + case 0xB1: + case 0xC1: + case 0xD1: + case 0xE1: + case 0xF1: + return "TCALL"; + case 0x02: + case 0x22: + case 0x42: + case 0x62: + case 0x82: + case 0xA2: + case 0xC2: + case 0xE2: + return "SET1"; + case 0x12: + case 0x32: + case 0x52: + case 0x72: + case 0x92: + case 0xB2: + case 0xD2: + case 0xF2: + return "CLR1"; + case 0x03: + case 0x13: + case 0x23: + case 0x33: + case 0x43: + case 0x53: + case 0x63: + case 0x73: + case 0x83: + case 0x93: + case 0xA3: + case 0xB3: + case 0xC3: + case 0xD3: + case 0xE3: + case 0xF3: + return "BBC"; + case 0x04 ... 0x09: + case 0x14 ... 0x19: + return "OR"; + case 0x24 ... 0x29: + case 0x34 ... 0x39: + return "AND"; + case 0x44 ... 0x49: + case 0x54 ... 0x59: + return "EOR"; + case 0x64 ... 0x69: + case 0x74 ... 0x79: + case 0xC8: + case 0xAD: + case 0x1E: + case 0x3E: + case 0x5E: + case 0x7E: + return "CMP"; + case 0x84 ... 0x89: + case 0x94 ... 0x99: + return "ADC"; + case 0xA4 ... 0xA9: + case 0xB4 ... 0xB9: + return "SBC"; + case 0xC4 ... 0xC7: + case 0xCB ... 0xCD: + case 0xD4 ... 0xD9: + case 0xE4 ... 0xE9: + case 0xEB ... 0xEC: + case 0xF4 ... 0xFB: + case 0xDB: + case 0xC9: + case 0x5D: + case 0x7D: + case 0x8D: + case 0x9D: + case 0xBD: + case 0xDD: + case 0xFD: + case 0x8F: + case 0xAF: + case 0xBF: + return "MOV"; + case 0x0A: + case 0x2A: + return "OR1"; + case 0x1A: + return "DECW"; + case 0x3A: + return "INCW"; + case 0x4A: + case 0x6A: + return "AND1"; + case 0x5A: + return "CMPW"; + case 0x7A: + return "ADDW"; + case 0x8A: + return "EOR1"; + case 0x9A: + return "SUBW"; + case 0xAA: + case 0xCA: + return "MOV1"; + case 0xBA: + case 0xDA: + return "MOVW"; + case 0xEA: + return "NOT1"; + case 0x0B ... 0x0C: + case 0x1B ... 0x1C: + return "ASL"; + case 0x2B ... 0x2C: + case 0x3B ... 0x3C: + return "ROL"; + case 0x4B ... 0x4C: + case 0x5B ... 0x5C: + return "LSR"; + case 0x6B ... 0x6C: + case 0x7B ... 0x7C: + return "ROR"; + case 0x8B ... 0x8C: + case 0x9B ... 0x9C: + case 0xDC: + case 0x1D: + return "DEC"; + case 0xAB ... 0xAC: + case 0xBB ... 0xBC: + case 0xFC: + case 0x3D: + return "INC"; + case 0x0D: + case 0x2D: + case 0x4D: + case 0x6D: + return "PUSH"; + case 0xED: + return "NOTC"; + case 0x0E: + return "TSET1"; + case 0x2E: + case 0xDE: + return "CBNE"; + case 0x4E: + return "TCLR1"; + case 0x6E: + case 0xFE: + return "DBNZ"; + case 0x8E: + case 0xAE: + case 0xCE: + case 0xEE: + return "POP"; + case 0x9E: + return "DIV"; + case 0xBE: + return "DAS"; + case 0x0F: + return "BRK"; + case 0x1F: + case 0x5F: + return "JMP"; + case 0x2F: + return "BRA"; + case 0x3F: + return "CALL"; + case 0x4F: + return "PCALL"; + case 0x6F: + return "RET"; + case 0x7F: + return "RETI"; + case 0x9F: + return "XCN"; + case 0xCF: + return "MUL"; + case 0xDF: + return "DAA"; + case 0xEF: + return "SLEEP"; + case 0xFF: + return "STOP"; + default: + return "Unknown"; + } + } + + int APUDebug::_executeInstruction() + { + this->_ui.logger->append(APUDebug::_getInstructionString().c_str()); + this->_updatePanel(); + return APU::_executeInstruction(); + } + + void APUDebug::update(unsigned cycles) + { + if (!this->isVisible()) { + this->_snes.disableAPUDebugging(); + return; + } + return APU::update(cycles); + } +} \ No newline at end of file diff --git a/sources/Debugger/APUDebug.hpp b/sources/Debugger/APUDebug.hpp new file mode 100644 index 0000000..41d66e1 --- /dev/null +++ b/sources/Debugger/APUDebug.hpp @@ -0,0 +1,45 @@ +// +// Created by Melefo on 19/02/2020. +// + +#ifndef COMSQUARE_APUDEBUG_HPP +#define COMSQUARE_APUDEBUG_HPP + +#include "../APU/APU.hpp" +#include "../SNES.hpp" +#include "../../ui/ui_apuView.h" + +namespace ComSquare::Debugger +{ + class APUDebug : public APU::APU, public QMainWindow { + private: + //! @brief A widget that contain the whole UI. + Ui::APUView _ui; + + //! @brief A reference to the snes (to disable the debugger). + SNES &_snes; + + //! @brief Update the debugger panel values + void _updatePanel(); + + //! @brief Convert CPU APU flags to a string. + std::string _getPSWString(); + + //! @brief Replace original _executeInstruction to write to the logger. + int _executeInstruction() override; + + //! @brief return the mnemonic of the current instruction done. + std::string _getInstructionString(); + public: + //! @brief Convert a basic APU to a debugging APU. + explicit APUDebug(ComSquare::APU::APU &apu, SNES &snes); + APUDebug(const APUDebug &) = delete; + APUDebug &operator=(const APUDebug &) = delete; + ~APUDebug() override = default; + + //! @brief Override the apu's update to disable debugging. + void update(unsigned cycles) override; + }; +} + +#endif //COMSQUARE_APUDEBUG_HPP diff --git a/sources/Renderer/QtRenderer/QtSFML.cpp b/sources/Renderer/QtRenderer/QtSFML.cpp index 1b9be1b..a2abe00 100644 --- a/sources/Renderer/QtRenderer/QtSFML.cpp +++ b/sources/Renderer/QtRenderer/QtSFML.cpp @@ -51,6 +51,10 @@ namespace ComSquare::Renderer headerViewer->setShortcut(Qt::Key_F3); QMainWindow::connect(headerViewer, &QAction::triggered, this->_sfWidget.get(), &QtFullSFML::enableHeaderViewer); debugger->addAction(headerViewer); + QAction *apuDebugger = new QAction("APU's Debugger", &this->_window); + apuDebugger->setShortcut(Qt::Key_F4); + QMainWindow::connect(apuDebugger, &QAction::triggered, this->_sfWidget.get(), &QtFullSFML::enableDebugAPU); + debugger->addAction(apuDebugger); this->_window.show(); } @@ -101,4 +105,9 @@ namespace ComSquare::Renderer { this->_snes.enableHeaderViewer(); } + + void QtFullSFML::enableDebugAPU() + { + this->_snes.enableAPUDebugging(); + } } \ No newline at end of file diff --git a/sources/Renderer/QtRenderer/QtSFML.hpp b/sources/Renderer/QtRenderer/QtSFML.hpp index aac6531..66096b6 100644 --- a/sources/Renderer/QtRenderer/QtSFML.hpp +++ b/sources/Renderer/QtRenderer/QtSFML.hpp @@ -29,6 +29,8 @@ namespace ComSquare::Renderer void enableRamViewer(); //! @brief Action called when clicking on the enable Header viewer button. void enableHeaderViewer(); + //! @brief Action called when clicking on the enable APU debugger button. + void enableDebugAPU(); //! @brief Action called when clicking on the reset button. void reset(); QtFullSFML(SNES &snes, QWidget* parent, const QPoint& position, const QSize& size, int frameRate = 0); diff --git a/sources/SNES.cpp b/sources/SNES.cpp index 5ffe583..10d742c 100644 --- a/sources/SNES.cpp +++ b/sources/SNES.cpp @@ -6,18 +6,20 @@ #include #include "SNES.hpp" #ifdef DEBUGGER_ENABLED - #include "Debugger/CPUDebug.hpp" +#include "Debugger/CPUDebug.hpp" +#include "Debugger/APUDebug.hpp" #endif namespace ComSquare { SNES::SNES(const std::shared_ptr &bus, const std::string &romPath, Renderer::IRenderer &renderer) : cartridge(new Cartridge::Cartridge(romPath)), + wram(new Ram::Ram(16384)), + sram(new Ram::Ram(this->cartridge->header.sramSize)), + apuRam(new APU::MemoryMap()), cpu(new CPU::CPU(bus, cartridge->header)), ppu(new PPU::PPU(bus, renderer)), - apu(new APU::APU()), - wram(new Ram::Ram(16384)), - sram(new Ram::Ram(this->cartridge->header.sramSize)) + apu(new APU::APU(this->apuRam)) { bus->mapComponents(*this); } @@ -33,6 +35,7 @@ namespace ComSquare void SNES::disableCPUDebugging() { + std::cout << "Disable the debugger of the CPU" << std::endl; this->cpu = std::make_shared(*this->cpu); } @@ -70,4 +73,18 @@ namespace ComSquare this->_headerViewer = nullptr; #endif } + + void SNES::enableAPUDebugging() + { + #ifdef DEBUGGER_ENABLED + this->apu = std::make_shared(*this->apu, *this); + #else + std::cerr << "Debugging features are not enabled. You can't enable the debugger." << std::endl; + #endif + } + + void SNES::disableAPUDebugging() + { + this->apu = std::make_shared(*this->apu); + } } diff --git a/sources/SNES.hpp b/sources/SNES.hpp index 868936d..282e8d7 100644 --- a/sources/SNES.hpp +++ b/sources/SNES.hpp @@ -32,16 +32,18 @@ namespace ComSquare public: //! @brief Cartridge containing instructions (ROM). std::shared_ptr cartridge; + //! @brief Work Ram shared by all the components. + std::shared_ptr wram; + //! @brief Save Ram residing inside the Cartridge in a real SNES. + std::shared_ptr sram; + //! @brief External Ram used only by the Audio Processing Unit + std::shared_ptr apuRam; //! @brief Central Processing Unit of the SNES. std::shared_ptr cpu; //! @brief Picture Processing Unit of the SNES std::shared_ptr ppu; //! @brief Audio Processing Unit if the SNES std::shared_ptr apu; - //! @brief Work Ram shared by all the components. - std::shared_ptr wram; - //! @brief Save Ram residing inside the Cartridge in a real SNES. - std::shared_ptr sram; //! @brief Call this function to update all the components void update(); @@ -58,6 +60,10 @@ namespace ComSquare void disableHeaderViewer(); //! @brief Enable the Header's debugging window. void enableHeaderViewer(); + //! @brief Disable the APU's debugging window. + void disableAPUDebugging(); + //! @brief Enable the APU's debugging window. + void enableAPUDebugging(); //! @brief Create all the components using a common memory bus for all of them. SNES(const std::shared_ptr &bus, const std::string &ramPath, Renderer::IRenderer &renderer); diff --git a/sources/Utility/Utility.cpp b/sources/Utility/Utility.cpp index 2273741..a56c99d 100644 --- a/sources/Utility/Utility.cpp +++ b/sources/Utility/Utility.cpp @@ -2,6 +2,7 @@ // Created by anonymus-raccoon on 2/17/20. // +#include #include "Utility.hpp" namespace ComSquare::Utility @@ -26,4 +27,21 @@ namespace ComSquare::Utility sprintf(buf, "0x%06X", i); return buf; } + + std::string to_binary(uint8_t i) + { + return std::bitset<8>(i).to_string(); + } + + std::string to_binary(uint16_t i) + { + return std::bitset<16>(i).to_string(); + + } + + std::string to_binary(uint24_t i) + { + return std::bitset<24>(i).to_string(); + + } } \ No newline at end of file diff --git a/sources/Utility/Utility.hpp b/sources/Utility/Utility.hpp index 367995a..2fbc8bb 100644 --- a/sources/Utility/Utility.hpp +++ b/sources/Utility/Utility.hpp @@ -17,6 +17,12 @@ namespace ComSquare::Utility std::string to_hex(uint16_t i); std::string to_hex(uint24_t i); + + std::string to_binary(uint8_t i); + + std::string to_binary(uint16_t i); + + std::string to_binary(uint24_t i); } #endif //COMSQUARE_UTILITY_HPP diff --git a/tests/APU/testAPU.cpp b/tests/APU/testAPU.cpp index c7aa1ce..2187296 100644 --- a/tests/APU/testAPU.cpp +++ b/tests/APU/testAPU.cpp @@ -32,7 +32,7 @@ Test(_internalRead, Page0) auto apu = Init().second.apu; uint8_t result = 0; - apu->_map.Page0._data[0x0010] = 123; + apu->_map->Page0._data[0x0010] = 123; result = apu->_internalRead(0x0010); cr_assert_eq(result, 123); } @@ -42,7 +42,7 @@ Test(_internalRead, Page1) auto apu = Init().second.apu; uint8_t result = 0; - apu->_map.Page1._data[0x0042] = 123; + apu->_map->Page1._data[0x0042] = 123; result = apu->_internalRead(0x0142); cr_assert_eq(result, 123); } @@ -52,7 +52,7 @@ Test(_internalRead, Memory) auto apu = Init().second.apu; uint8_t result = 0; - apu->_map.Memory._data[0xFCDC] = 123; + apu->_map->Memory._data[0xFCDC] = 123; result = apu->_internalRead(0xFEDC); cr_assert_eq(result, 123); } @@ -62,7 +62,7 @@ Test(_internalRead, IPL) auto apu = Init().second.apu; uint8_t result = 0; - apu->_map.IPL._data[0x001F] = 123; + apu->_map->IPL._data[0x001F] = 123; result = apu->_internalRead(0xFFDF); cr_assert_eq(result, 123); } @@ -85,7 +85,7 @@ Test(_internalWrite, Page0) auto apu = Init().second.apu; apu->_internalWrite(0x0001, 123); - cr_assert_eq(apu->_map.Page0._data[0x0001], 123); + cr_assert_eq(apu->_map->Page0._data[0x0001], 123); } Test(_internalWrite, register) @@ -101,7 +101,7 @@ Test(_internalWrite, Page1) auto apu = Init().second.apu; apu->_internalWrite(0x01FF, 123); - cr_assert_eq(apu->_map.Page1._data[0x00FF], 123); + cr_assert_eq(apu->_map->Page1._data[0x00FF], 123); } Test(_internalWrite, Memory) @@ -109,7 +109,7 @@ Test(_internalWrite, Memory) auto apu = Init().second.apu; apu->_internalWrite(0x0789, 123); - cr_assert_eq(apu->_map.Memory._data[0x0589], 123); + cr_assert_eq(apu->_map->Memory._data[0x0589], 123); } Test(_internalWrite, IPL) @@ -117,7 +117,7 @@ Test(_internalWrite, IPL) auto apu = Init().second.apu; apu->_internalWrite(0xFFF0, 123); - cr_assert_eq(apu->_map.IPL._data[0x0030], 123); + cr_assert_eq(apu->_map->IPL._data[0x0030], 123); } Test(_internalWrite, Invalid)