diff options
Diffstat (limited to 'engines/glk/frotz/processor.cpp')
-rw-r--r-- | engines/glk/frotz/processor.cpp | 664 |
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 |