Emulator: Add initial pixel FIFO to PPU
This commit is contained in:
+110
@@ -49,6 +49,10 @@ void PPU::update()
|
||||
// OAM logic goes here..
|
||||
|
||||
if (m_clocks_into_frame % 80 == 0) {
|
||||
// Clear FIFOs
|
||||
m_pixel_fifo.background = {};
|
||||
m_pixel_fifo.oam = {};
|
||||
|
||||
m_state = State::PixelTransfer;
|
||||
}
|
||||
break;
|
||||
@@ -128,6 +132,108 @@ void PPU::render()
|
||||
scene.addComponent<Inferno::SpriteComponent>(m_entity, glm::vec4 { 1.0f }, texture);
|
||||
}
|
||||
|
||||
void PPU::pixelFifo()
|
||||
{
|
||||
LCDC lcd_control = static_cast<LCDC>(Emu::the().readMemory(0xff40));
|
||||
|
||||
// Tile map
|
||||
uint32_t tile_map_size = 32 * 32; // 1 KiB
|
||||
uint32_t bg_tile_map_address = (lcd_control & LCDC::BGTileMapArea) ? 0x9c00 : 0x9800;
|
||||
// uint32_t window_tile_map_address = (lcd_control & LCDC::WindowTileMapArea) ? 0x9c00 : 0x9800;
|
||||
|
||||
// Tile data
|
||||
// uint32_t tile_data_size = 4096; // 4KiB / 16B = 256 tiles
|
||||
uint32_t tile_data_address = (lcd_control & LCDC::BGandWindowTileDataArea) ? 0x8000 : 0x8800;
|
||||
|
||||
// -------------------------------------
|
||||
// FIFO Pixel Fetcher
|
||||
|
||||
switch (m_pixel_fifo.state) {
|
||||
case PixelFifo::State::TileIndex:
|
||||
if (m_pixel_fifo.step == true) {
|
||||
m_pixel_fifo.step = false;
|
||||
m_pixel_fifo.state = PixelFifo::State::TileDataLow;
|
||||
|
||||
// Viewport
|
||||
// https://gbdev.io/pandocs/Scrolling.html#mid-frame-behavior
|
||||
m_viewport_x = Emu::the().readMemory(0xff43); // TODO: only read lower 3-bits at beginning of scanline
|
||||
m_viewport_y = Emu::the().readMemory(0xff42);
|
||||
|
||||
// Read the tile map index
|
||||
uint16_t offset = (((m_viewport_y + m_lcd_y_coordinate) / TILE_HEIGHT) * 32)
|
||||
+ ((m_viewport_x + m_lcd_x_coordinate) / TILE_WIDTH);
|
||||
m_pixel_fifo.tile_index = Emu::the().readMemory(bg_tile_map_address + offset) & 0xff;
|
||||
|
||||
// Set the tile line we're currently on
|
||||
m_pixel_fifo.tile_line = (m_viewport_y + m_lcd_y_coordinate) % TILE_HEIGHT;
|
||||
}
|
||||
|
||||
m_pixel_fifo.step = true;
|
||||
break;
|
||||
case PixelFifo::State::TileDataLow:
|
||||
if (m_pixel_fifo.step == true) {
|
||||
m_pixel_fifo.step = false;
|
||||
m_pixel_fifo.state = PixelFifo::State::TileDataHigh;
|
||||
|
||||
// Read tile data
|
||||
m_pixel_fifo.pixels_lsb = Emu::the().readMemory(tile_data_address
|
||||
+ (m_pixel_fifo.tile_index * TILE_SIZE) // Each tile is 16 bytes
|
||||
+ m_pixel_fifo.tile_line * 2); // Each tile line is 2 bytes
|
||||
}
|
||||
|
||||
m_pixel_fifo.step = true;
|
||||
break;
|
||||
case PixelFifo::State::TileDataHigh:
|
||||
if (m_pixel_fifo.step == true) {
|
||||
m_pixel_fifo.step = false;
|
||||
m_pixel_fifo.state = PixelFifo::State::Sleep;
|
||||
|
||||
// Read tile data
|
||||
m_pixel_fifo.pixels_msb = Emu::the().readMemory(tile_data_address
|
||||
+ (m_pixel_fifo.tile_index * TILE_SIZE)
|
||||
+ m_pixel_fifo.tile_line * 2
|
||||
+ 1);
|
||||
}
|
||||
|
||||
m_pixel_fifo.step = true;
|
||||
break;
|
||||
case PixelFifo::State::Sleep:
|
||||
if (m_pixel_fifo.background.size() <= 9) {
|
||||
m_pixel_fifo.state = PixelFifo::State::Push;
|
||||
}
|
||||
break;
|
||||
case PixelFifo::State::Push: {
|
||||
m_pixel_fifo.state = PixelFifo::State::TileIndex;
|
||||
|
||||
for (uint8_t i = 0; i < 8; ++i) {
|
||||
uint8_t color_index = (m_pixel_fifo.pixels_lsb >> (7 - i)
|
||||
| ((m_pixel_fifo.pixels_msb >> (7 - i)) << 1))
|
||||
& 0x3;
|
||||
m_pixel_fifo.background.push({ color_index, Palette::BGP });
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
};
|
||||
|
||||
// -------------------------------------
|
||||
// Mode 3 Operation
|
||||
|
||||
// The pixel FIFO needs to contain more than 8 pixels to shift one out
|
||||
if (m_pixel_fifo.background.size() > 8) {
|
||||
auto pixel = m_pixel_fifo.background.front();
|
||||
m_pixel_fifo.background.pop();
|
||||
|
||||
uint32_t index = (m_lcd_y_coordinate * SCREEN_WIDTH + m_lcd_x_coordinate) * FORMAT_SIZE;
|
||||
auto color = getPixelColor(pixel.first, pixel.second);
|
||||
m_screen[index + 0] = color[0];
|
||||
m_screen[index + 1] = color[1];
|
||||
m_screen[index + 2] = color[2];
|
||||
m_lcd_x_coordinate++;
|
||||
}
|
||||
}
|
||||
|
||||
void PPU::drawTile(uint32_t x, uint32_t y, uint32_t tile_address)
|
||||
{
|
||||
uint32_t viewport_x = Emu::the().readMemory(0xff43);
|
||||
@@ -200,4 +306,8 @@ void PPU::resetFrame()
|
||||
m_clocks_into_frame = 0;
|
||||
m_lcd_x_coordinate = 0;
|
||||
m_lcd_y_coordinate = 0;
|
||||
|
||||
// Clear FIFOs
|
||||
m_pixel_fifo.background = {};
|
||||
m_pixel_fifo.oam = {};
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
#include <array>
|
||||
#include <cstdint> // uint8_t, uint16_t, uint32_t
|
||||
#include <queue>
|
||||
#include <utility> // std::pair
|
||||
|
||||
#include "ruc/meta/core.h"
|
||||
|
||||
@@ -51,9 +53,32 @@ public:
|
||||
OBP1 = 0xff49, // OBJ palette 1
|
||||
};
|
||||
|
||||
struct PixelFifo {
|
||||
enum State : uint8_t {
|
||||
TileIndex,
|
||||
TileDataLow,
|
||||
TileDataHigh,
|
||||
Sleep,
|
||||
Push,
|
||||
};
|
||||
|
||||
State state = State::TileIndex;
|
||||
bool step = false;
|
||||
uint8_t tile_index = 0;
|
||||
uint8_t tile_line = 0;
|
||||
uint8_t pixels_lsb = 0;
|
||||
uint8_t pixels_msb = 0;
|
||||
|
||||
using Fifo = std::queue<std::pair<uint8_t, Palette>>; // colorID, source
|
||||
|
||||
Fifo background;
|
||||
Fifo oam;
|
||||
};
|
||||
|
||||
void update() override;
|
||||
void render();
|
||||
|
||||
void pixelFifo();
|
||||
void drawTile(uint32_t screen_x, uint32_t screen_y, uint32_t tile_address);
|
||||
std::array<uint8_t, 3> getPixelColor(uint8_t color_index, Palette palette);
|
||||
|
||||
@@ -65,6 +90,10 @@ private:
|
||||
uint32_t m_lcd_x_coordinate { 0 };
|
||||
uint32_t m_lcd_y_coordinate { 0 }; // Note: includes V-Blank
|
||||
|
||||
uint8_t m_viewport_x { 0 };
|
||||
uint8_t m_viewport_y { 0 };
|
||||
PixelFifo m_pixel_fifo;
|
||||
|
||||
uint32_t m_entity;
|
||||
std::array<uint8_t, SCREEN_WIDTH * FORMAT_SIZE * SCREEN_HEIGHT> m_screen;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user