1 /* ------------------------------------------------------------------------- */
2 /* "asm" : The Inform assembler */
4 /* Part of Inform 6.35 */
5 /* copyright (c) Graham Nelson 1993 - 2020 */
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 uchar *zcode_holding_area; /* Area holding code yet to be transferred
25 to either zcode_area or temp file no 1 */
26 uchar *zcode_markers; /* Bytes holding marker values for this
28 static int zcode_ha_size; /* Number of bytes in holding area */
30 memory_block zcode_area; /* Block to hold assembled code (if
31 temporary files are not being used) */
33 int32 zmachine_pc; /* PC position of assembly (byte offset
34 from start of Z-code area) */
36 int32 no_instructions; /* Number of instructions assembled */
37 int execution_never_reaches_here, /* TRUE if the current PC value in the
38 code area cannot be reached: e.g. if
39 the previous instruction was a "quit"
40 opcode and no label is set to here */
41 next_label, /* Used to count the labels created all
42 over Inform in current routine, from 0 */
43 next_sequence_point; /* Likewise, for sequence points */
44 int no_sequence_points; /* Kept for statistics purposes only */
46 static int label_moved_error_already_given;
47 /* When one label has moved, all subsequent
48 ones probably have too, and this flag
49 suppresses the runaway chain of error
50 messages which would otherwise result */
52 int sequence_point_follows; /* Will the next instruction assembled */
53 /* be at a sequence point in the routine? */
55 int uses_unicode_features; /* Makes use of Glulx Unicode (3.0)
57 int uses_memheap_features; /* Makes use of Glulx mem/heap (3.1)
59 int uses_acceleration_features; /* Makes use of Glulx acceleration (3.1.1)
61 int uses_float_features; /* Makes use of Glulx floating-point (3.1.2)
64 debug_location statement_debug_location;
65 /* Location of current statement */
68 int32 *variable_tokens; /* The allocated size is
69 (MAX_LOCAL_VARIABLES +
70 MAX_GLOBAL_VARIABLES). The entries
71 MAX_LOCAL_VARIABLES and up give the
72 symbol table index for the names of
73 the global variables */
74 int *variable_usage; /* TRUE if referred to, FALSE otherwise */
76 assembly_instruction AI; /* A structure used to hold the full
77 specification of a single Z-code
78 instruction: effectively this is the
80 assemble_instruction() */
82 static char opcode_syntax_string[128]; /* Text buffer holding the correct
83 syntax for an opcode: used to produce
84 helpful assembler error messages */
86 static int routine_symbol; /* The symbol index of the routine currently
88 static char *routine_name; /* The name of the routine currently being
90 static int routine_locals; /* The number of local variables used by
91 the routine currently being compiled */
93 static int32 routine_start_pc;
95 int32 *named_routine_symbols;
97 static void transfer_routine_z(void);
98 static void transfer_routine_g(void);
100 /* ------------------------------------------------------------------------- */
102 /* ------------------------------------------------------------------------- */
104 static int first_label, last_label;
105 static int32 *label_offsets; /* Double-linked list of label offsets */
106 static int *label_next, /* (i.e. zmachine_pc values) in PC order */
108 static int32 *label_symbols; /* Symbol numbers if defined in source */
110 static int *sequence_point_labels;
111 /* Label numbers for each */
112 static debug_location *sequence_point_locations;
113 /* Source code references for each */
114 /* (used for making debugging file) */
116 static void set_label_offset(int label, int32 offset)
118 if (label >= MAX_LABELS) memoryerror("MAX_LABELS", MAX_LABELS);
120 label_offsets[label] = offset;
121 if (last_label == -1)
122 { label_prev[label] = -1;
126 { label_prev[label] = last_label;
127 label_next[last_label] = label;
130 label_next[label] = -1;
131 label_symbols[label] = -1;
134 /* ------------------------------------------------------------------------- */
135 /* Useful tool for building operands */
136 /* ------------------------------------------------------------------------- */
138 extern void set_constant_ot(assembly_operand *AO)
141 if (AO->value >= 0 && AO->value <= 255)
142 AO->type = SHORT_CONSTANT_OT;
144 AO->type = LONG_CONSTANT_OT;
148 AO->type = ZEROCONSTANT_OT;
149 else if (AO->value >= -0x80 && AO->value < 0x80)
150 AO->type = BYTECONSTANT_OT;
151 else if (AO->value >= -0x8000 && AO->value < 0x8000)
152 AO->type = HALFCONSTANT_OT;
154 AO->type = CONSTANT_OT;
158 extern int is_constant_ot(int otval)
161 return ((otval == LONG_CONSTANT_OT)
162 || (otval == SHORT_CONSTANT_OT));
165 return ((otval == CONSTANT_OT)
166 || (otval == HALFCONSTANT_OT)
167 || (otval == BYTECONSTANT_OT)
168 || (otval == ZEROCONSTANT_OT));
172 extern int is_variable_ot(int otval)
175 return (otval == VARIABLE_OT);
178 return ((otval == LOCALVAR_OT)
179 || (otval == GLOBALVAR_OT));
183 /* ------------------------------------------------------------------------- */
184 /* Used in printing assembly traces */
185 /* ------------------------------------------------------------------------- */
187 extern char *variable_name(int32 i)
189 if (i==0) return("sp");
190 if (i<MAX_LOCAL_VARIABLES) return local_variable_texts[i-1];
193 if (i==255) return("TEMP1");
194 if (i==254) return("TEMP2");
195 if (i==253) return("TEMP3");
196 if (i==252) return("TEMP4");
197 if (i==251) return("self");
198 if (i==250) return("sender");
199 if (i==249) return("sw__var");
200 if (i >= 256 && i < 286)
201 { if (i - 256 < NUMBER_SYSTEM_FUNCTIONS) return system_functions.keywords[i - 256];
202 return "<unnamed system function>";
206 switch (i - MAX_LOCAL_VARIABLES) {
207 case 0: return "temp_global";
208 case 1: return "temp__global2";
209 case 2: return "temp__global3";
210 case 3: return "temp__global4";
211 case 4: return "self";
212 case 5: return "sender";
213 case 6: return "sw__var";
214 case 7: return "sys__glob0";
215 case 8: return "sys__glob1";
216 case 9: return "sys__glob2";
217 case 10: return "sys_statusline_flag";
221 return ((char *) symbs[variable_tokens[i]]);
224 static void print_operand_z(assembly_operand o)
226 { case EXPRESSION_OT: printf("expr_"); break;
227 case LONG_CONSTANT_OT: printf("long_"); break;
228 case SHORT_CONSTANT_OT: printf("short_"); break;
230 if (o.value==0) { printf("sp"); return; }
231 printf("%s", variable_name(o.value)); return;
232 case OMITTED_OT: printf("<no value>"); return;
234 printf("%d", o.value);
237 static void print_operand_g(assembly_operand o)
240 case EXPRESSION_OT: printf("expr_"); break;
241 case CONSTANT_OT: printf("long_"); break;
242 case HALFCONSTANT_OT: printf("short_"); break;
243 case BYTECONSTANT_OT: printf("byte_"); break;
244 case ZEROCONSTANT_OT: printf("zero_"); return;
245 case DEREFERENCE_OT: printf("*"); break;
247 printf("%s (global_%d)", variable_name(o.value), o.value);
253 printf("%s (local_%d)", variable_name(o.value), o.value-1);
256 if (o.value >= 0 && o.value < NUMBER_SYSTEM_FUNCTIONS)
257 printf("%s", system_functions.keywords[o.value]);
259 printf("<unnamed system function>");
261 case OMITTED_OT: printf("<no value>"); return;
262 default: printf("???_"); break;
264 printf("%d", o.value);
267 extern void print_operand(assembly_operand o)
275 /* ------------------------------------------------------------------------- */
276 /* Writing bytes to the code area */
277 /* ------------------------------------------------------------------------- */
279 static void byteout(int32 i, int mv)
280 { if (zcode_ha_size >= MAX_ZCODE_SIZE)
281 memoryerror("MAX_ZCODE_SIZE",MAX_ZCODE_SIZE);
282 zcode_markers[zcode_ha_size] = (uchar) mv;
283 zcode_holding_area[zcode_ha_size++] = (uchar) i;
287 /* ------------------------------------------------------------------------- */
288 /* A database of the 115 canonical Infocom opcodes in Versions 3 to 6 */
289 /* And of the however-many-there-are Glulx opcode */
290 /* ------------------------------------------------------------------------- */
292 typedef struct opcodez
293 { uchar *name; /* Lower case standard name */
294 int version1; /* Valid from this version number... */
295 int version2; /* ...until this one (or forever if this is 0) */
296 int extension; /* In later versions, see this line in extension table:
297 if -1, the opcode is illegal in later versions */
298 int code; /* Opcode number within its operand-number block */
299 int flags; /* Flags (see below) */
300 int op_rules; /* Any unusual operand rule applying (see below) */
301 int flags2_set; /* If not zero, set this bit in Flags 2 in the header
302 of any game using the opcode */
303 int no; /* Number of operands (see below) */
306 typedef struct opcodeg
307 { uchar *name; /* Lower case standard name */
308 int32 code; /* Opcode number */
309 int flags; /* Flags (see below) */
310 int op_rules; /* Any unusual operand rule applying (see below) */
311 int no; /* Number of operands */
314 /* Flags which can be set */
316 #define St 1 /* Store */
317 #define Br 2 /* Branch */
318 #define Rf 4 /* "Return flag": execution never continues after this
319 opcode (e.g., is a return or unconditional jump) */
320 #define St2 8 /* Store2 (second-to-last operand is store (Glulx)) */
322 /* Codes for any unusual operand assembly rules */
326 #define VARIAB 1 /* First operand expected to be a variable name and
327 assembled to a short constant: the variable number */
328 #define TEXT 2 /* One text operand, to be Z-encoded into the program */
329 #define LABEL 3 /* One operand, a label, given as long constant offset */
330 #define CALL 4 /* First operand is name of a routine, to be assembled
331 as long constant (the routine's packed address):
332 as if the name were prefixed by #r$ */
334 /* Glulx: (bit flags for Glulx VM features) */
336 #define GOP_Unicode 1 /* uses_unicode_features */
337 #define GOP_MemHeap 2 /* uses_memheap_features */
338 #define GOP_Acceleration 4 /* uses_acceleration_features */
339 #define GOP_Float 8 /* uses_float_features */
341 /* Codes for the number of operands */
343 #define TWO 1 /* 2 (with certain types of operand, compiled as VAR) */
344 #define VAR 2 /* 0 to 4 */
345 #define VAR_LONG 3 /* 0 to 8 */
346 #define ONE 4 /* 1 */
347 #define ZERO 5 /* 0 */
348 #define EXT 6 /* Extended opcode set VAR: 0 to 4 */
349 #define EXT_LONG 7 /* Extended: 0 to 8 (not used by the canonical opcodes) */
351 static opcodez opcodes_table_z[] =
353 /* Opcodes introduced in Version 3 */
355 /* 0 */ { (uchar *) "je", 3, 0, -1, 0x01, Br, 0, 0, TWO },
356 /* 1 */ { (uchar *) "jl", 3, 0, -1, 0x02, Br, 0, 0, TWO },
357 /* 2 */ { (uchar *) "jg", 3, 0, -1, 0x03, Br, 0, 0, TWO },
358 /* 3 */ { (uchar *) "dec_chk", 3, 0, -1, 0x04, Br, VARIAB, 0, TWO },
359 /* 4 */ { (uchar *) "inc_chk", 3, 0, -1, 0x05, Br, VARIAB, 0, TWO },
360 /* 5 */ { (uchar *) "jin", 3, 0, -1, 0x06, Br, 0, 0, TWO },
361 /* 6 */ { (uchar *) "test", 3, 0, -1, 0x07, Br, 0, 0, TWO },
362 /* 7 */ { (uchar *) "or", 3, 0, -1, 0x08, St, 0, 0, TWO },
363 /* 8 */ { (uchar *) "and", 3, 0, -1, 0x09, St, 0, 0, TWO },
364 /* 9 */ { (uchar *) "test_attr", 3, 0, -1, 0x0A, Br, 0, 0, TWO },
365 /* 10 */ {(uchar *) "set_attr", 3, 0, -1, 0x0B, 0, 0, 0, TWO },
366 /* 11 */ {(uchar *) "clear_attr", 3, 0, -1, 0x0C, 0, 0, 0, TWO },
367 /* 12 */ {(uchar *) "store", 3, 0, -1, 0x0D, 0, VARIAB, 0, TWO },
368 /* 13 */ {(uchar *) "insert_obj", 3, 0, -1, 0x0E, 0, 0, 0, TWO },
369 /* 14 */ {(uchar *) "loadw", 3, 0, -1, 0x0F, St, 0, 0, TWO },
370 /* 15 */ {(uchar *) "loadb", 3, 0, -1, 0x10, St, 0, 0, TWO },
371 /* 16 */ {(uchar *) "get_prop", 3, 0, -1, 0x11, St, 0, 0, TWO },
372 /* 17 */ {(uchar *) "get_prop_addr", 3, 0, -1, 0x12, St, 0, 0, TWO },
373 /* 18 */ {(uchar *) "get_next_prop", 3, 0, -1, 0x13, St, 0, 0, TWO },
374 /* 19 */ {(uchar *) "add", 3, 0, -1, 0x14, St, 0, 0, TWO },
375 /* 20 */ {(uchar *) "sub", 3, 0, -1, 0x15, St, 0, 0, TWO },
376 /* 21 */ {(uchar *) "mul", 3, 0, -1, 0x16, St, 0, 0, TWO },
377 /* 22 */ {(uchar *) "div", 3, 0, -1, 0x17, St, 0, 0, TWO },
378 /* 23 */ {(uchar *) "mod", 3, 0, -1, 0x18, St, 0, 0, TWO },
379 /* 24 */ {(uchar *) "call", 3, 0, -1, 0x20, St, CALL, 0, VAR },
380 /* 25 */ {(uchar *) "storew", 3, 0, -1, 0x21, 0, 0, 0, VAR },
381 /* 26 */ {(uchar *) "storeb", 3, 0, -1, 0x22, 0, 0, 0, VAR },
382 /* 27 */ {(uchar *) "put_prop", 3, 0, -1, 0x23, 0, 0, 0, VAR },
383 /* This is the version of "read" called "sread" internally: */
384 /* 28 */ {(uchar *) "read", 3, 0, -1, 0x24, 0, 0, 0, VAR },
385 /* 29 */ {(uchar *) "print_char", 3, 0, -1, 0x25, 0, 0, 0, VAR },
386 /* 30 */ {(uchar *) "print_num", 3, 0, -1, 0x26, 0, 0, 0, VAR },
387 /* 31 */ {(uchar *) "random", 3, 0, -1, 0x27, St, 0, 0, VAR },
388 /* 32 */ {(uchar *) "push", 3, 0, -1, 0x28, 0, 0, 0, VAR },
389 /* 33 */ {(uchar *) "pull", 3, 5, 6, 0x29, 0, VARIAB, 0, VAR },
390 /* 34 */ {(uchar *) "split_window", 3, 0, -1, 0x2A, 0, 0, 0, VAR },
391 /* 35 */ {(uchar *) "set_window", 3, 0, -1, 0x2B, 0, 0, 0, VAR },
392 /* 36 */ {(uchar *) "output_stream", 3, 0, -1, 0x33, 0, 0, 0, VAR },
393 /* 37 */ {(uchar *) "input_stream", 3, 0, -1, 0x34, 0, 0, 0, VAR },
394 /* 38 */ {(uchar *) "sound_effect", 3, 0, -1, 0x35, 0, 0, 7, VAR },
395 /* 39 */ {(uchar *) "jz", 3, 0, -1, 0x00, Br, 0, 0, ONE },
396 /* 40 */ {(uchar *) "get_sibling", 3, 0, -1, 0x01, St+Br, 0, 0, ONE },
397 /* 41 */ {(uchar *) "get_child", 3, 0, -1, 0x02, St+Br, 0, 0, ONE },
398 /* 42 */ {(uchar *) "get_parent", 3, 0, -1, 0x03, St, 0, 0, ONE },
399 /* 43 */ {(uchar *) "get_prop_len", 3, 0, -1, 0x04, St, 0, 0, ONE },
400 /* 44 */ {(uchar *) "inc", 3, 0, -1, 0x05, 0, VARIAB, 0, ONE },
401 /* 45 */ {(uchar *) "dec", 3, 0, -1, 0x06, 0, VARIAB, 0, ONE },
402 /* 46 */ {(uchar *) "print_addr", 3, 0, -1, 0x07, 0, 0, 0, ONE },
403 /* 47 */ {(uchar *) "remove_obj", 3, 0, -1, 0x09, 0, 0, 0, ONE },
404 /* 48 */ {(uchar *) "print_obj", 3, 0, -1, 0x0A, 0, 0, 0, ONE },
405 /* 49 */ {(uchar *) "ret", 3, 0, -1, 0x0B, Rf, 0, 0, ONE },
406 /* 50 */ {(uchar *) "jump", 3, 0, -1, 0x0C, Rf, LABEL, 0, ONE },
407 /* 51 */ {(uchar *) "print_paddr", 3, 0, -1, 0x0D, 0, 0, 0, ONE },
408 /* 52 */ {(uchar *) "load", 3, 0, -1, 0x0E, St, VARIAB, 0, ONE },
409 /* 53 */ {(uchar *) "not", 3, 3, 0, 0x0F, St, 0, 0, ONE },
410 /* 54 */ {(uchar *) "rtrue", 3, 0, -1, 0x00, Rf, 0, 0,ZERO },
411 /* 55 */ {(uchar *) "rfalse", 3, 0, -1, 0x01, Rf, 0, 0,ZERO },
412 /* 56 */ {(uchar *) "print", 3, 0, -1, 0x02, 0, TEXT, 0,ZERO },
413 /* 57 */ {(uchar *) "print_ret", 3, 0, -1, 0x03, Rf, TEXT, 0,ZERO },
414 /* 58 */ {(uchar *) "nop", 3, 0, -1, 0x04, 0, 0, 0,ZERO },
415 /* 59 */ {(uchar *) "save", 3, 3, 1, 0x05, Br, 0, 0,ZERO },
416 /* 60 */ {(uchar *) "restore", 3, 3, 2, 0x06, Br, 0, 0,ZERO },
417 /* 61 */ {(uchar *) "restart", 3, 0, -1, 0x07, 0, 0, 0,ZERO },
418 /* 62 */ {(uchar *) "ret_popped", 3, 0, -1, 0x08, Rf, 0, 0,ZERO },
419 /* 63 */ {(uchar *) "pop", 3, 4, -1, 0x09, 0, 0, 0,ZERO },
420 /* 64 */ {(uchar *) "quit", 3, 0, -1, 0x0A, Rf, 0, 0,ZERO },
421 /* 65 */ {(uchar *) "new_line", 3, 0, -1, 0x0B, 0, 0, 0,ZERO },
422 /* 66 */ {(uchar *) "show_status", 3, 3, -1, 0x0C, 0, 0, 0,ZERO },
423 /* 67 */ {(uchar *) "verify", 3, 0, -1, 0x0D, Br, 0, 0,ZERO },
425 /* Opcodes introduced in Version 4 */
427 /* 68 */ {(uchar *) "call_2s", 4, 0, -1, 0x19, St, CALL, 0, TWO },
428 /* 69 */ {(uchar *) "call_vs", 4, 0, -1, 0x20, St, CALL, 0, VAR },
429 /* This is the version of "read" called "aread" internally: */
430 /* 70 */ {(uchar *) "read", 4, 0, -1, 0x24, St, 0, 0, VAR },
431 /* 71 */ {(uchar *) "call_vs2", 4, 0, -1, 0x2C, St, CALL, 0,
433 /* 72 */ {(uchar *) "erase_window", 4, 0, -1, 0x2D, 0, 0, 0, VAR },
434 /* 73 */ {(uchar *) "erase_line", 4, 0, -1, 0x2E, 0, 0, 0, VAR },
435 /* 74 */ {(uchar *) "set_cursor", 4, 0, -1, 0x2F, 0, 0, 0, VAR },
436 /* 75 */ {(uchar *) "get_cursor", 4, 0, -1, 0x30, 0, 0, 0, VAR },
437 /* 76 */ {(uchar *) "set_text_style", 4, 0, -1, 0x31, 0, 0, 0, VAR },
438 /* 77 */ {(uchar *) "buffer_mode", 4, 0, -1, 0x32, 0, 0, 0, VAR },
439 /* 78 */ {(uchar *) "read_char", 4, 0, -1, 0x36, St, 0, 0, VAR },
440 /* 79 */ {(uchar *) "scan_table", 4, 0, -1, 0x37, St+Br, 0, 0, VAR },
441 /* 80 */ {(uchar *) "call_1s", 4, 0, -1, 0x08, St, CALL, 0, ONE },
443 /* Opcodes introduced in Version 5 */
445 /* 81 */ {(uchar *) "call_2n", 5, 0, -1, 0x1a, 0, CALL, 0, TWO },
446 /* 82 */ {(uchar *) "set_colour", 5, 0, -1, 0x1b, 0, 0, 6, TWO },
447 /* 83 */ {(uchar *) "throw", 5, 0, -1, 0x1c, 0, 0, 0, TWO },
448 /* 84 */ {(uchar *) "call_vn", 5, 0, -1, 0x39, 0, CALL, 0, VAR },
449 /* 85 */ {(uchar *) "call_vn2", 5, 0, -1, 0x3a, 0, CALL, 0,
451 /* 86 */ {(uchar *) "tokenise", 5, 0, -1, 0x3b, 0, 0, 0, VAR },
452 /* 87 */ {(uchar *) "encode_text", 5, 0, -1, 0x3c, 0, 0, 0, VAR },
453 /* 88 */ {(uchar *) "copy_table", 5, 0, -1, 0x3d, 0, 0, 0, VAR },
454 /* 89 */ {(uchar *) "print_table", 5, 0, -1, 0x3e, 0, 0, 0, VAR },
455 /* 90 */ {(uchar *) "check_arg_count", 5, 0, -1, 0x3f, Br, 0, 0, VAR },
456 /* 91 */ {(uchar *) "call_1n", 5, 0, -1, 0x0F, 0, CALL, 0, ONE },
457 /* 92 */ {(uchar *) "catch", 5, 0, -1, 0x09, St, 0, 0, ZERO },
458 /* 93 */ {(uchar *) "piracy", 5, 0, -1, 0x0F, Br, 0, 0, ZERO },
459 /* 94 */ {(uchar *) "log_shift", 5, 0, -1, 0x02, St, 0, 0, EXT },
460 /* 95 */ {(uchar *) "art_shift", 5, 0, -1, 0x03, St, 0, 0, EXT },
461 /* 96 */ {(uchar *) "set_font", 5, 0, -1, 0x04, St, 0, 0, EXT },
462 /* 97 */ {(uchar *) "save_undo", 5, 0, -1, 0x09, St, 0, 4, EXT },
463 /* 98 */ {(uchar *) "restore_undo", 5, 0, -1, 0x0A, St, 0, 4, EXT },
465 /* Opcodes introduced in Version 6 */
467 /* 99 */ { (uchar *) "draw_picture", 6, 6, -1, 0x05, 0, 0, 3, EXT },
468 /* 100 */ { (uchar *) "picture_data", 6, 6, -1, 0x06, Br, 0, 3, EXT },
469 /* 101 */ { (uchar *) "erase_picture", 6, 6, -1, 0x07, 0, 0, 3, EXT },
470 /* 102 */ { (uchar *) "set_margins", 6, 6, -1, 0x08, 0, 0, 0, EXT },
471 /* 103 */ { (uchar *) "move_window", 6, 6, -1, 0x10, 0, 0, 0, EXT },
472 /* 104 */ { (uchar *) "window_size", 6, 6, -1, 0x11, 0, 0, 0, EXT },
473 /* 105 */ { (uchar *) "window_style", 6, 6, -1, 0x12, 0, 0, 0, EXT },
474 /* 106 */ { (uchar *) "get_wind_prop", 6, 6, -1, 0x13, St, 0, 0, EXT },
475 /* 107 */ { (uchar *) "scroll_window", 6, 6, -1, 0x14, 0, 0, 0, EXT },
476 /* 108 */ { (uchar *) "pop_stack", 6, 6, -1, 0x15, 0, 0, 0, EXT },
477 /* 109 */ { (uchar *) "read_mouse", 6, 6, -1, 0x16, 0, 0, 5, EXT },
478 /* 110 */ { (uchar *) "mouse_window", 6, 6, -1, 0x17, 0, 0, 5, EXT },
479 /* 111 */ { (uchar *) "push_stack", 6, 6, -1, 0x18, Br, 0, 0, EXT },
480 /* 112 */ { (uchar *) "put_wind_prop", 6, 6, -1, 0x19, 0, 0, 0, EXT },
481 /* 113 */ { (uchar *) "print_form", 6, 6, -1, 0x1a, 0, 0, 0, EXT },
482 /* 114 */ { (uchar *) "make_menu", 6, 6, -1, 0x1b, Br, 0, 8, EXT },
483 /* 115 */ { (uchar *) "picture_table", 6, 6, -1, 0x1c, 0, 0, 3, EXT },
485 /* Opcodes introduced in Z-Machine Specification Standard 1.0 */
487 /* 116 */ { (uchar *) "print_unicode", 5, 0, -1, 0x0b, 0, 0, 0, EXT },
488 /* 117 */ { (uchar *) "check_unicode", 5, 0, -1, 0x0c, St, 0, 0, EXT }
491 /* Subsequent forms for opcodes whose meaning changes with version */
493 static opcodez extension_table_z[] =
495 /* 0 */ { (uchar *) "not", 4, 4, 3, 0x0F, St, 0, 0, ONE },
496 /* 1 */ { (uchar *) "save", 4, 4, 4, 0x05, St, 0, 0,ZERO },
497 /* 2 */ { (uchar *) "restore", 4, 4, 5, 0x06, St, 0, 0,ZERO },
498 /* 3 */ { (uchar *) "not", 5, 0, -1, 0x38, St, 0, 0, VAR },
499 /* 4 */ { (uchar *) "save", 5, 0, -1, 0x00, St, 0, 0, EXT },
500 /* 5 */ { (uchar *) "restore", 5, 0, -1, 0x01, St, 0, 0, EXT },
501 /* 6 */ { (uchar *) "pull", 6, 6, -1, 0x29, St, 0, 0, VAR }
504 static opcodez invalid_opcode_z =
505 { (uchar *) "invalid", 0, 0, -1, 0xff, 0, 0, 0, ZERO};
507 static opcodez custom_opcode_z;
509 /* Note that this table assumes that all opcodes have at most two
510 branch-label or store operands, and that if they exist, they are the
511 last operands. Glulx does not actually guarantee this. But it is
512 true for all opcodes in the current Glulx spec, so we will assume
515 Also note that Inform can only compile branches to constant offsets,
516 even though the Glulx machine can handle stack or memory-loaded
517 operands in a branch instruction.
520 static opcodeg opcodes_table_g[] = {
521 { (uchar *) "nop", 0x00, 0, 0, 0 },
522 { (uchar *) "add", 0x10, St, 0, 3 },
523 { (uchar *) "sub", 0x11, St, 0, 3 },
524 { (uchar *) "mul", 0x12, St, 0, 3 },
525 { (uchar *) "div", 0x13, St, 0, 3 },
526 { (uchar *) "mod", 0x14, St, 0, 3 },
527 { (uchar *) "neg", 0x15, St, 0, 2 },
528 { (uchar *) "bitand", 0x18, St, 0, 3 },
529 { (uchar *) "bitor", 0x19, St, 0, 3 },
530 { (uchar *) "bitxor", 0x1A, St, 0, 3 },
531 { (uchar *) "bitnot", 0x1B, St, 0, 2 },
532 { (uchar *) "shiftl", 0x1C, St, 0, 3 },
533 { (uchar *) "sshiftr", 0x1D, St, 0, 3 },
534 { (uchar *) "ushiftr", 0x1E, St, 0, 3 },
535 { (uchar *) "jump", 0x20, Br|Rf, 0, 1 },
536 { (uchar *) "jz", 0x22, Br, 0, 2 },
537 { (uchar *) "jnz", 0x23, Br, 0, 2 },
538 { (uchar *) "jeq", 0x24, Br, 0, 3 },
539 { (uchar *) "jne", 0x25, Br, 0, 3 },
540 { (uchar *) "jlt", 0x26, Br, 0, 3 },
541 { (uchar *) "jge", 0x27, Br, 0, 3 },
542 { (uchar *) "jgt", 0x28, Br, 0, 3 },
543 { (uchar *) "jle", 0x29, Br, 0, 3 },
544 { (uchar *) "jltu", 0x2A, Br, 0, 3 },
545 { (uchar *) "jgeu", 0x2B, Br, 0, 3 },
546 { (uchar *) "jgtu", 0x2C, Br, 0, 3 },
547 { (uchar *) "jleu", 0x2D, Br, 0, 3 },
548 { (uchar *) "call", 0x30, St, 0, 3 },
549 { (uchar *) "return", 0x31, Rf, 0, 1 },
550 { (uchar *) "catch", 0x32, Br|St, 0, 2 },
551 { (uchar *) "throw", 0x33, Rf, 0, 2 },
552 { (uchar *) "tailcall", 0x34, Rf, 0, 2 },
553 { (uchar *) "copy", 0x40, St, 0, 2 },
554 { (uchar *) "copys", 0x41, St, 0, 2 },
555 { (uchar *) "copyb", 0x42, St, 0, 2 },
556 { (uchar *) "sexs", 0x44, St, 0, 2 },
557 { (uchar *) "sexb", 0x45, St, 0, 2 },
558 { (uchar *) "aload", 0x48, St, 0, 3 },
559 { (uchar *) "aloads", 0x49, St, 0, 3 },
560 { (uchar *) "aloadb", 0x4A, St, 0, 3 },
561 { (uchar *) "aloadbit", 0x4B, St, 0, 3 },
562 { (uchar *) "astore", 0x4C, 0, 0, 3 },
563 { (uchar *) "astores", 0x4D, 0, 0, 3 },
564 { (uchar *) "astoreb", 0x4E, 0, 0, 3 },
565 { (uchar *) "astorebit", 0x4F, 0, 0, 3 },
566 { (uchar *) "stkcount", 0x50, St, 0, 1 },
567 { (uchar *) "stkpeek", 0x51, St, 0, 2 },
568 { (uchar *) "stkswap", 0x52, 0, 0, 0 },
569 { (uchar *) "stkroll", 0x53, 0, 0, 2 },
570 { (uchar *) "stkcopy", 0x54, 0, 0, 1 },
571 { (uchar *) "streamchar", 0x70, 0, 0, 1 },
572 { (uchar *) "streamnum", 0x71, 0, 0, 1 },
573 { (uchar *) "streamstr", 0x72, 0, 0, 1 },
574 { (uchar *) "gestalt", 0x0100, St, 0, 3 },
575 { (uchar *) "debugtrap", 0x0101, 0, 0, 1 },
576 { (uchar *) "getmemsize", 0x0102, St, 0, 1 },
577 { (uchar *) "setmemsize", 0x0103, St, 0, 2 },
578 { (uchar *) "jumpabs", 0x0104, Rf, 0, 1 },
579 { (uchar *) "random", 0x0110, St, 0, 2 },
580 { (uchar *) "setrandom", 0x0111, 0, 0, 1 },
581 { (uchar *) "quit", 0x0120, Rf, 0, 0 },
582 { (uchar *) "verify", 0x0121, St, 0, 1 },
583 { (uchar *) "restart", 0x0122, 0, 0, 0 },
584 { (uchar *) "save", 0x0123, St, 0, 2 },
585 { (uchar *) "restore", 0x0124, St, 0, 2 },
586 { (uchar *) "saveundo", 0x0125, St, 0, 1 },
587 { (uchar *) "restoreundo", 0x0126, St, 0, 1 },
588 { (uchar *) "protect", 0x0127, 0, 0, 2 },
589 { (uchar *) "glk", 0x0130, St, 0, 3 },
590 { (uchar *) "getstringtbl", 0x0140, St, 0, 1 },
591 { (uchar *) "setstringtbl", 0x0141, 0, 0, 1 },
592 { (uchar *) "getiosys", 0x0148, St|St2, 0, 2 },
593 { (uchar *) "setiosys", 0x0149, 0, 0, 2 },
594 { (uchar *) "linearsearch", 0x0150, St, 0, 8 },
595 { (uchar *) "binarysearch", 0x0151, St, 0, 8 },
596 { (uchar *) "linkedsearch", 0x0152, St, 0, 7 },
597 { (uchar *) "callf", 0x0160, St, 0, 2 },
598 { (uchar *) "callfi", 0x0161, St, 0, 3 },
599 { (uchar *) "callfii", 0x0162, St, 0, 4 },
600 { (uchar *) "callfiii", 0x0163, St, 0, 5 },
601 { (uchar *) "streamunichar", 0x73, 0, GOP_Unicode, 1 },
602 { (uchar *) "mzero", 0x170, 0, GOP_MemHeap, 2 },
603 { (uchar *) "mcopy", 0x171, 0, GOP_MemHeap, 3 },
604 { (uchar *) "malloc", 0x178, St, GOP_MemHeap, 2 },
605 { (uchar *) "mfree", 0x179, 0, GOP_MemHeap, 1 },
606 { (uchar *) "accelfunc", 0x180, 0, GOP_Acceleration, 2 },
607 { (uchar *) "accelparam", 0x181, 0, GOP_Acceleration, 2 },
608 { (uchar *) "numtof", 0x190, St, GOP_Float, 2 },
609 { (uchar *) "ftonumz", 0x191, St, GOP_Float, 2 },
610 { (uchar *) "ftonumn", 0x192, St, GOP_Float, 2 },
611 { (uchar *) "ceil", 0x198, St, GOP_Float, 2 },
612 { (uchar *) "floor", 0x199, St, GOP_Float, 2 },
613 { (uchar *) "fadd", 0x1A0, St, GOP_Float, 3 },
614 { (uchar *) "fsub", 0x1A1, St, GOP_Float, 3 },
615 { (uchar *) "fmul", 0x1A2, St, GOP_Float, 3 },
616 { (uchar *) "fdiv", 0x1A3, St, GOP_Float, 3 },
617 { (uchar *) "fmod", 0x1A4, St|St2, GOP_Float, 4 },
618 { (uchar *) "sqrt", 0x1A8, St, GOP_Float, 2 },
619 { (uchar *) "exp", 0x1A9, St, GOP_Float, 2 },
620 { (uchar *) "log", 0x1AA, St, GOP_Float, 2 },
621 { (uchar *) "pow", 0x1AB, St, GOP_Float, 3 },
622 { (uchar *) "sin", 0x1B0, St, GOP_Float, 2 },
623 { (uchar *) "cos", 0x1B1, St, GOP_Float, 2 },
624 { (uchar *) "tan", 0x1B2, St, GOP_Float, 2 },
625 { (uchar *) "asin", 0x1B3, St, GOP_Float, 2 },
626 { (uchar *) "acos", 0x1B4, St, GOP_Float, 2 },
627 { (uchar *) "atan", 0x1B5, St, GOP_Float, 2 },
628 { (uchar *) "atan2", 0x1B6, St, GOP_Float, 3 },
629 { (uchar *) "jfeq", 0x1C0, Br, GOP_Float, 4 },
630 { (uchar *) "jfne", 0x1C1, Br, GOP_Float, 4 },
631 { (uchar *) "jflt", 0x1C2, Br, GOP_Float, 3 },
632 { (uchar *) "jfle", 0x1C3, Br, GOP_Float, 3 },
633 { (uchar *) "jfgt", 0x1C4, Br, GOP_Float, 3 },
634 { (uchar *) "jfge", 0x1C5, Br, GOP_Float, 3 },
635 { (uchar *) "jisnan", 0x1C8, Br, GOP_Float, 2 },
636 { (uchar *) "jisinf", 0x1C9, Br, GOP_Float, 2 },
639 /* The opmacros table is used for fake opcodes. The opcode numbers are
640 ignored; this table is only used for argument parsing. */
641 static opcodeg opmacros_table_g[] = {
642 { (uchar *) "pull", 0, St, 0, 1 },
643 { (uchar *) "push", 0, 0, 0, 1 },
646 static opcodeg custom_opcode_g;
648 static opcodez internal_number_to_opcode_z(int32 i)
651 if (i == -1) return custom_opcode_z;
652 x = opcodes_table_z[i];
653 if (instruction_set_number < x.version1) return invalid_opcode_z;
654 if (x.version2 == 0) return x;
655 if (instruction_set_number <= x.version2) return x;
657 if (i < 0) return invalid_opcode_z;
658 x = extension_table_z[i];
659 if (instruction_set_number < x.version1) return invalid_opcode_z;
660 if (x.version2 == 0) return x;
661 if (instruction_set_number <= x.version2) return x;
662 return extension_table_z[x.extension];
665 static void make_opcode_syntax_z(opcodez opco)
666 { char *p = "", *q = opcode_syntax_string;
667 sprintf(q, "%s", opco.name);
669 { case ONE: p=" <operand>"; break;
670 case TWO: p=" <operand1> <operand2>"; break;
672 case VAR: p=" <0 to 4 operands>"; break;
673 case VAR_LONG: p=" <0 to 8 operands>"; break;
675 switch(opco.op_rules)
676 { case TEXT: sprintf(q+strlen(q), " <text>"); return;
677 case LABEL: sprintf(q+strlen(q), " <label>"); return;
679 sprintf(q+strlen(q), " <variable>");
681 if (opco.op_rules==CALL) sprintf(q+strlen(q), " <routine>");
683 { case ONE: p=""; break;
684 case TWO: p=" <operand>"; break;
686 case VAR: p=" <1 to 4 operands>"; break;
687 case VAR_LONG: p=" <1 to 8 operands>"; break;
691 sprintf(q+strlen(q), "%s", p);
692 if ((opco.flags & St) != 0) sprintf(q+strlen(q), " -> <result-variable>");
693 if ((opco.flags & Br) != 0) sprintf(q+strlen(q), " ?[~]<label>");
696 static opcodeg internal_number_to_opcode_g(int32 i)
699 if (i == -1) return custom_opcode_g;
700 x = opcodes_table_g[i];
704 static opcodeg internal_number_to_opmacro_g(int32 i)
706 return opmacros_table_g[i];
709 static void make_opcode_syntax_g(opcodeg opco)
713 char *q = opcode_syntax_string;
715 sprintf(q, "%s", opco.name);
716 sprintf(q+strlen(q), " <%d operand%s", opco.no,
717 ((opco.no==1) ? "" : "s"));
722 for (ix=0; ix<opco.no; ix++) {
727 if (ix == opco.no-1) {
728 if (opco.flags & Br) {
731 else if (opco.flags & St) {
738 else if (ix == opco.no-2 && (opco.flags & Br) && (opco.flags & St)) {
741 else if (ix == opco.no-2 && (opco.flags & St2)) {
748 sprintf(cx, "%d", ix+1);
752 sprintf(q+strlen(q), ">");
756 /* ========================================================================= */
757 /* The assembler itself does four things: */
759 /* assembles instructions */
760 /* sets label N to the current code position */
761 /* assembles routine headers */
762 /* assembles routine ends */
763 /* ------------------------------------------------------------------------- */
765 /* This is for Z-code only. */
766 static void write_operand(assembly_operand op)
768 if (module_switch && (op.marker != 0))
769 { if ((op.marker != VARIABLE_MV) && (op.type == SHORT_CONSTANT_OT))
770 op.type = LONG_CONSTANT_OT;
774 { case LONG_CONSTANT_OT:
775 byteout(j/256, op.marker); byteout(j%256, 0); return;
776 case SHORT_CONSTANT_OT:
779 else byteout(j, 0x80 + op.marker); return;
781 byteout(j, (module_switch)?(0x80 + op.marker):0); return;
783 case HALFCONSTANT_OT:
784 case BYTECONSTANT_OT:
785 case ZEROCONSTANT_OT:
790 compiler_error("Glulx OT in Z-code assembly operand.");
795 extern void assemblez_instruction(assembly_instruction *AI)
797 uchar *start_pc, *operands_pc;
798 int32 offset, j, topbits=0, types_byte1, types_byte2;
799 int operand_rules, min=0, max=0, no_operands_given, at_seq_point = FALSE;
800 assembly_operand o1, o2;
805 offset = zmachine_pc;
809 if (veneer_mode) sequence_point_follows = FALSE;
810 if (sequence_point_follows)
811 { sequence_point_follows = FALSE; at_seq_point = TRUE;
812 if (debugfile_switch)
813 { sequence_point_labels[next_sequence_point] = next_label;
814 sequence_point_locations[next_sequence_point] =
815 statement_debug_location;
816 set_label_offset(next_label++, zmachine_pc);
818 next_sequence_point++;
821 opco = internal_number_to_opcode_z(AI->internal_number);
822 if (opco.version1==0)
823 { error_named("Opcode unavailable in this Z-machine version",
824 opcode_names.keywords[AI->internal_number]);
828 if (execution_never_reaches_here)
829 warning("This statement can never be reached");
831 operand_rules = opco.op_rules;
832 execution_never_reaches_here = ((opco.flags & Rf) != 0);
834 if (opco.flags2_set != 0) flags2_requirements[opco.flags2_set] = 1;
836 no_operands_given = AI->operand_count;
838 if ((opco.no == TWO) && ((no_operands_given==3)||(no_operands_given==4)))
841 /* 1. Write the opcode byte(s) */
843 start_pc = zcode_holding_area + zcode_ha_size;
846 { case VAR_LONG: topbits=0xc0; min=0; max=8; break;
847 case VAR: topbits=0xc0; min=0; max=4; break;
848 case ZERO: topbits=0xb0; min=0; max=0; break;
849 case ONE: topbits=0x80; min=1; max=1; break;
850 case TWO: topbits=0x00; min=2; max=2; break;
851 case EXT: topbits=0x00; min=0; max=4;
852 byteout(0xbe, 0); opco.no=VAR; break;
853 case EXT_LONG: topbits=0x00; min=0; max=8;
854 byteout(0xbe, 0); opco.no=VAR_LONG; break;
856 byteout(opco.code + topbits, 0);
858 operands_pc = zcode_holding_area + zcode_ha_size;
860 /* 2. Dispose of the special rules LABEL and TEXT */
862 if (operand_rules==LABEL)
863 { j = (AI->operand[0]).value;
864 byteout(j/256, LABEL_MV); byteout(j%256, 0);
865 goto Instruction_Done;
868 if (operand_rules==TEXT)
870 uchar *tmp = translate_text(zcode_holding_area + zcode_ha_size, zcode_holding_area+MAX_ZCODE_SIZE, AI->text);
872 memoryerror("MAX_ZCODE_SIZE", MAX_ZCODE_SIZE);
873 j = subtract_pointers(tmp, (zcode_holding_area + zcode_ha_size));
874 for (i=0; i<j; i++) zcode_markers[zcode_ha_size++] = 0;
876 goto Instruction_Done;
879 /* 3. Sort out the operands */
881 if ((no_operands_given < min) || (no_operands_given > max))
882 goto OpcodeSyntaxError;
888 if (opco.no == VAR_LONG) byteout(0, 0);
889 types_byte1=0xff; types_byte2=0xff;
890 for (j=0; j<no_operands_given; j++)
891 { int multi=0, mask=0;
893 { case 0: case 4: multi=0x40; mask=0xc0; break;
894 case 1: case 5: multi=0x10; mask=0x30; break;
895 case 2: case 6: multi=0x04; mask=0x0c; break;
896 case 3: case 7: multi=0x01; mask=0x03; break;
901 types_byte1 = (types_byte1 & (~mask)) + o1.type*multi;
903 types_byte2 = (types_byte2 & (~mask)) + o1.type*multi;
905 *operands_pc=types_byte1;
906 if (opco.no == VAR_LONG) *(operands_pc+1)=types_byte2;
911 *start_pc=(*start_pc) + o1.type*0x10;
919 /* Transfer to VAR form if either operand is a long constant */
921 if ((o1.type==LONG_CONSTANT_OT)||(o2.type==LONG_CONSTANT_OT))
922 { *start_pc=(*start_pc) + 0xc0;
923 byteout(o1.type*0x40 + o2.type*0x10 + 0x0f, 0);
926 { if (o1.type==VARIABLE_OT) *start_pc=(*start_pc) + 0x40;
927 if (o2.type==VARIABLE_OT) *start_pc=(*start_pc) + 0x20;
934 /* 4. Assemble a Store destination, if needed */
936 if ((AI->store_variable_number) != -1)
937 { if (AI->store_variable_number >= MAX_LOCAL_VARIABLES+MAX_GLOBAL_VARIABLES) {
938 goto OpcodeSyntaxError;
940 o1.type = VARIABLE_OT;
941 o1.value = AI->store_variable_number;
942 variable_usage[o1.value] = TRUE;
945 /* Note that variable numbers 249 to 255 (i.e. globals 233 to 239)
946 are used as scratch workspace, so need no mapping between
947 modules and story files: nor do local variables 0 to 15 */
949 if ((o1.value >= MAX_LOCAL_VARIABLES) && (o1.value < 249))
950 o1.marker = VARIABLE_MV;
954 /* 5. Assemble a branch, if needed */
956 if (AI->branch_label_number != -1)
957 { int32 addr, long_form;
958 int branch_on_true = (AI->branch_flag)?1:0;
960 switch (AI->branch_label_number)
961 { case -2: addr = 2; branch_on_true = 0; long_form = 0; break;
962 /* branch nowhere, carry on */
963 case -3: addr = 0; long_form = 0; break; /* rfalse on condition */
964 case -4: addr = 1; long_form = 0; break; /* rtrue on condition */
966 long_form = 1; addr = AI->branch_label_number;
969 if (addr > 0x7fff) fatalerror("Too many branch points in routine.");
971 { byteout(branch_on_true*0x80 + addr/256, BRANCH_MV);
972 byteout(addr%256, 0);
975 byteout(branch_on_true*0x80+ 0x40 + (addr&0x3f), 0);
980 if (asm_trace_level > 0)
982 printf("%5d +%05lx %3s %-12s ", ErrorReport.line_number,
984 (at_seq_point)?"<*>":" ", opco.name);
986 if ((AI->internal_number == print_zc)
987 || (AI->internal_number == print_ret_zc))
989 for (i=0;(AI->text)[i]!=0 && i<35; i++) printf("%c",(AI->text)[i]);
990 if (i == 35) printf("...");
994 for (i=0; i<AI->operand_count; i++)
995 { if ((i==0) && (opco.op_rules == VARIAB))
996 { if ((AI->operand[0]).type == VARIABLE_OT)
997 { printf("["); print_operand_z(AI->operand[i]); }
999 printf("%s", variable_name((AI->operand[0]).value));
1002 if ((i==0) && (opco.op_rules == LABEL))
1003 { printf("L%d", AI->operand[0].value);
1005 else print_operand_z(AI->operand[i]);
1008 if (AI->store_variable_number != -1)
1009 { assembly_operand AO;
1011 AO.type = VARIABLE_OT; AO.value = AI->store_variable_number;
1012 print_operand_z(AO); printf(" ");
1015 switch(AI->branch_label_number)
1016 { case -4: printf("rtrue if %s", (AI->branch_flag)?"TRUE":"FALSE");
1018 case -3: printf("rfalse if %s", (AI->branch_flag)?"TRUE":"FALSE");
1020 case -2: printf("(no branch)"); break;
1023 printf("to L%d if %s", AI->branch_label_number,
1024 (AI->branch_flag)?"TRUE":"FALSE"); break;
1027 if (asm_trace_level>=2)
1028 { for (j=0;start_pc<zcode_holding_area + zcode_ha_size;
1030 { if (j%16==0) printf("\n ");
1031 printf("%02x ", *start_pc);
1037 if (module_switch) flush_link_data();
1043 make_opcode_syntax_z(opco);
1044 error_named("Assembly mistake: syntax is", opcode_syntax_string);
1047 static void assembleg_macro(assembly_instruction *AI)
1049 /* validate macro syntax first */
1050 int ix, no_operands_given;
1053 opco = internal_number_to_opmacro_g(AI->internal_number);
1054 no_operands_given = AI->operand_count;
1056 if (no_operands_given != opco.no)
1057 goto OpcodeSyntaxError;
1059 for (ix = 0; ix < no_operands_given; ix++) {
1060 int type = AI->operand[ix].type;
1061 if ((opco.flags & St)
1062 && ((!(opco.flags & Br) && (ix == no_operands_given-1))
1063 || ((opco.flags & Br) && (ix == no_operands_given-2)))) {
1064 if (is_constant_ot(type)) {
1065 error("*** assembly macro tried to store to a constant ***");
1066 goto OpcodeSyntaxError;
1069 if ((opco.flags & St2)
1070 && (ix == no_operands_given-2)) {
1071 if (is_constant_ot(type)) {
1072 error("*** assembly macro tried to store to a constant ***");
1073 goto OpcodeSyntaxError;
1078 /* expand the macro */
1079 switch (AI->internal_number) {
1081 assembleg_store(AI->operand[0], stack_pointer);
1085 assembleg_store(stack_pointer, AI->operand[0]);
1089 compiler_error("Invalid Glulx assembly macro");
1097 make_opcode_syntax_g(opco);
1098 error_named("Assembly mistake: syntax is", opcode_syntax_string);
1101 extern void assembleg_instruction(assembly_instruction *AI)
1103 uchar *start_pc, *opmodes_pc;
1105 int no_operands_given, at_seq_point = FALSE;
1111 offset = zmachine_pc;
1115 if (veneer_mode) sequence_point_follows = FALSE;
1116 if (sequence_point_follows)
1117 { sequence_point_follows = FALSE; at_seq_point = TRUE;
1118 if (debugfile_switch)
1119 { sequence_point_labels[next_sequence_point] = next_label;
1120 sequence_point_locations[next_sequence_point] =
1121 statement_debug_location;
1122 set_label_offset(next_label++, zmachine_pc);
1124 next_sequence_point++;
1127 opco = internal_number_to_opcode_g(AI->internal_number);
1129 if (execution_never_reaches_here)
1130 warning("This statement can never be reached");
1132 execution_never_reaches_here = ((opco.flags & Rf) != 0);
1134 if (opco.op_rules & GOP_Unicode) {
1135 uses_unicode_features = TRUE;
1137 if (opco.op_rules & GOP_MemHeap) {
1138 uses_memheap_features = TRUE;
1140 if (opco.op_rules & GOP_Acceleration) {
1141 uses_acceleration_features = TRUE;
1143 if (opco.op_rules & GOP_Float) {
1144 uses_float_features = TRUE;
1147 no_operands_given = AI->operand_count;
1149 /* 1. Write the opcode byte(s) */
1151 start_pc = zcode_holding_area + zcode_ha_size;
1153 if (opco.code < 0x80) {
1154 byteout(opco.code, 0);
1156 else if (opco.code < 0x4000) {
1157 byteout(((opco.code >> 8) & 0xFF) | 0x80, 0);
1158 byteout((opco.code & 0xFF), 0);
1161 byteout(((opco.code >> 24) & 0xFF) | 0xC0, 0);
1162 byteout(((opco.code >> 16) & 0xFF), 0);
1163 byteout(((opco.code >> 8) & 0xFF), 0);
1164 byteout(((opco.code) & 0xFF), 0);
1167 /* ... and the operand addressing modes. There's one byte for
1168 every two operands (rounded up). We write zeroes for now;
1169 when the operands are written, we'll go back and fix them. */
1171 opmodes_pc = zcode_holding_area + zcode_ha_size;
1173 for (ix=0; ix<opco.no; ix+=2) {
1177 /* 2. Dispose of the special rules */
1178 /* There aren't any in Glulx. */
1180 /* 3. Sort out the operands */
1182 if (no_operands_given != opco.no) {
1183 goto OpcodeSyntaxError;
1186 for (ix=0; ix<no_operands_given; ix++) {
1187 int marker = AI->operand[ix].marker;
1188 int type = AI->operand[ix].type;
1189 k = AI->operand[ix].value;
1191 if ((opco.flags & Br) && (ix == no_operands_given-1)) {
1192 if (!(marker >= BRANCH_MV && marker < BRANCHMAX_MV)) {
1193 compiler_error("Assembling branch without BRANCH_MV marker");
1194 goto OpcodeSyntaxError;
1197 k = 2; /* branch no-op */
1198 type = BYTECONSTANT_OT;
1202 k = 0; /* branch return 0 */
1203 type = ZEROCONSTANT_OT;
1207 k = 1; /* branch return 1 */
1208 type = BYTECONSTANT_OT;
1212 /* branch to label k */
1213 j = subtract_pointers((zcode_holding_area + zcode_ha_size),
1216 marker = BRANCH_MV + j;
1217 if (!(marker >= BRANCH_MV && marker < BRANCHMAX_MV)) {
1218 error("*** branch marker too far from opmode byte ***");
1219 goto OpcodeSyntaxError;
1223 if ((opco.flags & St)
1224 && ((!(opco.flags & Br) && (ix == no_operands_given-1))
1225 || ((opco.flags & Br) && (ix == no_operands_given-2)))) {
1226 if (type == BYTECONSTANT_OT || type == HALFCONSTANT_OT
1227 || type == CONSTANT_OT) {
1228 error("*** instruction tried to store to a constant ***");
1229 goto OpcodeSyntaxError;
1232 if ((opco.flags & St2)
1233 && (ix == no_operands_given-2)) {
1234 if (type == BYTECONSTANT_OT || type == HALFCONSTANT_OT
1235 || type == CONSTANT_OT) {
1236 error("*** instruction tried to store to a constant ***");
1237 goto OpcodeSyntaxError;
1241 if (marker && (type == HALFCONSTANT_OT
1242 || type == BYTECONSTANT_OT
1243 || type == ZEROCONSTANT_OT)) {
1244 compiler_error("Assembling marker in less than 32-bit constant.");
1245 /* Actually we should store marker|0x80 for a byte constant,
1246 but let's hold off on that. */
1250 case LONG_CONSTANT_OT:
1251 case SHORT_CONSTANT_OT:
1254 compiler_error("Z-code OT in Glulx assembly operand.");
1258 byteout((k >> 24) & 0xFF, marker);
1259 byteout((k >> 16) & 0xFF, 0);
1260 byteout((k >> 8) & 0xFF, 0);
1261 byteout((k & 0xFF), 0);
1263 case HALFCONSTANT_OT:
1265 byteout((k >> 8) & 0xFF, marker);
1266 byteout((k & 0xFF), 0);
1268 case BYTECONSTANT_OT:
1270 byteout((k & 0xFF), marker);
1272 case ZEROCONSTANT_OT:
1275 case DEREFERENCE_OT:
1277 byteout((k >> 24) & 0xFF, marker);
1278 byteout((k >> 16) & 0xFF, 0);
1279 byteout((k >> 8) & 0xFF, 0);
1280 byteout((k & 0xFF), 0);
1283 /* Global variable -- a constant address. */
1284 k -= MAX_LOCAL_VARIABLES;
1285 if (/* DISABLES CODE */ (0)) {
1286 /* We could write the value as a marker and patch it later... */
1288 byteout(((k) >> 24) & 0xFF, VARIABLE_MV);
1289 byteout(((k) >> 16) & 0xFF, 0);
1290 byteout(((k) >> 8) & 0xFF, 0);
1291 byteout(((k) & 0xFF), 0);
1294 /* ...but it's more efficient to write it as a RAM operand,
1295 which can be 1, 2, or 4 bytes. Remember that global variables
1296 are the very first thing in RAM. */
1297 k = k * 4; /* each variable is four bytes */
1300 byteout(((k) & 0xFF), 0);
1302 else if (k <= 65535) {
1304 byteout(((k) >> 8) & 0xFF, 0);
1305 byteout(((k) & 0xFF), 0);
1309 byteout(((k) >> 24) & 0xFF, 0);
1310 byteout(((k) >> 16) & 0xFF, 0);
1311 byteout(((k) >> 8) & 0xFF, 0);
1312 byteout(((k) & 0xFF), 0);
1318 /* Stack-pointer magic variable */
1322 /* Local variable -- a byte or short offset from the
1323 frame pointer. It's an unsigned offset, so we can
1324 fit up to long 63 (offset 4*63) in a byte. */
1327 byteout((k-1)*4, 0);
1331 byteout((((k-1)*4) >> 8) & 0xFF, 0);
1332 byteout(((k-1)*4) & 0xFF, 0);
1343 opmodes_pc[ix/2] |= j;
1346 /* Print assembly trace. */
1347 if (asm_trace_level > 0) {
1349 printf("%5d +%05lx %3s %-12s ", ErrorReport.line_number,
1350 ((long int) offset),
1351 (at_seq_point)?"<*>":" ", opco.name);
1352 for (i=0; i<AI->operand_count; i++) {
1353 if ((opco.flags & Br) && (i == opco.no-1)) {
1354 if (AI->operand[i].value == -4)
1356 else if (AI->operand[i].value == -3)
1357 printf("to rfalse");
1359 printf("to L%d", AI->operand[i].value);
1362 print_operand_g(AI->operand[i]);
1367 if (asm_trace_level>=2) {
1369 start_pc<zcode_holding_area + zcode_ha_size;
1371 if (j%16==0) printf("\n ");
1372 if (/* DISABLES CODE */ (0)) {
1373 printf("%02x ", *start_pc);
1376 printf("%02x", *start_pc);
1377 if (zcode_markers[start_pc-zcode_holding_area])
1378 printf("{%02x}", zcode_markers[start_pc-zcode_holding_area]);
1386 if (module_switch) flush_link_data();
1392 make_opcode_syntax_g(opco);
1393 error_named("Assembly mistake: syntax is", opcode_syntax_string);
1396 extern void assemble_label_no(int n)
1398 if (asm_trace_level > 0)
1399 printf("%5d +%05lx .L%d\n", ErrorReport.line_number,
1400 ((long int) zmachine_pc), n);
1401 set_label_offset(n, zmachine_pc);
1402 execution_never_reaches_here = FALSE;
1405 extern void define_symbol_label(int symbol)
1406 { label_symbols[svals[symbol]] = symbol;
1409 extern int32 assemble_routine_header(int no_locals,
1410 int routine_asterisked, char *name, int embedded_flag, int the_symbol)
1412 int stackargs = FALSE;
1415 execution_never_reaches_here = FALSE;
1417 routine_locals = no_locals;
1418 for (i=0; i<MAX_LOCAL_VARIABLES; i++) variable_usage[i] = FALSE;
1421 && !strcmp(local_variables.keywords[0], "_vararg_count")) {
1425 if (veneer_mode) routine_starts_line = blank_brief_location;
1426 else routine_starts_line = get_brief_location(&ErrorReport);
1428 if (asm_trace_level > 0)
1429 { printf("\n%5d +%05lx [ %s ", ErrorReport.line_number,
1430 ((long int) zmachine_pc), name);
1431 for (i=1; i<=no_locals; i++) printf("%s ", variable_name(i));
1435 routine_start_pc = zmachine_pc;
1437 if (track_unused_routines) {
1438 /* The name of an embedded function is in a temporary buffer,
1439 so we shouldn't keep a reference to it. (It is sad that we
1440 have to know this here.) */
1441 char *funcname = name;
1443 funcname = "<embedded>";
1445 df_note_function_start(funcname, zmachine_pc, embedded_flag,
1446 routine_starts_line);
1449 routine_symbol = the_symbol;
1450 name_length = strlen(name) + 1;
1452 my_malloc(name_length * sizeof(char), "temporary copy of routine name");
1453 strncpy(routine_name, name, name_length);
1455 /* Update the routine counter */
1459 /* Actually assemble the routine header into the code area; note */
1460 /* Inform doesn't support the setting of local variables to default */
1461 /* values other than 0 in V3 and V4. (In V5+ the Z-Machine doesn't */
1462 /* provide the possibility in any case.) */
1467 warning("Z-code does not support stack-argument function definitions.");
1469 byteout(no_locals, 0);
1471 /* Not the packed address, but the scaled offset from code area start: */
1473 rv = zmachine_pc/scale_factor;
1475 if (instruction_set_number<5)
1476 for (i=0; i<no_locals; i++) { byteout(0,0); byteout(0,0); }
1478 next_label = 0; next_sequence_point = 0; last_label = -1;
1480 /* Compile code to print out text like "a=3, b=4, c=5" when the */
1481 /* function is called, if it's required. */
1483 if ((routine_asterisked) || (define_INFIX_switch))
1484 { char fnt[256]; assembly_operand PV, RFA, CON, STP, SLF; int ln, ln2;
1489 if (define_INFIX_switch)
1492 { SLF.value = 251; SLF.type = VARIABLE_OT; SLF.marker = 0;
1493 CON.value = 0; CON.type = SHORT_CONSTANT_OT; CON.marker = 0;
1494 assemblez_2_branch(test_attr_zc, SLF, CON, ln2, FALSE);
1497 { i = no_named_routines++;
1498 named_routine_symbols[i] = the_symbol;
1499 CON.value = i/8; CON.type = LONG_CONSTANT_OT; CON.marker = 0;
1500 RFA.value = routine_flags_array_SC;
1501 RFA.type = LONG_CONSTANT_OT; RFA.marker = INCON_MV;
1502 STP.value = 0; STP.type = VARIABLE_OT; STP.marker = 0;
1503 assemblez_2_to(loadb_zc, RFA, CON, STP);
1504 CON.value = (1 << (i%8)); CON.type = SHORT_CONSTANT_OT;
1505 assemblez_2_to(and_zc, STP, CON, STP);
1506 assemblez_1_branch(jz_zc, STP, ln2, TRUE);
1509 sprintf(fnt, "[ %s(", name);
1510 AI.text = fnt; assemblez_0(print_zc);
1511 for (i=1; (i<=7)&&(i<=no_locals); i++)
1512 { if (version_number >= 5)
1513 { PV.type = SHORT_CONSTANT_OT;
1514 PV.value = i; PV.marker = 0;
1515 assemblez_1_branch(check_arg_count_zc, PV, ln, FALSE);
1517 sprintf(fnt, "%s%s = ", (i==1)?"":", ", variable_name(i));
1518 AI.text = fnt; assemblez_0(print_zc);
1519 PV.type = VARIABLE_OT; PV.value = i; PV.marker = 0;
1520 assemblez_1(print_num_zc, PV);
1522 assemble_label_no(ln);
1523 sprintf(fnt, ") ]^"); AI.text = fnt;
1524 assemblez_0(print_zc);
1525 assemble_label_no(ln2);
1533 byteout(0xC0, 0); /* Glulx type byte for function */
1535 byteout(0xC1, 0); /* Glulx type byte for function */
1537 /* Now the locals format list. This is simple; we only use
1538 four-byte locals. That's a single pair, unless we have more
1539 than 255 locals, or none at all. */
1549 /* Terminate the list with a (0, 0) pair. */
1554 /* The top stack value is the number of function arguments. Let's
1555 move that into the first local, which is _vararg_count. */
1556 /* @copy sp _vararg_count; */
1557 byteout(0x40, 0); byteout(0x98, 0); byteout(0x00, 0);
1560 next_label = 0; next_sequence_point = 0; last_label = -1;
1562 if ((routine_asterisked) || (define_INFIX_switch)) {
1565 assembly_operand AO, AO2;
1566 if (define_INFIX_switch) {
1567 /* This isn't supported */
1568 if (embedded_flag) {
1571 i = no_named_routines++;
1572 named_routine_symbols[i] = the_symbol;
1575 sprintf(fnt, "[ %s(", name);
1576 AO.marker = STRING_MV;
1577 AO.type = CONSTANT_OT;
1578 AO.value = compile_string(fnt, FALSE, FALSE);
1579 assembleg_1(streamstr_gc, AO);
1582 for (ix=1; ix<=no_locals; ix++) {
1583 sprintf(fnt, "%s%s = ", (ix==1)?"":", ", variable_name(ix));
1584 AO.marker = STRING_MV;
1585 AO.type = CONSTANT_OT;
1586 AO.value = compile_string(fnt, FALSE, FALSE);
1587 assembleg_1(streamstr_gc, AO);
1589 AO.type = LOCALVAR_OT;
1591 assembleg_1(streamnum_gc, AO);
1595 int lntop, lnbottom;
1596 sprintf(fnt, "%s = ", variable_name(1));
1597 AO.marker = STRING_MV;
1598 AO.type = CONSTANT_OT;
1599 AO.value = compile_string(fnt, FALSE, FALSE);
1600 assembleg_1(streamstr_gc, AO);
1602 AO.type = LOCALVAR_OT;
1604 assembleg_1(streamnum_gc, AO);
1605 AO2.type = BYTECONSTANT_OT;
1608 assembleg_1(streamchar_gc, AO2);
1609 AO2.type = BYTECONSTANT_OT;
1612 /* for (temp_var4=0 : temp_var4<_vararg_count : temp_var4++) {
1614 @stkpeek temp_var4 sp;
1618 assembleg_store(temp_var4, zero_operand);
1619 lntop = next_label++;
1620 lnbottom = next_label++;
1621 assemble_label_no(lntop);
1622 assembleg_2_branch(jge_gc, temp_var4, AO, lnbottom); /* AO is _vararg_count */
1623 assembleg_1(streamchar_gc, AO2); /* AO2 is space */
1624 assembleg_2(stkpeek_gc, temp_var4, stack_pointer);
1625 assembleg_1(streamnum_gc, stack_pointer);
1626 assembleg_3(add_gc, temp_var4, one_operand, temp_var4);
1627 assembleg_0_branch(jump_gc, lntop);
1628 assemble_label_no(lnbottom);
1631 AO.marker = STRING_MV;
1632 AO.type = CONSTANT_OT;
1633 AO.value = compile_string(") ]^", FALSE, FALSE);
1634 assembleg_1(streamstr_gc, AO);
1641 void assemble_routine_end(int embedded_flag, debug_locations locations)
1644 /* No marker is made in the Z-machine's code area to indicate the */
1645 /* end of a routine. Instead, we simply assemble a return opcode if */
1646 /* need be (it won't be if the last instruction was, say, a "quit"). */
1647 /* The return value is true (1) for normal routines, false (0) for */
1648 /* embedded routines (e.g. the library uses this for "before" */
1651 if (!execution_never_reaches_here)
1654 if (embedded_flag) assemblez_0(rfalse_zc);
1655 else assemblez_0(rtrue_zc);
1658 assembly_operand AO;
1663 assembleg_1(return_gc, AO);
1667 /* Dump the contents of the current routine into longer-term Z-code
1671 transfer_routine_z();
1673 transfer_routine_g();
1675 if (track_unused_routines)
1676 df_note_function_end(zmachine_pc);
1678 /* Tell the debugging file about the routine just ended. */
1680 if (debugfile_switch)
1682 debug_file_printf("<routine>");
1685 ("<identifier artificial=\"true\">%s</identifier>",
1688 else if (sflags[routine_symbol] & REPLACE_SFLAG)
1689 { /* The symbol type will be set to ROUTINE_T once the replaced
1690 version has been given; if it is already set, we must be dealing
1691 with a replacement, and we can use the routine name as-is.
1692 Otherwise we look for a rename. And if that doesn't work, we
1693 fall back to an artificial identifier. */
1694 if (stypes[routine_symbol] == ROUTINE_T)
1695 { /* Optional because there may be further replacements. */
1696 write_debug_optional_identifier(routine_symbol);
1698 else if (find_symbol_replacement(&routine_symbol))
1700 ("<identifier>%s</identifier>", symbs[routine_symbol]);
1704 ("<identifier artificial=\"true\">%s (replaced)"
1709 { debug_file_printf("<identifier>%s</identifier>", routine_name);
1711 debug_file_printf("<value>");
1713 { write_debug_code_backpatch(routine_start_pc);
1715 { write_debug_packed_code_backpatch(routine_start_pc);
1717 debug_file_printf("</value>");
1718 debug_file_printf("<address>");
1719 write_debug_code_backpatch(routine_start_pc);
1720 debug_file_printf("</address>");
1722 ("<byte-count>%d</byte-count>", zmachine_pc - routine_start_pc);
1723 write_debug_locations(locations);
1724 for (i = 1; i <= routine_locals; ++i)
1725 { debug_file_printf("<local-variable>");
1726 debug_file_printf("<identifier>%s</identifier>", variable_name(i));
1729 ("<frame-offset>%d</frame-offset>", 4 * (i - 1));
1732 { debug_file_printf("<index>%d</index>", i);
1734 debug_file_printf("</local-variable>");
1736 for (i = 0; i < next_sequence_point; ++i)
1737 { debug_file_printf("<sequence-point>");
1738 debug_file_printf("<address>");
1739 write_debug_code_backpatch
1740 (label_offsets[sequence_point_labels[i]]);
1741 debug_file_printf("</address>");
1742 write_debug_location(sequence_point_locations[i]);
1743 debug_file_printf("</sequence-point>");
1745 debug_file_printf("</routine>");
1748 my_free(&routine_name, "temporary copy of routine name");
1750 /* Issue warnings about any local variables not used in the routine. */
1752 for (i=1; i<=routine_locals; i++)
1753 if (!(variable_usage[i]))
1754 dbnu_warning("Local variable", variable_name(i),
1755 routine_starts_line);
1757 for (i=0; i<next_label; i++)
1758 { int j = label_symbols[i];
1760 { if (sflags[j] & CHANGE_SFLAG)
1761 error_named_at("Routine contains no such label as",
1762 (char *) symbs[j], slines[j]);
1764 if ((sflags[j] & USED_SFLAG) == 0)
1765 dbnu_warning("Label", (char *) symbs[j], slines[j]);
1766 stypes[j] = CONSTANT_T;
1767 sflags[j] = UNKNOWN_SFLAG;
1770 no_sequence_points += next_sequence_point;
1771 next_label = 0; next_sequence_point = 0;
1774 /* ------------------------------------------------------------------------- */
1775 /* Called when the holding area contains an entire routine of code: */
1776 /* backpatches the labels, issues module markers, then dumps the routine */
1777 /* into longer-term storage. */
1778 /* Note that in the code received, all branches have long form, and their */
1779 /* contents are not an offset but the label numbers they branch to. */
1780 /* Similarly, LABEL operands (those of "jump" instructions) are label */
1781 /* numbers. So this routine must change the label numbers to offsets, */
1782 /* slimming the code down as it does so to take advantage of short-form */
1783 /* branch operands where possible. */
1784 /* ------------------------------------------------------------------------- */
1786 static int32 adjusted_pc;
1788 static void transfer_to_temp_file(uchar *c)
1789 { fputc(*c,Temp2_fp);
1793 static void transfer_to_zcode_area(uchar *c)
1794 { write_byte_to_memory_block(&zcode_area, adjusted_pc++, *c);
1797 static void transfer_routine_z(void)
1798 { int32 i, j, pc, new_pc, label, long_form, offset_of_next, addr,
1799 branch_on_true, rstart_pc;
1800 void (* transfer_byte)(uchar *);
1802 adjusted_pc = zmachine_pc - zcode_ha_size; rstart_pc = adjusted_pc;
1804 if (asm_trace_level >= 3)
1805 { printf("Backpatching routine at %05lx: initial size %d, %d labels\n",
1806 (long int) adjusted_pc, zcode_ha_size, next_label);
1810 (temporary_files_switch)?transfer_to_temp_file:transfer_to_zcode_area;
1812 /* (1) Scan through for branches and make short/long decisions in each
1813 case. Mark omitted bytes (2nd bytes in branches converted to
1814 short form) with DELETED_MV. */
1816 for (i=0, pc=adjusted_pc; i<zcode_ha_size; i++, pc++)
1817 { if (zcode_markers[i] == BRANCH_MV)
1818 { if (asm_trace_level >= 4)
1819 printf("Branch detected at offset %04x\n", pc);
1820 j = (256*zcode_holding_area[i] + zcode_holding_area[i+1]) & 0x7fff;
1821 if (asm_trace_level >= 4)
1822 printf("To label %d, which is %d from here\n",
1823 j, label_offsets[j]-pc);
1824 if ((label_offsets[j] >= pc+2) && (label_offsets[j] < pc+64))
1825 { if (asm_trace_level >= 4) printf("Short form\n");
1826 zcode_markers[i+1] = DELETED_MV;
1831 /* (2) Calculate the new positions of the labels. Note that since the
1832 long/short decision was taken on the basis of the old labels,
1833 and since the new labels are slightly closer together because
1834 of branch bytes deleted, there may be a few further branch
1835 optimisations which are possible but which have been missed
1836 (if two labels move inside the "short" range as a result of
1837 a previous optimisation). However, this is acceptably uncommon. */
1840 { if (asm_trace_level >= 4)
1841 { printf("Opening label: %d\n", first_label);
1842 for (i=0;i<next_label;i++)
1843 printf("Label %d offset %04x next -> %d previous -> %d\n",
1844 i, label_offsets[i], label_next[i], label_prev[i]);
1847 for (i=0, pc=adjusted_pc, new_pc=adjusted_pc, label = first_label;
1848 i<zcode_ha_size; i++, pc++)
1849 { while ((label != -1) && (label_offsets[label] == pc))
1850 { if (asm_trace_level >= 4)
1851 printf("Position of L%d corrected from %04x to %04x\n",
1852 label, label_offsets[label], new_pc);
1853 label_offsets[label] = new_pc;
1854 label = label_next[label];
1856 if (zcode_markers[i] != DELETED_MV) new_pc++;
1860 /* (3) As we are transferring, replace the label numbers in branch
1861 operands with offsets to those labels. Also issue markers, now
1862 that we know where they occur in the final Z-code area. */
1864 for (i=0, new_pc=adjusted_pc; i<zcode_ha_size; i++)
1865 { switch(zcode_markers[i])
1867 long_form = 1; if (zcode_markers[i+1] == DELETED_MV) long_form = 0;
1869 j = (256*zcode_holding_area[i] + zcode_holding_area[i+1]) & 0x7fff;
1870 branch_on_true = ((zcode_holding_area[i]) & 0x80);
1871 offset_of_next = new_pc + long_form + 1;
1873 addr = label_offsets[j] - offset_of_next + 2;
1874 if (addr<-0x2000 || addr>0x1fff)
1875 fatalerror("Branch out of range: divide the routine up?");
1876 if (addr<0) addr+=(int32) 0x10000L;
1880 { zcode_holding_area[i] = branch_on_true + addr/256;
1881 zcode_holding_area[i+1] = addr%256;
1885 { compiler_error("Label out of range for branch");
1886 printf("Addr is %04x\n", addr);
1888 zcode_holding_area[i] = branch_on_true + 0x40 + (addr&0x3f);
1890 transfer_byte(zcode_holding_area + i); new_pc++;
1894 j = 256*zcode_holding_area[i] + zcode_holding_area[i+1];
1895 addr = label_offsets[j] - new_pc;
1896 if (addr<-0x8000 || addr>0x7fff)
1897 fatalerror("Jump out of range: divide the routine up?");
1898 if (addr<0) addr += (int32) 0x10000L;
1899 zcode_holding_area[i] = addr/256;
1900 zcode_holding_area[i+1] = addr%256;
1901 transfer_byte(zcode_holding_area + i); new_pc++;
1908 switch(zcode_markers[i] & 0x7f)
1909 { case NULL_MV: break;
1914 if (!module_switch) break;
1916 if ((zcode_markers[i] & 0x7f) > LARGEST_BPATCH_MV)
1917 { compiler_error("Illegal code backpatch value");
1918 printf("Illegal value of %02x at PC = %04x\n",
1919 zcode_markers[i] & 0x7f, new_pc);
1923 write_byte_to_memory_block(&zcode_backpatch_table,
1924 zcode_backpatch_size++,
1925 zcode_markers[i] + 32*(new_pc/65536));
1926 write_byte_to_memory_block(&zcode_backpatch_table,
1927 zcode_backpatch_size++, (new_pc/256)%256);
1928 write_byte_to_memory_block(&zcode_backpatch_table,
1929 zcode_backpatch_size++, new_pc%256);
1932 transfer_byte(zcode_holding_area + i); new_pc++;
1937 if (asm_trace_level >= 3)
1938 { printf("After branch optimisation, routine length is %d bytes\n",
1939 new_pc - rstart_pc);
1942 /* Insert null bytes if necessary to ensure the next routine address is */
1943 /* expressible as a packed address */
1947 if (oddeven_packing_switch)
1948 while ((adjusted_pc%(scale_factor*2))!=0) transfer_byte(zero);
1950 while ((adjusted_pc%scale_factor)!=0) transfer_byte(zero);
1953 zmachine_pc = adjusted_pc;
1957 static void transfer_routine_g(void)
1958 { int32 i, j, pc, new_pc, label, form_len, offset_of_next, addr,
1960 void (* transfer_byte)(uchar *);
1962 adjusted_pc = zmachine_pc - zcode_ha_size; rstart_pc = adjusted_pc;
1964 if (asm_trace_level >= 3)
1965 { printf("Backpatching routine at %05lx: initial size %d, %d labels\n",
1966 (long int) adjusted_pc, zcode_ha_size, next_label);
1970 (temporary_files_switch)?transfer_to_temp_file:transfer_to_zcode_area;
1972 /* (1) Scan through for branches and make short/long decisions in each
1973 case. Mark omitted bytes (bytes 2-4 in branches converted to
1974 short form) with DELETED_MV. */
1976 for (i=0, pc=adjusted_pc; i<zcode_ha_size; i++, pc++) {
1977 if (zcode_markers[i] >= BRANCH_MV && zcode_markers[i] < BRANCHMAX_MV) {
1978 int opmodeoffset = (zcode_markers[i] - BRANCH_MV);
1980 if (asm_trace_level >= 4)
1981 printf("Branch detected at offset %04x\n", pc);
1982 j = ((zcode_holding_area[i] << 24)
1983 | (zcode_holding_area[i+1] << 16)
1984 | (zcode_holding_area[i+2] << 8)
1985 | (zcode_holding_area[i+3]));
1986 offset_of_next = pc + 4;
1987 addr = (label_offsets[j] - offset_of_next) + 2;
1988 if (asm_trace_level >= 4)
1989 printf("To label %d, which is (%d-2) = %d from here\n",
1990 j, addr, label_offsets[j] - offset_of_next);
1991 if (addr >= -0x80 && addr < 0x80) {
1992 if (asm_trace_level >= 4) printf("...Byte form\n");
1993 zcode_markers[i+1] = DELETED_MV;
1994 zcode_markers[i+2] = DELETED_MV;
1995 zcode_markers[i+3] = DELETED_MV;
1996 opmodebyte = i - ((opmodeoffset+1)/2);
1997 if ((opmodeoffset & 1) == 0)
1998 zcode_holding_area[opmodebyte] =
1999 (zcode_holding_area[opmodebyte] & 0xF0) | 0x01;
2001 zcode_holding_area[opmodebyte] =
2002 (zcode_holding_area[opmodebyte] & 0x0F) | 0x10;
2004 else if (addr >= -0x8000 && addr < 0x8000) {
2005 if (asm_trace_level >= 4) printf("...Short form\n");
2006 zcode_markers[i+2] = DELETED_MV;
2007 zcode_markers[i+3] = DELETED_MV;
2008 opmodebyte = i - ((opmodeoffset+1)/2);
2009 if ((opmodeoffset & 1) == 0)
2010 zcode_holding_area[opmodebyte] =
2011 (zcode_holding_area[opmodebyte] & 0xF0) | 0x02;
2013 zcode_holding_area[opmodebyte] =
2014 (zcode_holding_area[opmodebyte] & 0x0F) | 0x20;
2019 /* (2) Calculate the new positions of the labels. Note that since the
2020 long/short decision was taken on the basis of the old labels,
2021 and since the new labels are slightly closer together because
2022 of branch bytes deleted, there may be a few further branch
2023 optimisations which are possible but which have been missed
2024 (if two labels move inside the "short" range as a result of
2025 a previous optimisation). However, this is acceptably uncommon. */
2026 if (next_label > 0) {
2027 if (asm_trace_level >= 4) {
2028 printf("Opening label: %d\n", first_label);
2029 for (i=0;i<next_label;i++)
2030 printf("Label %d offset %04x next -> %d previous -> %d\n",
2031 i, label_offsets[i], label_next[i], label_prev[i]);
2034 for (i=0, pc=adjusted_pc, new_pc=adjusted_pc, label = first_label;
2037 while ((label != -1) && (label_offsets[label] == pc)) {
2038 if (asm_trace_level >= 4)
2039 printf("Position of L%d corrected from %04x to %04x\n",
2040 label, label_offsets[label], new_pc);
2041 label_offsets[label] = new_pc;
2042 label = label_next[label];
2044 if (zcode_markers[i] != DELETED_MV) new_pc++;
2048 /* (3) As we are transferring, replace the label numbers in branch
2049 operands with offsets to those labels. Also issue markers, now
2050 that we know where they occur in the final Z-code area. */
2052 for (i=0, new_pc=adjusted_pc; i<zcode_ha_size; i++) {
2054 if (zcode_markers[i] >= BRANCH_MV && zcode_markers[i] < BRANCHMAX_MV) {
2056 if (zcode_markers[i+1] == DELETED_MV) {
2060 if (zcode_markers[i+2] == DELETED_MV)
2063 j = ((zcode_holding_area[i] << 24)
2064 | (zcode_holding_area[i+1] << 16)
2065 | (zcode_holding_area[i+2] << 8)
2066 | (zcode_holding_area[i+3]));
2068 /* At the moment, we can safely assume that the branch operand
2069 is the end of the opcode, so the next opcode starts right
2071 offset_of_next = new_pc + form_len;
2073 addr = (label_offsets[j] - offset_of_next) + 2;
2074 if (asm_trace_level >= 4) {
2075 printf("Branch at offset %04x: %04x (%s)\n",
2076 new_pc, addr, ((form_len == 1) ? "byte" :
2077 ((form_len == 2) ? "short" : "long")));
2079 if (form_len == 1) {
2080 if (addr < -0x80 || addr >= 0x80) {
2081 error("*** Label out of range for byte branch ***");
2083 zcode_holding_area[i] = (addr) & 0xFF;
2085 else if (form_len == 2) {
2086 if (addr < -0x8000 || addr >= 0x8000) {
2087 error("*** Label out of range for short branch ***");
2089 zcode_holding_area[i] = (addr >> 8) & 0xFF;
2090 zcode_holding_area[i+1] = (addr) & 0xFF;
2093 zcode_holding_area[i] = (addr >> 24) & 0xFF;
2094 zcode_holding_area[i+1] = (addr >> 16) & 0xFF;
2095 zcode_holding_area[i+2] = (addr >> 8) & 0xFF;
2096 zcode_holding_area[i+3] = (addr) & 0xFF;
2098 transfer_byte(zcode_holding_area + i); new_pc++;
2100 else if (zcode_markers[i] == LABEL_MV) {
2101 error("*** No LABEL opcodes in Glulx ***");
2103 else if (zcode_markers[i] == DELETED_MV) {
2107 switch(zcode_markers[i] & 0x7f) {
2112 if (!module_switch) break;
2116 if ((zcode_markers[i] & 0x7f) > LARGEST_BPATCH_MV) {
2117 error("*** Illegal code backpatch value ***");
2118 printf("Illegal value of %02x at PC = %04x\n",
2119 zcode_markers[i] & 0x7f, new_pc);
2122 /* The backpatch table format for Glulx:
2123 First, the marker byte (0..LARGEST_BPATCH_MV).
2124 Then a byte indicating the data size to be patched (1, 2, 4).
2125 Then the four-byte address (new_pc).
2127 write_byte_to_memory_block(&zcode_backpatch_table,
2128 zcode_backpatch_size++,
2130 write_byte_to_memory_block(&zcode_backpatch_table,
2131 zcode_backpatch_size++,
2133 write_byte_to_memory_block(&zcode_backpatch_table,
2134 zcode_backpatch_size++, ((new_pc >> 24) & 0xFF));
2135 write_byte_to_memory_block(&zcode_backpatch_table,
2136 zcode_backpatch_size++, ((new_pc >> 16) & 0xFF));
2137 write_byte_to_memory_block(&zcode_backpatch_table,
2138 zcode_backpatch_size++, ((new_pc >> 8) & 0xFF));
2139 write_byte_to_memory_block(&zcode_backpatch_table,
2140 zcode_backpatch_size++, (new_pc & 0xFF));
2143 transfer_byte(zcode_holding_area + i); new_pc++;
2147 if (asm_trace_level >= 3)
2148 { printf("After branch optimisation, routine length is %d bytes\n",
2149 new_pc - rstart_pc);
2152 zmachine_pc = adjusted_pc;
2157 /* ========================================================================= */
2158 /* Front ends for the instruction assembler: convenient shorthand forms */
2159 /* used in various code generation routines all over Inform. */
2160 /* ------------------------------------------------------------------------- */
2162 void assemble_jump(int n)
2170 void assemblez_0(int internal_number)
2171 { AI.internal_number = internal_number;
2172 AI.operand_count = 0;
2173 AI.store_variable_number = -1;
2174 AI.branch_label_number = -1;
2175 assemblez_instruction(&AI);
2178 void assemblez_0_to(int internal_number, assembly_operand o)
2179 { AI.internal_number = internal_number;
2180 AI.operand_count = 0;
2181 AI.store_variable_number = o.value;
2182 AI.branch_label_number = -1;
2183 assemblez_instruction(&AI);
2186 void assemblez_0_branch(int internal_number, int label, int flag)
2187 { AI.internal_number = internal_number;
2188 AI.operand_count = 0;
2189 AI.store_variable_number = -1;
2190 AI.branch_label_number = label;
2191 AI.branch_flag = flag;
2192 assemblez_instruction(&AI);
2195 void assemblez_1(int internal_number, assembly_operand o1)
2196 { AI.internal_number = internal_number;
2197 AI.operand_count = 1;
2199 AI.store_variable_number = -1;
2200 AI.branch_label_number = -1;
2201 assemblez_instruction(&AI);
2204 void assemblez_1_to(int internal_number,
2205 assembly_operand o1, assembly_operand st)
2206 { AI.internal_number = internal_number;
2207 AI.operand_count = 1;
2209 AI.store_variable_number = st.value;
2210 AI.branch_label_number = -1;
2211 assemblez_instruction(&AI);
2214 void assemblez_1_branch(int internal_number,
2215 assembly_operand o1, int label, int flag)
2216 { AI.internal_number = internal_number;
2217 AI.operand_count = 1;
2219 AI.branch_label_number = label;
2220 AI.store_variable_number = -1;
2221 AI.branch_flag = flag;
2222 assemblez_instruction(&AI);
2225 void assemblez_2(int internal_number,
2226 assembly_operand o1, assembly_operand o2)
2227 { AI.internal_number = internal_number;
2228 AI.operand_count = 2;
2231 AI.store_variable_number = -1;
2232 AI.branch_label_number = -1;
2233 assemblez_instruction(&AI);
2236 void assemblez_3(int internal_number,
2237 assembly_operand o1, assembly_operand o2, assembly_operand o3)
2238 { AI.internal_number = internal_number;
2239 AI.operand_count = 3;
2243 AI.store_variable_number = -1;
2244 AI.branch_label_number = -1;
2245 assemblez_instruction(&AI);
2248 void assemblez_3_to(int internal_number,
2249 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2250 assembly_operand st)
2251 { AI.internal_number = internal_number;
2252 AI.operand_count = 3;
2256 AI.store_variable_number = st.value;
2257 AI.branch_label_number = -1;
2258 assemblez_instruction(&AI);
2261 void assemblez_3_branch(int internal_number,
2262 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2263 int label, int flag)
2264 { AI.internal_number = internal_number;
2265 AI.operand_count = 3;
2269 AI.store_variable_number = -1;
2270 AI.branch_label_number = label;
2271 AI.branch_flag = flag;
2272 assemblez_instruction(&AI);
2275 void assemblez_4(int internal_number,
2276 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2277 assembly_operand o4)
2278 { AI.internal_number = internal_number;
2279 AI.operand_count = 4;
2284 AI.store_variable_number = -1;
2285 AI.branch_label_number = -1;
2286 assemblez_instruction(&AI);
2289 void assemblez_5(int internal_number,
2290 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2291 assembly_operand o4, assembly_operand o5)
2292 { AI.internal_number = internal_number;
2293 AI.operand_count = 5;
2299 AI.store_variable_number = -1;
2300 AI.branch_label_number = -1;
2301 assemblez_instruction(&AI);
2304 void assemblez_6(int internal_number,
2305 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2306 assembly_operand o4, assembly_operand o5, assembly_operand o6)
2307 { AI.internal_number = internal_number;
2308 AI.operand_count = 6;
2315 AI.store_variable_number = -1;
2316 AI.branch_label_number = -1;
2317 assemblez_instruction(&AI);
2320 void assemblez_4_branch(int internal_number,
2321 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2322 assembly_operand o4, int label, int flag)
2323 { AI.internal_number = internal_number;
2324 AI.operand_count = 4;
2329 AI.store_variable_number = -1;
2330 AI.branch_label_number = label;
2331 AI.branch_flag = flag;
2332 assemblez_instruction(&AI);
2335 void assemblez_4_to(int internal_number,
2336 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2337 assembly_operand o4, assembly_operand st)
2338 { AI.internal_number = internal_number;
2339 AI.operand_count = 4;
2344 AI.store_variable_number = st.value;
2345 AI.branch_label_number = -1;
2346 assemblez_instruction(&AI);
2349 void assemblez_5_to(int internal_number,
2350 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2351 assembly_operand o4, assembly_operand o5, assembly_operand st)
2352 { AI.internal_number = internal_number;
2353 AI.operand_count = 5;
2359 AI.store_variable_number = st.value;
2360 AI.branch_label_number = -1;
2361 assemblez_instruction(&AI);
2364 void assemblez_2_to(int internal_number,
2365 assembly_operand o1, assembly_operand o2, assembly_operand st)
2366 { AI.internal_number = internal_number;
2367 AI.operand_count = 2;
2370 AI.store_variable_number = st.value;
2371 AI.branch_label_number = -1;
2372 assemblez_instruction(&AI);
2375 void assemblez_2_branch(int internal_number,
2376 assembly_operand o1, assembly_operand o2, int label, int flag)
2377 { AI.internal_number = internal_number;
2378 AI.operand_count = 2;
2381 AI.branch_label_number = label;
2382 AI.store_variable_number = -1;
2383 AI.branch_flag = flag;
2384 assemblez_instruction(&AI);
2387 void assemblez_objcode(int internal_number,
2388 assembly_operand o1, assembly_operand st, int label, int flag)
2389 { AI.internal_number = internal_number;
2390 AI.operand_count = 1;
2392 AI.branch_label_number = label;
2393 AI.store_variable_number = st.value;
2394 AI.branch_flag = flag;
2395 assemblez_instruction(&AI);
2398 extern void assemblez_inc(assembly_operand o1)
2400 if ((o1.value >= MAX_LOCAL_VARIABLES)
2401 && (o1.value<LOWEST_SYSTEM_VAR_NUMBER))
2403 AI.internal_number = inc_zc;
2404 AI.operand_count = 1;
2405 AI.operand[0].value = o1.value;
2406 AI.operand[0].type = SHORT_CONSTANT_OT;
2407 AI.operand[0].marker = m;
2408 AI.store_variable_number = -1;
2409 AI.branch_label_number = -1;
2410 assemblez_instruction(&AI);
2413 extern void assemblez_dec(assembly_operand o1)
2415 if ((o1.value >= MAX_LOCAL_VARIABLES)
2416 && (o1.value<LOWEST_SYSTEM_VAR_NUMBER))
2418 AI.internal_number = dec_zc;
2419 AI.operand_count = 1;
2420 AI.operand[0].value = o1.value;
2421 AI.operand[0].type = SHORT_CONSTANT_OT;
2422 AI.operand[0].marker = m;
2423 AI.store_variable_number = -1;
2424 AI.branch_label_number = -1;
2425 assemblez_instruction(&AI);
2428 extern void assemblez_store(assembly_operand o1, assembly_operand o2)
2430 if ((o1.value >= MAX_LOCAL_VARIABLES)
2431 && (o1.value<LOWEST_SYSTEM_VAR_NUMBER))
2434 if ((o2.type == VARIABLE_OT) && (o2.value == 0))
2436 /* Assemble "pull VAR" rather than "store VAR sp",
2439 AI.internal_number = pull_zc;
2440 if (instruction_set_number == 6)
2441 { AI.operand_count = 0;
2442 AI.store_variable_number = o1.value;
2445 { AI.operand_count = 1;
2446 AI.operand[0].value = o1.value;
2447 AI.operand[0].type = SHORT_CONSTANT_OT;
2448 AI.operand[0].marker = m;
2449 AI.store_variable_number = -1;
2451 AI.branch_label_number = -1;
2452 assemblez_instruction(&AI);
2456 if ((o1.type == VARIABLE_OT) && (o1.value == 0))
2457 { /* Assemble "push VAR" rather than "store sp VAR",
2460 AI.internal_number = push_zc;
2461 AI.operand_count = 1;
2463 AI.store_variable_number = -1;
2464 AI.branch_label_number = -1;
2465 assemblez_instruction(&AI);
2468 AI.internal_number = store_zc;
2469 AI.operand_count = 2;
2470 AI.operand[0].value = o1.value;
2471 AI.operand[0].type = SHORT_CONSTANT_OT;
2472 AI.operand[0].marker = m;
2474 AI.store_variable_number = -1;
2475 AI.branch_label_number = -1;
2476 assemblez_instruction(&AI);
2479 void assemblez_jump(int n)
2480 { assembly_operand AO;
2481 if (n==-4) assemblez_0(rtrue_zc);
2482 else if (n==-3) assemblez_0(rfalse_zc);
2484 { AO.type = LONG_CONSTANT_OT; AO.value = n; AO.marker = 0;
2485 assemblez_1(jump_zc, AO);
2489 void assembleg_0(int internal_number)
2490 { AI.internal_number = internal_number;
2491 AI.operand_count = 0;
2492 assembleg_instruction(&AI);
2495 void assembleg_1(int internal_number, assembly_operand o1)
2496 { AI.internal_number = internal_number;
2497 AI.operand_count = 1;
2499 assembleg_instruction(&AI);
2502 void assembleg_2(int internal_number, assembly_operand o1,
2503 assembly_operand o2)
2504 { AI.internal_number = internal_number;
2505 AI.operand_count = 2;
2508 assembleg_instruction(&AI);
2511 void assembleg_3(int internal_number, assembly_operand o1,
2512 assembly_operand o2, assembly_operand o3)
2513 { AI.internal_number = internal_number;
2514 AI.operand_count = 3;
2518 assembleg_instruction(&AI);
2521 void assembleg_4(int internal_number, assembly_operand o1,
2522 assembly_operand o2, assembly_operand o3,
2523 assembly_operand o4)
2524 { AI.internal_number = internal_number;
2525 AI.operand_count = 4;
2530 assembleg_instruction(&AI);
2533 void assembleg_5(int internal_number, assembly_operand o1,
2534 assembly_operand o2, assembly_operand o3,
2535 assembly_operand o4, assembly_operand o5)
2536 { AI.internal_number = internal_number;
2537 AI.operand_count = 5;
2543 assembleg_instruction(&AI);
2546 void assembleg_0_branch(int internal_number,
2549 AI.internal_number = internal_number;
2550 AI.operand_count = 1;
2551 AI.operand[0].type = CONSTANT_OT;
2552 AI.operand[0].value = label;
2553 AI.operand[0].marker = BRANCH_MV;
2554 assembleg_instruction(&AI);
2557 void assembleg_1_branch(int internal_number,
2558 assembly_operand o1, int label)
2560 /* Some clever optimizations first. A constant is always or never equal
2562 if (o1.marker == 0 && is_constant_ot(o1.type)) {
2563 if ((internal_number == jz_gc && o1.value == 0)
2564 || (internal_number == jnz_gc && o1.value != 0)) {
2565 assembleg_0_branch(jump_gc, label);
2566 /* We clear the "can't reach statement" flag here,
2567 so that "if (1)" doesn't produce that warning. */
2568 execution_never_reaches_here = 0;
2571 if ((internal_number == jz_gc && o1.value != 0)
2572 || (internal_number == jnz_gc && o1.value == 0)) {
2573 /* assemble nothing at all! */
2577 AI.internal_number = internal_number;
2578 AI.operand_count = 2;
2580 AI.operand[1].type = CONSTANT_OT;
2581 AI.operand[1].value = label;
2582 AI.operand[1].marker = BRANCH_MV;
2583 assembleg_instruction(&AI);
2586 void assembleg_2_branch(int internal_number,
2587 assembly_operand o1, assembly_operand o2, int label)
2589 AI.internal_number = internal_number;
2590 AI.operand_count = 3;
2593 AI.operand[2].type = CONSTANT_OT;
2594 AI.operand[2].value = label;
2595 AI.operand[2].marker = BRANCH_MV;
2596 assembleg_instruction(&AI);
2599 void assembleg_call_1(assembly_operand oaddr, assembly_operand o1,
2600 assembly_operand odest)
2602 assembleg_3(callfi_gc, oaddr, o1, odest);
2605 void assembleg_call_2(assembly_operand oaddr, assembly_operand o1,
2606 assembly_operand o2, assembly_operand odest)
2608 assembleg_4(callfii_gc, oaddr, o1, o2, odest);
2611 void assembleg_call_3(assembly_operand oaddr, assembly_operand o1,
2612 assembly_operand o2, assembly_operand o3, assembly_operand odest)
2614 assembleg_5(callfiii_gc, oaddr, o1, o2, o3, odest);
2617 void assembleg_inc(assembly_operand o1)
2619 AI.internal_number = add_gc;
2620 AI.operand_count = 3;
2622 AI.operand[1] = one_operand;
2624 assembleg_instruction(&AI);
2627 void assembleg_dec(assembly_operand o1)
2629 AI.internal_number = sub_gc;
2630 AI.operand_count = 3;
2632 AI.operand[1] = one_operand;
2634 assembleg_instruction(&AI);
2637 void assembleg_store(assembly_operand o1, assembly_operand o2)
2639 /* Note the order is reversed: "o1 = o2;" */
2640 assembleg_2(copy_gc, o2, o1);
2643 void assembleg_jump(int n)
2646 assembleg_1(return_gc, one_operand);
2649 assembleg_1(return_gc, zero_operand);
2652 assembleg_0_branch(jump_gc, n);
2656 /* ========================================================================= */
2657 /* Parsing and then calling the assembler for @ (assembly language) */
2659 /* ------------------------------------------------------------------------- */
2661 static assembly_operand parse_operand_z(void)
2662 { assembly_operand AO;
2664 AO = parse_expression(ASSEMBLY_CONTEXT);
2665 if (AO.type == EXPRESSION_OT)
2666 { ebf_error("variable or constant", "expression");
2667 AO.type = SHORT_CONSTANT_OT;
2672 static void parse_assembly_z(void)
2673 { int n, min, max, indirect_addressed, error_flag = FALSE;
2676 AI.operand_count = 0;
2677 AI.store_variable_number = -1;
2678 AI.branch_label_number = -1;
2681 opcode_names.enabled = TRUE;
2683 opcode_names.enabled = FALSE;
2685 if (token_type == DQ_TT)
2687 AI.internal_number = -1;
2689 custom_opcode_z.name = (uchar *) token_text;
2690 custom_opcode_z.version1 = instruction_set_number;
2691 custom_opcode_z.version2 = instruction_set_number;
2692 custom_opcode_z.extension = -1;
2693 custom_opcode_z.flags = 0;
2694 custom_opcode_z.op_rules = 0;
2695 custom_opcode_z.flags2_set = 0;
2696 custom_opcode_z.no = ZERO;
2698 for (i=0; token_text[i]!=0; i++)
2699 { if (token_text[i] == ':')
2700 { token_text[i++] = 0;
2704 if (token_text[i] == 0)
2705 error("Opcode specification should have form \"VAR:102\"");
2708 if (strcmp(token_text, "0OP")==0) n=ZERO;
2709 if (strcmp(token_text, "1OP")==0) n=ONE;
2710 if (strcmp(token_text, "2OP")==0) n=TWO;
2711 if (strcmp(token_text, "VAR")==0) n=VAR;
2712 if (strcmp(token_text, "EXT")==0) n=EXT;
2713 if (strcmp(token_text, "VAR_LONG")==0) n=VAR_LONG;
2714 if (strcmp(token_text, "EXT_LONG")==0) n=EXT_LONG;
2716 if (i>0) token_text[i-1] = ':';
2719 { ebf_error("Expected 0OP, 1OP, 2OP, VAR, EXT, VAR_LONG or EXT_LONG",
2723 custom_opcode_z.no = n;
2725 custom_opcode_z.code = atoi(token_text+i);
2726 while (isdigit(token_text[i])) i++;
2730 { case ZERO: case ONE: max = 16; break;
2731 case VAR: case VAR_LONG: min = 32; max = 64; break;
2732 case EXT: case EXT_LONG: max = 256; break;
2733 case TWO: max = 32; break;
2735 if ((custom_opcode_z.code < min) || (custom_opcode_z.code >= max))
2737 sprintf(range, "%d to %d", min, max-1);
2738 error_named("For this operand type, opcode number must be in range",
2740 custom_opcode_z.code = min;
2744 while (token_text[i++] != 0)
2745 { switch(token_text[i-1])
2746 { case 'B': custom_opcode_z.flags |= Br; break;
2747 case 'S': custom_opcode_z.flags |= St; break;
2748 case 'T': custom_opcode_z.op_rules = TEXT; break;
2749 case 'I': custom_opcode_z.op_rules = VARIAB; break;
2750 case 'F': custom_opcode_z.flags2_set = atoi(token_text+i);
2751 while (isdigit(token_text[i])) i++; break;
2753 error("Unknown flag: options are B (branch), S (store), \
2754 T (text), I (indirect addressing), F** (set this Flags 2 bit)");
2758 O = custom_opcode_z;
2761 { if (token_type != OPCODE_NAME_TT)
2762 { ebf_error("an opcode name", token_text);
2763 panic_mode_error_recovery();
2766 AI.internal_number = token_value;
2767 O = internal_number_to_opcode_z(AI.internal_number);
2770 indirect_addressed = (O.op_rules == VARIAB);
2772 if (O.op_rules == TEXT)
2774 if (token_type != DQ_TT)
2775 ebf_error("literal text in double-quotes", token_text);
2776 AI.text = token_text;
2777 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) return;
2779 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
2780 { assemblez_instruction(&AI);
2783 ebf_error("semicolon ';' after print string", token_text);
2788 return_sp_as_variable = TRUE;
2792 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
2794 if ((token_type == SEP_TT) && (token_value == ARROW_SEP))
2795 { if (AI.store_variable_number != -1)
2796 error("Only one '->' store destination can be given");
2798 if ((token_type != SYMBOL_TT)
2799 && (token_type != LOCAL_VARIABLE_TT))
2800 ebf_error("variable name or 'sp'", token_text);
2802 if (token_type == LOCAL_VARIABLE_TT) n = token_value;
2804 { if (strcmp(token_text, "sp") == 0) n = 0;
2806 { if (stypes[token_value] != GLOBAL_VARIABLE_T)
2808 "Store '->' destination not 'sp' or a variable:",
2810 else n = svals[token_value];
2813 AI.store_variable_number = n;
2817 if ((token_type == SEP_TT) &&
2818 ((token_value == BRANCH_SEP) || (token_value == NBRANCH_SEP)))
2819 { if (AI.branch_label_number != -1)
2820 error("Only one '?' branch destination can be given");
2822 AI.branch_flag = (token_value == BRANCH_SEP);
2824 opcode_names.enabled = TRUE;
2826 opcode_names.enabled = FALSE;
2829 if ((token_type == OPCODE_NAME_TT)
2830 && (token_value == rfalse_zc)) n = -3;
2832 if ((token_type == OPCODE_NAME_TT)
2833 && (token_value == rtrue_zc)) n = -4;
2835 { if (token_type == SYMBOL_TT)
2840 ebf_error("label name after '?' or '?~'", token_text);
2842 AI.branch_label_number = n;
2846 if (AI.operand_count == 8)
2847 { error("No assembly instruction may have more than 8 operands");
2848 panic_mode_error_recovery(); break;
2851 if ((token_type == SEP_TT) && (token_value == OPEN_SQUARE_SEP))
2852 { if (!indirect_addressed)
2853 error("This opcode does not use indirect addressing");
2854 if (AI.operand_count > 0)
2855 error("Indirect addressing can only be used on the first operand");
2856 AI.operand[AI.operand_count++] = parse_operand_z();
2858 if (!((token_type == SEP_TT) && (token_value == CLOSE_SQUARE_SEP)))
2859 { ebf_error("']'", token_text);
2865 AI.operand[AI.operand_count++] = parse_operand_z();
2866 if ((indirect_addressed) && (AI.operand_count == 1)
2867 && (AI.operand[AI.operand_count-1].type == VARIABLE_OT))
2868 { AI.operand[AI.operand_count-1].type = SHORT_CONSTANT_OT;
2869 AI.operand[AI.operand_count-1].marker = VARIABLE_MV;
2875 return_sp_as_variable = FALSE;
2878 if (O.version1 == 0)
2879 { error_named("Opcode unavailable in this Z-machine version:",
2880 opcode_names.keywords[AI.internal_number]);
2884 if (((O.flags) & Br) != 0)
2885 { if (AI.branch_label_number == -1)
2886 { error_flag = TRUE;
2887 AI.branch_label_number = -2;
2891 { if (AI.branch_label_number != -1)
2892 { error_flag = TRUE;
2893 AI.branch_label_number = -1;
2896 if (((O.flags) & St) != 0)
2897 { if (AI.store_variable_number == -1)
2898 { if (AI.operand_count == 0)
2899 { error_flag = TRUE;
2900 AI.store_variable_number = 255;
2903 { AI.store_variable_number
2904 = AI.operand[--AI.operand_count].value;
2905 if (AI.operand[AI.operand_count].type != VARIABLE_OT)
2906 error("Store destination (the last operand) is not a variable");
2911 { if (AI.store_variable_number != -1)
2912 { error_flag = TRUE;
2913 AI.store_variable_number = -1;
2919 { case TWO: min = 2; max = 2;
2920 /* Exception for the V6 set_colour, which can take
2921 a third argument, thus forcing it into VAR form: */
2922 if ((version_number == 6) && (O.code == 0x1b)) max = 3;
2923 /* Also an exception for je, which can take from 1
2924 argument (useless) to 4 arguments */
2925 if (O.code == 0x01) { min = 1; max = 4; }
2927 case VAR: min = 0; max = 4; break;
2928 case VAR_LONG: min = 0; max = 8; break;
2929 case ONE: min = 1; max = 1; break;
2930 case ZERO: min = 0; max = 0; break;
2931 case EXT: min = 0; max = 4; break;
2932 case EXT_LONG: min = 0; max = 8; break;
2935 if ((AI.operand_count >= min) && (AI.operand_count <= max))
2936 assemblez_instruction(&AI);
2937 else error_flag = TRUE;
2940 { make_opcode_syntax_z(O);
2941 error_named("Assembly mistake: syntax is",
2942 opcode_syntax_string);
2946 static assembly_operand parse_operand_g(void)
2947 { assembly_operand AO;
2949 AO = parse_expression(ASSEMBLY_CONTEXT);
2950 if (AO.type == EXPRESSION_OT)
2951 { ebf_error("variable or constant", "expression");
2952 AO.type = CONSTANT_OT;
2957 static void parse_assembly_g(void)
2960 assembly_operand AO;
2961 int error_flag = FALSE, is_macro = FALSE;
2963 AI.operand_count = 0;
2965 opcode_names.enabled = TRUE;
2966 opcode_macros.enabled = TRUE;
2968 opcode_names.enabled = FALSE;
2969 opcode_macros.enabled = FALSE;
2971 if (token_type == DQ_TT) {
2975 AI.internal_number = -1;
2977 /* The format is @"FlagsCount:Code". Flags (which are optional)
2978 can include "S" for store, "SS" for two stores, "B" for branch
2979 format, "R" if execution never continues after the opcode. The
2980 Count is the number of arguments (currently limited to 0-9),
2981 and the Code is a decimal integer representing the opcode
2984 So: @"S3:123" for a three-argument opcode (load, load, store)
2985 whose opcode number is (decimal) 123. Or: @"2:234" for a
2986 two-argument opcode (load, load) whose number is 234. */
2988 custom_opcode_g.name = (uchar *) token_text;
2989 custom_opcode_g.flags = 0;
2990 custom_opcode_g.op_rules = 0;
2991 custom_opcode_g.no = 0;
2995 for (cx = token_text; *cx && *cx != ':'; cx++) {
3001 if (custom_opcode_g.flags & St)
3002 custom_opcode_g.flags |= St2;
3004 custom_opcode_g.flags |= St;
3007 custom_opcode_g.flags |= Br;
3010 custom_opcode_g.flags |= Rf;
3014 custom_opcode_g.no = (*cx) - '0';
3018 error("Unknown custom opcode flag: options are B (branch), \
3019 S (store), SS (two stores), R (execution never continues)");
3025 error("Custom opcode must have colon");
3030 error("Custom opcode must have colon followed by opcode number");
3032 custom_opcode_g.code = atoi(cx);
3035 O = custom_opcode_g;
3038 if (token_type != OPCODE_NAME_TT && token_type != OPCODE_MACRO_TT) {
3039 ebf_error("an opcode name", token_text);
3040 panic_mode_error_recovery();
3043 AI.internal_number = token_value;
3044 if (token_type == OPCODE_MACRO_TT) {
3045 O = internal_number_to_opmacro_g(AI.internal_number);
3049 O = internal_number_to_opcode_g(AI.internal_number);
3052 return_sp_as_variable = TRUE;
3057 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
3060 if (AI.operand_count == 8) {
3061 error("No assembly instruction may have more than 8 operands");
3062 panic_mode_error_recovery();
3066 if ((O.flags & Br) && (AI.operand_count == O.no-1)) {
3067 if (!((token_type == SEP_TT) && (token_value == BRANCH_SEP))) {
3069 error("Branch opcode must have '?' label");
3072 AO.type = CONSTANT_OT;
3073 AO.value = parse_label();
3074 AO.marker = BRANCH_MV;
3078 AO = parse_operand_g();
3081 AI.operand[AI.operand_count] = AO;
3085 return_sp_as_variable = FALSE;
3087 if (O.no != AI.operand_count) {
3093 assembleg_macro(&AI);
3095 assembleg_instruction(&AI);
3099 make_opcode_syntax_g(O);
3100 error_named("Assembly mistake: syntax is",
3101 opcode_syntax_string);
3105 extern void parse_assembly(void)
3113 /* ========================================================================= */
3114 /* Data structure management routines */
3115 /* ------------------------------------------------------------------------- */
3117 extern void asm_begin_pass(void)
3118 { no_instructions = 0;
3120 no_sequence_points = 0;
3122 next_sequence_point = 0;
3126 extern void init_asm_vars(void)
3129 for (i=0;i<16;i++) flags2_requirements[i]=0;
3131 uses_unicode_features = FALSE;
3132 uses_memheap_features = FALSE;
3133 uses_acceleration_features = FALSE;
3134 uses_float_features = FALSE;
3136 sequence_point_follows = TRUE;
3137 label_moved_error_already_given = FALSE;
3139 initialise_memory_block(&zcode_area);
3142 extern void asm_allocate_arrays(void)
3143 { if ((debugfile_switch) && (MAX_LABELS < 2000)) MAX_LABELS = 2000;
3145 variable_tokens = my_calloc(sizeof(int32),
3146 MAX_LOCAL_VARIABLES+MAX_GLOBAL_VARIABLES, "variable tokens");
3147 variable_usage = my_calloc(sizeof(int),
3148 MAX_LOCAL_VARIABLES+MAX_GLOBAL_VARIABLES, "variable usage");
3150 label_offsets = my_calloc(sizeof(int32), MAX_LABELS, "label offsets");
3151 label_symbols = my_calloc(sizeof(int32), MAX_LABELS, "label symbols");
3152 label_next = my_calloc(sizeof(int), MAX_LABELS, "label dll 1");
3153 label_prev = my_calloc(sizeof(int), MAX_LABELS, "label dll 1");
3154 sequence_point_labels
3155 = my_calloc(sizeof(int), MAX_LABELS, "sequence point labels");
3156 sequence_point_locations
3157 = my_calloc(sizeof(debug_location),
3159 "sequence point locations");
3161 zcode_holding_area = my_malloc(MAX_ZCODE_SIZE,"compiled routine code area");
3162 zcode_markers = my_malloc(MAX_ZCODE_SIZE, "compiled routine code area");
3164 named_routine_symbols
3165 = my_calloc(sizeof(int32), MAX_SYMBOLS, "named routine symbols");
3168 extern void asm_free_arrays(void)
3170 my_free(&variable_tokens, "variable tokens");
3171 my_free(&variable_usage, "variable usage");
3173 my_free(&label_offsets, "label offsets");
3174 my_free(&label_symbols, "label symbols");
3175 my_free(&label_next, "label dll 1");
3176 my_free(&label_prev, "label dll 2");
3177 my_free(&sequence_point_labels, "sequence point labels");
3178 my_free(&sequence_point_locations, "sequence point locations");
3180 my_free(&zcode_holding_area, "compiled routine code area");
3181 my_free(&zcode_markers, "compiled routine code markers");
3183 my_free(&named_routine_symbols, "named routine symbols");
3184 deallocate_memory_block(&zcode_area);
3187 /* ========================================================================= */