mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
reg32_t was a transitive solution, before reg_t's were adapted to use 32-bit addresses internally, and before support for SCI3 was added. It was introduced as another way to handle large script offsets in SCI3, and was only used for the program counter (PC). It's no longer needed, as we now support SCI3 script offsets using reg_t's, so we can use make_reg32 in all cases where we need to access offsets over 64KB
429 lines
14 KiB
C++
429 lines
14 KiB
C++
/* 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.
|
|
*
|
|
*/
|
|
|
|
#ifndef SCI_ENGINE_VM_H
|
|
#define SCI_ENGINE_VM_H
|
|
|
|
/* VM and kernel declarations */
|
|
|
|
#include "sci/engine/vm_types.h" // for reg_t
|
|
#include "sci/resource.h" // for SciVersion
|
|
|
|
#include "common/util.h"
|
|
|
|
namespace Sci {
|
|
|
|
class SegManager;
|
|
struct EngineState;
|
|
class Object;
|
|
class ResourceManager;
|
|
class Script;
|
|
|
|
/** Number of bytes to be allocated for the stack */
|
|
#define VM_STACK_SIZE 0x1000
|
|
|
|
/** Magical object identifier */
|
|
#define SCRIPT_OBJECT_MAGIC_NUMBER 0x1234
|
|
|
|
/** Offset of this identifier */
|
|
#define SCRIPT_OBJECT_MAGIC_OFFSET (getSciVersion() < SCI_VERSION_1_1 ? -8 : 0)
|
|
|
|
/** Stack pointer value: Use predecessor's value */
|
|
#define CALL_SP_CARRY NULL
|
|
|
|
/** Types of selectors as returned by lookupSelector() below. */
|
|
enum SelectorType {
|
|
kSelectorNone = 0,
|
|
kSelectorVariable,
|
|
kSelectorMethod
|
|
};
|
|
|
|
struct Class {
|
|
int script; ///< number of the script the class is in, -1 for non-existing
|
|
reg_t reg; ///< offset; script-relative offset, segment: 0 if not instantiated
|
|
};
|
|
|
|
// A reference to an object's variable.
|
|
// The object is stored as a reg_t, the variable as an index into _variables
|
|
struct ObjVarRef {
|
|
reg_t obj;
|
|
int varindex;
|
|
|
|
reg_t* getPointer(SegManager *segMan) const;
|
|
};
|
|
|
|
enum ExecStackType {
|
|
EXEC_STACK_TYPE_CALL = 0,
|
|
EXEC_STACK_TYPE_KERNEL = 1,
|
|
EXEC_STACK_TYPE_VARSELECTOR = 2
|
|
};
|
|
|
|
struct ExecStack {
|
|
reg_t objp; ///< Pointer to the beginning of the current object
|
|
reg_t sendp; ///< Pointer to the object containing the invoked method
|
|
|
|
union {
|
|
ObjVarRef varp; // Variable pointer for r/w access
|
|
reg_t pc; // Pointer to the initial program counter. Not accurate for the TOS element
|
|
} addr;
|
|
|
|
StackPtr fp; // Frame pointer
|
|
StackPtr sp; // Stack pointer
|
|
|
|
int argc;
|
|
StackPtr variables_argp; // Argument pointer
|
|
|
|
SegmentId local_segment; // local variables etc
|
|
|
|
Selector debugSelector; // The selector which was used to call or -1 if not applicable
|
|
int debugExportId; // The exportId which was called or -1 if not applicable
|
|
int debugLocalCallOffset; // Local call offset or -1 if not applicable
|
|
int debugOrigin; // The stack frame position the call was made from, or -1 if it was the initial call
|
|
int debugKernelFunction; // The kernel function called, or -1 if not applicable
|
|
int debugKernelSubFunction; // The kernel subfunction called, or -1 if not applicable
|
|
ExecStackType type;
|
|
|
|
reg_t* getVarPointer(SegManager *segMan) const;
|
|
|
|
ExecStack(reg_t objp_, reg_t sendp_, StackPtr sp_, int argc_, StackPtr argp_,
|
|
SegmentId localsSegment_, reg_t pc_, Selector debugSelector_,
|
|
int debugKernelFunction_, int debugKernelSubFunction_,
|
|
int debugExportId_, int debugLocalCallOffset_, int debugOrigin_,
|
|
ExecStackType type_) {
|
|
objp = objp_;
|
|
sendp = sendp_;
|
|
// varp is set separately for varselector calls
|
|
addr.pc = pc_;
|
|
fp = sp = sp_;
|
|
argc = argc_;
|
|
variables_argp = argp_;
|
|
if (localsSegment_ != kUninitializedSegment)
|
|
local_segment = localsSegment_;
|
|
else
|
|
local_segment = pc_.getSegment();
|
|
debugSelector = debugSelector_;
|
|
debugKernelFunction = debugKernelFunction_;
|
|
debugKernelSubFunction = debugKernelSubFunction_;
|
|
debugExportId = debugExportId_;
|
|
debugLocalCallOffset = debugLocalCallOffset_;
|
|
debugOrigin = debugOrigin_;
|
|
type = type_;
|
|
}
|
|
};
|
|
|
|
enum {
|
|
VAR_GLOBAL = 0,
|
|
VAR_LOCAL = 1,
|
|
VAR_TEMP = 2,
|
|
VAR_PARAM = 3
|
|
};
|
|
|
|
enum GlobalVar {
|
|
kGlobalVarEgo = 0,
|
|
kGlobalVarGame = 1,
|
|
kGlobalVarCurrentRoom = 2,
|
|
kGlobalVarSpeed = 3, // SCI16
|
|
kGlobalVarQuit = 4,
|
|
kGlobalVarSounds = 8,
|
|
kGlobalVarPlanes = 10, // SCI32
|
|
kGlobalVarCurrentRoomNo = 11,
|
|
kGlobalVarPreviousRoomNo = 12,
|
|
kGlobalVarNewRoomNo = 13,
|
|
kGlobalVarScore = 15,
|
|
kGlobalVarGK2MusicVolume = 76, // 0 to 127
|
|
kGlobalVarPhant2SecondaryVolume = 76, // 0 to 127
|
|
kGlobalVarFastCast = 84, // SCI16
|
|
kGlobalVarMessageType = 90,
|
|
kGlobalVarTextSpeed = 94, // SCI32; 0 is fastest, 8 is slowest
|
|
kGlobalVarGK1Music1 = 102, // 0 to 127
|
|
kGlobalVarGK1Music2 = 103, // 0 to 127
|
|
kGlobalVarRamaCatalogFile = 130,
|
|
kGlobalVarLSL6HiresGameFlags = 137,
|
|
kGlobalVarGK1NarratorMode = 166, // 0 for text, 1 for speech
|
|
kGlobalVarRamaMusicVolume = 176, // 0 to 16
|
|
kGlobalVarPhant1MusicVolume = 187, // 0 to 15
|
|
kGlobalVarPhant1DACVolume = 188, // 0 to 127
|
|
kGlobalVarLSL6HiresMusicVolume = 194, // 0 to 13
|
|
kGlobalVarGK1DAC1 = 207, // 0 to 127
|
|
kGlobalVarPhant2CensorshipFlag = 207,
|
|
kGlobalVarGK1DAC2 = 208, // 0 to 127
|
|
kGlobalVarLSL6HiresRestoreTextWindow = 210,
|
|
kGlobalVarGK1DAC3 = 211, // 0 to 127
|
|
kGlobalVarShiversFlags = 211,
|
|
kGlobalVarTorinMusicVolume = 227, // 0 to 100
|
|
kGlobalVarTorinSFXVolume = 228, // 0 to 100
|
|
kGlobalVarTorinSpeechVolume = 229, // 0 to 100
|
|
// Phant2 labels its volume slider as "music volume" but it is actually
|
|
// a master volume that affects both music *and* sound effects
|
|
kGlobalVarPhant2MasterVolume = 236, // 0 to 127
|
|
kGlobalVarPhant2ControlPanel = 250,
|
|
kGlobalVarShivers1Score = 349
|
|
};
|
|
|
|
/** Number of kernel calls in between gcs; should be < 50000 */
|
|
enum {
|
|
GC_INTERVAL = 0x8000
|
|
};
|
|
|
|
enum SciOpcodes {
|
|
op_bnot = 0x00, // 000
|
|
op_add = 0x01, // 001
|
|
op_sub = 0x02, // 002
|
|
op_mul = 0x03, // 003
|
|
op_div = 0x04, // 004
|
|
op_mod = 0x05, // 005
|
|
op_shr = 0x06, // 006
|
|
op_shl = 0x07, // 007
|
|
op_xor = 0x08, // 008
|
|
op_and = 0x09, // 009
|
|
op_or = 0x0a, // 010
|
|
op_neg = 0x0b, // 011
|
|
op_not = 0x0c, // 012
|
|
op_eq_ = 0x0d, // 013
|
|
op_ne_ = 0x0e, // 014
|
|
op_gt_ = 0x0f, // 015
|
|
op_ge_ = 0x10, // 016
|
|
op_lt_ = 0x11, // 017
|
|
op_le_ = 0x12, // 018
|
|
op_ugt_ = 0x13, // 019
|
|
op_uge_ = 0x14, // 020
|
|
op_ult_ = 0x15, // 021
|
|
op_ule_ = 0x16, // 022
|
|
op_bt = 0x17, // 023
|
|
op_bnt = 0x18, // 024
|
|
op_jmp = 0x19, // 025
|
|
op_ldi = 0x1a, // 026
|
|
op_push = 0x1b, // 027
|
|
op_pushi = 0x1c, // 028
|
|
op_toss = 0x1d, // 029
|
|
op_dup = 0x1e, // 030
|
|
op_link = 0x1f, // 031
|
|
op_call = 0x20, // 032
|
|
op_callk = 0x21, // 033
|
|
op_callb = 0x22, // 034
|
|
op_calle = 0x23, // 035
|
|
op_ret = 0x24, // 036
|
|
op_send = 0x25, // 037
|
|
op_info = 0x26, // 038
|
|
op_superP = 0x27, // 039
|
|
op_class = 0x28, // 040
|
|
// dummy 0x29, // 041
|
|
op_self = 0x2a, // 042
|
|
op_super = 0x2b, // 043
|
|
op_rest = 0x2c, // 044
|
|
op_lea = 0x2d, // 045
|
|
op_selfID = 0x2e, // 046
|
|
// dummy 0x2f // 047
|
|
op_pprev = 0x30, // 048
|
|
op_pToa = 0x31, // 049
|
|
op_aTop = 0x32, // 050
|
|
op_pTos = 0x33, // 051
|
|
op_sTop = 0x34, // 052
|
|
op_ipToa = 0x35, // 053
|
|
op_dpToa = 0x36, // 054
|
|
op_ipTos = 0x37, // 055
|
|
op_dpTos = 0x38, // 056
|
|
op_lofsa = 0x39, // 057
|
|
op_lofss = 0x3a, // 058
|
|
op_push0 = 0x3b, // 059
|
|
op_push1 = 0x3c, // 060
|
|
op_push2 = 0x3d, // 061
|
|
op_pushSelf = 0x3e, // 062
|
|
op_line = 0x3f, // 063
|
|
//
|
|
op_lag = 0x40, // 064
|
|
op_lal = 0x41, // 065
|
|
op_lat = 0x42, // 066
|
|
op_lap = 0x43, // 067
|
|
op_lsg = 0x44, // 068
|
|
op_lsl = 0x45, // 069
|
|
op_lst = 0x46, // 070
|
|
op_lsp = 0x47, // 071
|
|
op_lagi = 0x48, // 072
|
|
op_lali = 0x49, // 073
|
|
op_lati = 0x4a, // 074
|
|
op_lapi = 0x4b, // 075
|
|
op_lsgi = 0x4c, // 076
|
|
op_lsli = 0x4d, // 077
|
|
op_lsti = 0x4e, // 078
|
|
op_lspi = 0x4f, // 079
|
|
//
|
|
op_sag = 0x50, // 080
|
|
op_sal = 0x51, // 081
|
|
op_sat = 0x52, // 082
|
|
op_sap = 0x53, // 083
|
|
op_ssg = 0x54, // 084
|
|
op_ssl = 0x55, // 085
|
|
op_sst = 0x56, // 086
|
|
op_ssp = 0x57, // 087
|
|
op_sagi = 0x58, // 088
|
|
op_sali = 0x59, // 089
|
|
op_sati = 0x5a, // 090
|
|
op_sapi = 0x5b, // 091
|
|
op_ssgi = 0x5c, // 092
|
|
op_ssli = 0x5d, // 093
|
|
op_ssti = 0x5e, // 094
|
|
op_sspi = 0x5f, // 095
|
|
//
|
|
op_plusag = 0x60, // 096
|
|
op_plusal = 0x61, // 097
|
|
op_plusat = 0x62, // 098
|
|
op_plusap = 0x63, // 099
|
|
op_plussg = 0x64, // 100
|
|
op_plussl = 0x65, // 101
|
|
op_plusst = 0x66, // 102
|
|
op_plussp = 0x67, // 103
|
|
op_plusagi = 0x68, // 104
|
|
op_plusali = 0x69, // 105
|
|
op_plusati = 0x6a, // 106
|
|
op_plusapi = 0x6b, // 107
|
|
op_plussgi = 0x6c, // 108
|
|
op_plussli = 0x6d, // 109
|
|
op_plussti = 0x6e, // 110
|
|
op_plusspi = 0x6f, // 111
|
|
//
|
|
op_minusag = 0x70, // 112
|
|
op_minusal = 0x71, // 113
|
|
op_minusat = 0x72, // 114
|
|
op_minusap = 0x73, // 115
|
|
op_minussg = 0x74, // 116
|
|
op_minussl = 0x75, // 117
|
|
op_minusst = 0x76, // 118
|
|
op_minussp = 0x77, // 119
|
|
op_minusagi = 0x78, // 120
|
|
op_minusali = 0x79, // 121
|
|
op_minusati = 0x7a, // 122
|
|
op_minusapi = 0x7b, // 123
|
|
op_minussgi = 0x7c, // 124
|
|
op_minussli = 0x7d, // 125
|
|
op_minussti = 0x7e, // 126
|
|
op_minusspi = 0x7f // 127
|
|
};
|
|
|
|
void script_adjust_opcode_formats();
|
|
|
|
/**
|
|
* Executes function pubfunct of the specified script.
|
|
* @param[in] s The state which is to be executed with
|
|
* @param[in] script The script which is called
|
|
* @param[in] pubfunct The exported script function which is to
|
|
* be called
|
|
* @param[in] sp Stack pointer position
|
|
* @param[in] calling_obj The heap address of the object that
|
|
* executed the call
|
|
* @param[in] argc Number of arguments supplied
|
|
* @param[in] argp Pointer to the first supplied argument
|
|
* @return A pointer to the new exec stack TOS entry
|
|
*/
|
|
ExecStack *execute_method(EngineState *s, uint16 script, uint16 pubfunct,
|
|
StackPtr sp, reg_t calling_obj, uint16 argc, StackPtr argp);
|
|
|
|
|
|
/**
|
|
* Executes a "send" or related operation to a selector.
|
|
* @param[in] s The EngineState to operate on
|
|
* @param[in] send_obj Heap address of the object to send to
|
|
* @param[in] work_obj Heap address of the object initiating the send
|
|
* @param[in] sp Stack pointer position
|
|
* @param[in] framesize Size of the send as determined by the "send"
|
|
* operation
|
|
* @param[in] argp Pointer to the beginning of the heap block
|
|
* containing the data to be sent. This area is a
|
|
* succession of one or more sequences of
|
|
* [selector_number][argument_counter] and then
|
|
* "argument_counter" word entries with the
|
|
* parameter values.
|
|
* @return A pointer to the new execution stack TOS entry
|
|
*/
|
|
ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj,
|
|
StackPtr sp, int framesize, StackPtr argp);
|
|
|
|
|
|
/**
|
|
* This function executes SCI bytecode
|
|
* It executes the code on s->heap[pc] until it hits a 'ret' operation
|
|
* while (stack_base == stack_pos). Requires s to be set up correctly.
|
|
* @param[in] s The state to use
|
|
*/
|
|
void run_vm(EngineState *s);
|
|
|
|
/**
|
|
* Debugger functionality
|
|
* @param[in] s The state at which debugging should take place
|
|
*/
|
|
void script_debug(EngineState *s);
|
|
|
|
/**
|
|
* Looks up a selector and returns its type and value
|
|
* varindex is written to iff it is non-NULL and the selector indicates a property of the object.
|
|
* @param[in] segMan The Segment Manager
|
|
* @param[in] obj Address of the object to look the selector up in
|
|
* @param[in] selectorid The selector to look up
|
|
* @param[out] varp A reference to the selector, if it is a
|
|
* variable.
|
|
* @param[out] fptr A reference to the function described by that
|
|
* selector, if it is a valid function selector.
|
|
* fptr is written to iff it is non-NULL and the
|
|
* selector indicates a member function of that
|
|
* object.
|
|
* @return kSelectorNone if the selector was not found in
|
|
* the object or its superclasses.
|
|
* kSelectorVariable if the selector represents an
|
|
* object-relative variable.
|
|
* kSelectorMethod if the selector represents a
|
|
* method
|
|
*/
|
|
SelectorType lookupSelector(SegManager *segMan, reg_t obj, Selector selectorid,
|
|
ObjVarRef *varp, reg_t *fptr);
|
|
|
|
/**
|
|
* Read a PMachine instruction from a memory buffer and return its length.
|
|
*
|
|
* @param[in] src address from which to start parsing
|
|
* @param[out] extOpcode "extended" opcode of the parsed instruction
|
|
* @param[out] opparams parameter for the parsed instruction
|
|
* @return the length in bytes of the instruction
|
|
*
|
|
* @todo How about changing opparams from int16 to int / int32 to preserve
|
|
* unsigned 16bit words as read for Script_Word? In the past, this
|
|
* was irrelevant as only a debug opcode used Script_Word. But with
|
|
* SCI32 we are now using Script_Word for more opcodes. Maybe this is
|
|
* just a mistake and those opcodes should used Script_SWord -- but if
|
|
* not then we definitely should change this to int, else we might run
|
|
* into trouble if we encounter high value words. *If* those exist at all.
|
|
*/
|
|
int readPMachineInstruction(const byte *src, byte &extOpcode, int16 opparams[4]);
|
|
|
|
/**
|
|
* Finds the script-absolute offset of a relative object offset.
|
|
*
|
|
* @param[in] relOffset the relative object offset
|
|
* @param[in] scr the owner script object, used by SCI1.1+
|
|
* @param[in] pcOffset the offset of the program counter, used by SCI0early and
|
|
* SCI3
|
|
*/
|
|
uint32 findOffset(const int16 relOffset, const Script *scr, const uint32 pcOffset);
|
|
|
|
} // End of namespace Sci
|
|
|
|
#endif // SCI_ENGINE_VM_H
|