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#ifndef _PPU_H_
8#define _PPU_H_
9
10#define FIRST_VISIBLE_LINE 1
11
12#define TILE_2BIT 0
13#define TILE_4BIT 1
14#define TILE_8BIT 2
15#define TILE_2BIT_EVEN 3
16#define TILE_2BIT_ODD 4
17#define TILE_4BIT_EVEN 5
18#define TILE_4BIT_ODD 6
19
20#define MAX_2BIT_TILES 4096
21#define MAX_4BIT_TILES 2048
22#define MAX_8BIT_TILES 1024
23
24#define CLIP_OR 0
25#define CLIP_AND 1
26#define CLIP_XOR 2
27#define CLIP_XNOR 3
28
29struct ClipData
30{
31 uint8 Count;
32 uint8 DrawMode[6];
33 uint16 Left[6];
34 uint16 Right[6];
35};
36
37struct InternalPPU
38{
39 struct ClipData Clip[2][6];
40 bool8 ColorsChanged;
41 bool8 OBJChanged;
42 uint8 *TileCache[7];
43 uint8 *TileCached[7];
44 bool8 Interlace;
45 bool8 InterlaceOBJ;
46 bool8 PseudoHires;
47 bool8 DoubleWidthPixels;
48 bool8 DoubleHeightPixels;
49 int CurrentLine;
50 int PreviousLine;
51 uint8 *XB;
52 uint32 Red[256];
53 uint32 Green[256];
54 uint32 Blue[256];
55 uint16 ScreenColors[256];
56 uint8 MaxBrightness;
57 bool8 RenderThisFrame;
58 int RenderedScreenWidth;
59 int RenderedScreenHeight;
60 uint32 FrameCount;
61 uint32 RenderedFramesCount;
62 uint32 DisplayedRenderedFrameCount;
63 uint32 TotalEmulatedFrames;
64 uint32 SkippedFrames;
65 uint32 FrameSkip;
66};
67
68struct SOBJ
69{
70 int16 HPos;
71 uint16 VPos;
72 uint8 HFlip;
73 uint8 VFlip;
74 uint16 Name;
75 uint8 Priority;
76 uint8 Palette;
77 uint8 Size;
78};
79
80struct SPPU
81{
82 struct
83 {
84 bool8 High;
85 uint8 Increment;
86 uint16 Address;
87 uint16 Mask1;
88 uint16 FullGraphicCount;
89 uint16 Shift;
90 } VMA;
91
92 uint32 WRAM;
93
94 struct
95 {
96 uint16 SCBase;
97 uint16 HOffset;
98 uint16 VOffset;
99 uint8 BGSize;
100 uint16 NameBase;
101 uint16 SCSize;
102 } BG[4];
103
104 uint8 BGMode;
105 uint8 BG3Priority;
106
107 bool8 CGFLIP;
108 uint8 CGFLIPRead;
109 uint8 CGADD;
110 uint8 CGSavedByte;
111 uint16 CGDATA[256];
112
113 struct SOBJ OBJ[128];
114 bool8 OBJThroughMain;
115 bool8 OBJThroughSub;
116 bool8 OBJAddition;
117 uint16 OBJNameBase;
118 uint16 OBJNameSelect;
119 uint8 OBJSizeSelect;
120
121 uint16 OAMAddr;
122 uint16 SavedOAMAddr;
123 uint8 OAMPriorityRotation;
124 uint8 OAMFlip;
125 uint8 OAMReadFlip;
126 uint16 OAMTileAddress;
127 uint16 OAMWriteRegister;
128 uint8 OAMData[512 + 32];
129
130 uint8 FirstSprite;
131 uint8 LastSprite;
132 uint8 RangeTimeOver;
133
134 bool8 HTimerEnabled;
135 bool8 VTimerEnabled;
136 short HTimerPosition;
137 short VTimerPosition;
138 uint16 IRQHBeamPos;
139 uint16 IRQVBeamPos;
140
141 uint8 HBeamFlip;
142 uint8 VBeamFlip;
143 uint16 HBeamPosLatched;
144 uint16 VBeamPosLatched;
145 uint16 GunHLatch;
146 uint16 GunVLatch;
147 uint8 HVBeamCounterLatched;
148
149 bool8 Mode7HFlip;
150 bool8 Mode7VFlip;
151 uint8 Mode7Repeat;
152 short MatrixA;
153 short MatrixB;
154 short MatrixC;
155 short MatrixD;
156 short CentreX;
157 short CentreY;
158 short M7HOFS;
159 short M7VOFS;
160
161 uint8 Mosaic;
162 uint8 MosaicStart;
163 bool8 BGMosaic[4];
164
165 uint8 Window1Left;
166 uint8 Window1Right;
167 uint8 Window2Left;
168 uint8 Window2Right;
169 bool8 RecomputeClipWindows;
170 uint8 ClipCounts[6];
171 uint8 ClipWindowOverlapLogic[6];
172 uint8 ClipWindow1Enable[6];
173 uint8 ClipWindow2Enable[6];
174 bool8 ClipWindow1Inside[6];
175 bool8 ClipWindow2Inside[6];
176
177 bool8 ForcedBlanking;
178
179 uint8 FixedColourRed;
180 uint8 FixedColourGreen;
181 uint8 FixedColourBlue;
182 uint8 Brightness;
183 uint16 ScreenHeight;
184
185 bool8 Need16x8Mulitply;
186 uint8 BGnxOFSbyte;
187 uint8 M7byte;
188
189 uint8 HDMA;
190 uint8 HDMAEnded;
191
192 uint8 OpenBus1;
193 uint8 OpenBus2;
194
195 uint16 VRAMReadBuffer;
196};
197
198extern uint16 SignExtend[2];
199extern struct SPPU PPU;
200extern struct InternalPPU IPPU;
201
202void S9xResetPPU (void);
203void S9xResetPPUFast (void);
204void S9xSoftResetPPU (void);
205void S9xSetPPU (uint8, uint16);
206uint8 S9xGetPPU (uint16);
207void S9xSetCPU (uint8, uint16);
208uint8 S9xGetCPU (uint16);
209void S9xUpdateIRQPositions (bool initial);
210void S9xFixColourBrightness (void);
211void S9xDoAutoJoypad (void);
212
213#include "gfx.h"
214#include "memmap.h"
215
216typedef struct
217{
218 uint8 _5C77;
219 uint8 _5C78;
220 uint8 _5A22;
221} SnesModel;
222
223extern SnesModel *Model;
224extern SnesModel M1SNES;
225extern SnesModel M2SNES;
226
227#define MAX_5C77_VERSION 0x01
228#define MAX_5C78_VERSION 0x03
229#define MAX_5A22_VERSION 0x02
230
231void S9xUpdateScreen (void);
232static inline void FLUSH_REDRAW (void)
233{
234 if (IPPU.PreviousLine != IPPU.CurrentLine)
235 S9xUpdateScreen();
236}
237
238static inline void S9xUpdateVRAMReadBuffer()
239{
240 if (PPU.VMA.FullGraphicCount)
241 {
242 uint32 addr = PPU.VMA.Address;
243 uint32 rem = addr & PPU.VMA.Mask1;
244 uint32 address = (addr & ~PPU.VMA.Mask1) + (rem >> PPU.VMA.Shift) + ((rem & (PPU.VMA.FullGraphicCount - 1)) << 3);
245 PPU.VRAMReadBuffer = READ_WORD(Memory.VRAM + ((address << 1) & 0xffff));
246 }
247 else
248 PPU.VRAMReadBuffer = READ_WORD(Memory.VRAM + ((PPU.VMA.Address << 1) & 0xffff));
249}
250
251static inline void REGISTER_2104 (uint8 Byte)
252{
253 if (!(PPU.OAMFlip & 1))
254 {
255 PPU.OAMWriteRegister &= 0xff00;
256 PPU.OAMWriteRegister |= Byte;
257 }
258
259 if (PPU.OAMAddr & 0x100)
260 {
261 int addr = ((PPU.OAMAddr & 0x10f) << 1) + (PPU.OAMFlip & 1);
262 if (Byte != PPU.OAMData[addr])
263 {
264 FLUSH_REDRAW();
265 PPU.OAMData[addr] = Byte;
266 IPPU.OBJChanged = TRUE;
267
268 // X position high bit, and sprite size (x4)
269 struct SOBJ *pObj = &PPU.OBJ[(addr & 0x1f) * 4];
270 pObj->HPos = (pObj->HPos & 0xFF) | SignExtend[(Byte >> 0) & 1];
271 pObj++->Size = Byte & 2;
272 pObj->HPos = (pObj->HPos & 0xFF) | SignExtend[(Byte >> 2) & 1];
273 pObj++->Size = Byte & 8;
274 pObj->HPos = (pObj->HPos & 0xFF) | SignExtend[(Byte >> 4) & 1];
275 pObj++->Size = Byte & 32;
276 pObj->HPos = (pObj->HPos & 0xFF) | SignExtend[(Byte >> 6) & 1];
277 pObj->Size = Byte & 128;
278 }
279
280 }
281 else if (PPU.OAMFlip & 1)
282 {
283 PPU.OAMWriteRegister &= 0x00ff;
284 uint8 lowbyte = (uint8) (PPU.OAMWriteRegister);
285 uint8 highbyte = Byte;
286 PPU.OAMWriteRegister |= Byte << 8;
287
288 int addr = (PPU.OAMAddr << 1);
289 if (lowbyte != PPU.OAMData[addr] || highbyte != PPU.OAMData[addr + 1])
290 {
291 FLUSH_REDRAW();
292 PPU.OAMData[addr] = lowbyte;
293 PPU.OAMData[addr + 1] = highbyte;
294 IPPU.OBJChanged = TRUE;
295 if (addr & 2)
296 {
297 // Tile
298 PPU.OBJ[addr = PPU.OAMAddr >> 1].Name = PPU.OAMWriteRegister & 0x1ff;
299 // priority, h and v flip.
300 PPU.OBJ[addr].Palette = (highbyte >> 1) & 7;
301 PPU.OBJ[addr].Priority = (highbyte >> 4) & 3;
302 PPU.OBJ[addr].HFlip = (highbyte >> 6) & 1;
303 PPU.OBJ[addr].VFlip = (highbyte >> 7) & 1;
304 }
305 else
306 {
307 // X position (low)
308 PPU.OBJ[addr = PPU.OAMAddr >> 1].HPos &= 0xff00;
309 PPU.OBJ[addr].HPos |= lowbyte;
310 // Sprite Y position
311 PPU.OBJ[addr].VPos = highbyte;
312 }
313 }
314 }
315
316 PPU.OAMFlip ^= 1;
317 if (!(PPU.OAMFlip & 1))
318 {
319 ++PPU.OAMAddr;
320 PPU.OAMAddr &= 0x1ff;
321 if (PPU.OAMPriorityRotation && PPU.FirstSprite != (PPU.OAMAddr >> 1))
322 {
323 PPU.FirstSprite = (PPU.OAMAddr & 0xfe) >> 1;
324 IPPU.OBJChanged = TRUE;
325 }
326 }
327 else
328 {
329 if (PPU.OAMPriorityRotation && (PPU.OAMAddr & 1))
330 IPPU.OBJChanged = TRUE;
331 }
332}
333
334// This code is correct, however due to Snes9x's inaccurate timings, some games might be broken by this chage. :(
335#ifdef DEBUGGER
336#define CHECK_INBLANK() \
337 if (!PPU.ForcedBlanking && CPU.V_Counter < PPU.ScreenHeight + FIRST_VISIBLE_LINE) \
338 { \
339 printf("Invalid VRAM acess at (%04d, %04d) blank:%d\n", CPU.Cycles, CPU.V_Counter, PPU.ForcedBlanking); \
340 if (Settings.BlockInvalidVRAMAccess) \
341 { \
342 PPU.VMA.Address += !PPU.VMA.High ? PPU.VMA.Increment : 0; \
343 return; \
344 } \
345 }
346#else
347#define CHECK_INBLANK() \
348 if (Settings.BlockInvalidVRAMAccess && !PPU.ForcedBlanking && CPU.V_Counter < PPU.ScreenHeight + FIRST_VISIBLE_LINE) \
349 { \
350 PPU.VMA.Address += !PPU.VMA.High ? PPU.VMA.Increment : 0; \
351 return; \
352 }
353#endif
354
355static inline void REGISTER_2118 (uint8 Byte)
356{
357 CHECK_INBLANK();
358
359 uint32 address;
360
361 if (PPU.VMA.FullGraphicCount)
362 {
363 uint32 rem = PPU.VMA.Address & PPU.VMA.Mask1;
364 address = (((PPU.VMA.Address & ~PPU.VMA.Mask1) + (rem >> PPU.VMA.Shift) + ((rem & (PPU.VMA.FullGraphicCount - 1)) << 3)) << 1) & 0xffff;
365 Memory.VRAM[address] = Byte;
366 }
367 else
368 Memory.VRAM[address = (PPU.VMA.Address << 1) & 0xffff] = Byte;
369
370 IPPU.TileCached[TILE_2BIT][address >> 4] = FALSE;
371 IPPU.TileCached[TILE_4BIT][address >> 5] = FALSE;
372 IPPU.TileCached[TILE_8BIT][address >> 6] = FALSE;
373 IPPU.TileCached[TILE_2BIT_EVEN][address >> 4] = FALSE;
374 IPPU.TileCached[TILE_2BIT_EVEN][((address >> 4) - 1) & (MAX_2BIT_TILES - 1)] = FALSE;
375 IPPU.TileCached[TILE_2BIT_ODD] [address >> 4] = FALSE;
376 IPPU.TileCached[TILE_2BIT_ODD] [((address >> 4) - 1) & (MAX_2BIT_TILES - 1)] = FALSE;
377 IPPU.TileCached[TILE_4BIT_EVEN][address >> 5] = FALSE;
378 IPPU.TileCached[TILE_4BIT_EVEN][((address >> 5) - 1) & (MAX_4BIT_TILES - 1)] = FALSE;
379 IPPU.TileCached[TILE_4BIT_ODD] [address >> 5] = FALSE;
380 IPPU.TileCached[TILE_4BIT_ODD] [((address >> 5) - 1) & (MAX_4BIT_TILES - 1)] = FALSE;
381
382 if (!PPU.VMA.High)
383 {
384 #ifdef DEBUGGER
385 if (Settings.TraceVRAM && !CPU.InDMAorHDMA)
386 printf("VRAM write byte: $%04X (%d, %d)\n", PPU.VMA.Address, Memory.FillRAM[0x2115] & 3, (Memory.FillRAM[0x2115] & 0x0c) >> 2);
387 #endif
388 PPU.VMA.Address += PPU.VMA.Increment;
389 }
390}
391
392static inline void REGISTER_2118_tile (uint8 Byte)
393{
394 CHECK_INBLANK();
395
396 uint32 rem = PPU.VMA.Address & PPU.VMA.Mask1;
397 uint32 address = (((PPU.VMA.Address & ~PPU.VMA.Mask1) + (rem >> PPU.VMA.Shift) + ((rem & (PPU.VMA.FullGraphicCount - 1)) << 3)) << 1) & 0xffff;
398
399 Memory.VRAM[address] = Byte;
400
401 IPPU.TileCached[TILE_2BIT][address >> 4] = FALSE;
402 IPPU.TileCached[TILE_4BIT][address >> 5] = FALSE;
403 IPPU.TileCached[TILE_8BIT][address >> 6] = FALSE;
404 IPPU.TileCached[TILE_2BIT_EVEN][address >> 4] = FALSE;
405 IPPU.TileCached[TILE_2BIT_EVEN][((address >> 4) - 1) & (MAX_2BIT_TILES - 1)] = FALSE;
406 IPPU.TileCached[TILE_2BIT_ODD] [address >> 4] = FALSE;
407 IPPU.TileCached[TILE_2BIT_ODD] [((address >> 4) - 1) & (MAX_2BIT_TILES - 1)] = FALSE;
408 IPPU.TileCached[TILE_4BIT_EVEN][address >> 5] = FALSE;
409 IPPU.TileCached[TILE_4BIT_EVEN][((address >> 5) - 1) & (MAX_4BIT_TILES - 1)] = FALSE;
410 IPPU.TileCached[TILE_4BIT_ODD] [address >> 5] = FALSE;
411 IPPU.TileCached[TILE_4BIT_ODD] [((address >> 5) - 1) & (MAX_4BIT_TILES - 1)] = FALSE;
412
413 if (!PPU.VMA.High)
414 PPU.VMA.Address += PPU.VMA.Increment;
415}
416
417static inline void REGISTER_2118_linear (uint8 Byte)
418{
419 CHECK_INBLANK();
420
421 uint32 address;
422
423 Memory.VRAM[address = (PPU.VMA.Address << 1) & 0xffff] = Byte;
424
425 IPPU.TileCached[TILE_2BIT][address >> 4] = FALSE;
426 IPPU.TileCached[TILE_4BIT][address >> 5] = FALSE;
427 IPPU.TileCached[TILE_8BIT][address >> 6] = FALSE;
428 IPPU.TileCached[TILE_2BIT_EVEN][address >> 4] = FALSE;
429 IPPU.TileCached[TILE_2BIT_EVEN][((address >> 4) - 1) & (MAX_2BIT_TILES - 1)] = FALSE;
430 IPPU.TileCached[TILE_2BIT_ODD] [address >> 4] = FALSE;
431 IPPU.TileCached[TILE_2BIT_ODD] [((address >> 4) - 1) & (MAX_2BIT_TILES - 1)] = FALSE;
432 IPPU.TileCached[TILE_4BIT_EVEN][address >> 5] = FALSE;
433 IPPU.TileCached[TILE_4BIT_EVEN][((address >> 5) - 1) & (MAX_4BIT_TILES - 1)] = FALSE;
434 IPPU.TileCached[TILE_4BIT_ODD] [address >> 5] = FALSE;
435 IPPU.TileCached[TILE_4BIT_ODD] [((address >> 5) - 1) & (MAX_4BIT_TILES - 1)] = FALSE;
436
437 if (!PPU.VMA.High)
438 PPU.VMA.Address += PPU.VMA.Increment;
439}
440
441#undef CHECK_INBLANK
442#ifdef DEBUGGER
443#define CHECK_INBLANK() \
444 if (!PPU.ForcedBlanking && CPU.V_Counter < PPU.ScreenHeight + FIRST_VISIBLE_LINE) \
445 { \
446 printf("Invalid VRAM acess at (%04d, %04d) blank:%d\n", CPU.Cycles, CPU.V_Counter, PPU.ForcedBlanking); \
447 if (Settings.BlockInvalidVRAMAccess) \
448 { \
449 PPU.VMA.Address += PPU.VMA.High ? PPU.VMA.Increment : 0; \
450 return; \
451 } \
452 }
453#else
454#define CHECK_INBLANK() \
455 if (Settings.BlockInvalidVRAMAccess && !PPU.ForcedBlanking && CPU.V_Counter < PPU.ScreenHeight + FIRST_VISIBLE_LINE) \
456 { \
457 PPU.VMA.Address += PPU.VMA.High ? PPU.VMA.Increment : 0; \
458 return; \
459 }
460#endif
461
462
463static inline void REGISTER_2119 (uint8 Byte)
464{
465 CHECK_INBLANK();
466 uint32 address;
467
468 if (PPU.VMA.FullGraphicCount)
469 {
470 uint32 rem = PPU.VMA.Address & PPU.VMA.Mask1;
471 address = ((((PPU.VMA.Address & ~PPU.VMA.Mask1) + (rem >> PPU.VMA.Shift) + ((rem & (PPU.VMA.FullGraphicCount - 1)) << 3)) << 1) + 1) & 0xffff;
472 Memory.VRAM[address] = Byte;
473 }
474 else
475 Memory.VRAM[address = ((PPU.VMA.Address << 1) + 1) & 0xffff] = Byte;
476
477 IPPU.TileCached[TILE_2BIT][address >> 4] = FALSE;
478 IPPU.TileCached[TILE_4BIT][address >> 5] = FALSE;
479 IPPU.TileCached[TILE_8BIT][address >> 6] = FALSE;
480 IPPU.TileCached[TILE_2BIT_EVEN][address >> 4] = FALSE;
481 IPPU.TileCached[TILE_2BIT_EVEN][((address >> 4) - 1) & (MAX_2BIT_TILES - 1)] = FALSE;
482 IPPU.TileCached[TILE_2BIT_ODD] [address >> 4] = FALSE;
483 IPPU.TileCached[TILE_2BIT_ODD] [((address >> 4) - 1) & (MAX_2BIT_TILES - 1)] = FALSE;
484 IPPU.TileCached[TILE_4BIT_EVEN][address >> 5] = FALSE;
485 IPPU.TileCached[TILE_4BIT_EVEN][((address >> 5) - 1) & (MAX_4BIT_TILES - 1)] = FALSE;
486 IPPU.TileCached[TILE_4BIT_ODD] [address >> 5] = FALSE;
487 IPPU.TileCached[TILE_4BIT_ODD] [((address >> 5) - 1) & (MAX_4BIT_TILES - 1)] = FALSE;
488
489 if (PPU.VMA.High)
490 {
491 #ifdef DEBUGGER
492 if (Settings.TraceVRAM && !CPU.InDMAorHDMA)
493 printf("VRAM write word: $%04X (%d, %d)\n", PPU.VMA.Address, Memory.FillRAM[0x2115] & 3, (Memory.FillRAM[0x2115] & 0x0c) >> 2);
494 #endif
495 PPU.VMA.Address += PPU.VMA.Increment;
496 }
497}
498
499static inline void REGISTER_2119_tile (uint8 Byte)
500{
501 CHECK_INBLANK();
502
503 uint32 rem = PPU.VMA.Address & PPU.VMA.Mask1;
504 uint32 address = ((((PPU.VMA.Address & ~PPU.VMA.Mask1) + (rem >> PPU.VMA.Shift) + ((rem & (PPU.VMA.FullGraphicCount - 1)) << 3)) << 1) + 1) & 0xffff;
505
506 Memory.VRAM[address] = Byte;
507
508 IPPU.TileCached[TILE_2BIT][address >> 4] = FALSE;
509 IPPU.TileCached[TILE_4BIT][address >> 5] = FALSE;
510 IPPU.TileCached[TILE_8BIT][address >> 6] = FALSE;
511 IPPU.TileCached[TILE_2BIT_EVEN][address >> 4] = FALSE;
512 IPPU.TileCached[TILE_2BIT_EVEN][((address >> 4) - 1) & (MAX_2BIT_TILES - 1)] = FALSE;
513 IPPU.TileCached[TILE_2BIT_ODD] [address >> 4] = FALSE;
514 IPPU.TileCached[TILE_2BIT_ODD] [((address >> 4) - 1) & (MAX_2BIT_TILES - 1)] = FALSE;
515 IPPU.TileCached[TILE_4BIT_EVEN][address >> 5] = FALSE;
516 IPPU.TileCached[TILE_4BIT_EVEN][((address >> 5) - 1) & (MAX_4BIT_TILES - 1)] = FALSE;
517 IPPU.TileCached[TILE_4BIT_ODD] [address >> 5] = FALSE;
518 IPPU.TileCached[TILE_4BIT_ODD] [((address >> 5) - 1) & (MAX_4BIT_TILES - 1)] = FALSE;
519
520 if (PPU.VMA.High)
521 PPU.VMA.Address += PPU.VMA.Increment;
522}
523
524static inline void REGISTER_2119_linear (uint8 Byte)
525{
526 CHECK_INBLANK();
527
528 uint32 address;
529
530 Memory.VRAM[address = ((PPU.VMA.Address << 1) + 1) & 0xffff] = Byte;
531
532 IPPU.TileCached[TILE_2BIT][address >> 4] = FALSE;
533 IPPU.TileCached[TILE_4BIT][address >> 5] = FALSE;
534 IPPU.TileCached[TILE_8BIT][address >> 6] = FALSE;
535 IPPU.TileCached[TILE_2BIT_EVEN][address >> 4] = FALSE;
536 IPPU.TileCached[TILE_2BIT_EVEN][((address >> 4) - 1) & (MAX_2BIT_TILES - 1)] = FALSE;
537 IPPU.TileCached[TILE_2BIT_ODD] [address >> 4] = FALSE;
538 IPPU.TileCached[TILE_2BIT_ODD] [((address >> 4) - 1) & (MAX_2BIT_TILES - 1)] = FALSE;
539 IPPU.TileCached[TILE_4BIT_EVEN][address >> 5] = FALSE;
540 IPPU.TileCached[TILE_4BIT_EVEN][((address >> 5) - 1) & (MAX_4BIT_TILES - 1)] = FALSE;
541 IPPU.TileCached[TILE_4BIT_ODD] [address >> 5] = FALSE;
542 IPPU.TileCached[TILE_4BIT_ODD] [((address >> 5) - 1) & (MAX_4BIT_TILES - 1)] = FALSE;
543
544 if (PPU.VMA.High)
545 PPU.VMA.Address += PPU.VMA.Increment;
546}
547
548static inline void REGISTER_2122 (uint8 Byte)
549{
550 if (PPU.CGFLIP)
551 {
552 if ((Byte & 0x7f) != (PPU.CGDATA[PPU.CGADD] >> 8) || PPU.CGSavedByte != (uint8) (PPU.CGDATA[PPU.CGADD] & 0xff))
553 {
554 FLUSH_REDRAW();
555 PPU.CGDATA[PPU.CGADD] = (Byte & 0x7f) << 8 | PPU.CGSavedByte;
556 IPPU.ColorsChanged = TRUE;
557 IPPU.Red[PPU.CGADD] = IPPU.XB[PPU.CGSavedByte & 0x1f];
558 IPPU.Blue[PPU.CGADD] = IPPU.XB[(Byte >> 2) & 0x1f];
559 IPPU.Green[PPU.CGADD] = IPPU.XB[(PPU.CGDATA[PPU.CGADD] >> 5) & 0x1f];
560 IPPU.ScreenColors[PPU.CGADD] = (uint16) BUILD_PIXEL(IPPU.Red[PPU.CGADD], IPPU.Green[PPU.CGADD], IPPU.Blue[PPU.CGADD]);
561 }
562
563 PPU.CGADD++;
564 }
565 else
566 {
567 PPU.CGSavedByte = Byte;
568 }
569
570 PPU.CGFLIP ^= 1;
571}
572
573static inline void REGISTER_2180 (uint8 Byte)
574{
575 Memory.RAM[PPU.WRAM++] = Byte;
576 PPU.WRAM &= 0x1ffff;
577}
578
579static inline uint8 REGISTER_4212 (void)
580{
581 uint8 byte = 0;
582
583 if ((CPU.V_Counter >= PPU.ScreenHeight + FIRST_VISIBLE_LINE) && (CPU.V_Counter < PPU.ScreenHeight + FIRST_VISIBLE_LINE + 3))
584 byte = 1;
585 if ((CPU.Cycles < Timings.HBlankEnd) || (CPU.Cycles >= Timings.HBlankStart))
586 byte |= 0x40;
587 if (CPU.V_Counter >= PPU.ScreenHeight + FIRST_VISIBLE_LINE)
588 byte |= 0x80;
589
590 return (byte);
591}
592
593#endif
594