1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 * Virtual processor.
22 */
23
24 #include "tinsel/dw.h"
25 #include "tinsel/drives.h"
26 #include "tinsel/events.h" // 'POINTED' etc.
27 #include "tinsel/handle.h" // LockMem()
28 #include "tinsel/dialogs.h" // for inventory id's
29 #include "tinsel/pcode.h" // opcodes etc.
30 #include "tinsel/scn.h" // FindChunk()
31 #include "common/serializer.h"
32 #include "tinsel/timers.h"
33 #include "tinsel/tinlib.h" // Library routines
34 #include "tinsel/tinsel.h"
35
36 #include "common/textconsole.h"
37 #include "common/util.h"
38
39 namespace Tinsel {
40
41 //----------------- EXTERN FUNCTIONS --------------------
42
43 extern int CallLibraryRoutine(CORO_PARAM, int operand, int32 *pp, const INT_CONTEXT *pic, RESUME_STATE *pResumeState);
44
45 //----------------- LOCAL DEFINES --------------------
46
47 #define GLOBALS_FILENAME "gdata" // name of globals file
48
49 /** list of all opcodes */
50 enum OPCODE {
51 OP_HALT = 0, ///< end of program
52 OP_IMM = 1, ///< loads signed immediate onto stack
53 OP_ZERO = 2, ///< loads zero onto stack
54 OP_ONE = 3, ///< loads one onto stack
55 OP_MINUSONE = 4, ///< loads minus one onto stack
56 OP_STR = 5, ///< loads string offset onto stack
57 OP_FILM = 6, ///< loads film offset onto stack
58 OP_FONT = 7, ///< loads font offset onto stack
59 OP_PAL = 8, ///< loads palette offset onto stack
60 OP_LOAD = 9, ///< loads local variable onto stack
61 OP_GLOAD = 10, ///< loads global variable onto stack - long offset to variable
62 OP_STORE = 11, ///< pops stack and stores in local variable - long offset to variable
63 OP_GSTORE = 12, ///< pops stack and stores in global variable - long offset to variable
64 OP_CALL = 13, ///< procedure call
65 OP_LIBCALL = 14, ///< library procedure call - long offset to procedure
66 OP_RET = 15, ///< procedure return
67 OP_ALLOC = 16, ///< allocate storage on stack
68 OP_JUMP = 17, ///< unconditional jump - signed word offset
69 OP_JMPFALSE = 18, ///< conditional jump - signed word offset
70 OP_JMPTRUE = 19, ///< conditional jump - signed word offset
71 OP_EQUAL = 20, ///< tests top two items on stack for equality
72 OP_LESS, ///< tests top two items on stack
73 OP_LEQUAL, ///< tests top two items on stack
74 OP_NEQUAL, ///< tests top two items on stack
75 OP_GEQUAL, ///< tests top two items on stack
76 OP_GREAT = 25, ///< tests top two items on stack
77 OP_PLUS, ///< adds top two items on stack and replaces with result
78 OP_MINUS, ///< subs top two items on stack and replaces with result
79 OP_LOR, ///< logical or of top two items on stack and replaces with result
80 OP_MULT, ///< multiplies top two items on stack and replaces with result
81 OP_DIV = 30, ///< divides top two items on stack and replaces with result
82 OP_MOD, ///< divides top two items on stack and replaces with modulus
83 OP_AND, ///< bitwise ands top two items on stack and replaces with result
84 OP_OR, ///< bitwise ors top two items on stack and replaces with result
85 OP_EOR, ///< bitwise exclusive ors top two items on stack and replaces with result
86 OP_LAND = 35, ///< logical ands top two items on stack and replaces with result
87 OP_NOT, ///< logical nots top item on stack
88 OP_COMP, ///< complements top item on stack
89 OP_NEG, ///< negates top item on stack
90 OP_DUP, ///< duplicates top item on stack
91 OP_ESCON = 40, ///< start of escapable sequence
92 OP_ESCOFF = 41, ///< end of escapable sequence
93 OP_CIMM, ///< loads signed immediate onto stack (special to case statements)
94 OP_CDFILM ///< loads film offset onto stack but not in current scene
95 };
96
97 // modifiers for the above opcodes
98 #define OPSIZE8 0x40 ///< when this bit is set - the operand size is 8 bits
99 #define OPSIZE16 0x80 ///< when this bit is set - the operand size is 16 bits
100
101 #define OPMASK 0x3F ///< mask to isolate the opcode
102
103 bool g_bNoPause = false;
104
105 //----------------- LOCAL GLOBAL DATA --------------------
106
107 // FIXME: Avoid non-const global vars
108
109 static int32 *g_pGlobals = 0; // global vars
110
111 static int g_numGlobals = 0; // How many global variables to save/restore
112
113 static INT_CONTEXT *g_icList = 0;
114
115 static uint32 g_hMasterScript;
116
117 //----------------- SCRIPT BUGS WORKAROUNDS --------------
118
119 /**
120 * This structure is used to introduce bug fixes into the scripts used by the games.
121 */
122 struct WorkaroundEntry {
123 TinselEngineVersion version; ///< Engine version this workaround applies to
124 bool scnFlag; ///< Only applicable for Tinsel 1 (DW 1)
125 bool isDemo; ///< Flags whether it's for a demo
126 Common::Platform platform; ///< Platform filter
127 SCNHANDLE hCode; ///< Script to apply fragment to
128 int ip; ///< Script offset to run this fragment before
129 int numBytes; ///< Number of bytes in the script
130 const byte *script; ///< Instruction(s) to execute
131 };
132
133 #define FRAGMENT_WORD(x) (byte)(x & 0xFF), (byte)(x >> 8)
134 #define FRAGMENT_DWORD(x) (byte)(x & 0xFF), (byte)(x >> 8), (byte)(x >> 16), (byte)(x >> 24)
135
136 static const byte fragment1[] = {OP_ZERO, OP_GSTORE | OPSIZE16, 206, 0};
137 static const byte fragment2[] = {OP_LIBCALL | OPSIZE8, 110};
138 static const byte fragment3[] = {OP_ZERO, OP_GSTORE | OPSIZE16, FRAGMENT_WORD(490)};
139 static const byte fragment4[] = {OP_IMM | OPSIZE16, FRAGMENT_WORD(900), OP_JUMP | OPSIZE16, FRAGMENT_WORD(466)};
140 static const byte fragment5[] = {OP_IMM | OPSIZE16, FRAGMENT_WORD(901), OP_JUMP | OPSIZE16, FRAGMENT_WORD(488)};
141 static const byte fragment6[] = {OP_IMM | OPSIZE16, FRAGMENT_WORD(903), OP_JUMP | OPSIZE16, FRAGMENT_WORD(516)};
142 static const byte fragment7[] = {OP_IMM | OPSIZE16, FRAGMENT_WORD(908), OP_JUMP | OPSIZE16, FRAGMENT_WORD(616)};
143 static const byte fragment8[] = {OP_IMM | OPSIZE16, FRAGMENT_WORD(910), OP_JUMP | OPSIZE16, FRAGMENT_WORD(644)};
144 static const byte fragment9[] = {OP_JUMP | OPSIZE8, 123};
145 static const byte fragment10[] = {OP_IMM | OPSIZE16, FRAGMENT_WORD(160), OP_JUMP | OPSIZE16, FRAGMENT_WORD(136)};
146 static const byte fragment11[] = {OP_JMPTRUE | OPSIZE16, FRAGMENT_WORD(1572),
147 OP_ONE, OP_LIBCALL | OPSIZE8, 14, // Re-show the cursor
148 OP_IMM | OPSIZE16, FRAGMENT_WORD(322), OP_LIBCALL | OPSIZE8, 46, // Give back the whistle
149 OP_JUMP | OPSIZE16, FRAGMENT_WORD(1661)};
150 static const byte fragment12[] = {OP_JMPTRUE | OPSIZE16, FRAGMENT_WORD(1491),
151 OP_ONE, OP_LIBCALL | OPSIZE8, 14, // Re-show the cursor
152 OP_IMM | OPSIZE16, FRAGMENT_WORD(322), OP_LIBCALL | OPSIZE8, 46, // Give back the whistle
153 OP_JUMP | OPSIZE16, FRAGMENT_WORD(1568)};
154 static const byte fragment13[] = {OP_ZERO, OP_GSTORE | OPSIZE16, FRAGMENT_WORD(306)};
155 static const byte fragment14[] = {OP_LIBCALL | OPSIZE8, 58,
156 OP_IMM, FRAGMENT_DWORD((42 << 23)), OP_ONE, OP_ZERO, OP_LIBCALL | OPSIZE8, 44,
157 OP_LIBCALL | OPSIZE8, 97, OP_JUMP | OPSIZE16, FRAGMENT_WORD(2220)
158 };
159 static const byte fragment15[] = { OP_JMPFALSE | OPSIZE16, FRAGMENT_WORD(154) };
160
161 #undef FRAGMENT_WORD
162
163 const WorkaroundEntry workaroundList[] = {
164 // DW1-SCN: Global 206 is whether Rincewind is trying to take the
165 // book back to the present. In the GRA version, it was global 373,
166 // and was reset when he is returned to the past, but was forgotten
167 // in the SCN version, so this ensures the flag is properly reset.
168 {TINSEL_V1, true, false, Common::kPlatformUnknown, 427942095, 1, sizeof(fragment1), fragment1},
169
170 // DW1-GRA: Rincewind exiting the Inn is blocked by the luggage.
171 // Whilst you can then move into walkable areas, saving and
172 // restoring the game, it will error if you try to move. This
173 // fragment turns off NPC blocking for the Outside Inn rooms so that
174 // the luggage won't block Past Outside Inn.
175 // See bug report #2525010.
176 {TINSEL_V1, false, false, Common::kPlatformUnknown, 444622076, 0, sizeof(fragment2), fragment2},
177 // Present Outside Inn
178 {TINSEL_V1, false, false, Common::kPlatformUnknown, 352600876, 0, sizeof(fragment2), fragment2},
179
180 // DW1-GRA: Talking to palace guards in Act 2 gives !!!HIGH
181 // STRING||| - this happens if you initiate dialog with one of the
182 // guards, but not the other. So these fragments provide the correct
183 // talk parameters where needed.
184 // See bug report #2831159.
185 {TINSEL_V1, false, false, Common::kPlatformUnknown, 310506872, 463, sizeof(fragment4), fragment4},
186 {TINSEL_V1, false, false, Common::kPlatformUnknown, 310506872, 485, sizeof(fragment5), fragment5},
187 {TINSEL_V1, false, false, Common::kPlatformUnknown, 310506872, 513, sizeof(fragment6), fragment6},
188 {TINSEL_V1, false, false, Common::kPlatformUnknown, 310506872, 613, sizeof(fragment7), fragment7},
189 {TINSEL_V1, false, false, Common::kPlatformUnknown, 310506872, 641, sizeof(fragment8), fragment8},
190
191 // DW1-SCN: The script for the lovable street-Starfish does a
192 // 'StopSample' after flicking the coin to ensure it's sound is
193 // stopped, but which also accidentally can stop any active
194 // conversation with the Amazon.
195 {TINSEL_V1, true, false, Common::kPlatformUnknown, 394640351, 121, sizeof(fragment9), fragment9},
196
197 // DW2: In the garden, global #490 is set when the bees begin their
198 // 'out of hive' animation, and reset when done. But if the game is
199 // saved/restored during it, the animation sequence is reset without
200 // the global being cleared. This causes bugs in several actions
201 // which try to disable the bees animation, since they wait
202 // indefinitely for the global to be cleared, incorrectly believing
203 // the animation is currently playing. This includes:
204 // * Giving the brochure to the beekeeper (bug #2680397)
205 // * Stealing the mallets from the wizards (bug #2820788).
206 // This fix ensures that the global is reset when the Garden scene
207 // is loaded (both entering and restoring a game).
208 {TINSEL_V2, true, false, Common::kPlatformUnknown, 2888147476U, 0, sizeof(fragment3), fragment3},
209
210 // DW1-GRA: Corrects text being drawn partially off-screen during
211 // the blackboard description of the Librarian.
212 {TINSEL_V1, false, false, Common::kPlatformUnknown, 293831402, 133, sizeof(fragment10), fragment10},
213
214 // DW1-GRA/SCN: Corrects the dead-end of being able to give the
215 // whistle back to the pirate before giving him the parrot.
216 // See bug report #2934211.
217 {TINSEL_V1, true, false, Common::kPlatformUnknown, 352601285, 1569, sizeof(fragment11), fragment11},
218 {TINSEL_V1, false, false, Common::kPlatformUnknown, 352602304, 1488, sizeof(fragment12), fragment12},
219
220 // DW2: Corrects a bug with global 306 not being cleared if you leave
221 // the marketplace scene whilst D'Blah is talking (even if it's not
222 // actually audible); returning to the scene and clicking on him multiple
223 // times would cause the game to crash
224 {TINSEL_V2, true, false, Common::kPlatformUnknown, 1109294728, 0, sizeof(fragment13), fragment13},
225
226 // DW1 PSX DEMO: Alters a script in the PSX DW1 demo to show the Idle animation scene rather than
227 // quitting the game when no user input happens for a while
228 {TINSEL_V1, true, true, Common::kPlatformPSX, 0, 2186, sizeof(fragment14), fragment14},
229
230 // DW1-GRA: Fixes hang in Temple, when trying to use items on the big hammer
231 {TINSEL_V1, false, false, Common::kPlatformUnknown, 276915849, 0x98, sizeof(fragment15), fragment15},
232
233 {TINSEL_V0, false, false, Common::kPlatformUnknown, 0, 0, 0, NULL}
234 };
235
236 //----------------- LOCAL GLOBAL DATA --------------------
237
238 /**
239 * Keeps the code array pointer up to date.
240 */
LockCode(INT_CONTEXT * ic)241 void LockCode(INT_CONTEXT *ic) {
242 if (ic->GSort == GS_MASTER) {
243 if (TinselV2)
244 // Get the srcipt handle from a specific global chunk
245 ic->code = (byte *)LockMem(g_hMasterScript);
246 else
247 ic->code = (byte *)FindChunk(MASTER_SCNHANDLE, CHUNK_PCODE);
248 } else
249 ic->code = (byte *)LockMem(ic->hCode);
250 }
251
252 /**
253 * Find a free interpret context and allocate it to the calling process.
254 */
AllocateInterpretContext(GSORT gsort)255 static INT_CONTEXT *AllocateInterpretContext(GSORT gsort) {
256 INT_CONTEXT *pic;
257 int i;
258
259 for (i = 0, pic = g_icList; i < NUM_INTERPRET; i++, pic++) {
260 if (pic->GSort == GS_NONE) {
261 pic->pProc = CoroScheduler.getCurrentProcess();
262 pic->GSort = gsort;
263 return pic;
264 }
265 #ifdef DEBUG
266 else {
267 if (pic->pProc == CoroScheduler.getCurrentProcess())
268 error("Found unreleased interpret context");
269 }
270 #endif
271 }
272
273 error("Out of interpret contexts");
274 }
275
FreeWaitCheck(PINT_CONTEXT pic,bool bVoluntary)276 static void FreeWaitCheck(PINT_CONTEXT pic, bool bVoluntary) {
277 int i;
278
279 // Is this waiting for something?
280 if (pic->waitNumber1) {
281 for (i = 0; i < NUM_INTERPRET; i++) {
282 if ((g_icList + i)->waitNumber2 == pic->waitNumber1) {
283 (g_icList + i)->waitNumber2 = 0;
284 break;
285 }
286 }
287 }
288
289 // Is someone waiting for this?
290 if (pic->waitNumber2) {
291 for (i = 0; i < NUM_INTERPRET; i++) {
292 if ((g_icList + i)->waitNumber1 == pic->waitNumber2) {
293 (g_icList + i)->waitNumber1 = 0;
294 (g_icList + i)->resumeCode = bVoluntary ? RES_FINISHED : RES_CUTSHORT;
295 CoroScheduler.reschedule((g_icList + i)->pProc);
296 break;
297 }
298 }
299 assert(i < NUM_INTERPRET);
300 }
301 }
302
303 /**
304 * Normal release of an interpret context.
305 * Called from the end of Interpret().
306 */
FreeInterpretContextPi(INT_CONTEXT * pic)307 static void FreeInterpretContextPi(INT_CONTEXT *pic) {
308 FreeWaitCheck(pic, true);
309 if (TinselV2)
310 memset(pic, 0, sizeof(INT_CONTEXT));
311 pic->GSort = GS_NONE;
312 }
313
314 /**
315 * Free interpret context owned by a dying process.
316 * Ensures that interpret contexts don't get lost when an Interpret()
317 * call doesn't complete.
318 */
FreeInterpretContextPr(Common::PROCESS * pProc)319 void FreeInterpretContextPr(Common::PROCESS *pProc) {
320 INT_CONTEXT *pic;
321 int i;
322
323 for (i = 0, pic = g_icList; i < NUM_INTERPRET; i++, pic++) {
324 if (pic->GSort != GS_NONE && pic->pProc == pProc) {
325 FreeWaitCheck(pic, false);
326 if (TinselV2)
327 memset(pic, 0, sizeof(INT_CONTEXT));
328 pic->GSort = GS_NONE;
329 break;
330 }
331 }
332 }
333
334 /**
335 * Free all interpret contexts except for the master script's
336 */
FreeMostInterpretContexts()337 void FreeMostInterpretContexts() {
338 INT_CONTEXT *pic;
339 int i;
340
341 for (i = 0, pic = g_icList; i < NUM_INTERPRET; i++, pic++) {
342 if ((pic->GSort != GS_MASTER) && (pic->GSort != GS_GPROCESS)) {
343 memset(pic, 0, sizeof(INT_CONTEXT));
344 pic->GSort = GS_NONE;
345 }
346 }
347 }
348
349 /**
350 * Free the master script's interpret context.
351 */
FreeMasterInterpretContext()352 void FreeMasterInterpretContext() {
353 INT_CONTEXT *pic;
354 int i;
355
356 for (i = 0, pic = g_icList; i < NUM_INTERPRET; i++, pic++) {
357 if ((pic->GSort == GS_MASTER) || (pic->GSort == GS_GPROCESS)) {
358 memset(pic, 0, sizeof(INT_CONTEXT));
359 pic->GSort = GS_NONE;
360 return;
361 }
362 }
363 }
364
365 /**
366 * Allocate and initialize an interpret context.
367 * Called from a process prior to Interpret().
368 * @param gsort which sort of code
369 * @param hCode Handle to code to execute
370 * @param event Causal event
371 * @param hpoly Associated polygon (if any)
372 * @param actorId Associated actor (if any)
373 * @param pinvo Associated inventory object
374 */
InitInterpretContext(GSORT gsort,SCNHANDLE hCode,TINSEL_EVENT event,HPOLYGON hpoly,int actorid,INV_OBJECT * pinvo,int myEscape)375 INT_CONTEXT *InitInterpretContext(GSORT gsort, SCNHANDLE hCode, TINSEL_EVENT event,
376 HPOLYGON hpoly, int actorid, INV_OBJECT *pinvo, int myEscape) {
377 INT_CONTEXT *ic;
378
379 ic = AllocateInterpretContext(gsort);
380
381 // Previously parameters to Interpret()
382 ic->hCode = hCode;
383 LockCode(ic);
384 ic->event = event;
385 ic->hPoly = hpoly;
386 ic->idActor = actorid;
387 ic->pinvo = pinvo;
388
389 // Previously local variables in Interpret()
390 ic->bHalt = false; // set to exit interpeter
391 ic->escOn = myEscape > 0;
392 ic->myEscape = myEscape;
393 ic->sp = 0;
394 ic->bp = ic->sp + 1;
395 ic->ip = 0; // start of code
396
397 ic->resumeState = RES_NOT;
398
399 return ic;
400 }
401
402 /**
403 * Allocate and initialize an interpret context with restored data.
404 */
RestoreInterpretContext(INT_CONTEXT * ric)405 INT_CONTEXT *RestoreInterpretContext(INT_CONTEXT *ric) {
406 INT_CONTEXT *ic;
407
408 ic = AllocateInterpretContext(GS_NONE); // Sort will soon be overridden
409
410 memcpy(ic, ric, sizeof(INT_CONTEXT));
411 ic->pProc = CoroScheduler.getCurrentProcess();
412 ic->resumeState = RES_1;
413
414 LockCode(ic);
415
416 return ic;
417 }
418
419 /**
420 * Allocates enough RAM to hold the global Glitter variables.
421 */
RegisterGlobals(int num)422 void RegisterGlobals(int num) {
423 if (g_pGlobals == NULL) {
424 g_numGlobals = num;
425
426 g_hMasterScript = !TinselV2 ? 0 :
427 READ_32(FindChunk(MASTER_SCNHANDLE, CHUNK_MASTER_SCRIPT));
428
429 // Allocate RAM for pGlobals and make sure it's allocated
430 g_pGlobals = (int32 *)calloc(g_numGlobals, sizeof(int32));
431 if (g_pGlobals == NULL) {
432 error("Cannot allocate memory for global data");
433 }
434
435 // Allocate RAM for interpret contexts and make sure it's allocated
436 g_icList = (INT_CONTEXT *)calloc(NUM_INTERPRET, sizeof(INT_CONTEXT));
437 if (g_icList == NULL) {
438 error("Cannot allocate memory for interpret contexts");
439 }
440 CoroScheduler.setResourceCallback(FreeInterpretContextPr);
441 } else {
442 // Check size is still the same
443 assert(g_numGlobals == num);
444
445 memset(g_pGlobals, 0, g_numGlobals * sizeof(int32));
446 memset(g_icList, 0, NUM_INTERPRET * sizeof(INT_CONTEXT));
447 }
448
449 if (TinselV2) {
450 // read initial values
451 CdCD(Common::nullContext);
452
453 Common::File f;
454 if (!f.open(GLOBALS_FILENAME))
455 error(CANNOT_FIND_FILE, GLOBALS_FILENAME);
456
457 int32 length = f.readSint32LE();
458 if (length != num)
459 error(FILE_IS_CORRUPT, GLOBALS_FILENAME);
460
461 for (int i = 0; i < length; ++i)
462 g_pGlobals[i] = f.readSint32LE();
463
464 if (f.eos() || f.err())
465 error(FILE_IS_CORRUPT, GLOBALS_FILENAME);
466
467 f.close();
468 }
469 }
470
FreeGlobals()471 void FreeGlobals() {
472 free(g_pGlobals);
473 g_pGlobals = NULL;
474
475 free(g_icList);
476 g_icList = NULL;
477 }
478
479 /**
480 * (Un)serialize the global data for save/restore game.
481 */
syncGlobInfo(Common::Serializer & s)482 void syncGlobInfo(Common::Serializer &s) {
483 for (int i = 0; i < g_numGlobals; i++) {
484 s.syncAsSint32LE(g_pGlobals[i]);
485 }
486 }
487
488 /**
489 * (Un)serialize an interpreter context for save/restore game.
490 */
syncWithSerializer(Common::Serializer & s)491 void INT_CONTEXT::syncWithSerializer(Common::Serializer &s) {
492 if (s.isLoading()) {
493 // Null out the pointer fields
494 pProc = NULL;
495 code = NULL;
496 pinvo = NULL;
497 }
498 // Write out used fields
499 s.syncAsUint32LE(GSort);
500 s.syncAsUint32LE(hCode);
501 s.syncAsUint32LE(event);
502 s.syncAsSint32LE(hPoly);
503 s.syncAsSint32LE(idActor);
504
505 for (int i = 0; i < PCODE_STACK_SIZE; ++i)
506 s.syncAsSint32LE(stack[i]);
507
508 s.syncAsSint32LE(sp);
509 s.syncAsSint32LE(bp);
510 s.syncAsSint32LE(ip);
511 s.syncAsUint32LE(bHalt);
512 s.syncAsUint32LE(escOn);
513 s.syncAsSint32LE(myEscape);
514 }
515
516 /**
517 * Return pointer to and size of global data for save/restore game.
518 */
SaveInterpretContexts(INT_CONTEXT * sICInfo)519 void SaveInterpretContexts(INT_CONTEXT *sICInfo) {
520 memcpy(sICInfo, g_icList, NUM_INTERPRET * sizeof(INT_CONTEXT));
521 }
522
523 /**
524 * Fetches up to 4 bytes from the code script
525 */
GetBytes(const byte * scriptCode,const WorkaroundEntry * & wkEntry,int & ip,uint numBytes)526 static int32 GetBytes(const byte *scriptCode, const WorkaroundEntry* &wkEntry, int &ip, uint numBytes) {
527 assert(numBytes <= 4 && numBytes != 3);
528 const byte *code = scriptCode;
529
530 if (wkEntry != NULL) {
531 if (ip >= wkEntry->numBytes) {
532 // Finished the workaround
533 ip = wkEntry->ip;
534 wkEntry = NULL;
535 } else {
536 code = wkEntry->script;
537 }
538 }
539
540 uint32 tmp;
541 switch (numBytes) {
542 case 0:
543 // Instruction byte
544 tmp = code[ip++ * (TinselV0 ? 4 : 1)];
545 break;
546 case 1:
547 // Fetch and sign extend a 8 bit value to 32 bits.
548 tmp = (int8)code[ip++];
549 break;
550 case 2:
551 // Fetch and sign extend a 16 bit value to 32 bits.
552 tmp = (int16)READ_LE_UINT16(code + ip);
553 ip += 2;
554 break;
555 default:
556 if (TinselV0)
557 tmp = (int32)READ_LE_UINT32(code + ip++ * 4);
558 else {
559 tmp = (int32)READ_LE_UINT32(code + ip);
560 ip += 4;
561 }
562 break;
563 }
564
565 return tmp;
566 }
567
568 /**
569 * Fetch (and sign extend, if necessary) a 8/16/32 bit value from the code
570 * stream and advance the instruction pointer accordingly.
571 */
Fetch(byte opcode,const byte * code,const WorkaroundEntry * & wkEntry,int & ip)572 static int32 Fetch(byte opcode, const byte *code, const WorkaroundEntry* &wkEntry, int &ip) {
573 if (TinselV0)
574 // Fetch a 32 bit value.
575 return GetBytes(code, wkEntry, ip, 4);
576 else if (opcode & OPSIZE8)
577 // Fetch and sign extend a 8 bit value to 32 bits.
578 return GetBytes(code, wkEntry, ip, 1);
579 else if (opcode & OPSIZE16)
580 return GetBytes(code, wkEntry, ip, 2);
581
582 return GetBytes(code, wkEntry, ip, 4);
583 }
584
585 /**
586 * Interprets the PCODE instructions in the code array.
587 */
Interpret(CORO_PARAM,INT_CONTEXT * ic)588 void Interpret(CORO_PARAM, INT_CONTEXT *ic) {
589 do {
590 int tmp, tmp2;
591 int ip = ic->ip;
592 const WorkaroundEntry *wkEntry = ic->fragmentPtr;
593
594 if (wkEntry == NULL) {
595 // Check to see if a workaround fragment needs to be executed
596 for (wkEntry = workaroundList; wkEntry->script != NULL; ++wkEntry) {
597 if ((wkEntry->version == TinselVersion) &&
598 (wkEntry->hCode == ic->hCode) &&
599 (wkEntry->ip == ip) &&
600 (wkEntry->isDemo == _vm->getIsADGFDemo()) &&
601 ((wkEntry->platform == Common::kPlatformUnknown) || (wkEntry->platform == _vm->getPlatform())) &&
602 (!TinselV1 || (wkEntry->scnFlag == ((_vm->getFeatures() & GF_SCNFILES) != 0)))) {
603 // Point to start of workaround fragment
604 ip = 0;
605 break;
606 }
607 }
608 if (wkEntry->script == NULL)
609 wkEntry = NULL;
610 }
611
612 byte opcode = (byte)GetBytes(ic->code, wkEntry, ip, 0);
613 if (TinselV0 && ((opcode & OPMASK) > OP_IMM))
614 opcode += 3;
615
616 debug(7, "ip=%d Opcode %d (-> %d)", ic->ip, opcode, opcode & OPMASK);
617 switch (opcode & OPMASK) {
618 case OP_HALT: // end of program
619
620 ic->bHalt = true;
621 break;
622
623 case OP_IMM: // loads immediate data onto stack
624 case OP_STR: // loads string handle onto stack
625 case OP_FILM: // loads film handle onto stack
626 case OP_CDFILM: // loads film handle onto stack
627 case OP_FONT: // loads font handle onto stack
628 case OP_PAL: // loads palette handle onto stack
629
630 ic->stack[++ic->sp] = Fetch(opcode, ic->code, wkEntry, ip);
631 break;
632
633 case OP_ZERO: // loads zero onto stack
634 ic->stack[++ic->sp] = 0;
635 break;
636
637 case OP_ONE: // loads one onto stack
638 ic->stack[++ic->sp] = 1;
639 break;
640
641 case OP_MINUSONE: // loads minus one onto stack
642 ic->stack[++ic->sp] = -1;
643 break;
644
645 case OP_LOAD: // loads local variable onto stack
646
647 ic->stack[++ic->sp] = ic->stack[ic->bp + Fetch(opcode, ic->code, wkEntry, ip)];
648 break;
649
650 case OP_GLOAD: // loads global variable onto stack
651
652 tmp = Fetch(opcode, ic->code, wkEntry, ip);
653 assert(0 <= tmp && tmp < g_numGlobals);
654 ic->stack[++ic->sp] = g_pGlobals[tmp];
655 break;
656
657 case OP_STORE: // pops stack and stores in local variable
658
659 ic->stack[ic->bp + Fetch(opcode, ic->code, wkEntry, ip)] = ic->stack[ic->sp--];
660 break;
661
662 case OP_GSTORE: // pops stack and stores in global variable
663
664 tmp = Fetch(opcode, ic->code, wkEntry, ip);
665 assert(0 <= tmp && tmp < g_numGlobals);
666 g_pGlobals[tmp] = ic->stack[ic->sp--];
667 break;
668
669 case OP_CALL: // procedure call
670
671 tmp = Fetch(opcode, ic->code, wkEntry, ip);
672 //assert(0 <= tmp && tmp < codeSize); // TODO: Verify jumps are not out of bounds
673 ic->stack[ic->sp + 1] = 0; // static link
674 ic->stack[ic->sp + 2] = ic->bp; // dynamic link
675 ic->stack[ic->sp + 3] = ip; // return address
676 ic->bp = ic->sp + 1; // set new base pointer
677 ip = tmp; // set ip to procedure address
678 break;
679
680 case OP_LIBCALL: // library procedure or function call
681
682 tmp = Fetch(opcode, ic->code, wkEntry, ip);
683 // NOTE: Interpret() itself is not using the coroutine facilities,
684 // but still accepts a CORO_PARAM, so from the outside it looks
685 // like a coroutine. In fact it may still acts as a kind of "proxy"
686 // for some underlying coroutine. To enable this, we just pass on
687 // 'coroParam' to CallLibraryRoutine(). If we then detect that
688 // coroParam was set to a non-zero value, this means that some
689 // coroutine code did run at some point, and we are now supposed
690 // to sleep or die -- hence, we 'return' if coroParam != 0.
691 //
692 // This works because Interpret() is fully re-entrant: If we return
693 // now and are later called again, then we will end up in the very
694 // same spot (i.e. here).
695 //
696 // The reasons we do it this way, instead of turning Interpret into
697 // a 'proper' coroutine are (1) we avoid implementation problems
698 // (CORO_INVOKE involves adding 'case' statements, but Interpret
699 // already has a huge switch/case, so that would not work out of the
700 // box), (2) we incurr less overhead, (3) it's easier to debug,
701 // (4) it's simply cool ;).
702 tmp2 = CallLibraryRoutine(coroParam, tmp, &ic->stack[ic->sp], ic, &ic->resumeState);
703 if (coroParam)
704 return;
705 if (!TinselV0)
706 ic->sp += tmp2;
707 LockCode(ic);
708 if (TinselV2 && (ic->resumeState == RES_1))
709 ic->resumeState = RES_NOT;
710 break;
711
712 case OP_RET: // procedure return
713
714 ic->sp = ic->bp - 1; // restore stack
715 ip = ic->stack[ic->sp + 3]; // return address
716 ic->bp = ic->stack[ic->sp + 2]; // restore previous base pointer
717 break;
718
719 case OP_ALLOC: // allocate storage on stack
720
721 ic->sp += (int32)Fetch(opcode, ic->code, wkEntry, ip);
722 break;
723
724 case OP_JUMP: // unconditional jump
725
726 ip = Fetch(opcode, ic->code, wkEntry, ip);
727 wkEntry = NULL; // In case a jump occurs from a workaround
728 break;
729
730 case OP_JMPFALSE: // conditional jump
731
732 tmp = Fetch(opcode, ic->code, wkEntry, ip);
733 if (ic->stack[ic->sp--] == 0) {
734 // condition satisfied - do the jump
735 ip = tmp;
736 wkEntry = NULL; // In case a jump occurs from a workaround
737 }
738 break;
739
740 case OP_JMPTRUE: // conditional jump
741
742 tmp = Fetch(opcode, ic->code, wkEntry, ip);
743 if (ic->stack[ic->sp--] != 0) {
744 // condition satisfied - do the jump
745 ip = tmp;
746 wkEntry = NULL; // In case a jump occurs from a workaround
747 }
748 break;
749
750 case OP_EQUAL: // tests top two items on stack for equality
751 case OP_LESS: // tests top two items on stack
752 case OP_LEQUAL: // tests top two items on stack
753 case OP_NEQUAL: // tests top two items on stack
754 case OP_GEQUAL: // tests top two items on stack
755 case OP_GREAT: // tests top two items on stack
756 case OP_LOR: // logical or of top two items on stack and replaces with result
757 case OP_LAND: // logical ands top two items on stack and replaces with result
758
759 // pop one operand
760 ic->sp--;
761 assert(ic->sp >= 0);
762 tmp = ic->stack[ic->sp];
763 tmp2 = ic->stack[ic->sp + 1];
764
765 // replace other operand with result of operation
766 switch (opcode) {
767 case OP_EQUAL: tmp = (tmp == tmp2); break;
768 case OP_LESS: tmp = (tmp < tmp2); break;
769 case OP_LEQUAL: tmp = (tmp <= tmp2); break;
770 case OP_NEQUAL: tmp = (tmp != tmp2); break;
771 case OP_GEQUAL: tmp = (tmp >= tmp2); break;
772 case OP_GREAT: tmp = (tmp > tmp2); break;
773
774 case OP_LOR: tmp = (tmp || tmp2); break;
775 case OP_LAND: tmp = (tmp && tmp2); break;
776 }
777
778 ic->stack[ic->sp] = tmp;
779 break;
780
781 case OP_PLUS: // adds top two items on stack and replaces with result
782 case OP_MINUS: // subs top two items on stack and replaces with result
783 case OP_MULT: // multiplies top two items on stack and replaces with result
784 case OP_DIV: // divides top two items on stack and replaces with result
785 case OP_MOD: // divides top two items on stack and replaces with modulus
786 case OP_AND: // bitwise ands top two items on stack and replaces with result
787 case OP_OR: // bitwise ors top two items on stack and replaces with result
788 case OP_EOR: // bitwise exclusive ors top two items on stack and replaces with result
789
790 // pop one operand
791 ic->sp--;
792 assert(ic->sp >= 0);
793 tmp = ic->stack[ic->sp];
794 tmp2 = ic->stack[ic->sp + 1];
795
796 // replace other operand with result of operation
797 switch (opcode) {
798 case OP_PLUS: tmp += tmp2; break;
799 case OP_MINUS: tmp -= tmp2; break;
800 case OP_MULT: tmp *= tmp2; break;
801 case OP_DIV: tmp /= tmp2; break;
802 case OP_MOD: tmp %= tmp2; break;
803 case OP_AND: tmp &= tmp2; break;
804 case OP_OR: tmp |= tmp2; break;
805 case OP_EOR: tmp ^= tmp2; break;
806 }
807 ic->stack[ic->sp] = tmp;
808 break;
809
810 case OP_NOT: // logical nots top item on stack
811
812 ic->stack[ic->sp] = !ic->stack[ic->sp];
813 break;
814
815 case OP_COMP: // complements top item on stack
816 ic->stack[ic->sp] = ~ic->stack[ic->sp];
817 break;
818
819 case OP_NEG: // negates top item on stack
820 ic->stack[ic->sp] = -ic->stack[ic->sp];
821 break;
822
823 case OP_DUP: // duplicates top item on stack
824 ic->stack[ic->sp + 1] = ic->stack[ic->sp];
825 ic->sp++;
826 break;
827
828 case OP_ESCON:
829 g_bNoPause = true;
830 ic->escOn = true;
831 ic->myEscape = GetEscEvents();
832 break;
833
834 case OP_ESCOFF:
835 ic->escOn = false;
836 ic->myEscape = 0;
837 break;
838
839 default:
840 error("Interpret() - Unknown opcode");
841 }
842
843 // check for stack under-overflow
844 assert(ic->sp >= 0 && ic->sp < PCODE_STACK_SIZE);
845 ic->ip = ip;
846 ic->fragmentPtr = wkEntry;
847 } while (!ic->bHalt);
848
849 // make sure stack is unwound
850 assert(ic->sp == 0);
851
852 FreeInterpretContextPi(ic);
853 }
854
855 /**
856 * Associates an interpret context with the
857 * process that will run it.
858 */
AttachInterpret(INT_CONTEXT * pic,Common::PROCESS * pProc)859 void AttachInterpret(INT_CONTEXT *pic, Common::PROCESS *pProc) {
860 // Attach the process which is using this context
861 pic->pProc = pProc;
862 }
863
864 /**
865 * Generate a number that isn't being used.
866 */
UniqueWaitNumber()867 static uint32 UniqueWaitNumber() {
868 uint32 retval;
869 int i;
870
871 for (retval = DwGetCurrentTime(); 1; retval--) {
872 if (retval == 0)
873 retval = (uint32)-1;
874
875 for (i = 0; i < NUM_INTERPRET; i++) {
876 if ((g_icList+i)->waitNumber1 == retval
877 || (g_icList+i)->waitNumber2 == retval)
878 break;
879 }
880
881 if (i == NUM_INTERPRET)
882 return retval;
883 }
884 }
885
886 /**
887 * WaitInterpret
888 */
WaitInterpret(CORO_PARAM,Common::PPROCESS pWaitProc,bool * result)889 void WaitInterpret(CORO_PARAM, Common::PPROCESS pWaitProc, bool *result) {
890 int i;
891 Common::PPROCESS currentProcess = CoroScheduler.getCurrentProcess();
892 assert(currentProcess);
893 assert(currentProcess != pWaitProc);
894 if (result) *result = false;
895
896 /*
897 * Calling process is the waiter, find its interpret context.
898 */
899
900 CORO_BEGIN_CONTEXT;
901 PINT_CONTEXT picWaiter, picWaitee;
902 CORO_END_CONTEXT(_ctx);
903
904
905 CORO_BEGIN_CODE(_ctx);
906
907 for (i = 0, _ctx->picWaiter = g_icList; i < NUM_INTERPRET; i++, _ctx->picWaiter++) {
908 if (_ctx->picWaiter->GSort != GS_NONE && _ctx->picWaiter->pProc == currentProcess) {
909 break;
910 }
911 }
912
913 /*
914 * Find the interpret context of the process we're waiting for
915 */
916 for (i = 0, _ctx->picWaitee = g_icList; i < NUM_INTERPRET; i++, _ctx->picWaitee++) {
917 if (_ctx->picWaitee->GSort != GS_NONE && _ctx->picWaitee->pProc == pWaitProc) {
918 break;
919 }
920 }
921
922 /*
923 * Set the first as waiting for the second
924 */
925 assert(_ctx->picWaitee->waitNumber2 == 0);
926 _ctx->picWaiter->waitNumber1 = _ctx->picWaitee->waitNumber2 = UniqueWaitNumber();
927 _ctx->picWaiter->resumeCode = RES_WAITING;
928
929 /*
930 * Wait for it
931 */
932 CORO_GIVE_WAY;
933 while (_ctx->picWaiter->resumeCode == RES_WAITING) {
934 CORO_SLEEP(1);
935 }
936
937 if (result)
938 *result = (_ctx->picWaiter->resumeCode == RES_FINISHED);
939 CORO_END_CODE;
940 }
941
942 /**
943 * CheckOutWaiters
944 */
CheckOutWaiters()945 void CheckOutWaiters() {
946 int i, j;
947
948 // Check all waited for have someone waiting
949 for (i = 0; i < NUM_INTERPRET; i++) {
950 // If someone is supposedly waiting for this one
951 if ((g_icList + i)->GSort != GS_NONE && (g_icList + i)->waitNumber2) {
952 // Someone really must be waiting for this one
953 for (j = 0; j < NUM_INTERPRET; j++) {
954 if ((g_icList + j)->GSort != GS_NONE
955 && (g_icList + j)->waitNumber1 == (g_icList + i)->waitNumber2) {
956 break;
957 }
958 }
959 assert(j < NUM_INTERPRET);
960 }
961 }
962
963 // Check waiting for someone to wait for
964 for (i = 0; i < NUM_INTERPRET; i++) {
965 // If someone is supposedly waiting for this one
966 if ((g_icList + i)->GSort != GS_NONE && (g_icList + i)->waitNumber1) {
967 // Someone really must be waiting for this one
968 for (j = 0; j < NUM_INTERPRET; j++) {
969 if ((g_icList + j)->GSort != GS_NONE
970 && (g_icList + j)->waitNumber2 == (g_icList + i)->waitNumber1) {
971 break;
972 }
973 }
974 assert(j < NUM_INTERPRET);
975 }
976 }
977 }
978
979 } // End of namespace Tinsel
980