Browse Source

Emulator: Add initial pixel FIFO to PPU

master
Riyyi 2 years ago
parent
commit
f2a68e6294
  1. 110
      src/ppu.cpp
  2. 29
      src/ppu.h

110
src/ppu.cpp

@ -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 = {};
}

29
src/ppu.h

@ -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;
};

Loading…
Cancel
Save