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