nes/src/Ppu.h

161 lines
4.1 KiB
C++

//
// Created by selim on 8/22/23.
//
#ifndef NES_PPU_H
#define NES_PPU_H
#include "Cartridge.h"
#include "Shifter.h"
#include "Oam.h"
#include <cstdint>
#include <memory>
#include <functional>
#include <vector>
namespace nes {
struct Pixel {
uint8_t R = 0;
uint8_t G = 0;
uint8_t B = 0;
uint8_t A = 0xFF;
Pixel(): R{}, G{}, B{}, A{0xFF} {}
Pixel(uint8_t R, uint8_t G, uint8_t B): R{R}, G{G}, B{B}, A{0xFF} {}
};
class Ppu {
public:
static constexpr uint16_t SCREEN_WIDTH = 256;
static constexpr uint16_t SCREEN_HEIGHT = 240;
enum ControlAddress: uint16_t {
Control = 0x0000,
Mask = 0x0001,
Status = 0x0002,
OamAddress = 0x0003,
OamData = 0x0004,
Scroll = 0x0005,
PpuAddress = 0x0006,
PpuData = 0x0007
};
union StatusRegister {
struct {
uint8_t unused: 5;
uint8_t spriteOverflow: 1;
uint8_t spriteZeroHit: 1;
uint8_t verticalBlank: 1;
};
uint8_t value;
};
union ControlRegister {
struct {
uint8_t nameTableX: 1;
uint8_t nameTableY: 1;
uint8_t incrementMode: 1;
uint8_t patternSprite: 1;
uint8_t patternBackground: 1;
uint8_t spriteSize: 1;
uint8_t slaveMode: 1;
uint8_t enableNmi: 1;
};
uint8_t value;
};
union MaskRegister {
struct {
uint8_t grayscale: 1;
uint8_t renderBackgroundLeft: 1;
uint8_t renderSpritesLeft: 1;
uint8_t renderBackground: 1;
uint8_t renderSprites: 1;
uint8_t enhanceRed: 1;
uint8_t enhanceGreen: 1;
uint8_t enhanceBlue: 1;
};
uint8_t value;
};
union LoopyRegister {
struct {
uint16_t coarseX : 5;
uint16_t coarseY : 5;
uint16_t nameTableX : 1;
uint16_t nameTableY : 1;
uint16_t fineY : 3;
uint16_t unused : 1;
};
uint16_t value;
};
struct TileInfo {
uint8_t id;
uint8_t attribute;
uint8_t lsb;
uint8_t msb;
};
public:
Ppu();
bool tick();
void write(uint16_t address, uint8_t value);
uint8_t read(uint16_t address);
void setPixel(uint16_t row, uint16_t column, Pixel pixel);
void connect(Cartridge* cartridge);
void reset();
void writeOam(uint8_t address, uint8_t data);
uint8_t internalRead(uint16_t address);
void internalWrite(uint16_t address, uint8_t value);
#ifdef NES_LOGGING
[[nodiscard]] std::string state() const;
#endif
public:
std::function<void(const Pixel*)> onNewFrame;
private:
Pixel getColor(uint8_t palette, uint8_t pixel);
void prepareNextBgTile(uint16_t column);
void incrementScrollX();
void incrementScrollY();
void updateScrollX();
void updateScrollY();
private:
int16_t _column;
int16_t _scanline;
StatusRegister _status;
ControlRegister _control;
MaskRegister _mask;
LoopyRegister _vRamAddress;
LoopyRegister _tRamAddress;
bool _addressWriteInProgress;
uint8_t _dataTemp;
uint8_t _fineX;
TileInfo _nextBgTile;
Shifter _bgPatternShifter;
Shifter _bgAttribShifter;
bool _needEmitNmi;
private:
std::unique_ptr<uint8_t[]> _nameTable;
std::unique_ptr<uint8_t[]> _nameTable2;
std::vector<Pixel> _palette;
std::unique_ptr<uint8_t[]> _paletteTable;
std::unique_ptr<Oam> _oam;
private:
std::unique_ptr<Pixel[]> _frameBuffer;
Cartridge* _cartridge;
};
}
#endif //NES_PPU_H