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--;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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++;
|
||||
}
|
||||
|
||||
|
||||
192
src/Ppu.cpp
192
src/Ppu.cpp
@ -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];
|
||||
}
|
||||
}
|
||||
61
src/Ppu.h
61
src/Ppu.h
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user