[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