1 /* ------------------------------------------------------------------------- */
2 /* "asm" : The Inform assembler */
4 /* Part of Inform 6.41 */
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)
70 int uses_double_features; /* Makes use of Glulx double-prec (3.1.3)
73 debug_location statement_debug_location;
74 /* Location of current statement */
77 variableinfo *variables; /* The allocated size is
78 (MAX_LOCAL_VARIABLES + no_globals).
79 Local variables first, then globals. */
80 memory_list variables_memlist;
82 assembly_instruction AI; /* A structure used to hold the full
83 specification of a single Z-code
84 instruction: effectively this is the
86 assemble_instruction() */
88 static char opcode_syntax_string[128]; /* Text buffer holding the correct
89 syntax for an opcode: used to produce
90 helpful assembler error messages */
92 static int routine_symbol; /* The symbol index of the routine currently
94 static memory_list current_routine_name; /* The name of the routine currently
95 being compiled. (This may be longer
96 than MAX_IDENTIFIER_LENGTH, e.g. for
97 an "obj.prop" property routine.) */
98 static int routine_locals; /* The number of local variables used by
99 the routine currently being compiled */
101 static int32 routine_start_pc;
103 int32 *named_routine_symbols; /* Allocated up to no_named_routines */
104 static memory_list named_routine_symbols_memlist;
106 static void transfer_routine_z(void);
107 static void transfer_routine_g(void);
109 /* ------------------------------------------------------------------------- */
111 /* ------------------------------------------------------------------------- */
113 static labelinfo *labels; /* Label offsets (i.e. zmachine_pc values).
114 These are allocated sequentially, but accessed
115 as a double-linked list from first_label
116 to last_label (in PC order). */
117 static memory_list labels_memlist;
118 static int first_label, last_label;
120 static int *labeluse; /* Flags indicating whether a given label has been
121 used as a branch target yet. */
122 static memory_list labeluse_memlist;
123 static int labeluse_size; /* Entries up to here are initialized */
125 static sequencepointinfo *sequence_points; /* Allocated to next_sequence_point.
126 Only used when debugfile_switch
128 static memory_list sequence_points_memlist;
130 /* ------------------------------------------------------------------------- */
131 /* Label management */
132 /* ------------------------------------------------------------------------- */
134 /* The stripping of unreachable code requires a bit of explanation.
136 As we compile a function, we update the execution_never_reaches_here
137 flag to reflect whether the current line is reachable. If the flag
138 is set (EXECSTATE_UNREACHABLE), we skip generating opcodes,
139 compiling strings, and so on. (See assemblez_instruction(),
140 assembleg_instruction(), and compile_string() for these checks.)
142 If we're *between* functions, the execution_never_reaches_here flag
143 is always clear (EXECSTATE_REACHABLE), so global strings are
146 In general, every time we compile a JUMP or RETURN opcode, the flag
147 gets set. Every time we compile a label, the flag gets cleared.
148 This makes sense if you consider a common "while" loop:
161 @jnz flag ?ExitLabel;
167 Code after an unconditional JUMP is obviously unreachable. But the
168 next thing that happens is the .ExitLabel, which is the target of a
169 branch, so the next line is reachable again.
171 However, if the unreachable flag is set when we *begin* a statement
172 (or braced block of statements), we can get tougher. We set the
173 EXECSTATE_ENTIRE flag for the entire duration of the statement or
174 block. This flag cannot be cleared by compiling labels. An example
183 If the DOSTUFF constant is false, the entire "while" loop is definitely
184 unreachable. So we should skip .TopLabel, .ExitLabel, and everything
185 in between. To ensure this, we set EXECSTATE_ENTIRE upon entering the
186 "if {...}" braced block, and reset it upon leaving.
188 (See parse_code_block() and parse_statement() for the (slightly fugly)
189 bit-fidding that accomplishes this.)
191 As an added optimization, some labels are known to be "forward"; they
192 are only reached by forward jumps. (.ExitLabel above is an example.)
193 If we reach a forward label and nothing has in fact jumped there,
194 the label is dead and we can skip it. (And thus also skip clearing
195 the unreachable flag!)
197 To understand *that*, consider a "while true" loop with no "break":
205 This never branches to .ExitLabel. So when we reach .ExitLabel,
206 we can say for sure that *nothing* branches there. So we skip
207 compiling that label. The unreachable flag is left set (because we
208 just finished the jump to .TopLabel). Thus, any code following the
209 entire loop is known to be unreachable, and we can righteously
212 (In contrast, .TopLabel cannot be skipped because it might be the
213 target of a *backwards* branch later on. In fact there might be
214 several -- any "continue" in the loop will jump to .TopLabel.)
217 /* Set the position of the given label. The offset will be the current
218 zmachine_pc, or -1 if the label is definitely unused.
220 This adds the label to a linked list (via first_label, last_label).
222 The linked list must be in increasing PC order. We know this will
223 be true because we call this as we run through the function, so
224 zmachine_pc always increases.
226 (It won't necessarily be in *label index* order, though.)
228 static void set_label_offset(int label, int32 offset)
230 ensure_memory_list_available(&labels_memlist, label+1);
232 labels[label].offset = offset;
233 labels[label].symbol = -1;
235 /* Mark this label as invalid and don't put it in the linked list. */
236 labels[label].prev = -1;
237 labels[label].next = -1;
241 if (last_label == -1)
242 { labels[label].prev = -1;
246 { labels[label].prev = last_label;
247 labels[last_label].next = label;
250 labels[label].next = -1;
253 /* Set a flag indicating that the given label has been jumped to. */
254 static void mark_label_used(int label)
259 /* Entries from 0 to labeluse_size have meaningful values.
260 If we have to increase labeluse_size, initialize the new
262 ensure_memory_list_available(&labeluse_memlist, label+1);
263 for (; labeluse_size < label+1; labeluse_size++) {
264 labeluse[labeluse_size] = FALSE;
266 labeluse[label] = TRUE;
269 /* ------------------------------------------------------------------------- */
270 /* Useful tool for building operands */
271 /* ------------------------------------------------------------------------- */
273 extern void set_constant_ot(assembly_operand *AO)
276 if (AO->value >= 0 && AO->value <= 255)
277 AO->type = SHORT_CONSTANT_OT;
279 AO->type = LONG_CONSTANT_OT;
283 AO->type = ZEROCONSTANT_OT;
284 else if (AO->value >= -0x80 && AO->value < 0x80)
285 AO->type = BYTECONSTANT_OT;
286 else if (AO->value >= -0x8000 && AO->value < 0x8000)
287 AO->type = HALFCONSTANT_OT;
289 AO->type = CONSTANT_OT;
293 extern int is_constant_ot(int otval)
296 return ((otval == LONG_CONSTANT_OT)
297 || (otval == SHORT_CONSTANT_OT));
300 return ((otval == CONSTANT_OT)
301 || (otval == HALFCONSTANT_OT)
302 || (otval == BYTECONSTANT_OT)
303 || (otval == ZEROCONSTANT_OT));
307 extern int is_variable_ot(int otval)
310 return (otval == VARIABLE_OT);
313 return ((otval == LOCALVAR_OT)
314 || (otval == GLOBALVAR_OT));
318 /* ------------------------------------------------------------------------- */
319 /* Used in printing assembly traces */
320 /* ------------------------------------------------------------------------- */
322 extern char *variable_name(int32 i)
324 if (i==0) return("sp");
325 if (i<MAX_LOCAL_VARIABLES) return local_variable_names[i-1].text;
328 if (i==255) return("TEMP1");
329 if (i==254) return("TEMP2");
330 if (i==253) return("TEMP3");
331 if (i==252) return("TEMP4");
332 if (i==251) return("self");
333 if (i==250) return("sender");
334 if (i==249) return("sw__var");
335 if (i >= 256 && i < 286)
336 { if (i - 256 < NUMBER_SYSTEM_FUNCTIONS) return system_functions.keywords[i - 256];
337 return "<unnamed system function>";
341 switch (i - MAX_LOCAL_VARIABLES) {
342 case 0: return "temp_global";
343 case 1: return "temp__global2";
344 case 2: return "temp__global3";
345 case 3: return "temp__global4";
346 case 4: return "self";
347 case 5: return "sender";
348 case 6: return "sw__var";
349 case 7: return "sys__glob0";
350 case 8: return "sys__glob1";
351 case 9: return "sys__glob2";
352 case 10: return "sys_statusline_flag";
356 return (symbols[variables[i].token].name);
359 /* Print symbolic information about the AO, if there is any. */
360 static void print_operand_annotation(const assembly_operand *o)
364 printf((!any) ? " (" : ": ");
366 printf("%s", describe_mv(o->marker));
369 printf(": %s", veneer_routine_name(o->value));
372 printf(": %s", name_of_system_constant(o->value));
376 print_dict_word(o->value);
381 if (o->symindex >= 0 && o->symindex < no_symbols) {
382 printf((!any) ? " (" : ": ");
384 printf("%s", symbols[o->symindex].name);
386 if (any) printf(")");
389 static void print_operand_z(const assembly_operand *o, int annotate)
391 { case EXPRESSION_OT: printf("expr_"); break;
392 case LONG_CONSTANT_OT: printf("long_"); break;
393 case SHORT_CONSTANT_OT: printf("short_"); break;
395 if (o->value==0) { printf("sp"); return; }
396 printf("%s", variable_name(o->value)); return;
397 case OMITTED_OT: printf("<no value>"); return;
399 printf("%d", o->value);
401 print_operand_annotation(o);
404 static void print_operand_g(const assembly_operand *o, int annotate)
407 case EXPRESSION_OT: printf("expr_"); break;
408 case CONSTANT_OT: printf("long_"); break;
409 case HALFCONSTANT_OT: printf("short_"); break;
410 case BYTECONSTANT_OT: printf("byte_"); break;
411 case ZEROCONSTANT_OT: printf("zero_"); return;
412 case DEREFERENCE_OT: printf("*"); break;
414 printf("global_%d (%s)", o->value, variable_name(o->value));
420 printf("local_%d (%s)", o->value-1, variable_name(o->value));
423 if (o->value >= 0 && o->value < NUMBER_SYSTEM_FUNCTIONS)
424 printf("%s", system_functions.keywords[o->value]);
426 printf("<unnamed system function>");
428 case OMITTED_OT: printf("<no value>"); return;
429 default: printf("???_"); break;
431 printf("%d", o->value);
433 print_operand_annotation(o);
436 extern void print_operand(const assembly_operand *o, int annotate)
439 print_operand_z(o, annotate);
441 print_operand_g(o, annotate);
444 /* ------------------------------------------------------------------------- */
445 /* Writing bytes to the code area */
446 /* ------------------------------------------------------------------------- */
448 static void byteout(int32 i, int mv)
450 ensure_memory_list_available(&zcode_markers_memlist, zcode_ha_size+1);
451 ensure_memory_list_available(&zcode_holding_area_memlist, zcode_ha_size+1);
452 zcode_markers[zcode_ha_size] = (uchar) mv;
453 zcode_holding_area[zcode_ha_size++] = (uchar) i;
457 /* ------------------------------------------------------------------------- */
458 /* A database of the 115 canonical Infocom opcodes in Versions 3 to 6 */
459 /* And of the however-many-there-are Glulx opcodes */
460 /* ------------------------------------------------------------------------- */
462 typedef struct opcodez
463 { uchar *name; /* Lower case standard name */
464 int version1; /* Valid from this version number... */
465 int version2; /* ...until this one (or forever if this is 0) */
466 int extension; /* In later versions, see this line in extension table:
467 if -1, the opcode is illegal in later versions */
468 int code; /* Opcode number within its operand-number block */
469 int flags; /* Flags (see below) */
470 int op_rules; /* Any unusual operand rule applying (see below) */
471 int flags2_set; /* If not zero, set this bit in Flags 2 in the header
472 of any game using the opcode */
473 int no; /* Number of operands (see below) */
476 typedef struct opcodeg
477 { uchar *name; /* Lower case standard name */
478 int32 code; /* Opcode number */
479 int flags; /* Flags (see below) */
480 int op_rules; /* Any unusual operand rule applying (see below) */
481 int no; /* Number of operands */
484 /* Flags which can be set */
486 #define St 1 /* Store */
487 #define Br 2 /* Branch */
488 #define Rf 4 /* "Return flag": execution never continues after this
489 opcode (e.g., is a return or unconditional jump) */
490 #define St2 8 /* Store2 (second-to-last operand is store (Glulx)) */
492 /* Codes for any unusual operand assembly rules */
496 #define VARIAB 1 /* First operand expected to be a variable name and
497 assembled to a short constant: the variable number */
498 #define TEXT 2 /* One text operand, to be Z-encoded into the program */
499 #define LABEL 3 /* One operand, a label, given as long constant offset */
500 #define CALL 4 /* First operand is name of a routine, to be assembled
501 as long constant (the routine's packed address):
502 as if the name were prefixed by #r$ */
504 /* Glulx: (bit flags for Glulx VM features) */
506 #define GOP_Unicode 1 /* uses_unicode_features */
507 #define GOP_MemHeap 2 /* uses_memheap_features */
508 #define GOP_Acceleration 4 /* uses_acceleration_features */
509 #define GOP_Float 8 /* uses_float_features */
510 #define GOP_ExtUndo 16 /* uses_extundo_features */
511 #define GOP_Double 32 /* uses_double_features */
513 /* Codes for the number of operands */
515 #define TWO 1 /* 2 (with certain types of operand, compiled as VAR) */
516 #define VAR 2 /* 0 to 4 */
517 #define VAR_LONG 3 /* 0 to 8 */
518 #define ONE 4 /* 1 */
519 #define ZERO 5 /* 0 */
520 #define EXT 6 /* Extended opcode set VAR: 0 to 4 */
521 #define EXT_LONG 7 /* Extended: 0 to 8 (not used by the canonical opcodes) */
523 static opcodez opcodes_table_z[] =
525 /* Opcodes introduced in Version 3 */
527 /* 0 */ { (uchar *) "je", 3, 0, -1, 0x01, Br, 0, 0, TWO },
528 /* 1 */ { (uchar *) "jl", 3, 0, -1, 0x02, Br, 0, 0, TWO },
529 /* 2 */ { (uchar *) "jg", 3, 0, -1, 0x03, Br, 0, 0, TWO },
530 /* 3 */ { (uchar *) "dec_chk", 3, 0, -1, 0x04, Br, VARIAB, 0, TWO },
531 /* 4 */ { (uchar *) "inc_chk", 3, 0, -1, 0x05, Br, VARIAB, 0, TWO },
532 /* 5 */ { (uchar *) "jin", 3, 0, -1, 0x06, Br, 0, 0, TWO },
533 /* 6 */ { (uchar *) "test", 3, 0, -1, 0x07, Br, 0, 0, TWO },
534 /* 7 */ { (uchar *) "or", 3, 0, -1, 0x08, St, 0, 0, TWO },
535 /* 8 */ { (uchar *) "and", 3, 0, -1, 0x09, St, 0, 0, TWO },
536 /* 9 */ { (uchar *) "test_attr", 3, 0, -1, 0x0A, Br, 0, 0, TWO },
537 /* 10 */ {(uchar *) "set_attr", 3, 0, -1, 0x0B, 0, 0, 0, TWO },
538 /* 11 */ {(uchar *) "clear_attr", 3, 0, -1, 0x0C, 0, 0, 0, TWO },
539 /* 12 */ {(uchar *) "store", 3, 0, -1, 0x0D, 0, VARIAB, 0, TWO },
540 /* 13 */ {(uchar *) "insert_obj", 3, 0, -1, 0x0E, 0, 0, 0, TWO },
541 /* 14 */ {(uchar *) "loadw", 3, 0, -1, 0x0F, St, 0, 0, TWO },
542 /* 15 */ {(uchar *) "loadb", 3, 0, -1, 0x10, St, 0, 0, TWO },
543 /* 16 */ {(uchar *) "get_prop", 3, 0, -1, 0x11, St, 0, 0, TWO },
544 /* 17 */ {(uchar *) "get_prop_addr", 3, 0, -1, 0x12, St, 0, 0, TWO },
545 /* 18 */ {(uchar *) "get_next_prop", 3, 0, -1, 0x13, St, 0, 0, TWO },
546 /* 19 */ {(uchar *) "add", 3, 0, -1, 0x14, St, 0, 0, TWO },
547 /* 20 */ {(uchar *) "sub", 3, 0, -1, 0x15, St, 0, 0, TWO },
548 /* 21 */ {(uchar *) "mul", 3, 0, -1, 0x16, St, 0, 0, TWO },
549 /* 22 */ {(uchar *) "div", 3, 0, -1, 0x17, St, 0, 0, TWO },
550 /* 23 */ {(uchar *) "mod", 3, 0, -1, 0x18, St, 0, 0, TWO },
551 /* 24 */ {(uchar *) "call", 3, 0, -1, 0x20, St, CALL, 0, VAR },
552 /* 25 */ {(uchar *) "storew", 3, 0, -1, 0x21, 0, 0, 0, VAR },
553 /* 26 */ {(uchar *) "storeb", 3, 0, -1, 0x22, 0, 0, 0, VAR },
554 /* 27 */ {(uchar *) "put_prop", 3, 0, -1, 0x23, 0, 0, 0, VAR },
555 /* This is the version of "read" called "sread" internally: */
556 /* 28 */ {(uchar *) "read", 3, 0, -1, 0x24, 0, 0, 0, VAR },
557 /* 29 */ {(uchar *) "print_char", 3, 0, -1, 0x25, 0, 0, 0, VAR },
558 /* 30 */ {(uchar *) "print_num", 3, 0, -1, 0x26, 0, 0, 0, VAR },
559 /* 31 */ {(uchar *) "random", 3, 0, -1, 0x27, St, 0, 0, VAR },
560 /* 32 */ {(uchar *) "push", 3, 0, -1, 0x28, 0, 0, 0, VAR },
561 /* 33 */ {(uchar *) "pull", 3, 5, 6, 0x29, 0, VARIAB, 0, VAR },
562 /* 34 */ {(uchar *) "split_window", 3, 0, -1, 0x2A, 0, 0, 0, VAR },
563 /* 35 */ {(uchar *) "set_window", 3, 0, -1, 0x2B, 0, 0, 0, VAR },
564 /* 36 */ {(uchar *) "output_stream", 3, 0, -1, 0x33, 0, 0, 0, VAR },
565 /* 37 */ {(uchar *) "input_stream", 3, 0, -1, 0x34, 0, 0, 0, VAR },
566 /* 38 */ {(uchar *) "sound_effect", 3, 0, -1, 0x35, 0, 0, 7, VAR },
567 /* 39 */ {(uchar *) "jz", 3, 0, -1, 0x00, Br, 0, 0, ONE },
568 /* 40 */ {(uchar *) "get_sibling", 3, 0, -1, 0x01, St+Br, 0, 0, ONE },
569 /* 41 */ {(uchar *) "get_child", 3, 0, -1, 0x02, St+Br, 0, 0, ONE },
570 /* 42 */ {(uchar *) "get_parent", 3, 0, -1, 0x03, St, 0, 0, ONE },
571 /* 43 */ {(uchar *) "get_prop_len", 3, 0, -1, 0x04, St, 0, 0, ONE },
572 /* 44 */ {(uchar *) "inc", 3, 0, -1, 0x05, 0, VARIAB, 0, ONE },
573 /* 45 */ {(uchar *) "dec", 3, 0, -1, 0x06, 0, VARIAB, 0, ONE },
574 /* 46 */ {(uchar *) "print_addr", 3, 0, -1, 0x07, 0, 0, 0, ONE },
575 /* 47 */ {(uchar *) "remove_obj", 3, 0, -1, 0x09, 0, 0, 0, ONE },
576 /* 48 */ {(uchar *) "print_obj", 3, 0, -1, 0x0A, 0, 0, 0, ONE },
577 /* 49 */ {(uchar *) "ret", 3, 0, -1, 0x0B, Rf, 0, 0, ONE },
578 /* 50 */ {(uchar *) "jump", 3, 0, -1, 0x0C, Rf, LABEL, 0, ONE },
579 /* 51 */ {(uchar *) "print_paddr", 3, 0, -1, 0x0D, 0, 0, 0, ONE },
580 /* 52 */ {(uchar *) "load", 3, 0, -1, 0x0E, St, VARIAB, 0, ONE },
581 /* 53 */ {(uchar *) "not", 3, 3, 0, 0x0F, St, 0, 0, ONE },
582 /* 54 */ {(uchar *) "rtrue", 3, 0, -1, 0x00, Rf, 0, 0,ZERO },
583 /* 55 */ {(uchar *) "rfalse", 3, 0, -1, 0x01, Rf, 0, 0,ZERO },
584 /* 56 */ {(uchar *) "print", 3, 0, -1, 0x02, 0, TEXT, 0,ZERO },
585 /* 57 */ {(uchar *) "print_ret", 3, 0, -1, 0x03, Rf, TEXT, 0,ZERO },
586 /* 58 */ {(uchar *) "nop", 3, 0, -1, 0x04, 0, 0, 0,ZERO },
587 /* 59 */ {(uchar *) "save", 3, 3, 1, 0x05, Br, 0, 0,ZERO },
588 /* 60 */ {(uchar *) "restore", 3, 3, 2, 0x06, Br, 0, 0,ZERO },
589 /* 61 */ {(uchar *) "restart", 3, 0, -1, 0x07, 0, 0, 0,ZERO },
590 /* 62 */ {(uchar *) "ret_popped", 3, 0, -1, 0x08, Rf, 0, 0,ZERO },
591 /* 63 */ {(uchar *) "pop", 3, 4, -1, 0x09, 0, 0, 0,ZERO },
592 /* 64 */ {(uchar *) "quit", 3, 0, -1, 0x0A, Rf, 0, 0,ZERO },
593 /* 65 */ {(uchar *) "new_line", 3, 0, -1, 0x0B, 0, 0, 0,ZERO },
594 /* 66 */ {(uchar *) "show_status", 3, 3, -1, 0x0C, 0, 0, 0,ZERO },
595 /* 67 */ {(uchar *) "verify", 3, 0, -1, 0x0D, Br, 0, 0,ZERO },
597 /* Opcodes introduced in Version 4 */
599 /* 68 */ {(uchar *) "call_2s", 4, 0, -1, 0x19, St, CALL, 0, TWO },
600 /* 69 */ {(uchar *) "call_vs", 4, 0, -1, 0x20, St, CALL, 0, VAR },
601 /* This is the version of "read" called "aread" internally: */
602 /* 70 */ {(uchar *) "read", 4, 0, -1, 0x24, St, 0, 0, VAR },
603 /* 71 */ {(uchar *) "call_vs2", 4, 0, -1, 0x2C, St, CALL, 0,
605 /* 72 */ {(uchar *) "erase_window", 4, 0, -1, 0x2D, 0, 0, 0, VAR },
606 /* 73 */ {(uchar *) "erase_line", 4, 0, -1, 0x2E, 0, 0, 0, VAR },
607 /* 74 */ {(uchar *) "set_cursor", 4, 0, -1, 0x2F, 0, 0, 0, VAR },
608 /* 75 */ {(uchar *) "get_cursor", 4, 0, -1, 0x30, 0, 0, 0, VAR },
609 /* 76 */ {(uchar *) "set_text_style", 4, 0, -1, 0x31, 0, 0, 0, VAR },
610 /* 77 */ {(uchar *) "buffer_mode", 4, 0, -1, 0x32, 0, 0, 0, VAR },
611 /* 78 */ {(uchar *) "read_char", 4, 0, -1, 0x36, St, 0, 0, VAR },
612 /* 79 */ {(uchar *) "scan_table", 4, 0, -1, 0x37, St+Br, 0, 0, VAR },
613 /* 80 */ {(uchar *) "call_1s", 4, 0, -1, 0x08, St, CALL, 0, ONE },
615 /* Opcodes introduced in Version 5 */
617 /* 81 */ {(uchar *) "call_2n", 5, 0, -1, 0x1a, 0, CALL, 0, TWO },
618 /* 82 */ {(uchar *) "set_colour", 5, 0, -1, 0x1b, 0, 0, 6, TWO },
619 /* 83 */ {(uchar *) "throw", 5, 0, -1, 0x1c, 0, 0, 0, TWO },
620 /* 84 */ {(uchar *) "call_vn", 5, 0, -1, 0x39, 0, CALL, 0, VAR },
621 /* 85 */ {(uchar *) "call_vn2", 5, 0, -1, 0x3a, 0, CALL, 0,
623 /* 86 */ {(uchar *) "tokenise", 5, 0, -1, 0x3b, 0, 0, 0, VAR },
624 /* 87 */ {(uchar *) "encode_text", 5, 0, -1, 0x3c, 0, 0, 0, VAR },
625 /* 88 */ {(uchar *) "copy_table", 5, 0, -1, 0x3d, 0, 0, 0, VAR },
626 /* 89 */ {(uchar *) "print_table", 5, 0, -1, 0x3e, 0, 0, 0, VAR },
627 /* 90 */ {(uchar *) "check_arg_count", 5, 0, -1, 0x3f, Br, 0, 0, VAR },
628 /* 91 */ {(uchar *) "call_1n", 5, 0, -1, 0x0F, 0, CALL, 0, ONE },
629 /* 92 */ {(uchar *) "catch", 5, 0, -1, 0x09, St, 0, 0, ZERO },
630 /* 93 */ {(uchar *) "piracy", 5, 0, -1, 0x0F, Br, 0, 0, ZERO },
631 /* 94 */ {(uchar *) "log_shift", 5, 0, -1, 0x02, St, 0, 0, EXT },
632 /* 95 */ {(uchar *) "art_shift", 5, 0, -1, 0x03, St, 0, 0, EXT },
633 /* 96 */ {(uchar *) "set_font", 5, 0, -1, 0x04, St, 0, 0, EXT },
634 /* 97 */ {(uchar *) "save_undo", 5, 0, -1, 0x09, St, 0, 4, EXT },
635 /* 98 */ {(uchar *) "restore_undo", 5, 0, -1, 0x0A, St, 0, 4, EXT },
637 /* Opcodes introduced in Version 6 */
639 /* 99 */ { (uchar *) "draw_picture", 6, 6, -1, 0x05, 0, 0, 3, EXT },
640 /* 100 */ { (uchar *) "picture_data", 6, 6, -1, 0x06, Br, 0, 3, EXT },
641 /* 101 */ { (uchar *) "erase_picture", 6, 6, -1, 0x07, 0, 0, 3, EXT },
642 /* 102 */ { (uchar *) "set_margins", 6, 6, -1, 0x08, 0, 0, 0, EXT },
643 /* 103 */ { (uchar *) "move_window", 6, 6, -1, 0x10, 0, 0, 0, EXT },
644 /* 104 */ { (uchar *) "window_size", 6, 6, -1, 0x11, 0, 0, 0, EXT },
645 /* 105 */ { (uchar *) "window_style", 6, 6, -1, 0x12, 0, 0, 0, EXT },
646 /* 106 */ { (uchar *) "get_wind_prop", 6, 6, -1, 0x13, St, 0, 0, EXT },
647 /* 107 */ { (uchar *) "scroll_window", 6, 6, -1, 0x14, 0, 0, 0, EXT },
648 /* 108 */ { (uchar *) "pop_stack", 6, 6, -1, 0x15, 0, 0, 0, EXT },
649 /* 109 */ { (uchar *) "read_mouse", 6, 6, -1, 0x16, 0, 0, 5, EXT },
650 /* 110 */ { (uchar *) "mouse_window", 6, 6, -1, 0x17, 0, 0, 5, EXT },
651 /* 111 */ { (uchar *) "push_stack", 6, 6, -1, 0x18, Br, 0, 0, EXT },
652 /* 112 */ { (uchar *) "put_wind_prop", 6, 6, -1, 0x19, 0, 0, 0, EXT },
653 /* 113 */ { (uchar *) "print_form", 6, 6, -1, 0x1a, 0, 0, 0, EXT },
654 /* 114 */ { (uchar *) "make_menu", 6, 6, -1, 0x1b, Br, 0, 8, EXT },
655 /* 115 */ { (uchar *) "picture_table", 6, 6, -1, 0x1c, 0, 0, 3, EXT },
657 /* Opcodes introduced in Z-Machine Specification Standard 1.0 */
659 /* 116 */ { (uchar *) "print_unicode", 5, 0, -1, 0x0b, 0, 0, 0, EXT },
660 /* 117 */ { (uchar *) "check_unicode", 5, 0, -1, 0x0c, St, 0, 0, EXT }
663 /* Subsequent forms for opcodes whose meaning changes with version */
665 static opcodez extension_table_z[] =
667 /* 0 */ { (uchar *) "not", 4, 4, 3, 0x0F, St, 0, 0, ONE },
668 /* 1 */ { (uchar *) "save", 4, 4, 4, 0x05, St, 0, 0,ZERO },
669 /* 2 */ { (uchar *) "restore", 4, 4, 5, 0x06, St, 0, 0,ZERO },
670 /* 3 */ { (uchar *) "not", 5, 0, -1, 0x38, St, 0, 0, VAR },
671 /* 4 */ { (uchar *) "save", 5, 0, -1, 0x00, St, 0, 0, EXT },
672 /* 5 */ { (uchar *) "restore", 5, 0, -1, 0x01, St, 0, 0, EXT },
673 /* 6 */ { (uchar *) "pull", 6, 6, -1, 0x29, St, 0, 0, VAR }
676 static opcodez invalid_opcode_z =
677 { (uchar *) "invalid", 0, 0, -1, 0xff, 0, 0, 0, ZERO};
679 static opcodez custom_opcode_z;
681 /* Note that this table assumes that all opcodes have at most two
682 branch-label or store operands, and that if they exist, they are the
683 last operands. Glulx does not actually guarantee this. But it is
684 true for all opcodes in the current Glulx spec, so we will assume
687 Also note that Inform can only compile branches to constant offsets,
688 even though the Glulx machine can handle stack or memory-loaded
689 operands in a branch instruction.
692 static opcodeg opcodes_table_g[] = {
693 { (uchar *) "nop", 0x00, 0, 0, 0 },
694 { (uchar *) "add", 0x10, St, 0, 3 },
695 { (uchar *) "sub", 0x11, St, 0, 3 },
696 { (uchar *) "mul", 0x12, St, 0, 3 },
697 { (uchar *) "div", 0x13, St, 0, 3 },
698 { (uchar *) "mod", 0x14, St, 0, 3 },
699 { (uchar *) "neg", 0x15, St, 0, 2 },
700 { (uchar *) "bitand", 0x18, St, 0, 3 },
701 { (uchar *) "bitor", 0x19, St, 0, 3 },
702 { (uchar *) "bitxor", 0x1A, St, 0, 3 },
703 { (uchar *) "bitnot", 0x1B, St, 0, 2 },
704 { (uchar *) "shiftl", 0x1C, St, 0, 3 },
705 { (uchar *) "sshiftr", 0x1D, St, 0, 3 },
706 { (uchar *) "ushiftr", 0x1E, St, 0, 3 },
707 { (uchar *) "jump", 0x20, Br|Rf, 0, 1 },
708 { (uchar *) "jz", 0x22, Br, 0, 2 },
709 { (uchar *) "jnz", 0x23, Br, 0, 2 },
710 { (uchar *) "jeq", 0x24, Br, 0, 3 },
711 { (uchar *) "jne", 0x25, Br, 0, 3 },
712 { (uchar *) "jlt", 0x26, Br, 0, 3 },
713 { (uchar *) "jge", 0x27, Br, 0, 3 },
714 { (uchar *) "jgt", 0x28, Br, 0, 3 },
715 { (uchar *) "jle", 0x29, Br, 0, 3 },
716 { (uchar *) "jltu", 0x2A, Br, 0, 3 },
717 { (uchar *) "jgeu", 0x2B, Br, 0, 3 },
718 { (uchar *) "jgtu", 0x2C, Br, 0, 3 },
719 { (uchar *) "jleu", 0x2D, Br, 0, 3 },
720 { (uchar *) "call", 0x30, St, 0, 3 },
721 { (uchar *) "return", 0x31, Rf, 0, 1 },
722 { (uchar *) "catch", 0x32, Br|St, 0, 2 },
723 { (uchar *) "throw", 0x33, Rf, 0, 2 },
724 { (uchar *) "tailcall", 0x34, Rf, 0, 2 },
725 { (uchar *) "copy", 0x40, St, 0, 2 },
726 { (uchar *) "copys", 0x41, St, 0, 2 },
727 { (uchar *) "copyb", 0x42, St, 0, 2 },
728 { (uchar *) "sexs", 0x44, St, 0, 2 },
729 { (uchar *) "sexb", 0x45, St, 0, 2 },
730 { (uchar *) "aload", 0x48, St, 0, 3 },
731 { (uchar *) "aloads", 0x49, St, 0, 3 },
732 { (uchar *) "aloadb", 0x4A, St, 0, 3 },
733 { (uchar *) "aloadbit", 0x4B, St, 0, 3 },
734 { (uchar *) "astore", 0x4C, 0, 0, 3 },
735 { (uchar *) "astores", 0x4D, 0, 0, 3 },
736 { (uchar *) "astoreb", 0x4E, 0, 0, 3 },
737 { (uchar *) "astorebit", 0x4F, 0, 0, 3 },
738 { (uchar *) "stkcount", 0x50, St, 0, 1 },
739 { (uchar *) "stkpeek", 0x51, St, 0, 2 },
740 { (uchar *) "stkswap", 0x52, 0, 0, 0 },
741 { (uchar *) "stkroll", 0x53, 0, 0, 2 },
742 { (uchar *) "stkcopy", 0x54, 0, 0, 1 },
743 { (uchar *) "streamchar", 0x70, 0, 0, 1 },
744 { (uchar *) "streamnum", 0x71, 0, 0, 1 },
745 { (uchar *) "streamstr", 0x72, 0, 0, 1 },
746 { (uchar *) "gestalt", 0x0100, St, 0, 3 },
747 { (uchar *) "debugtrap", 0x0101, 0, 0, 1 },
748 { (uchar *) "getmemsize", 0x0102, St, 0, 1 },
749 { (uchar *) "setmemsize", 0x0103, St, 0, 2 },
750 { (uchar *) "jumpabs", 0x0104, Rf, 0, 1 },
751 { (uchar *) "random", 0x0110, St, 0, 2 },
752 { (uchar *) "setrandom", 0x0111, 0, 0, 1 },
753 { (uchar *) "quit", 0x0120, Rf, 0, 0 },
754 { (uchar *) "verify", 0x0121, St, 0, 1 },
755 { (uchar *) "restart", 0x0122, 0, 0, 0 },
756 { (uchar *) "save", 0x0123, St, 0, 2 },
757 { (uchar *) "restore", 0x0124, St, 0, 2 },
758 { (uchar *) "saveundo", 0x0125, St, 0, 1 },
759 { (uchar *) "restoreundo", 0x0126, St, 0, 1 },
760 { (uchar *) "protect", 0x0127, 0, 0, 2 },
761 { (uchar *) "glk", 0x0130, St, 0, 3 },
762 { (uchar *) "getstringtbl", 0x0140, St, 0, 1 },
763 { (uchar *) "setstringtbl", 0x0141, 0, 0, 1 },
764 { (uchar *) "getiosys", 0x0148, St|St2, 0, 2 },
765 { (uchar *) "setiosys", 0x0149, 0, 0, 2 },
766 { (uchar *) "linearsearch", 0x0150, St, 0, 8 },
767 { (uchar *) "binarysearch", 0x0151, St, 0, 8 },
768 { (uchar *) "linkedsearch", 0x0152, St, 0, 7 },
769 { (uchar *) "callf", 0x0160, St, 0, 2 },
770 { (uchar *) "callfi", 0x0161, St, 0, 3 },
771 { (uchar *) "callfii", 0x0162, St, 0, 4 },
772 { (uchar *) "callfiii", 0x0163, St, 0, 5 },
773 { (uchar *) "streamunichar", 0x73, 0, GOP_Unicode, 1 },
774 { (uchar *) "mzero", 0x170, 0, GOP_MemHeap, 2 },
775 { (uchar *) "mcopy", 0x171, 0, GOP_MemHeap, 3 },
776 { (uchar *) "malloc", 0x178, St, GOP_MemHeap, 2 },
777 { (uchar *) "mfree", 0x179, 0, GOP_MemHeap, 1 },
778 { (uchar *) "accelfunc", 0x180, 0, GOP_Acceleration, 2 },
779 { (uchar *) "accelparam", 0x181, 0, GOP_Acceleration, 2 },
780 { (uchar *) "hasundo", 0x128, St, GOP_ExtUndo, 1 },
781 { (uchar *) "discardundo",0x129, 0, GOP_ExtUndo, 0 },
782 { (uchar *) "numtof", 0x190, St, GOP_Float, 2 },
783 { (uchar *) "ftonumz", 0x191, St, GOP_Float, 2 },
784 { (uchar *) "ftonumn", 0x192, St, GOP_Float, 2 },
785 { (uchar *) "ceil", 0x198, St, GOP_Float, 2 },
786 { (uchar *) "floor", 0x199, St, GOP_Float, 2 },
787 { (uchar *) "fadd", 0x1A0, St, GOP_Float, 3 },
788 { (uchar *) "fsub", 0x1A1, St, GOP_Float, 3 },
789 { (uchar *) "fmul", 0x1A2, St, GOP_Float, 3 },
790 { (uchar *) "fdiv", 0x1A3, St, GOP_Float, 3 },
791 { (uchar *) "fmod", 0x1A4, St|St2, GOP_Float, 4 },
792 { (uchar *) "sqrt", 0x1A8, St, GOP_Float, 2 },
793 { (uchar *) "exp", 0x1A9, St, GOP_Float, 2 },
794 { (uchar *) "log", 0x1AA, St, GOP_Float, 2 },
795 { (uchar *) "pow", 0x1AB, St, GOP_Float, 3 },
796 { (uchar *) "sin", 0x1B0, St, GOP_Float, 2 },
797 { (uchar *) "cos", 0x1B1, St, GOP_Float, 2 },
798 { (uchar *) "tan", 0x1B2, St, GOP_Float, 2 },
799 { (uchar *) "asin", 0x1B3, St, GOP_Float, 2 },
800 { (uchar *) "acos", 0x1B4, St, GOP_Float, 2 },
801 { (uchar *) "atan", 0x1B5, St, GOP_Float, 2 },
802 { (uchar *) "atan2", 0x1B6, St, GOP_Float, 3 },
803 { (uchar *) "jfeq", 0x1C0, Br, GOP_Float, 4 },
804 { (uchar *) "jfne", 0x1C1, Br, GOP_Float, 4 },
805 { (uchar *) "jflt", 0x1C2, Br, GOP_Float, 3 },
806 { (uchar *) "jfle", 0x1C3, Br, GOP_Float, 3 },
807 { (uchar *) "jfgt", 0x1C4, Br, GOP_Float, 3 },
808 { (uchar *) "jfge", 0x1C5, Br, GOP_Float, 3 },
809 { (uchar *) "jisnan", 0x1C8, Br, GOP_Float, 2 },
810 { (uchar *) "jisinf", 0x1C9, Br, GOP_Float, 2 },
811 { (uchar *) "numtod", 0x200, St|St2, GOP_Double, 3 },
812 { (uchar *) "dtonumz", 0x201, St, GOP_Double, 3 },
813 { (uchar *) "dtonumn", 0x202, St, GOP_Double, 3 },
814 { (uchar *) "ftod", 0x203, St|St2, GOP_Double, 3 },
815 { (uchar *) "dtof", 0x204, St, GOP_Double, 3 },
816 { (uchar *) "dceil", 0x208, St|St2, GOP_Double, 4 },
817 { (uchar *) "dfloor", 0x209, St|St2, GOP_Double, 4 },
818 { (uchar *) "dadd", 0x210, St|St2, GOP_Double, 6 },
819 { (uchar *) "dsub", 0x211, St|St2, GOP_Double, 6 },
820 { (uchar *) "dmul", 0x212, St|St2, GOP_Double, 6 },
821 { (uchar *) "ddiv", 0x213, St|St2, GOP_Double, 6 },
822 { (uchar *) "dmodr", 0x214, St|St2, GOP_Double, 6 },
823 { (uchar *) "dmodq", 0x215, St|St2, GOP_Double, 6 },
824 { (uchar *) "dsqrt", 0x218, St|St2, GOP_Double, 4 },
825 { (uchar *) "dexp", 0x219, St|St2, GOP_Double, 4 },
826 { (uchar *) "dlog", 0x21A, St|St2, GOP_Double, 4 },
827 { (uchar *) "dpow", 0x21B, St|St2, GOP_Double, 6 },
828 { (uchar *) "dsin", 0x220, St|St2, GOP_Double, 4 },
829 { (uchar *) "dcos", 0x221, St|St2, GOP_Double, 4 },
830 { (uchar *) "dtan", 0x222, St|St2, GOP_Double, 4 },
831 { (uchar *) "dasin", 0x223, St|St2, GOP_Double, 4 },
832 { (uchar *) "dacos", 0x224, St|St2, GOP_Double, 4 },
833 { (uchar *) "datan", 0x225, St|St2, GOP_Double, 4 },
834 { (uchar *) "datan2", 0x226, St|St2, GOP_Double, 6 },
835 { (uchar *) "jdeq", 0x230, Br, GOP_Double, 7 },
836 { (uchar *) "jdne", 0x231, Br, GOP_Double, 7 },
837 { (uchar *) "jdlt", 0x232, Br, GOP_Double, 5 },
838 { (uchar *) "jdle", 0x233, Br, GOP_Double, 5 },
839 { (uchar *) "jdgt", 0x234, Br, GOP_Double, 5 },
840 { (uchar *) "jdge", 0x235, Br, GOP_Double, 5 },
841 { (uchar *) "jdisnan", 0x238, Br, GOP_Double, 3 },
842 { (uchar *) "jdisinf", 0x239, Br, GOP_Double, 3 },
845 /* The opmacros table is used for fake opcodes. The opcode numbers are
846 ignored; this table is only used for argument parsing. */
847 static opcodeg opmacros_table_g[] = {
848 { (uchar *) "pull", pull_gm, St, 0, 1 },
849 { (uchar *) "push", push_gm, 0, 0, 1 },
850 { (uchar *) "dload", dload_gm, St|St2, 0, 3 },
851 { (uchar *) "dstore", dstore_gm, 0, 0, 3 },
854 static opcodeg custom_opcode_g;
856 static opcodez internal_number_to_opcode_z(int32 i)
859 if (i == -1) return custom_opcode_z;
860 x = opcodes_table_z[i];
861 if (instruction_set_number < x.version1) return invalid_opcode_z;
862 if (x.version2 == 0) return x;
863 if (instruction_set_number <= x.version2) return x;
865 if (i < 0) return invalid_opcode_z;
866 x = extension_table_z[i];
867 if (instruction_set_number < x.version1) return invalid_opcode_z;
868 if (x.version2 == 0) return x;
869 if (instruction_set_number <= x.version2) return x;
870 return extension_table_z[x.extension];
873 static void make_opcode_syntax_z(opcodez opco)
874 { char *p = "", *q = opcode_syntax_string;
875 sprintf(q, "%s", opco.name);
877 { case ONE: p=" <operand>"; break;
878 case TWO: p=" <operand1> <operand2>"; break;
880 case VAR: p=" <0 to 4 operands>"; break;
881 case VAR_LONG: p=" <0 to 8 operands>"; break;
883 switch(opco.op_rules)
884 { case TEXT: sprintf(q+strlen(q), " <text>"); return;
885 case LABEL: sprintf(q+strlen(q), " <label>"); return;
887 sprintf(q+strlen(q), " <variable>");
889 if (opco.op_rules==CALL) sprintf(q+strlen(q), " <routine>");
891 { case ONE: p=""; break;
892 case TWO: p=" <operand>"; break;
894 case VAR: p=" <1 to 4 operands>"; break;
895 case VAR_LONG: p=" <1 to 8 operands>"; break;
899 sprintf(q+strlen(q), "%s", p);
900 if ((opco.flags & St) != 0) sprintf(q+strlen(q), " -> <result-variable>");
901 if ((opco.flags & Br) != 0) sprintf(q+strlen(q), " ?[~]<label>");
904 static opcodeg internal_number_to_opcode_g(int32 i)
907 if (i == -1) return custom_opcode_g;
908 x = opcodes_table_g[i];
912 static opcodeg internal_number_to_opmacro_g(int32 i)
914 return opmacros_table_g[i];
917 static void make_opcode_syntax_g(opcodeg opco)
921 char *q = opcode_syntax_string;
923 sprintf(q, "%s", opco.name);
924 sprintf(q+strlen(q), " <%d operand%s", opco.no,
925 ((opco.no==1) ? "" : "s"));
930 for (ix=0; ix<opco.no; ix++) {
935 if (ix == opco.no-1) {
936 if (opco.flags & Br) {
939 else if (opco.flags & St) {
946 else if (ix == opco.no-2 && (opco.flags & Br) && (opco.flags & St)) {
949 else if (ix == opco.no-2 && (opco.flags & St2)) {
956 sprintf(cx, "%d", ix+1);
960 sprintf(q+strlen(q), ">");
964 /* ========================================================================= */
965 /* The assembler itself does four things: */
967 /* assembles instructions */
968 /* sets label N to the current code position */
969 /* assembles routine headers */
970 /* assembles routine ends */
971 /* ------------------------------------------------------------------------- */
973 /* This is for Z-code only. */
974 static void write_operand(assembly_operand op)
978 { case LONG_CONSTANT_OT:
979 byteout(j/256, op.marker); byteout(j%256, 0); return;
980 case SHORT_CONSTANT_OT:
983 else byteout(j, 0x80 + op.marker); return;
985 byteout(j, 0); return;
987 case HALFCONSTANT_OT:
988 case BYTECONSTANT_OT:
989 case ZEROCONSTANT_OT:
994 compiler_error("Glulx OT in Z-code assembly operand.");
999 extern void assemblez_instruction(const assembly_instruction *AI)
1003 int32 offset, j, topbits=0, types_byte1, types_byte2;
1004 int operand_rules, min=0, max=0, no_operands_given, at_seq_point = FALSE;
1005 assembly_operand o1, o2;
1010 if (execution_never_reaches_here) {
1011 if (!(execution_never_reaches_here & EXECSTATE_NOWARN)) {
1012 warning("This statement can never be reached");
1013 /* only show the warning once */
1014 execution_never_reaches_here |= EXECSTATE_NOWARN;
1019 offset = zmachine_pc;
1023 if (veneer_mode) sequence_point_follows = FALSE;
1024 if (sequence_point_follows)
1025 { sequence_point_follows = FALSE; at_seq_point = TRUE;
1026 if (debugfile_switch)
1028 ensure_memory_list_available(&sequence_points_memlist, next_sequence_point+1);
1029 sequence_points[next_sequence_point].label = next_label;
1030 sequence_points[next_sequence_point].location =
1031 statement_debug_location;
1032 set_label_offset(next_label++, zmachine_pc);
1034 next_sequence_point++;
1037 opco = internal_number_to_opcode_z(AI->internal_number);
1038 if (opco.version1==0)
1039 { error_named("Opcode unavailable in this Z-machine version",
1040 opcode_names.keywords[AI->internal_number]);
1044 operand_rules = opco.op_rules;
1045 execution_never_reaches_here = ((opco.flags & Rf) ? EXECSTATE_UNREACHABLE : EXECSTATE_REACHABLE);
1047 if (opco.flags2_set != 0) flags2_requirements[opco.flags2_set] = 1;
1049 no_operands_given = AI->operand_count;
1051 if ((opco.no == TWO) && ((no_operands_given==3)||(no_operands_given==4)))
1054 /* 1. Write the opcode byte(s) */
1056 start_pc = zcode_ha_size;
1059 { case VAR_LONG: topbits=0xc0; min=0; max=8; break;
1060 case VAR: topbits=0xc0; min=0; max=4; break;
1061 case ZERO: topbits=0xb0; min=0; max=0; break;
1062 case ONE: topbits=0x80; min=1; max=1; break;
1063 case TWO: topbits=0x00; min=2; max=2; break;
1064 case EXT: topbits=0x00; min=0; max=4;
1065 byteout(0xbe, 0); opco.no=VAR; break;
1066 case EXT_LONG: topbits=0x00; min=0; max=8;
1067 byteout(0xbe, 0); opco.no=VAR_LONG; break;
1069 byteout(opco.code + topbits, 0);
1071 operands_pc = zcode_ha_size;
1073 /* 2. Dispose of the special rules LABEL and TEXT */
1075 if (operand_rules==LABEL)
1076 { j = (AI->operand[0]).value;
1078 byteout(j/256, LABEL_MV); byteout(j%256, 0);
1079 goto Instruction_Done;
1082 if (operand_rules==TEXT)
1084 j = translate_text(-1, AI->text, STRCTX_GAMEOPC);
1086 error("text translation failed");
1089 ensure_memory_list_available(&zcode_markers_memlist, zcode_ha_size+j);
1090 ensure_memory_list_available(&zcode_holding_area_memlist, zcode_ha_size+j);
1091 for (i=0; i<j; i++) {
1092 zcode_holding_area[zcode_ha_size] = translated_text[i];
1093 zcode_markers[zcode_ha_size] = 0;
1097 goto Instruction_Done;
1100 /* 3. Sort out the operands */
1102 if ((no_operands_given < min) || (no_operands_given > max))
1103 goto OpcodeSyntaxError;
1109 if (opco.no == VAR_LONG) byteout(0, 0);
1110 types_byte1=0xff; types_byte2=0xff;
1111 for (j=0; j<no_operands_given; j++)
1112 { int multi=0, mask=0;
1114 { case 0: case 4: multi=0x40; mask=0xc0; break;
1115 case 1: case 5: multi=0x10; mask=0x30; break;
1116 case 2: case 6: multi=0x04; mask=0x0c; break;
1117 case 3: case 7: multi=0x01; mask=0x03; break;
1119 o1 = AI->operand[j];
1122 types_byte1 = (types_byte1 & (~mask)) + o1.type*multi;
1124 types_byte2 = (types_byte2 & (~mask)) + o1.type*multi;
1126 zcode_holding_area[operands_pc]=types_byte1;
1127 if (opco.no == VAR_LONG) zcode_holding_area[operands_pc+1]=types_byte2;
1131 o1 = AI->operand[0];
1132 zcode_holding_area[start_pc] += o1.type*0x10;
1137 o1 = AI->operand[0];
1138 o2 = AI->operand[1];
1140 /* Transfer to VAR form if either operand is a long constant */
1142 if ((o1.type==LONG_CONSTANT_OT)||(o2.type==LONG_CONSTANT_OT))
1143 { zcode_holding_area[start_pc] += 0xc0;
1144 byteout(o1.type*0x40 + o2.type*0x10 + 0x0f, 0);
1147 { if (o1.type==VARIABLE_OT) zcode_holding_area[start_pc] += 0x40;
1148 if (o2.type==VARIABLE_OT) zcode_holding_area[start_pc] += 0x20;
1155 /* 4. Assemble a Store destination, if needed */
1157 if ((AI->store_variable_number) != -1)
1158 { if (AI->store_variable_number >= MAX_LOCAL_VARIABLES+MAX_ZCODE_GLOBAL_VARS) {
1159 goto OpcodeSyntaxError;
1161 o1.type = VARIABLE_OT;
1162 o1.value = AI->store_variable_number;
1163 variables[o1.value].usage = TRUE;
1166 /* Note that variable numbers 249 to 255 (i.e. globals 233 to 239)
1167 are used as scratch workspace, so need no mapping between
1168 modules and story files: nor do local variables 0 to 15.
1169 (Modules no longer exist but why drop a good comment.) */
1171 if ((o1.value >= MAX_LOCAL_VARIABLES) && (o1.value < 249))
1172 o1.marker = VARIABLE_MV;
1176 /* 5. Assemble a branch, if needed */
1178 if (AI->branch_label_number != -1)
1179 { int32 addr, long_form;
1180 int branch_on_true = (AI->branch_flag)?1:0;
1181 mark_label_used(AI->branch_label_number);
1182 switch (AI->branch_label_number)
1183 { case -2: addr = 2; branch_on_true = 0; long_form = 0; break;
1184 /* branch nowhere, carry on */
1185 case -3: addr = 0; long_form = 0; break; /* rfalse on condition */
1186 case -4: addr = 1; long_form = 0; break; /* rtrue on condition */
1188 long_form = 1; addr = AI->branch_label_number;
1191 if (addr > 0x7fff) fatalerror("Too many branch points in routine.");
1193 { byteout(branch_on_true*0x80 + addr/256, BRANCH_MV);
1194 byteout(addr%256, 0);
1197 byteout(branch_on_true*0x80+ 0x40 + (addr&0x3f), 0);
1202 if (asm_trace_level > 0)
1204 printf("%5d +%05lx %3s %-12s ", ErrorReport.line_number,
1205 ((long int) offset),
1206 (at_seq_point)?"<*>":" ", opco.name);
1208 if ((AI->internal_number == print_zc)
1209 || (AI->internal_number == print_ret_zc))
1211 for (i=0;(AI->text)[i]!=0 && i<35; i++) printf("%c",(AI->text)[i]);
1212 if (i == 35) printf("...");
1216 for (i=0; i<AI->operand_count; i++)
1217 { if ((i==0) && (opco.op_rules == VARIAB))
1218 { if ((AI->operand[0]).type == VARIABLE_OT)
1219 { printf("["); print_operand_z(&AI->operand[i], TRUE); }
1221 printf("%s", variable_name((AI->operand[0]).value));
1224 if ((i==0) && (opco.op_rules == LABEL))
1225 { printf("L%d", AI->operand[0].value);
1227 else print_operand_z(&AI->operand[i], TRUE);
1230 if (AI->store_variable_number != -1)
1231 { assembly_operand AO;
1233 AO.type = VARIABLE_OT; AO.value = AI->store_variable_number;
1234 print_operand_z(&AO, TRUE); printf(" ");
1237 switch(AI->branch_label_number)
1238 { case -4: printf("rtrue if %s", (AI->branch_flag)?"TRUE":"FALSE");
1240 case -3: printf("rfalse if %s", (AI->branch_flag)?"TRUE":"FALSE");
1242 case -2: printf("(no branch)"); break;
1245 printf("to L%d if %s", AI->branch_label_number,
1246 (AI->branch_flag)?"TRUE":"FALSE"); break;
1249 if (asm_trace_level>=2)
1250 { for (j=0;start_pc<zcode_ha_size;
1252 { if (j%16==0) printf("\n ");
1253 printf("%02x ", zcode_holding_area[start_pc]);
1263 make_opcode_syntax_z(opco);
1264 error_named("Assembly mistake: syntax is", opcode_syntax_string);
1267 static void assembleg_macro(const assembly_instruction *AI)
1269 int ix, no_operands_given;
1271 assembly_operand AMO_0, AMO_1, AMO_2;
1273 /* validate macro syntax first */
1275 opco = internal_number_to_opmacro_g(AI->internal_number);
1276 no_operands_given = AI->operand_count;
1278 if (no_operands_given != opco.no)
1279 goto OpcodeSyntaxError;
1281 for (ix = 0; ix < no_operands_given; ix++) {
1282 int type = AI->operand[ix].type;
1283 if ((opco.flags & St)
1284 && ((!(opco.flags & Br) && (ix == no_operands_given-1))
1285 || ((opco.flags & Br) && (ix == no_operands_given-2)))) {
1286 if (is_constant_ot(type)) {
1287 error("*** assembly macro tried to store to a constant ***");
1288 goto OpcodeSyntaxError;
1291 if ((opco.flags & St2)
1292 && (ix == no_operands_given-2)) {
1293 if (is_constant_ot(type)) {
1294 error("*** assembly macro tried to store to a constant ***");
1295 goto OpcodeSyntaxError;
1300 /* Expand the macro.
1301 The assembleg_() functions overwrite AI, so we need to copy out
1302 its operands before we call them. */
1304 switch (opco.code) {
1305 case pull_gm: /* @pull STORE */
1306 AMO_0 = AI->operand[0];
1307 assembleg_store(AMO_0, stack_pointer);
1310 case push_gm: /* @push LOAD */
1311 AMO_0 = AI->operand[0];
1312 assembleg_store(stack_pointer, AMO_0);
1315 case dload_gm: /* @dload LOAD STORELO STOREHI */
1316 AMO_0 = AI->operand[0];
1317 AMO_1 = AI->operand[1];
1318 AMO_2 = AI->operand[2];
1319 if ((AMO_0.type == LOCALVAR_OT) && (AMO_0.value == 0)) {
1320 // addr is on the stack
1321 assembleg_store(temp_var3, stack_pointer);
1322 assembleg_3(aload_gc, temp_var3, one_operand, AMO_1);
1323 assembleg_3(aload_gc, temp_var3, zero_operand, AMO_2);
1326 assembleg_3(aload_gc, AMO_0, one_operand, AMO_1);
1327 assembleg_3(aload_gc, AMO_0, zero_operand, AMO_2);
1331 case dstore_gm: /* @dload LOAD LOADHI LOADLO */
1332 AMO_0 = AI->operand[0];
1333 AMO_1 = AI->operand[1];
1334 AMO_2 = AI->operand[2];
1335 if ((AMO_0.type == LOCALVAR_OT) && (AMO_0.value == 0)) {
1336 // addr is on the stack
1337 assembleg_store(temp_var3, stack_pointer);
1338 assembleg_3(astore_gc, temp_var3, zero_operand, AMO_1);
1339 assembleg_3(astore_gc, temp_var3, one_operand, AMO_2);
1342 assembleg_3(astore_gc, AMO_0, zero_operand, AMO_1);
1343 assembleg_3(astore_gc, AMO_0, one_operand, AMO_2);
1348 compiler_error("Invalid Glulx assembly macro");
1356 make_opcode_syntax_g(opco);
1357 error_named("Assembly mistake: syntax is", opcode_syntax_string);
1360 extern void assembleg_instruction(const assembly_instruction *AI)
1365 int no_operands_given, at_seq_point = FALSE;
1371 if (execution_never_reaches_here) {
1372 if (!(execution_never_reaches_here & EXECSTATE_NOWARN)) {
1373 warning("This statement can never be reached");
1374 /* only show the warning once */
1375 execution_never_reaches_here |= EXECSTATE_NOWARN;
1380 offset = zmachine_pc;
1384 if (veneer_mode) sequence_point_follows = FALSE;
1385 if (sequence_point_follows)
1386 { sequence_point_follows = FALSE; at_seq_point = TRUE;
1387 if (debugfile_switch)
1389 ensure_memory_list_available(&sequence_points_memlist, next_sequence_point+1);
1390 sequence_points[next_sequence_point].label = next_label;
1391 sequence_points[next_sequence_point].location =
1392 statement_debug_location;
1393 set_label_offset(next_label++, zmachine_pc);
1395 next_sequence_point++;
1398 opco = internal_number_to_opcode_g(AI->internal_number);
1400 execution_never_reaches_here = ((opco.flags & Rf) ? EXECSTATE_UNREACHABLE : EXECSTATE_REACHABLE);
1402 if (opco.op_rules & GOP_Unicode) {
1403 uses_unicode_features = TRUE;
1405 if (opco.op_rules & GOP_MemHeap) {
1406 uses_memheap_features = TRUE;
1408 if (opco.op_rules & GOP_Acceleration) {
1409 uses_acceleration_features = TRUE;
1411 if (opco.op_rules & GOP_Float) {
1412 uses_float_features = TRUE;
1414 if (opco.op_rules & GOP_ExtUndo) {
1415 uses_extundo_features = TRUE;
1417 if (opco.op_rules & GOP_Double) {
1418 uses_double_features = TRUE;
1421 no_operands_given = AI->operand_count;
1423 /* 1. Write the opcode byte(s) */
1425 start_pc = zcode_ha_size;
1427 if (opco.code < 0x80) {
1428 byteout(opco.code, 0);
1430 else if (opco.code < 0x4000) {
1431 byteout(((opco.code >> 8) & 0xFF) | 0x80, 0);
1432 byteout((opco.code & 0xFF), 0);
1435 byteout(((opco.code >> 24) & 0xFF) | 0xC0, 0);
1436 byteout(((opco.code >> 16) & 0xFF), 0);
1437 byteout(((opco.code >> 8) & 0xFF), 0);
1438 byteout(((opco.code) & 0xFF), 0);
1441 /* ... and the operand addressing modes. There's one byte for
1442 every two operands (rounded up). We write zeroes for now;
1443 when the operands are written, we'll go back and fix them. */
1445 opmodes_pc = zcode_ha_size;
1447 for (ix=0; ix<opco.no; ix+=2) {
1451 /* 2. Dispose of the special rules */
1452 /* There aren't any in Glulx. */
1454 /* 3. Sort out the operands */
1456 if (no_operands_given != opco.no) {
1457 goto OpcodeSyntaxError;
1460 for (ix=0; ix<no_operands_given; ix++) {
1461 int marker = AI->operand[ix].marker;
1462 int type = AI->operand[ix].type;
1463 k = AI->operand[ix].value;
1465 if ((opco.flags & Br) && (ix == no_operands_given-1)) {
1466 if (!(marker >= BRANCH_MV && marker < BRANCHMAX_MV)) {
1467 compiler_error("Assembling branch without BRANCH_MV marker");
1468 goto OpcodeSyntaxError;
1472 k = 2; /* branch no-op */
1473 type = BYTECONSTANT_OT;
1477 k = 0; /* branch return 0 */
1478 type = ZEROCONSTANT_OT;
1482 k = 1; /* branch return 1 */
1483 type = BYTECONSTANT_OT;
1487 /* branch to label k */
1488 j = (zcode_ha_size - opmodes_pc);
1490 marker = BRANCH_MV + j;
1491 if (!(marker >= BRANCH_MV && marker < BRANCHMAX_MV)) {
1492 error("*** branch marker too far from opmode byte ***");
1493 goto OpcodeSyntaxError;
1497 if ((opco.flags & St)
1498 && ((!(opco.flags & Br) && (ix == no_operands_given-1))
1499 || ((opco.flags & Br) && (ix == no_operands_given-2)))) {
1500 if (type == BYTECONSTANT_OT || type == HALFCONSTANT_OT
1501 || type == CONSTANT_OT) {
1502 error("*** instruction tried to store to a constant ***");
1503 goto OpcodeSyntaxError;
1506 if ((opco.flags & St2)
1507 && (ix == no_operands_given-2)) {
1508 if (type == BYTECONSTANT_OT || type == HALFCONSTANT_OT
1509 || type == CONSTANT_OT) {
1510 error("*** instruction tried to store to a constant ***");
1511 goto OpcodeSyntaxError;
1515 if (marker && (type == HALFCONSTANT_OT
1516 || type == BYTECONSTANT_OT
1517 || type == ZEROCONSTANT_OT)) {
1518 compiler_error("Assembling marker in less than 32-bit constant.");
1519 /* Actually we should store marker|0x80 for a byte constant,
1520 but let's hold off on that. */
1524 case LONG_CONSTANT_OT:
1525 case SHORT_CONSTANT_OT:
1528 compiler_error("Z-code OT in Glulx assembly operand.");
1532 byteout((k >> 24) & 0xFF, marker);
1533 byteout((k >> 16) & 0xFF, 0);
1534 byteout((k >> 8) & 0xFF, 0);
1535 byteout((k & 0xFF), 0);
1537 case HALFCONSTANT_OT:
1539 byteout((k >> 8) & 0xFF, marker);
1540 byteout((k & 0xFF), 0);
1542 case BYTECONSTANT_OT:
1544 byteout((k & 0xFF), marker);
1546 case ZEROCONSTANT_OT:
1549 case DEREFERENCE_OT:
1551 byteout((k >> 24) & 0xFF, marker);
1552 byteout((k >> 16) & 0xFF, 0);
1553 byteout((k >> 8) & 0xFF, 0);
1554 byteout((k & 0xFF), 0);
1557 /* Global variable -- a constant address. */
1558 k -= MAX_LOCAL_VARIABLES;
1559 if (/* DISABLES CODE */ (0)) {
1560 /* We could write the value as a marker and patch it later... */
1562 byteout(((k) >> 24) & 0xFF, VARIABLE_MV);
1563 byteout(((k) >> 16) & 0xFF, 0);
1564 byteout(((k) >> 8) & 0xFF, 0);
1565 byteout(((k) & 0xFF), 0);
1568 /* ...but it's more efficient to write it as a RAM operand,
1569 which can be 1, 2, or 4 bytes. Remember that global variables
1570 are the very first thing in RAM. */
1571 k = k * 4; /* each variable is four bytes */
1574 byteout(((k) & 0xFF), 0);
1576 else if (k <= 65535) {
1578 byteout(((k) >> 8) & 0xFF, 0);
1579 byteout(((k) & 0xFF), 0);
1583 byteout(((k) >> 24) & 0xFF, 0);
1584 byteout(((k) >> 16) & 0xFF, 0);
1585 byteout(((k) >> 8) & 0xFF, 0);
1586 byteout(((k) & 0xFF), 0);
1592 /* Stack-pointer magic variable */
1596 /* Local variable -- a byte or short offset from the
1597 frame pointer. It's an unsigned offset, so we can
1598 fit up to long 63 (offset 4*63) in a byte. */
1601 byteout((k-1)*4, 0);
1605 byteout((((k-1)*4) >> 8) & 0xFF, 0);
1606 byteout(((k-1)*4) & 0xFF, 0);
1617 zcode_holding_area[opmodes_pc+ix/2] |= j;
1620 /* Print assembly trace. */
1621 if (asm_trace_level > 0) {
1623 printf("%5d +%05lx %3s %-12s ", ErrorReport.line_number,
1624 ((long int) offset),
1625 (at_seq_point)?"<*>":" ", opco.name);
1626 for (i=0; i<AI->operand_count; i++) {
1627 if ((opco.flags & Br) && (i == opco.no-1)) {
1628 if (AI->operand[i].value == -4)
1630 else if (AI->operand[i].value == -3)
1631 printf("to rfalse");
1633 printf("to L%d", AI->operand[i].value);
1636 print_operand_g(&AI->operand[i], TRUE);
1641 if (asm_trace_level>=2) {
1643 start_pc<zcode_ha_size;
1645 if (j%16==0) printf("\n ");
1646 if (/* DISABLES CODE */ (0)) {
1647 printf("%02x ", zcode_holding_area[start_pc]);
1650 printf("%02x", zcode_holding_area[start_pc]);
1651 if (zcode_markers[start_pc])
1652 printf("{%02x}", zcode_markers[start_pc]);
1664 make_opcode_syntax_g(opco);
1665 error_named("Assembly mistake: syntax is", opcode_syntax_string);
1668 /* Set up this label at zmachine_pc.
1669 This resets the execution_never_reaches_here flag, since every label
1670 is assumed to be reachable.
1671 However, if STRIP_UNREACHABLE_LABELS and EXECSTATE_ENTIRE are both set,
1672 that's not true. The entire statement is being skipped, so we can safely
1673 skip all unused labels within it.
1674 ("Unused" meaning there are no forward jumps to the label. We can't
1675 do anything about *backward* jumps because we haven't seen them yet!)
1676 (If STRIP_UNREACHABLE_LABELS is not set, the ENTIRE flag is ignored.)
1678 extern void assemble_label_no(int n)
1680 int inuse = (n >= 0 && n < labeluse_size && labeluse[n]);
1682 if ((!inuse) && (execution_never_reaches_here & EXECSTATE_ENTIRE) && STRIP_UNREACHABLE_LABELS) {
1683 /* We're not going to compile this label at all. Set a negative
1684 offset, which will trip an error if this label is jumped to. */
1685 set_label_offset(n, -1);
1689 if (asm_trace_level > 0)
1690 printf("%5d +%05lx .L%d\n", ErrorReport.line_number,
1691 ((long int) zmachine_pc), n);
1692 set_label_offset(n, zmachine_pc);
1693 execution_never_reaches_here = EXECSTATE_REACHABLE;
1696 /* This is the same as assemble_label_no, except we only set up the label
1697 if there has been a forward branch to it.
1698 Returns whether the label is created.
1699 Only use this for labels which never have backwards branches!
1701 extern int assemble_forward_label_no(int n)
1703 if (n >= 0 && n < labeluse_size && labeluse[n]) {
1704 assemble_label_no(n);
1708 /* There were no forward branches to this label and we promise
1709 there will be no backwards branches to it. Set a negative
1710 offset, which will trip an error if we break our promise. */
1711 set_label_offset(n, -1);
1716 extern void define_symbol_label(int symbol)
1718 int label = symbols[symbol].value;
1719 /* We may be creating a new label (label = next_label) or filling in
1720 the value of an old one. So we call ensure. */
1721 ensure_memory_list_available(&labels_memlist, label+1);
1722 labels[label].symbol = symbol;
1725 extern int32 assemble_routine_header(int no_locals,
1726 int routine_asterisked, char *name, int embedded_flag, int the_symbol)
1728 int stackargs = FALSE;
1731 execution_never_reaches_here = EXECSTATE_REACHABLE;
1733 routine_locals = no_locals;
1735 ensure_memory_list_available(&variables_memlist, MAX_LOCAL_VARIABLES);
1736 for (i=0; i<MAX_LOCAL_VARIABLES; i++) variables[i].usage = FALSE;
1739 && strcmpcis(local_variable_names[0].text, "_vararg_count")==0) {
1743 if (veneer_mode) routine_starts_line = blank_brief_location;
1744 else routine_starts_line = get_brief_location(&ErrorReport);
1746 if (asm_trace_level > 0)
1747 { printf("\n%5d +%05lx [ %s ", ErrorReport.line_number,
1748 ((long int) zmachine_pc), name);
1749 for (i=1; i<=no_locals; i++) printf("%s ", variable_name(i));
1753 routine_start_pc = zmachine_pc;
1755 if (track_unused_routines) {
1756 /* The name of an embedded function is in a temporary buffer,
1757 so we shouldn't keep a reference to it. (It is sad that we
1758 have to know this here.) */
1759 char *funcname = name;
1761 funcname = "<embedded>";
1763 df_note_function_start(funcname, zmachine_pc, embedded_flag,
1764 routine_starts_line);
1767 routine_symbol = the_symbol;
1768 name_length = strlen(name) + 1;
1769 ensure_memory_list_available(¤t_routine_name, name_length);
1770 strncpy(current_routine_name.data, name, name_length);
1772 /* Update the routine counter */
1776 /* Actually assemble the routine header into the code area; note */
1777 /* Inform doesn't support the setting of local variables to default */
1778 /* values other than 0 in V3 and V4. (In V5+ the Z-Machine doesn't */
1779 /* provide the possibility in any case.) */
1784 warning("Z-code does not support stack-argument function definitions.");
1786 byteout(no_locals, 0);
1788 /* Not the packed address, but the scaled offset from code area start: */
1790 rv = zmachine_pc/scale_factor;
1792 if (instruction_set_number<5)
1793 for (i=0; i<no_locals; i++) { byteout(0,0); byteout(0,0); }
1795 next_label = 0; next_sequence_point = 0; last_label = -1;
1798 /* Compile code to print out text like "a=3, b=4, c=5" when the */
1799 /* function is called, if it's required. */
1801 if ((routine_asterisked) || (define_INFIX_switch))
1802 { char fnt[256]; assembly_operand PV, RFA, CON, STP, SLF; int ln, ln2;
1807 if (define_INFIX_switch)
1810 { SLF.value = 251; SLF.type = VARIABLE_OT; SLF.marker = 0;
1811 CON.value = 0; CON.type = SHORT_CONSTANT_OT; CON.marker = 0;
1812 assemblez_2_branch(test_attr_zc, SLF, CON, ln2, FALSE);
1815 { i = no_named_routines++;
1816 ensure_memory_list_available(&named_routine_symbols_memlist, no_named_routines);
1817 named_routine_symbols[i] = the_symbol;
1818 CON.value = i/8; CON.type = LONG_CONSTANT_OT; CON.marker = 0;
1819 RFA.value = routine_flags_array_SC;
1820 RFA.type = LONG_CONSTANT_OT; RFA.marker = INCON_MV;
1821 STP.value = 0; STP.type = VARIABLE_OT; STP.marker = 0;
1822 assemblez_2_to(loadb_zc, RFA, CON, STP);
1823 CON.value = (1 << (i%8)); CON.type = SHORT_CONSTANT_OT;
1824 assemblez_2_to(and_zc, STP, CON, STP);
1825 assemblez_1_branch(jz_zc, STP, ln2, TRUE);
1828 sprintf(fnt, "[ %s(", name);
1829 AI.text = fnt; assemblez_0(print_zc);
1830 for (i=1; (i<=7)&&(i<=no_locals); i++)
1831 { if (version_number >= 5)
1832 { PV.type = SHORT_CONSTANT_OT;
1833 PV.value = i; PV.marker = 0;
1834 assemblez_1_branch(check_arg_count_zc, PV, ln, FALSE);
1836 sprintf(fnt, "%s%s = ", (i==1)?"":", ", variable_name(i));
1837 AI.text = fnt; assemblez_0(print_zc);
1838 PV.type = VARIABLE_OT; PV.value = i; PV.marker = 0;
1839 assemblez_1(print_num_zc, PV);
1841 assemble_label_no(ln);
1842 sprintf(fnt, ") ]^"); AI.text = fnt;
1843 assemblez_0(print_zc);
1845 assemble_label_no(ln2);
1853 byteout(0xC0, 0); /* Glulx type byte for function */
1855 byteout(0xC1, 0); /* Glulx type byte for function */
1857 /* Now the locals format list. This is simple; we only use
1858 four-byte locals. That's a single pair, unless we have more
1859 than 255 locals, or none at all. */
1869 /* Terminate the list with a (0, 0) pair. */
1874 /* The top stack value is the number of function arguments. Let's
1875 move that into the first local, which is _vararg_count. */
1876 /* @copy sp _vararg_count; */
1877 byteout(0x40, 0); byteout(0x98, 0); byteout(0x00, 0);
1880 next_label = 0; next_sequence_point = 0; last_label = -1;
1883 if ((routine_asterisked) || (define_INFIX_switch)) {
1886 assembly_operand AO, AO2;
1887 if (define_INFIX_switch) {
1888 /* This isn't supported */
1889 if (embedded_flag) {
1892 i = no_named_routines++;
1893 ensure_memory_list_available(&named_routine_symbols_memlist, no_named_routines);
1894 named_routine_symbols[i] = the_symbol;
1897 sprintf(fnt, "[ %s(", name);
1898 AO.marker = STRING_MV;
1899 AO.type = CONSTANT_OT;
1900 AO.value = compile_string(fnt, STRCTX_INFIX);
1901 assembleg_1(streamstr_gc, AO);
1904 for (ix=1; ix<=no_locals; ix++) {
1905 sprintf(fnt, "%s%s = ", (ix==1)?"":", ", variable_name(ix));
1906 AO.marker = STRING_MV;
1907 AO.type = CONSTANT_OT;
1908 AO.value = compile_string(fnt, STRCTX_INFIX);
1909 assembleg_1(streamstr_gc, AO);
1911 AO.type = LOCALVAR_OT;
1913 assembleg_1(streamnum_gc, AO);
1917 int lntop, lnbottom;
1918 sprintf(fnt, "%s = ", variable_name(1));
1919 AO.marker = STRING_MV;
1920 AO.type = CONSTANT_OT;
1921 AO.value = compile_string(fnt, STRCTX_INFIX);
1922 assembleg_1(streamstr_gc, AO);
1924 AO.type = LOCALVAR_OT;
1926 assembleg_1(streamnum_gc, AO);
1927 AO2.type = BYTECONSTANT_OT;
1930 assembleg_1(streamchar_gc, AO2);
1931 AO2.type = BYTECONSTANT_OT;
1934 /* for (temp_var4=0 : temp_var4<_vararg_count : temp_var4++) {
1936 @stkpeek temp_var4 sp;
1940 assembleg_store(temp_var4, zero_operand);
1941 lntop = next_label++;
1942 lnbottom = next_label++;
1943 assemble_label_no(lntop);
1944 assembleg_2_branch(jge_gc, temp_var4, AO, lnbottom); /* AO is _vararg_count */
1945 assembleg_1(streamchar_gc, AO2); /* AO2 is space */
1946 assembleg_2(stkpeek_gc, temp_var4, stack_pointer);
1947 assembleg_1(streamnum_gc, stack_pointer);
1948 assembleg_3(add_gc, temp_var4, one_operand, temp_var4);
1949 assembleg_0_branch(jump_gc, lntop);
1950 assemble_label_no(lnbottom);
1953 AO.marker = STRING_MV;
1954 AO.type = CONSTANT_OT;
1955 AO.value = compile_string(") ]^", STRCTX_INFIX);
1956 assembleg_1(streamstr_gc, AO);
1963 void assemble_routine_end(int embedded_flag, debug_locations locations)
1966 /* No marker is made in the Z-machine's code area to indicate the */
1967 /* end of a routine. Instead, we simply assemble a return opcode if */
1968 /* need be (it won't be if the last instruction was, say, a "quit"). */
1969 /* The return value is true (1) for normal routines, false (0) for */
1970 /* embedded routines (e.g. the library uses this for "before" */
1973 if (!execution_never_reaches_here)
1976 if (embedded_flag) assemblez_0(rfalse_zc);
1977 else assemblez_0(rtrue_zc);
1980 assembly_operand AO;
1985 assembleg_1(return_gc, AO);
1989 /* Dump the contents of the current routine into longer-term Z-code
1993 transfer_routine_z();
1995 transfer_routine_g();
1997 if (track_unused_routines)
1998 df_note_function_end(zmachine_pc);
2000 /* Tell the debugging file about the routine just ended. */
2002 if (debugfile_switch)
2004 char *routine_name = current_routine_name.data;
2005 debug_file_printf("<routine>");
2008 ("<identifier artificial=\"true\">%s</identifier>",
2011 else if (symbols[routine_symbol].flags & REPLACE_SFLAG)
2012 { /* The symbol type will be set to ROUTINE_T once the replaced
2013 version has been given; if it is already set, we must be dealing
2014 with a replacement, and we can use the routine name as-is.
2015 Otherwise we look for a rename. And if that doesn't work, we
2016 fall back to an artificial identifier. */
2017 if (symbols[routine_symbol].type == ROUTINE_T)
2018 { /* Optional because there may be further replacements. */
2019 write_debug_optional_identifier(routine_symbol);
2021 else if (find_symbol_replacement(&routine_symbol))
2023 ("<identifier>%s</identifier>", symbols[routine_symbol].name);
2027 ("<identifier artificial=\"true\">%s (replaced)"
2032 { debug_file_printf("<identifier>%s</identifier>", routine_name);
2034 debug_file_printf("<value>");
2036 { write_debug_code_backpatch(routine_start_pc);
2038 { write_debug_packed_code_backpatch(routine_start_pc);
2040 debug_file_printf("</value>");
2041 debug_file_printf("<address>");
2042 write_debug_code_backpatch(routine_start_pc);
2043 debug_file_printf("</address>");
2045 ("<byte-count>%d</byte-count>", zmachine_pc - routine_start_pc);
2046 write_debug_locations(locations);
2047 for (i = 1; i <= routine_locals; ++i)
2048 { debug_file_printf("<local-variable>");
2049 debug_file_printf("<identifier>%s</identifier>", variable_name(i));
2052 ("<frame-offset>%d</frame-offset>", 4 * (i - 1));
2055 { debug_file_printf("<index>%d</index>", i);
2057 debug_file_printf("</local-variable>");
2059 for (i = 0; i < next_sequence_point; ++i)
2060 { debug_file_printf("<sequence-point>");
2061 debug_file_printf("<address>");
2062 write_debug_code_backpatch
2063 (labels[sequence_points[i].label].offset);
2064 debug_file_printf("</address>");
2065 write_debug_location(sequence_points[i].location);
2066 debug_file_printf("</sequence-point>");
2068 debug_file_printf("</routine>");
2071 /* Issue warnings about any local variables not used in the routine. */
2073 for (i=1; i<=routine_locals; i++)
2074 if (!(variables[i].usage))
2075 dbnu_warning("Local variable", variable_name(i),
2076 routine_starts_line);
2078 for (i=0; i<next_label; i++)
2079 { int j = labels[i].symbol;
2081 { if (symbols[j].flags & CHANGE_SFLAG)
2082 error_named_at("Routine contains no such label as",
2083 symbols[j].name, symbols[j].line);
2085 if ((symbols[j].flags & USED_SFLAG) == 0)
2086 dbnu_warning("Label", symbols[j].name, symbols[j].line);
2087 symbols[j].type = CONSTANT_T;
2088 symbols[j].flags = UNKNOWN_SFLAG;
2091 no_sequence_points += next_sequence_point;
2092 next_label = 0; next_sequence_point = 0;
2094 execution_never_reaches_here = EXECSTATE_REACHABLE;
2097 /* ------------------------------------------------------------------------- */
2098 /* Called when the holding area contains an entire routine of code: */
2099 /* backpatches the labels, issues module markers, then dumps the routine */
2100 /* into longer-term storage. */
2102 /* Note that in the code received, all branches have long form, and their */
2103 /* contents are not an offset but the label numbers they branch to. */
2104 /* Similarly, LABEL operands (those of "jump" instructions) are label */
2105 /* numbers. So this routine must change the label numbers to offsets, */
2106 /* slimming the code down as it does so to take advantage of short-form */
2107 /* branch operands where possible. */
2109 /* zcode_ha_size is the number of bytes added since the last transfer */
2110 /* call. So we transfer starting at (zmachine_pc - zcode_ha_size). But we */
2111 /* might transfer fewer bytes than that. */
2112 /* ------------------------------------------------------------------------- */
2114 static void transfer_routine_z(void)
2115 { int32 i, j, pc, new_pc, label, long_form, offset_of_next, addr,
2116 branch_on_true, rstart_pc;
2119 adjusted_pc = zmachine_pc - zcode_ha_size; rstart_pc = adjusted_pc;
2121 if (asm_trace_level >= 3)
2122 { printf("Backpatching routine at %05lx: initial size %d, %d labels\n",
2123 (long int) adjusted_pc, zcode_ha_size, next_label);
2126 /* (1) Scan through for branches and make short/long decisions in each
2127 case. Mark omitted bytes (2nd bytes in branches converted to
2128 short form) with DELETED_MV.
2129 We also look for jumps that can be entirely eliminated (because
2130 they are jumping to the very next instruction). The opcode and
2131 both label bytes get DELETED_MV. */
2133 for (i=0, pc=adjusted_pc; i<zcode_ha_size; i++, pc++)
2134 { if (zcode_markers[i] == BRANCH_MV)
2135 { if (asm_trace_level >= 4)
2136 printf("Branch detected at offset %04x\n", pc);
2137 j = (256*zcode_holding_area[i] + zcode_holding_area[i+1]) & 0x7fff;
2138 if (asm_trace_level >= 4)
2139 printf("...To label %d, which is %d from here\n",
2140 j, labels[j].offset-pc);
2141 if ((labels[j].offset >= pc+2) && (labels[j].offset < pc+64))
2142 { if (asm_trace_level >= 4) printf("...Using short form\n");
2143 zcode_markers[i+1] = DELETED_MV;
2146 else if (zcode_markers[i] == LABEL_MV)
2148 if (asm_trace_level >= 4)
2149 printf("Jump detected at offset %04x\n", pc);
2150 j = (256*zcode_holding_area[i] + zcode_holding_area[i+1]) & 0x7fff;
2151 if (asm_trace_level >= 4)
2152 printf("...To label %d, which is %d from here\n",
2153 j, labels[j].offset-pc);
2154 if (labels[j].offset-pc == 2 && i >= 1 && zcode_holding_area[i-1] == opcodes_table_z[jump_zc].code+128) {
2155 if (asm_trace_level >= 4) printf("...Deleting jump\n");
2156 zcode_markers[i-1] = DELETED_MV;
2157 zcode_markers[i] = DELETED_MV;
2158 zcode_markers[i+1] = DELETED_MV;
2163 /* (2) Calculate the new positions of the labels. Note that since the
2164 long/short decision was taken on the basis of the old labels,
2165 and since the new labels are slightly closer together because
2166 of branch bytes deleted, there may be a few further branch
2167 optimisations which are possible but which have been missed
2168 (if two labels move inside the "short" range as a result of
2169 a previous optimisation). However, this is acceptably uncommon. */
2172 { if (asm_trace_level >= 4)
2173 { printf("Opening label: %d\n", first_label);
2174 for (i=0;i<next_label;i++)
2175 printf("Label %d offset %04x next -> %d previous -> %d\n",
2176 i, labels[i].offset, labels[i].next, labels[i].prev);
2179 /* label will advance through the linked list as pc increases. */
2180 for (i=0, pc=adjusted_pc, new_pc=adjusted_pc, label = first_label;
2181 i<zcode_ha_size; i++, pc++)
2182 { while ((label != -1) && (labels[label].offset == pc))
2183 { if (asm_trace_level >= 4)
2184 printf("Position of L%d corrected from %04x to %04x\n",
2185 label, labels[label].offset, new_pc);
2186 labels[label].offset = new_pc;
2187 label = labels[label].next;
2189 if (zcode_markers[i] != DELETED_MV) new_pc++;
2193 /* (3) As we are transferring, replace the label numbers in branch
2194 operands with offsets to those labels. Also issue markers, now
2195 that we know where they occur in the final Z-code area. */
2197 ensure_memory_list_available(&zcode_area_memlist, adjusted_pc+zcode_ha_size);
2199 for (i=0, new_pc=adjusted_pc; i<zcode_ha_size; i++)
2200 { switch(zcode_markers[i])
2202 long_form = 1; if (zcode_markers[i+1] == DELETED_MV) long_form = 0;
2204 j = (256*zcode_holding_area[i] + zcode_holding_area[i+1]) & 0x7fff;
2205 branch_on_true = ((zcode_holding_area[i]) & 0x80);
2206 offset_of_next = new_pc + long_form + 1;
2208 if (labels[j].offset < 0) {
2209 char *lname = "(anon)";
2210 if (labels[j].symbol >= 0 && labels[j].symbol < no_symbols)
2211 lname = symbols[labels[j].symbol].name;
2212 error_named("Attempt to jump to an unreachable label", lname);
2216 addr = labels[j].offset - offset_of_next + 2;
2218 if (addr<-0x2000 || addr>0x1fff)
2219 fatalerror("Branch out of range: divide the routine up?");
2220 if (addr<0) addr+=(int32) 0x10000L;
2224 { zcode_holding_area[i] = branch_on_true + addr/256;
2225 zcode_holding_area[i+1] = addr%256;
2229 { compiler_error("Label out of range for branch");
2230 printf("Addr is %04x\n", addr);
2232 zcode_holding_area[i] = branch_on_true + 0x40 + (addr&0x3f);
2234 zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++;
2238 j = 256*zcode_holding_area[i] + zcode_holding_area[i+1];
2239 if (labels[j].offset < 0) {
2240 char *lname = "(anon)";
2241 if (labels[j].symbol >= 0 && labels[j].symbol < no_symbols)
2242 lname = symbols[labels[j].symbol].name;
2243 error_named("Attempt to jump to an unreachable label", lname);
2247 addr = labels[j].offset - new_pc;
2249 if (addr<-0x8000 || addr>0x7fff)
2250 fatalerror("Jump out of range: divide the routine up?");
2251 if (addr<0) addr += (int32) 0x10000L;
2252 zcode_holding_area[i] = addr/256;
2253 zcode_holding_area[i+1] = addr%256;
2254 zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++;
2261 switch(zcode_markers[i] & 0x7f)
2262 { case NULL_MV: break;
2269 if ((zcode_markers[i] & 0x7f) > LARGEST_BPATCH_MV)
2270 { compiler_error("Illegal code backpatch value");
2271 printf("Illegal value of %02x at PC = %04x\n",
2272 zcode_markers[i] & 0x7f, new_pc);
2276 if (bpatch_trace_setting >= 2)
2277 printf("BP added: MV %d PC %04x\n", zcode_markers[i], new_pc);
2279 ensure_memory_list_available(&zcode_backpatch_table_memlist, zcode_backpatch_size+3);
2280 zcode_backpatch_table[zcode_backpatch_size++] = zcode_markers[i] + 32*(new_pc/65536);
2281 zcode_backpatch_table[zcode_backpatch_size++] = (new_pc/256)%256;
2282 zcode_backpatch_table[zcode_backpatch_size++] = new_pc%256;
2285 zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++;
2290 /* Consistency check */
2291 if (new_pc - rstart_pc > zcode_ha_size || adjusted_pc != new_pc)
2293 fatalerror("Optimisation increased routine length or failed to match; should not happen");
2296 if (asm_trace_level >= 3)
2297 { printf("After branch optimisation, routine length is %d bytes\n",
2298 new_pc - rstart_pc);
2301 /* Insert null bytes if necessary to ensure the next routine address is */
2302 /* expressible as a packed address */
2304 ensure_memory_list_available(&zcode_area_memlist, adjusted_pc+2*scale_factor);
2306 if (oddeven_packing_switch)
2307 while ((adjusted_pc%(scale_factor*2))!=0) zcode_area[adjusted_pc++] = 0;
2309 while ((adjusted_pc%scale_factor)!=0) zcode_area[adjusted_pc++] = 0;
2311 zmachine_pc = adjusted_pc;
2315 static void transfer_routine_g(void)
2316 { int32 i, j, pc, new_pc, label, form_len, offset_of_next, addr,
2320 adjusted_pc = zmachine_pc - zcode_ha_size; rstart_pc = adjusted_pc;
2322 if (asm_trace_level >= 3)
2323 { printf("Backpatching routine at %05lx: initial size %d, %d labels\n",
2324 (long int) adjusted_pc, zcode_ha_size, next_label);
2327 /* (1) Scan through for branches and make short/long decisions in each
2328 case. Mark omitted bytes (bytes 2-4 in branches converted to
2329 short form) with DELETED_MV.
2330 We also look for branches that can be entirely eliminated (because
2331 they are jumping to the very next instruction). The opcode and
2332 all label bytes get DELETED_MV. */
2334 for (i=0, pc=adjusted_pc; i<zcode_ha_size; i++, pc++) {
2335 if (zcode_markers[i] >= BRANCH_MV && zcode_markers[i] < BRANCHMAX_MV) {
2336 int opmodeoffset = (zcode_markers[i] - BRANCH_MV);
2338 if (asm_trace_level >= 4)
2339 printf("Branch detected at offset %04x\n", pc);
2340 j = ((zcode_holding_area[i] << 24)
2341 | (zcode_holding_area[i+1] << 16)
2342 | (zcode_holding_area[i+2] << 8)
2343 | (zcode_holding_area[i+3]));
2344 offset_of_next = pc + 4;
2345 addr = (labels[j].offset - offset_of_next) + 2;
2346 opmodebyte = i - ((opmodeoffset+1)/2);
2347 if (asm_trace_level >= 4)
2348 printf("...To label %d, which is (%d-2) = %d from here\n",
2349 j, addr, labels[j].offset - offset_of_next);
2350 if (addr == 2 && i >= 2 && opmodeoffset == 2 && zcode_holding_area[opmodebyte-1] == opcodes_table_g[jump_gc].code) {
2351 if (asm_trace_level >= 4) printf("...Deleting branch\n");
2352 zcode_markers[i-2] = DELETED_MV;
2353 zcode_markers[i-1] = DELETED_MV;
2354 zcode_markers[i] = DELETED_MV;
2355 zcode_markers[i+1] = DELETED_MV;
2356 zcode_markers[i+2] = DELETED_MV;
2357 zcode_markers[i+3] = DELETED_MV;
2359 else if (addr >= -0x80 && addr < 0x80) {
2360 if (asm_trace_level >= 4) printf("...Byte form\n");
2361 zcode_markers[i+1] = DELETED_MV;
2362 zcode_markers[i+2] = DELETED_MV;
2363 zcode_markers[i+3] = DELETED_MV;
2364 if ((opmodeoffset & 1) == 0)
2365 zcode_holding_area[opmodebyte] =
2366 (zcode_holding_area[opmodebyte] & 0xF0) | 0x01;
2368 zcode_holding_area[opmodebyte] =
2369 (zcode_holding_area[opmodebyte] & 0x0F) | 0x10;
2371 else if (addr >= -0x8000 && addr < 0x8000) {
2372 if (asm_trace_level >= 4) printf("...Short form\n");
2373 zcode_markers[i+2] = DELETED_MV;
2374 zcode_markers[i+3] = DELETED_MV;
2375 if ((opmodeoffset & 1) == 0)
2376 zcode_holding_area[opmodebyte] =
2377 (zcode_holding_area[opmodebyte] & 0xF0) | 0x02;
2379 zcode_holding_area[opmodebyte] =
2380 (zcode_holding_area[opmodebyte] & 0x0F) | 0x20;
2385 /* (2) Calculate the new positions of the labels. Note that since the
2386 long/short decision was taken on the basis of the old labels,
2387 and since the new labels are slightly closer together because
2388 of branch bytes deleted, there may be a few further branch
2389 optimisations which are possible but which have been missed
2390 (if two labels move inside the "short" range as a result of
2391 a previous optimisation). However, this is acceptably uncommon. */
2392 if (next_label > 0) {
2393 if (asm_trace_level >= 4) {
2394 printf("Opening label: %d\n", first_label);
2395 for (i=0;i<next_label;i++)
2396 printf("Label %d offset %04x next -> %d previous -> %d\n",
2397 i, labels[i].offset, labels[i].next, labels[i].prev);
2400 /* label will advance through the linked list as pc increases. */
2401 for (i=0, pc=adjusted_pc, new_pc=adjusted_pc, label = first_label;
2404 while ((label != -1) && (labels[label].offset == pc)) {
2405 if (asm_trace_level >= 4)
2406 printf("Position of L%d corrected from %04x to %04x\n",
2407 label, labels[label].offset, new_pc);
2408 labels[label].offset = new_pc;
2409 label = labels[label].next;
2411 if (zcode_markers[i] != DELETED_MV) new_pc++;
2415 /* (3) As we are transferring, replace the label numbers in branch
2416 operands with offsets to those labels. Also issue markers, now
2417 that we know where they occur in the final Z-code area. */
2419 ensure_memory_list_available(&zcode_area_memlist, adjusted_pc+zcode_ha_size);
2421 for (i=0, new_pc=adjusted_pc; i<zcode_ha_size; i++) {
2423 if (zcode_markers[i] >= BRANCH_MV && zcode_markers[i] < BRANCHMAX_MV) {
2425 if (zcode_markers[i+1] == DELETED_MV) {
2429 if (zcode_markers[i+2] == DELETED_MV)
2432 j = ((zcode_holding_area[i] << 24)
2433 | (zcode_holding_area[i+1] << 16)
2434 | (zcode_holding_area[i+2] << 8)
2435 | (zcode_holding_area[i+3]));
2437 /* At the moment, we can safely assume that the branch operand
2438 is the end of the opcode, so the next opcode starts right
2440 offset_of_next = new_pc + form_len;
2442 if (labels[j].offset < 0) {
2443 char *lname = "(anon)";
2444 if (labels[j].symbol >= 0 && labels[j].symbol < no_symbols)
2445 lname = symbols[labels[j].symbol].name;
2446 error_named("Attempt to jump to an unreachable label", lname);
2450 addr = (labels[j].offset - offset_of_next) + 2;
2452 if (asm_trace_level >= 4) {
2453 printf("Branch at offset %04x: %04x (%s)\n",
2454 new_pc, addr, ((form_len == 1) ? "byte" :
2455 ((form_len == 2) ? "short" : "long")));
2457 if (form_len == 1) {
2458 if (addr < -0x80 || addr >= 0x80) {
2459 error("*** Label out of range for byte branch ***");
2461 zcode_holding_area[i] = (addr) & 0xFF;
2463 else if (form_len == 2) {
2464 if (addr < -0x8000 || addr >= 0x8000) {
2465 error("*** Label out of range for short branch ***");
2467 zcode_holding_area[i] = (addr >> 8) & 0xFF;
2468 zcode_holding_area[i+1] = (addr) & 0xFF;
2471 zcode_holding_area[i] = (addr >> 24) & 0xFF;
2472 zcode_holding_area[i+1] = (addr >> 16) & 0xFF;
2473 zcode_holding_area[i+2] = (addr >> 8) & 0xFF;
2474 zcode_holding_area[i+3] = (addr) & 0xFF;
2476 zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++;
2478 else if (zcode_markers[i] == LABEL_MV) {
2479 error("*** No LABEL opcodes in Glulx ***");
2481 else if (zcode_markers[i] == DELETED_MV) {
2485 switch(zcode_markers[i] & 0x7f) {
2494 if ((zcode_markers[i] & 0x7f) > LARGEST_BPATCH_MV) {
2495 error("*** Illegal code backpatch value ***");
2496 printf("Illegal value of %02x at PC = %04x\n",
2497 zcode_markers[i] & 0x7f, new_pc);
2500 /* The backpatch table format for Glulx:
2501 First, the marker byte (0..LARGEST_BPATCH_MV).
2502 Then a byte indicating the data size to be patched (1, 2, 4).
2503 Then the four-byte address (new_pc).
2505 if (bpatch_trace_setting >= 2)
2506 printf("BP added: MV %d size %d PC %04x\n", zcode_markers[i], 4, new_pc);
2507 ensure_memory_list_available(&zcode_backpatch_table_memlist, zcode_backpatch_size+6);
2508 zcode_backpatch_table[zcode_backpatch_size++] = zcode_markers[i];
2509 zcode_backpatch_table[zcode_backpatch_size++] = 4;
2510 zcode_backpatch_table[zcode_backpatch_size++] = ((new_pc >> 24) & 0xFF);
2511 zcode_backpatch_table[zcode_backpatch_size++] = ((new_pc >> 16) & 0xFF);
2512 zcode_backpatch_table[zcode_backpatch_size++] = ((new_pc >> 8) & 0xFF);
2513 zcode_backpatch_table[zcode_backpatch_size++] = (new_pc & 0xFF);
2516 zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++;
2520 /* Consistency check */
2521 if (new_pc - rstart_pc > zcode_ha_size || adjusted_pc != new_pc)
2523 fatalerror("Optimisation increased routine length or failed to match; should not happen");
2526 if (asm_trace_level >= 3)
2527 { printf("After branch optimisation, routine length is %d bytes\n",
2528 new_pc - rstart_pc);
2531 zmachine_pc = adjusted_pc;
2536 /* ========================================================================= */
2537 /* Front ends for the instruction assembler: convenient shorthand forms */
2538 /* used in various code generation routines all over Inform. */
2539 /* ------------------------------------------------------------------------- */
2541 void assemble_jump(int n)
2549 void assemblez_0(int internal_number)
2550 { AI.internal_number = internal_number;
2551 AI.operand_count = 0;
2552 AI.store_variable_number = -1;
2553 AI.branch_label_number = -1;
2554 assemblez_instruction(&AI);
2557 void assemblez_0_to(int internal_number, assembly_operand o)
2558 { AI.internal_number = internal_number;
2559 AI.operand_count = 0;
2560 AI.store_variable_number = o.value;
2561 AI.branch_label_number = -1;
2562 assemblez_instruction(&AI);
2565 void assemblez_0_branch(int internal_number, int label, int flag)
2566 { AI.internal_number = internal_number;
2567 AI.operand_count = 0;
2568 AI.store_variable_number = -1;
2569 AI.branch_label_number = label;
2570 AI.branch_flag = flag;
2571 assemblez_instruction(&AI);
2574 void assemblez_1(int internal_number, assembly_operand o1)
2575 { AI.internal_number = internal_number;
2576 AI.operand_count = 1;
2578 AI.store_variable_number = -1;
2579 AI.branch_label_number = -1;
2580 assemblez_instruction(&AI);
2583 void assemblez_1_to(int internal_number,
2584 assembly_operand o1, assembly_operand st)
2585 { AI.internal_number = internal_number;
2586 AI.operand_count = 1;
2588 AI.store_variable_number = st.value;
2589 AI.branch_label_number = -1;
2590 assemblez_instruction(&AI);
2593 void assemblez_1_branch(int internal_number,
2594 assembly_operand o1, int label, int flag)
2596 /* Some clever optimizations first. A constant is always or never equal
2598 if (o1.marker == 0 && is_constant_ot(o1.type)) {
2599 if (internal_number == jz_zc) {
2600 if ((flag && o1.value == 0) || (!flag && o1.value != 0)) {
2601 assemblez_jump(label);
2605 /* assemble nothing at all! */
2610 AI.internal_number = internal_number;
2611 AI.operand_count = 1;
2613 AI.branch_label_number = label;
2614 AI.store_variable_number = -1;
2615 AI.branch_flag = flag;
2616 assemblez_instruction(&AI);
2619 void assemblez_2(int internal_number,
2620 assembly_operand o1, assembly_operand o2)
2621 { AI.internal_number = internal_number;
2622 AI.operand_count = 2;
2625 AI.store_variable_number = -1;
2626 AI.branch_label_number = -1;
2627 assemblez_instruction(&AI);
2630 void assemblez_3(int internal_number,
2631 assembly_operand o1, assembly_operand o2, assembly_operand o3)
2632 { AI.internal_number = internal_number;
2633 AI.operand_count = 3;
2637 AI.store_variable_number = -1;
2638 AI.branch_label_number = -1;
2639 assemblez_instruction(&AI);
2642 void assemblez_3_to(int internal_number,
2643 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2644 assembly_operand st)
2645 { AI.internal_number = internal_number;
2646 AI.operand_count = 3;
2650 AI.store_variable_number = st.value;
2651 AI.branch_label_number = -1;
2652 assemblez_instruction(&AI);
2655 void assemblez_3_branch(int internal_number,
2656 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2657 int label, int flag)
2658 { AI.internal_number = internal_number;
2659 AI.operand_count = 3;
2663 AI.store_variable_number = -1;
2664 AI.branch_label_number = label;
2665 AI.branch_flag = flag;
2666 assemblez_instruction(&AI);
2669 void assemblez_4(int internal_number,
2670 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2671 assembly_operand o4)
2672 { AI.internal_number = internal_number;
2673 AI.operand_count = 4;
2678 AI.store_variable_number = -1;
2679 AI.branch_label_number = -1;
2680 assemblez_instruction(&AI);
2683 void assemblez_5(int internal_number,
2684 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2685 assembly_operand o4, assembly_operand o5)
2686 { AI.internal_number = internal_number;
2687 AI.operand_count = 5;
2693 AI.store_variable_number = -1;
2694 AI.branch_label_number = -1;
2695 assemblez_instruction(&AI);
2698 void assemblez_6(int internal_number,
2699 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2700 assembly_operand o4, assembly_operand o5, assembly_operand o6)
2701 { AI.internal_number = internal_number;
2702 AI.operand_count = 6;
2709 AI.store_variable_number = -1;
2710 AI.branch_label_number = -1;
2711 assemblez_instruction(&AI);
2714 void assemblez_4_branch(int internal_number,
2715 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2716 assembly_operand o4, int label, int flag)
2717 { AI.internal_number = internal_number;
2718 AI.operand_count = 4;
2723 AI.store_variable_number = -1;
2724 AI.branch_label_number = label;
2725 AI.branch_flag = flag;
2726 assemblez_instruction(&AI);
2729 void assemblez_4_to(int internal_number,
2730 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2731 assembly_operand o4, assembly_operand st)
2732 { AI.internal_number = internal_number;
2733 AI.operand_count = 4;
2738 AI.store_variable_number = st.value;
2739 AI.branch_label_number = -1;
2740 assemblez_instruction(&AI);
2743 void assemblez_5_to(int internal_number,
2744 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2745 assembly_operand o4, assembly_operand o5, assembly_operand st)
2746 { AI.internal_number = internal_number;
2747 AI.operand_count = 5;
2753 AI.store_variable_number = st.value;
2754 AI.branch_label_number = -1;
2755 assemblez_instruction(&AI);
2758 void assemblez_2_to(int internal_number,
2759 assembly_operand o1, assembly_operand o2, assembly_operand st)
2760 { AI.internal_number = internal_number;
2761 AI.operand_count = 2;
2764 AI.store_variable_number = st.value;
2765 AI.branch_label_number = -1;
2766 assemblez_instruction(&AI);
2769 void assemblez_2_branch(int internal_number,
2770 assembly_operand o1, assembly_operand o2, int label, int flag)
2771 { AI.internal_number = internal_number;
2772 AI.operand_count = 2;
2775 AI.branch_label_number = label;
2776 AI.store_variable_number = -1;
2777 AI.branch_flag = flag;
2778 assemblez_instruction(&AI);
2781 void assemblez_objcode(int internal_number,
2782 assembly_operand o1, assembly_operand st, int label, int flag)
2783 { AI.internal_number = internal_number;
2784 AI.operand_count = 1;
2786 AI.branch_label_number = label;
2787 AI.store_variable_number = st.value;
2788 AI.branch_flag = flag;
2789 assemblez_instruction(&AI);
2792 extern void assemblez_inc(assembly_operand o1)
2794 if ((o1.value >= MAX_LOCAL_VARIABLES)
2795 && (o1.value<LOWEST_SYSTEM_VAR_NUMBER))
2797 AI.internal_number = inc_zc;
2798 AI.operand_count = 1;
2799 AI.operand[0].value = o1.value;
2800 AI.operand[0].type = SHORT_CONSTANT_OT;
2801 AI.operand[0].marker = m;
2802 AI.store_variable_number = -1;
2803 AI.branch_label_number = -1;
2804 assemblez_instruction(&AI);
2807 extern void assemblez_dec(assembly_operand o1)
2809 if ((o1.value >= MAX_LOCAL_VARIABLES)
2810 && (o1.value<LOWEST_SYSTEM_VAR_NUMBER))
2812 AI.internal_number = dec_zc;
2813 AI.operand_count = 1;
2814 AI.operand[0].value = o1.value;
2815 AI.operand[0].type = SHORT_CONSTANT_OT;
2816 AI.operand[0].marker = m;
2817 AI.store_variable_number = -1;
2818 AI.branch_label_number = -1;
2819 assemblez_instruction(&AI);
2822 extern void assemblez_store(assembly_operand o1, assembly_operand o2)
2824 if ((o1.value >= MAX_LOCAL_VARIABLES)
2825 && (o1.value<LOWEST_SYSTEM_VAR_NUMBER))
2828 if ((o2.type == VARIABLE_OT) && (o2.value == 0))
2830 /* Assemble "pull VAR" rather than "store VAR sp",
2833 AI.internal_number = pull_zc;
2834 if (instruction_set_number == 6)
2835 { AI.operand_count = 0;
2836 AI.store_variable_number = o1.value;
2839 { AI.operand_count = 1;
2840 AI.operand[0].value = o1.value;
2841 AI.operand[0].type = SHORT_CONSTANT_OT;
2842 AI.operand[0].marker = m;
2843 AI.store_variable_number = -1;
2845 AI.branch_label_number = -1;
2846 assemblez_instruction(&AI);
2850 if ((o1.type == VARIABLE_OT) && (o1.value == 0))
2851 { /* Assemble "push VAR" rather than "store sp VAR",
2854 AI.internal_number = push_zc;
2855 AI.operand_count = 1;
2857 AI.store_variable_number = -1;
2858 AI.branch_label_number = -1;
2859 assemblez_instruction(&AI);
2862 AI.internal_number = store_zc;
2863 AI.operand_count = 2;
2864 AI.operand[0].value = o1.value;
2865 AI.operand[0].type = SHORT_CONSTANT_OT;
2866 AI.operand[0].marker = m;
2868 AI.store_variable_number = -1;
2869 AI.branch_label_number = -1;
2870 assemblez_instruction(&AI);
2873 void assemblez_jump(int n)
2874 { assembly_operand AO;
2875 if (n==-4) assemblez_0(rtrue_zc);
2876 else if (n==-3) assemblez_0(rfalse_zc);
2878 { AO.type = LONG_CONSTANT_OT; AO.value = n; AO.marker = 0;
2879 assemblez_1(jump_zc, AO);
2883 void assembleg_0(int internal_number)
2884 { AI.internal_number = internal_number;
2885 AI.operand_count = 0;
2886 assembleg_instruction(&AI);
2889 void assembleg_1(int internal_number, assembly_operand o1)
2890 { AI.internal_number = internal_number;
2891 AI.operand_count = 1;
2893 assembleg_instruction(&AI);
2896 void assembleg_2(int internal_number, assembly_operand o1,
2897 assembly_operand o2)
2898 { AI.internal_number = internal_number;
2899 AI.operand_count = 2;
2902 assembleg_instruction(&AI);
2905 void assembleg_3(int internal_number, assembly_operand o1,
2906 assembly_operand o2, assembly_operand o3)
2907 { AI.internal_number = internal_number;
2908 AI.operand_count = 3;
2912 assembleg_instruction(&AI);
2915 void assembleg_4(int internal_number, assembly_operand o1,
2916 assembly_operand o2, assembly_operand o3,
2917 assembly_operand o4)
2918 { AI.internal_number = internal_number;
2919 AI.operand_count = 4;
2924 assembleg_instruction(&AI);
2927 void assembleg_5(int internal_number, assembly_operand o1,
2928 assembly_operand o2, assembly_operand o3,
2929 assembly_operand o4, assembly_operand o5)
2930 { AI.internal_number = internal_number;
2931 AI.operand_count = 5;
2937 assembleg_instruction(&AI);
2940 void assembleg_0_branch(int internal_number,
2943 AI.internal_number = internal_number;
2944 AI.operand_count = 1;
2945 AI.operand[0].type = CONSTANT_OT;
2946 AI.operand[0].value = label;
2947 AI.operand[0].marker = BRANCH_MV;
2948 assembleg_instruction(&AI);
2951 void assembleg_1_branch(int internal_number,
2952 assembly_operand o1, int label)
2954 /* Some clever optimizations first. A constant is always or never equal
2956 if (o1.marker == 0 && is_constant_ot(o1.type)) {
2957 if ((internal_number == jz_gc && o1.value == 0)
2958 || (internal_number == jnz_gc && o1.value != 0)) {
2959 assembleg_0_branch(jump_gc, label);
2962 if ((internal_number == jz_gc && o1.value != 0)
2963 || (internal_number == jnz_gc && o1.value == 0)) {
2964 /* assemble nothing at all! */
2968 AI.internal_number = internal_number;
2969 AI.operand_count = 2;
2971 AI.operand[1].type = CONSTANT_OT;
2972 AI.operand[1].value = label;
2973 AI.operand[1].marker = BRANCH_MV;
2974 assembleg_instruction(&AI);
2977 void assembleg_2_branch(int internal_number,
2978 assembly_operand o1, assembly_operand o2, int label)
2980 AI.internal_number = internal_number;
2981 AI.operand_count = 3;
2984 AI.operand[2].type = CONSTANT_OT;
2985 AI.operand[2].value = label;
2986 AI.operand[2].marker = BRANCH_MV;
2987 assembleg_instruction(&AI);
2990 void assembleg_call_1(assembly_operand oaddr, assembly_operand o1,
2991 assembly_operand odest)
2993 assembleg_3(callfi_gc, oaddr, o1, odest);
2996 void assembleg_call_2(assembly_operand oaddr, assembly_operand o1,
2997 assembly_operand o2, assembly_operand odest)
2999 assembleg_4(callfii_gc, oaddr, o1, o2, odest);
3002 void assembleg_call_3(assembly_operand oaddr, assembly_operand o1,
3003 assembly_operand o2, assembly_operand o3, assembly_operand odest)
3005 assembleg_5(callfiii_gc, oaddr, o1, o2, o3, odest);
3008 void assembleg_inc(assembly_operand o1)
3010 AI.internal_number = add_gc;
3011 AI.operand_count = 3;
3013 AI.operand[1] = one_operand;
3015 assembleg_instruction(&AI);
3018 void assembleg_dec(assembly_operand o1)
3020 AI.internal_number = sub_gc;
3021 AI.operand_count = 3;
3023 AI.operand[1] = one_operand;
3025 assembleg_instruction(&AI);
3028 void assembleg_store(assembly_operand o1, assembly_operand o2)
3030 /* Note the order is reversed: "o1 = o2;" */
3031 assembleg_2(copy_gc, o2, o1);
3034 void assembleg_jump(int n)
3037 assembleg_1(return_gc, one_operand);
3040 assembleg_1(return_gc, zero_operand);
3043 assembleg_0_branch(jump_gc, n);
3047 /* ========================================================================= */
3048 /* Parsing and then calling the assembler for @ (assembly language) */
3050 /* ------------------------------------------------------------------------- */
3052 static assembly_operand parse_operand_z(void)
3053 { assembly_operand AO;
3055 AO = parse_expression(ASSEMBLY_CONTEXT);
3056 if (AO.type == EXPRESSION_OT)
3057 { ebf_error("variable or constant", "expression");
3058 AO.type = SHORT_CONSTANT_OT;
3063 static void parse_assembly_z(void)
3064 { int n, min, max, indirect_addressed, error_flag = FALSE;
3067 AI.operand_count = 0;
3068 AI.store_variable_number = -1;
3069 AI.branch_label_number = -1;
3072 opcode_names.enabled = TRUE;
3074 opcode_names.enabled = FALSE;
3076 if (token_type == DQ_TT)
3078 AI.internal_number = -1;
3080 custom_opcode_z.name = (uchar *) token_text;
3081 custom_opcode_z.version1 = instruction_set_number;
3082 custom_opcode_z.version2 = instruction_set_number;
3083 custom_opcode_z.extension = -1;
3084 custom_opcode_z.flags = 0;
3085 custom_opcode_z.op_rules = 0;
3086 custom_opcode_z.flags2_set = 0;
3087 custom_opcode_z.no = ZERO;
3089 for (i=0; token_text[i]!=0; i++)
3090 { if (token_text[i] == ':')
3091 { token_text[i++] = 0;
3095 if (token_text[i] == 0)
3096 error("Opcode specification should have form \"VAR:102\"");
3099 if (strcmp(token_text, "0OP")==0) n=ZERO;
3100 if (strcmp(token_text, "1OP")==0) n=ONE;
3101 if (strcmp(token_text, "2OP")==0) n=TWO;
3102 if (strcmp(token_text, "VAR")==0) n=VAR;
3103 if (strcmp(token_text, "EXT")==0) n=EXT;
3104 if (strcmp(token_text, "VAR_LONG")==0) n=VAR_LONG;
3105 if (strcmp(token_text, "EXT_LONG")==0) n=EXT_LONG;
3107 if (i>0) token_text[i-1] = ':';
3110 { ebf_error("Expected 0OP, 1OP, 2OP, VAR, EXT, VAR_LONG or EXT_LONG",
3114 custom_opcode_z.no = n;
3116 custom_opcode_z.code = atoi(token_text+i);
3117 while (isdigit(token_text[i])) i++;
3121 { case ZERO: case ONE: max = 16; break;
3122 case VAR: case VAR_LONG: min = 32; max = 64; break;
3123 case EXT: case EXT_LONG: max = 256; break;
3124 case TWO: max = 32; break;
3126 if ((custom_opcode_z.code < min) || (custom_opcode_z.code >= max))
3128 sprintf(range, "%d to %d", min, max-1);
3129 error_named("For this operand type, opcode number must be in range",
3131 custom_opcode_z.code = min;
3135 while (token_text[i++] != 0)
3136 { switch(token_text[i-1])
3137 { case 'B': custom_opcode_z.flags |= Br; break;
3138 case 'S': custom_opcode_z.flags |= St; break;
3139 case 'T': custom_opcode_z.op_rules = TEXT; break;
3140 case 'I': custom_opcode_z.op_rules = VARIAB; break;
3141 case 'F': custom_opcode_z.flags2_set = atoi(token_text+i);
3142 while (isdigit(token_text[i])) i++; break;
3144 error("Unknown flag: options are B (branch), S (store), \
3145 T (text), I (indirect addressing), F** (set this Flags 2 bit)");
3149 O = custom_opcode_z;
3152 { if (token_type != OPCODE_NAME_TT)
3153 { ebf_error("an opcode name", token_text);
3154 panic_mode_error_recovery();
3157 AI.internal_number = token_value;
3158 O = internal_number_to_opcode_z(AI.internal_number);
3161 indirect_addressed = (O.op_rules == VARIAB);
3163 if (O.op_rules == TEXT)
3165 if (token_type != DQ_TT)
3166 ebf_error("literal text in double-quotes", token_text);
3167 AI.text = token_text;
3168 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) return;
3170 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
3171 { assemblez_instruction(&AI);
3175 ebf_error("semicolon ';' after print string", token_text);
3181 return_sp_as_variable = TRUE;
3185 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
3187 if ((token_type == SEP_TT) && (token_value == ARROW_SEP))
3188 { if (AI.store_variable_number != -1)
3189 error("Only one '->' store destination can be given");
3191 if ((token_type != SYMBOL_TT)
3192 && (token_type != LOCAL_VARIABLE_TT))
3193 ebf_error("variable name or 'sp'", token_text);
3195 if (token_type == LOCAL_VARIABLE_TT) n = token_value;
3197 { if (strcmp(token_text, "sp") == 0) n = 0;
3199 { if (symbols[token_value].type != GLOBAL_VARIABLE_T)
3201 "Store '->' destination not 'sp' or a variable:",
3203 else n = symbols[token_value].value;
3206 AI.store_variable_number = n;
3210 if ((token_type == SEP_TT) &&
3211 ((token_value == BRANCH_SEP) || (token_value == NBRANCH_SEP)))
3212 { if (AI.branch_label_number != -1)
3213 error("Only one '?' branch destination can be given");
3215 AI.branch_flag = (token_value == BRANCH_SEP);
3217 opcode_names.enabled = TRUE;
3219 opcode_names.enabled = FALSE;
3222 if ((token_type == OPCODE_NAME_TT)
3223 && (token_value == rfalse_zc)) n = -3;
3225 if ((token_type == OPCODE_NAME_TT)
3226 && (token_value == rtrue_zc)) n = -4;
3228 { if (token_type == SYMBOL_TT)
3233 ebf_error("label name after '?' or '?~'", token_text);
3235 AI.branch_label_number = n;
3239 if (AI.operand_count == 8)
3240 { error("No assembly instruction may have more than 8 operands");
3241 panic_mode_error_recovery(); break;
3244 if ((token_type == SEP_TT) && (token_value == OPEN_SQUARE_SEP))
3245 { if (!indirect_addressed)
3246 error("This opcode does not use indirect addressing");
3247 if (AI.operand_count > 0)
3248 error("Indirect addressing can only be used on the first operand");
3249 AI.operand[AI.operand_count++] = parse_operand_z();
3251 if (!((token_type == SEP_TT) && (token_value == CLOSE_SQUARE_SEP)))
3252 { ebf_error("']'", token_text);
3258 AI.operand[AI.operand_count++] = parse_operand_z();
3259 if ((indirect_addressed) && (AI.operand_count == 1)
3260 && (AI.operand[AI.operand_count-1].type == VARIABLE_OT))
3261 { AI.operand[AI.operand_count-1].type = SHORT_CONSTANT_OT;
3262 AI.operand[AI.operand_count-1].marker = VARIABLE_MV;
3268 return_sp_as_variable = FALSE;
3271 if (O.version1 == 0)
3272 { error_named("Opcode unavailable in this Z-machine version:",
3273 opcode_names.keywords[AI.internal_number]);
3277 if (((O.flags) & Br) != 0)
3278 { if (AI.branch_label_number == -1)
3279 { error_flag = TRUE;
3280 AI.branch_label_number = -2;
3284 { if (AI.branch_label_number != -1)
3285 { error_flag = TRUE;
3286 AI.branch_label_number = -1;
3289 if (((O.flags) & St) != 0)
3290 { if (AI.store_variable_number == -1)
3291 { if (AI.operand_count == 0)
3292 { error_flag = TRUE;
3293 AI.store_variable_number = 255;
3296 { AI.store_variable_number
3297 = AI.operand[--AI.operand_count].value;
3298 if (AI.operand[AI.operand_count].type != VARIABLE_OT)
3299 error("Store destination (the last operand) is not a variable");
3304 { if (AI.store_variable_number != -1)
3305 { error_flag = TRUE;
3306 AI.store_variable_number = -1;
3312 { case TWO: min = 2; max = 2;
3313 /* Exception for the V6 set_colour, which can take
3314 a third argument, thus forcing it into VAR form: */
3315 if ((version_number == 6) && (O.code == 0x1b)) max = 3;
3316 /* Also an exception for je, which can take from 1
3317 argument (useless) to 4 arguments */
3318 if (O.code == 0x01) { min = 1; max = 4; }
3320 case VAR: min = 0; max = 4; break;
3321 case VAR_LONG: min = 0; max = 8; break;
3322 case ONE: min = 1; max = 1; break;
3323 case ZERO: min = 0; max = 0; break;
3324 case EXT: min = 0; max = 4; break;
3325 case EXT_LONG: min = 0; max = 8; break;
3328 if ((AI.operand_count >= min) && (AI.operand_count <= max))
3329 assemblez_instruction(&AI);
3330 else error_flag = TRUE;
3333 { make_opcode_syntax_z(O);
3334 error_named("Assembly mistake: syntax is",
3335 opcode_syntax_string);
3339 static assembly_operand parse_operand_g(void)
3340 { assembly_operand AO;
3342 AO = parse_expression(ASSEMBLY_CONTEXT);
3343 if (AO.type == EXPRESSION_OT)
3344 { ebf_error("variable or constant", "expression");
3345 AO.type = CONSTANT_OT;
3350 static void parse_assembly_g(void)
3353 assembly_operand AO;
3354 int error_flag = FALSE, is_macro = FALSE;
3356 AI.operand_count = 0;
3359 opcode_names.enabled = TRUE;
3360 opcode_macros.enabled = TRUE;
3362 opcode_names.enabled = FALSE;
3363 opcode_macros.enabled = FALSE;
3365 if (token_type == DQ_TT) {
3369 AI.internal_number = -1;
3371 /* The format is @"FlagsCount:Code". Flags (which are optional)
3372 can include "S" for store, "SS" for two stores, "B" for branch
3373 format, "R" if execution never continues after the opcode. The
3374 Count is the number of arguments (currently limited to 0-9),
3375 and the Code is a decimal integer representing the opcode
3378 So: @"S3:123" for a three-argument opcode (load, load, store)
3379 whose opcode number is (decimal) 123. Or: @"2:234" for a
3380 two-argument opcode (load, load) whose number is 234. */
3382 custom_opcode_g.name = (uchar *) token_text;
3383 custom_opcode_g.flags = 0;
3384 custom_opcode_g.op_rules = 0;
3385 custom_opcode_g.no = 0;
3389 for (cx = token_text; *cx && *cx != ':'; cx++) {
3395 if (custom_opcode_g.flags & St)
3396 custom_opcode_g.flags |= St2;
3398 custom_opcode_g.flags |= St;
3401 custom_opcode_g.flags |= Br;
3404 custom_opcode_g.flags |= Rf;
3408 custom_opcode_g.no = (*cx) - '0';
3412 error("Unknown custom opcode flag: options are B (branch), \
3413 S (store), SS (two stores), R (execution never continues)");
3419 error("Custom opcode must have colon");
3424 error("Custom opcode must have colon followed by opcode number");
3426 custom_opcode_g.code = atoi(cx);
3429 O = custom_opcode_g;
3432 if (token_type != OPCODE_NAME_TT && token_type != OPCODE_MACRO_TT) {
3433 ebf_error("an opcode name", token_text);
3434 panic_mode_error_recovery();
3437 AI.internal_number = token_value;
3438 if (token_type == OPCODE_MACRO_TT) {
3439 O = internal_number_to_opmacro_g(AI.internal_number);
3443 O = internal_number_to_opcode_g(AI.internal_number);
3446 return_sp_as_variable = TRUE;
3451 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
3454 if (AI.operand_count == 8) {
3455 error("No assembly instruction may have more than 8 operands");
3456 panic_mode_error_recovery();
3460 if ((O.flags & Br) && (AI.operand_count == O.no-1)) {
3461 if (!((token_type == SEP_TT) && (token_value == BRANCH_SEP))) {
3463 error("Branch opcode must have '?' label");
3466 AO.type = CONSTANT_OT;
3467 AO.value = parse_label();
3468 AO.marker = BRANCH_MV;
3472 AO = parse_operand_g();
3475 AI.operand[AI.operand_count] = AO;
3479 return_sp_as_variable = FALSE;
3481 if (O.no != AI.operand_count) {
3487 assembleg_macro(&AI);
3489 assembleg_instruction(&AI);
3493 make_opcode_syntax_g(O);
3494 error_named("Assembly mistake: syntax is",
3495 opcode_syntax_string);
3499 extern void parse_assembly(void)
3507 /* ========================================================================= */
3508 /* Data structure management routines */
3509 /* ------------------------------------------------------------------------- */
3511 extern void init_asm_vars(void)
3514 for (i=0;i<16;i++) flags2_requirements[i]=0;
3516 uses_unicode_features = FALSE;
3517 uses_memheap_features = FALSE;
3518 uses_acceleration_features = FALSE;
3519 uses_float_features = FALSE;
3520 uses_extundo_features = FALSE;
3521 uses_double_features = FALSE;
3524 sequence_points = NULL;
3525 sequence_point_follows = TRUE;
3526 label_moved_error_already_given = FALSE;
3531 extern void asm_begin_pass(void)
3532 { no_instructions = 0;
3534 no_sequence_points = 0;
3537 next_sequence_point = 0;
3539 execution_never_reaches_here = EXECSTATE_REACHABLE;
3542 extern void asm_allocate_arrays(void)
3544 initialise_memory_list(&variables_memlist,
3545 sizeof(variableinfo), 200, (void**)&variables,
3548 initialise_memory_list(&labels_memlist,
3549 sizeof(labelinfo), 1000, (void**)&labels,
3551 initialise_memory_list(&labeluse_memlist,
3552 sizeof(int), 1000, (void**)&labeluse,
3554 initialise_memory_list(&sequence_points_memlist,
3555 sizeof(sequencepointinfo), 1000, (void**)&sequence_points,
3558 initialise_memory_list(&zcode_holding_area_memlist,
3559 sizeof(uchar), 2000, (void**)&zcode_holding_area,
3560 "compiled routine code area");
3561 initialise_memory_list(&zcode_markers_memlist,
3562 sizeof(uchar), 2000, (void**)&zcode_markers,
3563 "compiled routine markers area");
3565 initialise_memory_list(&named_routine_symbols_memlist,
3566 sizeof(int32), 1000, (void**)&named_routine_symbols,
3567 "named routine symbols");
3569 initialise_memory_list(&zcode_area_memlist,
3570 sizeof(uchar), 8192, (void**)&zcode_area,
3573 initialise_memory_list(¤t_routine_name,
3574 sizeof(char), 3*MAX_IDENTIFIER_LENGTH, NULL,
3575 "routine name currently being defined");
3578 extern void asm_free_arrays(void)
3580 deallocate_memory_list(&variables_memlist);
3582 deallocate_memory_list(&labels_memlist);
3583 deallocate_memory_list(&sequence_points_memlist);
3585 deallocate_memory_list(&zcode_holding_area_memlist);
3586 deallocate_memory_list(&zcode_markers_memlist);
3588 deallocate_memory_list(&named_routine_symbols_memlist);
3589 deallocate_memory_list(&zcode_area_memlist);
3590 deallocate_memory_list(¤t_routine_name);
3593 /* ========================================================================= */