1 #pragma once
2 
3 #include "stdafx.h"
4 #include "Snapshotable.h"
5 #include "EmulationSettings.h"
6 #include "Types.h"
7 #include "DebuggerTypes.h"
8 #include "IMemoryHandler.h"
9 
10 enum class NesModel;
11 
12 class BaseMapper;
13 class ControlManager;
14 class Console;
15 
16 enum PPURegisters
17 {
18 	Control = 0x00,
19 	Mask = 0x01,
20 	Status = 0x02,
21 	SpriteAddr = 0x03,
22 	SpriteData = 0x04,
23 	ScrollOffsets = 0x05,
24 	VideoMemoryAddr = 0x06,
25 	VideoMemoryData = 0x07,
26 	SpriteDMA = 0x4014,
27 };
28 
29 class PPU : public IMemoryHandler, public Snapshotable
30 {
31 	protected:
32 		shared_ptr<Console> _console;
33 		EmulationSettings* _settings;
34 
35 		PPUState _state;
36 		int32_t _scanline;
37 		uint32_t _cycle;
38 		uint32_t _frameCount;
39 		uint8_t _memoryReadBuffer;
40 
41 		uint8_t _paletteRAM[0x20];
42 
43 		uint8_t _spriteRAM[0x100];
44 		uint8_t _secondarySpriteRAM[0x20];
45 		bool _hasSprite[257];
46 
47 		uint16_t *_currentOutputBuffer;
48 		uint16_t *_outputBuffers[2];
49 
50 		NesModel _nesModel;
51 		uint16_t _standardVblankEnd;
52 		uint16_t _standardNmiScanline;
53 		uint16_t _vblankEnd;
54 		uint16_t _nmiScanline;
55 		uint16_t _palSpriteEvalScanline;
56 
57 		PPUControlFlags _flags;
58 		PPUStatusFlags _statusFlags;
59 
60 		uint16_t _intensifyColorBits;
61 		uint8_t _paletteRamMask;
62 		int32_t _lastUpdatedPixel;
63 
64 		SpriteInfo *_lastSprite; //used by HD ppu
65 
66 		uint16_t _ppuBusAddress;
67 		TileInfo _currentTile;
68 		TileInfo _nextTile;
69 		TileInfo _previousTile;
70 
71 		SpriteInfo _spriteTiles[64];
72 		uint32_t _spriteCount;
73 		uint32_t _secondaryOAMAddr;
74 		bool _sprite0Visible;
75 
76 		uint32_t _overflowSpriteAddr;
77 		uint32_t _spriteIndex;
78 
79 		uint8_t _openBus;
80 		int32_t _openBusDecayStamp[8];
81 		uint32_t _ignoreVramRead;
82 
83 		uint8_t _oamCopybuffer;
84 		bool _spriteInRange;
85 		bool _sprite0Added;
86 		uint8_t _spriteAddrH;
87 		uint8_t _spriteAddrL;
88 		bool _oamCopyDone;
89 		uint8_t _overflowBugCounter;
90 
91 		bool _needStateUpdate;
92 		bool _renderingEnabled;
93 		bool _prevRenderingEnabled;
94 
95 		double _cyclesNeeded;
96 
97 		uint16_t _updateVramAddr;
98 		uint8_t _updateVramAddrDelay;
99 
100 		uint32_t _minimumDrawBgCycle;
101 		uint32_t _minimumDrawSpriteCycle;
102 		uint32_t _minimumDrawSpriteStandardCycle;
103 
104 		uint64_t _oamDecayCycles[0x40];
105 		bool _enableOamDecay;
106 
107 		void UpdateStatusFlag();
108 
109 		void SetControlRegister(uint8_t value);
110 		void SetMaskRegister(uint8_t value);
111 
112 		bool IsRenderingEnabled();
113 
114 		void SetOpenBus(uint8_t mask, uint8_t value);
115 		uint8_t ApplyOpenBus(uint8_t mask, uint8_t value);
116 
117 		void ProcessStatusRegOpenBus(uint8_t & openBusMask, uint8_t & returnValue);
118 
119 		void UpdateVideoRamAddr();
120 		void IncVerticalScrolling();
121 		void IncHorizontalScrolling();
122 		uint16_t GetNameTableAddr();
123 		uint16_t GetAttributeAddr();
124 
125 		__forceinline void ProcessScanline();
126 		__forceinline void ProcessSpriteEvaluation();
127 
128 		void BeginVBlank();
129 		void TriggerNmi();
130 
131 		void LoadTileInfo();
132 		void LoadSprite(uint8_t spriteY, uint8_t tileIndex, uint8_t attributes, uint8_t spriteX, bool extraSprite);
133 		void LoadSpriteTileInfo();
134 		void LoadExtraSprites();
135 		__forceinline void ShiftTileRegisters();
136 
137 		__forceinline uint8_t ReadSpriteRam(uint8_t addr);
138 		__forceinline void WriteSpriteRam(uint8_t addr, uint8_t value);
139 
140 		void UpdateMinimumDrawCycles();
141 
142 		__forceinline uint8_t GetPixelColor();
143 		__forceinline virtual void DrawPixel();
144 		void UpdateGrayscaleAndIntensifyBits();
145 		virtual void SendFrame();
146 
147 		void UpdateState();
148 
149 		void UpdateApuStatus();
150 
GetRegisterID(uint16_t addr)151 		PPURegisters GetRegisterID(uint16_t addr)
152 		{
153 			if(addr == 0x4014) {
154 				return PPURegisters::SpriteDMA;
155 			} else {
156 				return (PPURegisters)(addr & 0x07);
157 			}
158 		}
159 
160 		__forceinline void SetBusAddress(uint16_t addr);
161 		__forceinline uint8_t ReadVram(uint16_t addr, MemoryOperationType type = MemoryOperationType::PpuRenderingRead);
162 		__forceinline void WriteVram(uint16_t addr, uint8_t value);
163 
164 		void StreamState(bool saving) override;
165 
166 	public:
167 		static constexpr int32_t ScreenWidth = 256;
168 		static constexpr int32_t ScreenHeight = 240;
169 		static constexpr int32_t PixelCount = 256*240;
170 		static constexpr int32_t OutputBufferSize = 256*240*2;
171 		static constexpr int32_t OamDecayCycleCount = 3000;
172 
173 		PPU(shared_ptr<Console> console);
174 		virtual ~PPU();
175 
176 		void Reset();
177 
178 		void DebugSendFrame();
179 		void DebugCopyOutputBuffer(uint16_t *target);
180 		void DebugUpdateFrameBuffer(bool toGrayscale);
181 		void GetState(PPUDebugState &state);
182 		void SetState(PPUDebugState &state);
183 
GetMemoryRanges(MemoryRanges & ranges)184 		void GetMemoryRanges(MemoryRanges &ranges) override
185 		{
186 			ranges.AddHandler(MemoryOperation::Read, 0x2000, 0x3FFF);
187 			ranges.AddHandler(MemoryOperation::Write, 0x2000, 0x3FFF);
188 			ranges.AddHandler(MemoryOperation::Write, 0x4014);
189 		}
190 
191 		__forceinline uint8_t ReadPaletteRAM(uint16_t addr);
192 		void WritePaletteRAM(uint16_t addr, uint8_t value);
193 
194 		uint8_t ReadRAM(uint16_t addr) override;
195 		uint8_t PeekRAM(uint16_t addr) override;
196 		void WriteRAM(uint16_t addr, uint8_t value) override;
197 
198 		void SetNesModel(NesModel model);
199 		double GetOverclockRate();
200 
201 		void Exec();
202 		void ProcessCpuClock();
203 
GetFrameCount()204 		uint32_t GetFrameCount()
205 		{
206 			return _frameCount;
207 		}
208 
GetFrameCycle()209 		uint32_t GetFrameCycle()
210 		{
211 			return ((_scanline + 1) * 341) + _cycle;
212 		}
213 
GetControlFlags()214 		PPUControlFlags GetControlFlags()
215 		{
216 			return _flags;
217 		}
218 
GetCurrentCycle()219 		uint32_t GetCurrentCycle()
220 		{
221 			return _cycle;
222 		}
223 
GetCurrentScanline()224 		int32_t GetCurrentScanline()
225 		{
226 			return _scanline;
227 		}
228 
229 		uint8_t* GetSpriteRam();
230 
GetSecondarySpriteRam()231 		uint8_t* GetSecondarySpriteRam()
232 		{
233 			return _secondarySpriteRAM;
234 		}
235 
236 		uint32_t GetPixelBrightness(uint8_t x, uint8_t y);
237 
GetPixel(uint8_t x,uint8_t y)238 		uint16_t GetPixel(uint8_t x, uint8_t y)
239 		{
240 			return _currentOutputBuffer[y << 8 | x];
241 		}
242 };
243