[Scummvm-git-logs] scummvm master -> 4ff974778cdd81b6f6b528a1a96667764b12f70e

dreammaster paulfgilbert at gmail.com
Thu Apr 18 05:48:24 CEST 2019


This automated email contains information about 15 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
8e11a14939 GLK: GLULXE: Set up method definitions, glkop methods
c3ebb5e68a GLK: GLULXE: Added accel methods
6307fd08ce GLK: GLULXE: Added exec methods
4f1387317f GLK: GLULXE: Added funcs methods
0b628784bd GLK: GLULXE: Added gestalt methods
9ceb83972a GLK: GLULXE: Added heap methods
4ee1b98b86 GLK: GLULXE: Add operand methods
a6cf55862d GLK: GLULXE: Add search methods
1c46490183 GLK: GLULXE: Add serial methods
5965b02bca GLK: GLULXE: Add string methods
ee8362cc07 GLK: GLULXE: Added vm methods
936b973153 GLK: GLULXE: Add miscellaneous missing methods
105c9cb885 GLK: GLULXE: Remove gamefile to use proper _gameFile
427e051f6a GLK: GLULXE: astyle formatting
4ff974778c GLK: GLULXE: Fix reading game header information


Commit: 8e11a14939365c3a2994602b5db11a4fe6e8eaac
    https://github.com/scummvm/scummvm/commit/8e11a14939365c3a2994602b5db11a4fe6e8eaac
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-04-17T20:46:06-07:00

Commit Message:
GLK: GLULXE: Set up method definitions, glkop methods

Changed paths:
  A engines/glk/glulxe/glulxe_types.h
  R engines/glk/glulxe/glkop.h
    engines/glk/glk_api.cpp
    engines/glk/glk_api.h
    engines/glk/glk_dispa.cpp
    engines/glk/glk_types.h
    engines/glk/glulxe/glkop.cpp
    engines/glk/glulxe/glulxe.cpp
    engines/glk/glulxe/glulxe.h
    engines/glk/sound.cpp
    engines/glk/sound.h
    engines/glk/streams.cpp
    engines/glk/streams.h


diff --git a/engines/glk/glk_api.cpp b/engines/glk/glk_api.cpp
index 61c7a55..e2f3117 100644
--- a/engines/glk/glk_api.cpp
+++ b/engines/glk/glk_api.cpp
@@ -36,7 +36,8 @@
 namespace Glk {
 
 GlkAPI::GlkAPI(OSystem *syst, const GlkGameDescription &gameDesc) :
-	GlkEngine(syst, gameDesc), _gliFirstEvent(false) {
+		GlkEngine(syst, gameDesc), _gliFirstEvent(false), gli_register_obj(nullptr), 
+		gli_unregister_obj(nullptr), gli_register_arr(nullptr), gli_unregister_arr(nullptr) {
 	// Set uppercase/lowercase tables
 	int ix, res;
 	for (ix = 0; ix < 256; ix++) {
diff --git a/engines/glk/glk_api.h b/engines/glk/glk_api.h
index 9e4a230..e54cd3c 100644
--- a/engines/glk/glk_api.h
+++ b/engines/glk/glk_api.h
@@ -52,6 +52,11 @@ private:
 	bool _gliFirstEvent;
 	unsigned char _charTolowerTable[256];
 	unsigned char _charToupperTable[256];
+
+	gidispatch_rock_t(*gli_register_obj)(void *obj, uint objclass);
+	void(*gli_unregister_obj)(void *obj, uint objclass, gidispatch_rock_t objrock);
+	gidispatch_rock_t(*gli_register_arr)(void *array, uint len, char *typecode);
+	void(*gli_unregister_arr)(void *array, uint len, char *typecode, gidispatch_rock_t objrock);
 public:
 	/**
 	 * Constructor
@@ -298,6 +303,12 @@ public:
 
 	/* dispa methods */
 
+	void gidispatch_set_object_registry(gidispatch_rock_t(*regi)(void *obj, uint objclass),
+		void(*unregi)(void *obj, uint objclass, gidispatch_rock_t objrock));
+
+	void gidispatch_set_retained_registry(gidispatch_rock_t(*regi)(void *array, uint len, char *typecode),
+		void(*unregi)(void *array, uint len, char *typecode, gidispatch_rock_t objrock));
+
 	uint32 gidispatch_count_classes() const;
 	const gidispatch_intconst_t *gidispatch_get_class(uint32 index) const;
 	uint32 gidispatch_count_intconst() const;
@@ -307,6 +318,7 @@ public:
 	gidispatch_function_t *gidispatch_get_function_by_id(uint32 id) const;
 	const char *gidispatch_prototype(uint32 funcnum) const;
 	void gidispatch_call(uint32 funcnum, uint32 numargs, gluniversal_t *arglist);
+	gidispatch_rock_t gidispatch_get_objrock(void *obj, uint objclass);
 };
 
 } // End of namespace Glk
diff --git a/engines/glk/glk_dispa.cpp b/engines/glk/glk_dispa.cpp
index 38b03e5..dfb638f 100644
--- a/engines/glk/glk_dispa.cpp
+++ b/engines/glk/glk_dispa.cpp
@@ -335,6 +335,46 @@ static gidispatch_function_t function_table[] = {
 #endif /* GLK_MODULE_GARGLKTEXT */
 };
 
+void GlkAPI::gidispatch_set_object_registry(gidispatch_rock_t(*regi)(void *obj, uint objclass),
+		void(*unregi)(void *obj, uint objclass, gidispatch_rock_t objrock)) {
+	Window *win;
+	Stream *str;
+	frefid_t fref;
+
+	gli_register_obj = regi;
+	gli_unregister_obj = unregi;
+
+	if (gli_register_obj)
+	{
+		/* It's now necessary to go through all existing objects, and register
+			them. */
+		for (win = glk_window_iterate(NULL, NULL);
+			win;
+			win = glk_window_iterate(win, NULL))
+		{
+			win->_dispRock = (*gli_register_obj)(win, gidisp_Class_Window);
+		}
+		for (str = glk_stream_iterate(NULL, NULL);
+			str;
+			str = glk_stream_iterate(str, NULL))
+		{
+			str->_dispRock = (*gli_register_obj)(str, gidisp_Class_Stream);
+		}
+		for (fref = glk_fileref_iterate(NULL, NULL);
+			fref;
+			fref = glk_fileref_iterate(fref, NULL))
+		{
+			fref->_dispRock = (*gli_register_obj)(fref, gidisp_Class_Fileref);
+		}
+	}
+}
+
+void GlkAPI::gidispatch_set_retained_registry(gidispatch_rock_t(*regi)(void *array, uint len, char *typecode),
+		void(*unregi)(void *array, uint len, char *typecode, gidispatch_rock_t objrock)) {
+	gli_register_arr = regi;
+	gli_unregister_arr = unregi;
+}
+
 uint32 GlkAPI::gidispatch_count_classes() const {
     return NUMCLASSES;
 }
@@ -1491,4 +1531,22 @@ void GlkAPI::gidispatch_call(uint32 funcnum, uint32 numargs, gluniversal_t *argl
     }
 }
 
+gidispatch_rock_t GlkAPI::gidispatch_get_objrock(void *obj, uint objclass) {
+	switch (objclass) {
+	case gidisp_Class_Window:
+		return ((Window *)obj)->_dispRock;
+	case gidisp_Class_Stream:
+		return ((Stream *)obj)->_dispRock;
+	case gidisp_Class_Fileref:
+		return ((FileReference *)obj)->_dispRock;
+	case gidisp_Class_Schannel:
+		return ((SoundChannel *)obj)->_dispRock;
+	default: {
+		gidispatch_rock_t dummy;
+		dummy.num = 0;
+		return dummy;
+	}
+	}
+}
+
 } // End of namespace Glk
diff --git a/engines/glk/glk_types.h b/engines/glk/glk_types.h
index 3c4b4fb..7095334 100644
--- a/engines/glk/glk_types.h
+++ b/engines/glk/glk_types.h
@@ -230,7 +230,7 @@ union gluniversal_union {
 	byte _uch;           ///< Cu
 	int8 _sch;           ///< Cs
 	char _ch;            ///< Cn
-	char *_charstr;      ///< S
+	const char *_charstr; ///< S
 	uint32 *_unicharstr; ///< U
 	void *_array;        ///< all # arguments
 	uint32 _ptrflag;     ///< [ ... ] or *?
diff --git a/engines/glk/glulxe/glkop.cpp b/engines/glk/glulxe/glkop.cpp
index af650a3..ee7f78b 100644
--- a/engines/glk/glulxe/glkop.cpp
+++ b/engines/glk/glulxe/glkop.cpp
@@ -20,11 +20,1362 @@
  *
  */
 
-#include "glk/glulxe/glkop.h"
+#include "engines/glk/glulxe/glulxe.h"
 
 namespace Glk {
 namespace Glulxe {
 
+/* This code is actually very general; it could work for almost any
+   32-bit VM which remotely resembles Glulxe or the Z-machine in design.
+   
+   To be precise, we make the following assumptions:
+
+   - An argument list is an array of 32-bit values, which can represent
+     either integers or addresses.
+   - We can read or write to a 32-bit integer in VM memory using the macros
+     ReadMemory(addr) and WriteMemory(addr), where addr is an address
+     taken from the argument list.
+   - A character array is a sequence of bytes somewhere in VM memory.
+     The array can be turned into a C char array by the macro
+     CaptureCArray(addr, len), and released by ReleaseCArray().
+     The passin, passout hints may be used to avoid unnecessary copying.
+   - An integer array is a sequence of integers somewhere in VM memory.
+     The array can be turned into a C integer array by the macro
+     CaptureIArray(addr, len), and released by ReleaseIArray().
+     These macros are responsible for fixing byte-order and alignment
+     (if the C ABI does not match the VM's). The passin, passout hints
+     may be used to avoid unnecessary copying.
+   - A Glk object array is a sequence of integers in VM memory. It is
+     turned into a C pointer array (remember that C pointers may be more
+     than 4 bytes!) The pointer array is allocated by
+     CapturePtrArray(addr, len, objclass) and released by ReleasePtrArray().
+     Again, the macros handle the conversion.
+   - A Glk structure (such as event_t) is a set of integers somewhere
+     in VM memory, which can be read and written with the macros
+     ReadStructField(addr, fieldnum) and WriteStructField(addr, fieldnum).
+     The fieldnum is an integer (from 0 to 3, for event_t.)
+   - A VM string can be turned into a C-style string with the macro
+     ptr = DecodeVMString(addr). After the string is used, this code
+     calls ReleaseVMString(ptr), which should free any memory that
+     DecodeVMString allocates.
+   - A VM Unicode string can be turned into a zero-terminated array
+     of 32-bit integers, in the same way, with DecodeVMUstring
+     and ReleaseVMUstring.
+
+     To work this code over for a new VM, just diddle the macros.
+*/
+
+#define ReadMemory(addr)  \
+    (((addr) == 0xffffffff) \
+      ? (stackptr -= 4, Stk4(stackptr)) \
+      : (Mem4(addr)))
+#define WriteMemory(addr, val)  \
+    (((addr) == 0xffffffff) \
+      ? (StkW4(stackptr, (val)), stackptr += 4) \
+      : (MemW4((addr), (val))))
+#define CaptureCArray(addr, len, passin)  \
+    (grab_temp_c_array(addr, len, passin))
+#define ReleaseCArray(ptr, addr, len, passout)  \
+    (release_temp_c_array(ptr, addr, len, passout))
+#define CaptureIArray(addr, len, passin)  \
+    (grab_temp_i_array(addr, len, passin))
+#define ReleaseIArray(ptr, addr, len, passout)  \
+    (release_temp_i_array(ptr, addr, len, passout))
+#define CapturePtrArray(addr, len, objclass, passin)  \
+    (grab_temp_ptr_array(addr, len, objclass, passin))
+#define ReleasePtrArray(ptr, addr, len, objclass, passout)  \
+    (release_temp_ptr_array(ptr, addr, len, objclass, passout))
+#define ReadStructField(addr, fieldnum)  \
+    (((addr) == 0xffffffff) \
+      ? (stackptr -= 4, Stk4(stackptr)) \
+      : (Mem4((addr)+(fieldnum)*4)))
+#define WriteStructField(addr, fieldnum, val)  \
+    (((addr) == 0xffffffff) \
+      ? (StkW4(stackptr, (val)), stackptr += 4) \
+      : (MemW4((addr)+(fieldnum)*4, (val))))
+#define DecodeVMString(addr)  \
+    (make_temp_string(addr))
+#define ReleaseVMString(ptr)  \
+    (free_temp_string(ptr))
+#define DecodeVMUstring(addr)  \
+    (make_temp_ustring(addr))
+#define ReleaseVMUstring(ptr)  \
+    (free_temp_ustring(ptr))
+
+static gidispatch_rock_t classtable_register(void *obj, uint objclass) {
+	return g_vm->glulxe_classtable_register(obj, objclass);
+}
+
+static void classtable_unregister(void *obj, uint objclass, gidispatch_rock_t objrock) {
+	g_vm->glulxe_classtable_unregister(obj, objclass, objrock);
+}
+
+static gidispatch_rock_t retained_register(void *array, uint len, char *typecode) {
+	return g_vm->glulxe_retained_register(array, len, typecode);
+}
+
+static void retained_unregister(void *array, uint len, char *typecode, gidispatch_rock_t objrock) {
+	g_vm->glulxe_retained_unregister(array, len, typecode, objrock);
+}
+
+void Glulxe::glkopInit() {
+	library_select_hook = nullptr;
+	arrays = nullptr;
+	num_classes = 0;
+	classes = nullptr;
+}
+
+/* init_dispatch():
+   Set up the class hash tables and other startup-time stuff.
+*/
+bool Glulxe::init_dispatch() {
+  int ix;
+  
+  /* What with one thing and another, this *could* be called more than
+     once. We only need to allocate the tables once. */
+  if (classes)
+      return true;
+  
+  /* Set up the game-ID hook. (This is ifdeffed because not all Glk
+     libraries have this call.) */
+#ifdef GI_DISPA_GAME_ID_AVAILABLE
+  gidispatch_set_game_id_hook(&get_game_id);
+#endif /* GI_DISPA_GAME_ID_AVAILABLE */
+    
+  /* Allocate the class hash tables. */
+  num_classes = gidispatch_count_classes();
+  classes = (classtable_t **)glulx_malloc(num_classes  * sizeof(classtable_t *));
+  if (!classes)
+    return false;
+    
+  for (ix=0; ix<num_classes; ix++) {
+    classes[ix] = new_classtable((glulx_random() % (uint)(101)) + 1);
+    if (!classes[ix])
+      return false;
+  }
+    
+  /* Set up the two callbacks. */
+  gidispatch_set_object_registry(&classtable_register, &classtable_unregister);
+  gidispatch_set_retained_registry(&retained_register, &retained_unregister);
+  
+  /* If the library supports autorestore callbacks, set those up too.
+     (These are only used in iosglk, currently.) */
+#ifdef GIDISPATCH_AUTORESTORE_REGISTRY
+  gidispatch_set_autorestore_registry(&glulxe_array_locate, &glulxe_array_restore);
+#endif /* GIDISPATCH_AUTORESTORE_REGISTRY */
+  
+  return true;
+}
+
+uint Glulxe::perform_glk(uint funcnum, uint numargs, uint *arglist) {
+  uint retval = 0;
+
+  switch (funcnum) {
+    /* To speed life up, we implement commonly-used Glk functions
+       directly -- instead of bothering with the whole prototype 
+       mess. */
+
+  case 0x0047: /* stream_set_current */
+    if (numargs != 1)
+      goto WrongArgNum;
+    glk_stream_set_current(find_stream_by_id(arglist[0]));
+    break;
+  case 0x0048: /* stream_get_current */
+    if (numargs != 0)
+      goto WrongArgNum;
+    retval = find_id_for_stream(glk_stream_get_current());
+    break;
+  case 0x0080: /* put_char */
+    if (numargs != 1)
+      goto WrongArgNum;
+    glk_put_char(arglist[0] & 0xFF);
+    break;
+  case 0x0081: /* put_char_stream */
+    if (numargs != 2)
+      goto WrongArgNum;
+    glk_put_char_stream(find_stream_by_id(arglist[0]), arglist[1] & 0xFF);
+    break;
+  case 0x00C0: /* select */
+    /* call a library hook on every glk_select() */
+    if (library_select_hook)
+      library_select_hook(arglist[0]);
+    /* but then fall through to full dispatcher, because there's no real
+       need for speed here */
+    goto FullDispatcher;
+  case 0x00A0: /* char_to_lower */
+    if (numargs != 1)
+      goto WrongArgNum;
+    retval = glk_char_to_lower(arglist[0] & 0xFF);
+    break;
+  case 0x00A1: /* char_to_upper */
+    if (numargs != 1)
+      goto WrongArgNum;
+    retval = glk_char_to_upper(arglist[0] & 0xFF);
+    break;
+  case 0x0128: /* put_char_uni */
+    if (numargs != 1)
+      goto WrongArgNum;
+    glk_put_char_uni(arglist[0]);
+    break;
+  case 0x012B: /* put_char_stream_uni */
+    if (numargs != 2)
+      goto WrongArgNum;
+    glk_put_char_stream_uni(find_stream_by_id(arglist[0]), arglist[1]);
+    break;
+
+  WrongArgNum:
+    error("Wrong number of arguments to Glk function.");
+    break;
+
+  FullDispatcher:
+  default: {
+    /* Go through the full dispatcher prototype foo. */
+    const char *proto, *cx;
+    dispatch_splot_t splot;
+    int argnum, argnum2;
+
+    /* Grab the string. */
+    proto = gidispatch_prototype(funcnum);
+    if (!proto)
+      error("Unknown Glk function.");
+
+    splot.varglist = arglist;
+    splot.numvargs = numargs;
+    splot.retval = &retval;
+
+    /* The work goes in four phases. First, we figure out how many
+       arguments we want, and allocate space for the Glk argument
+       list. Then we go through the Glulxe arguments and load them 
+       into the Glk list. Then we call. Then we go through the 
+       arguments again, unloading the data back into Glulx memory. */
+
+    /* Phase 0. */
+    prepare_glk_args(proto, &splot);
+
+    /* Phase 1. */
+    argnum = 0;
+    cx = proto;
+    parse_glk_args(&splot, &cx, 0, &argnum, 0, 0);
+
+    /* Phase 2. */
+    gidispatch_call(funcnum, argnum, splot.garglist);
+
+    /* Phase 3. */
+    argnum2 = 0;
+    cx = proto;
+    unparse_glk_args(&splot, &cx, 0, &argnum2, 0, 0);
+    if (argnum != argnum2)
+      error("Argument counts did not match.");
+
+    break;
+  }
+  }
+
+  return retval;
+}
+
+const char *Glulxe::read_prefix(const char *cx, int *isref, int *isarray, int *passin, int *passout,
+		int *nullok, int *isretained,  int *isreturn) {
+  *isref = false;
+  *passin = false;
+  *passout = false;
+  *nullok = true;
+  *isarray = false;
+  *isretained = false;
+  *isreturn = false;
+  while (1) {
+    if (*cx == '<') {
+      *isref = true;
+      *passout = true;
+    }
+    else if (*cx == '>') {
+      *isref = true;
+      *passin = true;
+    }
+    else if (*cx == '&') {
+      *isref = true;
+      *passout = true;
+      *passin = true;
+    }
+    else if (*cx == '+') {
+      *nullok = false;
+    }
+    else if (*cx == ':') {
+      *isref = true;
+      *passout = true;
+      *nullok = false;
+      *isreturn = true;
+    }
+    else if (*cx == '#') {
+      *isarray = true;
+    }
+    else if (*cx == '!') {
+      *isretained = true;
+    }
+    else {
+      break;
+    }
+    cx++;
+  }
+  return cx;
+}
+
+void Glulxe::prepare_glk_args(const char *proto, dispatch_splot_t *splot) {
+  static gluniversal_t *garglist = nullptr;
+  static int garglist_size = 0;
+
+  int ix;
+  int numwanted, numvargswanted, maxargs;
+  const char *cx;
+
+  cx = proto;
+  numwanted = 0;
+  while (*cx >= '0' && *cx <= '9') {
+    numwanted = 10 * numwanted + (*cx - '0');
+    cx++;
+  }
+  splot->numwanted = numwanted;
+
+  maxargs = 0; 
+  numvargswanted = 0; 
+  for (ix = 0; ix < numwanted; ix++) {
+    int isref, passin, passout, nullok, isarray, isretained, isreturn;
+    cx = read_prefix(cx, &isref, &isarray, &passin, &passout, &nullok,
+      &isretained, &isreturn);
+    if (isref) {
+      maxargs += 2;
+    }
+    else {
+      maxargs += 1;
+    }
+    if (!isreturn) {
+      if (isarray) {
+        numvargswanted += 2;
+      }
+      else {
+        numvargswanted += 1;
+      }
+    }
+        
+    if (*cx == 'I' || *cx == 'C') {
+      cx += 2;
+    }
+    else if (*cx == 'Q') {
+      cx += 2;
+    }
+    else if (*cx == 'S' || *cx == 'U') {
+      cx += 1;
+    }
+    else if (*cx == '[') {
+      int refdepth, nwx;
+      cx++;
+      nwx = 0;
+      while (*cx >= '0' && *cx <= '9') {
+        nwx = 10 * nwx + (*cx - '0');
+        cx++;
+      }
+      maxargs += nwx; /* This is *only* correct because all structs contain
+                         plain values. */
+      refdepth = 1;
+      while (refdepth > 0) {
+        if (*cx == '[')
+          refdepth++;
+        else if (*cx == ']')
+          refdepth--;
+        cx++;
+      }
+    }
+    else {
+      error("Illegal format string.");
+    }
+  }
+
+  if (*cx != ':' && *cx != '\0')
+    error("Illegal format string.");
+
+  splot->maxargs = maxargs;
+
+  if (splot->numvargs != numvargswanted)
+    error("Wrong number of arguments to Glk function.");
+
+  if (garglist && garglist_size < maxargs) {
+    glulx_free(garglist);
+    garglist = nullptr;
+    garglist_size = 0;
+  }
+  if (!garglist) {
+    garglist_size = maxargs + 16;
+    garglist = (gluniversal_t *)glulx_malloc(garglist_size 
+      * sizeof(gluniversal_t));
+  }
+  if (!garglist)
+    error("Unable to allocate storage for Glk arguments.");
+
+  splot->garglist = garglist;
+}
+
+void Glulxe::parse_glk_args(dispatch_splot_t *splot, const char **proto, int depth, int *argnumptr,
+		uint subaddress, int subpassin) {
+  const char *cx;
+  int ix, argx;
+  int gargnum, numwanted;
+  void *opref;
+  gluniversal_t *garglist;
+  uint *varglist;
+  
+  garglist = splot->garglist;
+  varglist = splot->varglist;
+  gargnum = *argnumptr;
+  cx = *proto;
+
+  numwanted = 0;
+  while (*cx >= '0' && *cx <= '9') {
+    numwanted = 10 * numwanted + (*cx - '0');
+    cx++;
+  }
+
+  for (argx = 0, ix = 0; argx < numwanted; argx++, ix++) {
+    char typeclass;
+    int skipval;
+    int isref, passin, passout, nullok, isarray, isretained, isreturn;
+    cx = read_prefix(cx, &isref, &isarray, &passin, &passout, &nullok,
+      &isretained, &isreturn);
+    
+    typeclass = *cx;
+    cx++;
+
+    skipval = false;
+    if (isref) {
+      if (!isreturn && varglist[ix] == 0) {
+        if (!nullok)
+          error("Zero passed invalidly to Glk function.");
+        garglist[gargnum]._ptrflag = false;
+        gargnum++;
+        skipval = true;
+      }
+      else {
+        garglist[gargnum]._ptrflag = true;
+        gargnum++;
+      }
+    }
+    if (!skipval) {
+      uint thisval;
+
+      if (typeclass == '[') {
+
+        parse_glk_args(splot, &cx, depth+1, &gargnum, varglist[ix], passin);
+
+      }
+      else if (isarray) {
+        /* definitely isref */
+
+        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 = CaptureCArray(varglist[ix], varglist[ix+1], passin);
+          gargnum++;
+          ix++;
+          garglist[gargnum]._uint = varglist[ix];
+          gargnum++;
+          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++;
+          garglist[gargnum]._uint = varglist[ix];
+          gargnum++;
+          cx++;
+          break;
+        case 'Q':
+          /* This case was added after the giant arrays were deprecated,
+             so we don't bother to allow for that case. We just verify
+             the length. */
+          verify_array_addresses(varglist[ix], varglist[ix+1], 4);
+          garglist[gargnum]._array = CapturePtrArray(varglist[ix], varglist[ix+1], (*cx-'a'), passin);
+          gargnum++;
+          ix++;
+          garglist[gargnum]._uint = varglist[ix];
+          gargnum++;
+          cx++;
+          break;
+        default:
+          error("Illegal format string.");
+          break;
+        }
+      }
+      else {
+        /* a plain value or a reference to one. */
+
+        if (isreturn) {
+          thisval = 0;
+        }
+        else if (depth > 0) {
+          /* Definitely not isref or isarray. */
+          if (subpassin)
+            thisval = ReadStructField(subaddress, ix);
+          else
+            thisval = 0;
+        }
+        else if (isref) {
+          if (passin)
+            thisval = ReadMemory(varglist[ix]);
+          else
+            thisval = 0;
+        }
+        else {
+          thisval = varglist[ix];
+        }
+
+        switch (typeclass) {
+        case 'I':
+          if (*cx == 'u')
+            garglist[gargnum]._uint = (uint)(thisval);
+          else if (*cx == 's')
+            garglist[gargnum]._sint = (int)(thisval);
+          else
+            error("Illegal format string.");
+          gargnum++;
+          cx++;
+          break;
+        case 'Q':
+          if (thisval) {
+            opref = classes_get(*cx-'a', thisval);
+            if (!opref) {
+              error("Reference to nonexistent Glk object.");
+            }
+          }
+          else {
+            opref = nullptr;
+          }
+          garglist[gargnum]._opaqueref = opref;
+          gargnum++;
+          cx++;
+          break;
+        case 'C':
+          if (*cx == 'u') 
+            garglist[gargnum]._uch = (unsigned char)(thisval);
+          else if (*cx == 's')
+            garglist[gargnum]._sch = (signed char)(thisval);
+          else if (*cx == 'n')
+            garglist[gargnum]._ch = (char)(thisval);
+          else
+            error("Illegal format string.");
+          gargnum++;
+          cx++;
+          break;
+        case 'S':
+          garglist[gargnum]._charstr = DecodeVMString(thisval);
+          gargnum++;
+          break;
+#ifdef GLK_MODULE_UNICODE
+        case 'U':
+          garglist[gargnum]._unicharstr = DecodeVMUstring(thisval);
+          gargnum++;
+          break;
+#endif /* GLK_MODULE_UNICODE */
+        default:
+          error("Illegal format string.");
+          break;
+        }
+      }
+    }
+    else {
+      /* We got a null reference, so we have to skip the format element. */
+      if (typeclass == '[') {
+        int numsubwanted, refdepth;
+        numsubwanted = 0;
+        while (*cx >= '0' && *cx <= '9') {
+          numsubwanted = 10 * numsubwanted + (*cx - '0');
+          cx++;
+        }
+        refdepth = 1;
+        while (refdepth > 0) {
+          if (*cx == '[')
+            refdepth++;
+          else if (*cx == ']')
+            refdepth--;
+          cx++;
+        }
+      }
+      else if (typeclass == 'S' || typeclass == 'U') {
+        /* leave it */
+      }
+      else {
+        cx++;
+        if (isarray)
+          ix++;
+      }
+    }    
+  }
+
+  if (depth > 0) {
+    if (*cx != ']')
+      error("Illegal format string.");
+    cx++;
+  }
+  else {
+    if (*cx != ':' && *cx != '\0')
+      error("Illegal format string.");
+  }
+  
+  *proto = cx;
+  *argnumptr = gargnum;
+}
+
+void Glulxe::unparse_glk_args(dispatch_splot_t *splot, const char **proto, int depth,
+  int *argnumptr, uint subaddress, int subpassout) {
+  const char *cx;
+  int ix, argx;
+  int gargnum, numwanted;
+  void *opref;
+  gluniversal_t *garglist;
+  uint *varglist;
+  
+  garglist = splot->garglist;
+  varglist = splot->varglist;
+  gargnum = *argnumptr;
+  cx = *proto;
+
+  numwanted = 0;
+  while (*cx >= '0' && *cx <= '9') {
+    numwanted = 10 * numwanted + (*cx - '0');
+    cx++;
+  }
+
+  for (argx = 0, ix = 0; argx < numwanted; argx++, ix++) {
+    char typeclass;
+    int skipval;
+    int isref, passin, passout, nullok, isarray, isretained, isreturn;
+    cx = read_prefix(cx, &isref, &isarray, &passin, &passout, &nullok,
+      &isretained, &isreturn);
+    
+    typeclass = *cx;
+    cx++;
+
+    skipval = false;
+    if (isref) {
+      if (!isreturn && varglist[ix] == 0) {
+        if (!nullok)
+          error("Zero passed invalidly to Glk function.");
+        garglist[gargnum]._ptrflag = false;
+        gargnum++;
+        skipval = true;
+      } else {
+        garglist[gargnum]._ptrflag = true;
+        gargnum++;
+      }
+    }
+    if (!skipval) {
+      uint thisval = 0;
+
+      if (typeclass == '[') {
+
+        unparse_glk_args(splot, &cx, depth+1, &gargnum, varglist[ix], passout);
+
+      }
+      else if (isarray) {
+        /* definitely isref */
+
+        switch (typeclass) {
+        case 'C':
+          ReleaseCArray((char *)garglist[gargnum]._array, varglist[ix], varglist[ix+1], passout);
+          gargnum++;
+          ix++;
+          gargnum++;
+          cx++;
+          break;
+        case 'I':
+          ReleaseIArray((uint *)garglist[gargnum]._array, varglist[ix], varglist[ix+1], passout);
+          gargnum++;
+          ix++;
+          gargnum++;
+          cx++;
+          break;
+        case 'Q':
+          ReleasePtrArray((void **)garglist[gargnum]._array, varglist[ix], varglist[ix+1], (*cx-'a'), passout);
+          gargnum++;
+          ix++;
+          gargnum++;
+          cx++;
+          break;
+        default:
+          error("Illegal format string.");
+          break;
+        }
+      }
+      else {
+        /* a plain value or a reference to one. */
+
+        if (isreturn || (depth > 0 && subpassout) || (isref && passout)) {
+          skipval = false;
+        }
+        else {
+          skipval = true;
+        }
+
+        switch (typeclass) {
+        case 'I':
+          if (!skipval) {
+            if (*cx == 'u')
+              thisval = (uint)garglist[gargnum]._uint;
+            else if (*cx == 's')
+              thisval = (uint)garglist[gargnum]._sint;
+            else
+              error("Illegal format string.");
+          }
+          gargnum++;
+          cx++;
+          break;
+        case 'Q':
+          if (!skipval) {
+            opref = garglist[gargnum]._opaqueref;
+            if (opref) {
+              gidispatch_rock_t objrock = gidispatch_get_objrock(opref, *cx - 'a');
+              thisval = ((classref_t *)objrock.ptr)->id;
+            }
+            else {
+              thisval = 0;
+            }
+          }
+          gargnum++;
+          cx++;
+          break;
+        case 'C':
+          if (!skipval) {
+            if (*cx == 'u') 
+              thisval = (uint)garglist[gargnum]._uch;
+            else if (*cx == 's')
+              thisval = (uint)garglist[gargnum]._sch;
+            else if (*cx == 'n')
+              thisval = (uint)garglist[gargnum]._ch;
+            else
+              error("Illegal format string.");
+          }
+          gargnum++;
+          cx++;
+          break;
+        case 'S':
+          if (garglist[gargnum]._charstr)
+            ReleaseVMString(garglist[gargnum]._charstr);
+          gargnum++;
+          break;
+#ifdef GLK_MODULE_UNICODE
+        case 'U':
+          if (garglist[gargnum]._unicharstr)
+            ReleaseVMUstring(garglist[gargnum]._unicharstr);
+          gargnum++;
+          break;
+#endif /* GLK_MODULE_UNICODE */
+        default:
+          error("Illegal format string.");
+          break;
+        }
+
+        if (isreturn) {
+          *(splot->retval) = thisval;
+        }
+        else if (depth > 0) {
+          /* Definitely not isref or isarray. */
+          if (subpassout)
+            WriteStructField(subaddress, ix, thisval);
+        }
+        else if (isref) {
+          if (passout)
+            WriteMemory(varglist[ix], thisval); 
+        }
+      }
+    }
+    else {
+      /* We got a null reference, so we have to skip the format element. */
+      if (typeclass == '[') {
+        int numsubwanted, refdepth;
+        numsubwanted = 0;
+        while (*cx >= '0' && *cx <= '9') {
+          numsubwanted = 10 * numsubwanted + (*cx - '0');
+          cx++;
+        }
+        refdepth = 1;
+        while (refdepth > 0) {
+          if (*cx == '[')
+            refdepth++;
+          else if (*cx == ']')
+            refdepth--;
+          cx++;
+        }
+      }
+      else if (typeclass == 'S' || typeclass == 'U') {
+        /* leave it */
+      }
+      else {
+        cx++;
+        if (isarray)
+          ix++;
+      }
+    }    
+  }
+
+  if (depth > 0) {
+    if (*cx != ']')
+      error("Illegal format string.");
+    cx++;
+  }
+  else {
+    if (*cx != ':' && *cx != '\0')
+      error("Illegal format string.");
+  }
+  
+  *proto = cx;
+  *argnumptr = gargnum;
+}
+
+strid_t Glulxe::find_stream_by_id(uint objid) {
+  if (!objid)
+    return nullptr;
+
+  // Recall that class 1 ("b") is streams
+  return (strid_t)classes_get(gidisp_Class_Stream, objid);
+}
+
+uint Glulxe::find_id_for_window(winid_t win) {
+  gidispatch_rock_t objrock;
+
+  if (!win)
+    return 0;
+
+  objrock = gidispatch_get_objrock(win, gidisp_Class_Window);
+  if (!objrock.ptr)
+    return 0;
+  return ((classref_t *)objrock.ptr)->id;
+}
+
+uint Glulxe::find_id_for_stream(strid_t str) {
+  gidispatch_rock_t objrock;
+
+  if (!str)
+    return 0;
+
+  objrock = gidispatch_get_objrock(str, gidisp_Class_Stream);
+  if (!objrock.ptr)
+    return 0;
+  return ((classref_t *)objrock.ptr)->id;
+}
+
+uint Glulxe::find_id_for_fileref(frefid_t fref) {
+  gidispatch_rock_t objrock;
+
+  if (!fref)
+    return 0;
+
+  objrock = gidispatch_get_objrock(fref, gidisp_Class_Fileref);
+  if (!objrock.ptr)
+    return 0;
+  return ((classref_t *)objrock.ptr)->id;
+}
+
+uint Glulxe::find_id_for_schannel(schanid_t schan) {
+  gidispatch_rock_t objrock;
+
+  if (!schan)
+    return 0;
+
+  objrock = gidispatch_get_objrock(schan, gidisp_Class_Schannel);
+  if (!objrock.ptr)
+    return 0;
+  return ((classref_t *)objrock.ptr)->id;
+}
+
+classtable_t *Glulxe::new_classtable(uint firstid) {
+  int ix;
+  classtable_t *ctab = (classtable_t *)glulx_malloc(sizeof(classtable_t));
+  if (!ctab)
+    return nullptr;
+    
+  for (ix=0; ix<CLASSHASH_SIZE; ix++)
+    ctab->bucket[ix] = nullptr;
+    
+  ctab->lastid = firstid;
+    
+  return ctab;
+}
+
+void *Glulxe::classes_get(int classid, uint objid) {
+  classtable_t *ctab;
+  classref_t *cref;
+  if (classid < 0 || classid >= num_classes)
+    return nullptr;
+  ctab = classes[classid];
+  cref = ctab->bucket[objid % CLASSHASH_SIZE];
+  for (; cref; cref = cref->next) {
+    if (cref->id == objid)
+      return cref->obj;
+  }
+  return nullptr;
+}
+
+classref_t *Glulxe::classes_put(int classid, void *obj, uint origid) {
+  int bucknum;
+  classtable_t *ctab;
+  classref_t *cref;
+  if (classid < 0 || classid >= num_classes)
+    return nullptr;
+  ctab = classes[classid];
+  cref = (classref_t *)glulx_malloc(sizeof(classref_t));
+  if (!cref)
+    return nullptr;
+  cref->obj = obj;
+  if (!origid) {
+    cref->id = ctab->lastid;
+    ctab->lastid++;
+  }
+  else {
+    cref->id = origid;
+    if (ctab->lastid <= origid)
+      ctab->lastid = origid+1;
+  }
+  bucknum = cref->id % CLASSHASH_SIZE;
+  cref->bucknum = bucknum;
+  cref->next = ctab->bucket[bucknum];
+  ctab->bucket[bucknum] = cref;
+  return cref;
+}
+
+void Glulxe::classes_remove(int classid, void *obj)
+{
+  classtable_t *ctab;
+  classref_t *cref;
+  classref_t **crefp;
+  gidispatch_rock_t objrock;
+  if (classid < 0 || classid >= num_classes)
+    return;
+  ctab = classes[classid];
+  objrock = gidispatch_get_objrock(obj, classid);
+  cref = (classref_t *)objrock.ptr;
+  if (!cref)
+    return;
+  crefp = &(ctab->bucket[cref->bucknum]);
+  for (; *crefp; crefp = &((*crefp)->next)) {
+    if ((*crefp) == cref) {
+      *crefp = cref->next;
+      if (!cref->obj) {
+        nonfatal_warning("attempt to free nullptr object!");
+      }
+      cref->obj = nullptr;
+      cref->id = 0;
+      cref->next = nullptr;
+      glulx_free(cref);
+      return;
+    }
+  }
+  return;
+}
+
+gidispatch_rock_t Glulxe::glulxe_classtable_register(void *obj,  uint objclass) {
+  classref_t *cref;
+  gidispatch_rock_t objrock;
+  cref = classes_put(objclass, obj, 0);
+  objrock.ptr = cref;
+  return objrock;
+}
+
+void Glulxe::glulxe_classtable_unregister(void *obj, uint objclass, 
+  gidispatch_rock_t objrock) {
+  classes_remove(objclass, obj);
+}
+
+gidispatch_rock_t Glulxe::glulxe_classtable_register_existing(void *obj, uint objclass, uint dispid) {
+  classref_t *cref;
+  gidispatch_rock_t objrock;
+  cref = classes_put(objclass, obj, dispid);
+  objrock.ptr = cref;
+  return objrock;
+}
+
+char *Glulxe::grab_temp_c_array(uint addr, uint len, int passin) {
+  arrayref_t *arref = nullptr;
+  char *arr = nullptr;
+  uint ix, addr2;
+
+  if (len) {
+    arr = (char *)glulx_malloc(len * sizeof(char));
+    arref = (arrayref_t *)glulx_malloc(sizeof(arrayref_t));
+    if (!arr || !arref) 
+      error("Unable to allocate space for array argument to Glk call.");
+
+    arref->array = arr;
+    arref->addr = addr;
+    arref->elemsize = 1;
+    arref->retained = false;
+    arref->len = len;
+    arref->next = arrays;
+    arrays = arref;
+
+    if (passin) {
+      for (ix=0, addr2=addr; ix<len; ix++, addr2+=1) {
+        arr[ix] = Mem1(addr2);
+      }
+    }
+  }
+
+  return arr;
+}
+
+void Glulxe::release_temp_c_array(char *arr, uint addr, uint len, int passout) {
+  arrayref_t *arref = nullptr;
+  arrayref_t **aptr;
+  uint ix, val, addr2;
+
+  if (arr) {
+    for (aptr=(&arrays); (*aptr); aptr=(&((*aptr)->next))) {
+      if ((*aptr)->array == arr)
+        break;
+    }
+    arref = *aptr;
+    if (!arref)
+      error("Unable to re-find array argument in Glk call.");
+    if (arref->addr != addr || arref->len != len)
+      error("Mismatched array argument in Glk call.");
+
+    if (arref->retained) {
+      return;
+    }
+
+    *aptr = arref->next;
+    arref->next = nullptr;
+
+    if (passout) {
+      for (ix=0, addr2=addr; ix<len; ix++, addr2+=1) {
+        val = arr[ix];
+        MemW1(addr2, val);
+      }
+    }
+    glulx_free(arr);
+    glulx_free(arref);
+  }
+}
+
+uint *Glulxe::grab_temp_i_array(uint addr, uint len, int passin) {
+  arrayref_t *arref = nullptr;
+  uint *arr = nullptr;
+  uint ix, addr2;
+
+  if (len) {
+    arr = (uint *)glulx_malloc(len * sizeof(uint));
+    arref = (arrayref_t *)glulx_malloc(sizeof(arrayref_t));
+    if (!arr || !arref) 
+      error("Unable to allocate space for array argument to Glk call.");
+
+    arref->array = arr;
+    arref->addr = addr;
+    arref->elemsize = 4;
+    arref->retained = false;
+    arref->len = len;
+    arref->next = arrays;
+    arrays = arref;
+
+    if (passin) {
+      for (ix=0, addr2=addr; ix<len; ix++, addr2+=4) {
+        arr[ix] = Mem4(addr2);
+      }
+    }
+  }
+
+  return arr;
+}
+
+void Glulxe::release_temp_i_array(uint *arr, uint addr, uint len, int passout) {
+  arrayref_t *arref = nullptr;
+  arrayref_t **aptr;
+  uint ix, val, addr2;
+
+  if (arr) {
+    for (aptr=(&arrays); (*aptr); aptr=(&((*aptr)->next))) {
+      if ((*aptr)->array == arr)
+        break;
+    }
+    arref = *aptr;
+    if (!arref)
+      error("Unable to re-find array argument in Glk call.");
+    if (arref->addr != addr || arref->len != len)
+      error("Mismatched array argument in Glk call.");
+
+    if (arref->retained) {
+      return;
+    }
+
+    *aptr = arref->next;
+    arref->next = nullptr;
+
+    if (passout) {
+      for (ix=0, addr2=addr; ix<len; ix++, addr2+=4) {
+        val = arr[ix];
+        MemW4(addr2, val);
+      }
+    }
+    glulx_free(arr);
+    glulx_free(arref);
+  }
+}
+
+void **Glulxe::grab_temp_ptr_array(uint addr, uint len, int objclass, int passin) {
+  arrayref_t *arref = nullptr;
+  void **arr = nullptr;
+  uint ix, addr2;
+
+  if (len) {
+    arr = (void **)glulx_malloc(len * sizeof(void *));
+    arref = (arrayref_t *)glulx_malloc(sizeof(arrayref_t));
+    if (!arr || !arref) 
+      error("Unable to allocate space for array argument to Glk call.");
+
+    arref->array = arr;
+    arref->addr = addr;
+    arref->elemsize = sizeof(void *);
+    arref->retained = false;
+    arref->len = len;
+    arref->next = arrays;
+    arrays = arref;
+
+    if (passin) {
+      for (ix=0, addr2=addr; ix<len; ix++, addr2+=4) {
+        uint thisval = Mem4(addr2);
+        if (thisval)
+          arr[ix] = classes_get(objclass, thisval);
+        else
+          arr[ix] = nullptr;
+      }
+    }
+  }
+
+  return arr;
+}
+
+void Glulxe::release_temp_ptr_array(void **arr, uint addr, uint len, int objclass, int passout) {
+  arrayref_t *arref = nullptr;
+  arrayref_t **aptr;
+  uint ix, val, addr2;
+
+  if (arr) {
+    for (aptr=(&arrays); (*aptr); aptr=(&((*aptr)->next))) {
+      if ((*aptr)->array == arr)
+        break;
+    }
+    arref = *aptr;
+    if (!arref)
+      error("Unable to re-find array argument in Glk call.");
+    if (arref->addr != addr || arref->len != len)
+      error("Mismatched array argument in Glk call.");
+
+    if (arref->retained) {
+      return;
+    }
+
+    *aptr = arref->next;
+    arref->next = nullptr;
+
+    if (passout) {
+      for (ix=0, addr2=addr; ix<len; ix++, addr2+=4) {
+        void *opref = arr[ix];
+        if (opref) {
+          gidispatch_rock_t objrock = 
+            gidispatch_get_objrock(opref, objclass);
+          val = ((classref_t *)objrock.ptr)->id;
+        }
+        else {
+          val = 0;
+        }
+        MemW4(addr2, val);
+      }
+    }
+    glulx_free(arr);
+    glulx_free(arref);
+  }
+}
+
+gidispatch_rock_t Glulxe::glulxe_retained_register(void *array, uint len, char *typecode) {
+  gidispatch_rock_t rock;
+  arrayref_t *arref = nullptr;
+  arrayref_t **aptr;
+  uint elemsize = 0;
+
+  if (typecode[4] == 'C')
+    elemsize = 1;
+  else if (typecode[4] == 'I')
+    elemsize = 4;
+
+  if (!elemsize || array == nullptr) {
+    rock.ptr = nullptr;
+    return rock;
+  }
+
+  for (aptr=(&arrays); (*aptr); aptr=(&((*aptr)->next))) {
+    if ((*aptr)->array == array)
+      break;
+  }
+  arref = *aptr;
+  if (!arref)
+    error("Unable to re-find array argument in Glk call.");
+  if (arref->elemsize != elemsize || arref->len != len)
+    error("Mismatched array argument in Glk call.");
+
+  arref->retained = true;
+
+  rock.ptr = arref;
+  return rock;
+}
+
+void Glulxe::glulxe_retained_unregister(void *array, uint len, char *typecode, gidispatch_rock_t objrock) {
+  arrayref_t *arref = nullptr;
+  arrayref_t **aptr;
+  uint ix, addr2, val;
+  uint elemsize = 0;
+
+  if (typecode[4] == 'C')
+    elemsize = 1;
+  else if (typecode[4] == 'I')
+    elemsize = 4;
+
+  if (!elemsize || array == nullptr) {
+    return;
+  }
+
+  for (aptr=(&arrays); (*aptr); aptr=(&((*aptr)->next))) {
+    if ((*aptr)->array == array)
+      break;
+  }
+  arref = *aptr;
+  if (!arref)
+    error("Unable to re-find array argument in Glk call.");
+  if (arref != objrock.ptr)
+    error("Mismatched array reference in Glk call.");
+  if (!arref->retained)
+    error("Unretained array reference in Glk call.");
+  if (arref->elemsize != elemsize || arref->len != len)
+    error("Mismatched array argument in Glk call.");
+
+  *aptr = arref->next;
+  arref->next = nullptr;
+
+  if (elemsize == 1) {
+    for (ix=0, addr2=arref->addr; ix<arref->len; ix++, addr2+=1) {
+      val = ((char *)array)[ix];
+      MemW1(addr2, val);
+    }
+  }
+  else if (elemsize == 4) {
+    for (ix=0, addr2=arref->addr; ix<arref->len; ix++, addr2+=4) {
+      val = ((uint *)array)[ix];
+      MemW4(addr2, val);
+    }
+  }
+
+  glulx_free(array);
+  glulx_free(arref);
+}
+
+long Glulxe::glulxe_array_locate(void *array, uint len, char *typecode, gidispatch_rock_t objrock, int *elemsizeref) {
+  arrayref_t *arref = nullptr;
+  arrayref_t **aptr;
+  uint elemsize = 0;
+
+  if (typecode[4] == 'C')
+    elemsize = 1;
+  else if (typecode[4] == 'I')
+    elemsize = 4;
+
+  if (!elemsize || array == nullptr) {
+    *elemsizeref = 0; /* No need to save the array separately */
+    return (unsigned char *)array - memmap;
+  }
+  
+  for (aptr=(&arrays); (*aptr); aptr=(&((*aptr)->next))) {
+    if ((*aptr)->array == array)
+      break;
+  }
+  arref = *aptr;
+  if (!arref)
+    error("Unable to re-find array argument in array_locate.");
+  if (arref != objrock.ptr)
+    error("Mismatched array reference in array_locate.");
+  if (!arref->retained)
+    error("Unretained array reference in array_locate.");
+  if (arref->elemsize != elemsize || arref->len != len)
+    error("Mismatched array argument in array_locate.");
+  
+  *elemsizeref = arref->elemsize;
+  return arref->addr;
+}
+
+gidispatch_rock_t Glulxe::glulxe_array_restore(long bufkey, uint len, char *typecode, void **arrayref) {
+  gidispatch_rock_t rock;
+  int elemsize = 0;
+
+  if (typecode[4] == 'C')
+    elemsize = 1;
+  else if (typecode[4] == 'I')
+    elemsize = 4;
+
+  if (!elemsize) {
+    unsigned char *buf = memmap + bufkey;
+    *arrayref = buf;
+    rock.ptr = nullptr;
+    return rock;
+  }
+
+  if (elemsize == 1) {
+    char *cbuf = grab_temp_c_array(bufkey, len, false);
+    rock = glulxe_retained_register(cbuf, len, typecode);
+    *arrayref = cbuf;
+  }
+  else {
+    uint *ubuf = grab_temp_i_array(bufkey, len, false);
+    rock = glulxe_retained_register(ubuf, len, typecode);
+    *arrayref = ubuf;
+  }
+  return rock;
+}
+
+void Glulxe::set_library_select_hook(void (*func)(uint)) {
+  library_select_hook = func;
+}
+
+char *Glulxe::get_game_id() {
+  /* This buffer gets rewritten on every call, but that's okay -- the caller
+     is supposed to copy out the result. */
+  static char buf[2*64+2];
+  int ix, jx;
+
+  if (!memmap)
+    return nullptr;
+
+  for (ix=0, jx=0; ix<64; ix++) {
+    char ch = memmap[ix];
+    int val = ((ch >> 4) & 0x0F);
+    buf[jx++] = ((val < 10) ? (val + '0') : (val + 'A' - 10));
+    val = (ch & 0x0F);
+    buf[jx++] = ((val < 10) ? (val + '0') : (val + 'A' - 10));
+  }
+  buf[jx++] = '\0';
+
+  return buf;
+}
 
 } // End of namespace Glulxe
 } // End of namespace Glk
diff --git a/engines/glk/glulxe/glkop.h b/engines/glk/glulxe/glkop.h
deleted file mode 100644
index ed9d716..0000000
--- a/engines/glk/glulxe/glkop.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GLK_GLULXE_FLOAT
-#define GLK_GLULXE_FLOAT
-
-#include "common/scummsys.h"
-#include "glk/glk_api.h"
-
-namespace Glk {
-namespace Glulxe {
-
-
-} // End of namespace Glulxe
-} // End of namespace Glk
-
-#endif
diff --git a/engines/glk/glulxe/glulxe.cpp b/engines/glk/glulxe/glulxe.cpp
index 40f4bdd..bb1a651 100644
--- a/engines/glk/glulxe/glulxe.cpp
+++ b/engines/glk/glulxe/glulxe.cpp
@@ -27,8 +27,14 @@
 namespace Glk {
 namespace Glulxe {
 
+Glulxe *g_vm;
+
 Glulxe::Glulxe(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc),
-		vm_exited_cleanly(false) {
+		vm_exited_cleanly(false), gamefile(nullptr), gamefile_start(0), gamefile_len(0),
+		memmap(nullptr), stack(nullptr), ramstart(0), endgamefile(0), origendmem(0),  stacksize(0),
+		startfuncaddr(0), checksum(0), stackptr(0), frameptr(0), pc(0), prevpc(0), origstringtable(0),
+		stringtable(0), valstackbase(0), localsbase(0), endmem(0), protectstart(0), protectend(0) {
+	g_vm = this;
 }
 
 void Glulxe::runGame() {
@@ -73,5 +79,43 @@ bool Glulxe::is_gamefile_valid() {
 	return true;
 }
 
+void Glulxe::fatal_error_handler(const char *str, const char *arg, bool useVal, int val) {
+	Common::String msg = "Glulxe fatal error: ";
+
+	if (arg || useVal) {
+		msg += " (";
+
+		if (arg)
+			msg += Common::String::format("%s", arg);
+		if (arg && useVal)
+			msg += " ";
+		if (useVal)
+			msg += Common::String::format("%x", val);
+
+		msg += ")";
+	}
+
+	error("%s", msg.c_str());
+}
+
+void Glulxe::nonfatal_warning_handler(const char *str, const char *arg, bool useVal, int val) {
+	Common::String msg = "Glulxe warning: ";
+
+	if (arg || useVal) {
+		msg += " (";
+
+		if (arg)
+			msg += Common::String::format("%s", arg);
+		if (arg && useVal)
+			msg += " ";
+		if (useVal)
+			msg += Common::String::format("%x", val);
+
+		msg += ")";
+	}
+
+	warning("%s", msg.c_str());
+}
+
 } // End of namespace Glulxe
 } // End of namespace Glk
diff --git a/engines/glk/glulxe/glulxe.h b/engines/glk/glulxe/glulxe.h
index decdc29..b1f4453 100644
--- a/engines/glk/glulxe/glulxe.h
+++ b/engines/glk/glulxe/glulxe.h
@@ -25,6 +25,7 @@
 
 #include "common/scummsys.h"
 #include "glk/glk_api.h"
+#include "glk/glulxe/glulxe_types.h"
 
 namespace Glk {
 namespace Glulxe {
@@ -35,11 +36,111 @@ namespace Glulxe {
 class Glulxe : public GlkAPI {
 public:
 	bool vm_exited_cleanly;
-private:
+	strid_t gamefile;
+	uint gamefile_start, gamefile_len;
+	char *init_err, *init_err2;
+
+	unsigned char *memmap;
+	unsigned char *stack;
+
+	uint ramstart;
+	uint endgamefile;
+	uint origendmem;
+	uint stacksize;
+	uint startfuncaddr;
+	uint checksum;
+	uint stackptr;
+	uint frameptr;
+	uint pc;
+	uint origstringtable;
+	uint stringtable;
+	uint valstackbase;
+	uint localsbase;
+	uint endmem;
+	uint protectstart, protectend;
+	uint prevpc;
+protected:
 	/**
-	 * Validates the game file, and if it's invalid, displays an error dialog
+	 * \defgroup glkop fields
+	 * @{
 	 */
-	bool is_gamefile_valid();
+
+	/**
+	 * The library_select_hook is called every time the VM blocks for input.
+	 * The app might take this opportunity to autosave, for example.
+	 */
+	void (*library_select_hook)(uint);
+
+	arrayref_t *arrays;
+
+	/**
+	 * The list of hash tables, for the classes.
+	 */
+	int num_classes;
+	classtable_t **classes;
+
+	/**@}*/
+protected:
+	/**
+	 * \defgroup glkop support methods
+	 * @{
+	 */
+
+	/**
+	 * Build a hash table to hold a set of Glk objects.
+	 */
+	classtable_t *new_classtable(uint firstid);
+
+	/**
+	 * Find a Glk object in the appropriate hash table.
+	 */
+	void *classes_get(int classid, uint objid);
+
+	/**
+	 * Put a Glk object in the appropriate hash table. If origid is zero, invent a new
+	 * unique ID for it.
+	 */
+	classref_t *classes_put(int classid, void *obj, uint origid);
+
+	/**
+	 * Delete a Glk object from the appropriate hash table.
+	 */
+	void classes_remove(int classid, void *obj);
+
+	long glulxe_array_locate(void *array, uint len, char *typecode, gidispatch_rock_t objrock, int *elemsizeref);
+	gidispatch_rock_t glulxe_array_restore(long bufkey, uint len, char *typecode, void **arrayref);
+
+	char *grab_temp_c_array(uint addr, uint len, int passin);
+	void release_temp_c_array(char *arr, uint addr, uint len, int passout);
+	uint *grab_temp_i_array(uint addr, uint len, int passin);
+	void release_temp_i_array(uint *arr, uint addr, uint len, int passout);
+	void **grab_temp_ptr_array(uint addr, uint len, int objclass, int passin);
+	void release_temp_ptr_array(void **arr, uint addr, uint len, int objclass, int passout);
+
+	/**
+	 * This reads through the prototype string, and pulls Floo objects off the stack. It also works out the maximal number
+	 * of gluniversal_t objects which could be used by the Glk call in question. It then allocates space for them.
+	 */
+	void prepare_glk_args(const char *proto, dispatch_splot_t *splot);
+
+	/**
+	 * This long and unpleasant function translates a set of Floo objects into a gluniversal_t array. It's recursive, too,
+	 * to deal with structures.
+	 */
+	void parse_glk_args(dispatch_splot_t *splot, const char **proto, int depth, int *argnumptr, uint subaddress, int subpassin);
+		
+	/**
+	 * This is about the reverse of parse_glk_args().
+	 */
+	void unparse_glk_args(dispatch_splot_t *splot, const char **proto, int depth,
+		int *argnumptr, uint subaddress, int subpassout);
+
+	/**
+	 * Create a string identifying this game. We use the first 64 bytes of the memory map, encoded as hex,
+	 */
+	char *get_game_id();
+
+	/**@}*/
 public:
 	/**
 	 * Constructor
@@ -65,8 +166,354 @@ public:
 	 * Save the game to the passed stream
 	 */
 	virtual Common::Error saveGameData(strid_t file, const Common::String &desc) override;
+
+	/**
+	 * \defgroup Main access methods
+	 * @{
+	 */
+	void set_library_start_hook(void(*)(void));
+	void set_library_autorestore_hook(void(*)(void));
+
+	/**
+	 * Display an error in the error window, and then exit.
+	 */
+	void fatal_error_handler(const char *str, const char *arg, bool useVal, int val);
+
+	/**
+	 * Display a warning in the error window, and then continue.
+	 */
+	void nonfatal_warning_handler(const char *str, const char *arg, bool useVal, int val);
+#define fatal_error(s)  (fatal_error_handler((s), nullptr, false, 0))
+#define fatal_error_2(s1, s2)  (fatal_error_handler((s1), (s2), false, 0))
+#define fatal_error_i(s, v)  (fatal_error_handler((s), nullptr, true, (v)))
+#define nonfatal_warning(s) (nonfatal_warning_handler((s), nullptr, false, 0))
+#define nonfatal_warning_2(s1, s2) (nonfatal_warning_handler((s1), (s2), false, 0))
+#define nonfatal_warning_i(s, v) (nonfatal_warning_handler((s), nullptr, true, (v)))
+
+	/**
+	 * \defgroup Files access methods
+	 * @{
+	 */
+
+	 /**
+	  * Validates the game file, and if it's invalid, displays an error dialog
+	  */
+	bool is_gamefile_valid();
+
+	int locate_gamefile(int isblorb);
+	
+	/**@}*/
+
+	/**
+	 * \defgroup Vm access methods
+	 * @{
+	 */
+
+	void setup_vm(void);
+	void finalize_vm(void);
+	void vm_restart(void);
+	uint change_memsize(uint newlen, int internal);
+	uint *pop_arguments(uint count, uint addr);
+	void verify_address(uint addr, uint count);
+	void verify_address_write(uint addr, uint count);
+	void verify_array_addresses(uint addr, uint count, uint size);
+
+	/**@}*/
+
+	/**
+	 * \defgroup Exec access methods
+	 * @{
+	 */
+	void execute_loop(void);
+
+	/**@}*/
+
+	/**
+	 * \defgroup Operand access methods
+	 * @{
+	 */	operandlist_t *fast_operandlist[0x80];
+	void init_operands(void);
+	operandlist_t *lookup_operandlist(uint opcode);
+	void parse_operands(oparg_t *opargs, operandlist_t *oplist);
+	void store_operand(uint desttype, uint destaddr, uint storeval);
+	void store_operand_s(uint desttype, uint destaddr, uint storeval);
+	void store_operand_b(uint desttype, uint destaddr, uint storeval);
+
+	/**@}*/
+
+	/**
+	 * \defgroup Func access methods
+	 * @{
+	 */
+
+	void enter_function(uint addr, uint argc, uint *argv);
+	void leave_function(void);
+	void push_callstub(uint desttype, uint destaddr);
+	void pop_callstub(uint returnvalue);
+	uint pop_callstub_string(int *bitnum);
+
+	/**@}*/
+
+	/**
+	 * \defgroup Strings access methods
+	 * @{
+	 */
+
+	void stream_num(int val, int inmiddle, int charnum);
+	void stream_string(uint addr, int inmiddle, int bitnum);
+	uint stream_get_table(void);
+	void stream_set_table(uint addr);
+	void stream_get_iosys(uint *mode, uint *rock);
+	void stream_set_iosys(uint mode, uint rock);
+	char *make_temp_string(uint addr);
+	uint *make_temp_ustring(uint addr);
+	void free_temp_string(const char *str);
+	void free_temp_ustring(const uint *str);
+
+	/**@}*/
+
+	/**
+	 * \defgroup Heap access methods
+	 * @{
+	 */
+	void heap_clear(void);
+	int heap_is_active(void);
+	uint heap_get_start(void);
+	uint heap_alloc(uint len);
+	void heap_free(uint addr);
+	int heap_get_summary(uint *valcount, uint **summary);
+	int heap_apply_summary(uint valcount, uint *summary);
+	void heap_sanity_check(void);
+
+	/**@}*/
+
+	/**
+	 * \defgroup Serial access methods
+	 * @{
+	 */
+
+	int max_undo_level;
+	int init_serial(void);
+	void final_serial(void);
+	uint perform_save(strid_t str);
+	uint perform_restore(strid_t str, int fromshell);
+	uint perform_saveundo(void);
+	uint perform_restoreundo(void);
+	uint perform_verify(void);
+
+	/**@}*/
+
+	/**
+	 * \defgroup Search access methods
+	 * @{
+	 */
+
+	uint linear_search(uint key, uint keysize,
+		uint start, uint structsize, uint numstructs,
+		uint keyoffset, uint options);
+	uint binary_search(uint key, uint keysize,
+		uint start, uint structsize, uint numstructs,
+		uint keyoffset, uint options);
+	uint linked_search(uint key, uint keysize,
+		uint start, uint keyoffset, uint nextoffset,
+		uint options);
+
+	/**@}*/
+
+	/**
+	 * \defgroup Osdepend access methods
+	 * @{
+	 */
+
+	void *glulx_malloc(uint len);
+	void *glulx_realloc(void *ptr, uint len);
+	void glulx_free(void *ptr);
+	void glulx_setrandom(uint seed);
+	uint glulx_random(void);
+	void glulx_sort(void *addr, int count, int size,
+		int(*comparefunc)(void *p1, void *p2));
+
+	/**@}*/
+
+	/**
+	 * \defgroup Gestalt access methods
+	 * @{
+	 */
+
+	uint do_gestalt(uint val, uint val2);
+
+	/**@}*/
+
+	/**
+	 * \defgroup Glkop access methods
+	 * @{
+	 */
+
+	 /**
+	  * glkop section initialization
+	  */
+	void glkopInit();
+
+	void set_library_select_hook(void(*func)(uint));
+
+	bool init_dispatch();
+
+	/**
+	 * The object registration/unregistration callbacks that the library calls
+	 * to keep the hash tables up to date.
+	 */
+	gidispatch_rock_t glulxe_classtable_register(void *obj, uint objclass);
+
+	gidispatch_rock_t glulxe_classtable_register_existing(void *obj, uint objclass, uint dispid);
+
+	void glulxe_classtable_unregister(void *obj, uint objclass, gidispatch_rock_t objrock);
+
+	gidispatch_rock_t glulxe_retained_register(void *array, uint len, char *typecode);
+	void glulxe_retained_unregister(void *array, uint len, char *typecode, gidispatch_rock_t objrock);
+
+	/**
+	 * Turn a list of Glulx arguments into a list of Glk arguments, dispatch the function call, and return the result.
+	 */
+	uint perform_glk(uint funcnum, uint numargs, uint *arglist);
+
+	/**
+	 * Read the prefixes of an argument string -- the "<>&+:#!" chars.
+	 */
+	const char *read_prefix(const char *cx, int *isref, int *isarray, int *passin, int *passout,
+		int *nullok, int *isretained, int *isreturn);
+
+	/**
+	 * This is used by some interpreter code which has to, well, find a Glk stream given its ID.
+	 */
+	strid_t find_stream_by_id(uint objid);
+
+	/**
+	 * Return the ID of a given Glk window.
+	 */
+	uint find_id_for_window(winid_t win);
+
+	/**
+	 * Return the ID of a given Glk stream.
+	 */
+	uint find_id_for_stream(strid_t str);
+
+	/**
+	 * Return the ID of a given Glk fileref.
+	 */
+	uint find_id_for_fileref(frefid_t fref);
+
+	/**
+	 * Return the ID of a given Glk schannel.
+	 */
+	uint find_id_for_schannel(schanid_t schan);
+
+	/**@}*/
+
+	/**
+	 * \defgroup Profile access methods
+	 * @{
+	 */
+
+	void setup_profile(strid_t stream, char *filename);
+	int init_profile(void);
+	void profile_set_call_counts(int flag);
+#if VM_PROFILING
+	uint profile_opcount;
+#define profile_tick() (profile_opcount++)
+	int profile_profiling_active(void);
+	void profile_in(uint addr, uint stackuse, int accel);
+	void profile_out(uint stackuse);
+	void profile_fail(char *reason);
+	void profile_quit(void);
+#else /* VM_PROFILING */
+#define profile_tick()         (0)
+#define profile_profiling_active()         (0)
+#define profile_in(addr, stackuse, accel)  (0)
+#define profile_out(stackuse)  (0)
+#define profile_fail(reason)   (0)
+#define profile_quit()         (0)
+#endif /* VM_PROFILING */
+
+#if VM_DEBUGGER
+	unsigned long debugger_opcount;
+#define debugger_tick() (debugger_opcount++)
+	int debugger_load_info_stream(strid_t stream);
+	int debugger_load_info_chunk(strid_t stream, uint pos, uint len);
+	void debugger_track_cpu(int flag);
+	void debugger_set_start_trap(int flag);
+	void debugger_set_quit_trap(int flag);
+	void debugger_set_crash_trap(int flag);
+	void debugger_check_story_file(void);
+	void debugger_setup_start_state(void);
+	int debugger_ever_invoked(void);
+	int debugger_cmd_handler(char *cmd);
+	void debugger_cycle_handler(int cycle);
+	void debugger_check_func_breakpoint(uint addr);
+	void debugger_block_and_debug(char *msg);
+	void debugger_handle_crash(char *msg);
+	void debugger_handle_quit(void);
+#else /* VM_DEBUGGER */
+#define debugger_tick()              (0)
+#define debugger_check_story_file()  (0)
+#define debugger_setup_start_state() (0)
+#define debugger_check_func_breakpoint(addr)  (0)
+#define debugger_handle_crash(msg)   (0)
+#endif /* VM_DEBUGGER */
+
+	/**@}*/
+
+	/**
+	 * \defgroup Accel access methods
+	 * @{
+	 */
+
+	typedef uint(*acceleration_func)(uint argc, uint *argv);
+	void init_accel(void);
+	acceleration_func accel_find_func(uint index);
+	acceleration_func accel_get_func(uint addr);
+	void accel_set_func(uint index, uint addr);
+	void accel_set_param(uint index, uint val);
+	uint accel_get_param_count(void);
+	uint accel_get_param(uint index);
+	void accel_iterate_funcs(void(*func)(uint index, uint addr));
+
+	/**@}*/
+
+	/**
+	 * \defgroup Float access methods
+	 * @{
+	 */
+#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 */
+	int init_float(void);
+	uint encode_float(gfloat32 val);
+	gfloat32 decode_float(uint 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) */
+
+	gfloat32 glulx_powf(gfloat32 val1, gfloat32 val2);
+
+#endif /* FLOAT_SUPPORT */
+	/**@}*/
 };
 
+extern Glulxe *g_vm;
+
 } // End of namespace Glulxe
 } // End of namespace Glk
 
diff --git a/engines/glk/glulxe/glulxe_types.h b/engines/glk/glulxe/glulxe_types.h
new file mode 100644
index 0000000..52a6363
--- /dev/null
+++ b/engines/glk/glulxe/glulxe_types.h
@@ -0,0 +1,187 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_GLULXE_TYPES
+#define GLK_GLULXE_TYPES
+
+#include "common/scummsys.h"
+
+namespace Glk {
+namespace Glulxe {
+
+
+	/* Comment this definition to turn off memory-address checking. With
+	   verification on, all reads and writes to main memory will be checked
+	   to ensure they're in range. This is slower, but prevents malformed
+	   game files from crashing the interpreter. */
+#define VERIFY_MEMORY_ACCESS (1)
+
+	   /* Uncomment this definition to permit an exception for memory-address
+		  checking for @glk and @copy opcodes that try to write to memory address 0.
+		  This was a bug in old Superglus-built game files. */
+		  /* #define TOLERATE_SUPERGLUS_BUG (1) */
+
+		  /* Uncomment this definition to turn on Glulx VM profiling. In this
+			 mode, all function calls are timed, and the timing information is
+			 written to a data file called "profile-raw".
+			 (Build note: on Linux, glibc may require you to also define
+			 _BSD_SOURCE or _DEFAULT_SOURCE or both for the timeradd() macro.) */
+			 /* #define VM_PROFILING (1) */
+
+			 /* Uncomment this definition to turn on the Glulx debugger. You should
+				only do this when debugging facilities are desired; it slows down
+				the interpreter. If you do, you will need to build with libxml2;
+				see the Makefile. */
+				/* #define VM_DEBUGGER (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)
+
+				   /* Comment this definition to not cache the original state of RAM in
+					  (real) memory. This saves some memory, but slows down save/restore/undo
+					  operations, which will have to read the original state off disk
+					  every time. */
+#define SERIALIZE_CACHE_RAM (1)
+
+/**
+ * Some macros to read and write integers to memory, always in big-endian format.
+ */
+#define Read4(ptr) READ_BE_UINT32(ptr)
+#define Read2(ptr) READ_BE_UINT16(ptr)
+#define Read1(ptr) ((byte)(((byte *)(ptr))[0]))
+#define Write4(ptr, vl) WRITE_BE_UINT32(ptr, vl)
+#define Write2(ptr, vl) WRITE_BE_UINT16(ptr, vl)
+#define Write1(ptr, vl) (((byte *)(ptr))[0] = (vl))
+
+#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)  (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 addresses which are multiples of four, etc.)
+ * If the alignment rules are not followed, the program will see performance
+ * degradation or even crashes, depending on the machine CPU.
+ */
+#define Stk1(adr)   \
+  (*((unsigned char *)(stack+(adr))))
+#define Stk2(adr)   \
+  (*((uint16 *)(stack+(adr))))
+#define Stk4(adr)   \
+  (*((uint32 *)(stack+(adr))))
+
+#define StkW1(adr, vl)   \
+  (*((byte *)(stack+(adr))) = (byte)(vl))
+#define StkW2(adr, vl)   \
+  (*((uint16 *)(stack+(adr))) = (uint16)(vl))
+#define StkW4(adr, vl)   \
+  (*((uint32 *)(stack+(adr))) = (uint32)(vl))
+
+
+struct dispatch_splot_struct {
+	int numwanted;
+	int maxargs;
+	gluniversal_t *garglist;
+	uint *varglist;
+	int numvargs;
+	uint *retval;
+};
+typedef dispatch_splot_struct dispatch_splot_t;
+
+/**
+ * We maintain a linked list of arrays being used for Glk calls. It is only used for integer
+ * (uint) arrays -- char arrays are handled in place. It's not worth bothering with a hash table,
+ * since most arrays appear here only momentarily.
+ */
+struct arrayref_struct {
+	void *array;
+	uint addr;
+	uint elemsize;
+	uint len; /* elements */
+	int retained;
+	arrayref_struct *next;
+};
+typedef arrayref_struct arrayref_t;
+
+/**
+ * We maintain a hash table for each opaque Glk class. classref_t are the nodes of the table,
+ * and classtable_t are the tables themselves.
+ */
+struct classref_struct {
+	void *obj;
+	uint id;
+	int bucknum;
+	classref_struct *next;
+};
+typedef classref_struct classref_t;
+
+#define CLASSHASH_SIZE (31)
+struct classtable_struct {
+	uint lastid;
+	classref_t *bucket[CLASSHASH_SIZE];
+};
+typedef classtable_struct classtable_t;
+
+/**
+ * Represents the operand structure of an opcode.
+ */
+struct operandlist_struct {
+	int num_ops; /* Number of operands for this opcode */
+	int arg_size; /* Usually 4, but can be 1 or 2 */
+	int *formlist; /* Array of values, either modeform_Load or modeform_Store */
+};
+typedef operandlist_struct operandlist_t;
+
+enum modeform {
+	modeform_Load = 1,
+	modeform_Store = 2
+};
+
+/**
+ * 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.
+*/
+struct oparg_struct {
+	uint desttype;
+	uint value;
+};
+typedef oparg_struct oparg_t;
+
+#define MAX_OPERANDS (8)
+
+} // End of namespace Glulxe
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/sound.cpp b/engines/glk/sound.cpp
index 7a93b77..70c33c2 100644
--- a/engines/glk/sound.cpp
+++ b/engines/glk/sound.cpp
@@ -75,6 +75,8 @@ void Sounds::poll() {
 
 SoundChannel::SoundChannel(Sounds *owner) : _owner(owner), _soundNum(0),
 		_rock(0), _notify(0) {
+	_dispRock.num = 0;
+	_dispRock.ptr = nullptr;
 }
 
 SoundChannel::~SoundChannel() {
diff --git a/engines/glk/sound.h b/engines/glk/sound.h
index 337239b..251c75a 100644
--- a/engines/glk/sound.h
+++ b/engines/glk/sound.h
@@ -43,6 +43,7 @@ private:
 	Audio::SoundHandle _handle;
 public:
 	uint _rock;
+	gidispatch_rock_t _dispRock;
 public:
 	/**
 	 * Constructor
diff --git a/engines/glk/streams.cpp b/engines/glk/streams.cpp
index 0fd9801..84904f9 100644
--- a/engines/glk/streams.cpp
+++ b/engines/glk/streams.cpp
@@ -34,8 +34,10 @@
 namespace Glk {
 
 Stream::Stream(Streams *streams, bool readable, bool writable, uint rock, bool unicode) :
-	_streams(streams), _readable(readable), _writable(writable), _rock(0), _unicode(unicode),
-	_readCount(0), _writeCount(0), _prev(nullptr), _next(nullptr) {
+		_streams(streams), _readable(readable), _writable(writable), _rock(0), _unicode(unicode),
+		_readCount(0), _writeCount(0), _prev(nullptr), _next(nullptr) {
+	_dispRock.num = 0;
+	_dispRock.ptr = nullptr;
 }
 
 Stream::~Stream() {
diff --git a/engines/glk/streams.h b/engines/glk/streams.h
index a17ee28..c254a89 100644
--- a/engines/glk/streams.h
+++ b/engines/glk/streams.h
@@ -136,6 +136,7 @@ public:
 	Stream *_prev;
 	Stream *_next;
 	uint _rock;
+	gidispatch_rock_t _dispRock;
 	bool _unicode;
 	uint _readCount;
 	uint _writeCount;


Commit: c3ebb5e68a0b36d992a48b508ab15c13a2edfd9c
    https://github.com/scummvm/scummvm/commit/c3ebb5e68a0b36d992a48b508ab15c13a2edfd9c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-04-17T20:46:06-07:00

Commit Message:
GLK: GLULXE: Added accel methods

Changed paths:
  A engines/glk/glulxe/accel.cpp
    engines/glk/glulxe/glulxe.cpp
    engines/glk/glulxe/glulxe.h
    engines/glk/glulxe/glulxe_types.h
    engines/glk/module.mk


diff --git a/engines/glk/glulxe/accel.cpp b/engines/glk/glulxe/accel.cpp
new file mode 100644
index 0000000..e4d60cf
--- /dev/null
+++ b/engines/glk/glulxe/accel.cpp
@@ -0,0 +1,622 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/glk/glulxe/glulxe.h"
+
+namespace Glk {
+namespace Glulxe {
+
+/**
+ * Git passes along function arguments in reverse order. To make our lives more interesting
+ */
+#ifdef ARGS_REVERSED
+#define ARG(argv, argc, ix) (argv[(argc-1)-ix])
+#else
+#define ARG(argv, argc, ix) (argv[ix])
+#endif
+
+/**
+ * Any function can be called with any number of arguments. This macro lets us snarf a given argument,
+ * or zero if it wasn't supplied.
+ */
+#define ARG_IF_GIVEN(argv, argc, ix)  ((argc > ix) ? (ARG(argv, argc, ix)) : 0)
+
+acceleration_func Glulxe::accel_find_func(uint index) {
+    switch (index) {
+        case 0: return nullptr;		// 0 always means no acceleration
+        case 1: return &Glulxe::func_1_z__region;
+        case 2: return &Glulxe::func_2_cp__tab;
+        case 3: return &Glulxe::func_3_ra__pr;
+        case 4: return &Glulxe::func_4_rl__pr;
+        case 5: return &Glulxe::func_5_oc__cl;
+        case 6: return &Glulxe::func_6_rv__pr;
+        case 7: return &Glulxe::func_7_op__pr;
+        case 8: return &Glulxe::func_8_cp__tab;
+        case 9: return &Glulxe::func_9_ra__pr;
+        case 10: return &Glulxe::func_10_rl__pr;
+        case 11: return &Glulxe::func_11_oc__cl;
+        case 12: return &Glulxe::func_12_rv__pr;
+        case 13: return &Glulxe::func_13_op__pr;
+    }
+    return nullptr;
+}
+
+acceleration_func Glulxe::accel_get_func(uint addr) {
+    int bucknum;
+    accelentry_t *ptr;
+
+    if (!accelentries)
+        return nullptr;
+
+    bucknum = (addr % ACCEL_HASH_SIZE);
+    for (ptr = accelentries[bucknum]; ptr; ptr = ptr->next) {
+        if (ptr->addr == addr)
+            return ptr->func;
+    }
+    return nullptr;
+}
+
+void Glulxe::accel_iterate_funcs(void (*func)(uint index, uint addr)) {
+    int bucknum;
+    accelentry_t *ptr;
+
+    if (!accelentries)
+        return;
+
+    for (bucknum=0; bucknum<ACCEL_HASH_SIZE; bucknum++) {
+        for (ptr = accelentries[bucknum]; ptr; ptr = ptr->next) {
+            if (ptr->func) {
+                func(ptr->index, ptr->addr);
+            }
+        }
+    }
+}
+
+void Glulxe::accel_set_func(uint index, uint addr) {
+    int bucknum;
+    accelentry_t *ptr;
+    int functype;
+    acceleration_func new_func = nullptr;
+
+    /* Check the Glulx type identifier byte. */
+    functype = Mem1(addr);
+    if (functype != 0xC0 && functype != 0xC1) {
+        fatal_error_i("Attempt to accelerate non-function.", addr);
+    }
+
+    if (!accelentries) {
+        accelentries = (accelentry_t **)glulx_malloc(ACCEL_HASH_SIZE 
+            * sizeof(accelentry_t *));
+        if (!accelentries) 
+            fatal_error("Cannot malloc acceleration table.");
+        for (bucknum=0; bucknum<ACCEL_HASH_SIZE; bucknum++)
+            accelentries[bucknum] = nullptr;
+    }
+
+    new_func = accel_find_func(index);
+    /* Might be nullptr, if the index is zero or not recognized. */
+
+    bucknum = (addr % ACCEL_HASH_SIZE);
+    for (ptr = accelentries[bucknum]; ptr; ptr = ptr->next) {
+        if (ptr->addr == addr)
+            break;
+    }
+    if (!ptr) {
+        if (!new_func) {
+            return; /* no need for a new entry */
+        }
+        ptr = (accelentry_t *)glulx_malloc(sizeof(accelentry_t));
+        if (!ptr)
+            fatal_error("Cannot malloc acceleration entry.");
+        ptr->addr = addr;
+        ptr->index = 0;
+        ptr->func = nullptr;
+        ptr->next = accelentries[bucknum];
+        accelentries[bucknum] = ptr;
+    }
+
+    ptr->index = index;
+    ptr->func = new_func;
+}
+
+void Glulxe::accel_set_param(uint index, uint val) {
+    switch (index) {
+        case 0: classes_table = val; break;
+        case 1: indiv_prop_start = val; break;
+        case 2: class_metaclass = val; break;
+        case 3: object_metaclass = val; break;
+        case 4: routine_metaclass = val; break;
+        case 5: string_metaclass = val; break;
+        case 6: self = val; break;
+        case 7: num_attr_bytes = val; break;
+        case 8: cpv__start = val; break;
+    }
+}
+
+uint Glulxe::accel_get_param_count() const {
+    return 9;
+}
+
+uint Glulxe::accel_get_param(uint index) const {
+    switch (index) {
+        case 0: return classes_table;
+        case 1: return indiv_prop_start;
+        case 2: return class_metaclass;
+        case 3: return object_metaclass;
+        case 4: return routine_metaclass;
+        case 5: return string_metaclass;
+        case 6: return self;
+        case 7: return num_attr_bytes;
+        case 8: return cpv__start;
+        default: return 0;
+    }
+}
+
+void Glulxe::accel_error(const char *msg) {
+    glk_put_char('\n');
+    glk_put_string(msg);
+    glk_put_char('\n');
+}
+
+int Glulxe::obj_in_class(uint obj) {
+    // This checks whether obj is contained in Class, not whether it is a member of Class
+    return (Mem4(obj + 13 + num_attr_bytes) == class_metaclass);
+}
+
+uint Glulxe::get_prop(uint obj, uint id) {
+    uint cla = 0;
+    uint prop;
+    uint call_argv[2];
+
+    if (id & 0xFFFF0000) {
+        cla = Mem4(classes_table+((id & 0xFFFF) * 4));
+        ARG(call_argv, 2, 0) = obj;
+        ARG(call_argv, 2, 1) = cla;
+        if (func_5_oc__cl(2, call_argv) == 0)
+            return 0;
+
+        id >>= 16;
+        obj = cla;
+    }
+
+    ARG(call_argv, 2, 0) = obj;
+    ARG(call_argv, 2, 1) = id;
+    prop = func_2_cp__tab(2, call_argv);
+    if (prop == 0)
+        return 0;
+
+    if (obj_in_class(obj) && (cla == 0)) {
+        if ((id < indiv_prop_start) || (id >= indiv_prop_start+8))
+            return 0;
+    }
+
+    if (Mem4(self) != obj) {
+        if (Mem1(prop + 9) & 1)
+            return 0;
+    }
+    return prop;
+}
+
+uint Glulxe::get_prop_new(uint obj, uint id) {
+    uint cla = 0;
+    uint prop;
+    uint call_argv[2];
+
+    if (id & 0xFFFF0000) {
+        cla = Mem4(classes_table+((id & 0xFFFF) * 4));
+        ARG(call_argv, 2, 0) = obj;
+        ARG(call_argv, 2, 1) = cla;
+        if (func_11_oc__cl(2, call_argv) == 0)
+            return 0;
+
+        id >>= 16;
+        obj = cla;
+    }
+
+    ARG(call_argv, 2, 0) = obj;
+    ARG(call_argv, 2, 1) = id;
+    prop = func_8_cp__tab(2, call_argv);
+    if (prop == 0)
+        return 0;
+
+    if (obj_in_class(obj) && (cla == 0)) {
+        if ((id < indiv_prop_start) || (id >= indiv_prop_start+8))
+            return 0;
+    }
+
+    if (Mem4(self) != obj) {
+        if (Mem1(prop + 9) & 1)
+            return 0;
+    }
+    return prop;
+}
+
+uint Glulxe::func_1_z__region(uint argc, uint *argv) {
+    uint addr;
+    uint tb;
+
+    if (argc < 1)
+        return 0;
+
+    addr = ARG(argv, argc, 0);
+    if (addr < 36)
+        return 0;
+    if (addr >= endmem)
+        return 0;
+
+    tb = Mem1(addr);
+    if (tb >= 0xE0) {
+        return 3;
+    }
+    if (tb >= 0xC0) {
+        return 2;
+    }
+    if (tb >= 0x70 && tb <= 0x7F && addr >= ramstart) {
+        return 1;
+    }
+    return 0;
+}
+
+uint Glulxe::func_2_cp__tab(uint argc, uint *argv) {
+    uint obj;
+    uint id;
+    uint otab, max;
+
+    obj = ARG_IF_GIVEN(argv, argc, 0);
+    id = ARG_IF_GIVEN(argv, argc, 1);
+
+    if (func_1_z__region(1, &obj) != 1) {
+        accel_error("[** Programming error: tried to find the \".\" of (something) **]");
+        return 0;
+    }
+
+    otab = Mem4(obj + 16);
+    if (!otab)
+        return 0;
+
+    max = Mem4(otab);
+    otab += 4;
+    /* @binarysearch id 2 otab 10 max 0 0 res; */
+    return binary_search(id, 2, otab, 10, max, 0, 0);
+}
+
+uint Glulxe::func_3_ra__pr(uint argc, uint *argv) {
+    uint obj;
+    uint id;
+    uint prop;
+
+    obj = ARG_IF_GIVEN(argv, argc, 0);
+    id = ARG_IF_GIVEN(argv, argc, 1);
+
+    prop = get_prop(obj, id);
+    if (prop == 0)
+        return 0;
+
+    return Mem4(prop + 4);
+}
+
+uint Glulxe::func_4_rl__pr(uint argc, uint *argv) {
+    uint obj;
+    uint id;
+    uint prop;
+
+    obj = ARG_IF_GIVEN(argv, argc, 0);
+    id = ARG_IF_GIVEN(argv, argc, 1);
+
+    prop = get_prop(obj, id);
+    if (prop == 0)
+        return 0;
+
+    return 4 * Mem2(prop + 2);
+}
+
+uint Glulxe::func_5_oc__cl(uint argc, uint *argv) {
+    uint obj;
+    uint cla;
+    uint zr, prop, inlist, inlistlen, jx;
+
+    obj = ARG_IF_GIVEN(argv, argc, 0);
+    cla = ARG_IF_GIVEN(argv, argc, 1);
+
+    zr = func_1_z__region(1, &obj);
+    if (zr == 3)
+        return (cla == string_metaclass) ? 1 : 0;
+    if (zr == 2)
+        return (cla == routine_metaclass) ? 1 : 0;
+    if (zr != 1)
+        return 0;
+
+    if (cla == class_metaclass) {
+        if (obj_in_class(obj))
+            return 1;
+        if (obj == class_metaclass)
+            return 1;
+        if (obj == string_metaclass)
+            return 1;
+        if (obj == routine_metaclass)
+            return 1;
+        if (obj == object_metaclass)
+            return 1;
+        return 0;
+    }
+    if (cla == object_metaclass) {
+        if (obj_in_class(obj))
+            return 0;
+        if (obj == class_metaclass)
+            return 0;
+        if (obj == string_metaclass)
+            return 0;
+        if (obj == routine_metaclass)
+            return 0;
+        if (obj == object_metaclass)
+            return 0;
+        return 1;
+    }
+    if ((cla == string_metaclass) || (cla == routine_metaclass))
+        return 0;
+
+    if (!obj_in_class(cla)) {
+        accel_error("[** Programming error: tried to apply 'ofclass' with non-class **]");
+        return 0;
+    }
+
+    prop = get_prop(obj, 2);
+    if (prop == 0)
+       return 0;
+
+    inlist = Mem4(prop + 4);
+    if (inlist == 0)
+       return 0;
+
+    inlistlen = Mem2(prop + 2);
+    for (jx = 0; jx < inlistlen; jx++) {
+        if (Mem4(inlist + (4 * jx)) == cla)
+            return 1;
+    }
+    return 0;
+}
+
+uint Glulxe::func_6_rv__pr(uint argc, uint *argv) {
+    uint id;
+    uint addr;
+
+    id = ARG_IF_GIVEN(argv, argc, 1);
+
+    addr = func_3_ra__pr(argc, argv);
+
+    if (addr == 0) {
+        if ((id > 0) && (id < indiv_prop_start))
+            return Mem4(cpv__start + (4 * id));
+
+        accel_error("[** Programming error: tried to read (something) **]");
+        return 0;
+    }
+
+    return Mem4(addr);
+}
+
+uint Glulxe::func_7_op__pr(uint argc, uint *argv) {
+    uint obj;
+    uint id;
+    uint zr;
+
+    obj = ARG_IF_GIVEN(argv, argc, 0);
+    id = ARG_IF_GIVEN(argv, argc, 1);
+
+    zr = func_1_z__region(1, &obj);
+    if (zr == 3) {
+        /* print is INDIV_PROP_START+6 */
+        if (id == indiv_prop_start+6)
+            return 1;
+        /* print_to_array is INDIV_PROP_START+7 */
+        if (id == indiv_prop_start+7)
+            return 1;
+        return 0;
+    }
+    if (zr == 2) {
+        /* call is INDIV_PROP_START+5 */
+        return ((id == indiv_prop_start+5) ? 1 : 0);
+    }
+    if (zr != 1)
+        return 0;
+
+    if ((id >= indiv_prop_start) && (id < indiv_prop_start+8)) {
+        if (obj_in_class(obj))
+            return 1;
+    }
+
+    return ((func_3_ra__pr(argc, argv)) ? 1 : 0);
+}
+
+uint Glulxe::func_8_cp__tab(uint argc, uint *argv) {
+    uint obj;
+    uint id;
+    uint otab, max;
+
+    obj = ARG_IF_GIVEN(argv, argc, 0);
+    id = ARG_IF_GIVEN(argv, argc, 1);
+
+    if (func_1_z__region(1, &obj) != 1) {
+        accel_error("[** Programming error: tried to find the \".\" of (something) **]");
+        return 0;
+    }
+
+    otab = Mem4(obj + 4*(3+(int)(num_attr_bytes/4)));
+    if (!otab)
+        return 0;
+
+    max = Mem4(otab);
+    otab += 4;
+    /* @binarysearch id 2 otab 10 max 0 0 res; */
+    return binary_search(id, 2, otab, 10, max, 0, 0);
+}
+
+uint Glulxe::func_9_ra__pr(uint argc, uint *argv) {
+    uint obj;
+    uint id;
+    uint prop;
+
+    obj = ARG_IF_GIVEN(argv, argc, 0);
+    id = ARG_IF_GIVEN(argv, argc, 1);
+
+    prop = get_prop_new(obj, id);
+    if (prop == 0)
+        return 0;
+
+    return Mem4(prop + 4);
+}
+
+uint Glulxe::func_10_rl__pr(uint argc, uint *argv) {
+    uint obj;
+    uint id;
+    uint prop;
+
+    obj = ARG_IF_GIVEN(argv, argc, 0);
+    id = ARG_IF_GIVEN(argv, argc, 1);
+
+    prop = get_prop_new(obj, id);
+    if (prop == 0)
+        return 0;
+
+    return 4 * Mem2(prop + 2);
+}
+
+uint Glulxe::func_11_oc__cl(uint argc, uint *argv) {
+    uint obj;
+    uint cla;
+    uint zr, prop, inlist, inlistlen, jx;
+
+    obj = ARG_IF_GIVEN(argv, argc, 0);
+    cla = ARG_IF_GIVEN(argv, argc, 1);
+
+    zr = func_1_z__region(1, &obj);
+    if (zr == 3)
+        return (cla == string_metaclass) ? 1 : 0;
+    if (zr == 2)
+        return (cla == routine_metaclass) ? 1 : 0;
+    if (zr != 1)
+        return 0;
+
+    if (cla == class_metaclass) {
+        if (obj_in_class(obj))
+            return 1;
+        if (obj == class_metaclass)
+            return 1;
+        if (obj == string_metaclass)
+            return 1;
+        if (obj == routine_metaclass)
+            return 1;
+        if (obj == object_metaclass)
+            return 1;
+        return 0;
+    }
+    if (cla == object_metaclass) {
+        if (obj_in_class(obj))
+            return 0;
+        if (obj == class_metaclass)
+            return 0;
+        if (obj == string_metaclass)
+            return 0;
+        if (obj == routine_metaclass)
+            return 0;
+        if (obj == object_metaclass)
+            return 0;
+        return 1;
+    }
+    if ((cla == string_metaclass) || (cla == routine_metaclass))
+        return 0;
+
+    if (!obj_in_class(cla)) {
+        accel_error("[** Programming error: tried to apply 'ofclass' with non-class **]");
+        return 0;
+    }
+
+    prop = get_prop_new(obj, 2);
+    if (prop == 0)
+       return 0;
+
+    inlist = Mem4(prop + 4);
+    if (inlist == 0)
+       return 0;
+
+    inlistlen = Mem2(prop + 2);
+    for (jx = 0; jx < inlistlen; jx++) {
+        if (Mem4(inlist + (4 * jx)) == cla)
+            return 1;
+    }
+    return 0;
+}
+
+uint Glulxe::func_12_rv__pr(uint argc, uint *argv) {
+    uint id;
+    uint addr;
+
+    id = ARG_IF_GIVEN(argv, argc, 1);
+
+    addr = func_9_ra__pr(argc, argv);
+
+    if (addr == 0) {
+        if ((id > 0) && (id < indiv_prop_start))
+            return Mem4(cpv__start + (4 * id));
+
+        accel_error("[** Programming error: tried to read (something) **]");
+        return 0;
+    }
+
+    return Mem4(addr);
+}
+
+uint Glulxe::func_13_op__pr(uint argc, uint *argv) {
+    uint obj;
+    uint id;
+    uint zr;
+
+    obj = ARG_IF_GIVEN(argv, argc, 0);
+    id = ARG_IF_GIVEN(argv, argc, 1);
+
+    zr = func_1_z__region(1, &obj);
+    if (zr == 3) {
+        /* print is INDIV_PROP_START+6 */
+        if (id == indiv_prop_start+6)
+            return 1;
+        /* print_to_array is INDIV_PROP_START+7 */
+        if (id == indiv_prop_start+7)
+            return 1;
+        return 0;
+    }
+    if (zr == 2) {
+        /* call is INDIV_PROP_START+5 */
+        return ((id == indiv_prop_start+5) ? 1 : 0);
+    }
+    if (zr != 1)
+        return 0;
+
+    if ((id >= indiv_prop_start) && (id < indiv_prop_start+8)) {
+        if (obj_in_class(obj))
+            return 1;
+    }
+
+    return ((func_9_ra__pr(argc, argv)) ? 1 : 0);
+}
+
+} // End of namespace Glulxe
+} // End of namespace Glk
diff --git a/engines/glk/glulxe/glulxe.cpp b/engines/glk/glulxe/glulxe.cpp
index bb1a651..6fdfb05 100644
--- a/engines/glk/glulxe/glulxe.cpp
+++ b/engines/glk/glulxe/glulxe.cpp
@@ -33,7 +33,11 @@ Glulxe::Glulxe(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst,
 		vm_exited_cleanly(false), gamefile(nullptr), gamefile_start(0), gamefile_len(0),
 		memmap(nullptr), stack(nullptr), ramstart(0), endgamefile(0), origendmem(0),  stacksize(0),
 		startfuncaddr(0), checksum(0), stackptr(0), frameptr(0), pc(0), prevpc(0), origstringtable(0),
-		stringtable(0), valstackbase(0), localsbase(0), endmem(0), protectstart(0), protectend(0) {
+		stringtable(0), valstackbase(0), localsbase(0), endmem(0), protectstart(0), protectend(0),
+		// Accel
+		classes_table(0), indiv_prop_start(0), class_metaclass(0), object_metaclass(0),
+		routine_metaclass(0), string_metaclass(0), self(0), num_attr_bytes(0), cpv__start(0),
+		accelentries(nullptr) {
 	g_vm = this;
 }
 
diff --git a/engines/glk/glulxe/glulxe.h b/engines/glk/glulxe/glulxe.h
index b1f4453..839e9eb 100644
--- a/engines/glk/glulxe/glulxe.h
+++ b/engines/glk/glulxe/glulxe.h
@@ -59,6 +59,24 @@ public:
 	uint endmem;
 	uint protectstart, protectend;
 	uint prevpc;
+
+	/**
+	 * \defgroup accel fields
+	 * @{
+	 */
+
+	uint classes_table;     ///< class object array
+	uint indiv_prop_start;  ///< first individual prop ID
+	uint class_metaclass;   ///< "Class" class object
+	uint object_metaclass;  ///< "Object" class object
+	uint routine_metaclass; ///< "Routine" class object
+	uint string_metaclass;  ///< "String" class object
+	uint self;              ///< address of global "self"
+	uint num_attr_bytes;    ///< number of attributes / 8
+	uint cpv__start;        ///< array of common prop defaults
+	accelentry_t **accelentries;
+
+	/**@}*/
 protected:
 	/**
 	 * \defgroup glkop fields
@@ -82,6 +100,52 @@ protected:
 	/**@}*/
 protected:
 	/**
+	 * \defgroup accel support methods
+	 * @{
+	 */
+
+	void accel_error(const char *msg);
+	uint func_1_z__region(uint argc, uint *argv);
+
+	/**
+	 * The old set of accel functions (2 through 7) are deprecated; they behave badly if the Inform 6
+	 * NUM_ATTR_BYTES option (parameter 7) is changed from its default value (7). They will not be removed,
+	 * but new games should use functions 8 through 13 instead
+	 */
+	uint func_2_cp__tab(uint argc, uint *argv);
+	uint func_3_ra__pr(uint argc, uint *argv);
+	uint func_4_rl__pr(uint argc, uint *argv);
+	uint func_5_oc__cl(uint argc, uint *argv);
+	uint func_6_rv__pr(uint argc, uint *argv);
+	uint func_7_op__pr(uint argc, uint *argv);
+
+	/**
+	 * Here are the newer functions, which support changing NUM_ATTR_BYTES.
+	   These call get_prop_new() instead of get_prop()
+	 */
+	uint func_8_cp__tab(uint argc, uint *argv);
+	uint func_9_ra__pr(uint argc, uint *argv);
+	uint func_10_rl__pr(uint argc, uint *argv);
+	uint func_11_oc__cl(uint argc, uint *argv);
+	uint func_12_rv__pr(uint argc, uint *argv);
+	uint func_13_op__pr(uint argc, uint *argv);
+	int obj_in_class(uint obj);
+
+	/**
+	 * Look up a property entry.
+	 */
+	uint get_prop(uint obj, uint id);
+
+	/**
+	 * Look up a property entry. This is part of the newer set of accel functions (8 through 13),
+	 * which support increasing NUM_ATTR_BYTES. It is identical to get_prop() except that it calls
+	 * the new versions of func_5 and func_2
+	 */
+	uint get_prop_new(uint obj, uint id);
+
+	 /**@}*/
+
+	/**
 	 * \defgroup glkop support methods
 	 * @{
 	 */
@@ -467,14 +531,18 @@ public:
 	 * @{
 	 */
 
-	typedef uint(*acceleration_func)(uint argc, uint *argv);
-	void init_accel(void);
 	acceleration_func accel_find_func(uint index);
 	acceleration_func accel_get_func(uint addr);
 	void accel_set_func(uint index, uint addr);
 	void accel_set_param(uint index, uint val);
-	uint accel_get_param_count(void);
-	uint accel_get_param(uint index);
+
+	uint accel_get_param_count() const;
+	uint accel_get_param(uint index) const;
+
+	/**
+	 * Iterate the entire acceleration table, calling the callback for each (non-nullptr) entry.
+	 * This is used only for autosave.
+	 */
 	void accel_iterate_funcs(void(*func)(uint index, uint addr));
 
 	/**@}*/
diff --git a/engines/glk/glulxe/glulxe_types.h b/engines/glk/glulxe/glulxe_types.h
index 52a6363..b06e435 100644
--- a/engines/glk/glulxe/glulxe_types.h
+++ b/engines/glk/glulxe/glulxe_types.h
@@ -28,40 +28,46 @@
 namespace Glk {
 namespace Glulxe {
 
+class Glulxe;
 
-	/* Comment this definition to turn off memory-address checking. With
-	   verification on, all reads and writes to main memory will be checked
-	   to ensure they're in range. This is slower, but prevents malformed
-	   game files from crashing the interpreter. */
+/**
+ * Comment this definition to turn off memory-address checking. With verification on,
+ * all reads and writes to main memory will be checked to ensure they're in range.
+ * This is slower, but prevents malformed game files from crashing the interpreter.
+ */
 #define VERIFY_MEMORY_ACCESS (1)
 
-	   /* Uncomment this definition to permit an exception for memory-address
-		  checking for @glk and @copy opcodes that try to write to memory address 0.
-		  This was a bug in old Superglus-built game files. */
-		  /* #define TOLERATE_SUPERGLUS_BUG (1) */
-
-		  /* Uncomment this definition to turn on Glulx VM profiling. In this
-			 mode, all function calls are timed, and the timing information is
-			 written to a data file called "profile-raw".
-			 (Build note: on Linux, glibc may require you to also define
-			 _BSD_SOURCE or _DEFAULT_SOURCE or both for the timeradd() macro.) */
-			 /* #define VM_PROFILING (1) */
-
-			 /* Uncomment this definition to turn on the Glulx debugger. You should
-				only do this when debugging facilities are desired; it slows down
-				the interpreter. If you do, you will need to build with libxml2;
-				see the Makefile. */
-				/* #define VM_DEBUGGER (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. */
+/**
+ * Uncomment this definition to permit an exception for memory-address checking for @glk and @copy
+ * opcodes that try to write to memory address 0. This was a bug in old Superglus-built game files.
+ */
+/* #define TOLERATE_SUPERGLUS_BUG (1) */
+
+/**
+ * Uncomment this definition to turn on Glulx VM profiling. In this mode, all function calls are timed,
+ * and the timing information is written to a data file called "profile-raw".
+ * (Build note: on Linux, glibc may require you to also define _BSD_SOURCE or _DEFAULT_SOURCE or both
+ * for the timeradd() macro.)
+ */
+/* #define VM_PROFILING (1) */
+
+/**
+ * Uncomment this definition to turn on the Glulx debugger. You should only do this when debugging
+ * facilities are desired; it slows down the interpreter. If you do, you will need to build with libxml2;
+ * see the Makefile.
+ */
+/* #define VM_DEBUGGER (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)
 
-				   /* Comment this definition to not cache the original state of RAM in
-					  (real) memory. This saves some memory, but slows down save/restore/undo
-					  operations, which will have to read the original state off disk
-					  every time. */
+/**
+ * Comment this definition to not cache the original state of RAM in (real) memory. This saves some memory,
+ * but slows down save/restore/undo operations, which will have to read the original state off disk
+ * every time.
+ */
 #define SERIALIZE_CACHE_RAM (1)
 
 /**
@@ -181,6 +187,19 @@ typedef oparg_struct oparg_t;
 
 #define MAX_OPERANDS (8)
 
+typedef uint(Glulxe::*acceleration_func)(uint argc, uint *argv);
+
+struct accelentry_struct {
+	uint addr;
+	uint index;
+	acceleration_func func;
+	accelentry_struct *next;
+};
+typedef accelentry_struct accelentry_t;
+
+#define ACCEL_HASH_SIZE (511)
+
+
 } // End of namespace Glulxe
 } // End of namespace Glk
 
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 2a41942..6fe51c9 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -57,6 +57,7 @@ MODULE_OBJS := \
 	frotz/screen.o \
 	frotz/sound_folder.o \
 	frotz/windows.o \
+	glulxe/accel.o \
 	glulxe/detection.o \
 	glulxe/float.o \
 	glulxe/glkop.o \


Commit: 6307fd08ce7cbb0b939f520f9741aef884f66f88
    https://github.com/scummvm/scummvm/commit/6307fd08ce7cbb0b939f520f9741aef884f66f88
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-04-17T20:46:06-07:00

Commit Message:
GLK: GLULXE: Added exec methods

Changed paths:
  A engines/glk/glulxe/exec.cpp
    engines/glk/glulxe/glulxe.cpp
    engines/glk/glulxe/glulxe.h
    engines/glk/glulxe/glulxe_types.h
    engines/glk/module.mk


diff --git a/engines/glk/glulxe/exec.cpp b/engines/glk/glulxe/exec.cpp
new file mode 100644
index 0000000..f3b511f
--- /dev/null
+++ b/engines/glk/glulxe/exec.cpp
@@ -0,0 +1,1069 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/glk/glulxe/glulxe.h"
+
+namespace Glk {
+namespace Glulxe {
+
+void Glulxe::execute_loop() {
+  bool done_executing = false;
+  int ix;
+  uint opcode;
+  operandlist_t *oplist;
+  oparg_t inst[MAX_OPERANDS];
+  uint value, addr, val0, val1;
+  int vals0, vals1;
+  uint *arglist;
+  uint arglistfix[3];
+#ifdef FLOAT_SUPPORT
+  gfloat32 valf, valf1, valf2;
+#endif /* FLOAT_SUPPORT */
+
+  while (!done_executing) {
+
+    profile_tick();
+    debugger_tick();
+    /* Do OS-specific processing, if appropriate. */
+    glk_tick();
+    
+    /* Stash the current opcode's address, in case the interpreter needs to serialize the VM state out-of-band. */
+    prevpc = pc;
+    
+    /* Fetch the opcode number. */
+    opcode = Mem1(pc);
+    pc++;
+    if (opcode & 0x80) {
+      /* More than one-byte opcode. */
+      if (opcode & 0x40) {
+        /* Four-byte opcode */
+        opcode &= 0x3F;
+        opcode = (opcode << 8) | Mem1(pc);
+        pc++;
+        opcode = (opcode << 8) | Mem1(pc);
+        pc++;
+        opcode = (opcode << 8) | Mem1(pc);
+        pc++;
+      }
+      else {
+        /* Two-byte opcode */
+        opcode &= 0x7F;
+        opcode = (opcode << 8) | Mem1(pc);
+        pc++;
+      }
+    }
+
+    /* Now we have an opcode number. */
+    
+    /* Fetch the structure that describes how the operands for this
+       opcode are arranged. This is a pointer to an immutable, 
+       static object. */
+    if (opcode < 0x80)
+      oplist = fast_operandlist[opcode];
+    else
+      oplist = lookup_operandlist(opcode);
+
+    if (!oplist)
+      fatal_error_i("Encountered unknown opcode.", opcode);
+
+    /* 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);
+
+    /* Perform the opcode. This switch statement is split in two, based
+       on some paranoid suspicions about the ability of compilers to
+       optimize large-range switches. Ignore that. */
+
+    if (opcode < 0x80) {
+
+      switch (opcode) {
+
+      case op_nop:
+        break;
+
+      case op_add:
+        value = inst[0].value + inst[1].value;
+        store_operand(inst[2].desttype, inst[2].value, value);
+        break;
+      case op_sub:
+        value = inst[0].value - inst[1].value;
+        store_operand(inst[2].desttype, inst[2].value, value);
+        break;
+      case op_mul:
+        value = inst[0].value * inst[1].value;
+        store_operand(inst[2].desttype, inst[2].value, value);
+        break;
+      case op_div:
+        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. 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 = -(int)(val0 / val1);
+          }
+        }
+        else {
+          val0 = vals0;
+          if (vals1 < 0) {
+            val1 = (-vals1);
+            value = -(int)(val0 / val1);
+          }
+          else {
+            val1 = vals1;
+            value = val0 / val1;
+          }
+        }
+        store_operand(inst[2].desttype, inst[2].value, value);
+        break;
+      case op_mod:
+        vals0 = inst[0].value;
+        vals1 = inst[1].value;
+        if (vals1 == 0)
+          fatal_error("Division by zero doing remainder.");
+        if (vals1 < 0) {
+            val1 = -vals1;
+        }
+        else {
+            val1 = vals1;
+        }
+        if (vals0 < 0) {
+          val0 = (-vals0);
+          value = -(int)(val0 % val1);
+        }
+        else {
+          val0 = vals0;
+          value = val0 % val1;
+        }
+        store_operand(inst[2].desttype, inst[2].value, value);
+        break;
+      case op_neg:
+        vals0 = inst[0].value;
+        value = (-vals0);
+        store_operand(inst[1].desttype, inst[1].value, value);
+        break;
+
+      case op_bitand:
+        value = (inst[0].value & inst[1].value);
+        store_operand(inst[2].desttype, inst[2].value, value);
+        break;
+      case op_bitor:
+        value = (inst[0].value | inst[1].value);
+        store_operand(inst[2].desttype, inst[2].value, value);
+        break;
+      case op_bitxor:
+        value = (inst[0].value ^ inst[1].value);
+        store_operand(inst[2].desttype, inst[2].value, value);
+        break;
+      case op_bitnot:
+        value = ~(inst[0].value);
+        store_operand(inst[1].desttype, inst[1].value, value);
+        break;
+
+      case op_shiftl:
+        vals0 = inst[1].value;
+        if (vals0 < 0 || vals0 >= 32)
+          value = 0;
+        else
+          value = ((uint)(inst[0].value) << (uint)vals0);
+        store_operand(inst[2].desttype, inst[2].value, value);
+        break;
+      case op_ushiftr:
+        vals0 = inst[1].value;
+        if (vals0 < 0 || vals0 >= 32)
+          value = 0;
+        else
+          value = ((uint)(inst[0].value) >> (uint)vals0);
+        store_operand(inst[2].desttype, inst[2].value, value);
+        break;
+      case op_sshiftr:
+        vals0 = inst[1].value;
+        if (vals0 < 0 || vals0 >= 32) {
+          if (inst[0].value & 0x80000000)
+            value = 0xFFFFFFFF;
+          else
+            value = 0;
+        }
+        else {
+          /* 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 = ((int)(inst[0].value) >> (int)vals0);
+        }
+        store_operand(inst[2].desttype, inst[2].value, value);
+        break;
+
+      case op_jump:
+        value = inst[0].value;
+        /* fall through to PerformJump label. */
+
+      PerformJump: /* goto label for successful jumping... ironic, no? */
+        if (value == 0 || value == 1) {
+          /* Return from function. This is exactly what happens in
+             return_op, but it's only a few lines of code, so I won't
+             bother with a "goto". */
+          leave_function();
+          if (stackptr == 0) {
+            done_executing = true;
+            break;
+          }
+          pop_callstub(value); /* zero or one */
+        }
+        else {
+          /* Branch to a new PC value. */
+          pc = (pc + value - 2);
+        }
+        break;
+
+      case op_jz:
+        if (inst[0].value == 0) {
+          value = inst[1].value;
+          goto PerformJump;
+        }
+        break;
+      case op_jnz:
+        if (inst[0].value != 0) {
+          value = inst[1].value;
+          goto PerformJump;
+        }
+        break;
+      case op_jeq:
+        if (inst[0].value == inst[1].value) {
+          value = inst[2].value;
+          goto PerformJump;
+        }
+        break;
+      case op_jne:
+        if (inst[0].value != inst[1].value) {
+          value = inst[2].value;
+          goto PerformJump;
+        }
+        break;
+      case op_jlt:
+        vals0 = inst[0].value;
+        vals1 = inst[1].value;
+        if (vals0 < vals1) {
+          value = inst[2].value;
+          goto PerformJump;
+        }
+        break;
+      case op_jgt:
+        vals0 = inst[0].value;
+        vals1 = inst[1].value;
+        if (vals0 > vals1) {
+          value = inst[2].value;
+          goto PerformJump;
+        }
+        break;
+      case op_jle:
+        vals0 = inst[0].value;
+        vals1 = inst[1].value;
+        if (vals0 <= vals1) {
+          value = inst[2].value;
+          goto PerformJump;
+        }
+        break;
+      case op_jge:
+        vals0 = inst[0].value;
+        vals1 = inst[1].value;
+        if (vals0 >= vals1) {
+          value = inst[2].value;
+          goto PerformJump;
+        }
+        break;
+      case op_jltu:
+        val0 = inst[0].value;
+        val1 = inst[1].value;
+        if (val0 < val1) {
+          value = inst[2].value;
+          goto PerformJump;
+        }
+        break;
+      case op_jgtu:
+        val0 = inst[0].value;
+        val1 = inst[1].value;
+        if (val0 > val1) {
+          value = inst[2].value;
+          goto PerformJump;
+        }
+        break;
+      case op_jleu:
+        val0 = inst[0].value;
+        val1 = inst[1].value;
+        if (val0 <= val1) {
+          value = inst[2].value;
+          goto PerformJump;
+        }
+        break;
+      case op_jgeu:
+        val0 = inst[0].value;
+        val1 = inst[1].value;
+        if (val0 >= val1) {
+          value = inst[2].value;
+          goto PerformJump;
+        }
+        break;
+
+      case op_call:
+        value = inst[1].value;
+        arglist = pop_arguments(value, 0);
+        push_callstub(inst[2].desttype, inst[2].value);
+        enter_function(inst[0].value, value, arglist);
+        break;
+      case op_return:
+        leave_function();
+        if (stackptr == 0) {
+          done_executing = true;
+          break;
+        }
+        pop_callstub(inst[0].value);
+        break;
+      case op_tailcall:
+        value = inst[1].value;
+        arglist = pop_arguments(value, 0);
+        leave_function();
+        enter_function(inst[0].value, value, arglist);
+        break;
+
+      case op_catch:
+        push_callstub(inst[0].desttype, inst[0].value);
+        value = inst[1].value;
+        val0 = stackptr;
+        store_operand(inst[0].desttype, inst[0].value, val0);
+        goto PerformJump;
+        break;
+      case op_throw:
+        profile_fail("throw");
+        value = inst[0].value;
+        stackptr = inst[1].value;
+        pop_callstub(value);
+        break;
+
+      case op_copy:
+        value = inst[0].value;
+#ifdef TOLERATE_SUPERGLUS_BUG
+        if (inst[1].desttype == 1 && inst[1].value == 0)
+            inst[1].desttype = 0;
+#endif /* TOLERATE_SUPERGLUS_BUG */
+        store_operand(inst[1].desttype, inst[1].value, value);
+        break;
+      case op_copys:
+        value = inst[0].value;
+        store_operand_s(inst[1].desttype, inst[1].value, value);
+        break;
+      case op_copyb:
+        value = inst[0].value;
+        store_operand_b(inst[1].desttype, inst[1].value, value);
+        break;
+
+      case op_sexs:
+        val0 = inst[0].value;
+        if (val0 & 0x8000)
+          val0 |= 0xFFFF0000;
+        else
+          val0 &= 0x0000FFFF;
+        store_operand(inst[1].desttype, inst[1].value, val0);
+        break;
+      case op_sexb:
+        val0 = inst[0].value;
+        if (val0 & 0x80)
+          val0 |= 0xFFFFFF00;
+        else
+          val0 &= 0x000000FF;
+        store_operand(inst[1].desttype, inst[1].value, val0);
+        break;
+
+      case op_aload:
+        value = inst[0].value;
+        value += 4 * inst[1].value;
+        val0 = Mem4(value);
+        store_operand(inst[2].desttype, inst[2].value, val0);
+        break;
+      case op_aloads:
+        value = inst[0].value;
+        value += 2 * inst[1].value;
+        val0 = Mem2(value);
+        store_operand(inst[2].desttype, inst[2].value, val0);
+        break;
+      case op_aloadb:
+        value = inst[0].value;
+        value += inst[1].value;
+        val0 = Mem1(value);
+        store_operand(inst[2].desttype, inst[2].value, val0);
+        break;
+      case op_aloadbit:
+        value = inst[0].value;
+        vals0 = inst[1].value;
+        val1 = (vals0 & 7);
+        if (vals0 >= 0)
+          value += (vals0 >> 3);
+        else
+          value -= (1 + ((-1 - vals0) >> 3));
+        if (Mem1(value) & (1 << val1))
+          val0 = 1;
+        else
+          val0 = 0;
+        store_operand(inst[2].desttype, inst[2].value, val0);
+        break;
+
+      case op_astore:
+        value = inst[0].value;
+        value += 4 * inst[1].value;
+        val0 = inst[2].value;
+        MemW4(value, val0);
+        break;
+      case op_astores:
+        value = inst[0].value;
+        value += 2 * inst[1].value;
+        val0 = inst[2].value;
+        MemW2(value, val0);
+        break;
+      case op_astoreb:
+        value = inst[0].value;
+        value += inst[1].value;
+        val0 = inst[2].value;
+        MemW1(value, val0);
+        break;
+      case op_astorebit:
+        value = inst[0].value;
+        vals0 = inst[1].value;
+        val1 = (vals0 & 7);
+        if (vals0 >= 0)
+          value += (vals0 >> 3);
+        else
+          value -= (1 + ((-1 - vals0) >> 3));
+        val0 = Mem1(value);
+        if (inst[2].value)
+          val0 |= (1 << val1);
+        else
+          val0 &= ~((uint)(1 << val1));
+        MemW1(value, val0);
+        break;
+
+      case op_stkcount:
+        value = (stackptr - valstackbase) / 4;
+        store_operand(inst[0].desttype, inst[0].value, value);
+        break;
+      case op_stkpeek:
+        vals0 = inst[0].value * 4;
+        if (vals0 < 0 || vals0 >= (int)(stackptr - valstackbase))
+          fatal_error("Stkpeek outside current stack range.");
+        value = Stk4(stackptr - (vals0+4));
+        store_operand(inst[1].desttype, inst[1].value, value);
+        break;
+      case op_stkswap:
+        if (stackptr < valstackbase+8) {
+          fatal_error("Stack underflow in stkswap.");
+        }
+        val0 = Stk4(stackptr-4);
+        val1 = Stk4(stackptr-8);
+        StkW4(stackptr-4, val1);
+        StkW4(stackptr-8, val0);
+        break;
+      case op_stkcopy:
+        vals0 = inst[0].value;
+        if (vals0 < 0)
+          fatal_error("Negative operand in stkcopy.");
+        if (vals0 == 0)
+          break;
+        if (stackptr < valstackbase+vals0*4)
+          fatal_error("Stack underflow in stkcopy.");
+        if (stackptr + vals0*4 > stacksize) 
+          fatal_error("Stack overflow in stkcopy.");
+        addr = stackptr - vals0*4;
+        for (ix=0; ix<vals0; ix++) {
+          value = Stk4(addr + ix*4);
+          StkW4(stackptr + ix*4, value);
+        }
+        stackptr += vals0*4;
+        break;
+      case op_stkroll:
+        vals0 = inst[0].value;
+        vals1 = inst[1].value;
+        if (vals0 < 0)
+          fatal_error("Negative operand in stkroll.");
+        if (stackptr < valstackbase+vals0*4)
+          fatal_error("Stack underflow in stkroll.");
+        if (vals0 == 0)
+          break;
+        /* The following is a bit ugly. We want to do vals1 = vals0-vals1,
+           because rolling down is sort of easier than rolling up. But
+           we also want to take the result mod vals0. The % operator is
+           annoying for negative numbers, so we need to do this in two 
+           cases. */
+        if (vals1 > 0) {
+          vals1 = vals1 % vals0;
+          vals1 = (vals0) - vals1;
+        }
+        else {
+          vals1 = (-vals1) % vals0;
+        }
+        if (vals1 == 0)
+          break;
+        addr = stackptr - vals0*4;
+        for (ix=0; ix<vals1; ix++) {
+          value = Stk4(addr + ix*4);
+          StkW4(stackptr + ix*4, value);
+        }
+        for (ix=0; ix<vals0; ix++) {
+          value = Stk4(addr + (vals1+ix)*4);
+          StkW4(addr + ix*4, value);
+        }
+        break;
+
+      case op_streamchar:
+        profile_in(0xE0000001, stackptr, false);
+        value = inst[0].value & 0xFF;
+        (*stream_char_handler)(value);
+        profile_out(stackptr);
+        break;
+      case op_streamunichar:
+        profile_in(0xE0000002, stackptr, false);
+        value = inst[0].value;
+        (*stream_unichar_handler)(value);
+        profile_out(stackptr);
+        break;
+      case op_streamnum:
+        profile_in(0xE0000003, stackptr, false);
+        vals0 = inst[0].value;
+        stream_num(vals0, false, 0);
+        profile_out(stackptr);
+        break;
+      case op_streamstr:
+        profile_in(0xE0000004, stackptr, false);
+        stream_string(inst[0].value, 0, 0);
+        profile_out(stackptr);
+        break;
+
+      default:
+        fatal_error_i("Executed unknown opcode.", opcode);
+      }
+    }
+    else {
+
+      switch (opcode) {
+
+      case op_gestalt:
+        value = do_gestalt(inst[0].value, inst[1].value);
+        store_operand(inst[2].desttype, inst[2].value, value);
+        break;
+
+      case op_debugtrap:
+#if VM_DEBUGGER
+        /* We block and handle debug commands, but only if the
+           library has invoked debug features. (Meaning, has
+           the cycle handler ever been called.) */
+        if (debugger_ever_invoked()) {
+          debugger_block_and_debug("user debugtrap, pausing...");
+          break;
+        }
+#endif /* VM_DEBUGGER */
+        fatal_error_i("user debugtrap encountered.", inst[0].value);
+
+      case op_jumpabs:
+        pc = inst[0].value;
+        break;
+
+      case op_callf:
+        push_callstub(inst[1].desttype, inst[1].value);
+        enter_function(inst[0].value, 0, arglistfix);
+        break;
+      case op_callfi:
+        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[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[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[0].desttype, inst[0].value, endmem);
+        break;
+      case op_setmemsize:
+        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[0].desttype, inst[0].value, value);
+        break;
+      case op_setstringtbl:
+        stream_set_table(inst[0].value);
+        break;
+
+      case op_getiosys:
+        stream_get_iosys(&val0, &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[0].value, inst[1].value);
+        break;
+
+      case op_glk:
+        profile_in(0xF0000000+inst[0].value, stackptr, false);
+        value = inst[1].value;
+        arglist = pop_arguments(value, 0);
+        val0 = perform_glk(inst[0].value, value, arglist);
+#ifdef TOLERATE_SUPERGLUS_BUG
+        if (inst[2].desttype == 1 && inst[2].value == 0)
+            inst[2].desttype = 0;
+#endif /* TOLERATE_SUPERGLUS_BUG */
+        store_operand(inst[2].desttype, inst[2].value, val0);
+        profile_out(stackptr);
+        break;
+
+      case op_random:
+        vals0 = inst[0].value;
+        if (vals0 == 0)
+          value = glulx_random();
+        else if (vals0 >= 1)
+          value = glulx_random() % (uint)(vals0);
+        else 
+          value = -(int)(glulx_random() % (uint)(-vals0));
+        store_operand(inst[1].desttype, inst[1].value, value);
+        break;
+      case op_setrandom:
+        glulx_setrandom(inst[0].value);
+        break;
+
+      case op_verify:
+        value = perform_verify();
+        store_operand(inst[0].desttype, inst[0].value, value);
+        break;
+
+      case op_restart:
+        profile_fail("restart");
+        vm_restart();
+        break;
+
+      case op_protect:
+        val0 = inst[0].value;
+        val1 = val0 + inst[1].value;
+        if (val0 == val1) {
+          val0 = 0;
+          val1 = 0;
+        }
+        protectstart = val0;
+        protectend = val1;
+        break;
+
+      case op_save:
+        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:
+        value = perform_restore(find_stream_by_id(inst[0].value), false);
+        if (value == 0) {
+          /* We've succeeded, and the stack now contains the callstub
+             saved during saveundo. Ignore this opcode's operand. */
+          value = (uint)-1;
+          pop_callstub(value);
+        }
+        else {
+          /* We've failed, so we must store the failure in this opcode's
+             operand. */
+          store_operand(inst[1].desttype, inst[1].value, value);
+        }
+        break;
+
+      case op_saveundo:
+        push_callstub(inst[0].desttype, inst[0].value);
+        value = perform_saveundo();
+        pop_callstub(value);
+        break;
+
+      case op_restoreundo:
+        value = perform_restoreundo();
+        if (value == 0) {
+          /* We've succeeded, and the stack now contains the callstub
+             saved during saveundo. Ignore this opcode's operand. */
+          value = (uint)-1;
+          pop_callstub(value);
+        }
+        else {
+          /* We've failed, so we must store the failure in this opcode's
+             operand. */
+          store_operand(inst[0].desttype, inst[0].value, value);
+        }
+        break;
+
+      case op_quit:
+        done_executing = true;
+        break;
+
+      case op_linearsearch:
+        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[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[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: {
+        uint lx;
+        uint count = inst[0].value;
+        addr = inst[1].value;
+        for (lx=0; lx<count; lx++, addr++) {
+          MemW1(addr, 0);
+        }
+        }
+        break;
+      case op_mcopy: {
+        uint lx;
+        uint count = inst[0].value;
+        uint addrsrc = inst[1].value;
+        uint addrdest = inst[2].value;
+        if (addrdest < addrsrc) {
+          for (lx=0; lx<count; lx++, addrsrc++, addrdest++) {
+            value = Mem1(addrsrc);
+            MemW1(addrdest, value);
+          }
+        }
+        else {
+          addrsrc += (count-1);
+          addrdest += (count-1);
+          for (lx=0; lx<count; lx++, addrsrc--, addrdest--) {
+            value = Mem1(addrsrc);
+            MemW1(addrdest, value);
+          }
+        }
+        }
+        break;
+      case op_malloc:
+        value = heap_alloc(inst[0].value);
+        store_operand(inst[1].desttype, inst[1].value, value);
+        break;
+      case op_mfree:
+        heap_free(inst[0].value);
+        break;
+
+      case op_accelfunc:
+        accel_set_func(inst[0].value, inst[1].value);
+        break;
+      case op_accelparam:
+        accel_set_param(inst[0].value, inst[1].value);
+        break;
+
+#ifdef FLOAT_SUPPORT
+
+      case op_numtof:
+        vals0 = inst[0].value;
+        value = encode_float((gfloat32)vals0);
+        store_operand(inst[1].desttype, inst[1].value, value);
+        break;
+      case op_ftonumz:
+        valf = decode_float(inst[0].value);
+        if (!signbit(valf)) {
+          if (isnan(valf) || isinf(valf) || (valf > 2147483647.0))
+            vals0 = 0x7FFFFFFF;
+          else
+            vals0 = (int)(truncf(valf));
+        }
+        else {
+          if (isnan(valf) || isinf(valf) || (valf < -2147483647.0))
+            vals0 = 0x80000000;
+          else
+            vals0 = (int)(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 = (int)(roundf(valf));
+        }
+        else {
+          if (isnan(valf) || isinf(valf) || (valf < -2147483647.0))
+            vals0 = 0x80000000;
+          else
+            vals0 = (int)(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 */
+
+#ifdef GLULX_EXTEND_OPCODES
+      GLULX_EXTEND_OPCODES
+#endif /* GLULX_EXTEND_OPCODES */
+
+      default:
+        fatal_error_i("Executed unknown opcode.", opcode);
+      }
+    }
+  }
+  /* done executing */
+#if VM_DEBUGGER
+  debugger_handle_quit();
+#endif /* VM_DEBUGGER */
+}
+
+} // End of namespace Glulxe
+} // End of namespace Glk
diff --git a/engines/glk/glulxe/glulxe.cpp b/engines/glk/glulxe/glulxe.cpp
index 6fdfb05..1f05b94 100644
--- a/engines/glk/glulxe/glulxe.cpp
+++ b/engines/glk/glulxe/glulxe.cpp
@@ -34,6 +34,7 @@ Glulxe::Glulxe(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst,
 		memmap(nullptr), stack(nullptr), ramstart(0), endgamefile(0), origendmem(0),  stacksize(0),
 		startfuncaddr(0), checksum(0), stackptr(0), frameptr(0), pc(0), prevpc(0), origstringtable(0),
 		stringtable(0), valstackbase(0), localsbase(0), endmem(0), protectstart(0), protectend(0),
+		stream_char_handler(nullptr), stream_unichar_handler(nullptr),
 		// Accel
 		classes_table(0), indiv_prop_start(0), class_metaclass(0), object_metaclass(0),
 		routine_metaclass(0), string_metaclass(0), self(0), num_attr_bytes(0), cpv__start(0),
diff --git a/engines/glk/glulxe/glulxe.h b/engines/glk/glulxe/glulxe.h
index 839e9eb..fb7f63b 100644
--- a/engines/glk/glulxe/glulxe.h
+++ b/engines/glk/glulxe/glulxe.h
@@ -60,6 +60,9 @@ public:
 	uint protectstart, protectend;
 	uint prevpc;
 
+	void (*Glulxe::stream_char_handler)(unsigned char ch);
+	void (*Glulxe::stream_unichar_handler)(uint ch);
+
 	/**
 	 * \defgroup accel fields
 	 * @{
@@ -288,7 +291,11 @@ public:
 	 * \defgroup Exec access methods
 	 * @{
 	 */
-	void execute_loop(void);
+
+	 /**
+	  * The main interpreter loop. This repeats until the program is done
+	  */
+	void execute_loop();
 
 	/**@}*/
 
diff --git a/engines/glk/glulxe/glulxe_types.h b/engines/glk/glulxe/glulxe_types.h
index b06e435..b4c9a14 100644
--- a/engines/glk/glulxe/glulxe_types.h
+++ b/engines/glk/glulxe/glulxe_types.h
@@ -26,53 +26,53 @@
 #include "common/scummsys.h"
 
 namespace Glk {
-namespace Glulxe {
+	namespace Glulxe {
 
-class Glulxe;
+		class Glulxe;
 
-/**
- * Comment this definition to turn off memory-address checking. With verification on,
- * all reads and writes to main memory will be checked to ensure they're in range.
- * This is slower, but prevents malformed game files from crashing the interpreter.
- */
+		/**
+		 * Comment this definition to turn off memory-address checking. With verification on,
+		 * all reads and writes to main memory will be checked to ensure they're in range.
+		 * This is slower, but prevents malformed game files from crashing the interpreter.
+		 */
 #define VERIFY_MEMORY_ACCESS (1)
 
-/**
- * Uncomment this definition to permit an exception for memory-address checking for @glk and @copy
- * opcodes that try to write to memory address 0. This was a bug in old Superglus-built game files.
- */
-/* #define TOLERATE_SUPERGLUS_BUG (1) */
+		 /**
+		  * Uncomment this definition to permit an exception for memory-address checking for @glk and @copy
+		  * opcodes that try to write to memory address 0. This was a bug in old Superglus-built game files.
+		  */
+		  /* #define TOLERATE_SUPERGLUS_BUG (1) */
 
-/**
- * Uncomment this definition to turn on Glulx VM profiling. In this mode, all function calls are timed,
- * and the timing information is written to a data file called "profile-raw".
- * (Build note: on Linux, glibc may require you to also define _BSD_SOURCE or _DEFAULT_SOURCE or both
- * for the timeradd() macro.)
- */
-/* #define VM_PROFILING (1) */
+		  /**
+		   * Uncomment this definition to turn on Glulx VM profiling. In this mode, all function calls are timed,
+		   * and the timing information is written to a data file called "profile-raw".
+		   * (Build note: on Linux, glibc may require you to also define _BSD_SOURCE or _DEFAULT_SOURCE or both
+		   * for the timeradd() macro.)
+		   */
+		   /* #define VM_PROFILING (1) */
 
-/**
- * Uncomment this definition to turn on the Glulx debugger. You should only do this when debugging
- * facilities are desired; it slows down the interpreter. If you do, you will need to build with libxml2;
- * see the Makefile.
- */
-/* #define VM_DEBUGGER (1) */
+		   /**
+			* Uncomment this definition to turn on the Glulx debugger. You should only do this when debugging
+			* facilities are desired; it slows down the interpreter. If you do, you will need to build with libxml2;
+			* see the Makefile.
+			*/
+			/* #define VM_DEBUGGER (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. */
+			/**
+			 * 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)
 
-/**
- * Comment this definition to not cache the original state of RAM in (real) memory. This saves some memory,
- * but slows down save/restore/undo operations, which will have to read the original state off disk
- * every time.
- */
+			 /**
+			  * Comment this definition to not cache the original state of RAM in (real) memory. This saves some memory,
+			  * but slows down save/restore/undo operations, which will have to read the original state off disk
+			  * every time.
+			  */
 #define SERIALIZE_CACHE_RAM (1)
 
-/**
- * Some macros to read and write integers to memory, always in big-endian format.
- */
+			  /**
+			   * Some macros to read and write integers to memory, always in big-endian format.
+			   */
 #define Read4(ptr) READ_BE_UINT32(ptr)
 #define Read2(ptr) READ_BE_UINT16(ptr)
 #define Read1(ptr) ((byte)(((byte *)(ptr))[0]))
@@ -95,12 +95,12 @@ class Glulxe;
 #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 addresses which are multiples of four, etc.)
- * If the alignment rules are not followed, the program will see performance
- * degradation or even crashes, depending on the machine CPU.
- */
+			   /**
+				* Macros to access values on the stack. These *must* be used with proper alignment!
+				* (That is, Stk4 and StkW4 must take addresses which are multiples of four, etc.)
+				* If the alignment rules are not followed, the program will see performance
+				* degradation or even crashes, depending on the machine CPU.
+				*/
 #define Stk1(adr)   \
   (*((unsigned char *)(stack+(adr))))
 #define Stk2(adr)   \
@@ -115,6 +115,140 @@ class Glulxe;
 #define StkW4(adr, vl)   \
   (*((uint32 *)(stack+(adr))) = (uint32)(vl))
 
+enum Opcode {
+	op_nop          = 0x00,
+
+	op_add          = 0x10,
+	op_sub          = 0x11,
+	op_mul          = 0x12,
+	op_div          = 0x13,
+	op_mod          = 0x14,
+	op_neg          = 0x15,
+	op_bitand       = 0x18,
+	op_bitor        = 0x19,
+	op_bitxor       = 0x1A,
+	op_bitnot       = 0x1B,
+	op_shiftl       = 0x1C,
+	op_sshiftr      = 0x1D,
+	op_ushiftr      = 0x1E,
+
+	op_jump         = 0x20,
+	op_jz           = 0x22,
+	op_jnz          = 0x23,
+	op_jeq          = 0x24,
+	op_jne          = 0x25,
+	op_jlt          = 0x26,
+	op_jge          = 0x27,
+	op_jgt          = 0x28,
+	op_jle          = 0x29,
+	op_jltu         = 0x2A,
+	op_jgeu         = 0x2B,
+	op_jgtu         = 0x2C,
+	op_jleu         = 0x2D,
+
+	op_call         = 0x30,
+	op_return       = 0x31,
+	op_catch        = 0x32,
+	op_throw        = 0x33,
+	op_tailcall     = 0x34,
+
+	op_copy         = 0x40,
+	op_copys        = 0x41,
+	op_copyb        = 0x42,
+	op_sexs         = 0x44,
+	op_sexb         = 0x45,
+	op_aload        = 0x48,
+	op_aloads       = 0x49,
+	op_aloadb       = 0x4A,
+	op_aloadbit     = 0x4B,
+	op_astore       = 0x4C,
+	op_astores      = 0x4D,
+	op_astoreb      = 0x4E,
+	op_astorebit    = 0x4F,
+
+	op_stkcount     = 0x50,
+	op_stkpeek      = 0x51,
+	op_stkswap      = 0x52,
+	op_stkroll      = 0x53,
+	op_stkcopy      = 0x54,
+
+	op_streamchar   = 0x70,
+	op_streamnum    = 0x71,
+	op_streamstr    = 0x72,
+	op_streamunichar = 0x73,
+
+	op_gestalt      = 0x100,
+	op_debugtrap    = 0x101,
+	op_getmemsize   = 0x102,
+	op_setmemsize   = 0x103,
+	op_jumpabs      = 0x104,
+
+	op_random       = 0x110,
+	op_setrandom    = 0x111,
+
+	op_quit         = 0x120,
+	op_verify       = 0x121,
+	op_restart      = 0x122,
+	op_save         = 0x123,
+	op_restore      = 0x124,
+	op_saveundo     = 0x125,
+	op_restoreundo  = 0x126,
+	op_protect      = 0x127,
+
+	op_glk          = 0x130,
+
+	op_getstringtbl = 0x140,
+	op_setstringtbl = 0x141,
+	op_getiosys     = 0x148,
+	op_setiosys     = 0x149,
+
+	op_linearsearch = 0x150,
+	op_binarysearch = 0x151,
+	op_linkedsearch = 0x152,
+
+	op_callf        = 0x160,
+	op_callfi       = 0x161,
+	op_callfii      = 0x162,
+	op_callfiii     = 0x163,
+
+	op_mzero        = 0x170,
+	op_mcopy        = 0x171,
+	op_malloc       = 0x178,
+	op_mfree        = 0x179,
+
+	op_accelfunc    = 0x180,
+	op_accelparam   = 0x181,
+
+	op_numtof       = 0x190,
+	op_ftonumz      = 0x191,
+	op_ftonumn      = 0x192,
+	op_ceil         = 0x198,
+	op_floor        = 0x199,
+	op_fadd         = 0x1A0,
+	op_fsub         = 0x1A1,
+	op_fmul         = 0x1A2,
+	op_fdiv         = 0x1A3,
+	op_fmod         = 0x1A4,
+	op_sqrt         = 0x1A8,
+	op_exp          = 0x1A9,
+	op_log          = 0x1AA,
+	op_pow          = 0x1AB,
+	op_sin          = 0x1B0,
+	op_cos          = 0x1B1,
+	op_tan          = 0x1B2,
+	op_asin         = 0x1B3,
+	op_acos         = 0x1B4,
+	op_atan         = 0x1B5,
+	op_atan2        = 0x1B6,
+	op_jfeq         = 0x1C0,
+	op_jfne         = 0x1C1,
+	op_jflt         = 0x1C2,
+	op_jfle         = 0x1C3,
+	op_jfgt         = 0x1C4,
+	op_jfge         = 0x1C5,
+	op_jisnan       = 0x1C8,
+	op_jisinf       = 0x1C9
+};
 
 struct dispatch_splot_struct {
 	int numwanted;
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 6fe51c9..8a59358 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -59,6 +59,7 @@ MODULE_OBJS := \
 	frotz/windows.o \
 	glulxe/accel.o \
 	glulxe/detection.o \
+	glulxe/exec.o \
 	glulxe/float.o \
 	glulxe/glkop.o \
 	glulxe/glulxe.o \


Commit: 4f1387317f2dc2dbff2b25708aef22e11d80d617
    https://github.com/scummvm/scummvm/commit/4f1387317f2dc2dbff2b25708aef22e11d80d617
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-04-17T20:46:06-07:00

Commit Message:
GLK: GLULXE: Added funcs methods

Changed paths:
  A engines/glk/glulxe/funcs.cpp
    engines/glk/glulxe/glulxe.h
    engines/glk/module.mk


diff --git a/engines/glk/glulxe/funcs.cpp b/engines/glk/glulxe/funcs.cpp
new file mode 100644
index 0000000..cb427e2
--- /dev/null
+++ b/engines/glk/glulxe/funcs.cpp
@@ -0,0 +1,305 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/glk/glulxe/glulxe.h"
+
+namespace Glk {
+namespace Glulxe {
+
+void Glulxe::enter_function(uint funcaddr, uint argc, uint *argv) {
+  uint ix, jx;
+  acceleration_func accelFunc;
+  int locallen;
+  int functype;
+  uint modeaddr, opaddr, val;
+  int loctype, locnum;
+  uint addr = funcaddr;
+
+  accelFunc = accel_get_func(addr);
+  if (accelFunc) {
+    profile_in(addr, stackptr, TRUE);
+    val = (this->*accelFunc)(argc, argv);
+    profile_out(stackptr);
+    pop_callstub(val);
+    return;
+  }
+    
+  profile_in(addr, stackptr, FALSE);
+
+  /* Check the Glulx type identifier byte. */
+  functype = Mem1(addr);
+  if (functype != 0xC0 && functype != 0xC1) {
+    if (functype >= 0xC0 && functype <= 0xDF)
+      fatal_error_i("Call to unknown type of function.", addr);
+    else
+      fatal_error_i("Call to non-function.", addr);
+  }
+  addr++;
+
+  /* Bump the frameptr to the top. */
+  frameptr = stackptr;
+
+  /* Go through the function's locals-format list, copying it to the
+     call frame. At the same time, we work out how much space the locals
+     will actually take up. (Including padding.) */
+  ix = 0;
+  locallen = 0;
+  while (1) {
+    /* Grab two bytes from the locals-format list. These are 
+       unsigned (0..255 range). */
+    loctype = Mem1(addr);
+    addr++;
+    locnum = Mem1(addr);
+    addr++;
+
+    /* Copy them into the call frame. */
+    StkW1(frameptr+8+2*ix, loctype);
+    StkW1(frameptr+8+2*ix+1, locnum);
+    ix++;
+
+    /* If the type is zero, we're done, except possibly for two more
+       zero bytes in the call frame (to ensure 4-byte alignment.) */
+    if (loctype == 0) {
+      /* Make sure ix is even. */
+      if (ix & 1) {
+        StkW1(frameptr+8+2*ix, 0);
+        StkW1(frameptr+8+2*ix+1, 0);
+        ix++;
+      }
+      break;
+    }
+
+    /* Pad to 4-byte or 2-byte alignment if these locals are 4 or 2
+       bytes long. */
+    if (loctype == 4) {
+      while (locallen & 3)
+        locallen++;
+    }
+    else if (loctype == 2) {
+      while (locallen & 1)
+        locallen++;
+    }
+    else if (loctype == 1) {
+      /* no padding */
+    }
+    else {
+      fatal_error("Illegal local type in locals-format list.");
+    }
+
+    /* Add the length of the locals themselves. */
+    locallen += (loctype * locnum);
+  }
+
+  /* Pad the locals to 4-byte alignment. */
+  while (locallen & 3)
+    locallen++;
+
+  /* We now know how long the locals-frame and locals segments are. */
+  localsbase = frameptr+8+2*ix;
+  valstackbase = localsbase+locallen;
+
+  /* Test for stack overflow. */
+  /* This really isn't good enough; if the format list overflowed the
+     stack, we've already written outside the stack array. */
+  if (valstackbase >= stacksize)
+    fatal_error("Stack overflow in function call.");
+
+  /* Fill in the beginning of the stack frame. */
+  StkW4(frameptr+4, 8+2*ix);
+  StkW4(frameptr, 8+2*ix+locallen);
+
+  /* Set the stackptr and PC. */
+  stackptr = valstackbase;
+  pc = addr;
+
+  /* Zero out all the locals. */
+  for (jx=0; jx < (uint)locallen; jx++) 
+    StkW1(localsbase+jx, 0);
+
+  if (functype == 0xC0) {
+    /* Push the function arguments on the stack. The locals have already
+       been zeroed. */
+    if (stackptr+4*(argc+1) >= stacksize)
+      fatal_error("Stack overflow in function arguments."); 
+    for (ix=0; ix<argc; ix++) {
+      val = argv[(argc-1)-ix];
+      StkW4(stackptr, val);
+      stackptr += 4;
+    }
+    StkW4(stackptr, argc);
+    stackptr += 4;
+  }
+  else {
+    /* Copy in function arguments. This is a bit gross, since we have to
+       follow the locals format. If there are fewer arguments than locals,
+       that's fine -- we've already zeroed out this space. If there are
+       more arguments than locals, the extras are silently dropped. */
+    modeaddr = frameptr+8;
+    opaddr = localsbase;
+    ix = 0;
+    while (ix < argc) {
+      loctype = Stk1(modeaddr);
+      modeaddr++;
+      locnum = Stk1(modeaddr);
+      modeaddr++;
+      if (loctype == 0)
+        break;
+      if (loctype == 4) {
+        while (opaddr & 3)
+          opaddr++;
+        while (ix < argc && locnum) {
+          val = argv[ix];
+          StkW4(opaddr, val);
+          opaddr += 4;
+          ix++;
+          locnum--;
+        }
+      }
+      else if (loctype == 2) {
+        while (opaddr & 1)
+          opaddr++;
+        while (ix < argc && locnum) {
+          val = argv[ix] & 0xFFFF;
+          StkW2(opaddr, val);
+          opaddr += 2;
+          ix++;
+          locnum--;
+        }
+      }
+      else if (loctype == 1) {
+        while (ix < argc && locnum) {
+          val = argv[ix] & 0xFF;
+          StkW1(opaddr, val);
+          opaddr += 1;
+          ix++;
+          locnum--;
+        }
+      }
+    }
+  }
+
+  /* If the debugger is compiled in, check for a breakpoint on this
+     function. (Checking the function address, not the starting PC.) */
+  debugger_check_func_breakpoint(funcaddr);
+}
+
+void Glulxe::leave_function() {
+  profile_out(stackptr);
+  stackptr = frameptr;
+}
+
+void Glulxe::push_callstub(uint desttype, uint destaddr) {
+  if (stackptr+16 > stacksize)
+    fatal_error("Stack overflow in callstub.");
+  StkW4(stackptr+0, desttype);
+  StkW4(stackptr+4, destaddr);
+  StkW4(stackptr+8, pc);
+  StkW4(stackptr+12, frameptr);
+  stackptr += 16;
+}
+
+void Glulxe::pop_callstub(uint returnvalue) {
+  uint desttype, destaddr;
+  uint newpc, newframeptr;
+
+  if (stackptr < 16)
+    fatal_error("Stack underflow in callstub.");
+  stackptr -= 16;
+
+  newframeptr = Stk4(stackptr+12);
+  newpc = Stk4(stackptr+8);
+  destaddr = Stk4(stackptr+4);
+  desttype = Stk4(stackptr+0);
+
+  pc = newpc;
+  frameptr = newframeptr;
+
+  /* Recompute valstackbase and localsbase */
+  valstackbase = frameptr + Stk4(frameptr);
+  localsbase = frameptr + Stk4(frameptr+4);
+
+  switch (desttype) {
+
+  case 0x11:
+    fatal_error("String-terminator call stub at end of function call.");
+    break;
+
+  case 0x10:
+    /* This call stub was pushed during a string-decoding operation!
+       We have to restart it. (Note that the return value is discarded.) */
+    stream_string(pc, 0xE1, destaddr); 
+    break;
+
+  case 0x12:
+    /* This call stub was pushed during a number-printing operation.
+       Restart that. (Return value discarded.) */
+    stream_num(pc, true, destaddr);
+    break;
+
+  case 0x13:
+    /* This call stub was pushed during a C-string printing operation.
+       We have to restart it. (Note that the return value is discarded.) */
+    stream_string(pc, 0xE0, destaddr); 
+    break;
+
+  case 0x14:
+    /* This call stub was pushed during a Unicode printing operation.
+       We have to restart it. (Note that the return value is discarded.) */
+    stream_string(pc, 0xE2, destaddr); 
+    break;
+
+  default:
+    /* We're back in the original frame, so we can store the returnvalue. 
+       (If we tried to do this before resetting frameptr, a result
+       destination on the stack would go astray.) */
+    store_operand(desttype, destaddr, returnvalue);
+    break;
+  }
+}
+
+uint Glulxe::pop_callstub_string(int *bitnum) {
+  uint desttype, destaddr, newpc;
+
+  if (stackptr < 16)
+    fatal_error("Stack underflow in callstub.");
+  stackptr -= 16;
+
+  newpc = Stk4(stackptr+8);
+  destaddr = Stk4(stackptr+4);
+  desttype = Stk4(stackptr+0);
+
+  pc = newpc;
+
+  if (desttype == 0x11) {
+    return 0;
+  }
+  if (desttype == 0x10) {
+    *bitnum = destaddr;
+    return pc;
+  }
+
+  fatal_error("Function-terminator call stub at end of string.");
+  return 0;
+}
+
+} // End of namespace Glulxe
+} // End of namespace Glk
diff --git a/engines/glk/glulxe/glulxe.h b/engines/glk/glulxe/glulxe.h
index fb7f63b..2eed876 100644
--- a/engines/glk/glulxe/glulxe.h
+++ b/engines/glk/glulxe/glulxe.h
@@ -317,10 +317,33 @@ public:
 	 * @{
 	 */
 
+	/**
+	 * This writes a new call frame onto the stack, at stackptr. It leaves frameptr pointing
+	 * to the frame (ie, the original stackptr value.) argc and argv are an array of arguments.
+	 * Note that if argc is zero, argv may be NULL.
+	 */
 	void enter_function(uint addr, uint argc, uint *argv);
-	void leave_function(void);
+	
+	/**
+	 * Pop the current call frame off the stack. This is very simple.
+	*/
+	void leave_function();
+
+	/**
+	 * Push the magic four values on the stack: result destination, PC, and frameptr.
+	 */
 	void push_callstub(uint desttype, uint destaddr);
+
+	/**
+	 * Remove the magic four values from the stack, and use them. The returnvalue, whatever it is,
+	 * is put at the result destination; the PC and frameptr registers are set.
+	*/
 	void pop_callstub(uint returnvalue);
+
+	/**
+	 * Remove the magic four values, but interpret them as a string restart state.
+	 * Returns zero if it's a termination stub, or returns the restart address. The bitnum is extra.
+	 */
 	uint pop_callstub_string(int *bitnum);
 
 	/**@}*/
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 8a59358..1f026b2 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -61,6 +61,7 @@ MODULE_OBJS := \
 	glulxe/detection.o \
 	glulxe/exec.o \
 	glulxe/float.o \
+	glulxe/funcs.o \
 	glulxe/glkop.o \
 	glulxe/glulxe.o \
 	magnetic/detection.o \


Commit: 0b628784bd9a65322fc6c04be41267310f497dea
    https://github.com/scummvm/scummvm/commit/0b628784bd9a65322fc6c04be41267310f497dea
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-04-17T20:46:06-07:00

Commit Message:
GLK: GLULXE: Added gestalt methods

Changed paths:
  A engines/glk/glulxe/gestalt.cpp
    engines/glk/glulxe/glulxe_types.h
    engines/glk/module.mk


diff --git a/engines/glk/glulxe/gestalt.cpp b/engines/glk/glulxe/gestalt.cpp
new file mode 100644
index 0000000..2839c52
--- /dev/null
+++ b/engines/glk/glulxe/gestalt.cpp
@@ -0,0 +1,101 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/glk/glulxe/glulxe.h"
+
+namespace Glk {
+namespace Glulxe {
+
+uint Glulxe::do_gestalt(uint val, uint val2) {
+  switch (val) {
+
+  case gestulx_GlulxVersion:
+    return 0x00030102; /* Glulx spec version 3.1.2 */
+
+  case gestulx_TerpVersion:
+    return 0x00000504; /* Glulxe version 0.5.4 */
+
+  case gestulx_ResizeMem:
+#ifdef FIXED_MEMSIZE
+    return 0; /* The setmemsize opcodes are compiled out. */
+#else /* FIXED_MEMSIZE */
+    return 1; /* We can handle setmemsize. */
+#endif /* FIXED_MEMSIZE */
+
+  case gestulx_Undo:
+    return 1; /* We can handle saveundo and restoreundo. */
+
+  case gestulx_IOSystem:
+    switch (val2) {
+    case 0:
+      return 1; /* The "null" system always works. */
+    case 1:
+      return 1; /* The "filter" system always works. */
+    case 2:
+      return 1; /* A Glk library is hooked up. */
+    default:
+      return 0;
+    }
+
+  case gestulx_Unicode:
+    return 1; /* We can handle Unicode. */
+
+  case gestulx_MemCopy:
+    return 1; /* We can do mcopy/mzero. */
+
+  case gestulx_MAlloc:
+#ifdef FIXED_MEMSIZE
+    return 0; /* The malloc opcodes are compiled out. */
+#else /* FIXED_MEMSIZE */
+    return 1; /* We can handle malloc/mfree. */
+#endif /* FIXED_MEMSIZE */
+
+  case gestulx_MAllocHeap:
+    return heap_get_start();
+
+  case gestulx_Acceleration:
+    return 1; /* We can do accelfunc/accelparam. */
+
+  case gestulx_AccelFunc:
+    if (accel_find_func(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 */
+
+#ifdef GLULX_EXTEND_GESTALT
+  GLULX_EXTEND_GESTALT
+#endif /* GLULX_EXTEND_GESTALT */
+
+  default:
+    return 0;
+
+  }
+}
+
+} // End of namespace Glulxe
+} // End of namespace Glk
diff --git a/engines/glk/glulxe/glulxe_types.h b/engines/glk/glulxe/glulxe_types.h
index b4c9a14..c6ce238 100644
--- a/engines/glk/glulxe/glulxe_types.h
+++ b/engines/glk/glulxe/glulxe_types.h
@@ -250,6 +250,21 @@ enum Opcode {
 	op_jisinf       = 0x1C9
 };
 
+enum gestulx {
+	gestulx_GlulxVersion = 0,
+	gestulx_TerpVersion  = 1,
+	gestulx_ResizeMem    = 2,
+	gestulx_Undo         = 3,
+	gestulx_IOSystem     = 4,
+	gestulx_Unicode      = 5,
+	gestulx_MemCopy      = 6,
+	gestulx_MAlloc       = 7,
+	gestulx_MAllocHeap   = 8,
+	gestulx_Acceleration = 9,
+	gestulx_AccelFunc    = 10,
+	gestulx_Float        = 11
+};
+
 struct dispatch_splot_struct {
 	int numwanted;
 	int maxargs;
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 1f026b2..f1dcd84 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -62,6 +62,7 @@ MODULE_OBJS := \
 	glulxe/exec.o \
 	glulxe/float.o \
 	glulxe/funcs.o \
+	glulxe/gestalt.o \
 	glulxe/glkop.o \
 	glulxe/glulxe.o \
 	magnetic/detection.o \


Commit: 9ceb83972adc62aff28618c196f0a473fb85b1a0
    https://github.com/scummvm/scummvm/commit/9ceb83972adc62aff28618c196f0a473fb85b1a0
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-04-17T20:46:06-07:00

Commit Message:
GLK: GLULXE: Added heap methods

Changed paths:
  A engines/glk/glulxe/heap.cpp
    engines/glk/glulxe/glulxe.cpp
    engines/glk/glulxe/glulxe.h
    engines/glk/glulxe/glulxe_types.h
    engines/glk/module.mk


diff --git a/engines/glk/glulxe/glulxe.cpp b/engines/glk/glulxe/glulxe.cpp
index 1f05b94..d16ecfb 100644
--- a/engines/glk/glulxe/glulxe.cpp
+++ b/engines/glk/glulxe/glulxe.cpp
@@ -35,10 +35,12 @@ Glulxe::Glulxe(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst,
 		startfuncaddr(0), checksum(0), stackptr(0), frameptr(0), pc(0), prevpc(0), origstringtable(0),
 		stringtable(0), valstackbase(0), localsbase(0), endmem(0), protectstart(0), protectend(0),
 		stream_char_handler(nullptr), stream_unichar_handler(nullptr),
-		// Accel
+		// accel
 		classes_table(0), indiv_prop_start(0), class_metaclass(0), object_metaclass(0),
 		routine_metaclass(0), string_metaclass(0), self(0), num_attr_bytes(0), cpv__start(0),
-		accelentries(nullptr) {
+		accelentries(nullptr),
+		// heap
+		heap_start(0), alloc_count(0), heap_head(nullptr), heap_tail(nullptr) {
 	g_vm = this;
 }
 
diff --git a/engines/glk/glulxe/glulxe.h b/engines/glk/glulxe/glulxe.h
index 2eed876..eacc51f 100644
--- a/engines/glk/glulxe/glulxe.h
+++ b/engines/glk/glulxe/glulxe.h
@@ -80,6 +80,34 @@ public:
 	accelentry_t **accelentries;
 
 	/**@}*/
+
+	/**
+	 * \defgroup heap fields
+	 * @{
+	 */
+
+	uint heap_start = 0;	///< zero for inactive heap
+	int alloc_count = 0;
+
+	/* The heap_head/heap_tail is a doubly-linked list of blocks, both
+	   free and allocated. It is kept in address order. It should be
+	   complete -- that is, the first block starts at heap_start, and each
+	   block ends at the beginning of the next block, until the last one,
+	   which ends at endmem.
+
+	   (Heap_start is never the same as end_mem; if there is no heap space,
+	   then the heap is inactive and heap_start is zero.)
+
+	   Adjacent free blocks may be merged at heap_alloc() time.
+
+	   ### To make alloc more efficient, we could keep a separate
+	   free-list. To make free more efficient, we could keep a hash
+	   table of allocations.
+	 */
+	heapblock_t *heap_head = NULL;
+	heapblock_t *heap_tail = NULL;
+
+	/**@}*/
 protected:
 	/**
 	 * \defgroup glkop fields
@@ -370,14 +398,61 @@ public:
 	 * \defgroup Heap access methods
 	 * @{
 	 */
+
+	/**
+	 * Set the heap state to inactive, and free the block lists. This is called when the game
+	 * starts or restarts.
+	 */
 	void heap_clear(void);
-	int heap_is_active(void);
-	uint heap_get_start(void);
+
+	/**
+	 * Returns whether the heap is active.
+	 */
+	int heap_is_active() const;
+
+	/**
+	 * Returns the start address of the heap, or 0 if the heap is not active.
+	 */
+	uint heap_get_start() const;
+
+	/**
+	 * Allocate a block. If necessary, activate the heap and/or extend memory. This may not be
+	 * available at all; #define FIXED_MEMSIZE if you want the interpreter to unconditionally refuse.
+	 * Returns the memory address of the block, or 0 if the operation failed.
+	 */
 	uint heap_alloc(uint len);
+
+	/**
+	 * Free a heap block. If necessary, deactivate the heap.
+	 */
 	void heap_free(uint addr);
+
+	/**
+	 * Create an array of words, in the VM serialization format:
+	 *
+	 *	 heap_start
+	 *	 alloc_count
+	 *	 addr of first block
+	 *	 len of first block
+	 *	 ...
+	 *
+	 * (Note that these are uint values -- native byte ordering. Also, the blocks will be in address order,
+	 * which is a stricter guarantee than the VM specifies; that'll help in heap_apply_summary().)
+	 *
+	 * If the heap is inactive, store NULL. Return 0 for success; otherwise, the operation failed.
+	 *
+	 * The array returned in summary must be freed with glulx_free() after the caller uses it.
+	 */
 	int heap_get_summary(uint *valcount, uint **summary);
+
+	/**
+	 * Given an array of words in the above format, set up the heap to contain it. As noted above,
+	 * the caller must ensure that the blocks are in address order. When this is called, the heap
+	 * must be inactive.
+	 *
+	 * Return 0 for success. Otherwise the operation failed (and, most likely, caused a fatal error).
+	*/
 	int heap_apply_summary(uint valcount, uint *summary);
-	void heap_sanity_check(void);
 
 	/**@}*/
 
diff --git a/engines/glk/glulxe/glulxe_types.h b/engines/glk/glulxe/glulxe_types.h
index c6ce238..96943d2 100644
--- a/engines/glk/glulxe/glulxe_types.h
+++ b/engines/glk/glulxe/glulxe_types.h
@@ -348,6 +348,14 @@ typedef accelentry_struct accelentry_t;
 
 #define ACCEL_HASH_SIZE (511)
 
+struct heapblock_struct {
+	uint addr;
+	uint len;
+	int isfree;
+	struct heapblock_struct *next;
+	struct heapblock_struct *prev;
+};
+typedef heapblock_struct heapblock_t;
 
 } // End of namespace Glulxe
 } // End of namespace Glk
diff --git a/engines/glk/glulxe/heap.cpp b/engines/glk/glulxe/heap.cpp
new file mode 100644
index 0000000..29d9405
--- /dev/null
+++ b/engines/glk/glulxe/heap.cpp
@@ -0,0 +1,322 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/glk/glulxe/glulxe.h"
+
+namespace Glk {
+namespace Glulxe {
+
+void Glulxe::heap_clear() {
+  while (heap_head) {
+    heapblock_t *blo = heap_head;
+    heap_head = blo->next;
+    blo->next = nullptr;
+    blo->prev = nullptr;
+    glulx_free(blo);
+  }
+  heap_tail = nullptr;
+
+  if (heap_start) {
+    uint res = change_memsize(heap_start, true);
+    if (res)
+      fatal_error_i("Unable to revert memory size when deactivating heap.",
+        heap_start);
+  }
+
+  heap_start = 0;
+  alloc_count = 0;
+  /* heap_sanity_check(); */
+}
+
+int Glulxe::heap_is_active() const {
+  return (heap_start != 0);
+}
+
+uint Glulxe::heap_get_start() const {
+  return heap_start;
+}
+
+uint Glulxe::heap_alloc(uint len) {
+  heapblock_t *blo, *newblo;
+
+#ifdef FIXED_MEMSIZE
+  return 0;
+#else /* FIXED_MEMSIZE */
+
+  if (len <= 0)
+    fatal_error("Heap allocation length must be positive.");
+
+  blo = heap_head;
+  while (blo) {
+    if (blo->isfree && blo->len >= len)
+      break;
+
+    if (!blo->isfree) {
+      blo = blo->next;
+      continue;
+    }
+
+    if (!blo->next || !blo->next->isfree) {
+      blo = blo->next;
+      continue;
+    }
+
+    /* This is a free block, but the next block in the list is also
+       free, so we "advance" by merging rather than by going to
+       blo->next. */
+    newblo = blo->next;
+    blo->len += newblo->len;
+    if (newblo->next) {
+      blo->next = newblo->next;
+      newblo->next->prev = blo;
+    }
+    else {
+      blo->next = nullptr;
+      heap_tail = blo;
+    }
+    newblo->next = nullptr;
+    newblo->prev = nullptr;
+    glulx_free(newblo);
+    newblo = nullptr;
+    continue;
+  }
+
+  if (!blo) {
+    /* No free area is visible on the list. Try extending memory. How
+       much? Double the heap size, or by 256 bytes, or by the memory
+       length requested -- whichever is greatest. */
+    uint res;
+    uint extension;
+    uint oldendmem = endmem;
+
+    extension = 0;
+    if (heap_start)
+      extension = endmem - heap_start;
+    if (extension < len)
+      extension = len;
+    if (extension < 256)
+      extension = 256;
+    /* And it must be rounded up to a multiple of 256. */
+    extension = (extension + 0xFF) & (~(uint)0xFF);
+
+    res = change_memsize(endmem+extension, true);
+    if (res)
+      return 0;
+
+    /* If we just started the heap, note that. */
+    if (heap_start == 0)
+      heap_start = oldendmem;
+
+    if (heap_tail && heap_tail->isfree) {
+      /* Append the new space to the last block. */
+      blo = heap_tail;
+      blo->len += extension;
+    }
+    else {
+      /* Append the new space to the block list, as a new block. */
+      newblo = (heapblock_t *)glulx_malloc(sizeof(heapblock_t));
+      if (!newblo)
+        fatal_error("Unable to allocate record for heap block.");
+      newblo->addr = oldendmem;
+      newblo->len = extension;
+      newblo->isfree = true;
+      newblo->next = nullptr;
+      newblo->prev = nullptr;
+
+      if (!heap_tail) {
+        heap_head = newblo;
+        heap_tail = newblo;
+      }
+      else {
+        blo = heap_tail;
+        heap_tail = newblo;
+        blo->next = newblo;
+        newblo->prev = blo;
+      }
+
+      blo = newblo;
+      newblo = nullptr;
+    }
+
+    /* and continue forwards, using this new block (blo). */
+  }
+
+  /* Something strange happened. */
+  if (!blo || !blo->isfree || blo->len < len)
+    return 0;
+
+  /* We now have a free block of size len or longer. */
+
+  if (blo->len == len) {
+    blo->isfree = false;
+  }
+  else {
+    newblo = (heapblock_t *)glulx_malloc(sizeof(heapblock_t));
+    if (!newblo)
+      fatal_error("Unable to allocate record for heap block.");
+    newblo->isfree = true;
+    newblo->addr = blo->addr + len;
+    newblo->len = blo->len - len;
+    blo->len = len;
+    blo->isfree = false;
+    newblo->next = blo->next;
+    if (newblo->next)
+      newblo->next->prev = newblo;
+    newblo->prev = blo;
+    blo->next = newblo;
+    if (heap_tail == blo)
+      heap_tail = newblo;
+  }
+
+  alloc_count++;
+  /* heap_sanity_check(); */
+  return blo->addr;
+
+#endif /* FIXED_MEMSIZE */
+}
+
+void Glulxe::heap_free(uint addr) {
+  heapblock_t *blo;
+
+  for (blo = heap_head; blo; blo = blo->next) { 
+    if (blo->addr == addr)
+      break;
+  };
+  if (!blo || blo->isfree)
+    fatal_error_i("Attempt to free unallocated address from heap.", addr);
+
+  blo->isfree = true;
+  alloc_count--;
+  if (alloc_count <= 0) {
+    heap_clear();
+  }
+
+  /* heap_sanity_check(); */
+}
+
+int Glulxe::heap_get_summary(uint *valcount, uint **summary) {
+  uint *arr, len, pos;
+  heapblock_t *blo;
+
+  *valcount = 0;
+  *summary = nullptr;
+
+  if (heap_start == 0)
+    return 0;
+
+  len = 2 + 2*alloc_count;
+  arr = (uint *)glulx_malloc(len * sizeof(uint));
+  if (!arr)
+    return 1;
+
+  pos = 0;
+  arr[pos++] = heap_start;
+  arr[pos++] = alloc_count;
+
+  for (blo = heap_head; blo; blo = blo->next) {
+    if (blo->isfree)
+      continue;
+    arr[pos++] = blo->addr;
+    arr[pos++] = blo->len;
+  }
+
+  if (pos != len)
+    fatal_error("Wrong number of active blocks in heap");
+
+  *valcount = len;
+  *summary = arr;
+  return 0;
+}
+
+int Glulxe::heap_apply_summary(uint valcount, uint *summary) {
+  uint lx, jx, lastend;
+
+  if (heap_start)
+    fatal_error("Heap active when heap_apply_summary called");
+
+  if (valcount == 0 || summary == nullptr)
+    return 0;
+  if (valcount == 2 && summary[0] == 0 && summary[1] == 0)
+    return 0;
+
+#ifdef FIXED_MEMSIZE
+  return 1;
+#else /* FIXED_MEMSIZE */
+
+  lx = 0;
+  heap_start = summary[lx++];
+  alloc_count = summary[lx++];
+
+  for (jx=lx; jx+2<valcount; jx+=2) {
+    if (summary[jx] >= summary[jx+2])
+      fatal_error("Heap block summary is out of order.");
+  }
+
+  lastend = heap_start;
+
+  while (lx < valcount || lastend < endmem) {
+    heapblock_t *blo;
+
+    blo = (heapblock_t *)glulx_malloc(sizeof(heapblock_t));
+    if (!blo)
+      fatal_error("Unable to allocate record for heap block.");
+
+    if (lx >= valcount) {
+      blo->addr = lastend;
+      blo->len = endmem - lastend;
+      blo->isfree = true;
+    } else {
+      if (lastend < summary[lx]) {
+        blo->addr = lastend;
+        blo->len = summary[lx] - lastend;
+        blo->isfree = true;
+      } else {
+        blo->addr = summary[lx++];
+        blo->len = summary[lx++];
+        blo->isfree = false;
+      }
+    }
+
+    blo->prev = nullptr;
+    blo->next = nullptr;
+
+    if (!heap_head) {
+      heap_head = blo;
+      heap_tail = blo;
+    }
+    else {
+      heap_tail->next = blo;
+      blo->prev = heap_tail;
+      heap_tail = blo;
+    }
+
+    lastend = blo->addr + blo->len;
+  }
+
+  /* heap_sanity_check(); */
+
+  return 0;
+#endif /* FIXED_MEMSIZE */
+}
+
+} // End of namespace Glulxe
+} // End of namespace Glk
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index f1dcd84..669f811 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -65,6 +65,7 @@ MODULE_OBJS := \
 	glulxe/gestalt.o \
 	glulxe/glkop.o \
 	glulxe/glulxe.o \
+	glulxe/heap.o \
 	magnetic/detection.o \
 	magnetic/magnetic.o \
 	scott/detection.o \


Commit: 4ee1b98b86dfa7ccc3b8097a96a883e8d23abe48
    https://github.com/scummvm/scummvm/commit/4ee1b98b86dfa7ccc3b8097a96a883e8d23abe48
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-04-17T20:46:06-07:00

Commit Message:
GLK: GLULXE: Add operand methods

Changed paths:
  A engines/glk/glulxe/operand.cpp
    engines/glk/glulxe/exec.cpp
    engines/glk/glulxe/glulxe.h
    engines/glk/glulxe/glulxe_types.h
    engines/glk/module.mk


diff --git a/engines/glk/glulxe/exec.cpp b/engines/glk/glulxe/exec.cpp
index f3b511f..766e8b6 100644
--- a/engines/glk/glulxe/exec.cpp
+++ b/engines/glk/glulxe/exec.cpp
@@ -29,7 +29,7 @@ void Glulxe::execute_loop() {
   bool done_executing = false;
   int ix;
   uint opcode;
-  operandlist_t *oplist;
+  const operandlist_t *oplist;
   oparg_t inst[MAX_OPERANDS];
   uint value, addr, val0, val1;
   int vals0, vals1;
diff --git a/engines/glk/glulxe/glulxe.h b/engines/glk/glulxe/glulxe.h
index eacc51f..97a7c09 100644
--- a/engines/glk/glulxe/glulxe.h
+++ b/engines/glk/glulxe/glulxe.h
@@ -108,6 +108,17 @@ public:
 	heapblock_t *heap_tail = NULL;
 
 	/**@}*/
+
+	/**
+	 * \defgroup operand fields
+	 * @{
+	 */
+
+	/**
+	 * This is a handy array in which to look up operandlists quickly. It stores the operandlists
+	 * for the first 128 opcodes, which are the ones used most frequently.
+	 */
+	const operandlist_t *fast_operandlist[0x80];
 protected:
 	/**
 	 * \defgroup glkop fields
@@ -330,11 +341,34 @@ public:
 	/**
 	 * \defgroup Operand access methods
 	 * @{
-	 */	operandlist_t *fast_operandlist[0x80];
-	void init_operands(void);
-	operandlist_t *lookup_operandlist(uint opcode);
-	void parse_operands(oparg_t *opargs, operandlist_t *oplist);
+	 */
+
+	 /**
+	  * Set up the fast-lookup array of operandlists. This is called just once, when the terp starts up.
+	  */
+	void init_operands();
+
+	/**
+	 * Return the operandlist for a given opcode. For opcodes in the range 00..7F, it's faster
+	 * to use the array fast_operandlist[].
+	*/
+	const operandlist_t *lookup_operandlist(uint opcode);
+
+	/**
+	 * Read the list of operands of an instruction, and put the values 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(oparg_t *opargs, const operandlist_t *oplist);
+
+	/**
+	 * Store a result value, according to the desttype and destaddress given. This is usually used to store
+	 * the result of an opcode, but it's also used by any code that pulls a call-stub off the stack.
+	 */
 	void store_operand(uint desttype, uint destaddr, uint storeval);
+
 	void store_operand_s(uint desttype, uint destaddr, uint storeval);
 	void store_operand_b(uint desttype, uint destaddr, uint storeval);
 
diff --git a/engines/glk/glulxe/glulxe_types.h b/engines/glk/glulxe/glulxe_types.h
index 96943d2..8c99d90 100644
--- a/engines/glk/glulxe/glulxe_types.h
+++ b/engines/glk/glulxe/glulxe_types.h
@@ -313,9 +313,9 @@ typedef classtable_struct classtable_t;
  * Represents the operand structure of an opcode.
  */
 struct operandlist_struct {
-	int num_ops; /* Number of operands for this opcode */
-	int arg_size; /* Usually 4, but can be 1 or 2 */
-	int *formlist; /* Array of values, either modeform_Load or modeform_Store */
+	int num_ops;			///< Number of operands for this opcode
+	int arg_size;			///< Usually 4, but can be 1 or 2
+	const int *formlist;	///< Array of values, either modeform_Load or modeform_Store
 };
 typedef operandlist_struct operandlist_t;
 
diff --git a/engines/glk/glulxe/operand.cpp b/engines/glk/glulxe/operand.cpp
new file mode 100644
index 0000000..a48d68e
--- /dev/null
+++ b/engines/glk/glulxe/operand.cpp
@@ -0,0 +1,617 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/glk/glulxe/glulxe.h"
+
+namespace Glk {
+namespace Glulxe {
+
+/**
+ * The actual immutable structures which lookup_operandlist() returns.
+ */
+static const operandlist_t list_none = { 0, 4, NULL };
+
+static const int array_S[1] = { modeform_Store };
+static const operandlist_t list_S = { 1, 4, &array_S[0] };
+static const int array_LS[2] = { modeform_Load, modeform_Store };
+static const operandlist_t list_LS = { 2, 4, &array_LS[0] };
+static const int array_LLS[3] = { modeform_Load, modeform_Load, modeform_Store };
+static const operandlist_t list_LLS = { 3, 4, &array_LLS[0] };
+static const int array_LLLS[4] = { modeform_Load, modeform_Load, modeform_Load, modeform_Store };
+static const operandlist_t list_LLLS = { 4, 4, &array_LLLS[0] };
+static const int array_LLLLS[5] = { modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Store };
+static const operandlist_t list_LLLLS = { 5, 4, &array_LLLLS[0] };
+/* static const int array_LLLLLS[6] = { modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Store };
+static const operandlist_t list_LLLLLS = { 6, 4, &array_LLLLLS }; */ /* not currently used */
+static const int array_LLLLLLS[7] = { modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Store };
+static const operandlist_t list_LLLLLLS = { 7, 4, &array_LLLLLLS[0] };
+static const int array_LLLLLLLS[8] = { modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Store };
+static const operandlist_t list_LLLLLLLS = { 8, 4, &array_LLLLLLLS[0] };
+
+static const int array_L[1] = { modeform_Load };
+static const operandlist_t list_L = { 1, 4, &array_L[0] };
+static const int array_LL[2] = { modeform_Load, modeform_Load };
+static const operandlist_t list_LL = { 2, 4, &array_LL[0] };
+static const int array_LLL[3] = { modeform_Load, modeform_Load, modeform_Load };
+static const operandlist_t list_LLL = { 3, 4, &array_LLL[0] };
+static const operandlist_t list_2LS = { 2, 2, &array_LS[0] };
+static const operandlist_t list_1LS = { 2, 1, &array_LS[0] };
+static const int array_LLLL[4] = { modeform_Load, modeform_Load, modeform_Load, modeform_Load };
+static const operandlist_t list_LLLL = { 4, 4, &array_LLLL[0] };
+static const int array_SL[2] = { modeform_Store, modeform_Load };
+static const operandlist_t list_SL = { 2, 4, &array_SL[0] };
+static const int array_SS[2] = { modeform_Store, modeform_Store };
+static const operandlist_t list_SS = { 2, 4, &array_SS[0] };
+static const int array_LLSS[4] = { modeform_Load, modeform_Load, modeform_Store, modeform_Store };
+static const operandlist_t list_LLSS = { 4, 4, &array_LLSS[0] };
+
+void Glulxe::init_operands() {
+  for (int ix=0; ix<0x80; ix++)
+    fast_operandlist[ix] = lookup_operandlist(ix);
+}
+
+const operandlist_t *Glulxe::lookup_operandlist(uint opcode) {
+  switch (opcode) {
+  case op_nop: 
+    return &list_none;
+
+  case op_add:
+  case op_sub:
+  case op_mul:
+  case op_div:
+  case op_mod:
+  case op_bitand:
+  case op_bitor:
+  case op_bitxor:
+  case op_shiftl:
+  case op_sshiftr:
+  case op_ushiftr:
+    return &list_LLS;
+
+  case op_neg:
+  case op_bitnot:
+    return &list_LS;
+
+  case op_jump:
+  case op_jumpabs:
+    return &list_L;
+  case op_jz:
+  case op_jnz:
+    return &list_LL;
+  case op_jeq:
+  case op_jne:
+  case op_jlt:
+  case op_jge:
+  case op_jgt:
+  case op_jle:
+  case op_jltu:
+  case op_jgeu:
+  case op_jgtu:
+  case op_jleu:
+    return &list_LLL;
+
+  case op_call:
+    return &list_LLS;
+  case op_return:
+    return &list_L;
+  case op_catch:
+    return &list_SL;
+  case op_throw:
+    return &list_LL;
+  case op_tailcall:
+    return &list_LL;
+
+  case op_sexb:
+  case op_sexs:
+    return &list_LS;
+
+  case op_copy:
+    return &list_LS;
+  case op_copys:
+    return &list_2LS;
+  case op_copyb:
+    return &list_1LS;
+  case op_aload:
+  case op_aloads:
+  case op_aloadb:
+  case op_aloadbit:
+    return &list_LLS;
+  case op_astore:
+  case op_astores:
+  case op_astoreb:
+  case op_astorebit:
+    return &list_LLL;
+
+  case op_stkcount:
+    return &list_S;
+  case op_stkpeek:
+    return &list_LS;
+  case op_stkswap: 
+    return &list_none;
+  case op_stkroll:
+    return &list_LL;
+  case op_stkcopy:
+    return &list_L;
+
+  case op_streamchar:
+  case op_streamunichar:
+  case op_streamnum:
+  case op_streamstr:
+    return &list_L;
+  case op_getstringtbl:
+    return &list_S;
+  case op_setstringtbl:
+    return &list_L;
+  case op_getiosys:
+    return &list_SS;
+  case op_setiosys:
+    return &list_LL;
+
+  case op_random:
+    return &list_LS;
+  case op_setrandom:
+    return &list_L;
+
+  case op_verify:
+    return &list_S;
+  case op_restart:
+    return &list_none;
+  case op_save:
+  case op_restore:
+    return &list_LS;
+  case op_saveundo:
+  case op_restoreundo:
+    return &list_S;
+  case op_protect:
+    return &list_LL;
+
+  case op_quit:
+    return &list_none;
+
+  case op_gestalt:
+    return &list_LLS;
+
+  case op_debugtrap: 
+    return &list_L;
+
+  case op_getmemsize:
+    return &list_S;
+  case op_setmemsize:
+    return &list_LS;
+
+  case op_linearsearch:
+    return &list_LLLLLLLS;
+  case op_binarysearch:
+    return &list_LLLLLLLS;
+  case op_linkedsearch:
+    return &list_LLLLLLS;
+
+  case op_glk:
+    return &list_LLS;
+
+  case op_callf:
+    return &list_LS;
+  case op_callfi:
+    return &list_LLS;
+  case op_callfii:
+    return &list_LLLS;
+  case op_callfiii:
+    return &list_LLLLS;
+
+  case op_mzero:
+    return &list_LL;
+  case op_mcopy:
+    return &list_LLL;
+  case op_malloc:
+    return &list_LS;
+  case op_mfree:
+    return &list_L;
+
+  case op_accelfunc:
+  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 */
+
+#ifdef GLULX_EXTEND_OPERANDS
+  GLULX_EXTEND_OPERANDS
+#endif /* GLULX_EXTEND_OPERANDS */
+
+  default: 
+    return NULL;
+  }
+}
+
+void Glulxe::parse_operands(oparg_t *args, const operandlist_t *oplist) {
+  int ix;
+  oparg_t *curarg;
+  int numops = oplist->num_ops;
+  int argsize = oplist->arg_size;
+  uint modeaddr = pc;
+  int modeval = 0;
+
+  pc += (numops+1) / 2;
+
+  for (ix=0, curarg=args; ix<numops; ix++, curarg++) {
+    int mode;
+    uint value;
+    uint addr;
+
+    curarg->desttype = 0;
+
+    if ((ix & 1) == 0) {
+      modeval = Mem1(modeaddr);
+      mode = (modeval & 0x0F);
+    }
+    else {
+      mode = ((modeval >> 4) & 0x0F);
+      modeaddr++;
+    }
+
+    if (oplist->formlist[ix] == modeform_Load) {
+
+      switch (mode) {
+
+      case 8: /* pop off stack */
+        if (stackptr < valstackbase+4) {
+          fatal_error("Stack underflow in operand.");
+        }
+        stackptr -= 4;
+        value = Stk4(stackptr);
+        break;
+
+      case 0: /* constant zero */
+        value = 0;
+        break;
+
+      case 1: /* one-byte constant */
+        /* Sign-extend from 8 bits to 32 */
+        value = (int)(signed char)(Mem1(pc));
+        pc++;
+        break;
+
+      case 2: /* two-byte constant */
+        /* Sign-extend the first byte from 8 bits to 32; the subsequent
+           byte must not be sign-extended. */
+        value = (int)(signed char)(Mem1(pc));
+        pc++;
+        value = (value << 8) | (uint)(Mem1(pc));
+        pc++;
+        break;
+
+      case 3: /* four-byte constant */
+        /* Bytes must not be sign-extended. */
+        value = Mem4(pc);
+        pc += 4;
+        break;
+
+      case 15: /* main memory RAM, four-byte address */
+        addr = Mem4(pc);
+        addr += ramstart;
+        pc += 4;
+        goto MainMemAddr; 
+
+      case 14: /* main memory RAM, two-byte address */
+        addr = (uint)Mem2(pc);
+        addr += ramstart;
+        pc += 2;
+        goto MainMemAddr; 
+
+      case 13: /* main memory RAM, one-byte address */
+        addr = (uint)(Mem1(pc));
+        addr += ramstart;
+        pc++;
+        goto MainMemAddr; 
+        
+      case 7: /* main memory, four-byte address */
+        addr = Mem4(pc);
+        pc += 4;
+        goto MainMemAddr;
+
+      case 6: /* main memory, two-byte address */
+        addr = (uint)Mem2(pc);
+        pc += 2;
+        goto MainMemAddr;
+
+      case 5: /* main memory, one-byte address */
+        addr = (uint)(Mem1(pc));
+        pc++;
+        /* fall through */
+
+      MainMemAddr:
+        /* cases 5, 6, 7, 13, 14, 15 all wind up here. */
+        if (argsize == 4) {
+          value = Mem4(addr);
+        }
+        else if (argsize == 2) {
+          value = Mem2(addr);
+        }
+        else {
+          value = Mem1(addr);
+        }
+        break;
+
+      case 11: /* locals, four-byte address */
+        addr = Mem4(pc);
+        pc += 4;
+        goto LocalsAddr;
+
+      case 10: /* locals, two-byte address */
+        addr = (uint)Mem2(pc);
+        pc += 2;
+        goto LocalsAddr; 
+
+      case 9: /* locals, one-byte address */
+        addr = (uint)(Mem1(pc));
+        pc++;
+        /* fall through */
+
+      LocalsAddr:
+        /* cases 9, 10, 11 all wind up here. It's illegal for addr to not
+           be four-byte aligned, but we don't check this explicitly. 
+           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. */
+        addr += localsbase;
+        if (argsize == 4) {
+          value = Stk4(addr);
+        }
+        else if (argsize == 2) {
+          value = Stk2(addr);
+        }
+        else {
+          value = Stk1(addr);
+        }
+        break;
+
+      default:
+        value = 0;
+        fatal_error("Unknown addressing mode in load operand.");
+      }
+
+      curarg->value = value;
+
+    }
+    else {  /* modeform_Store */
+      switch (mode) {
+
+      case 0: /* discard value */
+        curarg->desttype = 0;
+        curarg->value = 0;
+        break;
+
+      case 8: /* push on stack */
+        curarg->desttype = 3;
+        curarg->value = 0;
+        break;
+
+      case 15: /* main memory RAM, four-byte address */
+        addr = Mem4(pc);
+        addr += ramstart;
+        pc += 4;
+        goto WrMainMemAddr; 
+
+      case 14: /* main memory RAM, two-byte address */
+        addr = (uint)Mem2(pc);
+        addr += ramstart;
+        pc += 2;
+        goto WrMainMemAddr; 
+
+      case 13: /* main memory RAM, one-byte address */
+        addr = (uint)(Mem1(pc));
+        addr += ramstart;
+        pc++;
+        goto WrMainMemAddr; 
+
+      case 7: /* main memory, four-byte address */
+        addr = Mem4(pc);
+        pc += 4;
+        goto WrMainMemAddr;
+
+      case 6: /* main memory, two-byte address */
+        addr = (uint)Mem2(pc);
+        pc += 2;
+        goto WrMainMemAddr;
+
+      case 5: /* main memory, one-byte address */
+        addr = (uint)(Mem1(pc));
+        pc++;
+        /* fall through */
+
+      WrMainMemAddr:
+        /* cases 5, 6, 7 all wind up here. */
+        curarg->desttype = 1;
+        curarg->value = addr;
+        break;
+
+      case 11: /* locals, four-byte address */
+        addr = Mem4(pc);
+        pc += 4;
+        goto WrLocalsAddr;
+
+      case 10: /* locals, two-byte address */
+        addr = (uint)Mem2(pc);
+        pc += 2;
+        goto WrLocalsAddr; 
+
+      case 9: /* locals, one-byte address */
+        addr = (uint)(Mem1(pc));
+        pc++;
+        /* fall through */
+
+      WrLocalsAddr:
+        /* cases 9, 10, 11 all wind up here. It's illegal for addr to not
+           be four-byte aligned, but we don't check this explicitly. 
+           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. */
+        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. */
+        curarg->value = addr;
+        break;
+
+      case 1:
+      case 2:
+      case 3:
+        fatal_error("Constant addressing mode in store operand.");
+
+      default:
+        fatal_error("Unknown addressing mode in store operand.");
+      }
+    }
+  }
+}
+
+void Glulxe::store_operand(uint desttype, uint destaddr, uint storeval) {
+  switch (desttype) {
+
+  case 0: /* do nothing; discard the value. */
+    return;
+
+  case 1: /* main memory. */
+    MemW4(destaddr, storeval);
+    return;
+
+  case 2: /* locals. */
+    destaddr += localsbase;
+    StkW4(destaddr, storeval);
+    return;
+
+  case 3: /* push on stack. */
+    if (stackptr+4 > stacksize) {
+      fatal_error("Stack overflow in store operand.");
+    }
+    StkW4(stackptr, storeval);
+    stackptr += 4;
+    return;
+
+  default:
+    fatal_error("Unknown destination type in store operand.");
+
+  }
+}
+
+void Glulxe::store_operand_s(uint desttype, uint destaddr, uint storeval) {
+  storeval &= 0xFFFF;
+
+  switch (desttype) {
+
+  case 0: /* do nothing; discard the value. */
+    return;
+
+  case 1: /* main memory. */
+    MemW2(destaddr, storeval);
+    return;
+
+  case 2: /* locals. */
+    destaddr += localsbase;
+    StkW2(destaddr, storeval);
+    return;
+
+  case 3: /* push on stack. A four-byte value is actually pushed. */
+    if (stackptr+4 > stacksize) {
+      fatal_error("Stack overflow in store operand.");
+    }
+    StkW4(stackptr, storeval);
+    stackptr += 4;
+    return;
+
+  default:
+    fatal_error("Unknown destination type in store operand.");
+
+  }
+}
+
+void Glulxe::store_operand_b(uint desttype, uint destaddr, uint storeval) {
+  storeval &= 0xFF;
+
+  switch (desttype) {
+
+  case 0: /* do nothing; discard the value. */
+    return;
+
+  case 1: /* main memory. */
+    MemW1(destaddr, storeval);
+    return;
+
+  case 2: /* locals. */
+    destaddr += localsbase;
+    StkW1(destaddr, storeval);
+    return;
+
+  case 3: /* push on stack. A four-byte value is actually pushed. */
+    if (stackptr+4 > stacksize) {
+      fatal_error("Stack overflow in store operand.");
+    }
+    StkW4(stackptr, storeval);
+    stackptr += 4;
+    return;
+
+  default:
+    fatal_error("Unknown destination type in store operand.");
+
+  }
+}
+
+} // End of namespace Glulxe
+} // End of namespace Glk
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 669f811..6addf7e 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -66,6 +66,7 @@ MODULE_OBJS := \
 	glulxe/glkop.o \
 	glulxe/glulxe.o \
 	glulxe/heap.o \
+	glulxe/operand.o \
 	magnetic/detection.o \
 	magnetic/magnetic.o \
 	scott/detection.o \


Commit: a6cf55862d8c4ece72fc1f0ecda083b78b853597
    https://github.com/scummvm/scummvm/commit/a6cf55862d8c4ece72fc1f0ecda083b78b853597
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-04-17T20:46:06-07:00

Commit Message:
GLK: GLULXE: Add search methods

Changed paths:
  A engines/glk/glulxe/search.cpp
    engines/glk/glulxe/glulxe.h
    engines/glk/glulxe/glulxe_types.h
    engines/glk/module.mk


diff --git a/engines/glk/glulxe/glulxe.h b/engines/glk/glulxe/glulxe.h
index 97a7c09..5dac144 100644
--- a/engines/glk/glulxe/glulxe.h
+++ b/engines/glk/glulxe/glulxe.h
@@ -40,8 +40,8 @@ public:
 	uint gamefile_start, gamefile_len;
 	char *init_err, *init_err2;
 
-	unsigned char *memmap;
-	unsigned char *stack;
+	byte *memmap;
+	byte *stack;
 
 	uint ramstart;
 	uint endgamefile;
@@ -247,6 +247,19 @@ protected:
 	char *get_game_id();
 
 	/**@}*/
+
+	/**
+	 * \defgroup search support methods
+	 * @{
+	 */
+
+	/**
+	 * This massages the key into a form that's easier to handle. When it returns, the key will
+	 * be stored in keybuf if keysize <= 4; otherwise, it will be in memory.
+	 */
+	void fetchkey(unsigned char *keybuf, uint key, uint keysize, uint options);
+
+	/**@}*/
 public:
 	/**
 	 * Constructor
@@ -511,15 +524,39 @@ public:
 	 * @{
 	 */
 
-	uint linear_search(uint key, uint keysize,
-		uint start, uint structsize, uint numstructs,
+
+	 /**
+	  * An array of data structures is stored in memory, beginning at start, each structure being structsize bytes.
+	  * Within each struct, there is a key value keysize bytes long, starting at position keyoffset (from
+	  * the start of the structure.) Search through these in order. If one is found whose key matches, return it.
+	  * If numstructs are searched with no result, return NULL.
+	  *
+	  * numstructs may be -1 (0xFFFFFFFF) to indicate no upper limit to the number of structures to search.
+	  * The search will continue until a match is found, or (if ZeroKeyTerminates is set) a zero key.
+	  *
+	  * The KeyIndirect, ZeroKeyTerminates, and ReturnIndex options may be used.
+	  */
+	uint linear_search(uint key, uint keysize, uint start, uint structsize, uint numstructs,
 		uint keyoffset, uint options);
-	uint binary_search(uint key, uint keysize,
-		uint start, uint structsize, uint numstructs,
+
+	/**
+	 * An array of data structures is in memory, as above. However, the structs must be stored in forward
+	 * order of their keys (taking each key to be a multibyte unsigned integer.) There can be no duplicate keys.
+	 * numstructs must indicate the exact length of the array; it cannot be -1.
+	 *
+	 * The KeyIndirect and ReturnIndex options may be used.
+	 */
+	uint binary_search(uint key, uint keysize, uint start, uint structsize, uint numstructs,
 		uint keyoffset, uint options);
-	uint linked_search(uint key, uint keysize,
-		uint start, uint keyoffset, uint nextoffset,
-		uint options);
+
+	/**
+	 * The structures may be anywhere in memory, in any order. They are linked by a four-byte address field,
+	 * which is found in each struct at position nextoffset. If this field contains zero, it indicates
+	 * the end of the linked list.
+	 *
+	 * The KeyIndirect and ZeroKeyTerminates options may be used.
+	 */
+	uint linked_search(uint key, uint keysize, uint start, uint keyoffset, uint nextoffset, uint options);
 
 	/**@}*/
 
diff --git a/engines/glk/glulxe/glulxe_types.h b/engines/glk/glulxe/glulxe_types.h
index 8c99d90..50b62a0 100644
--- a/engines/glk/glulxe/glulxe_types.h
+++ b/engines/glk/glulxe/glulxe_types.h
@@ -26,53 +26,54 @@
 #include "common/scummsys.h"
 
 namespace Glk {
-	namespace Glulxe {
+namespace Glulxe {
 
-		class Glulxe;
+class Glulxe;
 
-		/**
-		 * Comment this definition to turn off memory-address checking. With verification on,
-		 * all reads and writes to main memory will be checked to ensure they're in range.
-		 * This is slower, but prevents malformed game files from crashing the interpreter.
-		 */
+/**
+ * Comment this definition to turn off memory-address checking. With verification on,
+ * all reads and writes to main memory will be checked to ensure they're in range.
+ * This is slower, but prevents malformed game files from crashing the interpreter.
+ */
 #define VERIFY_MEMORY_ACCESS (1)
 
-		 /**
-		  * Uncomment this definition to permit an exception for memory-address checking for @glk and @copy
-		  * opcodes that try to write to memory address 0. This was a bug in old Superglus-built game files.
-		  */
-		  /* #define TOLERATE_SUPERGLUS_BUG (1) */
-
-		  /**
-		   * Uncomment this definition to turn on Glulx VM profiling. In this mode, all function calls are timed,
-		   * and the timing information is written to a data file called "profile-raw".
-		   * (Build note: on Linux, glibc may require you to also define _BSD_SOURCE or _DEFAULT_SOURCE or both
-		   * for the timeradd() macro.)
-		   */
-		   /* #define VM_PROFILING (1) */
-
-		   /**
-			* Uncomment this definition to turn on the Glulx debugger. You should only do this when debugging
-			* facilities are desired; it slows down the interpreter. If you do, you will need to build with libxml2;
-			* see the Makefile.
-			*/
-			/* #define VM_DEBUGGER (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. */
+/**
+ * Uncomment this definition to permit an exception for memory-address checking for @glk and @copy
+ * opcodes that try to write to memory address 0. This was a bug in old Superglus-built game files.
+ */
+/* #define TOLERATE_SUPERGLUS_BUG (1) */
+
+/**
+ * Uncomment this definition to turn on Glulx VM profiling. In this mode, all function calls are timed,
+ * and the timing information is written to a data file called "profile-raw".
+ * (Build note: on Linux, glibc may require you to also define _BSD_SOURCE or _DEFAULT_SOURCE or both
+ * for the timeradd() macro.)
+ */
+/* #define VM_PROFILING (1) */
+
+/**
+ * Uncomment this definition to turn on the Glulx debugger. You should only do this when debugging
+ * facilities are desired; it slows down the interpreter. If you do, you will need to build with libxml2;
+ * see the Makefile.
+ */
+/* #define VM_DEBUGGER (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)
 
-			 /**
-			  * Comment this definition to not cache the original state of RAM in (real) memory. This saves some memory,
-			  * but slows down save/restore/undo operations, which will have to read the original state off disk
-			  * every time.
-			  */
+/**
+ * Comment this definition to not cache the original state of RAM in (real) memory. This saves some memory,
+ * but slows down save/restore/undo operations, which will have to read the original state off disk
+ * every time.
+ */
 #define SERIALIZE_CACHE_RAM (1)
 
-			  /**
-			   * Some macros to read and write integers to memory, always in big-endian format.
-			   */
+/**
+ * Some macros to read and write integers to memory, always in big-endian format.
+ */
 #define Read4(ptr) READ_BE_UINT32(ptr)
 #define Read2(ptr) READ_BE_UINT16(ptr)
 #define Read1(ptr) ((byte)(((byte *)(ptr))[0]))
@@ -88,19 +89,19 @@ namespace Glk {
 #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 Mem1(adr)  (Read1(memmap+(adr)))
+#define Mem2(adr)  (Read2(memmap+(adr)))
+#define Mem4(adr)  (Read4(memmap+(adr)))
 #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 addresses which are multiples of four, etc.)
-				* If the alignment rules are not followed, the program will see performance
-				* degradation or even crashes, depending on the machine CPU.
-				*/
+/**
+ * Macros to access values on the stack. These *must* be used with proper alignment!
+ * (That is, Stk4 and StkW4 must take addresses which are multiples of four, etc.)
+ * If the alignment rules are not followed, the program will see performance
+ * degradation or even crashes, depending on the machine CPU.
+ */
 #define Stk1(adr)   \
   (*((unsigned char *)(stack+(adr))))
 #define Stk2(adr)   \
diff --git a/engines/glk/glulxe/search.cpp b/engines/glk/glulxe/search.cpp
new file mode 100644
index 0000000..d2f1c85
--- /dev/null
+++ b/engines/glk/glulxe/search.cpp
@@ -0,0 +1,216 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/glk/glulxe/glulxe.h"
+
+namespace Glk {
+namespace Glulxe {
+
+enum serop {
+	serop_KeyIndirect       = 0x01,
+	serop_ZeroKeyTerminates = 0x02,
+	serop_ReturnIndex       = 0x04
+};
+
+uint Glulxe::linear_search(uint key, uint keysize,  uint start, uint structsize, uint numstructs, 
+		uint keyoffset, uint options) {
+  unsigned char keybuf[4];
+  uint count;
+  uint ix;
+  int retindex = ((options & serop_ReturnIndex) != 0);
+  int zeroterm = ((options & serop_ZeroKeyTerminates) != 0);
+
+  fetchkey(keybuf, key, keysize, options);
+
+  for (count=0; count<numstructs; count++, start+=structsize) {
+    int match = true;
+    if (keysize <= 4) {
+      for (ix=0; match && ix<keysize; ix++) {
+        if (Mem1(start + keyoffset + ix) != keybuf[ix])
+          match = false;
+      }
+    }
+    else {
+      for (ix=0; match && ix<keysize; ix++) {
+        if (Mem1(start + keyoffset + ix) != Mem1(key + ix))
+          match = false;
+      }
+    }
+
+    if (match) {
+      if (retindex)
+        return count;
+      else
+        return start;
+    }
+
+    if (zeroterm) {
+      match = true;
+      for (ix=0; match && ix<keysize; ix++) {
+        if (Mem1(start + keyoffset + ix) != 0)
+          match = false;
+      }
+      if (match) {
+        break;
+      }
+    }
+  }
+  
+  if (retindex)
+    return (uint)-1;
+  else
+    return 0;
+}
+
+
+uint Glulxe::binary_search(uint key, uint keysize,  uint start, uint structsize, uint numstructs, 
+		uint keyoffset, uint options) {
+  byte keybuf[4];
+  byte byte1, byte2;
+  uint top, bot, val, addr;
+  uint ix;
+  int retindex = ((options & serop_ReturnIndex) != 0);
+
+  fetchkey(keybuf, key, keysize, options);
+  
+  bot = 0;
+  top = numstructs;
+  while (bot < top) {
+    int cmp = 0;
+    val = (top+bot) / 2;
+    addr = start + val * structsize;
+
+    if (keysize <= 4) {
+      for (ix=0; (!cmp) && ix<keysize; ix++) {
+        byte1 = Mem1(addr + keyoffset + ix);
+        byte2 = keybuf[ix];
+        if (byte1 < byte2)
+          cmp = -1;
+        else if (byte1 > byte2)
+          cmp = 1;
+      }
+    }
+    else {
+      for (ix=0; (!cmp) && ix<keysize; ix++) {
+        byte1 = Mem1(addr + keyoffset + ix);
+        byte2 = Mem1(key + ix);
+        if (byte1 < byte2)
+          cmp = -1;
+        else if (byte1 > byte2)
+          cmp = 1;
+      }
+    }
+
+    if (!cmp) {
+      if (retindex)
+        return val;
+      else
+        return addr;
+    }
+
+    if (cmp < 0) {
+      bot = val+1;
+    }
+    else {
+      top = val;
+    }
+  }
+
+  if (retindex)
+    return (uint)-1;
+  else
+    return 0;
+}
+
+uint Glulxe::linked_search(uint key, uint keysize,  uint start, uint keyoffset, uint nextoffset, uint options) {
+  unsigned char keybuf[4];
+  uint ix;
+  uint val;
+  int zeroterm = ((options & serop_ZeroKeyTerminates) != 0);
+
+  fetchkey(keybuf, key, keysize, options);
+
+  while (start != 0) {
+    int match = true;
+    if (keysize <= 4) {
+      for (ix=0; match && ix<keysize; ix++) {
+        if (Mem1(start + keyoffset + ix) != keybuf[ix])
+          match = false;
+      }
+    }
+    else {
+      for (ix=0; match && ix<keysize; ix++) {
+        if (Mem1(start + keyoffset + ix) != Mem1(key + ix))
+          match = false;
+      }
+    }
+
+    if (match) {
+      return start;
+    }
+
+    if (zeroterm) {
+      match = true;
+      for (ix=0; match && ix<keysize; ix++) {
+        if (Mem1(start + keyoffset + ix) != 0)
+          match = false;
+      }
+      if (match) {
+        break;
+      }
+    }
+    
+    val = start + nextoffset;
+    start = Mem4(val);
+  }
+
+  return 0;
+}
+
+void Glulxe::fetchkey(unsigned char *keybuf, uint key, uint keysize,  uint options) {
+  uint ix;
+
+  if (options & serop_KeyIndirect) {
+    if (keysize <= 4) {
+      for (ix=0; ix<keysize; ix++)
+        keybuf[ix] = Mem1(key+ix);
+    }
+  }
+  else {
+    switch (keysize) {
+    case 4:
+      Write4(keybuf, key);
+      break;
+    case 2:
+      Write2(keybuf, key);
+      break;
+    case 1:
+      Write1(keybuf, key);
+      break;
+    default:
+      fatal_error("Direct search key must hold one, two, or four bytes.");
+    }
+  }
+}
+
+} // End of namespace Glulxe
+} // End of namespace Glk
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 6addf7e..e09a63e 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -67,6 +67,7 @@ MODULE_OBJS := \
 	glulxe/glulxe.o \
 	glulxe/heap.o \
 	glulxe/operand.o \
+	glulxe/search.o \
 	magnetic/detection.o \
 	magnetic/magnetic.o \
 	scott/detection.o \


Commit: 1c4649018388e7df15bc0787ed66b6dc7755db1d
    https://github.com/scummvm/scummvm/commit/1c4649018388e7df15bc0787ed66b6dc7755db1d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-04-17T20:46:06-07:00

Commit Message:
GLK: GLULXE: Add serial methods

Changed paths:
  A engines/glk/glulxe/serial.cpp
    engines/glk/glulxe/glulxe.cpp
    engines/glk/glulxe/glulxe.h
    engines/glk/glulxe/glulxe_types.h
    engines/glk/module.mk


diff --git a/engines/glk/glulxe/glulxe.cpp b/engines/glk/glulxe/glulxe.cpp
index d16ecfb..9db784c 100644
--- a/engines/glk/glulxe/glulxe.cpp
+++ b/engines/glk/glulxe/glulxe.cpp
@@ -40,7 +40,9 @@ Glulxe::Glulxe(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst,
 		routine_metaclass(0), string_metaclass(0), self(0), num_attr_bytes(0), cpv__start(0),
 		accelentries(nullptr),
 		// heap
-		heap_start(0), alloc_count(0), heap_head(nullptr), heap_tail(nullptr) {
+		heap_start(0), alloc_count(0), heap_head(nullptr), heap_tail(nullptr),
+		// serial
+		max_undo_level(8), undo_chain_size(0), undo_chain_num(0), undo_chain(nullptr), ramcache(nullptr) {
 	g_vm = this;
 }
 
diff --git a/engines/glk/glulxe/glulxe.h b/engines/glk/glulxe/glulxe.h
index 5dac144..8402a1b 100644
--- a/engines/glk/glulxe/glulxe.h
+++ b/engines/glk/glulxe/glulxe.h
@@ -86,8 +86,8 @@ public:
 	 * @{
 	 */
 
-	uint heap_start = 0;	///< zero for inactive heap
-	int alloc_count = 0;
+	uint heap_start;	///< zero for inactive heap
+	int alloc_count;
 
 	/* The heap_head/heap_tail is a doubly-linked list of blocks, both
 	   free and allocated. It is kept in address order. It should be
@@ -104,8 +104,8 @@ public:
 	   free-list. To make free more efficient, we could keep a hash
 	   table of allocations.
 	 */
-	heapblock_t *heap_head = NULL;
-	heapblock_t *heap_tail = NULL;
+	heapblock_t *heap_head;
+	heapblock_t *heap_tail;
 
 	/**@}*/
 
@@ -119,6 +119,30 @@ public:
 	 * for the first 128 opcodes, which are the ones used most frequently.
 	 */
 	const operandlist_t *fast_operandlist[0x80];
+
+	/**@}*/
+
+	/**
+	 * \defgroup serial fields
+	 * @{
+	 */
+
+	 /**
+	  * This can be adjusted before startup by platform-specific startup code -- that is, preference code.
+	  */
+	int max_undo_level;
+
+	int undo_chain_size;
+	int undo_chain_num;
+	byte **undo_chain;
+
+	/**
+	 * This will contain a copy of RAM (ramstate to endmem) as it exists in the game file.
+	 */
+	byte *ramcache;
+
+	/**@}*/
+
 protected:
 	/**
 	 * \defgroup glkop fields
@@ -140,7 +164,7 @@ protected:
 	classtable_t **classes;
 
 	/**@}*/
-protected:
+
 	/**
 	 * \defgroup accel support methods
 	 * @{
@@ -260,6 +284,34 @@ protected:
 	void fetchkey(unsigned char *keybuf, uint key, uint keysize, uint options);
 
 	/**@}*/
+
+	/**
+	 * \defgroup serial support methods
+	 * @{
+	 */
+
+	uint write_memstate(dest_t *dest);
+	uint write_heapstate(dest_t *dest, int portable);
+	uint write_stackstate(dest_t *dest, int portable);
+	uint read_memstate(dest_t *dest, uint chunklen);
+	uint read_heapstate(dest_t *dest, uint chunklen, int portable, uint *sumlen, uint **summary);
+	uint read_stackstate(dest_t *dest, uint chunklen, int portable);
+	uint write_heapstate_sub(uint sumlen, uint *sumarray, dest_t *dest, int portable);
+	static int sort_heap_summary(void *p1, void *p2);
+
+	int read_byte(dest_t *dest, byte *val);
+	int read_short(dest_t *dest, uint16 *val);
+	int read_long(dest_t *dest, uint *val);
+
+	int write_byte(dest_t *dest, byte val);
+	int write_short(dest_t *dest, uint16 val);
+	int write_long(dest_t *dest, uint val);
+
+	int read_buffer(dest_t *dest, byte *ptr, uint len);
+	int reposition_write(dest_t *dest, uint pos);
+	int write_buffer(dest_t *dest, const byte *ptr, uint len);
+
+	/**@}*/
 public:
 	/**
 	 * Constructor
@@ -508,15 +560,6 @@ public:
 	 * @{
 	 */
 
-	int max_undo_level;
-	int init_serial(void);
-	void final_serial(void);
-	uint perform_save(strid_t str);
-	uint perform_restore(strid_t str, int fromshell);
-	uint perform_saveundo(void);
-	uint perform_restoreundo(void);
-	uint perform_verify(void);
-
 	/**@}*/
 
 	/**
@@ -729,10 +772,6 @@ public:
 	 */
 #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
@@ -754,6 +793,51 @@ public:
 
 #endif /* FLOAT_SUPPORT */
 	/**@}*/
+
+	/**
+	 * \defgroup serial access methods
+	 * @{
+	 */
+
+	 /**
+	  * Set up the undo chain and anything else that needs to be set up.
+	  */
+	bool init_serial();
+
+	/**
+	 * Clean up memory when the VM shuts down.
+	 */
+	void final_serial();
+
+	/**
+	 * Write the state to the output stream. This returns 0 on success, 1 on failure.
+	 */
+	uint perform_save(strid_t str);
+
+	/**
+	 * Pull a state pointer from a stream. This returns 0 on success, 1 on failure. Note that if it succeeds,
+	 * the frameptr, localsbase, and valstackbase registers are invalid; they must be rebuilt from the stack.
+	 *
+	 * If fromshell is true, the restore is being invoked by the library shell (an autorestore of some kind).
+	 * This currently happens only in iosglk.
+	 */
+	uint perform_restore(strid_t str, int fromshell);
+
+	/**
+	 * Add a state pointer to the undo chain. This returns 0 on success, 1 on failure.
+	 */
+	uint perform_saveundo();
+
+	/**
+	 * Pull a state pointer from the undo chain. This returns 0 on success, 1 on failure.
+	 * Note that if it succeeds, the frameptr, localsbase, and valstackbase registers are invalid;
+	 * they must be rebuilt from the stack.
+	 */
+	uint perform_restoreundo();
+
+	uint perform_verify();
+
+	/**@}*/
 };
 
 extern Glulxe *g_vm;
diff --git a/engines/glk/glulxe/glulxe_types.h b/engines/glk/glulxe/glulxe_types.h
index 50b62a0..5b14a6a 100644
--- a/engines/glk/glulxe/glulxe_types.h
+++ b/engines/glk/glulxe/glulxe_types.h
@@ -266,6 +266,11 @@ enum gestulx {
 	gestulx_Float        = 11
 };
 
+/**
+ * You may have to edit the definition of gfloat32 to make sure it's really a 32-bit floating-point type.
+ */
+typedef float gfloat32;
+
 struct dispatch_splot_struct {
 	int numwanted;
 	int maxargs;
@@ -358,6 +363,22 @@ struct heapblock_struct {
 };
 typedef heapblock_struct heapblock_t;
 
+/**
+ * This structure allows us to write either to a Glk stream or to a dynamically-allocated memory chunk.
+ */
+struct dest_struct {
+	int ismem;
+
+	/* If it's a Glk stream: */
+	strid_t str;
+
+	/* If it's a block of memory: */
+	byte *ptr;
+	uint pos;
+	uint size;
+};
+typedef dest_struct dest_t;
+
 } // End of namespace Glulxe
 } // End of namespace Glk
 
diff --git a/engines/glk/glulxe/serial.cpp b/engines/glk/glulxe/serial.cpp
new file mode 100644
index 0000000..66464e8
--- /dev/null
+++ b/engines/glk/glulxe/serial.cpp
@@ -0,0 +1,1144 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/glk/glulxe/glulxe.h"
+
+namespace Glk {
+namespace Glulxe {
+
+#define IFFID(c1, c2, c3, c4) MKTAG(c1, c2, c3, c4)
+
+bool Glulxe::init_serial() {
+  undo_chain_num = 0;
+  undo_chain_size = max_undo_level;
+  undo_chain = (unsigned char **)glulx_malloc(sizeof(unsigned char *) * undo_chain_size);
+  if (!undo_chain)
+    return false;
+
+#ifdef SERIALIZE_CACHE_RAM
+  {
+    uint len = (endmem - ramstart);
+    uint res;
+    ramcache = (unsigned char *)glulx_malloc(sizeof(unsigned char *) * len);
+    if (!ramcache)
+      return false;
+    glk_stream_set_position(gamefile, gamefile_start+ramstart, seekmode_Start);
+    res = glk_get_buffer_stream(gamefile, (char *)ramcache, len);
+    if (res != len)
+      return false;
+  }
+#endif /* SERIALIZE_CACHE_RAM */
+
+  return true;
+}
+
+void Glulxe::final_serial() {
+  if (undo_chain) {
+    int ix;
+    for (ix=0; ix<undo_chain_num; ix++) {
+      glulx_free(undo_chain[ix]);
+    }
+    glulx_free(undo_chain);
+  }
+  undo_chain = NULL;
+  undo_chain_size = 0;
+  undo_chain_num = 0;
+
+#ifdef SERIALIZE_CACHE_RAM
+  if (ramcache) {
+    glulx_free(ramcache);
+    ramcache = NULL;
+  }
+#endif /* SERIALIZE_CACHE_RAM */
+}
+
+uint Glulxe::perform_saveundo() {
+  dest_t dest;
+  uint res;
+  uint memstart = 0, memlen = 0, heapstart = 0, heaplen = 0;
+  uint stackstart = 0, stacklen = 0;
+
+  /* The format for undo-saves is simpler than for saves on disk. We
+     just have a memory chunk, a heap chunk, and a stack chunk, in
+     that order. We skip the IFF chunk headers (although the size
+     fields are still there.) We also don't bother with IFF's 16-bit
+     alignment. */
+
+  if (undo_chain_size == 0)
+    return 1;
+
+  dest.ismem = true;
+  dest.size = 0;
+  dest.pos = 0;
+  dest.ptr = NULL;
+  dest.str = NULL;
+
+  res = 0;
+  if (res == 0) {
+    res = write_long(&dest, 0); /* space for chunk length */
+  }
+  if (res == 0) {
+    memstart = dest.pos;
+    res = write_memstate(&dest);
+    memlen = dest.pos - memstart;
+  }
+  if (res == 0) {
+    res = write_long(&dest, 0); /* space for chunk length */
+  }
+  if (res == 0) {
+    heapstart = dest.pos;
+    res = write_heapstate(&dest, false);
+    heaplen = dest.pos - heapstart;
+  }
+  if (res == 0) {
+    res = write_long(&dest, 0); /* space for chunk length */
+  }
+  if (res == 0) {
+    stackstart = dest.pos;
+    res = write_stackstate(&dest, false);
+    stacklen = dest.pos - stackstart;
+  }
+
+  if (res == 0) {
+    /* Trim it down to the perfect size. */
+    dest.ptr = (byte *)glulx_realloc(dest.ptr, dest.pos);
+    if (!dest.ptr)
+      res = 1;
+  }
+  if (res == 0) {
+    res = reposition_write(&dest, memstart-4);
+  }
+  if (res == 0) {
+    res = write_long(&dest, memlen);
+  }
+  if (res == 0) {
+    res = reposition_write(&dest, heapstart-4);
+  }
+  if (res == 0) {
+    res = write_long(&dest, heaplen);
+  }
+  if (res == 0) {
+    res = reposition_write(&dest, stackstart-4);
+  }
+  if (res == 0) {
+    res = write_long(&dest, stacklen);
+  }
+
+  if (res == 0) {
+    /* It worked. */
+    if (undo_chain_num >= undo_chain_size) {
+      glulx_free(undo_chain[undo_chain_num-1]);
+      undo_chain[undo_chain_num-1] = NULL;
+    }
+    if (undo_chain_size > 1)
+      memmove(undo_chain+1, undo_chain, 
+        (undo_chain_size-1) * sizeof(unsigned char *));
+    undo_chain[0] = dest.ptr;
+    if (undo_chain_num < undo_chain_size)
+      undo_chain_num += 1;
+    dest.ptr = NULL;
+  }
+  else {
+    /* It didn't work. */
+    if (dest.ptr) {
+      glulx_free(dest.ptr);
+      dest.ptr = NULL;
+    }
+  }
+    
+  return res;
+}
+
+uint Glulxe::perform_restoreundo() {
+  dest_t dest;
+  uint res, val = 0;
+  uint heapsumlen = 0;
+  uint *heapsumarr = NULL;
+
+  /* If profiling is enabled and active then fail. */
+  #if VM_PROFILING
+  if (profile_profiling_active())
+    return 1;
+  #endif /* VM_PROFILING */
+
+  if (undo_chain_size == 0 || undo_chain_num == 0)
+    return 1;
+
+  dest.ismem = true;
+  dest.size = 0;
+  dest.pos = 0;
+  dest.ptr = undo_chain[0];
+  dest.str = NULL;
+
+  res = 0;
+  if (res == 0) {
+    res = read_long(&dest, &val);
+  }
+  if (res == 0) {
+    res = read_memstate(&dest, val);
+  }
+  if (res == 0) {
+    res = read_long(&dest, &val);
+  }
+  if (res == 0) {
+    res = read_heapstate(&dest, val, false, &heapsumlen, &heapsumarr);
+  }
+  if (res == 0) {
+    res = read_long(&dest, &val);
+  }
+  if (res == 0) {
+    res = read_stackstate(&dest, val, false);
+  }
+  /* ### really, many of the failure modes of those calls ought to
+     cause fatal errors. The stack or main memory may be damaged now. */
+
+  if (res == 0) {
+    if (heapsumarr)
+      res = heap_apply_summary(heapsumlen, heapsumarr);
+  }
+
+  if (res == 0) {
+    /* It worked. */
+    if (undo_chain_size > 1)
+      memmove(undo_chain, undo_chain+1,
+        (undo_chain_size-1) * sizeof(unsigned char *));
+    undo_chain_num -= 1;
+    glulx_free(dest.ptr);
+    dest.ptr = NULL;
+  }
+  else {
+    /* It didn't work. */
+    dest.ptr = NULL;
+  }
+
+  return res;
+}
+
+uint Glulxe::perform_save(strid_t str) {
+  dest_t dest;
+  int ix;
+  uint res, lx, val;
+  uint memstart = 0, memlen = 0, stackstart = 0, stacklen = 0;
+  uint heapstart = 0, heaplen = 0, filestart = 0, filelen = 0;
+
+  stream_get_iosys(&val, &lx);
+  if (val != 2) {
+    /* Not using the Glk I/O system, so bail. This function only
+       knows how to write to a Glk stream. */
+    fatal_error("Streams are only available in Glk I/O system.");
+  }
+
+  if (str == 0)
+    return 1;
+
+  dest.ismem = false;
+  dest.size = 0;
+  dest.pos = 0;
+  dest.ptr = NULL;
+  dest.str = str;
+
+  res = 0;
+
+  /* Quetzal header. */
+  if (res == 0) {
+    res = write_long(&dest, IFFID('F', 'O', 'R', 'M'));
+  }
+  if (res == 0) {
+    res = write_long(&dest, 0); /* space for file length */
+    filestart = dest.pos;
+  }
+
+  if (res == 0) {
+    res = write_long(&dest, IFFID('I', 'F', 'Z', 'S')); /* ### ? */
+  }
+
+  /* Header chunk. This is the first 128 bytes of memory. */
+  if (res == 0) {
+    res = write_long(&dest, IFFID('I', 'F', 'h', 'd'));
+  }
+  if (res == 0) {
+    res = write_long(&dest, 128);
+  }
+  for (ix=0; res==0 && ix<128; ix++) {
+    res = write_byte(&dest, Mem1(ix));
+  }
+  /* Always even, so no padding necessary. */
+  
+  /* Memory chunk. */
+  if (res == 0) {
+    res = write_long(&dest, IFFID('C', 'M', 'e', 'm'));
+  }
+  if (res == 0) {
+    res = write_long(&dest, 0); /* space for chunk length */
+  }
+  if (res == 0) {
+    memstart = dest.pos;
+    res = write_memstate(&dest);
+    memlen = dest.pos - memstart;
+  }
+  if (res == 0 && (memlen & 1) != 0) {
+    res = write_byte(&dest, 0);
+  }
+
+  /* Heap chunk. */
+  if (res == 0) {
+    res = write_long(&dest, IFFID('M', 'A', 'l', 'l'));
+  }
+  if (res == 0) {
+    res = write_long(&dest, 0); /* space for chunk length */
+  }
+  if (res == 0) {
+    heapstart = dest.pos;
+    res = write_heapstate(&dest, true);
+    heaplen = dest.pos - heapstart;
+  }
+  /* Always even, so no padding necessary. */
+
+  /* Stack chunk. */
+  if (res == 0) {
+    res = write_long(&dest, IFFID('S', 't', 'k', 's'));
+  }
+  if (res == 0) {
+    res = write_long(&dest, 0); /* space for chunk length */
+  }
+  if (res == 0) {
+    stackstart = dest.pos;
+    res = write_stackstate(&dest, true);
+    stacklen = dest.pos - stackstart;
+  }
+  if (res == 0 && (stacklen & 1) != 0) {
+    res = write_byte(&dest, 0);
+  }
+
+  filelen = dest.pos - filestart;
+
+  /* Okay, fill in all the lengths. */
+  if (res == 0) {
+    res = reposition_write(&dest, memstart-4);
+  }
+  if (res == 0) {
+    res = write_long(&dest, memlen);
+  }
+  if (res == 0) {
+    res = reposition_write(&dest, heapstart-4);
+  }
+  if (res == 0) {
+    res = write_long(&dest, heaplen);
+  }
+  if (res == 0) {
+    res = reposition_write(&dest, stackstart-4);
+  }
+  if (res == 0) {
+    res = write_long(&dest, stacklen);
+  }
+  if (res == 0) {
+    res = reposition_write(&dest, filestart-4);
+  }
+  if (res == 0) {
+    res = write_long(&dest, filelen);
+  }
+
+  /* All done. */
+    
+  return res;
+}
+
+uint Glulxe::perform_restore(strid_t str, int fromshell) {
+  dest_t dest;
+  int ix;
+  uint lx, res, val;
+  uint filestart, filelen = 0;
+  uint heapsumlen = 0;
+  uint *heapsumarr = NULL;
+
+  /* If profiling is enabled and active then fail. */
+  #if VM_PROFILING
+  if (profile_profiling_active())
+    return 1;
+  #endif /* VM_PROFILING */
+
+  stream_get_iosys(&val, &lx);
+  if (val != 2 && !fromshell) {
+    /* Not using the Glk I/O system, so bail. This function only
+       knows how to read from a Glk stream. (But in the autorestore
+       case, iosys hasn't been set yet, so ignore this test.) */
+    fatal_error("Streams are only available in Glk I/O system.");
+  }
+
+  if (str == 0)
+    return 1;
+
+  dest.ismem = false;
+  dest.size = 0;
+  dest.pos = 0;
+  dest.ptr = NULL;
+  dest.str = str;
+
+  res = 0;
+
+  /* ### the format errors checked below should send error messages to
+     the current stream. */
+
+  if (res == 0) {
+    res = read_long(&dest, &val);
+  }
+  if (res == 0 && val != IFFID('F', 'O', 'R', 'M')) {
+    /* ### bad header */
+    return 1;
+  }
+  if (res == 0) {
+    res = read_long(&dest, &filelen);
+  }
+  filestart = dest.pos;
+
+  if (res == 0) {
+    res = read_long(&dest, &val);
+  }
+  if (res == 0 && val != IFFID('I', 'F', 'Z', 'S')) { /* ### ? */
+    /* ### bad header */
+    return 1;
+  }
+
+  while (res == 0 && dest.pos < filestart+filelen) {
+    /* Read a chunk and deal with it. */
+    uint chunktype=0, chunkstart=0, chunklen=0;
+    unsigned char dummy;
+
+    if (res == 0) {
+      res = read_long(&dest, &chunktype);
+    }
+    if (res == 0) {
+      res = read_long(&dest, &chunklen);
+    }
+    chunkstart = dest.pos;
+
+    if (chunktype == IFFID('I', 'F', 'h', 'd')) {
+      for (ix=0; res==0 && ix<128; ix++) {
+        res = read_byte(&dest, &dummy);
+        if (res == 0 && Mem1(ix) != dummy) {
+          /* ### non-matching header */
+          return 1;
+        }
+      }
+    }
+    else if (chunktype == IFFID('C', 'M', 'e', 'm')) {
+      res = read_memstate(&dest, chunklen);
+    }
+    else if (chunktype == IFFID('M', 'A', 'l', 'l')) {
+      res = read_heapstate(&dest, chunklen, true, &heapsumlen, &heapsumarr);
+    }
+    else if (chunktype == IFFID('S', 't', 'k', 's')) {
+      res = read_stackstate(&dest, chunklen, true);
+    }
+    else {
+      /* Unknown chunk type. Skip it. */
+      for (lx=0; res==0 && lx<chunklen; lx++) {
+        res = read_byte(&dest, &dummy);
+      }
+    }
+
+    if (chunkstart+chunklen != dest.pos) {
+      /* ### funny chunk length */
+      return 1;
+    }
+
+    if ((chunklen & 1) != 0) {
+      if (res == 0) {
+        res = read_byte(&dest, &dummy);
+      }
+    }
+  }
+
+  if (res == 0) {
+    if (heapsumarr) {
+      /* The summary might have come from any interpreter, so it could
+         be out of order. We'll sort it. */
+      glulx_sort(heapsumarr+2, (heapsumlen-2)/2, 2*sizeof(uint), &sort_heap_summary);
+      res = heap_apply_summary(heapsumlen, heapsumarr);
+    }
+  }
+
+  if (res)
+    return 1;
+
+  return 0;
+}
+
+int Glulxe::reposition_write(dest_t *dest, uint pos) {
+  if (dest->ismem) {
+    dest->pos = pos;
+  } else {
+    glk_stream_set_position(dest->str, pos, seekmode_Start);
+    dest->pos = pos;
+  }
+
+  return 0;
+}
+
+int Glulxe::write_buffer(dest_t *dest, const byte *ptr, uint len) {
+  if (dest->ismem) {
+    if (dest->pos+len > dest->size) {
+      dest->size = dest->pos+len+1024;
+      if (!dest->ptr) {
+        dest->ptr = (byte *)glulx_malloc(dest->size);
+      } else {
+        dest->ptr = (byte *)glulx_realloc(dest->ptr, dest->size);
+      }
+      if (!dest->ptr)
+        return 1;
+    }
+    memcpy(dest->ptr+dest->pos, ptr, len);
+  }
+  else {
+    glk_put_buffer_stream(dest->str, (char *)ptr, len);
+  }
+
+  dest->pos += len;
+
+  return 0;
+}
+
+int Glulxe::read_buffer(dest_t *dest, unsigned char *ptr, uint len) {
+  uint newlen;
+
+  if (dest->ismem) {
+    memcpy(ptr, dest->ptr+dest->pos, len);
+  }
+  else {
+    newlen = glk_get_buffer_stream(dest->str, (char *)ptr, len);
+    if (newlen != len)
+      return 1;
+  }
+
+  dest->pos += len;
+
+  return 0;
+}
+
+int Glulxe::write_long(dest_t *dest, uint val) {
+  unsigned char buf[4];
+  Write4(buf, val);
+  return write_buffer(dest, buf, 4);
+}
+
+int Glulxe::write_short(dest_t *dest, uint16 val) {
+  unsigned char buf[2];
+  Write2(buf, val);
+  return write_buffer(dest, buf, 2);
+}
+
+int Glulxe::write_byte(dest_t *dest, unsigned char val) {
+  return write_buffer(dest, &val, 1);
+}
+
+int Glulxe::read_long(dest_t *dest, uint *val) {
+  unsigned char buf[4];
+  int res = read_buffer(dest, buf, 4);
+  if (res)
+    return res;
+  *val = Read4(buf);
+  return 0;
+}
+
+int Glulxe::read_short(dest_t *dest, uint16 *val) {
+  unsigned char buf[2];
+  int res = read_buffer(dest, buf, 2);
+  if (res)
+    return res;
+  *val = Read2(buf);
+  return 0;
+}
+
+int Glulxe::read_byte(dest_t *dest, byte *val) {
+  return read_buffer(dest, val, 1);
+}
+
+uint Glulxe::write_memstate(dest_t *dest) {
+  uint res, pos;
+  int val;
+  int runlen;
+  unsigned char ch;
+#ifdef SERIALIZE_CACHE_RAM
+  uint cachepos;
+#endif /* SERIALIZE_CACHE_RAM */
+
+  res = write_long(dest, endmem);
+  if (res)
+    return res;
+
+  runlen = 0;
+
+#ifdef SERIALIZE_CACHE_RAM
+  cachepos = 0;
+#else /* SERIALIZE_CACHE_RAM */
+  glk_stream_set_position(gamefile, gamefile_start+ramstart, seekmode_Start);
+#endif /* SERIALIZE_CACHE_RAM */
+
+  for (pos=ramstart; pos<endmem; pos++) {
+    ch = Mem1(pos);
+    if (pos < endgamefile) {
+#ifdef SERIALIZE_CACHE_RAM
+      val = ramcache[cachepos];
+      cachepos++;
+#else /* SERIALIZE_CACHE_RAM */
+      val = glk_get_char_stream(gamefile);
+      if (val == -1) {
+        fatal_error("The game file ended unexpectedly while saving.");
+      }
+#endif /* SERIALIZE_CACHE_RAM */
+      ch ^= (unsigned char)val;
+    }
+    if (ch == 0) {
+      runlen++;
+    }
+    else {
+      /* Write any run we've got. */
+      while (runlen) {
+        if (runlen >= 0x100)
+          val = 0x100;
+        else
+          val = runlen;
+        res = write_byte(dest, 0);
+        if (res)
+          return res;
+        res = write_byte(dest, (val-1));
+        if (res)
+          return res;
+        runlen -= val;
+      }
+      /* Write the byte we got. */
+      res = write_byte(dest, ch);
+      if (res)
+        return res;
+    }
+  }
+  /* It's possible we've got a run left over, but we don't write it. */
+
+  return 0;
+}
+
+uint Glulxe::read_memstate(dest_t *dest, uint chunklen) {
+  uint chunkend = dest->pos + chunklen;
+  uint newlen;
+  uint res, pos;
+  int val;
+  int runlen;
+  unsigned char ch, ch2;
+#ifdef SERIALIZE_CACHE_RAM
+  uint cachepos;
+#endif /* SERIALIZE_CACHE_RAM */
+
+  heap_clear();
+
+  res = read_long(dest, &newlen);
+  if (res)
+    return res;
+
+  res = change_memsize(newlen, false);
+  if (res)
+    return res;
+
+  runlen = 0;
+
+#ifdef SERIALIZE_CACHE_RAM
+  cachepos = 0;
+#else /* SERIALIZE_CACHE_RAM */
+  glk_stream_set_position(gamefile, gamefile_start+ramstart, seekmode_Start);
+#endif /* SERIALIZE_CACHE_RAM */
+
+  for (pos=ramstart; pos<endmem; pos++) {
+    if (pos < endgamefile) {
+#ifdef SERIALIZE_CACHE_RAM
+      val = ramcache[cachepos];
+      cachepos++;
+#else /* SERIALIZE_CACHE_RAM */
+      val = glk_get_char_stream(gamefile);
+      if (val == -1) {
+        fatal_error("The game file ended unexpectedly while restoring.");
+      }
+#endif /* SERIALIZE_CACHE_RAM */
+      ch = (unsigned char)val;
+    }
+    else {
+      ch = 0;
+    }
+
+    if (dest->pos >= chunkend) {
+      /* we're into the final, unstored run. */
+    }
+    else if (runlen) {
+      runlen--;
+    }
+    else {
+      res = read_byte(dest, &ch2);
+      if (res)
+        return res;
+      if (ch2 == 0) {
+        res = read_byte(dest, &ch2);
+        if (res)
+          return res;
+        runlen = (uint)ch2;
+      }
+      else {
+        ch ^= ch2;
+      }
+    }
+
+    if (pos >= protectstart && pos < protectend)
+      continue;
+
+    MemW1(pos, ch);
+  }
+
+  return 0;
+}
+
+uint Glulxe::write_heapstate(dest_t *dest, int portable) {
+  uint res;
+  uint sumlen;
+  uint *sumarray;
+
+  res = heap_get_summary(&sumlen, &sumarray);
+  if (res)
+    return res;
+
+  if (!sumarray)
+    return 0; /* no heap */
+
+  res = write_heapstate_sub(sumlen, sumarray, dest, portable);
+
+  glulx_free(sumarray);
+  return res;
+}
+
+uint Glulxe::write_heapstate_sub(uint sumlen, uint *sumarray, dest_t *dest, int portable)  {
+  uint res, lx;
+
+  /* If we're storing for the purpose of undo, we don't need to do any
+     byte-swapping, because the result will only be used by this session. */
+  if (!portable) {
+    res = write_buffer(dest, (const byte *)sumarray, sumlen * sizeof(uint));
+    if (res)
+      return res;
+    return 0;
+  }
+
+  for (lx=0; lx<sumlen; lx++) {
+    res = write_long(dest, sumarray[lx]);
+    if (res)
+      return res;
+  }
+
+  return 0;
+}
+
+int Glulxe::sort_heap_summary(void *p1, void *p2) {
+  uint v1 = *(uint *)p1;
+  uint v2 = *(uint *)p2;
+
+  if (v1 < v2)
+    return -1;
+  if (v1 > v2)
+    return 1;
+  return 0;
+}
+
+uint Glulxe::read_heapstate(dest_t *dest, uint chunklen, int portable, uint *sumlen, uint **summary) {
+  uint res, count, lx;
+  uint *arr;
+
+  *sumlen = 0;
+  *summary = NULL;
+
+  if (chunklen == 0)
+    return 0; /* no heap */
+
+  if (!portable) {
+    count = chunklen / sizeof(uint);
+
+    arr = (uint *)glulx_malloc(chunklen);
+    if (!arr)
+      return 1;
+
+    res = read_buffer(dest, (byte *)arr, chunklen);
+    if (res)
+      return res;
+
+    *sumlen = count;
+    *summary = arr;
+
+    return 0;
+  }
+
+  count = chunklen / 4;
+
+  arr = (uint *)glulx_malloc(count * sizeof(uint));
+  if (!arr)
+    return 1;
+  
+  for (lx=0; lx<count; lx++) {
+    res = read_long(dest, arr+lx);
+    if (res)
+      return res;
+  }
+
+  *sumlen = count;
+  *summary = arr;
+
+  return 0;
+}
+
+uint Glulxe::write_stackstate(dest_t *dest, int portable) {
+  uint res;
+  uint lx;
+  uint lastframe;
+
+  /* If we're storing for the purpose of undo, we don't need to do any
+     byte-swapping, because the result will only be used by this session. */
+  if (!portable) {
+    res = write_buffer(dest, stack, stackptr);
+    if (res)
+      return res;
+    return 0;
+  }
+
+  /* Write a portable stack image. To do this, we have to write stack
+     frames in order, bottom to top. Remember that the last word of
+     every stack frame is a pointer to the beginning of that stack frame.
+     (This includes the last frame, because the save opcode pushes on
+     a call stub before it calls perform_save().) */
+
+  lastframe = (uint)(-1);
+  while (1) {
+    uint frameend, frm, frm2, frm3;
+    unsigned char loctype, loccount;
+    uint numlocals, frlen, locpos;
+
+    /* Find the next stack frame (after the one in lastframe). Sadly,
+       this requires searching the stack from the top down. We have to
+       do this for *every* frame, which takes N^2 time overall. But
+       save routines usually aren't nested very deep. 
+       If it becomes a practical problem, we can build a stack-frame 
+       array, which requires dynamic allocation. */
+    for (frm = stackptr, frameend = stackptr;
+         frm != 0 && (frm2 = Stk4(frm-4)) != lastframe;
+         frameend = frm, frm = frm2) { };
+
+    /* Write out the frame. */
+    frm2 = frm;
+
+    frlen = Stk4(frm2);
+    frm2 += 4;
+    res = write_long(dest, frlen);
+    if (res)
+      return res;
+    locpos = Stk4(frm2);
+    frm2 += 4;
+    res = write_long(dest, locpos);
+    if (res)
+      return res;
+
+    frm3 = frm2;
+
+    numlocals = 0;
+    while (1) {
+      loctype = Stk1(frm2);
+      frm2 += 1;
+      loccount = Stk1(frm2);
+      frm2 += 1;
+
+      res = write_byte(dest, loctype);
+      if (res)
+        return res;
+      res = write_byte(dest, loccount);
+      if (res)
+        return res;
+
+      if (loctype == 0 && loccount == 0)
+        break;
+
+      numlocals++;
+    }
+
+    if ((numlocals & 1) == 0) {
+      res = write_byte(dest, 0);
+      if (res)
+        return res;
+      res = write_byte(dest, 0);
+      if (res)
+        return res;
+      frm2 += 2;
+    }
+
+    if (frm2 != frm+locpos)
+      fatal_error("Inconsistent stack frame during save.");
+
+    /* Write out the locals. */
+    for (lx=0; lx<numlocals; lx++) {
+      loctype = Stk1(frm3);
+      frm3 += 1;
+      loccount = Stk1(frm3);
+      frm3 += 1;
+      
+      if (loctype == 0 && loccount == 0)
+        break;
+
+      /* Put in up to 0, 1, or 3 bytes of padding, depending on loctype. */
+      while (frm2 & (loctype-1)) {
+        res = write_byte(dest, 0);
+        if (res)
+          return res;
+        frm2 += 1;
+      }
+
+      /* Put in this set of locals. */
+      switch (loctype) {
+
+      case 1:
+        do {
+          res = write_byte(dest, Stk1(frm2));
+          if (res)
+            return res;
+          frm2 += 1;
+          loccount--;
+        } while (loccount);
+        break;
+
+      case 2:
+        do {
+          res = write_short(dest, Stk2(frm2));
+          if (res)
+            return res;
+          frm2 += 2;
+          loccount--;
+        } while (loccount);
+        break;
+
+      case 4:
+        do {
+          res = write_long(dest, Stk4(frm2));
+          if (res)
+            return res;
+          frm2 += 4;
+          loccount--;
+        } while (loccount);
+        break;
+
+      }
+    }
+
+    if (frm2 != frm+frlen)
+      fatal_error("Inconsistent stack frame during save.");
+
+    while (frm2 < frameend) {
+      res = write_long(dest, Stk4(frm2));
+      if (res)
+        return res;
+      frm2 += 4;
+    }
+
+    /* Go on to the next frame. */
+    if (frameend == stackptr)
+      break; /* All done. */
+    lastframe = frm;
+  }
+
+  return 0;
+}
+
+uint Glulxe::read_stackstate(dest_t *dest, uint chunklen, int portable) {
+  uint res;
+  uint frameend, frm, frm2, frm3, locpos, frlen, numlocals;
+
+  if (chunklen > stacksize)
+    return 1;
+
+  stackptr = chunklen;
+  frameptr = 0;
+  valstackbase = 0;
+  localsbase = 0;
+
+  if (!portable) {
+    res = read_buffer(dest, stack, stackptr);
+    if (res)
+      return res;
+    return 0;
+  }
+
+  /* This isn't going to be pleasant; we're going to read the data in
+     as a block, and then convert it in-place. */
+  res = read_buffer(dest, stack, stackptr);
+  if (res)
+    return res;
+
+  frameend = stackptr;
+  while (frameend != 0) {
+    /* Read the beginning-of-frame pointer. Remember, right now, the
+       whole frame is stored big-endian. So we have to read with the
+       Read*() macros, and then write with the StkW*() macros. */
+    frm = Read4(stack+(frameend-4));
+
+    frm2 = frm;
+
+    frlen = Read4(stack+frm2);
+    StkW4(frm2, frlen);
+    frm2 += 4;
+    locpos = Read4(stack+frm2);
+    StkW4(frm2, locpos);
+    frm2 += 4;
+
+    /* The locals-format list is in bytes, so we don't have to convert it. */
+    frm3 = frm2;
+    frm2 = frm+locpos;
+
+    numlocals = 0;
+
+    while (1) {
+      unsigned char loctype, loccount;
+      loctype = Read1(stack+frm3);
+      frm3 += 1;
+      loccount = Read1(stack+frm3);
+      frm3 += 1;
+
+      if (loctype == 0 && loccount == 0)
+        break;
+
+      /* Skip up to 0, 1, or 3 bytes of padding, depending on loctype. */
+      while (frm2 & (loctype-1)) {
+        StkW1(frm2, 0);
+        frm2++;
+      }
+      
+      /* Convert this set of locals. */
+      switch (loctype) {
+        
+      case 1:
+        do {
+          /* Don't need to convert bytes. */
+          frm2 += 1;
+          loccount--;
+        } while (loccount);
+        break;
+
+      case 2:
+        do {
+          uint16 loc = Read2(stack+frm2);
+          StkW2(frm2, loc);
+          frm2 += 2;
+          loccount--;
+        } while (loccount);
+        break;
+
+      case 4:
+        do {
+          uint loc = Read4(stack+frm2);
+          StkW4(frm2, loc);
+          frm2 += 4;
+          loccount--;
+        } while (loccount);
+        break;
+
+      }
+
+      numlocals++;
+    }
+
+    if ((numlocals & 1) == 0) {
+      StkW1(frm3, 0);
+      frm3++;
+      StkW1(frm3, 0);
+      frm3++;
+    }
+
+    if (frm3 != frm+locpos) {
+      return 1;
+    }
+
+    while (frm2 & 3) {
+      StkW1(frm2, 0);
+      frm2++;
+    }
+
+    if (frm2 != frm+frlen) {
+      return 1;
+    }
+
+    /* Now, the values pushed on the stack after the call frame itself.
+       This includes the stub. */
+    while (frm2 < frameend) {
+      uint loc = Read4(stack+frm2);
+      StkW4(frm2, loc);
+      frm2 += 4;
+    }
+
+    frameend = frm;
+  }
+
+  return 0;
+}
+
+uint Glulxe::perform_verify() {
+  uint len, chksum = 0, newlen;
+  unsigned char buf[4];
+  uint val, newsum, ix;
+
+  len = gamefile_len;
+
+  if (len < 256 || (len & 0xFF) != 0)
+    return 1;
+
+  glk_stream_set_position(gamefile, gamefile_start, seekmode_Start);
+  newsum = 0;
+
+  /* Read the header */
+  for (ix=0; ix<9; ix++) {
+    newlen = glk_get_buffer_stream(gamefile, (char *)buf, 4);
+    if (newlen != 4)
+      return 1;
+    val = Read4(buf);
+    if (ix == 3) {
+      if (len != val)
+        return 1;
+    }
+    if (ix == 8)
+      chksum = val;
+    else
+      newsum += val;
+  }
+
+  /* Read everything else */
+  for (; ix < len/4; ix++) {
+    newlen = glk_get_buffer_stream(gamefile, (char *)buf, 4);
+    if (newlen != 4)
+      return 1;
+    val = Read4(buf);
+    newsum += val;
+  }
+
+  if (newsum != chksum)
+    return 1;
+
+  return 0;  
+}
+
+} // End of namespace Glulxe
+} // End of namespace Glk
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index e09a63e..6cd298e3 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -68,6 +68,7 @@ MODULE_OBJS := \
 	glulxe/heap.o \
 	glulxe/operand.o \
 	glulxe/search.o \
+	glulxe/serial.o \
 	magnetic/detection.o \
 	magnetic/magnetic.o \
 	scott/detection.o \


Commit: 5965b02bcac9dbad6eb2c9b30a59edba0a3b1b5d
    https://github.com/scummvm/scummvm/commit/5965b02bcac9dbad6eb2c9b30a59edba0a3b1b5d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-04-17T20:46:06-07:00

Commit Message:
GLK: GLULXE: Add string methods

Changed paths:
  A engines/glk/glulxe/string.cpp
    engines/glk/glk_types.h
    engines/glk/glulxe/exec.cpp
    engines/glk/glulxe/glulxe.cpp
    engines/glk/glulxe/glulxe.h
    engines/glk/glulxe/glulxe_types.h
    engines/glk/module.mk


diff --git a/engines/glk/glk_types.h b/engines/glk/glk_types.h
index 7095334..84d3e15 100644
--- a/engines/glk/glk_types.h
+++ b/engines/glk/glk_types.h
@@ -230,7 +230,7 @@ union gluniversal_union {
 	byte _uch;           ///< Cu
 	int8 _sch;           ///< Cs
 	char _ch;            ///< Cn
-	const char *_charstr; ///< S
+	char *_charstr;		 ///< S
 	uint32 *_unicharstr; ///< U
 	void *_array;        ///< all # arguments
 	uint32 _ptrflag;     ///< [ ... ] or *?
diff --git a/engines/glk/glulxe/exec.cpp b/engines/glk/glulxe/exec.cpp
index 766e8b6..4fefe05 100644
--- a/engines/glk/glulxe/exec.cpp
+++ b/engines/glk/glulxe/exec.cpp
@@ -543,13 +543,13 @@ void Glulxe::execute_loop() {
       case op_streamchar:
         profile_in(0xE0000001, stackptr, false);
         value = inst[0].value & 0xFF;
-        (*stream_char_handler)(value);
+        (this->*stream_char_handler)(value);
         profile_out(stackptr);
         break;
       case op_streamunichar:
         profile_in(0xE0000002, stackptr, false);
         value = inst[0].value;
-        (*stream_unichar_handler)(value);
+        (this->*stream_unichar_handler)(value);
         profile_out(stackptr);
         break;
       case op_streamnum:
diff --git a/engines/glk/glulxe/glulxe.cpp b/engines/glk/glulxe/glulxe.cpp
index 9db784c..576b961 100644
--- a/engines/glk/glulxe/glulxe.cpp
+++ b/engines/glk/glulxe/glulxe.cpp
@@ -42,7 +42,9 @@ Glulxe::Glulxe(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst,
 		// heap
 		heap_start(0), alloc_count(0), heap_head(nullptr), heap_tail(nullptr),
 		// serial
-		max_undo_level(8), undo_chain_size(0), undo_chain_num(0), undo_chain(nullptr), ramcache(nullptr) {
+		max_undo_level(8), undo_chain_size(0), undo_chain_num(0), undo_chain(nullptr), ramcache(nullptr),
+		// string
+		iosys_mode(0), iosys_rock(0), tablecache_valid(false), glkio_unichar_han_ptr(nullptr) {
 	g_vm = this;
 }
 
diff --git a/engines/glk/glulxe/glulxe.h b/engines/glk/glulxe/glulxe.h
index 8402a1b..4044074 100644
--- a/engines/glk/glulxe/glulxe.h
+++ b/engines/glk/glulxe/glulxe.h
@@ -30,11 +30,18 @@
 namespace Glk {
 namespace Glulxe {
 
+class Glulxe;
+typedef void (Glulxe::*CharHandler)(unsigned char);
+typedef void (Glulxe::*UnicharHandler)(uint);
+
 /**
  * Glulxe game interpreter
  */
 class Glulxe : public GlkAPI {
 public:
+	CharHandler stream_char_handler;
+	UnicharHandler stream_unichar_handler, glkio_unichar_han_ptr;
+
 	bool vm_exited_cleanly;
 	strid_t gamefile;
 	uint gamefile_start, gamefile_len;
@@ -60,9 +67,6 @@ public:
 	uint protectstart, protectend;
 	uint prevpc;
 
-	void (*Glulxe::stream_char_handler)(unsigned char ch);
-	void (*Glulxe::stream_unichar_handler)(uint ch);
-
 	/**
 	 * \defgroup accel fields
 	 * @{
@@ -143,6 +147,26 @@ public:
 
 	/**@}*/
 
+	/**
+	 * \defgroup string fields
+	 * @{
+	 */
+
+	uint iosys_mode;
+	uint iosys_rock;
+
+	/**
+	 * The current string-decoding tables, broken out into a fast and easy-to-use form.
+	 */
+	bool tablecache_valid;
+	cacheblock_t tablecache;
+
+	/* This misbehaves if a Glk function has more than one S argument. */
+	#define STATIC_TEMP_BUFSIZE (127)
+	char temp_buf[STATIC_TEMP_BUFSIZE + 1];
+
+	/**@}*/
+
 protected:
 	/**
 	 * \defgroup glkop fields
@@ -312,6 +336,25 @@ protected:
 	int write_buffer(dest_t *dest, const byte *ptr, uint len);
 
 	/**@}*/
+
+	/**
+	 * \defgroup string support methods
+	 * @{
+	 */
+
+	void stream_setup_unichar(void);
+
+	void nopio_char_han(unsigned char ch);
+	void filio_char_han(unsigned char ch);
+	void nopio_unichar_han(uint ch);
+	void filio_unichar_han(uint ch);
+	void glkio_unichar_nouni_han(uint val);
+
+	void dropcache(cacheblock_t *cablist);
+	void buildcache(cacheblock_t *cablist, uint nodeaddr, int depth, int mask);
+	void dumpcache(cacheblock_t *cablist, int count, int indent);
+
+	/**@}*/
 public:
 	/**
 	 * Constructor
@@ -476,24 +519,6 @@ public:
 	/**@}*/
 
 	/**
-	 * \defgroup Strings access methods
-	 * @{
-	 */
-
-	void stream_num(int val, int inmiddle, int charnum);
-	void stream_string(uint addr, int inmiddle, int bitnum);
-	uint stream_get_table(void);
-	void stream_set_table(uint addr);
-	void stream_get_iosys(uint *mode, uint *rock);
-	void stream_set_iosys(uint mode, uint rock);
-	char *make_temp_string(uint addr);
-	uint *make_temp_ustring(uint addr);
-	void free_temp_string(const char *str);
-	void free_temp_ustring(const uint *str);
-
-	/**@}*/
-
-	/**
 	 * \defgroup Heap access methods
 	 * @{
 	 */
@@ -838,6 +863,42 @@ public:
 	uint perform_verify();
 
 	/**@}*/
+
+
+	/**
+	 * \defgroup Strings access methods
+	 * @{
+	 */
+
+	 /**
+	  * Write a signed integer to the current output stream.
+	  */
+	void stream_num(int val, int inmiddle, int charnum);
+
+	/**
+	 * Write a Glulx string object to the current output stream. inmiddle is zero if we are beginning
+	 * a new string, or nonzero if restarting one (E0/E1/E2, as appropriate for the string type).
+	 */
+	void stream_string(uint addr, int inmiddle, int bitnum);
+
+	/**
+	 * Get the current table address.
+	 */
+	uint stream_get_table(void);
+
+	/**
+	 * Set the current table address, and rebuild decoding cache.
+	 */
+	void stream_set_table(uint addr);
+
+	void stream_get_iosys(uint *mode, uint *rock);
+	void stream_set_iosys(uint mode, uint rock);
+	char *make_temp_string(uint addr);
+	uint *make_temp_ustring(uint addr);
+	void free_temp_string(char *str);
+	void free_temp_ustring(uint *str);
+
+	/**@}*/
 };
 
 extern Glulxe *g_vm;
diff --git a/engines/glk/glulxe/glulxe_types.h b/engines/glk/glulxe/glulxe_types.h
index 5b14a6a..9200ec2 100644
--- a/engines/glk/glulxe/glulxe_types.h
+++ b/engines/glk/glulxe/glulxe_types.h
@@ -379,6 +379,31 @@ struct dest_struct {
 };
 typedef dest_struct dest_t;
 
+/**
+ * These constants are defined in the Glulx spec.
+ */
+enum iosys {
+	iosys_None   = 0,
+	iosys_Filter = 1,
+	iosys_Glk    = 2
+};
+
+#define CACHEBITS (4)
+#define CACHESIZE (1 << CACHEBITS) 
+#define CACHEMASK (15)
+
+struct cacheblock_struct {
+	int depth; /* 1 to 4 */
+	int type;
+	union {
+		struct cacheblock_struct *branches;
+		unsigned char ch;
+		uint uch;
+		uint addr;
+	} u;
+};
+typedef cacheblock_struct cacheblock_t;
+
 } // End of namespace Glulxe
 } // End of namespace Glk
 
diff --git a/engines/glk/glulxe/string.cpp b/engines/glk/glulxe/string.cpp
new file mode 100644
index 0000000..2c95c3e
--- /dev/null
+++ b/engines/glk/glulxe/string.cpp
@@ -0,0 +1,828 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/glk/glulxe/glulxe.h"
+
+namespace Glk {
+namespace Glulxe {
+
+void Glulxe::stream_get_iosys(uint *mode, uint *rock) {
+  *mode = iosys_mode;
+  *rock = iosys_rock;
+}
+
+void Glulxe::stream_setup_unichar() {
+#ifdef GLK_MODULE_UNICODE
+
+  if (glk_gestalt(gestalt_Unicode, 0))
+    glkio_unichar_han_ptr = &Glulxe::glk_put_char_uni;
+  else
+    glkio_unichar_han_ptr = &Glulxe::glkio_unichar_nouni_han;
+
+#else /* GLK_MODULE_UNICODE */
+
+  glkio_unichar_han_ptr = glkio_unichar_nouni_han;
+
+#endif /* GLK_MODULE_UNICODE */
+}
+
+void Glulxe::stream_set_iosys(uint mode, uint rock) {
+  switch (mode) {
+  default:
+    mode = 0;
+    /* ...and fall through to next case (no-op I/O). */
+  case iosys_None:
+    rock = 0;
+    stream_char_handler = &Glulxe::nopio_char_han;
+    stream_unichar_handler = &Glulxe::nopio_unichar_han;
+    break;
+  case iosys_Filter:
+    stream_char_handler = &Glulxe::filio_char_han;
+    stream_unichar_handler = &Glulxe::filio_unichar_han;
+    break;
+  case iosys_Glk:
+    if (!glkio_unichar_han_ptr)
+      stream_setup_unichar();
+    rock = 0;
+    stream_char_handler = &Glulxe::glk_put_char;
+    stream_unichar_handler = glkio_unichar_han_ptr;
+    break;
+  }
+
+  iosys_mode = mode;
+  iosys_rock = rock;
+}
+
+void Glulxe::nopio_char_han(unsigned char ch) {
+}
+
+void Glulxe::nopio_unichar_han(uint ch) {
+}
+
+void Glulxe::filio_char_han(unsigned char ch) {
+  uint val = ch;
+  push_callstub(0, 0);
+  enter_function(iosys_rock, 1, &val);
+}
+
+void Glulxe::filio_unichar_han(uint val) {
+  push_callstub(0, 0);
+  enter_function(iosys_rock, 1, &val);
+}
+
+void Glulxe::glkio_unichar_nouni_han(uint val) {
+  /* Only used if the Glk library has no Unicode functions */
+  if (val > 0xFF)
+    val = '?';
+  glk_put_char(val);
+}
+
+void Glulxe::stream_num(int val, int inmiddle, int charnum) {
+  int ix = 0;
+  int res, jx;
+  char buf[16];
+  uint ival;
+
+  if (val == 0) {
+    buf[ix] = '0';
+    ix++;
+  }
+  else {
+    if (val < 0) 
+      ival = -val;
+    else 
+      ival = val;
+
+    while (ival != 0) {
+      buf[ix] = (ival % 10) + '0';
+      ix++;
+      ival /= 10;
+    }
+
+    if (val < 0) {
+      buf[ix] = '-';
+      ix++;
+    }
+  }
+
+  switch (iosys_mode) {
+
+  case iosys_Glk:
+    ix -= charnum;
+    while (ix > 0) {
+      ix--;
+      glk_put_char(buf[ix]);
+    }
+    break;
+
+  case iosys_Filter:
+    if (!inmiddle) {
+      push_callstub(0x11, 0);
+      inmiddle = true;
+    }
+    if (charnum < ix) {
+      ival = buf[(ix-1)-charnum] & 0xFF;
+      pc = val;
+      push_callstub(0x12, charnum+1);
+      enter_function(iosys_rock, 1, &ival);
+      return;
+    }
+    break;
+
+  default:
+    break;
+
+  }
+
+  if (inmiddle) {
+    res = pop_callstub_string(&jx);
+    if (res) 
+      fatal_error("String-on-string call stub while printing number.");
+  }
+}
+
+void Glulxe::stream_string(uint addr, int inmiddle, int bitnum) {
+  int ch;
+  int type;
+  int alldone = false;
+  int substring = (inmiddle != 0);
+  uint ival;
+
+  if (!addr)
+    fatal_error("Called stream_string with null address.");
+  
+  while (!alldone) {
+
+    if (inmiddle == 0) {
+      type = Mem1(addr);
+      if (type == 0xE2)
+        addr+=4;
+      else
+        addr++;
+      bitnum = 0;
+    }
+    else {
+      type = inmiddle;
+    }
+
+    if (type == 0xE1) {
+      if (tablecache_valid) {
+        int bits, numbits;
+        int readahead;
+        uint tmpaddr;
+        cacheblock_t *cablist;
+        int done = 0;
+
+        /* bitnum is already set right */
+        bits = Mem1(addr); 
+        if (bitnum)
+          bits >>= bitnum;
+        numbits = (8 - bitnum);
+        readahead = false;
+
+        if (tablecache.type != 0) {
+          /* This is a bit of a cheat. If the top-level block is not
+             a branch, then it must be a string-terminator -- otherwise
+             the string would be an infinite repetition of that block.
+             We check for this case and bail immediately. */
+          done = 1;
+        }
+
+        cablist = tablecache.u.branches;
+        while (!done) {
+          cacheblock_t *cab;
+
+          if (numbits < CACHEBITS) {
+            /* readahead is certainly false */
+            int newbyte = Mem1(addr+1);
+            bits |= (newbyte << numbits);
+            numbits += 8;
+            readahead = true;
+          }
+
+          cab = &(cablist[bits & CACHEMASK]);
+          numbits -= cab->depth;
+          bits >>= cab->depth;
+          bitnum += cab->depth;
+          if (bitnum >= 8) {
+            addr += 1;
+            bitnum -= 8;
+            if (readahead) {
+              readahead = false;
+            }
+            else {
+              int newbyte = Mem1(addr);
+              bits |= (newbyte << numbits);
+              numbits += 8;
+            }
+          }
+
+          switch (cab->type) {
+          case 0x00: /* non-leaf node */
+            cablist = cab->u.branches;
+            break;
+          case 0x01: /* string terminator */
+            done = 1;
+            break;
+          case 0x02: /* single character */
+            switch (iosys_mode) {
+            case iosys_Glk:
+              glk_put_char(cab->u.ch);
+              break;
+            case iosys_Filter: 
+              ival = cab->u.ch & 0xFF;
+              if (!substring) {
+                push_callstub(0x11, 0);
+                substring = true;
+              }
+              pc = addr;
+              push_callstub(0x10, bitnum);
+              enter_function(iosys_rock, 1, &ival);
+              return;
+            }
+            cablist = tablecache.u.branches;
+            break;
+          case 0x04: /* single Unicode character */
+            switch (iosys_mode) {
+            case iosys_Glk:
+              (this->*glkio_unichar_han_ptr)(cab->u.uch);
+              break;
+            case iosys_Filter: 
+              ival = cab->u.uch;
+              if (!substring) {
+                push_callstub(0x11, 0);
+                substring = true;
+              }
+              pc = addr;
+              push_callstub(0x10, bitnum);
+              enter_function(iosys_rock, 1, &ival);
+              return;
+            }
+            cablist = tablecache.u.branches;
+            break;
+          case 0x03: /* C string */
+            switch (iosys_mode) {
+            case iosys_Glk:
+              for (tmpaddr=cab->u.addr; (ch=Mem1(tmpaddr)) != '\0'; tmpaddr++) 
+                glk_put_char(ch);
+              cablist = tablecache.u.branches; 
+              break;
+            case iosys_Filter:
+              if (!substring) {
+                push_callstub(0x11, 0);
+                substring = true;
+              }
+              pc = addr;
+              push_callstub(0x10, bitnum);
+              inmiddle = 0xE0;
+              addr = cab->u.addr;
+              done = 2;
+              break;
+            default:
+              cablist = tablecache.u.branches; 
+              break;
+            }
+            break;
+          case 0x05: /* C Unicode string */
+            switch (iosys_mode) {
+            case iosys_Glk:
+              for (tmpaddr=cab->u.addr; (ival=Mem4(tmpaddr)) != 0; tmpaddr+=4) 
+                (this->*glkio_unichar_han_ptr)(ival);
+              cablist = tablecache.u.branches; 
+              break;
+            case iosys_Filter:
+              if (!substring) {
+                push_callstub(0x11, 0);
+                substring = true;
+              }
+              pc = addr;
+              push_callstub(0x10, bitnum);
+              inmiddle = 0xE2;
+              addr = cab->u.addr;
+              done = 2;
+              break;
+            default:
+              cablist = tablecache.u.branches; 
+              break;
+            }
+            break;
+          case 0x08:
+          case 0x09:
+          case 0x0A:
+          case 0x0B: 
+            {
+              uint oaddr;
+              int otype;
+              oaddr = cab->u.addr;
+              if (cab->type >= 0x09)
+                oaddr = Mem4(oaddr);
+              if (cab->type == 0x0B)
+                oaddr = Mem4(oaddr);
+              otype = Mem1(oaddr);
+              if (!substring) {
+                push_callstub(0x11, 0);
+                substring = true;
+              }
+              if (otype >= 0xE0 && otype <= 0xFF) {
+                pc = addr;
+                push_callstub(0x10, bitnum);
+                inmiddle = 0;
+                addr = oaddr;
+                done = 2;
+              }
+              else if (otype >= 0xC0 && otype <= 0xDF) {
+                uint argc;
+                uint *argv;
+                if (cab->type == 0x0A || cab->type == 0x0B) {
+                  argc = Mem4(cab->u.addr+4);
+                  argv = pop_arguments(argc, cab->u.addr+8);
+                }
+                else {
+                  argc = 0;
+                  argv = NULL;
+                }
+                pc = addr;
+                push_callstub(0x10, bitnum);
+                enter_function(oaddr, argc, argv);
+                return;
+              }
+              else {
+                fatal_error("Unknown object while decoding string indirect reference.");
+              }
+            }
+            break;
+          default:
+            fatal_error("Unknown entity in string decoding (cached).");
+            break;
+          }
+        }
+        if (done > 1) {
+          continue; /* restart the top-level loop */
+        }
+      }
+      else { /* tablecache not valid */
+        uint node;
+        int byte1;
+        int nodetype;
+        int done = 0;
+
+        if (!stringtable)
+          fatal_error("Attempted to print a compressed string with no table set.");
+        /* bitnum is already set right */
+        byte1 = Mem1(addr); 
+        if (bitnum)
+          byte1 >>= bitnum;
+        node = Mem4(stringtable+8);
+        while (!done) {
+          nodetype = Mem1(node);
+          node++;
+          switch (nodetype) {
+          case 0x00: /* non-leaf node */
+            if (byte1 & 1) 
+              node = Mem4(node+4);
+            else
+              node = Mem4(node+0);
+            if (bitnum == 7) {
+              bitnum = 0;
+              addr++;
+              byte1 = Mem1(addr);
+            }
+            else {
+              bitnum++;
+              byte1 >>= 1;
+            }
+            break;
+          case 0x01: /* string terminator */
+            done = 1;
+            break;
+          case 0x02: /* single character */
+            ch = Mem1(node);
+            switch (iosys_mode) {
+            case iosys_Glk:
+              glk_put_char(ch);
+              break;
+            case iosys_Filter: 
+              ival = ch & 0xFF;
+              if (!substring) {
+                push_callstub(0x11, 0);
+                substring = true;
+              }
+              pc = addr;
+              push_callstub(0x10, bitnum);
+              enter_function(iosys_rock, 1, &ival);
+              return;
+            }
+            node = Mem4(stringtable+8);
+            break;
+          case 0x04: /* single Unicode character */
+            ival = Mem4(node);
+            switch (iosys_mode) {
+            case iosys_Glk:
+              (this->*glkio_unichar_han_ptr)(ival);
+              break;
+            case iosys_Filter: 
+              if (!substring) {
+                push_callstub(0x11, 0);
+                substring = true;
+              }
+              pc = addr;
+              push_callstub(0x10, bitnum);
+              enter_function(iosys_rock, 1, &ival);
+              return;
+            }
+            node = Mem4(stringtable+8);
+            break;
+          case 0x03: /* C string */
+            switch (iosys_mode) {
+            case iosys_Glk:
+              for (; (ch=Mem1(node)) != '\0'; node++) 
+                glk_put_char(ch);
+              node = Mem4(stringtable+8);
+              break;
+            case iosys_Filter:
+              if (!substring) {
+                push_callstub(0x11, 0);
+                substring = true;
+              }
+              pc = addr;
+              push_callstub(0x10, bitnum);
+              inmiddle = 0xE0;
+              addr = node;
+              done = 2;
+              break;
+            default:
+              node = Mem4(stringtable+8);
+              break;
+            }
+            break;
+          case 0x05: /* C Unicode string */
+            switch (iosys_mode) {
+            case iosys_Glk:
+              for (; (ival=Mem4(node)) != 0; node+=4) 
+                (this->*glkio_unichar_han_ptr)(ival);
+              node = Mem4(stringtable+8);
+              break;
+            case iosys_Filter:
+              if (!substring) {
+                push_callstub(0x11, 0);
+                substring = true;
+              }
+              pc = addr;
+              push_callstub(0x10, bitnum);
+              inmiddle = 0xE2;
+              addr = node;
+              done = 2;
+              break;
+            default:
+              node = Mem4(stringtable+8);
+              break;
+            }
+            break;
+          case 0x08:
+          case 0x09:
+          case 0x0A:
+          case 0x0B: 
+            {
+              uint oaddr;
+              int otype;
+              oaddr = Mem4(node);
+              if (nodetype == 0x09 || nodetype == 0x0B)
+                oaddr = Mem4(oaddr);
+              otype = Mem1(oaddr);
+              if (!substring) {
+                push_callstub(0x11, 0);
+                substring = true;
+              }
+              if (otype >= 0xE0 && otype <= 0xFF) {
+                pc = addr;
+                push_callstub(0x10, bitnum);
+                inmiddle = 0;
+                addr = oaddr;
+                done = 2;
+              }
+              else if (otype >= 0xC0 && otype <= 0xDF) {
+                uint argc;
+                uint *argv;
+                if (nodetype == 0x0A || nodetype == 0x0B) {
+                  argc = Mem4(node+4);
+                  argv = pop_arguments(argc, node+8);
+                }
+                else {
+                  argc = 0;
+                  argv = NULL;
+                }
+                pc = addr;
+                push_callstub(0x10, bitnum);
+                enter_function(oaddr, argc, argv);
+                return;
+              }
+              else {
+                fatal_error("Unknown object while decoding string indirect reference.");
+              }
+            }
+            break;
+          default:
+            fatal_error("Unknown entity in string decoding.");
+            break;
+          }
+        }
+        if (done > 1) {
+          continue; /* restart the top-level loop */
+        }
+      }
+    }
+    else if (type == 0xE0) {
+      switch (iosys_mode) {
+      case iosys_Glk:
+        while (1) {
+          ch = Mem1(addr);
+          addr++;
+          if (ch == '\0')
+            break;
+          glk_put_char(ch);
+        }
+        break;
+      case iosys_Filter:
+        if (!substring) {
+          push_callstub(0x11, 0);
+          substring = true;
+        }
+        ch = Mem1(addr);
+        addr++;
+        if (ch != '\0') {
+          ival = ch & 0xFF;
+          pc = addr;
+          push_callstub(0x13, 0);
+          enter_function(iosys_rock, 1, &ival);
+          return;
+        }
+        break;
+      }
+    }
+    else if (type == 0xE2) {
+      switch (iosys_mode) {
+      case iosys_Glk:
+        while (1) {
+          ival = Mem4(addr);
+          addr+=4;
+          if (ival == 0)
+            break;
+          (this->*glkio_unichar_han_ptr)(ival);
+        }
+        break;
+      case iosys_Filter:
+        if (!substring) {
+          push_callstub(0x11, 0);
+          substring = true;
+        }
+        ival = Mem4(addr);
+        addr+=4;
+        if (ival != 0) {
+          pc = addr;
+          push_callstub(0x14, 0);
+          enter_function(iosys_rock, 1, &ival);
+          return;
+        }
+        break;
+      }
+    }
+    else if (type >= 0xE0 && type <= 0xFF) {
+      fatal_error("Attempt to print unknown type of string.");
+    }
+    else {
+      fatal_error("Attempt to print non-string.");
+    }
+
+    if (!substring) {
+      /* Just get straight out. */
+      alldone = true;
+    }
+    else {
+      /* Pop a stub and see what's to be done. */
+      addr = pop_callstub_string(&bitnum);
+      if (addr == 0) {
+        alldone = true;
+      }
+      else {
+        inmiddle = 0xE1;
+      }
+    }
+  }
+}
+
+uint Glulxe::stream_get_table() {
+  return stringtable;
+}
+
+void Glulxe::stream_set_table(uint addr) {
+  if (stringtable == addr)
+    return;
+
+  /* Drop cache. */
+  if (tablecache_valid) {
+    if (tablecache.type == 0)
+      dropcache(tablecache.u.branches);
+    tablecache.u.branches = NULL;
+    tablecache_valid = false;
+  }
+
+  stringtable = addr;
+
+  if (stringtable) {
+    /* Build cache. We can only do this if the table is entirely in ROM. */
+    uint tablelen = Mem4(stringtable);
+    uint rootaddr = Mem4(stringtable+8);
+    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;
+    }
+  }
+}
+
+void Glulxe::buildcache(cacheblock_t *cablist, uint nodeaddr, int depth, int mask) {
+  int ix, type;
+
+  type = Mem1(nodeaddr);
+
+  if (type == 0 && depth == CACHEBITS) {
+    cacheblock_t *list, *cab;
+    list = (cacheblock_t *)glulx_malloc(sizeof(cacheblock_t) * CACHESIZE);
+    buildcache(list, nodeaddr, 0, 0);
+    cab = &(cablist[mask]);
+    cab->type = 0;
+    cab->depth = CACHEBITS;
+    cab->u.branches = list;
+    return;
+  }
+
+  if (type == 0) {
+    uint leftaddr  = Mem4(nodeaddr+1);
+    uint rightaddr = Mem4(nodeaddr+5);
+    buildcache(cablist, leftaddr, depth+1, mask);
+    buildcache(cablist, rightaddr, depth+1, (mask | (1 << depth)));
+    return;
+  }
+
+  /* Leaf node. */
+  nodeaddr++;
+  for (ix = mask; ix < CACHESIZE; ix += (1 << depth)) {
+    cacheblock_t *cab = &(cablist[ix]);
+    cab->type = type;
+    cab->depth = depth;
+    switch (type) {
+    case 0x02:
+      cab->u.ch = Mem1(nodeaddr);
+      break;
+    case 0x04:
+      cab->u.uch = Mem4(nodeaddr);
+      break;
+    case 0x03:
+    case 0x05:
+    case 0x0A:
+    case 0x0B:
+      cab->u.addr = nodeaddr;
+      break;
+    case 0x08:
+    case 0x09:
+      cab->u.addr = Mem4(nodeaddr);
+      break;
+    }
+  }
+}
+
+#if 0
+#include <stdio.h>
+void Glulxe::dumpcache(cacheblock_t *cablist, int count, int indent) {
+  int ix, jx;
+
+  for (ix=0; ix<count; ix++) {
+    cacheblock_t *cab = &(cablist[ix]); 
+    for (jx=0; jx<indent; jx++)
+      printf("  ");
+    printf("%X: ", ix);
+    switch (cab->type) {
+    case 0:
+      printf("...\n");
+      dumpcache(cab->u.branches, CACHESIZE, indent+1);
+      break;
+    case 1:
+      printf("<EOS>\n");
+      break;
+    case 2:
+      printf("0x%02X", cab->u.ch);
+      if (cab->u.ch < 32)
+        printf(" ''\n");
+      else
+        printf(" '%c'\n", cab->u.ch);
+      break;
+    default:
+      printf("type %02X, address %06lX\n", cab->type, cab->u.addr);
+      break;
+    }
+  }
+}
+#endif /* 0 */
+
+void Glulxe::dropcache(cacheblock_t *cablist) {
+  int ix;
+  for (ix=0; ix<CACHESIZE; ix++) {
+    cacheblock_t *cab = &(cablist[ix]);
+    if (cab->type == 0) {
+      dropcache(cab->u.branches);
+      cab->u.branches = NULL;
+    }
+  }
+  glulx_free(cablist);
+}
+
+char *Glulxe::make_temp_string(uint addr) {
+  int ix, len;
+  uint addr2;
+  char *res;
+
+  if (Mem1(addr) != 0xE0)
+    fatal_error("String argument to a Glk call must be unencoded.");
+  addr++;
+
+  for (addr2=addr; Mem1(addr2); addr2++) { };
+  len = (addr2 - addr);
+  if (len < STATIC_TEMP_BUFSIZE) {
+    res = temp_buf;
+  }
+  else {
+    res = (char *)glulx_malloc(len+1);
+    if (!res) 
+      fatal_error("Unable to allocate space for string argument to Glk call.");
+  }
+  
+  for (ix=0, addr2=addr; ix<len; ix++, addr2++) {
+    res[ix] = Mem1(addr2);
+  }
+  res[len] = '\0';
+
+  return res;
+}
+
+uint *Glulxe::make_temp_ustring(uint addr) {
+  int ix, len;
+  uint addr2;
+  uint *res;
+
+  if (Mem1(addr) != 0xE2)
+    fatal_error("Ustring argument to a Glk call must be unencoded.");
+  addr+=4;
+
+  for (addr2=addr; Mem4(addr2); addr2+=4) { };
+  len = (addr2 - addr) / 4;
+  if ((len+1)*4 < STATIC_TEMP_BUFSIZE) {
+    res = (uint *)temp_buf;
+  }
+  else {
+    res = (uint *)glulx_malloc((len+1)*4);
+    if (!res) 
+      fatal_error("Unable to allocate space for ustring argument to Glk call.");
+  }
+  
+  for (ix=0, addr2=addr; ix<len; ix++, addr2+=4) {
+    res[ix] = Mem4(addr2);
+  }
+  res[len] = 0;
+
+  return res;
+}
+
+void Glulxe::free_temp_string(char *str) {
+  if (str && str != temp_buf) 
+    glulx_free(str);
+}
+
+void Glulxe::free_temp_ustring(uint *str) {
+  if (str && str != (uint *)temp_buf) 
+    glulx_free(str);
+}
+
+} // End of namespace Glulxe
+} // End of namespace Glk
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 6cd298e3..247f1d8 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -69,6 +69,7 @@ MODULE_OBJS := \
 	glulxe/operand.o \
 	glulxe/search.o \
 	glulxe/serial.o \
+	glulxe/string.o \
 	magnetic/detection.o \
 	magnetic/magnetic.o \
 	scott/detection.o \


Commit: ee8362cc073a02c91038add400f67dc2e6dba683
    https://github.com/scummvm/scummvm/commit/ee8362cc073a02c91038add400f67dc2e6dba683
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-04-17T20:46:06-07:00

Commit Message:
GLK: GLULXE: Added vm methods

Changed paths:
  A engines/glk/glulxe/vm.cpp
    engines/glk/glulxe/glulxe.h
    engines/glk/glulxe/serial.cpp
    engines/glk/module.mk


diff --git a/engines/glk/glulxe/glulxe.h b/engines/glk/glulxe/glulxe.h
index 4044074..f7684f1 100644
--- a/engines/glk/glulxe/glulxe.h
+++ b/engines/glk/glulxe/glulxe.h
@@ -38,7 +38,11 @@ typedef void (Glulxe::*UnicharHandler)(uint);
  * Glulxe game interpreter
  */
 class Glulxe : public GlkAPI {
-public:
+private:
+	/**
+	 * \defgroup vm fields
+	 * @{
+	 */
 	CharHandler stream_char_handler;
 	UnicharHandler stream_unichar_handler, glkio_unichar_han_ptr;
 
@@ -67,6 +71,8 @@ public:
 	uint protectstart, protectend;
 	uint prevpc;
 
+	/**@}*/
+
 	/**
 	 * \defgroup accel fields
 	 * @{
@@ -342,7 +348,7 @@ protected:
 	 * @{
 	 */
 
-	void stream_setup_unichar(void);
+	void stream_setup_unichar();
 
 	void nopio_char_han(unsigned char ch);
 	void filio_char_han(unsigned char ch);
@@ -385,8 +391,6 @@ public:
 	 * \defgroup Main access methods
 	 * @{
 	 */
-	void set_library_start_hook(void(*)(void));
-	void set_library_autorestore_hook(void(*)(void));
 
 	/**
 	 * Display an error in the error window, and then exit.
@@ -414,8 +418,6 @@ public:
 	  */
 	bool is_gamefile_valid();
 
-	int locate_gamefile(int isblorb);
-	
 	/**@}*/
 
 	/**
@@ -423,13 +425,53 @@ public:
 	 * @{
 	 */
 
-	void setup_vm(void);
-	void finalize_vm(void);
-	void vm_restart(void);
-	uint change_memsize(uint newlen, int internal);
+	 /**
+	  * Read in the game file and build the machine, allocating all the memory necessary.
+	 */
+	void setup_vm();
+
+	/**
+	 * Deallocate all the memory and shut down the machine.
+	 */
+	void finalize_vm();
+	
+	/**
+	 * Put the VM into a state where it's ready to begin executing the game. This is called
+	 * both at startup time, and when the machine performs a "restart" opcode.
+	 */
+	void vm_restart();
+
+	/**
+	 * Change the size of the memory map. This may not be available at all; #define FIXED_MEMSIZE
+	 * if you want the interpreter to unconditionally refuse. The internal flag should be true only
+	 * when the heap-allocation system is calling. Returns 0 for success; otherwise, the operation failed.
+	 */
+	uint change_memsize(uint newlen, bool internal);
+	
+	/**
+	 * If addr is 0, pop N arguments off the stack, and put them in an array. If non-0, take N arguments
+	 * from that main memory address instead. This has to dynamically allocate if there are more than
+	 * 32 arguments, but that shouldn't be a problem.
+	 */
 	uint *pop_arguments(uint count, uint addr);
+	
+	/**
+	 * Make sure that count bytes beginning with addr all fall within the current memory map.
+	 * This is called at every memory (read) access if VERIFY_MEMORY_ACCESS is defined in the header file.
+	*/
 	void verify_address(uint addr, uint count);
+
+	/**
+	 * 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(uint addr, uint count);
+
+	/**
+	 * 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(uint addr, uint count, uint size);
 
 	/**@}*/
@@ -527,7 +569,7 @@ public:
 	 * Set the heap state to inactive, and free the block lists. This is called when the game
 	 * starts or restarts.
 	 */
-	void heap_clear(void);
+	void heap_clear();
 
 	/**
 	 * Returns whether the heap is active.
@@ -637,7 +679,7 @@ public:
 	void *glulx_realloc(void *ptr, uint len);
 	void glulx_free(void *ptr);
 	void glulx_setrandom(uint seed);
-	uint glulx_random(void);
+	uint glulx_random();
 	void glulx_sort(void *addr, int count, int size,
 		int(*comparefunc)(void *p1, void *p2));
 
@@ -723,16 +765,16 @@ public:
 	 */
 
 	void setup_profile(strid_t stream, char *filename);
-	int init_profile(void);
+	int init_profile();
 	void profile_set_call_counts(int flag);
 #if VM_PROFILING
 	uint profile_opcount;
 #define profile_tick() (profile_opcount++)
-	int profile_profiling_active(void);
+	int profile_profiling_active();
 	void profile_in(uint addr, uint stackuse, int accel);
 	void profile_out(uint stackuse);
 	void profile_fail(char *reason);
-	void profile_quit(void);
+	void profile_quit();
 #else /* VM_PROFILING */
 #define profile_tick()         (0)
 #define profile_profiling_active()         (0)
@@ -751,15 +793,15 @@ public:
 	void debugger_set_start_trap(int flag);
 	void debugger_set_quit_trap(int flag);
 	void debugger_set_crash_trap(int flag);
-	void debugger_check_story_file(void);
-	void debugger_setup_start_state(void);
-	int debugger_ever_invoked(void);
+	void debugger_check_story_file();
+	void debugger_setup_start_state();
+	int debugger_ever_invoked();
 	int debugger_cmd_handler(char *cmd);
 	void debugger_cycle_handler(int cycle);
 	void debugger_check_func_breakpoint(uint addr);
 	void debugger_block_and_debug(char *msg);
 	void debugger_handle_crash(char *msg);
-	void debugger_handle_quit(void);
+	void debugger_handle_quit();
 #else /* VM_DEBUGGER */
 #define debugger_tick()              (0)
 #define debugger_check_story_file()  (0)
@@ -805,7 +847,7 @@ public:
 	   /* #define FLOAT_NOT_NATIVE (1) */
 
 	   /* float.c */
-	int init_float(void);
+	int init_float();
 	uint encode_float(gfloat32 val);
 	gfloat32 decode_float(uint val);
 
@@ -884,7 +926,7 @@ public:
 	/**
 	 * Get the current table address.
 	 */
-	uint stream_get_table(void);
+	uint stream_get_table();
 
 	/**
 	 * Set the current table address, and rebuild decoding cache.
diff --git a/engines/glk/glulxe/serial.cpp b/engines/glk/glulxe/serial.cpp
index 66464e8..6d66216 100644
--- a/engines/glk/glulxe/serial.cpp
+++ b/engines/glk/glulxe/serial.cpp
@@ -517,7 +517,7 @@ int Glulxe::write_buffer(dest_t *dest, const byte *ptr, uint len) {
   return 0;
 }
 
-int Glulxe::read_buffer(dest_t *dest, unsigned char *ptr, uint len) {
+int Glulxe::read_buffer(dest_t *dest, byte *ptr, uint len) {
   uint newlen;
 
   if (dest->ismem) {
@@ -546,7 +546,7 @@ int Glulxe::write_short(dest_t *dest, uint16 val) {
   return write_buffer(dest, buf, 2);
 }
 
-int Glulxe::write_byte(dest_t *dest, unsigned char val) {
+int Glulxe::write_byte(dest_t *dest, byte val) {
   return write_buffer(dest, &val, 1);
 }
 
diff --git a/engines/glk/glulxe/vm.cpp b/engines/glk/glulxe/vm.cpp
new file mode 100644
index 0000000..f332c92
--- /dev/null
+++ b/engines/glk/glulxe/vm.cpp
@@ -0,0 +1,325 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/glk/glulxe/glulxe.h"
+
+namespace Glk {
+namespace Glulxe {
+
+void Glulxe::setup_vm() {
+  unsigned char buf[4 * 7];
+  int res;
+
+  pc = 0; /* Clear this, so that error messages are cleaner. */
+  prevpc = 0;
+
+  /* Read in all the size constants from the game file header. */
+
+  stream_char_handler = NULL;
+  stream_unichar_handler = NULL;
+
+  glk_stream_set_position(gamefile, gamefile_start+8, seekmode_Start);
+  res = glk_get_buffer_stream(gamefile, (char *)buf, 4 * 7);
+  if (res != 4 * 7) {
+    fatal_error("The game file header is too short.");
+  }
+  
+  ramstart = Read4(buf+0);
+  endgamefile = Read4(buf+4);
+  origendmem = Read4(buf+8);
+  stacksize = Read4(buf+12);
+  startfuncaddr = Read4(buf+16);
+  origstringtable = Read4(buf+20);
+  checksum = Read4(buf+24);
+
+  /* Set the protection range to (0, 0), meaning "off". */
+  protectstart = 0;
+  protectend = 0;
+
+  /* Do a few sanity checks. */
+
+  if ((ramstart & 0xFF)
+    || (endgamefile & 0xFF) 
+    || (origendmem & 0xFF)
+    || (stacksize & 0xFF)) {
+    nonfatal_warning("One of the segment boundaries in the header is not "
+      "256-byte aligned.");
+  }
+
+  if (endgamefile != gamefile_len) {
+    nonfatal_warning("The gamefile length does not match the header "
+      "endgamefile length.");
+  }
+
+  if (ramstart < 0x100 || endgamefile < ramstart || origendmem < endgamefile) {
+    fatal_error("The segment boundaries in the header are in an impossible "
+      "order.");
+  }
+  if (stacksize < 0x100) {
+    fatal_error("The stack size in the header is too small.");
+  }
+  
+  /* Allocate main memory and the stack. This is where memory allocation
+     errors are most likely to occur. */
+  endmem = origendmem;
+  memmap = (unsigned char *)glulx_malloc(origendmem);
+  if (!memmap) {
+    fatal_error("Unable to allocate Glulx memory space.");
+  }
+  stack = (unsigned char *)glulx_malloc(stacksize);
+  if (!stack) {
+    glulx_free(memmap);
+    memmap = NULL;
+    fatal_error("Unable to allocate Glulx stack space.");
+  }
+  stringtable = 0;
+
+  /* Initialize various other things in the terp. */
+  init_operands(); 
+  init_serial();
+
+  /* Set up the initial machine state. */
+  vm_restart();
+
+  /* If the debugger is compiled in, check that the debug data matches
+     the game. (This only prints warnings for mismatch.) */
+  debugger_check_story_file();
+  /* Also, set up any start-time debugger state. This may do a block-
+     and-debug, if the user has requested that. */
+  debugger_setup_start_state();
+}
+
+void Glulxe::finalize_vm() {
+  stream_set_table(0);
+
+  if (memmap) {
+    glulx_free(memmap);
+    memmap = NULL;
+  }
+  if (stack) {
+    glulx_free(stack);
+    stack = NULL;
+  }
+
+  final_serial();
+}
+
+void Glulxe::vm_restart() {
+  uint lx;
+  int res;
+  int bufpos;
+  char buf[0x100];
+
+  /* Deactivate the heap (if it was active). */
+  heap_clear();
+
+  /* Reset memory to the original size. */
+  lx = change_memsize(origendmem, false);
+  if (lx)
+    fatal_error("Memory could not be reset to its original size.");
+
+  /* Load in all of main memory. We do this in 256-byte chunks, because
+     why rely on OS stream buffering? */
+  glk_stream_set_position(gamefile, gamefile_start, seekmode_Start);
+  bufpos = 0x100;
+
+  for (lx=0; lx<endgamefile; lx++) {
+    if (bufpos >= 0x100) {
+      int count = glk_get_buffer_stream(gamefile, buf, 0x100);
+      if (count != 0x100) {
+        fatal_error("The game file ended unexpectedly.");
+      }
+      bufpos = 0;
+    }
+
+    res = buf[bufpos++];
+    if (lx >= protectstart && lx < protectend)
+      continue;
+    memmap[lx] = res;
+  }
+  for (lx=endgamefile; lx<origendmem; lx++) {
+    memmap[lx] = 0;
+  }
+
+  /* Reset all the registers */
+  stackptr = 0;
+  frameptr = 0;
+  pc = 0;
+  prevpc = 0;
+  stream_set_iosys(0, 0);
+  stream_set_table(origstringtable);
+  valstackbase = 0;
+  localsbase = 0;
+
+  /* Note that we do not reset the protection range. */
+
+  /* Push the first function call. (No arguments.) */
+  enter_function(startfuncaddr, 0, NULL);
+
+  /* We're now ready to execute. */
+}
+
+uint Glulxe::change_memsize(uint newlen, bool internal) {
+  uint lx;
+  unsigned char *newmemmap;
+
+  if (newlen == endmem)
+    return 0;
+
+#ifdef FIXED_MEMSIZE
+  return 1;
+#else /* FIXED_MEMSIZE */
+
+  if ((!internal) && heap_is_active())
+    fatal_error("Cannot resize Glulx memory space while heap is active.");
+
+  if (newlen < origendmem)
+    fatal_error("Cannot resize Glulx memory space smaller than it started.");
+
+  if (newlen & 0xFF)
+    fatal_error("Can only resize Glulx memory space to a 256-byte boundary.");
+  
+  newmemmap = (unsigned char *)glulx_realloc(memmap, newlen);
+  if (!newmemmap) {
+    /* The old block is still in place, unchanged. */
+    return 1;
+  }
+  memmap = newmemmap;
+
+  if (newlen > endmem) {
+    for (lx=endmem; lx<newlen; lx++) {
+      memmap[lx] = 0;
+    }
+  }
+
+  endmem = newlen;
+
+  return 0;
+
+#endif /* FIXED_MEMSIZE */
+}
+
+uint *Glulxe::pop_arguments(uint count, uint addr) {
+  int ix;
+  uint argptr;
+  uint *array;
+
+  #define MAXARGS (32)
+  static uint statarray[MAXARGS];
+  static uint *dynarray = NULL;
+  static uint dynarray_size = 0;
+
+  if (count == 0)
+    return NULL;
+
+  if (count <= MAXARGS) {
+    /* Store in the static array. */
+    array = statarray;
+  }
+  else {
+    if (!dynarray) {
+      dynarray_size = count+8;
+      dynarray = (uint *)glulx_malloc(sizeof(uint) * dynarray_size);
+      if (!dynarray)
+        fatal_error("Unable to allocate function arguments.");
+      array = dynarray;
+    }
+    else {
+      if (dynarray_size >= count) {
+        /* It fits. */
+        array = dynarray;
+      }
+      else {
+        dynarray_size = count+8;
+        dynarray = (uint *)glulx_realloc(dynarray, sizeof(uint) * dynarray_size);
+        if (!dynarray)
+          fatal_error("Unable to reallocate function arguments.");
+        array = dynarray;
+      }
+    }
+  }
+
+  if (!addr) {
+    if (stackptr < valstackbase+4*count) 
+      fatal_error("Stack underflow in arguments.");
+    stackptr -= 4*count;
+    for (ix=0; ix<count; ix++) {
+      argptr = stackptr+4*((count-1)-ix);
+      array[ix] = Stk4(argptr);
+    }
+  }
+  else {
+    for (ix=0; ix<count; ix++) {
+      array[ix] = Mem4(addr);
+      addr += 4;
+    }
+  }
+
+  return array;
+}
+
+void Glulxe::verify_address(uint addr, uint count) {
+  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);
+  }
+}
+
+void Glulxe::verify_address_write(uint addr, uint 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);
+  }
+}
+
+void Glulxe::verify_array_addresses(uint addr, uint count, uint size) {
+  uint 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);
+}
+
+} // End of namespace Glulxe
+} // End of namespace Glk
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 247f1d8..be66caf 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -70,6 +70,7 @@ MODULE_OBJS := \
 	glulxe/search.o \
 	glulxe/serial.o \
 	glulxe/string.o \
+	glulxe/vm.o \
 	magnetic/detection.o \
 	magnetic/magnetic.o \
 	scott/detection.o \


Commit: 936b9731536d914c35b9632e919b510f3ae46506
    https://github.com/scummvm/scummvm/commit/936b9731536d914c35b9632e919b510f3ae46506
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-04-17T20:46:07-07:00

Commit Message:
GLK: GLULXE: Add miscellaneous missing methods

Changed paths:
    engines/glk/glulxe/accel.cpp
    engines/glk/glulxe/exec.cpp
    engines/glk/glulxe/float.cpp
    engines/glk/glulxe/funcs.cpp
    engines/glk/glulxe/gestalt.cpp
    engines/glk/glulxe/glkop.cpp
    engines/glk/glulxe/glulxe.cpp
    engines/glk/glulxe/glulxe.h
    engines/glk/glulxe/heap.cpp
    engines/glk/glulxe/operand.cpp
    engines/glk/glulxe/search.cpp
    engines/glk/glulxe/serial.cpp
    engines/glk/glulxe/string.cpp
    engines/glk/glulxe/vm.cpp
    engines/glk/tads/tads2/data.cpp
    engines/glk/tads/tads2/regex.cpp
    engines/glk/tads/tads2/regex.h


diff --git a/engines/glk/glulxe/accel.cpp b/engines/glk/glulxe/accel.cpp
index e4d60cf..929c6ab 100644
--- a/engines/glk/glulxe/accel.cpp
+++ b/engines/glk/glulxe/accel.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "engines/glk/glulxe/glulxe.h"
+#include "glk/glulxe/glulxe.h"
 
 namespace Glk {
 namespace Glulxe {
diff --git a/engines/glk/glulxe/exec.cpp b/engines/glk/glulxe/exec.cpp
index 4fefe05..f314ca4 100644
--- a/engines/glk/glulxe/exec.cpp
+++ b/engines/glk/glulxe/exec.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "engines/glk/glulxe/glulxe.h"
+#include "glk/glulxe/glulxe.h"
 
 namespace Glk {
 namespace Glulxe {
diff --git a/engines/glk/glulxe/float.cpp b/engines/glk/glulxe/float.cpp
index c1ffc77..36ef2f4 100644
--- a/engines/glk/glulxe/float.cpp
+++ b/engines/glk/glulxe/float.cpp
@@ -20,11 +20,111 @@
  *
  */
 
-#include "glk/glulxe/float.h"
+#include "glk/glulxe/glulxe.h"
 
 namespace Glk {
 namespace Glulxe {
 
+uint Glulxe::encode_float(gfloat32 val) {
+    gfloat32 absval;
+    uint sign;
+    int expo;
+    gfloat32 mant;
+    uint 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 = (uint)(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) | ((uint)(expo << 23)) | (fbits);
+}
+
+gfloat32 Glulxe::decode_float(uint val) {
+    int sign;
+    int expo;
+    uint 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));
+}
 
 } // End of namespace Glulxe
 } // End of namespace Glk
diff --git a/engines/glk/glulxe/funcs.cpp b/engines/glk/glulxe/funcs.cpp
index cb427e2..b85844a 100644
--- a/engines/glk/glulxe/funcs.cpp
+++ b/engines/glk/glulxe/funcs.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "engines/glk/glulxe/glulxe.h"
+#include "glk/glulxe/glulxe.h"
 
 namespace Glk {
 namespace Glulxe {
diff --git a/engines/glk/glulxe/gestalt.cpp b/engines/glk/glulxe/gestalt.cpp
index 2839c52..21f8974 100644
--- a/engines/glk/glulxe/gestalt.cpp
+++ b/engines/glk/glulxe/gestalt.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "engines/glk/glulxe/glulxe.h"
+#include "glk/glulxe/glulxe.h"
 
 namespace Glk {
 namespace Glulxe {
diff --git a/engines/glk/glulxe/glkop.cpp b/engines/glk/glulxe/glkop.cpp
index ee7f78b..12d4fef 100644
--- a/engines/glk/glulxe/glkop.cpp
+++ b/engines/glk/glulxe/glkop.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "engines/glk/glulxe/glulxe.h"
+#include "glk/glulxe/glulxe.h"
 
 namespace Glk {
 namespace Glulxe {
diff --git a/engines/glk/glulxe/glulxe.cpp b/engines/glk/glulxe/glulxe.cpp
index 576b961..1b82f58c 100644
--- a/engines/glk/glulxe/glulxe.cpp
+++ b/engines/glk/glulxe/glulxe.cpp
@@ -29,12 +29,14 @@ namespace Glulxe {
 
 Glulxe *g_vm;
 
-Glulxe::Glulxe(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc),
+Glulxe::Glulxe(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc), _random("glulxe"),
 		vm_exited_cleanly(false), gamefile(nullptr), gamefile_start(0), gamefile_len(0),
 		memmap(nullptr), stack(nullptr), ramstart(0), endgamefile(0), origendmem(0),  stacksize(0),
 		startfuncaddr(0), checksum(0), stackptr(0), frameptr(0), pc(0), prevpc(0), origstringtable(0),
 		stringtable(0), valstackbase(0), localsbase(0), endmem(0), protectstart(0), protectend(0),
 		stream_char_handler(nullptr), stream_unichar_handler(nullptr),
+		// main
+		library_autorestore_hook(nullptr),
 		// accel
 		classes_table(0), indiv_prop_start(0), class_metaclass(0), object_metaclass(0),
 		routine_metaclass(0), string_metaclass(0), self(0), num_attr_bytes(0), cpv__start(0),
@@ -52,7 +54,20 @@ void Glulxe::runGame() {
 	if (!is_gamefile_valid())
 		return;
 
-	// TODO
+	setup_vm();
+	if (library_autorestore_hook)
+		library_autorestore_hook();
+
+	execute_loop();
+	finalize_vm();
+
+	gamefile = NULL;
+	gamefile_start = 0;
+	gamefile_len = 0;
+	init_err = NULL;
+	vm_exited_cleanly = true;
+
+	profile_quit();
 }
 
 Common::Error Glulxe::loadGameData(strid_t file) {
@@ -128,5 +143,9 @@ void Glulxe::nonfatal_warning_handler(const char *str, const char *arg, bool use
 	warning("%s", msg.c_str());
 }
 
+void Glulxe::glulx_sort(void *addr, int count, int size, int(*comparefunc)(const void *p1, const void *p2)) {
+	qsort(addr, count, size, comparefunc);
+}
+
 } // End of namespace Glulxe
 } // End of namespace Glk
diff --git a/engines/glk/glulxe/glulxe.h b/engines/glk/glulxe/glulxe.h
index f7684f1..0690560 100644
--- a/engines/glk/glulxe/glulxe.h
+++ b/engines/glk/glulxe/glulxe.h
@@ -24,6 +24,7 @@
 #define GLK_GLULXE
 
 #include "common/scummsys.h"
+#include "common/random.h"
 #include "glk/glk_api.h"
 #include "glk/glulxe/glulxe_types.h"
 
@@ -74,6 +75,20 @@ private:
 	/**@}*/
 
 	/**
+	 * \defgroup main fields
+	 * @{
+	 */
+
+	 /**
+	  * The library_autorestore_hook is called right after the VM's initial setup. This is an appropriate time
+	  * to autorestore an initial game state, if the library has that capability. (Currently, only iosglk does.)
+	  */
+	void(*library_autorestore_hook)(void);
+	Common::RandomSource _random;
+
+	/**@}*/
+
+	/**
 	 * \defgroup accel fields
 	 * @{
 	 */
@@ -327,7 +342,7 @@ protected:
 	uint read_heapstate(dest_t *dest, uint chunklen, int portable, uint *sumlen, uint **summary);
 	uint read_stackstate(dest_t *dest, uint chunklen, int portable);
 	uint write_heapstate_sub(uint sumlen, uint *sumarray, dest_t *dest, int portable);
-	static int sort_heap_summary(void *p1, void *p2);
+	static int sort_heap_summary(const void *p1, const void *p2);
 
 	int read_byte(dest_t *dest, byte *val);
 	int read_short(dest_t *dest, uint16 *val);
@@ -675,13 +690,23 @@ public:
 	 * @{
 	 */
 
-	void *glulx_malloc(uint len);
-	void *glulx_realloc(void *ptr, uint len);
-	void glulx_free(void *ptr);
-	void glulx_setrandom(uint seed);
-	uint glulx_random();
-	void glulx_sort(void *addr, int count, int size,
-		int(*comparefunc)(void *p1, void *p2));
+	inline void *glulx_malloc(uint len) {
+		return malloc(len);
+	}
+	inline void *glulx_realloc(void *ptr, uint len) {
+		return realloc(ptr, len);
+	}
+	inline void glulx_free(void *ptr) {
+		free(ptr);
+	}
+	inline void glulx_setrandom(uint32 seed) {
+		_random.setSeed(seed);
+	}
+	inline uint glulx_random() {
+		return _random.getRandomNumber(0xffffffff);
+	}
+
+	void glulx_sort(void *addr, int count, int size, int(*comparefunc)(const void *p1, const void *p2));
 
 	/**@}*/
 
@@ -846,17 +871,28 @@ public:
 	   (but safer) encoding and decoding functions. */
 	   /* #define FLOAT_NOT_NATIVE (1) */
 
-	   /* float.c */
-	int init_float();
-	uint encode_float(gfloat32 val);
-	gfloat32 decode_float(uint val);
+	int init_float() { return true; }
+
+	/**
+	 * Encode floats by a lot of annoying bit manipulation.
+	 * The function is adapted from code in Python  (Objects/floatobject.c)
+	 */
+	static uint encode_float(gfloat32 val);
+
+	/**
+	 * Decode floats by a lot of annoying bit manipulation.
+	 * The function is adapted from code in Python  (Objects/floatobject.c)
+	 */
+	static gfloat32 decode_float(uint 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) */
 
-	gfloat32 glulx_powf(gfloat32 val1, gfloat32 val2);
+	inline gfloat32 glulx_powf(gfloat32 val1, gfloat32 val2) const {
+		return powf(val1, val2);
+	}
 
 #endif /* FLOAT_SUPPORT */
 	/**@}*/
@@ -906,7 +942,6 @@ public:
 
 	/**@}*/
 
-
 	/**
 	 * \defgroup Strings access methods
 	 * @{
diff --git a/engines/glk/glulxe/heap.cpp b/engines/glk/glulxe/heap.cpp
index 29d9405..00b8dcb 100644
--- a/engines/glk/glulxe/heap.cpp
+++ b/engines/glk/glulxe/heap.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "engines/glk/glulxe/glulxe.h"
+#include "glk/glulxe/glulxe.h"
 
 namespace Glk {
 namespace Glulxe {
diff --git a/engines/glk/glulxe/operand.cpp b/engines/glk/glulxe/operand.cpp
index a48d68e..cee8420 100644
--- a/engines/glk/glulxe/operand.cpp
+++ b/engines/glk/glulxe/operand.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "engines/glk/glulxe/glulxe.h"
+#include "glk/glulxe/glulxe.h"
 
 namespace Glk {
 namespace Glulxe {
diff --git a/engines/glk/glulxe/search.cpp b/engines/glk/glulxe/search.cpp
index d2f1c85..440da55 100644
--- a/engines/glk/glulxe/search.cpp
+++ b/engines/glk/glulxe/search.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "engines/glk/glulxe/glulxe.h"
+#include "glk/glulxe/glulxe.h"
 
 namespace Glk {
 namespace Glulxe {
diff --git a/engines/glk/glulxe/serial.cpp b/engines/glk/glulxe/serial.cpp
index 6d66216..1aa3163 100644
--- a/engines/glk/glulxe/serial.cpp
+++ b/engines/glk/glulxe/serial.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "engines/glk/glulxe/glulxe.h"
+#include "glk/glulxe/glulxe.h"
 
 namespace Glk {
 namespace Glulxe {
@@ -751,7 +751,7 @@ uint Glulxe::write_heapstate_sub(uint sumlen, uint *sumarray, dest_t *dest, int
   return 0;
 }
 
-int Glulxe::sort_heap_summary(void *p1, void *p2) {
+int Glulxe::sort_heap_summary(const void *p1, const void *p2) {
   uint v1 = *(uint *)p1;
   uint v2 = *(uint *)p2;
 
diff --git a/engines/glk/glulxe/string.cpp b/engines/glk/glulxe/string.cpp
index 2c95c3e..59420fa 100644
--- a/engines/glk/glulxe/string.cpp
+++ b/engines/glk/glulxe/string.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "engines/glk/glulxe/glulxe.h"
+#include "glk/glulxe/glulxe.h"
 
 namespace Glk {
 namespace Glulxe {
diff --git a/engines/glk/glulxe/vm.cpp b/engines/glk/glulxe/vm.cpp
index f332c92..eb4b112 100644
--- a/engines/glk/glulxe/vm.cpp
+++ b/engines/glk/glulxe/vm.cpp
@@ -20,43 +20,38 @@
  *
  */
 
-#include "engines/glk/glulxe/glulxe.h"
+#include "glk/glulxe/glulxe.h"
 
 namespace Glk {
 namespace Glulxe {
 
 void Glulxe::setup_vm() {
-  unsigned char buf[4 * 7];
-  int res;
+  byte buf[4 * 7];
 
-  pc = 0; /* Clear this, so that error messages are cleaner. */
+  pc = 0;			// Clear this, so that error messages are cleaner.
   prevpc = 0;
 
-  /* Read in all the size constants from the game file header. */
-
-  stream_char_handler = NULL;
-  stream_unichar_handler = NULL;
+  // Read in all the size constants from the game file header
+  stream_char_handler = nullptr;
+  stream_unichar_handler = nullptr;
 
-  glk_stream_set_position(gamefile, gamefile_start+8, seekmode_Start);
-  res = glk_get_buffer_stream(gamefile, (char *)buf, 4 * 7);
-  if (res != 4 * 7) {
+  _gameFile.seek(0);
+  if (_gameFile.read(buf, 4 * 7) != (4 * 7))
     fatal_error("The game file header is too short.");
-  }
   
-  ramstart = Read4(buf+0);
-  endgamefile = Read4(buf+4);
-  origendmem = Read4(buf+8);
-  stacksize = Read4(buf+12);
-  startfuncaddr = Read4(buf+16);
-  origstringtable = Read4(buf+20);
-  checksum = Read4(buf+24);
-
-  /* Set the protection range to (0, 0), meaning "off". */
+  ramstart = Read4(buf + 0);
+  endgamefile = Read4(buf + 4);
+  origendmem = Read4(buf + 8);
+  stacksize = Read4(buf + 12);
+  startfuncaddr = Read4(buf + 16);
+  origstringtable = Read4(buf + 20);
+  checksum = Read4(buf + 24);
+
+  // Set the protection range to (0, 0), meaning "off".
   protectstart = 0;
   protectend = 0;
 
-  /* Do a few sanity checks. */
-
+  // Do a few sanity checks.
   if ((ramstart & 0xFF)
     || (endgamefile & 0xFF) 
     || (origendmem & 0xFF)
@@ -81,28 +76,29 @@ void Glulxe::setup_vm() {
   /* Allocate main memory and the stack. This is where memory allocation
      errors are most likely to occur. */
   endmem = origendmem;
-  memmap = (unsigned char *)glulx_malloc(origendmem);
+  memmap = (byte *)glulx_malloc(origendmem);
   if (!memmap) {
     fatal_error("Unable to allocate Glulx memory space.");
   }
-  stack = (unsigned char *)glulx_malloc(stacksize);
+  stack = (byte *)glulx_malloc(stacksize);
   if (!stack) {
     glulx_free(memmap);
-    memmap = NULL;
+    memmap = nullptr;
     fatal_error("Unable to allocate Glulx stack space.");
   }
   stringtable = 0;
 
-  /* Initialize various other things in the terp. */
+  // Initialize various other things in the terp.
   init_operands(); 
   init_serial();
 
-  /* Set up the initial machine state. */
+  // Set up the initial machine state.
   vm_restart();
 
   /* If the debugger is compiled in, check that the debug data matches
      the game. (This only prints warnings for mismatch.) */
   debugger_check_story_file();
+
   /* Also, set up any start-time debugger state. This may do a block-
      and-debug, if the user has requested that. */
   debugger_setup_start_state();
@@ -113,11 +109,11 @@ void Glulxe::finalize_vm() {
 
   if (memmap) {
     glulx_free(memmap);
-    memmap = NULL;
+    memmap = nullptr;
   }
   if (stack) {
     glulx_free(stack);
-    stack = NULL;
+    stack = nullptr;
   }
 
   final_serial();
@@ -173,7 +169,7 @@ void Glulxe::vm_restart() {
   /* Note that we do not reset the protection range. */
 
   /* Push the first function call. (No arguments.) */
-  enter_function(startfuncaddr, 0, NULL);
+  enter_function(startfuncaddr, 0, nullptr);
 
   /* We're now ready to execute. */
 }
@@ -219,17 +215,17 @@ uint Glulxe::change_memsize(uint newlen, bool internal) {
 }
 
 uint *Glulxe::pop_arguments(uint count, uint addr) {
-  int ix;
+  uint ix;
   uint argptr;
   uint *array;
 
   #define MAXARGS (32)
   static uint statarray[MAXARGS];
-  static uint *dynarray = NULL;
+  static uint *dynarray = nullptr;
   static uint dynarray_size = 0;
 
   if (count == 0)
-    return NULL;
+    return nullptr;
 
   if (count <= MAXARGS) {
     /* Store in the static array. */
diff --git a/engines/glk/tads/tads2/data.cpp b/engines/glk/tads/tads2/data.cpp
index a2bbf74..c60fcb2 100644
--- a/engines/glk/tads/tads2/data.cpp
+++ b/engines/glk/tads/tads2/data.cpp
@@ -20,9 +20,9 @@
  *
  */
 
-#include "engines/glk/tads/tads2/data.h"
-#include "engines/glk/tads/tads2/types.h"
-#include "engines/glk/tads/tads2/vocabulary.h"
+#include "glk/tads/tads2/data.h"
+#include "glk/tads/tads2/types.h"
+#include "glk/tads/tads2/vocabulary.h"
 #include "common/algorithm.h"
 
 namespace Glk {
diff --git a/engines/glk/tads/tads2/regex.cpp b/engines/glk/tads/tads2/regex.cpp
index c040420..a7f7d99 100644
--- a/engines/glk/tads/tads2/regex.cpp
+++ b/engines/glk/tads/tads2/regex.cpp
@@ -67,10 +67,10 @@ Notes
   numbers.
 */
 
-#include "engines/glk/tads/tads2/regex.h"
-#include "engines/glk/tads/tads2/ler.h"
-#include "engines/glk/tads/tads2/os.h"
-//#include "engines/glk/tads/tads2/std.h"
+#include "glk/tads/tads2/regex.h"
+#include "glk/tads/tads2/ler.h"
+#include "glk/tads/tads2/os.h"
+//#include "glk/tads/tads2/std.h"
 #//include "engines/glk/tads/tads2/ler.h"
 
 namespace Glk {
diff --git a/engines/glk/tads/tads2/regex.h b/engines/glk/tads/tads2/regex.h
index 4c48a89..cd975e0 100644
--- a/engines/glk/tads/tads2/regex.h
+++ b/engines/glk/tads/tads2/regex.h
@@ -24,7 +24,7 @@
 #define GLK_TADS_TADS2_REGEX
 
 #include "common/array.h"
-#include "engines/glk/tads/tads2/ler.h"
+#include "glk/tads/tads2/ler.h"
 
 namespace Glk {
 namespace TADS {


Commit: 105c9cb885990c6de50eedb9928baa840e134000
    https://github.com/scummvm/scummvm/commit/105c9cb885990c6de50eedb9928baa840e134000
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-04-17T20:46:07-07:00

Commit Message:
GLK: GLULXE: Remove gamefile to use proper _gameFile

Changed paths:
    engines/glk/glk_dispa.cpp
    engines/glk/glulxe/glulxe.cpp
    engines/glk/glulxe/glulxe.h
    engines/glk/glulxe/operand.cpp
    engines/glk/glulxe/serial.cpp
    engines/glk/glulxe/string.cpp
    engines/glk/glulxe/vm.cpp


diff --git a/engines/glk/glk_dispa.cpp b/engines/glk/glk_dispa.cpp
index dfb638f..e4d089c 100644
--- a/engines/glk/glk_dispa.cpp
+++ b/engines/glk/glk_dispa.cpp
@@ -348,21 +348,21 @@ void GlkAPI::gidispatch_set_object_registry(gidispatch_rock_t(*regi)(void *obj,
 	{
 		/* It's now necessary to go through all existing objects, and register
 			them. */
-		for (win = glk_window_iterate(NULL, NULL);
+		for (win = glk_window_iterate(nullptr, nullptr);
 			win;
-			win = glk_window_iterate(win, NULL))
+			win = glk_window_iterate(win, nullptr))
 		{
 			win->_dispRock = (*gli_register_obj)(win, gidisp_Class_Window);
 		}
-		for (str = glk_stream_iterate(NULL, NULL);
+		for (str = glk_stream_iterate(nullptr, nullptr);
 			str;
-			str = glk_stream_iterate(str, NULL))
+			str = glk_stream_iterate(str, nullptr))
 		{
 			str->_dispRock = (*gli_register_obj)(str, gidisp_Class_Stream);
 		}
-		for (fref = glk_fileref_iterate(NULL, NULL);
+		for (fref = glk_fileref_iterate(nullptr, nullptr);
 			fref;
-			fref = glk_fileref_iterate(fref, NULL))
+			fref = glk_fileref_iterate(fref, nullptr))
 		{
 			fref->_dispRock = (*gli_register_obj)(fref, gidisp_Class_Fileref);
 		}
diff --git a/engines/glk/glulxe/glulxe.cpp b/engines/glk/glulxe/glulxe.cpp
index 1b82f58c..5d4420b 100644
--- a/engines/glk/glulxe/glulxe.cpp
+++ b/engines/glk/glulxe/glulxe.cpp
@@ -30,10 +30,10 @@ namespace Glulxe {
 Glulxe *g_vm;
 
 Glulxe::Glulxe(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc), _random("glulxe"),
-		vm_exited_cleanly(false), gamefile(nullptr), gamefile_start(0), gamefile_len(0),
-		memmap(nullptr), stack(nullptr), ramstart(0), endgamefile(0), origendmem(0),  stacksize(0),
-		startfuncaddr(0), checksum(0), stackptr(0), frameptr(0), pc(0), prevpc(0), origstringtable(0),
-		stringtable(0), valstackbase(0), localsbase(0), endmem(0), protectstart(0), protectend(0),
+		vm_exited_cleanly(false), gamefile_start(0), gamefile_len(0), memmap(nullptr), stack(nullptr),
+		ramstart(0), endgamefile(0), origendmem(0),  stacksize(0), startfuncaddr(0), checksum(0),
+		stackptr(0), frameptr(0), pc(0), prevpc(0), origstringtable(0), stringtable(0), valstackbase(0),
+		localsbase(0), endmem(0), protectstart(0), protectend(0),
 		stream_char_handler(nullptr), stream_unichar_handler(nullptr),
 		// main
 		library_autorestore_hook(nullptr),
@@ -61,10 +61,9 @@ void Glulxe::runGame() {
 	execute_loop();
 	finalize_vm();
 
-	gamefile = NULL;
 	gamefile_start = 0;
 	gamefile_len = 0;
-	init_err = NULL;
+	init_err = nullptr;
 	vm_exited_cleanly = true;
 
 	profile_quit();
diff --git a/engines/glk/glulxe/glulxe.h b/engines/glk/glulxe/glulxe.h
index 0690560..d9003c8 100644
--- a/engines/glk/glulxe/glulxe.h
+++ b/engines/glk/glulxe/glulxe.h
@@ -48,7 +48,6 @@ private:
 	UnicharHandler stream_unichar_handler, glkio_unichar_han_ptr;
 
 	bool vm_exited_cleanly;
-	strid_t gamefile;
 	uint gamefile_start, gamefile_len;
 	char *init_err, *init_err2;
 
@@ -547,7 +546,7 @@ public:
 	/**
 	 * This writes a new call frame onto the stack, at stackptr. It leaves frameptr pointing
 	 * to the frame (ie, the original stackptr value.) argc and argv are an array of arguments.
-	 * Note that if argc is zero, argv may be NULL.
+	 * Note that if argc is zero, argv may be nullptr.
 	 */
 	void enter_function(uint addr, uint argc, uint *argv);
 	
@@ -620,7 +619,7 @@ public:
 	 * (Note that these are uint values -- native byte ordering. Also, the blocks will be in address order,
 	 * which is a stricter guarantee than the VM specifies; that'll help in heap_apply_summary().)
 	 *
-	 * If the heap is inactive, store NULL. Return 0 for success; otherwise, the operation failed.
+	 * If the heap is inactive, store nullptr. Return 0 for success; otherwise, the operation failed.
 	 *
 	 * The array returned in summary must be freed with glulx_free() after the caller uses it.
 	 */
@@ -654,7 +653,7 @@ public:
 	  * An array of data structures is stored in memory, beginning at start, each structure being structsize bytes.
 	  * Within each struct, there is a key value keysize bytes long, starting at position keyoffset (from
 	  * the start of the structure.) Search through these in order. If one is found whose key matches, return it.
-	  * If numstructs are searched with no result, return NULL.
+	  * If numstructs are searched with no result, return nullptr.
 	  *
 	  * numstructs may be -1 (0xFFFFFFFF) to indicate no upper limit to the number of structures to search.
 	  * The search will continue until a match is found, or (if ZeroKeyTerminates is set) a zero key.
diff --git a/engines/glk/glulxe/operand.cpp b/engines/glk/glulxe/operand.cpp
index cee8420..23018d4 100644
--- a/engines/glk/glulxe/operand.cpp
+++ b/engines/glk/glulxe/operand.cpp
@@ -28,7 +28,7 @@ namespace Glulxe {
 /**
  * The actual immutable structures which lookup_operandlist() returns.
  */
-static const operandlist_t list_none = { 0, 4, NULL };
+static const operandlist_t list_none = { 0, 4, nullptr };
 
 static const int array_S[1] = { modeform_Store };
 static const operandlist_t list_S = { 1, 4, &array_S[0] };
@@ -276,7 +276,7 @@ const operandlist_t *Glulxe::lookup_operandlist(uint opcode) {
 #endif /* GLULX_EXTEND_OPERANDS */
 
   default: 
-    return NULL;
+    return nullptr;
   }
 }
 
diff --git a/engines/glk/glulxe/serial.cpp b/engines/glk/glulxe/serial.cpp
index 1aa3163..bc26788 100644
--- a/engines/glk/glulxe/serial.cpp
+++ b/engines/glk/glulxe/serial.cpp
@@ -41,8 +41,9 @@ bool Glulxe::init_serial() {
     ramcache = (unsigned char *)glulx_malloc(sizeof(unsigned char *) * len);
     if (!ramcache)
       return false;
-    glk_stream_set_position(gamefile, gamefile_start+ramstart, seekmode_Start);
-    res = glk_get_buffer_stream(gamefile, (char *)ramcache, len);
+
+	_gameFile.seek(gamefile_start + ramstart);
+    res = _gameFile.read(ramcache, len);
     if (res != len)
       return false;
   }
@@ -59,14 +60,14 @@ void Glulxe::final_serial() {
     }
     glulx_free(undo_chain);
   }
-  undo_chain = NULL;
+  undo_chain = nullptr;
   undo_chain_size = 0;
   undo_chain_num = 0;
 
 #ifdef SERIALIZE_CACHE_RAM
   if (ramcache) {
     glulx_free(ramcache);
-    ramcache = NULL;
+    ramcache = nullptr;
   }
 #endif /* SERIALIZE_CACHE_RAM */
 }
@@ -89,8 +90,8 @@ uint Glulxe::perform_saveundo() {
   dest.ismem = true;
   dest.size = 0;
   dest.pos = 0;
-  dest.ptr = NULL;
-  dest.str = NULL;
+  dest.ptr = nullptr;
+  dest.str = nullptr;
 
   res = 0;
   if (res == 0) {
@@ -147,7 +148,7 @@ uint Glulxe::perform_saveundo() {
     /* It worked. */
     if (undo_chain_num >= undo_chain_size) {
       glulx_free(undo_chain[undo_chain_num-1]);
-      undo_chain[undo_chain_num-1] = NULL;
+      undo_chain[undo_chain_num-1] = nullptr;
     }
     if (undo_chain_size > 1)
       memmove(undo_chain+1, undo_chain, 
@@ -155,13 +156,13 @@ uint Glulxe::perform_saveundo() {
     undo_chain[0] = dest.ptr;
     if (undo_chain_num < undo_chain_size)
       undo_chain_num += 1;
-    dest.ptr = NULL;
+    dest.ptr = nullptr;
   }
   else {
     /* It didn't work. */
     if (dest.ptr) {
       glulx_free(dest.ptr);
-      dest.ptr = NULL;
+      dest.ptr = nullptr;
     }
   }
     
@@ -172,7 +173,7 @@ uint Glulxe::perform_restoreundo() {
   dest_t dest;
   uint res, val = 0;
   uint heapsumlen = 0;
-  uint *heapsumarr = NULL;
+  uint *heapsumarr = nullptr;
 
   /* If profiling is enabled and active then fail. */
   #if VM_PROFILING
@@ -187,7 +188,7 @@ uint Glulxe::perform_restoreundo() {
   dest.size = 0;
   dest.pos = 0;
   dest.ptr = undo_chain[0];
-  dest.str = NULL;
+  dest.str = nullptr;
 
   res = 0;
   if (res == 0) {
@@ -223,11 +224,11 @@ uint Glulxe::perform_restoreundo() {
         (undo_chain_size-1) * sizeof(unsigned char *));
     undo_chain_num -= 1;
     glulx_free(dest.ptr);
-    dest.ptr = NULL;
+    dest.ptr = nullptr;
   }
   else {
     /* It didn't work. */
-    dest.ptr = NULL;
+    dest.ptr = nullptr;
   }
 
   return res;
@@ -253,7 +254,7 @@ uint Glulxe::perform_save(strid_t str) {
   dest.ismem = false;
   dest.size = 0;
   dest.pos = 0;
-  dest.ptr = NULL;
+  dest.ptr = nullptr;
   dest.str = str;
 
   res = 0;
@@ -368,7 +369,7 @@ uint Glulxe::perform_restore(strid_t str, int fromshell) {
   uint lx, res, val;
   uint filestart, filelen = 0;
   uint heapsumlen = 0;
-  uint *heapsumarr = NULL;
+  uint *heapsumarr = nullptr;
 
   /* If profiling is enabled and active then fail. */
   #if VM_PROFILING
@@ -390,7 +391,7 @@ uint Glulxe::perform_restore(strid_t str, int fromshell) {
   dest.ismem = false;
   dest.size = 0;
   dest.pos = 0;
-  dest.ptr = NULL;
+  dest.ptr = nullptr;
   dest.str = str;
 
   res = 0;
@@ -590,7 +591,7 @@ uint Glulxe::write_memstate(dest_t *dest) {
 #ifdef SERIALIZE_CACHE_RAM
   cachepos = 0;
 #else /* SERIALIZE_CACHE_RAM */
-  glk_stream_set_position(gamefile, gamefile_start+ramstart, seekmode_Start);
+  _gameFile.seek(gamefile_start + ramstart);
 #endif /* SERIALIZE_CACHE_RAM */
 
   for (pos=ramstart; pos<endmem; pos++) {
@@ -662,7 +663,7 @@ uint Glulxe::read_memstate(dest_t *dest, uint chunklen) {
 #ifdef SERIALIZE_CACHE_RAM
   cachepos = 0;
 #else /* SERIALIZE_CACHE_RAM */
-  glk_stream_set_position(gamefile, gamefile_start+ramstart, seekmode_Start);
+  _gameFile.seek(gamefile_start + ramstart);
 #endif /* SERIALIZE_CACHE_RAM */
 
   for (pos=ramstart; pos<endmem; pos++) {
@@ -671,14 +672,13 @@ uint Glulxe::read_memstate(dest_t *dest, uint chunklen) {
       val = ramcache[cachepos];
       cachepos++;
 #else /* SERIALIZE_CACHE_RAM */
-      val = glk_get_char_stream(gamefile);
-      if (val == -1) {
-        fatal_error("The game file ended unexpectedly while restoring.");
+		if (_gameFile.pos() >= _gameFile.size()) {
+			fatal_error("The game file ended unexpectedly while restoring.");
+		val = _gameFile.readByte();
       }
 #endif /* SERIALIZE_CACHE_RAM */
       ch = (unsigned char)val;
-    }
-    else {
+    } else {
       ch = 0;
     }
 
@@ -767,7 +767,7 @@ uint Glulxe::read_heapstate(dest_t *dest, uint chunklen, int portable, uint *sum
   uint *arr;
 
   *sumlen = 0;
-  *summary = NULL;
+  *summary = nullptr;
 
   if (chunklen == 0)
     return 0; /* no heap */
@@ -1106,12 +1106,12 @@ uint Glulxe::perform_verify() {
   if (len < 256 || (len & 0xFF) != 0)
     return 1;
 
-  glk_stream_set_position(gamefile, gamefile_start, seekmode_Start);
+  _gameFile.seek(gamefile_start);
   newsum = 0;
 
   /* Read the header */
   for (ix=0; ix<9; ix++) {
-    newlen = glk_get_buffer_stream(gamefile, (char *)buf, 4);
+	  newlen = _gameFile.read(buf, 4);
     if (newlen != 4)
       return 1;
     val = Read4(buf);
@@ -1127,7 +1127,7 @@ uint Glulxe::perform_verify() {
 
   /* Read everything else */
   for (; ix < len/4; ix++) {
-    newlen = glk_get_buffer_stream(gamefile, (char *)buf, 4);
+    newlen = _gameFile.read(buf, 4);
     if (newlen != 4)
       return 1;
     val = Read4(buf);
diff --git a/engines/glk/glulxe/string.cpp b/engines/glk/glulxe/string.cpp
index 59420fa..67a869e 100644
--- a/engines/glk/glulxe/string.cpp
+++ b/engines/glk/glulxe/string.cpp
@@ -358,7 +358,7 @@ void Glulxe::stream_string(uint addr, int inmiddle, int bitnum) {
                 }
                 else {
                   argc = 0;
-                  argv = NULL;
+                  argv = nullptr;
                 }
                 pc = addr;
                 push_callstub(0x10, bitnum);
@@ -528,7 +528,7 @@ void Glulxe::stream_string(uint addr, int inmiddle, int bitnum) {
                 }
                 else {
                   argc = 0;
-                  argv = NULL;
+                  argv = nullptr;
                 }
                 pc = addr;
                 push_callstub(0x10, bitnum);
@@ -641,7 +641,7 @@ void Glulxe::stream_set_table(uint addr) {
   if (tablecache_valid) {
     if (tablecache.type == 0)
       dropcache(tablecache.u.branches);
-    tablecache.u.branches = NULL;
+    tablecache.u.branches = nullptr;
     tablecache_valid = false;
   }
 
@@ -752,7 +752,7 @@ void Glulxe::dropcache(cacheblock_t *cablist) {
     cacheblock_t *cab = &(cablist[ix]);
     if (cab->type == 0) {
       dropcache(cab->u.branches);
-      cab->u.branches = NULL;
+      cab->u.branches = nullptr;
     }
   }
   glulx_free(cablist);
diff --git a/engines/glk/glulxe/vm.cpp b/engines/glk/glulxe/vm.cpp
index eb4b112..194152a 100644
--- a/engines/glk/glulxe/vm.cpp
+++ b/engines/glk/glulxe/vm.cpp
@@ -135,12 +135,12 @@ void Glulxe::vm_restart() {
 
   /* Load in all of main memory. We do this in 256-byte chunks, because
      why rely on OS stream buffering? */
-  glk_stream_set_position(gamefile, gamefile_start, seekmode_Start);
+  _gameFile.seek(gamefile_start);
   bufpos = 0x100;
 
   for (lx=0; lx<endgamefile; lx++) {
     if (bufpos >= 0x100) {
-      int count = glk_get_buffer_stream(gamefile, buf, 0x100);
+      int count = _gameFile.read(buf, 0x100);
       if (count != 0x100) {
         fatal_error("The game file ended unexpectedly.");
       }


Commit: 427e051f6a1d2b5740b9f3ab0c5e3df9df7494fb
    https://github.com/scummvm/scummvm/commit/427e051f6a1d2b5740b9f3ab0c5e3df9df7494fb
Author: dreammaster (dreammaster at scummvm.org)
Date: 2019-04-17T20:46:07-07:00

Commit Message:
GLK: GLULXE: astyle formatting

Changed paths:
    engines/glk/glulxe/accel.cpp
    engines/glk/glulxe/exec.cpp
    engines/glk/glulxe/float.cpp
    engines/glk/glulxe/funcs.cpp
    engines/glk/glulxe/gestalt.cpp
    engines/glk/glulxe/glkop.cpp
    engines/glk/glulxe/glulxe.cpp
    engines/glk/glulxe/glulxe.h
    engines/glk/glulxe/glulxe_types.h
    engines/glk/glulxe/heap.cpp
    engines/glk/glulxe/operand.cpp
    engines/glk/glulxe/search.cpp
    engines/glk/glulxe/serial.cpp
    engines/glk/glulxe/string.cpp
    engines/glk/glulxe/vm.cpp


diff --git a/engines/glk/glulxe/accel.cpp b/engines/glk/glulxe/accel.cpp
index 929c6ab..1d0b9e5 100644
--- a/engines/glk/glulxe/accel.cpp
+++ b/engines/glk/glulxe/accel.cpp
@@ -41,581 +41,623 @@ namespace Glulxe {
 #define ARG_IF_GIVEN(argv, argc, ix)  ((argc > ix) ? (ARG(argv, argc, ix)) : 0)
 
 acceleration_func Glulxe::accel_find_func(uint index) {
-    switch (index) {
-        case 0: return nullptr;		// 0 always means no acceleration
-        case 1: return &Glulxe::func_1_z__region;
-        case 2: return &Glulxe::func_2_cp__tab;
-        case 3: return &Glulxe::func_3_ra__pr;
-        case 4: return &Glulxe::func_4_rl__pr;
-        case 5: return &Glulxe::func_5_oc__cl;
-        case 6: return &Glulxe::func_6_rv__pr;
-        case 7: return &Glulxe::func_7_op__pr;
-        case 8: return &Glulxe::func_8_cp__tab;
-        case 9: return &Glulxe::func_9_ra__pr;
-        case 10: return &Glulxe::func_10_rl__pr;
-        case 11: return &Glulxe::func_11_oc__cl;
-        case 12: return &Glulxe::func_12_rv__pr;
-        case 13: return &Glulxe::func_13_op__pr;
-    }
-    return nullptr;
+	switch (index) {
+	case 0:
+		return nullptr;     // 0 always means no acceleration
+	case 1:
+		return &Glulxe::func_1_z__region;
+	case 2:
+		return &Glulxe::func_2_cp__tab;
+	case 3:
+		return &Glulxe::func_3_ra__pr;
+	case 4:
+		return &Glulxe::func_4_rl__pr;
+	case 5:
+		return &Glulxe::func_5_oc__cl;
+	case 6:
+		return &Glulxe::func_6_rv__pr;
+	case 7:
+		return &Glulxe::func_7_op__pr;
+	case 8:
+		return &Glulxe::func_8_cp__tab;
+	case 9:
+		return &Glulxe::func_9_ra__pr;
+	case 10:
+		return &Glulxe::func_10_rl__pr;
+	case 11:
+		return &Glulxe::func_11_oc__cl;
+	case 12:
+		return &Glulxe::func_12_rv__pr;
+	case 13:
+		return &Glulxe::func_13_op__pr;
+	}
+	return nullptr;
 }
 
 acceleration_func Glulxe::accel_get_func(uint addr) {
-    int bucknum;
-    accelentry_t *ptr;
-
-    if (!accelentries)
-        return nullptr;
-
-    bucknum = (addr % ACCEL_HASH_SIZE);
-    for (ptr = accelentries[bucknum]; ptr; ptr = ptr->next) {
-        if (ptr->addr == addr)
-            return ptr->func;
-    }
-    return nullptr;
+	int bucknum;
+	accelentry_t *ptr;
+
+	if (!accelentries)
+		return nullptr;
+
+	bucknum = (addr % ACCEL_HASH_SIZE);
+	for (ptr = accelentries[bucknum]; ptr; ptr = ptr->next) {
+		if (ptr->addr == addr)
+			return ptr->func;
+	}
+	return nullptr;
 }
 
 void Glulxe::accel_iterate_funcs(void (*func)(uint index, uint addr)) {
-    int bucknum;
-    accelentry_t *ptr;
-
-    if (!accelentries)
-        return;
-
-    for (bucknum=0; bucknum<ACCEL_HASH_SIZE; bucknum++) {
-        for (ptr = accelentries[bucknum]; ptr; ptr = ptr->next) {
-            if (ptr->func) {
-                func(ptr->index, ptr->addr);
-            }
-        }
-    }
+	int bucknum;
+	accelentry_t *ptr;
+
+	if (!accelentries)
+		return;
+
+	for (bucknum = 0; bucknum < ACCEL_HASH_SIZE; bucknum++) {
+		for (ptr = accelentries[bucknum]; ptr; ptr = ptr->next) {
+			if (ptr->func) {
+				func(ptr->index, ptr->addr);
+			}
+		}
+	}
 }
 
 void Glulxe::accel_set_func(uint index, uint addr) {
-    int bucknum;
-    accelentry_t *ptr;
-    int functype;
-    acceleration_func new_func = nullptr;
-
-    /* Check the Glulx type identifier byte. */
-    functype = Mem1(addr);
-    if (functype != 0xC0 && functype != 0xC1) {
-        fatal_error_i("Attempt to accelerate non-function.", addr);
-    }
-
-    if (!accelentries) {
-        accelentries = (accelentry_t **)glulx_malloc(ACCEL_HASH_SIZE 
-            * sizeof(accelentry_t *));
-        if (!accelentries) 
-            fatal_error("Cannot malloc acceleration table.");
-        for (bucknum=0; bucknum<ACCEL_HASH_SIZE; bucknum++)
-            accelentries[bucknum] = nullptr;
-    }
-
-    new_func = accel_find_func(index);
-    /* Might be nullptr, if the index is zero or not recognized. */
-
-    bucknum = (addr % ACCEL_HASH_SIZE);
-    for (ptr = accelentries[bucknum]; ptr; ptr = ptr->next) {
-        if (ptr->addr == addr)
-            break;
-    }
-    if (!ptr) {
-        if (!new_func) {
-            return; /* no need for a new entry */
-        }
-        ptr = (accelentry_t *)glulx_malloc(sizeof(accelentry_t));
-        if (!ptr)
-            fatal_error("Cannot malloc acceleration entry.");
-        ptr->addr = addr;
-        ptr->index = 0;
-        ptr->func = nullptr;
-        ptr->next = accelentries[bucknum];
-        accelentries[bucknum] = ptr;
-    }
-
-    ptr->index = index;
-    ptr->func = new_func;
+	int bucknum;
+	accelentry_t *ptr;
+	int functype;
+	acceleration_func new_func = nullptr;
+
+	/* Check the Glulx type identifier byte. */
+	functype = Mem1(addr);
+	if (functype != 0xC0 && functype != 0xC1) {
+		fatal_error_i("Attempt to accelerate non-function.", addr);
+	}
+
+	if (!accelentries) {
+		accelentries = (accelentry_t **)glulx_malloc(ACCEL_HASH_SIZE
+		               * sizeof(accelentry_t *));
+		if (!accelentries)
+			fatal_error("Cannot malloc acceleration table.");
+		for (bucknum = 0; bucknum < ACCEL_HASH_SIZE; bucknum++)
+			accelentries[bucknum] = nullptr;
+	}
+
+	new_func = accel_find_func(index);
+	/* Might be nullptr, if the index is zero or not recognized. */
+
+	bucknum = (addr % ACCEL_HASH_SIZE);
+	for (ptr = accelentries[bucknum]; ptr; ptr = ptr->next) {
+		if (ptr->addr == addr)
+			break;
+	}
+	if (!ptr) {
+		if (!new_func) {
+			return; /* no need for a new entry */
+		}
+		ptr = (accelentry_t *)glulx_malloc(sizeof(accelentry_t));
+		if (!ptr)
+			fatal_error("Cannot malloc acceleration entry.");
+		ptr->addr = addr;
+		ptr->index = 0;
+		ptr->func = nullptr;
+		ptr->next = accelentries[bucknum];
+		accelentries[bucknum] = ptr;
+	}
+
+	ptr->index = index;
+	ptr->func = new_func;
 }
 
 void Glulxe::accel_set_param(uint index, uint val) {
-    switch (index) {
-        case 0: classes_table = val; break;
-        case 1: indiv_prop_start = val; break;
-        case 2: class_metaclass = val; break;
-        case 3: object_metaclass = val; break;
-        case 4: routine_metaclass = val; break;
-        case 5: string_metaclass = val; break;
-        case 6: self = val; break;
-        case 7: num_attr_bytes = val; break;
-        case 8: cpv__start = val; break;
-    }
+	switch (index) {
+	case 0:
+		classes_table = val;
+		break;
+	case 1:
+		indiv_prop_start = val;
+		break;
+	case 2:
+		class_metaclass = val;
+		break;
+	case 3:
+		object_metaclass = val;
+		break;
+	case 4:
+		routine_metaclass = val;
+		break;
+	case 5:
+		string_metaclass = val;
+		break;
+	case 6:
+		self = val;
+		break;
+	case 7:
+		num_attr_bytes = val;
+		break;
+	case 8:
+		cpv__start = val;
+		break;
+	}
 }
 
 uint Glulxe::accel_get_param_count() const {
-    return 9;
+	return 9;
 }
 
 uint Glulxe::accel_get_param(uint index) const {
-    switch (index) {
-        case 0: return classes_table;
-        case 1: return indiv_prop_start;
-        case 2: return class_metaclass;
-        case 3: return object_metaclass;
-        case 4: return routine_metaclass;
-        case 5: return string_metaclass;
-        case 6: return self;
-        case 7: return num_attr_bytes;
-        case 8: return cpv__start;
-        default: return 0;
-    }
+	switch (index) {
+	case 0:
+		return classes_table;
+	case 1:
+		return indiv_prop_start;
+	case 2:
+		return class_metaclass;
+	case 3:
+		return object_metaclass;
+	case 4:
+		return routine_metaclass;
+	case 5:
+		return string_metaclass;
+	case 6:
+		return self;
+	case 7:
+		return num_attr_bytes;
+	case 8:
+		return cpv__start;
+	default:
+		return 0;
+	}
 }
 
 void Glulxe::accel_error(const char *msg) {
-    glk_put_char('\n');
-    glk_put_string(msg);
-    glk_put_char('\n');
+	glk_put_char('\n');
+	glk_put_string(msg);
+	glk_put_char('\n');
 }
 
 int Glulxe::obj_in_class(uint obj) {
-    // This checks whether obj is contained in Class, not whether it is a member of Class
-    return (Mem4(obj + 13 + num_attr_bytes) == class_metaclass);
+	// This checks whether obj is contained in Class, not whether it is a member of Class
+	return (Mem4(obj + 13 + num_attr_bytes) == class_metaclass);
 }
 
 uint Glulxe::get_prop(uint obj, uint id) {
-    uint cla = 0;
-    uint prop;
-    uint call_argv[2];
-
-    if (id & 0xFFFF0000) {
-        cla = Mem4(classes_table+((id & 0xFFFF) * 4));
-        ARG(call_argv, 2, 0) = obj;
-        ARG(call_argv, 2, 1) = cla;
-        if (func_5_oc__cl(2, call_argv) == 0)
-            return 0;
-
-        id >>= 16;
-        obj = cla;
-    }
-
-    ARG(call_argv, 2, 0) = obj;
-    ARG(call_argv, 2, 1) = id;
-    prop = func_2_cp__tab(2, call_argv);
-    if (prop == 0)
-        return 0;
-
-    if (obj_in_class(obj) && (cla == 0)) {
-        if ((id < indiv_prop_start) || (id >= indiv_prop_start+8))
-            return 0;
-    }
-
-    if (Mem4(self) != obj) {
-        if (Mem1(prop + 9) & 1)
-            return 0;
-    }
-    return prop;
+	uint cla = 0;
+	uint prop;
+	uint call_argv[2];
+
+	if (id & 0xFFFF0000) {
+		cla = Mem4(classes_table + ((id & 0xFFFF) * 4));
+		ARG(call_argv, 2, 0) = obj;
+		ARG(call_argv, 2, 1) = cla;
+		if (func_5_oc__cl(2, call_argv) == 0)
+			return 0;
+
+		id >>= 16;
+		obj = cla;
+	}
+
+	ARG(call_argv, 2, 0) = obj;
+	ARG(call_argv, 2, 1) = id;
+	prop = func_2_cp__tab(2, call_argv);
+	if (prop == 0)
+		return 0;
+
+	if (obj_in_class(obj) && (cla == 0)) {
+		if ((id < indiv_prop_start) || (id >= indiv_prop_start + 8))
+			return 0;
+	}
+
+	if (Mem4(self) != obj) {
+		if (Mem1(prop + 9) & 1)
+			return 0;
+	}
+	return prop;
 }
 
 uint Glulxe::get_prop_new(uint obj, uint id) {
-    uint cla = 0;
-    uint prop;
-    uint call_argv[2];
-
-    if (id & 0xFFFF0000) {
-        cla = Mem4(classes_table+((id & 0xFFFF) * 4));
-        ARG(call_argv, 2, 0) = obj;
-        ARG(call_argv, 2, 1) = cla;
-        if (func_11_oc__cl(2, call_argv) == 0)
-            return 0;
-
-        id >>= 16;
-        obj = cla;
-    }
-
-    ARG(call_argv, 2, 0) = obj;
-    ARG(call_argv, 2, 1) = id;
-    prop = func_8_cp__tab(2, call_argv);
-    if (prop == 0)
-        return 0;
-
-    if (obj_in_class(obj) && (cla == 0)) {
-        if ((id < indiv_prop_start) || (id >= indiv_prop_start+8))
-            return 0;
-    }
-
-    if (Mem4(self) != obj) {
-        if (Mem1(prop + 9) & 1)
-            return 0;
-    }
-    return prop;
+	uint cla = 0;
+	uint prop;
+	uint call_argv[2];
+
+	if (id & 0xFFFF0000) {
+		cla = Mem4(classes_table + ((id & 0xFFFF) * 4));
+		ARG(call_argv, 2, 0) = obj;
+		ARG(call_argv, 2, 1) = cla;
+		if (func_11_oc__cl(2, call_argv) == 0)
+			return 0;
+
+		id >>= 16;
+		obj = cla;
+	}
+
+	ARG(call_argv, 2, 0) = obj;
+	ARG(call_argv, 2, 1) = id;
+	prop = func_8_cp__tab(2, call_argv);
+	if (prop == 0)
+		return 0;
+
+	if (obj_in_class(obj) && (cla == 0)) {
+		if ((id < indiv_prop_start) || (id >= indiv_prop_start + 8))
+			return 0;
+	}
+
+	if (Mem4(self) != obj) {
+		if (Mem1(prop + 9) & 1)
+			return 0;
+	}
+	return prop;
 }
 
 uint Glulxe::func_1_z__region(uint argc, uint *argv) {
-    uint addr;
-    uint tb;
-
-    if (argc < 1)
-        return 0;
-
-    addr = ARG(argv, argc, 0);
-    if (addr < 36)
-        return 0;
-    if (addr >= endmem)
-        return 0;
-
-    tb = Mem1(addr);
-    if (tb >= 0xE0) {
-        return 3;
-    }
-    if (tb >= 0xC0) {
-        return 2;
-    }
-    if (tb >= 0x70 && tb <= 0x7F && addr >= ramstart) {
-        return 1;
-    }
-    return 0;
+	uint addr;
+	uint tb;
+
+	if (argc < 1)
+		return 0;
+
+	addr = ARG(argv, argc, 0);
+	if (addr < 36)
+		return 0;
+	if (addr >= endmem)
+		return 0;
+
+	tb = Mem1(addr);
+	if (tb >= 0xE0) {
+		return 3;
+	}
+	if (tb >= 0xC0) {
+		return 2;
+	}
+	if (tb >= 0x70 && tb <= 0x7F && addr >= ramstart) {
+		return 1;
+	}
+	return 0;
 }
 
 uint Glulxe::func_2_cp__tab(uint argc, uint *argv) {
-    uint obj;
-    uint id;
-    uint otab, max;
-
-    obj = ARG_IF_GIVEN(argv, argc, 0);
-    id = ARG_IF_GIVEN(argv, argc, 1);
-
-    if (func_1_z__region(1, &obj) != 1) {
-        accel_error("[** Programming error: tried to find the \".\" of (something) **]");
-        return 0;
-    }
-
-    otab = Mem4(obj + 16);
-    if (!otab)
-        return 0;
-
-    max = Mem4(otab);
-    otab += 4;
-    /* @binarysearch id 2 otab 10 max 0 0 res; */
-    return binary_search(id, 2, otab, 10, max, 0, 0);
+	uint obj;
+	uint id;
+	uint otab, max;
+
+	obj = ARG_IF_GIVEN(argv, argc, 0);
+	id = ARG_IF_GIVEN(argv, argc, 1);
+
+	if (func_1_z__region(1, &obj) != 1) {
+		accel_error("[** Programming error: tried to find the \".\" of (something) **]");
+		return 0;
+	}
+
+	otab = Mem4(obj + 16);
+	if (!otab)
+		return 0;
+
+	max = Mem4(otab);
+	otab += 4;
+	/* @binarysearch id 2 otab 10 max 0 0 res; */
+	return binary_search(id, 2, otab, 10, max, 0, 0);
 }
 
 uint Glulxe::func_3_ra__pr(uint argc, uint *argv) {
-    uint obj;
-    uint id;
-    uint prop;
+	uint obj;
+	uint id;
+	uint prop;
 
-    obj = ARG_IF_GIVEN(argv, argc, 0);
-    id = ARG_IF_GIVEN(argv, argc, 1);
+	obj = ARG_IF_GIVEN(argv, argc, 0);
+	id = ARG_IF_GIVEN(argv, argc, 1);
 
-    prop = get_prop(obj, id);
-    if (prop == 0)
-        return 0;
+	prop = get_prop(obj, id);
+	if (prop == 0)
+		return 0;
 
-    return Mem4(prop + 4);
+	return Mem4(prop + 4);
 }
 
 uint Glulxe::func_4_rl__pr(uint argc, uint *argv) {
-    uint obj;
-    uint id;
-    uint prop;
+	uint obj;
+	uint id;
+	uint prop;
 
-    obj = ARG_IF_GIVEN(argv, argc, 0);
-    id = ARG_IF_GIVEN(argv, argc, 1);
+	obj = ARG_IF_GIVEN(argv, argc, 0);
+	id = ARG_IF_GIVEN(argv, argc, 1);
 
-    prop = get_prop(obj, id);
-    if (prop == 0)
-        return 0;
+	prop = get_prop(obj, id);
+	if (prop == 0)
+		return 0;
 
-    return 4 * Mem2(prop + 2);
+	return 4 * Mem2(prop + 2);
 }
 
 uint Glulxe::func_5_oc__cl(uint argc, uint *argv) {
-    uint obj;
-    uint cla;
-    uint zr, prop, inlist, inlistlen, jx;
-
-    obj = ARG_IF_GIVEN(argv, argc, 0);
-    cla = ARG_IF_GIVEN(argv, argc, 1);
-
-    zr = func_1_z__region(1, &obj);
-    if (zr == 3)
-        return (cla == string_metaclass) ? 1 : 0;
-    if (zr == 2)
-        return (cla == routine_metaclass) ? 1 : 0;
-    if (zr != 1)
-        return 0;
-
-    if (cla == class_metaclass) {
-        if (obj_in_class(obj))
-            return 1;
-        if (obj == class_metaclass)
-            return 1;
-        if (obj == string_metaclass)
-            return 1;
-        if (obj == routine_metaclass)
-            return 1;
-        if (obj == object_metaclass)
-            return 1;
-        return 0;
-    }
-    if (cla == object_metaclass) {
-        if (obj_in_class(obj))
-            return 0;
-        if (obj == class_metaclass)
-            return 0;
-        if (obj == string_metaclass)
-            return 0;
-        if (obj == routine_metaclass)
-            return 0;
-        if (obj == object_metaclass)
-            return 0;
-        return 1;
-    }
-    if ((cla == string_metaclass) || (cla == routine_metaclass))
-        return 0;
-
-    if (!obj_in_class(cla)) {
-        accel_error("[** Programming error: tried to apply 'ofclass' with non-class **]");
-        return 0;
-    }
-
-    prop = get_prop(obj, 2);
-    if (prop == 0)
-       return 0;
-
-    inlist = Mem4(prop + 4);
-    if (inlist == 0)
-       return 0;
-
-    inlistlen = Mem2(prop + 2);
-    for (jx = 0; jx < inlistlen; jx++) {
-        if (Mem4(inlist + (4 * jx)) == cla)
-            return 1;
-    }
-    return 0;
+	uint obj;
+	uint cla;
+	uint zr, prop, inlist, inlistlen, jx;
+
+	obj = ARG_IF_GIVEN(argv, argc, 0);
+	cla = ARG_IF_GIVEN(argv, argc, 1);
+
+	zr = func_1_z__region(1, &obj);
+	if (zr == 3)
+		return (cla == string_metaclass) ? 1 : 0;
+	if (zr == 2)
+		return (cla == routine_metaclass) ? 1 : 0;
+	if (zr != 1)
+		return 0;
+
+	if (cla == class_metaclass) {
+		if (obj_in_class(obj))
+			return 1;
+		if (obj == class_metaclass)
+			return 1;
+		if (obj == string_metaclass)
+			return 1;
+		if (obj == routine_metaclass)
+			return 1;
+		if (obj == object_metaclass)
+			return 1;
+		return 0;
+	}
+	if (cla == object_metaclass) {
+		if (obj_in_class(obj))
+			return 0;
+		if (obj == class_metaclass)
+			return 0;
+		if (obj == string_metaclass)
+			return 0;
+		if (obj == routine_metaclass)
+			return 0;
+		if (obj == object_metaclass)
+			return 0;
+		return 1;
+	}
+	if ((cla == string_metaclass) || (cla == routine_metaclass))
+		return 0;
+
+	if (!obj_in_class(cla)) {
+		accel_error("[** Programming error: tried to apply 'ofclass' with non-class **]");
+		return 0;
+	}
+
+	prop = get_prop(obj, 2);
+	if (prop == 0)
+		return 0;
+
+	inlist = Mem4(prop + 4);
+	if (inlist == 0)
+		return 0;
+
+	inlistlen = Mem2(prop + 2);
+	for (jx = 0; jx < inlistlen; jx++) {
+		if (Mem4(inlist + (4 * jx)) == cla)
+			return 1;
+	}
+	return 0;
 }
 
 uint Glulxe::func_6_rv__pr(uint argc, uint *argv) {
-    uint id;
-    uint addr;
+	uint id;
+	uint addr;
 
-    id = ARG_IF_GIVEN(argv, argc, 1);
+	id = ARG_IF_GIVEN(argv, argc, 1);
 
-    addr = func_3_ra__pr(argc, argv);
+	addr = func_3_ra__pr(argc, argv);
 
-    if (addr == 0) {
-        if ((id > 0) && (id < indiv_prop_start))
-            return Mem4(cpv__start + (4 * id));
+	if (addr == 0) {
+		if ((id > 0) && (id < indiv_prop_start))
+			return Mem4(cpv__start + (4 * id));
 
-        accel_error("[** Programming error: tried to read (something) **]");
-        return 0;
-    }
+		accel_error("[** Programming error: tried to read (something) **]");
+		return 0;
+	}
 
-    return Mem4(addr);
+	return Mem4(addr);
 }
 
 uint Glulxe::func_7_op__pr(uint argc, uint *argv) {
-    uint obj;
-    uint id;
-    uint zr;
-
-    obj = ARG_IF_GIVEN(argv, argc, 0);
-    id = ARG_IF_GIVEN(argv, argc, 1);
-
-    zr = func_1_z__region(1, &obj);
-    if (zr == 3) {
-        /* print is INDIV_PROP_START+6 */
-        if (id == indiv_prop_start+6)
-            return 1;
-        /* print_to_array is INDIV_PROP_START+7 */
-        if (id == indiv_prop_start+7)
-            return 1;
-        return 0;
-    }
-    if (zr == 2) {
-        /* call is INDIV_PROP_START+5 */
-        return ((id == indiv_prop_start+5) ? 1 : 0);
-    }
-    if (zr != 1)
-        return 0;
-
-    if ((id >= indiv_prop_start) && (id < indiv_prop_start+8)) {
-        if (obj_in_class(obj))
-            return 1;
-    }
-
-    return ((func_3_ra__pr(argc, argv)) ? 1 : 0);
+	uint obj;
+	uint id;
+	uint zr;
+
+	obj = ARG_IF_GIVEN(argv, argc, 0);
+	id = ARG_IF_GIVEN(argv, argc, 1);
+
+	zr = func_1_z__region(1, &obj);
+	if (zr == 3) {
+		/* print is INDIV_PROP_START+6 */
+		if (id == indiv_prop_start + 6)
+			return 1;
+		/* print_to_array is INDIV_PROP_START+7 */
+		if (id == indiv_prop_start + 7)
+			return 1;
+		return 0;
+	}
+	if (zr == 2) {
+		/* call is INDIV_PROP_START+5 */
+		return ((id == indiv_prop_start + 5) ? 1 : 0);
+	}
+	if (zr != 1)
+		return 0;
+
+	if ((id >= indiv_prop_start) && (id < indiv_prop_start + 8)) {
+		if (obj_in_class(obj))
+			return 1;
+	}
+
+	return ((func_3_ra__pr(argc, argv)) ? 1 : 0);
 }
 
 uint Glulxe::func_8_cp__tab(uint argc, uint *argv) {
-    uint obj;
-    uint id;
-    uint otab, max;
-
-    obj = ARG_IF_GIVEN(argv, argc, 0);
-    id = ARG_IF_GIVEN(argv, argc, 1);
-
-    if (func_1_z__region(1, &obj) != 1) {
-        accel_error("[** Programming error: tried to find the \".\" of (something) **]");
-        return 0;
-    }
-
-    otab = Mem4(obj + 4*(3+(int)(num_attr_bytes/4)));
-    if (!otab)
-        return 0;
-
-    max = Mem4(otab);
-    otab += 4;
-    /* @binarysearch id 2 otab 10 max 0 0 res; */
-    return binary_search(id, 2, otab, 10, max, 0, 0);
+	uint obj;
+	uint id;
+	uint otab, max;
+
+	obj = ARG_IF_GIVEN(argv, argc, 0);
+	id = ARG_IF_GIVEN(argv, argc, 1);
+
+	if (func_1_z__region(1, &obj) != 1) {
+		accel_error("[** Programming error: tried to find the \".\" of (something) **]");
+		return 0;
+	}
+
+	otab = Mem4(obj + 4 * (3 + (int)(num_attr_bytes / 4)));
+	if (!otab)
+		return 0;
+
+	max = Mem4(otab);
+	otab += 4;
+	/* @binarysearch id 2 otab 10 max 0 0 res; */
+	return binary_search(id, 2, otab, 10, max, 0, 0);
 }
 
 uint Glulxe::func_9_ra__pr(uint argc, uint *argv) {
-    uint obj;
-    uint id;
-    uint prop;
+	uint obj;
+	uint id;
+	uint prop;
 
-    obj = ARG_IF_GIVEN(argv, argc, 0);
-    id = ARG_IF_GIVEN(argv, argc, 1);
+	obj = ARG_IF_GIVEN(argv, argc, 0);
+	id = ARG_IF_GIVEN(argv, argc, 1);
 
-    prop = get_prop_new(obj, id);
-    if (prop == 0)
-        return 0;
+	prop = get_prop_new(obj, id);
+	if (prop == 0)
+		return 0;
 
-    return Mem4(prop + 4);
+	return Mem4(prop + 4);
 }
 
 uint Glulxe::func_10_rl__pr(uint argc, uint *argv) {
-    uint obj;
-    uint id;
-    uint prop;
+	uint obj;
+	uint id;
+	uint prop;
 
-    obj = ARG_IF_GIVEN(argv, argc, 0);
-    id = ARG_IF_GIVEN(argv, argc, 1);
+	obj = ARG_IF_GIVEN(argv, argc, 0);
+	id = ARG_IF_GIVEN(argv, argc, 1);
 
-    prop = get_prop_new(obj, id);
-    if (prop == 0)
-        return 0;
+	prop = get_prop_new(obj, id);
+	if (prop == 0)
+		return 0;
 
-    return 4 * Mem2(prop + 2);
+	return 4 * Mem2(prop + 2);
 }
 
 uint Glulxe::func_11_oc__cl(uint argc, uint *argv) {
-    uint obj;
-    uint cla;
-    uint zr, prop, inlist, inlistlen, jx;
-
-    obj = ARG_IF_GIVEN(argv, argc, 0);
-    cla = ARG_IF_GIVEN(argv, argc, 1);
-
-    zr = func_1_z__region(1, &obj);
-    if (zr == 3)
-        return (cla == string_metaclass) ? 1 : 0;
-    if (zr == 2)
-        return (cla == routine_metaclass) ? 1 : 0;
-    if (zr != 1)
-        return 0;
-
-    if (cla == class_metaclass) {
-        if (obj_in_class(obj))
-            return 1;
-        if (obj == class_metaclass)
-            return 1;
-        if (obj == string_metaclass)
-            return 1;
-        if (obj == routine_metaclass)
-            return 1;
-        if (obj == object_metaclass)
-            return 1;
-        return 0;
-    }
-    if (cla == object_metaclass) {
-        if (obj_in_class(obj))
-            return 0;
-        if (obj == class_metaclass)
-            return 0;
-        if (obj == string_metaclass)
-            return 0;
-        if (obj == routine_metaclass)
-            return 0;
-        if (obj == object_metaclass)
-            return 0;
-        return 1;
-    }
-    if ((cla == string_metaclass) || (cla == routine_metaclass))
-        return 0;
-
-    if (!obj_in_class(cla)) {
-        accel_error("[** Programming error: tried to apply 'ofclass' with non-class **]");
-        return 0;
-    }
-
-    prop = get_prop_new(obj, 2);
-    if (prop == 0)
-       return 0;
-
-    inlist = Mem4(prop + 4);
-    if (inlist == 0)
-       return 0;
-
-    inlistlen = Mem2(prop + 2);
-    for (jx = 0; jx < inlistlen; jx++) {
-        if (Mem4(inlist + (4 * jx)) == cla)
-            return 1;
-    }
-    return 0;
+	uint obj;
+	uint cla;
+	uint zr, prop, inlist, inlistlen, jx;
+
+	obj = ARG_IF_GIVEN(argv, argc, 0);
+	cla = ARG_IF_GIVEN(argv, argc, 1);
+
+	zr = func_1_z__region(1, &obj);
+	if (zr == 3)
+		return (cla == string_metaclass) ? 1 : 0;
+	if (zr == 2)
+		return (cla == routine_metaclass) ? 1 : 0;
+	if (zr != 1)
+		return 0;
+
+	if (cla == class_metaclass) {
+		if (obj_in_class(obj))
+			return 1;
+		if (obj == class_metaclass)
+			return 1;
+		if (obj == string_metaclass)
+			return 1;
+		if (obj == routine_metaclass)
+			return 1;
+		if (obj == object_metaclass)
+			return 1;
+		return 0;
+	}
+	if (cla == object_metaclass) {
+		if (obj_in_class(obj))
+			return 0;
+		if (obj == class_metaclass)
+			return 0;
+		if (obj == string_metaclass)
+			return 0;
+		if (obj == routine_metaclass)
+			return 0;
+		if (obj == object_metaclass)
+			return 0;
+		return 1;
+	}
+	if ((cla == string_metaclass) || (cla == routine_metaclass))
+		return 0;
+
+	if (!obj_in_class(cla)) {
+		accel_error("[** Programming error: tried to apply 'ofclass' with non-class **]");
+		return 0;
+	}
+
+	prop = get_prop_new(obj, 2);
+	if (prop == 0)
+		return 0;
+
+	inlist = Mem4(prop + 4);
+	if (inlist == 0)
+		return 0;
+
+	inlistlen = Mem2(prop + 2);
+	for (jx = 0; jx < inlistlen; jx++) {
+		if (Mem4(inlist + (4 * jx)) == cla)
+			return 1;
+	}
+	return 0;
 }
 
 uint Glulxe::func_12_rv__pr(uint argc, uint *argv) {
-    uint id;
-    uint addr;
+	uint id;
+	uint addr;
 
-    id = ARG_IF_GIVEN(argv, argc, 1);
+	id = ARG_IF_GIVEN(argv, argc, 1);
 
-    addr = func_9_ra__pr(argc, argv);
+	addr = func_9_ra__pr(argc, argv);
 
-    if (addr == 0) {
-        if ((id > 0) && (id < indiv_prop_start))
-            return Mem4(cpv__start + (4 * id));
+	if (addr == 0) {
+		if ((id > 0) && (id < indiv_prop_start))
+			return Mem4(cpv__start + (4 * id));
 
-        accel_error("[** Programming error: tried to read (something) **]");
-        return 0;
-    }
+		accel_error("[** Programming error: tried to read (something) **]");
+		return 0;
+	}
 
-    return Mem4(addr);
+	return Mem4(addr);
 }
 
 uint Glulxe::func_13_op__pr(uint argc, uint *argv) {
-    uint obj;
-    uint id;
-    uint zr;
-
-    obj = ARG_IF_GIVEN(argv, argc, 0);
-    id = ARG_IF_GIVEN(argv, argc, 1);
-
-    zr = func_1_z__region(1, &obj);
-    if (zr == 3) {
-        /* print is INDIV_PROP_START+6 */
-        if (id == indiv_prop_start+6)
-            return 1;
-        /* print_to_array is INDIV_PROP_START+7 */
-        if (id == indiv_prop_start+7)
-            return 1;
-        return 0;
-    }
-    if (zr == 2) {
-        /* call is INDIV_PROP_START+5 */
-        return ((id == indiv_prop_start+5) ? 1 : 0);
-    }
-    if (zr != 1)
-        return 0;
-
-    if ((id >= indiv_prop_start) && (id < indiv_prop_start+8)) {
-        if (obj_in_class(obj))
-            return 1;
-    }
-
-    return ((func_9_ra__pr(argc, argv)) ? 1 : 0);
+	uint obj;
+	uint id;
+	uint zr;
+
+	obj = ARG_IF_GIVEN(argv, argc, 0);
+	id = ARG_IF_GIVEN(argv, argc, 1);
+
+	zr = func_1_z__region(1, &obj);
+	if (zr == 3) {
+		/* print is INDIV_PROP_START+6 */
+		if (id == indiv_prop_start + 6)
+			return 1;
+		/* print_to_array is INDIV_PROP_START+7 */
+		if (id == indiv_prop_start + 7)
+			return 1;
+		return 0;
+	}
+	if (zr == 2) {
+		/* call is INDIV_PROP_START+5 */
+		return ((id == indiv_prop_start + 5) ? 1 : 0);
+	}
+	if (zr != 1)
+		return 0;
+
+	if ((id >= indiv_prop_start) && (id < indiv_prop_start + 8)) {
+		if (obj_in_class(obj))
+			return 1;
+	}
+
+	return ((func_9_ra__pr(argc, argv)) ? 1 : 0);
 }
 
 } // End of namespace Glulxe
diff --git a/engines/glk/glulxe/exec.cpp b/engines/glk/glulxe/exec.cpp
index f314ca4..55cd918 100644
--- a/engines/glk/glulxe/exec.cpp
+++ b/engines/glk/glulxe/exec.cpp
@@ -26,1042 +26,1023 @@ namespace Glk {
 namespace Glulxe {
 
 void Glulxe::execute_loop() {
-  bool done_executing = false;
-  int ix;
-  uint opcode;
-  const operandlist_t *oplist;
-  oparg_t inst[MAX_OPERANDS];
-  uint value, addr, val0, val1;
-  int vals0, vals1;
-  uint *arglist;
-  uint arglistfix[3];
+	bool done_executing = false;
+	int ix;
+	uint opcode;
+	const operandlist_t *oplist;
+	oparg_t inst[MAX_OPERANDS];
+	uint value, addr, val0, val1;
+	int vals0, vals1;
+	uint *arglist;
+	uint arglistfix[3];
 #ifdef FLOAT_SUPPORT
-  gfloat32 valf, valf1, valf2;
+	gfloat32 valf, valf1, valf2;
 #endif /* FLOAT_SUPPORT */
 
-  while (!done_executing) {
-
-    profile_tick();
-    debugger_tick();
-    /* Do OS-specific processing, if appropriate. */
-    glk_tick();
-    
-    /* Stash the current opcode's address, in case the interpreter needs to serialize the VM state out-of-band. */
-    prevpc = pc;
-    
-    /* Fetch the opcode number. */
-    opcode = Mem1(pc);
-    pc++;
-    if (opcode & 0x80) {
-      /* More than one-byte opcode. */
-      if (opcode & 0x40) {
-        /* Four-byte opcode */
-        opcode &= 0x3F;
-        opcode = (opcode << 8) | Mem1(pc);
-        pc++;
-        opcode = (opcode << 8) | Mem1(pc);
-        pc++;
-        opcode = (opcode << 8) | Mem1(pc);
-        pc++;
-      }
-      else {
-        /* Two-byte opcode */
-        opcode &= 0x7F;
-        opcode = (opcode << 8) | Mem1(pc);
-        pc++;
-      }
-    }
-
-    /* Now we have an opcode number. */
-    
-    /* Fetch the structure that describes how the operands for this
-       opcode are arranged. This is a pointer to an immutable, 
-       static object. */
-    if (opcode < 0x80)
-      oplist = fast_operandlist[opcode];
-    else
-      oplist = lookup_operandlist(opcode);
-
-    if (!oplist)
-      fatal_error_i("Encountered unknown opcode.", opcode);
-
-    /* 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);
-
-    /* Perform the opcode. This switch statement is split in two, based
-       on some paranoid suspicions about the ability of compilers to
-       optimize large-range switches. Ignore that. */
-
-    if (opcode < 0x80) {
-
-      switch (opcode) {
-
-      case op_nop:
-        break;
-
-      case op_add:
-        value = inst[0].value + inst[1].value;
-        store_operand(inst[2].desttype, inst[2].value, value);
-        break;
-      case op_sub:
-        value = inst[0].value - inst[1].value;
-        store_operand(inst[2].desttype, inst[2].value, value);
-        break;
-      case op_mul:
-        value = inst[0].value * inst[1].value;
-        store_operand(inst[2].desttype, inst[2].value, value);
-        break;
-      case op_div:
-        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. 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 = -(int)(val0 / val1);
-          }
-        }
-        else {
-          val0 = vals0;
-          if (vals1 < 0) {
-            val1 = (-vals1);
-            value = -(int)(val0 / val1);
-          }
-          else {
-            val1 = vals1;
-            value = val0 / val1;
-          }
-        }
-        store_operand(inst[2].desttype, inst[2].value, value);
-        break;
-      case op_mod:
-        vals0 = inst[0].value;
-        vals1 = inst[1].value;
-        if (vals1 == 0)
-          fatal_error("Division by zero doing remainder.");
-        if (vals1 < 0) {
-            val1 = -vals1;
-        }
-        else {
-            val1 = vals1;
-        }
-        if (vals0 < 0) {
-          val0 = (-vals0);
-          value = -(int)(val0 % val1);
-        }
-        else {
-          val0 = vals0;
-          value = val0 % val1;
-        }
-        store_operand(inst[2].desttype, inst[2].value, value);
-        break;
-      case op_neg:
-        vals0 = inst[0].value;
-        value = (-vals0);
-        store_operand(inst[1].desttype, inst[1].value, value);
-        break;
-
-      case op_bitand:
-        value = (inst[0].value & inst[1].value);
-        store_operand(inst[2].desttype, inst[2].value, value);
-        break;
-      case op_bitor:
-        value = (inst[0].value | inst[1].value);
-        store_operand(inst[2].desttype, inst[2].value, value);
-        break;
-      case op_bitxor:
-        value = (inst[0].value ^ inst[1].value);
-        store_operand(inst[2].desttype, inst[2].value, value);
-        break;
-      case op_bitnot:
-        value = ~(inst[0].value);
-        store_operand(inst[1].desttype, inst[1].value, value);
-        break;
-
-      case op_shiftl:
-        vals0 = inst[1].value;
-        if (vals0 < 0 || vals0 >= 32)
-          value = 0;
-        else
-          value = ((uint)(inst[0].value) << (uint)vals0);
-        store_operand(inst[2].desttype, inst[2].value, value);
-        break;
-      case op_ushiftr:
-        vals0 = inst[1].value;
-        if (vals0 < 0 || vals0 >= 32)
-          value = 0;
-        else
-          value = ((uint)(inst[0].value) >> (uint)vals0);
-        store_operand(inst[2].desttype, inst[2].value, value);
-        break;
-      case op_sshiftr:
-        vals0 = inst[1].value;
-        if (vals0 < 0 || vals0 >= 32) {
-          if (inst[0].value & 0x80000000)
-            value = 0xFFFFFFFF;
-          else
-            value = 0;
-        }
-        else {
-          /* 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 = ((int)(inst[0].value) >> (int)vals0);
-        }
-        store_operand(inst[2].desttype, inst[2].value, value);
-        break;
-
-      case op_jump:
-        value = inst[0].value;
-        /* fall through to PerformJump label. */
-
-      PerformJump: /* goto label for successful jumping... ironic, no? */
-        if (value == 0 || value == 1) {
-          /* Return from function. This is exactly what happens in
-             return_op, but it's only a few lines of code, so I won't
-             bother with a "goto". */
-          leave_function();
-          if (stackptr == 0) {
-            done_executing = true;
-            break;
-          }
-          pop_callstub(value); /* zero or one */
-        }
-        else {
-          /* Branch to a new PC value. */
-          pc = (pc + value - 2);
-        }
-        break;
-
-      case op_jz:
-        if (inst[0].value == 0) {
-          value = inst[1].value;
-          goto PerformJump;
-        }
-        break;
-      case op_jnz:
-        if (inst[0].value != 0) {
-          value = inst[1].value;
-          goto PerformJump;
-        }
-        break;
-      case op_jeq:
-        if (inst[0].value == inst[1].value) {
-          value = inst[2].value;
-          goto PerformJump;
-        }
-        break;
-      case op_jne:
-        if (inst[0].value != inst[1].value) {
-          value = inst[2].value;
-          goto PerformJump;
-        }
-        break;
-      case op_jlt:
-        vals0 = inst[0].value;
-        vals1 = inst[1].value;
-        if (vals0 < vals1) {
-          value = inst[2].value;
-          goto PerformJump;
-        }
-        break;
-      case op_jgt:
-        vals0 = inst[0].value;
-        vals1 = inst[1].value;
-        if (vals0 > vals1) {
-          value = inst[2].value;
-          goto PerformJump;
-        }
-        break;
-      case op_jle:
-        vals0 = inst[0].value;
-        vals1 = inst[1].value;
-        if (vals0 <= vals1) {
-          value = inst[2].value;
-          goto PerformJump;
-        }
-        break;
-      case op_jge:
-        vals0 = inst[0].value;
-        vals1 = inst[1].value;
-        if (vals0 >= vals1) {
-          value = inst[2].value;
-          goto PerformJump;
-        }
-        break;
-      case op_jltu:
-        val0 = inst[0].value;
-        val1 = inst[1].value;
-        if (val0 < val1) {
-          value = inst[2].value;
-          goto PerformJump;
-        }
-        break;
-      case op_jgtu:
-        val0 = inst[0].value;
-        val1 = inst[1].value;
-        if (val0 > val1) {
-          value = inst[2].value;
-          goto PerformJump;
-        }
-        break;
-      case op_jleu:
-        val0 = inst[0].value;
-        val1 = inst[1].value;
-        if (val0 <= val1) {
-          value = inst[2].value;
-          goto PerformJump;
-        }
-        break;
-      case op_jgeu:
-        val0 = inst[0].value;
-        val1 = inst[1].value;
-        if (val0 >= val1) {
-          value = inst[2].value;
-          goto PerformJump;
-        }
-        break;
-
-      case op_call:
-        value = inst[1].value;
-        arglist = pop_arguments(value, 0);
-        push_callstub(inst[2].desttype, inst[2].value);
-        enter_function(inst[0].value, value, arglist);
-        break;
-      case op_return:
-        leave_function();
-        if (stackptr == 0) {
-          done_executing = true;
-          break;
-        }
-        pop_callstub(inst[0].value);
-        break;
-      case op_tailcall:
-        value = inst[1].value;
-        arglist = pop_arguments(value, 0);
-        leave_function();
-        enter_function(inst[0].value, value, arglist);
-        break;
-
-      case op_catch:
-        push_callstub(inst[0].desttype, inst[0].value);
-        value = inst[1].value;
-        val0 = stackptr;
-        store_operand(inst[0].desttype, inst[0].value, val0);
-        goto PerformJump;
-        break;
-      case op_throw:
-        profile_fail("throw");
-        value = inst[0].value;
-        stackptr = inst[1].value;
-        pop_callstub(value);
-        break;
-
-      case op_copy:
-        value = inst[0].value;
+	while (!done_executing) {
+
+		profile_tick();
+		debugger_tick();
+		/* Do OS-specific processing, if appropriate. */
+		glk_tick();
+
+		/* Stash the current opcode's address, in case the interpreter needs to serialize the VM state out-of-band. */
+		prevpc = pc;
+
+		/* Fetch the opcode number. */
+		opcode = Mem1(pc);
+		pc++;
+		if (opcode & 0x80) {
+			/* More than one-byte opcode. */
+			if (opcode & 0x40) {
+				/* Four-byte opcode */
+				opcode &= 0x3F;
+				opcode = (opcode << 8) | Mem1(pc);
+				pc++;
+				opcode = (opcode << 8) | Mem1(pc);
+				pc++;
+				opcode = (opcode << 8) | Mem1(pc);
+				pc++;
+			} else {
+				/* Two-byte opcode */
+				opcode &= 0x7F;
+				opcode = (opcode << 8) | Mem1(pc);
+				pc++;
+			}
+		}
+
+		/* Now we have an opcode number. */
+
+		/* Fetch the structure that describes how the operands for this
+		   opcode are arranged. This is a pointer to an immutable,
+		   static object. */
+		if (opcode < 0x80)
+			oplist = fast_operandlist[opcode];
+		else
+			oplist = lookup_operandlist(opcode);
+
+		if (!oplist)
+			fatal_error_i("Encountered unknown opcode.", opcode);
+
+		/* 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);
+
+		/* Perform the opcode. This switch statement is split in two, based
+		   on some paranoid suspicions about the ability of compilers to
+		   optimize large-range switches. Ignore that. */
+
+		if (opcode < 0x80) {
+
+			switch (opcode) {
+
+			case op_nop:
+				break;
+
+			case op_add:
+				value = inst[0].value + inst[1].value;
+				store_operand(inst[2].desttype, inst[2].value, value);
+				break;
+			case op_sub:
+				value = inst[0].value - inst[1].value;
+				store_operand(inst[2].desttype, inst[2].value, value);
+				break;
+			case op_mul:
+				value = inst[0].value * inst[1].value;
+				store_operand(inst[2].desttype, inst[2].value, value);
+				break;
+			case op_div:
+				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. 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 = -(int)(val0 / val1);
+					}
+				} else {
+					val0 = vals0;
+					if (vals1 < 0) {
+						val1 = (-vals1);
+						value = -(int)(val0 / val1);
+					} else {
+						val1 = vals1;
+						value = val0 / val1;
+					}
+				}
+				store_operand(inst[2].desttype, inst[2].value, value);
+				break;
+			case op_mod:
+				vals0 = inst[0].value;
+				vals1 = inst[1].value;
+				if (vals1 == 0)
+					fatal_error("Division by zero doing remainder.");
+				if (vals1 < 0) {
+					val1 = -vals1;
+				} else {
+					val1 = vals1;
+				}
+				if (vals0 < 0) {
+					val0 = (-vals0);
+					value = -(int)(val0 % val1);
+				} else {
+					val0 = vals0;
+					value = val0 % val1;
+				}
+				store_operand(inst[2].desttype, inst[2].value, value);
+				break;
+			case op_neg:
+				vals0 = inst[0].value;
+				value = (-vals0);
+				store_operand(inst[1].desttype, inst[1].value, value);
+				break;
+
+			case op_bitand:
+				value = (inst[0].value & inst[1].value);
+				store_operand(inst[2].desttype, inst[2].value, value);
+				break;
+			case op_bitor:
+				value = (inst[0].value | inst[1].value);
+				store_operand(inst[2].desttype, inst[2].value, value);
+				break;
+			case op_bitxor:
+				value = (inst[0].value ^ inst[1].value);
+				store_operand(inst[2].desttype, inst[2].value, value);
+				break;
+			case op_bitnot:
+				value = ~(inst[0].value);
+				store_operand(inst[1].desttype, inst[1].value, value);
+				break;
+
+			case op_shiftl:
+				vals0 = inst[1].value;
+				if (vals0 < 0 || vals0 >= 32)
+					value = 0;
+				else
+					value = ((uint)(inst[0].value) << (uint)vals0);
+				store_operand(inst[2].desttype, inst[2].value, value);
+				break;
+			case op_ushiftr:
+				vals0 = inst[1].value;
+				if (vals0 < 0 || vals0 >= 32)
+					value = 0;
+				else
+					value = ((uint)(inst[0].value) >> (uint)vals0);
+				store_operand(inst[2].desttype, inst[2].value, value);
+				break;
+			case op_sshiftr:
+				vals0 = inst[1].value;
+				if (vals0 < 0 || vals0 >= 32) {
+					if (inst[0].value & 0x80000000)
+						value = 0xFFFFFFFF;
+					else
+						value = 0;
+				} else {
+					/* 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 = ((int)(inst[0].value) >> (int)vals0);
+				}
+				store_operand(inst[2].desttype, inst[2].value, value);
+				break;
+
+			case op_jump:
+				value = inst[0].value;
+				/* fall through to PerformJump label. */
+
+PerformJump: /* goto label for successful jumping... ironic, no? */
+				if (value == 0 || value == 1) {
+					/* Return from function. This is exactly what happens in
+					   return_op, but it's only a few lines of code, so I won't
+					   bother with a "goto". */
+					leave_function();
+					if (stackptr == 0) {
+						done_executing = true;
+						break;
+					}
+					pop_callstub(value); /* zero or one */
+				} else {
+					/* Branch to a new PC value. */
+					pc = (pc + value - 2);
+				}
+				break;
+
+			case op_jz:
+				if (inst[0].value == 0) {
+					value = inst[1].value;
+					goto PerformJump;
+				}
+				break;
+			case op_jnz:
+				if (inst[0].value != 0) {
+					value = inst[1].value;
+					goto PerformJump;
+				}
+				break;
+			case op_jeq:
+				if (inst[0].value == inst[1].value) {
+					value = inst[2].value;
+					goto PerformJump;
+				}
+				break;
+			case op_jne:
+				if (inst[0].value != inst[1].value) {
+					value = inst[2].value;
+					goto PerformJump;
+				}
+				break;
+			case op_jlt:
+				vals0 = inst[0].value;
+				vals1 = inst[1].value;
+				if (vals0 < vals1) {
+					value = inst[2].value;
+					goto PerformJump;
+				}
+				break;
+			case op_jgt:
+				vals0 = inst[0].value;
+				vals1 = inst[1].value;
+				if (vals0 > vals1) {
+					value = inst[2].value;
+					goto PerformJump;
+				}
+				break;
+			case op_jle:
+				vals0 = inst[0].value;
+				vals1 = inst[1].value;
+				if (vals0 <= vals1) {
+					value = inst[2].value;
+					goto PerformJump;
+				}
+				break;
+			case op_jge:
+				vals0 = inst[0].value;
+				vals1 = inst[1].value;
+				if (vals0 >= vals1) {
+					value = inst[2].value;
+					goto PerformJump;
+				}
+				break;
+			case op_jltu:
+				val0 = inst[0].value;
+				val1 = inst[1].value;
+				if (val0 < val1) {
+					value = inst[2].value;
+					goto PerformJump;
+				}
+				break;
+			case op_jgtu:
+				val0 = inst[0].value;
+				val1 = inst[1].value;
+				if (val0 > val1) {
+					value = inst[2].value;
+					goto PerformJump;
+				}
+				break;
+			case op_jleu:
+				val0 = inst[0].value;
+				val1 = inst[1].value;
+				if (val0 <= val1) {
+					value = inst[2].value;
+					goto PerformJump;
+				}
+				break;
+			case op_jgeu:
+				val0 = inst[0].value;
+				val1 = inst[1].value;
+				if (val0 >= val1) {
+					value = inst[2].value;
+					goto PerformJump;
+				}
+				break;
+
+			case op_call:
+				value = inst[1].value;
+				arglist = pop_arguments(value, 0);
+				push_callstub(inst[2].desttype, inst[2].value);
+				enter_function(inst[0].value, value, arglist);
+				break;
+			case op_return:
+				leave_function();
+				if (stackptr == 0) {
+					done_executing = true;
+					break;
+				}
+				pop_callstub(inst[0].value);
+				break;
+			case op_tailcall:
+				value = inst[1].value;
+				arglist = pop_arguments(value, 0);
+				leave_function();
+				enter_function(inst[0].value, value, arglist);
+				break;
+
+			case op_catch:
+				push_callstub(inst[0].desttype, inst[0].value);
+				value = inst[1].value;
+				val0 = stackptr;
+				store_operand(inst[0].desttype, inst[0].value, val0);
+				goto PerformJump;
+				break;
+			case op_throw:
+				profile_fail("throw");
+				value = inst[0].value;
+				stackptr = inst[1].value;
+				pop_callstub(value);
+				break;
+
+			case op_copy:
+				value = inst[0].value;
 #ifdef TOLERATE_SUPERGLUS_BUG
-        if (inst[1].desttype == 1 && inst[1].value == 0)
-            inst[1].desttype = 0;
+				if (inst[1].desttype == 1 && inst[1].value == 0)
+					inst[1].desttype = 0;
 #endif /* TOLERATE_SUPERGLUS_BUG */
-        store_operand(inst[1].desttype, inst[1].value, value);
-        break;
-      case op_copys:
-        value = inst[0].value;
-        store_operand_s(inst[1].desttype, inst[1].value, value);
-        break;
-      case op_copyb:
-        value = inst[0].value;
-        store_operand_b(inst[1].desttype, inst[1].value, value);
-        break;
-
-      case op_sexs:
-        val0 = inst[0].value;
-        if (val0 & 0x8000)
-          val0 |= 0xFFFF0000;
-        else
-          val0 &= 0x0000FFFF;
-        store_operand(inst[1].desttype, inst[1].value, val0);
-        break;
-      case op_sexb:
-        val0 = inst[0].value;
-        if (val0 & 0x80)
-          val0 |= 0xFFFFFF00;
-        else
-          val0 &= 0x000000FF;
-        store_operand(inst[1].desttype, inst[1].value, val0);
-        break;
-
-      case op_aload:
-        value = inst[0].value;
-        value += 4 * inst[1].value;
-        val0 = Mem4(value);
-        store_operand(inst[2].desttype, inst[2].value, val0);
-        break;
-      case op_aloads:
-        value = inst[0].value;
-        value += 2 * inst[1].value;
-        val0 = Mem2(value);
-        store_operand(inst[2].desttype, inst[2].value, val0);
-        break;
-      case op_aloadb:
-        value = inst[0].value;
-        value += inst[1].value;
-        val0 = Mem1(value);
-        store_operand(inst[2].desttype, inst[2].value, val0);
-        break;
-      case op_aloadbit:
-        value = inst[0].value;
-        vals0 = inst[1].value;
-        val1 = (vals0 & 7);
-        if (vals0 >= 0)
-          value += (vals0 >> 3);
-        else
-          value -= (1 + ((-1 - vals0) >> 3));
-        if (Mem1(value) & (1 << val1))
-          val0 = 1;
-        else
-          val0 = 0;
-        store_operand(inst[2].desttype, inst[2].value, val0);
-        break;
-
-      case op_astore:
-        value = inst[0].value;
-        value += 4 * inst[1].value;
-        val0 = inst[2].value;
-        MemW4(value, val0);
-        break;
-      case op_astores:
-        value = inst[0].value;
-        value += 2 * inst[1].value;
-        val0 = inst[2].value;
-        MemW2(value, val0);
-        break;
-      case op_astoreb:
-        value = inst[0].value;
-        value += inst[1].value;
-        val0 = inst[2].value;
-        MemW1(value, val0);
-        break;
-      case op_astorebit:
-        value = inst[0].value;
-        vals0 = inst[1].value;
-        val1 = (vals0 & 7);
-        if (vals0 >= 0)
-          value += (vals0 >> 3);
-        else
-          value -= (1 + ((-1 - vals0) >> 3));
-        val0 = Mem1(value);
-        if (inst[2].value)
-          val0 |= (1 << val1);
-        else
-          val0 &= ~((uint)(1 << val1));
-        MemW1(value, val0);
-        break;
-
-      case op_stkcount:
-        value = (stackptr - valstackbase) / 4;
-        store_operand(inst[0].desttype, inst[0].value, value);
-        break;
-      case op_stkpeek:
-        vals0 = inst[0].value * 4;
-        if (vals0 < 0 || vals0 >= (int)(stackptr - valstackbase))
-          fatal_error("Stkpeek outside current stack range.");
-        value = Stk4(stackptr - (vals0+4));
-        store_operand(inst[1].desttype, inst[1].value, value);
-        break;
-      case op_stkswap:
-        if (stackptr < valstackbase+8) {
-          fatal_error("Stack underflow in stkswap.");
-        }
-        val0 = Stk4(stackptr-4);
-        val1 = Stk4(stackptr-8);
-        StkW4(stackptr-4, val1);
-        StkW4(stackptr-8, val0);
-        break;
-      case op_stkcopy:
-        vals0 = inst[0].value;
-        if (vals0 < 0)
-          fatal_error("Negative operand in stkcopy.");
-        if (vals0 == 0)
-          break;
-        if (stackptr < valstackbase+vals0*4)
-          fatal_error("Stack underflow in stkcopy.");
-        if (stackptr + vals0*4 > stacksize) 
-          fatal_error("Stack overflow in stkcopy.");
-        addr = stackptr - vals0*4;
-        for (ix=0; ix<vals0; ix++) {
-          value = Stk4(addr + ix*4);
-          StkW4(stackptr + ix*4, value);
-        }
-        stackptr += vals0*4;
-        break;
-      case op_stkroll:
-        vals0 = inst[0].value;
-        vals1 = inst[1].value;
-        if (vals0 < 0)
-          fatal_error("Negative operand in stkroll.");
-        if (stackptr < valstackbase+vals0*4)
-          fatal_error("Stack underflow in stkroll.");
-        if (vals0 == 0)
-          break;
-        /* The following is a bit ugly. We want to do vals1 = vals0-vals1,
-           because rolling down is sort of easier than rolling up. But
-           we also want to take the result mod vals0. The % operator is
-           annoying for negative numbers, so we need to do this in two 
-           cases. */
-        if (vals1 > 0) {
-          vals1 = vals1 % vals0;
-          vals1 = (vals0) - vals1;
-        }
-        else {
-          vals1 = (-vals1) % vals0;
-        }
-        if (vals1 == 0)
-          break;
-        addr = stackptr - vals0*4;
-        for (ix=0; ix<vals1; ix++) {
-          value = Stk4(addr + ix*4);
-          StkW4(stackptr + ix*4, value);
-        }
-        for (ix=0; ix<vals0; ix++) {
-          value = Stk4(addr + (vals1+ix)*4);
-          StkW4(addr + ix*4, value);
-        }
-        break;
-
-      case op_streamchar:
-        profile_in(0xE0000001, stackptr, false);
-        value = inst[0].value & 0xFF;
-        (this->*stream_char_handler)(value);
-        profile_out(stackptr);
-        break;
-      case op_streamunichar:
-        profile_in(0xE0000002, stackptr, false);
-        value = inst[0].value;
-        (this->*stream_unichar_handler)(value);
-        profile_out(stackptr);
-        break;
-      case op_streamnum:
-        profile_in(0xE0000003, stackptr, false);
-        vals0 = inst[0].value;
-        stream_num(vals0, false, 0);
-        profile_out(stackptr);
-        break;
-      case op_streamstr:
-        profile_in(0xE0000004, stackptr, false);
-        stream_string(inst[0].value, 0, 0);
-        profile_out(stackptr);
-        break;
-
-      default:
-        fatal_error_i("Executed unknown opcode.", opcode);
-      }
-    }
-    else {
-
-      switch (opcode) {
-
-      case op_gestalt:
-        value = do_gestalt(inst[0].value, inst[1].value);
-        store_operand(inst[2].desttype, inst[2].value, value);
-        break;
-
-      case op_debugtrap:
+				store_operand(inst[1].desttype, inst[1].value, value);
+				break;
+			case op_copys:
+				value = inst[0].value;
+				store_operand_s(inst[1].desttype, inst[1].value, value);
+				break;
+			case op_copyb:
+				value = inst[0].value;
+				store_operand_b(inst[1].desttype, inst[1].value, value);
+				break;
+
+			case op_sexs:
+				val0 = inst[0].value;
+				if (val0 & 0x8000)
+					val0 |= 0xFFFF0000;
+				else
+					val0 &= 0x0000FFFF;
+				store_operand(inst[1].desttype, inst[1].value, val0);
+				break;
+			case op_sexb:
+				val0 = inst[0].value;
+				if (val0 & 0x80)
+					val0 |= 0xFFFFFF00;
+				else
+					val0 &= 0x000000FF;
+				store_operand(inst[1].desttype, inst[1].value, val0);
+				break;
+
+			case op_aload:
+				value = inst[0].value;
+				value += 4 * inst[1].value;
+				val0 = Mem4(value);
+				store_operand(inst[2].desttype, inst[2].value, val0);
+				break;
+			case op_aloads:
+				value = inst[0].value;
+				value += 2 * inst[1].value;
+				val0 = Mem2(value);
+				store_operand(inst[2].desttype, inst[2].value, val0);
+				break;
+			case op_aloadb:
+				value = inst[0].value;
+				value += inst[1].value;
+				val0 = Mem1(value);
+				store_operand(inst[2].desttype, inst[2].value, val0);
+				break;
+			case op_aloadbit:
+				value = inst[0].value;
+				vals0 = inst[1].value;
+				val1 = (vals0 & 7);
+				if (vals0 >= 0)
+					value += (vals0 >> 3);
+				else
+					value -= (1 + ((-1 - vals0) >> 3));
+				if (Mem1(value) & (1 << val1))
+					val0 = 1;
+				else
+					val0 = 0;
+				store_operand(inst[2].desttype, inst[2].value, val0);
+				break;
+
+			case op_astore:
+				value = inst[0].value;
+				value += 4 * inst[1].value;
+				val0 = inst[2].value;
+				MemW4(value, val0);
+				break;
+			case op_astores:
+				value = inst[0].value;
+				value += 2 * inst[1].value;
+				val0 = inst[2].value;
+				MemW2(value, val0);
+				break;
+			case op_astoreb:
+				value = inst[0].value;
+				value += inst[1].value;
+				val0 = inst[2].value;
+				MemW1(value, val0);
+				break;
+			case op_astorebit:
+				value = inst[0].value;
+				vals0 = inst[1].value;
+				val1 = (vals0 & 7);
+				if (vals0 >= 0)
+					value += (vals0 >> 3);
+				else
+					value -= (1 + ((-1 - vals0) >> 3));
+				val0 = Mem1(value);
+				if (inst[2].value)
+					val0 |= (1 << val1);
+				else
+					val0 &= ~((uint)(1 << val1));
+				MemW1(value, val0);
+				break;
+
+			case op_stkcount:
+				value = (stackptr - valstackbase) / 4;
+				store_operand(inst[0].desttype, inst[0].value, value);
+				break;
+			case op_stkpeek:
+				vals0 = inst[0].value * 4;
+				if (vals0 < 0 || vals0 >= (int)(stackptr - valstackbase))
+					fatal_error("Stkpeek outside current stack range.");
+				value = Stk4(stackptr - (vals0 + 4));
+				store_operand(inst[1].desttype, inst[1].value, value);
+				break;
+			case op_stkswap:
+				if (stackptr < valstackbase + 8) {
+					fatal_error("Stack underflow in stkswap.");
+				}
+				val0 = Stk4(stackptr - 4);
+				val1 = Stk4(stackptr - 8);
+				StkW4(stackptr - 4, val1);
+				StkW4(stackptr - 8, val0);
+				break;
+			case op_stkcopy:
+				vals0 = inst[0].value;
+				if (vals0 < 0)
+					fatal_error("Negative operand in stkcopy.");
+				if (vals0 == 0)
+					break;
+				if (stackptr < valstackbase + vals0 * 4)
+					fatal_error("Stack underflow in stkcopy.");
+				if (stackptr + vals0 * 4 > stacksize)
+					fatal_error("Stack overflow in stkcopy.");
+				addr = stackptr - vals0 * 4;
+				for (ix = 0; ix < vals0; ix++) {
+					value = Stk4(addr + ix * 4);
+					StkW4(stackptr + ix * 4, value);
+				}
+				stackptr += vals0 * 4;
+				break;
+			case op_stkroll:
+				vals0 = inst[0].value;
+				vals1 = inst[1].value;
+				if (vals0 < 0)
+					fatal_error("Negative operand in stkroll.");
+				if (stackptr < valstackbase + vals0 * 4)
+					fatal_error("Stack underflow in stkroll.");
+				if (vals0 == 0)
+					break;
+				/* The following is a bit ugly. We want to do vals1 = vals0-vals1,
+				   because rolling down is sort of easier than rolling up. But
+				   we also want to take the result mod vals0. The % operator is
+				   annoying for negative numbers, so we need to do this in two
+				   cases. */
+				if (vals1 > 0) {
+					vals1 = vals1 % vals0;
+					vals1 = (vals0) - vals1;
+				} else {
+					vals1 = (-vals1) % vals0;
+				}
+				if (vals1 == 0)
+					break;
+				addr = stackptr - vals0 * 4;
+				for (ix = 0; ix < vals1; ix++) {
+					value = Stk4(addr + ix * 4);
+					StkW4(stackptr + ix * 4, value);
+				}
+				for (ix = 0; ix < vals0; ix++) {
+					value = Stk4(addr + (vals1 + ix) * 4);
+					StkW4(addr + ix * 4, value);
+				}
+				break;
+
+			case op_streamchar:
+				profile_in(0xE0000001, stackptr, false);
+				value = inst[0].value & 0xFF;
+				(this->*stream_char_handler)(value);
+				profile_out(stackptr);
+				break;
+			case op_streamunichar:
+				profile_in(0xE0000002, stackptr, false);
+				value = inst[0].value;
+				(this->*stream_unichar_handler)(value);
+				profile_out(stackptr);
+				break;
+			case op_streamnum:
+				profile_in(0xE0000003, stackptr, false);
+				vals0 = inst[0].value;
+				stream_num(vals0, false, 0);
+				profile_out(stackptr);
+				break;
+			case op_streamstr:
+				profile_in(0xE0000004, stackptr, false);
+				stream_string(inst[0].value, 0, 0);
+				profile_out(stackptr);
+				break;
+
+			default:
+				fatal_error_i("Executed unknown opcode.", opcode);
+			}
+		} else {
+
+			switch (opcode) {
+
+			case op_gestalt:
+				value = do_gestalt(inst[0].value, inst[1].value);
+				store_operand(inst[2].desttype, inst[2].value, value);
+				break;
+
+			case op_debugtrap:
 #if VM_DEBUGGER
-        /* We block and handle debug commands, but only if the
-           library has invoked debug features. (Meaning, has
-           the cycle handler ever been called.) */
-        if (debugger_ever_invoked()) {
-          debugger_block_and_debug("user debugtrap, pausing...");
-          break;
-        }
+				/* We block and handle debug commands, but only if the
+				   library has invoked debug features. (Meaning, has
+				   the cycle handler ever been called.) */
+				if (debugger_ever_invoked()) {
+					debugger_block_and_debug("user debugtrap, pausing...");
+					break;
+				}
 #endif /* VM_DEBUGGER */
-        fatal_error_i("user debugtrap encountered.", inst[0].value);
-
-      case op_jumpabs:
-        pc = inst[0].value;
-        break;
-
-      case op_callf:
-        push_callstub(inst[1].desttype, inst[1].value);
-        enter_function(inst[0].value, 0, arglistfix);
-        break;
-      case op_callfi:
-        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[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[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[0].desttype, inst[0].value, endmem);
-        break;
-      case op_setmemsize:
-        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[0].desttype, inst[0].value, value);
-        break;
-      case op_setstringtbl:
-        stream_set_table(inst[0].value);
-        break;
-
-      case op_getiosys:
-        stream_get_iosys(&val0, &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[0].value, inst[1].value);
-        break;
-
-      case op_glk:
-        profile_in(0xF0000000+inst[0].value, stackptr, false);
-        value = inst[1].value;
-        arglist = pop_arguments(value, 0);
-        val0 = perform_glk(inst[0].value, value, arglist);
+				fatal_error_i("user debugtrap encountered.", inst[0].value);
+
+			case op_jumpabs:
+				pc = inst[0].value;
+				break;
+
+			case op_callf:
+				push_callstub(inst[1].desttype, inst[1].value);
+				enter_function(inst[0].value, 0, arglistfix);
+				break;
+			case op_callfi:
+				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[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[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[0].desttype, inst[0].value, endmem);
+				break;
+			case op_setmemsize:
+				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[0].desttype, inst[0].value, value);
+				break;
+			case op_setstringtbl:
+				stream_set_table(inst[0].value);
+				break;
+
+			case op_getiosys:
+				stream_get_iosys(&val0, &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[0].value, inst[1].value);
+				break;
+
+			case op_glk:
+				profile_in(0xF0000000 + inst[0].value, stackptr, false);
+				value = inst[1].value;
+				arglist = pop_arguments(value, 0);
+				val0 = perform_glk(inst[0].value, value, arglist);
 #ifdef TOLERATE_SUPERGLUS_BUG
-        if (inst[2].desttype == 1 && inst[2].value == 0)
-            inst[2].desttype = 0;
+				if (inst[2].desttype == 1 && inst[2].value == 0)
+					inst[2].desttype = 0;
 #endif /* TOLERATE_SUPERGLUS_BUG */
-        store_operand(inst[2].desttype, inst[2].value, val0);
-        profile_out(stackptr);
-        break;
-
-      case op_random:
-        vals0 = inst[0].value;
-        if (vals0 == 0)
-          value = glulx_random();
-        else if (vals0 >= 1)
-          value = glulx_random() % (uint)(vals0);
-        else 
-          value = -(int)(glulx_random() % (uint)(-vals0));
-        store_operand(inst[1].desttype, inst[1].value, value);
-        break;
-      case op_setrandom:
-        glulx_setrandom(inst[0].value);
-        break;
-
-      case op_verify:
-        value = perform_verify();
-        store_operand(inst[0].desttype, inst[0].value, value);
-        break;
-
-      case op_restart:
-        profile_fail("restart");
-        vm_restart();
-        break;
-
-      case op_protect:
-        val0 = inst[0].value;
-        val1 = val0 + inst[1].value;
-        if (val0 == val1) {
-          val0 = 0;
-          val1 = 0;
-        }
-        protectstart = val0;
-        protectend = val1;
-        break;
-
-      case op_save:
-        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:
-        value = perform_restore(find_stream_by_id(inst[0].value), false);
-        if (value == 0) {
-          /* We've succeeded, and the stack now contains the callstub
-             saved during saveundo. Ignore this opcode's operand. */
-          value = (uint)-1;
-          pop_callstub(value);
-        }
-        else {
-          /* We've failed, so we must store the failure in this opcode's
-             operand. */
-          store_operand(inst[1].desttype, inst[1].value, value);
-        }
-        break;
-
-      case op_saveundo:
-        push_callstub(inst[0].desttype, inst[0].value);
-        value = perform_saveundo();
-        pop_callstub(value);
-        break;
-
-      case op_restoreundo:
-        value = perform_restoreundo();
-        if (value == 0) {
-          /* We've succeeded, and the stack now contains the callstub
-             saved during saveundo. Ignore this opcode's operand. */
-          value = (uint)-1;
-          pop_callstub(value);
-        }
-        else {
-          /* We've failed, so we must store the failure in this opcode's
-             operand. */
-          store_operand(inst[0].desttype, inst[0].value, value);
-        }
-        break;
-
-      case op_quit:
-        done_executing = true;
-        break;
-
-      case op_linearsearch:
-        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[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[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: {
-        uint lx;
-        uint count = inst[0].value;
-        addr = inst[1].value;
-        for (lx=0; lx<count; lx++, addr++) {
-          MemW1(addr, 0);
-        }
-        }
-        break;
-      case op_mcopy: {
-        uint lx;
-        uint count = inst[0].value;
-        uint addrsrc = inst[1].value;
-        uint addrdest = inst[2].value;
-        if (addrdest < addrsrc) {
-          for (lx=0; lx<count; lx++, addrsrc++, addrdest++) {
-            value = Mem1(addrsrc);
-            MemW1(addrdest, value);
-          }
-        }
-        else {
-          addrsrc += (count-1);
-          addrdest += (count-1);
-          for (lx=0; lx<count; lx++, addrsrc--, addrdest--) {
-            value = Mem1(addrsrc);
-            MemW1(addrdest, value);
-          }
-        }
-        }
-        break;
-      case op_malloc:
-        value = heap_alloc(inst[0].value);
-        store_operand(inst[1].desttype, inst[1].value, value);
-        break;
-      case op_mfree:
-        heap_free(inst[0].value);
-        break;
-
-      case op_accelfunc:
-        accel_set_func(inst[0].value, inst[1].value);
-        break;
-      case op_accelparam:
-        accel_set_param(inst[0].value, inst[1].value);
-        break;
+				store_operand(inst[2].desttype, inst[2].value, val0);
+				profile_out(stackptr);
+				break;
+
+			case op_random:
+				vals0 = inst[0].value;
+				if (vals0 == 0)
+					value = glulx_random();
+				else if (vals0 >= 1)
+					value = glulx_random() % (uint)(vals0);
+				else
+					value = -(int)(glulx_random() % (uint)(-vals0));
+				store_operand(inst[1].desttype, inst[1].value, value);
+				break;
+			case op_setrandom:
+				glulx_setrandom(inst[0].value);
+				break;
+
+			case op_verify:
+				value = perform_verify();
+				store_operand(inst[0].desttype, inst[0].value, value);
+				break;
+
+			case op_restart:
+				profile_fail("restart");
+				vm_restart();
+				break;
+
+			case op_protect:
+				val0 = inst[0].value;
+				val1 = val0 + inst[1].value;
+				if (val0 == val1) {
+					val0 = 0;
+					val1 = 0;
+				}
+				protectstart = val0;
+				protectend = val1;
+				break;
+
+			case op_save:
+				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:
+				value = perform_restore(find_stream_by_id(inst[0].value), false);
+				if (value == 0) {
+					/* We've succeeded, and the stack now contains the callstub
+					   saved during saveundo. Ignore this opcode's operand. */
+					value = (uint) - 1;
+					pop_callstub(value);
+				} else {
+					/* We've failed, so we must store the failure in this opcode's
+					   operand. */
+					store_operand(inst[1].desttype, inst[1].value, value);
+				}
+				break;
+
+			case op_saveundo:
+				push_callstub(inst[0].desttype, inst[0].value);
+				value = perform_saveundo();
+				pop_callstub(value);
+				break;
+
+			case op_restoreundo:
+				value = perform_restoreundo();
+				if (value == 0) {
+					/* We've succeeded, and the stack now contains the callstub
+					   saved during saveundo. Ignore this opcode's operand. */
+					value = (uint) - 1;
+					pop_callstub(value);
+				} else {
+					/* We've failed, so we must store the failure in this opcode's
+					   operand. */
+					store_operand(inst[0].desttype, inst[0].value, value);
+				}
+				break;
+
+			case op_quit:
+				done_executing = true;
+				break;
+
+			case op_linearsearch:
+				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[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[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: {
+				uint lx;
+				uint count = inst[0].value;
+				addr = inst[1].value;
+				for (lx = 0; lx < count; lx++, addr++) {
+					MemW1(addr, 0);
+				}
+			}
+			break;
+			case op_mcopy: {
+				uint lx;
+				uint count = inst[0].value;
+				uint addrsrc = inst[1].value;
+				uint addrdest = inst[2].value;
+				if (addrdest < addrsrc) {
+					for (lx = 0; lx < count; lx++, addrsrc++, addrdest++) {
+						value = Mem1(addrsrc);
+						MemW1(addrdest, value);
+					}
+				} else {
+					addrsrc += (count - 1);
+					addrdest += (count - 1);
+					for (lx = 0; lx < count; lx++, addrsrc--, addrdest--) {
+						value = Mem1(addrsrc);
+						MemW1(addrdest, value);
+					}
+				}
+			}
+			break;
+			case op_malloc:
+				value = heap_alloc(inst[0].value);
+				store_operand(inst[1].desttype, inst[1].value, value);
+				break;
+			case op_mfree:
+				heap_free(inst[0].value);
+				break;
+
+			case op_accelfunc:
+				accel_set_func(inst[0].value, inst[1].value);
+				break;
+			case op_accelparam:
+				accel_set_param(inst[0].value, inst[1].value);
+				break;
 
 #ifdef FLOAT_SUPPORT
 
-      case op_numtof:
-        vals0 = inst[0].value;
-        value = encode_float((gfloat32)vals0);
-        store_operand(inst[1].desttype, inst[1].value, value);
-        break;
-      case op_ftonumz:
-        valf = decode_float(inst[0].value);
-        if (!signbit(valf)) {
-          if (isnan(valf) || isinf(valf) || (valf > 2147483647.0))
-            vals0 = 0x7FFFFFFF;
-          else
-            vals0 = (int)(truncf(valf));
-        }
-        else {
-          if (isnan(valf) || isinf(valf) || (valf < -2147483647.0))
-            vals0 = 0x80000000;
-          else
-            vals0 = (int)(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 = (int)(roundf(valf));
-        }
-        else {
-          if (isnan(valf) || isinf(valf) || (valf < -2147483647.0))
-            vals0 = 0x80000000;
-          else
-            vals0 = (int)(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;
+			case op_numtof:
+				vals0 = inst[0].value;
+				value = encode_float((gfloat32)vals0);
+				store_operand(inst[1].desttype, inst[1].value, value);
+				break;
+			case op_ftonumz:
+				valf = decode_float(inst[0].value);
+				if (!signbit(valf)) {
+					if (isnan(valf) || isinf(valf) || (valf > 2147483647.0))
+						vals0 = 0x7FFFFFFF;
+					else
+						vals0 = (int)(truncf(valf));
+				} else {
+					if (isnan(valf) || isinf(valf) || (valf < -2147483647.0))
+						vals0 = 0x80000000;
+					else
+						vals0 = (int)(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 = (int)(roundf(valf));
+				} else {
+					if (isnan(valf) || isinf(valf) || (valf < -2147483647.0))
+						vals0 = 0x80000000;
+					else
+						vals0 = (int)(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 */
 
 #ifdef GLULX_EXTEND_OPCODES
-      GLULX_EXTEND_OPCODES
+				GLULX_EXTEND_OPCODES
 #endif /* GLULX_EXTEND_OPCODES */
 
-      default:
-        fatal_error_i("Executed unknown opcode.", opcode);
-      }
-    }
-  }
-  /* done executing */
+			default:
+				fatal_error_i("Executed unknown opcode.", opcode);
+			}
+		}
+	}
+	/* done executing */
 #if VM_DEBUGGER
-  debugger_handle_quit();
+	debugger_handle_quit();
 #endif /* VM_DEBUGGER */
 }
 
diff --git a/engines/glk/glulxe/float.cpp b/engines/glk/glulxe/float.cpp
index 36ef2f4..c978ef9 100644
--- a/engines/glk/glulxe/float.cpp
+++ b/engines/glk/glulxe/float.cpp
@@ -26,104 +26,97 @@ namespace Glk {
 namespace Glulxe {
 
 uint Glulxe::encode_float(gfloat32 val) {
-    gfloat32 absval;
-    uint sign;
-    int expo;
-    gfloat32 mant;
-    uint 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 = (uint)(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) | ((uint)(expo << 23)) | (fbits);
+	gfloat32 absval;
+	uint sign;
+	int expo;
+	gfloat32 mant;
+	uint 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 = (uint)(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) | ((uint)(expo << 23)) | (fbits);
 }
 
 gfloat32 Glulxe::decode_float(uint val) {
-    int sign;
-    int expo;
-    uint 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));
+	int sign;
+	int expo;
+	uint 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));
 }
 
 } // End of namespace Glulxe
diff --git a/engines/glk/glulxe/funcs.cpp b/engines/glk/glulxe/funcs.cpp
index b85844a..4af5263 100644
--- a/engines/glk/glulxe/funcs.cpp
+++ b/engines/glk/glulxe/funcs.cpp
@@ -26,279 +26,273 @@ namespace Glk {
 namespace Glulxe {
 
 void Glulxe::enter_function(uint funcaddr, uint argc, uint *argv) {
-  uint ix, jx;
-  acceleration_func accelFunc;
-  int locallen;
-  int functype;
-  uint modeaddr, opaddr, val;
-  int loctype, locnum;
-  uint addr = funcaddr;
-
-  accelFunc = accel_get_func(addr);
-  if (accelFunc) {
-    profile_in(addr, stackptr, TRUE);
-    val = (this->*accelFunc)(argc, argv);
-    profile_out(stackptr);
-    pop_callstub(val);
-    return;
-  }
-    
-  profile_in(addr, stackptr, FALSE);
-
-  /* Check the Glulx type identifier byte. */
-  functype = Mem1(addr);
-  if (functype != 0xC0 && functype != 0xC1) {
-    if (functype >= 0xC0 && functype <= 0xDF)
-      fatal_error_i("Call to unknown type of function.", addr);
-    else
-      fatal_error_i("Call to non-function.", addr);
-  }
-  addr++;
-
-  /* Bump the frameptr to the top. */
-  frameptr = stackptr;
-
-  /* Go through the function's locals-format list, copying it to the
-     call frame. At the same time, we work out how much space the locals
-     will actually take up. (Including padding.) */
-  ix = 0;
-  locallen = 0;
-  while (1) {
-    /* Grab two bytes from the locals-format list. These are 
-       unsigned (0..255 range). */
-    loctype = Mem1(addr);
-    addr++;
-    locnum = Mem1(addr);
-    addr++;
-
-    /* Copy them into the call frame. */
-    StkW1(frameptr+8+2*ix, loctype);
-    StkW1(frameptr+8+2*ix+1, locnum);
-    ix++;
-
-    /* If the type is zero, we're done, except possibly for two more
-       zero bytes in the call frame (to ensure 4-byte alignment.) */
-    if (loctype == 0) {
-      /* Make sure ix is even. */
-      if (ix & 1) {
-        StkW1(frameptr+8+2*ix, 0);
-        StkW1(frameptr+8+2*ix+1, 0);
-        ix++;
-      }
-      break;
-    }
-
-    /* Pad to 4-byte or 2-byte alignment if these locals are 4 or 2
-       bytes long. */
-    if (loctype == 4) {
-      while (locallen & 3)
-        locallen++;
-    }
-    else if (loctype == 2) {
-      while (locallen & 1)
-        locallen++;
-    }
-    else if (loctype == 1) {
-      /* no padding */
-    }
-    else {
-      fatal_error("Illegal local type in locals-format list.");
-    }
-
-    /* Add the length of the locals themselves. */
-    locallen += (loctype * locnum);
-  }
-
-  /* Pad the locals to 4-byte alignment. */
-  while (locallen & 3)
-    locallen++;
-
-  /* We now know how long the locals-frame and locals segments are. */
-  localsbase = frameptr+8+2*ix;
-  valstackbase = localsbase+locallen;
-
-  /* Test for stack overflow. */
-  /* This really isn't good enough; if the format list overflowed the
-     stack, we've already written outside the stack array. */
-  if (valstackbase >= stacksize)
-    fatal_error("Stack overflow in function call.");
-
-  /* Fill in the beginning of the stack frame. */
-  StkW4(frameptr+4, 8+2*ix);
-  StkW4(frameptr, 8+2*ix+locallen);
-
-  /* Set the stackptr and PC. */
-  stackptr = valstackbase;
-  pc = addr;
-
-  /* Zero out all the locals. */
-  for (jx=0; jx < (uint)locallen; jx++) 
-    StkW1(localsbase+jx, 0);
-
-  if (functype == 0xC0) {
-    /* Push the function arguments on the stack. The locals have already
-       been zeroed. */
-    if (stackptr+4*(argc+1) >= stacksize)
-      fatal_error("Stack overflow in function arguments."); 
-    for (ix=0; ix<argc; ix++) {
-      val = argv[(argc-1)-ix];
-      StkW4(stackptr, val);
-      stackptr += 4;
-    }
-    StkW4(stackptr, argc);
-    stackptr += 4;
-  }
-  else {
-    /* Copy in function arguments. This is a bit gross, since we have to
-       follow the locals format. If there are fewer arguments than locals,
-       that's fine -- we've already zeroed out this space. If there are
-       more arguments than locals, the extras are silently dropped. */
-    modeaddr = frameptr+8;
-    opaddr = localsbase;
-    ix = 0;
-    while (ix < argc) {
-      loctype = Stk1(modeaddr);
-      modeaddr++;
-      locnum = Stk1(modeaddr);
-      modeaddr++;
-      if (loctype == 0)
-        break;
-      if (loctype == 4) {
-        while (opaddr & 3)
-          opaddr++;
-        while (ix < argc && locnum) {
-          val = argv[ix];
-          StkW4(opaddr, val);
-          opaddr += 4;
-          ix++;
-          locnum--;
-        }
-      }
-      else if (loctype == 2) {
-        while (opaddr & 1)
-          opaddr++;
-        while (ix < argc && locnum) {
-          val = argv[ix] & 0xFFFF;
-          StkW2(opaddr, val);
-          opaddr += 2;
-          ix++;
-          locnum--;
-        }
-      }
-      else if (loctype == 1) {
-        while (ix < argc && locnum) {
-          val = argv[ix] & 0xFF;
-          StkW1(opaddr, val);
-          opaddr += 1;
-          ix++;
-          locnum--;
-        }
-      }
-    }
-  }
-
-  /* If the debugger is compiled in, check for a breakpoint on this
-     function. (Checking the function address, not the starting PC.) */
-  debugger_check_func_breakpoint(funcaddr);
+	uint ix, jx;
+	acceleration_func accelFunc;
+	int locallen;
+	int functype;
+	uint modeaddr, opaddr, val;
+	int loctype, locnum;
+	uint addr = funcaddr;
+
+	accelFunc = accel_get_func(addr);
+	if (accelFunc) {
+		profile_in(addr, stackptr, TRUE);
+		val = (this->*accelFunc)(argc, argv);
+		profile_out(stackptr);
+		pop_callstub(val);
+		return;
+	}
+
+	profile_in(addr, stackptr, FALSE);
+
+	/* Check the Glulx type identifier byte. */
+	functype = Mem1(addr);
+	if (functype != 0xC0 && functype != 0xC1) {
+		if (functype >= 0xC0 && functype <= 0xDF)
+			fatal_error_i("Call to unknown type of function.", addr);
+		else
+			fatal_error_i("Call to non-function.", addr);
+	}
+	addr++;
+
+	/* Bump the frameptr to the top. */
+	frameptr = stackptr;
+
+	/* Go through the function's locals-format list, copying it to the
+	   call frame. At the same time, we work out how much space the locals
+	   will actually take up. (Including padding.) */
+	ix = 0;
+	locallen = 0;
+	while (1) {
+		/* Grab two bytes from the locals-format list. These are
+		   unsigned (0..255 range). */
+		loctype = Mem1(addr);
+		addr++;
+		locnum = Mem1(addr);
+		addr++;
+
+		/* Copy them into the call frame. */
+		StkW1(frameptr + 8 + 2 * ix, loctype);
+		StkW1(frameptr + 8 + 2 * ix + 1, locnum);
+		ix++;
+
+		/* If the type is zero, we're done, except possibly for two more
+		   zero bytes in the call frame (to ensure 4-byte alignment.) */
+		if (loctype == 0) {
+			/* Make sure ix is even. */
+			if (ix & 1) {
+				StkW1(frameptr + 8 + 2 * ix, 0);
+				StkW1(frameptr + 8 + 2 * ix + 1, 0);
+				ix++;
+			}
+			break;
+		}
+
+		/* Pad to 4-byte or 2-byte alignment if these locals are 4 or 2
+		   bytes long. */
+		if (loctype == 4) {
+			while (locallen & 3)
+				locallen++;
+		} else if (loctype == 2) {
+			while (locallen & 1)
+				locallen++;
+		} else if (loctype == 1) {
+			/* no padding */
+		} else {
+			fatal_error("Illegal local type in locals-format list.");
+		}
+
+		/* Add the length of the locals themselves. */
+		locallen += (loctype * locnum);
+	}
+
+	/* Pad the locals to 4-byte alignment. */
+	while (locallen & 3)
+		locallen++;
+
+	/* We now know how long the locals-frame and locals segments are. */
+	localsbase = frameptr + 8 + 2 * ix;
+	valstackbase = localsbase + locallen;
+
+	/* Test for stack overflow. */
+	/* This really isn't good enough; if the format list overflowed the
+	   stack, we've already written outside the stack array. */
+	if (valstackbase >= stacksize)
+		fatal_error("Stack overflow in function call.");
+
+	/* Fill in the beginning of the stack frame. */
+	StkW4(frameptr + 4, 8 + 2 * ix);
+	StkW4(frameptr, 8 + 2 * ix + locallen);
+
+	/* Set the stackptr and PC. */
+	stackptr = valstackbase;
+	pc = addr;
+
+	/* Zero out all the locals. */
+	for (jx = 0; jx < (uint)locallen; jx++)
+		StkW1(localsbase + jx, 0);
+
+	if (functype == 0xC0) {
+		/* Push the function arguments on the stack. The locals have already
+		   been zeroed. */
+		if (stackptr + 4 * (argc + 1) >= stacksize)
+			fatal_error("Stack overflow in function arguments.");
+		for (ix = 0; ix < argc; ix++) {
+			val = argv[(argc - 1) - ix];
+			StkW4(stackptr, val);
+			stackptr += 4;
+		}
+		StkW4(stackptr, argc);
+		stackptr += 4;
+	} else {
+		/* Copy in function arguments. This is a bit gross, since we have to
+		   follow the locals format. If there are fewer arguments than locals,
+		   that's fine -- we've already zeroed out this space. If there are
+		   more arguments than locals, the extras are silently dropped. */
+		modeaddr = frameptr + 8;
+		opaddr = localsbase;
+		ix = 0;
+		while (ix < argc) {
+			loctype = Stk1(modeaddr);
+			modeaddr++;
+			locnum = Stk1(modeaddr);
+			modeaddr++;
+			if (loctype == 0)
+				break;
+			if (loctype == 4) {
+				while (opaddr & 3)
+					opaddr++;
+				while (ix < argc && locnum) {
+					val = argv[ix];
+					StkW4(opaddr, val);
+					opaddr += 4;
+					ix++;
+					locnum--;
+				}
+			} else if (loctype == 2) {
+				while (opaddr & 1)
+					opaddr++;
+				while (ix < argc && locnum) {
+					val = argv[ix] & 0xFFFF;
+					StkW2(opaddr, val);
+					opaddr += 2;
+					ix++;
+					locnum--;
+				}
+			} else if (loctype == 1) {
+				while (ix < argc && locnum) {
+					val = argv[ix] & 0xFF;
+					StkW1(opaddr, val);
+					opaddr += 1;
+					ix++;
+					locnum--;
+				}
+			}
+		}
+	}
+
+	/* If the debugger is compiled in, check for a breakpoint on this
+	   function. (Checking the function address, not the starting PC.) */
+	debugger_check_func_breakpoint(funcaddr);
 }
 
 void Glulxe::leave_function() {
-  profile_out(stackptr);
-  stackptr = frameptr;
+	profile_out(stackptr);
+	stackptr = frameptr;
 }
 
 void Glulxe::push_callstub(uint desttype, uint destaddr) {
-  if (stackptr+16 > stacksize)
-    fatal_error("Stack overflow in callstub.");
-  StkW4(stackptr+0, desttype);
-  StkW4(stackptr+4, destaddr);
-  StkW4(stackptr+8, pc);
-  StkW4(stackptr+12, frameptr);
-  stackptr += 16;
+	if (stackptr + 16 > stacksize)
+		fatal_error("Stack overflow in callstub.");
+	StkW4(stackptr + 0, desttype);
+	StkW4(stackptr + 4, destaddr);
+	StkW4(stackptr + 8, pc);
+	StkW4(stackptr + 12, frameptr);
+	stackptr += 16;
 }
 
 void Glulxe::pop_callstub(uint returnvalue) {
-  uint desttype, destaddr;
-  uint newpc, newframeptr;
-
-  if (stackptr < 16)
-    fatal_error("Stack underflow in callstub.");
-  stackptr -= 16;
-
-  newframeptr = Stk4(stackptr+12);
-  newpc = Stk4(stackptr+8);
-  destaddr = Stk4(stackptr+4);
-  desttype = Stk4(stackptr+0);
-
-  pc = newpc;
-  frameptr = newframeptr;
-
-  /* Recompute valstackbase and localsbase */
-  valstackbase = frameptr + Stk4(frameptr);
-  localsbase = frameptr + Stk4(frameptr+4);
-
-  switch (desttype) {
-
-  case 0x11:
-    fatal_error("String-terminator call stub at end of function call.");
-    break;
-
-  case 0x10:
-    /* This call stub was pushed during a string-decoding operation!
-       We have to restart it. (Note that the return value is discarded.) */
-    stream_string(pc, 0xE1, destaddr); 
-    break;
-
-  case 0x12:
-    /* This call stub was pushed during a number-printing operation.
-       Restart that. (Return value discarded.) */
-    stream_num(pc, true, destaddr);
-    break;
-
-  case 0x13:
-    /* This call stub was pushed during a C-string printing operation.
-       We have to restart it. (Note that the return value is discarded.) */
-    stream_string(pc, 0xE0, destaddr); 
-    break;
-
-  case 0x14:
-    /* This call stub was pushed during a Unicode printing operation.
-       We have to restart it. (Note that the return value is discarded.) */
-    stream_string(pc, 0xE2, destaddr); 
-    break;
-
-  default:
-    /* We're back in the original frame, so we can store the returnvalue. 
-       (If we tried to do this before resetting frameptr, a result
-       destination on the stack would go astray.) */
-    store_operand(desttype, destaddr, returnvalue);
-    break;
-  }
+	uint desttype, destaddr;
+	uint newpc, newframeptr;
+
+	if (stackptr < 16)
+		fatal_error("Stack underflow in callstub.");
+	stackptr -= 16;
+
+	newframeptr = Stk4(stackptr + 12);
+	newpc = Stk4(stackptr + 8);
+	destaddr = Stk4(stackptr + 4);
+	desttype = Stk4(stackptr + 0);
+
+	pc = newpc;
+	frameptr = newframeptr;
+
+	/* Recompute valstackbase and localsbase */
+	valstackbase = frameptr + Stk4(frameptr);
+	localsbase = frameptr + Stk4(frameptr + 4);
+
+	switch (desttype) {
+
+	case 0x11:
+		fatal_error("String-terminator call stub at end of function call.");
+		break;
+
+	case 0x10:
+		/* This call stub was pushed during a string-decoding operation!
+		   We have to restart it. (Note that the return value is discarded.) */
+		stream_string(pc, 0xE1, destaddr);
+		break;
+
+	case 0x12:
+		/* This call stub was pushed during a number-printing operation.
+		   Restart that. (Return value discarded.) */
+		stream_num(pc, true, destaddr);
+		break;
+
+	case 0x13:
+		/* This call stub was pushed during a C-string printing operation.
+		   We have to restart it. (Note that the return value is discarded.) */
+		stream_string(pc, 0xE0, destaddr);
+		break;
+
+	case 0x14:
+		/* This call stub was pushed during a Unicode printing operation.
+		   We have to restart it. (Note that the return value is discarded.) */
+		stream_string(pc, 0xE2, destaddr);
+		break;
+
+	default:
+		/* We're back in the original frame, so we can store the returnvalue.
+		   (If we tried to do this before resetting frameptr, a result
+		   destination on the stack would go astray.) */
+		store_operand(desttype, destaddr, returnvalue);
+		break;
+	}
 }
 
 uint Glulxe::pop_callstub_string(int *bitnum) {
-  uint desttype, destaddr, newpc;
+	uint desttype, destaddr, newpc;
 
-  if (stackptr < 16)
-    fatal_error("Stack underflow in callstub.");
-  stackptr -= 16;
+	if (stackptr < 16)
+		fatal_error("Stack underflow in callstub.");
+	stackptr -= 16;
 
-  newpc = Stk4(stackptr+8);
-  destaddr = Stk4(stackptr+4);
-  desttype = Stk4(stackptr+0);
+	newpc = Stk4(stackptr + 8);
+	destaddr = Stk4(stackptr + 4);
+	desttype = Stk4(stackptr + 0);
 
-  pc = newpc;
+	pc = newpc;
 
-  if (desttype == 0x11) {
-    return 0;
-  }
-  if (desttype == 0x10) {
-    *bitnum = destaddr;
-    return pc;
-  }
+	if (desttype == 0x11) {
+		return 0;
+	}
+	if (desttype == 0x10) {
+		*bitnum = destaddr;
+		return pc;
+	}
 
-  fatal_error("Function-terminator call stub at end of string.");
-  return 0;
+	fatal_error("Function-terminator call stub at end of string.");
+	return 0;
 }
 
 } // End of namespace Glulxe
diff --git a/engines/glk/glulxe/gestalt.cpp b/engines/glk/glulxe/gestalt.cpp
index 21f8974..18f9083 100644
--- a/engines/glk/glulxe/gestalt.cpp
+++ b/engines/glk/glulxe/gestalt.cpp
@@ -26,75 +26,75 @@ namespace Glk {
 namespace Glulxe {
 
 uint Glulxe::do_gestalt(uint val, uint val2) {
-  switch (val) {
+	switch (val) {
 
-  case gestulx_GlulxVersion:
-    return 0x00030102; /* Glulx spec version 3.1.2 */
+	case gestulx_GlulxVersion:
+		return 0x00030102; /* Glulx spec version 3.1.2 */
 
-  case gestulx_TerpVersion:
-    return 0x00000504; /* Glulxe version 0.5.4 */
+	case gestulx_TerpVersion:
+		return 0x00000504; /* Glulxe version 0.5.4 */
 
-  case gestulx_ResizeMem:
+	case gestulx_ResizeMem:
 #ifdef FIXED_MEMSIZE
-    return 0; /* The setmemsize opcodes are compiled out. */
+		return 0; /* The setmemsize opcodes are compiled out. */
 #else /* FIXED_MEMSIZE */
-    return 1; /* We can handle setmemsize. */
+		return 1; /* We can handle setmemsize. */
 #endif /* FIXED_MEMSIZE */
 
-  case gestulx_Undo:
-    return 1; /* We can handle saveundo and restoreundo. */
+	case gestulx_Undo:
+		return 1; /* We can handle saveundo and restoreundo. */
 
-  case gestulx_IOSystem:
-    switch (val2) {
-    case 0:
-      return 1; /* The "null" system always works. */
-    case 1:
-      return 1; /* The "filter" system always works. */
-    case 2:
-      return 1; /* A Glk library is hooked up. */
-    default:
-      return 0;
-    }
+	case gestulx_IOSystem:
+		switch (val2) {
+		case 0:
+			return 1; /* The "null" system always works. */
+		case 1:
+			return 1; /* The "filter" system always works. */
+		case 2:
+			return 1; /* A Glk library is hooked up. */
+		default:
+			return 0;
+		}
 
-  case gestulx_Unicode:
-    return 1; /* We can handle Unicode. */
+	case gestulx_Unicode:
+		return 1; /* We can handle Unicode. */
 
-  case gestulx_MemCopy:
-    return 1; /* We can do mcopy/mzero. */
+	case gestulx_MemCopy:
+		return 1; /* We can do mcopy/mzero. */
 
-  case gestulx_MAlloc:
+	case gestulx_MAlloc:
 #ifdef FIXED_MEMSIZE
-    return 0; /* The malloc opcodes are compiled out. */
+		return 0; /* The malloc opcodes are compiled out. */
 #else /* FIXED_MEMSIZE */
-    return 1; /* We can handle malloc/mfree. */
+		return 1; /* We can handle malloc/mfree. */
 #endif /* FIXED_MEMSIZE */
 
-  case gestulx_MAllocHeap:
-    return heap_get_start();
+	case gestulx_MAllocHeap:
+		return heap_get_start();
 
-  case gestulx_Acceleration:
-    return 1; /* We can do accelfunc/accelparam. */
+	case gestulx_Acceleration:
+		return 1; /* We can do accelfunc/accelparam. */
 
-  case gestulx_AccelFunc:
-    if (accel_find_func(val2))
-      return 1; /* We know this accelerated function. */
-    return 0;
+	case gestulx_AccelFunc:
+		if (accel_find_func(val2))
+			return 1; /* We know this accelerated function. */
+		return 0;
 
-  case gestulx_Float:
+	case gestulx_Float:
 #ifdef FLOAT_SUPPORT
-    return 1; /* We can do floating-point operations. */
+		return 1; /* We can do floating-point operations. */
 #else /* FLOAT_SUPPORT */
-    return 0; /* The floating-point opcodes are not compiled in. */
+		return 0; /* The floating-point opcodes are not compiled in. */
 #endif /* FLOAT_SUPPORT */
 
 #ifdef GLULX_EXTEND_GESTALT
-  GLULX_EXTEND_GESTALT
+		GLULX_EXTEND_GESTALT
 #endif /* GLULX_EXTEND_GESTALT */
 
-  default:
-    return 0;
+	default:
+		return 0;
 
-  }
+	}
 }
 
 } // End of namespace Glulxe
diff --git a/engines/glk/glulxe/glkop.cpp b/engines/glk/glulxe/glkop.cpp
index 12d4fef..cb912ec 100644
--- a/engines/glk/glulxe/glkop.cpp
+++ b/engines/glk/glulxe/glkop.cpp
@@ -27,7 +27,7 @@ namespace Glulxe {
 
 /* This code is actually very general; it could work for almost any
    32-bit VM which remotely resembles Glulxe or the Z-machine in design.
-   
+
    To be precise, we make the following assumptions:
 
    - An argument list is an array of 32-bit values, which can represent
@@ -66,41 +66,41 @@ namespace Glulxe {
 */
 
 #define ReadMemory(addr)  \
-    (((addr) == 0xffffffff) \
-      ? (stackptr -= 4, Stk4(stackptr)) \
-      : (Mem4(addr)))
+	(((addr) == 0xffffffff) \
+	 ? (stackptr -= 4, Stk4(stackptr)) \
+	 : (Mem4(addr)))
 #define WriteMemory(addr, val)  \
-    (((addr) == 0xffffffff) \
-      ? (StkW4(stackptr, (val)), stackptr += 4) \
-      : (MemW4((addr), (val))))
+	(((addr) == 0xffffffff) \
+	 ? (StkW4(stackptr, (val)), stackptr += 4) \
+	 : (MemW4((addr), (val))))
 #define CaptureCArray(addr, len, passin)  \
-    (grab_temp_c_array(addr, len, passin))
+	(grab_temp_c_array(addr, len, passin))
 #define ReleaseCArray(ptr, addr, len, passout)  \
-    (release_temp_c_array(ptr, addr, len, passout))
+	(release_temp_c_array(ptr, addr, len, passout))
 #define CaptureIArray(addr, len, passin)  \
-    (grab_temp_i_array(addr, len, passin))
+	(grab_temp_i_array(addr, len, passin))
 #define ReleaseIArray(ptr, addr, len, passout)  \
-    (release_temp_i_array(ptr, addr, len, passout))
+	(release_temp_i_array(ptr, addr, len, passout))
 #define CapturePtrArray(addr, len, objclass, passin)  \
-    (grab_temp_ptr_array(addr, len, objclass, passin))
+	(grab_temp_ptr_array(addr, len, objclass, passin))
 #define ReleasePtrArray(ptr, addr, len, objclass, passout)  \
-    (release_temp_ptr_array(ptr, addr, len, objclass, passout))
+	(release_temp_ptr_array(ptr, addr, len, objclass, passout))
 #define ReadStructField(addr, fieldnum)  \
-    (((addr) == 0xffffffff) \
-      ? (stackptr -= 4, Stk4(stackptr)) \
-      : (Mem4((addr)+(fieldnum)*4)))
+	(((addr) == 0xffffffff) \
+	 ? (stackptr -= 4, Stk4(stackptr)) \
+	 : (Mem4((addr)+(fieldnum)*4)))
 #define WriteStructField(addr, fieldnum, val)  \
-    (((addr) == 0xffffffff) \
-      ? (StkW4(stackptr, (val)), stackptr += 4) \
-      : (MemW4((addr)+(fieldnum)*4, (val))))
+	(((addr) == 0xffffffff) \
+	 ? (StkW4(stackptr, (val)), stackptr += 4) \
+	 : (MemW4((addr)+(fieldnum)*4, (val))))
 #define DecodeVMString(addr)  \
-    (make_temp_string(addr))
+	(make_temp_string(addr))
 #define ReleaseVMString(ptr)  \
-    (free_temp_string(ptr))
+	(free_temp_string(ptr))
 #define DecodeVMUstring(addr)  \
-    (make_temp_ustring(addr))
+	(make_temp_ustring(addr))
 #define ReleaseVMUstring(ptr)  \
-    (free_temp_ustring(ptr))
+	(free_temp_ustring(ptr))
 
 static gidispatch_rock_t classtable_register(void *obj, uint objclass) {
 	return g_vm->glulxe_classtable_register(obj, objclass);
@@ -129,1252 +129,1213 @@ void Glulxe::glkopInit() {
    Set up the class hash tables and other startup-time stuff.
 */
 bool Glulxe::init_dispatch() {
-  int ix;
-  
-  /* What with one thing and another, this *could* be called more than
-     once. We only need to allocate the tables once. */
-  if (classes)
-      return true;
-  
-  /* Set up the game-ID hook. (This is ifdeffed because not all Glk
-     libraries have this call.) */
+	int ix;
+
+	/* What with one thing and another, this *could* be called more than
+	   once. We only need to allocate the tables once. */
+	if (classes)
+		return true;
+
+	/* Set up the game-ID hook. (This is ifdeffed because not all Glk
+	   libraries have this call.) */
 #ifdef GI_DISPA_GAME_ID_AVAILABLE
-  gidispatch_set_game_id_hook(&get_game_id);
+	gidispatch_set_game_id_hook(&get_game_id);
 #endif /* GI_DISPA_GAME_ID_AVAILABLE */
-    
-  /* Allocate the class hash tables. */
-  num_classes = gidispatch_count_classes();
-  classes = (classtable_t **)glulx_malloc(num_classes  * sizeof(classtable_t *));
-  if (!classes)
-    return false;
-    
-  for (ix=0; ix<num_classes; ix++) {
-    classes[ix] = new_classtable((glulx_random() % (uint)(101)) + 1);
-    if (!classes[ix])
-      return false;
-  }
-    
-  /* Set up the two callbacks. */
-  gidispatch_set_object_registry(&classtable_register, &classtable_unregister);
-  gidispatch_set_retained_registry(&retained_register, &retained_unregister);
-  
-  /* If the library supports autorestore callbacks, set those up too.
-     (These are only used in iosglk, currently.) */
+
+	/* Allocate the class hash tables. */
+	num_classes = gidispatch_count_classes();
+	classes = (classtable_t **)glulx_malloc(num_classes  * sizeof(classtable_t *));
+	if (!classes)
+		return false;
+
+	for (ix = 0; ix < num_classes; ix++) {
+		classes[ix] = new_classtable((glulx_random() % (uint)(101)) + 1);
+		if (!classes[ix])
+			return false;
+	}
+
+	/* Set up the two callbacks. */
+	gidispatch_set_object_registry(&classtable_register, &classtable_unregister);
+	gidispatch_set_retained_registry(&retained_register, &retained_unregister);
+
+	/* If the library supports autorestore callbacks, set those up too.
+	   (These are only used in iosglk, currently.) */
 #ifdef GIDISPATCH_AUTORESTORE_REGISTRY
-  gidispatch_set_autorestore_registry(&glulxe_array_locate, &glulxe_array_restore);
+	gidispatch_set_autorestore_registry(&glulxe_array_locate, &glulxe_array_restore);
 #endif /* GIDISPATCH_AUTORESTORE_REGISTRY */
-  
-  return true;
+
+	return true;
 }
 
 uint Glulxe::perform_glk(uint funcnum, uint numargs, uint *arglist) {
-  uint retval = 0;
-
-  switch (funcnum) {
-    /* To speed life up, we implement commonly-used Glk functions
-       directly -- instead of bothering with the whole prototype 
-       mess. */
-
-  case 0x0047: /* stream_set_current */
-    if (numargs != 1)
-      goto WrongArgNum;
-    glk_stream_set_current(find_stream_by_id(arglist[0]));
-    break;
-  case 0x0048: /* stream_get_current */
-    if (numargs != 0)
-      goto WrongArgNum;
-    retval = find_id_for_stream(glk_stream_get_current());
-    break;
-  case 0x0080: /* put_char */
-    if (numargs != 1)
-      goto WrongArgNum;
-    glk_put_char(arglist[0] & 0xFF);
-    break;
-  case 0x0081: /* put_char_stream */
-    if (numargs != 2)
-      goto WrongArgNum;
-    glk_put_char_stream(find_stream_by_id(arglist[0]), arglist[1] & 0xFF);
-    break;
-  case 0x00C0: /* select */
-    /* call a library hook on every glk_select() */
-    if (library_select_hook)
-      library_select_hook(arglist[0]);
-    /* but then fall through to full dispatcher, because there's no real
-       need for speed here */
-    goto FullDispatcher;
-  case 0x00A0: /* char_to_lower */
-    if (numargs != 1)
-      goto WrongArgNum;
-    retval = glk_char_to_lower(arglist[0] & 0xFF);
-    break;
-  case 0x00A1: /* char_to_upper */
-    if (numargs != 1)
-      goto WrongArgNum;
-    retval = glk_char_to_upper(arglist[0] & 0xFF);
-    break;
-  case 0x0128: /* put_char_uni */
-    if (numargs != 1)
-      goto WrongArgNum;
-    glk_put_char_uni(arglist[0]);
-    break;
-  case 0x012B: /* put_char_stream_uni */
-    if (numargs != 2)
-      goto WrongArgNum;
-    glk_put_char_stream_uni(find_stream_by_id(arglist[0]), arglist[1]);
-    break;
-
-  WrongArgNum:
-    error("Wrong number of arguments to Glk function.");
-    break;
-
-  FullDispatcher:
-  default: {
-    /* Go through the full dispatcher prototype foo. */
-    const char *proto, *cx;
-    dispatch_splot_t splot;
-    int argnum, argnum2;
-
-    /* Grab the string. */
-    proto = gidispatch_prototype(funcnum);
-    if (!proto)
-      error("Unknown Glk function.");
-
-    splot.varglist = arglist;
-    splot.numvargs = numargs;
-    splot.retval = &retval;
-
-    /* The work goes in four phases. First, we figure out how many
-       arguments we want, and allocate space for the Glk argument
-       list. Then we go through the Glulxe arguments and load them 
-       into the Glk list. Then we call. Then we go through the 
-       arguments again, unloading the data back into Glulx memory. */
-
-    /* Phase 0. */
-    prepare_glk_args(proto, &splot);
-
-    /* Phase 1. */
-    argnum = 0;
-    cx = proto;
-    parse_glk_args(&splot, &cx, 0, &argnum, 0, 0);
-
-    /* Phase 2. */
-    gidispatch_call(funcnum, argnum, splot.garglist);
-
-    /* Phase 3. */
-    argnum2 = 0;
-    cx = proto;
-    unparse_glk_args(&splot, &cx, 0, &argnum2, 0, 0);
-    if (argnum != argnum2)
-      error("Argument counts did not match.");
-
-    break;
-  }
-  }
-
-  return retval;
+	uint retval = 0;
+
+	switch (funcnum) {
+	/* To speed life up, we implement commonly-used Glk functions
+	   directly -- instead of bothering with the whole prototype
+	   mess. */
+
+	case 0x0047: /* stream_set_current */
+		if (numargs != 1)
+			goto WrongArgNum;
+		glk_stream_set_current(find_stream_by_id(arglist[0]));
+		break;
+	case 0x0048: /* stream_get_current */
+		if (numargs != 0)
+			goto WrongArgNum;
+		retval = find_id_for_stream(glk_stream_get_current());
+		break;
+	case 0x0080: /* put_char */
+		if (numargs != 1)
+			goto WrongArgNum;
+		glk_put_char(arglist[0] & 0xFF);
+		break;
+	case 0x0081: /* put_char_stream */
+		if (numargs != 2)
+			goto WrongArgNum;
+		glk_put_char_stream(find_stream_by_id(arglist[0]), arglist[1] & 0xFF);
+		break;
+	case 0x00C0: /* select */
+		/* call a library hook on every glk_select() */
+		if (library_select_hook)
+			library_select_hook(arglist[0]);
+		/* but then fall through to full dispatcher, because there's no real
+		   need for speed here */
+		goto FullDispatcher;
+	case 0x00A0: /* char_to_lower */
+		if (numargs != 1)
+			goto WrongArgNum;
+		retval = glk_char_to_lower(arglist[0] & 0xFF);
+		break;
+	case 0x00A1: /* char_to_upper */
+		if (numargs != 1)
+			goto WrongArgNum;
+		retval = glk_char_to_upper(arglist[0] & 0xFF);
+		break;
+	case 0x0128: /* put_char_uni */
+		if (numargs != 1)
+			goto WrongArgNum;
+		glk_put_char_uni(arglist[0]);
+		break;
+	case 0x012B: /* put_char_stream_uni */
+		if (numargs != 2)
+			goto WrongArgNum;
+		glk_put_char_stream_uni(find_stream_by_id(arglist[0]), arglist[1]);
+		break;
+
+WrongArgNum:
+		error("Wrong number of arguments to Glk function.");
+		break;
+
+FullDispatcher:
+	default: {
+		/* Go through the full dispatcher prototype foo. */
+		const char *proto, *cx;
+		dispatch_splot_t splot;
+		int argnum, argnum2;
+
+		/* Grab the string. */
+		proto = gidispatch_prototype(funcnum);
+		if (!proto)
+			error("Unknown Glk function.");
+
+		splot.varglist = arglist;
+		splot.numvargs = numargs;
+		splot.retval = &retval;
+
+		/* The work goes in four phases. First, we figure out how many
+		   arguments we want, and allocate space for the Glk argument
+		   list. Then we go through the Glulxe arguments and load them
+		   into the Glk list. Then we call. Then we go through the
+		   arguments again, unloading the data back into Glulx memory. */
+
+		/* Phase 0. */
+		prepare_glk_args(proto, &splot);
+
+		/* Phase 1. */
+		argnum = 0;
+		cx = proto;
+		parse_glk_args(&splot, &cx, 0, &argnum, 0, 0);
+
+		/* Phase 2. */
+		gidispatch_call(funcnum, argnum, splot.garglist);
+
+		/* Phase 3. */
+		argnum2 = 0;
+		cx = proto;
+		unparse_glk_args(&splot, &cx, 0, &argnum2, 0, 0);
+		if (argnum != argnum2)
+			error("Argument counts did not match.");
+
+		break;
+	}
+	}
+
+	return retval;
 }
 
 const char *Glulxe::read_prefix(const char *cx, int *isref, int *isarray, int *passin, int *passout,
-		int *nullok, int *isretained,  int *isreturn) {
-  *isref = false;
-  *passin = false;
-  *passout = false;
-  *nullok = true;
-  *isarray = false;
-  *isretained = false;
-  *isreturn = false;
-  while (1) {
-    if (*cx == '<') {
-      *isref = true;
-      *passout = true;
-    }
-    else if (*cx == '>') {
-      *isref = true;
-      *passin = true;
-    }
-    else if (*cx == '&') {
-      *isref = true;
-      *passout = true;
-      *passin = true;
-    }
-    else if (*cx == '+') {
-      *nullok = false;
-    }
-    else if (*cx == ':') {
-      *isref = true;
-      *passout = true;
-      *nullok = false;
-      *isreturn = true;
-    }
-    else if (*cx == '#') {
-      *isarray = true;
-    }
-    else if (*cx == '!') {
-      *isretained = true;
-    }
-    else {
-      break;
-    }
-    cx++;
-  }
-  return cx;
+                                int *nullok, int *isretained,  int *isreturn) {
+	*isref = false;
+	*passin = false;
+	*passout = false;
+	*nullok = true;
+	*isarray = false;
+	*isretained = false;
+	*isreturn = false;
+	while (1) {
+		if (*cx == '<') {
+			*isref = true;
+			*passout = true;
+		} else if (*cx == '>') {
+			*isref = true;
+			*passin = true;
+		} else if (*cx == '&') {
+			*isref = true;
+			*passout = true;
+			*passin = true;
+		} else if (*cx == '+') {
+			*nullok = false;
+		} else if (*cx == ':') {
+			*isref = true;
+			*passout = true;
+			*nullok = false;
+			*isreturn = true;
+		} else if (*cx == '#') {
+			*isarray = true;
+		} else if (*cx == '!') {
+			*isretained = true;
+		} else {
+			break;
+		}
+		cx++;
+	}
+	return cx;
 }
 
 void Glulxe::prepare_glk_args(const char *proto, dispatch_splot_t *splot) {
-  static gluniversal_t *garglist = nullptr;
-  static int garglist_size = 0;
-
-  int ix;
-  int numwanted, numvargswanted, maxargs;
-  const char *cx;
-
-  cx = proto;
-  numwanted = 0;
-  while (*cx >= '0' && *cx <= '9') {
-    numwanted = 10 * numwanted + (*cx - '0');
-    cx++;
-  }
-  splot->numwanted = numwanted;
-
-  maxargs = 0; 
-  numvargswanted = 0; 
-  for (ix = 0; ix < numwanted; ix++) {
-    int isref, passin, passout, nullok, isarray, isretained, isreturn;
-    cx = read_prefix(cx, &isref, &isarray, &passin, &passout, &nullok,
-      &isretained, &isreturn);
-    if (isref) {
-      maxargs += 2;
-    }
-    else {
-      maxargs += 1;
-    }
-    if (!isreturn) {
-      if (isarray) {
-        numvargswanted += 2;
-      }
-      else {
-        numvargswanted += 1;
-      }
-    }
-        
-    if (*cx == 'I' || *cx == 'C') {
-      cx += 2;
-    }
-    else if (*cx == 'Q') {
-      cx += 2;
-    }
-    else if (*cx == 'S' || *cx == 'U') {
-      cx += 1;
-    }
-    else if (*cx == '[') {
-      int refdepth, nwx;
-      cx++;
-      nwx = 0;
-      while (*cx >= '0' && *cx <= '9') {
-        nwx = 10 * nwx + (*cx - '0');
-        cx++;
-      }
-      maxargs += nwx; /* This is *only* correct because all structs contain
+	static gluniversal_t *garglist = nullptr;
+	static int garglist_size = 0;
+
+	int ix;
+	int numwanted, numvargswanted, maxargs;
+	const char *cx;
+
+	cx = proto;
+	numwanted = 0;
+	while (*cx >= '0' && *cx <= '9') {
+		numwanted = 10 * numwanted + (*cx - '0');
+		cx++;
+	}
+	splot->numwanted = numwanted;
+
+	maxargs = 0;
+	numvargswanted = 0;
+	for (ix = 0; ix < numwanted; ix++) {
+		int isref, passin, passout, nullok, isarray, isretained, isreturn;
+		cx = read_prefix(cx, &isref, &isarray, &passin, &passout, &nullok,
+		                 &isretained, &isreturn);
+		if (isref) {
+			maxargs += 2;
+		} else {
+			maxargs += 1;
+		}
+		if (!isreturn) {
+			if (isarray) {
+				numvargswanted += 2;
+			} else {
+				numvargswanted += 1;
+			}
+		}
+
+		if (*cx == 'I' || *cx == 'C') {
+			cx += 2;
+		} else if (*cx == 'Q') {
+			cx += 2;
+		} else if (*cx == 'S' || *cx == 'U') {
+			cx += 1;
+		} else if (*cx == '[') {
+			int refdepth, nwx;
+			cx++;
+			nwx = 0;
+			while (*cx >= '0' && *cx <= '9') {
+				nwx = 10 * nwx + (*cx - '0');
+				cx++;
+			}
+			maxargs += nwx; /* This is *only* correct because all structs contain
                          plain values. */
-      refdepth = 1;
-      while (refdepth > 0) {
-        if (*cx == '[')
-          refdepth++;
-        else if (*cx == ']')
-          refdepth--;
-        cx++;
-      }
-    }
-    else {
-      error("Illegal format string.");
-    }
-  }
-
-  if (*cx != ':' && *cx != '\0')
-    error("Illegal format string.");
-
-  splot->maxargs = maxargs;
-
-  if (splot->numvargs != numvargswanted)
-    error("Wrong number of arguments to Glk function.");
-
-  if (garglist && garglist_size < maxargs) {
-    glulx_free(garglist);
-    garglist = nullptr;
-    garglist_size = 0;
-  }
-  if (!garglist) {
-    garglist_size = maxargs + 16;
-    garglist = (gluniversal_t *)glulx_malloc(garglist_size 
-      * sizeof(gluniversal_t));
-  }
-  if (!garglist)
-    error("Unable to allocate storage for Glk arguments.");
-
-  splot->garglist = garglist;
+			refdepth = 1;
+			while (refdepth > 0) {
+				if (*cx == '[')
+					refdepth++;
+				else if (*cx == ']')
+					refdepth--;
+				cx++;
+			}
+		} else {
+			error("Illegal format string.");
+		}
+	}
+
+	if (*cx != ':' && *cx != '\0')
+		error("Illegal format string.");
+
+	splot->maxargs = maxargs;
+
+	if (splot->numvargs != numvargswanted)
+		error("Wrong number of arguments to Glk function.");
+
+	if (garglist && garglist_size < maxargs) {
+		glulx_free(garglist);
+		garglist = nullptr;
+		garglist_size = 0;
+	}
+	if (!garglist) {
+		garglist_size = maxargs + 16;
+		garglist = (gluniversal_t *)glulx_malloc(garglist_size
+		           * sizeof(gluniversal_t));
+	}
+	if (!garglist)
+		error("Unable to allocate storage for Glk arguments.");
+
+	splot->garglist = garglist;
 }
 
 void Glulxe::parse_glk_args(dispatch_splot_t *splot, const char **proto, int depth, int *argnumptr,
-		uint subaddress, int subpassin) {
-  const char *cx;
-  int ix, argx;
-  int gargnum, numwanted;
-  void *opref;
-  gluniversal_t *garglist;
-  uint *varglist;
-  
-  garglist = splot->garglist;
-  varglist = splot->varglist;
-  gargnum = *argnumptr;
-  cx = *proto;
-
-  numwanted = 0;
-  while (*cx >= '0' && *cx <= '9') {
-    numwanted = 10 * numwanted + (*cx - '0');
-    cx++;
-  }
-
-  for (argx = 0, ix = 0; argx < numwanted; argx++, ix++) {
-    char typeclass;
-    int skipval;
-    int isref, passin, passout, nullok, isarray, isretained, isreturn;
-    cx = read_prefix(cx, &isref, &isarray, &passin, &passout, &nullok,
-      &isretained, &isreturn);
-    
-    typeclass = *cx;
-    cx++;
-
-    skipval = false;
-    if (isref) {
-      if (!isreturn && varglist[ix] == 0) {
-        if (!nullok)
-          error("Zero passed invalidly to Glk function.");
-        garglist[gargnum]._ptrflag = false;
-        gargnum++;
-        skipval = true;
-      }
-      else {
-        garglist[gargnum]._ptrflag = true;
-        gargnum++;
-      }
-    }
-    if (!skipval) {
-      uint thisval;
-
-      if (typeclass == '[') {
-
-        parse_glk_args(splot, &cx, depth+1, &gargnum, varglist[ix], passin);
-
-      }
-      else if (isarray) {
-        /* definitely isref */
-
-        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 = CaptureCArray(varglist[ix], varglist[ix+1], passin);
-          gargnum++;
-          ix++;
-          garglist[gargnum]._uint = varglist[ix];
-          gargnum++;
-          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++;
-          garglist[gargnum]._uint = varglist[ix];
-          gargnum++;
-          cx++;
-          break;
-        case 'Q':
-          /* This case was added after the giant arrays were deprecated,
-             so we don't bother to allow for that case. We just verify
-             the length. */
-          verify_array_addresses(varglist[ix], varglist[ix+1], 4);
-          garglist[gargnum]._array = CapturePtrArray(varglist[ix], varglist[ix+1], (*cx-'a'), passin);
-          gargnum++;
-          ix++;
-          garglist[gargnum]._uint = varglist[ix];
-          gargnum++;
-          cx++;
-          break;
-        default:
-          error("Illegal format string.");
-          break;
-        }
-      }
-      else {
-        /* a plain value or a reference to one. */
-
-        if (isreturn) {
-          thisval = 0;
-        }
-        else if (depth > 0) {
-          /* Definitely not isref or isarray. */
-          if (subpassin)
-            thisval = ReadStructField(subaddress, ix);
-          else
-            thisval = 0;
-        }
-        else if (isref) {
-          if (passin)
-            thisval = ReadMemory(varglist[ix]);
-          else
-            thisval = 0;
-        }
-        else {
-          thisval = varglist[ix];
-        }
-
-        switch (typeclass) {
-        case 'I':
-          if (*cx == 'u')
-            garglist[gargnum]._uint = (uint)(thisval);
-          else if (*cx == 's')
-            garglist[gargnum]._sint = (int)(thisval);
-          else
-            error("Illegal format string.");
-          gargnum++;
-          cx++;
-          break;
-        case 'Q':
-          if (thisval) {
-            opref = classes_get(*cx-'a', thisval);
-            if (!opref) {
-              error("Reference to nonexistent Glk object.");
-            }
-          }
-          else {
-            opref = nullptr;
-          }
-          garglist[gargnum]._opaqueref = opref;
-          gargnum++;
-          cx++;
-          break;
-        case 'C':
-          if (*cx == 'u') 
-            garglist[gargnum]._uch = (unsigned char)(thisval);
-          else if (*cx == 's')
-            garglist[gargnum]._sch = (signed char)(thisval);
-          else if (*cx == 'n')
-            garglist[gargnum]._ch = (char)(thisval);
-          else
-            error("Illegal format string.");
-          gargnum++;
-          cx++;
-          break;
-        case 'S':
-          garglist[gargnum]._charstr = DecodeVMString(thisval);
-          gargnum++;
-          break;
+                            uint subaddress, int subpassin) {
+	const char *cx;
+	int ix, argx;
+	int gargnum, numwanted;
+	void *opref;
+	gluniversal_t *garglist;
+	uint *varglist;
+
+	garglist = splot->garglist;
+	varglist = splot->varglist;
+	gargnum = *argnumptr;
+	cx = *proto;
+
+	numwanted = 0;
+	while (*cx >= '0' && *cx <= '9') {
+		numwanted = 10 * numwanted + (*cx - '0');
+		cx++;
+	}
+
+	for (argx = 0, ix = 0; argx < numwanted; argx++, ix++) {
+		char typeclass;
+		int skipval;
+		int isref, passin, passout, nullok, isarray, isretained, isreturn;
+		cx = read_prefix(cx, &isref, &isarray, &passin, &passout, &nullok,
+		                 &isretained, &isreturn);
+
+		typeclass = *cx;
+		cx++;
+
+		skipval = false;
+		if (isref) {
+			if (!isreturn && varglist[ix] == 0) {
+				if (!nullok)
+					error("Zero passed invalidly to Glk function.");
+				garglist[gargnum]._ptrflag = false;
+				gargnum++;
+				skipval = true;
+			} else {
+				garglist[gargnum]._ptrflag = true;
+				gargnum++;
+			}
+		}
+		if (!skipval) {
+			uint thisval;
+
+			if (typeclass == '[') {
+
+				parse_glk_args(splot, &cx, depth + 1, &gargnum, varglist[ix], passin);
+
+			} else if (isarray) {
+				/* definitely isref */
+
+				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 = CaptureCArray(varglist[ix], varglist[ix + 1], passin);
+					gargnum++;
+					ix++;
+					garglist[gargnum]._uint = varglist[ix];
+					gargnum++;
+					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++;
+					garglist[gargnum]._uint = varglist[ix];
+					gargnum++;
+					cx++;
+					break;
+				case 'Q':
+					/* This case was added after the giant arrays were deprecated,
+					   so we don't bother to allow for that case. We just verify
+					   the length. */
+					verify_array_addresses(varglist[ix], varglist[ix + 1], 4);
+					garglist[gargnum]._array = CapturePtrArray(varglist[ix], varglist[ix + 1], (*cx - 'a'), passin);
+					gargnum++;
+					ix++;
+					garglist[gargnum]._uint = varglist[ix];
+					gargnum++;
+					cx++;
+					break;
+				default:
+					error("Illegal format string.");
+					break;
+				}
+			} else {
+				/* a plain value or a reference to one. */
+
+				if (isreturn) {
+					thisval = 0;
+				} else if (depth > 0) {
+					/* Definitely not isref or isarray. */
+					if (subpassin)
+						thisval = ReadStructField(subaddress, ix);
+					else
+						thisval = 0;
+				} else if (isref) {
+					if (passin)
+						thisval = ReadMemory(varglist[ix]);
+					else
+						thisval = 0;
+				} else {
+					thisval = varglist[ix];
+				}
+
+				switch (typeclass) {
+				case 'I':
+					if (*cx == 'u')
+						garglist[gargnum]._uint = (uint)(thisval);
+					else if (*cx == 's')
+						garglist[gargnum]._sint = (int)(thisval);
+					else
+						error("Illegal format string.");
+					gargnum++;
+					cx++;
+					break;
+				case 'Q':
+					if (thisval) {
+						opref = classes_get(*cx - 'a', thisval);
+						if (!opref) {
+							error("Reference to nonexistent Glk object.");
+						}
+					} else {
+						opref = nullptr;
+					}
+					garglist[gargnum]._opaqueref = opref;
+					gargnum++;
+					cx++;
+					break;
+				case 'C':
+					if (*cx == 'u')
+						garglist[gargnum]._uch = (unsigned char)(thisval);
+					else if (*cx == 's')
+						garglist[gargnum]._sch = (signed char)(thisval);
+					else if (*cx == 'n')
+						garglist[gargnum]._ch = (char)(thisval);
+					else
+						error("Illegal format string.");
+					gargnum++;
+					cx++;
+					break;
+				case 'S':
+					garglist[gargnum]._charstr = DecodeVMString(thisval);
+					gargnum++;
+					break;
 #ifdef GLK_MODULE_UNICODE
-        case 'U':
-          garglist[gargnum]._unicharstr = DecodeVMUstring(thisval);
-          gargnum++;
-          break;
+				case 'U':
+					garglist[gargnum]._unicharstr = DecodeVMUstring(thisval);
+					gargnum++;
+					break;
 #endif /* GLK_MODULE_UNICODE */
-        default:
-          error("Illegal format string.");
-          break;
-        }
-      }
-    }
-    else {
-      /* We got a null reference, so we have to skip the format element. */
-      if (typeclass == '[') {
-        int numsubwanted, refdepth;
-        numsubwanted = 0;
-        while (*cx >= '0' && *cx <= '9') {
-          numsubwanted = 10 * numsubwanted + (*cx - '0');
-          cx++;
-        }
-        refdepth = 1;
-        while (refdepth > 0) {
-          if (*cx == '[')
-            refdepth++;
-          else if (*cx == ']')
-            refdepth--;
-          cx++;
-        }
-      }
-      else if (typeclass == 'S' || typeclass == 'U') {
-        /* leave it */
-      }
-      else {
-        cx++;
-        if (isarray)
-          ix++;
-      }
-    }    
-  }
-
-  if (depth > 0) {
-    if (*cx != ']')
-      error("Illegal format string.");
-    cx++;
-  }
-  else {
-    if (*cx != ':' && *cx != '\0')
-      error("Illegal format string.");
-  }
-  
-  *proto = cx;
-  *argnumptr = gargnum;
+				default:
+					error("Illegal format string.");
+					break;
+				}
+			}
+		} else {
+			/* We got a null reference, so we have to skip the format element. */
+			if (typeclass == '[') {
+				int numsubwanted, refdepth;
+				numsubwanted = 0;
+				while (*cx >= '0' && *cx <= '9') {
+					numsubwanted = 10 * numsubwanted + (*cx - '0');
+					cx++;
+				}
+				refdepth = 1;
+				while (refdepth > 0) {
+					if (*cx == '[')
+						refdepth++;
+					else if (*cx == ']')
+						refdepth--;
+					cx++;
+				}
+			} else if (typeclass == 'S' || typeclass == 'U') {
+				/* leave it */
+			} else {
+				cx++;
+				if (isarray)
+					ix++;
+			}
+		}
+	}
+
+	if (depth > 0) {
+		if (*cx != ']')
+			error("Illegal format string.");
+		cx++;
+	} else {
+		if (*cx != ':' && *cx != '\0')
+			error("Illegal format string.");
+	}
+
+	*proto = cx;
+	*argnumptr = gargnum;
 }
 
 void Glulxe::unparse_glk_args(dispatch_splot_t *splot, const char **proto, int depth,
-  int *argnumptr, uint subaddress, int subpassout) {
-  const char *cx;
-  int ix, argx;
-  int gargnum, numwanted;
-  void *opref;
-  gluniversal_t *garglist;
-  uint *varglist;
-  
-  garglist = splot->garglist;
-  varglist = splot->varglist;
-  gargnum = *argnumptr;
-  cx = *proto;
-
-  numwanted = 0;
-  while (*cx >= '0' && *cx <= '9') {
-    numwanted = 10 * numwanted + (*cx - '0');
-    cx++;
-  }
-
-  for (argx = 0, ix = 0; argx < numwanted; argx++, ix++) {
-    char typeclass;
-    int skipval;
-    int isref, passin, passout, nullok, isarray, isretained, isreturn;
-    cx = read_prefix(cx, &isref, &isarray, &passin, &passout, &nullok,
-      &isretained, &isreturn);
-    
-    typeclass = *cx;
-    cx++;
-
-    skipval = false;
-    if (isref) {
-      if (!isreturn && varglist[ix] == 0) {
-        if (!nullok)
-          error("Zero passed invalidly to Glk function.");
-        garglist[gargnum]._ptrflag = false;
-        gargnum++;
-        skipval = true;
-      } else {
-        garglist[gargnum]._ptrflag = true;
-        gargnum++;
-      }
-    }
-    if (!skipval) {
-      uint thisval = 0;
-
-      if (typeclass == '[') {
-
-        unparse_glk_args(splot, &cx, depth+1, &gargnum, varglist[ix], passout);
-
-      }
-      else if (isarray) {
-        /* definitely isref */
-
-        switch (typeclass) {
-        case 'C':
-          ReleaseCArray((char *)garglist[gargnum]._array, varglist[ix], varglist[ix+1], passout);
-          gargnum++;
-          ix++;
-          gargnum++;
-          cx++;
-          break;
-        case 'I':
-          ReleaseIArray((uint *)garglist[gargnum]._array, varglist[ix], varglist[ix+1], passout);
-          gargnum++;
-          ix++;
-          gargnum++;
-          cx++;
-          break;
-        case 'Q':
-          ReleasePtrArray((void **)garglist[gargnum]._array, varglist[ix], varglist[ix+1], (*cx-'a'), passout);
-          gargnum++;
-          ix++;
-          gargnum++;
-          cx++;
-          break;
-        default:
-          error("Illegal format string.");
-          break;
-        }
-      }
-      else {
-        /* a plain value or a reference to one. */
-
-        if (isreturn || (depth > 0 && subpassout) || (isref && passout)) {
-          skipval = false;
-        }
-        else {
-          skipval = true;
-        }
-
-        switch (typeclass) {
-        case 'I':
-          if (!skipval) {
-            if (*cx == 'u')
-              thisval = (uint)garglist[gargnum]._uint;
-            else if (*cx == 's')
-              thisval = (uint)garglist[gargnum]._sint;
-            else
-              error("Illegal format string.");
-          }
-          gargnum++;
-          cx++;
-          break;
-        case 'Q':
-          if (!skipval) {
-            opref = garglist[gargnum]._opaqueref;
-            if (opref) {
-              gidispatch_rock_t objrock = gidispatch_get_objrock(opref, *cx - 'a');
-              thisval = ((classref_t *)objrock.ptr)->id;
-            }
-            else {
-              thisval = 0;
-            }
-          }
-          gargnum++;
-          cx++;
-          break;
-        case 'C':
-          if (!skipval) {
-            if (*cx == 'u') 
-              thisval = (uint)garglist[gargnum]._uch;
-            else if (*cx == 's')
-              thisval = (uint)garglist[gargnum]._sch;
-            else if (*cx == 'n')
-              thisval = (uint)garglist[gargnum]._ch;
-            else
-              error("Illegal format string.");
-          }
-          gargnum++;
-          cx++;
-          break;
-        case 'S':
-          if (garglist[gargnum]._charstr)
-            ReleaseVMString(garglist[gargnum]._charstr);
-          gargnum++;
-          break;
+                              int *argnumptr, uint subaddress, int subpassout) {
+	const char *cx;
+	int ix, argx;
+	int gargnum, numwanted;
+	void *opref;
+	gluniversal_t *garglist;
+	uint *varglist;
+
+	garglist = splot->garglist;
+	varglist = splot->varglist;
+	gargnum = *argnumptr;
+	cx = *proto;
+
+	numwanted = 0;
+	while (*cx >= '0' && *cx <= '9') {
+		numwanted = 10 * numwanted + (*cx - '0');
+		cx++;
+	}
+
+	for (argx = 0, ix = 0; argx < numwanted; argx++, ix++) {
+		char typeclass;
+		int skipval;
+		int isref, passin, passout, nullok, isarray, isretained, isreturn;
+		cx = read_prefix(cx, &isref, &isarray, &passin, &passout, &nullok,
+		                 &isretained, &isreturn);
+
+		typeclass = *cx;
+		cx++;
+
+		skipval = false;
+		if (isref) {
+			if (!isreturn && varglist[ix] == 0) {
+				if (!nullok)
+					error("Zero passed invalidly to Glk function.");
+				garglist[gargnum]._ptrflag = false;
+				gargnum++;
+				skipval = true;
+			} else {
+				garglist[gargnum]._ptrflag = true;
+				gargnum++;
+			}
+		}
+		if (!skipval) {
+			uint thisval = 0;
+
+			if (typeclass == '[') {
+
+				unparse_glk_args(splot, &cx, depth + 1, &gargnum, varglist[ix], passout);
+
+			} else if (isarray) {
+				/* definitely isref */
+
+				switch (typeclass) {
+				case 'C':
+					ReleaseCArray((char *)garglist[gargnum]._array, varglist[ix], varglist[ix + 1], passout);
+					gargnum++;
+					ix++;
+					gargnum++;
+					cx++;
+					break;
+				case 'I':
+					ReleaseIArray((uint *)garglist[gargnum]._array, varglist[ix], varglist[ix + 1], passout);
+					gargnum++;
+					ix++;
+					gargnum++;
+					cx++;
+					break;
+				case 'Q':
+					ReleasePtrArray((void **)garglist[gargnum]._array, varglist[ix], varglist[ix + 1], (*cx - 'a'), passout);
+					gargnum++;
+					ix++;
+					gargnum++;
+					cx++;
+					break;
+				default:
+					error("Illegal format string.");
+					break;
+				}
+			} else {
+				/* a plain value or a reference to one. */
+
+				if (isreturn || (depth > 0 && subpassout) || (isref && passout)) {
+					skipval = false;
+				} else {
+					skipval = true;
+				}
+
+				switch (typeclass) {
+				case 'I':
+					if (!skipval) {
+						if (*cx == 'u')
+							thisval = (uint)garglist[gargnum]._uint;
+						else if (*cx == 's')
+							thisval = (uint)garglist[gargnum]._sint;
+						else
+							error("Illegal format string.");
+					}
+					gargnum++;
+					cx++;
+					break;
+				case 'Q':
+					if (!skipval) {
+						opref = garglist[gargnum]._opaqueref;
+						if (opref) {
+							gidispatch_rock_t objrock = gidispatch_get_objrock(opref, *cx - 'a');
+							thisval = ((classref_t *)objrock.ptr)->id;
+						} else {
+							thisval = 0;
+						}
+					}
+					gargnum++;
+					cx++;
+					break;
+				case 'C':
+					if (!skipval) {
+						if (*cx == 'u')
+							thisval = (uint)garglist[gargnum]._uch;
+						else if (*cx == 's')
+							thisval = (uint)garglist[gargnum]._sch;
+						else if (*cx == 'n')
+							thisval = (uint)garglist[gargnum]._ch;
+						else
+							error("Illegal format string.");
+					}
+					gargnum++;
+					cx++;
+					break;
+				case 'S':
+					if (garglist[gargnum]._charstr)
+						ReleaseVMString(garglist[gargnum]._charstr);
+					gargnum++;
+					break;
 #ifdef GLK_MODULE_UNICODE
-        case 'U':
-          if (garglist[gargnum]._unicharstr)
-            ReleaseVMUstring(garglist[gargnum]._unicharstr);
-          gargnum++;
-          break;
+				case 'U':
+					if (garglist[gargnum]._unicharstr)
+						ReleaseVMUstring(garglist[gargnum]._unicharstr);
+					gargnum++;
+					break;
 #endif /* GLK_MODULE_UNICODE */
-        default:
-          error("Illegal format string.");
-          break;
-        }
-
-        if (isreturn) {
-          *(splot->retval) = thisval;
-        }
-        else if (depth > 0) {
-          /* Definitely not isref or isarray. */
-          if (subpassout)
-            WriteStructField(subaddress, ix, thisval);
-        }
-        else if (isref) {
-          if (passout)
-            WriteMemory(varglist[ix], thisval); 
-        }
-      }
-    }
-    else {
-      /* We got a null reference, so we have to skip the format element. */
-      if (typeclass == '[') {
-        int numsubwanted, refdepth;
-        numsubwanted = 0;
-        while (*cx >= '0' && *cx <= '9') {
-          numsubwanted = 10 * numsubwanted + (*cx - '0');
-          cx++;
-        }
-        refdepth = 1;
-        while (refdepth > 0) {
-          if (*cx == '[')
-            refdepth++;
-          else if (*cx == ']')
-            refdepth--;
-          cx++;
-        }
-      }
-      else if (typeclass == 'S' || typeclass == 'U') {
-        /* leave it */
-      }
-      else {
-        cx++;
-        if (isarray)
-          ix++;
-      }
-    }    
-  }
-
-  if (depth > 0) {
-    if (*cx != ']')
-      error("Illegal format string.");
-    cx++;
-  }
-  else {
-    if (*cx != ':' && *cx != '\0')
-      error("Illegal format string.");
-  }
-  
-  *proto = cx;
-  *argnumptr = gargnum;
+				default:
+					error("Illegal format string.");
+					break;
+				}
+
+				if (isreturn) {
+					*(splot->retval) = thisval;
+				} else if (depth > 0) {
+					/* Definitely not isref or isarray. */
+					if (subpassout)
+						WriteStructField(subaddress, ix, thisval);
+				} else if (isref) {
+					if (passout)
+						WriteMemory(varglist[ix], thisval);
+				}
+			}
+		} else {
+			/* We got a null reference, so we have to skip the format element. */
+			if (typeclass == '[') {
+				int numsubwanted, refdepth;
+				numsubwanted = 0;
+				while (*cx >= '0' && *cx <= '9') {
+					numsubwanted = 10 * numsubwanted + (*cx - '0');
+					cx++;
+				}
+				refdepth = 1;
+				while (refdepth > 0) {
+					if (*cx == '[')
+						refdepth++;
+					else if (*cx == ']')
+						refdepth--;
+					cx++;
+				}
+			} else if (typeclass == 'S' || typeclass == 'U') {
+				/* leave it */
+			} else {
+				cx++;
+				if (isarray)
+					ix++;
+			}
+		}
+	}
+
+	if (depth > 0) {
+		if (*cx != ']')
+			error("Illegal format string.");
+		cx++;
+	} else {
+		if (*cx != ':' && *cx != '\0')
+			error("Illegal format string.");
+	}
+
+	*proto = cx;
+	*argnumptr = gargnum;
 }
 
 strid_t Glulxe::find_stream_by_id(uint objid) {
-  if (!objid)
-    return nullptr;
+	if (!objid)
+		return nullptr;
 
-  // Recall that class 1 ("b") is streams
-  return (strid_t)classes_get(gidisp_Class_Stream, objid);
+	// Recall that class 1 ("b") is streams
+	return (strid_t)classes_get(gidisp_Class_Stream, objid);
 }
 
 uint Glulxe::find_id_for_window(winid_t win) {
-  gidispatch_rock_t objrock;
+	gidispatch_rock_t objrock;
 
-  if (!win)
-    return 0;
+	if (!win)
+		return 0;
 
-  objrock = gidispatch_get_objrock(win, gidisp_Class_Window);
-  if (!objrock.ptr)
-    return 0;
-  return ((classref_t *)objrock.ptr)->id;
+	objrock = gidispatch_get_objrock(win, gidisp_Class_Window);
+	if (!objrock.ptr)
+		return 0;
+	return ((classref_t *)objrock.ptr)->id;
 }
 
 uint Glulxe::find_id_for_stream(strid_t str) {
-  gidispatch_rock_t objrock;
+	gidispatch_rock_t objrock;
 
-  if (!str)
-    return 0;
+	if (!str)
+		return 0;
 
-  objrock = gidispatch_get_objrock(str, gidisp_Class_Stream);
-  if (!objrock.ptr)
-    return 0;
-  return ((classref_t *)objrock.ptr)->id;
+	objrock = gidispatch_get_objrock(str, gidisp_Class_Stream);
+	if (!objrock.ptr)
+		return 0;
+	return ((classref_t *)objrock.ptr)->id;
 }
 
 uint Glulxe::find_id_for_fileref(frefid_t fref) {
-  gidispatch_rock_t objrock;
+	gidispatch_rock_t objrock;
 
-  if (!fref)
-    return 0;
+	if (!fref)
+		return 0;
 
-  objrock = gidispatch_get_objrock(fref, gidisp_Class_Fileref);
-  if (!objrock.ptr)
-    return 0;
-  return ((classref_t *)objrock.ptr)->id;
+	objrock = gidispatch_get_objrock(fref, gidisp_Class_Fileref);
+	if (!objrock.ptr)
+		return 0;
+	return ((classref_t *)objrock.ptr)->id;
 }
 
 uint Glulxe::find_id_for_schannel(schanid_t schan) {
-  gidispatch_rock_t objrock;
+	gidispatch_rock_t objrock;
 
-  if (!schan)
-    return 0;
+	if (!schan)
+		return 0;
 
-  objrock = gidispatch_get_objrock(schan, gidisp_Class_Schannel);
-  if (!objrock.ptr)
-    return 0;
-  return ((classref_t *)objrock.ptr)->id;
+	objrock = gidispatch_get_objrock(schan, gidisp_Class_Schannel);
+	if (!objrock.ptr)
+		return 0;
+	return ((classref_t *)objrock.ptr)->id;
 }
 
 classtable_t *Glulxe::new_classtable(uint firstid) {
-  int ix;
-  classtable_t *ctab = (classtable_t *)glulx_malloc(sizeof(classtable_t));
-  if (!ctab)
-    return nullptr;
-    
-  for (ix=0; ix<CLASSHASH_SIZE; ix++)
-    ctab->bucket[ix] = nullptr;
-    
-  ctab->lastid = firstid;
-    
-  return ctab;
+	int ix;
+	classtable_t *ctab = (classtable_t *)glulx_malloc(sizeof(classtable_t));
+	if (!ctab)
+		return nullptr;
+
+	for (ix = 0; ix < CLASSHASH_SIZE; ix++)
+		ctab->bucket[ix] = nullptr;
+
+	ctab->lastid = firstid;
+
+	return ctab;
 }
 
 void *Glulxe::classes_get(int classid, uint objid) {
-  classtable_t *ctab;
-  classref_t *cref;
-  if (classid < 0 || classid >= num_classes)
-    return nullptr;
-  ctab = classes[classid];
-  cref = ctab->bucket[objid % CLASSHASH_SIZE];
-  for (; cref; cref = cref->next) {
-    if (cref->id == objid)
-      return cref->obj;
-  }
-  return nullptr;
+	classtable_t *ctab;
+	classref_t *cref;
+	if (classid < 0 || classid >= num_classes)
+		return nullptr;
+	ctab = classes[classid];
+	cref = ctab->bucket[objid % CLASSHASH_SIZE];
+	for (; cref; cref = cref->next) {
+		if (cref->id == objid)
+			return cref->obj;
+	}
+	return nullptr;
 }
 
 classref_t *Glulxe::classes_put(int classid, void *obj, uint origid) {
-  int bucknum;
-  classtable_t *ctab;
-  classref_t *cref;
-  if (classid < 0 || classid >= num_classes)
-    return nullptr;
-  ctab = classes[classid];
-  cref = (classref_t *)glulx_malloc(sizeof(classref_t));
-  if (!cref)
-    return nullptr;
-  cref->obj = obj;
-  if (!origid) {
-    cref->id = ctab->lastid;
-    ctab->lastid++;
-  }
-  else {
-    cref->id = origid;
-    if (ctab->lastid <= origid)
-      ctab->lastid = origid+1;
-  }
-  bucknum = cref->id % CLASSHASH_SIZE;
-  cref->bucknum = bucknum;
-  cref->next = ctab->bucket[bucknum];
-  ctab->bucket[bucknum] = cref;
-  return cref;
+	int bucknum;
+	classtable_t *ctab;
+	classref_t *cref;
+	if (classid < 0 || classid >= num_classes)
+		return nullptr;
+	ctab = classes[classid];
+	cref = (classref_t *)glulx_malloc(sizeof(classref_t));
+	if (!cref)
+		return nullptr;
+	cref->obj = obj;
+	if (!origid) {
+		cref->id = ctab->lastid;
+		ctab->lastid++;
+	} else {
+		cref->id = origid;
+		if (ctab->lastid <= origid)
+			ctab->lastid = origid + 1;
+	}
+	bucknum = cref->id % CLASSHASH_SIZE;
+	cref->bucknum = bucknum;
+	cref->next = ctab->bucket[bucknum];
+	ctab->bucket[bucknum] = cref;
+	return cref;
 }
 
-void Glulxe::classes_remove(int classid, void *obj)
-{
-  classtable_t *ctab;
-  classref_t *cref;
-  classref_t **crefp;
-  gidispatch_rock_t objrock;
-  if (classid < 0 || classid >= num_classes)
-    return;
-  ctab = classes[classid];
-  objrock = gidispatch_get_objrock(obj, classid);
-  cref = (classref_t *)objrock.ptr;
-  if (!cref)
-    return;
-  crefp = &(ctab->bucket[cref->bucknum]);
-  for (; *crefp; crefp = &((*crefp)->next)) {
-    if ((*crefp) == cref) {
-      *crefp = cref->next;
-      if (!cref->obj) {
-        nonfatal_warning("attempt to free nullptr object!");
-      }
-      cref->obj = nullptr;
-      cref->id = 0;
-      cref->next = nullptr;
-      glulx_free(cref);
-      return;
-    }
-  }
-  return;
+void Glulxe::classes_remove(int classid, void *obj) {
+	classtable_t *ctab;
+	classref_t *cref;
+	classref_t **crefp;
+	gidispatch_rock_t objrock;
+	if (classid < 0 || classid >= num_classes)
+		return;
+	ctab = classes[classid];
+	objrock = gidispatch_get_objrock(obj, classid);
+	cref = (classref_t *)objrock.ptr;
+	if (!cref)
+		return;
+	crefp = &(ctab->bucket[cref->bucknum]);
+	for (; *crefp; crefp = &((*crefp)->next)) {
+		if ((*crefp) == cref) {
+			*crefp = cref->next;
+			if (!cref->obj) {
+				nonfatal_warning("attempt to free nullptr object!");
+			}
+			cref->obj = nullptr;
+			cref->id = 0;
+			cref->next = nullptr;
+			glulx_free(cref);
+			return;
+		}
+	}
+	return;
 }
 
 gidispatch_rock_t Glulxe::glulxe_classtable_register(void *obj,  uint objclass) {
-  classref_t *cref;
-  gidispatch_rock_t objrock;
-  cref = classes_put(objclass, obj, 0);
-  objrock.ptr = cref;
-  return objrock;
+	classref_t *cref;
+	gidispatch_rock_t objrock;
+	cref = classes_put(objclass, obj, 0);
+	objrock.ptr = cref;
+	return objrock;
 }
 
-void Glulxe::glulxe_classtable_unregister(void *obj, uint objclass, 
-  gidispatch_rock_t objrock) {
-  classes_remove(objclass, obj);
+void Glulxe::glulxe_classtable_unregister(void *obj, uint objclass,
+        gidispatch_rock_t objrock) {
+	classes_remove(objclass, obj);
 }
 
 gidispatch_rock_t Glulxe::glulxe_classtable_register_existing(void *obj, uint objclass, uint dispid) {
-  classref_t *cref;
-  gidispatch_rock_t objrock;
-  cref = classes_put(objclass, obj, dispid);
-  objrock.ptr = cref;
-  return objrock;
+	classref_t *cref;
+	gidispatch_rock_t objrock;
+	cref = classes_put(objclass, obj, dispid);
+	objrock.ptr = cref;
+	return objrock;
 }
 
 char *Glulxe::grab_temp_c_array(uint addr, uint len, int passin) {
-  arrayref_t *arref = nullptr;
-  char *arr = nullptr;
-  uint ix, addr2;
-
-  if (len) {
-    arr = (char *)glulx_malloc(len * sizeof(char));
-    arref = (arrayref_t *)glulx_malloc(sizeof(arrayref_t));
-    if (!arr || !arref) 
-      error("Unable to allocate space for array argument to Glk call.");
-
-    arref->array = arr;
-    arref->addr = addr;
-    arref->elemsize = 1;
-    arref->retained = false;
-    arref->len = len;
-    arref->next = arrays;
-    arrays = arref;
-
-    if (passin) {
-      for (ix=0, addr2=addr; ix<len; ix++, addr2+=1) {
-        arr[ix] = Mem1(addr2);
-      }
-    }
-  }
-
-  return arr;
+	arrayref_t *arref = nullptr;
+	char *arr = nullptr;
+	uint ix, addr2;
+
+	if (len) {
+		arr = (char *)glulx_malloc(len * sizeof(char));
+		arref = (arrayref_t *)glulx_malloc(sizeof(arrayref_t));
+		if (!arr || !arref)
+			error("Unable to allocate space for array argument to Glk call.");
+
+		arref->array = arr;
+		arref->addr = addr;
+		arref->elemsize = 1;
+		arref->retained = false;
+		arref->len = len;
+		arref->next = arrays;
+		arrays = arref;
+
+		if (passin) {
+			for (ix = 0, addr2 = addr; ix < len; ix++, addr2 += 1) {
+				arr[ix] = Mem1(addr2);
+			}
+		}
+	}
+
+	return arr;
 }
 
 void Glulxe::release_temp_c_array(char *arr, uint addr, uint len, int passout) {
-  arrayref_t *arref = nullptr;
-  arrayref_t **aptr;
-  uint ix, val, addr2;
-
-  if (arr) {
-    for (aptr=(&arrays); (*aptr); aptr=(&((*aptr)->next))) {
-      if ((*aptr)->array == arr)
-        break;
-    }
-    arref = *aptr;
-    if (!arref)
-      error("Unable to re-find array argument in Glk call.");
-    if (arref->addr != addr || arref->len != len)
-      error("Mismatched array argument in Glk call.");
-
-    if (arref->retained) {
-      return;
-    }
-
-    *aptr = arref->next;
-    arref->next = nullptr;
-
-    if (passout) {
-      for (ix=0, addr2=addr; ix<len; ix++, addr2+=1) {
-        val = arr[ix];
-        MemW1(addr2, val);
-      }
-    }
-    glulx_free(arr);
-    glulx_free(arref);
-  }
+	arrayref_t *arref = nullptr;
+	arrayref_t **aptr;
+	uint ix, val, addr2;
+
+	if (arr) {
+		for (aptr = (&arrays); (*aptr); aptr = (&((*aptr)->next))) {
+			if ((*aptr)->array == arr)
+				break;
+		}
+		arref = *aptr;
+		if (!arref)
+			error("Unable to re-find array argument in Glk call.");
+		if (arref->addr != addr || arref->len != len)
+			error("Mismatched array argument in Glk call.");
+
+		if (arref->retained) {
+			return;
+		}
+
+		*aptr = arref->next;
+		arref->next = nullptr;
+
+		if (passout) {
+			for (ix = 0, addr2 = addr; ix < len; ix++, addr2 += 1) {
+				val = arr[ix];
+				MemW1(addr2, val);
+			}
+		}
+		glulx_free(arr);
+		glulx_free(arref);
+	}
 }
 
 uint *Glulxe::grab_temp_i_array(uint addr, uint len, int passin) {
-  arrayref_t *arref = nullptr;
-  uint *arr = nullptr;
-  uint ix, addr2;
-
-  if (len) {
-    arr = (uint *)glulx_malloc(len * sizeof(uint));
-    arref = (arrayref_t *)glulx_malloc(sizeof(arrayref_t));
-    if (!arr || !arref) 
-      error("Unable to allocate space for array argument to Glk call.");
-
-    arref->array = arr;
-    arref->addr = addr;
-    arref->elemsize = 4;
-    arref->retained = false;
-    arref->len = len;
-    arref->next = arrays;
-    arrays = arref;
-
-    if (passin) {
-      for (ix=0, addr2=addr; ix<len; ix++, addr2+=4) {
-        arr[ix] = Mem4(addr2);
-      }
-    }
-  }
-
-  return arr;
+	arrayref_t *arref = nullptr;
+	uint *arr = nullptr;
+	uint ix, addr2;
+
+	if (len) {
+		arr = (uint *)glulx_malloc(len * sizeof(uint));
+		arref = (arrayref_t *)glulx_malloc(sizeof(arrayref_t));
+		if (!arr || !arref)
+			error("Unable to allocate space for array argument to Glk call.");
+
+		arref->array = arr;
+		arref->addr = addr;
+		arref->elemsize = 4;
+		arref->retained = false;
+		arref->len = len;
+		arref->next = arrays;
+		arrays = arref;
+
+		if (passin) {
+			for (ix = 0, addr2 = addr; ix < len; ix++, addr2 += 4) {
+				arr[ix] = Mem4(addr2);
+			}
+		}
+	}
+
+	return arr;
 }
 
 void Glulxe::release_temp_i_array(uint *arr, uint addr, uint len, int passout) {
-  arrayref_t *arref = nullptr;
-  arrayref_t **aptr;
-  uint ix, val, addr2;
-
-  if (arr) {
-    for (aptr=(&arrays); (*aptr); aptr=(&((*aptr)->next))) {
-      if ((*aptr)->array == arr)
-        break;
-    }
-    arref = *aptr;
-    if (!arref)
-      error("Unable to re-find array argument in Glk call.");
-    if (arref->addr != addr || arref->len != len)
-      error("Mismatched array argument in Glk call.");
-
-    if (arref->retained) {
-      return;
-    }
-
-    *aptr = arref->next;
-    arref->next = nullptr;
-
-    if (passout) {
-      for (ix=0, addr2=addr; ix<len; ix++, addr2+=4) {
-        val = arr[ix];
-        MemW4(addr2, val);
-      }
-    }
-    glulx_free(arr);
-    glulx_free(arref);
-  }
+	arrayref_t *arref = nullptr;
+	arrayref_t **aptr;
+	uint ix, val, addr2;
+
+	if (arr) {
+		for (aptr = (&arrays); (*aptr); aptr = (&((*aptr)->next))) {
+			if ((*aptr)->array == arr)
+				break;
+		}
+		arref = *aptr;
+		if (!arref)
+			error("Unable to re-find array argument in Glk call.");
+		if (arref->addr != addr || arref->len != len)
+			error("Mismatched array argument in Glk call.");
+
+		if (arref->retained) {
+			return;
+		}
+
+		*aptr = arref->next;
+		arref->next = nullptr;
+
+		if (passout) {
+			for (ix = 0, addr2 = addr; ix < len; ix++, addr2 += 4) {
+				val = arr[ix];
+				MemW4(addr2, val);
+			}
+		}
+		glulx_free(arr);
+		glulx_free(arref);
+	}
 }
 
 void **Glulxe::grab_temp_ptr_array(uint addr, uint len, int objclass, int passin) {
-  arrayref_t *arref = nullptr;
-  void **arr = nullptr;
-  uint ix, addr2;
-
-  if (len) {
-    arr = (void **)glulx_malloc(len * sizeof(void *));
-    arref = (arrayref_t *)glulx_malloc(sizeof(arrayref_t));
-    if (!arr || !arref) 
-      error("Unable to allocate space for array argument to Glk call.");
-
-    arref->array = arr;
-    arref->addr = addr;
-    arref->elemsize = sizeof(void *);
-    arref->retained = false;
-    arref->len = len;
-    arref->next = arrays;
-    arrays = arref;
-
-    if (passin) {
-      for (ix=0, addr2=addr; ix<len; ix++, addr2+=4) {
-        uint thisval = Mem4(addr2);
-        if (thisval)
-          arr[ix] = classes_get(objclass, thisval);
-        else
-          arr[ix] = nullptr;
-      }
-    }
-  }
-
-  return arr;
+	arrayref_t *arref = nullptr;
+	void **arr = nullptr;
+	uint ix, addr2;
+
+	if (len) {
+		arr = (void **)glulx_malloc(len * sizeof(void *));
+		arref = (arrayref_t *)glulx_malloc(sizeof(arrayref_t));
+		if (!arr || !arref)
+			error("Unable to allocate space for array argument to Glk call.");
+
+		arref->array = arr;
+		arref->addr = addr;
+		arref->elemsize = sizeof(void *);
+		arref->retained = false;
+		arref->len = len;
+		arref->next = arrays;
+		arrays = arref;
+
+		if (passin) {
+			for (ix = 0, addr2 = addr; ix < len; ix++, addr2 += 4) {
+				uint thisval = Mem4(addr2);
+				if (thisval)
+					arr[ix] = classes_get(objclass, thisval);
+				else
+					arr[ix] = nullptr;
+			}
+		}
+	}
+
+	return arr;
 }
 
 void Glulxe::release_temp_ptr_array(void **arr, uint addr, uint len, int objclass, int passout) {
-  arrayref_t *arref = nullptr;
-  arrayref_t **aptr;
-  uint ix, val, addr2;
-
-  if (arr) {
-    for (aptr=(&arrays); (*aptr); aptr=(&((*aptr)->next))) {
-      if ((*aptr)->array == arr)
-        break;
-    }
-    arref = *aptr;
-    if (!arref)
-      error("Unable to re-find array argument in Glk call.");
-    if (arref->addr != addr || arref->len != len)
-      error("Mismatched array argument in Glk call.");
-
-    if (arref->retained) {
-      return;
-    }
-
-    *aptr = arref->next;
-    arref->next = nullptr;
-
-    if (passout) {
-      for (ix=0, addr2=addr; ix<len; ix++, addr2+=4) {
-        void *opref = arr[ix];
-        if (opref) {
-          gidispatch_rock_t objrock = 
-            gidispatch_get_objrock(opref, objclass);
-          val = ((classref_t *)objrock.ptr)->id;
-        }
-        else {
-          val = 0;
-        }
-        MemW4(addr2, val);
-      }
-    }
-    glulx_free(arr);
-    glulx_free(arref);
-  }
+	arrayref_t *arref = nullptr;
+	arrayref_t **aptr;
+	uint ix, val, addr2;
+
+	if (arr) {
+		for (aptr = (&arrays); (*aptr); aptr = (&((*aptr)->next))) {
+			if ((*aptr)->array == arr)
+				break;
+		}
+		arref = *aptr;
+		if (!arref)
+			error("Unable to re-find array argument in Glk call.");
+		if (arref->addr != addr || arref->len != len)
+			error("Mismatched array argument in Glk call.");
+
+		if (arref->retained) {
+			return;
+		}
+
+		*aptr = arref->next;
+		arref->next = nullptr;
+
+		if (passout) {
+			for (ix = 0, addr2 = addr; ix < len; ix++, addr2 += 4) {
+				void *opref = arr[ix];
+				if (opref) {
+					gidispatch_rock_t objrock =
+					    gidispatch_get_objrock(opref, objclass);
+					val = ((classref_t *)objrock.ptr)->id;
+				} else {
+					val = 0;
+				}
+				MemW4(addr2, val);
+			}
+		}
+		glulx_free(arr);
+		glulx_free(arref);
+	}
 }
 
 gidispatch_rock_t Glulxe::glulxe_retained_register(void *array, uint len, char *typecode) {
-  gidispatch_rock_t rock;
-  arrayref_t *arref = nullptr;
-  arrayref_t **aptr;
-  uint elemsize = 0;
-
-  if (typecode[4] == 'C')
-    elemsize = 1;
-  else if (typecode[4] == 'I')
-    elemsize = 4;
-
-  if (!elemsize || array == nullptr) {
-    rock.ptr = nullptr;
-    return rock;
-  }
-
-  for (aptr=(&arrays); (*aptr); aptr=(&((*aptr)->next))) {
-    if ((*aptr)->array == array)
-      break;
-  }
-  arref = *aptr;
-  if (!arref)
-    error("Unable to re-find array argument in Glk call.");
-  if (arref->elemsize != elemsize || arref->len != len)
-    error("Mismatched array argument in Glk call.");
-
-  arref->retained = true;
-
-  rock.ptr = arref;
-  return rock;
+	gidispatch_rock_t rock;
+	arrayref_t *arref = nullptr;
+	arrayref_t **aptr;
+	uint elemsize = 0;
+
+	if (typecode[4] == 'C')
+		elemsize = 1;
+	else if (typecode[4] == 'I')
+		elemsize = 4;
+
+	if (!elemsize || array == nullptr) {
+		rock.ptr = nullptr;
+		return rock;
+	}
+
+	for (aptr = (&arrays); (*aptr); aptr = (&((*aptr)->next))) {
+		if ((*aptr)->array == array)
+			break;
+	}
+	arref = *aptr;
+	if (!arref)
+		error("Unable to re-find array argument in Glk call.");
+	if (arref->elemsize != elemsize || arref->len != len)
+		error("Mismatched array argument in Glk call.");
+
+	arref->retained = true;
+
+	rock.ptr = arref;
+	return rock;
 }
 
 void Glulxe::glulxe_retained_unregister(void *array, uint len, char *typecode, gidispatch_rock_t objrock) {
-  arrayref_t *arref = nullptr;
-  arrayref_t **aptr;
-  uint ix, addr2, val;
-  uint elemsize = 0;
-
-  if (typecode[4] == 'C')
-    elemsize = 1;
-  else if (typecode[4] == 'I')
-    elemsize = 4;
-
-  if (!elemsize || array == nullptr) {
-    return;
-  }
-
-  for (aptr=(&arrays); (*aptr); aptr=(&((*aptr)->next))) {
-    if ((*aptr)->array == array)
-      break;
-  }
-  arref = *aptr;
-  if (!arref)
-    error("Unable to re-find array argument in Glk call.");
-  if (arref != objrock.ptr)
-    error("Mismatched array reference in Glk call.");
-  if (!arref->retained)
-    error("Unretained array reference in Glk call.");
-  if (arref->elemsize != elemsize || arref->len != len)
-    error("Mismatched array argument in Glk call.");
-
-  *aptr = arref->next;
-  arref->next = nullptr;
-
-  if (elemsize == 1) {
-    for (ix=0, addr2=arref->addr; ix<arref->len; ix++, addr2+=1) {
-      val = ((char *)array)[ix];
-      MemW1(addr2, val);
-    }
-  }
-  else if (elemsize == 4) {
-    for (ix=0, addr2=arref->addr; ix<arref->len; ix++, addr2+=4) {
-      val = ((uint *)array)[ix];
-      MemW4(addr2, val);
-    }
-  }
-
-  glulx_free(array);
-  glulx_free(arref);
+	arrayref_t *arref = nullptr;
+	arrayref_t **aptr;
+	uint ix, addr2, val;
+	uint elemsize = 0;
+
+	if (typecode[4] == 'C')
+		elemsize = 1;
+	else if (typecode[4] == 'I')
+		elemsize = 4;
+
+	if (!elemsize || array == nullptr) {
+		return;
+	}
+
+	for (aptr = (&arrays); (*aptr); aptr = (&((*aptr)->next))) {
+		if ((*aptr)->array == array)
+			break;
+	}
+	arref = *aptr;
+	if (!arref)
+		error("Unable to re-find array argument in Glk call.");
+	if (arref != objrock.ptr)
+		error("Mismatched array reference in Glk call.");
+	if (!arref->retained)
+		error("Unretained array reference in Glk call.");
+	if (arref->elemsize != elemsize || arref->len != len)
+		error("Mismatched array argument in Glk call.");
+
+	*aptr = arref->next;
+	arref->next = nullptr;
+
+	if (elemsize == 1) {
+		for (ix = 0, addr2 = arref->addr; ix < arref->len; ix++, addr2 += 1) {
+			val = ((char *)array)[ix];
+			MemW1(addr2, val);
+		}
+	} else if (elemsize == 4) {
+		for (ix = 0, addr2 = arref->addr; ix < arref->len; ix++, addr2 += 4) {
+			val = ((uint *)array)[ix];
+			MemW4(addr2, val);
+		}
+	}
+
+	glulx_free(array);
+	glulx_free(arref);
 }
 
 long Glulxe::glulxe_array_locate(void *array, uint len, char *typecode, gidispatch_rock_t objrock, int *elemsizeref) {
-  arrayref_t *arref = nullptr;
-  arrayref_t **aptr;
-  uint elemsize = 0;
-
-  if (typecode[4] == 'C')
-    elemsize = 1;
-  else if (typecode[4] == 'I')
-    elemsize = 4;
-
-  if (!elemsize || array == nullptr) {
-    *elemsizeref = 0; /* No need to save the array separately */
-    return (unsigned char *)array - memmap;
-  }
-  
-  for (aptr=(&arrays); (*aptr); aptr=(&((*aptr)->next))) {
-    if ((*aptr)->array == array)
-      break;
-  }
-  arref = *aptr;
-  if (!arref)
-    error("Unable to re-find array argument in array_locate.");
-  if (arref != objrock.ptr)
-    error("Mismatched array reference in array_locate.");
-  if (!arref->retained)
-    error("Unretained array reference in array_locate.");
-  if (arref->elemsize != elemsize || arref->len != len)
-    error("Mismatched array argument in array_locate.");
-  
-  *elemsizeref = arref->elemsize;
-  return arref->addr;
+	arrayref_t *arref = nullptr;
+	arrayref_t **aptr;
+	uint elemsize = 0;
+
+	if (typecode[4] == 'C')
+		elemsize = 1;
+	else if (typecode[4] == 'I')
+		elemsize = 4;
+
+	if (!elemsize || array == nullptr) {
+		*elemsizeref = 0; /* No need to save the array separately */
+		return (unsigned char *)array - memmap;
+	}
+
+	for (aptr = (&arrays); (*aptr); aptr = (&((*aptr)->next))) {
+		if ((*aptr)->array == array)
+			break;
+	}
+	arref = *aptr;
+	if (!arref)
+		error("Unable to re-find array argument in array_locate.");
+	if (arref != objrock.ptr)
+		error("Mismatched array reference in array_locate.");
+	if (!arref->retained)
+		error("Unretained array reference in array_locate.");
+	if (arref->elemsize != elemsize || arref->len != len)
+		error("Mismatched array argument in array_locate.");
+
+	*elemsizeref = arref->elemsize;
+	return arref->addr;
 }
 
 gidispatch_rock_t Glulxe::glulxe_array_restore(long bufkey, uint len, char *typecode, void **arrayref) {
-  gidispatch_rock_t rock;
-  int elemsize = 0;
-
-  if (typecode[4] == 'C')
-    elemsize = 1;
-  else if (typecode[4] == 'I')
-    elemsize = 4;
-
-  if (!elemsize) {
-    unsigned char *buf = memmap + bufkey;
-    *arrayref = buf;
-    rock.ptr = nullptr;
-    return rock;
-  }
-
-  if (elemsize == 1) {
-    char *cbuf = grab_temp_c_array(bufkey, len, false);
-    rock = glulxe_retained_register(cbuf, len, typecode);
-    *arrayref = cbuf;
-  }
-  else {
-    uint *ubuf = grab_temp_i_array(bufkey, len, false);
-    rock = glulxe_retained_register(ubuf, len, typecode);
-    *arrayref = ubuf;
-  }
-  return rock;
+	gidispatch_rock_t rock;
+	int elemsize = 0;
+
+	if (typecode[4] == 'C')
+		elemsize = 1;
+	else if (typecode[4] == 'I')
+		elemsize = 4;
+
+	if (!elemsize) {
+		unsigned char *buf = memmap + bufkey;
+		*arrayref = buf;
+		rock.ptr = nullptr;
+		return rock;
+	}
+
+	if (elemsize == 1) {
+		char *cbuf = grab_temp_c_array(bufkey, len, false);
+		rock = glulxe_retained_register(cbuf, len, typecode);
+		*arrayref = cbuf;
+	} else {
+		uint *ubuf = grab_temp_i_array(bufkey, len, false);
+		rock = glulxe_retained_register(ubuf, len, typecode);
+		*arrayref = ubuf;
+	}
+	return rock;
 }
 
 void Glulxe::set_library_select_hook(void (*func)(uint)) {
-  library_select_hook = func;
+	library_select_hook = func;
 }
 
 char *Glulxe::get_game_id() {
-  /* This buffer gets rewritten on every call, but that's okay -- the caller
-     is supposed to copy out the result. */
-  static char buf[2*64+2];
-  int ix, jx;
-
-  if (!memmap)
-    return nullptr;
-
-  for (ix=0, jx=0; ix<64; ix++) {
-    char ch = memmap[ix];
-    int val = ((ch >> 4) & 0x0F);
-    buf[jx++] = ((val < 10) ? (val + '0') : (val + 'A' - 10));
-    val = (ch & 0x0F);
-    buf[jx++] = ((val < 10) ? (val + '0') : (val + 'A' - 10));
-  }
-  buf[jx++] = '\0';
-
-  return buf;
+	/* This buffer gets rewritten on every call, but that's okay -- the caller
+	   is supposed to copy out the result. */
+	static char buf[2 * 64 + 2];
+	int ix, jx;
+
+	if (!memmap)
+		return nullptr;
+
+	for (ix = 0, jx = 0; ix < 64; ix++) {
+		char ch = memmap[ix];
+		int val = ((ch >> 4) & 0x0F);
+		buf[jx++] = ((val < 10) ? (val + '0') : (val + 'A' - 10));
+		val = (ch & 0x0F);
+		buf[jx++] = ((val < 10) ? (val + '0') : (val + 'A' - 10));
+	}
+	buf[jx++] = '\0';
+
+	return buf;
 }
 
 } // End of namespace Glulxe
diff --git a/engines/glk/glulxe/glulxe.cpp b/engines/glk/glulxe/glulxe.cpp
index 5d4420b..a78e22c 100644
--- a/engines/glk/glulxe/glulxe.cpp
+++ b/engines/glk/glulxe/glulxe.cpp
@@ -30,23 +30,23 @@ namespace Glulxe {
 Glulxe *g_vm;
 
 Glulxe::Glulxe(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc), _random("glulxe"),
-		vm_exited_cleanly(false), gamefile_start(0), gamefile_len(0), memmap(nullptr), stack(nullptr),
-		ramstart(0), endgamefile(0), origendmem(0),  stacksize(0), startfuncaddr(0), checksum(0),
-		stackptr(0), frameptr(0), pc(0), prevpc(0), origstringtable(0), stringtable(0), valstackbase(0),
-		localsbase(0), endmem(0), protectstart(0), protectend(0),
-		stream_char_handler(nullptr), stream_unichar_handler(nullptr),
-		// main
-		library_autorestore_hook(nullptr),
-		// accel
-		classes_table(0), indiv_prop_start(0), class_metaclass(0), object_metaclass(0),
-		routine_metaclass(0), string_metaclass(0), self(0), num_attr_bytes(0), cpv__start(0),
-		accelentries(nullptr),
-		// heap
-		heap_start(0), alloc_count(0), heap_head(nullptr), heap_tail(nullptr),
-		// serial
-		max_undo_level(8), undo_chain_size(0), undo_chain_num(0), undo_chain(nullptr), ramcache(nullptr),
-		// string
-		iosys_mode(0), iosys_rock(0), tablecache_valid(false), glkio_unichar_han_ptr(nullptr) {
+	vm_exited_cleanly(false), gamefile_start(0), gamefile_len(0), memmap(nullptr), stack(nullptr),
+	ramstart(0), endgamefile(0), origendmem(0),  stacksize(0), startfuncaddr(0), checksum(0),
+	stackptr(0), frameptr(0), pc(0), prevpc(0), origstringtable(0), stringtable(0), valstackbase(0),
+	localsbase(0), endmem(0), protectstart(0), protectend(0),
+	stream_char_handler(nullptr), stream_unichar_handler(nullptr),
+	// main
+	library_autorestore_hook(nullptr),
+	// accel
+	classes_table(0), indiv_prop_start(0), class_metaclass(0), object_metaclass(0),
+	routine_metaclass(0), string_metaclass(0), self(0), num_attr_bytes(0), cpv__start(0),
+	accelentries(nullptr),
+	// heap
+	heap_start(0), alloc_count(0), heap_head(nullptr), heap_tail(nullptr),
+	// serial
+	max_undo_level(8), undo_chain_size(0), undo_chain_num(0), undo_chain(nullptr), ramcache(nullptr),
+	// string
+	iosys_mode(0), iosys_rock(0), tablecache_valid(false), glkio_unichar_han_ptr(nullptr) {
 	g_vm = this;
 }
 
diff --git a/engines/glk/glulxe/glulxe.h b/engines/glk/glulxe/glulxe.h
index d9003c8..b985beb 100644
--- a/engines/glk/glulxe/glulxe.h
+++ b/engines/glk/glulxe/glulxe.h
@@ -78,10 +78,10 @@ private:
 	 * @{
 	 */
 
-	 /**
-	  * The library_autorestore_hook is called right after the VM's initial setup. This is an appropriate time
-	  * to autorestore an initial game state, if the library has that capability. (Currently, only iosglk does.)
-	  */
+	/**
+	 * The library_autorestore_hook is called right after the VM's initial setup. This is an appropriate time
+	 * to autorestore an initial game state, if the library has that capability. (Currently, only iosglk does.)
+	 */
 	void(*library_autorestore_hook)(void);
 	Common::RandomSource _random;
 
@@ -110,7 +110,7 @@ private:
 	 * @{
 	 */
 
-	uint heap_start;	///< zero for inactive heap
+	uint heap_start;    ///< zero for inactive heap
 	int alloc_count;
 
 	/* The heap_head/heap_tail is a doubly-linked list of blocks, both
@@ -151,9 +151,9 @@ private:
 	 * @{
 	 */
 
-	 /**
-	  * This can be adjusted before startup by platform-specific startup code -- that is, preference code.
-	  */
+	/**
+	 * This can be adjusted before startup by platform-specific startup code -- that is, preference code.
+	 */
 	int max_undo_level;
 
 	int undo_chain_size;
@@ -182,7 +182,7 @@ private:
 	cacheblock_t tablecache;
 
 	/* This misbehaves if a Glk function has more than one S argument. */
-	#define STATIC_TEMP_BUFSIZE (127)
+#define STATIC_TEMP_BUFSIZE (127)
 	char temp_buf[STATIC_TEMP_BUFSIZE + 1];
 
 	/**@}*/
@@ -253,7 +253,7 @@ protected:
 	 */
 	uint get_prop_new(uint obj, uint id);
 
-	 /**@}*/
+	/**@}*/
 
 	/**
 	 * \defgroup glkop support methods
@@ -302,12 +302,12 @@ protected:
 	 * to deal with structures.
 	 */
 	void parse_glk_args(dispatch_splot_t *splot, const char **proto, int depth, int *argnumptr, uint subaddress, int subpassin);
-		
+
 	/**
 	 * This is about the reverse of parse_glk_args().
 	 */
 	void unparse_glk_args(dispatch_splot_t *splot, const char **proto, int depth,
-		int *argnumptr, uint subaddress, int subpassout);
+	                      int *argnumptr, uint subaddress, int subpassout);
 
 	/**
 	 * Create a string identifying this game. We use the first 64 bytes of the memory map, encoded as hex,
@@ -389,7 +389,9 @@ public:
 	/**
 	 * Returns the running interpreter type
 	 */
-	virtual InterpreterType getInterpreterType() const override { return INTERPRETER_GLULXE; }
+	virtual InterpreterType getInterpreterType() const override {
+		return INTERPRETER_GLULXE;
+	}
 
 	/**
 	 * Load a savegame from the passed stream
@@ -427,9 +429,9 @@ public:
 	 * @{
 	 */
 
-	 /**
-	  * Validates the game file, and if it's invalid, displays an error dialog
-	  */
+	/**
+	 * Validates the game file, and if it's invalid, displays an error dialog
+	 */
 	bool is_gamefile_valid();
 
 	/**@}*/
@@ -439,16 +441,16 @@ public:
 	 * @{
 	 */
 
-	 /**
-	  * Read in the game file and build the machine, allocating all the memory necessary.
-	 */
+	/**
+	 * Read in the game file and build the machine, allocating all the memory necessary.
+	*/
 	void setup_vm();
 
 	/**
 	 * Deallocate all the memory and shut down the machine.
 	 */
 	void finalize_vm();
-	
+
 	/**
 	 * Put the VM into a state where it's ready to begin executing the game. This is called
 	 * both at startup time, and when the machine performs a "restart" opcode.
@@ -461,14 +463,14 @@ public:
 	 * when the heap-allocation system is calling. Returns 0 for success; otherwise, the operation failed.
 	 */
 	uint change_memsize(uint newlen, bool internal);
-	
+
 	/**
 	 * If addr is 0, pop N arguments off the stack, and put them in an array. If non-0, take N arguments
 	 * from that main memory address instead. This has to dynamically allocate if there are more than
 	 * 32 arguments, but that shouldn't be a problem.
 	 */
 	uint *pop_arguments(uint count, uint addr);
-	
+
 	/**
 	 * Make sure that count bytes beginning with addr all fall within the current memory map.
 	 * This is called at every memory (read) access if VERIFY_MEMORY_ACCESS is defined in the header file.
@@ -495,9 +497,9 @@ public:
 	 * @{
 	 */
 
-	 /**
-	  * The main interpreter loop. This repeats until the program is done
-	  */
+	/**
+	 * The main interpreter loop. This repeats until the program is done
+	 */
 	void execute_loop();
 
 	/**@}*/
@@ -507,9 +509,9 @@ public:
 	 * @{
 	 */
 
-	 /**
-	  * Set up the fast-lookup array of operandlists. This is called just once, when the terp starts up.
-	  */
+	/**
+	 * Set up the fast-lookup array of operandlists. This is called just once, when the terp starts up.
+	 */
 	void init_operands();
 
 	/**
@@ -549,7 +551,7 @@ public:
 	 * Note that if argc is zero, argv may be nullptr.
 	 */
 	void enter_function(uint addr, uint argc, uint *argv);
-	
+
 	/**
 	 * Pop the current call frame off the stack. This is very simple.
 	*/
@@ -610,11 +612,11 @@ public:
 	/**
 	 * Create an array of words, in the VM serialization format:
 	 *
-	 *	 heap_start
-	 *	 alloc_count
-	 *	 addr of first block
-	 *	 len of first block
-	 *	 ...
+	 *   heap_start
+	 *   alloc_count
+	 *   addr of first block
+	 *   len of first block
+	 *   ...
 	 *
 	 * (Note that these are uint values -- native byte ordering. Also, the blocks will be in address order,
 	 * which is a stricter guarantee than the VM specifies; that'll help in heap_apply_summary().)
@@ -649,19 +651,19 @@ public:
 	 */
 
 
-	 /**
-	  * An array of data structures is stored in memory, beginning at start, each structure being structsize bytes.
-	  * Within each struct, there is a key value keysize bytes long, starting at position keyoffset (from
-	  * the start of the structure.) Search through these in order. If one is found whose key matches, return it.
-	  * If numstructs are searched with no result, return nullptr.
-	  *
-	  * numstructs may be -1 (0xFFFFFFFF) to indicate no upper limit to the number of structures to search.
-	  * The search will continue until a match is found, or (if ZeroKeyTerminates is set) a zero key.
-	  *
-	  * The KeyIndirect, ZeroKeyTerminates, and ReturnIndex options may be used.
-	  */
+	/**
+	 * An array of data structures is stored in memory, beginning at start, each structure being structsize bytes.
+	 * Within each struct, there is a key value keysize bytes long, starting at position keyoffset (from
+	 * the start of the structure.) Search through these in order. If one is found whose key matches, return it.
+	 * If numstructs are searched with no result, return nullptr.
+	 *
+	 * numstructs may be -1 (0xFFFFFFFF) to indicate no upper limit to the number of structures to search.
+	 * The search will continue until a match is found, or (if ZeroKeyTerminates is set) a zero key.
+	 *
+	 * The KeyIndirect, ZeroKeyTerminates, and ReturnIndex options may be used.
+	 */
 	uint linear_search(uint key, uint keysize, uint start, uint structsize, uint numstructs,
-		uint keyoffset, uint options);
+	                   uint keyoffset, uint options);
 
 	/**
 	 * An array of data structures is in memory, as above. However, the structs must be stored in forward
@@ -671,7 +673,7 @@ public:
 	 * The KeyIndirect and ReturnIndex options may be used.
 	 */
 	uint binary_search(uint key, uint keysize, uint start, uint structsize, uint numstructs,
-		uint keyoffset, uint options);
+	                   uint keyoffset, uint options);
 
 	/**
 	 * The structures may be anywhere in memory, in any order. They are linked by a four-byte address field,
@@ -723,9 +725,9 @@ public:
 	 * @{
 	 */
 
-	 /**
-	  * glkop section initialization
-	  */
+	/**
+	 * glkop section initialization
+	 */
 	void glkopInit();
 
 	void set_library_select_hook(void(*func)(uint));
@@ -754,7 +756,7 @@ public:
 	 * Read the prefixes of an argument string -- the "<>&+:#!" chars.
 	 */
 	const char *read_prefix(const char *cx, int *isref, int *isarray, int *passin, int *passout,
-		int *nullok, int *isretained, int *isreturn);
+	                        int *nullok, int *isretained, int *isreturn);
 
 	/**
 	 * This is used by some interpreter code which has to, well, find a Glk stream given its ID.
@@ -868,9 +870,11 @@ public:
 	   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) */
+	/* #define FLOAT_NOT_NATIVE (1) */
 
-	int init_float() { return true; }
+	int init_float() {
+		return true;
+	}
 
 	/**
 	 * Encode floats by a lot of annoying bit manipulation.
@@ -887,7 +891,7 @@ public:
 	/* 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) */
+	/* #define FLOAT_COMPILE_SAFER_POWF (1) */
 
 	inline gfloat32 glulx_powf(gfloat32 val1, gfloat32 val2) const {
 		return powf(val1, val2);
@@ -901,9 +905,9 @@ public:
 	 * @{
 	 */
 
-	 /**
-	  * Set up the undo chain and anything else that needs to be set up.
-	  */
+	/**
+	 * Set up the undo chain and anything else that needs to be set up.
+	 */
 	bool init_serial();
 
 	/**
@@ -946,9 +950,9 @@ public:
 	 * @{
 	 */
 
-	 /**
-	  * Write a signed integer to the current output stream.
-	  */
+	/**
+	 * Write a signed integer to the current output stream.
+	 */
 	void stream_num(int val, int inmiddle, int charnum);
 
 	/**
diff --git a/engines/glk/glulxe/glulxe_types.h b/engines/glk/glulxe/glulxe_types.h
index 9200ec2..d57600e 100644
--- a/engines/glk/glulxe/glulxe_types.h
+++ b/engines/glk/glulxe/glulxe_types.h
@@ -103,18 +103,18 @@ class Glulxe;
  * degradation or even crashes, depending on the machine CPU.
  */
 #define Stk1(adr)   \
-  (*((unsigned char *)(stack+(adr))))
+	(*((unsigned char *)(stack+(adr))))
 #define Stk2(adr)   \
-  (*((uint16 *)(stack+(adr))))
+	(*((uint16 *)(stack+(adr))))
 #define Stk4(adr)   \
-  (*((uint32 *)(stack+(adr))))
+	(*((uint32 *)(stack+(adr))))
 
 #define StkW1(adr, vl)   \
-  (*((byte *)(stack+(adr))) = (byte)(vl))
+	(*((byte *)(stack+(adr))) = (byte)(vl))
 #define StkW2(adr, vl)   \
-  (*((uint16 *)(stack+(adr))) = (uint16)(vl))
+	(*((uint16 *)(stack+(adr))) = (uint16)(vl))
 #define StkW4(adr, vl)   \
-  (*((uint32 *)(stack+(adr))) = (uint32)(vl))
+	(*((uint32 *)(stack+(adr))) = (uint32)(vl))
 
 enum Opcode {
 	op_nop          = 0x00,
@@ -319,9 +319,9 @@ typedef classtable_struct classtable_t;
  * Represents the operand structure of an opcode.
  */
 struct operandlist_struct {
-	int num_ops;			///< Number of operands for this opcode
-	int arg_size;			///< Usually 4, but can be 1 or 2
-	const int *formlist;	///< Array of values, either modeform_Load or modeform_Store
+	int num_ops;            ///< Number of operands for this opcode
+	int arg_size;           ///< Usually 4, but can be 1 or 2
+	const int *formlist;    ///< Array of values, either modeform_Load or modeform_Store
 };
 typedef operandlist_struct operandlist_t;
 
@@ -389,7 +389,7 @@ enum iosys {
 };
 
 #define CACHEBITS (4)
-#define CACHESIZE (1 << CACHEBITS) 
+#define CACHESIZE (1 << CACHEBITS)
 #define CACHEMASK (15)
 
 struct cacheblock_struct {
diff --git a/engines/glk/glulxe/heap.cpp b/engines/glk/glulxe/heap.cpp
index 00b8dcb..86cd443 100644
--- a/engines/glk/glulxe/heap.cpp
+++ b/engines/glk/glulxe/heap.cpp
@@ -26,295 +26,290 @@ namespace Glk {
 namespace Glulxe {
 
 void Glulxe::heap_clear() {
-  while (heap_head) {
-    heapblock_t *blo = heap_head;
-    heap_head = blo->next;
-    blo->next = nullptr;
-    blo->prev = nullptr;
-    glulx_free(blo);
-  }
-  heap_tail = nullptr;
-
-  if (heap_start) {
-    uint res = change_memsize(heap_start, true);
-    if (res)
-      fatal_error_i("Unable to revert memory size when deactivating heap.",
-        heap_start);
-  }
-
-  heap_start = 0;
-  alloc_count = 0;
-  /* heap_sanity_check(); */
+	while (heap_head) {
+		heapblock_t *blo = heap_head;
+		heap_head = blo->next;
+		blo->next = nullptr;
+		blo->prev = nullptr;
+		glulx_free(blo);
+	}
+	heap_tail = nullptr;
+
+	if (heap_start) {
+		uint res = change_memsize(heap_start, true);
+		if (res)
+			fatal_error_i("Unable to revert memory size when deactivating heap.",
+			              heap_start);
+	}
+
+	heap_start = 0;
+	alloc_count = 0;
+	/* heap_sanity_check(); */
 }
 
 int Glulxe::heap_is_active() const {
-  return (heap_start != 0);
+	return (heap_start != 0);
 }
 
 uint Glulxe::heap_get_start() const {
-  return heap_start;
+	return heap_start;
 }
 
 uint Glulxe::heap_alloc(uint len) {
-  heapblock_t *blo, *newblo;
+	heapblock_t *blo, *newblo;
 
 #ifdef FIXED_MEMSIZE
-  return 0;
+	return 0;
 #else /* FIXED_MEMSIZE */
 
-  if (len <= 0)
-    fatal_error("Heap allocation length must be positive.");
-
-  blo = heap_head;
-  while (blo) {
-    if (blo->isfree && blo->len >= len)
-      break;
-
-    if (!blo->isfree) {
-      blo = blo->next;
-      continue;
-    }
-
-    if (!blo->next || !blo->next->isfree) {
-      blo = blo->next;
-      continue;
-    }
-
-    /* This is a free block, but the next block in the list is also
-       free, so we "advance" by merging rather than by going to
-       blo->next. */
-    newblo = blo->next;
-    blo->len += newblo->len;
-    if (newblo->next) {
-      blo->next = newblo->next;
-      newblo->next->prev = blo;
-    }
-    else {
-      blo->next = nullptr;
-      heap_tail = blo;
-    }
-    newblo->next = nullptr;
-    newblo->prev = nullptr;
-    glulx_free(newblo);
-    newblo = nullptr;
-    continue;
-  }
-
-  if (!blo) {
-    /* No free area is visible on the list. Try extending memory. How
-       much? Double the heap size, or by 256 bytes, or by the memory
-       length requested -- whichever is greatest. */
-    uint res;
-    uint extension;
-    uint oldendmem = endmem;
-
-    extension = 0;
-    if (heap_start)
-      extension = endmem - heap_start;
-    if (extension < len)
-      extension = len;
-    if (extension < 256)
-      extension = 256;
-    /* And it must be rounded up to a multiple of 256. */
-    extension = (extension + 0xFF) & (~(uint)0xFF);
-
-    res = change_memsize(endmem+extension, true);
-    if (res)
-      return 0;
-
-    /* If we just started the heap, note that. */
-    if (heap_start == 0)
-      heap_start = oldendmem;
-
-    if (heap_tail && heap_tail->isfree) {
-      /* Append the new space to the last block. */
-      blo = heap_tail;
-      blo->len += extension;
-    }
-    else {
-      /* Append the new space to the block list, as a new block. */
-      newblo = (heapblock_t *)glulx_malloc(sizeof(heapblock_t));
-      if (!newblo)
-        fatal_error("Unable to allocate record for heap block.");
-      newblo->addr = oldendmem;
-      newblo->len = extension;
-      newblo->isfree = true;
-      newblo->next = nullptr;
-      newblo->prev = nullptr;
-
-      if (!heap_tail) {
-        heap_head = newblo;
-        heap_tail = newblo;
-      }
-      else {
-        blo = heap_tail;
-        heap_tail = newblo;
-        blo->next = newblo;
-        newblo->prev = blo;
-      }
-
-      blo = newblo;
-      newblo = nullptr;
-    }
-
-    /* and continue forwards, using this new block (blo). */
-  }
-
-  /* Something strange happened. */
-  if (!blo || !blo->isfree || blo->len < len)
-    return 0;
-
-  /* We now have a free block of size len or longer. */
-
-  if (blo->len == len) {
-    blo->isfree = false;
-  }
-  else {
-    newblo = (heapblock_t *)glulx_malloc(sizeof(heapblock_t));
-    if (!newblo)
-      fatal_error("Unable to allocate record for heap block.");
-    newblo->isfree = true;
-    newblo->addr = blo->addr + len;
-    newblo->len = blo->len - len;
-    blo->len = len;
-    blo->isfree = false;
-    newblo->next = blo->next;
-    if (newblo->next)
-      newblo->next->prev = newblo;
-    newblo->prev = blo;
-    blo->next = newblo;
-    if (heap_tail == blo)
-      heap_tail = newblo;
-  }
-
-  alloc_count++;
-  /* heap_sanity_check(); */
-  return blo->addr;
+	if (len <= 0)
+		fatal_error("Heap allocation length must be positive.");
+
+	blo = heap_head;
+	while (blo) {
+		if (blo->isfree && blo->len >= len)
+			break;
+
+		if (!blo->isfree) {
+			blo = blo->next;
+			continue;
+		}
+
+		if (!blo->next || !blo->next->isfree) {
+			blo = blo->next;
+			continue;
+		}
+
+		/* This is a free block, but the next block in the list is also
+		   free, so we "advance" by merging rather than by going to
+		   blo->next. */
+		newblo = blo->next;
+		blo->len += newblo->len;
+		if (newblo->next) {
+			blo->next = newblo->next;
+			newblo->next->prev = blo;
+		} else {
+			blo->next = nullptr;
+			heap_tail = blo;
+		}
+		newblo->next = nullptr;
+		newblo->prev = nullptr;
+		glulx_free(newblo);
+		newblo = nullptr;
+		continue;
+	}
+
+	if (!blo) {
+		/* No free area is visible on the list. Try extending memory. How
+		   much? Double the heap size, or by 256 bytes, or by the memory
+		   length requested -- whichever is greatest. */
+		uint res;
+		uint extension;
+		uint oldendmem = endmem;
+
+		extension = 0;
+		if (heap_start)
+			extension = endmem - heap_start;
+		if (extension < len)
+			extension = len;
+		if (extension < 256)
+			extension = 256;
+		/* And it must be rounded up to a multiple of 256. */
+		extension = (extension + 0xFF) & (~(uint)0xFF);
+
+		res = change_memsize(endmem + extension, true);
+		if (res)
+			return 0;
+
+		/* If we just started the heap, note that. */
+		if (heap_start == 0)
+			heap_start = oldendmem;
+
+		if (heap_tail && heap_tail->isfree) {
+			/* Append the new space to the last block. */
+			blo = heap_tail;
+			blo->len += extension;
+		} else {
+			/* Append the new space to the block list, as a new block. */
+			newblo = (heapblock_t *)glulx_malloc(sizeof(heapblock_t));
+			if (!newblo)
+				fatal_error("Unable to allocate record for heap block.");
+			newblo->addr = oldendmem;
+			newblo->len = extension;
+			newblo->isfree = true;
+			newblo->next = nullptr;
+			newblo->prev = nullptr;
+
+			if (!heap_tail) {
+				heap_head = newblo;
+				heap_tail = newblo;
+			} else {
+				blo = heap_tail;
+				heap_tail = newblo;
+				blo->next = newblo;
+				newblo->prev = blo;
+			}
+
+			blo = newblo;
+			newblo = nullptr;
+		}
+
+		/* and continue forwards, using this new block (blo). */
+	}
+
+	/* Something strange happened. */
+	if (!blo || !blo->isfree || blo->len < len)
+		return 0;
+
+	/* We now have a free block of size len or longer. */
+
+	if (blo->len == len) {
+		blo->isfree = false;
+	} else {
+		newblo = (heapblock_t *)glulx_malloc(sizeof(heapblock_t));
+		if (!newblo)
+			fatal_error("Unable to allocate record for heap block.");
+		newblo->isfree = true;
+		newblo->addr = blo->addr + len;
+		newblo->len = blo->len - len;
+		blo->len = len;
+		blo->isfree = false;
+		newblo->next = blo->next;
+		if (newblo->next)
+			newblo->next->prev = newblo;
+		newblo->prev = blo;
+		blo->next = newblo;
+		if (heap_tail == blo)
+			heap_tail = newblo;
+	}
+
+	alloc_count++;
+	/* heap_sanity_check(); */
+	return blo->addr;
 
 #endif /* FIXED_MEMSIZE */
 }
 
 void Glulxe::heap_free(uint addr) {
-  heapblock_t *blo;
-
-  for (blo = heap_head; blo; blo = blo->next) { 
-    if (blo->addr == addr)
-      break;
-  };
-  if (!blo || blo->isfree)
-    fatal_error_i("Attempt to free unallocated address from heap.", addr);
-
-  blo->isfree = true;
-  alloc_count--;
-  if (alloc_count <= 0) {
-    heap_clear();
-  }
-
-  /* heap_sanity_check(); */
+	heapblock_t *blo;
+
+	for (blo = heap_head; blo; blo = blo->next) {
+		if (blo->addr == addr)
+			break;
+	};
+	if (!blo || blo->isfree)
+		fatal_error_i("Attempt to free unallocated address from heap.", addr);
+
+	blo->isfree = true;
+	alloc_count--;
+	if (alloc_count <= 0) {
+		heap_clear();
+	}
+
+	/* heap_sanity_check(); */
 }
 
 int Glulxe::heap_get_summary(uint *valcount, uint **summary) {
-  uint *arr, len, pos;
-  heapblock_t *blo;
+	uint *arr, len, pos;
+	heapblock_t *blo;
 
-  *valcount = 0;
-  *summary = nullptr;
+	*valcount = 0;
+	*summary = nullptr;
 
-  if (heap_start == 0)
-    return 0;
+	if (heap_start == 0)
+		return 0;
 
-  len = 2 + 2*alloc_count;
-  arr = (uint *)glulx_malloc(len * sizeof(uint));
-  if (!arr)
-    return 1;
+	len = 2 + 2 * alloc_count;
+	arr = (uint *)glulx_malloc(len * sizeof(uint));
+	if (!arr)
+		return 1;
 
-  pos = 0;
-  arr[pos++] = heap_start;
-  arr[pos++] = alloc_count;
+	pos = 0;
+	arr[pos++] = heap_start;
+	arr[pos++] = alloc_count;
 
-  for (blo = heap_head; blo; blo = blo->next) {
-    if (blo->isfree)
-      continue;
-    arr[pos++] = blo->addr;
-    arr[pos++] = blo->len;
-  }
+	for (blo = heap_head; blo; blo = blo->next) {
+		if (blo->isfree)
+			continue;
+		arr[pos++] = blo->addr;
+		arr[pos++] = blo->len;
+	}
 
-  if (pos != len)
-    fatal_error("Wrong number of active blocks in heap");
+	if (pos != len)
+		fatal_error("Wrong number of active blocks in heap");
 
-  *valcount = len;
-  *summary = arr;
-  return 0;
+	*valcount = len;
+	*summary = arr;
+	return 0;
 }
 
 int Glulxe::heap_apply_summary(uint valcount, uint *summary) {
-  uint lx, jx, lastend;
+	uint lx, jx, lastend;
 
-  if (heap_start)
-    fatal_error("Heap active when heap_apply_summary called");
+	if (heap_start)
+		fatal_error("Heap active when heap_apply_summary called");
 
-  if (valcount == 0 || summary == nullptr)
-    return 0;
-  if (valcount == 2 && summary[0] == 0 && summary[1] == 0)
-    return 0;
+	if (valcount == 0 || summary == nullptr)
+		return 0;
+	if (valcount == 2 && summary[0] == 0 && summary[1] == 0)
+		return 0;
 
 #ifdef FIXED_MEMSIZE
-  return 1;
+	return 1;
 #else /* FIXED_MEMSIZE */
 
-  lx = 0;
-  heap_start = summary[lx++];
-  alloc_count = summary[lx++];
-
-  for (jx=lx; jx+2<valcount; jx+=2) {
-    if (summary[jx] >= summary[jx+2])
-      fatal_error("Heap block summary is out of order.");
-  }
-
-  lastend = heap_start;
-
-  while (lx < valcount || lastend < endmem) {
-    heapblock_t *blo;
-
-    blo = (heapblock_t *)glulx_malloc(sizeof(heapblock_t));
-    if (!blo)
-      fatal_error("Unable to allocate record for heap block.");
-
-    if (lx >= valcount) {
-      blo->addr = lastend;
-      blo->len = endmem - lastend;
-      blo->isfree = true;
-    } else {
-      if (lastend < summary[lx]) {
-        blo->addr = lastend;
-        blo->len = summary[lx] - lastend;
-        blo->isfree = true;
-      } else {
-        blo->addr = summary[lx++];
-        blo->len = summary[lx++];
-        blo->isfree = false;
-      }
-    }
-
-    blo->prev = nullptr;
-    blo->next = nullptr;
-
-    if (!heap_head) {
-      heap_head = blo;
-      heap_tail = blo;
-    }
-    else {
-      heap_tail->next = blo;
-      blo->prev = heap_tail;
-      heap_tail = blo;
-    }
-
-    lastend = blo->addr + blo->len;
-  }
-
-  /* heap_sanity_check(); */
-
-  return 0;
+	lx = 0;
+	heap_start = summary[lx++];
+	alloc_count = summary[lx++];
+
+	for (jx = lx; jx + 2 < valcount; jx += 2) {
+		if (summary[jx] >= summary[jx + 2])
+			fatal_error("Heap block summary is out of order.");
+	}
+
+	lastend = heap_start;
+
+	while (lx < valcount || lastend < endmem) {
+		heapblock_t *blo;
+
+		blo = (heapblock_t *)glulx_malloc(sizeof(heapblock_t));
+		if (!blo)
+			fatal_error("Unable to allocate record for heap block.");
+
+		if (lx >= valcount) {
+			blo->addr = lastend;
+			blo->len = endmem - lastend;
+			blo->isfree = true;
+		} else {
+			if (lastend < summary[lx]) {
+				blo->addr = lastend;
+				blo->len = summary[lx] - lastend;
+				blo->isfree = true;
+			} else {
+				blo->addr = summary[lx++];
+				blo->len = summary[lx++];
+				blo->isfree = false;
+			}
+		}
+
+		blo->prev = nullptr;
+		blo->next = nullptr;
+
+		if (!heap_head) {
+			heap_head = blo;
+			heap_tail = blo;
+		} else {
+			heap_tail->next = blo;
+			blo->prev = heap_tail;
+			heap_tail = blo;
+		}
+
+		lastend = blo->addr + blo->len;
+	}
+
+	/* heap_sanity_check(); */
+
+	return 0;
 #endif /* FIXED_MEMSIZE */
 }
 
diff --git a/engines/glk/glulxe/operand.cpp b/engines/glk/glulxe/operand.cpp
index 23018d4..d41b2b0 100644
--- a/engines/glk/glulxe/operand.cpp
+++ b/engines/glk/glulxe/operand.cpp
@@ -65,552 +65,546 @@ static const int array_LLSS[4] = { modeform_Load, modeform_Load, modeform_Store,
 static const operandlist_t list_LLSS = { 4, 4, &array_LLSS[0] };
 
 void Glulxe::init_operands() {
-  for (int ix=0; ix<0x80; ix++)
-    fast_operandlist[ix] = lookup_operandlist(ix);
+	for (int ix = 0; ix < 0x80; ix++)
+		fast_operandlist[ix] = lookup_operandlist(ix);
 }
 
 const operandlist_t *Glulxe::lookup_operandlist(uint opcode) {
-  switch (opcode) {
-  case op_nop: 
-    return &list_none;
-
-  case op_add:
-  case op_sub:
-  case op_mul:
-  case op_div:
-  case op_mod:
-  case op_bitand:
-  case op_bitor:
-  case op_bitxor:
-  case op_shiftl:
-  case op_sshiftr:
-  case op_ushiftr:
-    return &list_LLS;
-
-  case op_neg:
-  case op_bitnot:
-    return &list_LS;
-
-  case op_jump:
-  case op_jumpabs:
-    return &list_L;
-  case op_jz:
-  case op_jnz:
-    return &list_LL;
-  case op_jeq:
-  case op_jne:
-  case op_jlt:
-  case op_jge:
-  case op_jgt:
-  case op_jle:
-  case op_jltu:
-  case op_jgeu:
-  case op_jgtu:
-  case op_jleu:
-    return &list_LLL;
-
-  case op_call:
-    return &list_LLS;
-  case op_return:
-    return &list_L;
-  case op_catch:
-    return &list_SL;
-  case op_throw:
-    return &list_LL;
-  case op_tailcall:
-    return &list_LL;
-
-  case op_sexb:
-  case op_sexs:
-    return &list_LS;
-
-  case op_copy:
-    return &list_LS;
-  case op_copys:
-    return &list_2LS;
-  case op_copyb:
-    return &list_1LS;
-  case op_aload:
-  case op_aloads:
-  case op_aloadb:
-  case op_aloadbit:
-    return &list_LLS;
-  case op_astore:
-  case op_astores:
-  case op_astoreb:
-  case op_astorebit:
-    return &list_LLL;
-
-  case op_stkcount:
-    return &list_S;
-  case op_stkpeek:
-    return &list_LS;
-  case op_stkswap: 
-    return &list_none;
-  case op_stkroll:
-    return &list_LL;
-  case op_stkcopy:
-    return &list_L;
-
-  case op_streamchar:
-  case op_streamunichar:
-  case op_streamnum:
-  case op_streamstr:
-    return &list_L;
-  case op_getstringtbl:
-    return &list_S;
-  case op_setstringtbl:
-    return &list_L;
-  case op_getiosys:
-    return &list_SS;
-  case op_setiosys:
-    return &list_LL;
-
-  case op_random:
-    return &list_LS;
-  case op_setrandom:
-    return &list_L;
-
-  case op_verify:
-    return &list_S;
-  case op_restart:
-    return &list_none;
-  case op_save:
-  case op_restore:
-    return &list_LS;
-  case op_saveundo:
-  case op_restoreundo:
-    return &list_S;
-  case op_protect:
-    return &list_LL;
-
-  case op_quit:
-    return &list_none;
-
-  case op_gestalt:
-    return &list_LLS;
-
-  case op_debugtrap: 
-    return &list_L;
-
-  case op_getmemsize:
-    return &list_S;
-  case op_setmemsize:
-    return &list_LS;
-
-  case op_linearsearch:
-    return &list_LLLLLLLS;
-  case op_binarysearch:
-    return &list_LLLLLLLS;
-  case op_linkedsearch:
-    return &list_LLLLLLS;
-
-  case op_glk:
-    return &list_LLS;
-
-  case op_callf:
-    return &list_LS;
-  case op_callfi:
-    return &list_LLS;
-  case op_callfii:
-    return &list_LLLS;
-  case op_callfiii:
-    return &list_LLLLS;
-
-  case op_mzero:
-    return &list_LL;
-  case op_mcopy:
-    return &list_LLL;
-  case op_malloc:
-    return &list_LS;
-  case op_mfree:
-    return &list_L;
-
-  case op_accelfunc:
-  case op_accelparam:
-    return &list_LL;
+	switch (opcode) {
+	case op_nop:
+		return &list_none;
+
+	case op_add:
+	case op_sub:
+	case op_mul:
+	case op_div:
+	case op_mod:
+	case op_bitand:
+	case op_bitor:
+	case op_bitxor:
+	case op_shiftl:
+	case op_sshiftr:
+	case op_ushiftr:
+		return &list_LLS;
+
+	case op_neg:
+	case op_bitnot:
+		return &list_LS;
+
+	case op_jump:
+	case op_jumpabs:
+		return &list_L;
+	case op_jz:
+	case op_jnz:
+		return &list_LL;
+	case op_jeq:
+	case op_jne:
+	case op_jlt:
+	case op_jge:
+	case op_jgt:
+	case op_jle:
+	case op_jltu:
+	case op_jgeu:
+	case op_jgtu:
+	case op_jleu:
+		return &list_LLL;
+
+	case op_call:
+		return &list_LLS;
+	case op_return:
+		return &list_L;
+	case op_catch:
+		return &list_SL;
+	case op_throw:
+		return &list_LL;
+	case op_tailcall:
+		return &list_LL;
+
+	case op_sexb:
+	case op_sexs:
+		return &list_LS;
+
+	case op_copy:
+		return &list_LS;
+	case op_copys:
+		return &list_2LS;
+	case op_copyb:
+		return &list_1LS;
+	case op_aload:
+	case op_aloads:
+	case op_aloadb:
+	case op_aloadbit:
+		return &list_LLS;
+	case op_astore:
+	case op_astores:
+	case op_astoreb:
+	case op_astorebit:
+		return &list_LLL;
+
+	case op_stkcount:
+		return &list_S;
+	case op_stkpeek:
+		return &list_LS;
+	case op_stkswap:
+		return &list_none;
+	case op_stkroll:
+		return &list_LL;
+	case op_stkcopy:
+		return &list_L;
+
+	case op_streamchar:
+	case op_streamunichar:
+	case op_streamnum:
+	case op_streamstr:
+		return &list_L;
+	case op_getstringtbl:
+		return &list_S;
+	case op_setstringtbl:
+		return &list_L;
+	case op_getiosys:
+		return &list_SS;
+	case op_setiosys:
+		return &list_LL;
+
+	case op_random:
+		return &list_LS;
+	case op_setrandom:
+		return &list_L;
+
+	case op_verify:
+		return &list_S;
+	case op_restart:
+		return &list_none;
+	case op_save:
+	case op_restore:
+		return &list_LS;
+	case op_saveundo:
+	case op_restoreundo:
+		return &list_S;
+	case op_protect:
+		return &list_LL;
+
+	case op_quit:
+		return &list_none;
+
+	case op_gestalt:
+		return &list_LLS;
+
+	case op_debugtrap:
+		return &list_L;
+
+	case op_getmemsize:
+		return &list_S;
+	case op_setmemsize:
+		return &list_LS;
+
+	case op_linearsearch:
+		return &list_LLLLLLLS;
+	case op_binarysearch:
+		return &list_LLLLLLLS;
+	case op_linkedsearch:
+		return &list_LLLLLLS;
+
+	case op_glk:
+		return &list_LLS;
+
+	case op_callf:
+		return &list_LS;
+	case op_callfi:
+		return &list_LLS;
+	case op_callfii:
+		return &list_LLLS;
+	case op_callfiii:
+		return &list_LLLLS;
+
+	case op_mzero:
+		return &list_LL;
+	case op_mcopy:
+		return &list_LLL;
+	case op_malloc:
+		return &list_LS;
+	case op_mfree:
+		return &list_L;
+
+	case op_accelfunc:
+	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;
+	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 */
 
 #ifdef GLULX_EXTEND_OPERANDS
-  GLULX_EXTEND_OPERANDS
+		GLULX_EXTEND_OPERANDS
 #endif /* GLULX_EXTEND_OPERANDS */
 
-  default: 
-    return nullptr;
-  }
+	default:
+		return nullptr;
+	}
 }
 
 void Glulxe::parse_operands(oparg_t *args, const operandlist_t *oplist) {
-  int ix;
-  oparg_t *curarg;
-  int numops = oplist->num_ops;
-  int argsize = oplist->arg_size;
-  uint modeaddr = pc;
-  int modeval = 0;
-
-  pc += (numops+1) / 2;
-
-  for (ix=0, curarg=args; ix<numops; ix++, curarg++) {
-    int mode;
-    uint value;
-    uint addr;
-
-    curarg->desttype = 0;
-
-    if ((ix & 1) == 0) {
-      modeval = Mem1(modeaddr);
-      mode = (modeval & 0x0F);
-    }
-    else {
-      mode = ((modeval >> 4) & 0x0F);
-      modeaddr++;
-    }
-
-    if (oplist->formlist[ix] == modeform_Load) {
-
-      switch (mode) {
-
-      case 8: /* pop off stack */
-        if (stackptr < valstackbase+4) {
-          fatal_error("Stack underflow in operand.");
-        }
-        stackptr -= 4;
-        value = Stk4(stackptr);
-        break;
-
-      case 0: /* constant zero */
-        value = 0;
-        break;
-
-      case 1: /* one-byte constant */
-        /* Sign-extend from 8 bits to 32 */
-        value = (int)(signed char)(Mem1(pc));
-        pc++;
-        break;
-
-      case 2: /* two-byte constant */
-        /* Sign-extend the first byte from 8 bits to 32; the subsequent
-           byte must not be sign-extended. */
-        value = (int)(signed char)(Mem1(pc));
-        pc++;
-        value = (value << 8) | (uint)(Mem1(pc));
-        pc++;
-        break;
-
-      case 3: /* four-byte constant */
-        /* Bytes must not be sign-extended. */
-        value = Mem4(pc);
-        pc += 4;
-        break;
-
-      case 15: /* main memory RAM, four-byte address */
-        addr = Mem4(pc);
-        addr += ramstart;
-        pc += 4;
-        goto MainMemAddr; 
-
-      case 14: /* main memory RAM, two-byte address */
-        addr = (uint)Mem2(pc);
-        addr += ramstart;
-        pc += 2;
-        goto MainMemAddr; 
-
-      case 13: /* main memory RAM, one-byte address */
-        addr = (uint)(Mem1(pc));
-        addr += ramstart;
-        pc++;
-        goto MainMemAddr; 
-        
-      case 7: /* main memory, four-byte address */
-        addr = Mem4(pc);
-        pc += 4;
-        goto MainMemAddr;
-
-      case 6: /* main memory, two-byte address */
-        addr = (uint)Mem2(pc);
-        pc += 2;
-        goto MainMemAddr;
-
-      case 5: /* main memory, one-byte address */
-        addr = (uint)(Mem1(pc));
-        pc++;
-        /* fall through */
-
-      MainMemAddr:
-        /* cases 5, 6, 7, 13, 14, 15 all wind up here. */
-        if (argsize == 4) {
-          value = Mem4(addr);
-        }
-        else if (argsize == 2) {
-          value = Mem2(addr);
-        }
-        else {
-          value = Mem1(addr);
-        }
-        break;
-
-      case 11: /* locals, four-byte address */
-        addr = Mem4(pc);
-        pc += 4;
-        goto LocalsAddr;
-
-      case 10: /* locals, two-byte address */
-        addr = (uint)Mem2(pc);
-        pc += 2;
-        goto LocalsAddr; 
-
-      case 9: /* locals, one-byte address */
-        addr = (uint)(Mem1(pc));
-        pc++;
-        /* fall through */
-
-      LocalsAddr:
-        /* cases 9, 10, 11 all wind up here. It's illegal for addr to not
-           be four-byte aligned, but we don't check this explicitly. 
-           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. */
-        addr += localsbase;
-        if (argsize == 4) {
-          value = Stk4(addr);
-        }
-        else if (argsize == 2) {
-          value = Stk2(addr);
-        }
-        else {
-          value = Stk1(addr);
-        }
-        break;
-
-      default:
-        value = 0;
-        fatal_error("Unknown addressing mode in load operand.");
-      }
-
-      curarg->value = value;
-
-    }
-    else {  /* modeform_Store */
-      switch (mode) {
-
-      case 0: /* discard value */
-        curarg->desttype = 0;
-        curarg->value = 0;
-        break;
-
-      case 8: /* push on stack */
-        curarg->desttype = 3;
-        curarg->value = 0;
-        break;
-
-      case 15: /* main memory RAM, four-byte address */
-        addr = Mem4(pc);
-        addr += ramstart;
-        pc += 4;
-        goto WrMainMemAddr; 
-
-      case 14: /* main memory RAM, two-byte address */
-        addr = (uint)Mem2(pc);
-        addr += ramstart;
-        pc += 2;
-        goto WrMainMemAddr; 
-
-      case 13: /* main memory RAM, one-byte address */
-        addr = (uint)(Mem1(pc));
-        addr += ramstart;
-        pc++;
-        goto WrMainMemAddr; 
-
-      case 7: /* main memory, four-byte address */
-        addr = Mem4(pc);
-        pc += 4;
-        goto WrMainMemAddr;
-
-      case 6: /* main memory, two-byte address */
-        addr = (uint)Mem2(pc);
-        pc += 2;
-        goto WrMainMemAddr;
-
-      case 5: /* main memory, one-byte address */
-        addr = (uint)(Mem1(pc));
-        pc++;
-        /* fall through */
-
-      WrMainMemAddr:
-        /* cases 5, 6, 7 all wind up here. */
-        curarg->desttype = 1;
-        curarg->value = addr;
-        break;
-
-      case 11: /* locals, four-byte address */
-        addr = Mem4(pc);
-        pc += 4;
-        goto WrLocalsAddr;
-
-      case 10: /* locals, two-byte address */
-        addr = (uint)Mem2(pc);
-        pc += 2;
-        goto WrLocalsAddr; 
-
-      case 9: /* locals, one-byte address */
-        addr = (uint)(Mem1(pc));
-        pc++;
-        /* fall through */
-
-      WrLocalsAddr:
-        /* cases 9, 10, 11 all wind up here. It's illegal for addr to not
-           be four-byte aligned, but we don't check this explicitly. 
-           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. */
-        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. */
-        curarg->value = addr;
-        break;
-
-      case 1:
-      case 2:
-      case 3:
-        fatal_error("Constant addressing mode in store operand.");
-
-      default:
-        fatal_error("Unknown addressing mode in store operand.");
-      }
-    }
-  }
+	int ix;
+	oparg_t *curarg;
+	int numops = oplist->num_ops;
+	int argsize = oplist->arg_size;
+	uint modeaddr = pc;
+	int modeval = 0;
+
+	pc += (numops + 1) / 2;
+
+	for (ix = 0, curarg = args; ix < numops; ix++, curarg++) {
+		int mode;
+		uint value;
+		uint addr;
+
+		curarg->desttype = 0;
+
+		if ((ix & 1) == 0) {
+			modeval = Mem1(modeaddr);
+			mode = (modeval & 0x0F);
+		} else {
+			mode = ((modeval >> 4) & 0x0F);
+			modeaddr++;
+		}
+
+		if (oplist->formlist[ix] == modeform_Load) {
+
+			switch (mode) {
+
+			case 8: /* pop off stack */
+				if (stackptr < valstackbase + 4) {
+					fatal_error("Stack underflow in operand.");
+				}
+				stackptr -= 4;
+				value = Stk4(stackptr);
+				break;
+
+			case 0: /* constant zero */
+				value = 0;
+				break;
+
+			case 1: /* one-byte constant */
+				/* Sign-extend from 8 bits to 32 */
+				value = (int)(signed char)(Mem1(pc));
+				pc++;
+				break;
+
+			case 2: /* two-byte constant */
+				/* Sign-extend the first byte from 8 bits to 32; the subsequent
+				   byte must not be sign-extended. */
+				value = (int)(signed char)(Mem1(pc));
+				pc++;
+				value = (value << 8) | (uint)(Mem1(pc));
+				pc++;
+				break;
+
+			case 3: /* four-byte constant */
+				/* Bytes must not be sign-extended. */
+				value = Mem4(pc);
+				pc += 4;
+				break;
+
+			case 15: /* main memory RAM, four-byte address */
+				addr = Mem4(pc);
+				addr += ramstart;
+				pc += 4;
+				goto MainMemAddr;
+
+			case 14: /* main memory RAM, two-byte address */
+				addr = (uint)Mem2(pc);
+				addr += ramstart;
+				pc += 2;
+				goto MainMemAddr;
+
+			case 13: /* main memory RAM, one-byte address */
+				addr = (uint)(Mem1(pc));
+				addr += ramstart;
+				pc++;
+				goto MainMemAddr;
+
+			case 7: /* main memory, four-byte address */
+				addr = Mem4(pc);
+				pc += 4;
+				goto MainMemAddr;
+
+			case 6: /* main memory, two-byte address */
+				addr = (uint)Mem2(pc);
+				pc += 2;
+				goto MainMemAddr;
+
+			case 5: /* main memory, one-byte address */
+				addr = (uint)(Mem1(pc));
+				pc++;
+				/* fall through */
+
+MainMemAddr:
+				/* cases 5, 6, 7, 13, 14, 15 all wind up here. */
+				if (argsize == 4) {
+					value = Mem4(addr);
+				} else if (argsize == 2) {
+					value = Mem2(addr);
+				} else {
+					value = Mem1(addr);
+				}
+				break;
+
+			case 11: /* locals, four-byte address */
+				addr = Mem4(pc);
+				pc += 4;
+				goto LocalsAddr;
+
+			case 10: /* locals, two-byte address */
+				addr = (uint)Mem2(pc);
+				pc += 2;
+				goto LocalsAddr;
+
+			case 9: /* locals, one-byte address */
+				addr = (uint)(Mem1(pc));
+				pc++;
+				/* fall through */
+
+LocalsAddr:
+				/* cases 9, 10, 11 all wind up here. It's illegal for addr to not
+				   be four-byte aligned, but we don't check this explicitly.
+				   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. */
+				addr += localsbase;
+				if (argsize == 4) {
+					value = Stk4(addr);
+				} else if (argsize == 2) {
+					value = Stk2(addr);
+				} else {
+					value = Stk1(addr);
+				}
+				break;
+
+			default:
+				value = 0;
+				fatal_error("Unknown addressing mode in load operand.");
+			}
+
+			curarg->value = value;
+
+		} else { /* modeform_Store */
+			switch (mode) {
+
+			case 0: /* discard value */
+				curarg->desttype = 0;
+				curarg->value = 0;
+				break;
+
+			case 8: /* push on stack */
+				curarg->desttype = 3;
+				curarg->value = 0;
+				break;
+
+			case 15: /* main memory RAM, four-byte address */
+				addr = Mem4(pc);
+				addr += ramstart;
+				pc += 4;
+				goto WrMainMemAddr;
+
+			case 14: /* main memory RAM, two-byte address */
+				addr = (uint)Mem2(pc);
+				addr += ramstart;
+				pc += 2;
+				goto WrMainMemAddr;
+
+			case 13: /* main memory RAM, one-byte address */
+				addr = (uint)(Mem1(pc));
+				addr += ramstart;
+				pc++;
+				goto WrMainMemAddr;
+
+			case 7: /* main memory, four-byte address */
+				addr = Mem4(pc);
+				pc += 4;
+				goto WrMainMemAddr;
+
+			case 6: /* main memory, two-byte address */
+				addr = (uint)Mem2(pc);
+				pc += 2;
+				goto WrMainMemAddr;
+
+			case 5: /* main memory, one-byte address */
+				addr = (uint)(Mem1(pc));
+				pc++;
+				/* fall through */
+
+WrMainMemAddr:
+				/* cases 5, 6, 7 all wind up here. */
+				curarg->desttype = 1;
+				curarg->value = addr;
+				break;
+
+			case 11: /* locals, four-byte address */
+				addr = Mem4(pc);
+				pc += 4;
+				goto WrLocalsAddr;
+
+			case 10: /* locals, two-byte address */
+				addr = (uint)Mem2(pc);
+				pc += 2;
+				goto WrLocalsAddr;
+
+			case 9: /* locals, one-byte address */
+				addr = (uint)(Mem1(pc));
+				pc++;
+				/* fall through */
+
+WrLocalsAddr:
+				/* cases 9, 10, 11 all wind up here. It's illegal for addr to not
+				   be four-byte aligned, but we don't check this explicitly.
+				   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. */
+				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. */
+				curarg->value = addr;
+				break;
+
+			case 1:
+			case 2:
+			case 3:
+				fatal_error("Constant addressing mode in store operand.");
+
+			default:
+				fatal_error("Unknown addressing mode in store operand.");
+			}
+		}
+	}
 }
 
 void Glulxe::store_operand(uint desttype, uint destaddr, uint storeval) {
-  switch (desttype) {
+	switch (desttype) {
 
-  case 0: /* do nothing; discard the value. */
-    return;
+	case 0: /* do nothing; discard the value. */
+		return;
 
-  case 1: /* main memory. */
-    MemW4(destaddr, storeval);
-    return;
+	case 1: /* main memory. */
+		MemW4(destaddr, storeval);
+		return;
 
-  case 2: /* locals. */
-    destaddr += localsbase;
-    StkW4(destaddr, storeval);
-    return;
+	case 2: /* locals. */
+		destaddr += localsbase;
+		StkW4(destaddr, storeval);
+		return;
 
-  case 3: /* push on stack. */
-    if (stackptr+4 > stacksize) {
-      fatal_error("Stack overflow in store operand.");
-    }
-    StkW4(stackptr, storeval);
-    stackptr += 4;
-    return;
+	case 3: /* push on stack. */
+		if (stackptr + 4 > stacksize) {
+			fatal_error("Stack overflow in store operand.");
+		}
+		StkW4(stackptr, storeval);
+		stackptr += 4;
+		return;
 
-  default:
-    fatal_error("Unknown destination type in store operand.");
+	default:
+		fatal_error("Unknown destination type in store operand.");
 
-  }
+	}
 }
 
 void Glulxe::store_operand_s(uint desttype, uint destaddr, uint storeval) {
-  storeval &= 0xFFFF;
+	storeval &= 0xFFFF;
 
-  switch (desttype) {
+	switch (desttype) {
 
-  case 0: /* do nothing; discard the value. */
-    return;
+	case 0: /* do nothing; discard the value. */
+		return;
 
-  case 1: /* main memory. */
-    MemW2(destaddr, storeval);
-    return;
+	case 1: /* main memory. */
+		MemW2(destaddr, storeval);
+		return;
 
-  case 2: /* locals. */
-    destaddr += localsbase;
-    StkW2(destaddr, storeval);
-    return;
+	case 2: /* locals. */
+		destaddr += localsbase;
+		StkW2(destaddr, storeval);
+		return;
 
-  case 3: /* push on stack. A four-byte value is actually pushed. */
-    if (stackptr+4 > stacksize) {
-      fatal_error("Stack overflow in store operand.");
-    }
-    StkW4(stackptr, storeval);
-    stackptr += 4;
-    return;
+	case 3: /* push on stack. A four-byte value is actually pushed. */
+		if (stackptr + 4 > stacksize) {
+			fatal_error("Stack overflow in store operand.");
+		}
+		StkW4(stackptr, storeval);
+		stackptr += 4;
+		return;
 
-  default:
-    fatal_error("Unknown destination type in store operand.");
+	default:
+		fatal_error("Unknown destination type in store operand.");
 
-  }
+	}
 }
 
 void Glulxe::store_operand_b(uint desttype, uint destaddr, uint storeval) {
-  storeval &= 0xFF;
+	storeval &= 0xFF;
 
-  switch (desttype) {
+	switch (desttype) {
 
-  case 0: /* do nothing; discard the value. */
-    return;
+	case 0: /* do nothing; discard the value. */
+		return;
 
-  case 1: /* main memory. */
-    MemW1(destaddr, storeval);
-    return;
+	case 1: /* main memory. */
+		MemW1(destaddr, storeval);
+		return;
 
-  case 2: /* locals. */
-    destaddr += localsbase;
-    StkW1(destaddr, storeval);
-    return;
+	case 2: /* locals. */
+		destaddr += localsbase;
+		StkW1(destaddr, storeval);
+		return;
 
-  case 3: /* push on stack. A four-byte value is actually pushed. */
-    if (stackptr+4 > stacksize) {
-      fatal_error("Stack overflow in store operand.");
-    }
-    StkW4(stackptr, storeval);
-    stackptr += 4;
-    return;
+	case 3: /* push on stack. A four-byte value is actually pushed. */
+		if (stackptr + 4 > stacksize) {
+			fatal_error("Stack overflow in store operand.");
+		}
+		StkW4(stackptr, storeval);
+		stackptr += 4;
+		return;
 
-  default:
-    fatal_error("Unknown destination type in store operand.");
+	default:
+		fatal_error("Unknown destination type in store operand.");
 
-  }
+	}
 }
 
 } // End of namespace Glulxe
diff --git a/engines/glk/glulxe/search.cpp b/engines/glk/glulxe/search.cpp
index 440da55..1bbe8fa 100644
--- a/engines/glk/glulxe/search.cpp
+++ b/engines/glk/glulxe/search.cpp
@@ -31,185 +31,180 @@ enum serop {
 	serop_ReturnIndex       = 0x04
 };
 
-uint Glulxe::linear_search(uint key, uint keysize,  uint start, uint structsize, uint numstructs, 
-		uint keyoffset, uint options) {
-  unsigned char keybuf[4];
-  uint count;
-  uint ix;
-  int retindex = ((options & serop_ReturnIndex) != 0);
-  int zeroterm = ((options & serop_ZeroKeyTerminates) != 0);
-
-  fetchkey(keybuf, key, keysize, options);
-
-  for (count=0; count<numstructs; count++, start+=structsize) {
-    int match = true;
-    if (keysize <= 4) {
-      for (ix=0; match && ix<keysize; ix++) {
-        if (Mem1(start + keyoffset + ix) != keybuf[ix])
-          match = false;
-      }
-    }
-    else {
-      for (ix=0; match && ix<keysize; ix++) {
-        if (Mem1(start + keyoffset + ix) != Mem1(key + ix))
-          match = false;
-      }
-    }
-
-    if (match) {
-      if (retindex)
-        return count;
-      else
-        return start;
-    }
-
-    if (zeroterm) {
-      match = true;
-      for (ix=0; match && ix<keysize; ix++) {
-        if (Mem1(start + keyoffset + ix) != 0)
-          match = false;
-      }
-      if (match) {
-        break;
-      }
-    }
-  }
-  
-  if (retindex)
-    return (uint)-1;
-  else
-    return 0;
+uint Glulxe::linear_search(uint key, uint keysize,  uint start, uint structsize, uint numstructs,
+                           uint keyoffset, uint options) {
+	unsigned char keybuf[4];
+	uint count;
+	uint ix;
+	int retindex = ((options & serop_ReturnIndex) != 0);
+	int zeroterm = ((options & serop_ZeroKeyTerminates) != 0);
+
+	fetchkey(keybuf, key, keysize, options);
+
+	for (count = 0; count < numstructs; count++, start += structsize) {
+		int match = true;
+		if (keysize <= 4) {
+			for (ix = 0; match && ix < keysize; ix++) {
+				if (Mem1(start + keyoffset + ix) != keybuf[ix])
+					match = false;
+			}
+		} else {
+			for (ix = 0; match && ix < keysize; ix++) {
+				if (Mem1(start + keyoffset + ix) != Mem1(key + ix))
+					match = false;
+			}
+		}
+
+		if (match) {
+			if (retindex)
+				return count;
+			else
+				return start;
+		}
+
+		if (zeroterm) {
+			match = true;
+			for (ix = 0; match && ix < keysize; ix++) {
+				if (Mem1(start + keyoffset + ix) != 0)
+					match = false;
+			}
+			if (match) {
+				break;
+			}
+		}
+	}
+
+	if (retindex)
+		return (uint) - 1;
+	else
+		return 0;
 }
 
 
-uint Glulxe::binary_search(uint key, uint keysize,  uint start, uint structsize, uint numstructs, 
-		uint keyoffset, uint options) {
-  byte keybuf[4];
-  byte byte1, byte2;
-  uint top, bot, val, addr;
-  uint ix;
-  int retindex = ((options & serop_ReturnIndex) != 0);
-
-  fetchkey(keybuf, key, keysize, options);
-  
-  bot = 0;
-  top = numstructs;
-  while (bot < top) {
-    int cmp = 0;
-    val = (top+bot) / 2;
-    addr = start + val * structsize;
-
-    if (keysize <= 4) {
-      for (ix=0; (!cmp) && ix<keysize; ix++) {
-        byte1 = Mem1(addr + keyoffset + ix);
-        byte2 = keybuf[ix];
-        if (byte1 < byte2)
-          cmp = -1;
-        else if (byte1 > byte2)
-          cmp = 1;
-      }
-    }
-    else {
-      for (ix=0; (!cmp) && ix<keysize; ix++) {
-        byte1 = Mem1(addr + keyoffset + ix);
-        byte2 = Mem1(key + ix);
-        if (byte1 < byte2)
-          cmp = -1;
-        else if (byte1 > byte2)
-          cmp = 1;
-      }
-    }
-
-    if (!cmp) {
-      if (retindex)
-        return val;
-      else
-        return addr;
-    }
-
-    if (cmp < 0) {
-      bot = val+1;
-    }
-    else {
-      top = val;
-    }
-  }
-
-  if (retindex)
-    return (uint)-1;
-  else
-    return 0;
+uint Glulxe::binary_search(uint key, uint keysize,  uint start, uint structsize, uint numstructs,
+                           uint keyoffset, uint options) {
+	byte keybuf[4];
+	byte byte1, byte2;
+	uint top, bot, val, addr;
+	uint ix;
+	int retindex = ((options & serop_ReturnIndex) != 0);
+
+	fetchkey(keybuf, key, keysize, options);
+
+	bot = 0;
+	top = numstructs;
+	while (bot < top) {
+		int cmp = 0;
+		val = (top + bot) / 2;
+		addr = start + val * structsize;
+
+		if (keysize <= 4) {
+			for (ix = 0; (!cmp) && ix < keysize; ix++) {
+				byte1 = Mem1(addr + keyoffset + ix);
+				byte2 = keybuf[ix];
+				if (byte1 < byte2)
+					cmp = -1;
+				else if (byte1 > byte2)
+					cmp = 1;
+			}
+		} else {
+			for (ix = 0; (!cmp) && ix < keysize; ix++) {
+				byte1 = Mem1(addr + keyoffset + ix);
+				byte2 = Mem1(key + ix);
+				if (byte1 < byte2)
+					cmp = -1;
+				else if (byte1 > byte2)
+					cmp = 1;
+			}
+		}
+
+		if (!cmp) {
+			if (retindex)
+				return val;
+			else
+				return addr;
+		}
+
+		if (cmp < 0) {
+			bot = val + 1;
+		} else {
+			top = val;
+		}
+	}
+
+	if (retindex)
+		return (uint) - 1;
+	else
+		return 0;
 }
 
 uint Glulxe::linked_search(uint key, uint keysize,  uint start, uint keyoffset, uint nextoffset, uint options) {
-  unsigned char keybuf[4];
-  uint ix;
-  uint val;
-  int zeroterm = ((options & serop_ZeroKeyTerminates) != 0);
-
-  fetchkey(keybuf, key, keysize, options);
-
-  while (start != 0) {
-    int match = true;
-    if (keysize <= 4) {
-      for (ix=0; match && ix<keysize; ix++) {
-        if (Mem1(start + keyoffset + ix) != keybuf[ix])
-          match = false;
-      }
-    }
-    else {
-      for (ix=0; match && ix<keysize; ix++) {
-        if (Mem1(start + keyoffset + ix) != Mem1(key + ix))
-          match = false;
-      }
-    }
-
-    if (match) {
-      return start;
-    }
-
-    if (zeroterm) {
-      match = true;
-      for (ix=0; match && ix<keysize; ix++) {
-        if (Mem1(start + keyoffset + ix) != 0)
-          match = false;
-      }
-      if (match) {
-        break;
-      }
-    }
-    
-    val = start + nextoffset;
-    start = Mem4(val);
-  }
-
-  return 0;
+	unsigned char keybuf[4];
+	uint ix;
+	uint val;
+	int zeroterm = ((options & serop_ZeroKeyTerminates) != 0);
+
+	fetchkey(keybuf, key, keysize, options);
+
+	while (start != 0) {
+		int match = true;
+		if (keysize <= 4) {
+			for (ix = 0; match && ix < keysize; ix++) {
+				if (Mem1(start + keyoffset + ix) != keybuf[ix])
+					match = false;
+			}
+		} else {
+			for (ix = 0; match && ix < keysize; ix++) {
+				if (Mem1(start + keyoffset + ix) != Mem1(key + ix))
+					match = false;
+			}
+		}
+
+		if (match) {
+			return start;
+		}
+
+		if (zeroterm) {
+			match = true;
+			for (ix = 0; match && ix < keysize; ix++) {
+				if (Mem1(start + keyoffset + ix) != 0)
+					match = false;
+			}
+			if (match) {
+				break;
+			}
+		}
+
+		val = start + nextoffset;
+		start = Mem4(val);
+	}
+
+	return 0;
 }
 
 void Glulxe::fetchkey(unsigned char *keybuf, uint key, uint keysize,  uint options) {
-  uint ix;
-
-  if (options & serop_KeyIndirect) {
-    if (keysize <= 4) {
-      for (ix=0; ix<keysize; ix++)
-        keybuf[ix] = Mem1(key+ix);
-    }
-  }
-  else {
-    switch (keysize) {
-    case 4:
-      Write4(keybuf, key);
-      break;
-    case 2:
-      Write2(keybuf, key);
-      break;
-    case 1:
-      Write1(keybuf, key);
-      break;
-    default:
-      fatal_error("Direct search key must hold one, two, or four bytes.");
-    }
-  }
+	uint ix;
+
+	if (options & serop_KeyIndirect) {
+		if (keysize <= 4) {
+			for (ix = 0; ix < keysize; ix++)
+				keybuf[ix] = Mem1(key + ix);
+		}
+	} else {
+		switch (keysize) {
+		case 4:
+			Write4(keybuf, key);
+			break;
+		case 2:
+			Write2(keybuf, key);
+			break;
+		case 1:
+			Write1(keybuf, key);
+			break;
+		default:
+			fatal_error("Direct search key must hold one, two, or four bytes.");
+		}
+	}
 }
 
 } // End of namespace Glulxe
diff --git a/engines/glk/glulxe/serial.cpp b/engines/glk/glulxe/serial.cpp
index bc26788..01848fc 100644
--- a/engines/glk/glulxe/serial.cpp
+++ b/engines/glk/glulxe/serial.cpp
@@ -28,1116 +28,1104 @@ namespace Glulxe {
 #define IFFID(c1, c2, c3, c4) MKTAG(c1, c2, c3, c4)
 
 bool Glulxe::init_serial() {
-  undo_chain_num = 0;
-  undo_chain_size = max_undo_level;
-  undo_chain = (unsigned char **)glulx_malloc(sizeof(unsigned char *) * undo_chain_size);
-  if (!undo_chain)
-    return false;
+	undo_chain_num = 0;
+	undo_chain_size = max_undo_level;
+	undo_chain = (unsigned char **)glulx_malloc(sizeof(unsigned char *) * undo_chain_size);
+	if (!undo_chain)
+		return false;
 
 #ifdef SERIALIZE_CACHE_RAM
-  {
-    uint len = (endmem - ramstart);
-    uint res;
-    ramcache = (unsigned char *)glulx_malloc(sizeof(unsigned char *) * len);
-    if (!ramcache)
-      return false;
-
-	_gameFile.seek(gamefile_start + ramstart);
-    res = _gameFile.read(ramcache, len);
-    if (res != len)
-      return false;
-  }
+	{
+		uint len = (endmem - ramstart);
+		uint res;
+		ramcache = (unsigned char *)glulx_malloc(sizeof(unsigned char *) * len);
+		if (!ramcache)
+			return false;
+
+		_gameFile.seek(gamefile_start + ramstart);
+		res = _gameFile.read(ramcache, len);
+		if (res != len)
+			return false;
+	}
 #endif /* SERIALIZE_CACHE_RAM */
 
-  return true;
+	return true;
 }
 
 void Glulxe::final_serial() {
-  if (undo_chain) {
-    int ix;
-    for (ix=0; ix<undo_chain_num; ix++) {
-      glulx_free(undo_chain[ix]);
-    }
-    glulx_free(undo_chain);
-  }
-  undo_chain = nullptr;
-  undo_chain_size = 0;
-  undo_chain_num = 0;
+	if (undo_chain) {
+		int ix;
+		for (ix = 0; ix < undo_chain_num; ix++) {
+			glulx_free(undo_chain[ix]);
+		}
+		glulx_free(undo_chain);
+	}
+	undo_chain = nullptr;
+	undo_chain_size = 0;
+	undo_chain_num = 0;
 
 #ifdef SERIALIZE_CACHE_RAM
-  if (ramcache) {
-    glulx_free(ramcache);
-    ramcache = nullptr;
-  }
+	if (ramcache) {
+		glulx_free(ramcache);
+		ramcache = nullptr;
+	}
 #endif /* SERIALIZE_CACHE_RAM */
 }
 
 uint Glulxe::perform_saveundo() {
-  dest_t dest;
-  uint res;
-  uint memstart = 0, memlen = 0, heapstart = 0, heaplen = 0;
-  uint stackstart = 0, stacklen = 0;
-
-  /* The format for undo-saves is simpler than for saves on disk. We
-     just have a memory chunk, a heap chunk, and a stack chunk, in
-     that order. We skip the IFF chunk headers (although the size
-     fields are still there.) We also don't bother with IFF's 16-bit
-     alignment. */
-
-  if (undo_chain_size == 0)
-    return 1;
-
-  dest.ismem = true;
-  dest.size = 0;
-  dest.pos = 0;
-  dest.ptr = nullptr;
-  dest.str = nullptr;
-
-  res = 0;
-  if (res == 0) {
-    res = write_long(&dest, 0); /* space for chunk length */
-  }
-  if (res == 0) {
-    memstart = dest.pos;
-    res = write_memstate(&dest);
-    memlen = dest.pos - memstart;
-  }
-  if (res == 0) {
-    res = write_long(&dest, 0); /* space for chunk length */
-  }
-  if (res == 0) {
-    heapstart = dest.pos;
-    res = write_heapstate(&dest, false);
-    heaplen = dest.pos - heapstart;
-  }
-  if (res == 0) {
-    res = write_long(&dest, 0); /* space for chunk length */
-  }
-  if (res == 0) {
-    stackstart = dest.pos;
-    res = write_stackstate(&dest, false);
-    stacklen = dest.pos - stackstart;
-  }
-
-  if (res == 0) {
-    /* Trim it down to the perfect size. */
-    dest.ptr = (byte *)glulx_realloc(dest.ptr, dest.pos);
-    if (!dest.ptr)
-      res = 1;
-  }
-  if (res == 0) {
-    res = reposition_write(&dest, memstart-4);
-  }
-  if (res == 0) {
-    res = write_long(&dest, memlen);
-  }
-  if (res == 0) {
-    res = reposition_write(&dest, heapstart-4);
-  }
-  if (res == 0) {
-    res = write_long(&dest, heaplen);
-  }
-  if (res == 0) {
-    res = reposition_write(&dest, stackstart-4);
-  }
-  if (res == 0) {
-    res = write_long(&dest, stacklen);
-  }
-
-  if (res == 0) {
-    /* It worked. */
-    if (undo_chain_num >= undo_chain_size) {
-      glulx_free(undo_chain[undo_chain_num-1]);
-      undo_chain[undo_chain_num-1] = nullptr;
-    }
-    if (undo_chain_size > 1)
-      memmove(undo_chain+1, undo_chain, 
-        (undo_chain_size-1) * sizeof(unsigned char *));
-    undo_chain[0] = dest.ptr;
-    if (undo_chain_num < undo_chain_size)
-      undo_chain_num += 1;
-    dest.ptr = nullptr;
-  }
-  else {
-    /* It didn't work. */
-    if (dest.ptr) {
-      glulx_free(dest.ptr);
-      dest.ptr = nullptr;
-    }
-  }
-    
-  return res;
+	dest_t dest;
+	uint res;
+	uint memstart = 0, memlen = 0, heapstart = 0, heaplen = 0;
+	uint stackstart = 0, stacklen = 0;
+
+	/* The format for undo-saves is simpler than for saves on disk. We
+	   just have a memory chunk, a heap chunk, and a stack chunk, in
+	   that order. We skip the IFF chunk headers (although the size
+	   fields are still there.) We also don't bother with IFF's 16-bit
+	   alignment. */
+
+	if (undo_chain_size == 0)
+		return 1;
+
+	dest.ismem = true;
+	dest.size = 0;
+	dest.pos = 0;
+	dest.ptr = nullptr;
+	dest.str = nullptr;
+
+	res = 0;
+	if (res == 0) {
+		res = write_long(&dest, 0); /* space for chunk length */
+	}
+	if (res == 0) {
+		memstart = dest.pos;
+		res = write_memstate(&dest);
+		memlen = dest.pos - memstart;
+	}
+	if (res == 0) {
+		res = write_long(&dest, 0); /* space for chunk length */
+	}
+	if (res == 0) {
+		heapstart = dest.pos;
+		res = write_heapstate(&dest, false);
+		heaplen = dest.pos - heapstart;
+	}
+	if (res == 0) {
+		res = write_long(&dest, 0); /* space for chunk length */
+	}
+	if (res == 0) {
+		stackstart = dest.pos;
+		res = write_stackstate(&dest, false);
+		stacklen = dest.pos - stackstart;
+	}
+
+	if (res == 0) {
+		/* Trim it down to the perfect size. */
+		dest.ptr = (byte *)glulx_realloc(dest.ptr, dest.pos);
+		if (!dest.ptr)
+			res = 1;
+	}
+	if (res == 0) {
+		res = reposition_write(&dest, memstart - 4);
+	}
+	if (res == 0) {
+		res = write_long(&dest, memlen);
+	}
+	if (res == 0) {
+		res = reposition_write(&dest, heapstart - 4);
+	}
+	if (res == 0) {
+		res = write_long(&dest, heaplen);
+	}
+	if (res == 0) {
+		res = reposition_write(&dest, stackstart - 4);
+	}
+	if (res == 0) {
+		res = write_long(&dest, stacklen);
+	}
+
+	if (res == 0) {
+		/* It worked. */
+		if (undo_chain_num >= undo_chain_size) {
+			glulx_free(undo_chain[undo_chain_num - 1]);
+			undo_chain[undo_chain_num - 1] = nullptr;
+		}
+		if (undo_chain_size > 1)
+			memmove(undo_chain + 1, undo_chain,
+			        (undo_chain_size - 1) * sizeof(unsigned char *));
+		undo_chain[0] = dest.ptr;
+		if (undo_chain_num < undo_chain_size)
+			undo_chain_num += 1;
+		dest.ptr = nullptr;
+	} else {
+		/* It didn't work. */
+		if (dest.ptr) {
+			glulx_free(dest.ptr);
+			dest.ptr = nullptr;
+		}
+	}
+
+	return res;
 }
 
 uint Glulxe::perform_restoreundo() {
-  dest_t dest;
-  uint res, val = 0;
-  uint heapsumlen = 0;
-  uint *heapsumarr = nullptr;
-
-  /* If profiling is enabled and active then fail. */
-  #if VM_PROFILING
-  if (profile_profiling_active())
-    return 1;
-  #endif /* VM_PROFILING */
-
-  if (undo_chain_size == 0 || undo_chain_num == 0)
-    return 1;
-
-  dest.ismem = true;
-  dest.size = 0;
-  dest.pos = 0;
-  dest.ptr = undo_chain[0];
-  dest.str = nullptr;
-
-  res = 0;
-  if (res == 0) {
-    res = read_long(&dest, &val);
-  }
-  if (res == 0) {
-    res = read_memstate(&dest, val);
-  }
-  if (res == 0) {
-    res = read_long(&dest, &val);
-  }
-  if (res == 0) {
-    res = read_heapstate(&dest, val, false, &heapsumlen, &heapsumarr);
-  }
-  if (res == 0) {
-    res = read_long(&dest, &val);
-  }
-  if (res == 0) {
-    res = read_stackstate(&dest, val, false);
-  }
-  /* ### really, many of the failure modes of those calls ought to
-     cause fatal errors. The stack or main memory may be damaged now. */
-
-  if (res == 0) {
-    if (heapsumarr)
-      res = heap_apply_summary(heapsumlen, heapsumarr);
-  }
-
-  if (res == 0) {
-    /* It worked. */
-    if (undo_chain_size > 1)
-      memmove(undo_chain, undo_chain+1,
-        (undo_chain_size-1) * sizeof(unsigned char *));
-    undo_chain_num -= 1;
-    glulx_free(dest.ptr);
-    dest.ptr = nullptr;
-  }
-  else {
-    /* It didn't work. */
-    dest.ptr = nullptr;
-  }
-
-  return res;
+	dest_t dest;
+	uint res, val = 0;
+	uint heapsumlen = 0;
+	uint *heapsumarr = nullptr;
+
+	/* If profiling is enabled and active then fail. */
+#if VM_PROFILING
+	if (profile_profiling_active())
+		return 1;
+#endif /* VM_PROFILING */
+
+	if (undo_chain_size == 0 || undo_chain_num == 0)
+		return 1;
+
+	dest.ismem = true;
+	dest.size = 0;
+	dest.pos = 0;
+	dest.ptr = undo_chain[0];
+	dest.str = nullptr;
+
+	res = 0;
+	if (res == 0) {
+		res = read_long(&dest, &val);
+	}
+	if (res == 0) {
+		res = read_memstate(&dest, val);
+	}
+	if (res == 0) {
+		res = read_long(&dest, &val);
+	}
+	if (res == 0) {
+		res = read_heapstate(&dest, val, false, &heapsumlen, &heapsumarr);
+	}
+	if (res == 0) {
+		res = read_long(&dest, &val);
+	}
+	if (res == 0) {
+		res = read_stackstate(&dest, val, false);
+	}
+	/* ### really, many of the failure modes of those calls ought to
+	   cause fatal errors. The stack or main memory may be damaged now. */
+
+	if (res == 0) {
+		if (heapsumarr)
+			res = heap_apply_summary(heapsumlen, heapsumarr);
+	}
+
+	if (res == 0) {
+		/* It worked. */
+		if (undo_chain_size > 1)
+			memmove(undo_chain, undo_chain + 1,
+			        (undo_chain_size - 1) * sizeof(unsigned char *));
+		undo_chain_num -= 1;
+		glulx_free(dest.ptr);
+		dest.ptr = nullptr;
+	} else {
+		/* It didn't work. */
+		dest.ptr = nullptr;
+	}
+
+	return res;
 }
 
 uint Glulxe::perform_save(strid_t str) {
-  dest_t dest;
-  int ix;
-  uint res, lx, val;
-  uint memstart = 0, memlen = 0, stackstart = 0, stacklen = 0;
-  uint heapstart = 0, heaplen = 0, filestart = 0, filelen = 0;
-
-  stream_get_iosys(&val, &lx);
-  if (val != 2) {
-    /* Not using the Glk I/O system, so bail. This function only
-       knows how to write to a Glk stream. */
-    fatal_error("Streams are only available in Glk I/O system.");
-  }
-
-  if (str == 0)
-    return 1;
-
-  dest.ismem = false;
-  dest.size = 0;
-  dest.pos = 0;
-  dest.ptr = nullptr;
-  dest.str = str;
-
-  res = 0;
-
-  /* Quetzal header. */
-  if (res == 0) {
-    res = write_long(&dest, IFFID('F', 'O', 'R', 'M'));
-  }
-  if (res == 0) {
-    res = write_long(&dest, 0); /* space for file length */
-    filestart = dest.pos;
-  }
-
-  if (res == 0) {
-    res = write_long(&dest, IFFID('I', 'F', 'Z', 'S')); /* ### ? */
-  }
-
-  /* Header chunk. This is the first 128 bytes of memory. */
-  if (res == 0) {
-    res = write_long(&dest, IFFID('I', 'F', 'h', 'd'));
-  }
-  if (res == 0) {
-    res = write_long(&dest, 128);
-  }
-  for (ix=0; res==0 && ix<128; ix++) {
-    res = write_byte(&dest, Mem1(ix));
-  }
-  /* Always even, so no padding necessary. */
-  
-  /* Memory chunk. */
-  if (res == 0) {
-    res = write_long(&dest, IFFID('C', 'M', 'e', 'm'));
-  }
-  if (res == 0) {
-    res = write_long(&dest, 0); /* space for chunk length */
-  }
-  if (res == 0) {
-    memstart = dest.pos;
-    res = write_memstate(&dest);
-    memlen = dest.pos - memstart;
-  }
-  if (res == 0 && (memlen & 1) != 0) {
-    res = write_byte(&dest, 0);
-  }
-
-  /* Heap chunk. */
-  if (res == 0) {
-    res = write_long(&dest, IFFID('M', 'A', 'l', 'l'));
-  }
-  if (res == 0) {
-    res = write_long(&dest, 0); /* space for chunk length */
-  }
-  if (res == 0) {
-    heapstart = dest.pos;
-    res = write_heapstate(&dest, true);
-    heaplen = dest.pos - heapstart;
-  }
-  /* Always even, so no padding necessary. */
-
-  /* Stack chunk. */
-  if (res == 0) {
-    res = write_long(&dest, IFFID('S', 't', 'k', 's'));
-  }
-  if (res == 0) {
-    res = write_long(&dest, 0); /* space for chunk length */
-  }
-  if (res == 0) {
-    stackstart = dest.pos;
-    res = write_stackstate(&dest, true);
-    stacklen = dest.pos - stackstart;
-  }
-  if (res == 0 && (stacklen & 1) != 0) {
-    res = write_byte(&dest, 0);
-  }
-
-  filelen = dest.pos - filestart;
-
-  /* Okay, fill in all the lengths. */
-  if (res == 0) {
-    res = reposition_write(&dest, memstart-4);
-  }
-  if (res == 0) {
-    res = write_long(&dest, memlen);
-  }
-  if (res == 0) {
-    res = reposition_write(&dest, heapstart-4);
-  }
-  if (res == 0) {
-    res = write_long(&dest, heaplen);
-  }
-  if (res == 0) {
-    res = reposition_write(&dest, stackstart-4);
-  }
-  if (res == 0) {
-    res = write_long(&dest, stacklen);
-  }
-  if (res == 0) {
-    res = reposition_write(&dest, filestart-4);
-  }
-  if (res == 0) {
-    res = write_long(&dest, filelen);
-  }
-
-  /* All done. */
-    
-  return res;
+	dest_t dest;
+	int ix;
+	uint res, lx, val;
+	uint memstart = 0, memlen = 0, stackstart = 0, stacklen = 0;
+	uint heapstart = 0, heaplen = 0, filestart = 0, filelen = 0;
+
+	stream_get_iosys(&val, &lx);
+	if (val != 2) {
+		/* Not using the Glk I/O system, so bail. This function only
+		   knows how to write to a Glk stream. */
+		fatal_error("Streams are only available in Glk I/O system.");
+	}
+
+	if (str == 0)
+		return 1;
+
+	dest.ismem = false;
+	dest.size = 0;
+	dest.pos = 0;
+	dest.ptr = nullptr;
+	dest.str = str;
+
+	res = 0;
+
+	/* Quetzal header. */
+	if (res == 0) {
+		res = write_long(&dest, IFFID('F', 'O', 'R', 'M'));
+	}
+	if (res == 0) {
+		res = write_long(&dest, 0); /* space for file length */
+		filestart = dest.pos;
+	}
+
+	if (res == 0) {
+		res = write_long(&dest, IFFID('I', 'F', 'Z', 'S')); /* ### ? */
+	}
+
+	/* Header chunk. This is the first 128 bytes of memory. */
+	if (res == 0) {
+		res = write_long(&dest, IFFID('I', 'F', 'h', 'd'));
+	}
+	if (res == 0) {
+		res = write_long(&dest, 128);
+	}
+	for (ix = 0; res == 0 && ix < 128; ix++) {
+		res = write_byte(&dest, Mem1(ix));
+	}
+	/* Always even, so no padding necessary. */
+
+	/* Memory chunk. */
+	if (res == 0) {
+		res = write_long(&dest, IFFID('C', 'M', 'e', 'm'));
+	}
+	if (res == 0) {
+		res = write_long(&dest, 0); /* space for chunk length */
+	}
+	if (res == 0) {
+		memstart = dest.pos;
+		res = write_memstate(&dest);
+		memlen = dest.pos - memstart;
+	}
+	if (res == 0 && (memlen & 1) != 0) {
+		res = write_byte(&dest, 0);
+	}
+
+	/* Heap chunk. */
+	if (res == 0) {
+		res = write_long(&dest, IFFID('M', 'A', 'l', 'l'));
+	}
+	if (res == 0) {
+		res = write_long(&dest, 0); /* space for chunk length */
+	}
+	if (res == 0) {
+		heapstart = dest.pos;
+		res = write_heapstate(&dest, true);
+		heaplen = dest.pos - heapstart;
+	}
+	/* Always even, so no padding necessary. */
+
+	/* Stack chunk. */
+	if (res == 0) {
+		res = write_long(&dest, IFFID('S', 't', 'k', 's'));
+	}
+	if (res == 0) {
+		res = write_long(&dest, 0); /* space for chunk length */
+	}
+	if (res == 0) {
+		stackstart = dest.pos;
+		res = write_stackstate(&dest, true);
+		stacklen = dest.pos - stackstart;
+	}
+	if (res == 0 && (stacklen & 1) != 0) {
+		res = write_byte(&dest, 0);
+	}
+
+	filelen = dest.pos - filestart;
+
+	/* Okay, fill in all the lengths. */
+	if (res == 0) {
+		res = reposition_write(&dest, memstart - 4);
+	}
+	if (res == 0) {
+		res = write_long(&dest, memlen);
+	}
+	if (res == 0) {
+		res = reposition_write(&dest, heapstart - 4);
+	}
+	if (res == 0) {
+		res = write_long(&dest, heaplen);
+	}
+	if (res == 0) {
+		res = reposition_write(&dest, stackstart - 4);
+	}
+	if (res == 0) {
+		res = write_long(&dest, stacklen);
+	}
+	if (res == 0) {
+		res = reposition_write(&dest, filestart - 4);
+	}
+	if (res == 0) {
+		res = write_long(&dest, filelen);
+	}
+
+	/* All done. */
+
+	return res;
 }
 
 uint Glulxe::perform_restore(strid_t str, int fromshell) {
-  dest_t dest;
-  int ix;
-  uint lx, res, val;
-  uint filestart, filelen = 0;
-  uint heapsumlen = 0;
-  uint *heapsumarr = nullptr;
-
-  /* If profiling is enabled and active then fail. */
-  #if VM_PROFILING
-  if (profile_profiling_active())
-    return 1;
-  #endif /* VM_PROFILING */
-
-  stream_get_iosys(&val, &lx);
-  if (val != 2 && !fromshell) {
-    /* Not using the Glk I/O system, so bail. This function only
-       knows how to read from a Glk stream. (But in the autorestore
-       case, iosys hasn't been set yet, so ignore this test.) */
-    fatal_error("Streams are only available in Glk I/O system.");
-  }
-
-  if (str == 0)
-    return 1;
-
-  dest.ismem = false;
-  dest.size = 0;
-  dest.pos = 0;
-  dest.ptr = nullptr;
-  dest.str = str;
-
-  res = 0;
-
-  /* ### the format errors checked below should send error messages to
-     the current stream. */
-
-  if (res == 0) {
-    res = read_long(&dest, &val);
-  }
-  if (res == 0 && val != IFFID('F', 'O', 'R', 'M')) {
-    /* ### bad header */
-    return 1;
-  }
-  if (res == 0) {
-    res = read_long(&dest, &filelen);
-  }
-  filestart = dest.pos;
-
-  if (res == 0) {
-    res = read_long(&dest, &val);
-  }
-  if (res == 0 && val != IFFID('I', 'F', 'Z', 'S')) { /* ### ? */
-    /* ### bad header */
-    return 1;
-  }
-
-  while (res == 0 && dest.pos < filestart+filelen) {
-    /* Read a chunk and deal with it. */
-    uint chunktype=0, chunkstart=0, chunklen=0;
-    unsigned char dummy;
-
-    if (res == 0) {
-      res = read_long(&dest, &chunktype);
-    }
-    if (res == 0) {
-      res = read_long(&dest, &chunklen);
-    }
-    chunkstart = dest.pos;
-
-    if (chunktype == IFFID('I', 'F', 'h', 'd')) {
-      for (ix=0; res==0 && ix<128; ix++) {
-        res = read_byte(&dest, &dummy);
-        if (res == 0 && Mem1(ix) != dummy) {
-          /* ### non-matching header */
-          return 1;
-        }
-      }
-    }
-    else if (chunktype == IFFID('C', 'M', 'e', 'm')) {
-      res = read_memstate(&dest, chunklen);
-    }
-    else if (chunktype == IFFID('M', 'A', 'l', 'l')) {
-      res = read_heapstate(&dest, chunklen, true, &heapsumlen, &heapsumarr);
-    }
-    else if (chunktype == IFFID('S', 't', 'k', 's')) {
-      res = read_stackstate(&dest, chunklen, true);
-    }
-    else {
-      /* Unknown chunk type. Skip it. */
-      for (lx=0; res==0 && lx<chunklen; lx++) {
-        res = read_byte(&dest, &dummy);
-      }
-    }
-
-    if (chunkstart+chunklen != dest.pos) {
-      /* ### funny chunk length */
-      return 1;
-    }
-
-    if ((chunklen & 1) != 0) {
-      if (res == 0) {
-        res = read_byte(&dest, &dummy);
-      }
-    }
-  }
-
-  if (res == 0) {
-    if (heapsumarr) {
-      /* The summary might have come from any interpreter, so it could
-         be out of order. We'll sort it. */
-      glulx_sort(heapsumarr+2, (heapsumlen-2)/2, 2*sizeof(uint), &sort_heap_summary);
-      res = heap_apply_summary(heapsumlen, heapsumarr);
-    }
-  }
-
-  if (res)
-    return 1;
-
-  return 0;
+	dest_t dest;
+	int ix;
+	uint lx, res, val;
+	uint filestart, filelen = 0;
+	uint heapsumlen = 0;
+	uint *heapsumarr = nullptr;
+
+	/* If profiling is enabled and active then fail. */
+#if VM_PROFILING
+	if (profile_profiling_active())
+		return 1;
+#endif /* VM_PROFILING */
+
+	stream_get_iosys(&val, &lx);
+	if (val != 2 && !fromshell) {
+		/* Not using the Glk I/O system, so bail. This function only
+		   knows how to read from a Glk stream. (But in the autorestore
+		   case, iosys hasn't been set yet, so ignore this test.) */
+		fatal_error("Streams are only available in Glk I/O system.");
+	}
+
+	if (str == 0)
+		return 1;
+
+	dest.ismem = false;
+	dest.size = 0;
+	dest.pos = 0;
+	dest.ptr = nullptr;
+	dest.str = str;
+
+	res = 0;
+
+	/* ### the format errors checked below should send error messages to
+	   the current stream. */
+
+	if (res == 0) {
+		res = read_long(&dest, &val);
+	}
+	if (res == 0 && val != IFFID('F', 'O', 'R', 'M')) {
+		/* ### bad header */
+		return 1;
+	}
+	if (res == 0) {
+		res = read_long(&dest, &filelen);
+	}
+	filestart = dest.pos;
+
+	if (res == 0) {
+		res = read_long(&dest, &val);
+	}
+	if (res == 0 && val != IFFID('I', 'F', 'Z', 'S')) { /* ### ? */
+		/* ### bad header */
+		return 1;
+	}
+
+	while (res == 0 && dest.pos < filestart + filelen) {
+		/* Read a chunk and deal with it. */
+		uint chunktype = 0, chunkstart = 0, chunklen = 0;
+		unsigned char dummy;
+
+		if (res == 0) {
+			res = read_long(&dest, &chunktype);
+		}
+		if (res == 0) {
+			res = read_long(&dest, &chunklen);
+		}
+		chunkstart = dest.pos;
+
+		if (chunktype == IFFID('I', 'F', 'h', 'd')) {
+			for (ix = 0; res == 0 && ix < 128; ix++) {
+				res = read_byte(&dest, &dummy);
+				if (res == 0 && Mem1(ix) != dummy) {
+					/* ### non-matching header */
+					return 1;
+				}
+			}
+		} else if (chunktype == IFFID('C', 'M', 'e', 'm')) {
+			res = read_memstate(&dest, chunklen);
+		} else if (chunktype == IFFID('M', 'A', 'l', 'l')) {
+			res = read_heapstate(&dest, chunklen, true, &heapsumlen, &heapsumarr);
+		} else if (chunktype == IFFID('S', 't', 'k', 's')) {
+			res = read_stackstate(&dest, chunklen, true);
+		} else {
+			/* Unknown chunk type. Skip it. */
+			for (lx = 0; res == 0 && lx < chunklen; lx++) {
+				res = read_byte(&dest, &dummy);
+			}
+		}
+
+		if (chunkstart + chunklen != dest.pos) {
+			/* ### funny chunk length */
+			return 1;
+		}
+
+		if ((chunklen & 1) != 0) {
+			if (res == 0) {
+				res = read_byte(&dest, &dummy);
+			}
+		}
+	}
+
+	if (res == 0) {
+		if (heapsumarr) {
+			/* The summary might have come from any interpreter, so it could
+			   be out of order. We'll sort it. */
+			glulx_sort(heapsumarr + 2, (heapsumlen - 2) / 2, 2 * sizeof(uint), &sort_heap_summary);
+			res = heap_apply_summary(heapsumlen, heapsumarr);
+		}
+	}
+
+	if (res)
+		return 1;
+
+	return 0;
 }
 
 int Glulxe::reposition_write(dest_t *dest, uint pos) {
-  if (dest->ismem) {
-    dest->pos = pos;
-  } else {
-    glk_stream_set_position(dest->str, pos, seekmode_Start);
-    dest->pos = pos;
-  }
-
-  return 0;
+	if (dest->ismem) {
+		dest->pos = pos;
+	} else {
+		glk_stream_set_position(dest->str, pos, seekmode_Start);
+		dest->pos = pos;
+	}
+
+	return 0;
 }
 
 int Glulxe::write_buffer(dest_t *dest, const byte *ptr, uint len) {
-  if (dest->ismem) {
-    if (dest->pos+len > dest->size) {
-      dest->size = dest->pos+len+1024;
-      if (!dest->ptr) {
-        dest->ptr = (byte *)glulx_malloc(dest->size);
-      } else {
-        dest->ptr = (byte *)glulx_realloc(dest->ptr, dest->size);
-      }
-      if (!dest->ptr)
-        return 1;
-    }
-    memcpy(dest->ptr+dest->pos, ptr, len);
-  }
-  else {
-    glk_put_buffer_stream(dest->str, (char *)ptr, len);
-  }
-
-  dest->pos += len;
-
-  return 0;
+	if (dest->ismem) {
+		if (dest->pos + len > dest->size) {
+			dest->size = dest->pos + len + 1024;
+			if (!dest->ptr) {
+				dest->ptr = (byte *)glulx_malloc(dest->size);
+			} else {
+				dest->ptr = (byte *)glulx_realloc(dest->ptr, dest->size);
+			}
+			if (!dest->ptr)
+				return 1;
+		}
+		memcpy(dest->ptr + dest->pos, ptr, len);
+	} else {
+		glk_put_buffer_stream(dest->str, (char *)ptr, len);
+	}
+
+	dest->pos += len;
+
+	return 0;
 }
 
 int Glulxe::read_buffer(dest_t *dest, byte *ptr, uint len) {
-  uint newlen;
+	uint newlen;
 
-  if (dest->ismem) {
-    memcpy(ptr, dest->ptr+dest->pos, len);
-  }
-  else {
-    newlen = glk_get_buffer_stream(dest->str, (char *)ptr, len);
-    if (newlen != len)
-      return 1;
-  }
+	if (dest->ismem) {
+		memcpy(ptr, dest->ptr + dest->pos, len);
+	} else {
+		newlen = glk_get_buffer_stream(dest->str, (char *)ptr, len);
+		if (newlen != len)
+			return 1;
+	}
 
-  dest->pos += len;
+	dest->pos += len;
 
-  return 0;
+	return 0;
 }
 
 int Glulxe::write_long(dest_t *dest, uint val) {
-  unsigned char buf[4];
-  Write4(buf, val);
-  return write_buffer(dest, buf, 4);
+	unsigned char buf[4];
+	Write4(buf, val);
+	return write_buffer(dest, buf, 4);
 }
 
 int Glulxe::write_short(dest_t *dest, uint16 val) {
-  unsigned char buf[2];
-  Write2(buf, val);
-  return write_buffer(dest, buf, 2);
+	unsigned char buf[2];
+	Write2(buf, val);
+	return write_buffer(dest, buf, 2);
 }
 
 int Glulxe::write_byte(dest_t *dest, byte val) {
-  return write_buffer(dest, &val, 1);
+	return write_buffer(dest, &val, 1);
 }
 
 int Glulxe::read_long(dest_t *dest, uint *val) {
-  unsigned char buf[4];
-  int res = read_buffer(dest, buf, 4);
-  if (res)
-    return res;
-  *val = Read4(buf);
-  return 0;
+	unsigned char buf[4];
+	int res = read_buffer(dest, buf, 4);
+	if (res)
+		return res;
+	*val = Read4(buf);
+	return 0;
 }
 
 int Glulxe::read_short(dest_t *dest, uint16 *val) {
-  unsigned char buf[2];
-  int res = read_buffer(dest, buf, 2);
-  if (res)
-    return res;
-  *val = Read2(buf);
-  return 0;
+	unsigned char buf[2];
+	int res = read_buffer(dest, buf, 2);
+	if (res)
+		return res;
+	*val = Read2(buf);
+	return 0;
 }
 
 int Glulxe::read_byte(dest_t *dest, byte *val) {
-  return read_buffer(dest, val, 1);
+	return read_buffer(dest, val, 1);
 }
 
 uint Glulxe::write_memstate(dest_t *dest) {
-  uint res, pos;
-  int val;
-  int runlen;
-  unsigned char ch;
+	uint res, pos;
+	int val;
+	int runlen;
+	unsigned char ch;
 #ifdef SERIALIZE_CACHE_RAM
-  uint cachepos;
+	uint cachepos;
 #endif /* SERIALIZE_CACHE_RAM */
 
-  res = write_long(dest, endmem);
-  if (res)
-    return res;
+	res = write_long(dest, endmem);
+	if (res)
+		return res;
 
-  runlen = 0;
+	runlen = 0;
 
 #ifdef SERIALIZE_CACHE_RAM
-  cachepos = 0;
+	cachepos = 0;
 #else /* SERIALIZE_CACHE_RAM */
-  _gameFile.seek(gamefile_start + ramstart);
+	_gameFile.seek(gamefile_start + ramstart);
 #endif /* SERIALIZE_CACHE_RAM */
 
-  for (pos=ramstart; pos<endmem; pos++) {
-    ch = Mem1(pos);
-    if (pos < endgamefile) {
+	for (pos = ramstart; pos < endmem; pos++) {
+		ch = Mem1(pos);
+		if (pos < endgamefile) {
 #ifdef SERIALIZE_CACHE_RAM
-      val = ramcache[cachepos];
-      cachepos++;
+			val = ramcache[cachepos];
+			cachepos++;
 #else /* SERIALIZE_CACHE_RAM */
-      val = glk_get_char_stream(gamefile);
-      if (val == -1) {
-        fatal_error("The game file ended unexpectedly while saving.");
-      }
+			val = glk_get_char_stream(gamefile);
+			if (val == -1) {
+				fatal_error("The game file ended unexpectedly while saving.");
+			}
 #endif /* SERIALIZE_CACHE_RAM */
-      ch ^= (unsigned char)val;
-    }
-    if (ch == 0) {
-      runlen++;
-    }
-    else {
-      /* Write any run we've got. */
-      while (runlen) {
-        if (runlen >= 0x100)
-          val = 0x100;
-        else
-          val = runlen;
-        res = write_byte(dest, 0);
-        if (res)
-          return res;
-        res = write_byte(dest, (val-1));
-        if (res)
-          return res;
-        runlen -= val;
-      }
-      /* Write the byte we got. */
-      res = write_byte(dest, ch);
-      if (res)
-        return res;
-    }
-  }
-  /* It's possible we've got a run left over, but we don't write it. */
-
-  return 0;
+			ch ^= (unsigned char)val;
+		}
+		if (ch == 0) {
+			runlen++;
+		} else {
+			/* Write any run we've got. */
+			while (runlen) {
+				if (runlen >= 0x100)
+					val = 0x100;
+				else
+					val = runlen;
+				res = write_byte(dest, 0);
+				if (res)
+					return res;
+				res = write_byte(dest, (val - 1));
+				if (res)
+					return res;
+				runlen -= val;
+			}
+			/* Write the byte we got. */
+			res = write_byte(dest, ch);
+			if (res)
+				return res;
+		}
+	}
+	/* It's possible we've got a run left over, but we don't write it. */
+
+	return 0;
 }
 
 uint Glulxe::read_memstate(dest_t *dest, uint chunklen) {
-  uint chunkend = dest->pos + chunklen;
-  uint newlen;
-  uint res, pos;
-  int val;
-  int runlen;
-  unsigned char ch, ch2;
+	uint chunkend = dest->pos + chunklen;
+	uint newlen;
+	uint res, pos;
+	int val;
+	int runlen;
+	unsigned char ch, ch2;
 #ifdef SERIALIZE_CACHE_RAM
-  uint cachepos;
+	uint cachepos;
 #endif /* SERIALIZE_CACHE_RAM */
 
-  heap_clear();
+	heap_clear();
 
-  res = read_long(dest, &newlen);
-  if (res)
-    return res;
+	res = read_long(dest, &newlen);
+	if (res)
+		return res;
 
-  res = change_memsize(newlen, false);
-  if (res)
-    return res;
+	res = change_memsize(newlen, false);
+	if (res)
+		return res;
 
-  runlen = 0;
+	runlen = 0;
 
 #ifdef SERIALIZE_CACHE_RAM
-  cachepos = 0;
+	cachepos = 0;
 #else /* SERIALIZE_CACHE_RAM */
-  _gameFile.seek(gamefile_start + ramstart);
+	_gameFile.seek(gamefile_start + ramstart);
 #endif /* SERIALIZE_CACHE_RAM */
 
-  for (pos=ramstart; pos<endmem; pos++) {
-    if (pos < endgamefile) {
+	for (pos = ramstart; pos < endmem; pos++) {
+		if (pos < endgamefile) {
 #ifdef SERIALIZE_CACHE_RAM
-      val = ramcache[cachepos];
-      cachepos++;
+			val = ramcache[cachepos];
+			cachepos++;
 #else /* SERIALIZE_CACHE_RAM */


Commit: 4ff974778cdd81b6f6b528a1a96667764b12f70e
    https://github.com/scummvm/scummvm/commit/4ff974778cdd81b6f6b528a1a96667764b12f70e
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-04-17T20:46:07-07:00

Commit Message:
GLK: GLULXE: Fix reading game header information

Changed paths:
    engines/glk/glulxe/glulxe.cpp
    engines/glk/glulxe/vm.cpp


diff --git a/engines/glk/glulxe/glulxe.cpp b/engines/glk/glulxe/glulxe.cpp
index a78e22c..617bcb0 100644
--- a/engines/glk/glulxe/glulxe.cpp
+++ b/engines/glk/glulxe/glulxe.cpp
@@ -105,7 +105,7 @@ bool Glulxe::is_gamefile_valid() {
 }
 
 void Glulxe::fatal_error_handler(const char *str, const char *arg, bool useVal, int val) {
-	Common::String msg = "Glulxe fatal error: ";
+	Common::String msg = Common::String::format("Glulxe fatal error: %s", str);
 
 	if (arg || useVal) {
 		msg += " (";
@@ -124,7 +124,7 @@ void Glulxe::fatal_error_handler(const char *str, const char *arg, bool useVal,
 }
 
 void Glulxe::nonfatal_warning_handler(const char *str, const char *arg, bool useVal, int val) {
-	Common::String msg = "Glulxe warning: ";
+	Common::String msg = Common::String::format("Glulxe warning: %s", str);
 
 	if (arg || useVal) {
 		msg += " (";
diff --git a/engines/glk/glulxe/vm.cpp b/engines/glk/glulxe/vm.cpp
index 44ea106..c4d90ed 100644
--- a/engines/glk/glulxe/vm.cpp
+++ b/engines/glk/glulxe/vm.cpp
@@ -35,7 +35,7 @@ void Glulxe::setup_vm() {
 	stream_char_handler = nullptr;
 	stream_unichar_handler = nullptr;
 
-	_gameFile.seek(0);
+	_gameFile.seek(gamefile_start + 8);
 	if (_gameFile.read(buf, 4 * 7) != (4 * 7))
 		fatal_error("The game file header is too short.");
 





More information about the Scummvm-git-logs mailing list