mirror of
https://github.com/0ldsk00l/nestopia.git
synced 2025-04-02 10:31:51 -04:00
556 lines
12 KiB
C++
556 lines
12 KiB
C++
////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Nestopia - NES/Famicom emulator written in C++
|
|
//
|
|
// Copyright (C) 2003-2008 Martin Freij
|
|
//
|
|
// This file is part of Nestopia.
|
|
//
|
|
// Nestopia is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation; either version 2 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Nestopia is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Nestopia; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifndef NST_PPU_H
|
|
#define NST_PPU_H
|
|
|
|
#ifndef NST_IO_PORT_H
|
|
#include "NstIoPort.hpp"
|
|
#endif
|
|
|
|
#include "NstIoAccessor.hpp"
|
|
#include "NstIoLine.hpp"
|
|
#include "NstHook.hpp"
|
|
#include "NstMemory.hpp"
|
|
#include "NstVideoScreen.hpp"
|
|
|
|
#ifdef NST_PRAGMA_ONCE
|
|
#pragma once
|
|
#endif
|
|
|
|
namespace Nes
|
|
{
|
|
namespace Core
|
|
{
|
|
class Ppu
|
|
{
|
|
public:
|
|
|
|
explicit Ppu(Cpu&);
|
|
|
|
void Reset(bool,bool);
|
|
void PowerOff();
|
|
void BeginFrame(bool);
|
|
void EndFrame();
|
|
|
|
enum
|
|
{
|
|
SCANLINE_HDUMMY = -1,
|
|
SCANLINE_VBLANK = 240
|
|
};
|
|
|
|
enum NmtMirroring
|
|
{
|
|
NMT_H = 0xC,
|
|
NMT_V = 0xA,
|
|
NMT_0 = 0x0,
|
|
NMT_1 = 0xF
|
|
};
|
|
|
|
void SetModel(PpuModel,bool);
|
|
void SetMirroring(NmtMirroring);
|
|
void SetMirroring(const byte (&)[4]);
|
|
uint SetAddressLineHook(const Core::Io::Line&);
|
|
void SetHActiveHook(const Hook&);
|
|
void SetHBlankHook(const Hook&);
|
|
uint GetPixelCycles() const;
|
|
void EnableCpuSynchronization();
|
|
|
|
void LoadState(State::Loader&);
|
|
void SaveState(State::Saver&,dword) const;
|
|
|
|
class ChrMem : public Memory<SIZE_8K,SIZE_1K,2>
|
|
{
|
|
NES_DECL_ACCESSOR( Pattern );
|
|
|
|
protected:
|
|
|
|
Io::Accessor accessor;
|
|
|
|
public:
|
|
|
|
void ResetAccessor();
|
|
|
|
template<typename T,typename U>
|
|
void SetAccessor(T t,U u)
|
|
{
|
|
accessor.Set( t, u );
|
|
}
|
|
};
|
|
|
|
class NmtMem : public Memory<SIZE_4K,SIZE_1K,2>
|
|
{
|
|
NES_DECL_ACCESSOR( Name_2000 );
|
|
NES_DECL_ACCESSOR( Name_2400 );
|
|
NES_DECL_ACCESSOR( Name_2800 );
|
|
NES_DECL_ACCESSOR( Name_2C00 );
|
|
|
|
protected:
|
|
|
|
Io::Accessor accessors[4];
|
|
|
|
public:
|
|
|
|
void ResetAccessors();
|
|
|
|
template<typename T,typename U,typename V,typename W,typename X>
|
|
void SetAccessors(T t,U u,V v,W w,X x)
|
|
{
|
|
accessors[0].Set( t, u );
|
|
accessors[1].Set( t, v );
|
|
accessors[2].Set( t, w );
|
|
accessors[3].Set( t, x );
|
|
}
|
|
};
|
|
|
|
private:
|
|
|
|
struct Chr : ChrMem
|
|
{
|
|
NST_FORCE_INLINE uint FetchPattern(uint) const;
|
|
};
|
|
|
|
struct Nmt : NmtMem
|
|
{
|
|
NST_FORCE_INLINE uint FetchName(uint) const;
|
|
NST_FORCE_INLINE uint FetchAttribute(uint) const;
|
|
};
|
|
|
|
enum
|
|
{
|
|
HCLOCK_DUMMY = 341,
|
|
HCLOCK_VBLANK_0 = 681,
|
|
HCLOCK_VBLANK_1 = 682,
|
|
HCLOCK_VBLANK_2 = 684,
|
|
HCLOCK_BOOT = 685
|
|
};
|
|
|
|
NES_DECL_POKE( 2000 );
|
|
NES_DECL_PEEK( 2002 );
|
|
NES_DECL_PEEK( 2002_RC2C05_01_04 );
|
|
NES_DECL_PEEK( 2002_RC2C05_02 );
|
|
NES_DECL_PEEK( 2002_RC2C05_03 );
|
|
NES_DECL_POKE( 2001 );
|
|
NES_DECL_POKE( 2003 );
|
|
NES_DECL_PEEK( 2004 );
|
|
NES_DECL_POKE( 2004 );
|
|
NES_DECL_POKE( 2005 );
|
|
NES_DECL_POKE( 2006 );
|
|
NES_DECL_PEEK( 2007 );
|
|
NES_DECL_POKE( 2007 );
|
|
NES_DECL_PEEK( 2xxx );
|
|
NES_DECL_POKE( 2xxx );
|
|
NES_DECL_PEEK( 3000 );
|
|
NES_DECL_PEEK( 4014 );
|
|
NES_DECL_POKE( 4014 );
|
|
|
|
NES_DECL_HOOK( Sync );
|
|
|
|
NST_FORCE_INLINE Cycle GetCycles() const;
|
|
NST_FORCE_INLINE Cycle GetLocalCycles(Cycle) const;
|
|
|
|
NST_FORCE_INLINE bool IsDead() const;
|
|
NST_FORCE_INLINE uint Coloring() const;
|
|
NST_FORCE_INLINE uint Emphasis() const;
|
|
|
|
NST_FORCE_INLINE void UpdateAddressLine(uint);
|
|
NST_FORCE_INLINE void UpdateScrollAddressLine();
|
|
NST_FORCE_INLINE void UpdateVramAddress();
|
|
|
|
NST_FORCE_INLINE void OpenName();
|
|
NST_FORCE_INLINE void FetchName();
|
|
NST_FORCE_INLINE void OpenAttribute();
|
|
NST_FORCE_INLINE void FetchAttribute();
|
|
NST_FORCE_INLINE void OpenPattern(uint);
|
|
NST_FORCE_INLINE uint FetchSpPattern() const;
|
|
NST_FORCE_INLINE void FetchBgPattern0();
|
|
NST_FORCE_INLINE void FetchBgPattern1();
|
|
NST_FORCE_INLINE void UpdateDecay(byte);
|
|
|
|
NST_FORCE_INLINE void EvaluateSpritesEven();
|
|
NST_FORCE_INLINE void EvaluateSpritesOdd();
|
|
|
|
void EvaluateSpritesPhase0();
|
|
void EvaluateSpritesPhase1();
|
|
void EvaluateSpritesPhase2();
|
|
void EvaluateSpritesPhase3();
|
|
void EvaluateSpritesPhase4();
|
|
void EvaluateSpritesPhase5();
|
|
void EvaluateSpritesPhase6();
|
|
void EvaluateSpritesPhase7();
|
|
void EvaluateSpritesPhase8();
|
|
void EvaluateSpritesPhase9();
|
|
|
|
void Reset(bool,bool,bool);
|
|
void Update(Cycle,uint=0);
|
|
void UpdateStates();
|
|
void UpdatePalette();
|
|
void LoadExtendedSprites();
|
|
|
|
NST_FORCE_INLINE uint OpenSprite() const;
|
|
NST_FORCE_INLINE uint OpenSprite(const byte* NST_RESTRICT) const;
|
|
NST_FORCE_INLINE void LoadSprite(uint,uint,const byte* NST_RESTRICT);
|
|
NST_SINGLE_CALL void PreLoadTiles();
|
|
NST_SINGLE_CALL void LoadTiles();
|
|
NST_FORCE_INLINE void RenderPixel();
|
|
NST_SINGLE_CALL void RenderPixel255();
|
|
NST_NO_INLINE void Run();
|
|
|
|
struct Regs
|
|
{
|
|
enum
|
|
{
|
|
CTRL0_NAME_OFFSET = 0x03,
|
|
CTRL0_INC32 = 0x04,
|
|
CTRL0_SP_OFFSET = 0x08,
|
|
CTRL0_BG_OFFSET = 0x10,
|
|
CTRL0_SP8X16 = 0x20,
|
|
CTRL0_NMI = 0x80,
|
|
CTRL0_NMI_OCCUR = 0x100,
|
|
CTRL1_MONOCHROME = 0x01,
|
|
CTRL1_BG_NO_CLIP = 0x02,
|
|
CTRL1_SP_NO_CLIP = 0x04,
|
|
CTRL1_BG_ENABLED = 0x08,
|
|
CTRL1_SP_ENABLED = 0x10,
|
|
CTRL1_BG_ENABLED_NO_CLIP = CTRL1_BG_ENABLED|CTRL1_BG_NO_CLIP,
|
|
CTRL1_SP_ENABLED_NO_CLIP = CTRL1_SP_ENABLED|CTRL1_SP_NO_CLIP,
|
|
CTRL1_BG_SP_ENABLED = CTRL1_BG_ENABLED|CTRL1_SP_ENABLED,
|
|
CTRL1_EMPHASIS = 0xE0,
|
|
STATUS_LATCH = 0x1F,
|
|
STATUS_SP_OVERFLOW = 0x20,
|
|
STATUS_SP_ZERO_HIT = 0x40,
|
|
STATUS_VBLANK = 0x80,
|
|
STATUS_BITS = STATUS_SP_OVERFLOW|STATUS_SP_ZERO_HIT|STATUS_VBLANK,
|
|
STATUS_VBLANKING = 0x100,
|
|
FRAME_ODD = CTRL1_BG_ENABLED|CTRL1_SP_ENABLED
|
|
};
|
|
|
|
uint ctrl[2];
|
|
uint status;
|
|
uint frame;
|
|
uint oam;
|
|
};
|
|
|
|
struct Scroll
|
|
{
|
|
enum
|
|
{
|
|
X_TILE = 0x001F,
|
|
Y_TILE = 0x03E0,
|
|
Y_FINE = 0x7000,
|
|
LOW = 0x00FF,
|
|
HIGH = 0xFF00,
|
|
NAME = 0x0C00,
|
|
NAME_LOW = 0x0400,
|
|
NAME_HIGH = 0x0800
|
|
};
|
|
|
|
NST_FORCE_INLINE void ClockX();
|
|
NST_SINGLE_CALL void ResetX();
|
|
NST_SINGLE_CALL void ClockY();
|
|
|
|
uint address;
|
|
uint toggle;
|
|
uint latch;
|
|
uint xFine;
|
|
};
|
|
|
|
struct Tiles
|
|
{
|
|
Tiles();
|
|
|
|
byte pattern[2];
|
|
byte attribute;
|
|
byte index;
|
|
byte pixels[16];
|
|
uint mask;
|
|
byte show[2];
|
|
const byte padding0;
|
|
const byte padding1;
|
|
};
|
|
|
|
struct Palette
|
|
{
|
|
enum
|
|
{
|
|
SIZE = 0x20,
|
|
COLORS = 0x40,
|
|
SPRITE_OFFSET = 0x10,
|
|
COLOR = 0x3F,
|
|
MONO = 0x30
|
|
};
|
|
|
|
byte ram[SIZE];
|
|
};
|
|
|
|
struct Output
|
|
{
|
|
explicit Output(Video::Screen::Pixel*);
|
|
|
|
Video::Screen::Pixel* target;
|
|
Video::Screen::Pixel* pixels;
|
|
uint burstPhase;
|
|
word palette[Palette::SIZE];
|
|
uint bgColor;
|
|
};
|
|
|
|
struct Oam
|
|
{
|
|
Oam();
|
|
|
|
enum
|
|
{
|
|
SIZE = 0x100,
|
|
OFFSET_TO_0_1 = 0xF8,
|
|
STD_LINE_SPRITES = 8,
|
|
MAX_LINE_SPRITES = 32,
|
|
GARBAGE = 0xFF,
|
|
COLOR = 0x03,
|
|
BEHIND = 0x20,
|
|
X_FLIP = 0x40,
|
|
Y_FLIP = 0x80,
|
|
XFINE = 0x07,
|
|
RANGE_MSB = 0x08,
|
|
TILE_LSB = 0x01
|
|
};
|
|
|
|
struct Output
|
|
{
|
|
byte x;
|
|
byte behind;
|
|
byte zero;
|
|
byte palette;
|
|
byte pixels[8];
|
|
};
|
|
|
|
typedef void (Ppu::*Phase)();
|
|
|
|
const byte* limit;
|
|
Output* visible;
|
|
Phase phase;
|
|
uint latch;
|
|
uint index;
|
|
byte* buffered;
|
|
uint address;
|
|
uint height;
|
|
uint mask;
|
|
byte show[2];
|
|
bool spriteZeroInLine;
|
|
bool spriteLimit;
|
|
|
|
byte ram[0x100];
|
|
byte buffer[MAX_LINE_SPRITES*4];
|
|
|
|
Output output[MAX_LINE_SPRITES];
|
|
};
|
|
|
|
struct NameTable
|
|
{
|
|
enum
|
|
{
|
|
SIZE = SIZE_2K,
|
|
GARBAGE = 0xFF
|
|
};
|
|
|
|
byte ram[SIZE];
|
|
};
|
|
|
|
struct TileLut
|
|
{
|
|
TileLut();
|
|
|
|
byte block[0x400][4];
|
|
};
|
|
|
|
struct Io
|
|
{
|
|
enum
|
|
{
|
|
BUFFER_GARBAGE = 0xE8
|
|
};
|
|
|
|
uint address;
|
|
uint pattern;
|
|
uint latch;
|
|
uint buffer;
|
|
Core::Io::Line line;
|
|
};
|
|
|
|
Cpu& cpu;
|
|
|
|
struct
|
|
{
|
|
Cycle count;
|
|
Cycle hClock;
|
|
Cycle vClock;
|
|
uint one;
|
|
Cycle reset;
|
|
} cycles;
|
|
|
|
struct
|
|
{
|
|
Cycle timestamp[8];
|
|
Cycle rd2007;
|
|
} decay;
|
|
|
|
Io io;
|
|
Regs regs;
|
|
Scroll scroll;
|
|
Tiles tiles;
|
|
Chr chr;
|
|
Nmt nmt;
|
|
int scanline;
|
|
int scanline_sleep;
|
|
public:
|
|
Output output;
|
|
private:
|
|
PpuModel model;
|
|
Hook hActiveHook;
|
|
Hook hBlankHook;
|
|
const byte* rgbMap;
|
|
const byte* yuvMap;
|
|
Oam oam;
|
|
Palette palette;
|
|
NameTable nameTable;
|
|
const TileLut tileLut;
|
|
Video::Screen screen;
|
|
|
|
static const byte yuvMaps[4][0x40];
|
|
|
|
public:
|
|
|
|
void Update()
|
|
{
|
|
Update(0);
|
|
}
|
|
|
|
PpuModel GetModel() const
|
|
{
|
|
return model;
|
|
}
|
|
|
|
ibool IsEnabled() const
|
|
{
|
|
return regs.ctrl[1] & Regs::CTRL1_BG_SP_ENABLED;
|
|
}
|
|
|
|
int GetScanline() const
|
|
{
|
|
return scanline;
|
|
}
|
|
|
|
uint GetCtrl(uint i) const
|
|
{
|
|
NST_ASSERT( i < 2 );
|
|
return regs.ctrl[i];
|
|
}
|
|
|
|
Video::Screen& GetScreen()
|
|
{
|
|
return screen;
|
|
}
|
|
|
|
Video::Screen::Pixel* GetOutputPixels()
|
|
{
|
|
return output.pixels;
|
|
}
|
|
|
|
void SetOutputPixels(Video::Screen::Pixel* pixels)
|
|
{
|
|
NST_ASSERT( pixels );
|
|
output.pixels = pixels;
|
|
}
|
|
|
|
const Palette& GetPalette() const
|
|
{
|
|
return palette;
|
|
}
|
|
|
|
uint GetPixel(uint i) const
|
|
{
|
|
NST_ASSERT( i < Video::Screen::PIXELS );
|
|
return output.pixels[i];
|
|
}
|
|
|
|
uint GetYuvColor(uint i) const
|
|
{
|
|
NST_ASSERT( i < Palette::COLORS );
|
|
return yuvMap ? yuvMap[i] : i;
|
|
}
|
|
|
|
ChrMem& GetChrMem()
|
|
{
|
|
return chr;
|
|
}
|
|
|
|
NmtMem& GetNmtMem()
|
|
{
|
|
return nmt;
|
|
}
|
|
|
|
Cycle GetClock(dword count=1) const
|
|
{
|
|
NST_ASSERT( count );
|
|
return cycles.one * count;
|
|
}
|
|
|
|
Cycle GetHSyncClock() const
|
|
{
|
|
return model == PPU_RP2C07 ? PPU_RP2C07_HSYNC : model == PPU_DENDY ? PPU_DENDY_HSYNC : PPU_RP2C02_HSYNC;
|
|
}
|
|
|
|
Cycle GetHVIntClock() const
|
|
{
|
|
return model == PPU_RP2C07 ? PPU_RP2C07_HVINT : model == PPU_DENDY ? PPU_DENDY_HVINT : PPU_RP2C02_HVINT;
|
|
}
|
|
|
|
const Core::Io::Line& GetAddressLineHook() const
|
|
{
|
|
return io.line;
|
|
}
|
|
|
|
bool IsShortFrame() const
|
|
{
|
|
return regs.ctrl[1] & regs.frame;
|
|
}
|
|
|
|
uint GetBurstPhase() const
|
|
{
|
|
return output.burstPhase;
|
|
}
|
|
|
|
void EnableSpriteLimit(bool enable)
|
|
{
|
|
oam.spriteLimit = enable;
|
|
}
|
|
|
|
bool HasSpriteLimit() const
|
|
{
|
|
return oam.spriteLimit;
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
#endif
|