mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2025-02-17 01:45:41 +00:00
2397 lines
60 KiB
C
2397 lines
60 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
|
|
*/
|
|
|
|
/*
|
|
* model3.c
|
|
*
|
|
* Model 3 system emulation.
|
|
*
|
|
* Memory Regions:
|
|
*
|
|
* All Model 3 memory regions are allocated and freed in this file.
|
|
* However, RAM and backup RAM are the only RAM regions directly accessed
|
|
* here. Everything else is passed to its respective subsystem. This
|
|
* localizes all memory allocation to this single file and enforces
|
|
* separation between the different modules.
|
|
*
|
|
* VF3 SCSI Note:
|
|
*
|
|
* In VF3, the SCSI appears at 0xC0000000. We've observed that at this
|
|
* address on Step 1.5 there is some sort of 68K device. Oh, well :P
|
|
*
|
|
* TODO List:
|
|
*
|
|
* - von2_rev5_4g has what appears to be a spinning cursor effect when
|
|
* booting but sometimes the characters look invalid. Need to take a
|
|
* closer look...
|
|
*
|
|
* - Investigate backup RAM. Sega Rally 2 seems to want 256KB when R3D
|
|
* read commands are emulated with the DMA. This is emulated by making
|
|
* the Step 2.X backup RAM region (starting at 0xFE0C0000) 256KB in size
|
|
* but it is still kept as 128KB for Step 1.X.
|
|
*
|
|
* - Do more error checking when parsing games.ini (num_patches > 64,
|
|
* etc.)
|
|
*/
|
|
|
|
#include "model3.h"
|
|
|
|
#define PROFILE_MEMORY_OPERATIONS 0
|
|
|
|
/******************************************************************/
|
|
/* Global Configuration Structure */
|
|
/******************************************************************/
|
|
|
|
CONFIG m3_config;
|
|
|
|
/******************************************************************/
|
|
/* Internal Variables */
|
|
/******************************************************************/
|
|
|
|
/*
|
|
* Model 3 Memory Regions
|
|
*/
|
|
|
|
UINT8 *ram = NULL; // PowerPC RAM
|
|
static UINT8 *bram = NULL; // backup RAM
|
|
static UINT8 *sram = NULL; // sound RAM
|
|
static UINT8 *vram = NULL; // tile generator VRAM (scroll RAM)
|
|
static UINT8 *culling_ram_8e = NULL; // Real3D culling RAM
|
|
static UINT8 *culling_ram_8c = NULL; // Real3D culling RAM
|
|
static UINT8 *polygon_ram = NULL; // Real3D polygon RAM
|
|
static UINT8 *texture_ram = NULL; // Real3D texture RAM
|
|
static UINT8 *crom = NULL; // CROM (all CROM memory is allocated here)
|
|
static UINT8 *crom_bank; // points to current 8MB CROM bank
|
|
static UINT8 *vrom = NULL; // video ROM
|
|
static UINT8 *sprog = NULL;
|
|
static UINT8 *srom = NULL; // sound ROM
|
|
static UINT8 *dsbprog = NULL;
|
|
static UINT8 *dsbrom = NULL; // DSB1 ROM
|
|
|
|
static UINT8 *crom0 = NULL;
|
|
static UINT8 *crom1 = NULL;
|
|
static UINT8 *crom2 = NULL;
|
|
static UINT8 *crom3 = NULL;
|
|
|
|
/*
|
|
* Other
|
|
*/
|
|
|
|
static UINT ppc_freq; // PowerPC clock speed
|
|
|
|
/*
|
|
* Function Prototypes (for forward references)
|
|
*/
|
|
|
|
static UINT8 model3_sys_read_8(UINT32);
|
|
static UINT32 model3_sys_read_32(UINT32);
|
|
static void model3_sys_write_8(UINT32, UINT8);
|
|
static void model3_sys_write_32(UINT32, UINT32);
|
|
static UINT8 model3_midi_read(UINT32);
|
|
static void model3_midi_write(UINT32, UINT8);
|
|
|
|
/******************************************************************/
|
|
/* Output */
|
|
/******************************************************************/
|
|
|
|
void message(int flags, char * fmt, ...)
|
|
{
|
|
/* a simple _message_ to the user. */
|
|
/* must be used for unharmful warnings too. */
|
|
/* do not pause the emulator to prompt the user! */
|
|
/* using a cyclic buffer to output a few lines of */
|
|
/* timed messages to the screen would be best. */
|
|
|
|
/* integrable in the renderer. */
|
|
|
|
/* flags are provided for future expansion (i.e. for */
|
|
/* different text colors/priorities) */
|
|
|
|
va_list vl;
|
|
char string[512];
|
|
|
|
va_start(vl, fmt);
|
|
vsprintf(string, fmt, vl);
|
|
va_end(vl);
|
|
|
|
LOG("model3.log", "%s\n", string);
|
|
|
|
puts(string);
|
|
|
|
// osd_message(flags, string);
|
|
}
|
|
|
|
void error(char * fmt, ...)
|
|
{
|
|
/* you can bypass this calling directly osd_error, but i */
|
|
/* prefer it this way. :) */
|
|
|
|
char string[256] = "";
|
|
va_list vl;
|
|
|
|
va_start(vl, fmt);
|
|
vsprintf(string, fmt, vl);
|
|
va_end(vl);
|
|
|
|
osd_error(string);
|
|
}
|
|
|
|
void _log(char * path, char * fmt, ...)
|
|
{
|
|
/* logs to a file. */
|
|
|
|
/* NOTE: "log" conflicts with math.h, so i'm using _log instead. */
|
|
/* it doesn't make much difference since we're gonna log stuff */
|
|
/* with the LOG macro. */
|
|
|
|
if(m3_config.log_enabled)
|
|
{
|
|
char string[1024];
|
|
va_list vl;
|
|
FILE * file;
|
|
|
|
file = fopen(path, "ab");
|
|
if(file != NULL)
|
|
{
|
|
va_start(vl, fmt);
|
|
vsprintf(string, fmt, vl);
|
|
va_end(vl);
|
|
fprintf(file, string);
|
|
fclose(file);
|
|
}
|
|
}
|
|
}
|
|
|
|
void _log_init(char * path)
|
|
{
|
|
/* resets a file contents. */
|
|
/* since i'm opening the log file with fopen(path, "ab") every */
|
|
/* time (not to lost the file in the case of a crash), it's */
|
|
/* necessary to reset it on startup. */
|
|
|
|
FILE * file;
|
|
|
|
file = fopen(path, "wb");
|
|
if(file != NULL)
|
|
fclose(file);
|
|
}
|
|
|
|
|
|
|
|
|
|
#if PROFILE_MEMORY_OPERATIONS
|
|
#define PROFILE_MEMORY_OP(x, y, z) profile_memory_operation(x, y, z)
|
|
#else
|
|
#define PROFILE_MEMORY_OP(x, y, z)
|
|
#endif
|
|
|
|
|
|
static UINT64 num_mem_ops = 0;
|
|
static UINT64 num_read_ops = 0;
|
|
static UINT64 num_write_ops = 0;
|
|
|
|
static UINT64 num_read8_ops = 0;
|
|
static UINT64 num_read16_ops = 0;
|
|
static UINT64 num_read32_ops = 0;
|
|
static UINT64 num_read64_ops = 0;
|
|
static UINT64 num_write8_ops = 0;
|
|
static UINT64 num_write16_ops = 0;
|
|
static UINT64 num_write32_ops = 0;
|
|
static UINT64 num_write64_ops = 0;
|
|
|
|
static UINT64 num_ram_reads = 0;
|
|
static UINT64 num_ram_writes = 0;
|
|
static UINT64 num_crom_reads = 0;
|
|
static UINT64 num_bankcrom_reads = 0;
|
|
static UINT64 num_other_reads = 0;
|
|
static UINT64 num_other_writes = 0;
|
|
|
|
static int profile_memory_operation(int size, int write, UINT32 address)
|
|
{
|
|
num_mem_ops++;
|
|
|
|
if (write)
|
|
{
|
|
num_write_ops++;
|
|
|
|
switch (size)
|
|
{
|
|
case 8: num_write8_ops++; break;
|
|
case 16: num_write16_ops++; break;
|
|
case 32: num_write32_ops++; break;
|
|
case 64: num_write64_ops++; break;
|
|
}
|
|
|
|
if (address < 0x800000)
|
|
{
|
|
num_ram_writes++;
|
|
}
|
|
else
|
|
{
|
|
num_other_writes++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
num_read_ops++;
|
|
|
|
switch (size)
|
|
{
|
|
case 8: num_read8_ops++; break;
|
|
case 16: num_read16_ops++; break;
|
|
case 32: num_read32_ops++; break;
|
|
case 64: num_read64_ops++; break;
|
|
}
|
|
|
|
if (address < 0x800000)
|
|
{
|
|
num_ram_reads++;
|
|
}
|
|
else if (address >= 0xff000000 && address < 0xff800000)
|
|
{
|
|
num_bankcrom_reads++;
|
|
}
|
|
else if (address >= 0xff800000)
|
|
{
|
|
num_crom_reads++;
|
|
}
|
|
else
|
|
{
|
|
num_other_reads++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void print_memory_profile_stats(void)
|
|
{
|
|
printf("Total mem ops: %lld\n", num_mem_ops);
|
|
printf(" Reads: %lld, %f%%\n", num_read_ops, ((double)num_read_ops / (double)num_mem_ops) * 100.0f);
|
|
printf(" Writes: %lld, %f%%\n", num_write_ops, ((double)num_write_ops / (double)num_mem_ops) * 100.0f);
|
|
printf("\n");
|
|
printf(" Read8: %lld, %f%%\n", num_read8_ops, ((double)num_read8_ops / (double)num_read_ops) * 100.0f);
|
|
printf(" Read16: %lld, %f%%\n", num_read16_ops, ((double)num_read16_ops / (double)num_read_ops) * 100.0f);
|
|
printf(" Read32: %lld, %f%%\n", num_read32_ops, ((double)num_read32_ops / (double)num_read_ops) * 100.0f);
|
|
printf(" Read64: %lld, %f%%\n", num_read64_ops, ((double)num_read64_ops / (double)num_read_ops) * 100.0f);
|
|
printf(" Write8: %lld, %f%%\n", num_write8_ops, ((double)num_write8_ops / (double)num_write_ops) * 100.0f);
|
|
printf(" Write16: %lld, %f%%\n", num_write16_ops, ((double)num_write16_ops / (double)num_write_ops) * 100.0f);
|
|
printf(" Write32: %lld, %f%%\n", num_write32_ops, ((double)num_write32_ops / (double)num_write_ops) * 100.0f);
|
|
printf(" Write64: %lld, %f%%\n", num_write64_ops, ((double)num_write64_ops / (double)num_write_ops) * 100.0f);
|
|
printf("\n");
|
|
printf(" RAM reads: %lld, %f%%\n", num_ram_reads, ((double)num_ram_reads / (double)num_read_ops) * 100.0f);
|
|
printf(" RAM writes: %lld, %f%%\n", num_ram_writes, ((double)num_ram_writes / (double)num_write_ops) * 100.0f);
|
|
printf(" BANK CROM reads: %lld, %f%%\n", num_bankcrom_reads, ((double)num_bankcrom_reads / (double)num_read_ops) * 100.0f);
|
|
printf(" CROM reads: %lld, %f%%\n", num_crom_reads, ((double)num_crom_reads / (double)num_read_ops) * 100.0f);
|
|
printf(" Other reads: %lld, %f%%\n", num_other_reads, ((double)num_other_reads / (double)num_read_ops) * 100.0f);
|
|
printf(" Other writes: %lld, %f%%\n", num_other_writes, ((double)num_other_writes / (double)num_write_ops) * 100.0f);
|
|
}
|
|
|
|
|
|
static int prot_data_ptr = 0;
|
|
|
|
static UINT16 vs299_prot_data[] =
|
|
{
|
|
0xc800, 0x4a20, 0x5041, 0x4e41, 0x4920, 0x4154, 0x594c, 0x4220,
|
|
0x4152, 0x4953, 0x204c, 0x5241, 0x4547, 0x544e, 0x4e49, 0x2041,
|
|
0x4547, 0x4d52, 0x4e41, 0x2059, 0x4e45, 0x4c47, 0x4e41, 0x2044,
|
|
0x454e, 0x4854, 0x5245, 0x414c, 0x444e, 0x2053, 0x5246, 0x4e41,
|
|
0x4543, 0x4320, 0x4c4f, 0x4d4f, 0x4942, 0x2041, 0x4150, 0x4152,
|
|
0x5547, 0x5941, 0x4220, 0x4c55, 0x4147, 0x4952, 0x2041, 0x5053,
|
|
0x4941, 0x204e, 0x5243, 0x414f, 0x4954, 0x2041, 0x4542, 0x474c,
|
|
0x5549, 0x204d, 0x494e, 0x4547, 0x4952, 0x2041, 0x4153, 0x4455,
|
|
0x2049, 0x4f4b, 0x4552, 0x2041, 0x4544, 0x4d4e, 0x5241, 0x204b,
|
|
0x4f52, 0x414d, 0x494e, 0x2041, 0x4353, 0x544f, 0x414c, 0x444e,
|
|
0x5520, 0x4153, 0x5320, 0x554f, 0x4854, 0x4641, 0x4952, 0x4143,
|
|
0x4d20, 0x5845, 0x4349, 0x204f, 0x5559, 0x4f47, 0x4c53, 0x5641,
|
|
0x4149, 0x4620, 0x5f43, 0x4553, 0x4147
|
|
};
|
|
|
|
static UINT16 swt_prot_data[] =
|
|
{
|
|
0xffff,
|
|
0x3d3d, 0x3d3d, 0x203d, 0x5453, 0x5241, 0x5720, 0x5241, 0x2053,
|
|
0x3d3d, 0x3d3d, 0x0a3d, 0x6f43, 0x7970, 0x6952, 0x6867, 0x2074,
|
|
0x4553, 0x4147, 0x4520, 0x746e, 0x7265, 0x7270, 0x7369, 0x7365,
|
|
0x202c, 0x744c, 0x2e64, 0x410a, 0x756d, 0x6573, 0x656d, 0x746e,
|
|
0x5220, 0x4426, 0x4420, 0x7065, 0x2e74, 0x2320, 0x3231, 0x4b0a,
|
|
0x7461, 0x7573, 0x6179, 0x7573, 0x4120, 0x646e, 0x206f, 0x2026,
|
|
0x614b, 0x6f79, 0x6f6b, 0x5920, 0x6d61, 0x6d61, 0x746f, 0x0a6f,
|
|
};
|
|
|
|
static UINT16 fvipers2_prot_data[] =
|
|
{
|
|
0x2a2a,
|
|
0x2a2a, 0x2a2a, 0x2a2a, 0x2a2a, 0x2a2a, 0x2a2a, 0x202a, 0x5b5b,
|
|
0x4620, 0x6769, 0x7468, 0x6e69, 0x2067, 0x6956, 0x6570, 0x7372,
|
|
0x3220, 0x5d20, 0x205d, 0x6e69, 0x3c20, 0x4d3c, 0x444f, 0x4c45,
|
|
0x332d, 0x3e3e, 0x4320, 0x706f, 0x7279, 0x6769, 0x7468, 0x2820,
|
|
0x2943, 0x3931, 0x3839, 0x5320, 0x4745, 0x2041, 0x6e45, 0x6574,
|
|
0x7072, 0x6972, 0x6573, 0x2c73, 0x544c, 0x2e44, 0x2020, 0x4120,
|
|
0x6c6c, 0x7220, 0x6769, 0x7468, 0x7220, 0x7365, 0x7265, 0x6576,
|
|
0x2e64, 0x2a20, 0x2a2a, 0x2a2a, 0x2a2a, 0x2a2a, 0x2a2a, 0x2a2a,
|
|
};
|
|
|
|
static UINT16 spikeout_prot_data[] =
|
|
{
|
|
0x0000,
|
|
0x4f4d, 0x4544, 0x2d4c, 0x2033, 0x7953, 0x7473, 0x6d65, 0x5020,
|
|
0x6f72, 0x7267, 0x6d61, 0x4320, 0x706f, 0x7279, 0x6769, 0x7468,
|
|
0x2820, 0x2943, 0x3120, 0x3939, 0x2035, 0x4553, 0x4147, 0x4520,
|
|
0x746e, 0x7265, 0x7270, 0x7369, 0x7365, 0x4c2c, 0x4454, 0x202e,
|
|
0x6c41, 0x206c, 0x6972, 0x6867, 0x2074, 0x6572, 0x6573, 0x7672,
|
|
0x6465, 0x202e, 0x2020, 0x0020
|
|
};
|
|
|
|
static UINT16 eca_prot_data[] =
|
|
{
|
|
0x0000,
|
|
0x2d2f, 0x202d, 0x4d45, 0x5245, 0x4547, 0x434e, 0x2059, 0x4143,
|
|
0x4c4c, 0x4120, 0x424d, 0x4c55, 0x4e41, 0x4543, 0x2d20, 0x0a2d,
|
|
0x6f43, 0x7970, 0x6952, 0x6867, 0x2074, 0x4553, 0x4147, 0x4520,
|
|
0x746e, 0x7265, 0x7270, 0x7369, 0x7365, 0x202c, 0x744c, 0x2e64,
|
|
0x530a, 0x666f, 0x7774, 0x7261, 0x2065, 0x2652, 0x2044, 0x6544,
|
|
0x7470, 0x202e, 0x3123, 0x660a, 0x726f, 0x7420, 0x7365, 0x0a74,
|
|
};
|
|
|
|
/******************************************************************/
|
|
/* PPC Access */
|
|
/******************************************************************/
|
|
|
|
UINT8 m3_ppc_read_8(UINT32 a)
|
|
{
|
|
PROFILE_MEMORY_OP(8, 0, a);
|
|
|
|
/*
|
|
* RAM and ROM tested for first for speed
|
|
*/
|
|
|
|
if (a <= 0x007FFFFF)
|
|
{
|
|
return ram[a];
|
|
}
|
|
else if (a >= 0xFF000000 && a <= 0xFF7FFFFF)
|
|
{
|
|
return crom_bank[a - 0xFF000000];
|
|
}
|
|
else if (a >= 0xFF800000 && a <= 0xFFFFFFFF)
|
|
{
|
|
return crom[a - 0xFF800000];
|
|
}
|
|
|
|
switch (a >> 28)
|
|
{
|
|
case 0xc:
|
|
{
|
|
if (a >= 0xC0000000 && a <= 0xC00000FF) // 53C810 SCSI (Step 1.0)
|
|
{
|
|
if (m3_config.step == 0x10)
|
|
return scsi_read_8(a);
|
|
}
|
|
else if (a >= 0xC1000000 && a <= 0xC10000FF) // 53C810 SCSI
|
|
{
|
|
return scsi_read_8(a);
|
|
}
|
|
else if (a >= 0xC2000000 && a <= 0xC200001F) // DMA device
|
|
{
|
|
return dma_read_8(a);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case 0xf:
|
|
{
|
|
if ((a >= 0xF0040000 && a <= 0xF004003F) ||
|
|
(a >= 0xFE040000 && a <= 0xFE04003F)) // control area
|
|
{
|
|
return controls_read(a);
|
|
}
|
|
else if ((a >= 0xF0080000 && a <= 0xF00800FF) ||
|
|
(a >= 0xFE080000 && a <= 0xFE0800FF)) // MIDI?
|
|
{
|
|
return model3_midi_read(a);
|
|
}
|
|
else if ((a >= 0xF0100000 && a <= 0xF010003F) ||
|
|
(a >= 0xFE100000 && a <= 0xFE10003F)) // system control
|
|
{
|
|
return model3_sys_read_8(a);
|
|
}
|
|
else if ((a >= 0xF0140000 && a <= 0xF014003F) ||
|
|
(a >= 0xFE140000 && a <= 0xFE14003F)) // RTC
|
|
{
|
|
return rtc_read_8(a);
|
|
}
|
|
else if (a >= 0xFEE00000 && a <= 0xFEEFFFFF) // MPC106 CONFIG_DATA
|
|
{
|
|
return bridge_read_config_data_8(a);
|
|
}
|
|
else if (a >= 0xF9000000 && a <= 0xF90000FF) // 53C810 SCSI
|
|
{
|
|
return scsi_read_8(a);
|
|
}
|
|
|
|
|
|
switch (a)
|
|
{
|
|
case 0xF118000C: // TODO: 8-bit tilegen access is unimplemented
|
|
{
|
|
return 0xff;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
error("%08X: unknown read8, %08X\n", ppc_get_pc(), a);
|
|
return 0xff;
|
|
}
|
|
|
|
UINT16 m3_ppc_read_16(UINT32 a)
|
|
{
|
|
PROFILE_MEMORY_OP(16, 0, a);
|
|
|
|
/*
|
|
* RAM and ROM tested for first for speed
|
|
*/
|
|
|
|
if (a <= 0x007FFFFF)
|
|
{
|
|
return BSWAP16(*(UINT16 *) &ram[a]);
|
|
}
|
|
else if (a >= 0xFF000000 && a <= 0xFF7FFFFF)
|
|
{
|
|
return BSWAP16(*(UINT16 *) &crom_bank[a - 0xFF000000]);
|
|
}
|
|
else if (a >= 0xFF800000 && a <= 0xFFFFFFFF)
|
|
{
|
|
return BSWAP16(*(UINT16 *) &crom[a - 0xFF800000]);
|
|
}
|
|
|
|
switch (a >> 28)
|
|
{
|
|
case 0xf:
|
|
{
|
|
if ((a >= 0xF00C0000 && a <= 0xF00DFFFF) ||
|
|
(a >= 0xFE0C0000 && a <= 0xFE0FFFFF)) // backup RAM
|
|
{
|
|
return BSWAP16(*(UINT16 *) &bram[a & 0x3FFFF]);
|
|
}
|
|
else if( a >= 0xF1000000 && a <= 0xF111FFFF ) // tilegen VRAM
|
|
{
|
|
return tilegen_vram_read_16(a);
|
|
}
|
|
|
|
switch (a)
|
|
{
|
|
case 0xF0C00CFC: // MPC105/106 CONFIG_DATA (Sega Bass Fishing)
|
|
case 0xF0C00CFE:
|
|
case 0xFEE00CFC: // MPC105/106 CONFIG_DATA
|
|
case 0xFEE00CFE:
|
|
{
|
|
return bridge_read_config_data_16(a);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
error("%08X: unknown read16, %08X\n", ppc_get_pc(), a);
|
|
return 0xffff;
|
|
}
|
|
|
|
UINT32 m3_ppc_read_32(UINT32 a)
|
|
{
|
|
PROFILE_MEMORY_OP(32, 0, a);
|
|
|
|
/*
|
|
* RAM and ROM tested for first for speed
|
|
*/
|
|
|
|
if (a <= 0x007FFFFF)
|
|
{
|
|
return BSWAP32(*(UINT32 *) &ram[a]);
|
|
}
|
|
else if (a >= 0xFF000000 && a <= 0xFF7FFFFF)
|
|
{
|
|
return BSWAP32(*(UINT32 *) &crom_bank[a - 0xFF000000]);
|
|
}
|
|
else if (a >= 0xFF800000 && a <= 0xFFFFFFFF)
|
|
{
|
|
return BSWAP32(*(UINT32 *) &crom[a - 0xFF800000]);
|
|
}
|
|
|
|
switch (a >> 28)
|
|
{
|
|
case 0x8:
|
|
{
|
|
return r3d_read_32(a);
|
|
}
|
|
|
|
case 0xc:
|
|
{
|
|
if (a >= 0xC0000000 && a <= 0xC00000FF) // 53C810 SCSI (Step 1.0)
|
|
{
|
|
if (m3_config.step == 0x10)
|
|
return scsi_read_32(a);
|
|
}
|
|
else if (a >= 0xC1000000 && a <= 0xC10000FF) // 53C810 SCSI
|
|
{
|
|
return scsi_read_32(a);
|
|
}
|
|
else if (a >= 0xC2000000 && a <= 0xC200001F) // DMA device
|
|
{
|
|
return dma_read_32(a);
|
|
}
|
|
|
|
switch (a)
|
|
{
|
|
case 0xC0000000:
|
|
{
|
|
//return _C0000000;
|
|
return 0;
|
|
}
|
|
case 0xC0010110: // LeMans24
|
|
case 0xC0010114: // LeMans24
|
|
case 0xC0020000: // Network? Sega Rally 2 at 0x79268
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 0xf:
|
|
{
|
|
if ((a >= 0xF0040000 && a <= 0xF004003F) || // control area
|
|
(a >= 0xFE040000 && a <= 0xFE04003F))
|
|
{
|
|
return (controls_read(a + 0) << 24) |
|
|
(controls_read(a + 1) << 16) |
|
|
(controls_read(a + 2) << 8) |
|
|
(controls_read(a + 3) << 0);
|
|
}
|
|
else if (a >= 0xF0080000 && a <= 0xF00800FF) // MIDI?
|
|
{
|
|
return 0xFFFFFFFF;
|
|
}
|
|
else if ((a >= 0xF00C0000 && a <= 0xF00DFFFF) ||
|
|
(a >= 0xFE0C0000 && a <= 0xFE0FFFFF)) // backup RAM
|
|
{
|
|
return BSWAP32(*(UINT32 *) &bram[a & 0x3FFFF]);
|
|
}
|
|
else if ((a >= 0xF0100000 && a <= 0xF010003F) ||
|
|
(a >= 0xFE100000 && a <= 0xFE10003F)) // system control
|
|
{
|
|
return model3_sys_read_32(a);
|
|
}
|
|
else if ((a >= 0xF0140000 && a <= 0xF014003F) ||
|
|
(a >= 0xFE140000 && a <= 0xFE14003F)) // RTC
|
|
{
|
|
return rtc_read_32(a);
|
|
}
|
|
else if (a >= 0xF1000000 && a <= 0xF111FFFF) // tile generator VRAM
|
|
{
|
|
return tilegen_vram_read_32(a);
|
|
}
|
|
else if (a >= 0xF1180000 && a <= 0xF11800FF) // tile generator regs
|
|
{
|
|
return tilegen_read_32(a);
|
|
}
|
|
else if (a >= 0xFEE00000 && a <= 0xFEEFFFFF) // MPC106 CONFIG_DATA
|
|
{
|
|
return bridge_read_config_data_32(a);
|
|
}
|
|
else if (a >= 0xF9000000 && a <= 0xF90000FF) // 53C810 SCSI
|
|
{
|
|
return scsi_read_32(a);
|
|
}
|
|
|
|
switch (a)
|
|
{
|
|
case 0xF0C00CFC: // MPC105/106 CONFIG_DATA
|
|
{
|
|
return bridge_read_config_data_32(a);
|
|
}
|
|
|
|
case 0xFE180000: // ? SWT
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
case 0xFE1A0000: // ? Virtual On 2 -- important to return 0
|
|
{
|
|
return 0; // see my message on 13May ("VROM Port?")
|
|
}
|
|
|
|
case 0xFE1A001C:
|
|
{
|
|
if (_stricmp(m3_config.game_id, "vs299") == 0 ||
|
|
_stricmp(m3_config.game_id, "vs2v991") == 0)
|
|
{
|
|
UINT32 data = (UINT32)(vs299_prot_data[prot_data_ptr++]) << 16;
|
|
if (prot_data_ptr > 0x65)
|
|
{
|
|
prot_data_ptr = 0;
|
|
}
|
|
return data;
|
|
}
|
|
else if (_stricmp(m3_config.game_id, "swtrilgy") == 0 ||
|
|
_stricmp(m3_config.game_id, "swtrilga") == 0)
|
|
{
|
|
UINT32 data = (UINT32)swt_prot_data[prot_data_ptr++] << 16;
|
|
if (prot_data_ptr > 0x38)
|
|
{
|
|
prot_data_ptr = 0;
|
|
}
|
|
return data;
|
|
}
|
|
else if (_stricmp(m3_config.game_id, "fvipers2") == 0)
|
|
{
|
|
UINT32 data = (UINT32)fvipers2_prot_data[prot_data_ptr++] << 16;
|
|
if (prot_data_ptr >= 0x41)
|
|
{
|
|
prot_data_ptr = 0;
|
|
}
|
|
return data;
|
|
}
|
|
else if (_stricmp(m3_config.game_id, "spikeout") == 0 ||
|
|
_stricmp(m3_config.game_id, "spikeofe") == 0)
|
|
{
|
|
UINT32 data = (UINT32)spikeout_prot_data[prot_data_ptr++] << 16;
|
|
if (prot_data_ptr >= 0x55)
|
|
{
|
|
prot_data_ptr = 0;
|
|
}
|
|
return data;
|
|
}
|
|
else if (_stricmp(m3_config.game_id, "eca") == 0)
|
|
{
|
|
UINT32 data = (UINT32)(eca_prot_data[prot_data_ptr++]) << 16;
|
|
if (prot_data_ptr >= 0x31)
|
|
{
|
|
prot_data_ptr = 0;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
else
|
|
{
|
|
return 0xffffffff;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
error("%08X: unknown read32, %08X\n", ppc_get_pc(), a);
|
|
return 0xFFFFFFFF;
|
|
}
|
|
|
|
UINT64 m3_ppc_read_64(UINT32 a)
|
|
{
|
|
UINT64 d;
|
|
PROFILE_MEMORY_OP(64, 0, a);
|
|
|
|
d = m3_ppc_read_32(a + 0);
|
|
d <<= 32;
|
|
d |= m3_ppc_read_32(a + 4);
|
|
|
|
return d;
|
|
}
|
|
|
|
void m3_ppc_write_8(UINT32 a, UINT8 d)
|
|
{
|
|
PROFILE_MEMORY_OP(8, 1, a);
|
|
|
|
/*
|
|
* RAM tested for first for speed
|
|
*/
|
|
|
|
if (a <= 0x007FFFFF)
|
|
{
|
|
ram[a] = d;
|
|
return;
|
|
}
|
|
|
|
switch (a >> 28)
|
|
{
|
|
case 0xC:
|
|
|
|
if (a >= 0xC0000000 && a <= 0xC00000FF) // 53C810 SCSI
|
|
{
|
|
if (m3_config.step == 0x10)
|
|
{
|
|
scsi_write_8(a, d);
|
|
return;
|
|
}
|
|
}
|
|
else if (a >= 0xC2000000 && a <= 0xC200001F) // DMA device
|
|
{
|
|
dma_write_8(a, d);
|
|
return;
|
|
}
|
|
else if (a >= 0xc3800000 && a <= 0xc380001f) // Daytona 2 protection/bank-switch
|
|
{
|
|
crom_bank = &crom[0x800000 + (((~d) & 0xf) * 0x800000)];
|
|
return;
|
|
}
|
|
|
|
switch (a)
|
|
{
|
|
case 0xC0010180: // ? Lost World, PC = 0x11B510
|
|
case 0xC1000014: // ? Lost World, PC = 0xFF80098C
|
|
case 0xC1000038: // ? Sega Rally, PC = 0x7B1F0
|
|
case 0xC1000039: // ? Lost World, PC = 0x118BD8
|
|
case 0xC100003B: // Scud Race Plus
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
case 0xF:
|
|
|
|
if ((a >= 0xF0040000 && a <= 0xF004003F) || // control area
|
|
(a >= 0xFE040000 && a <= 0xFE04003F))
|
|
{
|
|
controls_write(a, d);
|
|
return;
|
|
}
|
|
else if ((a >= 0xF0080000 && a <= 0xF00800FF) ||
|
|
(a >= 0xFE080000 && a <= 0xFE0800FF)) // MIDI?
|
|
{
|
|
model3_midi_write(a, d);
|
|
return;
|
|
}
|
|
else if ((a >= 0xF0100000 && a <= 0xF010003F) ||
|
|
(a >= 0xFE100000 && a <= 0xFE10003F)) // system control
|
|
{
|
|
model3_sys_write_8(a, d);
|
|
return;
|
|
}
|
|
else if ((a >= 0xF0140000 && a <= 0xF014003F) ||
|
|
(a >= 0xFE140000 && a <= 0xFE14003F)) // RTC? (Sega Rally 2)
|
|
{
|
|
rtc_write(a, d);
|
|
return;
|
|
}
|
|
else if (a >= 0xF8FFF000 && a <= 0xF8FFF0FF) // MPC105 regs
|
|
{
|
|
bridge_write_8(a, d);
|
|
return;
|
|
}
|
|
else if (a >= 0xFEE00000 && a <= 0xFEEFFFFF) // MPC106 CONFIG_DATA
|
|
{
|
|
bridge_write_config_data_8(a, d);
|
|
return;
|
|
}
|
|
else if (a >= 0xF9000000 && a <= 0xF90000FF) // 53C810 SCSI
|
|
{
|
|
scsi_write_8(a, d);
|
|
return;
|
|
}
|
|
|
|
switch (a)
|
|
{
|
|
case 0xFE000004: // Sega Rally 2
|
|
case 0xF118000C: // Harley Davidson (tilegen 8-bit)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
error("%08X: unknown write8, %08X = %02X\n", ppc_get_pc(), a, d);
|
|
}
|
|
|
|
void m3_ppc_write_16(UINT32 a, UINT16 d)
|
|
{
|
|
PROFILE_MEMORY_OP(16, 1, a);
|
|
|
|
/*
|
|
* RAM tested for first for speed
|
|
*/
|
|
|
|
if (a <= 0x007FFFFF)
|
|
{
|
|
*(UINT16 *) &ram[a] = BSWAP16(d);
|
|
return;
|
|
}
|
|
|
|
switch (a >> 28)
|
|
{
|
|
case 0xC:
|
|
|
|
if (a >= 0xC2000000 && a <= 0xC200001F) // DMA device
|
|
{
|
|
dma_write_16(a, d);
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
case 0xF:
|
|
|
|
if (a >= 0xF8FFF000 && a <= 0xF8FFF0FF) // MPC105 regs
|
|
{
|
|
bridge_write_16(a, d);
|
|
return;
|
|
}
|
|
else if (a >= 0xFEE00000 && a <= 0xFEEFFFFF) // MPC106 CONFIG_DATA
|
|
{
|
|
bridge_write_config_data_16(a, d);
|
|
return;
|
|
}
|
|
else if ((a >= 0xF00C0000 && a <= 0xF00DFFFF) ||
|
|
(a >= 0xFE0C0000 && a <= 0xFE0FFFFF)) // backup RAM
|
|
{
|
|
*(UINT16 *) &bram[a & 0x3FFFF] = BSWAP16(d);
|
|
return;
|
|
}
|
|
|
|
switch (a)
|
|
{
|
|
case 0xF0C00CFC: // MPC105/106 CONFIG_DATA
|
|
bridge_write_config_data_16(a, d);
|
|
return;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
error("%08X: unknown write16, %08X = %04X\n", ppc_get_pc(), a, d);
|
|
}
|
|
|
|
void m3_ppc_write_32(UINT32 a, UINT32 d)
|
|
{
|
|
PROFILE_MEMORY_OP(32, 1, a);
|
|
|
|
/*
|
|
* RAM tested for first for speed
|
|
*/
|
|
|
|
if (a <= 0x007FFFFF)
|
|
{
|
|
*(UINT32 *) &ram[a] = BSWAP32(d);
|
|
return;
|
|
}
|
|
|
|
switch (a >> 28)
|
|
{
|
|
case 0x8:
|
|
case 0x9:
|
|
{
|
|
r3d_write_32(a, d); // Real3D memory regions
|
|
return;
|
|
}
|
|
|
|
case 0xc:
|
|
{
|
|
if (a >= 0xC0000000 && a <= 0xC00000FF) // 53C810 SCSI
|
|
{
|
|
if (m3_config.step == 0x10)
|
|
{
|
|
scsi_write_32(a, d);
|
|
return;
|
|
}
|
|
}
|
|
else if (a >= 0xC1000000 && a <= 0xC10000FF) // 53C810 SCSI
|
|
{
|
|
scsi_write_32(a, d);
|
|
return;
|
|
}
|
|
else if (a >= 0xC2000000 && a <= 0xC200001F) // DMA device
|
|
{
|
|
dma_write_32(a, d);
|
|
return;
|
|
}
|
|
|
|
switch (a)
|
|
{
|
|
case 0xC0000000: // latched value
|
|
{
|
|
//_C0000000 = d;
|
|
return;
|
|
}
|
|
case 0xC0010180: // Network ? Scud Race at 0xB2A4
|
|
{
|
|
return;
|
|
}
|
|
case 0xC0020000: // Network? Sega Rally 2 at 0x79264
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 0xf:
|
|
{
|
|
if ((a >= 0xF0040000 && a <= 0xF004003F) || // control area
|
|
(a >= 0xFE040000 && a <= 0xFE04003F))
|
|
{
|
|
controls_write(a + 0, (UINT8) (d >> 24));
|
|
controls_write(a + 1, (UINT8) (d >> 16));
|
|
controls_write(a + 2, (UINT8) (d >> 8));
|
|
controls_write(a + 3, (UINT8) (d >> 0));
|
|
return;
|
|
}
|
|
else if ((a >= 0xF00C0000 && a <= 0xF00DFFFF) ||
|
|
(a >= 0xFE0C0000 && a <= 0xFE0FFFFF)) // backup RAM
|
|
{
|
|
*(UINT32 *) &bram[a & 0x3FFFF] = BSWAP32(d);
|
|
return;
|
|
}
|
|
else if ((a >= 0xF0100000 && a <= 0xF010003F) ||
|
|
(a >= 0xFE100000 && a <= 0xFE10003F)) // system control
|
|
{
|
|
model3_sys_write_32(a, d);
|
|
return;
|
|
}
|
|
else if ((a >= 0xF0140000 && a <= 0xF014003F) ||
|
|
(a >= 0xFE140000 && a <= 0xFE14003F)) // RTC
|
|
{
|
|
rtc_write(a, (UINT8) (d >> 24));
|
|
return;
|
|
}
|
|
else if (a >= 0xFE180000 && a <= 0xFE19FFFF) // ?
|
|
{
|
|
// LOG("model3.log", "security %08X = %04X\n", a, d >> 16);
|
|
return;
|
|
}
|
|
else if (a >= 0xF1000000 && a <= 0xF111FFFF) // tile generator VRAM
|
|
{
|
|
tilegen_vram_write_32(a, d);
|
|
return;
|
|
}
|
|
else if (a >= 0xF1180000 && a <= 0xF11800FF) // tile generator regs
|
|
{
|
|
tilegen_write_32(a, d);
|
|
return;
|
|
}
|
|
else if (a >= 0xF8FFF000 && a <= 0xF8FFF0FF) // MPC105 regs
|
|
{
|
|
bridge_write_32(a, d);
|
|
return;
|
|
}
|
|
else if (a >= 0xFEC00000 && a <= 0xFEDFFFFF) // MPC106 CONFIG_ADDR
|
|
{
|
|
bridge_write_config_addr_32(a, d);
|
|
return;
|
|
}
|
|
else if (a >= 0xFEE00000 && a <= 0xFEEFFFFF) // MPC106 CONFIG_DATA
|
|
{
|
|
bridge_write_config_data_32(a, d);
|
|
return;
|
|
}
|
|
else if (a >= 0xF9000000 && a <= 0xF90000FF) // 53C810 SCSI
|
|
{
|
|
scsi_write_32(a, d);
|
|
return;
|
|
}
|
|
|
|
switch (a)
|
|
{
|
|
case 0xF0800CF8: // MPC105/106 CONFIG_ADDR
|
|
{
|
|
bridge_write_config_addr_32(a, d);
|
|
return;
|
|
}
|
|
case 0xF0C00CFC: // MPC105/106 CONFIG_DATA
|
|
{
|
|
bridge_write_config_data_32(a, d);
|
|
return;
|
|
}
|
|
case 0xFE1A0000: // ? Virtual On 2
|
|
{
|
|
return;
|
|
}
|
|
case 0xFE1A0010:
|
|
{
|
|
return;
|
|
}
|
|
case 0xFE1A0014:
|
|
{
|
|
LOG("model3.log", "security reset = %08X\n", d);
|
|
return;
|
|
}
|
|
case 0xFE1A0018:
|
|
{
|
|
LOG("model3.log", "security key = %08X\n", d);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
error("%08X: unknown write32, %08X = %08X\n", ppc_get_pc(), a, d);
|
|
}
|
|
|
|
void m3_ppc_write_64(UINT32 a, UINT64 d)
|
|
{
|
|
PROFILE_MEMORY_OP(64, 1, a);
|
|
|
|
m3_ppc_write_32(a + 0, (UINT32) (d >> 32));
|
|
m3_ppc_write_32(a + 4, (UINT32) d);
|
|
}
|
|
|
|
|
|
|
|
void model3_dma_transfer(UINT32 src, UINT32 dst, int length, BOOL swap_words)
|
|
{
|
|
UINT32 *s;
|
|
if (src < 0x800000)
|
|
{
|
|
s = (UINT32 *)&ram[src];
|
|
}
|
|
else if (src >= 0xff000000 && src < 0xff800000)
|
|
{
|
|
s = (UINT32 *)&crom_bank[src - 0xff000000];
|
|
}
|
|
else if (src >= 0xff800000)
|
|
{
|
|
s = (UINT32 *)&crom[src - 0xff800000];
|
|
}
|
|
else
|
|
{
|
|
error("model3_dma_transfer: source = %08X\n", src);
|
|
}
|
|
|
|
switch ((dst >> 24) & 0xff)
|
|
{
|
|
case 0x8c: r3d_dma_culling_ram_8c(s, dst, length, swap_words); break;
|
|
case 0x8e: r3d_dma_culling_ram_8e(s, dst, length, swap_words); break;
|
|
case 0x94: r3d_dma_texture_ram(s, dst, length, swap_words); break;
|
|
case 0x98: r3d_dma_polygon_ram(s, dst, length, swap_words); break;
|
|
|
|
default:
|
|
{
|
|
int i;
|
|
if (swap_words)
|
|
{
|
|
for (i=0; i < length; i+=4)
|
|
{
|
|
UINT32 d = *s++;
|
|
r3d_write_32(dst, d);
|
|
dst += 4;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i=0; i < length; i+=4)
|
|
{
|
|
UINT32 d = BSWAP32(*s++);
|
|
r3d_write_32(dst, d);
|
|
dst += 4;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* System Control (0xFx100000 - 0xFx10003F) */
|
|
/******************************************************************/
|
|
|
|
static UINT8 model3_irq_state = 0; // 0xF0100018
|
|
static UINT8 model3_irq_enable = 0; // 0xF0100014
|
|
static UINT8 crom_bank_reg;
|
|
|
|
/*
|
|
* void m3_add_irq(UINT8 mask);
|
|
*
|
|
* Raises an IRQ (sets its status bit.)
|
|
*
|
|
* Parameters:
|
|
* mask = Mask corresponding to upper 8 bits of IRQ status register.
|
|
*/
|
|
|
|
void model3_add_irq(UINT8 mask)
|
|
{
|
|
model3_irq_state |= mask;
|
|
}
|
|
|
|
/*
|
|
* void m3_remove_irq(UINT8 mask);
|
|
*
|
|
* Removes an IRQ (lowers its status bit.)
|
|
*
|
|
* Parameters:
|
|
* mask = Mask corresponding to upper 8 bits of IRQ status register.
|
|
*/
|
|
|
|
void model3_remove_irq(UINT8 mask)
|
|
{
|
|
model3_irq_state &= ~mask;
|
|
}
|
|
|
|
static UINT32 model3_ppc_irq_callback(void)
|
|
{
|
|
return(0); /* no other IRQs in the queue */
|
|
}
|
|
|
|
/*
|
|
* m3_set_crom_bank():
|
|
*
|
|
* Sets the CROM bank register and maps the requested 8MB CROM bank in.
|
|
* Note that all CROMs are stored in the same 72MB buffer which is why 8MB is
|
|
* added (to skip over CROM0-3.)
|
|
*/
|
|
|
|
static void model3_set_crom_bank(UINT8 d)
|
|
{
|
|
crom_bank_reg = d;
|
|
crom_bank = &crom[0x800000 + ((~d) & 0xf) * 0x800000];
|
|
|
|
LOG("model3.log", "CROM bank = %02X\n", d);
|
|
}
|
|
|
|
static UINT8 model3_sys_read_8(UINT32 a)
|
|
{
|
|
static UINT8 x = 0x20;
|
|
|
|
switch(a & 0xFF)
|
|
{
|
|
case 0x08: // CROM bank
|
|
{
|
|
return crom_bank_reg;
|
|
}
|
|
|
|
case 0x10: // JTAG TAP
|
|
{
|
|
return ((tap_read() & 1) << 5);
|
|
}
|
|
|
|
case 0x14: // IRQ enable
|
|
{
|
|
return model3_irq_enable;
|
|
}
|
|
|
|
case 0x18: // IRQ status
|
|
{
|
|
return model3_irq_state;
|
|
}
|
|
|
|
case 0x1C: // ?
|
|
{
|
|
// LOG("model3.log", "%08X: unknown sys read8, %08X\n", PPC_PC, a);
|
|
return 0xff;
|
|
}
|
|
}
|
|
|
|
LOG("model3.log", "%08X: unknown sys read8, %08X\n", ppc_get_pc(), a);
|
|
message(0, "%08X: unknown sys read8, %08X", ppc_get_pc(), a);
|
|
return 0xff;
|
|
}
|
|
|
|
static UINT32 model3_sys_read_32(UINT32 a)
|
|
{
|
|
switch(a & 0xFF)
|
|
{
|
|
case 0x10: // JTAG TAP
|
|
{
|
|
return ((tap_read() & 1) << (5+24));
|
|
}
|
|
|
|
case 0x14: // IRQ enable
|
|
{
|
|
return (model3_irq_enable << 24);
|
|
}
|
|
|
|
case 0x18: // IRQ status
|
|
{
|
|
return (model3_irq_state << 24);
|
|
}
|
|
}
|
|
|
|
LOG("model3.log", "%08X: unknown sys read32, %08X\n", ppc_get_pc(), a);
|
|
message(0, "%08X: unknown sys read32, %08X", ppc_get_pc(), a);
|
|
return 0xffffffff;
|
|
}
|
|
|
|
static void model3_sys_write_8(UINT32 a, UINT8 d)
|
|
{
|
|
switch(a & 0xff)
|
|
{
|
|
case 0x00: // ?
|
|
{
|
|
LOG("model3.log", "%08X: unknown sys write8, %08X = %02X\n", ppc_get_pc(), a, d);
|
|
return;
|
|
}
|
|
|
|
case 0x04: // ?
|
|
{
|
|
LOG("model3.log", "%08X: unknown sys write8, %08X = %02X\n", ppc_get_pc(), a, d);
|
|
return;
|
|
}
|
|
|
|
case 0x08: // CROM bank
|
|
{
|
|
model3_set_crom_bank(d);
|
|
return;
|
|
}
|
|
|
|
case 0x0c: // JTAG TAP
|
|
{
|
|
tap_write(
|
|
(d >> 6) & 1, // TCK
|
|
(d >> 2) & 1, // TMS
|
|
(d >> 5) & 1, // TDI
|
|
(d >> 7) & 1 // TRST
|
|
);
|
|
return;
|
|
}
|
|
|
|
case 0x14: // IRQ enable
|
|
{
|
|
model3_irq_enable = d;
|
|
return;
|
|
}
|
|
|
|
case 0x1c: // ? this may be the LED control
|
|
{
|
|
// LOG("model3.log", "%08X: unknown sys write8, %08X = %02X\n", PPC_PC, a, d);
|
|
return;
|
|
}
|
|
|
|
case 0x3c: // ?
|
|
{
|
|
LOG("model3.log", "%08X: unknown sys write8, %08X = %02X\n", ppc_get_pc(), a, d);
|
|
return;
|
|
}
|
|
}
|
|
|
|
LOG("model3.log", "%08X: unknown sys write8, %08X = %02X\n", ppc_get_pc(), a, d);
|
|
message(0, "%08X: unknown sys write8, %08X = %02X", ppc_get_pc(), a, d);
|
|
}
|
|
|
|
static void model3_sys_write_32(UINT32 a, UINT32 d)
|
|
{
|
|
switch(a & 0xff)
|
|
{
|
|
case 0x0c: // JTAG TAP
|
|
{
|
|
tap_write(
|
|
(d >> (6+24)) & 1,
|
|
(d >> (2+24)) & 1,
|
|
(d >> (5+24)) & 1,
|
|
(d >> (7+24)) & 1
|
|
);
|
|
return;
|
|
}
|
|
|
|
case 0x14: // IRQ mask
|
|
{
|
|
model3_irq_enable = (d >> 24);
|
|
return;
|
|
}
|
|
|
|
case 0x1c: // ?
|
|
{
|
|
LOG("model3.log", "%08X: unknown sys write32, %08X = %08X\n", ppc_get_pc(), a, d);
|
|
return;
|
|
}
|
|
}
|
|
|
|
LOG("model3.log", "%08X: unknown sys write32, %08X = %08X\n", ppc_get_pc(), a, d);
|
|
message(0, "%08X: unknown sys write32, %08X = %08X", ppc_get_pc(), a, d);
|
|
}
|
|
|
|
static UINT8 model3_midi_read(UINT32 a)
|
|
{
|
|
/* 0xFx0800xx */
|
|
|
|
return 0xFF;
|
|
// return(0);
|
|
}
|
|
|
|
static void model3_midi_write(UINT32 a, UINT8 d)
|
|
{
|
|
/* 0xFx0800xx */
|
|
|
|
//message(0, "%08X: MIDI write, %08X = %02X", ppc_get_pc(), a, d);
|
|
|
|
if ((a & 0xf) == 0)
|
|
{
|
|
model3_remove_irq(0x40);
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* PCI Command Callback */
|
|
/******************************************************************/
|
|
|
|
static UINT32 pci_command_callback(UINT32 cmd)
|
|
{
|
|
int pci_device = (cmd >> 11) & 0x1f;
|
|
int pci_reg = (cmd >> 2) & 0x3f;
|
|
|
|
switch (cmd)
|
|
{
|
|
case 0x80006800: // reg 0 of PCI config header
|
|
{
|
|
if (m3_config.step <= 0x15)
|
|
return 0x16C311DB; // 0x11DB = PCI vendor ID (Sega), 0x16C3 = device ID
|
|
else
|
|
return 0x178611DB;
|
|
}
|
|
|
|
case 0x80007000: // VF3
|
|
case 0x80007002: // Sega Rally 2
|
|
{
|
|
return 0x00011000;
|
|
}
|
|
|
|
case 0x80008000: // Daytona 2, protection/bank-switch
|
|
{
|
|
return 0x182711db;
|
|
}
|
|
|
|
default:
|
|
{
|
|
LOG("PCI callback: %08X (device %d, reg %d)\n", cmd, pci_device, pci_reg);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//LOG("model3.log", "%08X (%08X): PCI command issued: %08X\n", PPC_PC, PPC_LR, cmd);
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* Load/Save Stuff */
|
|
/******************************************************************/
|
|
|
|
static void word_swap(UINT8 *src, INT size)
|
|
{
|
|
while (size -= 4)
|
|
{
|
|
*((UINT32 *) src) = BSWAP32(*((UINT32 *) src));
|
|
src += sizeof(UINT32);
|
|
}
|
|
}
|
|
|
|
void model3_load_eeprom(void)
|
|
{
|
|
char string[512];
|
|
INT i;
|
|
|
|
sprintf( string, "%s/%s.epr", m3_config.backup_path, m3_config.game_id );
|
|
|
|
if((i = eeprom_load(string)) != MODEL3_OKAY)
|
|
{
|
|
message(0, "Can't load EEPROM from %s (%d)", string, i);
|
|
}
|
|
}
|
|
|
|
void model3_save_eeprom(void)
|
|
{
|
|
char string[512];
|
|
INT i;
|
|
|
|
sprintf( string, "%s/%s.epr", m3_config.backup_path, m3_config.game_id );
|
|
|
|
if((i = eeprom_save(string)) != MODEL3_OKAY)
|
|
{
|
|
message(0, "Can't save EEPROM to %s (%d)", string, i);
|
|
}
|
|
}
|
|
|
|
void model3_load_bram(void)
|
|
{
|
|
char string[512];
|
|
|
|
sprintf( string, "%s/%s.brm", m3_config.backup_path, m3_config.game_id );
|
|
|
|
if(load_file(string, bram, 256*1024))
|
|
{
|
|
message(0, "Can't load Backup RAM from file, creating a new file.");
|
|
memset(bram, 0xFF, 256*1024);
|
|
save_file(string, bram, 256*1024, 0);
|
|
}
|
|
}
|
|
|
|
void model3_save_bram(void)
|
|
{
|
|
char string[512];
|
|
sprintf( string, "%s/%s.brm", m3_config.backup_path, m3_config.game_id );
|
|
|
|
save_file(string, bram, 256*1024, 0);
|
|
}
|
|
|
|
/*
|
|
* BOOL model3_save_state(CHAR *file);
|
|
*
|
|
* Saves a state.
|
|
*
|
|
* Parameters:
|
|
* file = Name of save state file to generate.
|
|
*
|
|
* Returns:
|
|
* MODEL3_OKAY = Success.
|
|
* MODEL3_ERROR = Unable to open the save state file.
|
|
*/
|
|
|
|
BOOL model3_save_state(CHAR *file)
|
|
{
|
|
FILE *fp;
|
|
|
|
if ((fp = fopen(file, "wb")) == NULL)
|
|
{
|
|
error("Unable to save state to %s", file);
|
|
return MODEL3_ERROR;
|
|
}
|
|
|
|
/*
|
|
* Write out the main data: PowerPC RAM, backup RAM, and system control
|
|
* registers
|
|
*/
|
|
|
|
fwrite(ram, sizeof(UINT8), 8*1024*1024, fp);
|
|
fwrite(bram, sizeof(UINT8), 256*1024, fp);
|
|
fwrite(&model3_irq_state, sizeof(UINT8), 1, fp);
|
|
fwrite(&model3_irq_enable, sizeof(UINT8), 1, fp);
|
|
fwrite(&crom_bank_reg, sizeof(UINT8), 1, fp);
|
|
|
|
/*
|
|
* Save the rest of the system state
|
|
*/
|
|
|
|
//ppc_save_state(fp);
|
|
bridge_save_state(fp);
|
|
controls_save_state(fp);
|
|
dma_save_state(fp);
|
|
dsb1_save_state(fp);
|
|
eeprom_save_state(fp);
|
|
r3d_save_state(fp);
|
|
rtc_save_state(fp);
|
|
scsi_save_state(fp);
|
|
tilegen_save_state(fp);
|
|
// scsp_save_state(fp);
|
|
|
|
fclose(fp);
|
|
LOG("model3.log", "saved state: %s\n", file);
|
|
return MODEL3_OKAY;
|
|
}
|
|
|
|
/*
|
|
* void m3_load_state(CHAR *file);
|
|
*
|
|
* Loads a state.
|
|
*
|
|
* Parameters:
|
|
* file = Name of save state file to load.
|
|
*
|
|
* Returns:
|
|
* MODEL3_OKAY = Success.
|
|
* MODEL3_ERROR = Unable to open the save state file.
|
|
*/
|
|
|
|
BOOL model3_load_state(CHAR *file)
|
|
{
|
|
FILE *fp;
|
|
|
|
if ((fp = fopen(file, "rb")) == NULL)
|
|
{
|
|
error("Unable to load state from %s", file);
|
|
return MODEL3_ERROR;
|
|
}
|
|
|
|
/*
|
|
* Load main data: PowerPC RAM, backup RAM, and system control registers
|
|
*/
|
|
|
|
fread(ram, sizeof(UINT8), 8*1024*1024, fp);
|
|
fread(bram, sizeof(UINT8), 256*1024, fp);
|
|
fread(&model3_irq_state, sizeof(UINT8), 1, fp);
|
|
fread(&model3_irq_enable, sizeof(UINT8), 1, fp);
|
|
fread(&crom_bank_reg, sizeof(UINT8), 1, fp);
|
|
model3_set_crom_bank(crom_bank_reg);
|
|
|
|
/*
|
|
* Load the rest of the system state
|
|
*/
|
|
|
|
//ppc_load_state(fp);
|
|
bridge_load_state(fp);
|
|
controls_load_state(fp);
|
|
dma_load_state(fp);
|
|
dsb1_load_state(fp);
|
|
eeprom_load_state(fp);
|
|
r3d_load_state(fp);
|
|
rtc_load_state(fp);
|
|
scsi_load_state(fp);
|
|
tilegen_load_state(fp);
|
|
// scsp_load_state(fp);
|
|
|
|
fclose(fp);
|
|
LOG("model3.log", "loaded state: %s\n", file);
|
|
return MODEL3_OKAY;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* Machine Execution Loop */
|
|
/******************************************************************/
|
|
|
|
UINT m3_irq_bit = 0; // debug
|
|
|
|
static LONGLONG timer_start, timer_end;
|
|
static LONGLONG timer_frequency;
|
|
|
|
static int frame = 0;
|
|
|
|
void model3_run_frame(void)
|
|
{
|
|
/*
|
|
* Reset all profiling sections
|
|
*/
|
|
|
|
PROFILE_SECT_RESET("-");
|
|
PROFILE_SECT_RESET("ppc");
|
|
PROFILE_SECT_RESET("68k");
|
|
PROFILE_SECT_RESET("tilegen");
|
|
PROFILE_SECT_RESET("real3d");
|
|
PROFILE_SECT_RESET("dma");
|
|
PROFILE_SECT_RESET("scsi");
|
|
|
|
PROFILE_SECT_ENTRY("-");
|
|
|
|
QueryPerformanceFrequency((LARGE_INTEGER*)&timer_frequency);
|
|
|
|
if (m3_config.fps_limit)
|
|
{
|
|
double time = 0.0f;
|
|
do
|
|
{
|
|
QueryPerformanceCounter((LARGE_INTEGER*)&timer_end);
|
|
|
|
time = (double)(timer_end - timer_start) / (double)(timer_frequency);
|
|
} while (time <= 1.0/60.0);
|
|
|
|
timer_start = timer_end;
|
|
}
|
|
|
|
|
|
/*
|
|
* Run the PowerPC and 68K
|
|
*/
|
|
|
|
LOG("model3.log", "-- ACTIVE SCAN\n");
|
|
//model3_add_irq(model3_irq_enable & 0x0D);
|
|
//ppc_set_irq_line(1);
|
|
|
|
PROFILE_SECT_ENTRY("ppc");
|
|
ppc_execute(ppc_freq / 60);
|
|
PROFILE_SECT_EXIT("ppc");
|
|
|
|
PROFILE_SECT_ENTRY("68k");
|
|
PROFILE_SECT_EXIT("68k");
|
|
|
|
/*
|
|
* Enter VBlank and update the graphics
|
|
*/
|
|
|
|
rtc_step_frame();
|
|
render_frame();
|
|
controls_update();
|
|
|
|
/*
|
|
* Generate interrupts for this frame and run the VBlank
|
|
*/
|
|
|
|
LOG("model3.log", "-- VBL\n");
|
|
|
|
//if (frame & 1)
|
|
{
|
|
model3_add_irq(model3_irq_enable & 0x4a);
|
|
}
|
|
|
|
ppc_set_irq_line(1);
|
|
|
|
frame++;
|
|
|
|
//PROFILE_SECT_ENTRY("ppc");
|
|
//ppc_execute(100000);
|
|
//PROFILE_SECT_EXIT("ppc");
|
|
|
|
//model3_remove_irq(0xFF); // some games expect a bunch of IRQs to go low after some time
|
|
|
|
PROFILE_SECT_EXIT("-");
|
|
}
|
|
|
|
void model3_reset(void)
|
|
{
|
|
/* the ROM must be already loaded at this point. */
|
|
/* it must _always_ be called when you load a ROM. */
|
|
|
|
/* profiler */
|
|
|
|
#ifdef _PROFILE_
|
|
profile_cleanup();
|
|
#endif
|
|
|
|
/* init log file */
|
|
|
|
LOG_INIT("model3.log");
|
|
LOG("model3.log", "XMODEL "VERSION", built on "__DATE__" "__TIME__".\n\n");
|
|
|
|
/* reset all the buffers */
|
|
|
|
memset(ram, 0, 8*1024*1024);
|
|
memset(vram, 0, 1*1024*1024+2*65536);
|
|
memset(sram, 0, 1*1024*1024);
|
|
memset(bram, 0, 256*1024);
|
|
|
|
/* reset all the modules */
|
|
|
|
ppc_reset();
|
|
//if (ppc_reset() != PPC_OKAY)
|
|
// error("ppc_reset failed");
|
|
|
|
bridge_reset(m3_config.bridge);
|
|
eeprom_reset();
|
|
scsi_reset();
|
|
dma_reset();
|
|
|
|
tilegen_reset();
|
|
r3d_reset();
|
|
// scsp_reset();
|
|
// if(m3_config.flags & GAME_OWN_DSB1) dsb_reset();
|
|
controls_reset(m3_config.flags & (GAME_OWN_STEERING_WHEEL | GAME_OWN_GUN));
|
|
|
|
model3_remove_irq(0xFF);
|
|
model3_set_crom_bank(0xFF);
|
|
|
|
/* load NVRAMs */
|
|
|
|
model3_load_eeprom();
|
|
model3_load_bram();
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* File (and ROM) Management */
|
|
/******************************************************************/
|
|
|
|
/*
|
|
* Byteswap a buffer.
|
|
*/
|
|
|
|
static void byteswap(UINT8 *buf, UINT size)
|
|
{
|
|
UINT i;
|
|
UINT8 tmp;
|
|
|
|
for (i = 0; i < size; i += 2)
|
|
{
|
|
tmp = buf[i];
|
|
buf[i] = buf[i + 1];
|
|
buf[i + 1] = tmp;
|
|
}
|
|
}
|
|
|
|
static void byteswap32(UINT8 *buf, UINT size)
|
|
{
|
|
UINT i;
|
|
|
|
for(i = 0; i < size; i += 4)
|
|
*(UINT32 *)&buf[i] = BSWAP32(*(UINT32 *)&buf[i]);
|
|
}
|
|
|
|
#define NO_ITLV 1
|
|
#define ITLV_2 2
|
|
#define ITLV_4 4
|
|
#define ITLV_16 16
|
|
|
|
typedef struct
|
|
{
|
|
ROMFILE prog[4];
|
|
ROMFILE crom[4][4];
|
|
ROMFILE vrom[16];
|
|
ROMFILE sprog;
|
|
ROMFILE srom[4];
|
|
ROMFILE dsbprog;
|
|
ROMFILE dsbrom[4];
|
|
} ROM_LIST;
|
|
|
|
static BOOL load_rom(UINT8 *buffer, ROMFILE *romfile, UINT32 offset, UINT32 interleave)
|
|
{
|
|
int j;
|
|
int size;
|
|
UINT32 crc;
|
|
UINT8 *temp_buffer;
|
|
|
|
size = (int)get_file_size_crc(romfile->crc32);
|
|
if (size <= 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//message(0, "loading %s\r", romfile->name);
|
|
{
|
|
static longest_line = 0;
|
|
char string[200];
|
|
int length;
|
|
sprintf(string, "Loading %s", romfile->name);
|
|
length = strlen(string);
|
|
longest_line = max(longest_line, length);
|
|
|
|
for (j=0; j < longest_line; j++)
|
|
{
|
|
printf(" ");
|
|
}
|
|
printf("\r");
|
|
printf("%s\r", string);
|
|
}
|
|
|
|
if (size != romfile->size)
|
|
{
|
|
message(0, "File %s has wrong size ! (%d bytes, should be %d bytes)\n", romfile->name, size, romfile->size);
|
|
return FALSE;
|
|
}
|
|
|
|
temp_buffer = (UINT8*)malloc(size);
|
|
if (read_file_crc(romfile->crc32, temp_buffer, size) == FALSE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* check file CRC */
|
|
crc = 0;
|
|
crc = crc32(crc, temp_buffer, size);
|
|
if (crc != romfile->crc32)
|
|
{
|
|
message(0, "File %s has wrong CRC: %08X (should be %08X)\n", romfile->name, crc, romfile->crc32);
|
|
return FALSE;
|
|
}
|
|
|
|
/* interleave */
|
|
for (j = 0; j < romfile->size; j += 2)
|
|
{
|
|
buffer[offset + (j*interleave) + 0] = temp_buffer[j+0];
|
|
buffer[offset + (j*interleave) + 1] = temp_buffer[j+1];
|
|
}
|
|
|
|
SAFE_FREE(temp_buffer);
|
|
return TRUE;
|
|
}
|
|
|
|
static void load_romfiles(ROM_LIST *list)
|
|
{
|
|
int i;
|
|
|
|
// Load program ROMs
|
|
for (i=0; i < 4; i++)
|
|
{
|
|
if (list->prog[i].load)
|
|
{
|
|
if (load_rom(&crom[0x800000 - (list->prog[0].size * 4)], &list->prog[i], i*2, ITLV_4) == TRUE)
|
|
{
|
|
list->prog[i].load = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Load CROMs
|
|
for (i=0; i < 4; i++)
|
|
{
|
|
if (list->crom[0][i].load)
|
|
{
|
|
if (load_rom(crom0, &list->crom[0][i], i*2, ITLV_4) == TRUE) list->crom[0][i].load = 0;
|
|
}
|
|
}
|
|
for (i=0; i < 4; i++)
|
|
{
|
|
if (list->crom[1][i].load)
|
|
{
|
|
if (load_rom(crom1, &list->crom[1][i], i*2, ITLV_4) == TRUE) list->crom[1][i].load = 0;
|
|
}
|
|
}
|
|
for (i=0; i < 4; i++)
|
|
{
|
|
if (list->crom[2][i].load)
|
|
{
|
|
if (load_rom(crom2, &list->crom[2][i], i*2, ITLV_4) == TRUE) list->crom[2][i].load = 0;
|
|
}
|
|
}
|
|
for (i=0; i < 4; i++)
|
|
{
|
|
if (list->crom[3][i].load)
|
|
{
|
|
if (load_rom(crom3, &list->crom[3][i], i*2, ITLV_4) == TRUE) list->crom[3][i].load = 0;
|
|
}
|
|
}
|
|
|
|
// Load VROMs
|
|
for (i=0; i < 16; i++)
|
|
{
|
|
if (list->vrom[i].load)
|
|
{
|
|
if (load_rom(vrom, &list->vrom[i], i*2, ITLV_16) == TRUE)
|
|
{
|
|
list->vrom[i].load = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Load sound program
|
|
if (list->sprog.load)
|
|
{
|
|
if (load_rom(sprog, &list->sprog, 0, NO_ITLV) == TRUE)
|
|
{
|
|
list->sprog.load = 0;
|
|
}
|
|
}
|
|
|
|
// Load sound ROMs
|
|
for (i=0; i < 4; i++)
|
|
{
|
|
if (list->srom[i].load)
|
|
{
|
|
if (load_rom(&srom[i*0x400000], &list->srom[i], 0, NO_ITLV) == TRUE)
|
|
{
|
|
list->srom[i].load = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Load DSB program
|
|
if (list->dsbprog.load)
|
|
{
|
|
if (load_rom(dsbprog, &list->dsbprog, 0, NO_ITLV) == TRUE)
|
|
{
|
|
list->dsbprog.load = 0;
|
|
}
|
|
}
|
|
|
|
// Load DSB ROMs
|
|
for (i=0; i < 4; i++)
|
|
{
|
|
if (list->dsbrom[i].load)
|
|
{
|
|
if (load_rom(&dsbrom[i*0x400000], &list->dsbrom[i], 0, NO_ITLV) == TRUE)
|
|
{
|
|
list->dsbrom[i].load = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static BOOL load_romset(char *gamename, ROMSET *romset, int num_romsets)
|
|
{
|
|
int i, j;
|
|
ROMSET *game = NULL;
|
|
|
|
ROM_LIST list;
|
|
int romload_ok;
|
|
|
|
memset(&list, 0, sizeof(ROM_LIST));
|
|
|
|
// find the game
|
|
for (i=0; i < num_romsets; i++)
|
|
{
|
|
if (_stricmp(romset[i].game, gamename) == 0)
|
|
{
|
|
game = &romset[i];
|
|
break;
|
|
}
|
|
}
|
|
if (game == NULL)
|
|
{
|
|
message(0, "Game %s was not found in gamelist!\n", gamename);
|
|
return FALSE;
|
|
}
|
|
|
|
strcpy(m3_config.game_name, game->title);
|
|
message(0, "Loading \"%s\"\n", game->title);
|
|
|
|
for (i=0; i < 64; i++)
|
|
{
|
|
if (game->rom[i].size > 0)
|
|
{
|
|
switch (game->rom[i].type)
|
|
{
|
|
case ROMTYPE_PROG0: memcpy(&list.prog[3], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_PROG1: memcpy(&list.prog[2], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_PROG2: memcpy(&list.prog[1], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_PROG3: memcpy(&list.prog[0], &game->rom[i], sizeof(ROMFILE)); break;
|
|
|
|
case ROMTYPE_CROM00: memcpy(&list.crom[0][3], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_CROM01: memcpy(&list.crom[0][2], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_CROM02: memcpy(&list.crom[0][1], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_CROM03: memcpy(&list.crom[0][0], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_CROM10: memcpy(&list.crom[1][3], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_CROM11: memcpy(&list.crom[1][2], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_CROM12: memcpy(&list.crom[1][1], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_CROM13: memcpy(&list.crom[1][0], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_CROM20: memcpy(&list.crom[2][3], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_CROM21: memcpy(&list.crom[2][2], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_CROM22: memcpy(&list.crom[2][1], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_CROM23: memcpy(&list.crom[2][0], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_CROM30: memcpy(&list.crom[3][3], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_CROM31: memcpy(&list.crom[3][2], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_CROM32: memcpy(&list.crom[3][1], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_CROM33: memcpy(&list.crom[3][0], &game->rom[i], sizeof(ROMFILE)); break;
|
|
|
|
case ROMTYPE_VROM00: memcpy(&list.vrom[ 1], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_VROM01: memcpy(&list.vrom[ 0], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_VROM02: memcpy(&list.vrom[ 3], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_VROM03: memcpy(&list.vrom[ 2], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_VROM04: memcpy(&list.vrom[ 5], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_VROM05: memcpy(&list.vrom[ 4], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_VROM06: memcpy(&list.vrom[ 7], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_VROM07: memcpy(&list.vrom[ 6], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_VROM10: memcpy(&list.vrom[ 9], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_VROM11: memcpy(&list.vrom[ 8], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_VROM12: memcpy(&list.vrom[11], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_VROM13: memcpy(&list.vrom[10], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_VROM14: memcpy(&list.vrom[13], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_VROM15: memcpy(&list.vrom[12], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_VROM16: memcpy(&list.vrom[15], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_VROM17: memcpy(&list.vrom[14], &game->rom[i], sizeof(ROMFILE)); break;
|
|
|
|
case ROMTYPE_SPROG: memcpy(&list.sprog, &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_SROM0: memcpy(&list.srom[0], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_SROM1: memcpy(&list.srom[1], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_SROM2: memcpy(&list.srom[2], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_SROM3: memcpy(&list.srom[3], &game->rom[i], sizeof(ROMFILE)); break;
|
|
|
|
case ROMTYPE_DSBPROG: memcpy(&list.dsbprog, &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_DSBROM0: memcpy(&list.dsbrom[0], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_DSBROM1: memcpy(&list.dsbrom[1], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_DSBROM2: memcpy(&list.dsbrom[2], &game->rom[i], sizeof(ROMFILE)); break;
|
|
case ROMTYPE_DSBROM3: memcpy(&list.dsbrom[3], &game->rom[i], sizeof(ROMFILE)); break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (game->cromsize > 0)
|
|
{
|
|
// if CROM banksize = 64MB
|
|
crom0 = &crom[0x00800000];
|
|
crom1 = &crom[0x02800000];
|
|
crom2 = &crom[0x04800000];
|
|
crom3 = &crom[0x06800000];
|
|
}
|
|
else
|
|
{
|
|
crom0 = &crom[0x00800000];
|
|
crom1 = &crom[0x01800000];
|
|
crom2 = &crom[0x02800000];
|
|
crom3 = &crom[0x03800000];
|
|
}
|
|
|
|
/* load ROM files into memory */
|
|
|
|
// mark the files that need to be loaded
|
|
for (i=0; i < 4; i++)
|
|
{
|
|
if (list.prog[i].size > 0) list.prog[i].load = 1;
|
|
if (list.crom[0][i].size > 0) list.crom[0][i].load = 1;
|
|
if (list.crom[1][i].size > 0) list.crom[1][i].load = 1;
|
|
if (list.crom[2][i].size > 0) list.crom[2][i].load = 1;
|
|
if (list.crom[3][i].size > 0) list.crom[3][i].load = 1;
|
|
if (list.vrom[ 0+i].size > 0) list.vrom[ 0+i].load = 1;
|
|
if (list.vrom[ 4+i].size > 0) list.vrom[ 4+i].load = 1;
|
|
if (list.vrom[ 8+i].size > 0) list.vrom[ 8+i].load = 1;
|
|
if (list.vrom[12+i].size > 0) list.vrom[12+i].load = 1;
|
|
if (list.sprog.size > 0) list.sprog.load = 1;
|
|
if (list.srom[i].size > 0) list.srom[i].load = 1;
|
|
if (list.dsbprog.size > 0) list.dsbprog.load = 1;
|
|
if (list.dsbrom[i].size > 0) list.dsbrom[i].load = 1;
|
|
}
|
|
|
|
if (set_directory_zip("%s/%s.zip", m3_config.rom_path, game->game) == FALSE)
|
|
{
|
|
message(0, "Could not open zip file %s/%s.zip", m3_config.rom_path, game->game);
|
|
return FALSE;
|
|
}
|
|
|
|
load_romfiles(&list);
|
|
|
|
if (strlen(game->parent) > 0)
|
|
{
|
|
if (set_directory_zip("%s/%s.zip", m3_config.rom_path, game->parent) == FALSE)
|
|
{
|
|
message(0, "Could not open zip file %s/%s.zip", m3_config.rom_path, game->parent);
|
|
return FALSE;
|
|
}
|
|
|
|
load_romfiles(&list);
|
|
}
|
|
|
|
// check if everything was loaded
|
|
romload_ok = 1;
|
|
for (i=0; i < 4; i++)
|
|
{
|
|
if (list.prog[i].load)
|
|
{
|
|
romload_ok = 0;
|
|
message(0, "File %s was not found", list.prog[i].name);
|
|
}
|
|
}
|
|
for (j=0; j < 4; j++)
|
|
{
|
|
for (i=0; i < 4; i++)
|
|
{
|
|
if (list.crom[j][i].load)
|
|
{
|
|
romload_ok = 0;
|
|
message(0, "File %s was not found", list.crom[j][i].name);
|
|
}
|
|
}
|
|
}
|
|
for (i=0; i < 16; i++)
|
|
{
|
|
if (list.vrom[i].load)
|
|
{
|
|
romload_ok = 0;
|
|
message(0, "File %s was not found", list.vrom[i].name);
|
|
}
|
|
}
|
|
if (list.sprog.load)
|
|
{
|
|
romload_ok = 0;
|
|
message(0, "File %s was not found", list.sprog.name);
|
|
}
|
|
for (i=0; i < 4; i++)
|
|
{
|
|
if (list.srom[i].load)
|
|
{
|
|
romload_ok = 0;
|
|
message(0, "File %s was not found", list.srom[i].name);
|
|
}
|
|
}
|
|
if (list.dsbprog.load)
|
|
{
|
|
romload_ok = 0;
|
|
message(0, "File %s was not found", list.dsbprog.name);
|
|
}
|
|
for (i=0; i < 4; i++)
|
|
{
|
|
if (list.dsbrom[i].load)
|
|
{
|
|
romload_ok = 0;
|
|
message(0, "File %s was not found", list.dsbrom[i].name);
|
|
}
|
|
}
|
|
|
|
if (!romload_ok)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/* byteswap buffers */
|
|
byteswap(&crom[8*1024*1024 - (list.prog[0].size * 4)], list.prog[0].size * 4);
|
|
byteswap(crom0, list.crom[0][0].size * 4);
|
|
byteswap(crom1, list.crom[1][0].size * 4);
|
|
byteswap(crom2, list.crom[2][0].size * 4);
|
|
byteswap(crom3, list.crom[3][0].size * 4);
|
|
byteswap(vrom, list.vrom[0].size * 16);
|
|
byteswap32(vrom, list.vrom[0].size * 16);
|
|
|
|
/* set stepping */
|
|
|
|
m3_config.step = game->step;
|
|
|
|
/* set bridge controller */
|
|
|
|
if (game->bridge == 0)
|
|
game->bridge = (game->step < 0x20) ? 1 : 2;
|
|
|
|
m3_config.bridge = game->bridge;
|
|
|
|
// copy controls
|
|
memcpy(&m3_config.controls, &game->controls, sizeof(GAME_CONTROLS));
|
|
|
|
/* mirror CROM0 to CROM if needed */
|
|
|
|
if ((list.prog[0].size * 4) < 8*1024*1024)
|
|
{
|
|
memcpy(crom, crom0, 8*1024*1024 - list.prog[0].size*4);
|
|
}
|
|
|
|
if (game->cromsize < 1)
|
|
{
|
|
memcpy(&crom[0x4800000], crom0, 0x1000000);
|
|
memcpy(&crom[0x5800000], crom1, 0x1000000);
|
|
memcpy(&crom[0x6800000], crom2, 0x1000000);
|
|
memcpy(&crom[0x7800000], crom3, 0x1000000);
|
|
}
|
|
|
|
/*
|
|
* Perhaps mirroring must occur between the CROMx
|
|
* and the CROMx space, if CROMx is < 16MB?
|
|
*/
|
|
|
|
/* mirror lower VROM if necessary */
|
|
|
|
if ((list.vrom[0].size * 16) < 64*1024*1024)
|
|
{
|
|
memcpy(&vrom[list.vrom[0].size * 16], vrom, 64*1024*1024 - list.vrom[0].size * 16);
|
|
}
|
|
|
|
/* if we're here, everything went fine! */
|
|
|
|
message(0, "ROM loaded successfully!");
|
|
|
|
/*
|
|
* Debug ROM dump
|
|
*/
|
|
|
|
// save_file("crom.bin", &crom[0x800000 - romset.crom[0].size * 4], romset.crom[0].size * 4, 0);
|
|
// save_file("crom.bin", crom, 8*1024*1024, 0);
|
|
// save_file("vrom.bin", vrom, 64*1024*1024, 0);
|
|
// save_file("crom0.bin", crom0, 16*1024*1024, 0);
|
|
// save_file("crom1.bin", crom1, 16*1024*1024, 0);
|
|
// save_file("crom2.bin", crom2, 16*1024*1024, 0);
|
|
|
|
/*
|
|
* Apply the patches
|
|
*/
|
|
|
|
for( i=0; i < game->num_patches; i++ )
|
|
{
|
|
switch (game->patch[i].size)
|
|
{
|
|
case 8:
|
|
crom[game->patch[i].address] = BSWAP32(game->patch[i].data);
|
|
break;
|
|
case 16:
|
|
*(UINT16*) &crom[game->patch[i].address] = BSWAP16((UINT16) game->patch[i].data);
|
|
break;
|
|
case 32:
|
|
*(UINT32*) &crom[game->patch[i].address] = BSWAP32(game->patch[i].data);
|
|
break;
|
|
default:
|
|
message(0, "internal error, line %d of %s: invalid patch size (%d)\n", __LINE__, __FILE__, game->patch[i].size);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/******************************************************************/
|
|
/* Machine Interface */
|
|
/******************************************************************/
|
|
|
|
void model3_shutdown(void)
|
|
{
|
|
/* save NVRAMs */
|
|
|
|
model3_save_eeprom();
|
|
model3_save_bram();
|
|
|
|
/* shutdown all the modules */
|
|
controls_shutdown();
|
|
// scsp_shutdown();
|
|
// if(m3_config.flags & GAME_OWN_DSB1) dsb_reset();
|
|
render_shutdown();
|
|
r3d_shutdown();
|
|
tilegen_shutdown();
|
|
dma_shutdown();
|
|
scsi_shutdown();
|
|
|
|
ppc_shutdown();
|
|
|
|
/* free any allocated buffer */
|
|
//save_file("rom", crom, 0x800000, 0);
|
|
//save_file("ram", ram, 8*1024*1024, 0);
|
|
//save_file("vram", vram, 1*1024*1024+2*65536, 0);
|
|
//save_file("8e000000", culling_ram_8e, 1*1024*1024, 0);
|
|
//save_file("8c000000", culling_ram_8c, 4*1024*1024, 0);
|
|
//save_file("98000000", polygon_ram, 2*1024*1024, 0);
|
|
//save_file("texture.bin", texture_ram, 2048*2048*2, 0);
|
|
|
|
SAFE_FREE(ram);
|
|
SAFE_FREE(vram);
|
|
SAFE_FREE(sram);
|
|
SAFE_FREE(bram);
|
|
SAFE_FREE(culling_ram_8e);
|
|
SAFE_FREE(culling_ram_8c);
|
|
SAFE_FREE(polygon_ram);
|
|
SAFE_FREE(texture_ram);
|
|
|
|
SAFE_FREE(crom);
|
|
SAFE_FREE(vrom);
|
|
|
|
#if PROFILE_MEMORY_OPERATIONS
|
|
print_memory_profile_stats();
|
|
#endif
|
|
}
|
|
|
|
static PPC_FETCH_REGION m3_ppc_fetch[3];
|
|
|
|
static ROMSET romset[256];
|
|
static int num_romsets = 0;
|
|
|
|
BOOL model3_load(void)
|
|
{
|
|
int i;
|
|
crom = (UINT8 *)malloc(136*1024*1024);
|
|
vrom = (UINT8 *)malloc(64*1024*1024);
|
|
sprog = (UINT8 *)malloc(0x80000);
|
|
srom = (UINT8 *)malloc(0x1000000);
|
|
dsbprog = (UINT8 *)malloc(0x80000);
|
|
dsbrom = (UINT8 *)malloc(0x1000000);
|
|
|
|
if ((crom == NULL) || (vrom == NULL) || (srom == NULL) || (dsbrom == NULL) ||
|
|
(sprog == NULL) || (dsbprog == NULL))
|
|
{
|
|
SAFE_FREE(crom);
|
|
SAFE_FREE(vrom);
|
|
|
|
message(0, "ERROR: Couldn't allocate RAM for ROMs");
|
|
return FALSE;
|
|
}
|
|
|
|
memset(crom, 0xFF, 136*1024*1024);
|
|
|
|
num_romsets = parse_romlist(m3_config.rom_list, romset);
|
|
if (num_romsets == 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (load_romset(m3_config.game_id, romset, num_romsets) == FALSE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL model3_init(void)
|
|
{
|
|
PPC_CONFIG ppc_config;
|
|
/* setup m3_config (which is already partially done in parse_command_line) */
|
|
|
|
m3_config.log_enabled = 1;
|
|
|
|
/* allocate memory */
|
|
|
|
ram = (UINT8 *) malloc(8*1024*1024);
|
|
vram = (UINT8 *) malloc(2*1024*1024);
|
|
sram = (UINT8 *) malloc(1*1024*1024);
|
|
bram = (UINT8 *) malloc(256*1024);
|
|
culling_ram_8e = (UINT8 *) malloc(1*1024*1024);
|
|
culling_ram_8c = (UINT8 *) malloc(4*1024*1024);
|
|
polygon_ram = (UINT8 *) malloc(2*1024*1024);
|
|
texture_ram = (UINT8 *) malloc(2048*2048*2);
|
|
|
|
if ((ram == NULL) || (vram == NULL) || (sram == NULL) || (bram == NULL) ||
|
|
(culling_ram_8e == NULL) || (culling_ram_8c == NULL) || (polygon_ram == NULL) ||
|
|
(texture_ram == NULL))
|
|
{
|
|
SAFE_FREE(ram);
|
|
SAFE_FREE(vram);
|
|
SAFE_FREE(sram);
|
|
SAFE_FREE(bram);
|
|
SAFE_FREE(culling_ram_8e);
|
|
SAFE_FREE(culling_ram_8c);
|
|
SAFE_FREE(polygon_ram);
|
|
SAFE_FREE(texture_ram);
|
|
|
|
message(0, "Out of memory!");
|
|
return FALSE;
|
|
}
|
|
|
|
/* attach m3_shutdown to atexit */
|
|
|
|
atexit(model3_shutdown);
|
|
|
|
/* setup the PPC */
|
|
|
|
if (m3_config.step >= 0x20)
|
|
{
|
|
ppc_config.pvr = PPC_MODEL_603R;
|
|
ppc_config.bus_frequency = BUS_FREQUENCY_66MHZ;
|
|
ppc_config.bus_frequency_multiplier = 0x25; // multiplier 2.5
|
|
}
|
|
else if (m3_config.step == 0x15)
|
|
{
|
|
ppc_config.pvr = PPC_MODEL_603E;
|
|
ppc_config.bus_frequency = BUS_FREQUENCY_66MHZ;
|
|
ppc_config.bus_frequency_multiplier = 0x15; // multiplier 1.5
|
|
}
|
|
else
|
|
{
|
|
ppc_config.pvr = PPC_MODEL_603E;
|
|
ppc_config.bus_frequency = BUS_FREQUENCY_66MHZ;
|
|
ppc_config.bus_frequency_multiplier = 0x10; // multiplier 1.0
|
|
}
|
|
|
|
ppc_init(&ppc_config);
|
|
|
|
m3_ppc_fetch[0].start = 0;
|
|
m3_ppc_fetch[0].end = 0x7FFFFF;
|
|
m3_ppc_fetch[0].ptr = (UINT32 *)ram;
|
|
|
|
m3_ppc_fetch[1].start = 0xFF800000;
|
|
m3_ppc_fetch[1].end = 0xFFFFFFFF;
|
|
m3_ppc_fetch[1].ptr = (UINT32 *)crom;
|
|
|
|
m3_ppc_fetch[2].start = 0;
|
|
m3_ppc_fetch[2].end = 0;
|
|
m3_ppc_fetch[2].ptr = (UINT32 *)NULL;
|
|
|
|
ppc_set_fetch(m3_ppc_fetch);
|
|
|
|
switch (m3_config.step) // set frequency
|
|
{
|
|
case 0x15: ppc_freq = 100000000; break; // Step 1.5 PPC @ 100MHz
|
|
case 0x20: ppc_freq = 166000000; break; // Step 2.0 PPC @ 166MHz
|
|
case 0x21: ppc_freq = 166000000; break; // Step 2.1 PPC @ 166MHz
|
|
default: // assume Step 1.0...
|
|
case 0x10: ppc_freq = 66000000; break; // Step 1.0 PPC @ 66MHz
|
|
}
|
|
|
|
//ppc_freq = 20000000;
|
|
|
|
/* setup the 68K */
|
|
|
|
/* A68K INIT! */
|
|
|
|
/* setup remaining peripherals -- renderer and sound output is */
|
|
/* already setup at this point. */
|
|
|
|
bridge_init(pci_command_callback);
|
|
scsi_init(m3_ppc_read_8, m3_ppc_read_16, m3_ppc_read_32, m3_ppc_write_8, m3_ppc_write_16, m3_ppc_write_32);
|
|
dma_init(m3_ppc_read_32, m3_ppc_write_32);
|
|
tilegen_init(vram);
|
|
r3d_init(culling_ram_8e, culling_ram_8c, polygon_ram, texture_ram, vrom);
|
|
render_init(culling_ram_8e, culling_ram_8c, polygon_ram, texture_ram, vrom);
|
|
|
|
#ifndef RENDERER_D3D
|
|
osd_renderer_set_memory(polygon_ram, texture_ram, vrom);
|
|
#endif
|
|
|
|
// scsp_init();
|
|
// if(m3_config.flags & GAME_OWN_DSB1) dsb_reset();
|
|
controls_init();
|
|
|
|
return TRUE;
|
|
}
|