PPU work
This commit is contained in:
parent
3ad6418726
commit
4341e47e17
3 changed files with 192 additions and 66 deletions
172
Core/PPU.cpp
172
Core/PPU.cpp
|
@ -24,23 +24,31 @@ bool PPU::CheckFlag(PPUControlFlags flag)
|
|||
|
||||
uint8_t PPU::ReadRAM(uint16_t addr)
|
||||
{
|
||||
uint8_t returnValue;
|
||||
switch(GetRegisterID(addr)) {
|
||||
case PPURegisters::Control:
|
||||
return (uint8_t)_state.Control;
|
||||
case PPURegisters::Control2:
|
||||
return (uint8_t)_state.Control2;
|
||||
case PPURegisters::Status:
|
||||
_writeLow = true;
|
||||
_state.WriteToggle = false;
|
||||
_flags.IntensifyBlue = false;
|
||||
UpdateStatusFlag();
|
||||
return _state.Status;
|
||||
case PPURegisters::SpriteData:
|
||||
return _spriteRAM[_state.SpriteRamAddr];
|
||||
case PPURegisters::VideoMemoryData:
|
||||
uint8_t returnValue = _memoryReadBuffer;
|
||||
returnValue = _memoryReadBuffer;
|
||||
_memoryReadBuffer = _memoryManager->ReadVRAM(_state.VideoRamAddr);
|
||||
_state.VideoRamAddr += _flags.VerticalWrite ? 32 : 1;
|
||||
|
||||
if(_state.VideoRamAddr >= 0x3F00) {
|
||||
//No buffer for palette
|
||||
//TODO: Update read buffer when reading palette (See: http://wiki.nesdev.com/w/index.php/PPU_registers#The_PPUDATA_read_buffer_.28post-fetch.29)
|
||||
return _memoryReadBuffer;
|
||||
} else {
|
||||
return returnValue;
|
||||
}
|
||||
default:
|
||||
//other registers are meant to be read-only
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -59,19 +67,26 @@ void PPU::WriteRAM(uint16_t addr, uint8_t value)
|
|||
_state.SpriteRamAddr = value;
|
||||
break;
|
||||
case PPURegisters::SpriteData:
|
||||
_spriteRAM[_state.SpriteRamAddr&0xFF] = value;
|
||||
_spriteRAM[_state.SpriteRamAddr] = value;
|
||||
_state.SpriteRamAddr++;
|
||||
break;
|
||||
case PPURegisters::ScrollOffsets:
|
||||
_writeLow = !_writeLow;
|
||||
if(_state.WriteToggle) {
|
||||
_state.TmpVideoRamAddr = (_state.TmpVideoRamAddr & ~0x73E0) | ((value & 0xF8) << 2) | ((value & 0x0F) << 12);
|
||||
} else {
|
||||
_state.XScroll = value & 0x07;
|
||||
_state.TmpVideoRamAddr = (_state.TmpVideoRamAddr & ~0x00F8) | (value >> 3);
|
||||
}
|
||||
_state.WriteToggle = !_state.WriteToggle;
|
||||
break;
|
||||
case PPURegisters::VideoMemoryAddr:
|
||||
if(_writeLow) {
|
||||
_state.VideoRamAddr &= 0xFF00;
|
||||
_state.VideoRamAddr |= value;
|
||||
if(_state.WriteToggle) {
|
||||
_state.TmpVideoRamAddr |= (_state.TmpVideoRamAddr & ~0xFF00) | (value & 0x3F) << 8;
|
||||
_state.VideoRamAddr = _state.TmpVideoRamAddr;
|
||||
} else {
|
||||
_state.VideoRamAddr |= value<<8;
|
||||
_state.TmpVideoRamAddr = (_state.TmpVideoRamAddr & ~0x00FF) | value;
|
||||
}
|
||||
_writeLow = !_writeLow;
|
||||
_state.WriteToggle = !_state.WriteToggle;
|
||||
break;
|
||||
case PPURegisters::VideoMemoryData:
|
||||
_memoryManager->WriteVRAM(_state.VideoRamAddr, value);
|
||||
|
@ -80,6 +95,11 @@ void PPU::WriteRAM(uint16_t addr, uint8_t value)
|
|||
}
|
||||
}
|
||||
|
||||
bool PPU::IsRenderingEnabled()
|
||||
{
|
||||
return _flags.BackgroundEnabled || _flags.SpritesEnabled;
|
||||
}
|
||||
|
||||
void PPU::UpdateFlags()
|
||||
{
|
||||
uint8_t nameTable = (_state.Control & 0x03);
|
||||
|
@ -89,6 +109,8 @@ void PPU::UpdateFlags()
|
|||
case 2: _flags.NameTableAddr = 0x2800; break;
|
||||
case 3: _flags.NameTableAddr = 0x2C00; break;
|
||||
}
|
||||
_state.TmpVideoRamAddr = (_state.TmpVideoRamAddr & ~0xC0000) | (nameTable << 10);
|
||||
|
||||
_flags.VerticalWrite = (_state.Control & 0x04) == 0x04;
|
||||
_flags.SpritePatternAddr = ((_state.Control & 0x08) == 0x08) ? 0x1000 : 0x0000;
|
||||
_flags.BackgroundPatternAddr = ((_state.Control & 0x10) == 0x10) ? 0x1000 : 0x0000;
|
||||
|
@ -113,26 +135,98 @@ void PPU::UpdateStatusFlag()
|
|||
_statusFlags.VerticalBlank = false;
|
||||
}
|
||||
|
||||
void PPU::Exec()
|
||||
//Taken from http://wiki.nesdev.com/w/index.php/The_skinny_on_NES_scrolling#Wrapping_around
|
||||
void PPU::IncVerticalScrolling()
|
||||
{
|
||||
uint64_t equivalentCycleCount = CPU::GetCycleCount() * 3;
|
||||
while(_cycleCount < equivalentCycleCount) {
|
||||
if(_scanline == -1) {
|
||||
//Pre-render scanline
|
||||
uint16_t addr = _state.VideoRamAddr;
|
||||
|
||||
if((addr & 0x7000) != 0x7000) {
|
||||
// if fine Y < 7
|
||||
addr += 0x1000; // increment fine Y
|
||||
} else {
|
||||
// fine Y = 0
|
||||
addr &= ~0x7000;
|
||||
int y = (addr & 0x03E0) >> 5; // let y = coarse Y
|
||||
if(y == 29) {
|
||||
y = 0; // coarse Y = 0
|
||||
addr ^= 0x0800; // switch vertical nametable
|
||||
} else if(y == 31){
|
||||
y = 0; // coarse Y = 0, nametable not switched
|
||||
} else {
|
||||
y += 1; // increment coarse Y
|
||||
}
|
||||
addr = (addr & ~0x03E0) | (y << 5); // put coarse Y back into v
|
||||
}
|
||||
_state.VideoRamAddr = addr;
|
||||
}
|
||||
|
||||
//Taken from http://wiki.nesdev.com/w/index.php/The_skinny_on_NES_scrolling#Wrapping_around
|
||||
void PPU::IncHorizontalScrolling()
|
||||
{
|
||||
//Increase coarse X scrolling value.
|
||||
//When the value is 31, wrap around to 0 and switch nametable
|
||||
uint16_t addr = _state.VideoRamAddr;
|
||||
if((addr & 0x001F) == 31) {
|
||||
addr &= ~0x001F;
|
||||
addr ^= 0x0400; // switch horizontal nametable
|
||||
} else {
|
||||
addr += 1;
|
||||
}
|
||||
_state.VideoRamAddr = addr;
|
||||
}
|
||||
|
||||
//Take from http://wiki.nesdev.com/w/index.php/The_skinny_on_NES_scrolling#Tile_and_attribute_fetching
|
||||
uint16_t PPU::GetTileAddr()
|
||||
{
|
||||
return 0x2000 | (_state.VideoRamAddr & 0x0FFF);
|
||||
}
|
||||
|
||||
//Take from http://wiki.nesdev.com/w/index.php/The_skinny_on_NES_scrolling#Tile_and_attribute_fetching
|
||||
uint16_t PPU::GetAttributeAddr()
|
||||
{
|
||||
return 0x23C0 | (_state.VideoRamAddr & 0x0C00) | ((_state.VideoRamAddr >> 4) & 0x38) | ((_state.VideoRamAddr >> 2) & 0x07);
|
||||
}
|
||||
|
||||
void PPU::UpdateScrolling()
|
||||
{
|
||||
//For pre-render scanline & all visible scanlines
|
||||
if(_cycle == 256) {
|
||||
IncVerticalScrolling();
|
||||
} else if(_cycle == 257) {
|
||||
//copy horizontal scrolling value from t
|
||||
_state.VideoRamAddr = (_state.VideoRamAddr & ~0x041F) | (_state.TmpVideoRamAddr & 0x041F);
|
||||
} else if((_cycle % 8 == 0 && _cycle < 256) || _cycle == 328 || _cycle == 336) {
|
||||
IncHorizontalScrolling();
|
||||
}
|
||||
}
|
||||
|
||||
void PPU::ProcessPrerenderScanline()
|
||||
{
|
||||
if(IsRenderingEnabled()) {
|
||||
UpdateScrolling();
|
||||
}
|
||||
|
||||
if(_cycle == 1) {
|
||||
_statusFlags.SpriteOverflow = false;
|
||||
_statusFlags.Sprite0Hit = false;
|
||||
_statusFlags.VerticalBlank = false;
|
||||
} else if(_cycle == 304) {
|
||||
// Copy scroll latch into VRAMADDR register
|
||||
/*if(_flags.BackgroundEnabled || _flags.SpritesEnabled) {
|
||||
//p->registers.vramAddress = p->registers.vramLatch;
|
||||
}*/
|
||||
} else if(_cycle >= 280 && _cycle <= 304) {
|
||||
if(IsRenderingEnabled()) {
|
||||
//copy vertical scrolling value from t
|
||||
_state.VideoRamAddr = (_state.VideoRamAddr & ~0x7BF0) | (_state.TmpVideoRamAddr & 0x7BF0);
|
||||
}
|
||||
} else if(_cycle == 339 && _flags.BackgroundEnabled && (_frameCount % 2 == 1)) {
|
||||
//Skip a cycle for odd frames, if background drawing is enabled
|
||||
_cycle++;
|
||||
}
|
||||
} else if(_scanline < 240) {
|
||||
}
|
||||
|
||||
void PPU::ProcessVisibleScanline()
|
||||
{
|
||||
if(IsRenderingEnabled()) {
|
||||
UpdateScrolling();
|
||||
}
|
||||
|
||||
if(_cycle == 254) {
|
||||
if(_flags.BackgroundEnabled) {
|
||||
//Ppu_renderTileRow(p);
|
||||
|
@ -146,8 +240,10 @@ void PPU::Exec()
|
|||
//Ppu_updateEndScanlineRegisters(p);
|
||||
}
|
||||
}
|
||||
} else if(_scanline == 241) {
|
||||
//Start of VBlank
|
||||
}
|
||||
|
||||
void PPU::BeginVBlank()
|
||||
{
|
||||
if(_cycle == 1) {
|
||||
_statusFlags.VerticalBlank = true;
|
||||
/*if(!_suppressVBlank) {
|
||||
|
@ -158,18 +254,32 @@ void PPU::Exec()
|
|||
if(_flags.VBlank) {
|
||||
CPU::SetNMIFlag();
|
||||
}
|
||||
/*if(_flags.VBlank && !_suppressNMI) {
|
||||
VBlankInterrupt();
|
||||
}*/
|
||||
//Ppu_raster(p);
|
||||
}
|
||||
} else if(_scanline == 260) {
|
||||
//End of VBlank
|
||||
}
|
||||
|
||||
void PPU::EndVBlank()
|
||||
{
|
||||
if(_cycle == 340) {
|
||||
_frameCount++;
|
||||
}
|
||||
}
|
||||
|
||||
void PPU::Exec()
|
||||
{
|
||||
bool renderingEnabled = IsRenderingEnabled();
|
||||
uint64_t equivalentCycleCount = CPU::GetCycleCount() * 3;
|
||||
while(_cycleCount < equivalentCycleCount) {
|
||||
if(_scanline == -1) {
|
||||
ProcessPrerenderScanline();
|
||||
} else if(_scanline < 240) {
|
||||
ProcessVisibleScanline();
|
||||
} else if(_scanline == 241) {
|
||||
BeginVBlank();
|
||||
} else if(_scanline == 260) {
|
||||
EndVBlank();
|
||||
}
|
||||
|
||||
if(_cycle == 340) {
|
||||
_cycle = 0;
|
||||
_scanline++;
|
||||
|
|
25
Core/PPU.h
25
Core/PPU.h
|
@ -48,6 +48,9 @@ struct PPUState
|
|||
uint8_t Status;
|
||||
uint8_t SpriteRamAddr;
|
||||
uint16_t VideoRamAddr;
|
||||
uint8_t XScroll;
|
||||
uint16_t TmpVideoRamAddr;
|
||||
bool WriteToggle;
|
||||
};
|
||||
|
||||
class PPU : public IMemoryHandler
|
||||
|
@ -67,15 +70,27 @@ class PPU : public IMemoryHandler
|
|||
uint16_t _cycle = 0;
|
||||
uint32_t _frameCount = 0;
|
||||
|
||||
bool _writeLow = false;
|
||||
|
||||
PPUControlFlags _flags;
|
||||
PPUStatusFlags _statusFlags;
|
||||
|
||||
void PPU::UpdateStatusFlag();
|
||||
void UpdateStatusFlag();
|
||||
|
||||
void PPU::UpdateFlags();
|
||||
bool PPU::CheckFlag(PPUControlFlags flag);
|
||||
void UpdateFlags();
|
||||
bool CheckFlag(PPUControlFlags flag);
|
||||
|
||||
bool IsRenderingEnabled();
|
||||
|
||||
void IncVerticalScrolling();
|
||||
void IncHorizontalScrolling();
|
||||
uint16_t GetTileAddr();
|
||||
uint16_t GetAttributeAddr();
|
||||
|
||||
void UpdateScrolling();
|
||||
void ProcessPrerenderScanline();
|
||||
void ProcessVisibleScanline();
|
||||
|
||||
void BeginVBlank();
|
||||
void EndVBlank();
|
||||
|
||||
PPURegisters GetRegisterID(uint16_t addr)
|
||||
{
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <memory>
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
|
Loading…
Add table
Reference in a new issue