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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
1729 | void S9xResetPPU (void) |
1730 | { |
1731 | S9xSoftResetPPU(); |
1732 | S9xControlsReset(); |
1733 | PPU.M7HOFS = 0; |
1734 | PPU.M7VOFS = 0; |
1735 | PPU.M7byte = 0; |
1736 | } |
1737 | |
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 | |
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 | |