369 lines
7.9 KiB
C
369 lines
7.9 KiB
C
|
|
/* $Id: control.c,v 1.2 2000/05/25 22:28:56 jholder Exp $
|
|
* --------------------------------------------------------------------
|
|
* see doc/License.txt for License Information
|
|
* --------------------------------------------------------------------
|
|
*
|
|
* File name: $Id: control.c,v 1.2 2000/05/25 22:28:56 jholder Exp $
|
|
*
|
|
* Description:
|
|
*
|
|
* Modification history:
|
|
* $Log: control.c,v $
|
|
* Revision 1.2 2000/05/25 22:28:56 jholder
|
|
* changes routine names to reflect zmachine opcode names per spec 1.0
|
|
*
|
|
* Revision 1.1.1.1 2000/05/10 14:21:34 jholder
|
|
*
|
|
* imported
|
|
*
|
|
*
|
|
* --------------------------------------------------------------------
|
|
*/
|
|
|
|
/*
|
|
* control.c
|
|
*
|
|
* Functions that alter the flow of control.
|
|
*
|
|
*/
|
|
|
|
#include "ztypes.h"
|
|
|
|
static const char *v1_lookup_table[3] = {
|
|
"abcdefghijklmnopqrstuvwxyz",
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
|
" 0123456789.,!?_#'\"/\\<-:()"
|
|
};
|
|
|
|
static const char *v3_lookup_table[3] = {
|
|
"abcdefghijklmnopqrstuvwxyz",
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
|
" \n0123456789.,!?_#'\"/\\-:()"
|
|
};
|
|
|
|
/*
|
|
* z_check_arg_count
|
|
*
|
|
* Jump if argument is present.
|
|
*
|
|
*/
|
|
|
|
void z_check_arg_count( zword_t argc )
|
|
{
|
|
|
|
conditional_jump( argc <= ( zword_t ) ( stack[fp + 1] & ARGS_MASK ) );
|
|
|
|
} /* z_check_arg_count */
|
|
|
|
/*
|
|
* z_call
|
|
*
|
|
* Call a subroutine. Save PC and FP then load new PC and initialise stack based
|
|
* local arguments.
|
|
*
|
|
* Implements: call_1s, call_1n, call_2s, call_2n, call, call_vs, call_vs2, call_vn, call_vn2
|
|
*
|
|
*/
|
|
|
|
int z_call( int argc, zword_t * argv, int type )
|
|
{
|
|
zword_t arg;
|
|
int i = 1, args, status = 0;
|
|
|
|
/* Convert calls to 0 as returning FALSE */
|
|
|
|
if ( argv[0] == 0 )
|
|
{
|
|
if ( type == FUNCTION )
|
|
store_operand( FALSE );
|
|
return ( 0 );
|
|
}
|
|
|
|
/* Save current PC, FP and argument count on stack */
|
|
|
|
stack[--sp] = ( zword_t ) ( pc / PAGE_SIZE );
|
|
stack[--sp] = ( zword_t ) ( pc % PAGE_SIZE );
|
|
stack[--sp] = fp;
|
|
stack[--sp] = ( argc - 1 ) | type;
|
|
|
|
/* Create FP for new subroutine and load new PC */
|
|
|
|
fp = sp - 1;
|
|
pc = ( unsigned long ) argv[0] * story_scaler;
|
|
#if defined(USE_QUETZAL)
|
|
++frame_count;
|
|
#endif
|
|
|
|
/* Read argument count and initialise local variables */
|
|
|
|
args = ( unsigned int ) read_code_byte( );
|
|
#if defined(USE_QUETZAL)
|
|
stack[sp] |= args << VAR_SHIFT;
|
|
#endif
|
|
while ( --args >= 0 )
|
|
{
|
|
arg = ( h_type > V4 ) ? 0 : read_code_word( );
|
|
stack[--sp] = ( --argc > 0 ) ? argv[i++] : arg;
|
|
}
|
|
|
|
/* If the call is asynchronous then call the interpreter directly.
|
|
* We will return back here when the corresponding return frame is
|
|
* encountered in the ret call. */
|
|
|
|
if ( type == ASYNC )
|
|
{
|
|
status = interpret( );
|
|
interpreter_state = RUN;
|
|
interpreter_status = 1;
|
|
}
|
|
|
|
return ( status );
|
|
|
|
} /* z_call */
|
|
|
|
/*
|
|
* z_ret
|
|
*
|
|
* Return from subroutine. Restore FP and PC from stack.
|
|
*
|
|
*/
|
|
|
|
void z_ret( zword_t value )
|
|
{
|
|
zword_t argc;
|
|
|
|
/* Clean stack */
|
|
|
|
sp = fp + 1;
|
|
|
|
/* Restore argument count, FP and PC */
|
|
|
|
argc = stack[sp++];
|
|
fp = stack[sp++];
|
|
pc = stack[sp++];
|
|
pc += ( unsigned long ) stack[sp++] * PAGE_SIZE;
|
|
#if defined(USE_QUETZAL)
|
|
--frame_count;
|
|
#endif
|
|
|
|
/* If this was an async call then stop the interpreter and return
|
|
* the value from the async routine. This is slightly hacky using
|
|
* a global state variable, but ret can be called with conditional_jump
|
|
* which in turn can be called from all over the place, sigh. A
|
|
* better design would have all opcodes returning the status RUN, but
|
|
* this is too much work and makes the interpreter loop look ugly */
|
|
|
|
if ( ( argc & TYPE_MASK ) == ASYNC )
|
|
{
|
|
interpreter_state = STOP;
|
|
interpreter_status = ( int ) value;
|
|
}
|
|
else
|
|
{
|
|
/* Return subroutine value for function call only */
|
|
if ( ( argc & TYPE_MASK ) == FUNCTION )
|
|
{
|
|
store_operand( value );
|
|
}
|
|
}
|
|
|
|
} /* z_ret */
|
|
|
|
/*
|
|
* z_jump
|
|
*
|
|
* Unconditional jump. Jump is PC relative.
|
|
*
|
|
*/
|
|
|
|
void z_jump( zword_t offset )
|
|
{
|
|
|
|
pc = ( unsigned long ) ( pc + ( ZINT16 ) offset - 2 );
|
|
|
|
} /* z_jump */
|
|
|
|
/*
|
|
* z_restart
|
|
*
|
|
* Restart game by initialising environment and reloading start PC.
|
|
*
|
|
*/
|
|
|
|
void z_restart( void )
|
|
{
|
|
unsigned int i, j, restart_size, scripting_flag;
|
|
|
|
/* Reset output buffer */
|
|
|
|
flush_buffer( TRUE );
|
|
|
|
/* Reset text control flags */
|
|
|
|
formatting = ON;
|
|
outputting = ON;
|
|
redirecting = OFF;
|
|
redirect_depth = 0;
|
|
scripting_disable = OFF;
|
|
|
|
/* Randomise */
|
|
|
|
SRANDOM_FUNC( ( unsigned int ) time( NULL ) );
|
|
|
|
/* Remember scripting state */
|
|
|
|
scripting_flag = get_word( H_FLAGS ) & SCRIPTING_FLAG;
|
|
|
|
/* Load restart size and reload writeable data area */
|
|
|
|
restart_size = ( h_restart_size / PAGE_SIZE ) + 1;
|
|
for ( i = 0; i < restart_size; i++ )
|
|
{
|
|
read_page( i, &datap[i * PAGE_SIZE] );
|
|
}
|
|
|
|
/* Restart the screen */
|
|
|
|
z_split_window( 0 );
|
|
set_colours( 1, 1 ); /* set default colors, added by JDH 8/6/95 */
|
|
set_attribute( NORMAL );
|
|
z_erase_window( Z_SCREEN );
|
|
|
|
restart_screen( );
|
|
|
|
/* Reset the interpreter state */
|
|
|
|
restart_interp( scripting_flag );
|
|
|
|
/* Initialise the character translation lookup tables */
|
|
|
|
for ( i = 0; i < 3; i++ )
|
|
{
|
|
for ( j = 0; j < 26; j++ )
|
|
{
|
|
if ( h_alternate_alphabet_offset )
|
|
{
|
|
lookup_table[i][j] = get_byte( h_alternate_alphabet_offset + ( i * 26 ) + j );
|
|
}
|
|
else
|
|
{
|
|
if ( h_type == V1 )
|
|
{
|
|
lookup_table[i][j] = v1_lookup_table[i][j];
|
|
}
|
|
else
|
|
{
|
|
lookup_table[i][j] = v3_lookup_table[i][j];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Load start PC, SP and FP */
|
|
|
|
pc = h_start_pc;
|
|
sp = STACK_SIZE;
|
|
fp = STACK_SIZE - 1;
|
|
#if defined (USE_QUETZAL)
|
|
frame_count = 0;
|
|
#endif
|
|
|
|
} /* z_restart */
|
|
|
|
|
|
/*
|
|
* restart_interp
|
|
*
|
|
* Do all the things which need to be done after startup, restart, and restore
|
|
* commands.
|
|
*
|
|
*/
|
|
|
|
void restart_interp( int scripting_flag )
|
|
{
|
|
if ( scripting_flag )
|
|
set_word( H_FLAGS, ( get_word( H_FLAGS ) | SCRIPTING_FLAG ) );
|
|
|
|
set_byte( H_INTERPRETER, h_interpreter );
|
|
set_byte( H_INTERPRETER_VERSION, h_interpreter_version );
|
|
set_byte( H_SCREEN_ROWS, screen_rows ); /* Screen dimension in characters */
|
|
set_byte( H_SCREEN_COLUMNS, screen_cols );
|
|
|
|
set_byte( H_SCREEN_LEFT, 0 ); /* Screen dimension in smallest addressable units, ie. pixels */
|
|
set_byte( H_SCREEN_RIGHT, screen_cols );
|
|
set_byte( H_SCREEN_TOP, 0 );
|
|
set_byte( H_SCREEN_BOTTOM, screen_rows );
|
|
|
|
set_byte( H_MAX_CHAR_WIDTH, 1 ); /* Size of a character in screen units */
|
|
set_byte( H_MAX_CHAR_HEIGHT, 1 );
|
|
|
|
/* Initialise status region */
|
|
|
|
if ( h_type < V4 )
|
|
{
|
|
z_split_window( 0 );
|
|
blank_status_line( );
|
|
}
|
|
|
|
if ( h_type == V3 && fTandy )
|
|
{
|
|
zbyte_t config_byte = get_byte( H_CONFIG );
|
|
|
|
config_byte |= CONFIG_TANDY;
|
|
set_byte( H_CONFIG, config_byte );
|
|
}
|
|
|
|
} /* restart_interp */
|
|
|
|
/*
|
|
* z_catch
|
|
*
|
|
* Return the value of the frame pointer (FP) for later use with throw.
|
|
* Before V5 games this was a simple pop.
|
|
*
|
|
*/
|
|
|
|
void z_catch( void )
|
|
{
|
|
if ( h_type > V4 )
|
|
{
|
|
#if defined (USE_QUETZAL)
|
|
store_operand( frame_count );
|
|
#else
|
|
store_operand( fp );
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
sp++;
|
|
}
|
|
} /* z_catch */
|
|
|
|
/*
|
|
* z_throw
|
|
*
|
|
* Remove one or more stack frames and return. Works like longjmp, see z_catch.
|
|
*
|
|
*/
|
|
|
|
void z_throw( zword_t value, zword_t new_fp )
|
|
{
|
|
|
|
if ( new_fp > fp )
|
|
{
|
|
fatal( "z_throw(): nonexistant frame" );
|
|
}
|
|
#if defined (USE_QUETZAL)
|
|
for ( ; new_fp < frame_count; --frame_count )
|
|
{
|
|
sp = fp + 1;
|
|
fp = stack[sp + 1];
|
|
}
|
|
#else
|
|
fp = new_fp;
|
|
#endif
|
|
|
|
z_ret( value );
|
|
|
|
} /* z_throw */
|