1 //////////////////////////////////////////////////////////////////////////////////////// 2 // 3 // Nestopia - NES/Famicom emulator written in C++ 4 // 5 // Copyright (C) 2003-2008 Martin Freij 6 // 7 // This file is part of Nestopia. 8 // 9 // Nestopia is free software; you can redistribute it and/or modify 10 // it under the terms of the GNU General Public License as published by 11 // the Free Software Foundation; either version 2 of the License, or 12 // (at your option) any later version. 13 // 14 // Nestopia is distributed in the hope that it will be useful, 15 // but WITHOUT ANY WARRANTY; without even the implied warranty of 16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 // GNU General Public License for more details. 18 // 19 // You should have received a copy of the GNU General Public License 20 // along with Nestopia; if not, write to the Free Software 21 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22 // 23 //////////////////////////////////////////////////////////////////////////////////////// 24 25 #ifndef NST_PPU_H 26 #define NST_PPU_H 27 28 #ifndef NST_IO_PORT_H 29 #include "NstIoPort.hpp" 30 #endif 31 32 #include "NstIoAccessor.hpp" 33 #include "NstIoLine.hpp" 34 #include "NstHook.hpp" 35 #include "NstMemory.hpp" 36 #include "NstVideoScreen.hpp" 37 38 #ifdef NST_PRAGMA_ONCE 39 #pragma once 40 #endif 41 42 namespace Nes 43 { 44 namespace Core 45 { 46 class Ppu 47 { 48 public: 49 50 explicit Ppu(Cpu&); 51 52 void Reset(bool,bool); 53 void PowerOff(); 54 void BeginFrame(bool); 55 void EndFrame(); 56 57 enum 58 { 59 SCANLINE_HDUMMY = -1, 60 SCANLINE_VBLANK = 240 61 }; 62 63 enum NmtMirroring 64 { 65 NMT_H = 0xC, 66 NMT_V = 0xA, 67 NMT_0 = 0x0, 68 NMT_1 = 0xF 69 }; 70 71 void SetModel(PpuModel,bool); 72 void SetMirroring(NmtMirroring); 73 void SetMirroring(const byte (&)[4]); 74 uint SetAddressLineHook(const Core::Io::Line&); 75 void SetHActiveHook(const Hook&); 76 void SetHBlankHook(const Hook&); 77 uint GetPixelCycles() const; 78 void EnableCpuSynchronization(); 79 80 void LoadState(State::Loader&); 81 void SaveState(State::Saver&,dword) const; 82 83 class ChrMem : public Memory<SIZE_8K,SIZE_1K,2> 84 { 85 NES_DECL_ACCESSOR( Pattern ); 86 87 protected: 88 89 Io::Accessor accessor; 90 91 public: 92 93 void ResetAccessor(); 94 95 template<typename T,typename U> SetAccessor(T t,U u)96 void SetAccessor(T t,U u) 97 { 98 accessor.Set( t, u ); 99 } 100 }; 101 102 class NmtMem : public Memory<SIZE_4K,SIZE_1K,2> 103 { 104 NES_DECL_ACCESSOR( Name_2000 ); 105 NES_DECL_ACCESSOR( Name_2400 ); 106 NES_DECL_ACCESSOR( Name_2800 ); 107 NES_DECL_ACCESSOR( Name_2C00 ); 108 109 protected: 110 111 Io::Accessor accessors[4]; 112 113 public: 114 115 void ResetAccessors(); 116 117 template<typename T,typename U,typename V,typename W,typename X> SetAccessors(T t,U u,V v,W w,X x)118 void SetAccessors(T t,U u,V v,W w,X x) 119 { 120 accessors[0].Set( t, u ); 121 accessors[1].Set( t, v ); 122 accessors[2].Set( t, w ); 123 accessors[3].Set( t, x ); 124 } 125 }; 126 127 private: 128 129 struct Chr : ChrMem 130 { 131 NST_FORCE_INLINE uint FetchPattern(uint) const; 132 }; 133 134 struct Nmt : NmtMem 135 { 136 NST_FORCE_INLINE uint FetchName(uint) const; 137 NST_FORCE_INLINE uint FetchAttribute(uint) const; 138 }; 139 140 enum 141 { 142 HCLOCK_DUMMY = 341, 143 HCLOCK_VBLANK_0 = 681, 144 HCLOCK_VBLANK_1 = 682, 145 HCLOCK_VBLANK_2 = 684, 146 HCLOCK_BOOT = 685 147 }; 148 149 NES_DECL_POKE( 2000 ); 150 NES_DECL_PEEK( 2002 ); 151 NES_DECL_PEEK( 2002_RC2C05_01_04 ); 152 NES_DECL_PEEK( 2002_RC2C05_02 ); 153 NES_DECL_PEEK( 2002_RC2C05_03 ); 154 NES_DECL_POKE( 2001 ); 155 NES_DECL_POKE( 2003 ); 156 NES_DECL_PEEK( 2004 ); 157 NES_DECL_POKE( 2004 ); 158 NES_DECL_POKE( 2005 ); 159 NES_DECL_POKE( 2006 ); 160 NES_DECL_PEEK( 2007 ); 161 NES_DECL_POKE( 2007 ); 162 NES_DECL_PEEK( 2xxx ); 163 NES_DECL_POKE( 2xxx ); 164 NES_DECL_PEEK( 3000 ); 165 NES_DECL_PEEK( 4014 ); 166 NES_DECL_POKE( 4014 ); 167 168 NES_DECL_HOOK( Sync ); 169 170 NST_FORCE_INLINE Cycle GetCycles() const; 171 NST_FORCE_INLINE Cycle GetLocalCycles(Cycle) const; 172 173 NST_FORCE_INLINE bool IsDead() const; 174 NST_FORCE_INLINE uint Coloring() const; 175 NST_FORCE_INLINE uint Emphasis() const; 176 177 NST_FORCE_INLINE void UpdateAddressLine(uint); 178 NST_FORCE_INLINE void UpdateScrollAddressLine(); 179 NST_FORCE_INLINE void UpdateVramAddress(); 180 181 NST_FORCE_INLINE void OpenName(); 182 NST_FORCE_INLINE void FetchName(); 183 NST_FORCE_INLINE void OpenAttribute(); 184 NST_FORCE_INLINE void FetchAttribute(); 185 NST_FORCE_INLINE void OpenPattern(uint); 186 NST_FORCE_INLINE uint FetchSpPattern() const; 187 NST_FORCE_INLINE void FetchBgPattern0(); 188 NST_FORCE_INLINE void FetchBgPattern1(); 189 190 NST_FORCE_INLINE void EvaluateSpritesEven(); 191 NST_FORCE_INLINE void EvaluateSpritesOdd(); 192 193 void EvaluateSpritesPhase0(); 194 void EvaluateSpritesPhase1(); 195 void EvaluateSpritesPhase2(); 196 void EvaluateSpritesPhase3(); 197 void EvaluateSpritesPhase4(); 198 void EvaluateSpritesPhase5(); 199 void EvaluateSpritesPhase6(); 200 void EvaluateSpritesPhase7(); 201 void EvaluateSpritesPhase8(); 202 void EvaluateSpritesPhase9(); 203 204 void Reset(bool,bool,bool); 205 void Update(Cycle,uint=0); 206 void UpdateStates(); 207 void UpdatePalette(); 208 void LoadExtendedSprites(); 209 210 NST_FORCE_INLINE uint OpenSprite() const; 211 NST_FORCE_INLINE uint OpenSprite(const byte* NST_RESTRICT) const; 212 NST_FORCE_INLINE void LoadSprite(uint,uint,const byte* NST_RESTRICT); 213 NST_SINGLE_CALL void PreLoadTiles(); 214 NST_SINGLE_CALL void LoadTiles(); 215 NST_FORCE_INLINE void RenderPixel(); 216 NST_SINGLE_CALL void RenderPixel255(); 217 NST_NO_INLINE void Run(); 218 219 struct Regs 220 { 221 enum 222 { 223 CTRL0_NAME_OFFSET = 0x03, 224 CTRL0_INC32 = 0x04, 225 CTRL0_SP_OFFSET = 0x08, 226 CTRL0_BG_OFFSET = 0x10, 227 CTRL0_SP8X16 = 0x20, 228 CTRL0_NMI = 0x80, 229 CTRL0_NMI_OCCUR = 0x100, 230 CTRL1_MONOCHROME = 0x01, 231 CTRL1_BG_NO_CLIP = 0x02, 232 CTRL1_SP_NO_CLIP = 0x04, 233 CTRL1_BG_ENABLED = 0x08, 234 CTRL1_SP_ENABLED = 0x10, 235 CTRL1_BG_ENABLED_NO_CLIP = CTRL1_BG_ENABLED|CTRL1_BG_NO_CLIP, 236 CTRL1_SP_ENABLED_NO_CLIP = CTRL1_SP_ENABLED|CTRL1_SP_NO_CLIP, 237 CTRL1_BG_SP_ENABLED = CTRL1_BG_ENABLED|CTRL1_SP_ENABLED, 238 CTRL1_EMPHASIS = 0xE0, 239 STATUS_LATCH = 0x1F, 240 STATUS_SP_OVERFLOW = 0x20, 241 STATUS_SP_ZERO_HIT = 0x40, 242 STATUS_VBLANK = 0x80, 243 STATUS_BITS = STATUS_SP_OVERFLOW|STATUS_SP_ZERO_HIT|STATUS_VBLANK, 244 STATUS_VBLANKING = 0x100, 245 FRAME_ODD = CTRL1_BG_ENABLED|CTRL1_SP_ENABLED 246 }; 247 248 uint ctrl[2]; 249 uint status; 250 uint frame; 251 uint oam; 252 }; 253 254 struct Scroll 255 { 256 enum 257 { 258 X_TILE = 0x001F, 259 Y_TILE = 0x03E0, 260 Y_FINE = 0x7000, 261 LOW = 0x00FF, 262 HIGH = 0xFF00, 263 NAME = 0x0C00, 264 NAME_LOW = 0x0400, 265 NAME_HIGH = 0x0800 266 }; 267 268 NST_FORCE_INLINE void ClockX(); 269 NST_SINGLE_CALL void ResetX(); 270 NST_SINGLE_CALL void ClockY(); 271 272 uint address; 273 uint toggle; 274 uint latch; 275 uint xFine; 276 }; 277 278 struct Tiles 279 { 280 Tiles(); 281 282 byte pattern[2]; 283 byte attribute; 284 byte index; 285 byte pixels[16]; 286 uint mask; 287 byte show[2]; 288 const byte padding0; 289 const byte padding1; 290 }; 291 292 struct Palette 293 { 294 enum 295 { 296 SIZE = 0x20, 297 COLORS = 0x40, 298 SPRITE_OFFSET = 0x10, 299 COLOR = 0x3F, 300 MONO = 0x30 301 }; 302 303 byte ram[SIZE]; 304 }; 305 306 struct Output 307 { 308 explicit Output(Video::Screen::Pixel*); 309 310 Video::Screen::Pixel* target; 311 Video::Screen::Pixel* pixels; 312 uint burstPhase; 313 word palette[Palette::SIZE]; 314 uint bgColor; 315 }; 316 317 struct Oam 318 { 319 Oam(); 320 321 enum 322 { 323 SIZE = 0x100, 324 OFFSET_TO_0_1 = 0xF8, 325 STD_LINE_SPRITES = 8, 326 MAX_LINE_SPRITES = 32, 327 GARBAGE = 0xFF, 328 COLOR = 0x03, 329 BEHIND = 0x20, 330 X_FLIP = 0x40, 331 Y_FLIP = 0x80, 332 XFINE = 0x07, 333 RANGE_MSB = 0x08, 334 TILE_LSB = 0x01 335 }; 336 337 struct Output 338 { 339 byte x; 340 byte behind; 341 byte zero; 342 byte palette; 343 byte pixels[8]; 344 }; 345 346 typedef void (Ppu::*Phase)(); 347 348 const byte* limit; 349 Output* visible; 350 Phase phase; 351 uint latch; 352 uint index; 353 byte* buffered; 354 uint address; 355 uint height; 356 uint mask; 357 byte show[2]; 358 bool spriteZeroInLine; 359 bool spriteLimit; 360 361 byte ram[0x100]; 362 byte buffer[MAX_LINE_SPRITES*4]; 363 364 Output output[MAX_LINE_SPRITES]; 365 }; 366 367 struct NameTable 368 { 369 enum 370 { 371 SIZE = SIZE_2K, 372 GARBAGE = 0xFF 373 }; 374 375 byte ram[SIZE]; 376 }; 377 378 struct TileLut 379 { 380 TileLut(); 381 382 byte block[0x400][4]; 383 }; 384 385 struct Io 386 { 387 enum 388 { 389 BUFFER_GARBAGE = 0xE8 390 }; 391 392 uint address; 393 uint pattern; 394 uint latch; 395 uint buffer; 396 Core::Io::Line line; 397 }; 398 399 Cpu& cpu; 400 401 struct 402 { 403 Cycle count; 404 Cycle hClock; 405 Cycle vClock; 406 uint one; 407 Cycle reset; 408 } cycles; 409 410 Io io; 411 Regs regs; 412 Scroll scroll; 413 Tiles tiles; 414 Chr chr; 415 Nmt nmt; 416 int scanline; 417 int scanline_sleep; 418 public: 419 Output output; 420 private: 421 PpuModel model; 422 Hook hActiveHook; 423 Hook hBlankHook; 424 const byte* rgbMap; 425 const byte* yuvMap; 426 Oam oam; 427 Palette palette; 428 NameTable nameTable; 429 const TileLut tileLut; 430 Video::Screen screen; 431 432 static const byte yuvMaps[4][0x40]; 433 434 public: 435 Update()436 void Update() 437 { 438 Update(0); 439 } 440 GetModel() const441 PpuModel GetModel() const 442 { 443 return model; 444 } 445 IsEnabled() const446 ibool IsEnabled() const 447 { 448 return regs.ctrl[1] & Regs::CTRL1_BG_SP_ENABLED; 449 } 450 GetScanline() const451 int GetScanline() const 452 { 453 return scanline; 454 } 455 GetCtrl(uint i) const456 uint GetCtrl(uint i) const 457 { 458 NST_ASSERT( i < 2 ); 459 return regs.ctrl[i]; 460 } 461 GetScreen()462 Video::Screen& GetScreen() 463 { 464 return screen; 465 } 466 GetOutputPixels()467 Video::Screen::Pixel* GetOutputPixels() 468 { 469 return output.pixels; 470 } 471 SetOutputPixels(Video::Screen::Pixel * pixels)472 void SetOutputPixels(Video::Screen::Pixel* pixels) 473 { 474 NST_ASSERT( pixels ); 475 output.pixels = pixels; 476 } 477 GetPalette() const478 const Palette& GetPalette() const 479 { 480 return palette; 481 } 482 GetPixel(uint i) const483 uint GetPixel(uint i) const 484 { 485 NST_ASSERT( i < Video::Screen::PIXELS ); 486 return output.pixels[i]; 487 } 488 GetYuvColor(uint i) const489 uint GetYuvColor(uint i) const 490 { 491 NST_ASSERT( i < Palette::COLORS ); 492 return yuvMap ? yuvMap[i] : i; 493 } 494 GetChrMem()495 ChrMem& GetChrMem() 496 { 497 return chr; 498 } 499 GetNmtMem()500 NmtMem& GetNmtMem() 501 { 502 return nmt; 503 } 504 GetClock(dword count=1) const505 Cycle GetClock(dword count=1) const 506 { 507 NST_ASSERT( count ); 508 return cycles.one * count; 509 } 510 GetHSyncClock() const511 Cycle GetHSyncClock() const 512 { 513 return model == PPU_RP2C07 ? PPU_RP2C07_HSYNC : model == PPU_DENDY ? PPU_DENDY_HSYNC : PPU_RP2C02_HSYNC; 514 } 515 GetHVIntClock() const516 Cycle GetHVIntClock() const 517 { 518 return model == PPU_RP2C07 ? PPU_RP2C07_HVINT : model == PPU_DENDY ? PPU_DENDY_HVINT : PPU_RP2C02_HVINT; 519 } 520 GetAddressLineHook() const521 const Core::Io::Line& GetAddressLineHook() const 522 { 523 return io.line; 524 } 525 IsShortFrame() const526 bool IsShortFrame() const 527 { 528 return regs.ctrl[1] & regs.frame; 529 } 530 GetBurstPhase() const531 uint GetBurstPhase() const 532 { 533 return output.burstPhase; 534 } 535 EnableSpriteLimit(bool enable)536 void EnableSpriteLimit(bool enable) 537 { 538 oam.spriteLimit = enable; 539 } 540 HasSpriteLimit() const541 bool HasSpriteLimit() const 542 { 543 return oam.spriteLimit; 544 } 545 }; 546 } 547 } 548 549 #endif 550