Merge branch 'catch' of github.com:AnonymusRaccoon/ComSquare into PPU

# Conflicts:
#	CMakeLists.txt
#	sources/PPU/Background.cpp
#	sources/PPU/Background.hpp
#	sources/PPU/PPU.hpp
This commit is contained in:
Clément Le Bihan
2021-07-08 01:07:19 +02:00
119 changed files with 7356 additions and 7324 deletions

View File

@@ -10,7 +10,7 @@ on:
jobs:
Building:
runs-on: [ubuntu-18.04]
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v1
@@ -31,9 +31,9 @@ jobs:
- name: Build Makefile with CMake
run: mkdir -p build && cd build && cmake ..
- name: Build with Makefile
run: make -C build ComSquare
run: make -C build comsquare
- name: Archive production artifact
uses: actions/upload-artifact@v1
with:
name: ComSquare
path: build/ComSquare
name: comsquare
path: build/comsquare

View File

@@ -29,11 +29,11 @@ jobs:
cmake .. -G "MinGW Makefiles" -DCMAKE_SH="CMAKE_SH-NOTFOUND" -DGITBUILD=true
- name: Build with Makefile
run: |
mingw32-make -C build ComSquare
mingw32-make -C build comsquare
- name: Clean up
run: |
Set-Location build
Remove-Item -Recurse -Path * -Exclude ComSquare.exe,bin,bin/*
Remove-Item -Recurse -Path * -Exclude comsquare.exe,bin,bin/*
- name: Copy shared libs
run: |
Copy-Item -Path build\bin\*.dll -Destination build
@@ -41,5 +41,5 @@ jobs:
- name: Archive production artifact
uses: actions/upload-artifact@v1
with:
name: ComSquare-Windows
name: comsquare-Windows
path: build/

View File

@@ -21,18 +21,8 @@ jobs:
uses: actions/setup-python@v1
with:
python-version: '3.x'
- name: Install the SFML.
run: sudo apt-get update &&
sudo apt-get install --yes libsfml-dev qt5-default
- name: Install Criterion
run: git clone --recursive https://github.com/Snaipe/Criterion &&
cd Criterion &&
sudo apt install --yes ninja-build &&
pip3 install meson &&
meson build &&
ninja -C build &&
sudo ninja -C build install &&
sudo cp -r /usr/local/lib/x86_64-linux-gnu/libcriterion* /usr/lib
- name: Install the SFML and QT5.
run: sudo apt-get update && sudo apt-get install --yes libsfml-dev qt5-default
- name: Install Gcovr
run: python -m pip install --upgrade pip gcovr
- name: Update G++

View File

@@ -1,13 +1,11 @@
cmake_minimum_required(VERSION 3.12)
set(CMAKE_CXX_STANDARD 20)
# set the project name
project(ComSquare)
# show compilation warnings
add_compile_options(-W -Wall -Wextra -Wshadow)
include_directories(sources)
if (CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS_DEBUG "-Wall -Wextra -Wshadow -Wno-unused-parameter -W -g")
endif ()
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
include_directories(sources)
@@ -29,8 +27,8 @@ set(SOURCES
sources/APU/APU.cpp
sources/Exceptions/InvalidAddress.hpp
sources/Exceptions/InvalidRom.hpp
sources/Models/Int24.hpp
sources/Models/Int24.hpp
sources/Models/Ints.hpp
sources/Models/Ints.hpp
sources/Ram/Ram.cpp
sources/Ram/Ram.hpp
sources/Memory/MemoryShadow.cpp
@@ -55,8 +53,6 @@ set(SOURCES
sources/CPU/Instructions/MathematicalOperations.cpp
sources/CPU/Instructions/MemoryInstructions.cpp
sources/CPU/Instructions/InternalInstruction.cpp
sources/Ram/ExtendedRam.cpp
sources/Ram/ExtendedRam.hpp
sources/Utility/Utility.hpp
sources/Utility/Utility.cpp
sources/CPU/Instructions/BitsInstructions.cpp
@@ -98,6 +94,10 @@ set(SOURCES
sources/PPU/TileRenderer.cpp
sources/PPU/TileRenderer.hpp
sources/PPU/Tile.hpp
sources/CPU/Registers.hpp
sources/Memory/IMemoryBus.hpp
sources/Models/Callback.hpp
sources/Models/Logger.hpp
sources/PPU/PpuDebug.cpp
sources/PPU/PpuDebug.hpp
)
@@ -107,7 +107,7 @@ set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOUIC_SEARCH_PATHS ./)
add_executable(ComSquare
add_executable(comsquare
sources/main.cpp
${SOURCES}
sources/Renderer/SFRenderer.hpp
@@ -120,26 +120,24 @@ add_executable(ComSquare
sources/Renderer/QtRenderer/QtSfmlTileRenderer.hpp
sources/Renderer/QtRenderer/QtSfmlTileRenderer.cpp
sources/Renderer/QtRenderer/QtSfmlTileRenderer.hpp
sources/Debugger/ClosableWindow.hpp
sources/Debugger/CPU/CPUDebug.cpp
sources/Debugger/CPU/CPUDebug.hpp
sources/Debugger/CPU/Disassembly.cpp
sources/Debugger/CPU/SymbolLoaders/WlaDx.cpp
sources/Debugger/CPU/SymbolLoaders/WlaDx.hpp
sources/Debugger/MemoryViewer.cpp
sources/Debugger/MemoryViewer.hpp
sources/Debugger/HeaderViewer.cpp
sources/Debugger/HeaderViewer.hpp
sources/Debugger/HeaderViewer.cpp
sources/Debugger/HeaderViewer.hpp
sources/Debugger/APUDebug.hpp
sources/Debugger/APUDebug.cpp
sources/Debugger/MemoryBusDebug.cpp
sources/Debugger/MemoryBusDebug.hpp
sources/Debugger/ClosableWindow.hpp
sources/Debugger/CPU/Disassembly.cpp
sources/Debugger/CGramDebug.cpp
sources/Debugger/CGramDebug.hpp
sources/Debugger/RegisterViewer.cpp
sources/Debugger/RegisterViewer.hpp
sources/Debugger/CPU/SymbolLoaders/WlaDx.cpp
sources/Debugger/CPU/SymbolLoaders/WlaDx.hpp
sources/Debugger/TileViewer/TileViewer.cpp
sources/Debugger/TileViewer/TileViewer.hpp
sources/Debugger/TileViewer/RAMTileRenderer.cpp
@@ -153,42 +151,48 @@ add_executable(ComSquare
ui/busView.ui
resources/appResources.qrc
)
target_include_directories(ComSquare PRIVATE ./)
target_compile_definitions(ComSquare PUBLIC DEBUGGER_ENABLED)
target_include_directories(comsquare PUBLIC ${PROJECT_BINARY_DIR})
target_compile_definitions(comsquare PUBLIC DEBUGGER_ENABLED)
find_package(Qt5 COMPONENTS Widgets REQUIRED)
target_link_libraries(ComSquare
target_link_libraries(comsquare
sfml-graphics
sfml-window
sfml-system
sfml-audio
sfml-network
Qt5::Widgets
)
)
add_executable(unit_tests EXCLUDE_FROM_ALL
${SOURCES}
tests/PPU/testPpuWrite.cpp
tests/PPU/testPpuRead.cpp
tests/PPU/testBackground.cpp
tests/PPU/testPpuWriteFromVmain.cpp
tests/testRectangleMemory.cpp
tests/tests.hpp
tests/CPU/testInterupts.cpp
tests/APU/testAPUInstructions.cpp
tests/APU/testAPU.cpp
tests/APU/testOperand.cpp
tests/tests.hpp
tests/testMemoryBus.cpp
tests/CPU/Math/testSBC.cpp
tests/PPU/testPpuWrite.cpp
tests/PPU/testPpuWriteFromVmain.cpp
tests/CPU/Math/testADC.cpp
tests/CPU/Math/testCMP.cpp
tests/CPU/Math/testOthersMath.cpp
tests/CPU/testDMA.cpp
tests/CPU/testTransfers.cpp
tests/CPU/testInterupts.cpp
tests/CPU/testAddressingMode.cpp
tests/CPU/testStore.cpp
tests/CPU/testInternal.cpp
tests/CPU/testBits.cpp
tests/CPU/testStore.cpp
)
target_link_libraries(unit_tests criterion -lgcov)
target_compile_options(unit_tests PUBLIC -fprofile-arcs -ftest-coverage)
tests/APU/testOperand.cpp
tests/CPU/Math/testSBC.cpp
tests/CPU/testTransfers.cpp
tests/CPU/Math/testOthersMath.cpp
tests/testRectangleMemory.cpp
tests/CPU/Math/testCMP.cpp
tests/CPU/testDMA.cpp
tests/CPU/testAddressingMode.cpp
tests/testMemoryBus.cpp
)
target_include_directories(unit_tests PUBLIC tests)
target_compile_definitions(unit_tests PUBLIC TESTS)
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/libs)
find_package(Catch2 REQUIRED)
target_link_libraries(unit_tests PRIVATE Catch2::Catch2WithMain)
if (CMAKE_COMPILER_IS_GNUCXX)
target_link_libraries(unit_tests PRIVATE -lgcov)
target_compile_options(unit_tests PUBLIC -fprofile-arcs -ftest-coverage)
endif ()

13
libs/FindCatch2.cmake Normal file
View File

@@ -0,0 +1,13 @@
cmake_minimum_required(VERSION 3.11)
include(FetchContent)
FetchContent_Declare(Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.0.0-preview3
)
FetchContent_GetProperties(Catch2)
if(NOT Catch2_POPULATED)
FetchContent_Populate(Catch2)
add_subdirectory(${catch2_SOURCE_DIR} ${catch2_BINARY_DIR} EXCLUDE_FROM_ALL)
endif()

View File

@@ -2,29 +2,21 @@
// Created by Melefo on 27/01/2020.
//
#include <iostream>
#include <cstring>
#include "APU.hpp"
#include "../Exceptions/NotImplementedException.hpp"
#include "../Exceptions/InvalidAddress.hpp"
#include "../Exceptions/InvalidOpcode.hpp"
#include "Exceptions/InvalidAddress.hpp"
#include "Exceptions/InvalidOpcode.hpp"
#include <cstring>
#include <iostream>
#include <algorithm>
namespace ComSquare::APU
{
APU::APU(Renderer::IRenderer &renderer) :
_renderer(renderer),
_map(new MemoryMap()),
_soundBuffer(),
_dsp(this->_soundBuffer, this->_soundBuffer.size() / 2, _map)
APU::APU(Renderer::IRenderer &renderer)
: _dsp(renderer, this->_map)
{
this->reset();
}
bool APU::isDebugger() const
{
return false;
}
std::string APU::getName() const
{
return "APU";
@@ -39,7 +31,7 @@ namespace ComSquare::APU
{
switch (addr) {
case 0x0000 ... 0x00EF:
return this->_map->Page0.read(addr);
return this->_map.Page0[addr];
case 0xF0:
return this->_registers.unknown;
case 0xF2:
@@ -65,11 +57,11 @@ namespace ComSquare::APU
case 0xFF:
return this->_registers.counter2;
case 0x0100 ... 0x01FF:
return this->_map->Page1.read(addr - 0x0100);
return this->_map.Page1[addr - 0x0100];
case 0x0200 ... 0xFFBF:
return this->_map->Memory.read(addr - 0x200);
return this->_map.Memory[addr - 0x200];
case 0xFFC0 ... 0xFFFF:
return this->_map->IPL.read(addr - 0xFFC0);
return this->_map.IPL[addr - 0xFFC0];
default:
throw InvalidAddress("APU Registers read", addr);
}
@@ -79,7 +71,7 @@ namespace ComSquare::APU
{
switch (addr) {
case 0x0000 ... 0x00EF:
this->_map->Page0.write(addr, data);
this->_map.Page0.write(addr, data);
break;
case 0xF0:
this->_registers.unknown = data;
@@ -121,13 +113,13 @@ namespace ComSquare::APU
this->_registers.timer2 = data;
break;
case 0x0100 ... 0x01FF:
this->_map->Page1.write(addr - 0x0100, data);
this->_map.Page1.write(addr - 0x0100, data);
break;
case 0x0200 ... 0xFFBF:
this->_map->Memory.write(addr - 0x200, data);
this->_map.Memory.write(addr - 0x200, data);
break;
case 0xFFC0 ... 0xFFFF:
this->_map->IPL.write(addr - 0xFFC0, data);
this->_map.IPL.write(addr - 0xFFC0, data);
break;
default:
throw InvalidAddress("APU Registers write", addr);
@@ -278,7 +270,7 @@ namespace ComSquare::APU
return this->SET1(this->_getDirectAddr(), 1);
case 0x23: {
auto ope1 = this->_getDirectAddr();
auto ope2 = this->_getImmediateData();
auto ope2 = this->_getImmediateData();
return this->BBS(ope1, ope2, 1);
}
case 0x24:
@@ -751,11 +743,11 @@ namespace ComSquare::APU
case 0xE8:
return this->MOV(this->_internalRegisters.a, this->_getImmediateData(), 2);
case 0xE9:
return this->MOV(this->_internalRegisters.x, this->_getAbsoluteAddr(),4);
return this->MOV(this->_internalRegisters.x, this->_getAbsoluteAddr(), 4);
case 0xEA:
return this->NOT1(this->_getAbsoluteBit());
case 0xEB:
return this->MOV(this->_internalRegisters.y, this->_getDirectAddr(),3);
return this->MOV(this->_internalRegisters.y, this->_getDirectAddr(), 3);
case 0xEC:
return this->MOV(this->_internalRegisters.y, this->_getAbsoluteAddr(), 4);
case 0xED:
@@ -806,8 +798,10 @@ namespace ComSquare::APU
void APU::update(unsigned cycles)
{
if (this->isDisabled)
return;
unsigned total = 0;
int32_t samples = 0;
if (this->_paddingCycles > cycles) {
this->_paddingCycles -= cycles;
@@ -820,80 +814,77 @@ namespace ComSquare::APU
this->_paddingCycles = total - cycles;
this->_dsp.update();
samples = this->_dsp.getSamplesCount();
if (samples > 0)
this->_renderer.playAudio(std::span{this->_soundBuffer}, samples / 2);
}
void APU::loadFromSPC(const std::shared_ptr<Cartridge::Cartridge>& cartridge)
void APU::loadFromSPC(Cartridge::Cartridge &cartridge)
{
const uint8_t *data = cartridge->getData();
uint24_t size = cartridge->getSize();
std::span<const uint8_t> data = cartridge.getData();
uint24_t size = cartridge.getSize();
if (size < 0x101C0)
throw InvalidAddress("Cartridge is not the right size", size);
std::string song = std::string(reinterpret_cast<const char *>(data + 0x2E), 0x20);
std::string game = std::string(reinterpret_cast<const char *>(data + 0x4E), 0x20);
std::string dumper = std::string(reinterpret_cast<const char *>(data + 0x6E), 0x10);
std::string comment = std::string(reinterpret_cast<const char *>(data + 0x7E), 0x20);
std::string date = std::string(reinterpret_cast<const char *>(data + 0x9E), 0x0B);
std::string artist = std::string(reinterpret_cast<const char *>(data + 0xB1), 0x20);
std::string song = std::string(reinterpret_cast<const char *>(data.data() + 0x2E), 0x20);
std::string game = std::string(reinterpret_cast<const char *>(data.data() + 0x4E), 0x20);
std::string dumper = std::string(reinterpret_cast<const char *>(data.data() + 0x6E), 0x10);
std::string comment = std::string(reinterpret_cast<const char *>(data.data() + 0x7E), 0x20);
std::string date = std::string(reinterpret_cast<const char *>(data.data() + 0x9E), 0x0B);
std::string artist = std::string(reinterpret_cast<const char *>(data.data() + 0xB1), 0x20);
this->_internalRegisters.pcl = cartridge->read(0x25);
this->_internalRegisters.pch = cartridge->read(0x26);
this->_internalRegisters.a = cartridge->read(0x27);
this->_internalRegisters.x = cartridge->read(0x28);
this->_internalRegisters.y = cartridge->read(0x29);
this->_internalRegisters.psw = cartridge->read(0x2A);
this->_internalRegisters.sp = cartridge->read(0x2B);
this->_internalRegisters.pcl = cartridge.read(0x25);
this->_internalRegisters.pch = cartridge.read(0x26);
this->_internalRegisters.a = cartridge.read(0x27);
this->_internalRegisters.x = cartridge.read(0x28);
this->_internalRegisters.y = cartridge.read(0x29);
this->_internalRegisters.psw = cartridge.read(0x2A);
this->_internalRegisters.sp = cartridge.read(0x2B);
std::memcpy(this->_map->Page0.getData(), data + 0x100, this->_map->Page0.getSize());
std::memcpy(this->_map->Page1.getData(), data + 0x200, this->_map->Page1.getSize());
std::memcpy(this->_map->Memory.getData(), data + 0x300, this->_map->Memory.getSize());
std::copy_n(data.begin() + 0x100, this->_map.Page0.getSize(), this->_map.Page0.getData().begin());
std::copy_n(data.begin() + 0x200, this->_map.Page1.getSize(), this->_map.Page1.getData().begin());
std::copy_n(data.begin() + 0x300, this->_map.Memory.getSize(), this->_map.Memory.getData().begin());
this->_registers.unknown = cartridge->read(0x100 + 0xF0);
this->_registers.ctrlreg = cartridge->read(0x100 + 0xF1);
this->_registers.dspregAddr = cartridge->read(0x100 + 0xF2);
this->_dsp.write(this->_registers.dspregAddr, cartridge->read(0x100 + 0xF3));
this->_registers.port0 = cartridge->read(0x100 + 0xF4);
this->_registers.port1 = cartridge->read(0x100 + 0xF5);
this->_registers.port2 = cartridge->read(0x100 + 0xF6);
this->_registers.port3 = cartridge->read(0x100 + 0xF7);
this->_registers.regmem1 = cartridge->read(0x100 + 0xF8);
this->_registers.regmem2 = cartridge->read(0x100 + 0xF9);
this->_registers.timer0 = cartridge->read(0x100 + 0xFA);
this->_registers.timer1 = cartridge->read(0x100 + 0xFB);
this->_registers.timer2 = cartridge->read(0x100 + 0xFC);
this->_registers.counter0 = cartridge->read(0x100 + 0xFD);
this->_registers.counter1 = cartridge->read(0x100 + 0xFE);
this->_registers.counter2 = cartridge->read(0x100 + 0xFF);
this->_registers.unknown = cartridge.read(0x100 + 0xF0);
this->_registers.ctrlreg = cartridge.read(0x100 + 0xF1);
this->_registers.dspregAddr = cartridge.read(0x100 + 0xF2);
this->_dsp.write(this->_registers.dspregAddr, cartridge.read(0x100 + 0xF3));
this->_registers.port0 = cartridge.read(0x100 + 0xF4);
this->_registers.port1 = cartridge.read(0x100 + 0xF5);
this->_registers.port2 = cartridge.read(0x100 + 0xF6);
this->_registers.port3 = cartridge.read(0x100 + 0xF7);
this->_registers.regmem1 = cartridge.read(0x100 + 0xF8);
this->_registers.regmem2 = cartridge.read(0x100 + 0xF9);
this->_registers.timer0 = cartridge.read(0x100 + 0xFA);
this->_registers.timer1 = cartridge.read(0x100 + 0xFB);
this->_registers.timer2 = cartridge.read(0x100 + 0xFC);
this->_registers.counter0 = cartridge.read(0x100 + 0xFD);
this->_registers.counter1 = cartridge.read(0x100 + 0xFE);
this->_registers.counter2 = cartridge.read(0x100 + 0xFF);
for (int i = 0x00; i < 0x80; i += 0x10)
this->_dsp.write(i, cartridge->read(0x10100 + i));
this->_dsp.write(i, cartridge.read(0x10100 + i));
for (int i = 0x01; i < 0x80; i += 0x10)
this->_dsp.write(i, cartridge->read(0x10100 + i));
this->_dsp.write(i, cartridge.read(0x10100 + i));
for (int i = 0x02; i < 0x80; i += 0x10)
this->_dsp.write(i, cartridge->read(0x10100 + i));
this->_dsp.write(i, cartridge.read(0x10100 + i));
for (int i = 0x03; i < 0x80; i += 0x10)
this->_dsp.write(i, cartridge->read(0x10100 + i));
this->_dsp.write(i, cartridge.read(0x10100 + i));
for (int i = 0x04; i < 0x80; i += 0x10)
this->_dsp.write(i, cartridge->read(0x10100 + i));
this->_dsp.write(i, cartridge.read(0x10100 + i));
for (int i = 0x05; i < 0x80; i += 0x10)
this->_dsp.write(i, cartridge->read(0x10100 + i));
this->_dsp.write(i, cartridge.read(0x10100 + i));
for (int i = 0x06; i < 0x80; i += 0x10)
this->_dsp.write(i, cartridge->read(0x10100 + i));
this->_dsp.write(i, cartridge.read(0x10100 + i));
for (int i = 0x07; i < 0x80; i += 0x10)
this->_dsp.write(i, cartridge->read(0x10100 + i));
this->_dsp.write(i, cartridge.read(0x10100 + i));
for (int i = 0x08; i < 0x80; i += 0x10)
this->_dsp.write(i, cartridge->read(0x10100 + i));
this->_dsp.write(i, cartridge.read(0x10100 + i));
for (int i = 0x09; i < 0x80; i += 0x10)
this->_dsp.write(i, cartridge->read(0x10100 + i));
this->_dsp.write(i, cartridge.read(0x10100 + i));
for (int i = 0x0C; i < 0x80; i += 0x10)
this->_dsp.write(i, cartridge->read(0x10100 + i));
this->_dsp.write(i, cartridge.read(0x10100 + i));
for (int i = 0x0D; i < 0x80; i += 0x10)
this->_dsp.write(i, cartridge->read(0x10100 + i));
this->_dsp.write(i, cartridge.read(0x10100 + i));
for (int i = 0x0F; i < 0x80; i += 0x10)
this->_dsp.write(i, cartridge->read(0x10100 + i));
this->_dsp.write(i, cartridge.read(0x10100 + i));
}
void APU::_setNZflags(uint8_t value)
@@ -902,10 +893,10 @@ namespace ComSquare::APU
this->_internalRegisters.z = !value;
}
MemoryMap::MemoryMap() :
Page0(0x00F0, Apu, "APU's Page 0"),
Page1(0x0100, Apu, "APU's Page 1"),
Memory(0xFDC0, Apu, "APU's Ram"),
IPL(Apu, "IPL Rom")
{ }
}
MemoryMap::MemoryMap()
: Page0(0x00F0, Apu, "APU's Page 0"),
Page1(0x0100, Apu, "APU's Page 1"),
Memory(0xFDC0, Apu, "APU's Ram"),
IPL(Apu, "IPL Rom")
{}
}// namespace ComSquare::APU

View File

@@ -2,16 +2,19 @@
// Created by Melefo on 24/01/2020.
//
#ifndef COMSQUARE_APU_HPP
#define COMSQUARE_APU_HPP
#pragma once
#include <memory>
#include "DSP/DSP.hpp"
#include "../Memory/AMemory.hpp"
#include "../Ram/Ram.hpp"
#include "Memory/AMemory.hpp"
#include "Ram/Ram.hpp"
#include "IPL/IPL.hpp"
#include "../Renderer/IRenderer.hpp"
#include "../Cartridge/Cartridge.hpp"
#include "Renderer/IRenderer.hpp"
#include "Cartridge/Cartridge.hpp"
#ifdef DEBUGGER_ENABLED
#include "Debugger/APUDebug.hpp"
#endif
namespace ComSquare::APU
{
@@ -133,19 +136,14 @@ namespace ComSquare::APU
};
class APU : public Memory::AMemory {
protected:
private:
//! @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 Renderer used to play sounds
Renderer::IRenderer &_renderer;
//! @brief Internal APU memory separated according to their utility
std::shared_ptr<MemoryMap> _map;
//! @brief Buffer containing samples to be played
std::array<int16_t, 0x10000> _soundBuffer;
MemoryMap _map;
//! @brief The DSP component used to produce sound
DSP::DSP _dsp;
@@ -154,7 +152,7 @@ namespace ComSquare::APU
//! @param addr The address to read from. The address 0x0000 should refer to the first byte of the register.
//! @throw InvalidAddress will be thrown if the address is more than $FFFF (the number of register).
//! @return Return the data.
uint8_t _internalRead(uint24_t addr) const;
[[nodiscard]] uint8_t _internalRead(uint24_t addr) const;
//! @brief Write data to the APU ram.
//! @param addr The address to write to. The address 0x0000 should refer to the first byte of register.
@@ -200,7 +198,7 @@ namespace ComSquare::APU
//! @brief Execute a single instruction.
//! @return The number of cycles that the instruction took.
virtual int _executeInstruction();
int _executeInstruction();
//! @brief No Operation instruction, do nothing than delay
int NOP();
@@ -369,9 +367,12 @@ namespace ComSquare::APU
public:
explicit APU(Renderer::IRenderer &renderer);
APU(const APU &) = default;
APU &operator=(const APU &) = default;
APU &operator=(const APU &) = delete;
~APU() override = default;
//! @brief Is this APU disabled?
bool isDisabled = false;
//! @brief Read from the APU ram.
//! @param addr The address to read from. The address 0x0000 should refer to the first byte of the register.
//! @throw InvalidAddress will be thrown if the address is more than $FFFF (the number of register).
@@ -385,28 +386,31 @@ namespace ComSquare::APU
void write(uint24_t addr, uint8_t data) override;
//! @brief Get the name of this accessor (used for debug purpose)
std::string getName() const override;
[[nodiscard]] std::string getName() const override;
//! @brief Get the component of this accessor (used for debug purpose)
Component getComponent() const override;
[[nodiscard]] Component getComponent() const override;
//! @brief Get the name of the data at the address
//! @param addr The address (in local space)
[[nodiscard]] std::string getValueName(uint24_t addr) const override;
//! @brief Get the size of the data. This size can be lower than the mapped data.
//! @return The number of bytes inside this memory.
uint24_t getSize() const override;
[[nodiscard]] uint24_t getSize() const override;
//! @brief Parses rom data to uploads directly into RAM and corresponding registers
void loadFromSPC(const std::shared_ptr<Cartridge::Cartridge>& cartridge);
void loadFromSPC(Cartridge::Cartridge &cartridge);
//! @brief This function execute the instructions received until the maximum number of cycles is reached.
//! @return The number of cycles that elapsed.
virtual void update(unsigned cycles);
void update(unsigned cycles);
//! @brief This function is executed when the SNES is powered on or the reset button is pushed.
void reset();
//! @brief Return true if the CPU is overloaded with debugging features.
virtual bool isDebugger() const;
#ifdef DEBUGGER_ENABLED
friend Debugger::APU::APUDebug;
#endif
};
}
#endif //COMSQUARE_APU_HPP

View File

@@ -8,12 +8,12 @@
namespace ComSquare::APU::DSP
{
DSP::DSP(std::array<int16_t, 0x10000> &buffer, uint32_t size, std::weak_ptr<MemoryMap> map) :
_state(buffer), _map(map)
{
this->_state.buffer = buffer;
this->_state.bufferSize = size;
}
DSP::DSP(Renderer::IRenderer &renderer,
MemoryMap &map)
: _state(this->_soundBuffer, this->_soundBuffer.size() / 2),
_map(map),
_renderer(renderer)
{ }
uint8_t DSP::read(uint24_t addr) const
{
@@ -570,17 +570,15 @@ namespace ComSquare::APU::DSP
}
uint8_t DSP::_readRAM(uint24_t addr)
{
if (!this->_map.lock())
throw std::runtime_error("DSP read : MemoryMap inaccessible");
switch (addr) {
case 0x0000 ... 0x00EF:
return this->_map.lock()->Page0.read(addr);
return this->_map.Page0.read(addr);
case 0x0100 ... 0x01FF:
return this->_map.lock()->Page1.read(addr - 0x0100);
return this->_map.Page1.read(addr - 0x0100);
case 0x0200 ... 0xFFBF:
return this->_map.lock()->Memory.read(addr - 0x200);
return this->_map.Memory.read(addr - 0x200);
case 0xFFC0 ... 0xFFFF:
return this->_map.lock()->IPL.read(addr - 0xFFC0);
return this->_map.IPL.read(addr - 0xFFC0);
default:
throw InvalidAddress("DSP read", addr);
}
@@ -588,20 +586,18 @@ namespace ComSquare::APU::DSP
void DSP::_writeRAM(uint24_t addr, uint8_t data)
{
if (!this->_map.lock())
throw std::runtime_error("DSP write : MemoryMap inaccessible");
switch (addr) {
case 0x0000 ... 0x00EF:
this->_map.lock()->Page0.write(addr, data);
this->_map.Page0.write(addr, data);
break;
case 0x0100 ... 0x01FF:
this->_map.lock()->Page1.write(addr - 0x0100, data);
this->_map.Page1.write(addr - 0x0100, data);
break;
case 0x0200 ... 0xFFBF:
this->_map.lock()->Memory.write(addr - 0x200, data);
this->_map.Memory.write(addr - 0x200, data);
break;
case 0xFFC0 ... 0xFFFF:
this->_map.lock()->IPL.write(addr - 0xFFC0, data);
this->_map.IPL.write(addr - 0xFFC0, data);
break;
default:
throw InvalidAddress("DSP write", addr);
@@ -764,7 +760,11 @@ namespace ComSquare::APU::DSP
break;
}
this->_state.voice = (this->_state.voice + 1) % 32;
int32_t samples = this->getSamplesCount();
if (samples > 0)
this->_renderer.playAudio(std::span(this->_soundBuffer.begin(), samples / 2));
}
uint24_t DSP::getSize() const
{
return 0x7F;

View File

@@ -2,12 +2,12 @@
// Created by Melefo on 28/01/2020.
//
#ifndef COMSQUARE_DSP_HPP
#define COMSQUARE_DSP_HPP
#pragma once
#include <cstdint>
#include <array>
#include "../../Memory/AMemory.hpp"
#include "Renderer/IRenderer.hpp"
#include "Memory/AMemory.hpp"
namespace ComSquare::APU
{
@@ -191,7 +191,9 @@ namespace ComSquare::APU::DSP
//! @brief Current state of the DSP
struct State
{
State(std::array<int16_t, 0x10000> &array) : buffer(array) {};
State(std::array<int16_t, 0x10000> &array, uint32_t size)
: buffer(array), bufferSize(size)
{};
//! @brief Current voice modification to do
uint8_t voice = 0;
@@ -324,31 +326,36 @@ namespace ComSquare::APU::DSP
void decodeBRR(Voice &voice);
//! @brief Whole APU RAM map
std::weak_ptr<MemoryMap> _map;
MemoryMap &_map;
//! @brief Renderer used to play sounds
Renderer::IRenderer &_renderer;
//! @brief Buffer containing samples to be played
std::array<int16_t, 0x10000> _soundBuffer = {};
//! @brief Read inside APU RAM
uint8_t _readRAM(uint24_t addr);
//! @brief Write into APU RAM
void _writeRAM(uint24_t addr, uint8_t data);
public:
DSP(std::array<int16_t, 0x10000> &buffer, uint32_t size, std::weak_ptr<MemoryMap> map);
DSP(Renderer::IRenderer &renderer, MemoryMap &map);
DSP(const DSP &) = default;
DSP &operator=(const DSP &) = default;
DSP &operator=(const DSP &) = delete;
~DSP() = default;
//! @brief Return all 8 voices from DSP
const std::array<Voice, 8> &getVoices() const;
const Master &getMaster() const;
const Echo &getEcho() const;
const Noise &getNoise() const;
const BRR &getBrr() const;
const Latch &getLatch() const;
[[nodiscard]] const std::array<Voice, 8> &getVoices() const;
[[nodiscard]] const Master &getMaster() const;
[[nodiscard]] const Echo &getEcho() const;
[[nodiscard]] const Noise &getNoise() const;
[[nodiscard]] const BRR &getBrr() const;
[[nodiscard]] const Latch &getLatch() const;
//! @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).
//! @return Return the value of the register.
uint8_t read(uint24_t addr) const;
[[nodiscard]] uint8_t read(uint24_t addr) const;
//! @brief Write data to the internal DSP register.
//! @param addr The address to write to. The address 0x0 should refer to the first byte of register.
//! @param data The new value of the register.
@@ -356,19 +363,17 @@ namespace ComSquare::APU::DSP
void write(uint24_t addr, uint8_t data);
//! @brief Get the name of this accessor (used for debug purpose)
std::string getName() const;
[[nodiscard]] std::string getName() const;
//! @brief Execute current voice transformation
void update();
//! @brief Get the component of this accessor (used for debug purpose)
Component getComponent() const;
[[nodiscard]] Component getComponent() const;
//! @brief Get the size of the data. This size can be lower than the mapped data.
//! @return The number of bytes inside this memory.
uint24_t getSize() const;
[[nodiscard]] uint24_t getSize() const;
//! @brief Return the number of samples written
int32_t getSamplesCount() const;
[[nodiscard]] int32_t getSamplesCount() const;
};
}
#endif //COMSQUARE_DSP_HPP
}

View File

@@ -4,18 +4,15 @@
#include "IPL.hpp"
#include <utility>
#include "../../Exceptions/InvalidAddress.hpp"
#include <utility>
namespace ComSquare::APU::IPL
{
IPL::IPL(Component type, std::string iplName)
: _iplType(type),
_iplName(std::move(iplName))
{ }
IPL::~IPL()
{ }
: _iplType(type),
_iplName(std::move(iplName))
{}
uint8_t IPL::read(uint24_t addr)
{
@@ -45,4 +42,20 @@ namespace ComSquare::APU::IPL
{
return this->_iplType;
}
}
std::string IPL::getValueName(uint24_t) const
{
// TODO implement this
return "???";
}
uint8_t &IPL::operator[](uint24_t addr)
{
return this->_data[addr];
}
const uint8_t &IPL::operator[](uint24_t addr) const
{
return this->_data[addr];
}
}// namespace ComSquare::APU::IPL

View File

@@ -2,10 +2,9 @@
// Created by Melefo on 27/02/2020.
//
#ifndef COMSQUARE_IPL_HPP
#define COMSQUARE_IPL_HPP
#pragma once
#include "../../Memory/AMemory.hpp"
#include "Memory/AMemory.hpp"
namespace ComSquare::APU::IPL
{
@@ -31,15 +30,12 @@ namespace ComSquare::APU::IPL
public:
//! @brief Create the rom with its value.
explicit IPL(Component, std::string iplName);
//! @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 A default destructor
~IPL() override = default;
//! @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.
@@ -53,16 +49,27 @@ namespace ComSquare::APU::IPL
//! @throw InvalidAddress if the address is not mapped to the component.
void write(uint24_t addr, uint8_t data) override;
//! @brief Retrieve the data at the address given. This can be used instead of read or write.
//! @param addr The address of the data to retrieve.
//! @return The data at the address given as parameter.
uint8_t &operator[](uint24_t addr);
//! @brief Retrieve the data at the address given. This can be used instead of read or write.
//! @param addr The address of the data to retrieve.
//! @return The data at the address given as parameter.
const uint8_t &operator[](uint24_t addr) const;
//! @brief Get the size of the data. This size can be lower than the mapped data.
//! @return The number of bytes inside this memory.
uint24_t getSize() const override;
[[nodiscard]] uint24_t getSize() const override;
//! @brief Get the name of this accessor (used for debug purpose)
std::string getName() const override;
[[nodiscard]] std::string getName() const override;
//! @brief Get the component of this accessor (used for debug purpose)
Component getComponent() const override;
};
}
[[nodiscard]] Component getComponent() const override;
#endif //COMSQUARE_IPL_HPP
//! @brief Get the name of the data at the address
//! @param addr The address (in local space)
[[nodiscard]] std::string getValueName(uint24_t addr) const override;
};
}

View File

@@ -2,7 +2,7 @@
// Created by Melefo on 25/02/2020.
//
#include "../APU.hpp"
#include "APU/APU.hpp"
namespace ComSquare::APU
{
@@ -31,4 +31,9 @@ namespace ComSquare::APU
this->_setNZflags(this->_internalRegisters.a);
return 3;
}
std::string APU::getValueName(uint24_t) const
{
// TODO implement this
return "???";
}
}

View File

@@ -2,7 +2,7 @@
// Created by Melefo on 24/02/2020.
//
#include "../Models/Int24.hpp"
#include "../Models/Ints.hpp"
#include "APU.hpp"
namespace ComSquare::APU

View File

@@ -2,7 +2,7 @@
// Created by anonymus-raccoon on 3/20/20.
//
#include "../Models/Int24.hpp"
#include "../Models/Ints.hpp"
#include "CPU.hpp"
namespace ComSquare::CPU
@@ -41,31 +41,31 @@ namespace ComSquare::CPU
uint24_t CPU::_getDirectAddr()
{
uint8_t addr = this->readPC();
uint8_t addr = this->_readPC();
return this->_registers.d + addr;
}
uint24_t CPU::_getAbsoluteAddr()
{
uint24_t addr = this->_registers.dbr << 16u;
addr += this->readPC();
addr += this->readPC() << 8u;
addr += this->_readPC();
addr += this->_readPC() << 8u;
return addr;
}
uint24_t CPU::_getAbsoluteLongAddr()
{
uint24_t addr = this->readPC();
addr += this->readPC() << 8u;
addr += this->readPC() << 16u;
uint24_t addr = this->_readPC();
addr += this->_readPC() << 8u;
addr += this->_readPC() << 16u;
return addr;
}
uint24_t CPU::_getDirectIndirectIndexedYAddr()
{
uint16_t dp = this->readPC() + this->_registers.d;
uint24_t base = this->_bus->read(dp);
base += this->_bus->read(dp + 1) << 8u;
uint16_t dp = this->_readPC() + this->_registers.d;
uint24_t base = this->getBus().read(dp);
base += this->getBus().read(dp + 1) << 8u;
base += this->_registers.dbr << 16u;
if ((base & 0x80000000u) == (((base + this->_registers.y) & 0x80000000u)))
this->_hasIndexCrossedPageBoundary = true;
@@ -74,41 +74,41 @@ namespace ComSquare::CPU
uint24_t CPU::_getDirectIndirectIndexedYLongAddr()
{
uint16_t dp = this->readPC() + this->_registers.d;
uint24_t base = this->_bus->read(dp);
base += this->_bus->read(dp + 1) << 8u;
base += this->_bus->read(dp + 2) << 16u;
uint16_t dp = this->_readPC() + this->_registers.d;
uint24_t base = this->getBus().read(dp);
base += this->getBus().read(dp + 1) << 8u;
base += this->getBus().read(dp + 2) << 16u;
return base;
}
uint24_t CPU::_getDirectIndirectIndexedXAddr()
{
uint16_t dp = this->readPC() + this->_registers.d;
uint16_t dp = this->_readPC() + this->_registers.d;
dp += this->_registers.x;
uint24_t base = this->_bus->read(dp);
base += this->_bus->read(dp + 1) << 8u;
uint24_t base = this->getBus().read(dp);
base += this->getBus().read(dp + 1) << 8u;
base += this->_registers.dbr << 16u;
return base;
}
uint24_t CPU::_getDirectIndexedByXAddr()
{
uint16_t dp = this->readPC() + this->_registers.d;
uint16_t dp = this->_readPC() + this->_registers.d;
dp += this->_registers.x;
return dp;
}
uint24_t CPU::_getDirectIndexedByYAddr()
{
uint16_t dp = this->readPC() + this->_registers.d;
uint16_t dp = this->_readPC() + this->_registers.d;
dp += this->_registers.y;
return dp;
}
uint24_t CPU::_getAbsoluteIndexedByXAddr()
{
uint16_t abs = this->readPC();
abs += this->readPC() << 8u;
uint16_t abs = this->_readPC();
abs += this->_readPC() << 8u;
uint24_t effective = abs + (this->_registers.dbr << 16u);
if ((effective & 0x80000000u) == (((effective + this->_registers.x) & 0x80000000u)))
this->_hasIndexCrossedPageBoundary = true;
@@ -117,8 +117,8 @@ namespace ComSquare::CPU
uint24_t CPU::_getAbsoluteIndexedByYAddr()
{
uint16_t abs = this->readPC();
abs += this->readPC() << 8u;
uint16_t abs = this->_readPC();
abs += this->_readPC() << 8u;
uint24_t effective = abs + (this->_registers.dbr << 16u);
if ((effective & 0x80000000u) == (((effective + this->_registers.y) & 0x80000000u)))
this->_hasIndexCrossedPageBoundary = true;
@@ -127,67 +127,67 @@ namespace ComSquare::CPU
uint24_t CPU::_getAbsoluteIndexedByXLongAddr()
{
uint24_t lng = this->readPC();
lng += this->readPC() << 8u;
lng += this->readPC() << 16u;
uint24_t lng = this->_readPC();
lng += this->_readPC() << 8u;
lng += this->_readPC() << 16u;
return lng + this->_registers.x;
}
uint24_t CPU::_getAbsoluteIndirectAddr()
{
uint16_t abs = this->readPC();
abs += this->readPC() << 8u;
uint24_t effective = this->_bus->read(abs);
effective += this->_bus->read(abs + 1) << 8u;
uint16_t abs = this->_readPC();
abs += this->_readPC() << 8u;
uint24_t effective = this->getBus().read(abs);
effective += this->getBus().read(abs + 1) << 8u;
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;
uint16_t abs = this->_readPC();
abs += this->_readPC() << 8u;
uint24_t effective = this->getBus().read(abs);
effective += this->getBus().read(abs + 1) << 8u;
effective += this->getBus().read(abs + 2) << 16u;
return effective;
}
uint24_t CPU::_getAbsoluteIndirectIndexedByXAddr()
{
uint24_t abs = this->readPC();
abs += this->readPC() << 8u;
uint24_t abs = this->_readPC();
abs += this->_readPC() << 8u;
abs += this->_registers.x;
uint24_t effective = this->_bus->read(abs);
effective += this->_bus->read(abs + 1) << 8u;
uint24_t effective = this->getBus().read(abs);
effective += this->getBus().read(abs + 1) << 8u;
return effective;
}
uint24_t CPU::_getDirectIndirectAddr()
{
uint16_t dp = this->readPC() + this->_registers.d;
uint24_t effective = this->_bus->read(dp);
effective += this->_bus->read(dp + 1) << 8u;
uint16_t dp = this->_readPC() + this->_registers.d;
uint24_t effective = this->getBus().read(dp);
effective += this->getBus().read(dp + 1) << 8u;
effective += this->_registers.dbr << 16u;
return effective;
}
uint24_t CPU::_getDirectIndirectLongAddr()
{
uint16_t dp = this->readPC() + this->_registers.d;
uint24_t effective = this->_bus->read(dp);
effective += this->_bus->read(++dp) << 8u;
effective += this->_bus->read(++dp) << 16u;
uint16_t dp = this->_readPC() + this->_registers.d;
uint24_t effective = this->getBus().read(dp);
effective += this->getBus().read(++dp) << 8u;
effective += this->getBus().read(++dp) << 16u;
return effective;
}
uint24_t CPU::_getStackRelativeAddr()
{
return this->readPC() + this->_registers.s;
return this->_readPC() + this->_registers.s;
}
uint24_t CPU::_getStackRelativeIndirectIndexedYAddr()
{
uint24_t base = this->readPC() + this->_registers.s;
uint24_t base = this->_readPC() + this->_registers.s;
base += this->_registers.dbr << 16u;
return base + this->_registers.y;
}

View File

@@ -3,31 +3,26 @@
//
#include "CPU.hpp"
#include <utility>
#include "Exceptions/InvalidAddress.hpp"
#include "Exceptions/InvalidOpcode.hpp"
#include "Utility/Utility.hpp"
#include <iostream>
#include "../Exceptions/InvalidAddress.hpp"
#include "../Exceptions/InvalidOpcode.hpp"
namespace ComSquare::CPU
{
CPU::CPU(std::shared_ptr<Memory::MemoryBus> bus, Cartridge::Header &cartridgeHeader)
: _bus(std::move(bus)),
_cartridgeHeader(cartridgeHeader)
CPU::CPU(Memory::IMemoryBus &bus, Cartridge::Header &cartridgeHeader)
: _bus(bus),
_cartridgeHeader(cartridgeHeader),
_dmaChannels({DMA(bus), DMA(bus), DMA(bus), DMA(bus), DMA(bus), DMA(bus), DMA(bus), DMA(bus)})
{
this->RESB();
for (DMA &channel : this->_dmaChannels)
channel.setBus(_bus);
}
bool CPU::isDebugger() const
void CPU::setBus(Memory::IMemoryBus &bus)
{
return false;
}
void CPU::setMemoryBus(std::shared_ptr<Memory::MemoryBus> bus)
{
this->_bus = std::move(bus);
this->_bus = bus;
for (auto &dma : this->_dmaChannels)
dma.setBus(bus);
}
//! @bref The CPU's internal registers starts at $4200 and finish at $421F.
@@ -212,24 +207,13 @@ namespace ComSquare::CPU
return 0x180;
}
uint8_t CPU::readPC()
unsigned CPU::update(unsigned maxCycles)
{
uint8_t ret = this->_bus->read(this->_registers.pac);
this->_registers.pc++;
return ret;
}
if (this->isDisabled)
return 0xFF;
unsigned cycles = this->runDMA(maxCycles);
unsigned CPU::update()
{
unsigned cycles = 0;
const unsigned maxCycles = 0x0C;
for (DMA &channel : this->_dmaChannels) {
if (!channel.enabled)
continue;
cycles += channel.run(maxCycles - cycles);
}
for (unsigned i = 0; i < maxCycles; i++) {
while (cycles < maxCycles) {
if (this->_isStopped) {
cycles += 1;
continue;
@@ -238,7 +222,9 @@ namespace ComSquare::CPU
this->_checkInterrupts();
if (!this->_isWaitingForInterrupt)
cycles += this->_executeInstruction(this->readPC());
cycles += this->executeInstruction();
else
return 0xFF;
}
return cycles;
}
@@ -251,19 +237,31 @@ namespace ComSquare::CPU
if (this->IsNMIRequested) {
this->_runInterrupt(
this->_cartridgeHeader.nativeInterrupts.nmi,
this->_cartridgeHeader.emulationInterrupts.nmi);
this->_cartridgeHeader.nativeInterrupts.nmi,
this->_cartridgeHeader.emulationInterrupts.nmi);
return;
}
if (this->IsIRQRequested && !this->_registers.p.i) {
this->_runInterrupt(
this->_cartridgeHeader.nativeInterrupts.irq,
this->_cartridgeHeader.emulationInterrupts.irq);
this->_cartridgeHeader.nativeInterrupts.irq,
this->_cartridgeHeader.emulationInterrupts.irq);
return;
}
}
uint24_t CPU::_getValueAddr(Instruction &instruction)
unsigned CPU::runDMA(unsigned maxCycles)
{
unsigned cycles = 0;
for (DMA &channel : this->_dmaChannels) {
if (!channel.enabled)
continue;
cycles += channel.run(maxCycles - cycles);
}
return cycles;
}
uint24_t CPU::_getValueAddr(const Instruction &instruction)
{
switch (instruction.addressingMode) {
case Implied:
@@ -322,9 +320,9 @@ namespace ComSquare::CPU
throw InvalidOpcode("Unknown addressing mode for.");
}
unsigned CPU::_executeInstruction(uint8_t opcode)
unsigned CPU::executeInstruction()
{
Instruction instruction = this->_instructions[opcode];
const Instruction &instruction = this->instructions[this->_readPC()];
this->_hasIndexCrossedPageBoundary = false;
uint24_t valueAddr = this->_getValueAddr(instruction);
@@ -333,24 +331,24 @@ namespace ComSquare::CPU
void CPU::_push(uint8_t data)
{
this->_bus->write(this->_registers.s--, data);
this->getBus().write(this->_registers.s--, data);
}
void CPU::_push(uint16_t data)
{
this->_bus->write(this->_registers.s--, data >> 8u);
this->_bus->write(this->_registers.s--, data);
this->getBus().write(this->_registers.s--, data >> 8u);
this->getBus().write(this->_registers.s--, data);
}
uint8_t CPU::_pop()
{
return this->_bus->read(++this->_registers.s);
return this->getBus().read(++this->_registers.s);
}
uint16_t CPU::_pop16()
{
uint16_t value = this->_bus->read(++this->_registers.s);
value +=this->_bus->read(++this->_registers.s) << 8u;
uint16_t value = this->getBus().read(++this->_registers.s);
value += this->getBus().read(++this->_registers.s) << 8u;
return value;
}
@@ -363,4 +361,4 @@ namespace ComSquare::CPU
{
return Cpu;
}
}
}// namespace ComSquare::CPU

View File

@@ -2,190 +2,34 @@
// Created by anonymus-raccoon on 1/24/20.
//
#ifndef COMSQUARE_CPU_HPP
#define COMSQUARE_CPU_HPP
#pragma once
#include "../Memory/AMemory.hpp"
#include "../Memory/MemoryBus.hpp"
#include "../Models/Int24.hpp"
#include "../Cartridge/Cartridge.hpp"
#include "../Memory/AMemory.hpp"
#include "Memory/AMemory.hpp"
#include "Memory/MemoryBus.hpp"
#include "Models/Ints.hpp"
#include "Models/Callback.hpp"
#include "Cartridge/Cartridge.hpp"
#include "Memory/AMemory.hpp"
#include "Instruction.hpp"
#include "DMA/DMA.hpp"
#include "CPU/Registers.hpp"
#ifdef DEBUGGER_ENABLED
#include "Debugger/CPU/CPUDebug.hpp"
#include "Debugger/RegisterViewer.hpp"
#endif
namespace ComSquare::CPU
{
//! @brief Struct containing registers for the main CPU.
struct Registers {
//! @brief The Accumulator
union {
struct {
uint8_t al;
uint8_t ah;
};
uint16_t a;
};
//! @brief The Data Bank Register;
uint8_t dbr;
//! @brief The Direct Page register;
union {
struct {
uint8_t dl;
uint8_t dh;
};
uint16_t d;
};
union {
struct {
//! @brief The Program Counter;
union {
struct {
uint8_t pcl;
uint8_t pch;
};
uint16_t pc;
};
//! @brief The Program Bank Register;
uint8_t pbr;
};
//! @brief The current Program Address Counter (does not exist in a snes but is useful here).
uint24_t pac;
};
//! @brief The Stack pointer
union {
struct {
uint8_t sl;
uint8_t sh;
};
uint16_t s;
};
//! @brief The X index register
union {
struct {
uint8_t xl;
uint8_t xh;
};
uint16_t x;
};
//! @brief The Y index register
union {
struct {
uint8_t yl;
uint8_t yh;
};
uint16_t y;
};
//! @brief The Processor status register;
union {
struct {
//! @brief The Carry flag
bool c : 1;
//! @brief The Zero flag
bool z : 1;
//! @brief The Interrupt request disable flag
bool i : 1;
//! @brief The Decimal mode flag
bool d : 1;
//! @brief The indeX register width flag (in native mode only) - 0 = 16 bits mode, 1 = 8 bits mode OR the Break flag (in emulation mode only)
bool x_b : 1;
//! @brief The accumulator and Memory width flag (in native mode only) - 0 = 16 bits mode, 1 = 8 bits mode.
bool m : 1;
//! @brief The oVerflow flag
bool v : 1;
//! @brief The Negative flag
bool n : 1;
};
uint8_t flags;
} p;
};
//! @brief Struct containing internal registers of the CPU.
struct InternalRegisters
{
//! @brief Interrupt Enable Register
uint8_t nmitimen;
//! @brief IO Port Write Register
uint8_t wrio;
//! @brief Multiplicand Register A
uint8_t wrmpya;
//! @brief Multiplicand Register B
uint8_t wrmpyb;
//! @brief Divisor & Dividend Registers (A - Low)
uint8_t wrdivl;
//! @brief Divisor & Dividend Registers (A - High)
uint8_t wrdivh;
//! @brief Divisor & Dividend Registers (B)
uint8_t wrdivb;
//! @brief IRQ Timer Registers (Horizontal - Low)
uint8_t htimel;
//! @brief IRQ Timer Registers (Horizontal - High)
uint8_t htimeh;
//! @brief IRQ Timer Registers (Vertical - Low)
uint8_t vtimel;
//! @brief IRQ Timer Registers (Vertical - High)
uint8_t vtimeh;
//! @brief HDMA Enable Register
uint8_t hdmaen;
//! @brief ROM Speed Register
uint8_t memsel;
//! @brief Interrupt Flag Registers
uint8_t rdnmi;
//! @brief Interrupt Flag Registers - TimeUp
uint8_t timeup;
//! @brief PPU Status Register
uint8_t hvbjoy;
//! @brief IO Port Read Register
uint8_t rdio;
//! @brief Divide Result Registers (can sometimes be used as multiplication result register) - LOW
uint8_t rddivl;
//! @brief Divide Result Registers (can sometimes be used as multiplication result register) - HIGH
uint8_t rddivh;
//! @brief Multiplication Result Registers (can sometimes be used as divide result register) - LOW
uint8_t rdmpyl;
//! @brief Multiplication Result Registers (can sometimes be used as divide result register) - HIGH
uint8_t rdmpyh;
//! @brief Controller Port Data Registers (Pad 1 - Low)
uint8_t joy1l;
//! @brief Controller Port Data Registers (Pad 1 - High)
uint8_t joy1h;
//! @brief Controller Port Data Registers (Pad 2 - Low)
uint8_t joy2l;
//! @brief Controller Port Data Registers (Pad 2 - High)
uint8_t joy2h;
//! @brief Controller Port Data Registers (Pad 3 - Low)
uint8_t joy3l;
//! @brief Controller Port Data Registers (Pad 3 - High)
uint8_t joy3h;
//! @brief Controller Port Data Registers (Pad 4 - Low)
uint8_t joy4l;
//! @brief Controller Port Data Registers (Pad 4 - High)
uint8_t joy4h;
};
//! @brief The main CPU
class CPU : public Memory::AMemory {
protected:
class CPU : public Memory::AMemory
{
private:
//! @brief All the registers of the CPU
Registers _registers{};
Registers _registers {};
//! @brief Internal registers of the CPU (accessible from the bus via addr $4200 to $421F).
InternalRegisters _internalRegisters{};
InternalRegisters _internalRegisters {};
//! @brief Is the CPU running in emulation mode (in 8bits)
bool _isEmulationMode = true;
@@ -195,8 +39,8 @@ namespace ComSquare::CPU
bool _isWaitingForInterrupt = false;
//! @brief The memory bus to use for read/write.
std::shared_ptr<Memory::MemoryBus> _bus;
//! @brief The cartridge header (stored for interrupt vectors..
std::reference_wrapper<Memory::IMemoryBus> _bus;
//! @brief The cartridge header (stored for interrupt vectors..)
Cartridge::Header &_cartridgeHeader;
//! @brief DMA channels witch are mapped to the bus.
@@ -250,7 +94,6 @@ namespace ComSquare::CPU
//! @brief The <8-bit exp> is added to S and combined with DBR to form the base address. Y is added to the base address to form the effective address.
uint24_t _getStackRelativeIndirectIndexedYAddr();
//! @brief Push 8 bits of data to the stack.
void _push(uint8_t data);
//! @brief Push 16 bits of data to the stack.
@@ -261,21 +104,22 @@ namespace ComSquare::CPU
uint16_t _pop16();
//! @brief Return the data at the program bank concatenated with the program counter. It also increment the program counter (the program bank is not incremented on overflows).
uint8_t readPC();
inline uint8_t _readPC()
{
uint8_t ret = this->getBus().read(this->_registers.pac);
this->_registers.pc++;
return ret;
}
//! @brief Check if an interrupt is requested and handle it.
void _checkInterrupts();
//! @brief Run an interrupt (save state of the processor and jump to the interrupt handler)
void _runInterrupt(uint24_t nativeHandler, uint24_t emulationHandler);
//! @brief Execute a single instruction.
//! @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);
uint24_t _getValueAddr(const Instruction &instruction);
//! @brief Break instruction - Causes a software break. The PC is loaded from a vector table.
int BRK(uint24_t, AddressingMode);
@@ -468,275 +312,302 @@ namespace ComSquare::CPU
//! @param C_register (16 bits accumulator) Length -1
int MVP(uint24_t, AddressingMode);
public:
//! @brief Get the memory bus used by this CPU.
[[nodiscard]] inline Memory::IMemoryBus &getBus()
{
return this->_bus;
}
//! @brief Set the memory bus used by this CPU
//! @param bus The bus to use.
void setBus(Memory::IMemoryBus &bus);
//! @brief All the instructions of the CPU.
//! @info Instructions are indexed by their opcode
Instruction _instructions[0x100] = {
{&CPU::BRK, 7, "brk", AddressingMode::Immediate8bits, 2}, // 00
{&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::TSB, 5, "tsb", AddressingMode::DirectPage, 2}, // 04
{&CPU::ORA, 3, "ora", AddressingMode::DirectPage, 2}, // 05
{&CPU::ASL, 5, "asl", AddressingMode::DirectPage, 2}, // 06
{&CPU::ORA, 6, "ora", AddressingMode::DirectPageIndirectLong, 2}, // 07
{&CPU::PHP, 3, "php", AddressingMode::Implied, 1}, // 08
{&CPU::ORA, 2, "ora", AddressingMode::ImmediateForA, 2}, // 09
{&CPU::ASL, 2, "asl", AddressingMode::Implied, 1}, // 0A
{&CPU::PHD, 4, "phd", AddressingMode::Implied, 1}, // 0B
{&CPU::TSB, 6, "tsb", AddressingMode::Absolute, 3}, // 0C
{&CPU::ORA, 3, "ora", AddressingMode::Absolute, 4}, // 0D
{&CPU::ASL, 6, "asl", AddressingMode::Absolute, 3}, // 0E
{&CPU::ORA, 5, "ora", AddressingMode::AbsoluteLong, 5}, // 0F
{&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
{&CPU::TRB, 5, "trb", AddressingMode::DirectPage, 2}, // 14
{&CPU::ORA, 4, "ora", AddressingMode::DirectPageIndexedByX, 2}, // 15
{&CPU::ASL, 6, "asl", AddressingMode::DirectPageIndexedByX, 2}, // 16
const Instruction instructions[0x100] = {
{&CPU::BRK, 7, "brk", AddressingMode::Immediate8bits, 2}, // 00
{&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::TSB, 5, "tsb", AddressingMode::DirectPage, 2}, // 04
{&CPU::ORA, 3, "ora", AddressingMode::DirectPage, 2}, // 05
{&CPU::ASL, 5, "asl", AddressingMode::DirectPage, 2}, // 06
{&CPU::ORA, 6, "ora", AddressingMode::DirectPageIndirectLong, 2}, // 07
{&CPU::PHP, 3, "php", AddressingMode::Implied, 1}, // 08
{&CPU::ORA, 2, "ora", AddressingMode::ImmediateForA, 2}, // 09
{&CPU::ASL, 2, "asl", AddressingMode::Implied, 1}, // 0A
{&CPU::PHD, 4, "phd", AddressingMode::Implied, 1}, // 0B
{&CPU::TSB, 6, "tsb", AddressingMode::Absolute, 3}, // 0C
{&CPU::ORA, 3, "ora", AddressingMode::Absolute, 4}, // 0D
{&CPU::ASL, 6, "asl", AddressingMode::Absolute, 3}, // 0E
{&CPU::ORA, 5, "ora", AddressingMode::AbsoluteLong, 5}, // 0F
{&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
{&CPU::TRB, 5, "trb", AddressingMode::DirectPage, 2}, // 14
{&CPU::ORA, 4, "ora", AddressingMode::DirectPageIndexedByX, 2}, // 15
{&CPU::ASL, 6, "asl", AddressingMode::DirectPageIndexedByX, 2}, // 16
{&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::INC, 2, "inc", AddressingMode::Implied, 1}, // 1A
{&CPU::TCS, 2, "tcs", AddressingMode::Implied, 1}, // 1B
{&CPU::TRB, 6, "trb", AddressingMode::Absolute, 3}, // 1C
{&CPU::ORA, 4, "ora", AddressingMode::AbsoluteIndexedByX, 3}, // 1D
{&CPU::ASL, 7, "asl", AddressingMode::AbsoluteIndexedByX, 3}, // 1E
{&CPU::ORA, 5, "ora", AddressingMode::AbsoluteIndexedByXLong, 4}, // 1F
{&CPU::JSR, 6, "jsr", AddressingMode::Absolute, 3}, // 20
{&CPU::AND, 6, "and", AddressingMode::DirectPageIndirectIndexedByX, 2}, // 21
{&CPU::JSL, 8, "jsl", AddressingMode::AbsoluteLong, 4}, // 22
{&CPU::AND, 4, "and", AddressingMode::StackRelative, 2}, // 23
{&CPU::BIT, 3, "bit", AddressingMode::DirectPage, 2}, // 24
{&CPU::AND, 3, "and", AddressingMode::DirectPage, 2}, // 25
{&CPU::ROL, 5, "rol", AddressingMode::DirectPage, 2}, // 26
{&CPU::AND, 6, "and", AddressingMode::DirectPageIndirectLong, 2}, // 27
{&CPU::PLP, 4, "plp", AddressingMode::Implied, 1}, // 28
{&CPU::AND, 2, "and", AddressingMode::ImmediateForA, 2}, // 29
{&CPU::ROL, 2, "rol", AddressingMode::Implied, 1}, // 2A
{&CPU::PLD, 5, "pld", AddressingMode::Implied, 1}, // 2B
{&CPU::BIT, 4, "bit", AddressingMode::Absolute, 3}, // 2C
{&CPU::AND, 4, "and", AddressingMode::Absolute, 3}, // 2D
{&CPU::ROL, 6, "rol", AddressingMode::Absolute, 3}, // 2E
{&CPU::AND, 5, "and", AddressingMode::AbsoluteLong, 4}, // 2F
{&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
{&CPU::BIT, 4, "bit", AddressingMode::DirectPageIndexedByX, 2}, // 34
{&CPU::AND, 4, "and", AddressingMode::DirectPageIndexedByX, 2}, // 35
{&CPU::ROL, 6, "rol", AddressingMode::DirectPageIndexedByX, 2}, // 36
{&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::TCS, 2, "tcs", AddressingMode::Implied, 1}, // 1B
{&CPU::TRB, 6, "trb", AddressingMode::Absolute, 3}, // 1C
{&CPU::ORA, 4, "ora", AddressingMode::AbsoluteIndexedByX, 3}, // 1D
{&CPU::ASL, 7, "asl", AddressingMode::AbsoluteIndexedByX, 3}, // 1E
{&CPU::ORA, 5, "ora", AddressingMode::AbsoluteIndexedByXLong, 4}, // 1F
{&CPU::JSR, 6, "jsr", AddressingMode::Absolute, 3}, // 20
{&CPU::AND, 6, "and", AddressingMode::DirectPageIndirectIndexedByX, 2}, // 21
{&CPU::JSL, 8, "jsl", AddressingMode::AbsoluteLong, 4}, // 22
{&CPU::AND, 4, "and", AddressingMode::StackRelative, 2}, // 23
{&CPU::BIT, 3, "bit", AddressingMode::DirectPage, 2}, // 24
{&CPU::AND, 3, "and", AddressingMode::DirectPage, 2}, // 25
{&CPU::ROL, 5, "rol", AddressingMode::DirectPage, 2}, // 26
{&CPU::AND, 6, "and", AddressingMode::DirectPageIndirectLong, 2}, // 27
{&CPU::PLP, 4, "plp", AddressingMode::Implied, 1}, // 28
{&CPU::AND, 2, "and", AddressingMode::ImmediateForA, 2}, // 29
{&CPU::ROL, 2, "rol", AddressingMode::Implied, 1}, // 2A
{&CPU::PLD, 5, "pld", AddressingMode::Implied, 1}, // 2B
{&CPU::BIT, 4, "bit", AddressingMode::Absolute, 3}, // 2C
{&CPU::AND, 4, "and", AddressingMode::Absolute, 3}, // 2D
{&CPU::ROL, 6, "rol", AddressingMode::Absolute, 3}, // 2E
{&CPU::AND, 5, "and", AddressingMode::AbsoluteLong, 4}, // 2F
{&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
{&CPU::BIT, 4, "bit", AddressingMode::DirectPageIndexedByX, 2}, // 34
{&CPU::AND, 4, "and", AddressingMode::DirectPageIndexedByX, 2}, // 35
{&CPU::ROL, 6, "rol", AddressingMode::DirectPageIndexedByX, 2}, // 36
{&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::DEC, 2, "dec", AddressingMode::Implied, 1}, // 3A
{&CPU::TSC, 2, "tsc", AddressingMode::Implied, 1}, // 3B
{&CPU::BIT, 4, "bit", AddressingMode::AbsoluteIndexedByX, 3}, // 3C
{&CPU::AND, 4, "and", AddressingMode::AbsoluteIndexedByX, 3}, // 3D
{&CPU::ROL, 7, "rol", AddressingMode::AbsoluteIndexedByX, 3}, // 3E
{&CPU::AND, 5, "and", AddressingMode::AbsoluteIndexedByXLong, 4}, // 3F
{&CPU::RTI, 6, "rti", AddressingMode::Implied, 1}, // 40
{&CPU::EOR, 6, "eor", AddressingMode::DirectPageIndirectIndexedByX, 2}, // 41
{&CPU::WDM, 2, "wdm", AddressingMode::Immediate8bits, 2}, // 42
{&CPU::EOR, 4, "eor", AddressingMode::StackRelative, 2}, // 43
{&CPU::MVP, 0, "mvp", AddressingMode::Immediate16bits, 3}, // 44
{&CPU::EOR, 3, "eor", AddressingMode::DirectPage, 2}, // 45
{&CPU::LSR, 5, "lsr", AddressingMode::DirectPage, 2}, // 46
{&CPU::EOR, 6, "eor", AddressingMode::DirectPageIndirectLong, 2}, // 47
{&CPU::PHA, 3, "pha", AddressingMode::Implied, 1}, // 48
{&CPU::EOR, 2, "eor", AddressingMode::ImmediateForA, 2}, // 49
{&CPU::LSR, 2, "lsr", AddressingMode::Implied, 1}, // 4A
{&CPU::PHK, 3, "phk", AddressingMode::Implied, 1}, // 4B
{&CPU::JMP, 3, "jmp", AddressingMode::Absolute, 3}, // 4C
{&CPU::EOR, 4, "eor", AddressingMode::Absolute, 3}, // 4D
{&CPU::LSR, 6, "lsr", AddressingMode::Absolute, 3}, // 4E
{&CPU::EOR, 5, "eor", AddressingMode::AbsoluteLong, 4}, // 4F
{&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
{&CPU::MVN, 0, "mvn", AddressingMode::Immediate16bits, 2}, // 54
{&CPU::EOR, 4, "eor", AddressingMode::DirectPageIndexedByX, 2}, // 55
{&CPU::LSR, 6, "lsr", AddressingMode::DirectPageIndexedByX, 2}, // 56
{&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::TSC, 2, "tsc", AddressingMode::Implied, 1}, // 3B
{&CPU::BIT, 4, "bit", AddressingMode::AbsoluteIndexedByX, 3}, // 3C
{&CPU::AND, 4, "and", AddressingMode::AbsoluteIndexedByX, 3}, // 3D
{&CPU::ROL, 7, "rol", AddressingMode::AbsoluteIndexedByX, 3}, // 3E
{&CPU::AND, 5, "and", AddressingMode::AbsoluteIndexedByXLong, 4}, // 3F
{&CPU::RTI, 6, "rti", AddressingMode::Implied, 1}, // 40
{&CPU::EOR, 6, "eor", AddressingMode::DirectPageIndirectIndexedByX, 2}, // 41
{&CPU::WDM, 2, "wdm", AddressingMode::Immediate8bits, 2}, // 42
{&CPU::EOR, 4, "eor", AddressingMode::StackRelative, 2}, // 43
{&CPU::MVP, 0, "mvp", AddressingMode::Immediate16bits, 3}, // 44
{&CPU::EOR, 3, "eor", AddressingMode::DirectPage, 2}, // 45
{&CPU::LSR, 5, "lsr", AddressingMode::DirectPage, 2}, // 46
{&CPU::EOR, 6, "eor", AddressingMode::DirectPageIndirectLong, 2}, // 47
{&CPU::PHA, 3, "pha", AddressingMode::Implied, 1}, // 48
{&CPU::EOR, 2, "eor", AddressingMode::ImmediateForA, 2}, // 49
{&CPU::LSR, 2, "lsr", AddressingMode::Implied, 1}, // 4A
{&CPU::PHK, 3, "phk", AddressingMode::Implied, 1}, // 4B
{&CPU::JMP, 3, "jmp", AddressingMode::Absolute, 3}, // 4C
{&CPU::EOR, 4, "eor", AddressingMode::Absolute, 3}, // 4D
{&CPU::LSR, 6, "lsr", AddressingMode::Absolute, 3}, // 4E
{&CPU::EOR, 5, "eor", AddressingMode::AbsoluteLong, 4}, // 4F
{&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
{&CPU::MVN, 0, "mvn", AddressingMode::Immediate16bits, 2}, // 54
{&CPU::EOR, 4, "eor", AddressingMode::DirectPageIndexedByX, 2}, // 55
{&CPU::LSR, 6, "lsr", AddressingMode::DirectPageIndexedByX, 2}, // 56
{&CPU::EOR, 6, "eor", AddressingMode::DirectPageIndirectIndexedByYLong, 2}, // 57
{&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::TCD, 2, "tcd", AddressingMode::Implied, 1}, // 5B
{&CPU::JML, 4, "jml", AddressingMode::Implied, 4}, // 5C
{&CPU::EOR, 4, "eor", AddressingMode::AbsoluteIndexedByX, 3}, // 5D
{&CPU::LSR, 7, "lsr", AddressingMode::AbsoluteIndexedByX, 3}, // 5E
{&CPU::EOR, 5, "eor", AddressingMode::AbsoluteIndexedByXLong, 4}, // 5F
{&CPU::RTS, 6, "rts", AddressingMode::Implied, 1}, // 60
{&CPU::ADC, 6, "adc", AddressingMode::DirectPageIndirectIndexedByX, 2}, // 61
{&CPU::PER, 6, "per", AddressingMode::Immediate16bits, 3}, // 62
{&CPU::ADC, 4, "adc", AddressingMode::StackRelative, 2}, // 63
{&CPU::STZ, 3, "stz", AddressingMode::DirectPage, 2}, // 64
{&CPU::ADC, 3, "adc", AddressingMode::DirectPage, 2}, // 65
{&CPU::ROR, 5, "ror", AddressingMode::DirectPage, 2}, // 66
{&CPU::ADC, 6, "adc", AddressingMode::DirectPageIndirectLong, 2}, // 67
{&CPU::PLA, 4, "pla", AddressingMode::Implied, 1}, // 68
{&CPU::ADC, 2, "adc", AddressingMode::ImmediateForA, 2}, // 69
{&CPU::ROR, 2, "ror", AddressingMode::Implied, 1}, // 6A
{&CPU::RTL, 6, "rtl", AddressingMode::Implied, 1}, // 6B
{&CPU::JMP, 5, "jmp", AddressingMode::AbsoluteIndirect, 3}, // 6C
{&CPU::ADC, 4, "adc", AddressingMode::Absolute, 3}, // 6D
{&CPU::ROR, 6, "ror", AddressingMode::Absolute, 3}, // 6E
{&CPU::ADC, 5, "adc", AddressingMode::AbsoluteLong, 4}, // 6F
{&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
{&CPU::STZ, 4, "stz", AddressingMode::DirectPageIndexedByX, 2}, // 74
{&CPU::ADC, 4, "adc", AddressingMode::DirectPageIndexedByX, 2}, // 75
{&CPU::ROR, 6, "ror", AddressingMode::DirectPageIndexedByX, 2}, // 76
{&CPU::ADC, 6, "adc", AddressingMode::DirectPageIndirectIndexedByY, 2}, // 77
{&CPU::SEI, 2, "sei", AddressingMode::Implied, 1}, // 78
{&CPU::ADC, 4, "adc", AddressingMode::AbsoluteIndexedByY, 2}, // 79
{&CPU::PLY, 4, "ply", AddressingMode::Implied, 1}, // 7A
{&CPU::TDC, 2, "tdc", AddressingMode::Implied, 1}, // 7B
{&CPU::JMP, 6, "jmp", AddressingMode::AbsoluteIndirectIndexedByX, 3}, // 7C
{&CPU::ADC, 4, "adc", AddressingMode::AbsoluteIndexedByX, 3}, // 7D
{&CPU::ROR, 7, "ror", AddressingMode::AbsoluteIndexedByX, 3}, // 7E
{&CPU::ADC, 5, "adc", AddressingMode::AbsoluteIndexedByXLong, 4}, // 7F
{&CPU::BRA, 3, "bra", AddressingMode::Immediate8bits, 2}, // 80
{&CPU::STA, 6, "sta", AddressingMode::DirectPageIndexedByX, 2}, // 81
{&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
{&CPU::STX, 3, "stx", AddressingMode::DirectPage, 2}, // 86
{&CPU::STA, 6, "sta", AddressingMode::DirectPageIndirectLong, 2}, // 87
{&CPU::DEY, 2, "dey", AddressingMode::Implied, 1}, // 88
{&CPU::BIT, 2, "bit", AddressingMode::ImmediateForA, 2}, // 89
{&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
{&CPU::STX, 4, "stx", AddressingMode::Absolute, 3}, // 8E
{&CPU::STA, 5, "sta", AddressingMode::AbsoluteLong, 4}, // 8F
{&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
{&CPU::STY, 4, "sty", AddressingMode::DirectPageIndirectIndexedByX, 2}, // 94
{&CPU::STA, 4, "sta", AddressingMode::DirectPageIndexedByX, 2}, // 95
{&CPU::STX, 4, "stx", AddressingMode::DirectPageIndexedByY, 2}, // 96
{&CPU::CLI, 2, "cli", AddressingMode::Implied, 1}, // 58
{&CPU::EOR, 4, "eor", AddressingMode::AbsoluteIndexedByY, 3}, // 59
{&CPU::PHY, 3, "phy", AddressingMode::Implied, 1}, // 5A
{&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::LSR, 7, "lsr", AddressingMode::AbsoluteIndexedByX, 3}, // 5E
{&CPU::EOR, 5, "eor", AddressingMode::AbsoluteIndexedByXLong, 4}, // 5F
{&CPU::RTS, 6, "rts", AddressingMode::Implied, 1}, // 60
{&CPU::ADC, 6, "adc", AddressingMode::DirectPageIndirectIndexedByX, 2}, // 61
{&CPU::PER, 6, "per", AddressingMode::Immediate16bits, 3}, // 62
{&CPU::ADC, 4, "adc", AddressingMode::StackRelative, 2}, // 63
{&CPU::STZ, 3, "stz", AddressingMode::DirectPage, 2}, // 64
{&CPU::ADC, 3, "adc", AddressingMode::DirectPage, 2}, // 65
{&CPU::ROR, 5, "ror", AddressingMode::DirectPage, 2}, // 66
{&CPU::ADC, 6, "adc", AddressingMode::DirectPageIndirectLong, 2}, // 67
{&CPU::PLA, 4, "pla", AddressingMode::Implied, 1}, // 68
{&CPU::ADC, 2, "adc", AddressingMode::ImmediateForA, 2}, // 69
{&CPU::ROR, 2, "ror", AddressingMode::Implied, 1}, // 6A
{&CPU::RTL, 6, "rtl", AddressingMode::Implied, 1}, // 6B
{&CPU::JMP, 5, "jmp", AddressingMode::AbsoluteIndirect, 3}, // 6C
{&CPU::ADC, 4, "adc", AddressingMode::Absolute, 3}, // 6D
{&CPU::ROR, 6, "ror", AddressingMode::Absolute, 3}, // 6E
{&CPU::ADC, 5, "adc", AddressingMode::AbsoluteLong, 4}, // 6F
{&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
{&CPU::STZ, 4, "stz", AddressingMode::DirectPageIndexedByX, 2}, // 74
{&CPU::ADC, 4, "adc", AddressingMode::DirectPageIndexedByX, 2}, // 75
{&CPU::ROR, 6, "ror", AddressingMode::DirectPageIndexedByX, 2}, // 76
{&CPU::ADC, 6, "adc", AddressingMode::DirectPageIndirectIndexedByY, 2}, // 77
{&CPU::SEI, 2, "sei", AddressingMode::Implied, 1}, // 78
{&CPU::ADC, 4, "adc", AddressingMode::AbsoluteIndexedByY, 2}, // 79
{&CPU::PLY, 4, "ply", AddressingMode::Implied, 1}, // 7A
{&CPU::TDC, 2, "tdc", AddressingMode::Implied, 1}, // 7B
{&CPU::JMP, 6, "jmp", AddressingMode::AbsoluteIndirectIndexedByX, 3}, // 7C
{&CPU::ADC, 4, "adc", AddressingMode::AbsoluteIndexedByX, 3}, // 7D
{&CPU::ROR, 7, "ror", AddressingMode::AbsoluteIndexedByX, 3}, // 7E
{&CPU::ADC, 5, "adc", AddressingMode::AbsoluteIndexedByXLong, 4}, // 7F
{&CPU::BRA, 3, "bra", AddressingMode::Immediate8bits, 2}, // 80
{&CPU::STA, 6, "sta", AddressingMode::DirectPageIndexedByX, 2}, // 81
{&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
{&CPU::STX, 3, "stx", AddressingMode::DirectPage, 2}, // 86
{&CPU::STA, 6, "sta", AddressingMode::DirectPageIndirectLong, 2}, // 87
{&CPU::DEY, 2, "dey", AddressingMode::Implied, 1}, // 88
{&CPU::BIT, 2, "bit", AddressingMode::ImmediateForA, 2}, // 89
{&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
{&CPU::STX, 4, "stx", AddressingMode::Absolute, 3}, // 8E
{&CPU::STA, 5, "sta", AddressingMode::AbsoluteLong, 4}, // 8F
{&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
{&CPU::STY, 4, "sty", AddressingMode::DirectPageIndirectIndexedByX, 2}, // 94
{&CPU::STA, 4, "sta", AddressingMode::DirectPageIndexedByX, 2}, // 95
{&CPU::STX, 4, "stx", AddressingMode::DirectPageIndexedByY, 2}, // 96
{&CPU::STA, 6, "sta", AddressingMode::DirectPageIndirectIndexedByYLong, 2}, // 97
{&CPU::TYA, 2, "tya", AddressingMode::Implied, 1}, // 98
{&CPU::STA, 5, "sta", AddressingMode::AbsoluteIndexedByY, 3}, // 99
{&CPU::TXS, 2, "txs", AddressingMode::Implied, 1}, // 9A
{&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
{&CPU::STA, 5, "sta", AddressingMode::AbsoluteIndexedByXLong, 4}, // 9F
{&CPU::LDY, 2, "ldy", AddressingMode::ImmediateForX, 2}, // A0
{&CPU::LDA, 6, "lda", AddressingMode::DirectPageIndirectIndexedByX, 2}, // A1
{&CPU::LDX, 2, "ldx", AddressingMode::ImmediateForX, 2}, // A2
{&CPU::LDA, 4, "lda", AddressingMode::StackRelative, 2}, // A3
{&CPU::LDY, 3, "ldy", AddressingMode::DirectPage, 2}, // A4
{&CPU::LDA, 3, "lda", AddressingMode::DirectPage, 2}, // A5
{&CPU::LDX, 3, "ldx", AddressingMode::DirectPage, 2}, // A6
{&CPU::LDA, 6, "lda", AddressingMode::DirectPageIndirectLong, 2}, // A7
{&CPU::TAY, 2, "tay", AddressingMode::Implied, 1}, // A8
{&CPU::LDA, 2, "lda", AddressingMode::ImmediateForA, 2}, // A9
{&CPU::TAX, 2, "tax", AddressingMode::Implied, 1}, // AA
{&CPU::PLB, 4, "plb", AddressingMode::Implied, 1}, // AB
{&CPU::LDY, 4, "ldy", AddressingMode::Absolute, 4}, // AC
{&CPU::LDA, 4, "lda", AddressingMode::Absolute, 3}, // AD
{&CPU::LDX, 4, "ldx", AddressingMode::Absolute, 3}, // AE
{&CPU::LDA, 5, "lda", AddressingMode::AbsoluteLong, 4}, // AF
{&CPU::BCS, 2, "bcs", AddressingMode::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
{&CPU::LDY, 4, "ldy", AddressingMode::DirectPageIndexedByX, 2}, // B4
{&CPU::LDA, 4, "lda", AddressingMode::DirectPageIndexedByX, 2}, // B5
{&CPU::LDX, 4, "ldx", AddressingMode::DirectPageIndexedByY, 2}, // B6
{&CPU::TYA, 2, "tya", AddressingMode::Implied, 1}, // 98
{&CPU::STA, 5, "sta", AddressingMode::AbsoluteIndexedByY, 3}, // 99
{&CPU::TXS, 2, "txs", AddressingMode::Implied, 1}, // 9A
{&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
{&CPU::STA, 5, "sta", AddressingMode::AbsoluteIndexedByXLong, 4}, // 9F
{&CPU::LDY, 2, "ldy", AddressingMode::ImmediateForX, 2}, // A0
{&CPU::LDA, 6, "lda", AddressingMode::DirectPageIndirectIndexedByX, 2}, // A1
{&CPU::LDX, 2, "ldx", AddressingMode::ImmediateForX, 2}, // A2
{&CPU::LDA, 4, "lda", AddressingMode::StackRelative, 2}, // A3
{&CPU::LDY, 3, "ldy", AddressingMode::DirectPage, 2}, // A4
{&CPU::LDA, 3, "lda", AddressingMode::DirectPage, 2}, // A5
{&CPU::LDX, 3, "ldx", AddressingMode::DirectPage, 2}, // A6
{&CPU::LDA, 6, "lda", AddressingMode::DirectPageIndirectLong, 2}, // A7
{&CPU::TAY, 2, "tay", AddressingMode::Implied, 1}, // A8
{&CPU::LDA, 2, "lda", AddressingMode::ImmediateForA, 2}, // A9
{&CPU::TAX, 2, "tax", AddressingMode::Implied, 1}, // AA
{&CPU::PLB, 4, "plb", AddressingMode::Implied, 1}, // AB
{&CPU::LDY, 4, "ldy", AddressingMode::Absolute, 4}, // AC
{&CPU::LDA, 4, "lda", AddressingMode::Absolute, 3}, // AD
{&CPU::LDX, 4, "ldx", AddressingMode::Absolute, 3}, // AE
{&CPU::LDA, 5, "lda", AddressingMode::AbsoluteLong, 4}, // AF
{&CPU::BCS, 2, "bcs", AddressingMode::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
{&CPU::LDY, 4, "ldy", AddressingMode::DirectPageIndexedByX, 2}, // B4
{&CPU::LDA, 4, "lda", AddressingMode::DirectPageIndexedByX, 2}, // B5
{&CPU::LDX, 4, "ldx", AddressingMode::DirectPageIndexedByY, 2}, // B6
{&CPU::LDA, 6, "lda", AddressingMode::DirectPageIndirectIndexedByYLong, 2}, // B7
{&CPU::CLV, 7, "clv", AddressingMode::Implied, 1}, // B8
{&CPU::LDA, 4, "lda", AddressingMode::AbsoluteIndexedByY, 3}, // B9
{&CPU::TSX, 2, "tsx", AddressingMode::Implied, 1}, // BA
{&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
{&CPU::LDA, 5, "lda", AddressingMode::AbsoluteIndexedByXLong, 4}, // BF
{&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::CPY, 3, "cpy", AddressingMode::DirectPage, 2}, // C4
{&CPU::CMP, 3, "cmp", AddressingMode::DirectPage, 2}, // C5
{&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
{&CPU::DEX, 2, "dex", AddressingMode::Implied, 1}, // CA
{&CPU::WAI, 3, "wai", AddressingMode::Implied, 1}, // CB
{&CPU::CPY, 4, "cpy", AddressingMode::Absolute, 3}, // CC
{&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::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
{&CPU::PEI, 6, "pei", AddressingMode::DirectPage, 2}, // D4
{&CPU::CMP, 4, "cmp", AddressingMode::DirectPageIndexedByX, 2}, // D5
{&CPU::DEC, 6, "dec", AddressingMode::DirectPageIndexedByX, 2}, // D6
{&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::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
{&CPU::LDA, 5, "lda", AddressingMode::AbsoluteIndexedByXLong, 4}, // BF
{&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::CPY, 3, "cpy", AddressingMode::DirectPage, 2}, // C4
{&CPU::CMP, 3, "cmp", AddressingMode::DirectPage, 2}, // C5
{&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
{&CPU::DEX, 2, "dex", AddressingMode::Implied, 1}, // CA
{&CPU::WAI, 3, "wai", AddressingMode::Implied, 1}, // CB
{&CPU::CPY, 4, "cpy", AddressingMode::Absolute, 3}, // CC
{&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::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
{&CPU::PEI, 6, "pei", AddressingMode::DirectPage, 2}, // D4
{&CPU::CMP, 4, "cmp", AddressingMode::DirectPageIndexedByX, 2}, // D5
{&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
{&CPU::PHX, 3, "phx", AddressingMode::Implied, 1}, // DA
{&CPU::STP, 3, "stp", AddressingMode::Implied, 1}, // DB
{&CPU::JML, 7, "jml", AddressingMode::AbsoluteIndirectLong, 2}, // DC
{&CPU::CMP, 4, "cmp", AddressingMode::AbsoluteIndexedByX, 3}, // DD
{&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
{&CPU::SEP, 3, "sep", AddressingMode::Immediate8bits, 2}, // E2
{&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::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
{&CPU::NOP, 2, "nop", AddressingMode::Implied, 1}, // EA
{&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
{&CPU::SBC, 5, "sbc", AddressingMode::AbsoluteLong, 4}, // EF
{&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
{&CPU::PEA, 5, "pea", AddressingMode::Immediate16bits, 3}, // F4
{&CPU::SBC, 4, "sbc", AddressingMode::DirectPageIndexedByX, 2}, // F5
{&CPU::INC, 6, "inc", AddressingMode::DirectPageIndexedByX, 2}, // F6
{&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::STP, 3, "stp", AddressingMode::Implied, 1}, // DB
{&CPU::JML, 7, "jml", AddressingMode::AbsoluteIndirectLong, 2}, // DC
{&CPU::CMP, 4, "cmp", AddressingMode::AbsoluteIndexedByX, 3}, // DD
{&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
{&CPU::SEP, 3, "sep", AddressingMode::Immediate8bits, 2}, // E2
{&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::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
{&CPU::NOP, 2, "nop", AddressingMode::Implied, 1}, // EA
{&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
{&CPU::SBC, 5, "sbc", AddressingMode::AbsoluteLong, 4}, // EF
{&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
{&CPU::PEA, 5, "pea", AddressingMode::Immediate16bits, 3}, // F4
{&CPU::SBC, 4, "sbc", AddressingMode::DirectPageIndexedByX, 2}, // F5
{&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
{&CPU::PLX, 4, "plx", AddressingMode::Implied, 1}, // FA
{&CPU::XCE, 2, "xce", AddressingMode::Implied, 1}, // FB
{&CPU::JSR, 8, "jsr", AddressingMode::AbsoluteIndirectIndexedByX, 3}, // FC
{&CPU::SBC, 4, "sbc", AddressingMode::AbsoluteIndexedByX, 3}, // FD
{&CPU::INC, 7, "inc", AddressingMode::AbsoluteIndexedByX, 3}, // FE
{&CPU::SBC, 5, "sbc", AddressingMode::AbsoluteIndexedByXLong, 4}, // FF
{&CPU::SED, 2, "sed", AddressingMode::Implied, 1}, // F8
{&CPU::SBC, 4, "sbc", AddressingMode::AbsoluteIndexedByY, 3}, // F9
{&CPU::PLX, 4, "plx", AddressingMode::Implied, 1}, // FA
{&CPU::XCE, 2, "xce", AddressingMode::Implied, 1}, // FB
{&CPU::JSR, 8, "jsr", AddressingMode::AbsoluteIndirectIndexedByX, 3}, // FC
{&CPU::SBC, 4, "sbc", AddressingMode::AbsoluteIndexedByX, 3}, // FD
{&CPU::INC, 7, "inc", AddressingMode::AbsoluteIndexedByX, 3}, // FE
{&CPU::SBC, 5, "sbc", AddressingMode::AbsoluteIndexedByXLong, 4}, // FF
};
public:
explicit CPU(std::shared_ptr<Memory::MemoryBus> bus, Cartridge::Header &cartridgeHeader);
//! @brief Construct a new generic CPU.
//! @param bus The memory bus to use to transfer data.
//! @param cartridgeHeader The header used to know interrupts, main entry point etc...
CPU(Memory::IMemoryBus &bus, Cartridge::Header &cartridgeHeader);
//! @brief A default copy constructor
CPU(const CPU &) = default;
//! @brief A CPU is not assignable
CPU &operator=(const CPU &) = delete;
//! @brief A default destructor
~CPU() override = default;
//! @brief This function continue to execute the Cartridge code.
//! @param maxCycle The maximum number of cycle to run.
//! @return The number of CPU cycles that elapsed
virtual unsigned update();
unsigned update(unsigned maxCycle);
//! @brief Execute a single instruction.
//! @return The number of CPU cycles that the instruction took.
unsigned executeInstruction();
//! @brief Run DMA's pending transfers.
//! @param maxCycles The maximum of cycle to run
//! @return The number of CPU cycles that elapsed
unsigned runDMA(unsigned maxCycles);
//! @brief Read from the internal CPU 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 $1F (the number of register).
@@ -748,18 +619,26 @@ namespace ComSquare::CPU
//! @throw InvalidAddress will be thrown if the address is more than $1F (the number of register).
void write(uint24_t addr, uint8_t data) override;
//! @brief Get the name of the data at the address
//! @param addr The address (in local space)
[[nodiscard]] std::string getValueName(uint24_t addr) const override;
//! @brief Get the size of the data. This size can be lower than the mapped data.
//! @return The number of bytes inside this memory.
uint24_t getSize() const override;
[[nodiscard]] uint24_t getSize() const override;
//! @brief Get the name of this accessor (used for debug purpose)
std::string getName() const override;
[[nodiscard]] std::string getName() const override;
//! @brief Get the component of this accessor (used for debug purpose)
Component getComponent() const override;
[[nodiscard]] Component getComponent() const override;
//! @brief Reset interrupt - Called on boot and when the reset button is pressed.
virtual int RESB();
//! @note This also triggers the callback onReset;
int RESB();
//! @brief The callback triggered on reset.
Callback<> onReset;
//! @brief Is an NMI (non-maskable interrupt) requested.
bool IsNMIRequested = false;
@@ -768,16 +647,12 @@ namespace ComSquare::CPU
//! @brief Is an abort requested
bool IsAbortRequested = false;
//! @brief Return true if the CPU is overloaded with debugging features.
virtual bool isDebugger() const;
//! @brief True if you want to disable updates of this CPU.
bool isDisabled = false;
//! @brief Change the memory bus used by the CPU.
virtual void setMemoryBus(std::shared_ptr<Memory::MemoryBus> bus);
#ifdef DEBUGGER_ENABLED
#ifdef DEBUGGER_ENABLED
friend Debugger::CPU::CPUDebug;
friend Debugger::RegisterViewer;
#endif
#endif
};
}
#endif //COMSQUARE_CPU_HPP
}

View File

@@ -2,17 +2,19 @@
// Created by anonymus-raccoon on 5/26/20.
//
#include <iostream>
#include "DMA.hpp"
#include "../../Exceptions/InvalidAddress.hpp"
#include "Exceptions/InvalidAddress.hpp"
namespace ComSquare::CPU
{
DMA::DMA(std::shared_ptr<Memory::MemoryBus> bus) : _bus(std::move(bus)) {}
DMA::DMA(Memory::IMemoryBus &bus)
: _bus(bus),
enabled(false)
{}
void DMA::setBus(std::shared_ptr<Memory::MemoryBus> bus)
void DMA::setBus(Memory::IMemoryBus &bus)
{
this->_bus = std::move(bus);
this->_bus = bus;
}
uint8_t DMA::read(uint8_t addr) const
@@ -68,24 +70,25 @@ namespace ComSquare::CPU
unsigned DMA::_writeOneByte(uint24_t aAddress, uint24_t bAddress)
{
// Address $2180 refers to the WRam data register. Write to/Read from this port when the a address is on the vram cause different behaviors.
// Address $2180 refers to the WRam data register.
// Write to/Read from this port when the a address is on the vram cause different behaviors.
if (this->_port == 0x80) {
auto accessor = this->_bus->getAccessor(aAddress);
auto accessor = this->getBus().getAccessor(aAddress);
if (accessor && accessor->getComponent() == WRam) {
// WRAM->$2180 The write is not performed but the time is consumed anyway.
if (this->_controlRegister.direction == AtoB)
return 8;
// $2180->WRAM No read is performed (so only 4 master cycles are needed) but the value written is invalid.
this->_bus->write(aAddress, 0xFF);
this->getBus().write(aAddress, 0xFF);
return 4;
}
}
if (this->_controlRegister.direction == AtoB) {
uint8_t data = this->_bus->read(aAddress);
this->_bus->write(bAddress, data);
uint8_t data = this->getBus().read(aAddress);
this->getBus().write(bAddress, data);
} else {
uint8_t data = this->_bus->read(bAddress);
this->_bus->write(aAddress, data);
uint8_t data = this->getBus().read(bAddress);
this->getBus().write(aAddress, data);
}
return 8;
}
@@ -106,7 +109,7 @@ namespace ComSquare::CPU
return cycles;
}
int DMA::_getModeOffset(int index)
int DMA::_getModeOffset(int index) const
{
switch (this->_controlRegister.mode) {
case OneToOne:
@@ -127,4 +130,4 @@ namespace ComSquare::CPU
}
return 0;
}
}
}// namespace ComSquare::CPU

View File

@@ -2,25 +2,26 @@
// Created by anonymus-raccoon on 5/26/20.
//
#ifndef COMSQUARE_DMA_HPP
#define COMSQUARE_DMA_HPP
#pragma once
#include "Memory/MemoryBus.hpp"
#include "Models/Ints.hpp"
#include <cstdint>
#include <memory>
#include "../../Models/Int24.hpp"
#include "../../Memory/MemoryBus.hpp"
#ifdef DEBUGGER_ENABLED
#include "../../Debugger/RegisterViewer.hpp"
#include "Debugger/RegisterViewer.hpp"
#endif
namespace ComSquare::CPU
{
//! @brief Class handling all DMA/HDMA transfers (Direct Memory Access or H-Blank Direct Memory Access)
class DMA {
class DMA
{
public:
//! @brief The first three bytes of the DMA's control register. Used to tell how many bytes/registers there is.
enum DMAMode {
enum DMAMode
{
//! @brief 1 byte is transferred to 1 register (write once)
OneToOne = 0b000,
//! @brief 2 byte is transferred to 2 register (write once)
@@ -39,63 +40,82 @@ namespace ComSquare::CPU
FourToTwoBis = 0b111
};
enum Direction {
enum Direction
{
AtoB,
BtoA
};
private:
//! @brief Write one byte using the A address, the port and the _direction. Handle special cases where no write occurs.
//! @return The number of cycles used.
unsigned _writeOneByte(uint24_t aAddress, uint24_t bAddress);
//! @brief Get an offset corresponding to the current DMAMode and the index of the currently transferred byte.
int _getModeOffset(int index);
[[nodiscard]] int _getModeOffset(int index) const;
//! @brief DMA Control register (various information about the transfer)
union {
union
{
struct {
//! @brief DMA's mode: how many bytes/registers there is, how many writes...
DMAMode mode: 3;
DMAMode mode : 3;
//! @brief If this flag is set, no increment/decrement will be done.
bool fixed: 1;
bool fixed : 1;
//! @brief if this flag is 0: increment. Else: decrement. (The A address)
bool increment: 1;
bool increment : 1;
//! @brief Two unused bites.
bool _: 2;
bool _ : 2;
//! @brief The direction of the transfer.
Direction direction: 1;
Direction direction : 1;
};
uint8_t raw;
} _controlRegister;
} _controlRegister {};
//! @brief If this is 'xx', the register accessed will be $21xx.
uint8_t _port;
uint8_t _port {};
//! @brief The absolute long address of the data from the A bus.
union {
union
{
uint8_t bytes[3];
struct {
uint16_t page;
uint8_t bank;
};
uint24_t raw: 24;
} _aAddress;
uint24_t raw : 24;
} _aAddress {};
//! @brief The number of bytes to be transferred.
union {
union
{
uint8_t bytes[2];
uint16_t raw;
} _count;
} _count {};
//! @brief The memory bus to use for read/write.
std::shared_ptr<Memory::MemoryBus> _bus;
Memory::IMemoryBus &_bus;
public:
//! @brief Get the memory bus used by this CPU.
[[nodiscard]] inline Memory::IMemoryBus &getBus()
{
return this->_bus;
}
//! @brief Set the memory bus used by this CPU
//! @param bus The bus to use.
void setBus(Memory::IMemoryBus &bus);
//! @brief Is this channel set to run?
bool enabled;
//! @brief Set the memory bus used by this dma channel.
void setBus(std::shared_ptr<Memory::MemoryBus> bus);
//! @brief Bus helper to read from this channel.
uint8_t read(uint8_t addr) const;
//! @param addr The address to read from
//! @return The value at the given address.
[[nodiscard]] uint8_t read(uint8_t addr) const;
//! @brief Bus helper to write to this channel.
//! @param addr The address to write to
//! @param data The data to write.
void write(uint8_t addr, uint8_t data);
//! @brief Run the DMA for x cycles
@@ -103,16 +123,18 @@ namespace ComSquare::CPU
//! @return the number of cycles taken
unsigned run(unsigned cycles);
DMA() = default;
DMA(std::shared_ptr<Memory::MemoryBus> bus);
//! @brief Create a DMA channel with a given bus
//! @param bus The memory bus to use.
explicit DMA(Memory::IMemoryBus &bus);
//! @brief A DMA is copy constructable.
DMA(const DMA &) = default;
DMA &operator=(const DMA &) = default;
//! @brief A DMA is not assignable
DMA &operator=(const DMA &) = delete;
//! @brief A default destructor.
~DMA() = default;
#ifdef DEBUGGER_ENABLED
#ifdef DEBUGGER_ENABLED
friend Debugger::RegisterViewer;
#endif
#endif
};
}
#endif //COMSQUARE_DMA_HPP
}// namespace ComSquare::CPU

View File

@@ -2,11 +2,10 @@
// Created by anonymus-raccoon on 3/25/20.
//
#ifndef COMSQUARE_INSTRUCTION_HPP
#define COMSQUARE_INSTRUCTION_HPP
#pragma once
#include <string>
#include "../Models/Int24.hpp"
#include "Models/Ints.hpp"
namespace ComSquare::CPU
{
@@ -52,9 +51,8 @@ namespace ComSquare::CPU
struct Instruction {
int (CPU::*call)(uint24_t valueAddr, AddressingMode mode) = nullptr;
int cycleCount = 0;
std::string name = "";
std::string name;
AddressingMode addressingMode = Implied;
int size = 0;
};
}
#endif //COMSQUARE_INSTRUCTION_HPP
}

View File

@@ -3,20 +3,20 @@
//
#include <iostream>
#include "../../Models/Int24.hpp"
#include "../../Models/Ints.hpp"
#include "../CPU.hpp"
namespace ComSquare::CPU
{
int CPU::TSB(uint24_t valueAddr, AddressingMode mode)
{
uint16_t value = this->_bus->read(valueAddr);
uint16_t value = this->getBus().read(valueAddr);
if (!this->_registers.p.m)
value += this->_bus->read(valueAddr + 1) << 8u;
value += this->getBus().read(valueAddr + 1) << 8u;
value |= this->_registers.a;
this->_bus->write(valueAddr, value);
this->getBus().write(valueAddr, value);
if (!this->_registers.p.m)
this->_bus->write(valueAddr + 1, value >> 8u);
this->getBus().write(valueAddr + 1, value >> 8u);
this->_registers.p.z = value == 0;
@@ -30,14 +30,14 @@ namespace ComSquare::CPU
int CPU::TRB(uint24_t valueAddr, AddressingMode mode)
{
uint16_t value = this->_bus->read(valueAddr);
uint16_t value = this->getBus().read(valueAddr);
if (!this->_registers.p.m)
value += this->_bus->read(valueAddr + 1) << 8u;
value += this->getBus().read(valueAddr + 1) << 8u;
uint16_t newValue = value & ~this->_registers.a;
this->_bus->write(valueAddr, newValue);
this->getBus().write(valueAddr, newValue);
if (!this->_registers.p.m)
this->_bus->write(valueAddr + 1, newValue >> 8u);
this->getBus().write(valueAddr + 1, newValue >> 8u);
this->_registers.p.z = (value & this->_registers.a) == 0;
@@ -52,9 +52,9 @@ namespace ComSquare::CPU
int CPU::BIT(uint24_t valueAddr, AddressingMode mode)
{
unsigned negativeMask = this->_registers.p.m ? 0x80u : 0x8000u;
unsigned value = this->_bus->read(valueAddr);
unsigned value = this->getBus().read(valueAddr);
if (!this->_registers.p.m)
value += this->_bus->read(valueAddr + 1) << 8u;
value += this->getBus().read(valueAddr + 1) << 8u;
if (mode != ImmediateForA) {
this->_registers.p.n = value & negativeMask;
@@ -89,18 +89,18 @@ namespace ComSquare::CPU
return 0;
}
uint16_t value = this->_bus->read(valueAddr);
uint16_t value = this->getBus().read(valueAddr);
if (!this->_registers.p.m)
value += this->_bus->read(valueAddr + 1) << 8u;
value += this->getBus().read(valueAddr + 1) << 8u;
this->_registers.p.c = value & highByte;
value <<= 1u;
this->_registers.p.n = value & highByte;
this->_registers.p.z = value == 0;
this->_bus->write(valueAddr, value);
this->getBus().write(valueAddr, value);
if (!this->_registers.p.m)
this->_bus->write(valueAddr + 1, value >> 8u);
this->getBus().write(valueAddr + 1, value >> 8u);
int cycles = 2 * !this->_registers.p.m;
switch (mode) {
@@ -128,17 +128,17 @@ namespace ComSquare::CPU
return 0;
}
uint16_t value = this->_bus->read(valueAddr);
uint16_t value = this->getBus().read(valueAddr);
if (!this->_registers.p.m)
value += this->_bus->read(valueAddr + 1) << 8u;
value += this->getBus().read(valueAddr + 1) << 8u;
this->_registers.p.c = value & 1u;
value >>= 1u;
this->_registers.p.z = value == 0;
this->_bus->write(valueAddr, value);
this->getBus().write(valueAddr, value);
if (!this->_registers.p.m)
this->_bus->write(valueAddr + 1, value >> 8u);
this->getBus().write(valueAddr + 1, value >> 8u);
int cycles = 2 * !this->_registers.p.m;
switch (mode) {
@@ -169,9 +169,9 @@ namespace ComSquare::CPU
return 0;
}
uint16_t value = this->_bus->read(valueAddr);
uint16_t value = this->getBus().read(valueAddr);
if (!this->_registers.p.m)
value += this->_bus->read(valueAddr + 1) << 8u;
value += this->getBus().read(valueAddr + 1) << 8u;
this->_registers.p.c = value & highByte;
value <<= 1u;
@@ -179,9 +179,9 @@ namespace ComSquare::CPU
this->_registers.p.n = value & highByte;
this->_registers.p.z = value == 0;
this->_bus->write(valueAddr, value);
this->getBus().write(valueAddr, value);
if (!this->_registers.p.m)
this->_bus->write(valueAddr + 1, value >> 8u);
this->getBus().write(valueAddr + 1, value >> 8u);
int cycles = 2 * !this->_registers.p.m;
switch (mode) {
@@ -212,18 +212,18 @@ namespace ComSquare::CPU
return 0;
}
uint16_t value = this->_bus->read(valueAddr);
uint16_t value = this->getBus().read(valueAddr);
if (!this->_registers.p.m)
value += this->_bus->read(valueAddr + 1) << 8u;
value += this->getBus().read(valueAddr + 1) << 8u;
this->_registers.p.c = value & 1u;
value >>= 1u;
value |= oldCarry << highByteIndex;
this->_registers.p.z = value == 0;
this->_bus->write(valueAddr, value);
this->getBus().write(valueAddr, value);
if (!this->_registers.p.m)
this->_bus->write(valueAddr + 1, value >> 8u);
this->getBus().write(valueAddr + 1, value >> 8u);
int cycles = 2 * !this->_registers.p.m;
switch (mode) {

View File

@@ -50,13 +50,13 @@ namespace ComSquare::CPU
int CPU::SEP(uint24_t valueAddr, AddressingMode)
{
this->_registers.p.flags |= this->_bus->read(valueAddr);
this->_registers.p.flags |= this->getBus().read(valueAddr);
return 0;
}
int CPU::REP(uint24_t valueAddr, AddressingMode)
{
this->_registers.p.flags &= ~this->_bus->read(valueAddr);
this->_registers.p.flags &= ~this->getBus().read(valueAddr);
if (this->_isEmulationMode) {
this->_registers.p.x_b = true;
this->_registers.p.m = true;
@@ -189,8 +189,8 @@ namespace ComSquare::CPU
int CPU::PER(uint24_t valueAddr, AddressingMode)
{
uint16_t value = this->_bus->read(valueAddr);
value += this->_bus->read(valueAddr + 1) << 8u;
uint16_t value = this->getBus().read(valueAddr);
value += this->getBus().read(valueAddr + 1) << 8u;
value += this->_registers.pc;
this->_push(value);
return 0;
@@ -226,55 +226,55 @@ namespace ComSquare::CPU
int CPU::BCC(uint24_t valueAddr, AddressingMode)
{
if (!this->_registers.p.c)
this->_registers.pc += static_cast<int8_t>(this->_bus->read(valueAddr));
this->_registers.pc += static_cast<int8_t>(this->getBus().read(valueAddr));
return !this->_registers.p.c + this->_isEmulationMode;
}
int CPU::BCS(uint24_t valueAddr, AddressingMode)
{
if (this->_registers.p.c)
this->_registers.pc += static_cast<int8_t>(this->_bus->read(valueAddr));
this->_registers.pc += static_cast<int8_t>(this->getBus().read(valueAddr));
return this->_registers.p.c + this->_isEmulationMode;
}
int CPU::BEQ(uint24_t valueAddr, AddressingMode)
{
if (this->_registers.p.z)
this->_registers.pc += static_cast<int8_t>(this->_bus->read(valueAddr));
this->_registers.pc += static_cast<int8_t>(this->getBus().read(valueAddr));
return this->_registers.p.z + this->_isEmulationMode;
}
int CPU::BNE(uint24_t valueAddr, AddressingMode)
{
if (!this->_registers.p.z)
this->_registers.pc += static_cast<int8_t>(this->_bus->read(valueAddr));
this->_registers.pc += static_cast<int8_t>(this->getBus().read(valueAddr));
return !this->_registers.p.z + this->_isEmulationMode;
}
int CPU::BMI(uint24_t valueAddr, AddressingMode)
{
if (this->_registers.p.n)
this->_registers.pc += static_cast<int8_t>(this->_bus->read(valueAddr));
this->_registers.pc += static_cast<int8_t>(this->getBus().read(valueAddr));
return this->_registers.p.n + this->_isEmulationMode;
}
int CPU::BPL(uint24_t valueAddr, AddressingMode)
{
if (!this->_registers.p.n)
this->_registers.pc += static_cast<int8_t>(this->_bus->read(valueAddr));
this->_registers.pc += static_cast<int8_t>(this->getBus().read(valueAddr));
return !this->_registers.p.n + this->_isEmulationMode;
}
int CPU::BRA(uint24_t valueAddr, AddressingMode)
{
this->_registers.pc += static_cast<int8_t>(this->_bus->read(valueAddr));
this->_registers.pc += static_cast<int8_t>(this->getBus().read(valueAddr));
return this->_isEmulationMode;
}
int CPU::BRL(uint24_t valueAddr, AddressingMode)
{
unsigned value = this->_bus->read(valueAddr);
value += this->_bus->read(valueAddr + 1) << 8u;
unsigned value = this->getBus().read(valueAddr);
value += this->getBus().read(valueAddr + 1) << 8u;
this->_registers.pc += static_cast<int16_t>(value);
return 0;
@@ -283,14 +283,14 @@ namespace ComSquare::CPU
int CPU::BVC(uint24_t valueAddr, AddressingMode)
{
if (!this->_registers.p.v)
this->_registers.pc += static_cast<int8_t>(this->_bus->read(valueAddr));
this->_registers.pc += static_cast<int8_t>(this->getBus().read(valueAddr));
return !this->_registers.p.v + this->_isEmulationMode;
}
int CPU::BVS(uint24_t valueAddr, AddressingMode)
{
if (this->_registers.p.v)
this->_registers.pc += static_cast<int8_t>(this->_bus->read(valueAddr));
this->_registers.pc += static_cast<int8_t>(this->getBus().read(valueAddr));
return this->_registers.p.v + this->_isEmulationMode;
}

View File

@@ -2,7 +2,7 @@
// Created by anonymus-raccoon on 2/10/20.
//
#include "../CPU.hpp"
#include "CPU/CPU.hpp"
namespace ComSquare::CPU
{
@@ -19,6 +19,7 @@ namespace ComSquare::CPU
this->_registers.sh = 0x01; // the low bit of the stack pointer is undefined on reset.
this->_registers.pc = this->_cartridgeHeader.emulationInterrupts.reset;
this->_isStopped = false;
this->onReset();
return 0;
}

View File

@@ -9,9 +9,9 @@ namespace ComSquare::CPU
{
int CPU::ADC(uint24_t valueAddr, AddressingMode mode)
{
unsigned value = this->_bus->read(valueAddr) + this->_registers.p.c;
unsigned value = this->getBus().read(valueAddr) + this->_registers.p.c;
if (!this->_registers.p.m)
value += this->_bus->read(valueAddr + 1) << 8u;
value += this->getBus().read(valueAddr + 1) << 8u;
unsigned negativeMask = this->_registers.p.m ? 0x80u : 0x8000u;
unsigned maxValue = this->_registers.p.m ? UINT8_MAX : UINT16_MAX;
@@ -52,9 +52,9 @@ namespace ComSquare::CPU
int CPU::SBC(uint24_t valueAddr, AddressingMode mode)
{
unsigned negativeMask = this->_registers.p.m ? 0x80u : 0x8000u;
unsigned value = this->_bus->read(valueAddr);
unsigned value = this->getBus().read(valueAddr);
if (!this->_registers.p.m)
value += this->_bus->read(valueAddr + 1) << 8u;
value += this->getBus().read(valueAddr + 1) << 8u;
bool oldCarry = this->_registers.p.c;
this->_registers.p.c = this->_registers.a >= value;
@@ -94,9 +94,9 @@ namespace ComSquare::CPU
int CPU::ORA(uint24_t valueAddr, AddressingMode mode)
{
unsigned negativeMask = this->_registers.p.m ? 0x80u : 0x8000u;
unsigned value = this->_bus->read(valueAddr);
unsigned value = this->getBus().read(valueAddr);
if (!this->_registers.p.m)
value += this->_bus->read(valueAddr + 1) << 8u;
value += this->getBus().read(valueAddr + 1) << 8u;
this->_registers.a |= value;
this->_registers.p.z = this->_registers.a == 0;
this->_registers.p.n = this->_registers.a & negativeMask;
@@ -151,9 +151,9 @@ namespace ComSquare::CPU
int CPU::CMP(uint24_t valueAddr, AddressingMode mode)
{
unsigned negativeMask = this->_registers.p.m ? 0x80u : 0x8000u;
unsigned value = this->_bus->read(valueAddr);
unsigned value = this->getBus().read(valueAddr);
if (!this->_registers.p.m)
value += this->_bus->read(valueAddr + 1) << 8u;
value += this->getBus().read(valueAddr + 1) << 8u;
unsigned result = this->_registers.a - value;
if (this->_registers.p.m)
result %= 0x100;
@@ -214,7 +214,7 @@ namespace ComSquare::CPU
int CPU::CPX(uint24_t valueAddr, AddressingMode mode)
{
unsigned value = this->_bus->read(valueAddr++);
unsigned value = this->getBus().read(valueAddr++);
if (this->_registers.p.x_b) {
uint8_t x = this->_registers.x;
@@ -222,7 +222,7 @@ namespace ComSquare::CPU
this->_registers.p.z = x == 0;
this->_registers.p.n = x & 0x80u;
} else {
value += this->_bus->read(valueAddr) << 8u;
value += this->getBus().read(valueAddr) << 8u;
uint16_t x = this->_registers.x;
x -= value;
this->_registers.p.z = x == 0;
@@ -234,7 +234,7 @@ namespace ComSquare::CPU
int CPU::CPY(uint24_t valueAddr, AddressingMode mode)
{
unsigned value = this->_bus->read(valueAddr++);
unsigned value = this->getBus().read(valueAddr++);
this->_registers.p.c = this->_registers.y >= value;
if (this->_registers.p.x_b) {
@@ -243,7 +243,7 @@ namespace ComSquare::CPU
this->_registers.p.z = y == 0;
this->_registers.p.n = y & 0x80u;
} else {
value += this->_bus->read(valueAddr) << 8u;
value += this->getBus().read(valueAddr) << 8u;
uint16_t y = this->_registers.y;
y -= value;
this->_registers.p.z = y == 0;
@@ -255,9 +255,9 @@ namespace ComSquare::CPU
int CPU::AND(uint24_t valueAddr, AddressingMode mode)
{
unsigned negativeMask = this->_registers.p.m ? 0x80u : 0x8000u;
unsigned value = this->_bus->read(valueAddr);
unsigned value = this->getBus().read(valueAddr);
if (!this->_registers.p.m)
value += this->_bus->read(valueAddr + 1) << 8u;
value += this->getBus().read(valueAddr + 1) << 8u;
this->_registers.a &= value;
this->_registers.p.n = this->_registers.a & negativeMask;
@@ -297,15 +297,15 @@ namespace ComSquare::CPU
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 = this->getBus().read(valueAddr);
result += this->getBus().read(valueAddr + 1) << 8u;
result = (uint16_t)(result + 1);
this->_bus->write(valueAddr, result);
this->_bus->write(valueAddr + 1, result << 8u);
this->getBus().write(valueAddr, result);
this->getBus().write(valueAddr + 1, result << 8u);
} else {
result = this->_bus->read(valueAddr);
result = this->getBus().read(valueAddr);
result = (uint8_t)(result + 1);
this->_bus->write(valueAddr, result);
this->getBus().write(valueAddr, result);
}
this->_registers.p.z = result == 0;
@@ -337,15 +337,15 @@ namespace ComSquare::CPU
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 = this->getBus().read(valueAddr);
result += this->getBus().read(valueAddr + 1) << 8u;
result = (uint16_t)(result - 1);
this->_bus->write(valueAddr, result);
this->_bus->write(valueAddr + 1, result << 8u);
this->getBus().write(valueAddr, result);
this->getBus().write(valueAddr + 1, result << 8u);
} else {
result = this->_bus->read(valueAddr);
result = this->getBus().read(valueAddr);
result = (uint8_t)(result - 1);
this->_bus->write(valueAddr, result);
this->getBus().write(valueAddr, result);
}
this->_registers.p.z = result == 0;
@@ -369,9 +369,9 @@ namespace ComSquare::CPU
int CPU::EOR(uint24_t valueAddr, AddressingMode mode)
{
unsigned negativeMask = this->_registers.p.m ? 0x80u : 0x8000u;
unsigned value = this->_bus->read(valueAddr);
unsigned value = this->getBus().read(valueAddr);
if (!this->_registers.p.m)
value += this->_bus->read(valueAddr + 1) << 8u;
value += this->getBus().read(valueAddr + 1) << 8u;
this->_registers.a ^= value;
this->_registers.p.z = this->_registers.a == 0;
this->_registers.p.n = this->_registers.a & negativeMask;

View File

@@ -9,10 +9,10 @@ namespace ComSquare::CPU
int CPU::STA(uint24_t addr, AddressingMode mode)
{
if (this->_registers.p.m)
this->_bus->write(addr, this->_registers.al);
this->getBus().write(addr, this->_registers.al);
else {
this->_bus->write(addr, this->_registers.al);
this->_bus->write(addr + 1, this->_registers.ah);
this->getBus().write(addr, this->_registers.al);
this->getBus().write(addr + 1, this->_registers.ah);
}
int cycles = !this->_registers.p.m;
@@ -34,10 +34,10 @@ namespace ComSquare::CPU
int CPU::STX(uint24_t addr, AddressingMode mode)
{
if (this->_registers.p.x_b)
this->_bus->write(addr, this->_registers.xl);
this->getBus().write(addr, this->_registers.xl);
else {
this->_bus->write(addr, this->_registers.xl);
this->_bus->write(addr + 1, this->_registers.xh);
this->getBus().write(addr, this->_registers.xl);
this->getBus().write(addr + 1, this->_registers.xh);
}
return !this->_registers.p.x_b + (mode != Absolute && this->_registers.dl != 0);
}
@@ -45,19 +45,19 @@ namespace ComSquare::CPU
int CPU::STY(uint24_t addr, AddressingMode mode)
{
if (this->_registers.p.x_b)
this->_bus->write(addr, this->_registers.yl);
this->getBus().write(addr, this->_registers.yl);
else {
this->_bus->write(addr, this->_registers.yl);
this->_bus->write(addr + 1, this->_registers.yh);
this->getBus().write(addr, this->_registers.yl);
this->getBus().write(addr + 1, this->_registers.yh);
}
return !this->_registers.p.x_b + (mode != Absolute && this->_registers.dl != 0);
}
int CPU::STZ(uint24_t addr, AddressingMode mode)
{
this->_bus->write(addr, 0x00);
this->getBus().write(addr, 0x00);
if (!this->_registers.p.m)
this->_bus->write(addr + 1, 0x00);
this->getBus().write(addr + 1, 0x00);
if (mode == Absolute || mode == AbsoluteIndexedByX)
return !this->_registers.p.m;
return !this->_registers.p.m + this->_registers.dl != 0;
@@ -66,11 +66,11 @@ namespace ComSquare::CPU
int CPU::LDA(uint24_t addr, AddressingMode mode)
{
if (this->_registers.p.m) {
this->_registers.a = this->_bus->read(addr);
this->_registers.a = this->getBus().read(addr);
this->_registers.p.n = this->_registers.al & 0xF0u;
} else {
this->_registers.al = this->_bus->read(addr);
this->_registers.ah = this->_bus->read(addr + 1);
this->_registers.al = this->getBus().read(addr);
this->_registers.ah = this->getBus().read(addr + 1);
this->_registers.p.n = this->_registers.a & 0xF000u;
}
this->_registers.p.z = this->_registers.a == 0x0;
@@ -101,11 +101,11 @@ namespace ComSquare::CPU
int CPU::LDX(uint24_t addr, AddressingMode mode)
{
if (this->_registers.p.x_b) {
this->_registers.x = this->_bus->read(addr);
this->_registers.x = this->getBus().read(addr);
this->_registers.p.n = this->_registers.xl & 0xF0u;
} else {
this->_registers.xl = this->_bus->read(addr);
this->_registers.xh = this->_bus->read(addr + 1);
this->_registers.xl = this->getBus().read(addr);
this->_registers.xh = this->getBus().read(addr + 1);
this->_registers.p.n = this->_registers.x & 0xF000u;
}
this->_registers.p.z = this->_registers.x == 0x0;
@@ -128,11 +128,11 @@ namespace ComSquare::CPU
int CPU::LDY(uint24_t addr, AddressingMode mode)
{
if (this->_registers.p.x_b) {
this->_registers.y = this->_bus->read(addr);
this->_registers.y = this->getBus().read(addr);
this->_registers.p.n = this->_registers.yl & 0xF0u;
} else {
this->_registers.yl = this->_bus->read(addr);
this->_registers.yh = this->_bus->read(addr + 1);
this->_registers.yl = this->getBus().read(addr);
this->_registers.yh = this->getBus().read(addr + 1);
this->_registers.p.n = this->_registers.y & 0xF000u;
}
this->_registers.p.z = this->_registers.y == 0x0;

View File

@@ -160,8 +160,8 @@ namespace ComSquare::CPU
this->_registers.dbr = destBank;
while (this->_registers.a != 0xFFFF) {
uint8_t data = this->_bus->read(srcBank << 24u | this->_registers.x);
this->_bus->write(destBank << 24u | this->_registers.y, data);
uint8_t data = this->getBus().read(srcBank << 24u | this->_registers.x);
this->getBus().write(destBank << 24u | this->_registers.y, data);
this->_registers.x++;
this->_registers.y++;
this->_registers.a--;
@@ -177,12 +177,18 @@ namespace ComSquare::CPU
this->_registers.dbr = destBank;
while (this->_registers.a != 0xFFFF) {
uint8_t data = this->_bus->read(srcBank << 24u | this->_registers.x);
this->_bus->write(destBank << 24u | this->_registers.y, data);
uint8_t data = this->getBus().read(srcBank << 24u | this->_registers.x);
this->getBus().write(destBank << 24u | this->_registers.y, data);
this->_registers.x--;
this->_registers.y--;
this->_registers.a--;
}
return 7 * length;
}
std::string CPU::getValueName(uint24_t) const
{
// TODO implement this method
return "???";
}
}

192
sources/CPU/Registers.hpp Normal file
View File

@@ -0,0 +1,192 @@
//
// Created by Zoe Roux on 2021-07-03.
//
#pragma once
#include "Models/Ints.hpp"
namespace ComSquare::CPU
{
//! @brief Struct containing registers for the main CPU.
struct Registers
{
//! @brief The Accumulator
union
{
struct
{
uint8_t al;
uint8_t ah;
};
uint16_t a;
};
//! @brief The Data Bank Register;
uint8_t dbr;
//! @brief The Direct Page register;
union
{
struct
{
uint8_t dl;
uint8_t dh;
};
uint16_t d;
};
union
{
struct
{
//! @brief The Program Counter;
union
{
struct
{
uint8_t pcl;
uint8_t pch;
};
uint16_t pc;
};
//! @brief The Program Bank Register;
uint8_t pbr;
};
//! @brief The current Program Address Counter (does not exist in a snes but is useful here).
uint24_t pac;
};
//! @brief The Stack pointer
union
{
struct
{
uint8_t sl;
uint8_t sh;
};
uint16_t s;
};
//! @brief The X index register
union
{
struct
{
uint8_t xl;
uint8_t xh;
};
uint16_t x;
};
//! @brief The Y index register
union
{
struct
{
uint8_t yl;
uint8_t yh;
};
uint16_t y;
};
//! @brief The Processor status register;
union
{
struct
{
//! @brief The Carry flag
bool c: 1;
//! @brief The Zero flag
bool z: 1;
//! @brief The Interrupt request disable flag
bool i: 1;
//! @brief The Decimal mode flag
bool d: 1;
//! @brief The indeX register width flag (in native mode only) - 0 = 16 bits mode, 1 = 8 bits mode OR the Break flag (in emulation mode only)
bool x_b: 1;
//! @brief The accumulator and Memory width flag (in native mode only) - 0 = 16 bits mode, 1 = 8 bits mode.
bool m: 1;
//! @brief The oVerflow flag
bool v: 1;
//! @brief The Negative flag
bool n: 1;
};
uint8_t flags;
} p;
};
//! @brief Struct containing internal registers of the CPU.
struct InternalRegisters
{
//! @brief Interrupt Enable Register
uint8_t nmitimen;
//! @brief IO Port Write Register
uint8_t wrio;
//! @brief Multiplicand Register A
uint8_t wrmpya;
//! @brief Multiplicand Register B
uint8_t wrmpyb;
//! @brief Divisor & Dividend Registers (A - Low)
uint8_t wrdivl;
//! @brief Divisor & Dividend Registers (A - High)
uint8_t wrdivh;
//! @brief Divisor & Dividend Registers (B)
uint8_t wrdivb;
//! @brief IRQ Timer Registers (Horizontal - Low)
uint8_t htimel;
//! @brief IRQ Timer Registers (Horizontal - High)
uint8_t htimeh;
//! @brief IRQ Timer Registers (Vertical - Low)
uint8_t vtimel;
//! @brief IRQ Timer Registers (Vertical - High)
uint8_t vtimeh;
//! @brief HDMA Enable Register
uint8_t hdmaen;
//! @brief ROM Speed Register
uint8_t memsel;
//! @brief Interrupt Flag Registers
uint8_t rdnmi;
//! @brief Interrupt Flag Registers - TimeUp
uint8_t timeup;
//! @brief PPU Status Register
uint8_t hvbjoy;
//! @brief IO Port Read Register
uint8_t rdio;
//! @brief Divide Result Registers (can sometimes be used as multiplication result register) - LOW
uint8_t rddivl;
//! @brief Divide Result Registers (can sometimes be used as multiplication result register) - HIGH
uint8_t rddivh;
//! @brief Multiplication Result Registers (can sometimes be used as divide result register) - LOW
uint8_t rdmpyl;
//! @brief Multiplication Result Registers (can sometimes be used as divide result register) - HIGH
uint8_t rdmpyh;
//! @brief Controller Port Data Registers (Pad 1 - Low)
uint8_t joy1l;
//! @brief Controller Port Data Registers (Pad 1 - High)
uint8_t joy1h;
//! @brief Controller Port Data Registers (Pad 2 - Low)
uint8_t joy2l;
//! @brief Controller Port Data Registers (Pad 2 - High)
uint8_t joy2h;
//! @brief Controller Port Data Registers (Pad 3 - Low)
uint8_t joy3l;
//! @brief Controller Port Data Registers (Pad 3 - High)
uint8_t joy3h;
//! @brief Controller Port Data Registers (Pad 4 - Low)
uint8_t joy4l;
//! @brief Controller Port Data Registers (Pad 4 - High)
uint8_t joy4h;
};
}

View File

@@ -2,30 +2,37 @@
// Created by anonymus-raccoon on 1/27/20.
//
#include <sys/stat.h>
#include <cstring>
#include "Cartridge.hpp"
#include "../Exceptions/InvalidAddress.hpp"
#include "../Exceptions/InvalidRom.hpp"
#include "../Exceptions/InvalidAction.hpp"
#include "Exceptions/InvalidAction.hpp"
#include "Exceptions/InvalidRom.hpp"
#include <cstring>
#include <sys/stat.h>
#include <fstream>
namespace ComSquare::Cartridge
{
constexpr unsigned HeaderSize = 0x40u;
Cartridge::Cartridge()
: Ram::Ram(0, Rom, "Cartridge")
{}
Cartridge::Cartridge(const std::string &romPath)
: Ram::Ram(0, Rom, "Cartridge"),
_romPath(romPath)
_romPath(romPath)
{
if (romPath.empty())
throw InvalidRomException("Path is empty.");
size_t size = Cartridge::getRomSize(romPath);
FILE *rom = fopen(romPath.c_str(), "rb");
this->loadRom(romPath);
}
void Cartridge::loadRom(const std::string &path)
{
size_t size = Cartridge::getRomSize(path);
std::ifstream rom(path, std::ios::binary);
if (!rom)
throw InvalidRomException("Could not open the rom file at " + romPath + ". " + strerror(errno));
this->_size = size;
this->_data = new uint8_t[size];
std::memset(this->_data, 0, size);
fread(this->_data, 1, size, rom);
throw InvalidRomException("Could not open the rom file at " + path + ". " + strerror(errno));
this->_data.resize(size);
rom.read(reinterpret_cast<char *>(this->_data.data()), size);
this->_loadHeader();
}
@@ -38,7 +45,6 @@ namespace ComSquare::Cartridge
return info.st_size;
}
uint8_t Cartridge::read(uint24_t addr)
{
return Ram::read(addr + this->_romStart);
@@ -59,10 +65,10 @@ namespace ComSquare::Cartridge
Header head;
headerAddress -= 0xC0u;
ADDMAPPINGMODE(head.mappingMode, this->_data[headerAddress + 0xD5u] & 0x10u ? FastRom : SlowRom);
ADDMAPPINGMODE(head.mappingMode, this->_data[headerAddress + 0xD5u] & 0x1u ? HiRom : LoRom);
head.mappingMode |= this->_data[headerAddress + 0xD5u] & 0x10u ? FastRom : SlowRom;
head.mappingMode |= this->_data[headerAddress + 0xD5u] & 0x1u ? HiRom : LoRom;
if (this->_data[headerAddress + 0xD5u] & 0x2u || this->_data[headerAddress + 0xD5u] & 0x4u)
ADDMAPPINGMODE(head.mappingMode, ExRom);
head.mappingMode |= ExRom;
head.romType = this->_data[headerAddress + 0xD6u];
head.romSize = 0x400u << this->_data[headerAddress + 0xD7u];
head.sramSize = 0x400u << this->_data[headerAddress + 0xD8u];
@@ -105,7 +111,7 @@ namespace ComSquare::Cartridge
uint32_t Cartridge::_getHeaderAddress()
{
const std::vector<uint32_t> address = {0x7FC0, 0xFFC0};
unsigned int smc = this->_size % 1024;
unsigned int smc = this->getSize() % 1024;
int bestScore = -1;
uint32_t bestAddress = 0;
@@ -113,7 +119,7 @@ namespace ComSquare::Cartridge
int score = 0;
addr += smc;
if (addr + 0x32u >= this->_size)
if (addr + 0x32u >= this->getSize())
continue;
Header info = this->_mapHeader(addr);
@@ -131,25 +137,25 @@ namespace ComSquare::Cartridge
continue;
uint8_t resetOpCode = this->_data[info.emulationInterrupts.reset - 0x8000u];
switch (resetOpCode) {
case 0x18: //CLI
case 0x78: //SEI
case 0x4C: //JMP
case 0x5C: //JMP
case 0x20: //JSR
case 0x22: //JSL
case 0x9C: //STZ
score+= 8;
case 0x18://CLI
case 0x78://SEI
case 0x4C://JMP
case 0x5C://JMP
case 0x20://JSR
case 0x22://JSL
case 0x9C://STZ
score += 8;
break;
case 0xC2: //REP
case 0xE2: //SEP
case 0xA9: //LDA
case 0xA2: //LDX
case 0xA0: //LDY
case 0xC2://REP
case 0xE2://SEP
case 0xA9://LDA
case 0xA2://LDX
case 0xA0://LDY
score += 4;
break;
case 0x00: //BRK
case 0xFF: //SBC
case 0xCC: //CPY
case 0x00://BRK
case 0xFF://SBC
case 0xCC://CPY
score -= 8;
break;
default:
@@ -166,9 +172,9 @@ namespace ComSquare::Cartridge
bool Cartridge::_isSPCFile()
{
if (this->_size < 0x25)
if (this->getSize() < 0x25)
return false;
std::string str = std::string(reinterpret_cast<char *>(this->_data), 0x21);
std::string str = std::string(reinterpret_cast<char *>(this->getData().data()), 0x21);
if (str != Cartridge::_magicSPC)
return false;
@@ -187,14 +193,16 @@ namespace ComSquare::Cartridge
this->_type = Audio;
return false;
}
uint32_t headerAddress = this->_getHeaderAddress();
this->_type = Game;
uint32_t headerAddress = this->_getHeaderAddress();
if (headerAddress + HeaderSize > this->getSize())
return false;
this->header = this->_mapHeader(headerAddress);
this->header.gameName = std::string(reinterpret_cast<char *>(&this->_data[headerAddress]), 21);
if ((headerAddress + 0x40u) & 0x200u) {
this->_romStart = 0x200u;
this->_size -= 0x200u;
return true;
}
return false;
@@ -204,4 +212,21 @@ namespace ComSquare::Cartridge
{
return this->_type;
}
}
uint24_t Cartridge::getSize() const
{
return Ram::getSize() - this->_romStart;
}
MappingMode operator|(const MappingMode &self, const MappingMode &other)
{
return static_cast<MappingMode>(static_cast<int>(self) | static_cast<int>(other));
}
MappingMode &operator|=(MappingMode &self, const MappingMode &other)
{
int &selfInt = reinterpret_cast<int &>(self);
selfInt |= static_cast<int>(other);
return self;
}
}// namespace ComSquare::Cartridge

View File

@@ -4,36 +4,39 @@
#pragma once
#include <string>
#include <filesystem>
#include "../Memory/AMemory.hpp"
#include "../Models/Int24.hpp"
#include "../Memory/ARectangleMemory.hpp"
#include "InterruptVectors.hpp"
#include "../Ram/Ram.hpp"
#include "Memory/AMemory.hpp"
#include "Memory/ARectangleMemory.hpp"
#include "Models/Ints.hpp"
#include "Ram/Ram.hpp"
#include <filesystem>
#include <string>
namespace ComSquare::Cartridge
{
enum CartridgeType {
enum CartridgeType
{
Game,
Audio
};
#define ADDMAPPINGMODE(x, flag) (x = static_cast<MappingMode>(x | (flag)))
enum MappingMode {
enum MappingMode
{
LoRom = 1u << 0u,
HiRom = 1u << 1u,
SlowRom = 1u << 2u,
FastRom = 1u << 3u,
ExRom = 1u << 4u,
ExRom = 1u << 4u
};
MappingMode operator|(const MappingMode &self, const MappingMode &other);
MappingMode &operator|=(MappingMode &self, const MappingMode &other);
struct Header
{
//! @brief The name of the game
std::string gameName;
//! @brief The memory mapping of the ROM.
MappingMode mappingMode{};
MappingMode mappingMode {};
//! @brief The rom type (special information about the rom, still don't know what).
uint8_t romType = 0;
//! @brief The size (in bytes) of the ram
@@ -41,26 +44,29 @@ namespace ComSquare::Cartridge
//! @brief The size of the SRom inside the cartridge.
unsigned sramSize = 0;
//! @brief Creator license ID code.
union {
union
{
uint8_t creatorIDs[2];
uint16_t creatorID = 0;
};
//! @brief The version of the game
uint8_t version = 0;
//! @brief Checksum complement
union {
union
{
uint8_t checksumComplements[2];
uint16_t checksumComplement = 0;
};
//! @brief Checksum
union {
union
{
uint8_t checksums[2];
uint16_t checksum = 0;
};
//! @brief The interrupt vectors used to halt the CPU in native mode
InterruptVectors nativeInterrupts{};
InterruptVectors nativeInterrupts {};
//! @brief The interrupt vectors used to halt the CPU in emulation mode
InterruptVectors emulationInterrupts{};
InterruptVectors emulationInterrupts {};
Header() = default;
Header(const Header &) = default;
@@ -69,7 +75,8 @@ namespace ComSquare::Cartridge
};
//! @brief Contains the rom's memory/instructions.
class Cartridge : public Ram::Ram {
class Cartridge : public Ram::Ram
{
private:
//! @brief The path of the currently loaded rom.
std::string _romPath;
@@ -93,10 +100,13 @@ namespace ComSquare::Cartridge
//! @return A header struct representing the data at the memory address you passed.
Header _mapHeader(uint32_t headerAddress);
//! @brief Current type of the cartridge
CartridgeType _type;
CartridgeType _type = Game;
//! @brief Magic Header string of a SPC Rom
static constexpr std::string_view _magicSPC = "SNES-SPC700 Sound File Data v0.30";
public:
//! @brief A default constructor that doesn't load anything.
Cartridge();
//! @brief Load a rom from it's path.
explicit Cartridge(const std::string &romPath);
//! @brief The cartridge can't be copied.
@@ -108,13 +118,16 @@ namespace ComSquare::Cartridge
//! @brief The header of the cartridge.
Header header;
//! @brief Return current type of the cartridge
CartridgeType getType();
//! @brief Read from the rom.
//! @param addr The address to read from. The address 0x0 should refer to the first byte of the rom's memory.
//! @throw InvalidAddress will be thrown if the address is more than the size of the rom's memory.
//! @return Return the data at the address.
uint8_t read(uint24_t addr) override;
//! @brief Write data to the rom.
//! @param addr The address to write to. The address 0x0 should refer to the first byte of the rom's memory.
//! @param data The data to write.
@@ -123,6 +136,14 @@ namespace ComSquare::Cartridge
//! @brief The path of the rom file
//! @return The path of the currently loaded rom file.
std::filesystem::path getRomPath() const;
[[nodiscard]] std::filesystem::path getRomPath() const;
//! @brief Get the size of the rom in bytes (without the smc header).
uint24_t getSize() const override;
//! @brief Load the rom at the given path
//! @param rom The path of the rom.
//! @throws InvalidRomException If the rom is invalid, this exception is thrown.
void loadRom(const std::string& path);
};
}
}// namespace ComSquare::Cartridge

View File

@@ -2,43 +2,49 @@
// Created by anonymus-raccoon on 1/31/20.
//
#ifndef COMSQUARE_INTERRUPTVECTORS_HPP
#define COMSQUARE_INTERRUPTVECTORS_HPP
#pragma once
#include <cinttypes>
namespace ComSquare::Cartridge
{
struct InterruptVectors {
struct InterruptVectors
{
//! @brief The Co-Processor enable vector.
union {
union
{
uint8_t cop8[2];
uint16_t cop;
};
//! @brief The Break vector.
union {
union
{
uint8_t brk8[2];
uint16_t brk;
};
//! @brief The Abort vector.
union {
union
{
uint8_t abort8[2];
uint16_t abort;
};
//! @brief The non-maskable interrupt (The V-Blank interrupt).
union {
union
{
uint8_t nmi8[2];
uint16_t nmi;
};
//! @brief The Reset vector (execution of the SNES starts with this reset vector in emulation mode).
union {
union
{
uint8_t reset8[2];
uint16_t reset;
};
//! @brief The Interrupt Request vector.
union {
union
{
uint8_t irq8[2];
uint16_t irq;
};
};
}
#endif //COMSQUARE_INTERRUPTVECTORS_HPP
}// namespace ComSquare::Cartridge

View File

@@ -3,23 +3,19 @@
//
#include "APUDebug.hpp"
#include "../Utility/Utility.hpp"
#include "../Exceptions/InvalidOpcode.hpp"
#include "SNES.hpp"
#include "APU/APU.hpp"
#include "Utility/Utility.hpp"
#include "Exceptions/InvalidOpcode.hpp"
using namespace ComSquare::APU;
namespace ComSquare::Debugger
namespace ComSquare::Debugger::APU
{
APUDebug::APUDebug(APU &apu, SNES &snes) :
APU(apu),
_window(new ClosableWindow<APUDebug>(*this, &APUDebug::disableDebugger)),
_ui(),
_snes(snes)
APUDebug::APUDebug(ComSquare::APU::APU &apu, SNES &snes)
: _window(new ClosableWindow([&snes] { snes.disableAPUDebugging(); })),
_timer(),
_ui(),
_apu(apu)
{
this->_window->setContextMenuPolicy(Qt::NoContextMenu);
this->_window->setAttribute(Qt::WA_QuitOnClose, false);
this->_window->setAttribute(Qt::WA_DeleteOnClose);
this->_ui.setupUi(this->_window);
QMainWindow::connect(this->_ui.resumeButton, &QPushButton::clicked, this, &APUDebug::pause);
QMainWindow::connect(this->_ui.stepButton, &QPushButton::clicked, this, &APUDebug::step);
@@ -30,78 +26,89 @@ namespace ComSquare::Debugger
this->_ui.logger->setShowGrid(false);
this->_window->show();
this->_updatePanel();
this->_timer.setInterval(1000 / 60);
this->_timer.setSingleShot(false);
connect(&_timer, SIGNAL(timeout()), this, SLOT(update()));
this->_timer.start();
this->_apu.isDisabled = true;
}
APUDebug::~APUDebug()
{
this->_apu.isDisabled = false;
}
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.port0hexaLineEdit->setText(Utility::to_hex(this->_apu._registers.port0).c_str());
this->_ui.port0LineEdit->setText(Utility::to_binary(this->_apu._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.port1hexaLineEdit->setText(Utility::to_hex(this->_apu._registers.port1).c_str());
this->_ui.port1LineEdit->setText(Utility::to_binary(this->_apu._registers.port1).c_str());
this->_ui.port2hexaLineEdit->setText(Utility::to_hex(this->_registers.port2).c_str());
this->_ui.port2LineEdit->setText(Utility::to_binary(this->_registers.port2).c_str());
this->_ui.port2hexaLineEdit->setText(Utility::to_hex(this->_apu._registers.port2).c_str());
this->_ui.port2LineEdit->setText(Utility::to_binary(this->_apu._registers.port2).c_str());
this->_ui.port3hexaLineEdit->setText(Utility::to_hex(this->_registers.port3).c_str());
this->_ui.port3LineEdit->setText(Utility::to_binary(this->_registers.port3).c_str());
this->_ui.port3hexaLineEdit->setText(Utility::to_hex(this->_apu._registers.port3).c_str());
this->_ui.port3LineEdit->setText(Utility::to_binary(this->_apu._registers.port3).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.controlhexaLineEdit->setText(Utility::to_hex(this->_apu._registers.ctrlreg).c_str());
this->_ui.controlLineEdit->setText(Utility::to_binary(this->_apu._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.dSPRegAddresshexaLineEdit->setText(Utility::to_hex(this->_apu._registers.dspregAddr).c_str());
this->_ui.dSPRegAddressLineEdit->setText(Utility::to_binary(this->_apu._registers.dspregAddr).c_str());
this->_ui.dSPRegDatahexaLineEdit->setText(Utility::to_hex(this->_dsp.read(this->_registers.dspregAddr)).c_str());
this->_ui.dSPRegDataLineEdit->setText(Utility::to_binary(this->_dsp.read(this->_registers.dspregAddr)).c_str());
this->_ui.dSPRegDatahexaLineEdit->setText(Utility::to_hex(this->_apu._dsp.read(this->_apu._registers.dspregAddr)).c_str());
this->_ui.dSPRegDataLineEdit->setText(Utility::to_binary(this->_apu._dsp.read(this->_apu._registers.dspregAddr)).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.timer0hexaLineEdit->setText(Utility::to_hex(this->_apu._registers.timer0).c_str());
this->_ui.timer0LineEdit->setText(Utility::to_binary(this->_apu._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.timer1hexaLineEdit->setText(Utility::to_hex(this->_apu._registers.timer1).c_str());
this->_ui.timer1LineEdit->setText(Utility::to_binary(this->_apu._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.timer2hexaLineEdit->setText(Utility::to_hex(this->_apu._registers.timer2).c_str());
this->_ui.timer2LineEdit->setText(Utility::to_binary(this->_apu._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.counter0hexaLineEdit->setText(Utility::to_hex(this->_apu._registers.counter0).c_str());
this->_ui.counter0LineEdit->setText(Utility::to_binary(this->_apu._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.counter1hexaLineEdit->setText(Utility::to_hex(this->_apu._registers.counter1).c_str());
this->_ui.counter1LineEdit->setText(Utility::to_binary(this->_apu._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.counter2hexaLineEdit->setText(Utility::to_hex(this->_apu._registers.counter2).c_str());
this->_ui.counter2LineEdit->setText(Utility::to_binary(this->_apu._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->setText(Utility::to_hex(this->_apu._registers.regmem1).c_str());
this->_ui.regMemLineEdit->setText(Utility::to_binary(this->_apu._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.regMemhexaLineEdit_2->setText(Utility::to_hex(this->_apu._registers.regmem2).c_str());
this->_ui.regMemLineEdit_2->setText(Utility::to_binary(this->_apu._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.unknownhexaLineEdit->setText(Utility::to_hex(this->_apu._registers.unknown).c_str());
this->_ui.unknownLineEdit->setText(Utility::to_binary(this->_apu._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.bFlagCheckBox->setChecked(this->_internalRegisters.b);
this->_ui.nFlagCheckBox->setChecked(this->_internalRegisters.n);
this->_ui.pFlagCheckBox->setChecked(this->_internalRegisters.p);
this->_ui.hFlagCheckBox->setChecked(this->_internalRegisters.h);
this->_ui.vFlagCheckBox->setChecked(this->_internalRegisters.v);
this->_ui.iFlagCheckBox->setChecked(this->_internalRegisters.i);
this->_ui.zFlagCheckBox->setChecked(this->_internalRegisters.z);
this->_ui.cFlagCheckBox->setChecked(this->_internalRegisters.c);
this->_ui.stackPointerLineEdit->setText(Utility::to_hex(this->_apu._internalRegisters.sp).c_str());
this->_ui.xIndexLineEdit->setText(Utility::to_hex(this->_apu._internalRegisters.x).c_str());
this->_ui.yIndexLineEdit->setText(Utility::to_hex(this->_apu._internalRegisters.y).c_str());
this->_ui.accumlatorLineEdit->setText(Utility::to_hex(this->_apu._internalRegisters.a).c_str());
this->_ui.programCounterLineEdit->setText(Utility::to_hex(this->_apu._internalRegisters.pc).c_str());
this->_ui.bFlagCheckBox->setChecked(this->_apu._internalRegisters.b);
this->_ui.nFlagCheckBox->setChecked(this->_apu._internalRegisters.n);
this->_ui.pFlagCheckBox->setChecked(this->_apu._internalRegisters.p);
this->_ui.hFlagCheckBox->setChecked(this->_apu._internalRegisters.h);
this->_ui.vFlagCheckBox->setChecked(this->_apu._internalRegisters.v);
this->_ui.iFlagCheckBox->setChecked(this->_apu._internalRegisters.i);
this->_ui.zFlagCheckBox->setChecked(this->_apu._internalRegisters.z);
this->_ui.cFlagCheckBox->setChecked(this->_apu._internalRegisters.c);
auto voices = this->_dsp.getVoices();
auto master = this->_dsp.getMaster();
auto echo = this->_dsp.getEcho();
auto noise = this->_dsp.getNoise();
auto brr = this->_dsp.getBrr();
auto latch = this->_dsp.getLatch();
auto voices = this->_apu._dsp.getVoices();
auto master = this->_apu._dsp.getMaster();
auto echo = this->_apu._dsp.getEcho();
auto noise = this->_apu._dsp.getNoise();
auto brr = this->_apu._dsp.getBrr();
auto latch = this->_apu._dsp.getLatch();
auto max = std::numeric_limits<int8_t>::max();
this->_ui.mvolLprogressBar->setValue(master.volume[0] * 100 / max);
@@ -256,20 +263,19 @@ namespace ComSquare::Debugger
QStringList labels = QStringList();
uint16_t offset = 0;
if (this->_pc != 0)
if (this->_apu._internalRegisters.pc != 0)
{
auto pc = this->_internalRegisters.pc;
auto pc = this->_apu._internalRegisters.pc;
this->_internalRegisters.pc = this->_pc;
this->_appendInstruction(0);
labels.append(Utility::to_hex(this->_pc).c_str());
this->_internalRegisters.pc = pc;
labels.append(Utility::to_hex(pc).c_str());
this->_apu._internalRegisters.pc = pc;
}
else
labels.append("$0000");
for (uint16_t i = 1; i < 0x20; i++)
{
auto pc = this->_internalRegisters.pc;
auto pc = this->_apu._internalRegisters.pc;
offset += this->_appendInstruction(i);
labels.append(Utility::to_hex(pc).c_str());
@@ -277,7 +283,7 @@ namespace ComSquare::Debugger
this->_ui.logger->setVerticalHeaderLabels(labels);
for (int i = 0; i < 3; i++)
this->_ui.logger->item(1, i)->setData(Qt::BackgroundRole, QColor(200, 255, 148));
this->_internalRegisters.pc -= offset;
this->_apu._internalRegisters.pc -= offset;
}
int APUDebug::_appendInstruction(int row)
@@ -299,85 +305,105 @@ namespace ComSquare::Debugger
return instruction.size;
}
std::string APUDebug::_getOperand(Operand ope)
std::string APUDebug::_getOperand(Operand ope) const
{
uint16_t pc = this->_apu._internalRegisters.pc;
std::string ret = "UNKNOWN";
switch (ope) {
case None:
return "";
case A:
return Utility::to_hex(this->_internalRegisters.a);
case X:
return Utility::to_hex(this->_internalRegisters.x);
case Y:
return Utility::to_hex(this->_internalRegisters.y);
case SP:
return Utility::to_hex(this->_internalRegisters.sp);
case PSW:
return Utility::to_hex(this->_internalRegisters.psw);
case ImmediateData:
return Utility::to_hex(this->_getImmediateData());
case IndexXAddr:
return Utility::to_hex(this->_getIndexXAddr());
case IndexYAddr:
return Utility::to_hex(this->_getIndexYAddr());
case AbsoluteAddr:
return Utility::to_hex(this->_getAbsoluteAddr());
case AbsoluteBit: {
auto pair = this->_getAbsoluteBit();
return Utility::to_hex(std::get<0>(pair)) + Utility::to_hex(std::get<1>(pair));
}
case AbsoluteAddrByX:
return Utility::to_hex(this->_getAbsoluteAddrByX());
case AbsoluteAddrByY:
return Utility::to_hex(this->_getAbsoluteAddrByY());
case AbsoluteByXAddr:
return Utility::to_hex(this->_getAbsoluteByXAddr());
case AbsoluteDirectByXAddr:
return Utility::to_hex(this->_getAbsoluteDirectByXAddr());
case AbsoluteDirectAddrByY:
return Utility::to_hex(this->_getAbsoluteDirectAddrByY());
case DirectAddr:
return Utility::to_hex(this->_getDirectAddr());
case DirectAddrByX:
return Utility::to_hex(this->_getDirectAddrByX());
case DirectAddrByY:
return Utility::to_hex(this->_getDirectAddrByY());
case None:
return "";
case A:
ret = Utility::to_hex(this->_apu._internalRegisters.a);
break;
case X:
ret = Utility::to_hex(this->_apu._internalRegisters.x);
break;
case Y:
ret = Utility::to_hex(this->_apu._internalRegisters.y);
break;
case SP:
ret = Utility::to_hex(this->_apu._internalRegisters.sp);
break;
case PSW:
ret = Utility::to_hex(this->_apu._internalRegisters.psw);
break;
case ImmediateData:
ret = Utility::to_hex(this->_apu._getImmediateData());
break;
case IndexXAddr:
ret = Utility::to_hex(this->_apu._getIndexXAddr());
break;
case IndexYAddr:
ret = Utility::to_hex(this->_apu._getIndexYAddr());
break;
case AbsoluteAddr:
ret = Utility::to_hex(this->_apu._getAbsoluteAddr());
break;
case AbsoluteBit: {
auto pair = this->_apu._getAbsoluteBit();
ret = Utility::to_hex(std::get<0>(pair)) + Utility::to_hex(std::get<1>(pair));
break;
}
return "UNKNOWN";
case AbsoluteAddrByX:
ret = Utility::to_hex(this->_apu._getAbsoluteAddrByX());
break;
case AbsoluteAddrByY:
ret = Utility::to_hex(this->_apu._getAbsoluteAddrByY());
break;
case AbsoluteByXAddr:
ret = Utility::to_hex(this->_apu._getAbsoluteByXAddr());
break;
case AbsoluteDirectByXAddr:
ret = Utility::to_hex(this->_apu._getAbsoluteDirectByXAddr());
break;
case AbsoluteDirectAddrByY:
ret = Utility::to_hex(this->_apu._getAbsoluteDirectAddrByY());
break;
case DirectAddr:
ret = Utility::to_hex(this->_apu._getDirectAddr());
break;
case DirectAddrByX:
ret = Utility::to_hex(this->_apu._getDirectAddrByX());
break;
case DirectAddrByY:
ret = Utility::to_hex(this->_apu._getDirectAddrByY());
break;
}
this->_apu._internalRegisters.pc = pc;
return ret;
}
Instruction &APUDebug::_getInstruction()
const Instruction &APUDebug::_getInstruction() const
{
uint8_t opcode = this->_getImmediateData();
uint8_t opcode = this->_apu._internalRead(this->_apu._internalRegisters.pc);
return this->_instructions[opcode];
}
int APUDebug::_executeInstruction()
void APUDebug::update()
{
int cycles = 0;
if (this->_isPaused)
return 0xFF;
if (this->_isStepping) {
this->_isStepping = false;
this->_isPaused = true;
}
cycles = APU::_executeInstruction();
this->_updatePanel();
return cycles;
}
void APUDebug::update(unsigned cycles)
{
this->_pc = this->_internalRegisters.pc;
try {
if (this->_isPaused)
if (this->_isPaused) {
this->_apu._dsp.update();
return;
APU::update(cycles);
} catch (InvalidOpcode &e) {
}
for (int i = 0; i < 0xFF; i++) {
this->_apu._executeInstruction();
this->_updatePanel();
this->_updateLogger();
if (this->_isStepping) {
this->_isStepping = false;
this->pause();
return;
}
}
this->_apu._dsp.update();
} catch (const InvalidOpcode &e) {
this->pause();
//this->_ui.logger->append(e.what());
} catch (const std::exception &e) {
std::cerr << "An error occurred: " << e.what() << std::endl;
QApplication::quit();
}
}
@@ -396,17 +422,6 @@ namespace ComSquare::Debugger
this->_ui.resumeButton->setText("Pause");
}
void APUDebug::disableDebugger()
{
this->_snes.disableAPUDebugging();
}
bool APUDebug::isDebugger() const
{
return true;
}
void APUDebug::focus()
{
this->_window->activateWindow();

View File

@@ -2,368 +2,366 @@
// Created by Melefo on 19/02/2020.
//
#ifndef COMSQUARE_APUDEBUG_HPP
#define COMSQUARE_APUDEBUG_HPP
#pragma once
#include "../APU/APU.hpp"
#include "../SNES.hpp"
#include "../../ui/ui_apuView.h"
#include "ClosableWindow.hpp"
#include "ui/ui_apuView.h"
#include <QTimer>
namespace ComSquare::Debugger
namespace ComSquare
{
//! @brief List of all types of operands used by the instructions
enum Operand
class SNES;
namespace APU
{
None,
A,
X,
Y,
SP,
PSW,
ImmediateData,
IndexXAddr,
IndexYAddr,
AbsoluteBit,
AbsoluteAddr,
AbsoluteAddrByX,
AbsoluteAddrByY,
AbsoluteByXAddr,
AbsoluteDirectByXAddr,
AbsoluteDirectAddrByY,
DirectAddr,
DirectAddrByX,
DirectAddrByY
};
class APU;
}
//! @brief Small structure to store some values on the instructions
struct Instruction
namespace Debugger::APU
{
std::string name;
int size;
std::tuple<Operand, Operand> operands;
};
//! @brief List of all types of operands used by the instructions
enum Operand
{
None,
A,
X,
Y,
SP,
PSW,
ImmediateData,
IndexXAddr,
IndexYAddr,
AbsoluteBit,
AbsoluteAddr,
AbsoluteAddrByX,
AbsoluteAddrByY,
AbsoluteByXAddr,
AbsoluteDirectByXAddr,
AbsoluteDirectAddrByY,
DirectAddr,
DirectAddrByX,
DirectAddrByY
};
class APUDebug : public APU::APU, public QObject
{
private:
//! @brief List of instructions and their information
std::array<Instruction, 0x100> _instructions {{
{"NOP", 1, {None, None}},
{"TCALL", 1, {None, None}},
{"SET1", 2, {DirectAddr, None}},
{"BBS", 3, {DirectAddr, ImmediateData}},
{"OR", 2, {DirectAddr, None}},
{"OR", 3, {AbsoluteAddr, None}},
{"OR", 1, {IndexXAddr, None}},
{"OR", 2, {AbsoluteDirectByXAddr, None}},
{"OR", 2, {ImmediateData, None}},
{"OR", 3, {DirectAddr, DirectAddr}},
{"OR1", 3, {AbsoluteBit, None}},
{"ASL", 2, {DirectAddr, None}},
{"ASL", 3, {AbsoluteAddr, None}},
{"PUSH", 1, {PSW, None}},
{"TSET1", 3, {AbsoluteAddr, None}},
{"BRK", 1, {None, None}},
{"BPL", 2, {ImmediateData, None}},
{"TCALL", 1, {None, None}},
{"CLR1", 2, {DirectAddr, None}},
{"BBC", 3, {DirectAddr, ImmediateData}},
{"OR", 2, {DirectAddrByX, None}},
{"OR", 3, {AbsoluteAddrByX, None}},
{"OR", 3, {AbsoluteAddrByY, None}},
{"OR", 2, {AbsoluteDirectAddrByY, None}},
{"OR", 3, {DirectAddr, ImmediateData}},
{"OR", 1, {IndexYAddr, IndexYAddr}},
{"DECW", 2, {DirectAddr, None}},
{"ASL", 2, {DirectAddrByX, None}},
{"ASL", 1, {A, None}},
{"DEC", 1, {X, None}},
{"CMP", 3, {X, AbsoluteAddr}},
{"JMP", 3, {AbsoluteByXAddr, None}},
{"CLRP", 1, {None, None}},
{"TCALL", 1, {None, None}},
{"SET1", 2, {DirectAddr, None}},
{"BBS", 3, {DirectAddr, ImmediateData}},
{"AND", 2, {DirectAddr, None}},
{"AND", 3, {AbsoluteAddr, None}},
{"AND", 1, {IndexXAddr, None}},
{"AND", 2, {AbsoluteDirectByXAddr, None}},
{"AND", 2, {ImmediateData, None}},
{"AND", 3, {DirectAddr, DirectAddr}},
{"OR1", 3, {AbsoluteBit, None}},
{"ROL", 2, {DirectAddr, None}},
{"ROL", 3, {AbsoluteAddr, None}},
{"PUSH", 1, {A, None}},
{"CBNE", 3, {DirectAddrByX, ImmediateData}},
{"BRA", 2, {ImmediateData, None}},
{"BMI", 2, {ImmediateData, None}},
{"TCALL", 1, {None, None}},
{"CLR1", 2, {DirectAddr, None}},
{"BBC", 3, {DirectAddr, ImmediateData}},
{"AND", 2, {DirectAddrByX, None}},
{"AND", 3, {AbsoluteAddrByX, None}},
{"AND", 3, {AbsoluteAddrByY, None}},
{"AND", 2, {AbsoluteDirectAddrByY, None}},
{"AND", 3, {DirectAddr, ImmediateData}},
{"AND", 1, {IndexXAddr, IndexYAddr}},
{"INCW", 2, {DirectAddr, None}},
{"ROL", 2, {AbsoluteAddrByX, None}},
{"ROL", 1, {A, None}},
{"INC", 1, {X, None}},
{"CMP", 2, {X, DirectAddr}},
{"CALL", 3, {AbsoluteAddr, None}},
{"SETP", 1, {None, None}},
{"TCALL", 1, {None, None}},
{"SET1", 2, {DirectAddr, None}},
{"BBS", 3, {DirectAddr, ImmediateData}},
{"EOR", 2, {DirectAddr, None}},
{"EOR", 3, {AbsoluteAddr, None}},
{"EOR", 1, {IndexXAddr, None}},
{"EOR", 2, {AbsoluteDirectByXAddr, None}},
{"EOR", 2, {ImmediateData, None}},
{"EOR", 3, {DirectAddr, DirectAddr}},
{"AND1", 3, {AbsoluteBit, None}},
{"LSR", 2, {DirectAddr, None}},
{"LSR", 3, {AbsoluteAddr, None}},
{"PUSH", 1, {X, None}},
{"TCLR1", 3, {AbsoluteAddr, None}},
{"PCALL", 3, {None, None}},
{"BVC", 2, {ImmediateData, None}},
{"TCALL", 1, {None, None}},
{"CLR1", 2, {DirectAddr, None}},
{"BBC", 3, {DirectAddr, ImmediateData}},
{"EOR", 2, {DirectAddrByX, None}},
{"EOR", 3, {AbsoluteAddrByX, None}},
{"EOR", 3, {AbsoluteAddrByY, None}},
{"EOR", 2, {AbsoluteDirectAddrByY, None}},
{"EOR", 3, {DirectAddr, ImmediateData}},
{"EOR", 1, {IndexXAddr, IndexYAddr}},
{"CMPW", 2, {DirectAddr, None}},
{"LSR", 2, {DirectAddrByX, None}},
{"LSR", 1, {A, None}},
{"MOV", 1, {A, X}},
{"CMP", 3, {Y, AbsoluteAddr}},
{"JMP", 3, {AbsoluteAddr, None}},
{"CLRC", 1, {None, None}},
{"TCALL", 1, {None, None}},
{"SET1", 2, {DirectAddr, None}},
{"BBS", 3, {DirectAddr, ImmediateData}},
{"CMP", 2, {A, DirectAddr}},
{"CMP", 3, {A, AbsoluteAddr}},
{"CMP", 1, {A, IndexXAddr,}},
{"CMP", 2, {A, AbsoluteDirectByXAddr}},
{"CMP", 2, {A, ImmediateData}},
{"CMP", 3, {DirectAddr, DirectAddr}},
{"AND1", 3, {AbsoluteBit, None}},
{"ROR", 2, {DirectAddr , None}},
{"ROR", 3, {AbsoluteAddr, None}},
{"PUSH", 1, {Y, None}},
{"DBNZ", 3, {ImmediateData, None}},
{"RET", 1, {None, None}},
{"BVS", 2, {ImmediateData, None}},
{"TCALL", 1, {None, None}},
{"CLR1", 2, {DirectAddr, None}},
{"BBC", 3, {DirectAddr, ImmediateData}},
{"CMP", 2, {A, DirectAddrByX}},
{"CMP", 3, {A, AbsoluteAddrByX}},
{"CMP", 3, {A, AbsoluteAddrByY}},
{"CMP", 2, {A, AbsoluteDirectAddrByY}},
{"CMP", 3, {DirectAddr, ImmediateData}},
{"CMP", 1, {IndexXAddr, IndexYAddr}},
{"ADDW", 2, {DirectAddr, None}},
{"ROR", 2, {DirectAddrByX, None}},
{"ROR", 1, {A, None}},
{"MOV", 1, {X, A}},
{"CMP", 3, {Y, DirectAddr}},
{"RETI", 1, {None, None}},
{"SETC", 1, {None, None}},
{"TCALL", 1, {None, None}},
{"SET1", 2, {DirectAddr, None}},
{"BBS", 3, {DirectAddr, ImmediateData}},
{"ADC", 2, {DirectAddr, None}},
{"ADC", 3, {AbsoluteAddr, None}},
{"ADC", 1, {IndexXAddr, None}},
{"ADC", 2, {AbsoluteDirectByXAddr, None}},
{"ADC", 2, {ImmediateData, None}},
{"ADC", 3, {DirectAddr, DirectAddr}},
{"EOR1", 3, {AbsoluteBit, None}},
{"DEC", 2, {DirectAddr, None}},
{"DEC", 3, {AbsoluteAddr, None}},
{"MOV", 2, {ImmediateData, Y}},
{"POP", 1, {PSW, None}},
{"MOV", 3, {DirectAddr, ImmediateData}},
{"BCC", 2, {ImmediateData, None}},
{"TCALL", 1, {None, None}},
{"CLR1", 2, {DirectAddr, None}},
{"BBC", 3, {DirectAddr, ImmediateData}},
{"ADC", 2, {DirectAddrByX, None}},
{"ADC", 3, {AbsoluteAddrByX, None}},
{"ADC", 3, {AbsoluteAddrByY, None}},
{"ADC", 2, {AbsoluteDirectAddrByY, None}},
{"ADC", 3, {DirectAddr, ImmediateData}},
{"ADC", 1, {IndexXAddr, IndexYAddr}},
{"SUBW", 2, {DirectAddr, None}},
{"DEC", 2, {DirectAddrByX, None}},
{"DEC", 1, {A, None}},
{"MOV", 1, {SP, X}},
{"DIV", 1, {None, None}},
{"XCN", 1, {None, None}},
{"EI", 1, {None, None}},
{"TCALL", 1, {None, None}},
{"SET1", 2, {DirectAddr, None}},
{"BBS", 3, {DirectAddr, ImmediateData}},
{"SBC", 2, {DirectAddr, None}},
{"SBC", 3, {AbsoluteAddr, None}},
{"SBC", 1, {IndexXAddr, None}},
{"SBC", 2, {AbsoluteDirectByXAddr, None}},
{"SBC", 2, {ImmediateData, None}},
{"SBC", 3, {DirectAddr, DirectAddr}},
{"MOV1", 3, {AbsoluteBit, None}},
{"INC", 2, {DirectAddr, None}},
{"INC", 3, {AbsoluteAddr, None}},
{"CMP", 2, {Y, ImmediateData}},
{"POP", 1, {A, None}},
{"MOV", 1, {A, IndexXAddr}},
{"BCS", 2, {ImmediateData, None}},
{"TCALL", 1, {None, None}},
{"CLR1", 2, {DirectAddr, None}},
{"BBC", 3, {DirectAddr, ImmediateData}},
{"SBC", 2, {DirectAddrByX, None}},
{"SBC", 3, {AbsoluteAddrByX, None}},
{"SBC", 3, {AbsoluteAddrByY, None}},
{"SBC", 2, {AbsoluteDirectAddrByY, None}},
{"SBC", 2, {DirectAddr, ImmediateData}},
{"SBC", 1, {IndexXAddr, IndexYAddr}},
{"MOVW", 2, {DirectAddr, None}},
{"INC", 2, {DirectAddrByX, None}},
{"INC", 1, {A, None}},
{"MOV", 1, {X, SP}},
{"DAS", 1, {None, None}},
{"MOV", 1, {IndexXAddr, A}},
{"DI", 1, {None, None}},
{"TCALL", 1, {None, None}},
{"SET1", 2, {DirectAddr, None}},
{"BBS", 3, {DirectAddr, ImmediateData}},
{"MOV", 2, {A, DirectAddr}},
{"MOV", 3, {A, AbsoluteAddr}},
{"MOV", 1, {A, IndexXAddr}},
{"MOV", 2, {A, AbsoluteDirectByXAddr}},
{"CMP", 2, {X, ImmediateData}},
{"MOV", 3, {X, AbsoluteAddr}},
{"MOV1", 3, {AbsoluteBit, None}},
{"MOV", 2, {Y, DirectAddr}},
{"MOV", 3, {Y, AbsoluteAddr}},
{"MOV", 2, {ImmediateData, X}},
{"POP", 1, {X, None}},
{"MUL", 1, {None, None}},
{"BNE", 2, {ImmediateData, None}},
{"TCALL", 1, {None, None}},
{"CLR1", 2, {DirectAddr, None}},
{"BBC", 3, {DirectAddr, ImmediateData}},
{"MOV", 2, {A, DirectAddrByX}},
{"MOV", 3, {A, AbsoluteAddrByX}},
{"MOV", 3, {A, AbsoluteAddrByY}},
{"MOV", 2, {A, AbsoluteDirectAddrByY}},
{"MOV", 2, {X, DirectAddr}},
{"MOV", 2, {X, DirectAddrByY}},
{"MOVW", 2, {DirectAddr, None}},
{"MOV", 2, {Y, DirectAddrByX}},
{"DEC", 1, {Y, None}},
{"MOV", 1, {Y, A}},
{"CBNE", 3, {DirectAddrByX, ImmediateData}},
{"DAA", 1, {None, None}},
{"CLRV", 1, {None, None}},
{"TCALL", 1, {None, None}},
{"SET1", 2, {DirectAddr, None}},
{"BBS", 3, {DirectAddr, ImmediateData}},
{"MOV", 2, {DirectAddr, A}},
{"MOV", 3, {AbsoluteAddrByX, A}},
{"MOV", 1, {IndexXAddr, A}},
{"MOV", 2, {AbsoluteDirectByXAddr, A}},
{"MOV", 2, {ImmediateData, A}},
{"MOV", 3, {AbsoluteAddr, X}},
{"NOT1", 3, {AbsoluteBit, None}},
{"MOV", 2, {DirectAddr, Y}},
{"MOV", 3, {AbsoluteAddr, Y}},
{"NOTC", 1, {None, None}},
{"POP", 1, {Y, None}},
{"SLEEP", 1, {None, None}},
{"BEQ", 2, {ImmediateData, None}},
{"TCALL", 1, {None, None}},
{"CLR1", 2, {DirectAddr, None}},
{"BBC", 3, {DirectAddr, ImmediateData}},
{"MOV", 2, {DirectAddrByX, A}},
{"MOV", 3, {AbsoluteAddrByX, A}},
{"MOV", 3, {AbsoluteAddrByY, A}},
{"MOV", 2, {AbsoluteDirectAddrByY, A}},
{"MOV", 2, {DirectAddr, X}},
{"MOV", 2, {DirectAddrByY, X}},
{"MOV", 3, {DirectAddr, DirectAddr}},
{"MOV", 2, {DirectAddrByX, Y}},
{"INC", 1, {Y, None}},
{"MOV", 1, {A, Y}},
{"DBNZ", 3, {ImmediateData, None}},
{"STOP", 1, {None, None}}
}};
//! @brief Small structure to store some values on the instructions
struct Instruction
{
std::string name;
int size;
std::tuple<Operand, Operand> operands;
};
//! @brief Position of the last instruction executed
uint16_t _pc;
class APUDebug : public QObject
{
Q_OBJECT
private:
//! @brief List of instructions and their information
const std::array<Instruction, 0x100> _instructions {{
{"NOP", 1, {None, None}},
{"TCALL", 1, {None, None}},
{"SET1", 2, {DirectAddr, None}},
{"BBS", 3, {DirectAddr, ImmediateData}},
{"OR", 2, {DirectAddr, None}},
{"OR", 3, {AbsoluteAddr, None}},
{"OR", 1, {IndexXAddr, None}},
{"OR", 2, {AbsoluteDirectByXAddr, None}},
{"OR", 2, {ImmediateData, None}},
{"OR", 3, {DirectAddr, DirectAddr}},
{"OR1", 3, {AbsoluteBit, None}},
{"ASL", 2, {DirectAddr, None}},
{"ASL", 3, {AbsoluteAddr, None}},
{"PUSH", 1, {PSW, None}},
{"TSET1", 3, {AbsoluteAddr, None}},
{"BRK", 1, {None, None}},
{"BPL", 2, {ImmediateData, None}},
{"TCALL", 1, {None, None}},
{"CLR1", 2, {DirectAddr, None}},
{"BBC", 3, {DirectAddr, ImmediateData}},
{"OR", 2, {DirectAddrByX, None}},
{"OR", 3, {AbsoluteAddrByX, None}},
{"OR", 3, {AbsoluteAddrByY, None}},
{"OR", 2, {AbsoluteDirectAddrByY, None}},
{"OR", 3, {DirectAddr, ImmediateData}},
{"OR", 1, {IndexYAddr, IndexYAddr}},
{"DECW", 2, {DirectAddr, None}},
{"ASL", 2, {DirectAddrByX, None}},
{"ASL", 1, {A, None}},
{"DEC", 1, {X, None}},
{"CMP", 3, {X, AbsoluteAddr}},
{"JMP", 3, {AbsoluteByXAddr, None}},
{"CLRP", 1, {None, None}},
{"TCALL", 1, {None, None}},
{"SET1", 2, {DirectAddr, None}},
{"BBS", 3, {DirectAddr, ImmediateData}},
{"AND", 2, {DirectAddr, None}},
{"AND", 3, {AbsoluteAddr, None}},
{"AND", 1, {IndexXAddr, None}},
{"AND", 2, {AbsoluteDirectByXAddr, None}},
{"AND", 2, {ImmediateData, None}},
{"AND", 3, {DirectAddr, DirectAddr}},
{"OR1", 3, {AbsoluteBit, None}},
{"ROL", 2, {DirectAddr, None}},
{"ROL", 3, {AbsoluteAddr, None}},
{"PUSH", 1, {A, None}},
{"CBNE", 3, {DirectAddrByX, ImmediateData}},
{"BRA", 2, {ImmediateData, None}},
{"BMI", 2, {ImmediateData, None}},
{"TCALL", 1, {None, None}},
{"CLR1", 2, {DirectAddr, None}},
{"BBC", 3, {DirectAddr, ImmediateData}},
{"AND", 2, {DirectAddrByX, None}},
{"AND", 3, {AbsoluteAddrByX, None}},
{"AND", 3, {AbsoluteAddrByY, None}},
{"AND", 2, {AbsoluteDirectAddrByY, None}},
{"AND", 3, {DirectAddr, ImmediateData}},
{"AND", 1, {IndexXAddr, IndexYAddr}},
{"INCW", 2, {DirectAddr, None}},
{"ROL", 2, {AbsoluteAddrByX, None}},
{"ROL", 1, {A, None}},
{"INC", 1, {X, None}},
{"CMP", 2, {X, DirectAddr}},
{"CALL", 3, {AbsoluteAddr, None}},
{"SETP", 1, {None, None}},
{"TCALL", 1, {None, None}},
{"SET1", 2, {DirectAddr, None}},
{"BBS", 3, {DirectAddr, ImmediateData}},
{"EOR", 2, {DirectAddr, None}},
{"EOR", 3, {AbsoluteAddr, None}},
{"EOR", 1, {IndexXAddr, None}},
{"EOR", 2, {AbsoluteDirectByXAddr, None}},
{"EOR", 2, {ImmediateData, None}},
{"EOR", 3, {DirectAddr, DirectAddr}},
{"AND1", 3, {AbsoluteBit, None}},
{"LSR", 2, {DirectAddr, None}},
{"LSR", 3, {AbsoluteAddr, None}},
{"PUSH", 1, {X, None}},
{"TCLR1", 3, {AbsoluteAddr, None}},
{"PCALL", 3, {None, None}},
{"BVC", 2, {ImmediateData, None}},
{"TCALL", 1, {None, None}},
{"CLR1", 2, {DirectAddr, None}},
{"BBC", 3, {DirectAddr, ImmediateData}},
{"EOR", 2, {DirectAddrByX, None}},
{"EOR", 3, {AbsoluteAddrByX, None}},
{"EOR", 3, {AbsoluteAddrByY, None}},
{"EOR", 2, {AbsoluteDirectAddrByY, None}},
{"EOR", 3, {DirectAddr, ImmediateData}},
{"EOR", 1, {IndexXAddr, IndexYAddr}},
{"CMPW", 2, {DirectAddr, None}},
{"LSR", 2, {DirectAddrByX, None}},
{"LSR", 1, {A, None}},
{"MOV", 1, {A, X}},
{"CMP", 3, {Y, AbsoluteAddr}},
{"JMP", 3, {AbsoluteAddr, None}},
{"CLRC", 1, {None, None}},
{"TCALL", 1, {None, None}},
{"SET1", 2, {DirectAddr, None}},
{"BBS", 3, {DirectAddr, ImmediateData}},
{"CMP", 2, {A, DirectAddr}},
{"CMP", 3, {A, AbsoluteAddr}},
{"CMP", 1, {A, IndexXAddr,}},
{"CMP", 2, {A, AbsoluteDirectByXAddr}},
{"CMP", 2, {A, ImmediateData}},
{"CMP", 3, {DirectAddr, DirectAddr}},
{"AND1", 3, {AbsoluteBit, None}},
{"ROR", 2, {DirectAddr, None}},
{"ROR", 3, {AbsoluteAddr, None}},
{"PUSH", 1, {Y, None}},
{"DBNZ", 3, {ImmediateData, None}},
{"RET", 1, {None, None}},
{"BVS", 2, {ImmediateData, None}},
{"TCALL", 1, {None, None}},
{"CLR1", 2, {DirectAddr, None}},
{"BBC", 3, {DirectAddr, ImmediateData}},
{"CMP", 2, {A, DirectAddrByX}},
{"CMP", 3, {A, AbsoluteAddrByX}},
{"CMP", 3, {A, AbsoluteAddrByY}},
{"CMP", 2, {A, AbsoluteDirectAddrByY}},
{"CMP", 3, {DirectAddr, ImmediateData}},
{"CMP", 1, {IndexXAddr, IndexYAddr}},
{"ADDW", 2, {DirectAddr, None}},
{"ROR", 2, {DirectAddrByX, None}},
{"ROR", 1, {A, None}},
{"MOV", 1, {X, A}},
{"CMP", 3, {Y, DirectAddr}},
{"RETI", 1, {None, None}},
{"SETC", 1, {None, None}},
{"TCALL", 1, {None, None}},
{"SET1", 2, {DirectAddr, None}},
{"BBS", 3, {DirectAddr, ImmediateData}},
{"ADC", 2, {DirectAddr, None}},
{"ADC", 3, {AbsoluteAddr, None}},
{"ADC", 1, {IndexXAddr, None}},
{"ADC", 2, {AbsoluteDirectByXAddr, None}},
{"ADC", 2, {ImmediateData, None}},
{"ADC", 3, {DirectAddr, DirectAddr}},
{"EOR1", 3, {AbsoluteBit, None}},
{"DEC", 2, {DirectAddr, None}},
{"DEC", 3, {AbsoluteAddr, None}},
{"MOV", 2, {ImmediateData, Y}},
{"POP", 1, {PSW, None}},
{"MOV", 3, {DirectAddr, ImmediateData}},
{"BCC", 2, {ImmediateData, None}},
{"TCALL", 1, {None, None}},
{"CLR1", 2, {DirectAddr, None}},
{"BBC", 3, {DirectAddr, ImmediateData}},
{"ADC", 2, {DirectAddrByX, None}},
{"ADC", 3, {AbsoluteAddrByX, None}},
{"ADC", 3, {AbsoluteAddrByY, None}},
{"ADC", 2, {AbsoluteDirectAddrByY, None}},
{"ADC", 3, {DirectAddr, ImmediateData}},
{"ADC", 1, {IndexXAddr, IndexYAddr}},
{"SUBW", 2, {DirectAddr, None}},
{"DEC", 2, {DirectAddrByX, None}},
{"DEC", 1, {A, None}},
{"MOV", 1, {SP, X}},
{"DIV", 1, {None, None}},
{"XCN", 1, {None, None}},
{"EI", 1, {None, None}},
{"TCALL", 1, {None, None}},
{"SET1", 2, {DirectAddr, None}},
{"BBS", 3, {DirectAddr, ImmediateData}},
{"SBC", 2, {DirectAddr, None}},
{"SBC", 3, {AbsoluteAddr, None}},
{"SBC", 1, {IndexXAddr, None}},
{"SBC", 2, {AbsoluteDirectByXAddr, None}},
{"SBC", 2, {ImmediateData, None}},
{"SBC", 3, {DirectAddr, DirectAddr}},
{"MOV1", 3, {AbsoluteBit, None}},
{"INC", 2, {DirectAddr, None}},
{"INC", 3, {AbsoluteAddr, None}},
{"CMP", 2, {Y, ImmediateData}},
{"POP", 1, {A, None}},
{"MOV", 1, {A, IndexXAddr}},
{"BCS", 2, {ImmediateData, None}},
{"TCALL", 1, {None, None}},
{"CLR1", 2, {DirectAddr, None}},
{"BBC", 3, {DirectAddr, ImmediateData}},
{"SBC", 2, {DirectAddrByX, None}},
{"SBC", 3, {AbsoluteAddrByX, None}},
{"SBC", 3, {AbsoluteAddrByY, None}},
{"SBC", 2, {AbsoluteDirectAddrByY, None}},
{"SBC", 2, {DirectAddr, ImmediateData}},
{"SBC", 1, {IndexXAddr, IndexYAddr}},
{"MOVW", 2, {DirectAddr, None}},
{"INC", 2, {DirectAddrByX, None}},
{"INC", 1, {A, None}},
{"MOV", 1, {X, SP}},
{"DAS", 1, {None, None}},
{"MOV", 1, {IndexXAddr, A}},
{"DI", 1, {None, None}},
{"TCALL", 1, {None, None}},
{"SET1", 2, {DirectAddr, None}},
{"BBS", 3, {DirectAddr, ImmediateData}},
{"MOV", 2, {A, DirectAddr}},
{"MOV", 3, {A, AbsoluteAddr}},
{"MOV", 1, {A, IndexXAddr}},
{"MOV", 2, {A, AbsoluteDirectByXAddr}},
{"CMP", 2, {X, ImmediateData}},
{"MOV", 3, {X, AbsoluteAddr}},
{"MOV1", 3, {AbsoluteBit, None}},
{"MOV", 2, {Y, DirectAddr}},
{"MOV", 3, {Y, AbsoluteAddr}},
{"MOV", 2, {ImmediateData, X}},
{"POP", 1, {X, None}},
{"MUL", 1, {None, None}},
{"BNE", 2, {ImmediateData, None}},
{"TCALL", 1, {None, None}},
{"CLR1", 2, {DirectAddr, None}},
{"BBC", 3, {DirectAddr, ImmediateData}},
{"MOV", 2, {A, DirectAddrByX}},
{"MOV", 3, {A, AbsoluteAddrByX}},
{"MOV", 3, {A, AbsoluteAddrByY}},
{"MOV", 2, {A, AbsoluteDirectAddrByY}},
{"MOV", 2, {X, DirectAddr}},
{"MOV", 2, {X, DirectAddrByY}},
{"MOVW", 2, {DirectAddr, None}},
{"MOV", 2, {Y, DirectAddrByX}},
{"DEC", 1, {Y, None}},
{"MOV", 1, {Y, A}},
{"CBNE", 3, {DirectAddrByX, ImmediateData}},
{"DAA", 1, {None, None}},
{"CLRV", 1, {None, None}},
{"TCALL", 1, {None, None}},
{"SET1", 2, {DirectAddr, None}},
{"BBS", 3, {DirectAddr, ImmediateData}},
{"MOV", 2, {DirectAddr, A}},
{"MOV", 3, {AbsoluteAddrByX, A}},
{"MOV", 1, {IndexXAddr, A}},
{"MOV", 2, {AbsoluteDirectByXAddr, A}},
{"MOV", 2, {ImmediateData, A}},
{"MOV", 3, {AbsoluteAddr, X}},
{"NOT1", 3, {AbsoluteBit, None}},
{"MOV", 2, {DirectAddr, Y}},
{"MOV", 3, {AbsoluteAddr, Y}},
{"NOTC", 1, {None, None}},
{"POP", 1, {Y, None}},
{"SLEEP", 1, {None, None}},
{"BEQ", 2, {ImmediateData, None}},
{"TCALL", 1, {None, None}},
{"CLR1", 2, {DirectAddr, None}},
{"BBC", 3, {DirectAddr, ImmediateData}},
{"MOV", 2, {DirectAddrByX, A}},
{"MOV", 3, {AbsoluteAddrByX, A}},
{"MOV", 3, {AbsoluteAddrByY, A}},
{"MOV", 2, {AbsoluteDirectAddrByY, A}},
{"MOV", 2, {DirectAddr, X}},
{"MOV", 2, {DirectAddrByY, X}},
{"MOV", 3, {DirectAddr, DirectAddr}},
{"MOV", 2, {DirectAddrByX, Y}},
{"INC", 1, {Y, None}},
{"MOV", 1, {A, Y}},
{"DBNZ", 3, {ImmediateData, None}},
{"STOP", 1, {None, None}}
}};
//! @brief Add instruction to disassembly
int _appendInstruction(int row);
//! @brief Add instruction to disassembly
int _appendInstruction(int row);
//! @brief The QT window for this debugger.
ClosableWindow<APUDebug> *_window;
//! @brief The QT window for this debugger.
ClosableWindow *_window;
//! @brief Internal timer used for update intervals.
QTimer _timer;
//! @brief A widget that contain the whole UI.
Ui::APUView _ui;
//! @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 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;
//! @brief The APU to debug.
ComSquare::APU::APU &_apu;
//! @brief Update the debugger panel values
void _updatePanel();
//! @brief Update the debugger panel values
void _updatePanel();
//! @brief Updates the object that serves as the disassembly
void _updateLogger();
//! @brief Updates the object that serves as the disassembly
void _updateLogger();
//! @brief Replace original _executeInstruction to write to the logger.
int _executeInstruction() override;
//! @brief Retrieves the instruction from the SP location
[[nodiscard]] const Instruction &_getInstruction() const;
//! @brief Retrieves the instruction from the SP location
Instruction &_getInstruction();
//! @brief Returns an operand in text format
[[nodiscard]] std::string _getOperand(Operand ope) const;
//! @brief Returns an operand in text format
std::string _getOperand(Operand ope);
public slots:
//! @brief Pause/Resume the APU.
void pause();
//! @brief Step - Execute a single instruction.
void step();
//! @brief Update the debugger and the underlying APU.
void update();
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;
public slots:
//! @brief Pause/Resume the APU.
void pause();
//! @brief Step - Execute a single instruction.
void step();
//! @brief Called when the window is closed. Turn off the debugger and revert to a basic APU.
void disableDebugger();
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;
//! @brief Return true if the CPU is overloaded with debugging features.
bool isDebugger() const override;
//! @brief Focus the debugger's window.
void focus();
};
}
#endif //COMSQUARE_APUDEBUG_HPP
//! @brief Focus the debugger's window.
void focus();
};
}
}

View File

@@ -3,26 +3,21 @@
//
#include "CGramDebug.hpp"
#include "../SNES.hpp"
#include "SNES.hpp"
#include <QColor>
#include <string>
#include <iostream>
#include <QtWidgets/QTableWidget>
#include "../Utility/Utility.hpp"
#include "Utility/Utility.hpp"
namespace ComSquare::Debugger
{
CGramDebug::CGramDebug(SNES &snes, ComSquare::PPU::PPU &ppu)
: _window(new ClosableWindow<CGramDebug>(*this, &CGramDebug::disableViewer)),
: _window(new ClosableWindow([&snes] { snes.disableCgramViewer(); })),
_snes(snes),
_ui(),
_model(ppu),
_ppu(ppu)
{
this->_window->setContextMenuPolicy(Qt::NoContextMenu);
this->_window->setAttribute(Qt::WA_QuitOnClose, false);
this->_window->setAttribute(Qt::WA_DeleteOnClose);
this->_ui.setupUi(this->_window);
QMainWindow::connect(this->_ui.cgram_view, &QTableView::pressed, this, &CGramDebug::tileClicked);
this->_ui.cgram_view->setModel(&this->_model);
@@ -31,21 +26,11 @@ namespace ComSquare::Debugger
QEvent::registerEventType();
}
void CGramDebug::disableViewer()
{
this->_snes.disableCgramDebugging();
}
void CGramDebug::focus()
{
this->_window->activateWindow();
}
bool CGramDebug::isDebugger()
{
return true;
}
uint16_t CGramDebug::read(uint8_t addr)
{
return this->_ppu.cgramRead(addr);
@@ -79,42 +64,44 @@ namespace ComSquare::Debugger
return;
this->updateInfoTile(index.row(), index.column());
}
}
CGramModel::CGramModel(ComSquare::PPU::PPU &ppu) : _ppu(ppu) {}
CGramModel::CGramModel(ComSquare::PPU::PPU &ppu)
: _ppu(ppu)
{}
int CGramModel::rowCount(const QModelIndex &) const
{
return this->rows;
}
int CGramModel::rowCount(const QModelIndex &) const
{
return this->rows;
}
int CGramModel::columnCount(const QModelIndex &) const
{
return this->column;
}
int CGramModel::columnCount(const QModelIndex &) const
{
return this->column;
}
QVariant CGramModel::data(const QModelIndex &index, int role) const
{
u_int16_t addressValue;
uint8_t red;
uint8_t green;
uint8_t blue;
QVariant CGramModel::data(const QModelIndex &index, int role) const
{
u_int16_t addressValue;
uint8_t red;
uint8_t green;
uint8_t blue;
if (role == Qt::TextAlignmentRole)
return Qt::AlignCenter;
if (role != Qt::BackgroundRole)
return QVariant();
int idDisplayTile = index.row() * 16 + index.column();
uint16_t cgramAddress = idDisplayTile / 8 * 16 + (idDisplayTile % 8 * 2);
addressValue = this->_ppu.cgramRead(cgramAddress);
addressValue += this->_ppu.cgramRead(cgramAddress + 1) << 8U;
if (role == Qt::TextAlignmentRole)
return Qt::AlignCenter;
if (role != Qt::BackgroundRole)
return QVariant();
int idDisplayTile = index.row() * 16 + index.column();
uint16_t cgramAddress = idDisplayTile / 8 * 16 + (idDisplayTile % 8 * 2);
addressValue = this->_ppu.cgramRead(cgramAddress);
addressValue += this->_ppu.cgramRead(cgramAddress + 1) << 8U;
blue = (addressValue & 0x7D00U) >> 10U;
green = (addressValue & 0x03E0U) >> 5U;
red = (addressValue & 0x001FU);
blue = (addressValue & 0x7D00U) >> 10U;
green = (addressValue & 0x03E0U) >> 5U;
red = (addressValue & 0x001FU);
red = red * 255U / 31U;
green = green * 255U / 31U;
blue = blue * 255U / 31U;
return QColor(red, green, blue);
red = red * 255U / 31U;
green = green * 255U / 31U;
blue = blue * 255U / 31U;
return QColor(red, green, blue);
}
}

View File

@@ -2,52 +2,53 @@
// Created by cbihan on 3/27/20.
//
#ifndef COMSQUARE_CGRAMDEBUG_HPP
#define COMSQUARE_CGRAMDEBUG_HPP
#pragma once
#include <QtWidgets/QMainWindow>
#include "../PPU/PPU.hpp"
#include "../../ui/ui_cgramView.h"
#include "PPU/PPU.hpp"
#include "ui/ui_cgramView.h"
#include <QtCore/QSortFilterProxyModel>
#include <QEvent>
#include <QMouseEvent>
#include <QTableView>
#include "ClosableWindow.hpp"
//! @brief The qt model that bind the logs to the view.
class CGramModel : public QAbstractTableModel
{
Q_OBJECT
private:
//! @brief The ppu to log the cgram.
ComSquare::PPU::PPU &_ppu;
public:
//! @brief The number of columns
const int column = 16;
//! @brief The number of rows
const int rows = 16;
int x;
int y;
explicit CGramModel(ComSquare::PPU::PPU &ppu);
CGramModel(const CGramModel &) = delete;
const CGramModel &operator=(const CGramModel &) = delete;
~CGramModel() 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;
};
namespace ComSquare::Debugger
{
//! @brief The qt model that bind the logs to the view.
class CGramModel : public QAbstractTableModel
{
Q_OBJECT
private:
//! @brief The ppu to log the cgram.
ComSquare::PPU::PPU &_ppu;
public:
//! @brief The number of columns
const int column = 16;
//! @brief The number of rows
const int rows = 16;
int x;
int y;
explicit CGramModel(ComSquare::PPU::PPU &ppu);
CGramModel(const CGramModel &) = delete;
const CGramModel &operator=(const CGramModel &) = delete;
~CGramModel() override = default;
//! @brief The number of row the table has.
[[nodiscard]] int rowCount(const QModelIndex &parent) const override;
//! @brief The number of column the table has.
[[nodiscard]] int columnCount(const QModelIndex &parent) const override;
//! @brief Return a data representing the table cell.
[[nodiscard]] QVariant data(const QModelIndex &index, int role) const override;
};
//! @brief window that allow the user to view all data going through the memory bus.
class CGramDebug : public QObject {
class CGramDebug : public QObject
{
private:
//! @brief The QT window for this debugger.
ClosableWindow<CGramDebug> *_window;
ClosableWindow *_window;
//! @brief A reference to the snes (to disable the debugger).
SNES &_snes;
//! @brief A widget that contain the whole UI.
@@ -56,14 +57,11 @@ namespace ComSquare::Debugger
CGramModel _model;
//! @brief A reference to the ppu
ComSquare::PPU::PPU &_ppu;
public:
//! @brief Called when the window is closed. Turn off the debugger.
void disableViewer();
public:
explicit CGramDebug(SNES &snes, ComSquare::PPU::PPU &ppu);
CGramDebug(const CGramDebug &) = delete;
CGramDebug &operator=(const CGramDebug &) = delete;
~CGramDebug() = default;
~CGramDebug() override = default;
//! @brief Read data at the CGRAM address send it to the debugger.
//! @param addr The address to read from.
@@ -71,13 +69,9 @@ namespace ComSquare::Debugger
uint16_t read(uint8_t addr);
//! @brief Focus the debugger's window.
void focus();
//! @brief Return true if the Bus is overloaded with debugging features.
bool isDebugger();
//! @brief Update the text fields with corresponding tile info
void updateInfoTile(int row, int column);
//! @brief Update call updateInfoTile with the correct address
void tileClicked(const QModelIndex &index);
};
}
#endif //COMSQUARE_CGRAMDEBUG_HPP

View File

@@ -3,37 +3,38 @@
//
#include "CPUDebug.hpp"
#include "Utility/Utility.hpp"
#include "SNES.hpp"
#include "Exceptions/InvalidOpcode.hpp"
#include "SymbolLoaders/WlaDx.hpp"
#include <QtEvents>
#include <QPainter>
#include <iostream>
#include <utility>
#include "Utility/Utility.hpp"
#include <QMessageBox>
#include <QPainter>
#include <QtEvents>
#include <fstream>
#include <iostream>
using namespace ComSquare::CPU;
namespace ComSquare::Debugger
namespace ComSquare::Debugger::CPU
{
CPUDebug::CPUDebug(const CPU &basicCPU, SNES &snes)
: CPU(basicCPU),
_window(new ClosableWindow<CPUDebug>(*this, &CPUDebug::disableDebugger)),
_ui(),
_model(*this),
_painter(*this),
_stackModel(this->_bus, *this),
_snes(snes),
_labels(this->_loadLabels(snes.cartridge->getRomPath()))
CPUDebug::CPUDebug(ComSquare::CPU::CPU &basicCPU, SNES &snes)
: _cpu(basicCPU),
_window(new ClosableWindow([&snes] { snes.disableCPUDebugging(); })),
_ui(),
_model(*this),
_painter(*this),
_stackModel(snes.bus, *this),
_snes(snes),
_labels(),
initialStackPointer(this->_cpu._registers.s)
{
this->_window->setContextMenuPolicy(Qt::NoContextMenu);
this->_window->setAttribute(Qt::WA_QuitOnClose, false);
this->_window->setAttribute(Qt::WA_DeleteOnClose);
this->_loadLabels(snes.cartridge.getRomPath());
this->_ui.setupUi(this->_window);
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).
//Parse the first page of the ROM (the code can't reach the second page without a jump).
uint16_t resetInter = snes.cartridge.header.emulationInterrupts.reset;
this->_updateDisassembly(resetInter, 0xFFFF - resetInter);
this->_ui.disassembly->setModel(&this->_model);
this->_ui.disassembly->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
this->_ui.disassembly->horizontalHeader()->setStretchLastSection(true);
@@ -43,7 +44,7 @@ namespace ComSquare::Debugger
this->_ui.stackView->setModel(&this->_stackModel);
this->_ui.stackView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
this->_ui.stackView->verticalHeader()->setSectionResizeMode (QHeaderView::Fixed);
this->_ui.stackView->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
this->_ui.stackView->verticalHeader()->setHighlightSections(false);
@@ -60,82 +61,73 @@ 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);
this->_updateDisassembly(this->_cpu._registers.pac, 0);
this->_cpu.isDisabled = true;
this->_callback = this->_cpu.onReset.addCallback([this] {
this->disassembled.clear();
//Parse the first page of the ROM (the code can't reach the second page without a jump).
uint16_t reset = this->_snes.cartridge.header.emulationInterrupts.reset;
this->_updateDisassembly(reset, 0xFFFF - reset);
this->_updateRegistersPanel();
});
this->_timer.setInterval(1000 / 60);
this->_timer.setSingleShot(false);
connect(&_timer, SIGNAL(timeout()), this, SLOT(update()));
this->_timer.start();
}
bool CPUDebug::isDebugger() const
CPUDebug::~CPUDebug()
{
return true;
}
void CPUDebug::disableDebugger()
{
this->_snes.disableCPUDebugging();
}
void CPUDebug::setMemoryBus(std::shared_ptr<Memory::MemoryBus> bus)
{
this->_stackModel.setMemoryBus(bus);
CPU::setMemoryBus(bus);
this->_cpu.onReset.removeCallback(this->_callback);
this->_cpu.isDisabled = false;
}
unsigned CPUDebug::update()
{
try {
unsigned cycles = 0;
for (auto &channel : this->_dmaChannels)
if (channel.enabled)
cycles += channel.run(INT_MAX);
unsigned cycles = this->_cpu.runDMA(INT_MAX);
if (this->_isPaused)
return 0xFF;
if (this->_isStepping) {
cycles = this->_executeInstruction(this->readPC());
this->_updateDisassembly(this->_registers.pac);
return cycles;
}
for (int i = 0; i < 0xFF; i++) {
auto breakpoint = std::find_if(this->breakpoints.begin(), this->breakpoints.end(), [this](Breakpoint &brk) {
return brk.address == this->_registers.pac;
auto breakpoint = std::find_if(this->breakpoints.begin(), this->breakpoints.end(), [this](auto &brk) {
return brk.address == this->_cpu._registers.pac;
});
if (i != 0 && breakpoint != this->breakpoints.end()) {
if (breakpoint->oneTime)
this->breakpoints.erase(breakpoint);
this->_isPaused = false;
this->pause();
this->pause(true);
return cycles;
}
this->_logInstruction();
cycles += this->_cpu.executeInstruction();
this->_updateRegistersPanel();
if (this->_isStepping) {
this->_isStepping = false;
this->pause(true);
return cycles;
}
cycles += this->_executeInstruction(this->readPC());
}
return cycles;
} catch (InvalidOpcode &e) {
if (!this->_isPaused)
this->pause();
std::cout << "Invalid Opcode: " << e.what() << std::endl;
} catch (const DebuggableError &e) {
this->pause(true);
CPUDebug::showError(e);
return 0xFF;
} catch (const std::exception &e) {
std::cerr << "An error occurred: " << e.what() << std::endl;
QApplication::quit();
}
}
unsigned CPUDebug::_executeInstruction(uint8_t opcode)
void CPUDebug::_logInstruction()
{
if (this->_isPaused)
return 0;
if (this->_isStepping) {
this->_isStepping = false;
this->_isPaused = true;
}
uint24_t pc = (this->_registers.pbr << 16u) | (this->_registers.pc - 1u);
DisassemblyContext ctx = this->_getDisassemblyContext();
DisassembledInstruction instruction = this->_parseInstruction(pc, ctx);
this->_registers.pc--;
this->_historyModel.log({opcode, instruction.name, instruction.argument, this->getProceededParameters()});
DisassembledInstruction instruction = this->_parseInstruction(this->_cpu._registers.pac, ctx);
this->_historyModel.log({instruction.opcode, instruction.name, instruction.argument, this->getProceededParameters()});
this->_ui.history->scrollToBottom();
this->_registers.pc++;
unsigned ret = CPU::_executeInstruction(opcode);
this->_updateRegistersPanel();
return ret;
}
void CPUDebug::showError(const DebuggableError &error)
@@ -156,7 +148,7 @@ namespace ComSquare::Debugger
this->_ui.actionPause->setText("Continue");
else
this->_ui.actionPause->setText("Pause");
this->_updateDisassembly(this->_registers.pac);
this->_updateDisassembly(this->_cpu._registers.pac);
}
void CPUDebug::step()
@@ -167,8 +159,8 @@ namespace ComSquare::Debugger
void CPUDebug::next()
{
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->disassembled.begin(), this->disassembled.end(), [this](auto &i) {
return i.address > this->_cpu._registers.pac;
});
this->breakpoints.push_back({next->address, true});
this->_isPaused = false;
@@ -176,8 +168,8 @@ namespace ComSquare::Debugger
void CPUDebug::toggleBreakpoint(int logicalIndex)
{
DisassembledInstruction instruction = this->disassembledInstructions[logicalIndex];
auto existing = std::find_if(this->breakpoints.begin(), this->breakpoints.end(), [instruction](Breakpoint &i) {
DisassembledInstruction instruction = this->disassembled[logicalIndex];
auto existing = std::find_if(this->breakpoints.begin(), this->breakpoints.end(), [instruction](auto &i) {
return i.address == instruction.address;
});
if (existing == this->breakpoints.end())
@@ -189,55 +181,38 @@ namespace ComSquare::Debugger
void CPUDebug::_updateRegistersPanel()
{
if (!this->_registers.p.m)
this->_ui.accumulatorLineEdit->setText(Utility::to_hex(this->_registers.a).c_str());
if (!this->_cpu._registers.p.m)
this->_ui.accumulatorLineEdit->setText(Utility::to_hex(this->_cpu._registers.a).c_str());
else
this->_ui.accumulatorLineEdit->setText(Utility::to_hex(this->_registers.al).c_str());
this->_ui.programBankRegisterLineEdit->setText(Utility::to_hex(this->_registers.pbr).c_str());
this->_ui.programCounterLineEdit->setText(Utility::to_hex(this->_registers.pc).c_str());
this->_ui.directBankLineEdit->setText(Utility::to_hex(this->_registers.dbr).c_str());
this->_ui.directPageLineEdit->setText(Utility::to_hex(this->_registers.d).c_str());
this->_ui.stackPointerLineEdit->setText(Utility::to_hex(this->_registers.s).c_str());
if (!this->_registers.p.x_b) {
this->_ui.xIndexLineEdit->setText(Utility::to_hex(this->_registers.x).c_str());
this->_ui.yIndexLineEdit->setText(Utility::to_hex(this->_registers.y).c_str());
this->_ui.accumulatorLineEdit->setText(Utility::to_hex(this->_cpu._registers.al).c_str());
this->_ui.programBankRegisterLineEdit->setText(Utility::to_hex(this->_cpu._registers.pbr).c_str());
this->_ui.programCounterLineEdit->setText(Utility::to_hex(this->_cpu._registers.pc).c_str());
this->_ui.directBankLineEdit->setText(Utility::to_hex(this->_cpu._registers.dbr).c_str());
this->_ui.directPageLineEdit->setText(Utility::to_hex(this->_cpu._registers.d).c_str());
this->_ui.stackPointerLineEdit->setText(Utility::to_hex(this->_cpu._registers.s).c_str());
if (!this->_cpu._registers.p.x_b) {
this->_ui.xIndexLineEdit->setText(Utility::to_hex(this->_cpu._registers.x).c_str());
this->_ui.yIndexLineEdit->setText(Utility::to_hex(this->_cpu._registers.y).c_str());
} else {
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.xIndexLineEdit->setText(Utility::to_hex(this->_cpu._registers.xl).c_str());
this->_ui.yIndexLineEdit->setText(Utility::to_hex(this->_cpu._registers.yl).c_str());
}
this->_ui.emulationModeCheckBox->setCheckState(this->_isEmulationMode ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
this->_ui.emulationModeCheckBox->setCheckState(this->_cpu._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.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);
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);
this->_ui.mCheckbox->setCheckState(this->_cpu._registers.p.m ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
this->_ui.xCheckbox->setCheckState(this->_cpu._registers.p.x_b ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
this->_ui.bCheckbox->setCheckState(this->_cpu._registers.p.x_b ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
this->_ui.iCheckbox->setCheckState(this->_cpu._registers.p.i ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
this->_ui.vCheckbox->setCheckState(this->_cpu._registers.p.v ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
this->_ui.dCheckbox->setCheckState(this->_cpu._registers.p.d ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
this->_ui.cCheckbox->setCheckState(this->_cpu._registers.p.c ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
this->_ui.zCheckbox->setCheckState(this->_cpu._registers.p.z ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
this->_ui.nCheckbox->setCheckState(this->_cpu._registers.p.n ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
auto index = this->_stackModel.index(this->_registers.s / 2, 0);
auto index = this->_stackModel.index(this->_cpu._registers.s / 2, 0);
this->_ui.stackView->scrollTo(index, QAbstractItemView::PositionAtCenter);
}
std::string CPUDebug::_getFlagsString()
{
std::string str;
str += this->_registers.p.n ? "n" : "-";
str += this->_registers.p.v ? "v" : "-";
str += this->_registers.p.m ? "m" : "-";
if (this->_isEmulationMode)
str += this->_registers.p.x_b ? "b" : "-";
else
str += this->_registers.p.x_b ? "x" : "-";
str += this->_registers.p.d ? "d" : "-";
str += this->_registers.p.i ? "i" : "-";
str += this->_registers.p.z ? "z" : "-";
str += this->_registers.p.c ? "c" : "-";
return str;
}
void CPUDebug::clearHistory()
{
this->_historyModel.clear();
@@ -245,22 +220,22 @@ namespace ComSquare::Debugger
void CPUDebug::_updateDisassembly(uint24_t start, uint24_t refreshSize)
{
auto first = std::find_if(this->disassembledInstructions.begin(), this->disassembledInstructions.end(), [start](DisassembledInstruction &i) {
auto first = std::find_if(this->disassembled.begin(), this->disassembled.end(), [start](DisassembledInstruction &i) {
return i.address >= start;
});
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 end = std::find_if(this->disassembled.begin(), this->disassembled.end(), [start, refreshSize](DisassembledInstruction &i) {
return i.address >= start + refreshSize;
});
auto next = this->disassembled.erase(first, end);
auto next = std::find_if(this->disassembledInstructions.begin(), this->disassembledInstructions.end(), [start](DisassembledInstruction &i) {
return i.address >= start;
});
DisassemblyContext ctx = this->_getDisassemblyContext();
std::vector<DisassembledInstruction> nextInstructions = this->_disassemble(start, refreshSize, ctx);
this->disassembledInstructions.insert(next, nextInstructions.begin(), nextInstructions.end());
auto inserted = this->disassembled.insert(next, nextInstructions.begin(), nextInstructions.end());
int row = next - this->disassembledInstructions.begin();
if (this->disassembled.empty())
return;
int row = static_cast<int>(inserted - this->disassembled.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);
@@ -270,16 +245,7 @@ namespace ComSquare::Debugger
DisassemblyContext CPUDebug::_getDisassemblyContext()
{
return {this->_registers.p.m, this->_registers.p.x_b, this->_isEmulationMode};
}
int CPUDebug::RESB()
{
CPU::RESB();
this->disassembledInstructions.clear();
this->_updateDisassembly(0xFFFF - this->_cartridgeHeader.emulationInterrupts.reset);
this->_updateRegistersPanel();
return (0);
return {this->_cpu._registers.p.m, this->_cpu._registers.p.x_b, this->_cpu._isEmulationMode};
}
void CPUDebug::focus()
@@ -287,251 +253,251 @@ namespace ComSquare::Debugger
this->_window->activateWindow();
}
uint24_t CPUDebug::getPC()
uint24_t CPUDebug::getPC() const
{
return this->_registers.pac;
return this->_cpu._registers.pac;
}
uint16_t CPUDebug::getStackPointer()
uint16_t CPUDebug::getStackPointer() const
{
return this->_registers.s;
return this->_cpu._registers.s;
}
std::string CPUDebug::getProceededParameters()
std::string CPUDebug::getProceededParameters() const
{
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;
uint24_t pac = this->_cpu._registers.pac;
auto &bus = this->_cpu.getBus();
this->_cpu.setBus(this->_snes.bus);
const Instruction &instruction = this->_cpu.instructions[this->_cpu._readPC()];
uint24_t valueAddr = this->_cpu._getValueAddr(instruction);
this->_cpu._registers.pac = pac;
this->_cpu.setBus(bus);
if (instruction.size == 1)
return "";
return "[" + Utility::to_hex(valueAddr, Utility::AsmPrefix) + "]";
}
std::vector<Label> CPUDebug::_loadLabels(std::filesystem::path romPath) const
void CPUDebug::_loadLabels(std::filesystem::path romPath)
{
std::vector<Label> labels;
std::string symbolPath = romPath.replace_extension(".sym");
std::ifstream sym(symbolPath);
// if (sym) {
// std::vector<Label> symLabels = WlaDx::parse(sym);
// labels.insert(labels.end(),
// std::make_move_iterator(symLabels.begin()),
// std::make_move_iterator(symLabels.end()));
// }
return labels;
this->_labels.clear();
//if (sym) {
// std::vector<Label> symLabels = WlaDx::parse(sym);
// this->_labels.insert(labels.end(),
// std::make_move_iterator(symLabels.begin()),
// std::make_move_iterator(symLabels.end()));
//}
}
}
DisassemblyModel::DisassemblyModel(ComSquare::Debugger::CPUDebug &cpu) : QAbstractTableModel(), _cpu(cpu){ }
DisassemblyModel::DisassemblyModel(CPUDebug &cpu)
: QAbstractTableModel(), _cpu(cpu)
{}
int DisassemblyModel::columnCount(const QModelIndex &) const
{
return 4;
}
int DisassemblyModel::columnCount(const QModelIndex &) const
{
return 4;
}
int DisassemblyModel::rowCount(const QModelIndex &) const
{
return this->_cpu.disassembledInstructions.size();
}
int DisassemblyModel::rowCount(const QModelIndex &) const
{
return static_cast<int>(this->_cpu.disassembled.size());
}
QVariant DisassemblyModel::data(const QModelIndex &index, int role) const
{
ComSquare::Debugger::DisassembledInstruction instruction = this->_cpu.disassembledInstructions[index.row()];
QVariant DisassemblyModel::data(const QModelIndex &index, int role) const
{
DisassembledInstruction instruction = this->_cpu.disassembled[index.row()];
switch (role) {
case Qt::DecorationRole:
if (index.column() == 2 && instruction.level == ComSquare::Debugger::TrustLevel::Unsafe)
return QColor(Qt::yellow);
if (index.column() == 2 && instruction.level == ComSquare::Debugger::TrustLevel::Compromised)
return QColor(Qt::red);
return QVariant();
case Qt::DisplayRole:
switch (role) {
case Qt::DecorationRole:
if (index.column() == 2 && instruction.level == TrustLevel::Unsafe)
return QColor(Qt::yellow);
if (index.column() == 2 && instruction.level == TrustLevel::Compromised)
return QColor(Qt::red);
return QVariant();
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();
}
}
QVariant DisassemblyModel::headerData(int section, Qt::Orientation orientation, int role) const
{
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();
DisassembledInstruction instruction = this->_cpu.disassembled[section];
return QString(Utility::to_hex(instruction.address, Utility::HexString::NoPrefix).c_str());
}
RowPainter::RowPainter(CPUDebug &cpu, QObject *parent)
: QStyledItemDelegate(parent), _cpu(cpu)
{}
void RowPainter::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
DisassembledInstruction instruction = this->_cpu.disassembled[index.row()];
bool isBreakpoint = false;
auto breakpoint = std::find_if(this->_cpu.breakpoints.begin(), this->_cpu.breakpoints.end(), [instruction](auto &brk) {
return brk.address == instruction.address;
});
if (breakpoint != this->_cpu.breakpoints.end())
isBreakpoint = true;
QStyleOptionViewItem style = option;
if (instruction.address == this->_cpu.getPC()) {
painter->fillRect(option.rect, QColor(Qt::darkGreen));
style.state &= ~QStyle::State_Selected;
} else if (isBreakpoint && !breakpoint->oneTime) {
painter->fillRect(option.rect, QColor(Qt::darkRed));
style.state &= ~QStyle::State_Selected;
}
QStyledItemDelegate::paint(painter, style, index);
}
QSize RowPainter::sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const
{
return QSize();
}
StackModel::StackModel(Memory::IMemoryBus &bus, 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.peek_v(addr);
return (Utility::to_hex(value, Utility::NoPrefix).c_str());
} catch (const 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(Utility::to_hex(addr, Utility::HexString::NoPrefix).c_str());
}
HistoryModel::HistoryModel() = default;
int HistoryModel::rowCount(const QModelIndex &) const
{
return static_cast<int>(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();
ExecutedInstruction instruction = this->_instructions[index.row()];
switch (index.column()) {
case 0:
return QString(instruction.name.c_str());
return QString(Utility::to_hex(instruction.opcode, Utility::NoPrefix).c_str());
case 1:
return QString(instruction.argument.c_str());
return QString(instruction.name.c_str());
case 2:
return QString(instruction.params.c_str());
case 3:
if (instruction.address != this->_cpu.getPC())
return QVariant();
return QString(this->_cpu.getProceededParameters().c_str());
return QString(instruction.proceededParams.c_str());
default:
return QVariant();
}
default:
return QVariant();
}
}
QVariant DisassemblyModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal) {
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("INST");
return QString("op");
case 1:
return QString("Parameter");
return QString("ins");
case 2:
return QString("Thrust");
return QString("Parameter");
case 3:
return QString("Data Address");
return QString("Pointer");
default:
return QVariant();
}
}
if (role != Qt::DisplayRole)
return QVariant();
ComSquare::Debugger::DisassembledInstruction instruction = this->_cpu.disassembledInstructions[section];
return QString(ComSquare::Utility::to_hex(instruction.address, ComSquare::Utility::HexString::NoPrefix).c_str());
}
RowPainter::RowPainter(ComSquare::Debugger::CPUDebug &cpu, QObject *parent) : QStyledItemDelegate(parent), _cpu(cpu) { }
void RowPainter::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
ComSquare::Debugger::DisassembledInstruction instruction = this->_cpu.disassembledInstructions[index.row()];
bool isBreakpoint = false;
auto breakpoint = std::find_if(this->_cpu.breakpoints.begin(), this->_cpu.breakpoints.end(), [instruction](ComSquare::Debugger::Breakpoint brk) {
return brk.address == instruction.address;
});
if (breakpoint != this->_cpu.breakpoints.end())
isBreakpoint = true;
QStyleOptionViewItem style = option;
if (instruction.address == this->_cpu.getPC()) {
painter->fillRect(option.rect, QColor(Qt::darkGreen));
style.state &= ~QStyle::State_Selected;
} else if (isBreakpoint && !breakpoint->oneTime) {
painter->fillRect(option.rect,QColor(Qt::darkRed));
style.state &= ~QStyle::State_Selected;
void HistoryModel::log(const ExecutedInstruction &instruction)
{
int row = static_cast<int>(this->_instructions.size());
this->beginInsertRows(QModelIndex(), row, row);
this->_instructions.push_back(instruction);
this->insertRow(row);
this->endInsertRows();
}
QStyledItemDelegate::paint(painter, style, index);
}
QSize RowPainter::sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const
{
return QSize();
}
StackModel::StackModel(std::shared_ptr<ComSquare::Memory::MemoryBus> bus, ComSquare::Debugger::CPUDebug &cpu)
: _bus(std::move(bus)), _cpu(cpu)
{ }
void StackModel::setMemoryBus(std::shared_ptr<ComSquare::Memory::MemoryBus> bus)
{
this->_bus = std::move(bus);
}
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);
void HistoryModel::clear()
{
this->beginResetModel();
this->_instructions.clear();
this->endResetModel();
}
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, true);
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());
}
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("ins");
case 2:
return QString("Parameter");
case 3:
return QString("Pointer");
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();
}
}// namespace ComSquare::Debugger

View File

@@ -2,315 +2,332 @@
// Created by anonymus-raccoon on 2/14/20.
//
#ifndef COMSQUARE_CPUDEBUG_HPP
#define COMSQUARE_CPUDEBUG_HPP
#pragma once
#include <QtWidgets/QStyledItemDelegate>
#include "CPU/CPU.hpp"
#include "SNES.hpp"
#include "ui/ui_cpuView.h"
#include "Debugger/ClosableWindow.hpp"
#include "Exceptions/DebuggableError.hpp"
#include "ui/ui_cpuView.h"
#include "Models/Ints.hpp"
#include "Memory/MemoryBus.hpp"
#include "CPU/Instruction.hpp"
#include <QtWidgets/QStyledItemDelegate>
#include <filesystem>
#include <optional>
#include <QTimer>
namespace ComSquare::Debugger
namespace ComSquare
{
class CPUDebug;
class SNES;
//! @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;
};
}
namespace CPU
{
class CPU;
}
//! @brief The qt model that show the stack.
class StackModel : public QAbstractTableModel
{
Q_OBJECT
private:
std::shared_ptr<ComSquare::Memory::MemoryBus> _bus;
ComSquare::Debugger::CPUDebug &_cpu;
public:
explicit StackModel(std::shared_ptr<ComSquare::Memory::MemoryBus> bus, ComSquare::Debugger::CPUDebug &cpu);
StackModel(const StackModel &) = delete;
const StackModel &operator=(const StackModel &) = delete;
~StackModel() override = default;
namespace Debugger::CPU
{
class CPUDebug;
//! @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 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 Change the memory bus used by the view.
void setMemoryBus(std::shared_ptr<ComSquare::Memory::MemoryBus> bus);
};
//! @brief The qt model that show the stack.
class StackModel : public QAbstractTableModel
{
Q_OBJECT
private:
Memory::IMemoryBus &_bus;
CPUDebug &_cpu;
//! @brief The qt model that show the history.
class HistoryModel : public QAbstractTableModel
{
Q_OBJECT
private:
std::vector<ComSquare::Debugger::ExecutedInstruction> _instructions = {};
public:
HistoryModel();
HistoryModel(const HistoryModel &) = delete;
const HistoryModel &operator=(const HistoryModel &) = delete;
~HistoryModel() override = default;
public:
explicit StackModel(Memory::IMemoryBus &bus, CPUDebug &cpu);
StackModel(const StackModel &) = delete;
const StackModel &operator=(const StackModel &) = delete;
~StackModel() 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.
[[nodiscard]] int rowCount(const QModelIndex &parent) const override;
//! @brief The number of column the table has.
[[nodiscard]] int columnCount(const QModelIndex &parent) const override;
//! @brief Return a data representing the table cell.
[[nodiscard]] QVariant data(const QModelIndex &index, int role) const override;
//! @brief Override the headers to use hex values.
[[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
};
//! @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 history.
class HistoryModel : public QAbstractTableModel
{
Q_OBJECT
private:
std::vector<ExecutedInstruction> _instructions = {};
//! @brief The qt model that show the disassembly.
class DisassemblyModel : public QAbstractTableModel
{
Q_OBJECT
private:
ComSquare::Debugger::CPUDebug &_cpu;
public:
explicit DisassemblyModel(ComSquare::Debugger::CPUDebug &cpu);
DisassemblyModel(const DisassemblyModel &) = delete;
const DisassemblyModel &operator=(const DisassemblyModel &) = delete;
~DisassemblyModel() override = default;
public:
HistoryModel();
HistoryModel(const HistoryModel &) = delete;
const HistoryModel &operator=(const HistoryModel &) = delete;
~HistoryModel() 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 Log a new instruction
void log(const ExecutedInstruction &);
//! @brief Remove every instructions of the history.
void clear();
//! @brief The qt class that highlight breakpoints and the PC's position
class RowPainter : public QStyledItemDelegate {
Q_OBJECT
private:
//! @brief The CPU to get PC and breakpoints from.
ComSquare::Debugger::CPUDebug &_cpu;
public:
explicit RowPainter(ComSquare::Debugger::CPUDebug &cpu, QObject *parent = nullptr);
RowPainter &operator=(const RowPainter &) = delete;
~RowPainter() override = default;
protected:
QSize sizeHint(const QStyleOptionViewItem &options, const QModelIndex &index) const override;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
};
//! @brief The number of row the table has.
[[nodiscard]] int rowCount(const QModelIndex &parent) const override;
//! @brief The number of column the table has.
[[nodiscard]] int columnCount(const QModelIndex &parent) const override;
//! @brief Return a data representing the table cell.
[[nodiscard]] QVariant data(const QModelIndex &index, int role) const override;
//! @brief Override the headers to use hex values.
[[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
};
namespace ComSquare::Debugger
{
enum TrustLevel {
Safe,
Unsafe,
Compromised
};
//! @brief The qt model that show the disassembly.
class DisassemblyModel : public QAbstractTableModel
{
Q_OBJECT
private:
CPUDebug &_cpu;
//! @brief Struct used to emulate the state of the processor during the disassembly since instructions take a different amount of space depending on some flags.
struct DisassemblyContext {
//! @brief The accumulator and Memory width flag (in native mode only) - 0 = 16 bits mode, 1 = 8 bits mode.
//! @info If this flag is set to false, instructions that have the ImmediateByA addressing mode take 1 more bit of space.
bool mFlag = true;
//! @brief The indeX register width flag (in native mode only) - 0 = 16 bits mode, 1 = 8 bits mode OR the Break flag (in emulation mode only)
//! @info If this flag is set to false, instructions that have the ImmediateByX addressing mode take 1 more bit of space.
bool xFlag = true;
//! @brief Is the CPU emulating a 6502? If yes, some instructions don't change flags the same way.
bool isEmulationMode = true;
//! @brief Sometimes, the flags can't be tracked correctly after an instruction so the next instructions may not be correctly disassembled.
TrustLevel level = Safe;
};
public:
explicit DisassemblyModel(CPUDebug &cpu);
DisassemblyModel(const DisassemblyModel &) = delete;
const DisassemblyModel &operator=(const DisassemblyModel &) = delete;
~DisassemblyModel() override = default;
//! @brief Struct representing an instruction in an human readable way (created by disassembling the rom).
struct DisassembledInstruction : public CPU::Instruction {
//! @brief The address of the instruction
uint24_t address;
//! @brief A string representing the argument with the right addressing mode.
std::string argument;
//! @brief The opcode of the instruction
uint8_t opcode;
//! @brief Are we sure that this instruction has been correctly disassembled?
TrustLevel level;
//! @brief The number of row the table has.
[[nodiscard]] int rowCount(const QModelIndex &parent) const override;
//! @brief The number of column the table has.
[[nodiscard]] int columnCount(const QModelIndex &parent) const override;
//! @brief Return a data representing the table cell.
[[nodiscard]] QVariant data(const QModelIndex &index, int role) const override;
//! @brief Override the headers to use hex values.
[[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
};
DisassembledInstruction(const CPU::Instruction &instruction, uint24_t address, std::string argument, uint8_t opcode);
DisassembledInstruction(const DisassembledInstruction &) = default;
DisassembledInstruction &operator=(const DisassembledInstruction &) = default;
~DisassembledInstruction() = default;
//! @brief The qt class that highlight breakpoints and the PC's position
class RowPainter : public QStyledItemDelegate
{
Q_OBJECT
private:
//! @brief The CPU to get PC and breakpoints from.
CPUDebug &_cpu;
std::string toString();
};
public:
explicit RowPainter(CPUDebug &cpu, QObject *parent = nullptr);
RowPainter &operator=(const RowPainter &) = delete;
~RowPainter() override = default;
//! @brief Struct representing a breakpoint set by the user or by the app
struct Breakpoint {
//! @brief The address of the breakpoint
uint24_t address;
//! @brief If this is true, the breakpoint will be deleted on first hit and won't be shown on the disassembly view.
bool oneTime;
};
protected:
[[nodiscard]] QSize sizeHint(const QStyleOptionViewItem &options, const QModelIndex &index) const override;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
};
//! @brief Struct representing a label.
struct Label {
//! @brief The address of this label
uint24_t address;
//! @brief The name of this label
std::string name;
//! @brief The size of the definition related to this label
std::optional<unsigned> size;
};
enum TrustLevel
{
Safe,
Unsafe,
Compromised
};
//! @brief A custom CPU with a window that show it's registers and the disassembly.
class CPUDebug : public CPU::CPU, public QObject {
private:
//! @brief The QT window for this debugger.
ClosableWindow<CPUDebug> *_window;
//! @brief A widget that contain the whole UI.
Ui::CPUView _ui;
//! @brief The disassembly viewer's model.
DisassemblyModel _model;
//! @brief A custom painter that highlight breakpoints and the PC's position.
RowPainter _painter;
//! @brief 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.
bool _isStepping = false;
//! @brief A reference to the snes (to disable the debugger).
SNES &_snes;
//! @brief A list of labels and their size.
std::vector<Label> _labels;
//! @brief Struct used to emulate the state of the processor during the disassembly since instructions take a different amount of space depending on some flags.
struct DisassemblyContext
{
//! @brief The accumulator and Memory width flag (in native mode only) - 0 = 16 bits mode, 1 = 8 bits mode.
//! @info If this flag is set to false, instructions that have the ImmediateByA addressing mode take 1 more bit of space.
bool mFlag = true;
//! @brief The indeX register width flag (in native mode only) - 0 = 16 bits mode, 1 = 8 bits mode OR the Break flag (in emulation mode only)
//! @info If this flag is set to false, instructions that have the ImmediateByX addressing mode take 1 more bit of space.
bool xFlag = true;
//! @brief Is the CPU emulating a 6502? If yes, some instructions don't change flags the same way.
bool isEmulationMode = true;
//! @brief Sometimes, the flags can't be tracked correctly after an instruction so the next instructions may not be correctly disassembled.
TrustLevel level = Safe;
};
//! @brief Load labels from a symbol file.
std::vector<Label> _loadLabels(std::filesystem::path romPath) const;
//! @brief Reimplement the basic instruction execution method to log instructions inside the logger view.
unsigned _executeInstruction(uint8_t opcode) override;
//! @brief Return a disassembly context representing the current state of the processor.
DisassemblyContext _getDisassemblyContext();
//! @brief Disassemble part of the memory (using the bus) and parse it to a map of address and disassembled instruction.
//! @param ctx The initial context of the processor before the disassembly begin.
std::vector<DisassembledInstruction> _disassemble(uint24_t startAddr, uint24_t size, DisassemblyContext &ctx);
//! @brief Update disassembly with the new state of the processor.
void _updateDisassembly(uint24_t 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.
std::string _getInstructionParameter(ComSquare::CPU::Instruction &instruction, uint24_t pc, DisassemblyContext &ctx);
//! @brief Get a printable string representing the flags.
std::string _getFlagsString();
//! @brief Update the register's panel (accumulator, stack pointer...)
void _updateRegistersPanel();
//! @brief Struct representing an instruction in an human readable way (created by disassembling the rom).
struct DisassembledInstruction : public ComSquare::CPU::Instruction
{
//! @brief The address of the instruction
uint24_t address;
//! @brief A string representing the argument with the right addressing mode.
std::string argument;
//! @brief The opcode of the instruction
uint8_t opcode;
//! @brief Are we sure that this instruction has been correctly disassembled?
TrustLevel level;
//! @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 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 _getStackRelativeIndirectIndexedByYValue(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);
DisassembledInstruction(const ComSquare::CPU::Instruction &instruction,
uint24_t address,
std::string argument,
uint8_t opcode);
DisassembledInstruction(const DisassembledInstruction &) = default;
DisassembledInstruction &operator=(const DisassembledInstruction &) = default;
~DisassembledInstruction() = default;
};
public:
//! @brief Show an error dialog related to an exception.
void showError(const DebuggableError &error);
//! @brief Pause/Resume the CPU.
void pause(bool forcePause = false);
//! @brief Step - Execute a single instruction.
void step();
//! @brief Next - Continue running instructions until the next line is reached.
void next();
//! @brief Clear the history panel.
void clearHistory();
//! @brief Called when the user clicks on a section header. It enable/disable a breakpoint for this address.
void toggleBreakpoint(int logicalIndex);
//! @brief Called when the window is closed. Turn off the debugger and revert to a basic CPU.
void disableDebugger();
//! @brief The list of disassembled instructions to show on the debugger.
std::vector<DisassembledInstruction> disassembledInstructions;
//! @brief The list of breakpoints the user has set.
std::vector<Breakpoint> breakpoints;
//! @brief 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.
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 Struct representing a breakpoint set by the user or by the app
struct Breakpoint
{
//! @brief The address of the breakpoint
uint24_t address;
//! @brief If this is true, the breakpoint will be deleted on first hit and won't be shown on the disassembly view.
bool oneTime;
};
//! @brief Struct representing a label.
struct Label
{
//! @brief The address of this label
uint24_t address;
//! @brief The name of this label
std::string name;
//! @brief The size of the definition related to this label
std::optional<unsigned> size;
};
//! @brief Convert a basic CPU to a debugging CPU.
CPUDebug(const ComSquare::CPU::CPU &cpu, SNES &snes);
CPUDebug(const CPUDebug &) = delete;
CPUDebug &operator=(const CPUDebug &) = delete;
~CPUDebug() override = default;
//! @brief A window that show registers and the disassembly of a CPU.
class CPUDebug : public QObject
{
Q_OBJECT
private:
//! @brief The basic CPU to debug.
ComSquare::CPU::CPU &_cpu;
//! @brief The QT window for this debugger.
ClosableWindow *_window;
//! @brief Internal timer used for update intervals.
QTimer _timer;
//! @brief A widget that contain the whole UI.
Ui::CPUView _ui;
//! @brief The disassembly viewer's model.
DisassemblyModel _model;
//! @brief A custom painter that highlight breakpoints and the PC's position.
RowPainter _painter;
//! @brief 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.
bool _isStepping = false;
//! @brief A reference to the snes (to disable the debugger).
SNES &_snes;
//! @brief A list of labels and their size.
std::vector<Label> _labels;
//! @brief The callback ID for the on reset of the CPU.
int _callback;
//! @brief Return true if the CPU is overloaded with debugging features.
bool isDebugger() const override;
//! @brief Load labels from a symbol file.
void _loadLabels(std::filesystem::path romPath);
//! @brief Add the instruction under the PC to the history log.
void _logInstruction();
//! @brief Return a disassembly context representing the current state of the processor.
DisassemblyContext _getDisassemblyContext();
//! @brief Disassemble part of the memory (using the bus) and parse it to a map of address and disassembled instruction.
//! @param ctx The initial context of the processor before the disassembly begin.
std::vector<DisassembledInstruction> _disassemble(uint24_t startAddr, uint24_t size,
DisassemblyContext &ctx);
//! @brief Update disassembly with the new state of the processor.
void _updateDisassembly(uint24_t 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) const;
//! @brief Get the parameter of the instruction as an hexadecimal string.
std::string _getInstructionParameter(const ComSquare::CPU::Instruction &instruction,
uint24_t pc,
DisassemblyContext &ctx) const;
//! @brief Update the register's panel (accumulator, stack pointer...)
void _updateRegistersPanel();
//! @brief Focus the debugger's window.
void focus();
//! @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).
[[nodiscard]] std::string _getImmediateValue(uint24_t pc, bool dual) const;
//! @brief Return a printable string corresponding to the value of an absolute addressing mode.
[[nodiscard]] std::string _getAbsoluteValue(uint24_t pc) const;
//! @brief Return a printable string corresponding to the value of an absolute long addressing mode.
[[nodiscard]] std::string _getAbsoluteLongValue(uint24_t pc) const;
//! @brief Return a printable string corresponding to the value of a direct addressing mode.
[[nodiscard]] std::string _getDirectValue(uint24_t pc) const;
//! @brief Return a printable string corresponding to the value of a direct indirect addressing mode.
[[nodiscard]] std::string _getDirectIndirectValue(uint24_t pc) const;
//! @brief Return a printable string corresponding to the value of a direct indirect long addressing mode.
[[nodiscard]] std::string _getDirectIndirectLongValue(uint24_t pc) const;
//! @brief Return a printable string corresponding to the value of a absolute indexed by x addressing mode.
[[nodiscard]] std::string _getAbsoluteIndexByXValue(uint24_t pc) const;
//! @brief Return a printable string corresponding to the value of a absolute indexed by x long addressing mode.
[[nodiscard]] std::string _getAbsoluteIndexByXLongValue(uint24_t pc) const;
//! @brief Return a printable string corresponding to the value of a absolute indexed by y addressing mode.
[[nodiscard]] std::string _getAbsoluteIndexByYValue(uint24_t pc) const;
//! @brief Return a printable string corresponding to the value of a direct index by x addressing mode.
[[nodiscard]] std::string _getDirectIndexedByXValue(uint24_t pc) const;
//! @brief Return a printable string corresponding to the value of a direct index by y addressing mode.
[[nodiscard]] std::string _getDirectIndexedByYValue(uint24_t pc) const;
//! @brief Return a printable string corresponding to the value of a direct indirect index by x addressing mode.
[[nodiscard]] std::string _getDirectIndexedByXIndirectValue(uint24_t pc) const;
//! @brief Return a printable string corresponding to the value of a direct indirect index by y addressing mode.
[[nodiscard]] std::string _getDirectIndirectIndexedByYValue(uint24_t pc) const;
//! @brief Return a printable string corresponding to the value of a direct indirect index by y long addressing mode.
[[nodiscard]] std::string _getDirectIndirectIndexedByYLongValue(uint24_t pc) const;
//! @brief Return a printable string corresponding to the value of a stack relative addressing mode.
[[nodiscard]] std::string _getStackRelativeValue(uint24_t pc) const;
//! @brief Return a printable string corresponding to the value of a stack relative indirect indexed by y addressing mode.
[[nodiscard]] std::string _getStackRelativeIndirectIndexedByYValue(uint24_t pc) const;
//! @brief Return a printable string corresponding to the value of a absolute indirect addressing mode.
[[nodiscard]] std::string _getAbsoluteIndirectValue(uint24_t pc) const;
//! @brief Return a printable string corresponding to the value of a absolute indirect indexed by x addressing mode.
[[nodiscard]] std::string _getAbsoluteIndirectIndexedByXValue(uint24_t pc) const;
//! @brief Return a printable string corresponding to the value of a absolute indirect long addressing mode.
[[nodiscard]] std::string _getAbsoluteIndirectLongValue(uint24_t pc) const;
//! @brief Override the basic cpu's update to allow pausing of the CPU only.
unsigned update() override;
public slots:
//! @brief Update the current debugger and the underlying CPU.
unsigned update();
public:
//! @brief Show an error dialog related to an exception.
static void showError(const DebuggableError &error);
//! @brief Pause/Resume the CPU.
//! @param forcePause True if you want to set the set the state of the CPU to paused. False if you want to toggle the pause status.
void pause(bool forcePause = false);
//! @brief Step - Execute a single instruction.
void step();
//! @brief Next - Continue running instructions until the next line is reached.
void next();
//! @brief Clear the history panel.
void clearHistory();
//! @brief Called when the user clicks on a section header. It enable/disable a breakpoint for this address.
void toggleBreakpoint(int logicalIndex);
//! @brief The list of disassembled instructions to show on the debugger.
std::vector<DisassembledInstruction> disassembled;
//! @brief The list of breakpoints the user has set.
std::vector<Breakpoint> breakpoints;
//! @brief Get a string representing the actual value of the arguments of the next instruction to execute.
[[nodiscard]] std::string getProceededParameters() const;
//! @brief Return the current program counter of this CPU.
[[nodiscard]] uint24_t getPC() const;
//! @brief Return the current stack pointer.
[[nodiscard]] uint16_t getStackPointer() const;
//! @brief The stack pointer before the execution of any instructions.
uint16_t initialStackPointer;
//! @brief Change the memory bus used by the CPU.
void setMemoryBus(std::shared_ptr<Memory::MemoryBus> bus) override;
};
}
//! @brief Convert a basic CPU to a debugging CPU.
CPUDebug(ComSquare::CPU::CPU &cpu, SNES &snes);
CPUDebug(const CPUDebug &) = delete;
CPUDebug &operator=(const CPUDebug &) = delete;
~CPUDebug() override;
#endif //COMSQUARE_CPUDEBUG_HPP
//! @brief Focus the debugger's window.
void focus();
};
}// namespace Debugger
}// namespace ComSquare

View File

@@ -2,21 +2,25 @@
// Created by anonymus-raccoon on 4/3/20.
//
#include <sstream>
#include "Utility/Utility.hpp"
#include "CPUDebug.hpp"
#include "../../Utility/Utility.hpp"
#include "SNES.hpp"
#include <sstream>
using namespace ComSquare::CPU;
namespace ComSquare::Debugger
namespace ComSquare::Debugger::CPU
{
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;
}
DisassembledInstruction::DisassembledInstruction(const ComSquare::CPU::Instruction &instruction,
uint24_t addr,
std::string arg,
uint8_t op)
: ComSquare::CPU::Instruction(instruction),
address(addr),
argument(std::move(arg)),
opcode(op),
level(Safe)
{}
std::vector<DisassembledInstruction> CPUDebug::_disassemble(uint24_t pc, uint24_t length, DisassemblyContext &ctx)
{
@@ -33,26 +37,26 @@ namespace ComSquare::Debugger
if (instruction.addressingMode == ImmediateForX && !ctx.xFlag)
pc++;
if (instruction.opcode == 0x40 && ctx.isEmulationMode) { // RTI
if (instruction.opcode == 0x40 && ctx.isEmulationMode) {// RTI
ctx.mFlag = true;
ctx.xFlag = true;
}
if (instruction.opcode == 0xC2) { // REP
if (instruction.opcode == 0xC2) {// REP
if (ctx.isEmulationMode) {
ctx.mFlag = true;
ctx.xFlag = true;
} else {
uint8_t m = this->_bus->read(pc - 1, true);
uint8_t m = this->_snes.bus.peek_v(pc - 1);
ctx.mFlag &= ~m & 0b00100000u;
ctx.xFlag &= ~m & 0b00010000u;
}
}
if (instruction.opcode == 0xE2) { // SEP
uint8_t m = this->_bus->read(pc - 1, true);
if (instruction.opcode == 0xE2) {// SEP
uint8_t m = this->_snes.bus.peek_v(pc - 1);
ctx.mFlag |= m & 0b00100000u;
ctx.xFlag |= m & 0b00010000u;
}
if (instruction.opcode == 0x28) { // PLP
if (instruction.opcode == 0x28) {// PLP
if (ctx.isEmulationMode) {
ctx.mFlag = true;
ctx.xFlag = true;
@@ -61,21 +65,23 @@ namespace ComSquare::Debugger
}
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.
// 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.
ctx.isEmulationMode = false;
}
}
return map;
}
DisassembledInstruction CPUDebug::_parseInstruction(uint24_t pc, DisassemblyContext &ctx)
DisassembledInstruction CPUDebug::_parseInstruction(uint24_t pc, DisassemblyContext &ctx) const
{
uint24_t opcode = this->_bus->read(pc, true);
Instruction instruction = this->_instructions[opcode];
uint24_t opcode = this->_snes.bus.peek_v(pc);
const Instruction &instruction = this->_cpu.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)
std::string CPUDebug::_getInstructionParameter(const Instruction &instruction, uint24_t pc, DisassemblyContext &ctx) const
{
switch (instruction.addressingMode) {
case Implied:
@@ -132,141 +138,141 @@ namespace ComSquare::Debugger
}
}
std::string CPUDebug::_getImmediateValue(uint24_t pc, bool dual)
std::string CPUDebug::_getImmediateValue(uint24_t pc, bool dual) const
{
unsigned value = this->_bus->read(pc, true);
unsigned value = this->_snes.bus.peek_v(pc);
if (dual)
value += this->_bus->read(pc + 1, true) << 8u;
value += this->_snes.bus.peek_v(pc + 1) << 8u;
std::stringstream ss;
ss << "#$" << std::hex << value;
return ss.str();
}
std::string CPUDebug::_getDirectValue(uint24_t pc)
std::string CPUDebug::_getDirectValue(uint24_t pc) const
{
return Utility::to_hex(this->_bus->read(pc, true), Utility::HexString::AsmPrefix);
return Utility::to_hex(this->_snes.bus.peek_v(pc), Utility::HexString::AsmPrefix);
}
std::string CPUDebug::_getAbsoluteValue(uint24_t pc)
std::string CPUDebug::_getAbsoluteValue(uint24_t pc) const
{
uint24_t value = this->_bus->read(pc, true) + (this->_bus->read(pc + 1, true) << 8u);
uint24_t value = this->_snes.bus.peek_v(pc) + (this->_snes.bus.peek_v(pc + 1) << 8u);
return Utility::to_hex(value, Utility::HexString::AsmPrefix);
}
std::string CPUDebug::_getAbsoluteLongValue(uint24_t pc)
std::string CPUDebug::_getAbsoluteLongValue(uint24_t pc) const
{
unsigned value = this->_bus->read(pc++, true);
value += this->_bus->read(pc++, true) << 8u;
value += this->_bus->read(pc, true) << 16u;
unsigned value = this->_snes.bus.peek_v(pc++);
value += this->_snes.bus.peek_v(pc++) << 8u;
value += this->_snes.bus.peek_v(pc) << 16u;
return Utility::to_hex(value, Utility::HexString::AsmPrefix);
}
std::string CPUDebug::_getDirectIndexedByXValue(uint24_t pc)
std::string CPUDebug::_getDirectIndexedByXValue(uint24_t pc) const
{
unsigned value = this->_bus->read(pc, true);
unsigned value = this->_snes.bus.peek_v(pc);
std::stringstream ss;
ss << "$" << std::hex << value << ", x";
return ss.str();
}
std::string CPUDebug::_getDirectIndexedByYValue(uint24_t pc)
std::string CPUDebug::_getDirectIndexedByYValue(uint24_t pc) const
{
unsigned value = this->_bus->read(pc, true);
unsigned value = this->_snes.bus.peek_v(pc);
std::stringstream ss;
ss << "$" << std::hex << value << ", y";
return ss.str();
}
std::string CPUDebug::_getDirectIndirectValue(uint24_t pc)
std::string CPUDebug::_getDirectIndirectValue(uint24_t pc) const
{
return "(" + Utility::to_hex(this->_bus->read(pc, true), Utility::AsmPrefix) + ")";
return "(" + Utility::to_hex(this->_snes.bus.peek_v(pc), Utility::AsmPrefix) + ")";
}
std::string CPUDebug::_getDirectIndirectLongValue(uint24_t pc)
std::string CPUDebug::_getDirectIndirectLongValue(uint24_t pc) const
{
return "[" + Utility::to_hex(this->_bus->read(pc, true), Utility::AsmPrefix) + "]";
return "[" + Utility::to_hex(this->_snes.bus.peek_v(pc), Utility::AsmPrefix) + "]";
}
std::string CPUDebug::_getAbsoluteIndexByXValue(uint24_t pc)
std::string CPUDebug::_getAbsoluteIndexByXValue(uint24_t pc) const
{
uint24_t value = this->_bus->read(pc, true) + (this->_bus->read(pc + 1, true) << 8u);
uint24_t value = this->_snes.bus.peek_v(pc) + (this->_snes.bus.peek_v(pc + 1) << 8u);
return Utility::to_hex(value, Utility::HexString::AsmPrefix) + ", x";
}
std::string CPUDebug::_getAbsoluteIndexByYValue(uint24_t pc)
std::string CPUDebug::_getAbsoluteIndexByYValue(uint24_t pc) const
{
uint24_t value = this->_bus->read(pc, true) + (this->_bus->read(pc + 1, true) << 8u);
uint24_t value = this->_snes.bus.peek_v(pc) + (this->_snes.bus.peek_v(pc + 1) << 8u);
return Utility::to_hex(value, Utility::HexString::AsmPrefix) + ", y";
}
std::string CPUDebug::_getAbsoluteIndexByXLongValue(uint24_t pc)
std::string CPUDebug::_getAbsoluteIndexByXLongValue(uint24_t pc) const
{
unsigned value = this->_bus->read(pc++, true);
value += this->_bus->read(pc++, true) << 8u;
value += this->_bus->read(pc, true) << 16u;
unsigned value = this->_snes.bus.peek_v(pc++);
value += this->_snes.bus.peek_v(pc++) << 8u;
value += this->_snes.bus.peek_v(pc) << 16u;
return Utility::to_hex(value, Utility::HexString::AsmPrefix) + ", x";
}
std::string CPUDebug::_getDirectIndexedByXIndirectValue(uint24_t pc)
std::string CPUDebug::_getDirectIndexedByXIndirectValue(uint24_t pc) const
{
unsigned value = this->_bus->read(pc, true);
unsigned value = this->_snes.bus.peek_v(pc);
std::stringstream ss;
ss << "($" << std::hex << value << ", x)";
return ss.str();
}
std::string CPUDebug::_getDirectIndirectIndexedByYValue(uint24_t pc)
std::string CPUDebug::_getDirectIndirectIndexedByYValue(uint24_t pc) const
{
unsigned value = this->_bus->read(pc, true);
unsigned value = this->_snes.bus.peek_v(pc);
std::stringstream ss;
ss << "($" << std::hex << value << "), y";
return ss.str();
}
std::string CPUDebug::_getDirectIndirectIndexedByYLongValue(uint24_t pc)
std::string CPUDebug::_getDirectIndirectIndexedByYLongValue(uint24_t pc) const
{
unsigned value = this->_bus->read(pc, true);
unsigned value = this->_snes.bus.peek_v(pc);
std::stringstream ss;
ss << "[$" << std::hex << value << "], y";
return ss.str();
}
std::string CPUDebug::_getStackRelativeValue(uint24_t pc)
std::string CPUDebug::_getStackRelativeValue(uint24_t pc) const
{
return Utility::to_hex(this->_bus->read(pc, true), Utility::AsmPrefix) + ", s";
return Utility::to_hex(this->_snes.bus.peek_v(pc), Utility::AsmPrefix) + ", s";
}
std::string CPUDebug::_getStackRelativeIndirectIndexedByYValue(uint24_t pc)
std::string CPUDebug::_getStackRelativeIndirectIndexedByYValue(uint24_t pc) const
{
return "(" + Utility::to_hex(this->_bus->read(pc, true), Utility::AsmPrefix) + ", s), y";
return "(" + Utility::to_hex(this->_snes.bus.peek_v(pc), Utility::AsmPrefix) + ", s), y";
}
std::string CPUDebug::_getAbsoluteIndirectValue(uint24_t pc)
std::string CPUDebug::_getAbsoluteIndirectValue(uint24_t pc) const
{
uint24_t value = this->_bus->read(pc, true) + (this->_bus->read(pc + 1, true) << 8u);
uint24_t value = this->_snes.bus.peek_v(pc) + (this->_snes.bus.peek_v(pc + 1) << 8u);
return "(" + Utility::to_hex(value, Utility::HexString::AsmPrefix) + ")";
}
std::string CPUDebug::_getAbsoluteIndirectLongValue(uint24_t pc)
std::string CPUDebug::_getAbsoluteIndirectLongValue(uint24_t pc) const
{
unsigned value = this->_bus->read(pc++, true);
value += this->_bus->read(pc++, true) << 8u;
value += this->_bus->read(pc, true) << 16u;
unsigned value = this->_snes.bus.peek_v(pc++);
value += this->_snes.bus.peek_v(pc++) << 8u;
value += this->_snes.bus.peek_v(pc) << 16u;
return "(" + Utility::to_hex(value, Utility::HexString::AsmPrefix) + ")";
}
std::string CPUDebug::_getAbsoluteIndirectIndexedByXValue(uint24_t pc)
std::string CPUDebug::_getAbsoluteIndirectIndexedByXValue(uint24_t pc) const
{
uint24_t value = this->_bus->read(pc, true) + (this->_bus->read(pc + 1, true) << 8u);
uint24_t value = this->_snes.bus.peek_v(pc) + (this->_snes.bus.peek_v(pc + 1) << 8u);
return "(" + Utility::to_hex(value, Utility::HexString::AsmPrefix) + ", x)";
}
}
}// namespace ComSquare::Debugger

View File

@@ -6,7 +6,7 @@
#include "Utility/Utility.hpp"
#include "WlaDx.hpp"
namespace ComSquare::Debugger
namespace ComSquare::Debugger::CPU
{
std::vector<Label> WlaDx::parse(std::ifstream &symbolFile)
{
@@ -16,6 +16,7 @@ namespace ComSquare::Debugger
std::smatch match;
std::regex re(R"(\[(\S+)\])");
return ret;
while (symbolFile) {
if (line.empty()) {
std::getline(symbolFile, line);

View File

@@ -4,10 +4,10 @@
#pragma once
#include "../CPUDebug.hpp"
#include "Debugger/CPU/CPUDebug.hpp"
#include <fstream>
namespace ComSquare::Debugger
namespace ComSquare::Debugger::CPU
{
//! @brief Class to parse WLA-DX symbol files.
class WlaDx {

View File

@@ -2,33 +2,34 @@
// Created by anonymus-raccoon on 3/20/20.
//
#ifndef COMSQUARE_CLOSABLEWINDOW_HPP
#define COMSQUARE_CLOSABLEWINDOW_HPP
#pragma once
#include <QtWidgets/QMainWindow>
#include <utility>
namespace ComSquare::Debugger
{
template <typename T>
class ClosableWindow : public QMainWindow {
class ClosableWindow : public QMainWindow
{
protected:
void closeEvent(QCloseEvent *) override
{
(this->_obj.*this->_onClose)();
this->_onClose();
}
private:
T &_obj;
void (T::*_onClose)();
std::function<void ()> _onClose;
public:
explicit ClosableWindow(T &obj, void (T::*onClose)())
: _obj(obj), _onClose(onClose)
{ }
explicit ClosableWindow(std::function<void ()> onClose)
: _onClose(std::move(onClose))
{
this->setContextMenuPolicy(Qt::NoContextMenu);
this->setAttribute(Qt::WA_QuitOnClose, false);
this->setAttribute(Qt::WA_DeleteOnClose);
}
ClosableWindow(const ClosableWindow &) = delete;
ClosableWindow &operator=(const ClosableWindow &) = delete;
~ClosableWindow() override = default;
};
}
#endif //COMSQUARE_CLOSABLEWINDOW_HPP
}// namespace ComSquare::Debugger

View File

@@ -3,21 +3,17 @@
//
#include "HeaderViewer.hpp"
#include "../Utility/Utility.hpp"
#include "../SNES.hpp"
#include "Utility/Utility.hpp"
#include "SNES.hpp"
namespace ComSquare::Debugger
{
HeaderViewer::HeaderViewer(ComSquare::SNES &snes)
: _window(new ClosableWindow<HeaderViewer>(*this, &HeaderViewer::disableDebugger)),
_snes(snes),
_cartridge(*snes.cartridge),
_ui()
: _window(new ClosableWindow([&snes] { snes.disableHeaderViewer(); })),
_snes(snes),
_cartridge(snes.cartridge),
_ui()
{
this->_window->setContextMenuPolicy(Qt::NoContextMenu);
this->_window->setAttribute(Qt::WA_QuitOnClose, false);
this->_window->setAttribute(Qt::WA_DeleteOnClose);
this->_ui.setupUi(this->_window);
this->_ui.nameLineEdit->setText(this->_cartridge.header.gameName.c_str());
std::string memType;
@@ -55,11 +51,6 @@ namespace ComSquare::Debugger
this->_window->show();
}
void HeaderViewer::disableDebugger()
{
this->_snes.disableHeaderViewer();
}
void HeaderViewer::focus()
{
this->_window->activateWindow();

View File

@@ -2,12 +2,11 @@
// Created by anonymus-raccoon on 2/18/20.
//
#ifndef COMSQUARE_HEADERVIEWER_HPP
#define COMSQUARE_HEADERVIEWER_HPP
#pragma once
#include <QtWidgets/QMainWindow>
#include "../Cartridge/Cartridge.hpp"
#include "../../ui/ui_cartridgeView.h"
#include "Cartridge/Cartridge.hpp"
#include "ui/ui_cartridgeView.h"
#include "ClosableWindow.hpp"
namespace ComSquare
@@ -16,19 +15,17 @@ namespace ComSquare
namespace Debugger
{
//! @brief Window that show the header of the currently running game.
class HeaderViewer {
class HeaderViewer
{
private:
//! @brief The QT window for this debugger.
ClosableWindow<HeaderViewer> *_window{};
ClosableWindow *_window;
//! @brief A reference to the snes (to disable the debugger).
SNES &_snes;
//! @brief The cartridge containing the header.
Cartridge::Cartridge &_cartridge;
//! @brief The layout of the viewer.
Ui::CatridgeView _ui;
public slots:
//! @brief Called when the window is closed. Turn off the debugger and revert to a basic CPU.
void disableDebugger();
public:
//! @brief Focus the debugger's window.
void focus();
@@ -39,6 +36,4 @@ namespace ComSquare
~HeaderViewer() = default;
};
}
}
#endif //COMSQUARE_HEADERVIEWER_HPP
}

View File

@@ -3,25 +3,19 @@
//
#include "MemoryBusDebug.hpp"
#include "../SNES.hpp"
#include "../Utility/Utility.hpp"
#include "../Exceptions/InvalidAction.hpp"
#include "../Exceptions/InvalidAddress.hpp"
#include "SNES.hpp"
#include "Utility/Utility.hpp"
#include "Exceptions/InvalidAction.hpp"
namespace ComSquare::Debugger
{
MemoryBusDebug::MemoryBusDebug(SNES &snes, const Memory::MemoryBus &bus)
: MemoryBus(bus),
_window(new ClosableWindow<MemoryBusDebug>(*this, &MemoryBusDebug::disableViewer)),
_snes(snes),
_ui(),
_model(),
_proxy(this->_model)
MemoryBusDebug::MemoryBusDebug(SNES &snes, Memory::IMemoryBus &bus)
: _window(new ClosableWindow([&snes] { snes.disableMemoryBusDebugging(); })),
_bus(bus),
_ui(),
_model(),
_proxy(this->_model)
{
this->_window->setContextMenuPolicy(Qt::NoContextMenu);
this->_window->setAttribute(Qt::WA_QuitOnClose, false);
this->_window->setAttribute(Qt::WA_DeleteOnClose);
this->_ui.setupUi(this->_window);
this->_proxy.setSourceModel(&this->_model);
this->_ui.log->setModel(&this->_proxy);
@@ -133,178 +127,170 @@ namespace ComSquare::Debugger
this->_window->show();
}
void MemoryBusDebug::disableViewer()
{
this->_snes.disableMemoryBusDebugging();
}
void MemoryBusDebug::focus()
{
this->_window->activateWindow();
}
bool MemoryBusDebug::isDebugger()
{
return true;
}
uint8_t MemoryBusDebug::read(uint24_t addr)
{
if (this->forceSilence)
return MemoryBus::read(addr);
return this->read(addr, false);
uint8_t value = this->_bus.read(addr);
this->_model.log(BusLog(false, addr, this->getAccessor(addr), value, value));
return value;
}
uint8_t MemoryBusDebug::read(uint24_t addr, bool silence)
std::optional<uint8_t> MemoryBusDebug::peek(uint24_t addr)
{
if (!silence && !this->forceSilence) {
auto accessor = this->getAccessor(addr);
if (!accessor) {
this->_model.log(BusLog(true, addr, accessor, this->_openBus, this->_openBus));
} else {
uint8_t value = accessor->read(accessor->getRelativeAddress(addr));
this->_model.log(BusLog(false, addr, accessor, value, value));
}
}
return MemoryBus::read(addr, silence);
return this->_bus.peek(addr);
}
uint8_t MemoryBusDebug::peek_v(uint24_t addr)
{
return this->_bus.peek_v(addr);
}
void MemoryBusDebug::write(uint24_t addr, uint8_t data)
{
auto accessor = this->getAccessor(addr);
std::optional<uint8_t> value = std::nullopt;
try {
if (accessor)
value = accessor->read(accessor->getRelativeAddress(addr));
} catch (InvalidAddress &) {
value = std::nullopt;
}
if (!forceSilence)
this->_model.log(BusLog(true, addr, accessor, value, data));
MemoryBus::write(addr, data);
std::optional<uint8_t> value = this->peek(addr);
this->_model.log(BusLog(true, addr, this->getAccessor(addr), value, data));
this->_bus.write(addr, data);
}
BusLog::BusLog(bool _write, uint24_t _addr, std::shared_ptr<Memory::IMemory> &_accessor, std::optional<uint8_t> _oldData, uint8_t _newData) :
write(_write), addr(_addr), accessor(std::move(_accessor)), oldData(_oldData), newData(_newData)
Memory::IMemory *MemoryBusDebug::getAccessor(uint24_t addr)
{
return this->_bus.getAccessor(addr);
}
BusLog::BusLog(bool _write, uint24_t _addr,
Memory::IMemory *_accessor,
std::optional<uint8_t> _oldData,
uint8_t _newData)
: write(_write),
addr(_addr),
accessor(_accessor),
oldData(_oldData),
newData(_newData)
{}
}
int BusLogModel::rowCount(const QModelIndex &) const
{
return this->_logs.size();
}
int BusLogModel::columnCount(const QModelIndex &) const
{
return this->column;
}
QVariant BusLogModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::TextAlignmentRole)
return Qt::AlignCenter;
if (role != Qt::DisplayRole)
return QVariant();
ComSquare::Debugger::BusLog log = this->_logs[index.row()];
switch (index.column()) {
case 0:
return QString(log.write ? "Write" : "Read");
case 1:
return QString(ComSquare::Utility::to_hex(log.addr).c_str());
case 2:
return QString(log.accessor ? log.accessor->getName().c_str() : "Bus");
case 3: {
uint24_t addr = log.accessor->getRelativeAddress(log.addr);
return QString(log.accessor ? log.accessor->getValueName(addr).c_str() : "Open bus");
int BusLogModel::rowCount(const QModelIndex &) const
{
return static_cast<int>(this->_logs.size());
}
case 4:
if (!log.oldData)
return QString("???");
return QString(ComSquare::Utility::to_hex(*log.oldData).c_str());
case 5:
return QString(ComSquare::Utility::to_hex(log.newData).c_str());
default:
return QVariant();
int BusLogModel::columnCount(const QModelIndex &) const
{
return this->column;
}
}
QVariant BusLogModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole || orientation == Qt::Vertical)
return QVariant();
switch (section) {
case 0:
return QString("Type");
case 1:
return QString("Address");
case 2:
return QString("Component");
case 3:
return QString("Data Name");
case 4:
return QString("Old Data");
case 5:
return QString("New Data");
default:
return QString("");
QVariant BusLogModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::TextAlignmentRole)
return Qt::AlignCenter;
if (role != Qt::DisplayRole)
return QVariant();
BusLog log = this->_logs[index.row()];
switch (index.column()) {
case 0:
return QString(log.write ? "Write" : "Read");
case 1:
return QString(ComSquare::Utility::to_hex(log.addr).c_str());
case 2:
return QString(log.accessor ? log.accessor->getName().c_str() : "Bus");
case 3: {
uint24_t addr = log.accessor->getRelativeAddress(log.addr);
return QString(log.accessor ? log.accessor->getValueName(addr).c_str() : "Open bus");
}
case 4:
if (!log.oldData)
return QString("???");
return QString(ComSquare::Utility::to_hex(*log.oldData).c_str());
case 5:
return QString(ComSquare::Utility::to_hex(log.newData).c_str());
default:
return QVariant();
}
}
}
void BusLogModel::log(const ComSquare::Debugger::BusLog& log)
{
int row = this->_logs.size();
this->beginInsertRows(QModelIndex(), row, row);
this->_logs.push_back(log);
this->insertRow(row);
this->endInsertRows();
}
ComSquare::Debugger::BusLog BusLogModel::getLogAt(int index)
{
return this->_logs[index];
}
void BusLogModel::clearLogs()
{
this->beginResetModel();
this->_logs.clear();
this->endResetModel();
}
BusLoggerProxy::BusLoggerProxy(BusLogModel &parent) : QSortFilterProxyModel(), _parent(parent) {}
bool BusLoggerProxy::filterAcceptsRow(int sourceRow, const QModelIndex &) const
{
ComSquare::Debugger::BusLog log = this->_parent.getLogAt(sourceRow);
if (!log.accessor)
return true;
ComSquare::Component component = log.accessor->getComponent();
switch (component) {
case ComSquare::Component::Cpu:
return this->filters[log.write].cpu;
case ComSquare::Component::Ppu:
return this->filters[log.write].ppu;
case ComSquare::Component::Apu:
return this->filters[log.write].apu;
case ComSquare::Component::Rom:
return this->filters[log.write].rom;
case ComSquare::Component::WRam:
return this->filters[log.write].wram;
case ComSquare::Component::VRam:
return this->filters[log.write].vram;
case ComSquare::Component::CGRam:
return this->filters[log.write].cgram;
case ComSquare::Component::OAMRam:
return this->filters[log.write].oamram;
case ComSquare::Component::SRam:
return this->filters[log.write].sram;
default:
return true;
QVariant BusLogModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole || orientation == Qt::Vertical)
return QVariant();
switch (section) {
case 0:
return QString("Type");
case 1:
return QString("Address");
case 2:
return QString("Component");
case 3:
return QString("Data Name");
case 4:
return QString("Old Data");
case 5:
return QString("New Data");
default:
return QString("");
}
}
}
void BusLoggerProxy::refresh()
{
this->invalidateFilter();
}
void BusLogModel::log(const BusLog &log)
{
int row = static_cast<int>(this->_logs.size());
this->beginInsertRows(QModelIndex(), row, row);
this->_logs.push_back(log);
this->insertRow(row);
this->endInsertRows();
}
BusLog BusLogModel::getLogAt(int index)
{
return this->_logs[index];
}
void BusLogModel::clearLogs()
{
this->beginResetModel();
this->_logs.clear();
this->endResetModel();
}
BusLoggerProxy::BusLoggerProxy(BusLogModel &parent)
: QSortFilterProxyModel(), _parent(parent)
{}
bool BusLoggerProxy::filterAcceptsRow(int sourceRow, const QModelIndex &) const
{
BusLog log = this->_parent.getLogAt(sourceRow);
if (!log.accessor)
return true;
Component component = log.accessor->getComponent();
switch (component) {
case Component::Cpu:
return this->filters[log.write].cpu;
case Component::Ppu:
return this->filters[log.write].ppu;
case Component::Apu:
return this->filters[log.write].apu;
case Component::Rom:
return this->filters[log.write].rom;
case Component::WRam:
return this->filters[log.write].wram;
case Component::VRam:
return this->filters[log.write].vram;
case Component::CGRam:
return this->filters[log.write].cgram;
case Component::OAMRam:
return this->filters[log.write].oamram;
case Component::SRam:
return this->filters[log.write].sram;
default:
return true;
}
}
void BusLoggerProxy::refresh()
{
this->invalidateFilter();
}
}

View File

@@ -2,35 +2,37 @@
// Created by anonymus-raccoon on 3/20/20.
//
#ifndef COMSQUARE_MEMORYBUSDEBUG_HPP
#define COMSQUARE_MEMORYBUSDEBUG_HPP
#pragma once
#include <QtWidgets/QMainWindow>
#include <QtCore/QSortFilterProxyModel>
#include "../Memory/MemoryBus.hpp"
#include "../../ui/ui_busView.h"
#include "Memory/MemoryBus.hpp"
#include "Memory/IMemory.hpp"
#include "ui/ui_busView.h"
#include "ClosableWindow.hpp"
#include <optional>
namespace ComSquare::Debugger
{
//! @brief The struct used to represent memory bus logs.
struct BusLog {
struct BusLog
{
BusLog(bool write,
uint24_t addr,
std::shared_ptr<Memory::IMemory> &accessor,
std::optional<uint8_t> oldData,
uint8_t newData);
uint24_t addr,
Memory::IMemory *accessor,
std::optional<uint8_t> oldData,
uint8_t newData);
bool write;
uint24_t addr;
std::shared_ptr<Memory::IMemory> accessor;
Memory::IMemory *accessor;
std::optional<uint8_t> oldData;
uint8_t newData;
};
//! @brief The struct representing filters of the memory bus's logger.
struct BusLoggerFilters {
struct BusLoggerFilters
{
bool cpu = true;
bool apu = true;
bool ppu = true;
@@ -41,75 +43,75 @@ namespace ComSquare::Debugger
bool oamram = true;
bool cgram = true;
};
}
//! @brief The qt model that bind the logs to the view.
class BusLogModel : public QAbstractTableModel
{
//! @brief The qt model that bind the logs to the view.
class BusLogModel : public QAbstractTableModel
{
Q_OBJECT
private:
//! @brief The logs to display.
std::vector<ComSquare::Debugger::BusLog> _logs;
public:
BusLogModel() = default;
BusLogModel(const BusLogModel &) = delete;
const BusLogModel &operator=(const BusLogModel &) = delete;
~BusLogModel() override = default;
private:
//! @brief The logs to display.
std::vector<ComSquare::Debugger::BusLog> _logs;
public:
BusLogModel() = default;
BusLogModel(const BusLogModel &) = delete;
const BusLogModel &operator=(const BusLogModel &) = delete;
~BusLogModel() override = default;
//! @brief The number of column;
const int column = 6;
//! @brief The number of column;
const int column = 6;
//! @brief Add a log to the model
void log(const ComSquare::Debugger::BusLog& log);
//! @brief Add a log to the model
void log(const ComSquare::Debugger::BusLog &log);
//! @brief Get a log at an index.
ComSquare::Debugger::BusLog getLogAt(int index);
//! @brief Clear all the logs
void clearLogs();
//! @brief Get a log at an index.
ComSquare::Debugger::BusLog getLogAt(int index);
//! @brief Clear all the logs
void clearLogs();
//! @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 number of row the table has.
[[nodiscard]] int rowCount(const QModelIndex &parent) const override;
//! @brief The number of column the table has.
[[nodiscard]] int columnCount(const QModelIndex &parent) const override;
//! @brief Return a data representing the table cell.
[[nodiscard]] QVariant data(const QModelIndex &index, int role) const override;
//! @brief Override the headers to use hex values.
[[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
};
//! @brief A class to filter logs from the memory bus's debugger.
class BusLoggerProxy : public QSortFilterProxyModel {
//! @brief A class to filter logs from the memory bus's debugger.
class BusLoggerProxy : public QSortFilterProxyModel
{
Q_OBJECT
private:
//! @brief The parent to get the original data for filters
BusLogModel &_parent;
protected:
//! @brief Function that filter logs.
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
public:
//! @brief Currently enabled filters, index 0 is for reads, index 1 for writes.
ComSquare::Debugger::BusLoggerFilters filters[2] = {ComSquare::Debugger::BusLoggerFilters(), ComSquare::Debugger::BusLoggerFilters()};
private:
//! @brief The parent to get the original data for filters
BusLogModel &_parent;
protected:
//! @brief Function that filter logs.
[[nodiscard]] bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
public:
//! @brief Currently enabled filters, index 0 is for reads, index 1 for writes.
ComSquare::Debugger::BusLoggerFilters filters[2] = {
ComSquare::Debugger::BusLoggerFilters(),
ComSquare::Debugger::BusLoggerFilters()
};
//! @brief Refresh the view after a change of filters.
void refresh();
//! @brief Refresh the view after a change of filters.
void refresh();
explicit BusLoggerProxy(BusLogModel &parent);
BusLoggerProxy(const BusLoggerProxy &) = delete;
const BusLoggerProxy &operator=(const BusLoggerProxy &) = delete;
~BusLoggerProxy() override = default;
};
explicit BusLoggerProxy(BusLogModel &parent);
BusLoggerProxy(const BusLoggerProxy &) = delete;
const BusLoggerProxy &operator=(const BusLoggerProxy &) = delete;
~BusLoggerProxy() override = default;
};
namespace ComSquare::Debugger
{
//! @brief window that allow the user to view all data going through the memory bus.
class MemoryBusDebug : public Memory::MemoryBus {
class MemoryBusDebug : public Memory::IMemoryBus
{
private:
//! @brief The QT window for this debugger.
ClosableWindow<MemoryBusDebug> *_window;
//! @brief A reference to the snes (to disable the debugger).
SNES &_snes;
ClosableWindow *_window;
//! @brief A reference to the underlying bus..
Memory::IMemoryBus &_bus;
//! @brief A widget that contain the whole UI.
Ui::BusView _ui;
//! @brief The Log visualizer model for QT.
@@ -117,35 +119,39 @@ namespace ComSquare::Debugger
//! @brief A QT proxy to filter the logs.
BusLoggerProxy _proxy;
public:
//! @brief Called when the window is closed. Turn off the debugger and revert to a basic CPU.
void disableViewer();
public:
explicit MemoryBusDebug(SNES &snes, const Memory::MemoryBus &bus);
explicit MemoryBusDebug(SNES &snes, Memory::IMemoryBus &bus);
MemoryBusDebug(const MemoryBusDebug &) = delete;
MemoryBusDebug &operator=(const MemoryBusDebug &) = delete;
~MemoryBusDebug() = default;
~MemoryBusDebug() override = default;
//! @brief Read data at a global address and log it to the debugger.
//! @brief Read data at a global address. This form allow read to be silenced.
//! @param addr The address to read from.
//! @throws InvalidAddress If the address is not mapped to the bus, this exception is thrown.
//! @return The value that the component returned for this address. If the address was mapped to ram, it simply returned the value. If the address was mapped to a register the component returned the register.
uint8_t read(uint24_t addr) override;
//! @brief Read data at a global address and log it to the debugger.
//! @brief This as the same purpose as a read but it does not change the open bus and won't throw an exception.
//! @param addr The address to read from.
//! @return The value that the component returned for this address. If the address was mapped to ram, it simply returned the value. If the address was mapped to a register the component returned the register.
uint8_t read(uint24_t addr, bool silence) override;
std::optional<uint8_t> peek(uint24_t addr) override;
//! @brief Write a data to a global address and log it to the debugger.
//! @brief This as the same purpose as a read but it does not change the open bus and won't throw an exception.
//! @param addr The address to read from.
//! @return The value that the component returned for this address. If the address was mapped to ram, it simply returned the value. If the address was mapped to a register the component returned the register.
//! @note If the value address is not mapped, 0 is returned instead of nullopt.
uint8_t peek_v(uint24_t addr) override;
//! @brief Write a data to a global address.
//! @param addr The address to write to.
//! @param data The data to write.
void write(uint24_t addr, uint8_t data) override;
//! @brief Helper function to get the components that is responsible of read/write at an address.
//! @param addr The address you want to look for.
//! @return The components responsible for the address param or nullptr if none was found.
Memory::IMemory *getAccessor(uint24_t addr) override;
//! @brief Focus the debugger's window.
void focus();
//! @brief Return true if the Bus is overloaded with debugging features.
bool isDebugger() override;
};
}
#endif //COMSQUARE_MEMORYBUSDEBUG_HPP

View File

@@ -4,77 +4,71 @@
#include <iostream>
#include <cmath>
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QSpinBox>
#include <QMessageBox>
#include "MemoryViewer.hpp"
#include "../SNES.hpp"
#include "../Memory/MemoryShadow.hpp"
#include "../Exceptions/InvalidAddress.hpp"
MemoryViewerModel::MemoryViewerModel(std::shared_ptr<Ram> memory, QObject *parent) :
QAbstractTableModel(parent),
_memory(std::move(memory))
{ }
int MemoryViewerModel::rowCount(const QModelIndex &) const
{
return this->_memory->getSize() / 16u;
}
int MemoryViewerModel::columnCount(const QModelIndex &parent) const
{
if (parent.row() == this->rowCount(parent) - 1)
return this->_memory->getSize() - (parent.row() << 8u);
return 16;
}
QVariant MemoryViewerModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::TextAlignmentRole)
return Qt::AlignCenter;
if (role != Qt::DisplayRole)
return QVariant();
char buf[3];
snprintf(buf, 3, "%02X", this->_memory->read((index.row() << 4u) + index.column()));
return QString(buf);
}
QVariant MemoryViewerModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
if (orientation == Qt::Horizontal) {
char buf[2];
snprintf(buf, 2, "%1X", section);
return QString(buf);
} else {
char buf[6];
snprintf(buf, 6, "%0*Xx", this->_headerIndentSize, section);
return QString(buf);
}
}
void MemoryViewerModel::setMemory(std::shared_ptr<Ram> memory)
{
this->_memory = std::move(memory);
this->_headerIndentSize = this->_memory->getSize() >= 0x10000 ? 4 : 3;
emit this->layoutChanged();
}
#include "SNES.hpp"
#include "Exceptions/InvalidAddress.hpp"
#include "Utility/Utility.hpp"
namespace ComSquare::Debugger
{
MemoryViewer::MemoryViewer(ComSquare::SNES &snes, Memory::MemoryBus &bus) :
_window(new ClosableWindow<MemoryViewer>(*this, &MemoryViewer::disableViewer)),
_snes(snes),
_bus(bus),
_ui(),
_model(snes.wram)
{
this->_window->setContextMenuPolicy(Qt::NoContextMenu);
this->_window->setAttribute(Qt::WA_QuitOnClose, false);
this->_window->setAttribute(Qt::WA_DeleteOnClose);
MemoryViewerModel::MemoryViewerModel(Ram::Ram &memory, QObject *parent)
: QAbstractTableModel(parent),
_memory(memory)
{}
int MemoryViewerModel::rowCount(const QModelIndex &) const
{
return static_cast<int>(this->_memory.get().getSize() / 16u);
}
int MemoryViewerModel::columnCount(const QModelIndex &parent) const
{
if (parent.row() == this->rowCount(parent) - 1)
return static_cast<int>(this->_memory.get().getSize() - (parent.row() << 8u));
return 16;
}
QVariant MemoryViewerModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::TextAlignmentRole)
return Qt::AlignCenter;
if (role != Qt::DisplayRole)
return QVariant();
auto value = this->_memory.get().read((index.row() << 4u) + index.column());
return QString(Utility::to_hex(value, Utility::NoPrefix).c_str());
}
QVariant MemoryViewerModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
if (orientation == Qt::Horizontal) {
char buf[2];
snprintf(buf, 2, "%1X", section);
return QString(buf);
} else {
char buf[6];
snprintf(buf, 6, "%0*Xx", this->_headerIndentSize, section);
return QString(buf);
}
}
void MemoryViewerModel::setMemory(Ram::Ram &memory)
{
this->_memory = memory;
this->_headerIndentSize = this->_memory.get().getSize() >= 0x10000 ? 4 : 3;
emit this->layoutChanged();
}
MemoryViewer::MemoryViewer(ComSquare::SNES &snes, Memory::MemoryBus &bus)
: _window(new ClosableWindow([&snes] { snes.disableRamViewer(); })),
_snes(snes),
_bus(bus),
_ui(),
_model(snes.wram)
{
this->_ui.setupUi(this->_window);
this->_ui.tableView->setModel(&this->_model);
this->_ui.tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
@@ -90,11 +84,6 @@ namespace ComSquare::Debugger
this->_window->show();
}
void MemoryViewer::disableViewer()
{
this->_snes.disableRamViewer();
}
void MemoryViewer::changeRam(int id)
{
switch (id) {
@@ -109,13 +98,13 @@ namespace ComSquare::Debugger
this->_model.setMemory(this->_snes.cartridge);
break;
case 3:
this->_model.setMemory(this->_snes.ppu->cgram);
this->_model.setMemory(this->_snes.ppu.cgram);
break;
case 4:
this->_model.setMemory(this->_snes.ppu->vram);
this->_model.setMemory(this->_snes.ppu.vram);
break;
case 5:
this->_model.setMemory(this->_snes.ppu->oamram);
this->_model.setMemory(this->_snes.ppu.oamram);
break;
}
}
@@ -141,7 +130,6 @@ namespace ComSquare::Debugger
dialogUI.spinBox->setFont(font);
dialogUI.spinBox->selectAll();
dialogUI.checkBox->setChecked(isAbsolute);
if (dialog.exec() != QDialog::Accepted)
return;
long value = std::strtol(dialogUI.spinBox->text().toStdString().c_str() + 1, nullptr, 16);
@@ -164,10 +152,9 @@ namespace ComSquare::Debugger
unsigned MemoryViewer::switchToAddrTab(uint24_t addr)
{
std::shared_ptr<Memory::IMemory> accessor = this->_bus.getAccessor(addr);
Memory::IMemory *accessor = this->_bus.getAccessor(addr);
if (!accessor)
throw InvalidAddress("Memory viewer switch to address", addr);
switch (accessor->getComponent()) {
case WRam:
this->_ui.tabs->setCurrentIndex(0);

View File

@@ -2,59 +2,57 @@
// Created by anonymus-raccoon on 2/17/20.
//
#ifndef COMSQUARE_MEMORYVIEWER_HPP
#define COMSQUARE_MEMORYVIEWER_HPP
#pragma once
#include <QtWidgets/QMainWindow>
#include "../../ui/ui_ramView.h"
#include "../../ui/ui_gotoDialog.h"
#include "../Ram/Ram.hpp"
#include "../Memory/MemoryBus.hpp"
#include "ui/ui_ramView.h"
#include "ui/ui_gotoDialog.h"
#include "Ram/Ram.hpp"
#include "Memory/MemoryBus.hpp"
#include "ClosableWindow.hpp"
#include <memory>
using ComSquare::Ram::Ram;
//! @brief The qt model that bind the ram to the view.
class MemoryViewerModel : public QAbstractTableModel
{
Q_OBJECT
private:
//! @brief The ram to watch.
std::shared_ptr<Ram> _memory;
//! @brief The number of char inside the left header number.
int _headerIndentSize = 3;
public:
//! @brief Change the ram currently watched.
void setMemory(std::shared_ptr<Ram> memory);
explicit MemoryViewerModel(std::shared_ptr<Ram> memory, QObject *parent = nullptr);
MemoryViewerModel(const MemoryViewerModel &) = delete;
const MemoryViewerModel &operator=(const MemoryViewerModel &) = delete;
~MemoryViewerModel() 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 represneting 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;
};
#include <QAbstractTableModel>
namespace ComSquare
{
class SNES;
namespace Debugger
{
//! @brief The qt model that bind the ram to the view.
class MemoryViewerModel : public QAbstractTableModel
{
Q_OBJECT
private:
//! @brief The ram to watch.
std::reference_wrapper<Ram::Ram> _memory;
//! @brief The number of char inside the left header number.
int _headerIndentSize = 3;
public:
//! @brief Change the ram currently watched.
void setMemory(Ram::Ram &memory);
explicit MemoryViewerModel(Ram::Ram &memory, QObject *parent = nullptr);
MemoryViewerModel(const MemoryViewerModel &) = delete;
const MemoryViewerModel &operator=(const MemoryViewerModel &) = delete;
~MemoryViewerModel() override = default;
//! @brief The number of row the table has.
[[nodiscard]] int rowCount(const QModelIndex &parent) const override;
//! @brief The number of column the table has.
[[nodiscard]] int columnCount(const QModelIndex &parent) const override;
//! @brief Return a data representing the table cell.
[[nodiscard]] QVariant data(const QModelIndex &index, int role) const override;
//! @brief Override the headers to use hex values.
[[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
};
//! @brief Class responsible of the Memory Viewer.
class MemoryViewer : public QObject {
class MemoryViewer : public QObject
{
Q_OBJECT
private:
//! @brief The QT window for this debugger.
ClosableWindow<MemoryViewer> *_window;
ClosableWindow *_window;
//! @brief SNES containing all rams to view.
SNES &_snes;
//! @brief The memory bus used to get the view for a given address.
@@ -63,11 +61,9 @@ namespace ComSquare
Ui::RamView _ui;
//! @brief The Ram visualizer model for QT.
MemoryViewerModel _model;
//! @brief Helper function to create the goto dialog.
void _internalGoto(bool isAbsolute);
public slots:
//! @brief Called when the window is closed. Turn off the debugger.
void disableViewer();
public:
//! @brief Select the memory tab corresponding to a 24 bit address (map the address via the bus).
//! @return The address converted to the new tab's locale space.
@@ -79,7 +75,6 @@ namespace ComSquare
void gotoAddr();
//! @brief Create a popup asking you where you want to jump to with the absolute mode selected.
void gotoAbsoluteAddr();
//! @brief Focus the memory viewer's window.
void focus();
@@ -89,6 +84,4 @@ namespace ComSquare
~MemoryViewer() override = default;
};
}
}
#endif //COMSQUARE_MEMORYVIEWER_HPP
}

View File

@@ -3,21 +3,18 @@
//
#include "RegisterViewer.hpp"
#include "../SNES.hpp"
#include "../Utility/Utility.hpp"
#include "SNES.hpp"
#include "Utility/Utility.hpp"
#include <sstream>
#include <utility>
namespace ComSquare::Debugger
{
RegisterViewer::RegisterViewer(SNES &snes)
: _window(new ClosableWindow<RegisterViewer>(*this, &RegisterViewer::disableDebugger)),
_ui(),
_snes(snes)
: _window(new ClosableWindow([&snes] { snes.disableRegisterViewer(); })),
_ui(),
_snes(snes)
{
this->_window->setContextMenuPolicy(Qt::NoContextMenu);
this->_window->setAttribute(Qt::WA_QuitOnClose, false);
this->_window->setAttribute(Qt::WA_DeleteOnClose);
this->_ui.setupUi(this->_window);
this->_setupUi();
this->_window->show();
@@ -26,7 +23,6 @@ namespace ComSquare::Debugger
void RegisterViewer::_setupUi()
{
this->_models.clear();
std::array<QTableView *, 8> channels = {
this->_ui.dmaChannel1,
this->_ui.dmaChannel2,
@@ -41,28 +37,28 @@ namespace ComSquare::Debugger
for (int i = 0; i < 8; i++) {
model = new RegistersViewerModel(this->_snes);
model->addRegister(Register(0x420B, std::string(":") + std::to_string(i), "Enabled", [i](SNES &snes) {
return snes.cpu->_dmaChannels[i].enabled;
return snes.cpu._dmaChannels[i].enabled;
}, nullptr, Boolean));
model->addRegister(Register(0x4302 + (i << 4u), "-4", "A address", [i](SNES &snes) {
return snes.cpu->_dmaChannels[i]._aAddress.raw;
return snes.cpu._dmaChannels[i]._aAddress.raw;
}, nullptr, TwentyFourBits));
model->addRegister(Register(0x4301 + (i << 4u), "", "B address", [i](SNES &snes) {
return 0x2100 | snes.cpu->_dmaChannels[i]._port;
return 0x2100 | snes.cpu._dmaChannels[i]._port;
}, nullptr, SixteenBits));
model->addRegister(Register(0x4305 + (i << 4u), "-6", "Count", [i](SNES &snes) {
return snes.cpu->_dmaChannels[i]._count.raw;
return snes.cpu._dmaChannels[i]._count.raw;
}, nullptr, SixteenBits));
model->addRegister(Register(0x4300 + (i << 4u), ":7", "B To A", [i](SNES &snes) {
return snes.cpu->_dmaChannels[i]._controlRegister.direction;
return snes.cpu._dmaChannels[i]._controlRegister.direction;
}, nullptr, Boolean));
model->addRegister(Register(0x4300 + (i << 4u), ":3", "Fixed", [i](SNES &snes) {
return snes.cpu->_dmaChannels[i]._controlRegister.fixed;
return snes.cpu._dmaChannels[i]._controlRegister.fixed;
}, nullptr, Boolean));
model->addRegister(Register(0x4300 + (i << 4u), ":4", "Decrement", [i](SNES &snes) {
return snes.cpu->_dmaChannels[i]._controlRegister.increment;
return snes.cpu._dmaChannels[i]._controlRegister.increment;
}, nullptr, Boolean));
model->addRegister(Register(0x4300 + (i << 4u), ":0-2", "Mode", [i](SNES &snes) {
return snes.cpu->_dmaChannels[i]._controlRegister.increment;
return snes.cpu._dmaChannels[i]._controlRegister.increment;
}, nullptr, EightBits));
channels[i]->setModel(model);
this->_models.push_back(model);
@@ -70,7 +66,7 @@ namespace ComSquare::Debugger
// ppuRegisters
model = new RegistersViewerModel(this->_snes);
const PPU::Registers &ppuRegisters = this->_snes.ppu->getWriteRegisters();
const PPU::Registers &ppuRegisters = this->_snes.ppu.getWriteRegisters();
//INIDISP 0X2100
model->addRegister(Register(0x2100, "", "INIDISP", [ppuRegisters](SNES &) {
@@ -127,9 +123,11 @@ namespace ComSquare::Debugger
return ppuRegisters._bgmode.mode1Bg3PriorityBit;
}, nullptr, Boolean));
for (int i = 0; i < 4; i++) {
model->addRegister(Register(0x2105, ":" + std::to_string(i + 4), "BG"+ std::to_string(i + 1) + " 16x16 Tiles", [ppuRegisters, i](SNES &) {
return (ppuRegisters._bgmode.raw >> (i + 4)) & 1;
}, nullptr, Boolean));
model->addRegister(
Register(0x2105, ":" + std::to_string(i + 4), "BG" + std::to_string(i + 1) + " 16x16 Tiles",
[ppuRegisters, i](SNES &) {
return (ppuRegisters._bgmode.raw >> (i + 4)) & 1;
}, nullptr, Boolean));
}
//MOSAIC 0x2106
@@ -137,56 +135,65 @@ namespace ComSquare::Debugger
return ppuRegisters._mosaic.raw;
}, nullptr, EightBits));
for (int i = 0; i < 4; i++) {
model->addRegister(Register(0x2106, ":" + std::to_string(i), "BG"+ std::to_string(i + 1) + " Mosaic", [ppuRegisters, i](SNES &) {
return (ppuRegisters._mosaic.raw >> i) & 1;
}, nullptr, Boolean));
model->addRegister(Register(0x2106, ":" + std::to_string(i), "BG" + std::to_string(i + 1) + " Mosaic",
[ppuRegisters, i](SNES &) {
return (ppuRegisters._mosaic.raw >> i) & 1;
}, nullptr, Boolean));
}
model->addRegister(Register(0x2106, ":4-7", "Size", [ppuRegisters](SNES &) {
return ppuRegisters._mosaic.pixelSize;
}, nullptr, Integer));
/* model->addRegister(Register(0x2106, ":4-7", "Value", [](SNES &) {
return "A lot";
}, nullptr, String)); */
/* model->addRegister(Register(0x2106, ":4-7", "Value", [](SNES &) {
return "A lot";
}, nullptr, String)); */
// BGNSC 0x2107 è 0x210A
for (int i = 0; i < 4; i++) {
model->addRegister(Register(0x2107 + i, "", "BG" + std::to_string(i + 1) + "SC", [ppuRegisters, i](SNES &) {
return ppuRegisters._bgsc[i].raw;
}, nullptr, EightBits));
model->addRegister(Register(0x2107 + i, ":0", "BG" + std::to_string(i + 1) + " Tilemap H mirroring", [ppuRegisters, i](SNES &) {
return ppuRegisters._bgsc[i].tilemapHorizontalMirroring;
}, nullptr, Boolean));
model->addRegister(Register(0x2107 + i, ":1", "BG" + std::to_string(i + 1) + " Tilemap V mirroring", [ppuRegisters, i](SNES &) {
return ppuRegisters._bgsc[i].tilemapVerticalMirroring;
}, nullptr, Boolean));
model->addRegister(Register(0x2107 + i, ":2-7", "BG" + std::to_string(i + 1) + " Tilemap addr", [ppuRegisters, i](SNES &) {
return ppuRegisters._bgsc[i].tilemapAddress;
}, nullptr, EightBits));
model->addRegister(Register(0x2107 + i, ":0", "BG" + std::to_string(i + 1) + " Tilemap H mirroring",
[ppuRegisters, i](SNES &) {
return ppuRegisters._bgsc[i].tilemapHorizontalMirroring;
}, nullptr, Boolean));
model->addRegister(Register(0x2107 + i, ":1", "BG" + std::to_string(i + 1) + " Tilemap V mirroring",
[ppuRegisters, i](SNES &) {
return ppuRegisters._bgsc[i].tilemapVerticalMirroring;
}, nullptr, Boolean));
model->addRegister(
Register(0x2107 + i, ":2-7", "BG" + std::to_string(i + 1) + " Tilemap addr", [ppuRegisters, i](SNES &) {
return ppuRegisters._bgsc[i].tilemapAddress;
}, nullptr, EightBits));
}
// BGnxNBA 0x210B 0x210C
for (int i = 0; i < 2; i++) {
model->addRegister(Register(0x210B + i, "", "BG" + std::string(i ? "34" : "12") + "NBA", [ppuRegisters, i](SNES &) {
return ppuRegisters._bgnba[i].raw;
}, nullptr, EightBits));
model->addRegister(Register(0x210B + i, ":0-3", "BG" + std::string((i ? "3" : "1")) + " Base addr", [ppuRegisters, i](SNES &) {
return ppuRegisters._bgnba[i].baseAddressBg1a3;
}, nullptr, EightBits));
model->addRegister(Register(0x210B + i, ":4-7", "BG" + std::string((i ? "4" : "2")) + " Base addr", [ppuRegisters, i](SNES &) {
return ppuRegisters._bgnba[i].baseAddressBg2a4;
}, nullptr, EightBits));
model->addRegister(
Register(0x210B + i, "", "BG" + std::string(i ? "34" : "12") + "NBA", [ppuRegisters, i](SNES &) {
return ppuRegisters._bgnba[i].raw;
}, nullptr, EightBits));
model->addRegister(Register(0x210B + i, ":0-3", "BG" + std::string((i ? "3" : "1")) + " Base addr",
[ppuRegisters, i](SNES &) {
return ppuRegisters._bgnba[i].baseAddressBg1a3;
}, nullptr, EightBits));
model->addRegister(Register(0x210B + i, ":4-7", "BG" + std::string((i ? "4" : "2")) + " Base addr",
[ppuRegisters, i](SNES &) {
return ppuRegisters._bgnba[i].baseAddressBg2a4;
}, nullptr, EightBits));
}
// BGnxOFS M7nOFS 0x210D - 0x2114
for (int i = 0; i < 4; i++) {
int tmp = i * 2;
model->addRegister(Register(0x210D + tmp, "", "BG" + std::to_string(i + 1) + "HOFS", [ppuRegisters, tmp](SNES &) {
return ppuRegisters._bgofs[tmp].offsetBg;
}, nullptr, SixteenBits));
model->addRegister(
Register(0x210D + tmp, "", "BG" + std::to_string(i + 1) + "HOFS", [ppuRegisters, tmp](SNES &) {
return ppuRegisters._bgofs[tmp].offsetBg;
}, nullptr, SixteenBits));
tmp++;
model->addRegister(Register(0x210D + tmp, "", "BG" + std::to_string(i + 1) + "VOFS", [ppuRegisters, tmp](SNES &) {
return ppuRegisters._bgofs[tmp].offsetBg;
}, nullptr, SixteenBits));
model->addRegister(
Register(0x210D + tmp, "", "BG" + std::to_string(i + 1) + "VOFS", [ppuRegisters, tmp](SNES &) {
return ppuRegisters._bgofs[tmp].offsetBg;
}, nullptr, SixteenBits));
}
// VMAIN 0x2115
@@ -240,22 +247,22 @@ namespace ComSquare::Debugger
}
// M7X 0x211F
model->addRegister(Register(0x211F, "","M7X", [ppuRegisters](SNES &) {
model->addRegister(Register(0x211F, "", "M7X", [ppuRegisters](SNES &) {
return ppuRegisters._m7x.value;
}, nullptr, SixteenBits));
// M7Y 0x2120
model->addRegister(Register(0x2120, "","M7Y", [ppuRegisters](SNES &) {
model->addRegister(Register(0x2120, "", "M7Y", [ppuRegisters](SNES &) {
return ppuRegisters._m7y.value;
}, nullptr, SixteenBits));
// CGADD 0x2121
model->addRegister(Register(0x2121, "","CGADD", [ppuRegisters](SNES &) {
model->addRegister(Register(0x2121, "", "CGADD", [ppuRegisters](SNES &) {
return ppuRegisters._cgadd;
}, nullptr, EightBits));
// CGDATA 0x2122
model->addRegister(Register(0x2122, "","CGDATA", [ppuRegisters](SNES &) {
model->addRegister(Register(0x2122, "", "CGDATA", [ppuRegisters](SNES &) {
return ppuRegisters._cgdata.raw;
}, nullptr, SixteenBits));
@@ -281,32 +288,31 @@ namespace ComSquare::Debugger
arr[2] = "Color";
break;
}
model->addRegister(Register(0x2123 + i, "",arr[0], [ppuRegisters, i](SNES &) {
model->addRegister(Register(0x2123 + i, "", arr[0], [ppuRegisters, i](SNES &) {
return ppuRegisters._wsel[i].raw;
}, nullptr, EightBits));
model->addRegister(Register(0x2123 + i, ":0",arr[1] + " Window 1 inverted", [ppuRegisters, i](SNES &) {
model->addRegister(Register(0x2123 + i, ":0", arr[1] + " Window 1 inverted", [ppuRegisters, i](SNES &) {
return ppuRegisters._wsel[i].window1InversionForBg1Bg3Obj;
}, nullptr, Boolean));
model->addRegister(Register(0x2123 + i, ":1",arr[1] + " Window 1 enabled", [ppuRegisters, i](SNES &) {
model->addRegister(Register(0x2123 + i, ":1", arr[1] + " Window 1 enabled", [ppuRegisters, i](SNES &) {
return ppuRegisters._wsel[i].enableWindow1ForBg1Bg3Obj;
}, nullptr, Boolean));
model->addRegister(Register(0x2123 + i, ":2",arr[1] + " Window 2 inverted", [ppuRegisters, i](SNES &) {
model->addRegister(Register(0x2123 + i, ":2", arr[1] + " Window 2 inverted", [ppuRegisters, i](SNES &) {
return ppuRegisters._wsel[i].window2InversionForBg1Bg3Obj;
}, nullptr, Boolean));
model->addRegister(Register(0x2123 + i, ":3",arr[1] + " Window 2 enabled", [ppuRegisters, i](SNES &) {
model->addRegister(Register(0x2123 + i, ":3", arr[1] + " Window 2 enabled", [ppuRegisters, i](SNES &) {
return ppuRegisters._wsel[i].enableWindow2ForBg1Bg3Obj;
}, nullptr, Boolean));
model->addRegister(Register(0x2123 + i, ":4",arr[2] + " Window 1 inverted", [ppuRegisters, i](SNES &) {
model->addRegister(Register(0x2123 + i, ":4", arr[2] + " Window 1 inverted", [ppuRegisters, i](SNES &) {
return ppuRegisters._wsel[i].window1InversionForBg2Bg4Color;
}, nullptr, Boolean));
model->addRegister(Register(0x2123 + i, ":5",arr[2] + " Window 1 enabled", [ppuRegisters, i](SNES &) {
model->addRegister(Register(0x2123 + i, ":5", arr[2] + " Window 1 enabled", [ppuRegisters, i](SNES &) {
return ppuRegisters._wsel[i].enableWindow1ForBg2Bg4Color;
}, nullptr, Boolean));
model->addRegister(Register(0x2123 + i, ":6",arr[2] + " Window 2 inverted", [ppuRegisters, i](SNES &) {
model->addRegister(Register(0x2123 + i, ":6", arr[2] + " Window 2 inverted", [ppuRegisters, i](SNES &) {
return ppuRegisters._wsel[i].window2InversionForBg2Bg4Color;
}, nullptr, Boolean));
model->addRegister(Register(0x2123 + i, ":7",arr[2] + " Window 2 enabled", [ppuRegisters, i](SNES &) {
model->addRegister(Register(0x2123 + i, ":7", arr[2] + " Window 2 enabled", [ppuRegisters, i](SNES &) {
return ppuRegisters._wsel[i].enableWindow1ForBg2Bg4Color;
}, nullptr, Boolean));
}
@@ -314,12 +320,14 @@ namespace ComSquare::Debugger
// WHx 0x2126 - 0x2129
for (int tmp = 0; tmp < 2; tmp++) {
int i = tmp * 2;
model->addRegister(Register(0x2126 + i, "", "Window " + std::to_string(tmp + 1) + " Left", [ppuRegisters, i](SNES &) {
return ppuRegisters._wh[i];
}, nullptr, EightBits));
model->addRegister(Register(0x2126 + i + 1, "", "Window " + std::to_string(tmp + 1) + " Right", [ppuRegisters, i](SNES &) {
return ppuRegisters._wh[i + 1];
}, nullptr, EightBits));
model->addRegister(
Register(0x2126 + i, "", "Window " + std::to_string(tmp + 1) + " Left", [ppuRegisters, i](SNES &) {
return ppuRegisters._wh[i];
}, nullptr, EightBits));
model->addRegister(
Register(0x2126 + i + 1, "", "Window " + std::to_string(tmp + 1) + " Right", [ppuRegisters, i](SNES &) {
return ppuRegisters._wh[i + 1];
}, nullptr, EightBits));
}
// WBGLOG 0x212A
@@ -369,11 +377,12 @@ namespace ComSquare::Debugger
model->addRegister(Register(0x212e + j, "", std::string((j ? "TSW" : "TMW")), [ppuRegisters, j](SNES &) {
return ppuRegisters._tw[j].raw;
}, nullptr, EightBits));
for (int i = 0; i < 4; i ++) {
model->addRegister(Register(0x212e + j, ":" + std::to_string(i), "BG" + std::to_string(i + 1) + " Window Mask Enabled",
[ppuRegisters, i, j](SNES &) {
return (ppuRegisters._tw[j].raw >> i) & 1;
}, nullptr, Boolean));
for (int i = 0; i < 4; i++) {
model->addRegister(
Register(0x212e + j, ":" + std::to_string(i), "BG" + std::to_string(i + 1) + " Window Mask Enabled",
[ppuRegisters, i, j](SNES &) {
return (ppuRegisters._tw[j].raw >> i) & 1;
}, nullptr, Boolean));
}
model->addRegister(Register(0x212e + j, ":4", "OBJ Window Mask Enabled", [ppuRegisters, j](SNES &) {
return ppuRegisters._tw[j].enableWindowMaskingObj;
@@ -401,7 +410,6 @@ namespace ComSquare::Debugger
model->addRegister(Register(0x2131, "", "CGADSUB", [ppuRegisters](SNES &) {
return ppuRegisters._cgadsub.raw;
}, nullptr, EightBits));
std::array<std::string, 8> tmp = {
"BG1 Enabled",
"BG2 Enabled",
@@ -457,7 +465,6 @@ namespace ComSquare::Debugger
model->addRegister(Register(0x2133, ":7", "External Sync", [ppuRegisters](SNES &) {
return ppuRegisters._setini.externalSync;
}, nullptr, Boolean));
this->_ui.ppuRegisters->setModel(model);
}
@@ -466,11 +473,6 @@ namespace ComSquare::Debugger
this->_window->activateWindow();
}
void RegisterViewer::disableDebugger()
{
this->_snes.disableRegisterDebugging();
}
RegisterViewer::~RegisterViewer()
{
for (auto &model : this->_models)
@@ -478,90 +480,86 @@ namespace ComSquare::Debugger
}
Register::Register(uint24_t addr,
const std::string &usedBits,
const std::string &regName,
const std::function<unsigned int(SNES &)> &getValue,
const std::function<void(SNES &, unsigned int)> &setValue,
RegisterType regType)
: address(addr),
bits(usedBits),
name(regName),
get(getValue),
set(setValue),
type(regType) {}
}
std::string usedBits,
std::string regName,
std::function<unsigned int(SNES &)> getValue,
std::function<void(SNES &, unsigned int)> setValue,
RegisterType regType)
: address(addr),
bits(std::move(usedBits)),
name(std::move(regName)),
get(std::move(getValue)),
set(std::move(setValue)),
type(regType)
{}
using namespace ComSquare;
using namespace ComSquare::Debugger;
RegistersViewerModel::RegistersViewerModel(SNES &snes, QObject *parent)
: QAbstractTableModel(parent), _snes(snes)
{}
RegistersViewerModel::RegistersViewerModel(SNES &snes, QObject *parent) : QAbstractTableModel(parent), _snes(snes) { }
void RegistersViewerModel::addRegister(const Register &reg)
{
int row = static_cast<int>(this->_registers.size());
this->beginInsertRows(QModelIndex(), row, row);
this->_registers.push_back(reg);
this->insertRow(row);
this->endInsertRows();
}
int RegistersViewerModel::rowCount(const QModelIndex &) const
{
return static_cast<int>(this->_registers.size());
}
void RegistersViewerModel::addRegister(Register reg)
{
int row = this->_registers.size();
this->beginInsertRows(QModelIndex(), row, row);
this->_registers.push_back(reg);
this->insertRow(row);
this->endInsertRows();
}
int RegistersViewerModel::columnCount(const QModelIndex &) const
{
return 3;
}
int RegistersViewerModel::rowCount(const QModelIndex &) const
{
return this->_registers.size();
}
int RegistersViewerModel::columnCount(const QModelIndex &) const
{
return 3;
}
QVariant RegistersViewerModel::data(const QModelIndex &index, int role) const
{
Register reg = this->_registers[index.row()];
if (role == Qt::CheckStateRole && reg.type == Boolean && index.column() == 2)
return reg.get(this->_snes) ? Qt::Checked : Qt::Unchecked;
if (role != Qt::DisplayRole)
QVariant RegistersViewerModel::data(const QModelIndex &index, int role) const
{
Register reg = this->_registers[index.row()];
if (role == Qt::CheckStateRole && reg.type == Boolean && index.column() == 2)
return reg.get(this->_snes) ? Qt::Checked : Qt::Unchecked;
if (role != Qt::DisplayRole)
return QVariant();
switch (index.column()) {
case 0:
return QString((Utility::to_hex(reg.address) + reg.bits).c_str());
case 1:
return QString(reg.name.c_str());
case 2:
switch (reg.type) {
case Boolean:
return QString(reg.get(this->_snes) ? "True" : "False");
case String:
return QString(reg.get(this->_snes));
case Integer:
return QString::number(reg.get(this->_snes));
case EightBits:
return QString(Utility::to_hex(static_cast<uint8_t>(reg.get(this->_snes))).c_str());
case SixteenBits:
return QString(Utility::to_hex(static_cast<uint16_t>(reg.get(this->_snes))).c_str());
case TwentyFourBits:
return QString(Utility::to_hex(static_cast<uint24_t>(reg.get(this->_snes))).c_str());
}
}
return QVariant();
}
switch (index.column()) {
case 0:
return QString((Utility::to_hex(reg.address) + reg.bits).c_str());
case 1:
return QString(reg.name.c_str());
case 2:
switch (reg.type) {
case Boolean:
return QString(reg.get(this->_snes) ? "True" : "False");
case String:
return QString(reg.get(this->_snes));
case Integer:
return QString::number(reg.get(this->_snes));
case EightBits:
return QString(Utility::to_hex(static_cast<uint8_t>(reg.get(this->_snes))).c_str());
case SixteenBits:
return QString(Utility::to_hex(static_cast<uint16_t>(reg.get(this->_snes))).c_str());
case TwentyFourBits:
return QString(Utility::to_hex(static_cast<uint24_t>(reg.get(this->_snes))).c_str());
QVariant RegistersViewerModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Vertical || role != Qt::DisplayRole)
return QVariant();
switch (section) {
case 0:
return QString("Address");
case 1:
return QString("Name");
case 2:
return QString("Value");
default:
return QVariant();
}
}
return QVariant();
}
QVariant RegistersViewerModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Vertical || role != Qt::DisplayRole)
return QVariant();
switch (section) {
case 0:
return QString("Address");
case 1:
return QString("Name");
case 2:
return QString("Value");
default:
return QVariant();
}
}
}// namespace ComSquare::Debugger

View File

@@ -2,47 +2,14 @@
// Created by anonymus-raccoon on 5/28/20.
//
#ifndef COMSQUARE_REGISTERVIEWER_HPP
#define COMSQUARE_REGISTERVIEWER_HPP
#pragma once
#include <QtCore/QObject>
#include "ClosableWindow.hpp"
#include "../../ui/ui_registersView.h"
#include "../Models/Int24.hpp"
#include "../Memory/MemoryBus.hpp"
namespace ComSquare::Debugger
{
struct Register;
}
class RegistersViewerModel : public QAbstractTableModel
{
Q_OBJECT
private:
//! @brief The list of registers to display / update.
std::vector<ComSquare::Debugger::Register> _registers;
//! @brief Reference to the snes to get information from registers.
ComSquare::SNES &_snes;
public:
//! @brief Add a register.
void addRegister(ComSquare::Debugger::Register reg);
RegistersViewerModel(ComSquare::SNES &snes, QObject *parent = nullptr);
RegistersViewerModel(const RegistersViewerModel &) = delete;
const RegistersViewerModel &operator=(const RegistersViewerModel &) = delete;
~RegistersViewerModel() 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;
};
#include "Memory/MemoryBus.hpp"
#include "Models/Ints.hpp"
#include "ui/ui_registersView.h"
#include <QAbstractTableModel>
#include <QtCore/QObject>
namespace ComSquare
{
@@ -50,10 +17,40 @@ namespace ComSquare
namespace Debugger
{
class RegisterViewer : public QObject {
struct Register;
class RegistersViewerModel : public QAbstractTableModel
{
Q_OBJECT
private:
//! @brief The list of registers to display / update.
std::vector<ComSquare::Debugger::Register> _registers;
//! @brief Reference to the snes to get information from registers.
ComSquare::SNES &_snes;
public:
//! @brief Add a register.
void addRegister(const ComSquare::Debugger::Register &reg);
explicit RegistersViewerModel(ComSquare::SNES &snes, QObject *parent = nullptr);
RegistersViewerModel(const RegistersViewerModel &) = delete;
const RegistersViewerModel &operator=(const RegistersViewerModel &) = delete;
~RegistersViewerModel() override = default;
//! @brief The number of row the table has.
[[nodiscard]] int rowCount(const QModelIndex &parent) const override;
//! @brief The number of column the table has.
[[nodiscard]] int columnCount(const QModelIndex &parent) const override;
//! @brief Return a data representing the table cell.
[[nodiscard]] QVariant data(const QModelIndex &index, int role) const override;
//! @brief Override the headers to use hex values.
[[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
};
class RegisterViewer : public QObject
{
private:
//! @brief The QT window for this debugger.
ClosableWindow<RegisterViewer> *_window;
ClosableWindow *_window;
//! @brief A widget that contain the whole UI.
Ui::RegistersView _ui;
//! @brief The list of models used by different panels.
@@ -65,21 +62,18 @@ namespace ComSquare
//! @brief Set models to the different tables and initialize them.
void _setupUi();
public:
//! @brief Called when the window is closed. Turn off the debugger.
void disableDebugger();
explicit RegisterViewer(SNES &snes);
RegisterViewer(
const RegisterViewer &) = delete;
RegisterViewer(const RegisterViewer &) = delete;
RegisterViewer &operator=(const RegisterViewer &) = delete;
~RegisterViewer();
~RegisterViewer() override;
//! @brief Focus the debugger's window.
void focus();
};
//! @brief The types of registers
enum RegisterType {
enum RegisterType
{
//! @brief This type display a checkbox
Boolean,
//! @brief A 8 bits hexadecimal value.
@@ -95,13 +89,14 @@ namespace ComSquare
};
//! @brief Struct containing information about a register.
struct Register {
struct Register
{
Register(uint24_t addr,
const std::string &usedBits,
const std::string &regName,
const std::function<unsigned int(SNES &)> &getValue,
const std::function<void(SNES &, unsigned int)> &setValue,
RegisterType regType);
std::string usedBits,
std::string regName,
std::function<unsigned int(SNES &)> getValue,
std::function<void(SNES &, unsigned int)> setValue,
RegisterType regType);
//! @brief Where this register is located on the bus.
uint24_t address;
@@ -116,7 +111,5 @@ namespace ComSquare
//! @brief How this value should be displayed/asked for input.
RegisterType type;
};
}
}
#endif //COMSQUARE_REGISTERVIEWER_HPP
}// namespace Debugger
}// namespace ComSquare

View File

@@ -2,30 +2,21 @@
// Created by cbihan on 24/05/2021.
//
#include <complex>
#include <cmath>
#include "RAMTileRenderer.hpp"
#include "PPU/PPU.hpp"
#include "PPU/Tile.hpp"
#include <iostream>
namespace ComSquare::Debugger
{
RAMTileRenderer::RAMTileRenderer()
: _ram(nullptr),
RAMTileRenderer::RAMTileRenderer(Ram::Ram &ram, Ram::Ram &cgram)
: _ram(ram),
_renderSize(0x5000),
_nbColumns(16),
_ramOffset(0),
_bpp(2),
_tileRenderer(ram, cgram),
buffer({{{0}}})
{
}
void RAMTileRenderer::setRam(std::shared_ptr<Ram::Ram> ram)
{
this->_ram = ram;
this->_tileRenderer.setRam(ram);
}
{}
void RAMTileRenderer::render()
{
@@ -35,7 +26,7 @@ namespace ComSquare::Debugger
int resetX = bufX;
for (auto &i : this->buffer)
i.fill(0);
uint24_t limit = fmin(this->_ram->getSize(), this->_renderSize) + this->_ramOffset;
uint24_t limit = std::fmin(this->_ram.getSize(), this->_renderSize) + this->_ramOffset;
for (uint24_t i = this->_ramOffset; i < limit; i += PPU::Tile::BaseByteSize * this->_bpp, nbTilesDrawn++) {
if (bufX > 1024 || bufY > 1024)
@@ -75,11 +66,6 @@ namespace ComSquare::Debugger
this->_tileRenderer.setBpp(bpp);
}
void RAMTileRenderer::setCgram(std::shared_ptr<Ram::Ram> ram)
{
this->_tileRenderer.setCgram(ram);
}
void RAMTileRenderer::setRenderSize(int size)
{
this->_renderSize = size;

View File

@@ -10,10 +10,11 @@
namespace ComSquare::Debugger
{
class RAMTileRenderer {
class RAMTileRenderer
{
private:
//! @brief ram to render
std::shared_ptr<Ram::Ram> _ram;
Ram::Ram &_ram;
//! @brief The size to render in the ram
int _renderSize;
//! @brief The number of tile columns to display
@@ -29,34 +30,31 @@ namespace ComSquare::Debugger
std::array<std::array<uint32_t, 1024>, 1024> buffer;
//! @brief Set the palette to use for render (index of palette)
void setPaletteIndex(int paletteIndex);
//! @brief Set the ram to look for color references
void setCgram(std::shared_ptr<Ram::Ram> ram);
//! @brief Set the bpp to render graphics
void setBpp(int bpp);
//! @brief Set the number of maximum columns
void setNbColumns(int nbColumns);
//! @brief Set the size of ram to render
void setRenderSize(int size);
//! @brief The ram to render
void setRam(std::shared_ptr<Ram::Ram> ram);
//! @brief Set the ram offset
void setRamOffset(int offset);
//! @brief Get the current bpp
int getBpp() const;
[[nodiscard]] int getBpp() const;
//! @brief Get the index of the current palette used
int getPaletteIndex() const;
[[nodiscard]] int getPaletteIndex() const;
//! @brief Get the numbr of maximum tile columns to render
int getNbColumns() const;
[[nodiscard]] int getNbColumns() const;
//! @brief render the selected ram
void render();
//! @brief ctor
RAMTileRenderer();
RAMTileRenderer(Ram::Ram &ram, Ram::Ram &cgram);
//! @brief copy ctor
RAMTileRenderer(const RAMTileRenderer &) = default;
//! @brief dtor
~RAMTileRenderer() = default;
//! @brief assignment operator
RAMTileRenderer &operator=(const RAMTileRenderer &) = default;
//! @brief A RAMTileRender is not assignable.
RAMTileRenderer &operator=(const RAMTileRenderer &) = delete;
};
}

View File

@@ -2,10 +2,6 @@
// Created by cbihan on 5/7/21.
//
namespace ComSquare::Renderer
{
class QtFullSFML;
}
#include "Renderer/QtRenderer/QtSFML.hpp"
#include "TileViewer.hpp"
@@ -13,33 +9,30 @@ namespace ComSquare::Renderer
#include <QColor>
#include <string>
#include <iostream>
#include <QtWidgets/QTableWidget>
#include "Utility/Utility.hpp"
#include "RAMTileRenderer.hpp"
#include "PPU/PPU.hpp"
namespace ComSquare::Debugger
{
TileViewer::TileViewer(SNES &snes, ComSquare::PPU::PPU &ppu)
: _window(new ClosableWindow<TileViewer>(*this, &TileViewer::disableViewer)),
: _window(new ClosableWindow([&snes] { snes.disableTileViewer(); })),
_snes(snes),
_ui(),
_ppu(ppu),
_ramTileRenderer()
_ramTileRenderer(ppu.vram, ppu.cgram)
{
this->_ramTileRenderer.setRam(ppu.vram);
this->_ramTileRenderer.setCgram(ppu.cgram);
this->_window->setContextMenuPolicy(Qt::NoContextMenu);
this->_window->setAttribute(Qt::WA_QuitOnClose, false);
this->_window->setAttribute(Qt::WA_DeleteOnClose);
this->_ui.setupUi(this->_window);
this->_sfWidget = std::make_unique<Renderer::QtSFMLTileRenderer>(this->_ui.widget_sfml);
QMainWindow::connect(this->_ui.NbColumns, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int nb) -> void { this->setNbColumns(nb); });
QMainWindow::connect(this->_ui.ByteSize, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int nb) -> void { this->setRenderSize(nb); });
QMainWindow::connect(this->_ui.Address, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int nb) -> void { this->setRamOffset(nb); });
QMainWindow::connect(this->_ui.PaletteIndex, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int nb) -> void { this->setPaletteIndex(nb); });
QMainWindow::connect(this->_ui.BppFormat, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index) -> void { this->_bppChangeUIHandler(index); });
QMainWindow::connect(this->_ui.NbColumns, QOverload<int>::of(&QSpinBox::valueChanged), this,
[this](int nb) -> void { this->setNbColumns(nb); });
QMainWindow::connect(this->_ui.ByteSize, QOverload<int>::of(&QSpinBox::valueChanged), this,
[this](int nb) -> void { this->setRenderSize(nb); });
QMainWindow::connect(this->_ui.Address, QOverload<int>::of(&QSpinBox::valueChanged), this,
[this](int nb) -> void { this->setRamOffset(nb); });
QMainWindow::connect(this->_ui.PaletteIndex, QOverload<int>::of(&QSpinBox::valueChanged), this,
[this](int nb) -> void { this->setPaletteIndex(nb); });
QMainWindow::connect(this->_ui.BppFormat, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
[this](int index) -> void { this->_bppChangeUIHandler(index); });
// used to setup ui restrictions
this->setBpp(this->getBpp());
@@ -47,21 +40,11 @@ namespace ComSquare::Debugger
this->internalUpdate();
}
void TileViewer::disableViewer()
{
this->_snes.disableTileViewerDebugging();
}
void TileViewer::focus()
{
this->_window->activateWindow();
}
bool TileViewer::isDebugger()
{
return true;
}
uint16_t TileViewer::read(uint8_t addr)
{
return this->_ppu.cgramRead(addr);
@@ -138,9 +121,12 @@ namespace ComSquare::Debugger
void TileViewer::_bppChangeUIHandler(int index)
{
switch (index) {
case 0: return this->setBpp(2);
case 1: return this->setBpp(4);
case 2: return this->setBpp(8);
case 0:
return this->setBpp(2);
case 1:
return this->setBpp(4);
case 2:
return this->setBpp(8);
default:
throw std::runtime_error("Invalid Index");
}

View File

@@ -4,11 +4,6 @@
#pragma once
namespace ComSquare::PPU
{
class PPU;
}
#include <QtCore/QSortFilterProxyModel>
#include <QEvent>
#include <QMouseEvent>
@@ -16,7 +11,7 @@ namespace ComSquare::PPU
#include "PPU/PPU.hpp"
#include "Debugger/ClosableWindow.hpp"
#include "Renderer/QtRenderer/QtSfmlTileRenderer.hpp"
#include "../../../ui/ui_tileView.h"
#include "ui/ui_tileView.h"
#include "Ram/Ram.hpp"
#include "RAMTileRenderer.hpp"
@@ -24,10 +19,11 @@ namespace ComSquare::Debugger
{
//! @brief window that allow the user to view all data going through the memory bus.
class TileViewer : public QObject {
class TileViewer : public QObject
{
private:
//! @brief The QT window for this debugger.
ClosableWindow<TileViewer> *_window;
ClosableWindow *_window;
//! @brief A reference to the snes (to disable the debugger).
SNES &_snes;
//! @brief A widget that contain the whole UI.
@@ -41,8 +37,6 @@ namespace ComSquare::Debugger
//! @brief Change the bpp from the index given by the ui (QT combo box)
void _bppChangeUIHandler(int index);
public:
//! @brief Called when the window is closed. Turn off the debugger.
void disableViewer();
//! @brief ctor
explicit TileViewer(SNES &snes, ComSquare::PPU::PPU &ppu);
//! @brief copy ctor
@@ -58,8 +52,6 @@ namespace ComSquare::Debugger
uint16_t read(uint8_t addr);
//! @brief Focus the debugger's window.
void focus();
//! @brief Return true if the Bus is overloaded with debugging features.
bool isDebugger();
//! @brief Set the palette to use for render (index of palette)
void setPaletteIndex(int paletteIndex);
//! @brief Set the bpp to render graphics
@@ -71,13 +63,12 @@ namespace ComSquare::Debugger
//! @brief Set the ram offset
void setRamOffset(int offset);
//! @brief Get the current bpp
int getBpp() const;
[[nodiscard]] int getBpp() const;
//! @brief Get the index of the current palette used
int getPaletteIndex() const;
[[nodiscard]] int getPaletteIndex() const;
//! @brief Get the numbr of maximum tile columns to render
int getNbColumns() const;
[[nodiscard]] int getNbColumns() const;
//! @brief Update the tile renderer
void internalUpdate();
};
}

View File

@@ -2,35 +2,39 @@
// Created by anonymus-raccoon on 1/27/20.
//
#ifndef COMSQUARE_INVALIDADDRESS_HPP
#define COMSQUARE_INVALIDADDRESS_HPP
#pragma once
#include "DebuggableError.hpp"
#include "Models/Ints.hpp"
#include <exception>
#include <string>
#include <ios>
#include <sstream>
#include "DebuggableError.hpp"
#include <string>
namespace ComSquare
{
//! @brief Exception thrown when trying to read/write to an invalid address.
class InvalidAddress : public DebuggableError {
class InvalidAddress : public DebuggableError
{
private:
std::string _msg;
public:
InvalidAddress(std::string where, int32_t addr)
InvalidAddress(std::string where, uint24_t addr)
{
std::stringstream stream;
stream << "Could not read/write data at address: 0x" << std::hex << addr << " from " << where;
stream << "Could not read/write data at address: " << addr << " from " << where;
this->_msg = stream.str();
}
const char *what() const noexcept override { return this->_msg.c_str(); }
};
//! @brief Exception thrown when trying to read/write to an invalid address in a rectangle memory region.
class InvalidRectangleAddress : public std::exception {
class InvalidRectangleAddress : public std::exception
{
private:
std::string _msg;
public:
InvalidRectangleAddress(std::string where, int32_t addr, int16_t subaddr, int16_t start, int16_t end)
{
@@ -44,6 +48,4 @@ namespace ComSquare
}
const char *what() const noexcept override { return this->_msg.c_str(); }
};
}
#endif //COMSQUARE_INVALIDADDRESS_HPP
}// namespace ComSquare

View File

@@ -21,19 +21,4 @@ namespace ComSquare::Memory
{
return this->_start <= addr && addr <= this->_end;
}
bool AMemory::isMirror() const
{
return false;
}
std::shared_ptr<IMemory> AMemory::getMirrored() const
{
return nullptr;
}
std::string AMemory::getValueName(uint24_t) const
{
return "???";
}
}

View File

@@ -8,8 +8,8 @@
#include <vector>
#include <memory>
#include <string>
#include "../Models/Int24.hpp"
#include "../Models/Components.hpp"
#include "Models/Ints.hpp"
#include "Models/Components.hpp"
#include "IMemory.hpp"
namespace ComSquare::Memory
@@ -26,25 +26,20 @@ namespace ComSquare::Memory
//! @param addr The absolute address (in the 24 bit bus)
//! @return The local address (0 refers to the first byte of this component).
//! @throw InvalidAddress is thrown if the address is not mapped by this component.
virtual uint24_t getRelativeAddress(uint24_t addr) const override;
[[nodiscard]] uint24_t getRelativeAddress(uint24_t addr) const override;
//! @brief Change starting and ending points of this mapped memory.
//! @param start The first address mapped to this component.
//! @param end The last address mapped to this component.
//! @warning The start/end address should be a continuous range. You can't map address 0x0 and 0x2 but not 0x1. To do that, use two AMemory.
void setMemoryRegion(uint24_t start, uint24_t end);
//! @brief Return true if this component has mapped the address.
//! @param addr The address to check.
//! @return True if this address is mapped to the component. False otherwise.
virtual bool hasMemoryAt(uint24_t addr) const override;
//! @brief Check if this memory is a mirror or not.
//! @return True if this memory is a mirror. False otherwise.
virtual bool isMirror() const override;
//! @brief Get the name of the data at the address
//! @param addr The address (in local space)
virtual std::string getValueName(uint24_t addr) const override;
//! @brief Return the memory accessor this accessor mirror if any
//! @return nullptr if isMirror is false, the source otherwise.
virtual std::shared_ptr<IMemory> getMirrored() const override;
virtual ~AMemory() override = default;
[[nodiscard]] bool hasMemoryAt(uint24_t addr) const override;
//! @brief A default destructor
~AMemory() override = default;
};
}

View File

@@ -4,7 +4,7 @@
#include <iostream>
#include "ARectangleMemory.hpp"
#include "../Exceptions/InvalidAddress.hpp"
#include "Exceptions/InvalidAddress.hpp"
namespace ComSquare::Memory
{
@@ -42,16 +42,6 @@ namespace ComSquare::Memory
this->_endPage = endPage;
}
bool ARectangleMemory::isMirror() const
{
return false;
}
std::shared_ptr<IMemory> ARectangleMemory::getMirrored() const
{
return nullptr;
}
std::string ARectangleMemory::getValueName(uint24_t) const
{
return "???";

View File

@@ -5,11 +5,13 @@
#pragma once
#include "IMemory.hpp"
#include "Models/Ints.hpp"
namespace ComSquare::Memory
{
//! @brief Base memory class 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 IMemory {
class ARectangleMemory : public IMemory
{
protected:
//! @brief The first bank to map to.
uint8_t _startBank = 0;
@@ -19,16 +21,19 @@ namespace ComSquare::Memory
uint16_t _startPage = 0;
//! @brief The last address of each bank to map.
uint16_t _endPage = 0;
public:
//! @brief Translate an absolute address to a relative address
//! @param addr The absolute address (in the 24 bit bus)
//! @return The local address (0 refers to the first byte of this component).
//! @throw InvalidAddress is thrown if the address is not mapped by this component.
virtual uint24_t getRelativeAddress(uint24_t addr) const override;
[[nodiscard]] uint24_t getRelativeAddress(uint24_t addr) const override;
//! @brief Return true if this component has mapped the address.
//! @param addr The address to check.
//! @return True if this address is mapped to the component. False otherwise.
bool hasMemoryAt(uint24_t addr) const override;
[[nodiscard]] bool hasMemoryAt(uint24_t addr) const override;
//! @brief Change starting and ending points of this mapped memory.
//! @param startBank The first bank mapped to this component.
//! @param endBank The last bank mapped to this component.
@@ -36,15 +41,12 @@ namespace ComSquare::Memory
//! @param endPage The end page mapped to this component (every mapped banks will have this pages lower than this mapped)
//! @warning The start/end address should be a rectangle. To mirror memory, use the MemoryShadow class and not this one.
void setMemoryRegion(uint8_t startBank, uint8_t endBank, uint16_t startPage, uint16_t endPage);
//! @brief Check if this memory is a mirror or not.
//! @return True if this memory is a mirror. False otherwise.
virtual bool isMirror() const override;
//! @brief Get the name of the data at the address
//! @param addr The address (in local space)
virtual std::string getValueName(uint24_t addr) const override;
//! @brief Return the memory accessor this accessor mirror if any
//! @return nullptr if isMirror is false, the source otherwise.
virtual std::shared_ptr<IMemory> getMirrored() const override;
virtual ~ARectangleMemory() override = default;
[[nodiscard]] std::string getValueName(uint24_t addr) const override;
//! @brief A default destructor.
~ARectangleMemory() override = default;
};
}
}// namespace ComSquare::Memory

View File

@@ -8,49 +8,52 @@
#include <vector>
#include <memory>
#include <string>
#include "../Models/Int24.hpp"
#include "../Models/Components.hpp"
#include "Models/Ints.hpp"
#include "Models/Components.hpp"
namespace ComSquare::Memory
{
//! @brief Common interface implemented by all components mapping memory.
class IMemory {
class IMemory
{
public:
//! @brief Read data from the component.
//! @param addr The local address to read from (0x0 should refer to the first byte of this component).
//! @throw This function should thrown an InvalidAddress for address that are not mapped to the component.
//! @return Return the data at the address given as parameter.
virtual uint8_t read(uint24_t addr) = 0;
//! @brief Write data to this component.
//! @param addr The local address to write data (0x0 should refer to the first byte of this component).
//! @param data The new data to write.
//! @throw This function should thrown an InvalidAddress for address that are not mapped to the component.
virtual void write(uint24_t addr, uint8_t data) = 0;
//! @brief Return true if this component has mapped the address.
//! @param addr The address to check.
//! @return True if this address is mapped to the component. False otherwise.
virtual bool hasMemoryAt(uint24_t addr) const = 0;
//! @brief Translate an absolute address to a relative address
//! @param addr The absolute address (in the 24 bit bus)
//! @return The local address (0 refers to the first byte of this component).
//! @throw InvalidAddress is thrown if the address is not mapped by this component.
virtual uint24_t getRelativeAddress(uint24_t addr) const = 0;
//! @brief Get the size of the data. This size can be lower than the mapped data.
//! @return The number of bytes inside this memory.
virtual uint24_t getSize() const = 0;
//! @brief Check if this memory is a mirror or not.
//! @return True if this memory is a mirror. False otherwise.
virtual bool isMirror() const = 0;
//! @brief Get the name of this accessor (used for debug purpose)
virtual std::string getName() const = 0;
//! @brief Get the component of this accessor (used for debug purpose)
virtual Component getComponent() const = 0;
//! @brief Get the name of the data at the address
//! @param addr The address (in local space)
virtual std::string getValueName(uint24_t addr) const = 0;
//! @brief Return the memory accessor this accessor mirror if any
//! @return nullptr if isMirror is false, the source otherwise.
virtual std::shared_ptr<IMemory> getMirrored() const = 0;
//! @brief A virtual default destructor.
virtual ~IMemory() = default;
};
};

View File

@@ -0,0 +1,53 @@
//
// Created by Zoe Roux on 2021-07-04.
//
#pragma once
#include <cinttypes>
#include <optional>
#include "IMemory.hpp"
namespace ComSquare
{
class SNES;
namespace Memory
{
//! @brief The memory bus is the component responsible of mapping addresses to components address and transmitting the data.
class IMemoryBus
{
public:
//! @brief A virtual default destructor.
virtual ~IMemoryBus() = default;
//! @brief Read data at a global address. This form allow read to be silenced.
//! @param addr The address to read from.
//! @throws InvalidAddress If the address is not mapped to the bus, this exception is thrown.
//! @return The value that the component returned for this address. If the address was mapped to ram, it simply returned the value. If the address was mapped to a register the component returned the register.
virtual uint8_t read(uint24_t addr) = 0;
//! @brief This as the same purpose as a read but it does not change the open bus and won't throw an exception.
//! @param addr The address to read from.
//! @return The value that the component returned for this address. If the address was mapped to ram, it simply returned the value. If the address was mapped to a register the component returned the register.
virtual std::optional<uint8_t> peek(uint24_t addr) = 0;
//! @brief This as the same purpose as a read but it does not change the open bus and won't throw an exception.
//! @param addr The address to read from.
//! @return The value that the component returned for this address. If the address was mapped to ram, it simply returned the value. If the address was mapped to a register the component returned the register.
//! @note If the value address is not mapped, 0 is returned instead of nullopt.
virtual uint8_t peek_v(uint24_t addr) = 0;
//! @brief Write a data to a global address.
//! @param addr The address to write to.
//! @param data The data to write.
virtual void write(uint24_t addr, uint8_t data) = 0;
//! @brief Helper function to get the components that is responsible of read/write at an address.
//! @param addr The address you want to look for.
//! @return The components responsible for the address param or nullptr if none was found.
virtual IMemory *getAccessor(uint24_t addr) = 0;
};
}
}

View File

@@ -2,61 +2,67 @@
// Created by anonymus-raccoon on 1/23/20.
//
#include <algorithm>
#include <iostream>
#include "MemoryBus.hpp"
#include "../SNES.hpp"
#include "MemoryShadow.hpp"
#include "RectangleShadow.hpp"
#include "../Exceptions/InvalidAddress.hpp"
#include "SNES.hpp"
#include "Memory/MemoryBus.hpp"
#include "Memory/MemoryShadow.hpp"
#include "Exceptions/InvalidAddress.hpp"
#include "Models/Logger.hpp"
namespace ComSquare::Memory
{
std::shared_ptr<IMemory> MemoryBus::getAccessor(uint24_t addr)
IMemory *MemoryBus::getAccessor(uint24_t addr)
{
auto it = std::find_if(this->_memoryAccessors.begin(), this->_memoryAccessors.end(), [addr](std::shared_ptr<IMemory> &accessor)
auto it = std::find_if(this->_memoryAccessors.begin(), this->_memoryAccessors.end(), [addr](IMemory &accessor)
{
return accessor->hasMemoryAt(addr);
return accessor.hasMemoryAt(addr);
});
if (it == this->_memoryAccessors.end())
return nullptr;
return *it;
return &it->get();
}
uint8_t MemoryBus::read(uint24_t addr)
{
std::shared_ptr<IMemory> handler = this->getAccessor(addr);
IMemory *handler = this->getAccessor(addr);
if (!handler) {
std::cout << "Unknown memory accessor for address $" << std::hex << addr << ". Using open bus." << std::endl;
logMsg(LogLevel::WARNING, "Unknown memory accessor for address $" << std::hex << addr << ". Using open bus.");
return this->_openBus;
}
uint8_t data = handler->read(handler->getRelativeAddress(addr));
this->_openBus = data;
return data;
}
uint8_t MemoryBus::read(uint24_t addr, bool silence)
std::optional<uint8_t> MemoryBus::peek(uint24_t addr)
{
if (!silence)
return this->read(addr);
std::shared_ptr<IMemory> handler = this->getAccessor(addr);
IMemory *handler = this->getAccessor(addr);
if (!handler)
return this->_openBus;
try {
return handler->read(handler->getRelativeAddress(addr));
} catch (const InvalidAddress &) {
return 0;
return std::nullopt;
}
}
uint8_t MemoryBus::peek_v(uint24_t addr)
{
auto value = this->peek(addr);
if (value.has_value())
return value.value();
return 0;
}
void MemoryBus::write(uint24_t addr, uint8_t data)
{
std::shared_ptr<IMemory> handler = this->getAccessor(addr);
IMemory *handler = this->getAccessor(addr);
if (!handler) {
std::cout << "Unknown memory accessor for address " << std::hex << addr << ". Warning, it was a write." << std::endl;
logMsg(LogLevel::ERROR, "Unknown memory accessor for address " << std::hex << addr << ". Warning, it was a write.");
return;
}
handler->write(handler->getRelativeAddress(addr), data);
@@ -64,28 +70,30 @@ namespace ComSquare::Memory
void MemoryBus::_mirrorComponents(SNES &console, unsigned i)
{
this->_memoryAccessors.emplace_back(new Memory::RectangleShadow(console.wram, i, i, 0x0000, 0x1FFF));
this->_memoryAccessors.emplace_back(new Memory::MemoryShadow(console.ppu, (i << 16u) + 0x2100, (i << 16u) + 0x213F));
this->_memoryAccessors.emplace_back(new Memory::MemoryShadow(console.apu, (i << 16u) + 0x2140, (i << 16u) + 0x2143));
this->_memoryAccessors.emplace_back(new Memory::MemoryShadow(console.cpu, (i << 16u) + 0x4200, (i << 16u) + 0x421F));
this->_rectangleShadows.emplace_back(console.wram, i, i, 0x0000, 0x1FFF);
this->_shadows.emplace_back(console.ppu, (i << 16u) + 0x2100, (i << 16u) + 0x213F);
this->_shadows.emplace_back(console.apu, (i << 16u) + 0x2140, (i << 16u) + 0x2143);
this->_shadows.emplace_back(console.cpu, (i << 16u) + 0x4200, (i << 16u) + 0x421F);
}
void MemoryBus::mapComponents(SNES &console)
{
this->_memoryAccessors.clear();
this->_shadows.clear();
this->_rectangleShadows.clear();
// The WRam and PU registers are always mapped at the same address no matter the mapping mode.
console.wram->setMemoryRegion(0x7E, 0x7F, 0x0000, 0xFFFF);
this->_memoryAccessors.push_back(console.wram);
console.wram.setMemoryRegion(0x7E, 0x7F, 0x0000, 0xFFFF);
this->_memoryAccessors.emplace_back(console.wram);
console.ppu->setMemoryRegion(0x2100, 0x213F);
this->_memoryAccessors.push_back(console.ppu);
console.ppu.setMemoryRegion(0x2100, 0x213F);
this->_memoryAccessors.emplace_back(console.ppu);
console.apu->setMemoryRegion(0x2140, 0x2143);
this->_memoryAccessors.push_back(console.apu);
console.apu.setMemoryRegion(0x2140, 0x2143);
this->_memoryAccessors.emplace_back(console.apu);
console.cpu->setMemoryRegion(0x4200, 0x44FF);
this->_memoryAccessors.push_back(console.cpu);
console.cpu.setMemoryRegion(0x4200, 0x44FF);
this->_memoryAccessors.emplace_back(console.cpu);
// TODO implement Joys.
@@ -96,27 +104,27 @@ namespace ComSquare::Memory
for (uint8_t i = 0x80; i < 0xC0; i += 0x01)
this->_mirrorComponents(console, i);
if (console.cartridge->header.mappingMode & Cartridge::LoRom) {
console.cartridge->setMemoryRegion(0x80, 0xFF, 0x8000, 0xFFFF);
this->_memoryAccessors.push_back(console.cartridge);
if (console.cartridge.header.mappingMode & Cartridge::LoRom) {
console.cartridge.setMemoryRegion(0x80, 0xFF, 0x8000, 0xFFFF);
this->_memoryAccessors.emplace_back(console.cartridge);
// Mirror on the Q1 and Q2.
this->_memoryAccessors.emplace_back(new Memory::RectangleShadow(console.cartridge, 0x00, 0x7D, 0x8000, 0xFFFF));
this->_rectangleShadows.emplace_back(console.cartridge, 0x00, 0x7D, 0x8000, 0xFFFF);
// Mirror on the lower half of the Q2.
this->_memoryAccessors.emplace_back((new Memory::RectangleShadow(console.cartridge, 0x40, 0x6F, 0x0000, 0x7FFF))->setBankOffset(0x40));
this->_rectangleShadows.emplace_back(console.cartridge, 0x40, 0x6F, 0x0000, 0x7FFF).setBankOffset(0x40);
// Mirror on the lower half of the Q4
this->_memoryAccessors.emplace_back((new Memory::RectangleShadow(console.cartridge, 0xC0, 0xEF, 0x0000, 0x7FFF))->setBankOffset(0x40));
this->_rectangleShadows.emplace_back(console.cartridge, 0xC0, 0xEF, 0x0000, 0x7FFF).setBankOffset(0x40);
console.sram->setMemoryRegion(0xF0, 0xFD, 0x0000, 0x7FFF);
this->_memoryAccessors.push_back(console.sram);
this->_memoryAccessors.emplace_back(new Memory::RectangleShadow(console.sram, 0x70, 0x7D, 0x0000, 0x7FFF));
console.sram.setMemoryRegion(0xF0, 0xFD, 0x0000, 0x7FFF);
this->_memoryAccessors.emplace_back(console.sram);
this->_rectangleShadows.emplace_back(console.sram, 0x70, 0x7D, 0x0000, 0x7FFF);
// TODO implement the SRam accessor for the FE/FF
}
// TODO should implement HiRom.
}
bool MemoryBus::isDebugger()
{
return false;
for (auto &shadow : this->_shadows)
this->_memoryAccessors.emplace_back(shadow);
for (auto &shadow : this->_rectangleShadows)
this->_memoryAccessors.emplace_back(shadow);
}
}

View File

@@ -2,13 +2,15 @@
// Created by anonymus-raccoon on 1/23/20.
//
#ifndef COMSQUARE_MEMORYBUS_HPP
#define COMSQUARE_MEMORYBUS_HPP
#pragma once
#include <cstdint>
#include <vector>
#include <memory>
#include "AMemory.hpp"
#include "RectangleShadow.hpp"
#include "MemoryShadow.hpp"
#include "IMemoryBus.hpp"
#include <cstdint>
#include <memory>
#include <vector>
namespace ComSquare
{
@@ -17,10 +19,16 @@ namespace ComSquare
namespace Memory
{
//! @brief The memory bus is the component responsible of mapping addresses to components address and transmitting the data.
class MemoryBus {
class MemoryBus : public IMemoryBus
{
private:
//! @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<std::shared_ptr<IMemory>> _memoryAccessors;
std::vector<std::reference_wrapper<IMemory>> _memoryAccessors;
//! @brief The list of simple memory shadows that are used to map duplicated zones of memory.
std::vector<MemoryShadow> _shadows = {};
//! @brief The list of rectangle memory shadows that are used to map duplicated zones of memory.
std::vector<RectangleShadow> _rectangleShadows = {};
//! @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.
@@ -30,29 +38,36 @@ namespace ComSquare
//! @brief The last value read via the memory bus.
uint8_t _openBus = 0;
public:
//! @brief Create a new default memory bus.
MemoryBus() = default;
//! @brief A memory bus is copyable.
MemoryBus(const MemoryBus &) = default;
//! @brief A memory bus is assignable.
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.
//! @return The value that the component returned for this address. If the address was mapped to ram, it simply returned the value. If the address was mapped to a register the component returned the register.
virtual uint8_t read(uint24_t addr);
//! @brief A default destructor
~MemoryBus() override = default;
//! @brief Read data at a global address. This form allow read to be silenced.
//! @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. This also won't affect the open bus.
//! @throws InvalidAddress If the address is not mapped to the bus, this exception is thrown.
//! @return The value that the component returned for this address. If the address was mapped to ram, it simply returned the value. If the address was mapped to a register the component returned the register.
virtual uint8_t read(uint24_t addr, bool silence);
uint8_t read(uint24_t addr) override;
//! @brief This as the same purpose as a read but it does not change the open bus and won't throw an exception.
//! @param addr The address to read from.
//! @return The value that the component returned for this address. If the address was mapped to ram, it simply returned the value. If the address was mapped to a register the component returned the register.
std::optional<uint8_t> peek(uint24_t addr) override;
//! @brief This as the same purpose as a read but it does not change the open bus and won't throw an exception.
//! @param addr The address to read from.
//! @return The value that the component returned for this address. If the address was mapped to ram, it simply returned the value. If the address was mapped to a register the component returned the register.
//! @note If the value address is not mapped, 0 is returned instead of nullopt.
uint8_t peek_v(uint24_t addr) override;
//! @brief Write a data to a global address.
//! @param addr The address to write to.
//! @param data The data to write.
virtual void write(uint24_t addr, uint8_t data);
void write(uint24_t addr, uint8_t data) override;
//! @brief Map components to the address space using the currently loaded cartridge to set the right mapping mode.
//! @param console All the components.
@@ -61,13 +76,7 @@ namespace ComSquare
//! @brief Helper function to get the components that is responsible of read/write at an address.
//! @param addr The address you want to look for.
//! @return The components responsible for the address param or nullptr if none was found.
std::shared_ptr<IMemory> getAccessor(uint24_t addr);
//! @brief Return true if the Bus is overloaded with debugging features.
virtual bool isDebugger();
IMemory *getAccessor(uint24_t addr) override;
};
}
}
#endif //COMSQUARE_MEMORYBUS_HPP

View File

@@ -4,48 +4,46 @@
#include "MemoryShadow.hpp"
#include <utility>
namespace ComSquare::Memory
{
MemoryShadow::MemoryShadow(std::shared_ptr<IMemory> initial, uint24_t start, uint24_t end)
: _initial(std::move(initial))
MemoryShadow::MemoryShadow(IMemory &initial, uint24_t start, uint24_t end)
: _initial(initial)
{
this->setMemoryRegion(start, end);
}
uint8_t MemoryShadow::read(uint24_t addr)
{
return this->_initial->read(addr);
return this->_initial.read(addr);
}
void MemoryShadow::write(uint24_t addr, uint8_t data)
{
return this->_initial->write(addr, data);
return this->_initial.write(addr, data);
}
uint24_t MemoryShadow::getSize() const
{
return this->_initial->getSize();
return this->_initial.getSize();
}
bool MemoryShadow::isMirror() const
{
return true;
}
std::shared_ptr<IMemory> MemoryShadow::getMirrored() const
IMemory &MemoryShadow::getMirrored() const
{
return this->_initial;
}
std::string MemoryShadow::getName() const
{
return this->_initial->getName();
return this->_initial.getName();
}
Component MemoryShadow::getComponent() const
{
return this->_initial->getComponent();
return this->_initial.getComponent();
}
std::string MemoryShadow::getValueName(uint24_t addr) const
{
return this->_initial.getValueName(addr);
}
}

View File

@@ -9,39 +9,51 @@
namespace ComSquare::Memory
{
class MemoryShadow : public AMemory {
class MemoryShadow : public AMemory
{
private:
//! @brief Memory to shadow from.
std::shared_ptr<IMemory> _initial;
IMemory &_initial;
public:
//! @brief Create a shadow for the memory given as parameter.
MemoryShadow(std::shared_ptr<IMemory> initial, uint24_t start, uint24_t end);
//! @param initial The memory to shadow
//! @param start The start position of the initial memory to shadow.
//! @param end The end position of the initial memory to shadow.
MemoryShadow(IMemory &initial, uint24_t start, uint24_t end);
//! @brief A memory shadow is copy constructable
MemoryShadow(const MemoryShadow &) = default;
MemoryShadow &operator=(const MemoryShadow &) = default;
~MemoryShadow() = default;
//! @brief A memory shadow is not assignable.
MemoryShadow &operator=(const MemoryShadow &) = delete;
//! @brief A default destructor
~MemoryShadow() override = default;
//! @brief Read from the initial AMemory given.
//! @param addr The address to read from. The address 0x0 should refer to the first byte of the initial AMemory.
//! @throw InvalidAddress will be thrown if the address is more than the size of the initial AMemory.
//! @return Return the data at the address.
uint8_t read(uint24_t addr) override;
//! @brief Write data to the ram.
//! @param addr The address to write to. The address 0x0 should refer to the first byte of the initial AMemory.
//! @param data The data to write.
//! @throw InvalidAddress will be thrown if the address is more than the size of the initial AMemory.
void write(uint24_t addr, uint8_t data) override;
//! @brief Get the size of the data. This size can be lower than the mapped data.
//! @return The number of bytes inside this memory.
virtual uint24_t getSize() const override;
//! @brief Check if this memory is a mirror or not.
//! @return True if this memory is a mirror. False otherwise.
bool isMirror() const override;
[[nodiscard]] uint24_t getSize() const override;
//! @brief Get the name of this accessor (used for debug purpose)
std::string getName() const override;
[[nodiscard]] std::string getName() const override;
//! @brief Get the component of this accessor (used for debug purpose)
Component getComponent() const override;
[[nodiscard]] Component getComponent() const override;
//! @brief Get the name of the data at the address
//! @param addr The address (in local space)
[[nodiscard]] std::string getValueName(uint24_t addr) const override;
//! @brief Return the memory accessor this accessor mirror if any
//! @return nullptr if isMirror is false, the source otherwise.
std::shared_ptr<IMemory> getMirrored() const override;
[[nodiscard]] IMemory &getMirrored() const;
};
}

View File

@@ -3,17 +3,16 @@
//
#include "RectangleShadow.hpp"
#include <utility>
#include <iostream>
namespace ComSquare::Memory
{
RectangleShadow::RectangleShadow(std::shared_ptr<IMemory> initial,
RectangleShadow::RectangleShadow(IMemory &initial,
uint8_t startBank,
uint8_t endBank,
uint16_t startPage,
uint16_t endPage)
: _initial(std::move(initial))
: _initial(initial)
{
this->setMemoryRegion(startBank, endBank, startPage, endPage);
}
@@ -26,42 +25,36 @@ namespace ComSquare::Memory
uint8_t RectangleShadow::read(uint24_t addr)
{
return this->_initial->read(addr);
return this->_initial.read(addr);
}
void RectangleShadow::write(uint24_t addr, uint8_t data)
{
return this->_initial->write(addr, data);
return this->_initial.write(addr, data);
}
RectangleShadow *RectangleShadow::setBankOffset(int bankOffset)
void RectangleShadow::setBankOffset(int bankOffset)
{
this->_bankOffset = bankOffset;
return this;
}
uint24_t RectangleShadow::getSize() const
{
return this->_initial->getSize();
return this->_initial.getSize();
}
bool RectangleShadow::isMirror() const
{
return true;
}
std::shared_ptr<IMemory> RectangleShadow::getMirrored() const
IMemory &RectangleShadow::getMirrored() const
{
return this->_initial;
}
std::string RectangleShadow::getName() const
{
return this->_initial->getName();
return this->_initial.getName();
}
Component RectangleShadow::getComponent() const
{
return this->_initial->getComponent();
return this->_initial.getComponent();
}
}

View File

@@ -13,14 +13,21 @@ namespace ComSquare::Memory
class RectangleShadow : public ARectangleMemory {
private:
//! @brief Memory to shadow from.
std::shared_ptr<IMemory> _initial;
IMemory &_initial;
//! @brief The number of banks to add to the memory before accessing it from the initial data.
int _bankOffset = 0;
public:
//! @brief Create a shadow for the memory given as parameter.
explicit RectangleShadow(std::shared_ptr<IMemory> initial, uint8_t startBank, uint8_t endBank, uint16_t startPage, uint16_t endPage);
//! @param startBank The starting bank of the memory to shadow.
//! @param endBank The ending bank of the memory to shadow
//! @param startPage The starting page of the memory to shadow
//! @param endPage The ending page of the memory to shadow
RectangleShadow(IMemory &initial, uint8_t startBank, uint8_t endBank, uint16_t startPage, uint16_t endPage);
//! @brief A rectangle shadow is copy constructable.
RectangleShadow(const RectangleShadow &) = default;
RectangleShadow &operator=(const RectangleShadow &) = default;
//! @brrief A rectangle shadow is not assignable
RectangleShadow &operator=(const RectangleShadow &) = delete;
//! @brief A default destructor.
~RectangleShadow() override = default;
//! @brief Read from the initial AMemory given.
@@ -28,31 +35,34 @@ namespace ComSquare::Memory
//! @throw InvalidAddress will be thrown if the address is more than the size of the initial AMemory.
//! @return Return the data at the address.
uint8_t read(uint24_t addr) override;
//! @brief Write data to the ram.
//! @param addr The address to write to. The address 0x0 should refer to the first byte of the initial AMemory.
//! @param data The data to write.
//! @throw InvalidAddress will be thrown if the address is more than the size of the initial AMemory.
void write(uint24_t addr, uint8_t data) override;
//! @brief Translate an absolute address to a relative address
//! @param addr The absolute address (in the 24 bit bus)
//! @return The local address (0 refers to the first byte of this component).
//! @throw InvalidAddress is thrown if the address is not mapped by this component.
uint24_t getRelativeAddress(uint24_t addr) const override;
[[nodiscard]] uint24_t getRelativeAddress(uint24_t addr) const override;
//! @brief Get the size of the data. This size can be lower than the mapped data.
//! @return The number of bytes inside this memory.
virtual uint24_t getSize() const override;
//! @brief Check if this memory is a mirror or not.
//! @return True if this memory is a mirror. False otherwise.
bool isMirror() const override;
[[nodiscard]] uint24_t getSize() const override;
//! @brief Get the name of this accessor (used for debug purpose)
std::string getName() const override;
[[nodiscard]] std::string getName() const override;
//! @brief Get the component of this accessor (used for debug purpose)
Component getComponent() const override;
[[nodiscard]] Component getComponent() const override;
//! @brief Return the memory accessor this accessor mirror if any
//! @return nullptr if isMirror is false, the source otherwise.
std::shared_ptr<IMemory> getMirrored() const override;
[[nodiscard]] IMemory &getMirrored() const;
//! @brief Set the number of bank this component do not shadow. Referring to the first byte of this component will refer to the first byte of the bank at (bankOffset + start of initial memory).
RectangleShadow *setBankOffset(int bankOffset);
void setBankOffset(int bankOffset);
};
}

View File

@@ -0,0 +1,67 @@
//
// Created by Zoe Roux on 5/21/21.
//
#pragma once
#include <functional>
#include <utility>
#include <unordered_map>
namespace ComSquare
{
//! @brief A callback where you can subscribe to and emit it.
template<typename ...Types>
class Callback
{
private:
int _nextID = 0;
//! @brief The list of functions to call.
std::unordered_map<int, std::function<void (Types...)>> _functions = {};
public:
//! @brief Add a method to be called when this callback is invoked.
//! @param callback The list of arguments of the callback method
//! @return A unique ID for this callback. That can be used to remove the callback later.
template<typename Func>
int addCallback(Func callback)
{
int id = this->_nextID++;
if constexpr(std::is_same_v<Func, std::function<void (Types...)>>)
this->_functions[id] = std::move(callback);
else
this->_functions[id] = std::function<void (Types...)>(callback);
return id;
}
//! @brief Remove a function from this callback.
//! @param id The ID of the function.
void removeCallback(int id)
{
this->_functions.erase(id);
}
void operator()(Types ...args) const
{
for (const auto &[_, callback] : this->_functions)
callback(args...);
}
//! @brief A default constructor
Callback() = default;
//! @brief A default copy constructor
Callback(const Callback &) = default;
//! @brief A default destructor
~Callback() = default;
//! @brief A default assignment operator
Callback &operator=(const Callback &) = default;
//! @brief Implicitly transform a callable into a callback.
template<typename Func>
Callback(Func callback) // NOLINT(google-explicit-constructor)
{
this->addCallback(callback);
}
};
} // namespace WAL

View File

@@ -2,8 +2,7 @@
// Created by anonymus-raccoon on 3/24/20.
//
#ifndef COMSQUARE_COMPONENTS_HPP
#define COMSQUARE_COMPONENTS_HPP
#pragma once
namespace ComSquare
{
@@ -18,5 +17,4 @@ namespace ComSquare
CGRam = 1u << 7u,
SRam = 1u << 8u,
};
}
#endif //COMSQUARE_COMPONENTS_HPP
}

View File

@@ -1,10 +0,0 @@
//
// Created by anonymus-raccoon on 1/28/20.
//
#ifndef COMSQUARE_INT24_HPP
#define COMSQUARE_INT24_HPP
typedef unsigned uint24_t;
#endif //COMSQUARE_INT24_HPP

13
sources/Models/Ints.hpp Normal file
View File

@@ -0,0 +1,13 @@
//
// Created by anonymus-raccoon on 1/28/20.
//
#pragma once
#include <cinttypes>
#include <iostream>
typedef unsigned uint24_t;
std::ostream &operator<<(std::ostream &os, uint8_t value);

32
sources/Models/Logger.hpp Normal file
View File

@@ -0,0 +1,32 @@
//
// Created by Zoe Roux on 2021-07-06.
//
#pragma once
#include <iostream>
#ifdef TESTS
#include <catch2/catch_message.hpp>
#endif
namespace ComSquare
{
enum LogLevel
{
INFO,
WARNING,
ERROR
};
constexpr LogLevel GlobalLevel = INFO;
#ifndef TESTS
#define logMsg(level, message) \
if constexpr((level) >= GlobalLevel) \
std::cout << message << std::endl // NOLINT(bugprone-macro-parentheses)
#else
#define logMsg(_, msg) INFO(msg)
#endif
}

View File

@@ -2,8 +2,7 @@
// Created by cbihan on 5/13/20.
//
#ifndef COMSQUARE_VECTOR2_HPP
#define COMSQUARE_VECTOR2_HPP
#pragma once
#include <ostream>
#include <SFML/System/Vector2.hpp>
@@ -18,13 +17,16 @@ namespace ComSquare
T y;
Vector2()
: x(0), y(0) {}
: x(0), y(0)
{}
Vector2(T _x, T _y)
: x(_x), y(_y) {}
: x(_x), y(_y)
{}
Vector2(sf::Vector2<T> v)
: x(v.x), y(v.y) {}
Vector2(sf::Vector2<T> v) // NOLINT(google-explicit-constructor)
: x(v.x), y(v.y)
{}
template<typename T2>
Vector2<T> &operator+=(const Vector2<T2> &vec)
@@ -48,6 +50,12 @@ namespace ComSquare
return *this;
}
template<typename T2>
Vector2<T> operator-(const Vector2<T2> &vec) const
{
return Vector2<T>(this->x - vec.x, this->y - vec.y);
}
template<typename T2>
Vector2<T> &operator*=(T2 d)
{
@@ -98,7 +106,9 @@ namespace ComSquare
};
typedef Vector2<float> Vector2f;
typedef Vector2<unsigned> Vector2u;
typedef Vector2<int> Vector2i;
}
@@ -108,5 +118,3 @@ std::ostream &operator<<(std::ostream &s, const ComSquare::Vector2<T> &v)
s << v.x << " " << v.y;
return s;
}
#endif //COMSQUARE_VECTOR2_HPP

View File

@@ -5,32 +5,28 @@
#include "PPUUtils.hpp"
#include "PPU.hpp"
#include "Background.hpp"
#include <cmath>
#include "Tile.hpp"
#include "PPUUtils.hpp"
#include "Models/Vector2.hpp"
namespace ComSquare::PPU
{
Background::Background(ComSquare::PPU::PPU &ppu, int backGroundNumber, bool hasPriority):
_ppu(ppu),
_tileMapMirroring(ppu.getBackgroundMirroring(backGroundNumber)),
_characterNbPixels(ppu.getCharacterSize(backGroundNumber)),
_bpp(ppu.getBPP(backGroundNumber)),
_directColor(false),
_highRes(false),
_tileMapStartAddress(ppu.getTileMapStartAddress(backGroundNumber)),
_tilesetAddress(ppu.getTilesetAddress(backGroundNumber)),
_priority(hasPriority),
_bgNumber(backGroundNumber),
_tileBuffer({{{0}}}),
_vram(ppu.vram),
_cgram(ppu.cgram),
buffer({{{0}}})
{
this->_tileRenderer.setRam(this->_vram);
this->_tileRenderer.setCgram(this->_cgram);
}
Background::Background(ComSquare::PPU::PPU &ppu, int backGroundNumber, bool hasPriority)
: _ppu(ppu),
_tileMapMirroring(ppu.getBackgroundMirroring(backGroundNumber)),
_characterNbPixels(ppu.getCharacterSize(backGroundNumber)),
_bpp(ppu.getBPP(backGroundNumber)),
_directColor(false),
_highRes(false),
_tileMapStartAddress(ppu.getTileMapStartAddress(backGroundNumber)),
_tilesetAddress(ppu.getTilesetAddress(backGroundNumber)),
_priority(hasPriority),
_bgNumber(backGroundNumber),
_tileBuffer({{{0}}}),
_vram(ppu.vram),
_cgram(ppu.cgram),
_tileRenderer(this->_vram, this->_cgram),
buffer({{{0}}})
{}
void Background::renderBackground()
{
@@ -105,14 +101,13 @@ namespace ComSquare::PPU
void Background::_drawBasicTileMap(uint16_t baseAddress, Vector2<int> offset)
{
uint16_t tileMapValue = 0;
Vector2<int> pos(0, 0);
uint16_t vramAddress = baseAddress;
while (vramAddress < baseAddress + TileMapByteSize) {
// TODO function to read 2 bytes (LSB order or bits reversed)
tileMapValue = this->_vram->read(vramAddress);
tileMapValue += this->_vram->read(vramAddress + 1) << 8U;
uint16_t tileMapValue = this->_vram.read(vramAddress);
tileMapValue += this->_vram.read(vramAddress + 1) << 8U;
this->_drawTile(tileMapValue, {(offset.x * NbCharacterWidth) + pos.x,
(offset.y * NbCharacterHeight) + pos.y});
vramAddress += 2;

View File

@@ -4,20 +4,21 @@
#pragma once
#include <stdint-gcc.h>
#include <array>
#include <vector>
#include <iostream>
#include "../Models/Vector2.hpp"
#include "Models/Vector2.hpp"
#include "TileRenderer.hpp"
#include "../Ram/Ram.hpp"
#include "Ram/Ram.hpp"
#include "PPU.hpp"
#include "PPUUtils.hpp"
namespace ComSquare::PPU
{
class PPU;
class Background {
class Background
{
private:
//! @brief The number of character a TileMap has in width
static constexpr int NbCharacterWidth = 32;
@@ -29,9 +30,8 @@ namespace ComSquare::PPU
//! @brief The size of a TileMap in memory
static constexpr unsigned short TileMapByteSize = 0x800;
//! @brief the ppu used to get registers values (ex: bg scroll)
ComSquare::PPU::PPU &_ppu;
PPU &_ppu;
//! @brief The tilemap configuration nb of tileMap vertically and horizontally
//! @note members are set to true if the tilemap is expended in their direction
Vector2<bool> _tileMapMirroring;
@@ -52,14 +52,14 @@ namespace ComSquare::PPU
bool _priority;
//! @brief The bg number (used to get the corresponding scroll)
int _bgNumber;
//! @brief Class that actually render a tile
TileRenderer _tileRenderer;
//! @brief Buffer if we have tiles that are more than 8x8
std::array<std::array<uint32_t, 16>, 16> _tileBuffer;
//! @brief the access to vram
std::shared_ptr<Ram::Ram> _vram;
Ram::Ram &_vram;
//! @brief The access to cgram
std::shared_ptr<Ram::Ram> _cgram;
Ram::Ram &_cgram;
//! @brief Class that actually render a tile
TileRenderer _tileRenderer;
//! @brief Draw a tile on the screen at x y pos
//! @param data The VRAM value to be interpreted as a Utils::TileData
//! @param indexOffset The index offset of the Tile (ranging from 0 to 63)
@@ -142,7 +142,7 @@ namespace ComSquare::PPU
}
//! @brief ctor
Background(ComSquare::PPU::PPU &_ppu, int backGroundNumber, bool hasPriority);
Background(PPU &_ppu, int backGroundNumber, bool hasPriority);
//! @brief Default copy ctor
Background(const Background &) = default;
//! @brief Default destructor

View File

@@ -5,9 +5,7 @@
#include <iostream>
#include <bitset>
#include "PPU.hpp"
#include "Exceptions/NotImplementedException.hpp"
#include "Exceptions/InvalidAddress.hpp"
#include "Ram/Ram.hpp"
#include "Models/Vector2.hpp"
namespace ComSquare::PPU::Utils::Debug {
@@ -17,9 +15,9 @@ namespace ComSquare::PPU::Utils::Debug {
namespace ComSquare::PPU
{
PPU::PPU(Renderer::IRenderer &renderer):
vram(new Ram::Ram(VramSize, ComSquare::VRam, "VRAM")),
oamram(new Ram::Ram(OAMRamSize, ComSquare::OAMRam, "OAMRAM")),
cgram(new Ram::Ram(CGRamSize, ComSquare::CGRam, "CGRAM")),
vram(VramSize, ComSquare::VRam, "VRAM"),
oamram(OAMRamSize, ComSquare::OAMRam, "OAMRAM"),
cgram(CGRamSize, ComSquare::CGRam, "CGRAM"),
_renderer(renderer),
_backgrounds{
Background(*this, 1, false),
@@ -32,7 +30,7 @@ namespace ComSquare::PPU
{
this->_registers._isLowByte = true;
//Utils::Debug::populateEnvironment(*this, 0);
Utils::Debug::populateEnvironment(*this, 1);
}
uint8_t PPU::read(uint24_t addr)
@@ -68,7 +66,7 @@ namespace ComSquare::PPU
return returnValue;
}
case PpuRegisters::cgdataread: {
return this->cgram->read(this->_registers._cgadd++);
return this->cgram.read(this->_registers._cgadd++);
}
case PpuRegisters::ophct:
case PpuRegisters::opvct:
@@ -101,7 +99,7 @@ namespace ComSquare::PPU
//throw InvalidAddress("oamdata", addr);
//std::cout << "oamdata" << std::endl;
// the oamAddress have to be calculated if fblank or not (not implemented)
oamram->write(this->_registers._oamadd.oamAddress, this->_registers._oamdata);
oamram.write(this->_registers._oamadd.oamAddress, this->_registers._oamdata);
this->_registers._oamadd.oamAddress++;
break;
case PpuRegisters::bgmode:
@@ -180,7 +178,7 @@ namespace ComSquare::PPU
//std::cout << "vmdatal" << std::endl;
if (!this->_registers._inidisp.fblank) {
this->_registers._vmdata.vmdatal = data;
this->vram->write(this->getVramAddress(), data);
this->vram.write(this->getVramAddress(), data);
}
if (!this->_registers._vmain.incrementMode)
this->_registers._vmadd.vmadd += this->_registers._incrementAmount;
@@ -189,7 +187,7 @@ namespace ComSquare::PPU
//std::cout << "vmdatah" << std::endl;
if (!this->_registers._inidisp.fblank) {
this->_registers._vmdata.vmdatah = data;
this->vram->write(this->getVramAddress() + 1, data);
this->vram.write(this->getVramAddress() + 1, data);
}
if (this->_registers._vmain.incrementMode)
this->_registers._vmadd.vmadd += this->_registers._incrementAmount;
@@ -217,9 +215,9 @@ namespace ComSquare::PPU
}
else {
this->_registers._cgdata.cgdatah = data;
this->cgram->write(this->_registers._cgadd, this->_registers._cgdata.cgdatal);
this->cgram.write(this->_registers._cgadd, this->_registers._cgdata.cgdatal);
this->_registers._cgadd++;
this->cgram->write(this->_registers._cgadd, this->_registers._cgdata.cgdatah);
this->cgram.write(this->_registers._cgadd, this->_registers._cgdata.cgdatah);
this->_registers._cgadd++;
}
this->_registers._isLowByte = !this->_registers._isLowByte;
@@ -479,14 +477,9 @@ namespace ComSquare::PPU
return Ppu;
}
bool PPU::isDebugger() const
{
return false;
}
uint16_t PPU::cgramRead(uint16_t addr)
{
return this->cgram->read(addr);
return this->cgram.read(addr);
}
int PPU::getBPP(int bgNumber) const
@@ -563,8 +556,8 @@ namespace ComSquare::PPU
_background.renderBackground();
}
// TODO make a function getDefaultBgColor
colorPalette = this->cgram->read(0);
colorPalette += this->cgram->read(1) << 8U;
colorPalette = this->cgram.read(0);
colorPalette += this->cgram.read(1) << 8U;
uint32_t color = Utils::getRealColor(colorPalette);
for (auto &row : this->_subScreen)
@@ -678,8 +671,8 @@ namespace ComSquare::PPU
void PPU::updateVramReadBuffer()
{
this->_vramReadBuffer = this->vram->read(this->getVramAddress());
this->_vramReadBuffer += this->vram->read(this->getVramAddress() + 1) << 8;
this->_vramReadBuffer = this->vram.read(this->getVramAddress());
this->_vramReadBuffer += this->vram.read(this->getVramAddress() + 1) << 8;
}
Vector2<int> PPU::getBgScroll(int bgNumber) const

View File

@@ -2,8 +2,7 @@
// Created by cbihan on 1/27/20.
//
#ifndef COMSQUARE_PPU_HPP
#define COMSQUARE_PPU_HPP
#pragma once
#include <cstdint>
#include "Memory/AMemory.hpp"
@@ -14,13 +13,17 @@
#include <algorithm>
#include "Background.hpp"
#include "PPUUtils.hpp"
#ifdef DEBUGGER_ENABLED
#include "Debugger/TileViewer/RAMTileRenderer.hpp"
#endif
#define FALLTHROUGH __attribute__((fallthrough));
namespace ComSquare::PPU::Utils {
namespace ComSquare::PPU::Utils
{
struct PpuState;
};
}
namespace ComSquare::PPU
{
@@ -552,10 +555,10 @@ namespace ComSquare::PPU
class PPU : public Memory::AMemory {
public:
//! @brief Rams
std::shared_ptr<Ram::Ram> vram;
std::shared_ptr<Ram::Ram> oamram;
std::shared_ptr<Ram::Ram> cgram;
//private:
Ram::Ram vram;
Ram::Ram oamram;
Ram::Ram cgram;
private:
//! @brief Init ppuRegisters
Registers _registers{};
Renderer::IRenderer &_renderer;
@@ -592,46 +595,43 @@ namespace ComSquare::PPU
//! @throw This function should thrown an InvalidAddress for address that are not mapped to the component.
void write(uint24_t addr, uint8_t data) override;
//! @brief Get the name of this accessor (used for debug purpose)
std::string getName() const override;
[[nodiscard]] std::string getName() const override;
//! @brief Get the component of this accessor (used for debug purpose)
Component getComponent() const override;
[[nodiscard]] Component getComponent() const override;
//! @brief Get the size of the data. This size can be lower than the mapped data.
//! @return The number of bytes inside this memory.
uint24_t getSize() const override;
[[nodiscard]] uint24_t getSize() const override;
//! @brief Update the PPU of n cycles.
//! @param The number of cycles to update.
virtual void update(unsigned cycles);
//! @brief Give the Vram Address with the right Address remapping
uint16_t getVramAddress() const;
[[nodiscard]] uint16_t getVramAddress() const;
//! @brief Give the name of the Address register (used for debug)
std::string getValueName(uint24_t addr) const;
//! @brief Return true if the CPU is overloaded with debugging features.
virtual bool isDebugger() const;
[[nodiscard]] std::string getValueName(uint24_t addr) const override;
//! @brief Allow others components to read the CGRAM
uint16_t cgramRead(uint16_t addr);
//! @brief get the bpp depending of the bgNumber and the Bgmode
int getBPP(int bgNumber) const;
[[nodiscard]] int getBPP(int bgNumber) const;
//! @brief Give the correct character size depending of the bgMode
Vector2<int> getCharacterSize(int bgNumber) const;
[[nodiscard]] Vector2<int> getCharacterSize(int bgNumber) const;
//! @brief Give the address where the tilemap starts
uint16_t getTileMapStartAddress(int bgNumber) const;
[[nodiscard]] uint16_t getTileMapStartAddress(int bgNumber) const;
//! @brief Give the address to find the correct tileset for a given x and y
uint16_t getTilesetAddress(int bgNumber) const;
[[nodiscard]] uint16_t getTilesetAddress(int bgNumber) const;
//! @brief Tells if the tilemap is expanded for the x and y directions
Vector2<bool> getBackgroundMirroring(int bgNumber) const;
[[nodiscard]] Vector2<bool> getBackgroundMirroring(int bgNumber) const;
//! @brief Render the Main and sub screen correctly
void renderMainAndSubScreen();
//! @brief Add a bg to the sub and/or main screen
void addToMainSubScreen(Background &bg, const Vector2<int> &level);
//! @brief Get the current background Mode
int getBgMode() const;
[[nodiscard]] int getBgMode() const;
//! @brief update the Vram buffer
void updateVramReadBuffer();
//! @brief update the Vram buffer
Vector2<int> getBgScroll(int bgNumber) const;
[[nodiscard]] Vector2<int> getBgScroll(int bgNumber) const;
//! @brief Allow to look the value of each write register (used by Register debugger)
const Registers &getWriteRegisters() const;
[[nodiscard]] const Registers &getWriteRegisters() const;
};
}
#endif //COMSQUARE_PPU_HPP

View File

@@ -2,8 +2,7 @@
// Created by cbihan on 1/27/20.
//
#ifndef COMSQUARE_PPU_UTILS_HPP
#define COMSQUARE_PPU_UTILS_HPP
#pragma once
#include <stdint-gcc.h>
#include <cstddef>
@@ -95,4 +94,3 @@ namespace ComSquare::PPU::Utils
});
}
}
#endif //COMSQUARE_PPU_UTILS_HPP

View File

@@ -7,19 +7,19 @@
namespace ComSquare::PPU::Utils::Debug
{
void populateCgramTicTacToe(const std::shared_ptr<Ram::Ram> &cgram)
void populateCgramTicTacToe(Ram::Ram &cgram)
{
//colors for the cgram
cgram->write(2, 0xE0);
cgram->write(3, 0x7F);
cgram->write(4, 0x1F); // 0x1F
cgram->write(6, 0xFF);
cgram->write(7, 0x03);
cgram->write(66, 0xE0);
cgram->write(67, 0x7F);
cgram.write(2, 0xE0);
cgram.write(3, 0x7F);
cgram.write(4, 0x1F); // 0x1F
cgram.write(6, 0xFF);
cgram.write(7, 0x03);
cgram.write(66, 0xE0);
cgram.write(67, 0x7F);
}
void populateVramTicTacToe(const std::shared_ptr<Ram::Ram> &vram)
void populateVramTicTacToe(Ram::Ram &vram)
{
int vram_test[] = {
00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
@@ -54,37 +54,37 @@ namespace ComSquare::PPU::Utils::Debug
00,0x00,0x00,0x00,0x01,0x00,0x03,0x00,0x07,0x00,0x0f,00,0x1f,00,0x3f,00, -1
};
for (int i = 0; vram_test[i] != -1; i++) {
vram->write(i, vram_test[i]);
vram.write(i, vram_test[i]);
}
int vram_test_2[] = {8, 00, 02, 00, 0x0A, 00, 02, 00, 0x0A, 00, 00, 00, 00, 00, 00, -1};
for (int i = 0; vram_test_2[i] != -1; i++) {
vram->write(i + 0x8000, vram_test_2[i]);
vram.write(i + 0x8000, vram_test_2[i]);
}
int vram_test_3[] = {8, 00, 02, 00, 0x8, 00, 02, 00, 0x8, 00, 00, 00, 00, 00, 00, -1};
for (int i = 0; vram_test_3[i] != -1; i++) {
vram->write(i + 0x8080, vram_test_3[i]);
vram.write(i + 0x8080, vram_test_3[i]);
}
int vram_test_4[] = {8, 00, 02, 00, 0x0A, 00, 02, 00, 0x0A, 00, 00, 00, 00, 00, 00, -1};
for (int i = 0; vram_test_4[i] != -1; i++) {
vram->write(i + 0x8100, vram_test_4[i]);
vram.write(i + 0x8100, vram_test_4[i]);
}
vram->write(0x8040, 04);
vram->write(0x8042, 06);
vram->write(0x8044, 04);
vram->write(0x8046, 06);
vram->write(0x8048, 04);
vram.write(0x8040, 04);
vram.write(0x8042, 06);
vram.write(0x8044, 04);
vram.write(0x8046, 06);
vram.write(0x8048, 04);
vram->write(0x80C0, 04);
vram->write(0x80C2, 06);
vram->write(0x80C4, 04);
vram->write(0x80C6, 06);
vram->write(0x80C8, 04);
vram.write(0x80C0, 04);
vram.write(0x80C2, 06);
vram.write(0x80C4, 04);
vram.write(0x80C6, 06);
vram.write(0x80C8, 04);
vram->write(0xC000, 0x0C);
vram.write(0xC000, 0x0C);
}
void populateCgramAladin(const std::shared_ptr<Ram::Ram> &cgram)
void populateCgramAladin(Ram::Ram &cgram)
{
int cgram_dump[] = {
0xCE, 0x69, 0xDF, 0x63, 0xDE, 0x16, 0x8B, 0x00, 0x00, 0x00, 0xBF, 0x67, 0x98, 0x42, 0x0E, 0x15, 0x00, 0x00,
@@ -118,11 +118,11 @@ namespace ComSquare::PPU::Utils::Debug
0xC9, 0x0C, 0xD2, 0x25, 0xA5, 0x14, 0x00, 0x00, -1
};
for (int i = 0; cgram_dump[i] != -1; i++) {
cgram->write(i, cgram_dump[i]);
cgram.write(i, cgram_dump[i]);
}
}
void populateVramAladin(const std::shared_ptr<Ram::Ram> &vram)
void populateVramAladin(Ram::Ram &vram)
{
static int vram_dump[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -3768,7 +3768,7 @@ namespace ComSquare::PPU::Utils::Debug
0x24, 0x7D, 0x1A, 0x79, 0x16, 0x31, 0x18, 0x39, 0x17, 0x17, 0x0D, 0x0D, 0x07, 0x07, 0x01, 0x01, -1
};
for (int i = 0; vram_dump[i] != -1; i++) {
vram->write(i, vram_dump[i]);
vram.write(i, vram_dump[i]);
}
}
@@ -3858,7 +3858,8 @@ namespace ComSquare::PPU::Utils::Debug
ppu._registers._t[0].enableWindowDisplayBg2 = true;
}
void populateVram(std::shared_ptr<Ram::Ram> vram, int dumpNumber) {
void populateVram(Ram::Ram &vram, int dumpNumber)
{
switch (dumpNumber)
{
case 0: return populateVramTicTacToe(vram);
@@ -3868,7 +3869,7 @@ namespace ComSquare::PPU::Utils::Debug
}
}
void populateCgram(std::shared_ptr<Ram::Ram> cgram, int dumpNumber)
void populateCgram(Ram::Ram &cgram, int dumpNumber)
{
switch (dumpNumber)
{

View File

@@ -5,6 +5,8 @@
#pragma once
#include "Ram/Ram.hpp"
#define private public
#include "PPU.hpp"

View File

@@ -11,20 +11,15 @@
namespace ComSquare::PPU
{
TileRenderer::TileRenderer()
: _ram(nullptr),
_cgram(nullptr),
TileRenderer::TileRenderer(Ram::Ram &vram, Ram::Ram &cgram)
: _ram(vram),
_cgram(cgram),
_bpp(2),
_paletteIndex(0),
buffer({{{0}}})
{
}
void TileRenderer::setRam(std::shared_ptr<Ram::Ram> ram)
{
this->_ram = std::move(ram);
}
void TileRenderer::render(uint16_t tileAddress)
{
std::vector<uint16_t> palette = this->getPalette(this->_paletteIndex);
@@ -71,9 +66,9 @@ namespace ComSquare::PPU
uint8_t TileRenderer::read2BPPValue(uint16_t tileRowAddress, uint8_t pixelIndex)
{
// TODO unit test this
size_t size = this->_ram->getSize();
uint8_t highByte = this->_ram->read(tileRowAddress % size);
uint8_t lowByte = this->_ram->read((tileRowAddress + 1) % size);
size_t size = this->_ram.getSize();
uint8_t highByte = this->_ram.read(tileRowAddress % size);
uint8_t lowByte = this->_ram.read((tileRowAddress + 1) % size);
uint8_t shift = 8 - 1U - pixelIndex;
return ((highByte & (1U << shift)) | ((lowByte & (1U << shift)) << 1U)) >> shift;
@@ -108,18 +103,13 @@ namespace ComSquare::PPU
std::vector<uint16_t> palette(nbColors);
for (int i = 0; i < nbColors; i++) {
palette[i] = this->_cgram->read(addr);
palette[i] += this->_cgram->read(addr + 1) << 8U;
palette[i] = this->_cgram.read(addr);
palette[i] += this->_cgram.read(addr + 1) << 8U;
addr += 2;
}
return palette;
}
void TileRenderer::setCgram(std::shared_ptr<Ram::Ram> ram)
{
this->_cgram = std::move(ram);
}
int TileRenderer::getBpp() const
{
return this->_bpp;

View File

@@ -15,9 +15,9 @@ namespace ComSquare::PPU
static constexpr int TileByteSizeRow = 16;
//! @brief ram to render
std::shared_ptr<Ram::Ram> _ram;
Ram::Ram &_ram;
//! @brief cgram to access the colors
std::shared_ptr<Ram::Ram> _cgram;
Ram::Ram &_cgram;
//! @brief The bpp to use while rendering
int _bpp;
//! @brief The palette number to use while rendering
@@ -28,12 +28,8 @@ namespace ComSquare::PPU
std::array<std::array<uint32_t, 8>, 8> buffer;
//! @brief Set the palette to use for render (index of palette)
void setPaletteIndex(int paletteIndex);
//! @brief Set the ram to look for color references
void setCgram(std::shared_ptr<Ram::Ram> ram);
//! @brief Set the bpp to render graphics
void setBpp(int bpp);
//! @brief The ram to render
void setRam(std::shared_ptr<Ram::Ram> ram);
//! @brief Get the current bpp
int getBpp() const;
//! @brief Get the index of the current palette used
@@ -58,9 +54,13 @@ namespace ComSquare::PPU
//! @brief render the tile (8x8) at the tileAddress
//! @param tileAddress The address of the tile to render
void render(uint16_t tileAddress);
TileRenderer();
TileRenderer(Ram::Ram &vram, Ram::Ram &cgram);
//! @brief A tile renderer is copy constructable.
TileRenderer(const TileRenderer &) = default;
//! @brief A default destructor
~TileRenderer() = default;
TileRenderer &operator=(const TileRenderer &) = default;
//! @brief A tile render is not assignable.
TileRenderer &operator=(const TileRenderer &) = delete;
};
}

View File

@@ -1,36 +0,0 @@
//
// Created by anonymus-raccoon on 2/13/20.
//
#include <cstring>
#include "ExtendedRam.hpp"
#include "../Exceptions/InvalidAddress.hpp"
namespace ComSquare::Ram
{
ExtendedRam::ExtendedRam(size_t size)
: _size(size)
{
this->_data = new uint16_t[size];
std::memset(this->_data, 0, size * sizeof(uint16_t));
}
ExtendedRam::~ExtendedRam()
{
delete [] this->_data;
}
uint16_t ExtendedRam::read(uint24_t addr)
{
if (addr >= this->_size)
throw InvalidAddress("ExtendedRam Read", addr);
return this->_data[addr];
}
void ExtendedRam::write(uint24_t addr, uint16_t data)
{
if (addr >= this->_size)
throw InvalidAddress("ExtendedRam Write", addr);
this->_data[addr] = data;
}
}

View File

@@ -1,29 +0,0 @@
//
// Created by anonymus-raccoon on 2/13/20.
//
#ifndef COMSQUARE_EXTENDEDRAM_HPP
#define COMSQUARE_EXTENDEDRAM_HPP
#include <cstddef>
#include <cstdint>
#include "../Models/Int24.hpp"
namespace ComSquare::Ram
{
class ExtendedRam {
private:
uint16_t *_data;
size_t _size;
public:
explicit ExtendedRam(size_t size);
ExtendedRam(const ExtendedRam &) = delete;
ExtendedRam &operator=(const ExtendedRam &) = delete;
~ExtendedRam();
uint16_t read(uint24_t addr);
void write(uint24_t addr, uint16_t data);
};
}
#endif //COMSQUARE_EXTENDEDRAM_HPP

View File

@@ -10,41 +10,44 @@
namespace ComSquare::Ram
{
Ram::Ram(size_t size, Component type, std::string ramName)
: _size(size),
_ramType(type),
_ramName(std::move(ramName))
: _data(size),
_ramType(type),
_ramName(std::move(ramName))
{ }
uint8_t &Ram::operator[](uint24_t addr)
{
if (size == 0)
this->_data = nullptr;
else {
this->_data = new uint8_t[size];
std::memset(this->_data, 0, size * sizeof(uint8_t));
}
return this->_data[addr];
}
Ram::~Ram()
const uint8_t &Ram::operator[](uint24_t addr) const
{
delete[] this->_data;
return this->_data[addr];
}
uint8_t Ram::read(uint24_t addr)
{
// TODO read/write after the size of the rom should noop or behave like a mirror. I don't really know.
if (addr >= this->_size)
if (addr >= this->_data.size())
throw InvalidAddress(this->getName() + " read", addr);
return this->_data[addr];
}
void Ram::write(uint24_t addr, uint8_t data)
{
if (addr >= this->_size)
if (addr >= this->_data.size())
throw InvalidAddress(this->getName() + " write", addr);
this->_data[addr] = data;
}
uint24_t Ram::getSize() const
{
return this->_size;
return this->_data.size();
}
void Ram::setSize(uint24_t size)
{
this->_data.resize(size);
}
std::string Ram::getName() const
@@ -57,8 +60,13 @@ namespace ComSquare::Ram
return this->_ramType;
}
uint8_t *Ram::getData() const
std::span<uint8_t> Ram::getData()
{
return this->_data;
return std::span(this->_data);
}
std::span<const uint8_t> Ram::getData() const
{
return std::span(this->_data);
}
}

View File

@@ -2,20 +2,20 @@
// Created by anonymus-raccoon on 1/28/20.
//
#ifndef COMSQUARE_RAM_HPP
#define COMSQUARE_RAM_HPP
#pragma once
#include "../Memory/ARectangleMemory.hpp"
#include "Memory/ARectangleMemory.hpp"
#include <string>
#include <span>
#include <vector>
namespace ComSquare::Ram
{
class Ram : public Memory::ARectangleMemory {
class Ram : public Memory::ARectangleMemory
{
protected:
//! @brief The ram. (Can be used for WRam, SRam, VRam etc)
uint8_t *_data;
//! @brief The size of the ram (in bytes).
uint24_t _size;
std::vector<uint8_t> _data;
//! @brief An id identifying the type of memory this is (for the debugger)
Component _ramType;
//! @brief The name of this ram.
@@ -28,7 +28,7 @@ namespace ComSquare::Ram
//! @brief The ram can't be assigned.
Ram &operator=(Ram &) = delete;
//! @brief Destructor that free the ram.
~Ram() override;
~Ram() override = default;
//! @brief Read data from the component.
//! @param addr The local address to read from (0x0 should refer to the first byte of this component).
@@ -41,19 +41,34 @@ namespace ComSquare::Ram
//! @throw This function should thrown an InvalidAddress for address that are not mapped to the component.
void write(uint24_t addr, uint8_t data) override;
//! @brief Retrieve the data at the address given. This can be used instead of read or write.
//! @param addr The address of the data to retrieve.
//! @return The data at the address given as parameter.
uint8_t &operator[](uint24_t addr);
//! @brief Retrieve the data at the address given. This can be used instead of read or write.
//! @param addr The address of the data to retrieve.
//! @return The data at the address given as parameter.
const uint8_t &operator[](uint24_t addr) const;
//! @brief Get the name of this accessor (used for debug purpose)
std::string getName() const override;
[[nodiscard]] std::string getName() const override;
//! @brief Get the component of this accessor (used for debug purpose)
Component getComponent() const override;
[[nodiscard]] Component getComponent() const override;
//! @brief Get the size of the ram in bytes.
uint24_t getSize() const override;
[[nodiscard]] uint24_t getSize() const override;
//! @brief Change the size of this ram.
//! @brief size The new size of this ram.
void setSize(uint24_t size);
//! @brief Get the raw data of the RAM
uint8_t *getData() const;
};
}
//! @return A raw accessor to the data.
[[nodiscard]] std::span<uint8_t> getData();
#endif //COMSQUARE_RAM_HPP
//! @brief Get the raw data of the RAM
//! @return A raw accessor to the data.
[[nodiscard]] std::span<const uint8_t> getData() const;
};
}

View File

@@ -2,8 +2,7 @@
// Created by cbihan on 1/30/20.
//
#ifndef COMSQUARE_IRENDERER_HPP
#define COMSQUARE_IRENDERER_HPP
#pragma once
#include <string>
#include <span>
@@ -34,10 +33,7 @@ namespace ComSquare
//! @brief Playing all samples from buffer
//! @param samples Buffer containing samples
//! @param sampleCount number of samples inside buffer
virtual void playAudio(std::span<int16_t> samples, uint64_t sampleCount) = 0;
virtual void playAudio(std::span<int16_t> samples) = 0;
};
}
}
#endif //COMSQUARE_IRENDERER_HPP

View File

@@ -6,36 +6,21 @@
namespace ComSquare::Renderer
{
void NoRenderer::setWindowName(std::string &newWindowName)
{
(void)newWindowName;
}
void NoRenderer::setWindowName(std::string &)
{}
void NoRenderer::drawScreen() { }
void NoRenderer::drawScreen()
{}
void NoRenderer::putPixel(unsigned y, unsigned x, uint32_t rgba)
{
(void)x;
(void)y;
(void)rgba;
}
void NoRenderer::putPixel(unsigned, unsigned, uint32_t)
{}
void NoRenderer::playAudio(std::span<int16_t>, uint64_t)
{
}
void NoRenderer::playAudio(std::span<int16_t>)
{}
void NoRenderer::getEvents() { }
NoRenderer::NoRenderer(unsigned int, unsigned int, int)
{}
NoRenderer::NoRenderer(unsigned int height, unsigned int width, int maxFPS)
{
(void)height;
(void)width;
(void)maxFPS;
}
void NoRenderer::createWindow(SNES &snes, int maxFPS)
{
(void)snes;
(void)maxFPS;
}
void NoRenderer::createWindow(SNES &, int)
{}
}

View File

@@ -2,15 +2,15 @@
// Created by anonymus-raccoon on 2/5/20.
//
#ifndef COMSQUARE_NORENDERER_HPP
#define COMSQUARE_NORENDERER_HPP
#pragma once
#include "IRenderer.hpp"
namespace ComSquare::Renderer
{
//! @brief A renderer that discard everything you give. (Used for tests).
class NoRenderer : public IRenderer {
class NoRenderer : public IRenderer
{
public:
//! @brief Set a new name to the window, if there is already a name it will be overwrite.
//! @param newWindowName new title for the window.
@@ -25,9 +25,7 @@ namespace ComSquare::Renderer
//! @brief Playing all samples from buffer
//! @param samples Buffer containing samples
//! @param sampleCount number of samples inside buffer
void playAudio(std::span<int16_t> samples, uint64_t sampleCount) override;
//! @brief Get the inputs from the Window
void getEvents();
void playAudio(std::span<int16_t> samples) override;
//! @brief Use this function to create the window.
//! @param maxFPS The number of FPS you aim to run on.
void createWindow(SNES &snes, int maxFPS) override;
@@ -41,5 +39,3 @@ namespace ComSquare::Renderer
~NoRenderer() = default;
};
}
#endif //COMSQUARE_NORENDERER_HPP

View File

@@ -7,12 +7,15 @@
#include <QIcon>
#include <QMenuBar>
#include <iostream>
#include <QDir>
#include <QFileDialog>
#include "Models/Logger.hpp"
#include "SNES.hpp"
#include "QtSFML.hpp"
#ifdef Q_WS_X11
#include <Qt/qx11info_x11.h>
#include <X11/Xlib.h>
#include <Qt/qx11info_x11.h>
#include <X11/Xlib.h>
#endif
namespace ComSquare::Renderer
@@ -33,9 +36,9 @@ namespace ComSquare::Renderer
this->_sfWidget->putPixel(y, x, rgba);
}
void QtSFML::playAudio(std::span<int16_t> samples, uint64_t sampleCount)
void QtSFML::playAudio(std::span<int16_t> samples)
{
this->_sfWidget->playAudio(samples, sampleCount);
this->_sfWidget->playAudio(samples);
}
void QtSFML::drawScreen() { }
@@ -54,23 +57,36 @@ namespace ComSquare::Renderer
{
try {
this->_snes.update();
} catch (const DebuggableError &e) {
std::cout << "Invalid rom's instruction: " << e.what() << std::endl;
}
#ifdef DEBUGGER_ENABLED
catch (const DebuggableError &e) {
logMsg(LogLevel::ERROR, "Invalid rom's instruction: " << e.what());
this->_snes.enableCPUDebuggingWithError(e);
} catch (std::exception &e) {
}
#endif
catch (const std::exception &e) {
std::cerr << "An error occurred: " << e.what() << std::endl;
QApplication::quit();
}
}
void QtFullSFML::enableDebugCPU()
void QtFullSFML::openRom()
{
this->_snes.enableCPUDebugging();
auto rom = QFileDialog::getOpenFileName(nullptr, tr("Open a ROM"), QDir::homePath(),
tr("Rom files (*.sfc, *.smc);;Audio rom files (*.spc);;All files (*)"));
if (!rom.isEmpty())
this->_snes.loadRom(rom.toStdString());
}
void QtFullSFML::reset()
{
this->_snes.cpu->RESB();
this->_snes.cpu.RESB();
}
#ifdef DEBUGGER_ENABLED
void QtFullSFML::enableDebugCPU()
{
this->_snes.enableCPUDebugging();
}
void QtFullSFML::enableRamViewer()
@@ -95,20 +111,21 @@ namespace ComSquare::Renderer
void QtFullSFML::enableCgramViewer()
{
this->_snes.enableCgramDebugging();
this->_snes.enableCgramViewer();
}
void QtFullSFML::enableRegisterViewer()
{
this->_snes.enableRegisterDebugging();
this->_snes.enableRegisterViewer();
}
void QtFullSFML::enableTileViewer()
{
this->_snes.enableTileViewerDebugging();
this->_snes.enableTileViewer();
}
#endif
QtSFMLWindow::QtSFMLWindow(unsigned int height, unsigned int width)
QtSFMLWindow::QtSFMLWindow(int height, int width)
: QtSFML(&this->_window)
{
this->_window.resize(width, height);
@@ -118,60 +135,61 @@ namespace ComSquare::Renderer
void QtSFMLWindow::createWindow(SNES &snes, int maxFPS)
{
QtSFML::createWindow(snes, maxFPS);
this->setWindowName(snes.cartridge->header.gameName);
this->setWindowName(snes.cartridge.header.gameName);
this->_window.setCentralWidget(this->_sfWidget);
QMenu *file = this->_window.menuBar()->addMenu("&File");
//TODO implement rom opening from this menu.
(void)file;
auto *open = new QAction("Open", &this->_window);
QMainWindow::connect(open, &QAction::triggered, this->_sfWidget, &QtFullSFML::openRom);
file->addAction(open);
QMenu *game = this->_window.menuBar()->addMenu("&Game");
QAction *reset = new QAction("Reset", &this->_window);
auto *reset = new QAction("Reset", &this->_window);
QMainWindow::connect(reset, &QAction::triggered, this->_sfWidget, &QtFullSFML::reset);
game->addAction(reset);
#ifdef DEBUGGER_ENABLED
QMenu *debugger = this->_window.menuBar()->addMenu("&Debugger");
QAction *cpuDebugger = new QAction("CPU's Debugger", &this->_window);
auto *cpuDebugger = new QAction("CPU's Debugger", &this->_window);
cpuDebugger->setShortcut(Qt::Key_F1);
QMainWindow::connect(cpuDebugger, &QAction::triggered, this->_sfWidget, &QtFullSFML::enableDebugCPU);
debugger->addAction(cpuDebugger);
QAction *ramViewer = new QAction("Memory viewer", &this->_window);
auto *ramViewer = new QAction("Memory viewer", &this->_window);
ramViewer->setShortcut(Qt::Key_F2);
QMainWindow::connect(ramViewer, &QAction::triggered, this->_sfWidget, &QtFullSFML::enableRamViewer);
debugger->addAction(ramViewer);
QAction *headerViewer = new QAction("Header viewer", &this->_window);
auto *headerViewer = new QAction("Header viewer", &this->_window);
headerViewer->setShortcut(Qt::Key_F3);
QMainWindow::connect(headerViewer, &QAction::triggered, this->_sfWidget, &QtFullSFML::enableHeaderViewer);
debugger->addAction(headerViewer);
QAction *apuDebugger = new QAction("APU's Debugger", &this->_window);
auto *apuDebugger = new QAction("APU's Debugger", &this->_window);
apuDebugger->setShortcut(Qt::Key_F4);
QMainWindow::connect(apuDebugger, &QAction::triggered, this->_sfWidget, &QtFullSFML::enableDebugAPU);
debugger->addAction(apuDebugger);
QAction *busDebugger = new QAction("Memory bus Viewer", &this->_window);
auto *busDebugger = new QAction("Memory bus Viewer", &this->_window);
busDebugger->setShortcut(Qt::Key_F5);
QMainWindow::connect(busDebugger, &QAction::triggered, this->_sfWidget, &QtFullSFML::enableDebugBus);
debugger->addAction(busDebugger);
QAction *cgramDebugger = new QAction("Palette Viewer", &this->_window);
auto *cgramDebugger = new QAction("Palette Viewer", &this->_window);
cgramDebugger->setShortcut(Qt::Key_F6);
QMainWindow::connect(cgramDebugger, &QAction::triggered, this->_sfWidget, &QtFullSFML::enableCgramViewer);
debugger->addAction(cgramDebugger);
QAction *registerDebugger = new QAction("Registers Viewer", &this->_window);
auto *registerDebugger = new QAction("Registers Viewer", &this->_window);
registerDebugger->setShortcut(Qt::Key_F7);
QMainWindow::connect(registerDebugger, &QAction::triggered, this->_sfWidget, &QtFullSFML::enableRegisterViewer);
debugger->addAction(registerDebugger);
QAction *tileDebugger = new QAction("Tile Viewer", &this->_window);
auto *tileDebugger = new QAction("Tile Viewer", &this->_window);
tileDebugger->setShortcut(Qt::Key_F8);
QMainWindow::connect(tileDebugger, &QAction::triggered, this->_sfWidget, &QtFullSFML::enableTileViewer);
debugger->addAction(tileDebugger);
#endif
this->_window.show();
}

View File

@@ -2,16 +2,15 @@
// Created by anonymus-raccoon on 2/15/20.
//
#ifndef COMSQUARE_QTSFML_HPP
#define COMSQUARE_QTSFML_HPP
#pragma once
#include <QtWidgets/QWidget>
#include <SFML/Graphics/RenderWindow.hpp>
#include <QtCore/QTimer>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QGridLayout>
#include "../IRenderer.hpp"
#include "../SFRenderer.hpp"
#include "Renderer/IRenderer.hpp"
#include "Renderer/SFRenderer.hpp"
#include "QtWidgetSFML.hpp"
namespace ComSquare::Renderer
@@ -23,6 +22,10 @@ namespace ComSquare::Renderer
SNES &_snes;
void onUpdate() override;
public:
//! @brief Open the select rom dialog and load a new one if the option is selected.
void openRom();
#ifdef DEBUGGER_ENABLED
//! @brief Action called when clicking on the enable CPU debugger button.
void enableDebugCPU();
//! @brief Action called when clicking on the enable Ram viewer button.
@@ -39,6 +42,7 @@ namespace ComSquare::Renderer
void enableRegisterViewer();
//! @brief Action called when clicking on the enable Tile viewer button
void enableTileViewer();
#endif
//! @brief Action called when clicking on the reset button.
void reset();
@@ -46,7 +50,7 @@ namespace ComSquare::Renderer
QtFullSFML(SNES &snes, QWidget* parent, const QPoint& position, const QSize& size, int frameRate = 0);
QtFullSFML(const QtFullSFML &) = delete;
QtFullSFML &operator=(const QtFullSFML &) = delete;
~QtFullSFML() = default;
~QtFullSFML() override = default;
};
//! @brief A SFML renderer inside a QT widget.
@@ -60,7 +64,7 @@ namespace ComSquare::Renderer
public:
//! @brief Use this function to create the window.
//! @param maxFPS The number of FPS you aim to run on.
virtual void createWindow(SNES &snes, int maxFPS) override;
void createWindow(SNES &snes, int maxFPS) override;
//! @brief Add a pixel to the buffer to the coordinates x, y with the color rgba.
//! @param X horizontal index.
//! @param Y vertical index.
@@ -70,13 +74,12 @@ namespace ComSquare::Renderer
void drawScreen() override;
//! @brief Playing all samples from buffer
//! @param samples Buffer containing samples
//! @param sampleCount number of samples inside buffer
void playAudio(std::span<int16_t> samples, uint64_t sampleCount) override;
void playAudio(std::span<int16_t> samples) override;
//! @brief Set a new name to the window, if there is already a name it will be overwrite.
//! @param newWindowName new title for the window.
void setWindowName(std::string &newWindowName) override;
//! @brief Constructor that return a SFML renderer inside a QT widget.
QtSFML(QWidget *parentWidget);
explicit QtSFML(QWidget *parentWidget);
QtSFML(const QtSFML &) = delete;
QtSFML &operator=(const QtSFML &) = delete;
~QtSFML() = default;
@@ -93,11 +96,9 @@ namespace ComSquare::Renderer
//! @brief Constructor that return a SFML renderer inside a QT window.
//! @param height _height of the window.
//! @param width _width of the window.
QtSFMLWindow(unsigned int height, unsigned int width);
QtSFMLWindow(int height, int width);
QtSFMLWindow(const QtSFMLWindow &) = delete;
QtSFMLWindow &operator=(const QtSFMLWindow &) = delete;
~QtSFMLWindow() = default;
};
}
#endif //COMSQUARE_QTSFML_HPP

View File

@@ -6,8 +6,9 @@
namespace ComSquare::Renderer
{
QtWidgetSFML::QtWidgetSFML(QWidget *parent, const QPoint &position, const QSize &size, int frameRate) :
QWidget(parent), SFRenderer(size.height(), size.width())
QtWidgetSFML::QtWidgetSFML(QWidget *parent, const QPoint &position, const QSize &size, int frameRate)
: QWidget(parent),
SFRenderer(size.height(), size.width())
{
this->setAttribute(Qt::WA_PaintOnScreen);
this->setAttribute(Qt::WA_OpaquePaintEvent);
@@ -25,10 +26,10 @@ namespace ComSquare::Renderer
if (!this->_isInitialized) {
// Under X11, we need to flush the commands sent to the server to ensure that
// SFML will get an updated view of the windows
#ifdef Q_WS_X11
XFlush(QX11Info::display());
#endif
this->_window.create((sf::WindowHandle)this->winId());
#ifdef Q_WS_X11
XFlush(QX11Info::display());
#endif
this->_window.create(static_cast<sf::WindowHandle>(this->winId()));
this->_window.setFramerateLimit(60);
this->_onInit();
@@ -49,5 +50,6 @@ namespace ComSquare::Renderer
this->drawScreen();
}
void QtWidgetSFML::_onInit(){ }
void QtWidgetSFML::_onInit()
{}
}

View File

@@ -9,7 +9,7 @@
#include <QtCore/QTimer>
#include <SFML/Graphics/Sprite.hpp>
#include <SFML/Graphics/Texture.hpp>
#include "../SFRenderer.hpp"
#include "Renderer/SFRenderer.hpp"
namespace ComSquare::Renderer
{

View File

@@ -2,13 +2,11 @@
// Created by cbihan on 1/30/20.
//
#include "SNES.hpp"
#include "SFRenderer.hpp"
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include <SFML/System.hpp>
#include <SFML/Window.hpp>
#include <SFML/Graphics/RenderWindow.hpp>
#include "SNES.hpp"
#include <iostream>
namespace ComSquare::Renderer
@@ -29,7 +27,7 @@ namespace ComSquare::Renderer
if (icon.loadFromFile("resources/Logo.png"))
this->_window.setIcon(314, 314, icon.getPixelsPtr());
this->_window.setFramerateLimit(maxFPS);
this->setWindowName(snes.cartridge->header.gameName);
this->setWindowName(snes.cartridge.header.gameName);
while (!this->shouldExit) {
snes.update();
@@ -55,9 +53,9 @@ namespace ComSquare::Renderer
this->_window.display();
}
void SFRenderer::playAudio(std::span<int16_t> samples, uint64_t sampleCount)
void SFRenderer::playAudio(std::span<int16_t> samples)
{
this->_soundBuffer.loadFromSamples(samples.data(), sampleCount, 2, 32040);
this->_soundBuffer.loadFromSamples(samples.data(), samples.size(), 2, 32040);
this->_sound.play();
}

View File

@@ -56,7 +56,7 @@ namespace ComSquare::Renderer
//! @brief Playing all samples from buffer
//! @param samples Buffer containing samples
//! @param sampleCount number of samples inside buffer
void playAudio(std::span<int16_t> samples, uint64_t sampleCount) override;
void playAudio(std::span<int16_t> samples) override;
//! @brief Get the inputs from the Window
void getEvents();
//! @brief Use this function to create the window.

View File

@@ -2,208 +2,175 @@
// Created by anonymus-raccoon on 1/27/20.
//
#include <ios>
#include "SNES.hpp"
#include <iostream>
#ifdef DEBUGGER_ENABLED
#include "Debugger/CPU/CPUDebug.hpp"
#include "Debugger/APUDebug.hpp"
#include "Debugger/MemoryBusDebug.hpp"
#include "Debugger/CGramDebug.hpp"
#include "Debugger/TileViewer/TileViewer.hpp"
#endif
#include <ios>
namespace ComSquare
{
SNES::SNES(const std::string &romPath, Renderer::IRenderer &renderer) :
bus(std::make_shared<Memory::MemoryBus>()),
cartridge(new Cartridge::Cartridge(romPath)),
wram(new Ram::Ram(16384, WRam, "WRam")),
sram(new Ram::Ram(this->cartridge->header.sramSize, SRam, "SRam")),
cpu(new CPU::CPU(this->bus, cartridge->header)),
ppu(new PPU::PPU(renderer)),
apu(new APU::APU(renderer))
SNES::SNES(Renderer::IRenderer &renderer)
: bus(),
cartridge(),
wram(16384, WRam, "WRam"),
sram(0, SRam, "SRam"),
cpu(this->bus, cartridge.header),
ppu(renderer),
apu(renderer)
{}
SNES::SNES(const std::string &romPath, Renderer::IRenderer &renderer)
: bus(),
cartridge(romPath),
wram(16384, WRam, "WRam"),
sram(this->cartridge.header.sramSize, SRam, "SRam"),
cpu(this->bus, cartridge.header),
ppu(renderer),
apu(renderer)
{
this->bus->mapComponents(*this);
if (this->cartridge->getType() == Cartridge::Audio)
this->apu->loadFromSPC(this->cartridge);
this->bus.mapComponents(*this);
if (this->cartridge.getType() == Cartridge::Audio)
this->apu.loadFromSPC(this->cartridge);
}
void SNES::update()
{
if (this->cartridge->getType() == Cartridge::Audio)
{
this->apu->update(0x01);
if (this->cartridge.getType() == Cartridge::Audio) {
this->apu.update(0x01);
return;
}
unsigned cycleCount = this->cpu->update();
this->ppu->update(cycleCount);
this->apu->update(cycleCount);
unsigned cycleCount = this->cpu.update(0x0C);
this->ppu.update(cycleCount);
this->apu.update(cycleCount);
}
void SNES::loadRom(const std::string &path)
{
this->cartridge.loadRom(path);
this->sram.setSize(this->cartridge.header.sramSize);
this->bus.mapComponents(*this);
this->cpu.RESB();
this->apu.reset();
if (this->cartridge.getType() == Cartridge::Audio)
this->apu.loadFromSPC(this->cartridge);
}
#ifdef DEBUGGER_ENABLED
void SNES::enableCPUDebuggingWithError(const DebuggableError &exception)
{
this->enableCPUDebugging(true);
#ifdef DEBUGGER_ENABLED
auto cpuDebug = std::static_pointer_cast<Debugger::CPUDebug>(this->cpu);
cpuDebug->showError(exception);
#else
(void)exception;
#endif
this->_cpuDebugger->showError(exception);
}
void SNES::enableCPUDebugging(bool pause)
{
#ifdef DEBUGGER_ENABLED
if (this->cpu->isDebugger()) {
auto cpuDebug = std::static_pointer_cast<Debugger::CPUDebug>(this->cpu);
cpuDebug->focus();
if (pause)
cpuDebug->pause(true);
} else {
this->cpu = std::make_shared<Debugger::CPUDebug>(*this->cpu, *this);
this->bus->mapComponents(*this);
}
#else
std::cerr << "Debugging features are not enabled. You can't enable the debugger." << std::endl;
(void)pause;
#endif
if (!this->_cpuDebugger.has_value())
this->_cpuDebugger.emplace(this->cpu, *this);
else {
this->_cpuDebugger->focus();
if (pause)
this->_cpuDebugger->pause(true);
}
}
void SNES::disableCPUDebugging()
{
this->cpu = std::make_shared<CPU::CPU>(*this->cpu);
this->bus->mapComponents(*this);
this->_cpuDebugger = std::nullopt;
}
void SNES::enableRamViewer()
{
#ifdef DEBUGGER_ENABLED
if (this->_ramViewer)
this->_ramViewer->focus();
else
this->_ramViewer = std::make_unique<Debugger::MemoryViewer>(*this, *this->bus);
#endif
if (this->_ramViewer)
this->_ramViewer->focus();
else
this->_ramViewer.emplace(*this, this->bus);
}
void SNES::disableRamViewer()
{
#ifdef DEBUGGER_ENABLED
this->_ramViewer = nullptr;
#endif
this->_ramViewer = std::nullopt;
}
void SNES::enableHeaderViewer()
{
#ifdef DEBUGGER_ENABLED
if (this->_headerViewer)
this->_headerViewer->focus();
else
this->_headerViewer = std::make_unique<Debugger::HeaderViewer>(*this);
#endif
if (this->_headerViewer)
this->_headerViewer->focus();
else
this->_headerViewer.emplace(*this);
}
void SNES::disableHeaderViewer()
{
#ifdef DEBUGGER_ENABLED
this->_headerViewer = nullptr;
#endif
this->_headerViewer = std::nullopt;
}
void SNES::enableAPUDebugging()
{
#ifdef DEBUGGER_ENABLED
if (this->apu->isDebugger())
std::static_pointer_cast<Debugger::APUDebug>(this->apu)->focus();
else {
this->apu = std::make_shared<Debugger::APUDebug>(*this->apu, *this);
this->bus->mapComponents(*this);
}
#else
std::cerr << "Debugging features are not enabled. You can't enable the debugger." << std::endl;
#endif
if (!this->_apuDebugger.has_value())
this->_apuDebugger.emplace(this->apu, *this);
else {
this->_apuDebugger->focus();
}
}
void SNES::disableAPUDebugging()
{
this->apu = std::make_shared<APU::APU>(*this->apu);
this->bus->mapComponents(*this);
this->_apuDebugger = std::nullopt;
}
void SNES::enableMemoryBusDebugging()
{
#ifdef DEBUGGER_ENABLED
if (this->bus->isDebugger())
std::static_pointer_cast<Debugger::MemoryBusDebug>(this->bus)->focus();
else
{
this->bus = std::make_shared<Debugger::MemoryBusDebug>(*this, *this->bus);
this->cpu->setMemoryBus(this->bus);
}
#else
std::cerr << "Debugging features are not enabled. You can't enable the debugger." << std::endl;
#endif
if (this->_busDebugger)
this->_busDebugger->focus();
else
this->_busDebugger.emplace(*this, this->bus);
this->cpu.setBus(this->_busDebugger.value());
}
void SNES::disableMemoryBusDebugging()
{
#ifdef DEBUGGER_ENABLED
this->bus = std::make_shared<Memory::MemoryBus>(*this->bus);
this->cpu->setMemoryBus(this->bus);
#else
std::cerr << "Debugging features are not enabled. You can't enable the debugger." << std::endl;
#endif
this->_busDebugger = std::nullopt;
this->cpu.setBus(this->bus);
}
void SNES::enableCgramDebugging()
void SNES::enableCgramViewer()
{
#ifdef DEBUGGER_ENABLED
if (this->_cgramViewer)
this->_cgramViewer->focus();
else
this->_cgramViewer = std::make_unique<Debugger::CGramDebug>(*this, *this->ppu);
#endif
if (this->_cgramViewer)
this->_cgramViewer->focus();
else
this->_cgramViewer.emplace(*this, this->ppu);
}
void SNES::disableCgramDebugging()
void SNES::disableCgramViewer()
{
#ifdef DEBUGGER_ENABLED
this->_cgramViewer = nullptr;
#endif
this->_cgramViewer = std::nullopt;
}
void SNES::disableRegisterDebugging()
void SNES::disableRegisterViewer()
{
#ifdef DEBUGGER_ENABLED
this->_registerViewer = nullptr;
#endif
this->_registerViewer = std::nullopt;
}
void SNES::enableRegisterDebugging()
void SNES::enableRegisterViewer()
{
#ifdef DEBUGGER_ENABLED
if (this->_registerViewer)
this->_registerViewer->focus();
else
this->_registerViewer = std::make_unique<Debugger::RegisterViewer>(*this);
#endif
if (this->_registerViewer)
this->_registerViewer->focus();
else
this->_registerViewer.emplace(*this);
}
void SNES::disableTileViewerDebugging()
void SNES::disableTileViewer()
{
#ifdef DEBUGGER_ENABLED
this->_tileViewer = nullptr;
#endif
this->_tileViewer = std::nullopt;
}
void SNES::enableTileViewerDebugging()
void SNES::enableTileViewer()
{
#ifdef DEBUGGER_ENABLED
if (this->_tileViewer)
this->_tileViewer->focus();
else
this->_tileViewer = std::make_unique<Debugger::TileViewer>(*this, *this->ppu);
#endif
if (this->_tileViewer)
this->_tileViewer->focus();
else
this->_tileViewer.emplace(*this, this->ppu);
}
}
#endif
}// namespace ComSquare

View File

@@ -2,63 +2,95 @@
// Created by anonymus-raccoon on 1/27/20.
//
#ifndef COMSQUARE_SNES_HPP
#define COMSQUARE_SNES_HPP
#pragma once
#include "Memory/MemoryBus.hpp"
#include "APU/APU.hpp"
#include "CPU/CPU.hpp"
#include "Cartridge/Cartridge.hpp"
#include "Ram/Ram.hpp"
#include "PPU/PPU.hpp"
#include "APU/APU.hpp"
#include "Renderer/IRenderer.hpp"
#include "Exceptions/DebuggableError.hpp"
#include "Memory/MemoryBus.hpp"
#include "PPU/PPU.hpp"
#include "Ram/Ram.hpp"
#include "Renderer/IRenderer.hpp"
#include <optional>
#ifdef DEBUGGER_ENABLED
#include "Debugger/MemoryViewer.hpp"
#include "Debugger/HeaderViewer.hpp"
#include "Debugger/CGramDebug.hpp"
#include "Debugger/RegisterViewer.hpp"
#include "Debugger/TileViewer/TileViewer.hpp"
#include "Debugger/CPU/CPUDebug.hpp"
#include "Debugger/MemoryViewer.hpp"
#include "Debugger/HeaderViewer.hpp"
#include "Debugger/MemoryBusDebug.hpp"
#include "Debugger/APUDebug.hpp"
#include "Debugger/CGramDebug.hpp"
#include "Debugger/RegisterViewer.hpp"
#include "Debugger/TileViewer/TileViewer.hpp"
#endif
namespace ComSquare
{
//! @brief Container of all the components of the SNES.
class SNES {
class SNES
{
private:
#ifdef DEBUGGER_ENABLED
//! @brief The CPU's debugger with disassembly, pause, step by step...
std::optional<Debugger::CPU::CPUDebug> _cpuDebugger;
//! @brief A debugger that shows every read and write made over the bus.
std::optional<Debugger::MemoryBusDebug> _busDebugger;
//! @brief The APU's debugger with disassembly, pause, step by step...
std::optional<Debugger::APU::APUDebug> _apuDebugger;
//! @brief The window that allow the user to view a memory.
std::unique_ptr<Debugger::MemoryViewer> _ramViewer;
std::optional<Debugger::MemoryViewer> _ramViewer;
//! @brief The window that allow the user to view the cartridge's header.
std::unique_ptr<Debugger::HeaderViewer> _headerViewer;
std::optional<Debugger::HeaderViewer> _headerViewer;
//! @brief The window that allow the user to view the CGRAM.
std::unique_ptr<Debugger::CGramDebug> _cgramViewer;
std::optional<Debugger::CGramDebug> _cgramViewer;
//! @brief The window that allow the user to view registers.
std::unique_ptr<Debugger::RegisterViewer> _registerViewer;
//! @brief The window that allow the user to view the cgram as tiles.
std::unique_ptr<Debugger::TileViewer> _tileViewer;
std::optional<Debugger::RegisterViewer> _registerViewer;
//! @brief The window that allow the user to view the CGRAM as tiles.
std::optional<Debugger::TileViewer> _tileViewer;
#endif
public:
//! @brief The memory bus that map addresses to components.
std::shared_ptr<Memory::MemoryBus> bus;
Memory::MemoryBus bus;
//! @brief Cartridge containing instructions (ROM).
std::shared_ptr<Cartridge::Cartridge> cartridge;
Cartridge::Cartridge cartridge;
//! @brief Work Ram shared by all the components.
std::shared_ptr<Ram::Ram> wram;
Ram::Ram wram;
//! @brief Save Ram residing inside the Cartridge in a real SNES.
std::shared_ptr<Ram::Ram> sram;
Ram::Ram sram;
//! @brief Central Processing Unit of the SNES.
std::shared_ptr<CPU::CPU> cpu;
CPU::CPU cpu;
//! @brief Picture Processing Unit of the SNES
std::shared_ptr<PPU::PPU> ppu;
PPU::PPU ppu;
//! @brief Audio Processing Unit if the SNES
std::shared_ptr<APU::APU> apu;
APU::APU apu;
//! @brief Create all the components using a common memory bus for all of them.
//! @param renderer The renderer to use.
explicit SNES(Renderer::IRenderer &renderer);
//! @brief Create all the components using a common memory bus for all of them and load a rom
//! @param ramPath The rom to load.
//! @param renderer The renderer to use.
SNES(const std::string &ramPath, Renderer::IRenderer &renderer);
//! @brief A SNES is not copyable.
SNES(const SNES &) = delete;
//! @brief A SNES can't be assigned
SNES &operator=(const SNES &) = delete;
//! @brief A default destructor.
~SNES() = default;
//! @brief Call this function to update all the components
void update();
//! @brief Load the rom at the given path
//! @param rom The path of the rom.
//! @throws InvalidRomException If the rom is invalid, this exception is thrown.
void loadRom(const std::string& path);
#ifdef DEBUGGER_ENABLED
//! @brief Disable the CPU's debugging window.
void disableCPUDebugging();
//! @brief Enable the CPU's debugging window.
@@ -82,25 +114,18 @@ namespace ComSquare
void disableMemoryBusDebugging();
//! @brief Enable the Memory Bus's debugging window.
void enableMemoryBusDebugging();
//! @brief Disable the Cgram's debugging window.
void disableCgramDebugging();
//! @brief Enable the Cgram's debugging window.
void enableCgramDebugging();
//! @brief Disable the CGRAM's debugging window.
void disableCgramViewer();
//! @brief Enable the CGRAM's debugging window.
void enableCgramViewer();
//! @brief Disable the Register's debugging window.
void disableRegisterDebugging();
void disableRegisterViewer();
//! @brief Enable the Register's debugging window.
void enableRegisterDebugging();
void enableRegisterViewer();
//! @brief Disable the TileViewer's debugging window.
void disableTileViewerDebugging();
void disableTileViewer();
//! @brief Enable the TileViewer's debugging window.
void enableTileViewerDebugging();
//! @brief Create all the components using a common memory bus for all of them.
SNES(const std::string &ramPath, Renderer::IRenderer &renderer);
SNES(const SNES &) = delete;
SNES &operator=(const SNES &) = delete;
~SNES() = default;
void enableTileViewer();
#endif
};
}
#endif //COMSQUARE_SNES_HPP
}// namespace ComSquare

View File

@@ -55,6 +55,9 @@ namespace ComSquare::Utility
{
return std::bitset<24>(i).to_string();
}
}
const std::string WHITESPACES = " \t\n\r\f\v";
std::ostream &operator<<(std::ostream &os, uint8_t value)
{
return os << ComSquare::Utility::to_hex(value);
}

View File

@@ -7,7 +7,7 @@
#include <string>
#include <ios>
#include <sstream>
#include "Models/Int24.hpp"
#include "Models/Ints.hpp"
namespace ComSquare::Utility
{
@@ -25,5 +25,5 @@ namespace ComSquare::Utility
std::string to_binary(uint16_t i);
std::string to_binary(uint24_t i);
extern const std::string WHITESPACES;
constexpr std::string_view WHITESPACES = " \t\n\r\f\v";
}

View File

@@ -15,36 +15,53 @@ void usage(char *bin)
std::cout << "ComSquare:" << std::endl
<< "\tUsage: " << bin << " rom_path [options]" << std::endl
<< "Options:" << std::endl
<< "\t-h, --help: \tDisplay this help message and exit." << std::endl
#ifdef DEBUGGER_ENABLED
<< "\t-c, --cpu: \tEnable the debugger of the CPU." << std::endl
<< "\t-m, --memory: \tEnable the memory viewer panel." << std::endl
<< "\t-h, --header: \tShow the header of the cartridge." << std::endl
<< "\t-H, --header: \tShow the header of the cartridge." << std::endl
<< "\t-b, --bus: \tShow the memory bus's log." << std::endl
<< "\t-g, --cgram: \tShow the palette viewer." << std::endl
<< "\t-r, --registers: \tShow the registers viewer." << std::endl;
<< "\t-r, --registers: \tShow the registers viewer." << std::endl
#endif
;
}
void parseArguments(int argc, char **argv, SNES &snes)
{
while (true) {
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'},
{"bus", no_argument, 0, 'b'},
{"cgram", no_argument, 0, 'g'},
{"registers", no_argument, 0, 'r'},
{0, 0, 0, 0}
};
int option_index = 0;
static struct option long_options[] = {
{"help", no_argument, 0, 'h'},
#ifdef DEBUGGER_ENABLED
{"cpu", no_argument, 0, 'c'},
{"apu", no_argument, 0, 'a'},
{"memory", no_argument, 0, 'm'},
{"header", no_argument, 0, 'H'},
{"bus", no_argument, 0, 'b'},
{"cgram", no_argument, 0, 'g'},
{"registers", no_argument, 0, 'r'},
#endif
{0, 0, 0, 0}
};
#ifdef DEBUGGER_ENABLED
char short_options[] = "hcamHbgr";
#else
char short_options[] = "h";
#endif
int c = getopt_long(argc, argv, "camhbgr", long_options, &option_index);
while (true) {
int c = getopt_long(argc, argv, short_options, long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 0:
usage(argv[0]);
break;
exit(2);
case 'h':
usage(argv[0]);
exit(0);
#ifdef DEBUGGER_ENABLED
case 'c':
snes.enableCPUDebugging();
break;
@@ -54,27 +71,34 @@ void parseArguments(int argc, char **argv, SNES &snes)
case 'm':
snes.enableRamViewer();
break;
case 'h':
case 'H':
snes.enableHeaderViewer();
break;
case 'b':
snes.enableMemoryBusDebugging();
break;
case 'g':
snes.enableCgramDebugging();
snes.enableCgramViewer();
break;
case 'r':
snes.enableRegisterDebugging();
snes.enableRegisterViewer();
break;
#endif
default:
break;
exit(2);
}
}
if (optind != argc - 1) {
usage(argv[0]);
exit(2);
}
snes.loadRom(argv[optind]);
}
int main(int argc, char **argv)
{
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
if (argc < 2) {
usage(argv[0]);
return 1;
}
@@ -82,9 +106,9 @@ int main(int argc, char **argv)
QApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton);
Renderer::QtSFMLWindow renderer(1100, 1100);
try {
SNES snes(argv[1], renderer);
renderer.createWindow(snes, 60);
parseArguments(argc, argv, snes);
auto snes = std::make_unique<SNES>(renderer);
parseArguments(argc, argv, *snes);
renderer.createWindow(*snes, 60);
return QApplication::exec();
}
catch(std::exception &ex) {

View File

@@ -2,12 +2,11 @@
// Created by Melefo on 12/02/2020.
//
#include <criterion/criterion.h>
#include "../tests.hpp"
#include "../../sources/SNES.hpp"
#include "../../sources/APU/APU.hpp"
#include "../../sources/Exceptions/InvalidAddress.hpp"
#include "../../sources/Exceptions/InvalidOpcode.hpp"
#include <catch2/catch_test_macros.hpp>
#include "tests.hpp"
#include "SNES.hpp"
#include "APU/APU.hpp"
#include "Exceptions/InvalidAddress.hpp"
using namespace ComSquare;
@@ -17,66 +16,61 @@ using namespace ComSquare;
// //
//////////////////////////////
Test(_internalRead, register)
TEST_CASE("register internalRead", "[internalRead]")
{
Init()
auto apu = snes.apu;int8_t result = 0;
apu->_registers.counter0 = 123;
result = apu->_internalRead(0x00FD);
cr_assert_eq(result, 123);
snes.apu._registers.counter0 = 123;
auto result = snes.apu._internalRead(0x00FD);
REQUIRE(result == 123);
}
Test(_internalRead, Page0)
TEST_CASE("Page0 read Read", "[Read]")
{
Init()
uint8_t result;
snes.apu._map.Page0._data[0x0010] = 123;
result = snes.apu._internalRead(0x0010);
REQUIRE(result == 123);
}
TEST_CASE("Page1 read Read", "[Read]")
{
Init()
auto apu = snes.apu;
uint8_t result = 0;
apu->_map->Page0._data[0x0010] = 123;
result = apu->_internalRead(0x0010);
cr_assert_eq(result, 123);
snes.apu._map.Page1._data[0x0042] = 123;
result = snes.apu._internalRead(0x0142);
REQUIRE(result == 123);
}
Test(_internalRead, Page1)
TEST_CASE("Memory internalRead", "[internalRead]")
{
Init()
auto apu = snes.apu;
uint8_t result = 0;
apu->_map->Page1._data[0x0042] = 123;
result = apu->_internalRead(0x0142);
cr_assert_eq(result, 123);
snes.apu._map.Memory._data[0xFCDC] = 123;
result = snes.apu._internalRead(0xFEDC);
REQUIRE(result == 123);
}
Test(_internalRead, Memory)
TEST_CASE("IPL internalRead", "[internalRead]")
{
Init()
auto apu = snes.apu;
uint8_t result = 0;
apu->_map->Memory._data[0xFCDC] = 123;
result = apu->_internalRead(0xFEDC);
cr_assert_eq(result, 123);
snes.apu._map.IPL._data[0x001F] = 123;
result = snes.apu._internalRead(0xFFDF);
REQUIRE(result == 123);
}
Test(_internalRead, IPL)
TEST_CASE("Invalid internalRead", "[internalRead]")
{
Init()
auto apu = snes.apu;
uint8_t result = 0;
apu->_map->IPL._data[0x001F] = 123;
result = apu->_internalRead(0xFFDF);
cr_assert_eq(result, 123);
}
Test(_internalRead, Invalid)
{
Init()
auto apu = snes.apu;
cr_assert_throw(apu->_internalRead(0x10000), InvalidAddress);
REQUIRE_THROWS_AS(snes.apu._internalRead(0x10000), InvalidAddress);
}
///////////////////////////////
@@ -85,57 +79,57 @@ Test(_internalRead, Invalid)
// //
///////////////////////////////
Test(_internalWrite, Page0)
TEST_CASE("Page0 write Write", "[Write]")
{
Init()
auto apu = snes.apu;
apu->_internalWrite(0x0001, 123);
cr_assert_eq(apu->_map->Page0._data[0x0001], 123);
snes.apu._internalWrite(0x0001, 123);
REQUIRE(snes.apu._map.Page0._data[0x0001] == 123);
}
Test(_internalWrite, register)
TEST_CASE("register write Write", "[Write]")
{
Init()
auto apu = snes.apu;
apu->_internalWrite(0x00F4, 123);
cr_assert_eq(apu->_registers.port0, 123);
snes.apu._internalWrite(0x00F4, 123);
REQUIRE(snes.apu._registers.port0 == 123);
}
Test(_internalWrite, Page1)
TEST_CASE("Page1 internalWrite", "[internalWrite]")
{
Init()
auto apu = snes.apu;
apu->_internalWrite(0x01FF, 123);
cr_assert_eq(apu->_map->Page1._data[0x00FF], 123);
snes.apu._internalWrite(0x01FF, 123);
REQUIRE(snes.apu._map.Page1._data[0x00FF] == 123);
}
Test(_internalWrite, Memory)
TEST_CASE("Memory write internalWrite", "[internalWrite]")
{
Init()
auto apu = snes.apu;
apu->_internalWrite(0x0789, 123);
cr_assert_eq(apu->_map->Memory._data[0x0589], 123);
snes.apu._internalWrite(0x0789, 123);
REQUIRE(snes.apu._map.Memory._data[0x0589] == 123);
}
Test(_internalWrite, IPL)
TEST_CASE("IPL internalWrite", "[internalWrite]")
{
Init()
auto apu = snes.apu;
apu->_internalWrite(0xFFF0, 123);
cr_assert_eq(apu->_map->IPL._data[0x0030], 123);
snes.apu._internalWrite(0xFFF0, 123);
REQUIRE(snes.apu._map.IPL._data[0x0030] == 123);
}
Test(_internalWrite, Invalid)
TEST_CASE("Invalid internalWrite", "[internalWrite]")
{
Init()
auto apu = snes.apu;
cr_assert_throw(apu->_internalWrite(0x10000, 123), InvalidAddress);
REQUIRE_THROWS_AS(snes.apu._internalWrite(0x10000, 123), InvalidAddress);
}
/////////////////////
@@ -144,23 +138,22 @@ Test(_internalWrite, Invalid)
// //
/////////////////////
Test(read, Valid)
TEST_CASE("Valid read", "[read]")
{
Init()
auto apu = snes.apu;
uint8_t result = 0;
apu->_registers.port2 = 123;
result = apu->read(0x02);
cr_assert_eq(result, 123);
snes.apu._registers.port2 = 123;
result = snes.apu.read(0x02);
REQUIRE(result == 123);
}
Test(read, Invalid)
TEST_CASE("Invalid read", "[read]")
{
Init()
auto apu = snes.apu;
cr_assert_throw(apu->read(0x10000), InvalidAddress);
REQUIRE_THROWS_AS(snes.apu.read(0x10000), InvalidAddress);
}
//////////////////////
@@ -169,21 +162,21 @@ Test(read, Invalid)
// //
//////////////////////
Test(write, Valid)
TEST_CASE("Valid write", "[write]")
{
Init()
auto apu = snes.apu;
apu->write(0x03, 123);
cr_assert_eq(apu->_registers.port3, 123);
snes.apu.write(0x03, 123);
REQUIRE(snes.apu._registers.port3 == 123);
}
Test(write, Invalid)
TEST_CASE("Invalid write", "[write]")
{
Init()
auto apu = snes.apu;
cr_assert_throw(apu->write(0x04, 123), InvalidAddress);
REQUIRE_THROWS_AS(snes.apu.write(0x04, 123), InvalidAddress);
}
///////////////////////////////////
@@ -192,15 +185,14 @@ Test(write, Invalid)
// //
///////////////////////////////////
Test(executeInstruction, Valid)
TEST_CASE("Valid executeInstruction", "[executeInstruction]")
{
Init()
auto apu = snes.apu;
uint8_t result = 0;
apu->_internalRegisters.pc = 0x00;
result = apu->_executeInstruction();
cr_assert_eq(result, 2);
snes.apu._internalRegisters.pc = 0x00;
result = snes.apu._executeInstruction();
REQUIRE(result == 2);
}
///////////////////////
@@ -209,24 +201,24 @@ Test(executeInstruction, Valid)
// //
///////////////////////
Test(update, running)
TEST_CASE("running update", "[update]")
{
Init()
auto apu = snes.apu;
apu->_internalRegisters.pc = 0x00;
apu->update(1);
cr_assert_eq(apu->_paddingCycles, 1);
snes.apu._internalRegisters.pc = 0x00;
snes.apu.update(1);
REQUIRE(snes.apu._paddingCycles == 1);
}
Test(update, stopped)
TEST_CASE("stopped update", "[update]")
{
Init()
auto apu = snes.apu;
apu->_state = APU::Stopped;
apu->update(1);
cr_assert_eq(apu->_paddingCycles, 0);
snes.apu._state = APU::Stopped;
snes.apu.update(1);
REQUIRE(snes.apu._paddingCycles == 0);
}
//////////////////////////
@@ -235,23 +227,23 @@ Test(update, stopped)
// //
//////////////////////////
Test(_get, direct)
TEST_CASE("direct get", "[get]")
{
Init()
auto apu = snes.apu;
apu->_internalRegisters.pc = 0x32;
apu->_internalWrite(0x32, 123);
cr_assert_eq(apu->_getDirectAddr(), 123);
snes.apu._internalRegisters.pc = 0x32;
snes.apu._internalWrite(0x32, 123);
REQUIRE(snes.apu._getDirectAddr() == 123);
}
Test(_get, absolute)
TEST_CASE("absolute get", "[get]")
{
Init()
auto apu = snes.apu;
apu->_internalRegisters.pc = 0x32;
apu->_internalWrite(0x32, 0b00001111);
apu->_internalWrite(0x33, 0b11110000);
cr_assert_eq(apu->_getAbsoluteAddr(), 61455);
snes.apu._internalRegisters.pc = 0x32;
snes.apu._internalWrite(0x32, 0b00001111);
snes.apu._internalWrite(0x33, 0b11110000);
REQUIRE(snes.apu._getAbsoluteAddr() == 61455);
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,164 +2,150 @@
// Created by Melefo on 26/02/2020.
//
#include <criterion/criterion.h>
#include "../tests.hpp"
#include "../../sources/SNES.hpp"
#include "../../sources/APU/APU.hpp"
#include "../../sources/Exceptions/InvalidAddress.hpp"
#include "../../sources/Exceptions/InvalidOpcode.hpp"
#include <catch2/catch_test_macros.hpp>
#include "tests.hpp"
#include "SNES.hpp"
#include "APU/APU.hpp"
#include "Exceptions/InvalidAddress.hpp"
using namespace ComSquare;
Test(apu_get, immediate)
TEST_CASE("immediate apu_get", "[apu_get]")
{
Init()
auto apu = snes.apu;
apu->_internalRegisters.pc = 0x32;
apu->_internalWrite(0x32, 0x40);
cr_assert_eq(apu->_getImmediateData(), 0x40);
snes.apu._internalRegisters.pc = 0x32;
snes.apu._internalWrite(0x32, 0x40);
REQUIRE(snes.apu._getImmediateData() == 0x40);
}
Test(apu_get, direct)
TEST_CASE("direct apu_get", "[apu_get]")
{
Init()
auto apu = snes.apu;
apu->_internalRegisters.pc = 0x32;
apu->_internalRegisters.p = true;
apu->_internalWrite(0x32, 0x40);
cr_assert_eq(apu->_getDirectAddr(), 0x140);
snes.apu._internalRegisters.pc = 0x32;
snes.apu._internalRegisters.p = true;
snes.apu._internalWrite(0x32, 0x40);
REQUIRE(snes.apu._getDirectAddr() == 0x140);
}
Test(apu_get, X)
TEST_CASE("X apu_get", "[apu_get]")
{
Init()
auto apu = snes.apu;
apu->_internalRegisters.x = 0x32;
apu->_internalRegisters.p = true;
cr_assert_eq(apu->_getIndexXAddr(), 0x132);
snes.apu._internalRegisters.x = 0x32;
snes.apu._internalRegisters.p = true;
REQUIRE(snes.apu._getIndexXAddr() == 0x132);
}
Test(apu_get, Y)
TEST_CASE("Y apu_get", "[apu_get]")
{
Init()
auto apu = snes.apu;
apu->_internalRegisters.y = 0x32;
apu->_internalRegisters.p = true;
cr_assert_eq(apu->_getIndexYAddr(), 0x132);
snes.apu._internalRegisters.y = 0x32;
snes.apu._internalRegisters.p = true;
REQUIRE(snes.apu._getIndexYAddr() == 0x132);
}
Test(apu_get, directbyX)
TEST_CASE("directbyX apu_get", "[apu_get]")
{
Init()
auto apu = snes.apu;
apu->_internalRegisters.pc = 0x32;
apu->_internalRegisters.x = 0x03;
apu->_internalWrite(0x32, 0x40);
cr_assert_eq(apu->_getDirectAddrByX(), 0x43);
snes.apu._internalRegisters.pc = 0x32;
snes.apu._internalRegisters.x = 0x03;
snes.apu._internalWrite(0x32, 0x40);
REQUIRE(snes.apu._getDirectAddrByX() == 0x43);
}
Test(apu_get, directbyY)
TEST_CASE("directbyY apu_get", "[apu_get]")
{
Init()
auto apu = snes.apu;
apu->_internalRegisters.pc = 0x32;
apu->_internalRegisters.y = 0x05;
apu->_internalWrite(0x32, 0x40);
cr_assert_eq(apu->_getDirectAddrByY(), 0x45);
snes.apu._internalRegisters.pc = 0x32;
snes.apu._internalRegisters.y = 0x05;
snes.apu._internalWrite(0x32, 0x40);
REQUIRE(snes.apu._getDirectAddrByY() == 0x45);
}
Test(apu_get, absolute)
TEST_CASE("absolute apu_get", "[apu_get]")
{
Init()
auto apu = snes.apu;
apu->_internalRegisters.pc = 0x32;
apu->_internalWrite(0x32, 0b00001111);
apu->_internalWrite(0x33, 0b11110000);
cr_assert_eq(apu->_getAbsoluteAddr(), 61455);
snes.apu._internalRegisters.pc = 0x32;
snes.apu._internalWrite(0x32, 0b00001111);
snes.apu._internalWrite(0x33, 0b11110000);
REQUIRE(snes.apu._getAbsoluteAddr() == 61455);
}
Test(apu_get, absolutebyx)
TEST_CASE("absolutebyx apu_get", "[apu_get]")
{
Init()
auto apu = snes.apu;
apu->_internalRegisters.pc = 0x32;
apu->_internalRegisters.x = 10;
apu->_internalWrite(0x32, 0b00001111);
apu->_internalWrite(0x33, 0b11110000);
apu->_internalWrite(0b1111000000001111 + 10, 255);
cr_assert_eq(apu->_getAbsoluteByXAddr(), 255);
snes.apu._internalRegisters.pc = 0x32;
snes.apu._internalRegisters.x = 10;
snes.apu._internalWrite(0x32, 0b00001111);
snes.apu._internalWrite(0x33, 0b11110000);
snes.apu._internalWrite(0b1111000000001111 + 10, 255);
REQUIRE(snes.apu._getAbsoluteByXAddr() == 255);
}
Test(apu_get, absoluteaddrbyx)
TEST_CASE("absoluteaddrbyx apu_get", "[apu_get]")
{
Init()
auto apu = snes.apu;
apu->_internalRegisters.pc = 0x32;
apu->_internalRegisters.x = 10;
apu->_internalWrite(0x32, 0b00001111);
apu->_internalWrite(0x33, 0b11110000);
cr_assert_eq(apu->_getAbsoluteAddrByX(), 61465);
snes.apu._internalRegisters.pc = 0x32;
snes.apu._internalRegisters.x = 10;
snes.apu._internalWrite(0x32, 0b00001111);
snes.apu._internalWrite(0x33, 0b11110000);
REQUIRE(snes.apu._getAbsoluteAddrByX() == 61465);
}
Test(apu_get, absoluteaddrbyy)
TEST_CASE("absoluteaddrbyy apu_get", "[apu_get]")
{
Init()
auto apu = snes.apu;
apu->_internalRegisters.pc = 0x32;
apu->_internalRegisters.y = 10;
apu->_internalWrite(0x32, 0b00001111);
apu->_internalWrite(0x33, 0b11110000);
cr_assert_eq(apu->_getAbsoluteAddrByY(), 61465);
snes.apu._internalRegisters.pc = 0x32;
snes.apu._internalRegisters.y = 10;
snes.apu._internalWrite(0x32, 0b00001111);
snes.apu._internalWrite(0x33, 0b11110000);
REQUIRE(snes.apu._getAbsoluteAddrByY() == 61465);
}
Test(apu_get, absolutebit)
TEST_CASE("absolutebit apu_get", "[apu_get]")
{
Init()
auto apu = snes.apu;
std::pair<uint24_t, uint24_t> result;
apu->_internalRegisters.pc = 0x32;
apu->_internalWrite(0x32, 0b00001111);
apu->_internalWrite(0x33, 0b11110000);
result = apu->_getAbsoluteBit();
cr_assert_eq(result.first, 4111);
cr_assert_eq(result.second, 7);
snes.apu._internalRegisters.pc = 0x32;
snes.apu._internalWrite(0x32, 0b00001111);
snes.apu._internalWrite(0x33, 0b11110000);
result = snes.apu._getAbsoluteBit();
REQUIRE(result.first == 4111);
REQUIRE(result.second == 7);
}
Test(apu_get, absolutebyxdirect)
TEST_CASE("absolutebyxdirect apu_get", "[apu_get]")
{
Init()
auto apu = snes.apu;
apu->_internalRegisters.pc = 0x32;
apu->_internalRegisters.p = true;
apu->_internalRegisters.x = 0x10;
apu->_internalWrite(0x32, 0x42);
apu->_internalWrite(0x152, 0b00001101);
apu->_internalWrite(0x253, 0b01101011);
cr_assert_eq(apu->_getAbsoluteDirectByXAddr(), 0b0110101100001101);
snes.apu._internalRegisters.pc = 0x32;
snes.apu._internalRegisters.p = true;
snes.apu._internalRegisters.x = 0x10;
snes.apu._internalWrite(0x32, 0x42);
snes.apu._internalWrite(0x152, 0b00001101);
snes.apu._internalWrite(0x253, 0b01101011);
REQUIRE(snes.apu._getAbsoluteDirectByXAddr() == 0b0110101100001101);
}
Test(apu_get, absolutedirectbyy)
TEST_CASE("absolutedirectbyy apu_get", "[apu_get]")
{
Init()
auto apu = snes.apu;
apu->_internalRegisters.pc = 0x32;
apu->_internalRegisters.p = true;
apu->_internalRegisters.y = 0x10;
apu->_internalWrite(0x32, 0x42);
apu->_internalWrite(0x142, 0b00001101);
apu->_internalWrite(0x243, 0b01101011);
cr_assert_eq(apu->_getAbsoluteDirectAddrByY(), 0b0110101100011101);
snes.apu._internalRegisters.pc = 0x32;
snes.apu._internalRegisters.p = true;
snes.apu._internalRegisters.y = 0x10;
snes.apu._internalWrite(0x32, 0x42);
snes.apu._internalWrite(0x142, 0b00001101);
snes.apu._internalWrite(0x243, 0b01101011);
REQUIRE(snes.apu._getAbsoluteDirectAddrByY() == 0b0110101100011101);
}

Some files were not shown because too many files have changed in this diff Show More