1 /* ------------------------------------------------------------------------- */
2 /* "asm" : The Inform assembler */
4 /* Part of Inform 6.40 */
5 /* copyright (c) Graham Nelson 1993 - 2022 */
7 /* Inform is free software: you can redistribute it and/or modify */
8 /* it under the terms of the GNU General Public License as published by */
9 /* the Free Software Foundation, either version 3 of the License, or */
10 /* (at your option) any later version. */
12 /* Inform is distributed in the hope that it will be useful, */
13 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
14 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
15 /* GNU General Public License for more details. */
17 /* You should have received a copy of the GNU General Public License */
18 /* along with Inform. If not, see https://gnu.org/licenses/ */
20 /* ------------------------------------------------------------------------- */
24 static uchar *zcode_holding_area; /* Area holding code yet to be transferred
25 to either zcode_area or temp file no 1 */
26 static memory_list zcode_holding_area_memlist;
27 static uchar *zcode_markers; /* Bytes holding marker values for this
29 static memory_list zcode_markers_memlist;
30 static int zcode_ha_size; /* Number of bytes in holding area */
32 uchar *zcode_area; /* Array to hold assembled code */
34 memory_list zcode_area_memlist; /* Manages zcode_area */
36 int32 zmachine_pc; /* PC position of assembly (byte offset
37 from start of Z-code area) */
39 int32 no_instructions; /* Number of instructions assembled */
40 int execution_never_reaches_here; /* nonzero if the current PC value in the
41 code area cannot be reached: e.g. if
42 the previous instruction was a "quit"
43 opcode and no label is set to here
44 (see EXECSTATE flags for more) */
45 int next_label, /* Used to count the labels created all
46 over Inform in current routine, from 0 */
47 next_sequence_point; /* Likewise, for sequence points */
48 int no_sequence_points; /* Total over all routines; kept for
49 statistics purposes only */
51 static int label_moved_error_already_given;
52 /* When one label has moved, all subsequent
53 ones probably have too, and this flag
54 suppresses the runaway chain of error
55 messages which would otherwise result */
57 int sequence_point_follows; /* Will the next instruction assembled */
58 /* be at a sequence point in the routine? */
60 int uses_unicode_features; /* Makes use of Glulx Unicode (3.0)
62 int uses_memheap_features; /* Makes use of Glulx mem/heap (3.1)
64 int uses_acceleration_features; /* Makes use of Glulx acceleration (3.1.1)
66 int uses_float_features; /* Makes use of Glulx floating-point (3.1.2)
68 int uses_extundo_features; /* Makes use of Glulx extended undo (3.1.3)
71 debug_location statement_debug_location;
72 /* Location of current statement */
75 variableinfo *variables; /* The allocated size is
76 (MAX_LOCAL_VARIABLES + no_globals).
77 Local variables first, then globals. */
78 memory_list variables_memlist;
80 assembly_instruction AI; /* A structure used to hold the full
81 specification of a single Z-code
82 instruction: effectively this is the
84 assemble_instruction() */
86 static char opcode_syntax_string[128]; /* Text buffer holding the correct
87 syntax for an opcode: used to produce
88 helpful assembler error messages */
90 static int routine_symbol; /* The symbol index of the routine currently
92 static memory_list current_routine_name; /* The name of the routine currently
93 being compiled. (This may be longer
94 than MAX_IDENTIFIER_LENGTH, e.g. for
95 an "obj.prop" property routine.) */
96 static int routine_locals; /* The number of local variables used by
97 the routine currently being compiled */
99 static int32 routine_start_pc;
101 int32 *named_routine_symbols; /* Allocated up to no_named_routines */
102 static memory_list named_routine_symbols_memlist;
104 static void transfer_routine_z(void);
105 static void transfer_routine_g(void);
107 /* ------------------------------------------------------------------------- */
109 /* ------------------------------------------------------------------------- */
111 static labelinfo *labels; /* Label offsets (i.e. zmachine_pc values).
112 These are allocated sequentially, but accessed
113 as a double-linked list from first_label
114 to last_label (in PC order). */
115 static memory_list labels_memlist;
116 static int first_label, last_label;
118 static int *labeluse; /* Flags indicating whether a given label has been
119 used as a branch target yet. */
120 static memory_list labeluse_memlist;
121 static int labeluse_size; /* Entries up to here are initialized */
123 static sequencepointinfo *sequence_points; /* Allocated to next_sequence_point.
124 Only used when debugfile_switch
126 static memory_list sequence_points_memlist;
128 /* ------------------------------------------------------------------------- */
129 /* Label management */
130 /* ------------------------------------------------------------------------- */
132 /* The stripping of unreachable code requires a bit of explanation.
134 As we compile a function, we update the execution_never_reaches_here
135 flag to reflect whether the current line is reachable. If the flag
136 is set (EXECSTATE_UNREACHABLE), we skip generating opcodes,
137 compiling strings, and so on. (See assemblez_instruction(),
138 assembleg_instruction(), and compile_string() for these checks.)
140 If we're *between* functions, the execution_never_reaches_here flag
141 is always clear (EXECSTATE_REACHABLE), so global strings are
144 In general, every time we compile a JUMP or RETURN opcode, the flag
145 gets set. Every time we compile a label, the flag gets cleared.
146 This makes sense if you consider a common "while" loop:
159 @jnz flag ?ExitLabel;
165 Code after an unconditional JUMP is obviously unreachable. But the
166 next thing that happens is the .ExitLabel, which is the target of a
167 branch, so the next line is reachable again.
169 However, if the unreachable flag is set when we *begin* a statement
170 (or braced block of statements), we can get tougher. We set the
171 EXECSTATE_ENTIRE flag for the entire duration of the statement or
172 block. This flag cannot be cleared by compiling labels. An example
181 If the DOSTUFF constant is false, the entire "while" loop is definitely
182 unreachable. So we should skip .TopLabel, .ExitLabel, and everything
183 in between. To ensure this, we set EXECSTATE_ENTIRE upon entering the
184 "if {...}" braced block, and reset it upon leaving.
186 (See parse_code_block() and parse_statement() for the (slightly fugly)
187 bit-fidding that accomplishes this.)
189 As an added optimization, some labels are known to be "forward"; they
190 are only reached by forward jumps. (.ExitLabel above is an example.)
191 If we reach a forward label and nothing has in fact jumped there,
192 the label is dead and we can skip it. (And thus also skip clearing
193 the unreachable flag!)
195 To understand *that*, consider a "while true" loop with no "break":
203 This never branches to .ExitLabel. So when we reach .ExitLabel,
204 we can say for sure that *nothing* branches there. So we skip
205 compiling that label. The unreachable flag is left set (because we
206 just finished the jump to .TopLabel). Thus, any code following the
207 entire loop is known to be unreachable, and we can righteously
210 (In contrast, .TopLabel cannot be skipped because it might be the
211 target of a *backwards* branch later on. In fact there might be
212 several -- any "continue" in the loop will jump to .TopLabel.)
215 /* Set the position of the given label. The offset will be the current
216 zmachine_pc, or -1 if the label is definitely unused.
218 This adds the label to a linked list (via first_label, last_label).
220 The linked list must be in increasing PC order. We know this will
221 be true because we call this as we run through the function, so
222 zmachine_pc always increases.
224 static void set_label_offset(int label, int32 offset)
226 ensure_memory_list_available(&labels_memlist, label+1);
228 labels[label].offset = offset;
229 labels[label].symbol = -1;
231 /* Mark this label as invalid and don't put it in the linked list. */
232 labels[label].prev = -1;
233 labels[label].next = -1;
237 if (last_label == -1)
238 { labels[label].prev = -1;
242 { labels[label].prev = last_label;
243 labels[last_label].next = label;
246 labels[label].next = -1;
249 /* Set a flag indicating that the given label has been jumped to. */
250 static void mark_label_used(int label)
255 /* Entries from 0 to labeluse_size have meaningful values.
256 If we have to increase labeluse_size, initialize the new
258 ensure_memory_list_available(&labeluse_memlist, label+1);
259 for (; labeluse_size < label+1; labeluse_size++) {
260 labeluse[labeluse_size] = FALSE;
262 labeluse[label] = TRUE;
265 /* ------------------------------------------------------------------------- */
266 /* Useful tool for building operands */
267 /* ------------------------------------------------------------------------- */
269 extern void set_constant_ot(assembly_operand *AO)
272 if (AO->value >= 0 && AO->value <= 255)
273 AO->type = SHORT_CONSTANT_OT;
275 AO->type = LONG_CONSTANT_OT;
279 AO->type = ZEROCONSTANT_OT;
280 else if (AO->value >= -0x80 && AO->value < 0x80)
281 AO->type = BYTECONSTANT_OT;
282 else if (AO->value >= -0x8000 && AO->value < 0x8000)
283 AO->type = HALFCONSTANT_OT;
285 AO->type = CONSTANT_OT;
289 extern int is_constant_ot(int otval)
292 return ((otval == LONG_CONSTANT_OT)
293 || (otval == SHORT_CONSTANT_OT));
296 return ((otval == CONSTANT_OT)
297 || (otval == HALFCONSTANT_OT)
298 || (otval == BYTECONSTANT_OT)
299 || (otval == ZEROCONSTANT_OT));
303 extern int is_variable_ot(int otval)
306 return (otval == VARIABLE_OT);
309 return ((otval == LOCALVAR_OT)
310 || (otval == GLOBALVAR_OT));
314 /* ------------------------------------------------------------------------- */
315 /* Used in printing assembly traces */
316 /* ------------------------------------------------------------------------- */
318 extern char *variable_name(int32 i)
320 if (i==0) return("sp");
321 if (i<MAX_LOCAL_VARIABLES) return local_variable_names[i-1].text;
324 if (i==255) return("TEMP1");
325 if (i==254) return("TEMP2");
326 if (i==253) return("TEMP3");
327 if (i==252) return("TEMP4");
328 if (i==251) return("self");
329 if (i==250) return("sender");
330 if (i==249) return("sw__var");
331 if (i >= 256 && i < 286)
332 { if (i - 256 < NUMBER_SYSTEM_FUNCTIONS) return system_functions.keywords[i - 256];
333 return "<unnamed system function>";
337 switch (i - MAX_LOCAL_VARIABLES) {
338 case 0: return "temp_global";
339 case 1: return "temp__global2";
340 case 2: return "temp__global3";
341 case 3: return "temp__global4";
342 case 4: return "self";
343 case 5: return "sender";
344 case 6: return "sw__var";
345 case 7: return "sys__glob0";
346 case 8: return "sys__glob1";
347 case 9: return "sys__glob2";
348 case 10: return "sys_statusline_flag";
352 return (symbols[variables[i].token].name);
355 /* Print symbolic information about the AO, if there is any. */
356 static void print_operand_annotation(const assembly_operand *o)
360 printf((!any) ? " (" : ": ");
362 printf("%s", describe_mv(o->marker));
365 printf(": %s", veneer_routine_name(o->value));
368 printf(": %s", name_of_system_constant(o->value));
372 print_dict_word(o->value);
377 if (o->symindex >= 0 && o->symindex < no_symbols) {
378 printf((!any) ? " (" : ": ");
380 printf("%s", symbols[o->symindex].name);
382 if (any) printf(")");
385 static void print_operand_z(const assembly_operand *o, int annotate)
387 { case EXPRESSION_OT: printf("expr_"); break;
388 case LONG_CONSTANT_OT: printf("long_"); break;
389 case SHORT_CONSTANT_OT: printf("short_"); break;
391 if (o->value==0) { printf("sp"); return; }
392 printf("%s", variable_name(o->value)); return;
393 case OMITTED_OT: printf("<no value>"); return;
395 printf("%d", o->value);
397 print_operand_annotation(o);
400 static void print_operand_g(const assembly_operand *o, int annotate)
403 case EXPRESSION_OT: printf("expr_"); break;
404 case CONSTANT_OT: printf("long_"); break;
405 case HALFCONSTANT_OT: printf("short_"); break;
406 case BYTECONSTANT_OT: printf("byte_"); break;
407 case ZEROCONSTANT_OT: printf("zero_"); return;
408 case DEREFERENCE_OT: printf("*"); break;
410 printf("global_%d (%s)", o->value, variable_name(o->value));
416 printf("local_%d (%s)", o->value-1, variable_name(o->value));
419 if (o->value >= 0 && o->value < NUMBER_SYSTEM_FUNCTIONS)
420 printf("%s", system_functions.keywords[o->value]);
422 printf("<unnamed system function>");
424 case OMITTED_OT: printf("<no value>"); return;
425 default: printf("???_"); break;
427 printf("%d", o->value);
429 print_operand_annotation(o);
432 extern void print_operand(const assembly_operand *o, int annotate)
435 print_operand_z(o, annotate);
437 print_operand_g(o, annotate);
440 /* ------------------------------------------------------------------------- */
441 /* Writing bytes to the code area */
442 /* ------------------------------------------------------------------------- */
444 static void byteout(int32 i, int mv)
446 ensure_memory_list_available(&zcode_markers_memlist, zcode_ha_size+1);
447 ensure_memory_list_available(&zcode_holding_area_memlist, zcode_ha_size+1);
448 zcode_markers[zcode_ha_size] = (uchar) mv;
449 zcode_holding_area[zcode_ha_size++] = (uchar) i;
453 /* ------------------------------------------------------------------------- */
454 /* A database of the 115 canonical Infocom opcodes in Versions 3 to 6 */
455 /* And of the however-many-there-are Glulx opcodes */
456 /* ------------------------------------------------------------------------- */
458 typedef struct opcodez
459 { uchar *name; /* Lower case standard name */
460 int version1; /* Valid from this version number... */
461 int version2; /* ...until this one (or forever if this is 0) */
462 int extension; /* In later versions, see this line in extension table:
463 if -1, the opcode is illegal in later versions */
464 int code; /* Opcode number within its operand-number block */
465 int flags; /* Flags (see below) */
466 int op_rules; /* Any unusual operand rule applying (see below) */
467 int flags2_set; /* If not zero, set this bit in Flags 2 in the header
468 of any game using the opcode */
469 int no; /* Number of operands (see below) */
472 typedef struct opcodeg
473 { uchar *name; /* Lower case standard name */
474 int32 code; /* Opcode number */
475 int flags; /* Flags (see below) */
476 int op_rules; /* Any unusual operand rule applying (see below) */
477 int no; /* Number of operands */
480 /* Flags which can be set */
482 #define St 1 /* Store */
483 #define Br 2 /* Branch */
484 #define Rf 4 /* "Return flag": execution never continues after this
485 opcode (e.g., is a return or unconditional jump) */
486 #define St2 8 /* Store2 (second-to-last operand is store (Glulx)) */
488 /* Codes for any unusual operand assembly rules */
492 #define VARIAB 1 /* First operand expected to be a variable name and
493 assembled to a short constant: the variable number */
494 #define TEXT 2 /* One text operand, to be Z-encoded into the program */
495 #define LABEL 3 /* One operand, a label, given as long constant offset */
496 #define CALL 4 /* First operand is name of a routine, to be assembled
497 as long constant (the routine's packed address):
498 as if the name were prefixed by #r$ */
500 /* Glulx: (bit flags for Glulx VM features) */
502 #define GOP_Unicode 1 /* uses_unicode_features */
503 #define GOP_MemHeap 2 /* uses_memheap_features */
504 #define GOP_Acceleration 4 /* uses_acceleration_features */
505 #define GOP_Float 8 /* uses_float_features */
506 #define GOP_ExtUndo 16 /* uses_extundo_features */
508 /* Codes for the number of operands */
510 #define TWO 1 /* 2 (with certain types of operand, compiled as VAR) */
511 #define VAR 2 /* 0 to 4 */
512 #define VAR_LONG 3 /* 0 to 8 */
513 #define ONE 4 /* 1 */
514 #define ZERO 5 /* 0 */
515 #define EXT 6 /* Extended opcode set VAR: 0 to 4 */
516 #define EXT_LONG 7 /* Extended: 0 to 8 (not used by the canonical opcodes) */
518 static opcodez opcodes_table_z[] =
520 /* Opcodes introduced in Version 3 */
522 /* 0 */ { (uchar *) "je", 3, 0, -1, 0x01, Br, 0, 0, TWO },
523 /* 1 */ { (uchar *) "jl", 3, 0, -1, 0x02, Br, 0, 0, TWO },
524 /* 2 */ { (uchar *) "jg", 3, 0, -1, 0x03, Br, 0, 0, TWO },
525 /* 3 */ { (uchar *) "dec_chk", 3, 0, -1, 0x04, Br, VARIAB, 0, TWO },
526 /* 4 */ { (uchar *) "inc_chk", 3, 0, -1, 0x05, Br, VARIAB, 0, TWO },
527 /* 5 */ { (uchar *) "jin", 3, 0, -1, 0x06, Br, 0, 0, TWO },
528 /* 6 */ { (uchar *) "test", 3, 0, -1, 0x07, Br, 0, 0, TWO },
529 /* 7 */ { (uchar *) "or", 3, 0, -1, 0x08, St, 0, 0, TWO },
530 /* 8 */ { (uchar *) "and", 3, 0, -1, 0x09, St, 0, 0, TWO },
531 /* 9 */ { (uchar *) "test_attr", 3, 0, -1, 0x0A, Br, 0, 0, TWO },
532 /* 10 */ {(uchar *) "set_attr", 3, 0, -1, 0x0B, 0, 0, 0, TWO },
533 /* 11 */ {(uchar *) "clear_attr", 3, 0, -1, 0x0C, 0, 0, 0, TWO },
534 /* 12 */ {(uchar *) "store", 3, 0, -1, 0x0D, 0, VARIAB, 0, TWO },
535 /* 13 */ {(uchar *) "insert_obj", 3, 0, -1, 0x0E, 0, 0, 0, TWO },
536 /* 14 */ {(uchar *) "loadw", 3, 0, -1, 0x0F, St, 0, 0, TWO },
537 /* 15 */ {(uchar *) "loadb", 3, 0, -1, 0x10, St, 0, 0, TWO },
538 /* 16 */ {(uchar *) "get_prop", 3, 0, -1, 0x11, St, 0, 0, TWO },
539 /* 17 */ {(uchar *) "get_prop_addr", 3, 0, -1, 0x12, St, 0, 0, TWO },
540 /* 18 */ {(uchar *) "get_next_prop", 3, 0, -1, 0x13, St, 0, 0, TWO },
541 /* 19 */ {(uchar *) "add", 3, 0, -1, 0x14, St, 0, 0, TWO },
542 /* 20 */ {(uchar *) "sub", 3, 0, -1, 0x15, St, 0, 0, TWO },
543 /* 21 */ {(uchar *) "mul", 3, 0, -1, 0x16, St, 0, 0, TWO },
544 /* 22 */ {(uchar *) "div", 3, 0, -1, 0x17, St, 0, 0, TWO },
545 /* 23 */ {(uchar *) "mod", 3, 0, -1, 0x18, St, 0, 0, TWO },
546 /* 24 */ {(uchar *) "call", 3, 0, -1, 0x20, St, CALL, 0, VAR },
547 /* 25 */ {(uchar *) "storew", 3, 0, -1, 0x21, 0, 0, 0, VAR },
548 /* 26 */ {(uchar *) "storeb", 3, 0, -1, 0x22, 0, 0, 0, VAR },
549 /* 27 */ {(uchar *) "put_prop", 3, 0, -1, 0x23, 0, 0, 0, VAR },
550 /* This is the version of "read" called "sread" internally: */
551 /* 28 */ {(uchar *) "read", 3, 0, -1, 0x24, 0, 0, 0, VAR },
552 /* 29 */ {(uchar *) "print_char", 3, 0, -1, 0x25, 0, 0, 0, VAR },
553 /* 30 */ {(uchar *) "print_num", 3, 0, -1, 0x26, 0, 0, 0, VAR },
554 /* 31 */ {(uchar *) "random", 3, 0, -1, 0x27, St, 0, 0, VAR },
555 /* 32 */ {(uchar *) "push", 3, 0, -1, 0x28, 0, 0, 0, VAR },
556 /* 33 */ {(uchar *) "pull", 3, 5, 6, 0x29, 0, VARIAB, 0, VAR },
557 /* 34 */ {(uchar *) "split_window", 3, 0, -1, 0x2A, 0, 0, 0, VAR },
558 /* 35 */ {(uchar *) "set_window", 3, 0, -1, 0x2B, 0, 0, 0, VAR },
559 /* 36 */ {(uchar *) "output_stream", 3, 0, -1, 0x33, 0, 0, 0, VAR },
560 /* 37 */ {(uchar *) "input_stream", 3, 0, -1, 0x34, 0, 0, 0, VAR },
561 /* 38 */ {(uchar *) "sound_effect", 3, 0, -1, 0x35, 0, 0, 7, VAR },
562 /* 39 */ {(uchar *) "jz", 3, 0, -1, 0x00, Br, 0, 0, ONE },
563 /* 40 */ {(uchar *) "get_sibling", 3, 0, -1, 0x01, St+Br, 0, 0, ONE },
564 /* 41 */ {(uchar *) "get_child", 3, 0, -1, 0x02, St+Br, 0, 0, ONE },
565 /* 42 */ {(uchar *) "get_parent", 3, 0, -1, 0x03, St, 0, 0, ONE },
566 /* 43 */ {(uchar *) "get_prop_len", 3, 0, -1, 0x04, St, 0, 0, ONE },
567 /* 44 */ {(uchar *) "inc", 3, 0, -1, 0x05, 0, VARIAB, 0, ONE },
568 /* 45 */ {(uchar *) "dec", 3, 0, -1, 0x06, 0, VARIAB, 0, ONE },
569 /* 46 */ {(uchar *) "print_addr", 3, 0, -1, 0x07, 0, 0, 0, ONE },
570 /* 47 */ {(uchar *) "remove_obj", 3, 0, -1, 0x09, 0, 0, 0, ONE },
571 /* 48 */ {(uchar *) "print_obj", 3, 0, -1, 0x0A, 0, 0, 0, ONE },
572 /* 49 */ {(uchar *) "ret", 3, 0, -1, 0x0B, Rf, 0, 0, ONE },
573 /* 50 */ {(uchar *) "jump", 3, 0, -1, 0x0C, Rf, LABEL, 0, ONE },
574 /* 51 */ {(uchar *) "print_paddr", 3, 0, -1, 0x0D, 0, 0, 0, ONE },
575 /* 52 */ {(uchar *) "load", 3, 0, -1, 0x0E, St, VARIAB, 0, ONE },
576 /* 53 */ {(uchar *) "not", 3, 3, 0, 0x0F, St, 0, 0, ONE },
577 /* 54 */ {(uchar *) "rtrue", 3, 0, -1, 0x00, Rf, 0, 0,ZERO },
578 /* 55 */ {(uchar *) "rfalse", 3, 0, -1, 0x01, Rf, 0, 0,ZERO },
579 /* 56 */ {(uchar *) "print", 3, 0, -1, 0x02, 0, TEXT, 0,ZERO },
580 /* 57 */ {(uchar *) "print_ret", 3, 0, -1, 0x03, Rf, TEXT, 0,ZERO },
581 /* 58 */ {(uchar *) "nop", 3, 0, -1, 0x04, 0, 0, 0,ZERO },
582 /* 59 */ {(uchar *) "save", 3, 3, 1, 0x05, Br, 0, 0,ZERO },
583 /* 60 */ {(uchar *) "restore", 3, 3, 2, 0x06, Br, 0, 0,ZERO },
584 /* 61 */ {(uchar *) "restart", 3, 0, -1, 0x07, 0, 0, 0,ZERO },
585 /* 62 */ {(uchar *) "ret_popped", 3, 0, -1, 0x08, Rf, 0, 0,ZERO },
586 /* 63 */ {(uchar *) "pop", 3, 4, -1, 0x09, 0, 0, 0,ZERO },
587 /* 64 */ {(uchar *) "quit", 3, 0, -1, 0x0A, Rf, 0, 0,ZERO },
588 /* 65 */ {(uchar *) "new_line", 3, 0, -1, 0x0B, 0, 0, 0,ZERO },
589 /* 66 */ {(uchar *) "show_status", 3, 3, -1, 0x0C, 0, 0, 0,ZERO },
590 /* 67 */ {(uchar *) "verify", 3, 0, -1, 0x0D, Br, 0, 0,ZERO },
592 /* Opcodes introduced in Version 4 */
594 /* 68 */ {(uchar *) "call_2s", 4, 0, -1, 0x19, St, CALL, 0, TWO },
595 /* 69 */ {(uchar *) "call_vs", 4, 0, -1, 0x20, St, CALL, 0, VAR },
596 /* This is the version of "read" called "aread" internally: */
597 /* 70 */ {(uchar *) "read", 4, 0, -1, 0x24, St, 0, 0, VAR },
598 /* 71 */ {(uchar *) "call_vs2", 4, 0, -1, 0x2C, St, CALL, 0,
600 /* 72 */ {(uchar *) "erase_window", 4, 0, -1, 0x2D, 0, 0, 0, VAR },
601 /* 73 */ {(uchar *) "erase_line", 4, 0, -1, 0x2E, 0, 0, 0, VAR },
602 /* 74 */ {(uchar *) "set_cursor", 4, 0, -1, 0x2F, 0, 0, 0, VAR },
603 /* 75 */ {(uchar *) "get_cursor", 4, 0, -1, 0x30, 0, 0, 0, VAR },
604 /* 76 */ {(uchar *) "set_text_style", 4, 0, -1, 0x31, 0, 0, 0, VAR },
605 /* 77 */ {(uchar *) "buffer_mode", 4, 0, -1, 0x32, 0, 0, 0, VAR },
606 /* 78 */ {(uchar *) "read_char", 4, 0, -1, 0x36, St, 0, 0, VAR },
607 /* 79 */ {(uchar *) "scan_table", 4, 0, -1, 0x37, St+Br, 0, 0, VAR },
608 /* 80 */ {(uchar *) "call_1s", 4, 0, -1, 0x08, St, CALL, 0, ONE },
610 /* Opcodes introduced in Version 5 */
612 /* 81 */ {(uchar *) "call_2n", 5, 0, -1, 0x1a, 0, CALL, 0, TWO },
613 /* 82 */ {(uchar *) "set_colour", 5, 0, -1, 0x1b, 0, 0, 6, TWO },
614 /* 83 */ {(uchar *) "throw", 5, 0, -1, 0x1c, 0, 0, 0, TWO },
615 /* 84 */ {(uchar *) "call_vn", 5, 0, -1, 0x39, 0, CALL, 0, VAR },
616 /* 85 */ {(uchar *) "call_vn2", 5, 0, -1, 0x3a, 0, CALL, 0,
618 /* 86 */ {(uchar *) "tokenise", 5, 0, -1, 0x3b, 0, 0, 0, VAR },
619 /* 87 */ {(uchar *) "encode_text", 5, 0, -1, 0x3c, 0, 0, 0, VAR },
620 /* 88 */ {(uchar *) "copy_table", 5, 0, -1, 0x3d, 0, 0, 0, VAR },
621 /* 89 */ {(uchar *) "print_table", 5, 0, -1, 0x3e, 0, 0, 0, VAR },
622 /* 90 */ {(uchar *) "check_arg_count", 5, 0, -1, 0x3f, Br, 0, 0, VAR },
623 /* 91 */ {(uchar *) "call_1n", 5, 0, -1, 0x0F, 0, CALL, 0, ONE },
624 /* 92 */ {(uchar *) "catch", 5, 0, -1, 0x09, St, 0, 0, ZERO },
625 /* 93 */ {(uchar *) "piracy", 5, 0, -1, 0x0F, Br, 0, 0, ZERO },
626 /* 94 */ {(uchar *) "log_shift", 5, 0, -1, 0x02, St, 0, 0, EXT },
627 /* 95 */ {(uchar *) "art_shift", 5, 0, -1, 0x03, St, 0, 0, EXT },
628 /* 96 */ {(uchar *) "set_font", 5, 0, -1, 0x04, St, 0, 0, EXT },
629 /* 97 */ {(uchar *) "save_undo", 5, 0, -1, 0x09, St, 0, 4, EXT },
630 /* 98 */ {(uchar *) "restore_undo", 5, 0, -1, 0x0A, St, 0, 4, EXT },
632 /* Opcodes introduced in Version 6 */
634 /* 99 */ { (uchar *) "draw_picture", 6, 6, -1, 0x05, 0, 0, 3, EXT },
635 /* 100 */ { (uchar *) "picture_data", 6, 6, -1, 0x06, Br, 0, 3, EXT },
636 /* 101 */ { (uchar *) "erase_picture", 6, 6, -1, 0x07, 0, 0, 3, EXT },
637 /* 102 */ { (uchar *) "set_margins", 6, 6, -1, 0x08, 0, 0, 0, EXT },
638 /* 103 */ { (uchar *) "move_window", 6, 6, -1, 0x10, 0, 0, 0, EXT },
639 /* 104 */ { (uchar *) "window_size", 6, 6, -1, 0x11, 0, 0, 0, EXT },
640 /* 105 */ { (uchar *) "window_style", 6, 6, -1, 0x12, 0, 0, 0, EXT },
641 /* 106 */ { (uchar *) "get_wind_prop", 6, 6, -1, 0x13, St, 0, 0, EXT },
642 /* 107 */ { (uchar *) "scroll_window", 6, 6, -1, 0x14, 0, 0, 0, EXT },
643 /* 108 */ { (uchar *) "pop_stack", 6, 6, -1, 0x15, 0, 0, 0, EXT },
644 /* 109 */ { (uchar *) "read_mouse", 6, 6, -1, 0x16, 0, 0, 5, EXT },
645 /* 110 */ { (uchar *) "mouse_window", 6, 6, -1, 0x17, 0, 0, 5, EXT },
646 /* 111 */ { (uchar *) "push_stack", 6, 6, -1, 0x18, Br, 0, 0, EXT },
647 /* 112 */ { (uchar *) "put_wind_prop", 6, 6, -1, 0x19, 0, 0, 0, EXT },
648 /* 113 */ { (uchar *) "print_form", 6, 6, -1, 0x1a, 0, 0, 0, EXT },
649 /* 114 */ { (uchar *) "make_menu", 6, 6, -1, 0x1b, Br, 0, 8, EXT },
650 /* 115 */ { (uchar *) "picture_table", 6, 6, -1, 0x1c, 0, 0, 3, EXT },
652 /* Opcodes introduced in Z-Machine Specification Standard 1.0 */
654 /* 116 */ { (uchar *) "print_unicode", 5, 0, -1, 0x0b, 0, 0, 0, EXT },
655 /* 117 */ { (uchar *) "check_unicode", 5, 0, -1, 0x0c, St, 0, 0, EXT }
658 /* Subsequent forms for opcodes whose meaning changes with version */
660 static opcodez extension_table_z[] =
662 /* 0 */ { (uchar *) "not", 4, 4, 3, 0x0F, St, 0, 0, ONE },
663 /* 1 */ { (uchar *) "save", 4, 4, 4, 0x05, St, 0, 0,ZERO },
664 /* 2 */ { (uchar *) "restore", 4, 4, 5, 0x06, St, 0, 0,ZERO },
665 /* 3 */ { (uchar *) "not", 5, 0, -1, 0x38, St, 0, 0, VAR },
666 /* 4 */ { (uchar *) "save", 5, 0, -1, 0x00, St, 0, 0, EXT },
667 /* 5 */ { (uchar *) "restore", 5, 0, -1, 0x01, St, 0, 0, EXT },
668 /* 6 */ { (uchar *) "pull", 6, 6, -1, 0x29, St, 0, 0, VAR }
671 static opcodez invalid_opcode_z =
672 { (uchar *) "invalid", 0, 0, -1, 0xff, 0, 0, 0, ZERO};
674 static opcodez custom_opcode_z;
676 /* Note that this table assumes that all opcodes have at most two
677 branch-label or store operands, and that if they exist, they are the
678 last operands. Glulx does not actually guarantee this. But it is
679 true for all opcodes in the current Glulx spec, so we will assume
682 Also note that Inform can only compile branches to constant offsets,
683 even though the Glulx machine can handle stack or memory-loaded
684 operands in a branch instruction.
687 static opcodeg opcodes_table_g[] = {
688 { (uchar *) "nop", 0x00, 0, 0, 0 },
689 { (uchar *) "add", 0x10, St, 0, 3 },
690 { (uchar *) "sub", 0x11, St, 0, 3 },
691 { (uchar *) "mul", 0x12, St, 0, 3 },
692 { (uchar *) "div", 0x13, St, 0, 3 },
693 { (uchar *) "mod", 0x14, St, 0, 3 },
694 { (uchar *) "neg", 0x15, St, 0, 2 },
695 { (uchar *) "bitand", 0x18, St, 0, 3 },
696 { (uchar *) "bitor", 0x19, St, 0, 3 },
697 { (uchar *) "bitxor", 0x1A, St, 0, 3 },
698 { (uchar *) "bitnot", 0x1B, St, 0, 2 },
699 { (uchar *) "shiftl", 0x1C, St, 0, 3 },
700 { (uchar *) "sshiftr", 0x1D, St, 0, 3 },
701 { (uchar *) "ushiftr", 0x1E, St, 0, 3 },
702 { (uchar *) "jump", 0x20, Br|Rf, 0, 1 },
703 { (uchar *) "jz", 0x22, Br, 0, 2 },
704 { (uchar *) "jnz", 0x23, Br, 0, 2 },
705 { (uchar *) "jeq", 0x24, Br, 0, 3 },
706 { (uchar *) "jne", 0x25, Br, 0, 3 },
707 { (uchar *) "jlt", 0x26, Br, 0, 3 },
708 { (uchar *) "jge", 0x27, Br, 0, 3 },
709 { (uchar *) "jgt", 0x28, Br, 0, 3 },
710 { (uchar *) "jle", 0x29, Br, 0, 3 },
711 { (uchar *) "jltu", 0x2A, Br, 0, 3 },
712 { (uchar *) "jgeu", 0x2B, Br, 0, 3 },
713 { (uchar *) "jgtu", 0x2C, Br, 0, 3 },
714 { (uchar *) "jleu", 0x2D, Br, 0, 3 },
715 { (uchar *) "call", 0x30, St, 0, 3 },
716 { (uchar *) "return", 0x31, Rf, 0, 1 },
717 { (uchar *) "catch", 0x32, Br|St, 0, 2 },
718 { (uchar *) "throw", 0x33, Rf, 0, 2 },
719 { (uchar *) "tailcall", 0x34, Rf, 0, 2 },
720 { (uchar *) "copy", 0x40, St, 0, 2 },
721 { (uchar *) "copys", 0x41, St, 0, 2 },
722 { (uchar *) "copyb", 0x42, St, 0, 2 },
723 { (uchar *) "sexs", 0x44, St, 0, 2 },
724 { (uchar *) "sexb", 0x45, St, 0, 2 },
725 { (uchar *) "aload", 0x48, St, 0, 3 },
726 { (uchar *) "aloads", 0x49, St, 0, 3 },
727 { (uchar *) "aloadb", 0x4A, St, 0, 3 },
728 { (uchar *) "aloadbit", 0x4B, St, 0, 3 },
729 { (uchar *) "astore", 0x4C, 0, 0, 3 },
730 { (uchar *) "astores", 0x4D, 0, 0, 3 },
731 { (uchar *) "astoreb", 0x4E, 0, 0, 3 },
732 { (uchar *) "astorebit", 0x4F, 0, 0, 3 },
733 { (uchar *) "stkcount", 0x50, St, 0, 1 },
734 { (uchar *) "stkpeek", 0x51, St, 0, 2 },
735 { (uchar *) "stkswap", 0x52, 0, 0, 0 },
736 { (uchar *) "stkroll", 0x53, 0, 0, 2 },
737 { (uchar *) "stkcopy", 0x54, 0, 0, 1 },
738 { (uchar *) "streamchar", 0x70, 0, 0, 1 },
739 { (uchar *) "streamnum", 0x71, 0, 0, 1 },
740 { (uchar *) "streamstr", 0x72, 0, 0, 1 },
741 { (uchar *) "gestalt", 0x0100, St, 0, 3 },
742 { (uchar *) "debugtrap", 0x0101, 0, 0, 1 },
743 { (uchar *) "getmemsize", 0x0102, St, 0, 1 },
744 { (uchar *) "setmemsize", 0x0103, St, 0, 2 },
745 { (uchar *) "jumpabs", 0x0104, Rf, 0, 1 },
746 { (uchar *) "random", 0x0110, St, 0, 2 },
747 { (uchar *) "setrandom", 0x0111, 0, 0, 1 },
748 { (uchar *) "quit", 0x0120, Rf, 0, 0 },
749 { (uchar *) "verify", 0x0121, St, 0, 1 },
750 { (uchar *) "restart", 0x0122, 0, 0, 0 },
751 { (uchar *) "save", 0x0123, St, 0, 2 },
752 { (uchar *) "restore", 0x0124, St, 0, 2 },
753 { (uchar *) "saveundo", 0x0125, St, 0, 1 },
754 { (uchar *) "restoreundo", 0x0126, St, 0, 1 },
755 { (uchar *) "protect", 0x0127, 0, 0, 2 },
756 { (uchar *) "glk", 0x0130, St, 0, 3 },
757 { (uchar *) "getstringtbl", 0x0140, St, 0, 1 },
758 { (uchar *) "setstringtbl", 0x0141, 0, 0, 1 },
759 { (uchar *) "getiosys", 0x0148, St|St2, 0, 2 },
760 { (uchar *) "setiosys", 0x0149, 0, 0, 2 },
761 { (uchar *) "linearsearch", 0x0150, St, 0, 8 },
762 { (uchar *) "binarysearch", 0x0151, St, 0, 8 },
763 { (uchar *) "linkedsearch", 0x0152, St, 0, 7 },
764 { (uchar *) "callf", 0x0160, St, 0, 2 },
765 { (uchar *) "callfi", 0x0161, St, 0, 3 },
766 { (uchar *) "callfii", 0x0162, St, 0, 4 },
767 { (uchar *) "callfiii", 0x0163, St, 0, 5 },
768 { (uchar *) "streamunichar", 0x73, 0, GOP_Unicode, 1 },
769 { (uchar *) "mzero", 0x170, 0, GOP_MemHeap, 2 },
770 { (uchar *) "mcopy", 0x171, 0, GOP_MemHeap, 3 },
771 { (uchar *) "malloc", 0x178, St, GOP_MemHeap, 2 },
772 { (uchar *) "mfree", 0x179, 0, GOP_MemHeap, 1 },
773 { (uchar *) "accelfunc", 0x180, 0, GOP_Acceleration, 2 },
774 { (uchar *) "accelparam", 0x181, 0, GOP_Acceleration, 2 },
775 { (uchar *) "numtof", 0x190, St, GOP_Float, 2 },
776 { (uchar *) "ftonumz", 0x191, St, GOP_Float, 2 },
777 { (uchar *) "ftonumn", 0x192, St, GOP_Float, 2 },
778 { (uchar *) "ceil", 0x198, St, GOP_Float, 2 },
779 { (uchar *) "floor", 0x199, St, GOP_Float, 2 },
780 { (uchar *) "fadd", 0x1A0, St, GOP_Float, 3 },
781 { (uchar *) "fsub", 0x1A1, St, GOP_Float, 3 },
782 { (uchar *) "fmul", 0x1A2, St, GOP_Float, 3 },
783 { (uchar *) "fdiv", 0x1A3, St, GOP_Float, 3 },
784 { (uchar *) "fmod", 0x1A4, St|St2, GOP_Float, 4 },
785 { (uchar *) "sqrt", 0x1A8, St, GOP_Float, 2 },
786 { (uchar *) "exp", 0x1A9, St, GOP_Float, 2 },
787 { (uchar *) "log", 0x1AA, St, GOP_Float, 2 },
788 { (uchar *) "pow", 0x1AB, St, GOP_Float, 3 },
789 { (uchar *) "sin", 0x1B0, St, GOP_Float, 2 },
790 { (uchar *) "cos", 0x1B1, St, GOP_Float, 2 },
791 { (uchar *) "tan", 0x1B2, St, GOP_Float, 2 },
792 { (uchar *) "asin", 0x1B3, St, GOP_Float, 2 },
793 { (uchar *) "acos", 0x1B4, St, GOP_Float, 2 },
794 { (uchar *) "atan", 0x1B5, St, GOP_Float, 2 },
795 { (uchar *) "atan2", 0x1B6, St, GOP_Float, 3 },
796 { (uchar *) "jfeq", 0x1C0, Br, GOP_Float, 4 },
797 { (uchar *) "jfne", 0x1C1, Br, GOP_Float, 4 },
798 { (uchar *) "jflt", 0x1C2, Br, GOP_Float, 3 },
799 { (uchar *) "jfle", 0x1C3, Br, GOP_Float, 3 },
800 { (uchar *) "jfgt", 0x1C4, Br, GOP_Float, 3 },
801 { (uchar *) "jfge", 0x1C5, Br, GOP_Float, 3 },
802 { (uchar *) "jisnan", 0x1C8, Br, GOP_Float, 2 },
803 { (uchar *) "jisinf", 0x1C9, Br, GOP_Float, 2 },
804 { (uchar *) "hasundo", 0x128, St, GOP_ExtUndo, 1 },
805 { (uchar *) "discardundo",0x129, 0, GOP_ExtUndo, 0 },
808 /* The opmacros table is used for fake opcodes. The opcode numbers are
809 ignored; this table is only used for argument parsing. */
810 static opcodeg opmacros_table_g[] = {
811 { (uchar *) "pull", 0, St, 0, 1 },
812 { (uchar *) "push", 0, 0, 0, 1 },
815 static opcodeg custom_opcode_g;
817 static opcodez internal_number_to_opcode_z(int32 i)
820 if (i == -1) return custom_opcode_z;
821 x = opcodes_table_z[i];
822 if (instruction_set_number < x.version1) return invalid_opcode_z;
823 if (x.version2 == 0) return x;
824 if (instruction_set_number <= x.version2) return x;
826 if (i < 0) return invalid_opcode_z;
827 x = extension_table_z[i];
828 if (instruction_set_number < x.version1) return invalid_opcode_z;
829 if (x.version2 == 0) return x;
830 if (instruction_set_number <= x.version2) return x;
831 return extension_table_z[x.extension];
834 static void make_opcode_syntax_z(opcodez opco)
835 { char *p = "", *q = opcode_syntax_string;
836 sprintf(q, "%s", opco.name);
838 { case ONE: p=" <operand>"; break;
839 case TWO: p=" <operand1> <operand2>"; break;
841 case VAR: p=" <0 to 4 operands>"; break;
842 case VAR_LONG: p=" <0 to 8 operands>"; break;
844 switch(opco.op_rules)
845 { case TEXT: sprintf(q+strlen(q), " <text>"); return;
846 case LABEL: sprintf(q+strlen(q), " <label>"); return;
848 sprintf(q+strlen(q), " <variable>");
850 if (opco.op_rules==CALL) sprintf(q+strlen(q), " <routine>");
852 { case ONE: p=""; break;
853 case TWO: p=" <operand>"; break;
855 case VAR: p=" <1 to 4 operands>"; break;
856 case VAR_LONG: p=" <1 to 8 operands>"; break;
860 sprintf(q+strlen(q), "%s", p);
861 if ((opco.flags & St) != 0) sprintf(q+strlen(q), " -> <result-variable>");
862 if ((opco.flags & Br) != 0) sprintf(q+strlen(q), " ?[~]<label>");
865 static opcodeg internal_number_to_opcode_g(int32 i)
868 if (i == -1) return custom_opcode_g;
869 x = opcodes_table_g[i];
873 static opcodeg internal_number_to_opmacro_g(int32 i)
875 return opmacros_table_g[i];
878 static void make_opcode_syntax_g(opcodeg opco)
882 char *q = opcode_syntax_string;
884 sprintf(q, "%s", opco.name);
885 sprintf(q+strlen(q), " <%d operand%s", opco.no,
886 ((opco.no==1) ? "" : "s"));
891 for (ix=0; ix<opco.no; ix++) {
896 if (ix == opco.no-1) {
897 if (opco.flags & Br) {
900 else if (opco.flags & St) {
907 else if (ix == opco.no-2 && (opco.flags & Br) && (opco.flags & St)) {
910 else if (ix == opco.no-2 && (opco.flags & St2)) {
917 sprintf(cx, "%d", ix+1);
921 sprintf(q+strlen(q), ">");
925 /* ========================================================================= */
926 /* The assembler itself does four things: */
928 /* assembles instructions */
929 /* sets label N to the current code position */
930 /* assembles routine headers */
931 /* assembles routine ends */
932 /* ------------------------------------------------------------------------- */
934 /* This is for Z-code only. */
935 static void write_operand(assembly_operand op)
937 if (module_switch && (op.marker != 0))
938 { if ((op.marker != VARIABLE_MV) && (op.type == SHORT_CONSTANT_OT))
939 op.type = LONG_CONSTANT_OT;
943 { case LONG_CONSTANT_OT:
944 byteout(j/256, op.marker); byteout(j%256, 0); return;
945 case SHORT_CONSTANT_OT:
948 else byteout(j, 0x80 + op.marker); return;
950 byteout(j, (module_switch)?(0x80 + op.marker):0); return;
952 case HALFCONSTANT_OT:
953 case BYTECONSTANT_OT:
954 case ZEROCONSTANT_OT:
959 compiler_error("Glulx OT in Z-code assembly operand.");
964 extern void assemblez_instruction(const assembly_instruction *AI)
968 int32 offset, j, topbits=0, types_byte1, types_byte2;
969 int operand_rules, min=0, max=0, no_operands_given, at_seq_point = FALSE;
970 assembly_operand o1, o2;
975 if (execution_never_reaches_here) {
976 if (!(execution_never_reaches_here & EXECSTATE_NOWARN)) {
977 warning("This statement can never be reached");
978 /* only show the warning once */
979 execution_never_reaches_here |= EXECSTATE_NOWARN;
984 offset = zmachine_pc;
988 if (veneer_mode) sequence_point_follows = FALSE;
989 if (sequence_point_follows)
990 { sequence_point_follows = FALSE; at_seq_point = TRUE;
991 if (debugfile_switch)
993 ensure_memory_list_available(&sequence_points_memlist, next_sequence_point+1);
994 sequence_points[next_sequence_point].label = next_label;
995 sequence_points[next_sequence_point].location =
996 statement_debug_location;
997 set_label_offset(next_label++, zmachine_pc);
999 next_sequence_point++;
1002 opco = internal_number_to_opcode_z(AI->internal_number);
1003 if (opco.version1==0)
1004 { error_named("Opcode unavailable in this Z-machine version",
1005 opcode_names.keywords[AI->internal_number]);
1009 operand_rules = opco.op_rules;
1010 execution_never_reaches_here = ((opco.flags & Rf) ? EXECSTATE_UNREACHABLE : EXECSTATE_REACHABLE);
1012 if (opco.flags2_set != 0) flags2_requirements[opco.flags2_set] = 1;
1014 no_operands_given = AI->operand_count;
1016 if ((opco.no == TWO) && ((no_operands_given==3)||(no_operands_given==4)))
1019 /* 1. Write the opcode byte(s) */
1021 start_pc = zcode_ha_size;
1024 { case VAR_LONG: topbits=0xc0; min=0; max=8; break;
1025 case VAR: topbits=0xc0; min=0; max=4; break;
1026 case ZERO: topbits=0xb0; min=0; max=0; break;
1027 case ONE: topbits=0x80; min=1; max=1; break;
1028 case TWO: topbits=0x00; min=2; max=2; break;
1029 case EXT: topbits=0x00; min=0; max=4;
1030 byteout(0xbe, 0); opco.no=VAR; break;
1031 case EXT_LONG: topbits=0x00; min=0; max=8;
1032 byteout(0xbe, 0); opco.no=VAR_LONG; break;
1034 byteout(opco.code + topbits, 0);
1036 operands_pc = zcode_ha_size;
1038 /* 2. Dispose of the special rules LABEL and TEXT */
1040 if (operand_rules==LABEL)
1041 { j = (AI->operand[0]).value;
1043 byteout(j/256, LABEL_MV); byteout(j%256, 0);
1044 goto Instruction_Done;
1047 if (operand_rules==TEXT)
1049 j = translate_text(-1, AI->text, STRCTX_GAMEOPC);
1051 error("text translation failed");
1054 ensure_memory_list_available(&zcode_markers_memlist, zcode_ha_size+j);
1055 ensure_memory_list_available(&zcode_holding_area_memlist, zcode_ha_size+j);
1056 for (i=0; i<j; i++) {
1057 zcode_holding_area[zcode_ha_size] = translated_text[i];
1058 zcode_markers[zcode_ha_size] = 0;
1062 goto Instruction_Done;
1065 /* 3. Sort out the operands */
1067 if ((no_operands_given < min) || (no_operands_given > max))
1068 goto OpcodeSyntaxError;
1074 if (opco.no == VAR_LONG) byteout(0, 0);
1075 types_byte1=0xff; types_byte2=0xff;
1076 for (j=0; j<no_operands_given; j++)
1077 { int multi=0, mask=0;
1079 { case 0: case 4: multi=0x40; mask=0xc0; break;
1080 case 1: case 5: multi=0x10; mask=0x30; break;
1081 case 2: case 6: multi=0x04; mask=0x0c; break;
1082 case 3: case 7: multi=0x01; mask=0x03; break;
1084 o1 = AI->operand[j];
1087 types_byte1 = (types_byte1 & (~mask)) + o1.type*multi;
1089 types_byte2 = (types_byte2 & (~mask)) + o1.type*multi;
1091 zcode_holding_area[operands_pc]=types_byte1;
1092 if (opco.no == VAR_LONG) zcode_holding_area[operands_pc+1]=types_byte2;
1096 o1 = AI->operand[0];
1097 zcode_holding_area[start_pc] += o1.type*0x10;
1102 o1 = AI->operand[0];
1103 o2 = AI->operand[1];
1105 /* Transfer to VAR form if either operand is a long constant */
1107 if ((o1.type==LONG_CONSTANT_OT)||(o2.type==LONG_CONSTANT_OT))
1108 { zcode_holding_area[start_pc] += 0xc0;
1109 byteout(o1.type*0x40 + o2.type*0x10 + 0x0f, 0);
1112 { if (o1.type==VARIABLE_OT) zcode_holding_area[start_pc] += 0x40;
1113 if (o2.type==VARIABLE_OT) zcode_holding_area[start_pc] += 0x20;
1120 /* 4. Assemble a Store destination, if needed */
1122 if ((AI->store_variable_number) != -1)
1123 { if (AI->store_variable_number >= MAX_LOCAL_VARIABLES+MAX_ZCODE_GLOBAL_VARS) {
1124 goto OpcodeSyntaxError;
1126 o1.type = VARIABLE_OT;
1127 o1.value = AI->store_variable_number;
1128 variables[o1.value].usage = TRUE;
1131 /* Note that variable numbers 249 to 255 (i.e. globals 233 to 239)
1132 are used as scratch workspace, so need no mapping between
1133 modules and story files: nor do local variables 0 to 15 */
1135 if ((o1.value >= MAX_LOCAL_VARIABLES) && (o1.value < 249))
1136 o1.marker = VARIABLE_MV;
1140 /* 5. Assemble a branch, if needed */
1142 if (AI->branch_label_number != -1)
1143 { int32 addr, long_form;
1144 int branch_on_true = (AI->branch_flag)?1:0;
1145 mark_label_used(AI->branch_label_number);
1146 switch (AI->branch_label_number)
1147 { case -2: addr = 2; branch_on_true = 0; long_form = 0; break;
1148 /* branch nowhere, carry on */
1149 case -3: addr = 0; long_form = 0; break; /* rfalse on condition */
1150 case -4: addr = 1; long_form = 0; break; /* rtrue on condition */
1152 long_form = 1; addr = AI->branch_label_number;
1155 if (addr > 0x7fff) fatalerror("Too many branch points in routine.");
1157 { byteout(branch_on_true*0x80 + addr/256, BRANCH_MV);
1158 byteout(addr%256, 0);
1161 byteout(branch_on_true*0x80+ 0x40 + (addr&0x3f), 0);
1166 if (asm_trace_level > 0)
1168 printf("%5d +%05lx %3s %-12s ", ErrorReport.line_number,
1169 ((long int) offset),
1170 (at_seq_point)?"<*>":" ", opco.name);
1172 if ((AI->internal_number == print_zc)
1173 || (AI->internal_number == print_ret_zc))
1175 for (i=0;(AI->text)[i]!=0 && i<35; i++) printf("%c",(AI->text)[i]);
1176 if (i == 35) printf("...");
1180 for (i=0; i<AI->operand_count; i++)
1181 { if ((i==0) && (opco.op_rules == VARIAB))
1182 { if ((AI->operand[0]).type == VARIABLE_OT)
1183 { printf("["); print_operand_z(&AI->operand[i], TRUE); }
1185 printf("%s", variable_name((AI->operand[0]).value));
1188 if ((i==0) && (opco.op_rules == LABEL))
1189 { printf("L%d", AI->operand[0].value);
1191 else print_operand_z(&AI->operand[i], TRUE);
1194 if (AI->store_variable_number != -1)
1195 { assembly_operand AO;
1197 AO.type = VARIABLE_OT; AO.value = AI->store_variable_number;
1198 print_operand_z(&AO, TRUE); printf(" ");
1201 switch(AI->branch_label_number)
1202 { case -4: printf("rtrue if %s", (AI->branch_flag)?"TRUE":"FALSE");
1204 case -3: printf("rfalse if %s", (AI->branch_flag)?"TRUE":"FALSE");
1206 case -2: printf("(no branch)"); break;
1209 printf("to L%d if %s", AI->branch_label_number,
1210 (AI->branch_flag)?"TRUE":"FALSE"); break;
1213 if (asm_trace_level>=2)
1214 { for (j=0;start_pc<zcode_ha_size;
1216 { if (j%16==0) printf("\n ");
1217 printf("%02x ", zcode_holding_area[start_pc]);
1223 if (module_switch) flush_link_data();
1229 make_opcode_syntax_z(opco);
1230 error_named("Assembly mistake: syntax is", opcode_syntax_string);
1233 static void assembleg_macro(const assembly_instruction *AI)
1235 /* validate macro syntax first */
1236 int ix, no_operands_given;
1239 opco = internal_number_to_opmacro_g(AI->internal_number);
1240 no_operands_given = AI->operand_count;
1242 if (no_operands_given != opco.no)
1243 goto OpcodeSyntaxError;
1245 for (ix = 0; ix < no_operands_given; ix++) {
1246 int type = AI->operand[ix].type;
1247 if ((opco.flags & St)
1248 && ((!(opco.flags & Br) && (ix == no_operands_given-1))
1249 || ((opco.flags & Br) && (ix == no_operands_given-2)))) {
1250 if (is_constant_ot(type)) {
1251 error("*** assembly macro tried to store to a constant ***");
1252 goto OpcodeSyntaxError;
1255 if ((opco.flags & St2)
1256 && (ix == no_operands_given-2)) {
1257 if (is_constant_ot(type)) {
1258 error("*** assembly macro tried to store to a constant ***");
1259 goto OpcodeSyntaxError;
1264 /* expand the macro */
1265 switch (AI->internal_number) {
1267 assembleg_store(AI->operand[0], stack_pointer);
1271 assembleg_store(stack_pointer, AI->operand[0]);
1275 compiler_error("Invalid Glulx assembly macro");
1283 make_opcode_syntax_g(opco);
1284 error_named("Assembly mistake: syntax is", opcode_syntax_string);
1287 extern void assembleg_instruction(const assembly_instruction *AI)
1292 int no_operands_given, at_seq_point = FALSE;
1298 if (execution_never_reaches_here) {
1299 if (!(execution_never_reaches_here & EXECSTATE_NOWARN)) {
1300 warning("This statement can never be reached");
1301 /* only show the warning once */
1302 execution_never_reaches_here |= EXECSTATE_NOWARN;
1307 offset = zmachine_pc;
1311 if (veneer_mode) sequence_point_follows = FALSE;
1312 if (sequence_point_follows)
1313 { sequence_point_follows = FALSE; at_seq_point = TRUE;
1314 if (debugfile_switch)
1316 ensure_memory_list_available(&sequence_points_memlist, next_sequence_point+1);
1317 sequence_points[next_sequence_point].label = next_label;
1318 sequence_points[next_sequence_point].location =
1319 statement_debug_location;
1320 set_label_offset(next_label++, zmachine_pc);
1322 next_sequence_point++;
1325 opco = internal_number_to_opcode_g(AI->internal_number);
1327 execution_never_reaches_here = ((opco.flags & Rf) ? EXECSTATE_UNREACHABLE : EXECSTATE_REACHABLE);
1329 if (opco.op_rules & GOP_Unicode) {
1330 uses_unicode_features = TRUE;
1332 if (opco.op_rules & GOP_MemHeap) {
1333 uses_memheap_features = TRUE;
1335 if (opco.op_rules & GOP_Acceleration) {
1336 uses_acceleration_features = TRUE;
1338 if (opco.op_rules & GOP_Float) {
1339 uses_float_features = TRUE;
1341 if (opco.op_rules & GOP_ExtUndo) {
1342 uses_extundo_features = TRUE;
1345 no_operands_given = AI->operand_count;
1347 /* 1. Write the opcode byte(s) */
1349 start_pc = zcode_ha_size;
1351 if (opco.code < 0x80) {
1352 byteout(opco.code, 0);
1354 else if (opco.code < 0x4000) {
1355 byteout(((opco.code >> 8) & 0xFF) | 0x80, 0);
1356 byteout((opco.code & 0xFF), 0);
1359 byteout(((opco.code >> 24) & 0xFF) | 0xC0, 0);
1360 byteout(((opco.code >> 16) & 0xFF), 0);
1361 byteout(((opco.code >> 8) & 0xFF), 0);
1362 byteout(((opco.code) & 0xFF), 0);
1365 /* ... and the operand addressing modes. There's one byte for
1366 every two operands (rounded up). We write zeroes for now;
1367 when the operands are written, we'll go back and fix them. */
1369 opmodes_pc = zcode_ha_size;
1371 for (ix=0; ix<opco.no; ix+=2) {
1375 /* 2. Dispose of the special rules */
1376 /* There aren't any in Glulx. */
1378 /* 3. Sort out the operands */
1380 if (no_operands_given != opco.no) {
1381 goto OpcodeSyntaxError;
1384 for (ix=0; ix<no_operands_given; ix++) {
1385 int marker = AI->operand[ix].marker;
1386 int type = AI->operand[ix].type;
1387 k = AI->operand[ix].value;
1389 if ((opco.flags & Br) && (ix == no_operands_given-1)) {
1390 if (!(marker >= BRANCH_MV && marker < BRANCHMAX_MV)) {
1391 compiler_error("Assembling branch without BRANCH_MV marker");
1392 goto OpcodeSyntaxError;
1396 k = 2; /* branch no-op */
1397 type = BYTECONSTANT_OT;
1401 k = 0; /* branch return 0 */
1402 type = ZEROCONSTANT_OT;
1406 k = 1; /* branch return 1 */
1407 type = BYTECONSTANT_OT;
1411 /* branch to label k */
1412 j = (zcode_ha_size - opmodes_pc);
1414 marker = BRANCH_MV + j;
1415 if (!(marker >= BRANCH_MV && marker < BRANCHMAX_MV)) {
1416 error("*** branch marker too far from opmode byte ***");
1417 goto OpcodeSyntaxError;
1421 if ((opco.flags & St)
1422 && ((!(opco.flags & Br) && (ix == no_operands_given-1))
1423 || ((opco.flags & Br) && (ix == no_operands_given-2)))) {
1424 if (type == BYTECONSTANT_OT || type == HALFCONSTANT_OT
1425 || type == CONSTANT_OT) {
1426 error("*** instruction tried to store to a constant ***");
1427 goto OpcodeSyntaxError;
1430 if ((opco.flags & St2)
1431 && (ix == no_operands_given-2)) {
1432 if (type == BYTECONSTANT_OT || type == HALFCONSTANT_OT
1433 || type == CONSTANT_OT) {
1434 error("*** instruction tried to store to a constant ***");
1435 goto OpcodeSyntaxError;
1439 if (marker && (type == HALFCONSTANT_OT
1440 || type == BYTECONSTANT_OT
1441 || type == ZEROCONSTANT_OT)) {
1442 compiler_error("Assembling marker in less than 32-bit constant.");
1443 /* Actually we should store marker|0x80 for a byte constant,
1444 but let's hold off on that. */
1448 case LONG_CONSTANT_OT:
1449 case SHORT_CONSTANT_OT:
1452 compiler_error("Z-code OT in Glulx assembly operand.");
1456 byteout((k >> 24) & 0xFF, marker);
1457 byteout((k >> 16) & 0xFF, 0);
1458 byteout((k >> 8) & 0xFF, 0);
1459 byteout((k & 0xFF), 0);
1461 case HALFCONSTANT_OT:
1463 byteout((k >> 8) & 0xFF, marker);
1464 byteout((k & 0xFF), 0);
1466 case BYTECONSTANT_OT:
1468 byteout((k & 0xFF), marker);
1470 case ZEROCONSTANT_OT:
1473 case DEREFERENCE_OT:
1475 byteout((k >> 24) & 0xFF, marker);
1476 byteout((k >> 16) & 0xFF, 0);
1477 byteout((k >> 8) & 0xFF, 0);
1478 byteout((k & 0xFF), 0);
1481 /* Global variable -- a constant address. */
1482 k -= MAX_LOCAL_VARIABLES;
1483 if (/* DISABLES CODE */ (0)) {
1484 /* We could write the value as a marker and patch it later... */
1486 byteout(((k) >> 24) & 0xFF, VARIABLE_MV);
1487 byteout(((k) >> 16) & 0xFF, 0);
1488 byteout(((k) >> 8) & 0xFF, 0);
1489 byteout(((k) & 0xFF), 0);
1492 /* ...but it's more efficient to write it as a RAM operand,
1493 which can be 1, 2, or 4 bytes. Remember that global variables
1494 are the very first thing in RAM. */
1495 k = k * 4; /* each variable is four bytes */
1498 byteout(((k) & 0xFF), 0);
1500 else if (k <= 65535) {
1502 byteout(((k) >> 8) & 0xFF, 0);
1503 byteout(((k) & 0xFF), 0);
1507 byteout(((k) >> 24) & 0xFF, 0);
1508 byteout(((k) >> 16) & 0xFF, 0);
1509 byteout(((k) >> 8) & 0xFF, 0);
1510 byteout(((k) & 0xFF), 0);
1516 /* Stack-pointer magic variable */
1520 /* Local variable -- a byte or short offset from the
1521 frame pointer. It's an unsigned offset, so we can
1522 fit up to long 63 (offset 4*63) in a byte. */
1525 byteout((k-1)*4, 0);
1529 byteout((((k-1)*4) >> 8) & 0xFF, 0);
1530 byteout(((k-1)*4) & 0xFF, 0);
1541 zcode_holding_area[opmodes_pc+ix/2] |= j;
1544 /* Print assembly trace. */
1545 if (asm_trace_level > 0) {
1547 printf("%5d +%05lx %3s %-12s ", ErrorReport.line_number,
1548 ((long int) offset),
1549 (at_seq_point)?"<*>":" ", opco.name);
1550 for (i=0; i<AI->operand_count; i++) {
1551 if ((opco.flags & Br) && (i == opco.no-1)) {
1552 if (AI->operand[i].value == -4)
1554 else if (AI->operand[i].value == -3)
1555 printf("to rfalse");
1557 printf("to L%d", AI->operand[i].value);
1560 print_operand_g(&AI->operand[i], TRUE);
1565 if (asm_trace_level>=2) {
1567 start_pc<zcode_ha_size;
1569 if (j%16==0) printf("\n ");
1570 if (/* DISABLES CODE */ (0)) {
1571 printf("%02x ", zcode_holding_area[start_pc]);
1574 printf("%02x", zcode_holding_area[start_pc]);
1575 if (zcode_markers[start_pc])
1576 printf("{%02x}", zcode_markers[start_pc]);
1584 if (module_switch) flush_link_data();
1590 make_opcode_syntax_g(opco);
1591 error_named("Assembly mistake: syntax is", opcode_syntax_string);
1594 /* Set up this label at zmachine_pc.
1595 This resets the execution_never_reaches_here flag, since every label
1596 is assumed to be reachable.
1597 However, if STRIP_UNREACHABLE_LABELS and EXECSTATE_ENTIRE are both set,
1598 that's not true. The entire statement is being skipped, so we can safely
1599 skip all labels within it.
1600 (If STRIP_UNREACHABLE_LABELS is not set, the ENTIRE flag is ignored.)
1602 extern void assemble_label_no(int n)
1604 if ((execution_never_reaches_here & EXECSTATE_ENTIRE) && STRIP_UNREACHABLE_LABELS) {
1605 /* We're not going to compile this label at all. Set a negative
1606 offset, which will trip an error if this label is jumped to. */
1607 set_label_offset(n, -1);
1611 if (asm_trace_level > 0)
1612 printf("%5d +%05lx .L%d\n", ErrorReport.line_number,
1613 ((long int) zmachine_pc), n);
1614 set_label_offset(n, zmachine_pc);
1615 execution_never_reaches_here = EXECSTATE_REACHABLE;
1618 /* This is the same as assemble_label_no, except we only set up the label
1619 if there has been a forward branch to it.
1620 Returns whether the label is created.
1621 Only use this for labels which never have backwards branches!
1623 extern int assemble_forward_label_no(int n)
1625 if (n >= 0 && n < labeluse_size && labeluse[n]) {
1626 assemble_label_no(n);
1630 /* There were no forward branches to this label and we promise
1631 there will be no backwards branches to it. Set a negative
1632 offset, which will trip an error if we break our promise. */
1633 set_label_offset(n, -1);
1638 extern void define_symbol_label(int symbol)
1640 int label = symbols[symbol].value;
1641 /* We may be creating a new label (label = next_label) or filling in
1642 the value of an old one. So we call ensure. */
1643 ensure_memory_list_available(&labels_memlist, label+1);
1644 labels[label].symbol = symbol;
1647 extern int32 assemble_routine_header(int no_locals,
1648 int routine_asterisked, char *name, int embedded_flag, int the_symbol)
1650 int stackargs = FALSE;
1653 execution_never_reaches_here = EXECSTATE_REACHABLE;
1655 routine_locals = no_locals;
1657 ensure_memory_list_available(&variables_memlist, MAX_LOCAL_VARIABLES);
1658 for (i=0; i<MAX_LOCAL_VARIABLES; i++) variables[i].usage = FALSE;
1661 && strcmpcis(local_variable_names[0].text, "_vararg_count")==0) {
1665 if (veneer_mode) routine_starts_line = blank_brief_location;
1666 else routine_starts_line = get_brief_location(&ErrorReport);
1668 if (asm_trace_level > 0)
1669 { printf("\n%5d +%05lx [ %s ", ErrorReport.line_number,
1670 ((long int) zmachine_pc), name);
1671 for (i=1; i<=no_locals; i++) printf("%s ", variable_name(i));
1675 routine_start_pc = zmachine_pc;
1677 if (track_unused_routines) {
1678 /* The name of an embedded function is in a temporary buffer,
1679 so we shouldn't keep a reference to it. (It is sad that we
1680 have to know this here.) */
1681 char *funcname = name;
1683 funcname = "<embedded>";
1685 df_note_function_start(funcname, zmachine_pc, embedded_flag,
1686 routine_starts_line);
1689 routine_symbol = the_symbol;
1690 name_length = strlen(name) + 1;
1691 ensure_memory_list_available(¤t_routine_name, name_length);
1692 strncpy(current_routine_name.data, name, name_length);
1694 /* Update the routine counter */
1698 /* Actually assemble the routine header into the code area; note */
1699 /* Inform doesn't support the setting of local variables to default */
1700 /* values other than 0 in V3 and V4. (In V5+ the Z-Machine doesn't */
1701 /* provide the possibility in any case.) */
1706 warning("Z-code does not support stack-argument function definitions.");
1708 byteout(no_locals, 0);
1710 /* Not the packed address, but the scaled offset from code area start: */
1712 rv = zmachine_pc/scale_factor;
1714 if (instruction_set_number<5)
1715 for (i=0; i<no_locals; i++) { byteout(0,0); byteout(0,0); }
1717 next_label = 0; next_sequence_point = 0; last_label = -1;
1720 /* Compile code to print out text like "a=3, b=4, c=5" when the */
1721 /* function is called, if it's required. */
1723 if ((routine_asterisked) || (define_INFIX_switch))
1724 { char fnt[256]; assembly_operand PV, RFA, CON, STP, SLF; int ln, ln2;
1729 if (define_INFIX_switch)
1732 { SLF.value = 251; SLF.type = VARIABLE_OT; SLF.marker = 0;
1733 CON.value = 0; CON.type = SHORT_CONSTANT_OT; CON.marker = 0;
1734 assemblez_2_branch(test_attr_zc, SLF, CON, ln2, FALSE);
1737 { i = no_named_routines++;
1738 ensure_memory_list_available(&named_routine_symbols_memlist, no_named_routines);
1739 named_routine_symbols[i] = the_symbol;
1740 CON.value = i/8; CON.type = LONG_CONSTANT_OT; CON.marker = 0;
1741 RFA.value = routine_flags_array_SC;
1742 RFA.type = LONG_CONSTANT_OT; RFA.marker = INCON_MV;
1743 STP.value = 0; STP.type = VARIABLE_OT; STP.marker = 0;
1744 assemblez_2_to(loadb_zc, RFA, CON, STP);
1745 CON.value = (1 << (i%8)); CON.type = SHORT_CONSTANT_OT;
1746 assemblez_2_to(and_zc, STP, CON, STP);
1747 assemblez_1_branch(jz_zc, STP, ln2, TRUE);
1750 sprintf(fnt, "[ %s(", name);
1751 AI.text = fnt; assemblez_0(print_zc);
1752 for (i=1; (i<=7)&&(i<=no_locals); i++)
1753 { if (version_number >= 5)
1754 { PV.type = SHORT_CONSTANT_OT;
1755 PV.value = i; PV.marker = 0;
1756 assemblez_1_branch(check_arg_count_zc, PV, ln, FALSE);
1758 sprintf(fnt, "%s%s = ", (i==1)?"":", ", variable_name(i));
1759 AI.text = fnt; assemblez_0(print_zc);
1760 PV.type = VARIABLE_OT; PV.value = i; PV.marker = 0;
1761 assemblez_1(print_num_zc, PV);
1763 assemble_label_no(ln);
1764 sprintf(fnt, ") ]^"); AI.text = fnt;
1765 assemblez_0(print_zc);
1767 assemble_label_no(ln2);
1775 byteout(0xC0, 0); /* Glulx type byte for function */
1777 byteout(0xC1, 0); /* Glulx type byte for function */
1779 /* Now the locals format list. This is simple; we only use
1780 four-byte locals. That's a single pair, unless we have more
1781 than 255 locals, or none at all. */
1791 /* Terminate the list with a (0, 0) pair. */
1796 /* The top stack value is the number of function arguments. Let's
1797 move that into the first local, which is _vararg_count. */
1798 /* @copy sp _vararg_count; */
1799 byteout(0x40, 0); byteout(0x98, 0); byteout(0x00, 0);
1802 next_label = 0; next_sequence_point = 0; last_label = -1;
1805 if ((routine_asterisked) || (define_INFIX_switch)) {
1808 assembly_operand AO, AO2;
1809 if (define_INFIX_switch) {
1810 /* This isn't supported */
1811 if (embedded_flag) {
1814 i = no_named_routines++;
1815 ensure_memory_list_available(&named_routine_symbols_memlist, no_named_routines);
1816 named_routine_symbols[i] = the_symbol;
1819 sprintf(fnt, "[ %s(", name);
1820 AO.marker = STRING_MV;
1821 AO.type = CONSTANT_OT;
1822 AO.value = compile_string(fnt, STRCTX_INFIX);
1823 assembleg_1(streamstr_gc, AO);
1826 for (ix=1; ix<=no_locals; ix++) {
1827 sprintf(fnt, "%s%s = ", (ix==1)?"":", ", variable_name(ix));
1828 AO.marker = STRING_MV;
1829 AO.type = CONSTANT_OT;
1830 AO.value = compile_string(fnt, STRCTX_INFIX);
1831 assembleg_1(streamstr_gc, AO);
1833 AO.type = LOCALVAR_OT;
1835 assembleg_1(streamnum_gc, AO);
1839 int lntop, lnbottom;
1840 sprintf(fnt, "%s = ", variable_name(1));
1841 AO.marker = STRING_MV;
1842 AO.type = CONSTANT_OT;
1843 AO.value = compile_string(fnt, STRCTX_INFIX);
1844 assembleg_1(streamstr_gc, AO);
1846 AO.type = LOCALVAR_OT;
1848 assembleg_1(streamnum_gc, AO);
1849 AO2.type = BYTECONSTANT_OT;
1852 assembleg_1(streamchar_gc, AO2);
1853 AO2.type = BYTECONSTANT_OT;
1856 /* for (temp_var4=0 : temp_var4<_vararg_count : temp_var4++) {
1858 @stkpeek temp_var4 sp;
1862 assembleg_store(temp_var4, zero_operand);
1863 lntop = next_label++;
1864 lnbottom = next_label++;
1865 assemble_label_no(lntop);
1866 assembleg_2_branch(jge_gc, temp_var4, AO, lnbottom); /* AO is _vararg_count */
1867 assembleg_1(streamchar_gc, AO2); /* AO2 is space */
1868 assembleg_2(stkpeek_gc, temp_var4, stack_pointer);
1869 assembleg_1(streamnum_gc, stack_pointer);
1870 assembleg_3(add_gc, temp_var4, one_operand, temp_var4);
1871 assembleg_0_branch(jump_gc, lntop);
1872 assemble_label_no(lnbottom);
1875 AO.marker = STRING_MV;
1876 AO.type = CONSTANT_OT;
1877 AO.value = compile_string(") ]^", STRCTX_INFIX);
1878 assembleg_1(streamstr_gc, AO);
1885 void assemble_routine_end(int embedded_flag, debug_locations locations)
1888 /* No marker is made in the Z-machine's code area to indicate the */
1889 /* end of a routine. Instead, we simply assemble a return opcode if */
1890 /* need be (it won't be if the last instruction was, say, a "quit"). */
1891 /* The return value is true (1) for normal routines, false (0) for */
1892 /* embedded routines (e.g. the library uses this for "before" */
1895 if (!execution_never_reaches_here)
1898 if (embedded_flag) assemblez_0(rfalse_zc);
1899 else assemblez_0(rtrue_zc);
1902 assembly_operand AO;
1907 assembleg_1(return_gc, AO);
1911 /* Dump the contents of the current routine into longer-term Z-code
1915 transfer_routine_z();
1917 transfer_routine_g();
1919 if (track_unused_routines)
1920 df_note_function_end(zmachine_pc);
1922 /* Tell the debugging file about the routine just ended. */
1924 if (debugfile_switch)
1926 char *routine_name = current_routine_name.data;
1927 debug_file_printf("<routine>");
1930 ("<identifier artificial=\"true\">%s</identifier>",
1933 else if (symbols[routine_symbol].flags & REPLACE_SFLAG)
1934 { /* The symbol type will be set to ROUTINE_T once the replaced
1935 version has been given; if it is already set, we must be dealing
1936 with a replacement, and we can use the routine name as-is.
1937 Otherwise we look for a rename. And if that doesn't work, we
1938 fall back to an artificial identifier. */
1939 if (symbols[routine_symbol].type == ROUTINE_T)
1940 { /* Optional because there may be further replacements. */
1941 write_debug_optional_identifier(routine_symbol);
1943 else if (find_symbol_replacement(&routine_symbol))
1945 ("<identifier>%s</identifier>", symbols[routine_symbol].name);
1949 ("<identifier artificial=\"true\">%s (replaced)"
1954 { debug_file_printf("<identifier>%s</identifier>", routine_name);
1956 debug_file_printf("<value>");
1958 { write_debug_code_backpatch(routine_start_pc);
1960 { write_debug_packed_code_backpatch(routine_start_pc);
1962 debug_file_printf("</value>");
1963 debug_file_printf("<address>");
1964 write_debug_code_backpatch(routine_start_pc);
1965 debug_file_printf("</address>");
1967 ("<byte-count>%d</byte-count>", zmachine_pc - routine_start_pc);
1968 write_debug_locations(locations);
1969 for (i = 1; i <= routine_locals; ++i)
1970 { debug_file_printf("<local-variable>");
1971 debug_file_printf("<identifier>%s</identifier>", variable_name(i));
1974 ("<frame-offset>%d</frame-offset>", 4 * (i - 1));
1977 { debug_file_printf("<index>%d</index>", i);
1979 debug_file_printf("</local-variable>");
1981 for (i = 0; i < next_sequence_point; ++i)
1982 { debug_file_printf("<sequence-point>");
1983 debug_file_printf("<address>");
1984 write_debug_code_backpatch
1985 (labels[sequence_points[i].label].offset);
1986 debug_file_printf("</address>");
1987 write_debug_location(sequence_points[i].location);
1988 debug_file_printf("</sequence-point>");
1990 debug_file_printf("</routine>");
1993 /* Issue warnings about any local variables not used in the routine. */
1995 for (i=1; i<=routine_locals; i++)
1996 if (!(variables[i].usage))
1997 dbnu_warning("Local variable", variable_name(i),
1998 routine_starts_line);
2000 for (i=0; i<next_label; i++)
2001 { int j = labels[i].symbol;
2003 { if (symbols[j].flags & CHANGE_SFLAG)
2004 error_named_at("Routine contains no such label as",
2005 symbols[j].name, symbols[j].line);
2007 if ((symbols[j].flags & USED_SFLAG) == 0)
2008 dbnu_warning("Label", symbols[j].name, symbols[j].line);
2009 symbols[j].type = CONSTANT_T;
2010 symbols[j].flags = UNKNOWN_SFLAG;
2013 no_sequence_points += next_sequence_point;
2014 next_label = 0; next_sequence_point = 0;
2016 execution_never_reaches_here = EXECSTATE_REACHABLE;
2019 /* ------------------------------------------------------------------------- */
2020 /* Called when the holding area contains an entire routine of code: */
2021 /* backpatches the labels, issues module markers, then dumps the routine */
2022 /* into longer-term storage. */
2024 /* Note that in the code received, all branches have long form, and their */
2025 /* contents are not an offset but the label numbers they branch to. */
2026 /* Similarly, LABEL operands (those of "jump" instructions) are label */
2027 /* numbers. So this routine must change the label numbers to offsets, */
2028 /* slimming the code down as it does so to take advantage of short-form */
2029 /* branch operands where possible. */
2031 /* zcode_ha_size is the number of bytes added since the last transfer */
2032 /* call. So we transfer starting at (zmachine_pc - zcode_ha_size). But we */
2033 /* might transfer fewer bytes than that. */
2034 /* ------------------------------------------------------------------------- */
2036 static void transfer_routine_z(void)
2037 { int32 i, j, pc, new_pc, label, long_form, offset_of_next, addr,
2038 branch_on_true, rstart_pc;
2041 adjusted_pc = zmachine_pc - zcode_ha_size; rstart_pc = adjusted_pc;
2043 if (asm_trace_level >= 3)
2044 { printf("Backpatching routine at %05lx: initial size %d, %d labels\n",
2045 (long int) adjusted_pc, zcode_ha_size, next_label);
2048 /* (1) Scan through for branches and make short/long decisions in each
2049 case. Mark omitted bytes (2nd bytes in branches converted to
2050 short form) with DELETED_MV.
2051 We also look for jumps that can be entirely eliminated (because
2052 they are jumping to the very next instruction). The opcode and
2053 both label bytes get DELETED_MV. */
2055 for (i=0, pc=adjusted_pc; i<zcode_ha_size; i++, pc++)
2056 { if (zcode_markers[i] == BRANCH_MV)
2057 { if (asm_trace_level >= 4)
2058 printf("Branch detected at offset %04x\n", pc);
2059 j = (256*zcode_holding_area[i] + zcode_holding_area[i+1]) & 0x7fff;
2060 if (asm_trace_level >= 4)
2061 printf("...To label %d, which is %d from here\n",
2062 j, labels[j].offset-pc);
2063 if ((labels[j].offset >= pc+2) && (labels[j].offset < pc+64))
2064 { if (asm_trace_level >= 4) printf("...Using short form\n");
2065 zcode_markers[i+1] = DELETED_MV;
2068 else if (zcode_markers[i] == LABEL_MV)
2070 if (asm_trace_level >= 4)
2071 printf("Jump detected at offset %04x\n", pc);
2072 j = (256*zcode_holding_area[i] + zcode_holding_area[i+1]) & 0x7fff;
2073 if (asm_trace_level >= 4)
2074 printf("...To label %d, which is %d from here\n",
2075 j, labels[j].offset-pc);
2076 if (labels[j].offset-pc == 2 && i >= 1 && zcode_holding_area[i-1] == opcodes_table_z[jump_zc].code+128) {
2077 if (asm_trace_level >= 4) printf("...Deleting jump\n");
2078 zcode_markers[i-1] = DELETED_MV;
2079 zcode_markers[i] = DELETED_MV;
2080 zcode_markers[i+1] = DELETED_MV;
2085 /* (2) Calculate the new positions of the labels. Note that since the
2086 long/short decision was taken on the basis of the old labels,
2087 and since the new labels are slightly closer together because
2088 of branch bytes deleted, there may be a few further branch
2089 optimisations which are possible but which have been missed
2090 (if two labels move inside the "short" range as a result of
2091 a previous optimisation). However, this is acceptably uncommon. */
2094 { if (asm_trace_level >= 4)
2095 { printf("Opening label: %d\n", first_label);
2096 for (i=0;i<next_label;i++)
2097 printf("Label %d offset %04x next -> %d previous -> %d\n",
2098 i, labels[i].offset, labels[i].next, labels[i].prev);
2101 /* label will advance through the linked list as pc increases. */
2102 for (i=0, pc=adjusted_pc, new_pc=adjusted_pc, label = first_label;
2103 i<zcode_ha_size; i++, pc++)
2104 { while ((label != -1) && (labels[label].offset == pc))
2105 { if (asm_trace_level >= 4)
2106 printf("Position of L%d corrected from %04x to %04x\n",
2107 label, labels[label].offset, new_pc);
2108 labels[label].offset = new_pc;
2109 label = labels[label].next;
2111 if (zcode_markers[i] != DELETED_MV) new_pc++;
2115 /* (3) As we are transferring, replace the label numbers in branch
2116 operands with offsets to those labels. Also issue markers, now
2117 that we know where they occur in the final Z-code area. */
2119 ensure_memory_list_available(&zcode_area_memlist, adjusted_pc+zcode_ha_size);
2121 for (i=0, new_pc=adjusted_pc; i<zcode_ha_size; i++)
2122 { switch(zcode_markers[i])
2124 long_form = 1; if (zcode_markers[i+1] == DELETED_MV) long_form = 0;
2126 j = (256*zcode_holding_area[i] + zcode_holding_area[i+1]) & 0x7fff;
2127 branch_on_true = ((zcode_holding_area[i]) & 0x80);
2128 offset_of_next = new_pc + long_form + 1;
2130 if (labels[j].offset < 0) {
2131 error("Attempt to jump to an unreachable label");
2135 addr = labels[j].offset - offset_of_next + 2;
2137 if (addr<-0x2000 || addr>0x1fff)
2138 fatalerror("Branch out of range: divide the routine up?");
2139 if (addr<0) addr+=(int32) 0x10000L;
2143 { zcode_holding_area[i] = branch_on_true + addr/256;
2144 zcode_holding_area[i+1] = addr%256;
2148 { compiler_error("Label out of range for branch");
2149 printf("Addr is %04x\n", addr);
2151 zcode_holding_area[i] = branch_on_true + 0x40 + (addr&0x3f);
2153 zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++;
2157 j = 256*zcode_holding_area[i] + zcode_holding_area[i+1];
2158 if (labels[j].offset < 0) {
2159 error("Attempt to jump to an unreachable label");
2163 addr = labels[j].offset - new_pc;
2165 if (addr<-0x8000 || addr>0x7fff)
2166 fatalerror("Jump out of range: divide the routine up?");
2167 if (addr<0) addr += (int32) 0x10000L;
2168 zcode_holding_area[i] = addr/256;
2169 zcode_holding_area[i+1] = addr%256;
2170 zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++;
2177 switch(zcode_markers[i] & 0x7f)
2178 { case NULL_MV: break;
2183 if (!module_switch) break;
2185 if ((zcode_markers[i] & 0x7f) > LARGEST_BPATCH_MV)
2186 { compiler_error("Illegal code backpatch value");
2187 printf("Illegal value of %02x at PC = %04x\n",
2188 zcode_markers[i] & 0x7f, new_pc);
2192 if (bpatch_trace_setting >= 2)
2193 printf("BP added: MV %d PC %04x\n", zcode_markers[i], new_pc);
2195 ensure_memory_list_available(&zcode_backpatch_table_memlist, zcode_backpatch_size+3);
2196 zcode_backpatch_table[zcode_backpatch_size++] = zcode_markers[i] + 32*(new_pc/65536);
2197 zcode_backpatch_table[zcode_backpatch_size++] = (new_pc/256)%256;
2198 zcode_backpatch_table[zcode_backpatch_size++] = new_pc%256;
2201 zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++;
2206 /* Consistency check */
2207 if (new_pc - rstart_pc > zcode_ha_size || adjusted_pc != new_pc)
2209 fatalerror("Optimisation increased routine length or failed to match; should not happen");
2212 if (asm_trace_level >= 3)
2213 { printf("After branch optimisation, routine length is %d bytes\n",
2214 new_pc - rstart_pc);
2217 /* Insert null bytes if necessary to ensure the next routine address is */
2218 /* expressible as a packed address */
2220 ensure_memory_list_available(&zcode_area_memlist, adjusted_pc+2*scale_factor);
2222 if (oddeven_packing_switch)
2223 while ((adjusted_pc%(scale_factor*2))!=0) zcode_area[adjusted_pc++] = 0;
2225 while ((adjusted_pc%scale_factor)!=0) zcode_area[adjusted_pc++] = 0;
2227 zmachine_pc = adjusted_pc;
2231 static void transfer_routine_g(void)
2232 { int32 i, j, pc, new_pc, label, form_len, offset_of_next, addr,
2236 adjusted_pc = zmachine_pc - zcode_ha_size; rstart_pc = adjusted_pc;
2238 if (asm_trace_level >= 3)
2239 { printf("Backpatching routine at %05lx: initial size %d, %d labels\n",
2240 (long int) adjusted_pc, zcode_ha_size, next_label);
2243 /* (1) Scan through for branches and make short/long decisions in each
2244 case. Mark omitted bytes (bytes 2-4 in branches converted to
2245 short form) with DELETED_MV.
2246 We also look for branches that can be entirely eliminated (because
2247 they are jumping to the very next instruction). The opcode and
2248 all label bytes get DELETED_MV. */
2250 for (i=0, pc=adjusted_pc; i<zcode_ha_size; i++, pc++) {
2251 if (zcode_markers[i] >= BRANCH_MV && zcode_markers[i] < BRANCHMAX_MV) {
2252 int opmodeoffset = (zcode_markers[i] - BRANCH_MV);
2254 if (asm_trace_level >= 4)
2255 printf("Branch detected at offset %04x\n", pc);
2256 j = ((zcode_holding_area[i] << 24)
2257 | (zcode_holding_area[i+1] << 16)
2258 | (zcode_holding_area[i+2] << 8)
2259 | (zcode_holding_area[i+3]));
2260 offset_of_next = pc + 4;
2261 addr = (labels[j].offset - offset_of_next) + 2;
2262 opmodebyte = i - ((opmodeoffset+1)/2);
2263 if (asm_trace_level >= 4)
2264 printf("...To label %d, which is (%d-2) = %d from here\n",
2265 j, addr, labels[j].offset - offset_of_next);
2266 if (addr == 2 && i >= 2 && opmodeoffset == 2 && zcode_holding_area[opmodebyte-1] == opcodes_table_g[jump_gc].code) {
2267 if (asm_trace_level >= 4) printf("...Deleting branch\n");
2268 zcode_markers[i-2] = DELETED_MV;
2269 zcode_markers[i-1] = DELETED_MV;
2270 zcode_markers[i] = DELETED_MV;
2271 zcode_markers[i+1] = DELETED_MV;
2272 zcode_markers[i+2] = DELETED_MV;
2273 zcode_markers[i+3] = DELETED_MV;
2275 else if (addr >= -0x80 && addr < 0x80) {
2276 if (asm_trace_level >= 4) printf("...Byte form\n");
2277 zcode_markers[i+1] = DELETED_MV;
2278 zcode_markers[i+2] = DELETED_MV;
2279 zcode_markers[i+3] = DELETED_MV;
2280 if ((opmodeoffset & 1) == 0)
2281 zcode_holding_area[opmodebyte] =
2282 (zcode_holding_area[opmodebyte] & 0xF0) | 0x01;
2284 zcode_holding_area[opmodebyte] =
2285 (zcode_holding_area[opmodebyte] & 0x0F) | 0x10;
2287 else if (addr >= -0x8000 && addr < 0x8000) {
2288 if (asm_trace_level >= 4) printf("...Short form\n");
2289 zcode_markers[i+2] = DELETED_MV;
2290 zcode_markers[i+3] = DELETED_MV;
2291 if ((opmodeoffset & 1) == 0)
2292 zcode_holding_area[opmodebyte] =
2293 (zcode_holding_area[opmodebyte] & 0xF0) | 0x02;
2295 zcode_holding_area[opmodebyte] =
2296 (zcode_holding_area[opmodebyte] & 0x0F) | 0x20;
2301 /* (2) Calculate the new positions of the labels. Note that since the
2302 long/short decision was taken on the basis of the old labels,
2303 and since the new labels are slightly closer together because
2304 of branch bytes deleted, there may be a few further branch
2305 optimisations which are possible but which have been missed
2306 (if two labels move inside the "short" range as a result of
2307 a previous optimisation). However, this is acceptably uncommon. */
2308 if (next_label > 0) {
2309 if (asm_trace_level >= 4) {
2310 printf("Opening label: %d\n", first_label);
2311 for (i=0;i<next_label;i++)
2312 printf("Label %d offset %04x next -> %d previous -> %d\n",
2313 i, labels[i].offset, labels[i].next, labels[i].prev);
2316 /* label will advance through the linked list as pc increases. */
2317 for (i=0, pc=adjusted_pc, new_pc=adjusted_pc, label = first_label;
2320 while ((label != -1) && (labels[label].offset == pc)) {
2321 if (asm_trace_level >= 4)
2322 printf("Position of L%d corrected from %04x to %04x\n",
2323 label, labels[label].offset, new_pc);
2324 labels[label].offset = new_pc;
2325 label = labels[label].next;
2327 if (zcode_markers[i] != DELETED_MV) new_pc++;
2331 /* (3) As we are transferring, replace the label numbers in branch
2332 operands with offsets to those labels. Also issue markers, now
2333 that we know where they occur in the final Z-code area. */
2335 ensure_memory_list_available(&zcode_area_memlist, adjusted_pc+zcode_ha_size);
2337 for (i=0, new_pc=adjusted_pc; i<zcode_ha_size; i++) {
2339 if (zcode_markers[i] >= BRANCH_MV && zcode_markers[i] < BRANCHMAX_MV) {
2341 if (zcode_markers[i+1] == DELETED_MV) {
2345 if (zcode_markers[i+2] == DELETED_MV)
2348 j = ((zcode_holding_area[i] << 24)
2349 | (zcode_holding_area[i+1] << 16)
2350 | (zcode_holding_area[i+2] << 8)
2351 | (zcode_holding_area[i+3]));
2353 /* At the moment, we can safely assume that the branch operand
2354 is the end of the opcode, so the next opcode starts right
2356 offset_of_next = new_pc + form_len;
2358 if (labels[j].offset < 0) {
2359 error("Attempt to jump to an unreachable label");
2363 addr = (labels[j].offset - offset_of_next) + 2;
2365 if (asm_trace_level >= 4) {
2366 printf("Branch at offset %04x: %04x (%s)\n",
2367 new_pc, addr, ((form_len == 1) ? "byte" :
2368 ((form_len == 2) ? "short" : "long")));
2370 if (form_len == 1) {
2371 if (addr < -0x80 || addr >= 0x80) {
2372 error("*** Label out of range for byte branch ***");
2374 zcode_holding_area[i] = (addr) & 0xFF;
2376 else if (form_len == 2) {
2377 if (addr < -0x8000 || addr >= 0x8000) {
2378 error("*** Label out of range for short branch ***");
2380 zcode_holding_area[i] = (addr >> 8) & 0xFF;
2381 zcode_holding_area[i+1] = (addr) & 0xFF;
2384 zcode_holding_area[i] = (addr >> 24) & 0xFF;
2385 zcode_holding_area[i+1] = (addr >> 16) & 0xFF;
2386 zcode_holding_area[i+2] = (addr >> 8) & 0xFF;
2387 zcode_holding_area[i+3] = (addr) & 0xFF;
2389 zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++;
2391 else if (zcode_markers[i] == LABEL_MV) {
2392 error("*** No LABEL opcodes in Glulx ***");
2394 else if (zcode_markers[i] == DELETED_MV) {
2398 switch(zcode_markers[i] & 0x7f) {
2403 if (!module_switch) break;
2407 if ((zcode_markers[i] & 0x7f) > LARGEST_BPATCH_MV) {
2408 error("*** Illegal code backpatch value ***");
2409 printf("Illegal value of %02x at PC = %04x\n",
2410 zcode_markers[i] & 0x7f, new_pc);
2413 /* The backpatch table format for Glulx:
2414 First, the marker byte (0..LARGEST_BPATCH_MV).
2415 Then a byte indicating the data size to be patched (1, 2, 4).
2416 Then the four-byte address (new_pc).
2418 if (bpatch_trace_setting >= 2)
2419 printf("BP added: MV %d size %d PC %04x\n", zcode_markers[i], 4, new_pc);
2420 ensure_memory_list_available(&zcode_backpatch_table_memlist, zcode_backpatch_size+6);
2421 zcode_backpatch_table[zcode_backpatch_size++] = zcode_markers[i];
2422 zcode_backpatch_table[zcode_backpatch_size++] = 4;
2423 zcode_backpatch_table[zcode_backpatch_size++] = ((new_pc >> 24) & 0xFF);
2424 zcode_backpatch_table[zcode_backpatch_size++] = ((new_pc >> 16) & 0xFF);
2425 zcode_backpatch_table[zcode_backpatch_size++] = ((new_pc >> 8) & 0xFF);
2426 zcode_backpatch_table[zcode_backpatch_size++] = (new_pc & 0xFF);
2429 zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++;
2433 /* Consistency check */
2434 if (new_pc - rstart_pc > zcode_ha_size || adjusted_pc != new_pc)
2436 fatalerror("Optimisation increased routine length or failed to match; should not happen");
2439 if (asm_trace_level >= 3)
2440 { printf("After branch optimisation, routine length is %d bytes\n",
2441 new_pc - rstart_pc);
2444 zmachine_pc = adjusted_pc;
2449 /* ========================================================================= */
2450 /* Front ends for the instruction assembler: convenient shorthand forms */
2451 /* used in various code generation routines all over Inform. */
2452 /* ------------------------------------------------------------------------- */
2454 void assemble_jump(int n)
2462 void assemblez_0(int internal_number)
2463 { AI.internal_number = internal_number;
2464 AI.operand_count = 0;
2465 AI.store_variable_number = -1;
2466 AI.branch_label_number = -1;
2467 assemblez_instruction(&AI);
2470 void assemblez_0_to(int internal_number, assembly_operand o)
2471 { AI.internal_number = internal_number;
2472 AI.operand_count = 0;
2473 AI.store_variable_number = o.value;
2474 AI.branch_label_number = -1;
2475 assemblez_instruction(&AI);
2478 void assemblez_0_branch(int internal_number, int label, int flag)
2479 { AI.internal_number = internal_number;
2480 AI.operand_count = 0;
2481 AI.store_variable_number = -1;
2482 AI.branch_label_number = label;
2483 AI.branch_flag = flag;
2484 assemblez_instruction(&AI);
2487 void assemblez_1(int internal_number, assembly_operand o1)
2488 { AI.internal_number = internal_number;
2489 AI.operand_count = 1;
2491 AI.store_variable_number = -1;
2492 AI.branch_label_number = -1;
2493 assemblez_instruction(&AI);
2496 void assemblez_1_to(int internal_number,
2497 assembly_operand o1, assembly_operand st)
2498 { AI.internal_number = internal_number;
2499 AI.operand_count = 1;
2501 AI.store_variable_number = st.value;
2502 AI.branch_label_number = -1;
2503 assemblez_instruction(&AI);
2506 void assemblez_1_branch(int internal_number,
2507 assembly_operand o1, int label, int flag)
2509 /* Some clever optimizations first. A constant is always or never equal
2511 if (o1.marker == 0 && is_constant_ot(o1.type)) {
2512 if (internal_number == jz_zc) {
2513 if ((flag && o1.value == 0) || (!flag && o1.value != 0)) {
2514 assemblez_jump(label);
2518 /* assemble nothing at all! */
2523 AI.internal_number = internal_number;
2524 AI.operand_count = 1;
2526 AI.branch_label_number = label;
2527 AI.store_variable_number = -1;
2528 AI.branch_flag = flag;
2529 assemblez_instruction(&AI);
2532 void assemblez_2(int internal_number,
2533 assembly_operand o1, assembly_operand o2)
2534 { AI.internal_number = internal_number;
2535 AI.operand_count = 2;
2538 AI.store_variable_number = -1;
2539 AI.branch_label_number = -1;
2540 assemblez_instruction(&AI);
2543 void assemblez_3(int internal_number,
2544 assembly_operand o1, assembly_operand o2, assembly_operand o3)
2545 { AI.internal_number = internal_number;
2546 AI.operand_count = 3;
2550 AI.store_variable_number = -1;
2551 AI.branch_label_number = -1;
2552 assemblez_instruction(&AI);
2555 void assemblez_3_to(int internal_number,
2556 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2557 assembly_operand st)
2558 { AI.internal_number = internal_number;
2559 AI.operand_count = 3;
2563 AI.store_variable_number = st.value;
2564 AI.branch_label_number = -1;
2565 assemblez_instruction(&AI);
2568 void assemblez_3_branch(int internal_number,
2569 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2570 int label, int flag)
2571 { AI.internal_number = internal_number;
2572 AI.operand_count = 3;
2576 AI.store_variable_number = -1;
2577 AI.branch_label_number = label;
2578 AI.branch_flag = flag;
2579 assemblez_instruction(&AI);
2582 void assemblez_4(int internal_number,
2583 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2584 assembly_operand o4)
2585 { AI.internal_number = internal_number;
2586 AI.operand_count = 4;
2591 AI.store_variable_number = -1;
2592 AI.branch_label_number = -1;
2593 assemblez_instruction(&AI);
2596 void assemblez_5(int internal_number,
2597 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2598 assembly_operand o4, assembly_operand o5)
2599 { AI.internal_number = internal_number;
2600 AI.operand_count = 5;
2606 AI.store_variable_number = -1;
2607 AI.branch_label_number = -1;
2608 assemblez_instruction(&AI);
2611 void assemblez_6(int internal_number,
2612 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2613 assembly_operand o4, assembly_operand o5, assembly_operand o6)
2614 { AI.internal_number = internal_number;
2615 AI.operand_count = 6;
2622 AI.store_variable_number = -1;
2623 AI.branch_label_number = -1;
2624 assemblez_instruction(&AI);
2627 void assemblez_4_branch(int internal_number,
2628 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2629 assembly_operand o4, int label, int flag)
2630 { AI.internal_number = internal_number;
2631 AI.operand_count = 4;
2636 AI.store_variable_number = -1;
2637 AI.branch_label_number = label;
2638 AI.branch_flag = flag;
2639 assemblez_instruction(&AI);
2642 void assemblez_4_to(int internal_number,
2643 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2644 assembly_operand o4, assembly_operand st)
2645 { AI.internal_number = internal_number;
2646 AI.operand_count = 4;
2651 AI.store_variable_number = st.value;
2652 AI.branch_label_number = -1;
2653 assemblez_instruction(&AI);
2656 void assemblez_5_to(int internal_number,
2657 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2658 assembly_operand o4, assembly_operand o5, assembly_operand st)
2659 { AI.internal_number = internal_number;
2660 AI.operand_count = 5;
2666 AI.store_variable_number = st.value;
2667 AI.branch_label_number = -1;
2668 assemblez_instruction(&AI);
2671 void assemblez_2_to(int internal_number,
2672 assembly_operand o1, assembly_operand o2, assembly_operand st)
2673 { AI.internal_number = internal_number;
2674 AI.operand_count = 2;
2677 AI.store_variable_number = st.value;
2678 AI.branch_label_number = -1;
2679 assemblez_instruction(&AI);
2682 void assemblez_2_branch(int internal_number,
2683 assembly_operand o1, assembly_operand o2, int label, int flag)
2684 { AI.internal_number = internal_number;
2685 AI.operand_count = 2;
2688 AI.branch_label_number = label;
2689 AI.store_variable_number = -1;
2690 AI.branch_flag = flag;
2691 assemblez_instruction(&AI);
2694 void assemblez_objcode(int internal_number,
2695 assembly_operand o1, assembly_operand st, int label, int flag)
2696 { AI.internal_number = internal_number;
2697 AI.operand_count = 1;
2699 AI.branch_label_number = label;
2700 AI.store_variable_number = st.value;
2701 AI.branch_flag = flag;
2702 assemblez_instruction(&AI);
2705 extern void assemblez_inc(assembly_operand o1)
2707 if ((o1.value >= MAX_LOCAL_VARIABLES)
2708 && (o1.value<LOWEST_SYSTEM_VAR_NUMBER))
2710 AI.internal_number = inc_zc;
2711 AI.operand_count = 1;
2712 AI.operand[0].value = o1.value;
2713 AI.operand[0].type = SHORT_CONSTANT_OT;
2714 AI.operand[0].marker = m;
2715 AI.store_variable_number = -1;
2716 AI.branch_label_number = -1;
2717 assemblez_instruction(&AI);
2720 extern void assemblez_dec(assembly_operand o1)
2722 if ((o1.value >= MAX_LOCAL_VARIABLES)
2723 && (o1.value<LOWEST_SYSTEM_VAR_NUMBER))
2725 AI.internal_number = dec_zc;
2726 AI.operand_count = 1;
2727 AI.operand[0].value = o1.value;
2728 AI.operand[0].type = SHORT_CONSTANT_OT;
2729 AI.operand[0].marker = m;
2730 AI.store_variable_number = -1;
2731 AI.branch_label_number = -1;
2732 assemblez_instruction(&AI);
2735 extern void assemblez_store(assembly_operand o1, assembly_operand o2)
2737 if ((o1.value >= MAX_LOCAL_VARIABLES)
2738 && (o1.value<LOWEST_SYSTEM_VAR_NUMBER))
2741 if ((o2.type == VARIABLE_OT) && (o2.value == 0))
2743 /* Assemble "pull VAR" rather than "store VAR sp",
2746 AI.internal_number = pull_zc;
2747 if (instruction_set_number == 6)
2748 { AI.operand_count = 0;
2749 AI.store_variable_number = o1.value;
2752 { AI.operand_count = 1;
2753 AI.operand[0].value = o1.value;
2754 AI.operand[0].type = SHORT_CONSTANT_OT;
2755 AI.operand[0].marker = m;
2756 AI.store_variable_number = -1;
2758 AI.branch_label_number = -1;
2759 assemblez_instruction(&AI);
2763 if ((o1.type == VARIABLE_OT) && (o1.value == 0))
2764 { /* Assemble "push VAR" rather than "store sp VAR",
2767 AI.internal_number = push_zc;
2768 AI.operand_count = 1;
2770 AI.store_variable_number = -1;
2771 AI.branch_label_number = -1;
2772 assemblez_instruction(&AI);
2775 AI.internal_number = store_zc;
2776 AI.operand_count = 2;
2777 AI.operand[0].value = o1.value;
2778 AI.operand[0].type = SHORT_CONSTANT_OT;
2779 AI.operand[0].marker = m;
2781 AI.store_variable_number = -1;
2782 AI.branch_label_number = -1;
2783 assemblez_instruction(&AI);
2786 void assemblez_jump(int n)
2787 { assembly_operand AO;
2788 if (n==-4) assemblez_0(rtrue_zc);
2789 else if (n==-3) assemblez_0(rfalse_zc);
2791 { AO.type = LONG_CONSTANT_OT; AO.value = n; AO.marker = 0;
2792 assemblez_1(jump_zc, AO);
2796 void assembleg_0(int internal_number)
2797 { AI.internal_number = internal_number;
2798 AI.operand_count = 0;
2799 assembleg_instruction(&AI);
2802 void assembleg_1(int internal_number, assembly_operand o1)
2803 { AI.internal_number = internal_number;
2804 AI.operand_count = 1;
2806 assembleg_instruction(&AI);
2809 void assembleg_2(int internal_number, assembly_operand o1,
2810 assembly_operand o2)
2811 { AI.internal_number = internal_number;
2812 AI.operand_count = 2;
2815 assembleg_instruction(&AI);
2818 void assembleg_3(int internal_number, assembly_operand o1,
2819 assembly_operand o2, assembly_operand o3)
2820 { AI.internal_number = internal_number;
2821 AI.operand_count = 3;
2825 assembleg_instruction(&AI);
2828 void assembleg_4(int internal_number, assembly_operand o1,
2829 assembly_operand o2, assembly_operand o3,
2830 assembly_operand o4)
2831 { AI.internal_number = internal_number;
2832 AI.operand_count = 4;
2837 assembleg_instruction(&AI);
2840 void assembleg_5(int internal_number, assembly_operand o1,
2841 assembly_operand o2, assembly_operand o3,
2842 assembly_operand o4, assembly_operand o5)
2843 { AI.internal_number = internal_number;
2844 AI.operand_count = 5;
2850 assembleg_instruction(&AI);
2853 void assembleg_0_branch(int internal_number,
2856 AI.internal_number = internal_number;
2857 AI.operand_count = 1;
2858 AI.operand[0].type = CONSTANT_OT;
2859 AI.operand[0].value = label;
2860 AI.operand[0].marker = BRANCH_MV;
2861 assembleg_instruction(&AI);
2864 void assembleg_1_branch(int internal_number,
2865 assembly_operand o1, int label)
2867 /* Some clever optimizations first. A constant is always or never equal
2869 if (o1.marker == 0 && is_constant_ot(o1.type)) {
2870 if ((internal_number == jz_gc && o1.value == 0)
2871 || (internal_number == jnz_gc && o1.value != 0)) {
2872 assembleg_0_branch(jump_gc, label);
2875 if ((internal_number == jz_gc && o1.value != 0)
2876 || (internal_number == jnz_gc && o1.value == 0)) {
2877 /* assemble nothing at all! */
2881 AI.internal_number = internal_number;
2882 AI.operand_count = 2;
2884 AI.operand[1].type = CONSTANT_OT;
2885 AI.operand[1].value = label;
2886 AI.operand[1].marker = BRANCH_MV;
2887 assembleg_instruction(&AI);
2890 void assembleg_2_branch(int internal_number,
2891 assembly_operand o1, assembly_operand o2, int label)
2893 AI.internal_number = internal_number;
2894 AI.operand_count = 3;
2897 AI.operand[2].type = CONSTANT_OT;
2898 AI.operand[2].value = label;
2899 AI.operand[2].marker = BRANCH_MV;
2900 assembleg_instruction(&AI);
2903 void assembleg_call_1(assembly_operand oaddr, assembly_operand o1,
2904 assembly_operand odest)
2906 assembleg_3(callfi_gc, oaddr, o1, odest);
2909 void assembleg_call_2(assembly_operand oaddr, assembly_operand o1,
2910 assembly_operand o2, assembly_operand odest)
2912 assembleg_4(callfii_gc, oaddr, o1, o2, odest);
2915 void assembleg_call_3(assembly_operand oaddr, assembly_operand o1,
2916 assembly_operand o2, assembly_operand o3, assembly_operand odest)
2918 assembleg_5(callfiii_gc, oaddr, o1, o2, o3, odest);
2921 void assembleg_inc(assembly_operand o1)
2923 AI.internal_number = add_gc;
2924 AI.operand_count = 3;
2926 AI.operand[1] = one_operand;
2928 assembleg_instruction(&AI);
2931 void assembleg_dec(assembly_operand o1)
2933 AI.internal_number = sub_gc;
2934 AI.operand_count = 3;
2936 AI.operand[1] = one_operand;
2938 assembleg_instruction(&AI);
2941 void assembleg_store(assembly_operand o1, assembly_operand o2)
2943 /* Note the order is reversed: "o1 = o2;" */
2944 assembleg_2(copy_gc, o2, o1);
2947 void assembleg_jump(int n)
2950 assembleg_1(return_gc, one_operand);
2953 assembleg_1(return_gc, zero_operand);
2956 assembleg_0_branch(jump_gc, n);
2960 /* ========================================================================= */
2961 /* Parsing and then calling the assembler for @ (assembly language) */
2963 /* ------------------------------------------------------------------------- */
2965 static assembly_operand parse_operand_z(void)
2966 { assembly_operand AO;
2968 AO = parse_expression(ASSEMBLY_CONTEXT);
2969 if (AO.type == EXPRESSION_OT)
2970 { ebf_error("variable or constant", "expression");
2971 AO.type = SHORT_CONSTANT_OT;
2976 static void parse_assembly_z(void)
2977 { int n, min, max, indirect_addressed, error_flag = FALSE;
2980 AI.operand_count = 0;
2981 AI.store_variable_number = -1;
2982 AI.branch_label_number = -1;
2985 opcode_names.enabled = TRUE;
2987 opcode_names.enabled = FALSE;
2989 if (token_type == DQ_TT)
2991 AI.internal_number = -1;
2993 custom_opcode_z.name = (uchar *) token_text;
2994 custom_opcode_z.version1 = instruction_set_number;
2995 custom_opcode_z.version2 = instruction_set_number;
2996 custom_opcode_z.extension = -1;
2997 custom_opcode_z.flags = 0;
2998 custom_opcode_z.op_rules = 0;
2999 custom_opcode_z.flags2_set = 0;
3000 custom_opcode_z.no = ZERO;
3002 for (i=0; token_text[i]!=0; i++)
3003 { if (token_text[i] == ':')
3004 { token_text[i++] = 0;
3008 if (token_text[i] == 0)
3009 error("Opcode specification should have form \"VAR:102\"");
3012 if (strcmp(token_text, "0OP")==0) n=ZERO;
3013 if (strcmp(token_text, "1OP")==0) n=ONE;
3014 if (strcmp(token_text, "2OP")==0) n=TWO;
3015 if (strcmp(token_text, "VAR")==0) n=VAR;
3016 if (strcmp(token_text, "EXT")==0) n=EXT;
3017 if (strcmp(token_text, "VAR_LONG")==0) n=VAR_LONG;
3018 if (strcmp(token_text, "EXT_LONG")==0) n=EXT_LONG;
3020 if (i>0) token_text[i-1] = ':';
3023 { ebf_error("Expected 0OP, 1OP, 2OP, VAR, EXT, VAR_LONG or EXT_LONG",
3027 custom_opcode_z.no = n;
3029 custom_opcode_z.code = atoi(token_text+i);
3030 while (isdigit(token_text[i])) i++;
3034 { case ZERO: case ONE: max = 16; break;
3035 case VAR: case VAR_LONG: min = 32; max = 64; break;
3036 case EXT: case EXT_LONG: max = 256; break;
3037 case TWO: max = 32; break;
3039 if ((custom_opcode_z.code < min) || (custom_opcode_z.code >= max))
3041 sprintf(range, "%d to %d", min, max-1);
3042 error_named("For this operand type, opcode number must be in range",
3044 custom_opcode_z.code = min;
3048 while (token_text[i++] != 0)
3049 { switch(token_text[i-1])
3050 { case 'B': custom_opcode_z.flags |= Br; break;
3051 case 'S': custom_opcode_z.flags |= St; break;
3052 case 'T': custom_opcode_z.op_rules = TEXT; break;
3053 case 'I': custom_opcode_z.op_rules = VARIAB; break;
3054 case 'F': custom_opcode_z.flags2_set = atoi(token_text+i);
3055 while (isdigit(token_text[i])) i++; break;
3057 error("Unknown flag: options are B (branch), S (store), \
3058 T (text), I (indirect addressing), F** (set this Flags 2 bit)");
3062 O = custom_opcode_z;
3065 { if (token_type != OPCODE_NAME_TT)
3066 { ebf_error("an opcode name", token_text);
3067 panic_mode_error_recovery();
3070 AI.internal_number = token_value;
3071 O = internal_number_to_opcode_z(AI.internal_number);
3074 indirect_addressed = (O.op_rules == VARIAB);
3076 if (O.op_rules == TEXT)
3078 if (token_type != DQ_TT)
3079 ebf_error("literal text in double-quotes", token_text);
3080 AI.text = token_text;
3081 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) return;
3083 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
3084 { assemblez_instruction(&AI);
3088 ebf_error("semicolon ';' after print string", token_text);
3094 return_sp_as_variable = TRUE;
3098 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
3100 if ((token_type == SEP_TT) && (token_value == ARROW_SEP))
3101 { if (AI.store_variable_number != -1)
3102 error("Only one '->' store destination can be given");
3104 if ((token_type != SYMBOL_TT)
3105 && (token_type != LOCAL_VARIABLE_TT))
3106 ebf_error("variable name or 'sp'", token_text);
3108 if (token_type == LOCAL_VARIABLE_TT) n = token_value;
3110 { if (strcmp(token_text, "sp") == 0) n = 0;
3112 { if (symbols[token_value].type != GLOBAL_VARIABLE_T)
3114 "Store '->' destination not 'sp' or a variable:",
3116 else n = symbols[token_value].value;
3119 AI.store_variable_number = n;
3123 if ((token_type == SEP_TT) &&
3124 ((token_value == BRANCH_SEP) || (token_value == NBRANCH_SEP)))
3125 { if (AI.branch_label_number != -1)
3126 error("Only one '?' branch destination can be given");
3128 AI.branch_flag = (token_value == BRANCH_SEP);
3130 opcode_names.enabled = TRUE;
3132 opcode_names.enabled = FALSE;
3135 if ((token_type == OPCODE_NAME_TT)
3136 && (token_value == rfalse_zc)) n = -3;
3138 if ((token_type == OPCODE_NAME_TT)
3139 && (token_value == rtrue_zc)) n = -4;
3141 { if (token_type == SYMBOL_TT)
3146 ebf_error("label name after '?' or '?~'", token_text);
3148 AI.branch_label_number = n;
3152 if (AI.operand_count == 8)
3153 { error("No assembly instruction may have more than 8 operands");
3154 panic_mode_error_recovery(); break;
3157 if ((token_type == SEP_TT) && (token_value == OPEN_SQUARE_SEP))
3158 { if (!indirect_addressed)
3159 error("This opcode does not use indirect addressing");
3160 if (AI.operand_count > 0)
3161 error("Indirect addressing can only be used on the first operand");
3162 AI.operand[AI.operand_count++] = parse_operand_z();
3164 if (!((token_type == SEP_TT) && (token_value == CLOSE_SQUARE_SEP)))
3165 { ebf_error("']'", token_text);
3171 AI.operand[AI.operand_count++] = parse_operand_z();
3172 if ((indirect_addressed) && (AI.operand_count == 1)
3173 && (AI.operand[AI.operand_count-1].type == VARIABLE_OT))
3174 { AI.operand[AI.operand_count-1].type = SHORT_CONSTANT_OT;
3175 AI.operand[AI.operand_count-1].marker = VARIABLE_MV;
3181 return_sp_as_variable = FALSE;
3184 if (O.version1 == 0)
3185 { error_named("Opcode unavailable in this Z-machine version:",
3186 opcode_names.keywords[AI.internal_number]);
3190 if (((O.flags) & Br) != 0)
3191 { if (AI.branch_label_number == -1)
3192 { error_flag = TRUE;
3193 AI.branch_label_number = -2;
3197 { if (AI.branch_label_number != -1)
3198 { error_flag = TRUE;
3199 AI.branch_label_number = -1;
3202 if (((O.flags) & St) != 0)
3203 { if (AI.store_variable_number == -1)
3204 { if (AI.operand_count == 0)
3205 { error_flag = TRUE;
3206 AI.store_variable_number = 255;
3209 { AI.store_variable_number
3210 = AI.operand[--AI.operand_count].value;
3211 if (AI.operand[AI.operand_count].type != VARIABLE_OT)
3212 error("Store destination (the last operand) is not a variable");
3217 { if (AI.store_variable_number != -1)
3218 { error_flag = TRUE;
3219 AI.store_variable_number = -1;
3225 { case TWO: min = 2; max = 2;
3226 /* Exception for the V6 set_colour, which can take
3227 a third argument, thus forcing it into VAR form: */
3228 if ((version_number == 6) && (O.code == 0x1b)) max = 3;
3229 /* Also an exception for je, which can take from 1
3230 argument (useless) to 4 arguments */
3231 if (O.code == 0x01) { min = 1; max = 4; }
3233 case VAR: min = 0; max = 4; break;
3234 case VAR_LONG: min = 0; max = 8; break;
3235 case ONE: min = 1; max = 1; break;
3236 case ZERO: min = 0; max = 0; break;
3237 case EXT: min = 0; max = 4; break;
3238 case EXT_LONG: min = 0; max = 8; break;
3241 if ((AI.operand_count >= min) && (AI.operand_count <= max))
3242 assemblez_instruction(&AI);
3243 else error_flag = TRUE;
3246 { make_opcode_syntax_z(O);
3247 error_named("Assembly mistake: syntax is",
3248 opcode_syntax_string);
3252 static assembly_operand parse_operand_g(void)
3253 { assembly_operand AO;
3255 AO = parse_expression(ASSEMBLY_CONTEXT);
3256 if (AO.type == EXPRESSION_OT)
3257 { ebf_error("variable or constant", "expression");
3258 AO.type = CONSTANT_OT;
3263 static void parse_assembly_g(void)
3266 assembly_operand AO;
3267 int error_flag = FALSE, is_macro = FALSE;
3269 AI.operand_count = 0;
3272 opcode_names.enabled = TRUE;
3273 opcode_macros.enabled = TRUE;
3275 opcode_names.enabled = FALSE;
3276 opcode_macros.enabled = FALSE;
3278 if (token_type == DQ_TT) {
3282 AI.internal_number = -1;
3284 /* The format is @"FlagsCount:Code". Flags (which are optional)
3285 can include "S" for store, "SS" for two stores, "B" for branch
3286 format, "R" if execution never continues after the opcode. The
3287 Count is the number of arguments (currently limited to 0-9),
3288 and the Code is a decimal integer representing the opcode
3291 So: @"S3:123" for a three-argument opcode (load, load, store)
3292 whose opcode number is (decimal) 123. Or: @"2:234" for a
3293 two-argument opcode (load, load) whose number is 234. */
3295 custom_opcode_g.name = (uchar *) token_text;
3296 custom_opcode_g.flags = 0;
3297 custom_opcode_g.op_rules = 0;
3298 custom_opcode_g.no = 0;
3302 for (cx = token_text; *cx && *cx != ':'; cx++) {
3308 if (custom_opcode_g.flags & St)
3309 custom_opcode_g.flags |= St2;
3311 custom_opcode_g.flags |= St;
3314 custom_opcode_g.flags |= Br;
3317 custom_opcode_g.flags |= Rf;
3321 custom_opcode_g.no = (*cx) - '0';
3325 error("Unknown custom opcode flag: options are B (branch), \
3326 S (store), SS (two stores), R (execution never continues)");
3332 error("Custom opcode must have colon");
3337 error("Custom opcode must have colon followed by opcode number");
3339 custom_opcode_g.code = atoi(cx);
3342 O = custom_opcode_g;
3345 if (token_type != OPCODE_NAME_TT && token_type != OPCODE_MACRO_TT) {
3346 ebf_error("an opcode name", token_text);
3347 panic_mode_error_recovery();
3350 AI.internal_number = token_value;
3351 if (token_type == OPCODE_MACRO_TT) {
3352 O = internal_number_to_opmacro_g(AI.internal_number);
3356 O = internal_number_to_opcode_g(AI.internal_number);
3359 return_sp_as_variable = TRUE;
3364 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
3367 if (AI.operand_count == 8) {
3368 error("No assembly instruction may have more than 8 operands");
3369 panic_mode_error_recovery();
3373 if ((O.flags & Br) && (AI.operand_count == O.no-1)) {
3374 if (!((token_type == SEP_TT) && (token_value == BRANCH_SEP))) {
3376 error("Branch opcode must have '?' label");
3379 AO.type = CONSTANT_OT;
3380 AO.value = parse_label();
3381 AO.marker = BRANCH_MV;
3385 AO = parse_operand_g();
3388 AI.operand[AI.operand_count] = AO;
3392 return_sp_as_variable = FALSE;
3394 if (O.no != AI.operand_count) {
3400 assembleg_macro(&AI);
3402 assembleg_instruction(&AI);
3406 make_opcode_syntax_g(O);
3407 error_named("Assembly mistake: syntax is",
3408 opcode_syntax_string);
3412 extern void parse_assembly(void)
3420 /* ========================================================================= */
3421 /* Data structure management routines */
3422 /* ------------------------------------------------------------------------- */
3424 extern void init_asm_vars(void)
3427 for (i=0;i<16;i++) flags2_requirements[i]=0;
3429 uses_unicode_features = FALSE;
3430 uses_memheap_features = FALSE;
3431 uses_acceleration_features = FALSE;
3432 uses_float_features = FALSE;
3433 uses_extundo_features = FALSE;
3436 sequence_points = NULL;
3437 sequence_point_follows = TRUE;
3438 label_moved_error_already_given = FALSE;
3443 extern void asm_begin_pass(void)
3444 { no_instructions = 0;
3446 no_sequence_points = 0;
3449 next_sequence_point = 0;
3451 execution_never_reaches_here = EXECSTATE_REACHABLE;
3454 extern void asm_allocate_arrays(void)
3456 initialise_memory_list(&variables_memlist,
3457 sizeof(variableinfo), 200, (void**)&variables,
3460 initialise_memory_list(&labels_memlist,
3461 sizeof(labelinfo), 1000, (void**)&labels,
3463 initialise_memory_list(&labeluse_memlist,
3464 sizeof(int), 1000, (void**)&labeluse,
3466 initialise_memory_list(&sequence_points_memlist,
3467 sizeof(sequencepointinfo), 1000, (void**)&sequence_points,
3470 initialise_memory_list(&zcode_holding_area_memlist,
3471 sizeof(uchar), 2000, (void**)&zcode_holding_area,
3472 "compiled routine code area");
3473 initialise_memory_list(&zcode_markers_memlist,
3474 sizeof(uchar), 2000, (void**)&zcode_markers,
3475 "compiled routine markers area");
3477 initialise_memory_list(&named_routine_symbols_memlist,
3478 sizeof(int32), 1000, (void**)&named_routine_symbols,
3479 "named routine symbols");
3481 initialise_memory_list(&zcode_area_memlist,
3482 sizeof(uchar), 8192, (void**)&zcode_area,
3485 initialise_memory_list(¤t_routine_name,
3486 sizeof(char), 3*MAX_IDENTIFIER_LENGTH, NULL,
3487 "routine name currently being defined");
3490 extern void asm_free_arrays(void)
3492 deallocate_memory_list(&variables_memlist);
3494 deallocate_memory_list(&labels_memlist);
3495 deallocate_memory_list(&sequence_points_memlist);
3497 deallocate_memory_list(&zcode_holding_area_memlist);
3498 deallocate_memory_list(&zcode_markers_memlist);
3500 deallocate_memory_list(&named_routine_symbols_memlist);
3501 deallocate_memory_list(&zcode_area_memlist);
3502 deallocate_memory_list(¤t_routine_name);
3505 /* ========================================================================= */