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 | |
29 | struct ClipData |
30 | { |
31 | uint8 Count; |
32 | uint8 DrawMode[6]; |
33 | uint16 Left[6]; |
34 | uint16 Right[6]; |
35 | }; |
36 | |
37 | struct 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 | |
68 | struct 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 | |
80 | struct 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 | |
198 | extern uint16 SignExtend[2]; |
199 | extern struct SPPU PPU; |
200 | extern struct InternalPPU IPPU; |
201 | |
202 | void S9xResetPPU (void); |
203 | void S9xResetPPUFast (void); |
204 | void S9xSoftResetPPU (void); |
205 | void S9xSetPPU (uint8, uint16); |
206 | uint8 S9xGetPPU (uint16); |
207 | void S9xSetCPU (uint8, uint16); |
208 | uint8 S9xGetCPU (uint16); |
209 | void S9xUpdateIRQPositions (bool initial); |
210 | void S9xFixColourBrightness (void); |
211 | void S9xDoAutoJoypad (void); |
212 | |
213 | #include "gfx.h" |
214 | #include "memmap.h" |
215 | |
216 | typedef struct |
217 | { |
218 | uint8 _5C77; |
219 | uint8 _5C78; |
220 | uint8 _5A22; |
221 | } SnesModel; |
222 | |
223 | extern SnesModel *Model; |
224 | extern SnesModel M1SNES; |
225 | extern SnesModel M2SNES; |
226 | |
227 | #define MAX_5C77_VERSION 0x01 |
228 | #define MAX_5C78_VERSION 0x03 |
229 | #define MAX_5A22_VERSION 0x02 |
230 | |
231 | void S9xUpdateScreen (void); |
232 | static inline void FLUSH_REDRAW (void) |
233 | { |
234 | if (IPPU.PreviousLine != IPPU.CurrentLine) |
235 | S9xUpdateScreen(); |
236 | } |
237 | |
238 | static 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 | |
251 | static 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 | |
355 | static 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 | |
392 | static 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 | |
417 | static 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 | |
463 | static 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 | |
499 | static 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 | |
524 | static 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 | |
548 | static 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 | |
573 | static inline void REGISTER_2180 (uint8 Byte) |
574 | { |
575 | Memory.RAM[PPU.WRAM++] = Byte; |
576 | PPU.WRAM &= 0x1ffff; |
577 | } |
578 | |
579 | static 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 | |