Initial commit.

This commit is contained in:
Scott Duensing 2018-09-08 19:51:49 -05:00
commit c4e3fb7939
27 changed files with 9733 additions and 0 deletions

2
.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
*.sta filter=lfs diff=lfs merge=lfs -text
*.z5 filter=lfs diff=lfs merge=lfs -text

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*~
*.user

BIN
8x8thin.sta Normal file

Binary file not shown.

535
ansiterm.c Normal file
View file

@ -0,0 +1,535 @@
#include <string.h>
#include <stdarg.h>
#include "joey.h"
#ifdef JOEY_IIGS
segment "ansiterm";
#endif
#include "ansiterm.h"
typedef struct {
int x;
int y;
} Vector2T;
static int _columns = 40;
static int _rows = 25;
static int _xoff = 0;
static int _yoff = 0;
static bool _escFound = false;
static bool _inEscape = false;
static bool _bold = false;
static bool _blink = false;
static bool _reverse = false;
static int _foreground = 7;
static int _background = 0;
static int _fColor = 7;
static int _bColor = 0;
static int _number = 0;
static int _parameters[5];
static int _parameterCount = 0;
static Vector2T _cursorSave;
static Vector2T _cursor;
static jlStaT *_ansiFont = NULL;
static int *_screenBuffer = NULL;
void termDebug(const char *what, ...);
bool termParseANSI(char c);
void termRenderCharacterAtCursor(char c);
void termRepaint(void);
void termResetSequence(void);
void termScrollUp(void);
void termUpdateColors(void);
void termClearScreen(void) {
memset(_screenBuffer, 0, (size_t)(2 * _columns * _rows));
}
__attribute__((__format__ (__printf__, 1, 0)))
void termDebug(const char *what, ...) {
char msg[1024];
va_list va;
va_start(va, what);
vsprintf(msg, what, va);
va_end(va);
printf("%s\n", msg);
}
void termGetCursor(int *x, int *y) {
*x = _cursor.x;
*y = _cursor.y;
}
void termMoveCursor(int x, int y) {
if (x < 1) {
_cursor.x = 1;
termDebug("Attempt to position cursor too far left: %d", x);
} else {
if (x > _columns) {
_cursor.x = _columns;
termDebug("Attempt to position cursor too far right: %d", x);
} else {
_cursor.x = x;
}
}
if (y < 1) {
_cursor.y = 1;
termDebug("Attempt to position cursor too far up: %d", y);
} else {
if (y > _rows) {
_cursor.y = _rows;
termDebug("Attempt to position cursor too far down: %d", y);
} else {
_cursor.y = y;
}
}
}
bool termParseANSI(char c) {
int x;
int y;
int oldX;
int cx;
int p;
bool updateDisplay = false;
Vector2T cursor = _cursor;
// Find leading ESC character.
if ((c == (char)27) && !_escFound && !_inEscape) {
_escFound = true;
return updateDisplay;
}
// Find [ character.
if ((c == '[') && _escFound && !_inEscape) {
_inEscape = true;
return updateDisplay;
}
// We don't do non-'ESC[' sequences.
if (_escFound && !_inEscape) {
termDebug("Unexpected Character: %d", (int)c);
termResetSequence();
}
if (_inEscape) {
switch (c) {
// All we know is that a Xenomorph may be involved.
case '[':
case (char)27:
termDebug("BUG! Escape found inside sequence.");
break;
// End of a parameter.
case ';':
_parameters[_parameterCount++] = _number;
_number = 0;
break;
// Cursor up.
case 'A':
y = _number;
termDebug("Moving cursor up %d", y);
termMoveCursor(cursor.x, cursor.y - y);
termResetSequence();
break;
// Cursor down.
case 'B':
y = _number;
termDebug("Moving cursor down %d", y);
termMoveCursor(cursor.x, cursor.y + y);
termResetSequence();
break;
// Cursor forward.
case 'C':
x = _number;
termDebug("Moving cursor right %d", x);
termMoveCursor(cursor.x + x, cursor.y);
termResetSequence();
break;
// Cursor backward.
case 'D':
x = _number;
termDebug("Moving cursor left %d", x);
termMoveCursor(cursor.x - x, cursor.y);
termResetSequence();
break;
// Cursor line down.
case 'E':
y = _number;
termDebug("Moving cursor down line %d", y);
termMoveCursor(1, cursor.y + y);
//***TODO*** This should allow scrolling
termResetSequence();
break;
// Cursor line up.
case 'F':
y = _number;
termDebug("Moving cursor up line %d", y);
termMoveCursor(1, cursor.y - y);
termResetSequence();
break;
// Cursor horizontal absolute.
case 'G':
x = _number;
termDebug("Moving cursor horizontally to %d", x);
termMoveCursor(x, cursor.y);
termResetSequence();
break;
// Move cursor.
case 'H':
case 'f':
switch (_parameterCount) {
case 0:
// Absolute position, Y. Kinda. Moves X to 1.
y = _number;
termDebug("Moving cursor to 1x%d", y);
termMoveCursor(1, y);
break;
case 1:
// Absolute position.
x = _number;
y = _parameters[0];
termDebug("Moving cursor to %dx%d", x, y);
termMoveCursor(x, y);
break;
default:
termDebug("Unknown Cursor Move");
break;
}
termResetSequence();
break;
// Clear display.
case 'J':
if ((_parameterCount == 0) && (_number == 2)) {
termDebug("Clear display");
termClearScreen();
updateDisplay = true;
termMoveCursor(1, 1);
} else {
termDebug("Unimplemented Screen Clear");
}
termResetSequence();
break;
// Clear from cursor to end of line.
case 'K':
x = _number;
if (x == 0) {
termDebug("Clear to end of line");
oldX = cursor.x;
for (cx=cursor.x; cx<=_columns; cx++) {
termMoveCursor(cx, cursor.y);
termRenderCharacterAtCursor(' ');
}
termMoveCursor(oldX, cursor.y);
} else {
termDebug("Unimplemented Line Clear");
}
termResetSequence();
break;
// Insert lines.
case 'L':
//***TODO***
termDebug("Unimplemented Insert Line");
termResetSequence();
break;
// Delete lines.
case 'M':
//***TODO***
termDebug("Unimplemented Delete Line");
termResetSequence();
break;
// Delete characters.
case 'P':
//***TODO***
termDebug("Unimplemented Delete Character");
termResetSequence();
break;
// Scroll Up.
case 'S':
//***TODO***
termDebug("Unimplemented Scroll Up");
termResetSequence();
break;
// Scroll Down.
case 'T':
//***TODO***
termDebug("Unimplemented Scroll Down");
termResetSequence();
break;
// Clear Screen with Normal Attribute.
case 'U':
x = _bColor;
_bColor = 0;
termUpdateColors();
termClearScreen();
updateDisplay = true;
_bColor = x;
termUpdateColors();
termMoveCursor(1, 1);
termResetSequence();
break;
// Back-TAB.
case 'Z':
//***TODO***
termDebug("Unimplemented Back TAB");
termResetSequence();
break;
// Set attributes.
case 'm':
_parameters[_parameterCount++] = _number;
for (p=0; p<_parameterCount; p++) {
termDebug("Set attribute %d", _parameters[p]);
x = _parameters[p];
switch (x) {
case 0:
_bold = false;
_blink = false;
_reverse = false;
_fColor = 7;
_bColor = 0;
termUpdateColors();
break;
case 1:
_bold = true;
termUpdateColors();
break;
case 5:
case 6:
_blink = true;
break;
case 7:
if (!_reverse) {
x = _fColor;
_fColor = _bColor;
_bColor = x;
termUpdateColors();
}
_reverse = true;
break;
case 21:
case 22:
_bold = false;
termUpdateColors();
break;
case 25:
_blink = false;
break;
case 27:
if (_reverse) {
x = _fColor;
_fColor = _bColor;
_bColor = x;
termUpdateColors();
}
_reverse = false;
break;
default:
if ((x > 29) && (x < 38)) {
_fColor = x - 30;
termUpdateColors();
} else {
if ((x > 39) && (x < 48)) {
_bColor = x - 40;
termUpdateColors();
} else {
termDebug("Unknown attribute: %d", (int)c);
}
}
break;
}
}
termResetSequence();
break;
// Define scroll region.
case 'r':
//***TODO***
termDebug("Unimplemented Scroll Region");
termResetSequence();
break;
// Cursor save.
case 's':
termDebug("Saving cursor");
_cursorSave = cursor;
termResetSequence();
break;
// Cursor restore.
case 'u':
termDebug("Restoring cursor");
termMoveCursor(_cursorSave.x, _cursorSave.y);
termResetSequence();
break;
// Something else.
default:
// Number?
if ((c >= '0') && (c <= '9')) {
_number = _number * 10 + (c - '0');
} else {
termDebug("Unknown sequence: %d", (int)c);
termResetSequence();
}
break;
}
} else {
switch (c) {
case (char)7:
// Beep!
termDebug("Unimplemented beep");
break;
case (char)8:
case (char)127:
//***TODO*** Backspace
termDebug("Unimplemented backspace");
break;
case (char)9:
//***TODO*** TAB
termDebug("Unimplemented tab");
break;
case (char)10:
cursor.y++;
if (cursor.y > _rows) {
cursor.y--;
termScrollUp();
updateDisplay = true;
}
termMoveCursor(cursor.x, cursor.y);
break;
case (char)12:
x = _bColor;
_bColor = 0;
termUpdateColors();
termClearScreen();
updateDisplay = true;
_bColor = x;
termUpdateColors();
termMoveCursor(1, 1);
break;
case (char)13:
termMoveCursor(1, cursor.y);
break;
default:
updateDisplay = true;
termRenderCharacterAtCursor(c);
if (_blink) {
//***TODO*** Not handled
}
cursor.x++;
if (cursor.x > _columns) {
cursor.x = 1;
cursor.y++;
if (cursor.y > _rows) {
cursor.y--;
termScrollUp();
}
}
termMoveCursor(cursor.x, cursor.y);
break;
}
}
return updateDisplay;
}
void termPrint(char *string) {
int x;
int l = (int)strlen(string);
for (x=0; x<l; x++) {
termPrintChar(string[x]);
}
}
void termPrintChar(char c) {
if (termParseANSI(c)) {
jlDisplayPresent();
}
}
void termRenderCharacterAtCursor(char c) {
int r = (_reverse ? 7 : 0);
int cx = c % 40;
int cy = (c / 40) + r;
_screenBuffer[(_cursor.y * _columns) + _cursor.x] = (cy * 40) + cx;
jlDrawBlit8x8(_ansiFont, cx, cy, _cursor.x - 1 + _xoff, _cursor.y - 1 + _yoff);
}
void termRepaint(void) {
int i = 0;
int x;
int y;
for (y=0; y<_rows; y++) {
for (x=0; x<_columns; x++) {
jlDrawBlit8x8(_ansiFont, _screenBuffer[i] % 40, _screenBuffer[i] / 40, x, y);
i++;
}
}
}
void termResetSequence(void) {
_number = 0;
_inEscape = false;
_escFound = false;
_parameterCount = 0;
}
void termRestoreCursor(void) {
_cursor = _cursorSave;
}
void termSaveCursor(void) {
_cursorSave = _cursor;
}
void termScrollUp(void) {
int x;
int i = 0;
int j = _columns;
for (x=0; x < (_rows - 1) * _columns; x++) {
_screenBuffer[i++] = _screenBuffer[j++];
}
termRepaint();
}
void termStart(jlStaT *font, int xoff, int yoff, int cols, int rows) {
_ansiFont = font;
_xoff = xoff;
_yoff = yoff;
_columns = cols;
_rows = rows;
_cursorSave.x = 1;
_cursorSave.y = 1;
_cursor.x = 1;
_cursor.y = 1;
_screenBuffer = (int *)jlMalloc((size_t)(2 * _columns * _rows));
}
void termStop(void) {
if (_screenBuffer != NULL) {
jlFree(_screenBuffer);
_screenBuffer = NULL;
}
}
void termUpdateColors(void) {
_foreground = _fColor + (_bold ? 8 : 0);
_background = _bColor;
}

16
ansiterm.h Normal file
View file

@ -0,0 +1,16 @@
#ifndef H_ANSITERM_
#define H_ANSITERM_
#include "joey.h"
void termClearScreen(void);
void termGetCursor(int *x, int *y);
void termMoveCursor(int x, int y);
void termPrint(char *string);
void termPrintChar(char c);
void termRestoreCursor(void);
void termSaveCursor(void);
void termStart(jlStaT *font, int xoff, int yoff, int cols, int rows);
void termStop(void);
#endif // _H_ANSITERM_

26
build-IIgs.sh Executable file
View file

@ -0,0 +1,26 @@
#!/bin/bash -ex
PROJECT=ifengine
TARGET=${JOEY}/sdks/iix/IIgs/out/${PROJECT}
GSTARGET=31:/out/${PROJECT}
if [ -d ${TARGET} ]; then
rm -rf ${TARGET}
fi
mkdir -p ${TARGET}
cp -f ${JOEY}/dist/joey.h .
CFILES=($(ls -1 *.c))
OFILES=""
for F in "${CFILES[@]}"; do
O=${F%.*}
OFILES="${OFILES} ${GSTARGET}/${O}"
echo "Compiling ${F}..."
iix compile ${F} keep=${GSTARGET}/${O}
done
rm joey.h
cp -f ${JOEY}/dist/IIgs/joeylib#b20000 ${JOEY}/sdks/iix/IIgs/Libraries/joeylib
iix chtyp -t lib ${JOEY}/sdks/iix/IIgs/Libraries/joeylib
iix -DKeepType=S16 link ${OFILES} 13:joeylib keep=${GSTARGET}/${PROJECT}

369
control.c Normal file
View file

@ -0,0 +1,369 @@
/* $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 */

147
extern.c Normal file
View file

@ -0,0 +1,147 @@
/* $Id: extern.c,v 1.2 2000/10/04 23:07:57 jholder Exp $
* --------------------------------------------------------------------
* see doc/License.txt for License Information
* --------------------------------------------------------------------
*
* File name: $Id: extern.c,v 1.2 2000/10/04 23:07:57 jholder Exp $
*
* Description:
*
* Modification history:
* $Log: extern.c,v $
* Revision 1.2 2000/10/04 23:07:57 jholder
* fixed redirect problem with isolatin1 range chars
*
* Revision 1.1.1.1 2000/05/10 14:21:34 jholder
*
* imported
*
*
* --------------------------------------------------------------------
*/
/*
* extern.c
*
* Global data.
*
*/
#include "ztypes.h"
unsigned char JTERP;
int GLOBALVER;
/* Stuff for Latin-1 Charset */
unsigned char zscii2latin1[69] = {
0xe4, 0xf6, 0xfc, 0xc4, 0xd6, 0xdc, 0xdf, 0xbb,
0xab, 0xeb, 0xef, 0xff, 0xcb, 0xcf, 0xe1, 0xe9,
0xed, 0xf3, 0xfa, 0xfd, 0xc1, 0xc9, 0xcd, 0xd3,
0xda, 0xdd, 0xe0, 0xe8, 0xec, 0xf2, 0xf9, 0xc0,
0xc8, 0xcc, 0xd2, 0xd9, 0xe2, 0xea, 0xee, 0xf4,
0xfb, 0xc2, 0xca, 0xce, 0xd4, 0xdb, 0xe5, 0xc5,
0xf8, 0xd8, 0xe3, 0xf1, 0xf5, 0xc3, 0xd1, 0xd5,
0xe6, 0xc6, 0xe7, 0xc7, 0xfe, 0xf0, 0xde, 0xd0,
0xa3, 'o', 'O', 0xa1, 0xbf
};
/* Game header data */
zbyte_t h_type = 0;
zbyte_t h_config = 0;
zword_t h_version = 0;
zword_t h_data_size = 0;
zword_t h_start_pc = 0;
zword_t h_words_offset = 0;
zword_t h_objects_offset = 0;
zword_t h_globals_offset = 0;
zword_t h_restart_size = 0;
zword_t h_flags = 0;
zword_t h_synonyms_offset = 0;
zword_t h_file_size = 0;
zword_t h_checksum = 0;
zbyte_t h_interpreter = INTERP_MSDOS;
zbyte_t h_interpreter_version = 'B'; /* Interpreter version 2 */
zword_t h_alternate_alphabet_offset = 0;
zword_t h_unicode_table = 0;
/* Game version specific data */
int story_scaler = 0;
int story_shift = 0;
int property_mask = 0;
int property_size_mask = 0;
/* Stack and PC data */
zword_t stack[STACK_SIZE];
zword_t sp = STACK_SIZE;
zword_t fp = STACK_SIZE - 1;
zword_t frame_count = 0; /* frame pointer for get_fp */
unsigned long pc = 0;
int interpreter_state = RUN;
int interpreter_status = 0;
/* Data region data */
unsigned int data_size = 0;
zbyte_t *datap = NULL;
zbyte_t *undo_datap = NULL;
/* Screen size data */
int screen_rows = 0;
int screen_cols = 0;
int right_margin = DEFAULT_RIGHT_MARGIN;
int top_margin = DEFAULT_TOP_MARGIN;
char bigscreen = 0;
char monochrome = 0;
int hist_buf_size;
/* Current window data */
int screen_window = TEXT_WINDOW;
int interp_initialized = 0;
/* Formatting and output control data */
int gFontNum = 0;
int formatting = ON;
int outputting = ON;
int redirecting = OFF;
int redirect_depth = 0; /* 1 or higher means ON */
int scripting_disable = OFF;
int scripting = OFF;
int recording = OFF;
int replaying = OFF;
int font = 1;
int use_bg_color = 1;
ZINT16 default_fg = 9, default_bg = 6;
/* Status region data */
int status_active = OFF;
int status_size = 0;
/* Tandy bit requested */
char fTandy = 0;
/* IBM (not international) glyphs requested */
char fIBMGraphics = 0;
/* Text output buffer data */
int lines_written = 0;
int status_pos = 0;
/* Dynamic buffer data */
char *line = NULL;
char *status_line = NULL;
/* Character translation tables */
char lookup_table[3][26];

1350
fileio.c Normal file

File diff suppressed because it is too large Load diff

BIN
gamedata.z5 Normal file

Binary file not shown.

75
ifengine.pro Normal file
View file

@ -0,0 +1,75 @@
TEMPLATE = app
CONFIG += console
CONFIG -= \
app_bundle \
qt
DESTDIR = $$OUT_PWD/build
JOEY = /home/scott/joey
JOEY_INCLUDES = \
$$JOEY/dist
JOEY_HEADERS = \
$$JOEY/dist/joey.h
JOEY_LIBS = \
-L$$JOEY/dist/linux/x64 \
-Wl,-rpath,$$JOEY/dist/linux/x64 \
-Wl,--enable-new-dtags \
-l:joeylib.a \
-Wl,--no-undefined \
-ldl \
-lsndio \
-lpthread \
-lrt \
-lm
JOEY_FLAGS = \
-pthread \
-D_REENTRANT
ZIP_FLAGS = \
-DANSI_COLOR \
-DUNIX \
-DSCREEN_WIDTH=40 \
-DSCREEN_HEIGHT=25 \
QMAKE_CFLAGS += \
$$JOEY_FLAGS
INCLUDEPATH += \
$$JOEY_INCLUDES
HEADERS += \
$$JOEY_HEADERS \
ansiterm.h \
jzip.h \
ztypes.h
SOURCES += \
main.c \
ansiterm.c \
control.c \
extern.c \
fileio.c \
input.c \
interpre.c \
math.c \
memory.c \
object.c \
operand.c \
osdepend.c \
property.c \
quetzal.c \
screen.c \
text.c \
variable.c \
joeyio.c
LIBS += \
$$JOEY_LIBS \
DISTFILES += \
build-IIgs.sh

