aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/engine/vm.h
blob: c80070409b680e077ad4fa6da7dc4b996ce1b70a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
/* 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,
	kGlobalVarKQ7UpscaleVideos     = 160,
	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,
	kGlobalVarQFG4Flags            = 500,
	kGlobalVarHoyle5MusicVolume    = 897,
	kGlobalVarHoyle5ResponseTime  = 899
};

/** 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