/* ------------------------------------------------------------------------- */
/* "asm" : The Inform assembler */
/* */
-/* Part of Inform 6.35 */
-/* copyright (c) Graham Nelson 1993 - 2020 */
+/* Part of Inform 6.40 */
+/* copyright (c) Graham Nelson 1993 - 2022 */
/* */
/* Inform is free software: you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
#include "header.h"
-uchar *zcode_holding_area; /* Area holding code yet to be transferred
+static uchar *zcode_holding_area; /* Area holding code yet to be transferred
to either zcode_area or temp file no 1 */
-uchar *zcode_markers; /* Bytes holding marker values for this
+static memory_list zcode_holding_area_memlist;
+static uchar *zcode_markers; /* Bytes holding marker values for this
code */
+static memory_list zcode_markers_memlist;
static int zcode_ha_size; /* Number of bytes in holding area */
-memory_block zcode_area; /* Block to hold assembled code (if
- temporary files are not being used) */
+uchar *zcode_area; /* Array to hold assembled code */
+
+memory_list zcode_area_memlist; /* Manages zcode_area */
int32 zmachine_pc; /* PC position of assembly (byte offset
from start of Z-code area) */
int32 no_instructions; /* Number of instructions assembled */
-int execution_never_reaches_here, /* TRUE if the current PC value in the
+int execution_never_reaches_here; /* nonzero if the current PC value in the
code area cannot be reached: e.g. if
the previous instruction was a "quit"
- opcode and no label is set to here */
- next_label, /* Used to count the labels created all
+ opcode and no label is set to here
+ (see EXECSTATE flags for more) */
+int next_label, /* Used to count the labels created all
over Inform in current routine, from 0 */
next_sequence_point; /* Likewise, for sequence points */
-int no_sequence_points; /* Kept for statistics purposes only */
+int no_sequence_points; /* Total over all routines; kept for
+ statistics purposes only */
static int label_moved_error_already_given;
/* When one label has moved, all subsequent
features? */
int uses_float_features; /* Makes use of Glulx floating-point (3.1.2)
features? */
+int uses_extundo_features; /* Makes use of Glulx extended undo (3.1.3)
+ features? */
debug_location statement_debug_location;
/* Location of current statement */
-int32 *variable_tokens; /* The allocated size is
- (MAX_LOCAL_VARIABLES +
- MAX_GLOBAL_VARIABLES). The entries
- MAX_LOCAL_VARIABLES and up give the
- symbol table index for the names of
- the global variables */
-int *variable_usage; /* TRUE if referred to, FALSE otherwise */
+variableinfo *variables; /* The allocated size is
+ (MAX_LOCAL_VARIABLES + no_globals).
+ Local variables first, then globals. */
+memory_list variables_memlist;
assembly_instruction AI; /* A structure used to hold the full
specification of a single Z-code
static int routine_symbol; /* The symbol index of the routine currently
being compiled */
-static char *routine_name; /* The name of the routine currently being
- compiled */
+static memory_list current_routine_name; /* The name of the routine currently
+ being compiled. (This may be longer
+ than MAX_IDENTIFIER_LENGTH, e.g. for
+ an "obj.prop" property routine.) */
static int routine_locals; /* The number of local variables used by
the routine currently being compiled */
static int32 routine_start_pc;
-int32 *named_routine_symbols;
+int32 *named_routine_symbols; /* Allocated up to no_named_routines */
+static memory_list named_routine_symbols_memlist;
static void transfer_routine_z(void);
static void transfer_routine_g(void);
/* Label data */
/* ------------------------------------------------------------------------- */
+static labelinfo *labels; /* Label offsets (i.e. zmachine_pc values).
+ These are allocated sequentially, but accessed
+ as a double-linked list from first_label
+ to last_label (in PC order). */
+static memory_list labels_memlist;
static int first_label, last_label;
-static int32 *label_offsets; /* Double-linked list of label offsets */
-static int *label_next, /* (i.e. zmachine_pc values) in PC order */
- *label_prev;
-static int32 *label_symbols; /* Symbol numbers if defined in source */
-static int *sequence_point_labels;
- /* Label numbers for each */
-static debug_location *sequence_point_locations;
- /* Source code references for each */
- /* (used for making debugging file) */
+static int *labeluse; /* Flags indicating whether a given label has been
+ used as a branch target yet. */
+static memory_list labeluse_memlist;
+static int labeluse_size; /* Entries up to here are initialized */
+
+static sequencepointinfo *sequence_points; /* Allocated to next_sequence_point.
+ Only used when debugfile_switch
+ is set. */
+static memory_list sequence_points_memlist;
+
+/* ------------------------------------------------------------------------- */
+/* Label management */
+/* ------------------------------------------------------------------------- */
+
+/* The stripping of unreachable code requires a bit of explanation.
+
+ As we compile a function, we update the execution_never_reaches_here
+ flag to reflect whether the current line is reachable. If the flag
+ is set (EXECSTATE_UNREACHABLE), we skip generating opcodes,
+ compiling strings, and so on. (See assemblez_instruction(),
+ assembleg_instruction(), and compile_string() for these checks.)
+
+ If we're *between* functions, the execution_never_reaches_here flag
+ is always clear (EXECSTATE_REACHABLE), so global strings are
+ compiled correctly.
+
+ In general, every time we compile a JUMP or RETURN opcode, the flag
+ gets set. Every time we compile a label, the flag gets cleared.
+ This makes sense if you consider a common "while" loop:
+
+ while (true) {
+ ...
+ if (flag) break;
+ ...
+ }
+ ...
+
+ This is compiled as:
+
+ .TopLabel;
+ ...
+ @jnz flag ?ExitLabel;
+ ...
+ @jump TopLabel;
+ .ExitLabel;
+ ...
+
+ Code after an unconditional JUMP is obviously unreachable. But the
+ next thing that happens is the .ExitLabel, which is the target of a
+ branch, so the next line is reachable again.
+
+ However, if the unreachable flag is set when we *begin* a statement
+ (or braced block of statements), we can get tougher. We set the
+ EXECSTATE_ENTIRE flag for the entire duration of the statement or
+ block. This flag cannot be cleared by compiling labels. An example
+ of this:
+
+ if (DOSTUFF) {
+ while (true) {
+ if (flag) break;
+ }
+ }
+
+ If the DOSTUFF constant is false, the entire "while" loop is definitely
+ unreachable. So we should skip .TopLabel, .ExitLabel, and everything
+ in between. To ensure this, we set EXECSTATE_ENTIRE upon entering the
+ "if {...}" braced block, and reset it upon leaving.
+
+ (See parse_code_block() and parse_statement() for the (slightly fugly)
+ bit-fidding that accomplishes this.)
+
+ As an added optimization, some labels are known to be "forward"; they
+ are only reached by forward jumps. (.ExitLabel above is an example.)
+ If we reach a forward label and nothing has in fact jumped there,
+ the label is dead and we can skip it. (And thus also skip clearing
+ the unreachable flag!)
+
+ To understand *that*, consider a "while true" loop with no "break":
+
+ while (true) {
+ ...
+ if (flag) return;
+ ...
+ }
+
+ This never branches to .ExitLabel. So when we reach .ExitLabel,
+ we can say for sure that *nothing* branches there. So we skip
+ compiling that label. The unreachable flag is left set (because we
+ just finished the jump to .TopLabel). Thus, any code following the
+ entire loop is known to be unreachable, and we can righteously
+ complain about it.
+
+ (In contrast, .TopLabel cannot be skipped because it might be the
+ target of a *backwards* branch later on. In fact there might be
+ several -- any "continue" in the loop will jump to .TopLabel.)
+*/
+
+/* Set the position of the given label. The offset will be the current
+ zmachine_pc, or -1 if the label is definitely unused.
+ This adds the label to a linked list (via first_label, last_label).
+
+ The linked list must be in increasing PC order. We know this will
+ be true because we call this as we run through the function, so
+ zmachine_pc always increases.
+*/
static void set_label_offset(int label, int32 offset)
{
- if (label >= MAX_LABELS) memoryerror("MAX_LABELS", MAX_LABELS);
-
- label_offsets[label] = offset;
+ ensure_memory_list_available(&labels_memlist, label+1);
+
+ labels[label].offset = offset;
+ labels[label].symbol = -1;
+ if (offset < 0) {
+ /* Mark this label as invalid and don't put it in the linked list. */
+ labels[label].prev = -1;
+ labels[label].next = -1;
+ return;
+ }
+
if (last_label == -1)
- { label_prev[label] = -1;
+ { labels[label].prev = -1;
first_label = label;
}
else
- { label_prev[label] = last_label;
- label_next[last_label] = label;
+ { labels[label].prev = last_label;
+ labels[last_label].next = label;
}
last_label = label;
- label_next[label] = -1;
- label_symbols[label] = -1;
+ labels[label].next = -1;
+}
+
+/* Set a flag indicating that the given label has been jumped to. */
+static void mark_label_used(int label)
+{
+ if (label < 0)
+ return;
+
+ /* Entries from 0 to labeluse_size have meaningful values.
+ If we have to increase labeluse_size, initialize the new
+ entries to FALSE. */
+ ensure_memory_list_available(&labeluse_memlist, label+1);
+ for (; labeluse_size < label+1; labeluse_size++) {
+ labeluse[labeluse_size] = FALSE;
+ }
+ labeluse[label] = TRUE;
}
/* ------------------------------------------------------------------------- */
extern char *variable_name(int32 i)
{
if (i==0) return("sp");
- if (i<MAX_LOCAL_VARIABLES) return local_variable_texts[i-1];
+ if (i<MAX_LOCAL_VARIABLES) return local_variable_names[i-1].text;
if (!glulx_mode) {
if (i==255) return("TEMP1");
}
}
- return ((char *) symbs[variable_tokens[i]]);
+ return (symbols[variables[i].token].name);
}
-static void print_operand_z(assembly_operand o)
-{ switch(o.type)
+/* Print symbolic information about the AO, if there is any. */
+static void print_operand_annotation(const assembly_operand *o)
+{
+ int any = FALSE;
+ if (o->marker) {
+ printf((!any) ? " (" : ": ");
+ any = TRUE;
+ printf("%s", describe_mv(o->marker));
+ switch (o->marker) {
+ case VROUTINE_MV:
+ printf(": %s", veneer_routine_name(o->value));
+ break;
+ case INCON_MV:
+ printf(": %s", name_of_system_constant(o->value));
+ break;
+ case DWORD_MV:
+ printf(": '");
+ print_dict_word(o->value);
+ printf("'");
+ break;
+ }
+ }
+ if (o->symindex >= 0 && o->symindex < no_symbols) {
+ printf((!any) ? " (" : ": ");
+ any = TRUE;
+ printf("%s", symbols[o->symindex].name);
+ }
+ if (any) printf(")");
+}
+
+static void print_operand_z(const assembly_operand *o, int annotate)
+{ switch(o->type)
{ case EXPRESSION_OT: printf("expr_"); break;
case LONG_CONSTANT_OT: printf("long_"); break;
case SHORT_CONSTANT_OT: printf("short_"); break;
case VARIABLE_OT:
- if (o.value==0) { printf("sp"); return; }
- printf("%s", variable_name(o.value)); return;
+ if (o->value==0) { printf("sp"); return; }
+ printf("%s", variable_name(o->value)); return;
case OMITTED_OT: printf("<no value>"); return;
}
- printf("%d", o.value);
+ printf("%d", o->value);
+ if (annotate)
+ print_operand_annotation(o);
}
-static void print_operand_g(assembly_operand o)
+static void print_operand_g(const assembly_operand *o, int annotate)
{
- switch (o.type) {
+ switch (o->type) {
case EXPRESSION_OT: printf("expr_"); break;
case CONSTANT_OT: printf("long_"); break;
case HALFCONSTANT_OT: printf("short_"); break;
case ZEROCONSTANT_OT: printf("zero_"); return;
case DEREFERENCE_OT: printf("*"); break;
case GLOBALVAR_OT:
- printf("%s (global_%d)", variable_name(o.value), o.value);
+ printf("global_%d (%s)", o->value, variable_name(o->value));
return;
case LOCALVAR_OT:
- if (o.value == 0)
+ if (o->value == 0)
printf("stackptr");
else
- printf("%s (local_%d)", variable_name(o.value), o.value-1);
+ printf("local_%d (%s)", o->value-1, variable_name(o->value));
return;
case SYSFUN_OT:
- if (o.value >= 0 && o.value < NUMBER_SYSTEM_FUNCTIONS)
- printf("%s", system_functions.keywords[o.value]);
+ if (o->value >= 0 && o->value < NUMBER_SYSTEM_FUNCTIONS)
+ printf("%s", system_functions.keywords[o->value]);
else
printf("<unnamed system function>");
return;
case OMITTED_OT: printf("<no value>"); return;
default: printf("???_"); break;
}
- printf("%d", o.value);
+ printf("%d", o->value);
+ if (annotate)
+ print_operand_annotation(o);
}
-extern void print_operand(assembly_operand o)
+extern void print_operand(const assembly_operand *o, int annotate)
{
if (!glulx_mode)
- print_operand_z(o);
+ print_operand_z(o, annotate);
else
- print_operand_g(o);
+ print_operand_g(o, annotate);
}
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
static void byteout(int32 i, int mv)
-{ if (zcode_ha_size >= MAX_ZCODE_SIZE)
- memoryerror("MAX_ZCODE_SIZE",MAX_ZCODE_SIZE);
+{
+ ensure_memory_list_available(&zcode_markers_memlist, zcode_ha_size+1);
+ ensure_memory_list_available(&zcode_holding_area_memlist, zcode_ha_size+1);
zcode_markers[zcode_ha_size] = (uchar) mv;
zcode_holding_area[zcode_ha_size++] = (uchar) i;
zmachine_pc++;
/* ------------------------------------------------------------------------- */
/* A database of the 115 canonical Infocom opcodes in Versions 3 to 6 */
-/* And of the however-many-there-are Glulx opcode */
+/* And of the however-many-there-are Glulx opcodes */
/* ------------------------------------------------------------------------- */
typedef struct opcodez
#define GOP_MemHeap 2 /* uses_memheap_features */
#define GOP_Acceleration 4 /* uses_acceleration_features */
#define GOP_Float 8 /* uses_float_features */
+#define GOP_ExtUndo 16 /* uses_extundo_features */
/* Codes for the number of operands */
{ (uchar *) "jfge", 0x1C5, Br, GOP_Float, 3 },
{ (uchar *) "jisnan", 0x1C8, Br, GOP_Float, 2 },
{ (uchar *) "jisinf", 0x1C9, Br, GOP_Float, 2 },
+ { (uchar *) "hasundo", 0x128, St, GOP_ExtUndo, 1 },
+ { (uchar *) "discardundo",0x129, 0, GOP_ExtUndo, 0 },
};
/* The opmacros table is used for fake opcodes. The opcode numbers are
}
}
-extern void assemblez_instruction(assembly_instruction *AI)
+extern void assemblez_instruction(const assembly_instruction *AI)
{
- uchar *start_pc, *operands_pc;
+ int32 operands_pc;
+ int32 start_pc;
int32 offset, j, topbits=0, types_byte1, types_byte2;
int operand_rules, min=0, max=0, no_operands_given, at_seq_point = FALSE;
assembly_operand o1, o2;
ASSERT_ZCODE();
+ if (execution_never_reaches_here) {
+ if (!(execution_never_reaches_here & EXECSTATE_NOWARN)) {
+ warning("This statement can never be reached");
+ /* only show the warning once */
+ execution_never_reaches_here |= EXECSTATE_NOWARN;
+ }
+ return;
+ }
+
offset = zmachine_pc;
no_instructions++;
if (sequence_point_follows)
{ sequence_point_follows = FALSE; at_seq_point = TRUE;
if (debugfile_switch)
- { sequence_point_labels[next_sequence_point] = next_label;
- sequence_point_locations[next_sequence_point] =
+ {
+ ensure_memory_list_available(&sequence_points_memlist, next_sequence_point+1);
+ sequence_points[next_sequence_point].label = next_label;
+ sequence_points[next_sequence_point].location =
statement_debug_location;
set_label_offset(next_label++, zmachine_pc);
}
return;
}
- if (execution_never_reaches_here)
- warning("This statement can never be reached");
-
operand_rules = opco.op_rules;
- execution_never_reaches_here = ((opco.flags & Rf) != 0);
+ execution_never_reaches_here = ((opco.flags & Rf) ? EXECSTATE_UNREACHABLE : EXECSTATE_REACHABLE);
if (opco.flags2_set != 0) flags2_requirements[opco.flags2_set] = 1;
/* 1. Write the opcode byte(s) */
- start_pc = zcode_holding_area + zcode_ha_size;
+ start_pc = zcode_ha_size;
switch(opco.no)
{ case VAR_LONG: topbits=0xc0; min=0; max=8; break;
}
byteout(opco.code + topbits, 0);
- operands_pc = zcode_holding_area + zcode_ha_size;
+ operands_pc = zcode_ha_size;
/* 2. Dispose of the special rules LABEL and TEXT */
if (operand_rules==LABEL)
{ j = (AI->operand[0]).value;
+ mark_label_used(j);
byteout(j/256, LABEL_MV); byteout(j%256, 0);
goto Instruction_Done;
}
if (operand_rules==TEXT)
{ int32 i;
- uchar *tmp = translate_text(zcode_holding_area + zcode_ha_size, zcode_holding_area+MAX_ZCODE_SIZE, AI->text);
- if (!tmp)
- memoryerror("MAX_ZCODE_SIZE", MAX_ZCODE_SIZE);
- j = subtract_pointers(tmp, (zcode_holding_area + zcode_ha_size));
- for (i=0; i<j; i++) zcode_markers[zcode_ha_size++] = 0;
+ j = translate_text(-1, AI->text, STRCTX_GAMEOPC);
+ if (j < 0) {
+ error("text translation failed");
+ j = 0;
+ }
+ ensure_memory_list_available(&zcode_markers_memlist, zcode_ha_size+j);
+ ensure_memory_list_available(&zcode_holding_area_memlist, zcode_ha_size+j);
+ for (i=0; i<j; i++) {
+ zcode_holding_area[zcode_ha_size] = translated_text[i];
+ zcode_markers[zcode_ha_size] = 0;
+ zcode_ha_size++;
+ }
zmachine_pc += j;
goto Instruction_Done;
}
else
types_byte2 = (types_byte2 & (~mask)) + o1.type*multi;
}
- *operands_pc=types_byte1;
- if (opco.no == VAR_LONG) *(operands_pc+1)=types_byte2;
+ zcode_holding_area[operands_pc]=types_byte1;
+ if (opco.no == VAR_LONG) zcode_holding_area[operands_pc+1]=types_byte2;
break;
case ONE:
o1 = AI->operand[0];
- *start_pc=(*start_pc) + o1.type*0x10;
+ zcode_holding_area[start_pc] += o1.type*0x10;
write_operand(o1);
break;
/* Transfer to VAR form if either operand is a long constant */
if ((o1.type==LONG_CONSTANT_OT)||(o2.type==LONG_CONSTANT_OT))
- { *start_pc=(*start_pc) + 0xc0;
+ { zcode_holding_area[start_pc] += 0xc0;
byteout(o1.type*0x40 + o2.type*0x10 + 0x0f, 0);
}
else
- { if (o1.type==VARIABLE_OT) *start_pc=(*start_pc) + 0x40;
- if (o2.type==VARIABLE_OT) *start_pc=(*start_pc) + 0x20;
+ { if (o1.type==VARIABLE_OT) zcode_holding_area[start_pc] += 0x40;
+ if (o2.type==VARIABLE_OT) zcode_holding_area[start_pc] += 0x20;
}
write_operand(o1);
write_operand(o2);
/* 4. Assemble a Store destination, if needed */
if ((AI->store_variable_number) != -1)
- { if (AI->store_variable_number >= MAX_LOCAL_VARIABLES+MAX_GLOBAL_VARIABLES) {
+ { if (AI->store_variable_number >= MAX_LOCAL_VARIABLES+MAX_ZCODE_GLOBAL_VARS) {
goto OpcodeSyntaxError;
}
o1.type = VARIABLE_OT;
o1.value = AI->store_variable_number;
- variable_usage[o1.value] = TRUE;
+ variables[o1.value].usage = TRUE;
o1.marker = 0;
/* Note that variable numbers 249 to 255 (i.e. globals 233 to 239)
if (AI->branch_label_number != -1)
{ int32 addr, long_form;
int branch_on_true = (AI->branch_flag)?1:0;
-
+ mark_label_used(AI->branch_label_number);
switch (AI->branch_label_number)
{ case -2: addr = 2; branch_on_true = 0; long_form = 0; break;
/* branch nowhere, carry on */
for (i=0; i<AI->operand_count; i++)
{ if ((i==0) && (opco.op_rules == VARIAB))
{ if ((AI->operand[0]).type == VARIABLE_OT)
- { printf("["); print_operand_z(AI->operand[i]); }
+ { printf("["); print_operand_z(&AI->operand[i], TRUE); }
else
printf("%s", variable_name((AI->operand[0]).value));
}
if ((i==0) && (opco.op_rules == LABEL))
{ printf("L%d", AI->operand[0].value);
}
- else print_operand_z(AI->operand[i]);
+ else print_operand_z(&AI->operand[i], TRUE);
printf(" ");
}
if (AI->store_variable_number != -1)
{ assembly_operand AO;
printf("-> ");
AO.type = VARIABLE_OT; AO.value = AI->store_variable_number;
- print_operand_z(AO); printf(" ");
+ print_operand_z(&AO, TRUE); printf(" ");
}
switch(AI->branch_label_number)
}
if (asm_trace_level>=2)
- { for (j=0;start_pc<zcode_holding_area + zcode_ha_size;
+ { for (j=0;start_pc<zcode_ha_size;
j++, start_pc++)
{ if (j%16==0) printf("\n ");
- printf("%02x ", *start_pc);
+ printf("%02x ", zcode_holding_area[start_pc]);
}
}
printf("\n");
error_named("Assembly mistake: syntax is", opcode_syntax_string);
}
-static void assembleg_macro(assembly_instruction *AI)
+static void assembleg_macro(const assembly_instruction *AI)
{
/* validate macro syntax first */
int ix, no_operands_given;
error_named("Assembly mistake: syntax is", opcode_syntax_string);
}
-extern void assembleg_instruction(assembly_instruction *AI)
+extern void assembleg_instruction(const assembly_instruction *AI)
{
- uchar *start_pc, *opmodes_pc;
+ int32 opmodes_pc;
+ int32 start_pc;
int32 offset, j;
int no_operands_given, at_seq_point = FALSE;
int ix, k;
ASSERT_GLULX();
+ if (execution_never_reaches_here) {
+ if (!(execution_never_reaches_here & EXECSTATE_NOWARN)) {
+ warning("This statement can never be reached");
+ /* only show the warning once */
+ execution_never_reaches_here |= EXECSTATE_NOWARN;
+ }
+ return;
+ }
+
offset = zmachine_pc;
no_instructions++;
if (sequence_point_follows)
{ sequence_point_follows = FALSE; at_seq_point = TRUE;
if (debugfile_switch)
- { sequence_point_labels[next_sequence_point] = next_label;
- sequence_point_locations[next_sequence_point] =
+ {
+ ensure_memory_list_available(&sequence_points_memlist, next_sequence_point+1);
+ sequence_points[next_sequence_point].label = next_label;
+ sequence_points[next_sequence_point].location =
statement_debug_location;
set_label_offset(next_label++, zmachine_pc);
}
opco = internal_number_to_opcode_g(AI->internal_number);
- if (execution_never_reaches_here)
- warning("This statement can never be reached");
-
- execution_never_reaches_here = ((opco.flags & Rf) != 0);
+ execution_never_reaches_here = ((opco.flags & Rf) ? EXECSTATE_UNREACHABLE : EXECSTATE_REACHABLE);
if (opco.op_rules & GOP_Unicode) {
uses_unicode_features = TRUE;
if (opco.op_rules & GOP_Float) {
uses_float_features = TRUE;
}
+ if (opco.op_rules & GOP_ExtUndo) {
+ uses_extundo_features = TRUE;
+ }
no_operands_given = AI->operand_count;
/* 1. Write the opcode byte(s) */
- start_pc = zcode_holding_area + zcode_ha_size;
+ start_pc = zcode_ha_size;
if (opco.code < 0x80) {
byteout(opco.code, 0);
every two operands (rounded up). We write zeroes for now;
when the operands are written, we'll go back and fix them. */
- opmodes_pc = zcode_holding_area + zcode_ha_size;
+ opmodes_pc = zcode_ha_size;
for (ix=0; ix<opco.no; ix+=2) {
byteout(0, 0);
compiler_error("Assembling branch without BRANCH_MV marker");
goto OpcodeSyntaxError;
}
+ mark_label_used(k);
if (k == -2) {
k = 2; /* branch no-op */
type = BYTECONSTANT_OT;
}
else {
/* branch to label k */
- j = subtract_pointers((zcode_holding_area + zcode_ha_size),
- opmodes_pc);
+ j = (zcode_ha_size - opmodes_pc);
j = 2*j - ix;
marker = BRANCH_MV + j;
if (!(marker >= BRANCH_MV && marker < BRANCHMAX_MV)) {
if (ix & 1)
j = (j << 4);
- opmodes_pc[ix/2] |= j;
+ zcode_holding_area[opmodes_pc+ix/2] |= j;
}
/* Print assembly trace. */
printf("to L%d", AI->operand[i].value);
}
else {
- print_operand_g(AI->operand[i]);
+ print_operand_g(&AI->operand[i], TRUE);
}
printf(" ");
}
if (asm_trace_level>=2) {
for (j=0;
- start_pc<zcode_holding_area + zcode_ha_size;
+ start_pc<zcode_ha_size;
j++, start_pc++) {
if (j%16==0) printf("\n ");
if (/* DISABLES CODE */ (0)) {
- printf("%02x ", *start_pc);
+ printf("%02x ", zcode_holding_area[start_pc]);
}
else {
- printf("%02x", *start_pc);
- if (zcode_markers[start_pc-zcode_holding_area])
- printf("{%02x}", zcode_markers[start_pc-zcode_holding_area]);
+ printf("%02x", zcode_holding_area[start_pc]);
+ if (zcode_markers[start_pc])
+ printf("{%02x}", zcode_markers[start_pc]);
printf(" ");
}
}
error_named("Assembly mistake: syntax is", opcode_syntax_string);
}
+/* Set up this label at zmachine_pc.
+ This resets the execution_never_reaches_here flag, since every label
+ is assumed to be reachable.
+ However, if STRIP_UNREACHABLE_LABELS and EXECSTATE_ENTIRE are both set,
+ that's not true. The entire statement is being skipped, so we can safely
+ skip all labels within it.
+ (If STRIP_UNREACHABLE_LABELS is not set, the ENTIRE flag is ignored.)
+*/
extern void assemble_label_no(int n)
{
+ if ((execution_never_reaches_here & EXECSTATE_ENTIRE) && STRIP_UNREACHABLE_LABELS) {
+ /* We're not going to compile this label at all. Set a negative
+ offset, which will trip an error if this label is jumped to. */
+ set_label_offset(n, -1);
+ return;
+ }
+
if (asm_trace_level > 0)
printf("%5d +%05lx .L%d\n", ErrorReport.line_number,
((long int) zmachine_pc), n);
set_label_offset(n, zmachine_pc);
- execution_never_reaches_here = FALSE;
+ execution_never_reaches_here = EXECSTATE_REACHABLE;
+}
+
+/* This is the same as assemble_label_no, except we only set up the label
+ if there has been a forward branch to it.
+ Returns whether the label is created.
+ Only use this for labels which never have backwards branches!
+*/
+extern int assemble_forward_label_no(int n)
+{
+ if (n >= 0 && n < labeluse_size && labeluse[n]) {
+ assemble_label_no(n);
+ return TRUE;
+ }
+ else {
+ /* There were no forward branches to this label and we promise
+ there will be no backwards branches to it. Set a negative
+ offset, which will trip an error if we break our promise. */
+ set_label_offset(n, -1);
+ return FALSE;
+ }
}
extern void define_symbol_label(int symbol)
-{ label_symbols[svals[symbol]] = symbol;
+{
+ int label = symbols[symbol].value;
+ /* We may be creating a new label (label = next_label) or filling in
+ the value of an old one. So we call ensure. */
+ ensure_memory_list_available(&labels_memlist, label+1);
+ labels[label].symbol = symbol;
}
extern int32 assemble_routine_header(int no_locals,
int stackargs = FALSE;
int name_length;
- execution_never_reaches_here = FALSE;
+ execution_never_reaches_here = EXECSTATE_REACHABLE;
routine_locals = no_locals;
- for (i=0; i<MAX_LOCAL_VARIABLES; i++) variable_usage[i] = FALSE;
+
+ ensure_memory_list_available(&variables_memlist, MAX_LOCAL_VARIABLES);
+ for (i=0; i<MAX_LOCAL_VARIABLES; i++) variables[i].usage = FALSE;
- if (no_locals >= 1
- && !strcmp(local_variables.keywords[0], "_vararg_count")) {
+ if (no_locals >= 1
+ && strcmpcis(local_variable_names[0].text, "_vararg_count")==0) {
stackargs = TRUE;
}
routine_symbol = the_symbol;
name_length = strlen(name) + 1;
- routine_name =
- my_malloc(name_length * sizeof(char), "temporary copy of routine name");
- strncpy(routine_name, name, name_length);
+ ensure_memory_list_available(¤t_routine_name, name_length);
+ strncpy(current_routine_name.data, name, name_length);
/* Update the routine counter */
for (i=0; i<no_locals; i++) { byteout(0,0); byteout(0,0); }
next_label = 0; next_sequence_point = 0; last_label = -1;
+ labeluse_size = 0;
/* Compile code to print out text like "a=3, b=4, c=5" when the */
/* function is called, if it's required. */
}
else
{ i = no_named_routines++;
- named_routine_symbols[i] = the_symbol;
+ ensure_memory_list_available(&named_routine_symbols_memlist, no_named_routines);
+ named_routine_symbols[i] = the_symbol;
CON.value = i/8; CON.type = LONG_CONSTANT_OT; CON.marker = 0;
RFA.value = routine_flags_array_SC;
RFA.type = LONG_CONSTANT_OT; RFA.marker = INCON_MV;
assemble_label_no(ln);
sprintf(fnt, ") ]^"); AI.text = fnt;
assemblez_0(print_zc);
+ AI.text = NULL;
assemble_label_no(ln2);
}
}
next_label = 0; next_sequence_point = 0; last_label = -1;
+ labeluse_size = 0;
if ((routine_asterisked) || (define_INFIX_switch)) {
int ix;
}
else {
i = no_named_routines++;
+ ensure_memory_list_available(&named_routine_symbols_memlist, no_named_routines);
named_routine_symbols[i] = the_symbol;
}
}
sprintf(fnt, "[ %s(", name);
AO.marker = STRING_MV;
AO.type = CONSTANT_OT;
- AO.value = compile_string(fnt, FALSE, FALSE);
+ AO.value = compile_string(fnt, STRCTX_INFIX);
assembleg_1(streamstr_gc, AO);
if (!stackargs) {
sprintf(fnt, "%s%s = ", (ix==1)?"":", ", variable_name(ix));
AO.marker = STRING_MV;
AO.type = CONSTANT_OT;
- AO.value = compile_string(fnt, FALSE, FALSE);
+ AO.value = compile_string(fnt, STRCTX_INFIX);
assembleg_1(streamstr_gc, AO);
AO.marker = 0;
AO.type = LOCALVAR_OT;
sprintf(fnt, "%s = ", variable_name(1));
AO.marker = STRING_MV;
AO.type = CONSTANT_OT;
- AO.value = compile_string(fnt, FALSE, FALSE);
+ AO.value = compile_string(fnt, STRCTX_INFIX);
assembleg_1(streamstr_gc, AO);
AO.marker = 0;
AO.type = LOCALVAR_OT;
AO.marker = STRING_MV;
AO.type = CONSTANT_OT;
- AO.value = compile_string(") ]^", FALSE, FALSE);
+ AO.value = compile_string(") ]^", STRCTX_INFIX);
assembleg_1(streamstr_gc, AO);
}
}
if (debugfile_switch)
{
+ char *routine_name = current_routine_name.data;
debug_file_printf("<routine>");
if (embedded_flag)
{ debug_file_printf
("<identifier artificial=\"true\">%s</identifier>",
routine_name);
}
- else if (sflags[routine_symbol] & REPLACE_SFLAG)
+ else if (symbols[routine_symbol].flags & REPLACE_SFLAG)
{ /* The symbol type will be set to ROUTINE_T once the replaced
version has been given; if it is already set, we must be dealing
with a replacement, and we can use the routine name as-is.
Otherwise we look for a rename. And if that doesn't work, we
fall back to an artificial identifier. */
- if (stypes[routine_symbol] == ROUTINE_T)
+ if (symbols[routine_symbol].type == ROUTINE_T)
{ /* Optional because there may be further replacements. */
write_debug_optional_identifier(routine_symbol);
}
else if (find_symbol_replacement(&routine_symbol))
{ debug_file_printf
- ("<identifier>%s</identifier>", symbs[routine_symbol]);
+ ("<identifier>%s</identifier>", symbols[routine_symbol].name);
}
else
{ debug_file_printf
{ debug_file_printf("<sequence-point>");
debug_file_printf("<address>");
write_debug_code_backpatch
- (label_offsets[sequence_point_labels[i]]);
+ (labels[sequence_points[i].label].offset);
debug_file_printf("</address>");
- write_debug_location(sequence_point_locations[i]);
+ write_debug_location(sequence_points[i].location);
debug_file_printf("</sequence-point>");
}
debug_file_printf("</routine>");
}
- my_free(&routine_name, "temporary copy of routine name");
-
/* Issue warnings about any local variables not used in the routine. */
for (i=1; i<=routine_locals; i++)
- if (!(variable_usage[i]))
+ if (!(variables[i].usage))
dbnu_warning("Local variable", variable_name(i),
routine_starts_line);
for (i=0; i<next_label; i++)
- { int j = label_symbols[i];
+ { int j = labels[i].symbol;
if (j != -1)
- { if (sflags[j] & CHANGE_SFLAG)
+ { if (symbols[j].flags & CHANGE_SFLAG)
error_named_at("Routine contains no such label as",
- (char *) symbs[j], slines[j]);
+ symbols[j].name, symbols[j].line);
else
- if ((sflags[j] & USED_SFLAG) == 0)
- dbnu_warning("Label", (char *) symbs[j], slines[j]);
- stypes[j] = CONSTANT_T;
- sflags[j] = UNKNOWN_SFLAG;
+ if ((symbols[j].flags & USED_SFLAG) == 0)
+ dbnu_warning("Label", symbols[j].name, symbols[j].line);
+ symbols[j].type = CONSTANT_T;
+ symbols[j].flags = UNKNOWN_SFLAG;
}
}
no_sequence_points += next_sequence_point;
next_label = 0; next_sequence_point = 0;
+ labeluse_size = 0;
+ execution_never_reaches_here = EXECSTATE_REACHABLE;
}
/* ------------------------------------------------------------------------- */
/* Called when the holding area contains an entire routine of code: */
/* backpatches the labels, issues module markers, then dumps the routine */
/* into longer-term storage. */
+/* */
/* Note that in the code received, all branches have long form, and their */
/* contents are not an offset but the label numbers they branch to. */
/* Similarly, LABEL operands (those of "jump" instructions) are label */
/* numbers. So this routine must change the label numbers to offsets, */
/* slimming the code down as it does so to take advantage of short-form */
/* branch operands where possible. */
+/* */
+/* zcode_ha_size is the number of bytes added since the last transfer */
+/* call. So we transfer starting at (zmachine_pc - zcode_ha_size). But we */
+/* might transfer fewer bytes than that. */
/* ------------------------------------------------------------------------- */
-static int32 adjusted_pc;
-
-static void transfer_to_temp_file(uchar *c)
-{ fputc(*c,Temp2_fp);
- adjusted_pc++;
-}
-
-static void transfer_to_zcode_area(uchar *c)
-{ write_byte_to_memory_block(&zcode_area, adjusted_pc++, *c);
-}
-
static void transfer_routine_z(void)
{ int32 i, j, pc, new_pc, label, long_form, offset_of_next, addr,
branch_on_true, rstart_pc;
- void (* transfer_byte)(uchar *);
+ int32 adjusted_pc;
adjusted_pc = zmachine_pc - zcode_ha_size; rstart_pc = adjusted_pc;
(long int) adjusted_pc, zcode_ha_size, next_label);
}
- transfer_byte =
- (temporary_files_switch)?transfer_to_temp_file:transfer_to_zcode_area;
-
/* (1) Scan through for branches and make short/long decisions in each
case. Mark omitted bytes (2nd bytes in branches converted to
- short form) with DELETED_MV. */
+ short form) with DELETED_MV.
+ We also look for jumps that can be entirely eliminated (because
+ they are jumping to the very next instruction). The opcode and
+ both label bytes get DELETED_MV. */
for (i=0, pc=adjusted_pc; i<zcode_ha_size; i++, pc++)
{ if (zcode_markers[i] == BRANCH_MV)
printf("Branch detected at offset %04x\n", pc);
j = (256*zcode_holding_area[i] + zcode_holding_area[i+1]) & 0x7fff;
if (asm_trace_level >= 4)
- printf("To label %d, which is %d from here\n",
- j, label_offsets[j]-pc);
- if ((label_offsets[j] >= pc+2) && (label_offsets[j] < pc+64))
- { if (asm_trace_level >= 4) printf("Short form\n");
+ printf("...To label %d, which is %d from here\n",
+ j, labels[j].offset-pc);
+ if ((labels[j].offset >= pc+2) && (labels[j].offset < pc+64))
+ { if (asm_trace_level >= 4) printf("...Using short form\n");
+ zcode_markers[i+1] = DELETED_MV;
+ }
+ }
+ else if (zcode_markers[i] == LABEL_MV)
+ {
+ if (asm_trace_level >= 4)
+ printf("Jump detected at offset %04x\n", pc);
+ j = (256*zcode_holding_area[i] + zcode_holding_area[i+1]) & 0x7fff;
+ if (asm_trace_level >= 4)
+ printf("...To label %d, which is %d from here\n",
+ j, labels[j].offset-pc);
+ if (labels[j].offset-pc == 2 && i >= 1 && zcode_holding_area[i-1] == opcodes_table_z[jump_zc].code+128) {
+ if (asm_trace_level >= 4) printf("...Deleting jump\n");
+ zcode_markers[i-1] = DELETED_MV;
+ zcode_markers[i] = DELETED_MV;
zcode_markers[i+1] = DELETED_MV;
}
}
{ printf("Opening label: %d\n", first_label);
for (i=0;i<next_label;i++)
printf("Label %d offset %04x next -> %d previous -> %d\n",
- i, label_offsets[i], label_next[i], label_prev[i]);
+ i, labels[i].offset, labels[i].next, labels[i].prev);
}
+ /* label will advance through the linked list as pc increases. */
for (i=0, pc=adjusted_pc, new_pc=adjusted_pc, label = first_label;
i<zcode_ha_size; i++, pc++)
- { while ((label != -1) && (label_offsets[label] == pc))
+ { while ((label != -1) && (labels[label].offset == pc))
{ if (asm_trace_level >= 4)
printf("Position of L%d corrected from %04x to %04x\n",
- label, label_offsets[label], new_pc);
- label_offsets[label] = new_pc;
- label = label_next[label];
+ label, labels[label].offset, new_pc);
+ labels[label].offset = new_pc;
+ label = labels[label].next;
}
if (zcode_markers[i] != DELETED_MV) new_pc++;
}
operands with offsets to those labels. Also issue markers, now
that we know where they occur in the final Z-code area. */
+ ensure_memory_list_available(&zcode_area_memlist, adjusted_pc+zcode_ha_size);
+
for (i=0, new_pc=adjusted_pc; i<zcode_ha_size; i++)
{ switch(zcode_markers[i])
{ case BRANCH_MV:
branch_on_true = ((zcode_holding_area[i]) & 0x80);
offset_of_next = new_pc + long_form + 1;
- addr = label_offsets[j] - offset_of_next + 2;
+ if (labels[j].offset < 0) {
+ error("Attempt to jump to an unreachable label");
+ addr = 0;
+ }
+ else {
+ addr = labels[j].offset - offset_of_next + 2;
+ }
if (addr<-0x2000 || addr>0x1fff)
fatalerror("Branch out of range: divide the routine up?");
if (addr<0) addr+=(int32) 0x10000L;
}
zcode_holding_area[i] = branch_on_true + 0x40 + (addr&0x3f);
}
- transfer_byte(zcode_holding_area + i); new_pc++;
+ zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++;
break;
case LABEL_MV:
j = 256*zcode_holding_area[i] + zcode_holding_area[i+1];
- addr = label_offsets[j] - new_pc;
+ if (labels[j].offset < 0) {
+ error("Attempt to jump to an unreachable label");
+ addr = 0;
+ }
+ else {
+ addr = labels[j].offset - new_pc;
+ }
if (addr<-0x8000 || addr>0x7fff)
fatalerror("Jump out of range: divide the routine up?");
if (addr<0) addr += (int32) 0x10000L;
zcode_holding_area[i] = addr/256;
zcode_holding_area[i+1] = addr%256;
- transfer_byte(zcode_holding_area + i); new_pc++;
+ zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++;
break;
case DELETED_MV:
break;
}
- write_byte_to_memory_block(&zcode_backpatch_table,
- zcode_backpatch_size++,
- zcode_markers[i] + 32*(new_pc/65536));
- write_byte_to_memory_block(&zcode_backpatch_table,
- zcode_backpatch_size++, (new_pc/256)%256);
- write_byte_to_memory_block(&zcode_backpatch_table,
- zcode_backpatch_size++, new_pc%256);
+ if (bpatch_trace_setting >= 2)
+ printf("BP added: MV %d PC %04x\n", zcode_markers[i], new_pc);
+
+ ensure_memory_list_available(&zcode_backpatch_table_memlist, zcode_backpatch_size+3);
+ zcode_backpatch_table[zcode_backpatch_size++] = zcode_markers[i] + 32*(new_pc/65536);
+ zcode_backpatch_table[zcode_backpatch_size++] = (new_pc/256)%256;
+ zcode_backpatch_table[zcode_backpatch_size++] = new_pc%256;
break;
}
- transfer_byte(zcode_holding_area + i); new_pc++;
+ zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++;
break;
}
}
+ /* Consistency check */
+ if (new_pc - rstart_pc > zcode_ha_size || adjusted_pc != new_pc)
+ {
+ fatalerror("Optimisation increased routine length or failed to match; should not happen");
+ }
+
if (asm_trace_level >= 3)
{ printf("After branch optimisation, routine length is %d bytes\n",
new_pc - rstart_pc);
/* Insert null bytes if necessary to ensure the next routine address is */
/* expressible as a packed address */
- { uchar zero[1];
- zero[0] = 0;
- if (oddeven_packing_switch)
- while ((adjusted_pc%(scale_factor*2))!=0) transfer_byte(zero);
- else
- while ((adjusted_pc%scale_factor)!=0) transfer_byte(zero);
- }
+ ensure_memory_list_available(&zcode_area_memlist, adjusted_pc+2*scale_factor);
+
+ if (oddeven_packing_switch)
+ while ((adjusted_pc%(scale_factor*2))!=0) zcode_area[adjusted_pc++] = 0;
+ else
+ while ((adjusted_pc%scale_factor)!=0) zcode_area[adjusted_pc++] = 0;
zmachine_pc = adjusted_pc;
zcode_ha_size = 0;
static void transfer_routine_g(void)
{ int32 i, j, pc, new_pc, label, form_len, offset_of_next, addr,
rstart_pc;
- void (* transfer_byte)(uchar *);
+ int32 adjusted_pc;
adjusted_pc = zmachine_pc - zcode_ha_size; rstart_pc = adjusted_pc;
(long int) adjusted_pc, zcode_ha_size, next_label);
}
- transfer_byte =
- (temporary_files_switch)?transfer_to_temp_file:transfer_to_zcode_area;
-
/* (1) Scan through for branches and make short/long decisions in each
case. Mark omitted bytes (bytes 2-4 in branches converted to
- short form) with DELETED_MV. */
+ short form) with DELETED_MV.
+ We also look for branches that can be entirely eliminated (because
+ they are jumping to the very next instruction). The opcode and
+ all label bytes get DELETED_MV. */
for (i=0, pc=adjusted_pc; i<zcode_ha_size; i++, pc++) {
if (zcode_markers[i] >= BRANCH_MV && zcode_markers[i] < BRANCHMAX_MV) {
| (zcode_holding_area[i+2] << 8)
| (zcode_holding_area[i+3]));
offset_of_next = pc + 4;
- addr = (label_offsets[j] - offset_of_next) + 2;
+ addr = (labels[j].offset - offset_of_next) + 2;
+ opmodebyte = i - ((opmodeoffset+1)/2);
if (asm_trace_level >= 4)
- printf("To label %d, which is (%d-2) = %d from here\n",
- j, addr, label_offsets[j] - offset_of_next);
- if (addr >= -0x80 && addr < 0x80) {
+ printf("...To label %d, which is (%d-2) = %d from here\n",
+ j, addr, labels[j].offset - offset_of_next);
+ if (addr == 2 && i >= 2 && opmodeoffset == 2 && zcode_holding_area[opmodebyte-1] == opcodes_table_g[jump_gc].code) {
+ if (asm_trace_level >= 4) printf("...Deleting branch\n");
+ zcode_markers[i-2] = DELETED_MV;
+ zcode_markers[i-1] = DELETED_MV;
+ zcode_markers[i] = DELETED_MV;
+ zcode_markers[i+1] = DELETED_MV;
+ zcode_markers[i+2] = DELETED_MV;
+ zcode_markers[i+3] = DELETED_MV;
+ }
+ else if (addr >= -0x80 && addr < 0x80) {
if (asm_trace_level >= 4) printf("...Byte form\n");
zcode_markers[i+1] = DELETED_MV;
zcode_markers[i+2] = DELETED_MV;
zcode_markers[i+3] = DELETED_MV;
- opmodebyte = i - ((opmodeoffset+1)/2);
if ((opmodeoffset & 1) == 0)
zcode_holding_area[opmodebyte] =
(zcode_holding_area[opmodebyte] & 0xF0) | 0x01;
if (asm_trace_level >= 4) printf("...Short form\n");
zcode_markers[i+2] = DELETED_MV;
zcode_markers[i+3] = DELETED_MV;
- opmodebyte = i - ((opmodeoffset+1)/2);
if ((opmodeoffset & 1) == 0)
zcode_holding_area[opmodebyte] =
(zcode_holding_area[opmodebyte] & 0xF0) | 0x02;
printf("Opening label: %d\n", first_label);
for (i=0;i<next_label;i++)
printf("Label %d offset %04x next -> %d previous -> %d\n",
- i, label_offsets[i], label_next[i], label_prev[i]);
+ i, labels[i].offset, labels[i].next, labels[i].prev);
}
+ /* label will advance through the linked list as pc increases. */
for (i=0, pc=adjusted_pc, new_pc=adjusted_pc, label = first_label;
i<zcode_ha_size;
i++, pc++) {
- while ((label != -1) && (label_offsets[label] == pc)) {
+ while ((label != -1) && (labels[label].offset == pc)) {
if (asm_trace_level >= 4)
printf("Position of L%d corrected from %04x to %04x\n",
- label, label_offsets[label], new_pc);
- label_offsets[label] = new_pc;
- label = label_next[label];
+ label, labels[label].offset, new_pc);
+ labels[label].offset = new_pc;
+ label = labels[label].next;
}
if (zcode_markers[i] != DELETED_MV) new_pc++;
}
operands with offsets to those labels. Also issue markers, now
that we know where they occur in the final Z-code area. */
+ ensure_memory_list_available(&zcode_area_memlist, adjusted_pc+zcode_ha_size);
+
for (i=0, new_pc=adjusted_pc; i<zcode_ha_size; i++) {
if (zcode_markers[i] >= BRANCH_MV && zcode_markers[i] < BRANCHMAX_MV) {
after it. */
offset_of_next = new_pc + form_len;
- addr = (label_offsets[j] - offset_of_next) + 2;
+ if (labels[j].offset < 0) {
+ error("Attempt to jump to an unreachable label");
+ addr = 0;
+ }
+ else {
+ addr = (labels[j].offset - offset_of_next) + 2;
+ }
if (asm_trace_level >= 4) {
printf("Branch at offset %04x: %04x (%s)\n",
new_pc, addr, ((form_len == 1) ? "byte" :
zcode_holding_area[i+2] = (addr >> 8) & 0xFF;
zcode_holding_area[i+3] = (addr) & 0xFF;
}
- transfer_byte(zcode_holding_area + i); new_pc++;
+ zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++;
}
else if (zcode_markers[i] == LABEL_MV) {
error("*** No LABEL opcodes in Glulx ***");
Then a byte indicating the data size to be patched (1, 2, 4).
Then the four-byte address (new_pc).
*/
- write_byte_to_memory_block(&zcode_backpatch_table,
- zcode_backpatch_size++,
- zcode_markers[i]);
- write_byte_to_memory_block(&zcode_backpatch_table,
- zcode_backpatch_size++,
- 4);
- write_byte_to_memory_block(&zcode_backpatch_table,
- zcode_backpatch_size++, ((new_pc >> 24) & 0xFF));
- write_byte_to_memory_block(&zcode_backpatch_table,
- zcode_backpatch_size++, ((new_pc >> 16) & 0xFF));
- write_byte_to_memory_block(&zcode_backpatch_table,
- zcode_backpatch_size++, ((new_pc >> 8) & 0xFF));
- write_byte_to_memory_block(&zcode_backpatch_table,
- zcode_backpatch_size++, (new_pc & 0xFF));
+ if (bpatch_trace_setting >= 2)
+ printf("BP added: MV %d size %d PC %04x\n", zcode_markers[i], 4, new_pc);
+ ensure_memory_list_available(&zcode_backpatch_table_memlist, zcode_backpatch_size+6);
+ zcode_backpatch_table[zcode_backpatch_size++] = zcode_markers[i];
+ zcode_backpatch_table[zcode_backpatch_size++] = 4;
+ zcode_backpatch_table[zcode_backpatch_size++] = ((new_pc >> 24) & 0xFF);
+ zcode_backpatch_table[zcode_backpatch_size++] = ((new_pc >> 16) & 0xFF);
+ zcode_backpatch_table[zcode_backpatch_size++] = ((new_pc >> 8) & 0xFF);
+ zcode_backpatch_table[zcode_backpatch_size++] = (new_pc & 0xFF);
break;
}
- transfer_byte(zcode_holding_area + i); new_pc++;
+ zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++;
}
}
+ /* Consistency check */
+ if (new_pc - rstart_pc > zcode_ha_size || adjusted_pc != new_pc)
+ {
+ fatalerror("Optimisation increased routine length or failed to match; should not happen");
+ }
+
if (asm_trace_level >= 3)
{ printf("After branch optimisation, routine length is %d bytes\n",
new_pc - rstart_pc);
void assemblez_1_branch(int internal_number,
assembly_operand o1, int label, int flag)
-{ AI.internal_number = internal_number;
+{
+ /* Some clever optimizations first. A constant is always or never equal
+ to zero. */
+ if (o1.marker == 0 && is_constant_ot(o1.type)) {
+ if (internal_number == jz_zc) {
+ if ((flag && o1.value == 0) || (!flag && o1.value != 0)) {
+ assemblez_jump(label);
+ return;
+ }
+ else {
+ /* assemble nothing at all! */
+ return;
+ }
+ }
+ }
+ AI.internal_number = internal_number;
AI.operand_count = 1;
AI.operand[0] = o1;
AI.branch_label_number = label;
if ((internal_number == jz_gc && o1.value == 0)
|| (internal_number == jnz_gc && o1.value != 0)) {
assembleg_0_branch(jump_gc, label);
- /* We clear the "can't reach statement" flag here,
- so that "if (1)" doesn't produce that warning. */
- execution_never_reaches_here = 0;
return;
}
if ((internal_number == jz_gc && o1.value != 0)
get_next_token();
if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
{ assemblez_instruction(&AI);
+ AI.text = NULL;
return;
}
ebf_error("semicolon ';' after print string", token_text);
+ AI.text = NULL;
put_token_back();
return;
}
else
{ if (strcmp(token_text, "sp") == 0) n = 0;
else
- { if (stypes[token_value] != GLOBAL_VARIABLE_T)
+ { if (symbols[token_value].type != GLOBAL_VARIABLE_T)
error_named(
"Store '->' destination not 'sp' or a variable:",
token_text);
- else n = svals[token_value];
+ else n = symbols[token_value].value;
}
}
AI.store_variable_number = n;
int error_flag = FALSE, is_macro = FALSE;
AI.operand_count = 0;
+ AI.text = NULL;
opcode_names.enabled = TRUE;
opcode_macros.enabled = TRUE;
/* Data structure management routines */
/* ------------------------------------------------------------------------- */
-extern void asm_begin_pass(void)
-{ no_instructions = 0;
- zmachine_pc = 0;
- no_sequence_points = 0;
- next_label = 0;
- next_sequence_point = 0;
- zcode_ha_size = 0;
-}
-
extern void init_asm_vars(void)
{ int i;
uses_memheap_features = FALSE;
uses_acceleration_features = FALSE;
uses_float_features = FALSE;
+ uses_extundo_features = FALSE;
+ labels = NULL;
+ sequence_points = NULL;
sequence_point_follows = TRUE;
label_moved_error_already_given = FALSE;
- initialise_memory_block(&zcode_area);
+ zcode_area = NULL;
}
-extern void asm_allocate_arrays(void)
-{ if ((debugfile_switch) && (MAX_LABELS < 2000)) MAX_LABELS = 2000;
-
- variable_tokens = my_calloc(sizeof(int32),
- MAX_LOCAL_VARIABLES+MAX_GLOBAL_VARIABLES, "variable tokens");
- variable_usage = my_calloc(sizeof(int),
- MAX_LOCAL_VARIABLES+MAX_GLOBAL_VARIABLES, "variable usage");
-
- label_offsets = my_calloc(sizeof(int32), MAX_LABELS, "label offsets");
- label_symbols = my_calloc(sizeof(int32), MAX_LABELS, "label symbols");
- label_next = my_calloc(sizeof(int), MAX_LABELS, "label dll 1");
- label_prev = my_calloc(sizeof(int), MAX_LABELS, "label dll 1");
- sequence_point_labels
- = my_calloc(sizeof(int), MAX_LABELS, "sequence point labels");
- sequence_point_locations
- = my_calloc(sizeof(debug_location),
- MAX_LABELS,
- "sequence point locations");
-
- zcode_holding_area = my_malloc(MAX_ZCODE_SIZE,"compiled routine code area");
- zcode_markers = my_malloc(MAX_ZCODE_SIZE, "compiled routine code area");
+extern void asm_begin_pass(void)
+{ no_instructions = 0;
+ zmachine_pc = 0;
+ no_sequence_points = 0;
+ next_label = 0;
+ labeluse_size = 0;
+ next_sequence_point = 0;
+ zcode_ha_size = 0;
+ execution_never_reaches_here = EXECSTATE_REACHABLE;
+}
- named_routine_symbols
- = my_calloc(sizeof(int32), MAX_SYMBOLS, "named routine symbols");
+extern void asm_allocate_arrays(void)
+{
+ initialise_memory_list(&variables_memlist,
+ sizeof(variableinfo), 200, (void**)&variables,
+ "variables");
+
+ initialise_memory_list(&labels_memlist,
+ sizeof(labelinfo), 1000, (void**)&labels,
+ "labels");
+ initialise_memory_list(&labeluse_memlist,
+ sizeof(int), 1000, (void**)&labeluse,
+ "labeluse");
+ initialise_memory_list(&sequence_points_memlist,
+ sizeof(sequencepointinfo), 1000, (void**)&sequence_points,
+ "sequence points");
+
+ initialise_memory_list(&zcode_holding_area_memlist,
+ sizeof(uchar), 2000, (void**)&zcode_holding_area,
+ "compiled routine code area");
+ initialise_memory_list(&zcode_markers_memlist,
+ sizeof(uchar), 2000, (void**)&zcode_markers,
+ "compiled routine markers area");
+
+ initialise_memory_list(&named_routine_symbols_memlist,
+ sizeof(int32), 1000, (void**)&named_routine_symbols,
+ "named routine symbols");
+
+ initialise_memory_list(&zcode_area_memlist,
+ sizeof(uchar), 8192, (void**)&zcode_area,
+ "code area");
+
+ initialise_memory_list(¤t_routine_name,
+ sizeof(char), 3*MAX_IDENTIFIER_LENGTH, NULL,
+ "routine name currently being defined");
}
extern void asm_free_arrays(void)
{
- my_free(&variable_tokens, "variable tokens");
- my_free(&variable_usage, "variable usage");
+ deallocate_memory_list(&variables_memlist);
- my_free(&label_offsets, "label offsets");
- my_free(&label_symbols, "label symbols");
- my_free(&label_next, "label dll 1");
- my_free(&label_prev, "label dll 2");
- my_free(&sequence_point_labels, "sequence point labels");
- my_free(&sequence_point_locations, "sequence point locations");
+ deallocate_memory_list(&labels_memlist);
+ deallocate_memory_list(&sequence_points_memlist);
- my_free(&zcode_holding_area, "compiled routine code area");
- my_free(&zcode_markers, "compiled routine code markers");
+ deallocate_memory_list(&zcode_holding_area_memlist);
+ deallocate_memory_list(&zcode_markers_memlist);
- my_free(&named_routine_symbols, "named routine symbols");
- deallocate_memory_block(&zcode_area);
+ deallocate_memory_list(&named_routine_symbols_memlist);
+ deallocate_memory_list(&zcode_area_memlist);
+ deallocate_memory_list(¤t_routine_name);
}
/* ========================================================================= */