//////////////////////////////////////////////////////////////////////////////////////// // // 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 { NES_DECL_ACCESSOR( Pattern ); protected: Io::Accessor accessor; public: void ResetAccessor(); template void SetAccessor(T t,U u) { accessor.Set( t, u ); } }; class NmtMem : public Memory { 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 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