summaryrefslogtreecommitdiff
path: root/apps/plugins/xworld/vm.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/xworld/vm.c')
-rw-r--r--apps/plugins/xworld/vm.c763
1 files changed, 763 insertions, 0 deletions
diff --git a/apps/plugins/xworld/vm.c b/apps/plugins/xworld/vm.c
new file mode 100644
index 0000000000..de632d710d
--- /dev/null
+++ b/apps/plugins/xworld/vm.c
@@ -0,0 +1,763 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2014 Franklin Wei, Benjamin Brown
11 * Copyright (C) 2004 Gregory Montoir
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ***************************************************************************/
22
23#include "plugin.h"
24#include "vm.h"
25#include "mixer.h"
26#include "resource.h"
27#include "video.h"
28#include "serializer.h"
29#include "sfxplayer.h"
30#include "sys.h"
31#include "parts.h"
32#include "file.h"
33
34static const uint16_t vm_frequenceTable[] = {
35 0x0CFF, 0x0DC3, 0x0E91, 0x0F6F, 0x1056, 0x114E, 0x1259, 0x136C,
36 0x149F, 0x15D9, 0x1726, 0x1888, 0x19FD, 0x1B86, 0x1D21, 0x1EDE,
37 0x20AB, 0x229C, 0x24B3, 0x26D7, 0x293F, 0x2BB2, 0x2E4C, 0x3110,
38 0x33FB, 0x370D, 0x3A43, 0x3DDF, 0x4157, 0x4538, 0x4998, 0x4DAE,
39 0x5240, 0x5764, 0x5C9A, 0x61C8, 0x6793, 0x6E19, 0x7485, 0x7BBD
40};
41
42void vm_create(struct VirtualMachine* m, struct Mixer *mix, struct Resource* res, struct SfxPlayer *ply, struct Video *vid, struct System *stub)
43{
44 m->res = res;
45 m->video = vid;
46 m->sys = stub;
47 m->mixer = mix;
48 m->player = ply;
49}
50
51void vm_init(struct VirtualMachine* m) {
52
53 rb->memset(m->vmVariables, 0, sizeof(m->vmVariables));
54 m->vmVariables[0x54] = 0x81;
55 m->vmVariables[VM_VARIABLE_RANDOM_SEED] = *rb->current_tick;
56
57 m->_fastMode = false;
58 m->player->_markVar = &m->vmVariables[VM_VARIABLE_MUS_MARK];
59}
60
61void vm_op_movConst(struct VirtualMachine* m) {
62 uint8_t variableId = scriptPtr_fetchByte(&m->_scriptPtr);
63 int16_t value = scriptPtr_fetchWord(&m->_scriptPtr);
64 debug(DBG_VM, "vm_op_movConst(0x%02X, %d)", variableId, value);
65 m->vmVariables[variableId] = value;
66}
67
68void vm_op_mov(struct VirtualMachine* m) {
69 uint8_t dstVariableId = scriptPtr_fetchByte(&m->_scriptPtr);
70 uint8_t srcVariableId = scriptPtr_fetchByte(&m->_scriptPtr);
71 debug(DBG_VM, "vm_op_mov(0x%02X, 0x%02X)", dstVariableId, srcVariableId);
72 m->vmVariables[dstVariableId] = m->vmVariables[srcVariableId];
73}
74
75void vm_op_add(struct VirtualMachine* m) {
76 uint8_t dstVariableId = scriptPtr_fetchByte(&m->_scriptPtr);
77 uint8_t srcVariableId = scriptPtr_fetchByte(&m->_scriptPtr);
78 debug(DBG_VM, "vm_op_add(0x%02X, 0x%02X)", dstVariableId, srcVariableId);
79 m->vmVariables[dstVariableId] += m->vmVariables[srcVariableId];
80}
81
82void vm_op_addConst(struct VirtualMachine* m) {
83 if (m->res->currentPartId == 0x3E86 && m->_scriptPtr.pc == m->res->segBytecode + 0x6D48) {
84 warning("vm_op_addConst() hack for non-stop looping gun sound bug");
85 // the script 0x27 slot 0x17 doesn't stop the gun sound from looping, I
86 // don't really know why ; for now, let's play the 'stopping sound' like
87 // the other scripts do
88 // (0x6D43) jmp(0x6CE5)
89 // (0x6D46) break
90 // (0x6D47) VAR(6) += -50
91 vm_snd_playSound(m, 0x5B, 1, 64, 1);
92 }
93 uint8_t variableId = scriptPtr_fetchByte(&m->_scriptPtr);
94 int16_t value = scriptPtr_fetchWord(&m->_scriptPtr);
95 debug(DBG_VM, "vm_op_addConst(0x%02X, %d)", variableId, value);
96 m->vmVariables[variableId] += value;
97}
98
99void vm_op_call(struct VirtualMachine* m) {
100
101 uint16_t offset = scriptPtr_fetchWord(&m->_scriptPtr);
102 uint8_t sp = m->_stackPtr;
103
104 debug(DBG_VM, "vm_op_call(0x%X)", offset);
105 m->_scriptStackCalls[sp] = m->_scriptPtr.pc - m->res->segBytecode;
106 if (m->_stackPtr == 0xFF) {
107 error("vm_op_call() ec=0x%X stack overflow", 0x8F);
108 }
109 ++m->_stackPtr;
110 m->_scriptPtr.pc = m->res->segBytecode + offset ;
111}
112
113void vm_op_ret(struct VirtualMachine* m) {
114 debug(DBG_VM, "vm_op_ret()");
115 if (m->_stackPtr == 0) {
116 error("vm_op_ret() ec=0x%X stack underflow", 0x8F);
117 }
118 --m->_stackPtr;
119 uint8_t sp = m->_stackPtr;
120 m->_scriptPtr.pc = m->res->segBytecode + m->_scriptStackCalls[sp];
121}
122
123void vm_op_pauseThread(struct VirtualMachine* m) {
124 debug(DBG_VM, "vm_op_pauseThread()");
125 m->gotoNextThread = true;
126}
127
128void vm_op_jmp(struct VirtualMachine* m) {
129 uint16_t pcOffset = scriptPtr_fetchWord(&m->_scriptPtr);
130 debug(DBG_VM, "vm_op_jmp(0x%02X)", pcOffset);
131 m->_scriptPtr.pc = m->res->segBytecode + pcOffset;
132}
133
134void vm_op_setSetVect(struct VirtualMachine* m) {
135 uint8_t threadId = scriptPtr_fetchByte(&m->_scriptPtr);
136 uint16_t pcOffsetRequested = scriptPtr_fetchWord(&m->_scriptPtr);
137 debug(DBG_VM, "vm_op_setSetVect(0x%X, 0x%X)", threadId, pcOffsetRequested);
138 m->threadsData[REQUESTED_PC_OFFSET][threadId] = pcOffsetRequested;
139}
140
141void vm_op_jnz(struct VirtualMachine* m) {
142 uint8_t i = scriptPtr_fetchByte(&m->_scriptPtr);
143 debug(DBG_VM, "vm_op_jnz(0x%02X)", i);
144 --m->vmVariables[i];
145 if (m->vmVariables[i] != 0) {
146 vm_op_jmp(m);
147 } else {
148 scriptPtr_fetchWord(&m->_scriptPtr);
149 }
150}
151
152#define BYPASS_PROTECTION
153void vm_op_condJmp(struct VirtualMachine* m) {
154
155 //printf("Jump : %X \n",m->_scriptPtr.pc-m->res->segBytecode);
156//FCS Whoever wrote this is patching the bytecode on the fly. This is ballzy !!
157#ifdef BYPASS_PROTECTION
158
159 if (m->res->currentPartId == GAME_PART_FIRST && m->_scriptPtr.pc == m->res->segBytecode + 0xCB9) {
160
161 // (0x0CB8) condJmp(0x80, VAR(41), VAR(30), 0xCD3)
162 *(m->_scriptPtr.pc + 0x00) = 0x81;
163 *(m->_scriptPtr.pc + 0x03) = 0x0D;
164 *(m->_scriptPtr.pc + 0x04) = 0x24;
165 // (0x0D4E) condJmp(0x4, VAR(50), 6, 0xDBC)
166 *(m->_scriptPtr.pc + 0x99) = 0x0D;
167 *(m->_scriptPtr.pc + 0x9A) = 0x5A;
168 debug(DBG_VM, "vm_op_condJmp() bypassing protection");
169 debug(DBG_VM, "bytecode has been patched");
170
171 //vm_bypassProtection(m);
172 }
173
174
175#endif
176
177 uint8_t opcode = scriptPtr_fetchByte(&m->_scriptPtr);
178 int16_t b = m->vmVariables[scriptPtr_fetchByte(&m->_scriptPtr)];
179 uint8_t c = scriptPtr_fetchByte(&m->_scriptPtr);
180 int16_t a;
181
182 if (opcode & 0x80) {
183 a = m->vmVariables[c];
184 } else if (opcode & 0x40) {
185 a = c * 256 + scriptPtr_fetchByte(&m->_scriptPtr);
186 } else {
187 a = c;
188 }
189 debug(DBG_VM, "vm_op_condJmp(%d, 0x%02X, 0x%02X)", opcode, b, a);
190
191 // Check if the conditional value is met.
192 bool expr = false;
193 switch (opcode & 7) {
194 case 0: // jz
195 expr = (b == a);
196 break;
197 case 1: // jnz
198 expr = (b != a);
199 break;
200 case 2: // jg
201 expr = (b > a);
202 break;
203 case 3: // jge
204 expr = (b >= a);
205 break;
206 case 4: // jl
207 expr = (b < a);
208 break;
209 case 5: // jle
210 expr = (b <= a);
211 break;
212 default:
213 warning("vm_op_condJmp() invalid condition %d", (opcode & 7));
214 break;
215 }
216
217 if (expr) {
218 vm_op_jmp(m);
219 } else {
220 scriptPtr_fetchWord(&m->_scriptPtr);
221 }
222
223}
224
225void vm_op_setPalette(struct VirtualMachine* m) {
226 uint16_t paletteId = scriptPtr_fetchWord(&m->_scriptPtr);
227 debug(DBG_VM, "vm_op_changePalette(%d)", paletteId);
228 m->video->paletteIdRequested = paletteId >> 8;
229}
230
231void vm_op_resetThread(struct VirtualMachine* m) {
232
233 uint8_t threadId = scriptPtr_fetchByte(&m->_scriptPtr);
234 uint8_t i = scriptPtr_fetchByte(&m->_scriptPtr);
235
236 // FCS: WTF, this is cryptic as hell !!
237 // int8_t n = (i & 0x3F) - threadId; //0x3F = 0011 1111
238 // The following is so much clearer
239
240 //Make sure i is within [0-VM_NUM_THREADS-1]
241 i = i & (VM_NUM_THREADS - 1) ;
242 int8_t n = i - threadId;
243
244 if (n < 0) {
245 warning("vm_op_m->resetThread() ec=0x%X (n < 0)", 0x880);
246 return;
247 }
248 ++n;
249 uint8_t a = scriptPtr_fetchByte(&m->_scriptPtr);
250
251 debug(DBG_VM, "vm_op_m->resetThread(%d, %d, %d)", threadId, i, a);
252
253 if (a == 2) {
254 uint16_t *p = &m->threadsData[REQUESTED_PC_OFFSET][threadId];
255 while (n--) {
256 *p++ = 0xFFFE;
257 }
258 } else if (a < 2) {
259 uint8_t *p = &m->vmIsChannelActive[REQUESTED_STATE][threadId];
260 while (n--) {
261 *p++ = a;
262 }
263 }
264}
265
266void vm_op_selectVideoPage(struct VirtualMachine* m) {
267 uint8_t frameBufferId = scriptPtr_fetchByte(&m->_scriptPtr);
268 debug(DBG_VM, "vm_op_selectVideoPage(%d)", frameBufferId);
269 video_changePagePtr1(m->video, frameBufferId);
270}
271
272void vm_op_fillVideoPage(struct VirtualMachine* m) {
273 uint8_t pageId = scriptPtr_fetchByte(&m->_scriptPtr);
274 uint8_t color = scriptPtr_fetchByte(&m->_scriptPtr);
275 debug(DBG_VM, "vm_op_fillVideoPage(%d, %d)", pageId, color);
276 video_fillPage(m->video, pageId, color);
277}
278
279void vm_op_copyVideoPage(struct VirtualMachine* m) {
280 uint8_t srcPageId = scriptPtr_fetchByte(&m->_scriptPtr);
281 uint8_t dstPageId = scriptPtr_fetchByte(&m->_scriptPtr);
282 debug(DBG_VM, "vm_op_copyVideoPage(%d, %d)", srcPageId, dstPageId);
283 video_copyPage(m->video, srcPageId, dstPageId, m->vmVariables[VM_VARIABLE_SCROLL_Y]);
284}
285
286
287static uint32_t lastTimeStamp = 0;
288void vm_op_blitFramebuffer(struct VirtualMachine* m) {
289
290 uint8_t pageId = scriptPtr_fetchByte(&m->_scriptPtr);
291 debug(DBG_VM, "vm_op_blitFramebuffer(%d)", pageId);
292 vm_inp_handleSpecialKeys(m);
293
294 /* Nasty hack....was this present in the original assembly ??!! */
295 if (m->res->currentPartId == GAME_PART_FIRST && m->vmVariables[0x67] == 1)
296 m->vmVariables[0xDC] = 0x21;
297
298 if (!m->_fastMode) {
299
300 int32_t delay = sys_getTimeStamp(m->sys) - lastTimeStamp;
301 int32_t timeToSleep = m->vmVariables[VM_VARIABLE_PAUSE_SLICES] * 20 - delay;
302
303 /* The bytecode will set m->vmVariables[VM_VARIABLE_PAUSE_SLICES] from 1 to 5 */
304 /* The virtual machine hence indicates how long the image should be displayed. */
305
306 if (timeToSleep > 0)
307 {
308 sys_sleep(m->sys, timeToSleep);
309 }
310
311 lastTimeStamp = sys_getTimeStamp(m->sys);
312 }
313
314 /* WTF ? */
315 m->vmVariables[0xF7] = 0;
316
317 video_updateDisplay(m->video, pageId);
318}
319
320void vm_op_killThread(struct VirtualMachine* m) {
321 debug(DBG_VM, "vm_op_killThread()");
322 m->_scriptPtr.pc = m->res->segBytecode + 0xFFFF;
323 m->gotoNextThread = true;
324}
325
326void vm_op_drawString(struct VirtualMachine* m) {
327 uint16_t stringId = scriptPtr_fetchWord(&m->_scriptPtr);
328 uint16_t x = scriptPtr_fetchByte(&m->_scriptPtr);
329 uint16_t y = scriptPtr_fetchByte(&m->_scriptPtr);
330 uint16_t color = scriptPtr_fetchByte(&m->_scriptPtr);
331
332 debug(DBG_VM, "vm_op_drawString(0x%03X, %d, %d, %d)", stringId, x, y, color);
333
334 video_drawString(m->video, color, x, y, stringId);
335}
336
337void vm_op_sub(struct VirtualMachine* m) {
338 uint8_t i = scriptPtr_fetchByte(&m->_scriptPtr);
339 uint8_t j = scriptPtr_fetchByte(&m->_scriptPtr);
340 debug(DBG_VM, "vm_op_sub(0x%02X, 0x%02X)", i, j);
341 m->vmVariables[i] -= m->vmVariables[j];
342}
343
344void vm_op_and(struct VirtualMachine* m) {
345 uint8_t variableId = scriptPtr_fetchByte(&m->_scriptPtr);
346 uint16_t n = scriptPtr_fetchWord(&m->_scriptPtr);
347 debug(DBG_VM, "vm_op_and(0x%02X, %d)", variableId, n);
348 m->vmVariables[variableId] = (uint16_t)m->vmVariables[variableId] & n;
349}
350
351void vm_op_or(struct VirtualMachine* m) {
352 uint8_t variableId = scriptPtr_fetchByte(&m->_scriptPtr);
353 uint16_t value = scriptPtr_fetchWord(&m->_scriptPtr);
354 debug(DBG_VM, "vm_op_or(0x%02X, %d)", variableId, value);
355 m->vmVariables[variableId] = (uint16_t)m->vmVariables[variableId] | value;
356}
357
358void vm_op_shl(struct VirtualMachine* m) {
359 uint8_t variableId = scriptPtr_fetchByte(&m->_scriptPtr);
360 uint16_t leftShiftValue = scriptPtr_fetchWord(&m->_scriptPtr);
361 debug(DBG_VM, "vm_op_shl(0x%02X, %d)", variableId, leftShiftValue);
362 m->vmVariables[variableId] = (uint16_t)m->vmVariables[variableId] << leftShiftValue;
363}
364
365void vm_op_shr(struct VirtualMachine* m) {
366 uint8_t variableId = scriptPtr_fetchByte(&m->_scriptPtr);
367 uint16_t rightShiftValue = scriptPtr_fetchWord(&m->_scriptPtr);
368 debug(DBG_VM, "vm_op_shr(0x%02X, %d)", variableId, rightShiftValue);
369 m->vmVariables[variableId] = (uint16_t)m->vmVariables[variableId] >> rightShiftValue;
370}
371
372void vm_op_playSound(struct VirtualMachine* m) {
373 uint16_t resourceId = scriptPtr_fetchWord(&m->_scriptPtr);
374 uint8_t freq = scriptPtr_fetchByte(&m->_scriptPtr);
375 uint8_t vol = scriptPtr_fetchByte(&m->_scriptPtr);
376 uint8_t channel = scriptPtr_fetchByte(&m->_scriptPtr);
377 debug(DBG_VM, "vm_op_playSound(0x%X, %d, %d, %d)", resourceId, freq, vol, channel);
378 vm_snd_playSound(m, resourceId, freq, vol, channel);
379}
380
381void vm_op_updateMemList(struct VirtualMachine* m) {
382
383 uint16_t resourceId = scriptPtr_fetchWord(&m->_scriptPtr);
384 debug(DBG_VM, "vm_op_updateMemList(%d)", resourceId);
385
386 if (resourceId == 0) {
387 player_stop(m->player);
388 mixer_stopAll(m->mixer);
389 res_invalidateRes(m->res);
390 } else {
391 res_loadPartsOrMemoryEntry(m->res, resourceId);
392 }
393}
394
395void vm_op_playMusic(struct VirtualMachine* m) {
396 uint16_t resNum = scriptPtr_fetchWord(&m->_scriptPtr);
397 uint16_t delay = scriptPtr_fetchWord(&m->_scriptPtr);
398 uint8_t pos = scriptPtr_fetchByte(&m->_scriptPtr);
399 debug(DBG_VM, "vm_op_playMusic(0x%X, %d, %d)", resNum, delay, pos);
400 vm_snd_playMusic(m, resNum, delay, pos);
401}
402
403void vm_initForPart(struct VirtualMachine* m, uint16_t partId) {
404
405 player_stop(m->player);
406 mixer_stopAll(m->mixer);
407
408 /* WTF is that ? */
409 m->vmVariables[0xE4] = 0x14;
410
411 res_setupPart(m->res, partId);
412
413 /* Set all thread to inactive (pc at 0xFFFF or 0xFFFE ) */
414 rb->memset((uint8_t *)m->threadsData, 0xFF, sizeof(m->threadsData));
415
416 rb->memset((uint8_t *)m->vmIsChannelActive, 0, sizeof(m->vmIsChannelActive));
417
418 int firstThreadId = 0;
419 m->threadsData[PC_OFFSET][firstThreadId] = 0;
420}
421
422/*
423 This is called every frames in the infinite loop.
424*/
425void vm_checkThreadRequests(struct VirtualMachine* m) {
426
427 /* Check if a part switch has been requested. */
428 if (m->res->requestedNextPart != 0) {
429 vm_initForPart(m, m->res->requestedNextPart);
430 m->res->requestedNextPart = 0;
431 }
432
433
434 /* Check if a state update has been requested for any thread during the previous VM execution: */
435 /* - Pause */
436 /* - Jump */
437
438 /* JUMP: */
439 /* Note: If a jump has been requested, the jump destination is stored */
440 /* in m->threadsData[REQUESTED_PC_OFFSET]. Otherwise m->threadsData[REQUESTED_PC_OFFSET] == 0xFFFF */
441
442 /* PAUSE: */
443 /* Note: If a pause has been requested it is stored in m->vmIsChannelActive[REQUESTED_STATE][i] */
444
445 for (int threadId = 0; threadId < VM_NUM_THREADS; threadId++) {
446
447 m->vmIsChannelActive[CURR_STATE][threadId] = m->vmIsChannelActive[REQUESTED_STATE][threadId];
448
449 uint16_t n = m->threadsData[REQUESTED_PC_OFFSET][threadId];
450
451 if (n != VM_NO_SETVEC_REQUESTED) {
452
453 m->threadsData[PC_OFFSET][threadId] = (n == 0xFFFE) ? VM_INACTIVE_THREAD : n;
454 m->threadsData[REQUESTED_PC_OFFSET][threadId] = VM_NO_SETVEC_REQUESTED;
455 }
456 }
457}
458
459void vm_hostFrame(struct VirtualMachine* m) {
460
461 /* Run the Virtual Machine for every active threads (one vm frame). */
462 /* Inactive threads are marked with a thread instruction pointer set to 0xFFFF (VM_INACTIVE_THREAD). */
463 /* A thread must feature a break opcode so the interpreter can move to the next thread. */
464
465 for (int threadId = 0; threadId < VM_NUM_THREADS; threadId++) {
466
467 if (m->vmIsChannelActive[CURR_STATE][threadId])
468 continue;
469
470 uint16_t n = m->threadsData[PC_OFFSET][threadId];
471
472 if (n != VM_INACTIVE_THREAD) {
473
474 /* Set the script pointer to the right location. */
475 /* script pc is used in executeThread in order */
476 /* to get the next opcode. */
477 m->_scriptPtr.pc = m->res->segBytecode + n;
478 m->_stackPtr = 0;
479
480 m->gotoNextThread = false;
481 debug(DBG_VM, "vm_hostFrame() i=0x%02X n=0x%02X *p=0x%02X", threadId, n, *m->_scriptPtr.pc);
482 vm_executeThread(m);
483
484 /* Since .pc is going to be modified by this next loop iteration, we need to save it. */
485 m->threadsData[PC_OFFSET][threadId] = m->_scriptPtr.pc - m->res->segBytecode;
486
487
488 debug(DBG_VM, "vm_hostFrame() i=0x%02X pos=0x%X", threadId, m->threadsData[PC_OFFSET][threadId]);
489 if (m->sys->input.quit) {
490 break;
491 }
492 }
493 }
494}
495
496#define COLOR_BLACK 0xFF
497#define DEFAULT_ZOOM 0x40
498
499
500void vm_executeThread(struct VirtualMachine* m) {
501
502 while (!m->gotoNextThread) {
503 uint8_t opcode = scriptPtr_fetchByte(&m->_scriptPtr);
504
505 /* 1000 0000 is set */
506 if (opcode & 0x80)
507 {
508 uint16_t off = ((opcode << 8) | scriptPtr_fetchByte(&m->_scriptPtr)) * 2;
509 m->res->_useSegVideo2 = false;
510 int16_t x = scriptPtr_fetchByte(&m->_scriptPtr);
511 int16_t y = scriptPtr_fetchByte(&m->_scriptPtr);
512 int16_t h = y - 199;
513 if (h > 0) {
514 y = 199;
515 x += h;
516 }
517 debug(DBG_VIDEO, "vid_opcd_0x80 : opcode=0x%X off=0x%X x=%d y=%d", opcode, off, x, y);
518
519 /* This switch the polygon database to "cinematic" and probably draws a black polygon */
520 /* over all the screen. */
521 video_setDataBuffer(m->video, m->res->segCinematic, off);
522 struct Point temp;
523 temp.x = x;
524 temp.y = y;
525 video_readAndDrawPolygon(m->video, COLOR_BLACK, DEFAULT_ZOOM, &temp);
526
527 continue;
528 }
529
530 /* 0100 0000 is set */
531 if (opcode & 0x40)
532 {
533 int16_t x, y;
534 uint16_t off = scriptPtr_fetchWord(&m->_scriptPtr) * 2;
535 x = scriptPtr_fetchByte(&m->_scriptPtr);
536
537 m->res->_useSegVideo2 = false;
538
539 if (!(opcode & 0x20))
540 {
541 if (!(opcode & 0x10)) /* 0001 0000 is set */
542 {
543 x = (x << 8) | scriptPtr_fetchByte(&m->_scriptPtr);
544 } else {
545 x = m->vmVariables[x];
546 }
547 }
548 else
549 {
550 if (opcode & 0x10) { /* 0001 0000 is set */
551 x += 0x100;
552 }
553 }
554
555 y = scriptPtr_fetchByte(&m->_scriptPtr);
556
557 if (!(opcode & 8)) /* 0000 1000 is set */
558 {
559 if (!(opcode & 4)) { /* 0000 0100 is set */
560 y = (y << 8) | scriptPtr_fetchByte(&m->_scriptPtr);
561 } else {
562 y = m->vmVariables[y];
563 }
564 }
565
566 uint16_t zoom = scriptPtr_fetchByte(&m->_scriptPtr);
567
568 if (!(opcode & 2)) /* 0000 0010 is set */
569 {
570 if (!(opcode & 1)) /* 0000 0001 is set */
571 {
572 --m->_scriptPtr.pc;
573 zoom = 0x40;
574 }
575 else
576 {
577 zoom = m->vmVariables[zoom];
578 }
579 }
580 else
581 {
582
583 if (opcode & 1) { /* 0000 0001 is set */
584 m->res->_useSegVideo2 = true;
585 --m->_scriptPtr.pc;
586 zoom = 0x40;
587 }
588 }
589 debug(DBG_VIDEO, "vid_opcd_0x40 : off=0x%X x=%d y=%d", off, x, y);
590 video_setDataBuffer(m->video, m->res->_useSegVideo2 ? m->res->_segVideo2 : m->res->segCinematic, off);
591 struct Point temp;
592 temp.x = x;
593 temp.y = y;
594 video_readAndDrawPolygon(m->video, 0xFF, zoom, &temp);
595
596 continue;
597 }
598
599
600 if (opcode > 0x1A)
601 {
602 error("vm_executeThread() ec=0x%X invalid opcode=0x%X", 0xFFF, opcode);
603 }
604 else
605 {
606 (vm_opcodeTable[opcode])(m);
607 }
608
609 rb->yield();
610 }
611}
612
613void vm_inp_updatePlayer(struct VirtualMachine* m) {
614
615 sys_processEvents(m->sys);
616
617 if (m->res->currentPartId == 0x3E89) {
618 char c = m->sys->input.lastChar;
619 if (c == 8 || /*c == 0xD |*/ c == 0 || (c >= 'a' && c <= 'z')) {
620 m->vmVariables[VM_VARIABLE_LAST_KEYCHAR] = c & ~0x20;
621 m->sys->input.lastChar = 0;
622 }
623 }
624
625 int16_t lr = 0;
626 int16_t mask = 0;
627 int16_t ud = 0;
628
629 if (m->sys->input.dirMask & DIR_RIGHT) {
630 lr = 1;
631 mask |= 1;
632 }
633 if (m->sys->input.dirMask & DIR_LEFT) {
634 lr = -1;
635 mask |= 2;
636 }
637 if (m->sys->input.dirMask & DIR_DOWN) {
638 ud = 1;
639 mask |= 4;
640 }
641
642 m->vmVariables[VM_VARIABLE_HERO_POS_UP_DOWN] = ud;
643
644 if (m->sys->input.dirMask & DIR_UP) {
645 m->vmVariables[VM_VARIABLE_HERO_POS_UP_DOWN] = -1;
646 }
647
648 if (m->sys->input.dirMask & DIR_UP) { /* inpJump */
649 ud = -1;
650 mask |= 8;
651 }
652
653 m->vmVariables[VM_VARIABLE_HERO_POS_JUMP_DOWN] = ud;
654 m->vmVariables[VM_VARIABLE_HERO_POS_LEFT_RIGHT] = lr;
655 m->vmVariables[VM_VARIABLE_HERO_POS_MASK] = mask;
656 int16_t button = 0;
657
658 if (m->sys->input.button) {
659 button = 1;
660 mask |= 0x80;
661 }
662
663 m->vmVariables[VM_VARIABLE_HERO_ACTION] = button;
664 m->vmVariables[VM_VARIABLE_HERO_ACTION_POS_MASK] = mask;
665}
666
667void vm_inp_handleSpecialKeys(struct VirtualMachine* m) {
668
669 if (m->sys->input.pause) {
670
671 if (m->res->currentPartId != GAME_PART1 && m->res->currentPartId != GAME_PART2) {
672 m->sys->input.pause = false;
673 while (!m->sys->input.pause) {
674 sys_processEvents(m->sys);
675 sys_sleep(m->sys, 200);
676 }
677 }
678 m->sys->input.pause = false;
679 }
680
681 if (m->sys->input.code) {
682 m->sys->input.code = false;
683 if (m->res->currentPartId != GAME_PART_LAST && m->res->currentPartId != GAME_PART_FIRST) {
684 m->res->requestedNextPart = GAME_PART_LAST;
685 }
686 }
687
688 /* User has inputted a bad code, the "ERROR" screen is showing */
689 if (m->vmVariables[0xC9] == 1) {
690 debug(DBG_VM, "vm_inp_handleSpecialKeys() unhandled case (m->vmVariables[0xC9] == 1)");
691 }
692
693}
694
695void vm_snd_playSound(struct VirtualMachine* m, uint16_t resNum, uint8_t freq, uint8_t vol, uint8_t channel) {
696
697 debug(DBG_SND, "snd_playSound(0x%X, %d, %d, %d)", resNum, freq, vol, channel);
698
699 struct MemEntry *me = &m->res->_memList[resNum];
700
701 if (me->state != MEMENTRY_STATE_LOADED)
702 return;
703
704
705 if (vol == 0) {
706 mixer_stopChannel(m->mixer, channel);
707 } else {
708 struct MixerChunk mc;
709 rb->memset(&mc, 0, sizeof(mc));
710 mc.data = me->bufPtr + 8; /* skip header */
711 mc.len = READ_BE_UINT16(me->bufPtr) * 2;
712 mc.loopLen = READ_BE_UINT16(me->bufPtr + 2) * 2;
713 if (mc.loopLen != 0) {
714 mc.loopPos = mc.len;
715 }
716 assert(freq < 40);
717 mixer_playChannel(m->mixer, channel & 3, &mc, vm_frequenceTable[freq], MIN(vol, 0x3F));
718 }
719
720}
721
722void vm_snd_playMusic(struct VirtualMachine* m, uint16_t resNum, uint16_t delay, uint8_t pos) {
723
724 debug(DBG_SND, "snd_playMusic(0x%X, %d, %d)", resNum, delay, pos);
725
726 if (resNum != 0) {
727 player_loadSfxModule(m->player, resNum, delay, pos);
728 player_start(m->player);
729 } else if (delay != 0) {
730 player_setEventsDelay(m->player, delay);
731 } else {
732 player_stop(m->player);
733 }
734}
735
736void vm_saveOrLoad(struct VirtualMachine* m, struct Serializer *ser) {
737 struct Entry entries[] = {
738 SE_ARRAY(m->vmVariables, 0x100, SES_INT16, VER(1)),
739 SE_ARRAY(m->_scriptStackCalls, 0x100, SES_INT16, VER(1)),
740 SE_ARRAY(m->threadsData, 0x40 * 2, SES_INT16, VER(1)),
741 SE_ARRAY(m->vmIsChannelActive, 0x40 * 2, SES_INT8, VER(1)),
742 SE_END()
743 };
744 ser_saveOrLoadEntries(ser, entries);
745}
746
747void vm_bypassProtection(struct VirtualMachine* m)
748{
749 File f;
750 file_create(&f, true);
751 if (!file_open(&f, "bank0e", res_getDataDir(m->res), "rb")) {
752 warning("Unable to bypass protection: add bank0e file to datadir");
753 } else {
754 struct Serializer s;
755 ser_create(&s, &f, SM_LOAD, m->res->_memPtrStart, 2);
756 vm_saveOrLoad(m, &s);
757 res_saveOrLoad(m->res, &s);
758 video_saveOrLoad(m->video, &s);
759 player_saveOrLoad(m->player, &s);
760 mixer_saveOrLoad(m->mixer, &s);
761 }
762 file_close(&f);
763}