1 /* ------------------------------------------------------------------------- */
2 /* "asm" : The Inform assembler */
4 /* Part of Inform 6.42 */
5 /* copyright (c) Graham Nelson 1993 - 2024 */
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 not be a
96 simple symbol, e.g. for an "obj.prop"
99 static int32 routine_start_pc;
101 int32 *named_routine_symbols; /* Allocated up to no_named_routines */
102 static memory_list named_routine_symbols_memlist;
104 static void transfer_routine_z(void);
105 static void transfer_routine_g(void);
107 /* ------------------------------------------------------------------------- */
109 /* ------------------------------------------------------------------------- */
111 static labelinfo *labels; /* Label offsets (i.e. zmachine_pc values).
112 These are allocated sequentially, but accessed
113 as a double-linked list from first_label
114 to last_label (in PC order). */
115 static memory_list labels_memlist;
116 static int first_label, last_label;
118 static int *labeluse; /* Flags indicating whether a given label has been
119 used as a branch target yet. */
120 static memory_list labeluse_memlist;
121 static int labeluse_size; /* Entries up to here are initialized */
123 static sequencepointinfo *sequence_points; /* Allocated to next_sequence_point.
124 Only used when debugfile_switch
126 static memory_list sequence_points_memlist;
128 /* ------------------------------------------------------------------------- */
129 /* Label management */
130 /* ------------------------------------------------------------------------- */
132 /* The stripping of unreachable code requires a bit of explanation.
134 As we compile a function, we update the execution_never_reaches_here
135 flag to reflect whether the current line is reachable. If the flag
136 is set (EXECSTATE_UNREACHABLE), we skip generating opcodes,
137 compiling strings, and so on. (See assemblez_instruction(),
138 assembleg_instruction(), and compile_string() for these checks.)
140 If we're *between* functions, the execution_never_reaches_here flag
141 is always clear (EXECSTATE_REACHABLE), so global strings are
144 In general, every time we compile a JUMP or RETURN opcode, the flag
145 gets set. Every time we compile a label, the flag gets cleared.
146 This makes sense if you consider a common "while" loop:
159 @jnz flag ?ExitLabel;
165 Code after an unconditional JUMP is obviously unreachable. But the
166 next thing that happens is the .ExitLabel, which is the target of a
167 branch, so the next line is reachable again.
169 However, if the unreachable flag is set when we *begin* a statement
170 (or braced block of statements), we can get tougher. We set the
171 EXECSTATE_ENTIRE flag for the entire duration of the statement or
172 block. This flag cannot be cleared by compiling labels. An example
181 If the DOSTUFF constant is false, the entire "while" loop is definitely
182 unreachable. So we should skip .TopLabel, .ExitLabel, and everything
183 in between. To ensure this, we set EXECSTATE_ENTIRE upon entering the
184 "if {...}" braced block, and reset it upon leaving.
186 (See parse_code_block() and parse_statement() for the (slightly fugly)
187 bit-fidding that accomplishes this.)
189 As an added optimization, some labels are known to be "forward"; they
190 are only reached by forward jumps. (.ExitLabel above is an example.)
191 If we reach a forward label and nothing has in fact jumped there,
192 the label is dead and we can skip it. (And thus also skip clearing
193 the unreachable flag!)
195 To understand *that*, consider a "while true" loop with no "break":
203 This never branches to .ExitLabel. So when we reach .ExitLabel,
204 we can say for sure that *nothing* branches there. So we skip
205 compiling that label. The unreachable flag is left set (because we
206 just finished the jump to .TopLabel). Thus, any code following the
207 entire loop is known to be unreachable, and we can righteously
210 (In contrast, .TopLabel cannot be skipped because it might be the
211 target of a *backwards* branch later on. In fact there might be
212 several -- any "continue" in the loop will jump to .TopLabel.)
215 /* Set the position of the given label. The offset will be the current
216 zmachine_pc, or -1 if the label is definitely unused.
218 This adds the label to a linked list (via first_label, last_label).
220 The linked list must be in increasing PC order. We know this will
221 be true because we call this as we run through the function, so
222 zmachine_pc always increases.
224 (It won't necessarily be in *label index* order, though.)
226 static void set_label_offset(int label, int32 offset)
228 ensure_memory_list_available(&labels_memlist, label+1);
230 labels[label].offset = offset;
231 labels[label].symbol = -1;
233 /* Mark this label as invalid and don't put it in the linked list. */
234 labels[label].prev = -1;
235 labels[label].next = -1;
239 if (last_label == -1)
240 { labels[label].prev = -1;
244 { labels[label].prev = last_label;
245 labels[last_label].next = label;
248 labels[label].next = -1;
251 /* Set a flag indicating that the given label has been jumped to. */
252 static void mark_label_used(int label)
257 /* Entries from 0 to labeluse_size have meaningful values.
258 If we have to increase labeluse_size, initialize the new
260 ensure_memory_list_available(&labeluse_memlist, label+1);
261 for (; labeluse_size < label+1; labeluse_size++) {
262 labeluse[labeluse_size] = FALSE;
264 labeluse[label] = TRUE;
267 /* ------------------------------------------------------------------------- */
268 /* Useful tool for building operands */
269 /* ------------------------------------------------------------------------- */
271 extern void set_constant_ot(assembly_operand *AO)
274 if (AO->value >= 0 && AO->value <= 255)
275 AO->type = SHORT_CONSTANT_OT;
277 AO->type = LONG_CONSTANT_OT;
281 AO->type = ZEROCONSTANT_OT;
282 else if (AO->value >= -0x80 && AO->value < 0x80)
283 AO->type = BYTECONSTANT_OT;
284 else if (AO->value >= -0x8000 && AO->value < 0x8000)
285 AO->type = HALFCONSTANT_OT;
287 AO->type = CONSTANT_OT;
291 extern int is_constant_ot(int otval)
294 return ((otval == LONG_CONSTANT_OT)
295 || (otval == SHORT_CONSTANT_OT));
298 return ((otval == CONSTANT_OT)
299 || (otval == HALFCONSTANT_OT)
300 || (otval == BYTECONSTANT_OT)
301 || (otval == ZEROCONSTANT_OT));
305 extern int is_variable_ot(int otval)
308 return (otval == VARIABLE_OT);
311 return ((otval == LOCALVAR_OT)
312 || (otval == GLOBALVAR_OT));
316 /* ------------------------------------------------------------------------- */
317 /* Used in printing assembly traces */
318 /* ------------------------------------------------------------------------- */
320 extern char *variable_name(int32 i)
322 if (i==0) return("sp");
323 if (i<MAX_LOCAL_VARIABLES) return get_local_variable_name(i-1);
326 if (i==255) return("TEMP1");
327 if (i==254) return("TEMP2");
328 if (i==253) return("TEMP3");
329 if (i==252) return("TEMP4");
330 if (i==251) return("self");
331 if (i==250) return("sender");
332 if (i==249) return("sw__var");
333 if (i >= 256 && i < 286)
334 { if (i - 256 < NUMBER_SYSTEM_FUNCTIONS) return system_functions.keywords[i - 256];
335 return "<unnamed system function>";
339 switch (i - MAX_LOCAL_VARIABLES) {
340 case 0: return "temp_global";
341 case 1: return "temp__global2";
342 case 2: return "temp__global3";
343 case 3: return "temp__global4";
344 case 4: return "self";
345 case 5: return "sender";
346 case 6: return "sw__var";
347 case 7: return "sys__glob0";
348 case 8: return "sys__glob1";
349 case 9: return "sys__glob2";
350 case 10: return "sys_statusline_flag";
354 return (symbols[variables[i].token].name);
357 /* Print symbolic information about the AO, if there is any. */
358 static void print_operand_annotation(const assembly_operand *o)
362 printf((!any) ? " (" : ": ");
364 printf("%s", describe_mv(o->marker));
367 printf(": %s", veneer_routine_name(o->value));
370 printf(": %s", name_of_system_constant(o->value));
374 print_dict_word(o->value);
379 if (o->symindex >= 0 && o->symindex < no_symbols) {
380 printf((!any) ? " (" : ": ");
382 printf("%s", symbols[o->symindex].name);
384 if (any) printf(")");
387 static void print_operand_z(const assembly_operand *o, int annotate)
389 { case EXPRESSION_OT: printf("expr_"); break;
390 case LONG_CONSTANT_OT: printf("long_"); break;
391 case SHORT_CONSTANT_OT: printf("short_"); break;
393 if (o->value==0) { printf("sp"); return; }
394 printf("%s", variable_name(o->value)); return;
395 case OMITTED_OT: printf("<no value>"); return;
397 printf("%d", o->value);
399 print_operand_annotation(o);
402 static void print_operand_g(const assembly_operand *o, int annotate)
405 case EXPRESSION_OT: printf("expr_"); break;
406 case CONSTANT_OT: printf("long_"); break;
407 case HALFCONSTANT_OT: printf("short_"); break;
408 case BYTECONSTANT_OT: printf("byte_"); break;
409 case ZEROCONSTANT_OT: printf("zero_"); return;
410 case DEREFERENCE_OT: printf("*"); break;
412 printf("global_%d (%s)", o->value, variable_name(o->value));
418 printf("local_%d (%s)", o->value-1, variable_name(o->value));
421 if (o->value >= 0 && o->value < NUMBER_SYSTEM_FUNCTIONS)
422 printf("%s", system_functions.keywords[o->value]);
424 printf("<unnamed system function>");
426 case OMITTED_OT: printf("<no value>"); return;
427 default: printf("???_"); break;
429 printf("%d", o->value);
431 print_operand_annotation(o);
434 extern void print_operand(const assembly_operand *o, int annotate)
437 print_operand_z(o, annotate);
439 print_operand_g(o, annotate);
442 /* ------------------------------------------------------------------------- */
443 /* Writing bytes to the code area */
444 /* ------------------------------------------------------------------------- */
446 static void byteout(int32 i, int mv)
448 ensure_memory_list_available(&zcode_markers_memlist, zcode_ha_size+1);
449 ensure_memory_list_available(&zcode_holding_area_memlist, zcode_ha_size+1);
450 zcode_markers[zcode_ha_size] = (uchar) mv;
451 zcode_holding_area[zcode_ha_size++] = (uchar) i;
455 /* ------------------------------------------------------------------------- */
456 /* A database of the 115 canonical Infocom opcodes in Versions 3 to 6 */
457 /* And of the however-many-there-are Glulx opcodes */
458 /* ------------------------------------------------------------------------- */
460 typedef struct opcodez
461 { uchar *name; /* Lower case standard name */
462 int version1; /* Valid from this version number... */
463 int version2; /* ...until this one (or forever if this is 0) */
464 int extension; /* In later versions, see this line in extension table:
465 if -1, the opcode is illegal in later versions */
466 int code; /* Opcode number within its operand-number block */
467 int flags; /* Flags (see below) */
468 int op_rules; /* Any unusual operand rule applying (see below) */
469 int flags2_set; /* If not zero, set this bit in Flags 2 in the header
470 of any game using the opcode */
471 int no; /* Number of operands (see below) */
474 typedef struct opcodeg
475 { uchar *name; /* Lower case standard name */
476 int32 code; /* Opcode number */
477 int flags; /* Flags (see below) */
478 int op_rules; /* Any unusual operand rule applying (see below) */
479 int no; /* Number of operands */
482 /* Flags which can be set */
484 #define St 1 /* Store */
485 #define Br 2 /* Branch */
486 #define Rf 4 /* "Return flag": execution never continues after this
487 opcode (e.g., is a return or unconditional jump) */
488 #define St2 8 /* Store2 (second-to-last operand is store (Glulx)) */
490 /* Codes for any unusual operand assembly rules */
494 #define VARIAB 1 /* First operand expected to be a variable name and
495 assembled to a short constant: the variable number */
496 #define TEXT 2 /* One text operand, to be Z-encoded into the program */
497 #define LABEL 3 /* One operand, a label, given as long constant offset */
498 #define CALL 4 /* First operand is name of a routine, to be assembled
499 as long constant (the routine's packed address):
500 as if the name were prefixed by #r$ */
502 /* Glulx: (bit flags for Glulx VM features) */
504 #define GOP_Unicode 1 /* uses_unicode_features */
505 #define GOP_MemHeap 2 /* uses_memheap_features */
506 #define GOP_Acceleration 4 /* uses_acceleration_features */
507 #define GOP_Float 8 /* uses_float_features */
508 #define GOP_ExtUndo 16 /* uses_extundo_features */
509 #define GOP_Double 32 /* uses_double_features */
511 /* Codes for the number of operands */
513 #define TWO 1 /* 2 (with certain types of operand, compiled as VAR) */
514 #define VAR 2 /* 0 to 4 */
515 #define VAR_LONG 3 /* 0 to 8 */
516 #define ONE 4 /* 1 */
517 #define ZERO 5 /* 0 */
518 #define EXT 6 /* Extended opcode set VAR: 0 to 4 */
519 #define EXT_LONG 7 /* Extended: 0 to 8 (not used by the canonical opcodes) */
521 static opcodez opcodes_table_z[] =
523 /* Opcodes introduced in Version 3 */
525 /* 0 */ { (uchar *) "je", 3, 0, -1, 0x01, Br, 0, 0, TWO },
526 /* 1 */ { (uchar *) "jl", 3, 0, -1, 0x02, Br, 0, 0, TWO },
527 /* 2 */ { (uchar *) "jg", 3, 0, -1, 0x03, Br, 0, 0, TWO },
528 /* 3 */ { (uchar *) "dec_chk", 3, 0, -1, 0x04, Br, VARIAB, 0, TWO },
529 /* 4 */ { (uchar *) "inc_chk", 3, 0, -1, 0x05, Br, VARIAB, 0, TWO },
530 /* 5 */ { (uchar *) "jin", 3, 0, -1, 0x06, Br, 0, 0, TWO },
531 /* 6 */ { (uchar *) "test", 3, 0, -1, 0x07, Br, 0, 0, TWO },
532 /* 7 */ { (uchar *) "or", 3, 0, -1, 0x08, St, 0, 0, TWO },
533 /* 8 */ { (uchar *) "and", 3, 0, -1, 0x09, St, 0, 0, TWO },
534 /* 9 */ { (uchar *) "test_attr", 3, 0, -1, 0x0A, Br, 0, 0, TWO },
535 /* 10 */ {(uchar *) "set_attr", 3, 0, -1, 0x0B, 0, 0, 0, TWO },
536 /* 11 */ {(uchar *) "clear_attr", 3, 0, -1, 0x0C, 0, 0, 0, TWO },
537 /* 12 */ {(uchar *) "store", 3, 0, -1, 0x0D, 0, VARIAB, 0, TWO },
538 /* 13 */ {(uchar *) "insert_obj", 3, 0, -1, 0x0E, 0, 0, 0, TWO },
539 /* 14 */ {(uchar *) "loadw", 3, 0, -1, 0x0F, St, 0, 0, TWO },
540 /* 15 */ {(uchar *) "loadb", 3, 0, -1, 0x10, St, 0, 0, TWO },
541 /* 16 */ {(uchar *) "get_prop", 3, 0, -1, 0x11, St, 0, 0, TWO },
542 /* 17 */ {(uchar *) "get_prop_addr", 3, 0, -1, 0x12, St, 0, 0, TWO },
543 /* 18 */ {(uchar *) "get_next_prop", 3, 0, -1, 0x13, St, 0, 0, TWO },
544 /* 19 */ {(uchar *) "add", 3, 0, -1, 0x14, St, 0, 0, TWO },
545 /* 20 */ {(uchar *) "sub", 3, 0, -1, 0x15, St, 0, 0, TWO },
546 /* 21 */ {(uchar *) "mul", 3, 0, -1, 0x16, St, 0, 0, TWO },
547 /* 22 */ {(uchar *) "div", 3, 0, -1, 0x17, St, 0, 0, TWO },
548 /* 23 */ {(uchar *) "mod", 3, 0, -1, 0x18, St, 0, 0, TWO },
549 /* 24 */ {(uchar *) "call", 3, 0, -1, 0x20, St, CALL, 0, VAR },
550 /* 25 */ {(uchar *) "storew", 3, 0, -1, 0x21, 0, 0, 0, VAR },
551 /* 26 */ {(uchar *) "storeb", 3, 0, -1, 0x22, 0, 0, 0, VAR },
552 /* 27 */ {(uchar *) "put_prop", 3, 0, -1, 0x23, 0, 0, 0, VAR },
553 /* This is the version of "read" called "sread" internally: */
554 /* 28 */ {(uchar *) "read", 3, 0, -1, 0x24, 0, 0, 0, VAR },
555 /* 29 */ {(uchar *) "print_char", 3, 0, -1, 0x25, 0, 0, 0, VAR },
556 /* 30 */ {(uchar *) "print_num", 3, 0, -1, 0x26, 0, 0, 0, VAR },
557 /* 31 */ {(uchar *) "random", 3, 0, -1, 0x27, St, 0, 0, VAR },
558 /* 32 */ {(uchar *) "push", 3, 0, -1, 0x28, 0, 0, 0, VAR },
559 /* 33 */ {(uchar *) "pull", 3, 5, 6, 0x29, 0, VARIAB, 0, VAR },
560 /* 34 */ {(uchar *) "split_window", 3, 0, -1, 0x2A, 0, 0, 0, VAR },
561 /* 35 */ {(uchar *) "set_window", 3, 0, -1, 0x2B, 0, 0, 0, VAR },
562 /* 36 */ {(uchar *) "output_stream", 3, 0, -1, 0x33, 0, 0, 0, VAR },
563 /* 37 */ {(uchar *) "input_stream", 3, 0, -1, 0x34, 0, 0, 0, VAR },
564 /* 38 */ {(uchar *) "sound_effect", 3, 0, -1, 0x35, 0, 0, 7, VAR },
565 /* 39 */ {(uchar *) "jz", 3, 0, -1, 0x00, Br, 0, 0, ONE },
566 /* 40 */ {(uchar *) "get_sibling", 3, 0, -1, 0x01, St+Br, 0, 0, ONE },
567 /* 41 */ {(uchar *) "get_child", 3, 0, -1, 0x02, St+Br, 0, 0, ONE },
568 /* 42 */ {(uchar *) "get_parent", 3, 0, -1, 0x03, St, 0, 0, ONE },
569 /* 43 */ {(uchar *) "get_prop_len", 3, 0, -1, 0x04, St, 0, 0, ONE },
570 /* 44 */ {(uchar *) "inc", 3, 0, -1, 0x05, 0, VARIAB, 0, ONE },
571 /* 45 */ {(uchar *) "dec", 3, 0, -1, 0x06, 0, VARIAB, 0, ONE },
572 /* 46 */ {(uchar *) "print_addr", 3, 0, -1, 0x07, 0, 0, 0, ONE },
573 /* 47 */ {(uchar *) "remove_obj", 3, 0, -1, 0x09, 0, 0, 0, ONE },
574 /* 48 */ {(uchar *) "print_obj", 3, 0, -1, 0x0A, 0, 0, 0, ONE },
575 /* 49 */ {(uchar *) "ret", 3, 0, -1, 0x0B, Rf, 0, 0, ONE },
576 /* 50 */ {(uchar *) "jump", 3, 0, -1, 0x0C, Rf, LABEL, 0, ONE },
577 /* 51 */ {(uchar *) "print_paddr", 3, 0, -1, 0x0D, 0, 0, 0, ONE },
578 /* 52 */ {(uchar *) "load", 3, 0, -1, 0x0E, St, VARIAB, 0, ONE },
579 /* 53 */ {(uchar *) "not", 3, 3, 0, 0x0F, St, 0, 0, ONE },
580 /* 54 */ {(uchar *) "rtrue", 3, 0, -1, 0x00, Rf, 0, 0,ZERO },
581 /* 55 */ {(uchar *) "rfalse", 3, 0, -1, 0x01, Rf, 0, 0,ZERO },
582 /* 56 */ {(uchar *) "print", 3, 0, -1, 0x02, 0, TEXT, 0,ZERO },
583 /* 57 */ {(uchar *) "print_ret", 3, 0, -1, 0x03, Rf, TEXT, 0,ZERO },
584 /* 58 */ {(uchar *) "nop", 3, 0, -1, 0x04, 0, 0, 0,ZERO },
585 /* 59 */ {(uchar *) "save", 3, 3, 1, 0x05, Br, 0, 0,ZERO },
586 /* 60 */ {(uchar *) "restore", 3, 3, 2, 0x06, Br, 0, 0,ZERO },
587 /* 61 */ {(uchar *) "restart", 3, 0, -1, 0x07, 0, 0, 0,ZERO },
588 /* 62 */ {(uchar *) "ret_popped", 3, 0, -1, 0x08, Rf, 0, 0,ZERO },
589 /* 63 */ {(uchar *) "pop", 3, 4, -1, 0x09, 0, 0, 0,ZERO },
590 /* 64 */ {(uchar *) "quit", 3, 0, -1, 0x0A, Rf, 0, 0,ZERO },
591 /* 65 */ {(uchar *) "new_line", 3, 0, -1, 0x0B, 0, 0, 0,ZERO },
592 /* 66 */ {(uchar *) "show_status", 3, 3, -1, 0x0C, 0, 0, 0,ZERO },
593 /* 67 */ {(uchar *) "verify", 3, 0, -1, 0x0D, Br, 0, 0,ZERO },
595 /* Opcodes introduced in Version 4 */
597 /* 68 */ {(uchar *) "call_2s", 4, 0, -1, 0x19, St, CALL, 0, TWO },
598 /* 69 */ {(uchar *) "call_vs", 4, 0, -1, 0x20, St, CALL, 0, VAR },
599 /* This is the version of "read" called "aread" internally: */
600 /* 70 */ {(uchar *) "read", 4, 0, -1, 0x24, St, 0, 0, VAR },
601 /* 71 */ {(uchar *) "call_vs2", 4, 0, -1, 0x2C, St, CALL, 0,
603 /* 72 */ {(uchar *) "erase_window", 4, 0, -1, 0x2D, 0, 0, 0, VAR },
604 /* 73 */ {(uchar *) "erase_line", 4, 0, -1, 0x2E, 0, 0, 0, VAR },
605 /* 74 */ {(uchar *) "set_cursor", 4, 0, -1, 0x2F, 0, 0, 0, VAR },
606 /* 75 */ {(uchar *) "get_cursor", 4, 0, -1, 0x30, 0, 0, 0, VAR },
607 /* 76 */ {(uchar *) "set_text_style", 4, 0, -1, 0x31, 0, 0, 0, VAR },
608 /* 77 */ {(uchar *) "buffer_mode", 4, 0, -1, 0x32, 0, 0, 0, VAR },
609 /* 78 */ {(uchar *) "read_char", 4, 0, -1, 0x36, St, 0, 0, VAR },
610 /* 79 */ {(uchar *) "scan_table", 4, 0, -1, 0x37, St+Br, 0, 0, VAR },
611 /* 80 */ {(uchar *) "call_1s", 4, 0, -1, 0x08, St, CALL, 0, ONE },
613 /* Opcodes introduced in Version 5 */
615 /* 81 */ {(uchar *) "call_2n", 5, 0, -1, 0x1a, 0, CALL, 0, TWO },
616 /* 82 */ {(uchar *) "set_colour", 5, 0, -1, 0x1b, 0, 0, 6, TWO },
617 /* 83 */ {(uchar *) "throw", 5, 0, -1, 0x1c, 0, 0, 0, TWO },
618 /* 84 */ {(uchar *) "call_vn", 5, 0, -1, 0x39, 0, CALL, 0, VAR },
619 /* 85 */ {(uchar *) "call_vn2", 5, 0, -1, 0x3a, 0, CALL, 0,
621 /* 86 */ {(uchar *) "tokenise", 5, 0, -1, 0x3b, 0, 0, 0, VAR },
622 /* 87 */ {(uchar *) "encode_text", 5, 0, -1, 0x3c, 0, 0, 0, VAR },
623 /* 88 */ {(uchar *) "copy_table", 5, 0, -1, 0x3d, 0, 0, 0, VAR },
624 /* 89 */ {(uchar *) "print_table", 5, 0, -1, 0x3e, 0, 0, 0, VAR },
625 /* 90 */ {(uchar *) "check_arg_count", 5, 0, -1, 0x3f, Br, 0, 0, VAR },
626 /* 91 */ {(uchar *) "call_1n", 5, 0, -1, 0x0F, 0, CALL, 0, ONE },
627 /* 92 */ {(uchar *) "catch", 5, 0, -1, 0x09, St, 0, 0, ZERO },
628 /* 93 */ {(uchar *) "piracy", 5, 0, -1, 0x0F, Br, 0, 0, ZERO },
629 /* 94 */ {(uchar *) "log_shift", 5, 0, -1, 0x02, St, 0, 0, EXT },
630 /* 95 */ {(uchar *) "art_shift", 5, 0, -1, 0x03, St, 0, 0, EXT },
631 /* 96 */ {(uchar *) "set_font", 5, 0, -1, 0x04, St, 0, 0, EXT },
632 /* 97 */ {(uchar *) "save_undo", 5, 0, -1, 0x09, St, 0, 4, EXT },
633 /* 98 */ {(uchar *) "restore_undo", 5, 0, -1, 0x0A, St, 0, 4, EXT },
635 /* Opcodes introduced in Version 6 */
637 /* 99 */ { (uchar *) "draw_picture", 6, 6, -1, 0x05, 0, 0, 3, EXT },
638 /* 100 */ { (uchar *) "picture_data", 6, 6, -1, 0x06, Br, 0, 3, EXT },
639 /* 101 */ { (uchar *) "erase_picture", 6, 6, -1, 0x07, 0, 0, 3, EXT },
640 /* 102 */ { (uchar *) "set_margins", 6, 6, -1, 0x08, 0, 0, 0, EXT },
641 /* 103 */ { (uchar *) "move_window", 6, 6, -1, 0x10, 0, 0, 0, EXT },
642 /* 104 */ { (uchar *) "window_size", 6, 6, -1, 0x11, 0, 0, 0, EXT },
643 /* 105 */ { (uchar *) "window_style", 6, 6, -1, 0x12, 0, 0, 0, EXT },
644 /* 106 */ { (uchar *) "get_wind_prop", 6, 6, -1, 0x13, St, 0, 0, EXT },
645 /* 107 */ { (uchar *) "scroll_window", 6, 6, -1, 0x14, 0, 0, 0, EXT },
646 /* 108 */ { (uchar *) "pop_stack", 6, 6, -1, 0x15, 0, 0, 0, EXT },
647 /* 109 */ { (uchar *) "read_mouse", 6, 6, -1, 0x16, 0, 0, 5, EXT },
648 /* 110 */ { (uchar *) "mouse_window", 6, 6, -1, 0x17, 0, 0, 5, EXT },
649 /* 111 */ { (uchar *) "push_stack", 6, 6, -1, 0x18, Br, 0, 0, EXT },
650 /* 112 */ { (uchar *) "put_wind_prop", 6, 6, -1, 0x19, 0, 0, 0, EXT },
651 /* 113 */ { (uchar *) "print_form", 6, 6, -1, 0x1a, 0, 0, 0, EXT },
652 /* 114 */ { (uchar *) "make_menu", 6, 6, -1, 0x1b, Br, 0, 8, EXT },
653 /* 115 */ { (uchar *) "picture_table", 6, 6, -1, 0x1c, 0, 0, 3, EXT },
655 /* Opcodes introduced in Z-Machine Specification Standard 1.0 */
657 /* 116 */ { (uchar *) "print_unicode", 5, 0, -1, 0x0b, 0, 0, 0, EXT },
658 /* 117 */ { (uchar *) "check_unicode", 5, 0, -1, 0x0c, St, 0, 0, EXT },
660 /* Opcodes introduced in Z-Machine Specification Standard 1.1 */
662 /* 118 */ { (uchar *) "set_true_colour", 5, 0, -1, 0x0d, 0, 0, 0, EXT },
663 /* 119 */ { (uchar *) "buffer_screen", 6, 6, -1, 0x1d, St, 0, 0, EXT }
666 /* Subsequent forms for opcodes whose meaning changes with version */
668 static opcodez extension_table_z[] =
670 /* 0 */ { (uchar *) "not", 4, 4, 3, 0x0F, St, 0, 0, ONE },
671 /* 1 */ { (uchar *) "save", 4, 4, 4, 0x05, St, 0, 0,ZERO },
672 /* 2 */ { (uchar *) "restore", 4, 4, 5, 0x06, St, 0, 0,ZERO },
673 /* 3 */ { (uchar *) "not", 5, 0, -1, 0x38, St, 0, 0, VAR },
674 /* 4 */ { (uchar *) "save", 5, 0, -1, 0x00, St, 0, 0, EXT },
675 /* 5 */ { (uchar *) "restore", 5, 0, -1, 0x01, St, 0, 0, EXT },
676 /* 6 */ { (uchar *) "pull", 6, 6, -1, 0x29, St, 0, 0, VAR }
679 static opcodez invalid_opcode_z =
680 { (uchar *) "invalid", 0, 0, -1, 0xff, 0, 0, 0, ZERO};
682 static opcodez custom_opcode_z;
684 /* Note that this table assumes that all opcodes have at most two
685 branch-label or store operands, and that if they exist, they are the
686 last operands. Glulx does not actually guarantee this. But it is
687 true for all opcodes in the current Glulx spec, so we will assume
690 Also note that Inform can only compile branches to constant offsets,
691 even though the Glulx machine can handle stack or memory-loaded
692 operands in a branch instruction.
695 static opcodeg opcodes_table_g[] = {
696 { (uchar *) "nop", 0x00, 0, 0, 0 },
697 { (uchar *) "add", 0x10, St, 0, 3 },
698 { (uchar *) "sub", 0x11, St, 0, 3 },
699 { (uchar *) "mul", 0x12, St, 0, 3 },
700 { (uchar *) "div", 0x13, St, 0, 3 },
701 { (uchar *) "mod", 0x14, St, 0, 3 },
702 { (uchar *) "neg", 0x15, St, 0, 2 },
703 { (uchar *) "bitand", 0x18, St, 0, 3 },
704 { (uchar *) "bitor", 0x19, St, 0, 3 },
705 { (uchar *) "bitxor", 0x1A, St, 0, 3 },
706 { (uchar *) "bitnot", 0x1B, St, 0, 2 },
707 { (uchar *) "shiftl", 0x1C, St, 0, 3 },
708 { (uchar *) "sshiftr", 0x1D, St, 0, 3 },
709 { (uchar *) "ushiftr", 0x1E, St, 0, 3 },
710 { (uchar *) "jump", 0x20, Br|Rf, 0, 1 },
711 { (uchar *) "jz", 0x22, Br, 0, 2 },
712 { (uchar *) "jnz", 0x23, Br, 0, 2 },
713 { (uchar *) "jeq", 0x24, Br, 0, 3 },
714 { (uchar *) "jne", 0x25, Br, 0, 3 },
715 { (uchar *) "jlt", 0x26, Br, 0, 3 },
716 { (uchar *) "jge", 0x27, Br, 0, 3 },
717 { (uchar *) "jgt", 0x28, Br, 0, 3 },
718 { (uchar *) "jle", 0x29, Br, 0, 3 },
719 { (uchar *) "jltu", 0x2A, Br, 0, 3 },
720 { (uchar *) "jgeu", 0x2B, Br, 0, 3 },
721 { (uchar *) "jgtu", 0x2C, Br, 0, 3 },
722 { (uchar *) "jleu", 0x2D, Br, 0, 3 },
723 { (uchar *) "call", 0x30, St, 0, 3 },
724 { (uchar *) "return", 0x31, Rf, 0, 1 },
725 { (uchar *) "catch", 0x32, Br|St, 0, 2 },
726 { (uchar *) "throw", 0x33, Rf, 0, 2 },
727 { (uchar *) "tailcall", 0x34, Rf, 0, 2 },
728 { (uchar *) "copy", 0x40, St, 0, 2 },
729 { (uchar *) "copys", 0x41, St, 0, 2 },
730 { (uchar *) "copyb", 0x42, St, 0, 2 },
731 { (uchar *) "sexs", 0x44, St, 0, 2 },
732 { (uchar *) "sexb", 0x45, St, 0, 2 },
733 { (uchar *) "aload", 0x48, St, 0, 3 },
734 { (uchar *) "aloads", 0x49, St, 0, 3 },
735 { (uchar *) "aloadb", 0x4A, St, 0, 3 },
736 { (uchar *) "aloadbit", 0x4B, St, 0, 3 },
737 { (uchar *) "astore", 0x4C, 0, 0, 3 },
738 { (uchar *) "astores", 0x4D, 0, 0, 3 },
739 { (uchar *) "astoreb", 0x4E, 0, 0, 3 },
740 { (uchar *) "astorebit", 0x4F, 0, 0, 3 },
741 { (uchar *) "stkcount", 0x50, St, 0, 1 },
742 { (uchar *) "stkpeek", 0x51, St, 0, 2 },
743 { (uchar *) "stkswap", 0x52, 0, 0, 0 },
744 { (uchar *) "stkroll", 0x53, 0, 0, 2 },
745 { (uchar *) "stkcopy", 0x54, 0, 0, 1 },
746 { (uchar *) "streamchar", 0x70, 0, 0, 1 },
747 { (uchar *) "streamnum", 0x71, 0, 0, 1 },
748 { (uchar *) "streamstr", 0x72, 0, 0, 1 },
749 { (uchar *) "gestalt", 0x0100, St, 0, 3 },
750 { (uchar *) "debugtrap", 0x0101, 0, 0, 1 },
751 { (uchar *) "getmemsize", 0x0102, St, 0, 1 },
752 { (uchar *) "setmemsize", 0x0103, St, 0, 2 },
753 { (uchar *) "jumpabs", 0x0104, Rf, 0, 1 },
754 { (uchar *) "random", 0x0110, St, 0, 2 },
755 { (uchar *) "setrandom", 0x0111, 0, 0, 1 },
756 { (uchar *) "quit", 0x0120, Rf, 0, 0 },
757 { (uchar *) "verify", 0x0121, St, 0, 1 },
758 { (uchar *) "restart", 0x0122, 0, 0, 0 },
759 { (uchar *) "save", 0x0123, St, 0, 2 },
760 { (uchar *) "restore", 0x0124, St, 0, 2 },
761 { (uchar *) "saveundo", 0x0125, St, 0, 1 },
762 { (uchar *) "restoreundo", 0x0126, St, 0, 1 },
763 { (uchar *) "protect", 0x0127, 0, 0, 2 },
764 { (uchar *) "glk", 0x0130, St, 0, 3 },
765 { (uchar *) "getstringtbl", 0x0140, St, 0, 1 },
766 { (uchar *) "setstringtbl", 0x0141, 0, 0, 1 },
767 { (uchar *) "getiosys", 0x0148, St|St2, 0, 2 },
768 { (uchar *) "setiosys", 0x0149, 0, 0, 2 },
769 { (uchar *) "linearsearch", 0x0150, St, 0, 8 },
770 { (uchar *) "binarysearch", 0x0151, St, 0, 8 },
771 { (uchar *) "linkedsearch", 0x0152, St, 0, 7 },
772 { (uchar *) "callf", 0x0160, St, 0, 2 },
773 { (uchar *) "callfi", 0x0161, St, 0, 3 },
774 { (uchar *) "callfii", 0x0162, St, 0, 4 },
775 { (uchar *) "callfiii", 0x0163, St, 0, 5 },
776 { (uchar *) "streamunichar", 0x73, 0, GOP_Unicode, 1 },
777 { (uchar *) "mzero", 0x170, 0, GOP_MemHeap, 2 },
778 { (uchar *) "mcopy", 0x171, 0, GOP_MemHeap, 3 },
779 { (uchar *) "malloc", 0x178, St, GOP_MemHeap, 2 },
780 { (uchar *) "mfree", 0x179, 0, GOP_MemHeap, 1 },
781 { (uchar *) "accelfunc", 0x180, 0, GOP_Acceleration, 2 },
782 { (uchar *) "accelparam", 0x181, 0, GOP_Acceleration, 2 },
783 { (uchar *) "hasundo", 0x128, St, GOP_ExtUndo, 1 },
784 { (uchar *) "discardundo",0x129, 0, GOP_ExtUndo, 0 },
785 { (uchar *) "numtof", 0x190, St, GOP_Float, 2 },
786 { (uchar *) "ftonumz", 0x191, St, GOP_Float, 2 },
787 { (uchar *) "ftonumn", 0x192, St, GOP_Float, 2 },
788 { (uchar *) "ceil", 0x198, St, GOP_Float, 2 },
789 { (uchar *) "floor", 0x199, St, GOP_Float, 2 },
790 { (uchar *) "fadd", 0x1A0, St, GOP_Float, 3 },
791 { (uchar *) "fsub", 0x1A1, St, GOP_Float, 3 },
792 { (uchar *) "fmul", 0x1A2, St, GOP_Float, 3 },
793 { (uchar *) "fdiv", 0x1A3, St, GOP_Float, 3 },
794 { (uchar *) "fmod", 0x1A4, St|St2, GOP_Float, 4 },
795 { (uchar *) "sqrt", 0x1A8, St, GOP_Float, 2 },
796 { (uchar *) "exp", 0x1A9, St, GOP_Float, 2 },
797 { (uchar *) "log", 0x1AA, St, GOP_Float, 2 },
798 { (uchar *) "pow", 0x1AB, St, GOP_Float, 3 },
799 { (uchar *) "sin", 0x1B0, St, GOP_Float, 2 },
800 { (uchar *) "cos", 0x1B1, St, GOP_Float, 2 },
801 { (uchar *) "tan", 0x1B2, St, GOP_Float, 2 },
802 { (uchar *) "asin", 0x1B3, St, GOP_Float, 2 },
803 { (uchar *) "acos", 0x1B4, St, GOP_Float, 2 },
804 { (uchar *) "atan", 0x1B5, St, GOP_Float, 2 },
805 { (uchar *) "atan2", 0x1B6, St, GOP_Float, 3 },
806 { (uchar *) "jfeq", 0x1C0, Br, GOP_Float, 4 },
807 { (uchar *) "jfne", 0x1C1, Br, GOP_Float, 4 },
808 { (uchar *) "jflt", 0x1C2, Br, GOP_Float, 3 },
809 { (uchar *) "jfle", 0x1C3, Br, GOP_Float, 3 },
810 { (uchar *) "jfgt", 0x1C4, Br, GOP_Float, 3 },
811 { (uchar *) "jfge", 0x1C5, Br, GOP_Float, 3 },
812 { (uchar *) "jisnan", 0x1C8, Br, GOP_Float, 2 },
813 { (uchar *) "jisinf", 0x1C9, Br, GOP_Float, 2 },
814 { (uchar *) "numtod", 0x200, St|St2, GOP_Double, 3 },
815 { (uchar *) "dtonumz", 0x201, St, GOP_Double, 3 },
816 { (uchar *) "dtonumn", 0x202, St, GOP_Double, 3 },
817 { (uchar *) "ftod", 0x203, St|St2, GOP_Double, 3 },
818 { (uchar *) "dtof", 0x204, St, GOP_Double, 3 },
819 { (uchar *) "dceil", 0x208, St|St2, GOP_Double, 4 },
820 { (uchar *) "dfloor", 0x209, St|St2, GOP_Double, 4 },
821 { (uchar *) "dadd", 0x210, St|St2, GOP_Double, 6 },
822 { (uchar *) "dsub", 0x211, St|St2, GOP_Double, 6 },
823 { (uchar *) "dmul", 0x212, St|St2, GOP_Double, 6 },
824 { (uchar *) "ddiv", 0x213, St|St2, GOP_Double, 6 },
825 { (uchar *) "dmodr", 0x214, St|St2, GOP_Double, 6 },
826 { (uchar *) "dmodq", 0x215, St|St2, GOP_Double, 6 },
827 { (uchar *) "dsqrt", 0x218, St|St2, GOP_Double, 4 },
828 { (uchar *) "dexp", 0x219, St|St2, GOP_Double, 4 },
829 { (uchar *) "dlog", 0x21A, St|St2, GOP_Double, 4 },
830 { (uchar *) "dpow", 0x21B, St|St2, GOP_Double, 6 },
831 { (uchar *) "dsin", 0x220, St|St2, GOP_Double, 4 },
832 { (uchar *) "dcos", 0x221, St|St2, GOP_Double, 4 },
833 { (uchar *) "dtan", 0x222, St|St2, GOP_Double, 4 },
834 { (uchar *) "dasin", 0x223, St|St2, GOP_Double, 4 },
835 { (uchar *) "dacos", 0x224, St|St2, GOP_Double, 4 },
836 { (uchar *) "datan", 0x225, St|St2, GOP_Double, 4 },
837 { (uchar *) "datan2", 0x226, St|St2, GOP_Double, 6 },
838 { (uchar *) "jdeq", 0x230, Br, GOP_Double, 7 },
839 { (uchar *) "jdne", 0x231, Br, GOP_Double, 7 },
840 { (uchar *) "jdlt", 0x232, Br, GOP_Double, 5 },
841 { (uchar *) "jdle", 0x233, Br, GOP_Double, 5 },
842 { (uchar *) "jdgt", 0x234, Br, GOP_Double, 5 },
843 { (uchar *) "jdge", 0x235, Br, GOP_Double, 5 },
844 { (uchar *) "jdisnan", 0x238, Br, GOP_Double, 3 },
845 { (uchar *) "jdisinf", 0x239, Br, GOP_Double, 3 },
848 /* The opmacros table is used for fake opcodes. The opcode numbers are
849 ignored; this table is only used for argument parsing. */
850 static opcodeg opmacros_table_g[] = {
851 { (uchar *) "pull", pull_gm, St, 0, 1 },
852 { (uchar *) "push", push_gm, 0, 0, 1 },
853 { (uchar *) "dload", dload_gm, St|St2, 0, 3 },
854 { (uchar *) "dstore", dstore_gm, 0, 0, 3 },
857 static opcodeg custom_opcode_g;
859 static opcodez internal_number_to_opcode_z(int32 i)
862 if (i == -1) return custom_opcode_z;
863 x = opcodes_table_z[i];
864 if (instruction_set_number < x.version1) return invalid_opcode_z;
865 if (x.version2 == 0) return x;
866 if (instruction_set_number <= x.version2) return x;
868 if (i < 0) return invalid_opcode_z;
869 x = extension_table_z[i];
870 if (instruction_set_number < x.version1) return invalid_opcode_z;
871 if (x.version2 == 0) return x;
872 if (instruction_set_number <= x.version2) return x;
873 return extension_table_z[x.extension];
876 static void make_opcode_syntax_z(opcodez opco)
877 { char *p = "", *q = opcode_syntax_string;
878 /* TODO: opcode_syntax_string[128] is unsafe */
879 sprintf(q, "%s", opco.name);
881 { case ONE: p=" <operand>"; break;
882 case TWO: p=" <operand1> <operand2>"; break;
884 case VAR: p=" <0 to 4 operands>"; break;
885 case VAR_LONG: p=" <0 to 8 operands>"; break;
887 switch(opco.op_rules)
888 { case TEXT: sprintf(q+strlen(q), " <text>"); return;
889 case LABEL: sprintf(q+strlen(q), " <label>"); return;
891 sprintf(q+strlen(q), " <variable>");
893 if (opco.op_rules==CALL) sprintf(q+strlen(q), " <routine>");
895 { case ONE: p=""; break;
896 case TWO: p=" <operand>"; break;
898 case VAR: p=" <1 to 4 operands>"; break;
899 case VAR_LONG: p=" <1 to 8 operands>"; break;
903 sprintf(q+strlen(q), "%s", p);
904 if ((opco.flags & St) != 0) sprintf(q+strlen(q), " -> <result-variable>");
905 if ((opco.flags & Br) != 0) sprintf(q+strlen(q), " ?[~]<label>");
908 static opcodeg internal_number_to_opcode_g(int32 i)
911 if (i == -1) return custom_opcode_g;
912 x = opcodes_table_g[i];
916 static opcodeg internal_number_to_opmacro_g(int32 i)
918 return opmacros_table_g[i];
921 static void make_opcode_syntax_g(opcodeg opco)
925 char *q = opcode_syntax_string;
926 /* TODO: opcode_syntax_string[128] is unsafe */
928 sprintf(q, "%s", opco.name);
929 sprintf(q+strlen(q), " <%d operand%s", opco.no,
930 ((opco.no==1) ? "" : "s"));
935 for (ix=0; ix<opco.no; ix++) {
940 if (ix == opco.no-1) {
941 if (opco.flags & Br) {
944 else if (opco.flags & St) {
951 else if (ix == opco.no-2 && (opco.flags & Br) && (opco.flags & St)) {
954 else if (ix == opco.no-2 && (opco.flags & St2)) {
961 sprintf(cx, "%d", ix+1);
965 sprintf(q+strlen(q), ">");
969 /* ========================================================================= */
970 /* The assembler itself does four things: */
972 /* assembles instructions */
973 /* sets label N to the current code position */
974 /* assembles routine headers */
975 /* assembles routine ends */
976 /* ------------------------------------------------------------------------- */
978 /* This is for Z-code only. */
979 static void write_operand(assembly_operand op)
983 { case LONG_CONSTANT_OT:
984 byteout(j/256, op.marker); byteout(j%256, 0); return;
985 case SHORT_CONSTANT_OT:
988 else byteout(j, 0x80 + op.marker); return;
990 byteout(j, 0); return;
992 case HALFCONSTANT_OT:
993 case BYTECONSTANT_OT:
994 case ZEROCONSTANT_OT:
999 compiler_error("Glulx OT in Z-code assembly operand.");
1004 extern void assemblez_instruction(const assembly_instruction *AI)
1008 int32 offset, j, topbits=0, types_byte1, types_byte2;
1009 int operand_rules, min=0, max=0, no_operands_given, at_seq_point = FALSE;
1010 assembly_operand o1, o2;
1015 if (execution_never_reaches_here) {
1016 if (!(execution_never_reaches_here & EXECSTATE_NOWARN)) {
1017 warning("This statement can never be reached");
1018 /* only show the warning once */
1019 execution_never_reaches_here |= EXECSTATE_NOWARN;
1024 offset = zmachine_pc;
1028 if (veneer_mode) sequence_point_follows = FALSE;
1029 if (sequence_point_follows)
1030 { sequence_point_follows = FALSE; at_seq_point = TRUE;
1031 if (debugfile_switch)
1033 ensure_memory_list_available(&sequence_points_memlist, next_sequence_point+1);
1034 sequence_points[next_sequence_point].label = next_label;
1035 sequence_points[next_sequence_point].location =
1036 statement_debug_location;
1037 set_label_offset(next_label++, zmachine_pc);
1039 next_sequence_point++;
1042 opco = internal_number_to_opcode_z(AI->internal_number);
1043 if (opco.version1==0)
1044 { error_named("Opcode unavailable in this Z-machine version",
1045 opcode_names.keywords[AI->internal_number]);
1049 operand_rules = opco.op_rules;
1050 execution_never_reaches_here = ((opco.flags & Rf) ? EXECSTATE_UNREACHABLE : EXECSTATE_REACHABLE);
1052 if (opco.flags2_set != 0) flags2_requirements[opco.flags2_set] = 1;
1054 no_operands_given = AI->operand_count;
1056 if ((opco.no == TWO) && ((no_operands_given==3)||(no_operands_given==4)))
1059 /* 1. Write the opcode byte(s) */
1061 start_pc = zcode_ha_size;
1064 { case VAR_LONG: topbits=0xc0; min=0; max=8; break;
1065 case VAR: topbits=0xc0; min=0; max=4; break;
1066 case ZERO: topbits=0xb0; min=0; max=0; break;
1067 case ONE: topbits=0x80; min=1; max=1; break;
1068 case TWO: topbits=0x00; min=2; max=2; break;
1069 case EXT: topbits=0x00; min=0; max=4;
1070 byteout(0xbe, 0); opco.no=VAR; break;
1071 case EXT_LONG: topbits=0x00; min=0; max=8;
1072 byteout(0xbe, 0); opco.no=VAR_LONG; break;
1074 byteout(opco.code + topbits, 0);
1076 operands_pc = zcode_ha_size;
1078 /* 2. Dispose of the special rules LABEL and TEXT */
1080 if (operand_rules==LABEL)
1081 { j = (AI->operand[0]).value;
1083 byteout(j/256, LABEL_MV); byteout(j%256, 0);
1084 goto Instruction_Done;
1087 if (operand_rules==TEXT)
1089 j = translate_text(-1, AI->text, STRCTX_GAMEOPC);
1091 error("text translation failed");
1094 ensure_memory_list_available(&zcode_markers_memlist, zcode_ha_size+j);
1095 ensure_memory_list_available(&zcode_holding_area_memlist, zcode_ha_size+j);
1096 for (i=0; i<j; i++) {
1097 zcode_holding_area[zcode_ha_size] = translated_text[i];
1098 zcode_markers[zcode_ha_size] = 0;
1102 goto Instruction_Done;
1105 /* 3. Sort out the operands */
1107 if ((no_operands_given < min) || (no_operands_given > max))
1108 goto OpcodeSyntaxError;
1114 if (opco.no == VAR_LONG) byteout(0, 0);
1115 types_byte1=0xff; types_byte2=0xff;
1116 for (j=0; j<no_operands_given; j++)
1117 { int multi=0, mask=0;
1119 { case 0: case 4: multi=0x40; mask=0xc0; break;
1120 case 1: case 5: multi=0x10; mask=0x30; break;
1121 case 2: case 6: multi=0x04; mask=0x0c; break;
1122 case 3: case 7: multi=0x01; mask=0x03; break;
1124 o1 = AI->operand[j];
1127 types_byte1 = (types_byte1 & (~mask)) + o1.type*multi;
1129 types_byte2 = (types_byte2 & (~mask)) + o1.type*multi;
1131 zcode_holding_area[operands_pc]=types_byte1;
1132 if (opco.no == VAR_LONG) zcode_holding_area[operands_pc+1]=types_byte2;
1136 o1 = AI->operand[0];
1137 zcode_holding_area[start_pc] += o1.type*0x10;
1142 o1 = AI->operand[0];
1143 o2 = AI->operand[1];
1145 /* Transfer to VAR form if either operand is a long constant */
1147 if ((o1.type==LONG_CONSTANT_OT)||(o2.type==LONG_CONSTANT_OT))
1148 { zcode_holding_area[start_pc] += 0xc0;
1149 byteout(o1.type*0x40 + o2.type*0x10 + 0x0f, 0);
1152 { if (o1.type==VARIABLE_OT) zcode_holding_area[start_pc] += 0x40;
1153 if (o2.type==VARIABLE_OT) zcode_holding_area[start_pc] += 0x20;
1160 /* 4. Assemble a Store destination, if needed */
1162 if ((AI->store_variable_number) != -1)
1163 { if (AI->store_variable_number >= MAX_LOCAL_VARIABLES+MAX_ZCODE_GLOBAL_VARS) {
1164 goto OpcodeSyntaxError;
1166 o1.type = VARIABLE_OT;
1167 o1.value = AI->store_variable_number;
1168 variables[o1.value].usage = TRUE;
1171 /* Note that variable numbers 249 to 255 (i.e. globals 233 to 239)
1172 are used as scratch workspace, so need no mapping between
1173 modules and story files: nor do local variables 0 to 15.
1174 (Modules no longer exist but why drop a good comment.) */
1176 if ((o1.value >= MAX_LOCAL_VARIABLES) && (o1.value < 249))
1177 o1.marker = VARIABLE_MV;
1181 /* 5. Assemble a branch, if needed */
1183 if (AI->branch_label_number != -1)
1184 { int32 addr, long_form;
1185 int branch_on_true = (AI->branch_flag)?1:0;
1186 mark_label_used(AI->branch_label_number);
1187 switch (AI->branch_label_number)
1188 { case -2: addr = 2; branch_on_true = 0; long_form = 0; break;
1189 /* branch nowhere, carry on */
1190 case -3: addr = 0; long_form = 0; break; /* rfalse on condition */
1191 case -4: addr = 1; long_form = 0; break; /* rtrue on condition */
1193 long_form = 1; addr = AI->branch_label_number;
1196 if (addr > 0x7fff) fatalerror("Too many branch points in routine.");
1198 { byteout(branch_on_true*0x80 + addr/256, BRANCH_MV);
1199 byteout(addr%256, 0);
1202 byteout(branch_on_true*0x80+ 0x40 + (addr&0x3f), 0);
1207 if (asm_trace_level > 0)
1209 printf("%5d +%05lx %3s %-12s ", ErrorReport.line_number,
1210 ((long int) offset),
1211 (at_seq_point)?"<*>":" ", opco.name);
1213 if ((AI->internal_number == print_zc)
1214 || (AI->internal_number == print_ret_zc))
1216 for (i=0;(AI->text)[i]!=0 && i<35; i++) printf("%c",(AI->text)[i]);
1217 if (i == 35) printf("...");
1221 for (i=0; i<AI->operand_count; i++)
1222 { if ((i==0) && (opco.op_rules == VARIAB))
1223 { if ((AI->operand[0]).type == VARIABLE_OT)
1224 { printf("["); print_operand_z(&AI->operand[i], TRUE); }
1226 printf("%s", variable_name((AI->operand[0]).value));
1229 if ((i==0) && (opco.op_rules == LABEL))
1230 { printf("L%d", AI->operand[0].value);
1232 else print_operand_z(&AI->operand[i], TRUE);
1235 if (AI->store_variable_number != -1)
1236 { assembly_operand AO;
1238 AO.type = VARIABLE_OT; AO.value = AI->store_variable_number;
1239 print_operand_z(&AO, TRUE); printf(" ");
1242 switch(AI->branch_label_number)
1243 { case -4: printf("rtrue if %s", (AI->branch_flag)?"TRUE":"FALSE");
1245 case -3: printf("rfalse if %s", (AI->branch_flag)?"TRUE":"FALSE");
1247 case -2: printf("(no branch)"); break;
1250 printf("to L%d if %s", AI->branch_label_number,
1251 (AI->branch_flag)?"TRUE":"FALSE"); break;
1254 if (asm_trace_level>=2)
1255 { for (j=0;start_pc<zcode_ha_size;
1257 { if (j%16==0) printf("\n ");
1258 if (zcode_markers[start_pc] & 0x7f)
1259 printf("{%s}", describe_mv_short(zcode_markers[start_pc] & 0x7f));
1260 printf("%02x ", zcode_holding_area[start_pc]);
1270 make_opcode_syntax_z(opco);
1271 error_named("Assembly mistake: syntax is", opcode_syntax_string);
1274 static void assembleg_macro(const assembly_instruction *AI)
1276 int ix, no_operands_given;
1278 assembly_operand AMO_0, AMO_1, AMO_2;
1280 /* validate macro syntax first */
1282 opco = internal_number_to_opmacro_g(AI->internal_number);
1283 no_operands_given = AI->operand_count;
1285 if (no_operands_given != opco.no)
1286 goto OpcodeSyntaxError;
1288 for (ix = 0; ix < no_operands_given; ix++) {
1289 int type = AI->operand[ix].type;
1290 if ((opco.flags & St)
1291 && ((!(opco.flags & Br) && (ix == no_operands_given-1))
1292 || ((opco.flags & Br) && (ix == no_operands_given-2)))) {
1293 if (is_constant_ot(type)) {
1294 error("*** assembly macro tried to store to a constant ***");
1295 goto OpcodeSyntaxError;
1298 if ((opco.flags & St2)
1299 && (ix == no_operands_given-2)) {
1300 if (is_constant_ot(type)) {
1301 error("*** assembly macro tried to store to a constant ***");
1302 goto OpcodeSyntaxError;
1307 /* Expand the macro.
1308 The assembleg_() functions overwrite AI, so we need to copy out
1309 its operands before we call them. */
1311 switch (opco.code) {
1312 case pull_gm: /* @pull STORE */
1313 AMO_0 = AI->operand[0];
1314 assembleg_store(AMO_0, stack_pointer);
1317 case push_gm: /* @push LOAD */
1318 AMO_0 = AI->operand[0];
1319 assembleg_store(stack_pointer, AMO_0);
1322 case dload_gm: /* @dload LOAD STORELO STOREHI */
1323 AMO_0 = AI->operand[0];
1324 AMO_1 = AI->operand[1];
1325 AMO_2 = AI->operand[2];
1326 if ((AMO_0.type == LOCALVAR_OT) && (AMO_0.value == 0)) {
1327 /* addr is on the stack */
1328 assembleg_store(temp_var3, stack_pointer);
1329 assembleg_3(aload_gc, temp_var3, one_operand, AMO_1);
1330 assembleg_3(aload_gc, temp_var3, zero_operand, AMO_2);
1333 assembleg_3(aload_gc, AMO_0, one_operand, AMO_1);
1334 assembleg_3(aload_gc, AMO_0, zero_operand, AMO_2);
1338 case dstore_gm: /* @dload LOAD LOADHI LOADLO */
1339 AMO_0 = AI->operand[0];
1340 AMO_1 = AI->operand[1];
1341 AMO_2 = AI->operand[2];
1342 if ((AMO_0.type == LOCALVAR_OT) && (AMO_0.value == 0)) {
1343 /* addr is on the stack */
1344 assembleg_store(temp_var3, stack_pointer);
1345 assembleg_3(astore_gc, temp_var3, zero_operand, AMO_1);
1346 assembleg_3(astore_gc, temp_var3, one_operand, AMO_2);
1349 assembleg_3(astore_gc, AMO_0, zero_operand, AMO_1);
1350 assembleg_3(astore_gc, AMO_0, one_operand, AMO_2);
1355 compiler_error("Invalid Glulx assembly macro");
1363 make_opcode_syntax_g(opco);
1364 error_named("Assembly mistake: syntax is", opcode_syntax_string);
1367 extern void assembleg_instruction(const assembly_instruction *AI)
1372 int no_operands_given, at_seq_point = FALSE;
1378 if (execution_never_reaches_here) {
1379 if (!(execution_never_reaches_here & EXECSTATE_NOWARN)) {
1380 warning("This statement can never be reached");
1381 /* only show the warning once */
1382 execution_never_reaches_here |= EXECSTATE_NOWARN;
1387 offset = zmachine_pc;
1391 if (veneer_mode) sequence_point_follows = FALSE;
1392 if (sequence_point_follows)
1393 { sequence_point_follows = FALSE; at_seq_point = TRUE;
1394 if (debugfile_switch)
1396 ensure_memory_list_available(&sequence_points_memlist, next_sequence_point+1);
1397 sequence_points[next_sequence_point].label = next_label;
1398 sequence_points[next_sequence_point].location =
1399 statement_debug_location;
1400 set_label_offset(next_label++, zmachine_pc);
1402 next_sequence_point++;
1405 opco = internal_number_to_opcode_g(AI->internal_number);
1407 execution_never_reaches_here = ((opco.flags & Rf) ? EXECSTATE_UNREACHABLE : EXECSTATE_REACHABLE);
1409 if (opco.op_rules & GOP_Unicode) {
1410 uses_unicode_features = TRUE;
1412 if (opco.op_rules & GOP_MemHeap) {
1413 uses_memheap_features = TRUE;
1415 if (opco.op_rules & GOP_Acceleration) {
1416 uses_acceleration_features = TRUE;
1418 if (opco.op_rules & GOP_Float) {
1419 uses_float_features = TRUE;
1421 if (opco.op_rules & GOP_ExtUndo) {
1422 uses_extundo_features = TRUE;
1424 if (opco.op_rules & GOP_Double) {
1425 uses_double_features = TRUE;
1428 no_operands_given = AI->operand_count;
1430 /* 1. Write the opcode byte(s) */
1432 start_pc = zcode_ha_size;
1434 if (opco.code < 0x80) {
1435 byteout(opco.code, 0);
1437 else if (opco.code < 0x4000) {
1438 byteout(((opco.code >> 8) & 0xFF) | 0x80, 0);
1439 byteout((opco.code & 0xFF), 0);
1442 byteout(((opco.code >> 24) & 0xFF) | 0xC0, 0);
1443 byteout(((opco.code >> 16) & 0xFF), 0);
1444 byteout(((opco.code >> 8) & 0xFF), 0);
1445 byteout(((opco.code) & 0xFF), 0);
1448 /* ... and the operand addressing modes. There's one byte for
1449 every two operands (rounded up). We write zeroes for now;
1450 when the operands are written, we'll go back and fix them. */
1452 opmodes_pc = zcode_ha_size;
1454 for (ix=0; ix<opco.no; ix+=2) {
1458 /* 2. Dispose of the special rules */
1459 /* There aren't any in Glulx. */
1461 /* 3. Sort out the operands */
1463 if (no_operands_given != opco.no) {
1464 goto OpcodeSyntaxError;
1467 for (ix=0; ix<no_operands_given; ix++) {
1468 int marker = AI->operand[ix].marker;
1469 int type = AI->operand[ix].type;
1470 k = AI->operand[ix].value;
1472 if ((opco.flags & Br) && (ix == no_operands_given-1)) {
1473 if (!(marker >= BRANCH_MV && marker < BRANCHMAX_MV)) {
1474 compiler_error("Assembling branch without BRANCH_MV marker");
1475 goto OpcodeSyntaxError;
1479 k = 2; /* branch no-op */
1480 type = BYTECONSTANT_OT;
1484 k = 0; /* branch return 0 */
1485 type = ZEROCONSTANT_OT;
1489 k = 1; /* branch return 1 */
1490 type = BYTECONSTANT_OT;
1494 /* branch to label k */
1495 j = (zcode_ha_size - opmodes_pc);
1497 marker = BRANCH_MV + j;
1498 if (!(marker >= BRANCH_MV && marker < BRANCHMAX_MV)) {
1499 error("*** branch marker too far from opmode byte ***");
1500 goto OpcodeSyntaxError;
1504 if ((opco.flags & St)
1505 && ((!(opco.flags & Br) && (ix == no_operands_given-1))
1506 || ((opco.flags & Br) && (ix == no_operands_given-2)))) {
1507 if (type == BYTECONSTANT_OT || type == HALFCONSTANT_OT
1508 || type == CONSTANT_OT) {
1509 error("*** instruction tried to store to a constant ***");
1510 goto OpcodeSyntaxError;
1513 if ((opco.flags & St2)
1514 && (ix == no_operands_given-2)) {
1515 if (type == BYTECONSTANT_OT || type == HALFCONSTANT_OT
1516 || type == CONSTANT_OT) {
1517 error("*** instruction tried to store to a constant ***");
1518 goto OpcodeSyntaxError;
1522 if (marker && (type == HALFCONSTANT_OT
1523 || type == BYTECONSTANT_OT
1524 || type == ZEROCONSTANT_OT)) {
1525 compiler_error("Assembling marker in less than 32-bit constant.");
1526 /* Actually we should store marker|0x80 for a byte constant,
1527 but let's hold off on that. */
1531 case LONG_CONSTANT_OT:
1532 case SHORT_CONSTANT_OT:
1535 compiler_error("Z-code OT in Glulx assembly operand.");
1539 byteout((k >> 24) & 0xFF, marker);
1540 byteout((k >> 16) & 0xFF, 0);
1541 byteout((k >> 8) & 0xFF, 0);
1542 byteout((k & 0xFF), 0);
1544 case HALFCONSTANT_OT:
1546 byteout((k >> 8) & 0xFF, marker);
1547 byteout((k & 0xFF), 0);
1549 case BYTECONSTANT_OT:
1551 byteout((k & 0xFF), marker);
1553 case ZEROCONSTANT_OT:
1556 case DEREFERENCE_OT:
1558 byteout((k >> 24) & 0xFF, marker);
1559 byteout((k >> 16) & 0xFF, 0);
1560 byteout((k >> 8) & 0xFF, 0);
1561 byteout((k & 0xFF), 0);
1564 /* Global variable -- a constant address. */
1565 k -= MAX_LOCAL_VARIABLES;
1566 if (/* DISABLES CODE */ (0)) {
1567 /* We could write the value as a marker and patch it later... */
1569 byteout(((k) >> 24) & 0xFF, VARIABLE_MV);
1570 byteout(((k) >> 16) & 0xFF, 0);
1571 byteout(((k) >> 8) & 0xFF, 0);
1572 byteout(((k) & 0xFF), 0);
1575 /* ...but it's more efficient to write it as a RAM operand,
1576 which can be 1, 2, or 4 bytes. Remember that global variables
1577 are the very first thing in RAM. */
1578 k = k * 4; /* each variable is four bytes */
1581 byteout(((k) & 0xFF), 0);
1583 else if (k <= 65535) {
1585 byteout(((k) >> 8) & 0xFF, 0);
1586 byteout(((k) & 0xFF), 0);
1590 byteout(((k) >> 24) & 0xFF, 0);
1591 byteout(((k) >> 16) & 0xFF, 0);
1592 byteout(((k) >> 8) & 0xFF, 0);
1593 byteout(((k) & 0xFF), 0);
1599 /* Stack-pointer magic variable */
1603 /* Local variable -- a byte or short offset from the
1604 frame pointer. It's an unsigned offset, so we can
1605 fit up to long 63 (offset 4*63) in a byte. */
1608 byteout((k-1)*4, 0);
1612 byteout((((k-1)*4) >> 8) & 0xFF, 0);
1613 byteout(((k-1)*4) & 0xFF, 0);
1624 zcode_holding_area[opmodes_pc+ix/2] |= j;
1627 /* Print assembly trace. */
1628 if (asm_trace_level > 0) {
1630 printf("%5d +%05lx %3s %-12s ", ErrorReport.line_number,
1631 ((long int) offset),
1632 (at_seq_point)?"<*>":" ", opco.name);
1633 for (i=0; i<AI->operand_count; i++) {
1634 if ((opco.flags & Br) && (i == opco.no-1)) {
1635 if (AI->operand[i].value == -4)
1637 else if (AI->operand[i].value == -3)
1638 printf("to rfalse");
1640 printf("to L%d", AI->operand[i].value);
1643 print_operand_g(&AI->operand[i], TRUE);
1648 if (asm_trace_level>=2) {
1650 start_pc<zcode_ha_size;
1652 if (j%16==0) printf("\n ");
1653 if (/* DISABLES CODE */ (0)) {
1654 printf("%02x ", zcode_holding_area[start_pc]);
1657 if (zcode_markers[start_pc])
1658 printf("{%s}", describe_mv_short(zcode_markers[start_pc]));
1659 printf("%02x", zcode_holding_area[start_pc]);
1671 make_opcode_syntax_g(opco);
1672 error_named("Assembly mistake: syntax is", opcode_syntax_string);
1675 /* Set up this label at zmachine_pc.
1676 This resets the execution_never_reaches_here flag, since every label
1677 is assumed to be reachable.
1678 However, if STRIP_UNREACHABLE_LABELS and EXECSTATE_ENTIRE are both set,
1679 that's not true. The entire statement is being skipped, so we can safely
1680 skip all unused labels within it.
1681 ("Unused" meaning there are no forward jumps to the label. We can't
1682 do anything about *backward* jumps because we haven't seen them yet!)
1683 (If STRIP_UNREACHABLE_LABELS is not set, the ENTIRE flag is ignored.)
1685 extern void assemble_label_no(int n)
1687 int inuse = (n >= 0 && n < labeluse_size && labeluse[n]);
1689 if ((!inuse) && (execution_never_reaches_here & EXECSTATE_ENTIRE) && STRIP_UNREACHABLE_LABELS) {
1690 /* We're not going to compile this label at all. Set a negative
1691 offset, which will trip an error if this label is jumped to. */
1692 set_label_offset(n, -1);
1696 if (asm_trace_level > 0)
1697 printf("%5d +%05lx .L%d\n", ErrorReport.line_number,
1698 ((long int) zmachine_pc), n);
1699 set_label_offset(n, zmachine_pc);
1700 execution_never_reaches_here = EXECSTATE_REACHABLE;
1703 /* This is the same as assemble_label_no, except we only set up the label
1704 if there has been a forward branch to it.
1705 Returns whether the label is created.
1706 Only use this for labels which never have backwards branches!
1708 extern int assemble_forward_label_no(int n)
1710 if (n >= 0 && n < labeluse_size && labeluse[n]) {
1711 assemble_label_no(n);
1715 /* There were no forward branches to this label and we promise
1716 there will be no backwards branches to it. Set a negative
1717 offset, which will trip an error if we break our promise. */
1718 set_label_offset(n, -1);
1723 extern void define_symbol_label(int symbol)
1725 int label = symbols[symbol].value;
1726 /* We may be creating a new label (label = next_label) or filling in
1727 the value of an old one. So we call ensure. */
1728 ensure_memory_list_available(&labels_memlist, label+1);
1729 labels[label].symbol = symbol;
1732 /* The local variables must already be set up; no_locals indicates
1734 extern int32 assemble_routine_header(int routine_asterisked, char *name,
1735 int embedded_flag, int the_symbol)
1737 int stackargs = FALSE;
1740 execution_never_reaches_here = EXECSTATE_REACHABLE;
1742 ensure_memory_list_available(&variables_memlist, MAX_LOCAL_VARIABLES);
1743 for (i=0; i<MAX_LOCAL_VARIABLES; i++) variables[i].usage = FALSE;
1746 && strcmpcis(get_local_variable_name(0), "_vararg_count")==0) {
1750 if (veneer_mode) routine_starts_line = blank_brief_location;
1751 else routine_starts_line = get_brief_location(&ErrorReport);
1753 if (asm_trace_level > 0)
1754 { printf("\n%5d +%05lx [ %s ", ErrorReport.line_number,
1755 ((long int) zmachine_pc), name);
1756 for (i=1; i<=no_locals; i++) printf("%s ", variable_name(i));
1760 routine_start_pc = zmachine_pc;
1762 if (track_unused_routines) {
1763 /* The name of an embedded function is in a temporary buffer,
1764 so we shouldn't keep a reference to it. (It is sad that we
1765 have to know this here.) */
1766 char *funcname = name;
1768 funcname = "<embedded>";
1770 df_note_function_start(funcname, zmachine_pc, embedded_flag,
1771 routine_starts_line);
1774 routine_symbol = the_symbol;
1775 name_length = strlen(name) + 1;
1776 ensure_memory_list_available(¤t_routine_name, name_length);
1777 strncpy(current_routine_name.data, name, name_length);
1779 /* Update the routine counter */
1783 /* Actually assemble the routine header into the code area; note */
1784 /* Inform doesn't support the setting of local variables to default */
1785 /* values other than 0 in V3 and V4. (In V5+ the Z-Machine doesn't */
1786 /* provide the possibility in any case.) */
1791 warning("Z-code does not support stack-argument function definitions.");
1793 byteout(no_locals, 0);
1795 /* Not the packed address, but the scaled offset from code area start: */
1797 rv = zmachine_pc/scale_factor;
1799 if (instruction_set_number<5)
1800 for (i=0; i<no_locals; i++) { byteout(0,0); byteout(0,0); }
1802 next_label = 0; next_sequence_point = 0; last_label = -1;
1805 /* Compile code to print out text like "a=3, b=4, c=5" when the */
1806 /* function is called, if it's required. */
1808 if ((routine_asterisked) || (define_INFIX_switch))
1809 { char fnt[256]; assembly_operand PV, RFA, CON, STP, SLF; int ln, ln2;
1810 /* TODO: fnt[256] is unsafe */
1815 if (define_INFIX_switch)
1818 { SLF.value = 251; SLF.type = VARIABLE_OT; SLF.marker = 0;
1819 CON.value = 0; CON.type = SHORT_CONSTANT_OT; CON.marker = 0;
1820 assemblez_2_branch(test_attr_zc, SLF, CON, ln2, FALSE);
1823 { i = no_named_routines++;
1824 ensure_memory_list_available(&named_routine_symbols_memlist, no_named_routines);
1825 named_routine_symbols[i] = the_symbol;
1826 CON.value = i/8; CON.type = LONG_CONSTANT_OT; CON.marker = 0;
1827 RFA.value = routine_flags_array_SC;
1828 RFA.type = LONG_CONSTANT_OT; RFA.marker = INCON_MV;
1829 STP.value = 0; STP.type = VARIABLE_OT; STP.marker = 0;
1830 assemblez_2_to(loadb_zc, RFA, CON, STP);
1831 CON.value = (1 << (i%8)); CON.type = SHORT_CONSTANT_OT;
1832 assemblez_2_to(and_zc, STP, CON, STP);
1833 assemblez_1_branch(jz_zc, STP, ln2, TRUE);
1836 sprintf(fnt, "[ %s(", name);
1837 AI.text = fnt; assemblez_0(print_zc);
1838 for (i=1; (i<=7)&&(i<=no_locals); i++)
1839 { if (version_number >= 5)
1840 { PV.type = SHORT_CONSTANT_OT;
1841 PV.value = i; PV.marker = 0;
1842 assemblez_1_branch(check_arg_count_zc, PV, ln, FALSE);
1844 sprintf(fnt, "%s%s = ", (i==1)?"":", ", variable_name(i));
1845 AI.text = fnt; assemblez_0(print_zc);
1846 PV.type = VARIABLE_OT; PV.value = i; PV.marker = 0;
1847 assemblez_1(print_num_zc, PV);
1849 assemble_label_no(ln);
1850 sprintf(fnt, ") ]^"); AI.text = fnt;
1851 assemblez_0(print_zc);
1853 assemble_label_no(ln2);
1861 byteout(0xC0, 0); /* Glulx type byte for function */
1863 byteout(0xC1, 0); /* Glulx type byte for function */
1865 /* Now the locals format list. This is simple; we only use
1866 four-byte locals. That's a single pair, unless we have more
1867 than 255 locals, or none at all. */
1877 /* Terminate the list with a (0, 0) pair. */
1882 /* The top stack value is the number of function arguments. Let's
1883 move that into the first local, which is _vararg_count. */
1884 /* @copy sp _vararg_count; */
1885 byteout(0x40, 0); byteout(0x98, 0); byteout(0x00, 0);
1888 next_label = 0; next_sequence_point = 0; last_label = -1;
1891 if ((routine_asterisked) || (define_INFIX_switch)) {
1894 assembly_operand AO, AO2;
1895 if (define_INFIX_switch) {
1896 /* This isn't supported */
1897 if (embedded_flag) {
1900 i = no_named_routines++;
1901 ensure_memory_list_available(&named_routine_symbols_memlist, no_named_routines);
1902 named_routine_symbols[i] = the_symbol;
1905 sprintf(fnt, "[ %s(", name);
1906 AO.marker = STRING_MV;
1907 AO.type = CONSTANT_OT;
1908 AO.value = compile_string(fnt, STRCTX_INFIX);
1909 assembleg_1(streamstr_gc, AO);
1912 for (ix=1; ix<=no_locals; ix++) {
1913 sprintf(fnt, "%s%s = ", (ix==1)?"":", ", variable_name(ix));
1914 AO.marker = STRING_MV;
1915 AO.type = CONSTANT_OT;
1916 AO.value = compile_string(fnt, STRCTX_INFIX);
1917 assembleg_1(streamstr_gc, AO);
1919 AO.type = LOCALVAR_OT;
1921 assembleg_1(streamnum_gc, AO);
1925 int lntop, lnbottom;
1926 sprintf(fnt, "%s = ", variable_name(1));
1927 AO.marker = STRING_MV;
1928 AO.type = CONSTANT_OT;
1929 AO.value = compile_string(fnt, STRCTX_INFIX);
1930 assembleg_1(streamstr_gc, AO);
1932 AO.type = LOCALVAR_OT;
1934 assembleg_1(streamnum_gc, AO);
1935 AO2.type = BYTECONSTANT_OT;
1938 assembleg_1(streamchar_gc, AO2);
1939 AO2.type = BYTECONSTANT_OT;
1942 /* for (temp_var4=0 : temp_var4<_vararg_count : temp_var4++) {
1944 @stkpeek temp_var4 sp;
1948 assembleg_store(temp_var4, zero_operand);
1949 lntop = next_label++;
1950 lnbottom = next_label++;
1951 assemble_label_no(lntop);
1952 assembleg_2_branch(jge_gc, temp_var4, AO, lnbottom); /* AO is _vararg_count */
1953 assembleg_1(streamchar_gc, AO2); /* AO2 is space */
1954 assembleg_2(stkpeek_gc, temp_var4, stack_pointer);
1955 assembleg_1(streamnum_gc, stack_pointer);
1956 assembleg_3(add_gc, temp_var4, one_operand, temp_var4);
1957 assembleg_0_branch(jump_gc, lntop);
1958 assemble_label_no(lnbottom);
1961 AO.marker = STRING_MV;
1962 AO.type = CONSTANT_OT;
1963 AO.value = compile_string(") ]^", STRCTX_INFIX);
1964 assembleg_1(streamstr_gc, AO);
1971 void assemble_routine_end(int embedded_flag, debug_locations locations)
1974 /* No marker is made in the Z-machine's code area to indicate the */
1975 /* end of a routine. Instead, we simply assemble a return opcode if */
1976 /* need be (it won't be if the last instruction was, say, a "quit"). */
1977 /* The return value is true (1) for normal routines, false (0) for */
1978 /* embedded routines (e.g. the library uses this for "before" */
1981 if (!execution_never_reaches_here)
1984 if (embedded_flag) assemblez_0(rfalse_zc);
1985 else assemblez_0(rtrue_zc);
1988 assembly_operand AO;
1993 assembleg_1(return_gc, AO);
1997 /* Dump the contents of the current routine into longer-term Z-code
2001 transfer_routine_z();
2003 transfer_routine_g();
2005 if (track_unused_routines)
2006 df_note_function_end(zmachine_pc);
2008 /* Tell the debugging file about the routine just ended. */
2010 if (debugfile_switch)
2012 char *routine_name = current_routine_name.data;
2013 debug_file_printf("<routine>");
2016 ("<identifier artificial=\"true\">%s</identifier>",
2019 else if (symbols[routine_symbol].flags & REPLACE_SFLAG)
2020 { /* The symbol type will be set to ROUTINE_T once the replaced
2021 version has been given; if it is already set, we must be dealing
2022 with a replacement, and we can use the routine name as-is.
2023 Otherwise we look for a rename. And if that doesn't work, we
2024 fall back to an artificial identifier. */
2025 if (symbols[routine_symbol].type == ROUTINE_T)
2026 { /* Optional because there may be further replacements. */
2027 write_debug_optional_identifier(routine_symbol);
2029 else if (find_symbol_replacement(&routine_symbol))
2031 ("<identifier>%s</identifier>", symbols[routine_symbol].name);
2035 ("<identifier artificial=\"true\">%s (replaced)"
2040 { debug_file_printf("<identifier>%s</identifier>", routine_name);
2042 debug_file_printf("<value>");
2044 { write_debug_code_backpatch(routine_start_pc);
2046 { write_debug_packed_code_backpatch(routine_start_pc);
2048 debug_file_printf("</value>");
2049 debug_file_printf("<address>");
2050 write_debug_code_backpatch(routine_start_pc);
2051 debug_file_printf("</address>");
2053 ("<byte-count>%d</byte-count>", zmachine_pc - routine_start_pc);
2054 write_debug_locations(locations);
2055 for (i = 1; i <= no_locals; ++i)
2056 { debug_file_printf("<local-variable>");
2057 debug_file_printf("<identifier>%s</identifier>", variable_name(i));
2060 ("<frame-offset>%d</frame-offset>", 4 * (i - 1));
2063 { debug_file_printf("<index>%d</index>", i);
2065 debug_file_printf("</local-variable>");
2067 for (i = 0; i < next_sequence_point; ++i)
2068 { debug_file_printf("<sequence-point>");
2069 debug_file_printf("<address>");
2070 write_debug_code_backpatch
2071 (labels[sequence_points[i].label].offset);
2072 debug_file_printf("</address>");
2073 write_debug_location(sequence_points[i].location);
2074 debug_file_printf("</sequence-point>");
2076 debug_file_printf("</routine>");
2079 /* Issue warnings about any local variables not used in the routine. */
2081 for (i=1; i<=no_locals; i++)
2082 if (!(variables[i].usage))
2083 dbnu_warning("Local variable", variable_name(i),
2084 routine_starts_line);
2086 for (i=0; i<next_label; i++)
2087 { int j = labels[i].symbol;
2089 { if (symbols[j].flags & CHANGE_SFLAG)
2090 error_named_at("Routine contains no such label as",
2091 symbols[j].name, symbols[j].line);
2093 if ((symbols[j].flags & USED_SFLAG) == 0)
2094 dbnu_warning("Label", symbols[j].name, symbols[j].line);
2095 symbols[j].type = CONSTANT_T;
2096 symbols[j].flags = UNKNOWN_SFLAG;
2099 no_sequence_points += next_sequence_point;
2100 next_label = 0; next_sequence_point = 0;
2102 execution_never_reaches_here = EXECSTATE_REACHABLE;
2105 /* ------------------------------------------------------------------------- */
2106 /* Called when the holding area contains an entire routine of code: */
2107 /* backpatches the labels, issues module markers, then dumps the routine */
2108 /* into longer-term storage. */
2110 /* Note that in the code received, all branches have long form, and their */
2111 /* contents are not an offset but the label numbers they branch to. */
2112 /* Similarly, LABEL operands (those of "jump" instructions) are label */
2113 /* numbers. So this routine must change the label numbers to offsets, */
2114 /* slimming the code down as it does so to take advantage of short-form */
2115 /* branch operands where possible. */
2117 /* zcode_ha_size is the number of bytes added since the last transfer */
2118 /* call. So we transfer starting at (zmachine_pc - zcode_ha_size). But we */
2119 /* might transfer fewer bytes than that. */
2120 /* ------------------------------------------------------------------------- */
2122 static void transfer_routine_z(void)
2123 { int32 i, j, pc, new_pc, label, long_form, offset_of_next, addr,
2124 branch_on_true, rstart_pc;
2127 adjusted_pc = zmachine_pc - zcode_ha_size; rstart_pc = adjusted_pc;
2129 if (asm_trace_level >= 3)
2130 { printf("Backpatching routine at %05lx: initial size %d, %d labels\n",
2131 (long int) adjusted_pc, zcode_ha_size, next_label);
2134 /* (1) Scan through for branches and make short/long decisions in each
2135 case. Mark omitted bytes (2nd bytes in branches converted to
2136 short form) with DELETED_MV.
2137 We also look for jumps that can be entirely eliminated (because
2138 they are jumping to the very next instruction). The opcode and
2139 both label bytes get DELETED_MV. */
2141 for (i=0, pc=adjusted_pc; i<zcode_ha_size; i++, pc++)
2142 { if (zcode_markers[i] == BRANCH_MV)
2143 { if (asm_trace_level >= 4)
2144 printf("Branch detected at offset %04x\n", pc);
2145 j = (256*zcode_holding_area[i] + zcode_holding_area[i+1]) & 0x7fff;
2146 if (asm_trace_level >= 4)
2147 printf("...To label %d, which is %d from here\n",
2148 j, labels[j].offset-pc);
2149 if ((labels[j].offset >= pc+2) && (labels[j].offset < pc+64))
2150 { if (asm_trace_level >= 4) printf("...Using short form\n");
2151 zcode_markers[i+1] = DELETED_MV;
2154 else if (zcode_markers[i] == LABEL_MV)
2156 if (asm_trace_level >= 4)
2157 printf("Jump detected at offset %04x\n", pc);
2158 j = (256*zcode_holding_area[i] + zcode_holding_area[i+1]) & 0x7fff;
2159 if (asm_trace_level >= 4)
2160 printf("...To label %d, which is %d from here\n",
2161 j, labels[j].offset-pc);
2162 if (labels[j].offset-pc == 2 && i >= 1 && zcode_holding_area[i-1] == opcodes_table_z[jump_zc].code+128) {
2163 if (asm_trace_level >= 4) printf("...Deleting jump\n");
2164 zcode_markers[i-1] = DELETED_MV;
2165 zcode_markers[i] = DELETED_MV;
2166 zcode_markers[i+1] = DELETED_MV;
2171 /* (2) Calculate the new positions of the labels. Note that since the
2172 long/short decision was taken on the basis of the old labels,
2173 and since the new labels are slightly closer together because
2174 of branch bytes deleted, there may be a few further branch
2175 optimisations which are possible but which have been missed
2176 (if two labels move inside the "short" range as a result of
2177 a previous optimisation). However, this is acceptably uncommon. */
2180 { if (asm_trace_level >= 4)
2181 { printf("Opening label: %d\n", first_label);
2182 for (i=0;i<next_label;i++)
2183 printf("Label %d offset %04x next -> %d previous -> %d\n",
2184 i, labels[i].offset, labels[i].next, labels[i].prev);
2187 /* label will advance through the linked list as pc increases. */
2188 for (i=0, pc=adjusted_pc, new_pc=adjusted_pc, label = first_label;
2189 i<zcode_ha_size; i++, pc++)
2190 { while ((label != -1) && (labels[label].offset == pc))
2191 { if (asm_trace_level >= 4)
2192 printf("Position of L%d corrected from %04x to %04x\n",
2193 label, labels[label].offset, new_pc);
2194 labels[label].offset = new_pc;
2195 label = labels[label].next;
2197 if (zcode_markers[i] != DELETED_MV) new_pc++;
2201 /* (3) As we are transferring, replace the label numbers in branch
2202 operands with offsets to those labels. Also issue markers, now
2203 that we know where they occur in the final Z-code area. */
2205 ensure_memory_list_available(&zcode_area_memlist, adjusted_pc+zcode_ha_size);
2207 for (i=0, new_pc=adjusted_pc; i<zcode_ha_size; i++)
2208 { switch(zcode_markers[i])
2210 long_form = 1; if (zcode_markers[i+1] == DELETED_MV) long_form = 0;
2212 j = (256*zcode_holding_area[i] + zcode_holding_area[i+1]) & 0x7fff;
2213 branch_on_true = ((zcode_holding_area[i]) & 0x80);
2214 offset_of_next = new_pc + long_form + 1;
2216 if (labels[j].offset < 0) {
2217 char *lname = "(anon)";
2218 if (labels[j].symbol >= 0 && labels[j].symbol < no_symbols)
2219 lname = symbols[labels[j].symbol].name;
2220 error_named("Attempt to jump to an unreachable label", lname);
2224 addr = labels[j].offset - offset_of_next + 2;
2226 if (addr<-0x2000 || addr>0x1fff)
2227 error_fmt("Branch out of range: routine \"%s\" is too large", current_routine_name.data);
2228 if (addr<0) addr+=(int32) 0x10000L;
2232 { zcode_holding_area[i] = branch_on_true + addr/256;
2233 zcode_holding_area[i+1] = addr%256;
2237 { compiler_error("Label out of range for branch");
2238 printf("Addr is %04x\n", addr);
2240 zcode_holding_area[i] = branch_on_true + 0x40 + (addr&0x3f);
2242 zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++;
2246 j = 256*zcode_holding_area[i] + zcode_holding_area[i+1];
2247 if (labels[j].offset < 0) {
2248 char *lname = "(anon)";
2249 if (labels[j].symbol >= 0 && labels[j].symbol < no_symbols)
2250 lname = symbols[labels[j].symbol].name;
2251 error_named("Attempt to jump to an unreachable label", lname);
2255 addr = labels[j].offset - new_pc;
2257 if (addr<-0x8000 || addr>0x7fff)
2258 error_fmt("Jump out of range: routine \"%s\" is too large", current_routine_name.data);
2259 if (addr<0) addr += (int32) 0x10000L;
2260 zcode_holding_area[i] = addr/256;
2261 zcode_holding_area[i+1] = addr%256;
2262 zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++;
2269 switch(zcode_markers[i] & 0x7f)
2270 { case NULL_MV: break;
2271 case ERROR_MV: break;
2278 if ((zcode_markers[i] & 0x7f) > LARGEST_BPATCH_MV)
2279 { compiler_error("Illegal code backpatch value");
2280 printf("Illegal value of %02x at PC = %04x\n",
2281 zcode_markers[i] & 0x7f, new_pc);
2285 if (bpatch_trace_setting >= 2)
2286 printf("BP added: MV %d PC %04x\n", zcode_markers[i], new_pc);
2288 ensure_memory_list_available(&zcode_backpatch_table_memlist, zcode_backpatch_size+3);
2289 zcode_backpatch_table[zcode_backpatch_size++] = zcode_markers[i] + 32*(new_pc/65536);
2290 zcode_backpatch_table[zcode_backpatch_size++] = (new_pc/256)%256;
2291 zcode_backpatch_table[zcode_backpatch_size++] = new_pc%256;
2294 zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++;
2299 /* Consistency check */
2300 if (new_pc - rstart_pc > zcode_ha_size || adjusted_pc != new_pc)
2302 fatalerror("Optimisation increased routine length or failed to match; should not happen");
2305 if (asm_trace_level >= 3)
2306 { printf("After branch optimisation, routine length is %d bytes\n",
2307 new_pc - rstart_pc);
2310 /* Insert null bytes if necessary to ensure the next routine address is */
2311 /* expressible as a packed address */
2313 ensure_memory_list_available(&zcode_area_memlist, adjusted_pc+2*scale_factor);
2315 if (oddeven_packing_switch)
2316 while ((adjusted_pc%(scale_factor*2))!=0) zcode_area[adjusted_pc++] = 0;
2318 while ((adjusted_pc%scale_factor)!=0) zcode_area[adjusted_pc++] = 0;
2320 zmachine_pc = adjusted_pc;
2324 static void transfer_routine_g(void)
2325 { int32 i, j, pc, new_pc, label, form_len, offset_of_next, addr,
2329 adjusted_pc = zmachine_pc - zcode_ha_size; rstart_pc = adjusted_pc;
2331 if (asm_trace_level >= 3)
2332 { printf("Backpatching routine at %05lx: initial size %d, %d labels\n",
2333 (long int) adjusted_pc, zcode_ha_size, next_label);
2336 /* (1) Scan through for branches and make short/long decisions in each
2337 case. Mark omitted bytes (bytes 2-4 in branches converted to
2338 short form) with DELETED_MV.
2339 We also look for branches that can be entirely eliminated (because
2340 they are jumping to the very next instruction). The opcode and
2341 all label bytes get DELETED_MV. */
2343 for (i=0, pc=adjusted_pc; i<zcode_ha_size; i++, pc++) {
2344 if (zcode_markers[i] >= BRANCH_MV && zcode_markers[i] < BRANCHMAX_MV) {
2345 int opmodeoffset = (zcode_markers[i] - BRANCH_MV);
2347 if (asm_trace_level >= 4)
2348 printf("Branch detected at offset %04x\n", pc);
2349 j = ((zcode_holding_area[i] << 24)
2350 | (zcode_holding_area[i+1] << 16)
2351 | (zcode_holding_area[i+2] << 8)
2352 | (zcode_holding_area[i+3]));
2353 offset_of_next = pc + 4;
2354 addr = (labels[j].offset - offset_of_next) + 2;
2355 opmodebyte = i - ((opmodeoffset+1)/2);
2356 if (asm_trace_level >= 4)
2357 printf("...To label %d, which is (%d-2) = %d from here\n",
2358 j, addr, labels[j].offset - offset_of_next);
2359 if (addr == 2 && i >= 2 && opmodeoffset == 2 && zcode_holding_area[opmodebyte-1] == opcodes_table_g[jump_gc].code) {
2360 if (asm_trace_level >= 4) printf("...Deleting branch\n");
2361 zcode_markers[i-2] = DELETED_MV;
2362 zcode_markers[i-1] = DELETED_MV;
2363 zcode_markers[i] = DELETED_MV;
2364 zcode_markers[i+1] = DELETED_MV;
2365 zcode_markers[i+2] = DELETED_MV;
2366 zcode_markers[i+3] = DELETED_MV;
2368 else if (addr >= -0x80 && addr < 0x80) {
2369 if (asm_trace_level >= 4) printf("...Byte form\n");
2370 zcode_markers[i+1] = DELETED_MV;
2371 zcode_markers[i+2] = DELETED_MV;
2372 zcode_markers[i+3] = DELETED_MV;
2373 if ((opmodeoffset & 1) == 0)
2374 zcode_holding_area[opmodebyte] =
2375 (zcode_holding_area[opmodebyte] & 0xF0) | 0x01;
2377 zcode_holding_area[opmodebyte] =
2378 (zcode_holding_area[opmodebyte] & 0x0F) | 0x10;
2380 else if (addr >= -0x8000 && addr < 0x8000) {
2381 if (asm_trace_level >= 4) printf("...Short form\n");
2382 zcode_markers[i+2] = DELETED_MV;
2383 zcode_markers[i+3] = DELETED_MV;
2384 if ((opmodeoffset & 1) == 0)
2385 zcode_holding_area[opmodebyte] =
2386 (zcode_holding_area[opmodebyte] & 0xF0) | 0x02;
2388 zcode_holding_area[opmodebyte] =
2389 (zcode_holding_area[opmodebyte] & 0x0F) | 0x20;
2394 /* (2) Calculate the new positions of the labels. Note that since the
2395 long/short decision was taken on the basis of the old labels,
2396 and since the new labels are slightly closer together because
2397 of branch bytes deleted, there may be a few further branch
2398 optimisations which are possible but which have been missed
2399 (if two labels move inside the "short" range as a result of
2400 a previous optimisation). However, this is acceptably uncommon. */
2401 if (next_label > 0) {
2402 if (asm_trace_level >= 4) {
2403 printf("Opening label: %d\n", first_label);
2404 for (i=0;i<next_label;i++)
2405 printf("Label %d offset %04x next -> %d previous -> %d\n",
2406 i, labels[i].offset, labels[i].next, labels[i].prev);
2409 /* label will advance through the linked list as pc increases. */
2410 for (i=0, pc=adjusted_pc, new_pc=adjusted_pc, label = first_label;
2413 while ((label != -1) && (labels[label].offset == pc)) {
2414 if (asm_trace_level >= 4)
2415 printf("Position of L%d corrected from %04x to %04x\n",
2416 label, labels[label].offset, new_pc);
2417 labels[label].offset = new_pc;
2418 label = labels[label].next;
2420 if (zcode_markers[i] != DELETED_MV) new_pc++;
2424 /* (3) As we are transferring, replace the label numbers in branch
2425 operands with offsets to those labels. Also issue markers, now
2426 that we know where they occur in the final Z-code area. */
2428 ensure_memory_list_available(&zcode_area_memlist, adjusted_pc+zcode_ha_size);
2430 for (i=0, new_pc=adjusted_pc; i<zcode_ha_size; i++) {
2432 if (zcode_markers[i] >= BRANCH_MV && zcode_markers[i] < BRANCHMAX_MV) {
2434 if (zcode_markers[i+1] == DELETED_MV) {
2438 if (zcode_markers[i+2] == DELETED_MV)
2441 j = ((zcode_holding_area[i] << 24)
2442 | (zcode_holding_area[i+1] << 16)
2443 | (zcode_holding_area[i+2] << 8)
2444 | (zcode_holding_area[i+3]));
2446 /* At the moment, we can safely assume that the branch operand
2447 is the end of the opcode, so the next opcode starts right
2449 offset_of_next = new_pc + form_len;
2451 if (labels[j].offset < 0) {
2452 char *lname = "(anon)";
2453 if (labels[j].symbol >= 0 && labels[j].symbol < no_symbols)
2454 lname = symbols[labels[j].symbol].name;
2455 error_named("Attempt to jump to an unreachable label", lname);
2459 addr = (labels[j].offset - offset_of_next) + 2;
2461 if (asm_trace_level >= 4) {
2462 printf("Branch at offset %04x: %04x (%s)\n",
2463 new_pc, addr, ((form_len == 1) ? "byte" :
2464 ((form_len == 2) ? "short" : "long")));
2466 if (form_len == 1) {
2467 if (addr < -0x80 || addr >= 0x80) {
2468 error("*** Label out of range for byte branch ***");
2470 zcode_holding_area[i] = (addr) & 0xFF;
2472 else if (form_len == 2) {
2473 if (addr < -0x8000 || addr >= 0x8000) {
2474 error("*** Label out of range for short branch ***");
2476 zcode_holding_area[i] = (addr >> 8) & 0xFF;
2477 zcode_holding_area[i+1] = (addr) & 0xFF;
2480 zcode_holding_area[i] = (addr >> 24) & 0xFF;
2481 zcode_holding_area[i+1] = (addr >> 16) & 0xFF;
2482 zcode_holding_area[i+2] = (addr >> 8) & 0xFF;
2483 zcode_holding_area[i+3] = (addr) & 0xFF;
2485 zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++;
2487 else if (zcode_markers[i] == LABEL_MV) {
2488 error("*** No LABEL opcodes in Glulx ***");
2490 else if (zcode_markers[i] == DELETED_MV) {
2494 switch(zcode_markers[i] & 0x7f) {
2505 if ((zcode_markers[i] & 0x7f) > LARGEST_BPATCH_MV) {
2506 error("*** Illegal code backpatch value ***");
2507 printf("Illegal value of %02x at PC = %04x\n",
2508 zcode_markers[i] & 0x7f, new_pc);
2511 /* The backpatch table format for Glulx:
2512 First, the marker byte (0..LARGEST_BPATCH_MV).
2513 Then a byte indicating the data size to be patched (1, 2, 4).
2514 Then the four-byte address (new_pc).
2516 if (bpatch_trace_setting >= 2)
2517 printf("BP added: MV %d size %d PC %04x\n", zcode_markers[i], 4, new_pc);
2518 ensure_memory_list_available(&zcode_backpatch_table_memlist, zcode_backpatch_size+6);
2519 zcode_backpatch_table[zcode_backpatch_size++] = zcode_markers[i];
2520 zcode_backpatch_table[zcode_backpatch_size++] = 4;
2521 zcode_backpatch_table[zcode_backpatch_size++] = ((new_pc >> 24) & 0xFF);
2522 zcode_backpatch_table[zcode_backpatch_size++] = ((new_pc >> 16) & 0xFF);
2523 zcode_backpatch_table[zcode_backpatch_size++] = ((new_pc >> 8) & 0xFF);
2524 zcode_backpatch_table[zcode_backpatch_size++] = (new_pc & 0xFF);
2527 zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++;
2531 /* Consistency check */
2532 if (new_pc - rstart_pc > zcode_ha_size || adjusted_pc != new_pc)
2534 fatalerror("Optimisation increased routine length or failed to match; should not happen");
2537 if (asm_trace_level >= 3)
2538 { printf("After branch optimisation, routine length is %d bytes\n",
2539 new_pc - rstart_pc);
2542 zmachine_pc = adjusted_pc;
2547 /* ========================================================================= */
2548 /* Front ends for the instruction assembler: convenient shorthand forms */
2549 /* used in various code generation routines all over Inform. */
2550 /* ------------------------------------------------------------------------- */
2552 void assemble_jump(int n)
2560 void assemblez_0(int internal_number)
2561 { AI.internal_number = internal_number;
2562 AI.operand_count = 0;
2563 AI.store_variable_number = -1;
2564 AI.branch_label_number = -1;
2565 assemblez_instruction(&AI);
2568 void assemblez_0_to(int internal_number, assembly_operand o)
2569 { AI.internal_number = internal_number;
2570 AI.operand_count = 0;
2571 AI.store_variable_number = o.value;
2572 AI.branch_label_number = -1;
2573 assemblez_instruction(&AI);
2576 void assemblez_0_branch(int internal_number, int label, int flag)
2577 { AI.internal_number = internal_number;
2578 AI.operand_count = 0;
2579 AI.store_variable_number = -1;
2580 AI.branch_label_number = label;
2581 AI.branch_flag = flag;
2582 assemblez_instruction(&AI);
2585 void assemblez_1(int internal_number, assembly_operand o1)
2586 { AI.internal_number = internal_number;
2587 AI.operand_count = 1;
2589 AI.store_variable_number = -1;
2590 AI.branch_label_number = -1;
2591 assemblez_instruction(&AI);
2594 void assemblez_1_to(int internal_number,
2595 assembly_operand o1, assembly_operand st)
2596 { AI.internal_number = internal_number;
2597 AI.operand_count = 1;
2599 AI.store_variable_number = st.value;
2600 AI.branch_label_number = -1;
2601 assemblez_instruction(&AI);
2604 void assemblez_1_branch(int internal_number,
2605 assembly_operand o1, int label, int flag)
2607 /* Some clever optimizations first. A constant is always or never equal
2609 if (o1.marker == 0 && is_constant_ot(o1.type)) {
2610 if (internal_number == jz_zc) {
2611 if ((flag && o1.value == 0) || (!flag && o1.value != 0)) {
2612 assemblez_jump(label);
2616 /* assemble nothing at all! */
2621 AI.internal_number = internal_number;
2622 AI.operand_count = 1;
2624 AI.branch_label_number = label;
2625 AI.store_variable_number = -1;
2626 AI.branch_flag = flag;
2627 assemblez_instruction(&AI);
2630 void assemblez_2(int internal_number,
2631 assembly_operand o1, assembly_operand o2)
2632 { AI.internal_number = internal_number;
2633 AI.operand_count = 2;
2636 AI.store_variable_number = -1;
2637 AI.branch_label_number = -1;
2638 assemblez_instruction(&AI);
2641 void assemblez_3(int internal_number,
2642 assembly_operand o1, assembly_operand o2, assembly_operand o3)
2643 { AI.internal_number = internal_number;
2644 AI.operand_count = 3;
2648 AI.store_variable_number = -1;
2649 AI.branch_label_number = -1;
2650 assemblez_instruction(&AI);
2653 void assemblez_3_to(int internal_number,
2654 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2655 assembly_operand st)
2656 { AI.internal_number = internal_number;
2657 AI.operand_count = 3;
2661 AI.store_variable_number = st.value;
2662 AI.branch_label_number = -1;
2663 assemblez_instruction(&AI);
2666 void assemblez_3_branch(int internal_number,
2667 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2668 int label, int flag)
2669 { AI.internal_number = internal_number;
2670 AI.operand_count = 3;
2674 AI.store_variable_number = -1;
2675 AI.branch_label_number = label;
2676 AI.branch_flag = flag;
2677 assemblez_instruction(&AI);
2680 void assemblez_4(int internal_number,
2681 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2682 assembly_operand o4)
2683 { AI.internal_number = internal_number;
2684 AI.operand_count = 4;
2689 AI.store_variable_number = -1;
2690 AI.branch_label_number = -1;
2691 assemblez_instruction(&AI);
2694 void assemblez_5(int internal_number,
2695 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2696 assembly_operand o4, assembly_operand o5)
2697 { AI.internal_number = internal_number;
2698 AI.operand_count = 5;
2704 AI.store_variable_number = -1;
2705 AI.branch_label_number = -1;
2706 assemblez_instruction(&AI);
2709 void assemblez_6(int internal_number,
2710 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2711 assembly_operand o4, assembly_operand o5, assembly_operand o6)
2712 { AI.internal_number = internal_number;
2713 AI.operand_count = 6;
2720 AI.store_variable_number = -1;
2721 AI.branch_label_number = -1;
2722 assemblez_instruction(&AI);
2725 void assemblez_4_branch(int internal_number,
2726 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2727 assembly_operand o4, int label, int flag)
2728 { AI.internal_number = internal_number;
2729 AI.operand_count = 4;
2734 AI.store_variable_number = -1;
2735 AI.branch_label_number = label;
2736 AI.branch_flag = flag;
2737 assemblez_instruction(&AI);
2740 void assemblez_4_to(int internal_number,
2741 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2742 assembly_operand o4, assembly_operand st)
2743 { AI.internal_number = internal_number;
2744 AI.operand_count = 4;
2749 AI.store_variable_number = st.value;
2750 AI.branch_label_number = -1;
2751 assemblez_instruction(&AI);
2754 void assemblez_5_to(int internal_number,
2755 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2756 assembly_operand o4, assembly_operand o5, assembly_operand st)
2757 { AI.internal_number = internal_number;
2758 AI.operand_count = 5;
2764 AI.store_variable_number = st.value;
2765 AI.branch_label_number = -1;
2766 assemblez_instruction(&AI);
2769 void assemblez_2_to(int internal_number,
2770 assembly_operand o1, assembly_operand o2, assembly_operand st)
2771 { AI.internal_number = internal_number;
2772 AI.operand_count = 2;
2775 AI.store_variable_number = st.value;
2776 AI.branch_label_number = -1;
2777 assemblez_instruction(&AI);
2780 void assemblez_2_branch(int internal_number,
2781 assembly_operand o1, assembly_operand o2, int label, int flag)
2782 { AI.internal_number = internal_number;
2783 AI.operand_count = 2;
2786 AI.branch_label_number = label;
2787 AI.store_variable_number = -1;
2788 AI.branch_flag = flag;
2789 assemblez_instruction(&AI);
2792 void assemblez_objcode(int internal_number,
2793 assembly_operand o1, assembly_operand st, int label, int flag)
2794 { AI.internal_number = internal_number;
2795 AI.operand_count = 1;
2797 AI.branch_label_number = label;
2798 AI.store_variable_number = st.value;
2799 AI.branch_flag = flag;
2800 assemblez_instruction(&AI);
2803 extern void assemblez_inc(assembly_operand o1)
2805 if ((o1.value >= MAX_LOCAL_VARIABLES)
2806 && (o1.value<LOWEST_SYSTEM_VAR_NUMBER))
2808 AI.internal_number = inc_zc;
2809 AI.operand_count = 1;
2810 AI.operand[0].value = o1.value;
2811 AI.operand[0].type = SHORT_CONSTANT_OT;
2812 AI.operand[0].marker = m;
2813 AI.store_variable_number = -1;
2814 AI.branch_label_number = -1;
2815 assemblez_instruction(&AI);
2818 extern void assemblez_dec(assembly_operand o1)
2820 if ((o1.value >= MAX_LOCAL_VARIABLES)
2821 && (o1.value<LOWEST_SYSTEM_VAR_NUMBER))
2823 AI.internal_number = dec_zc;
2824 AI.operand_count = 1;
2825 AI.operand[0].value = o1.value;
2826 AI.operand[0].type = SHORT_CONSTANT_OT;
2827 AI.operand[0].marker = m;
2828 AI.store_variable_number = -1;
2829 AI.branch_label_number = -1;
2830 assemblez_instruction(&AI);
2833 extern void assemblez_store(assembly_operand o1, assembly_operand o2)
2835 if ((o1.value >= MAX_LOCAL_VARIABLES)
2836 && (o1.value<LOWEST_SYSTEM_VAR_NUMBER))
2839 if ((o2.type == VARIABLE_OT) && (o2.value == 0))
2841 /* Assemble "pull VAR" rather than "store VAR sp",
2844 AI.internal_number = pull_zc;
2845 if (instruction_set_number == 6)
2846 { AI.operand_count = 0;
2847 AI.store_variable_number = o1.value;
2850 { AI.operand_count = 1;
2851 AI.operand[0].value = o1.value;
2852 AI.operand[0].type = SHORT_CONSTANT_OT;
2853 AI.operand[0].marker = m;
2854 AI.store_variable_number = -1;
2856 AI.branch_label_number = -1;
2857 assemblez_instruction(&AI);
2861 if ((o1.type == VARIABLE_OT) && (o1.value == 0))
2862 { /* Assemble "push VAR" rather than "store sp VAR",
2865 AI.internal_number = push_zc;
2866 AI.operand_count = 1;
2868 AI.store_variable_number = -1;
2869 AI.branch_label_number = -1;
2870 assemblez_instruction(&AI);
2873 AI.internal_number = store_zc;
2874 AI.operand_count = 2;
2875 AI.operand[0].value = o1.value;
2876 AI.operand[0].type = SHORT_CONSTANT_OT;
2877 AI.operand[0].marker = m;
2879 AI.store_variable_number = -1;
2880 AI.branch_label_number = -1;
2881 assemblez_instruction(&AI);
2884 void assemblez_jump(int n)
2885 { assembly_operand AO;
2886 if (n==-4) assemblez_0(rtrue_zc);
2887 else if (n==-3) assemblez_0(rfalse_zc);
2889 { AO.type = LONG_CONSTANT_OT; AO.value = n; AO.marker = 0;
2890 assemblez_1(jump_zc, AO);
2894 void assembleg_0(int internal_number)
2895 { AI.internal_number = internal_number;
2896 AI.operand_count = 0;
2897 assembleg_instruction(&AI);
2900 void assembleg_1(int internal_number, assembly_operand o1)
2901 { AI.internal_number = internal_number;
2902 AI.operand_count = 1;
2904 assembleg_instruction(&AI);
2907 void assembleg_2(int internal_number, assembly_operand o1,
2908 assembly_operand o2)
2909 { AI.internal_number = internal_number;
2910 AI.operand_count = 2;
2913 assembleg_instruction(&AI);
2916 void assembleg_3(int internal_number, assembly_operand o1,
2917 assembly_operand o2, assembly_operand o3)
2918 { AI.internal_number = internal_number;
2919 AI.operand_count = 3;
2923 assembleg_instruction(&AI);
2926 void assembleg_4(int internal_number, assembly_operand o1,
2927 assembly_operand o2, assembly_operand o3,
2928 assembly_operand o4)
2929 { AI.internal_number = internal_number;
2930 AI.operand_count = 4;
2935 assembleg_instruction(&AI);
2938 void assembleg_5(int internal_number, assembly_operand o1,
2939 assembly_operand o2, assembly_operand o3,
2940 assembly_operand o4, assembly_operand o5)
2941 { AI.internal_number = internal_number;
2942 AI.operand_count = 5;
2948 assembleg_instruction(&AI);
2951 void assembleg_0_branch(int internal_number,
2954 AI.internal_number = internal_number;
2955 AI.operand_count = 1;
2956 AI.operand[0].type = CONSTANT_OT;
2957 AI.operand[0].value = label;
2958 AI.operand[0].marker = BRANCH_MV;
2959 assembleg_instruction(&AI);
2962 void assembleg_1_branch(int internal_number,
2963 assembly_operand o1, int label)
2965 /* Some clever optimizations first. A constant is always or never equal
2967 if (o1.marker == 0 && is_constant_ot(o1.type)) {
2968 if ((internal_number == jz_gc && o1.value == 0)
2969 || (internal_number == jnz_gc && o1.value != 0)) {
2970 assembleg_0_branch(jump_gc, label);
2973 if ((internal_number == jz_gc && o1.value != 0)
2974 || (internal_number == jnz_gc && o1.value == 0)) {
2975 /* assemble nothing at all! */
2979 AI.internal_number = internal_number;
2980 AI.operand_count = 2;
2982 AI.operand[1].type = CONSTANT_OT;
2983 AI.operand[1].value = label;
2984 AI.operand[1].marker = BRANCH_MV;
2985 assembleg_instruction(&AI);
2988 void assembleg_2_branch(int internal_number,
2989 assembly_operand o1, assembly_operand o2, int label)
2991 AI.internal_number = internal_number;
2992 AI.operand_count = 3;
2995 AI.operand[2].type = CONSTANT_OT;
2996 AI.operand[2].value = label;
2997 AI.operand[2].marker = BRANCH_MV;
2998 assembleg_instruction(&AI);
3001 void assembleg_call_1(assembly_operand oaddr, assembly_operand o1,
3002 assembly_operand odest)
3004 assembleg_3(callfi_gc, oaddr, o1, odest);
3007 void assembleg_call_2(assembly_operand oaddr, assembly_operand o1,
3008 assembly_operand o2, assembly_operand odest)
3010 assembleg_4(callfii_gc, oaddr, o1, o2, odest);
3013 void assembleg_call_3(assembly_operand oaddr, assembly_operand o1,
3014 assembly_operand o2, assembly_operand o3, assembly_operand odest)
3016 assembleg_5(callfiii_gc, oaddr, o1, o2, o3, odest);
3019 void assembleg_inc(assembly_operand o1)
3021 AI.internal_number = add_gc;
3022 AI.operand_count = 3;
3024 AI.operand[1] = one_operand;
3026 assembleg_instruction(&AI);
3029 void assembleg_dec(assembly_operand o1)
3031 AI.internal_number = sub_gc;
3032 AI.operand_count = 3;
3034 AI.operand[1] = one_operand;
3036 assembleg_instruction(&AI);
3039 void assembleg_store(assembly_operand o1, assembly_operand o2)
3041 /* Note the order is reversed: "o1 = o2;" */
3042 assembleg_2(copy_gc, o2, o1);
3045 void assembleg_jump(int n)
3048 assembleg_1(return_gc, one_operand);
3051 assembleg_1(return_gc, zero_operand);
3054 assembleg_0_branch(jump_gc, n);
3058 /* ========================================================================= */
3059 /* Parsing and then calling the assembler for @ (assembly language) */
3061 /* ------------------------------------------------------------------------- */
3063 static assembly_operand parse_operand_z(void)
3064 { assembly_operand AO;
3066 AO = parse_expression(ASSEMBLY_CONTEXT);
3067 if (AO.type == EXPRESSION_OT)
3068 { ebf_error("variable or constant", "expression");
3069 AO.type = SHORT_CONSTANT_OT;
3074 static void parse_assembly_z(void)
3076 int indirect_addressed, jumplabel_args;
3077 int error_flag = FALSE;
3080 AI.operand_count = 0;
3081 AI.store_variable_number = -1;
3082 AI.branch_label_number = -1;
3085 opcode_names.enabled = TRUE;
3087 opcode_names.enabled = FALSE;
3089 if (token_type == DQ_TT)
3091 AI.internal_number = -1;
3093 custom_opcode_z.name = (uchar *) token_text;
3094 custom_opcode_z.version1 = instruction_set_number;
3095 custom_opcode_z.version2 = instruction_set_number;
3096 custom_opcode_z.extension = -1;
3097 custom_opcode_z.flags = 0;
3098 custom_opcode_z.op_rules = 0;
3099 custom_opcode_z.flags2_set = 0;
3100 custom_opcode_z.no = ZERO;
3102 for (i=0; token_text[i]!=0; i++)
3103 { if (token_text[i] == ':')
3104 { token_text[i++] = 0;
3108 if (token_text[i] == 0)
3109 error("Opcode specification should have form \"VAR:102\"");
3112 if (strcmp(token_text, "0OP")==0) n=ZERO;
3113 if (strcmp(token_text, "1OP")==0) n=ONE;
3114 if (strcmp(token_text, "2OP")==0) n=TWO;
3115 if (strcmp(token_text, "VAR")==0) n=VAR;
3116 if (strcmp(token_text, "EXT")==0) n=EXT;
3117 if (strcmp(token_text, "VAR_LONG")==0) n=VAR_LONG;
3118 if (strcmp(token_text, "EXT_LONG")==0) n=EXT_LONG;
3120 if (i>0) token_text[i-1] = ':';
3123 { ebf_curtoken_error("Expected 0OP, 1OP, 2OP, VAR, EXT, VAR_LONG or EXT_LONG");
3126 custom_opcode_z.no = n;
3128 custom_opcode_z.code = atoi(token_text+i);
3129 while (isdigit(token_text[i])) i++;
3133 { case ZERO: case ONE: max = 16; break;
3134 case VAR: case VAR_LONG: min = 32; max = 64; break;
3135 case EXT: case EXT_LONG: max = 256; break;
3136 case TWO: max = 32; break;
3138 if ((custom_opcode_z.code < min) || (custom_opcode_z.code >= max))
3140 error_fmt("For this operand type, opcode number must be in range %d to %d",
3142 custom_opcode_z.code = min;
3146 while (token_text[i++] != 0)
3147 { switch(token_text[i-1])
3148 { case 'B': custom_opcode_z.flags |= Br; break;
3149 case 'S': custom_opcode_z.flags |= St; break;
3150 case 'T': custom_opcode_z.op_rules = TEXT; break;
3151 case 'I': custom_opcode_z.op_rules = VARIAB; break;
3152 case 'F': custom_opcode_z.flags2_set = atoi(token_text+i);
3153 while (isdigit(token_text[i])) i++; break;
3155 error("Unknown flag: options are B (branch), S (store), \
3156 T (text), I (indirect addressing), F** (set this Flags 2 bit)");
3160 O = custom_opcode_z;
3162 else if ((token_type == SEP_TT) && (token_value == ARROW_SEP || token_value == DARROW_SEP))
3164 int32 start_pc = zcode_ha_size;
3166 int isword = (token_value == DARROW_SEP);
3168 assembly_operand AO;
3169 /* This isn't the start of a statement, but it's safe to
3170 release token texts anyway. */
3171 release_token_texts();
3173 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
3175 AO = parse_expression(ARRAY_CONTEXT);
3176 if (AO.marker == ERROR_MV) {
3181 error("Entries in code byte arrays must be known constants");
3182 if (AO.value >= 256)
3183 warning("Entry in code byte array not in range 0 to 255");
3185 if (execution_never_reaches_here) {
3188 if (bytecount == 0 && asm_trace_level > 0) {
3189 printf("%5d +%05lx %3s %-12s", ErrorReport.line_number,
3190 ((long int) zmachine_pc), " ",
3191 isword?"<words>":"<bytes>");
3194 byteout((AO.value & 0xFF), 0);
3196 if (asm_trace_level > 0) {
3197 printf(" %02x", (AO.value & 0xFF));
3201 byteout(((AO.value >> 8) & 0xFF), AO.marker);
3202 byteout((AO.value & 0xFF), 0);
3204 if (asm_trace_level > 0) {
3206 print_operand(&AO, TRUE);
3210 if (bytecount > 0 && asm_trace_level > 0) {
3213 if (asm_trace_level>=2)
3216 for (j=0;start_pc<zcode_ha_size;
3218 { if (j%16==0) printf(" ");
3219 if (zcode_markers[start_pc] & 0x7f)
3220 printf("{%s}", describe_mv_short(zcode_markers[start_pc] & 0x7f));
3221 printf("%02x ", zcode_holding_area[start_pc]);
3223 if (j) printf("\n");
3228 { if (token_type != OPCODE_NAME_TT)
3229 { ebf_curtoken_error("an opcode name");
3230 panic_mode_error_recovery();
3233 AI.internal_number = token_value;
3234 O = internal_number_to_opcode_z(AI.internal_number);
3237 indirect_addressed = (O.op_rules == VARIAB);
3238 jumplabel_args = (O.op_rules == LABEL); /* only @jump */
3240 if (O.op_rules == TEXT)
3242 if (token_type != DQ_TT)
3243 ebf_curtoken_error("literal text in double-quotes");
3244 AI.text = token_text;
3245 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) return;
3247 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
3248 { assemblez_instruction(&AI);
3252 ebf_curtoken_error("semicolon ';' after print string");
3258 return_sp_as_variable = TRUE;
3262 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
3264 if ((token_type == SEP_TT) && (token_value == ARROW_SEP))
3265 { if (AI.store_variable_number != -1)
3266 error("Only one '->' store destination can be given");
3268 if ((token_type != SYMBOL_TT)
3269 && (token_type != LOCAL_VARIABLE_TT))
3270 ebf_curtoken_error("variable name or 'sp'");
3272 if (token_type == LOCAL_VARIABLE_TT) n = token_value;
3274 { if (strcmp(token_text, "sp") == 0) n = 0;
3276 { if (symbols[token_value].type != GLOBAL_VARIABLE_T)
3278 "Store '->' destination not 'sp' or a variable:",
3280 else n = symbols[token_value].value;
3283 AI.store_variable_number = n;
3287 if ((token_type == SEP_TT) &&
3288 ((token_value == BRANCH_SEP) || (token_value == NBRANCH_SEP)))
3289 { if (AI.branch_label_number != -1)
3290 error("Only one '?' branch destination can be given");
3292 AI.branch_flag = (token_value == BRANCH_SEP);
3294 opcode_names.enabled = TRUE;
3296 opcode_names.enabled = FALSE;
3299 if ((token_type == OPCODE_NAME_TT)
3300 && (token_value == rfalse_zc)) n = -3;
3302 if ((token_type == OPCODE_NAME_TT)
3303 && (token_value == rtrue_zc)) n = -4;
3305 { if (token_type == SYMBOL_TT)
3310 ebf_curtoken_error("label name after '?' or '?~'");
3312 AI.branch_label_number = n;
3316 if (AI.operand_count == 8)
3317 { error("No assembly instruction may have more than 8 operands");
3318 panic_mode_error_recovery(); break;
3321 if ((token_type == SEP_TT) && (token_value == OPEN_SQUARE_SEP))
3322 { if (!indirect_addressed)
3323 error("This opcode does not use indirect addressing");
3324 if (AI.operand_count > 0)
3325 error("Indirect addressing can only be used on the first operand");
3326 AI.operand[AI.operand_count++] = parse_operand_z();
3328 if (!((token_type == SEP_TT) && (token_value == CLOSE_SQUARE_SEP)))
3329 { ebf_curtoken_error("']'");
3333 else if (jumplabel_args)
3334 { assembly_operand AO;
3336 INITAOTV(&AO, LONG_CONSTANT_OT, parse_label());
3337 AI.operand[AI.operand_count++] = AO;
3341 AI.operand[AI.operand_count++] = parse_operand_z();
3342 if ((indirect_addressed) && (AI.operand_count == 1)
3343 && (AI.operand[AI.operand_count-1].type == VARIABLE_OT))
3344 { AI.operand[AI.operand_count-1].type = SHORT_CONSTANT_OT;
3345 AI.operand[AI.operand_count-1].marker = VARIABLE_MV;
3351 return_sp_as_variable = FALSE;
3354 if (O.version1 == 0)
3355 { error_named("Opcode unavailable in this Z-machine version:",
3356 opcode_names.keywords[AI.internal_number]);
3360 if (((O.flags) & Br) != 0)
3361 { if (AI.branch_label_number == -1)
3362 { error_flag = TRUE;
3363 AI.branch_label_number = -2;
3367 { if (AI.branch_label_number != -1)
3368 { error_flag = TRUE;
3369 AI.branch_label_number = -1;
3372 if (((O.flags) & St) != 0)
3373 { if (AI.store_variable_number == -1)
3374 { if (AI.operand_count == 0)
3375 { error_flag = TRUE;
3376 AI.store_variable_number = 255;
3379 { AI.store_variable_number
3380 = AI.operand[--AI.operand_count].value;
3381 if (AI.operand[AI.operand_count].type != VARIABLE_OT)
3382 error("Store destination (the last operand) is not a variable");
3387 { if (AI.store_variable_number != -1)
3388 { error_flag = TRUE;
3389 AI.store_variable_number = -1;
3395 { case TWO: min = 2; max = 2;
3396 /* Exception for the V6 set_colour, which can take
3397 a third argument, thus forcing it into VAR form: */
3398 if ((version_number == 6) && (O.code == 0x1b)) max = 3;
3399 /* Also an exception for je, which can take from 1
3400 argument (useless) to 4 arguments */
3401 if (O.code == 0x01) { min = 1; max = 4; }
3403 case VAR: min = 0; max = 4; break;
3404 case VAR_LONG: min = 0; max = 8; break;
3405 case ONE: min = 1; max = 1; break;
3406 case ZERO: min = 0; max = 0; break;
3407 case EXT: min = 0; max = 4; break;
3408 case EXT_LONG: min = 0; max = 8; break;
3411 if ((AI.operand_count >= min) && (AI.operand_count <= max))
3412 assemblez_instruction(&AI);
3413 else error_flag = TRUE;
3416 { make_opcode_syntax_z(O);
3417 error_named("Assembly mistake: syntax is",
3418 opcode_syntax_string);
3422 static assembly_operand parse_operand_g(void)
3423 { assembly_operand AO;
3425 AO = parse_expression(ASSEMBLY_CONTEXT);
3426 if (AO.type == EXPRESSION_OT)
3427 { ebf_error("variable or constant", "expression");
3428 AO.type = CONSTANT_OT;
3433 static void parse_assembly_g(void)
3436 assembly_operand AO;
3437 int error_flag = FALSE, is_macro = FALSE;
3439 AI.operand_count = 0;
3442 opcode_names.enabled = TRUE;
3443 opcode_macros.enabled = TRUE;
3445 opcode_names.enabled = FALSE;
3446 opcode_macros.enabled = FALSE;
3448 if (token_type == DQ_TT) {
3452 AI.internal_number = -1;
3454 /* The format is @"FlagsCount:Code". Flags (which are optional)
3455 can include "S" for store, "SS" for two stores, "B" for branch
3456 format, "R" if execution never continues after the opcode. The
3457 Count is the number of arguments (currently limited to 0-9),
3458 and the Code is a decimal integer representing the opcode
3461 So: @"S3:123" for a three-argument opcode (load, load, store)
3462 whose opcode number is (decimal) 123. Or: @"2:234" for a
3463 two-argument opcode (load, load) whose number is 234. */
3465 custom_opcode_g.name = (uchar *) token_text;
3466 custom_opcode_g.flags = 0;
3467 custom_opcode_g.op_rules = 0;
3468 custom_opcode_g.no = 0;
3472 for (cx = token_text; *cx && *cx != ':'; cx++) {
3478 if (custom_opcode_g.flags & St)
3479 custom_opcode_g.flags |= St2;
3481 custom_opcode_g.flags |= St;
3484 custom_opcode_g.flags |= Br;
3487 custom_opcode_g.flags |= Rf;
3491 custom_opcode_g.no = (*cx) - '0';
3495 error("Unknown custom opcode flag: options are B (branch), \
3496 S (store), SS (two stores), R (execution never continues)");
3502 error("Custom opcode must have colon");
3507 error("Custom opcode must have colon followed by opcode number");
3509 custom_opcode_g.code = atoi(cx);
3512 O = custom_opcode_g;
3514 else if ((token_type == SEP_TT) && (token_value == ARROW_SEP || token_value == DARROW_SEP))
3516 int32 start_pc = zcode_ha_size;
3518 int isword = (token_value == DARROW_SEP);
3520 assembly_operand AO;
3521 /* This isn't the start of a statement, but it's safe to
3522 release token texts anyway. */
3523 release_token_texts();
3525 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
3527 AO = parse_expression(ARRAY_CONTEXT);
3528 if (AO.marker == ERROR_MV) {
3533 error("Entries in code byte arrays must be known constants");
3534 if (AO.value >= 256)
3535 warning("Entry in code byte array not in range 0 to 255");
3537 if (execution_never_reaches_here) {
3540 if (bytecount == 0 && asm_trace_level > 0) {
3541 printf("%5d +%05lx %3s %-12s", ErrorReport.line_number,
3542 ((long int) zmachine_pc), " ",
3543 isword?"<words>":"<bytes>");
3546 byteout((AO.value & 0xFF), 0);
3548 if (asm_trace_level > 0) {
3549 printf(" %02x", (AO.value & 0xFF));
3553 byteout(((AO.value >> 24) & 0xFF), AO.marker);
3554 byteout(((AO.value >> 16) & 0xFF), 0);
3555 byteout(((AO.value >> 8) & 0xFF), 0);
3556 byteout((AO.value & 0xFF), 0);
3558 if (asm_trace_level > 0) {
3560 print_operand(&AO, TRUE);
3564 if (bytecount > 0 && asm_trace_level > 0) {
3567 if (asm_trace_level>=2)
3570 for (j=0;start_pc<zcode_ha_size;
3572 { if (j%16==0) printf(" ");
3573 if (zcode_markers[start_pc])
3574 printf("{%s}", describe_mv_short(zcode_markers[start_pc]));
3575 printf("%02x ", zcode_holding_area[start_pc]);
3577 if (j) printf("\n");
3582 if (token_type != OPCODE_NAME_TT && token_type != OPCODE_MACRO_TT) {
3583 ebf_curtoken_error("an opcode name");
3584 panic_mode_error_recovery();
3587 AI.internal_number = token_value;
3588 if (token_type == OPCODE_MACRO_TT) {
3589 O = internal_number_to_opmacro_g(AI.internal_number);
3593 O = internal_number_to_opcode_g(AI.internal_number);
3596 return_sp_as_variable = TRUE;
3601 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
3604 if (AI.operand_count == 8) {
3605 error("No assembly instruction may have more than 8 operands");
3606 panic_mode_error_recovery();
3610 if ((O.flags & Br) && (AI.operand_count == O.no-1)) {
3611 if (!((token_type == SEP_TT) && (token_value == BRANCH_SEP))) {
3613 error("Branch opcode must have '?' label");
3616 AO.type = CONSTANT_OT;
3617 AO.value = parse_label();
3618 AO.marker = BRANCH_MV;
3622 AO = parse_operand_g();
3625 AI.operand[AI.operand_count] = AO;
3629 return_sp_as_variable = FALSE;
3631 if (O.no != AI.operand_count) {
3637 assembleg_macro(&AI);
3639 assembleg_instruction(&AI);
3643 make_opcode_syntax_g(O);
3644 error_named("Assembly mistake: syntax is",
3645 opcode_syntax_string);
3649 extern void parse_assembly(void)
3657 /* ========================================================================= */
3658 /* Data structure management routines */
3659 /* ------------------------------------------------------------------------- */
3661 extern void init_asm_vars(void)
3664 for (i=0;i<16;i++) flags2_requirements[i]=0;
3666 uses_unicode_features = FALSE;
3667 uses_memheap_features = FALSE;
3668 uses_acceleration_features = FALSE;
3669 uses_float_features = FALSE;
3670 uses_extundo_features = FALSE;
3671 uses_double_features = FALSE;
3674 sequence_points = NULL;
3675 sequence_point_follows = TRUE;
3676 label_moved_error_already_given = FALSE;
3681 extern void asm_begin_pass(void)
3682 { no_instructions = 0;
3684 no_sequence_points = 0;
3687 next_sequence_point = 0;
3689 execution_never_reaches_here = EXECSTATE_REACHABLE;
3692 extern void asm_allocate_arrays(void)
3694 initialise_memory_list(&variables_memlist,
3695 sizeof(variableinfo), 200, (void**)&variables,
3698 initialise_memory_list(&labels_memlist,
3699 sizeof(labelinfo), 1000, (void**)&labels,
3701 initialise_memory_list(&labeluse_memlist,
3702 sizeof(int), 1000, (void**)&labeluse,
3704 initialise_memory_list(&sequence_points_memlist,
3705 sizeof(sequencepointinfo), 1000, (void**)&sequence_points,
3708 initialise_memory_list(&zcode_holding_area_memlist,
3709 sizeof(uchar), 2000, (void**)&zcode_holding_area,
3710 "compiled routine code area");
3711 initialise_memory_list(&zcode_markers_memlist,
3712 sizeof(uchar), 2000, (void**)&zcode_markers,
3713 "compiled routine markers area");
3715 initialise_memory_list(&named_routine_symbols_memlist,
3716 sizeof(int32), 1000, (void**)&named_routine_symbols,
3717 "named routine symbols");
3719 initialise_memory_list(&zcode_area_memlist,
3720 sizeof(uchar), 8192, (void**)&zcode_area,
3723 initialise_memory_list(¤t_routine_name,
3724 sizeof(char), 64, NULL,
3725 "routine name currently being defined");
3728 extern void asm_free_arrays(void)
3730 deallocate_memory_list(&variables_memlist);
3732 deallocate_memory_list(&labels_memlist);
3733 deallocate_memory_list(&sequence_points_memlist);
3735 deallocate_memory_list(&zcode_holding_area_memlist);
3736 deallocate_memory_list(&zcode_markers_memlist);
3738 deallocate_memory_list(&named_routine_symbols_memlist);
3739 deallocate_memory_list(&zcode_area_memlist);
3740 deallocate_memory_list(¤t_routine_name);
3743 /* ========================================================================= */