606
input.c Normal file
View file

@ -0,0 +1,606 @@
/* $Id: input.c,v 1.3 2000/07/05 15:20:34 jholder Exp $
* --------------------------------------------------------------------
* see doc/License.txt for License Information
* --------------------------------------------------------------------
*
* File name: $Id: input.c,v 1.3 2000/07/05 15:20:34 jholder Exp $
*
* Description:
*
* Modification history:
* $Log: input.c,v $
* Revision 1.3 2000/07/05 15:20:34 jholder
* Updated code to remove warnings.
*
* 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
*
*
* --------------------------------------------------------------------
*/
/*
* input.c
*
* Input routines
*
*/
#include "ztypes.h"
/* Statically defined word separator list */
static const char *separators = " \t\n\f.,?";
static zword_t dictionary_offset = 0;
static ZINT16 dictionary_size = 0;
static unsigned int entry_size = 0;
static void tokenise_line( zword_t, zword_t, zword_t, zword_t );
static const char *next_token( const char *, const char *, const char **, int *, const char * );
static zword_t find_word( int, const char *, long );
/*
* z_read_char
*
* Read one character with optional timeout
*
* argv[0] = input device (must be 1)
* argv[1] = timeout value in tenths of a second (optional)
* argv[2] = timeout action routine (optional)
*
*/
void z_read_char( int argc, zword_t * argv )
{
int c;
zword_t arg_list[2];
/* Supply default parameters */
if ( argc < 3 )
argv[2] = 0;
if ( argc < 2 )
argv[1] = 0;
/* Flush any buffered output before read */
flush_buffer( FALSE );
/* Reset line count */
lines_written = 0;
/* If more than one characters was asked for then fail the call */
if ( argv[0] != 1 )
c = 0;
else
{
if ( ( c = playback_key( ) ) == -1 )
{
/* Setup the timeout routine argument list */
arg_list[0] = argv[2];
arg_list[1] = 0; /* as per spec 1.0 */
/* was: arg_list[1] = argv[1]/10; */
/* Read a character with a timeout. If the input timed out then
* call the timeout action routine. If the return status from the
* timeout routine was 0 then try to read a character again */
do
{
flush_buffer( FALSE );
c = input_character( ( int ) argv[1] );
}
while ( c == -1 && z_call( 1, arg_list, ASYNC ) == 0 );
/* Fail call if input timed out */
if ( c == -1 )
c = 0;
else
record_key( c );
}
}
store_operand( (zword_t)c );
} /* z_read_char */
/*
* z_sread_aread
*
* Read a line of input with optional timeout.
*
* argv[0] = character buffer address
* argv[1] = token buffer address
* argv[2] = timeout value in seconds (optional)
* argv[3] = timeout action routine (optional)
*
*/
void z_sread_aread( int argc, zword_t * argv )
{
int i, in_size, out_size, terminator;
char *cbuf, *buffer;
/* Supply default parameters */
if ( argc < 4 )
argv[3] = 0;
if ( argc < 3 )
argv[2] = 0;
if ( argc < 2 )
argv[1] = 0;
/* Refresh status line */
if ( h_type < V4 )
z_show_status( );
/* Flush any buffered output before read */
flush_buffer( TRUE );
/* Reset line count */
lines_written = 0;
/* Initialise character pointer and initial read size */
cbuf = ( char * ) &datap[argv[0]];
in_size = ( h_type > V4 ) ? cbuf[1] : 0;
/* Read the line then script and record it */
terminator = get_line( cbuf, argv[2], argv[3] );
script_line( ( h_type > V4 ) ? &cbuf[2] : &cbuf[1] );
record_line( ( h_type > V4 ) ? &cbuf[2] : &cbuf[1] );
/* Convert new text in line to lowercase */
if ( h_type > V4 )
{
buffer = &cbuf[2];
out_size = cbuf[1];
}
else
{
buffer = &cbuf[1];
out_size = strlen( buffer );
}
if ( out_size > in_size )
for ( i = in_size; i < out_size; i++ )
buffer[i] = ( char ) tolower( buffer[i] );
/* Tokenise the line, if a token buffer is present */
if ( argv[1] )
tokenise_line( argv[0], argv[1], h_words_offset, 0 );
/* Return the line terminator */
if ( h_type > V4 )
store_operand( ( zword_t ) terminator );
} /* z_sread_aread */
/*
* get_line
*
* Read a line of input and lower case it.
*
*/
int get_line( char *cbuf, zword_t timeout, zword_t action_routine )
{
char *buffer;
int buflen, read_size, status, c;
zword_t arg_list[2];
/* Set maximum buffer size to width of screen minus any
* right margin and 1 character for a terminating NULL */
buflen = ( screen_cols > 127 ) ? 127 : screen_cols;
buflen -= right_margin + 1;
if ( ( int ) cbuf[0] <= buflen )
buflen = cbuf[0];
/* Set read size and start of read buffer. The buffer may already be
* primed with some text in V5 games. The Z-code will have already
* displayed the text so we don't have to do that */
if ( h_type > V4 )
{
read_size = cbuf[1];
buffer = &cbuf[2];
}
else
{
read_size = 0;
buffer = &cbuf[1];
}
/* Try to read input from command file */
c = playback_line( buflen, buffer, &read_size );
if ( c == -1 )
{
/* Setup the timeout routine argument list */
arg_list[0] = action_routine;
arg_list[1] = 0; /* as per spec.1.0 */
/* arg_list[1] = timeout/10; */
/* Read a line with a timeout. If the input timed out then
* call the timeout action routine. If the return status from the
* timeout routine was 0 then try to read the line again */
do
{
c = input_line( buflen, buffer, timeout, &read_size );
status = 0;
}
while ( c == -1 && ( status = z_call( 1, arg_list, ASYNC ) ) == 0 );
/* Throw away any input if timeout returns success */
if ( status )
read_size = 0;
}
/* Zero terminate line */
if ( h_type > V4 )
{
cbuf[1] = ( char ) read_size;
}
else
{
/* Zero terminate line (V1-4 only) */
buffer[read_size] = '\0';
}
return ( c );
} /* get_line */
/*
* tokenise_line
*
* Convert a typed input line into tokens. The token buffer needs some
* additional explanation. The first byte is the maximum number of tokens
* allowed. The second byte is set to the actual number of token read. Each
* token is composed of 3 fields. The first (word) field contains the word
* offset in the dictionary, the second (byte) field contains the token length,
* and the third (byte) field contains the start offset of the token in the
* character buffer.
*
*/
static void tokenise_line( zword_t char_buf, zword_t token_buf, zword_t dictionary, zword_t flag )
{
int i, count, words, token_length;
long word_index, chop = 0;
int slen;
char *str_end;
char *cbuf, *tbuf, *tp;
const char *cp, *token;
char punctuation[16];
zword_t word;
/* Initialise character and token buffer pointers */
cbuf = ( char * ) &datap[char_buf];
tbuf = ( char * ) &datap[token_buf];
/* Find the string length */
if ( h_type > V4 )
{
slen = ( unsigned char ) ( cbuf[1] );
str_end = cbuf + 2 + slen;
}
else
{
slen = strlen( cbuf + 1 );
str_end = cbuf + 1 + slen;
}
/* Initialise word count and pointers */
words = 0;
cp = ( h_type > V4 ) ? cbuf + 2 : cbuf + 1;
tp = tbuf + 2;
/* Initialise dictionary */
count = get_byte( dictionary++ );
for ( i = 0; i < count; i++ )
punctuation[i] = get_byte( dictionary++ );
punctuation[i] = '\0';
entry_size = get_byte( dictionary++ );
dictionary_size = ( ZINT16 ) get_word( dictionary );
dictionary_offset = dictionary + 2;
/* Calculate the binary chop start position */
if ( dictionary_size > 0 )
{
word_index = dictionary_size / 2;
chop = 1;
do
chop *= 2;
while ( word_index /= 2 );
}
/* Tokenise the line */
do
{
/* Skip to next token */
cp = next_token( cp, str_end, &token, &token_length, punctuation );
if ( token_length )
/* If still space in token buffer then store word */
if ( words <= tbuf[0] )
{
/* Get the word offset from the dictionary */
word = find_word( token_length, token, chop );
/* Store the dictionary offset, token length and offset */
if ( word || flag == 0 )
{
tp[0] = ( char ) ( word >> 8 );
tp[1] = ( char ) ( word & 0xff );
}
tp[2] = ( char ) token_length;
tp[3] = ( char ) ( token - cbuf );
/* Step to next token position and count the word */
tp += 4;
words++;
}
else
{
/* Moan if token buffer space exhausted */
output_string( "Too many words typed, discarding: " );
output_line( token );
}
}
while ( token_length );
/* Store word count */
tbuf[1] = ( char ) words;
} /* tokenise_line */
/*
* next_token
*
* Find next token in a string. The token (word) is delimited by a statically
* defined and a game specific set of word separators. The game specific set
* of separators look like real word separators, but the parser wants to know
* about them. An example would be: 'grue, take the axe. go north'. The
* parser wants to know about the comma and the period so that it can correctly
* parse the line. The 'interesting' word separators normally appear at the
* start of the dictionary, and are also put in a separate list in the game
* file.
*
*/
static const char *next_token( const char *s, const char *str_end, const char **token, int *length,
const char *punctuation )
{
int i;
/* Set the token length to zero */
*length = 0;
/* Step through the string looking for separators */
for ( ; s < str_end; s++ )
{
/* Look for game specific word separators first */
for ( i = 0; punctuation[i] && *s != punctuation[i]; i++ )
;
/* If a separator is found then return the information */
if ( punctuation[i] )
{
/* If length has been set then just return the word position */
if ( *length )
return ( s );
else
{
/* End of word, so set length, token pointer and return string */
( *length )++;
*token = s;
return ( ++s );
}
}
/* Look for statically defined separators last */
for ( i = 0; separators[i] && *s != separators[i]; i++ )
;
/* If a separator is found then return the information */
if ( separators[i] )
{
/* If length has been set then just return the word position */
if ( *length )
return ( ++s );
}
else
{
/* If first token character then remember its position */
if ( *length == 0 )
*token = s;
( *length )++;
}
}
return ( s );
} /* next_token */
/*
* find_word
*
* Search the dictionary for a word. Just encode the word and binary chop the
* dictionary looking for it.
*
*/
static zword_t find_word( int len, const char *cp, long chop )
{
ZINT16 word[3];
long word_index, offset, status;
/* Don't look up the word if there are no dictionary entries */
if ( dictionary_size == 0 )
return ( 0 );
/* Encode target word */
encode_text( len, cp, word );
/* Do a binary chop search on the main dictionary, otherwise do
* a linear search */
word_index = chop - 1;
if ( dictionary_size > 0 )
{
/* Binary chop until the word is found */
while ( chop )
{
chop /= 2;
/* Calculate dictionary offset */
if ( word_index > ( dictionary_size - 1 ) )
word_index = dictionary_size - 1;
offset = dictionary_offset + ( word_index * entry_size );
/* If word matches then return dictionary offset */
if ( ( status = word[0] - ( ZINT16 ) get_word( offset + 0 ) ) == 0 &&
( status = word[1] - ( ZINT16 ) get_word( offset + 2 ) ) == 0 &&
( h_type < V4 || ( status = word[2] - ( ZINT16 ) get_word( offset + 4 ) ) == 0 ) )
return ( ( zword_t ) offset );
/* Set next position depending on direction of overshoot */
if ( status > 0 )
{
word_index += chop;
/* Deal with end of dictionary case */
if ( word_index >= ( int ) dictionary_size )
word_index = dictionary_size - 1;
}
else
{
word_index -= chop;
/* Deal with start of dictionary case */
if ( word_index < 0 )
word_index = 0;
}
}
}
else
{
for ( word_index = 0; word_index < -dictionary_size; word_index++ )
{
/* Calculate dictionary offset */
offset = dictionary_offset + ( word_index * entry_size );
/* If word matches then return dictionary offset */
if ( ( status = word[0] - ( ZINT16 ) get_word( offset + 0 ) ) == 0 &&
( status = word[1] - ( ZINT16 ) get_word( offset + 2 ) ) == 0 &&
( h_type < V4 || ( status = word[2] - ( ZINT16 ) get_word( offset + 4 ) ) == 0 ) )
return ( ( zword_t ) offset );
}
}
return ( 0 );
} /* find_word */
/*
* z_tokenise
*
* argv[0] = character buffer address
* argv[1] = token buffer address
* argv[2] = alternate vocabulary table
* argv[3] = ignore unknown words flag
*
*/
void z_tokenise( int argc, zword_t * argv )
{
/* Supply default parameters */
if ( argc < 4 )
argv[3] = 0;
if ( argc < 3 )
argv[2] = h_words_offset;
/* Convert the line to tokens */
tokenise_line( argv[0], argv[1], argv[2], argv[3] );
} /* z_tokenise */

483
interpre.c Normal file
View file

@ -0,0 +1,483 @@
/* $Id: interpre.c,v 1.3 2000/07/05 15:20:34 jholder Exp $
* --------------------------------------------------------------------
* see doc/License.txt for License Information
* --------------------------------------------------------------------
*
* File name: $Id: interpre.c,v 1.3 2000/07/05 15:20:34 jholder Exp $
*
* Description:
*
* Modification history:
* $Log: interpre.c,v $
* Revision 1.3 2000/07/05 15:20:34 jholder
* Updated code to remove warnings.
*
* 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
*
*
* --------------------------------------------------------------------
*/
/*
* interpre.c
*
* Main interpreter loop
*
*/
#include "ztypes.h"
/*#define DEBUG_TERPRE*/
static int halt = FALSE;
/*
* interpret
*
* Interpret Z code
*
*/
int interpret( void )
{
zbyte_t opcode;
zword_t specifier;
zword_t operand[8];
int maxoperands;
int count;
int extend;
int i;
interpreter_status = 1;
/* Loop until HALT instruction executed */
for ( interpreter_state = RUN; interpreter_state == RUN && halt == FALSE; )
{
/* Load opcode and set operand count */
opcode = read_code_byte( );
if ( h_type > V4 && opcode == 0xbe )
{
opcode = read_code_byte( );
extend = TRUE;
}
else
extend = FALSE;
count = 0;
/* Multiple operand instructions */
if ( ( opcode < 0x80 || opcode > 0xc0 ) || extend == TRUE )
{
/* Two operand class, load both operands */
if ( opcode < 0x80 && extend == FALSE )
{
operand[count++] = load_operand( ( opcode & 0x40 ) ? 2 : 1 );
operand[count++] = load_operand( ( opcode & 0x20 ) ? 2 : 1 );
opcode &= 0x1f;
}
else
{
/* Variable operand class, load operand specifier */
opcode &= 0x3f;
if ( opcode == 0x2c || opcode == 0x3a )
{ /* Extended CALL instruction */
specifier = read_code_word( );
maxoperands = 8;
}
else
{
specifier = read_code_byte( );
maxoperands = 4;
}
/* Load operands */
for ( i = ( maxoperands - 1 ) * 2; i >= 0; i -= 2 )
if ( ( ( specifier >> i ) & 0x03 ) != 3 )
operand[count++] = load_operand( ( specifier >> i ) & 0x03 );
else
i = 0;
}
if ( extend == TRUE )
{
#ifdef DEBUG_TERPRE
fprintf( stderr, "PC = 0x%08lx Op%s = 0x%02x %d, %d, %d\n", pc, "(EX)", opcode,
operand[0], operand[1], operand[2] );
#endif
switch ( ( char ) opcode )
{
/* Extended operand instructions */
case 0x00:
z_save( count, operand[0], operand[1], operand[2] );
break;
case 0x01:
z_restore( count, operand[0], operand[1], operand[2] );
break;
case 0x02:
z_log_shift( operand[0], operand[1] );
break;
case 0x03:
z_art_shift( operand[0], operand[1] );
break;
case 0x04:
z_set_font( operand[0] );
break;
case 0x09:
z_save_undo( );
break;
case 0x0a:
z_restore_undo( );
break;
default:
fatal( "interpret(): Illegal extended operand instruction" );
}
}
else
{
#ifdef DEBUG_TERPRE
fprintf( stderr, "PC = 0x%08lx Op%s = 0x%02x %d, %d, %d\n", pc, "(2+)", opcode,
operand[0], operand[1], operand[2] );
#endif
switch ( ( char ) opcode )
{
/* Two or multiple operand instructions */
case 0x01:
z_je( count, operand );
break;
case 0x02:
z_jl( operand[0], operand[1] );
break;
case 0x03:
z_jg( operand[0], operand[1] );
break;
case 0x04:
z_dec_chk( operand[0], operand[1] );
break;
case 0x05:
z_inc_chk( operand[0], operand[1] );
break;
case 0x06:
z_jin( operand[0], operand[1] );
break;
case 0x07:
z_test( operand[0], operand[1] );
break;
case 0x08:
z_or( operand[0], operand[1] );
break;
case 0x09:
z_and( operand[0], operand[1] );
break;
case 0x0a:
z_test_attr( operand[0], operand[1] );
break;
case 0x0b:
z_set_attr( operand[0], operand[1] );
break;
case 0x0c:
z_clear_attr( operand[0], operand[1] );
break;
case 0x0d:
z_store( operand[0], operand[1] );
break;
case 0x0e:
z_insert_obj( operand[0], operand[1] );
break;
case 0x0f:
z_loadw( operand[0], operand[1] );
break;
case 0x10:
z_loadb( operand[0], operand[1] );
break;
case 0x11:
z_get_prop( operand[0], operand[1] );
break;
case 0x12:
z_get_prop_addr( operand[0], operand[1] );
break;
case 0x13:
z_get_next_prop( operand[0], operand[1] );
break;
case 0x14:
z_add( operand[0], operand[1] );
break;
case 0x15:
z_sub( operand[0], operand[1] );
break;
case 0x16:
z_mul( operand[0], operand[1] );
break;
case 0x17:
z_div( operand[0], operand[1] );
break;
case 0x18:
z_mod( operand[0], operand[1] );
break;
case 0x19:
z_call( count, operand, FUNCTION );
break;
case 0x1a:
z_call( count, operand, PROCEDURE );
break;
case 0x1b:
z_set_colour( operand[0], operand[1] );
break;
case 0x1c:
z_throw( operand[0], operand[1] );
break;
/* Multiple operand instructions */
case 0x20:
z_call( count, operand, FUNCTION );
break;
case 0x21:
z_storew( operand[0], operand[1], operand[2] );
break;
case 0x22:
z_storeb( operand[0], operand[1], operand[2] );
break;
case 0x23:
z_put_prop( operand[0], operand[1], operand[2] );
break;
case 0x24:
z_sread_aread( count, operand );
break;
case 0x25:
z_print_char( operand[0] );
break;
case 0x26:
z_print_num( operand[0] );
break;
case 0x27:
z_random( operand[0] );
break;
case 0x28:
z_push( operand[0] );
break;
case 0x29:
z_pull( operand[0] );
break;
case 0x2a:
z_split_window( operand[0] );
break;
case 0x2b:
z_set_window( operand[0] );
break;
case 0x2c:
z_call( count, operand, FUNCTION );
break;
case 0x2d:
z_erase_window( operand[0] );
break;
case 0x2e:
z_erase_line( operand[0] );
break;
case 0x2f:
z_set_cursor( operand[0], operand[1] );
break;
case 0x31:
z_set_text_style( operand[0] );
break;
case 0x32:
z_buffer_mode( operand[0] );
break;
case 0x33:
z_output_stream( operand[0], operand[1] );
break;
case 0x34:
z_input_stream( operand[0] );
break;
case 0x35:
sound( count, operand );
break;
case 0x36:
z_read_char( count, operand );
break;
case 0x37:
z_scan_table( count, operand );
break;
case 0x38:
z_not( operand[0] );
break;
case 0x39:
z_call( count, operand, PROCEDURE );
break;
case 0x3a:
z_call( count, operand, PROCEDURE );
break;
case 0x3b:
z_tokenise( count, operand );
break;
case 0x3c:
z_encode( operand[0], operand[1], operand[2], operand[3] );
break;
case 0x3d:
z_copy_table( operand[0], operand[1], operand[2] );
break;
case 0x3e:
z_print_table( count, operand );
break;
case 0x3f:
z_check_arg_count( operand[0] );
break;
default:
fatal( "interpret(): Illegal 2 or more operand instruction" );
}
}
}
else
{
/* Single operand class, load operand and execute instruction */
if ( opcode < 0xb0 )
{
operand[0] = load_operand( ( opcode >> 4 ) & 0x03 );
#ifdef DEBUG_TERPRE
fprintf( stderr, "PC = 0x%08lx Op%s = 0x%02x %d\n", pc, "(1 )", opcode,
operand[0] );
#endif
switch ( ( char ) opcode & 0x0f )
{
case 0x00:
z_jz( operand[0] );
break;
case 0x01:
z_get_sibling( operand[0] );
break;
case 0x02:
z_get_child( operand[0] );
break;
case 0x03:
z_get_parent( operand[0] );
break;
case 0x04:
z_get_prop_len( operand[0] );
break;
case 0x05:
z_inc( operand[0] );
break;
case 0x06:
z_dec( operand[0] );
break;
case 0x07:
z_print_addr( operand[0] );
break;
case 0x08:
z_call( 1, operand, FUNCTION );
break;
case 0x09:
z_remove_obj( operand[0] );
break;
case 0x0a:
z_print_obj( operand[0] );
break;
case 0x0b:
z_ret( operand[0] );
break;
case 0x0c:
z_jump( operand[0] );
break;
case 0x0d:
z_print_paddr( operand[0] );
break;
case 0x0e:
z_load( operand[0] );
break;
case 0x0f:
if ( h_type > V4 )
z_call( 1, operand, PROCEDURE );
else
z_not( operand[0] );
break;
}
}
else
{
/* Zero operand class, execute instruction */
#ifdef DEBUG_TERPRE
fprintf( stderr, "PC = 0x%08lx Op%s = 0x%02x\n", pc, "(0 )", opcode );
#endif
switch ( ( char ) opcode & 0x0f )
{
case 0x00:
z_ret( TRUE );
break;
case 0x01:
z_ret( FALSE );
break;
case 0x02:
z_print( );
break;
case 0x03:
z_print_ret( );
break;
case 0x04:
/* z_nop */
break;
case 0x05:
z_save( 0, 0, 0, 0 );
break;
case 0x06:
z_restore( 0, 0, 0, 0 );
break;
case 0x07:
z_restart( );
break;
case 0x08:
z_ret( stack[sp++] );
break;
case 0x09:
z_catch( );
break;
case 0x0a:
halt = TRUE; /* z_quit */
break;
case 0x0b:
z_new_line( );
break;
case 0x0c:
z_show_status( );
break;
case 0x0d:
z_verify( );
break;
case 0x0f:
z_piracy( TRUE );
break;
default:
fatal( "interpret(): Illegal zero operand instruction" );
}
}
}
}
return ( interpreter_status );
} /* interpret */

