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_POSTRENDER = 340,
143 				HCLOCK_DUMMY    = 341,
144 				HCLOCK_VBLANK_0 = 681,
145 				HCLOCK_VBLANK_1 = 682,
146 				HCLOCK_VBLANK_2 = 684,
147 				HCLOCK_BOOT     = 685
148 			};
149 
150 			NES_DECL_POKE( 2000 );
151 			NES_DECL_PEEK( 2002 );
152 			NES_DECL_PEEK( 2002_RC2C05_01_04 );
153 			NES_DECL_PEEK( 2002_RC2C05_02 );
154 			NES_DECL_PEEK( 2002_RC2C05_03 );
155 			NES_DECL_POKE( 2001 );
156 			NES_DECL_POKE( 2003 );
157 			NES_DECL_PEEK( 2004 );
158 			NES_DECL_POKE( 2004 );
159 			NES_DECL_POKE( 2005 );
160 			NES_DECL_POKE( 2006 );
161 			NES_DECL_PEEK( 2007 );
162 			NES_DECL_POKE( 2007 );
163 			NES_DECL_PEEK( 2xxx );
164 			NES_DECL_POKE( 2xxx );
165 			NES_DECL_PEEK( 3000 );
166 			NES_DECL_PEEK( 4014 );
167 			NES_DECL_POKE( 4014 );
168 
169 			NES_DECL_HOOK( Sync );
170 
171 			NST_FORCE_INLINE Cycle GetCycles() const;
172 			NST_FORCE_INLINE Cycle GetLocalCycles(Cycle) const;
173 
174 			NST_FORCE_INLINE bool IsDead() const;
175 			NST_FORCE_INLINE uint Coloring() const;
176 			NST_FORCE_INLINE uint Emphasis() const;
177 
178 			NST_FORCE_INLINE void UpdateAddressLine(uint);
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 				byte ram[0x100];
349 				byte buffer[MAX_LINE_SPRITES * 4];
350 
351 				const byte* limit;
352 				Output* visible;
353 				Phase phase;
354 				uint latch;
355 				uint index;
356 				byte* buffered;
357 				uint address;
358 				uint height;
359 				uint mask;
360 				byte show[2];
361 				bool spriteZeroInLine;
362 				bool spriteLimit;
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 			int ssleep;
419 			bool overclocked;
420 
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 			Output output;
436 
Update()437 			void Update()
438 			{
439 				Update(0);
440 			}
441 
GetModel() const442 			PpuModel GetModel() const
443 			{
444 				return model;
445 			}
446 
IsEnabled() const447 			ibool IsEnabled() const
448 			{
449 				return regs.ctrl[1] & Regs::CTRL1_BG_SP_ENABLED;
450 			}
451 
GetScanline() const452 			int GetScanline() const
453 			{
454 				return scanline;
455 			}
456 
GetCtrl(uint i) const457 			uint GetCtrl(uint i) const
458 			{
459 				NST_ASSERT( i < 2 );
460 				return regs.ctrl[i];
461 			}
462 
GetScreen()463 			Video::Screen& GetScreen()
464 			{
465 				return screen;
466 			}
467 
GetOutputPixels()468 			Video::Screen::Pixel* GetOutputPixels()
469 			{
470 				return output.pixels;
471 			}
472 
SetOutputPixels(Video::Screen::Pixel * pixels)473 			void SetOutputPixels(Video::Screen::Pixel* pixels)
474 			{
475 				NST_ASSERT( pixels );
476 				output.pixels = pixels;
477 			}
478 
GetPalette() const479 			const Palette& GetPalette() const
480 			{
481 				return palette;
482 			}
483 
GetPixel(uint i) const484 			uint GetPixel(uint i) const
485 			{
486 				NST_ASSERT( i < Video::Screen::PIXELS );
487 				return output.pixels[i];
488 			}
489 
GetYuvColor(uint i) const490 			uint GetYuvColor(uint i) const
491 			{
492 				NST_ASSERT( i < Palette::COLORS );
493 				return yuvMap ? yuvMap[i] : i;
494 			}
495 
GetChrMem()496 			ChrMem& GetChrMem()
497 			{
498 				return chr;
499 			}
500 
GetNmtMem()501 			NmtMem& GetNmtMem()
502 			{
503 				return nmt;
504 			}
505 
GetClock(dword count=1) const506 			Cycle GetClock(dword count=1) const
507 			{
508 				NST_ASSERT( count );
509 				return cycles.one * count;
510 			}
511 
GetHSyncClock() const512 			Cycle GetHSyncClock() const
513 			{
514 				return model == PPU_RP2C07 ? PPU_RP2C07_HSYNC : model == PPU_DENDY ? PPU_DENDY_HSYNC : PPU_RP2C02_HSYNC;
515 			}
516 
GetHVIntClock() const517 			Cycle GetHVIntClock() const
518 			{
519 				return model == PPU_RP2C07 ? PPU_RP2C07_HVINT : model == PPU_DENDY ? PPU_DENDY_HVINT : PPU_RP2C02_HVINT;
520 			}
521 
GetAddressLineHook() const522 			const Core::Io::Line& GetAddressLineHook() const
523 			{
524 				return io.line;
525 			}
526 
IsShortFrame() const527 			bool IsShortFrame() const
528 			{
529 				return regs.ctrl[1] & regs.frame;
530 			}
531 
GetBurstPhase() const532 			uint GetBurstPhase() const
533 			{
534 				return output.burstPhase;
535 			}
536 
EnableSpriteLimit(bool enable)537 			void EnableSpriteLimit(bool enable)
538 			{
539 				oam.spriteLimit = enable;
540 			}
541 
HasSpriteLimit() const542 			bool HasSpriteLimit() const
543 			{
544 				return oam.spriteLimit;
545 			}
546 
GetOverclockState() const547 			bool GetOverclockState() const
548 			{
549 				return overclocked;
550 			}
551 
SetOverclockState(bool overclock2x)552 			void SetOverclockState(bool overclock2x)
553 			{
554 				overclocked = overclock2x;
555 			}
556 		};
557 	}
558 }
559 
560 #endif
561