1 #include "stdafx.h"
2 #include "PPU.h"
3 #include "CPU.h"
4 #include "APU.h"
5 #include "EmulationSettings.h"
6 #include "VideoDecoder.h"
7 #include "Debugger.h"
8 #include "BaseMapper.h"
9 #include "RewindManager.h"
10 #include "ControlManager.h"
11 #include "MemoryManager.h"
12 #include "Console.h"
13 #include "NotificationManager.h"
14 
PPU(shared_ptr<Console> console)15 PPU::PPU(shared_ptr<Console> console)
16 {
17 	_console = console;
18 	_settings = _console->GetSettings();
19 
20 	_outputBuffers[0] = new uint16_t[256 * 240];
21 	_outputBuffers[1] = new uint16_t[256 * 240];
22 
23 	_currentOutputBuffer = _outputBuffers[0];
24 	memset(_outputBuffers[0], 0, 256 * 240 * sizeof(uint16_t));
25 	memset(_outputBuffers[1], 0, 256 * 240 * sizeof(uint16_t));
26 
27 	uint8_t paletteRamBootValues[0x20] { 0x09, 0x01, 0x00, 0x01, 0x00, 0x02, 0x02, 0x0D, 0x08, 0x10, 0x08, 0x24, 0x00, 0x00, 0x04, 0x2C,
28 		0x09, 0x01, 0x34, 0x03, 0x00, 0x04, 0x00, 0x14, 0x08, 0x3A, 0x00, 0x02, 0x00, 0x20, 0x2C, 0x08 };
29 	memcpy(_paletteRAM, paletteRamBootValues, sizeof(_paletteRAM));
30 
31 	_console->InitializeRam(_spriteRAM, 0x100);
32 	_console->InitializeRam(_secondarySpriteRAM, 0x20);
33 
34 	Reset();
35 }
36 
~PPU()37 PPU::~PPU()
38 {
39 	delete[] _outputBuffers[0];
40 	delete[] _outputBuffers[1];
41 }
42 
Reset()43 void PPU::Reset()
44 {
45 	_cyclesNeeded = 0;
46 
47 	_needStateUpdate = false;
48 	_prevRenderingEnabled = false;
49 	_renderingEnabled = false;
50 
51 	_ignoreVramRead = 0;
52 	_openBus = 0;
53 	memset(_openBusDecayStamp, 0, sizeof(_openBusDecayStamp));
54 
55 	_state = {};
56 	_flags = {};
57 	_statusFlags = {};
58 
59 	_previousTile = {};
60 	_currentTile = {};
61 	_nextTile = {};
62 
63 	_ppuBusAddress = 0;
64 	_intensifyColorBits = 0;
65 	_paletteRamMask = 0x3F;
66 	_lastUpdatedPixel = -1;
67 	_lastSprite = nullptr;
68 	_oamCopybuffer = 0;
69 	_spriteInRange = false;
70 	_sprite0Added = false;
71 	_spriteAddrH = 0;
72 	_spriteAddrL = 0;
73 	_oamCopyDone = false;
74 	_renderingEnabled = false;
75 	_prevRenderingEnabled = false;
76 	_cyclesNeeded = 0.0;
77 
78 	memset(_hasSprite, 0, sizeof(_hasSprite));
79 	memset(_spriteTiles, 0, sizeof(_spriteTiles));
80 	_spriteCount = 0;
81 	_secondaryOAMAddr = 0;
82 	_sprite0Visible = false;
83 	_overflowSpriteAddr = 0;
84 	_spriteIndex = 0;
85 	_openBus = 0;
86 	memset(_openBusDecayStamp, 0, sizeof(_openBusDecayStamp));
87 	_ignoreVramRead = 0;
88 
89 	//First execution will be cycle 0, scanline 0
90 	_scanline = -1;
91 	_cycle = 340;
92 
93 	_frameCount = 1;
94 	_memoryReadBuffer = 0;
95 
96 	_overflowBugCounter = 0;
97 
98 	_updateVramAddrDelay = 0;
99 	_updateVramAddr = 0;
100 
101 	memset(_oamDecayCycles, 0, sizeof(_oamDecayCycles));
102 	_enableOamDecay = _settings->CheckFlag(EmulationFlags::EnableOamDecay);
103 
104 	UpdateMinimumDrawCycles();
105 }
106 
SetNesModel(NesModel model)107 void PPU::SetNesModel(NesModel model)
108 {
109 	_nesModel = model;
110 
111 	switch(_nesModel) {
112 		case NesModel::Auto:
113 			//Should never be Auto
114 			break;
115 
116 		case NesModel::NTSC:
117 			_nmiScanline = 241;
118 			_vblankEnd = 260;
119 			_standardNmiScanline = 241;
120 			_standardVblankEnd = 260;
121 			break;
122 		case NesModel::PAL:
123 			_nmiScanline = 241;
124 			_vblankEnd = 310;
125 			_standardNmiScanline = 241;
126 			_standardVblankEnd = 310;
127 			break;
128 		case NesModel::Dendy:
129 			_nmiScanline = 291;
130 			_vblankEnd = 310;
131 			_standardNmiScanline = 291;
132 			_standardVblankEnd = 310;
133 			break;
134 	}
135 
136 	_nmiScanline += _settings->GetPpuExtraScanlinesBeforeNmi();
137 	_palSpriteEvalScanline = _nmiScanline + 24;
138 	_standardVblankEnd += _settings->GetPpuExtraScanlinesBeforeNmi();
139 	_vblankEnd += _settings->GetPpuExtraScanlinesAfterNmi() + _settings->GetPpuExtraScanlinesBeforeNmi();
140 }
141 
GetOverclockRate()142 double PPU::GetOverclockRate()
143 {
144 	uint32_t regularVblankEnd;
145 	switch(_nesModel) {
146 		default:
147 		case NesModel::NTSC: regularVblankEnd = 260; break;
148 		case NesModel::PAL: regularVblankEnd = 310; break;
149 		case NesModel::Dendy: regularVblankEnd = 310; break;
150 	}
151 
152 	return (double)(_vblankEnd + 2) / (regularVblankEnd + 2);
153 }
154 
GetState(PPUDebugState & state)155 void PPU::GetState(PPUDebugState &state)
156 {
157 	state.ControlFlags = _flags;
158 	state.StatusFlags = _statusFlags;
159 	state.State = _state;
160 	state.Cycle = _cycle;
161 	state.Scanline = _scanline;
162 	state.FrameCount = _frameCount;
163 	state.NmiScanline = _nmiScanline;
164 	state.ScanlineCount = _vblankEnd + 2;
165 	state.SafeOamScanline = _nesModel == NesModel::NTSC ? _nmiScanline + 19 : _palSpriteEvalScanline;
166 	state.BusAddress = _ppuBusAddress;
167 	state.MemoryReadBuffer = _memoryReadBuffer;
168 }
169 
SetState(PPUDebugState & state)170 void PPU::SetState(PPUDebugState &state)
171 {
172 	_flags = state.ControlFlags;
173 	_statusFlags = state.StatusFlags;
174 	_state = state.State;
175 	_cycle = state.Cycle;
176 	_scanline = state.Scanline;
177 	_frameCount = state.FrameCount;
178 
179 	UpdateMinimumDrawCycles();
180 
181 	_paletteRamMask = _flags.Grayscale ? 0x30 : 0x3F;
182 	if(_nesModel == NesModel::NTSC) {
183 		_intensifyColorBits = (_flags.IntensifyGreen ? 0x40 : 0x00) | (_flags.IntensifyRed ? 0x80 : 0x00) | (_flags.IntensifyBlue ? 0x100 : 0x00);
184 	} else if(_nesModel == NesModel::PAL || _nesModel == NesModel::Dendy) {
185 		//"Note that on the Dendy and PAL NES, the green and red bits swap meaning."
186 		_intensifyColorBits = (_flags.IntensifyRed ? 0x40 : 0x00) | (_flags.IntensifyGreen ? 0x80 : 0x00) | (_flags.IntensifyBlue ? 0x100 : 0x00);
187 	}
188 }
189 
UpdateVideoRamAddr()190 void PPU::UpdateVideoRamAddr()
191 {
192 	if(_scanline >= 240 || !IsRenderingEnabled()) {
193 		_state.VideoRamAddr = (_state.VideoRamAddr + (_flags.VerticalWrite ? 32 : 1)) & 0x7FFF;
194 
195 		//Trigger memory read when setting the vram address - needed by MMC3 IRQ counter
196 		//"Should be clocked when A12 changes to 1 via $2007 read/write"
197 		SetBusAddress(_state.VideoRamAddr & 0x3FFF);
198 	} else {
199 		//"During rendering (on the pre-render line and the visible lines 0-239, provided either background or sprite rendering is enabled), "
200 		//it will update v in an odd way, triggering a coarse X increment and a Y increment simultaneously"
201 		IncHorizontalScrolling();
202 		IncVerticalScrolling();
203 	}
204 }
205 
SetOpenBus(uint8_t mask,uint8_t value)206 void PPU::SetOpenBus(uint8_t mask, uint8_t value)
207 {
208 	//Decay expired bits, set new bits and update stamps on each individual bit
209 	if(mask == 0xFF) {
210 		//Shortcut when mask is 0xFF - all bits are set to the value and stamps updated
211 		_openBus = value;
212 		for(int i = 0; i < 8; i++) {
213 			_openBusDecayStamp[i] = _frameCount;
214 		}
215 	} else {
216 		uint16_t openBus = (_openBus << 8);
217 		for(int i = 0; i < 8; i++) {
218 			openBus >>= 1;
219 			if(mask & 0x01) {
220 				if(value & 0x01) {
221 					openBus |= 0x80;
222 				} else {
223 					openBus &= 0xFF7F;
224 				}
225 				_openBusDecayStamp[i] = _frameCount;
226 			} else if(_frameCount - _openBusDecayStamp[i] > 30) {
227 				openBus &= 0xFF7F;
228 			}
229 			value >>= 1;
230 			mask >>= 1;
231 		}
232 
233 		_openBus = (uint8_t)openBus;
234 	}
235 }
236 
ApplyOpenBus(uint8_t mask,uint8_t value)237 uint8_t PPU::ApplyOpenBus(uint8_t mask, uint8_t value)
238 {
239 	SetOpenBus(~mask, value);
240 	return value | (_openBus & mask);
241 }
242 
ProcessStatusRegOpenBus(uint8_t & openBusMask,uint8_t & returnValue)243 void PPU::ProcessStatusRegOpenBus(uint8_t &openBusMask, uint8_t &returnValue)
244 {
245 	switch(_settings->GetPpuModel()) {
246 		case PpuModel::Ppu2C05A: openBusMask = 0x00; returnValue |= 0x1B; break;
247 		case PpuModel::Ppu2C05B: openBusMask = 0x00; returnValue |= 0x3D; break;
248 		case PpuModel::Ppu2C05C: openBusMask = 0x00; returnValue |= 0x1C; break;
249 		case PpuModel::Ppu2C05D: openBusMask = 0x00; returnValue |= 0x1B; break;
250 		case PpuModel::Ppu2C05E: openBusMask = 0x00; break;
251 		default: break;
252 	}
253 }
254 
PeekRAM(uint16_t addr)255 uint8_t PPU::PeekRAM(uint16_t addr)
256 {
257 	//Used by debugger to get register values without side-effects (heavily edited copy of ReadRAM)
258 	uint8_t openBusMask = 0xFF;
259 	uint8_t returnValue = 0;
260 	switch(GetRegisterID(addr)) {
261 		case PPURegisters::Status:
262 			returnValue = ((uint8_t)_statusFlags.SpriteOverflow << 5) | ((uint8_t)_statusFlags.Sprite0Hit << 6) | ((uint8_t)_statusFlags.VerticalBlank << 7);
263 			if(_scanline == 241 && _cycle < 3) {
264 				//Clear vertical blank flag
265 				returnValue &= 0x7F;
266 			}
267 			openBusMask = 0x1F;
268 			ProcessStatusRegOpenBus(openBusMask, returnValue);
269 			break;
270 
271 		case PPURegisters::SpriteData:
272 			if(!_settings->CheckFlag(EmulationFlags::DisablePpu2004Reads)) {
273 				if(_scanline <= 239 && IsRenderingEnabled()) {
274 					if(_cycle >= 257 && _cycle <= 320) {
275 						uint8_t step = ((_cycle - 257) % 8) > 3 ? 3 : ((_cycle - 257) % 8);
276 						uint8_t addr = (_cycle - 257) / 8 * 4 + step;
277 						returnValue = _secondarySpriteRAM[addr];
278 					} else {
279 						returnValue = _oamCopybuffer;
280 					}
281 				} else {
282 					returnValue = _spriteRAM[_state.SpriteRamAddr];
283 				}
284 				openBusMask = 0x00;
285 			}
286 			break;
287 
288 		case PPURegisters::VideoMemoryData:
289 			returnValue = _memoryReadBuffer;
290 
291 			if((_state.VideoRamAddr & 0x3FFF) >= 0x3F00 && !_settings->CheckFlag(EmulationFlags::DisablePaletteRead)) {
292 				returnValue = ReadPaletteRAM(_state.VideoRamAddr) | (_openBus & 0xC0);
293 				openBusMask = 0xC0;
294 			} else {
295 				openBusMask = 0x00;
296 			}
297 			break;
298 
299 		default:
300 			break;
301 	}
302 	return returnValue | (_openBus & openBusMask);
303 }
304 
ReadRAM(uint16_t addr)305 uint8_t PPU::ReadRAM(uint16_t addr)
306 {
307 	uint8_t openBusMask = 0xFF;
308 	uint8_t returnValue = 0;
309 	switch(GetRegisterID(addr)) {
310 		case PPURegisters::Status:
311 			_state.WriteToggle = false;
312 			UpdateStatusFlag();
313 			returnValue = _state.Status;
314 			openBusMask = 0x1F;
315 
316 			ProcessStatusRegOpenBus(openBusMask, returnValue);
317 			break;
318 
319 		case PPURegisters::SpriteData:
320 			if(!_settings->CheckFlag(EmulationFlags::DisablePpu2004Reads)) {
321 				if(_scanline <= 239 && IsRenderingEnabled()) {
322 					//While the screen is begin drawn
323 					if(_cycle >= 257 && _cycle <= 320) {
324 						//If we're doing sprite rendering, set OAM copy buffer to its proper value.  This is done here for performance.
325 						//It's faster to only do this here when it's needed, rather than splitting LoadSpriteTileInfo() into an 8-step process
326 						uint8_t step = ((_cycle - 257) % 8) > 3 ? 3 : ((_cycle - 257) % 8);
327 						_secondaryOAMAddr = (_cycle - 257) / 8 * 4 + step;
328 						_oamCopybuffer = _secondarySpriteRAM[_secondaryOAMAddr];
329 					}
330 					//Return the value that PPU is currently using for sprite evaluation/rendering
331 					returnValue = _oamCopybuffer;
332 				} else {
333 					returnValue = ReadSpriteRam(_state.SpriteRamAddr);
334 				}
335 				openBusMask = 0x00;
336 			}
337 			break;
338 
339 		case PPURegisters::VideoMemoryData:
340 			if(_ignoreVramRead) {
341 				//2 reads to $2007 in quick succession (2 consecutive CPU cycles) causes the 2nd read to be ignored (normally depends on PPU/CPU timing, but this is the simplest solution)
342 				//Return open bus in this case? (which will match the last value read)
343 				openBusMask = 0xFF;
344 			} else {
345 				returnValue = _memoryReadBuffer;
346 				_memoryReadBuffer = ReadVram(_ppuBusAddress & 0x3FFF, MemoryOperationType::Read);
347 
348 				if((_ppuBusAddress & 0x3FFF) >= 0x3F00 && !_settings->CheckFlag(EmulationFlags::DisablePaletteRead)) {
349 					returnValue = ReadPaletteRAM(_ppuBusAddress) | (_openBus & 0xC0);
350 					_console->DebugProcessVramReadOperation(MemoryOperationType::Read, _ppuBusAddress & 0x3FFF, returnValue);
351 					openBusMask = 0xC0;
352 				} else {
353 					openBusMask = 0x00;
354 				}
355 
356 				UpdateVideoRamAddr();
357 				_ignoreVramRead = 6;
358 				_needStateUpdate = true;
359 			}
360 			break;
361 
362 		default:
363 			break;
364 	}
365 	return ApplyOpenBus(openBusMask, returnValue);
366 }
367 
WriteRAM(uint16_t addr,uint8_t value)368 void PPU::WriteRAM(uint16_t addr, uint8_t value)
369 {
370 	if(addr != 0x4014) {
371 		SetOpenBus(0xFF, value);
372 	}
373 
374 	switch(GetRegisterID(addr)) {
375 		case PPURegisters::Control:
376 			if(_settings->GetPpuModel() >= PpuModel::Ppu2C05A && _settings->GetPpuModel() <= PpuModel::Ppu2C05E) {
377 				SetMaskRegister(value);
378 			} else {
379 				SetControlRegister(value);
380 			}
381 			break;
382 		case PPURegisters::Mask:
383 			if(_settings->GetPpuModel() >= PpuModel::Ppu2C05A && _settings->GetPpuModel() <= PpuModel::Ppu2C05E) {
384 				SetControlRegister(value);
385 			} else {
386 				SetMaskRegister(value);
387 			}
388 			break;
389 		case PPURegisters::SpriteAddr:
390 			_state.SpriteRamAddr = value;
391 			break;
392 		case PPURegisters::SpriteData:
393 			if((_scanline >= 240 && (_nesModel != NesModel::PAL || _scanline < _palSpriteEvalScanline)) || !IsRenderingEnabled()) {
394 				if((_state.SpriteRamAddr & 0x03) == 0x02) {
395 					//"The three unimplemented bits of each sprite's byte 2 do not exist in the PPU and always read back as 0 on PPU revisions that allow reading PPU OAM through OAMDATA ($2004)"
396 					value &= 0xE3;
397 				}
398 				WriteSpriteRam(_state.SpriteRamAddr, value);
399 				_state.SpriteRamAddr = (_state.SpriteRamAddr + 1) & 0xFF;
400 			} else {
401 				//"Writes to OAMDATA during rendering (on the pre-render line and the visible lines 0-239, provided either sprite or background rendering is enabled) do not modify values in OAM,
402 				//but do perform a glitchy increment of OAMADDR, bumping only the high 6 bits"
403 				_state.SpriteRamAddr = (_state.SpriteRamAddr + 4) & 0xFF;
404 			}
405 			break;
406 		case PPURegisters::ScrollOffsets:
407 			if(_state.WriteToggle) {
408 				_state.TmpVideoRamAddr = (_state.TmpVideoRamAddr & ~0x73E0) | ((value & 0xF8) << 2) | ((value & 0x07) << 12);
409 			} else {
410 				_state.XScroll = value & 0x07;
411 				_state.TmpVideoRamAddr = (_state.TmpVideoRamAddr & ~0x001F) | (value >> 3);
412 			}
413 			_state.WriteToggle = !_state.WriteToggle;
414 			break;
415 		case PPURegisters::VideoMemoryAddr:
416 			if(_state.WriteToggle) {
417 				_state.TmpVideoRamAddr = (_state.TmpVideoRamAddr & ~0x00FF) | value;
418 
419 				//Video RAM update is apparently delayed by 2-3 PPU cycles (based on Visual NES findings)
420 				//A 3-cycle delay causes issues with the scanline test.
421 				_needStateUpdate = true;
422 				_updateVramAddrDelay = 2;
423 				_updateVramAddr = _state.TmpVideoRamAddr;
424 				_console->DebugSetLastFramePpuScroll(_updateVramAddr, _state.XScroll, false);
425 			} else {
426 				_state.TmpVideoRamAddr = (_state.TmpVideoRamAddr & ~0xFF00) | ((value & 0x3F) << 8);
427 			}
428 			_state.WriteToggle = !_state.WriteToggle;
429 			break;
430 		case PPURegisters::VideoMemoryData:
431 			if((_ppuBusAddress & 0x3FFF) >= 0x3F00) {
432 				WritePaletteRAM(_ppuBusAddress, value);
433 				_console->DebugProcessVramWriteOperation(_ppuBusAddress & 0x3FFF, value);
434 			} else {
435 				if(_scanline >= 240 || !IsRenderingEnabled()) {
436 					_console->GetMapper()->WriteVRAM(_ppuBusAddress & 0x3FFF, value);
437 				} else {
438 					//During rendering, the value written is ignored, and instead the address' LSB is used (not confirmed, based on Visual NES)
439 					_console->GetMapper()->WriteVRAM(_ppuBusAddress & 0x3FFF, _ppuBusAddress & 0xFF);
440 				}
441 			}
442 			UpdateVideoRamAddr();
443 			break;
444 		case PPURegisters::SpriteDMA:
445 			_console->GetCpu()->RunDMATransfer(value);
446 			break;
447 		default:
448 			break;
449 	}
450 }
451 
ReadPaletteRAM(uint16_t addr)452 uint8_t PPU::ReadPaletteRAM(uint16_t addr)
453 {
454 	addr &= 0x1F;
455 	if(addr == 0x10 || addr == 0x14 || addr == 0x18 || addr == 0x1C) {
456 		addr &= ~0x10;
457 	}
458 	return _paletteRAM[addr];
459 }
460 
WritePaletteRAM(uint16_t addr,uint8_t value)461 void PPU::WritePaletteRAM(uint16_t addr, uint8_t value)
462 {
463 	addr &= 0x1F;
464 	value &= 0x3F;
465 	if(addr == 0x00 || addr == 0x10) {
466 		_paletteRAM[0x00] = value;
467 		_paletteRAM[0x10] = value;
468 	} else if(addr == 0x04 || addr == 0x14) {
469 		_paletteRAM[0x04] = value;
470 		_paletteRAM[0x14] = value;
471 	} else if(addr == 0x08 || addr == 0x18) {
472 		_paletteRAM[0x08] = value;
473 		_paletteRAM[0x18] = value;
474 	} else if(addr == 0x0C || addr == 0x1C) {
475 		_paletteRAM[0x0C] = value;
476 		_paletteRAM[0x1C] = value;
477 	} else {
478 		_paletteRAM[addr] = value;
479 	}
480 }
481 
IsRenderingEnabled()482 bool PPU::IsRenderingEnabled()
483 {
484 	return _renderingEnabled;
485 }
486 
SetControlRegister(uint8_t value)487 void PPU::SetControlRegister(uint8_t value)
488 {
489 	_state.Control = value;
490 
491 	uint8_t nameTable = (_state.Control & 0x03);
492 	_state.TmpVideoRamAddr = (_state.TmpVideoRamAddr & ~0x0C00) | (nameTable << 10);
493 
494 	_flags.VerticalWrite = (_state.Control & 0x04) == 0x04;
495 	_flags.SpritePatternAddr = ((_state.Control & 0x08) == 0x08) ? 0x1000 : 0x0000;
496 	_flags.BackgroundPatternAddr = ((_state.Control & 0x10) == 0x10) ? 0x1000 : 0x0000;
497 	_flags.LargeSprites = (_state.Control & 0x20) == 0x20;
498 
499 	//"By toggling NMI_output ($2000 bit 7) during vertical blank without reading $2002, a program can cause /NMI to be pulled low multiple times, causing multiple NMIs to be generated."
500 	bool originalVBlank = _flags.VBlank;
501 	_flags.VBlank = (_state.Control & 0x80) == 0x80;
502 
503 	if(!originalVBlank && _flags.VBlank && _statusFlags.VerticalBlank && (_scanline != -1 || _cycle != 0)) {
504 		_console->GetCpu()->SetNmiFlag();
505 	}
506 	if(_scanline == 241 && _cycle < 3 && !_flags.VBlank) {
507 		_console->GetCpu()->ClearNmiFlag();
508 	}
509 }
510 
UpdateMinimumDrawCycles()511 void PPU::UpdateMinimumDrawCycles()
512 {
513 	_minimumDrawBgCycle = _flags.BackgroundEnabled ? ((_flags.BackgroundMask || _settings->CheckFlag(EmulationFlags::ForceBackgroundFirstColumn)) ? 0 : 8) : 300;
514 	_minimumDrawSpriteCycle = _flags.SpritesEnabled ? ((_flags.SpriteMask || _settings->CheckFlag(EmulationFlags::ForceSpritesFirstColumn)) ? 0 : 8) : 300;
515 	_minimumDrawSpriteStandardCycle = _flags.SpritesEnabled ? (_flags.SpriteMask ? 0 : 8) : 300;
516 }
517 
SetMaskRegister(uint8_t value)518 void PPU::SetMaskRegister(uint8_t value)
519 {
520 	_state.Mask = value;
521 	_flags.Grayscale = (_state.Mask & 0x01) == 0x01;
522 	_flags.BackgroundMask = (_state.Mask & 0x02) == 0x02;
523 	_flags.SpriteMask = (_state.Mask & 0x04) == 0x04;
524 	_flags.BackgroundEnabled = (_state.Mask & 0x08) == 0x08;
525 	_flags.SpritesEnabled = (_state.Mask & 0x10) == 0x10;
526 	_flags.IntensifyBlue = (_state.Mask & 0x80) == 0x80;
527 
528 	if(_renderingEnabled != (_flags.BackgroundEnabled | _flags.SpritesEnabled)) {
529 		_needStateUpdate = true;
530 	}
531 
532 	UpdateMinimumDrawCycles();
533 
534 	UpdateGrayscaleAndIntensifyBits();
535 
536 	//"Bit 0 controls a greyscale mode, which causes the palette to use only the colors from the grey column: $00, $10, $20, $30. This is implemented as a bitwise AND with $30 on any value read from PPU $3F00-$3FFF"
537 	_paletteRamMask = _flags.Grayscale ? 0x30 : 0x3F;
538 
539 	if(_nesModel == NesModel::NTSC) {
540 		_flags.IntensifyRed = (_state.Mask & 0x20) == 0x20;
541 		_flags.IntensifyGreen = (_state.Mask & 0x40) == 0x40;
542 		_intensifyColorBits = (value & 0xE0) << 1;
543 	} else if(_nesModel == NesModel::PAL || _nesModel == NesModel::Dendy) {
544 		//"Note that on the Dendy and PAL NES, the green and red bits swap meaning."
545 		_flags.IntensifyRed = (_state.Mask & 0x40) == 0x40;
546 		_flags.IntensifyGreen = (_state.Mask & 0x20) == 0x20;
547 		_intensifyColorBits = (_flags.IntensifyRed ? 0x40 : 0x00) | (_flags.IntensifyGreen ? 0x80 : 0x00) | (_flags.IntensifyBlue ? 0x100 : 0x00);
548 	}
549 }
550 
UpdateStatusFlag()551 void PPU::UpdateStatusFlag()
552 {
553 	_state.Status = ((uint8_t)_statusFlags.SpriteOverflow << 5) |
554 		((uint8_t)_statusFlags.Sprite0Hit << 6) |
555 		((uint8_t)_statusFlags.VerticalBlank << 7);
556 	_statusFlags.VerticalBlank = false;
557 
558 	if(_scanline == 241 && _cycle < 3) {
559 		//"Reading on the same PPU clock or one later reads it as set, clears it, and suppresses the NMI for that frame."
560 		_statusFlags.VerticalBlank = false;
561 		_console->GetCpu()->ClearNmiFlag();
562 
563 		if(_cycle == 0) {
564 			//"Reading one PPU clock before reads it as clear and never sets the flag or generates NMI for that frame. "
565 			_state.Status = ((uint8_t)_statusFlags.SpriteOverflow << 5) | ((uint8_t)_statusFlags.Sprite0Hit << 6);
566 		}
567 	}
568 }
569 
570 //Taken from http://wiki.nesdev.com/w/index.php/The_skinny_on_NES_scrolling#Wrapping_around
IncVerticalScrolling()571 void PPU::IncVerticalScrolling()
572 {
573 	uint16_t addr = _state.VideoRamAddr;
574 
575 	if((addr & 0x7000) != 0x7000) {
576 		// if fine Y < 7
577 		addr += 0x1000;                    // increment fine Y
578 	} else {
579 		// fine Y = 0
580 		addr &= ~0x7000;
581 		int y = (addr & 0x03E0) >> 5;	// let y = coarse Y
582 		if(y == 29) {
583 			y = 0;                  // coarse Y = 0
584 			addr ^= 0x0800;                  // switch vertical nametable
585 		} else if(y == 31){
586 			y = 0;              // coarse Y = 0, nametable not switched
587 		} else {
588 			y++;                  // increment coarse Y
589 		}
590 		addr = (addr & ~0x03E0) | (y << 5);     // put coarse Y back into v
591 	}
592 	_state.VideoRamAddr = addr;
593 }
594 
595 //Taken from http://wiki.nesdev.com/w/index.php/The_skinny_on_NES_scrolling#Wrapping_around
IncHorizontalScrolling()596 void PPU::IncHorizontalScrolling()
597 {
598 	//Increase coarse X scrolling value.
599 	uint16_t addr = _state.VideoRamAddr;
600 	if((addr & 0x001F) == 31) {
601 		//When the value is 31, wrap around to 0 and switch nametable
602 		addr = (addr & ~0x001F) ^ 0x0400;
603 	} else {
604 		addr++;
605 	}
606 	_state.VideoRamAddr = addr;
607 }
608 
609 //Taken from http://wiki.nesdev.com/w/index.php/The_skinny_on_NES_scrolling#Tile_and_attribute_fetching
GetNameTableAddr()610 uint16_t PPU::GetNameTableAddr()
611 {
612 	return 0x2000 | (_state.VideoRamAddr & 0x0FFF);
613 }
614 
615 //Taken from http://wiki.nesdev.com/w/index.php/The_skinny_on_NES_scrolling#Tile_and_attribute_fetching
GetAttributeAddr()616 uint16_t PPU::GetAttributeAddr()
617 {
618 	return 0x23C0 | (_state.VideoRamAddr & 0x0C00) | ((_state.VideoRamAddr >> 4) & 0x38) | ((_state.VideoRamAddr >> 2) & 0x07);
619 }
620 
SetBusAddress(uint16_t addr)621 void PPU::SetBusAddress(uint16_t addr)
622 {
623 	_ppuBusAddress = addr;
624 	_console->GetMapper()->NotifyVRAMAddressChange(addr);
625 }
626 
ReadVram(uint16_t addr,MemoryOperationType type)627 uint8_t PPU::ReadVram(uint16_t addr, MemoryOperationType type)
628 {
629 	SetBusAddress(addr);
630 	return _console->GetMapper()->ReadVRAM(addr, type);
631 }
632 
WriteVram(uint16_t addr,uint8_t value)633 void PPU::WriteVram(uint16_t addr, uint8_t value)
634 {
635 	SetBusAddress(addr);
636 	_console->GetMapper()->WriteVRAM(addr, value);
637 }
638 
LoadTileInfo()639 void PPU::LoadTileInfo()
640 {
641 	if(IsRenderingEnabled()) {
642 		switch((_cycle - 1) & 0x07) {
643 			case 0: {
644 				_previousTile = _currentTile;
645 				_currentTile = _nextTile;
646 
647 				_state.LowBitShift |= _nextTile.LowByte;
648 				_state.HighBitShift |= _nextTile.HighByte;
649 
650 				uint8_t tileIndex = ReadVram(GetNameTableAddr());
651 				_nextTile.TileAddr = (tileIndex << 4) | (_state.VideoRamAddr >> 12) | _flags.BackgroundPatternAddr;
652 				_nextTile.OffsetY = _state.VideoRamAddr >> 12;
653 				break;
654 			}
655 
656 			case 2: {
657 				uint8_t shift = ((_state.VideoRamAddr >> 4) & 0x04) | (_state.VideoRamAddr & 0x02);
658 				_nextTile.PaletteOffset = ((ReadVram(GetAttributeAddr()) >> shift) & 0x03) << 2;
659 				break;
660 			}
661 
662 			case 3:
663 				_nextTile.LowByte = ReadVram(_nextTile.TileAddr);
664 				_nextTile.AbsoluteTileAddr = _console->GetMapper()->ToAbsoluteChrAddress(_nextTile.TileAddr);
665 				break;
666 
667 			case 5:
668 				_nextTile.HighByte = ReadVram(_nextTile.TileAddr + 8);
669 				break;
670 		}
671 	}
672 }
673 
LoadSprite(uint8_t spriteY,uint8_t tileIndex,uint8_t attributes,uint8_t spriteX,bool extraSprite)674 void PPU::LoadSprite(uint8_t spriteY, uint8_t tileIndex, uint8_t attributes, uint8_t spriteX, bool extraSprite)
675 {
676 	bool backgroundPriority = (attributes & 0x20) == 0x20;
677 	bool horizontalMirror = (attributes & 0x40) == 0x40;
678 	bool verticalMirror = (attributes & 0x80) == 0x80;
679 
680 	uint16_t tileAddr;
681 	uint8_t lineOffset;
682 	if(verticalMirror) {
683 		lineOffset = (_flags.LargeSprites ? 15 : 7) - (_scanline - spriteY);
684 	} else {
685 		lineOffset = _scanline - spriteY;
686 	}
687 
688 	if(_flags.LargeSprites) {
689 		tileAddr = (((tileIndex & 0x01) ? 0x1000 : 0x0000) | ((tileIndex & ~0x01) << 4)) + (lineOffset >= 8 ? lineOffset + 8 : lineOffset);
690 	} else {
691 		tileAddr = ((tileIndex << 4) | _flags.SpritePatternAddr) + lineOffset;
692 	}
693 
694 	bool fetchLastSprite = true;
695 	if((_spriteIndex < _spriteCount || extraSprite) && spriteY < 240) {
696 		SpriteInfo &info = _spriteTiles[_spriteIndex];
697 		info.BackgroundPriority = backgroundPriority;
698 		info.HorizontalMirror = horizontalMirror;
699 		info.VerticalMirror = verticalMirror;
700 		info.PaletteOffset = ((attributes & 0x03) << 2) | 0x10;
701 		if(extraSprite) {
702 			//Use DebugReadVRAM for extra sprites to prevent side-effects.
703 			info.LowByte = _console->GetMapper()->DebugReadVRAM(tileAddr);
704 			info.HighByte = _console->GetMapper()->DebugReadVRAM(tileAddr + 8);
705 		} else {
706 			fetchLastSprite = false;
707 			info.LowByte = ReadVram(tileAddr);
708 			info.HighByte = ReadVram(tileAddr + 8);
709 		}
710 		info.TileAddr = tileAddr;
711 		info.AbsoluteTileAddr = _console->GetMapper()->ToAbsoluteChrAddress(tileAddr);
712 		info.OffsetY = lineOffset;
713 		info.SpriteX = spriteX;
714 
715 		if(_scanline >= 0) {
716 			//Sprites read on prerender scanline are not shown on scanline 0
717 			for(int i = 0; i < 8 && spriteX + i + 1 < 257; i++) {
718 				_hasSprite[spriteX + i + 1] = true;
719 			}
720 		}
721 	}
722 
723 	if(fetchLastSprite) {
724 		//Fetches to sprite 0xFF for remaining sprites/hidden - used by MMC3 IRQ counter
725 		lineOffset = 0;
726 		tileIndex = 0xFF;
727 		if(_flags.LargeSprites) {
728 			tileAddr = (((tileIndex & 0x01) ? 0x1000 : 0x0000) | ((tileIndex & ~0x01) << 4)) + (lineOffset >= 8 ? lineOffset + 8 : lineOffset);
729 		} else {
730 			tileAddr = ((tileIndex << 4) | _flags.SpritePatternAddr) + lineOffset;
731 		}
732 
733 		ReadVram(tileAddr);
734 		ReadVram(tileAddr + 8);
735 	}
736 
737 	_spriteIndex++;
738 }
739 
LoadExtraSprites()740 void PPU::LoadExtraSprites()
741 {
742 	if(_spriteCount == 8 && _settings->CheckFlag(EmulationFlags::RemoveSpriteLimit)) {
743 		bool loadExtraSprites = true;
744 
745 		if(_settings->CheckFlag(EmulationFlags::AdaptiveSpriteLimit)) {
746 			uint16_t lastPosition = 0xFFFF;
747 			uint8_t identicalSpriteCount = 0;
748 			uint8_t maxIdenticalSpriteCount = 0;
749 			for(int i = 0; i < 64; i++) {
750 				uint8_t y = _spriteRAM[i << 2];
751 				if(_scanline >= y && _scanline < y + (_flags.LargeSprites ? 16 : 8)) {
752 					uint8_t x = _spriteRAM[(i << 2) + 3];
753 					uint16_t position = (y << 8) | x;
754 					if(lastPosition != position) {
755 						if(identicalSpriteCount > maxIdenticalSpriteCount) {
756 							maxIdenticalSpriteCount = identicalSpriteCount;
757 						}
758 						lastPosition = position;
759 						identicalSpriteCount = 1;
760 					} else {
761 						identicalSpriteCount++;
762 					}
763 				}
764 			}
765 			loadExtraSprites = identicalSpriteCount < 8 && maxIdenticalSpriteCount < 8;
766 		}
767 
768 		if(loadExtraSprites) {
769 			for(uint32_t i = _overflowSpriteAddr; i < 0x100; i += 4) {
770 				uint8_t spriteY = _spriteRAM[i];
771 				if(_scanline >= spriteY && _scanline < spriteY + (_flags.LargeSprites ? 16 : 8)) {
772 					LoadSprite(spriteY, _spriteRAM[i + 1], _spriteRAM[i + 2], _spriteRAM[i + 3], true);
773 					_spriteCount++;
774 				}
775 			}
776 		}
777 	}
778 }
779 
LoadSpriteTileInfo()780 void PPU::LoadSpriteTileInfo()
781 {
782 	uint8_t *spriteAddr = _secondarySpriteRAM + _spriteIndex * 4;
783 	LoadSprite(*spriteAddr, *(spriteAddr+1), *(spriteAddr+2), *(spriteAddr+3), false);
784 	if(_cycle == 316) {
785 		LoadExtraSprites();
786 	}
787 }
788 
ShiftTileRegisters()789 void PPU::ShiftTileRegisters()
790 {
791 	_state.LowBitShift <<= 1;
792 	_state.HighBitShift <<= 1;
793 }
794 
GetPixelColor()795 uint8_t PPU::GetPixelColor()
796 {
797 	uint8_t offset = _state.XScroll;
798 	uint8_t backgroundColor = 0;
799 	uint8_t spriteBgColor = 0;
800 
801 	if(_cycle > _minimumDrawBgCycle) {
802 		//BackgroundMask = false: Hide background in leftmost 8 pixels of screen
803 		spriteBgColor = (((_state.LowBitShift << offset) & 0x8000) >> 15) | (((_state.HighBitShift << offset) & 0x8000) >> 14);
804 		if(_settings->GetBackgroundEnabled()) {
805 			backgroundColor = spriteBgColor;
806 		}
807 	}
808 
809 	if(_hasSprite[_cycle] && _cycle > _minimumDrawSpriteCycle) {
810 		//SpriteMask = true: Hide sprites in leftmost 8 pixels of screen
811 		for(uint8_t i = 0; i < _spriteCount; i++) {
812 			int32_t shift = (int32_t)_cycle - _spriteTiles[i].SpriteX - 1;
813 			if(shift >= 0 && shift < 8) {
814 				_lastSprite = &_spriteTiles[i];
815 				uint8_t spriteColor;
816 				if(_spriteTiles[i].HorizontalMirror) {
817 					spriteColor = ((_lastSprite->LowByte >> shift) & 0x01) | ((_lastSprite->HighByte >> shift) & 0x01) << 1;
818 				} else {
819 					spriteColor = ((_lastSprite->LowByte << shift) & 0x80) >> 7 | ((_lastSprite->HighByte << shift) & 0x80) >> 6;
820 				}
821 
822 				if(spriteColor != 0) {
823 					//First sprite without a 00 color, use it.
824 					if(i == 0 && spriteBgColor != 0 && _sprite0Visible && _cycle != 256 && _flags.BackgroundEnabled && !_statusFlags.Sprite0Hit && _cycle > _minimumDrawSpriteStandardCycle) {
825 						//"The hit condition is basically sprite zero is in range AND the first sprite output unit is outputting a non-zero pixel AND the background drawing unit is outputting a non-zero pixel."
826 						//"Sprite zero hits do not register at x=255" (cycle 256)
827 						//"... provided that background and sprite rendering are both enabled"
828 						//"Should always miss when Y >= 239"
829 						_statusFlags.Sprite0Hit = true;
830 
831 						_console->DebugProcessEvent(EventType::SpriteZeroHit);
832 					}
833 
834 					if(_settings->GetSpritesEnabled() && (backgroundColor == 0 || !_spriteTiles[i].BackgroundPriority)) {
835 						//Check sprite priority
836 						return _lastSprite->PaletteOffset + spriteColor;
837 					}
838 					break;
839 				}
840 			}
841 		}
842 	}
843 	return ((offset + ((_cycle - 1) & 0x07) < 8) ? _previousTile : _currentTile).PaletteOffset + backgroundColor;
844 }
845 
DrawPixel()846 void PPU::DrawPixel()
847 {
848 	//This is called 3.7 million times per second - needs to be as fast as possible.
849 	if(IsRenderingEnabled() || ((_state.VideoRamAddr & 0x3F00) != 0x3F00)) {
850 		uint32_t color = GetPixelColor();
851 		_currentOutputBuffer[(_scanline << 8) + _cycle - 1] = _paletteRAM[color & 0x03 ? color : 0];
852 	} else {
853 		//"If the current VRAM address points in the range $3F00-$3FFF during forced blanking, the color indicated by this palette location will be shown on screen instead of the backdrop color."
854 		_currentOutputBuffer[(_scanline << 8) + _cycle - 1] = _paletteRAM[_state.VideoRamAddr & 0x1F];
855 	}
856 }
857 
UpdateGrayscaleAndIntensifyBits()858 void PPU::UpdateGrayscaleAndIntensifyBits()
859 {
860 	if(_scanline < 0 || _scanline > _nmiScanline) {
861 		return;
862 	}
863 
864 	int pixelNumber;
865 	if(_scanline >= 240) {
866 		pixelNumber = 61439;
867 	} else if(_cycle < 3) {
868 		pixelNumber = (_scanline << 8) - 1;
869 	} else if(_cycle <= 258) {
870 		pixelNumber = (_scanline << 8) + _cycle - 3;
871 	} else {
872 		pixelNumber = (_scanline << 8) + 255;
873 	}
874 
875 	if(_paletteRamMask == 0x3F && _intensifyColorBits == 0) {
876 		//Nothing to do (most common case)
877 		_lastUpdatedPixel = pixelNumber;
878 		return;
879 	}
880 
881 	if(_lastUpdatedPixel < pixelNumber) {
882 		uint16_t *out = _currentOutputBuffer + _lastUpdatedPixel + 1;
883 		while(_lastUpdatedPixel < pixelNumber) {
884 			*out = (*out & _paletteRamMask) | _intensifyColorBits;
885 			out++;
886 			_lastUpdatedPixel++;
887 		}
888 	}
889 }
890 
ProcessScanline()891 void PPU::ProcessScanline()
892 {
893 	//Only called for cycle 1+
894 	if(_cycle <= 256) {
895 		LoadTileInfo();
896 
897 		if(_prevRenderingEnabled && (_cycle & 0x07) == 0) {
898 			IncHorizontalScrolling();
899 			if(_cycle == 256) {
900 				IncVerticalScrolling();
901 			}
902 		}
903 
904 		if(_scanline >= 0) {
905 			DrawPixel();
906 			ShiftTileRegisters();
907 
908 			//"Secondary OAM clear and sprite evaluation do not occur on the pre-render line"
909 			ProcessSpriteEvaluation();
910 		} else if(_cycle < 9) {
911 			//Pre-render scanline logic
912 			if(_cycle == 1) {
913 				_statusFlags.VerticalBlank = false;
914 			}
915 			if(_state.SpriteRamAddr >= 0x08 && IsRenderingEnabled() && !_settings->CheckFlag(EmulationFlags::DisableOamAddrBug)) {
916 				//This should only be done if rendering is enabled (otherwise oam_stress test fails immediately)
917 				//"If OAMADDR is not less than eight when rendering starts, the eight bytes starting at OAMADDR & 0xF8 are copied to the first eight bytes of OAM"
918 				WriteSpriteRam(_cycle - 1, ReadSpriteRam((_state.SpriteRamAddr & 0xF8) + _cycle - 1));
919 			}
920 		}
921 	} else if(_cycle >= 257 && _cycle <= 320) {
922 		if(_cycle == 257) {
923 			_spriteIndex = 0;
924 			memset(_hasSprite, 0, sizeof(_hasSprite));
925 			if(_prevRenderingEnabled) {
926 				//copy horizontal scrolling value from t
927 				_state.VideoRamAddr = (_state.VideoRamAddr & ~0x041F) | (_state.TmpVideoRamAddr & 0x041F);
928 				_console->DebugSetLastFramePpuScroll(_state.VideoRamAddr, _state.XScroll, true);
929 			}
930 		}
931 		if(IsRenderingEnabled()) {
932 			//"OAMADDR is set to 0 during each of ticks 257-320 (the sprite tile loading interval) of the pre-render and visible scanlines." (When rendering)
933 			_state.SpriteRamAddr = 0;
934 
935 			if((_cycle - 260) % 8 == 0) {
936 				//Cycle 260, 268, etc.  This is an approximation (each tile is actually loaded in 8 steps (e.g from 257 to 264))
937 				LoadSpriteTileInfo();
938 			} else if((_cycle - 257) % 8 == 0) {
939 				//Garbage NT sprite fetch (257, 265, 273, etc.) - Required for proper MC-ACC IRQs (MMC3 clone)
940 				ReadVram(GetNameTableAddr());
941 			} else if((_cycle - 259) % 8 == 0) {
942 				//Garbage AT sprite fetch
943 				ReadVram(GetAttributeAddr());
944 			}
945 
946 			if(_scanline == -1 && _cycle >= 280 && _cycle <= 304) {
947 				//copy vertical scrolling value from t
948 				_state.VideoRamAddr = (_state.VideoRamAddr & ~0x7BE0) | (_state.TmpVideoRamAddr & 0x7BE0);
949 			}
950 		}
951 	} else if(_cycle >= 321 && _cycle <= 336) {
952 		LoadTileInfo();
953 		if(_cycle == 321) {
954 			if(IsRenderingEnabled()) {
955 				_oamCopybuffer = _secondarySpriteRAM[0];
956 			}
957 			if(_scanline == -1) {
958 				_console->DebugSetLastFramePpuScroll(_state.VideoRamAddr, _state.XScroll, false);
959 			}
960 		} else if(_prevRenderingEnabled && (_cycle == 328 || _cycle == 336)) {
961 			_state.LowBitShift <<= 8;
962 			_state.HighBitShift <<= 8;
963 			IncHorizontalScrolling();
964 		}
965 	} else if(_cycle == 337 || _cycle == 339) {
966 		if(IsRenderingEnabled()) {
967 			ReadVram(GetNameTableAddr());
968 
969 			if(_scanline == -1 && _cycle == 339 && (_frameCount & 0x01) && _nesModel == NesModel::NTSC && _settings->GetPpuModel() == PpuModel::Ppu2C02) {
970 				//This behavior is NTSC-specific - PAL frames are always the same number of cycles
971 				//"With rendering enabled, each odd PPU frame is one PPU clock shorter than normal" (skip from 339 to 0, going over 340)
972 				_cycle = 340;
973 			}
974 		}
975 	}
976 }
977 
ProcessSpriteEvaluation()978 void PPU::ProcessSpriteEvaluation()
979 {
980 	if(IsRenderingEnabled() || (_nesModel == NesModel::PAL && _scanline >= _palSpriteEvalScanline)) {
981 		if(_cycle < 65) {
982 			//Clear secondary OAM at between cycle 1 and 64
983 			_oamCopybuffer = 0xFF;
984 			_secondarySpriteRAM[(_cycle - 1) >> 1] = 0xFF;
985 		} else {
986 			if(_cycle == 65) {
987 				_sprite0Added = false;
988 				_spriteInRange = false;
989 				_secondaryOAMAddr = 0;
990 				_overflowSpriteAddr = 0;
991 				_overflowBugCounter = 0;
992 
993 				_oamCopyDone = false;
994 				_spriteAddrH = (_state.SpriteRamAddr >> 2) & 0x3F;
995 				_spriteAddrL = _state.SpriteRamAddr & 0x03;
996 			} else if(_cycle == 256) {
997 				_sprite0Visible = _sprite0Added;
998 				_spriteCount = (_secondaryOAMAddr >> 2);
999 			}
1000 
1001 			if(_cycle & 0x01) {
1002 				//Read a byte from the primary OAM on odd cycles
1003 				_oamCopybuffer = ReadSpriteRam(_state.SpriteRamAddr);
1004 			} else {
1005 				if(_oamCopyDone) {
1006 					_spriteAddrH = (_spriteAddrH + 1) & 0x3F;
1007 					if(_secondaryOAMAddr >= 0x20) {
1008 						//"As seen above, a side effect of the OAM write disable signal is to turn writes to the secondary OAM into reads from it."
1009 						_oamCopybuffer = _secondarySpriteRAM[_secondaryOAMAddr & 0x1F];
1010 					}
1011 				} else {
1012 					if(!_spriteInRange && _scanline >= _oamCopybuffer && _scanline < _oamCopybuffer + (_flags.LargeSprites ? 16 : 8)) {
1013 						_spriteInRange = true;
1014 					}
1015 
1016 					if(_secondaryOAMAddr < 0x20) {
1017 						//Copy 1 byte to secondary OAM
1018 						_secondarySpriteRAM[_secondaryOAMAddr] = _oamCopybuffer;
1019 
1020 						if(_spriteInRange) {
1021 							_spriteAddrL++;
1022 							_secondaryOAMAddr++;
1023 
1024 							if(_spriteAddrH == 0) {
1025 								_sprite0Added = true;
1026 							}
1027 
1028 							if(_spriteAddrL == 4) {
1029 								//Done copying all 4 bytes
1030 								_spriteInRange = false;
1031 								_spriteAddrL = 0;
1032 								_spriteAddrH = (_spriteAddrH + 1) & 0x3F;
1033 								if(_spriteAddrH == 0) {
1034 									_oamCopyDone = true;
1035 								}
1036 							}
1037 						} else {
1038 							//Nothing to copy, skip to next sprite
1039 							_spriteAddrH = (_spriteAddrH + 1) & 0x3F;
1040 							if(_spriteAddrH == 0) {
1041 								_oamCopyDone = true;
1042 							}
1043 						}
1044 					} else {
1045 						//"As seen above, a side effect of the OAM write disable signal is to turn writes to the secondary OAM into reads from it."
1046 						_oamCopybuffer = _secondarySpriteRAM[_secondaryOAMAddr & 0x1F];
1047 
1048 						//8 sprites have been found, check next sprite for overflow + emulate PPU bug
1049 						if(_overflowSpriteAddr == 0) {
1050 							//Used to remove sprite limit
1051 							_overflowSpriteAddr = _spriteAddrH * 4;
1052 						}
1053 
1054 						if(_spriteInRange) {
1055 							//Sprite is visible, consider this to be an overflow
1056 							_statusFlags.SpriteOverflow = true;
1057 							_spriteAddrL = (_spriteAddrL + 1);
1058 							if(_spriteAddrL == 4) {
1059 								_spriteAddrH = (_spriteAddrH + 1) & 0x3F;
1060 								_spriteAddrL = 0;
1061 							}
1062 
1063 							if(_overflowBugCounter == 0) {
1064 								_overflowBugCounter = 3;
1065 							} else if(_overflowBugCounter > 0) {
1066 								_overflowBugCounter--;
1067 								if(_overflowBugCounter == 0) {
1068 									//"After it finishes "fetching" this sprite(and setting the overflow flag), it realigns back at the beginning of this line and then continues here on the next sprite"
1069 									_oamCopyDone = true;
1070 									_spriteAddrL = 0;
1071 								}
1072 							}
1073 						} else {
1074 							//Sprite isn't on this scanline, trigger sprite evaluation bug - increment both H & L at the same time
1075 							_spriteAddrH = (_spriteAddrH + 1) & 0x3F;
1076 							_spriteAddrL = (_spriteAddrL + 1) & 0x03;
1077 
1078 							if(_spriteAddrH == 0) {
1079 								_oamCopyDone = true;
1080 							}
1081 						}
1082 					}
1083 				}
1084 				_state.SpriteRamAddr = (_spriteAddrL & 0x03) | (_spriteAddrH << 2);
1085 			}
1086 		}
1087 	}
1088 }
1089 
ReadSpriteRam(uint8_t addr)1090 uint8_t PPU::ReadSpriteRam(uint8_t addr)
1091 {
1092 	if(!_enableOamDecay) {
1093 		return _spriteRAM[addr];
1094 	} else {
1095 		uint64_t elapsedCycles = _console->GetCpu()->GetCycleCount() - _oamDecayCycles[addr >> 3];
1096 		if(elapsedCycles <= PPU::OamDecayCycleCount) {
1097 			_oamDecayCycles[addr >> 3] = _console->GetCpu()->GetCycleCount();
1098 			return _spriteRAM[addr];
1099 		} else {
1100 			if(_flags.SpritesEnabled) {
1101 				shared_ptr<Debugger> debugger = _console->GetDebugger(false);
1102 				if(debugger && debugger->CheckFlag(DebuggerFlags::BreakOnDecayedOamRead)) {
1103 					//When debugging with the break on decayed oam read flag turned on, break (only if sprite rendering is enabled to avoid false positives)
1104 					debugger->BreakImmediately(BreakSource::BreakOnDecayedOamRead);
1105 				}
1106 			}
1107 			//If this 8-byte row hasn't been read/written to in over 3000 cpu cycles (~1.7ms), return 0xFF to simulate decay
1108 			return 0x10;
1109 		}
1110 	}
1111 }
1112 
WriteSpriteRam(uint8_t addr,uint8_t value)1113 void PPU::WriteSpriteRam(uint8_t addr, uint8_t value)
1114 {
1115 	_spriteRAM[addr] = value;
1116 	if(_enableOamDecay) {
1117 		_oamDecayCycles[addr >> 3] = _console->GetCpu()->GetCycleCount();
1118 	}
1119 }
1120 
DebugSendFrame()1121 void PPU::DebugSendFrame()
1122 {
1123 	_console->GetVideoDecoder()->UpdateFrame(_currentOutputBuffer);
1124 }
1125 
DebugCopyOutputBuffer(uint16_t * target)1126 void PPU::DebugCopyOutputBuffer(uint16_t *target)
1127 {
1128 	memcpy(target, _currentOutputBuffer, PPU::PixelCount * sizeof(uint16_t));
1129 }
1130 
SendFrame()1131 void PPU::SendFrame()
1132 {
1133 	UpdateGrayscaleAndIntensifyBits();
1134 
1135 	_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::PpuFrameDone, _currentOutputBuffer);
1136 
1137 #ifdef LIBRETRO
1138 	_console->GetVideoDecoder()->UpdateFrameSync(_currentOutputBuffer);
1139 #else
1140 	if(_console->GetRewindManager()->IsRewinding()) {
1141 		if(!_console->GetRewindManager()->IsStepBack()) {
1142 			_console->GetVideoDecoder()->UpdateFrameSync(_currentOutputBuffer);
1143 		}
1144 	} else {
1145 		//If VideoDecoder isn't done with the previous frame, UpdateFrame will block until it is ready to accept a new frame.
1146 		_console->GetVideoDecoder()->UpdateFrame(_currentOutputBuffer);
1147 	}
1148 
1149 	_enableOamDecay = _settings->CheckFlag(EmulationFlags::EnableOamDecay);
1150 #endif
1151 }
1152 
BeginVBlank()1153 void PPU::BeginVBlank()
1154 {
1155 	TriggerNmi();
1156 }
1157 
TriggerNmi()1158 void PPU::TriggerNmi()
1159 {
1160 	_statusFlags.VerticalBlank = true;
1161 	if(_flags.VBlank) {
1162 		_console->GetCpu()->SetNmiFlag();
1163 	}
1164 }
1165 
UpdateApuStatus()1166 void PPU::UpdateApuStatus()
1167 {
1168 	APU* apu = _console->GetApu();
1169 	apu->SetApuStatus(true);
1170 	if(_scanline > 240) {
1171 		if(_scanline > _standardVblankEnd) {
1172 			//Disable APU for extra lines after NMI
1173 			apu->SetApuStatus(false);
1174 		} else if(_scanline >= _standardNmiScanline && _scanline < _nmiScanline) {
1175 			//Disable APU for extra lines before NMI
1176 			apu->SetApuStatus(false);
1177 		}
1178 	}
1179 }
1180 
DebugUpdateFrameBuffer(bool toGrayscale)1181 void PPU::DebugUpdateFrameBuffer(bool toGrayscale)
1182 {
1183 	//Clear output buffer for "Draw partial frame" feature
1184 	if(toGrayscale) {
1185 		for(int i = 0; i < PPU::PixelCount; i++) {
1186 			_currentOutputBuffer[i] &= 0x30;
1187 		}
1188 	} else {
1189 		memset(_currentOutputBuffer, 0, PPU::PixelCount * 2);
1190 	}
1191 }
1192 
Exec()1193 void PPU::Exec()
1194 {
1195 	if(_cycle > 339) {
1196 		_cycle = 0;
1197 		if(++_scanline > _vblankEnd) {
1198 			_lastUpdatedPixel = -1;
1199 			_scanline = -1;
1200 			UpdateMinimumDrawCycles();
1201 		}
1202 
1203 		_console->DebugProcessPpuCycle();
1204 
1205 		UpdateApuStatus();
1206 
1207 		if(_scanline == _settings->GetInputPollScanline()) {
1208 			_console->GetControlManager()->UpdateInputState();
1209 		}
1210 
1211 		//Cycle = 0
1212 		if(_scanline == -1) {
1213 			_statusFlags.SpriteOverflow = false;
1214 			_statusFlags.Sprite0Hit = false;
1215 
1216 			//Switch to alternate output buffer (VideoDecoder may still be decoding the last frame buffer)
1217 			_currentOutputBuffer = (_currentOutputBuffer == _outputBuffers[0]) ? _outputBuffers[1] : _outputBuffers[0];
1218 		} else if(_scanline == 240) {
1219 			//At the start of vblank, the bus address is set back to VideoRamAddr.
1220 			//According to Visual NES, this occurs on scanline 240, cycle 1, but is done here on cycle for performance reasons
1221 			SetBusAddress(_state.VideoRamAddr);
1222 			SendFrame();
1223 			_frameCount++;
1224 		} else if(_scanline == _nmiScanline) {
1225 			BeginVBlank();
1226 		}
1227 	} else {
1228 		//Cycle > 0
1229 		_cycle++;
1230 
1231 		_console->DebugProcessPpuCycle();
1232 		if(_scanline < 240) {
1233 			ProcessScanline();
1234 		} else if(_nesModel == NesModel::PAL && _scanline >= _palSpriteEvalScanline) {
1235 			//"On a PAL machine, because of its extended vertical blank, the PPU begins refreshing OAM roughly 21 scanlines after NMI[2], to prevent it
1236 			//from decaying during the longer hiatus of rendering. Additionally, it will continue to refresh during the visible portion of the screen
1237 			//even if rendering is disabled. Because of this, OAM DMA must be done near the beginning of vertical blank on PAL, and everywhere else
1238 			//it is liable to conflict with the refresh. Since the refresh can't be disabled like on the NTSC hardware, OAM decay does not occur at all on the PAL NES."
1239 			if(_cycle <= 256) {
1240 				ProcessSpriteEvaluation();
1241 			} else if(_cycle >= 257 && _cycle < 320) {
1242 				_state.SpriteRamAddr = 0;
1243 			}
1244 		}
1245 	}
1246 
1247 	if(_needStateUpdate) {
1248 		UpdateState();
1249 	}
1250 }
1251 
UpdateState()1252 void PPU::UpdateState()
1253 {
1254 	_needStateUpdate = false;
1255 
1256 	//Rendering enabled flag is apparently set with a 1 cycle delay (i.e setting it at cycle 5 will render cycle 6 like cycle 5 and then take the new settings for cycle 7)
1257 	_prevRenderingEnabled = _renderingEnabled;
1258 	_renderingEnabled = _flags.BackgroundEnabled | _flags.SpritesEnabled;
1259 	if(_prevRenderingEnabled != _renderingEnabled) {
1260 		_needStateUpdate = true;
1261 	}
1262 
1263 	if(_prevRenderingEnabled && !_renderingEnabled && _cycle >= 65 && _cycle <= 256 && _scanline < 240) {
1264 		//Disabling rendering during OAM evaluation will trigger a glitch causing the current address to be incremented by 1
1265 		//The increment can be "delayed" by 1 PPU cycle depending on whether or not rendering is disabled on an even/odd cycle
1266 		//e.g, if rendering is disabled on an even cycle, the following PPU cycle will increment the address by 5 (instead of 4)
1267 		//     if rendering is disabled on an odd cycle, the increment will wait until the next odd cycle (at which point it will be incremented by 1)
1268 		//In practice, there is no way to see the difference, so we just increment by 1 at the end of the next cycle after rendering was disabled
1269 		_state.SpriteRamAddr++;
1270 	}
1271 
1272 	if(_updateVramAddrDelay > 0) {
1273 		_updateVramAddrDelay--;
1274 		if(_updateVramAddrDelay == 0) {
1275 			_state.VideoRamAddr = _updateVramAddr;
1276 
1277 			if(_scanline >= 240 || !IsRenderingEnabled()) {
1278 				//Only set the VRAM address on the bus if the PPU is rendering
1279 				//More info here: https://forums.nesdev.com/viewtopic.php?p=132145#p132145
1280 				//Trigger bus address change when setting the vram address - needed by MMC3 IRQ counter
1281 				//"4) Should be clocked when A12 changes to 1 via $2006 write"
1282 				SetBusAddress(_state.VideoRamAddr & 0x3FFF);
1283 			}
1284 		} else {
1285 			_needStateUpdate = true;
1286 		}
1287 	}
1288 
1289 	if(_ignoreVramRead > 0) {
1290 		_ignoreVramRead--;
1291 		if(_ignoreVramRead > 0) {
1292 			_needStateUpdate = true;
1293 		}
1294 	}
1295 }
1296 
ProcessCpuClock()1297 void PPU::ProcessCpuClock()
1298 {
1299 	if(!_settings->HasOverclock()) {
1300 		Exec();
1301 		Exec();
1302 		Exec();
1303 		if(_nesModel == NesModel::PAL && _console->GetCpu()->GetCycleCount() % 5 == 0) {
1304 			//PAL PPU runs 3.2 clocks for every CPU clock, so we need to run an extra clock every 5 CPU clocks
1305 			Exec();
1306 		}
1307 	} else {
1308 		if(_nesModel == NesModel::PAL) {
1309 			//PAL PPU runs 3.2 clocks for every CPU clock, so we need to run an extra clock every 5 CPU clocks
1310 			_cyclesNeeded += 3.2 / (_settings->GetOverclockRate() / 100.0);
1311 		} else {
1312 			_cyclesNeeded += 3.0 / (_settings->GetOverclockRate() / 100.0);
1313 		}
1314 
1315 		while(_cyclesNeeded >= 1.0) {
1316 			Exec();
1317 			_cyclesNeeded--;
1318 		}
1319 	}
1320 }
1321 
GetSpriteRam()1322 uint8_t* PPU::GetSpriteRam()
1323 {
1324 	//Used by debugger
1325 	if(_enableOamDecay) {
1326 		for(int i = 0; i < 0x100; i++) {
1327 			//Apply OAM decay to sprite RAM before letting debugger access it
1328 			if((_console->GetCpu()->GetCycleCount() - _oamDecayCycles[i >> 3]) > PPU::OamDecayCycleCount) {
1329 				_spriteRAM[i] = 0x10;
1330 			}
1331 		}
1332 	}
1333 	return _spriteRAM;
1334 }
1335 
GetPixelBrightness(uint8_t x,uint8_t y)1336 uint32_t PPU::GetPixelBrightness(uint8_t x, uint8_t y)
1337 {
1338 	//Used by Zapper, gives a rough approximation of the brightness level of the specific pixel
1339 	uint16_t pixelData = _currentOutputBuffer[y << 8 | x];
1340 	uint32_t argbColor = _settings->GetRgbPalette()[pixelData & 0x3F];
1341 	return (argbColor & 0xFF) + ((argbColor >> 8) & 0xFF) + ((argbColor >> 16) & 0xFF);
1342 }
1343 
StreamState(bool saving)1344 void PPU::StreamState(bool saving)
1345 {
1346 	ArrayInfo<uint8_t> paletteRam = { _paletteRAM, 0x20 };
1347 	ArrayInfo<uint8_t> spriteRam = { _spriteRAM, 0x100 };
1348 	ArrayInfo<uint8_t> secondarySpriteRam = { _secondarySpriteRAM, 0x20 };
1349 	ArrayInfo<int32_t> openBusDecayStamp = { _openBusDecayStamp, 8 };
1350 
1351 	bool disablePpu2004Reads = false;
1352 	bool disablePaletteRead = false;
1353 	bool disableOamAddrBug = false;
1354 
1355 	if(saving) {
1356 		disablePpu2004Reads = _settings->CheckFlag(EmulationFlags::DisablePpu2004Reads);
1357 		disablePaletteRead = _settings->CheckFlag(EmulationFlags::DisablePaletteRead);
1358 		disableOamAddrBug = _settings->CheckFlag(EmulationFlags::DisableOamAddrBug);
1359 	}
1360 
1361 	Stream(_state.Control, _state.Mask, _state.Status, _state.SpriteRamAddr, _state.VideoRamAddr, _state.XScroll, _state.TmpVideoRamAddr, _state.WriteToggle,
1362 		_state.HighBitShift, _state.LowBitShift, _flags.VerticalWrite, _flags.SpritePatternAddr, _flags.BackgroundPatternAddr, _flags.LargeSprites, _flags.VBlank,
1363 		_flags.Grayscale, _flags.BackgroundMask, _flags.SpriteMask, _flags.BackgroundEnabled, _flags.SpritesEnabled, _flags.IntensifyRed, _flags.IntensifyGreen,
1364 		_flags.IntensifyBlue, _paletteRamMask, _intensifyColorBits, _statusFlags.SpriteOverflow, _statusFlags.Sprite0Hit, _statusFlags.VerticalBlank, _scanline,
1365 		_cycle, _frameCount, _memoryReadBuffer, _currentTile.LowByte, _currentTile.HighByte, _currentTile.PaletteOffset, _nextTile.LowByte, _nextTile.HighByte,
1366 		_nextTile.PaletteOffset, _nextTile.TileAddr, _previousTile.LowByte, _previousTile.HighByte, _previousTile.PaletteOffset, _spriteIndex, _spriteCount,
1367 		_secondaryOAMAddr, _sprite0Visible, _oamCopybuffer, _spriteInRange, _sprite0Added, _spriteAddrH, _spriteAddrL, _oamCopyDone, _nesModel,
1368 		_prevRenderingEnabled, _renderingEnabled, _openBus, _ignoreVramRead, paletteRam, spriteRam, secondarySpriteRam,
1369 		openBusDecayStamp, _cyclesNeeded, disablePpu2004Reads, disablePaletteRead, disableOamAddrBug, _overflowBugCounter, _updateVramAddr, _updateVramAddrDelay,
1370 		_needStateUpdate, _ppuBusAddress);
1371 
1372 	for(int i = 0; i < 64; i++) {
1373 		Stream(_spriteTiles[i].SpriteX, _spriteTiles[i].LowByte, _spriteTiles[i].HighByte, _spriteTiles[i].PaletteOffset, _spriteTiles[i].HorizontalMirror, _spriteTiles[i].BackgroundPriority);
1374 	}
1375 
1376 	if(!saving) {
1377 		_settings->SetFlagState(EmulationFlags::DisablePpu2004Reads, disablePpu2004Reads);
1378 		_settings->SetFlagState(EmulationFlags::DisablePaletteRead, disablePaletteRead);
1379 		_settings->SetFlagState(EmulationFlags::DisableOamAddrBug, disableOamAddrBug);
1380 
1381 		SetNesModel(_nesModel);
1382 		UpdateMinimumDrawCycles();
1383 
1384 		for(int i = 0; i < 0x20; i++) {
1385 			//Set oam decay cycle to the current cycle to ensure it doesn't decay when loading a state
1386 			_oamDecayCycles[i] = _console->GetCpu()->GetCycleCount();
1387 		}
1388 
1389 		for(int i = 0; i < 257; i++) {
1390 			_hasSprite[i] = true;
1391 		}
1392 
1393 		_lastUpdatedPixel = -1;
1394 
1395 		UpdateApuStatus();
1396 	}
1397 }