1 /*****************************************************************************\
2      Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3                 This file is licensed under the Snes9x License.
4    For further information, consult the LICENSE file in the root directory.
5 \*****************************************************************************/
6 
7 #include "snes9x.h"
8 #include "memmap.h"
9 #include "dma.h"
10 #include "apu/apu.h"
11 #include "fxemu.h"
12 #include "sdd1.h"
13 #include "srtc.h"
14 #include "controls.h"
15 #include "movie.h"
16 #include "display.h"
17 #ifdef NETPLAY_SUPPORT
18 #include "netplay.h"
19 #endif
20 #ifdef DEBUGGER
21 #include "debug.h"
22 #include "missing.h"
23 #endif
24 
25 extern uint8	*HDMAMemPointers[8];
26 
27 
S9xLatchCounters(bool force)28 static inline void S9xLatchCounters (bool force)
29 {
30 	if (force || (Memory.FillRAM[0x4213] & 0x80))
31 	{
32 		// Latch h and v counters, like the gun
33 	#ifdef DEBUGGER
34 		missing.h_v_latch = 1;
35 	#endif
36 
37 		PPU.HVBeamCounterLatched = 1;
38 		PPU.VBeamPosLatched = (uint16) CPU.V_Counter;
39 
40 		// From byuu:
41 		// All dots are 4 cycles long, except dots 322 and 326. dots 322 and 326 are 6 cycles long.
42 		// This holds true for all scanlines except scanline 240 on non-interlace odd frames.
43 		// The reason for this is because this scanline is only 1360 cycles long,
44 		// instead of 1364 like all other scanlines.
45 		// This makes the effective range of hscan_pos 0-339 at all times.
46 		int32	hc = CPU.Cycles;
47 
48 		if (Timings.H_Max == Timings.H_Max_Master) // 1364
49 		{
50 			if (hc >= 1292)
51 				hc -= (ONE_DOT_CYCLE / 2);
52 			if (hc >= 1308)
53 				hc -= (ONE_DOT_CYCLE / 2);
54 		}
55 
56 		PPU.HBeamPosLatched = (uint16) (hc / ONE_DOT_CYCLE);
57 
58 		Memory.FillRAM[0x213f] |= 0x40;
59 	}
60 
61 	if (CPU.V_Counter >  PPU.GunVLatch || (CPU.V_Counter == PPU.GunVLatch && CPU.Cycles >= PPU.GunHLatch * ONE_DOT_CYCLE))
62 		PPU.GunVLatch = 1000;
63 }
64 
S9xTryGunLatch(bool force)65 static inline void S9xTryGunLatch (bool force)
66 {
67 	if (CPU.V_Counter >  PPU.GunVLatch || (CPU.V_Counter == PPU.GunVLatch && CPU.Cycles >= PPU.GunHLatch * ONE_DOT_CYCLE))
68 	{
69 		if (force || (Memory.FillRAM[0x4213] & 0x80))
70 		{
71 		#ifdef DEBUGGER
72 			missing.h_v_latch = 1;
73 		#endif
74 
75 			PPU.HVBeamCounterLatched = 1;
76 			PPU.VBeamPosLatched = (uint16) PPU.GunVLatch;
77 			PPU.HBeamPosLatched = (uint16) PPU.GunHLatch;
78 
79 			Memory.FillRAM[0x213f] |= 0x40;
80 		}
81 
82 		PPU.GunVLatch = 1000;
83 	}
84 }
85 
CyclesUntilNext(int hc,int vc)86 static int CyclesUntilNext (int hc, int vc)
87 {
88 	int32 total = 0;
89 	int vpos = CPU.V_Counter;
90 
91 	if (vc - vpos > 0)
92 	{
93 		// It's still in this frame */
94 		// Add number of lines
95 		total += (vc - vpos) * Timings.H_Max_Master;
96 		// If line 240 is in there and we're odd, subtract a dot
97 		if (vpos <= 240 && vc > 240 && Timings.InterlaceField & !IPPU.Interlace)
98 			total -= ONE_DOT_CYCLE;
99 	}
100 	else
101 	{
102 		if (vc == vpos && (hc > CPU.Cycles))
103 		{
104 			return hc;
105 		}
106 
107 		total += (Timings.V_Max - vpos) * Timings.H_Max_Master;
108 		if (vpos <= 240 && Timings.InterlaceField && !IPPU.Interlace)
109 			total -= ONE_DOT_CYCLE;
110 
111 		total += (vc) * Timings.H_Max_Master;
112 		if (vc > 240 && !Timings.InterlaceField && !IPPU.Interlace)
113 			total -= ONE_DOT_CYCLE;
114 	}
115 
116 	total += hc;
117 
118 	return total;
119 }
120 
S9xUpdateIRQPositions(bool initial)121 void S9xUpdateIRQPositions (bool initial)
122 {
123 	PPU.HTimerPosition = PPU.IRQHBeamPos * ONE_DOT_CYCLE + Timings.IRQTriggerCycles;
124 	PPU.HTimerPosition -= PPU.IRQHBeamPos ? 0 : ONE_DOT_CYCLE;
125 	PPU.HTimerPosition += PPU.IRQHBeamPos > 322 ? (ONE_DOT_CYCLE / 2) : 0;
126 	PPU.HTimerPosition += PPU.IRQHBeamPos > 326 ? (ONE_DOT_CYCLE / 2) : 0;
127 	PPU.VTimerPosition = PPU.IRQVBeamPos;
128 
129 	if (PPU.VTimerEnabled && (PPU.VTimerPosition >= (Timings.V_Max + (IPPU.Interlace ? 1 : 0))))
130 	{
131 		Timings.NextIRQTimer = 0x0fffffff;
132 	}
133 	else if (!PPU.HTimerEnabled && !PPU.VTimerEnabled)
134 	{
135 		Timings.NextIRQTimer = 0x0fffffff;
136 	}
137 	else if (PPU.HTimerEnabled && !PPU.VTimerEnabled)
138 	{
139 		int v_pos = CPU.V_Counter;
140 
141 		Timings.NextIRQTimer = PPU.HTimerPosition;
142 		if (CPU.Cycles > Timings.NextIRQTimer - Timings.IRQTriggerCycles)
143 		{
144 			Timings.NextIRQTimer += Timings.H_Max;
145 			v_pos++;
146 		}
147 
148 		// Check for short dot scanline
149 		if (v_pos == 240 && Timings.InterlaceField && !IPPU.Interlace)
150 		{
151 			Timings.NextIRQTimer -= PPU.IRQHBeamPos <= 322 ? ONE_DOT_CYCLE / 2 : 0;
152 			Timings.NextIRQTimer -= PPU.IRQHBeamPos <= 326 ? ONE_DOT_CYCLE / 2 : 0;
153 		}
154 	}
155 	else if (!PPU.HTimerEnabled && PPU.VTimerEnabled)
156 	{
157 		if (CPU.V_Counter == PPU.VTimerPosition && initial)
158 			Timings.NextIRQTimer = CPU.Cycles + Timings.IRQTriggerCycles - ONE_DOT_CYCLE;
159 		else
160 			Timings.NextIRQTimer = CyclesUntilNext (Timings.IRQTriggerCycles - ONE_DOT_CYCLE, PPU.VTimerPosition);
161 	}
162 	else
163 	{
164 		Timings.NextIRQTimer = CyclesUntilNext (PPU.HTimerPosition, PPU.VTimerPosition);
165 
166 		// Check for short dot scanline
167 		int field = Timings.InterlaceField;
168 
169 		if (PPU.VTimerPosition < CPU.V_Counter ||
170 		   (PPU.VTimerPosition == CPU.V_Counter && Timings.NextIRQTimer > Timings.H_Max))
171 		{
172 			field = !field;
173 		}
174 
175 		if (PPU.VTimerPosition == 240 && field && !IPPU.Interlace)
176 		{
177 			Timings.NextIRQTimer -= PPU.IRQHBeamPos <= 322 ? ONE_DOT_CYCLE / 2 : 0;
178 			Timings.NextIRQTimer -= PPU.IRQHBeamPos <= 326 ? ONE_DOT_CYCLE / 2 : 0;
179 		}
180 	}
181 
182 #ifdef DEBUGGER
183 	S9xTraceFormattedMessage("--- IRQ Timer HC:%d VC:%d set %d cycles HTimer:%d Pos:%04d->%04d  VTimer:%d Pos:%03d->%03d", CPU.Cycles, CPU.V_Counter,
184 		Timings.NextIRQTimer, PPU.HTimerEnabled, PPU.IRQHBeamPos, PPU.HTimerPosition, PPU.VTimerEnabled, PPU.IRQVBeamPos, PPU.VTimerPosition);
185 #endif
186 }
187 
S9xFixColourBrightness(void)188 void S9xFixColourBrightness (void)
189 {
190 	IPPU.XB = mul_brightness[PPU.Brightness];
191 
192 	for (int i = 0; i < 64; i++)
193 	{
194 		if (i > IPPU.XB[0x1f])
195 			brightness_cap[i] = IPPU.XB[0x1f];
196 		else
197 			brightness_cap[i] = i;
198 	}
199 
200 	for (int i = 0; i < 256; i++)
201 	{
202 		IPPU.Red[i]   = IPPU.XB[(PPU.CGDATA[i])       & 0x1f];
203 		IPPU.Green[i] = IPPU.XB[(PPU.CGDATA[i] >>  5) & 0x1f];
204 		IPPU.Blue[i]  = IPPU.XB[(PPU.CGDATA[i] >> 10) & 0x1f];
205 		IPPU.ScreenColors[i] = BUILD_PIXEL(IPPU.Red[i], IPPU.Green[i], IPPU.Blue[i]);
206 	}
207 }
208 
S9xSetPPU(uint8 Byte,uint16 Address)209 void S9xSetPPU (uint8 Byte, uint16 Address)
210 {
211 	// MAP_PPU: $2000-$3FFF
212 
213 	if (CPU.InDMAorHDMA)
214 	{
215 		if (CPU.CurrentDMAorHDMAChannel >= 0 && DMA[CPU.CurrentDMAorHDMAChannel].ReverseTransfer)
216 		{
217 			// S9xSetPPU() is called to write to DMA[].AAddress
218 			if ((Address & 0xff00) == 0x2100)
219 			{
220 				// Cannot access to Address Bus B ($2100-$21ff) via (H)DMA
221 				return;
222 			}
223 			else
224 			{
225 				// 0x2000-0x3FFF is connected to Address Bus A
226 				// SA1, SuperFX and SRTC are mapped here
227 				// I don't bother for now...
228 				return;
229 			}
230 		}
231 		else
232 		{
233 			// S9xSetPPU() is called to read from $21xx
234 			// Take care of DMA wrapping
235 			if (Address > 0x21ff)
236 				Address = 0x2100 + (Address & 0xff);
237 		}
238 	}
239 
240 #ifdef DEBUGGER
241 	if (CPU.InHDMA)
242 		S9xTraceFormattedMessage("--- HDMA PPU %04X -> %02X", Address, Byte);
243 #endif
244 
245 	if (Settings.MSU1 && (Address & 0xfff8) == 0x2000) // MSU-1
246 		S9xMSU1WritePort(Address & 7, Byte);
247 	else
248 	if ((Address & 0xffc0) == 0x2140) // APUIO0, APUIO1, APUIO2, APUIO3
249 		// write_port will run the APU until given clock before writing value
250 		S9xAPUWritePort(Address & 3, Byte);
251 	else
252 	if (Address <= 0x2183)
253 	{
254 		switch (Address)
255 		{
256 			case 0x2100: // INIDISP
257 				if (Byte != Memory.FillRAM[0x2100])
258 				{
259 					FLUSH_REDRAW();
260 
261 					if (PPU.Brightness != (Byte & 0xf))
262 					{
263 						IPPU.ColorsChanged = TRUE;
264 						PPU.Brightness = Byte & 0xf;
265 						S9xFixColourBrightness();
266 						S9xBuildDirectColourMaps();
267 						if (PPU.Brightness > IPPU.MaxBrightness)
268 							IPPU.MaxBrightness = PPU.Brightness;
269 					}
270 
271 					if ((Memory.FillRAM[0x2100] & 0x80) != (Byte & 0x80))
272 					{
273 						IPPU.ColorsChanged = TRUE;
274 						PPU.ForcedBlanking = (Byte >> 7) & 1;
275 					}
276 				}
277 
278 				if ((Memory.FillRAM[0x2100] & 0x80) && CPU.V_Counter == PPU.ScreenHeight + FIRST_VISIBLE_LINE)
279 				{
280 					PPU.OAMAddr = PPU.SavedOAMAddr;
281 
282 					uint8 tmp = 0;
283 					if (PPU.OAMPriorityRotation)
284 						tmp = (PPU.OAMAddr & 0xfe) >> 1;
285 					if ((PPU.OAMFlip & 1) || PPU.FirstSprite != tmp)
286 					{
287 						PPU.FirstSprite = tmp;
288 						IPPU.OBJChanged = TRUE;
289 					}
290 
291 					PPU.OAMFlip = 0;
292 				}
293 
294 				break;
295 
296 			case 0x2101: // OBSEL
297 				if (Byte != Memory.FillRAM[0x2101])
298 				{
299 					FLUSH_REDRAW();
300 					PPU.OBJNameBase = (Byte & 3) << 14;
301 					PPU.OBJNameSelect = ((Byte >> 3) & 3) << 13;
302 					PPU.OBJSizeSelect = (Byte >> 5) & 7;
303 					IPPU.OBJChanged = TRUE;
304 				}
305 
306 				break;
307 
308 			case 0x2102: // OAMADDL
309 				PPU.OAMAddr = ((Memory.FillRAM[0x2103] & 1) << 8) | Byte;
310 				PPU.OAMFlip = 0;
311 				PPU.OAMReadFlip = 0;
312 				PPU.SavedOAMAddr = PPU.OAMAddr;
313 				if (PPU.OAMPriorityRotation && PPU.FirstSprite != (PPU.OAMAddr >> 1))
314 				{
315 					PPU.FirstSprite = (PPU.OAMAddr & 0xfe) >> 1;
316 					IPPU.OBJChanged = TRUE;
317 				#ifdef DEBUGGER
318 					missing.sprite_priority_rotation = 1;
319 				#endif
320 				}
321 
322 				break;
323 
324 			case 0x2103: // OAMADDH
325 				PPU.OAMAddr = ((Byte & 1) << 8) | Memory.FillRAM[0x2102];
326 				PPU.OAMPriorityRotation = (Byte & 0x80) ? 1 : 0;
327 				if (PPU.OAMPriorityRotation)
328 				{
329 					if (PPU.FirstSprite != (PPU.OAMAddr >> 1))
330 					{
331 						PPU.FirstSprite = (PPU.OAMAddr & 0xfe) >> 1;
332 						IPPU.OBJChanged = TRUE;
333 					#ifdef DEBUGGER
334 						missing.sprite_priority_rotation = 1;
335 					#endif
336 					}
337 				}
338 				else
339 				{
340 					if (PPU.FirstSprite != 0)
341 					{
342 						PPU.FirstSprite = 0;
343 						IPPU.OBJChanged = TRUE;
344 					#ifdef DEBUGGER
345 						missing.sprite_priority_rotation = 1;
346 					#endif
347 					}
348 				}
349 
350 				PPU.OAMFlip = 0;
351 				PPU.OAMReadFlip = 0;
352 				PPU.SavedOAMAddr = PPU.OAMAddr;
353 
354 				break;
355 
356 			case 0x2104: // OAMDATA
357 				REGISTER_2104(Byte);
358 				break;
359 
360 			case 0x2105: // BGMODE
361 				if (Byte != Memory.FillRAM[0x2105])
362 				{
363 					FLUSH_REDRAW();
364 					PPU.BG[0].BGSize = (Byte >> 4) & 1;
365 					PPU.BG[1].BGSize = (Byte >> 5) & 1;
366 					PPU.BG[2].BGSize = (Byte >> 6) & 1;
367 					PPU.BG[3].BGSize = (Byte >> 7) & 1;
368 					PPU.BGMode = Byte & 7;
369 					// BJ: BG3Priority only takes effect if BGMode == 1 and the bit is set
370 					PPU.BG3Priority = ((Byte & 0x0f) == 0x09);
371 					if (PPU.BGMode == 6 || PPU.BGMode == 5 || PPU.BGMode == 7)
372 					    IPPU.Interlace = Memory.FillRAM[0x2133] & 1;
373 					else
374 					    IPPU.Interlace = 0;
375 				#ifdef DEBUGGER
376 					missing.modes[PPU.BGMode] = 1;
377 				#endif
378 				}
379 
380 				break;
381 
382 			case 0x2106: // MOSAIC
383 				if (Byte != Memory.FillRAM[0x2106])
384 				{
385 					FLUSH_REDRAW();
386 					PPU.MosaicStart = CPU.V_Counter;
387 					if (PPU.MosaicStart > PPU.ScreenHeight)
388 						PPU.MosaicStart = 0;
389 					PPU.Mosaic = (Byte >> 4) + 1;
390 					PPU.BGMosaic[0] = (Byte & 1);
391 					PPU.BGMosaic[1] = (Byte & 2);
392 					PPU.BGMosaic[2] = (Byte & 4);
393 					PPU.BGMosaic[3] = (Byte & 8);
394 				#ifdef DEBUGGER
395 					if ((Byte & 0xf0) && (Byte & 0x0f))
396 						missing.mosaic = 1;
397 				#endif
398 				}
399 
400 				break;
401 
402 			case 0x2107: // BG1SC
403 				if (Byte != Memory.FillRAM[0x2107])
404 				{
405 					FLUSH_REDRAW();
406 					PPU.BG[0].SCSize = Byte & 3;
407 					PPU.BG[0].SCBase = (Byte & 0x7c) << 8;
408 				}
409 
410 				break;
411 
412 			case 0x2108: // BG2SC
413 				if (Byte != Memory.FillRAM[0x2108])
414 				{
415 					FLUSH_REDRAW();
416 					PPU.BG[1].SCSize = Byte & 3;
417 					PPU.BG[1].SCBase = (Byte & 0x7c) << 8;
418 				}
419 
420 				break;
421 
422 			case 0x2109: // BG3SC
423 				if (Byte != Memory.FillRAM[0x2109])
424 				{
425 					FLUSH_REDRAW();
426 					PPU.BG[2].SCSize = Byte & 3;
427 					PPU.BG[2].SCBase = (Byte & 0x7c) << 8;
428 				}
429 
430 				break;
431 
432 			case 0x210a: // BG4SC
433 				if (Byte != Memory.FillRAM[0x210a])
434 				{
435 					FLUSH_REDRAW();
436 					PPU.BG[3].SCSize = Byte & 3;
437 					PPU.BG[3].SCBase = (Byte & 0x7c) << 8;
438 				}
439 
440 				break;
441 
442 			case 0x210b: // BG12NBA
443 				if (Byte != Memory.FillRAM[0x210b])
444 				{
445 					FLUSH_REDRAW();
446 					PPU.BG[0].NameBase = (Byte & 7) << 12;
447 					PPU.BG[1].NameBase = ((Byte >> 4) & 7) << 12;
448 				}
449 
450 				break;
451 
452 			case 0x210c: // BG34NBA
453 				if (Byte != Memory.FillRAM[0x210c])
454 				{
455 					FLUSH_REDRAW();
456 					PPU.BG[2].NameBase = (Byte & 7) << 12;
457 					PPU.BG[3].NameBase = ((Byte >> 4) & 7) << 12;
458 				}
459 
460 				break;
461 
462 			case 0x210d: // BG1HOFS, M7HOFS
463 				PPU.BG[0].HOffset = (Byte << 8) | (PPU.BGnxOFSbyte & ~7) | ((PPU.BG[0].HOffset >> 8) & 7);
464 				PPU.M7HOFS = (Byte << 8) | PPU.M7byte;
465 				PPU.BGnxOFSbyte = Byte;
466 				PPU.M7byte = Byte;
467 				break;
468 
469 			case 0x210e: // BG1VOFS, M7VOFS
470 				PPU.BG[0].VOffset = (Byte << 8) | PPU.BGnxOFSbyte;
471 				PPU.M7VOFS = (Byte << 8) | PPU.M7byte;
472 				PPU.BGnxOFSbyte = Byte;
473 				PPU.M7byte = Byte;
474 				break;
475 
476 			case 0x210f: // BG2HOFS
477 				PPU.BG[1].HOffset = (Byte << 8) | (PPU.BGnxOFSbyte & ~7) | ((PPU.BG[1].HOffset >> 8) & 7);
478 				PPU.BGnxOFSbyte = Byte;
479 				break;
480 
481 			case 0x2110: // BG2VOFS
482 				PPU.BG[1].VOffset = (Byte << 8) | PPU.BGnxOFSbyte;
483 				PPU.BGnxOFSbyte = Byte;
484 				break;
485 
486 			case 0x2111: // BG3HOFS
487 				PPU.BG[2].HOffset = (Byte << 8) | (PPU.BGnxOFSbyte & ~7) | ((PPU.BG[2].HOffset >> 8) & 7);
488 				PPU.BGnxOFSbyte = Byte;
489 				break;
490 
491 			case 0x2112: // BG3VOFS
492 				PPU.BG[2].VOffset = (Byte << 8) | PPU.BGnxOFSbyte;
493 				PPU.BGnxOFSbyte = Byte;
494 				break;
495 
496 			case 0x2113: // BG4HOFS
497 				PPU.BG[3].HOffset = (Byte << 8) | (PPU.BGnxOFSbyte & ~7) | ((PPU.BG[3].HOffset >> 8) & 7);
498 				PPU.BGnxOFSbyte = Byte;
499 				break;
500 
501 			case 0x2114: // BG4VOFS
502 				PPU.BG[3].VOffset = (Byte << 8) | PPU.BGnxOFSbyte;
503 				PPU.BGnxOFSbyte = Byte;
504 				break;
505 
506 			case 0x2115: // VMAIN
507 				PPU.VMA.High = (Byte & 0x80) == 0 ? FALSE : TRUE;
508 				switch (Byte & 3)
509 				{
510 					case 0: PPU.VMA.Increment = 1;   break;
511 					case 1: PPU.VMA.Increment = 32;  break;
512 					case 2: PPU.VMA.Increment = 128; break;
513 					case 3: PPU.VMA.Increment = 128; break;
514 				}
515 
516 				if (Byte & 0x0c)
517 				{
518 					static uint16 Shift[4]    = { 0, 5, 6, 7 };
519 					static uint16 IncCount[4] = { 0, 32, 64, 128 };
520 
521 					uint8 i = (Byte & 0x0c) >> 2;
522 					PPU.VMA.FullGraphicCount = IncCount[i];
523 					PPU.VMA.Mask1 = IncCount[i] * 8 - 1;
524 					PPU.VMA.Shift = Shift[i];
525 				#ifdef DEBUGGER
526 					missing.vram_full_graphic_inc = (Byte & 0x0c) >> 2;
527 				#endif
528 				}
529 				else
530 					PPU.VMA.FullGraphicCount = 0;
531 			#ifdef DEBUGGER
532 				if (Byte & 3)
533 					missing.vram_inc = Byte & 3;
534 			#endif
535 				break;
536 
537 			case 0x2116: // VMADDL
538 				PPU.VMA.Address &= 0xff00;
539 				PPU.VMA.Address |= Byte;
540 
541 				S9xUpdateVRAMReadBuffer();
542 
543 				break;
544 
545 			case 0x2117: // VMADDH
546 				PPU.VMA.Address &= 0x00ff;
547 				PPU.VMA.Address |= Byte << 8;
548 
549 				S9xUpdateVRAMReadBuffer();
550 
551 				break;
552 
553 			case 0x2118: // VMDATAL
554 				REGISTER_2118(Byte);
555 				break;
556 
557 			case 0x2119: // VMDATAH
558 				REGISTER_2119(Byte);
559 				break;
560 
561 			case 0x211a: // M7SEL
562 				if (Byte != Memory.FillRAM[0x211a])
563 				{
564 					FLUSH_REDRAW();
565 					PPU.Mode7Repeat = Byte >> 6;
566 					if (PPU.Mode7Repeat == 1)
567 						PPU.Mode7Repeat = 0;
568 					PPU.Mode7VFlip = (Byte & 2) >> 1;
569 					PPU.Mode7HFlip = Byte & 1;
570 				}
571 
572 				break;
573 
574 			case 0x211b: // M7A
575 				PPU.MatrixA = PPU.M7byte | (Byte << 8);
576 				PPU.Need16x8Mulitply = TRUE;
577 				PPU.M7byte = Byte;
578 				break;
579 
580 			case 0x211c: // M7B
581 				PPU.MatrixB = PPU.M7byte | (Byte << 8);
582 				PPU.Need16x8Mulitply = TRUE;
583 				PPU.M7byte = Byte;
584 				break;
585 
586 			case 0x211d: // M7C
587 				PPU.MatrixC = PPU.M7byte | (Byte << 8);
588 				PPU.M7byte = Byte;
589 				break;
590 
591 			case 0x211e: // M7D
592 				PPU.MatrixD = PPU.M7byte | (Byte << 8);
593 				PPU.M7byte = Byte;
594 				break;
595 
596 			case 0x211f: // M7X
597 				PPU.CentreX = PPU.M7byte | (Byte << 8);
598 				PPU.M7byte = Byte;
599 				break;
600 
601 			case 0x2120: // M7Y
602 				PPU.CentreY = PPU.M7byte | (Byte << 8);
603 				PPU.M7byte = Byte;
604 				break;
605 
606 			case 0x2121: // CGADD
607 				PPU.CGFLIP = 0;
608 				PPU.CGFLIPRead = 0;
609 				PPU.CGADD = Byte;
610 				break;
611 
612 			case 0x2122: // CGDATA
613 				REGISTER_2122(Byte);
614 				break;
615 
616 			case 0x2123: // W12SEL
617 				if (Byte != Memory.FillRAM[0x2123])
618 				{
619 					FLUSH_REDRAW();
620 					PPU.ClipWindow1Enable[0] = !!(Byte & 0x02);
621 					PPU.ClipWindow1Enable[1] = !!(Byte & 0x20);
622 					PPU.ClipWindow2Enable[0] = !!(Byte & 0x08);
623 					PPU.ClipWindow2Enable[1] = !!(Byte & 0x80);
624 					PPU.ClipWindow1Inside[0] = !(Byte & 0x01);
625 					PPU.ClipWindow1Inside[1] = !(Byte & 0x10);
626 					PPU.ClipWindow2Inside[0] = !(Byte & 0x04);
627 					PPU.ClipWindow2Inside[1] = !(Byte & 0x40);
628 					PPU.RecomputeClipWindows = TRUE;
629 				#ifdef DEBUGGER
630 					if (Byte & 0x80)
631 						missing.window2[1] = 1;
632 					if (Byte & 0x20)
633 						missing.window1[1] = 1;
634 					if (Byte & 0x08)
635 						missing.window2[0] = 1;
636 					if (Byte & 0x02)
637 						missing.window1[0] = 1;
638 				#endif
639 				}
640 
641 				break;
642 
643 			case 0x2124: // W34SEL
644 				if (Byte != Memory.FillRAM[0x2124])
645 				{
646 					FLUSH_REDRAW();
647 					PPU.ClipWindow1Enable[2] = !!(Byte & 0x02);
648 					PPU.ClipWindow1Enable[3] = !!(Byte & 0x20);
649 					PPU.ClipWindow2Enable[2] = !!(Byte & 0x08);
650 					PPU.ClipWindow2Enable[3] = !!(Byte & 0x80);
651 					PPU.ClipWindow1Inside[2] = !(Byte & 0x01);
652 					PPU.ClipWindow1Inside[3] = !(Byte & 0x10);
653 					PPU.ClipWindow2Inside[2] = !(Byte & 0x04);
654 					PPU.ClipWindow2Inside[3] = !(Byte & 0x40);
655 					PPU.RecomputeClipWindows = TRUE;
656 				#ifdef DEBUGGER
657 					if (Byte & 0x80)
658 						missing.window2[3] = 1;
659 					if (Byte & 0x20)
660 						missing.window1[3] = 1;
661 					if (Byte & 0x08)
662 						missing.window2[2] = 1;
663 					if (Byte & 0x02)
664 						missing.window1[2] = 1;
665 				#endif
666 				}
667 
668 				break;
669 
670 			case 0x2125: // WOBJSEL
671 				if (Byte != Memory.FillRAM[0x2125])
672 				{
673 					FLUSH_REDRAW();
674 					PPU.ClipWindow1Enable[4] = !!(Byte & 0x02);
675 					PPU.ClipWindow1Enable[5] = !!(Byte & 0x20);
676 					PPU.ClipWindow2Enable[4] = !!(Byte & 0x08);
677 					PPU.ClipWindow2Enable[5] = !!(Byte & 0x80);
678 					PPU.ClipWindow1Inside[4] = !(Byte & 0x01);
679 					PPU.ClipWindow1Inside[5] = !(Byte & 0x10);
680 					PPU.ClipWindow2Inside[4] = !(Byte & 0x04);
681 					PPU.ClipWindow2Inside[5] = !(Byte & 0x40);
682 					PPU.RecomputeClipWindows = TRUE;
683 				#ifdef DEBUGGER
684 					if (Byte & 0x80)
685 						missing.window2[5] = 1;
686 					if (Byte & 0x20)
687 						missing.window1[5] = 1;
688 					if (Byte & 0x08)
689 						missing.window2[4] = 1;
690 					if (Byte & 0x02)
691 						missing.window1[4] = 1;
692 				#endif
693 				}
694 
695 				break;
696 
697 			case 0x2126: // WH0
698 				if (Byte != Memory.FillRAM[0x2126])
699 				{
700 					FLUSH_REDRAW();
701 					PPU.Window1Left = Byte;
702 					PPU.RecomputeClipWindows = TRUE;
703 				}
704 
705 				break;
706 
707 			case 0x2127: // WH1
708 				if (Byte != Memory.FillRAM[0x2127])
709 				{
710 					FLUSH_REDRAW();
711 					PPU.Window1Right = Byte;
712 					PPU.RecomputeClipWindows = TRUE;
713 				}
714 
715 				break;
716 
717 			case 0x2128: // WH2
718 				if (Byte != Memory.FillRAM[0x2128])
719 				{
720 					FLUSH_REDRAW();
721 					PPU.Window2Left = Byte;
722 					PPU.RecomputeClipWindows = TRUE;
723 				}
724 
725 				break;
726 
727 			case 0x2129: // WH3
728 				if (Byte != Memory.FillRAM[0x2129])
729 				{
730 					FLUSH_REDRAW();
731 					PPU.Window2Right = Byte;
732 					PPU.RecomputeClipWindows = TRUE;
733 				}
734 
735 				break;
736 
737 			case 0x212a: // WBGLOG
738 				if (Byte != Memory.FillRAM[0x212a])
739 				{
740 					FLUSH_REDRAW();
741 					PPU.ClipWindowOverlapLogic[0] = (Byte & 0x03);
742 					PPU.ClipWindowOverlapLogic[1] = (Byte & 0x0c) >> 2;
743 					PPU.ClipWindowOverlapLogic[2] = (Byte & 0x30) >> 4;
744 					PPU.ClipWindowOverlapLogic[3] = (Byte & 0xc0) >> 6;
745 					PPU.RecomputeClipWindows = TRUE;
746 				}
747 
748 				break;
749 
750 			case 0x212b: // WOBJLOG
751 				if (Byte != Memory.FillRAM[0x212b])
752 				{
753 					FLUSH_REDRAW();
754 					PPU.ClipWindowOverlapLogic[4] = (Byte & 0x03);
755 					PPU.ClipWindowOverlapLogic[5] = (Byte & 0x0c) >> 2;
756 					PPU.RecomputeClipWindows = TRUE;
757 				}
758 
759 				break;
760 
761 			case 0x212c: // TM
762 				if (Byte != Memory.FillRAM[0x212c])
763 				{
764 					FLUSH_REDRAW();
765 					PPU.RecomputeClipWindows = TRUE;
766 				}
767 
768 				break;
769 
770 			case 0x212d: // TS
771 				if (Byte != Memory.FillRAM[0x212d])
772 				{
773 					FLUSH_REDRAW();
774 					PPU.RecomputeClipWindows = TRUE;
775 				#ifdef DEBUGGER
776 					if (Byte & 0x1f)
777 						missing.subscreen = 1;
778 				#endif
779 				}
780 
781 				break;
782 
783 			case 0x212e: // TMW
784 				if (Byte != Memory.FillRAM[0x212e])
785 				{
786 					FLUSH_REDRAW();
787 					PPU.RecomputeClipWindows = TRUE;
788 				}
789 
790 				break;
791 
792 			case 0x212f: // TSW
793 				if (Byte != Memory.FillRAM[0x212f])
794 				{
795 					FLUSH_REDRAW();
796 					PPU.RecomputeClipWindows = TRUE;
797 				}
798 
799 				break;
800 
801 			case 0x2130: // CGWSEL
802 				if (Byte != Memory.FillRAM[0x2130])
803 				{
804 					FLUSH_REDRAW();
805 					PPU.RecomputeClipWindows = TRUE;
806 				#ifdef DEBUGGER
807 					if ((Byte & 1) && (PPU.BGMode == 3 || PPU.BGMode == 4 || PPU.BGMode == 7))
808 						missing.direct = 1;
809 				#endif
810 				}
811 
812 				break;
813 
814 			case 0x2131: // CGADSUB
815 				if (Byte != Memory.FillRAM[0x2131])
816 				{
817 					FLUSH_REDRAW();
818 				#ifdef DEBUGGER
819 					if (Byte & 0x80)
820 					{
821 						if (Memory.FillRAM[0x2130] & 0x02)
822 							missing.subscreen_sub = 1;
823 						else
824 							missing.fixed_colour_sub = 1;
825 					}
826 					else
827 					{
828 						if (Memory.FillRAM[0x2130] & 0x02)
829 							missing.subscreen_add = 1;
830 						else
831 							missing.fixed_colour_add = 1;
832 					}
833 				#endif
834 				}
835 
836 				break;
837 
838 			case 0x2132: // COLDATA
839 				if (Byte != Memory.FillRAM[0x2132])
840 				{
841 					FLUSH_REDRAW();
842 					if (Byte & 0x80)
843 						PPU.FixedColourBlue  = Byte & 0x1f;
844 					if (Byte & 0x40)
845 						PPU.FixedColourGreen = Byte & 0x1f;
846 					if (Byte & 0x20)
847 						PPU.FixedColourRed   = Byte & 0x1f;
848 				}
849 
850 				break;
851 
852 			case 0x2133: // SETINI
853 				if (Byte != Memory.FillRAM[0x2133])
854 				{
855 					if ((Memory.FillRAM[0x2133] ^ Byte) & 8)
856 					{
857 						FLUSH_REDRAW();
858 						IPPU.PseudoHires = Byte & 8;
859 					}
860 
861 					if (Byte & 0x04)
862 					{
863 						PPU.ScreenHeight = SNES_HEIGHT_EXTENDED;
864 						if (IPPU.DoubleHeightPixels)
865 							IPPU.RenderedScreenHeight = PPU.ScreenHeight << 1;
866 						else
867 							IPPU.RenderedScreenHeight = PPU.ScreenHeight;
868 					#ifdef DEBUGGER
869 						missing.lines_239 = 1;
870 					#endif
871 					}
872 					else
873 					{
874 						PPU.ScreenHeight = SNES_HEIGHT;
875 						if (IPPU.DoubleHeightPixels)
876 							IPPU.RenderedScreenHeight = PPU.ScreenHeight << 1;
877 						else
878 							IPPU.RenderedScreenHeight = PPU.ScreenHeight;
879 					}
880 
881 					if ((Memory.FillRAM[0x2133] ^ Byte) & 3)
882 					{
883 						FLUSH_REDRAW();
884 						if ((Memory.FillRAM[0x2133] ^ Byte) & 2)
885 							IPPU.OBJChanged = TRUE;
886 
887 						IPPU.Interlace = Byte & 1;
888 						IPPU.InterlaceOBJ = Byte & 2;
889 					}
890 				#ifdef DEBUGGER
891 					if (Byte & 0x40)
892 						missing.mode7_bgmode = 1;
893 					if (Byte & 0x08)
894 						missing.pseudo_512 = 1;
895 					if (Byte & 0x02)
896 						missing.sprite_double_height = 1;
897 					if (Byte & 0x01)
898 						missing.interlace = 1;
899 				#endif
900 				}
901 
902 				break;
903 
904 			case 0x2134: // MPYL
905 			case 0x2135: // MPYM
906 			case 0x2136: // MPYH
907 			case 0x2137: // SLHV
908 			case 0x2138: // OAMDATAREAD
909 			case 0x2139: // VMDATALREAD
910 			case 0x213a: // VMDATAHREAD
911 			case 0x213b: // CGDATAREAD
912 			case 0x213c: // OPHCT
913 			case 0x213d: // OPVCT
914 			case 0x213e: // STAT77
915 			case 0x213f: // STAT78
916 				return;
917 
918 			case 0x2180: // WMDATA
919 				if (!CPU.InWRAMDMAorHDMA)
920 					REGISTER_2180(Byte);
921 				break;
922 
923 			case 0x2181: // WMADDL
924 				if (!CPU.InWRAMDMAorHDMA)
925 				{
926 					PPU.WRAM &= 0x1ff00;
927 					PPU.WRAM |= Byte;
928 				}
929 
930 				break;
931 
932 			case 0x2182: // WMADDM
933 				if (!CPU.InWRAMDMAorHDMA)
934 				{
935 					PPU.WRAM &= 0x100ff;
936 					PPU.WRAM |= Byte << 8;
937 				}
938 
939 				break;
940 
941 			case 0x2183: // WMADDH
942 				if (!CPU.InWRAMDMAorHDMA)
943 				{
944 					PPU.WRAM &= 0x0ffff;
945 					PPU.WRAM |= Byte << 16;
946 					PPU.WRAM &= 0x1ffff;
947 				}
948 
949 				break;
950 		}
951 	}
952 	else
953 	{
954 		if (Settings.SuperFX && Address >= 0x3000 && Address <= 0x32ff)
955 		{
956 			S9xSetSuperFX(Byte, Address);
957 			return;
958 		}
959 		else
960 		if (Settings.SA1     && Address >= 0x2200)
961 		{
962 			if (Address <= 0x23ff)
963 				S9xSetSA1(Byte, Address);
964 			else
965 				Memory.FillRAM[Address] = Byte;
966 			return;
967 		}
968 		else
969 		if (Settings.BS      && Address >= 0x2188 && Address <= 0x219f)
970 			S9xSetBSXPPU(Byte, Address);
971 		else
972 		if (Settings.SRTC    && Address == 0x2801)
973 			S9xSetSRTC(Byte, Address);
974 	#ifdef DEBUGGER
975 		else
976 		{
977 			missing.unknownppu_write = Address;
978 			if (Settings.TraceUnknownRegisters)
979 			{
980 				sprintf(String, "Unknown register write: $%02X->$%04X\n", Byte, Address);
981 				S9xMessage(S9X_TRACE, S9X_PPU_TRACE, String);
982 			}
983 		}
984 	#endif
985 	}
986 
987 	Memory.FillRAM[Address] = Byte;
988 }
989 
S9xGetPPU(uint16 Address)990 uint8 S9xGetPPU (uint16 Address)
991 {
992 	// MAP_PPU: $2000-$3FFF
993 	if (Settings.MSU1 && (Address & 0xfff8) == 0x2000)
994 		return (S9xMSU1ReadPort(Address & 7));
995 	else
996 	if (Address < 0x2100)
997 		return (OpenBus);
998 
999 	if (CPU.InDMAorHDMA)
1000 	{
1001 		if (CPU.CurrentDMAorHDMAChannel >= 0 && !DMA[CPU.CurrentDMAorHDMAChannel].ReverseTransfer)
1002 		{
1003 			// S9xGetPPU() is called to read from DMA[].AAddress
1004 			if ((Address & 0xff00) == 0x2100)
1005 				// Cannot access to Address Bus B ($2100-$21FF) via (H)DMA
1006 				return (OpenBus);
1007 			else
1008 				// $2200-$3FFF are connected to Address Bus A
1009 				// SA1, SuperFX and SRTC are mapped here
1010 				// I don't bother for now...
1011 				return (OpenBus);
1012 		}
1013 		else
1014 		{
1015 			// S9xGetPPU() is called to write to $21xx
1016 			// Take care of DMA wrapping
1017 			if (Address > 0x21ff)
1018 				Address = 0x2100 + (Address & 0xff);
1019 		}
1020 	}
1021 
1022 	if ((Address & 0xffc0) == 0x2140) // APUIO0, APUIO1, APUIO2, APUIO3
1023 		// read_port will run the APU until given APU time before reading value
1024 		return (S9xAPUReadPort(Address & 3));
1025 	else
1026 	if (Address <= 0x2183)
1027     {
1028 		uint8	byte;
1029 
1030 		switch (Address)
1031 		{
1032 			case 0x2104: // OAMDATA
1033 			case 0x2105: // BGMODE
1034 			case 0x2106: // MOSAIC
1035 			case 0x2108: // BG2SC
1036 			case 0x2109: // BG3SC
1037 			case 0x210a: // BG4SC
1038 			case 0x2114: // BG4VOFS
1039 			case 0x2115: // VMAIN
1040 			case 0x2116: // VMADDL
1041 			case 0x2118: // VMDATAL
1042 			case 0x2119: // VMDATAH
1043 			case 0x211a: // M7SEL
1044 			case 0x2124: // W34SEL
1045 			case 0x2125: // WOBJSEL
1046 			case 0x2126: // WH0
1047 			case 0x2128: // WH2
1048 			case 0x2129: // WH3
1049 			case 0x212a: // WBGLOG
1050 				return (PPU.OpenBus1);
1051 
1052 			case 0x2134: // MPYL
1053 			case 0x2135: // MPYM
1054 			case 0x2136: // MPYH
1055 				if (PPU.Need16x8Mulitply)
1056 				{
1057 					int32 r = (int32) PPU.MatrixA * (int32) (PPU.MatrixB >> 8);
1058 					Memory.FillRAM[0x2134] = (uint8) r;
1059 					Memory.FillRAM[0x2135] = (uint8) (r >> 8);
1060 					Memory.FillRAM[0x2136] = (uint8) (r >> 16);
1061 					PPU.Need16x8Mulitply = FALSE;
1062 				}
1063 			#ifdef DEBUGGER
1064 				missing.matrix_multiply = 1;
1065 			#endif
1066 				return (PPU.OpenBus1 = Memory.FillRAM[Address]);
1067 
1068 			case 0x2137: // SLHV
1069 				S9xLatchCounters(0);
1070 				return (PPU.OpenBus1);
1071 
1072 			case 0x2138: // OAMDATAREAD
1073 				if (PPU.OAMAddr & 0x100)
1074 				{
1075 					if (!(PPU.OAMFlip & 1))
1076 						byte = PPU.OAMData[(PPU.OAMAddr & 0x10f) << 1];
1077 					else
1078 					{
1079 						byte = PPU.OAMData[((PPU.OAMAddr & 0x10f) << 1) + 1];
1080 						PPU.OAMAddr = (PPU.OAMAddr + 1) & 0x1ff;
1081 						if (PPU.OAMPriorityRotation && PPU.FirstSprite != (PPU.OAMAddr >> 1))
1082 						{
1083 							PPU.FirstSprite = (PPU.OAMAddr & 0xfe) >> 1;
1084 							IPPU.OBJChanged = TRUE;
1085 						#ifdef DEBUGGER
1086 							missing.sprite_priority_rotation = 1;
1087 						#endif
1088 						}
1089 					}
1090 				}
1091 				else
1092 				{
1093 					if (!(PPU.OAMFlip & 1))
1094 						byte = PPU.OAMData[PPU.OAMAddr << 1];
1095 					else
1096 					{
1097 						byte = PPU.OAMData[(PPU.OAMAddr << 1) + 1];
1098 						++PPU.OAMAddr;
1099 						if (PPU.OAMPriorityRotation && PPU.FirstSprite != (PPU.OAMAddr >> 1))
1100 						{
1101 							PPU.FirstSprite = (PPU.OAMAddr & 0xfe) >> 1;
1102 							IPPU.OBJChanged = TRUE;
1103 						#ifdef DEBUGGER
1104 							missing.sprite_priority_rotation = 1;
1105 						#endif
1106 						}
1107 					}
1108 				}
1109 
1110 				PPU.OAMFlip ^= 1;
1111 			#ifdef DEBUGGER
1112 				missing.oam_read = 1;
1113 			#endif
1114 				return (PPU.OpenBus1 = byte);
1115 
1116 			case 0x2139: // VMDATALREAD
1117 				byte = PPU.VRAMReadBuffer & 0xff;
1118 				if (!PPU.VMA.High)
1119 				{
1120 					S9xUpdateVRAMReadBuffer();
1121 
1122 					PPU.VMA.Address += PPU.VMA.Increment;
1123 				}
1124 
1125 			#ifdef DEBUGGER
1126 				missing.vram_read = 1;
1127 			#endif
1128 				return (PPU.OpenBus1 = byte);
1129 
1130 			case 0x213a: // VMDATAHREAD
1131 				byte = (PPU.VRAMReadBuffer >> 8) & 0xff;
1132 				if (PPU.VMA.High)
1133 				{
1134 					S9xUpdateVRAMReadBuffer();
1135 
1136 					PPU.VMA.Address += PPU.VMA.Increment;
1137 				}
1138 			#ifdef DEBUGGER
1139 				missing.vram_read = 1;
1140 			#endif
1141 				return (PPU.OpenBus1 = byte);
1142 
1143 			case 0x213b: // CGDATAREAD
1144 				if (PPU.CGFLIPRead)
1145 					byte = (PPU.OpenBus2 & 0x80) | ((PPU.CGDATA[PPU.CGADD++] >> 8) & 0x7f);
1146 				else
1147 					byte = PPU.CGDATA[PPU.CGADD] & 0xff;
1148 				PPU.CGFLIPRead ^= 1;
1149 			#ifdef DEBUGGER
1150 				missing.cgram_read = 1;
1151 			#endif
1152 				return (PPU.OpenBus2 = byte);
1153 
1154 			case 0x213c: // OPHCT
1155 				S9xTryGunLatch(false);
1156 				if (PPU.HBeamFlip)
1157 					byte = (PPU.OpenBus2 & 0xfe) | ((PPU.HBeamPosLatched >> 8) & 0x01);
1158 				else
1159 					byte = (uint8) PPU.HBeamPosLatched;
1160 				PPU.HBeamFlip ^= 1;
1161 			#ifdef DEBUGGER
1162 				missing.h_counter_read = 1;
1163 			#endif
1164 				return (PPU.OpenBus2 = byte);
1165 
1166 			case 0x213d: // OPVCT
1167 				S9xTryGunLatch(false);
1168 				if (PPU.VBeamFlip)
1169 					byte = (PPU.OpenBus2 & 0xfe) | ((PPU.VBeamPosLatched >> 8) & 0x01);
1170 				else
1171 					byte = (uint8) PPU.VBeamPosLatched;
1172 				PPU.VBeamFlip ^= 1;
1173 			#ifdef DEBUGGER
1174 				missing.v_counter_read = 1;
1175 			#endif
1176 				return (PPU.OpenBus2 = byte);
1177 
1178 			case 0x213e: // STAT77
1179 				FLUSH_REDRAW();
1180 				byte = (PPU.OpenBus1 & 0x10) | PPU.RangeTimeOver | Model->_5C77;
1181 				return (PPU.OpenBus1 = byte);
1182 
1183 			case 0x213f: // STAT78
1184 				S9xTryGunLatch(false);
1185 				PPU.VBeamFlip = PPU.HBeamFlip = 0;
1186 				byte = (PPU.OpenBus2 & 0x20) | (Memory.FillRAM[0x213f] & 0xc0) | (Settings.PAL ? 0x10 : 0) | Model->_5C78;
1187 				Memory.FillRAM[0x213f] &= ~0x40;
1188 				return (PPU.OpenBus2 = byte);
1189 
1190 			case 0x2180: // WMDATA
1191 				if (!CPU.InWRAMDMAorHDMA)
1192 				{
1193 					byte = Memory.RAM[PPU.WRAM++];
1194 					PPU.WRAM &= 0x1ffff;
1195 				}
1196 				else
1197 					byte = OpenBus;
1198 			#ifdef DEBUGGER
1199 				missing.wram_read = 1;
1200 			#endif
1201 				return (byte);
1202 
1203 			default:
1204 				return (OpenBus);
1205 		}
1206 	}
1207 	else
1208     {
1209 		if (Settings.SuperFX && Address >= 0x3000 && Address <= 0x32ff)
1210 			return (S9xGetSuperFX(Address));
1211 		else
1212 		if (Settings.SA1     && Address >= 0x2200)
1213 			return (S9xGetSA1(Address));
1214 		else
1215 		if (Settings.BS      && Address >= 0x2188 && Address <= 0x219f)
1216 			return (S9xGetBSXPPU(Address));
1217 		else
1218 		if (Settings.SRTC    && Address == 0x2800)
1219 			return (S9xGetSRTC(Address));
1220 		else
1221 		switch (Address)
1222 		{
1223 			case 0x21c2:
1224 				if (Model->_5C77 == 2)
1225 					return (0x20);
1226 				return (OpenBus);
1227 
1228 			case 0x21c3:
1229 				if (Model->_5C77 == 2)
1230 					return (0);
1231 				return (OpenBus);
1232 
1233 			default:
1234 				return (OpenBus);
1235 		}
1236 	}
1237 }
1238 
S9xSetCPU(uint8 Byte,uint16 Address)1239 void S9xSetCPU (uint8 Byte, uint16 Address)
1240 {
1241 	if (Address < 0x4200)
1242 	{
1243 		switch (Address)
1244 		{
1245 			case 0x4016: // JOYSER0
1246 				S9xSetJoypadLatch(Byte & 1);
1247 				break;
1248 
1249 			case 0x4017: // JOYSER1
1250 				return;
1251 
1252 			default:
1253 				break;
1254 		}
1255 	}
1256 	else
1257 	if ((Address & 0xff80) == 0x4300)
1258 	{
1259 		if (CPU.InDMAorHDMA)
1260 			return;
1261 
1262 		int	d = (Address >> 4) & 0x7;
1263 
1264 		switch (Address & 0xf)
1265 		{
1266 			case 0x0: // 0x43x0: DMAPx
1267 				DMA[d].ReverseTransfer        = (Byte & 0x80) ? TRUE : FALSE;
1268 				DMA[d].HDMAIndirectAddressing = (Byte & 0x40) ? TRUE : FALSE;
1269 				DMA[d].UnusedBit43x0          = (Byte & 0x20) ? TRUE : FALSE;
1270 				DMA[d].AAddressDecrement      = (Byte & 0x10) ? TRUE : FALSE;
1271 				DMA[d].AAddressFixed          = (Byte & 0x08) ? TRUE : FALSE;
1272 				DMA[d].TransferMode           = (Byte & 7);
1273 				return;
1274 
1275 			case 0x1: // 0x43x1: BBADx
1276 				DMA[d].BAddress = Byte;
1277 				return;
1278 
1279 			case 0x2: // 0x43x2: A1TxL
1280 				DMA[d].AAddress &= 0xff00;
1281 				DMA[d].AAddress |= Byte;
1282 				return;
1283 
1284 			case 0x3: // 0x43x3: A1TxH
1285 				DMA[d].AAddress &= 0xff;
1286 				DMA[d].AAddress |= Byte << 8;
1287 				return;
1288 
1289 			case 0x4: // 0x43x4: A1Bx
1290 				DMA[d].ABank = Byte;
1291 				HDMAMemPointers[d] = NULL;
1292 				return;
1293 
1294 			case 0x5: // 0x43x5: DASxL
1295 				DMA[d].DMACount_Or_HDMAIndirectAddress &= 0xff00;
1296 				DMA[d].DMACount_Or_HDMAIndirectAddress |= Byte;
1297 				HDMAMemPointers[d] = NULL;
1298 				return;
1299 
1300 			case 0x6: // 0x43x6: DASxH
1301 				DMA[d].DMACount_Or_HDMAIndirectAddress &= 0xff;
1302 				DMA[d].DMACount_Or_HDMAIndirectAddress |= Byte << 8;
1303 				HDMAMemPointers[d] = NULL;
1304 				return;
1305 
1306 			case 0x7: // 0x43x7: DASBx
1307 				DMA[d].IndirectBank = Byte;
1308 				HDMAMemPointers[d] = NULL;
1309 				return;
1310 
1311 			case 0x8: // 0x43x8: A2AxL
1312 				DMA[d].Address &= 0xff00;
1313 				DMA[d].Address |= Byte;
1314 				HDMAMemPointers[d] = NULL;
1315 				return;
1316 
1317 			case 0x9: // 0x43x9: A2AxH
1318 				DMA[d].Address &= 0xff;
1319 				DMA[d].Address |= Byte << 8;
1320 				HDMAMemPointers[d] = NULL;
1321 				return;
1322 
1323 			case 0xa: // 0x43xa: NLTRx
1324 				if (Byte & 0x7f)
1325 				{
1326 					DMA[d].LineCount = Byte & 0x7f;
1327 					DMA[d].Repeat = !(Byte & 0x80);
1328 				}
1329 				else
1330 				{
1331 					DMA[d].LineCount = 128;
1332 					DMA[d].Repeat = !!(Byte & 0x80);
1333 				}
1334 
1335 				return;
1336 
1337 			case 0xb: // 0x43xb: ????x
1338 			case 0xf: // 0x43xf: mirror of 0x43xb
1339 				DMA[d].UnknownByte = Byte;
1340 				return;
1341 
1342 			default:
1343 				break;
1344 		}
1345 	}
1346 	else
1347 	{
1348 		uint16	pos;
1349 
1350 		switch (Address)
1351 		{
1352 			case 0x4200: // NMITIMEN
1353 				#ifdef DEBUGGER
1354 				if (Settings.TraceHCEvent)
1355 					S9xTraceFormattedMessage("Write to 0x4200. Byte is %2x was %2x\n", Byte, Memory.FillRAM[Address]);
1356 				#endif
1357 
1358 				if (Byte == Memory.FillRAM[0x4200])
1359 					break;
1360 
1361 				if (Byte & 0x20)
1362 				{
1363 					PPU.VTimerEnabled = TRUE;
1364 
1365 					#ifdef DEBUGGER
1366 					missing.virq = 1;
1367 					missing.virq_pos = PPU.IRQVBeamPos;
1368 					#endif
1369 				}
1370 				else
1371 					PPU.VTimerEnabled = FALSE;
1372 
1373 				if (Byte & 0x10)
1374 				{
1375 					PPU.HTimerEnabled = TRUE;
1376 
1377 					#ifdef DEBUGGER
1378 					missing.hirq = 1;
1379 					missing.hirq_pos = PPU.IRQHBeamPos;
1380 					#endif
1381 				}
1382 				else
1383 					PPU.HTimerEnabled = FALSE;
1384 
1385 				if (!(Byte & 0x10) && !(Byte & 0x20))
1386 				{
1387 					CPU.IRQLine = FALSE;
1388 					CPU.IRQTransition = FALSE;
1389 				}
1390 
1391 				if ((Byte & 0x30) != (Memory.FillRAM[0x4200] & 0x30))
1392 				{
1393 					// Only allow instantaneous IRQ if turning it completely on or off
1394 					if ((Byte & 0x30) == 0 || (Memory.FillRAM[0x4200] & 0x30) == 0)
1395 						S9xUpdateIRQPositions(true);
1396 					else
1397 						S9xUpdateIRQPositions(false);
1398 				}
1399 
1400 				// NMI can trigger immediately during VBlank as long as NMI_read ($4210) wasn't cleard.
1401 				if ((Byte & 0x80) && !(Memory.FillRAM[0x4200] & 0x80) &&
1402 					(CPU.V_Counter >= PPU.ScreenHeight + FIRST_VISIBLE_LINE) && (Memory.FillRAM[0x4210] & 0x80))
1403 				{
1404 					// FIXME: triggered at HC+=6, checked just before the final CPU cycle,
1405 					// then, when to call S9xOpcode_NMI()?
1406 					Timings.IRQFlagChanging |= IRQ_TRIGGER_NMI;
1407 
1408 					#ifdef DEBUGGER
1409 					if (Settings.TraceHCEvent)
1410 						S9xTraceFormattedMessage("NMI Triggered on low-to-high occurring at next HC=%d\n", Timings.NMITriggerPos);
1411 					#endif
1412 				}
1413 
1414 				#ifdef DEBUGGER
1415 				S9xTraceFormattedMessage("--- IRQ Timer Enable HTimer:%d Pos:%04d  VTimer:%d Pos:%03d",
1416 				PPU.HTimerEnabled, PPU.HTimerPosition, PPU.VTimerEnabled, PPU.VTimerPosition);
1417 				#endif
1418 
1419 				break;
1420 
1421 			case 0x4201: // WRIO
1422 				if ((Byte & 0x80) == 0 && (Memory.FillRAM[0x4213] & 0x80) == 0x80)
1423 					S9xLatchCounters(1);
1424 				else
1425 					S9xTryGunLatch((Byte & 0x80) ? true : false);
1426 				Memory.FillRAM[0x4201] = Memory.FillRAM[0x4213] = Byte;
1427 				break;
1428 
1429 			case 0x4202: // WRMPYA
1430 				break;
1431 
1432 			case 0x4203: // WRMPYB
1433 			{
1434 				uint32 res = Memory.FillRAM[0x4202] * Byte;
1435 				// FIXME: The update occurs 8 machine cycles after $4203 is set.
1436 				Memory.FillRAM[0x4216] = (uint8) res;
1437 				Memory.FillRAM[0x4217] = (uint8) (res >> 8);
1438 				break;
1439 			}
1440 
1441 			case 0x4204: // WRDIVL
1442 			case 0x4205: // WRDIVH
1443 				break;
1444 
1445 			case 0x4206: // WRDIVB
1446 			{
1447 				uint16 a = Memory.FillRAM[0x4204] + (Memory.FillRAM[0x4205] << 8);
1448 				uint16 div = Byte ? a / Byte : 0xffff;
1449 				uint16 rem = Byte ? a % Byte : a;
1450 				// FIXME: The update occurs 16 machine cycles after $4206 is set.
1451 				Memory.FillRAM[0x4214] = (uint8) div;
1452 				Memory.FillRAM[0x4215] = div >> 8;
1453 				Memory.FillRAM[0x4216] = (uint8) rem;
1454 				Memory.FillRAM[0x4217] = rem >> 8;
1455 				break;
1456 			}
1457 
1458 			case 0x4207: // HTIMEL
1459 				pos = PPU.IRQHBeamPos;
1460 				PPU.IRQHBeamPos = (PPU.IRQHBeamPos & 0xff00) | Byte;
1461 				if (PPU.IRQHBeamPos != pos)
1462 					S9xUpdateIRQPositions(false);
1463 			#ifdef DEBUGGER
1464 				missing.hirq_pos = PPU.IRQHBeamPos;
1465 			#endif
1466 				break;
1467 
1468 			case 0x4208: // HTIMEH
1469 				pos = PPU.IRQHBeamPos;
1470 				PPU.IRQHBeamPos = (PPU.IRQHBeamPos & 0xff) | ((Byte & 1) << 8);
1471 				if (PPU.IRQHBeamPos != pos)
1472 					S9xUpdateIRQPositions(false);
1473 			#ifdef DEBUGGER
1474 				missing.hirq_pos = PPU.IRQHBeamPos;
1475 			#endif
1476 				break;
1477 
1478 			case 0x4209: // VTIMEL
1479 				pos = PPU.IRQVBeamPos;
1480 				PPU.IRQVBeamPos = (PPU.IRQVBeamPos & 0xff00) | Byte;
1481 				if (PPU.IRQVBeamPos != pos)
1482 					S9xUpdateIRQPositions(true);
1483 			#ifdef DEBUGGER
1484 				missing.virq_pos = PPU.IRQVBeamPos;
1485 			#endif
1486 				break;
1487 
1488 			case 0x420a: // VTIMEH
1489 				pos = PPU.IRQVBeamPos;
1490 				PPU.IRQVBeamPos = (PPU.IRQVBeamPos & 0xff) | ((Byte & 1) << 8);
1491 				if (PPU.IRQVBeamPos != pos)
1492 					S9xUpdateIRQPositions(true);
1493 			#ifdef DEBUGGER
1494 				missing.virq_pos = PPU.IRQVBeamPos;
1495 			#endif
1496 				break;
1497 
1498 			case 0x420b: // MDMAEN
1499 				if (CPU.InDMAorHDMA)
1500 					return;
1501 				// XXX: Not quite right...
1502                 if (Byte) {
1503 				CPU.Cycles += Timings.DMACPUSync;
1504                 }
1505 				if (Byte & 0x01)
1506 					S9xDoDMA(0);
1507 				if (Byte & 0x02)
1508 					S9xDoDMA(1);
1509 				if (Byte & 0x04)
1510 					S9xDoDMA(2);
1511 				if (Byte & 0x08)
1512 					S9xDoDMA(3);
1513 				if (Byte & 0x10)
1514 					S9xDoDMA(4);
1515 				if (Byte & 0x20)
1516 					S9xDoDMA(5);
1517 				if (Byte & 0x40)
1518 					S9xDoDMA(6);
1519 				if (Byte & 0x80)
1520 					S9xDoDMA(7);
1521 			#ifdef DEBUGGER
1522 				missing.dma_this_frame = Byte;
1523 				missing.dma_channels = Byte;
1524 			#endif
1525 				break;
1526 
1527 			case 0x420c: // HDMAEN
1528 				if (CPU.InDMAorHDMA)
1529 					return;
1530 				Memory.FillRAM[0x420c] = Byte;
1531 				// Yoshi's Island, Genjyu Ryodan, Mortal Kombat, Tales of Phantasia
1532 				PPU.HDMA = Byte & ~PPU.HDMAEnded;
1533 			#ifdef DEBUGGER
1534 				missing.hdma_this_frame |= Byte;
1535 				missing.hdma_channels |= Byte;
1536 			#endif
1537 				break;
1538 
1539 			case 0x420d: // MEMSEL
1540 				if ((Byte & 1) != (Memory.FillRAM[0x420d] & 1))
1541 				{
1542 					if (Byte & 1)
1543 					{
1544 						CPU.FastROMSpeed = ONE_CYCLE;
1545 					#ifdef DEBUGGER
1546 						missing.fast_rom = 1;
1547 					#endif
1548 					}
1549 					else
1550 						CPU.FastROMSpeed = SLOW_ONE_CYCLE;
1551 					// we might currently be in FastROMSpeed region, S9xSetPCBase will update CPU.MemSpeed
1552 					S9xSetPCBase(Registers.PBPC);
1553 				}
1554 
1555 				break;
1556 
1557 			case 0x4210: // RDNMI
1558 			case 0x4211: // TIMEUP
1559 			case 0x4212: // HVBJOY
1560 			case 0x4213: // RDIO
1561 			case 0x4214: // RDDIVL
1562 			case 0x4215: // RDDIVH
1563 			case 0x4216: // RDMPYL
1564 			case 0x4217: // RDMPYH
1565 			case 0x4218: // JOY1L
1566 			case 0x4219: // JOY1H
1567 			case 0x421a: // JOY2L
1568 			case 0x421b: // JOY2H
1569 			case 0x421c: // JOY3L
1570 			case 0x421d: // JOY3H
1571 			case 0x421e: // JOY4L
1572 			case 0x421f: // JOY4H
1573 				return;
1574 
1575 			default:
1576 				if (Settings.SPC7110 && Address >= 0x4800)
1577 					S9xSetSPC7110(Byte, Address);
1578 				else
1579 				if (Settings.SDD1 && Address >= 0x4804 && Address <= 0x4807)
1580 					S9xSetSDD1MemoryMap(Address - 0x4804, Byte & 7);
1581 				break;
1582 		}
1583 	}
1584 
1585 	Memory.FillRAM[Address] = Byte;
1586 }
1587 
S9xGetCPU(uint16 Address)1588 uint8 S9xGetCPU (uint16 Address)
1589 {
1590 	if (Address < 0x4200)
1591 	{
1592 	#ifdef SNES_JOY_READ_CALLBACKS
1593 		extern bool8 pad_read;
1594 		if (Address == 0x4016 || Address == 0x4017)
1595 		{
1596 			S9xOnSNESPadRead();
1597 			pad_read = TRUE;
1598 		}
1599 	#endif
1600 
1601 		switch (Address)
1602 		{
1603 			case 0x4016: // JOYSER0
1604 			case 0x4017: // JOYSER1
1605 				return (S9xReadJOYSERn(Address));
1606 
1607 			default:
1608 				return (OpenBus);
1609 		}
1610 	}
1611 	else
1612 	if ((Address & 0xff80) == 0x4300)
1613 	{
1614 		if (CPU.InDMAorHDMA)
1615 			return (OpenBus);
1616 
1617 		int	d = (Address >> 4) & 0x7;
1618 
1619 		switch (Address & 0xf)
1620 		{
1621 			case 0x0: // 0x43x0: DMAPx
1622 				return ((DMA[d].ReverseTransfer        ? 0x80 : 0) |
1623 						(DMA[d].HDMAIndirectAddressing ? 0x40 : 0) |
1624 						(DMA[d].UnusedBit43x0          ? 0x20 : 0) |
1625 						(DMA[d].AAddressDecrement      ? 0x10 : 0) |
1626 						(DMA[d].AAddressFixed          ? 0x08 : 0) |
1627 						(DMA[d].TransferMode & 7));
1628 
1629 			case 0x1: // 0x43x1: BBADx
1630 				return (DMA[d].BAddress);
1631 
1632 			case 0x2: // 0x43x2: A1TxL
1633 				return (DMA[d].AAddress & 0xff);
1634 
1635 			case 0x3: // 0x43x3: A1TxH
1636 				return (DMA[d].AAddress >> 8);
1637 
1638 			case 0x4: // 0x43x4: A1Bx
1639 				return (DMA[d].ABank);
1640 
1641 			case 0x5: // 0x43x5: DASxL
1642 				return (DMA[d].DMACount_Or_HDMAIndirectAddress & 0xff);
1643 
1644 			case 0x6: // 0x43x6: DASxH
1645 				return (DMA[d].DMACount_Or_HDMAIndirectAddress >> 8);
1646 
1647 			case 0x7: // 0x43x7: DASBx
1648 				return (DMA[d].IndirectBank);
1649 
1650 			case 0x8: // 0x43x8: A2AxL
1651 				return (DMA[d].Address & 0xff);
1652 
1653 			case 0x9: // 0x43x9: A2AxH
1654 				return (DMA[d].Address >> 8);
1655 
1656 			case 0xa: // 0x43xa: NLTRx
1657 				return (DMA[d].LineCount ^ (DMA[d].Repeat ? 0x00 : 0x80));
1658 
1659 			case 0xb: // 0x43xb: ????x
1660 			case 0xf: // 0x43xf: mirror of 0x43xb
1661 				return (DMA[d].UnknownByte);
1662 
1663 			default:
1664 				return (OpenBus);
1665 		}
1666 	}
1667 	else
1668 	{
1669 		uint8	byte;
1670 
1671 		switch (Address)
1672 		{
1673 			case 0x4210: // RDNMI
1674 				byte = Memory.FillRAM[0x4210];
1675 				Memory.FillRAM[0x4210] = Model->_5A22;
1676 				return ((byte & 0x80) | (OpenBus & 0x70) | Model->_5A22);
1677 
1678 			case 0x4211: // TIMEUP
1679 				byte = 0;
1680 				if (CPU.IRQLine)
1681 				{
1682 					byte = 0x80;
1683 					CPU.IRQLine = FALSE;
1684 					CPU.IRQTransition = FALSE;
1685 				}
1686 
1687 				return (byte | (OpenBus & 0x7f));
1688 
1689 			case 0x4212: // HVBJOY
1690 				return (REGISTER_4212() | (OpenBus & 0x3e));
1691 
1692 			case 0x4213: // RDIO
1693 				return (Memory.FillRAM[0x4213]);
1694 
1695 			case 0x4214: // RDDIVL
1696 			case 0x4215: // RDDIVH
1697 			case 0x4216: // RDMPYL
1698 			case 0x4217: // RDMPYH
1699 				return (Memory.FillRAM[Address]);
1700 
1701 			case 0x4218: // JOY1L
1702 			case 0x4219: // JOY1H
1703 			case 0x421a: // JOY2L
1704 			case 0x421b: // JOY2H
1705 			case 0x421c: // JOY3L
1706 			case 0x421d: // JOY3H
1707 			case 0x421e: // JOY4L
1708 			case 0x421f: // JOY4H
1709 			#ifdef SNES_JOY_READ_CALLBACKS
1710 				extern bool8 pad_read;
1711 				if (Memory.FillRAM[0x4200] & 1)
1712 				{
1713 					S9xOnSNESPadRead();
1714 					pad_read = TRUE;
1715 				}
1716 			#endif
1717 				return (Memory.FillRAM[Address]);
1718 
1719 			default:
1720 				if (Settings.SPC7110 && Address >= 0x4800)
1721 					return (S9xGetSPC7110(Address));
1722 				if (Settings.SDD1 && Address >= 0x4800 && Address <= 0x4807)
1723 					return (Memory.FillRAM[Address]);
1724 				return (OpenBus);
1725 		}
1726 	}
1727 }
1728 
S9xResetPPU(void)1729 void S9xResetPPU (void)
1730 {
1731 	S9xSoftResetPPU();
1732 	S9xControlsReset();
1733 	PPU.M7HOFS = 0;
1734 	PPU.M7VOFS = 0;
1735 	PPU.M7byte = 0;
1736 }
1737 
S9xResetPPUFast(void)1738 void S9xResetPPUFast (void)
1739 {
1740 	PPU.RecomputeClipWindows = TRUE;
1741 	IPPU.ColorsChanged = TRUE;
1742 	IPPU.OBJChanged = TRUE;
1743 	memset(IPPU.TileCached[TILE_2BIT], 0, MAX_2BIT_TILES);
1744 	memset(IPPU.TileCached[TILE_4BIT], 0, MAX_4BIT_TILES);
1745 	memset(IPPU.TileCached[TILE_8BIT], 0, MAX_8BIT_TILES);
1746 	memset(IPPU.TileCached[TILE_2BIT_EVEN], 0, MAX_2BIT_TILES);
1747 	memset(IPPU.TileCached[TILE_2BIT_ODD], 0, MAX_2BIT_TILES);
1748 	memset(IPPU.TileCached[TILE_4BIT_EVEN], 0, MAX_4BIT_TILES);
1749 	memset(IPPU.TileCached[TILE_4BIT_ODD], 0, MAX_4BIT_TILES);
1750 }
1751 
S9xSoftResetPPU(void)1752 void S9xSoftResetPPU (void)
1753 {
1754 	S9xControlsSoftReset();
1755 
1756 	PPU.VMA.High = 0;
1757 	PPU.VMA.Increment = 1;
1758 	PPU.VMA.Address = 0;
1759 	PPU.VMA.FullGraphicCount = 0;
1760 	PPU.VMA.Shift = 0;
1761 
1762 	PPU.WRAM = 0;
1763 
1764 	for (int c = 0; c < 4; c++)
1765 	{
1766 		PPU.BG[c].SCBase = 0;
1767 		PPU.BG[c].HOffset = 0;
1768 		PPU.BG[c].VOffset = 0;
1769 		PPU.BG[c].BGSize = 0;
1770 		PPU.BG[c].NameBase = 0;
1771 		PPU.BG[c].SCSize = 0;
1772 	}
1773 
1774 	PPU.BGMode = 0;
1775 	PPU.BG3Priority = 0;
1776 
1777 	PPU.CGFLIP = 0;
1778 	PPU.CGFLIPRead = 0;
1779 	PPU.CGADD = 0;
1780 
1781 	for (int c = 0; c < 256; c++)
1782 	{
1783 		IPPU.Red[c]   = (c & 7) << 2;
1784 		IPPU.Green[c] = ((c >> 3) & 7) << 2;
1785 		IPPU.Blue[c]  = ((c >> 6) & 2) << 3;
1786 		PPU.CGDATA[c] = IPPU.Red[c] | (IPPU.Green[c] << 5) | (IPPU.Blue[c] << 10);
1787 	}
1788 
1789 	for (int c = 0; c < 128; c++)
1790 	{
1791 		PPU.OBJ[c].HPos = 0;
1792 		PPU.OBJ[c].VPos = 0;
1793 		PPU.OBJ[c].HFlip = 0;
1794 		PPU.OBJ[c].VFlip = 0;
1795 		PPU.OBJ[c].Name = 0;
1796 		PPU.OBJ[c].Priority = 0;
1797 		PPU.OBJ[c].Palette = 0;
1798 		PPU.OBJ[c].Size = 0;
1799 	}
1800 
1801 	PPU.OBJThroughMain = FALSE;
1802 	PPU.OBJThroughSub = FALSE;
1803 	PPU.OBJAddition = FALSE;
1804 	PPU.OBJNameBase = 0;
1805 	PPU.OBJNameSelect = 0;
1806 	PPU.OBJSizeSelect = 0;
1807 
1808 	PPU.OAMAddr = 0;
1809 	PPU.SavedOAMAddr = 0;
1810 	PPU.OAMPriorityRotation = 0;
1811 	PPU.OAMFlip = 0;
1812 	PPU.OAMReadFlip = 0;
1813 	PPU.OAMTileAddress = 0;
1814 	PPU.OAMWriteRegister = 0;
1815 	memset(PPU.OAMData, 0, 512 + 32);
1816 
1817 	PPU.FirstSprite = 0;
1818 	PPU.LastSprite = 127;
1819 	PPU.RangeTimeOver = 0;
1820 
1821 	PPU.HTimerEnabled = FALSE;
1822 	PPU.VTimerEnabled = FALSE;
1823 	PPU.HTimerPosition = Timings.H_Max + 1;
1824 	PPU.VTimerPosition = Timings.V_Max + 1;
1825 	PPU.IRQHBeamPos = 0x1ff;
1826 	PPU.IRQVBeamPos = 0x1ff;
1827 
1828 	PPU.HBeamFlip = 0;
1829 	PPU.VBeamFlip = 0;
1830 	PPU.HBeamPosLatched = 0;
1831 	PPU.VBeamPosLatched = 0;
1832 	PPU.GunHLatch = 0;
1833 	PPU.GunVLatch = 1000;
1834 	PPU.HVBeamCounterLatched = 0;
1835 
1836 	PPU.Mode7HFlip = FALSE;
1837 	PPU.Mode7VFlip = FALSE;
1838 	PPU.Mode7Repeat = 0;
1839 	PPU.MatrixA = 0;
1840 	PPU.MatrixB = 0;
1841 	PPU.MatrixC = 0;
1842 	PPU.MatrixD = 0;
1843 	PPU.CentreX = 0;
1844 	PPU.CentreY = 0;
1845 
1846 	PPU.Mosaic = 0;
1847 	PPU.BGMosaic[0] = FALSE;
1848 	PPU.BGMosaic[1] = FALSE;
1849 	PPU.BGMosaic[2] = FALSE;
1850 	PPU.BGMosaic[3] = FALSE;
1851 
1852 	PPU.Window1Left = 1;
1853 	PPU.Window1Right = 0;
1854 	PPU.Window2Left = 1;
1855 	PPU.Window2Right = 0;
1856 	PPU.RecomputeClipWindows = TRUE;
1857 
1858 	for (int c = 0; c < 6; c++)
1859 	{
1860 		PPU.ClipCounts[c] = 0;
1861 		PPU.ClipWindowOverlapLogic[c] = CLIP_OR;
1862 		PPU.ClipWindow1Enable[c] = FALSE;
1863 		PPU.ClipWindow2Enable[c] = FALSE;
1864 		PPU.ClipWindow1Inside[c] = TRUE;
1865 		PPU.ClipWindow2Inside[c] = TRUE;
1866 	}
1867 
1868 	PPU.ForcedBlanking = TRUE;
1869 
1870 	PPU.FixedColourRed = 0;
1871 	PPU.FixedColourGreen = 0;
1872 	PPU.FixedColourBlue = 0;
1873 	PPU.Brightness = 0;
1874 	PPU.ScreenHeight = SNES_HEIGHT;
1875 
1876 	PPU.Need16x8Mulitply = FALSE;
1877 	PPU.BGnxOFSbyte = 0;
1878 
1879 	PPU.HDMA = 0;
1880 	PPU.HDMAEnded = 0;
1881 
1882 	PPU.OpenBus1 = 0;
1883 	PPU.OpenBus2 = 0;
1884 
1885 	for (int c = 0; c < 2; c++)
1886 		memset(&IPPU.Clip[c], 0, sizeof(struct ClipData));
1887 	IPPU.ColorsChanged = TRUE;
1888 	IPPU.OBJChanged = TRUE;
1889 	memset(IPPU.TileCached[TILE_2BIT], 0, MAX_2BIT_TILES);
1890 	memset(IPPU.TileCached[TILE_4BIT], 0, MAX_4BIT_TILES);
1891 	memset(IPPU.TileCached[TILE_8BIT], 0, MAX_8BIT_TILES);
1892 	memset(IPPU.TileCached[TILE_2BIT_EVEN], 0, MAX_2BIT_TILES);
1893 	memset(IPPU.TileCached[TILE_2BIT_ODD], 0,  MAX_2BIT_TILES);
1894 	memset(IPPU.TileCached[TILE_4BIT_EVEN], 0, MAX_4BIT_TILES);
1895 	memset(IPPU.TileCached[TILE_4BIT_ODD], 0,  MAX_4BIT_TILES);
1896 	PPU.VRAMReadBuffer = 0; // XXX: FIXME: anything better?
1897 	GFX.InterlaceFrame = 0;
1898 	GFX.DoInterlace = 0;
1899 	IPPU.Interlace = FALSE;
1900 	IPPU.InterlaceOBJ = FALSE;
1901 	IPPU.DoubleWidthPixels = FALSE;
1902 	IPPU.DoubleHeightPixels = FALSE;
1903 	IPPU.CurrentLine = 0;
1904 	IPPU.PreviousLine = 0;
1905 	IPPU.XB = NULL;
1906 	for (int c = 0; c < 256; c++)
1907 		IPPU.ScreenColors[c] = c;
1908 	IPPU.MaxBrightness = 0;
1909 	IPPU.RenderThisFrame = TRUE;
1910 	IPPU.RenderedScreenWidth = SNES_WIDTH;
1911 	IPPU.RenderedScreenHeight = SNES_HEIGHT;
1912 	IPPU.FrameCount = 0;
1913 	IPPU.RenderedFramesCount = 0;
1914 	IPPU.DisplayedRenderedFrameCount = 0;
1915 	IPPU.SkippedFrames = 0;
1916 	IPPU.FrameSkip = 0;
1917 
1918 	S9xFixColourBrightness();
1919 	S9xBuildDirectColourMaps();
1920 
1921 	for (int c = 0; c < 0x8000; c += 0x100)
1922 		memset(&Memory.FillRAM[c], c >> 8, 0x100);
1923 	memset(&Memory.FillRAM[0x2100], 0, 0x100);
1924 	memset(&Memory.FillRAM[0x4200], 0, 0x100);
1925 	memset(&Memory.FillRAM[0x4000], 0, 0x100);
1926 	// For BS Suttehakkun 2...
1927 	memset(&Memory.FillRAM[0x1000], 0, 0x1000);
1928 
1929 	Memory.FillRAM[0x4201] = Memory.FillRAM[0x4213] = 0xff;
1930 	Memory.FillRAM[0x2126] = Memory.FillRAM[0x2128] = 1;
1931 }
1932