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"
2010-07-21 14:18:26 +00:00
# include "sci/engine/workarounds.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)
/**
* 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
// If this occurs, it means there's probably something wrong with the garbage
// collector, so don't hide it with fake return values
2010-08-06 15:05:05 +00:00
if ( ! obj )
2010-06-24 18:48:50 +00:00
error ( " validate_property: Sending to disposed object " ) ;
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.
2010-08-05 23:08:05 +00:00
// If such an error occurs, we usually need to find the last kernel function called and check its return value.
2010-08-06 11:45:32 +00:00
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-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-07-18 12:25:40 +00:00
static bool validate_unsignedInteger ( reg_t reg , uint16 & integer ) {
if ( reg . segment )
return false ;
integer = reg . offset ;
return true ;
}
2010-07-14 19:12:31 +00:00
static bool validate_signedInteger ( reg_t reg , int16 & integer ) {
if ( reg . segment )
return false ;
integer = ( int16 ) reg . offset ;
return true ;
}
extern const char * opcodeNames [ ] ; // from scriptdebug.cpp
static reg_t arithmetic_lookForWorkaround ( const byte opcode , const SciWorkaroundEntry * workaroundList , reg_t value1 , reg_t value2 ) {
SciTrackOriginReply originReply ;
2010-07-20 17:58:04 +00:00
SciWorkaroundSolution solution = trackOriginAndFindWorkaround ( 0 , workaroundList , & originReply ) ;
if ( solution . type = = WORKAROUND_NONE )
2010-07-28 11:37:22 +00:00
error ( " %s on non-integer (%04x:%04x, %04x:%04x) from method %s::%s (script %d, room %d, localCall %x) " ,
opcodeNames [ opcode ] , PRINT_REG ( value1 ) , PRINT_REG ( value2 ) , originReply . objectName . c_str ( ) ,
originReply . methodName . c_str ( ) , originReply . scriptNr , g_sci - > getEngineState ( ) - > currentRoomNumber ( ) ,
originReply . localCallOffset ) ;
2010-07-20 17:58:04 +00:00
assert ( solution . type = = WORKAROUND_FAKE ) ;
return make_reg ( 0 , solution . value ) ;
2010-07-14 19:12:31 +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 ;
2010-07-20 17:58:04 +00:00
SciWorkaroundSolution solution = trackOriginAndFindWorkaround ( index , uninitializedReadWorkarounds , & originReply ) ;
2010-10-07 14:40:11 +00:00
if ( solution . type = = WORKAROUND_NONE ) {
# ifdef RELEASE_BUILD
// If we are running an official ScummVM release -> fake 0 in unknown cases
2010-10-07 14:57:59 +00:00
warning ( " Uninitialized read for temp %d from method %s::%s (script %d, room %d, localCall %x) " ,
index , originReply . objectName . c_str ( ) , originReply . methodName . c_str ( ) , originReply . scriptNr ,
g_sci - > getEngineState ( ) - > currentRoomNumber ( ) , originReply . localCallOffset ) ;
2010-10-07 14:40:11 +00:00
r [ index ] = NULL_REG ;
break ;
# else
2010-07-28 11:37:22 +00:00
error ( " Uninitialized read for temp %d from method %s::%s (script %d, room %d, localCall %x) " ,
index , originReply . objectName . c_str ( ) , originReply . methodName . c_str ( ) , originReply . scriptNr ,
g_sci - > getEngineState ( ) - > currentRoomNumber ( ) , originReply . localCallOffset ) ;
2010-10-07 14:40:11 +00:00
# endif
}
2010-07-20 17:58:04 +00:00
assert ( solution . type = = WORKAROUND_FAKE ) ;
r [ index ] = make_reg ( 0 , solution . value ) ;
2010-07-12 08:38:30 +00:00
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
2010-07-31 14:38:28 +00:00
debugC ( 2 , kDebugLevelVM , " [VM] Read for a parameter goes out-of-bounds, onto the stack and gets uninitialized temp " ) ;
2010-07-12 08:38:30 +00:00
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));
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))
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 ;
2010-09-02 05:12:07 +00:00
return true ;
2010-07-12 23:21:03 +00:00
}
}
}
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 )
2010-09-09 12:01:04 +00:00
warning ( " Request for invalid exported function 0x%x of script %d " , pubfunct , script ) ;
2010-01-06 14:04:56 +00:00
else
# endif
2010-09-09 12:01:04 +00:00
error ( " Request for invalid exported function 0x%x of script %d " , 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-09-02 09:05:08 +00:00
bool SciEngine : : checkSelectorBreakpoint ( BreakpointType breakpointType , reg_t send_obj , int 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 = = breakpointType & & ! 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 ;
2010-07-12 23:21:03 +00:00
}
}
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
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
2010-09-02 09:05:08 +00:00
int activeBreakpointTypes = g_sci - > _debugState . _activeBreakpointTypes ;
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
}
# ifdef VM_DEBUG_SEND
2010-08-07 12:36:17 +00:00
printf ( " Send to %04x:%04x (%s), selector %04x (%s): " , PRINT_REG ( send_obj ) ,
s - > _segMan - > getObjectName ( 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
2010-09-02 09:05:08 +00:00
if ( ! argc ) {
// read selector
if ( activeBreakpointTypes & BREAK_SELECTORREAD ) {
if ( g_sci - > checkSelectorBreakpoint ( BREAK_SELECTORREAD , send_obj , selector ) )
debug ( " [read selector] \n " ) ;
}
} else {
// write selector
if ( activeBreakpointTypes & BREAK_SELECTORWRITE ) {
if ( g_sci - > checkSelectorBreakpoint ( BREAK_SELECTORWRITE , send_obj , selector ) ) {
reg_t oldReg = * varp . getPointer ( s - > _segMan ) ;
reg_t newReg = argp [ 1 ] ;
warning ( " [write to selector (%s:%s): change %04x:%04x to %04x:%04x] \n " ,
s - > _segMan - > getObjectName ( send_obj ) , g_sci - > getKernel ( ) - > getSelectorName ( selector ) . c_str ( ) ,
PRINT_REG ( oldReg ) , PRINT_REG ( newReg ) ) ;
}
}
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
2010-07-22 15:56:26 +00:00
// result of a script bug. Usually these aren't fatal.
2010-06-17 20:52:53 +00:00
const char * objectName = s - > _segMan - > getObjectName ( send_obj ) ;
2010-07-22 15:56:26 +00:00
reg_t oldReg = * varp . getPointer ( s - > _segMan ) ;
reg_t newReg = argp [ 1 ] ;
const char * selectorName = g_sci - > getKernel ( ) - > getSelectorName ( selector ) . c_str ( ) ;
debug ( 2 , " send_selector(): argc = %d while modifying variable selector "
" %x (%s) of object %04x:%04x (%s) from %04x:%04x to %04x:%04x " ,
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
2010-06-28 07:42:16 +00:00
# ifndef VM_DEBUG_SEND
2010-09-02 09:05:08 +00:00
if ( activeBreakpointTypes & BREAK_SELECTOREXEC ) {
if ( g_sci - > checkSelectorBreakpoint ( BREAK_SELECTOREXEC , send_obj , selector ) ) {
printf ( " [execute selector] " ) ;
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 ;
}
2010-06-28 07:42:16 +00:00
}
2010-09-02 10:21:20 +00:00
printf ( " \n " ) ;
2010-06-28 07:42:16 +00:00
}
2009-02-15 06:10:59 +00:00
}
2010-09-02 18:58:25 +00:00
# else // VM_DEBUG_SEND
if ( activeBreakpointTypes & BREAK_SELECTOREXEC )
g_sci - > checkSelectorBreakpoint ( BREAK_SELECTOREXEC , send_obj , selector ) ;
printf ( " Funcselector( " ) ;
for ( int i = 0 ; i < argc ; i + + ) {
printf ( " %04x:%04x " , PRINT_REG ( argp [ i + 1 ] ) ) ;
if ( i + 1 < argc )
printf ( " , " ) ;
}
printf ( " ) at %04x:%04x \n " , PRINT_REG ( funcp ) ) ;
# endif // VM_DEBUG_SEND
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-29 17:22:38 +00:00
static void logKernelCall ( const KernelFunction * kernelCall , const KernelSubFunction * kernelSubCall , EngineState * s , int argc , reg_t * argv , reg_t result ) {
2010-07-23 20:47:15 +00:00
Kernel * kernel = g_sci - > getKernel ( ) ;
2010-07-29 17:22:38 +00:00
if ( ! kernelSubCall ) {
printf ( " k%s: " , kernelCall - > name ) ;
} else {
int callNameLen = strlen ( kernelCall - > name ) ;
if ( strncmp ( kernelCall - > name , kernelSubCall - > name , callNameLen ) = = 0 ) {
const char * subCallName = kernelSubCall - > name + callNameLen ;
printf ( " k%s(%s): " , kernelCall - > name , subCallName ) ;
} else {
printf ( " k%s(%s): " , kernelCall - > name , kernelSubCall - > name ) ;
}
}
2010-07-23 20:47:15 +00:00
for ( int parmNr = 0 ; parmNr < argc ; parmNr + + ) {
if ( parmNr )
printf ( " , " ) ;
uint16 regType = kernel - > findRegType ( argv [ parmNr ] ) ;
if ( regType & SIG_TYPE_NULL )
2010-07-23 21:13:57 +00:00
printf ( " 0 " ) ;
2010-07-23 20:47:15 +00:00
else if ( regType & SIG_TYPE_UNINITIALIZED )
printf ( " UNINIT " ) ;
else if ( regType & SIG_IS_INVALID )
printf ( " INVALID " ) ;
else if ( regType & SIG_TYPE_INTEGER )
2010-07-23 21:13:57 +00:00
printf ( " %d " , argv [ parmNr ] . offset ) ;
2010-07-23 20:47:15 +00:00
else {
printf ( " %04x:%04x " , PRINT_REG ( argv [ parmNr ] ) ) ;
switch ( regType ) {
case SIG_TYPE_OBJECT :
printf ( " (%s) " , s - > _segMan - > getObjectName ( argv [ parmNr ] ) ) ;
break ;
case SIG_TYPE_REFERENCE :
2010-07-24 11:51:09 +00:00
if ( kernelCall - > function = = kSaid ) {
SegmentRef saidSpec = s - > _segMan - > dereference ( argv [ parmNr ] ) ;
if ( saidSpec . isRaw ) {
printf ( " (' " ) ;
g_sci - > getVocabulary ( ) - > debugDecipherSaidBlock ( saidSpec . raw ) ;
printf ( " ') " ) ;
} else {
printf ( " (non-raw said-spec) " ) ;
}
} else {
printf ( " ('%s') " , s - > _segMan - > getString ( argv [ parmNr ] ) . c_str ( ) ) ;
}
2010-07-23 20:47:15 +00:00
default :
break ;
}
}
}
2010-07-23 20:59:27 +00:00
if ( result . segment )
printf ( " = %04x:%04x \n " , PRINT_REG ( result ) ) ;
else
2010-07-23 21:20:35 +00:00
printf ( " = %d \n " , result . offset ) ;
2010-07-23 20:47:15 +00:00
}
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 ;
2010-07-20 17:58:04 +00:00
SciWorkaroundSolution solution = trackOriginAndFindWorkaround ( 0 , kernelCall . workarounds , & originReply ) ;
switch ( solution . type ) {
case WORKAROUND_NONE :
2010-07-09 12:06:41 +00:00
kernel - > signatureDebug ( kernelCall . signature , argc , argv ) ;
2010-07-27 01:51:10 +00:00
error ( " [VM] k%s[%x]: signature mismatch via method %s::%s (script %d, room %d, localCall 0x%x) " ,
2010-07-26 07:55:50 +00:00
kernelCall . name , kernelCallNr , originReply . objectName . c_str ( ) , originReply . methodName . c_str ( ) ,
originReply . scriptNr , s - > currentRoomNumber ( ) , originReply . localCallOffset ) ;
2010-07-20 17:58:04 +00:00
break ;
case WORKAROUND_IGNORE : // don't do kernel call, leave acc alone
2010-07-20 11:45:06 +00:00
return ;
2010-07-20 17:58:04 +00:00
case WORKAROUND_STILLCALL : // call kernel anyway
2010-07-20 11:45:06 +00:00
break ;
2010-07-20 17:58:04 +00:00
case WORKAROUND_FAKE : // don't do kernel call, fake acc
s - > r_acc = make_reg ( 0 , solution . value ) ;
2010-06-30 09:47:04 +00:00
return ;
2010-07-20 11:45:06 +00:00
default :
error ( " unknown workaround type " ) ;
}
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 ) ;
2010-07-23 20:59:27 +00:00
if ( kernelCall . debugLogging )
2010-07-29 17:22:38 +00:00
logKernelCall ( & kernelCall , NULL , s , argc , argv , s - > r_acc ) ;
2010-08-03 19:06:40 +00:00
if ( kernelCall . debugBreakpoint ) {
printf ( " Break on k%s \n " , kernelCall . name ) ;
g_sci - > _debugState . debugging = true ;
g_sci - > _debugState . breakpointWasHit = true ;
}
2010-07-10 18:21:09 +00:00
} 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 ;
2010-07-20 17:58:04 +00:00
SciWorkaroundSolution solution = trackOriginAndFindWorkaround ( 0 , kernelSubCall . workarounds , & originReply ) ;
switch ( solution . type ) {
case WORKAROUND_NONE : {
2010-07-10 18:21:09 +00:00
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 ;
2010-07-26 07:55:50 +00:00
error ( " [VM] k%s(%s): signature mismatch via method %s::%s (script %d, room %d, localCall %x) " ,
kernelCall . name , subCallName , originReply . objectName . c_str ( ) , originReply . methodName . c_str ( ) ,
originReply . scriptNr , s - > currentRoomNumber ( ) , originReply . localCallOffset ) ;
2010-07-09 21:33:12 +00:00
}
2010-07-26 07:55:50 +00:00
error ( " [VM] k%s: signature mismatch via method %s::%s (script %d, room %d, localCall %x) " ,
kernelSubCall . name , originReply . objectName . c_str ( ) , originReply . methodName . c_str ( ) ,
originReply . scriptNr , s - > currentRoomNumber ( ) , originReply . localCallOffset ) ;
2010-07-20 17:58:04 +00:00
break ;
2010-07-09 12:06:41 +00:00
}
2010-07-20 17:58:04 +00:00
case WORKAROUND_IGNORE : // don't do kernel call, leave acc alone
2010-07-20 12:50:13 +00:00
return ;
2010-07-20 17:58:04 +00:00
case WORKAROUND_STILLCALL : // call kernel anyway
2010-07-20 12:50:13 +00:00
break ;
2010-07-20 17:58:04 +00:00
case WORKAROUND_FAKE : // don't do kernel call, fake acc
s - > r_acc = make_reg ( 0 , solution . value ) ;
2010-07-10 18:21:09 +00:00
return ;
2010-07-20 12:50:13 +00:00
default :
error ( " unknown workaround type " ) ;
}
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-07-29 17:22:38 +00:00
if ( kernelSubCall . debugLogging )
logKernelCall ( & kernelCall , & kernelSubCall , s , argc , argv , s - > r_acc ) ;
2010-08-03 19:06:40 +00:00
if ( kernelSubCall . debugBreakpoint ) {
printf ( " Break on k%s \n " , kernelSubCall . name ) ;
g_sci - > _debugState . debugging = true ;
g_sci - > _debugState . breakpointWasHit = true ;
}
2010-07-10 18:21:09 +00:00
}
2010-06-18 14:48:39 +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-08-25 09:02:43 +00:00
assert ( i < 3 ) ;
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-07-20 23:15:07 +00:00
void run_vm ( EngineState * s ) {
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
2010-07-22 12:38:48 +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-08-29 00:39:33 +00:00
if ( s - > abortScriptProcessing ! = kAbortNone )
2010-06-21 20:17:59 +00:00
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 ) {
2010-08-05 16:58:59 +00:00
error ( " Could not find local script from segment %x " , s - > xs - > local_segment ) ;
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-08-29 00:39:33 +00:00
if ( s - > abortScriptProcessing ! = kAbortNone )
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 ( ) ;
2010-07-17 18:38:42 +00:00
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-07-18 20:36:19 +00:00
case op_bnot : { // 0x00 (00)
2010-07-04 14:55:28 +00:00
// Binary not
2010-07-18 18:26:00 +00:00
int16 value ;
if ( validate_signedInteger ( s - > r_acc , value ) )
s - > r_acc = make_reg ( 0 , 0xffff ^ value ) ;
else
s - > r_acc = arithmetic_lookForWorkaround ( opcode , NULL , s - > r_acc , NULL_REG ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-07-18 20:36:19 +00:00
}
2009-02-15 06:10:59 +00:00
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-07-18 18:57:28 +00:00
case op_mul : { // 0x03 (03)
2010-07-18 18:26:00 +00:00
r_temp = POP32 ( ) ;
int16 value1 , value2 ;
if ( validate_signedInteger ( s - > r_acc , value1 ) & & validate_signedInteger ( r_temp , value2 ) )
s - > r_acc = make_reg ( 0 , value1 * value2 ) ;
else
2010-08-04 18:11:11 +00:00
s - > r_acc = arithmetic_lookForWorkaround ( opcode , opcodeMulWorkarounds , s - > r_acc , r_temp ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-07-18 18:57:28 +00:00
}
2009-02-15 06:10:59 +00:00
2010-07-06 07:42:25 +00:00
case op_div : { // 0x04 (04)
2010-07-14 19:12:31 +00:00
r_temp = POP32 ( ) ;
2010-07-18 18:26:00 +00:00
int16 divisor , dividend ;
2010-07-14 19:12:31 +00:00
if ( validate_signedInteger ( s - > r_acc , divisor ) & & validate_signedInteger ( r_temp , dividend ) )
s - > r_acc = make_reg ( 0 , ( divisor ! = 0 ? dividend / divisor : 0 ) ) ;
else
s - > r_acc = arithmetic_lookForWorkaround ( opcode , opcodeDivWorkarounds , s - > r_acc , r_temp ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-07-06 07:42:25 +00:00
}
2010-07-18 18:57:28 +00:00
2010-07-06 07:42:25 +00:00
case op_mod : { // 0x05 (05)
2010-07-18 18:26:00 +00:00
r_temp = POP32 ( ) ;
2010-07-30 15:19:21 +00:00
if ( getSciVersion ( ) < = SCI_VERSION_0_LATE ) {
uint16 modulo , value ;
if ( validate_unsignedInteger ( s - > r_acc , modulo ) & & validate_unsignedInteger ( r_temp , value ) )
s - > r_acc = make_reg ( 0 , ( modulo ! = 0 ? value % modulo : 0 ) ) ;
else
s - > r_acc = arithmetic_lookForWorkaround ( opcode , NULL , s - > r_acc , r_temp ) ;
2010-07-30 14:56:38 +00:00
} else {
2010-07-30 15:19:21 +00:00
// In Iceman (and perhaps from SCI0 0.000.685 onwards in general),
// handling for negative numbers was added. Since Iceman doesn't
// seem to have issues with the older code, we exclude it for now
// for simplicity's sake and use the new code for SCI01 and newer
// games. Fixes the battlecruiser mini game in SQ5 (room 850),
// bug #3035755
int16 modulo , value , result ;
if ( validate_signedInteger ( s - > r_acc , modulo ) & & validate_signedInteger ( r_temp , value ) ) {
modulo = ABS ( modulo ) ;
result = ( modulo ! = 0 ? value % modulo : 0 ) ;
if ( result < 0 )
result + = modulo ;
s - > r_acc = make_reg ( 0 , result ) ;
} else
s - > r_acc = arithmetic_lookForWorkaround ( opcode , NULL , s - > r_acc , r_temp ) ;
2010-07-30 14:56:38 +00:00
}
2009-02-15 06:10:59 +00:00
break ;
2010-07-06 07:42:25 +00:00
}
2010-07-18 18:57:28 +00:00
case op_shr : { // 0x06 (06)
2010-07-04 14:55:28 +00:00
// Shift right logical
2010-07-18 18:57:28 +00:00
r_temp = POP32 ( ) ;
uint16 value , shiftCount ;
if ( validate_unsignedInteger ( r_temp , value ) & & validate_unsignedInteger ( s - > r_acc , shiftCount ) )
s - > r_acc = make_reg ( 0 , value > > shiftCount ) ;
else
s - > r_acc = arithmetic_lookForWorkaround ( opcode , NULL , r_temp , s - > r_acc ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-07-18 18:57:28 +00:00
}
2009-02-15 06:10:59 +00:00
2010-07-18 18:57:28 +00:00
case op_shl : { // 0x07 (07)
2010-07-04 14:55:28 +00:00
// Shift left logical
2010-07-18 18:57:28 +00:00
r_temp = POP32 ( ) ;
uint16 value , shiftCount ;
if ( validate_unsignedInteger ( r_temp , value ) & & validate_unsignedInteger ( s - > r_acc , shiftCount ) )
s - > r_acc = make_reg ( 0 , value < < shiftCount ) ;
else
s - > r_acc = arithmetic_lookForWorkaround ( opcode , NULL , r_temp , s - > r_acc ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-07-18 18:57:28 +00:00
}
2009-02-15 06:10:59 +00:00
2010-07-18 18:57:28 +00:00
case op_xor : { // 0x08 (08)
r_temp = POP32 ( ) ;
uint16 value1 , value2 ;
if ( validate_unsignedInteger ( r_temp , value1 ) & & validate_unsignedInteger ( s - > r_acc , value2 ) )
s - > r_acc = make_reg ( 0 , value1 ^ value2 ) ;
else
s - > r_acc = arithmetic_lookForWorkaround ( opcode , NULL , r_temp , s - > r_acc ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-07-18 18:57:28 +00:00
}
2009-02-15 06:10:59 +00:00
2010-07-18 18:57:28 +00:00
case op_and : { // 0x09 (09)
r_temp = POP32 ( ) ;
uint16 value1 , value2 ;
if ( validate_unsignedInteger ( r_temp , value1 ) & & validate_unsignedInteger ( s - > r_acc , value2 ) )
s - > r_acc = make_reg ( 0 , value1 & value2 ) ;
else
2010-08-06 15:05:05 +00:00
s - > r_acc = arithmetic_lookForWorkaround ( opcode , opcodeAndWorkarounds , r_temp , s - > r_acc ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-07-18 18:57:28 +00:00
}
2009-02-15 06:10:59 +00:00
2010-07-18 18:57:28 +00:00
case op_or : { // 0x0a (10)
r_temp = POP32 ( ) ;
uint16 value1 , value2 ;
if ( validate_unsignedInteger ( r_temp , value1 ) & & validate_unsignedInteger ( s - > r_acc , value2 ) )
s - > r_acc = make_reg ( 0 , value1 | value2 ) ;
else
2010-07-26 05:40:58 +00:00
s - > r_acc = arithmetic_lookForWorkaround ( opcode , opcodeOrWorkarounds , r_temp , s - > r_acc ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-07-18 18:57:28 +00:00
}
2009-02-15 06:10:59 +00:00
2010-07-18 18:57:28 +00:00
case op_neg : { // 0x0b (11)
int16 value ;
if ( validate_signedInteger ( s - > r_acc , value ) )
s - > r_acc = make_reg ( 0 , - value ) ;
else
s - > r_acc = arithmetic_lookForWorkaround ( opcode , NULL , s - > r_acc , NULL_REG ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-07-18 18:57:28 +00:00
}
2009-02-15 06:10:59 +00:00
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 )
2010-07-14 02:02:03 +00:00
error ( " [VM] op_gt: comparison between a pointer and number " ) ;
// Pseudo-WORKAROUND: Sierra allows any pointer <-> value comparison
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-07-18 17:18:56 +00:00
} else {
int16 compare1 , compare2 ;
if ( validate_signedInteger ( r_temp , compare1 ) & & validate_signedInteger ( s - > r_acc , compare2 ) )
s - > r_acc = make_reg ( 0 , compare1 > compare2 ) ;
else
s - > r_acc = arithmetic_lookForWorkaround ( opcode , NULL , r_temp , s - > r_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-07-18 17:18:56 +00:00
} else {
int16 compare1 , compare2 ;
if ( validate_signedInteger ( r_temp , compare1 ) & & validate_signedInteger ( s - > r_acc , compare2 ) )
s - > r_acc = make_reg ( 0 , compare1 > = compare2 ) ;
else
2010-08-03 15:51:31 +00:00
s - > r_acc = arithmetic_lookForWorkaround ( opcode , opcodeGeWorkarounds , r_temp , s - > r_acc ) ;
2010-07-18 17:18:56 +00:00
}
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 )
2010-07-14 02:02:03 +00:00
error ( " [VM] op_lt: comparison between a pointer and number " ) ;
// Pseudo-WORKAROUND: Sierra allows any pointer <-> value comparison
2010-06-25 22:48:55 +00:00
// Happens in SQ1, room 58, when giving id-card to robot
s - > r_acc = make_reg ( 0 , 1 ) ;
2010-07-18 17:18:56 +00:00
} else {
int16 compare1 , compare2 ;
if ( validate_signedInteger ( r_temp , compare1 ) & & validate_signedInteger ( s - > r_acc , compare2 ) )
s - > r_acc = make_reg ( 0 , compare1 < compare2 ) ;
else
s - > r_acc = arithmetic_lookForWorkaround ( opcode , NULL , r_temp , s - > r_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-07-18 17:18:56 +00:00
} else {
int16 compare1 , compare2 ;
if ( validate_signedInteger ( r_temp , compare1 ) & & validate_signedInteger ( s - > r_acc , compare2 ) )
s - > r_acc = make_reg ( 0 , compare1 < = compare2 ) ;
else
2010-08-07 01:09:32 +00:00
s - > r_acc = arithmetic_lookForWorkaround ( opcode , opcodeLeWorkarounds , r_temp , s - > r_acc ) ;
2010-07-18 17:18:56 +00:00
}
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 ) ;
2010-07-18 17:18:56 +00:00
else {
uint16 compare1 , compare2 ;
if ( validate_unsignedInteger ( r_temp , compare1 ) & & validate_unsignedInteger ( s - > r_acc , compare2 ) )
s - > r_acc = make_reg ( 0 , compare1 > compare2 ) ;
else
s - > r_acc = arithmetic_lookForWorkaround ( opcode , NULL , r_temp , s - > r_acc ) ;
}
2010-01-30 04:01:15 +00:00
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 ) ;
2010-07-18 17:18:56 +00:00
else {
uint16 compare1 , compare2 ;
if ( validate_unsignedInteger ( r_temp , compare1 ) & & validate_unsignedInteger ( s - > r_acc , compare2 ) )
s - > r_acc = make_reg ( 0 , compare1 > = compare2 ) ;
else
s - > r_acc = arithmetic_lookForWorkaround ( opcode , NULL , r_temp , s - > r_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-07-19 10:19:13 +00:00
// PQ2 japanese compares pointers to 2000 to find out if its a pointer or a resourceid
if ( r_temp . segment & & ( s - > r_acc = = make_reg ( 0 , 1000 ) | | ( s - > r_acc = = make_reg ( 0 , 2000 ) ) ) )
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 ) ;
2010-07-18 17:18:56 +00:00
else {
uint16 compare1 , compare2 ;
if ( validate_unsignedInteger ( r_temp , compare1 ) & & validate_unsignedInteger ( s - > r_acc , compare2 ) )
s - > r_acc = make_reg ( 0 , compare1 < compare2 ) ;
else
2010-08-19 15:58:39 +00:00
s - > r_acc = arithmetic_lookForWorkaround ( opcode , opcodeUltWorkarounds , r_temp , s - > r_acc ) ;
2010-07-18 17:18:56 +00:00
}
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 ) ;
2010-07-18 17:18:56 +00:00
else {
uint16 compare1 , compare2 ;
if ( validate_unsignedInteger ( r_temp , compare1 ) & & validate_unsignedInteger ( s - > r_acc , compare2 ) )
s - > r_acc = make_reg ( 0 , compare1 < = compare2 ) ;
else
s - > r_acc = arithmetic_lookForWorkaround ( opcode , NULL , r_temp , s - > r_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
2010-08-29 00:39:33 +00:00
if ( s - > abortScriptProcessing ! = kAbortNone )
2010-06-25 17:25:00 +00:00
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-07-18 18:01:52 +00:00
case op_ipToa : { // 0x35 (53)
// Increment Property and copy To Accumulator
reg_t & opProperty = validate_property ( obj , opparams [ 0 ] > > 1 ) ;
uint16 valueProperty ;
if ( validate_unsignedInteger ( opProperty , valueProperty ) )
s - > r_acc = make_reg ( 0 , valueProperty + 1 ) ;
else
s - > r_acc = arithmetic_lookForWorkaround ( opcode , NULL , opProperty , NULL_REG ) ;
opProperty = s - > r_acc ;
2009-02-15 06:10:59 +00:00
break ;
2010-07-18 18:01:52 +00:00
}
2009-02-15 06:10:59 +00:00
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-18 12:25:40 +00:00
reg_t & opProperty = validate_property ( obj , opparams [ 0 ] > > 1 ) ;
uint16 valueProperty ;
if ( validate_unsignedInteger ( opProperty , valueProperty ) )
s - > r_acc = make_reg ( 0 , valueProperty - 1 ) ;
else
s - > r_acc = arithmetic_lookForWorkaround ( opcode , opcodeDptoaWorkarounds , opProperty , NULL_REG ) ;
opProperty = s - > r_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-07-18 18:01:52 +00:00
case op_ipTos : { // 0x37 (55)
2010-07-04 14:55:28 +00:00
// Increment Property and push to Stack
2010-07-18 18:01:52 +00:00
reg_t & opProperty = validate_property ( obj , opparams [ 0 ] > > 1 ) ;
uint16 valueProperty ;
if ( validate_unsignedInteger ( opProperty , valueProperty ) )
valueProperty + + ;
else
valueProperty = arithmetic_lookForWorkaround ( opcode , NULL , opProperty , NULL_REG ) . offset ;
opProperty = make_reg ( 0 , valueProperty ) ;
PUSH ( valueProperty ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-07-18 18:01:52 +00:00
}
2009-02-15 06:10:59 +00:00
2010-07-18 18:01:52 +00:00
case op_dpTos : { // 0x38 (56)
2010-07-04 14:55:28 +00:00
// Decrement Property and push to Stack
2010-07-18 18:01:52 +00:00
reg_t & opProperty = validate_property ( obj , opparams [ 0 ] > > 1 ) ;
uint16 valueProperty ;
if ( validate_unsignedInteger ( opProperty , valueProperty ) )
valueProperty - - ;
else
valueProperty = arithmetic_lookForWorkaround ( opcode , NULL , opProperty , NULL_REG ) . offset ;
opProperty = make_reg ( 0 , valueProperty ) ;
PUSH ( valueProperty ) ;
2009-02-15 06:10:59 +00:00
break ;
2010-07-18 18:01:52 +00:00
}
2009-02-15 06:10:59 +00:00
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)
2010-08-14 08:44:29 +00:00
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
2010-08-14 06:05:54 +00:00
int16 value ;
if ( ! validate_signedInteger ( s - > r_acc , value ) )
value = arithmetic_lookForWorkaround ( opcode , opcodeLaiWorkarounds , s - > r_acc , NULL_REG ) . offset ;
var_number = opparams [ 0 ] + value ;
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-08-14 08:44:29 +00:00
}
2009-02-15 06:10:59 +00:00
2010-01-28 13:07:47 +00:00
case op_lsgi : // 0x4c (76)
case op_lsli : // 0x4d (77)
case op_lsti : // 0x4e (78)
2010-08-06 11:45:32 +00:00
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
2010-08-06 11:45:32 +00:00
int16 value ;
if ( ! validate_signedInteger ( s - > r_acc , value ) )
value = arithmetic_lookForWorkaround ( opcode , opcodeLsiWorkarounds , s - > r_acc , NULL_REG ) . offset ;
var_number = opparams [ 0 ] + value ;
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-08-06 11:45:32 +00:00
}
2009-02-15 06:10:59 +00:00
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