dma.cpp
   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 "memory.h"
   9  #include "dma.h"
  10  #include "apu.h"
  11  #ifdef DEBUGGER
  12  #include "debug.h"
  13  #endif
  14  
  15  #define ADD_CYCLES(n)	{ CPU.Cycles += (n); }
  16  #define GET_MEM_PTR(Address) ({ \
  17  	uint32 a = (Address); \
  18  	uint8 *ptr = S9xGetBasePointer(a); \
  19  	ptr ? (ptr + (a & 0xffff)) : NULL; \
  20  })
  21  
  22  struct SDMA	DMA[8];
  23  
  24  
  25  static inline bool8 addCyclesInDMA (uint32 dma_channel)
  26  {
  27  	// Add 8 cycles per byte, sync APU, and do HC related events.
  28  	// If HDMA was done in S9xDoHEventProcessing(), check if it used the same channel as DMA.
  29  	ADD_CYCLES(SLOW_ONE_CYCLE);
  30  	if (CPU.Cycles >= CPU.NextEvent)
  31  		S9xDoHEventProcessing();
  32  
  33  	if (CPU.HDMARanInDMA & (1 << dma_channel))
  34  	{
  35  		CPU.HDMARanInDMA = 0;
  36  	#ifdef DEBUGGER
  37  		printf("HDMA and DMA use the same channel %d!\n", dma_channel);
  38  	#endif
  39  		// If HDMA triggers in the middle of DMA transfer and it uses the same channel,
  40  		// it kills the DMA transfer immediately. $43x2 and $43x5 stop updating.
  41  		return (FALSE);
  42  	}
  43  
  44  	CPU.HDMARanInDMA = 0;
  45  	return (TRUE);
  46  }
  47  
  48  bool8 S9xDoDMA (uint8 Channel)
  49  {
  50  	CPU.InDMA = TRUE;
  51      CPU.InDMAorHDMA = TRUE;
  52  	CPU.CurrentDMAorHDMAChannel = Channel;
  53  
  54      SDMA	*d = &DMA[Channel];
  55  
  56  	// Check invalid DMA first
  57  	if ((d->ABank == 0x7E || d->ABank == 0x7F) && d->BAddress == 0x80 && !d->ReverseTransfer)
  58  	{
  59  		// Attempting a DMA from WRAM to $2180 will not work, WRAM will not be written.
  60  		// Attempting a DMA from $2180 to WRAM will similarly not work,
  61  		// the value written is (initially) the OpenBus value.
  62  		// In either case, the address in $2181-3 is not incremented.
  63  
  64  		// Does an invalid DMA actually take time?
  65  		// I'd say yes, since 'invalid' is probably just the WRAM chip
  66  		// not being able to read and write itself at the same time
  67  		// And no, PPU.WRAM should not be updated.
  68  
  69  		int32	c = d->TransferBytes;
  70  		// Writing $0000 to $43x5 actually results in a transfer of $10000 bytes, not 0.
  71  		if (c == 0)
  72  			c = 0x10000;
  73  
  74  		// 8 cycles per channel
  75  		ADD_CYCLES(SLOW_ONE_CYCLE);
  76  		// 8 cycles per byte
  77  		while (c)
  78  		{
  79  			d->TransferBytes--;
  80  			d->AAddress++;
  81  			c--;
  82  			if (!addCyclesInDMA(Channel))
  83  			{
  84  				CPU.InDMA = FALSE;
  85  				CPU.InDMAorHDMA = FALSE;
  86  				CPU.CurrentDMAorHDMAChannel = -1;
  87  				return (FALSE);
  88  			}
  89  		}
  90  
  91  	#ifdef DEBUGGER
  92  		if (Settings.TraceDMA)
  93  		{
  94  			sprintf(String, "DMA[%d]: WRAM Bank:%02X->$2180", Channel, d->ABank);
  95  			S9xMessage(S9X_TRACE, S9X_DMA_TRACE, String);
  96  		}
  97  	#endif
  98  
  99  		CPU.InDMA = FALSE;
 100  		CPU.InDMAorHDMA = FALSE;
 101  		CPU.CurrentDMAorHDMAChannel = -1;
 102  		return (TRUE);
 103  	}
 104  
 105  	// Prepare for accessing $2118-2119
 106  	switch (d->BAddress)
 107  	{
 108  		case 0x18:
 109  		case 0x19:
 110  			if (IPPU.RenderThisFrame)
 111  				FLUSH_REDRAW();
 112  			break;
 113  	}
 114  
 115  	int32	inc = d->AAddressFixed ? 0 : (!d->AAddressDecrement ? 1 : -1);
 116  	int32	count = d->TransferBytes;
 117  	// Writing $0000 to $43x5 actually results in a transfer of $10000 bytes, not 0.
 118  	if (count == 0)
 119  		count = 0x10000;
 120  
 121  	// Prepare for custom chip DMA
 122  
 123  #ifdef DEBUGGER
 124  	if (Settings.TraceDMA)
 125  	{
 126  		sprintf(String, "DMA[%d]: %s Mode:%d 0x%02X%04X->0x21%02X Bytes:%d (%s) V:%03d",
 127  			Channel, d->ReverseTransfer ? "PPU->CPU" : "CPU->PPU", d->TransferMode, d->ABank, d->AAddress, d->BAddress,
 128  			d->TransferBytes, d->AAddressFixed ? "fixed" : (d->AAddressDecrement ? "dec" : "inc"), CPU.V_Counter);
 129  
 130  		if (d->BAddress == 0x18 || d->BAddress == 0x19 || d->BAddress == 0x39 || d->BAddress == 0x3a)
 131  			sprintf(String, "%s VRAM: %04X (%d,%d) %s", String,
 132  				PPU.VMA.Address, PPU.VMA.Increment, PPU.VMA.FullGraphicCount, PPU.VMA.High ? "word" : "byte");
 133  		else
 134  		if (d->BAddress == 0x22 || d->BAddress == 0x3b)
 135  			sprintf(String, "%s CGRAM: %02X (%x)", String, PPU.CGADD, PPU.CGFLIP);
 136  		else
 137  		if (d->BAddress == 0x04 || d->BAddress == 0x38)
 138  			sprintf(String, "%s OBJADDR: %04X", String, PPU.OAMAddr);
 139  
 140  		S9xMessage(S9X_TRACE, S9X_DMA_TRACE, String);
 141  	}
 142  #endif
 143  
 144  	// Do Transfer
 145  
 146  	uint8	Work;
 147  
 148  	// 8 cycles per channel
 149  	ADD_CYCLES(SLOW_ONE_CYCLE);
 150  
 151  	if (!d->ReverseTransfer)
 152      {
 153  		// CPU -> PPU
 154  		int32	b = 0;
 155  		uint16	p = d->AAddress;
 156  		uint8	*base = S9xGetBasePointer((d->ABank << 16) + d->AAddress);
 157  		bool8	inWRAM_DMA;
 158  
 159  		int32	rem = count;
 160  		// Transfer per block if d->AAdressFixed is FALSE
 161  		count = d->AAddressFixed ? rem : (d->AAddressDecrement ? ((p & MEMMAP_MASK) + 1) : (MEMMAP_BLOCK_SIZE - (p & MEMMAP_MASK)));
 162  
 163  		inWRAM_DMA = ((d->ABank == 0x7e || d->ABank == 0x7f || (!(d->ABank & 0x40) && d->AAddress < 0x2000)));
 164  
 165  		// 8 cycles per byte
 166  		#define	UPDATE_COUNTERS \
 167  			d->TransferBytes--; \
 168  			d->AAddress += inc; \
 169  			p += inc; \
 170  			if (!addCyclesInDMA(Channel)) \
 171  			{ \
 172  				CPU.InDMA = FALSE; \
 173  				CPU.InDMAorHDMA = FALSE; \
 174  				CPU.InWRAMDMAorHDMA = FALSE; \
 175  				CPU.CurrentDMAorHDMAChannel = -1; \
 176  				return (FALSE); \
 177  			}
 178  
 179  		while (1)
 180  		{
 181  			if (count > rem)
 182  				count = rem;
 183  			rem -= count;
 184  
 185  			CPU.InWRAMDMAorHDMA = inWRAM_DMA;
 186  
 187  			if (!base)
 188  			{
 189  				// DMA SLOW PATH
 190  				if (d->TransferMode == 0 || d->TransferMode == 2 || d->TransferMode == 6)
 191  				{
 192  					do
 193  					{
 194  						Work = S9xGetByte((d->ABank << 16) + p);
 195  						S9xSetPPU(Work, 0x2100 + d->BAddress);
 196  						UPDATE_COUNTERS;
 197  					} while (--count > 0);
 198  				}
 199  				else
 200  				if (d->TransferMode == 1 || d->TransferMode == 5)
 201  				{
 202  					// This is a variation on Duff's Device. It is legal C/C++.
 203  					switch (b)
 204  					{
 205  						default:
 206  						while (count > 1)
 207  						{
 208  							Work = S9xGetByte((d->ABank << 16) + p);
 209  							S9xSetPPU(Work, 0x2100 + d->BAddress);
 210  							UPDATE_COUNTERS;
 211  							count--;
 212  						// Fall through
 213  						case 1:
 214  							Work = S9xGetByte((d->ABank << 16) + p);
 215  							S9xSetPPU(Work, 0x2101 + d->BAddress);
 216  							UPDATE_COUNTERS;
 217  							count--;
 218  						}
 219  					}
 220  
 221  					if (count == 1)
 222  					{
 223  						Work = S9xGetByte((d->ABank << 16) + p);
 224  						S9xSetPPU(Work, 0x2100 + d->BAddress);
 225  						UPDATE_COUNTERS;
 226  						b = 1;
 227  					}
 228  					else
 229  						b = 0;
 230  				}
 231  				else
 232  				if (d->TransferMode == 3 || d->TransferMode == 7)
 233  				{
 234  					switch (b)
 235  					{
 236  						default:
 237  						do
 238  						{
 239  							Work = S9xGetByte((d->ABank << 16) + p);
 240  							S9xSetPPU(Work, 0x2100 + d->BAddress);
 241  							UPDATE_COUNTERS;
 242  							if (--count <= 0)
 243  							{
 244  								b = 1;
 245  								break;
 246  							}
 247  						// Fall through
 248  						case 1:
 249  							Work = S9xGetByte((d->ABank << 16) + p);
 250  							S9xSetPPU(Work, 0x2100 + d->BAddress);
 251  							UPDATE_COUNTERS;
 252  							if (--count <= 0)
 253  							{
 254  								b = 2;
 255  								break;
 256  							}
 257  						// Fall through
 258  						case 2:
 259  							Work = S9xGetByte((d->ABank << 16) + p);
 260  							S9xSetPPU(Work, 0x2101 + d->BAddress);
 261  							UPDATE_COUNTERS;
 262  							if (--count <= 0)
 263  							{
 264  								b = 3;
 265  								break;
 266  							}
 267  						// Fall through
 268  						case 3:
 269  							Work = S9xGetByte((d->ABank << 16) + p);
 270  							S9xSetPPU(Work, 0x2101 + d->BAddress);
 271  							UPDATE_COUNTERS;
 272  							if (--count <= 0)
 273  							{
 274  								b = 0;
 275  								break;
 276  							}
 277  						} while (1);
 278  					}
 279  				}
 280  				else
 281  				if (d->TransferMode == 4)
 282  				{
 283  					switch (b)
 284  					{
 285  						default:
 286  						do
 287  						{
 288  							Work = S9xGetByte((d->ABank << 16) + p);
 289  							S9xSetPPU(Work, 0x2100 + d->BAddress);
 290  							UPDATE_COUNTERS;
 291  							if (--count <= 0)
 292  							{
 293  								b = 1;
 294  								break;
 295  							}
 296  						// Fall through
 297  						case 1:
 298  							Work = S9xGetByte((d->ABank << 16) + p);
 299  							S9xSetPPU(Work, 0x2101 + d->BAddress);
 300  							UPDATE_COUNTERS;
 301  							if (--count <= 0)
 302  							{
 303  								b = 2;
 304  								break;
 305  							}
 306  						// Fall through
 307  						case 2:
 308  							Work = S9xGetByte((d->ABank << 16) + p);
 309  							S9xSetPPU(Work, 0x2102 + d->BAddress);
 310  							UPDATE_COUNTERS;
 311  							if (--count <= 0)
 312  							{
 313  								b = 3;
 314  								break;
 315  							}
 316  						// Fall through
 317  						case 3:
 318  							Work = S9xGetByte((d->ABank << 16) + p);
 319  							S9xSetPPU(Work, 0x2103 + d->BAddress);
 320  							UPDATE_COUNTERS;
 321  							if (--count <= 0)
 322  							{
 323  								b = 0;
 324  								break;
 325  							}
 326  						} while (1);
 327  					}
 328  				}
 329  			#ifdef DEBUGGER
 330  				else
 331  				{
 332  					sprintf(String, "Unknown DMA transfer mode: %d on channel %d\n", d->TransferMode, Channel);
 333  					S9xMessage(S9X_TRACE, S9X_DMA_TRACE, String);
 334  				}
 335  			#endif
 336  			}
 337  			else
 338  			{
 339  				// DMA FAST PATH
 340  				if (d->TransferMode == 0 || d->TransferMode == 2 || d->TransferMode == 6)
 341  				{
 342  					switch (d->BAddress)
 343  					{
 344  						case 0x04: // OAMDATA
 345  							do
 346  							{
 347  								Work = *(base + p);
 348  								REGISTER_2104(Work);
 349  								UPDATE_COUNTERS;
 350  							} while (--count > 0);
 351  
 352  							break;
 353  
 354  						case 0x18: // VMDATAL
 355  							if (!PPU.VMA.FullGraphicCount)
 356  							{
 357  								do
 358  								{
 359  									Work = *(base + p);
 360  									REGISTER_2118_linear(Work);
 361  									UPDATE_COUNTERS;
 362  								} while (--count > 0);
 363  							}
 364  							else
 365  							{
 366  								do
 367  								{
 368  									Work = *(base + p);
 369  									REGISTER_2118_tile(Work);
 370  									UPDATE_COUNTERS;
 371  								} while (--count > 0);
 372  							}
 373  
 374  							break;
 375  
 376  						case 0x19: // VMDATAH
 377  							if (!PPU.VMA.FullGraphicCount)
 378  							{
 379  								do
 380  								{
 381  									Work = *(base + p);
 382  									REGISTER_2119_linear(Work);
 383  									UPDATE_COUNTERS;
 384  								} while (--count > 0);
 385  							}
 386  							else
 387  							{
 388  								do
 389  								{
 390  									Work = *(base + p);
 391  									REGISTER_2119_tile(Work);
 392  									UPDATE_COUNTERS;
 393  								} while (--count > 0);
 394  							}
 395  
 396  							break;
 397  
 398  						case 0x22: // CGDATA
 399  							do
 400  							{
 401  								Work = *(base + p);
 402  								REGISTER_2122(Work);
 403  								UPDATE_COUNTERS;
 404  							} while (--count > 0);
 405  
 406  							break;
 407  
 408  						case 0x80: // WMDATA
 409  							if (!CPU.InWRAMDMAorHDMA)
 410  							{
 411  								do
 412  								{
 413  									Work = *(base + p);
 414  									REGISTER_2180(Work);
 415  									UPDATE_COUNTERS;
 416  								} while (--count > 0);
 417  							}
 418  							else
 419  							{
 420  								do
 421  								{
 422  									UPDATE_COUNTERS;
 423  								} while (--count > 0);
 424  							}
 425  
 426  							break;
 427  
 428  						default:
 429  							do
 430  							{
 431  								Work = *(base + p);
 432  								S9xSetPPU(Work, 0x2100 + d->BAddress);
 433  								UPDATE_COUNTERS;
 434  							} while (--count > 0);
 435  
 436  							break;
 437  					}
 438  				}
 439  				else
 440  				if (d->TransferMode == 1 || d->TransferMode == 5)
 441  				{
 442  					if (d->BAddress == 0x18)
 443  					{
 444  						// VMDATAL
 445  						if (!PPU.VMA.FullGraphicCount)
 446  						{
 447  							switch (b)
 448  							{
 449  								default:
 450  								while (count > 1)
 451  								{
 452  									Work = *(base + p);
 453  									REGISTER_2118_linear(Work);
 454  									UPDATE_COUNTERS;
 455  									count--;
 456  								// Fall through
 457  								case 1:
 458  									OpenBus = *(base + p);
 459  									REGISTER_2119_linear(OpenBus);
 460  									UPDATE_COUNTERS;
 461  									count--;
 462  								}
 463  							}
 464  
 465  							if (count == 1)
 466  							{
 467  								Work = *(base + p);
 468  								REGISTER_2118_linear(Work);
 469  								UPDATE_COUNTERS;
 470  								b = 1;
 471  							}
 472  							else
 473  								b = 0;
 474  						}
 475  						else
 476  						{
 477  							switch (b)
 478  							{
 479  								default:
 480  								while (count > 1)
 481  								{
 482  									Work = *(base + p);
 483  									REGISTER_2118_tile(Work);
 484  									UPDATE_COUNTERS;
 485  									count--;
 486  								// Fall through
 487  								case 1:
 488  									Work = *(base + p);
 489  									REGISTER_2119_tile(Work);
 490  									UPDATE_COUNTERS;
 491  									count--;
 492  								}
 493  							}
 494  
 495  							if (count == 1)
 496  							{
 497  								Work = *(base + p);
 498  								REGISTER_2118_tile(Work);
 499  								UPDATE_COUNTERS;
 500  								b = 1;
 501  							}
 502  							else
 503  								b = 0;
 504  						}
 505  					}
 506  					else
 507  					{
 508  						// DMA mode 1 general case
 509  						switch (b)
 510  						{
 511  							default:
 512  							while (count > 1)
 513  							{
 514  								Work = *(base + p);
 515  								S9xSetPPU(Work, 0x2100 + d->BAddress);
 516  								UPDATE_COUNTERS;
 517  								count--;
 518  							// Fall through
 519  							case 1:
 520  								Work = *(base + p);
 521  								S9xSetPPU(Work, 0x2101 + d->BAddress);
 522  								UPDATE_COUNTERS;
 523  								count--;
 524  							}
 525  						}
 526  
 527  						if (count == 1)
 528  						{
 529  							Work = *(base + p);
 530  							S9xSetPPU(Work, 0x2100 + d->BAddress);
 531  							UPDATE_COUNTERS;
 532  							b = 1;
 533  						}
 534  						else
 535  							b = 0;
 536  					}
 537  				}
 538  				else
 539  				if (d->TransferMode == 3 || d->TransferMode == 7)
 540  				{
 541  					switch (b)
 542  					{
 543  						default:
 544  						do
 545  						{
 546  							Work = *(base + p);
 547  							S9xSetPPU(Work, 0x2100 + d->BAddress);
 548  							UPDATE_COUNTERS;
 549  							if (--count <= 0)
 550  							{
 551  								b = 1;
 552  								break;
 553  							}
 554  						// Fall through
 555  						case 1:
 556  							Work = *(base + p);
 557  							S9xSetPPU(Work, 0x2100 + d->BAddress);
 558  							UPDATE_COUNTERS;
 559  							if (--count <= 0)
 560  							{
 561  								b = 2;
 562  								break;
 563  							}
 564  						// Fall through
 565  						case 2:
 566  							Work = *(base + p);
 567  							S9xSetPPU(Work, 0x2101 + d->BAddress);
 568  							UPDATE_COUNTERS;
 569  							if (--count <= 0)
 570  							{
 571  								b = 3;
 572  								break;
 573  							}
 574  						// Fall through
 575  						case 3:
 576  							Work = *(base + p);
 577  							S9xSetPPU(Work, 0x2101 + d->BAddress);
 578  							UPDATE_COUNTERS;
 579  							if (--count <= 0)
 580  							{
 581  								b = 0;
 582  								break;
 583  							}
 584  						} while (1);
 585  					}
 586  				}
 587  				else
 588  				if (d->TransferMode == 4)
 589  				{
 590  					switch (b)
 591  					{
 592  						default:
 593  						do
 594  						{
 595  							Work = *(base + p);
 596  							S9xSetPPU(Work, 0x2100 + d->BAddress);
 597  							UPDATE_COUNTERS;
 598  							if (--count <= 0)
 599  							{
 600  								b = 1;
 601  								break;
 602  							}
 603  						// Fall through
 604  						case 1:
 605  							Work = *(base + p);
 606  							S9xSetPPU(Work, 0x2101 + d->BAddress);
 607  							UPDATE_COUNTERS;
 608  							if (--count <= 0)
 609  							{
 610  								b = 2;
 611  								break;
 612  							}
 613  						// Fall through
 614  						case 2:
 615  							Work = *(base + p);
 616  							S9xSetPPU(Work, 0x2102 + d->BAddress);
 617  							UPDATE_COUNTERS;
 618  							if (--count <= 0)
 619  							{
 620  								b = 3;
 621  								break;
 622  							}
 623  						// Fall through
 624  						case 3:
 625  							Work = *(base + p);
 626  							S9xSetPPU(Work, 0x2103 + d->BAddress);
 627  							UPDATE_COUNTERS;
 628  							if (--count <= 0)
 629  							{
 630  								b = 0;
 631  								break;
 632  							}
 633  						} while (1);
 634  					}
 635  				}
 636  			#ifdef DEBUGGER
 637  				else
 638  				{
 639  					sprintf(String, "Unknown DMA transfer mode: %d on channel %d\n", d->TransferMode, Channel);
 640  					S9xMessage(S9X_TRACE, S9X_DMA_TRACE, String);
 641  				}
 642  			#endif
 643  			}
 644  
 645  			if (rem <= 0)
 646  				break;
 647  
 648  			base = S9xGetBasePointer((d->ABank << 16) + d->AAddress);
 649  			count = MEMMAP_BLOCK_SIZE;
 650  			inWRAM_DMA = ((d->ABank == 0x7e || d->ABank == 0x7f || (!(d->ABank & 0x40) && d->AAddress < 0x2000)));
 651  		}
 652  
 653  		#undef UPDATE_COUNTERS
 654  	}
 655      else
 656      {
 657  		// PPU -> CPU
 658  
 659  		// 8 cycles per byte
 660  		#define	UPDATE_COUNTERS \
 661  			d->TransferBytes--; \
 662  			d->AAddress += inc; \
 663  			if (!addCyclesInDMA(Channel)) \
 664  			{ \
 665  				CPU.InDMA = FALSE; \
 666  				CPU.InDMAorHDMA = FALSE; \
 667  				CPU.InWRAMDMAorHDMA = FALSE; \
 668  				CPU.CurrentDMAorHDMAChannel = -1; \
 669  				return (FALSE); \
 670  			}
 671  
 672  		if (d->BAddress > 0x80 - 4 && d->BAddress <= 0x83 && !(d->ABank & 0x40))
 673  		{
 674  			// REVERSE-DMA REALLY-SLOW PATH
 675  			do
 676  			{
 677  				switch (d->TransferMode)
 678  				{
 679  					case 0:
 680  					case 2:
 681  					case 6:
 682  						CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000);
 683  						Work = S9xGetPPU(0x2100 + d->BAddress);
 684  						S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
 685  						UPDATE_COUNTERS;
 686  						count--;
 687  
 688  						break;
 689  
 690  					case 1:
 691  					case 5:
 692  						CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000);
 693  						Work = S9xGetPPU(0x2100 + d->BAddress);
 694  						S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
 695  						UPDATE_COUNTERS;
 696  						if (!--count)
 697  							break;
 698  
 699  						CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000);
 700  						Work = S9xGetPPU(0x2101 + d->BAddress);
 701  						S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
 702  						UPDATE_COUNTERS;
 703  						count--;
 704  
 705  						break;
 706  
 707  					case 3:
 708  					case 7:
 709  						CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000);
 710  						Work = S9xGetPPU(0x2100 + d->BAddress);
 711  						S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
 712  						UPDATE_COUNTERS;
 713  						if (!--count)
 714  							break;
 715  
 716  						CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000);
 717  						Work = S9xGetPPU(0x2100 + d->BAddress);
 718  						S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
 719  						UPDATE_COUNTERS;
 720  						if (!--count)
 721  							break;
 722  
 723  						CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000);
 724  						Work = S9xGetPPU(0x2101 + d->BAddress);
 725  						S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
 726  						UPDATE_COUNTERS;
 727  						if (!--count)
 728  							break;
 729  
 730  						CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000);
 731  						Work = S9xGetPPU(0x2101 + d->BAddress);
 732  						S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
 733  						UPDATE_COUNTERS;
 734  						count--;
 735  
 736  						break;
 737  
 738  					case 4:
 739  						CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000);
 740  						Work = S9xGetPPU(0x2100 + d->BAddress);
 741  						S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
 742  						UPDATE_COUNTERS;
 743  						if (!--count)
 744  							break;
 745  
 746  						CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000);
 747  						Work = S9xGetPPU(0x2101 + d->BAddress);
 748  						S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
 749  						UPDATE_COUNTERS;
 750  						if (!--count)
 751  							break;
 752  
 753  						CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000);
 754  						Work = S9xGetPPU(0x2102 + d->BAddress);
 755  						S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
 756  						UPDATE_COUNTERS;
 757  						if (!--count)
 758  							break;
 759  
 760  						CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000);
 761  						Work = S9xGetPPU(0x2103 + d->BAddress);
 762  						S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
 763  						UPDATE_COUNTERS;
 764  						count--;
 765  
 766  						break;
 767  
 768  					default:
 769  					#ifdef DEBUGGER
 770  						sprintf(String, "Unknown DMA transfer mode: %d on channel %d\n", d->TransferMode, Channel);
 771  						S9xMessage(S9X_TRACE, S9X_DMA_TRACE, String);
 772  					#endif
 773  						while (count)
 774  						{
 775  							UPDATE_COUNTERS;
 776  							count--;
 777  						}
 778  
 779  						break;
 780  				}
 781  			} while (count);
 782  		}
 783  		else
 784  		{
 785  			// REVERSE-DMA FASTER PATH
 786  			CPU.InWRAMDMAorHDMA = (d->ABank == 0x7e || d->ABank == 0x7f);
 787  			do
 788  			{
 789  				switch (d->TransferMode)
 790  				{
 791  					case 0:
 792  					case 2:
 793  					case 6:
 794  						Work = S9xGetPPU(0x2100 + d->BAddress);
 795  						S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
 796  						UPDATE_COUNTERS;
 797  						count--;
 798  
 799  						break;
 800  
 801  					case 1:
 802  					case 5:
 803  						Work = S9xGetPPU(0x2100 + d->BAddress);
 804  						S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
 805  						UPDATE_COUNTERS;
 806  						if (!--count)
 807  							break;
 808  
 809  						Work = S9xGetPPU(0x2101 + d->BAddress);
 810  						S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
 811  						UPDATE_COUNTERS;
 812  						count--;
 813  
 814  						break;
 815  
 816  					case 3:
 817  					case 7:
 818  						Work = S9xGetPPU(0x2100 + d->BAddress);
 819  						S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
 820  						UPDATE_COUNTERS;
 821  						if (!--count)
 822  							break;
 823  
 824  						Work = S9xGetPPU(0x2100 + d->BAddress);
 825  						S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
 826  						UPDATE_COUNTERS;
 827  						if (!--count)
 828  							break;
 829  
 830  						Work = S9xGetPPU(0x2101 + d->BAddress);
 831  						S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
 832  						UPDATE_COUNTERS;
 833  						if (!--count)
 834  							break;
 835  
 836  						Work = S9xGetPPU(0x2101 + d->BAddress);
 837  						S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
 838  						UPDATE_COUNTERS;
 839  						count--;
 840  
 841  						break;
 842  
 843  					case 4:
 844  						Work = S9xGetPPU(0x2100 + d->BAddress);
 845  						S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
 846  						UPDATE_COUNTERS;
 847  						if (!--count)
 848  							break;
 849  
 850  						Work = S9xGetPPU(0x2101 + d->BAddress);
 851  						S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
 852  						UPDATE_COUNTERS;
 853  						if (!--count)
 854  							break;
 855  
 856  						Work = S9xGetPPU(0x2102 + d->BAddress);
 857  						S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
 858  						UPDATE_COUNTERS;
 859  						if (!--count)
 860  							break;
 861  
 862  						Work = S9xGetPPU(0x2103 + d->BAddress);
 863  						S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
 864  						UPDATE_COUNTERS;
 865  						count--;
 866  
 867  						break;
 868  
 869  					default:
 870  					#ifdef DEBUGGER
 871  						sprintf(String, "Unknown DMA transfer mode: %d on channel %d\n", d->TransferMode, Channel);
 872  						S9xMessage(S9X_TRACE, S9X_DMA_TRACE, String);
 873  					#endif
 874  						while (count)
 875  						{
 876  							UPDATE_COUNTERS;
 877  							count--;
 878  						}
 879  
 880  						break;
 881  				}
 882  			} while (count);
 883  		}
 884  	}
 885  
 886  	if (CPU.NMIPending && (CPU.NMITriggerPos != 0xffff))
 887  	{
 888  		CPU.NMITriggerPos = CPU.Cycles + SNES_NMI_DMA_DELAY;
 889  	}
 890  
 891  #if 0
 892  	// sanity check
 893      if (d->TransferBytes != 0)
 894  		fprintf(stderr,"DMA[%d] TransferBytes not 0! $21%02x Reverse:%d %04x\n", Channel, d->BAddress, d->ReverseTransfer, d->TransferBytes);
 895  #endif
 896  
 897  	CPU.InDMA = FALSE;
 898  	CPU.InDMAorHDMA = FALSE;
 899  	CPU.InWRAMDMAorHDMA = FALSE;
 900  	CPU.CurrentDMAorHDMAChannel = -1;
 901  
 902  	return (TRUE);
 903  }
 904  
 905  static inline bool8 HDMAReadLineCount (int d)
 906  {
 907  	// CPU.InDMA is set, so S9xGetXXX() / S9xSetXXX() incur no charges.
 908  
 909  	uint8	line;
 910  
 911  	line = S9xGetByte((DMA[d].ABank << 16) + DMA[d].Address);
 912  	ADD_CYCLES(SLOW_ONE_CYCLE);
 913  
 914  	if (!line)
 915  	{
 916  		DMA[d].Repeat = FALSE;
 917  		DMA[d].LineCount = 128;
 918  
 919  		if (DMA[d].HDMAIndirectAddressing)
 920  		{
 921  			if (PPU.HDMA & (0xfe << d))
 922  			{
 923  				DMA[d].Address++;
 924  				ADD_CYCLES(SLOW_ONE_CYCLE << 1);
 925  			}
 926  			else
 927  				ADD_CYCLES(SLOW_ONE_CYCLE);
 928  
 929  			DMA[d].IndirectAddress = S9xGetWord((DMA[d].ABank << 16) + DMA[d].Address);
 930  			DMA[d].Address++;
 931  		}
 932  
 933  		DMA[d].Address++;
 934  		DMA[d].MemPointer = NULL;
 935  
 936  		return (FALSE);
 937  	}
 938  	else
 939  	if (line == 0x80)
 940  	{
 941  		DMA[d].Repeat = TRUE;
 942  		DMA[d].LineCount = 128;
 943  	}
 944  	else
 945  	{
 946  		DMA[d].Repeat = !(line & 0x80);
 947  		DMA[d].LineCount = line & 0x7f;
 948  	}
 949  
 950  	DMA[d].Address++;
 951  	DMA[d].DoTransfer = TRUE;
 952  
 953  	if (DMA[d].HDMAIndirectAddressing)
 954  	{
 955  		ADD_CYCLES(SLOW_ONE_CYCLE << 1);
 956  		DMA[d].IndirectAddress = S9xGetWord((DMA[d].ABank << 16) + DMA[d].Address);
 957  		DMA[d].Address += 2;
 958  		DMA[d].MemPointer = GET_MEM_PTR((DMA[d].IndirectBank << 16) + DMA[d].IndirectAddress);
 959  	}
 960  	else
 961  		DMA[d].MemPointer = GET_MEM_PTR((DMA[d].ABank << 16) + DMA[d].Address);
 962  
 963  	return (TRUE);
 964  }
 965  
 966  void S9xStartHDMA (void)
 967  {
 968  	PPU.HDMA = Memory.CPU_IO[0x20c];
 969  	PPU.HDMAEnded = 0;
 970  
 971  	int32	tmpch;
 972  
 973  	CPU.InHDMA = TRUE;
 974  	CPU.InDMAorHDMA = TRUE;
 975  	tmpch = CPU.CurrentDMAorHDMAChannel;
 976  
 977  	// XXX: Not quite right...
 978  	if (PPU.HDMA != 0)
 979  	{
 980  		ADD_CYCLES(SNES_DMA_CPU_SYNC_CYCLES);
 981  	}
 982  
 983  	for (int i = 0; i < 8; i++)
 984  	{
 985  		if (PPU.HDMA & (1 << i))
 986  		{
 987  			CPU.CurrentDMAorHDMAChannel = i;
 988  
 989  			DMA[i].Address = DMA[i].AAddress;
 990  
 991  			if (!HDMAReadLineCount(i))
 992  			{
 993  				PPU.HDMA &= ~(1 << i);
 994  				PPU.HDMAEnded |= (1 << i);
 995  			}
 996  		}
 997  		else
 998  			DMA[i].DoTransfer = FALSE;
 999  	}
1000  
1001  	CPU.InHDMA = FALSE;
1002  	CPU.InDMAorHDMA = CPU.InDMA;
1003  	CPU.HDMARanInDMA = CPU.InDMA ? PPU.HDMA : 0;
1004  	CPU.CurrentDMAorHDMAChannel = tmpch;
1005  }
1006  
1007  uint8 S9xDoHDMA (uint8 byte)
1008  {
1009  	const uint8 HDMA_ModeByteCounts[8] = {1, 2, 2, 4, 4, 4, 2, 4};
1010  	struct SDMA *p;
1011  
1012  	uint32	ShiftedIBank;
1013  	uint16	IAddr;
1014  	bool8	temp;
1015  	int32	tmpch;
1016  	int	d;
1017  	uint8	mask;
1018  
1019  	CPU.InHDMA = TRUE;
1020  	CPU.InDMAorHDMA = TRUE;
1021  	CPU.HDMARanInDMA = CPU.InDMA ? byte : 0;
1022  	temp = CPU.InWRAMDMAorHDMA;
1023  	tmpch = CPU.CurrentDMAorHDMAChannel;
1024  
1025  	// XXX: Not quite right...
1026  	ADD_CYCLES(SNES_DMA_CPU_SYNC_CYCLES);
1027  
1028  	for (mask = 1, p = &DMA[0], d = 0; mask; mask <<= 1, p++, d++)
1029  	{
1030  		if (byte & mask)
1031  		{
1032  			CPU.InWRAMDMAorHDMA = FALSE;
1033  			CPU.CurrentDMAorHDMAChannel = d;
1034  
1035  			if (p->HDMAIndirectAddressing)
1036  			{
1037  				ShiftedIBank = (p->IndirectBank << 16);
1038  				IAddr = p->IndirectAddress;
1039  			}
1040  			else
1041  			{
1042  				ShiftedIBank = (p->ABank << 16);
1043  				IAddr = p->Address;
1044  			}
1045  
1046  			if (!DMA[d].MemPointer)
1047  				DMA[d].MemPointer = GET_MEM_PTR(ShiftedIBank + IAddr);
1048  
1049  			if (p->DoTransfer)
1050  			{
1051  			#ifdef DEBUGGER
1052  				if (Settings.TraceHDMA && p->DoTransfer)
1053  				{
1054  					sprintf(String, "H-DMA[%d] %s (%d) 0x%06X->0x21%02X %s, Count: %3d, Rep: %s, V-LINE: %3ld %02X%04X",
1055  							p-DMA, p->ReverseTransfer? "read" : "write",
1056  							p->TransferMode, ShiftedIBank+IAddr, p->BAddress,
1057  							p->HDMAIndirectAddressing ? "ind" : "abs",
1058  							p->LineCount,
1059  							p->Repeat ? "yes" : "no ", (long) CPU.V_Counter,
1060  							p->ABank, p->Address);
1061  					S9xMessage(S9X_TRACE, S9X_HDMA_TRACE, String);
1062  				}
1063  			#endif
1064  
1065  				if (!p->ReverseTransfer)
1066  				{
1067  					if ((IAddr & MEMMAP_MASK) + HDMA_ModeByteCounts[p->TransferMode] >= MEMMAP_BLOCK_SIZE)
1068  					{
1069  						// HDMA REALLY-SLOW PATH
1070  						DMA[d].MemPointer = NULL;
1071  
1072  						#define DOBYTE(Addr, RegOff) \
1073  							CPU.InWRAMDMAorHDMA = (ShiftedIBank == 0x7e0000 || ShiftedIBank == 0x7f0000 || \
1074  								(!(ShiftedIBank & 0x400000) && ((uint16) (Addr)) < 0x2000)); \
1075  							S9xSetPPU(S9xGetByte(ShiftedIBank + ((uint16) (Addr))), 0x2100 + p->BAddress + (RegOff));
1076  
1077  						switch (p->TransferMode)
1078  						{
1079  							case 0:
1080  								DOBYTE(IAddr, 0);
1081  								ADD_CYCLES(SLOW_ONE_CYCLE);
1082  								break;
1083  
1084  							case 5:
1085  								DOBYTE(IAddr + 0, 0);
1086  								ADD_CYCLES(SLOW_ONE_CYCLE);
1087  								DOBYTE(IAddr + 1, 1);
1088  								ADD_CYCLES(SLOW_ONE_CYCLE);
1089  								DOBYTE(IAddr + 2, 0);
1090  								ADD_CYCLES(SLOW_ONE_CYCLE);
1091  								DOBYTE(IAddr + 3, 1);
1092  								ADD_CYCLES(SLOW_ONE_CYCLE);
1093  								break;
1094  
1095  							case 1:
1096  								DOBYTE(IAddr + 0, 0);
1097  								ADD_CYCLES(SLOW_ONE_CYCLE);
1098  								DOBYTE(IAddr + 1, 1);
1099  								ADD_CYCLES(SLOW_ONE_CYCLE);
1100  								break;
1101  
1102  							case 2:
1103  							case 6:
1104  								DOBYTE(IAddr + 0, 0);
1105  								ADD_CYCLES(SLOW_ONE_CYCLE);
1106  								DOBYTE(IAddr + 1, 0);
1107  								ADD_CYCLES(SLOW_ONE_CYCLE);
1108  								break;
1109  
1110  							case 3:
1111  							case 7:
1112  								DOBYTE(IAddr + 0, 0);
1113  								ADD_CYCLES(SLOW_ONE_CYCLE);
1114  								DOBYTE(IAddr + 1, 0);
1115  								ADD_CYCLES(SLOW_ONE_CYCLE);
1116  								DOBYTE(IAddr + 2, 1);
1117  								ADD_CYCLES(SLOW_ONE_CYCLE);
1118  								DOBYTE(IAddr + 3, 1);
1119  								ADD_CYCLES(SLOW_ONE_CYCLE);
1120  								break;
1121  
1122  							case 4:
1123  								DOBYTE(IAddr + 0, 0);
1124  								ADD_CYCLES(SLOW_ONE_CYCLE);
1125  								DOBYTE(IAddr + 1, 1);
1126  								ADD_CYCLES(SLOW_ONE_CYCLE);
1127  								DOBYTE(IAddr + 2, 2);
1128  								ADD_CYCLES(SLOW_ONE_CYCLE);
1129  								DOBYTE(IAddr + 3, 3);
1130  								ADD_CYCLES(SLOW_ONE_CYCLE);
1131  								break;
1132  						}
1133  
1134  						#undef DOBYTE
1135  					}
1136  					else
1137  					{
1138  						CPU.InWRAMDMAorHDMA = (ShiftedIBank == 0x7e0000 || ShiftedIBank == 0x7f0000 ||
1139  							(!(ShiftedIBank & 0x400000) && IAddr < 0x2000));
1140  
1141  						if (!DMA[d].MemPointer)
1142  						{
1143  							// HDMA SLOW PATH
1144  							uint32	Addr = ShiftedIBank + IAddr;
1145  
1146  							switch (p->TransferMode)
1147  							{
1148  								case 0:
1149  									S9xSetPPU(S9xGetByte(Addr), 0x2100 + p->BAddress);
1150  									ADD_CYCLES(SLOW_ONE_CYCLE);
1151  									break;
1152  
1153  								case 5:
1154  									S9xSetPPU(S9xGetByte(Addr + 0), 0x2100 + p->BAddress);
1155  									ADD_CYCLES(SLOW_ONE_CYCLE);
1156  									S9xSetPPU(S9xGetByte(Addr + 1), 0x2101 + p->BAddress);
1157  									ADD_CYCLES(SLOW_ONE_CYCLE);
1158  									Addr += 2;
1159  									/* fall through */
1160  								case 1:
1161  									S9xSetPPU(S9xGetByte(Addr + 0), 0x2100 + p->BAddress);
1162  									ADD_CYCLES(SLOW_ONE_CYCLE);
1163  									S9xSetPPU(S9xGetByte(Addr + 1), 0x2101 + p->BAddress);
1164  									ADD_CYCLES(SLOW_ONE_CYCLE);
1165  									break;
1166  
1167  								case 2:
1168  								case 6:
1169  									S9xSetPPU(S9xGetByte(Addr + 0), 0x2100 + p->BAddress);
1170  									ADD_CYCLES(SLOW_ONE_CYCLE);
1171  									S9xSetPPU(S9xGetByte(Addr + 1), 0x2100 + p->BAddress);
1172  									ADD_CYCLES(SLOW_ONE_CYCLE);
1173  									break;
1174  
1175  								case 3:
1176  								case 7:
1177  									S9xSetPPU(S9xGetByte(Addr + 0), 0x2100 + p->BAddress);
1178  									ADD_CYCLES(SLOW_ONE_CYCLE);
1179  									S9xSetPPU(S9xGetByte(Addr + 1), 0x2100 + p->BAddress);
1180  									ADD_CYCLES(SLOW_ONE_CYCLE);
1181  									S9xSetPPU(S9xGetByte(Addr + 2), 0x2101 + p->BAddress);
1182  									ADD_CYCLES(SLOW_ONE_CYCLE);
1183  									S9xSetPPU(S9xGetByte(Addr + 3), 0x2101 + p->BAddress);
1184  									ADD_CYCLES(SLOW_ONE_CYCLE);
1185  									break;
1186  
1187  								case 4:
1188  									S9xSetPPU(S9xGetByte(Addr + 0), 0x2100 + p->BAddress);
1189  									ADD_CYCLES(SLOW_ONE_CYCLE);
1190  									S9xSetPPU(S9xGetByte(Addr + 1), 0x2101 + p->BAddress);
1191  									ADD_CYCLES(SLOW_ONE_CYCLE);
1192  									S9xSetPPU(S9xGetByte(Addr + 2), 0x2102 + p->BAddress);
1193  									ADD_CYCLES(SLOW_ONE_CYCLE);
1194  									S9xSetPPU(S9xGetByte(Addr + 3), 0x2103 + p->BAddress);
1195  									ADD_CYCLES(SLOW_ONE_CYCLE);
1196  									break;
1197  							}
1198  						}
1199  						else
1200  						{
1201  							// HDMA FAST PATH
1202  							switch (p->TransferMode)
1203  							{
1204  								case 0:
1205  									S9xSetPPU(*DMA[d].MemPointer++, 0x2100 + p->BAddress);
1206  									ADD_CYCLES(SLOW_ONE_CYCLE);
1207  									break;
1208  
1209  								case 5:
1210  									S9xSetPPU(*(DMA[d].MemPointer + 0), 0x2100 + p->BAddress);
1211  									ADD_CYCLES(SLOW_ONE_CYCLE);
1212  									S9xSetPPU(*(DMA[d].MemPointer + 1), 0x2101 + p->BAddress);
1213  									ADD_CYCLES(SLOW_ONE_CYCLE);
1214  									DMA[d].MemPointer += 2;
1215  									/* fall through */
1216  								case 1:
1217  									S9xSetPPU(*(DMA[d].MemPointer + 0), 0x2100 + p->BAddress);
1218  									ADD_CYCLES(SLOW_ONE_CYCLE);
1219  									// XXX: All HDMA should read to MDR first. This one just
1220  									// happens to fix Speedy Gonzales.
1221  									OpenBus = *(DMA[d].MemPointer + 1);
1222  									S9xSetPPU(OpenBus, 0x2101 + p->BAddress);
1223  									ADD_CYCLES(SLOW_ONE_CYCLE);
1224  									DMA[d].MemPointer += 2;
1225  									break;
1226  
1227  								case 2:
1228  								case 6:
1229  									S9xSetPPU(*(DMA[d].MemPointer + 0), 0x2100 + p->BAddress);
1230  									ADD_CYCLES(SLOW_ONE_CYCLE);
1231  									S9xSetPPU(*(DMA[d].MemPointer + 1), 0x2100 + p->BAddress);
1232  									ADD_CYCLES(SLOW_ONE_CYCLE);
1233  									DMA[d].MemPointer += 2;
1234  									break;
1235  
1236  								case 3:
1237  								case 7:
1238  									S9xSetPPU(*(DMA[d].MemPointer + 0), 0x2100 + p->BAddress);
1239  									ADD_CYCLES(SLOW_ONE_CYCLE);
1240  									S9xSetPPU(*(DMA[d].MemPointer + 1), 0x2100 + p->BAddress);
1241  									ADD_CYCLES(SLOW_ONE_CYCLE);
1242  									S9xSetPPU(*(DMA[d].MemPointer + 2), 0x2101 + p->BAddress);
1243  									ADD_CYCLES(SLOW_ONE_CYCLE);
1244  									S9xSetPPU(*(DMA[d].MemPointer + 3), 0x2101 + p->BAddress);
1245  									ADD_CYCLES(SLOW_ONE_CYCLE);
1246  									DMA[d].MemPointer += 4;
1247  									break;
1248  
1249  								case 4:
1250  									S9xSetPPU(*(DMA[d].MemPointer + 0), 0x2100 + p->BAddress);
1251  									ADD_CYCLES(SLOW_ONE_CYCLE);
1252  									S9xSetPPU(*(DMA[d].MemPointer + 1), 0x2101 + p->BAddress);
1253  									ADD_CYCLES(SLOW_ONE_CYCLE);
1254  									S9xSetPPU(*(DMA[d].MemPointer + 2), 0x2102 + p->BAddress);
1255  									ADD_CYCLES(SLOW_ONE_CYCLE);
1256  									S9xSetPPU(*(DMA[d].MemPointer + 3), 0x2103 + p->BAddress);
1257  									ADD_CYCLES(SLOW_ONE_CYCLE);
1258  									DMA[d].MemPointer += 4;
1259  									break;
1260  							}
1261  						}
1262  					}
1263  				}
1264  				else
1265  				{
1266  					// REVERSE HDMA REALLY-SLOW PATH
1267  					// anomie says: Since this is apparently never used
1268  					// (otherwise we would have noticed before now), let's not bother with faster paths.
1269  					DMA[d].MemPointer = NULL;
1270  
1271  					#define DOBYTE(Addr, RegOff) \
1272  						CPU.InWRAMDMAorHDMA = (ShiftedIBank == 0x7e0000 || ShiftedIBank == 0x7f0000 || \
1273  							(!(ShiftedIBank & 0x400000) && ((uint16) (Addr)) < 0x2000)); \
1274  						S9xSetByte(S9xGetPPU(0x2100 + p->BAddress + (RegOff)), ShiftedIBank + ((uint16) (Addr)));
1275  
1276  					switch (p->TransferMode)
1277  					{
1278  						case 0:
1279  							DOBYTE(IAddr, 0);
1280  							ADD_CYCLES(SLOW_ONE_CYCLE);
1281  							break;
1282  
1283  						case 5:
1284  							DOBYTE(IAddr + 0, 0);
1285  							ADD_CYCLES(SLOW_ONE_CYCLE);
1286  							DOBYTE(IAddr + 1, 1);
1287  							ADD_CYCLES(SLOW_ONE_CYCLE);
1288  							DOBYTE(IAddr + 2, 0);
1289  							ADD_CYCLES(SLOW_ONE_CYCLE);
1290  							DOBYTE(IAddr + 3, 1);
1291  							ADD_CYCLES(SLOW_ONE_CYCLE);
1292  							break;
1293  
1294  						case 1:
1295  							DOBYTE(IAddr + 0, 0);
1296  							ADD_CYCLES(SLOW_ONE_CYCLE);
1297  							DOBYTE(IAddr + 1, 1);
1298  							ADD_CYCLES(SLOW_ONE_CYCLE);
1299  							break;
1300  
1301  						case 2:
1302  						case 6:
1303  							DOBYTE(IAddr + 0, 0);
1304  							ADD_CYCLES(SLOW_ONE_CYCLE);
1305  							DOBYTE(IAddr + 1, 0);
1306  							ADD_CYCLES(SLOW_ONE_CYCLE);
1307  							break;
1308  
1309  						case 3:
1310  						case 7:
1311  							DOBYTE(IAddr + 0, 0);
1312  							ADD_CYCLES(SLOW_ONE_CYCLE);
1313  							DOBYTE(IAddr + 1, 0);
1314  							ADD_CYCLES(SLOW_ONE_CYCLE);
1315  							DOBYTE(IAddr + 2, 1);
1316  							ADD_CYCLES(SLOW_ONE_CYCLE);
1317  							DOBYTE(IAddr + 3, 1);
1318  							ADD_CYCLES(SLOW_ONE_CYCLE);
1319  							break;
1320  
1321  						case 4:
1322  							DOBYTE(IAddr + 0, 0);
1323  							ADD_CYCLES(SLOW_ONE_CYCLE);
1324  							DOBYTE(IAddr + 1, 1);
1325  							ADD_CYCLES(SLOW_ONE_CYCLE);
1326  							DOBYTE(IAddr + 2, 2);
1327  							ADD_CYCLES(SLOW_ONE_CYCLE);
1328  							DOBYTE(IAddr + 3, 3);
1329  							ADD_CYCLES(SLOW_ONE_CYCLE);
1330  							break;
1331  					}
1332  
1333  					#undef DOBYTE
1334  				}
1335  			}
1336  		}
1337  	}
1338  
1339  	for (mask = 1, p = &DMA[0], d = 0; mask; mask <<= 1, p++, d++)
1340  	{
1341  		if (byte & mask)
1342  		{
1343  			if (p->DoTransfer)
1344  			{
1345  				if (p->HDMAIndirectAddressing)
1346  					p->IndirectAddress += HDMA_ModeByteCounts[p->TransferMode];
1347  				else
1348  					p->Address += HDMA_ModeByteCounts[p->TransferMode];
1349  			}
1350  
1351  			p->DoTransfer = !p->Repeat;
1352  
1353  			if (!--p->LineCount)
1354  			{
1355  				if (!HDMAReadLineCount(d))
1356  				{
1357  					byte &= ~mask;
1358  					PPU.HDMAEnded |= mask;
1359  					p->DoTransfer = FALSE;
1360  				}
1361  			}
1362  			else
1363  				ADD_CYCLES(SLOW_ONE_CYCLE);
1364  		}
1365  	}
1366  
1367  	CPU.InHDMA = FALSE;
1368  	CPU.InDMAorHDMA = CPU.InDMA;
1369  	CPU.InWRAMDMAorHDMA = temp;
1370  	CPU.CurrentDMAorHDMAChannel = tmpch;
1371  
1372  	return (byte);
1373  }
1374  
1375  void S9xResetDMA (void)
1376  {
1377  	for (int d = 0; d < 8; d++)
1378  	{
1379  		DMA[d].ReverseTransfer = TRUE;
1380  		DMA[d].HDMAIndirectAddressing = TRUE;
1381  		DMA[d].AAddressFixed = TRUE;
1382  		DMA[d].AAddressDecrement = TRUE;
1383  		DMA[d].TransferMode = 7;
1384  		DMA[d].BAddress = 0xff;
1385  		DMA[d].AAddress = 0xffff;
1386  		DMA[d].ABank = 0xff;
1387  		DMA[d].DMACount_Or_HDMAIndirectAddress = 0xffff;
1388  		DMA[d].IndirectBank = 0xff;
1389  		DMA[d].Address = 0xffff;
1390  		DMA[d].Repeat = FALSE;
1391  		DMA[d].LineCount = 0x7f;
1392  		DMA[d].UnknownByte = 0xff;
1393  		DMA[d].DoTransfer = FALSE;
1394  		DMA[d].UnusedBit43x0 = 1;
1395  	}
1396  }