223
joeyio.c Normal file
View file

@ -0,0 +1,223 @@
#include "joey.h"
#include "ansiterm.h"
#include "ztypes.h"
static int head_col;
void clear_line(void) {
termPrint("\33[K");
}
void clear_screen(void) {
termClearScreen();
jlDisplayPresent();
termMoveCursor(1, 1);
}
void clear_status_window(void) {
int i;
int row;
int col;
get_cursor_position(&row, &col);
for (i=status_size; i; i--) {
move_cursor(i, 1);
clear_line();
}
move_cursor(row, col);
}
void clear_text_window(void) {
int i;
int row;
int col;
get_cursor_position(&row, &col);
for (i=status_size + 1; i<=screen_rows; i++) {
move_cursor(i, 1);
clear_line();
}
move_cursor(row, col);
}
void create_status_window(void) {
printf("Create Status Window\n");
}
void delete_status_window(void) {
printf("Delete Status Window\n");
}
void display_char(int c) {
termPrintChar((char)c);
}
void get_cursor_position(int *row, int *col) {
termGetCursor(col, row);
}
void initialize_screen(void) {
// Handled in main
}
int input_character(int timeout) {
char k = 0;
(void)timeout; // In 1/10ths of a second
while (!jlKeyPressed() && !jlUtilMustExit()) {
//***TODO*** Animate cursor here?
}
if (jlKeyPressed()) {
while (jlKeyPressed()) {
// Wait for key to be released
}
k = jlKeyRead();
} else {
interpreter_state = STOP;
}
return (int)k;
}
int input_line(int buflen, char *buffer, int timeout, int *read_size) {
int c = -1;
int col;
int row;
int init_char_pos;
int curr_char_pos;
int loop;
int tail_col;
(void)timeout; // In 1/10ths of a second
get_cursor_position(&row, &col);
head_col = tail_col = col;
init_char_pos = curr_char_pos = *read_size;
while (!jlUtilMustExit()) {
c = input_character(0);
if (c == 8) {
// Backspace
get_cursor_position(&row, &col);
if (col > head_col) {
move_cursor(row, --col);
for (loop = curr_char_pos; loop < *read_size; loop++) {
buffer[loop - 1] = buffer[loop];
display_char(buffer[loop - 1]);
}
display_char(' ');
curr_char_pos--;
tail_col--;
(*read_size)--;
move_cursor(row, col);
}
} else {
// Normal key
if (*read_size == ( buflen - 1 )) {
//***TODO*** Buffer full - Flash border?
} else {
if (c == 13) {
// CR
scroll_line();
return c;
} else {
// Used if the cursor is not at the end of the line
if (col < tail_col) {
// Moves the input line one character to the right
for (loop = *read_size; loop >= curr_char_pos; loop--) {
buffer[loop + 1] = buffer[loop];
}
// Puts the character into the space created by the "for" loop above
buffer[curr_char_pos] = (char)c;
// Increment the end of the line values
(*read_size)++;
tail_col++;
// Move the cursor back to its original position
move_cursor(row, col);
// Redisplays the input line from the point of insertion
for (loop = curr_char_pos; loop < *read_size; loop++) {
display_char(buffer[loop]);
}
// Moves the cursor to the next position
move_cursor(row, ++col);
curr_char_pos++;
} else {
// Used if the cursor is at the end of the line
buffer[curr_char_pos++] = (char)c;
display_char(c);
(*read_size)++;
tail_col++;
}
}
}
}
}
return c;
}
void move_cursor(int row, int col) {
termMoveCursor(col, row);
}
void reset_screen(void) {
// Handled in main
}
void restart_screen(void) {
// I don't think we need this.
}
void restore_cursor_position(void) {
termRestoreCursor();
}
void save_cursor_position(void) {
termSaveCursor();
}
void scroll_line(void) {
//***TODO*** Should really not clobber the status window.
termPrintChar(13);
termPrintChar(10);
}
void select_status_window(void) {
save_cursor_position();
}
void select_text_window(void) {
restore_cursor_position();
}
void set_attribute(int attribute) {
if (attribute == NORMAL) {
termPrint("\33[0m");
}
if (attribute & REVERSE) {
termPrint("\33[7m");
}
}

37
jzip.h Normal file
View file

@ -0,0 +1,37 @@
/* $Id: jzip.h,v 1.5 2000/10/10 14:46:22 jholder Exp $
* --------------------------------------------------------------------
* see doc/License.txt for License Information
* --------------------------------------------------------------------
*
* File name: $Id: jzip.h,v 1.5 2000/10/10 14:46:22 jholder Exp $
*
* Description:
*
* Modification history:
* $Log: jzip.h,v $
* Revision 1.5 2000/10/10 14:46:22 jholder
* Fixed text wrap bug when printing array w/ \r chars in it
*
* Revision 1.4 2000/10/05 17:09:12 jholder
* removed old email address
*
* Revision 1.3 2000/10/04 23:07:57 jholder
* fixed redirect problem with isolatin1 range chars
*
* Revision 1.2 2000/09/18 15:31:44 jholder
* Updated release date for package release
*
* Revision 1.1.1.1 2000/05/10 14:21:34 jholder
*
* imported
*
*
* --------------------------------------------------------------------
*/
#define JZIPVER "Jzip V2.1"
#define JZIPRELDATE "Tue, Oct 10 2000"
#define JZIPAUTHOR "John Holder (j-holder@home.com)"
#define JZIPURL "http://jzip.sourceforge.net/"

122
main.c Normal file
View file

@ -0,0 +1,122 @@
#include <stdarg.h>
#include <string.h>
#include "joey.h"
#include "ansiterm.h"
#include "ztypes.h"
extern int GLOBALVER;
void configure(zbyte_t min_version, zbyte_t max_version);
void process_arguments(char *game);
void configure(zbyte_t min_version, zbyte_t max_version) {
zbyte_t header[PAGE_SIZE];
read_page(0, header);
datap = header;
h_type = get_byte(H_TYPE);
GLOBALVER = h_type;
if (h_type < min_version || h_type > max_version || (get_byte( H_CONFIG ) & CONFIG_BYTE_SWAPPED)) {
fatal( "Wrong game or version" );
}
if (h_type < V4) {
story_scaler = 2;
story_shift = 1;
property_mask = P3_MAX_PROPERTIES - 1;
property_size_mask = 0xe0;
} else if (h_type < V8) {
story_scaler = 4;
story_shift = 2;
property_mask = P4_MAX_PROPERTIES - 1;
property_size_mask = 0x3f;
} else {
story_scaler = 8;
story_shift = 3;
property_mask = P4_MAX_PROPERTIES - 1;
property_size_mask = 0x3f;
}
h_config = get_byte(H_CONFIG);
h_version = get_word(H_VERSION);
h_data_size = get_word(H_DATA_SIZE);
h_start_pc = get_word(H_START_PC);
h_words_offset = get_word(H_WORDS_OFFSET);
h_objects_offset = get_word(H_OBJECTS_OFFSET);
h_globals_offset = get_word(H_GLOBALS_OFFSET);
h_restart_size = get_word(H_RESTART_SIZE);
h_flags = get_word(H_FLAGS);
h_synonyms_offset = get_word(H_SYNONYMS_OFFSET);
h_file_size = get_word(H_FILE_SIZE);
if (h_file_size == 0) {
h_file_size = (zword_t)get_story_size();
}
h_checksum = get_word(H_CHECKSUM);
h_alternate_alphabet_offset = get_word(H_ALTERNATE_ALPHABET_OFFSET);
if (h_type >= V5) {
h_unicode_table = get_word(H_UNICODE_TABLE);
}
datap = NULL;
}
void process_arguments(char *game) {
int size;
monochrome = 0;
hist_buf_size = 1024;
bigscreen = 0;
screen_rows = 25;
screen_cols = 40;
right_margin = 0;
top_margin = 0;
size = 1024;
hist_buf_size = (hist_buf_size > size) ? hist_buf_size : size;
if (hist_buf_size > 16384) {
hist_buf_size = 16384;
}
fTandy = 0;
//default_fg = atoi( optarg );
//default_bg = atoi( optarg );
monochrome = 1;
open_story(game);
}
int main(void) {
jlStaT *font = NULL;
jlUtilStartup("IF Engine");
jlStaLoad(font, "8x8thin.sta");
termStart(font, 0, 0, 40, 25);
process_arguments("gamedata.z5");
configure(V1, V8);
initialize_screen();
load_cache();
z_restart();
(void)interpret();
unload_cache();
close_story();
close_script();
reset_screen();
termStop();
jlDisplayPresent();
jlKeyWaitForAny();
jlStaFree(font);
jlUtilShutdown();
}

305
math.c Normal file
View file

@ -0,0 +1,305 @@
/* $Id: math.c,v 1.3 2000/07/05 15:20:34 jholder Exp $
* --------------------------------------------------------------------
* see doc/License.txt for License Information
* --------------------------------------------------------------------
*
* File name: $Id: math.c,v 1.3 2000/07/05 15:20:34 jholder Exp $
*
* Description:
*
* Modification history:
* $Log: math.c,v $
* Revision 1.3 2000/07/05 15:20:34 jholder
* Updated code to remove warnings.
*
* 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
*
*
* --------------------------------------------------------------------
*/
/*
* math.c
*
* Arithmetic, compare and logical instructions
*
*/
#include "ztypes.h"
/*
* z_add
*
* Add two operands
*
*/
void z_add( zword_t a, zword_t b )
{
store_operand( (zword_t)(a + b) );
}
/*
* z_sub
*
* Subtract two operands
*
*/
void z_sub( zword_t a, zword_t b )
{
store_operand( (zword_t)(a - b) );
}
/*
* mul
*
* Multiply two operands
*
*/
void z_mul( zword_t a, zword_t b )
{
store_operand( (zword_t)(( ZINT16 ) a * ( ZINT16 ) b) );
}
/*
* z_div
*
* Divide two operands
*
*/
void z_div( zword_t a, zword_t b )
{
/* The magic rule is: round towards zero. */
ZINT16 sa = ( ZINT16 ) a;
ZINT16 sb = ( ZINT16 ) b;
ZINT16 res;
if ( sb < 0 )
{
sa = -sa;
sb = -sb;
}
if ( sb == 0 )
{
#ifdef STRICTZ
report_strictz_error( STRZERR_DIV_ZERO,
"@div attempted with a divisor of zero. Result set to 32767 (0x7fff)." );
#endif
res = 32767;
}
else if ( sa >= 0 )
{
res = sa / sb;
}
else
{
res = -( ( -sa ) / ( sb ) );
}
store_operand( res );
}
/*
* z_mod
*
* Modulus divide two operands
*
*/
void z_mod( zword_t a, zword_t b )
{
/* The magic rule is: be consistent with divide, because
* (a/b)*a + (a%b) == a. So (a%b) has the same sign as a. */
ZINT16 sa = ( ZINT16 ) a;
ZINT16 sb = ( ZINT16 ) b;
ZINT16 res;
if ( sb < 0 )
{
sb = -sb;
}
if ( sb == 0 )
{
#ifdef STRICTZ
report_strictz_error( STRZERR_DIV_ZERO,
"@mod attempted with a divisor of zero. Result set to 0." );
#endif
res = 0;
}
if ( sa >= 0 )
{
res = sa % sb;
}
else
{
res = -( ( -sa ) % ( sb ) );
}
store_operand( res );
}
/*
* z_log_shift
*
* Shift +/- n bits
*
*/
void z_log_shift( zword_t a, zword_t b )
{
if ( ( ZINT16 ) b > 0 )
store_operand( (zword_t)( a << ( ZINT16 ) b ) );
else
store_operand( (zword_t)( a >> -( ( ZINT16 ) b ) ) );
}
/*
* z_art_shift
*
* Aritmetic shift +/- n bits
*
*/
void z_art_shift( zword_t a, zword_t b )
{
if ( ( ZINT16 ) b > 0 )
store_operand( ( zword_t ) ( ( ZINT16 ) a << ( ZINT16 ) b ) );
else
store_operand( ( zword_t ) ( ( ZINT16 ) a >> -( ( ZINT16 ) b ) ) );
}
/*
* z_or
*
* Logical OR
*
*/
void z_or( zword_t a, zword_t b )
{
store_operand( (zword_t)(a | b) );
}
/*
* z_not
*
* Logical NOT
*
*/
void z_not( zword_t a )
{
store_operand( (zword_t)(~a) );
}
/*
* z_and
*
* Logical AND
*
*/
void z_and( zword_t a, zword_t b )
{
store_operand( (zword_t)(a & b) );
}
/*
* z_random
*
* Return random number between 1 and operand
*
*/
void z_random( zword_t a )
{
if ( a == 0 )
store_operand( 0 );
else if ( a & 0x8000 )
{ /* (a < 0) - used to set seed with #RANDOM */
SRANDOM_FUNC( ( unsigned int ) abs( a ) );
store_operand( 0 );
}
else /* (a > 0) */
store_operand( (zword_t)(( RANDOM_FUNC( ) % a ) + 1) );
}
/*
* z_test
*
* Jump if operand 2 bit mask not set in operand 1
*
*/
void z_test( zword_t a, zword_t b )
{
conditional_jump( ( ( ~a ) & b ) == 0 );
}
/*
* z_jz
*
* Compare operand against zero
*
*/
void z_jz( zword_t a )
{
conditional_jump( a == 0 );
}
/*
* z_je
*
* Jump if operand 1 is equal to any other operand
*
*/
void z_je( int count, zword_t * operand )
{
int i;
for ( i = 1; i < count; i++ )
if ( operand[0] == operand[i] )
{
conditional_jump( TRUE );
return;
}
conditional_jump( FALSE );
}
/*
* z_jl
*
* Jump if operand 1 is less than operand 2
*
*/
void z_jl( zword_t a, zword_t b )
{
conditional_jump( ( ZINT16 ) a < ( ZINT16 ) b );
}
/*
* z_jg
*
* Jump if operand 1 is greater than operand 2
*
*/
void z_jg( zword_t a, zword_t b )
{
conditional_jump( ( ZINT16 ) a > ( ZINT16 ) b );
}

419
memory.c Normal file
View file

