--- /dev/null
+/* ------------------------------------------------------------------------- */
+/* "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)
+{
+}
+
+/* ========================================================================= */