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
25extern uint8 *HDMAMemPointers[8];
26
27
28static 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
65static 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
86static 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
121void 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
188void 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
209void 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
990uint8 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
1239void 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
1588uint8 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
1729void S9xResetPPU (void)
1730{
1731 S9xSoftResetPPU();
1732 S9xControlsReset();
1733 PPU.M7HOFS = 0;
1734 PPU.M7VOFS = 0;
1735 PPU.M7byte = 0;
1736}
1737
1738void 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
1752void 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