@ -0,0 +1,419 @@
/* $Id: memory.c,v 1.2 2000/05/25 22:28:56 jholder Exp $
* --------------------------------------------------------------------
* see doc/License.txt for License Information
* --------------------------------------------------------------------
*
* File name: $Id: memory.c,v 1.2 2000/05/25 22:28:56 jholder Exp $
*
* Description:
*
* Modification history:
* $Log: memory.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
*
*
* --------------------------------------------------------------------
*/
/*
* memory.c
*
* Code and data caching routines
*
*/
#include "ztypes.h"
/* A cache entry */
typedef struct cache_entry
{
struct cache_entry *flink;
int page_number;
zbyte_t data[PAGE_SIZE];
}
cache_entry_t;
/* Cache chain anchor */
static cache_entry_t *cache = NULL;
/* Pseudo translation buffer, one entry each for code and data pages */
static unsigned int current_code_page = 0;
static cache_entry_t *current_code_cachep = NULL;
static unsigned int current_data_page = 0;
static cache_entry_t *current_data_cachep = NULL;
static unsigned int calc_data_pages( void );
static cache_entry_t *update_cache( int );
/*
* load_cache
*
* Initialise the cache and any other dynamic memory objects. The memory
* required can be split into two areas. Firstly, three buffers are required for
* input, output and status line. Secondly, two data areas are required for
* writeable data and read only data. The writeable data is the first chunk of
* the file and is put into non-paged cache. The read only data is the remainder
* of the file which can be paged into the cache as required. Writeable data has
* to be memory resident because it cannot be written out to a backing store.
*
*/
void load_cache( void )
{
unsigned long file_size;
unsigned int i, file_pages, data_pages;
cache_entry_t *cachep;
/* Allocate output and status line buffers */
line = ( char * ) malloc( screen_cols + 1 );
if ( line == NULL )
{
fatal( "load_cache(): Insufficient memory to play game" );
}
status_line = ( char * ) malloc( screen_cols + 1 );
if ( status_line == NULL )
{
fatal( "load_cache(): Insufficient memory to play game" );
}
/* Must have at least one cache page for memory calculation */
cachep = ( cache_entry_t * ) malloc( sizeof ( cache_entry_t ) );
if ( cachep == NULL )
{
fatal( "load_cache(): Insufficient memory to play game" );
}
cachep->flink = cache;
cachep->page_number = 0;
cache = cachep;
/* Calculate dynamic cache pages required */
if ( h_config & CONFIG_MAX_DATA )
{
data_pages = calc_data_pages( );
}
else
{
data_pages = ( h_data_size + PAGE_MASK ) >> PAGE_SHIFT;
}
data_size = data_pages * PAGE_SIZE;
file_size = ( unsigned long ) h_file_size *story_scaler;
file_pages = ( unsigned int ) ( ( file_size + PAGE_MASK ) >> PAGE_SHIFT );
/* Allocate static data area and initialise it */
datap = ( zbyte_t * ) malloc( data_size );
if ( datap == NULL )
{
fatal( "load_cache(): Insufficient memory to play game" );
}
for ( i = 0; i < data_pages; i++ )
{
read_page( i, &datap[i * PAGE_SIZE] );
}
/* Allocate memory for undo */
undo_datap = ( zbyte_t * ) malloc( data_size );
/* Allocate cache pages and initialise them */
for ( i = data_pages; cachep != NULL && i < file_pages; i++ )
{
cachep = ( cache_entry_t * ) malloc( sizeof ( cache_entry_t ) );
if ( cachep != NULL )
{
cachep->flink = cache;
cachep->page_number = i;
read_page( cachep->page_number, cachep->data );
cache = cachep;
}
}
} /* load_cache */
/*
* unload_cache
*
* Deallocate cache and other memory objects.
*
*/
void unload_cache( void )
{
cache_entry_t *cachep, *nextp;
/* Make sure all output has been flushed */
z_new_line( );
/* Free output buffer, status line and data memory */
free( line );
free( status_line );
free( datap );
free( undo_datap );
/* Free cache memory */
for ( cachep = cache; cachep->flink != NULL; cachep = nextp )
{
nextp = cachep->flink;
free( cachep );
}
} /* unload_cache */
/*
* read_code_word
*
* Read a word from the instruction stream.
*
*/
zword_t read_code_word( void )
{
zword_t w;
w = ( zword_t ) read_code_byte( ) << 8;
w |= ( zword_t ) read_code_byte( );
return ( w );
} /* read_code_word */
/*
* read_code_byte
*
* Read a byte from the instruction stream.
*
*/
zbyte_t read_code_byte( void )
{
unsigned int page_number, page_offset;
/* Calculate page and offset values */
page_number = ( unsigned int ) ( pc >> PAGE_SHIFT );
page_offset = ( unsigned int ) pc & PAGE_MASK;
/* Load page into translation buffer */
if ( page_number != current_code_page )
{
current_code_cachep = update_cache( page_number );
current_code_page = page_number;
}
/* Update the PC */
pc++;
/* Return byte from page offset */
if ( !current_code_cachep )
{
fatal
( "read_code_byte(): read from non-existant page!\n\t(Your dynamic memory usage _may_ be over 64k in size!)" );
}
return ( current_code_cachep->data[page_offset] );
} /* read_code_byte */
/*
* read_data_word
*
* Read a word from the data area.
*
*/
zword_t read_data_word( unsigned long *addr )
{
zword_t w;
w = ( zword_t ) read_data_byte( addr ) << 8;
w |= ( zword_t ) read_data_byte( addr );
return ( w );
} /* read_data_word */
/*
* read_data_byte
*
* Read a byte from the data area.
*
*/
zbyte_t read_data_byte( unsigned long *addr )
{
unsigned int page_number, page_offset;
zbyte_t value;
/* Check if byte is in non-paged cache */
if ( *addr < ( unsigned long ) data_size )
{
value = datap[*addr];
}
else
{
/* Calculate page and offset values */
page_number = ( int ) ( *addr >> PAGE_SHIFT );
page_offset = ( int ) *addr & PAGE_MASK;
/* Load page into translation buffer */
if ( page_number != current_data_page )
{
current_data_cachep = update_cache( page_number );
current_data_page = page_number;
}
/* Fetch byte from page offset */
if ( current_data_cachep )
{
value = current_data_cachep->data[page_offset];
}
else
{
fatal( "read_data_byte(): Fetching data from invalid page!" );
}
}
/* Update the address */
( *addr )++;
return ( value );
} /* read_data_byte */
/*
* calc_data_pages
*
* Compute the best size for the data area cache. Some games have the data size
* header parameter set too low. This causes a write outside of data area on
* some games. To alleviate this problem the data area size is set to the
* maximum of the restart size, the data size and the end of the dictionary. An
* attempt is made to put the dictionary in the data area to stop paging during
* a dictionary lookup. Some games have the dictionary end very close to the
* 64K limit which may cause problems for machines that allocate memory in
* 64K chunks.
*
*/
static unsigned int calc_data_pages( void )
{
unsigned long offset, data_end, dictionary_end;
int separator_count, word_size, word_count;
unsigned int data_pages;
/* Calculate end of data area, use restart size if data size is too low */
if ( h_data_size > h_restart_size )
{
data_end = h_data_size;
}
else
{
data_end = h_restart_size;
}
/* Calculate end of dictionary table */
offset = h_words_offset;
separator_count = read_data_byte( &offset );
offset += separator_count;
word_size = read_data_byte( &offset );
word_count = read_data_word( &offset );
dictionary_end = offset + ( word_size * word_count );
/* If data end is too low then use end of dictionary instead */
if ( dictionary_end > data_end )
{
data_pages = ( unsigned int ) ( ( dictionary_end + PAGE_MASK ) >> PAGE_SHIFT );
}
else
{
data_pages = ( unsigned int ) ( ( data_end + PAGE_MASK ) >> PAGE_SHIFT );
}
return ( data_pages );
} /* calc_data_pages */
/*
* update_cache
*
* Called on a code or data page cache miss to find the page in the cache or
* read the page in from disk. The chain is kept as a simple LRU chain. If a
* page cannot be found then the page on the end of the chain is reused. If the
* page is found, or reused, then it is moved to the front of the chain.
*
*/
static cache_entry_t *update_cache( int page_number )
{
cache_entry_t *cachep, *lastp;
/* Search the cache chain for the page */
for ( lastp = cache, cachep = cache;
cachep->flink != NULL && cachep->page_number && cachep->page_number != page_number;
lastp = cachep, cachep = cachep->flink )
;
/* If no page in chain then read it from disk */
if ( cachep->page_number != page_number )
{
/* Reusing last cache page, so invalidate cache if page was in use */
if ( cachep->flink == NULL && cachep->page_number )
{
if ( current_code_page == ( unsigned int ) cachep->page_number )
{
current_code_page = 0;
}
if ( current_data_page == ( unsigned int ) cachep->page_number )
{
current_data_page = 0;
}
}
/* Load the new page number and the page contents from disk */
cachep->page_number = page_number;
read_page( page_number, cachep->data );
}
/* If page is not at front of cache chain then move it there */
if ( lastp != cache )
{
lastp->flink = cachep->flink;
cachep->flink = cache;
cache = cachep;
}
return ( cachep );
} /* update_cache */

454
object.c Normal file
View file

@ -0,0 +1,454 @@
/* $Id: object.c,v 1.2 2000/05/25 22:28:56 jholder Exp $
* --------------------------------------------------------------------
* see doc/License.txt for License Information
* --------------------------------------------------------------------
*
* File name: $Id: object.c,v 1.2 2000/05/25 22:28:56 jholder Exp $
*
* Description:
*
* Modification history:
* $Log: object.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
*
*
* --------------------------------------------------------------------
*/
/*
* object.c
*
* Object manipulation routines.
*
*/
#include "ztypes.h"
#define PARENT 0
#define NEXT 1
#define CHILD 2
static zword_t read_object( zword_t objp, int field );
static void write_object( zword_t objp, int field, zword_t value );
/*
* get_object_address
*
* Calculate the address of an object in the data area.
*
*/
zword_t get_object_address( zword_t obj )
{
int offset;
/* Address calculation is object table base + size of default properties area +
* object number-1 * object size */
if ( h_type < V4 )
offset = h_objects_offset + ( ( P3_MAX_PROPERTIES - 1 ) * 2 ) + ( ( obj - 1 ) * O3_SIZE );
else
offset = h_objects_offset + ( ( P4_MAX_PROPERTIES - 1 ) * 2 ) + ( ( obj - 1 ) * O4_SIZE );
return ( ( zword_t ) offset );
} /* get_object_address */
/*
* z_insert_obj
*
* Insert object 1 as the child of object 2 after first removing it from its
* previous parent. The object is inserted at the front of the child object
* chain.
*
*/
void z_insert_obj( zword_t obj1, zword_t obj2 )
{
zword_t obj1p, obj2p, child2;
#ifdef STRICTZ
if ( obj1 == 0 )
{
report_strictz_error( STRZERR_MOVE_OBJECT, "@insert_obj called moving object 0" );
}
if ( obj2 == 0 )
{
report_strictz_error( STRZERR_MOVE_OBJECT_2, "@insert_obj called moving into object 0" );
}
#endif
/* Get addresses of both objects */
obj1p = get_object_address( obj1 );
obj2p = get_object_address( obj2 );
/* Remove object 1 from current parent */
z_remove_obj( obj1 );
/* Make object 2 object 1's parent */
write_object( obj1p, PARENT, obj2 );
/* Get current first child of object 2 */
child2 = read_object( obj2p, CHILD );
/* Make object 1 first child of object 2 */
write_object( obj2p, CHILD, obj1 );
/* If object 2 had children then link them into the next child field of object 1 */
if ( child2 )
write_object( obj1p, NEXT, child2 );
} /* z_insert_obj */
/*
* z_remove_obj
*
* Remove an object by unlinking from the its parent object and from its
* siblings.
*
*/
void z_remove_obj( zword_t obj )
{
zword_t objp, parentp, childp, parent, child;
#ifdef STRICTZ
if ( obj == 0 )
{
report_strictz_error( STRZERR_REMOVE_OBJECT, "@remove_obj called with object 0" );
}
#endif
/* Get address of object to be removed */
objp = get_object_address( obj );
/* Get parent of object, and return if no parent */
if ( ( parent = read_object( objp, PARENT ) ) == 0 )
return;
/* Get address of parent object */
parentp = get_object_address( parent );
/* Find first child of parent */
child = read_object( parentp, CHILD );
/* If object is first child then just make the parent child pointer
* equal to the next child */
if ( child == obj )
write_object( parentp, CHILD, read_object( objp, NEXT ) );
else
{
/* Walk down the child chain looking for this object */
do
{
childp = get_object_address( child );
child = read_object( childp, NEXT );
}
while ( child != obj );
/* Set the next pointer thre previous child to the next pointer
* of the current object child pointer */
write_object( childp, NEXT, read_object( objp, NEXT ) );
}
/* Set the parent and next child pointers to NULL */
write_object( objp, PARENT, 0 );
write_object( objp, NEXT, 0 );
} /* z_remove_obj */
/*
* z_get_parent
*
* Load the parent object pointer of an object
*
*/
void z_get_parent( zword_t obj )
{
#ifdef STRICTZ
if ( obj == 0 )
{
report_strictz_error( STRZERR_GET_PARENT, "@get_parent called with object 0" );
store_operand( 0 );
return;
}
#endif
store_operand( read_object( get_object_address( obj ), PARENT ) );
} /* z_get_parent */
/*
* z_get_child
*
* Load the child object pointer of an object and jump if the child pointer is
* not NULL.
*
*/
void z_get_child( zword_t obj )
{
zword_t child;
#ifdef STRICTZ
if ( obj == 0 )
{
report_strictz_error( STRZERR_GET_CHILD, "@get_child called with object 0" );
store_operand( 0 );
conditional_jump( FALSE );
return;
}
#endif
child = read_object( get_object_address( obj ), CHILD );
store_operand( child );
conditional_jump( child != 0 );
} /* z_get_child */
/*
* z_get_sibling
*
* Load the next child object pointer of an object and jump if the next child
* pointer is not NULL.
*
*/
void z_get_sibling( zword_t obj )
{
zword_t next;
#ifdef STRICTZ
if ( obj == 0 )
{
report_strictz_error( STRZERR_GET_SIBLING, "@get_sibling called with object 0" );
store_operand( 0 );
conditional_jump( FALSE );
return;
}
#endif
next = read_object( get_object_address( obj ), NEXT );
store_operand( next );
conditional_jump( next != 0 );
} /* z_get_sibling */
/*
* z_jin
*
* Jump if object 2 is the parent of object 1
*
*/
void z_jin( zword_t obj1, zword_t obj2 )
{
#ifdef STRICTZ
if ( obj1 == 0 )
{
report_strictz_error( STRZERR_JIN, "@jin called with object 0" );
conditional_jump( 0 == obj2 );
return;
}
#endif
conditional_jump( read_object( get_object_address( obj1 ), PARENT ) == obj2 );
} /* z_jin */
/*
* z_test_attr
*
* Test if an attribute bit is set.
*
*/
void z_test_attr( zword_t obj, zword_t bit )
{
zword_t objp;
zbyte_t value;
assert( O3_ATTRIBUTES == O4_ATTRIBUTES );
#ifdef STRICTZ
if ( obj == 0 )
{
report_strictz_error( STRZERR_TEST_ATTR, "@test_attr called with object 0" );
conditional_jump( FALSE );
return;
}
#endif
/* Get attribute address */
objp = get_object_address( obj ) + ( bit >> 3 );
/* Load attribute byte */
value = get_byte( objp );
/* Test attribute */
conditional_jump( ( value >> ( 7 - ( bit & 7 ) ) ) & 1 );
} /* z_test_attr */
/*
* z_set_attr
*
* Set an attribute bit.
*
*/
void z_set_attr( zword_t obj, zword_t bit )
{
zword_t objp;
zbyte_t value;
assert( O3_ATTRIBUTES == O4_ATTRIBUTES );
#ifdef STRICTZ
if ( obj == 0 )
{
report_strictz_error( STRZERR_SET_ATTR, "@set_attr called with object 0" );
return;
}
#endif
/* Get attribute address */
objp = get_object_address( obj ) + ( bit >> 3 );
/* Load attribute byte */
value = get_byte( objp );
/* Set attribute bit */
value |= ( zbyte_t ) ( 1 << ( 7 - ( bit & 7 ) ) );
/* Store attribute byte */
set_byte( objp, value );
} /* z_set_attr */
/*
* z_clear_attr
*
* Clear an attribute bit
*
*/
void z_clear_attr( zword_t obj, zword_t bit )
{
zword_t objp;
zbyte_t value;
assert( O3_ATTRIBUTES == O4_ATTRIBUTES );
#ifdef STRICTZ
if ( obj == 0 )
{
report_strictz_error( STRZERR_CLEAR_ATTR, "@clear_attr called with object 0" );
return;
}
#endif
/* Get attribute address */
objp = get_object_address( obj ) + ( bit >> 3 );
/* Load attribute byte */
value = get_byte( objp );
/* Clear attribute bit */
value &= ( zbyte_t ) ~ ( 1 << ( 7 - ( bit & 7 ) ) );
/* Store attribute byte */
set_byte( objp, value );
} /* z_clear_attr */
static zword_t read_object( zword_t objp, int field )
{
zword_t value;
if ( h_type < V4 )
{
if ( field == PARENT )
value = ( zword_t ) get_byte( PARENT3( objp ) );
else if ( field == NEXT )
value = ( zword_t ) get_byte( NEXT3( objp ) );
else
value = ( zword_t ) get_byte( CHILD3( objp ) );
}
else
{
if ( field == PARENT )
value = get_word( PARENT4( objp ) );
else if ( field == NEXT )
value = get_word( NEXT4( objp ) );
else
value = get_word( CHILD4( objp ) );
}
return ( value );
} /* read_object */
static void write_object( zword_t objp, int field, zword_t value )
{
if ( h_type < V4 )
{
if ( field == PARENT )
set_byte( PARENT3( objp ), value );
else if ( field == NEXT )
set_byte( NEXT3( objp ), value );
else
set_byte( CHILD3( objp ), value );
}
else
{
if ( field == PARENT )
set_word( PARENT4( objp ), value );
else if ( field == NEXT )
set_word( NEXT4( objp ), value );
else
set_word( CHILD4( objp ), value );
}
} /* write_object */

234
operand.c Normal file
View file

@ -0,0 +1,234 @@
/* $Id: operand.c,v 1.2 2000/05/25 22:28:56 jholder Exp $
* --------------------------------------------------------------------
* see doc/License.txt for License Information
* --------------------------------------------------------------------
*
* File name: $Id: operand.c,v 1.2 2000/05/25 22:28:56 jholder Exp $
*
* Description:
*
* Modification history:
* $Log: operand.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
*
*
* --------------------------------------------------------------------
*/
/*
* operand.c
*
* Operand manipulation routines
*
*/
#include "ztypes.h"
/*
* load_operand
*
* Load an operand, either: a variable, popped from the stack or a literal.
*
*/
zword_t load_operand( int type )
{
zword_t operand;
if ( type )
{
/* Type 1: byte literal, or type 2: operand specifier */
operand = ( zword_t ) read_code_byte( );
if ( type == 2 )
{
/* If operand specifier non-zero then it's a variable, otherwise
* it's the top of the stack */
if ( operand )
{
return load_variable( operand );
}
else
{
return stack[sp++];
}
}
}
else
{
/* Type 0: word literal */
return read_code_word( );
}
return ( operand );
} /* load_operand */
/*
* store_operand
*
* Store an operand, either as a variable pushed on the stack.
*
*/
void store_operand( zword_t operand )
{
zbyte_t specifier;
/* Read operand specifier byte */
specifier = read_code_byte( );
/* If operand specifier non-zero then it's a variable, otherwise it's the
* pushed on the stack */
if ( specifier )
z_store( specifier, operand );
else
stack[--sp] = operand;
} /* store_operand */
/*
* load_variable
*
* Load a variable, either: a stack local variable, a global variable or
* the top of the stack.
*
*/
zword_t load_variable( int number )
{
if ( number )
{
if ( number < 16 )
{
/* number in range 1 - 15, it's a stack local variable */
return stack[fp - ( number - 1 )];
}
else
{
/* number > 15, it's a global variable */
return get_word( h_globals_offset + ( ( number - 16 ) * 2 ) );
}
}
else
{
/* number = 0, get from top of stack */
return stack[sp];
}
} /* load_variable */
/*
* z_store
*
* Store a variable, either: a stack local variable, a global variable or the
* top of the stack.
*
*/
void z_store( int number, zword_t variable )
{
if ( number )
{
if ( number < 16 )
/* number in range 1 - 15, it's a stack local variable */
stack[fp - ( number - 1 )] = variable;
else
/* number > 15, it's a global variable */
set_word( h_globals_offset + ( ( number - 16 ) * 2 ), variable );
}
else
/* number = 0, get from top of stack */
stack[sp] = variable;
} /* z_store */
/*
* z_piracy
*
* Supposed to jump if the game thinks that it is pirated.
*/
void z_piracy( int flag )
{
conditional_jump( flag );
}
/*
* conditional_jump
*
* Take a jump after an instruction based on the flag, either true or false. The
* jump can be modified by the change logic flag. Normally jumps are taken
* when the flag is true. When the change logic flag is set then the jump is
* taken when flag is false. A PC relative jump can also be taken. This jump can
* either be a positive or negative byte or word range jump. An additional
* feature is the return option. If the jump offset is zero or one then that
* literal value is passed to the return instruction, instead of a jump being
* taken. Complicated or what!
*
*/
void conditional_jump( int flag )
{
zbyte_t specifier;
zword_t offset;
/* Read the specifier byte */
specifier = read_code_byte( );
/* If the reverse logic flag is set then reverse the flag */
if ( specifier & 0x80 )
flag = ( flag ) ? 0 : 1;
/* Jump offset is in bottom 6 bits */
offset = ( zword_t ) specifier & 0x3f;
/* If the byte range jump flag is not set then load another offset byte */
if ( ( specifier & 0x40 ) == 0 )
{
/* Add extra offset byte to existing shifted offset */
offset = ( offset << 8 ) + read_code_byte( );
/* If top bit of offset is set then propogate the sign bit */
if ( offset & 0x2000 )
offset |= 0xc000;
}
/* If the flag is false then do the jump */
if ( flag == 0 )
{
/* If offset equals 0 or 1 return that value instead */
if ( offset == 0 || offset == 1 )
{
z_ret( offset );
}
else
{ /* Add offset to PC */
pc = ( unsigned long ) ( pc + ( ZINT16 ) offset - 2 );
}
}
} /* conditional_jump */

