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

368 lines
9.7 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
*/
/*
* scsi.c
*
* NCR 53C810 SCSI controller emulation.
*
* Multi-byte data written is assumed to be little endian and is therefore
* reversed because the PowerPC operates in big endian mode in the Model 3.
*
* NOTE: scsi_run() is often called with an argument of 100. This is
* actually incorrect. It should run until an interrupt condition. A count of
* 100 is used as a runaway counter to prevent lock-ups.
*/
#include "model3.h"
/*
* Read/Write Handlers
*
* These are used as an interface to the rest of the system.
*/
static UINT8 (*read_8)(UINT32);
static UINT16 (*read_16)(UINT32);
static UINT32 (*read_32)(UINT32);
static void (*write_8)(UINT32, UINT8);
static void (*write_16)(UINT32, UINT16);
static void (*write_32)(UINT32, UINT32);
/*
* 53C810 Context
*
* Offsets within the register array correspond to register addresses but the
* endianness in which they are stored is little endian. Macros are provided
* to access them.
*/
#define REG8(r) scsi_regs[r]
#define REG16(r) *((UINT16 *) &scsi_regs[r])
#define REG32(r) *((UINT32 *) &scsi_regs[r])
static UINT8 scsi_regs[0x60];
static BOOL scripts_exec; // true if SCRIPTS code should be executed
/*
* SCRIPTS Emulation
*/
#include "scsiop.h"
/*
* UINT8 scsi_read_8(UINT32 addr);
*
* Reads from the SCSI register space.
*
* Parameters:
* addr = Register address (only lower 8 bits matter.)
*
* Returns:
* Returns data from the register read.
*/
UINT8 scsi_read_8(UINT32 addr)
{
UINT8 reg;
addr &= 0xFF;
//if (addr > 0x5F)
// error("%08X: SCSI invalid read from %02X", ppc_get_reg(PPC_REG_PC), addr);
reg = REG8(addr);
switch (addr)
{
case 0x14:
REG8(addr) &= 0xFE; // clear DIP
break;
default:
break;
}
return reg;
}
/*
* UINT32 scsi_read_32(UINT32 addr);
*
* Reads from the SCSI register space.
*
* Parameters:
* addr = Register address (only lower 8 bits matter.)
*
* Returns:
* Returns data from the register read.
*/
UINT32 scsi_read_32(UINT32 addr)
{
UINT32 reg;
// error("SCSI 32-bit read from %08X", addr);
addr &= 0xFF;
if (addr > 0x5F)
error("%08X: SCSI invalid read from %02X", ppc_get_pc(), addr);
reg = REG32(addr);
switch (addr)
{
case 0x14:
error("%08X: SCSI 32-bit read from reg %02X -- don't know what to do", ppc_get_pc(), addr);
break;
default:
// NOTE: some registers might have to be handled to clear certain
// bits
break;
}
return reg;
}
/*
* void scsi_write_8(UINT32 addr, UINT8 data);
*
* Writes a byte to the SCSI register space.
*
* Parameters:
* addr = Register address (only lower 8 bits matter.)
* data = Data to write.
*/
void scsi_write_8(UINT32 addr, UINT8 data)
{
addr &= 0xFF;
if (addr > 0x5F)
error("%08X: SCSI invalid write8 to %02X", ppc_get_pc(), addr);
//message(0, "%08X: SCSI %02X = %02X", ppc_get_pc(), addr, data);
REG8(addr) = data;
switch (addr)
{
case 0x3B: // DCNTL: DMA Control
/*
* 7 6 5 4 3 2 1 0
* +------+------+------+------+------+------+------+------+
* | CLSE | PFF | PFEN | SSM | IRQM | STD | IRQD | COM |
* +------+------+------+------+------+------+------+------+
*
* CLSE Cache Line Size Enable
*
* PFF Prefetch Flush
*
* PFEN Prefetch Enable
*
* SSM Single Step Mode
*
* Setting this bit causes the SCSI to stop after executing
* each SCRIPTS instruction and generate a single step
* interrupt. To restart the SCSI after the interrupt has
* been generated, read ISTAT and DSTAT to recognize and
* clear the interrupt, then set STD bit in this register
* to resume execution.
*
* IRQM IRQ Mode
*
* STD Start DMA Operation
*
* The SCSI fetches an instruction and executes it when this
* bit is set. It is required when the SCSI is in one of the
* following modes:
*
* - Manual start mode: Bit 0 in the DMODE register is
* set.
* - Single step mode: Bit 4 in the DCNTL register is
* set.
*
* When in manual mode, setting this bit starts instruction
* execution -- it remains set until an interrupt occurs. In
* single step mode, it restarts execution after an
* interrupt.
*
* IRQD IRQ Disable
*
* COM LSI53C700 Family Compatibility
*/
/*
* Single-stepping is checked for in scsi_run() (the number of
* instructions to execute will be reduced to 1 there.)
*/
if ((REG8(0x38) & 0x01) || // MAN=1, start SCRIPTS on STD=1
(REG8(0x3B) & 0x10)) // single step, resume execution
{
scripts_exec = data & 0x04;
scsi_run(500);
}
break;
}
}
/*
* void scsi_write_16(UINT32 addr, UINT16 data);
*
* Writes a word to the SCSI register space.
*
* Parameters:
* addr = Register address (only lower 8 bits matter.)
* data = Data to write.
*/
void scsi_write_16(UINT32 addr, UINT16 data)
{
data = BSWAP16(data);
//message(0, "%08X: SCSI %02X = %04X", ppc_get_pc(), addr, data);
}
/*
* void scsi_write_32(UINT32 addr, UINT32 data);
*
* Writes a byte to the SCSI register space.
*
* Parameters:
* addr = Register address (only lower 8 bits matter.)
* data = Data to write.
*/
void scsi_write_32(UINT32 addr, UINT32 data)
{
if ((addr & 0xff) > 0x5C)
error("%08X: SCSI invalid write32 to %08X", ppc_get_pc(), addr);
addr &= 0xff;
data = BSWAP32(data);
REG32(addr) = data;
// message(0, "%08X: SCSI %02X = %08X", ppc_get_reg(PPC_REG_PC), addr, data);
switch (addr)
{
case 0x2C: // DSP: DMA SCRIPTS Pointer
if ((REG8(0x38) & 0x01) == 0) // MAN=0, start SCRIPTS automatically
scripts_exec = 1;
scsi_run(500);
break;
default:
break;
}
}
/*
* void scsi_save_state(FILE *fp);
*
* Saves the state of the SCSI controller to a file.
*
* Parameters:
* fp = File to save to.
*/
void scsi_save_state(FILE *fp)
{
fwrite(scsi_regs, sizeof(UINT8), 0x60, fp);
fwrite(&scripts_exec, sizeof(BOOL), 1, fp);
}
/*
* void scsi_load_state(FILE *fp);
*
* Loads the state of the SCSI controller from a file.
*
* Parameters:
* fp = File to load from.
*/
void scsi_load_state(FILE *fp)
{
fread(scsi_regs, sizeof(UINT8), 0x60, fp);
fread(&scripts_exec, sizeof(BOOL), 1, fp);
}
/*
* void scsi_reset(void);
*
* Initializes the SCSI controller to its power-on state. All bits which are
* marked as undefined by the manual are simply set to 0 here.
*/
void scsi_reset(void)
{
memset(scsi_regs, 0, sizeof(scsi_regs));
scripts_exec = 0;
REG8(0x00) = 0xc0;
REG8(0x0C) = 0x80;
REG8(0x0F) = 0x02;
REG8(0x18) = 0xff;
REG8(0x19) = 0xf0;
REG8(0x1A) = 0x01;
REG8(0x46) = 0x60;
REG8(0x47) = 0x0f;
REG8(0x4C) = 0x03;
}
/*
* void scsi_init(UINT8 (*read8)(UINT32), UINT16 (*read16)(UINT32),
* UINT32 (*read32)(UINT32), void (*write8)(UINT32, UINT8),
* void (*write16)(UINT32, UINT16),
* void (*write32)(UINT32, UINT32));
*
* Initializes the SCSI controller emulation by building the SCRIPTS opcode
* table and setting the memory access handlers.
*
* Parameters:
* read8 = Handler to use for 8-bit reads.
* read16 = Handler to use for 16-bit reads.
* read32 = Handler to use for 32-bit reads.
* write8 = Handler to use for 8-bit writes.
* write16 = Handler to use for 16-bit writes.
* write32 = Handler to use for 32-bit writes.
*/
void scsi_init(UINT8 (*read8)(UINT32), UINT16 (*read16)(UINT32),
UINT32 (*read32)(UINT32), void (*write8)(UINT32, UINT8),
void (*write16)(UINT32, UINT16),
void (*write32)(UINT32, UINT32))
{
read_8 = read8;
read_16 = read16;
read_32 = read32;
write_8 = write8;
write_16 = write16;
write_32 = write32;
build_opcode_table();
}
/*
* void scsi_shutdown(void);
*
* Shuts down the SCSI emulation (actually does nothing.)
*/
void scsi_shutdown(void)
{
}