2009-02-17 15:02:16 +00:00
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers , whose names
* are too numerous to list here . Please refer to the COPYRIGHT
* file distributed with this source distribution .
*
* 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 . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*
* $ URL $
* $ Id $
*
*/
2009-02-15 06:10:59 +00:00
2009-02-20 20:11:12 +00:00
# include "common/debug.h"
2010-05-04 11:59:22 +00:00
# include "common/debug-channels.h"
2009-04-11 09:58:30 +00:00
# include "common/stack.h"
2010-01-21 22:20:16 +00:00
# include "common/config-manager.h"
2009-02-15 06:10:59 +00:00
2009-05-14 09:12:27 +00:00
# include "sci/sci.h"
2009-07-06 10:39:22 +00:00
# include "sci/console.h"
2009-05-15 14:07:45 +00:00
# include "sci/resource.h"
2010-02-13 17:44:58 +00:00
# include "sci/engine/features.h"
2009-02-27 02:23:40 +00:00
# include "sci/engine/state.h"
2009-02-24 05:51:55 +00:00
# include "sci/engine/kernel.h"
2010-01-29 11:05:06 +00:00
# include "sci/engine/script.h"
2010-06-10 09:18:57 +00:00
# include "sci/engine/seg_manager.h"
# include "sci/engine/selector.h" // for SELECTOR
2009-02-15 08:34:13 +00:00
# include "sci/engine/gc.h"
2009-02-15 06:10:59 +00:00
2009-02-21 10:23:36 +00:00
namespace Sci {
2009-10-02 11:04:36 +00:00
const reg_t NULL_REG = { 0 , 0 } ;
const reg_t SIGNAL_REG = { 0 , SIGNAL_OFFSET } ;
2010-07-01 22:44:36 +00:00
const reg_t TRUE_REG = { 0 , 1 } ;
2009-02-20 23:09:29 +00:00
//#define VM_DEBUG_SEND
2009-02-15 06:10:59 +00:00
2010-02-03 01:34:39 +00:00
# define SCI_XS_CALLEE_LOCALS ((SegmentId)-1)
2010-06-27 20:38:43 +00:00
# define END Script_None
opcode_format g_opcode_formats [ 128 ] [ 4 ] = {
/*00*/
{ Script_None } , { Script_None } , { Script_None } , { Script_None } ,
/*04*/
{ Script_None } , { Script_None } , { Script_None } , { Script_None } ,
/*08*/
{ Script_None } , { Script_None } , { Script_None } , { Script_None } ,
/*0C*/
{ Script_None } , { Script_None } , { Script_None } , { Script_None } ,
/*10*/
{ Script_None } , { Script_None } , { Script_None } , { Script_None } ,
/*14*/
{ Script_None } , { Script_None } , { Script_None } , { Script_SRelative , END } ,
/*18*/
{ Script_SRelative , END } , { Script_SRelative , END } , { Script_SVariable , END } , { Script_None } ,
/*1C*/
{ Script_SVariable , END } , { Script_None } , { Script_None } , { Script_Variable , END } ,
/*20*/
{ Script_SRelative , Script_Byte , END } , { Script_Variable , Script_Byte , END } , { Script_Variable , Script_Byte , END } , { Script_Variable , Script_SVariable , Script_Byte , END } ,
/*24 (24=ret)*/
{ Script_End } , { Script_Byte , END } , { Script_Invalid } , { Script_Invalid } ,
/*28*/
{ Script_Variable , END } , { Script_Invalid } , { Script_Byte , END } , { Script_Variable , Script_Byte , END } ,
/*2C*/
{ Script_SVariable , END } , { Script_SVariable , Script_Variable , END } , { Script_None } , { Script_Invalid } ,
/*30*/
{ Script_None } , { Script_Property , END } , { Script_Property , END } , { Script_Property , END } ,
/*34*/
{ Script_Property , END } , { Script_Property , END } , { Script_Property , END } , { Script_Property , END } ,
/*38*/
{ Script_Property , END } , { Script_SRelative , END } , { Script_SRelative , END } , { Script_None } ,
/*3C*/
{ Script_None } , { Script_None } , { Script_None } , { Script_Word } ,
/*40-4F*/
{ Script_Global , END } , { Script_Local , END } , { Script_Temp , END } , { Script_Param , END } ,
{ Script_Global , END } , { Script_Local , END } , { Script_Temp , END } , { Script_Param , END } ,
{ Script_Global , END } , { Script_Local , END } , { Script_Temp , END } , { Script_Param , END } ,
{ Script_Global , END } , { Script_Local , END } , { Script_Temp , END } , { Script_Param , END } ,
/*50-5F*/
{ Script_Global , END } , { Script_Local , END } , { Script_Temp , END } , { Script_Param , END } ,
{ Script_Global , END } , { Script_Local , END } , { Script_Temp , END } , { Script_Param , END } ,
{ Script_Global , END } , { Script_Local , END } , { Script_Temp , END } , { Script_Param , END } ,
{ Script_Global , END } , { Script_Local , END } , { Script_Temp , END } , { Script_Param , END } ,
/*60-6F*/
{ Script_Global , END } , { Script_Local , END } , { Script_Temp , END } , { Script_Param , END } ,
{ Script_Global , END } , { Script_Local , END } , { Script_Temp , END } , { Script_Param , END } ,
{ Script_Global , END } , { Script_Local , END } , { Script_Temp , END } , { Script_Param , END } ,
{ Script_Global , END } , { Script_Local , END } , { Script_Temp , END } , { Script_Param , END } ,
/*70-7F*/
{ Script_Global , END } , { Script_Local , END } , { Script_Temp , END } , { Script_Param , END } ,
{ Script_Global , END } , { Script_Local , END } , { Script_Temp , END } , { Script_Param , END } ,
{ Script_Global , END } , { Script_Local , END } , { Script_Temp , END } , { Script_Param , END } ,
{ Script_Global , END } , { Script_Local , END } , { Script_Temp , END } , { Script_Param , END }
} ;
# undef END
// TODO: script_adjust_opcode_formats should probably be part of the
// constructor (?) of a VirtualMachine or a ScriptManager class.
void script_adjust_opcode_formats ( ) {
if ( g_sci - > _features - > detectLofsType ( ) ! = SCI_VERSION_0_EARLY ) {
g_opcode_formats [ op_lofsa ] [ 0 ] = Script_Offset ;
g_opcode_formats [ op_lofss ] [ 0 ] = Script_Offset ;
}
# ifdef ENABLE_SCI32
// In SCI32, some arguments are now words instead of bytes
if ( getSciVersion ( ) > = SCI_VERSION_2 ) {
g_opcode_formats [ op_calle ] [ 2 ] = Script_Word ;
g_opcode_formats [ op_callk ] [ 1 ] = Script_Word ;
g_opcode_formats [ op_super ] [ 1 ] = Script_Word ;
g_opcode_formats [ op_send ] [ 0 ] = Script_Word ;
g_opcode_formats [ op_self ] [ 0 ] = Script_Word ;
g_opcode_formats [ op_call ] [ 1 ] = Script_Word ;
g_opcode_formats [ op_callb ] [ 1 ] = Script_Word ;
}
# endif
}
2010-02-03 01:34:39 +00:00
/**
* Adds an entry to the top of the execution stack .
*
* @ param [ in ] s The state with which to execute
* @ param [ in ] pc The initial program counter
* @ param [ in ] sp The initial stack pointer
* @ param [ in ] objp Pointer to the beginning of the current object
* @ param [ in ] argc Number of parameters to call with
* @ param [ in ] argp Heap pointer to the first parameter
* @ param [ in ] selector The selector by which it was called or
* NULL_SELECTOR if n . a . For debugging .
2010-06-25 22:34:53 +00:00
* @ param [ in ] exportId The exportId by which it was called or
* - 1 if n . a . For debugging .
2010-02-03 01:34:39 +00:00
* @ param [ in ] sendp Pointer to the object which the message was
* sent to . Equal to objp for anything but super .
* @ param [ in ] origin Number of the execution stack element this
* entry was created by ( usually the current TOS
* number , except for multiple sends ) .
* @ param [ in ] local_segment The segment to use for local variables ,
* or SCI_XS_CALLEE_LOCALS to use obj ' s segment .
* @ return A pointer to the new exec stack TOS entry
*/
static ExecStack * add_exec_stack_entry ( Common : : List < ExecStack > & execStack , reg_t pc , StackPtr sp ,
2010-06-26 08:29:55 +00:00
reg_t objp , int argc , StackPtr argp , Selector selector , int exportId , int localCallOffset ,
2010-02-03 01:34:39 +00:00
reg_t sendp , int origin , SegmentId local_segment ) ;
/**
* Adds one varselector access to the execution stack .
* This function is called from send_selector only .
* @ param [ in ] s The EngineState to use
* @ param [ in ] objp Pointer to the object owning the selector
* @ param [ in ] argc 1 for writing , 0 for reading
* @ param [ in ] argp Pointer to the address of the data to write - 2
* @ param [ in ] selector Selector name
* @ param [ in ] address Heap address of the selector
* @ param [ in ] origin Stack frame which the access originated from
* @ return Pointer to the new exec - TOS element
*/
static ExecStack * add_exec_stack_varselector ( Common : : List < ExecStack > & execStack , reg_t objp , int argc ,
StackPtr argp , Selector selector , const ObjVarRef & address ,
int origin ) ;
2009-02-20 23:09:29 +00:00
// validation functionality
2009-02-15 06:10:59 +00:00
2009-03-12 03:26:47 +00:00
static reg_t & validate_property ( Object * obj , int index ) {
2009-10-02 11:04:36 +00:00
// A static dummy reg_t, which we return if obj or index turn out to be
// invalid. Note that we cannot just return NULL_REG, because client code
2010-02-07 17:56:57 +00:00
// may modify the value of the returned reg_t.
2010-06-24 20:10:51 +00:00
static reg_t dummyReg = NULL_REG ;
2009-10-02 11:04:36 +00:00
2010-06-24 11:29:26 +00:00
// FIXME/TODO: Where does this occur? Returning a dummy reg here could lead
// to all sorts of issues! Turned it into an error for now...
// If this occurs, it means there's probably something wrong with the garbage
// collector, so don't hide it with fake return values
2009-02-15 22:28:12 +00:00
if ( ! obj ) {
2010-06-24 18:48:50 +00:00
error ( " validate_property: Sending to disposed object " ) ;
//return dummyReg;
2009-02-15 06:10:59 +00:00
}
2010-06-24 20:01:31 +00:00
if ( index < 0 | | ( uint ) index > = obj - > getVarCount ( ) ) {
// This is same way sierra does it and there are some games, that contain such scripts like
// iceman script 998 (fred::canBeHere, executed right at the start)
debugC ( 2 , kDebugLevelVM , " [VM] Invalid property #%d (out of [0..%d]) requested! " ,
index , obj - > getVarCount ( ) ) ;
return dummyReg ;
2009-02-15 06:10:59 +00:00
}
2010-05-26 16:30:10 +00:00
return obj - > getVariableRef ( index ) ;
2009-02-15 06:10:59 +00:00
}
2009-03-12 03:26:47 +00:00
static StackPtr validate_stack_addr ( EngineState * s , StackPtr sp ) {
2009-02-15 06:10:59 +00:00
if ( sp > = s - > stack_base & & sp < s - > stack_top )
return sp ;
2010-01-25 01:39:44 +00:00
error ( " [VM] Stack index %d out of valid range [%d..%d] " ,
2009-05-30 15:40:49 +00:00
( int ) ( sp - s - > stack_base ) , 0 , ( int ) ( s - > stack_top - s - > stack_base - 1 ) ) ;
2009-02-15 06:10:59 +00:00
return 0 ;
}
2009-03-12 03:26:47 +00:00
static int validate_arithmetic ( reg_t reg ) {
2009-02-15 06:10:59 +00:00
if ( reg . segment ) {
2010-06-18 14:48:39 +00:00
// The results of this are likely unpredictable... It most likely means that a kernel function is returning something wrong.
// If such an error occurs, we usually need to find the last kernel function called and check its return value. Check
// callKernelFunc() below
error ( " [VM] Attempt to read arithmetic value from non-zero segment [%04x]. Address: %04x:%04x " , reg . segment , PRINT_REG ( reg ) ) ;
2010-01-29 22:51:22 +00:00
return 0 ;
2009-02-15 06:10:59 +00:00
}
return reg . offset ;
}
2009-03-12 03:26:47 +00:00
static int signed_validate_arithmetic ( reg_t reg ) {
2010-06-18 14:48:39 +00:00
return ( int16 ) validate_arithmetic ( reg ) ;
2009-02-15 06:10:59 +00:00
}
2010-07-04 21:31:09 +00:00
static bool validate_variable ( reg_t * r , reg_t * stack_base , int type , int max , int index ) {
2009-02-15 06:10:59 +00:00
const char * names [ 4 ] = { " global " , " local " , " temp " , " param " } ;
if ( index < 0 | | index > = max ) {
2009-10-12 12:03:06 +00:00
Common : : String txt = Common : : String : : printf (
2010-01-25 01:39:44 +00:00
" [VM] Attempt to use invalid %s variable %04x " ,
2009-10-12 12:03:06 +00:00
names [ type ] , index ) ;
2009-02-15 06:10:59 +00:00
if ( max = = 0 )
2009-10-02 12:44:12 +00:00
txt + = " (variable type invalid) " ;
else
txt + = Common : : String : : printf ( " (out of range [%d..%d]) " , 0 , max - 1 ) ;
2009-06-03 09:45:16 +00:00
2009-02-15 06:10:59 +00:00
if ( type = = VAR_PARAM | | type = = VAR_TEMP ) {
int total_offset = r - stack_base ;
if ( total_offset < 0 | | total_offset > = VM_STACK_SIZE ) {
2010-06-17 23:50:28 +00:00
// Fatal, as the game is trying to do an OOB access
error ( " %s. [VM] Access would be outside even of the stack (%d); access denied " , txt . c_str ( ) , total_offset ) ;
2009-10-30 18:01:27 +00:00
return false ;
2009-02-15 06:10:59 +00:00
} else {
2010-06-21 15:51:04 +00:00
// WORKAROUND: Mixed-Up Mother Goose tries to use an invalid parameter in Event::new().
// Just skip around it here so we don't error out in validate_arithmetic.
2010-06-25 16:16:29 +00:00
if ( g_sci - > getGameId ( ) = = GID_MOTHERGOOSE & & getSciVersion ( ) < = SCI_VERSION_1_1 & & type = = VAR_PARAM & & index = = 1 )
2010-06-21 15:51:04 +00:00
return false ;
2010-01-30 11:59:05 +00:00
debugC ( 2 , kDebugLevelVM , " %s " , txt . c_str ( ) ) ;
debugC ( 2 , kDebugLevelVM , " [VM] Access within stack boundaries; access granted. " ) ;
2009-10-30 18:01:27 +00:00
return true ;
2009-02-15 06:10:59 +00:00
}
2009-06-03 09:45:16 +00:00
}
2009-10-30 18:01:27 +00:00
return false ;
2009-02-15 06:10:59 +00:00
}
2009-10-30 18:01:27 +00:00
return true ;
2009-02-15 06:10:59 +00:00
}
2010-06-30 09:47:04 +00:00
struct SciTrackOriginReply {
2010-06-24 17:48:15 +00:00
int scriptNr ;
2010-06-30 09:47:04 +00:00
Common : : String objectName ;
Common : : String methodName ;
2010-06-26 08:29:55 +00:00
int localCallOffset ;
2010-06-25 16:16:29 +00:00
} ;
2010-07-09 21:33:12 +00:00
static reg_t trackOriginAndFindWorkaround ( int index , const SciWorkaroundEntry * workaroundList , SciTrackOriginReply * trackOrigin ) {
2010-06-30 09:47:04 +00:00
EngineState * state = g_sci - > getEngineState ( ) ;
ExecStack * lastCall = state - > xs ;
Script * local_script = state - > _segMan - > getScriptIfLoaded ( lastCall - > local_segment ) ;
int curScriptNr = local_script - > getScriptNumber ( ) ;
if ( lastCall - > debugLocalCallOffset ! = - 1 ) {
// if lastcall was actually a local call search back for a real call
Common : : List < ExecStack > : : iterator callIterator = state - > _executionStack . end ( ) ;
while ( callIterator ! = state - > _executionStack . begin ( ) ) {
callIterator - - ;
ExecStack loopCall = * callIterator ;
if ( ( loopCall . debugSelector ! = - 1 ) | | ( loopCall . debugExportId ! = - 1 ) ) {
lastCall - > debugSelector = loopCall . debugSelector ;
lastCall - > debugExportId = loopCall . debugExportId ;
break ;
}
}
}
Common : : String curObjectName = state - > _segMan - > getObjectName ( lastCall - > sendp ) ;
Common : : String curMethodName ;
const SciGameId gameId = g_sci - > getGameId ( ) ;
if ( lastCall - > type = = EXEC_STACK_TYPE_CALL ) {
if ( lastCall - > debugSelector ! = - 1 ) {
curMethodName = g_sci - > getKernel ( ) - > getSelectorName ( lastCall - > debugSelector ) ;
} else if ( lastCall - > debugExportId ! = - 1 ) {
curObjectName = " " ;
curMethodName = curMethodName . printf ( " export %d " , lastCall - > debugExportId ) ;
}
}
if ( workaroundList ) {
// Search if there is a workaround for this one
const SciWorkaroundEntry * workaround ;
int16 inheritanceLevel = 0 ;
Common : : String searchObjectName = curObjectName ;
reg_t searchObject = lastCall - > sendp ;
do {
workaround = workaroundList ;
while ( workaround - > objectName ) {
2010-07-11 18:51:28 +00:00
if ( workaround - > gameId = = gameId & & workaround - > scriptNr = = curScriptNr
& & ( ( workaround - > inheritanceLevel = = - 1 ) | | ( workaround - > inheritanceLevel = = inheritanceLevel ) )
& & ( workaround - > objectName = = searchObjectName )
2010-06-30 09:47:04 +00:00
& & workaround - > methodName = = curMethodName & & workaround - > localCallOffset = = lastCall - > debugLocalCallOffset & & workaround - > index = = index ) {
// Workaround found
return workaround - > newValue ;
}
workaround + + ;
}
// Go back to the parent
inheritanceLevel + + ;
searchObject = state - > _segMan - > getObject ( searchObject ) - > getSuperClassSelector ( ) ;
if ( ! searchObject . isNull ( ) )
searchObjectName = state - > _segMan - > getObjectName ( searchObject ) ;
} while ( ! searchObject . isNull ( ) ) ; // no parent left?
}
// give caller origin data
trackOrigin - > objectName = curObjectName ;
trackOrigin - > methodName = curMethodName ;
trackOrigin - > scriptNr = curScriptNr ;
trackOrigin - > localCallOffset = lastCall - > debugLocalCallOffset ;
return make_reg ( 0xFFFF , 0xFFFF ) ;
}
// gameID, scriptNr,lvl, object-name, method-name, call, index, replace
static const SciWorkaroundEntry uninitializedReadWorkarounds [ ] = {
2010-07-12 08:59:43 +00:00
{ GID_FREDDYPHARKAS , 24 , 0 , " gcWin " , " open " , - 1 , 5 , { 0 , 0xf } } , // is used as priority for game menu
{ GID_FREDDYPHARKAS , 31 , 0 , " quitWin " , " open " , - 1 , 5 , { 0 , 0xf } } , // is used as priority for game menu
{ GID_GK2 , 11 , 0 , " " , " export 10 " , - 1 , 3 , { 0 , 0 } } , // called when the game starts
2010-07-13 20:42:42 +00:00
{ GID_ISLANDBRAIN , 140 , 0 , " piece " , " init " , - 1 , 3 , { 0 , 1 } } , // first puzzle right at the start, some initialization variable. bnt is done on it, and it should be non-0
{ GID_ISLANDBRAIN , 268 , 0 , " anElement " , " select " , - 1 , 0 , { 0 , 0 } } , // elements puzzle, gets used before super TextIcon
2010-07-12 08:59:43 +00:00
{ GID_JONES , 232 , 0 , " weekendText " , " draw " , 0x3d3 , 0 , { 0 , 0 } } , // jones/cd only - gets called during the game
{ GID_JONES , 255 , 0 , " " , " export 0 " , - 1 , 13 , { 0 , 0 } } , // jones/ega&vga only - called when the game starts
{ GID_JONES , 255 , 0 , " " , " export 0 " , - 1 , 14 , { 0 , 0 } } , // jones/ega&vga only - called when the game starts
{ GID_KQ5 , 0 , 0 , " " , " export 29 " , - 1 , 3 , { 0 , 0 } } , // called when playing harp for the harpies, is used for kDoAudio
{ GID_KQ5 , 25 , 0 , " rm025 " , " doit " , - 1 , 0 , { 0 , 0 } } , // inside witch forest, where the walking rock is
2010-07-13 20:42:42 +00:00
{ GID_KQ6 , 30 , 0 , " rats " , " changeState " , - 1 , 0 , { 0 , 0 } } , // rats in the catacombs
2010-07-12 22:26:48 +00:00
{ GID_KQ6 , 500 , 0 , " rm500 " , " init " , - 1 , 0 , { 0 , 0 } } , // going to island of the beast
{ GID_KQ6 , 520 , 0 , " rm520 " , " init " , - 1 , 0 , { 0 , 0 } } , // going to boiling water trap on beast isle
2010-07-13 20:42:42 +00:00
{ GID_KQ6 , 903 , 0 , " controlWin " , " open " , - 1 , 4 , { 0 , 0 } } , // when opening the controls window (save, load etc)
{ GID_LAURABOW2 , 24 , 0 , " gcWin " , " open " , - 1 , 5 , { 0 , 0xf } } , // is used as priority for game menu
{ GID_LSL1 , 720 , 0 , " rm720 " , " init " , - 1 , 0 , { 0 , 0 } } , // age check room
{ GID_LSL3 , 997 , 0 , " TheMenuBar " , " handleEvent " , - 1 , 1 , { 0 , 0xf } } , // when setting volume the first time, this temp is used to set volume on entry (normally it would have been initialized to 's')
2010-07-12 11:20:42 +00:00
{ GID_LSL6 , 85 , 0 , " washcloth " , " doVerb " , - 1 , 0 , { 0 , 0 } } , // washcloth in inventory
2010-07-13 20:42:42 +00:00
{ GID_LSL6 , 928 , - 1 , " Narrator " , " startText " , - 1 , 0 , { 0 , 0 } } , // used by various objects that are even translated in foreign versions, that's why we use the base-class
{ GID_QFG2 , 71 , 0 , " theInvSheet " , " doit " , - 1 , 1 , { 0 , 0 } } , // accessing the inventory
2010-07-12 08:59:43 +00:00
{ GID_SQ1 , 703 , 0 , " " , " export 1 " , - 1 , 0 , { 0 , 0 } } , // sub that's called from several objects while on sarien battle cruiser
{ GID_SQ1 , 703 , 0 , " firePulsar " , " changeState " , 0x18a , 0 , { 0 , 0 } } , // export 1, but called locally (when shooting at aliens)
{ GID_SQ4 , 928 , 0 , " Narrator " , " startText " , - 1 , 1000 , { 0 , 1 } } , // sq4cd: method returns this to the caller
{ GID_SQ6 , 0 , 0 , " SQ6 " , " init " , - 1 , 2 , { 0 , 0 } } , // called when the game starts
{ GID_SQ6 , 64950 , 0 , " View " , " handleEvent " , - 1 , 0 , { 0 , 0 } } , // called when pressing "Start game" in the main menu
SCI_WORKAROUNDENTRY_TERMINATOR
2010-06-24 13:17:45 +00:00
} ;
2010-07-04 21:31:09 +00:00
static reg_t validate_read_var ( reg_t * r , reg_t * stack_base , int type , int max , int index , reg_t default_value ) {
if ( validate_variable ( r , stack_base , type , max , index ) ) {
2010-07-12 08:38:30 +00:00
if ( r [ index ] . segment = = 0xffff ) {
switch ( type ) {
case VAR_TEMP : {
// Uninitialized read on a temp
// We need to find correct replacements for each situation manually
SciTrackOriginReply originReply ;
r [ index ] = trackOriginAndFindWorkaround ( index , uninitializedReadWorkarounds , & originReply ) ;
if ( ( r [ index ] . segment = = 0xFFFF ) & & ( r [ index ] . offset = = 0xFFFF ) )
error ( " Uninitialized read for temp %d from method %s::%s (script %d, localCall %x) " , index , originReply . objectName . c_str ( ) , originReply . methodName . c_str ( ) , originReply . scriptNr , originReply . localCallOffset ) ;
break ;
}
case VAR_PARAM :
// Out-of-bounds read for a parameter that goes onto stack and hits an uninitialized temp
// We return 0 currently in that case
warning ( " Read for a parameter goes out-of-bounds, onto the stack and gets uninitialized temp " ) ;
return NULL_REG ;
default :
break ;
}
2010-06-24 11:54:33 +00:00
}
2009-02-15 06:10:59 +00:00
return r [ index ] ;
2010-06-24 11:54:33 +00:00
} else
2009-02-15 06:10:59 +00:00
return default_value ;
}
2010-07-04 21:31:09 +00:00
static void validate_write_var ( reg_t * r , reg_t * stack_base , int type , int max , int index , reg_t value , SegManager * segMan , Kernel * kernel ) {
if ( validate_variable ( r , stack_base , type , max , index ) ) {
2009-10-21 08:28:39 +00:00
2009-10-21 10:00:08 +00:00
// WORKAROUND: This code is needed to work around a probable script bug, or a
// limitation of the original SCI engine, which can be observed in LSL5.
//
// In some games, ego walks via the "Grooper" object, in particular its "stopGroop"
// child. In LSL5, during the game, ego is swapped from Larry to Patti. When this
// happens in the original interpreter, the new actor is loaded in the same memory
// location as the old one, therefore the client variable in the stopGroop object
// points to the new actor. This is probably why the reference of the stopGroop
// object is never updated (which is why I mentioned that this is either a script
// bug or some kind of limitation).
//
// In our implementation, each new object is loaded in a different memory location,
// and we can't overwrite the old one. This means that in our implementation,
// whenever ego is changed, we need to update the "client" variable of the
// stopGroop object, which points to ego, to the new ego object. If this is not
// done, ego's movement will not be updated properly, so the result is
// unpredictable (for example in LSL5, Patti spins around instead of walking).
2010-06-13 22:01:10 +00:00
if ( index = = 0 & & type = = VAR_GLOBAL & & getSciVersion ( ) > SCI_VERSION_0_EARLY ) { // global 0 is ego
2009-10-21 08:28:39 +00:00
reg_t stopGroopPos = segMan - > findObjectByName ( " stopGroop " ) ;
if ( ! stopGroopPos . isNull ( ) ) { // does the game have a stopGroop object?
2009-10-22 05:42:14 +00:00
// Find the "client" member variable of the stopGroop object, and update it
2009-10-21 08:28:39 +00:00
ObjVarRef varp ;
2010-06-10 09:18:57 +00:00
if ( lookupSelector ( segMan , stopGroopPos , SELECTOR ( client ) , & varp , NULL ) = = kSelectorVariable ) {
2009-10-21 08:28:39 +00:00
reg_t * clientVar = varp . getPointer ( segMan ) ;
* clientVar = value ;
}
}
}
2010-06-25 16:04:37 +00:00
// If we are writing an uninitialized value into a temp, we remove the uninitialized segment
// this happens at least in sq1/room 44 (slot-machine), because a send is missing parameters, then
// those parameters are taken from uninitialized stack and afterwards they are copied back into temps
// if we don't remove the segment, we would get false-positive uninitialized reads later
if ( type = = VAR_TEMP & & value . segment = = 0xffff )
value . segment = 0 ;
2009-02-15 06:10:59 +00:00
r [ index ] = value ;
2009-10-21 08:28:39 +00:00
}
2009-02-15 06:10:59 +00:00
}
2010-07-04 21:31:09 +00:00
# define READ_VAR(type, index) validate_read_var(s->variables[type], s->stack_base, type, s->variablesMax[type], index, s->r_acc)
# define WRITE_VAR(type, index, value) validate_write_var(s->variables[type], s->stack_base, type, s->variablesMax[type], index, value, s->_segMan, g_sci->getKernel())
2009-02-15 06:10:59 +00:00
# define WRITE_VAR16(type, index, value) WRITE_VAR(type, index, make_reg(0, value));
# define ACC_ARITHMETIC_L(op) make_reg(0, (op validate_arithmetic(s->r_acc)))
2009-02-20 23:09:29 +00:00
// Operating on the stack
// 16 bit:
2009-02-15 06:10:59 +00:00
# define PUSH(v) PUSH32(make_reg(0, v))
# define POP() (validate_arithmetic(POP32()))
2009-02-20 23:09:29 +00:00
// 32 bit:
2010-06-06 23:00:33 +00:00
# define PUSH32(a) (*(validate_stack_addr(s, (s->xs->sp)++)) = (a))
# define POP32() (*(validate_stack_addr(s, --(s->xs->sp))))
2009-02-15 06:10:59 +00:00
2010-07-12 23:21:03 +00:00
bool SciEngine : : checkExportBreakpoint ( uint16 script , uint16 pubfunct ) {
if ( _debugState . _activeBreakpointTypes & BREAK_EXPORT ) {
uint32 bpaddress ;
bpaddress = ( script < < 16 | pubfunct ) ;
Common : : List < Breakpoint > : : const_iterator bp ;
for ( bp = _debugState . _breakpoints . begin ( ) ; bp ! = _debugState . _breakpoints . end ( ) ; + + bp ) {
if ( bp - > type = = BREAK_EXPORT & & bp - > address = = bpaddress ) {
_console - > DebugPrintf ( " Break on script %d, export %d \n " , script , pubfunct ) ;
_debugState . debugging = true ;
_debugState . breakpointWasHit = true ;
return true ; ;
}
}
}
return false ;
}
2009-03-12 03:26:47 +00:00
ExecStack * execute_method ( EngineState * s , uint16 script , uint16 pubfunct , StackPtr sp , reg_t calling_obj , uint16 argc , StackPtr argp ) {
2009-10-04 18:38:18 +00:00
int seg = s - > _segMan - > getScriptSegment ( script ) ;
Script * scr = s - > _segMan - > getScriptIfLoaded ( seg ) ;
2009-02-15 06:10:59 +00:00
2010-05-19 08:50:24 +00:00
if ( ! scr | | scr - > isMarkedAsDeleted ( ) ) { // Script not present yet?
2010-06-27 21:18:19 +00:00
seg = s - > _segMan - > instantiateScript ( script ) ;
2010-05-19 08:50:24 +00:00
scr = s - > _segMan - > getScript ( seg ) ;
}
2009-02-15 06:10:59 +00:00
2010-05-19 08:50:24 +00:00
const int temp = scr - > validateExportFunc ( pubfunct ) ;
2009-02-15 22:28:12 +00:00
if ( ! temp ) {
2010-01-06 14:04:56 +00:00
# ifdef ENABLE_SCI32
// HACK: Temporarily switch to a warning in SCI32 games until we can figure out why Torin has
// an invalid exported function.
if ( getSciVersion ( ) > = SCI_VERSION_2 )
warning ( " Request for invalid exported function 0x%x of script 0x%x " , pubfunct , script ) ;
else
# endif
error ( " Request for invalid exported function 0x%x of script 0x%x " , pubfunct , script ) ;
2009-02-15 06:10:59 +00:00
return NULL ;
}
2009-02-20 23:09:29 +00:00
// Check if a breakpoint is set on this method
2010-07-12 23:21:03 +00:00
g_sci - > checkExportBreakpoint ( script , pubfunct ) ;
2009-02-15 22:28:12 +00:00
2010-06-26 08:29:55 +00:00
return add_exec_stack_entry ( s - > _executionStack , make_reg ( seg , temp ) , sp , calling_obj , argc , argp , - 1 , pubfunct , - 1 , calling_obj , s - > _executionStack . size ( ) - 1 , seg ) ;
2009-02-15 06:10:59 +00:00
}
2009-02-22 13:11:43 +00:00
static void _exec_varselectors ( EngineState * s ) {
2009-02-20 23:09:29 +00:00
// Executes all varselector read/write ops on the TOS
2009-05-19 00:02:10 +00:00
while ( ! s - > _executionStack . empty ( ) & & s - > _executionStack . back ( ) . type = = EXEC_STACK_TYPE_VARSELECTOR ) {
ExecStack & xs = s - > _executionStack . back ( ) ;
2009-10-04 18:38:18 +00:00
reg_t * var = xs . getVarPointer ( s - > _segMan ) ;
2009-10-14 22:41:03 +00:00
if ( ! var ) {
2010-06-17 23:50:28 +00:00
error ( " Invalid varselector exec stack entry " ) ;
2009-10-14 22:41:03 +00:00
} else {
// varselector access?
if ( xs . argc ) { // write?
* var = xs . variables_argp [ 1 ] ;
} else // No, read
s - > r_acc = * var ;
}
2009-05-19 00:02:10 +00:00
s - > _executionStack . pop_back ( ) ;
}
2009-02-15 06:10:59 +00:00
}
2009-09-17 16:56:36 +00:00
/** This struct is used to buffer the list of send calls in send_selector() */
struct CallsStruct {
reg_t addr_func ;
reg_t varp_objp ;
union {
reg_t func ;
ObjVarRef var ;
} address ;
StackPtr argp ;
int argc ;
Selector selector ;
StackPtr sp ; /**< Stack pointer */
int type ; /**< Same as ExecStack.type */
} ;
2010-07-12 23:21:03 +00:00
bool SciEngine : : checkSelectorBreakpoint ( reg_t send_obj , int selector ) {
if ( _debugState . _activeBreakpointTypes & BREAK_SELECTOR ) {
char method_name [ 256 ] ;
sprintf ( method_name , " %s::%s " , _gamestate - > _segMan - > getObjectName ( send_obj ) , getKernel ( ) - > getSelectorName ( selector ) . c_str ( ) ) ;
Common : : List < Breakpoint > : : const_iterator bp ;
for ( bp = _debugState . _breakpoints . begin ( ) ; bp ! = _debugState . _breakpoints . end ( ) ; + + bp ) {
int cmplen = bp - > name . size ( ) ;
if ( bp - > name . lastChar ( ) ! = ' : ' )
cmplen = 256 ;
if ( bp - > type = = BREAK_SELECTOR & & ! strncmp ( bp - > name . c_str ( ) , method_name , cmplen ) ) {
_console - > DebugPrintf ( " Break on %s (in [%04x:%04x]) \n " , method_name , PRINT_REG ( send_obj ) ) ;
_debugState . debugging = true ;
_debugState . breakpointWasHit = true ;
return true ;
}
}
}
return false ;
}
2009-02-28 11:12:59 +00:00
ExecStack * send_selector ( EngineState * s , reg_t send_obj , reg_t work_obj , StackPtr sp , int framesize , StackPtr argp ) {
2009-02-20 23:09:29 +00:00
// send_obj and work_obj are equal for anything but 'super'
// Returns a pointer to the TOS exec_stack element
2009-05-18 18:15:45 +00:00
assert ( s ) ;
2009-02-15 06:10:59 +00:00
reg_t funcp ;
int selector ;
int argc ;
2009-05-18 18:15:45 +00:00
int origin = s - > _executionStack . size ( ) - 1 ; // Origin: Used for debugging
2010-05-26 09:33:33 +00:00
bool printSendActions = false ;
2009-02-28 11:12:59 +00:00
// We return a pointer to the new active ExecStack
2009-02-15 06:10:59 +00:00
2009-02-20 23:09:29 +00:00
// The selector calls we catch are stored below:
2009-04-11 09:58:30 +00:00
Common : : Stack < CallsStruct > sendCalls ;
2009-02-15 06:10:59 +00:00
while ( framesize > 0 ) {
selector = validate_arithmetic ( * argp + + ) ;
argc = validate_arithmetic ( * argp ) ;
2009-02-20 23:09:29 +00:00
if ( argc > 0x800 ) { // More arguments than the stack could possibly accomodate for
2009-07-03 21:59:07 +00:00
error ( " send_selector(): More than 0x800 arguments to function call " ) ;
2009-02-15 06:10:59 +00:00
}
2009-02-20 23:09:29 +00:00
// Check if a breakpoint is set on this method
2010-07-12 23:21:03 +00:00
printSendActions = g_sci - > checkSelectorBreakpoint ( send_obj , selector ) ;
2009-02-15 06:10:59 +00:00
# ifdef VM_DEBUG_SEND
2010-02-13 17:42:49 +00:00
printf ( " Send to %04x:%04x, selector %04x (%s): " , PRINT_REG ( send_obj ) , selector , g_sci - > getKernel ( ) - > getSelectorName ( selector ) . c_str ( ) ) ;
2009-02-20 23:09:29 +00:00
# endif // VM_DEBUG_SEND
2009-02-15 06:10:59 +00:00
2009-06-06 11:38:20 +00:00
ObjVarRef varp ;
2010-05-29 23:37:15 +00:00
switch ( lookupSelector ( s - > _segMan , send_obj , selector , & varp , & funcp ) ) {
2009-02-28 11:12:59 +00:00
case kSelectorNone :
2010-01-28 10:34:11 +00:00
error ( " Send to invalid selector 0x%x of object at %04x:%04x " , 0xffff & selector , PRINT_REG ( send_obj ) ) ;
2009-02-15 06:10:59 +00:00
break ;
2009-02-28 11:12:59 +00:00
case kSelectorVariable :
2009-02-15 06:10:59 +00:00
# ifdef VM_DEBUG_SEND
if ( argc )
2009-07-06 10:39:22 +00:00
printf ( " Varselector: Write %04x:%04x \n " , PRINT_REG ( argp [ 1 ] ) ) ;
2009-02-15 06:10:59 +00:00
else
2009-07-06 10:39:22 +00:00
printf ( " Varselector: Read \n " ) ;
2009-02-20 23:09:29 +00:00
# endif // VM_DEBUG_SEND
2009-02-15 06:10:59 +00:00
2009-10-01 12:41:21 +00:00
// argc == 0: read selector
2010-05-26 09:33:33 +00:00
// argc != 0: write selector
if ( printSendActions & & ! argc ) { // read selector
debug ( " [read selector] \n " ) ;
printSendActions = false ;
2009-10-01 12:41:21 +00:00
}
2009-02-15 06:10:59 +00:00
2010-05-26 09:33:33 +00:00
if ( printSendActions & & argc ) {
2009-10-04 18:38:18 +00:00
reg_t oldReg = * varp . getPointer ( s - > _segMan ) ;
2009-10-01 12:41:21 +00:00
reg_t newReg = argp [ 1 ] ;
2010-05-26 09:33:33 +00:00
debug ( " [write to selector: change %04x:%04x to %04x:%04x] \n " , PRINT_REG ( oldReg ) , PRINT_REG ( newReg ) ) ;
printSendActions = false ;
2009-10-01 12:41:21 +00:00
}
2010-05-26 10:35:54 +00:00
if ( argc > 1 ) {
2010-06-17 20:52:53 +00:00
// argc can indeed be bigger than 1 in some cases, and it's usually the
// result of a script bug
const char * objectName = s - > _segMan - > getObjectName ( send_obj ) ;
if ( ! strcmp ( objectName , " Sq4GlobalNarrator " ) & & selector = = 606 ) {
// SQ4 has a script bug in the Sq4GlobalNarrator object when invoking the
// returnVal selector, which doesn't affect gameplay, thus don't diplay it
2010-06-25 16:16:29 +00:00
} else if ( ! strcmp ( objectName , " longSong " ) & & selector = = 3 & & g_sci - > getGameId ( ) = = GID_QFG1 ) {
2010-06-22 18:25:15 +00:00
// QFG1VGA has a script bug in the longSong object when invoking the
// loop selector, which doesn't affect gameplay, thus don't diplay it
2010-06-30 17:47:44 +00:00
} else if ( ! strcmp ( objectName , " PuzPiece " ) & & selector = = 77 & & g_sci - > getGameId ( ) = = GID_CASTLEBRAIN ) {
// Castle of Dr. Brain has a script bug in the PuzPiece object when invoking
// the value selector, which doesn't affect gameplay, thus don't display it
2010-06-17 20:52:53 +00:00
} else {
2010-06-24 11:29:26 +00:00
// Unknown script bug, show it. Usually these aren't fatal.
2010-06-17 20:52:53 +00:00
reg_t oldReg = * varp . getPointer ( s - > _segMan ) ;
reg_t newReg = argp [ 1 ] ;
const char * selectorName = g_sci - > getKernel ( ) - > getSelectorName ( selector ) . c_str ( ) ;
2010-06-24 11:29:26 +00:00
warning ( " send_selector(): argc = %d while modifying variable selector "
2010-06-26 16:21:28 +00:00
" %x (%s) of object %04x:%04x (%s) from %04x:%04x to %04x:%04x " ,
2010-06-17 20:52:53 +00:00
argc , selector , selectorName , PRINT_REG ( send_obj ) ,
objectName , PRINT_REG ( oldReg ) , PRINT_REG ( newReg ) ) ;
}
2010-05-26 10:35:54 +00:00
}
2010-05-26 09:59:40 +00:00
2009-10-01 12:41:21 +00:00
{
CallsStruct call ;
call . address . var = varp ; // register the call
call . argp = argp ;
call . argc = argc ;
call . selector = selector ;
call . type = EXEC_STACK_TYPE_VARSELECTOR ; // Register as a varselector
sendCalls . push ( call ) ;
2009-02-15 06:10:59 +00:00
}
2010-01-25 01:39:44 +00:00
2009-02-15 06:10:59 +00:00
break ;
2009-02-28 11:12:59 +00:00
case kSelectorMethod :
2009-02-15 06:10:59 +00:00
# ifdef VM_DEBUG_SEND
2009-07-06 10:39:22 +00:00
printf ( " Funcselector( " ) ;
2009-05-18 18:15:45 +00:00
for ( int i = 0 ; i < argc ; i + + ) {
2009-10-14 22:40:43 +00:00
printf ( " %04x:%04x " , PRINT_REG ( argp [ i + 1 ] ) ) ;
2009-02-15 06:10:59 +00:00
if ( i + 1 < argc )
2009-07-06 10:39:22 +00:00
printf ( " , " ) ;
2009-02-15 06:10:59 +00:00
}
2009-07-06 10:39:22 +00:00
printf ( " ) at %04x:%04x \n " , PRINT_REG ( funcp ) ) ;
2009-02-20 23:09:29 +00:00
# endif // VM_DEBUG_SEND
2010-05-26 09:33:33 +00:00
if ( printSendActions ) {
2010-06-28 07:42:16 +00:00
printf ( " [invoke selector] " ) ;
# ifndef VM_DEBUG_SEND
int displaySize = 0 ;
for ( int argNr = 1 ; argNr < = argc ; argNr + + ) {
if ( argNr = = 1 )
printf ( " - " ) ;
reg_t curParam = argp [ argNr ] ;
if ( curParam . segment ) {
printf ( " [%04x:%04x] " , PRINT_REG ( curParam ) ) ;
displaySize + = 12 ;
} else {
printf ( " [%04x] " , curParam . offset ) ;
displaySize + = 7 ;
}
if ( displaySize > 50 ) {
if ( argNr < argc )
printf ( " ... " ) ;
break ;
}
}
# endif
printf ( " \n " ) ;
2010-05-26 09:33:33 +00:00
printSendActions = false ;
2009-02-15 06:10:59 +00:00
}
2009-10-01 12:41:21 +00:00
{
CallsStruct call ;
call . address . func = funcp ; // register call
call . argp = argp ;
call . argc = argc ;
call . selector = selector ;
call . type = EXEC_STACK_TYPE_CALL ;
call . sp = sp ;
sp = CALL_SP_CARRY ; // Destroy sp, as it will be carried over
sendCalls . push ( call ) ;
}
2009-02-15 06:10:59 +00:00
break ;
2010-05-29 23:37:15 +00:00
} // switch (lookupSelector())
2009-02-15 06:10:59 +00:00
framesize - = ( 2 + argc ) ;
argp + = argc + 1 ;
}
2009-02-20 23:09:29 +00:00
// Iterate over all registered calls in the reverse order. This way, the first call is
// placed on the TOS; as soon as it returns, it will cause the second call to be executed.
2009-04-11 09:58:30 +00:00
while ( ! sendCalls . empty ( ) ) {
CallsStruct call = sendCalls . pop ( ) ;
if ( call . type = = EXEC_STACK_TYPE_VARSELECTOR ) // Write/read variable?
2010-02-03 01:34:39 +00:00
add_exec_stack_varselector ( s - > _executionStack , work_obj , call . argc , call . argp ,
2009-04-11 09:58:30 +00:00
call . selector , call . address . var , origin ) ;
2009-02-15 06:10:59 +00:00
else
2010-02-03 01:34:39 +00:00
add_exec_stack_entry ( s - > _executionStack , call . address . func , call . sp , work_obj ,
2009-04-11 09:58:30 +00:00
call . argc , call . argp ,
2010-06-26 08:29:55 +00:00
call . selector , - 1 , - 1 , send_obj , origin , SCI_XS_CALLEE_LOCALS ) ;
2009-04-11 09:58:30 +00:00
}
2009-02-15 06:10:59 +00:00
_exec_varselectors ( s ) ;
2010-05-26 09:33:33 +00:00
return s - > _executionStack . empty ( ) ? NULL : & ( s - > _executionStack . back ( ) ) ;
2009-02-15 06:10:59 +00:00
}
2010-02-03 01:34:39 +00:00
static ExecStack * add_exec_stack_varselector ( Common : : List < ExecStack > & execStack , reg_t objp , int argc , StackPtr argp , Selector selector , const ObjVarRef & address , int origin ) {
2010-06-26 08:29:55 +00:00
ExecStack * xstack = add_exec_stack_entry ( execStack , NULL_REG , 0 , objp , argc , argp , selector , - 1 , - 1 , objp , origin , SCI_XS_CALLEE_LOCALS ) ;
2009-02-20 23:09:29 +00:00
// Store selector address in sp
2009-02-15 06:10:59 +00:00
xstack - > addr . varp = address ;
xstack - > type = EXEC_STACK_TYPE_VARSELECTOR ;
return xstack ;
}
2010-02-03 01:34:39 +00:00
static ExecStack * add_exec_stack_entry ( Common : : List < ExecStack > & execStack , reg_t pc , StackPtr sp , reg_t objp , int argc ,
2010-06-26 08:29:55 +00:00
StackPtr argp , Selector selector , int exportId , int localCallOffset , reg_t sendp , int origin , SegmentId _localsSegment ) {
2009-02-20 23:09:29 +00:00
// Returns new TOS element for the execution stack
2009-09-16 23:32:27 +00:00
// _localsSegment may be -1 if derived from the called object
2009-02-15 06:10:59 +00:00
2009-07-06 10:39:22 +00:00
//printf("Exec stack: [%d/%d], origin %d, at %p\n", s->execution_stack_pos, s->_executionStack.size(), origin, s->execution_stack);
2009-02-15 06:10:59 +00:00
2009-05-18 18:15:45 +00:00
ExecStack xstack ;
2009-02-15 06:10:59 +00:00
2009-05-18 18:15:45 +00:00
xstack . objp = objp ;
2009-09-16 23:32:27 +00:00
if ( _localsSegment ! = SCI_XS_CALLEE_LOCALS )
xstack . local_segment = _localsSegment ;
2009-02-15 06:10:59 +00:00
else
2009-05-18 18:15:45 +00:00
xstack . local_segment = pc . segment ;
2009-02-15 06:10:59 +00:00
2009-05-18 18:15:45 +00:00
xstack . sendp = sendp ;
xstack . addr . pc = pc ;
xstack . fp = xstack . sp = sp ;
xstack . argc = argc ;
2009-02-15 06:10:59 +00:00
2009-05-18 18:15:45 +00:00
xstack . variables_argp = argp ; // Parameters
2009-02-15 06:10:59 +00:00
2009-02-20 23:09:29 +00:00
* argp = make_reg ( 0 , argc ) ; // SCI code relies on the zeroeth argument to equal argc
2009-02-15 06:10:59 +00:00
2009-02-20 23:09:29 +00:00
// Additional debug information
2010-06-28 14:21:56 +00:00
xstack . debugSelector = selector ;
xstack . debugExportId = exportId ;
xstack . debugLocalCallOffset = localCallOffset ;
xstack . debugOrigin = origin ;
2009-02-15 06:10:59 +00:00
2009-05-18 18:15:45 +00:00
xstack . type = EXEC_STACK_TYPE_CALL ; // Normal call
2009-02-15 06:10:59 +00:00
2010-02-03 01:34:39 +00:00
execStack . push_back ( xstack ) ;
return & ( execStack . back ( ) ) ;
2009-02-15 06:10:59 +00:00
}
2009-02-21 10:47:56 +00:00
static reg_t pointer_add ( EngineState * s , reg_t base , int offset ) {
2009-10-04 18:38:18 +00:00
SegmentObj * mobj = s - > _segMan - > getSegmentObj ( base . segment ) ;
2009-02-15 06:10:59 +00:00
if ( ! mobj ) {
2009-07-06 10:39:22 +00:00
error ( " [VM] Error: Attempt to add %d to invalid pointer %04x:%04x " , offset , PRINT_REG ( base ) ) ;
2009-02-15 06:10:59 +00:00
return NULL_REG ;
}
2009-05-03 09:30:33 +00:00
switch ( mobj - > getType ( ) ) {
2009-02-15 06:10:59 +00:00
2009-09-17 00:45:12 +00:00
case SEG_TYPE_LOCALS :
case SEG_TYPE_SCRIPT :
case SEG_TYPE_STACK :
case SEG_TYPE_DYNMEM :
2009-02-15 06:10:59 +00:00
base . offset + = offset ;
return base ;
default :
2010-02-07 17:57:51 +00:00
// FIXME: Changed this to warning, because iceman does this during dancing with girl.
// Investigate why that is so and either fix the underlying issue or implement a more
// specialized workaround!
2010-01-03 17:40:17 +00:00
warning ( " [VM] Error: Attempt to add %d to pointer %04x:%04x, type %d: Pointer arithmetics of this type unsupported " , offset , PRINT_REG ( base ) , mobj - > getType ( ) ) ;
2009-02-15 06:10:59 +00:00
return NULL_REG ;
}
}
2010-07-10 20:50:52 +00:00
static void addKernelCallToExecStack ( EngineState * s , int kernelCallNr , int argc , reg_t * argv ) {
// Add stack frame to indicate we're executing a callk.
// This is useful in debugger backtraces if this
// kernel function calls a script itself.
ExecStack * xstack ;
xstack = add_exec_stack_entry ( s - > _executionStack , NULL_REG , NULL , NULL_REG , argc , argv - 1 , 0 , - 1 , - 1 , NULL_REG ,
s - > _executionStack . size ( ) - 1 , SCI_XS_CALLEE_LOCALS ) ;
xstack - > debugSelector = kernelCallNr ;
xstack - > type = EXEC_STACK_TYPE_KERNEL ;
}
2010-07-09 21:33:12 +00:00
static void callKernelFunc ( EngineState * s , int kernelCallNr , int argc ) {
2010-06-30 10:09:07 +00:00
Kernel * kernel = g_sci - > getKernel ( ) ;
2010-02-07 17:57:51 +00:00
2010-07-09 21:33:12 +00:00
if ( kernelCallNr > = ( int ) kernel - > _kernelFuncs . size ( ) )
error ( " Invalid kernel function 0x%x requested " , kernelCallNr ) ;
2010-02-07 17:57:51 +00:00
2010-07-09 21:33:12 +00:00
const KernelFunction & kernelCall = kernel - > _kernelFuncs [ kernelCallNr ] ;
2010-07-09 12:06:41 +00:00
reg_t * argv = s - > xs - > sp + 1 ;
2010-02-07 17:57:51 +00:00
2010-06-30 10:09:07 +00:00
if ( kernelCall . signature
2010-07-09 12:06:41 +00:00
& & ! kernel - > signatureMatch ( kernelCall . signature , argc , argv ) ) {
2010-06-30 09:47:04 +00:00
// signature mismatch, check if a workaround is available
SciTrackOriginReply originReply ;
reg_t workaround ;
2010-07-09 21:33:12 +00:00
workaround = trackOriginAndFindWorkaround ( 0 , kernelCall . workarounds , & originReply ) ;
if ( ( workaround . segment = = 0xFFFF ) & & ( workaround . offset = = 0xFFFF ) ) {
2010-07-09 12:06:41 +00:00
kernel - > signatureDebug ( kernelCall . signature , argc , argv ) ;
2010-07-09 21:33:12 +00:00
error ( " [VM] k%s[%x]: signature mismatch via method %s::%s (script %d, localCall %x) " , kernelCall . name , kernelCallNr , originReply . objectName . c_str ( ) , originReply . methodName . c_str ( ) , originReply . scriptNr , originReply . localCallOffset ) ;
2010-06-30 10:09:07 +00:00
}
2010-06-30 09:47:04 +00:00
// FIXME: implement some real workaround type logic - ignore call, still do call etc.
2010-07-10 19:58:25 +00:00
if ( workaround . segment = = 2 )
s - > r_acc = make_reg ( 0 , workaround . offset ) ;
2010-06-30 11:09:49 +00:00
if ( workaround . segment )
2010-06-30 09:47:04 +00:00
return ;
2010-02-07 17:57:51 +00:00
}
2010-07-10 18:21:09 +00:00
// Call kernel function
if ( ! kernelCall . subFunctionCount ) {
2010-07-10 20:50:52 +00:00
addKernelCallToExecStack ( s , kernelCallNr , argc , argv ) ;
2010-07-10 18:21:09 +00:00
s - > r_acc = kernelCall . function ( s , argc , argv ) ;
} else {
// Sub-functions available, check signature and call that one directly
if ( argc < 1 )
error ( " [VM] k%s[%x]: no subfunction-id parameter given " , kernelCall . name , kernelCallNr ) ;
2010-07-12 09:24:18 +00:00
if ( argv [ 0 ] . segment )
error ( " [VM] k%s[%x]: given subfunction-id is actually a pointer " , kernelCall . name , kernelCallNr ) ;
2010-07-10 18:21:09 +00:00
const uint16 subId = argv [ 0 ] . toUint16 ( ) ;
// Skip over subfunction-id
argc - - ;
argv + + ;
if ( subId > = kernelCall . subFunctionCount )
error ( " [VM] k%s: subfunction-id %d requested, but not available " , kernelCall . name , subId ) ;
const KernelSubFunction & kernelSubCall = kernelCall . subFunctions [ subId ] ;
if ( kernelSubCall . signature & & ! kernel - > signatureMatch ( kernelSubCall . signature , argc , argv ) ) {
// Signature mismatch
SciTrackOriginReply originReply ;
reg_t workaround ;
workaround = trackOriginAndFindWorkaround ( 0 , kernelSubCall . workarounds , & originReply ) ;
if ( ( workaround . segment = = 0xFFFF ) & & ( workaround . offset = = 0xFFFF ) ) {
kernel - > signatureDebug ( kernelSubCall . signature , argc , argv ) ;
int callNameLen = strlen ( kernelCall . name ) ;
if ( strncmp ( kernelCall . name , kernelSubCall . name , callNameLen ) = = 0 ) {
const char * subCallName = kernelSubCall . name + callNameLen ;
error ( " [VM] k%s(%s): signature mismatch via method %s::%s (script %d, localCall %x) " , kernelCall . name , subCallName , originReply . objectName . c_str ( ) , originReply . methodName . c_str ( ) , originReply . scriptNr , originReply . localCallOffset ) ;
2010-07-09 21:33:12 +00:00
}
2010-07-10 18:21:09 +00:00
error ( " [VM] k%s: signature mismatch via method %s::%s (script %d, localCall %x) " , kernelSubCall . name , originReply . objectName . c_str ( ) , originReply . methodName . c_str ( ) , originReply . scriptNr , originReply . localCallOffset ) ;
2010-07-09 12:06:41 +00:00
}
2010-07-10 18:21:09 +00:00
// FIXME: implement some real workaround type logic - ignore call, still do call etc.
2010-07-10 19:58:25 +00:00
if ( workaround . segment = = 2 )
s - > r_acc = make_reg ( 0 , workaround . offset ) ;
2010-07-10 18:21:09 +00:00
if ( workaround . segment )
return ;
2010-07-09 12:06:41 +00:00
}
2010-07-10 18:21:09 +00:00
if ( ! kernelSubCall . function )
error ( " [VM] k%s: subfunction-id %d requested, but not available " , kernelCall . name , subId ) ;
2010-07-10 20:50:52 +00:00
addKernelCallToExecStack ( s , kernelCallNr , argc , argv ) ;
2010-07-10 18:21:09 +00:00
s - > r_acc = kernelSubCall . function ( s , argc , argv ) ;
}
2010-06-18 14:48:39 +00:00
#if 0
2010-06-27 23:20:08 +00:00
// Used for debugging
2010-07-10 22:27:28 +00:00
Common : : String debugMsg = Common : : String : : printf ( " %s [0x%x] " , kernelCall . name , kernelCallNr ) +
2010-06-27 23:20:08 +00:00
Common : : String : : printf ( " , %d params: " , argc ) +
" ( " ;
for ( int i = 0 ; i < argc ; i + + ) {
debugMsg + = Common : : String : : printf ( " %04x:%04x " , PRINT_REG ( argv [ i ] ) ) ;
debugMsg + = ( i = = argc - 1 ? " ) " : " , " ) ;
}
2010-06-18 14:48:39 +00:00
2010-06-27 23:20:08 +00:00
debugMsg + = " , result: " + Common : : String : : printf ( " %04x:%04x " , PRINT_REG ( s - > r_acc ) ) ;
debug ( " %s " , debugMsg . c_str ( ) ) ;
2010-06-18 14:48:39 +00:00
# endif
2010-02-07 17:57:51 +00:00
2010-07-10 18:21:09 +00:00
// Remove callk stack frame again, if there's still an execution stack
if ( s - > _executionStack . begin ( ) ! = s - > _executionStack . end ( ) )
s - > _executionStack . pop_back ( ) ;
2010-02-07 17:57:51 +00:00
}
2010-06-10 11:43:20 +00:00
static void gcCountDown ( EngineState * s ) {
if ( s - > gcCountDown - - < = 0 ) {
s - > gcCountDown = s - > scriptGCInterval ;
2010-02-12 02:23:28 +00:00
run_gc ( s ) ;
2009-02-15 06:10:59 +00:00
}
}
2010-02-07 12:13:59 +00:00
int readPMachineInstruction ( const byte * src , byte & extOpcode , int16 opparams [ 4 ] ) {
uint offset = 0 ;
extOpcode = src [ offset + + ] ; // Get "extended" opcode (lower bit has special meaning)
2010-02-07 17:57:25 +00:00
const byte opcode = extOpcode > > 1 ; // get the actual opcode
2010-02-07 12:13:59 +00:00
memset ( opparams , 0 , sizeof ( opparams ) ) ;
2010-02-07 17:57:25 +00:00
for ( int i = 0 ; g_opcode_formats [ opcode ] [ i ] ; + + i ) {
//printf("Opcode: 0x%x, Opnumber: 0x%x, temp: %d\n", opcode, opcode, temp);
2010-02-07 12:13:59 +00:00
assert ( i < 4 ) ;
2010-02-07 17:57:25 +00:00
switch ( g_opcode_formats [ opcode ] [ i ] ) {
2010-02-07 12:13:59 +00:00
case Script_Byte :
opparams [ i ] = src [ offset + + ] ;
break ;
case Script_SByte :
opparams [ i ] = ( int8 ) src [ offset + + ] ;
break ;
case Script_Word :
2010-05-18 04:17:58 +00:00
opparams [ i ] = READ_SCI11ENDIAN_UINT16 ( src + offset ) ;
2010-02-07 12:13:59 +00:00
offset + = 2 ;
break ;
case Script_SWord :
2010-05-18 04:17:58 +00:00
opparams [ i ] = ( int16 ) READ_SCI11ENDIAN_UINT16 ( src + offset ) ;
2010-02-07 12:13:59 +00:00
offset + = 2 ;
break ;
case Script_Variable :
case Script_Property :
case Script_Local :
case Script_Temp :
case Script_Global :
case Script_Param :
case Script_Offset :
if ( extOpcode & 1 ) {
opparams [ i ] = src [ offset + + ] ;
} else {
2010-05-18 04:17:58 +00:00
opparams [ i ] = READ_SCI11ENDIAN_UINT16 ( src + offset ) ;
2010-02-07 12:13:59 +00:00
offset + = 2 ;
}
break ;
case Script_SVariable :
case Script_SRelative :
if ( extOpcode & 1 ) {
opparams [ i ] = ( int8 ) src [ offset + + ] ;
} else {
2010-05-18 04:17:58 +00:00
opparams [ i ] = ( int16 ) READ_SCI11ENDIAN_UINT16 ( src + offset ) ;
2010-02-07 12:13:59 +00:00
offset + = 2 ;
}
break ;
case Script_None :
case Script_End :
break ;
case Script_Invalid :
default :
error ( " opcode %02x: Invalid " , extOpcode ) ;
}
}
return offset ;
}
2010-02-03 01:33:23 +00:00
void run_vm ( EngineState * s , bool restoring ) {
2009-05-19 00:02:10 +00:00
assert ( s ) ;
2009-02-15 06:10:59 +00:00
int temp ;
2009-02-20 23:09:29 +00:00
reg_t r_temp ; // Temporary register
2009-02-28 11:12:59 +00:00
StackPtr s_temp ; // Temporary stack pointer
2009-02-21 21:16:41 +00:00
int16 opparams [ 4 ] ; // opcode parameters
2009-02-20 23:09:29 +00:00
2010-07-06 07:42:25 +00:00
s - > restAdjust = 0 ; // &rest adjusts the parameter count by this value
2009-02-20 23:09:29 +00:00
// Current execution data:
2010-06-06 23:00:33 +00:00
s - > xs = & ( s - > _executionStack . back ( ) ) ;
2009-02-28 11:12:59 +00:00
ExecStack * xs_new = NULL ;
2010-06-06 23:00:33 +00:00
Object * obj = s - > _segMan - > getObject ( s - > xs - > objp ) ;
2010-07-06 07:42:25 +00:00
Script * scr = 0 ;
2010-06-06 23:00:33 +00:00
Script * local_script = s - > _segMan - > getScriptIfLoaded ( s - > xs - > local_segment ) ;
2010-06-10 11:18:10 +00:00
int old_executionStackBase = s - > executionStackBase ;
2009-02-22 13:11:43 +00:00
// Used to detect the stack bottom, for "physical" returns
2009-02-15 06:10:59 +00:00
2010-07-06 07:42:25 +00:00
if ( ! local_script )
2009-07-03 21:59:07 +00:00
error ( " run_vm(): program counter gone astray (local_script pointer is null) " ) ;
2009-02-15 22:28:12 +00:00
2009-02-15 06:10:59 +00:00
if ( ! restoring )
2010-06-10 11:18:10 +00:00
s - > executionStackBase = s - > _executionStack . size ( ) - 1 ;
2009-02-15 06:10:59 +00:00
2010-06-10 11:43:20 +00:00
s - > variablesSegment [ VAR_TEMP ] = s - > variablesSegment [ VAR_PARAM ] = s - > _segMan - > findSegmentByType ( SEG_TYPE_STACK ) ;
s - > variablesBase [ VAR_TEMP ] = s - > variablesBase [ VAR_PARAM ] = s - > stack_base ;
2009-02-15 06:10:59 +00:00
2009-04-28 15:58:19 +00:00
s - > _executionStackPosChanged = true ; // Force initialization
2009-02-15 06:10:59 +00:00
while ( 1 ) {
2009-02-20 23:09:29 +00:00
int var_type ; // See description below
2009-02-15 06:10:59 +00:00
int var_number ;
2010-07-12 23:20:33 +00:00
g_sci - > _debugState . old_pc_offset = s - > xs - > addr . pc . offset ;
g_sci - > _debugState . old_sp = s - > xs - > sp ;
2009-02-15 06:10:59 +00:00
2010-06-21 20:17:59 +00:00
if ( s - > abortScriptProcessing ! = kAbortNone | | g_engine - > shouldQuit ( ) )
return ; // Stop processing
2009-04-28 15:58:19 +00:00
if ( s - > _executionStackPosChanged ) {
2010-07-06 07:42:25 +00:00
scr = s - > _segMan - > getScriptIfLoaded ( s - > xs - > addr . pc . segment ) ;
2010-06-25 17:13:47 +00:00
if ( ! scr )
error ( " No script in segment %d " , s - > xs - > addr . pc . segment ) ;
2010-06-06 23:00:33 +00:00
s - > xs = & ( s - > _executionStack . back ( ) ) ;
2009-04-28 15:58:19 +00:00
s - > _executionStackPosChanged = false ;
2009-02-15 06:10:59 +00:00
2010-06-25 17:13:47 +00:00
obj = s - > _segMan - > getObject ( s - > xs - > objp ) ;
local_script = s - > _segMan - > getScriptIfLoaded ( s - > xs - > local_segment ) ;
if ( ! local_script ) {
// FIXME: Why does this happen? Is the script not loaded yet at this point?
warning ( " Could not find local script from segment %x " , s - > xs - > local_segment ) ;
local_script = NULL ;
s - > variablesBase [ VAR_LOCAL ] = s - > variables [ VAR_LOCAL ] = NULL ;
s - > variablesMax [ VAR_LOCAL ] = 0 ;
2009-02-15 06:10:59 +00:00
} else {
2010-06-25 17:13:47 +00:00
s - > variablesSegment [ VAR_LOCAL ] = local_script - > _localsSegment ;
if ( local_script - > _localsBlock )
s - > variablesBase [ VAR_LOCAL ] = s - > variables [ VAR_LOCAL ] = local_script - > _localsBlock - > _locals . begin ( ) ;
else
2010-06-10 11:43:20 +00:00
s - > variablesBase [ VAR_LOCAL ] = s - > variables [ VAR_LOCAL ] = NULL ;
2010-06-25 17:13:47 +00:00
if ( local_script - > _localsBlock )
s - > variablesMax [ VAR_LOCAL ] = local_script - > _localsBlock - > _locals . size ( ) ;
else
2010-06-10 11:43:20 +00:00
s - > variablesMax [ VAR_LOCAL ] = 0 ;
2010-06-25 17:13:47 +00:00
s - > variablesMax [ VAR_TEMP ] = s - > xs - > sp - s - > xs - > fp ;
s - > variablesMax [ VAR_PARAM ] = s - > xs - > argc + 1 ;
2009-02-15 06:10:59 +00:00
}
2010-06-25 17:13:47 +00:00
s - > variables [ VAR_TEMP ] = s - > xs - > fp ;
s - > variables [ VAR_PARAM ] = s - > xs - > variables_argp ;
2009-02-15 06:10:59 +00:00
}
2010-06-08 21:05:46 +00:00
if ( s - > abortScriptProcessing ! = kAbortNone | | g_engine - > shouldQuit ( ) )
2010-06-21 20:17:59 +00:00
return ; // Stop processing
2009-02-15 06:10:59 +00:00
2009-02-20 23:09:29 +00:00
// Debug if this has been requested:
2009-07-18 12:51:12 +00:00
// TODO: re-implement sci_debug_flags
2010-07-12 23:20:33 +00:00
if ( g_sci - > _debugState . debugging /* sci_debug_flags*/ ) {
g_sci - > scriptDebug ( ) ;
g_sci - > _debugState . breakpointWasHit = false ;
2009-02-15 06:10:59 +00:00
}
2010-02-13 17:42:49 +00:00
Console * con = g_sci - > getSciDebugger ( ) ;
2009-07-18 12:51:12 +00:00
if ( con - > isAttached ( ) ) {
con - > onFrame ( ) ;
}
2009-02-15 06:10:59 +00:00
2010-06-06 23:00:33 +00:00
if ( s - > xs - > sp < s - > xs - > fp )
2010-06-26 16:21:28 +00:00
error ( " run_vm(): stack underflow, sp: %04x:%04x, fp: %04x:%04x " ,
2010-06-06 23:00:33 +00:00
PRINT_REG ( * s - > xs - > sp ) , PRINT_REG ( * s - > xs - > fp ) ) ;
2009-02-15 06:10:59 +00:00
2010-06-10 11:43:20 +00:00
s - > variablesMax [ VAR_TEMP ] = s - > xs - > sp - s - > xs - > fp ;
2009-02-15 06:10:59 +00:00
2010-07-06 07:42:25 +00:00
if ( s - > xs - > addr . pc . offset > = scr - > getBufSize ( ) )
2010-06-26 16:21:28 +00:00
error ( " run_vm(): program counter gone astray, addr: %d, code buffer size: %d " ,
2010-07-06 07:42:25 +00:00
s - > xs - > addr . pc . offset , scr - > getBufSize ( ) ) ;
2009-02-15 06:10:59 +00:00
2010-02-07 12:13:59 +00:00
// Get opcode
2010-02-07 17:57:25 +00:00
byte extOpcode ;
2010-07-06 07:42:25 +00:00
s - > xs - > addr . pc . offset + = readPMachineInstruction ( scr - > getBuf ( ) + s - > xs - > addr . pc . offset , extOpcode , opparams ) ;
2010-02-07 17:57:25 +00:00
const byte opcode = extOpcode > > 1 ;
2009-02-15 06:10:59 +00:00
2010-02-07 17:57:25 +00:00
switch ( opcode ) {
2009-02-15 06:10:59 +00:00
2010-01-28 13:07:47 +00:00
case op_bnot : // 0x00 (00)
2010-07-04 14:55:28 +00:00
// Binary not
2009-02-15 22:28:12 +00:00
s - > r_acc = ACC_ARITHMETIC_L ( 0xffff ^ /*acc*/ ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_add : // 0x01 (01)
2009-02-15 06:10:59 +00:00
r_temp = POP32 ( ) ;
2010-06-25 15:28:24 +00:00
// Happens in SQ1, room 28, when throwing the water at Orat
if ( s - > r_acc . segment = = 0xFFFF ) {
// WORKAROUND: init uninitialized variable to 0
warning ( " op_add: attempt to write to uninitialized variable " ) ;
s - > r_acc = NULL_REG ;
}
2009-02-15 06:10:59 +00:00
if ( r_temp . segment | | s - > r_acc . segment ) {
2009-05-14 23:09:04 +00:00
reg_t r_ptr = NULL_REG ;
2009-02-15 06:10:59 +00:00
int offset ;
2009-02-20 23:09:29 +00:00
// Pointer arithmetics!
2009-02-15 06:10:59 +00:00
if ( s - > r_acc . segment ) {
if ( r_temp . segment ) {
2009-05-31 15:34:23 +00:00
error ( " Attempt to add two pointers, stack=%04x:%04x and acc=%04x:%04x " ,
2009-02-21 14:11:41 +00:00
PRINT_REG ( r_temp ) , PRINT_REG ( s - > r_acc ) ) ;
2009-02-15 06:10:59 +00:00
offset = 0 ;
} else {
2009-02-15 22:28:12 +00:00
r_ptr = s - > r_acc ;
2009-02-15 06:10:59 +00:00
offset = r_temp . offset ;
}
} else {
r_ptr = r_temp ;
offset = s - > r_acc . offset ;
}
s - > r_acc = pointer_add ( s , r_ptr , offset ) ;
} else
s - > r_acc = make_reg ( 0 , r_temp . offset + s - > r_acc . offset ) ;
break ;
2010-01-28 13:07:47 +00:00
case op_sub : // 0x02 (02)
2009-02-15 06:10:59 +00:00
r_temp = POP32 ( ) ;
2010-01-27 12:13:39 +00:00
if ( r_temp . segment ! = s - > r_acc . segment ) {
2009-05-14 23:09:04 +00:00
reg_t r_ptr = NULL_REG ;
2009-02-15 06:10:59 +00:00
int offset ;
2009-02-20 23:09:29 +00:00
// Pointer arithmetics!
2009-02-15 06:10:59 +00:00
if ( s - > r_acc . segment ) {
if ( r_temp . segment ) {
2009-05-31 15:34:23 +00:00
error ( " Attempt to subtract two pointers, stack=%04x:%04x and acc=%04x:%04x " ,
2009-02-21 14:11:41 +00:00
PRINT_REG ( r_temp ) , PRINT_REG ( s - > r_acc ) ) ;
2009-02-15 06:10:59 +00:00
offset = 0 ;
} else {
2009-02-15 22:28:12 +00:00
r_ptr = s - > r_acc ;
2009-02-15 06:10:59 +00:00
offset = r_temp . offset ;
}
} else {
r_ptr = r_temp ;
offset = s - > r_acc . offset ;
}
s - > r_acc = pointer_add ( s , r_ptr , - offset ) ;
2010-01-27 12:13:39 +00:00
} else {
// We can subtract numbers, or pointers with the same segment,
// an operation which will yield a number like in C
2009-02-15 06:10:59 +00:00
s - > r_acc = make_reg ( 0 , r_temp . offset - s - > r_acc . offset ) ;
2010-01-27 12:13:39 +00:00
}
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_mul : // 0x03 (03)
2009-02-21 21:16:41 +00:00
s - > r_acc = ACC_ARITHMETIC_L ( ( ( int16 ) POP ( ) ) * ( int16 ) /*acc*/ ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-07-06 07:42:25 +00:00
case op_div : { // 0x04 (04)
int16 divisor = signed_validate_arithmetic ( s - > r_acc ) ;
s - > r_acc = make_reg ( 0 , ( divisor ! = 0 ? ( ( int16 ) POP ( ) ) / divisor : 0 ) ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-07-06 07:42:25 +00:00
}
case op_mod : { // 0x05 (05)
int16 modulo = signed_validate_arithmetic ( s - > r_acc ) ;
s - > r_acc = make_reg ( 0 , ( modulo ! = 0 ? ( ( int16 ) POP ( ) ) % modulo : 0 ) ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-07-06 07:42:25 +00:00
}
2010-01-28 13:07:47 +00:00
case op_shr : // 0x06 (06)
2010-07-04 14:55:28 +00:00
// Shift right logical
2010-01-28 13:07:47 +00:00
s - > r_acc = ACC_ARITHMETIC_L ( ( ( uint16 ) POP ( ) ) > > /*acc*/ ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_shl : // 0x07 (07)
2010-07-04 14:55:28 +00:00
// Shift left logical
2009-02-21 21:16:41 +00:00
s - > r_acc = ACC_ARITHMETIC_L ( ( ( uint16 ) POP ( ) ) < < /*acc*/ ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_xor : // 0x08 (08)
2009-02-15 06:10:59 +00:00
s - > r_acc = ACC_ARITHMETIC_L ( POP ( ) ^ /*acc*/ ) ;
break ;
2010-01-28 13:07:47 +00:00
case op_and : // 0x09 (09)
2009-02-15 06:10:59 +00:00
s - > r_acc = ACC_ARITHMETIC_L ( POP ( ) & /*acc*/ ) ;
break ;
2010-01-28 13:07:47 +00:00
case op_or : // 0x0a (10)
2009-02-15 06:10:59 +00:00
s - > r_acc = ACC_ARITHMETIC_L ( POP ( ) | /*acc*/ ) ;
break ;
2010-01-28 13:07:47 +00:00
case op_neg : // 0x0b (11)
2009-02-15 06:10:59 +00:00
s - > r_acc = ACC_ARITHMETIC_L ( - /*acc*/ ) ;
break ;
2010-01-28 13:07:47 +00:00
case op_not : // 0x0c (12)
2009-02-15 06:10:59 +00:00
s - > r_acc = make_reg ( 0 , ! ( s - > r_acc . offset | | s - > r_acc . segment ) ) ;
2009-02-22 13:11:43 +00:00
// Must allow pointers to be negated, as this is used for checking whether objects exist
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_eq_ : // 0x0d (13)
2010-07-04 14:55:28 +00:00
// ==
2009-02-15 06:10:59 +00:00
s - > r_prev = s - > r_acc ;
r_temp = POP32 ( ) ;
2009-05-21 17:18:46 +00:00
s - > r_acc = make_reg ( 0 , r_temp = = s - > r_acc ) ;
2009-02-20 23:09:29 +00:00
// Explicitly allow pointers to be compared
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_ne_ : // 0x0e (14)
2010-07-04 14:55:28 +00:00
// !=
2009-02-15 06:10:59 +00:00
s - > r_prev = s - > r_acc ;
r_temp = POP32 ( ) ;
2009-05-21 17:18:46 +00:00
s - > r_acc = make_reg ( 0 , r_temp ! = s - > r_acc ) ;
2009-02-20 23:09:29 +00:00
// Explicitly allow pointers to be compared
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_gt_ : // 0x0f (15)
2010-07-04 14:55:28 +00:00
// >
2009-02-15 06:10:59 +00:00
s - > r_prev = s - > r_acc ;
2010-01-27 01:47:41 +00:00
r_temp = POP32 ( ) ;
if ( r_temp . segment & & s - > r_acc . segment ) {
// Signed pointer comparison. We do unsigned comparison instead, as that is probably what was intended.
2010-02-07 17:57:25 +00:00
if ( r_temp . segment ! = s - > r_acc . segment )
warning ( " [VM] Comparing pointers in different segments (%04x:%04x vs. %04x:%04x) " , PRINT_REG ( r_temp ) , PRINT_REG ( s - > r_acc ) ) ;
2010-01-27 01:47:41 +00:00
s - > r_acc = make_reg ( 0 , ( r_temp . segment = = s - > r_acc . segment ) & & r_temp . offset > s - > r_acc . offset ) ;
2010-06-25 12:15:36 +00:00
} else if ( r_temp . segment & & ! s - > r_acc . segment ) {
2010-06-25 14:50:27 +00:00
if ( s - > r_acc . offset > = 1000 )
error ( " [VM] op_gt: comparsion between a pointer and number " ) ;
// Pseudo-WORKAROUND: sierra allows any pointer <-> value comparsion
2010-06-25 12:15:36 +00:00
// Happens in SQ1, room 28, when throwing the water at Orat
2010-06-25 17:59:48 +00:00
s - > r_acc = make_reg ( 0 , 1 ) ;
2010-01-27 01:47:41 +00:00
} else
2010-02-07 17:57:25 +00:00
s - > r_acc = ACC_ARITHMETIC_L ( signed_validate_arithmetic ( r_temp ) > ( int16 ) /*acc*/ ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_ge_ : // 0x10 (16)
2010-07-04 14:55:28 +00:00
// >=
2009-02-15 06:10:59 +00:00
s - > r_prev = s - > r_acc ;
2010-01-27 01:47:41 +00:00
r_temp = POP32 ( ) ;
2010-02-07 17:57:25 +00:00
if ( r_temp . segment & & s - > r_acc . segment ) {
if ( r_temp . segment ! = s - > r_acc . segment )
warning ( " [VM] Comparing pointers in different segments (%04x:%04x vs. %04x:%04x) " , PRINT_REG ( r_temp ) , PRINT_REG ( s - > r_acc ) ) ;
2010-01-27 01:47:41 +00:00
s - > r_acc = make_reg ( 0 , ( r_temp . segment = = s - > r_acc . segment ) & & r_temp . offset > = s - > r_acc . offset ) ;
2010-02-07 17:57:25 +00:00
} else
s - > r_acc = ACC_ARITHMETIC_L ( signed_validate_arithmetic ( r_temp ) > = ( int16 ) /*acc*/ ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_lt_ : // 0x11 (17)
2010-07-04 14:55:28 +00:00
// <
2009-02-15 06:10:59 +00:00
s - > r_prev = s - > r_acc ;
2010-01-27 01:47:41 +00:00
r_temp = POP32 ( ) ;
2010-02-07 17:57:25 +00:00
if ( r_temp . segment & & s - > r_acc . segment ) {
if ( r_temp . segment ! = s - > r_acc . segment )
warning ( " [VM] Comparing pointers in different segments (%04x:%04x vs. %04x:%04x) " , PRINT_REG ( r_temp ) , PRINT_REG ( s - > r_acc ) ) ;
2010-01-27 01:47:41 +00:00
s - > r_acc = make_reg ( 0 , ( r_temp . segment = = s - > r_acc . segment ) & & r_temp . offset < s - > r_acc . offset ) ;
2010-06-25 22:48:55 +00:00
} else if ( r_temp . segment & & ! s - > r_acc . segment ) {
if ( s - > r_acc . offset > = 1000 )
error ( " [VM] op_lt: comparsion between a pointer and number " ) ;
// Pseudo-WORKAROUND: sierra allows any pointer <-> value comparsion
// Happens in SQ1, room 58, when giving id-card to robot
s - > r_acc = make_reg ( 0 , 1 ) ;
2010-02-07 17:57:25 +00:00
} else
s - > r_acc = ACC_ARITHMETIC_L ( signed_validate_arithmetic ( r_temp ) < ( int16 ) /*acc*/ ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_le_ : // 0x12 (18)
2010-07-04 14:55:28 +00:00
// <=
2009-02-15 06:10:59 +00:00
s - > r_prev = s - > r_acc ;
2010-01-27 01:47:41 +00:00
r_temp = POP32 ( ) ;
2010-02-07 17:57:25 +00:00
if ( r_temp . segment & & s - > r_acc . segment ) {
if ( r_temp . segment ! = s - > r_acc . segment )
warning ( " [VM] Comparing pointers in different segments (%04x:%04x vs. %04x:%04x) " , PRINT_REG ( r_temp ) , PRINT_REG ( s - > r_acc ) ) ;
2010-01-27 01:47:41 +00:00
s - > r_acc = make_reg ( 0 , ( r_temp . segment = = s - > r_acc . segment ) & & r_temp . offset < = s - > r_acc . offset ) ;
2010-02-07 17:57:25 +00:00
} else
s - > r_acc = ACC_ARITHMETIC_L ( signed_validate_arithmetic ( r_temp ) < = ( int16 ) /*acc*/ ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_ugt_ : // 0x13 (19)
2010-07-04 14:55:28 +00:00
// > (unsigned)
2009-02-15 06:10:59 +00:00
s - > r_prev = s - > r_acc ;
r_temp = POP32 ( ) ;
2010-01-29 22:29:03 +00:00
// SCI0/SCI1 scripts use this to check whether a
// parameter is a pointer or a far text
// reference. It is used e.g. by the standard library
// Print function to distinguish two ways of calling it:
//
// (Print "foo") // Pointer to a string
// (Print 420 5) // Reference to the fifth message in text resource 420
2010-06-26 16:21:28 +00:00
// It works because in those games, the maximum resource number is 999,
// so any parameter value above that threshold must be a pointer.
2010-02-07 17:57:25 +00:00
if ( r_temp . segment & & ( s - > r_acc = = make_reg ( 0 , 1000 ) ) )
2010-06-25 17:59:48 +00:00
s - > r_acc = make_reg ( 0 , 1 ) ;
2010-01-30 04:01:15 +00:00
else if ( r_temp . segment & & s - > r_acc . segment )
s - > r_acc = make_reg ( 0 , ( r_temp . segment = = s - > r_acc . segment ) & & r_temp . offset > s - > r_acc . offset ) ;
else
s - > r_acc = ACC_ARITHMETIC_L ( validate_arithmetic ( r_temp ) > /*acc*/ ) ;
break ;
case op_uge_ : // 0x14 (20)
2010-07-04 14:55:28 +00:00
// >= (unsigned)
2010-01-30 04:01:15 +00:00
s - > r_prev = s - > r_acc ;
r_temp = POP32 ( ) ;
// See above
2010-02-07 17:57:25 +00:00
if ( r_temp . segment & & ( s - > r_acc = = make_reg ( 0 , 1000 ) ) )
2010-01-30 04:01:15 +00:00
s - > r_acc = make_reg ( 0 , 1 ) ;
2010-01-29 22:17:27 +00:00
else if ( r_temp . segment & & s - > r_acc . segment )
2010-01-27 13:14:28 +00:00
s - > r_acc = make_reg ( 0 , ( r_temp . segment = = s - > r_acc . segment ) & & r_temp . offset > = s - > r_acc . offset ) ;
else
s - > r_acc = ACC_ARITHMETIC_L ( validate_arithmetic ( r_temp ) > = /*acc*/ ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_ult_ : // 0x15 (21)
2010-07-04 14:55:28 +00:00
// < (unsigned)
2009-02-15 06:10:59 +00:00
s - > r_prev = s - > r_acc ;
r_temp = POP32 ( ) ;
2010-01-29 22:29:03 +00:00
// See above
2010-02-07 17:57:25 +00:00
if ( r_temp . segment & & ( s - > r_acc = = make_reg ( 0 , 1000 ) ) )
2010-01-30 04:01:15 +00:00
s - > r_acc = NULL_REG ;
2010-01-29 22:17:27 +00:00
else if ( r_temp . segment & & s - > r_acc . segment )
2010-01-27 13:14:28 +00:00
s - > r_acc = make_reg ( 0 , ( r_temp . segment = = s - > r_acc . segment ) & & r_temp . offset < s - > r_acc . offset ) ;
else
s - > r_acc = ACC_ARITHMETIC_L ( validate_arithmetic ( r_temp ) < /*acc*/ ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_ule_ : // 0x16 (22)
2010-07-04 14:55:28 +00:00
// <= (unsigned)
2009-02-15 06:10:59 +00:00
s - > r_prev = s - > r_acc ;
r_temp = POP32 ( ) ;
2010-01-30 04:01:15 +00:00
// See above
2010-02-07 17:57:25 +00:00
if ( r_temp . segment & & ( s - > r_acc = = make_reg ( 0 , 1000 ) ) )
2010-01-30 04:01:15 +00:00
s - > r_acc = NULL_REG ;
else if ( r_temp . segment & & s - > r_acc . segment )
2010-01-27 13:14:28 +00:00
s - > r_acc = make_reg ( 0 , ( r_temp . segment = = s - > r_acc . segment ) & & r_temp . offset < = s - > r_acc . offset ) ;
else
s - > r_acc = ACC_ARITHMETIC_L ( validate_arithmetic ( r_temp ) < = /*acc*/ ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_bt : // 0x17 (23)
2010-07-04 14:55:28 +00:00
// Branch relative if true
2009-02-15 06:10:59 +00:00
if ( s - > r_acc . offset | | s - > r_acc . segment )
2010-06-06 23:00:33 +00:00
s - > xs - > addr . pc . offset + = opparams [ 0 ] ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_bnt : // 0x18 (24)
2010-07-04 14:55:28 +00:00
// Branch relative if not true
2009-02-15 06:10:59 +00:00
if ( ! ( s - > r_acc . offset | | s - > r_acc . segment ) )
2010-06-06 23:00:33 +00:00
s - > xs - > addr . pc . offset + = opparams [ 0 ] ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_jmp : // 0x19 (25)
2010-06-06 23:00:33 +00:00
s - > xs - > addr . pc . offset + = opparams [ 0 ] ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_ldi : // 0x1a (26)
2010-07-04 14:55:28 +00:00
// Load data immediate
2009-02-15 06:10:59 +00:00
s - > r_acc = make_reg ( 0 , opparams [ 0 ] ) ;
break ;
2010-01-28 13:07:47 +00:00
case op_push : // 0x1b (27)
2010-07-04 14:55:28 +00:00
// Push to stack
2009-02-15 06:10:59 +00:00
PUSH32 ( s - > r_acc ) ;
break ;
2010-01-28 13:07:47 +00:00
case op_pushi : // 0x1c (28)
2010-07-04 14:55:28 +00:00
// Push immediate
2009-02-15 06:10:59 +00:00
PUSH ( opparams [ 0 ] ) ;
break ;
2010-01-28 13:07:47 +00:00
case op_toss : // 0x1d (29)
2010-07-04 14:55:28 +00:00
// TOS (Top Of Stack) subtract
2010-06-06 23:00:33 +00:00
s - > xs - > sp - - ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_dup : // 0x1e (30)
2010-07-04 14:55:28 +00:00
// Duplicate TOD (Top Of Stack) element
2010-06-06 23:00:33 +00:00
r_temp = s - > xs - > sp [ - 1 ] ;
2009-02-15 06:10:59 +00:00
PUSH32 ( r_temp ) ;
break ;
2010-01-28 13:07:47 +00:00
case op_link : // 0x1f (31)
2010-06-24 11:54:33 +00:00
// We shouldn't initialize temp variables at all
// We put special segment 0xFFFF in there, so that uninitialized reads can get detected
for ( int i = 0 ; i < opparams [ 0 ] ; i + + )
2010-06-24 21:32:29 +00:00
s - > xs - > sp [ i ] = make_reg ( 0xffff , 0 ) ;
2010-06-24 07:54:02 +00:00
2010-06-06 23:00:33 +00:00
s - > xs - > sp + = opparams [ 0 ] ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_call : { // 0x20 (32)
2010-07-04 14:55:28 +00:00
// Call a script subroutine
2009-02-22 13:11:43 +00:00
int argc = ( opparams [ 1 ] > > 1 ) // Given as offset, but we need count
2010-06-09 07:32:17 +00:00
+ 1 + s - > restAdjust ;
2010-06-06 23:00:33 +00:00
StackPtr call_base = s - > xs - > sp - argc ;
2010-06-09 07:32:17 +00:00
s - > xs - > sp [ 1 ] . offset + = s - > restAdjust ;
2010-06-06 23:00:33 +00:00
2010-06-26 08:29:55 +00:00
uint16 localCallOffset = s - > xs - > addr . pc . offset + opparams [ 0 ] ;
2010-06-06 23:00:33 +00:00
xs_new = add_exec_stack_entry ( s - > _executionStack , make_reg ( s - > xs - > addr . pc . segment ,
2010-06-26 08:29:55 +00:00
localCallOffset ) ,
2010-06-06 23:00:33 +00:00
s - > xs - > sp , s - > xs - > objp ,
2010-06-09 07:32:17 +00:00
( validate_arithmetic ( * call_base ) ) + s - > restAdjust ,
2010-06-26 08:29:55 +00:00
call_base , NULL_SELECTOR , - 1 , localCallOffset , s - > xs - > objp ,
2010-06-06 23:00:33 +00:00
s - > _executionStack . size ( ) - 1 , s - > xs - > local_segment ) ;
2010-06-09 07:32:17 +00:00
s - > restAdjust = 0 ; // Used up the &rest adjustment
2010-06-06 23:00:33 +00:00
s - > xs - > sp = call_base ;
2009-02-15 06:10:59 +00:00
2009-04-28 15:58:19 +00:00
s - > _executionStackPosChanged = true ;
2009-02-15 06:10:59 +00:00
break ;
}
2010-01-28 13:07:47 +00:00
case op_callk : { // 0x21 (33)
2010-07-04 14:55:28 +00:00
// Call kernel function
2010-06-10 11:43:20 +00:00
gcCountDown ( s ) ;
2009-02-15 06:10:59 +00:00
2010-06-06 23:00:33 +00:00
s - > xs - > sp - = ( opparams [ 1 ] > > 1 ) + 1 ;
2009-08-25 08:38:14 +00:00
2009-09-17 16:50:53 +00:00
bool oldScriptHeader = ( getSciVersion ( ) = = SCI_VERSION_0_EARLY ) ;
2010-06-09 07:32:17 +00:00
if ( ! oldScriptHeader )
s - > xs - > sp - = s - > restAdjust ;
2009-02-15 06:10:59 +00:00
2010-06-06 23:00:33 +00:00
int argc = validate_arithmetic ( s - > xs - > sp [ 0 ] ) ;
- Changed the unimplemented debug SCI kernel functions (InspectObj, ShowSends, ShowObjs, ShowFree, StackUsage and Profiler) to be dummy functions - we have our own debugger, and don't use these functions for debugging
- Removed the function number parameter from all kernel functions, as it's no longer needed, and removed the FAKE_FUNCT_NR hack
- Removed kUnknown() and kStub()
- Dummy/unknown kernel functions are no longer invoked, and a warning is shown instead, with the paremeters passed to them
Note: there is an evil hack used for debugging scripts in invoke_selector(), which probably no longer works now
svn-id: r44461
2009-09-29 14:24:07 +00:00
2010-02-07 17:57:51 +00:00
if ( ! oldScriptHeader )
2010-06-09 07:32:17 +00:00
argc + = s - > restAdjust ;
- Changed the unimplemented debug SCI kernel functions (InspectObj, ShowSends, ShowObjs, ShowFree, StackUsage and Profiler) to be dummy functions - we have our own debugger, and don't use these functions for debugging
- Removed the function number parameter from all kernel functions, as it's no longer needed, and removed the FAKE_FUNCT_NR hack
- Removed kUnknown() and kStub()
- Dummy/unknown kernel functions are no longer invoked, and a warning is shown instead, with the paremeters passed to them
Note: there is an evil hack used for debugging scripts in invoke_selector(), which probably no longer works now
svn-id: r44461
2009-09-29 14:24:07 +00:00
2010-02-07 17:57:51 +00:00
callKernelFunc ( s , opparams [ 0 ] , argc ) ;
2009-02-15 06:10:59 +00:00
2010-02-07 17:57:51 +00:00
if ( ! oldScriptHeader )
2010-06-09 07:32:17 +00:00
s - > restAdjust = 0 ;
2009-02-15 06:10:59 +00:00
2010-02-07 17:57:51 +00:00
// Calculate xs again: The kernel function might
// have spawned a new VM
2009-02-15 06:10:59 +00:00
2010-02-07 17:57:51 +00:00
xs_new = & ( s - > _executionStack . back ( ) ) ;
s - > _executionStackPosChanged = true ;
2010-06-25 17:25:00 +00:00
// If a game is being loaded, stop processing
if ( s - > abortScriptProcessing ! = kAbortNone | | g_engine - > shouldQuit ( ) )
return ; // Stop processing
2009-02-15 06:10:59 +00:00
break ;
2009-09-17 13:20:58 +00:00
}
2009-02-15 06:10:59 +00:00
2010-01-28 13:07:47 +00:00
case op_callb : // 0x22 (34)
2010-07-04 14:55:28 +00:00
// Call base script
2010-06-09 07:32:17 +00:00
temp = ( ( opparams [ 1 ] > > 1 ) + s - > restAdjust + 1 ) ;
2010-06-06 23:00:33 +00:00
s_temp = s - > xs - > sp ;
s - > xs - > sp - = temp ;
2010-06-09 07:32:17 +00:00
s - > xs - > sp [ 0 ] . offset + = s - > restAdjust ;
2010-06-06 23:00:33 +00:00
xs_new = execute_method ( s , 0 , opparams [ 0 ] , s_temp , s - > xs - > objp ,
s - > xs - > sp [ 0 ] . offset , s - > xs - > sp ) ;
2010-06-09 07:32:17 +00:00
s - > restAdjust = 0 ; // Used up the &rest adjustment
2009-02-22 13:11:43 +00:00
if ( xs_new ) // in case of error, keep old stack
2009-04-28 15:58:19 +00:00
s - > _executionStackPosChanged = true ;
2009-02-15 06:10:59 +00:00
break ;
2009-02-15 22:28:12 +00:00
2010-01-28 13:07:47 +00:00
case op_calle : // 0x23 (35)
2010-07-04 14:55:28 +00:00
// Call external script
2010-06-09 07:32:17 +00:00
temp = ( ( opparams [ 2 ] > > 1 ) + s - > restAdjust + 1 ) ;
2010-06-06 23:00:33 +00:00
s_temp = s - > xs - > sp ;
s - > xs - > sp - = temp ;
2009-02-15 06:10:59 +00:00
2010-06-09 07:32:17 +00:00
s - > xs - > sp [ 0 ] . offset + = s - > restAdjust ;
2010-06-06 23:00:33 +00:00
xs_new = execute_method ( s , opparams [ 0 ] , opparams [ 1 ] , s_temp , s - > xs - > objp ,
s - > xs - > sp [ 0 ] . offset , s - > xs - > sp ) ;
2010-06-09 07:32:17 +00:00
s - > restAdjust = 0 ; // Used up the &rest adjustment
2009-02-15 22:28:12 +00:00
2009-02-22 13:11:43 +00:00
if ( xs_new ) // in case of error, keep old stack
2009-04-28 15:58:19 +00:00
s - > _executionStackPosChanged = true ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_ret : // 0x24 (36)
2010-07-04 14:55:28 +00:00
// Return from an execution loop started by call, calle, callb, send, self or super
2009-02-15 06:10:59 +00:00
do {
2010-06-06 23:00:33 +00:00
StackPtr old_sp2 = s - > xs - > sp ;
StackPtr old_fp = s - > xs - > fp ;
2009-05-18 18:15:45 +00:00
ExecStack * old_xs = & ( s - > _executionStack . back ( ) ) ;
2009-02-15 06:10:59 +00:00
2010-06-10 11:18:10 +00:00
if ( ( int ) s - > _executionStack . size ( ) - 1 = = s - > executionStackBase ) { // Have we reached the base?
s - > executionStackBase = old_executionStackBase ; // Restore stack base
2009-02-15 06:10:59 +00:00
2009-05-18 18:15:45 +00:00
s - > _executionStack . pop_back ( ) ;
2009-02-15 06:10:59 +00:00
2009-04-28 15:58:19 +00:00
s - > _executionStackPosChanged = true ;
2009-02-22 13:11:43 +00:00
return ; // "Hard" return
2009-02-15 06:10:59 +00:00
}
if ( old_xs - > type = = EXEC_STACK_TYPE_VARSELECTOR ) {
2009-02-22 13:11:43 +00:00
// varselector access?
2009-10-04 18:38:18 +00:00
reg_t * var = old_xs - > getVarPointer ( s - > _segMan ) ;
2009-02-22 13:11:43 +00:00
if ( old_xs - > argc ) // write?
2009-09-21 21:39:00 +00:00
* var = old_xs - > variables_argp [ 1 ] ;
2009-02-22 13:11:43 +00:00
else // No, read
2009-09-21 21:39:00 +00:00
s - > r_acc = * var ;
2009-02-15 06:10:59 +00:00
}
2009-02-22 13:11:43 +00:00
// Not reached the base, so let's do a soft return
2009-05-18 18:15:45 +00:00
s - > _executionStack . pop_back ( ) ;
2009-04-28 15:58:19 +00:00
s - > _executionStackPosChanged = true ;
2010-06-06 23:00:33 +00:00
s - > xs = & ( s - > _executionStack . back ( ) ) ;
2009-02-15 06:10:59 +00:00
2010-06-06 23:00:33 +00:00
if ( s - > xs - > sp = = CALL_SP_CARRY // Used in sends to 'carry' the stack pointer
| | s - > xs - > type ! = EXEC_STACK_TYPE_CALL ) {
s - > xs - > sp = old_sp2 ;
s - > xs - > fp = old_fp ;
2009-02-15 06:10:59 +00:00
}
2010-06-06 23:00:33 +00:00
} while ( s - > xs - > type = = EXEC_STACK_TYPE_VARSELECTOR ) ;
2009-02-22 13:11:43 +00:00
// Iterate over all varselector accesses
2009-04-28 15:58:19 +00:00
s - > _executionStackPosChanged = true ;
2010-06-06 23:00:33 +00:00
xs_new = s - > xs ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_send : // 0x25 (37)
2010-07-04 14:55:28 +00:00
// Send for one or more selectors
2010-06-06 23:00:33 +00:00
s_temp = s - > xs - > sp ;
2010-06-09 07:32:17 +00:00
s - > xs - > sp - = ( ( opparams [ 0 ] > > 1 ) + s - > restAdjust ) ; // Adjust stack
2009-02-15 06:10:59 +00:00
2010-06-09 07:32:17 +00:00
s - > xs - > sp [ 1 ] . offset + = s - > restAdjust ;
2009-07-11 06:19:29 +00:00
xs_new = send_selector ( s , s - > r_acc , s - > r_acc , s_temp ,
2010-06-09 07:32:17 +00:00
( int ) ( opparams [ 0 ] > > 1 ) + ( uint16 ) s - > restAdjust , s - > xs - > sp ) ;
2009-02-15 06:10:59 +00:00
2010-06-06 23:00:33 +00:00
if ( xs_new & & xs_new ! = s - > xs )
2009-04-28 15:58:19 +00:00
s - > _executionStackPosChanged = true ;
2009-02-15 06:10:59 +00:00
2010-06-09 07:32:17 +00:00
s - > restAdjust = 0 ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case 0x26 : // (38)
case 0x27 : // (39)
2010-02-07 17:57:25 +00:00
error ( " Dummy opcode 0x%x called " , opcode ) ; // should never happen
2010-01-28 13:07:47 +00:00
break ;
case op_class : // 0x28 (40)
2010-07-04 14:55:28 +00:00
// Get class address
2009-10-04 18:38:18 +00:00
s - > r_acc = s - > _segMan - > getClassAddress ( ( unsigned ) opparams [ 0 ] , SCRIPT_GET_LOCK ,
2010-06-06 23:00:33 +00:00
s - > xs - > addr . pc ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case 0x29 : // (41)
2010-02-07 17:57:25 +00:00
error ( " Dummy opcode 0x%x called " , opcode ) ; // should never happen
2010-01-28 13:07:47 +00:00
break ;
case op_self : // 0x2a (42)
2010-07-04 14:55:28 +00:00
// Send to self
2010-06-06 23:00:33 +00:00
s_temp = s - > xs - > sp ;
2010-06-09 07:32:17 +00:00
s - > xs - > sp - = ( ( opparams [ 0 ] > > 1 ) + s - > restAdjust ) ; // Adjust stack
2009-02-15 06:10:59 +00:00
2010-06-09 07:32:17 +00:00
s - > xs - > sp [ 1 ] . offset + = s - > restAdjust ;
2010-06-06 23:00:33 +00:00
xs_new = send_selector ( s , s - > xs - > objp , s - > xs - > objp ,
2010-06-09 07:32:17 +00:00
s_temp , ( int ) ( opparams [ 0 ] > > 1 ) + ( uint16 ) s - > restAdjust ,
2010-06-06 23:00:33 +00:00
s - > xs - > sp ) ;
2009-02-15 06:10:59 +00:00
2010-06-06 23:00:33 +00:00
if ( xs_new & & xs_new ! = s - > xs )
2009-04-28 15:58:19 +00:00
s - > _executionStackPosChanged = true ;
2009-02-15 06:10:59 +00:00
2010-06-09 07:32:17 +00:00
s - > restAdjust = 0 ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_super : // 0x2b (43)
2010-07-04 14:55:28 +00:00
// Send to any class
2010-06-06 23:00:33 +00:00
r_temp = s - > _segMan - > getClassAddress ( opparams [ 0 ] , SCRIPT_GET_LOAD , s - > xs - > addr . pc ) ;
2009-02-15 06:10:59 +00:00
if ( ! r_temp . segment )
2009-07-03 21:59:07 +00:00
error ( " [VM]: Invalid superclass in object " ) ;
2009-02-15 06:10:59 +00:00
else {
2010-06-06 23:00:33 +00:00
s_temp = s - > xs - > sp ;
2010-06-09 07:32:17 +00:00
s - > xs - > sp - = ( ( opparams [ 1 ] > > 1 ) + s - > restAdjust ) ; // Adjust stack
2009-02-15 06:10:59 +00:00
2010-06-09 07:32:17 +00:00
s - > xs - > sp [ 1 ] . offset + = s - > restAdjust ;
2010-06-06 23:00:33 +00:00
xs_new = send_selector ( s , r_temp , s - > xs - > objp , s_temp ,
2010-06-09 07:32:17 +00:00
( int ) ( opparams [ 1 ] > > 1 ) + ( uint16 ) s - > restAdjust ,
2010-06-06 23:00:33 +00:00
s - > xs - > sp ) ;
2009-02-15 06:10:59 +00:00
2010-06-06 23:00:33 +00:00
if ( xs_new & & xs_new ! = s - > xs )
2009-04-28 15:58:19 +00:00
s - > _executionStackPosChanged = true ;
2009-02-15 06:10:59 +00:00
2010-06-09 07:32:17 +00:00
s - > restAdjust = 0 ;
2009-02-15 06:10:59 +00:00
}
break ;
2010-01-28 13:07:47 +00:00
case op_rest : // 0x2c (44)
2010-07-04 14:55:28 +00:00
// Pushes all or part of the parameter variable list on the stack
2009-02-22 13:11:43 +00:00
temp = ( uint16 ) opparams [ 0 ] ; // First argument
2010-06-09 07:32:17 +00:00
s - > restAdjust = MAX < int16 > ( s - > xs - > argc - temp + 1 , 0 ) ; // +1 because temp counts the paramcount while argc doesn't
2009-02-15 06:10:59 +00:00
2010-06-06 23:00:33 +00:00
for ( ; temp < = s - > xs - > argc ; temp + + )
PUSH32 ( s - > xs - > variables_argp [ temp ] ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_lea : // 0x2d (45)
2010-07-04 14:55:28 +00:00
// Load Effective Address
2009-02-21 21:16:41 +00:00
temp = ( uint16 ) opparams [ 0 ] > > 1 ;
2009-02-22 13:11:43 +00:00
var_number = temp & 0x03 ; // Get variable type
2009-02-15 06:10:59 +00:00
2009-02-22 13:11:43 +00:00
// Get variable block offset
2010-06-10 11:43:20 +00:00
r_temp . segment = s - > variablesSegment [ var_number ] ;
r_temp . offset = s - > variables [ var_number ] - s - > variablesBase [ var_number ] ;
2009-02-15 06:10:59 +00:00
2009-02-22 13:11:43 +00:00
if ( temp & 0x08 ) // Add accumulator offset if requested
2009-02-15 06:10:59 +00:00
r_temp . offset + = signed_validate_arithmetic ( s - > r_acc ) ;
2009-02-22 13:11:43 +00:00
r_temp . offset + = opparams [ 1 ] ; // Add index
2009-09-27 12:23:14 +00:00
r_temp . offset * = 2 ; // variables are 16 bit
2009-02-22 13:11:43 +00:00
// That's the immediate address now
2009-02-15 06:10:59 +00:00
s - > r_acc = r_temp ;
break ;
2010-01-28 13:07:47 +00:00
case op_selfID : // 0x2e (46)
2010-07-04 14:55:28 +00:00
// Get 'self' identity
2010-06-06 23:00:33 +00:00
s - > r_acc = s - > xs - > objp ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case 0x2f : // (47)
2010-02-07 17:57:25 +00:00
error ( " Dummy opcode 0x%x called " , opcode ) ; // should never happen
2010-01-28 13:07:47 +00:00
break ;
case op_pprev : // 0x30 (48)
2010-07-04 14:55:28 +00:00
// Pushes the value of the prev register, set by the last comparison
// bytecode (eq?, lt?, etc.), on the stack
2009-02-15 06:10:59 +00:00
PUSH32 ( s - > r_prev ) ;
break ;
2010-01-28 13:07:47 +00:00
case op_pToa : // 0x31 (49)
2010-07-04 14:55:28 +00:00
// Property To Accumulator
2010-07-06 07:42:25 +00:00
s - > r_acc = validate_property ( obj , ( opparams [ 0 ] > > 1 ) ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_aTop : // 0x32 (50)
2010-07-04 14:55:28 +00:00
// Accumulator To Property
2010-07-06 07:42:25 +00:00
validate_property ( obj , ( opparams [ 0 ] > > 1 ) ) = s - > r_acc ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_pTos : // 0x33 (51)
2010-07-04 14:55:28 +00:00
// Property To Stack
2010-07-06 07:42:25 +00:00
PUSH32 ( validate_property ( obj , opparams [ 0 ] > > 1 ) ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_sTop : // 0x34 (52)
2010-07-04 14:55:28 +00:00
// Stack To Property
2010-07-06 07:42:25 +00:00
validate_property ( obj , ( opparams [ 0 ] > > 1 ) ) = POP32 ( ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_ipToa : // 0x35 (53)
2010-07-04 14:55:28 +00:00
// Incement Property and copy To Accumulator
2010-07-06 07:42:25 +00:00
s - > r_acc = validate_property ( obj , ( opparams [ 0 ] > > 1 ) ) ;
s - > r_acc = validate_property ( obj , ( opparams [ 0 ] > > 1 ) ) = ACC_ARITHMETIC_L ( 1 + /*acc*/ ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_dpToa : { // 0x36 (54)
2010-07-04 14:55:28 +00:00
// Decrement Property and copy To Accumulator
2010-07-06 07:42:25 +00:00
s - > r_acc = validate_property ( obj , ( opparams [ 0 ] > > 1 ) ) ;
s - > r_acc = validate_property ( obj , ( opparams [ 0 ] > > 1 ) ) = ACC_ARITHMETIC_L ( - 1 + /*acc*/ ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-17 18:27:57 +00:00
}
2009-02-15 06:10:59 +00:00
2010-01-28 13:07:47 +00:00
case op_ipTos : // 0x37 (55)
2010-07-04 14:55:28 +00:00
// Increment Property and push to Stack
2010-07-06 07:42:25 +00:00
validate_arithmetic ( validate_property ( obj , ( opparams [ 0 ] > > 1 ) ) ) ;
temp = + + validate_property ( obj , ( opparams [ 0 ] > > 1 ) ) . offset ;
2009-02-15 06:10:59 +00:00
PUSH ( temp ) ;
break ;
2010-01-28 13:07:47 +00:00
case op_dpTos : // 0x38 (56)
2010-07-04 14:55:28 +00:00
// Decrement Property and push to Stack
2010-07-06 07:42:25 +00:00
validate_arithmetic ( validate_property ( obj , ( opparams [ 0 ] > > 1 ) ) ) ;
temp = - - validate_property ( obj , ( opparams [ 0 ] > > 1 ) ) . offset ;
2009-02-15 06:10:59 +00:00
PUSH ( temp ) ;
break ;
2010-01-28 13:07:47 +00:00
case op_lofsa : // 0x39 (57)
2010-07-04 14:55:28 +00:00
// Load Offset to Accumulator
2010-06-06 23:00:33 +00:00
s - > r_acc . segment = s - > xs - > addr . pc . segment ;
2009-02-15 06:10:59 +00:00
2010-02-13 17:44:58 +00:00
switch ( g_sci - > _features - > detectLofsType ( ) ) {
2009-08-30 14:53:58 +00:00
case SCI_VERSION_1_1 :
2010-05-30 16:14:31 +00:00
s - > r_acc . offset = opparams [ 0 ] + local_script - > getScriptSize ( ) ;
2009-08-30 14:53:58 +00:00
break ;
case SCI_VERSION_1_MIDDLE :
s - > r_acc . offset = opparams [ 0 ] ;
break ;
default :
2010-06-06 23:00:33 +00:00
s - > r_acc . offset = s - > xs - > addr . pc . offset + opparams [ 0 ] ;
2009-05-14 12:38:50 +00:00
}
2010-07-06 07:42:25 +00:00
if ( s - > r_acc . offset > = scr - > getBufSize ( ) ) {
2009-05-31 15:34:23 +00:00
error ( " VM: lofsa operation overflowed: %04x:%04x beyond end "
2010-07-06 07:42:25 +00:00
" of script (at %04x) \n " , PRINT_REG ( s - > r_acc ) , scr - > getBufSize ( ) ) ;
2009-02-15 06:10:59 +00:00
}
break ;
2010-01-28 13:07:47 +00:00
case op_lofss : // 0x3a (58)
2010-07-04 14:55:28 +00:00
// Load Offset to Stack
2010-06-06 23:00:33 +00:00
r_temp . segment = s - > xs - > addr . pc . segment ;
2009-02-15 06:10:59 +00:00
2010-02-13 17:44:58 +00:00
switch ( g_sci - > _features - > detectLofsType ( ) ) {
2009-08-30 14:53:58 +00:00
case SCI_VERSION_1_1 :
2010-05-30 16:14:31 +00:00
r_temp . offset = opparams [ 0 ] + local_script - > getScriptSize ( ) ;
2009-08-30 14:53:58 +00:00
break ;
case SCI_VERSION_1_MIDDLE :
r_temp . offset = opparams [ 0 ] ;
break ;
default :
2010-06-06 23:00:33 +00:00
r_temp . offset = s - > xs - > addr . pc . offset + opparams [ 0 ] ;
2009-07-07 07:51:26 +00:00
}
2010-07-06 07:42:25 +00:00
if ( r_temp . offset > = scr - > getBufSize ( ) ) {
2009-05-31 15:34:23 +00:00
error ( " VM: lofss operation overflowed: %04x:%04x beyond end "
2010-07-06 07:42:25 +00:00
" of script (at %04x) " , PRINT_REG ( r_temp ) , scr - > getBufSize ( ) ) ;
2009-02-15 06:10:59 +00:00
}
PUSH32 ( r_temp ) ;
break ;
2010-01-28 13:07:47 +00:00
case op_push0 : // 0x3b (59)
2009-02-15 06:10:59 +00:00
PUSH ( 0 ) ;
break ;
2010-01-28 13:07:47 +00:00
case op_push1 : // 0x3c (60)
2009-02-15 06:10:59 +00:00
PUSH ( 1 ) ;
break ;
2010-01-28 13:07:47 +00:00
case op_push2 : // 0x3d (61)
2009-02-15 06:10:59 +00:00
PUSH ( 2 ) ;
break ;
2010-01-28 13:07:47 +00:00
case op_pushSelf : // 0x3e (62)
2010-02-07 17:57:25 +00:00
if ( ! ( extOpcode & 1 ) ) {
2010-06-06 23:00:33 +00:00
PUSH32 ( s - > xs - > objp ) ;
2010-01-28 19:22:58 +00:00
} else {
// Debug opcode op_file, skip null-terminated string (file name)
2010-07-06 07:42:25 +00:00
const byte * code_buf = scr - > getBuf ( ) ;
2010-06-06 23:00:33 +00:00
while ( code_buf [ s - > xs - > addr . pc . offset + + ] ) ;
2010-01-28 19:22:58 +00:00
}
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 19:22:58 +00:00
case op_line : // 0x3f (63)
// Debug opcode (line number)
2010-01-28 13:07:47 +00:00
break ;
case op_lag : // 0x40 (64)
case op_lal : // 0x41 (65)
case op_lat : // 0x42 (66)
case op_lap : // 0x43 (67)
2010-07-04 14:55:28 +00:00
// Load global, local, temp or param variable into the accumulator
2010-02-07 17:57:25 +00:00
var_type = opcode & 0x3 ; // Gets the variable type: g, l, t or p
2009-02-15 06:10:59 +00:00
var_number = opparams [ 0 ] ;
2010-06-28 11:19:27 +00:00
s - > r_acc = READ_VAR ( var_type , var_number ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_lsg : // 0x44 (68)
case op_lsl : // 0x45 (69)
case op_lst : // 0x46 (70)
case op_lsp : // 0x47 (71)
2010-07-04 14:55:28 +00:00
// Load global, local, temp or param variable into the stack
2010-02-07 17:57:25 +00:00
var_type = opcode & 0x3 ; // Gets the variable type: g, l, t or p
2009-02-15 06:10:59 +00:00
var_number = opparams [ 0 ] ;
2010-06-28 11:19:27 +00:00
PUSH32 ( READ_VAR ( var_type , var_number ) ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_lagi : // 0x48 (72)
case op_lali : // 0x49 (73)
case op_lati : // 0x4a (74)
case op_lapi : // 0x4b (75)
2010-07-04 14:55:28 +00:00
// Load global, local, temp or param variable into the accumulator,
// using the accumulator as an additional index
2010-02-07 17:57:25 +00:00
var_type = opcode & 0x3 ; // Gets the variable type: g, l, t or p
2009-02-15 06:10:59 +00:00
var_number = opparams [ 0 ] + signed_validate_arithmetic ( s - > r_acc ) ;
2010-06-28 11:19:27 +00:00
s - > r_acc = READ_VAR ( var_type , var_number ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_lsgi : // 0x4c (76)
case op_lsli : // 0x4d (77)
case op_lsti : // 0x4e (78)
case op_lspi : // 0x4f (79)
2010-07-04 14:55:28 +00:00
// Load global, local, temp or param variable into the stack,
// using the accumulator as an additional index
2010-02-07 17:57:25 +00:00
var_type = opcode & 0x3 ; // Gets the variable type: g, l, t or p
2009-02-15 06:10:59 +00:00
var_number = opparams [ 0 ] + signed_validate_arithmetic ( s - > r_acc ) ;
2010-06-28 11:19:27 +00:00
PUSH32 ( READ_VAR ( var_type , var_number ) ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_sag : // 0x50 (80)
case op_sal : // 0x51 (81)
case op_sat : // 0x52 (82)
case op_sap : // 0x53 (83)
2010-07-04 14:55:28 +00:00
// Save the accumulator into the global, local, temp or param variable
2010-02-07 17:57:25 +00:00
var_type = opcode & 0x3 ; // Gets the variable type: g, l, t or p
2009-02-15 06:10:59 +00:00
var_number = opparams [ 0 ] ;
WRITE_VAR ( var_type , var_number , s - > r_acc ) ;
break ;
2010-01-28 13:07:47 +00:00
case op_ssg : // 0x54 (84)
case op_ssl : // 0x55 (85)
case op_sst : // 0x56 (86)
case op_ssp : // 0x57 (87)
2010-07-04 14:55:28 +00:00
// Save the stack into the global, local, temp or param variable
2010-02-07 17:57:25 +00:00
var_type = opcode & 0x3 ; // Gets the variable type: g, l, t or p
2009-02-15 06:10:59 +00:00
var_number = opparams [ 0 ] ;
WRITE_VAR ( var_type , var_number , POP32 ( ) ) ;
break ;
2010-01-28 13:07:47 +00:00
case op_sagi : // 0x58 (88)
case op_sali : // 0x59 (89)
case op_sati : // 0x5a (90)
case op_sapi : // 0x5b (91)
2010-07-04 14:55:28 +00:00
// Save the accumulator into the global, local, temp or param variable,
// using the accumulator as an additional index
2009-02-20 23:09:29 +00:00
// Special semantics because it wouldn't really make a whole lot
// of sense otherwise, with acc being used for two things
2009-02-22 13:11:43 +00:00
// simultaneously...
2010-02-07 17:57:25 +00:00
var_type = opcode & 0x3 ; // Gets the variable type: g, l, t or p
2009-02-15 06:10:59 +00:00
var_number = opparams [ 0 ] + signed_validate_arithmetic ( s - > r_acc ) ;
2010-02-07 17:57:25 +00:00
s - > r_acc = POP32 ( ) ;
WRITE_VAR ( var_type , var_number , s - > r_acc ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-01-28 13:07:47 +00:00
case op_ssgi : // 0x5c (92)
case op_ssli : // 0x5d (93)
case op_ssti : // 0x5e (94)
case op_sspi : // 0x5f (95)
2010-07-04 14:55:28 +00:00
// Save the stack into the global, local, temp or param variable,
// using the accumulator as an additional index
2010-02-07 17:57:25 +00:00
var_type = opcode & 0x3 ; // Gets the variable type: g, l, t or p
2009-02-15 06:10:59 +00:00
var_number = opparams [ 0 ] + signed_validate_arithmetic ( s - > r_acc ) ;
WRITE_VAR ( var_type , var_number , POP32 ( ) ) ;
break ;
2010-01-28 13:07:47 +00:00
case op_plusag : // 0x60 (96)
case op_plusal : // 0x61 (97)
case op_plusat : // 0x62 (98)
case op_plusap : // 0x63 (99)
2010-07-04 14:55:28 +00:00
// Increment the global, local, temp or param variable and save it
// to the accumulator
2010-02-07 17:57:25 +00:00
var_type = opcode & 0x3 ; // Gets the variable type: g, l, t or p
2009-02-15 06:10:59 +00:00
var_number = opparams [ 0 ] ;
2010-06-28 11:19:27 +00:00
r_temp = READ_VAR ( var_type , var_number ) ;
2010-01-03 17:40:17 +00:00
if ( r_temp . segment ) {
// Pointer arithmetics!
s - > r_acc = pointer_add ( s , r_temp , 1 ) ;
} else
s - > r_acc = make_reg ( 0 , r_temp . offset + 1 ) ;
2009-02-15 06:10:59 +00:00
WRITE_VAR ( var_type , var_number , s - > r_acc ) ;
break ;
2010-01-28 13:07:47 +00:00
case op_plussg : // 0x64 (100)
case op_plussl : // 0x65 (101)
case op_plusst : // 0x66 (102)
case op_plussp : // 0x67 (103)
2010-07-04 14:55:28 +00:00
// Increment the global, local, temp or param variable and save it
// to the stack
2010-02-07 17:57:25 +00:00
var_type = opcode & 0x3 ; // Gets the variable type: g, l, t or p
2009-02-15 06:10:59 +00:00
var_number = opparams [ 0 ] ;
2010-06-28 11:19:27 +00:00
r_temp = READ_VAR ( var_type , var_number ) ;
2010-01-03 17:40:17 +00:00
if ( r_temp . segment ) {
// Pointer arithmetics!
r_temp = pointer_add ( s , r_temp , 1 ) ;
} else
r_temp = make_reg ( 0 , r_temp . offset + 1 ) ;
2009-02-15 06:10:59 +00:00
PUSH32 ( r_temp ) ;
WRITE_VAR ( var_type , var_number , r_temp ) ;
break ;
2010-01-28 13:07:47 +00:00
case op_plusagi : // 0x68 (104)
case op_plusali : // 0x69 (105)
case op_plusati : // 0x6a (106)
case op_plusapi : // 0x6b (107)
2010-07-04 14:55:28 +00:00
// Increment the global, local, temp or param variable and save it
// to the accumulator, using the accumulator as an additional index
2010-02-07 17:57:25 +00:00
var_type = opcode & 0x3 ; // Gets the variable type: g, l, t or p
2009-02-15 06:10:59 +00:00
var_number = opparams [ 0 ] + signed_validate_arithmetic ( s - > r_acc ) ;
2010-06-28 11:19:27 +00:00
r_temp = READ_VAR ( var_type , var_number ) ;
2010-01-03 17:40:17 +00:00
if ( r_temp . segment ) {
// Pointer arithmetics!
s - > r_acc = pointer_add ( s , r_temp , 1 ) ;
} else
s - > r_acc = make_reg ( 0 , r_temp . offset + 1 ) ;
2009-02-15 06:10:59 +00:00
WRITE_VAR ( var_type , var_number , s - > r_acc ) ;
break ;
2010-01-28 13:07:47 +00:00
case op_plussgi : // 0x6c (108)
case op_plussli : // 0x6d (109)
case op_plussti : // 0x6e (110)
case op_plusspi : // 0x6f (111)
2010-07-04 14:55:28 +00:00
// Increment the global, local, temp or param variable and save it
// to the stack, using the accumulator as an additional index
2010-02-07 17:57:25 +00:00
var_type = opcode & 0x3 ; // Gets the variable type: g, l, t or p
2009-02-15 06:10:59 +00:00
var_number = opparams [ 0 ] + signed_validate_arithmetic ( s - > r_acc ) ;
2010-06-28 11:19:27 +00:00
r_temp = READ_VAR ( var_type , var_number ) ;
2010-01-03 17:40:17 +00:00
if ( r_temp . segment ) {
// Pointer arithmetics!
r_temp = pointer_add ( s , r_temp , 1 ) ;
} else
r_temp = make_reg ( 0 , r_temp . offset + 1 ) ;
2009-02-15 06:10:59 +00:00
PUSH32 ( r_temp ) ;
WRITE_VAR ( var_type , var_number , r_temp ) ;
break ;
2010-01-28 13:07:47 +00:00
case op_minusag : // 0x70 (112)
case op_minusal : // 0x71 (113)
case op_minusat : // 0x72 (114)
case op_minusap : // 0x73 (115)
2010-07-04 14:55:28 +00:00
// Decrement the global, local, temp or param variable and save it
// to the accumulator
2010-02-07 17:57:25 +00:00
var_type = opcode & 0x3 ; // Gets the variable type: g, l, t or p
2009-02-15 06:10:59 +00:00
var_number = opparams [ 0 ] ;
2010-06-28 11:19:27 +00:00
r_temp = READ_VAR ( var_type , var_number ) ;
2010-01-03 17:40:17 +00:00
if ( r_temp . segment ) {
// Pointer arithmetics!
s - > r_acc = pointer_add ( s , r_temp , - 1 ) ;
} else
s - > r_acc = make_reg ( 0 , r_temp . offset - 1 ) ;
2009-02-15 06:10:59 +00:00
WRITE_VAR ( var_type , var_number , s - > r_acc ) ;
break ;
2010-01-28 13:07:47 +00:00
case op_minussg : // 0x74 (116)
case op_minussl : // 0x75 (117)
case op_minusst : // 0x76 (118)
case op_minussp : // 0x77 (119)
2010-07-04 14:55:28 +00:00
// Decrement the global, local, temp or param variable and save it
// to the stack
2010-02-07 17:57:25 +00:00
var_type = opcode & 0x3 ; // Gets the variable type: g, l, t or p
2009-02-15 06:10:59 +00:00
var_number = opparams [ 0 ] ;
2010-06-28 11:19:27 +00:00
r_temp = READ_VAR ( var_type , var_number ) ;
2010-01-03 17:40:17 +00:00
if ( r_temp . segment ) {
// Pointer arithmetics!
r_temp = pointer_add ( s , r_temp , - 1 ) ;
} else
r_temp = make_reg ( 0 , r_temp . offset - 1 ) ;
2009-02-15 06:10:59 +00:00
PUSH32 ( r_temp ) ;
WRITE_VAR ( var_type , var_number , r_temp ) ;
break ;
2010-01-28 13:07:47 +00:00
case op_minusagi : // 0x78 (120)
case op_minusali : // 0x79 (121)
case op_minusati : // 0x7a (122)
case op_minusapi : // 0x7b (123)
2010-07-04 14:55:28 +00:00
// Decrement the global, local, temp or param variable and save it
// to the accumulator, using the accumulator as an additional index
2010-02-07 17:57:25 +00:00
var_type = opcode & 0x3 ; // Gets the variable type: g, l, t or p
2009-02-15 06:10:59 +00:00
var_number = opparams [ 0 ] + signed_validate_arithmetic ( s - > r_acc ) ;
2010-06-28 11:19:27 +00:00
r_temp = READ_VAR ( var_type , var_number ) ;
2010-01-03 17:40:17 +00:00
if ( r_temp . segment ) {
// Pointer arithmetics!
s - > r_acc = pointer_add ( s , r_temp , - 1 ) ;
} else
s - > r_acc = make_reg ( 0 , r_temp . offset - 1 ) ;
2009-02-15 06:10:59 +00:00
WRITE_VAR ( var_type , var_number , s - > r_acc ) ;
break ;
2010-01-28 13:07:47 +00:00
case op_minussgi : // 0x7c (124)
case op_minussli : // 0x7d (125)
case op_minussti : // 0x7e (126)
case op_minusspi : // 0x7f (127)
2010-07-04 14:55:28 +00:00
// Decrement the global, local, temp or param variable and save it
// to the stack, using the accumulator as an additional index
2010-02-07 17:57:25 +00:00
var_type = opcode & 0x3 ; // Gets the variable type: g, l, t or p
2009-02-15 06:10:59 +00:00
var_number = opparams [ 0 ] + signed_validate_arithmetic ( s - > r_acc ) ;
2010-06-28 11:19:27 +00:00
r_temp = READ_VAR ( var_type , var_number ) ;
2010-01-03 17:40:17 +00:00
if ( r_temp . segment ) {
// Pointer arithmetics!
r_temp = pointer_add ( s , r_temp , - 1 ) ;
} else
r_temp = make_reg ( 0 , r_temp . offset - 1 ) ;
2009-02-15 06:10:59 +00:00
PUSH32 ( r_temp ) ;
WRITE_VAR ( var_type , var_number , r_temp ) ;
break ;
default :
2010-02-07 17:57:25 +00:00
error ( " run_vm(): illegal opcode %x " , opcode ) ;
2009-02-15 06:10:59 +00:00
2010-02-07 17:57:25 +00:00
} // switch (opcode)
2009-02-15 06:10:59 +00:00
2009-04-28 15:58:19 +00:00
if ( s - > _executionStackPosChanged ) // Force initialization
2010-06-06 23:00:33 +00:00
s - > xs = xs_new ;
2009-02-15 06:10:59 +00:00
2010-06-06 23:00:33 +00:00
if ( s - > xs ! = & ( s - > _executionStack . back ( ) ) ) {
2010-06-17 23:50:28 +00:00
error ( " xs is stale (%p vs %p); last command was %02x " ,
2010-06-06 23:00:33 +00:00
( void * ) s - > xs , ( void * ) & ( s - > _executionStack . back ( ) ) ,
2010-02-07 17:57:25 +00:00
opcode ) ;
2009-02-15 06:10:59 +00:00
}
2010-06-10 11:18:10 +00:00
+ + s - > scriptStepCounter ;
2009-02-15 06:10:59 +00:00
}
}
2010-05-26 16:30:10 +00:00
reg_t * ObjVarRef : : getPointer ( SegManager * segMan ) const {
2009-09-12 00:10:07 +00:00
Object * o = segMan - > getObject ( obj ) ;
2010-05-26 16:30:10 +00:00
return o ? & o - > getVariableRef ( varindex ) : 0 ;
2009-06-06 11:38:20 +00:00
}
2010-05-26 16:30:10 +00:00
reg_t * ExecStack : : getVarPointer ( SegManager * segMan ) const {
2009-06-06 11:38:20 +00:00
assert ( type = = EXEC_STACK_TYPE_VARSELECTOR ) ;
2009-09-06 12:57:42 +00:00
return addr . varp . getPointer ( segMan ) ;
2009-06-06 11:38:20 +00:00
}
2009-05-28 22:42:18 +00:00
2009-02-21 10:23:36 +00:00
} // End of namespace Sci