Adding some more PPU operations
This commit is contained in:
parent
2278082cbb
commit
0019266efe
16
src/Cpu.cpp
16
src/Cpu.cpp
@ -204,6 +204,22 @@ namespace nes {
|
|||||||
_ticks--;
|
_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) {
|
void Cpu::setFlag(CpuFlags flag, bool value) {
|
||||||
if(value) {
|
if(value) {
|
||||||
flags |= flag;
|
flags |= flag;
|
||||||
|
|||||||
@ -47,6 +47,7 @@ namespace nes {
|
|||||||
bool getFlag(CpuFlags flag) const;
|
bool getFlag(CpuFlags flag) const;
|
||||||
void setStartAddress(uint16_t address);
|
void setStartAddress(uint16_t address);
|
||||||
Bus* bus() const;
|
Bus* bus() const;
|
||||||
|
void nmi();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t _ticks;
|
size_t _ticks;
|
||||||
|
|||||||
@ -19,6 +19,7 @@ namespace nes {
|
|||||||
_cartridge = std::make_shared<Cartridge>(path);
|
_cartridge = std::make_shared<Cartridge>(path);
|
||||||
_cpu->bus()->connect(_cartridge);
|
_cpu->bus()->connect(_cartridge);
|
||||||
_cpu->bus()->connect(_ppu);
|
_cpu->bus()->connect(_ppu);
|
||||||
|
_ppu->connect(_cartridge);
|
||||||
reset(address);
|
reset(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,10 +35,15 @@ namespace nes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Nes::tick() {
|
void Nes::tick() {
|
||||||
_ppu->tick();
|
bool needInterrupt = _ppu->tick();
|
||||||
if(_cycles % 3 == 0) {
|
if(_cycles % 3 == 0) {
|
||||||
_cpu->tick();
|
_cpu->tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(needInterrupt) {
|
||||||
|
_cpu->nmi();
|
||||||
|
}
|
||||||
|
|
||||||
_cycles++;
|
_cycles++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
192
src/Ppu.cpp
192
src/Ppu.cpp
@ -6,17 +6,97 @@
|
|||||||
|
|
||||||
namespace nes {
|
namespace nes {
|
||||||
|
|
||||||
Ppu::Ppu(): _column{}, _scanline{}, _status{} {
|
Ppu::Ppu(): _column{}, _scanline{}, _status{}, _control{}, _mask{} {
|
||||||
_frameBuffer = std::make_unique<Pixel[]>(SCREEN_WIDTH*SCREEN_HEIGHT);
|
_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) {
|
if(_column < SCREEN_WIDTH && _scanline < SCREEN_HEIGHT && _scanline >= 0) {
|
||||||
Pixel black = {0, 0, 0, 100};
|
// Pixel black = {0, 0, 0, 100};
|
||||||
Pixel white = {255, 255, 255, 100};
|
// Pixel white = {255, 255, 255, 100};
|
||||||
Pixel pixel = std::rand() % 2 == 0 ? black : white;
|
// Pixel pixel = std::rand() % 2 == 0 ? black : white;
|
||||||
setPixel(_scanline, _column, pixel);
|
// setPixel(_scanline, _column, pixel);
|
||||||
|
|
||||||
|
if(_mask.renderBackground) {
|
||||||
|
Pixel pixel = getColor(palette, colorIndex);
|
||||||
|
setPixel(_scanline, _column, pixel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_column++;
|
_column++;
|
||||||
@ -29,26 +109,108 @@ namespace nes {
|
|||||||
onNewFrame(_frameBuffer.get());
|
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) {
|
void Ppu::write(uint16_t address, uint8_t value) {
|
||||||
switch (address) {
|
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:
|
default:
|
||||||
break;
|
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) {
|
void Ppu::setPixel(uint16_t row, uint16_t column, Pixel pixel) {
|
||||||
size_t index = row*SCREEN_WIDTH + column;
|
size_t index = row*SCREEN_WIDTH + column;
|
||||||
_frameBuffer[index] = pixel;
|
_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];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
61
src/Ppu.h
61
src/Ppu.h
@ -5,6 +5,8 @@
|
|||||||
#ifndef NES_PPU_H
|
#ifndef NES_PPU_H
|
||||||
#define NES_PPU_H
|
#define NES_PPU_H
|
||||||
|
|
||||||
|
#include "Cartridge.h"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@ -12,10 +14,10 @@
|
|||||||
namespace nes {
|
namespace nes {
|
||||||
|
|
||||||
struct Pixel {
|
struct Pixel {
|
||||||
uint8_t R;
|
uint8_t R = 0;
|
||||||
uint8_t G;
|
uint8_t G = 0;
|
||||||
uint8_t B;
|
uint8_t B = 0;
|
||||||
uint8_t A;
|
uint8_t A = 0xFF;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Ppu {
|
class Ppu {
|
||||||
@ -44,21 +46,70 @@ namespace nes {
|
|||||||
uint8_t value;
|
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:
|
public:
|
||||||
Ppu();
|
Ppu();
|
||||||
void tick();
|
bool tick();
|
||||||
void write(uint16_t address, uint8_t value);
|
void write(uint16_t address, uint8_t value);
|
||||||
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);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::function<void(const Pixel*)> onNewFrame;
|
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:
|
private:
|
||||||
int16_t _column;
|
int16_t _column;
|
||||||
int16_t _scanline;
|
int16_t _scanline;
|
||||||
StatusRegister _status;
|
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::unique_ptr<Pixel[]> _frameBuffer;
|
||||||
|
std::shared_ptr<Cartridge> _cartridge;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user