Implement a Makefile for Inform.
[inform.git] / src / expressc.c
diff --git a/src/expressc.c b/src/expressc.c
new file mode 100644 (file)
index 0000000..e6db119
--- /dev/null
@@ -0,0 +1,2920 @@
+/* ------------------------------------------------------------------------- */
+/*   "expressc" :  The expression code generator                             */
+/*                                                                           */
+/* Copyright (c) Graham Nelson 1993 - 2018                                   */
+/*                                                                           */
+/* This file is part of Inform.                                              */
+/*                                                                           */
+/* Inform 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 3 of the License, or         */
+/* (at your option) any later version.                                       */
+/*                                                                           */
+/* Inform 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 Inform. If not, see https://gnu.org/licenses/                  */
+/*                                                                           */
+/* ------------------------------------------------------------------------- */
+
+#include "header.h"
+
+int vivc_flag;                      /*  TRUE if the last code-generated
+                                        expression produced a "value in void
+                                        context" error: used to help the syntax
+                                        analyser recover from unknown-keyword
+                                        errors, since unknown keywords are
+                                        treated as yet-to-be-defined constants
+                                        and thus as values in void context  */
+
+/* These data structures are global, because they're too useful to be
+   static. */
+assembly_operand stack_pointer, temp_var1, temp_var2, temp_var3,
+  temp_var4, zero_operand, one_operand, two_operand, three_operand,
+  four_operand, valueless_operand;
+
+static void make_operands(void)
+{
+  if (!glulx_mode) {
+    INITAOTV(&stack_pointer, VARIABLE_OT, 0);
+    INITAOTV(&temp_var1, VARIABLE_OT, 255);
+    INITAOTV(&temp_var2, VARIABLE_OT, 254);
+    INITAOTV(&temp_var3, VARIABLE_OT, 253);
+    INITAOTV(&temp_var4, VARIABLE_OT, 252);
+    INITAOTV(&zero_operand, SHORT_CONSTANT_OT, 0);
+    INITAOTV(&one_operand, SHORT_CONSTANT_OT, 1);
+    INITAOTV(&two_operand, SHORT_CONSTANT_OT, 2);
+    INITAOTV(&three_operand, SHORT_CONSTANT_OT, 3);
+    INITAOTV(&four_operand, SHORT_CONSTANT_OT, 4);
+    INITAOTV(&valueless_operand, OMITTED_OT, 0);
+  }
+  else {
+    INITAOTV(&stack_pointer, LOCALVAR_OT, 0);
+    INITAOTV(&temp_var1, GLOBALVAR_OT, MAX_LOCAL_VARIABLES+0);
+    INITAOTV(&temp_var2, GLOBALVAR_OT, MAX_LOCAL_VARIABLES+1);
+    INITAOTV(&temp_var3, GLOBALVAR_OT, MAX_LOCAL_VARIABLES+2);
+    INITAOTV(&temp_var4, GLOBALVAR_OT, MAX_LOCAL_VARIABLES+3);
+    INITAOTV(&zero_operand, ZEROCONSTANT_OT, 0);
+    INITAOTV(&one_operand, BYTECONSTANT_OT, 1);
+    INITAOTV(&two_operand, BYTECONSTANT_OT, 2);
+    INITAOTV(&three_operand, BYTECONSTANT_OT, 3);
+    INITAOTV(&four_operand, BYTECONSTANT_OT, 4);
+    INITAOTV(&valueless_operand, OMITTED_OT, 0);
+  }
+}
+
+/* ------------------------------------------------------------------------- */
+/*  The table of conditionals. (Only used in Glulx)                          */
+
+#define ZERO_CC (500)
+#define EQUAL_CC (502)
+#define LT_CC (504)
+#define GT_CC (506)
+#define HAS_CC (508)
+#define IN_CC (510)
+#define OFCLASS_CC (512)
+#define PROVIDES_CC (514)
+
+#define FIRST_CC (500)
+#define LAST_CC (515)
+
+typedef struct condclass_s {
+  int32 posform; /* Opcode for the conditional in its positive form. */
+  int32 negform; /* Opcode for the conditional in its negated form. */
+} condclass;
+
+condclass condclasses[] = {
+  { jz_gc, jnz_gc },
+  { jeq_gc, jne_gc },
+  { jlt_gc, jge_gc },
+  { jgt_gc, jle_gc },
+  { -1, -1 },
+  { -1, -1 },
+  { -1, -1 },
+  { -1, -1 }
+};
+
+/* ------------------------------------------------------------------------- */
+/*  The table of operators.
+
+    The ordering in this table is not significant except that it must match
+    the #define's in "header.h"                                              */
+
+operator operators[NUM_OPERATORS] =
+{
+                         /* ------------------------ */
+                         /*  Level 0:  ,             */
+                         /* ------------------------ */
+
+  { 0, SEP_TT, COMMA_SEP,       IN_U, L_A, 0, -1, -1, 0, 0, "comma" },
+
+                         /* ------------------------ */
+                         /*  Level 1:  =             */
+                         /* ------------------------ */
+
+  { 1, SEP_TT, SETEQUALS_SEP,   IN_U, R_A, 1, -1, -1, 1, 0,
+      "assignment operator '='" },
+
+                         /* ------------------------ */
+                         /*  Level 2:  ~~  &&  ||    */
+                         /* ------------------------ */
+
+  { 2, SEP_TT, LOGAND_SEP,      IN_U, L_A, 0, -1, -1, 0, LOGOR_OP,
+      "logical conjunction '&&'" },
+  { 2, SEP_TT, LOGOR_SEP,       IN_U, L_A, 0, -1, -1, 0, LOGAND_OP,
+      "logical disjunction '||'" },
+  { 2, SEP_TT, LOGNOT_SEP,     PRE_U, R_A, 0, -1, -1, 0, LOGNOT_OP,
+      "logical negation '~~'" },
+
+                         /* ------------------------ */
+                         /*  Level 3:  ==  ~=        */
+                         /*            >  >=  <  <=  */
+                         /*            has  hasnt    */
+                         /*            in  notin     */
+                         /*            provides      */
+                         /*            ofclass       */
+                         /* ------------------------ */
+
+  { 3,     -1, -1,                -1, 0, 0, 400 + jz_zc, ZERO_CC+0, 0, NONZERO_OP,
+      "expression used as condition then negated" },
+  { 3,     -1, -1,                -1, 0, 0, 800 + jz_zc, ZERO_CC+1, 0, ZERO_OP,
+      "expression used as condition" },
+  { 3, SEP_TT, CONDEQUALS_SEP,  IN_U, 0, 0, 400 + je_zc, EQUAL_CC+0, 0, NOTEQUAL_OP,
+      "'==' condition" },
+  { 3, SEP_TT, NOTEQUAL_SEP,    IN_U, 0, 0, 800 + je_zc, EQUAL_CC+1, 0, CONDEQUALS_OP,
+      "'~=' condition" },
+  { 3, SEP_TT, GE_SEP,          IN_U, 0, 0, 800 + jl_zc, LT_CC+1, 0, LESS_OP,
+      "'>=' condition" },
+  { 3, SEP_TT, GREATER_SEP,     IN_U, 0, 0, 400 + jg_zc, GT_CC+0, 0, LE_OP,
+      "'>' condition" },
+  { 3, SEP_TT, LE_SEP,          IN_U, 0, 0, 800 + jg_zc, GT_CC+1, 0, GREATER_OP,
+      "'<=' condition" },
+  { 3, SEP_TT, LESS_SEP,        IN_U, 0, 0, 400 + jl_zc, LT_CC+0, 0, GE_OP,
+      "'<' condition" },
+  { 3, CND_TT, HAS_COND,        IN_U, 0, 0, 400 + test_attr_zc, HAS_CC+0, 0, HASNT_OP,
+      "'has' condition" },
+  { 3, CND_TT, HASNT_COND,      IN_U, 0, 0, 800 + test_attr_zc, HAS_CC+1, 0, HAS_OP,
+      "'hasnt' condition" },
+  { 3, CND_TT, IN_COND,         IN_U, 0, 0, 400 + jin_zc, IN_CC+0, 0, NOTIN_OP,
+      "'in' condition" },
+  { 3, CND_TT, NOTIN_COND,      IN_U, 0, 0, 800 + jin_zc, IN_CC+1, 0, IN_OP,
+      "'notin' condition" },
+  { 3, CND_TT, OFCLASS_COND,    IN_U, 0, 0, 600, OFCLASS_CC+0, 0, NOTOFCLASS_OP,
+      "'ofclass' condition" },
+  { 3, CND_TT, PROVIDES_COND,   IN_U, 0, 0, 601, PROVIDES_CC+0, 0, NOTPROVIDES_OP,
+      "'provides' condition" },
+  { 3,     -1, -1,                -1, 0, 0, 1000, OFCLASS_CC+1, 0, OFCLASS_OP,
+      "negated 'ofclass' condition" },
+  { 3,     -1, -1,                -1, 0, 0, 1001, PROVIDES_CC+1, 0, PROVIDES_OP,
+      "negated 'provides' condition" },
+
+                         /* ------------------------ */
+                         /*  Level 4:  or            */
+                         /* ------------------------ */
+
+  { 4, CND_TT, OR_COND,         IN_U, L_A, 0, -1, -1, 0, 0, "'or'" },
+
+                         /* ------------------------ */
+                         /*  Level 5:  +  binary -   */
+                         /* ------------------------ */
+
+  { 5, SEP_TT, PLUS_SEP,        IN_U, L_A, 0, add_zc, add_gc, 0, 0, "'+'" },
+  { 5, SEP_TT, MINUS_SEP,       IN_U, L_A, 0, sub_zc, sub_gc, 0, 0, "'-'" },
+
+                         /* ------------------------ */
+                         /*  Level 6:  *  /  %       */
+                         /*            &  |  ~       */
+                         /* ------------------------ */
+
+  { 6, SEP_TT, TIMES_SEP,       IN_U, L_A, 0, mul_zc, mul_gc, 0, 0, "'*'" },
+  { 6, SEP_TT, DIVIDE_SEP,      IN_U, L_A, 0, div_zc, div_gc, 0, 0, "'/'" },
+  { 6, SEP_TT, REMAINDER_SEP,   IN_U, L_A, 0, mod_zc, mod_gc, 0, 0,
+      "remainder after division '%'" },
+  { 6, SEP_TT, ARTAND_SEP,      IN_U, L_A, 0, and_zc, bitand_gc, 0, 0,
+      "bitwise AND '&'" },
+  { 6, SEP_TT, ARTOR_SEP,       IN_U, L_A, 0, or_zc, bitor_gc, 0, 0,
+      "bitwise OR '|'" },
+  { 6, SEP_TT, ARTNOT_SEP,     PRE_U, R_A, 0, -1, bitnot_gc, 0, 0,
+      "bitwise NOT '~'" },
+
+                         /* ------------------------ */
+                         /*  Level 7:  ->  -->       */
+                         /* ------------------------ */
+
+  { 7, SEP_TT, ARROW_SEP,       IN_U, L_A, 0, -1, -1, 0, 0,
+      "byte array operator '->'" },
+  { 7, SEP_TT, DARROW_SEP,      IN_U, L_A, 0, -1, -1, 0, 0,
+      "word array operator '-->'" },
+
+                         /* ------------------------ */
+                         /*  Level 8:  unary -       */
+                         /* ------------------------ */
+
+  { 8, SEP_TT, UNARY_MINUS_SEP, PRE_U, R_A, 0, -1, neg_gc, 0, 0,
+      "unary minus" },
+
+                         /* ------------------------ */
+                         /*  Level 9:  ++  --        */
+                         /*  (prefix or postfix)     */
+                         /* ------------------------ */
+
+  { 9, SEP_TT, INC_SEP,         PRE_U, R_A, 2, -1, -1, 1, 0,
+      "pre-increment operator '++'" },
+  { 9, SEP_TT, POST_INC_SEP,   POST_U, R_A, 3, -1, -1, 1, 0,
+      "post-increment operator '++'" },
+  { 9, SEP_TT, DEC_SEP,         PRE_U, R_A, 4, -1, -1, 1, 0,
+      "pre-decrement operator '--'" },
+  { 9, SEP_TT, POST_DEC_SEP,   POST_U, R_A, 5, -1, -1, 1, 0,
+      "post-decrement operator '--'" },
+
+                         /* ------------------------ */
+                         /*  Level 10: .&  .#        */
+                         /*            ..&  ..#      */
+                         /* ------------------------ */
+
+  {10, SEP_TT, PROPADD_SEP,     IN_U, L_A, 0, -1, -1, 0, 0,
+      "property address operator '.&'" },
+  {10, SEP_TT, PROPNUM_SEP,     IN_U, L_A, 0, -1, -1, 0, 0,
+      "property length operator '.#'" },
+  {10, SEP_TT, MPROPADD_SEP,    IN_U, L_A, 0, -1, -1, 0, 0,
+      "individual property address operator '..&'" },
+  {10, SEP_TT, MPROPNUM_SEP,    IN_U, L_A, 0, -1, -1, 0, 0,
+      "individual property length operator '..#'" },
+
+                         /* ------------------------ */
+                         /*  Level 11:  function (   */
+                         /* ------------------------ */
+
+  {11, SEP_TT, OPENB_SEP,       IN_U, L_A, 0, -1, -1, 1, 0,
+      "function call" },
+
+                         /* ------------------------ */
+                         /*  Level 12:  .  ..        */
+                         /* ------------------------ */
+
+  {12, SEP_TT, MESSAGE_SEP,     IN_U, L_A, 0, -1, -1, 0, 0,
+      "individual property selector '..'" },
+  {12, SEP_TT, PROPERTY_SEP,    IN_U, L_A, 0, -1, -1, 0, 0,
+      "property selector '.'" },
+
+                         /* ------------------------ */
+                         /*  Level 13:  ::           */
+                         /* ------------------------ */
+
+  {13, SEP_TT, SUPERCLASS_SEP,  IN_U, L_A, 0, -1, -1, 0, 0,
+      "superclass operator '::'" },
+
+                         /* ------------------------ */
+                         /*  Miscellaneous operators */
+                         /*  generated at lvalue     */
+                         /*  checking time           */
+                         /* ------------------------ */
+
+  { 1,     -1, -1,              -1,   -1,  0, -1, -1, 1, 0, /*      ->   =   */
+      "byte array entry assignment" },
+  { 1,     -1, -1,              -1,   -1,  0, -1, -1, 1, 0, /*      -->  =   */
+      "word array entry assignment" },
+  { 1,     -1, -1,              -1,   -1,  0, -1, -1, 1, 0, /*      ..   =   */
+      "individual property assignment" },
+  { 1,     -1, -1,              -1,   -1,  0, -1, -1, 1, 0, /*      .    =   */
+      "common property assignment" },
+
+  { 9,     -1, -1,              -1,   -1,  0, -1, -1, 1, 0, /*   ++ ->       */
+      "byte array entry preincrement" },
+  { 9,     -1, -1,              -1,   -1,  0, -1, -1, 1, 0, /*   ++ -->      */
+      "word array entry preincrement" },
+  { 9,     -1, -1,              -1,   -1,  0, -1, -1, 1, 0, /*   ++ ..       */
+      "individual property preincrement" },
+  { 9,     -1, -1,              -1,   -1,  0, -1, -1, 1, 0, /*   ++ .        */
+      "common property preincrement" },
+
+  { 9,     -1, -1,              -1,   -1,  0, -1, -1, 1, 0, /*   -- ->       */
+      "byte array entry predecrement" },
+  { 9,     -1, -1,              -1,   -1,  0, -1, -1, 1, 0, /*   -- -->      */
+      "word array entry predecrement" },
+  { 9,     -1, -1,              -1,   -1,  0, -1, -1, 1, 0, /*   -- ..       */
+      "individual property predecrement" },
+  { 9,     -1, -1,              -1,   -1,  0, -1, -1, 1, 0, /*   -- .        */
+      "common property predecrement" },
+
+  { 9,     -1, -1,              -1,   -1,  0, -1, -1, 1, 0, /*      ->  ++   */
+      "byte array entry postincrement" },
+  { 9,     -1, -1,              -1,   -1,  0, -1, -1, 1, 0, /*      --> ++   */
+      "word array entry postincrement" },
+  { 9,     -1, -1,              -1,   -1,  0, -1, -1, 1, 0, /*      ..  ++   */
+      "individual property postincrement" },
+  { 9,     -1, -1,              -1,   -1,  0, -1, -1, 1, 0, /*      .   ++   */
+      "common property postincrement" },
+
+  { 9,     -1, -1,              -1,   -1,  0, -1, -1, 1, 0, /*      ->  --   */
+      "byte array entry postdecrement" },
+  { 9,     -1, -1,              -1,   -1,  0, -1, -1, 1, 0, /*      --> --   */
+      "word array entry postdecrement" },
+  { 9,     -1, -1,              -1,   -1,  0, -1, -1, 1, 0, /*      ..  --   */
+      "individual property postdecrement" },
+  { 9,     -1, -1,              -1,   -1,  0, -1, -1, 1, 0, /*      .   --   */
+      "common property postdecrement" },
+
+  {11,     -1, -1,              -1,   -1,  0, -1, -1, 1, 0, /*   x.y(args)   */
+      "call to common property" },
+  {11,     -1, -1,              -1,   -1,  0, -1, -1, 1, 0, /*   x..y(args)  */
+      "call to individual property" },
+
+                         /* ------------------------ */
+                         /*  And one Glulx-only op   */
+                         /*  which just pushes its   */
+                         /*  argument on the stack,  */
+                         /*  unchanged.              */
+                         /* ------------------------ */
+
+  {14,     -1, -1,              -1,   -1,  0, -1, -1, 1, 0,     
+      "push on stack" }
+};
+
+/* --- Condition annotater ------------------------------------------------- */
+
+static void annotate_for_conditions(int n, int a, int b)
+{   int i, opnum = ET[n].operator_number;
+
+    ET[n].label_after = -1;
+    ET[n].to_expression = FALSE;
+    ET[n].true_label = a;
+    ET[n].false_label = b;
+
+    if (ET[n].down == -1) return;
+
+    if ((operators[opnum].precedence == 2)
+        || (operators[opnum].precedence == 3))
+    {   if ((a == -1) && (b == -1))
+        {   if (opnum == LOGAND_OP)
+            {   b = next_label++;
+                ET[n].false_label = b;
+                ET[n].to_expression = TRUE;
+            }
+            else
+            {   a = next_label++;
+                ET[n].true_label = a;
+                ET[n].to_expression = TRUE;
+            }
+        }
+    }
+
+    switch(opnum)
+    {   case LOGAND_OP:
+            if (b == -1)
+            {   b = next_label++;
+                ET[n].false_label = b;
+                ET[n].label_after = b;
+            }
+            annotate_for_conditions(ET[n].down, -1, b);
+            if (b == ET[n].label_after)
+                 annotate_for_conditions(ET[ET[n].down].right, a, -1);
+            else annotate_for_conditions(ET[ET[n].down].right, a, b);
+            return;
+        case LOGOR_OP:
+            if (a == -1)
+            {   a = next_label++;
+                ET[n].true_label = a;
+                ET[n].label_after = a;
+            }
+            annotate_for_conditions(ET[n].down, a, -1);
+            if (a == ET[n].label_after)
+                 annotate_for_conditions(ET[ET[n].down].right, -1, b);
+            else annotate_for_conditions(ET[ET[n].down].right, a, b);
+            return;
+    }
+
+    i = ET[n].down;
+    while (i != -1)
+    {   annotate_for_conditions(i, -1, -1); i = ET[i].right; }
+}
+
+/* --- Code generator ------------------------------------------------------ */
+
+static void value_in_void_context_z(assembly_operand AO)
+{   char *t;
+
+    ASSERT_ZCODE(); 
+    switch(AO.type)
+    {   case LONG_CONSTANT_OT:
+        case SHORT_CONSTANT_OT:
+            t = "<constant>";
+            if (AO.marker == SYMBOL_MV)
+                t = (char *) (symbs[AO.value]);
+            break;
+        case VARIABLE_OT:
+            t = variable_name(AO.value);
+            break;
+        default:
+            compiler_error("Unable to print value in void context");
+            t = "<expression>";
+            break;
+    }
+    vivc_flag = TRUE;
+
+    if (strcmp(t, "print_paddr") == 0)
+    obsolete_warning("ignoring 'print_paddr': use 'print (string)' instead");
+    else
+    if (strcmp(t, "print_addr") == 0)
+    obsolete_warning("ignoring 'print_addr': use 'print (address)' instead");
+    else
+    if (strcmp(t, "print_char") == 0)
+    obsolete_warning("ignoring 'print_char': use 'print (char)' instead");
+    else
+    ebf_error("expression with side-effects", t);
+}
+
+static void write_result_z(assembly_operand to, assembly_operand from)
+{   if (to.value == from.value) return;
+    if (to.value == 0) assemblez_1(push_zc, from);
+    else               assemblez_store(to, from);
+}
+
+static void pop_zm_stack(void)
+{   assembly_operand st;
+    if (version_number < 5) assemblez_0(pop_zc);
+    else
+    {   INITAOTV(&st, VARIABLE_OT, 0);
+        assemblez_1_branch(jz_zc, st, -2, TRUE);
+    }
+}
+
+static void access_memory_z(int oc, assembly_operand AO1, assembly_operand AO2,
+    assembly_operand AO3)
+{   int vr = 0;
+
+    assembly_operand zero_ao, max_ao, size_ao, en_ao, type_ao, an_ao,
+        index_ao;
+    int x = 0, y = 0, byte_flag = FALSE, read_flag = FALSE, from_module = FALSE;
+
+    if (AO1.marker == ARRAY_MV)
+    {   
+        INITAO(&zero_ao);
+
+        if ((oc == loadb_zc) || (oc == storeb_zc)) byte_flag=TRUE;
+        else byte_flag = FALSE;
+        if ((oc == loadb_zc) || (oc == loadw_zc)) read_flag=TRUE;
+        else read_flag = FALSE;
+
+        zero_ao.type = SHORT_CONSTANT_OT;
+        zero_ao.value = 0;
+
+        size_ao = zero_ao; size_ao.value = -1;
+        for (x=0; x<no_arrays; x++)
+        {   if (AO1.value == svals[array_symbols[x]])
+            {   size_ao.value = array_sizes[x]; y=x;
+            }
+        }
+        if (size_ao.value==-1) 
+            from_module=TRUE;
+        else {
+            from_module=FALSE;
+            type_ao = zero_ao; type_ao.value = array_types[y];
+
+            if ((!is_systemfile()))
+            {   if (byte_flag)
+                {
+                    if ((array_types[y] == WORD_ARRAY)
+                        || (array_types[y] == TABLE_ARRAY))
+                        warning("Using '->' to access a --> or table array");
+                }
+                else
+                {
+                    if ((array_types[y] == BYTE_ARRAY)
+                        || (array_types[y] == STRING_ARRAY))
+                    warning("Using '-->' to access a -> or string array");
+                }
+            }
+        }
+    }
+
+
+    if ((!runtime_error_checking_switch) || (veneer_mode))
+    {   if ((oc == loadb_zc) || (oc == loadw_zc))
+            assemblez_2_to(oc, AO1, AO2, AO3);
+        else
+            assemblez_3(oc, AO1, AO2, AO3);
+        return;
+    }
+
+    /* If we recognise AO1 as arising textually from a declared
+       array, we can check bounds explicitly. */
+
+    if ((AO1.marker == ARRAY_MV) && (!from_module))
+    {   
+        int passed_label = next_label++, failed_label = next_label++,
+            final_label = next_label++; 
+        /* Calculate the largest permitted array entry + 1
+           Here "size_ao.value" = largest permitted entry of its own kind */
+        max_ao = size_ao;
+
+        if (byte_flag
+            && ((array_types[y] == WORD_ARRAY)
+                || (array_types[y] == TABLE_ARRAY)))
+        {   max_ao.value = size_ao.value*2 + 1;
+            type_ao.value += 8;
+        }
+        if ((!byte_flag)
+            && ((array_types[y] == BYTE_ARRAY)
+                || (array_types[y] == STRING_ARRAY) 
+                || (array_types[y] == BUFFER_ARRAY)))
+        {   if ((size_ao.value % 2) == 0)
+                 max_ao.value = size_ao.value/2 - 1;
+            else max_ao.value = (size_ao.value-1)/2;
+            type_ao.value += 16;
+        }
+        max_ao.value++;
+
+        if (size_ao.value >= 256) size_ao.type = LONG_CONSTANT_OT;
+        if (max_ao.value >= 256) max_ao.type = LONG_CONSTANT_OT;
+
+        /* Can't write to the size entry in a string or table */
+        if (((array_types[y] == STRING_ARRAY)
+             || (array_types[y] == TABLE_ARRAY))
+            && (!read_flag))
+        {   if ((array_types[y] == TABLE_ARRAY) && byte_flag)
+                zero_ao.value = 2;
+            else zero_ao.value = 1;
+        }
+
+        en_ao = zero_ao; en_ao.value = ABOUNDS_RTE;
+        switch(oc) { case loadb_zc:  en_ao.value = ABOUNDS_RTE; break;
+                     case loadw_zc:  en_ao.value = ABOUNDS_RTE+1; break;
+                     case storeb_zc: en_ao.value = ABOUNDS_RTE+2; break;
+                     case storew_zc: en_ao.value = ABOUNDS_RTE+3; break; }
+
+        index_ao = AO2;
+        if ((AO2.type == VARIABLE_OT)&&(AO2.value == 0))
+        {   assemblez_store(temp_var2, AO2);
+            assemblez_store(AO2, temp_var2);
+            index_ao = temp_var2;
+        }
+        assemblez_2_branch(jl_zc, index_ao, zero_ao, failed_label, TRUE);
+        assemblez_2_branch(jl_zc, index_ao, max_ao, passed_label, TRUE);
+        assemble_label_no(failed_label);
+        an_ao = zero_ao; an_ao.value = y;
+        assemblez_6(call_vn2_zc, veneer_routine(RT__Err_VR), en_ao,
+            index_ao, size_ao, type_ao, an_ao);
+
+        /* We have to clear any of AO1, AO2, AO3 off the stack if
+           present, so that we can achieve the same effect on the stack
+           that executing the opcode would have had */
+
+        if ((AO1.type == VARIABLE_OT) && (AO1.value == 0)) pop_zm_stack();
+        if ((AO2.type == VARIABLE_OT) && (AO2.value == 0)) pop_zm_stack();
+        if ((AO3.type == VARIABLE_OT) && (AO3.value == 0))
+        {   if ((oc == loadb_zc) || (oc == loadw_zc))
+            {   assemblez_store(AO3, zero_ao);
+            }
+            else pop_zm_stack();
+        }
+        assemblez_jump(final_label);
+
+        assemble_label_no(passed_label);
+        if ((oc == loadb_zc) || (oc == loadw_zc))
+            assemblez_2_to(oc, AO1, AO2, AO3);
+        else
+            assemblez_3(oc, AO1, AO2, AO3);
+        assemble_label_no(final_label);
+        return;
+    }
+
+    /* Otherwise, compile a call to the veneer which verifies that
+       the proposed read/write is within dynamic Z-machine memory. */
+
+    switch(oc) { case loadb_zc: vr = RT__ChLDB_VR; break;
+                 case loadw_zc: vr = RT__ChLDW_VR; break;
+                 case storeb_zc: vr = RT__ChSTB_VR; break;
+                 case storew_zc: vr = RT__ChSTW_VR; break;
+                 default: compiler_error("unknown array opcode");
+    }
+
+    if ((oc == loadb_zc) || (oc == loadw_zc))
+        assemblez_3_to(call_vs_zc, veneer_routine(vr), AO1, AO2, AO3);
+    else
+        assemblez_4(call_vn_zc, veneer_routine(vr), AO1, AO2, AO3);
+}
+
+static assembly_operand check_nonzero_at_runtime_z(assembly_operand AO1,
+        int error_label, int rte_number)
+{   assembly_operand AO2, AO3;
+    int check_sp = FALSE, passed_label, failed_label, last_label;
+    if (veneer_mode) return AO1;
+
+    /*  Assemble to code to check that the operand AO1 is ofclass Object:
+        if it is, execution should continue and the stack should be
+        unchanged.  Otherwise, call the veneer's run-time-error routine
+        with the given error number, and then: if the label isn't -1,
+        switch execution to this label, with the value popped from
+        the stack if it was on the stack in the first place;
+        if the label is -1, either replace the top of the stack with
+        the constant 2, or return the operand (short constant) 2.
+
+        The point of 2 is that object 2 is the class-object Object
+        and therefore has no parent, child or sibling, so that the
+        built-in tree functions will safely return 0 on this object. */
+
+    /*  Sometimes we can already see that the object number is valid. */
+    if (((AO1.type == LONG_CONSTANT_OT) || (AO1.type == SHORT_CONSTANT_OT))
+        && (AO1.marker == 0) && (AO1.value >= 1) && (AO1.value < no_objects))
+        return AO1;
+
+    passed_label = next_label++;
+    failed_label = next_label++;
+    INITAOTV(&AO2, LONG_CONSTANT_OT, actual_largest_object_SC);
+    AO2.marker = INCON_MV;
+    INITAOTV(&AO3, SHORT_CONSTANT_OT, 5);
+
+    if ((rte_number == IN_RTE) || (rte_number == HAS_RTE)
+        || (rte_number == PROPERTY_RTE) || (rte_number == PROP_NUM_RTE)
+        || (rte_number == PROP_ADD_RTE))
+    {   /* Allow classes */
+        AO3.value = 1;
+        if ((AO1.type == VARIABLE_OT) && (AO1.value == 0))
+        {   /* That is, if AO1 is the stack pointer */
+            check_sp = TRUE;
+            assemblez_store(temp_var2, AO1);
+            assemblez_store(AO1, temp_var2);
+            assemblez_2_branch(jg_zc, AO3, temp_var2, failed_label, TRUE);
+            assemblez_2_branch(jg_zc, temp_var2, AO2, passed_label, FALSE);
+        }
+        else
+        {   assemblez_2_branch(jg_zc, AO3, AO1, failed_label, TRUE);
+            assemblez_2_branch(jg_zc, AO1, AO2, passed_label, FALSE);
+        }
+    }
+    else
+    {   if ((AO1.type == VARIABLE_OT) && (AO1.value == 0))
+        {   /* That is, if AO1 is the stack pointer */
+            check_sp = TRUE;
+            assemblez_store(temp_var2, AO1);
+            assemblez_store(AO1, temp_var2);
+            assemblez_2_branch(jg_zc, AO3, temp_var2, failed_label, TRUE);
+            assemblez_2_branch(jg_zc, temp_var2, AO2, failed_label, TRUE);
+            AO3.value = 1;
+            assemblez_2_branch(jin_zc, temp_var2, AO3, passed_label, FALSE);
+        }
+        else
+        {   assemblez_2_branch(jg_zc, AO3, AO1, failed_label, TRUE);
+            assemblez_2_branch(jg_zc, AO1, AO2, failed_label, TRUE);
+            AO3.value = 1;
+            assemblez_2_branch(jin_zc, AO1, AO3, passed_label, FALSE);
+        }
+    }
+
+    assemble_label_no(failed_label);
+    INITAOTV(&AO2, SHORT_CONSTANT_OT, rte_number);
+    if (version_number >= 5)
+      assemblez_3(call_vn_zc, veneer_routine(RT__Err_VR), AO2, AO1);
+    else
+      assemblez_3_to(call_zc, veneer_routine(RT__Err_VR), AO2, AO1, temp_var2);
+
+    if (error_label != -1)
+    {   /* Jump to the error label */
+        if (error_label == -3) assemblez_0(rfalse_zc);
+        else if (error_label == -4) assemblez_0(rtrue_zc);
+        else assemblez_jump(error_label);
+    }
+    else
+    {   if (check_sp)
+        {   /* Push the short constant 2 */
+            INITAOTV(&AO2, SHORT_CONSTANT_OT, 2);
+            assemblez_store(AO1, AO2);
+        }
+        else
+        {   /* Store either short constant 2 or the operand's value in
+               the temporary variable */
+            INITAOTV(&AO2, SHORT_CONSTANT_OT, 2);
+            AO3 = temp_var2; assemblez_store(AO3, AO2);
+            last_label = next_label++;
+            assemblez_jump(last_label);
+            assemble_label_no(passed_label);
+            assemblez_store(AO3, AO1);
+            assemble_label_no(last_label);
+            return AO3;
+        }
+    }
+    assemble_label_no(passed_label);
+    return AO1;
+}
+
+static void compile_conditional_z(int oc,
+    assembly_operand AO1, assembly_operand AO2, int label, int flag)
+{   assembly_operand AO3; int the_zc, error_label = label,
+    va_flag = FALSE, va_label = 0;
+
+    ASSERT_ZCODE(); 
+
+    if (oc<200)
+    {   if ((runtime_error_checking_switch) && (oc == jin_zc))
+        {   if (flag) error_label = next_label++;
+            AO1 = check_nonzero_at_runtime(AO1, error_label, IN_RTE);
+        }
+        if ((runtime_error_checking_switch) && (oc == test_attr_zc))
+        {   if (flag) error_label = next_label++;
+            AO1 = check_nonzero_at_runtime(AO1, error_label, HAS_RTE);
+            switch(AO2.type)
+            {   case SHORT_CONSTANT_OT:
+                case LONG_CONSTANT_OT:
+                    if (AO2.marker == 0)
+                    {   if ((AO2.value < 0) || (AO2.value > 47))
+                error("'has'/'hasnt' applied to illegal attribute number");
+                        break;
+                    }
+                case VARIABLE_OT:
+                {   int pa_label = next_label++, fa_label = next_label++;
+                    assembly_operand en_ao, zero_ao, max_ao;
+                    assemblez_store(temp_var1, AO1);
+                    if ((AO1.type == VARIABLE_OT)&&(AO1.value == 0))
+                        assemblez_store(AO1, temp_var1);
+                    assemblez_store(temp_var2, AO2);
+                    if ((AO2.type == VARIABLE_OT)&&(AO2.value == 0))
+                        assemblez_store(AO2, temp_var2);
+                    INITAOT(&zero_ao, SHORT_CONSTANT_OT);
+                    zero_ao.value = 0; 
+                    max_ao = zero_ao; max_ao.value = 48;
+                    assemblez_2_branch(jl_zc,temp_var2,zero_ao,fa_label,TRUE);
+                    assemblez_2_branch(jl_zc,temp_var2,max_ao,pa_label,TRUE);
+                    assemble_label_no(fa_label);
+                    en_ao = zero_ao; en_ao.value = 19;
+                    assemblez_4(call_vn_zc, veneer_routine(RT__Err_VR),
+                        en_ao, temp_var1, temp_var2);
+                    va_flag = TRUE; va_label = next_label++;
+                    assemblez_jump(va_label);
+                    assemble_label_no(pa_label);
+                }
+            }
+        }
+        assemblez_2_branch(oc, AO1, AO2, label, flag);
+        if (error_label != label) assemble_label_no(error_label);
+        if (va_flag) assemble_label_no(va_label);
+        return;
+    }
+
+    INITAOTV(&AO3, VARIABLE_OT, 0);
+
+    the_zc = (version_number == 3)?call_zc:call_vs_zc;
+    if (oc == 201)
+    assemblez_3_to(the_zc, veneer_routine(OP__Pr_VR), AO1, AO2, AO3);
+    else
+    assemblez_3_to(the_zc, veneer_routine(OC__Cl_VR), AO1, AO2, AO3);
+
+    assemblez_1_branch(jz_zc, AO3, label, !flag);
+}
+
+static void value_in_void_context_g(assembly_operand AO)
+{   char *t;
+
+    ASSERT_GLULX(); 
+
+    switch(AO.type)
+    {   case CONSTANT_OT:
+        case HALFCONSTANT_OT:
+        case BYTECONSTANT_OT:
+        case ZEROCONSTANT_OT:
+            t = "<constant>";
+            if (AO.marker == SYMBOL_MV)
+                t = (char *) (symbs[AO.value]);
+            break;
+        case GLOBALVAR_OT:
+        case LOCALVAR_OT:
+            t = variable_name(AO.value);
+            break;
+        default:
+            compiler_error("Unable to print value in void context");
+            t = "<expression>";
+            break;
+    }
+    vivc_flag = TRUE;
+
+    ebf_error("expression with side-effects", t);
+}
+
+static void write_result_g(assembly_operand to, assembly_operand from)
+{   if (to.value == from.value && to.type == from.type) return;
+    assembleg_store(to, from);
+}
+
+static void access_memory_g(int oc, assembly_operand AO1, assembly_operand AO2,
+    assembly_operand AO3)
+{   int vr = 0;
+    int data_len, read_flag; 
+    assembly_operand zero_ao, max_ao, size_ao, en_ao, type_ao, an_ao,
+        index_ao, five_ao;
+    int passed_label, failed_label, final_label, x = 0, y = 0;
+
+    if ((oc == aloadb_gc) || (oc == astoreb_gc)) data_len = 1;
+    else if ((oc == aloads_gc) || (oc == astores_gc)) data_len = 2;
+    else data_len = 4;
+
+    if ((oc == aloadb_gc) || (oc == aloads_gc) || (oc == aload_gc)) 
+      read_flag = TRUE;
+    else 
+      read_flag = FALSE;
+
+    if (AO1.marker == ARRAY_MV)
+    {   
+        INITAO(&zero_ao);
+
+        size_ao = zero_ao; size_ao.value = -1;
+        for (x=0; x<no_arrays; x++)
+        {   if (AO1.value == svals[array_symbols[x]])
+            {   size_ao.value = array_sizes[x]; y=x;
+            }
+        }
+        if (size_ao.value==-1) compiler_error("Array size can't be found");
+
+        type_ao = zero_ao; type_ao.value = array_types[y];
+
+        if ((!is_systemfile()))
+        {   if (data_len == 1)
+            {
+                if ((array_types[y] == WORD_ARRAY)
+                    || (array_types[y] == TABLE_ARRAY))
+                    warning("Using '->' to access a --> or table array");
+            }
+            else
+            {
+                if ((array_types[y] == BYTE_ARRAY)
+                    || (array_types[y] == STRING_ARRAY))
+                 warning("Using '-->' to access a -> or string array");
+            }
+        }
+    }
+
+
+    if ((!runtime_error_checking_switch) || (veneer_mode))
+    {
+        assembleg_3(oc, AO1, AO2, AO3);
+        return;
+    }
+
+    /* If we recognise AO1 as arising textually from a declared
+       array, we can check bounds explicitly. */
+
+    if (AO1.marker == ARRAY_MV)
+    {   
+        /* Calculate the largest permitted array entry + 1
+           Here "size_ao.value" = largest permitted entry of its own kind */
+        max_ao = size_ao;
+        if (data_len == 1
+            && ((array_types[y] == WORD_ARRAY)
+                || (array_types[y] == TABLE_ARRAY)))
+        {   max_ao.value = size_ao.value*4 + 3;
+            type_ao.value += 8;
+        }
+        if (data_len == 4
+            && ((array_types[y] == BYTE_ARRAY)
+                || (array_types[y] == STRING_ARRAY)
+                || (array_types[y] == BUFFER_ARRAY)))
+        {   max_ao.value = (size_ao.value-3)/4;
+            type_ao.value += 16;
+        }
+        max_ao.value++;
+
+        /* Can't write to the size entry in a string or table */
+        if (((array_types[y] == STRING_ARRAY)
+             || (array_types[y] == TABLE_ARRAY))
+            && (!read_flag))
+        {   if ((array_types[y] == TABLE_ARRAY) && data_len == 1)
+                zero_ao.value = 4;
+            else zero_ao.value = 1;
+        }
+
+        en_ao = zero_ao; en_ao.value = ABOUNDS_RTE;
+
+        switch(oc) { case aloadb_gc:  en_ao.value = ABOUNDS_RTE; break;
+                     case aload_gc:  en_ao.value = ABOUNDS_RTE+1; break;
+                     case astoreb_gc: en_ao.value = ABOUNDS_RTE+2; break;
+                     case astore_gc: en_ao.value = ABOUNDS_RTE+3; break; }
+
+        set_constant_ot(&zero_ao);
+        set_constant_ot(&size_ao);
+        set_constant_ot(&max_ao);
+        set_constant_ot(&type_ao);
+        set_constant_ot(&en_ao);
+
+        /* If we recognize A02 as a constant, we can do the test right
+           now. */
+        if (is_constant_ot(AO2.type) && AO2.marker == 0) {
+            if (AO2.value < zero_ao.value || AO2.value >= max_ao.value) {
+              error("Array reference is out-of-bounds");
+            }
+            assembleg_3(oc, AO1, AO2, AO3);
+            return;
+        }
+
+        passed_label = next_label++; 
+        failed_label = next_label++;
+        final_label = next_label++;
+
+        index_ao = AO2;
+        if ((AO2.type == LOCALVAR_OT)&&(AO2.value == 0))
+        {   assembleg_store(temp_var2, AO2); /* ### could peek */
+            assembleg_store(AO2, temp_var2);
+            index_ao = temp_var2;
+        }
+        assembleg_2_branch(jlt_gc, index_ao, zero_ao, failed_label);
+        assembleg_2_branch(jlt_gc, index_ao, max_ao, passed_label);
+        assemble_label_no(failed_label);
+
+        an_ao = zero_ao; an_ao.value = y;
+        set_constant_ot(&an_ao);
+        five_ao = zero_ao; five_ao.value = 5;
+        set_constant_ot(&five_ao);
+
+        /* Call the error veneer routine. */
+        assembleg_store(stack_pointer, an_ao);
+        assembleg_store(stack_pointer, type_ao);
+        assembleg_store(stack_pointer, size_ao);
+        assembleg_store(stack_pointer, index_ao);
+        assembleg_store(stack_pointer, en_ao);
+        assembleg_3(call_gc, veneer_routine(RT__Err_VR),
+            five_ao, zero_operand);
+
+        /* We have to clear any of AO1, AO2, AO3 off the stack if
+           present, so that we can achieve the same effect on the stack
+           that executing the opcode would have had */
+
+        if ((AO1.type == LOCALVAR_OT) && (AO1.value == 0)) 
+            assembleg_2(copy_gc, stack_pointer, zero_operand);
+        if ((AO2.type == LOCALVAR_OT) && (AO2.value == 0)) 
+            assembleg_2(copy_gc, stack_pointer, zero_operand);
+        if ((AO3.type == LOCALVAR_OT) && (AO3.value == 0))
+        {   if ((oc == aloadb_gc) || (oc == aload_gc))
+            {   assembleg_store(AO3, zero_ao);
+            }
+            else assembleg_2(copy_gc, stack_pointer, zero_operand);
+        }
+        assembleg_jump(final_label);
+
+        assemble_label_no(passed_label);
+        assembleg_3(oc, AO1, AO2, AO3);
+        assemble_label_no(final_label);
+        return;
+    }
+
+    /* Otherwise, compile a call to the veneer which verifies that
+       the proposed read/write is within dynamic Z-machine memory. */
+
+    switch(oc) { 
+        case aloadb_gc: vr = RT__ChLDB_VR; break;
+        case aload_gc: vr = RT__ChLDW_VR; break;
+        case astoreb_gc: vr = RT__ChSTB_VR; break;
+        case astore_gc: vr = RT__ChSTW_VR; break;
+        default: compiler_error("unknown array opcode");
+    }
+
+    if ((oc == aloadb_gc) || (oc == aload_gc)) 
+      assembleg_call_2(veneer_routine(vr), AO1, AO2, AO3);
+    else
+      assembleg_call_3(veneer_routine(vr), AO1, AO2, AO3, zero_operand);
+}
+
+static assembly_operand check_nonzero_at_runtime_g(assembly_operand AO1,
+        int error_label, int rte_number)
+{
+  assembly_operand AO, AO2, AO3;
+  int ln;
+  int check_sp = FALSE, passed_label, failed_label, last_label;
+
+  if (veneer_mode) 
+    return AO1;
+
+  /*  Assemble to code to check that the operand AO1 is ofclass Object:
+      if it is, execution should continue and the stack should be
+      unchanged.  Otherwise, call the veneer's run-time-error routine
+      with the given error number, and then: if the label isn't -1,
+      switch execution to this label, with the value popped from
+      the stack if it was on the stack in the first place;
+      if the label is -1, either replace the top of the stack with
+      the constant symbol (class-object) Object.
+
+      The Object has no parent, child or sibling, so that the
+      built-in tree functions will safely return 0 on this object. */
+
+  /*  Sometimes we can already see that the object number is valid. */
+  if (AO1.marker == OBJECT_MV && 
+    ((AO1.value >= 1) && (AO1.value <= no_objects))) {
+    return AO1;
+  }
+
+  passed_label = next_label++;
+  failed_label = next_label++;  
+
+  if ((AO1.type == LOCALVAR_OT) && (AO1.value == 0) && (AO1.marker == 0)) {
+    /* That is, if AO1 is the stack pointer */
+    check_sp = TRUE;
+    assembleg_store(temp_var2, stack_pointer);
+    assembleg_store(stack_pointer, temp_var2);
+    AO = temp_var2;
+  }
+  else {
+    AO = AO1;
+  }
+  
+  if ((rte_number == IN_RTE) || (rte_number == HAS_RTE)
+    || (rte_number == PROPERTY_RTE) || (rte_number == PROP_NUM_RTE)
+    || (rte_number == PROP_ADD_RTE)) {   
+    /* Allow classes */
+    /* Test if zero... */
+    assembleg_1_branch(jz_gc, AO, failed_label);
+    /* Test if first byte is 0x70... */
+    assembleg_3(aloadb_gc, AO, zero_operand, stack_pointer);
+    INITAO(&AO3);
+    AO3.value = 0x70; /* type byte -- object */
+    set_constant_ot(&AO3);
+    assembleg_2_branch(jeq_gc, stack_pointer, AO3, passed_label);
+  }
+  else {
+    /* Test if zero... */
+    assembleg_1_branch(jz_gc, AO, failed_label);
+    /* Test if first byte is 0x70... */
+    assembleg_3(aloadb_gc, AO, zero_operand, stack_pointer);
+    INITAO(&AO3);
+    AO3.value = 0x70; /* type byte -- object */
+    set_constant_ot(&AO3);
+    assembleg_2_branch(jne_gc, stack_pointer, AO3, failed_label);
+    /* Test if inside the "Class" object... */
+    INITAOTV(&AO3, BYTECONSTANT_OT, GOBJFIELD_PARENT());
+    assembleg_3(aload_gc, AO, AO3, stack_pointer);
+    ln = symbol_index("Class", -1);
+    AO3.value = svals[ln];
+    AO3.marker = OBJECT_MV;
+    AO3.type = CONSTANT_OT;
+    assembleg_2_branch(jne_gc, stack_pointer, AO3, passed_label);
+  }
+  
+  assemble_label_no(failed_label);
+  INITAO(&AO2);
+  AO2.value = rte_number; 
+  set_constant_ot(&AO2);
+  assembleg_call_2(veneer_routine(RT__Err_VR), AO2, AO1, zero_operand);
+  
+  if (error_label != -1) {
+    /* Jump to the error label */
+    if (error_label == -3) assembleg_1(return_gc, zero_operand);
+    else if (error_label == -4) assembleg_1(return_gc, one_operand);
+    else assembleg_jump(error_label);
+  }
+  else {
+    /* Build the symbol for "Object" */
+    ln = symbol_index("Object", -1);
+    AO2.value = svals[ln];
+    AO2.marker = OBJECT_MV;
+    AO2.type = CONSTANT_OT;
+    if (check_sp) {
+      /* Push "Object" */
+      assembleg_store(AO1, AO2);
+    }
+    else {
+      /* Store either "Object" or the operand's value in the temporary
+         variable. */
+      assembleg_store(temp_var2, AO2);
+      last_label = next_label++;
+      assembleg_jump(last_label);
+      assemble_label_no(passed_label);
+      assembleg_store(temp_var2, AO1);
+      assemble_label_no(last_label);
+      return temp_var2;
+    }
+  }
+    
+  assemble_label_no(passed_label);
+  return AO1;
+}
+
+static void compile_conditional_g(condclass *cc,
+    assembly_operand AO1, assembly_operand AO2, int label, int flag)
+{   assembly_operand AO4; 
+    int the_zc, error_label = label,
+    va_flag = FALSE, va_label = 0;
+
+    ASSERT_GLULX(); 
+
+    the_zc = (flag ? cc->posform : cc->negform);
+
+    if (the_zc == -1) {
+      switch ((cc-condclasses)*2 + 500) {
+
+      case HAS_CC:
+        if (runtime_error_checking_switch) {
+          if (flag) 
+            error_label = next_label++;
+          AO1 = check_nonzero_at_runtime(AO1, error_label, HAS_RTE);
+          if (is_constant_ot(AO2.type) && AO2.marker == 0) {
+            if ((AO2.value < 0) || (AO2.value >= NUM_ATTR_BYTES*8)) {
+              error("'has'/'hasnt' applied to illegal attribute number");
+            }
+          }
+          else {
+            int pa_label = next_label++, fa_label = next_label++;
+            assembly_operand en_ao, max_ao;
+
+            if ((AO1.type == LOCALVAR_OT) && (AO1.value == 0)) {
+              if ((AO2.type == LOCALVAR_OT) && (AO2.value == 0)) {
+                assembleg_2(stkpeek_gc, zero_operand, temp_var1);
+                assembleg_2(stkpeek_gc, one_operand, temp_var2);
+              }
+              else {
+                assembleg_2(stkpeek_gc, zero_operand, temp_var1);
+                assembleg_store(temp_var2, AO2);
+              }
+            }
+            else {
+              assembleg_store(temp_var1, AO1);
+              if ((AO2.type == LOCALVAR_OT) && (AO2.value == 0)) {
+                assembleg_2(stkpeek_gc, zero_operand, temp_var2);
+              }
+              else {
+                assembleg_store(temp_var2, AO2);
+              }
+            }
+
+            INITAO(&max_ao);
+            max_ao.value = NUM_ATTR_BYTES*8;
+            set_constant_ot(&max_ao);
+            assembleg_2_branch(jlt_gc, temp_var2, zero_operand, fa_label);
+            assembleg_2_branch(jlt_gc, temp_var2, max_ao, pa_label);
+            assemble_label_no(fa_label);
+            INITAO(&en_ao);
+            en_ao.value = 19; /* INVALIDATTR_RTE */
+            set_constant_ot(&en_ao);
+            assembleg_store(stack_pointer, temp_var2);
+            assembleg_store(stack_pointer, temp_var1);
+            assembleg_store(stack_pointer, en_ao);
+            assembleg_3(call_gc, veneer_routine(RT__Err_VR),
+              three_operand, zero_operand);
+            va_flag = TRUE; 
+            va_label = next_label++;
+            assembleg_jump(va_label);
+            assemble_label_no(pa_label);
+          }
+        }
+        if (is_constant_ot(AO2.type) && AO2.marker == 0) {
+          AO2.value += 8;
+          set_constant_ot(&AO2);
+        }
+        else {
+          INITAO(&AO4);
+          AO4.value = 8;
+          AO4.type = BYTECONSTANT_OT;
+          if ((AO1.type == LOCALVAR_OT) && (AO1.value == 0)) {
+            if ((AO2.type == LOCALVAR_OT) && (AO2.value == 0)) 
+              assembleg_0(stkswap_gc);
+            assembleg_3(add_gc, AO2, AO4, stack_pointer);
+            assembleg_0(stkswap_gc);
+          }
+          else {
+            assembleg_3(add_gc, AO2, AO4, stack_pointer);
+          }
+          AO2 = stack_pointer;
+        }
+        assembleg_3(aloadbit_gc, AO1, AO2, stack_pointer);
+        the_zc = (flag ? jnz_gc : jz_gc);
+        AO1 = stack_pointer;
+        break;
+
+      case IN_CC:
+        if (runtime_error_checking_switch) {
+          if (flag) 
+            error_label = next_label++;
+          AO1 = check_nonzero_at_runtime(AO1, error_label, IN_RTE);
+        }
+        INITAO(&AO4);
+        AO4.value = GOBJFIELD_PARENT();
+        AO4.type = BYTECONSTANT_OT;
+        assembleg_3(aload_gc, AO1, AO4, stack_pointer);
+        AO1 = stack_pointer;
+        the_zc = (flag ? jeq_gc : jne_gc);
+        break;
+
+      case OFCLASS_CC:
+        assembleg_call_2(veneer_routine(OC__Cl_VR), AO1, AO2, stack_pointer);
+        the_zc = (flag ? jnz_gc : jz_gc);
+        AO1 = stack_pointer;
+        break;
+
+      case PROVIDES_CC:
+        assembleg_call_2(veneer_routine(OP__Pr_VR), AO1, AO2, stack_pointer);
+        the_zc = (flag ? jnz_gc : jz_gc);
+        AO1 = stack_pointer;
+        break;
+
+      default:
+        error("condition not yet supported in Glulx");
+        return;
+      }
+    }
+
+    if (the_zc == jnz_gc || the_zc == jz_gc)
+      assembleg_1_branch(the_zc, AO1, label);
+    else
+      assembleg_2_branch(the_zc, AO1, AO2, label);
+    if (error_label != label) assemble_label_no(error_label);
+    if (va_flag) assemble_label_no(va_label);
+}
+
+static void value_in_void_context(assembly_operand AO)
+{
+  if (!glulx_mode)
+    value_in_void_context_z(AO);
+  else
+    value_in_void_context_g(AO);
+}
+
+
+extern assembly_operand check_nonzero_at_runtime(assembly_operand AO1,
+  int error_label, int rte_number)
+{
+  if (!glulx_mode)
+    return check_nonzero_at_runtime_z(AO1, error_label, rte_number);
+  else
+    return check_nonzero_at_runtime_g(AO1, error_label, rte_number);
+}
+
+static void generate_code_from(int n, int void_flag)
+{
+    /*  When void, this must not leave anything on the stack. */
+
+    int i, j, below, above, opnum, arity; assembly_operand Result;
+
+    below = ET[n].down; above = ET[n].up;
+    if (below == -1)
+    {   if ((void_flag) && (ET[n].value.type != OMITTED_OT))
+            value_in_void_context(ET[n].value);
+        return;
+    }
+
+    opnum = ET[n].operator_number;
+
+    if (opnum == COMMA_OP)
+    {   generate_code_from(below, TRUE);
+        generate_code_from(ET[below].right, void_flag);
+        ET[n].value = ET[ET[below].right].value;
+        goto OperatorGenerated;
+    }
+
+    if ((opnum == LOGAND_OP) || (opnum == LOGOR_OP))
+    {   generate_code_from(below, FALSE);
+        generate_code_from(ET[below].right, FALSE);
+        goto OperatorGenerated;
+    }
+
+    if (opnum == -1)
+    {
+        /*  Signifies a SETEQUALS_OP which has already been done */
+
+        ET[n].down = -1; return;
+    }
+
+    /*  Note that (except in the cases of comma and logical and/or) it
+        is essential to code generate the operands right to left, because
+        of the peculiar way the Z-machine's stack works:
+
+            @sub sp sp -> a;
+
+        (for instance) pulls to the first operand, then the second.  So
+
+            @mul a 2 -> sp;
+            @add b 7 -> sp;
+            @sub sp sp -> a;
+
+        calculates (b+7)-(a*2), not the other way around (as would be more
+        usual in stack machines evaluating expressions written in reverse
+        Polish notation).  (Basically this is because the Z-machine was
+        designed to implement a LISP-like language naturally expressed
+        in forward Polish notation: (PLUS 3 4), for instance.)               */
+
+    /*  And the Glulx machine follows the Z-machine in this respect. */
+
+    i=below; arity = 0;
+    while (i != -1)
+    {   i = ET[i].right; arity++;
+    }
+    for (j=arity;j>0;j--)
+    {   int k = 1;
+        i = below;
+        while (k<j)
+        {   k++; i = ET[i].right;
+        }
+        generate_code_from(i, FALSE);
+    }
+
+
+    /*  Check this again, because code generation lower down may have
+        stubbed it into -1  */
+
+    if (ET[n].operator_number == -1)
+    {   ET[n].down = -1; return;
+    }
+
+  if (!glulx_mode) {
+
+    if (operators[opnum].opcode_number_z >= 400)
+    {
+        /*  Conditional terms such as '==': */
+
+        int a = ET[n].true_label, b = ET[n].false_label,
+            branch_away, branch_other,
+            make_jump_away = FALSE, make_branch_label = FALSE;
+        int oc = operators[opnum].opcode_number_z-400, flag = TRUE;
+
+        if (oc >= 400) { oc = oc - 400; flag = FALSE; }
+
+        if ((oc == je_zc) && (arity == 2))
+        {   i = ET[ET[n].down].right;
+            if ((ET[i].value.value == zero_operand.value)
+                && (ET[i].value.type == zero_operand.type))
+                oc = jz_zc;
+        }
+
+        /*  If the condition has truth state flag, branch to
+            label a, and if not, to label b.  Possibly one of a, b
+            equals -1, meaning "continue from this instruction".
+
+            branch_away is the label which is a branch away (the one
+            which isn't immediately after) and flag is the truth
+            state to branch there.
+
+            Note that when multiple instructions are needed (because
+            of the use of the 'or' operator) the branch_other label
+            is created if need be.
+        */
+
+        /*  Reduce to the case where the branch_away label does exist:  */
+
+        if (a == -1) { a = b; b = -1; flag = !flag; }
+
+        branch_away = a; branch_other = b;
+        if (branch_other != -1) make_jump_away = TRUE;
+
+        if ((((oc != je_zc)&&(arity > 2)) || (arity > 4)) && (flag == FALSE))
+        {
+            /*  In this case, we have an 'or' situation where multiple
+                instructions are needed and where the overall condition
+                is negated.  That is, we have, e.g.
+
+                   if not (A cond B or C or D) then branch_away
+
+                which we transform into
+
+                   if (A cond B) then branch_other
+                   if (A cond C) then branch_other
+                   if not (A cond D) then branch_away
+                  .branch_other                                          */
+
+            if (branch_other == -1)
+            {   branch_other = next_label++; make_branch_label = TRUE;
+            }
+        }
+
+        if (oc == jz_zc)
+            assemblez_1_branch(jz_zc, ET[below].value, branch_away, flag);
+        else
+        {   assembly_operand left_operand;
+
+            if (arity == 2)
+                compile_conditional_z(oc, ET[below].value,
+                    ET[ET[below].right].value, branch_away, flag);
+            else
+            {   /*  The case of a condition using "or".
+                    First: if the condition tests the stack pointer,
+                    and it can't always be done in a single test, move
+                    the value off the stack and into temporary variable
+                    storage.  */
+
+                if (((ET[below].value.type == VARIABLE_OT)
+                     && (ET[below].value.value == 0))
+                    && ((oc != je_zc) || (arity>4)) )
+                {   INITAOTV(&left_operand, VARIABLE_OT, 255);
+                    assemblez_store(left_operand, ET[below].value);
+                }
+                else left_operand = ET[below].value;
+                i = ET[below].right; arity--;
+
+                /*  "left_operand" now holds the quantity to be tested;
+                    "i" holds the right operand reached so far;
+                    "arity" the number of right operands.  */
+
+                while (i != -1)
+                {   if ((oc == je_zc) && (arity>1))
+                    {
+                        /*  je_zc is an especially good case since the
+                            Z-machine implements "or" for up to three
+                            right operands automatically, though it's an
+                            especially bad case to generate code for!  */
+
+                        if (arity == 2)
+                        {   assemblez_3_branch(je_zc,
+                              left_operand, ET[i].value,
+                              ET[ET[i].right].value, branch_away, flag);
+                            i = ET[i].right; arity--;
+                        }
+                        else
+                        {   if ((arity == 3) || flag)
+                              assemblez_4_branch(je_zc, left_operand,
+                                ET[i].value,
+                                ET[ET[i].right].value,
+                                ET[ET[ET[i].right].right].value,
+                                branch_away, flag);
+                            else
+                              assemblez_4_branch(je_zc, left_operand,
+                                ET[i].value,
+                                ET[ET[i].right].value,
+                                ET[ET[ET[i].right].right].value,
+                                branch_other, !flag);
+                            i = ET[ET[i].right].right; arity -= 2;
+                        }
+                    }
+                    else
+                    {   /*  Otherwise we can compare the left_operand with
+                            only one right operand at the time.  There are
+                            two cases: it's the last right operand, or it
+                            isn't.  */
+
+                        if ((arity == 1) || flag)
+                            compile_conditional_z(oc, left_operand,
+                                ET[i].value, branch_away, flag);
+                        else
+                            compile_conditional_z(oc, left_operand,
+                                ET[i].value, branch_other, !flag);
+                    }
+                    i = ET[i].right; arity--;
+                }
+
+            }
+        }
+
+        /*  NB: These two conditions cannot both occur, fortunately!  */
+
+        if (make_branch_label) assemble_label_no(branch_other);
+        if (make_jump_away) assemblez_jump(branch_other);
+
+        goto OperatorGenerated;
+    }
+
+  }
+  else {
+    if (operators[opnum].opcode_number_g >= FIRST_CC 
+      && operators[opnum].opcode_number_g <= LAST_CC) {
+      /*  Conditional terms such as '==': */
+
+      int a = ET[n].true_label, b = ET[n].false_label;
+      int branch_away, branch_other, flag,
+        make_jump_away = FALSE, make_branch_label = FALSE;
+      int ccode = operators[opnum].opcode_number_g;
+      condclass *cc = &condclasses[(ccode-FIRST_CC) / 2];
+      flag = (ccode & 1) ? 0 : 1;
+
+      /*  If the comparison is "equal to (constant) 0", change it
+          to the simple "zero" test. Unfortunately, this doesn't
+          work for the commutative form "(constant) 0 is equal to". 
+          At least I don't think it does. */
+
+      if ((cc == &condclasses[1]) && (arity == 2)) {
+        i = ET[ET[n].down].right;
+        if ((ET[i].value.value == 0)
+          && (ET[i].value.marker == 0) 
+          && is_constant_ot(ET[i].value.type)) {
+          cc = &condclasses[0];
+        }
+      }
+
+      /*  If the condition has truth state flag, branch to
+          label a, and if not, to label b.  Possibly one of a, b
+          equals -1, meaning "continue from this instruction".
+          
+          branch_away is the label which is a branch away (the one
+          which isn't immediately after) and flag is the truth
+          state to branch there.
+
+          Note that when multiple instructions are needed (because
+          of the use of the 'or' operator) the branch_other label
+          is created if need be.
+      */
+      
+      /*  Reduce to the case where the branch_away label does exist:  */
+
+      if (a == -1) { a = b; b = -1; flag = !flag; }
+
+      branch_away = a; branch_other = b;
+      if (branch_other != -1) make_jump_away = TRUE;
+      
+      if ((arity > 2) && (flag == FALSE)) {
+        /*  In this case, we have an 'or' situation where multiple
+            instructions are needed and where the overall condition
+            is negated.  That is, we have, e.g.
+            
+            if not (A cond B or C or D) then branch_away
+            
+            which we transform into
+            
+            if (A cond B) then branch_other
+            if (A cond C) then branch_other
+            if not (A cond D) then branch_away
+            .branch_other                                          */
+        
+        if (branch_other == -1) {
+          branch_other = next_label++; make_branch_label = TRUE;
+        }
+      }
+
+      if (cc == &condclasses[0]) {
+        assembleg_1_branch((flag ? cc->posform : cc->negform), 
+          ET[below].value, branch_away);
+      }
+      else {
+        if (arity == 2) {
+          compile_conditional_g(cc, ET[below].value,
+            ET[ET[below].right].value, branch_away, flag);
+        }
+        else {
+          /*  The case of a condition using "or".
+              First: if the condition tests the stack pointer,
+              and it can't always be done in a single test, move
+              the value off the stack and into temporary variable
+              storage.  */
+
+          assembly_operand left_operand;
+          if (((ET[below].value.type == LOCALVAR_OT)
+            && (ET[below].value.value == 0))) {
+            assembleg_store(temp_var1, ET[below].value);
+            left_operand = temp_var1;
+          }
+          else {
+            left_operand = ET[below].value;
+          }
+          i = ET[below].right; 
+          arity--;
+
+          /*  "left_operand" now holds the quantity to be tested;
+              "i" holds the right operand reached so far;
+              "arity" the number of right operands.  */
+
+          while (i != -1) {
+            /*  We can compare the left_operand with
+            only one right operand at the time.  There are
+            two cases: it's the last right operand, or it
+            isn't.  */
+
+            if ((arity == 1) || flag)
+              compile_conditional_g(cc, left_operand,
+            ET[i].value, branch_away, flag);
+            else
+              compile_conditional_g(cc, left_operand,
+            ET[i].value, branch_other, !flag);
+
+            i = ET[i].right; 
+            arity--;
+          }
+        }
+      }
+      
+      /*  NB: These two conditions cannot both occur, fortunately!  */
+      
+      if (make_branch_label) assemble_label_no(branch_other);
+      if (make_jump_away) assembleg_jump(branch_other);
+      
+      goto OperatorGenerated;
+    }
+
+  }
+
+    /*  The operator is now definitely one which produces a value  */
+
+    if (void_flag && (!(operators[opnum].side_effect)))
+        error_named("Evaluating this has no effect:",
+            operators[opnum].description);
+
+    /*  Where shall we put the resulting value? (In Glulx, this could 
+        be smarter, and peg the result into ZEROCONSTANT.) */
+
+    if (void_flag) Result = temp_var1;  /*  Throw it away  */
+    else
+    {   if ((above != -1) && (ET[above].operator_number == SETEQUALS_OP))
+        {
+            /*  If the node above is "set variable equal to", then
+                make that variable the place to put the result, and
+                delete the SETEQUALS_OP node since its effect has already
+                been accomplished.  */
+
+            ET[above].operator_number = -1;
+            Result = ET[ET[above].down].value;
+            ET[above].value = Result;
+        }
+        else Result = stack_pointer;  /*  Otherwise, put it on the stack  */
+    }
+
+  if (!glulx_mode) {
+
+    if (operators[opnum].opcode_number_z != -1)
+    {
+        /*  Operators directly translatable into Z-code opcodes: infix ops
+            take two operands whereas pre/postfix operators take only one */
+
+        if (operators[opnum].usage == IN_U)
+        {   int o_n = operators[opnum].opcode_number_z;
+            if (runtime_error_checking_switch && (!veneer_mode)
+                && ((o_n == div_zc) || (o_n == mod_zc)))
+            {   assembly_operand by_ao, error_ao; int ln;
+                by_ao = ET[ET[below].right].value;
+                if ((by_ao.value != 0) && (by_ao.marker == 0)
+                    && ((by_ao.type == SHORT_CONSTANT_OT)
+                        || (by_ao.type == LONG_CONSTANT_OT)))
+                    assemblez_2_to(o_n, ET[below].value,
+                        by_ao, Result);
+                else
+                {
+                    assemblez_store(temp_var1, ET[below].value);
+                    assemblez_store(temp_var2, by_ao);
+                    ln = next_label++;
+                    assemblez_1_branch(jz_zc, temp_var2, ln, FALSE);
+                    INITAOT(&error_ao, SHORT_CONSTANT_OT);
+                    error_ao.value = DBYZERO_RTE;
+                    assemblez_2(call_vn_zc, veneer_routine(RT__Err_VR),
+                        error_ao);
+                    assemblez_inc(temp_var2);
+                    assemble_label_no(ln);
+                    assemblez_2_to(o_n, temp_var1, temp_var2, Result);
+                }
+            }
+            else {
+            assemblez_2_to(o_n, ET[below].value,
+                ET[ET[below].right].value, Result);
+            }
+        }
+        else
+            assemblez_1_to(operators[opnum].opcode_number_z, ET[below].value,
+                Result);
+    }
+    else
+    switch(opnum)
+    {   case ARROW_OP:
+             access_memory_z(loadb_zc, ET[below].value,
+                                     ET[ET[below].right].value, Result);
+             break;
+        case DARROW_OP:
+             access_memory_z(loadw_zc, ET[below].value,
+                                     ET[ET[below].right].value, Result);
+             break;
+        case UNARY_MINUS_OP:
+             assemblez_2_to(sub_zc, zero_operand, ET[below].value, Result);
+             break;
+        case ARTNOT_OP:
+             assemblez_1_to(not_zc, ET[below].value, Result);
+             break;
+
+        case PROP_ADD_OP:
+             {   assembly_operand AO = ET[below].value;
+                 if (runtime_error_checking_switch && (!veneer_mode))
+                     AO = check_nonzero_at_runtime(AO, -1, PROP_ADD_RTE);
+                 assemblez_2_to(get_prop_addr_zc, AO,
+                     ET[ET[below].right].value, temp_var1);
+                 if (!void_flag) write_result_z(Result, temp_var1);
+             }
+             break;
+
+        case PROP_NUM_OP:
+             {   assembly_operand AO = ET[below].value;
+                 if (runtime_error_checking_switch && (!veneer_mode))
+                     AO = check_nonzero_at_runtime(AO, -1, PROP_NUM_RTE);
+                 assemblez_2_to(get_prop_addr_zc, AO,
+                     ET[ET[below].right].value, temp_var1);
+                 assemblez_1_branch(jz_zc, temp_var1, next_label++, TRUE);
+                 assemblez_1_to(get_prop_len_zc, temp_var1, temp_var1);
+                 assemble_label_no(next_label-1);
+                 if (!void_flag) write_result_z(Result, temp_var1);
+             }
+             break;
+
+        case PROPERTY_OP:
+             {   assembly_operand AO = ET[below].value;
+
+                 if (runtime_error_checking_switch && (!veneer_mode))
+                       assemblez_3_to(call_vs_zc, veneer_routine(RT__ChPR_VR),
+                         AO, ET[ET[below].right].value, temp_var1);
+                 else
+                 assemblez_2_to(get_prop_zc, AO,
+                     ET[ET[below].right].value, temp_var1);
+                 if (!void_flag) write_result_z(Result, temp_var1);
+             }
+             break;
+
+        case MESSAGE_OP:
+             j=1; AI.operand[0] = veneer_routine(RV__Pr_VR);
+             goto GenFunctionCallZ;
+        case MPROP_ADD_OP:
+             j=1; AI.operand[0] = veneer_routine(RA__Pr_VR);
+             goto GenFunctionCallZ;
+        case MPROP_NUM_OP:
+             j=1; AI.operand[0] = veneer_routine(RL__Pr_VR);
+             goto GenFunctionCallZ;
+        case MESSAGE_SETEQUALS_OP:
+             j=1; AI.operand[0] = veneer_routine(WV__Pr_VR);
+             goto GenFunctionCallZ;
+        case MESSAGE_INC_OP:
+             j=1; AI.operand[0] = veneer_routine(IB__Pr_VR);
+             goto GenFunctionCallZ;
+        case MESSAGE_DEC_OP:
+             j=1; AI.operand[0] = veneer_routine(DB__Pr_VR);
+             goto GenFunctionCallZ;
+        case MESSAGE_POST_INC_OP:
+             j=1; AI.operand[0] = veneer_routine(IA__Pr_VR);
+             goto GenFunctionCallZ;
+        case MESSAGE_POST_DEC_OP:
+             j=1; AI.operand[0] = veneer_routine(DA__Pr_VR);
+             goto GenFunctionCallZ;
+        case SUPERCLASS_OP:
+             j=1; AI.operand[0] = veneer_routine(RA__Sc_VR);
+             goto GenFunctionCallZ;
+        case PROP_CALL_OP:
+             j=1; AI.operand[0] = veneer_routine(CA__Pr_VR);
+             goto GenFunctionCallZ;
+        case MESSAGE_CALL_OP:
+             j=1; AI.operand[0] = veneer_routine(CA__Pr_VR);
+             goto GenFunctionCallZ;
+
+
+        case FCALL_OP:
+             j = 0;
+
+             if ((ET[below].value.type == VARIABLE_OT)
+                 && (ET[below].value.value >= 256))
+             {   int sf_number = ET[below].value.value - 256;
+
+                 i = ET[below].right;
+                 if (i == -1)
+                 {   error("Argument to system function missing");
+                     AI.operand[0] = one_operand;
+                     AI.operand_count = 1;
+                 }
+                 else
+                 {   j=0;
+                     while (i != -1) { j++; i = ET[i].right; }
+
+                     if (((sf_number != INDIRECT_SYSF) &&
+                         (sf_number != RANDOM_SYSF) && (j > 1))
+                         || ((sf_number == INDIRECT_SYSF) && (j>7)))
+                     {   j=1;
+                         error("System function given with too many arguments");
+                     }
+                     if (sf_number != RANDOM_SYSF)
+                     {   int jcount;
+                         i = ET[below].right;
+                         for (jcount = 0; jcount < j; jcount++)
+                         {   AI.operand[jcount] = ET[i].value;
+                             i = ET[i].right;
+                         }
+                         AI.operand_count = j;
+                     }
+                 }
+                 AI.store_variable_number = Result.value;
+                 AI.branch_label_number = -1;
+
+                 switch(sf_number)
+                 {   case RANDOM_SYSF:
+                         if (j>1)
+                         {  assembly_operand AO, AO2; int arg_c, arg_et;
+                            INITAOTV(&AO, SHORT_CONSTANT_OT, j);
+                            INITAOT(&AO2, LONG_CONSTANT_OT);
+                            AO2.value = begin_word_array();
+                            AO2.marker = ARRAY_MV;
+
+                            for (arg_c=0, arg_et = ET[below].right;arg_c<j;
+                                 arg_c++, arg_et = ET[arg_et].right)
+                            {   if (ET[arg_et].value.type == VARIABLE_OT)
+              error("Only constants can be used as possible 'random' results");
+                                array_entry(arg_c, ET[arg_et].value);
+                            }
+                            finish_array(arg_c);
+
+                            assemblez_1_to(random_zc, AO, temp_var1);
+                            assemblez_dec(temp_var1);
+                            assemblez_2_to(loadw_zc, AO2, temp_var1, Result);
+                         }
+                         else
+                         assemblez_1_to(random_zc,
+                             ET[ET[below].right].value, Result);
+                         break;
+
+                     case PARENT_SYSF:
+                         {  assembly_operand AO;
+                            AO = ET[ET[below].right].value;
+                            if (runtime_error_checking_switch)
+                                AO = check_nonzero_at_runtime(AO, -1,
+                                    PARENT_RTE);
+                            assemblez_1_to(get_parent_zc, AO, Result);
+                         }
+                         break;
+
+                     case ELDEST_SYSF:
+                     case CHILD_SYSF:
+                         {  assembly_operand AO;
+                            AO = ET[ET[below].right].value;
+                            if (runtime_error_checking_switch)
+                               AO = check_nonzero_at_runtime(AO, -1,
+                               (sf_number==CHILD_SYSF)?CHILD_RTE:ELDEST_RTE);
+                            assemblez_objcode(get_child_zc,
+                               AO, Result, -2, TRUE);
+                         }
+                         break;
+
+                     case YOUNGER_SYSF:
+                     case SIBLING_SYSF:
+                         {  assembly_operand AO;
+                            AO = ET[ET[below].right].value;
+                            if (runtime_error_checking_switch)
+                               AO = check_nonzero_at_runtime(AO, -1,
+                               (sf_number==SIBLING_SYSF)
+                                   ?SIBLING_RTE:YOUNGER_RTE);
+                            assemblez_objcode(get_sibling_zc,
+                               AO, Result, -2, TRUE);
+                         }
+                         break;
+
+                     case INDIRECT_SYSF:
+                         j=0; i = ET[below].right;
+                         goto IndirectFunctionCallZ;
+
+                     case CHILDREN_SYSF:
+                         {  assembly_operand AO;
+                             AO = ET[ET[below].right].value;
+                             if (runtime_error_checking_switch)
+                                 AO = check_nonzero_at_runtime(AO, -1,
+                                     CHILDREN_RTE);
+                             assemblez_store(temp_var1, zero_operand);
+                             assemblez_objcode(get_child_zc,
+                                 AO, stack_pointer, next_label+1, FALSE);
+                             assemble_label_no(next_label);
+                             assemblez_inc(temp_var1);
+                             assemblez_objcode(get_sibling_zc,
+                                 stack_pointer, stack_pointer,
+                                 next_label, TRUE);
+                             assemble_label_no(next_label+1);
+                             assemblez_store(temp_var2, stack_pointer);
+                             if (!void_flag) write_result_z(Result, temp_var1);
+                             next_label += 2;
+                         }
+                         break;
+
+                     case YOUNGEST_SYSF:
+                         {  assembly_operand AO;
+                             AO = ET[ET[below].right].value;
+                             if (runtime_error_checking_switch)
+                                 AO = check_nonzero_at_runtime(AO, -1,
+                                     YOUNGEST_RTE);
+                             assemblez_objcode(get_child_zc,
+                                 AO, temp_var1, next_label+1, FALSE);
+                             assemblez_1(push_zc, temp_var1);
+                             assemble_label_no(next_label);
+                             assemblez_store(temp_var1, stack_pointer);
+                             assemblez_objcode(get_sibling_zc,
+                                 temp_var1, stack_pointer, next_label, TRUE);
+                             assemble_label_no(next_label+1);
+                             if (!void_flag) write_result_z(Result, temp_var1);
+                             next_label += 2;
+                         }
+                         break;
+
+                     case ELDER_SYSF:
+                         assemblez_store(temp_var1, ET[ET[below].right].value);
+                         if (runtime_error_checking_switch)
+                             check_nonzero_at_runtime(temp_var1, -1,
+                                 ELDER_RTE);
+                         assemblez_1_to(get_parent_zc, temp_var1, temp_var3);
+                         assemblez_1_branch(jz_zc, temp_var3,next_label+1,TRUE);
+                         assemblez_store(temp_var2, temp_var3);
+                         assemblez_store(temp_var3, zero_operand);
+                         assemblez_objcode(get_child_zc,
+                             temp_var2, temp_var2, next_label, TRUE);
+                         assemble_label_no(next_label++);
+                         assemblez_2_branch(je_zc, temp_var1, temp_var2,
+                             next_label, TRUE);
+                         assemblez_store(temp_var3, temp_var2);
+                         assemblez_objcode(get_sibling_zc,
+                             temp_var2, temp_var2, next_label - 1, TRUE);
+                         assemble_label_no(next_label++);
+                         if (!void_flag) write_result_z(Result, temp_var3);
+                         break;
+
+                     case METACLASS_SYSF:
+                         assemblez_2_to((version_number==3)?call_zc:call_vs_zc,
+                             veneer_routine(Metaclass_VR),
+                             ET[ET[below].right].value, Result);
+                         break;
+
+                     case GLK_SYSF: 
+                         error("The glk() system function does not exist in Z-code");
+                         break;
+                 }
+                 break;
+             }
+
+             GenFunctionCallZ:
+
+             i = below;
+
+             IndirectFunctionCallZ:
+
+             while ((i != -1) && (j<8))
+             {   AI.operand[j++] = ET[i].value;
+                 i = ET[i].right;
+             }
+
+             if ((j > 4) && (version_number == 3))
+             {   error("A function may be called with at most 3 arguments");
+                 j = 4;
+             }
+             if ((j==8) && (i != -1))
+             {   error("A function may be called with at most 7 arguments");
+             }
+
+             AI.operand_count = j;
+
+             if ((void_flag) && (version_number >= 5))
+             {   AI.store_variable_number = -1;
+                 switch(j)
+                 {   case 1: AI.internal_number = call_1n_zc; break;
+                     case 2: AI.internal_number = call_2n_zc; break;
+                     case 3: case 4: AI.internal_number = call_vn_zc; break;
+                     case 5: case 6: case 7: case 8:
+                         AI.internal_number = call_vn2_zc; break;
+                 }
+             }
+             else
+             {   AI.store_variable_number = Result.value;
+                 if (version_number == 3)
+                     AI.internal_number = call_zc;
+                 else
+                 switch(j)
+                 {   case 1: AI.internal_number = call_1s_zc; break;
+                     case 2: AI.internal_number = call_2s_zc; break;
+                     case 3: case 4: AI.internal_number = call_vs_zc; break;
+                     case 5: case 6: case 7: case 8:
+                         AI.internal_number = call_vs2_zc; break;
+                 }
+             }
+
+             AI.branch_label_number = -1;
+             assemblez_instruction(&AI);
+             break;
+
+        case SETEQUALS_OP:
+             assemblez_store(ET[below].value,
+                 ET[ET[below].right].value);
+             if (!void_flag) write_result_z(Result, ET[below].value);
+             break;
+
+        case PROPERTY_SETEQUALS_OP:
+             if (!void_flag)
+             {   if (runtime_error_checking_switch)
+                     assemblez_4_to(call_zc, veneer_routine(RT__ChPS_VR),
+                         ET[below].value, ET[ET[below].right].value,
+                         ET[ET[ET[below].right].right].value, Result);
+                 else
+                 {   assemblez_store(temp_var1,
+                         ET[ET[ET[below].right].right].value);
+                     assemblez_3(put_prop_zc, ET[below].value,
+                         ET[ET[below].right].value,
+                         temp_var1);
+                     write_result_z(Result, temp_var1);
+                 }
+             }
+             else
+             {   if (runtime_error_checking_switch && (!veneer_mode))
+                     assemblez_4(call_vn_zc, veneer_routine(RT__ChPS_VR),
+                         ET[below].value, ET[ET[below].right].value,
+                         ET[ET[ET[below].right].right].value);
+                 else assemblez_3(put_prop_zc, ET[below].value,
+                     ET[ET[below].right].value,
+                     ET[ET[ET[below].right].right].value);
+             }
+             break;
+        case ARROW_SETEQUALS_OP:
+             if (!void_flag)
+             {   assemblez_store(temp_var1,
+                     ET[ET[ET[below].right].right].value);
+                 access_memory_z(storeb_zc, ET[below].value,
+                     ET[ET[below].right].value,
+                     temp_var1);
+                 write_result_z(Result, temp_var1);
+             }
+             else access_memory_z(storeb_zc, ET[below].value,
+                     ET[ET[below].right].value,
+                     ET[ET[ET[below].right].right].value);
+             break;
+
+        case DARROW_SETEQUALS_OP:
+             if (!void_flag)
+             {   assemblez_store(temp_var1,
+                     ET[ET[ET[below].right].right].value);
+                 access_memory_z(storew_zc, ET[below].value,
+                     ET[ET[below].right].value,
+                     temp_var1);
+                 write_result_z(Result, temp_var1);
+             }
+             else
+                 access_memory_z(storew_zc, ET[below].value,
+                     ET[ET[below].right].value,
+                     ET[ET[ET[below].right].right].value);
+             break;
+
+        case INC_OP:
+             assemblez_inc(ET[below].value);
+             if (!void_flag) write_result_z(Result, ET[below].value);
+             break;
+        case DEC_OP:
+             assemblez_dec(ET[below].value);
+             if (!void_flag) write_result_z(Result, ET[below].value);
+             break;
+        case POST_INC_OP:
+             if (!void_flag) write_result_z(Result, ET[below].value);
+             assemblez_inc(ET[below].value);
+             break;
+        case POST_DEC_OP:
+             if (!void_flag) write_result_z(Result, ET[below].value);
+             assemblez_dec(ET[below].value);
+             break;
+
+        case ARROW_INC_OP:
+             assemblez_store(temp_var1, ET[below].value);
+             assemblez_store(temp_var2, ET[ET[below].right].value);
+             access_memory_z(loadb_zc, temp_var1, temp_var2, temp_var3);
+             assemblez_inc(temp_var3);
+             access_memory_z(storeb_zc, temp_var1, temp_var2, temp_var3);
+             if (!void_flag) write_result_z(Result, temp_var3);
+             break;
+
+        case ARROW_DEC_OP:
+             assemblez_store(temp_var1, ET[below].value);
+             assemblez_store(temp_var2, ET[ET[below].right].value);
+             access_memory_z(loadb_zc, temp_var1, temp_var2, temp_var3);
+             assemblez_dec(temp_var3);
+             access_memory_z(storeb_zc, temp_var1, temp_var2, temp_var3);
+             if (!void_flag) write_result_z(Result, temp_var3);
+             break;
+
+        case ARROW_POST_INC_OP:
+             assemblez_store(temp_var1, ET[below].value);
+             assemblez_store(temp_var2, ET[ET[below].right].value);
+             access_memory_z(loadb_zc, temp_var1, temp_var2, temp_var3);
+             if (!void_flag) write_result_z(Result, temp_var3);
+             assemblez_inc(temp_var3);
+             access_memory_z(storeb_zc, temp_var1, temp_var2, temp_var3);
+             break;
+
+        case ARROW_POST_DEC_OP:
+             assemblez_store(temp_var1, ET[below].value);
+             assemblez_store(temp_var2, ET[ET[below].right].value);
+             access_memory_z(loadb_zc, temp_var1, temp_var2, temp_var3);
+             if (!void_flag) write_result_z(Result, temp_var3);
+             assemblez_dec(temp_var3);
+             access_memory_z(storeb_zc, temp_var1, temp_var2, temp_var3);
+             break;
+
+        case DARROW_INC_OP:
+             assemblez_store(temp_var1, ET[below].value);
+             assemblez_store(temp_var2, ET[ET[below].right].value);
+             access_memory_z(loadw_zc, temp_var1, temp_var2, temp_var3);
+             assemblez_inc(temp_var3);
+             access_memory_z(storew_zc, temp_var1, temp_var2, temp_var3);
+             if (!void_flag) write_result_z(Result, temp_var3);
+             break;
+
+        case DARROW_DEC_OP:
+             assemblez_store(temp_var1, ET[below].value);
+             assemblez_store(temp_var2, ET[ET[below].right].value);
+             access_memory_z(loadw_zc, temp_var1, temp_var2, temp_var3);
+             assemblez_dec(temp_var3);
+             access_memory_z(storew_zc, temp_var1, temp_var2, temp_var3);
+             if (!void_flag) write_result_z(Result, temp_var3);
+             break;
+
+        case DARROW_POST_INC_OP:
+             assemblez_store(temp_var1, ET[below].value);
+             assemblez_store(temp_var2, ET[ET[below].right].value);
+             access_memory_z(loadw_zc, temp_var1, temp_var2, temp_var3);
+             if (!void_flag) write_result_z(Result, temp_var3);
+             assemblez_inc(temp_var3);
+             access_memory_z(storew_zc, temp_var1, temp_var2, temp_var3);
+             break;
+
+        case DARROW_POST_DEC_OP:
+             assemblez_store(temp_var1, ET[below].value);
+             assemblez_store(temp_var2, ET[ET[below].right].value);
+             access_memory_z(loadw_zc, temp_var1, temp_var2, temp_var3);
+             if (!void_flag) write_result_z(Result, temp_var3);
+             assemblez_dec(temp_var3);
+             access_memory_z(storew_zc, temp_var1, temp_var2, temp_var3);
+             break;
+
+        case PROPERTY_INC_OP:
+             assemblez_store(temp_var1, ET[below].value);
+             assemblez_store(temp_var2, ET[ET[below].right].value);
+             assemblez_2_to(get_prop_zc, temp_var1, temp_var2, temp_var3);
+             assemblez_inc(temp_var3);
+             if (runtime_error_checking_switch && (!veneer_mode))
+                  assemblez_4(call_vn_zc, veneer_routine(RT__ChPS_VR),
+                         temp_var1, temp_var2, temp_var3);
+             else assemblez_3(put_prop_zc, temp_var1, temp_var2, temp_var3);
+             if (!void_flag) write_result_z(Result, temp_var3);
+             break;
+
+        case PROPERTY_DEC_OP:
+             assemblez_store(temp_var1, ET[below].value);
+             assemblez_store(temp_var2, ET[ET[below].right].value);
+             assemblez_2_to(get_prop_zc, temp_var1, temp_var2, temp_var3);
+             assemblez_dec(temp_var3);
+             if (runtime_error_checking_switch && (!veneer_mode))
+                  assemblez_4(call_vn_zc, veneer_routine(RT__ChPS_VR),
+                         temp_var1, temp_var2, temp_var3);
+             else assemblez_3(put_prop_zc, temp_var1, temp_var2, temp_var3);
+             if (!void_flag) write_result_z(Result, temp_var3);
+             break;
+
+        case PROPERTY_POST_INC_OP:
+             assemblez_store(temp_var1, ET[below].value);
+             assemblez_store(temp_var2, ET[ET[below].right].value);
+             assemblez_2_to(get_prop_zc, temp_var1, temp_var2, temp_var3);
+             if (!void_flag) write_result_z(Result, temp_var3);
+             assemblez_inc(temp_var3);
+             if (runtime_error_checking_switch && (!veneer_mode))
+                  assemblez_4(call_vn_zc, veneer_routine(RT__ChPS_VR),
+                         temp_var1, temp_var2, temp_var3);
+             else assemblez_3(put_prop_zc, temp_var1, temp_var2, temp_var3);
+             break;
+
+        case PROPERTY_POST_DEC_OP:
+             assemblez_store(temp_var1, ET[below].value);
+             assemblez_store(temp_var2, ET[ET[below].right].value);
+             assemblez_2_to(get_prop_zc, temp_var1, temp_var2, temp_var3);
+             if (!void_flag) write_result_z(Result, temp_var3);
+             assemblez_dec(temp_var3);
+             if (runtime_error_checking_switch && (!veneer_mode))
+                  assemblez_4(call_vn_zc, veneer_routine(RT__ChPS_VR),
+                         temp_var1, temp_var2, temp_var3);
+             else assemblez_3(put_prop_zc, temp_var1, temp_var2, temp_var3);
+             break;
+
+        default:
+            printf("** Trouble op = %d i.e. '%s' **\n",
+                opnum, operators[opnum].description);
+            compiler_error("Expr code gen: Can't generate yet");
+    }
+  }
+  else {
+    assembly_operand AO, AO2;
+    if (operators[opnum].opcode_number_g != -1)
+    {
+        /*  Operators directly translatable into opcodes: infix ops
+            take two operands whereas pre/postfix operators take only one */
+
+        if (operators[opnum].usage == IN_U)
+        {   int o_n = operators[opnum].opcode_number_g;
+            if (runtime_error_checking_switch && (!veneer_mode)
+                && ((o_n == div_gc) || (o_n == mod_gc)))
+            {   assembly_operand by_ao, error_ao; int ln;
+                by_ao = ET[ET[below].right].value;
+                if ((by_ao.value != 0) && (by_ao.marker == 0)
+                    && is_constant_ot(by_ao.type))
+                    assembleg_3(o_n, ET[below].value,
+                        by_ao, Result);
+                else
+                {   assembleg_store(temp_var1, ET[below].value);
+                    assembleg_store(temp_var2, by_ao);
+                    ln = next_label++;
+                    assembleg_1_branch(jnz_gc, temp_var2, ln);
+                    INITAO(&error_ao);
+                    error_ao.value = DBYZERO_RTE;
+                    set_constant_ot(&error_ao);
+                    assembleg_call_1(veneer_routine(RT__Err_VR),
+                      error_ao, zero_operand);
+                    assembleg_store(temp_var2, one_operand);
+                    assemble_label_no(ln);
+                    assembleg_3(o_n, temp_var1, temp_var2, Result);
+                }
+            }
+            else
+            assembleg_3(o_n, ET[below].value,
+                ET[ET[below].right].value, Result);
+        }
+        else
+            assembleg_2(operators[opnum].opcode_number_g, ET[below].value,
+                Result);
+    }
+    else
+    switch(opnum)
+    {
+
+        case PUSH_OP:
+             if (ET[below].value.type == Result.type
+               && ET[below].value.value == Result.value
+               && ET[below].value.marker == Result.marker)
+               break;
+             assembleg_2(copy_gc, ET[below].value, Result);
+             break;
+
+        case UNARY_MINUS_OP:
+             assembleg_2(neg_gc, ET[below].value, Result);
+             break;
+        case ARTNOT_OP:
+             assembleg_2(bitnot_gc, ET[below].value, Result);
+             break;
+
+        case ARROW_OP:
+             access_memory_g(aloadb_gc, ET[below].value,
+                                      ET[ET[below].right].value, Result);
+             break;
+        case DARROW_OP:
+             access_memory_g(aload_gc, ET[below].value,
+                                     ET[ET[below].right].value, Result);
+             break;
+
+        case SETEQUALS_OP:
+             assembleg_store(ET[below].value,
+                 ET[ET[below].right].value);
+             if (!void_flag) write_result_g(Result, ET[below].value);
+             break;
+
+        case ARROW_SETEQUALS_OP:
+             if (!void_flag)
+             {   assembleg_store(temp_var1,
+                     ET[ET[ET[below].right].right].value);
+                 access_memory_g(astoreb_gc, ET[below].value,
+                     ET[ET[below].right].value,
+                     temp_var1);
+                 write_result_g(Result, temp_var1);
+             }
+             else access_memory_g(astoreb_gc, ET[below].value,
+                     ET[ET[below].right].value,
+                     ET[ET[ET[below].right].right].value);
+             break;
+
+        case DARROW_SETEQUALS_OP:
+             if (!void_flag)
+             {   assembleg_store(temp_var1,
+                     ET[ET[ET[below].right].right].value);
+                 access_memory_g(astore_gc, ET[below].value,
+                     ET[ET[below].right].value,
+                     temp_var1);
+                 write_result_g(Result, temp_var1);
+             }
+             else
+                 access_memory_g(astore_gc, ET[below].value,
+                     ET[ET[below].right].value,
+                     ET[ET[ET[below].right].right].value);
+             break;
+
+        case INC_OP:
+             assembleg_inc(ET[below].value);
+             if (!void_flag) write_result_g(Result, ET[below].value);
+             break;
+        case DEC_OP:
+             assembleg_dec(ET[below].value);
+             if (!void_flag) write_result_g(Result, ET[below].value);
+             break;
+        case POST_INC_OP:
+             if (!void_flag) write_result_g(Result, ET[below].value);
+             assembleg_inc(ET[below].value);
+             break;
+        case POST_DEC_OP:
+             if (!void_flag) write_result_g(Result, ET[below].value);
+             assembleg_dec(ET[below].value);
+             break;
+
+        case ARROW_INC_OP:
+             assembleg_store(temp_var1, ET[below].value);
+             assembleg_store(temp_var2, ET[ET[below].right].value);
+             access_memory_g(aloadb_gc, temp_var1, temp_var2, temp_var3);
+             assembleg_inc(temp_var3);
+             access_memory_g(astoreb_gc, temp_var1, temp_var2, temp_var3);
+             if (!void_flag) write_result_g(Result, temp_var3);
+             break;
+
+        case ARROW_DEC_OP:
+             assembleg_store(temp_var1, ET[below].value);
+             assembleg_store(temp_var2, ET[ET[below].right].value);
+             access_memory_g(aloadb_gc, temp_var1, temp_var2, temp_var3);
+             assembleg_dec(temp_var3);
+             access_memory_g(astoreb_gc, temp_var1, temp_var2, temp_var3);
+             if (!void_flag) write_result_g(Result, temp_var3);
+             break;
+
+        case ARROW_POST_INC_OP:
+             assembleg_store(temp_var1, ET[below].value);
+             assembleg_store(temp_var2, ET[ET[below].right].value);
+             access_memory_g(aloadb_gc, temp_var1, temp_var2, temp_var3);
+             if (!void_flag) write_result_g(Result, temp_var3);
+             assembleg_inc(temp_var3);
+             access_memory_g(astoreb_gc, temp_var1, temp_var2, temp_var3);
+             break;
+
+        case ARROW_POST_DEC_OP:
+             assembleg_store(temp_var1, ET[below].value);
+             assembleg_store(temp_var2, ET[ET[below].right].value);
+             access_memory_g(aloadb_gc, temp_var1, temp_var2, temp_var3);
+             if (!void_flag) write_result_g(Result, temp_var3);
+             assembleg_dec(temp_var3);
+             access_memory_g(astoreb_gc, temp_var1, temp_var2, temp_var3);
+             break;
+
+        case DARROW_INC_OP:
+             assembleg_store(temp_var1, ET[below].value);
+             assembleg_store(temp_var2, ET[ET[below].right].value);
+             access_memory_g(aload_gc, temp_var1, temp_var2, temp_var3);
+             assembleg_inc(temp_var3);
+             access_memory_g(astore_gc, temp_var1, temp_var2, temp_var3);
+             if (!void_flag) write_result_g(Result, temp_var3);
+             break;
+
+        case DARROW_DEC_OP:
+             assembleg_store(temp_var1, ET[below].value);
+             assembleg_store(temp_var2, ET[ET[below].right].value);
+             access_memory_g(aload_gc, temp_var1, temp_var2, temp_var3);
+             assembleg_dec(temp_var3);
+             access_memory_g(astore_gc, temp_var1, temp_var2, temp_var3);
+             if (!void_flag) write_result_g(Result, temp_var3);
+             break;
+
+        case DARROW_POST_INC_OP:
+             assembleg_store(temp_var1, ET[below].value);
+             assembleg_store(temp_var2, ET[ET[below].right].value);
+             access_memory_g(aload_gc, temp_var1, temp_var2, temp_var3);
+             if (!void_flag) write_result_g(Result, temp_var3);
+             assembleg_inc(temp_var3);
+             access_memory_g(astore_gc, temp_var1, temp_var2, temp_var3);
+             break;
+
+        case DARROW_POST_DEC_OP:
+             assembleg_store(temp_var1, ET[below].value);
+             assembleg_store(temp_var2, ET[ET[below].right].value);
+             access_memory_g(aload_gc, temp_var1, temp_var2, temp_var3);
+             if (!void_flag) write_result_g(Result, temp_var3);
+             assembleg_dec(temp_var3);
+             access_memory_g(astore_gc, temp_var1, temp_var2, temp_var3);
+             break;
+
+        case PROPERTY_OP:
+        case MESSAGE_OP:
+             AO = veneer_routine(RV__Pr_VR);
+             goto TwoArgFunctionCall;
+        case MPROP_ADD_OP:
+        case PROP_ADD_OP:
+             AO = veneer_routine(RA__Pr_VR);
+             goto TwoArgFunctionCall;
+        case MPROP_NUM_OP:
+        case PROP_NUM_OP:
+             AO = veneer_routine(RL__Pr_VR);
+             goto TwoArgFunctionCall;
+
+        case PROP_CALL_OP:
+        case MESSAGE_CALL_OP:
+             AO2 = veneer_routine(CA__Pr_VR);
+             i = below;
+             goto DoFunctionCall;
+
+        case MESSAGE_INC_OP:
+        case PROPERTY_INC_OP:
+             AO = veneer_routine(IB__Pr_VR);
+             goto TwoArgFunctionCall;
+        case MESSAGE_DEC_OP:
+        case PROPERTY_DEC_OP:
+             AO = veneer_routine(DB__Pr_VR);
+             goto TwoArgFunctionCall;
+        case MESSAGE_POST_INC_OP:
+        case PROPERTY_POST_INC_OP:
+             AO = veneer_routine(IA__Pr_VR);
+             goto TwoArgFunctionCall;
+        case MESSAGE_POST_DEC_OP:
+        case PROPERTY_POST_DEC_OP:
+             AO = veneer_routine(DA__Pr_VR);
+             goto TwoArgFunctionCall;
+        case SUPERCLASS_OP:
+             AO = veneer_routine(RA__Sc_VR);
+             goto TwoArgFunctionCall;
+
+             TwoArgFunctionCall:
+             {
+               assembly_operand AO2 = ET[below].value;
+               assembly_operand AO3 = ET[ET[below].right].value;
+               if (void_flag)
+                 assembleg_call_2(AO, AO2, AO3, zero_operand);
+               else
+                 assembleg_call_2(AO, AO2, AO3, Result);
+             }
+             break;
+
+        case PROPERTY_SETEQUALS_OP:
+        case MESSAGE_SETEQUALS_OP:
+             if (runtime_error_checking_switch && (!veneer_mode))
+                 AO = veneer_routine(RT__ChPS_VR);
+               else
+                 AO = veneer_routine(WV__Pr_VR);
+
+             {
+               assembly_operand AO2 = ET[below].value;
+               assembly_operand AO3 = ET[ET[below].right].value;
+               assembly_operand AO4 = ET[ET[ET[below].right].right].value;
+               if (AO4.type == LOCALVAR_OT && AO4.value == 0) {
+                 /* Rightmost is on the stack; reduce to previous case. */
+                 if (AO2.type == LOCALVAR_OT && AO2.value == 0) {
+                   if (AO3.type == LOCALVAR_OT && AO3.value == 0) {
+                     /* both already on stack. */
+                   }
+                   else {
+                     assembleg_store(stack_pointer, AO3);
+                     assembleg_0(stkswap_gc);
+                   }
+                 }
+                 else {
+                   if (AO3.type == LOCALVAR_OT && AO3.value == 0) {
+                     assembleg_store(stack_pointer, AO2);
+                   }
+                   else {
+                     assembleg_store(stack_pointer, AO3);
+                     assembleg_store(stack_pointer, AO2);
+                   }
+                 }
+               }
+               else {
+                 /* We have to get the rightmost on the stack, below the 
+                    others. */
+                 if (AO3.type == LOCALVAR_OT && AO3.value == 0) {
+                   if (AO2.type == LOCALVAR_OT && AO2.value == 0) {
+                     assembleg_store(stack_pointer, AO4);
+                     assembleg_2(stkroll_gc, three_operand, one_operand);
+                   }
+                   else {
+                     assembleg_store(stack_pointer, AO4);
+                     assembleg_0(stkswap_gc);
+                     assembleg_store(stack_pointer, AO2); 
+                   }
+                 }
+                 else {
+                   if (AO2.type == LOCALVAR_OT && AO2.value == 0) {
+                     assembleg_store(stack_pointer, AO4);
+                     assembleg_store(stack_pointer, AO3);
+                     assembleg_2(stkroll_gc, three_operand, two_operand);
+                   }
+                   else {
+                     assembleg_store(stack_pointer, AO4);
+                     assembleg_store(stack_pointer, AO3);
+                     assembleg_store(stack_pointer, AO2);
+                   }
+                 }
+               }
+               if (void_flag)
+                 assembleg_3(call_gc, AO, three_operand, zero_operand);
+               else
+                 assembleg_3(call_gc, AO, three_operand, Result);
+             }
+             break;
+
+        case FCALL_OP:
+             j = 0;
+
+             if (ET[below].value.type == SYSFUN_OT)
+             {   int sf_number = ET[below].value.value;
+
+                 i = ET[below].right;
+                 if (i == -1)
+                 {   error("Argument to system function missing");
+                     AI.operand[0] = one_operand;
+                     AI.operand_count = 1;
+                 }
+                 else
+                 {   j=0;
+                     while (i != -1) { j++; i = ET[i].right; }
+
+                     if (((sf_number != INDIRECT_SYSF) &&
+                         (sf_number != GLK_SYSF) &&
+                         (sf_number != RANDOM_SYSF) && (j > 1)))
+                     {   j=1;
+                         error("System function given with too many arguments");
+                     }
+                     if (sf_number != RANDOM_SYSF)
+                     {   int jcount;
+                         i = ET[below].right;
+                         for (jcount = 0; jcount < j; jcount++)
+                         {   AI.operand[jcount] = ET[i].value;
+                             i = ET[i].right;
+                         }
+                         AI.operand_count = j;
+                     }
+                 }
+
+                 switch(sf_number)
+                 {
+                     case RANDOM_SYSF:
+                         if (j>1)
+                         {  assembly_operand AO, AO2; 
+                            int arg_c, arg_et;
+                            INITAO(&AO);
+                            AO.value = j; 
+                            set_constant_ot(&AO);
+                            INITAOTV(&AO2, CONSTANT_OT, begin_word_array());
+                            AO2.marker = ARRAY_MV;
+
+                            for (arg_c=0, arg_et = ET[below].right;arg_c<j;
+                                 arg_c++, arg_et = ET[arg_et].right)
+                            {   if (ET[arg_et].value.type == LOCALVAR_OT
+                                    || ET[arg_et].value.type == GLOBALVAR_OT)
+              error("Only constants can be used as possible 'random' results");
+                                array_entry(arg_c, ET[arg_et].value);
+                            }
+                            finish_array(arg_c);
+
+                            assembleg_2(random_gc, AO, stack_pointer);
+                            assembleg_3(aload_gc, AO2, stack_pointer, Result);
+                         }
+                         else {
+                           assembleg_2(random_gc,
+                             ET[ET[below].right].value, stack_pointer);
+                           assembleg_3(add_gc, stack_pointer, one_operand,
+                             Result);
+                         }
+                         break;
+
+                     case PARENT_SYSF:
+                         {  assembly_operand AO;
+                            AO = ET[ET[below].right].value;
+                            if (runtime_error_checking_switch)
+                                AO = check_nonzero_at_runtime(AO, -1,
+                                    PARENT_RTE);
+                            INITAOTV(&AO2, BYTECONSTANT_OT, GOBJFIELD_PARENT());
+                            assembleg_3(aload_gc, AO, AO2, Result);
+                         }
+                         break;
+
+                     case ELDEST_SYSF:
+                     case CHILD_SYSF:
+                         {  assembly_operand AO;
+                            AO = ET[ET[below].right].value;
+                            if (runtime_error_checking_switch)
+                               AO = check_nonzero_at_runtime(AO, -1,
+                               (sf_number==CHILD_SYSF)?CHILD_RTE:ELDEST_RTE);
+                            INITAOTV(&AO2, BYTECONSTANT_OT, GOBJFIELD_CHILD());
+                            assembleg_3(aload_gc, AO, AO2, Result);
+                         }
+                         break;
+
+                     case YOUNGER_SYSF:
+                     case SIBLING_SYSF:
+                         {  assembly_operand AO;
+                            AO = ET[ET[below].right].value;
+                            if (runtime_error_checking_switch)
+                               AO = check_nonzero_at_runtime(AO, -1,
+                               (sf_number==SIBLING_SYSF)
+                                   ?SIBLING_RTE:YOUNGER_RTE);
+                            INITAOTV(&AO2, BYTECONSTANT_OT, GOBJFIELD_SIBLING());
+                            assembleg_3(aload_gc, AO, AO2, Result);
+                         }
+                         break;
+
+                     case CHILDREN_SYSF:
+                         {  assembly_operand AO;
+                            AO = ET[ET[below].right].value;
+                            if (runtime_error_checking_switch)
+                                AO = check_nonzero_at_runtime(AO, -1,
+                                    CHILDREN_RTE);
+                            INITAOTV(&AO2, BYTECONSTANT_OT, GOBJFIELD_CHILD());
+                            assembleg_store(temp_var1, zero_operand);
+                            assembleg_3(aload_gc, AO, AO2, temp_var2);
+                            AO2.value = GOBJFIELD_SIBLING();
+                            assemble_label_no(next_label);
+                            assembleg_1_branch(jz_gc, temp_var2, next_label+1);
+                            assembleg_3(add_gc, temp_var1, one_operand, 
+                              temp_var1);
+                            assembleg_3(aload_gc, temp_var2, AO2, temp_var2);
+                            assembleg_0_branch(jump_gc, next_label);
+                            assemble_label_no(next_label+1);
+                            next_label += 2;
+                            if (!void_flag) 
+                              write_result_g(Result, temp_var1);
+                         }
+                         break;
+
+                     case INDIRECT_SYSF: 
+                         i = ET[below].right;
+                         goto IndirectFunctionCallG;
+
+                     case GLK_SYSF: 
+                         AO2 = veneer_routine(Glk__Wrap_VR);
+                         i = ET[below].right;
+                         goto DoFunctionCall;
+
+                     case METACLASS_SYSF:
+                         assembleg_call_1(veneer_routine(Metaclass_VR),
+                             ET[ET[below].right].value, Result);
+                         break;
+
+                     case YOUNGEST_SYSF:
+                         AO = ET[ET[below].right].value;
+                         if (runtime_error_checking_switch)
+                           AO = check_nonzero_at_runtime(AO, -1,
+                             YOUNGEST_RTE);
+                         INITAOTV(&AO2, BYTECONSTANT_OT, GOBJFIELD_CHILD());
+                         assembleg_3(aload_gc, AO, AO2, temp_var1);
+                         AO2.value = GOBJFIELD_SIBLING();
+                         assembleg_1_branch(jz_gc, temp_var1, next_label+1);
+                         assemble_label_no(next_label);
+                         assembleg_3(aload_gc, temp_var1, AO2, temp_var2);
+                         assembleg_1_branch(jz_gc, temp_var2, next_label+1);
+                         assembleg_store(temp_var1, temp_var2);
+                         assembleg_0_branch(jump_gc, next_label);
+                         assemble_label_no(next_label+1);
+                         if (!void_flag) 
+                           write_result_g(Result, temp_var1);
+                         next_label += 2;
+                         break;
+
+                     case ELDER_SYSF: 
+                         AO = ET[ET[below].right].value;
+                         if (runtime_error_checking_switch)
+                           AO = check_nonzero_at_runtime(AO, -1,
+                             YOUNGEST_RTE);
+                         assembleg_store(temp_var3, AO);
+                         INITAOTV(&AO2, BYTECONSTANT_OT, GOBJFIELD_PARENT());
+                         assembleg_3(aload_gc, temp_var3, AO2, temp_var1);
+                         assembleg_1_branch(jz_gc, temp_var1, next_label+2);
+                         AO2.value = GOBJFIELD_CHILD();
+                         assembleg_3(aload_gc, temp_var1, AO2, temp_var1);
+                         assembleg_1_branch(jz_gc, temp_var1, next_label+2);
+                         assembleg_2_branch(jeq_gc, temp_var3, temp_var1, 
+                           next_label+1);
+                         assemble_label_no(next_label);
+                         AO2.value = GOBJFIELD_SIBLING();
+                         assembleg_3(aload_gc, temp_var1, AO2, temp_var2);
+                         assembleg_2_branch(jeq_gc, temp_var3, temp_var2,
+                           next_label+2);
+                         assembleg_store(temp_var1, temp_var2);
+                         assembleg_0_branch(jump_gc, next_label);
+                         assemble_label_no(next_label+1);
+                         assembleg_store(temp_var1, zero_operand);
+                         assemble_label_no(next_label+2);
+                         if (!void_flag)
+                           write_result_g(Result, temp_var1);
+                         next_label += 3;
+                         break;
+
+                     default:
+                         error("*** system function not implemented ***");
+                         break;
+
+                 }
+                 break;
+             }
+
+             i = below;
+
+             IndirectFunctionCallG:
+
+             /* Get the function address. */
+             AO2 = ET[i].value;
+             i = ET[i].right;
+
+             DoFunctionCall:
+
+             {
+               /* If all the function arguments are in local/global
+                  variables, we have to push them all on the stack.
+                  If all of them are on the stack, we have to do nothing.
+                  If some are and some aren't, we have a hopeless mess,
+                  and we should throw a compiler error.
+               */
+
+               int onstack = 0;
+               int offstack = 0;
+
+               /* begin part of patch G03701 */
+               int nargs = 0;
+               j = i;
+               while (j != -1) {
+                 nargs++;
+                 j = ET[j].right;
+               }
+
+               if (nargs==0) {
+                 assembleg_2(callf_gc, AO2, void_flag ? zero_operand : Result);
+               } else if (nargs==1) {
+                 assembleg_call_1(AO2, ET[i].value, void_flag ? zero_operand : Result);
+               } else if (nargs==2) {
+                 assembly_operand o1 = ET[i].value;
+                 assembly_operand o2 = ET[ET[i].right].value;
+                 assembleg_call_2(AO2, o1, o2, void_flag ? zero_operand : Result);
+               } else if (nargs==3) {
+                 assembly_operand o1 = ET[i].value;
+                 assembly_operand o2 = ET[ET[i].right].value;
+                 assembly_operand o3 = ET[ET[ET[i].right].right].value;
+                 assembleg_call_3(AO2, o1, o2, o3, void_flag ? zero_operand : Result);
+               } else {
+
+                 j = 0;
+                 while (i != -1) {
+                     if (ET[i].value.type == LOCALVAR_OT 
+                       && ET[i].value.value == 0) {
+                       onstack++;
+                     }
+                     else {
+                       assembleg_store(stack_pointer, ET[i].value);
+                       offstack++;
+                     }
+                     i = ET[i].right;
+                     j++;
+                 }
+
+                 if (onstack && offstack)
+                     error("*** Function call cannot be generated with mixed arguments ***");
+                 if (offstack > 1)
+                     error("*** Function call cannot be generated with more than one nonstack argument ***");
+
+                 INITAO(&AO);
+                 AO.value = j;
+                 set_constant_ot(&AO);
+
+                 if (void_flag)
+                   assembleg_3(call_gc, AO2, AO, zero_operand);
+                 else
+                   assembleg_3(call_gc, AO2, AO, Result);
+
+               } /* else nargs>=4 */
+             } /* DoFunctionCall: */
+
+             break;
+
+        default:
+            printf("** Trouble op = %d i.e. '%s' **\n",
+                opnum, operators[opnum].description);
+            compiler_error("Expr code gen: Can't generate yet");
+    }
+  }
+
+    ET[n].value = Result;
+
+    OperatorGenerated:
+
+    if (!glulx_mode) {
+
+        if (ET[n].to_expression)
+        {
+            if (void_flag) {
+                warning("Logical expression has no side-effects");
+                if (ET[n].true_label != -1)
+                    assemble_label_no(ET[n].true_label);
+                else
+                    assemble_label_no(ET[n].false_label);
+            }
+            else if (ET[n].true_label != -1)
+            {   assemblez_1(push_zc, zero_operand);
+                assemblez_jump(next_label++);
+                assemble_label_no(ET[n].true_label);
+                assemblez_1(push_zc, one_operand);
+                assemble_label_no(next_label-1);
+            }
+            else
+            {   assemblez_1(push_zc, one_operand);
+                assemblez_jump(next_label++);
+                assemble_label_no(ET[n].false_label);
+                assemblez_1(push_zc, zero_operand);
+                assemble_label_no(next_label-1);
+            }
+            ET[n].value = stack_pointer;
+        }
+        else
+            if (ET[n].label_after != -1)
+                assemble_label_no(ET[n].label_after);
+
+    }
+    else {
+
+        if (ET[n].to_expression)
+        {   
+            if (void_flag) {
+                warning("Logical expression has no side-effects");
+                if (ET[n].true_label != -1)
+                    assemble_label_no(ET[n].true_label);
+                else
+                    assemble_label_no(ET[n].false_label);
+            }
+            else if (ET[n].true_label != -1)
+            {   assembleg_store(stack_pointer, zero_operand);
+                assembleg_jump(next_label++);
+                assemble_label_no(ET[n].true_label);
+                assembleg_store(stack_pointer, one_operand);
+                assemble_label_no(next_label-1);
+            }
+            else
+            {   assembleg_store(stack_pointer, one_operand);
+                assembleg_jump(next_label++);
+                assemble_label_no(ET[n].false_label);
+                assembleg_store(stack_pointer, zero_operand);
+                assemble_label_no(next_label-1);
+            }
+            ET[n].value = stack_pointer;
+        }
+        else
+            if (ET[n].label_after != -1)
+                assemble_label_no(ET[n].label_after);
+
+    }
+
+    ET[n].down = -1;
+}
+
+assembly_operand code_generate(assembly_operand AO, int context, int label)
+{
+    /*  Used in three contexts: VOID_CONTEXT, CONDITION_CONTEXT and
+            QUANTITY_CONTEXT.
+
+        If CONDITION_CONTEXT, then compile code branching to label number
+            "label" if the condition is false: there's no return value.
+        (Except that if label is -3 or -4 (internal codes for rfalse and
+        rtrue rather than branch) then this is for branching when the
+        condition is true.  This is used for optimising code generation
+        for "if" statements.)
+
+        Otherwise return the assembly operand containing the result
+        (probably the stack pointer variable but not necessarily:
+         e.g. is would be short constant 2 from the expression "j++, 2")     */
+
+    vivc_flag = FALSE;
+
+    if (AO.type != EXPRESSION_OT)
+    {   switch(context)
+        {   case VOID_CONTEXT:
+                value_in_void_context(AO);
+                AO.type = OMITTED_OT;
+                AO.value = 0;
+                break;
+            case CONDITION_CONTEXT:
+                if (!glulx_mode) {
+                  if (label < -2) assemblez_1_branch(jz_zc, AO, label, FALSE);
+                  else assemblez_1_branch(jz_zc, AO, label, TRUE);
+                }
+                else {
+                  if (label < -2) 
+                    assembleg_1_branch(jnz_gc, AO, label);
+                  else 
+                    assembleg_1_branch(jz_gc, AO, label);
+                }
+                AO.type = OMITTED_OT;
+                AO.value = 0;
+                break;
+        }
+        return AO;
+    }
+
+    if (expr_trace_level >= 2)
+    {   printf("Raw parse tree:\n"); show_tree(AO, FALSE);
+    }
+
+    if (context == CONDITION_CONTEXT)
+    {   if (label < -2) annotate_for_conditions(AO.value, label, -1);
+        else annotate_for_conditions(AO.value, -1, label);
+    }
+    else annotate_for_conditions(AO.value, -1, -1);
+
+    if (expr_trace_level >= 1)
+    {   printf("Code generation for expression in ");
+        switch(context)
+        {   case VOID_CONTEXT: printf("void"); break;
+            case CONDITION_CONTEXT: printf("condition"); break;
+            case QUANTITY_CONTEXT: printf("quantity"); break;
+            case ASSEMBLY_CONTEXT: printf("assembly"); break;
+            case ARRAY_CONTEXT: printf("array initialisation"); break;
+            default: printf("* ILLEGAL *"); break;
+        }
+        printf(" context with annotated tree:\n");
+        show_tree(AO, TRUE);
+    }
+
+    generate_code_from(AO.value, (context==VOID_CONTEXT));
+    return ET[AO.value].value;
+}
+
+/* ========================================================================= */
+/*   Data structure management routines                                      */
+/* ------------------------------------------------------------------------- */
+
+extern void init_expressc_vars(void)
+{   make_operands();
+}
+
+extern void expressc_begin_pass(void)
+{
+}
+
+extern void expressc_allocate_arrays(void)
+{
+}
+
+extern void expressc_free_arrays(void)
+{
+}
+
+/* ========================================================================= */