mirror of
https://github.com/DaedalusX64/daedalus.git
synced 2025-04-02 10:21:48 -04:00
245 lines
7.4 KiB
C++
245 lines
7.4 KiB
C++
/*
|
|
Copyright (C) 2009 StrmnNrmn
|
|
|
|
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 "stdafx.h"
|
|
#include "Microcode.h"
|
|
|
|
#include "Core/ROM.h"
|
|
#include "Core/Memory.h"
|
|
|
|
#include "Debug/DBGConsole.h"
|
|
|
|
// Limit cache ucode entries to 6
|
|
// In theory we should never reach this max
|
|
#define MAX_UCODE_CACHE_ENTRIES 6
|
|
|
|
|
|
//////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////
|
|
// uCode Config //
|
|
//////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////
|
|
|
|
// NoN No Near clipping
|
|
// Rej Reject polys with one or more points outside screenspace
|
|
|
|
//F3DEX: Extended fast3d. Vertex cache is 32, up to 18 DL links
|
|
//F3DLX: Compatible with F3DEX, GBI, but not sub-pixel accurate. Clipping can be explicitly enabled/disabled
|
|
//F3DLX.Rej: No clipping, rejection instead. Vertex cache is 64
|
|
//F3FLP.Rej: Like F3DLX.Rej. Vertex cache is 80
|
|
//L3DEX: Line processing, Vertex cache is 32.
|
|
|
|
|
|
//
|
|
// Used to keep track of used ucode entries
|
|
//
|
|
struct UcodeInfo
|
|
{
|
|
u32 ucode;
|
|
u32 index;
|
|
|
|
bool set;
|
|
};
|
|
|
|
static UcodeInfo gUcodeInfo[ MAX_UCODE_CACHE_ENTRIES ];
|
|
|
|
static bool GBIMicrocode_DetectVersionString( u32 data_base, u32 data_size, char * str, u32 str_len )
|
|
{
|
|
DAEDALUS_ASSERT( data_base < MAX_RAM_ADDRESS + 0x1000 ,"GBIMicrocode out of bound %08X", data_base );
|
|
|
|
const s8 * ram( g_ps8RamBase );
|
|
|
|
for ( u32 i {}; i+2 < data_size; i++ )
|
|
{
|
|
if ( ram[ (data_base + i+0) ^ 3 ] == 'R' &&
|
|
ram[ (data_base + i+1) ^ 3 ] == 'S' &&
|
|
ram[ (data_base + i+2) ^ 3 ] == 'P' )
|
|
{
|
|
char * p {str};
|
|
char * e {str+str_len};
|
|
|
|
// Loop while we haven't filled our buffer, and there's space for our terminator
|
|
while (p+1 < e)
|
|
{
|
|
char c( ram[ (data_base + i) ^ U8_TWIDDLE ] );
|
|
if( c < ' ')
|
|
break;
|
|
|
|
*p++ = c;
|
|
++i;
|
|
}
|
|
*p++ = 0;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static u32 GBIMicrocode_MicrocodeHash(u32 code_base, u32 code_size)
|
|
{
|
|
// Needed for Conker's Bad Fur Day
|
|
if( code_size == 0 ) code_size = 0x1000;
|
|
|
|
const u8 * ram( g_pu8RamBase );
|
|
|
|
u32 hash {};
|
|
for (u32 i = 0; i < code_size; ++i)
|
|
{
|
|
hash = (hash << 4) + hash + ram[ (code_base+i) ^ U8_TWIDDLE ]; // Best hash ever!
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
void GBIMicrocode_Reset()
|
|
{
|
|
memset(&gUcodeInfo, 0, sizeof(gUcodeInfo));
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
struct MicrocodeData
|
|
{
|
|
u32 ucode;
|
|
u32 offset;
|
|
u32 hash;
|
|
};
|
|
|
|
static const MicrocodeData gMicrocodeData[] =
|
|
{
|
|
//
|
|
// The only games that need defining are custom ucodes and incorrectly detected ones
|
|
// If you believe a title should be here post the line for it from ucodes.txt @ http://www.daedalusx64.com
|
|
// Note - Games are in alphabetical order by game title
|
|
//
|
|
{ GBI_CONKER, GBI_2, 0x60256efc }, //"RSP Gfx ucode F3DEXBG.NoN fifo 2.08 Yoshitaka Yasumoto 1999 Nintendo.", "Conker's Bad Fur Day"},
|
|
{ GBI_LL, GBI_1, 0x6d8bec3e }, //"", "Dark Rift"},
|
|
{ GBI_DKR, GBI_0, 0x0c10181a }, //"", "Diddy Kong Racing (v1.0)"},
|
|
{ GBI_DKR, GBI_0, 0x713311dc }, //"", "Diddy Kong Racing (v1.1)"},
|
|
{ GBI_GE, GBI_0, 0x23f92542 }, //"RSP SW Version: 2.0G, 09-30-96", "GoldenEye 007"},
|
|
{ GBI_DKR, GBI_0, 0x169dcc9d }, //"", "Jet Force Gemini"},
|
|
{ GBI_LL, GBI_1, 0x26da8a4c }, //"", "Last Legion UX"},
|
|
{ GBI_PD, GBI_0, 0xcac47dc4 }, //"", "Perfect Dark (v1.1)"},
|
|
{ GBI_SE, GBI_0, 0x6cbb521d }, //"RSP SW Version: 2.0D, 04-01-96", "Star Wars - Shadows of the Empire (v1.0)"},
|
|
{ GBI_LL, GBI_1, 0xdd560323 }, //"", "Toukon Road - Brave Spirits"},
|
|
{ GBI_WR, GBI_0, 0x64cc729d }, //"RSP SW Version: 2.0D, 04-01-96", "Wave Race 64"},
|
|
};
|
|
|
|
u32 GBIMicrocode_DetectVersion( u32 code_base, u32 code_size, u32 data_base, u32 data_size, CustomMicrocodeCallback custom_callback )
|
|
{
|
|
// I think only checking code_base should be enough..
|
|
u32 idx {code_base + data_base};
|
|
|
|
// Cheap way to cache ucodes, don't check for strings (too slow!) but check last used ucode entries which is alot faster than string comparison.
|
|
// This only needed for GBI1/2/SDEX ucodes that use LoadUcode, else we only check when code_base changes, which usually never happens
|
|
//
|
|
u32 i ;
|
|
for( i = 0; i < MAX_UCODE_CACHE_ENTRIES; i++ )
|
|
{
|
|
const UcodeInfo &used( gUcodeInfo[ i ] );
|
|
|
|
// If this returns false, it means this entry is free to use
|
|
if( used.set == false )
|
|
break;
|
|
|
|
if( used.index == idx )
|
|
return used.ucode;
|
|
}
|
|
|
|
//
|
|
// Try to find the version string in the microcode data. This is faster than calculating a crc of the code
|
|
//
|
|
char str[256] = "";
|
|
GBIMicrocode_DetectVersionString( data_base, data_size, str, 256 );
|
|
|
|
// It wasn't the same as the last time around, we'll hash it and check if is a custom ucode.
|
|
//
|
|
u32 code_hash = GBIMicrocode_MicrocodeHash( code_base, code_size );
|
|
u32 ucode_version = GBI_0;
|
|
u32 ucode_offset = ~0;
|
|
|
|
for ( u32 i = 0; i < ARRAYSIZE(gMicrocodeData); i++ )
|
|
{
|
|
if ( code_hash == gMicrocodeData[i].hash )
|
|
{
|
|
//DBGConsole_Msg(0, "Ucode has been Detected in Array :[M\"%s\", Ucode %d]", str, gMicrocodeData[ i ].ucode);
|
|
ucode_version = gMicrocodeData[ i ].ucode;
|
|
ucode_offset = gMicrocodeData[ i ].offset;
|
|
}
|
|
}
|
|
|
|
if( ucode_version != GBI_0 )
|
|
{
|
|
// If this a custom ucode, let's build an array based from ucode_offset
|
|
custom_callback( ucode_version, ucode_offset );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If it wasn't a custom ucode
|
|
// See if we can identify it by string, if no match was found set default for Fast3D ucode
|
|
//
|
|
const char *ucodes[] { "F3", "L3", "S2DEX" };
|
|
char *match {};
|
|
|
|
for(u32 j {}; j<3;j++)
|
|
{
|
|
if( (match = strstr(str, ucodes[j])) )
|
|
break;
|
|
}
|
|
|
|
if( match )
|
|
{
|
|
if( strstr(match, "fifo") || strstr(match, "xbus") )
|
|
{
|
|
if( !strncmp(match, "S2DEX", 5) )
|
|
ucode_version = GBI_2_S2DEX;
|
|
else
|
|
ucode_version = GBI_2;
|
|
}
|
|
else
|
|
{
|
|
if( !strncmp(match, "S2DEX", 5) )
|
|
ucode_version = GBI_1_S2DEX;
|
|
else
|
|
ucode_version = GBI_1;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Retain used ucode info which will be cached
|
|
//
|
|
gUcodeInfo[ i ].index = idx;
|
|
gUcodeInfo[ i ].ucode = ucode_version;
|
|
gUcodeInfo[ i ].set = true;
|
|
|
|
DBGConsole_Msg(0,"Detected %s Ucode is: [M Ucode %d, 0x%08x, \"%s\", \"%s\"]",ucode_offset == u32(~0) ? "" :"Custom", ucode_version, code_hash, str, g_ROM.settings.GameName.c_str() );
|
|
// This is no longer needed as we now have an auto ucode detector, I'll leave it as reference ~Salvy
|
|
//
|
|
/*
|
|
FILE * fh = fopen( "ucodes.txt", "a" );
|
|
if ( fh )
|
|
{
|
|
fprintf( fh, "{ ucode=%d, 0x%08x, \"%s\", \"%s\"}, \n", ucode_version, code_hash, str, g_ROM.settings.GameName.c_str() );
|
|
fclose(fh);
|
|
}
|
|
*/
|
|
|
|
return ucode_version;
|
|
}
|