2013-04-17 22:43:11 -04:00
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
2012-01-02 13:53:39 +11:00
2010-07-22 01:48:48 +00:00
# include "GeckoCode.h"
# include "Thread.h"
# include "HW/Memmap.h"
2010-07-29 10:21:48 +00:00
# include "ConfigManager.h"
2010-07-22 01:48:48 +00:00
# include "vector"
2011-12-31 15:18:48 +11:00
# include "PowerPC/PowerPC.h"
# include "CommonPaths.h"
2010-07-22 01:48:48 +00:00
namespace Gecko
{
enum
{
// Code Types
2013-04-16 23:14:36 -04:00
CODETYPE_WRITE_FILL = 0x0 ,
CODETYPE_IF = 0x1 ,
CODETYPE_BA_PO_OPS = 0x2 ,
CODETYPE_FLOW_CONTROL = 0x3 ,
CODETYPE_REGISTER_OPS = 0x4 ,
CODETYPE_SPECIAL_IF = 0x5 ,
2010-07-22 01:48:48 +00:00
CODETYPE_ASM_SWITCH_RANGE = 0x6 ,
2013-04-16 23:14:36 -04:00
CODETYPE_END_CODES = 0x7 ,
2010-07-22 01:48:48 +00:00
// Data Types
DATATYPE_8BIT = 0x0 ,
DATATYPE_16BIT = 0x1 ,
DATATYPE_32BIT = 0x2 ,
} ;
// globals
static u32 base_address = 0 ;
static u32 pointer_address = 0 ;
static u32 gecko_register [ 0x10 ] = { 0 } ;
static struct
{
u32 address ;
u32 number ;
} block [ 0x10 ] ;
// codes execute when counter is 0
static int code_execution_counter = 0 ;
2011-12-31 15:18:48 +11:00
// Track whether the code handler has been installed
static bool code_handler_installed = false ;
2010-07-22 01:48:48 +00:00
// the currently active codes
std : : vector < GeckoCode > active_codes ;
// return true if code execution is on
inline bool CodeExecution ( )
{
return ( 0 = = code_execution_counter ) ;
}
u32 GeckoCode : : Code : : GetAddress ( ) const
{
return gcaddress + ( use_po ? pointer_address : ( base_address & 0xFE000000 ) ) ;
}
2013-05-05 04:50:39 +02:00
// return true if a code exists
bool GeckoCode : : Exist ( u32 address , u32 data )
{
std : : vector < GeckoCode : : Code > : : const_iterator
codes_iter = codes . begin ( ) ,
codes_end = codes . end ( ) ;
for ( ; codes_iter ! = codes_end ; + + codes_iter )
{
if ( codes_iter - > address = = address & & codes_iter - > data = = data )
return true ;
}
return false ;
}
// return true if the code is identical
bool GeckoCode : : Compare ( GeckoCode compare ) const
{
if ( codes . size ( ) ! = compare . codes . size ( ) )
return false ;
2013-07-05 19:56:15 -05:00
unsigned int exist = 0 ;
2013-05-05 04:50:39 +02:00
std : : vector < GeckoCode : : Code > : : const_iterator
codes_iter = codes . begin ( ) ,
codes_end = codes . end ( ) ;
for ( ; codes_iter ! = codes_end ; + + codes_iter )
{
if ( compare . Exist ( codes_iter - > address , codes_iter - > data ) )
exist + + ;
}
return exist = = codes . size ( ) ;
}
2011-03-05 06:11:26 +00:00
static std : : mutex active_codes_lock ;
2010-07-22 01:48:48 +00:00
// currently running code
2010-07-23 05:22:12 +00:00
static GeckoCode : : Code * codes_start = NULL , * current_code = NULL ;
static const GeckoCode : : Code * codes_end = NULL ;
2010-07-22 01:48:48 +00:00
2010-09-26 06:31:45 +00:00
// asm codes used for CT6 CST1
static std : : map < u32 , std : : vector < u32 > > inserted_asm_codes ;
2010-07-22 01:48:48 +00:00
// Functions for each code type
bool RamWriteAndFill ( ) ;
bool RegularIf ( ) ;
bool BaPoOps ( ) ;
bool FlowControl ( ) ;
bool RegisterOps ( ) ;
bool SpecialIf ( ) ;
bool AsmSwitchRange ( ) ;
bool EndCodes ( ) ;
bool MathOperation ( u32 & ret , const u32 left , const u32 right , const u8 type ) ;
void SetActiveCodes ( const std : : vector < GeckoCode > & gcodes )
{
2011-03-05 06:11:26 +00:00
std : : lock_guard < std : : mutex > lk ( active_codes_lock ) ;
2010-07-22 01:48:48 +00:00
active_codes . clear ( ) ;
// add enabled codes
std : : vector < GeckoCode > : : const_iterator
gcodes_iter = gcodes . begin ( ) ,
gcodes_end = gcodes . end ( ) ;
for ( ; gcodes_iter ! = gcodes_end ; + + gcodes_iter )
2013-04-16 23:14:36 -04:00
{
2010-07-22 01:48:48 +00:00
if ( gcodes_iter - > enabled )
{
// TODO: apply modifiers
// TODO: don't need description or creator string, just takin up memory
active_codes . push_back ( * gcodes_iter ) ;
}
2013-04-16 23:14:36 -04:00
}
2010-07-22 01:48:48 +00:00
2010-09-26 06:31:45 +00:00
inserted_asm_codes . clear ( ) ;
2011-12-31 15:18:48 +11:00
code_handler_installed = false ;
}
bool InstallCodeHandler ( )
{
2012-01-02 15:14:30 +11:00
u32 codelist_location = 0x800028B8 ; // Debugger on location (0x800022A8 = Debugger off, using codehandleronly.bin)
2011-12-31 15:18:48 +11:00
std : : string data ;
std : : string _rCodeHandlerFilename = File : : GetSysDirectory ( ) + GECKO_CODE_HANDLER ;
if ( ! File : : ReadFileToString ( false , _rCodeHandlerFilename . c_str ( ) , data ) )
return false ;
// Install code handler
Memory : : WriteBigEData ( ( const u8 * ) data . data ( ) , 0x80001800 , data . length ( ) ) ;
// Turn off Pause on start
2012-01-02 15:14:30 +11:00
Memory : : Write_U32 ( 0 , 0x80002774 ) ;
2011-12-31 15:18:48 +11:00
// Write the Game ID into the location expected by WiiRD
Memory : : WriteBigEData ( Memory : : GetPointer ( 0x80000000 ) , 0x80001800 , 6 ) ;
// Create GCT in memory
2012-01-02 15:14:30 +11:00
Memory : : Write_U32 ( 0x00d0c0de , codelist_location ) ;
Memory : : Write_U32 ( 0x00d0c0de , codelist_location + 4 ) ;
2011-12-31 15:18:48 +11:00
std : : lock_guard < std : : mutex > lk ( active_codes_lock ) ;
int i = 0 ;
std : : vector < GeckoCode > : : iterator
gcodes_iter = active_codes . begin ( ) ,
gcodes_end = active_codes . end ( ) ;
for ( ; gcodes_iter ! = gcodes_end ; + + gcodes_iter )
{
if ( gcodes_iter - > enabled )
{
current_code = codes_start = & * gcodes_iter - > codes . begin ( ) ;
codes_end = & * gcodes_iter - > codes . end ( ) ;
for ( ; current_code < codes_end ; + + current_code )
{
const GeckoCode : : Code & code = * current_code ;
2012-01-02 15:14:30 +11:00
// Make sure we have enough memory to hold the code list
if ( ( codelist_location + 24 + i ) < 0x80003000 )
{
Memory : : Write_U32 ( code . address , codelist_location + 8 + i ) ;
Memory : : Write_U32 ( code . data , codelist_location + 12 + i ) ;
i + = 8 ;
}
2011-12-31 15:18:48 +11:00
}
}
}
2012-01-02 15:14:30 +11:00
Memory : : Write_U32 ( 0xff000000 , codelist_location + 8 + i ) ;
Memory : : Write_U32 ( 0x00000000 , codelist_location + 12 + i ) ;
2011-12-31 15:18:48 +11:00
// Turn on codes
Memory : : Write_U8 ( 1 , 0x80001807 ) ;
// Invalidate the icache
2013-01-29 23:24:51 -06:00
for ( unsigned int j = 0 ; j < data . length ( ) ; j + = 32 )
2011-12-31 15:18:48 +11:00
{
2013-01-29 23:24:51 -06:00
PowerPC : : ppcState . iCache . Invalidate ( 0x80001800 + j ) ;
2011-12-31 15:18:48 +11:00
}
return true ;
2010-07-22 01:48:48 +00:00
}
2010-07-23 05:22:12 +00:00
bool RunGeckoCode ( GeckoCode & gecko_code )
2010-07-22 01:48:48 +00:00
{
static bool ( * code_type_funcs [ ] ) ( void ) =
{ RamWriteAndFill , RegularIf , BaPoOps , FlowControl , RegisterOps , SpecialIf , AsmSwitchRange , EndCodes } ;
base_address = 0x80000000 ;
pointer_address = 0x80000000 ;
code_execution_counter = 0 ;
2010-07-23 05:22:12 +00:00
current_code = codes_start = & * gecko_code . codes . begin ( ) ;
codes_end = & * gecko_code . codes . end ( ) ;
for ( ; current_code < codes_end ; + + current_code )
2010-07-22 01:48:48 +00:00
{
2010-07-23 05:22:12 +00:00
const GeckoCode : : Code & code = * current_code ;
2010-07-22 01:48:48 +00:00
bool result = true ;
switch ( code . type )
{
// These codetypes run even if code_execution is off
case CODETYPE_IF :
case CODETYPE_FLOW_CONTROL :
case CODETYPE_SPECIAL_IF :
case CODETYPE_ASM_SWITCH_RANGE :
case CODETYPE_END_CODES :
result = code_type_funcs [ code . type ] ( ) ;
break ;
// The rest of the codetypes only run if code_execution is on
default :
if ( CodeExecution ( ) )
result = code_type_funcs [ code . type ] ( ) ;
break ;
}
// code failed
if ( false = = result )
{
2011-02-03 08:12:47 +00:00
// disable code to stop annoying error messages
gecko_code . enabled = false ;
2011-01-13 02:05:58 +00:00
PanicAlertT ( " GeckoCode failed to run (CT%i CST%i) (%s) "
2012-01-02 15:14:30 +11:00
" \n (either a bad code or the code type is not yet supported. Try using the native code handler by placing the codehandler.bin file into the Sys directory and restarting Dolphin.) "
2010-07-22 01:48:48 +00:00
, code . type , code . subtype , gecko_code . name . c_str ( ) ) ;
return false ;
}
}
return true ;
}
bool RunActiveCodes ( )
{
2011-03-05 06:11:26 +00:00
if ( SConfig : : GetInstance ( ) . m_LocalCoreStartupParameter . bEnableCheats )
2010-07-22 01:48:48 +00:00
{
2011-03-05 06:11:26 +00:00
std : : lock_guard < std : : mutex > lk ( active_codes_lock ) ;
std : : vector < GeckoCode > : : iterator
gcodes_iter = active_codes . begin ( ) ,
gcodes_end = active_codes . end ( ) ;
for ( ; gcodes_iter ! = gcodes_end ; + + gcodes_iter )
{
if ( gcodes_iter - > enabled )
RunGeckoCode ( * gcodes_iter ) ;
// we don't need to stop all codes if one fails, maybe
//if (false == RunGeckoCode(*gcodes_iter))
//return false;
}
2010-07-22 01:48:48 +00:00
}
return true ;
}
2011-12-31 15:18:48 +11:00
void RunCodeHandler ( )
{
2012-03-25 04:13:41 +00:00
if ( SConfig : : GetInstance ( ) . m_LocalCoreStartupParameter . bEnableCheats & & active_codes . size ( ) > 0 )
2011-12-31 15:18:48 +11:00
{
2012-03-13 23:41:40 +11:00
u8 * gameId = Memory : : GetPointer ( 0x80000000 ) ;
u8 * wiirdId = Memory : : GetPointer ( 0x80001800 ) ;
if ( ! code_handler_installed | | memcmp ( gameId , wiirdId , 6 ) )
2011-12-31 15:18:48 +11:00
code_handler_installed = InstallCodeHandler ( ) ;
if ( code_handler_installed )
{
if ( PC = = LR )
{
u32 oldLR = LR ;
PowerPC : : CoreMode oldMode = PowerPC : : GetMode ( ) ;
PC = 0x800018A8 ;
LR = 0 ;
// Execute the code handler in interpreter mode to track when it exits
PowerPC : : SetMode ( PowerPC : : MODE_INTERPRETER ) ;
while ( PC ! = 0 )
PowerPC : : SingleStep ( ) ;
PowerPC : : SetMode ( oldMode ) ;
PC = LR = oldLR ;
}
}
else
{
// Use the emulated code handler
Gecko : : RunActiveCodes ( ) ;
}
}
}
2013-04-16 23:14:36 -04:00
const std : : map < u32 , std : : vector < u32 > > & GetInsertedAsmCodes ( )
{
2010-09-26 06:31:45 +00:00
return inserted_asm_codes ;
}
2010-07-22 01:48:48 +00:00
// CT0: Direct ram write/fill
2010-07-23 05:22:12 +00:00
// COMPLETE, maybe
2010-07-22 01:48:48 +00:00
bool RamWriteAndFill ( )
{
2010-07-23 05:22:12 +00:00
const GeckoCode : : Code & code = * current_code ;
2010-07-22 01:48:48 +00:00
u32 new_addr = code . GetAddress ( ) ;
const u32 & data = code . data ;
2010-07-26 05:30:50 +00:00
u32 count = ( data > > 16 ) + 1 ; // note: +1
2010-07-22 01:48:48 +00:00
switch ( code . subtype )
{
// CST0: 8bits Write & Fill
2010-07-23 05:22:12 +00:00
case DATATYPE_8BIT :
2010-07-22 01:48:48 +00:00
while ( count - - )
{
2010-07-26 05:30:50 +00:00
Memory : : Write_U8 ( ( u8 ) data , new_addr ) ;
2010-07-22 01:48:48 +00:00
+ + new_addr ;
}
break ;
// CST1: 16bits Write & Fill
2010-07-23 05:22:12 +00:00
case DATATYPE_16BIT :
2010-07-22 01:48:48 +00:00
while ( count - - )
{
Memory : : Write_U16 ( ( u16 ) data , new_addr ) ;
new_addr + = 2 ;
}
break ;
// CST2: 32bits Write
2010-07-23 05:22:12 +00:00
case DATATYPE_32BIT :
2010-07-22 01:48:48 +00:00
Memory : : Write_U32 ( ( u32 ) data , new_addr ) ;
break ;
// CST3: String Code
case 0x3 :
2010-07-23 05:22:12 +00:00
count = code . data ; // count is different from the other subtypes
while ( count )
{
if ( codes_end = = + + current_code )
return false ;
// write bytes from address
int byte_num = 4 ;
while ( byte_num - - & & count )
{
Memory : : Write_U8 ( ( u8 ) ( current_code - > address > > byte_num * 8 ) , new_addr ) ;
+ + new_addr ;
- - count ;
}
// write bytes from data
byte_num = 4 ;
while ( byte_num - - & & count )
{
Memory : : Write_U8 ( ( u8 ) ( current_code - > data > > byte_num * 8 ) , new_addr ) ;
+ + new_addr ;
- - count ;
}
}
2010-07-22 01:48:48 +00:00
break ;
// CST4: Serial Code
case 0x4 :
{
2010-07-23 05:22:12 +00:00
if ( codes_end = = + + current_code )
return false ;
u32 new_data = data ; // starting value of data
const u8 data_type = current_code - > address > > 28 ;
const u32 data_inc = current_code - > data ; // amount to increment the data
const u16 addr_inc = ( u16 ) current_code - > address ; // amount to increment the address
2013-04-16 23:14:36 -04:00
count = ( ( current_code - > address > > 16 ) & 0xFFF ) + 1 ; // count is different from the other subtypes, note: +1
2010-07-23 05:22:12 +00:00
while ( count - - )
{
// switch inside the loop, :/ o well
switch ( data_type )
{
case DATATYPE_8BIT :
Memory : : Write_U8 ( ( u8 ) new_data , new_addr ) ;
new_data = ( u8 ) new_data + ( u8 ) data_inc ;
break ;
case DATATYPE_16BIT :
Memory : : Write_U16 ( ( u16 ) new_data , new_addr ) ;
new_data = ( u16 ) new_data + ( u16 ) data_inc ;
break ;
case DATATYPE_32BIT :
Memory : : Write_U32 ( ( u32 ) new_data , new_addr ) ;
new_data + = data_inc ;
break ;
// INVALID DATATYPE
default :
return false ;
break ;
}
new_addr + = addr_inc ;
}
2010-07-22 01:48:48 +00:00
}
break ;
// INVALID SUBTYPE
default :
return false ;
break ;
}
return true ;
}
// CT1 Regular If codes (16/32 bits)
// COMPLETE
bool RegularIf ( )
{
2010-07-23 05:22:12 +00:00
const GeckoCode : : Code & code = * current_code ;
2010-07-22 01:48:48 +00:00
const bool is_endif = ! ! ( code . address & 0x1 ) ;
// if code_execution is off and this is an endif, decrease the execution counter
if ( false = = CodeExecution ( ) )
{
if ( is_endif )
- - code_execution_counter ;
}
bool result = false ;
// if code_execution is on, execute the conditional
if ( CodeExecution ( ) )
{
const u32 new_addr = code . GetAddress ( ) & ~ 0x1 ;
const u32 & data = code . data ;
s32 read_value = 0 ;
s32 data_value = 0 ;
// 16bit vs 32bit
if ( code . subtype & 0x4 )
{
// 16bits
read_value = Memory : : Read_U16 ( new_addr ) & ~ ( data > > 16 ) ;
data_value = ( data & 0xFFFF ) ;
}
else
{
// 32bits
read_value = Memory : : Read_U32 ( new_addr ) ;
data_value = data ;
}
switch ( code . subtype & 0x3 )
{
// CST0 : 32bits (endif, then) If equal
// CST4 : 16bits (endif, then) If equal
case 0x0 :
result = ( read_value = = data_value ) ;
break ;
// CST1 : 32bits (endif, then) If not equal
// CST5 : 16bits (endif, then) If not equal
case 0x1 :
result = ( read_value ! = data_value ) ;
break ;
// CST2 : 32bits (endif, then) If greater
// CST6 : 16bits (endif, then) If greater
case 0x2 :
result = ( read_value > data_value ) ;
break ;
// CST3 : 32bits (endif, then) If lower
// CST7 : 16bits (endif, then) If lower
case 0x3 :
result = ( read_value < data_value ) ;
2013-04-16 23:14:36 -04:00
break ;
2010-07-22 01:48:48 +00:00
}
}
// if the conditional returned false, or it never ran because execution is off,
// increase the code execution counter
if ( false = = result )
+ + code_execution_counter ;
return true ;
}
// CT2 Base Address/Pointer Operations
// NOT COMPLETE, last 2 subtypes aren't done
bool BaPoOps ( )
{
2010-07-23 05:22:12 +00:00
const GeckoCode : : Code & code = * current_code ;
2010-07-22 01:48:48 +00:00
// base_address vs pointer (ba vs po)
u32 & change_address = ( code . subtype & 0x4 ) ? pointer_address : base_address ;
// 4STYZ00N XXXXXXXX
u32 new_data = code . data ;
// append grN
if ( code . z )
new_data + = gecko_register [ code . n ] ;
// append ba or po (depending on last nibble's first bit)
if ( code . y )
new_data + = ( code . use_po ? pointer_address : base_address ) ;
// append to current value (not used in all subtypes, T will be 0 in those)
if ( code . t )
new_data + = change_address ;
switch ( code . subtype & 0x3 )
{
// CST0 : Load into Base Address
// CST4 : Load into Pointer
case 0x0 :
change_address = Memory : : Read_U32 ( new_data ) ;
break ;
// CST1 : Set Base Address to
// CST5 : Set Pointer to
case 0x1 :
change_address = new_data ;
break ;
// CST2 : Save Base Address to
// CST6 : Save Pointer to
case 0x2 :
Memory : : Write_U32 ( change_address , new_data ) ;
break ;
// CST3 : Put next line of code location into the Base Address
// CST7 : Put grN's location into the Pointer
// conflicting documentation, one doc says CST7 is the same as CST3 but with po
case 0x3 :
// TODO:
return false ; //
break ;
}
return true ;
}
// CT3 Repeat/Goto/Gosub/Return
2010-07-23 05:22:12 +00:00
// COMPLETE, maybe
2010-07-22 01:48:48 +00:00
bool FlowControl ( )
{
2010-07-23 05:22:12 +00:00
const GeckoCode : : Code & code = * current_code ;
2010-07-22 01:48:48 +00:00
// only the return subtype runs when code execution is off
if ( false = = CodeExecution ( ) & & code . subtype ! = 0x2 )
return true ;
// not all of these are used in all subtypes
const u8 block_num = ( u8 ) ( code . data & 0xF ) ;
switch ( code . subtype )
{
// CST0 : Set Repeat
case 0x0 :
2010-07-23 05:22:12 +00:00
block [ block_num ] . number = code . address & 0xFFFF ;
block [ block_num ] . address = ( u32 ) ( current_code - codes_start + 1 ) ;
2010-07-22 01:48:48 +00:00
break ;
// CST1 : Execute Repeat
case 0x1 :
if ( block [ block_num ] . number )
{
- - block [ block_num ] . number ;
2010-07-23 05:22:12 +00:00
// needs -1 cause iterator gets ++ after code runs
current_code = codes_start + block [ block_num ] . address - 1 ;
2010-07-22 01:48:48 +00:00
}
break ;
// CST2 : Return
case 0x2 :
if ( ( ( code . address > > 20 ) & 0xF ) ^ ( u32 ) CodeExecution ( ) )
2010-07-23 05:22:12 +00:00
{
// needs -1 cause iterator gets ++ after code runs
current_code = codes_start + block [ block_num ] . address - 1 ;
}
2010-07-22 01:48:48 +00:00
break ;
// CST3 : Goto
case 0x3 :
2010-07-23 05:22:12 +00:00
if ( ( ( code . address > > 20 ) & 0xF ) ^ ( u32 ) CodeExecution ( ) )
{
GeckoCode : : Code * const target_code = current_code + ( s16 ) ( code . address & 0xFFFF ) ;
if ( target_code > = codes_start & & target_code < codes_end )
{
// needs -1 cause iterator gets ++ after code runs
current_code = target_code - 1 ;
}
else
2013-04-16 23:14:36 -04:00
{
2010-07-23 05:22:12 +00:00
return false ; // trying to GOTO to bad address
2013-04-16 23:14:36 -04:00
}
2010-07-23 05:22:12 +00:00
}
2010-07-22 01:48:48 +00:00
break ;
// CST4 : Gosub
case 0x4 :
2010-07-23 05:22:12 +00:00
if ( ( ( code . address > > 20 ) & 0xF ) ^ ( u32 ) CodeExecution ( ) )
{
GeckoCode : : Code * const target_code = current_code + ( s16 ) ( code . address & 0xFFFF ) ;
if ( target_code > = codes_start & & target_code < codes_end )
{
block [ block_num ] . address = u32 ( current_code - codes_start + 1 ) ;
// needs -1 cause iterator gets ++ after code runs
current_code = target_code - 1 ;
}
else
2013-04-16 23:14:36 -04:00
{
2010-07-23 05:22:12 +00:00
return false ; // trying to GOSUB to bad address
2013-04-16 23:14:36 -04:00
}
2010-07-23 05:22:12 +00:00
}
2010-07-22 01:48:48 +00:00
break ;
// INVALID SUBTYPE
default :
return false ;
break ;
}
return true ;
}
// CT4 Gecko Register Operations
2010-07-23 05:22:12 +00:00
// COMPLETE, maybe
2010-07-22 01:48:48 +00:00
bool RegisterOps ( )
{
2010-07-23 05:22:12 +00:00
const GeckoCode : : Code & code = * current_code ;
2010-07-22 01:48:48 +00:00
// 80TYZZZN XXXXXXXX
u32 new_data = code . data ;
// append ba or po (depending on last nibble's first bit)
if ( code . y )
new_data + = ( code . use_po ? pointer_address : base_address ) ;
u32 & geckreg = gecko_register [ code . n ] ;
switch ( code . subtype )
{
// CST0 : Set Gecko Register to
case 0x0 :
// append to or set register
geckreg = new_data + ( code . t ? geckreg : 0 ) ;
break ;
// CST1 : Load into Gecko Register
case 0x1 :
switch ( code . t )
{
// 8bit
case DATATYPE_8BIT :
geckreg = Memory : : Read_U8 ( new_data ) ;
break ;
// 16bit
case DATATYPE_16BIT :
geckreg = Memory : : Read_U16 ( new_data ) ;
break ;
// 32bit
case DATATYPE_32BIT :
geckreg = Memory : : Read_U32 ( new_data ) ;
break ;
// INVALID DATATYPE
default :
return false ;
break ;
}
break ;
// CST2 : Save Gecko Register to
case 0x2 :
switch ( code . t )
{
// 8bit
case DATATYPE_8BIT :
for ( u16 i = 0 ; i < = code . z ; + + i )
Memory : : Write_U8 ( ( u8 ) geckreg , new_data + i ) ;
break ;
// 16bit
case DATATYPE_16BIT :
for ( u16 i = 0 ; i < = code . z ; + + i )
Memory : : Write_U16 ( ( u16 ) geckreg , new_data + ( i < < 1 ) ) ;
break ;
// 32bit
case DATATYPE_32BIT :
for ( u16 i = 0 ; i < = code . z ; + + i )
Memory : : Write_U32 ( ( u32 ) geckreg , new_data + ( i < < 2 ) ) ;
break ;
// INVALID DATATYPE
default :
return false ;
break ;
}
break ;
// CST3 : Gecko Register / Direct Value operations
// CST4 : Gecko Registers operations
case 0x3 :
case 0x4 :
{
// subtype 3 uses value in .data subtype 4 uses register
u32 right_val = ( code . subtype & 0x1 ? code . data : gecko_register [ code . data & 0xF ] ) ;
if ( code . y & 0x2 )
right_val = Memory : : Read_U32 ( right_val ) ;
u32 left_val = geckreg ;
if ( code . y & 0x1 )
left_val = Memory : : Read_U32 ( left_val ) ;
if ( false = = MathOperation ( geckreg , left_val , right_val , code . t ) )
return false ;
}
break ;
// CST5 : Memory Copy 1
// CST6 : Memory Copy 2
2010-07-23 05:22:12 +00:00
case 0x5 :
2010-07-22 01:48:48 +00:00
case 0x6 :
2010-07-23 05:22:12 +00:00
{
const u8 src_gr = ( code . z & 0xF ) ;
const u8 dst_gr = ( code . n ) ;
u16 count = ( u16 ) ( code . address > > 8 ) ;
// docs don't specify allowing 0xF for source and dest, but it can't hurt
u32 src_addr = ( ( 0xF = = src_gr ) ? ( code . use_po ? pointer_address : base_address ) : gecko_register [ src_gr ] ) ;
u32 dst_addr = ( ( 0xF = = dst_gr ) ? ( code . use_po ? pointer_address : base_address ) : gecko_register [ dst_gr ] ) ;
if ( 0x5 = = code . subtype )
dst_addr + = new_data ;
else
src_addr + = new_data ;
while ( count - - )
Memory : : Write_U8 ( Memory : : Read_U8 ( src_addr + + ) , dst_addr + + ) ;
}
2010-07-22 01:48:48 +00:00
break ;
// INVALID SUBTYPE
default :
return false ;
break ;
}
return true ;
}
// NOT COMPLETE, at least one op needs to be coded/fixed
bool MathOperation ( u32 & ret , const u32 left , const u32 right , const u8 type )
{
// ? = T :
switch ( type )
{
// 0 : add (+)
case 0x0 :
ret = left + right ;
break ;
// 1 : mul (*)
case 0x1 :
ret = left * right ;
break ;
// 2 : or (|)
case 0x2 :
ret = left | right ;
break ;
// 3 : and (&)
case 0x3 :
ret = left & right ;
break ;
// 4 : xor (^)
case 0x4 :
ret = left ^ right ;
break ;
// 5 : slw (<<)
case 0x5 :
ret = left < < right ;
break ;
// 6 : srw (>>)
case 0x6 :
ret = left > > right ;
break ;
// TODO: this one good?
// 7 : rol (rotate left)
case 0x7 :
ret = ( left < < right ) | ( left > > ( 8 - right ) ) ;
break ;
// 8 : asr (arithmetic shift right)
case 0x8 :
2010-12-26 06:23:33 +00:00
ret = ( s32 ) left > > right ;
2010-07-22 01:48:48 +00:00
break ;
// TODO: these float ops good?
// A : fadds (single float add)
2011-04-12 01:06:45 +00:00
case 0x9 :
2010-07-22 01:48:48 +00:00
* ( float * ) & ret = * ( float * ) & left + * ( float * ) & right ;
break ;
// B : fmuls (single float mul)
2011-04-12 01:06:45 +00:00
case 0xA :
2010-07-22 01:48:48 +00:00
* ( float * ) & ret = * ( float * ) & left * * ( float * ) & right ;
break ;
// INVALID OPERATION TYPE
default :
return false ;
break ;
}
return true ;
}
// CT5: Special If codes (16bits)
2010-07-23 05:22:12 +00:00
// COMPLETE, maybe (ugly)
2010-07-22 01:48:48 +00:00
bool SpecialIf ( )
{
2010-07-23 05:22:12 +00:00
// counter can modify the code :/
GeckoCode : : Code & code = * current_code ;
2010-07-22 01:48:48 +00:00
const bool is_endif = ! ! ( code . address & 0x1 ) ;
// if code_execution is off and this is an endif, decrease the execution counter
if ( false = = CodeExecution ( ) )
{
if ( is_endif )
- - code_execution_counter ;
}
bool result = false ;
2010-07-23 05:22:12 +00:00
// TODO: should these be signed? probably?
s16 left_val = 0 , right_val = 0 ;
2010-07-22 01:48:48 +00:00
// if code_execution is on, execute the conditional
if ( CodeExecution ( ) )
{
const u32 addr = code . GetAddress ( ) & ~ 0x1 ;
const u32 & data = code . data ;
if ( code . subtype ^ 0x4 )
{
// CT5 Part1 : Unknown values comparison
2010-07-23 05:22:12 +00:00
// A-______ NM00YYYY
2010-07-22 01:48:48 +00:00
const u8 n = ( u8 ) ( data > > 28 ) ;
const u8 m = ( u8 ) ( ( data > > 24 ) & 0xF ) ;
const u16 y = ( u16 ) data ;
2010-07-23 05:22:12 +00:00
left_val = Memory : : Read_U16 ( ( ( 0xF = = n ) ? addr : gecko_register [ n ] ) & ~ y ) ;
right_val = Memory : : Read_U16 ( ( ( 0xF = = m ) ? addr : gecko_register [ m ] ) & ~ y ) ;
2010-07-22 01:48:48 +00:00
}
else
{
// CT5 Part2 : 16bits Counter check
2010-07-23 05:22:12 +00:00
// A-0ZZZZT MMMMXXXX
2010-07-22 01:48:48 +00:00
2010-07-23 05:22:12 +00:00
left_val = ( u16 ) ( data ) & ~ ( u16 ) ( data > > 16 ) ;
right_val = ( u16 ) ( addr > > 4 ) ;
}
2010-07-22 01:48:48 +00:00
2010-07-23 05:22:12 +00:00
switch ( code . subtype & 0x3 )
{
// CST0 : 16bits (endif, then) If equal
case 0x0 :
result = ( left_val = = right_val ) ;
break ;
2010-07-22 01:48:48 +00:00
2010-07-23 05:22:12 +00:00
// CST1 : 16bits (endif, then) If not equal
case 0x1 :
result = ( left_val ! = right_val ) ;
break ;
2010-07-22 01:48:48 +00:00
2010-07-23 05:22:12 +00:00
// CST2 : 16bits (endif, then) If greater
case 0x2 :
result = ( left_val > right_val ) ;
break ;
// CST3 : 16bits (endif, then) If lower
case 0x3 :
result = ( left_val < right_val ) ;
break ;
2010-07-22 01:48:48 +00:00
}
2010-07-23 05:22:12 +00:00
}
else if ( code . subtype & 0x4 )
{
// counters get reset if code execution is off
code . address & = ~ 0x000FFFF0 ;
2010-07-22 01:48:48 +00:00
}
// if the conditional returned false, or it never ran because execution is off, increase the code execution counter
if ( false = = result )
+ + code_execution_counter ;
2010-07-23 05:22:12 +00:00
else if ( code . subtype & 0x4 )
{
// counters gets advanced if condition was true
// right_val is the value of the counter
code . address & = ~ 0x000FFFF0 ;
code . address | = ( ( right_val + 1 ) < < 4 ) ;
}
2010-07-22 01:48:48 +00:00
return true ;
}
// CT6 ASM Codes, On/Off switch and Address Range Check
2010-07-23 05:22:12 +00:00
// NOT COMPLETE, asm stuff not started
2013-04-19 09:21:45 -04:00
// TODO: Fix the ugliness
2010-07-22 01:48:48 +00:00
bool AsmSwitchRange ( )
{
2010-07-23 05:22:12 +00:00
// the switch subtype modifies the code :/
GeckoCode : : Code & code = * current_code ;
2010-07-22 01:48:48 +00:00
// only run if code_execution is set or this code is a switch or rangecheck subtype
2013-04-19 09:21:45 -04:00
// the switch and rangecheck run if execution_counter is 1 (directly inside the failed if) if they are an endif
2010-07-22 01:48:48 +00:00
if ( false = = CodeExecution ( ) )
{
if ( code . subtype < 0x6 )
return true ;
2010-07-23 05:22:12 +00:00
else if ( 1 ! = code_execution_counter )
2010-07-22 01:48:48 +00:00
return true ;
}
2010-07-23 05:22:12 +00:00
u32 & data = code . data ;
2010-07-22 01:48:48 +00:00
switch ( code . subtype )
{
// CST0 : Execute following ASM Code
case 0x0 :
// TODO:
return false ; //
break ;
// CST1 : Insert ASM code in the game
case 0x1 :
2010-09-26 06:31:45 +00:00
{
const int number_of_codes = code . data ;
std : : vector < u32 > & asm_code = inserted_asm_codes [ code . GetAddress ( ) ] ;
if ( asm_code . empty ( ) ) {
for ( int index = 0 ; index < number_of_codes ; + + index ) {
// Reserved for padding
if ( current_code [ index + 1 ] . address ! = 0x60000000 ) {
asm_code . push_back ( current_code [ index + 1 ] . address ) ;
}
// Reserved for b instruction
if ( current_code [ index + 1 ] . data ! = 0x00000000 ) {
asm_code . push_back ( current_code [ index + 1 ] . data ) ;
}
}
}
// Though the next code starts at current_code+number_of_codes+1,
2013-04-19 09:21:45 -04:00
// we add only number_of_codes. It is because the for statement in
2010-09-26 06:31:45 +00:00
// RunGeckoCode() increments current_code.
current_code + = number_of_codes ;
break ;
}
2010-07-22 01:48:48 +00:00
// CST3 : Create a branch
case 0x3 :
2010-07-23 05:22:12 +00:00
// watever
//if (code.data)
2010-07-22 01:48:48 +00:00
return false ; //
break ;
// CST6 : On/Off switch
case 0x6 :
2010-07-23 05:22:12 +00:00
// in the 1st bit of code.data, i store if code execution was previously off
// in the 2nd bit of code.data, i store the switch's on/off state
if ( CodeExecution ( ) )
{
if ( data & 0x1 )
data ^ = 0x2 ; // if code exec was previously off, flip the switch
// mark code execution as previously on
data & = ~ 0x1 ;
}
else
{
// mark code execution as previously off
data | = 0x1 ;
}
// set code execution to the state of the switch
code_execution_counter = ! ( data & 0x2 ) ;
2010-07-22 01:48:48 +00:00
break ;
// CST7 : Address range check (If... code)
case 0x7 :
2010-07-23 05:22:12 +00:00
{
const bool is_endif = ! ! ( code . address & 0x1 ) ;
if ( is_endif )
- - code_execution_counter ;
if ( CodeExecution ( ) )
2010-07-22 01:48:48 +00:00
{
const u32 addr = ( code . use_po ? pointer_address : base_address ) ;
2010-07-23 05:22:12 +00:00
if ( addr < ( data & 0xFFFF0000 ) | | addr > ( data < < 16 ) )
+ + code_execution_counter ;
2010-07-22 01:48:48 +00:00
}
2010-07-23 05:22:12 +00:00
}
2010-07-22 01:48:48 +00:00
break ;
// INVALID SUBTYPE
default :
return false ;
break ;
}
return true ;
}
// CT7 End of codes, Endif (Else)
// COMPLETE, maybe
bool EndCodes ( )
{
2010-07-23 05:22:12 +00:00
const GeckoCode : : Code & code = * current_code ;
2010-07-22 01:48:48 +00:00
const u32 & data = code . data ;
const u32 x = ( data & 0xFFFF0000 ) ;
const u32 y = ( data < < 16 ) ;
// these 2 do not happen in the "CST7 : End of Code", but in that subtype they will be 0
if ( x )
base_address = x ;
if ( y )
pointer_address = y ;
switch ( code . subtype )
{
// CST0 : Full Terminator
case 0x0 :
// clears the code execution status
2010-07-23 05:22:12 +00:00
code_execution_counter = 0 ;
2010-07-22 01:48:48 +00:00
break ;
// CST1 : Endif (+else)
case 0x1 :
{
// apply endifs
const u8 v = ( u8 ) code . address ;
if ( code_execution_counter > = v )
code_execution_counter - = v ;
else
{
// too many endifs
2010-07-23 06:39:07 +00:00
// no it's not, i gotta fix my code execution on/off stuff
code_execution_counter = 0 ;
2010-07-22 01:48:48 +00:00
}
const bool is_else = ! ! ( code . address & 0x00100000 ) ;
// apply else
if ( is_else )
if ( code_execution_counter < = 1 )
code_execution_counter ^ = 1 ;
}
break ;
// CST7 : End of Code
case 0x7 :
// tell the code handler that there are no more codes in the code list
// TODO: should this always stop all codes, even if execution is off?
if ( CodeExecution ( ) )
code_execution_counter = - 1 ; // silly maybe
break ;
// INVALID SUBTYPE
default :
return false ;
break ;
}
return true ;
}
2010-07-22 03:29:35 +00:00
} // namespace Gecko