Browse Source

Emulator: Add state machine to PPU

master
Riyyi 2 years ago
parent
commit
869438e0e0
  1. 17
      src/emu.cpp
  2. 1
      src/emu.h
  3. 65
      src/ppu.cpp
  4. 13
      src/ppu.h

17
src/emu.cpp

@ -64,6 +64,16 @@ void Emu::removeMemorySpace(std::string_view name)
void Emu::writeMemory(uint32_t address, uint32_t value) void Emu::writeMemory(uint32_t address, uint32_t value)
{ {
// Bail if the CPU tries to write to a read-only address
switch (address) {
case 0xff44:
ruc::error("writing to read-only address: {:#06x}", address);
VERIFY_NOT_REACHED();
break;
default:
break;
}
for (auto& memory_space : m_memory_spaces) { for (auto& memory_space : m_memory_spaces) {
auto& memory = memory_space.second; auto& memory = memory_space.second;
if (address >= memory.start_address && address <= memory.end_address) { if (address >= memory.start_address && address <= memory.end_address) {
@ -96,6 +106,13 @@ void Emu::writeMemory(uint32_t address, uint32_t value)
uint32_t Emu::readMemory(uint32_t address) const uint32_t Emu::readMemory(uint32_t address) const
{ {
switch (address) {
case 0xff44:
return *m_processing_units.at("PPU")->sharedRegister("LY");
default:
break;
};
for (const auto& memory_space : m_memory_spaces) { for (const auto& memory_space : m_memory_spaces) {
const auto& memory = memory_space.second; const auto& memory = memory_space.second;
if (address >= memory.start_address && address <= memory.end_address) { if (address >= memory.start_address && address <= memory.end_address) {

1
src/emu.h

@ -9,7 +9,6 @@
#include <cstdint> // uint32_t #include <cstdint> // uint32_t
#include <memory> // std::shared_ptr #include <memory> // std::shared_ptr
#include <string>
#include <string_view> #include <string_view>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>

65
src/ppu.cpp

@ -18,10 +18,15 @@
#include "emu.h" #include "emu.h"
#include "ppu.h" #include "ppu.h"
#include "ruc/meta/assert.h"
PPU ::PPU(uint32_t frequency) PPU ::PPU(uint32_t frequency)
: ProcessingUnit(frequency) : ProcessingUnit(frequency)
{ {
m_shared_registers.emplace("LY", &m_lcd_y_coordinate);
// Setup screen
auto& scene = Inferno::Application::the().scene(); auto& scene = Inferno::Application::the().scene();
m_entity = scene.createEntity("Screen"); m_entity = scene.createEntity("Screen");
@ -46,12 +51,54 @@ void PPU::update()
// print("PPU update\n"); // print("PPU update\n");
// Increment LY (LCD Y Coordinate) m_clocks_into_frame++;
uint32_t ly_register = Emu::the().readMemory(0xff44) + 1;
if (ly_register >= 154) { switch (m_state) {
ly_register = 0; case State::OAMSearch:
// OAM logic goes here..
if (m_clocks_into_frame % 20 == 0) {
m_state = State::PixelTransfer;
}
break;
case State::PixelTransfer:
// PixelTransfer logic goes here..
m_lcd_x_coordinate++;
if (m_lcd_x_coordinate == 160) {
m_lcd_x_coordinate = 0;
m_state = State::HBlank;
}
break;
case State::HBlank:
// H-Blank logic goes here..
if (m_clocks_into_frame % (20 + 43 + 51) == 0) {
if (m_lcd_y_coordinate == 144) {
m_state = State::VBlank;
}
else {
m_state = State::OAMSearch;
}
m_lcd_y_coordinate++;
}
break;
case State::VBlank:
// V-Blank logic goes here..
if (m_clocks_into_frame % (20 + 43 + 51) == 0) {
if (m_lcd_y_coordinate == 154) {
m_lcd_y_coordinate = 0;
m_clocks_into_frame = 0;
m_state = State::OAMSearch;
}
m_lcd_y_coordinate++;
} }
Emu::the().writeMemory(0xff44, ly_register);
break;
default:
VERIFY_NOT_REACHED();
};
} }
void PPU::render() void PPU::render()
@ -141,3 +188,11 @@ void PPU::drawTile(uint32_t x, uint32_t y, uint32_t tile_address, uint8_t bg_pal
screen_index += SCREEN_WIDTH * FORMAT_SIZE; screen_index += SCREEN_WIDTH * FORMAT_SIZE;
} }
} }
void PPU::resetFrame()
{
m_state = State::OAMSearch;
m_clocks_into_frame = 0;
m_lcd_x_coordinate = 0;
m_lcd_y_coordinate = 0;
}

13
src/ppu.h

@ -38,12 +38,25 @@ public:
PPU(uint32_t frequency); PPU(uint32_t frequency);
~PPU(); ~PPU();
enum State : uint8_t {
OAMSearch,
PixelTransfer,
HBlank,
VBlank,
};
void update() override; void update() override;
void render(); void render();
void drawTile(uint32_t screen_x, uint32_t screen_y, uint32_t tile_address, uint8_t bg_palette); void drawTile(uint32_t screen_x, uint32_t screen_y, uint32_t tile_address, uint8_t bg_palette);
void resetFrame();
private: private:
State m_state { State::OAMSearch };
uint32_t m_clocks_into_frame { 0 };
uint32_t m_lcd_x_coordinate { 0 };
uint32_t m_lcd_y_coordinate { 0 }; // Note: includes V-Blank
uint32_t m_entity; uint32_t m_entity;
std::array<uint8_t, SCREEN_WIDTH * FORMAT_SIZE * SCREEN_HEIGHT> m_screen; std::array<uint8_t, SCREEN_WIDTH * FORMAT_SIZE * SCREEN_HEIGHT> m_screen;
}; };

Loading…
Cancel
Save