1 /***********************************************************************************
2 Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3
4 (c) Copyright 1996 - 2002 Gary Henderson (gary.henderson@ntlworld.com),
5 Jerremy Koot (jkoot@snes9x.com)
6
7 (c) Copyright 2002 - 2004 Matthew Kendora
8
9 (c) Copyright 2002 - 2005 Peter Bortas (peter@bortas.org)
10
11 (c) Copyright 2004 - 2005 Joel Yliluoma (http://iki.fi/bisqwit/)
12
13 (c) Copyright 2001 - 2006 John Weidman (jweidman@slip.net)
14
15 (c) Copyright 2002 - 2006 funkyass (funkyass@spam.shaw.ca),
16 Kris Bleakley (codeviolation@hotmail.com)
17
18 (c) Copyright 2002 - 2010 Brad Jorsch (anomie@users.sourceforge.net),
19 Nach (n-a-c-h@users.sourceforge.net),
20 zones (kasumitokoduck@yahoo.com)
21
22 (c) Copyright 2006 - 2007 nitsuja
23
24 (c) Copyright 2009 - 2010 BearOso,
25 OV2
26
27
28 BS-X C emulator code
29 (c) Copyright 2005 - 2006 Dreamer Nom,
30 zones
31
32 C4 x86 assembler and some C emulation code
33 (c) Copyright 2000 - 2003 _Demo_ (_demo_@zsnes.com),
34 Nach,
35 zsKnight (zsknight@zsnes.com)
36
37 C4 C++ code
38 (c) Copyright 2003 - 2006 Brad Jorsch,
39 Nach
40
41 DSP-1 emulator code
42 (c) Copyright 1998 - 2006 _Demo_,
43 Andreas Naive (andreasnaive@gmail.com),
44 Gary Henderson,
45 Ivar (ivar@snes9x.com),
46 John Weidman,
47 Kris Bleakley,
48 Matthew Kendora,
49 Nach,
50 neviksti (neviksti@hotmail.com)
51
52 DSP-2 emulator code
53 (c) Copyright 2003 John Weidman,
54 Kris Bleakley,
55 Lord Nightmare (lord_nightmare@users.sourceforge.net),
56 Matthew Kendora,
57 neviksti
58
59 DSP-3 emulator code
60 (c) Copyright 2003 - 2006 John Weidman,
61 Kris Bleakley,
62 Lancer,
63 z80 gaiden
64
65 DSP-4 emulator code
66 (c) Copyright 2004 - 2006 Dreamer Nom,
67 John Weidman,
68 Kris Bleakley,
69 Nach,
70 z80 gaiden
71
72 OBC1 emulator code
73 (c) Copyright 2001 - 2004 zsKnight,
74 pagefault (pagefault@zsnes.com),
75 Kris Bleakley
76 Ported from x86 assembler to C by sanmaiwashi
77
78 SPC7110 and RTC C++ emulator code used in 1.39-1.51
79 (c) Copyright 2002 Matthew Kendora with research by
80 zsKnight,
81 John Weidman,
82 Dark Force
83
84 SPC7110 and RTC C++ emulator code used in 1.52+
85 (c) Copyright 2009 byuu,
86 neviksti
87
88 S-DD1 C emulator code
89 (c) Copyright 2003 Brad Jorsch with research by
90 Andreas Naive,
91 John Weidman
92
93 S-RTC C emulator code
94 (c) Copyright 2001 - 2006 byuu,
95 John Weidman
96
97 ST010 C++ emulator code
98 (c) Copyright 2003 Feather,
99 John Weidman,
100 Kris Bleakley,
101 Matthew Kendora
102
103 Super FX x86 assembler emulator code
104 (c) Copyright 1998 - 2003 _Demo_,
105 pagefault,
106 zsKnight
107
108 Super FX C emulator code
109 (c) Copyright 1997 - 1999 Ivar,
110 Gary Henderson,
111 John Weidman
112
113 Sound emulator code used in 1.5-1.51
114 (c) Copyright 1998 - 2003 Brad Martin
115 (c) Copyright 1998 - 2006 Charles Bilyue'
116
117 Sound emulator code used in 1.52+
118 (c) Copyright 2004 - 2007 Shay Green (gblargg@gmail.com)
119
120 SH assembler code partly based on x86 assembler code
121 (c) Copyright 2002 - 2004 Marcus Comstedt (marcus@mc.pp.se)
122
123 2xSaI filter
124 (c) Copyright 1999 - 2001 Derek Liauw Kie Fa
125
126 HQ2x, HQ3x, HQ4x filters
127 (c) Copyright 2003 Maxim Stepin (maxim@hiend3d.com)
128
129 NTSC filter
130 (c) Copyright 2006 - 2007 Shay Green
131
132 GTK+ GUI code
133 (c) Copyright 2004 - 2010 BearOso
134
135 Win32 GUI code
136 (c) Copyright 2003 - 2006 blip,
137 funkyass,
138 Matthew Kendora,
139 Nach,
140 nitsuja
141 (c) Copyright 2009 - 2010 OV2
142
143 Mac OS GUI code
144 (c) Copyright 1998 - 2001 John Stiles
145 (c) Copyright 2001 - 2010 zones
146
147 (c) Copyright 2010 - 2016 Daniel De Matteis. (UNDER NO CIRCUMSTANCE
148 WILL COMMERCIAL RIGHTS EVER BE APPROPRIATED TO ANY PARTY)
149
150 Specific ports contains the works of other authors. See headers in
151 individual files.
152
153
154 Snes9x homepage: http://www.snes9x.com/
155
156 Permission to use, copy, modify and/or distribute Snes9x in both binary
157 and source form, for non-commercial purposes, is hereby granted without
158 fee, providing that this license information and copyright notice appear
159 with all copies and any derived work.
160
161 This software is provided 'as-is', without any express or implied
162 warranty. In no event shall the authors be held liable for any damages
163 arising from the use of this software or it's derivatives.
164
165 Snes9x is freeware for PERSONAL USE only. Commercial users should
166 seek permission of the copyright holders first. Commercial use includes,
167 but is not limited to, charging money for Snes9x or software derived from
168 Snes9x, including Snes9x or derivatives in commercial game bundles, and/or
169 using Snes9x as a promotion for your commercial product.
170
171 The copyright holders request that bug fixes and improvements to the code
172 should be forwarded to them so everyone can benefit from the modifications
173 in future versions.
174
175 Super NES and Super Nintendo Entertainment System are trademarks of
176 Nintendo Co., Limited and its subsidiary companies.
177 ***********************************************************************************/
178
179 #include <string.h>
180 #include "snes9x.h"
181 #include "memmap.h"
182 #include "getset.h"
183 #include "cpuops.h"
184 #include "dma.h"
185 #include "apu.h"
186 #include "fxinst.h"
187 #include "fxemu.h"
188 #include "boolean.h"
189 #include "controls.h"
190 #include "cheats.h"
191 #include "ppu.h"
192 #include "snapshot.h"
193
194 extern struct SLineData LineData[240];
195 extern struct SLineMatrixData LineMatrixData[240];
196 extern uint8 *HDMAMemPointers[8];
197 static uint32 idle_loop_target_pc;
198 static bool8 idle_loop_elimination_enable;
199
200 #ifdef LAGFIX
201 bool8 finishedFrame = false;
202 #endif
203
S9xMainLoop(void)204 void S9xMainLoop (void)
205 {
206 do
207 {
208 #ifdef LAGFIX
209 do
210 {
211 #endif
212 register uint8 Op;
213 register struct SOpcodes *Opcodes;
214
215 /* Speedhack - skip idle loop if exists. */
216 if (idle_loop_elimination_enable &&
217 (Registers.PBPC == idle_loop_target_pc) && (CPU.Cycles < CPU.NextEvent))
218 CPU.Cycles = CPU.NextEvent;
219
220 if (CPU.Flags)
221 {
222 if (CPU.Flags & NMI_FLAG)
223 {
224 if (Timings.NMITriggerPos <= CPU.Cycles)
225 {
226 CPU.Flags &= ~NMI_FLAG;
227 Timings.NMITriggerPos = 0xffff;
228 if (CPU.WaitingForInterrupt)
229 {
230 CPU.WaitingForInterrupt = FALSE;
231 Registers.PCw++;
232 }
233
234 S9xOpcode_NMI();
235 }
236 }
237
238
239 if (CPU.Flags & IRQ_FLAG)
240 {
241 if (CPU.IRQPending)
242 CPU.IRQPending--; /* FIXME: In case of IRQ during WRAM refresh */
243 else
244 {
245 if (CPU.WaitingForInterrupt)
246 {
247 CPU.WaitingForInterrupt = FALSE;
248 Registers.PCw++;
249 }
250
251 if (CPU.IRQActive)
252 {
253 /* in IRQ handler $4211 is supposed to be read, so IRQ_FLAG should be cleared. */
254 if (!CheckFlag(IRQ))
255 S9xOpcode_IRQ();
256 }
257 else
258 CPU.Flags &= ~IRQ_FLAG;
259 }
260 }
261
262 if (CPU.Flags & SCAN_KEYS_FLAG)
263 break;
264
265 }
266
267 Opcodes = S9xOpcodesSlow;
268
269 CPU.PrevCycles = CPU.Cycles;
270
271 if (CPU.PCBase)
272 {
273 Op = CPU.PCBase[Registers.PCw];
274 CPU.Cycles += CPU.MemSpeed;
275 Opcodes = ICPU.S9xOpcodes;
276 }
277 else
278 {
279 Op = S9xGetByte(Registers.PBPC);
280 OpenBus = Op;
281 }
282
283 if ((Registers.PCw & MEMMAP_MASK) + ICPU.S9xOpLengths[Op] >= MEMMAP_BLOCK_SIZE)
284 {
285 uint8 *oldPCBase = CPU.PCBase;
286
287 CPU.PCBase = S9xGetBasePointer(ICPU.ShiftedPB + ((uint16) (Registers.PCw + 4)));
288 if (oldPCBase != CPU.PCBase || (Registers.PCw & ~MEMMAP_MASK) == (0xffff & ~MEMMAP_MASK))
289 Opcodes = S9xOpcodesSlow;
290 }
291
292 Registers.PCw++;
293 (*Opcodes[Op].S9xOpcode)();
294
295
296 if (Settings.SA1)
297 S9xSA1MainLoop();
298
299 #if (S9X_ACCURACY_LEVEL <= 2)
300 while (CPU.Cycles >= CPU.NextEvent)
301 S9xDoHEventProcessing();
302 #endif
303
304 #ifdef LAGFIX
305 if (finishedFrame)
306 break;
307 #endif
308
309 }while(1);
310
311 #ifdef LAGFIX
312 if (!finishedFrame)
313 {
314 #endif
315 S9xPackStatus();
316
317 if (CPU.Flags & SCAN_KEYS_FLAG)
318 {
319 CPU.Flags &= ~SCAN_KEYS_FLAG;
320 }
321 #ifdef LAGFIX
322 }
323 else
324 {
325 finishedFrame = false;
326 break;
327 }
328 }while(!finishedFrame);
329 #endif
330 }
331
332
S9xCheckMissingHTimerPosition(void)333 static void S9xCheckMissingHTimerPosition (void)
334 {
335 if (PPU.HTimerEnabled && (!PPU.VTimerEnabled || (CPU.V_Counter == PPU.VTimerPosition)))
336 {
337 S9X_SET_IRQ(PPU_IRQ_SOURCE);
338 }
339 else if (PPU.VTimerEnabled && (CPU.V_Counter == PPU.VTimerPosition))
340 {
341 S9X_SET_IRQ(PPU_IRQ_SOURCE);
342 }
343 }
344
S9xCheckMissingHTimerHalt(void)345 static void S9xCheckMissingHTimerHalt(void)
346 {
347 if (PPU.HTimerEnabled && (!PPU.VTimerEnabled || (CPU.V_Counter == PPU.VTimerPosition)))
348 CPU.IRQPending = 1;
349 else if (PPU.VTimerEnabled && (CPU.V_Counter == PPU.VTimerPosition))
350 CPU.IRQPending = 1;
351 }
352
speedhacks_manager(void)353 static INLINE void speedhacks_manager (void)
354 {
355 uint8 var_mem, var_mem2, var_mem3;
356
357 idle_loop_target_pc = 0x00;
358 idle_loop_elimination_enable = FALSE;
359
360 switch(Settings.SpeedhackGameID)
361 {
362 case SPEEDHACK_CT:
363 /* Chrono Trigger mid-frame overscan hack - field to battle transition */
364 if (IPPU.RenderedScreenHeight == 239)
365 IPPU.RenderedScreenHeight = 224;
366 break;
367 case SPEEDHACK_DKC1:
368 PPU.SFXSpeedupHack = FALSE;
369 var_mem = Memory.RAM[0x003E]; /* current level - 7E003E */
370 if(var_mem == 49 || var_mem == 217 || var_mem == 66 || var_mem == 67)
371 PPU.SFXSpeedupHack = TRUE;
372 break;
373 case SPEEDHACK_FF6:
374 PPU.FullClipping = FALSE;
375 coldata_update_screen = FALSE;
376
377 var_mem = Memory.RAM[0x0063]; /* menu on/off - 7E0063 */
378 var_mem2 = Memory.RAM[0x3A8F]; /* battle active - 3A8F */
379 var_mem3 = Memory.RAM[0x00BA]; /* covers window toggle - 8991 */
380 if(var_mem)
381 PPU.FullClipping = var_mem;
382
383 /* check if window toggle is on */
384 if(var_mem3 == 1)
385 coldata_update_screen = TRUE;
386 /* check if menu or battle screen is on */
387 if(var_mem2 == 1)
388 coldata_update_screen = TRUE;
389 if(var_mem)
390 coldata_update_screen = TRUE;
391 break;
392 #if 0
393 case SPEEDHACK_KILLER_INSTINCT:
394 {
395 PPU.SFXSpeedupHack = TRUE;
396 //fprintf(stderr, "character: %d\n", Memory.RAM[0x024E]);
397 //fprintf(stderr, "character #2: %d\n", Memory.RAM[0x0252]);
398 //fprintf(stderr, "stage: %d\n", Memory.RAM[0x12F0]);
399 uint8 level = Memory.RAM[0x12F0]; /* current level - 8012F0XX */
400 if(level == 8)
401 PPU.SFXSpeedupHack = FALSE;
402 break;
403 }
404 case SPEEDHACK_SUPER_METROID:
405 {
406 uint8 song = (Memory.RAM[0x07f3] | Memory.RAM[0x07f4] << 8);
407 fprintf(stderr, "current_song: %d.\n", song);
408 }
409 #endif
410 case SPEEDHACK_STAR_FOX_1:
411 if (PPU.BGMode == 1 || PPU.BGMode == 2)
412 PPU.SFXSpeedupHack = TRUE;
413 else
414 PPU.SFXSpeedupHack = FALSE;
415 break;
416 case SPEEDHACK_SUPER_MARIO_WORLD:
417 idle_loop_target_pc = 0x00806B;
418 idle_loop_elimination_enable = TRUE;
419 break;
420 default:
421 break;
422 }
423 }
424
S9xEndScreenRefresh(void)425 static void S9xEndScreenRefresh (void)
426 {
427 if (IPPU.RenderThisFrame)
428 {
429 FLUSH_REDRAW();
430 }
431
432 if (!(GFX.DoInterlace && GFX.InterlaceFrame == 0))
433 {
434 S9xDeinitUpdate(IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight);
435 #ifdef LAGFIX
436 finishedFrame = true;
437 #endif
438 }
439 PPU.GunVLatch = 1000; /* i.e., never latch */
440 PPU.GunHLatch = 0;
441
442 if (!Settings.NormalControls && pad_read)
443 S9xControlEOF();
444
445 pad_read = FALSE;
446
447 if (Settings.SpeedhackGameID > SPEEDHACK_NONE)
448 speedhacks_manager();
449
450 S9xApplyCheats();
451 }
452
RenderLine(uint8 C)453 static void RenderLine (uint8 C)
454 {
455 if (IPPU.RenderThisFrame)
456 {
457 LineData[C].BG[0].VOffset = PPU.BG[0].VOffset + 1;
458 LineData[C].BG[0].HOffset = PPU.BG[0].HOffset;
459 LineData[C].BG[1].VOffset = PPU.BG[1].VOffset + 1;
460 LineData[C].BG[1].HOffset = PPU.BG[1].HOffset;
461
462 if (PPU.BGMode == 7)
463 {
464 struct SLineMatrixData *p = &LineMatrixData[C];
465 p->MatrixA = PPU.MatrixA;
466 p->MatrixB = PPU.MatrixB;
467 p->MatrixC = PPU.MatrixC;
468 p->MatrixD = PPU.MatrixD;
469 p->CentreX = PPU.CentreX;
470 p->CentreY = PPU.CentreY;
471 p->M7HOFS = PPU.M7HOFS;
472 p->M7VOFS = PPU.M7VOFS;
473 }
474 else
475 {
476 LineData[C].BG[2].VOffset = PPU.BG[2].VOffset + 1;
477 LineData[C].BG[2].HOffset = PPU.BG[2].HOffset;
478 LineData[C].BG[3].VOffset = PPU.BG[3].VOffset + 1;
479 LineData[C].BG[3].HOffset = PPU.BG[3].HOffset;
480 }
481
482 IPPU.CurrentLine = C + 1;
483 }
484 else
485 {
486 // if we're not rendering this frame, we still need to update this
487 // XXX: Check ForceBlank? Or anything else?
488 if (IPPU.OBJChanged)
489 SetupOBJ();
490 PPU.RangeTimeOver |= GFX.OBJLines[C].RTOFlags;
491 }
492 }
493
S9xReschedule(void)494 static INLINE void S9xReschedule (void)
495 {
496 uint8 next;
497 int32 hpos;
498
499 next = 0;
500 hpos = 0;
501
502 switch (CPU.WhichEvent)
503 {
504 case HC_HBLANK_START_EVENT:
505 case HC_IRQ_1_3_EVENT:
506 next = HC_HDMA_START_EVENT;
507 hpos = Timings.HDMAStart;
508 break;
509
510 case HC_HDMA_START_EVENT:
511 case HC_IRQ_3_5_EVENT:
512 next = HC_HCOUNTER_MAX_EVENT;
513 hpos = Timings.H_Max;
514 break;
515
516 case HC_HCOUNTER_MAX_EVENT:
517 case HC_IRQ_5_7_EVENT:
518 next = HC_HDMA_INIT_EVENT;
519 hpos = Timings.HDMAInit;
520 break;
521
522 case HC_HDMA_INIT_EVENT:
523 case HC_IRQ_7_9_EVENT:
524 next = HC_RENDER_EVENT;
525 hpos = Timings.RenderPos;
526 break;
527
528 case HC_RENDER_EVENT:
529 case HC_IRQ_9_A_EVENT:
530 next = HC_WRAM_REFRESH_EVENT;
531 hpos = Timings.WRAMRefreshPos;
532 break;
533
534 case HC_WRAM_REFRESH_EVENT:
535 case HC_IRQ_A_1_EVENT:
536 next = HC_HBLANK_START_EVENT;
537 hpos = Timings.HBlankStart;
538 break;
539 }
540
541 if (((int32) PPU.HTimerPosition > CPU.NextEvent) && ((int32) PPU.HTimerPosition < hpos))
542 {
543 hpos = (int32) PPU.HTimerPosition;
544
545 switch (next)
546 {
547 case HC_HDMA_START_EVENT:
548 next = HC_IRQ_1_3_EVENT;
549 break;
550
551 case HC_HCOUNTER_MAX_EVENT:
552 next = HC_IRQ_3_5_EVENT;
553 break;
554
555 case HC_HDMA_INIT_EVENT:
556 next = HC_IRQ_5_7_EVENT;
557 break;
558
559 case HC_RENDER_EVENT:
560 next = HC_IRQ_7_9_EVENT;
561 break;
562
563 case HC_WRAM_REFRESH_EVENT:
564 next = HC_IRQ_9_A_EVENT;
565 break;
566
567 case HC_HBLANK_START_EVENT:
568 next = HC_IRQ_A_1_EVENT;
569 break;
570 }
571 }
572
573 CPU.NextEvent = hpos;
574 CPU.WhichEvent = next;
575 }
576
577
HDMAReadLineCount(int d)578 static INLINE bool8 HDMAReadLineCount (int d)
579 {
580 /* CPU.InDMA is set, so S9xGetXXX() / S9xSetXXX() incur no charges. */
581
582 uint8 line;
583
584 line = S9xGetByte((DMA[d].ABank << 16) + DMA[d].Address);
585 CPU.Cycles += SLOW_ONE_CYCLE;
586
587 DMA[d].LineCount = 128;
588
589 if (!line)
590 {
591 DMA[d].Repeat = FALSE;
592
593 if (DMA[d].HDMAIndirectAddressing)
594 {
595 if (PPU.HDMA & (0xfe << d))
596 {
597 DMA[d].Address++;
598 CPU.Cycles += (SLOW_ONE_CYCLE << 1);
599 }
600 else
601 CPU.Cycles += SLOW_ONE_CYCLE;
602
603 DMA[d].IndirectAddress = S9xGetWord((DMA[d].ABank << 16) + DMA[d].Address, WRAP_NONE);
604 DMA[d].Address++;
605 }
606
607 DMA[d].Address++;
608 HDMAMemPointers[d] = NULL;
609
610 return (FALSE);
611 }
612 else
613 if (line == 0x80)
614 DMA[d].Repeat = TRUE;
615 else
616 {
617 DMA[d].Repeat = !(line & 0x80);
618 DMA[d].LineCount = line & 0x7f;
619 }
620
621 DMA[d].Address++;
622 DMA[d].DoTransfer = TRUE;
623
624 if (DMA[d].HDMAIndirectAddressing)
625 {
626 CPU.Cycles += (SLOW_ONE_CYCLE << 1);
627 DMA[d].IndirectAddress = S9xGetWord((DMA[d].ABank << 16) + DMA[d].Address, WRAP_NONE);
628 DMA[d].Address += 2;
629 HDMAMemPointers[d] = S9xGetMemPointer((DMA[d].IndirectBank << 16) + DMA[d].IndirectAddress);
630 }
631 else
632 HDMAMemPointers[d] = S9xGetMemPointer((DMA[d].ABank << 16) + DMA[d].Address);
633
634 return (TRUE);
635 }
636
S9xStartHDMA(void)637 static void S9xStartHDMA (void)
638 {
639 uint8 i;
640 int32 tmpch;
641
642 PPU.HDMA = Memory.FillRAM[0x420c];
643 PPU.HDMAEnded = 0;
644
645 CPU.InHDMA = TRUE;
646 CPU.InDMAorHDMA = TRUE;
647
648 tmpch = CPU.CurrentDMAorHDMAChannel;
649
650 /* XXX: Not quite right... */
651 if (PPU.HDMA != 0)
652 CPU.Cycles += Timings.DMACPUSync;
653
654 for ( i = 0; i < 8; i++)
655 {
656 if (PPU.HDMA & (1 << i))
657 {
658 CPU.CurrentDMAorHDMAChannel = i;
659
660 DMA[i].Address = DMA[i].AAddress;
661
662 if (!HDMAReadLineCount(i))
663 {
664 PPU.HDMA &= ~(1 << i);
665 PPU.HDMAEnded |= (1 << i);
666 }
667 }
668 else
669 DMA[i].DoTransfer = FALSE;
670 }
671
672 CPU.InHDMA = FALSE;
673 CPU.InDMAorHDMA = CPU.InDMA;
674 CPU.HDMARanInDMA = CPU.InDMA ? PPU.HDMA : 0;
675 CPU.CurrentDMAorHDMAChannel = tmpch;
676 }
677
678 static int HDMA_ModeByteCounts[8] =
679 {
680 1, 2, 2, 4, 4, 4, 2, 4
681 };
682
S9xDoHDMA(uint8 byte)683 static uint8 S9xDoHDMA (uint8 byte)
684 {
685 uint8 mask;
686 uint32 ShiftedIBank;
687 uint16 IAddr;
688 bool8 temp;
689 int32 tmpch;
690 int d;
691 struct SDMA *p;
692
693 p = &DMA[0];
694
695 d = 0;
696
697 CPU.InHDMA = TRUE;
698 CPU.InDMAorHDMA = TRUE;
699 CPU.HDMARanInDMA = CPU.InDMA ? byte : 0;
700 temp = CPU.InWRAMDMAorHDMA;
701 tmpch = CPU.CurrentDMAorHDMAChannel;
702
703 /* XXX: Not quite right... */
704 CPU.Cycles += Timings.DMACPUSync;
705
706 for ( mask = 1; mask; mask <<= 1, p++, d++)
707 {
708 if (byte & mask)
709 {
710 CPU.InWRAMDMAorHDMA = FALSE;
711 CPU.CurrentDMAorHDMAChannel = d;
712
713 if (p->HDMAIndirectAddressing)
714 {
715 ShiftedIBank = (p->IndirectBank << 16);
716 IAddr = p->IndirectAddress;
717 }
718 else
719 {
720 ShiftedIBank = (p->ABank << 16);
721 IAddr = p->Address;
722 }
723
724 if (!HDMAMemPointers[d])
725 HDMAMemPointers[d] = S9xGetMemPointer(ShiftedIBank + IAddr);
726
727 if (p->DoTransfer)
728 {
729 /* XXX: Hack for Uniracers, because we don't understand
730 OAM Address Invalidation */
731 if (p->BAddress == 0x04 && SNESGameFixes.Uniracers)
732 {
733 PPU.OAMAddr = 0x10c;
734 PPU.OAMFlip = 0;
735 }
736
737
738 if (!p->ReverseTransfer)
739 {
740 if ((IAddr & MEMMAP_MASK) + HDMA_ModeByteCounts[p->TransferMode] >= MEMMAP_BLOCK_SIZE)
741 {
742 /* HDMA REALLY-SLOW PATH */
743 HDMAMemPointers[d] = NULL;
744
745 #define DOBYTE(Addr, RegOff) \
746 CPU.InWRAMDMAorHDMA = (ShiftedIBank == 0x7e0000 || ShiftedIBank == 0x7f0000 || \
747 (!(ShiftedIBank & 0x400000) && ((uint16) (Addr)) < 0x2000)); \
748 S9xSetPPU(S9xGetByte(ShiftedIBank + ((uint16) (Addr))), 0x2100 + p->BAddress + (RegOff));
749
750 DOBYTE(IAddr, 0);
751 CPU.Cycles += SLOW_ONE_CYCLE;
752 switch (p->TransferMode)
753 {
754 case 0:
755 break;
756
757 case 5:
758 DOBYTE(IAddr + 1, 1);
759 CPU.Cycles += SLOW_ONE_CYCLE;
760 DOBYTE(IAddr + 2, 0);
761 CPU.Cycles += SLOW_ONE_CYCLE;
762 DOBYTE(IAddr + 3, 1);
763 CPU.Cycles += SLOW_ONE_CYCLE;
764 break;
765
766 case 1:
767 DOBYTE(IAddr + 1, 1);
768 CPU.Cycles += SLOW_ONE_CYCLE;
769 break;
770
771 case 2:
772 case 6:
773 DOBYTE(IAddr + 1, 0);
774 CPU.Cycles += SLOW_ONE_CYCLE;
775 break;
776
777 case 3:
778 case 7:
779 DOBYTE(IAddr + 1, 0);
780 CPU.Cycles += SLOW_ONE_CYCLE;
781 DOBYTE(IAddr + 2, 1);
782 CPU.Cycles += SLOW_ONE_CYCLE;
783 DOBYTE(IAddr + 3, 1);
784 CPU.Cycles += SLOW_ONE_CYCLE;
785 break;
786
787 case 4:
788 DOBYTE(IAddr + 1, 1);
789 CPU.Cycles += SLOW_ONE_CYCLE;
790 DOBYTE(IAddr + 2, 2);
791 CPU.Cycles += SLOW_ONE_CYCLE;
792 DOBYTE(IAddr + 3, 3);
793 CPU.Cycles += SLOW_ONE_CYCLE;
794 break;
795 }
796
797 #undef DOBYTE
798 }
799 else
800 {
801 CPU.InWRAMDMAorHDMA = (ShiftedIBank == 0x7e0000 || ShiftedIBank == 0x7f0000 ||
802 (!(ShiftedIBank & 0x400000) && IAddr < 0x2000));
803
804 if (!HDMAMemPointers[d])
805 {
806 /* HDMA SLOW PATH */
807 uint32 Addr = ShiftedIBank + IAddr;
808
809 switch (p->TransferMode)
810 {
811 case 0:
812 S9xSetPPU(S9xGetByte(Addr), 0x2100 + p->BAddress);
813 CPU.Cycles += SLOW_ONE_CYCLE;
814 break;
815
816 case 5:
817 S9xSetPPU(S9xGetByte(Addr + 0), 0x2100 + p->BAddress);
818 CPU.Cycles += SLOW_ONE_CYCLE;
819 S9xSetPPU(S9xGetByte(Addr + 1), 0x2101 + p->BAddress);
820 CPU.Cycles += SLOW_ONE_CYCLE;
821 Addr += 2;
822 /* fall through */
823 case 1:
824 S9xSetPPU(S9xGetByte(Addr + 0), 0x2100 + p->BAddress);
825 CPU.Cycles += SLOW_ONE_CYCLE;
826 S9xSetPPU(S9xGetByte(Addr + 1), 0x2101 + p->BAddress);
827 CPU.Cycles += SLOW_ONE_CYCLE;
828 break;
829
830 case 2:
831 case 6:
832 S9xSetPPU(S9xGetByte(Addr + 0), 0x2100 + p->BAddress);
833 CPU.Cycles += SLOW_ONE_CYCLE;
834 S9xSetPPU(S9xGetByte(Addr + 1), 0x2100 + p->BAddress);
835 CPU.Cycles += SLOW_ONE_CYCLE;
836 break;
837
838 case 3:
839 case 7:
840 S9xSetPPU(S9xGetByte(Addr + 0), 0x2100 + p->BAddress);
841 CPU.Cycles += SLOW_ONE_CYCLE;
842 S9xSetPPU(S9xGetByte(Addr + 1), 0x2100 + p->BAddress);
843 CPU.Cycles += SLOW_ONE_CYCLE;
844 S9xSetPPU(S9xGetByte(Addr + 2), 0x2101 + p->BAddress);
845 CPU.Cycles += SLOW_ONE_CYCLE;
846 S9xSetPPU(S9xGetByte(Addr + 3), 0x2101 + p->BAddress);
847 CPU.Cycles += SLOW_ONE_CYCLE;
848 break;
849
850 case 4:
851 S9xSetPPU(S9xGetByte(Addr + 0), 0x2100 + p->BAddress);
852 CPU.Cycles += SLOW_ONE_CYCLE;
853 S9xSetPPU(S9xGetByte(Addr + 1), 0x2101 + p->BAddress);
854 CPU.Cycles += SLOW_ONE_CYCLE;
855 S9xSetPPU(S9xGetByte(Addr + 2), 0x2102 + p->BAddress);
856 CPU.Cycles += SLOW_ONE_CYCLE;
857 S9xSetPPU(S9xGetByte(Addr + 3), 0x2103 + p->BAddress);
858 CPU.Cycles += SLOW_ONE_CYCLE;
859 break;
860 }
861 }
862 else
863 {
864 /* HDMA FAST PATH */
865 switch (p->TransferMode)
866 {
867 case 0:
868 S9xSetPPU(*HDMAMemPointers[d]++, 0x2100 + p->BAddress);
869 CPU.Cycles += SLOW_ONE_CYCLE;
870 break;
871
872 case 5:
873 S9xSetPPU(*(HDMAMemPointers[d]), 0x2100 + p->BAddress);
874 CPU.Cycles += SLOW_ONE_CYCLE;
875 S9xSetPPU(*(HDMAMemPointers[d] + 1), 0x2101 + p->BAddress);
876 CPU.Cycles += SLOW_ONE_CYCLE;
877 HDMAMemPointers[d] += 2;
878 /* fall through */
879 case 1:
880 S9xSetPPU(*(HDMAMemPointers[d]), 0x2100 + p->BAddress);
881 CPU.Cycles += SLOW_ONE_CYCLE;
882 S9xSetPPU(*(HDMAMemPointers[d] + 1), 0x2101 + p->BAddress);
883 CPU.Cycles += SLOW_ONE_CYCLE;
884 HDMAMemPointers[d] += 2;
885 break;
886
887 case 2:
888 case 6:
889 S9xSetPPU(*(HDMAMemPointers[d]), 0x2100 + p->BAddress);
890 CPU.Cycles += SLOW_ONE_CYCLE;
891 S9xSetPPU(*(HDMAMemPointers[d] + 1), 0x2100 + p->BAddress);
892 CPU.Cycles += SLOW_ONE_CYCLE;
893 HDMAMemPointers[d] += 2;
894 break;
895
896 case 3:
897 case 7:
898 S9xSetPPU(*(HDMAMemPointers[d]), 0x2100 + p->BAddress);
899 CPU.Cycles += SLOW_ONE_CYCLE;
900 S9xSetPPU(*(HDMAMemPointers[d] + 1), 0x2100 + p->BAddress);
901 CPU.Cycles += SLOW_ONE_CYCLE;
902 S9xSetPPU(*(HDMAMemPointers[d] + 2), 0x2101 + p->BAddress);
903 CPU.Cycles += SLOW_ONE_CYCLE;
904 S9xSetPPU(*(HDMAMemPointers[d] + 3), 0x2101 + p->BAddress);
905 CPU.Cycles += SLOW_ONE_CYCLE;
906 HDMAMemPointers[d] += 4;
907 break;
908
909 case 4:
910 S9xSetPPU(*(HDMAMemPointers[d]), 0x2100 + p->BAddress);
911 CPU.Cycles += SLOW_ONE_CYCLE;
912 S9xSetPPU(*(HDMAMemPointers[d] + 1), 0x2101 + p->BAddress);
913 CPU.Cycles += SLOW_ONE_CYCLE;
914 S9xSetPPU(*(HDMAMemPointers[d] + 2), 0x2102 + p->BAddress);
915 CPU.Cycles += SLOW_ONE_CYCLE;
916 S9xSetPPU(*(HDMAMemPointers[d] + 3), 0x2103 + p->BAddress);
917 CPU.Cycles += SLOW_ONE_CYCLE;
918 HDMAMemPointers[d] += 4;
919 break;
920 }
921 }
922 }
923 }
924
925 if (p->HDMAIndirectAddressing)
926 p->IndirectAddress += HDMA_ModeByteCounts[p->TransferMode];
927 else
928 p->Address += HDMA_ModeByteCounts[p->TransferMode];
929 }
930
931 p->DoTransfer = !p->Repeat;
932
933 if (!--p->LineCount)
934 {
935 if (!HDMAReadLineCount(d))
936 {
937 byte &= ~mask;
938 PPU.HDMAEnded |= mask;
939 p->DoTransfer = FALSE;
940 continue;
941 }
942 }
943 else
944 CPU.Cycles += SLOW_ONE_CYCLE;
945 }
946 }
947
948 CPU.InHDMA = FALSE;
949 CPU.InDMAorHDMA = CPU.InDMA;
950 CPU.InWRAMDMAorHDMA = temp;
951 CPU.CurrentDMAorHDMAChannel = tmpch;
952
953 return (byte);
954 }
955
S9xDoHEventProcessing(void)956 void S9xDoHEventProcessing (void)
957 {
958 uint8 tmp;
959
960 switch (CPU.WhichEvent)
961 {
962 case HC_HBLANK_START_EVENT:
963 if (PPU.HTimerPosition == Timings.HBlankStart)
964 S9xCheckMissingHTimerPosition();
965 S9xReschedule();
966 break;
967
968 case HC_HDMA_START_EVENT:
969 if (PPU.HTimerPosition == Timings.HDMAStart)
970 S9xCheckMissingHTimerPosition();
971 S9xReschedule();
972
973 if (PPU.HDMA && CPU.V_Counter <= PPU.ScreenHeight)
974 PPU.HDMA = S9xDoHDMA(PPU.HDMA);
975
976 break;
977
978 case HC_HCOUNTER_MAX_EVENT:
979 if (Settings.SuperFX && !SuperFX.oneLineDone && CHECK_EXEC_SUPERFX())
980 S9xSuperFXExec();
981 SuperFX.oneLineDone = FALSE; // do this even without SFX
982
983 S9xAPUExecute();
984 CPU.Cycles -= Timings.H_Max;
985 S9xAPUSetReferenceTime(CPU.Cycles);
986
987 if ((Timings.NMITriggerPos != 0xffff) && (Timings.NMITriggerPos >= Timings.H_Max))
988 Timings.NMITriggerPos -= Timings.H_Max;
989
990 CPU.V_Counter++;
991 if (CPU.V_Counter >= Timings.V_Max) /* V ranges from 0 to Timings.V_Max - 1 */
992 {
993 CPU.V_Counter = 0;
994 Timings.InterlaceField ^= 1;
995
996 /* From byuu:
997 [NTSC]
998 interlace mode has 525 scanlines: 263 on
999 the even frame, and 262 on the odd.
1000
1001 non-interlace mode has 524 scanlines:
1002 262 scanlines on both even and odd frames.
1003
1004 [PAL] <PAL info is unverified on hardware>
1005 interlace mode has 625 scanlines: 313 on
1006 the even frame, and 312 on the odd.
1007
1008 non-interlace mode has 624 scanlines:
1009 312 scanlines on both even and odd frames. */
1010
1011 Timings.V_Max = Timings.V_Max_Master; /* 262 (NTSC), 312?(PAL) */
1012
1013 if (IPPU.Interlace && !Timings.InterlaceField)
1014 Timings.V_Max += 1; /* 263 (NTSC), 313?(PAL) */
1015
1016 Memory.FillRAM[0x213F] ^= 0x80;
1017 PPU.RangeTimeOver = 0;
1018
1019 /* FIXME: reading $4210 will wait 2 cycles,
1020 then perform reading, then wait 4 more cycles. */
1021
1022 Memory.FillRAM[0x4210] = MAX_5A22_VERSION;
1023 CPU.Flags &= ~NMI_FLAG;
1024 Timings.NMITriggerPos = 0xffff;
1025
1026 ICPU.Frame++;
1027 PPU.HVBeamCounterLatched = 0;
1028 CPU.Flags |= SCAN_KEYS_FLAG;
1029 }
1030
1031 /* From byuu:
1032 In non-interlace mode, there are 341 dots
1033 per scanline, and 262 scanlines per frame.
1034
1035 On odd frames, scanline 240 is one dot short.
1036 In interlace mode, there are always 341 dots
1037 per scanline. Even frames have 263 scanlines,
1038 and odd frames have 262 scanlines.
1039
1040 Interlace mode scanline 240 on odd frames is
1041 not missing a dot. */
1042
1043 Timings.H_Max = Timings.H_Max_Master; /* HC=1364 */
1044
1045 if (CPU.V_Counter == 240 && !IPPU.Interlace && Timings.InterlaceField) /* V=240 */
1046 Timings.H_Max -= ONE_DOT_CYCLE; /* HC=1360 */
1047
1048 if (CPU.V_Counter != 240 || IPPU.Interlace || !Timings.InterlaceField) /* V=240 */
1049 {
1050 if (Timings.WRAMRefreshPos == SNES_WRAM_REFRESH_HC_v2_MIN_ONE_DOT_CYCLE) /* HC=534 */
1051 Timings.WRAMRefreshPos = SNES_WRAM_REFRESH_HC_v2; /* HC=538 */
1052 else
1053 Timings.WRAMRefreshPos = SNES_WRAM_REFRESH_HC_v2_MIN_ONE_DOT_CYCLE; /* HC=534 */
1054 }
1055
1056 if (PPU.HTimerPosition == 0)
1057 S9xCheckMissingHTimerPosition();
1058
1059 if (CPU.V_Counter == PPU.ScreenHeight + FIRST_VISIBLE_LINE) /* VBlank starts from V=225(240). */
1060 {
1061 S9xEndScreenRefresh();
1062
1063 PPU.HDMA = 0;
1064 /* Bits 7 and 6 of $4212 are computed when read in S9xGetPPU. */
1065 PPU.ForcedBlanking = (Memory.FillRAM[0x2100] >> 7) & 1;
1066
1067 if (!PPU.ForcedBlanking)
1068 {
1069 PPU.OAMAddr = PPU.SavedOAMAddr;
1070
1071 tmp = 0;
1072
1073 if (PPU.OAMPriorityRotation)
1074 tmp = (PPU.OAMAddr & 0xFE) >> 1;
1075 if ((PPU.OAMFlip & 1) || PPU.FirstSprite != tmp)
1076 {
1077 PPU.FirstSprite = tmp;
1078 IPPU.OBJChanged = TRUE;
1079 }
1080
1081 PPU.OAMFlip = 0;
1082 }
1083
1084 /* FIXME: writing to $4210 will wait 6 cycles. */
1085 Memory.FillRAM[0x4210] = 0x80 | MAX_5A22_VERSION;
1086 if (Memory.FillRAM[0x4200] & 0x80)
1087 {
1088 /* FIXME: triggered at HC=6, checked just
1089 before the final CPU cycle, then, when to
1090 call S9xOpcode_NMI()? */
1091 CPU.Flags |= NMI_FLAG;
1092 Timings.NMITriggerPos = 6 + 6;
1093 }
1094
1095 }
1096
1097 /* FIXME: not true */
1098 if ((CPU.V_Counter == PPU.ScreenHeight + 3) && (Memory.FillRAM[0x4200] & 1))
1099 {
1100 S9xDoAutoJoypad();
1101 }
1102
1103 /* V = 1 */
1104 if (CPU.V_Counter == FIRST_VISIBLE_LINE)
1105 {
1106 if (IPPU.RenderThisFrame)
1107 {
1108 GFX.InterlaceFrame = !GFX.InterlaceFrame;
1109
1110 if (!GFX.DoInterlace || !GFX.InterlaceFrame)
1111 {
1112 /* S9x Start Screen Refresh */
1113 bool8 cond_1, cond_2;
1114
1115 GFX.DoInterlace -= (GFX.DoInterlace == TRUE);
1116
1117 IPPU.Interlace = Memory.FillRAM[0x2133] & 1;
1118 IPPU.InterlaceOBJ = Memory.FillRAM[0x2133] & 2;
1119 IPPU.PseudoHires = Memory.FillRAM[0x2133] & 8;
1120
1121 cond_1 = (Settings.SupportHiRes && (PPU.BGMode == 5 || PPU.BGMode == 6 || IPPU.PseudoHires));
1122 cond_2 = (Settings.SupportHiRes && IPPU.Interlace);
1123
1124 GFX.RealPPL = GFX.Pitch >> 1;
1125 IPPU.RenderedScreenWidth = SNES_WIDTH << cond_1;
1126 IPPU.RenderedScreenHeight = PPU.ScreenHeight << cond_2;
1127 IPPU.DoubleWidthPixels = cond_1;
1128 IPPU.DoubleHeightPixels = cond_2;
1129
1130 GFX.PPL = GFX.RealPPL << cond_2;
1131 GFX.DoInterlace += cond_2;
1132 }
1133
1134 PPU.MosaicStart = 0;
1135 PPU.RecomputeClipWindows = TRUE;
1136 IPPU.PreviousLine = IPPU.CurrentLine = 0;
1137
1138 memset(GFX.ZBuffer, 0, GFX.ScreenSize);
1139 memset(GFX.SubZBuffer, 0, GFX.ScreenSize);
1140 }
1141 }
1142
1143 CPU.NextEvent = -1;
1144 S9xReschedule();
1145
1146 break;
1147
1148 case HC_HDMA_INIT_EVENT:
1149 if (PPU.HTimerPosition == Timings.HDMAInit)
1150 S9xCheckMissingHTimerPosition();
1151 S9xReschedule();
1152
1153 if (CPU.V_Counter == 0)
1154 S9xStartHDMA();
1155
1156 break;
1157
1158 case HC_RENDER_EVENT:
1159 if (CPU.V_Counter >= FIRST_VISIBLE_LINE && CPU.V_Counter <= PPU.ScreenHeight)
1160 RenderLine((uint8) (CPU.V_Counter - FIRST_VISIBLE_LINE));
1161
1162 if (PPU.HTimerPosition == Timings.RenderPos)
1163 S9xCheckMissingHTimerPosition();
1164 S9xReschedule();
1165
1166 break;
1167
1168 case HC_WRAM_REFRESH_EVENT:
1169
1170 if ((PPU.HTimerPosition >= Timings.WRAMRefreshPos) && (PPU.HTimerPosition < (Timings.WRAMRefreshPos + SNES_WRAM_REFRESH_CYCLES)))
1171 S9xCheckMissingHTimerHalt();
1172 CPU.Cycles += SNES_WRAM_REFRESH_CYCLES;
1173
1174 if (PPU.HTimerPosition == Timings.WRAMRefreshPos)
1175 S9xCheckMissingHTimerPosition();
1176 S9xReschedule();
1177
1178 break;
1179
1180 case HC_IRQ_1_3_EVENT:
1181 case HC_IRQ_3_5_EVENT:
1182 case HC_IRQ_5_7_EVENT:
1183 case HC_IRQ_7_9_EVENT:
1184 case HC_IRQ_9_A_EVENT:
1185 case HC_IRQ_A_1_EVENT:
1186 if ((PPU.HTimerEnabled && (!PPU.VTimerEnabled || (CPU.V_Counter == PPU.VTimerPosition))) || (PPU.VTimerEnabled && (CPU.V_Counter == PPU.VTimerPosition)))
1187 {
1188 S9X_SET_IRQ(PPU_IRQ_SOURCE);
1189 }
1190
1191 S9xReschedule();
1192 break;
1193 }
1194
1195 }
1196
1197 #include "cpuops_.h"
1198