525
osdepend.c Normal file
View file

@ -0,0 +1,525 @@
/* $Id: osdepend.c,v 1.2 2000/05/25 22:28:56 jholder Exp $
* --------------------------------------------------------------------
* see doc/License.txt for License Information
* --------------------------------------------------------------------
*
* File name: $Id: osdepend.c,v 1.2 2000/05/25 22:28:56 jholder Exp $
*
* Description:
*
* Modification history:
* $Log: osdepend.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
*
*
* --------------------------------------------------------------------
*/
/*
* osdepend.c
*
* All non screen specific operating system dependent routines.
*
* Olaf Barthel 28-Jul-1992
* Modified John Holder(j-holder@home.com) 25-July-1995
* Support for standalone storyfiles by Magnu Olsson (mol@df.lth.se) Nov.1995
*
*/
#include "ztypes.h"
//#include "jzexe.h"
/* File names will be O/S dependent */
#if defined(AMIGA)
#define SAVE_NAME "Story.Save" /* Default save name */
#define SCRIPT_NAME "PRT:" /* Default script name */
#define RECORD_NAME "Story.Record" /* Default record name */
#define AUXILARY_NAME "Story.Aux" /* Default auxilary name */
#else /* defined(AMIGA) */
#define SAVE_NAME "story.sav" /* Default save name */
#define SCRIPT_NAME "story.scr" /* Default script name */
#define RECORD_NAME "story.rec" /* Default record name */
#define AUXILARY_NAME "story.aux" /* Default auxilary name */
#endif /* defined(AMIGA) */
#ifdef STRICTZ
/* Define stuff for stricter Z-code error checking, for the generic
* Unix/DOS/etc terminal-window interface. Feel free to change the way
* player prefs are specified, or replace report_zstrict_error()
* completely if you want to change the way errors are reported.
*/
/* There are four error reporting modes: never report errors;
* report only the first time a given error type occurs; report
* every time an error occurs; or treat all errors as fatal
* errors, killing the interpreter. I strongly recommend
* "report once" as the default. But you can compile in a
* different default by changing the definition of
* STRICTZ_DEFAULT_REPORT_MODE. In any case, the player can
* specify a report mode on the command line by typing "-s 0"
* through "-s 3".
*/
#define STRICTZ_REPORT_NEVER (0)
#define STRICTZ_REPORT_ONCE (1)
#define STRICTZ_REPORT_ALWAYS (2)
#define STRICTZ_REPORT_FATAL (3)
#define STRICTZ_DEFAULT_REPORT_MODE STRICTZ_REPORT_NEVER
static int strictz_report_mode;
static int strictz_error_count[STRICTZ_NUM_ERRORS];
#endif /* STRICTZ */
#if !defined(AMIGA)
/* getopt linkages */
extern int optind;
extern const char *optarg;
extern ZINT16 default_fg, default_bg;
#endif /* !defined(AMIGA) */
#if defined OS2 || defined __MSDOS__
int iPalette;
#endif
#if !defined(AMIGA)
/*
* file_cleanup
*
* Perform actions when a file is successfully closed. Flag can be one of:
* GAME_SAVE, GAME_RESTORE, GAME_SCRIPT.
*
*/
void file_cleanup( const char *file_name, int flag )
{
UNUSEDVAR( file_name );
UNUSEDVAR( flag );
} /* file_cleanup */
#endif /* !defined(AMIGA) */
#if !defined(AMIGA)
/*
* sound
*
* Play a sound file or a note.
*
* argc = 1: argv[0] = note# (range 1 - 3)
*
* Play note.
*
* argc = 2: argv[0] = 0
* argv[1] = 3
*
* Stop playing current sound.
*
* argc = 2: argv[0] = 0
* argv[1] = 4
*
* Free allocated resources.
*
* argc = 3: argv[0] = ID# of sound file to replay.
* argv[1] = 2
* argv[2] = Volume to replay sound with, this value
* can range between 1 and 8.
*
* argc = 4: argv[0] = ID# of sound file to replay.
* argv[1] = 2
* argv[2] = Control information
* argv[3] = Volume information
*
* Volume information:
*
* 0x34FB -> Fade sound in
* 0x3507 -> Fade sound out
* other -> Replay sound at maximum volume
*
* Control information:
*
* This word is divided into two bytes,
* the upper byte determines the number of
* cycles to play the sound (e.g. how many
* times a clock chimes or a dog barks).
* The meaning of the lower byte is yet to
* be discovered :)
*
*/
void sound( int argc, zword_t * argv )
{
/* Supply default parameters */
if ( argc < 4 )
argv[3] = 0;
if ( argc < 3 )
argv[2] = 0xff;
if ( argc < 2 )
argv[1] = 2;
/* Generic bell sounder */
if ( argc == 1 || argv[1] == 2 )
display_char( '\007' );
} /* sound */
#endif /* !defined(AMIGA) */
/*
* get_file_name
*
* Return the name of a file. Flag can be one of:
* GAME_SAVE - Save file (write only)
* GAME_RESTORE - Save file (read only)
* GAME_SCRIPT - Script file (write only)
* GAME_RECORD - Keystroke record file (write only)
* GAME_PLABACK - Keystroke record file (read only)
* GAME_SAVE_AUX - Auxilary (preferred settings) file (write only)
* GAME_LOAD_AUX - Auxilary (preferred settings) file (read only)
*/
int get_file_name( char *file_name, char *default_name, int flag )
{
char buffer[127 + 2]; /* 127 is the biggest positive char */
int status = 0;
/* If no default file name then supply the standard name */
if ( default_name[0] == '\0' )
{
if ( flag == GAME_SCRIPT )
strcpy( default_name, SCRIPT_NAME );
else if ( flag == GAME_RECORD || flag == GAME_PLAYBACK )
strcpy( default_name, RECORD_NAME );
else if ( flag == GAME_SAVE_AUX || GAME_LOAD_AUX )
strcpy( default_name, AUXILARY_NAME );
else /* (flag == GAME_SAVE || flag == GAME_RESTORE) */
strcpy( default_name, SAVE_NAME );
}
/* Prompt for the file name */
output_line( "Enter a file name." );
output_string( "(Default is \"" );
output_string( default_name );
output_string( "\"): " );
buffer[0] = 127;
buffer[1] = 0;
( void ) get_line( buffer, 0, 0 );
/* Copy file name from the input buffer */
if ( h_type > V4 )
{
unsigned char len = buffer[1];
memcpy( file_name, &buffer[2], len );
file_name[len] = '\0';
}
else
strcpy( file_name, &buffer[1] );
/* If nothing typed then use the default name */
if ( file_name[0] == '\0' )
strcpy( file_name, default_name );
#if !defined(VMS) /* VMS has file version numbers, so cannot overwrite */
/* Check if we are going to overwrite the file */
if ( flag == GAME_SAVE || flag == GAME_SCRIPT || flag == GAME_RECORD || flag == GAME_SAVE_AUX )
{
FILE *tfp;
#if defined BUFFER_FILES
char tfpbuffer[BUFSIZ];
#endif
char c;
/* Try to access the file */
tfp = fopen( file_name, "r" );
if ( tfp != NULL )
{
/* If it succeeded then prompt to overwrite */
#if defined BUFFER_FILES
setbuf( tfp, tfpbuffer );
#endif
output_line( "You are about to write over an existing file." );
output_string( "Proceed? (Y/N) " );
do
{
c = ( char ) input_character( 0 );
c = ( char ) toupper( c );
}
while ( c != 'Y' && c != 'N' );
output_char( c );
output_new_line( );
/* If no overwrite then fail the routine */
if ( c == 'N' )
status = 1;
fclose( tfp );
}
}
#endif /* !defined(VMS) */
/* Record the file name if it was OK */
if ( status == 0 )
record_line( file_name );
return ( status );
} /* get_file_name */
#if !defined(AMIGA)
/*
* fatal
*
* Display message and stop interpreter.
*
*/
void fatal( const char *s )
{
reset_screen( );
fprintf( stderr, "\nFatal error: %s (PC = 0x%08lX)\n", s, pc );
#ifdef DEBUG_TERPRE
fprintf( stdout, "\nFatal error: %s (PC = 0x%08lX)\n", s, pc );
#endif
exit( 1 );
} /* fatal */
#endif /* !defined(AMIGA) */
/*
* report_strictz_error
*
* This handles Z-code error conditions which ought to be fatal errors,
* but which players might want to ignore for the sake of finishing the
* game.
*
* The error is provided as both a numeric code and a string. This allows
* us to print a warning the first time a particular error occurs, and
* ignore it thereafter.
*
* errnum : Numeric code for error (0 to STRICTZ_NUM_ERRORS-1)
* errstr : Text description of error
*
*/
#ifdef STRICTZ
void report_strictz_error( int errnum, const char *errstr )
{
int wasfirst;
char buf[256] = { 0 };
if ( errnum <= 0 || errnum >= STRICTZ_NUM_ERRORS )
return;
if ( strictz_report_mode == STRICTZ_REPORT_FATAL )
{
sprintf( buf, "STRICTZ: %s (PC = %lx)", errstr, pc );
fatal( buf );
return;
}
wasfirst = ( strictz_error_count[errnum] == 0 );
strictz_error_count[errnum]++;
if ( ( strictz_report_mode == STRICTZ_REPORT_ALWAYS ) ||
( strictz_report_mode == STRICTZ_REPORT_ONCE && wasfirst ) )
{
sprintf( buf, "STRICTZ Warning: %s (PC = %lx)", errstr, pc );
write_string( buf );
if ( strictz_report_mode == STRICTZ_REPORT_ONCE )
{
write_string( " (will ignore further occurrences)" );
}
else
{
sprintf( buf, " (occurrence %d)", strictz_error_count[errnum] );
write_string( buf );
}
z_new_line( );
}
} /* report_strictz_error */
#endif /* STRICTZ */
#if !defined(AMIGA)
/*
* fit_line
*
* This routine determines whether a line of text will still fit
* on the screen.
*
* line : Line of text to test.
* pos : Length of text line (in characters).
* max : Maximum number of characters to fit on the screen.
*
*/
int fit_line( const char *line_buffer, int pos, int max )
{
UNUSEDVAR( line_buffer );
return ( pos < max );
} /* fit_line */
#endif /* !defined(AMIGA) */
#if !defined(AMIGA)
/*
* print_status
*
* Print the status line (type 3 games only).
*
* argv[0] : Location name
* argv[1] : Moves/Time
* argv[2] : Score
*
* Depending on how many arguments are passed to this routine
* it is to print the status line. The rendering attributes
* and the status line window will be have been activated
* when this routine is called. It is to return FALSE if it
* cannot render the status line in which case the interpreter
* will use display_char() to render it on its own.
*
* This routine has been provided in order to support
* proportional-spaced fonts.
*
*/
int print_status( int argc, char *argv[] )
{
UNUSEDVAR( argc );
UNUSEDVAR( argv );
return ( FALSE );
} /* print_status */
#endif /* !defined(AMIGA) */
#if !defined(AMIGA)
/*
* set_font
*
* Set a new character font. Font can be either be:
*
* TEXT_FONT (1) = normal text character font
* GRAPHICS_FONT (3) = graphical character font
*
*/
void set_font( int font_type )
{
UNUSEDVAR( font_type );
} /* set_font */
#endif /* !defined(AMIGA) */
#if !defined MSDOS && !defined OS2 && !defined AMIGA && !defined HARD_COLORS && !defined ATARIST
/*
* set_colours
*
* Sets screen foreground and background colours.
*
*/
void set_colours( zword_t foreground, zword_t background )
{
} /* set_colours */
#endif /* !defined MSDOS && !defined OS2 && !defined AMIGA !defined HARD_COLORS && !defined ATARIST */
#if !defined VMS && !defined MSDOS && !defined OS2 && !defined POSIX
/*
* codes_to_text
*
* Translate Z-code characters to machine specific characters. These characters
* include line drawing characters and international characters.
*
* The routine takes one of the Z-code characters from the following table and
* writes the machine specific text replacement. The target replacement buffer
* is defined by MAX_TEXT_SIZE in ztypes.h. The replacement text should be in a
* normal C, zero terminated, string.
*
* Return 0 if a translation was available, otherwise 1.
*
* Arrow characters (0x18 - 0x1b):
*
* 0x18 Up arrow
* 0x19 Down arrow
* 0x1a Right arrow
* 0x1b Left arrow
*
* International characters (0x9b - 0xa3):
*
* 0x9b a umlaut (ae)
* 0x9c o umlaut (oe)
* 0x9d u umlaut (ue)
* 0x9e A umlaut (Ae)
* 0x9f O umlaut (Oe)
* 0xa0 U umlaut (Ue)
* 0xa1 sz (ss)
* 0xa2 open quote (>>)
* 0xa3 close quota (<<)
*
* Line drawing characters (0xb3 - 0xda):
*
* 0xb3 vertical line (|)
* 0xba double vertical line (#)
* 0xc4 horizontal line (-)
* 0xcd double horizontal line (=)
* all other are corner pieces (+)
*
*/
int codes_to_text( int c, char *s )
{
return 1;
} /* codes_to_text */
#endif /* !defined VMS && !defined MSDOS && !defined OS2 && !defined POSIX */

583
property.c Normal file
View file

