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 | 
