More PPU BG drawing code

This commit is contained in:
Selim Mustafaev 2023-09-09 18:35:33 +03:00
parent c6f65e5ad2
commit d07f61b081
5 changed files with 190 additions and 16 deletions

View File

@ -20,7 +20,9 @@ add_executable(nes
src/Mapper/Mapper0.cpp src/Mapper/Mapper0.cpp
src/Mapper/Mapper0.h src/Ppu.cpp src/Ppu.h src/Mapper/Mapper0.h src/Ppu.cpp src/Ppu.h
src/Window.cpp src/Window.cpp
src/Window.h) src/Window.h
src/Shifter.cpp
src/Shifter.h)
find_package(SDL2 CONFIG REQUIRED) find_package(SDL2 CONFIG REQUIRED)
target_link_libraries(nes PRIVATE SDL2::SDL2) target_link_libraries(nes PRIVATE SDL2::SDL2)

View File

@ -88,22 +88,39 @@ namespace nes {
// All visible scanlines // All visible scanlines
if (_scanline >= -1 && _scanline < 240) { if (_scanline >= -1 && _scanline < 240) {
if(_scanline == 0 && _column == 0) {
_column = 1;
}
if (_scanline == -1 && _column == 1) { if (_scanline == -1 && _column == 1) {
_status.verticalBlank = 0; _status.verticalBlank = 0;
} }
// Preloading some data ahead of time // Preloading some data ahead of time
if ((_column >= 2 && _column < 258) || (_column >= 321 && _column < 338)) { if ((_column >= 2 && _column < 258) || (_column >= 321 && _column < 338)) {
// Every 8 ticks extract info about next tile _bgPatternShifter.shift();
// Spread the work across those 8 ticks _bgAttribShifter.shift();
switch ((_column - 1) % 8) { _nextBgTile = prepareNextBgTile(_column);
case 0:
_nextBgTile.id = internalRead(0x2000 + _vRamAddress.value & 0x0FFF);
break;
case 2:
//_nextBgTile.attribute
break;
} }
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); // setPixel(_scanline, _column, pixel);
if(_mask.renderBackground) { if(_mask.renderBackground) {
colorIndex = _bgPatternShifter.getValue(_fineX);
palette = _bgAttribShifter.getValue(_fineX);
Pixel pixel = getColor(palette, colorIndex); Pixel pixel = getColor(palette, colorIndex);
setPixel(_scanline, _column, pixel); setPixel(_scanline, _column, pixel);
} }
@ -211,8 +230,8 @@ namespace nes {
return _cartridge->readChr(address); return _cartridge->readChr(address);
} }
else if(address >= 0x2000 && address < 0x3F00) { else if(address >= 0x2000 && address < 0x3F00) {
address &= 0x03FF; address &= 0x0FFF;
return _nameTable[address]; return _nameTable[address & 0x03FF];
} }
else if(address >= 0x3F00 && address < 0x4000) { else if(address >= 0x3F00 && address < 0x4000) {
address &= 0x1F; address &= 0x1F;
@ -233,9 +252,8 @@ namespace nes {
// Can't write to CHR ROM // Can't write to CHR ROM
} }
else if(address >= 0x2000 && address < 0x3F00) { else if(address >= 0x2000 && address < 0x3F00) {
address &= 0x03FF; address &= 0x0FFF;
//std::cout << std::hex << "Writing nametable, address: " << (int)address << ", value: " << (int)value << std::endl; _nameTable[address & 0x03FF] = value;
_nameTable[address] = value;
} }
else if(address >= 0x3F00 && address < 0x4000) { else if(address >= 0x3F00 && address < 0x4000) {
address &= 0x1F; address &= 0x1F;
@ -251,4 +269,96 @@ namespace nes {
uint16_t address = 0x3F00 + palette*4 + pixel; uint16_t address = 0x3F00 + palette*4 + pixel;
return _palette[internalRead(address) & 0x3F]; 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;
}
}
} }

View File

@ -6,6 +6,7 @@
#define NES_PPU_H #define NES_PPU_H
#include "Cartridge.h" #include "Cartridge.h"
#include "Shifter.h"
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
@ -111,6 +112,11 @@ namespace nes {
uint8_t internalRead(uint16_t address); uint8_t internalRead(uint16_t address);
void internalWrite(uint16_t address, uint8_t value); void internalWrite(uint16_t address, uint8_t value);
Pixel getColor(uint8_t palette, uint8_t pixel); Pixel getColor(uint8_t palette, uint8_t pixel);
TileInfo prepareNextBgTile(uint16_t column);
void incrementScrollX();
void incrementScrollY();
void updateScrollX();
void updateScrollY();
private: private:
int16_t _column; int16_t _column;
@ -125,6 +131,8 @@ namespace nes {
uint8_t _dataTemp; uint8_t _dataTemp;
uint8_t _fineX; uint8_t _fineX;
TileInfo _nextBgTile; TileInfo _nextBgTile;
Shifter _bgPatternShifter;
Shifter _bgAttribShifter;
private: private:
std::unique_ptr<uint8_t[]> _nameTable; std::unique_ptr<uint8_t[]> _nameTable;

28
src/Shifter.cpp Normal file
View File

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

26
src/Shifter.h Normal file
View File

@ -0,0 +1,26 @@
//
// Created by Selim Mustafaev on 09.09.2023.
//
#ifndef NES_SHIFTER_H
#define NES_SHIFTER_H
#include <cstdint>
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