@ -0,0 +1,583 @@
/* $Id: property.c,v 1.2 2000/05/25 22:28:56 jholder Exp $
* --------------------------------------------------------------------
* see doc/License.txt for License Information
* --------------------------------------------------------------------
*
* File name: $Id: property.c,v 1.2 2000/05/25 22:28:56 jholder Exp $
*
* Description:
*
* Modification history:
* $Log: property.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
*
*
* --------------------------------------------------------------------
*/
/*
* property.c
*
* Property manipulation routines
*
*/
#include "ztypes.h"
/*
* get_property_addr
*
* Calculate the address of the start of the property list associated with an
* object.
*
*/
static zword_t get_property_addr( zword_t obj )
{
zword_t object_addr;
zword_t prop_addr;
zbyte_t size;
/* Calculate the address of the property pointer in the object */
object_addr = get_object_address( obj );
object_addr += ( h_type <= V3 ) ? O3_PROPERTY_OFFSET : O4_PROPERTY_OFFSET;
/* Read the property address */
prop_addr = get_word( object_addr );
/* Skip past object description which is an ASCIC of encoded words */
size = get_byte( prop_addr );
return prop_addr + ( size * 2 ) + 1;
} /* get_property_addr */
/*
* get_next_property
*
* Calculate the address of the next property in a property list.
*
*/
static zword_t get_next_property( zword_t prop_addr )
{
zbyte_t value;
/* Load the current property id */
value = get_byte( prop_addr );
prop_addr++;
/* Calculate the length of this property */
if ( h_type <= V3 )
value >>= 5;
else if ( !( value & 0x80 ) )
value >>= 6;
else
{
value = get_byte( prop_addr );
value &= property_size_mask;
if ( value == 0 )
value = 64; /* spec 1.0 */
}
/* Address property length to current property pointer */
return prop_addr + value + 1;
} /* get_next_property */
/*
* z_get_prop
*
* Load a property from a property list. Properties are held in list sorted by
* property id, with highest ids first. There is also a concept of a default
* property for loading only. The default properties are held in a table pointed
* to be the object pointer, and occupy the space before the first object.
*
*/
void z_get_prop( zword_t obj, zword_t prop )
{
zword_t prop_addr;
zword_t wprop_val;
zbyte_t bprop_val;
zbyte_t value;
#ifdef STRICTZ
if ( obj == 0 )
{
report_strictz_error( STRZERR_GET_PROP, "@get_prop called with object 0" );
store_operand( 0 );
return;
}
#endif
/* Load address of first property */
prop_addr = get_property_addr( obj );
/* Scan down the property list */
for ( ;; )
{
value = get_byte( prop_addr );
if ( ( zbyte_t ) ( value & property_mask ) <= ( zbyte_t ) prop )
break;
prop_addr = get_next_property( prop_addr );
}
/* If the property ids match then load the first property */
if ( ( zbyte_t ) ( value & property_mask ) == ( zbyte_t ) prop ) /* property found */
{
prop_addr++;
/* Only load first property if it is a byte sized property */
if ( h_type <= V3 && !( value & 0xe0 ) || h_type >= V4 && !( value & 0xc0 ) )
{
bprop_val = get_byte( prop_addr );
wprop_val = bprop_val;
}
else
{
wprop_val = get_word( prop_addr );
}
}
else /* property not found */
{
/* Calculate the address of the default property */
prop_addr = h_objects_offset + ( ( prop - 1 ) * 2 );
wprop_val = get_word( prop_addr );
}
/* store the property value */
store_operand( wprop_val );
} /* z_get_prop */
/*
* z_put_prop
*
* Store a property value in a property list. The property must exist in the
* property list to be replaced.
*
*/
void z_put_prop( zword_t obj, zword_t prop, zword_t setvalue )
{
zword_t prop_addr;
zword_t value;
#ifdef STRICTZ
if ( obj == 0 )
{
report_strictz_error( STRZERR_PUT_PROP, "@put_prop called with object 0" );
return;
}
#endif
/* Load address of first property */
prop_addr = get_property_addr( obj );
/* Scan down the property list */
for ( ;; )
{
value = get_byte( prop_addr );
if ( ( value & property_mask ) <= prop )
break;
prop_addr = get_next_property( prop_addr );
}
/* If the property id was found, store a new value, otherwise complain */
if ( ( value & property_mask ) != prop )
{
fatal( "store_property(): No such property" );
}
/* Determine if this is a byte or word sized property */
prop_addr++;
if ( h_type <= V3 && !( value & 0xe0 ) || h_type >= V4 && !( value & 0xc0 ) )
{
set_byte( prop_addr, ( zbyte_t ) setvalue );
}
else
{
set_word( prop_addr, ( zword_t ) setvalue );
}
} /* z_put_prop */
/*
* z_get_next_prop
*
* Load the property after the current property. If the current property is zero
* then load the first property.
*
*/
void z_get_next_prop( zword_t obj, zword_t prop )
{
zword_t prop_addr;
zbyte_t value;
#ifdef STRICTZ
if ( obj == 0 )
{
report_strictz_error( STRZERR_GET_NEXT_PROP, "@get_next_prop called with object 0" );
store_operand( 0 );
return;
}
#endif
/* Load address of first property */
prop_addr = get_property_addr( obj );
/* If the property id is non zero then find the next property */
if ( prop != 0 )
{
/* Scan down the property list while the target property id is less
* than the property id in the list */
do
{
value = get_byte( prop_addr );
prop_addr = get_next_property( prop_addr );
}
while ( ( zbyte_t ) ( value & property_mask ) > ( zbyte_t ) prop );
/* If the property id wasn't found then complain */
if ( ( zbyte_t ) ( value & property_mask ) != ( zbyte_t ) prop )
{
fatal( "load_next_property(): No such property" );
}
}
/* Return the next property id */
value = get_byte( prop_addr );
store_operand( ( zword_t ) ( value & property_mask ) );
} /* z_get_next_prop */
/*
* z_get_prop_addr
*
* Load the address address of the data associated with a property.
*
*/
void z_get_prop_addr( zword_t obj, zword_t prop )
{
zword_t prop_addr;
zbyte_t value;
#ifdef STRICTZ
if ( obj == 0 )
{
report_strictz_error( STRZERR_GET_PROP_ADDR, "@get_prop_addr called with object 0" );
store_operand( 0 );
return;
}
#endif
/* Load address of first property */
prop_addr = get_property_addr( obj );
/* Scan down the property list */
for ( ;; )
{
value = get_byte( prop_addr );
if ( ( zbyte_t ) ( value & property_mask ) <= ( zbyte_t ) prop )
break;
prop_addr = get_next_property( prop_addr );
}
/* If the property id was found, calc the prop addr, else return zero */
if ( ( zbyte_t ) ( value & property_mask ) == ( zbyte_t ) prop )
{
/* Skip past property id, can be a byte or a word */
if ( h_type >= V4 && ( value & 0x80 ) )
{
prop_addr++;
}
store_operand( ( zword_t ) ( prop_addr + 1 ) );
}
else
{
/* No property found, just return 0 */
store_operand( 0 );
}
} /* z_get_prop_addr */
/*
* z_get_prop_len
*
* Load the length of a property.
*
*/
void z_get_prop_len( zword_t prop_addr )
{
zbyte_t value;
/* This is proper according to an email to the Zmachine list by Graham*/
if ( prop_addr == 0 )
{
store_operand( ( zword_t ) 0 );
return;
}
/* Back up the property pointer to the property id */
prop_addr--;
value = get_byte( prop_addr );
if ( h_type <= V3 )
{
value = ( zbyte_t ) ( ( value >> ( zbyte_t ) 5 ) + ( zbyte_t ) 1 );
}
else if ( !( value & 0x80 ) )
{
value = ( zbyte_t ) ( ( value >> ( zbyte_t ) 6 ) + ( zbyte_t ) 1 );
}
else
{
value &= ( zbyte_t ) property_size_mask;
if ( value == 0 )
value = ( zbyte_t ) 64; /* spec 1.0 */
}
store_operand( value );
} /* z_get_prop_len */
/*
* z_scan_table
*
* Scan an array of bytes or words looking for a target byte or word. The
* optional 4th parameter can set the address step and also whether to scan a
* byte array.
*
*/
void z_scan_table( int argc, zword_t * argv )
{
unsigned long address;
unsigned int i, step;
/* Supply default parameters */
if ( argc < 4 )
argv[3] = 0x82;
address = argv[1];
step = argv[3];
/* Check size bit (bit 7 of step, 1 = word, 0 = byte) */
if ( step & 0x80 )
{
step &= 0x7f;
/* Scan down an array for count words looking for a match */
for ( i = 0; i < argv[2]; i++ )
{
/* If the word was found store its address and jump */
if ( read_data_word( &address ) == argv[0] )
{
store_operand( ( zword_t ) ( address - 2 ) );
conditional_jump( TRUE );
return;
}
/* Back up address then step by increment */
address = ( address - 2 ) + step;
}
}
else
{
step &= 0x7f;
/* Scan down an array for count bytes looking for a match */
for ( i = 0; i < argv[2]; i++ )
{
/* If the byte was found store its address and jump */
if ( ( zword_t ) read_data_byte( &address ) == ( zword_t ) argv[0] )
{
store_operand( ( zword_t ) ( address - 1 ) );
conditional_jump( TRUE );
return;
}
/* Back up address then step by increment */
address = ( address - 1 ) + step;
}
}
/* If the data was not found store zero and jump */
store_operand( 0 );
conditional_jump( FALSE );
} /* z_scan_table */
/*
* z_copy_table
*
*/
void z_copy_table( zword_t src, zword_t dst, zword_t count )
{
unsigned long address;
unsigned int i;
/* Catch no-op move case */
if ( src == dst || count == 0 )
return;
/* If destination address is zero then fill source with zeros */
if ( dst == 0 )
{
for ( i = 0; i < count; i++ )
z_storeb( src++, 0, 0 );
return;
}
address = src;
if ( ( ZINT16 ) count < 0 )
{
while ( count++ )
z_storeb( dst++, 0, read_data_byte( &address ) );
}
else
{
address += ( unsigned long ) count;
dst += count;
while ( count-- )
{
address--;
z_storeb( --dst, 0, read_data_byte( &address ) );
address--;
}
}
} /* z_copy_table */
/*
* z_loadw
*
* Load a word from an array of words
*
*/
void z_loadw( zword_t addr, zword_t offset )
{
unsigned long address;
/* Calculate word array index address */
address = addr + ( offset * 2 );
/* Store the byte */
store_operand( read_data_word( &address ) );
} /* z_loadw */
/*
* z_loadb
*
* Load a byte from an array of bytes
*
*/
void z_loadb( zword_t addr, zword_t offset )
{
unsigned long address;
/* Calculate byte array index address */
address = addr + offset;
/* Load the byte */
store_operand( read_data_byte( &address ) );
} /* z_loadb */
/*
* z_storew
*
* Store a word in an array of words
*
*/
void z_storew( zword_t addr, zword_t offset, zword_t value )
{
/* Calculate word array index address */
addr += offset * 2;
/* Check we are not writing outside of the writeable data area */
if ( addr > data_size )
fatal( "z_storew(): Attempted write outside of data area" );
/* Store the word */
set_word( addr, value );
} /* z_storew */
/*
* z_storeb
*
* Store a byte in an array of bytes
*
*/
void z_storeb( zword_t addr, zword_t offset, zword_t value )
{
/* Calculate byte array index address */
addr += offset;
/* Check we are not writing outside of the writeable data area */
if ( addr > data_size )
fatal( "z_storeb(): Attempted write outside of data area" );
/* Store the byte */
set_byte( addr, value );
} /* z_storeb */

573
quetzal.c Normal file
View file

@ -0,0 +1,573 @@
/* $Id: quetzal.c,v 1.3 2000/07/05 15:20:34 jholder Exp $
* --------------------------------------------------------------------
* see doc/License.txt for License Information
* --------------------------------------------------------------------
*
* File name: $Id: quetzal.c,v 1.3 2000/07/05 15:20:34 jholder Exp $
*
* Description:
*
* Modification history:
* $Log: quetzal.c,v $
* Revision 1.3 2000/07/05 15:20:34 jholder
* Updated code to remove warnings.
*
* 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
*
*
* --------------------------------------------------------------------
*/
/* quetzal.c
*
* routines to handle QUETZAL save format
*/
#include <stdio.h>
#include "ztypes.h"
/* You may want to define these as getc and putc, but then the code gets
* quite big (especially for put_c).
*/
#define get_c getc
#define put_c fputc
/* Some systems appear to have this in funny places, rather than in <stdio.h>
* where it should be.
*/
#ifndef SEEK_CUR
#define SEEK_CUR 1
#endif
#if defined(USE_QUETZAL) /* don't compile anything otherwise */
typedef unsigned long ul_t;
/* IDs of chunks we understand */
#define ID_FORM 0x464f524d
#define ID_IFZS 0x49465a53
#define ID_IFhd 0x49466864
#define ID_UMem 0x554d656d
#define ID_CMem 0x434d656d
#define ID_Stks 0x53746b73
#define ID_ANNO 0x414e4e4f
/* macros to write QUETZAL files */
#define write_byte(fp,b) (put_c ((unsigned)(b),fp) != EOF)
#define write_bytx(fp,b) write_byte(fp,(b) & 0xFF)
#define write_word(fp,w) \
(write_bytx(fp,(w)>> 8) && write_bytx(fp,(w)))
#define write_long(fp,l) \
(write_bytx(fp,(ul_t)(l)>>24) && write_bytx(fp,(ul_t)(l)>>16) && \
write_bytx(fp,(ul_t)(l)>> 8) && write_bytx(fp,(ul_t)(l)))
#define write_chnk(fp,id,len) \
(write_long(fp,id) && write_long(fp,len))
#define write_run(fp,run) \
(write_byte(fp,0) && write_byte(fp,(run)))
/* save_quetzal
*
* attempt to save game in QUETZAL format; return TRUE on success
*/
#ifdef USE_ZLIB
int save_quetzal( FILE * sfp, gzFile * gfp )
#else
int save_quetzal( FILE * sfp, FILE * gfp )
#endif
{
ul_t ifzslen = 0, cmemlen = 0, stkslen = 0, tmp_pc;
int c;
zword_t i, j, n, init_fp, tmp_fp, nstk, nvars, args;
zword_t frames[STACK_SIZE / 4 + 1];
zbyte_t var;
long cmempos, stkspos;
/* write IFZS header */
if ( !write_chnk( sfp, ID_FORM, 0 ) )
return FALSE;
if ( !write_long( sfp, ID_IFZS ) )
return FALSE;
/* write IFhd chunk */
if ( !write_chnk( sfp, ID_IFhd, 13 ) )
return FALSE;
if ( !write_word( sfp, h_version ) )
return FALSE;
for ( i = 0; i < 6; ++i )
if ( !write_byte( sfp, get_byte( H_RELEASE_DATE + i ) ) )
return FALSE;
if ( !write_word( sfp, h_checksum ) )
return FALSE;
if ( !write_long( sfp, ( ( ul_t ) pc ) << 8 ) ) /* includes pad byte */
return FALSE;
/* write CMem chunk */
/* j is current run length */
if ( ( cmempos = ftell( sfp ) ) < 0 )
return FALSE;
if ( !write_chnk( sfp, ID_CMem, 0 ) )
return FALSE;
jz_rewind( gfp );
for ( i = 0, j = 0, cmemlen = 0; i < h_restart_size; ++i )
{
if ( ( c = jz_getc( gfp ) ) == EOF )
return FALSE;
c ^= get_byte( i );
if ( c == 0 )
++j;
else
{
/* write any run there may be */
if ( j > 0 )
{
for ( ; j > 0x100; j -= 0x100 )
{
if ( !write_run( sfp, 0xFF ) )
return FALSE;
cmemlen += 2;
}
if ( !write_run( sfp, j - 1 ) )
return FALSE;
cmemlen += 2;
j = 0;
}
/* write this byte */
if ( !write_byte( sfp, c ) )
return FALSE;
++cmemlen;
}
}
/* there may be a run here, which we ignore */
if ( cmemlen & 1 ) /* chunk length must be even */
if ( !write_byte( sfp, 0 ) )
return FALSE;
/* write Stks chunk */
if ( ( stkspos = ftell( sfp ) ) < 0 )
return FALSE;
if ( !write_chnk( sfp, ID_Stks, 0 ) )
return FALSE;
/* frames is a list of FPs, most recent first */
frames[0] = sp - 5; /* what FP would be if we did a call now */
for ( init_fp = fp, n = 0; init_fp <= STACK_SIZE - 5; init_fp = stack[init_fp + 2] )
frames[++n] = init_fp;
init_fp = frames[n] + 4;
if ( h_type != 6 )
{ /* write a dummy frame for stack used before first call */
for ( i = 0; i < 6; ++i )
if ( !write_byte( sfp, 0 ) )
return FALSE;
nstk = STACK_SIZE - 1 - init_fp;
if ( !write_word( sfp, nstk ) )
return FALSE;
for ( i = STACK_SIZE - 1; i > init_fp; --i )
if ( !write_word( sfp, stack[i] ) )
return FALSE;
stkslen = 8 + 2 * nstk;
}
for ( i = n; i > 0; --i )
{
/* write out one stack frame.
*
* tmp_fp : FP when this frame was current
* tmp_pc : PC on return from this frame, plus 000pvvvv
* nvars : number of local vars for this frame
* args : argument mask for this frame
* nstk : words of evaluation stack used for this frame
* var : variable to store result
*/
tmp_fp = frames[i];
nvars = ( stack[tmp_fp + 1] & VARS_MASK ) >> VAR_SHIFT;
args = stack[tmp_fp + 1] & ARGS_MASK;
nstk = tmp_fp - frames[i - 1] - nvars - 4;
tmp_pc = stack[tmp_fp + 3] + ( ul_t ) stack[tmp_fp + 4] * PAGE_SIZE;
switch ( stack[tmp_fp + 1] & TYPE_MASK )
{
case FUNCTION:
var = read_data_byte( &tmp_pc ); /* also increments tmp_pc */
tmp_pc = ( tmp_pc << 8 ) | nvars;
break;
case PROCEDURE:
var = 0;
tmp_pc = ( tmp_pc << 8 ) | 0x10 | nvars; /* set procedure flag */
break;
/* case ASYNC: */
default:
output_line( "Illegal Z-machine operation: can't save while in interrupt." );
return FALSE;
}
if ( args != 0 )
args = ( 1 << args ) - 1; /* make args into bitmap */
if ( !write_long( sfp, tmp_pc ) )
return FALSE;
if ( !write_byte( sfp, var ) )
return FALSE;
if ( !write_byte( sfp, args ) )
return FALSE;
if ( !write_word( sfp, nstk ) )
return FALSE;
for ( j = 0; j < nvars + nstk; ++j, --tmp_fp )
if ( !write_word( sfp, stack[tmp_fp] ) )
return FALSE;
stkslen += 8 + 2 * ( nvars + nstk );
}
/* fill in lengths for variable-sized chunks */
ifzslen = 3 * 8 + 4 + 14 + cmemlen + stkslen;
if ( cmemlen & 1 )
++ifzslen;
( void ) fseek( sfp, ( long ) 4, SEEK_SET );
if ( !write_long( sfp, ifzslen ) )
return FALSE;
( void ) fseek( sfp, cmempos + 4, SEEK_SET );
if ( !write_long( sfp, cmemlen ) )
return FALSE;
( void ) fseek( sfp, stkspos + 4, SEEK_SET );
if ( !write_long( sfp, stkslen ) )
return FALSE;
return TRUE;
}
/* end save_quetzal */
/* read_word
*
* attempt to read a word; return TRUE on success
*/
static int read_word( FILE * fp, zword_t * result )
{
int a, b;
if ( ( a = get_c( fp ) ) == EOF )
return FALSE;
if ( ( b = get_c( fp ) ) == EOF )
return FALSE;
*result = ( ( zword_t ) a << 8 ) | b;
return TRUE;
}
/* read_long
*
* attempt to read a longword; return TRUE on success
*/
static int read_long( FILE * fp, ul_t * result )
{
int a, b, c, d;
if ( ( a = get_c( fp ) ) == EOF )
return FALSE;
if ( ( b = get_c( fp ) ) == EOF )
return FALSE;
if ( ( c = get_c( fp ) ) == EOF )
return FALSE;
if ( ( d = get_c( fp ) ) == EOF )
return FALSE;
*result = ( ( ul_t ) a << 24 ) | ( ( ul_t ) b << 16 ) | ( ( ul_t ) c << 8 ) | d;
#ifdef QDEBUG
printf( "%c%c%c%c", a, b, c, d );
#endif
return TRUE;
}
/* restore_quetzal
*
* attempt to restore game in QUETZAL format; return TRUE on success
*/
#define GOT_HEADER 0x01
#define GOT_STACK 0x02
#define GOT_MEMORY 0x04
#define GOT_NONE 0x00
#define GOT_ALL 0x07
#define GOT_ERROR 0x80
#ifdef USE_ZLIB
int restore_quetzal( FILE * sfp, gzFile * gfp )
#else
int restore_quetzal( FILE * sfp, FILE * gfp )
#endif
{
ul_t ifzslen, currlen, tmpl, skip = 0;
zword_t i, tmpw;
zbyte_t progress = GOT_NONE;
int x, y;
/* check for IFZS file */
if ( !read_long( sfp, &tmpl ) || !read_long( sfp, &ifzslen ) )
return FALSE;
if ( !read_long( sfp, &currlen ) )
return FALSE;
if ( tmpl != ID_FORM || currlen != ID_IFZS )
{
output_line( "This is not a saved game file!" );
return FALSE;
}
if ( ( ifzslen & 1 ) || ifzslen < 4 )
return FALSE;
ifzslen -= 4;
/* read a chunk and process it */
while ( ifzslen > 0 )
{
/* read chunk header */
if ( ifzslen < 8 )
return FALSE;
if ( !read_long( sfp, &tmpl ) || !read_long( sfp, &currlen ) )
return FALSE;
ifzslen -= 8;
/* body of chunk */
if ( ifzslen < currlen )
return FALSE;
skip = currlen & 1;
ifzslen -= currlen + skip;
switch ( tmpl )
{
case ID_IFhd:
if ( progress & GOT_HEADER )
{
output_line( "Save file has two IFhd chunks!" );
return FALSE;
}
progress |= GOT_HEADER;
if ( currlen < 13 || !read_word( sfp, &i ) )
return FALSE;
if ( i != h_version )
progress = GOT_ERROR;
for ( i = H_RELEASE_DATE; i < H_RELEASE_DATE + 6; ++i )
{
if ( ( x = get_c( sfp ) ) == EOF )
return FALSE;
if ( x != ( int ) get_byte( i ) )
progress = GOT_ERROR;
}
if ( !read_word( sfp, &i ) )
return FALSE;
if ( i != h_checksum )
progress = GOT_ERROR;
if ( progress == GOT_ERROR )
{
output_line( "File was not saved from this story!" );
return FALSE;
}
if ( ( x = get_c( sfp ) ) == EOF )
return FALSE;
pc = ( ul_t ) x << 16;
if ( ( x = get_c( sfp ) ) == EOF )
return FALSE;
pc |= ( ul_t ) x << 8;
if ( ( x = get_c( sfp ) ) == EOF )
return FALSE;
pc |= ( ul_t ) x;
for ( i = 13; ( ul_t ) i < currlen; ++i )
( void ) get_c( sfp ); /* skip rest of chunk */
break;
case ID_Stks:
if ( progress & GOT_STACK )
{
output_line( "File contains two stack chunks!" );
break;
}
progress |= GOT_STACK;
sp = STACK_SIZE;
if ( h_type != 6 )
{
/* dummy stack frame for stack used before call */
if ( currlen < 8 )
return FALSE;
for ( i = 0; i < 6; ++i )
if ( get_c( sfp ) != 0 )
return FALSE;
if ( !read_word( sfp, &tmpw ) )
return FALSE;
currlen -= 8;
if ( currlen < (unsigned long)(tmpw * 2) )
return FALSE;
for ( i = 0; i < tmpw; ++i )
if ( !read_word( sfp, stack + ( --sp ) ) )
return FALSE;
currlen -= tmpw * 2;
}
for ( fp = STACK_SIZE - 1, frame_count = 0; currlen > 0; currlen -= 8 )
{
if ( currlen < 8 )
return FALSE;
if ( sp < 4 )
{
output_line( "error: this save-file has too much stack, and I can't cope." );
return FALSE;
}
/* read PC, procedure flag, and arg count */
if ( !read_long( sfp, &tmpl ) )
return FALSE;
y = ( zword_t ) tmpl & 0x0F;
tmpw = y << VAR_SHIFT; /* number of variables */
/* read result variable */
if ( ( x = get_c( sfp ) ) == EOF )
return FALSE;
if ( tmpl & 0x10 )
{
tmpw |= PROCEDURE;
tmpl >>= 8;
}
else
{
tmpw |= FUNCTION;
tmpl >>= 8;
--tmpl;
/* sanity check on result variable */
if ( read_data_byte( &tmpl ) != ( zbyte_t ) x )
{
output_line( "error: wrong variable number on stack (wrong story file?)." );
return FALSE;
}
--tmpl; /* read_data_byte increments it */
}
stack[--sp] = ( zword_t ) ( tmpl / PAGE_SIZE );
stack[--sp] = ( zword_t ) ( tmpl % PAGE_SIZE );
stack[--sp] = fp;
if ( ( x = get_c( sfp ) ) == EOF )
return FALSE;
++x; /* hopefully x now contains a single set bit */
for ( i = 0; i < 8; ++i )
if ( x & ( 1 << i ) )
break;
if ( x ^ ( 1 << i ) ) /* if more than 1 bit set */
{
output_line
( "error: this game uses incomplete argument lists (which I can't handle)." );
return FALSE;
}
tmpw |= i;
stack[--sp] = tmpw;
fp = sp - 1; /* FP for next frame */
if ( !read_word( sfp, &tmpw ) )
return FALSE;
tmpw += y; /* local vars plus eval stack used */
if ( tmpw >= sp )
{
output_line( "error: this save-file uses more stack than I can cope with." );
return FALSE;
}
if ( currlen < (unsigned long)(tmpw * 2) )
return FALSE;
for ( i = 0; i < tmpw; ++i )
if ( !read_word( sfp, stack + ( --sp ) ) )
return FALSE;
currlen -= tmpw * 2;
}
break;
case ID_ANNO:
z_buffer_mode( ON );
for ( ; currlen > 0; --currlen )
if ( ( x = get_c( sfp ) ) == EOF )
return FALSE;
else
write_char( x );
write_char( ( char ) 13 );
break;
case ID_CMem:
if ( !( progress & GOT_MEMORY ) )
{
jz_rewind( gfp );
i = 0; /* bytes written to data area */
for ( ; currlen > 0; --currlen )
{
if ( ( x = get_c( sfp ) ) == EOF )
return FALSE;
if ( x == 0 ) /* start run */
{
if ( currlen < 2 )
{
output_line( "File contains bogus CMem chunk" );
for ( ; currlen > 0; --currlen )
( void ) get_c( sfp ); /* skip rest */
currlen = 1;
i = 0xFFFF;
break; /* keep going in case there's a UMem */
}
--currlen;
if ( ( x = get_c( sfp ) ) == EOF )
return FALSE;
for ( ; x >= 0 && i < h_restart_size; --x, ++i )
if ( ( y = jz_getc( gfp ) ) == EOF )
return FALSE;
else
set_byte( i, y );
}
else /* not a run */
{
if ( ( y = jz_getc( gfp ) ) == EOF )
return FALSE;
set_byte( i, x ^ y );
++i;
}
if ( i > h_restart_size )
{
output_line( "warning: CMem chunk too long!" );
for ( ; currlen > 1; --currlen )
( void ) get_c( sfp ); /* skip rest */
break; /* keep going in case there's a UMem */
}
}
/* if chunk is short, assume a run */
for ( ; i < h_restart_size; ++i )
if ( ( y = jz_getc( gfp ) ) == EOF )
return FALSE;
else
set_byte( i, y );
if ( currlen == 0 )
progress |= GOT_MEMORY; /* only if succeeded */
break;
}
/* Fall thru (to default) if already got memory */
case ID_UMem:
if ( !( progress & GOT_MEMORY ) )
{
if ( currlen == h_restart_size )
{
if ( fread( datap, h_restart_size, 1, sfp ) == 1 )
{
progress |= GOT_MEMORY; /* only if succeeded */
break;
}
}
else
output_line( "warning: UMem chunk wrong size!" );
/* and fall thru into default */
}
/* Fall thru (to default) if already got memory */
default:
( void ) fseek( sfp, currlen, SEEK_CUR ); /* skip chunk */
break;
}
if ( skip )
( void ) get_c( sfp ); /* skip pad byte */
}
if ( !( progress & GOT_HEADER ) )
output_line( "error: no header chunk in file." );
if ( !( progress & GOT_STACK ) )
output_line( "error: no stack chunk in file." );
if ( !( progress & GOT_MEMORY ) )
output_line( "error: no memory chunk in file." );
return ( progress == GOT_ALL );
}
#endif /* defined(USE_QUETZAL) */

