added latch to ppu
This commit is contained in:
parent
7e50290634
commit
2b00f1ecb2
28
src/PPU.cpp
28
src/PPU.cpp
|
@ -35,8 +35,10 @@ void PPU::Reset()
|
||||||
|
|
||||||
void PPU::Tick()
|
void PPU::Tick()
|
||||||
{
|
{
|
||||||
|
// On this cycle the VBlankStarted bit is set in the ppustatus
|
||||||
if (y == 241 && x == 1)
|
if (y == 241 && x == 1)
|
||||||
{
|
{
|
||||||
|
// Set flag and send NMI if necessary
|
||||||
ppustatus.Flag.VBlankStarted = 1;
|
ppustatus.Flag.VBlankStarted = 1;
|
||||||
if (ppuctrl.Flag.VBlankNMI)
|
if (ppuctrl.Flag.VBlankNMI)
|
||||||
bus->NMI();
|
bus->NMI();
|
||||||
|
@ -44,9 +46,11 @@ void PPU::Tick()
|
||||||
isFrameDone = true;
|
isFrameDone = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This cycle resets the VBlankStarted flag
|
||||||
if (y == 261 && x == 1)
|
if (y == 261 && x == 1)
|
||||||
ppustatus.Flag.VBlankStarted = 0;
|
ppustatus.Flag.VBlankStarted = 0;
|
||||||
|
|
||||||
|
// Advance pixel counters
|
||||||
x++;
|
x++;
|
||||||
if (x > 340)
|
if (x > 340)
|
||||||
{
|
{
|
||||||
|
@ -59,34 +63,33 @@ void PPU::Tick()
|
||||||
|
|
||||||
Byte PPU::ReadRegister(Byte id)
|
Byte PPU::ReadRegister(Byte id)
|
||||||
{
|
{
|
||||||
Byte returnVal = 0x00;
|
// 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
|
||||||
switch (id)
|
switch (id)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
returnVal = ppuctrl.Raw;
|
latch = ppuctrl.Raw;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
returnVal = ppumask.Raw;
|
latch = ppumask.Raw;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
returnVal = ppustatus.Raw;
|
latch = ppustatus.Raw;
|
||||||
ppustatus.Flag.VBlankStarted = 0;
|
ppustatus.Flag.VBlankStarted = 0;
|
||||||
addressLatch = 0;
|
addressLatch = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 5:
|
case 5:
|
||||||
returnVal = 0x00;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 6:
|
case 6:
|
||||||
returnVal = 0x00;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 7:
|
case 7:
|
||||||
returnVal = bus->ReadPPU(ppuaddr.Raw);
|
latch = bus->ReadPPU(ppuaddr.Raw);
|
||||||
ppuaddr.Raw += (ppuctrl.Flag.VRAMAddrIncrement ? 32 : 1);
|
ppuaddr.Raw += (ppuctrl.Flag.VRAMAddrIncrement ? 32 : 1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -95,7 +98,7 @@ Byte PPU::ReadRegister(Byte id)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return returnVal;
|
return latch;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::WriteRegister(Byte id, Byte val)
|
void PPU::WriteRegister(Byte id, Byte val)
|
||||||
|
@ -114,13 +117,16 @@ void PPU::WriteRegister(Byte id, Byte val)
|
||||||
ppustatus.Raw = val;
|
ppustatus.Raw = val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// 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
|
||||||
case 5:
|
case 5:
|
||||||
if (addressLatch == 0)
|
if (addressLatch == 0)
|
||||||
ppuscroll.x = val;
|
ppuscroll.x = val;
|
||||||
else
|
else
|
||||||
ppuscroll.y = val;
|
ppuscroll.y = val;
|
||||||
|
|
||||||
addressLatch = 1 - addressLatch;
|
addressLatch = !addressLatch;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 6:
|
case 6:
|
||||||
|
@ -129,7 +135,7 @@ void PPU::WriteRegister(Byte id, Byte val)
|
||||||
else
|
else
|
||||||
ppuaddr.Bytes.lo = val;
|
ppuaddr.Bytes.lo = val;
|
||||||
|
|
||||||
addressLatch = 1 - addressLatch;
|
addressLatch = !addressLatch;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 7:
|
case 7:
|
||||||
|
|
38
src/PPU.hpp
38
src/PPU.hpp
|
@ -4,6 +4,9 @@
|
||||||
|
|
||||||
class Bus;
|
class Bus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The PPU of the NES.
|
||||||
|
*/
|
||||||
class PPU
|
class PPU
|
||||||
{
|
{
|
||||||
friend class PPUWatcher;
|
friend class PPUWatcher;
|
||||||
|
@ -11,18 +14,49 @@ class PPU
|
||||||
public:
|
public:
|
||||||
PPU(Bus* bus);
|
PPU(Bus* bus);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Powerup PPU.
|
||||||
|
* Internal state corresponds to powerup state
|
||||||
|
*/
|
||||||
void Powerup();
|
void Powerup();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Powerup PPU.
|
||||||
|
* Internal state corresponds to reset state
|
||||||
|
*/
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Tick PPU forward once.
|
||||||
|
*/
|
||||||
void Tick();
|
void Tick();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read from memory mapped PPU regs.
|
||||||
|
*/
|
||||||
Byte ReadRegister(Byte id);
|
Byte ReadRegister(Byte id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Write to memory mapped PPU regs.
|
||||||
|
*/
|
||||||
void WriteRegister(Byte id, Byte val);
|
void WriteRegister(Byte id, Byte val);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check whether the PPU finished rendering a frame.
|
||||||
|
* Returns true if the VBlankStart cycle was hit previously. The function resets
|
||||||
|
* the boolearn when it is called
|
||||||
|
*/
|
||||||
inline bool IsFrameDone() { bool returnVal = isFrameDone; isFrameDone = false; return returnVal; }
|
inline bool IsFrameDone() { bool returnVal = isFrameDone; isFrameDone = false; return returnVal; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/**
|
||||||
|
* @brief Wraps Bus::ReadPPU.
|
||||||
|
*/
|
||||||
Byte Read(Word addr);
|
Byte Read(Word addr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Wraps Bus::WritePPU.
|
||||||
|
*/
|
||||||
void Write(Word addr, Byte val);
|
void Write(Word addr, Byte val);
|
||||||
|
|
||||||
private: // Registers
|
private: // Registers
|
||||||
|
@ -81,8 +115,10 @@ private: // Registers
|
||||||
|
|
||||||
Address ppuaddr;
|
Address ppuaddr;
|
||||||
|
|
||||||
|
// Current x, y position the PPU operates on
|
||||||
uint16_t x, y;
|
uint16_t x, y;
|
||||||
Byte addressLatch = 0;
|
Byte latch = 0;
|
||||||
|
bool addressLatch = false;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool isFrameDone = false;
|
bool isFrameDone = false;
|
||||||
|
|
Loading…
Reference in a new issue