From: P. F. Chimento Date: Sat, 29 Jan 2011 14:31:35 +0000 (+0100) Subject: Updated interpreters X-Git-Tag: v0.9~158 X-Git-Url: https://git.stderr.nl/gitweb?a=commitdiff_plain;h=75009f8f40bbb580194e1722db76f4644bf18641;p=projects%2Fchimara%2Fchimara.git Updated interpreters All bundled interpreters are now current with revision 496 of Gargoyle's interpreters. Added new Gargoyle extension functions to garglk.c and garglk.h. --- diff --git a/interpreters/frotz/AUTHORS b/interpreters/frotz/AUTHORS index 9e4139d..cb92bd2 100644 --- a/interpreters/frotz/AUTHORS +++ b/interpreters/frotz/AUTHORS @@ -17,6 +17,17 @@ V6 semi-support: OSS sound support (from xfrotz 2.32.1): Daniel Schepler +Treaty of Babel code: + L. Ross Raszewski + +Unicode support (largely from WinFrotz): + David Kinder + Michael Martin (curses interface to WinFrotz) + +NFrotz port: + Michael Martin + +The original Frotz special thanks list: Thanks also to those who posted to rec.arts.int-fiction feedback on what I was doing with Unix Frotz, people who checked the betas for bugs, and sent @@ -30,3 +41,7 @@ that I've forgotten. Michael Edmonson (author of Rezrov) and Evin Robertson (author of Nitfol) deserve recognition for the ideas that I've borrowed from their programs. + +For NFrotz, thanks are also due to John Campbell, David Cuthbert, and +Simon Fowler for their assistance in testing, and to Torbjorn Anderson +for some additional patches. diff --git a/interpreters/frotz/Makefile.am b/interpreters/frotz/Makefile.am index f72505c..fdecc89 100644 --- a/interpreters/frotz/Makefile.am +++ b/interpreters/frotz/Makefile.am @@ -1,7 +1,7 @@ pkglib_LTLIBRARIES = frotz.la frotz_la_SOURCES = buffer.c err.c fastmem.c files.c input.c main.c math.c \ object.c process.c quetzal.c random.c redirect.c sound.c stream.c table.c \ - text.c variable.c glkscreen.c glkmisc.c frotz.h glkfrotz.h glkio.h setup.h + text.c variable.c glkscreen.c glkmisc.c frotz.h glkfrotz.h glkio.h frotz_la_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/libchimara if TARGET_ILIAD diff --git a/interpreters/frotz/README b/interpreters/frotz/README index 970146b..0694b76 100644 --- a/interpreters/frotz/README +++ b/interpreters/frotz/README @@ -1,5 +1,6 @@ -GlkFrotz v2.43 - An interpreter for most Infocom and other Z-machine games. -Glk port created and maintained by Tor Andersson. +GlkFrotz v2.50 - An interpreter for most Infocom and other Z-machine games. +Glk port created by Tor Andersson. +Maintained by Ben Cressey and Philip Chimento. Read more on the web page: @@ -11,17 +12,18 @@ you try to compile, unless you are using Gargoyle: char * garglk_fileref_get_name(frefid_t fref); --- Original readme --- - -FROTZ V2.43 - An interpreter for all Infocom and other Z-machine games. +NFROTZ V0.3 - An interpreter for all Infocom and other Z-machine games. Complies with standard 1.0 of Graham Nelson's specification. Originally written by Stefan Jokisch in 1995-1997. Ported to Unix by Galen Hazelwood. Reference code and Unix port currently maintained by David Griffith. +Windows port by David Kinder. +NFrotz is an ncurses-based synthesis of the Windows port with the Unix + one, maintained by Michael Martin. - Compiles and runs on most common flavors of Unix, both open source and not. - Plays all Z-code games including V6. -- Old-style sound support through OSS driver. - Config files. - Configurable error checking. - Default use of the Quetzal file format. Command line option to use the @@ -29,51 +31,15 @@ Reference code and Unix port currently maintained by David Griffith. format at Quetzal such that converting from old-style and Quetzal is difficult if not impossible. This also means you can't restore an old-style save and then save your game in Quetzal. -- Optional speech synthesis and recognition through FLITE and Sphinx. +- Blorb and iFiction support. +- Full Unicode output on terminals that support it. - Distributed under the GNU Public License. - For information on what Interactive Fiction is and how to play it, see the file "HOW_TO_PLAY". For installation information, see the file "INSTALL". -For information on the speech synthesis and recognition capabilities of -Frotz, see the file "SPEECH". - For update history, see the file "Changelog". For information on known bugs in Frotz, see the file "BUGS". - -For bug reports, check the Unix Frotz website to see if there's a new -release. If not, send your bug report to me at dgriffi@cs.csubak.edu. - -For information on porting Frotz to new platforms, see the file "PORTING". - -For those who are involved in creating and distributing binary packages -containing Frotz and including Frotz in BSD-style ports/pkgsrc trees, -see the file "PACKAGING". - -The latest information on Unix Frotz is available at the Unix Frotz -homepage at http://www.cs.csubak.edu/~dgriffi/proj/frotz/. - -The latest release of Unix Frotz is available from the Interactive -Fiction Archive at: -http://www.ifarchive.org/if-archive/infocom/interpreters/ - frotz/frotz-.tar.gz -and -ftp://ftp.ifarchive.org/if-archive/infocom/interpreters/ - frotz/frotz-.tar.gz - -The Interactive Fiction Archive has several mirrors which may be better -choices depending on where you live. Here is a partial list. - -http://www.ifarchive.org/ (USA, Pittsburgh, PA) -http://mirror.ifarchive.org/ (USA) -ftp://ftp.ifarchive.com/if-archive/ (USA, Los Angeles, CA) -ftp://ftp.guetech.org/if-archive/ (USA, Bremerton, WA) -ftp://ftp.plover.net/if-archive/ (USA, Chicago, IL) -ftp://ftp.funet.fi/pub/misc/if-archive/ (Finland) -http://www.planetmirror.com/pub/if-archive/ (Australia) -ftp://ftp.planetmirror.com/pub/if-archive/ (Australia) - diff --git a/interpreters/frotz/buffer.c b/interpreters/frotz/buffer.c index f3a721e..bff9572 100644 --- a/interpreters/frotz/buffer.c +++ b/interpreters/frotz/buffer.c @@ -27,6 +27,7 @@ extern void stream_new_line (void); static zchar buffer[TEXT_BUFFER_SIZE]; static int bufpos = 0; +static bool locked = FALSE; static zchar prev_c = 0; @@ -39,7 +40,6 @@ static zchar prev_c = 0; void flush_buffer (void) { - static bool locked = FALSE; /* Make sure we stop when flush_buffer is called from flush_buffer. Note that this is difficult to avoid as we might print a newline @@ -149,5 +149,6 @@ void init_buffer(void) memset(buffer, 0, sizeof (zchar) * TEXT_BUFFER_SIZE); bufpos = 0; prev_c = 0; + locked = FALSE; } diff --git a/interpreters/frotz/err.c b/interpreters/frotz/err.c index 61ca78c..ffb5168 100644 --- a/interpreters/frotz/err.c +++ b/interpreters/frotz/err.c @@ -48,6 +48,7 @@ static char *err_messages[] = { "Illegal window", "Illegal window property", "Print at illegal address", + "Illegal dictionary word length", "@jin called with object 0", "@get_child called with object 0", "@get_parent called with object 0", @@ -100,8 +101,8 @@ void runtime_error (int errnum) if (errnum <= 0 || errnum > ERR_NUM_ERRORS) return; - if (f_setup.err_report_mode == ERR_REPORT_FATAL - || (!f_setup.ignore_errors && errnum <= ERR_MAX_FATAL)) { + if (option_err_report_mode == ERR_REPORT_FATAL + || (!option_ignore_errors && errnum <= ERR_MAX_FATAL)) { flush_buffer (); os_fatal (err_messages[errnum - 1]); return; @@ -110,8 +111,8 @@ void runtime_error (int errnum) wasfirst = (error_count[errnum - 1] == 0); error_count[errnum - 1]++; - if ((f_setup.err_report_mode == ERR_REPORT_ALWAYS) - || (f_setup.err_report_mode == ERR_REPORT_ONCE && wasfirst)) { + if ((option_err_report_mode == ERR_REPORT_ALWAYS) + || (option_err_report_mode == ERR_REPORT_ONCE && wasfirst)) { long pc; GET_PC (pc); @@ -121,7 +122,7 @@ void runtime_error (int errnum) print_long (pc, 16); print_char (')'); - if (f_setup.err_report_mode == ERR_REPORT_ONCE) { + if (option_err_report_mode == ERR_REPORT_ONCE) { print_string (" (will ignore further occurrences)"); } else { print_string (" (occurence "); diff --git a/interpreters/frotz/fastmem.c b/interpreters/frotz/fastmem.c index 113c50c..cb4a20e 100644 --- a/interpreters/frotz/fastmem.c +++ b/interpreters/frotz/fastmem.c @@ -180,6 +180,10 @@ void restart_header (void) SET_BYTE (H_STANDARD_HIGH, h_standard_high); SET_BYTE (H_STANDARD_LOW, h_standard_low); + set_header_extension (HX_FLAGS, hx_flags); + set_header_extension (HX_FORE_COLOUR, hx_fore_colour); + set_header_extension (HX_BACK_COLOUR, hx_back_colour); + }/* restart_header */ /* @@ -395,6 +399,7 @@ no_match: ; /* null statement */ hx_table_size = get_header_extension (HX_TABLE_SIZE); hx_unicode_table = get_header_extension (HX_UNICODE_TABLE); + hx_flags = get_header_extension (HX_FLAGS); }/* init_memory */ @@ -426,7 +431,7 @@ void init_undo (void) undo_diff = undo_mem + h_dynamic_size; memcpy (prev_zmp, zmp, h_dynamic_size); } else - f_setup.undo_slots = 0; + option_undo_slots = 0; if (reserve_mem != 0) free (reserved); @@ -569,7 +574,7 @@ void z_restart (void) sp = fp = stack + STACK_SIZE; frame_count = 0; - if (h_version != V6) { + if (h_version != V6 && h_version != V9) { long pc = (long) h_start_pc; SET_PC (pc); @@ -624,7 +629,7 @@ void z_restore (void) if ((gfp = frotzopenprompt(FILE_RESTORE)) == NULL) goto finished; - if (f_setup.save_quetzal) { + if (option_save_quetzal) { success = restore_quetzal (gfp, story_fp, blorb_ofs); } else { @@ -813,7 +818,7 @@ static void mem_undiff (zbyte *diff, long diff_length, zbyte *dest) int restore_undo (void) { - if (f_setup.undo_slots == 0) /* undo feature unavailable */ + if (option_undo_slots == 0) /* undo feature unavailable */ return -1; @@ -897,7 +902,7 @@ void z_save (void) if ((gfp = frotzopenprompt (FILE_SAVE)) == NULL) goto finished; - if (f_setup.save_quetzal) { + if (option_save_quetzal) { success = save_quetzal (gfp, story_fp, blorb_ofs); } else { /* Write game file */ @@ -971,7 +976,7 @@ int save_undo (void) zword stack_size; undo_t *p; - if (f_setup.undo_slots == 0) /* undo feature unavailable */ + if (option_undo_slots == 0) /* undo feature unavailable */ return -1; /* save undo possible */ @@ -987,7 +992,7 @@ int save_undo (void) else first_undo = NULL; - if (undo_count == f_setup.undo_slots) + if (undo_count == option_undo_slots) free_undo (1); diff_size = mem_diff (zmp, prev_zmp, h_dynamic_size, undo_diff); diff --git a/interpreters/frotz/files.c b/interpreters/frotz/files.c index 6ed8c33..f407395 100644 --- a/interpreters/frotz/files.c +++ b/interpreters/frotz/files.c @@ -46,30 +46,21 @@ static FILE *pfp = NULL; * the old transscription file in V1 to V4, and to ask for a new file * name in V5+. * - * Alas, we cannot do this, since glk cannot give us the filename - * to reopen it again, and I dont want to mess with filerefs here. - * */ +static bool script_valid = FALSE; + void script_open (void) { - static bool script_valid = FALSE; h_flags &= ~SCRIPTING_FLAG; -#if 0 - if (h_version >= V5 || !script_valid) { - if (!os_read_file_name (new_name, script_name, FILE_SCRIPT)) - goto done; - } - - /* Opening in "at" mode doesn't work for script_erase_input... */ - - if ((sfp = fopen (sfp = fopen (script_name, "r+t")) != NULL || (sfp = fopen (script_name, "w+t")) != NULL) { - -#endif + if (h_version < V5 && script_valid) + sfp = frotzreopen(FILE_SCRIPT); + else + sfp = frotzopenprompt(FILE_SCRIPT); - if ((sfp = frotzopenprompt(FILE_SCRIPT)) != NULL) + if (sfp != NULL) { fseek (sfp, 0, SEEK_END); @@ -82,8 +73,6 @@ void script_open (void) } else print_string ("Cannot open file\n"); -/* done: */ - SET_WORD (H_FLAGS, h_flags) }/* script_open */ @@ -140,7 +129,7 @@ void script_char (zchar c) if (c == ZC_GAP) { script_char (' '); script_char (' '); return; } - fputc (c, sfp); script_width++; + fputwc (c, sfp); script_width++; }/* script_char */ @@ -170,7 +159,7 @@ void script_word (const zchar *s) else width += 1; - if (f_setup.script_cols != 0 && script_width + width > f_setup.script_cols) { + if (option_script_cols != 0 && script_width + width > option_script_cols) { if (*s == ' ' || *s == ZC_INDENT || *s == ZC_GAP) s++; @@ -203,7 +192,7 @@ void script_write_input (const zchar *buf, zchar key) for (i = 0, width = 0; buf[i] != 0; i++) width++; - if (f_setup.script_cols != 0 && script_width + width > f_setup.script_cols) + if (option_script_cols != 0 && script_width + width > option_script_cols) script_new_line (); for (i = 0; buf[i] != 0; i++) diff --git a/interpreters/frotz/frotz.h b/interpreters/frotz/frotz.h index ba800fc..56b9c60 100644 --- a/interpreters/frotz/frotz.h +++ b/interpreters/frotz/frotz.h @@ -21,6 +21,18 @@ typedef int bool; typedef unsigned char zbyte; typedef unsigned short zword; +/*** Glk needs a 32-bit integer type for Unicode characters ***/ +#include +#if (USHORT_MAX == 4294967295) +typedef unsigned short zchar; +#elif (UINT_MAX == 4294967295) +typedef unsigned int zchar; +#elif (ULONG_MAX == 4294967295) +typedef unsigned long zchar; +#else +#error No 32-bit integer type found. +#endif + enum story { BEYOND_ZORK, @@ -33,15 +45,13 @@ enum story UNKNOWN }; -typedef unsigned char zchar; - /*** Constants that may be set at compile time ***/ #ifndef MAX_UNDO_SLOTS #define MAX_UNDO_SLOTS 500 #endif #ifndef MAX_FILE_NAME -#define MAX_FILE_NAME 80 +#define MAX_FILE_NAME 256 #endif #ifndef TEXT_BUFFER_SIZE #define TEXT_BUFFER_SIZE 200 @@ -50,7 +60,7 @@ typedef unsigned char zchar; #define INPUT_BUFFER_SIZE 200 #endif #ifndef STACK_SIZE -#define STACK_SIZE 60000 +#define STACK_SIZE 61440 #endif #ifndef DEFAULT_SAVE_NAME @@ -109,6 +119,9 @@ typedef unsigned char zchar; #define HX_MOUSE_X 1 #define HX_MOUSE_Y 2 #define HX_UNICODE_TABLE 3 +#define HX_FLAGS 4 +#define HX_FORE_COLOUR 5 +#define HX_BACK_COLOUR 6 /*** Various Z-machine constants ***/ @@ -120,6 +133,7 @@ typedef unsigned char zchar; #define V6 6 #define V7 7 #define V8 8 +#define V9 9 #define CONFIG_BYTE_SWAPPED 0x01 /* Story file is byte swapped - V3 */ #define CONFIG_TIME 0x02 /* Status line displays time - V3 */ @@ -135,7 +149,6 @@ typedef unsigned char zchar; #define CONFIG_EMPHASIS 0x08 /* Interpr supports emphasis style - V4+ */ #define CONFIG_FIXED 0x10 /* Interpr supports fixed width style - V4+ */ #define CONFIG_SOUND 0x20 /* Interpr supports sound - V6 */ - #define CONFIG_TIMEDINPUT 0x80 /* Interpr supports timed input - V4+ */ #define SCRIPTING_FLAG 0x0001 /* Outputting to transscription file - V1+ */ @@ -149,6 +162,8 @@ typedef unsigned char zchar; #define SOUND_FLAG 0x0080 /* Game wants to use sound effects - V5+ */ #define MENU_FLAG 0x0100 /* Game wants to use menus - V6 */ +#define TRANSPARENT_FLAG 0x0001 /* Game wants to use transparency - V6 */ + #define INTERP_DEFAULT 0 #define INTERP_DEC_20 1 #define INTERP_APPLE_IIE 2 @@ -174,6 +189,7 @@ typedef unsigned char zchar; #define LIGHTGREY_COLOUR 10 /* INTERP_AMIGA only */ #define MEDIUMGREY_COLOUR 11 /* INTERP_AMIGA only */ #define DARKGREY_COLOUR 12 /* INTERP_AMIGA only */ +#define TRANSPARENT_COLOUR 15 /* ZSpec 1.1 */ #define REVERSE_STYLE 1 #define BOLDFACE_STYLE 2 @@ -185,8 +201,10 @@ typedef unsigned char zchar; #define GRAPHICS_FONT 3 #define FIXED_WIDTH_FONT 4 -#define BEEP_HIGH 1 -#define BEEP_LOW 2 +/*** Constants for os_beep */ + +#define BEEP_HIGH 1 +#define BEEP_LOW 2 /*** Constants for os_restart_game */ @@ -194,6 +212,12 @@ typedef unsigned char zchar; #define RESTART_WPROP_SET 1 #define RESTART_END 2 +/*** Constants for os_menu */ + +#define MENU_NEW 0 +#define MENU_ADD 1 +#define MENU_REMOVE 2 + /*** Character codes ***/ #define ZC_TIME_OUT 0x00 @@ -254,8 +278,8 @@ typedef unsigned char zchar; extern zbyte *pcp; extern zbyte *zmp; -#define lo(v) ((zbyte *)&v)[1] -#define hi(v) ((zbyte *)&v)[0] +#define lo(v) ((zbyte *)&v)[1] +#define hi(v) ((zbyte *)&v)[0] #define SET_WORD(addr,v) { zmp[addr] = hi(v); zmp[addr+1] = lo(v); } #define LOW_WORD(addr,v) { hi(v) = zmp[addr]; lo(v) = zmp[addr+1]; } @@ -279,7 +303,11 @@ extern zbyte *zmp; #define SET_WORD(addr,v) { zmp[addr] = hi(v); zmp[addr+1] = lo(v); } #define LOW_WORD(addr,v) { v = ((zword) zmp[addr] << 8) | zmp[addr+1]; } #define HIGH_WORD(addr,v) { v = ((zword) zmp[addr] << 8) | zmp[addr+1]; } +#define HIGH_LONG(addr,v) { v = ((zword) zmp[addr] << 24) | \ + ((zword) zmp[addr+1] << 16) | \ + ((zword) zmp[addr+2] << 8) | zmp[addr+3]; } #define CODE_WORD(v) { v = ((zword) pcp[0] << 8) | pcp[1]; pcp += 2; } +#define CODE_IDX_WORD(v,i){ v = ((zword) pcp[i] << 8) | pcp[i+1]; } #define GET_PC(v) { v = pcp - zmp; } #define SET_PC(v) { pcp = zmp + v; } @@ -326,6 +354,9 @@ extern zword hx_table_size; extern zword hx_mouse_x; extern zword hx_mouse_y; extern zword hx_unicode_table; +extern zword hx_flags; +extern zword hx_fore_colour; +extern zword hx_back_colour; /*** Various data ***/ @@ -354,24 +385,39 @@ extern int mwin; extern int mouse_x; extern int mouse_y; +extern int menu_selected; extern bool enable_wrapping; extern bool enable_scripting; extern bool enable_scrolling; extern bool enable_buffering; - +extern int option_attribute_assignment; +extern int option_attribute_testing; +extern int option_object_locating; +extern int option_object_movement; +extern int option_context_lines; +extern int option_left_margin; +extern int option_right_margin; +extern int option_ignore_errors; +extern int option_piracy; +extern int option_undo_slots; +extern int option_expand_abbreviations; +extern int option_script_cols; +extern int option_save_quetzal; +extern int option_sound; /* dg */ +extern int option_err_report_mode; extern char *option_zcode_path; /* dg */ extern long reserve_mem; - /*** Z-machine opcodes ***/ void z_add (void); void z_and (void); void z_art_shift (void); void z_buffer_mode (void); +void z_buffer_screen (void); void z_call_n (void); void z_call_s (void); void z_catch (void); @@ -464,6 +510,7 @@ void z_set_cursor (void); void z_set_margins (void); void z_set_window (void); void z_set_text_style (void); +void z_set_true_colour (void); void z_show_status (void); void z_sound_effect (void); void z_split_window (void); @@ -505,24 +552,25 @@ void runtime_error (int); #define ERR_ILL_WIN 16 /* Illegal window */ #define ERR_ILL_WIN_PROP 17 /* Illegal window property */ #define ERR_ILL_PRINT_ADDR 18 /* Print at illegal address */ -#define ERR_MAX_FATAL 18 +#define ERR_DICT_LEN 19 /* Illegal dictionary word length */ +#define ERR_MAX_FATAL 19 /* Less serious errors */ -#define ERR_JIN_0 19 /* @jin called with object 0 */ -#define ERR_GET_CHILD_0 20 /* @get_child called with object 0 */ -#define ERR_GET_PARENT_0 21 /* @get_parent called with object 0 */ -#define ERR_GET_SIBLING_0 22 /* @get_sibling called with object 0 */ -#define ERR_GET_PROP_ADDR_0 23 /* @get_prop_addr called with object 0 */ -#define ERR_GET_PROP_0 24 /* @get_prop called with object 0 */ -#define ERR_PUT_PROP_0 25 /* @put_prop called with object 0 */ -#define ERR_CLEAR_ATTR_0 26 /* @clear_attr called with object 0 */ -#define ERR_SET_ATTR_0 27 /* @set_attr called with object 0 */ -#define ERR_TEST_ATTR_0 28 /* @test_attr called with object 0 */ -#define ERR_MOVE_OBJECT_0 29 /* @move_object called moving object 0 */ -#define ERR_MOVE_OBJECT_TO_0 30 /* @move_object called moving into object 0 */ -#define ERR_REMOVE_OBJECT_0 31 /* @remove_object called with object 0 */ -#define ERR_GET_NEXT_PROP_0 32 /* @get_next_prop called with object 0 */ -#define ERR_NUM_ERRORS (32) +#define ERR_JIN_0 20 /* @jin called with object 0 */ +#define ERR_GET_CHILD_0 21 /* @get_child called with object 0 */ +#define ERR_GET_PARENT_0 22 /* @get_parent called with object 0 */ +#define ERR_GET_SIBLING_0 23 /* @get_sibling called with object 0 */ +#define ERR_GET_PROP_ADDR_0 24 /* @get_prop_addr called with object 0 */ +#define ERR_GET_PROP_0 25 /* @get_prop called with object 0 */ +#define ERR_PUT_PROP_0 26 /* @put_prop called with object 0 */ +#define ERR_CLEAR_ATTR_0 27 /* @clear_attr called with object 0 */ +#define ERR_SET_ATTR_0 28 /* @set_attr called with object 0 */ +#define ERR_TEST_ATTR_0 29 /* @test_attr called with object 0 */ +#define ERR_MOVE_OBJECT_0 30 /* @move_object called moving object 0 */ +#define ERR_MOVE_OBJECT_TO_0 31 /* @move_object called moving into object 0 */ +#define ERR_REMOVE_OBJECT_0 32 /* @remove_object called with object 0 */ +#define ERR_GET_NEXT_PROP_0 33 /* @get_next_prop called with object 0 */ +#define ERR_NUM_ERRORS (33) /* There are four error reporting modes: never report errors; report only the first time a given error type occurs; report @@ -544,17 +592,10 @@ void runtime_error (int); /*** Various global functions ***/ -/* MacOSX libm defines this */ -#define init_process frotz_process - -void init_process(void); -void init_sound(void); - zchar translate_from_zscii (zbyte); zbyte translate_to_zscii (zchar); -void init_buffer(void); -void flush_buffer(void); +void flush_buffer (void); void new_line (void); void print_char (zchar); void print_num (zword); @@ -574,42 +615,67 @@ void storew (zword, zword); /*** Interface functions ***/ void os_beep (int); +int os_buffer_screen (int); int os_char_width (zchar); +int os_check_unicode (int, zchar); void os_display_char (zchar); void os_display_string (const zchar *); - +void os_display_cstring (const char *); void os_draw_picture (int, int, int); -void os_erase_area (int, int, int, int); - -void os_fatal (char *); +void os_erase_area (int, int, int, int, int); +void os_fatal (const char *); void os_finish_with_sample (int); - int os_font_data (int, int *, int *); +int os_from_true_colour (zword); void os_init_screen (void); +void os_menu(int, int, const zword *); void os_more_prompt (void); int os_peek_colour (void); int os_picture_data (int, int *, int *); - void os_prepare_sample (int); void os_process_arguments (int, char *[]); int os_random_seed (void); int os_read_file_name (char *, const char *, int); zchar os_read_key (int, int); zchar os_read_line (int, zchar *, int, int, int); +zword os_read_mouse (void); void os_reset_screen (void); void os_restart_game (int); - void os_scroll_area (int, int, int, int, int); +void os_scrollback_char (zchar); +void os_scrollback_erase (int); void os_set_colour (int, int); void os_set_cursor (int, int); void os_set_font (int); - void os_set_text_style (int); - void os_start_sample (int, int, int, zword); void os_stop_sample (int); int os_string_width (const zchar *); -void os_init_setup (void); -int os_speech_output(const zchar *); - -#include "setup.h" +int os_string_length (zchar *); +void os_tick (void); +zword os_to_true_colour (int); +int os_wrap_window (int); +void os_window_height (int, int); + +void seed_random (int); +void restart_screen (void); +void refresh_text_style (void); +void call (zword, int, zword *, int); +void split_window (zword); +void script_open (void); +void script_close (void); + +//FILE *os_path_open (const char *, const char *, long *); + +//zword save_quetzal (FILE *, zbyte *); +//zword restore_quetzal (FILE *, zbyte *); + +void erase_window (zword); + +extern void (*op0_opcodes[]) (void); +extern void (*op1_opcodes[]) (void); +extern void (*op2_opcodes[]) (void); +extern void (*var_opcodes[]) (void); + +extern zchar* decoded; +extern zchar* encoded; diff --git a/interpreters/frotz/glkfrotz.h b/interpreters/frotz/glkfrotz.h index 85b2222..92230b0 100644 --- a/interpreters/frotz/glkfrotz.h +++ b/interpreters/frotz/glkfrotz.h @@ -22,7 +22,7 @@ /* glk-frotz.h * - * Frotz os functions for the Glk library version 0.6.1. + * Frotz os functions for the Glk library version 0.7.0. */ #include "frotz.h" @@ -45,14 +45,11 @@ extern winid_t gos_upper; extern winid_t gos_lower; extern winid_t gos_curwin; extern int gos_linepending; -extern char *gos_linebuf; +extern zchar *gos_linebuf; extern winid_t gos_linewin; extern schanid_t gos_channel; -/* from ../common/setup.h */ -extern f_setup_t f_setup; - /* From input.c. */ bool is_terminator (zchar); diff --git a/interpreters/frotz/glkio.h b/interpreters/frotz/glkio.h index 8b0b9c0..ef54799 100644 --- a/interpreters/frotz/glkio.h +++ b/interpreters/frotz/glkio.h @@ -47,6 +47,8 @@ typedef struct glk_stream_struct FILE; #define fprintf(f,s,a) (glk_put_string_stream(f, a), 0) #undef fputc #define fputc(c, f) (glk_put_char_stream(f, (unsigned char)(c)), 0) +#undef fputwc +#define fputwc(c, f) (glk_put_char_stream_uni(f, (zchar)(c)), 0); #undef fputs #define fputs(s, f) (glk_put_buffer_stream(f, s, strlen(s)), 0) #undef ftell @@ -62,5 +64,5 @@ typedef struct glk_stream_struct FILE; #define SEEK_END seekmode_End FILE *frotzopenprompt(int flag); +FILE *frotzreopen(int flag); FILE *frotzopen(char *filename, int flag); - diff --git a/interpreters/frotz/glkmisc.c b/interpreters/frotz/glkmisc.c index 2e0ade7..f18f149 100644 --- a/interpreters/frotz/glkmisc.c +++ b/interpreters/frotz/glkmisc.c @@ -24,9 +24,7 @@ #include "glkfrotz.h" -#define VERSION "2.43" - -f_setup_t f_setup; +#define VERSION "2.50" int curr_status_ht = 0; int mach_status_ht = 0; @@ -36,7 +34,7 @@ winid_t gos_lower = NULL; winid_t gos_curwin = NULL; int gos_linepending = 0; -char *gos_linebuf = NULL; +zchar *gos_linebuf = NULL; winid_t gos_linewin = NULL; schanid_t gos_channel = NULL; @@ -50,7 +48,6 @@ schanid_t gos_channel = NULL; " -a watch attribute setting\n"\ " -A watch attribute testing\n"\ " -i ignore fatal errors\n"\ -" -I # interpreter number\n"\ " -o watch object movement\n"\ " -O watch object locating\n"\ " -P alter piracy opcode\n"\ @@ -122,26 +119,25 @@ void os_process_arguments(int argc, char *argv[]) /* Parse the options */ do { - c = zgetopt(argc, argv, "aAiI:oOPQs:S:tu:xZ:"); + c = zgetopt(argc, argv, "aAi:oOPQs:S:tu:xZ:"); switch (c) { - case 'a': f_setup.attribute_assignment = 1; break; - case 'A': f_setup.attribute_testing = 1; break; - case 'i': f_setup.ignore_errors = 1; break; - case 'I': f_setup.interpreter_number = atoi(zoptarg); break; - case 'o': f_setup.object_movement = 1; break; - case 'O': f_setup.object_locating = 1; break; - case 'P': f_setup.piracy = 1; break; - case 'Q': f_setup.save_quetzal = 0; break; + case 'a': option_attribute_assignment = 1; break; + case 'A': option_attribute_testing = 1; break; + case 'i': option_ignore_errors = 1; break; + case 'o': option_object_movement = 1; break; + case 'O': option_object_locating = 1; break; + case 'P': option_piracy = 1; break; + case 'Q': option_save_quetzal = 0; break; case 's': user_random_seed = atoi(zoptarg); break; - case 'S': f_setup.script_cols = atoi(zoptarg); break; + case 'S': option_script_cols = atoi(zoptarg); break; case 't': user_tandy_bit = 1; break; - case 'u': f_setup.undo_slots = atoi(zoptarg); break; - case 'x': f_setup.expand_abbreviations = 1; break; - case 'Z': f_setup.err_report_mode = atoi(zoptarg); - if ((f_setup.err_report_mode < ERR_REPORT_NEVER) || - (f_setup.err_report_mode > ERR_REPORT_FATAL)) - f_setup.err_report_mode = ERR_DEFAULT_REPORT_MODE; + case 'u': option_undo_slots = atoi(zoptarg); break; + case 'x': option_expand_abbreviations = 1; break; + case 'Z': option_err_report_mode = atoi(zoptarg); + if ((option_err_report_mode < ERR_REPORT_NEVER) || + (option_err_report_mode > ERR_REPORT_FATAL)) + option_err_report_mode = ERR_DEFAULT_REPORT_MODE; break; } } while (c != EOF); @@ -152,7 +148,7 @@ void os_process_arguments(int argc, char *argv[]) char buf[256]; win = glk_window_open(0, 0, 0, wintype_TextBuffer, 0); glk_set_window(win); - glk_put_string("FROTZ V" VERSION " -- Glk 0.6.1 interface.\n"); + glk_put_string("FROTZ V" VERSION " -- Glk 0.7.0 interface.\n"); glk_put_string(INFORMATION); sprintf(buf, " -Z # error checking mode (default = %d)\n" @@ -189,8 +185,57 @@ void os_init_screen(void) * Init glk stuff */ - glk_stylehint_set(wintype_TextGrid, style_User1, stylehint_ReverseColor, 1); - + /* monor */ + glk_stylehint_set(wintype_AllTypes, style_Preformatted, stylehint_Proportional, 0); + glk_stylehint_set(wintype_AllTypes, style_Preformatted, stylehint_Weight, 0); + glk_stylehint_set(wintype_AllTypes, style_Preformatted, stylehint_Oblique, 0); + glk_stylehint_set(wintype_TextGrid, style_Preformatted, stylehint_ReverseColor, 1); + + /* monob */ + glk_stylehint_set(wintype_AllTypes, style_Subheader, stylehint_Proportional, 0); + glk_stylehint_set(wintype_AllTypes, style_Subheader, stylehint_Weight, 1); + glk_stylehint_set(wintype_AllTypes, style_Subheader, stylehint_Oblique, 0); + glk_stylehint_set(wintype_TextGrid, style_Subheader, stylehint_ReverseColor, 1); + + /* monoi */ + glk_stylehint_set(wintype_AllTypes, style_Alert, stylehint_Proportional, 0); + glk_stylehint_set(wintype_AllTypes, style_Alert, stylehint_Weight, 0); + glk_stylehint_set(wintype_AllTypes, style_Alert, stylehint_Oblique, 1); + glk_stylehint_set(wintype_TextGrid, style_Alert, stylehint_ReverseColor, 1); + + /* monoz */ + glk_stylehint_set(wintype_AllTypes, style_BlockQuote, stylehint_Proportional, 0); + glk_stylehint_set(wintype_AllTypes, style_BlockQuote, stylehint_Weight, 1); + glk_stylehint_set(wintype_AllTypes, style_BlockQuote, stylehint_Oblique, 1); + glk_stylehint_set(wintype_TextGrid, style_BlockQuote, stylehint_ReverseColor, 1); + + /* propr */ + glk_stylehint_set(wintype_TextBuffer, style_Normal, stylehint_Proportional, 1); + glk_stylehint_set(wintype_TextGrid, style_Normal, stylehint_Proportional, 0); + glk_stylehint_set(wintype_AllTypes, style_Normal, stylehint_Weight, 0); + glk_stylehint_set(wintype_AllTypes, style_Normal, stylehint_Oblique, 0); + glk_stylehint_set(wintype_TextGrid, style_Normal, stylehint_ReverseColor, 1); + + /* propb */ + glk_stylehint_set(wintype_TextBuffer, style_Header, stylehint_Proportional, 1); + glk_stylehint_set(wintype_TextGrid, style_Header, stylehint_Proportional, 0); + glk_stylehint_set(wintype_AllTypes, style_Header, stylehint_Weight, 1); + glk_stylehint_set(wintype_AllTypes, style_Header, stylehint_Oblique, 0); + glk_stylehint_set(wintype_TextGrid, style_Header, stylehint_ReverseColor, 1); + + /* propi */ + glk_stylehint_set(wintype_TextBuffer, style_Emphasized, stylehint_Proportional, 1); + glk_stylehint_set(wintype_TextGrid, style_Emphasized, stylehint_Proportional, 0); + glk_stylehint_set(wintype_AllTypes, style_Emphasized, stylehint_Weight, 0); + glk_stylehint_set(wintype_AllTypes, style_Emphasized, stylehint_Oblique, 1); + glk_stylehint_set(wintype_TextGrid, style_Emphasized, stylehint_ReverseColor, 1); + + /* propi */ + glk_stylehint_set(wintype_TextBuffer, style_Note, stylehint_Proportional, 1); + glk_stylehint_set(wintype_TextGrid, style_Note, stylehint_Proportional, 0); + glk_stylehint_set(wintype_AllTypes, style_Note, stylehint_Weight, 1); + glk_stylehint_set(wintype_AllTypes, style_Note, stylehint_Oblique, 1); + glk_stylehint_set(wintype_TextGrid, style_Note, stylehint_ReverseColor, 1); gos_lower = glk_window_open(0, 0, 0, wintype_TextGrid, 0); if (!gos_lower) @@ -224,7 +269,7 @@ void os_init_screen(void) if (h_version >= V4) h_config |= CONFIG_BOLDFACE | CONFIG_EMPHASIS | - CONFIG_FIXED | CONFIG_TIMEDINPUT; + CONFIG_FIXED | CONFIG_TIMEDINPUT | CONFIG_COLOUR; if (h_version >= V5) h_flags &= ~(GRAPHICS_FLAG | MOUSE_FLAG | MENU_FLAG); @@ -235,11 +280,11 @@ void os_init_screen(void) if ((h_version == 3) && (h_flags & OLD_SOUND_FLAG)) h_flags |= OLD_SOUND_FLAG; - if ((h_version == 6) && (f_setup.sound != 0)) + if ((h_version == 6) && (option_sound != 0)) h_config |= CONFIG_SOUND; if (h_version >= V5 && (h_flags & UNDO_FLAG)) - if (f_setup.undo_slots == 0) + if (option_undo_slots == 0) h_flags &= ~UNDO_FLAG; h_screen_cols = width; @@ -259,8 +304,6 @@ void os_init_screen(void) /* Use the ms-dos interpreter number for v6, because that's the * kind of graphics files we understand. Otherwise, use DEC. */ h_interpreter_number = h_version == 6 ? INTERP_MSDOS : INTERP_DEC_20; - if (f_setup.interpreter_number > 0) - h_interpreter_number = f_setup.interpreter_number; h_interpreter_version = 'F'; { /* Set these per spec 8.3.2. */ @@ -280,37 +323,39 @@ int os_random_seed (void) void os_restart_game (int stage) {} -void os_fatal (char *s) +void os_fatal (const char *s) { + char err[256]; + sprintf(err,"%s",s); + if (!gos_lower) gos_lower = glk_window_open(0, 0, 0, wintype_TextBuffer, 0); glk_set_window(gos_lower); glk_set_style(style_Normal); glk_put_string("\n\nFatal error: "); - glk_put_string(s); + glk_put_string(err); glk_put_string("\n"); glk_exit(); } void os_init_setup(void) { - f_setup.attribute_assignment = 0; - f_setup.attribute_testing = 0; - f_setup.context_lines = 0; - f_setup.object_locating = 0; - f_setup.object_movement = 0; - f_setup.left_margin = 0; - f_setup.right_margin = 0; - f_setup.ignore_errors = 0; - f_setup.interpreter_number = 0; - f_setup.piracy = 0; - f_setup.undo_slots = MAX_UNDO_SLOTS; - f_setup.expand_abbreviations = 0; - f_setup.script_cols = 80; - f_setup.save_quetzal = 1; - f_setup.sound = 1; - f_setup.err_report_mode = ERR_DEFAULT_REPORT_MODE; + option_attribute_assignment = 0; + option_attribute_testing = 0; + option_context_lines = 0; + option_object_locating = 0; + option_object_movement = 0; + option_left_margin = 0; + option_right_margin = 0; + option_ignore_errors = 0; + option_piracy = 0; + option_undo_slots = MAX_UNDO_SLOTS; + option_expand_abbreviations = 0; + option_script_cols = 80; + option_save_quetzal = 1; + option_sound = 1; + option_err_report_mode = ERR_DEFAULT_REPORT_MODE; } void gos_cancel_pending_line(void) @@ -329,7 +374,7 @@ zchar os_read_key (int timeout, bool show_cursor) if (gos_linepending) gos_cancel_pending_line(); - glk_request_char_event(win); + glk_request_char_event_uni(win); if (timeout != 0) glk_request_timer_events(timeout * 100); @@ -383,7 +428,7 @@ zchar os_read_line (int max, zchar *buf, int timeout, int width, int continued) if (!continued || !gos_linepending) { - glk_request_line_event(win, buf, max, strlen(buf)); + glk_request_line_event_uni(win, buf, max, os_string_length(buf)); if (timeout != 0) glk_request_timer_events(timeout * 100); } @@ -424,6 +469,16 @@ zword os_read_mouse(void) return 0; } +void os_scrollback_char (zchar z) +{ + /* NOT IMPLEMENTED */ +} + +void os_scrollback_erase (int amount) +{ + /* NOT IMPLEMENTED */ +} + static glui32 flag2usage(int flag) { switch (flag) @@ -468,6 +523,9 @@ static glui32 flag2mode(int flag) return filemode_ReadWrite; } +frefid_t script_fref = NULL; +int script_exists = 0; + strid_t frotzopenprompt(int flag) { frefid_t fref; @@ -481,11 +539,37 @@ strid_t frotzopenprompt(int flag) stm = glk_stream_open_file(fref, gmode, 0); + if (flag == FILE_SCRIPT) + { + if (script_fref) + glk_fileref_destroy(script_fref); + script_fref = glk_fileref_create_from_fileref(gusage, fref, 0); + script_exists = (script_fref != NULL); + } + glk_fileref_destroy(fref); return stm; } +strid_t frotzreopen(int flag) +{ + frefid_t fref; + + if (flag == FILE_SCRIPT && script_exists) + fref = script_fref; + else + return NULL; + + strid_t stm; + glui32 gusage = flag2usage(flag); + glui32 gmode = flag2mode(flag); + + stm = glk_stream_open_file(fref, gmode, 0); + + return stm; +} + strid_t frotzopen(char *filename, int flag) { frefid_t fref; diff --git a/interpreters/frotz/glkscreen.c b/interpreters/frotz/glkscreen.c index 37ef562..f7c8e08 100644 --- a/interpreters/frotz/glkscreen.c +++ b/interpreters/frotz/glkscreen.c @@ -1,6 +1,7 @@ /****************************************************************************** * * * Copyright (C) 2006-2009 by Tor Andersson. * + * Copyright (C) 2010 by Ben Cressey, Chris Spiegel. * * * * This file is part of Gargoyle. * * * @@ -27,16 +28,18 @@ #include "glkfrotz.h" -static unsigned char statusline[256]; +static zchar statusline[256]; static int oldstyle = 0; static int curstyle = 0; -static int upperstyle = 0; -static int lowerstyle = 0; static int cury = 1; static int curx = 1; +static int fixforced = 0; -int curr_fg = 0; -int curr_bg = 0; +static int curr_fg = -2; +static int curr_bg = -2; +static int curr_font = 1; +static int prev_font = 1; +static int temp_font = 0; /* To make the common code happy */ @@ -57,6 +60,13 @@ int os_string_width (const zchar *s) return width; } +int os_string_length (zchar *s) +{ + int length = 0; + while (*s++) length++; + return length; +} + void os_prepare_sample (int a) { glk_sound_load_hint(a, 1); @@ -155,19 +165,26 @@ void reset_status_ht(void) { glk_window_get_size(gos_upper, NULL, &height); if (mach_status_ht != height) + { glk_window_set_arrangement( glk_window_get_parent(gos_upper), winmethod_Above | winmethod_Fixed, mach_status_ht, NULL); + } } } -void erase_window (int w) +void erase_window (zword w) { if (w == 0) glk_window_clear(gos_lower); else if (gos_upper) { +#ifdef GARGLK + garglk_set_reversevideo_stream( + glk_window_get_stream(gos_upper), + TRUE); +#endif /* GARGLK */ memset(statusline, ' ', sizeof statusline); glk_window_clear(gos_upper); reset_status_ht(); @@ -175,7 +192,7 @@ void erase_window (int w) } } -void split_window (int lines) +void split_window (zword lines) { if (!gos_upper) return; @@ -219,11 +236,13 @@ void restart_screen (void) * so ... split status text into regions, reformat and print anew. */ -void packspaces(unsigned char *src, unsigned char *dst) +void packspaces(zchar *src, zchar *dst) { int killing = 0; while (*src) { + if (*src == 0x20202020) + *src = ' '; if (*src == ' ') killing++; else @@ -238,17 +257,17 @@ void packspaces(unsigned char *src, unsigned char *dst) void smartstatusline (void) { - unsigned char packed[256]; - unsigned char buf[256]; - unsigned char *a, *b, *c, *d; + zchar packed[256]; + zchar buf[256]; + zchar *a, *b, *c, *d; int roomlen, scorelen, scoreofs; - int len; + int len, tmp; statusline[curx - 1] = 0; /* terminate! */ packspaces(statusline, packed); //strcpy(packed, statusline); - len = strlen(packed); + len = os_string_length(packed); a = packed; while (a[0] == ' ') @@ -279,15 +298,16 @@ void smartstatusline (void) if (scoreofs <= roomlen) scoreofs = roomlen + 2; - memset(buf, ' ', h_screen_cols); - memcpy(buf + 1 + scoreofs, c, scorelen); - memcpy(buf + 1, a, roomlen); + for (tmp = 0; tmp < h_screen_cols; tmp++) + buf[tmp] = ' '; + + memcpy(buf + 1 + scoreofs, c, scorelen * sizeof(zchar)); + memcpy(buf + 1, a, roomlen * sizeof(zchar)); //if (roomlen >= scoreofs) // buf[roomlen + 1] = '|'; glk_window_move_cursor(gos_upper, 0, 0); - glk_set_style(style_User1); - glk_put_buffer(buf, h_screen_cols); + glk_put_buffer_uni(buf, h_screen_cols); glk_window_move_cursor(gos_upper, cury - 1, curx - 1); } @@ -306,16 +326,19 @@ void screen_char (zchar c) } /* check fixed flag in header, game can change it at whim */ - if (gos_curwin == gos_lower) + int forcefix = ((h_flags & FIXED_FONT_FLAG) != 0); + int curfix = ((curstyle & FIXED_WIDTH_STYLE) != 0); + if (forcefix && !curfix) { - static int forcefix = -1; - int curfix = h_flags & FIXED_FONT_FLAG; - if (forcefix != curfix) - { - forcefix = curfix; - zargs[0] = 0xf000; /* tickle tickle! */ - z_set_text_style(); - } + zargs[0] = 0xf000; /* tickle tickle! */ + z_set_text_style(); + fixforced = TRUE; + } + else if (!forcefix && fixforced) + { + zargs[0] = 0xf000; /* tickle tickle! */ + z_set_text_style(); + fixforced = FALSE; } if (gos_upper && gos_curwin == gos_upper) @@ -332,13 +355,13 @@ void screen_char (zchar c) statusline[curx - 1] = c; curx++; if (curx <= h_screen_cols) - glk_put_char(c); + glk_put_char_uni(c); else smartstatusline(); } else { - glk_put_char(c); + glk_put_char_uni(c); curx++; if (curx > h_screen_cols) { curx = 1; @@ -351,7 +374,7 @@ void screen_char (zchar c) { if (c == ZC_RETURN) glk_put_char('\n'); - else glk_put_char(c); + else glk_put_char_uni(c); } } @@ -405,6 +428,18 @@ void z_buffer_mode (void) { } +/* + * z_buffer_screen, set the screen buffering mode. + * + * zargs[0] = mode + * + */ + +void z_buffer_screen (void) +{ + store (0); +} + /* * z_erase_line, erase the line starting at the cursor position. * @@ -527,6 +562,59 @@ void z_print_table (void) } } +#define zB(i) ((((i >> 10) & 0x1F) << 3) | (((i >> 10) & 0x1F) >> 2)) +#define zG(i) ((((i >> 5) & 0x1F) << 3) | (((i >> 5) & 0x1F) >> 2)) +#define zR(i) ((((i ) & 0x1F) << 3) | (((i ) & 0x1F) >> 2)) + +#define zRGB(i) (zR(i) << 16 | zG(i) << 8 | zB(i)) + +/* + * z_set_true_colour, set the foreground and background colours + * to specific RGB colour values. + * + * zargs[0] = foreground colour + * zargs[1] = background colour + * zargs[2] = window (-3 is the current one, optional) + * + */ + +void z_set_true_colour (void) +{ + int zfore = zargs[0]; + int zback = zargs[1]; + + if (!(zfore < 0)) + zfore = zRGB(zargs[0]); + + if (!(zback < 0)) + zback = zRGB(zargs[1]); + +#ifdef GARGLK + garglk_set_zcolors(zfore, zback); +#endif /* GARGLK */ + + curr_fg = zfore; + curr_bg = zback; +} + +static int zcolor_map[] = { + -2, /* 0 = current */ + -1, /* 1 = default */ + 0x0000, /* 2 = black */ + 0x001D, /* 3 = red */ + 0x0340, /* 4 = green */ + 0x03BD, /* 5 = yellow */ + 0x59A0, /* 6 = blue */ + 0x7C1F, /* 7 = magenta */ + 0x77A0, /* 8 = cyan */ + 0x7FFF, /* 9 = white */ + 0x5AD6, /* 10 = light grey */ + 0x4631, /* 11 = medium grey */ + 0x2D6B, /* 12 = dark grey */ +}; + +#define zcolor_NUMCOLORS (13) + /* * z_set_colour, set the foreground and background colours. * @@ -541,12 +629,41 @@ void z_set_colour (void) int zfore = zargs[0]; int zback = zargs[1]; + switch (zfore) + { + case -1: + zfore = -3; + + case 0: + case 1: + zfore = zcolor_map[zfore]; + break; + + default: + if (zfore < zcolor_NUMCOLORS) + zfore = zRGB(zcolor_map[zfore]); + break; + } + + switch (zback) + { + case -1: + zback = -3; + + case 0: + case 1: + zback = zcolor_map[zback]; + break; + + default: + if (zback < zcolor_NUMCOLORS) + zback = zRGB(zcolor_map[zback]); + break; + } - if (!(zfore == 0 && zback == 0)) { #ifdef GARGLK - garglk_set_zcolors(zfore, zback); + garglk_set_zcolors(zfore, zback); #endif /* GARGLK */ - } curr_fg = zfore; curr_bg = zback; @@ -561,6 +678,41 @@ void z_set_colour (void) void z_set_font (void) { + zword font = zargs[0]; + + switch (font) + { + case 0: /* previous font */ + temp_font = curr_font; + curr_font = prev_font; + prev_font = temp_font; + zargs[0] = 0xf000; /* tickle tickle! */ + z_set_text_style(); + store (curr_font); + break; + + case 1: /* normal font */ + prev_font = curr_font; + curr_font = 1; + zargs[0] = 0xf000; /* tickle tickle! */ + z_set_text_style(); + store (prev_font); + break; + + case 4: /* fixed-pitch font*/ + prev_font = curr_font; + curr_font = 4; + zargs[0] = 0xf000; /* tickle tickle! */ + z_set_text_style(); + store (prev_font); + break; + + case 2: /* picture font, undefined per 1.1 */ + case 3: /* character graphics font */ + default: /* unavailable */ + store (0); + break; + } } /* @@ -603,7 +755,7 @@ void z_set_text_style (void) else if (zargs[0] != 0xf000) /* not tickle time */ curstyle |= zargs[0]; - if (h_flags & FIXED_FONT_FLAG) + if (h_flags & FIXED_FONT_FLAG || curr_font == 4) style = curstyle | FIXED_WIDTH_STYLE; else style = curstyle; @@ -613,29 +765,41 @@ void z_set_text_style (void) if (style & REVERSE_STYLE) { - if (gos_curwin == gos_upper && gos_upper) { - glk_set_style(style_User1); - } #ifdef GARGLK garglk_set_reversevideo(TRUE); #endif /* GARGLK */ } - else if (style & FIXED_WIDTH_STYLE) - glk_set_style(style_Preformatted); - else if (style & BOLDFACE_STYLE && style & EMPHASIS_STYLE) - glk_set_style(style_Alert); - else if (style & BOLDFACE_STYLE) - glk_set_style(style_Subheader); - else if (style & EMPHASIS_STYLE) - glk_set_style(style_Emphasized); + + if (style & FIXED_WIDTH_STYLE) + { + if (style & BOLDFACE_STYLE && style & EMPHASIS_STYLE) + glk_set_style(style_BlockQuote); /* monoz */ + else if (style & EMPHASIS_STYLE) + glk_set_style(style_Alert); /* monoi */ + else if (style & BOLDFACE_STYLE) + glk_set_style(style_Subheader); /* monob */ + else + glk_set_style(style_Preformatted); /* monor */ + } else - glk_set_style(style_Normal); + { + if (style & BOLDFACE_STYLE && style & EMPHASIS_STYLE) + glk_set_style(style_Note); /* propz */ + else if (style & EMPHASIS_STYLE) + glk_set_style(style_Emphasized); /* propi */ + else if (style & BOLDFACE_STYLE) + glk_set_style(style_Header); /* propb */ + else + glk_set_style(style_Normal); /* propr */ + } - if (curstyle == 0) { + if (curstyle == 0) + { #ifdef GARGLK garglk_set_reversevideo(FALSE); #endif /* GARGLK */ } + } /* @@ -649,29 +813,25 @@ void z_set_window (void) { int win = zargs[0]; - if (gos_curwin == gos_lower) - lowerstyle = curstyle; - else - upperstyle = curstyle; - if (win == 0) { glk_set_window(gos_lower); gos_curwin = gos_lower; - curstyle = lowerstyle; } else { if (gos_upper) glk_set_window(gos_upper); gos_curwin = gos_upper; - curstyle = upperstyle; } - if (win == 0) - enable_scripting = TRUE; - else - enable_scripting = FALSE; + if (win == 0) + enable_scripting = TRUE; + else + enable_scripting = FALSE; + + zargs[0] = 0xf000; /* tickle tickle! */ + z_set_text_style(); } /* @@ -724,7 +884,6 @@ void z_show_status (void) curx = cury = 1; glk_window_move_cursor(gos_upper, 0, 0); - glk_set_style(style_User1); /* If the screen width is below 55 characters then we have to use the brief status line format */ diff --git a/interpreters/frotz/input.c b/interpreters/frotz/input.c index 975830c..f17ec66 100644 --- a/interpreters/frotz/input.c +++ b/interpreters/frotz/input.c @@ -22,10 +22,11 @@ extern int save_undo (void); -extern zchar stream_read_key (zword, zword); -extern zchar stream_read_input (int, zchar *, zword, zword, bool); +extern zchar stream_read_key (zword, zword, bool); +extern zchar stream_read_input (int, zchar *, zword, zword, bool, bool); extern void tokenise_line (zword, zword, zword, bool); +zchar unicode_tolower (zchar); /* * is_terminator @@ -77,7 +78,7 @@ void z_make_menu (void) /* This opcode was only used for the Macintosh version of Journey. It controls menus with numbers greater than 2 (menus 0, 1 and 2 - are system menus). Frotz doesn't implement menus yet. */ + are system menus). */ branch (FALSE); @@ -97,7 +98,7 @@ bool read_yes_or_no (const char *s) print_string (s); print_string ("? (y/n) >"); - key = stream_read_key (0, 0); + key = stream_read_key (0, 0, FALSE); if (key == 'y' || key == 'Y') { print_string ("y\n"); @@ -124,7 +125,7 @@ void read_string (int max, zchar *buffer) do { - key = stream_read_input (max, buffer, 0, 0, FALSE); + key = stream_read_input (max, buffer, 0, 0, FALSE, FALSE); } while (key != ZC_RETURN); @@ -217,6 +218,7 @@ void z_read (void) max, buffer, /* buffer and size */ zargs[2], /* timeout value */ zargs[3], /* timeout routine */ + FALSE, /* enable hot keys */ h_version == V6); /* no script in V6 */ if (key == ZC_BAD) @@ -233,10 +235,7 @@ void z_read (void) if (key == ZC_RETURN) { - if (buffer[i] >= 'A' && buffer[i] <= 'Z') - buffer[i] += 'a' - 'A'; - if (buffer[i] >= 0xc0 && buffer[i] <= 0xde && buffer[i] != 0xd7) - buffer[i] += 0x20; + buffer[i] = unicode_tolower (buffer[i]); } @@ -285,7 +284,8 @@ void z_read_char (void) key = stream_read_key ( zargs[1], /* timeout value */ - zargs[2]); /* timeout routine */ + zargs[2], /* timeout routine */ + FALSE); /* enable hot keys */ if (key == ZC_BAD) return; @@ -296,3 +296,27 @@ void z_read_char (void) }/* z_read_char */ +/* + * z_read_mouse, write the current mouse status into a table. + * + * zargs[0] = address of table + * + */ + +void z_read_mouse (void) +{ + zword btn; + + /* Read the mouse position, the last menu click + and which buttons are down */ + + btn = os_read_mouse (); + hx_mouse_y = mouse_y; + hx_mouse_x = mouse_x; + + storew ((zword) (zargs[0] + 0), hx_mouse_y); + storew ((zword) (zargs[0] + 2), hx_mouse_x); + storew ((zword) (zargs[0] + 4), btn); /* mouse button bits */ + storew ((zword) (zargs[0] + 6), menu_selected); /* menu selection */ + +}/* z_read_mouse */ diff --git a/interpreters/frotz/main.c b/interpreters/frotz/main.c index a403c13..4fbbd1e 100644 --- a/interpreters/frotz/main.c +++ b/interpreters/frotz/main.c @@ -34,6 +34,9 @@ extern void interpret (void); extern void init_memory (void); +extern void init_proc (void); +extern void init_sound (void); +extern void init_text (void); extern void init_undo (void); extern void reset_memory (void); @@ -75,7 +78,7 @@ zbyte h_default_foreground = 0; zword h_terminating_keys = 0; zword h_line_width = 0; zbyte h_standard_high = 1; -zbyte h_standard_low = 0; +zbyte h_standard_low = 1; zword h_alphabet = 0; zword h_extension_table = 0; zbyte h_user_name[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -84,6 +87,9 @@ zword hx_table_size = 0; zword hx_mouse_x = 0; zword hx_mouse_y = 0; zword hx_unicode_table = 0; +zword hx_flags = 0; +zword hx_fore_colour = 0; +zword hx_back_colour = 0; /* Stack data */ @@ -108,6 +114,7 @@ int mwin = 0; int mouse_y = 0; int mouse_x = 0; +int menu_selected = 0; /* Window attributes */ @@ -118,7 +125,6 @@ bool enable_buffering = FALSE; /* User options */ -/* int option_attribute_assignment = 0; int option_attribute_testing = 0; int option_context_lines = 0; @@ -132,7 +138,7 @@ int option_undo_slots = MAX_UNDO_SLOTS; int option_expand_abbreviations = 0; int option_script_cols = 80; int option_save_quetzal = 1; -*/ +int option_err_report_mode = ERR_DEFAULT_REPORT_MODE; int option_sound = 1; char *option_zcode_path; @@ -152,7 +158,7 @@ long reserve_mem = 0; void z_piracy (void) { - branch (!f_setup.piracy); + branch (!option_piracy); }/* z_piracy */ @@ -180,7 +186,6 @@ glkunix_argumentlist_t glkunix_arguments[] = { "-Q", glkunix_arg_NoValue, "-Q: use old-style save format" }, { "-t", glkunix_arg_NoValue, "-t: set Tandy bit" }, { "-x", glkunix_arg_NoValue, "-x: expand abbreviations g/x/z" }, -{ "-I", glkunix_arg_NumberValue, "-I: interpreter number" }, { "-s", glkunix_arg_NumberValue, "-s: random number seed value" }, { "-S", glkunix_arg_NumberValue, "-S: transcript width" }, { "-u", glkunix_arg_NumberValue, "-u: slots for multiple undo" }, @@ -191,8 +196,8 @@ glkunix_argumentlist_t glkunix_arguments[] = int glkunix_startup_code(glkunix_startup_t *data) { - myargc = data->argc; - myargv = data->argv; + myargc = data->argc; + myargv = data->argv; os_init_setup (); os_process_arguments (myargc, myargv); @@ -200,8 +205,9 @@ int glkunix_startup_code(glkunix_startup_t *data) init_buffer (); init_err (); init_memory (); - init_process (); + init_proc (); init_sound (); + init_text (); os_init_screen (); diff --git a/interpreters/frotz/object.c b/interpreters/frotz/object.c index 22ffd08..a1e86e4 100644 --- a/interpreters/frotz/object.c +++ b/interpreters/frotz/object.c @@ -1,5 +1,5 @@ /* object.c - Object manipulation opcodes - * Copyright (c) 1995-1997 Stefan Jokisch + * Copyright (c) 1995-1997 Stefan Jokisch * * This file is part of Frotz. * @@ -43,8 +43,6 @@ static zword object_address (zword obj) { -/* zchar obj_num[10]; */ - /* Check object number */ if (obj > ((h_version <= V3) ? 255 : MAX_OBJECT)) { @@ -279,7 +277,7 @@ void z_clear_attr (void) /* If we are monitoring attribute assignment display a short note */ - if (f_setup.attribute_assignment) { + if (option_attribute_assignment) { stream_mssg_on (); print_string ("@clear_attr "); print_object (zargs[0]); @@ -319,7 +317,7 @@ void z_jin (void) /* If we are monitoring object locating display a short note */ - if (f_setup.object_locating) { + if (option_object_locating) { stream_mssg_on (); print_string ("@jin "); print_object (zargs[0]); @@ -379,7 +377,7 @@ void z_get_child (void) /* If we are monitoring object locating display a short note */ - if (f_setup.object_locating) { + if (option_object_locating) { stream_mssg_on (); print_string ("@get_child "); print_object (zargs[0]); @@ -491,7 +489,7 @@ void z_get_parent (void) /* If we are monitoring object locating display a short note */ - if (f_setup.object_locating) { + if (option_object_locating) { stream_mssg_on (); print_string ("@get_parent "); print_object (zargs[0]); @@ -762,7 +760,7 @@ void z_insert_obj (void) /* If we are monitoring object movements display a short note */ - if (f_setup.object_movement) { + if (option_object_movement) { stream_mssg_on (); print_string ("@move_obj "); print_object (obj1); @@ -888,7 +886,7 @@ void z_remove_obj (void) /* If we are monitoring object movements display a short note */ - if (f_setup.object_movement) { + if (option_object_movement) { stream_mssg_on (); print_string ("@remove_obj "); print_object (zargs[0]); @@ -923,7 +921,7 @@ void z_set_attr (void) /* If we are monitoring attribute assignment display a short note */ - if (f_setup.attribute_assignment) { + if (option_attribute_assignment) { stream_mssg_on (); print_string ("@set_attr "); print_object (zargs[0]); @@ -973,7 +971,7 @@ void z_test_attr (void) /* If we are monitoring attribute testing display a short note */ - if (f_setup.attribute_testing) { + if (option_attribute_testing) { stream_mssg_on (); print_string ("@test_attr "); print_object (zargs[0]); diff --git a/interpreters/frotz/process.c b/interpreters/frotz/process.c index e79edf5..58c8607 100644 --- a/interpreters/frotz/process.c +++ b/interpreters/frotz/process.c @@ -138,7 +138,7 @@ void (*var_opcodes[0x40]) (void) = { z_check_arg_count }; -void (*ext_opcodes[0x1d]) (void) = { +void (*ext_opcodes[0x1e]) (void) = { z_save, z_restore, z_log_shift, @@ -152,7 +152,7 @@ void (*ext_opcodes[0x1d]) (void) = { z_restore_undo, z_print_unicode, z_check_unicode, - __illegal__, + z_set_true_colour, /* spec 1.1 */ __illegal__, __illegal__, __illegal__, // glkify - z_move_window, @@ -168,20 +168,21 @@ void (*ext_opcodes[0x1d]) (void) = { z_print_form, z_make_menu, __illegal__, // glkify - z_picture_table + z_buffer_screen, /* spec 1.1 */ }; /* - * init_process + * init_proc * * Initialize process variables. * */ -void init_process (void) +void init_proc (void) { finished = 0; -} /* init_process */ +} /* init_proc */ /* @@ -337,7 +338,7 @@ void call (zword routine, int argc, zword *args, int ct) *--sp = (zword) (pc >> 9); *--sp = (zword) (pc & 0x1ff); *--sp = (zword) (fp - stack - 1); - *--sp = (zword) (argc | (ct << (f_setup.save_quetzal ? 12 : 8))); + *--sp = (zword) (argc | (ct << (option_save_quetzal ? 12 : 8))); fp = sp; frame_count++; @@ -350,8 +351,12 @@ void call (zword routine, int argc, zword *args, int ct) pc = (long) routine << 2; else if (h_version <= V7) pc = ((long) routine << 2) + ((long) h_functions_offset << 3); - else /* h_version == V8 */ + else if (h_version <= V8) pc = (long) routine << 3; + else /* h_version == V9 */ { + long indirect = (long) routine << 2; + HIGH_LONG(indirect, pc); + } if (pc >= story_size) runtime_error (ERR_ILL_CALL_ADDR); @@ -367,7 +372,7 @@ void call (zword routine, int argc, zword *args, int ct) if (sp - stack < count) runtime_error (ERR_STK_OVF); - if (f_setup.save_quetzal) + if (option_save_quetzal) fp[0] |= (zword) count << 8; /* Save local var count for Quetzal. */ value = 0; @@ -408,7 +413,7 @@ void ret (zword value) sp = fp; - ct = *sp++ >> (f_setup.save_quetzal ? 12 : 8); + ct = *sp++ >> (option_save_quetzal ? 12 : 8); frame_count--; fp = stack + 1 + *sp++; pc = *sp++; @@ -573,7 +578,7 @@ static void __extended__ (void) load_all_operands (specifier); - if (opcode < 0x1d) /* extended opcodes from 0x1d on */ + if (opcode < 0x1e) /* extended opcodes from 0x1e on */ ext_opcodes[opcode] (); /* are reserved for future spec' */ }/* __extended__ */ @@ -602,7 +607,7 @@ static void __illegal__ (void) void z_catch (void) { - store (f_setup.save_quetzal ? frame_count : (zword) (fp - stack)); + store (option_save_quetzal ? frame_count : (zword) (fp - stack)); }/* z_catch */ @@ -617,7 +622,7 @@ void z_catch (void) void z_throw (void) { - if (f_setup.save_quetzal) { + if (option_save_quetzal) { if (zargs[1] > frame_count) runtime_error (ERR_BAD_FRAME); diff --git a/interpreters/frotz/setup.h b/interpreters/frotz/setup.h deleted file mode 100644 index a9b9360..0000000 --- a/interpreters/frotz/setup.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Various status thingies for the interpreter and interface. - * - */ - -typedef struct frotz_setup_struct { - int attribute_assignment; /* done */ - int attribute_testing; /* done */ - int context_lines; /* done */ - int object_locating; /* done */ - int object_movement; /* done */ - int left_margin; /* done */ - int right_margin; /* done */ - int ignore_errors; /* done */ - int interpreter_number; /* Just dumb frotz now */ - int piracy; /* done */ - int undo_slots; /* done */ - int expand_abbreviations; /* done */ - int script_cols; /* done */ - int save_quetzal; /* done */ - int sound; /* done */ - int err_report_mode; /* done */ -} f_setup_t; - -extern f_setup_t f_setup; - - -typedef struct zcode_header_struct { - zbyte h_version; - zbyte h_config; - zword h_release; - zword h_resident_size; - zword h_start_pc; - zword h_dictionary; - zword h_objects; - zword h_globals; - zword h_dynamic_size; - zword h_flags; - zbyte h_serial[6]; - zword h_abbreviations; - zword h_file_size; - zword h_checksum; - zbyte h_interpreter_number; - zbyte h_interpreter_version; - zbyte h_screen_rows; - zbyte h_screen_cols; - zword h_screen_width; - zword h_screen_height; - zbyte h_font_height; - zbyte h_font_width; - zword h_functions_offset; - zword h_strings_offset; - zbyte h_default_background; - zbyte h_default_foreground; - zword h_terminating_keys; - zword h_line_width; - zbyte h_standard_high; - zbyte h_standard_low; - zword h_alphabet; - zword h_extension_table; - zbyte h_user_name[8]; - - zword hx_table_size; - zword hx_mouse_x; - zword hx_mouse_y; - zword hx_unicode_table; -} z_header_t; diff --git a/interpreters/frotz/sound.c b/interpreters/frotz/sound.c index 26d3835..9c4d63a 100644 --- a/interpreters/frotz/sound.c +++ b/interpreters/frotz/sound.c @@ -31,8 +31,6 @@ extern int direct_call (zword); -static zword routine = 0; - static int next_sample = 0; static int next_volume = 0; @@ -75,7 +73,6 @@ static void start_sample (int number, int volume, int repeats, zword eos) os_start_sample (number, volume, repeats, eos); - routine = eos; playing = TRUE; }/* start_sample */ @@ -109,7 +106,7 @@ static void start_next_sample (void) * */ -void end_of_sound (void) +void end_of_sound (zword routine) { #if defined(DJGPP) && defined(SOUND_SUPPORT) @@ -148,9 +145,8 @@ void z_sound_effect (void) zword effect = zargs[1]; zword volume = zargs[2]; - /* By default play sound 1 at volume 8 */ if (zargc < 1) - number = 1; + number = 0; if (zargc < 2) effect = EFFECT_PLAY; if (zargc < 3) diff --git a/interpreters/frotz/stream.c b/interpreters/frotz/stream.c index c7be749..54cef78 100644 --- a/interpreters/frotz/stream.c +++ b/interpreters/frotz/stream.c @@ -30,6 +30,8 @@ zchar console_read_key (zword timeout) return os_read_key(timeout, 0); } +/* extern bool handle_hot_key (zword); */ + extern bool validate_click (void); extern void replay_open (void); @@ -60,13 +62,88 @@ extern void screen_erase_input (const zchar *); extern void screen_mssg_on (void); extern void screen_mssg_off (void); -extern zchar replay_read_key (void); -extern zchar replay_read_input (zchar *); -extern zchar console_read_key (zword); -extern zchar console_read_input (int, zchar *, zword, bool); +extern zword replay_read_key (void); +extern zword replay_read_input (zchar *); extern int direct_call (zword); +/* + * scrollback_char + * + * Write a single character to the scrollback buffer. + * + */ + +void scrollback_char (zchar c) +{ + + if (c == ZC_INDENT) + { scrollback_char (' '); scrollback_char (' '); scrollback_char (' '); return; } + if (c == ZC_GAP) + { scrollback_char (' '); scrollback_char (' '); return; } + + os_scrollback_char (c); + +}/* scrollback_char */ + +/* + * scrollback_word + * + * Write a string to the scrollback buffer. + * + */ + +void scrollback_word (const zchar *s) +{ + int i; + + for (i = 0; s[i] != 0; i++) + + if (s[i] == ZC_NEW_FONT || s[i] == ZC_NEW_STYLE) + i++; + else + scrollback_char (s[i]); + +}/* scrollback_word */ + +/* + * scrollback_write_input + * + * Send an input line to the scrollback buffer. + * + */ + +void scrollback_write_input (const zchar *buf, zchar key) +{ + int i; + + for (i = 0; buf[i] != 0; i++) + scrollback_char (buf[i]); + + if (key == ZC_RETURN) + scrollback_char ('\n'); + +}/* scrollback_write_input */ + +/* + * scrollback_erase_input + * + * Remove an input line from the scrollback buffer. + * + */ + +void scrollback_erase_input (const zchar *buf) +{ + int width; + int i; + + for (i = 0, width = 0; buf[i] != 0; i++) + width++; + + os_scrollback_erase (width); + +}/* scrollback_erase_input */ + /* * stream_mssg_on * @@ -160,6 +237,8 @@ void stream_char (zchar c) screen_char (c); if (ostream_script && enable_scripting) script_char (c); + if (enable_scripting) + scrollback_char (c); }/* stream_char */ @@ -183,7 +262,8 @@ void stream_word (const zchar *s) screen_word (s); if (ostream_script && enable_scripting) script_word (s); - + if (enable_scripting) + scrollback_word (s); } }/* stream_word */ @@ -208,6 +288,8 @@ void stream_new_line (void) screen_new_line (); if (ostream_script && enable_scripting) script_new_line (); + if (enable_scripting) + os_scrollback_char ('\n'); } @@ -239,7 +321,7 @@ void z_input_stream (void) * */ -zchar stream_read_key ( zword timeout, zword routine ) +zchar stream_read_key ( zword timeout, zword routine, bool hot_keys ) { zchar key = ZC_BAD; @@ -277,6 +359,21 @@ continue_input: if (direct_call (routine) == 0) goto continue_input; +/* glkify + // Handle hot keys + + if (hot_keys && key >= ZC_HKEY_MIN && key <= ZC_HKEY_MAX) { + + if (h_version == V4 && key == ZC_HKEY_UNDO) + goto continue_input; + if (!handle_hot_key (key)) + goto continue_input; + + return ZC_BAD; + + } +*/ + /* Return key */ return key; @@ -292,9 +389,14 @@ continue_input: zchar stream_read_input ( int max, zchar *buf, zword timeout, zword routine, + bool hot_keys, bool no_scripting ) { zchar key = ZC_BAD; + bool no_scrollback = no_scripting; + + if (h_version == V6 && story_id == UNKNOWN && !ostream_script) + no_scrollback = FALSE; flush_buffer (); @@ -302,6 +404,8 @@ zchar stream_read_input ( int max, zchar *buf, if (ostream_script && enable_scripting && !no_scripting) script_erase_input (buf); +//glkify if (enable_scripting && !no_scrollback) +//glkify scrollback_erase_input (buf); //glkify if (istream_replay) //glkify screen_erase_input (buf); @@ -337,10 +441,25 @@ continue_input: if (direct_call (routine) == 0) goto continue_input; +/* glkify + // Handle hot keys + + if (hot_keys && key >= ZC_HKEY_MIN && key <= ZC_HKEY_MAX) { + + if (!handle_hot_key (key)) + goto continue_input; + + return ZC_BAD; + + } +*/ + /* Copy input line to transscript file or to the screen */ if (ostream_script && enable_scripting && !no_scripting) script_write_input (buf, key); +//glkify if (enable_scripting && !no_scrollback) +//glkify scrollback_write_input (buf, key); //glkify if (istream_replay) //glkify screen_write_input (buf, key); diff --git a/interpreters/frotz/text.c b/interpreters/frotz/text.c index 8145cfe..9cccc2c 100644 --- a/interpreters/frotz/text.c +++ b/interpreters/frotz/text.c @@ -25,9 +25,11 @@ enum string_type { }; extern zword object_name (zword); +extern zword get_window_font (zword); -static zchar decoded[10]; -static zword encoded[3]; +zchar* decoded; +zchar* encoded; +static int resolution; /* * According to Matteo De Luigi , @@ -35,21 +37,36 @@ static zword encoded[3]; * Sat Apr 21, 2001 */ static zchar zscii_to_latin1[] = { - 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, 0x00, 0x00, 0xa1, 0xbf + 0x0e4, 0x0f6, 0x0fc, 0x0c4, 0x0d6, 0x0dc, 0x0df, 0x0bb, + 0x0ab, 0x0eb, 0x0ef, 0x0ff, 0x0cb, 0x0cf, 0x0e1, 0x0e9, + 0x0ed, 0x0f3, 0x0fa, 0x0fd, 0x0c1, 0x0c9, 0x0cd, 0x0d3, + 0x0da, 0x0dd, 0x0e0, 0x0e8, 0x0ec, 0x0f2, 0x0f9, 0x0c0, + 0x0c8, 0x0cc, 0x0d2, 0x0d9, 0x0e2, 0x0ea, 0x0ee, 0x0f4, + 0x0fb, 0x0c2, 0x0ca, 0x0ce, 0x0d4, 0x0db, 0x0e5, 0x0c5, + 0x0f8, 0x0d8, 0x0e3, 0x0f1, 0x0f5, 0x0c3, 0x0d1, 0x0d5, + 0x0e6, 0x0c6, 0x0e7, 0x0c7, 0x0fe, 0x0f0, 0x0de, 0x0d0, + 0x0a3, 0x153, 0x152, 0x0a1, 0x0bf }; +/* + * init_text + * + * Initialize text variables. + * + */ + +void init_text (void) +{ + decoded = NULL; + encoded = NULL; + + resolution = 0; +} + /* * translate_from_zscii * - * Map a ZSCII character onto the ISO Latin-1 alphabet. + * Map a ZSCII character into Unicode. * */ @@ -78,7 +95,10 @@ zchar translate_from_zscii (zbyte c) LOW_WORD (addr, unicode) - return (unicode < 0x100) ? (zchar) unicode : '?'; + if (unicode < 0x20) + return '?'; + + return unicode; } else return '?'; @@ -86,36 +106,26 @@ zchar translate_from_zscii (zbyte c) if (c <= 0xdf) { - if (c == 0xdc || c == 0xdd) /* Oe and oe ligatures */ - return '?'; /* are not ISO-Latin 1 */ - return zscii_to_latin1[c - 0x9b]; } else return '?'; } - return c; + return (zchar) c; }/* translate_from_zscii */ /* - * translate_to_zscii + * unicode_to_zscii * - * Map an ISO Latin-1 character onto the ZSCII alphabet. + * Convert a Unicode character to ZSCII, returning 0 on failure. * */ -zbyte translate_to_zscii (zchar c) +zbyte unicode_to_zscii (zchar c) { int i; - if (c == ZC_SINGLE_CLICK) - return 0xfe; - if (c == ZC_DOUBLE_CLICK) - return 0xfd; - if (c == ZC_MENU_CLICK) - return 0xfc; - if (c >= ZC_LATIN1_MIN) { if (hx_unicode_table != 0) { /* game has its own Unicode table */ @@ -137,7 +147,7 @@ zbyte translate_to_zscii (zchar c) } - return '?'; + return 0; } else { /* game uses standard set */ @@ -145,16 +155,39 @@ zbyte translate_to_zscii (zchar c) if (c == zscii_to_latin1[i - 0x9b]) return (zbyte) i; - return '?'; + return 0; } } - if (c == 0) /* Safety thing from David Kinder */ - c = '?'; /* regarding his Unicode patches */ - /* Sept 15, 2002 */ + return (zbyte) c; - return c; +}/* unicode_to_zscii */ + +/* + * translate_to_zscii + * + * Map a Unicode character onto the ZSCII alphabet. + * + */ + +zbyte translate_to_zscii (zchar c) +{ + + if (c == ZC_SINGLE_CLICK) + return 0xfe; + if (c == ZC_DOUBLE_CLICK) + return 0xfd; + if (c == ZC_MENU_CLICK) + return 0xfc; + if (c == 0) + return 0; + + c = unicode_to_zscii (c); + if (c == 0) + c = '?'; + + return (zbyte) c; }/* translate_to_zscii */ @@ -167,6 +200,8 @@ zbyte translate_to_zscii (zchar c) static zchar alphabet (int set, int index) { + if (h_version > V1 && set == 2 && index == 1) + return 0x0D; /* always newline */ if (h_alphabet != 0) { /* game uses its own alphabet */ @@ -190,6 +225,66 @@ static zchar alphabet (int set, int index) }/* alphabet */ +/* + * find_resolution + * + * Find the number of bytes used for dictionary resolution. + * + */ + +static void find_resolution (void) +{ + zword dct = h_dictionary; + zword entry_count; + zbyte sep_count; + zbyte entry_len; + + LOW_BYTE (dct, sep_count) + dct += 1 + sep_count; /* skip word separators */ + LOW_BYTE (dct, entry_len) + dct += 1; /* skip entry length */ + LOW_WORD (dct, entry_count) + dct += 2; /* get number of entries */ + + if (h_version < V9) { + + resolution = (h_version <= V3) ? 2 : 3; + + } else { + + zword addr = dct; + zword code; + + if (entry_count == 0) { + + runtime_error (ERR_DICT_LEN); + + } + + /* check the first word in the dictionary */ + + do { + + LOW_WORD (addr, code) + addr += 2; + + } while (!(code & 0x8000) && (addr - dct < entry_len + 1)); + + resolution = (addr - dct) / 2; + + } + + if (2 * resolution > entry_len) { + + runtime_error (ERR_DICT_LEN); + + } + + decoded = (zchar *)malloc (sizeof (zchar) * (3 * resolution) + 1); + encoded = (zchar *)malloc (sizeof (zchar) * resolution); + +}/* find_resolution */ + /* * load_string * @@ -199,9 +294,10 @@ static zchar alphabet (int set, int index) static void load_string (zword addr, zword length) { - int resolution = (h_version <= V3) ? 2 : 3; int i = 0; + if (resolution == 0) find_resolution(); + while (i < 3 * resolution) if (i < length) { @@ -223,29 +319,34 @@ static void load_string (zword addr, zword length) * Encode the Unicode text in the global "decoded" string then write * the result to the global "encoded" array. (This is used to look up * words in the dictionary.) Up to V3 the vocabulary resolution is - * two, since V4 it is three words. Because each word contains three - * Z-characters, that makes six or nine Z-characters respectively. - * Longer words are chopped to the proper size, shorter words are are - * padded out with 5's. For word completion we pad with 0s and 31s, - * the minimum and maximum Z-characters. + * two, from V4 it is three, and from V9 it is any number of words. + * Because each word contains three Z-characters, that makes six or + * nine Z-characters respectively. Longer words are chopped to the + * proper size, shorter words are are padded out with 5's. For word + * completion we pad with 0s and 31s, the minimum and maximum + * Z-characters. * */ static void encode_text (int padding) { - static zchar again[] = { 'a', 'g', 'a', 'i', 'n', 0 }; - static zchar examine[] = { 'e', 'x', 'a', 'm', 'i', 'n', 'e', 0 }; - static zchar wait[] = { 'w', 'a', 'i', 't', 0 }; + static zchar again[] = { 'a', 'g', 'a', 'i', 'n', 0, 0, 0, 0 }; + static zchar examine[] = { 'e', 'x', 'a', 'm', 'i', 'n', 'e', 0, 0 }; + static zchar wait[] = { 'w', 'a', 'i', 't', 0, 0, 0, 0, 0 }; - zbyte zchars[12]; - const zchar *ptr = decoded; + zbyte *zchars; + const zchar *ptr; zchar c; - int resolution = (h_version <= V3) ? 2 : 3; int i = 0; + if (resolution == 0) find_resolution(); + + zchars = (zbyte *)malloc (sizeof (zbyte) * 3 * (resolution + 1)); + ptr = decoded; + /* Expand abbreviations that some old Infocom games lack */ - if (f_setup.expand_abbreviations) + if (option_expand_abbreviations && (h_version <= V8)) if (padding == 0x05 && decoded[1] == 0) @@ -264,6 +365,14 @@ static void encode_text (int padding) int index, set; zbyte c2; + if (c == 32) { + + zchars[i++] = 0; + + continue; + + } + /* Search character in the alphabet */ for (set = 0; set < 3; set++) @@ -304,10 +413,12 @@ static void encode_text (int padding) encoded[resolution - 1] |= 0x8000; + free (zchars); + }/* encode_text */ /* - * z_check_unicode, test if a unicode character can be read and printed. + * z_check_unicode, test if a unicode character can be printed (bit 0) and read (bit 1). * * zargs[0] = Unicode * @@ -316,15 +427,19 @@ static void encode_text (int padding) void z_check_unicode (void) { zword c = zargs[0]; + zword result = 0; - if (c >= 0x20 && c <= 0x7e) - store (3); - else if (c == 0xa0) - store (1); - else if (c >= 0xa1 && c <= 0xff) - store (3); + if (c <= 0x1f) + { + if ((c == 0x08) || (c == 0x0d) || (c == 0x1b)) + result = 2; + } + else if (c <= 0x7e) + result = 3; else - store (0); + result = 1; // we support unicode + + store (result); }/* z_check_unicode */ @@ -349,7 +464,7 @@ void z_encode_text (void) encode_text (0x05); - for (i = 0; i < 3; i++) + for (i = 0; i < resolution; i++) storew ((zword) (zargs[3] + 2 * i), encoded[i]); }/* z_encode_text */ @@ -391,6 +506,8 @@ static void decode_text (enum string_type st, zword addr) ptr = NULL; /* makes compilers shut up */ byte_addr = 0; + if (resolution == 0) find_resolution(); + /* Calculate the byte address if necessary */ if (st == ABBREVIATION) @@ -405,8 +522,12 @@ static void decode_text (enum string_type st, zword addr) byte_addr = (long) addr << 2; else if (h_version <= V7) byte_addr = ((long) addr << 2) + ((long) h_strings_offset << 3); - else /* h_version == V8 */ + else if (h_version <= V8) byte_addr = (long) addr << 3; + else /* h_version == V9 */ { + long indirect = (long) addr << 2; + HIGH_LONG(indirect, byte_addr); + } if (byte_addr >= story_size) runtime_error (ERR_ILL_PRINT_ADDR); @@ -439,6 +560,7 @@ static void decode_text (enum string_type st, zword addr) zword abbr_addr; zword ptr_addr; + zchar zc; c = (code >> i) & 0x1f; @@ -499,8 +621,30 @@ static void decode_text (enum string_type st, zword addr) case 3: /* ZSCII character - second part */ - c2 = translate_from_zscii ((prev_c << 5) | c); - outchar (c2); + zc = (prev_c << 5) | c; + + if (zc > 767) { /* Unicode escape */ + + while (zc-- > 767) { + + if (st == LOW_STRING || st == VOCABULARY) { + LOW_WORD (addr, c2) + addr += 2; + } else if (st == HIGH_STRING || st == ABBREVIATION) { + HIGH_WORD (byte_addr, c2) + byte_addr += 2; + } else + CODE_WORD (c2) + + outchar (c2 ^ 0xFFFF); + } + + } else { + + c2 = translate_from_zscii (zc); + outchar (c2); + + } status = 0; break; @@ -760,7 +904,10 @@ void print_string (const char *s) void z_print_unicode (void) { - print_char ((zargs[0] <= 0xff) ? zargs[0] : '?'); + if (zargs[0] < 0x20) + print_char ('?'); + else + print_char (zargs[0]); }/* z_print_unicode */ @@ -786,12 +933,13 @@ static zword lookup_text (int padding, zword dct) zword addr; zbyte entry_len; zbyte sep_count; - int resolution = (h_version <= V3) ? 2 : 3; int entry_number; int lower, upper; int i; bool sorted; + if (resolution == 0) find_resolution(); + encode_text (padding); LOW_BYTE (dct, sep_count) /* skip word separators */ @@ -1061,6 +1209,8 @@ int completion (const zchar *buffer, zchar *result) *result = 0; + if (resolution == 0) find_resolution(); + /* Copy last word to "decoded" string */ len = 0; @@ -1069,7 +1219,7 @@ int completion (const zchar *buffer, zchar *result) if (c != ' ') { - if (len < 9) + if (len < 3 * resolution) decoded[len++] = c; } else len = 0; @@ -1107,3 +1257,74 @@ int completion (const zchar *buffer, zchar *result) return (minaddr == maxaddr) ? 0 : 1; }/* completion */ + +/* + * unicode_tolower + * + * Convert a Unicode character to lowercase. + * Taken from Zip2000 by Kevin Bracey. + * + */ + +zchar unicode_tolower (zchar c) +{ + const static unsigned char tolower_basic_latin[0x100] = { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, + 0x40,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, + 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x5B,0x5C,0x5D,0x5E,0x5F, + 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, + 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F, + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xD7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xDF, + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF + }; + const static unsigned char tolower_latin_extended_a[0x80] = { + 0x01,0x01,0x03,0x03,0x05,0x05,0x07,0x07,0x09,0x09,0x0B,0x0B,0x0D,0x0D,0x0F,0x0F, + 0x11,0x11,0x13,0x13,0x15,0x15,0x17,0x17,0x19,0x19,0x1B,0x1B,0x1D,0x1D,0x1F,0x1F, + 0x21,0x21,0x23,0x23,0x25,0x25,0x27,0x27,0x29,0x29,0x2B,0x2B,0x2D,0x2D,0x2F,0x2F, + 0x00,0x31,0x33,0x33,0x35,0x35,0x37,0x37,0x38,0x3A,0x3A,0x3C,0x3C,0x3E,0x3E,0x40, + 0x40,0x42,0x42,0x44,0x44,0x46,0x46,0x48,0x48,0x49,0x4B,0x4B,0x4D,0x4D,0x4F,0x4F, + 0x51,0x51,0x53,0x53,0x55,0x55,0x57,0x57,0x59,0x59,0x5B,0x5B,0x5D,0x5D,0x5F,0x5F, + 0x61,0x61,0x63,0x63,0x65,0x65,0x67,0x67,0x69,0x69,0x6B,0x6B,0x6D,0x6D,0x6F,0x6F, + 0x71,0x71,0x73,0x73,0x75,0x75,0x77,0x77,0x00,0x7A,0x7A,0x7C,0x7C,0x7E,0x7E,0x7F + }; + const static unsigned char tolower_greek[0x50] = { + 0x80,0x81,0x82,0x83,0x84,0x85,0xAC,0x87,0xAD,0xAE,0xAF,0x8B,0xCC,0x8D,0xCD,0xCE, + 0x90,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, + 0xC0,0xC1,0xA2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xAC,0xAD,0xAE,0xAF, + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF + }; + const static unsigned char tolower_cyrillic[0x60] = { + 0x00,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, + 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, + 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, + 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F + }; + + if (c < 0x0100) + c = tolower_basic_latin[c]; + else if (c == 0x0130) + c = 0x0069; /* Capital I with dot -> lower case i */ + else if (c == 0x0178) + c = 0x00FF; /* Capital Y diaeresis -> lower case y diaeresis */ + else if (c < 0x0180) + c = tolower_latin_extended_a[c-0x100] + 0x100; + else if (c >= 0x380 && c < 0x3D0) + c = tolower_greek[c-0x380] + 0x300; + else if (c >= 0x400 && c < 0x460) + c = tolower_cyrillic[c-0x400] + 0x400; + + return c; +} + diff --git a/interpreters/git/Makefile.am b/interpreters/git/Makefile.am index c8641b0..8d5766d 100644 --- a/interpreters/git/Makefile.am +++ b/interpreters/git/Makefile.am @@ -1,7 +1,7 @@ # Automatically generate version.h MAJOR = 1 MINOR = 2 -PATCH = 4 +PATCH = 8 version.h: Makefile.am echo "// Automatically generated file -- do not edit!" > version.h echo "#define GIT_MAJOR" $(MAJOR) >> version.h diff --git a/interpreters/git/README.txt b/interpreters/git/README.txt index a297ace..2462bcf 100644 --- a/interpreters/git/README.txt +++ b/interpreters/git/README.txt @@ -1,255 +1,269 @@ -Git is an interpreter for the Glulx virtual machine. Its homepage is here: - - http://diden.net/if/git - -Git's main goal in life is to be fast. It's about five times faster than Glulxe, -and about twice as fast as Frotz (using the same Inform source compiled for the -Z-machine). It also tries to be reasonably careful with memory: it's possible to -trade speed off against memory by changing the sizes of Git's internal buffers. - -I wrote Git because I want people to be able to write huge games or try out -complicated algorithms without worrying about how fast their games are going to -run. I want to play City of Secrets on a Palm without having to wait ten seconds -between each prompt. - -Have fun, and let me know what you think! - - Iain Merrick - iain@diden.net - --------------------------------------------------------------------------------- - -* Building and installing Git - -This is just source code, not a usable application. You'll have to do a bit of -work before you can start playing games with it. If you're not confident about -compiling stuff yourself, you probably want to wait until somebody uploads a -compiled version of Git for your own platform. - -Git needs to be linked with a Glk library in order to run. This can be easy or -hard, depending on what kind of computer you're using and whether you want Git -to be able to display graphics and play sounds. To find a suitable Glk library, -look here: - - http://eblong.com/zarf/glk - -Exactly how you build and link everything depends on what platform you're on and -which Glk library you're using. The supplied Makefile should work on any Unix -machine (including Macs with OS X), but you'll probably want to tweak it to -account for your particular setup. If you're not using Unix, I'm afraid you'll -have to play it by ear. If the Glk library you chose comes with instructions, -that's probably a good place to start. - -On Unix, git_unix.c contains the startup code required by the Glk library. -git_mac.c and git_windows.c contain startup code for MacGlk and WinGlk -respectively, but I can't guarantee that they're fully up-to-date. - -It should be possible to build Git with any C compiler, but it works best with -GCC, because that has a non-standard extension that Git can use for a big speed -boost. GCC 2.95 actually generates faster code than GCC 3, so if you have a -choice, use the former. (On OS X, this means compiling with 'gcc2'.) - --------------------------------------------------------------------------------- - -* Configuring Git - -There are several configuration options you can use when compiling Git. Have a -look at config.h and see which ones look applicable to your platform. The -Makefile includes settings to configure Git for maximum speed on Mac OS X; the -best settings for other Unix platforms should be similar. - -The most important setting is USE_DIRECT_THREADING, which makes the interpreter -engine use GCC's labels-as-values extension, but this only works with GCC 2.95. - --------------------------------------------------------------------------------- - -* Porting to a new platform - -To do a new port, you first need to find a suitable Glk library, or write a new -one. Then you need to write the startup code. Start with a copy of git_unix.c, -git_mac.c or git_windows.c and modify it appropriately. - -The startup code needs to implement the following functions: - - void glk_main() // Standard Glk entrypoint - void fatalError(const char* s) // Display error message and quit - -In glk_main(), you need to locate the game file somehow. Then you have two -options. You can open the game as a Glk stream and pass it to this function: - - extern void gitWithStream (strid_t stream, - git_uint32 cacheSize, - git_uint32 undoSize); - -Or you can load the game yourself, and just pass Git a pointer to your buffer: - - extern void git (const git_uint8 * game, - git_uint32 gameSize, - git_uint32 cacheSize, - git_uint32 undoSize); - -If the operating system provides some way of memory-mapping files (such as -Unix's mmap() system call), you should do that and call git(), because it will -allow the game to start up much more quickly. If you can't do memory-mapping, -you should just open the game as a file stream and call gitWithStream(). Note -that some Glk libraries, such as xglk, aren't compatible with memory-mapped -files. - -"cacheSize" and "undoSize" tell Git what size to use for its two main internal -buffers. Both sizes are in bytes. You may want to make these values -user-configurable, or you may just want to pick values that make sense for your -platform and use those. (My Unix version currently uses fixed values, but I'm -going to add some optional command-line parameters to override these defaults.) - -"cacheSize" is the size of the buffer used to store Glulx code that Git has -recompiled into its internal format. Git will run faster with a larger buffer, -but using a huge buffer is just a waste of memory; 256KB is plenty. - -"undoSize" is the maximum amount of memory used to remember previous moves. The -larger you make it, the more levels of undo will be available. The amount of -memory required to remember one undo position varies from a few KB up to tens of -KB. 256KB is usually enough to store dozens of moves. - --------------------------------------------------------------------------------- - -* Known problems - -GCC 3 has bigger problems than I thought. On PowerPC, the direct threading -option results in much slower code; and on x86, terp.c crashes GCC itself if -direct threading is used. Therefore, I recommend that you use GCC 2.95 if -possible. If you only have GCC 3, don't define USE_DIRECT_THREADING, at least -until the compiler bug is fixed. - -Since the previous update, GCC 4 has been released, but I haven't evaluated it -yet. If you want to give it a try, let me know how you get on! - -Some Glk libraries, such as xglk, can't deal with memory-mapped files. You can -tell that this is happening if Git can open .ulx files, but complains that .blb -files are invalid. The solution is to use gitWithStream() rather than git() in -your startup file, and make sure you're giving it a file stream rather than a -memory stream. If you're using the git_unix.c startup file, just make sure -USE_MMAP isn't defined. - -1-byte and 2-byte local variables are not implemented yet. This means git can't -currently play games created with the Superglus system. This will be fixed at -some point. - -In the search opcodes, direct keys don't work unless they're exactly 4 bytes -long. - --------------------------------------------------------------------------------- - -* Copyright information - -Note: previous versions of Git used an informal freeware license, but I've -decided it's worth formalising. As of version 1.2.3, I've switched to the -MIT license. - -Copyright (c) 2003 Iain Merrick - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - --------------------------------------------------------------------------------- - -* Credits - -Andrew Plotkin invented Glulx, so obviously Git wouldn't exist without him. I -also reused some code from his Glulxe interpreter (glkop.c and search.c), which -saved me a lot of time and let me concentrate on the more interesting stuff. - -Many thanks are due to John Cater, who not only persuaded me to use source -control, but let me use his own CVS server. John also provided lots of useful -advice and encouragement, as did Sean Barrett. - -Thanks also to Joe Mason, Adam Thornton, Simon Baldwin and Joonas Pihlaja who -were among the first to try it out and complain that it wasn't working. Joonas -also gets special brownie points for trying out more bizarre boundary cases than -I realised existed in the first place. - -Tor Andersson was apparently the first person to use setmemsize, since he also -explained why it didn't work and contributed a fix. Thanks, Tor! - -David Kinder has done a stellar job of maintaining the code recently. Thanks -also to Eliuk Blau for tracking down bugs in the memory management opcodes. - --------------------------------------------------------------------------------- - -* Version History - -1.2.4 2009-04-02 More David Kinder! Accelerated opcode support (VM spec 3.1.1). - -1.2.3 2009-02-22 David Kinder and Eliuk Blau fixed some memory management bugs. - Added a regression test (thanks to Emily Short for assistance) - Switched to MIT-style license (see above). - -1.2.2 2009-01-21 malloc & mfree contributed by the most excellent David Kinder. - -1.2.1 2008-09-14 Support for 64-bit machines, contributed by Alexander Beels. - Fix for crashing bug in RESTORE, contributed by David Kinder. - Non-Unicode display bug fix, contributed by Jeremy Bernstein. - -1.2 2008-01-06 Minor version increment for VM spec 3.1. - Implemented mzero and mcopy, but not malloc and mfree (yet). - -1.1.3 2006-10-04 Fixed a bug in the cache logic that broke the game Floatpoint. - Added some other caching tweaks and put in a few more asserts. - -1.1.2 2006-08-22 streamnum in filter I/O mode no longer prints a garbage char. - Merged in David Kinder's updated Windows startup code. - -1.1.1 2006-08-17 Wow, over a year since the last update. - Rolled in Tor Andersson's fix for setmemsize. - -1.1 2004-12-22 Minor version increment because we now implement VM spec 3.0. - Implemented new Unicode opcodes and string types. - -1.0.6 2004-12-10 Random number generator now handles random(0) correctly. - Code cache now tracks the number of function calls properly. - Fixed a bug that could hang the terp when the cache filled up. - -1.0.5 2004-05-31 Random number generator is now initialised properly. - Some source files had Mac line-endings, now fixed. - Version number is now set in the Makefile, not in git.h. - Merged David Kinder's Windows Git code into main distribution. - -1.0.4 2004-03-13 Fixed a silly bug in direct threading mode that broke stkroll. - Memory access bounds checking has been tightened up slightly. - aload and astore now work correctly with negative offsets. - Rewrote the shift opcodes a bit more defensively. - Implemented the "verify" opcode. - Code in RAM is no longer cached by default. - Adding some special opcodes to control the code cache. - Bad instructions are now caught in the terp, not the compiler. - Now passes all of Joonas' indirect string decoding tests. - -1.0.3 2004-01-22 No longer hangs when using streamnum in the "filter" I/O mode. - setstringtbl opcode now works correctly. - -1.0.2 2003-10-25 Stupid bug in 1.0.1 -- gitWithStream() was broken and wasn't - able to load Blorb files. Now it's *really* fixed. - -1.0.1 2003-10-23 Fixed a bug where strings were printed as "[string]" - Fixed a bug in tailcall - Implemented setmemsize - Implemented protect - Moved git_init_dispatch() call out of startup code, into git.c - Added divide-by-zero check - Compiler now stops when it finds a 'quit' or 'restart' - Added gitWithStream() as a workaround for xglk - -1.0 2003-10-18 First public release +Git is an interpreter for the Glulx virtual machine. Its homepage is here: + + http://diden.net/if/git + +Git's main goal in life is to be fast. It's about five times faster than Glulxe, +and about twice as fast as Frotz (using the same Inform source compiled for the +Z-machine). It also tries to be reasonably careful with memory: it's possible to +trade speed off against memory by changing the sizes of Git's internal buffers. + +I wrote Git because I want people to be able to write huge games or try out +complicated algorithms without worrying about how fast their games are going to +run. I want to play City of Secrets on a Palm without having to wait ten seconds +between each prompt. + +Have fun, and let me know what you think! + + Iain Merrick + iain@diden.net + +-------------------------------------------------------------------------------- + +* Building and installing Git + +This is just source code, not a usable application. You'll have to do a bit of +work before you can start playing games with it. If you're not confident about +compiling stuff yourself, you probably want to wait until somebody uploads a +compiled version of Git for your own platform. + +Git needs to be linked with a Glk library in order to run. This can be easy or +hard, depending on what kind of computer you're using and whether you want Git +to be able to display graphics and play sounds. To find a suitable Glk library, +look here: + + http://eblong.com/zarf/glk + +Exactly how you build and link everything depends on what platform you're on and +which Glk library you're using. The supplied Makefile should work on any Unix +machine (including Macs with OS X), but you'll probably want to tweak it to +account for your particular setup. If you're not using Unix, I'm afraid you'll +have to play it by ear. If the Glk library you chose comes with instructions, +that's probably a good place to start. + +On Unix, git_unix.c contains the startup code required by the Glk library. +git_mac.c and git_windows.c contain startup code for MacGlk and WinGlk +respectively, but I can't guarantee that they're fully up-to-date. + +It should be possible to build Git with any C compiler, but it works best with +GCC, because that has a non-standard extension that Git can use for a big speed +boost. GCC 2.95 actually generates faster code than GCC 3, so if you have a +choice, use the former. (On OS X, this means compiling with 'gcc2'.) + +-------------------------------------------------------------------------------- + +* Configuring Git + +There are several configuration options you can use when compiling Git. Have a +look at config.h and see which ones look applicable to your platform. The +Makefile includes settings to configure Git for maximum speed on Mac OS X; the +best settings for other Unix platforms should be similar. + +The most important setting is USE_DIRECT_THREADING, which makes the interpreter +engine use GCC's labels-as-values extension, but this only works with GCC 2.95. + +-------------------------------------------------------------------------------- + +* Porting to a new platform + +To do a new port, you first need to find a suitable Glk library, or write a new +one. Then you need to write the startup code. Start with a copy of git_unix.c, +git_mac.c or git_windows.c and modify it appropriately. + +The startup code needs to implement the following functions: + + void glk_main() // Standard Glk entrypoint + void fatalError(const char* s) // Display error message and quit + +In glk_main(), you need to locate the game file somehow. Then you have two +options. You can open the game as a Glk stream and pass it to this function: + + extern void gitWithStream (strid_t stream, + git_uint32 cacheSize, + git_uint32 undoSize); + +Or you can load the game yourself, and just pass Git a pointer to your buffer: + + extern void git (const git_uint8 * game, + git_uint32 gameSize, + git_uint32 cacheSize, + git_uint32 undoSize); + +If the operating system provides some way of memory-mapping files (such as +Unix's mmap() system call), you should do that and call git(), because it will +allow the game to start up much more quickly. If you can't do memory-mapping, +you should just open the game as a file stream and call gitWithStream(). Note +that some Glk libraries, such as xglk, aren't compatible with memory-mapped +files. + +"cacheSize" and "undoSize" tell Git what size to use for its two main internal +buffers. Both sizes are in bytes. You may want to make these values +user-configurable, or you may just want to pick values that make sense for your +platform and use those. (My Unix version currently uses fixed values, but I'm +going to add some optional command-line parameters to override these defaults.) + +"cacheSize" is the size of the buffer used to store Glulx code that Git has +recompiled into its internal format. Git will run faster with a larger buffer, +but using a huge buffer is just a waste of memory; 256KB is plenty. + +"undoSize" is the maximum amount of memory used to remember previous moves. The +larger you make it, the more levels of undo will be available. The amount of +memory required to remember one undo position varies from a few KB up to tens of +KB. 256KB is usually enough to store dozens of moves. + +-------------------------------------------------------------------------------- + +* Known problems + +GCC 3 has bigger problems than I thought. On PowerPC, the direct threading +option results in much slower code; and on x86, terp.c crashes GCC itself if +direct threading is used. Therefore, I recommend that you use GCC 2.95 if +possible. If you only have GCC 3, don't define USE_DIRECT_THREADING, at least +until the compiler bug is fixed. + +Since the previous update, GCC 4 has been released, but I haven't evaluated it +yet. If you want to give it a try, let me know how you get on! + +Some Glk libraries, such as xglk, can't deal with memory-mapped files. You can +tell that this is happening if Git can open .ulx files, but complains that .blb +files are invalid. The solution is to use gitWithStream() rather than git() in +your startup file, and make sure you're giving it a file stream rather than a +memory stream. If you're using the git_unix.c startup file, just make sure +USE_MMAP isn't defined. + +1-byte and 2-byte local variables are not implemented yet. This means git can't +currently play games created with the Superglus system. This will be fixed at +some point. + +In the search opcodes, direct keys don't work unless they're exactly 4 bytes +long. + +-------------------------------------------------------------------------------- + +* Copyright information + +Note: previous versions of Git used an informal freeware license, but I've +decided it's worth formalising. As of version 1.2.3, I've switched to the +MIT license. + +Copyright (c) 2003 Iain Merrick + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +-------------------------------------------------------------------------------- + +* Credits + +Andrew Plotkin invented Glulx, so obviously Git wouldn't exist without him. I +also reused some code from his Glulxe interpreter (glkop.c and search.c), which +saved me a lot of time and let me concentrate on the more interesting stuff. + +Many thanks are due to John Cater, who not only persuaded me to use source +control, but let me use his own CVS server. John also provided lots of useful +advice and encouragement, as did Sean Barrett. + +Thanks also to Joe Mason, Adam Thornton, Simon Baldwin and Joonas Pihlaja who +were among the first to try it out and complain that it wasn't working. Joonas +also gets special brownie points for trying out more bizarre boundary cases than +I realised existed in the first place. + +Tor Andersson was apparently the first person to use setmemsize, since he also +explained why it didn't work and contributed a fix. Thanks, Tor! + +David Kinder has done a stellar job of maintaining the code recently. Thanks +also to Eliuk Blau for tracking down bugs in the memory management opcodes. + +-------------------------------------------------------------------------------- + +* Version History + +1.2.8 2010-08-25 Fixed a problem with 'undo' when compiled as 64 bit, + contributed by Ben Cressey. + Fixed a sign problem for the @fceil opcode, following a + similar fix in Glulxe. + +1.2.7 2010-08-20 Floating point opcode support (VM spec 3.1.2). + Restart does not now discard undo information, so that a + restart can be undone. + +1.2.6 2010-02-09 Imported fix for retained Glk array handling from Glulxe. + +1.2.5 2009-11-21 Fixes for problems shown by Andrew Plotkin's glulxercise test + cases, from David Kinder. + +1.2.4 2009-04-02 More David Kinder! Accelerated opcode support (VM spec 3.1.1). + +1.2.3 2009-02-22 David Kinder and Eliuk Blau fixed some memory management bugs. + Added a regression test (thanks to Emily Short for assistance) + Switched to MIT-style license (see above). + +1.2.2 2009-01-21 malloc & mfree contributed by the most excellent David Kinder. + +1.2.1 2008-09-14 Support for 64-bit machines, contributed by Alexander Beels. + Fix for crashing bug in RESTORE, contributed by David Kinder. + Non-Unicode display bug fix, contributed by Jeremy Bernstein. + +1.2 2008-01-06 Minor version increment for VM spec 3.1. + Implemented mzero and mcopy, but not malloc and mfree (yet). + +1.1.3 2006-10-04 Fixed a bug in the cache logic that broke the game Floatpoint. + Added some other caching tweaks and put in a few more asserts. + +1.1.2 2006-08-22 streamnum in filter I/O mode no longer prints a garbage char. + Merged in David Kinder's updated Windows startup code. + +1.1.1 2006-08-17 Wow, over a year since the last update. + Rolled in Tor Andersson's fix for setmemsize. + +1.1 2004-12-22 Minor version increment because we now implement VM spec 3.0. + Implemented new Unicode opcodes and string types. + +1.0.6 2004-12-10 Random number generator now handles random(0) correctly. + Code cache now tracks the number of function calls properly. + Fixed a bug that could hang the terp when the cache filled up. + +1.0.5 2004-05-31 Random number generator is now initialised properly. + Some source files had Mac line-endings, now fixed. + Version number is now set in the Makefile, not in git.h. + Merged David Kinder's Windows Git code into main distribution. + +1.0.4 2004-03-13 Fixed a silly bug in direct threading mode that broke stkroll. + Memory access bounds checking has been tightened up slightly. + aload and astore now work correctly with negative offsets. + Rewrote the shift opcodes a bit more defensively. + Implemented the "verify" opcode. + Code in RAM is no longer cached by default. + Adding some special opcodes to control the code cache. + Bad instructions are now caught in the terp, not the compiler. + Now passes all of Joonas' indirect string decoding tests. + +1.0.3 2004-01-22 No longer hangs when using streamnum in the "filter" I/O mode. + setstringtbl opcode now works correctly. + +1.0.2 2003-10-25 Stupid bug in 1.0.1 -- gitWithStream() was broken and wasn't + able to load Blorb files. Now it's *really* fixed. + +1.0.1 2003-10-23 Fixed a bug where strings were printed as "[string]" + Fixed a bug in tailcall + Implemented setmemsize + Implemented protect + Moved git_init_dispatch() call out of startup code, into git.c + Added divide-by-zero check + Compiler now stops when it finds a 'quit' or 'restart' + Added gitWithStream() as a workaround for xglk + +1.0 2003-10-18 First public release diff --git a/interpreters/git/config.h b/interpreters/git/config.h index d1dea0a..77df286 100644 --- a/interpreters/git/config.h +++ b/interpreters/git/config.h @@ -19,6 +19,11 @@ // Define this to memory-map the game file to speed up loading. (Unix-specific) // #define USE_MMAP +// Define this to use an OS-specific git_powf() power math function. This +// is useful if your compiler's powf() doesn't implement every special case +// of the C99 standard. +// #define USE_OWN_POWF + // ------------------------------------------------------------------- // Make sure we're compiling for a sane platform. For now, this means @@ -76,4 +81,6 @@ typedef unsigned long git_uint32; #define GIT_INLINE static #endif +typedef float git_float; + #endif // GIT_CONFIG_H diff --git a/interpreters/git/gestalt.c b/interpreters/git/gestalt.c index 0253433..2fabcda 100644 --- a/interpreters/git/gestalt.c +++ b/interpreters/git/gestalt.c @@ -5,7 +5,7 @@ git_uint32 gestalt (enum GestaltSelector sel, git_uint32 param) switch (sel) { case GESTALT_SPEC_VERSION: - return 0x00030101; + return 0x00030102; case GESTALT_TERP_VERSION: return GIT_VERSION_NUM; @@ -31,6 +31,9 @@ git_uint32 gestalt (enum GestaltSelector sel, git_uint32 param) case GESTALT_MALLOC: return 1; + case GESTALT_MALLOC_HEAP: + return heap_get_start(); + case GESTALT_ACCELERATION: return 1; @@ -39,9 +42,9 @@ git_uint32 gestalt (enum GestaltSelector sel, git_uint32 param) return 1; return 0; - case GESTALT_MALLOC_HEAP: - return heap_get_start(); - + case GESTALT_FLOAT: + return 1; + case GESTALT_GIT_CACHE_CONTROL: return 1; diff --git a/interpreters/git/git.h b/interpreters/git/git.h index 3a56a44..cd1f90e 100644 --- a/interpreters/git/git.h +++ b/interpreters/git/git.h @@ -69,6 +69,7 @@ enum GestaltSelector GESTALT_MALLOC_HEAP = 8, GESTALT_ACCELERATION = 9, GESTALT_ACCELFUNC = 10, + GESTALT_FLOAT = 11, // This special selector returns 1 if the cache control // opcodes 'git_setcacheram' and 'git_prunecache' are available. @@ -92,9 +93,9 @@ extern git_uint32 parseLoad (git_uint32 * pc, LoadReg reg, int mode, TransferSi extern void parseStore (git_uint32 * pc, StoreReg reg, int mode, TransferSize); extern void parseCallStub (git_uint32 * pc, int mode); -extern void parseCatchStub (git_uint32 * pc, int mode); extern void parseSaveStub (git_uint32 * pc, int mode); extern void parseUndoStub (git_uint32 * pc, int mode); +extern void parseCatchStub (git_uint32 * pc, int * modes); // compiler.c diff --git a/interpreters/git/git_unix.c b/interpreters/git/git_unix.c index 2b9c6e3..3159c32 100644 --- a/interpreters/git/git_unix.c +++ b/interpreters/git/git_unix.c @@ -8,6 +8,8 @@ #include #include // This comes with the Glk library. +#include + #ifdef USE_MMAP #include #include @@ -27,7 +29,7 @@ glkunix_argumentlist_t glkunix_arguments[] = int gHasInited = 0; -#ifdef CHIMARA_EXTENSIONS +#ifdef GARGLK void fatalError (const char * s) { @@ -52,7 +54,7 @@ void fatalError (const char * s) exit (1); } -#endif /* CHIMARA_EXTENSIONS */ +#endif /* GARGLK */ #ifdef USE_MMAP // Fast loader that uses some fancy Unix features. @@ -62,11 +64,33 @@ char * gStartupError = 0; int glkunix_startup_code(glkunix_startup_t *data) { +#ifdef GARGLK + { + char buf[255]; + sprintf(buf, "Git %d.%d.%d", GIT_MAJOR, GIT_MINOR, GIT_PATCH); + garglk_set_program_name(buf); + sprintf(buf, "Git %d.%d.%d by Iain Merrick and David Kinder\n", + GIT_MAJOR, GIT_MINOR, GIT_PATCH); + garglk_set_program_info(buf); + } +#endif /* GARGLK */ + if (data->argc <= 1) { gStartupError = "No file given"; return 1; } + +#ifdef GARGLK + { + char *s; + s = strrchr(data->argv[1], '\\'); + if (s) garglk_set_story_name(s+1); + s = strrchr(data->argv[1], '/'); + if (s) garglk_set_story_name(s+1); + } +#endif /* GARGLK */ + gFilename = data->argv[1]; return 1; } @@ -113,11 +137,33 @@ char * gStartupError = 0; int glkunix_startup_code(glkunix_startup_t *data) { +#ifdef GARGLK + { + char buf[255]; + sprintf(buf, "Git %d.%d.%d", GIT_MAJOR, GIT_MINOR, GIT_PATCH); + garglk_set_program_name(buf); + sprintf(buf, "Git %d.%d.%d by Iain Merrick and David Kinder\n", + GIT_MAJOR, GIT_MINOR, GIT_PATCH); + garglk_set_program_info(buf); + } +#endif /* GARGLK */ + if (data->argc <= 1) { gStartupError = "No file given"; return 1; } + +#ifdef GARGLK + { + char *s; + s = strrchr(data->argv[1], '\\'); + if (s) garglk_set_story_name(s+1); + s = strrchr(data->argv[1], '/'); + if (s) garglk_set_story_name(s+1); + } +#endif /* GARGLK */ + gStream = glkunix_stream_open_pathname ((char*) data->argv[1], 0, 0); return 1; } diff --git a/interpreters/git/glkop.c b/interpreters/git/glkop.c index 71452ef..f8c7ff0 100644 --- a/interpreters/git/glkop.c +++ b/interpreters/git/glkop.c @@ -1140,6 +1140,9 @@ void glulxe_retained_unregister(void *array, glui32 len, if (arref->elemsize != 4 || arref->len != len) fatalError("Mismatched array argument in Glk call."); + *aptr = arref->next; + arref->next = NULL; + for (ix=0, addr2=arref->addr; ixlen; ix++, addr2+=4) { val = ((glui32 *)array)[ix]; memWrite32(addr2, val); diff --git a/interpreters/git/labels.inc b/interpreters/git/labels.inc index 5268616..d0dfbbd 100644 --- a/interpreters/git/labels.inc +++ b/interpreters/git/labels.inc @@ -42,20 +42,20 @@ LABEL (throw) // Loading and storing registers. #define LOAD_LABELS(tag) \ - LABEL (L1_ ## tag) \ - LABEL (L2_ ## tag) \ - LABEL (L3_ ## tag) \ - LABEL (L4_ ## tag) \ - LABEL (L5_ ## tag) \ - LABEL (L6_ ## tag) \ - LABEL (L7_ ## tag) \ + LABEL (L1_ ## tag) \ + LABEL (L2_ ## tag) \ + LABEL (L3_ ## tag) \ + LABEL (L4_ ## tag) \ + LABEL (L5_ ## tag) \ + LABEL (L6_ ## tag) \ + LABEL (L7_ ## tag) \ LABEL (L1_const_L2_ ## tag) \ LABEL (L1_stack_L2_ ## tag) \ LABEL (L1_local_L2_ ## tag) \ LABEL (L1_addr_L2_ ## tag) #define STORE_LABELS(tag) \ - LABEL (S1_ ## tag) \ + LABEL (S1_ ## tag) \ LABEL (S2_ ## tag) LOAD_LABELS(const) @@ -101,7 +101,11 @@ LABEL (nop) LABEL (aload ## tag) \ LABEL (aloads ## tag) \ LABEL (aloadb ## tag) \ - LABEL (aloadbit ## tag) + LABEL (aloadbit ## tag) \ + LABEL (fadd ## tag) \ + LABEL (fsub ## tag) \ + LABEL (fmul ## tag) \ + LABEL (fdiv ## tag) PEEPHOLE_STORE_LABELS(_discard) PEEPHOLE_STORE_LABELS(_S1_stack) @@ -128,20 +132,28 @@ LABEL (astorebit) #undef PEEPHOLE_STORE_LABELS -#define BRANCH_LABELS(tag) \ - LABEL (jump ## tag) \ - LABEL (jz ## tag) \ - LABEL (jnz ## tag) \ - LABEL (jeq ## tag) \ - LABEL (jne ## tag) \ - LABEL (jlt ## tag) \ - LABEL (jge ## tag) \ - LABEL (jgt ## tag) \ - LABEL (jle ## tag) \ - LABEL (jltu ## tag) \ - LABEL (jgeu ## tag) \ - LABEL (jgtu ## tag) \ - LABEL (jleu ## tag) +#define BRANCH_LABELS(tag) \ + LABEL (jump ## tag) \ + LABEL (jz ## tag) \ + LABEL (jnz ## tag) \ + LABEL (jeq ## tag) \ + LABEL (jne ## tag) \ + LABEL (jlt ## tag) \ + LABEL (jge ## tag) \ + LABEL (jgt ## tag) \ + LABEL (jle ## tag) \ + LABEL (jltu ## tag) \ + LABEL (jgeu ## tag) \ + LABEL (jgtu ## tag) \ + LABEL (jleu ## tag) \ + LABEL (jflt ## tag) \ + LABEL (jfge ## tag) \ + LABEL (jfgt ## tag) \ + LABEL (jfle ## tag) \ + LABEL (jisnan ## tag) \ + LABEL (jisinf ## tag) \ + LABEL (jfeq ## tag) \ + LABEL (jfne ## tag) BRANCH_LABELS(_var) BRANCH_LABELS(_const) @@ -195,6 +207,24 @@ LABEL (mfree) LABEL (accelfunc) LABEL (accelparam) +LABEL (numtof) +LABEL (ftonumz) +LABEL (ftonumn) +LABEL (ceil) +LABEL (floor) +LABEL (fmod) +LABEL (sqrt) +LABEL (exp) +LABEL (log) +LABEL (pow) +LABEL (sin) +LABEL (cos) +LABEL (tan) +LABEL (asin) +LABEL (acos) +LABEL (atan) +LABEL (atan2) + LABEL (git_setcacheram) LABEL (git_prunecache) diff --git a/interpreters/git/opcodes.c b/interpreters/git/opcodes.c index a57abb3..cc0b188 100644 --- a/interpreters/git/opcodes.c +++ b/interpreters/git/opcodes.c @@ -45,6 +45,17 @@ static void parseLLS (git_uint32* pc, Label op) emitCode (op); parseStore (pc, reg_S1, modes [2], size32); } +static void parseLLSS (git_uint32* pc, Label op) +{ + int modes [4]; + parseModeNibbles (pc, 4, modes); + + parseLoad (pc, reg_L1, modes [0], size32, NULL); + parseLoad (pc, reg_L2, modes [1], size32, NULL); + emitCode (op); + parseStore (pc, reg_S1, modes [2], size32); + parseStore (pc, reg_S2, modes [3], size32); +} static void parseL (git_uint32* pc, Label op) { int modes [1]; @@ -94,6 +105,16 @@ static void parse_finish_branch (git_uint32* pc, Label op, LoadReg reg, int mode emitData(*pc); } } +static void parseLLLL_branch (git_uint32* pc, Label op) +{ + int modes [4]; + parseModeNibbles (pc, 4, modes); + + parseLoad (pc, reg_L1, modes [0], size32, NULL); + parseLoad (pc, reg_L2, modes [1], size32, NULL); + parseLoad (pc, reg_L3, modes [2], size32, NULL); + parse_finish_branch (pc, op, reg_L4, modes [3]); +} static void parseLLL_branch (git_uint32* pc, Label op) { int modes [3]; @@ -147,27 +168,10 @@ static void parseSS (git_uint32* pc, Label op) } static void parseCatch (git_uint32 * pc) { - Block stubCode; int modes [2]; parseModeNibbles (pc, 2, modes); - parseCatchStub (pc, modes[0]); - - // This is a little nasty. The last thing emitted by - // parseCatchStub() is the current value of the PC, - // which is where execution will resume when and if - // the stub is used; but execution should resume - // after the branch we're about to do, so we'll need - // to fix up that emitted value. - - stubCode = peekAtEmittedStuff (1); - - parseLoad (pc, reg_L1, modes[1], size32, NULL); - emitCode (label_jump_var); - emitData(*pc); - - // Fix up the end of the stub, as described above. - *stubCode = *pc; + parseCatchStub (pc, modes); } void parseInstruction (git_uint32* pc, int * done) { @@ -359,7 +363,7 @@ void parseInstruction (git_uint32* pc, int * done) // Continuations case op_catch: parseCatch (pc); break; - case op_throw: + case op_throw: parseLL (pc, label_throw); *done = 1; break; @@ -452,7 +456,45 @@ void parseInstruction (git_uint32* pc, int * done) case op_accelfunc: parseLL (pc, label_accelfunc); break; case op_accelparam: parseLL (pc, label_accelparam); break; - + + // Floating point + + case op_numtof: parseLS (pc, label_numtof); break; + case op_ftonumz: parseLS (pc, label_ftonumz); break; + case op_ftonumn: parseLS (pc, label_ftonumn); break; + case op_ceil: parseLS (pc, label_ceil); break; + case op_floor: parseLS (pc, label_floor); break; + case op_sqrt: parseLS (pc, label_sqrt); break; + case op_exp: parseLS (pc, label_exp); break; + case op_log: parseLS (pc, label_log); break; + + case op_fadd: parseLLS (pc, label_fadd_discard); break; + case op_fsub: parseLLS (pc, label_fsub_discard); break; + case op_fmul: parseLLS (pc, label_fmul_discard); break; + case op_fdiv: parseLLS (pc, label_fdiv_discard); break; + case op_pow: parseLLS (pc, label_pow); break; + case op_atan2: parseLLS (pc, label_atan2); break; + + case op_fmod: parseLLSS (pc, label_fmod); break; + + case op_sin: parseLS (pc, label_sin); break; + case op_cos: parseLS (pc, label_cos); break; + case op_tan: parseLS (pc, label_tan); break; + case op_asin: parseLS (pc, label_asin); break; + case op_acos: parseLS (pc, label_acos); break; + case op_atan: parseLS (pc, label_atan); break; + + case op_jfeq: parseLLLL_branch (pc, label_jfeq_var); break; + case op_jfne: parseLLLL_branch (pc, label_jfne_var); break; + + case op_jflt: parseLLL_branch (pc, label_jflt_var); break; + case op_jfle: parseLLL_branch (pc, label_jfle_var); break; + case op_jfgt: parseLLL_branch (pc, label_jfgt_var); break; + case op_jfge: parseLLL_branch (pc, label_jfge_var); break; + + case op_jisnan: parseLL_branch (pc, label_jisnan_var); break; + case op_jisinf: parseLL_branch (pc, label_jisinf_var); break; + // Special Git opcodes case op_git_setcacheram: parseL (pc, label_git_setcacheram); break; diff --git a/interpreters/git/opcodes.h b/interpreters/git/opcodes.h index 677c450..71b210a 100644 --- a/interpreters/git/opcodes.h +++ b/interpreters/git/opcodes.h @@ -108,6 +108,36 @@ #define op_accelfunc (0x180) #define op_accelparam (0x181) +#define op_numtof (0x190) +#define op_ftonumz (0x191) +#define op_ftonumn (0x192) +#define op_ceil (0x198) +#define op_floor (0x199) +#define op_fadd (0x1A0) +#define op_fsub (0x1A1) +#define op_fmul (0x1A2) +#define op_fdiv (0x1A3) +#define op_fmod (0x1A4) +#define op_sqrt (0x1A8) +#define op_exp (0x1A9) +#define op_log (0x1AA) +#define op_pow (0x1AB) +#define op_sin (0x1B0) +#define op_cos (0x1B1) +#define op_tan (0x1B2) +#define op_asin (0x1B3) +#define op_acos (0x1B4) +#define op_atan (0x1B5) +#define op_atan2 (0x1B6) +#define op_jfeq (0x1C0) +#define op_jfne (0x1C1) +#define op_jflt (0x1C2) +#define op_jfle (0x1C3) +#define op_jfgt (0x1C4) +#define op_jfge (0x1C5) +#define op_jisnan (0x1C8) +#define op_jisinf (0x1C9) + // Special cache control opcodes. #define op_git_setcacheram (0x7940) diff --git a/interpreters/git/operands.c b/interpreters/git/operands.c index a4b6e0b..a869361 100644 --- a/interpreters/git/operands.c +++ b/interpreters/git/operands.c @@ -279,7 +279,7 @@ static void parseStub (git_uint32 * pc, int mode, Label discardOp) break; store_local: emitCode (discardOp + (label_call_stub_local - label_call_stub_discard)); - emitData (value); // Convert byte offset to word offset. + emitData (value); break; } @@ -293,10 +293,6 @@ void parseCallStub (git_uint32 * pc, int mode) { parseStub (pc, mode, label_call_stub_discard); } -void parseCatchStub (git_uint32 * pc, int mode) -{ - parseStub (pc, mode, label_catch_stub_discard); -} void parseSaveStub (git_uint32 * pc, int mode) { parseStub (pc, mode, label_save_stub_discard); @@ -305,3 +301,98 @@ void parseUndoStub (git_uint32 * pc, int mode) { parseStub (pc, mode, label_undo_stub_discard); } + +void parseCatchStub (git_uint32 * pc, int * modes) +{ + git_uint32 tokenVal, branchVal; + git_uint32 branchConst = 0; + Block stubCode; + + switch (modes[0]) + { + case 0x0: // Discard + goto store_discard; + case 0x5: // Contents of address 00 to FF. (One byte) + tokenVal = memRead8(*pc); + *pc += 1; + goto store_addr; + case 0x6: // Contents of address 0000 to FFFF. (Two bytes) + tokenVal = memRead16(*pc); + *pc += 2; + goto store_addr; + case 0x7: // Contents of any address. (Four bytes) + tokenVal = memRead32(*pc); + *pc += 4; + goto store_addr; + case 0x8: // Value popped off stack. (Zero bytes) + goto store_stack; + case 0x9: // Call frame local at store_address 00 to FF. (One byte) + tokenVal = memRead8(*pc); + *pc += 1; + goto store_local; + case 0xA: // Call frame local at store_address 0000 to FFFF. (Two bytes) + tokenVal = memRead16(*pc); + *pc += 2; + goto store_local; + case 0xB: // Call frame local at any store_address. (Four bytes) + tokenVal = memRead32(*pc); + *pc += 4; + goto store_local; + case 0xD: // Contents of RAM address 00 to FF. (One byte) + tokenVal = memRead8(*pc) + gRamStart; + *pc += 1; + goto store_addr; + case 0xE: // Contents of RAM address 0000 to FFFF. (Two bytes) + tokenVal = memRead16(*pc) + gRamStart; + *pc += 2; + goto store_addr; + case 0xF: // Contents of RAM, any address. (Four bytes) + tokenVal = memRead32(*pc) + gRamStart; + *pc += 4; + goto store_addr; + // ------------------------------------------------------ + store_discard: + branchConst = parseLoad (pc, reg_L1, modes[1], size32, &branchVal); + emitCode (label_catch_stub_discard); + break; + store_stack: + branchConst = parseLoad (pc, reg_L1, modes[1], size32, &branchVal); + emitCode (label_catch_stub_stack); + break; + store_addr: + branchConst = parseLoad (pc, reg_L1, modes[1], size32, &branchVal); + emitCode (label_catch_stub_addr); + emitData (tokenVal); + break; + store_local: + branchConst = parseLoad (pc, reg_L1, modes[1], size32, &branchVal); + emitCode (label_catch_stub_local); + emitData (tokenVal); + break; + } + + // The catch stub ends with the address to go to on throw, + // which is after the branch, so we don't know what it is yet. + emitData (0); + stubCode = peekAtEmittedStuff (1); + + // Emit the branch taken after storing the catch token. + if (branchConst) + { + if (branchVal == 0) + emitCode (label_jump_return0); + else if (branchVal == 1) + emitCode (label_jump_return1); + else + emitConstBranch (label_jump_const, *pc + branchVal - 2); + } + else + { + emitCode (label_jump_var); + emitData (*pc); + } + + // Fix up the throw return address + *stubCode = *pc; + nextInstructionIsReferenced (); +} diff --git a/interpreters/git/peephole.c b/interpreters/git/peephole.c index 4068b67..08b9606 100644 --- a/interpreters/git/peephole.c +++ b/interpreters/git/peephole.c @@ -50,6 +50,10 @@ extern void resetPeepholeOptimiser () CASE_NO_OPERANDS (aloads_discard, aloads_ ## storeOp); \ CASE_NO_OPERANDS (aloadb_discard, aloadb_ ## storeOp); \ CASE_NO_OPERANDS (aloadbit_discard, aloadbit_ ## storeOp); \ + CASE_NO_OPERANDS (fadd_discard, fadd_ ## storeOp); \ + CASE_NO_OPERANDS (fsub_discard, fsub_ ## storeOp); \ + CASE_NO_OPERANDS (fmul_discard, fmul_ ## storeOp); \ + CASE_NO_OPERANDS (fdiv_discard, fdiv_ ## storeOp); \ default: break; \ } \ break diff --git a/interpreters/git/saveundo.c b/interpreters/git/saveundo.c index 13620f3..aa9de5f 100644 --- a/interpreters/git/saveundo.c +++ b/interpreters/git/saveundo.c @@ -40,7 +40,7 @@ int saveUndo (git_sint32 * base, git_sint32 * sp) { git_uint32 undoSize = sizeof(UndoRecord); git_uint32 mapSize = sizeof(MemoryPage*) * (gEndMem - gRamStart) / 256; - git_uint32 stackSize = sizeof(git_sint32*) * (sp - base); + git_uint32 stackSize = sizeof(git_sint32) * (sp - base); git_uint32 totalSize = undoSize + mapSize + stackSize; git_uint32 addr = gRamStart; // Address in glulx memory. diff --git a/interpreters/git/terp.c b/interpreters/git/terp.c index 8377c5d..870b4f1 100644 --- a/interpreters/git/terp.c +++ b/interpreters/git/terp.c @@ -3,6 +3,7 @@ #include "git.h" #include +#include #include #include #include @@ -29,6 +30,39 @@ Opcode* gOpcodeTable; #define CHECK_FREE(n) if ((top - sp) < (n)) goto stack_overflow #define CHECK_USED(n) if ((sp - values) < (n)) goto stack_underflow +// ------------------------------------------------------------- +// Floating point support + +#define ENCODE_FLOAT(f) (* (git_uint32*) &f) +#define DECODE_FLOAT(n) (* (git_float*) &n) + +int floatCompare(git_sint32 L1, git_sint32 L2, git_sint32 L3) +{ + git_float F1, F2; + + if (((L3 & 0x7F800000) == 0x7F800000) && ((L3 & 0x007FFFFF) != 0)) + return 0; + if ((L1 == 0x7F800000 || L1 == 0xFF800000) && (L2 == 0x7F800000 || L2 == 0xFF800000)) + return (L1 == L2); + + F1 = DECODE_FLOAT(L2) - DECODE_FLOAT(L1); + F2 = fabs(DECODE_FLOAT(L3)); + return ((F1 <= F2) && (F1 >= -F2)); +} + +#ifdef USE_OWN_POWF +float git_powf(float x, float y) +{ + if (x == 1.0f) + return 1.0f; + else if ((y == 0.0f) || (y == -0.0f)) + return 1.0f; + else if ((x == -1.0f) && isinf(y)) + return 1.0f; + return powf(x,y); +} +#endif + // ------------------------------------------------------------- // Functions @@ -39,6 +73,7 @@ void startProgram (size_t cacheSize, enum IOMode ioMode) git_sint32 L1=0, L2=0, L3=0, L4=0, L5=0, L6=0, L7=0; #define S1 L1 #define S2 L2 + git_float F1=0.0f, F2=0.0f, F3=0.0f, F4=0.0f; git_sint32* base; // Bottom of the stack. git_sint32* frame; // Bottom of the current stack frame. @@ -278,9 +313,9 @@ do_enter_function_L1: // Arg count is in L2. PEEPHOLE_STORE(bitor, S1 = L1 | L2); PEEPHOLE_STORE(bitxor, S1 = L1 ^ L2); - PEEPHOLE_STORE(shiftl, if (L2 > 31) S1 = 0; else S1 = L1 << ((git_uint32) L2)); - PEEPHOLE_STORE(sshiftr, if (L2 > 31) L2 = 31; S1 = ((git_sint32) L1) >> ((git_uint32) L2)); - PEEPHOLE_STORE(ushiftr, if (L2 > 31) S1 = 0; else S1 = ((git_uint32) L1) >> ((git_uint32) L2)); + PEEPHOLE_STORE(shiftl, if (L2 > 31 || L2 < 0) S1 = 0; else S1 = L1 << ((git_uint32) L2)); + PEEPHOLE_STORE(sshiftr, if (L2 > 31 || L2 < 0) L2 = 31; S1 = ((git_sint32) L1) >> ((git_uint32) L2)); + PEEPHOLE_STORE(ushiftr, if (L2 > 31 || L2 < 0) S1 = 0; else S1 = ((git_uint32) L1) >> ((git_uint32) L2)); PEEPHOLE_STORE(aload, S1 = memRead32 (L1 + (L2<<2))); PEEPHOLE_STORE(aloads, S1 = memRead16 (L1 + (L2<<1))); @@ -292,6 +327,11 @@ do_enter_function_L1: // Arg count is in L2. PEEPHOLE_STORE(sexs, S1 = (git_sint32)((signed short)(L1 & 0xFFFF))); PEEPHOLE_STORE(sexb, S1 = (git_sint32)((signed char)(L1 & 0x00FF))); + PEEPHOLE_STORE(fadd, F1 = DECODE_FLOAT(L1) + DECODE_FLOAT(L2); S1 = ENCODE_FLOAT(F1)); + PEEPHOLE_STORE(fsub, F1 = DECODE_FLOAT(L1) - DECODE_FLOAT(L2); S1 = ENCODE_FLOAT(F1)); + PEEPHOLE_STORE(fmul, F1 = DECODE_FLOAT(L1) * DECODE_FLOAT(L2); S1 = ENCODE_FLOAT(F1)); + PEEPHOLE_STORE(fdiv, F1 = DECODE_FLOAT(L1) / DECODE_FLOAT(L2); S1 = ENCODE_FLOAT(F1)); + #define PEEPHOLE_LOAD(tag,reg) \ do_ ## tag ## _ ## reg ## _const: reg = READ_PC; goto do_ ## tag; \ do_ ## tag ## _ ## reg ## _stack: CHECK_USED(1); reg = POP; goto do_ ## tag; \ @@ -325,24 +365,33 @@ do_enter_function_L1: // Arg count is in L2. do_ ## tag ## _return0: if (cond) { L1 = 0; goto do_return; } NEXT; \ do_ ## tag ## _return1: if (cond) { L1 = 1; goto do_return; } NEXT - DO_JUMP(jump, L1, 1 == 1); - DO_JUMP(jz, L2, L1 == 0); - DO_JUMP(jnz, L2, L1 != 0); - DO_JUMP(jeq, L3, L1 == L2); - DO_JUMP(jne, L3, L1 != L2); - DO_JUMP(jlt, L3, L1 < L2); - DO_JUMP(jge, L3, L1 >= L2); - DO_JUMP(jgt, L3, L1 > L2); - DO_JUMP(jle, L3, L1 <= L2); - DO_JUMP(jltu, L3, ((git_uint32)L1 < (git_uint32)L2)); - DO_JUMP(jgeu, L3, ((git_uint32)L1 >= (git_uint32)L2)); - DO_JUMP(jgtu, L3, ((git_uint32)L1 > (git_uint32)L2)); - DO_JUMP(jleu, L3, ((git_uint32)L1 <= (git_uint32)L2)); + DO_JUMP(jump, L1, 1 == 1); + DO_JUMP(jz, L2, L1 == 0); + DO_JUMP(jnz, L2, L1 != 0); + DO_JUMP(jeq, L3, L1 == L2); + DO_JUMP(jne, L3, L1 != L2); + DO_JUMP(jlt, L3, L1 < L2); + DO_JUMP(jge, L3, L1 >= L2); + DO_JUMP(jgt, L3, L1 > L2); + DO_JUMP(jle, L3, L1 <= L2); + DO_JUMP(jltu, L3, ((git_uint32)L1 < (git_uint32)L2)); + DO_JUMP(jgeu, L3, ((git_uint32)L1 >= (git_uint32)L2)); + DO_JUMP(jgtu, L3, ((git_uint32)L1 > (git_uint32)L2)); + DO_JUMP(jleu, L3, ((git_uint32)L1 <= (git_uint32)L2)); + DO_JUMP(jisnan, L2, (((L1 & 0x7F800000) == 0x7F800000) && ((L1 & 0x007FFFFF) != 0))); + DO_JUMP(jisinf, L2, ((L1 == 0x7F800000) || (L1 == 0xFF800000))); + DO_JUMP(jflt, L3, DECODE_FLOAT(L1) < DECODE_FLOAT(L2)); + DO_JUMP(jfge, L3, DECODE_FLOAT(L1) >= DECODE_FLOAT(L2)); + DO_JUMP(jfgt, L3, DECODE_FLOAT(L1) > DECODE_FLOAT(L2)); + DO_JUMP(jfle, L3, DECODE_FLOAT(L1) <= DECODE_FLOAT(L2)); + DO_JUMP(jfeq, L4, floatCompare(L1, L2, L3) != 0); + DO_JUMP(jfne, L4, floatCompare(L1, L2, L3) == 0); #undef DO_JUMP do_jumpabs: L7 = L1; goto do_jump_abs_L7; NEXT; + do_goto_L4_from_L7: L1 = L4; goto do_goto_L1_from_L7; do_goto_L3_from_L7: L1 = L3; goto do_goto_L1_from_L7; do_goto_L2_from_L7: L1 = L2; goto do_goto_L1_from_L7; do_goto_L1_from_L7: @@ -505,7 +554,7 @@ finish_save_stub: do_catch_stub_local: CHECK_FREE(4); L7 = READ_PC; - memWrite32(L7, (sp-base+4)*4); + LOCAL(L7 / 4) = (sp-base+4)*4; PUSH (2); // DestType goto finish_catch_stub_addr_L7; @@ -952,8 +1001,8 @@ do_tailcall: case 0xC0: case 0xC1: // Function. // Retrieve arguments. - for (L1 += 8, L4 = 0; L4 < L2 ; ++L4, L1+=4) - args[L4] = memRead32(L1); + for (L1 += 8, L4 = L2; L4 > 0 ; --L4, L1+=4) + args[L4-1] = memRead32(L1); // Enter function. L1 = L3; goto do_enter_function_L1; @@ -1104,7 +1153,6 @@ do_tailcall: do_restart: // Reset game memory to its initial state. resetMemory(protectPos, protectSize); - resetUndo(); // Reset all the stack pointers. frame = locals = values = sp = base; @@ -1128,8 +1176,12 @@ do_tailcall: // The parameter is zero, so we should generate a // random number in "the full 32-bit range". The rand() // function might not cover the entire range, so we'll - // generate the high 16 bits and low 16 bits separately. + // generate the number with several calls. +#if (RAND_MAX < 0xffff) + S1 = rand() ^ (rand() << 12) ^ (rand() << 24); +#else S1 = (rand() & 0xffff) | (rand() << 16); +#endif } NEXT; @@ -1177,13 +1229,15 @@ do_tailcall: fatalError ("Negative number of elements to rotate in stkroll"); if (L1 > (sp - values)) fatalError ("Tried to rotate too many elements in stkroll"); + if (L1 == 0) + NEXT; // Now, let's normalise L2 into the range [0..L1). if (L2 >= 0) L2 = L2 % L1; else L2 = L1 - (-L2 % L1); // Avoid trivial cases. - if (L1 == 0 || L2 == 0 || L2 == L1) + if (L2 == 0 || L2 == L1) NEXT; L2 = L1 - L2; // The problem is reduced to swapping elements [0..L2) with @@ -1267,6 +1321,127 @@ do_tailcall: accel_set_param(L1, L2); NEXT; + // Floating point (new with glulx spec 3.1.2) + + do_numtof: + F1 = (git_float) L1; + S1 = ENCODE_FLOAT(F1); + NEXT; + + do_ftonumz: + F1 = DECODE_FLOAT(L1); + if (!signbit(F1)) { + if (isnan(F1) || isinf(F1) || (F1 > 2147483647.0)) + S1 = 0x7FFFFFFF; + else + S1 = (git_sint32) truncf(F1); + } else { + if (isnan(F1) || isinf(F1) || (F1 < -2147483647.0)) + S1 = 0x80000000; + else + S1 = (git_sint32) truncf(F1); + } + NEXT; + + do_ftonumn: + F1 = DECODE_FLOAT(L1); + if (!signbit(F1)) { + if (isnan(F1) || isinf(F1) || (F1 > 2147483647.0)) + S1 = 0x7FFFFFFF; + else + S1 = (git_sint32) roundf(F1); + } else { + if (isnan(F1) || isinf(F1) || (F1 < -2147483647.0)) + S1 = 0x80000000; + else + S1 = (git_sint32) roundf(F1); + } + NEXT; + + do_ceil: + F1 = ceilf(DECODE_FLOAT(L1)); + L2 = ENCODE_FLOAT(F1); + if ((L2 == 0x0) || (L2 == 0x80000000)) + L2 = L1 & 0x80000000; + S1 = L2; + NEXT; + + do_floor: + F1 = floorf(DECODE_FLOAT(L1)); + S1 = ENCODE_FLOAT(F1); + NEXT; + + do_sqrt: + F1 = sqrtf(DECODE_FLOAT(L1)); + S1 = ENCODE_FLOAT(F1); + NEXT; + + do_exp: + F1 = expf(DECODE_FLOAT(L1)); + S1 = ENCODE_FLOAT(F1); + NEXT; + + do_log: + F1 = logf(DECODE_FLOAT(L1)); + S1 = ENCODE_FLOAT(F1); + NEXT; + + do_pow: +#ifdef USE_OWN_POWF + F1 = git_powf(DECODE_FLOAT(L1), DECODE_FLOAT(L2)); +#else + F1 = powf(DECODE_FLOAT(L1), DECODE_FLOAT(L2)); +#endif + S1 = ENCODE_FLOAT(F1); + NEXT; + + do_atan2: + F1 = atan2f(DECODE_FLOAT(L1), DECODE_FLOAT(L2)); + S1 = ENCODE_FLOAT(F1); + NEXT; + + do_fmod: + F1 = DECODE_FLOAT(L1); + F2 = DECODE_FLOAT(L2); + F3 = fmodf(F1, F2); + F4 = (F1 - F3) / F2; + L4 = ENCODE_FLOAT(F4); + if ((L4 == 0) || (L4 == 0x80000000)) + L4 = (L1 ^ L2) & 0x80000000; + S1 = ENCODE_FLOAT(F3); + S2 = L4; + NEXT; + + do_sin: + F1 = sinf(DECODE_FLOAT(L1)); + S1 = ENCODE_FLOAT(F1); + NEXT; + + do_cos: + F1 = cosf(DECODE_FLOAT(L1)); + S1 = ENCODE_FLOAT(F1); + NEXT; + + do_tan: + F1 = tanf(DECODE_FLOAT(L1)); + S1 = ENCODE_FLOAT(F1); + NEXT; + + do_asin: + F1 = asinf(DECODE_FLOAT(L1)); + S1 = ENCODE_FLOAT(F1); + NEXT; + + do_acos: + F1 = acosf(DECODE_FLOAT(L1)); + S1 = ENCODE_FLOAT(F1); + NEXT; + + do_atan: + F1 = atanf(DECODE_FLOAT(L1)); + S1 = ENCODE_FLOAT(F1); + NEXT; + // Special Git opcodes do_git_setcacheram: diff --git a/interpreters/git/version.h b/interpreters/git/version.h index 8e542f8..ed0e7ab 100644 --- a/interpreters/git/version.h +++ b/interpreters/git/version.h @@ -1,4 +1,4 @@ // Automatically generated file -- do not edit! #define GIT_MAJOR 1 #define GIT_MINOR 2 -#define GIT_PATCH 4 +#define GIT_PATCH 8 diff --git a/interpreters/glulxe/Makefile.am b/interpreters/glulxe/Makefile.am index 6ddf4ed..70a9d63 100644 --- a/interpreters/glulxe/Makefile.am +++ b/interpreters/glulxe/Makefile.am @@ -1,7 +1,7 @@ pkglib_LTLIBRARIES = glulxe.la glulxe_la_SOURCES = accel.c exec.c files.c funcs.c gestalt.c gestalt.h glkop.c \ glulxe.h heap.c main.c opcodes.h operand.c osdepend.c profile.c search.c \ - serial.c string.c unixstrt.c vm.c + serial.c string.c unixstrt.c vm.c float.c glulxe_la_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/libchimara -DOS_UNIX glulxe_la_CFLAGS = -Wall -Wmissing-prototypes -Wstrict-prototypes -Wno-unused \ $(AM_CFLAGS) diff --git a/interpreters/glulxe/README b/interpreters/glulxe/README index 2ba34f4..e903d1e 100644 --- a/interpreters/glulxe/README +++ b/interpreters/glulxe/README @@ -1,5 +1,5 @@ Glulxe: the Glulx VM interpreter -Version 0.4.4 +Version 0.4.### Designed by Andrew Plotkin http://eblong.com/zarf/glulx/index.html @@ -12,9 +12,9 @@ the Glk home page at http://eblong.com/zarf/glk/index.html The Unix Makefile that comes with this package is designed to link any -of the Unix libraries (XGlk, GlkTerm, and CheapGlk.) You'll have to -go into the Makefile and set three variables to find the library. There -are instructions at the top of the Makefile. Then just type +of the Unix libraries (GlkTerm and CheapGlk.) You'll have to go into +the Makefile and set three variables to find the library. There are +instructions at the top of the Makefile. Then just type make glulxe @@ -24,14 +24,41 @@ That should suffice. When the program is built, type where "filename.ulx" is a Glulx game file to execute. -To build this program on a Macintosh, you'll need the MacGlk library. -See the instructions that come with that. It's fairly straightforward; -compile the library, the source code, and the "macstart.c" file. +To build this program with a Mac or Window interface, you'll need the +appropriate Glk library. -Ditto for Windows, using "winstart.c". +This program supports floating-point operations, which are implemented +using the standard C99 math functions. The Makefile uses "-lm" to link +these in. If your platform does not support these functions, you can +comment out the "#define FLOAT_SUPPORT" line in glulxe.h. * Version +###: + Abstracted powf() to an osdepend wrapper. (Needed for Windows.) + Fixed a @ceil bug, for some C math libraries. + +0.4.6: + Added floating-point math feature. + Updated winstart.c. (Thanks David Kinder.) + Fixed @random even more, on Windows. + @verify works right on game files with extended memory. + @getiosys works right when the two store operands are different + variable types. (E.g., one local and one global.) + +0.4.5: + VERIFY_MEMORY_ACCESS now detects writes to the ROM section of memory. + Fixed off-by-eight bug in @astorebit and @aloadbit with negative bit + numbers. + Fixed an obscure bug with division and modulo of $80000000. (Thanks + Evin Robertson.) + Fixed an extremely obscure problem with changing I/O mode in the middle + of printing a number. + Glk array/string operations are now checked for memory overflows + (though not for ROM writing). This generates a warning at present; + in the future, it will be a fatal error. + Better fix for the @random bug. + 0.4.4: Added profiling code, which is turned off by default. To compile it in, define VM_PROFILING in Makefile or in glulxe.h. @@ -86,8 +113,8 @@ Ditto for Windows, using "winstart.c". * Permissions -The source code in this package is copyright 1999 by Andrew Plotkin. You -may copy and distribute it freely, by any means and under any conditions, +The source code in this package is copyright 1999-2010 by Andrew Plotkin. +You may copy and distribute it freely, by any means and under any conditions, as long as the code and documentation is not changed. You may also incorporate this code into your own program and distribute that, or modify this code and use and distribute the modified version, as long as you retain diff --git a/interpreters/glulxe/exec.c b/interpreters/glulxe/exec.c index 03ac456..4dd3fcd 100644 --- a/interpreters/glulxe/exec.c +++ b/interpreters/glulxe/exec.c @@ -7,6 +7,10 @@ #include "glulxe.h" #include "opcodes.h" +#ifdef FLOAT_SUPPORT +#include +#endif /* FLOAT_SUPPORT */ + /* execute_loop(): The main interpreter loop. This repeats until the program is done. */ @@ -16,11 +20,14 @@ void execute_loop() int ix; glui32 opcode; operandlist_t *oplist; - instruction_t inst; + oparg_t inst[MAX_OPERANDS]; glui32 value, addr, val0, val1; glsi32 vals0, vals1; glui32 *arglist; glui32 arglistfix[3]; +#ifdef FLOAT_SUPPORT + gfloat32 valf, valf1, valf2; +#endif /* FLOAT_SUPPORT */ while (!done_executing) { @@ -66,7 +73,7 @@ void execute_loop() /* Based on the oplist structure, load the actual operand values into inst. This moves the PC up to the end of the instruction. */ - parse_operands(&inst, oplist); + parse_operands(inst, oplist); /* Perform the opcode. This switch statement is split in two, based on some paranoid suspicions about the ability of compilers to @@ -80,96 +87,114 @@ void execute_loop() break; case op_add: - value = inst.value[0] + inst.value[1]; - store_operand(inst.desttype, inst.value[2], value); + value = inst[0].value + inst[1].value; + store_operand(inst[2].desttype, inst[2].value, value); break; case op_sub: - value = inst.value[0] - inst.value[1]; - store_operand(inst.desttype, inst.value[2], value); + value = inst[0].value - inst[1].value; + store_operand(inst[2].desttype, inst[2].value, value); break; case op_mul: - value = inst.value[0] * inst.value[1]; - store_operand(inst.desttype, inst.value[2], value); + value = inst[0].value * inst[1].value; + store_operand(inst[2].desttype, inst[2].value, value); break; case op_div: - vals0 = inst.value[0]; - vals1 = inst.value[1]; + vals0 = inst[0].value; + vals1 = inst[1].value; if (vals1 == 0) fatal_error("Division by zero."); /* Since C doesn't guarantee the results of division of negative numbers, we carefully convert everything to positive values - first. */ - if (vals1 < 0) { - vals0 = (-vals0); - vals1 = (-vals1); - } - if (vals0 >= 0) { - value = vals0 / vals1; + first. They have to be unsigned values, too, otherwise the + 0x80000000 case goes wonky. */ + if (vals0 < 0) { + val0 = (-vals0); + if (vals1 < 0) { + val1 = (-vals1); + value = val0 / val1; + } + else { + val1 = vals1; + value = -(val0 / val1); + } } else { - value = -((-vals0) / vals1); + val0 = vals0; + if (vals1 < 0) { + val1 = (-vals1); + value = -(val0 / val1); + } + else { + val1 = vals1; + value = val0 / val1; + } } - store_operand(inst.desttype, inst.value[2], value); + store_operand(inst[2].desttype, inst[2].value, value); break; case op_mod: - vals0 = inst.value[0]; - vals1 = inst.value[1]; + vals0 = inst[0].value; + vals1 = inst[1].value; if (vals1 == 0) fatal_error("Division by zero doing remainder."); if (vals1 < 0) { - vals1 = (-vals1); + val1 = -vals1; } - if (vals0 >= 0) { - value = vals0 % vals1; + else { + val1 = vals1; + } + if (vals0 < 0) { + val0 = (-vals0); + value = -(val0 % val1); } else { - value = -((-vals0) % vals1); + val0 = vals0; + value = val0 % val1; } - store_operand(inst.desttype, inst.value[2], value); + store_operand(inst[2].desttype, inst[2].value, value); break; case op_neg: - vals0 = inst.value[0]; + vals0 = inst[0].value; value = (-vals0); - store_operand(inst.desttype, inst.value[1], value); + store_operand(inst[1].desttype, inst[1].value, value); break; case op_bitand: - value = (inst.value[0] & inst.value[1]); - store_operand(inst.desttype, inst.value[2], value); + value = (inst[0].value & inst[1].value); + store_operand(inst[2].desttype, inst[2].value, value); break; case op_bitor: - value = (inst.value[0] | inst.value[1]); - store_operand(inst.desttype, inst.value[2], value); + value = (inst[0].value | inst[1].value); + store_operand(inst[2].desttype, inst[2].value, value); break; case op_bitxor: - value = (inst.value[0] ^ inst.value[1]); - store_operand(inst.desttype, inst.value[2], value); + value = (inst[0].value ^ inst[1].value); + store_operand(inst[2].desttype, inst[2].value, value); break; case op_bitnot: - value = ~(inst.value[0]); - store_operand(inst.desttype, inst.value[1], value); + value = ~(inst[0].value); + store_operand(inst[1].desttype, inst[1].value, value); break; case op_shiftl: - vals0 = inst.value[1]; + vals0 = inst[1].value; if (vals0 < 0 || vals0 >= 32) value = 0; else - value = ((glui32)(inst.value[0]) << (glui32)vals0); - store_operand(inst.desttype, inst.value[2], value); + value = ((glui32)(inst[0].value) << (glui32)vals0); + store_operand(inst[2].desttype, inst[2].value, value); break; case op_ushiftr: - vals0 = inst.value[1]; + vals0 = inst[1].value; if (vals0 < 0 || vals0 >= 32) value = 0; else - value = ((glui32)(inst.value[0]) >> (glui32)vals0); - store_operand(inst.desttype, inst.value[2], value); + value = ((glui32)(inst[0].value) >> (glui32)vals0); + store_operand(inst[2].desttype, inst[2].value, value); break; case op_sshiftr: - vals0 = inst.value[1]; + vals0 = inst[1].value; if (vals0 < 0 || vals0 >= 32) { - if (inst.value[0] & 0x80000000) + if (inst[0].value & 0x80000000) value = 0xFFFFFFFF; else value = 0; @@ -178,13 +203,13 @@ void execute_loop() /* This is somewhat foolhardy -- C doesn't guarantee that right-shifting a signed value replicates the sign bit. We'll assume it for now. */ - value = ((glsi32)(inst.value[0]) >> (glsi32)vals0); + value = ((glsi32)(inst[0].value) >> (glsi32)vals0); } - store_operand(inst.desttype, inst.value[2], value); + store_operand(inst[2].desttype, inst[2].value, value); break; case op_jump: - value = inst.value[0]; + value = inst[0].value; /* fall through to PerformJump label. */ PerformJump: /* goto label for successful jumping... ironic, no? */ @@ -206,99 +231,99 @@ void execute_loop() break; case op_jz: - if (inst.value[0] == 0) { - value = inst.value[1]; + if (inst[0].value == 0) { + value = inst[1].value; goto PerformJump; } break; case op_jnz: - if (inst.value[0] != 0) { - value = inst.value[1]; + if (inst[0].value != 0) { + value = inst[1].value; goto PerformJump; } break; case op_jeq: - if (inst.value[0] == inst.value[1]) { - value = inst.value[2]; + if (inst[0].value == inst[1].value) { + value = inst[2].value; goto PerformJump; } break; case op_jne: - if (inst.value[0] != inst.value[1]) { - value = inst.value[2]; + if (inst[0].value != inst[1].value) { + value = inst[2].value; goto PerformJump; } break; case op_jlt: - vals0 = inst.value[0]; - vals1 = inst.value[1]; + vals0 = inst[0].value; + vals1 = inst[1].value; if (vals0 < vals1) { - value = inst.value[2]; + value = inst[2].value; goto PerformJump; } break; case op_jgt: - vals0 = inst.value[0]; - vals1 = inst.value[1]; + vals0 = inst[0].value; + vals1 = inst[1].value; if (vals0 > vals1) { - value = inst.value[2]; + value = inst[2].value; goto PerformJump; } break; case op_jle: - vals0 = inst.value[0]; - vals1 = inst.value[1]; + vals0 = inst[0].value; + vals1 = inst[1].value; if (vals0 <= vals1) { - value = inst.value[2]; + value = inst[2].value; goto PerformJump; } break; case op_jge: - vals0 = inst.value[0]; - vals1 = inst.value[1]; + vals0 = inst[0].value; + vals1 = inst[1].value; if (vals0 >= vals1) { - value = inst.value[2]; + value = inst[2].value; goto PerformJump; } break; case op_jltu: - val0 = inst.value[0]; - val1 = inst.value[1]; + val0 = inst[0].value; + val1 = inst[1].value; if (val0 < val1) { - value = inst.value[2]; + value = inst[2].value; goto PerformJump; } break; case op_jgtu: - val0 = inst.value[0]; - val1 = inst.value[1]; + val0 = inst[0].value; + val1 = inst[1].value; if (val0 > val1) { - value = inst.value[2]; + value = inst[2].value; goto PerformJump; } break; case op_jleu: - val0 = inst.value[0]; - val1 = inst.value[1]; + val0 = inst[0].value; + val1 = inst[1].value; if (val0 <= val1) { - value = inst.value[2]; + value = inst[2].value; goto PerformJump; } break; case op_jgeu: - val0 = inst.value[0]; - val1 = inst.value[1]; + val0 = inst[0].value; + val1 = inst[1].value; if (val0 >= val1) { - value = inst.value[2]; + value = inst[2].value; goto PerformJump; } break; case op_call: - value = inst.value[1]; + value = inst[1].value; arglist = pop_arguments(value, 0); - push_callstub(inst.desttype, inst.value[2]); - enter_function(inst.value[0], value, arglist); + push_callstub(inst[2].desttype, inst[2].value); + enter_function(inst[0].value, value, arglist); break; case op_return: leave_function(); @@ -306,120 +331,120 @@ void execute_loop() done_executing = TRUE; break; } - pop_callstub(inst.value[0]); + pop_callstub(inst[0].value); break; case op_tailcall: - value = inst.value[1]; + value = inst[1].value; arglist = pop_arguments(value, 0); leave_function(); - enter_function(inst.value[0], value, arglist); + enter_function(inst[0].value, value, arglist); break; case op_catch: - push_callstub(inst.desttype, inst.value[0]); - value = inst.value[1]; + push_callstub(inst[0].desttype, inst[0].value); + value = inst[1].value; val0 = stackptr; - store_operand(inst.desttype, inst.value[0], val0); + store_operand(inst[0].desttype, inst[0].value, val0); goto PerformJump; break; case op_throw: profile_fail("throw"); - value = inst.value[0]; - stackptr = inst.value[1]; + value = inst[0].value; + stackptr = inst[1].value; pop_callstub(value); break; case op_copy: - value = inst.value[0]; - store_operand(inst.desttype, inst.value[1], value); + value = inst[0].value; + store_operand(inst[1].desttype, inst[1].value, value); break; case op_copys: - value = inst.value[0]; - store_operand_s(inst.desttype, inst.value[1], value); + value = inst[0].value; + store_operand_s(inst[1].desttype, inst[1].value, value); break; case op_copyb: - value = inst.value[0]; - store_operand_b(inst.desttype, inst.value[1], value); + value = inst[0].value; + store_operand_b(inst[1].desttype, inst[1].value, value); break; case op_sexs: - val0 = inst.value[0]; + val0 = inst[0].value; if (val0 & 0x8000) val0 |= 0xFFFF0000; else val0 &= 0x0000FFFF; - store_operand(inst.desttype, inst.value[1], val0); + store_operand(inst[1].desttype, inst[1].value, val0); break; case op_sexb: - val0 = inst.value[0]; + val0 = inst[0].value; if (val0 & 0x80) val0 |= 0xFFFFFF00; else val0 &= 0x000000FF; - store_operand(inst.desttype, inst.value[1], val0); + store_operand(inst[1].desttype, inst[1].value, val0); break; case op_aload: - value = inst.value[0]; - value += 4 * inst.value[1]; + value = inst[0].value; + value += 4 * inst[1].value; val0 = Mem4(value); - store_operand(inst.desttype, inst.value[2], val0); + store_operand(inst[2].desttype, inst[2].value, val0); break; case op_aloads: - value = inst.value[0]; - value += 2 * inst.value[1]; + value = inst[0].value; + value += 2 * inst[1].value; val0 = Mem2(value); - store_operand(inst.desttype, inst.value[2], val0); + store_operand(inst[2].desttype, inst[2].value, val0); break; case op_aloadb: - value = inst.value[0]; - value += inst.value[1]; + value = inst[0].value; + value += inst[1].value; val0 = Mem1(value); - store_operand(inst.desttype, inst.value[2], val0); + store_operand(inst[2].desttype, inst[2].value, val0); break; case op_aloadbit: - value = inst.value[0]; - vals0 = inst.value[1]; + value = inst[0].value; + vals0 = inst[1].value; val1 = (vals0 & 7); if (vals0 >= 0) value += (vals0 >> 3); else - value -= ((-1 - vals0) >> 3); + value -= (1 + ((-1 - vals0) >> 3)); if (Mem1(value) & (1 << val1)) val0 = 1; else val0 = 0; - store_operand(inst.desttype, inst.value[2], val0); + store_operand(inst[2].desttype, inst[2].value, val0); break; case op_astore: - value = inst.value[0]; - value += 4 * inst.value[1]; - val0 = inst.value[2]; + value = inst[0].value; + value += 4 * inst[1].value; + val0 = inst[2].value; MemW4(value, val0); break; case op_astores: - value = inst.value[0]; - value += 2 * inst.value[1]; - val0 = inst.value[2]; + value = inst[0].value; + value += 2 * inst[1].value; + val0 = inst[2].value; MemW2(value, val0); break; case op_astoreb: - value = inst.value[0]; - value += inst.value[1]; - val0 = inst.value[2]; + value = inst[0].value; + value += inst[1].value; + val0 = inst[2].value; MemW1(value, val0); break; case op_astorebit: - value = inst.value[0]; - vals0 = inst.value[1]; + value = inst[0].value; + vals0 = inst[1].value; val1 = (vals0 & 7); if (vals0 >= 0) value += (vals0 >> 3); else - value -= ((-1 - vals0) >> 3); + value -= (1 + ((-1 - vals0) >> 3)); val0 = Mem1(value); - if (inst.value[2]) + if (inst[2].value) val0 |= (1 << val1); else val0 &= ~((glui32)(1 << val1)); @@ -428,14 +453,14 @@ void execute_loop() case op_stkcount: value = (stackptr - valstackbase) / 4; - store_operand(inst.desttype, inst.value[0], value); + store_operand(inst[0].desttype, inst[0].value, value); break; case op_stkpeek: - vals0 = inst.value[0] * 4; + vals0 = inst[0].value * 4; if (vals0 < 0 || vals0 >= (stackptr - valstackbase)) fatal_error("Stkpeek outside current stack range."); value = Stk4(stackptr - (vals0+4)); - store_operand(inst.desttype, inst.value[1], value); + store_operand(inst[1].desttype, inst[1].value, value); break; case op_stkswap: if (stackptr < valstackbase+8) { @@ -447,7 +472,7 @@ void execute_loop() StkW4(stackptr-8, val0); break; case op_stkcopy: - vals0 = inst.value[0]; + vals0 = inst[0].value; if (vals0 < 0) fatal_error("Negative operand in stkcopy."); if (vals0 == 0) @@ -464,8 +489,8 @@ void execute_loop() stackptr += vals0*4; break; case op_stkroll: - vals0 = inst.value[0]; - vals1 = inst.value[1]; + vals0 = inst[0].value; + vals1 = inst[1].value; if (vals0 < 0) fatal_error("Negative operand in stkroll."); if (stackptr < valstackbase+vals0*4) @@ -499,25 +524,25 @@ void execute_loop() case op_streamchar: profile_in(2, FALSE); - value = inst.value[0] & 0xFF; + value = inst[0].value & 0xFF; (*stream_char_handler)(value); profile_out(); break; case op_streamunichar: profile_in(2, FALSE); - value = inst.value[0]; + value = inst[0].value; (*stream_unichar_handler)(value); profile_out(); break; case op_streamnum: profile_in(2, FALSE); - vals0 = inst.value[0]; + vals0 = inst[0].value; stream_num(vals0, FALSE, 0); profile_out(); break; case op_streamstr: profile_in(2, FALSE); - stream_string(inst.value[0], 0, 0); + stream_string(inst[0].value, 0, 0); profile_out(); break; @@ -530,91 +555,91 @@ void execute_loop() switch (opcode) { case op_gestalt: - value = do_gestalt(inst.value[0], inst.value[1]); - store_operand(inst.desttype, inst.value[2], value); + value = do_gestalt(inst[0].value, inst[1].value); + store_operand(inst[2].desttype, inst[2].value, value); break; case op_debugtrap: - fatal_error_i("user debugtrap encountered.", inst.value[0]); + fatal_error_i("user debugtrap encountered.", inst[0].value); case op_jumpabs: - pc = inst.value[0]; + pc = inst[0].value; break; case op_callf: - push_callstub(inst.desttype, inst.value[1]); - enter_function(inst.value[0], 0, arglistfix); + push_callstub(inst[1].desttype, inst[1].value); + enter_function(inst[0].value, 0, arglistfix); break; case op_callfi: - arglistfix[0] = inst.value[1]; - push_callstub(inst.desttype, inst.value[2]); - enter_function(inst.value[0], 1, arglistfix); + arglistfix[0] = inst[1].value; + push_callstub(inst[2].desttype, inst[2].value); + enter_function(inst[0].value, 1, arglistfix); break; case op_callfii: - arglistfix[0] = inst.value[1]; - arglistfix[1] = inst.value[2]; - push_callstub(inst.desttype, inst.value[3]); - enter_function(inst.value[0], 2, arglistfix); + arglistfix[0] = inst[1].value; + arglistfix[1] = inst[2].value; + push_callstub(inst[3].desttype, inst[3].value); + enter_function(inst[0].value, 2, arglistfix); break; case op_callfiii: - arglistfix[0] = inst.value[1]; - arglistfix[1] = inst.value[2]; - arglistfix[2] = inst.value[3]; - push_callstub(inst.desttype, inst.value[4]); - enter_function(inst.value[0], 3, arglistfix); + arglistfix[0] = inst[1].value; + arglistfix[1] = inst[2].value; + arglistfix[2] = inst[3].value; + push_callstub(inst[4].desttype, inst[4].value); + enter_function(inst[0].value, 3, arglistfix); break; case op_getmemsize: - store_operand(inst.desttype, inst.value[0], endmem); + store_operand(inst[0].desttype, inst[0].value, endmem); break; case op_setmemsize: - value = change_memsize(inst.value[0], FALSE); - store_operand(inst.desttype, inst.value[1], value); + value = change_memsize(inst[0].value, FALSE); + store_operand(inst[1].desttype, inst[1].value, value); break; case op_getstringtbl: value = stream_get_table(); - store_operand(inst.desttype, inst.value[0], value); + store_operand(inst[0].desttype, inst[0].value, value); break; case op_setstringtbl: - stream_set_table(inst.value[0]); + stream_set_table(inst[0].value); break; case op_getiosys: stream_get_iosys(&val0, &val1); - store_operand(inst.desttype, inst.value[0], val0); - store_operand(inst.desttype, inst.value[1], val1); + store_operand(inst[0].desttype, inst[0].value, val0); + store_operand(inst[1].desttype, inst[1].value, val1); break; case op_setiosys: - stream_set_iosys(inst.value[0], inst.value[1]); + stream_set_iosys(inst[0].value, inst[1].value); break; case op_glk: profile_in(1, FALSE); - value = inst.value[1]; + value = inst[1].value; arglist = pop_arguments(value, 0); - val0 = perform_glk(inst.value[0], value, arglist); - store_operand(inst.desttype, inst.value[2], val0); + val0 = perform_glk(inst[0].value, value, arglist); + store_operand(inst[2].desttype, inst[2].value, val0); profile_out(); break; case op_random: - vals0 = inst.value[0]; + vals0 = inst[0].value; if (vals0 == 0) - value = glulx_random() ^ (glulx_random() << 16); + value = glulx_random(); else if (vals0 >= 1) value = glulx_random() % (glui32)(vals0); else value = -(glulx_random() % (glui32)(-vals0)); - store_operand(inst.desttype, inst.value[1], value); + store_operand(inst[1].desttype, inst[1].value, value); break; case op_setrandom: - glulx_setrandom(inst.value[0]); + glulx_setrandom(inst[0].value); break; case op_verify: value = perform_verify(); - store_operand(inst.desttype, inst.value[0], value); + store_operand(inst[0].desttype, inst[0].value, value); break; case op_restart: @@ -623,8 +648,8 @@ void execute_loop() break; case op_protect: - val0 = inst.value[0]; - val1 = val0 + inst.value[1]; + val0 = inst[0].value; + val1 = val0 + inst[1].value; if (val0 == val1) { val0 = 0; val1 = 0; @@ -634,14 +659,14 @@ void execute_loop() break; case op_save: - push_callstub(inst.desttype, inst.value[1]); - value = perform_save(find_stream_by_id(inst.value[0])); + push_callstub(inst[1].desttype, inst[1].value); + value = perform_save(find_stream_by_id(inst[0].value)); pop_callstub(value); break; case op_restore: profile_fail("restore"); - value = perform_restore(find_stream_by_id(inst.value[0])); + value = perform_restore(find_stream_by_id(inst[0].value)); if (value == 0) { /* We've succeeded, and the stack now contains the callstub saved during saveundo. Ignore this opcode's operand. */ @@ -651,12 +676,12 @@ void execute_loop() else { /* We've failed, so we must store the failure in this opcode's operand. */ - store_operand(inst.desttype, inst.value[1], value); + store_operand(inst[1].desttype, inst[1].value, value); } break; case op_saveundo: - push_callstub(inst.desttype, inst.value[0]); + push_callstub(inst[0].desttype, inst[0].value); value = perform_saveundo(); pop_callstub(value); break; @@ -673,7 +698,7 @@ void execute_loop() else { /* We've failed, so we must store the failure in this opcode's operand. */ - store_operand(inst.desttype, inst.value[0], value); + store_operand(inst[0].desttype, inst[0].value, value); } break; @@ -682,25 +707,25 @@ void execute_loop() break; case op_linearsearch: - value = linear_search(inst.value[0], inst.value[1], inst.value[2], - inst.value[3], inst.value[4], inst.value[5], inst.value[6]); - store_operand(inst.desttype, inst.value[7], value); + value = linear_search(inst[0].value, inst[1].value, inst[2].value, + inst[3].value, inst[4].value, inst[5].value, inst[6].value); + store_operand(inst[7].desttype, inst[7].value, value); break; case op_binarysearch: - value = binary_search(inst.value[0], inst.value[1], inst.value[2], - inst.value[3], inst.value[4], inst.value[5], inst.value[6]); - store_operand(inst.desttype, inst.value[7], value); + value = binary_search(inst[0].value, inst[1].value, inst[2].value, + inst[3].value, inst[4].value, inst[5].value, inst[6].value); + store_operand(inst[7].desttype, inst[7].value, value); break; case op_linkedsearch: - value = linked_search(inst.value[0], inst.value[1], inst.value[2], - inst.value[3], inst.value[4], inst.value[5]); - store_operand(inst.desttype, inst.value[6], value); + value = linked_search(inst[0].value, inst[1].value, inst[2].value, + inst[3].value, inst[4].value, inst[5].value); + store_operand(inst[6].desttype, inst[6].value, value); break; case op_mzero: { glui32 lx; - glui32 count = inst.value[0]; - addr = inst.value[1]; + glui32 count = inst[0].value; + addr = inst[1].value; for (lx=0; lx 2147483647.0)) + vals0 = 0x7FFFFFFF; + else + vals0 = (glsi32)(truncf(valf)); + } + else { + if (isnan(valf) || isinf(valf) || (valf < -2147483647.0)) + vals0 = 0x80000000; + else + vals0 = (glsi32)(truncf(valf)); + } + store_operand(inst[1].desttype, inst[1].value, vals0); + break; + case op_ftonumn: + valf = decode_float(inst[0].value); + if (!signbit(valf)) { + if (isnan(valf) || isinf(valf) || (valf > 2147483647.0)) + vals0 = 0x7FFFFFFF; + else + vals0 = (glsi32)(roundf(valf)); + } + else { + if (isnan(valf) || isinf(valf) || (valf < -2147483647.0)) + vals0 = 0x80000000; + else + vals0 = (glsi32)(roundf(valf)); + } + store_operand(inst[1].desttype, inst[1].value, vals0); + break; + + case op_fadd: + valf1 = decode_float(inst[0].value); + valf2 = decode_float(inst[1].value); + value = encode_float(valf1 + valf2); + store_operand(inst[2].desttype, inst[2].value, value); + break; + case op_fsub: + valf1 = decode_float(inst[0].value); + valf2 = decode_float(inst[1].value); + value = encode_float(valf1 - valf2); + store_operand(inst[2].desttype, inst[2].value, value); + break; + case op_fmul: + valf1 = decode_float(inst[0].value); + valf2 = decode_float(inst[1].value); + value = encode_float(valf1 * valf2); + store_operand(inst[2].desttype, inst[2].value, value); + break; + case op_fdiv: + valf1 = decode_float(inst[0].value); + valf2 = decode_float(inst[1].value); + value = encode_float(valf1 / valf2); + store_operand(inst[2].desttype, inst[2].value, value); + break; + + case op_fmod: + valf1 = decode_float(inst[0].value); + valf2 = decode_float(inst[1].value); + valf = fmodf(valf1, valf2); + val0 = encode_float(valf); + val1 = encode_float((valf1-valf) / valf2); + if (val1 == 0x0 || val1 == 0x80000000) { + /* When the quotient is zero, the sign has been lost in the + shuffle. We'll set that by hand, based on the original + arguments. */ + val1 = (inst[0].value ^ inst[1].value) & 0x80000000; + } + store_operand(inst[2].desttype, inst[2].value, val0); + store_operand(inst[3].desttype, inst[3].value, val1); + break; + + case op_floor: + valf = decode_float(inst[0].value); + value = encode_float(floorf(valf)); + store_operand(inst[1].desttype, inst[1].value, value); + break; + case op_ceil: + valf = decode_float(inst[0].value); + value = encode_float(ceilf(valf)); + if (value == 0x0 || value == 0x80000000) { + /* When the result is zero, the sign may have been lost in the + shuffle. (This is a bug in some C libraries.) We'll set the + sign by hand, based on the original argument. */ + value = inst[0].value & 0x80000000; + } + store_operand(inst[1].desttype, inst[1].value, value); + break; + + case op_sqrt: + valf = decode_float(inst[0].value); + value = encode_float(sqrtf(valf)); + store_operand(inst[1].desttype, inst[1].value, value); + break; + case op_log: + valf = decode_float(inst[0].value); + value = encode_float(logf(valf)); + store_operand(inst[1].desttype, inst[1].value, value); + break; + case op_exp: + valf = decode_float(inst[0].value); + value = encode_float(expf(valf)); + store_operand(inst[1].desttype, inst[1].value, value); + break; + case op_pow: + valf1 = decode_float(inst[0].value); + valf2 = decode_float(inst[1].value); + value = encode_float(glulx_powf(valf1, valf2)); + store_operand(inst[2].desttype, inst[2].value, value); + break; + + case op_sin: + valf = decode_float(inst[0].value); + value = encode_float(sinf(valf)); + store_operand(inst[1].desttype, inst[1].value, value); + break; + case op_cos: + valf = decode_float(inst[0].value); + value = encode_float(cosf(valf)); + store_operand(inst[1].desttype, inst[1].value, value); + break; + case op_tan: + valf = decode_float(inst[0].value); + value = encode_float(tanf(valf)); + store_operand(inst[1].desttype, inst[1].value, value); + break; + case op_asin: + valf = decode_float(inst[0].value); + value = encode_float(asinf(valf)); + store_operand(inst[1].desttype, inst[1].value, value); + break; + case op_acos: + valf = decode_float(inst[0].value); + value = encode_float(acosf(valf)); + store_operand(inst[1].desttype, inst[1].value, value); + break; + case op_atan: + valf = decode_float(inst[0].value); + value = encode_float(atanf(valf)); + store_operand(inst[1].desttype, inst[1].value, value); + break; + case op_atan2: + valf1 = decode_float(inst[0].value); + valf2 = decode_float(inst[1].value); + value = encode_float(atan2f(valf1, valf2)); + store_operand(inst[2].desttype, inst[2].value, value); + break; + + case op_jisinf: + /* Infinity is well-defined, so we don't bother to convert to + float. */ + val0 = inst[0].value; + if (val0 == 0x7F800000 || val0 == 0xFF800000) { + value = inst[1].value; + goto PerformJump; + } + break; + case op_jisnan: + /* NaN is well-defined, so we don't bother to convert to + float. */ + val0 = inst[0].value; + if ((val0 & 0x7F800000) == 0x7F800000 && (val0 & 0x007FFFFF) != 0) { + value = inst[1].value; + goto PerformJump; + } + break; + + case op_jfeq: + if ((inst[2].value & 0x7F800000) == 0x7F800000 && (inst[2].value & 0x007FFFFF) != 0) { + /* The delta is NaN, which can never match. */ + val0 = 0; + } + else if ((inst[0].value == 0x7F800000 || inst[0].value == 0xFF800000) + && (inst[1].value == 0x7F800000 || inst[1].value == 0xFF800000)) { + /* Both are infinite. Opposite infinities are never equal, + even if the difference is infinite, so this is easy. */ + val0 = (inst[0].value == inst[1].value); + } + else { + valf1 = decode_float(inst[1].value) - decode_float(inst[0].value); + valf2 = fabs(decode_float(inst[2].value)); + val0 = (valf1 <= valf2 && valf1 >= -valf2); + } + if (val0) { + value = inst[3].value; + goto PerformJump; + } + break; + case op_jfne: + if ((inst[2].value & 0x7F800000) == 0x7F800000 && (inst[2].value & 0x007FFFFF) != 0) { + /* The delta is NaN, which can never match. */ + val0 = 0; + } + else if ((inst[0].value == 0x7F800000 || inst[0].value == 0xFF800000) + && (inst[1].value == 0x7F800000 || inst[1].value == 0xFF800000)) { + /* Both are infinite. Opposite infinities are never equal, + even if the difference is infinite, so this is easy. */ + val0 = (inst[0].value == inst[1].value); + } + else { + valf1 = decode_float(inst[1].value) - decode_float(inst[0].value); + valf2 = fabs(decode_float(inst[2].value)); + val0 = (valf1 <= valf2 && valf1 >= -valf2); + } + if (!val0) { + value = inst[3].value; + goto PerformJump; + } + break; + + case op_jflt: + valf1 = decode_float(inst[0].value); + valf2 = decode_float(inst[1].value); + if (valf1 < valf2) { + value = inst[2].value; + goto PerformJump; + } + break; + case op_jfgt: + valf1 = decode_float(inst[0].value); + valf2 = decode_float(inst[1].value); + if (valf1 > valf2) { + value = inst[2].value; + goto PerformJump; + } + break; + case op_jfle: + valf1 = decode_float(inst[0].value); + valf2 = decode_float(inst[1].value); + if (valf1 <= valf2) { + value = inst[2].value; + goto PerformJump; + } + break; + case op_jfge: + valf1 = decode_float(inst[0].value); + valf2 = decode_float(inst[1].value); + if (valf1 >= valf2) { + value = inst[2].value; + goto PerformJump; + } + break; + +#endif /* FLOAT_SUPPORT */ + default: fatal_error_i("Executed unknown opcode.", opcode); } diff --git a/interpreters/glulxe/float.c b/interpreters/glulxe/float.c new file mode 100644 index 0000000..c3a6aca --- /dev/null +++ b/interpreters/glulxe/float.c @@ -0,0 +1,166 @@ +/* float.c: Glulxe code for floating-point operations + Designed by Andrew Plotkin + http://eblong.com/zarf/glulx/index.html +*/ + +#include "glk.h" +#include "glulxe.h" + +#ifdef FLOAT_SUPPORT + +#include + +/* This entire file is compiled out if the FLOAT_SUPPORT option is off. + (Because we probably can't define a gfloat32 in that case.) */ + +#ifndef FLOAT_NOT_NATIVE + +int init_float() +{ + /* Check and make sure the native float format is really + IEEE-754 single-precision. */ + + if (sizeof(gfloat32) != 4) { + fatal_error("gfloat32 is not 32 bits."); + return FALSE; + } + if (encode_float((gfloat32)(-1)) != 0xBF800000) { + fatal_error("The gfloat32 format of -1 did not match."); + return FALSE; + } + return TRUE; +} + +/* Encode and decode floats by reinterpret-casting. */ + +glui32 encode_float(gfloat32 val) +{ + glui32 res; + *(gfloat32 *)(&res) = val; + return res; +} + +gfloat32 decode_float(glui32 val) +{ + gfloat32 res; + *(glui32 *)(&res) = val; + return res; +} + +#else /* FLOAT_NOT_NATIVE */ + +int init_float() +{ + return TRUE; +} + +/* Encode and decode floats by a lot of annoying bit manipulation. + The following functions are adapted from code in Python + (Objects/floatobject.c). */ + +glui32 encode_float(gfloat32 val) +{ + gfloat32 absval; + glui32 sign; + int expo; + gfloat32 mant; + glui32 fbits; + + if (signbit(val)) { + sign = 0x80000000; + absval = -val; + } + else { + sign = 0x0; + absval = val; + } + + if (isinf(val)) { + return sign | 0x7f800000; /* infinity */ + } + + if (isnan(val)) { + return sign | 0x7fc00000; + } + + mant = frexpf(absval, &expo); + + /* Normalize mantissa to be in the range [1.0, 2.0) */ + if (0.5 <= mant && mant < 1.0) { + mant *= 2.0; + expo--; + } + else if (mant == 0.0) { + expo = 0; + } + else { + return sign | 0x7f800000; /* infinity */ + } + + if (expo >= 128) { + return sign | 0x7f800000; /* infinity */ + } + else if (expo < -126) { + /* Denormalized (very small) number */ + mant = ldexpf(mant, 126 + expo); + expo = 0; + } + else if (!(expo == 0 && mant == 0.0)) { + expo += 127; + mant -= 1.0; /* Get rid of leading 1 */ + } + + mant *= 8388608.0; /* 2^23 */ + fbits = (glui32)(mant + 0.5); /* round mant to nearest int */ + if (fbits >> 23) { + /* The carry propagated out of a string of 23 1 bits. */ + fbits = 0; + expo++; + if (expo >= 255) { + return sign | 0x7f800000; /* infinity */ + } + } + + return (sign) | ((glui32)(expo << 23)) | (fbits); +} + +gfloat32 decode_float(glui32 val) +{ + int sign; + int expo; + glui32 mant; + gfloat32 res; + + /* First byte */ + sign = ((val & 0x80000000) != 0); + expo = (val >> 23) & 0xFF; + mant = val & 0x7FFFFF; + + if (expo == 255) { + if (mant == 0) { + /* Infinity */ + return (sign ? (-INFINITY) : (INFINITY)); + } + else { + /* Not a number */ + return (sign ? (-NAN) : (NAN)); + } + } + + res = (gfloat32)mant / 8388608.0; + + if (expo == 0) { + expo = -126; + } + else { + res += 1.0; + expo -= 127; + } + res = ldexpf(res, expo); + + return (sign ? (-res) : (res)); +} + +#endif /* FLOAT_NOT_NATIVE */ + +#endif /* FLOAT_SUPPORT */ diff --git a/interpreters/glulxe/gestalt.c b/interpreters/glulxe/gestalt.c index c1c933f..87a8182 100644 --- a/interpreters/glulxe/gestalt.c +++ b/interpreters/glulxe/gestalt.c @@ -12,10 +12,10 @@ glui32 do_gestalt(glui32 val, glui32 val2) switch (val) { case gestulx_GlulxVersion: - return 0x00030101; /* Glulx spec version 3.1.1 */ + return 0x00030102; /* Glulx spec version 3.1.2 */ case gestulx_TerpVersion: - return 0x00000404; /* Glulxe version 0.4.4 */ + return 0x00000406; /* Glulxe version 0.4.6 */ case gestulx_ResizeMem: #ifdef FIXED_MEMSIZE @@ -63,6 +63,13 @@ glui32 do_gestalt(glui32 val, glui32 val2) return 1; /* We know this accelerated function. */ return 0; + case gestulx_Float: +#ifdef FLOAT_SUPPORT + return 1; /* We can do floating-point operations. */ +#else /* FLOAT_SUPPORT */ + return 0; /* The floating-point opcodes are not compiled in. */ +#endif /* FLOAT_SUPPORT */ + default: return 0; diff --git a/interpreters/glulxe/gestalt.h b/interpreters/glulxe/gestalt.h index 5c9b5be..b109ff8 100644 --- a/interpreters/glulxe/gestalt.h +++ b/interpreters/glulxe/gestalt.h @@ -17,6 +17,7 @@ #define gestulx_MAllocHeap (8) #define gestulx_Acceleration (9) #define gestulx_AccelFunc (10) +#define gestulx_Float (11) #endif /* _GESTALT_H */ diff --git a/interpreters/glulxe/glkop.c b/interpreters/glulxe/glkop.c index e9c5191..e761cd7 100644 --- a/interpreters/glulxe/glkop.c +++ b/interpreters/glulxe/glkop.c @@ -468,6 +468,17 @@ static void parse_glk_args(dispatch_splot_t *splot, char **proto, int depth, switch (typeclass) { case 'C': + /* This test checks for a giant array length, which is + deprecated. It displays a warning and cuts it down to + something reasonable. Future releases of this interpreter + may remove this test and go on to verify_array_addresses(), + which treats this case as a fatal error. */ + if (varglist[ix+1] > endmem + || varglist[ix]+varglist[ix+1] > endmem) { + nonfatal_warning_i("Memory access was much too long -- perhaps a print_to_array call with only one argument", varglist[ix+1]); + varglist[ix+1] = endmem - varglist[ix]; + } + verify_array_addresses(varglist[ix], varglist[ix+1], 1); garglist[gargnum].array = AddressOfArray(varglist[ix]); gargnum++; ix++; @@ -476,6 +487,13 @@ static void parse_glk_args(dispatch_splot_t *splot, char **proto, int depth, cx++; break; case 'I': + /* See comment above. */ + if (varglist[ix+1] > endmem/4 + || varglist[ix+1] > (endmem-varglist[ix])/4) { + nonfatal_warning_i("Memory access was much too long -- perhaps a print_to_array call with only one argument", varglist[ix+1]); + varglist[ix+1] = (endmem - varglist[ix]) / 4; + } + verify_array_addresses(varglist[ix], varglist[ix+1], 4); garglist[gargnum].array = CaptureIArray(varglist[ix], varglist[ix+1], passin); gargnum++; ix++; diff --git a/interpreters/glulxe/glulxe.h b/interpreters/glulxe/glulxe.h index 093ac6a..72308a6 100644 --- a/interpreters/glulxe/glulxe.h +++ b/interpreters/glulxe/glulxe.h @@ -35,6 +35,11 @@ typedef signed short glsi16; written to a data file called "profile-raw". */ /* #define VM_PROFILING (1) */ +/* Comment this definition to turn off floating-point support. You + might need to do this if you are building on a very limited platform + with no math library. */ +#define FLOAT_SUPPORT (1) + /* Some macros to read and write integers to memory, always in big-endian format. */ #define Read4(ptr) \ @@ -61,16 +66,18 @@ typedef signed short glsi16; #if VERIFY_MEMORY_ACCESS #define Verify(adr, ln) verify_address(adr, ln) +#define VerifyW(adr, ln) verify_address_write(adr, ln) #else #define Verify(adr, ln) (0) +#define VerifyW(adr, ln) (0) #endif /* VERIFY_MEMORY_ACCESS */ #define Mem1(adr) (Verify(adr, 1), Read1(memmap+(adr))) #define Mem2(adr) (Verify(adr, 2), Read2(memmap+(adr))) #define Mem4(adr) (Verify(adr, 4), Read4(memmap+(adr))) -#define MemW1(adr, vl) (Verify(adr, 1), Write1(memmap+(adr), (vl))) -#define MemW2(adr, vl) (Verify(adr, 2), Write2(memmap+(adr), (vl))) -#define MemW4(adr, vl) (Verify(adr, 4), Write4(memmap+(adr), (vl))) +#define MemW1(adr, vl) (VerifyW(adr, 1), Write1(memmap+(adr), (vl))) +#define MemW2(adr, vl) (VerifyW(adr, 2), Write2(memmap+(adr), (vl))) +#define MemW4(adr, vl) (VerifyW(adr, 4), Write4(memmap+(adr), (vl))) /* Macros to access values on the stack. These *must* be used with proper alignment! (That is, Stk4 and StkW4 must take @@ -94,16 +101,17 @@ typedef signed short glsi16; /* Some useful structures. */ -/* instruction_t: - Represents the list of operands to an instruction being executed. - (Yes, it's somewhat misnamed. Sorry.) We assume, for the indefinite - moment, that no opcode has more than 8 operands, and no opcode - has two "store" operands. +/* oparg_t: + Represents one operand value to an instruction being executed. The + code in exec.c assumes that no instruction has more than MAX_OPERANDS + of these. */ -typedef struct instruction_struct { +typedef struct oparg_struct { glui32 desttype; - glui32 value[8]; -} instruction_t; + glui32 value; +} oparg_t; + +#define MAX_OPERANDS (8) /* operandlist_t: Represents the operand structure of an opcode. @@ -165,6 +173,8 @@ extern void vm_restart(void); extern glui32 change_memsize(glui32 newlen, int internal); extern glui32 *pop_arguments(glui32 count, glui32 addr); extern void verify_address(glui32 addr, glui32 count); +extern void verify_address_write(glui32 addr, glui32 count); +extern void verify_array_addresses(glui32 addr, glui32 count, glui32 size); /* exec.c */ extern void execute_loop(void); @@ -173,7 +183,7 @@ extern void execute_loop(void); extern operandlist_t *fast_operandlist[0x80]; extern void init_operands(void); extern operandlist_t *lookup_operandlist(glui32 opcode); -extern void parse_operands(instruction_t *inst, operandlist_t *oplist); +extern void parse_operands(oparg_t *opargs, operandlist_t *oplist); extern void store_operand(glui32 desttype, glui32 destaddr, glui32 storeval); extern void store_operand_s(glui32 desttype, glui32 destaddr, glui32 storeval); extern void store_operand_b(glui32 desttype, glui32 destaddr, glui32 storeval); @@ -268,4 +278,31 @@ extern acceleration_func accel_get_func(glui32 addr); extern void accel_set_func(glui32 index, glui32 addr); extern void accel_set_param(glui32 index, glui32 val); +#ifdef FLOAT_SUPPORT + +/* You may have to edit the definition of gfloat32 to make sure it's really + a 32-bit floating-point type. */ +typedef float gfloat32; + +/* Uncomment this definition if your gfloat32 type is not a standard + IEEE-754 single-precision (32-bit) format. Normally, Glulxe assumes + that it can reinterpret-cast IEEE-754 int values into gfloat32 + values. If you uncomment this, Glulxe switches to lengthier + (but safer) encoding and decoding functions. */ +/* #define FLOAT_NOT_NATIVE (1) */ + +/* float.c */ +extern int init_float(void); +extern glui32 encode_float(gfloat32 val); +extern gfloat32 decode_float(glui32 val); + +/* Uncomment this definition if your powf() function does not support + all the corner cases specified by C99. If you uncomment this, + osdepend.c will provide a safer implementation of glulx_powf(). */ +/* #define FLOAT_COMPILE_SAFER_POWF (1) */ + +extern gfloat32 glulx_powf(gfloat32 val1, gfloat32 val2); + +#endif /* FLOAT_SUPPORT */ + #endif /* _GLULXE_H */ diff --git a/interpreters/glulxe/main.c b/interpreters/glulxe/main.c index 63aba89..665449a 100644 --- a/interpreters/glulxe/main.c +++ b/interpreters/glulxe/main.c @@ -33,6 +33,11 @@ void glk_main() } glulx_setrandom(0); +#ifdef FLOAT_SUPPORT + if (!init_float()) { + return; + } +#endif /* FLOAT_SUPPORT */ if (!init_dispatch()) { return; } diff --git a/interpreters/glulxe/opcodes.h b/interpreters/glulxe/opcodes.h index 8da99d2..dc4870c 100644 --- a/interpreters/glulxe/opcodes.h +++ b/interpreters/glulxe/opcodes.h @@ -109,5 +109,35 @@ #define op_accelfunc (0x180) #define op_accelparam (0x181) +#define op_numtof (0x190) +#define op_ftonumz (0x191) +#define op_ftonumn (0x192) +#define op_ceil (0x198) +#define op_floor (0x199) +#define op_fadd (0x1A0) +#define op_fsub (0x1A1) +#define op_fmul (0x1A2) +#define op_fdiv (0x1A3) +#define op_fmod (0x1A4) +#define op_sqrt (0x1A8) +#define op_exp (0x1A9) +#define op_log (0x1AA) +#define op_pow (0x1AB) +#define op_sin (0x1B0) +#define op_cos (0x1B1) +#define op_tan (0x1B2) +#define op_asin (0x1B3) +#define op_acos (0x1B4) +#define op_atan (0x1B5) +#define op_atan2 (0x1B6) +#define op_jfeq (0x1C0) +#define op_jfne (0x1C1) +#define op_jflt (0x1C2) +#define op_jfle (0x1C3) +#define op_jfgt (0x1C4) +#define op_jfge (0x1C5) +#define op_jisnan (0x1C8) +#define op_jisinf (0x1C9) + #endif /* _OPCODES_H */ diff --git a/interpreters/glulxe/operand.c b/interpreters/glulxe/operand.c index bb5a7a3..25d7c5a 100644 --- a/interpreters/glulxe/operand.c +++ b/interpreters/glulxe/operand.c @@ -46,10 +46,14 @@ static int array_LLL[3] = { modeform_Load, modeform_Load, modeform_Load }; static operandlist_t list_LLL = { 3, 4, array_LLL }; static operandlist_t list_2LS = { 2, 2, array_LS }; static operandlist_t list_1LS = { 2, 1, array_LS }; +static int array_LLLL[4] = { modeform_Load, modeform_Load, modeform_Load, modeform_Load }; +static operandlist_t list_LLLL = { 4, 4, array_LLLL }; static int array_SL[2] = { modeform_Store, modeform_Load }; static operandlist_t list_SL = { 2, 4, array_SL }; static int array_SS[2] = { modeform_Store, modeform_Store }; static operandlist_t list_SS = { 2, 4, array_SS }; +static int array_LLSS[4] = { modeform_Load, modeform_Load, modeform_Store, modeform_Store }; +static operandlist_t list_LLSS = { 4, 4, array_LLSS }; /* init_operands(): Set up the fast-lookup array of operandlists. This is called just @@ -228,6 +232,47 @@ operandlist_t *lookup_operandlist(glui32 opcode) case op_accelparam: return &list_LL; +#ifdef FLOAT_SUPPORT + + case op_numtof: + case op_ftonumz: + case op_ftonumn: + case op_ceil: + case op_floor: + case op_sqrt: + case op_exp: + case op_log: + return &list_LS; + case op_fadd: + case op_fsub: + case op_fmul: + case op_fdiv: + case op_pow: + case op_atan2: + return &list_LLS; + case op_fmod: + return &list_LLSS; + case op_sin: + case op_cos: + case op_tan: + case op_asin: + case op_acos: + case op_atan: + return &list_LS; + case op_jfeq: + case op_jfne: + return &list_LLLL; + case op_jflt: + case op_jfle: + case op_jfgt: + case op_jfge: + return &list_LLL; + case op_jisnan: + case op_jisinf: + return &list_LL; + +#endif /* FLOAT_SUPPORT */ + default: return NULL; } @@ -235,27 +280,31 @@ operandlist_t *lookup_operandlist(glui32 opcode) /* parse_operands(): Read the list of operands of an instruction, and put the values - in inst. This assumes that the PC is at the beginning of the + in args. This assumes that the PC is at the beginning of the operand mode list (right after an opcode number.) Upon return, the PC will be at the beginning of the next instruction. + + This also assumes that args points at an allocated array of + MAX_OPERANDS oparg_t structures. */ -void parse_operands(instruction_t *inst, operandlist_t *oplist) +void parse_operands(oparg_t *args, operandlist_t *oplist) { int ix; + oparg_t *curarg; int numops = oplist->num_ops; int argsize = oplist->arg_size; glui32 modeaddr = pc; int modeval; - inst->desttype = 0; - pc += (numops+1) / 2; - for (ix=0; ixdesttype = 0; + if ((ix & 1) == 0) { modeval = Mem1(modeaddr); mode = (modeval & 0x0F); @@ -385,20 +434,20 @@ void parse_operands(instruction_t *inst, operandlist_t *oplist) fatal_error("Unknown addressing mode in load operand."); } - inst->value[ix] = value; + curarg->value = value; } else { /* modeform_Store */ switch (mode) { case 0: /* discard value */ - inst->desttype = 0; - inst->value[ix] = 0; + curarg->desttype = 0; + curarg->value = 0; break; case 8: /* push on stack */ - inst->desttype = 3; - inst->value[ix] = 0; + curarg->desttype = 3; + curarg->value = 0; break; case 15: /* main memory RAM, four-byte address */ @@ -436,8 +485,8 @@ void parse_operands(instruction_t *inst, operandlist_t *oplist) WrMainMemAddr: /* cases 5, 6, 7 all wind up here. */ - inst->desttype = 1; - inst->value[ix] = addr; + curarg->desttype = 1; + curarg->value = addr; break; case 11: /* locals, four-byte address */ @@ -461,11 +510,11 @@ void parse_operands(instruction_t *inst, operandlist_t *oplist) A "strict mode" interpreter probably should. It's also illegal for addr to be less than zero or greater than the size of the locals segment. */ - inst->desttype = 2; + curarg->desttype = 2; /* We don't add localsbase here; the store address for desttype 2 is relative to the current locals segment, not an absolute stack position. */ - inst->value[ix] = addr; + curarg->value = addr; break; case 1: diff --git a/interpreters/glulxe/osdepend.c b/interpreters/glulxe/osdepend.c index 8c720dd..bcd4beb 100644 --- a/interpreters/glulxe/osdepend.c +++ b/interpreters/glulxe/osdepend.c @@ -51,7 +51,7 @@ void glulx_setrandom(glui32 seed) /* Return a random number in the range 0 to 2^32-1. */ glui32 glulx_random() { - return random(); + return (random() << 16) ^ random(); } #endif /* OS_UNIX */ @@ -88,7 +88,7 @@ static void lo_seed_random(glui32 seed); /* Return a random number in the range 0 to 2^32-1. */ glui32 glulx_random() { - return lo_random(); + return (lo_random() << 16) ^ lo_random(); } /* Set the random-number seed; zero means use as random a source as @@ -139,7 +139,7 @@ void glulx_setrandom(glui32 seed) /* Return a random number in the range 0 to 2^32-1. */ glui32 glulx_random() { - return rand(); + return (rand() << 24) ^ (rand() << 12) ^ rand(); } #endif /* WIN32 */ @@ -193,3 +193,33 @@ void glulx_sort(void *addr, int count, int size, { qsort(addr, count, size, (int (*)(const void *, const void *))comparefunc); } + +#ifdef FLOAT_SUPPORT +#include + +#ifdef FLOAT_COMPILE_SAFER_POWF + +/* This wrapper handles all special cases, even if the underlying + powf() function doesn't. */ +gfloat32 glulx_powf(gfloat32 val1, gfloat32 val2) +{ + if (val1 == 1.0f) + return 1.0f; + else if ((val2 == 0.0f) || (val2 == -0.0f)) + return 1.0f; + else if ((val1 == -1.0f) && isinf(val2)) + return 1.0f; + return powf(val1, val2); +} + +#else /* FLOAT_COMPILE_SAFER_POWF */ + +/* This is the standard powf() function, unaltered. */ +gfloat32 glulx_powf(gfloat32 val1, gfloat32 val2) +{ + return powf(val1, val2); +} + +#endif /* FLOAT_COMPILE_SAFER_POWF */ + +#endif /* FLOAT_SUPPORT */ diff --git a/interpreters/glulxe/serial.c b/interpreters/glulxe/serial.c index f7da9ec..a547652 100644 --- a/interpreters/glulxe/serial.c +++ b/interpreters/glulxe/serial.c @@ -1121,7 +1121,7 @@ glui32 perform_verify() if (newlen != 4) return 1; val = Read4(buf); - if (ix == 4) { + if (ix == 3) { if (len != val) return 1; } diff --git a/interpreters/glulxe/string.c b/interpreters/glulxe/string.c index 1f3b6f0..b3f119d 100644 --- a/interpreters/glulxe/string.c +++ b/interpreters/glulxe/string.c @@ -28,8 +28,6 @@ typedef struct cacheblock_struct { } u; } cacheblock_t; -static int never_cache_stringtable = FALSE; - /* The current string-decoding tables, broken out into a fast and easy-to-use form. */ static int tablecache_valid = FALSE; @@ -163,7 +161,8 @@ void stream_num(glsi32 val, int inmiddle, int charnum) switch (iosys_mode) { case iosys_Glk: - while (ix) { + ix -= charnum; + while (ix > 0) { ix--; glk_put_char(buf[ix]); } @@ -172,17 +171,14 @@ void stream_num(glsi32 val, int inmiddle, int charnum) case iosys_Filter: if (!inmiddle) { push_callstub(0x11, 0); + inmiddle = TRUE; } - if (charnum >= ix) { - res = pop_callstub_string(&jx); - if (res) - fatal_error("String-on-string call stub while printing number."); - } - else { + if (charnum < ix) { ival = buf[(ix-1)-charnum] & 0xFF; pc = val; push_callstub(0x12, charnum+1); enter_function(iosys_rock, 1, &ival); + return; } break; @@ -190,6 +186,12 @@ void stream_num(glsi32 val, int inmiddle, int charnum) break; } + + if (inmiddle) { + res = pop_callstub_string(&jx); + if (res) + fatal_error("String-on-string call stub while printing number."); + } } /* stream_string(): @@ -698,7 +700,10 @@ void stream_set_table(glui32 addr) /* Build cache. We can only do this if the table is entirely in ROM. */ glui32 tablelen = Mem4(stringtable); glui32 rootaddr = Mem4(stringtable+8); - if (stringtable+tablelen <= ramstart && !never_cache_stringtable) { + int cache_stringtable = (stringtable+tablelen <= ramstart); + /* cache_stringtable = TRUE; ...for testing only */ + /* cache_stringtable = FALSE; ...for testing only */ + if (cache_stringtable) { buildcache(&tablecache, rootaddr, CACHEBITS, 0); /* dumpcache(&tablecache, 1, 0); */ tablecache_valid = TRUE; diff --git a/interpreters/glulxe/unixstrt.c b/interpreters/glulxe/unixstrt.c index 4e62905..2e62f22 100644 --- a/interpreters/glulxe/unixstrt.c +++ b/interpreters/glulxe/unixstrt.c @@ -6,6 +6,7 @@ #include "glk.h" #include "glulxe.h" #include "glkstart.h" /* This comes with the Glk library. */ +#include /* The only command-line argument is the filename. */ glkunix_argumentlist_t glkunix_arguments[] = { @@ -21,9 +22,17 @@ int glkunix_startup_code(glkunix_startup_t *data) unsigned char buf[12]; int res; +#ifdef GARGLK + garglk_set_program_name("Glulxe 0.4.6"); + garglk_set_program_info("Glulxe 0.4.6 by Andrew Plotkin"); +#endif + if (data->argc <= 1) { init_err = "You must supply the name of a game file."; - return FALSE; +#ifdef GARGLK + return TRUE; /* Hack! but I want error message in glk window */ +#endif + return FALSE; } cx = data->argv[1]; @@ -34,6 +43,12 @@ int glkunix_startup_code(glkunix_startup_t *data) return TRUE; } +#ifdef GARGLK + cx = strrchr(data->argv[1], '/'); + if (!cx) cx = strrchr(data->argv[1], '\\'); + garglk_set_story_name(cx ? cx + 1 : data->argv[1]); +#endif + /* Now we have to check to see if it's a Blorb file. */ glk_stream_set_position(gamefile, 0, seekmode_Start); diff --git a/interpreters/glulxe/vm.c b/interpreters/glulxe/vm.c index 4a113f5..72fd868 100644 --- a/interpreters/glulxe/vm.c +++ b/interpreters/glulxe/vm.c @@ -287,7 +287,7 @@ glui32 *pop_arguments(glui32 count, glui32 addr) /* verify_address(): Make sure that count bytes beginning with addr all fall within the - current memory map. This is called at every memory access if + current memory map. This is called at every memory (read) access if VERIFY_MEMORY_ACCESS is defined in the header file. */ void verify_address(glui32 addr, glui32 count) @@ -301,3 +301,50 @@ void verify_address(glui32 addr, glui32 count) } } +/* verify_address_write(): + Make sure that count bytes beginning with addr all fall within RAM. + This is called at every memory write if VERIFY_MEMORY_ACCESS is + defined in the header file. +*/ +void verify_address_write(glui32 addr, glui32 count) +{ + if (addr < ramstart) + fatal_error_i("Memory write to read-only address", addr); + if (addr >= endmem) + fatal_error_i("Memory access out of range", addr); + if (count > 1) { + addr += (count-1); + if (addr >= endmem) + fatal_error_i("Memory access out of range", addr); + } +} + +/* verify_array_addresses(): + Make sure that an array of count elements (size bytes each), + starting at addr, does not fall outside the memory map. This goes + to some trouble that verify_address() does not, because we need + to be wary of lengths near -- or beyond -- 0x7FFFFFFF. +*/ +void verify_array_addresses(glui32 addr, glui32 count, glui32 size) +{ + glui32 bytecount; + if (addr >= endmem) + fatal_error_i("Memory access out of range", addr); + + if (count == 0) + return; + bytecount = count*size; + + /* If just multiplying by the element size overflows, we have trouble. */ + if (bytecount < count) + fatal_error_i("Memory access way too long", addr); + + /* If the byte length by itself is too long, or if its end overflows, + we have trouble. */ + if (bytecount > endmem || addr+bytecount < addr) + fatal_error_i("Memory access much too long", addr); + /* The simple length test. */ + if (addr+bytecount > endmem) + fatal_error_i("Memory access too long", addr); +} + diff --git a/interpreters/nitfol/Makefile.am b/interpreters/nitfol/Makefile.am index da18211..82791cb 100644 --- a/interpreters/nitfol/Makefile.am +++ b/interpreters/nitfol/Makefile.am @@ -10,7 +10,7 @@ dist_noinst_SCRIPTS = copying.awk opt2glkc.pl y2help.pl pkglib_LTLIBRARIES = nitfol.la nitfol_la_SOURCES = automap.c automap.h binary.h copying.h debug.c debug.h \ decode.c decode.h errmesg.c errmesg.h globals.c globals.h hash.c hash.h \ - iff.c iff.h infix.c infix.h inform.y inform.h init.c init.h io.c io.h \ + iff.c iff.h infix.c infix.h inform.y inform.h init.c init.h io.c nio.h \ linkevil.h main.c main.h nitfol.h no_blorb.h objects.c objects.h op_call.c \ op_call.h op_jmp.c op_jmp.h oplist.c oplist.h op_math.c op_math.h \ op_save.c op_save.h op_table.c op_table.h op_v6.c op_v6.h portfunc.c \ diff --git a/interpreters/nitfol/automap.c b/interpreters/nitfol/automap.c index 3322f9f..b2647b9 100644 --- a/interpreters/nitfol/automap.c +++ b/interpreters/nitfol/automap.c @@ -1,5 +1,6 @@ /* automap.c: main automapping code Copyright (C) 1999 Evin Robertson + Copyright (C) 2010 Jörg Walter This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -25,24 +26,27 @@ struct dirinfo { const char *name; int deltax, deltay; - char symbol; - char oneway; + glui32 symbol; + glui32 oneway; }; static const struct dirinfo dirways[] = { - { "n", 0, -1, '|', '^' }, - { "s", 0, 1, '|', 'v' }, - { "w", -1, 0, '-', '<' }, - { "e", 1, 0, '-', '>' }, - { "nw", -1, -1, '\\', '^' }, - { "se", 1, 1, '\\', 'v' }, - { "ne", 1, -1, '/', '^' }, - { "sw", -1, 1, '/', 'v' }, - { "up", 0, 0, 0, 0 }, - { "down", 0, 0, 0, 0 }, - { "wait", 0, 0, 0, 0 } + { "n", 0, -1, 0x2502, 0x2191 }, + { "s", 0, 1, 0x2502, 0x2193 }, + { "w", -1, 0, 0x2500, 0x2190 }, + { "e", 1, 0, 0x2500, 0x2192 }, + { "nw", -1, -1, 0x2572, 0x2196 }, + { "se", 1, 1, 0x2572, 0x2198 }, + { "ne", 1, -1, 0x2571, 0x2197 }, + { "sw", -1, 1, 0x2571, 0x2199 }, + { "up", 0, 0, 0x21c8, 0 }, + { "down", 0, 0, 0x21ca, 0 }, + { "wait", 0, 0, 0x21ba, 0 } }; +#define CROSS 0x253c +#define DIAGONAL_CROSS 0x2573 + #define NUM_EXITS (sizeof(dirways) / sizeof(*dirways)) #define REVERSE_DIR(dir) (dir ^ 1) @@ -53,9 +57,11 @@ static const struct dirinfo dirways[] = { #define DIR_DOWN (NUM_DIRS + 1) #define DIR_WAIT (NUM_DIRS + 2) +// "*udb@UDB+" char *roomsymbol = NULL; +glui32 uni_roomsymbol[9] = { 0x25cb, 0x25b3, 0x25bd, 0x25c7, 0x25cf, 0x25b2, 0x25bc, 0x25c6, 0x25cc }; -#define ROOM_SYMBOL(is_player, is_up, is_down, is_real) (is_real ? roomsymbol[(is_up != 0) | ((is_down != 0) << 1) | ((is_player != 0) << 2)] : roomsymbol[8]) +#define ROOM_SYMBOL(is_player, is_up, is_down, is_real) (is_real ? uni_roomsymbol[(is_up != 0) | ((is_down != 0) << 1) | ((is_player != 0) << 2)] : uni_roomsymbol[8]) typedef struct edge edge; @@ -153,14 +159,17 @@ void automap_kill(void) z_kill_window(automap_win); } - BOOL automap_init(int numobj, const char *location_exp) { + int i; automap_kill(); - if(!roomsymbol) - roomsymbol = n_strdup("*udb@UDB+"); - + if(roomsymbol) { + for (i = 0; i < strlen(roomsymbol); i++) { + uni_roomsymbol[i] = (unsigned char)roomsymbol[i]; + } + } + if(location_exp) loc_exp = n_strdup(location_exp); @@ -233,7 +242,7 @@ struct interlist { }; -static BOOL mymap_plot(int x, int y, char symbol, loc_node *node); +static BOOL mymap_plot(int x, int y, glui32 symbol, loc_node *node); static edge *automap_get_edge(loc_node *location, int dir); static void automap_calc_location(loc_node *location, loc_node *last, int x, int y); @@ -513,7 +522,7 @@ static void automap_adjust_length(loc_node *location, int dir, int newlen) static int mapwidth; static int mapheight; -static char *mymap = NULL; +static glui32 *mymap = NULL; static loc_node **mymapnode = NULL; static char mymap_read(int x, int y) @@ -525,19 +534,19 @@ static char mymap_read(int x, int y) } -static BOOL mymap_plot(int x, int y, char symbol, loc_node *node) +static BOOL mymap_plot(int x, int y, glui32 symbol, loc_node *node) { BOOL status = TRUE; - char *dest; + glui32 *dest; x += mapwidth / 2; y += mapheight / 2; if(x < 0 || x >= mapwidth || y < 0 || y >= mapheight) return status; dest = &mymap[x + y * mapwidth]; if(*dest != ' ') { - if((*dest=='/' && symbol=='\\') || (*dest=='\\' && symbol=='/')) - symbol = 'X'; - else if((*dest=='-' && symbol=='|') || (*dest=='|' && symbol=='-')) - symbol = '+'; + if((*dest==dirways[4].symbol && symbol==dirways[6].symbol) || (*dest==dirways[6].symbol && symbol==dirways[4].symbol)) + symbol = DIAGONAL_CROSS; + else if((*dest==dirways[0].symbol && symbol==dirways[2].symbol) || (*dest==dirways[2].symbol && symbol==dirways[0].symbol)) + symbol = CROSS; else status = FALSE; } else { @@ -564,7 +573,7 @@ void mymap_init(int width, int height) max = mapwidth * mapheight; n_free(mymap); n_free(mymapnode); - mymap = (char *) n_malloc(max); + mymap = (glui32 *) n_malloc(max*sizeof(*mymap)); mymapnode = (loc_node **) n_malloc(max * sizeof(*mymapnode)); for(i = 0; i < max; i++) { mymap[i] = ' '; @@ -634,7 +643,7 @@ static void mymap_draw(void) for(y = 0; y < mapheight/2; y++) { for(x = 0; x < mapwidth/2; x++) - glk_put_char(mymap[x+xoffset + (y+yoffset) * mapwidth]); + glk_put_char_uni(mymap[x+xoffset + (y+yoffset) * mapwidth]); } } @@ -693,7 +702,7 @@ static void automap_calc_location(loc_node *location, loc_node *last, int x, int y) { unsigned i; - char symbol; + glui32 symbol; loc_node *is_up, *is_down; if(!location) diff --git a/interpreters/nitfol/io.c b/interpreters/nitfol/io.c index dafa47a..ee29799 100644 --- a/interpreters/nitfol/io.c +++ b/interpreters/nitfol/io.c @@ -18,7 +18,7 @@ The author can be reached at nitfol@deja.com */ #include "nitfol.h" -#include "io.h" +#include "nio.h" #ifdef HEADER @@ -502,7 +502,7 @@ void z_kill_window(zwinid win) void kill_windows(void) { int i; - + for(i = 0; i < num_z_windows; i++) z_clear_window(&game_windows[i]); @@ -688,7 +688,7 @@ void z_flush_fixed(zwinid window) if(window->biggest_height > window->last_height && window->biggest_height > window->height) end_line = window->biggest_height; - + /* For v3 games, there's a callback function to draw the room name and score; if this is present, we start drawing at a lower position */ start_line = 0; @@ -697,8 +697,15 @@ void z_flush_fixed(zwinid window) end_line += start_line; o = glk_window_get_parent(window->win); - glk_window_set_arrangement(o, window->method, - end_line, window->win); +#if 0 + glk_window_get_size(window->win, &winx, &winy); + if (!(window->method & winmethod_Above || window->method & winmethod_Below) + || winy != end_line) + glk_window_set_arrangement(o, window->method, + end_line, window->win); +#else + glk_window_set_arrangement(o, window->method, end_line, window->win); +#endif glk_window_get_size(window->win, &winx, &winy); if(window->draw_callback) { diff --git a/interpreters/nitfol/io.h b/interpreters/nitfol/io.h deleted file mode 100644 index 980b4c5..0000000 --- a/interpreters/nitfol/io.h +++ /dev/null @@ -1,51 +0,0 @@ -/* This is a Cfunctions (version 0.24) generated header file. - Cfunctions is a free program for extracting headers from C files. - Get Cfunctions from `http://www.hayamasa.demon.co.uk/cfunctions'. */ - -/* This file was generated with: -`cfunctions -i io.c' */ -#ifndef CFH_IO_H -#define CFH_IO_H - -/* From `io.c': */ -typedef struct z_window * zwinid; -extern BOOL is_fixed; -extern glsi32 bgcolortable[]; -extern glsi32 fgcolortable[]; -void set_glk_stream_current (void); -void draw_intext_picture (zwinid window , glui32 picture , glui32 alignment ); -void draw_picture (zwinid window , glui32 picture , glui32 x , glui32 y ); -void showstuff (const char *title , const char *type , const char *message , offset number ); -void init_lower (zwinid *lower ); -void init_upper (zwinid *upper ); -void z_init_windows (BOOL dofixed , glui32 ( *draw_callback ) ( winid_t , glui32 , glui32 ) , BOOL ( *mouse_callback ) ( BOOL , winid_t , glui32 , glui32 ) , glui32 maxwidth , glui32 maxheight , zwinid *upper , zwinid *lower ); -zwinid z_split_screen (glui32 wintype , glui32 method , glui32 ( *draw_callback ) ( winid_t , glui32 , glui32 ) , BOOL ( *mouse_callback ) ( BOOL , winid_t , glui32 , glui32 ) ); -void z_kill_window (zwinid win ); -void kill_windows (void); -void free_windows (void); -zwinid z_find_win (winid_t win ); -void z_pause_timed_input (zwinid window ); -void z_flush_all_windows (void); -void z_draw_all_windows (void); -void z_flush_fixed (zwinid window ); -void z_flush_text (zwinid window ); -void z_flush_graphics (zwinid window ); -void z_print_number (zwinid window , int number ); -void z_put_char (zwinid window , unsigned c ); -void z_setxy (zwinid window , zword x , zword y ); -void z_getxy (zwinid window , zword *x , zword *y ); -void z_getsize (zwinid window , unsigned *width , unsigned *height ); -void z_find_size (glui32 *wid , glui32 *hei ); -void z_set_height (zwinid window , unsigned height ); -void z_set_color (zwinid window , unsigned fore , unsigned back ); -void z_set_style (zwinid window , int style ); -void set_fixed (BOOL p ); -void z_set_transcript (zwinid window , strid_t stream ); -void z_clear_window (zwinid window ); -void z_erase_line (zwinid window ); -void z_wait_for_key (zwinid window ); -zwinid check_valid_for_input (zwinid window ); -int z_read (zwinid window , char *dest , unsigned maxlen , unsigned initlen , zword timer , BOOL ( *timer_callback ) ( zword ) , zword timer_arg , unsigned char *terminator ); -zword z_read_char (zwinid window , zword timer , BOOL ( *timer_callback ) ( zword ) , zword timer_arg ); - -#endif /* CFH_IO_H */ diff --git a/interpreters/nitfol/main.c b/interpreters/nitfol/main.c index c6d555c..ce0ceac 100644 --- a/interpreters/nitfol/main.c +++ b/interpreters/nitfol/main.c @@ -87,7 +87,8 @@ int game_use_file(strid_t file) void glk_main(void) { - if(!current_zfile) { + if(!current_zfile) + { winid_t tempwin; tempwin = glk_window_open(0, 0, 100, wintype_TextBuffer, 0); glk_set_window(tempwin); diff --git a/interpreters/nitfol/nio.h b/interpreters/nitfol/nio.h new file mode 100644 index 0000000..980b4c5 --- /dev/null +++ b/interpreters/nitfol/nio.h @@ -0,0 +1,51 @@ +/* This is a Cfunctions (version 0.24) generated header file. + Cfunctions is a free program for extracting headers from C files. + Get Cfunctions from `http://www.hayamasa.demon.co.uk/cfunctions'. */ + +/* This file was generated with: +`cfunctions -i io.c' */ +#ifndef CFH_IO_H +#define CFH_IO_H + +/* From `io.c': */ +typedef struct z_window * zwinid; +extern BOOL is_fixed; +extern glsi32 bgcolortable[]; +extern glsi32 fgcolortable[]; +void set_glk_stream_current (void); +void draw_intext_picture (zwinid window , glui32 picture , glui32 alignment ); +void draw_picture (zwinid window , glui32 picture , glui32 x , glui32 y ); +void showstuff (const char *title , const char *type , const char *message , offset number ); +void init_lower (zwinid *lower ); +void init_upper (zwinid *upper ); +void z_init_windows (BOOL dofixed , glui32 ( *draw_callback ) ( winid_t , glui32 , glui32 ) , BOOL ( *mouse_callback ) ( BOOL , winid_t , glui32 , glui32 ) , glui32 maxwidth , glui32 maxheight , zwinid *upper , zwinid *lower ); +zwinid z_split_screen (glui32 wintype , glui32 method , glui32 ( *draw_callback ) ( winid_t , glui32 , glui32 ) , BOOL ( *mouse_callback ) ( BOOL , winid_t , glui32 , glui32 ) ); +void z_kill_window (zwinid win ); +void kill_windows (void); +void free_windows (void); +zwinid z_find_win (winid_t win ); +void z_pause_timed_input (zwinid window ); +void z_flush_all_windows (void); +void z_draw_all_windows (void); +void z_flush_fixed (zwinid window ); +void z_flush_text (zwinid window ); +void z_flush_graphics (zwinid window ); +void z_print_number (zwinid window , int number ); +void z_put_char (zwinid window , unsigned c ); +void z_setxy (zwinid window , zword x , zword y ); +void z_getxy (zwinid window , zword *x , zword *y ); +void z_getsize (zwinid window , unsigned *width , unsigned *height ); +void z_find_size (glui32 *wid , glui32 *hei ); +void z_set_height (zwinid window , unsigned height ); +void z_set_color (zwinid window , unsigned fore , unsigned back ); +void z_set_style (zwinid window , int style ); +void set_fixed (BOOL p ); +void z_set_transcript (zwinid window , strid_t stream ); +void z_clear_window (zwinid window ); +void z_erase_line (zwinid window ); +void z_wait_for_key (zwinid window ); +zwinid check_valid_for_input (zwinid window ); +int z_read (zwinid window , char *dest , unsigned maxlen , unsigned initlen , zword timer , BOOL ( *timer_callback ) ( zword ) , zword timer_arg , unsigned char *terminator ); +zword z_read_char (zwinid window , zword timer , BOOL ( *timer_callback ) ( zword ) , zword timer_arg ); + +#endif /* CFH_IO_H */ diff --git a/interpreters/nitfol/nitfol.h b/interpreters/nitfol/nitfol.h index 3c2cd6c..319a536 100644 --- a/interpreters/nitfol/nitfol.h +++ b/interpreters/nitfol/nitfol.h @@ -23,6 +23,7 @@ #include /* For NULL, rand, srand */ #include /* For time() */ #include /* for isspace, isgraph, etc. */ +#include #include "glk.h" #define GLK_EOF ((glsi32) -1) @@ -30,12 +31,29 @@ #define NITFOL_MINOR 5 /* Change these next few typedefs depending on your compiler */ -#include -typedef uint8_t zbyte; +#if UCHAR_MAX==0xff +typedef unsigned char zbyte; +#else +#error "Can't find an 8-bit integer type" +#endif #ifdef FAST_SHORT -typedef uint16_t zword; -typedef uint32_t offset; + +#if SHRT_MAX==0x7fff +typedef unsigned short zword; +#elif INT_MAX==0x7fff +typedef unsigned int zword; +#else +#error "Can't find a 16-bit integer type" +#endif + +#if INT_MAX==0x7fffffff +typedef unsigned int offset; +#elif LONG_MAX==0x7fffffff +typedef unsigned long offset; +#else +#error "Can't find a 32-bit integer type" +#endif #ifdef TWOS16SHORT #define FAST_TWOS16SHORT @@ -44,11 +62,28 @@ typedef uint32_t offset; #else #ifdef FAST_SIGNED -typedef int32_t zword; -typedef int32_t offset; +#if INT_MAX==0x7fffffff +typedef int zword; +typedef int offset; +#elif LONG_MAX==0x7fffffff +typedef long zword; +typedef long offset; #else -typedef uint32_t zword; /* Needs to be >= real zword */ -typedef uint32_t offset; +#error "Can't find a 32-bit integer type" +#endif + +#else + +#if INT_MAX==0x7fffffff +typedef unsigned int zword; +typedef unsigned int offset; +#elif LONG_MAX==0x7fffffff +typedef unsigned long zword; +typedef unsigned long offset; +#else +#error "Can't find a 32-bit integer type" +#endif + #endif #endif @@ -289,7 +324,7 @@ typedef enum { OBJ_GET_INFO, OBJ_RECEIVE, OBJ_MOVE } watchinfo; #include "decode.h" #include "main.h" -#include "io.h" +#include "nio.h" #include "z_io.h" #include "no_snd.h" diff --git a/interpreters/nitfol/nitfol.opt b/interpreters/nitfol/nitfol.opt index 100bb40..6de0f53 100644 --- a/interpreters/nitfol/nitfol.opt +++ b/interpreters/nitfol/nitfol.opt @@ -64,7 +64,7 @@ Removes an alias previously added by -alias. Useful for removing aliases in pre "Random seed" random r "Set random seed" number 0 { faked_random_seed = number; } Normally the random number generator is initialized with the time of day. If this option is used with a non-zero argument, the given number will be used to initialize the generator and for @code{@@random 0}. -"Automap symbols" mapsym - "Specify mapping glyphs" string "*udb@UDB+" { n_free(roomsymbol); roomsymbol = n_strdup(string); } +"Automap symbols" mapsym - "Specify mapping glyphs" string "" { n_free(roomsymbol); roomsymbol = n_strdup(string); } Nitfol draws maps using ASCII characters; you can choose which characters it uses to draw rooms. Defaults to @samp{*udb@@UDB+}, which is, in order: empty room, room with down exit, room with up exit, room with up and down exits, room with player, room with player and up exit, room with player and down exit, room with player and up and down exits, bend symbol. "Automap size" mapsize - "Specify map size" number 12 { automap_size = number; } diff --git a/libchimara/garglk.c b/libchimara/garglk.c index 6beb20e..e7333a6 100644 --- a/libchimara/garglk.c +++ b/libchimara/garglk.c @@ -158,6 +158,14 @@ garglk_unput_string_uni(glui32 *str) WARNING(_("Not implemented")); } +/* TODO document */ +void +garglk_set_zcolors_stream(strid_t str, glui32 fg, glui32 bg) +{ + VALID_STREAM(str, return); + WARNING(_("Not implemented")); +} + /** * garglk_set_zcolors: * @fg: one of the zcolor_ constants. @@ -178,7 +186,7 @@ garglk_set_zcolors(glui32 fg, glui32 bg) g_return_if_fail(glk_data->current_stream != NULL); g_return_if_fail(glk_data->current_stream->window != NULL); - WARNING(_("Not implemented")); + garglk_set_zcolors_stream(glk_data->current_stream, fg, bg); } static void @@ -187,6 +195,17 @@ apply_reverse_color(GtkTextTag *tag, gpointer data) g_object_set_data( G_OBJECT(tag), "reverse_color", data ); } +/* TODO document */ +void +garglk_set_reversevideo_stream(strid_t str, glui32 reverse) +{ + VALID_STREAM(str, return); + + GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(str->window->widget) ); + GtkTextTagTable *tags = gtk_text_buffer_get_tag_table(buffer); + gtk_text_tag_table_foreach( tags, apply_reverse_color, GINT_TO_POINTER(reverse) ); +} + /** * garglk_set_reversevideo: * @reverse: nonzero for reverse colors, zero for normal colors. @@ -202,7 +221,5 @@ garglk_set_reversevideo(glui32 reverse) g_return_if_fail(glk_data->current_stream != NULL); g_return_if_fail(glk_data->current_stream->window != NULL); - GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(glk_data->current_stream->window->widget) ); - GtkTextTagTable *tags = gtk_text_buffer_get_tag_table(buffer); - gtk_text_tag_table_foreach( tags, apply_reverse_color, GINT_TO_POINTER(reverse) ); + garglk_set_reversevideo_stream(glk_data->current_stream, reverse); } diff --git a/libchimara/garglk.h b/libchimara/garglk.h index 1ca8fbe..7396814 100644 --- a/libchimara/garglk.h +++ b/libchimara/garglk.h @@ -22,123 +22,41 @@ extern void garglk_set_program_info(const char *info); extern void garglk_set_story_name(const char *name); /* - These functions are not implemented even in Gargoyle. Looks like they were - planned, but never added. + This function is not implemented even in Gargoyle. Looks like it was planned, + but never added. extern void garglk_set_config(const char *name); - -#define garglk_font_Roman (0) -#define garglk_font_Italic (1) -#define garglk_font_Bold (2) -#define garglk_font_BoldItalic (3) -#define garglk_font_MonoRoman (4) -#define garglk_font_MonoItalic (5) -#define garglk_font_MonoBold (6) -#define garglk_font_MonoBoldItalic (7) - -#define garglk_color_White (0) -#define garglk_color_Red (1) -#define garglk_color_Green (2) -#define garglk_color_Blue (3) -#define garglk_color_Cyan (4) -#define garglk_color_Magenta (5) -#define garglk_color_Yellow (6) -#define garglk_color_Black (7) - -extern void garglk_set_style_font(glui32 font); -extern void garglk_set_style_stream_font(strid_t str, glui32 font); -extern void garglk_set_style_color(glui32 bg, glui32 fg); -extern void garglk_set_style_stream_color(strid_t str, glui32 bg, glui32 fg); */ /* JM: functions added to support Z-machine features that aren't in the Glk standard */ extern void garglk_set_line_terminators(winid_t win, const glui32 *keycodes, glui32 numkeycodes); +/* garglk_unput_string - removes the specified string from the end of the output buffer, if + * indeed it is there. */ extern void garglk_unput_string(char *str); extern void garglk_unput_string_uni(glui32 *str); +/* TODO document */ +#define zcolor_Transparent (-4) +/* TODO document */ +#define zcolor_Cursor (-3) /** * zcolor_Current: * * Z-machine color constant representing the current color. */ -#define zcolor_Current (0) +#define zcolor_Current (-2) /** * zcolor_Default: * * Z-machine color constant representing the default color. */ -#define zcolor_Default (1) -/** - * zcolor_Black: - * - * Z-machine color constant representing black (0x000000). - */ -#define zcolor_Black (2) -/** - * zcolor_Red: - * - * Z-machine color constant representing red (0x0000E8). - */ -#define zcolor_Red (3) -/** - * zcolor_Green: - * - * Z-machine color constant representing green (0x00D000). - */ -#define zcolor_Green (4) -/** - * zcolor_Yellow: - * - * Z-machine color constant representing yellow (0x00E8E8). - */ -#define zcolor_Yellow (5) -/** - * zcolor_Blue: - * - * Z-machine color constant representing blue (0xB06800). - */ -#define zcolor_Blue (6) -/** - * zcolor_Magenta: - * - * Z-machine color constant representing magenta (0xFF00FF). - */ -#define zcolor_Magenta (7) -/** - * zcolor_Cyan: - * - * Z-machine color constant representing cyan (0xE8E800). - */ -#define zcolor_Cyan (8) -/** - * zcolor_White: - * - * Z-machine color constant representing white (0xFFFFFF). - */ -#define zcolor_White (9) -/** - * zcolor_LightGrey: - * - * Z-machine color constant representing light grey (0xB0B0B0). - */ -#define zcolor_LightGrey (10) -/** - * zcolor_MediumGrey: - * - * Z-machine color constant representing grey (0x888888). - */ -#define zcolor_MediumGrey (11) -/** - * zcolor_DarkGrey: - * - * Z-machine color constant representing dark grey (0x585858). - */ -#define zcolor_DarkGrey (12) -#define zcolor_NUMCOLORS (13) +#define zcolor_Default (-1) extern void garglk_set_zcolors(glui32 fg, glui32 bg); +extern void garglk_set_zcolors_stream(strid_t str, glui32 fg, glui32 bg); extern void garglk_set_reversevideo(glui32 reverse); +extern void garglk_set_reversevideo_stream(strid_t str, glui32 reverse); /* non standard keycodes */ /** @@ -152,5 +70,9 @@ extern void garglk_set_reversevideo(glui32 reverse); * garglk_set_line_terminators(). */ #define keycode_Erase (0xffffef7f) +/* TODO document */ +#define keycode_MouseWheelUp (0xffffeffe) +/* TODO document */ +#define keycode_MouseWheelDown (0xffffefff) #endif /* __GARGLK_H__ */