Adding background shifters

This commit is contained in:
Selim Mustafaev 2023-09-10 22:17:15 +03:00
parent d07f61b081
commit 40c7ff5882
8 changed files with 73 additions and 36 deletions

View File

@ -25,4 +25,5 @@ add_executable(nes
src/Shifter.h) src/Shifter.h)
find_package(SDL2 CONFIG REQUIRED) find_package(SDL2 CONFIG REQUIRED)
target_link_libraries(nes PRIVATE SDL2::SDL2) find_package(fmt REQUIRED)
target_link_libraries(nes PRIVATE SDL2::SDL2 fmt::fmt)

View File

@ -11,10 +11,12 @@ int main() {
nes::Nes device; nes::Nes device;
nes::SdlWindow window(nes::Ppu::SCREEN_WIDTH, nes::Ppu::SCREEN_HEIGHT); nes::SdlWindow window(nes::Ppu::SCREEN_WIDTH, nes::Ppu::SCREEN_HEIGHT);
window.setSize(nes::Ppu::SCREEN_WIDTH * 4, nes::Ppu::SCREEN_HEIGHT * 4);
device.setNewFrameCallback(std::bind(&nes::SdlWindow::drawFrame, &window, _1)); device.setNewFrameCallback(std::bind(&nes::SdlWindow::drawFrame, &window, _1));
//device.insertCartridge("/home/selim/Downloads/nestest.nes"); //device.insertCartridge("/home/selim/Downloads/nestest.nes");
device.insertCartridge("/Users/selim/Documents/nestest.nes"); //device.insertCartridge("/Users/selim/Documents/nestest.nes");
device.insertCartridge("C:\\Users\\selim\\Documents\\nestest.nes");
SDL_Event e; SDL_Event e;
uint64_t cycles = 0; uint64_t cycles = 0;

View File

@ -25,6 +25,7 @@ namespace nes {
void Nes::reset(std::optional<uint16_t> address) { void Nes::reset(std::optional<uint16_t> address) {
_cpu->reset(); _cpu->reset();
_ppu->reset();
if(address) { if(address) {
_cpu->setStartAddress(address.value()); _cpu->setStartAddress(address.value());
} }

View File

@ -4,8 +4,6 @@
#include "Ppu.h" #include "Ppu.h"
#include <iostream>
namespace nes { namespace nes {
Ppu::Ppu(): _column{}, _scanline{}, _status{}, _control{}, _mask{} { Ppu::Ppu(): _column{}, _scanline{}, _status{}, _control{}, _mask{} {
@ -101,7 +99,7 @@ namespace nes {
if ((_column >= 2 && _column < 258) || (_column >= 321 && _column < 338)) { if ((_column >= 2 && _column < 258) || (_column >= 321 && _column < 338)) {
_bgPatternShifter.shift(); _bgPatternShifter.shift();
_bgAttribShifter.shift(); _bgAttribShifter.shift();
_nextBgTile = prepareNextBgTile(_column); prepareNextBgTile(_column);
} }
if(_column == 256) { if(_column == 256) {
@ -128,15 +126,20 @@ namespace nes {
uint8_t palette = 0; uint8_t palette = 0;
if(_column < SCREEN_WIDTH && _scanline < SCREEN_HEIGHT && _scanline >= 0) { if(_column < SCREEN_WIDTH && _scanline < SCREEN_HEIGHT && _scanline >= 0) {
// Pixel black = {0, 0, 0, 100};
// Pixel white = {255, 255, 255, 100};
// Pixel pixel = std::rand() % 2 == 0 ? black : white;
// setPixel(_scanline, _column, pixel);
if(_mask.renderBackground) { if(_mask.renderBackground) {
colorIndex = _bgPatternShifter.getValue(_fineX); colorIndex = _bgPatternShifter.getValue(_fineX);
palette = _bgAttribShifter.getValue(_fineX); palette = _bgAttribShifter.getValue(_fineX);
Pixel pixel = getColor(palette, colorIndex); Pixel pixel = getColor(palette, colorIndex);
// For debugging
if (colorIndex != 0) {
pixel = Pixel(0xFF, 0xFF, 0xFF);
}
else {
pixel = Pixel();
}
setPixel(_scanline, _column, pixel); setPixel(_scanline, _column, pixel);
} }
} }
@ -223,6 +226,22 @@ namespace nes {
_cartridge = std::move(cartridge); _cartridge = std::move(cartridge);
} }
void Ppu::reset() {
_column = 0;
_scanline = 0;
_status.value = 0;
_control.value = 0;
_mask.value = 0;
_vRamAddress.value = 0;
_tRamAddress.value = 0;
_addressWriteInProgress = false;
_dataTemp = 0;
_fineX = 0;
_nextBgTile = { 0, 0, 0, 0 };
_bgPatternShifter = {};
_bgAttribShifter = {};
}
uint8_t Ppu::internalRead(uint16_t address) { uint8_t Ppu::internalRead(uint16_t address) {
address &= 0x3FFF; address &= 0x3FFF;
@ -254,6 +273,9 @@ namespace nes {
else if(address >= 0x2000 && address < 0x3F00) { else if(address >= 0x2000 && address < 0x3F00) {
address &= 0x0FFF; address &= 0x0FFF;
_nameTable[address & 0x03FF] = value; _nameTable[address & 0x03FF] = value;
if (value == 32) {
int x = 0;
}
} }
else if(address >= 0x3F00 && address < 0x4000) { else if(address >= 0x3F00 && address < 0x4000) {
address &= 0x1F; address &= 0x1F;
@ -270,8 +292,7 @@ namespace nes {
return _palette[internalRead(address) & 0x3F]; return _palette[internalRead(address) & 0x3F];
} }
Ppu::TileInfo Ppu::prepareNextBgTile(uint16_t column) { void Ppu::prepareNextBgTile(uint16_t column) {
TileInfo nextBgTile{};
// Every 8 ticks extract info about next tile // Every 8 ticks extract info about next tile
// Spread the work across those 8 ticks // Spread the work across those 8 ticks
@ -280,43 +301,41 @@ namespace nes {
_bgPatternShifter.load(_nextBgTile.lsb, _nextBgTile.msb); _bgPatternShifter.load(_nextBgTile.lsb, _nextBgTile.msb);
_bgAttribShifter.load((_nextBgTile.attribute & 0b01) ? 0xFF : 0x00, _bgAttribShifter.load((_nextBgTile.attribute & 0b01) ? 0xFF : 0x00,
(_nextBgTile.attribute & 0b10) ? 0xFF : 0x00); (_nextBgTile.attribute & 0b10) ? 0xFF : 0x00);
nextBgTile.id = internalRead(0x2000 + _vRamAddress.value & 0x0FFF); _nextBgTile.id = internalRead(0x2000 | (_vRamAddress.value & 0x0FFF));
break; break;
case 2: { case 2: {
uint16_t offset = (_vRamAddress.nameTableX << 10) uint16_t offset = (_vRamAddress.nameTableX << 10)
| (_vRamAddress.nameTableY << 11) | (_vRamAddress.nameTableY << 11)
| (_vRamAddress.coarseX >> 2) | (_vRamAddress.coarseX >> 2)
| ((_vRamAddress.coarseY >> 2) << 3); | ((_vRamAddress.coarseY >> 2) << 3);
nextBgTile.attribute = internalRead(0x23C0 + offset); _nextBgTile.attribute = internalRead(0x23C0 + offset);
if (_vRamAddress.coarseY & 0x02) nextBgTile.attribute >>= 4; if (_vRamAddress.coarseY & 0x02) _nextBgTile.attribute >>= 4;
if (_vRamAddress.coarseX & 0x02) nextBgTile.attribute >>= 2; if (_vRamAddress.coarseX & 0x02) _nextBgTile.attribute >>= 2;
nextBgTile.attribute &= 0x03; _nextBgTile.attribute &= 0x03;
break;
} }
break;
case 4: { case 4: {
uint16_t address = (_control.patternBackground << 12) uint16_t address = (_control.patternBackground << 12)
| ((uint16_t)nextBgTile.id << 4) | ((uint16_t)_nextBgTile.id << 4)
| _vRamAddress.fineY; | _vRamAddress.fineY;
nextBgTile.lsb = internalRead(address); _nextBgTile.lsb = internalRead(address);
break;
} }
break;
case 6: { case 6: {
uint16_t address = (_control.patternBackground << 12) uint16_t address = (_control.patternBackground << 12)
| ((uint16_t)nextBgTile.id << 4) | ((uint16_t)_nextBgTile.id << 4)
| (_vRamAddress.fineY + 8); | (_vRamAddress.fineY + 8);
nextBgTile.lsb = internalRead(address); _nextBgTile.lsb = internalRead(address);
break;
} }
break;
case 7: case 7:
incrementScrollX(); incrementScrollX();
break; break;
} }
return nextBgTile;
} }
void Ppu::incrementScrollX() { void Ppu::incrementScrollX() {
if(!_mask.renderBackground) if(!_mask.renderBackground && !_mask.renderSprites)
return; return;
if(_vRamAddress.coarseX == 31) { if(_vRamAddress.coarseX == 31) {
@ -328,7 +347,7 @@ namespace nes {
} }
void Ppu::incrementScrollY() { void Ppu::incrementScrollY() {
if(!_mask.renderBackground) if(!_mask.renderBackground && !_mask.renderSprites)
return; return;
if(_vRamAddress.fineY < 7) { if(_vRamAddress.fineY < 7) {
@ -348,14 +367,14 @@ namespace nes {
} }
void Ppu::updateScrollX() { void Ppu::updateScrollX() {
if(_mask.renderBackground) { if(_mask.renderBackground && !_mask.renderSprites) {
_vRamAddress.nameTableX = _tRamAddress.nameTableX; _vRamAddress.nameTableX = _tRamAddress.nameTableX;
_vRamAddress.coarseX = _tRamAddress.coarseX; _vRamAddress.coarseX = _tRamAddress.coarseX;
} }
} }
void Ppu::updateScrollY() { void Ppu::updateScrollY() {
if(_mask.renderBackground) { if(_mask.renderBackground && !_mask.renderSprites) {
_vRamAddress.nameTableY =_tRamAddress.nameTableY; _vRamAddress.nameTableY =_tRamAddress.nameTableY;
_vRamAddress.coarseY = _tRamAddress.coarseY; _vRamAddress.coarseY = _tRamAddress.coarseY;
_vRamAddress.fineY = _tRamAddress.fineY; _vRamAddress.fineY = _tRamAddress.fineY;

View File

@ -20,7 +20,7 @@ namespace nes {
uint8_t B = 0; uint8_t B = 0;
uint8_t A = 0xFF; uint8_t A = 0xFF;
Pixel(): R{}, G{}, B{}, A{} {} Pixel(): R{}, G{}, B{}, A{0xFF} {}
Pixel(uint8_t R, uint8_t G, uint8_t B): R{R}, G{G}, B{B}, A{0xFF} {} Pixel(uint8_t R, uint8_t G, uint8_t B): R{R}, G{G}, B{B}, A{0xFF} {}
}; };
@ -104,6 +104,7 @@ namespace nes {
uint8_t read(uint16_t address); uint8_t read(uint16_t address);
void setPixel(uint16_t row, uint16_t column, Pixel pixel); void setPixel(uint16_t row, uint16_t column, Pixel pixel);
void connect(std::shared_ptr<Cartridge> cartridge); void connect(std::shared_ptr<Cartridge> cartridge);
void reset();
public: public:
std::function<void(const Pixel*)> onNewFrame; std::function<void(const Pixel*)> onNewFrame;
@ -112,7 +113,7 @@ 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 prepareNextBgTile(uint16_t column);
void incrementScrollX(); void incrementScrollX();
void incrementScrollY(); void incrementScrollY();
void updateScrollX(); void updateScrollX();

View File

@ -14,10 +14,12 @@ namespace nes {
_wnd.reset(SDL_CreateWindow("NES", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, flags)); _wnd.reset(SDL_CreateWindow("NES", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, flags));
if(!_wnd) throw std::runtime_error("Error creating SDL window"); if(!_wnd) throw std::runtime_error("Error creating SDL window");
SDL_SetWindowResizable(_wnd.get(), SDL_TRUE);
_renderer.reset(SDL_CreateRenderer(_wnd.get(), -1, 0)); _renderer.reset(SDL_CreateRenderer(_wnd.get(), -1, 0));
if(!_renderer) throw std::runtime_error("Error creating SDL renderer"); if(!_renderer) throw std::runtime_error("Error creating SDL renderer");
_texture.reset(SDL_CreateTexture(_renderer.get(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, width, height)); _texture.reset(SDL_CreateTexture(_renderer.get(), SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, width, height));
if(!_texture) throw std::runtime_error("Error creating SDL texture"); if(!_texture) throw std::runtime_error("Error creating SDL texture");
} }
@ -28,4 +30,8 @@ namespace nes {
SDL_RenderCopy(_renderer.get(), _texture.get(), nullptr, nullptr); SDL_RenderCopy(_renderer.get(), _texture.get(), nullptr, nullptr);
SDL_RenderPresent(_renderer.get()); SDL_RenderPresent(_renderer.get());
} }
void SdlWindow::setSize(int width, int height) {
SDL_SetWindowSize(_wnd.get(), width, height);
}
} }

View File

@ -28,6 +28,7 @@ namespace nes {
public: public:
SdlWindow(uint16_t width, uint16_t height); SdlWindow(uint16_t width, uint16_t height);
void drawFrame(const Pixel* buffer); void drawFrame(const Pixel* buffer);
void setSize(int width, int height);
private: private:
uint16_t _width; uint16_t _width;

View File

@ -2,8 +2,14 @@
"name" : "nes", "name" : "nes",
"version-string" : "1.0.0", "version-string" : "1.0.0",
"builtin-baseline" : "c95000e1b5bb62884de08d5e952993c8bced9db6", "builtin-baseline" : "c95000e1b5bb62884de08d5e952993c8bced9db6",
"dependencies" : [ { "dependencies": [
{
"name": "sdl2", "name": "sdl2",
"version>=": "2.26.5" "version>=": "2.26.5"
} ] },
{
"name": "fmt",
"version>=": "10.0.0"
}
]
} }