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