mirror of
https://github.com/zoriya/ComSquare.git
synced 2025-12-06 07:16:17 +00:00
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:
8
.github/workflows/build.yml
vendored
8
.github/workflows/build.yml
vendored
@@ -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
|
||||
|
||||
6
.github/workflows/buildwin.yml
vendored
6
.github/workflows/buildwin.yml
vendored
@@ -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/
|
||||
|
||||
14
.github/workflows/test.yml
vendored
14
.github/workflows/test.yml
vendored
@@ -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++
|
||||
|
||||
@@ -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
13
libs/FindCatch2.cmake
Normal 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()
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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 "???";
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
192
sources/CPU/Registers.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 ®Name,
|
||||
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 ®)
|
||||
{
|
||||
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
|
||||
@@ -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 ®);
|
||||
|
||||
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 ®Name,
|
||||
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
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 "???";
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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 "???";
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
};
|
||||
};
|
||||
53
sources/Memory/IMemoryBus.hpp
Normal file
53
sources/Memory/IMemoryBus.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
67
sources/Models/Callback.hpp
Normal file
67
sources/Models/Callback.hpp
Normal 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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
13
sources/Models/Ints.hpp
Normal 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
32
sources/Models/Logger.hpp
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "Ram/Ram.hpp"
|
||||
|
||||
#define private public
|
||||
#include "PPU.hpp"
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
{}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
225
sources/SNES.cpp
225
sources/SNES.cpp
@@ -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
|
||||
|
||||
111
sources/SNES.hpp
111
sources/SNES.hpp
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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
Reference in New Issue
Block a user