mirror of
https://github.com/ekeeke/Genesis-Plus-GX.git
synced 2024-11-09 04:15:14 +01:00
2c2f760f44
.improved hardware initialization (fixes Ultimate Mortal Kombat Trilogy) .improved soft-reset emulation .fixed some menu bugs on Gamecube
493 lines
14 KiB
C
493 lines
14 KiB
C
/***************************************************************************************
|
|
* Genesis Plus
|
|
* ROM Loading Support
|
|
*
|
|
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code)
|
|
* Eke-Eke (2007,2008,2009), additional code & fixes for the GCN/Wii port
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* 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; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
****************************************************************************************/
|
|
|
|
#include <ctype.h>
|
|
#include "shared.h"
|
|
|
|
/*** ROM Information ***/
|
|
#define ROMCONSOLE 256
|
|
#define ROMCOPYRIGHT 272
|
|
#define ROMDOMESTIC 288
|
|
#define ROMWORLD 336
|
|
#define ROMTYPE 384
|
|
#define ROMPRODUCT 386
|
|
#define ROMCHECKSUM 398
|
|
#define ROMIOSUPPORT 400
|
|
#define ROMROMSTART 416
|
|
#define ROMROMEND 420
|
|
#define ROMRAMINFO 424
|
|
#define ROMRAMSTART 436
|
|
#define ROMRAMEND 440
|
|
#define ROMMODEMINFO 444
|
|
#define ROMMEMO 456
|
|
#define ROMCOUNTRY 496
|
|
|
|
#define P3BUTTONS 1
|
|
#define P6BUTTONS 2
|
|
#define PKEYBOARD 4
|
|
#define PPRINTER 8
|
|
#define PBALL 16
|
|
#define PFLOPPY 32
|
|
#define PACTIVATOR 64
|
|
#define PTEAMPLAYER 128
|
|
#define PMSYSTEMPAD 256
|
|
#define PSERIAL 512
|
|
#define PTABLET 1024
|
|
#define PPADDLE 2048
|
|
#define PCDROM 4096
|
|
#define PMOUSE 8192
|
|
|
|
#define MAXCOMPANY 64
|
|
#define MAXPERIPHERALS 14
|
|
|
|
|
|
typedef struct
|
|
{
|
|
char companyid[6];
|
|
char company[26];
|
|
} COMPANYINFO;
|
|
|
|
typedef struct
|
|
{
|
|
char pID[2];
|
|
char pName[14];
|
|
} PERIPHERALINFO;
|
|
|
|
|
|
ROMINFO rominfo;
|
|
char rom_filename[256];
|
|
|
|
|
|
/***************************************************************************
|
|
* Genesis ROM Manufacturers
|
|
*
|
|
* Based on the document provided at
|
|
* http://www.zophar.net/tech/files/Genesis_ROM_Format.txt
|
|
**************************************************************************/
|
|
static COMPANYINFO companyinfo[MAXCOMPANY] = {
|
|
{"ACLD", "Ballistic"},
|
|
{"RSI", "Razorsoft"},
|
|
{"SEGA", "SEGA"},
|
|
{"TREC", "Treco"},
|
|
{"VRGN", "Virgin Games"},
|
|
{"WSTN", "Westone"},
|
|
{"10", "Takara"},
|
|
{"11", "Taito or Accolade"},
|
|
{"12", "Capcom"},
|
|
{"13", "Data East"},
|
|
{"14", "Namco or Tengen"},
|
|
{"15", "Sunsoft"},
|
|
{"16", "Bandai"},
|
|
{"17", "Dempa"},
|
|
{"18", "Technosoft"},
|
|
{"19", "Technosoft"},
|
|
{"20", "Asmik"},
|
|
{"22", "Micronet"},
|
|
{"23", "Vic Tokai"},
|
|
{"24", "American Sammy"},
|
|
{"29", "Kyugo"},
|
|
{"32", "Wolfteam"},
|
|
{"33", "Kaneko"},
|
|
{"35", "Toaplan"},
|
|
{"36", "Tecmo"},
|
|
{"40", "Toaplan"},
|
|
{"42", "UFL Company Limited"},
|
|
{"43", "Human"},
|
|
{"45", "Game Arts"},
|
|
{"47", "Sage's Creation"},
|
|
{"48", "Tengen"},
|
|
{"49", "Renovation or Telenet"},
|
|
{"50", "Electronic Arts"},
|
|
{"56", "Razorsoft"},
|
|
{"58", "Mentrix"},
|
|
{"60", "Victor Musical Ind."},
|
|
{"69", "Arena"},
|
|
{"70", "Virgin"},
|
|
{"73", "Soft Vision"},
|
|
{"74", "Palsoft"},
|
|
{"76", "Koei"},
|
|
{"79", "U.S. Gold"},
|
|
{"81", "Acclaim/Flying Edge"},
|
|
{"83", "Gametek"},
|
|
{"86", "Absolute"},
|
|
{"87", "Mindscape"},
|
|
{"93", "Sony"},
|
|
{"95", "Konami"},
|
|
{"97", "Tradewest"},
|
|
{"100", "T*HQ Software"},
|
|
{"101", "Tecmagik"},
|
|
{"112", "Designer Software"},
|
|
{"113", "Psygnosis"},
|
|
{"119", "Accolade"},
|
|
{"120", "Code Masters"},
|
|
{"125", "Interplay"},
|
|
{"130", "Activision"},
|
|
{"132", "Shiny & Playmates"},
|
|
{"144", "Atlus"},
|
|
{"151", "Infogrames"},
|
|
{"161", "Fox Interactive"},
|
|
{"177", "Ubisoft"},
|
|
{"239", "Disney Interactive"},
|
|
{"---", "Unknown"}
|
|
};
|
|
|
|
/***************************************************************************
|
|
* Genesis Peripheral Information
|
|
*
|
|
* Based on the document provided at
|
|
* http://www.zophar.net/tech/files/Genesis_ROM_Format.txt
|
|
***************************************************************************/
|
|
static PERIPHERALINFO peripheralinfo[MAXPERIPHERALS] = {
|
|
{"J", "3B Joypad"},
|
|
{"6", "6B Joypad"},
|
|
{"K", "Keyboard"},
|
|
{"P", "Printer"},
|
|
{"B", "Control Ball"},
|
|
{"F", "Floppy Drive"},
|
|
{"L", "Activator"},
|
|
{"4", "Team Player"},
|
|
{"0", "MS Joypad"},
|
|
{"R", "RS232C Serial"},
|
|
{"T", "Tablet"},
|
|
{"V", "Paddle"},
|
|
{"C", "CD-ROM"},
|
|
{"M", "Mega Mouse"},
|
|
};
|
|
|
|
/***************************************************************************
|
|
* GetRealChecksum
|
|
*
|
|
* Compute ROM checksum.
|
|
***************************************************************************/
|
|
static uint16 GetRealChecksum (uint8 *rom, int length)
|
|
{
|
|
int i;
|
|
uint16 checksum = 0;
|
|
|
|
for (i = 0; i < length; i += 2)
|
|
{
|
|
checksum += ((rom[i] << 8) + rom[i + 1]);
|
|
}
|
|
|
|
return checksum;
|
|
}
|
|
|
|
/***************************************************************************
|
|
* getrominfo
|
|
*
|
|
* Pass a pointer to the ROM base address.
|
|
***************************************************************************/
|
|
static void getrominfo (char *romheader)
|
|
{
|
|
int i,j;
|
|
|
|
memset (&rominfo, 0, sizeof (ROMINFO));
|
|
|
|
memcpy (&rominfo.consoletype, romheader + ROMCONSOLE, 16);
|
|
memcpy (&rominfo.copyright, romheader + ROMCOPYRIGHT, 16);
|
|
|
|
rominfo.domestic[0] = romheader[ROMDOMESTIC];
|
|
j=1;
|
|
for (i=1; i<48; i++)
|
|
{
|
|
if ((rominfo.domestic[j-1] != 32) || (romheader[ROMDOMESTIC + i] != 32))
|
|
{
|
|
rominfo.domestic[j] = romheader[ROMDOMESTIC + i];
|
|
j++;
|
|
}
|
|
}
|
|
rominfo.domestic[j] = 0;
|
|
|
|
rominfo.international[0] = romheader[ROMWORLD];
|
|
j=1;
|
|
for (i=1; i<48; i++)
|
|
{
|
|
if ((rominfo.international[j-1] != 32) || (romheader[ROMWORLD + i] != 32))
|
|
{
|
|
rominfo.international[j] = romheader[ROMWORLD + i];
|
|
j++;
|
|
}
|
|
}
|
|
rominfo.international[j] = 0;
|
|
|
|
memcpy (&rominfo.ROMType, romheader + ROMTYPE, 2);
|
|
memcpy (&rominfo.product, romheader + ROMPRODUCT, 12);
|
|
memcpy (&rominfo.checksum, romheader + ROMCHECKSUM, 2);
|
|
memcpy (&rominfo.romstart, romheader + ROMROMSTART, 4);
|
|
memcpy (&rominfo.romend, romheader + ROMROMEND, 4);
|
|
memcpy (&rominfo.country, romheader + ROMCOUNTRY, 16);
|
|
|
|
#ifdef LSB_FIRST
|
|
rominfo.checksum = (rominfo.checksum >> 8) | ((rominfo.checksum & 0xff) << 8);
|
|
#endif
|
|
rominfo.realchecksum = GetRealChecksum (((uint8 *) cart.rom) + 0x200, cart.romsize - 0x200);
|
|
|
|
rominfo.peripherals = 0;
|
|
for (i = 0; i < 14; i++)
|
|
for (j=0; j < 14; j++)
|
|
if (romheader[ROMIOSUPPORT+i] == peripheralinfo[j].pID[0])
|
|
rominfo.peripherals |= (1 << j);
|
|
}
|
|
|
|
/***************************************************************************
|
|
* deinterleave_block
|
|
*
|
|
* Convert interleaved (.smd) ROM files.
|
|
***************************************************************************/
|
|
static void deinterleave_block(uint8 * src)
|
|
{
|
|
int i;
|
|
uint8 block[0x4000];
|
|
memcpy (block, src, 0x4000);
|
|
for (i = 0; i < 0x2000; i += 1)
|
|
{
|
|
src[i * 2 + 0] = block[0x2000 + (i)];
|
|
src[i * 2 + 1] = block[0x0000 + (i)];
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
* load_rom
|
|
*
|
|
* Load a new ROM file.
|
|
***************************************************************************/
|
|
int load_rom(char *filename)
|
|
{
|
|
int i, size, offset = 0;
|
|
|
|
#ifdef NGC
|
|
size = cart.romsize;
|
|
sprintf(rom_filename,"%s",filename);
|
|
rom_filename[strlen(rom_filename) - 4] = 0;
|
|
#else
|
|
uint8 *ptr;
|
|
ptr = load_archive(filename, &size);
|
|
if(!ptr) return (0);
|
|
memcpy(cart.rom, ptr + offset, size);
|
|
free(ptr);
|
|
#endif
|
|
|
|
/* detect interleaved format (.SMD) */
|
|
if (strncmp((char *)(cart.rom + 0x100),"SEGA", 4) && ((size / 512) & 1))
|
|
{
|
|
size -= 512;
|
|
offset += 512;
|
|
for (i = 0; i < (size / 0x4000); i += 1)
|
|
deinterleave_block (cart.rom + offset + (i * 0x4000));
|
|
memcpy(cart.rom, cart.rom + offset, size);
|
|
}
|
|
|
|
/* max. 10 MBytes supported */
|
|
if (size > MAXROMSIZE) size = MAXROMSIZE;
|
|
cart.romsize = size;
|
|
|
|
/* clear unused ROM space */
|
|
memset(cart.rom + size, 0xff, MAXROMSIZE - size);
|
|
|
|
/* get infos from ROM header */
|
|
getrominfo((char *)cart.rom);
|
|
|
|
/* get specific input devices */
|
|
input_autodetect();
|
|
|
|
/* get default region */
|
|
region_autodetect();
|
|
|
|
#ifdef LSB_FIRST
|
|
/* Byteswap ROM */
|
|
uint8 temp;
|
|
for(i = 0; i < size; i += 2)
|
|
{
|
|
temp = cart.rom[i];
|
|
cart.rom[i] = cart.rom[i+1];
|
|
cart.rom[i+1] = temp;
|
|
}
|
|
#endif
|
|
|
|
/* byteswapped RADICA dumps (from Haze) */
|
|
if (((strstr(rominfo.product,"-K0101") != NULL) && (rominfo.checksum == 0xf424)) ||
|
|
((strstr(rominfo.product,"-K0109") != NULL) && (rominfo.checksum == 0x4f10)))
|
|
{
|
|
uint8 temp;
|
|
for(i = 0; i < size; i += 2)
|
|
{
|
|
temp = cart.rom[i];
|
|
cart.rom[i] = cart.rom[i+1];
|
|
cart.rom[i+1] = temp;
|
|
}
|
|
}
|
|
|
|
/* console hardware */
|
|
if (strstr(rominfo.consoletype, "SEGA PICO") != NULL)
|
|
system_hw = SYSTEM_PICO;
|
|
else if (strstr(rominfo.consoletype, "SEGA MEGADRIVE") != NULL)
|
|
system_hw = SYSTEM_MEGADRIVE;
|
|
else
|
|
system_hw = SYSTEM_GENESIS;
|
|
|
|
return(1);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* region_autodetect
|
|
*
|
|
* Set console region upon ROM header
|
|
*
|
|
****************************************************************************/
|
|
void region_autodetect(void)
|
|
{
|
|
/* country codes used to differentiate region */
|
|
/* 0001 = japan ntsc (1) */
|
|
/* 0010 = japan pal (2) */
|
|
/* 0100 = usa (4) */
|
|
/* 1000 = europe (8) */
|
|
|
|
int country = 0;
|
|
int i = 0;
|
|
char c;
|
|
|
|
/* reading header to find the country */
|
|
if (!strnicmp(rominfo.country, "eur", 3)) country |= 8;
|
|
else if (!strnicmp(rominfo.country, "usa", 3)) country |= 4;
|
|
else if (!strnicmp(rominfo.country, "jap", 3)) country |= 1;
|
|
|
|
else for(i = 0; i < 4; i++)
|
|
{
|
|
c = toupper((int)rominfo.country[i]);
|
|
if (c == 'U') country |= 4;
|
|
else if (c == 'J') country |= 1;
|
|
else if (c == 'E') country |= 8;
|
|
else if (c == 'K') country |= 1;
|
|
else if (c < 16) country |= c;
|
|
else if ((c >= '0') && (c <= '9')) country |= c - '0';
|
|
else if ((c >= 'A') && (c <= 'F')) country |= c - 'A' + 10;
|
|
}
|
|
|
|
/* automatic detection */
|
|
/* setting region */
|
|
/* this is used by IO register */
|
|
if (country & 4) region_code = REGION_USA;
|
|
else if (country & 1) region_code = REGION_JAPAN_NTSC;
|
|
else if (country & 8) region_code = REGION_EUROPE;
|
|
else if (country & 2) region_code = REGION_JAPAN_PAL;
|
|
else region_code = REGION_USA;
|
|
|
|
/* some games need specific REGION setting */
|
|
if (((strstr(rominfo.product,"T-45033") != NULL) && (rominfo.checksum == 0x0F81)) || /* Alisia Dragon (PAL) */
|
|
(strstr(rominfo.product,"T-69046-50") != NULL) || /* Back to the Future III (PAL) */
|
|
(strstr(rominfo.product,"T-120106-00") != NULL) || /* Brian Lara Cricket (PAL) */
|
|
(strstr(rominfo.product,"T-70096 -00") != NULL)) /* Muhammad Ali Heavyweight Boxing (PAL) */
|
|
{
|
|
/* need PAL settings */
|
|
region_code = REGION_EUROPE;
|
|
}
|
|
else if ((rominfo.realchecksum == 0x532e) && (strstr(rominfo.product,"1011-00") != NULL))
|
|
{
|
|
/* On Dal Jang Goon (Korea) needs JAP region code */
|
|
region_code = REGION_JAPAN_NTSC;
|
|
}
|
|
|
|
/* Force region setting */
|
|
if (config.region_detect == 1)
|
|
region_code = REGION_USA;
|
|
else if (config.region_detect == 2)
|
|
region_code = REGION_EUROPE;
|
|
else if (config.region_detect == 3)
|
|
region_code = REGION_JAPAN_NTSC;
|
|
else if (config.region_detect == 4)
|
|
region_code = REGION_JAPAN_PAL;
|
|
|
|
/* Set VDP default mode */
|
|
switch (region_code)
|
|
{
|
|
case REGION_EUROPE:
|
|
case REGION_JAPAN_PAL:
|
|
vdp_pal = 1;
|
|
break;
|
|
|
|
default:
|
|
vdp_pal = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* get_company
|
|
*
|
|
* Try to determine which company made this rom
|
|
*
|
|
* Ok, for some reason there's no standard for this.
|
|
* It seems that there can be pretty much anything you like following the
|
|
* copyright (C) symbol!
|
|
****************************************************************************/
|
|
char *get_company(void)
|
|
{
|
|
char *s;
|
|
int i;
|
|
char company[10];
|
|
|
|
for (i = 3; i < 8; i++)
|
|
company[i - 3] = rominfo.copyright[i];
|
|
company[5] = 0;
|
|
|
|
/** OK, first look for a hyphen
|
|
* Capcom use T-12 for example
|
|
*/
|
|
s = strstr (company, "-");
|
|
if (s != NULL)
|
|
{
|
|
s++;
|
|
strcpy (company, s);
|
|
}
|
|
|
|
/** Strip any trailing spaces **/
|
|
for (i = strlen (company) - 1; i >= 0; i--)
|
|
if (company[i] == 32)
|
|
company[i] = 0;
|
|
|
|
if (strlen (company) == 0)
|
|
return companyinfo[MAXCOMPANY - 1].company;
|
|
|
|
for (i = 0; i < MAXCOMPANY - 1; i++)
|
|
{
|
|
if (!(strncmp (company, companyinfo[i].companyid, strlen (company))))
|
|
return companyinfo[i].company;
|
|
}
|
|
|
|
return companyinfo[MAXCOMPANY - 1].company;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* get_peripheral
|
|
*
|
|
* Return peripheral name based on header code
|
|
*
|
|
****************************************************************************/
|
|
char *get_peripheral(int index)
|
|
{
|
|
if (index < MAXPERIPHERALS)
|
|
return peripheralinfo[index].pName;
|
|
return companyinfo[MAXCOMPANY - 1].company;
|
|
}
|
|
|