Adding background shifters
This commit is contained in:
parent
d07f61b081
commit
40c7ff5882
@ -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)
|
||||||
4
main.cpp
4
main.cpp
@ -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;
|
||||||
|
|||||||
@ -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());
|
||||||
}
|
}
|
||||||
|
|||||||
73
src/Ppu.cpp
73
src/Ppu.cpp
@ -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;
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -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;
|
||||||
|
|||||||
10
vcpkg.json
10
vcpkg.json
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user