aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/frotz/processor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/glk/frotz/processor.cpp')
-rw-r--r--engines/glk/frotz/processor.cpp664
1 files changed, 664 insertions, 0 deletions
diff --git a/engines/glk/frotz/processor.cpp b/engines/glk/frotz/processor.cpp
new file mode 100644
index 0000000000..1cdc67a589
--- /dev/null
+++ b/engines/glk/frotz/processor.cpp
@@ -0,0 +1,664 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/frotz/processor.h"
+#include "glk/frotz/frotz.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+// TODO: Stubs to replace with actual code
+zword save_undo() { return 0; }
+zword restore_undo() { return 0; }
+
+
+Opcode Processor::var_opcodes[64] = {
+ &Processor::__illegal__,
+ &Processor::z_je,
+ &Processor::z_jl,
+ &Processor::z_jg,
+ &Processor::z_dec_chk,
+ &Processor::z_inc_chk,
+ &Processor::z_jin,
+ &Processor::z_test,
+ &Processor::z_or,
+ &Processor::z_and,
+ &Processor::z_test_attr,
+ &Processor::z_set_attr,
+ &Processor::z_clear_attr,
+ &Processor::z_store,
+ &Processor::z_insert_obj,
+ &Processor::z_loadw,
+ &Processor::z_loadb,
+ &Processor::z_get_prop,
+ &Processor::z_get_prop_addr,
+ &Processor::z_get_next_prop,
+ &Processor::z_add,
+ &Processor::z_sub,
+ &Processor::z_mul,
+ &Processor::z_div,
+ &Processor::z_mod,
+ &Processor::z_call_s,
+ &Processor::z_call_n,
+ &Processor::z_set_colour,
+ &Processor::z_throw,
+ &Processor::__illegal__,
+ &Processor::__illegal__,
+ &Processor::__illegal__,
+ &Processor::z_call_s,
+ &Processor::z_storew,
+ &Processor::z_storeb,
+ &Processor::z_put_prop,
+ &Processor::z_read,
+ &Processor::z_print_char,
+ &Processor::z_print_num,
+ &Processor::z_random,
+ &Processor::z_push,
+ &Processor::z_pull,
+ &Processor::z_split_window,
+ &Processor::z_set_window,
+ &Processor::z_call_s,
+ &Processor::z_erase_window,
+ &Processor::z_erase_line,
+ &Processor::z_set_cursor,
+ &Processor::z_get_cursor,
+ &Processor::z_set_text_style,
+ &Processor::z_buffer_mode,
+ &Processor::z_output_stream,
+ &Processor::z_input_stream,
+ &Processor::z_sound_effect,
+ &Processor::z_read_char,
+ &Processor::z_scan_table,
+ &Processor::z_not,
+ &Processor::z_call_n,
+ &Processor::z_call_n,
+ &Processor::z_tokenise,
+ &Processor::z_encode_text,
+ &Processor::z_copy_table,
+ &Processor::z_print_table,
+ &Processor::z_check_arg_count
+};
+
+Opcode Processor::ext_opcodes[64] = {
+ &Processor::z_save,
+ &Processor::z_restore,
+ &Processor::z_log_shift,
+ &Processor::z_art_shift,
+ &Processor::z_set_font,
+ &Processor::__illegal__, // glkify - Processor::z_draw_picture,
+ &Processor::__illegal__, // glkify - Processor::z_picture_data,
+ &Processor::__illegal__, // glkify - Processor::z_erase_picture,
+ &Processor::__illegal__, // glkify - Processor::z_set_margins,
+ &Processor::z_save_undo,
+ &Processor::z_restore_undo,
+ &Processor::z_print_unicode,
+ &Processor::z_check_unicode,
+ &Processor::z_set_true_colour, // spec 1.1
+ &Processor::__illegal__,
+ &Processor::__illegal__,
+ &Processor::__illegal__, // glkify - Processor::z_move_window,
+ &Processor::__illegal__, // glkify - Processor::z_window_size,
+ &Processor::__illegal__, // glkify - Processor::z_window_style,
+ &Processor::__illegal__, // glkify - Processor::z_get_wind_prop,
+ &Processor::__illegal__, // glkify - Processor::z_scroll_window,
+ &Processor::z_pop_stack,
+ &Processor::__illegal__, // glkify - Processor::z_read_mouse,
+ &Processor::__illegal__, // glkify - Processor::z_mouse_window,
+ &Processor::z_push_stack,
+ &Processor::__illegal__, // glkify - Processor::z_put_wind_prop,
+ &Processor::z_print_form,
+ &Processor::z_make_menu,
+ &Processor::__illegal__, // glkify - Processor::z_picture_table
+ &Processor::z_buffer_screen, // spec 1.1
+};
+
+Processor::Processor(OSystem *syst, const GargoyleGameDescription *gameDesc) :
+ GlkInterface(syst, gameDesc),
+ _finished(0), _sp(nullptr), _fp(nullptr), _frameCount(0),
+ zargc(0), _decoded(nullptr), _encoded(nullptr), _resolution(0),
+ _randomInterval(0), _randomCtr(0), first_restart(true), script_valid(false),
+ _bufPos(0), _locked(false), _prevC('\0'), script_width(0),
+ sfp(nullptr), rfp(nullptr), pfp(nullptr) {
+ static const Opcode OP0_OPCODES[16] = {
+ &Processor::z_rtrue,
+ &Processor::z_rfalse,
+ &Processor::z_print,
+ &Processor::z_print_ret,
+ &Processor::z_nop,
+ &Processor::z_save,
+ &Processor::z_restore,
+ &Processor::z_restart,
+ &Processor::z_ret_popped,
+ &Processor::z_catch,
+ &Processor::z_quit,
+ &Processor::z_new_line,
+ &Processor::z_show_status,
+ &Processor::z_verify,
+ &Processor::__extended__,
+ &Processor::z_piracy
+ };
+ static const Opcode OP1_OPCODES[16] = {
+ &Processor::z_jz,
+ &Processor::z_get_sibling,
+ &Processor::z_get_child,
+ &Processor::z_get_parent,
+ &Processor::z_get_prop_len,
+ &Processor::z_inc,
+ &Processor::z_dec,
+ &Processor::z_print_addr,
+ &Processor::z_call_s,
+ &Processor::z_remove_obj,
+ &Processor::z_print_obj,
+ &Processor::z_ret,
+ &Processor::z_jump,
+ &Processor::z_print_paddr,
+ &Processor::z_load,
+ &Processor::z_call_n
+ };
+
+ Common::copy(&OP0_OPCODES[0], &OP0_OPCODES[16], op0_opcodes);
+ Common::copy(&OP1_OPCODES[0], &OP1_OPCODES[16], op1_opcodes);
+ Common::fill(&_stack[0], &_stack[STACK_SIZE], 0);
+ Common::fill(&zargs[0], &zargs[8], 0);
+ Common::fill(&_buffer[0], &_buffer[TEXT_BUFFER_SIZE], '\0');
+ Common::fill(&_errorCount[0], &_errorCount[ERR_NUM_ERRORS], 0);
+}
+
+void Processor::initialize() {
+ Mem::initialize();
+ GlkInterface::initialize();
+
+ if (h_version <= V4) {
+ op0_opcodes[9] = &Processor::z_pop;
+ op1_opcodes[15] = &Processor::z_not;
+ } else {
+ op0_opcodes[9] = &Processor::z_catch;
+ op1_opcodes[15] = &Processor::z_call_n;
+ }
+}
+
+void Processor::load_operand(zbyte type) {
+ zword value;
+
+ if (type & 2) {
+ // variable
+ zbyte variable;
+
+ CODE_BYTE(variable);
+
+ if (variable == 0)
+ value = *_sp++;
+ else if (variable < 16)
+ value = *(_fp - variable);
+ else {
+ zword addr = h_globals + 2 * (variable - 16);
+ LOW_WORD(addr, value);
+ }
+ } else if (type & 1) {
+ // small constant
+ zbyte bvalue;
+
+ CODE_BYTE(bvalue);
+ value = bvalue;
+
+ } else {
+ // large constant
+ CODE_WORD(value);
+ }
+
+ zargs[zargc++] = value;
+}
+
+void Processor::load_all_operands(zbyte specifier) {
+ for (int i = 6; i >= 0; i -= 2) {
+ zbyte type = (specifier >> i) & 0x03;
+
+ if (type == 3)
+ break;
+
+ load_operand(type);
+ }
+}
+
+void Processor::interpret() {
+ do {
+ zbyte opcode;
+ CODE_BYTE(opcode);
+ zargc = 0;
+
+ if (opcode < 0x80) {
+ // 2OP opcodes
+ load_operand((zbyte)(opcode & 0x40) ? 2 : 1);
+ load_operand((zbyte)(opcode & 0x20) ? 2 : 1);
+
+ (*this.*var_opcodes[opcode & 0x1f])();
+
+ } else if (opcode < 0xb0) {
+ // 1OP opcodes
+ load_operand((zbyte)(opcode >> 4));
+
+ (*this.*op1_opcodes[opcode & 0x0f])();
+
+ } else if (opcode < 0xc0) {
+ // 0OP opcodes
+ (*this.*op0_opcodes[opcode - 0xb0])();
+
+ } else {
+ // VAR opcodes
+ zbyte specifier1;
+ zbyte specifier2;
+
+ if (opcode == 0xec || opcode == 0xfa) { // opcodes 0xec
+ CODE_BYTE(specifier1); // and 0xfa are
+ CODE_BYTE(specifier2); // call opcodes
+ load_all_operands(specifier1); // with up to 8
+ load_all_operands(specifier2); // arguments
+ } else {
+ CODE_BYTE(specifier1);
+ load_all_operands(specifier1);
+ }
+
+ (*this.*var_opcodes[opcode - 0xc0])();
+ }
+
+#if defined(DJGPP) && defined(SOUND_SUPPORT)
+ if (end_of_sound_flag)
+ end_of_sound();
+#endif
+ } while (!_finished);
+
+ _finished--;
+}
+
+void Processor::call(zword routine, int argc, zword *args, int ct) {
+ long pc;
+ zword value;
+ zbyte count;
+ int i;
+
+ if (_sp - _stack < 4)
+ runtimeError(ERR_STK_OVF);
+
+ GET_PC(pc);
+
+ *--_sp = (zword)(pc >> 9);
+ *--_sp = (zword)(pc & 0x1ff);
+ *--_sp = (zword)(_fp - _stack - 1);
+ *--_sp = (zword)(argc | (ct << (_save_quetzal ? 12 : 8)));
+
+ _fp = _sp;
+ _frameCount++;
+
+ // Calculate byte address of routine
+ if (h_version <= V3)
+ pc = (long)routine << 1;
+ else if (h_version <= V5)
+ pc = (long)routine << 2;
+ else if (h_version <= V7)
+ pc = ((long)routine << 2) + ((long)h_functions_offset << 3);
+ else if (h_version <= V8)
+ pc = (long)routine << 3;
+ else {
+ // h_version == V9
+ long indirect = (long)routine << 2;
+ HIGH_LONG(indirect, pc);
+ }
+
+ if ((uint)pc >= story_size)
+ runtimeError(ERR_ILL_CALL_ADDR);
+
+ SET_PC(pc);
+
+ // Initialise local variables
+ CODE_BYTE(count);
+
+ if (count > 15)
+ runtimeError(ERR_CALL_NON_RTN);
+ if (_sp - _stack < count)
+ runtimeError(ERR_STK_OVF);
+
+ if (_save_quetzal)
+ _fp[0] |= (zword)count << 8; // Save local var count for Quetzal.
+
+ value = 0;
+
+ for (i = 0; i < count; i++) {
+ if (h_version <= V4) // V1 to V4 games provide default
+ CODE_WORD(value); // values for all local variables
+
+ *--_sp = (zword)((argc-- > 0) ? args[i] : value);
+ }
+
+ // Start main loop for direct calls
+ if (ct == 2)
+ interpret();
+}
+
+void Processor::ret(zword value) {
+ long pc;
+ int ct;
+
+ if (_sp > _fp)
+ runtimeError(ERR_STK_UNDF);
+
+ _sp = _fp;
+
+ ct = *_sp++ >> (_save_quetzal ? 12 : 8);
+ _frameCount--;
+ _fp = _stack + 1 + *_sp++;
+ pc = *_sp++;
+ pc = ((long)*_sp++ << 9) | pc;
+
+ SET_PC(pc);
+
+ // Handle resulting value
+ if (ct == 0)
+ store(value);
+ if (ct == 2)
+ *--_sp = value;
+
+ // Stop main loop for direct calls
+ if (ct == 2)
+ _finished++;
+}
+
+void Processor::branch(bool flag) {
+ long pc;
+ zword offset;
+ zbyte specifier;
+ zbyte off1;
+ zbyte off2;
+
+ CODE_BYTE(specifier);
+ off1 = specifier & 0x3f;
+
+ if (!flag)
+ specifier ^= 0x80;
+
+ if (!(specifier & 0x40)) {
+ // it's a long branch
+ if (off1 & 0x20) // propagate sign bit
+ off1 |= 0xc0;
+
+ CODE_BYTE(off2);
+ offset = (off1 << 8) | off2;
+ } else {
+ // It's a short branch
+ offset = off1;
+ }
+
+ if (specifier & 0x80) {
+ if (offset > 1) {
+ // normal branch
+ GET_PC(pc);
+ pc += (short)offset - 2;
+ SET_PC(pc);
+ } else {
+ // special case, return 0 or 1
+ ret(offset);
+ }
+ }
+}
+
+void Processor::store(zword value) {
+ zbyte variable;
+
+ CODE_BYTE(variable);
+
+ if (variable == 0)
+ *--_sp = value;
+ else if (variable < 16)
+ *(_fp - variable) = value;
+ else {
+ zword addr = h_globals + 2 * (variable - 16);
+ SET_WORD(addr, value);
+ }
+}
+
+int Processor::direct_call(zword addr) {
+ zword saved_zargs[8];
+ int saved_zargc;
+ int i;
+
+ // Calls to address 0 return false
+ if (addr == 0)
+ return 0;
+
+ // Save operands and operand count
+ for (i = 0; i < 8; i++)
+ saved_zargs[i] = zargs[i];
+
+ saved_zargc = zargc;
+
+ // Call routine directly
+ call(addr, 0, 0, 2);
+
+ // Restore operands and operand count
+ for (i = 0; i < 8; i++)
+ zargs[i] = saved_zargs[i];
+
+ zargc = saved_zargc;
+
+ // Resulting value lies on top of the stack
+ return (short)*_sp++;
+}
+
+void Processor::seed_random(int value) {
+ if (value == 0) {
+ // Now using random values
+ _randomInterval = 0;
+ } else if (value < 1000) {
+ // special seed value
+ _randomCtr = 0;
+ _randomInterval = value;
+ } else {
+ // standard seed value
+ _random.setSeed(value);
+ _randomInterval = 0;
+ }
+}
+
+void Processor::__extended__() {
+ zbyte opcode;
+ zbyte specifier;
+
+ CODE_BYTE(opcode);
+ CODE_BYTE(specifier);
+
+ load_all_operands(specifier);
+
+ if (opcode < 0x1e) // extended opcodes from 0x1e on
+ (*this.*ext_opcodes[opcode])(); // are reserved for future spec'
+}
+
+void Processor::__illegal__() {
+ runtimeError(ERR_ILL_OPCODE);
+}
+
+void Processor::z_catch() {
+ store(_save_quetzal ? _frameCount : (zword)(_fp - _stack));
+}
+
+void Processor::z_throw() {
+ if (_save_quetzal) {
+ if (zargs[1] > _frameCount)
+ runtimeError(ERR_BAD_FRAME);
+
+ // Unwind the stack a frame at a time.
+ for (; _frameCount > zargs[1]; --_frameCount)
+ _fp = _stack + 1 + _fp[1];
+ } else {
+ if (zargs[1] > STACK_SIZE)
+ runtimeError(ERR_BAD_FRAME);
+
+ _fp = _stack + zargs[1];
+ }
+
+ ret(zargs[0]);
+}
+
+void Processor::z_call_n() {
+ if (zargs[0] != 0)
+ call(zargs[0], zargc - 1, zargs + 1, 1);
+}
+
+void Processor::z_call_s() {
+ if (zargs[0] != 0)
+ call(zargs[0], zargc - 1, zargs + 1, 0);
+ else
+ store(0);
+}
+
+void Processor::z_check_arg_count() {
+ if (_fp == _stack + STACK_SIZE)
+ branch(zargs[0] == 0);
+ else
+ branch(zargs[0] <= (*_fp & 0xff));
+}
+
+void Processor::z_jump() {
+ long pc;
+ GET_PC(pc);
+
+ pc += (short)zargs[0] - 2;
+
+ if ((uint)pc >= story_size)
+ runtimeError(ERR_ILL_JUMP_ADDR);
+
+ SET_PC(pc);
+}
+
+void Processor::z_nop() {
+ // Do nothing
+}
+
+void Processor::z_quit() {
+ _finished = 9999;
+}
+
+void Processor::z_ret() {
+ ret(zargs[0]);
+}
+
+void Processor::z_ret_popped() {
+ ret(*_sp++);
+}
+
+void Processor::z_rfalse() {
+ ret(0);
+}
+
+void Processor::z_rtrue() {
+ ret(1);
+}
+
+void Processor::z_random() {
+ if ((short) zargs[0] <= 0) {
+ // set random seed
+ seed_random(- (short) zargs[0]);
+ store(0);
+
+ } else {
+ // generate random number
+ zword result;
+ if (_randomInterval != 0) {
+ // ...in special mode
+ result = _randomCtr++;
+ if (_randomCtr == _randomInterval)
+ _randomCtr = 0;
+ } else {
+ // ...in standard mode
+ result = _random.getRandomNumber(0xffff);
+ }
+
+ store((zword)(result % zargs[0] + 1));
+ }
+}
+
+void Processor::z_sound_effect() {
+ zword number = zargs[0];
+ zword effect = zargs[1];
+ zword volume = zargs[2];
+
+ if (zargc < 1)
+ number = 0;
+ if (zargc < 2)
+ effect = EFFECT_PLAY;
+ if (zargc < 3)
+ volume = 8;
+
+ if (number >= 3 || number == 0) {
+ _soundLocked = true;
+
+ if (_storyId == LURKING_HORROR && (number == 9 || number == 16)) {
+ if (effect == EFFECT_PLAY) {
+ next_sample = number;
+ next_volume = volume;
+
+ _soundLocked = false;
+
+ if (!_soundPlaying)
+ start_next_sample();
+ } else {
+ _soundLocked = false;
+ }
+ return;
+ }
+
+ _soundPlaying = false;
+
+ switch (effect) {
+
+ case EFFECT_PREPARE:
+ os_prepare_sample (number);
+ break;
+ case EFFECT_PLAY:
+ start_sample(number, lo (volume), hi (volume), (zargc == 4) ? zargs[3] : 0);
+ break;
+ case EFFECT_STOP:
+ os_stop_sample (number);
+ break;
+ case EFFECT_FINISH_WITH:
+ os_finish_with_sample (number);
+ break;
+ }
+
+ _soundLocked = false;
+ } else {
+ os_beep(number);
+ }
+}
+
+void Processor::z_piracy() {
+ branch(!_piracy);
+}
+
+void Processor::z_save_undo(void) {
+ store((zword)save_undo());
+}
+
+void Processor::z_restore_undo(void) {
+ store((zword)restore_undo());
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle