Compare commits
5 commits
fix/w4-war
...
master
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6504148654 | ||
![]() |
f495a88081 | ||
![]() |
57e0eb2fbe | ||
![]() |
e9264127c5 | ||
![]() |
9ce2fa6124 |
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,6 +4,7 @@
|
||||||
out/
|
out/
|
||||||
build/
|
build/
|
||||||
*.json
|
*.json
|
||||||
|
*.ini
|
||||||
|
|
||||||
*.nes
|
*.nes
|
||||||
!roms/nestest.nes
|
!roms/nestest.nes
|
||||||
|
|
|
@ -5,6 +5,9 @@ cmake_minimum_required (VERSION 3.8)
|
||||||
|
|
||||||
project ("NES Emulator")
|
project ("NES Emulator")
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED 17)
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
find_package(glfw3)
|
find_package(glfw3)
|
||||||
if(NOT glfw3_FOUND)
|
if(NOT glfw3_FOUND)
|
||||||
add_subdirectory("vendor/glfw")
|
add_subdirectory("vendor/glfw")
|
||||||
|
|
|
@ -14,14 +14,14 @@
|
||||||
#include "Debugger.hpp"
|
#include "Debugger.hpp"
|
||||||
#include "gfx/Window.hpp"
|
#include "gfx/Window.hpp"
|
||||||
|
|
||||||
void Application::Launch()
|
void Application::Launch(const char* rom)
|
||||||
{
|
{
|
||||||
glfwInit();
|
glfwInit();
|
||||||
|
|
||||||
Application* app = nullptr;
|
Application* app = nullptr;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
app = new Application;
|
app = new Application(rom);
|
||||||
}
|
}
|
||||||
catch (const std::runtime_error& err)
|
catch (const std::runtime_error& err)
|
||||||
{
|
{
|
||||||
|
@ -43,7 +43,7 @@ void Application::Launch()
|
||||||
glfwTerminate();
|
glfwTerminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::Application() :
|
Application::Application(const char* rom) :
|
||||||
bus(nullptr), window(nullptr)
|
bus(nullptr), window(nullptr)
|
||||||
{
|
{
|
||||||
LOG_CORE_INFO("Creating window");
|
LOG_CORE_INFO("Creating window");
|
||||||
|
@ -93,7 +93,7 @@ Application::Application() :
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
bus = new Bus(screen);
|
bus = new Bus(rom, screen);
|
||||||
debugger = new Debugger(bus);
|
debugger = new Debugger(bus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,5 +140,13 @@ bool Application::Update()
|
||||||
debugger->Render();
|
debugger->Render();
|
||||||
window->End();
|
window->End();
|
||||||
|
|
||||||
|
std::chrono::microseconds frametime = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - lastFrameTime);
|
||||||
|
lastFrameTime = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
if (frametime < std::chrono::microseconds(1000000 / 60)) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::microseconds(1000000 / 60) - frametime);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return !window->ShouldClose();
|
return !window->ShouldClose();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
class Bus;
|
class Bus;
|
||||||
class Window;
|
class Window;
|
||||||
class Debugger;
|
class Debugger;
|
||||||
|
@ -14,10 +16,10 @@ public:
|
||||||
/**
|
/**
|
||||||
* @brief Create and launch a new application.
|
* @brief Create and launch a new application.
|
||||||
*/
|
*/
|
||||||
static void Launch();
|
static void Launch(const char* rom);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Application();
|
Application(const char* rom);
|
||||||
~Application();
|
~Application();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,4 +35,6 @@ private:
|
||||||
Bus* bus;
|
Bus* bus;
|
||||||
Screen* screen;
|
Screen* screen;
|
||||||
Debugger* debugger;
|
Debugger* debugger;
|
||||||
|
|
||||||
|
std::chrono::steady_clock::time_point lastFrameTime;
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
#include "controllers/StandardController.hpp"
|
#include "controllers/StandardController.hpp"
|
||||||
|
|
||||||
Bus::Bus(Screen* screen) :
|
Bus::Bus(const char* rom, Screen* screen) :
|
||||||
cpu(this), ppu(this, screen), apu(this), cartridge(this)
|
cpu(this), ppu(this, screen), apu(this), cartridge(this)
|
||||||
{
|
{
|
||||||
LOG_CORE_INFO("Allocating RAM");
|
LOG_CORE_INFO("Allocating RAM");
|
||||||
|
@ -16,7 +16,7 @@ Bus::Bus(Screen* screen) :
|
||||||
palettes = std::vector<Byte>(0x20, 0);
|
palettes = std::vector<Byte>(0x20, 0);
|
||||||
|
|
||||||
LOG_CORE_INFO("Inserting cartridge");
|
LOG_CORE_INFO("Inserting cartridge");
|
||||||
cartridge.Load("roms/mario.nes");
|
cartridge.Load(rom);
|
||||||
|
|
||||||
LOG_CORE_INFO("Powering up CPU");
|
LOG_CORE_INFO("Powering up CPU");
|
||||||
cpu.Powerup();
|
cpu.Powerup();
|
||||||
|
|
|
@ -25,7 +25,7 @@ class Bus
|
||||||
friend class Palettes;
|
friend class Palettes;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Bus(Screen* screen);
|
Bus(const char* rom, Screen* screen);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Reboot the NES.
|
* @brief Reboot the NES.
|
||||||
|
|
|
@ -7,13 +7,26 @@ add_executable(nesemu
|
||||||
"mappers/Mapper000.cpp"
|
"mappers/Mapper000.cpp"
|
||||||
"Log.cpp"
|
"Log.cpp"
|
||||||
"PPU.cpp"
|
"PPU.cpp"
|
||||||
|
"APU.cpp"
|
||||||
"gfx/Window.cpp"
|
"gfx/Window.cpp"
|
||||||
|
"gfx/Input.cpp"
|
||||||
|
"gfx/Screen.cpp"
|
||||||
"debugger/CPUWatcher.cpp"
|
"debugger/CPUWatcher.cpp"
|
||||||
"debugger/Debugger.cpp"
|
"debugger/Debugger.cpp"
|
||||||
"debugger/PPUWatcher.cpp"
|
"debugger/PPUWatcher.cpp"
|
||||||
"debugger/Disassembler.cpp"
|
"debugger/Disassembler.cpp"
|
||||||
"debugger/MemoryViewer.cpp"
|
"debugger/MemoryViewer.cpp"
|
||||||
"debugger/NametableViewer.cpp" "ControllerPort.cpp" "controllers/StandardController.cpp" "gfx/Input.cpp" "debugger/ControllerPortViewer.cpp" "gfx/Screen.cpp" "debugger/Palettes.cpp" "APU.cpp" "debugger/PatternTableViewer.cpp" "mappers/Mapper003.cpp" "mappers/Mapper001.cpp" "debugger/OAMViewer.cpp")
|
"debugger/NametableViewer.cpp"
|
||||||
|
"debugger/ControllerPortViewer.cpp"
|
||||||
|
"debugger/PatternTableViewer.cpp"
|
||||||
|
"debugger/OAMViewer.cpp"
|
||||||
|
"debugger/Palettes.cpp"
|
||||||
|
"debugger/Logger.cpp"
|
||||||
|
"ControllerPort.cpp"
|
||||||
|
"controllers/StandardController.cpp"
|
||||||
|
"mappers/Mapper003.cpp"
|
||||||
|
"mappers/Mapper001.cpp"
|
||||||
|
)
|
||||||
|
|
||||||
target_include_directories(nesemu PRIVATE
|
target_include_directories(nesemu PRIVATE
|
||||||
mappers
|
mappers
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "PatternTableViewer.hpp"
|
#include "PatternTableViewer.hpp"
|
||||||
#include "ControllerPortViewer.hpp"
|
#include "ControllerPortViewer.hpp"
|
||||||
#include "Palettes.hpp"
|
#include "Palettes.hpp"
|
||||||
|
#include "Logger.hpp"
|
||||||
|
|
||||||
Debugger::Debugger(Bus* bus) :
|
Debugger::Debugger(Bus* bus) :
|
||||||
bus(bus)
|
bus(bus)
|
||||||
|
@ -32,6 +33,9 @@ Debugger::Debugger(Bus* bus) :
|
||||||
windows.push_back(new PatternTableViewer(this, bus->cartridge.GetMapper()));
|
windows.push_back(new PatternTableViewer(this, bus->cartridge.GetMapper()));
|
||||||
windows.push_back(new ControllerPortViewer(this, &bus->controllerPort));
|
windows.push_back(new ControllerPortViewer(this, &bus->controllerPort));
|
||||||
windows.push_back(new Palettes(this, bus));
|
windows.push_back(new Palettes(this, bus));
|
||||||
|
|
||||||
|
Logger::Init(this);
|
||||||
|
windows.push_back(Logger::GetInstance());
|
||||||
}
|
}
|
||||||
|
|
||||||
Debugger::~Debugger()
|
Debugger::~Debugger()
|
||||||
|
@ -42,6 +46,7 @@ Debugger::~Debugger()
|
||||||
|
|
||||||
bool Debugger::Frame()
|
bool Debugger::Frame()
|
||||||
{
|
{
|
||||||
|
Logger::GetInstance()->Log("Debugger", "Frame!\n");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (!bus->ppu.IsFrameDone())
|
while (!bus->ppu.IsFrameDone())
|
||||||
|
@ -61,7 +66,7 @@ bool Debugger::Frame()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Debugger::Update()
|
bool Debugger::Update()
|
||||||
|
@ -161,6 +166,10 @@ void Debugger::Render()
|
||||||
ImGui::InputScalar("Reset Vector", ImGuiDataType_U16, &resetVector, (const void*)0, (const void*)0, "%04X", ImGuiInputTextFlags_CharsHexadecimal);
|
ImGui::InputScalar("Reset Vector", ImGuiDataType_U16, &resetVector, (const void*)0, (const void*)0, "%04X", ImGuiInputTextFlags_CharsHexadecimal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
ImGui::Text("FPS: %f", ImGui::GetIO().Framerate);
|
||||||
|
|
||||||
for (DebugWindow* window : windows)
|
for (DebugWindow* window : windows)
|
||||||
{
|
{
|
||||||
if (window->isOpen) window->OnRender();
|
if (window->isOpen) window->OnRender();
|
||||||
|
|
80
src/debugger/Logger.cpp
Normal file
80
src/debugger/Logger.cpp
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
#include "Logger.hpp"
|
||||||
|
|
||||||
|
#include <imgui/imgui.h>
|
||||||
|
|
||||||
|
Logger::Logger(Debugger* debugger) :
|
||||||
|
DebugWindow("Log", debugger), autoScroll(true)
|
||||||
|
{
|
||||||
|
buffer.clear();
|
||||||
|
|
||||||
|
offsets.clear();
|
||||||
|
offsets.push_back(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Logger::Init(Debugger* debugger)
|
||||||
|
{
|
||||||
|
if (Logger::instance != nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::instance = new Logger(debugger);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Logger::Log(const char* module, const char* fmt, ...)
|
||||||
|
{
|
||||||
|
int old_size = buffer.size();
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
|
||||||
|
buffer.appendf("[%s] ", module);
|
||||||
|
buffer.appendfv(fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
for (int new_size = buffer.size(); old_size < new_size; old_size++) {
|
||||||
|
if (buffer[old_size] == '\n') {
|
||||||
|
offsets.push_back(old_size + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Logger::OnRender()
|
||||||
|
{
|
||||||
|
ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
|
||||||
|
|
||||||
|
if (!ImGui::Begin(title.c_str(), &isOpen)) {
|
||||||
|
ImGui::End();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::BeginPopup("Options")) {
|
||||||
|
ImGui::Checkbox("Auto scroll", &autoScroll);
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::BeginChild("scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar))
|
||||||
|
{
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
||||||
|
const char* buf = buffer.begin();
|
||||||
|
const char* buf_end = buffer.end();
|
||||||
|
|
||||||
|
ImGuiListClipper clipper;
|
||||||
|
clipper.Begin((int)offsets.size());
|
||||||
|
while (clipper.Step())
|
||||||
|
{
|
||||||
|
for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
|
||||||
|
{
|
||||||
|
const char* line_start = buf + offsets[line_no];
|
||||||
|
const char* line_end = (line_no + 1 < offsets.size()) ? (buf + offsets[line_no + 1] - 1) : buf_end;
|
||||||
|
ImGui::TextUnformatted(line_start, line_end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clipper.End();
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
|
||||||
|
if (autoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
|
||||||
|
ImGui::SetScrollHereY(1.0f);
|
||||||
|
}
|
||||||
|
ImGui::EndChild();
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
}
|
29
src/debugger/Logger.hpp
Normal file
29
src/debugger/Logger.hpp
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <imgui/imgui.h>
|
||||||
|
#include "DebugWindow.hpp"
|
||||||
|
|
||||||
|
class Logger final:
|
||||||
|
public DebugWindow
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void Init(Debugger* debugger);
|
||||||
|
|
||||||
|
static Logger* GetInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Log(const char* module, const char* fmt, ...);
|
||||||
|
virtual void OnRender() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Logger(Debugger* debugger);
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline static Logger* instance = nullptr;
|
||||||
|
|
||||||
|
ImGuiTextBuffer buffer;
|
||||||
|
std::vector<int> offsets;
|
||||||
|
bool autoScroll;
|
||||||
|
};
|
|
@ -11,6 +11,7 @@ Window::Window(uint16_t width, uint16_t height, const std::string& title) :
|
||||||
handle(nullptr)
|
handle(nullptr)
|
||||||
{
|
{
|
||||||
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
|
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
|
||||||
|
|
||||||
handle = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr);
|
handle = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr);
|
||||||
if (handle == nullptr)
|
if (handle == nullptr)
|
||||||
{
|
{
|
||||||
|
@ -20,6 +21,7 @@ Window::Window(uint16_t width, uint16_t height, const std::string& title) :
|
||||||
}
|
}
|
||||||
|
|
||||||
glfwMakeContextCurrent(handle);
|
glfwMakeContextCurrent(handle);
|
||||||
|
glfwSwapInterval(0);
|
||||||
Input::SetWindow(this);
|
Input::SetWindow(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
src/main.cpp
10
src/main.cpp
|
@ -1,10 +1,16 @@
|
||||||
#include "Application.hpp"
|
#include "Application.hpp"
|
||||||
#include "Log.hpp"
|
#include "Log.hpp"
|
||||||
|
|
||||||
int main()
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
Log::Init();
|
Log::Init();
|
||||||
Application::Launch();
|
|
||||||
|
if (argc != 2) {
|
||||||
|
LOG_CORE_FATAL("Usage: {0} <rom>", argv[0]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Application::Launch(argv[1]);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ Byte Mapper001::ReadCPU(Word addr)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PRG_ROM[addr & (0x4000 * selectedBank - 1)];
|
return PRG_ROM[(addr & (0x4000 * selectedBank - 1)) & 0x7FFF];
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0x00;
|
return 0x00;
|
||||||
|
|
2
vendor/imgui
vendored
2
vendor/imgui
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 8639a2f9f8d6d53f4c7a221579de5871051153d9
|
Subproject commit 88dfd85e9296fc03ac858f66cc6507ba443294ef
|
Loading…
Reference in a new issue