aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Gilbert2019-04-14 18:43:30 -0700
committerPaul Gilbert2019-04-17 20:46:06 -0700
commit8e11a14939365c3a2994602b5db11a4fe6e8eaac (patch)
treea0bc1be59cf578317f3a0bd0d809d1b194388a2f
parente271cdc6534763f4472c3ff70c22292268300814 (diff)
downloadscummvm-rg350-8e11a14939365c3a2994602b5db11a4fe6e8eaac.tar.gz
scummvm-rg350-8e11a14939365c3a2994602b5db11a4fe6e8eaac.tar.bz2
scummvm-rg350-8e11a14939365c3a2994602b5db11a4fe6e8eaac.zip
GLK: GLULXE: Set up method definitions, glkop methods
-rw-r--r--engines/glk/glk_api.cpp3
-rw-r--r--engines/glk/glk_api.h12
-rw-r--r--engines/glk/glk_dispa.cpp58
-rw-r--r--engines/glk/glk_types.h2
-rw-r--r--engines/glk/glulxe/glkop.cpp1353
-rw-r--r--engines/glk/glulxe/glkop.h36
-rw-r--r--engines/glk/glulxe/glulxe.cpp46
-rw-r--r--engines/glk/glulxe/glulxe.h453
-rw-r--r--engines/glk/glulxe/glulxe_types.h187
-rw-r--r--engines/glk/sound.cpp2
-rw-r--r--engines/glk/sound.h1
-rw-r--r--engines/glk/streams.cpp6
-rw-r--r--engines/glk/streams.h1
13 files changed, 2115 insertions, 45 deletions
diff --git a/engines/glk/glk_api.cpp b/engines/glk/glk_api.cpp
index 61c7a55aea..e2f3117ef7 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 9e4a230cd9..e54cd3c042 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 38b03e5b42..dfb638fa0a 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 3c4b4fb675..7095334344 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 af650a3add..ee7f78bf42 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 ed9d716764..0000000000
--- 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 40f4bdde57..bb1a65101c 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 decdc298f7..b1f44535e8 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 0000000000..52a6363833
--- /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 7a93b772f9..70c33c27da 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 337239b761..251c75a2fe 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 0fd98018b9..84904f9a70 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 a17ee28cbb..c254a89a28 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;