//performance-focused, scanline-based, parallelized implementation of PPU //limitations: //* mid-scanline effects not support //* mid-frame OAM changes not supported struct PPU : Thread, PPUcounter { //as a scanline-based renderer, PPU::PPUcounter values are not cycle-accurate alwaysinline auto field() const -> bool { return cpu.field(); } alwaysinline auto vcounter() const -> uint16 { return cpu.vcounter(); } alwaysinline auto hcounter() const -> uint16 { return cpu.hcounter(); } alwaysinline auto hdot() const -> uint16 { return cpu.hdot(); } alwaysinline auto lineclocks() const -> uint16 { return cpu.lineclocks(); } alwaysinline auto interlace() const -> bool { return false; } alwaysinline auto overscan() const -> bool { return false; } alwaysinline auto vdisp() const -> uint { return !io.overscan ? 225 : 240; } //ppu.cpp PPU(); ~PPU(); static auto Enter() -> void; alwaysinline auto step(uint clocks) -> void; auto main() -> void; auto scanline() -> void; auto frame() -> void; auto refresh() -> void; auto load(Markup::Node) -> bool; auto power(bool reset) -> void; //serialization.cpp auto serialize(serializer&) -> void; public: uint32* output = nullptr; uint16 vram[32 * 1024]; uint16 cgram[256]; struct { uint4 version; uint8 mdr; } ppu1, ppu2; struct Latch { uint16 vram; uint8 oam; uint8 cgram; uint8 bgofsPPU1; uint8 bgofsPPU2; uint8 mode7; uint1 counters; uint1 hcounter; //hdot uint1 vcounter; uint10 oamAddress; uint8 cgramAddress; } latch; //io.cpp auto latchCounters() -> void; alwaysinline auto vramAddress() const -> uint15; alwaysinline auto readVRAM() -> uint16; alwaysinline auto writeVRAM(uint1 byte, uint8 data) -> void; alwaysinline auto readOAM(uint10 address) -> uint8; alwaysinline auto writeOAM(uint10 address, uint8 data) -> void; alwaysinline auto readCGRAM(uint1 byte, uint8 address) -> uint8; alwaysinline auto writeCGRAM(uint8 address, uint15 data) -> void; auto readIO(uint24 address, uint8 data) -> uint8; auto writeIO(uint24 address, uint8 data) -> void; auto updateVideoMode() -> void; struct TileMode { enum : uint { BPP2, BPP4, BPP8, Mode7, Inactive }; }; struct TileSize { enum : uint { Size8x8, Size16x16 }; }; struct ScreenMode { enum : uint { Above, Below }; }; struct ScreenSize { enum : uint { Size32x32, Size32x64, Size64x32, Size64x64 }; }; struct IO { uint1 displayDisable; uint4 displayBrightness; uint10 oamBaseAddress; uint10 oamAddress; uint1 oamPriority; uint1 bgPriority; uint3 bgMode; uint4 mosaicSize; uint1 vramIncrementMode; uint2 vramMapping; uint8 vramIncrementSize; uint16 vramAddress; uint8 cgramAddress; uint1 cgramAddressLatch; uint9 hcounter; //hdot uint9 vcounter; uint1 interlace; uint1 overscan; uint1 pseudoHires; uint1 extbg; struct WindowLayer { uint1 oneEnable; uint1 oneInvert; uint1 twoEnable; uint1 twoInvert; uint2 mask; uint1 aboveEnable; uint1 belowEnable; }; struct WindowColor { uint1 oneEnable; uint1 oneInvert; uint1 twoEnable; uint1 twoInvert; uint2 mask; uint2 aboveMask; uint2 belowMask; }; struct Window { uint8 oneLeft; uint8 oneRight; uint8 twoLeft; uint8 twoRight; } window; struct Mode7 { uint16 a; uint16 b; uint16 c; uint16 d; uint16 x; uint16 y; uint16 hoffset; uint16 voffset; } mode7; struct Background { WindowLayer window; uint1 aboveEnable; uint1 belowEnable; uint1 colorEnable; uint1 mosaicEnable; uint15 tiledataAddress; uint15 screenAddress; uint2 screenSize; uint1 tileSize; uint16 hoffset; uint16 voffset; uint3 tileMode; uint4 priority[2]; } bg1, bg2, bg3, bg4; struct Object { WindowLayer window; uint1 aboveEnable; uint1 belowEnable; uint1 colorEnable; uint1 interlace; uint3 baseSize; uint2 nameselect; uint15 tiledataAddress; uint7 firstObject; uint1 rangeOver; uint1 timeOver; uint4 priority[4]; } obj; struct Color { WindowColor window; uint1 colorEnable; uint1 directColor; uint1 blendMode; uint1 colorHalve; uint1 colorMode; uint5 colorRed; uint5 colorGreen; uint5 colorBlue; } col; } io; //object.cpp auto oamAddressReset() -> void; auto oamSetFirstObject() -> void; auto readObject(uint10 address) -> uint8; auto writeObject(uint10 address, uint8 data) -> void; struct Object { uint9 x; uint8 y; uint8 character; uint1 nameselect; uint1 vflip; uint1 hflip; uint2 priority; uint3 palette; uint1 size; } object[128]; //bitplane -> bitmap tile caches uint8 vram2bpp[4096 * 8 * 8]; uint8 vram4bpp[2048 * 8 * 8]; uint8 vram8bpp[1024 * 8 * 8]; struct Line { //line.cpp auto render() -> void; //background.cpp auto renderBackground(PPU::IO::Background&) -> void; //object.cpp auto renderObject(PPU::IO::Object&) -> void; //window.cpp auto renderWindow(PPU::IO::WindowLayer&) -> void; auto renderWindow(PPU::IO::WindowColor&) -> void; uint9 y; uint32* outputLo = nullptr; uint32* outputHi = nullptr; uint15 cgram[256]; IO io; } lines[240]; }; extern PPU ppu;