mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-22 22:05:38 +00:00
368 lines
9.7 KiB
C
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)
|
||
|
{
|
||
|
}
|