mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-25 23:25:40 +00:00
372 lines
9 KiB
C
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);
|
|
}
|
|
|