ifengine/control.c
2018-09-08 19:51:49 -05:00

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 */