started ppu

This commit is contained in:
Lauchmelder 2022-03-03 02:33:08 +01:00
parent e7b78f281f
commit f9f401c6c0
No known key found for this signature in database
GPG key ID: C2403C69D78F011D
15 changed files with 313 additions and 54 deletions

BIN
roms/donkeykong.nes Normal file

Binary file not shown.

View file

@ -10,6 +10,7 @@
#include "Log.hpp"
#include "Bus.hpp"
#include "Screen.hpp"
#include "Debugger.hpp"
#include "gfx/Window.hpp"
@ -48,7 +49,7 @@ Application::Application() :
LOG_CORE_INFO("Creating window");
try
{
window = new Window(1280, 720, "NES Emulator");
window = new Window(256, 240, "NES Emulator");
}
catch (const std::runtime_error& err)
{
@ -60,6 +61,8 @@ Application::Application() :
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
throw std::runtime_error("Failed to set up OpenGL loader");
window->SetScale(scale);
LOG_CORE_INFO("Setting up ImGui");
IMGUI_CHECKVERSION();
ImGui::CreateContext();
@ -80,7 +83,17 @@ Application::Application() :
style.Colors[ImGuiCol_WindowBg].w = 1.0f;
}
bus = new Bus;
try
{
screen = new Screen;
}
catch(const std::runtime_error& err)
{
LOG_CORE_ERROR("Screen creation failed");
throw err;
}
bus = new Bus(screen);
debugger = new Debugger(bus);
}
@ -96,12 +109,35 @@ Application::~Application()
bool Application::Update()
{
if (window->GetScale() != scale)
window->SetScale(scale);
glfwPollEvents();
if (!debugger->Update())
return false;
window->Begin();
screen->Render();
if (ImGui::BeginMainMenuBar())
{
if (ImGui::BeginMenu("Screen"))
{
if (ImGui::BeginMenu("Scale"))
{
ImGui::RadioButton("x1", &scale, 1);
ImGui::RadioButton("x2", &scale, 2);
ImGui::RadioButton("x3", &scale, 3);
ImGui::RadioButton("x4", &scale, 4);
ImGui::EndMenu();
}
ImGui::EndMenu();
}
ImGui::EndMainMenuBar();
}
debugger->Render();
window->End();

View file

@ -3,6 +3,7 @@
class Bus;
class Window;
class Debugger;
class Screen;
/**
* @brief Contains the program loop and invokes other objects update functions.
@ -26,7 +27,10 @@ private:
bool Update();
private:
int scale = 3;
Window* window;
Bus* bus;
Screen* screen;
Debugger* debugger;
};

View file

@ -5,8 +5,8 @@
#include "controllers/StandardController.hpp"
Bus::Bus() :
cpu(this), ppu(this), cartridge(this)
Bus::Bus(Screen* screen) :
cpu(this), ppu(this, screen), cartridge(this)
{
LOG_CORE_INFO("Allocating RAM");
RAM = std::vector<Byte>(0x800);
@ -15,7 +15,7 @@ Bus::Bus() :
VRAM = std::vector<Byte>(0x800);
LOG_CORE_INFO("Inserting cartridge");
cartridge.Load("roms/nestest.nes");
cartridge.Load("roms/donkeykong.nes");
LOG_CORE_INFO("Powering up CPU");
cpu.Powerup();

View file

@ -23,7 +23,7 @@ class Bus
friend class NametableViewer;
public:
Bus();
Bus(Screen* screen);
/**
* @brief Reboot the NES.

View file

@ -13,7 +13,7 @@ add_executable(nesemu
"debugger/PPUWatcher.cpp"
"debugger/Disassembler.cpp"
"debugger/MemoryViewer.cpp"
"debugger/NametableViewer.cpp" "ControllerPort.cpp" "controllers/StandardController.cpp" "gfx/Input.cpp" "debugger/ControllerPortViewer.cpp")
"debugger/NametableViewer.cpp" "ControllerPort.cpp" "controllers/StandardController.cpp" "gfx/Input.cpp" "debugger/ControllerPortViewer.cpp" "gfx/Screen.cpp")
target_include_directories(nesemu PRIVATE
mappers

View file

@ -28,6 +28,9 @@ Byte ControllerPort::Write(Word addr, Byte val)
Byte ControllerPort::Read(Word addr)
{
if (connectedDevices[addr & 1] == nullptr)
return 0xFF;
return connectedDevices[addr & 1]->CLK();
}

View file

@ -2,9 +2,12 @@
#include "Log.hpp"
#include "Bus.hpp"
PPU::PPU(Bus* bus) :
bus(bus), ppuctrl{0}, ppustatus{0}
#include "gfx/Screen.hpp"
PPU::PPU(Bus* bus, Screen* screen) :
bus(bus), screen(screen), ppuctrl{ 0 }, ppustatus{ 0 }
{
LOG_CORE_INFO("{0}", sizeof(VRAMAddress));
}
void PPU::Powerup()
@ -57,38 +60,6 @@ void PPU::Tick()
y = 0;
}
current.CoarseX++;
if (current.CoarseX > 31)
{
current.CoarseX = 0;
current.NametableSel ^= 0x1;
}
if (x == 256)
{
if (current.FineY < 7)
{
current.FineY++;
}
else
{
current.FineY = 0;
if (current.CoarseY == 29)
{
current.CoarseY = 0;
current.NametableSel ^= 0x2;
}
else if (current.CoarseY == 31)
{
current.CoarseY = 0;
}
else
{
current.CoarseY++;
}
}
}
UpdateState();
// On this cycle the VBlankStarted bit is set in the ppustatus
@ -113,6 +84,14 @@ void PPU::Tick()
PerformRenderAction();
}
if (x < 256 && y < 240)
{
uint8_t xOffset = 7 - fineX;
uint8_t color = (((patternTableHi >> xOffset) & 0x1) << 1) | ((patternTableLo >> xOffset) & 0x1);
color *= 80;
screen->SetPixel(x, y, { color, color, color});
}
}
Byte PPU::ReadRegister(Byte id)
@ -283,6 +262,12 @@ void PPU::UpdateState()
void PPU::PerformRenderAction()
{
if (cycleType == CycleType::Idle)
{
fineX = 0;
return;
}
if (cycleType == CycleType::SpriteFetching)
return;
if (memoryAccessLatch == 1)
@ -302,16 +287,49 @@ void PPU::PerformRenderAction()
break;
case FetchingPhase::PatternTableLo:
patternTableLo = Read(ppuctrl.Flag.BackgrPatternTableAddr | nametableByte);
patternTableLo = Read(((Word)ppuctrl.Flag.BackgrPatternTableAddr << 12) | ((Word)nametableByte << 4) + current.FineY);
fetchPhase = FetchingPhase::PatternTableHi;
break;
case FetchingPhase::PatternTableHi:
patternTableLo = Read((ppuctrl.Flag.BackgrPatternTableAddr | nametableByte) + 8);
patternTableLo = Read((((Word)ppuctrl.Flag.BackgrPatternTableAddr << 12) | ((Word)nametableByte << 4)) + 8 + current.FineY);
current.CoarseX++;
if (x == 256)
{
current.CoarseX = temporary.CoarseX;
current.NametableSel ^= 0x1;
if (current.FineY < 7)
{
current.FineY++;
}
else
{
current.FineY = temporary.FineY;
if (current.CoarseY == 29)
{
current.CoarseY = temporary.CoarseY;
current.NametableSel ^= 0x2;
}
else if (current.CoarseY == 31)
{
current.CoarseY = temporary.CoarseY;
}
else
{
current.CoarseY++;
}
}
}
fetchPhase = FetchingPhase::NametableByte;
break;
}
}
fineX++;
if (fineX >= 8)
fineX = 0;
memoryAccessLatch = 1 - memoryAccessLatch;
}

View file

@ -3,6 +3,7 @@
#include "Types.hpp"
class Bus;
class Screen;
enum class ScanlineType
{
@ -33,10 +34,10 @@ union VRAMAddress
{
struct
{
Byte CoarseX : 5;
Byte CoarseY : 5;
Byte NametableSel : 2;
Byte FineY : 3;
Word CoarseX : 5;
Word CoarseY : 5;
Word NametableSel : 2;
Word FineY : 3;
};
Word Raw;
@ -50,7 +51,7 @@ class PPU
friend class PPUWatcher;
public:
PPU(Bus* bus);
PPU(Bus* bus, Screen* screen);
/**
* @brief Powerup PPU.
@ -177,4 +178,5 @@ private:
uint8_t memoryAccessLatch = 0;
bool isFrameDone = false;
Bus* bus;
Screen* screen;
};

View file

@ -22,9 +22,4 @@ void StandardController::OUT(PortLatch latch)
pressed.Buttons.Down = Input::IsKeyDown(GLFW_KEY_DOWN);
pressed.Buttons.Left = Input::IsKeyDown(GLFW_KEY_LEFT);
pressed.Buttons.Right = Input::IsKeyDown(GLFW_KEY_RIGHT);
if (pressed.Raw != 0)
volatile int jdfi = 3;
outRegister = pressed.Raw;
}

View file

@ -34,7 +34,7 @@ void ControllerPortViewer::OnRender()
ImGui::Separator();
ImGui::InputScalar("Shift Register", ImGuiDataType_U8, &controller->outRegister, (const void*)0, (const void*)0, "%02X", ImGuiInputTextFlags_CharsHexadecimal);
if (ImGui::BeginTable(controllerName.c_str(), 8))
{
for(int i = 0; i < 8; i++)
@ -52,6 +52,8 @@ void ControllerPortViewer::OnRender()
ImGui::EndTable();
}
}
counter++;
}
ImGui::End();

154
src/gfx/Screen.cpp Normal file
View file

@ -0,0 +1,154 @@
#include "Screen.hpp"
#include <glad/glad.h>
#include <string>
#include <stdexcept>
#include "../Log.hpp"
Screen::Screen()
{
pixels.resize(256 * 240);
LOG_CORE_INFO("Creating vertex arrays");
CreateVertexArray();
LOG_CORE_INFO("Creating screen texture");
CreateTexture();
LOG_CORE_INFO("Creating screen shader");
CreateShader();
}
Screen::~Screen()
{
glDeleteTextures(1, &texture);
glDeleteProgram(shader);
glDeleteBuffers(1, &vbo);
glDeleteVertexArrays(1, &vao);
}
void Screen::SetPixel(uint16_t x, uint16_t y, Color color)
{
pixels[y * 256 + x] = color;
}
void Screen::Render()
{
glTextureSubImage2D(texture, 0, 0, 0, 256, 240, GL_RGB, GL_UNSIGNED_BYTE, (const void*)pixels.data());
glBindTexture(GL_TEXTURE_2D, texture);
glUseProgram(shader);
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
void Screen::CreateVertexArray()
{
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
float vertices[4 * (3 + 2)] = {
-1.0f, -1.0f, 0.0f, 0.0f, 1.0f,
-1.0f, 1.0f, 0.0f, 0.0f, 0.0f,
1.0f, 1.0f, 0.0f, 1.0f, 0.0f,
1.0f, -1.0f, 0.0f, 1.0f, 1.0f
};
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (const void*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (const void*)(3 * sizeof(float)));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
}
void Screen::CreateTexture()
{
glCreateTextures(GL_TEXTURE_2D, 1, &texture);
glTextureStorage2D(texture, 1, GL_RGB8, 256, 240);
glTextureParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
void Screen::CreateShader()
{
GLint status;
shader = glCreateProgram();
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
const char* vertexShaderSource = R"(
#version 460 core
layout (location = 0) in vec3 a_Pos;
layout (location = 1) in vec2 a_UV;
out vec2 uvCoords;
void main()
{
uvCoords = a_UV;
gl_Position = vec4(a_Pos, 1.0f);
}
)";
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
{
char errorBuf[512];
glGetShaderInfoLog(vertexShader, 512, NULL, errorBuf);
glDeleteShader(vertexShader);
throw std::runtime_error("Vertex shader compilation error: " + std::string(errorBuf));
}
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
const char* fragmentShaderSource = R"(
#version 460 core
out vec4 FragColor;
in vec2 uvCoords;
uniform sampler2D screen;
void main()
{
FragColor = texture(screen, uvCoords);
}
)";
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
{
char errorBuf[512];
glGetShaderInfoLog(fragmentShader, 512, NULL, errorBuf);
glDeleteShader(fragmentShader);
glDeleteShader(vertexShader);
throw std::runtime_error("Fragment shader compilation error: " + std::string(errorBuf));
}
glAttachShader(shader, vertexShader);
glAttachShader(shader, fragmentShader);
glLinkProgram(shader);
glGetProgramiv(shader, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
{
char errorBuf[512];
glGetProgramInfoLog(shader, 512, NULL, errorBuf);
glDeleteShader(fragmentShader);
glDeleteShader(vertexShader);
throw std::runtime_error("Shader link error: " + std::string(errorBuf));
}
glDeleteShader(fragmentShader);
glDeleteShader(vertexShader);
}

34
src/gfx/Screen.hpp Normal file
View file

@ -0,0 +1,34 @@
#pragma once
#include <cstdint>
#include <vector>
struct Color
{
uint8_t r;
uint8_t g;
uint8_t b;
};
class Screen
{
public:
Screen();
~Screen();
void SetPixel(uint16_t x, uint16_t y, Color color);
void Render();
private:
void CreateVertexArray();
void CreateTexture();
void CreateShader();
private:
uint32_t texture = 0;
uint32_t shader = 0;
uint32_t vao = 0;
uint32_t vbo = 0;
std::vector<Color> pixels;
};

View file

@ -10,6 +10,7 @@
Window::Window(uint16_t width, uint16_t height, const std::string& title) :
handle(nullptr)
{
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
handle = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr);
if (handle == nullptr)
{
@ -28,6 +29,12 @@ Window::~Window()
glfwDestroyWindow(handle);
}
void Window::SetScale(int scale)
{
glfwSetWindowSize(handle, 256 * scale, 240 * scale + 20);
glViewport(0, 0, 256 * scale, 240 * scale);
}
void Window::Begin()
{
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

View file

@ -12,12 +12,16 @@ public:
Window(uint16_t width, uint16_t height, const std::string& title);
~Window();
inline int GetScale() { return scale; }
inline bool ShouldClose() { return glfwWindowShouldClose(handle); }
inline GLFWwindow* GetNativeWindow() { return handle; }
void SetScale(int scale);
void Begin();
void End();
private:
int scale = -1;
GLFWwindow* handle;
};