snes9xgx/source/snes9x/dma.cpp

1637 lines
39 KiB
C++
Raw Normal View History

/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
2008-09-10 07:57:37 +02:00
2009-11-30 09:14:38 +01:00
#include "snes9x.h"
#include "memmap.h"
#include "dma.h"
2010-01-27 23:08:56 +01:00
#include "apu/apu.h"
#include "sdd1emu.h"
2010-01-27 23:08:56 +01:00
#include "spc7110emu.h"
#ifdef DEBUGGER
#include "missing.h"
#endif
#define ADD_CYCLES(n) { CPU.Cycles += (n); }
2008-09-10 07:57:37 +02:00
extern uint8 *HDMAMemPointers[8];
extern int HDMA_ModeByteCounts[8];
2010-01-27 23:08:56 +01:00
extern SPC7110 s7emu;
2008-09-10 07:57:37 +02:00
static uint8 sdd1_decode_buffer[0x10000];
2010-01-27 23:08:56 +01:00
static inline bool8 addCyclesInDMA (uint8);
static inline bool8 HDMAReadLineCount (int);
2008-09-10 07:57:37 +02:00
static inline bool8 addCyclesInDMA (uint8 dma_channel)
{
2010-01-27 23:08:56 +01:00
// Add 8 cycles per byte, sync APU, and do HC related events.
// If HDMA was done in S9xDoHEventProcessing(), check if it used the same channel as DMA.
ADD_CYCLES(SLOW_ONE_CYCLE);
2008-09-10 07:57:37 +02:00
while (CPU.Cycles >= CPU.NextEvent)
S9xDoHEventProcessing();
2008-09-10 07:57:37 +02:00
if (CPU.HDMARanInDMA & (1 << dma_channel))
{
CPU.HDMARanInDMA = 0;
2010-01-27 23:08:56 +01:00
#ifdef DEBUGGER
printf("HDMA and DMA use the same channel %d!\n", dma_channel);
2008-09-10 07:57:37 +02:00
#endif
// If HDMA triggers in the middle of DMA transfer and it uses the same channel,
// it kills the DMA transfer immediately. $43x2 and $43x5 stop updating.
2010-01-27 23:08:56 +01:00
return (FALSE);
2008-09-10 07:57:37 +02:00
}
2008-09-10 07:57:37 +02:00
CPU.HDMARanInDMA = 0;
2010-01-27 23:08:56 +01:00
return (TRUE);
2008-09-10 07:57:37 +02:00
}
2008-09-10 07:57:37 +02:00
bool8 S9xDoDMA (uint8 Channel)
{
CPU.InDMA = TRUE;
CPU.InDMAorHDMA = TRUE;
2010-01-27 23:08:56 +01:00
CPU.CurrentDMAorHDMAChannel = Channel;
2008-09-10 07:57:37 +02:00
SDMA *d = &DMA[Channel];
2008-09-10 07:57:37 +02:00
// Check invalid DMA first
if ((d->ABank == 0x7E || d->ABank == 0x7F) && d->BAddress == 0x80 && !d->ReverseTransfer)
{
// Attempting a DMA from WRAM to $2180 will not work, WRAM will not be written.
// Attempting a DMA from $2180 to WRAM will similarly not work,
// the value written is (initially) the OpenBus value.
// In either case, the address in $2181-3 is not incremented.
// Does an invalid DMA actually take time?
// I'd say yes, since 'invalid' is probably just the WRAM chip
// not being able to read and write itself at the same time
// And no, PPU.WRAM should not be updated.
int32 c = d->TransferBytes;
// Writing $0000 to $43x5 actually results in a transfer of $10000 bytes, not 0.
if (c == 0)
c = 0x10000;
// 8 cycles per channel
ADD_CYCLES(SLOW_ONE_CYCLE);
2008-09-10 07:57:37 +02:00
// 8 cycles per byte
while (c)
{
d->TransferBytes--;
d->AAddress++;
c--;
if (!addCyclesInDMA(Channel))
{
CPU.InDMA = FALSE;
CPU.InDMAorHDMA = FALSE;
2010-01-27 23:08:56 +01:00
CPU.CurrentDMAorHDMAChannel = -1;
return (FALSE);
2008-09-10 07:57:37 +02:00
}
}
#ifdef DEBUGGER
if (Settings.TraceDMA)
{
sprintf(String, "DMA[%d]: WRAM Bank:%02X->$2180", Channel, d->ABank);
S9xMessage(S9X_TRACE, S9X_DMA_TRACE, String);
}
#endif
CPU.InDMA = FALSE;
CPU.InDMAorHDMA = FALSE;
2010-01-27 23:08:56 +01:00
CPU.CurrentDMAorHDMAChannel = -1;
return (TRUE);
2008-09-10 07:57:37 +02:00
}
2008-09-10 07:57:37 +02:00
// Prepare for accessing $2118-2119
switch (d->BAddress)
{
2010-01-27 23:08:56 +01:00
case 0x18:
case 0x19:
if (IPPU.RenderThisFrame)
FLUSH_REDRAW();
break;
2008-09-10 07:57:37 +02:00
}
2008-09-10 07:57:37 +02:00
int32 inc = d->AAddressFixed ? 0 : (!d->AAddressDecrement ? 1 : -1);
int32 count = d->TransferBytes;
// Writing $0000 to $43x5 actually results in a transfer of $10000 bytes, not 0.
if (count == 0)
count = 0x10000;
2008-09-10 07:57:37 +02:00
// Prepare for custom chip DMA
2008-09-10 07:57:37 +02:00
// S-DD1
2008-09-10 07:57:37 +02:00
uint8 *in_sdd1_dma = NULL;
2008-09-10 07:57:37 +02:00
if (Settings.SDD1)
{
if (d->AAddressFixed && Memory.FillRAM[0x4801] > 0)
{
// XXX: Should probably verify that we're DMAing from ROM?
// And somewhere we should make sure we're not running across a mapping boundary too.
// Hacky support for pre-decompressed S-DD1 data
inc = !d->AAddressDecrement ? 1 : -1;
2010-01-27 23:08:56 +01:00
uint8 *in_ptr = S9xGetBasePointer(((d->ABank << 16) | d->AAddress));
if (in_ptr)
2008-09-10 07:57:37 +02:00
{
2010-01-27 23:08:56 +01:00
in_ptr += d->AAddress;
SDD1_decompress(sdd1_decode_buffer, in_ptr, d->TransferBytes);
2008-09-10 07:57:37 +02:00
}
2010-01-27 23:08:56 +01:00
#ifdef DEBUGGER
2008-09-10 07:57:37 +02:00
else
{
2010-01-27 23:08:56 +01:00
sprintf(String, "S-DD1: DMA from non-block address $%02X:%04X", d->ABank, d->AAddress);
S9xMessage(S9X_WARNING, S9X_DMA_TRACE, String);
2009-11-30 09:14:38 +01:00
}
2010-01-27 23:08:56 +01:00
#endif
in_sdd1_dma = sdd1_decode_buffer;
2008-09-10 07:57:37 +02:00
}
Memory.FillRAM[0x4801] = 0;
}
2008-09-10 07:57:37 +02:00
// SPC7110
2010-01-27 23:08:56 +01:00
uint8 *spc7110_dma = NULL;
2010-01-27 23:08:56 +01:00
if (Settings.SPC7110)
2008-09-10 07:57:37 +02:00
{
if (d->AAddress == 0x4800 || d->ABank == 0x50)
{
2010-01-27 23:08:56 +01:00
spc7110_dma = new uint8[d->TransferBytes];
for (int i = 0; i < d->TransferBytes; i++)
spc7110_dma[i] = s7emu.decomp.read();
2009-11-30 09:14:38 +01:00
2010-01-27 23:08:56 +01:00
int32 icount = s7emu.r4809 | (s7emu.r480a << 8);
2008-09-10 07:57:37 +02:00
icount -= d->TransferBytes;
2010-01-27 23:08:56 +01:00
s7emu.r4809 = icount & 0x00ff;
s7emu.r480a = (icount & 0xff00) >> 8;
2008-09-10 07:57:37 +02:00
inc = 1;
d->AAddress -= count;
}
}
2008-09-10 07:57:37 +02:00
// SA-1
2008-09-10 07:57:37 +02:00
bool8 in_sa1_dma = FALSE;
2008-09-10 07:57:37 +02:00
if (Settings.SA1)
{
if (SA1.in_char_dma && d->BAddress == 0x18 && (d->ABank & 0xf0) == 0x40)
{
// Perform packed bitmap to PPU character format conversion on the data
// before transmitting it to V-RAM via-DMA.
int32 num_chars = 1 << ((Memory.FillRAM[0x2231] >> 2) & 7);
int32 depth = (Memory.FillRAM[0x2231] & 3) == 0 ? 8 : (Memory.FillRAM[0x2231] & 3) == 1 ? 4 : 2;
int32 bytes_per_char = 8 * depth;
int32 bytes_per_line = depth * num_chars;
int32 char_line_bytes = bytes_per_char * num_chars;
uint32 addr = (d->AAddress / char_line_bytes) * char_line_bytes;
2010-01-27 23:08:56 +01:00
uint8 *base = S9xGetBasePointer((d->ABank << 16) + addr);
2008-09-10 07:57:37 +02:00
if (!base)
{
sprintf(String, "SA-1: DMA from non-block address $%02X:%04X", d->ABank, addr);
S9xMessage(S9X_WARNING, S9X_DMA_TRACE, String);
base = Memory.ROM;
}
base += addr;
uint8 *buffer = &Memory.ROM[CMemory::MAX_ROM_SIZE - 0x10000];
uint8 *p = buffer;
uint32 inc_sa1 = char_line_bytes - (d->AAddress % char_line_bytes);
uint32 char_count = inc_sa1 / bytes_per_char;
in_sa1_dma = TRUE;
#if 0
printf("SA-1 DMA: %08x,", base);
printf("depth = %d, count = %d, bytes_per_char = %d, bytes_per_line = %d, num_chars = %d, char_line_bytes = %d\n",
depth, count, bytes_per_char, bytes_per_line, num_chars, char_line_bytes);
#endif
switch (depth)
{
2010-01-27 23:08:56 +01:00
case 2:
for (int32 i = 0; i < count; i += inc_sa1, base += char_line_bytes, inc_sa1 = char_line_bytes, char_count = num_chars)
2008-09-10 07:57:37 +02:00
{
2010-01-27 23:08:56 +01:00
uint8 *line = base + (num_chars - char_count) * 2;
for (uint32 j = 0; j < char_count && p - buffer < count; j++, line += 2)
2008-09-10 07:57:37 +02:00
{
2010-01-27 23:08:56 +01:00
uint8 *q = line;
for (int32 l = 0; l < 8; l++, q += bytes_per_line)
2008-09-10 07:57:37 +02:00
{
2010-01-27 23:08:56 +01:00
for (int32 b = 0; b < 2; b++)
{
uint8 r = *(q + b);
*(p + 0) = (*(p + 0) << 1) | ((r >> 0) & 1);
*(p + 1) = (*(p + 1) << 1) | ((r >> 1) & 1);
*(p + 0) = (*(p + 0) << 1) | ((r >> 2) & 1);
*(p + 1) = (*(p + 1) << 1) | ((r >> 3) & 1);
*(p + 0) = (*(p + 0) << 1) | ((r >> 4) & 1);
*(p + 1) = (*(p + 1) << 1) | ((r >> 5) & 1);
*(p + 0) = (*(p + 0) << 1) | ((r >> 6) & 1);
*(p + 1) = (*(p + 1) << 1) | ((r >> 7) & 1);
}
2009-11-30 09:14:38 +01:00
2010-01-27 23:08:56 +01:00
p += 2;
}
2008-09-10 07:57:37 +02:00
}
}
2010-01-27 23:08:56 +01:00
break;
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
case 4:
for (int32 i = 0; i < count; i += inc_sa1, base += char_line_bytes, inc_sa1 = char_line_bytes, char_count = num_chars)
2008-09-10 07:57:37 +02:00
{
2010-01-27 23:08:56 +01:00
uint8 *line = base + (num_chars - char_count) * 4;
for (uint32 j = 0; j < char_count && p - buffer < count; j++, line += 4)
2008-09-10 07:57:37 +02:00
{
2010-01-27 23:08:56 +01:00
uint8 *q = line;
for (int32 l = 0; l < 8; l++, q += bytes_per_line)
2008-09-10 07:57:37 +02:00
{
2010-01-27 23:08:56 +01:00
for (int32 b = 0; b < 4; b++)
{
uint8 r = *(q + b);
*(p + 0) = (*(p + 0) << 1) | ((r >> 0) & 1);
*(p + 1) = (*(p + 1) << 1) | ((r >> 1) & 1);
*(p + 16) = (*(p + 16) << 1) | ((r >> 2) & 1);
*(p + 17) = (*(p + 17) << 1) | ((r >> 3) & 1);
*(p + 0) = (*(p + 0) << 1) | ((r >> 4) & 1);
*(p + 1) = (*(p + 1) << 1) | ((r >> 5) & 1);
*(p + 16) = (*(p + 16) << 1) | ((r >> 6) & 1);
*(p + 17) = (*(p + 17) << 1) | ((r >> 7) & 1);
}
p += 2;
2008-09-10 07:57:37 +02:00
}
2010-01-27 23:08:56 +01:00
p += 32 - 16;
2008-09-10 07:57:37 +02:00
}
}
2010-01-27 23:08:56 +01:00
break;
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
case 8:
for (int32 i = 0; i < count; i += inc_sa1, base += char_line_bytes, inc_sa1 = char_line_bytes, char_count = num_chars)
2008-09-10 07:57:37 +02:00
{
2010-01-27 23:08:56 +01:00
uint8 *line = base + (num_chars - char_count) * 8;
for (uint32 j = 0; j < char_count && p - buffer < count; j++, line += 8)
2008-09-10 07:57:37 +02:00
{
2010-01-27 23:08:56 +01:00
uint8 *q = line;
for (int32 l = 0; l < 8; l++, q += bytes_per_line)
2008-09-10 07:57:37 +02:00
{
2010-01-27 23:08:56 +01:00
for (int32 b = 0; b < 8; b++)
{
uint8 r = *(q + b);
*(p + 0) = (*(p + 0) << 1) | ((r >> 0) & 1);
*(p + 1) = (*(p + 1) << 1) | ((r >> 1) & 1);
*(p + 16) = (*(p + 16) << 1) | ((r >> 2) & 1);
*(p + 17) = (*(p + 17) << 1) | ((r >> 3) & 1);
*(p + 32) = (*(p + 32) << 1) | ((r >> 4) & 1);
*(p + 33) = (*(p + 33) << 1) | ((r >> 5) & 1);
*(p + 48) = (*(p + 48) << 1) | ((r >> 6) & 1);
*(p + 49) = (*(p + 49) << 1) | ((r >> 7) & 1);
}
p += 2;
2008-09-10 07:57:37 +02:00
}
2010-01-27 23:08:56 +01:00
p += 64 - 16;
2008-09-10 07:57:37 +02:00
}
}
break;
}
}
}
#ifdef DEBUGGER
2008-09-10 07:57:37 +02:00
if (Settings.TraceDMA)
{
sprintf(String, "DMA[%d]: %s Mode:%d 0x%02X%04X->0x21%02X Bytes:%d (%s) V:%03d",
Channel, d->ReverseTransfer ? "PPU->CPU" : "CPU->PPU", d->TransferMode, d->ABank, d->AAddress, d->BAddress,
d->TransferBytes, d->AAddressFixed ? "fixed" : (d->AAddressDecrement ? "dec" : "inc"), CPU.V_Counter);
if (d->BAddress == 0x18 || d->BAddress == 0x19 || d->BAddress == 0x39 || d->BAddress == 0x3a)
sprintf(String, "%s VRAM: %04X (%d,%d) %s", String,
PPU.VMA.Address, PPU.VMA.Increment, PPU.VMA.FullGraphicCount, PPU.VMA.High ? "word" : "byte");
else
if (d->BAddress == 0x22 || d->BAddress == 0x3b)
sprintf(String, "%s CGRAM: %02X (%x)", String, PPU.CGADD, PPU.CGFLIP);
else
if (d->BAddress == 0x04 || d->BAddress == 0x38)
sprintf(String, "%s OBJADDR: %04X", String, PPU.OAMAddr);
S9xMessage(S9X_TRACE, S9X_DMA_TRACE, String);
}
#endif
2008-09-10 07:57:37 +02:00
// Do Transfer
2008-09-10 07:57:37 +02:00
uint8 Work;
2008-09-10 07:57:37 +02:00
// 8 cycles per channel
ADD_CYCLES(SLOW_ONE_CYCLE);
2008-09-10 07:57:37 +02:00
if (!d->ReverseTransfer)
{
// CPU -> PPU
int32 b = 0;
uint16 p = d->AAddress;
2010-01-27 23:08:56 +01:00
uint8 *base = S9xGetBasePointer((d->ABank << 16) + d->AAddress);
2008-09-10 07:57:37 +02:00
bool8 inWRAM_DMA;
int32 rem = count;
// Transfer per block if d->AAdressFixed is FALSE
count = d->AAddressFixed ? rem : (d->AAddressDecrement ? ((p & MEMMAP_MASK) + 1) : (MEMMAP_BLOCK_SIZE - (p & MEMMAP_MASK)));
// Settings for custom chip DMA
if (in_sa1_dma)
{
base = &Memory.ROM[CMemory::MAX_ROM_SIZE - 0x10000];
p = 0;
count = rem;
}
else
if (in_sdd1_dma)
{
base = in_sdd1_dma;
p = 0;
count = rem;
}
else
if (spc7110_dma)
{
base = spc7110_dma;
p = 0;
count = rem;
}
2008-09-10 07:57:37 +02:00
inWRAM_DMA = ((!in_sa1_dma && !in_sdd1_dma && !spc7110_dma) &&
(d->ABank == 0x7e || d->ABank == 0x7f || (!(d->ABank & 0x40) && d->AAddress < 0x2000)));
// 8 cycles per byte
#define UPDATE_COUNTERS \
d->TransferBytes--; \
d->AAddress += inc; \
p += inc; \
if (!addCyclesInDMA(Channel)) \
{ \
CPU.InDMA = FALSE; \
CPU.InDMAorHDMA = FALSE; \
CPU.InWRAMDMAorHDMA = FALSE; \
2010-01-27 23:08:56 +01:00
CPU.CurrentDMAorHDMAChannel = -1; \
return (FALSE); \
2008-09-10 07:57:37 +02:00
}
while (1)
{
if (count > rem)
count = rem;
rem -= count;
CPU.InWRAMDMAorHDMA = inWRAM_DMA;
if (!base)
{
// DMA SLOW PATH
if (d->TransferMode == 0 || d->TransferMode == 2 || d->TransferMode == 6)
{
do
{
Work = S9xGetByte((d->ABank << 16) + p);
S9xSetPPU(Work, 0x2100 + d->BAddress);
UPDATE_COUNTERS;
} while (--count > 0);
}
else
if (d->TransferMode == 1 || d->TransferMode == 5)
{
// This is a variation on Duff's Device. It is legal C/C++.
switch (b)
{
2010-01-27 23:08:56 +01:00
default:
2008-09-10 07:57:37 +02:00
while (count > 1)
{
Work = S9xGetByte((d->ABank << 16) + p);
S9xSetPPU(Work, 0x2100 + d->BAddress);
UPDATE_COUNTERS;
count--;
// Fall through
2010-01-27 23:08:56 +01:00
case 1:
2008-09-10 07:57:37 +02:00
Work = S9xGetByte((d->ABank << 16) + p);
S9xSetPPU(Work, 0x2101 + d->BAddress);
UPDATE_COUNTERS;
count--;
}
}
if (count == 1)
{
Work = S9xGetByte((d->ABank << 16) + p);
S9xSetPPU(Work, 0x2100 + d->BAddress);
UPDATE_COUNTERS;
b = 1;
}
else
b = 0;
}
else
if (d->TransferMode == 3 || d->TransferMode == 7)
{
switch (b)
{
2010-01-27 23:08:56 +01:00
default:
2008-09-10 07:57:37 +02:00
do
{
Work = S9xGetByte((d->ABank << 16) + p);
S9xSetPPU(Work, 0x2100 + d->BAddress);
UPDATE_COUNTERS;
if (--count <= 0)
{
b = 1;
break;
}
// Fall through
2010-01-27 23:08:56 +01:00
case 1:
2008-09-10 07:57:37 +02:00
Work = S9xGetByte((d->ABank << 16) + p);
S9xSetPPU(Work, 0x2100 + d->BAddress);
UPDATE_COUNTERS;
if (--count <= 0)
{
b = 2;
break;
}
// Fall through
2010-01-27 23:08:56 +01:00
case 2:
2008-09-10 07:57:37 +02:00
Work = S9xGetByte((d->ABank << 16) + p);
S9xSetPPU(Work, 0x2101 + d->BAddress);
UPDATE_COUNTERS;
if (--count <= 0)
{
b = 3;
break;
}
// Fall through
2010-01-27 23:08:56 +01:00
case 3:
2008-09-10 07:57:37 +02:00
Work = S9xGetByte((d->ABank << 16) + p);
S9xSetPPU(Work, 0x2101 + d->BAddress);
UPDATE_COUNTERS;
if (--count <= 0)
{
b = 0;
break;
}
} while (1);
}
}
else
if (d->TransferMode == 4)
{
switch (b)
{
2010-01-27 23:08:56 +01:00
default:
2008-09-10 07:57:37 +02:00
do
{
Work = S9xGetByte((d->ABank << 16) + p);
S9xSetPPU(Work, 0x2100 + d->BAddress);
UPDATE_COUNTERS;
if (--count <= 0)
{
b = 1;
break;
}
// Fall through
2010-01-27 23:08:56 +01:00
case 1:
2008-09-10 07:57:37 +02:00
Work = S9xGetByte((d->ABank << 16) + p);
S9xSetPPU(Work, 0x2101 + d->BAddress);
UPDATE_COUNTERS;
if (--count <= 0)
{
b = 2;
break;
}
// Fall through
2010-01-27 23:08:56 +01:00
case 2:
2008-09-10 07:57:37 +02:00
Work = S9xGetByte((d->ABank << 16) + p);
S9xSetPPU(Work, 0x2102 + d->BAddress);
UPDATE_COUNTERS;
if (--count <= 0)
{
b = 3;
break;
}
// Fall through
2010-01-27 23:08:56 +01:00
case 3:
2008-09-10 07:57:37 +02:00
Work = S9xGetByte((d->ABank << 16) + p);
S9xSetPPU(Work, 0x2103 + d->BAddress);
UPDATE_COUNTERS;
if (--count <= 0)
{
b = 0;
break;
}
} while (1);
}
}
#ifdef DEBUGGER
else
{
sprintf(String, "Unknown DMA transfer mode: %d on channel %d\n", d->TransferMode, Channel);
S9xMessage(S9X_TRACE, S9X_DMA_TRACE, String);
}
#endif
}
else
{
// DMA FAST PATH
if (d->TransferMode == 0 || d->TransferMode == 2 || d->TransferMode == 6)
{
switch (d->BAddress)
{
2010-01-27 23:08:56 +01:00
case 0x04: // OAMDATA
2008-09-10 07:57:37 +02:00
do
{
Work = *(base + p);
2010-01-27 23:08:56 +01:00
REGISTER_2104(Work);
2008-09-10 07:57:37 +02:00
UPDATE_COUNTERS;
} while (--count > 0);
2010-01-27 23:08:56 +01:00
break;
case 0x18: // VMDATAL
if (!PPU.VMA.FullGraphicCount)
2009-11-25 07:35:14 +01:00
{
2010-01-27 23:08:56 +01:00
do
{
Work = *(base + p);
REGISTER_2118_linear(Work);
UPDATE_COUNTERS;
} while (--count > 0);
}
else
{
do
{
Work = *(base + p);
REGISTER_2118_tile(Work);
UPDATE_COUNTERS;
} while (--count > 0);
}
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
break;
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
case 0x19: // VMDATAH
if (!PPU.VMA.FullGraphicCount)
2009-11-25 07:35:14 +01:00
{
2010-01-27 23:08:56 +01:00
do
{
Work = *(base + p);
REGISTER_2119_linear(Work);
UPDATE_COUNTERS;
} while (--count > 0);
}
else
{
do
{
Work = *(base + p);
REGISTER_2119_tile(Work);
UPDATE_COUNTERS;
} while (--count > 0);
}
break;
case 0x22: // CGDATA
2008-09-10 07:57:37 +02:00
do
{
Work = *(base + p);
2010-01-27 23:08:56 +01:00
REGISTER_2122(Work);
2008-09-10 07:57:37 +02:00
UPDATE_COUNTERS;
} while (--count > 0);
2010-01-27 23:08:56 +01:00
break;
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
case 0x80: // WMDATA
if (!CPU.InWRAMDMAorHDMA)
{
do
{
Work = *(base + p);
REGISTER_2180(Work);
UPDATE_COUNTERS;
} while (--count > 0);
}
else
{
do
{
UPDATE_COUNTERS;
} while (--count > 0);
}
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
break;
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
default:
2008-09-10 07:57:37 +02:00
do
{
Work = *(base + p);
2010-01-27 23:08:56 +01:00
S9xSetPPU(Work, 0x2100 + d->BAddress);
2008-09-10 07:57:37 +02:00
UPDATE_COUNTERS;
} while (--count > 0);
2010-01-27 23:08:56 +01:00
break;
2008-09-10 07:57:37 +02:00
}
}
else
if (d->TransferMode == 1 || d->TransferMode == 5)
{
if (d->BAddress == 0x18)
{
// VMDATAL
if (!PPU.VMA.FullGraphicCount)
{
switch (b)
{
2010-01-27 23:08:56 +01:00
default:
2008-09-10 07:57:37 +02:00
while (count > 1)
{
Work = *(base + p);
REGISTER_2118_linear(Work);
UPDATE_COUNTERS;
count--;
// Fall through
2010-01-27 23:08:56 +01:00
case 1:
OpenBus = *(base + p);
REGISTER_2119_linear(OpenBus);
2008-09-10 07:57:37 +02:00
UPDATE_COUNTERS;
count--;
}
}
if (count == 1)
{
Work = *(base + p);
REGISTER_2118_linear(Work);
UPDATE_COUNTERS;
b = 1;
}
else
b = 0;
}
else
{
switch (b)
{
2010-01-27 23:08:56 +01:00
default:
2008-09-10 07:57:37 +02:00
while (count > 1)
{
Work = *(base + p);
REGISTER_2118_tile(Work);
UPDATE_COUNTERS;
count--;
// Fall through
2010-01-27 23:08:56 +01:00
case 1:
2008-09-10 07:57:37 +02:00
Work = *(base + p);
REGISTER_2119_tile(Work);
UPDATE_COUNTERS;
count--;
}
}
if (count == 1)
{
Work = *(base + p);
REGISTER_2118_tile(Work);
UPDATE_COUNTERS;
b = 1;
}
else
b = 0;
}
}
else
{
// DMA mode 1 general case
switch (b)
{
2010-01-27 23:08:56 +01:00
default:
2008-09-10 07:57:37 +02:00
while (count > 1)
{
Work = *(base + p);
S9xSetPPU(Work, 0x2100 + d->BAddress);
UPDATE_COUNTERS;
count--;
// Fall through
2010-01-27 23:08:56 +01:00
case 1:
2008-09-10 07:57:37 +02:00
Work = *(base + p);
S9xSetPPU(Work, 0x2101 + d->BAddress);
UPDATE_COUNTERS;
count--;
}
}
if (count == 1)
{
Work = *(base + p);
S9xSetPPU(Work, 0x2100 + d->BAddress);
UPDATE_COUNTERS;
b = 1;
}
else
b = 0;
}
}
else
if (d->TransferMode == 3 || d->TransferMode == 7)
{
switch (b)
{
2010-01-27 23:08:56 +01:00
default:
2008-09-10 07:57:37 +02:00
do
{
Work = *(base + p);
S9xSetPPU(Work, 0x2100 + d->BAddress);
UPDATE_COUNTERS;
if (--count <= 0)
{
b = 1;
break;
}
// Fall through
2010-01-27 23:08:56 +01:00
case 1:
2008-09-10 07:57:37 +02:00
Work = *(base + p);
S9xSetPPU(Work, 0x2100 + d->BAddress);
UPDATE_COUNTERS;
if (--count <= 0)
{
b = 2;
break;
}
// Fall through
2010-01-27 23:08:56 +01:00
case 2:
2008-09-10 07:57:37 +02:00
Work = *(base + p);
S9xSetPPU(Work, 0x2101 + d->BAddress);
UPDATE_COUNTERS;
if (--count <= 0)
{
b = 3;
break;
}
// Fall through
2010-01-27 23:08:56 +01:00
case 3:
2008-09-10 07:57:37 +02:00
Work = *(base + p);
S9xSetPPU(Work, 0x2101 + d->BAddress);
UPDATE_COUNTERS;
if (--count <= 0)
{
b = 0;
break;
}
} while (1);
}
}
else
if (d->TransferMode == 4)
{
switch (b)
{
2010-01-27 23:08:56 +01:00
default:
2008-09-10 07:57:37 +02:00
do
{
Work = *(base + p);
S9xSetPPU(Work, 0x2100 + d->BAddress);
UPDATE_COUNTERS;
if (--count <= 0)
{
b = 1;
break;
}
// Fall through
2010-01-27 23:08:56 +01:00
case 1:
2008-09-10 07:57:37 +02:00
Work = *(base + p);
S9xSetPPU(Work, 0x2101 + d->BAddress);
UPDATE_COUNTERS;
if (--count <= 0)
{
b = 2;
break;
}
// Fall through
2010-01-27 23:08:56 +01:00
case 2:
2008-09-10 07:57:37 +02:00
Work = *(base + p);
S9xSetPPU(Work, 0x2102 + d->BAddress);
UPDATE_COUNTERS;
if (--count <= 0)
{
b = 3;
break;
}
// Fall through
2010-01-27 23:08:56 +01:00
case 3:
2008-09-10 07:57:37 +02:00
Work = *(base + p);
S9xSetPPU(Work, 0x2103 + d->BAddress);
UPDATE_COUNTERS;
if (--count <= 0)
{
b = 0;
break;
}
} while (1);
}
}
#ifdef DEBUGGER
else
{
sprintf(String, "Unknown DMA transfer mode: %d on channel %d\n", d->TransferMode, Channel);
S9xMessage(S9X_TRACE, S9X_DMA_TRACE, String);
}
#endif
}
if (rem <= 0)
break;
2010-01-27 23:08:56 +01:00
base = S9xGetBasePointer((d->ABank << 16) + d->AAddress);
2008-09-10 07:57:37 +02:00
count = MEMMAP_BLOCK_SIZE;
inWRAM_DMA = ((!in_sa1_dma && !in_sdd1_dma && !spc7110_dma) &&
2008-09-10 07:57:37 +02:00
(d->ABank == 0x7e || d->ABank == 0x7f || (!(d->ABank & 0x40) && d->AAddress < 0x2000)));
}
#undef UPDATE_COUNTERS
}
else
{
2008-09-10 07:57:37 +02:00
// PPU -> CPU
// 8 cycles per byte
#define UPDATE_COUNTERS \
d->TransferBytes--; \
d->AAddress += inc; \
if (!addCyclesInDMA(Channel)) \
{ \
CPU.InDMA = FALSE; \
CPU.InDMAorHDMA = FALSE; \
CPU.InWRAMDMAorHDMA = FALSE; \
2010-01-27 23:08:56 +01:00
CPU.CurrentDMAorHDMAChannel = -1; \
return (FALSE); \
2008-09-10 07:57:37 +02:00
}
if (d->BAddress > 0x80 - 4 && d->BAddress <= 0x83 && !(d->ABank & 0x40))
{
// REVERSE-DMA REALLY-SLOW PATH
do
{
switch (d->TransferMode)
{
2010-01-27 23:08:56 +01:00
case 0:
case 2:
case 6:
CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000);
Work = S9xGetPPU(0x2100 + d->BAddress);
S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
UPDATE_COUNTERS;
count--;
2009-11-30 09:14:38 +01:00
2008-09-10 07:57:37 +02:00
break;
2010-01-27 23:08:56 +01:00
case 1:
case 5:
CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000);
Work = S9xGetPPU(0x2100 + d->BAddress);
S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
UPDATE_COUNTERS;
if (!--count)
break;
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000);
Work = S9xGetPPU(0x2101 + d->BAddress);
S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
UPDATE_COUNTERS;
count--;
2008-09-10 07:57:37 +02:00
2009-11-30 09:14:38 +01:00
break;
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
case 3:
case 7:
CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000);
Work = S9xGetPPU(0x2100 + d->BAddress);
S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
UPDATE_COUNTERS;
if (!--count)
break;
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000);
Work = S9xGetPPU(0x2100 + d->BAddress);
S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
UPDATE_COUNTERS;
if (!--count)
break;
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000);
Work = S9xGetPPU(0x2101 + d->BAddress);
S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
UPDATE_COUNTERS;
if (!--count)
break;
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000);
Work = S9xGetPPU(0x2101 + d->BAddress);
S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
UPDATE_COUNTERS;
count--;
2008-09-10 07:57:37 +02:00
2009-11-30 09:14:38 +01:00
break;
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
case 4:
CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000);
Work = S9xGetPPU(0x2100 + d->BAddress);
S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
UPDATE_COUNTERS;
if (!--count)
break;
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000);
Work = S9xGetPPU(0x2101 + d->BAddress);
S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
UPDATE_COUNTERS;
if (!--count)
break;
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000);
Work = S9xGetPPU(0x2102 + d->BAddress);
S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
UPDATE_COUNTERS;
if (!--count)
break;
2009-11-30 09:14:38 +01:00
2010-01-27 23:08:56 +01:00
CPU.InWRAMDMAorHDMA = (d->AAddress < 0x2000);
Work = S9xGetPPU(0x2103 + d->BAddress);
S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
2008-09-10 07:57:37 +02:00
UPDATE_COUNTERS;
count--;
2010-01-27 23:08:56 +01:00
break;
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
default:
#ifdef DEBUGGER
sprintf(String, "Unknown DMA transfer mode: %d on channel %d\n", d->TransferMode, Channel);
S9xMessage(S9X_TRACE, S9X_DMA_TRACE, String);
#endif
while (count)
{
UPDATE_COUNTERS;
count--;
}
break;
}
2008-09-10 07:57:37 +02:00
} while (count);
}
else
{
// REVERSE-DMA FASTER PATH
CPU.InWRAMDMAorHDMA = (d->ABank == 0x7e || d->ABank == 0x7f);
do
{
switch (d->TransferMode)
{
2010-01-27 23:08:56 +01:00
case 0:
case 2:
case 6:
Work = S9xGetPPU(0x2100 + d->BAddress);
S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
UPDATE_COUNTERS;
count--;
2008-09-10 07:57:37 +02:00
break;
2010-01-27 23:08:56 +01:00
case 1:
case 5:
Work = S9xGetPPU(0x2100 + d->BAddress);
S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
UPDATE_COUNTERS;
if (!--count)
break;
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
Work = S9xGetPPU(0x2101 + d->BAddress);
S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
UPDATE_COUNTERS;
count--;
2008-09-10 07:57:37 +02:00
2009-11-30 09:14:38 +01:00
break;
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
case 3:
case 7:
Work = S9xGetPPU(0x2100 + d->BAddress);
S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
UPDATE_COUNTERS;
if (!--count)
break;
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
Work = S9xGetPPU(0x2100 + d->BAddress);
S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
UPDATE_COUNTERS;
if (!--count)
break;
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
Work = S9xGetPPU(0x2101 + d->BAddress);
S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
UPDATE_COUNTERS;
if (!--count)
break;
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
Work = S9xGetPPU(0x2101 + d->BAddress);
S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
UPDATE_COUNTERS;
count--;
2008-09-10 07:57:37 +02:00
2009-11-30 09:14:38 +01:00
break;
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
case 4:
Work = S9xGetPPU(0x2100 + d->BAddress);
S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
UPDATE_COUNTERS;
if (!--count)
break;
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
Work = S9xGetPPU(0x2101 + d->BAddress);
S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
UPDATE_COUNTERS;
if (!--count)
break;
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
Work = S9xGetPPU(0x2102 + d->BAddress);
S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
UPDATE_COUNTERS;
if (!--count)
break;
2009-11-30 09:14:38 +01:00
2010-01-27 23:08:56 +01:00
Work = S9xGetPPU(0x2103 + d->BAddress);
S9xSetByte(Work, (d->ABank << 16) + d->AAddress);
2008-09-10 07:57:37 +02:00
UPDATE_COUNTERS;
count--;
2010-01-27 23:08:56 +01:00
break;
default:
#ifdef DEBUGGER
sprintf(String, "Unknown DMA transfer mode: %d on channel %d\n", d->TransferMode, Channel);
S9xMessage(S9X_TRACE, S9X_DMA_TRACE, String);
#endif
while (count)
{
UPDATE_COUNTERS;
count--;
}
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
break;
}
2008-09-10 07:57:37 +02:00
} while (count);
}
}
if (CPU.NMIPending && (Timings.NMITriggerPos != 0xffff))
{
2010-01-27 23:08:56 +01:00
Timings.NMITriggerPos = CPU.Cycles + Timings.NMIDMADelay;
}
2008-09-10 07:57:37 +02:00
// Release the memory used in SPC7110 DMA
if (Settings.SPC7110)
{
2010-01-27 23:08:56 +01:00
if (spc7110_dma)
delete [] spc7110_dma;
}
2010-01-27 23:08:56 +01:00
#if 0
2008-09-10 07:57:37 +02:00
// sanity check
if (d->TransferBytes != 0)
fprintf(stderr,"DMA[%d] TransferBytes not 0! $21%02x Reverse:%d %04x\n", Channel, d->BAddress, d->ReverseTransfer, d->TransferBytes);
#endif
CPU.InDMA = FALSE;
CPU.InDMAorHDMA = FALSE;
CPU.InWRAMDMAorHDMA = FALSE;
2010-01-27 23:08:56 +01:00
CPU.CurrentDMAorHDMAChannel = -1;
2010-01-27 23:08:56 +01:00
return (TRUE);
}
2010-01-27 23:08:56 +01:00
static inline bool8 HDMAReadLineCount (int d)
{
// CPU.InDMA is set, so S9xGetXXX() / S9xSetXXX() incur no charges.
2009-11-25 07:35:14 +01:00
2010-01-27 23:08:56 +01:00
uint8 line;
line = S9xGetByte((DMA[d].ABank << 16) + DMA[d].Address);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
if (!line)
{
DMA[d].Repeat = FALSE;
DMA[d].LineCount = 128;
if (DMA[d].HDMAIndirectAddressing)
{
if (PPU.HDMA & (0xfe << d))
{
DMA[d].Address++;
ADD_CYCLES(SLOW_ONE_CYCLE << 1);
2010-01-27 23:08:56 +01:00
}
else
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
DMA[d].IndirectAddress = S9xGetWord((DMA[d].ABank << 16) + DMA[d].Address);
DMA[d].Address++;
}
DMA[d].Address++;
HDMAMemPointers[d] = NULL;
return (FALSE);
}
else
if (line == 0x80)
{
DMA[d].Repeat = TRUE;
DMA[d].LineCount = 128;
}
else
{
DMA[d].Repeat = !(line & 0x80);
DMA[d].LineCount = line & 0x7f;
}
DMA[d].Address++;
DMA[d].DoTransfer = TRUE;
if (DMA[d].HDMAIndirectAddressing)
{
ADD_CYCLES(SLOW_ONE_CYCLE << 1);
2010-01-27 23:08:56 +01:00
DMA[d].IndirectAddress = S9xGetWord((DMA[d].ABank << 16) + DMA[d].Address);
DMA[d].Address += 2;
HDMAMemPointers[d] = S9xGetMemPointer((DMA[d].IndirectBank << 16) + DMA[d].IndirectAddress);
}
else
HDMAMemPointers[d] = S9xGetMemPointer((DMA[d].ABank << 16) + DMA[d].Address);
return (TRUE);
}
2010-01-27 23:08:56 +01:00
void S9xStartHDMA (void)
{
PPU.HDMA = Memory.FillRAM[0x420c];
2010-01-27 23:08:56 +01:00
#ifdef DEBUGGER
missing.hdma_this_frame = PPU.HDMA;
#endif
PPU.HDMAEnded = 0;
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
int32 tmpch;
CPU.InHDMA = TRUE;
2008-09-10 07:57:37 +02:00
CPU.InDMAorHDMA = TRUE;
2010-01-27 23:08:56 +01:00
tmpch = CPU.CurrentDMAorHDMAChannel;
2010-01-27 23:08:56 +01:00
// XXX: Not quite right...
if (PPU.HDMA != 0)
ADD_CYCLES(Timings.DMACPUSync);
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
for (uint8 i = 0; i < 8; i++)
{
if (PPU.HDMA & (1 << i))
{
CPU.CurrentDMAorHDMAChannel = i;
2009-11-25 07:35:14 +01:00
2010-01-27 23:08:56 +01:00
DMA[i].Address = DMA[i].AAddress;
2009-11-30 09:14:38 +01:00
2010-01-27 23:08:56 +01:00
if (!HDMAReadLineCount(i))
{
PPU.HDMA &= ~(1 << i);
PPU.HDMAEnded |= (1 << i);
}
}
else
DMA[i].DoTransfer = FALSE;
}
CPU.InHDMA = FALSE;
2008-09-10 07:57:37 +02:00
CPU.InDMAorHDMA = CPU.InDMA;
CPU.HDMARanInDMA = CPU.InDMA ? PPU.HDMA : 0;
2010-01-27 23:08:56 +01:00
CPU.CurrentDMAorHDMAChannel = tmpch;
}
uint8 S9xDoHDMA (uint8 byte)
{
struct SDMA *p;
2010-01-27 23:08:56 +01:00
uint32 ShiftedIBank;
uint16 IAddr;
bool8 temp;
int32 tmpch;
int d;
uint8 mask;
2010-01-27 23:08:56 +01:00
CPU.InHDMA = TRUE;
2008-09-10 07:57:37 +02:00
CPU.InDMAorHDMA = TRUE;
CPU.HDMARanInDMA = CPU.InDMA ? byte : 0;
temp = CPU.InWRAMDMAorHDMA;
2010-01-27 23:08:56 +01:00
tmpch = CPU.CurrentDMAorHDMAChannel;
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
// XXX: Not quite right...
ADD_CYCLES(Timings.DMACPUSync);
2008-09-10 07:57:37 +02:00
for (mask = 1, p = &DMA[0], d = 0; mask; mask <<= 1, p++, d++)
2010-01-27 23:08:56 +01:00
{
if (byte & mask)
{
CPU.InWRAMDMAorHDMA = FALSE;
CPU.CurrentDMAorHDMAChannel = d;
2009-11-25 07:35:14 +01:00
2010-01-27 23:08:56 +01:00
if (p->HDMAIndirectAddressing)
{
ShiftedIBank = (p->IndirectBank << 16);
IAddr = p->IndirectAddress;
}
else
{
ShiftedIBank = (p->ABank << 16);
IAddr = p->Address;
}
2009-11-25 07:35:14 +01:00
2010-01-27 23:08:56 +01:00
if (!HDMAMemPointers[d])
HDMAMemPointers[d] = S9xGetMemPointer(ShiftedIBank + IAddr);
if (p->DoTransfer)
{
// XXX: Hack for Uniracers, because we don't understand
// OAM Address Invalidation
if (p->BAddress == 0x04)
{
if (SNESGameFixes.Uniracers)
{
PPU.OAMAddr = 0x10c;
PPU.OAMFlip = 0;
}
}
#ifdef DEBUGGER
if (Settings.TraceHDMA && p->DoTransfer)
{
sprintf(String, "H-DMA[%d] %s (%d) 0x%06X->0x21%02X %s, Count: %3d, Rep: %s, V-LINE: %3ld %02X%04X",
p-DMA, p->ReverseTransfer? "read" : "write",
p->TransferMode, ShiftedIBank+IAddr, p->BAddress,
p->HDMAIndirectAddressing ? "ind" : "abs",
p->LineCount,
p->Repeat ? "yes" : "no ", (long) CPU.V_Counter,
p->ABank, p->Address);
S9xMessage(S9X_TRACE, S9X_HDMA_TRACE, String);
}
#endif
if (!p->ReverseTransfer)
{
if ((IAddr & MEMMAP_MASK) + HDMA_ModeByteCounts[p->TransferMode] >= MEMMAP_BLOCK_SIZE)
{
// HDMA REALLY-SLOW PATH
HDMAMemPointers[d] = NULL;
#define DOBYTE(Addr, RegOff) \
CPU.InWRAMDMAorHDMA = (ShiftedIBank == 0x7e0000 || ShiftedIBank == 0x7f0000 || \
(!(ShiftedIBank & 0x400000) && ((uint16) (Addr)) < 0x2000)); \
S9xSetPPU(S9xGetByte(ShiftedIBank + ((uint16) (Addr))), 0x2100 + p->BAddress + (RegOff));
switch (p->TransferMode)
{
case 0:
DOBYTE(IAddr, 0);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
break;
case 5:
DOBYTE(IAddr + 0, 0);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
DOBYTE(IAddr + 1, 1);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
DOBYTE(IAddr + 2, 0);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
DOBYTE(IAddr + 3, 1);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
break;
case 1:
DOBYTE(IAddr + 0, 0);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
DOBYTE(IAddr + 1, 1);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
break;
case 2:
case 6:
DOBYTE(IAddr + 0, 0);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
DOBYTE(IAddr + 1, 0);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
break;
case 3:
case 7:
DOBYTE(IAddr + 0, 0);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
DOBYTE(IAddr + 1, 0);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
DOBYTE(IAddr + 2, 1);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
DOBYTE(IAddr + 3, 1);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
break;
case 4:
DOBYTE(IAddr + 0, 0);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
DOBYTE(IAddr + 1, 1);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
DOBYTE(IAddr + 2, 2);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
DOBYTE(IAddr + 3, 3);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
break;
}
2009-11-25 07:35:14 +01:00
2010-01-27 23:08:56 +01:00
#undef DOBYTE
}
else
{
CPU.InWRAMDMAorHDMA = (ShiftedIBank == 0x7e0000 || ShiftedIBank == 0x7f0000 ||
(!(ShiftedIBank & 0x400000) && IAddr < 0x2000));
if (!HDMAMemPointers[d])
{
// HDMA SLOW PATH
uint32 Addr = ShiftedIBank + IAddr;
switch (p->TransferMode)
{
case 0:
S9xSetPPU(S9xGetByte(Addr), 0x2100 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
break;
case 5:
S9xSetPPU(S9xGetByte(Addr + 0), 0x2100 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
S9xSetPPU(S9xGetByte(Addr + 1), 0x2101 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
Addr += 2;
/* fall through */
case 1:
S9xSetPPU(S9xGetByte(Addr + 0), 0x2100 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
S9xSetPPU(S9xGetByte(Addr + 1), 0x2101 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
break;
case 2:
case 6:
S9xSetPPU(S9xGetByte(Addr + 0), 0x2100 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
S9xSetPPU(S9xGetByte(Addr + 1), 0x2100 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
break;
case 3:
case 7:
S9xSetPPU(S9xGetByte(Addr + 0), 0x2100 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
S9xSetPPU(S9xGetByte(Addr + 1), 0x2100 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
S9xSetPPU(S9xGetByte(Addr + 2), 0x2101 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
S9xSetPPU(S9xGetByte(Addr + 3), 0x2101 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
break;
case 4:
S9xSetPPU(S9xGetByte(Addr + 0), 0x2100 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
S9xSetPPU(S9xGetByte(Addr + 1), 0x2101 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
S9xSetPPU(S9xGetByte(Addr + 2), 0x2102 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
S9xSetPPU(S9xGetByte(Addr + 3), 0x2103 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
break;
}
}
else
{
// HDMA FAST PATH
switch (p->TransferMode)
{
case 0:
S9xSetPPU(*HDMAMemPointers[d]++, 0x2100 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
break;
case 5:
S9xSetPPU(*(HDMAMemPointers[d] + 0), 0x2100 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
S9xSetPPU(*(HDMAMemPointers[d] + 1), 0x2101 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
HDMAMemPointers[d] += 2;
/* fall through */
case 1:
S9xSetPPU(*(HDMAMemPointers[d] + 0), 0x2100 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
// XXX: All HDMA should read to MDR first. This one just
// happens to fix Speedy Gonzales.
OpenBus = *(HDMAMemPointers[d] + 1);
S9xSetPPU(OpenBus, 0x2101 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
HDMAMemPointers[d] += 2;
break;
case 2:
case 6:
S9xSetPPU(*(HDMAMemPointers[d] + 0), 0x2100 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
S9xSetPPU(*(HDMAMemPointers[d] + 1), 0x2100 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
HDMAMemPointers[d] += 2;
break;
case 3:
case 7:
S9xSetPPU(*(HDMAMemPointers[d] + 0), 0x2100 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
S9xSetPPU(*(HDMAMemPointers[d] + 1), 0x2100 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
S9xSetPPU(*(HDMAMemPointers[d] + 2), 0x2101 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
S9xSetPPU(*(HDMAMemPointers[d] + 3), 0x2101 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
HDMAMemPointers[d] += 4;
break;
case 4:
S9xSetPPU(*(HDMAMemPointers[d] + 0), 0x2100 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
S9xSetPPU(*(HDMAMemPointers[d] + 1), 0x2101 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
S9xSetPPU(*(HDMAMemPointers[d] + 2), 0x2102 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
S9xSetPPU(*(HDMAMemPointers[d] + 3), 0x2103 + p->BAddress);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
HDMAMemPointers[d] += 4;
break;
}
}
}
}
else
{
// REVERSE HDMA REALLY-SLOW PATH
// anomie says: Since this is apparently never used
// (otherwise we would have noticed before now), let's not bother with faster paths.
HDMAMemPointers[d] = NULL;
#define DOBYTE(Addr, RegOff) \
CPU.InWRAMDMAorHDMA = (ShiftedIBank == 0x7e0000 || ShiftedIBank == 0x7f0000 || \
(!(ShiftedIBank & 0x400000) && ((uint16) (Addr)) < 0x2000)); \
S9xSetByte(S9xGetPPU(0x2100 + p->BAddress + (RegOff)), ShiftedIBank + ((uint16) (Addr)));
switch (p->TransferMode)
{
case 0:
DOBYTE(IAddr, 0);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
break;
case 5:
DOBYTE(IAddr + 0, 0);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
DOBYTE(IAddr + 1, 1);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
DOBYTE(IAddr + 2, 0);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
DOBYTE(IAddr + 3, 1);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
break;
case 1:
DOBYTE(IAddr + 0, 0);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
DOBYTE(IAddr + 1, 1);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
break;
2009-11-25 07:35:14 +01:00
2010-01-27 23:08:56 +01:00
case 2:
case 6:
DOBYTE(IAddr + 0, 0);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
DOBYTE(IAddr + 1, 0);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
break;
case 3:
case 7:
DOBYTE(IAddr + 0, 0);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
DOBYTE(IAddr + 1, 0);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
DOBYTE(IAddr + 2, 1);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
DOBYTE(IAddr + 3, 1);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
break;
case 4:
DOBYTE(IAddr + 0, 0);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
DOBYTE(IAddr + 1, 1);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
DOBYTE(IAddr + 2, 2);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
DOBYTE(IAddr + 3, 3);
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
break;
}
#undef DOBYTE
}
}
}
}
2010-01-27 23:08:56 +01:00
for (mask = 1, p = &DMA[0], d = 0; mask; mask <<= 1, p++, d++)
{
if (byte & mask)
{
if (p->DoTransfer)
{
2010-01-27 23:08:56 +01:00
if (p->HDMAIndirectAddressing)
p->IndirectAddress += HDMA_ModeByteCounts[p->TransferMode];
else
p->Address += HDMA_ModeByteCounts[p->TransferMode];
}
p->DoTransfer = !p->Repeat;
if (!--p->LineCount)
{
if (!HDMAReadLineCount(d))
{
byte &= ~mask;
PPU.HDMAEnded |= mask;
p->DoTransfer = FALSE;
}
}
else
ADD_CYCLES(SLOW_ONE_CYCLE);
2010-01-27 23:08:56 +01:00
}
}
CPU.InHDMA = FALSE;
2008-09-10 07:57:37 +02:00
CPU.InDMAorHDMA = CPU.InDMA;
CPU.InWRAMDMAorHDMA = temp;
2010-01-27 23:08:56 +01:00
CPU.CurrentDMAorHDMAChannel = tmpch;
2008-09-10 07:57:37 +02:00
2010-01-27 23:08:56 +01:00
return (byte);
}
2010-01-27 23:08:56 +01:00
void S9xResetDMA (void)
{
for (int d = 0; d < 8; d++)
{
DMA[d].ReverseTransfer = TRUE;
DMA[d].HDMAIndirectAddressing = TRUE;
DMA[d].AAddressFixed = TRUE;
DMA[d].AAddressDecrement = TRUE;
DMA[d].TransferMode = 7;
DMA[d].BAddress = 0xff;
DMA[d].AAddress = 0xffff;
DMA[d].ABank = 0xff;
DMA[d].DMACount_Or_HDMAIndirectAddress = 0xffff;
DMA[d].IndirectBank = 0xff;
DMA[d].Address = 0xffff;
DMA[d].Repeat = FALSE;
DMA[d].LineCount = 0x7f;
DMA[d].UnknownByte = 0xff;
DMA[d].DoTransfer = FALSE;
2008-09-10 07:57:37 +02:00
DMA[d].UnusedBit43x0 = 1;
2010-01-27 23:08:56 +01:00
}
}