From d07f61b0815f272cfa9fb4ee3760c48122d36e72 Mon Sep 17 00:00:00 2001 From: Selim Mustafaev Date: Sat, 9 Sep 2023 18:35:33 +0300 Subject: [PATCH] More PPU BG drawing code --- CMakeLists.txt | 4 +- src/Ppu.cpp | 140 ++++++++++++++++++++++++++++++++++++++++++------ src/Ppu.h | 8 +++ src/Shifter.cpp | 28 ++++++++++ src/Shifter.h | 26 +++++++++ 5 files changed, 190 insertions(+), 16 deletions(-) create mode 100644 src/Shifter.cpp create mode 100644 src/Shifter.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c4e510f..e5053ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,9 @@ add_executable(nes src/Mapper/Mapper0.cpp src/Mapper/Mapper0.h src/Ppu.cpp src/Ppu.h src/Window.cpp - src/Window.h) + src/Window.h + src/Shifter.cpp + src/Shifter.h) find_package(SDL2 CONFIG REQUIRED) target_link_libraries(nes PRIVATE SDL2::SDL2) \ No newline at end of file diff --git a/src/Ppu.cpp b/src/Ppu.cpp index f501fca..db6ac79 100644 --- a/src/Ppu.cpp +++ b/src/Ppu.cpp @@ -88,22 +88,39 @@ namespace nes { // All visible scanlines if (_scanline >= -1 && _scanline < 240) { + + if(_scanline == 0 && _column == 0) { + _column = 1; + } + if (_scanline == -1 && _column == 1) { _status.verticalBlank = 0; } // Preloading some data ahead of time if ((_column >= 2 && _column < 258) || (_column >= 321 && _column < 338)) { - // Every 8 ticks extract info about next tile - // Spread the work across those 8 ticks - switch ((_column - 1) % 8) { - case 0: - _nextBgTile.id = internalRead(0x2000 + _vRamAddress.value & 0x0FFF); - break; - case 2: - //_nextBgTile.attribute - break; - } + _bgPatternShifter.shift(); + _bgAttribShifter.shift(); + _nextBgTile = prepareNextBgTile(_column); + } + + if(_column == 256) { + incrementScrollY(); + } + + if(_column == 257) { + _bgPatternShifter.load(_nextBgTile.lsb, _nextBgTile.msb); + _bgAttribShifter.load((_nextBgTile.attribute & 0b01) ? 0xFF : 0x00, + (_nextBgTile.attribute & 0b10) ? 0xFF : 0x00); + updateScrollX(); + } + + if(_column == 338 || _column == 340) { + _nextBgTile.id = internalRead(0x2000 | (_vRamAddress.value & 0x0FFF)); + } + + if(_scanline == -1 && _column >= 280 && _column < 305) { + updateScrollY(); } } @@ -117,6 +134,8 @@ namespace nes { // setPixel(_scanline, _column, pixel); if(_mask.renderBackground) { + colorIndex = _bgPatternShifter.getValue(_fineX); + palette = _bgAttribShifter.getValue(_fineX); Pixel pixel = getColor(palette, colorIndex); setPixel(_scanline, _column, pixel); } @@ -211,8 +230,8 @@ namespace nes { return _cartridge->readChr(address); } else if(address >= 0x2000 && address < 0x3F00) { - address &= 0x03FF; - return _nameTable[address]; + address &= 0x0FFF; + return _nameTable[address & 0x03FF]; } else if(address >= 0x3F00 && address < 0x4000) { address &= 0x1F; @@ -233,9 +252,8 @@ namespace nes { // Can't write to CHR ROM } else if(address >= 0x2000 && address < 0x3F00) { - address &= 0x03FF; - //std::cout << std::hex << "Writing nametable, address: " << (int)address << ", value: " << (int)value << std::endl; - _nameTable[address] = value; + address &= 0x0FFF; + _nameTable[address & 0x03FF] = value; } else if(address >= 0x3F00 && address < 0x4000) { address &= 0x1F; @@ -251,4 +269,96 @@ namespace nes { uint16_t address = 0x3F00 + palette*4 + pixel; return _palette[internalRead(address) & 0x3F]; } + + Ppu::TileInfo Ppu::prepareNextBgTile(uint16_t column) { + TileInfo nextBgTile{}; + + // Every 8 ticks extract info about next tile + // Spread the work across those 8 ticks + switch ((column - 1) % 8) { + case 0: + _bgPatternShifter.load(_nextBgTile.lsb, _nextBgTile.msb); + _bgAttribShifter.load((_nextBgTile.attribute & 0b01) ? 0xFF : 0x00, + (_nextBgTile.attribute & 0b10) ? 0xFF : 0x00); + nextBgTile.id = internalRead(0x2000 + _vRamAddress.value & 0x0FFF); + break; + case 2: { + uint16_t offset = (_vRamAddress.nameTableX << 10) + | (_vRamAddress.nameTableY << 11) + | (_vRamAddress.coarseX >> 2) + | ((_vRamAddress.coarseY >> 2) << 3); + nextBgTile.attribute = internalRead(0x23C0 + offset); + if (_vRamAddress.coarseY & 0x02) nextBgTile.attribute >>= 4; + if (_vRamAddress.coarseX & 0x02) nextBgTile.attribute >>= 2; + nextBgTile.attribute &= 0x03; + break; + } + case 4: { + uint16_t address = (_control.patternBackground << 12) + | ((uint16_t)nextBgTile.id << 4) + | _vRamAddress.fineY; + nextBgTile.lsb = internalRead(address); + break; + } + case 6: { + uint16_t address = (_control.patternBackground << 12) + | ((uint16_t)nextBgTile.id << 4) + | (_vRamAddress.fineY + 8); + nextBgTile.lsb = internalRead(address); + break; + } + case 7: + incrementScrollX(); + break; + } + + return nextBgTile; + } + + void Ppu::incrementScrollX() { + if(!_mask.renderBackground) + return; + + if(_vRamAddress.coarseX == 31) { + _vRamAddress.coarseX = 0; + _vRamAddress.nameTableX = ~_vRamAddress.nameTableX; + } else { + _vRamAddress.coarseX++; + } + } + + void Ppu::incrementScrollY() { + if(!_mask.renderBackground) + return; + + if(_vRamAddress.fineY < 7) { + _vRamAddress.fineY++; + } else { + _vRamAddress.fineY = 0; + + if(_vRamAddress.coarseY == 29) { + _vRamAddress.coarseY = 0; + _vRamAddress.nameTableY = ~_vRamAddress.nameTableY; + } else if(_vRamAddress.coarseY == 31) { + _vRamAddress.coarseY = 0; + } else { + _vRamAddress.coarseY++; + } + } + } + + void Ppu::updateScrollX() { + if(_mask.renderBackground) { + _vRamAddress.nameTableX = _tRamAddress.nameTableX; + _vRamAddress.coarseX = _tRamAddress.coarseX; + } + } + + void Ppu::updateScrollY() { + if(_mask.renderBackground) { + _vRamAddress.nameTableY =_tRamAddress.nameTableY; + _vRamAddress.coarseY = _tRamAddress.coarseY; + _vRamAddress.fineY = _tRamAddress.fineY; + } + } } \ No newline at end of file diff --git a/src/Ppu.h b/src/Ppu.h index df9ca4c..d294521 100644 --- a/src/Ppu.h +++ b/src/Ppu.h @@ -6,6 +6,7 @@ #define NES_PPU_H #include "Cartridge.h" +#include "Shifter.h" #include #include @@ -111,6 +112,11 @@ namespace nes { uint8_t internalRead(uint16_t address); void internalWrite(uint16_t address, uint8_t value); Pixel getColor(uint8_t palette, uint8_t pixel); + TileInfo prepareNextBgTile(uint16_t column); + void incrementScrollX(); + void incrementScrollY(); + void updateScrollX(); + void updateScrollY(); private: int16_t _column; @@ -125,6 +131,8 @@ namespace nes { uint8_t _dataTemp; uint8_t _fineX; TileInfo _nextBgTile; + Shifter _bgPatternShifter; + Shifter _bgAttribShifter; private: std::unique_ptr _nameTable; diff --git a/src/Shifter.cpp b/src/Shifter.cpp new file mode 100644 index 0000000..12acd0a --- /dev/null +++ b/src/Shifter.cpp @@ -0,0 +1,28 @@ +// +// Created by Selim Mustafaev on 09.09.2023. +// + +#include "Shifter.h" + +namespace nes { + + Shifter::Shifter(): _lo{}, _hi{} { + } + + void Shifter::load(uint8_t lo, uint8_t hi) { + _lo = (_lo & 0xFF00) | lo; + _hi = (_hi & 0xFF00) | hi; + } + + void Shifter::shift() { + _lo <<= 1; + _hi <<= 1; + } + + uint16_t Shifter::getValue(uint8_t offset) { + uint16_t mask = 0x8000 >> offset; + bool loBit = _lo & mask; + bool hiBit = _hi & mask; + return (hiBit << 1) | loBit; + } +} \ No newline at end of file diff --git a/src/Shifter.h b/src/Shifter.h new file mode 100644 index 0000000..1cbc23c --- /dev/null +++ b/src/Shifter.h @@ -0,0 +1,26 @@ +// +// Created by Selim Mustafaev on 09.09.2023. +// + +#ifndef NES_SHIFTER_H +#define NES_SHIFTER_H + +#include + +namespace nes { + + class Shifter { + public: + Shifter(); + void load(uint8_t lo, uint8_t hi); + void shift(); + uint16_t getValue(uint8_t offset); + + private: + uint16_t _lo; + uint16_t _hi; + }; + +} + +#endif //NES_SHIFTER_H