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  * Additional copyright for this file:
8  * Copyright (C) 1994-1998 Revolution Software Ltd.
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23  */
24 
25 
26 #include "common/util.h"
27 #include "common/stack.h"
28 #include "common/textconsole.h"
29 
30 #include "sword2/sword2.h"
31 #include "sword2/header.h"
32 #include "sword2/defs.h"
33 #include "sword2/interpreter.h"
34 #include "sword2/logic.h"
35 #include "sword2/memory.h"
36 #include "sword2/resman.h"
37 
38 namespace Sword2 {
39 
40 #define STACK_SIZE 10
41 
42 // The machine code table
43 
44 #ifndef REDUCE_MEMORY_USAGE
45 #	define OPCODE(x)	{ &Logic::x, #x }
46 #else
47 #	define OPCODE(x)	{ &Logic::x, "" }
48 #endif
49 
setupOpcodes()50 void Logic::setupOpcodes() {
51 	static const OpcodeEntry opcodes[] = {
52 		/* 00 */
53 		OPCODE(fnTestFunction),
54 		OPCODE(fnTestFlags),
55 		OPCODE(fnRegisterStartPoint),
56 		OPCODE(fnInitBackground),
57 		/* 04 */
58 		OPCODE(fnSetSession),
59 		OPCODE(fnBackSprite),
60 		OPCODE(fnSortSprite),
61 		OPCODE(fnForeSprite),
62 		/* 08 */
63 		OPCODE(fnRegisterMouse),
64 		OPCODE(fnAnim),
65 		OPCODE(fnRandom),
66 		OPCODE(fnPreLoad),
67 		/* 0C */
68 		OPCODE(fnAddSubject),
69 		OPCODE(fnInteract),
70 		OPCODE(fnChoose),
71 		OPCODE(fnWalk),
72 		/* 10 */
73 		OPCODE(fnWalkToAnim),
74 		OPCODE(fnTurn),
75 		OPCODE(fnStandAt),
76 		OPCODE(fnStand),
77 		/* 14 */
78 		OPCODE(fnStandAfterAnim),
79 		OPCODE(fnPause),
80 		OPCODE(fnMegaTableAnim),
81 		OPCODE(fnAddMenuObject),
82 		/* 18 */
83 		OPCODE(fnStartConversation),
84 		OPCODE(fnEndConversation),
85 		OPCODE(fnSetFrame),
86 		OPCODE(fnRandomPause),
87 		/* 1C */
88 		OPCODE(fnRegisterFrame),
89 		OPCODE(fnNoSprite),
90 		OPCODE(fnSendSync),
91 		OPCODE(fnUpdatePlayerStats),
92 		/* 20 */
93 		OPCODE(fnPassGraph),
94 		OPCODE(fnInitFloorMouse),
95 		OPCODE(fnPassMega),
96 		OPCODE(fnFaceXY),
97 		/* 24 */
98 		OPCODE(fnEndSession),
99 		OPCODE(fnNoHuman),
100 		OPCODE(fnAddHuman),
101 		OPCODE(fnWeWait),
102 		/* 28 */
103 		OPCODE(fnTheyDoWeWait),
104 		OPCODE(fnTheyDo),
105 		OPCODE(fnWalkToTalkToMega),
106 		OPCODE(fnFadeDown),
107 		/* 2C */
108 		OPCODE(fnISpeak),
109 		OPCODE(fnTotalRestart),
110 		OPCODE(fnSetWalkGrid),
111 		OPCODE(fnSpeechProcess),
112 		/* 30 */
113 		OPCODE(fnSetScaling),
114 		OPCODE(fnStartEvent),
115 		OPCODE(fnCheckEventWaiting),
116 		OPCODE(fnRequestSpeech),
117 		/* 34 */
118 		OPCODE(fnGosub),
119 		OPCODE(fnTimedWait),
120 		OPCODE(fnPlayFx),
121 		OPCODE(fnStopFx),
122 		/* 38 */
123 		OPCODE(fnPlayMusic),
124 		OPCODE(fnStopMusic),
125 		OPCODE(fnSetValue),
126 		OPCODE(fnNewScript),
127 		/* 3C */
128 		OPCODE(fnGetSync),
129 		OPCODE(fnWaitSync),
130 		OPCODE(fnRegisterWalkGrid),
131 		OPCODE(fnReverseMegaTableAnim),
132 		/* 40 */
133 		OPCODE(fnReverseAnim),
134 		OPCODE(fnAddToKillList),
135 		OPCODE(fnSetStandbyCoords),
136 		OPCODE(fnBackPar0Sprite),
137 		/* 44 */
138 		OPCODE(fnBackPar1Sprite),
139 		OPCODE(fnForePar0Sprite),
140 		OPCODE(fnForePar1Sprite),
141 		OPCODE(fnSetPlayerActionEvent),
142 		/* 48 */
143 		OPCODE(fnSetScrollCoordinate),
144 		OPCODE(fnStandAtAnim),
145 		OPCODE(fnSetScrollLeftMouse),
146 		OPCODE(fnSetScrollRightMouse),
147 		/* 4C */
148 		OPCODE(fnColor),
149 		OPCODE(fnFlash),
150 		OPCODE(fnPreFetch),
151 		OPCODE(fnGetPlayerSaveData),
152 		/* 50 */
153 		OPCODE(fnPassPlayerSaveData),
154 		OPCODE(fnSendEvent),
155 		OPCODE(fnAddWalkGrid),
156 		OPCODE(fnRemoveWalkGrid),
157 		/* 54 */
158 		OPCODE(fnCheckForEvent),
159 		OPCODE(fnPauseForEvent),
160 		OPCODE(fnClearEvent),
161 		OPCODE(fnFaceMega),
162 		/* 58 */
163 		OPCODE(fnPlaySequence),
164 		OPCODE(fnShadedSprite),
165 		OPCODE(fnUnshadedSprite),
166 		OPCODE(fnFadeUp),
167 		/* 5C */
168 		OPCODE(fnDisplayMsg),
169 		OPCODE(fnSetObjectHeld),
170 		OPCODE(fnAddSequenceText),
171 		OPCODE(fnResetGlobals),
172 		/* 60 */
173 		OPCODE(fnSetPalette),
174 		OPCODE(fnRegisterPointerText),
175 		OPCODE(fnFetchWait),
176 		OPCODE(fnRelease),
177 		/* 64 */
178 		OPCODE(fnPrepareMusic),
179 		OPCODE(fnSoundFetch),
180 		OPCODE(fnPrepareMusic),	// Again, apparently
181 		OPCODE(fnSmackerLeadIn),
182 		/* 68 */
183 		OPCODE(fnSmackerLeadOut),
184 		OPCODE(fnStopAllFx),
185 		OPCODE(fnCheckPlayerActivity),
186 		OPCODE(fnResetPlayerActivityDelay),
187 		/* 6C */
188 		OPCODE(fnCheckMusicPlaying),
189 		OPCODE(fnPlayCredits),
190 		OPCODE(fnSetScrollSpeedNormal),
191 		OPCODE(fnSetScrollSpeedSlow),
192 		/* 70 */
193 		OPCODE(fnRemoveChooser),
194 		OPCODE(fnSetFxVolAndPan),
195 		OPCODE(fnSetFxVol),
196 		OPCODE(fnRestoreGame),
197 		/* 74 */
198 		OPCODE(fnRefreshInventory),
199 		OPCODE(fnChangeShadows)
200 	};
201 
202 	_numOpcodes = ARRAYSIZE(opcodes);
203 	_opcodes = opcodes;
204 }
205 
runResScript(uint32 scriptRes,uint32 offset)206 int Logic::runResScript(uint32 scriptRes, uint32 offset) {
207 	byte *scriptAddr;
208 	int result;
209 
210 	scriptAddr = _vm->_resman->openResource(scriptRes);
211 	result = runScript(scriptAddr, scriptAddr, offset);
212 	_vm->_resman->closeResource(scriptRes);
213 
214 	return result;
215 }
216 
runResObjScript(uint32 scriptRes,uint32 objRes,uint32 offset)217 int Logic::runResObjScript(uint32 scriptRes, uint32 objRes, uint32 offset) {
218 	byte *scriptAddr, *objAddr;
219 	int result;
220 
221 	scriptAddr = _vm->_resman->openResource(scriptRes);
222 	objAddr = _vm->_resman->openResource(objRes);
223 	result = runScript(scriptAddr, objAddr, offset);
224 	_vm->_resman->closeResource(objRes);
225 	_vm->_resman->closeResource(scriptRes);
226 
227 	return result;
228 }
229 
runScript(byte * scriptData,byte * objectData,uint32 offset)230 int Logic::runScript(byte *scriptData, byte *objectData, uint32 offset) {
231 	byte pc[4];
232 
233 	WRITE_LE_UINT32(pc, offset);
234 	return runScript2(scriptData, objectData, pc);
235 }
236 
237 // This form of the runScript function is only called directly from
238 // the processSession() function, which uses it to update the script PC in the
239 // current object hub. For reasons which I do not understand, I couldn't get it
240 // to work if I called the function first with a dummy offset variable, and
241 // and then updated the object hub myself.
242 
runScript2(byte * scriptData,byte * objectData,byte * offsetPtr)243 int Logic::runScript2(byte *scriptData, byte *objectData, byte *offsetPtr) {
244 	int i;
245 
246 	// Interestingly, unlike our BASS engine the stack is a local variable.
247 	// I don't know whether or not this is relevant to the working of the
248 	// BS2 engine.
249 
250 	Common::FixedStack<int32, STACK_SIZE> stack;
251 	int32 opcodeParams[STACK_SIZE];
252 
253 	uint32 offset = READ_LE_UINT32(offsetPtr);
254 
255 	ResHeader header;
256 
257 	header.read(scriptData);
258 
259 	scriptData += ResHeader::size() + ObjectHub::size();
260 
261 	// The script data format:
262 	//	int32_TYPE	1		Size of variable space in bytes
263 	//	...				The variable space
264 	//	int32_TYPE	1		numberOfScripts
265 	//	int32_TYPE	numberOfScripts	The offsets for each script
266 
267 	// Initialize some stuff
268 
269 	uint32 ip = 0;			 // Code pointer
270 	int scriptNumber;
271 
272 	// Get the start of variables and start of code
273 
274 	byte *localVars = scriptData + 4;
275 	byte *code = scriptData + READ_LE_UINT32(scriptData) + 4;
276 	uint32 noScripts = READ_LE_UINT32(code);
277 
278 	code += 4;
279 
280 	byte *offsetTable = code;
281 
282 	if (offset < noScripts) {
283 		ip = READ_LE_UINT32(offsetTable + offset * 4);
284 		scriptNumber = offset;
285 		debug(8, "Starting script %d from %d", scriptNumber, ip);
286 	} else {
287 		ip = offset;
288 
289 		for (i = 1; i < (int)noScripts; i++) {
290 			if (READ_LE_UINT32(offsetTable + 4 * i) >= ip)
291 				break;
292 		}
293 
294 		scriptNumber = i - 1;
295 		debug(8, "Resuming script %d from %d", scriptNumber, ip);
296 	}
297 
298 	// There are a couple of known script bugs related to interacting with
299 	// certain objects. We try to work around a few of them.
300 
301 	bool checkMopBug = false;
302 	bool checkPyramidBug = false;
303 	bool checkElevatorBug = false;
304 	bool checkPearlBug = false;
305 
306 	if (scriptNumber == 2) {
307 		if (strcmp((char *)header.name, "mop_73") == 0)
308 			checkMopBug = true;
309 		else if (strcmp((char *)header.name, "titipoco_81") == 0)
310 			checkPyramidBug = true;
311 		else if (strcmp((char *)header.name, "lift_82") == 0)
312 			checkElevatorBug = true;
313 		else if (strcmp((char *)header.name, "pearl_31") == 0)
314 			checkPearlBug = true;
315 	}
316 
317 	code += noScripts * 4;
318 
319 	// Code should now be pointing at an identifier and a checksum
320 	byte *checksumBlock = code;
321 
322 	code += 4 * 3;
323 
324 	if (READ_LE_UINT32(checksumBlock) != 12345678) {
325 		error("Invalid script in object %s", header.name);
326 		//return 0;
327 	}
328 
329 	int32 codeLen = READ_LE_UINT32(checksumBlock + 4);
330 	int32 checksum = 0;
331 
332 	for (i = 0; i < codeLen; i++)
333 		checksum += (unsigned char)code[i];
334 
335 	if (checksum != (int32)READ_LE_UINT32(checksumBlock + 8)) {
336 		debug(1, "Checksum error in object %s", header.name);
337 		// This could be bad, but there has been a report about someone
338 		// who had problems running the German version because of
339 		// checksum errors. Could there be a version where checksums
340 		// weren't properly calculated?
341 	}
342 
343 	bool runningScript = true;
344 
345 	int parameterReturnedFromMcodeFunction = 0;	// Allow scripts to return things
346 	int savedStartOfMcode = 0;	// For saving start of mcode commands
347 
348 	while (runningScript) {
349 		int32 a, b;
350 		int curCommand, parameter, value; // Command and parameter variables
351 		int retVal;
352 		int caseCount;
353 		bool foundCase;
354 		byte *ptr;
355 
356 		curCommand = code[ip++];
357 
358 		switch (curCommand) {
359 
360 		// Script-related opcodes
361 
362 		case CP_END_SCRIPT:
363 			// End the script
364 			runningScript = false;
365 
366 			// WORKAROUND: The dreaded pyramid bug makes the torch
367 			// untakeable when you speak to Titipoco. This is
368 			// because one of the conditions for the torch to be
369 			// takeable is that Titipoco isn't doing anything out
370 			// of the ordinary. Global variable 913 has to be 0 to
371 			// signify that he is in his "idle" state.
372 			//
373 			// Unfortunately, simply the act of speaking to him
374 			// sets variable 913 to 1 (probably to stop him from
375 			// turning around every now and then). The script may
376 			// then go on to set the variable to different values
377 			// to trigger various behaviors in him, but if you
378 			// have run out of these cases the script won't ever
379 			// set it back to 0 again.
380 			//
381 			// So if his click hander finishes, and variable 913 is
382 			// 1, we set it back to 0 manually.
383 
384 			if (checkPyramidBug && readVar(913) == 1) {
385 				warning("Working around pyramid bug: Resetting Titipoco's state");
386 				writeVar(913, 0);
387 			}
388 
389 			// WORKAROUND: The not-so-known-but-should-be-dreaded
390 			// elevator bug.
391 			//
392 			// The click handler for the top of the elevator only
393 			// handles using the elevator, not examining it. When
394 			// examining it, the mouse cursor is removed but never
395 			// restored.
396 
397 			if (checkElevatorBug && readVar(RIGHT_BUTTON)) {
398 				warning("Working around elevator bug: Restoring mouse pointer");
399 				fnAddHuman(NULL);
400 			}
401 
402 			debug(9, "CP_END_SCRIPT");
403 			break;
404 		case CP_QUIT:
405 			// Quit out for a cycle
406 			WRITE_LE_UINT32(offsetPtr, ip);
407 			debug(9, "CP_QUIT");
408 			return 0;
409 		case CP_TERMINATE:
410 			// Quit out immediately without affecting the offset
411 			// pointer
412 			debug(9, "CP_TERMINATE");
413 			return 3;
414 		case CP_RESTART_SCRIPT:
415 			// Start the script again
416 			ip = FROM_LE_32(offsetTable[scriptNumber]);
417 			debug(9, "CP_RESTART_SCRIPT");
418 			break;
419 
420 		// Stack-related opcodes
421 
422 		case CP_PUSH_INT32:
423 			// Push a long word value on to the stack
424 			Read32ip(parameter);
425 			stack.push(parameter);
426 			debug(9, "CP_PUSH_INT32: %d", parameter);
427 			break;
428 		case CP_PUSH_LOCAL_VAR32:
429 			// Push the contents of a local variable
430 			Read16ip(parameter);
431 			stack.push(READ_LE_UINT32(localVars + parameter));
432 			debug(9, "CP_PUSH_LOCAL_VAR32: localVars[%d] => %d", parameter / 4, READ_LE_UINT32(localVars + parameter));
433 			break;
434 		case CP_PUSH_GLOBAL_VAR32:
435 			// Push a global variable
436 			Read16ip(parameter);
437 			stack.push(readVar(parameter));
438 			debug(9, "CP_PUSH_GLOBAL_VAR32: scriptVars[%d] => %d", parameter, readVar(parameter));
439 			break;
440 		case CP_PUSH_LOCAL_ADDR:
441 			// Push the address of a local variable
442 
443 			// From what I understand, some scripts store data
444 			// (e.g. mouse pointers) in their local variable space
445 			// from the very beginning, and use this mechanism to
446 			// pass that data to the opcode function. I don't yet
447 			// know the conceptual difference between this and the
448 			// CP_PUSH_DEREFERENCED_STRUCTURE opcode.
449 
450 			Read16ip(parameter);
451 			stack.push(_vm->_memory->encodePtr(localVars + parameter));
452 			debug(9, "CP_PUSH_LOCAL_ADDR: &localVars[%d] => %p", parameter / 4, (void *)(localVars + parameter));
453 			break;
454 		case CP_PUSH_STRING:
455 			// Push the address of a string on to the stack
456 			// Get the string size
457 			Read8ip(parameter);
458 
459 			// ip now points to the string
460 			ptr = code + ip;
461 			stack.push(_vm->_memory->encodePtr(ptr));
462 			debug(9, "CP_PUSH_STRING: \"%s\"", ptr);
463 			ip += (parameter + 1);
464 			break;
465 		case CP_PUSH_DEREFERENCED_STRUCTURE:
466 			// Push the address of a dereferenced structure
467 			Read32ip(parameter);
468 			ptr = objectData + 4 + ResHeader::size() + ObjectHub::size() + parameter;
469 			stack.push(_vm->_memory->encodePtr(ptr));
470 			debug(9, "CP_PUSH_DEREFERENCED_STRUCTURE: %d => %p", parameter, (void *)ptr);
471 			break;
472 		case CP_POP_LOCAL_VAR32:
473 			// Pop a value into a local word variable
474 			Read16ip(parameter);
475 			value = stack.pop();
476 			WRITE_LE_UINT32(localVars + parameter, value);
477 			debug(9, "CP_POP_LOCAL_VAR32: localVars[%d] = %d", parameter / 4, value);
478 			break;
479 		case CP_POP_GLOBAL_VAR32:
480 			// Pop a global variable
481 			Read16ip(parameter);
482 			value = stack.pop();
483 
484 			// WORKAROUND for bug #1214168: The not-at-all dreaded
485 			// mop bug.
486 			//
487 			// At the London Docks, global variable 1003 keeps
488 			// track of Nico:
489 			//
490 			// 0: Hiding behind the first crate.
491 			// 1: Hiding behind the second crate.
492 			// 2: Standing in plain view on the deck.
493 			// 3: Hiding on the roof.
494 			//
495 			// The bug happens when trying to pick up the mop while
496 			// hiding on the roof. Nico climbs down, the mop is
497 			// picked up, but the variable remains set to 3.
498 			// Visually, everything looks ok. But as far as the
499 			// scripts are concerned, she's still hiding up on the
500 			// roof. This is not fatal, but leads to a number of
501 			// glitches until the state is corrected. E.g. trying
502 			// to climb back up the ladder will cause Nico to climb
503 			// down again.
504 			//
505 			// Global variable 1017 keeps track of the mop. Setting
506 			// it to 2 means that the mop has been picked up. We
507 			// use that as the signal that Nico's state needs to be
508 			// updated as well.
509 
510 			if (checkMopBug && parameter == 1017 && readVar(1003) != 2) {
511 				warning("Working around mop bug: Setting Nico's state");
512 				writeVar(1003, 2);
513 			}
514 
515 			writeVar(parameter, value);
516 			debug(9, "CP_POP_GLOBAL_VAR32: scriptsVars[%d] = %d", parameter, value);
517 			break;
518 		case CP_ADDNPOP_LOCAL_VAR32:
519 			Read16ip(parameter);
520 			value = READ_LE_UINT32(localVars + parameter) + stack.pop();
521 			WRITE_LE_UINT32(localVars + parameter, value);
522 			debug(9, "CP_ADDNPOP_LOCAL_VAR32: localVars[%d] => %d", parameter / 4, value);
523 			break;
524 		case CP_SUBNPOP_LOCAL_VAR32:
525 			Read16ip(parameter);
526 			value = READ_LE_UINT32(localVars + parameter) - stack.pop();
527 			WRITE_LE_UINT32(localVars + parameter, value);
528 			debug(9, "CP_SUBNPOP_LOCAL_VAR32: localVars[%d] => %d", parameter / 4, value);
529 			break;
530 		case CP_ADDNPOP_GLOBAL_VAR32:
531 			// Add and pop a global variable
532 			Read16ip(parameter);
533 			value = readVar(parameter) + stack.pop();
534 			writeVar(parameter, value);
535 			debug(9, "CP_ADDNPOP_GLOBAL_VAR32: scriptVars[%d] => %d", parameter, value);
536 			break;
537 		case CP_SUBNPOP_GLOBAL_VAR32:
538 			// Sub and pop a global variable
539 			Read16ip(parameter);
540 			value = readVar(parameter) - stack.pop();
541 			writeVar(parameter, value);
542 			debug(9, "CP_SUBNPOP_GLOBAL_VAR32: scriptVars[%d] => %d", parameter, value);
543 			break;
544 
545 		// Jump opcodes
546 
547 		case CP_SKIPONTRUE:
548 			// Skip if the value on the stack is true
549 			Read32ipLeaveip(parameter);
550 			value = stack.pop();
551 			if (!value) {
552 				ip += 4;
553 				debug(9, "CP_SKIPONTRUE: %d (IS FALSE (NOT SKIPPED))", parameter);
554 			} else {
555 				ip += parameter;
556 				debug(9, "CP_SKIPONTRUE: %d (IS TRUE (SKIPPED))", parameter);
557 			}
558 			break;
559 		case CP_SKIPONFALSE:
560 			// Skip if the value on the stack is false
561 			Read32ipLeaveip(parameter);
562 			value = stack.pop();
563 			if (value) {
564 				ip += 4;
565 				debug(9, "CP_SKIPONFALSE: %d (IS TRUE (NOT SKIPPED))", parameter);
566 			} else {
567 				ip += parameter;
568 				debug(9, "CP_SKIPONFALSE: %d (IS FALSE (SKIPPED))", parameter);
569 			}
570 			break;
571 		case CP_SKIPALWAYS:
572 			// skip a block
573 			Read32ipLeaveip(parameter);
574 			ip += parameter;
575 			debug(9, "CP_SKIPALWAYS: %d", parameter);
576 			break;
577 		case CP_SWITCH:
578 			// switch
579 			value = stack.pop();
580 			Read32ip(caseCount);
581 
582 			// Search the cases
583 			foundCase = false;
584 			for (i = 0; i < caseCount && !foundCase; i++) {
585 				if (value == (int32)READ_LE_UINT32(code + ip)) {
586 					// We have found the case, so lets
587 					// jump to it
588 					foundCase = true;
589 					ip += READ_LE_UINT32(code + ip + 4);
590 				} else
591 					ip += 4 * 2;
592 			}
593 
594 			// If we found no matching case then use the default
595 
596 			if (!foundCase)
597 				ip += READ_LE_UINT32(code + ip);
598 
599 			debug(9, "CP_SWITCH: [SORRY, NO DEBUG INFO]");
600 			break;
601 		case CP_SAVE_MCODE_START:
602 			// Save the start position on an mcode instruction in
603 			// case we need to restart it again
604 			savedStartOfMcode = ip - 1;
605 			debug(9, "CP_SAVE_MCODE_START");
606 			break;
607 		case CP_CALL_MCODE:
608 			// Call an mcode routine
609 			Read16ip(parameter);
610 			assert(parameter < _numOpcodes);
611 			// amount to adjust stack by (no of parameters)
612 			Read8ip(value);
613 			debug(9, "CP_CALL_MCODE: '%s', %d", _opcodes[parameter].desc, value);
614 
615 			// The scripts do not always call the mcode command
616 			// with as many parameters as it can accept. To keep
617 			// things predictable, initialize the remaining
618 			// parameters to 0.
619 
620 			for (i = STACK_SIZE - 1; i >= value; i--) {
621 				opcodeParams[i] = 0;
622 			}
623 
624 			while (--value >= 0) {
625 				opcodeParams[value] = stack.pop();
626 			}
627 
628 			retVal = (this->*_opcodes[parameter].proc)(opcodeParams);
629 
630 			switch (retVal & 7) {
631 			case IR_STOP:
632 				// Quit out for a cycle
633 				WRITE_LE_UINT32(offsetPtr, ip);
634 				return 0;
635 			case IR_CONT:
636 				// Continue as normal
637 				break;
638 			case IR_TERMINATE:
639 				if (checkPearlBug && readVar(1290) == 0) {
640 					// Pearl's interaction script will wait
641 					// until global(1290) is no longer 0
642 					// before doing anything. But if the
643 					// script was terminated prematurely,
644 					// that never happens.
645 					warning("Working around Pearl bug: Resetting Pearl's state");
646 					writeVar(1290, 1);
647 				}
648 				// Return without updating the offset
649 				return 2;
650 			case IR_REPEAT:
651 				// Return setting offset to start of this
652 				// function call
653 				WRITE_LE_UINT32(offsetPtr, savedStartOfMcode);
654 				return 0;
655 			case IR_GOSUB:
656 				// that's really neat
657 				WRITE_LE_UINT32(offsetPtr, ip);
658 				return 2;
659 			default:
660 				error("Bad return code (%d) from '%s'", retVal & 7, _opcodes[parameter].desc);
661 			}
662 			parameterReturnedFromMcodeFunction = retVal >> 3;
663 			break;
664 		case CP_JUMP_ON_RETURNED:
665 			// Jump to a part of the script depending on
666 			// the return value from an mcode routine
667 
668 			// Get the maximum value
669 			Read8ip(parameter);
670 			debug(9, "CP_JUMP_ON_RETURNED: %d => %d",
671 				parameterReturnedFromMcodeFunction,
672 				READ_LE_UINT32(code + ip + parameterReturnedFromMcodeFunction * 4));
673 			ip += READ_LE_UINT32(code + ip + parameterReturnedFromMcodeFunction * 4);
674 			break;
675 
676 		// Operators
677 
678 		case OP_ISEQUAL:
679 			b = stack.pop();
680 			a = stack.pop();
681 			stack.push(a == b);
682 			debug(9, "OP_ISEQUAL: RESULT = %d", a == b);
683 			break;
684 		case OP_NOTEQUAL:
685 			b = stack.pop();
686 			a = stack.pop();
687 			stack.push(a != b);
688 			debug(9, "OP_NOTEQUAL: RESULT = %d", a != b);
689 			break;
690 		case OP_GTTHAN:
691 			b = stack.pop();
692 			a = stack.pop();
693 			stack.push(a > b);
694 			debug(9, "OP_GTTHAN: RESULT = %d", a > b);
695 			break;
696 		case OP_LSTHAN:
697 			b = stack.pop();
698 			a = stack.pop();
699 			stack.push(a < b);
700 			debug(9, "OP_LSTHAN: RESULT = %d", a < b);
701 			break;
702 		case OP_GTTHANE:
703 			b = stack.pop();
704 			a = stack.pop();
705 			stack.push(a >= b);
706 			debug(9, "OP_GTTHANE: RESULT = %d", a >= b);
707 			break;
708 		case OP_LSTHANE:
709 			b = stack.pop();
710 			a = stack.pop();
711 			stack.push(a <= b);
712 			debug(9, "OP_LSTHANE: RESULT = %d", a <= b);
713 			break;
714 		case OP_PLUS:
715 			b = stack.pop();
716 			a = stack.pop();
717 			stack.push(a + b);
718 			debug(9, "OP_PLUS: RESULT = %d", a + b);
719 			break;
720 		case OP_MINUS:
721 			b = stack.pop();
722 			a = stack.pop();
723 			stack.push(a - b);
724 			debug(9, "OP_MINUS: RESULT = %d", a - b);
725 			break;
726 		case OP_TIMES:
727 			b = stack.pop();
728 			a = stack.pop();
729 			stack.push(a * b);
730 			debug(9, "OP_TIMES: RESULT = %d", a * b);
731 			break;
732 		case OP_DIVIDE:
733 			b = stack.pop();
734 			a = stack.pop();
735 			stack.push(a / b);
736 			debug(9, "OP_DIVIDE: RESULT = %d", a / b);
737 			break;
738 		case OP_ANDAND:
739 			b = stack.pop();
740 			a = stack.pop();
741 			stack.push(a && b);
742 			debug(9, "OP_ANDAND: RESULT = %d", a && b);
743 			break;
744 		case OP_OROR:
745 			b = stack.pop();
746 			a = stack.pop();
747 			stack.push(a || b);
748 			debug(9, "OP_OROR: RESULT = %d", a || b);
749 			break;
750 
751 		// Debugging opcodes, I think
752 
753 		case CP_DEBUGON:
754 			debug(9, "CP_DEBUGON");
755 			break;
756 		case CP_DEBUGOFF:
757 			debug(9, "CP_DEBUGOFF");
758 			break;
759 		case CP_TEMP_TEXT_PROCESS:
760 			Read32ip(parameter);
761 			debug(9, "CP_TEMP_TEXT_PROCESS: %d", parameter);
762 			break;
763 		default:
764 			error("Invalid script command %d", curCommand);
765 			return 3;	// for compilers that don't support NORETURN
766 		}
767 	}
768 
769 	return 1;
770 }
771 
772 } // End of namespace Sword2
773