Supermodel/core/dma.c
Ville Linde a1c9f6cf92
2006-07-12 13:53:14 +00:00

372 lines
9 KiB
C

/*
* Sega Model 3 Emulator
* Copyright (C) 2003 Bart Trzynadlowski, Ville Linde, Stefano Teso
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License Version 2 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program (license.txt); if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* dma.c
*
* Step 2.0+ DMA device emulation. We're unsure whether or not this is a SCSI
* controller; it appears to only be used for its DMA capabilities and is
* thought to be a Sega custom part.
*/
/*
* 0xC2000000 32-bit -W DMA Source
* 0xC2000004 32-bit -W DMA Destination
* 0xC2000008 32-bit -W DMA Length (in words)
* 0xC200000C 8-bit RW DMA Status? AxxxxxxB (A and B must be cleared)
* 0xC200000D 8-bit -W DMA Control? xxxxxxx? (bit0 clears C200000C bit0)
* 0xC200000E 8-bit -W DMA Config?
* 0xC2000010 32-bit -W DMA Command? (Written, result appears at 0x14)
* 0xC2000014 32-bit R- DMA Command Result
*/
#include "model3.h"
/******************************************************************/
/* Private Variables */
/******************************************************************/
/*
* Registers
*/
static UINT8 dma_regs[0x20];
/*
* Read/Write Handlers
*
* These are used as an interface to the rest of the system.
*/
static UINT32 (*read_32)(UINT32);
static void (*write_32)(UINT32, UINT32);
/******************************************************************/
/* DMA Transfer */
/******************************************************************/
/*
* do_dma():
*
* Performs a DMA transfer. The paramaters are expected to be in the
* appropriate registers.
*/
#ifdef _PROFILE_
extern UINT is_dma;
#endif
static void do_dma(void)
{
UINT32 src, dest, len;
PROFILE_SECT_ENTRY("dma");
#ifdef _PROFILE_
is_dma = 1;
#endif
src = *(UINT32 *) &dma_regs[0x00];
dest = *(UINT32 *) &dma_regs[0x04];
len = (*(UINT32 *) &dma_regs[0x08]) * 4;
LOG("model3.log", "DMA %08X -> %08X, %X\n", src, dest, len);
if ((dma_regs[0x0E] & 0x80)) // swap words
{
/*while (len)
{
write_32(dest, BSWAP32(read_32(src)));
src += 4;
dest += 4;
len -= 4;
}*/
model3_dma_transfer(src, dest, len, TRUE);
}
else
{
/*while (len)
{
write_32(dest, read_32(src));
src += 4;
dest += 4;
len -= 4;
}*/
model3_dma_transfer(src, dest, len, FALSE);
}
#ifdef _PROFILE_
is_dma = 0;
#endif
*(UINT32 *) &dma_regs[0x08] = 0; // not sure if this is necessary
/*
* An IRQ may be generated by the DMA device after a copy is performed (or
* at some other time.)
*
* Evidence for this is in Harley Davidson's ISR. The subroutine at 0x8ABE8
* appears to check the status register bit 0 and performs a copy if it is
* set (after first clearing it.)
*
* Here, bit 0 is set, an IRQ is triggered, and the code will clear bit 0 by
* writing a 1 to bit 0 of DMA reg 0xD.
*/
dma_regs[0x0C] |= 1; // this bit may indicate IRQ raised
ppc_set_irq_line(1);
PROFILE_SECT_EXIT("dma");
}
/******************************************************************/
/* Interface */
/******************************************************************/
/*
* void dma_shutdown(void);
*
* Shuts down the DMA device emulation.
*/
void dma_shutdown(void)
{
}
/*
* void dma_init(UINT32 (*read32)(UINT32), void (*write32)(UINT32, UINT32));
*
* Initializes the DMA device emulation by setting the memory access handlers.
*
* Parameters:
* read32 = Handler to use for 32-bit reads.
* write32 = Handler to use for 32-bit writes.
*/
void dma_init(UINT32 (*read32)(UINT32), void (*write32)(UINT32, UINT32))
{
read_32 = read32;
write_32 = write32;
}
/*
* void dma_reset(void);
*
* Resets the DMA device.
*/
void dma_reset(void)
{
memset(dma_regs, 0, 0x20);
}
/*
* void dma_save_state(FILE *fp);
*
* Saves the state of the DMA device (for Step 2.0 or higher games only) to a
* file.
*
* Parameters:
* fp = File to save to.
*/
void dma_save_state(FILE *fp)
{
if (m3_config.step < 0x20)
return;
fwrite(dma_regs, sizeof(UINT8), 0x20, fp);
}
/*
* void dma_load_state(FILE *fp);
*
* Loads the state of the DMA device (for Step 2.0 or higher games only) from
* a file.
*
* Parameters:
* fp = File to load from.
*/
void dma_load_state(FILE *fp)
{
if (m3_config.step < 0x20)
return;
fread(dma_regs, sizeof(UINT8), 0x20, fp);
}
/******************************************************************/
/* Access */
/******************************************************************/
/*
* UINT8 dma_read_8(UINT32 a);
*
* Reads a byte from the DMA device.
*
* Parameters:
* a = Address.
*
* Returns:
* Data read.
*/
UINT8 dma_read_8(UINT32 a)
{
// message(0, "%08X: Unknown DMA read8, %08X", PPC_PC, a);
return dma_regs[a & 0x1F];
}
/*
* UINT32 dma_read_32(UINT32 a);
*
* Reads a word from the DMA device.
*
* Parameters:
* a = Address.
*
* Returns:
* Data read.
*/
UINT32 dma_read_32(UINT32 a)
{
// message(0, "%08X: Unknown DMA read32, %08X", PPC_PC, a);
return BSWAP32(*(UINT32 *) &dma_regs[a & 0x1F]);
}
/*
* void dma_write_8(UINT32 a, UINT8 d);
*
* Writes a byte to the DMA device.
*
* Parameters:
* a = Address.
* d = Data to write.
*/
void dma_write_8(UINT32 a, UINT8 d)
{
dma_regs[a & 0x1F] = d;
switch (a & 0x1F)
{
case 0xD:
if ((d & 1)) // clear status bit 0
dma_regs[0xC] &= ~1;
break;
default:
LOG("model3.log", "%08X: Unknown DMA write8, %08X = %02X\n", ppc_get_pc(), a, d);
break;
}
}
/*
* void dma_write_16(UINT32 a, UINT16 d);
*
* Writes a half-word to the DMA device.
*
* Parameters:
* a = Address.
* d = Data to write.
*/
void dma_write_16(UINT32 a, UINT16 d)
{
switch (a & 0x1F)
{
case 0x14:
message(0, "%08X = %04X", a, d);
LOG("model3.log", "DMA %08X = %04X\n", a, d);
break;
default:
error("unknown DMA write: %08X = %04X", a, d);
break;
}
}
/*
* void dma_write_32(UINT32 a, UINT32 d);
*
* Writes a word to the DMA device.
*
* Parameters:
* a = Address.
* d = Data to write.
*/
void dma_write_32(UINT32 a, UINT32 d)
{
d = BSWAP32(d); // this is a little endian device, only bswap here once
*(UINT32 *) &dma_regs[a & 0x1F] = d;
switch (a & 0x1F)
{
case 0x00: // DMA Source
// message(0, "DMA SRC = %08X", d);
return;
case 0x04: // DMA Destination
// message(0, "DMA DST = %08X", d);
return;
case 0x08: // DMA Length
// message(0, "DMA LEN = %X", d * 4);
do_dma();
return;
case 0x10: // DMA Command
/*
* Virtual On 2 has been observed to write commands to reg 0x10 and
* then expects particular values back from 0x14.
*
* Command 0x80000000 is a little strange. It is issued and twice and
* each time, a bit of the result is expected to be different. This
* is crudely simulated by flipping a bit.
*
* Virtua Striker 2 '99 does something similar, except that it writes
* commands 0x80000000, 0x80000004, ... 0x80000020.
*/
if (d & 0x20000000)
*(UINT32 *) &dma_regs[0x14] = 0x178611db; //0x16C311DB; // PCI Vendor and Device ID
else if ((d & 0x80000000))
*(UINT32 *) &dma_regs[0x14] = r3d_read_32((d & 0xFF) | 0x84000000);
#if 0
else if (d == 0x80000000)
{
static UINT32 result = 0x02000000;
result ^= 0x02000000;
*(UINT32 *) &dma_regs[0x14] = result;
}
#endif
else
message(0, "%08X: Unknown DMA command, %08X", ppc_get_pc(), d);
return;
case 0x14:
*(UINT32 *) &dma_regs[0x14] = 0xffffffff;
return;
}
message(0, "%08X: Unknown DMA write32, %08X = %08X", ppc_get_pc(), a, d);
}