Adding some more PPU operations

This commit is contained in:
Selim Mustafaev 2023-08-28 00:30:41 +03:00
parent 2278082cbb
commit 0019266efe
5 changed files with 257 additions and 21 deletions

View File

@ -204,6 +204,22 @@ namespace nes {
_ticks--;
}
void Cpu::nmi() {
_bus->write(STACK_BASE + SP--, PC >> 8);
_bus->write(STACK_BASE + SP--, PC & 0x00FF);
setFlag(Break, false);
setFlag(Unused, true);
setFlag(InterruptDisable, true);
_bus->write(STACK_BASE + SP--, flags);
uint8_t lo = _bus->read(0xFFFA);
uint8_t hi = _bus->read(0xFFFB);
PC = (hi << 8) | lo;
_ticks = 8;
}
void Cpu::setFlag(CpuFlags flag, bool value) {
if(value) {
flags |= flag;

View File

@ -47,6 +47,7 @@ namespace nes {
bool getFlag(CpuFlags flag) const;
void setStartAddress(uint16_t address);
Bus* bus() const;
void nmi();
private:
size_t _ticks;

View File

@ -19,6 +19,7 @@ namespace nes {
_cartridge = std::make_shared<Cartridge>(path);
_cpu->bus()->connect(_cartridge);
_cpu->bus()->connect(_ppu);
_ppu->connect(_cartridge);
reset(address);
}
@ -34,10 +35,15 @@ namespace nes {
}
void Nes::tick() {
_ppu->tick();
bool needInterrupt = _ppu->tick();
if(_cycles % 3 == 0) {
_cpu->tick();
}
if(needInterrupt) {
_cpu->nmi();
}
_cycles++;
}

View File

@ -6,17 +6,97 @@
namespace nes {
Ppu::Ppu(): _column{}, _scanline{}, _status{} {
Ppu::Ppu(): _column{}, _scanline{}, _status{}, _control{}, _mask{} {
_frameBuffer = std::make_unique<Pixel[]>(SCREEN_WIDTH*SCREEN_HEIGHT);
_nameTable = std::make_unique<uint8_t[]>(1024);
_paletteTable = std::make_unique<uint8_t[]>(32);
_palette = std::make_unique<Pixel[]>(64);
_palette[0x00] = Pixel(84, 84, 84);
_palette[0x01] = Pixel(0, 30, 116);
_palette[0x02] = Pixel(8, 16, 144);
_palette[0x03] = Pixel(48, 0, 136);
_palette[0x04] = Pixel(68, 0, 100);
_palette[0x05] = Pixel(92, 0, 48);
_palette[0x06] = Pixel(84, 4, 0);
_palette[0x07] = Pixel(60, 24, 0);
_palette[0x08] = Pixel(32, 42, 0);
_palette[0x09] = Pixel(8, 58, 0);
_palette[0x0A] = Pixel(0, 64, 0);
_palette[0x0B] = Pixel(0, 60, 0);
_palette[0x0C] = Pixel(0, 50, 60);
_palette[0x0D] = Pixel(0, 0, 0);
_palette[0x0E] = Pixel(0, 0, 0);
_palette[0x0F] = Pixel(0, 0, 0);
_palette[0x10] = Pixel(152, 150, 152);
_palette[0x11] = Pixel(8, 76, 196);
_palette[0x12] = Pixel(48, 50, 236);
_palette[0x13] = Pixel(92, 30, 228);
_palette[0x14] = Pixel(136, 20, 176);
_palette[0x15] = Pixel(160, 20, 100);
_palette[0x16] = Pixel(152, 34, 32);
_palette[0x17] = Pixel(120, 60, 0);
_palette[0x18] = Pixel(84, 90, 0);
_palette[0x19] = Pixel(40, 114, 0);
_palette[0x1A] = Pixel(8, 124, 0);
_palette[0x1B] = Pixel(0, 118, 40);
_palette[0x1C] = Pixel(0, 102, 120);
_palette[0x1D] = Pixel(0, 0, 0);
_palette[0x1E] = Pixel(0, 0, 0);
_palette[0x1F] = Pixel(0, 0, 0);
_palette[0x20] = Pixel(236, 238, 236);
_palette[0x21] = Pixel(76, 154, 236);
_palette[0x22] = Pixel(120, 124, 236);
_palette[0x23] = Pixel(176, 98, 236);
_palette[0x24] = Pixel(228, 84, 236);
_palette[0x25] = Pixel(236, 88, 180);
_palette[0x26] = Pixel(236, 106, 100);
_palette[0x27] = Pixel(212, 136, 32);
_palette[0x28] = Pixel(160, 170, 0);
_palette[0x29] = Pixel(116, 196, 0);
_palette[0x2A] = Pixel(76, 208, 32);
_palette[0x2B] = Pixel(56, 204, 108);
_palette[0x2C] = Pixel(56, 180, 204);
_palette[0x2D] = Pixel(60, 60, 60);
_palette[0x2E] = Pixel(0, 0, 0);
_palette[0x2F] = Pixel(0, 0, 0);
_palette[0x30] = Pixel(236, 238, 236);
_palette[0x31] = Pixel(168, 204, 236);
_palette[0x32] = Pixel(188, 188, 236);
_palette[0x33] = Pixel(212, 178, 236);
_palette[0x34] = Pixel(236, 174, 236);
_palette[0x35] = Pixel(236, 174, 212);
_palette[0x36] = Pixel(236, 180, 176);
_palette[0x37] = Pixel(228, 196, 144);
_palette[0x38] = Pixel(204, 210, 120);
_palette[0x39] = Pixel(180, 222, 120);
_palette[0x3A] = Pixel(168, 226, 144);
_palette[0x3B] = Pixel(152, 226, 180);
_palette[0x3C] = Pixel(160, 214, 228);
_palette[0x3D] = Pixel(160, 162, 160);
_palette[0x3E] = Pixel(0, 0, 0);
_palette[0x3F] = Pixel(0, 0, 0);
}
void Ppu::tick() {
bool Ppu::tick() {
if(_scanline == 241 && _column == 1) {
_status.verticalBlank = 1;
}
uint8_t colorIndex = 0;
uint8_t palette = 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);
// 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) {
Pixel pixel = getColor(palette, colorIndex);
setPixel(_scanline, _column, pixel);
}
}
_column++;
@ -29,26 +109,108 @@ namespace nes {
onNewFrame(_frameBuffer.get());
}
}
return _status.verticalBlank && _control.enableNmi;
}
uint8_t Ppu::read(uint16_t address) {
uint8_t value = 0;
switch (address) {
case Status:
value = _status.value & 0xE0;
_status.verticalBlank = 0;
break;
case PpuData:
value = _dataTemp;
_dataTemp = internalRead(address);
if(address >= 0x3F00) {
value = _dataTemp;
}
_vRamAddress += _control.incrementMode ? 32 : 1;
break;
}
return value;
}
void Ppu::write(uint16_t address, uint8_t value) {
switch (address) {
case Control:
_control.value = value;
break;
case Mask:
_mask.value = value;
break;
case PpuAddress:
if(_addressWriteInProgress) {
_vRamAddressTemp = (_vRamAddressTemp & 0xFF00) | value;
_vRamAddress = _vRamAddressTemp;
} else {
_vRamAddressTemp = ((value & 0x3F) << 8) | (_vRamAddressTemp & 0x00FF);
}
_addressWriteInProgress = !_addressWriteInProgress;
break;
case PpuData:
internalWrite(_vRamAddress, value);
_vRamAddress += _control.incrementMode ? 32 : 1;
break;
default:
break;
}
}
uint8_t Ppu::read(uint16_t address) {
switch (address) {
case Status:
uint8_t value = _status.value & 0xE0;
_status.verticalBlank = 0;
return value;
}
}
void Ppu::setPixel(uint16_t row, uint16_t column, Pixel pixel) {
size_t index = row*SCREEN_WIDTH + column;
_frameBuffer[index] = pixel;
}
void Ppu::connect(std::shared_ptr<Cartridge> cartridge) {
_cartridge = std::move(cartridge);
}
uint8_t Ppu::internalRead(uint16_t address) {
address &= 0x3FFF;
if(address < 0x2000) {
return _cartridge->readChr(address);
}
else if(address >= 0x2000 && address < 0x3F00) {
address &= 0x03FF;
return _nameTable[address];
}
else if(address >= 0x3F00 && address < 0x4000) {
address &= 0x1F;
if (address == 0x0010) address = 0x0000;
if (address == 0x0014) address = 0x0004;
if (address == 0x0018) address = 0x0008;
if (address == 0x001C) address = 0x000C;
return _paletteTable[address] & (_mask.grayscale ? 0x30 : 0x3F);
}
return 0;
}
void Ppu::internalWrite(uint16_t address, uint8_t value) {
address &= 0x3FFF;
if(address < 0x2000) {
// Can't write to CHR ROM
}
else if(address >= 0x2000 && address < 0x3F00) {
address &= 0x03FF;
_nameTable[address] = value;
}
else if(address >= 0x3F00 && address < 0x4000) {
address &= 0x1F;
if (address == 0x0010) address = 0x0000;
if (address == 0x0014) address = 0x0004;
if (address == 0x0018) address = 0x0008;
if (address == 0x001C) address = 0x000C;
_paletteTable[address] = value;
}
}
Pixel Ppu::getColor(uint8_t palette, uint8_t pixel) {
uint16_t address = 0x3F00 + palette*4 + pixel;
return _palette[internalRead(address) & 0x3F];
}
}

View File

@ -5,6 +5,8 @@
#ifndef NES_PPU_H
#define NES_PPU_H
#include "Cartridge.h"
#include <cstdint>
#include <memory>
#include <functional>
@ -12,10 +14,10 @@
namespace nes {
struct Pixel {
uint8_t R;
uint8_t G;
uint8_t B;
uint8_t A;
uint8_t R = 0;
uint8_t G = 0;
uint8_t B = 0;
uint8_t A = 0xFF;
};
class Ppu {
@ -44,21 +46,70 @@ namespace nes {
uint8_t value;
};
union ControlRegister {
struct {
uint8_t nameTableX: 1;
uint8_t nameTableY: 1;
uint8_t incrementMode: 1;
uint8_t patternSprite: 1;
uint8_t patternBackground: 1;
uint8_t spriteSize: 1;
uint8_t slaveMode: 1;
uint8_t enableNmi: 1;
};
uint8_t value;
};
union MaskRegister {
struct {
uint8_t grayscale: 1;
uint8_t renderBackgroundLeft: 1;
uint8_t renderSpritesLeft: 1;
uint8_t renderBackground: 1;
uint8_t renderSprites: 1;
uint8_t enhanceRed: 1;
uint8_t enhanceGreen: 1;
uint8_t enhanceBlue: 1;
};
uint8_t value;
};
public:
Ppu();
void tick();
bool tick();
void write(uint16_t address, uint8_t value);
uint8_t read(uint16_t address);
void setPixel(uint16_t row, uint16_t column, Pixel pixel);
void connect(std::shared_ptr<Cartridge> cartridge);
public:
std::function<void(const Pixel*)> onNewFrame;
private:
uint8_t internalRead(uint16_t address);
void internalWrite(uint16_t address, uint8_t value);
Pixel getColor(uint8_t palette, uint8_t pixel);
private:
int16_t _column;
int16_t _scanline;
StatusRegister _status;
ControlRegister _control;
MaskRegister _mask;
uint16_t _vRamAddress;
uint16_t _vRamAddressTemp;
bool _addressWriteInProgress;
uint8_t _dataTemp;
private:
std::unique_ptr<uint8_t[]> _nameTable;
std::unique_ptr<Pixel[]> _palette;
std::unique_ptr<uint8_t[]> _paletteTable;
private:
std::unique_ptr<Pixel[]> _frameBuffer;
std::shared_ptr<Cartridge> _cartridge;
};
}