NESemu/src/PPU.cpp

163 lines
2.6 KiB
C++
Raw Normal View History

2022-02-28 15:04:25 +00:00
#include "PPU.hpp"
#include "Log.hpp"
#include "Bus.hpp"
PPU::PPU(Bus* bus) :
bus(bus), ppuctrl{0}, ppustatus{0}
{
}
void PPU::Powerup()
{
ppuctrl.Raw = 0b00000000;
ppumask.Raw = 0b00000000;
ppustatus.Raw = 0b10100000;
ppuscroll.x = 0x00;
ppuscroll.y = 0x00;
ppuaddr.Raw = 0x0000;
x = 0;
y = 0;
addressLatch = 0;
}
void PPU::Reset()
{
ppuctrl.Raw = 0b00000000;
ppumask.Raw = 0b00000000;
ppuscroll.x = 0x00;
ppuscroll.y = 0x00;
x = 0;
y = 0;
addressLatch = 1;
}
void PPU::Tick()
{
2022-02-28 16:14:32 +00:00
// On this cycle the VBlankStarted bit is set in the ppustatus
2022-02-28 15:04:25 +00:00
if (y == 241 && x == 1)
{
2022-02-28 16:14:32 +00:00
// Set flag and send NMI if necessary
2022-02-28 15:04:25 +00:00
ppustatus.Flag.VBlankStarted = 1;
if (ppuctrl.Flag.VBlankNMI)
bus->NMI();
isFrameDone = true;
}
2022-02-28 16:14:32 +00:00
// This cycle resets the VBlankStarted flag
2022-02-28 15:04:25 +00:00
if (y == 261 && x == 1)
ppustatus.Flag.VBlankStarted = 0;
2022-02-28 16:14:32 +00:00
// Advance pixel counters
2022-02-28 15:04:25 +00:00
x++;
if (x > 340)
{
x = 0;
y++;
if (y > 261)
y = 0;
}
}
Byte PPU::ReadRegister(Byte id)
{
2022-02-28 16:14:32 +00:00
// Reading from a register fills the latch with the contents of the register
// Write-only regs don't fill the latch
// But in any case, the latch contents are returned
2022-02-28 15:04:25 +00:00
switch (id)
{
case 0:
2022-02-28 16:14:32 +00:00
latch = ppuctrl.Raw;
2022-02-28 15:04:25 +00:00
break;
case 1:
2022-02-28 16:14:32 +00:00
latch = ppumask.Raw;
2022-02-28 15:04:25 +00:00
break;
case 2:
2022-02-28 16:14:32 +00:00
latch = ppustatus.Raw;
2022-02-28 15:04:25 +00:00
ppustatus.Flag.VBlankStarted = 0;
addressLatch = 0;
break;
case 5:
break;
case 6:
break;
case 7:
2022-02-28 16:14:32 +00:00
latch = bus->ReadPPU(ppuaddr.Raw);
2022-02-28 15:04:25 +00:00
ppuaddr.Raw += (ppuctrl.Flag.VRAMAddrIncrement ? 32 : 1);
break;
default:
LOG_CORE_WARN("Tried to read unimplemented PPU register $20{0:02X}", (Word)id);
break;
}
2022-02-28 16:14:32 +00:00
return latch;
2022-02-28 15:04:25 +00:00
}
void PPU::WriteRegister(Byte id, Byte val)
{
switch (id)
{
case 0:
ppuctrl.Raw = val;
break;
case 1:
ppumask.Raw = val;
break;
case 2:
ppustatus.Raw = val;
break;
2022-02-28 16:14:32 +00:00
// PPUADDR and PPUSCROLL both take 2 accesses to fully set
// When writing to them the address latch is switched. The latch
// determines whether the hi or lo byte should be written next
2022-02-28 15:04:25 +00:00
case 5:
if (addressLatch == 0)
ppuscroll.x = val;
else
ppuscroll.y = val;
2022-02-28 16:14:32 +00:00
addressLatch = !addressLatch;
2022-02-28 15:04:25 +00:00
break;
case 6:
if (addressLatch == 0)
ppuaddr.Bytes.hi = val;
else
ppuaddr.Bytes.lo = val;
2022-02-28 16:14:32 +00:00
addressLatch = !addressLatch;
2022-02-28 15:04:25 +00:00
break;
case 7:
bus->WritePPU(ppuaddr.Raw, val);
ppuaddr.Raw += (ppuctrl.Flag.VRAMAddrIncrement ? 32 : 1);
break;
default:
LOG_CORE_WARN("Tried to write unimplemented PPU register $20{0:02X}", (Word)id);
return;
}
ppustatus.Flag.Unused = val & 0x1F;
}
Byte PPU::Read(Word addr)
{
return bus->ReadPPU(addr);
}
void PPU::Write(Word addr, Byte val)
{
bus->WritePPU(addr, val);
}