mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-22 22:05:38 +00:00
493 lines
15 KiB
C
493 lines
15 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
|
|
*/
|
|
|
|
/*
|
|
* bridge.c
|
|
*
|
|
* MPC105 and MPC106 PCI bridge/memory controller emulation.
|
|
*
|
|
* Warning: Portability hazard. Code assumes it is running on a little endian
|
|
* machine.
|
|
*
|
|
* NOTE: Data written to the bridge controller is byte-reversed by the PPC.
|
|
* This is to make it little endian.
|
|
*/
|
|
|
|
#include "model3.h"
|
|
|
|
/******************************************************************/
|
|
/* Internal Bridge State */
|
|
/******************************************************************/
|
|
|
|
/*
|
|
* Macros for Register Access
|
|
*
|
|
* Assuming a little endian system (portability hazard!)
|
|
*/
|
|
|
|
#define DWORD(addr) *((UINT32 *) ®s[addr])
|
|
#define WORD(addr) *((UINT16 *) ®s[addr])
|
|
#define BYTE(addr) regs[addr]
|
|
|
|
/*
|
|
* Bridge State
|
|
*
|
|
* This code is intended for use on little-endian systems. Data from the
|
|
* registers should be returned as if a big endian read was performed (thus
|
|
* returning the little endian word 0x1057 as 0x5710.)
|
|
*
|
|
* The DWORD() and WORD() macros only work on little endian systems and are
|
|
* intended to set up the bridge state. Reads and writes using the handlers
|
|
* are done in big endian fashion by manually breaking down the data into
|
|
* bytes.
|
|
*
|
|
* To make this code fully portable, it would probably be enough to remove
|
|
* the DWORD() and WORD() macros.
|
|
*/
|
|
|
|
static UINT8 regs[0x100];
|
|
static UINT8 config_data[4]; // data for last CONFIG_ADDR command
|
|
static UINT32 reg_ptr; // current register address
|
|
|
|
/******************************************************************/
|
|
/* Interface */
|
|
/******************************************************************/
|
|
|
|
/*
|
|
* Callback for Bus Commands
|
|
*
|
|
* Note that there are no callbacks for reads/writes to CONFIG_DATA yet as
|
|
* the need for them has not yet appeared.
|
|
*/
|
|
|
|
static UINT32 (*config_addr_callback)(UINT32);
|
|
|
|
/*
|
|
* UINT8 bridge_read_8(UINT32 addr);
|
|
*
|
|
* Reads directly from a register. The lower 8 bits of the address are the
|
|
* register to read from. This is intended for the MPC105 which can map its
|
|
* internal registers to 0xF8FFF0xx.
|
|
*
|
|
* Parameters:
|
|
* addr = Address (only lower 8 bits matter.)
|
|
*
|
|
* Returns:
|
|
* Data read.
|
|
*/
|
|
|
|
UINT8 bridge_read_8(UINT32 addr)
|
|
{
|
|
LOG("model3.log", "RB REG%02X\n", addr & 0xff);
|
|
|
|
return BYTE(addr & 0xff);
|
|
}
|
|
|
|
/*
|
|
* UINT16 bridge_read_16(UINT32 addr);
|
|
*
|
|
* Reads directly from a register. The lower 8 bits of the address are the
|
|
* register to read from. This is intended for the MPC105 which can map its
|
|
* internal registers to 0xF8FFF0xx.
|
|
*
|
|
* Bit 0 is masked off to ensure 16-bit alignment (I'm not sure how a real
|
|
* PCIBMC would handle this.)
|
|
*
|
|
* Parameters:
|
|
* addr = Address (only lower 8 bits matter.)
|
|
*
|
|
* Returns:
|
|
* Data read.
|
|
*/
|
|
|
|
UINT16 bridge_read_16(UINT32 addr)
|
|
{
|
|
LOG("model3.log", "RH REG%02X\n", addr & 0xff);
|
|
addr &= 0xfe;
|
|
return (BYTE(addr + 0) << 8) | BYTE(addr + 1);
|
|
}
|
|
|
|
/*
|
|
* UINT32 bridge_read_32(UINT32 addr);
|
|
*
|
|
* Reads directly from a register. The lower 8 bits of the address are the
|
|
* register to read from. This is intended for the MPC105 which can map its
|
|
* internal registers to 0xF8FFF0xx.
|
|
*
|
|
* The lower 2 bits are masked off to ensure 32-bit alignment.
|
|
*
|
|
* Parameters:
|
|
* addr = Address (only lower 8 bits matter.)
|
|
*
|
|
* Returns:
|
|
* Data read.
|
|
*/
|
|
|
|
UINT32 bridge_read_32(UINT32 addr)
|
|
{
|
|
LOG("model3.log", "RW REG%02X\n", addr & 0xff);
|
|
addr &= 0xfc;
|
|
return (BYTE(addr + 0) << 24) | (BYTE(addr + 1) << 16) |
|
|
(BYTE(addr + 2) << 8) | BYTE(addr + 3);
|
|
}
|
|
|
|
/*
|
|
* void bridge_write_8(UINT32 addr, UINT8 data);
|
|
*
|
|
* Writes directly to a register. Only the lower 8 bits matter. This is
|
|
* intended for the MPC105.
|
|
*
|
|
* Arguments:
|
|
* addr = Address (only lower 8 bits matter.)
|
|
* data = Data to write.
|
|
*/
|
|
|
|
void bridge_write_8(UINT32 addr, UINT8 data)
|
|
{
|
|
LOG("model3.log", "REG%02X=%02X\n", addr & 0xff, data);
|
|
BYTE(addr & 0xff) = data;
|
|
}
|
|
|
|
/*
|
|
* void bridge_write_16(UINT32 addr, UINT16 data);
|
|
*
|
|
* Writes directly to a register. Only the lower 8 bits matter. This is
|
|
* intended for the MPC105.
|
|
*
|
|
* Bit 0 is masked off to ensure 16-bit alignment.
|
|
*
|
|
* Arguments:
|
|
* addr = Address (only lower 8 bits matter.)
|
|
* data = Data to write.
|
|
*/
|
|
|
|
void bridge_write_16(UINT32 addr, UINT16 data)
|
|
{
|
|
LOG("model3.log", "REG%02X=%02X%02X\n", addr & 0xff, data & 0xff, (data >> 8) & 0xff);
|
|
addr &= 0xfe;
|
|
BYTE(addr + 0) = data >> 8;
|
|
BYTE(addr + 1) = (UINT8) data;
|
|
}
|
|
|
|
/*
|
|
* void bridge_write_32(UINT32 addr, UINT32 data);
|
|
*
|
|
* Writes directly to a register. Only the lower 8 bits matter. This is
|
|
* intended for the MPC105.
|
|
*
|
|
* The lower 2 bits are masked off to ensure 32-bit alignment.
|
|
*
|
|
* Arguments:
|
|
* addr = Address (only lower 8 bits matter.)
|
|
* data = Data to write.
|
|
*/
|
|
|
|
void bridge_write_32(UINT32 addr, UINT32 data)
|
|
{
|
|
LOG("model3.log", "REG%02X=%02X%02X%02X%02X\n", addr & 0xff, data & 0xff, (data >> 8) & 0xff, (data >> 16) & 0xff, (data >> 24) & 0xff);
|
|
addr &= 0xfc;
|
|
BYTE(addr + 0) = data >> 24;
|
|
BYTE(addr + 1) = data >> 16;
|
|
BYTE(addr + 2) = data >> 8;
|
|
BYTE(addr + 3) = data;
|
|
}
|
|
|
|
/*
|
|
* UINT8 bridge_read_config_data_8(UINT32 addr);
|
|
*
|
|
* Reads from CONFIG_DATA offset 0 to 3. Only the lower 2 bits of the address
|
|
* are checked to determine which byte to read. I'm not sure whether this is
|
|
* correct behavior (perhaps all byte offsets return the same data.)
|
|
*
|
|
* Parameters:
|
|
* addr = Address to read from (only lower 2 bits matter.)
|
|
*
|
|
* Returns:
|
|
* Data read.
|
|
*/
|
|
|
|
UINT8 bridge_read_config_data_8(UINT32 addr)
|
|
{
|
|
return config_data[addr & 3];
|
|
// return BYTE(reg_ptr + (addr & 3));
|
|
}
|
|
|
|
/*
|
|
* UINT16 bridge_read_config_data_16(UINT32 addr);
|
|
*
|
|
* Reads from CONFIG_DATA offset 0 or 2. Only bit 1 of the address is checked
|
|
* to determine which word to read. I'm not sure whether this is correct
|
|
* behavior for word reads (both words might return the same data, for
|
|
* example.)
|
|
*
|
|
* Parameters:
|
|
* addr = Address to read from (only bit 1 matters.)
|
|
*
|
|
* Returns:
|
|
* Data read.
|
|
*/
|
|
|
|
UINT16 bridge_read_config_data_16(UINT32 addr)
|
|
{
|
|
return (config_data[(addr & 2) + 0] << 8) | config_data[(addr & 2) + 1];
|
|
// return (BYTE(reg_ptr + (addr & 2) + 0) << 8) |
|
|
// BYTE(reg_ptr + (addr & 2) + 1);
|
|
}
|
|
|
|
/*
|
|
* UINT32 bridge_read_config_data_32(UINT32 addr);
|
|
*
|
|
* Reads from CONFIG_DATA. The address argument is ignored because it is
|
|
* assumed that this function is only called for valid CONFIG_DATA addresses.
|
|
*
|
|
* Parameters:
|
|
* addr = Ignored.
|
|
*
|
|
* Returns:
|
|
* Data read.
|
|
*/
|
|
|
|
UINT32 bridge_read_config_data_32(UINT32 addr)
|
|
{
|
|
LOG("model3.log", "%08X: READ CONFIG DATA\n", ppc_get_pc());
|
|
return (config_data[0] << 24) | (config_data[1] << 16) |
|
|
(config_data[2] << 8) | config_data[3];
|
|
// return (BYTE(reg_ptr + 0) << 24) | (BYTE(reg_ptr + 1) << 16) |
|
|
// (BYTE(reg_ptr + 2) << 8) | BYTE(reg_ptr + 3);
|
|
}
|
|
|
|
/*
|
|
* void bridge_write_config_addr_32(UINT32 addr, UINT32 data);
|
|
*
|
|
* Writes to CONFIG_ADDR. The address argument is ignored because it is
|
|
* assumed that this function is only called for valid CONFIG_ADDR addresses.
|
|
*
|
|
* Parameters:
|
|
* addr = Ignored.
|
|
* data = Data to write.
|
|
*/
|
|
|
|
void bridge_write_config_addr_32(UINT32 addr, UINT32 data)
|
|
{
|
|
UINT32 callback_data;
|
|
|
|
/*
|
|
* I'm not sure how the bridge controller distinguishes between indirect
|
|
* register accesses and accesses which must be passed to some device on
|
|
* the PCI bus.
|
|
*
|
|
* Currently, I test to see if the command is of the format 0x800000XX,
|
|
* and if so, I return put register data in the return buffer
|
|
* (config_data), otherwise I invoke the callback.
|
|
*/
|
|
|
|
|
|
// LOG("model3.log", "CONFIG_ADDR=%02X%02X%02X%02X @ %08X", data & 0xff, (data >> 8) & 0xff, (data >> 16) & 0xff, (data >> 24) & 0xff, PowerPC_ReadIntegerRegister(POWERPC_IREG_PC));
|
|
// reg_ptr = data >> 24; // remember, data comes in little endian form
|
|
// // see page 3-15 of MPC106UM/D REV.1
|
|
|
|
if ((data & 0x00ffffff) == 0x00000080) // indirect register access (little endian data, see page 3-15 of MPC106UM/D REV.1)
|
|
{
|
|
reg_ptr = data >> 24;
|
|
config_data[0] = BYTE(reg_ptr + 0);
|
|
config_data[1] = BYTE(reg_ptr + 1);
|
|
config_data[2] = BYTE(reg_ptr + 2);
|
|
config_data[3] = BYTE(reg_ptr + 3);
|
|
}
|
|
else // pass it to the callback
|
|
{
|
|
if (config_addr_callback != NULL)
|
|
{
|
|
callback_data = config_addr_callback(BSWAP32(data));
|
|
callback_data = BSWAP32(callback_data); // so we can return as little endian
|
|
}
|
|
else
|
|
callback_data = 0xFFFFFFFF;
|
|
|
|
config_data[0] = callback_data >> 24;
|
|
config_data[1] = callback_data >> 16;
|
|
config_data[2] = callback_data >> 8;
|
|
config_data[3] = callback_data;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* void bridge_write_config_data_8(UINT32 addr, UINT8 data);
|
|
*
|
|
* Writes to CONFIG_DATA. The lower 2 bits determine which byte offset to
|
|
* write to.
|
|
*
|
|
* Parameters:
|
|
* addr = Address to write to (only lower 2 bits matter.)
|
|
* data = Data to write.
|
|
*/
|
|
|
|
void bridge_write_config_data_8(UINT32 addr, UINT8 data)
|
|
{
|
|
// LOG("model3.log", "CONFIG_DATA=%02X @ %08X", data, PowerPC_ReadIntegerRegister(POWERPC_IREG_PC));
|
|
BYTE(reg_ptr + (addr & 3)) = data;
|
|
config_data[addr & 3] = data;
|
|
}
|
|
|
|
/*
|
|
* void bridge_write_config_data_16(UINT32 addr, UINT16 data);
|
|
*
|
|
* Writes to CONFIG_DATA. Only bit 1 of the address is checked to determine
|
|
* which word to write to.
|
|
*
|
|
* Parameters:
|
|
* addr = Address to write to (only bit 1 matters.)
|
|
* data = Data to write.
|
|
*/
|
|
|
|
void bridge_write_config_data_16(UINT32 addr, UINT16 data)
|
|
{
|
|
BYTE(reg_ptr + (addr & 2) + 0) = data >> 8;
|
|
BYTE(reg_ptr + (addr & 2) + 1) = (UINT8) data;
|
|
config_data[(addr & 2) + 0] = data >> 8;
|
|
config_data[(addr & 2) + 1] = (UINT8) data;
|
|
// LOG("model3.log", "CONFIG_DATA=%04X @ %08X", WORD(reg_ptr), PowerPC_ReadIntegerRegister(POWERPC_IREG_PC));
|
|
}
|
|
|
|
/*
|
|
* void bridge_write_config_data_32(UINT32 addr, UINT32 data);
|
|
*
|
|
* Writes to CONFIG_DATA. The address argument is ignored because it is
|
|
* assumed that this function is only called for valid CONFIG_DATA addresses.
|
|
*
|
|
* Parameters:
|
|
* addr = Ignored.
|
|
* data = Data to write.
|
|
*/
|
|
|
|
void bridge_write_config_data_32(UINT32 addr, UINT32 data)
|
|
{
|
|
BYTE(reg_ptr + 0) = data >> 24;
|
|
BYTE(reg_ptr + 1) = data >> 16;
|
|
BYTE(reg_ptr + 2) = data >> 8;
|
|
BYTE(reg_ptr + 3) = data;
|
|
config_data[0] = data >> 24;
|
|
config_data[1] = data >> 16;
|
|
config_data[2] = data >> 8;
|
|
config_data[3] = data;
|
|
// LOG("model3.log", "CONFIG_DATA=%08X @ %08X", DWORD(reg_ptr), PowerPC_ReadIntegerRegister(POWERPC_IREG_PC));
|
|
}
|
|
|
|
/*
|
|
* void bridge_save_state(FILE *fp);
|
|
*
|
|
* Saves the bridge controller state by writing it out to a file.
|
|
*
|
|
* Parameters:
|
|
* fp = File to write to.
|
|
*/
|
|
|
|
void bridge_save_state(FILE *fp)
|
|
{
|
|
fwrite(regs, sizeof(UINT8), 0x100, fp);
|
|
fwrite(config_data, sizeof(UINT8), 4, fp);
|
|
fwrite(®_ptr, sizeof(UINT32), 1, fp);
|
|
}
|
|
|
|
/*
|
|
* void bridge_load_state(FILE *fp);
|
|
*
|
|
* Loads the bridge controller state by reading it in from a file.
|
|
*
|
|
* Parameters:
|
|
* fp = File to read from.
|
|
*/
|
|
|
|
void bridge_load_state(FILE *fp)
|
|
{
|
|
fread(regs, sizeof(UINT8), 0x100, fp);
|
|
fread(config_data, sizeof(UINT8), 4, fp);
|
|
fread(®_ptr, sizeof(UINT32), 1, fp);
|
|
}
|
|
|
|
/*
|
|
* void bridge_reset(INT device);
|
|
*
|
|
* Initializes the bridge controller's internal state.
|
|
*
|
|
* Memory control configuration 1 is to be set to 0xFFn20000, where n is data
|
|
* sampled from signals sent by hardware to the MPC105 (see the MPC105
|
|
* manual.) For Model 3, I'm guessing that n == 0.
|
|
*
|
|
* Parameters:
|
|
* device = Device ID of the bridge to emulate (1 = MPC105, 2 = MPC106.)
|
|
* If it is not one of these values, behavior is undefined.
|
|
*/
|
|
|
|
void bridge_reset(INT device)
|
|
{
|
|
if (device == 1)
|
|
LOG("model3.log", "Using MPC105\n");
|
|
else if (device == 2)
|
|
LOG("model3.log", "Using MPC106\n");
|
|
else
|
|
LOG("model3.log", "ERROR: Unknown bridge controller device ID (%d)\n", device);
|
|
|
|
memset(regs, 0, sizeof(UINT8) * 0x100);
|
|
memset(config_data, 0, sizeof(UINT8) * 4);
|
|
reg_ptr = 0;
|
|
|
|
/*
|
|
* Set up common fields
|
|
*/
|
|
|
|
WORD(0x00) = 0x1057; // vendor ID (Motorola)
|
|
WORD(0x02) = device; // device ID
|
|
WORD(0x04) = 0x0006; // PCI command
|
|
WORD(0x06) = 0x0080; // PCI status
|
|
BYTE(0x08) = 0x00; // revision ID (?)
|
|
BYTE(0x0b) = 0x06; // class code
|
|
DWORD(0xa8) = 0xff000010; // processor interface configuration 1
|
|
DWORD(0xac) = 0x000c060c; // processor interface configuration 2
|
|
BYTE(0xba) = 0x04; // alternate OS-visible parameters 1
|
|
BYTE(0xc0) = 0x01; // error enabling 1
|
|
DWORD(0xf0) = 0xff020000; // memory control configuration 1
|
|
DWORD(0xf4) = 0x00000003; // memory control configuration 2
|
|
DWORD(0xfc) = 0x00100000; // memory control configuration 4
|
|
|
|
/*
|
|
* Additional settings for MPC106
|
|
*/
|
|
|
|
if (device == 2) // MPC106
|
|
{
|
|
BYTE(0x0c) = 0x08; // cache line size
|
|
BYTE(0x73) = 0xcd; // output driver control
|
|
DWORD(0xe0) = 0x0fff0042; // emulation support configuration 1
|
|
DWORD(0xe8) = 0x00000020; // emulation support configuration 2
|
|
}
|
|
}
|
|
|
|
/*
|
|
* void bridge_init(UINT32 (*config_addr_callback_ptr)(UINT32));
|
|
*
|
|
* Initializes the bridge controller by setting up the CONFIG_ADDR callback.
|
|
* bridge_reset() must still be used to put the controller in a usable (reset)
|
|
* state.
|
|
*/
|
|
|
|
void bridge_init(UINT32 (*config_addr_callback_ptr)(UINT32))
|
|
{
|
|
config_addr_callback = config_addr_callback_ptr;
|
|
}
|