161 lines
4.1 KiB
C++
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
|