577
screen.c Normal file
View file

@ -0,0 +1,577 @@
/* $Id: screen.c,v 1.3 2000/07/05 15:20:34 jholder Exp $
* --------------------------------------------------------------------
* see doc/License.txt for License Information
* --------------------------------------------------------------------
*
* File name: $Id: screen.c,v 1.3 2000/07/05 15:20:34 jholder Exp $
*
* Description:
*
* Modification history:
* $Log: screen.c,v $
* Revision 1.3 2000/07/05 15:20:34 jholder
* Updated code to remove warnings.
*
* 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
*
*
* --------------------------------------------------------------------
*/
/*
* screen.c
*
* Generic screen manipulation routines. Most of these routines call the machine
* specific routines to do the actual work.
*
*/
#include "ztypes.h"
/*
* z_set_window
*
* Put the cursor in the text or status window. The cursor is free to move in
* the status window, but is fixed to the input line in the text window.
*
*/
void z_set_window( zword_t w )
{
int row, col;
flush_buffer( FALSE );
screen_window = w;
if ( screen_window == STATUS_WINDOW )
{
/* Status window: disable formatting and select status window */
formatting = OFF;
scripting_disable = ON;
select_status_window( );
/* Put cursor at top of status area */
if ( h_type < V4 )
move_cursor( 2, 1 );
else
move_cursor( 1, 1 );
}
else
{
/* Text window: enable formatting and select text window */
select_text_window( );
scripting_disable = OFF;
formatting = ON;
/* Move cursor if it has been left in the status area */
get_cursor_position( &row, &col );
if ( row <= status_size )
move_cursor( status_size + 1, 1 );
}
/* Force text attribute to normal rendition */
set_attribute( NORMAL );
} /* z_set_window */
/*
* z_split_window
*
* Set the size of the status window. The default size for the status window is
* zero lines for both type 3 and 4 games. The status line is handled specially
* for type 3 games and always occurs the line immediately above the status
* window.
*
*/
void z_split_window( zword_t lines )
{
/* Maximum status window size is 255 */
lines &= 0xff;
/* The top line is always set for V1 to V3 games, so account for it here. */
if ( h_type < V4 )
lines++;
if ( lines )
{
/* If size is non zero the turn on the status window */
status_active = ON;
/* Bound the status size to one line less than the total screen height */
if ( lines > ( zword_t ) ( screen_rows - 1 ) )
status_size = ( zword_t ) ( screen_rows - 1 );
else
status_size = lines;
/* Create the status window, or resize it */
create_status_window( );
/* Need to clear the status window for type 3 games */
if ( h_type < V4 )
z_erase_window( STATUS_WINDOW );
}
else
{
/* Lines are zero so turn off the status window */
status_active = OFF;
/* Reset the lines written counter and status size */
lines_written = 0;
status_size = 0;
/* Delete the status window */
delete_status_window( );
/* Return cursor to text window */
select_text_window( );
}
} /* z_split_window */
/*
* z_erase_window
*
* Clear one or all windows on the screen.
*
*/
void z_erase_window( zword_t w )
{
flush_buffer( TRUE );
if ( ( zbyte_t ) w == ( zbyte_t ) Z_SCREEN )
{
clear_screen( );
}
else if ( ( zbyte_t ) w == TEXT_WINDOW )
{
clear_text_window( );
}
else if ( ( zbyte_t ) w == STATUS_WINDOW )
{
clear_status_window( );
return;
}
if ( h_type > V4 )
move_cursor( 1, 1 );
else
move_cursor( screen_rows, 1 );
} /* z_erase_window */
/*
* z_erase_line
*
* Clear one line on the screen.
*
*/
void z_erase_line( zword_t flag )
{
if ( flag == TRUE )
clear_line( );
} /* z_erase_line */
/*
* z_set_cursor
*
* Set the cursor position in the status window only.
*
*/
void z_set_cursor( zword_t row, zword_t column )
{
/* Can only move cursor if format mode is off and in status window */
if ( formatting == OFF && screen_window == STATUS_WINDOW )
{
move_cursor( row, column );
}
#ifdef STRICTZ
else
{
report_strictz_error( STRZERR_MOV_CURSOR, "@set_cursor called outside the status window!" );
}
#endif
} /* z_set_cursor */
/*
* pad_line
*
* Pad the status line with spaces up to a column position.
*
*/
static void pad_line( int column )
{
int i;
for ( i = status_pos; i < column; i++ )
write_char( ' ' );
status_pos = column;
} /* pad_line */
/*
* z_show_status
*
* Format and output the status line for type 3 games only.
*
*/
void z_show_status( void )
{
int i, count = 0, end_of_string[3];
char *status_part[3];
/* Move the cursor to the top line of the status window, set the reverse
* rendition and print the status line */
z_set_window( STATUS_WINDOW );
move_cursor( 1, 1 );
set_attribute( REVERSE );
/* Redirect output to the status line buffer */
z_output_stream( 3, 0 );
/* Print the object description for global variable 16 */
pad_line( 1 );
status_part[count] = &status_line[status_pos];
if ( load_variable( 16 ) != 0 )
z_print_obj( load_variable( 16 ) );
end_of_string[count++] = status_pos;
status_line[status_pos++] = '\0';
if ( get_byte( H_CONFIG ) & CONFIG_TIME )
{
/* If a time display print the hours and minutes from global
* variables 17 and 18 */
pad_line( screen_cols - 21 );
status_part[count] = &status_line[status_pos];
write_string( " Time: " );
print_time( load_variable( 17 ), load_variable( 18 ) );
end_of_string[count++] = status_pos;
status_line[status_pos++] = '\0';
}
else
{
/* If a moves/score display print the score and moves from global
* variables 17 and 18 */
pad_line( screen_cols - 31 );
status_part[count] = &status_line[status_pos];
write_string( " Score: " );
z_print_num( load_variable( 17 ) );
end_of_string[count++] = status_pos;
status_line[status_pos++] = '\0';
pad_line( screen_cols - 15 );
status_part[count] = &status_line[status_pos];
write_string( " Moves: " );
z_print_num( load_variable( 18 ) );
end_of_string[count++] = status_pos;
status_line[status_pos++] = '\0';
}
/* Pad the end of status line with spaces then disable output redirection */
pad_line( screen_cols );
z_output_stream( ( zword_t ) - 3, 0 );
/* Try and print the status line for a proportional font screen. If this
* fails then remove embedded nulls in status line buffer and just output
* it to the screen */
if ( print_status( count, status_part ) == FALSE )
{
for ( i = 0; i < count; i++ )
status_line[end_of_string[i]] = ' ';
status_line[status_pos] = '\0';
write_string( status_line );
}
set_attribute( NORMAL );
z_set_window( TEXT_WINDOW );
} /* z_show_status */
/*
* blank_status_line
*
* Output a blank status line for type 3 games only.
*
*/
void blank_status_line( void )
{
/* Move the cursor to the top line of the status window, set the reverse
* rendition and print the status line */
z_set_window( STATUS_WINDOW );
move_cursor( 1, 1 );
set_attribute( REVERSE );
/* Redirect output to the status line buffer and pad the status line with
* spaces then disable output redirection */
z_output_stream( 3, 0 );
pad_line( screen_cols );
status_line[status_pos] = '\0';
z_output_stream( ( zword_t ) - 3, 0 );
/* Write the status line */
write_string( status_line );
/* Turn off attributes and return to text window */
set_attribute( NORMAL );
z_set_window( TEXT_WINDOW );
} /* blank_status_line */
/*
* output_string
*
* Output a string of characters.
*
*/
void output_string( const char *s )
{
while ( *s )
output_char( *s++ );
} /* output_string */
/*
* output_line
*
* Output a string of characters followed by a new line.
*
*/
void output_line( const char *s )
{
output_string( s );
output_new_line( );
} /* output_line */
/*
* output_char
*
* Output a character.
*
*/
void output_char( int c )
{
/* If output is enabled then either select the rendition attribute
* or just display the character */
if ( outputting == ON )
{
display_char( (unsigned int)(c & 0xff) );
}
} /* output_char */
/*
* output_new_line
*
* Scroll the text window up one line and pause the window if it is full.
*
*/
void output_new_line( void )
{
int row, col;
/* Don't print if output is disabled or replaying commands */
if ( outputting == ON )
{
if ( formatting == ON && screen_window == TEXT_WINDOW )
{
/* If this is the text window then scroll it up one line */
scroll_line( );
/* See if we have filled the screen. The spare line is for
* the [MORE] message
*/
if ( ++lines_written >= ( ( screen_rows - top_margin ) - status_size - 1 ) )
{
/* Display the new status line while the screen in paused */
if ( h_type < V4 )
z_show_status( );
/* Reset the line count and display the more message */
lines_written = 0;
if ( replaying == OFF )
{
get_cursor_position( &row, &col );
output_string( "[MORE]" );
( void ) input_character( 0 );
move_cursor( row, col );
output_string( " " );
move_cursor( row, col );
/* clear_line (); */
}
}
}
else
/* If this is the status window then just output a new line */
output_char( '\n' );
}
} /* output_new_line */
/*
* z_print_table
*
* Writes text into a rectangular window on the screen.
*
* argv[0] = start of text address
* argv[1] = rectangle width
* argv[2] = rectangle height (default = 1)
*
*/
void z_print_table( int argc, zword_t * argv )
{
unsigned long address;
unsigned int width, height;
unsigned int row, column;
/* Supply default arguments */
if ( argc < 3 )
argv[2] = 1;
/* Don't do anything if the window is zero high or wide */
if ( argv[1] == 0 || argv[2] == 0 )
return;
/* Get coordinates of top left corner of rectangle */
get_cursor_position( ( int * ) &row, ( int * ) &column );
address = argv[0];
/* Write text in width * height rectangle */
for ( height = 0; height < argv[2]; height++ )
{
for ( width = 0; width < argv[1]; width++ )
write_char( read_data_byte( &address ) );
/* Put cursor back to lefthand side of rectangle on next line */
if ( height != (unsigned)( argv[2] - 1 ) )
move_cursor( ++row, column );
}
} /* z_print_table */
/*
* z_set_font
*
* Set text or graphic font. 1 = text font, 3 = graphics font.
*
*/
void z_set_font( zword_t new_font )
{
zword_t old_font = font;
if ( new_font != old_font )
{
font = new_font;
set_font( font );
}
store_operand( old_font );
} /* z_set_font */
/*
* z_set_colour
*
* Set the colour of the screen. Colour can be set on four things:
* Screen background
* Text typed by player
* Text written by game
* Graphics characters
*
* Colors can be set to 1 of 9 values:
* 1 = machine default (IBM/PC = blue background, everything else white)
* 2 = black
* 3 = red
* 4 = green
* 5 = brown
* 6 = blue
* 7 = magenta
* 8 = cyan
* 9 = white
*
*/
void z_set_colour( zword_t foreground, zword_t background )
{
if ( ( ZINT16 ) foreground < -1 || ( ZINT16 ) foreground > 9 || ( ZINT16 ) background < -1 ||
( ZINT16 ) background > 9 )
fatal( "Bad colour!" );
flush_buffer( FALSE );
set_colours( foreground, background );
return;
} /* z_set_colour */

1145
text.c Normal file

File diff suppressed because it is too large Load diff

126
variable.c Normal file
View file

@ -0,0 +1,126 @@
/* $Id: variable.c,v 1.3 2000/07/05 15:20:34 jholder Exp $
* --------------------------------------------------------------------
* see doc/License.txt for License Information
* --------------------------------------------------------------------
*
* File name: $Id: variable.c,v 1.3 2000/07/05 15:20:34 jholder Exp $
*
* Description:
*
* Modification history:
* $Log: variable.c,v $
* Revision 1.3 2000/07/05 15:20:34 jholder
* Updated code to remove warnings.
*
* 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
*
*
* --------------------------------------------------------------------
*/
/*
* variable.c
*
* Variable manipulation routines
*
*/
#include "ztypes.h"
/*
* z_load
*
* Load and store a variable value on stack.
*
*/
void z_load( zword_t variable )
{
store_operand( load_variable( variable ) );
} /* load */
/*
* z_push
*
* Push a value onto the stack
*
*/
void z_push( zword_t value )
{
stack[--sp] = value;
} /* push_var */
/*
* z_pull
*
* Pop a variable from the stack.
*
*/
void z_pull( zword_t variable )
{
z_store( variable, stack[sp++] );
} /* pop_var */
/*
* z_inc
*
* Increment a variable.
*
*/
void z_inc( zword_t variable )
{
z_store( variable, (zword_t)(load_variable( variable ) + 1) );
} /* increment */
/*
* z_dec
*
* Decrement a variable.
*
*/
void z_dec( zword_t variable )
{
z_store( variable, (zword_t)(load_variable( variable ) - 1) );
} /* decrement */
/*
* z_inc_chk
*
* Increment a variable and then check its value against a target.
*
*/
void z_inc_chk( zword_t variable, zword_t target )
{
ZINT16 value;
value = ( ZINT16 ) load_variable( variable );
z_store( variable, ++value );
conditional_jump( value > ( ZINT16 ) target );
} /* increment_check */
/*
* z_dec_chk
*
* Decrement a variable and then check its value against a target.
*
*/
void z_dec_chk( zword_t variable, zword_t target )
{
ZINT16 value;
value = ( ZINT16 ) load_variable( variable );
z_store( variable, --value );
conditional_jump( value < ( ZINT16 ) target );
} /* decrement_check */

799
ztypes.h Normal file
View file

@ -0,0 +1,799 @@
/* $Id: ztypes.h,v 1.4 2000/10/04 23:07:57 jholder Exp $
* --------------------------------------------------------------------
* see doc/License.txt for License Information
* --------------------------------------------------------------------
*
* File name: $Id: ztypes.h,v 1.4 2000/10/04 23:07:57 jholder Exp $
*
* Description:
*
* Modification history:
* $Log: ztypes.h,v $
* Revision 1.4 2000/10/04 23:07:57 jholder
* fixed redirect problem with isolatin1 range chars
*
* Revision 1.3 2000/07/05 15:20:34 jholder
* Updated code to remove warnings.
*
* 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
*
*
* --------------------------------------------------------------------
*/
/*
* ztypes.h
*
* Any global stuff required by the C modules.
*
*/
#if !defined(__ZTYPES_INCLUDED)
#define __ZTYPES_INCLUDED
// IIgs stuff - everyone gets it
#include "joey.h"
#define LOUSY_RANDOM
/* AIX likes to see this define... */
#if defined(AIX)
#define _POSIX_SOURCE
#define POSIX
#endif
/* for Turbo C & MSC */
#if defined(__MSDOS__)
#ifndef MSDOS
#define MSDOS
#endif
#define LOUSY_RANDOM
#define Z_FILENAME_MAX FILENAME_MAX
#endif
#if defined OS2
#define LOUSY_RANDOM
#define Z_FILENAME_MAX FILENAME_MAX
#endif
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#if defined(MSDOS)
#include <malloc.h>
#endif /* MSDOS */
/* Set Version of JZIP */
#include "jzip.h"
extern unsigned char JTERP;
/* Configuration options */
#ifdef USE_ZLIB
#include <zlib.h>
#define jz_rewind gzrewind
#define jz_seek gzseek
#define jz_open gzopen
#define jz_close gzclose
#define jz_getc gzgetc
#else
#define jz_rewind rewind
#define jz_seek fseek
#define jz_open fopen
#define jz_close fclose
#define jz_getc getc
#endif
#define USE_QUETZAL
#define DEFAULT_ROWS 25 /* Default screen height */
#define DEFAULT_COLS 40 /* Deafult screen width */
#define DEFAULT_RIGHT_MARGIN 1 /* # of characters in rt margin (UNIX likes 1)*/
#define DEFAULT_TOP_MARGIN 0 /* # of lines on screen before [MORE] message */
#ifdef LOUSY_RANDOM
#define RANDOM_FUNC rand
#define SRANDOM_FUNC srand
#else
#define RANDOM_FUNC random
#define SRANDOM_FUNC srandom
#endif
/* Perform stricter z-code error checking. If STRICTZ is #defined,
* the interpreter will check for common opcode errors, such as reading
* or writing properties of the "nothing" (0) object. When such an
* error occurs, the opcode will call report_zstrict_error() and
* then continue in some safe manner. This may mean doing nothing,
* returning 0, or something like that.
*
* See osdepend.c for the definition of report_zstrict_error(). Note that
* this function may call fatal() to shut down the interpreter.
*
* If STRICTZ is not #defined, the STRICTZ patch has no effect at all.
* It does not even check to continue safely when an error occurs;
* it just behaves the way ZIP has always behaved. This typically
* means calling get_property_addr(0) or get_object_address(0),
* which will return a meaningless value, and continuing on with
* that.
*/
//#define STRICTZ
/* Global defines */
#ifndef UNUSEDVAR
#define UNUSEDVAR(a) a=a;
#endif
/* number of bits in a byte. needed by AIX!!! ;^) */
#ifndef NBBY
#define NBBY 8
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef Z_FILENAME_MAX
#define Z_FILENAME_MAX 255
#endif
#ifndef Z_PATHNAME_MAX
#define Z_PATHNAME_MAX 1024
#endif
#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0
#endif
#ifndef EXIT_FAILURE
#define EXIT_FAILURE 1
#endif
#ifndef SEEK_SET
#define SEEK_SET 0
#endif
#ifndef SEEK_END
#define SEEK_END 2
#endif
#ifdef unix
#if defined (HAVE_BCOPY)
#define memmove(a, b, c) bcopy (b, a, c)
#else
#define memmove(a, b, c) memcpy ((a), (b), (c))
#endif
#ifndef const
#define const
#endif
#endif /* unix */
/* Z types */
typedef unsigned char zbyte_t; /* unsigned 1 byte quantity */
typedef unsigned short zword_t; /* unsigned 2 byte quantity */
typedef short ZINT16; /* signed 2 byte quantity */
/* Data file header format */
typedef struct zheader
{
zbyte_t type;
zbyte_t config;
zword_t version;
zword_t data_size;
zword_t start_pc;
zword_t words_offset;
zword_t objects_offset;
zword_t globals_offset;
zword_t restart_size;
zword_t flags;
zbyte_t release_date[6];
zword_t synonyms_offset;
zword_t file_size;
zword_t checksum;
zbyte_t interpreter;
zbyte_t interpreter_version;
zbyte_t screen_rows;
zbyte_t screen_columns;
zbyte_t screen_left;
zbyte_t screen_right;
zbyte_t screen_top;
zbyte_t screen_bottom;
zbyte_t max_char_width;
zbyte_t max_char_height;
zword_t filler1[3];
zword_t function_keys_offset;
zword_t filler2[2];
zword_t alternate_alphabet_offset;
zword_t mouse_position_offset;
zword_t filler3[4];
}
zheader_t;
#define H_TYPE 0
#define H_CONFIG 1
#define CONFIG_BYTE_SWAPPED 0x01 /* Game data is byte swapped - V3 */
#define CONFIG_TIME 0x02 /* Status line displays time - V3 */
#define CONFIG_MAX_DATA 0x04 /* Data area should 64K if possible - V3 */
#define CONFIG_TANDY 0x08 /* Tandy licensed game - V3 */
#define CONFIG_NOSTATUSLINE 0x10 /* Interp can't support a status line - V3 */
#define CONFIG_WINDOWS 0x20 /* Interpr supports split screen mode - V3 */
#define CONFIG_COLOUR 0x01 /* Game supports colour - V5+ */
#define CONFIG_PICTURES 0x02 /* Picture displaying available? - V4+ */
#define CONFIG_BOLDFACE 0x04 /* Interpr supports boldface style - V4+ */
#define CONFIG_EMPHASIS 0x08 /* Interpreter supports text emphasis - V4+ */
#define CONFIG_FIXED 0x10 /* Interpr supports fixed width style - V4+ */
#define CONFIG_SFX 0x20 /* Interpr supports sound effects - V4+ */
#define CONFIG_TIMEDINPUT 0x80 /* Interpr supports timed input - V4+ */
#define CONFIG_PROPORTIONAL 0x40 /* Interpr uses proportional font - V3+ */
#define H_VERSION 2
#define H_DATA_SIZE 4
#define H_START_PC 6
#define H_WORDS_OFFSET 8
#define H_OBJECTS_OFFSET 10
#define H_GLOBALS_OFFSET 12
#define H_RESTART_SIZE 14
#define H_FLAGS 16
#define SCRIPTING_FLAG 0x01
#define FIXED_FONT_FLAG 0x02
#define REFRESH_FLAG 0x04
#define GRAPHICS_FLAG 0x08
#define SOUND_FLAG 0x10 /* V4 */
#define UNDO_AVAILABLE_FLAG 0x10 /* V5 */
#define COLOUR_FLAG 0x40
#define NEW_SOUND_FLAG 0x80
#define H_RELEASE_DATE 18
#define H_SYNONYMS_OFFSET 24
#define H_FILE_SIZE 26
#define H_CHECKSUM 28
#define H_INTERPRETER 30
#define H_UNICODE_TABLE 34
#define INTERP_GENERIC 0
#define INTERP_DEC_20 1
#define INTERP_APPLE_IIE 2
#define INTERP_MACINTOSH 3
#define INTERP_AMIGA 4
#define INTERP_ATARI_ST 5
#define INTERP_MSDOS 6
#define INTERP_CBM_128 7
#define INTERP_CBM_64 8
#define INTERP_APPLE_IIC 9
#define INTERP_APPLE_IIGS 10
#define INTERP_TANDY 11
#define INTERP_UNIX 12
#define INTERP_VMS 13
#define H_INTERPRETER_VERSION 31
#define H_SCREEN_ROWS 32
#define H_SCREEN_COLUMNS 33
#define H_SCREEN_LEFT 34
#define H_SCREEN_RIGHT 35
#define H_SCREEN_TOP 36
#define H_SCREEN_BOTTOM 37
#define H_MAX_CHAR_WIDTH 38
#define H_MAX_CHAR_HEIGHT 39
#define H_FILLER1 40
#define H_FUNCTION_KEYS_OFFSET 46
#define H_FILLER2 48
#define H_STANDARD_HIGH 50
#define H_STANDARD_LOW 51
#define H_ALTERNATE_ALPHABET_OFFSET 52
#define H_MOUSE_POSITION_OFFSET 54
#define H_FILLER3 56
#define V1 1
#define V2 2
/* Version 3 object format */
#define V3 3
typedef struct zobjectv3
{
zword_t attributes[2];
zbyte_t parent;
zbyte_t next;
zbyte_t child;
zword_t property_offset;
}
zobjectv3_t;
#define O3_ATTRIBUTES 0
#define O3_PARENT 4
#define O3_NEXT 5
#define O3_CHILD 6
#define O3_PROPERTY_OFFSET 7
#define O3_SIZE 9
#define PARENT3(offset) (offset + O3_PARENT)
#define NEXT3(offset) (offset + O3_NEXT)
#define CHILD3(offset) (offset + O3_CHILD)
#define P3_MAX_PROPERTIES 0x20
/* Version 4 object format */
#define V4 4
typedef struct zobjectv4
{
zword_t attributes[3];
zword_t parent;
zword_t next;
zword_t child;
zword_t property_offset;
}
zobjectv4_t;
#define O4_ATTRIBUTES 0
#define O4_PARENT 6
#define O4_NEXT 8
#define O4_CHILD 10
#define O4_PROPERTY_OFFSET 12
#define O4_SIZE 14
#define PARENT4(offset) (offset + O4_PARENT)
#define NEXT4(offset) (offset + O4_NEXT)
#define CHILD4(offset) (offset + O4_CHILD)
#define P4_MAX_PROPERTIES 0x40
#define V5 5
#define V6 6
#define V7 7
#define V8 8
/* Interpreter states */
#define STOP 0
#define RUN 1
/* Call types */
#define FUNCTION 0x0000
#if defined(USE_QUETZAL)
#define PROCEDURE 0x1000
#define ASYNC 0x2000
#else
#define PROCEDURE 0x0100
#define ASYNC 0x0200
#endif
#if defined(USE_QUETZAL)
#define ARGS_MASK 0x00FF
#define VARS_MASK 0x0F00
#define TYPE_MASK 0xF000
#define VAR_SHIFT 8
#else
#define ARGS_MASK 0x00ff
#define TYPE_MASK 0xff00
#endif
/* Local defines */
#define PAGE_SIZE 0x200
#define PAGE_MASK 0x1FF
#define PAGE_SHIFT 9
#define STACK_SIZE 1024
#define ON 1
#define OFF 0
#define RESET -1
#define Z_SCREEN 255
#define TEXT_WINDOW 0
#define STATUS_WINDOW 1
#define MIN_ATTRIBUTE 0
#define NORMAL 0
#define REVERSE 1
#define BOLD 2
#define EMPHASIS 4
#define FIXED_FONT 8
#define MAX_ATTRIBUTE 8
#define TEXT_FONT 1
#define GRAPHICS_FONT 3
#define FOREGROUND 0
#define BACKGROUND 1
#define GAME_RESTORE 0
#define GAME_SAVE 1
#define GAME_SCRIPT 2
#define GAME_RECORD 3
#define GAME_PLAYBACK 4
#define UNDO_SAVE 5
#define UNDO_RESTORE 6
#define GAME_SAVE_AUX 7
#define GAME_LOAD_AUX 8
#define MAX_TEXT_SIZE 8
/* Data access macros */
#define get_byte(offset) ((zbyte_t) datap[offset])
#define get_word(offset) ((zword_t) (((zword_t) datap[offset] << 8) + (zword_t) datap[offset + 1]))
#define set_byte(offset,value) datap[offset] = (zbyte_t) (value)
#define set_word(offset,value) datap[offset] = (zbyte_t) ((zword_t) (value) >> 8), datap[offset + 1] = (zbyte_t) ((zword_t) (value) & 0xff)
/* External data */
extern int GLOBALVER;
extern zbyte_t h_type;
extern zbyte_t h_config;
extern zword_t h_version;
extern zword_t h_data_size;
extern zword_t h_start_pc;
extern zword_t h_words_offset;
extern zword_t h_objects_offset;
extern zword_t h_globals_offset;
extern zword_t h_restart_size;
extern zword_t h_flags;
extern zword_t h_synonyms_offset;
extern zword_t h_file_size;
extern zword_t h_checksum;
extern zbyte_t h_interpreter;
extern zbyte_t h_interpreter_version;
extern zword_t h_alternate_alphabet_offset;
extern zword_t h_unicode_table;
extern int story_scaler;
extern int story_shift;
extern int property_mask;
extern int property_size_mask;
extern zword_t stack[STACK_SIZE];
extern zword_t sp;
extern zword_t fp;
extern zword_t frame_count;
extern unsigned long pc;
extern int interpreter_state;
extern int interpreter_status;
extern unsigned int data_size;
extern zbyte_t *datap;
extern zbyte_t *undo_datap;
extern int screen_rows;
extern int screen_cols;
extern int right_margin;
extern int top_margin;
extern int screen_window;
extern int interp_initialized;
extern int formatting;
extern int outputting;
extern int redirect_depth;
extern int redirecting;
extern int scripting;
extern int scripting_disable;
extern int recording;
extern int replaying;
extern int font;
extern int status_active;
extern int status_size;
extern char fTandy;
extern char fIBMGraphics;
extern int lines_written;
extern int status_pos;
extern char *line;
extern char *status_line;
extern char lookup_table[3][26];
extern char monochrome;
extern int hist_buf_size;
extern char bigscreen;
extern unsigned char zscii2latin1[69];
#ifdef STRICTZ
/* Definitions for STRICTZ functions and error codes. */
void report_strictz_error( int, const char * );
/* Error codes */
#define STRZERR_NO_ERROR (0)
#define STRZERR_JIN (1)
#define STRZERR_GET_CHILD (2)
#define STRZERR_GET_PARENT (3)
#define STRZERR_GET_SIBLING (4)
#define STRZERR_GET_PROP_ADDR (5)
#define STRZERR_GET_PROP (6)
#define STRZERR_PUT_PROP (7)
#define STRZERR_CLEAR_ATTR (8)
#define STRZERR_SET_ATTR (9)
#define STRZERR_TEST_ATTR (10)
#define STRZERR_MOVE_OBJECT (11)
#define STRZERR_MOVE_OBJECT_2 (12)
#define STRZERR_REMOVE_OBJECT (13)
#define STRZERR_GET_NEXT_PROP (14)
#define STRZERR_DIV_ZERO (15)
#define STRZERR_MOV_CURSOR (16)
#define STRICTZ_NUM_ERRORS (17)
#endif /* STRICTZ */
/* Global routines */
/* control.c */
void z_check_arg_count( zword_t );
int z_call( int, zword_t *, int );
void z_jump( zword_t );
void z_restart( void );
void restart_interp( int );
void z_ret( zword_t );
void z_catch( void );
void z_throw( zword_t, zword_t );
/* fileio.c */
void z_verify( void );
int z_restore( int, zword_t, zword_t, zword_t );
int z_save( int, zword_t, zword_t, zword_t );
void z_restore_undo( void );
void z_save_undo( void );
void z_open_playback( int );
void close_record( void );
void close_script( void );
void close_story( void );
void flush_script( void );
unsigned int get_story_size( void );
void open_record( void );
void open_script( void );
void open_story( const char * );
int playback_key( void );
int playback_line( int, char *, int * );
void read_page( int, void * );
void record_key( int );
void record_line( const char * );
void script_char( int );
void script_string( const char * );
void script_line( const char * );
void script_new_line( void );
/* getopt.c */
#ifndef HAVE_GETOPT
int getopt( int, char *[], const char * );
#endif
/* input.c */
int get_line( char *, zword_t, zword_t );
void z_read_char( int, zword_t * );
void z_sread_aread( int, zword_t * );
void z_tokenise( int, zword_t * );
/* interpre.c */
int interpret( void );
/* license.c */
void print_license( void );
/* math.c */
void z_add( zword_t, zword_t );
void z_div( zword_t, zword_t );
void z_mul( zword_t, zword_t );
void z_sub( zword_t, zword_t );
void z_mod( zword_t, zword_t );
void z_or( zword_t, zword_t );
void z_and( zword_t, zword_t );
void z_not( zword_t );
void z_art_shift( zword_t, zword_t );
void z_log_shift( zword_t, zword_t );
void z_je( int, zword_t * );
void z_jg( zword_t, zword_t );
void z_jl( zword_t, zword_t );
void z_jz( zword_t );
void z_random( zword_t );
void z_test( zword_t, zword_t );
/* memory.c */
void load_cache( void );
void unload_cache( void );
zbyte_t read_code_byte( void );
zbyte_t read_data_byte( unsigned long * );
zword_t read_code_word( void );
zword_t read_data_word( unsigned long * );
/* object.c */
zword_t get_object_address( zword_t );
void z_insert_obj( zword_t, zword_t );
void z_remove_obj( zword_t );
void z_get_child( zword_t );
void z_get_sibling( zword_t );
void z_get_parent( zword_t );
void z_jin( zword_t, zword_t );
void z_clear_attr( zword_t, zword_t );
void z_set_attr( zword_t, zword_t );
void z_test_attr( zword_t, zword_t );
/* operand.c */
void z_piracy( int );
void z_store( int, zword_t );
void conditional_jump( int );
void store_operand( zword_t );
zword_t load_operand( int );
zword_t load_variable( int );
/* osdepend.c */
int codes_to_text( int, char * );
void fatal( const char * );
void file_cleanup( const char *, int );
int fit_line( const char *, int, int );
int get_file_name( char *, char *, int );
int print_status( int, char *[] );
//void process_arguments( int, char *[] );
void set_colours( zword_t, zword_t );
void set_font( int );
void sound( int, zword_t * );
/* property.c */
void z_get_next_prop( zword_t, zword_t );
void z_get_prop( zword_t, zword_t );
void z_get_prop_addr( zword_t, zword_t );
void z_get_prop_len( zword_t );
void z_put_prop( zword_t, zword_t, zword_t );
void z_copy_table( zword_t, zword_t, zword_t );
void z_scan_table( int, zword_t * );
void z_loadb( zword_t, zword_t );
void z_loadw( zword_t, zword_t );
void z_storeb( zword_t, zword_t, zword_t );
void z_storew( zword_t, zword_t, zword_t );
/* quetzal.c */
#ifdef USE_ZLIB
int save_quetzal( FILE *, gzFile * );
int restore_quetzal( FILE *, gzFile * );
#else
int save_quetzal( FILE *, FILE * );
int restore_quetzal( FILE *, FILE * );
#endif
/* screen.c */
void z_show_status( void );
void z_set_cursor( zword_t, zword_t );
void z_set_font( zword_t );
void z_split_window( zword_t );
void z_set_window( zword_t );
void z_set_colour( zword_t, zword_t );
void z_erase_line( zword_t );
void z_erase_window( zword_t );
void z_print_table( int, zword_t * );
void blank_status_line( void );
void output_char( int );
void output_new_line( void );
void output_string( const char * );
void output_line( const char * );
/* screenio.c */
int input_character( int );
void clear_line( void );
void clear_screen( void );
void clear_status_window( void );
void clear_text_window( void );
void create_status_window( void );
void delete_status_window( void );
void display_char( int );
int fit_line( const char *, int, int );
void get_cursor_position( int *, int * );
void initialize_screen( void );
int input_line( int, char *, int, int * );
void move_cursor( int, int );
int print_status( int, char *[] );
void reset_screen( void );
void restart_screen( void );
void restore_cursor_position( void );
void save_cursor_position( void );
void scroll_line( void );
void select_status_window( void );
void select_text_window( void );
void set_attribute( int );
/* text.c */
void z_encode( zword_t, zword_t, zword_t, zword_t );
void z_new_line( void );
void z_print_char( zword_t );
void z_print_num( zword_t );
void z_print( void );
void z_print_addr( zword_t );
void z_print_paddr( zword_t );
void z_print_obj( zword_t );
void z_print_ret( void );
void z_buffer_mode( zword_t );
void z_output_stream( zword_t, zword_t );
void z_input_stream( int );
void z_set_text_style( zword_t );
void decode_text( unsigned long * );
void encode_text( int, const char *, ZINT16 * );
void flush_buffer( int );
void print_time( int, int );
void write_char( int );
void write_string( const char * );
void write_zchar( int );
/* variable.c */
void z_inc( zword_t );
void z_dec( zword_t );
void z_inc_chk( zword_t, zword_t );
void z_dec_chk( zword_t, zword_t );
void z_load( zword_t );
void z_pull( zword_t );
void z_push( zword_t );
#endif /* !defined(__ZTYPES_INCLUDED) */