1 /* ------------------------------------------------------------------------- */
2 /* "asm" : The Inform assembler */
4 /* Copyright (c) Graham Nelson 1993 - 2016 */
6 /* This file is part of Inform. */
8 /* Inform is free software: you can redistribute it and/or modify */
9 /* it under the terms of the GNU General Public License as published by */
10 /* the Free Software Foundation, either version 3 of the License, or */
11 /* (at your option) any later version. */
13 /* Inform is distributed in the hope that it will be useful, */
14 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
15 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
16 /* GNU General Public License for more details. */
18 /* You should have received a copy of the GNU General Public License */
19 /* along with Inform. If not, see https://gnu.org/licenses/ */
21 /* ------------------------------------------------------------------------- */
25 uchar *zcode_holding_area; /* Area holding code yet to be transferred
26 to either zcode_area or temp file no 1 */
27 uchar *zcode_markers; /* Bytes holding marker values for this
29 static int zcode_ha_size; /* Number of bytes in holding area */
31 memory_block zcode_area; /* Block to hold assembled code (if
32 temporary files are not being used) */
34 int32 zmachine_pc; /* PC position of assembly (byte offset
35 from start of Z-code area) */
37 int32 no_instructions; /* Number of instructions assembled */
38 int execution_never_reaches_here, /* TRUE if the current PC value in the
39 code area cannot be reached: e.g. if
40 the previous instruction was a "quit"
41 opcode and no label is set to here */
42 next_label, /* Used to count the labels created all
43 over Inform in current routine, from 0 */
44 next_sequence_point; /* Likewise, for sequence points */
45 int no_sequence_points; /* Kept for statistics purposes only */
47 static int label_moved_error_already_given;
48 /* When one label has moved, all subsequent
49 ones probably have too, and this flag
50 suppresses the runaway chain of error
51 messages which would otherwise result */
53 int sequence_point_follows; /* Will the next instruction assembled */
54 /* be at a sequence point in the routine? */
56 int uses_unicode_features; /* Makes use of Glulx Unicode (3.0)
58 int uses_memheap_features; /* Makes use of Glulx mem/heap (3.1)
60 int uses_acceleration_features; /* Makes use of Glulx acceleration (3.1.1)
62 int uses_float_features; /* Makes use of Glulx floating-point (3.1.2)
65 debug_location statement_debug_location;
66 /* Location of current statement */
69 int32 *variable_tokens; /* The allocated size is
70 (MAX_LOCAL_VARIABLES +
71 MAX_GLOBAL_VARIABLES). The entries
72 MAX_LOCAL_VARIABLES and up give the
73 symbol table index for the names of
74 the global variables */
75 int *variable_usage; /* TRUE if referred to, FALSE otherwise */
77 assembly_instruction AI; /* A structure used to hold the full
78 specification of a single Z-code
79 instruction: effectively this is the
81 assemble_instruction() */
83 static char opcode_syntax_string[128]; /* Text buffer holding the correct
84 syntax for an opcode: used to produce
85 helpful assembler error messages */
87 static int routine_symbol; /* The symbol index of the routine currently
89 static char *routine_name; /* The name of the routine currently being
91 static int routine_locals; /* The number of local variables used by
92 the routine currently being compiled */
94 static int32 routine_start_pc;
96 int32 *named_routine_symbols;
98 static void transfer_routine_z(void);
99 static void transfer_routine_g(void);
101 /* ------------------------------------------------------------------------- */
103 /* ------------------------------------------------------------------------- */
105 static int first_label, last_label;
106 static int32 *label_offsets; /* Double-linked list of label offsets */
107 static int *label_next, /* (i.e. zmachine_pc values) in PC order */
109 static int32 *label_symbols; /* Symbol numbers if defined in source */
111 static int *sequence_point_labels;
112 /* Label numbers for each */
113 static debug_location *sequence_point_locations;
114 /* Source code references for each */
115 /* (used for making debugging file) */
117 static void set_label_offset(int label, int32 offset)
119 if (label >= MAX_LABELS) memoryerror("MAX_LABELS", MAX_LABELS);
121 label_offsets[label] = offset;
122 if (last_label == -1)
123 { label_prev[label] = -1;
127 { label_prev[label] = last_label;
128 label_next[last_label] = label;
131 label_next[label] = -1;
132 label_symbols[label] = -1;
135 /* ------------------------------------------------------------------------- */
136 /* Useful tool for building operands */
137 /* ------------------------------------------------------------------------- */
139 extern void set_constant_ot(assembly_operand *AO)
142 if (AO->value >= 0 && AO->value <= 255)
143 AO->type = SHORT_CONSTANT_OT;
145 AO->type = LONG_CONSTANT_OT;
149 AO->type = ZEROCONSTANT_OT;
150 else if (AO->value >= -0x80 && AO->value < 0x80)
151 AO->type = BYTECONSTANT_OT;
152 else if (AO->value >= -0x8000 && AO->value < 0x8000)
153 AO->type = HALFCONSTANT_OT;
155 AO->type = CONSTANT_OT;
159 extern int is_constant_ot(int otval)
162 return ((otval == LONG_CONSTANT_OT)
163 || (otval == SHORT_CONSTANT_OT));
166 return ((otval == CONSTANT_OT)
167 || (otval == HALFCONSTANT_OT)
168 || (otval == BYTECONSTANT_OT)
169 || (otval == ZEROCONSTANT_OT));
173 extern int is_variable_ot(int otval)
176 return (otval == VARIABLE_OT);
179 return ((otval == LOCALVAR_OT)
180 || (otval == GLOBALVAR_OT));
184 /* ------------------------------------------------------------------------- */
185 /* Used in printing assembly traces */
186 /* ------------------------------------------------------------------------- */
188 extern char *variable_name(int32 i)
190 if (i==0) return("sp");
191 if (i<MAX_LOCAL_VARIABLES) return local_variable_texts[i-1];
194 if (i==255) return("TEMP1");
195 if (i==254) return("TEMP2");
196 if (i==253) return("TEMP3");
197 if (i==252) return("TEMP4");
198 if (i==251) return("self");
199 if (i==250) return("sender");
200 if (i==249) return("sw__var");
201 if (i >= 256 && i < 286)
202 { if (i - 256 < NUMBER_SYSTEM_FUNCTIONS) return system_functions.keywords[i - 256];
203 return "<unnamed system function>";
207 switch (i - MAX_LOCAL_VARIABLES) {
208 case 0: return "temp_global";
209 case 1: return "temp__global2";
210 case 2: return "temp__global3";
211 case 3: return "temp__global4";
212 case 4: return "self";
213 case 5: return "sender";
214 case 6: return "sw__var";
215 case 7: return "sys__glob0";
216 case 8: return "sys__glob1";
217 case 9: return "sys__glob2";
218 case 10: return "sys_statusline_flag";
222 return ((char *) symbs[variable_tokens[i]]);
225 static void print_operand_z(assembly_operand o)
227 { case EXPRESSION_OT: printf("expr_"); break;
228 case LONG_CONSTANT_OT: printf("long_"); break;
229 case SHORT_CONSTANT_OT: printf("short_"); break;
231 if (o.value==0) { printf("sp"); return; }
232 printf("%s", variable_name(o.value)); return;
233 case OMITTED_OT: printf("<no value>"); return;
235 printf("%d", o.value);
238 static void print_operand_g(assembly_operand o)
241 case EXPRESSION_OT: printf("expr_"); break;
242 case CONSTANT_OT: printf("long_"); break;
243 case HALFCONSTANT_OT: printf("short_"); break;
244 case BYTECONSTANT_OT: printf("byte_"); break;
245 case ZEROCONSTANT_OT: printf("zero_"); return;
246 case DEREFERENCE_OT: printf("*"); break;
248 printf("%s (global_%d)", variable_name(o.value), o.value);
254 printf("%s (local_%d)", variable_name(o.value), o.value-1);
257 if (o.value >= 0 && o.value < NUMBER_SYSTEM_FUNCTIONS)
258 printf("%s", system_functions.keywords[o.value]);
260 printf("<unnamed system function>");
262 case OMITTED_OT: printf("<no value>"); return;
263 default: printf("???_"); break;
265 printf("%d", o.value);
268 extern void print_operand(assembly_operand o)
276 /* ------------------------------------------------------------------------- */
277 /* Writing bytes to the code area */
278 /* ------------------------------------------------------------------------- */
280 static void byteout(int32 i, int mv)
281 { if (zcode_ha_size >= MAX_ZCODE_SIZE)
282 memoryerror("MAX_ZCODE_SIZE",MAX_ZCODE_SIZE);
283 zcode_markers[zcode_ha_size] = (uchar) mv;
284 zcode_holding_area[zcode_ha_size++] = (uchar) i;
288 /* ------------------------------------------------------------------------- */
289 /* A database of the 115 canonical Infocom opcodes in Versions 3 to 6 */
290 /* And of the however-many-there-are Glulx opcode */
291 /* ------------------------------------------------------------------------- */
293 typedef struct opcodez
294 { uchar *name; /* Lower case standard name */
295 int version1; /* Valid from this version number... */
296 int version2; /* ...until this one (or forever if this is 0) */
297 int extension; /* In later versions, see this line in extension table:
298 if -1, the opcode is illegal in later versions */
299 int code; /* Opcode number within its operand-number block */
300 int flags; /* Flags (see below) */
301 int op_rules; /* Any unusual operand rule applying (see below) */
302 int flags2_set; /* If not zero, set this bit in Flags 2 in the header
303 of any game using the opcode */
304 int no; /* Number of operands (see below) */
307 typedef struct opcodeg
308 { uchar *name; /* Lower case standard name */
309 int32 code; /* Opcode number */
310 int flags; /* Flags (see below) */
311 int op_rules; /* Any unusual operand rule applying (see below) */
312 int no; /* Number of operands */
315 /* Flags which can be set */
317 #define St 1 /* Store */
318 #define Br 2 /* Branch */
319 #define Rf 4 /* "Return flag": execution never continues after this
320 opcode (e.g., is a return or unconditional jump) */
321 #define St2 8 /* Store2 (second-to-last operand is store (Glulx)) */
323 /* Codes for any unusual operand assembly rules */
327 #define VARIAB 1 /* First operand expected to be a variable name and
328 assembled to a short constant: the variable number */
329 #define TEXT 2 /* One text operand, to be Z-encoded into the program */
330 #define LABEL 3 /* One operand, a label, given as long constant offset */
331 #define CALL 4 /* First operand is name of a routine, to be assembled
332 as long constant (the routine's packed address):
333 as if the name were prefixed by #r$ */
335 /* Glulx: (bit flags for Glulx VM features) */
337 #define GOP_Unicode 1 /* uses_unicode_features */
338 #define GOP_MemHeap 2 /* uses_memheap_features */
339 #define GOP_Acceleration 4 /* uses_acceleration_features */
340 #define GOP_Float 8 /* uses_float_features */
342 /* Codes for the number of operands */
344 #define TWO 1 /* 2 (with certain types of operand, compiled as VAR) */
345 #define VAR 2 /* 0 to 4 */
346 #define VAR_LONG 3 /* 0 to 8 */
347 #define ONE 4 /* 1 */
348 #define ZERO 5 /* 0 */
349 #define EXT 6 /* Extended opcode set VAR: 0 to 4 */
350 #define EXT_LONG 7 /* Extended: 0 to 8 (not used by the canonical opcodes) */
352 static opcodez opcodes_table_z[] =
354 /* Opcodes introduced in Version 3 */
356 /* 0 */ { (uchar *) "je", 3, 0, -1, 0x01, Br, 0, 0, TWO },
357 /* 1 */ { (uchar *) "jl", 3, 0, -1, 0x02, Br, 0, 0, TWO },
358 /* 2 */ { (uchar *) "jg", 3, 0, -1, 0x03, Br, 0, 0, TWO },
359 /* 3 */ { (uchar *) "dec_chk", 3, 0, -1, 0x04, Br, VARIAB, 0, TWO },
360 /* 4 */ { (uchar *) "inc_chk", 3, 0, -1, 0x05, Br, VARIAB, 0, TWO },
361 /* 5 */ { (uchar *) "jin", 3, 0, -1, 0x06, Br, 0, 0, TWO },
362 /* 6 */ { (uchar *) "test", 3, 0, -1, 0x07, Br, 0, 0, TWO },
363 /* 7 */ { (uchar *) "or", 3, 0, -1, 0x08, St, 0, 0, TWO },
364 /* 8 */ { (uchar *) "and", 3, 0, -1, 0x09, St, 0, 0, TWO },
365 /* 9 */ { (uchar *) "test_attr", 3, 0, -1, 0x0A, Br, 0, 0, TWO },
366 /* 10 */ {(uchar *) "set_attr", 3, 0, -1, 0x0B, 0, 0, 0, TWO },
367 /* 11 */ {(uchar *) "clear_attr", 3, 0, -1, 0x0C, 0, 0, 0, TWO },
368 /* 12 */ {(uchar *) "store", 3, 0, -1, 0x0D, 0, VARIAB, 0, TWO },
369 /* 13 */ {(uchar *) "insert_obj", 3, 0, -1, 0x0E, 0, 0, 0, TWO },
370 /* 14 */ {(uchar *) "loadw", 3, 0, -1, 0x0F, St, 0, 0, TWO },
371 /* 15 */ {(uchar *) "loadb", 3, 0, -1, 0x10, St, 0, 0, TWO },
372 /* 16 */ {(uchar *) "get_prop", 3, 0, -1, 0x11, St, 0, 0, TWO },
373 /* 17 */ {(uchar *) "get_prop_addr", 3, 0, -1, 0x12, St, 0, 0, TWO },
374 /* 18 */ {(uchar *) "get_next_prop", 3, 0, -1, 0x13, St, 0, 0, TWO },
375 /* 19 */ {(uchar *) "add", 3, 0, -1, 0x14, St, 0, 0, TWO },
376 /* 20 */ {(uchar *) "sub", 3, 0, -1, 0x15, St, 0, 0, TWO },
377 /* 21 */ {(uchar *) "mul", 3, 0, -1, 0x16, St, 0, 0, TWO },
378 /* 22 */ {(uchar *) "div", 3, 0, -1, 0x17, St, 0, 0, TWO },
379 /* 23 */ {(uchar *) "mod", 3, 0, -1, 0x18, St, 0, 0, TWO },
380 /* 24 */ {(uchar *) "call", 3, 0, -1, 0x20, St, CALL, 0, VAR },
381 /* 25 */ {(uchar *) "storew", 3, 0, -1, 0x21, 0, 0, 0, VAR },
382 /* 26 */ {(uchar *) "storeb", 3, 0, -1, 0x22, 0, 0, 0, VAR },
383 /* 27 */ {(uchar *) "put_prop", 3, 0, -1, 0x23, 0, 0, 0, VAR },
384 /* This is the version of "read" called "sread" internally: */
385 /* 28 */ {(uchar *) "read", 3, 0, -1, 0x24, 0, 0, 0, VAR },
386 /* 29 */ {(uchar *) "print_char", 3, 0, -1, 0x25, 0, 0, 0, VAR },
387 /* 30 */ {(uchar *) "print_num", 3, 0, -1, 0x26, 0, 0, 0, VAR },
388 /* 31 */ {(uchar *) "random", 3, 0, -1, 0x27, St, 0, 0, VAR },
389 /* 32 */ {(uchar *) "push", 3, 0, -1, 0x28, 0, 0, 0, VAR },
390 /* 33 */ {(uchar *) "pull", 3, 5, 6, 0x29, 0, VARIAB, 0, VAR },
391 /* 34 */ {(uchar *) "split_window", 3, 0, -1, 0x2A, 0, 0, 0, VAR },
392 /* 35 */ {(uchar *) "set_window", 3, 0, -1, 0x2B, 0, 0, 0, VAR },
393 /* 36 */ {(uchar *) "output_stream", 3, 0, -1, 0x33, 0, 0, 0, VAR },
394 /* 37 */ {(uchar *) "input_stream", 3, 0, -1, 0x34, 0, 0, 0, VAR },
395 /* 38 */ {(uchar *) "sound_effect", 3, 0, -1, 0x35, 0, 0, 7, VAR },
396 /* 39 */ {(uchar *) "jz", 3, 0, -1, 0x00, Br, 0, 0, ONE },
397 /* 40 */ {(uchar *) "get_sibling", 3, 0, -1, 0x01, St+Br, 0, 0, ONE },
398 /* 41 */ {(uchar *) "get_child", 3, 0, -1, 0x02, St+Br, 0, 0, ONE },
399 /* 42 */ {(uchar *) "get_parent", 3, 0, -1, 0x03, St, 0, 0, ONE },
400 /* 43 */ {(uchar *) "get_prop_len", 3, 0, -1, 0x04, St, 0, 0, ONE },
401 /* 44 */ {(uchar *) "inc", 3, 0, -1, 0x05, 0, VARIAB, 0, ONE },
402 /* 45 */ {(uchar *) "dec", 3, 0, -1, 0x06, 0, VARIAB, 0, ONE },
403 /* 46 */ {(uchar *) "print_addr", 3, 0, -1, 0x07, 0, 0, 0, ONE },
404 /* 47 */ {(uchar *) "remove_obj", 3, 0, -1, 0x09, 0, 0, 0, ONE },
405 /* 48 */ {(uchar *) "print_obj", 3, 0, -1, 0x0A, 0, 0, 0, ONE },
406 /* 49 */ {(uchar *) "ret", 3, 0, -1, 0x0B, Rf, 0, 0, ONE },
407 /* 50 */ {(uchar *) "jump", 3, 0, -1, 0x0C, Rf, LABEL, 0, ONE },
408 /* 51 */ {(uchar *) "print_paddr", 3, 0, -1, 0x0D, 0, 0, 0, ONE },
409 /* 52 */ {(uchar *) "load", 3, 0, -1, 0x0E, St, VARIAB, 0, ONE },
410 /* 53 */ {(uchar *) "not", 3, 3, 0, 0x0F, St, 0, 0, ONE },
411 /* 54 */ {(uchar *) "rtrue", 3, 0, -1, 0x00, Rf, 0, 0,ZERO },
412 /* 55 */ {(uchar *) "rfalse", 3, 0, -1, 0x01, Rf, 0, 0,ZERO },
413 /* 56 */ {(uchar *) "print", 3, 0, -1, 0x02, 0, TEXT, 0,ZERO },
414 /* 57 */ {(uchar *) "print_ret", 3, 0, -1, 0x03, Rf, TEXT, 0,ZERO },
415 /* 58 */ {(uchar *) "nop", 3, 0, -1, 0x04, 0, 0, 0,ZERO },
416 /* 59 */ {(uchar *) "save", 3, 3, 1, 0x05, Br, 0, 0,ZERO },
417 /* 60 */ {(uchar *) "restore", 3, 3, 2, 0x06, Br, 0, 0,ZERO },
418 /* 61 */ {(uchar *) "restart", 3, 0, -1, 0x07, 0, 0, 0,ZERO },
419 /* 62 */ {(uchar *) "ret_popped", 3, 0, -1, 0x08, Rf, 0, 0,ZERO },
420 /* 63 */ {(uchar *) "pop", 3, 4, -1, 0x09, 0, 0, 0,ZERO },
421 /* 64 */ {(uchar *) "quit", 3, 0, -1, 0x0A, Rf, 0, 0,ZERO },
422 /* 65 */ {(uchar *) "new_line", 3, 0, -1, 0x0B, 0, 0, 0,ZERO },
423 /* 66 */ {(uchar *) "show_status", 3, 3, -1, 0x0C, 0, 0, 0,ZERO },
424 /* 67 */ {(uchar *) "verify", 3, 0, -1, 0x0D, Br, 0, 0,ZERO },
426 /* Opcodes introduced in Version 4 */
428 /* 68 */ {(uchar *) "call_2s", 4, 0, -1, 0x19, St, CALL, 0, TWO },
429 /* 69 */ {(uchar *) "call_vs", 4, 0, -1, 0x20, St, CALL, 0, VAR },
430 /* This is the version of "read" called "aread" internally: */
431 /* 70 */ {(uchar *) "read", 4, 0, -1, 0x24, St, 0, 0, VAR },
432 /* 71 */ {(uchar *) "call_vs2", 4, 0, -1, 0x2C, St, CALL, 0,
434 /* 72 */ {(uchar *) "erase_window", 4, 0, -1, 0x2D, 0, 0, 0, VAR },
435 /* 73 */ {(uchar *) "erase_line", 4, 0, -1, 0x2E, 0, 0, 0, VAR },
436 /* 74 */ {(uchar *) "set_cursor", 4, 0, -1, 0x2F, 0, 0, 0, VAR },
437 /* 75 */ {(uchar *) "get_cursor", 4, 0, -1, 0x30, 0, 0, 0, VAR },
438 /* 76 */ {(uchar *) "set_text_style", 4, 0, -1, 0x31, 0, 0, 0, VAR },
439 /* 77 */ {(uchar *) "buffer_mode", 4, 0, -1, 0x32, 0, 0, 0, VAR },
440 /* 78 */ {(uchar *) "read_char", 4, 0, -1, 0x36, St, 0, 0, VAR },
441 /* 79 */ {(uchar *) "scan_table", 4, 0, -1, 0x37, St+Br, 0, 0, VAR },
442 /* 80 */ {(uchar *) "call_1s", 4, 0, -1, 0x08, St, CALL, 0, ONE },
444 /* Opcodes introduced in Version 5 */
446 /* 81 */ {(uchar *) "call_2n", 5, 0, -1, 0x1a, 0, CALL, 0, TWO },
447 /* 82 */ {(uchar *) "set_colour", 5, 0, -1, 0x1b, 0, 0, 6, TWO },
448 /* 83 */ {(uchar *) "throw", 5, 0, -1, 0x1c, 0, 0, 0, TWO },
449 /* 84 */ {(uchar *) "call_vn", 5, 0, -1, 0x39, 0, CALL, 0, VAR },
450 /* 85 */ {(uchar *) "call_vn2", 5, 0, -1, 0x3a, 0, CALL, 0,
452 /* 86 */ {(uchar *) "tokenise", 5, 0, -1, 0x3b, 0, 0, 0, VAR },
453 /* 87 */ {(uchar *) "encode_text", 5, 0, -1, 0x3c, 0, 0, 0, VAR },
454 /* 88 */ {(uchar *) "copy_table", 5, 0, -1, 0x3d, 0, 0, 0, VAR },
455 /* 89 */ {(uchar *) "print_table", 5, 0, -1, 0x3e, 0, 0, 0, VAR },
456 /* 90 */ {(uchar *) "check_arg_count", 5, 0, -1, 0x3f, Br, 0, 0, VAR },
457 /* 91 */ {(uchar *) "call_1n", 5, 0, -1, 0x0F, 0, CALL, 0, ONE },
458 /* 92 */ {(uchar *) "catch", 5, 0, -1, 0x09, St, 0, 0, ZERO },
459 /* 93 */ {(uchar *) "piracy", 5, 0, -1, 0x0F, Br, 0, 0, ZERO },
460 /* 94 */ {(uchar *) "log_shift", 5, 0, -1, 0x02, St, 0, 0, EXT },
461 /* 95 */ {(uchar *) "art_shift", 5, 0, -1, 0x03, St, 0, 0, EXT },
462 /* 96 */ {(uchar *) "set_font", 5, 0, -1, 0x04, St, 0, 0, EXT },
463 /* 97 */ {(uchar *) "save_undo", 5, 0, -1, 0x09, St, 0, 4, EXT },
464 /* 98 */ {(uchar *) "restore_undo", 5, 0, -1, 0x0A, St, 0, 4, EXT },
466 /* Opcodes introduced in Version 6 */
468 /* 99 */ { (uchar *) "draw_picture", 6, 6, -1, 0x05, 0, 0, 3, EXT },
469 /* 100 */ { (uchar *) "picture_data", 6, 6, -1, 0x06, Br, 0, 3, EXT },
470 /* 101 */ { (uchar *) "erase_picture", 6, 6, -1, 0x07, 0, 0, 3, EXT },
471 /* 102 */ { (uchar *) "set_margins", 6, 6, -1, 0x08, 0, 0, 0, EXT },
472 /* 103 */ { (uchar *) "move_window", 6, 6, -1, 0x10, 0, 0, 0, EXT },
473 /* 104 */ { (uchar *) "window_size", 6, 6, -1, 0x11, 0, 0, 0, EXT },
474 /* 105 */ { (uchar *) "window_style", 6, 6, -1, 0x12, 0, 0, 0, EXT },
475 /* 106 */ { (uchar *) "get_wind_prop", 6, 6, -1, 0x13, St, 0, 0, EXT },
476 /* 107 */ { (uchar *) "scroll_window", 6, 6, -1, 0x14, 0, 0, 0, EXT },
477 /* 108 */ { (uchar *) "pop_stack", 6, 6, -1, 0x15, 0, 0, 0, EXT },
478 /* 109 */ { (uchar *) "read_mouse", 6, 6, -1, 0x16, 0, 0, 5, EXT },
479 /* 110 */ { (uchar *) "mouse_window", 6, 6, -1, 0x17, 0, 0, 5, EXT },
480 /* 111 */ { (uchar *) "push_stack", 6, 6, -1, 0x18, Br, 0, 0, EXT },
481 /* 112 */ { (uchar *) "put_wind_prop", 6, 6, -1, 0x19, 0, 0, 0, EXT },
482 /* 113 */ { (uchar *) "print_form", 6, 6, -1, 0x1a, 0, 0, 0, EXT },
483 /* 114 */ { (uchar *) "make_menu", 6, 6, -1, 0x1b, Br, 0, 8, EXT },
484 /* 115 */ { (uchar *) "picture_table", 6, 6, -1, 0x1c, 0, 0, 3, EXT },
486 /* Opcodes introduced in Z-Machine Specification Standard 1.0 */
488 /* 116 */ { (uchar *) "print_unicode", 5, 0, -1, 0x0b, 0, 0, 0, EXT },
489 /* 117 */ { (uchar *) "check_unicode", 5, 0, -1, 0x0c, St, 0, 0, EXT }
492 /* Subsequent forms for opcodes whose meaning changes with version */
494 static opcodez extension_table_z[] =
496 /* 0 */ { (uchar *) "not", 4, 4, 3, 0x0F, St, 0, 0, ONE },
497 /* 1 */ { (uchar *) "save", 4, 4, 4, 0x05, St, 0, 0,ZERO },
498 /* 2 */ { (uchar *) "restore", 4, 4, 5, 0x06, St, 0, 0,ZERO },
499 /* 3 */ { (uchar *) "not", 5, 0, -1, 0x38, St, 0, 0, VAR },
500 /* 4 */ { (uchar *) "save", 5, 0, -1, 0x00, St, 0, 0, EXT },
501 /* 5 */ { (uchar *) "restore", 5, 0, -1, 0x01, St, 0, 0, EXT },
502 /* 6 */ { (uchar *) "pull", 6, 6, -1, 0x29, St, 0, 0, VAR }
505 static opcodez invalid_opcode_z =
506 { (uchar *) "invalid", 0, 0, -1, 0xff, 0, 0, 0, ZERO};
508 static opcodez custom_opcode_z;
510 /* Note that this table assumes that all opcodes have at most two
511 branch-label or store operands, and that if they exist, they are the
512 last operands. Glulx does not actually guarantee this. But it is
513 true for all opcodes in the current Glulx spec, so we will assume
516 Also note that Inform can only compile branches to constant offsets,
517 even though the Glulx machine can handle stack or memory-loaded
518 operands in a branch instruction.
521 static opcodeg opcodes_table_g[] = {
522 { (uchar *) "nop", 0x00, 0, 0, 0 },
523 { (uchar *) "add", 0x10, St, 0, 3 },
524 { (uchar *) "sub", 0x11, St, 0, 3 },
525 { (uchar *) "mul", 0x12, St, 0, 3 },
526 { (uchar *) "div", 0x13, St, 0, 3 },
527 { (uchar *) "mod", 0x14, St, 0, 3 },
528 { (uchar *) "neg", 0x15, St, 0, 2 },
529 { (uchar *) "bitand", 0x18, St, 0, 3 },
530 { (uchar *) "bitor", 0x19, St, 0, 3 },
531 { (uchar *) "bitxor", 0x1A, St, 0, 3 },
532 { (uchar *) "bitnot", 0x1B, St, 0, 2 },
533 { (uchar *) "shiftl", 0x1C, St, 0, 3 },
534 { (uchar *) "sshiftr", 0x1D, St, 0, 3 },
535 { (uchar *) "ushiftr", 0x1E, St, 0, 3 },
536 { (uchar *) "jump", 0x20, Br|Rf, 0, 1 },
537 { (uchar *) "jz", 0x22, Br, 0, 2 },
538 { (uchar *) "jnz", 0x23, Br, 0, 2 },
539 { (uchar *) "jeq", 0x24, Br, 0, 3 },
540 { (uchar *) "jne", 0x25, Br, 0, 3 },
541 { (uchar *) "jlt", 0x26, Br, 0, 3 },
542 { (uchar *) "jge", 0x27, Br, 0, 3 },
543 { (uchar *) "jgt", 0x28, Br, 0, 3 },
544 { (uchar *) "jle", 0x29, Br, 0, 3 },
545 { (uchar *) "jltu", 0x2A, Br, 0, 3 },
546 { (uchar *) "jgeu", 0x2B, Br, 0, 3 },
547 { (uchar *) "jgtu", 0x2C, Br, 0, 3 },
548 { (uchar *) "jleu", 0x2D, Br, 0, 3 },
549 { (uchar *) "call", 0x30, St, 0, 3 },
550 { (uchar *) "return", 0x31, Rf, 0, 1 },
551 { (uchar *) "catch", 0x32, Br|St, 0, 2 },
552 { (uchar *) "throw", 0x33, Rf, 0, 2 },
553 { (uchar *) "tailcall", 0x34, Rf, 0, 2 },
554 { (uchar *) "copy", 0x40, St, 0, 2 },
555 { (uchar *) "copys", 0x41, St, 0, 2 },
556 { (uchar *) "copyb", 0x42, St, 0, 2 },
557 { (uchar *) "sexs", 0x44, St, 0, 2 },
558 { (uchar *) "sexb", 0x45, St, 0, 2 },
559 { (uchar *) "aload", 0x48, St, 0, 3 },
560 { (uchar *) "aloads", 0x49, St, 0, 3 },
561 { (uchar *) "aloadb", 0x4A, St, 0, 3 },
562 { (uchar *) "aloadbit", 0x4B, St, 0, 3 },
563 { (uchar *) "astore", 0x4C, 0, 0, 3 },
564 { (uchar *) "astores", 0x4D, 0, 0, 3 },
565 { (uchar *) "astoreb", 0x4E, 0, 0, 3 },
566 { (uchar *) "astorebit", 0x4F, 0, 0, 3 },
567 { (uchar *) "stkcount", 0x50, St, 0, 1 },
568 { (uchar *) "stkpeek", 0x51, St, 0, 2 },
569 { (uchar *) "stkswap", 0x52, 0, 0, 0 },
570 { (uchar *) "stkroll", 0x53, 0, 0, 2 },
571 { (uchar *) "stkcopy", 0x54, 0, 0, 1 },
572 { (uchar *) "streamchar", 0x70, 0, 0, 1 },
573 { (uchar *) "streamnum", 0x71, 0, 0, 1 },
574 { (uchar *) "streamstr", 0x72, 0, 0, 1 },
575 { (uchar *) "gestalt", 0x0100, St, 0, 3 },
576 { (uchar *) "debugtrap", 0x0101, 0, 0, 1 },
577 { (uchar *) "getmemsize", 0x0102, St, 0, 1 },
578 { (uchar *) "setmemsize", 0x0103, St, 0, 2 },
579 { (uchar *) "jumpabs", 0x0104, Rf, 0, 1 },
580 { (uchar *) "random", 0x0110, St, 0, 2 },
581 { (uchar *) "setrandom", 0x0111, 0, 0, 1 },
582 { (uchar *) "quit", 0x0120, Rf, 0, 0 },
583 { (uchar *) "verify", 0x0121, St, 0, 1 },
584 { (uchar *) "restart", 0x0122, 0, 0, 0 },
585 { (uchar *) "save", 0x0123, St, 0, 2 },
586 { (uchar *) "restore", 0x0124, St, 0, 2 },
587 { (uchar *) "saveundo", 0x0125, St, 0, 1 },
588 { (uchar *) "restoreundo", 0x0126, St, 0, 1 },
589 { (uchar *) "protect", 0x0127, 0, 0, 2 },
590 { (uchar *) "glk", 0x0130, St, 0, 3 },
591 { (uchar *) "getstringtbl", 0x0140, St, 0, 1 },
592 { (uchar *) "setstringtbl", 0x0141, 0, 0, 1 },
593 { (uchar *) "getiosys", 0x0148, St|St2, 0, 2 },
594 { (uchar *) "setiosys", 0x0149, 0, 0, 2 },
595 { (uchar *) "linearsearch", 0x0150, St, 0, 8 },
596 { (uchar *) "binarysearch", 0x0151, St, 0, 8 },
597 { (uchar *) "linkedsearch", 0x0152, St, 0, 7 },
598 { (uchar *) "callf", 0x0160, St, 0, 2 },
599 { (uchar *) "callfi", 0x0161, St, 0, 3 },
600 { (uchar *) "callfii", 0x0162, St, 0, 4 },
601 { (uchar *) "callfiii", 0x0163, St, 0, 5 },
602 { (uchar *) "streamunichar", 0x73, 0, GOP_Unicode, 1 },
603 { (uchar *) "mzero", 0x170, 0, GOP_MemHeap, 2 },
604 { (uchar *) "mcopy", 0x171, 0, GOP_MemHeap, 3 },
605 { (uchar *) "malloc", 0x178, St, GOP_MemHeap, 2 },
606 { (uchar *) "mfree", 0x179, 0, GOP_MemHeap, 1 },
607 { (uchar *) "accelfunc", 0x180, 0, GOP_Acceleration, 2 },
608 { (uchar *) "accelparam", 0x181, 0, GOP_Acceleration, 2 },
609 { (uchar *) "numtof", 0x190, St, GOP_Float, 2 },
610 { (uchar *) "ftonumz", 0x191, St, GOP_Float, 2 },
611 { (uchar *) "ftonumn", 0x192, St, GOP_Float, 2 },
612 { (uchar *) "ceil", 0x198, St, GOP_Float, 2 },
613 { (uchar *) "floor", 0x199, St, GOP_Float, 2 },
614 { (uchar *) "fadd", 0x1A0, St, GOP_Float, 3 },
615 { (uchar *) "fsub", 0x1A1, St, GOP_Float, 3 },
616 { (uchar *) "fmul", 0x1A2, St, GOP_Float, 3 },
617 { (uchar *) "fdiv", 0x1A3, St, GOP_Float, 3 },
618 { (uchar *) "fmod", 0x1A4, St|St2, GOP_Float, 4 },
619 { (uchar *) "sqrt", 0x1A8, St, GOP_Float, 2 },
620 { (uchar *) "exp", 0x1A9, St, GOP_Float, 2 },
621 { (uchar *) "log", 0x1AA, St, GOP_Float, 2 },
622 { (uchar *) "pow", 0x1AB, St, GOP_Float, 3 },
623 { (uchar *) "sin", 0x1B0, St, GOP_Float, 2 },
624 { (uchar *) "cos", 0x1B1, St, GOP_Float, 2 },
625 { (uchar *) "tan", 0x1B2, St, GOP_Float, 2 },
626 { (uchar *) "asin", 0x1B3, St, GOP_Float, 2 },
627 { (uchar *) "acos", 0x1B4, St, GOP_Float, 2 },
628 { (uchar *) "atan", 0x1B5, St, GOP_Float, 2 },
629 { (uchar *) "atan2", 0x1B6, St, GOP_Float, 3 },
630 { (uchar *) "jfeq", 0x1C0, Br, GOP_Float, 4 },
631 { (uchar *) "jfne", 0x1C1, Br, GOP_Float, 4 },
632 { (uchar *) "jflt", 0x1C2, Br, GOP_Float, 3 },
633 { (uchar *) "jfle", 0x1C3, Br, GOP_Float, 3 },
634 { (uchar *) "jfgt", 0x1C4, Br, GOP_Float, 3 },
635 { (uchar *) "jfge", 0x1C5, Br, GOP_Float, 3 },
636 { (uchar *) "jisnan", 0x1C8, Br, GOP_Float, 2 },
637 { (uchar *) "jisinf", 0x1C9, Br, GOP_Float, 2 },
640 /* The opmacros table is used for fake opcodes. The opcode numbers are
641 ignored; this table is only used for argument parsing. */
642 static opcodeg opmacros_table_g[] = {
643 { (uchar *) "pull", 0, St, 0, 1 },
644 { (uchar *) "push", 0, 0, 0, 1 },
647 static opcodeg custom_opcode_g;
649 static opcodez internal_number_to_opcode_z(int32 i)
652 if (i == -1) return custom_opcode_z;
653 x = opcodes_table_z[i];
654 if (instruction_set_number < x.version1) return invalid_opcode_z;
655 if (x.version2 == 0) return x;
656 if (instruction_set_number <= x.version2) return x;
658 if (i < 0) return invalid_opcode_z;
659 x = extension_table_z[i];
660 if (instruction_set_number < x.version1) return invalid_opcode_z;
661 if (x.version2 == 0) return x;
662 if (instruction_set_number <= x.version2) return x;
663 return extension_table_z[x.extension];
666 static void make_opcode_syntax_z(opcodez opco)
667 { char *p = "", *q = opcode_syntax_string;
668 sprintf(q, "%s", opco.name);
670 { case ONE: p=" <operand>"; break;
671 case TWO: p=" <operand1> <operand2>"; break;
673 case VAR: p=" <0 to 4 operands>"; break;
674 case VAR_LONG: p=" <0 to 8 operands>"; break;
676 switch(opco.op_rules)
677 { case TEXT: sprintf(q+strlen(q), " <text>"); return;
678 case LABEL: sprintf(q+strlen(q), " <label>"); return;
680 sprintf(q+strlen(q), " <variable>");
682 if (opco.op_rules==CALL) sprintf(q+strlen(q), " <routine>");
684 { case ONE: p=""; break;
685 case TWO: p=" <operand>"; break;
687 case VAR: p=" <1 to 4 operands>"; break;
688 case VAR_LONG: p=" <1 to 8 operands>"; break;
692 sprintf(q+strlen(q), "%s", p);
693 if ((opco.flags & St) != 0) sprintf(q+strlen(q), " -> <result-variable>");
694 if ((opco.flags & Br) != 0) sprintf(q+strlen(q), " ?[~]<label>");
697 static opcodeg internal_number_to_opcode_g(int32 i)
700 if (i == -1) return custom_opcode_g;
701 x = opcodes_table_g[i];
705 static opcodeg internal_number_to_opmacro_g(int32 i)
707 return opmacros_table_g[i];
710 static void make_opcode_syntax_g(opcodeg opco)
714 char *q = opcode_syntax_string;
716 sprintf(q, "%s", opco.name);
717 sprintf(q+strlen(q), " <%d operand%s", opco.no,
718 ((opco.no==1) ? "" : "s"));
723 for (ix=0; ix<opco.no; ix++) {
728 if (ix == opco.no-1) {
729 if (opco.flags & Br) {
732 else if (opco.flags & St) {
739 else if (ix == opco.no-2 && (opco.flags & Br) && (opco.flags & St)) {
742 else if (ix == opco.no-2 && (opco.flags & St2)) {
749 sprintf(cx, "%d", ix+1);
753 sprintf(q+strlen(q), ">");
757 /* ========================================================================= */
758 /* The assembler itself does four things: */
760 /* assembles instructions */
761 /* sets label N to the current code position */
762 /* assembles routine headers */
763 /* assembles routine ends */
764 /* ------------------------------------------------------------------------- */
766 /* This is for Z-code only. */
767 static void write_operand(assembly_operand op)
769 if (module_switch && (op.marker != 0))
770 { if ((op.marker != VARIABLE_MV) && (op.type == SHORT_CONSTANT_OT))
771 op.type = LONG_CONSTANT_OT;
775 { case LONG_CONSTANT_OT:
776 byteout(j/256, op.marker); byteout(j%256, 0); return;
777 case SHORT_CONSTANT_OT:
780 else byteout(j, 0x80 + op.marker); return;
782 byteout(j, (module_switch)?(0x80 + op.marker):0); return;
784 case HALFCONSTANT_OT:
785 case BYTECONSTANT_OT:
786 case ZEROCONSTANT_OT:
791 compiler_error("Glulx OT in Z-code assembly operand.");
796 extern void assemblez_instruction(assembly_instruction *AI)
798 uchar *start_pc, *operands_pc;
799 int32 offset, j, topbits=0, types_byte1, types_byte2;
800 int operand_rules, min=0, max=0, no_operands_given, at_seq_point = FALSE;
801 assembly_operand o1, o2;
806 offset = zmachine_pc;
810 if (veneer_mode) sequence_point_follows = FALSE;
811 if (sequence_point_follows)
812 { sequence_point_follows = FALSE; at_seq_point = TRUE;
813 if (debugfile_switch)
814 { sequence_point_labels[next_sequence_point] = next_label;
815 sequence_point_locations[next_sequence_point] =
816 statement_debug_location;
817 set_label_offset(next_label++, zmachine_pc);
819 next_sequence_point++;
822 opco = internal_number_to_opcode_z(AI->internal_number);
823 if (opco.version1==0)
824 { error_named("Opcode unavailable in this Z-machine version",
825 opcode_names.keywords[AI->internal_number]);
829 if (execution_never_reaches_here)
830 warning("This statement can never be reached");
832 operand_rules = opco.op_rules;
833 execution_never_reaches_here = ((opco.flags & Rf) != 0);
835 if (opco.flags2_set != 0) flags2_requirements[opco.flags2_set] = 1;
837 no_operands_given = AI->operand_count;
839 if ((opco.no == TWO) && ((no_operands_given==3)||(no_operands_given==4)))
842 /* 1. Write the opcode byte(s) */
844 start_pc = zcode_holding_area + zcode_ha_size;
847 { case VAR_LONG: topbits=0xc0; min=0; max=8; break;
848 case VAR: topbits=0xc0; min=0; max=4; break;
849 case ZERO: topbits=0xb0; min=0; max=0; break;
850 case ONE: topbits=0x80; min=1; max=1; break;
851 case TWO: topbits=0x00; min=2; max=2; break;
852 case EXT: topbits=0x00; min=0; max=4;
853 byteout(0xbe, 0); opco.no=VAR; break;
854 case EXT_LONG: topbits=0x00; min=0; max=8;
855 byteout(0xbe, 0); opco.no=VAR_LONG; break;
857 byteout(opco.code + topbits, 0);
859 operands_pc = zcode_holding_area + zcode_ha_size;
861 /* 2. Dispose of the special rules LABEL and TEXT */
863 if (operand_rules==LABEL)
864 { j = (AI->operand[0]).value;
865 byteout(j/256, LABEL_MV); byteout(j%256, 0);
866 goto Instruction_Done;
869 if (operand_rules==TEXT)
871 uchar *tmp = translate_text(zcode_holding_area + zcode_ha_size, zcode_holding_area+MAX_ZCODE_SIZE, AI->text);
873 memoryerror("MAX_ZCODE_SIZE", MAX_ZCODE_SIZE);
874 j = subtract_pointers(tmp, (zcode_holding_area + zcode_ha_size));
875 for (i=0; i<j; i++) zcode_markers[zcode_ha_size++] = 0;
877 goto Instruction_Done;
880 /* 3. Sort out the operands */
882 if ((no_operands_given < min) || (no_operands_given > max))
883 goto OpcodeSyntaxError;
889 if (opco.no == VAR_LONG) byteout(0, 0);
890 types_byte1=0xff; types_byte2=0xff;
891 for (j=0; j<no_operands_given; j++)
892 { int multi=0, mask=0;
894 { case 0: case 4: multi=0x40; mask=0xc0; break;
895 case 1: case 5: multi=0x10; mask=0x30; break;
896 case 2: case 6: multi=0x04; mask=0x0c; break;
897 case 3: case 7: multi=0x01; mask=0x03; break;
902 types_byte1 = (types_byte1 & (~mask)) + o1.type*multi;
904 types_byte2 = (types_byte2 & (~mask)) + o1.type*multi;
906 *operands_pc=types_byte1;
907 if (opco.no == VAR_LONG) *(operands_pc+1)=types_byte2;
912 *start_pc=(*start_pc) + o1.type*0x10;
920 /* Transfer to VAR form if either operand is a long constant */
922 if ((o1.type==LONG_CONSTANT_OT)||(o2.type==LONG_CONSTANT_OT))
923 { *start_pc=(*start_pc) + 0xc0;
924 byteout(o1.type*0x40 + o2.type*0x10 + 0x0f, 0);
927 { if (o1.type==VARIABLE_OT) *start_pc=(*start_pc) + 0x40;
928 if (o2.type==VARIABLE_OT) *start_pc=(*start_pc) + 0x20;
935 /* 4. Assemble a Store destination, if needed */
937 if ((AI->store_variable_number) != -1)
938 { if (AI->store_variable_number >= MAX_LOCAL_VARIABLES+MAX_GLOBAL_VARIABLES) {
939 goto OpcodeSyntaxError;
941 o1.type = VARIABLE_OT;
942 o1.value = AI->store_variable_number;
943 variable_usage[o1.value] = TRUE;
946 /* Note that variable numbers 249 to 255 (i.e. globals 233 to 239)
947 are used as scratch workspace, so need no mapping between
948 modules and story files: nor do local variables 0 to 15 */
950 if ((o1.value >= MAX_LOCAL_VARIABLES) && (o1.value < 249))
951 o1.marker = VARIABLE_MV;
955 /* 5. Assemble a branch, if needed */
957 if (AI->branch_label_number != -1)
958 { int32 addr, long_form;
959 int branch_on_true = (AI->branch_flag)?1:0;
961 switch (AI->branch_label_number)
962 { case -2: addr = 2; branch_on_true = 0; long_form = 0; break;
963 /* branch nowhere, carry on */
964 case -3: addr = 0; long_form = 0; break; /* rfalse on condition */
965 case -4: addr = 1; long_form = 0; break; /* rtrue on condition */
967 long_form = 1; addr = AI->branch_label_number;
970 if (addr > 0x7fff) fatalerror("Too many branch points in routine.");
972 { byteout(branch_on_true*0x80 + addr/256, BRANCH_MV);
973 byteout(addr%256, 0);
976 byteout(branch_on_true*0x80+ 0x40 + (addr&0x3f), 0);
981 if (asm_trace_level > 0)
983 printf("%5d +%05lx %3s %-12s ", ErrorReport.line_number,
985 (at_seq_point)?"<*>":" ", opco.name);
987 if ((AI->internal_number == print_zc)
988 || (AI->internal_number == print_ret_zc))
990 for (i=0;(AI->text)[i]!=0 && i<35; i++) printf("%c",(AI->text)[i]);
991 if (i == 35) printf("...");
995 for (i=0; i<AI->operand_count; i++)
996 { if ((i==0) && (opco.op_rules == VARIAB))
997 { if ((AI->operand[0]).type == VARIABLE_OT)
998 { printf("["); print_operand_z(AI->operand[i]); }
1000 printf("%s", variable_name((AI->operand[0]).value));
1003 if ((i==0) && (opco.op_rules == LABEL))
1004 { printf("L%d", AI->operand[0].value);
1006 else print_operand_z(AI->operand[i]);
1009 if (AI->store_variable_number != -1)
1010 { assembly_operand AO;
1012 AO.type = VARIABLE_OT; AO.value = AI->store_variable_number;
1013 print_operand_z(AO); printf(" ");
1016 switch(AI->branch_label_number)
1017 { case -4: printf("rtrue if %s", (AI->branch_flag)?"TRUE":"FALSE");
1019 case -3: printf("rfalse if %s", (AI->branch_flag)?"TRUE":"FALSE");
1021 case -2: printf("(no branch)"); break;
1024 printf("to L%d if %s", AI->branch_label_number,
1025 (AI->branch_flag)?"TRUE":"FALSE"); break;
1028 if (asm_trace_level>=2)
1029 { for (j=0;start_pc<zcode_holding_area + zcode_ha_size;
1031 { if (j%16==0) printf("\n ");
1032 printf("%02x ", *start_pc);
1038 if (module_switch) flush_link_data();
1044 make_opcode_syntax_z(opco);
1045 error_named("Assembly mistake: syntax is", opcode_syntax_string);
1048 static void assembleg_macro(assembly_instruction *AI)
1050 /* validate macro syntax first */
1051 int ix, no_operands_given;
1054 opco = internal_number_to_opmacro_g(AI->internal_number);
1055 no_operands_given = AI->operand_count;
1057 if (no_operands_given != opco.no)
1058 goto OpcodeSyntaxError;
1060 for (ix = 0; ix < no_operands_given; ix++) {
1061 int type = AI->operand[ix].type;
1062 if ((opco.flags & St)
1063 && ((!(opco.flags & Br) && (ix == no_operands_given-1))
1064 || ((opco.flags & Br) && (ix == no_operands_given-2)))) {
1065 if (is_constant_ot(type)) {
1066 error("*** assembly macro tried to store to a constant ***");
1067 goto OpcodeSyntaxError;
1070 if ((opco.flags & St2)
1071 && (ix == no_operands_given-2)) {
1072 if (is_constant_ot(type)) {
1073 error("*** assembly macro tried to store to a constant ***");
1074 goto OpcodeSyntaxError;
1079 /* expand the macro */
1080 switch (AI->internal_number) {
1082 assembleg_store(AI->operand[0], stack_pointer);
1086 assembleg_store(stack_pointer, AI->operand[0]);
1090 compiler_error("Invalid Glulx assembly macro");
1098 make_opcode_syntax_g(opco);
1099 error_named("Assembly mistake: syntax is", opcode_syntax_string);
1102 extern void assembleg_instruction(assembly_instruction *AI)
1104 uchar *start_pc, *opmodes_pc;
1106 int no_operands_given, at_seq_point = FALSE;
1112 offset = zmachine_pc;
1116 if (veneer_mode) sequence_point_follows = FALSE;
1117 if (sequence_point_follows)
1118 { sequence_point_follows = FALSE; at_seq_point = TRUE;
1119 if (debugfile_switch)
1120 { sequence_point_labels[next_sequence_point] = next_label;
1121 sequence_point_locations[next_sequence_point] =
1122 statement_debug_location;
1123 set_label_offset(next_label++, zmachine_pc);
1125 next_sequence_point++;
1128 opco = internal_number_to_opcode_g(AI->internal_number);
1130 if (execution_never_reaches_here)
1131 warning("This statement can never be reached");
1133 execution_never_reaches_here = ((opco.flags & Rf) != 0);
1135 if (opco.op_rules & GOP_Unicode) {
1136 uses_unicode_features = TRUE;
1138 if (opco.op_rules & GOP_MemHeap) {
1139 uses_memheap_features = TRUE;
1141 if (opco.op_rules & GOP_Acceleration) {
1142 uses_acceleration_features = TRUE;
1144 if (opco.op_rules & GOP_Float) {
1145 uses_float_features = TRUE;
1148 no_operands_given = AI->operand_count;
1150 /* 1. Write the opcode byte(s) */
1152 start_pc = zcode_holding_area + zcode_ha_size;
1154 if (opco.code < 0x80) {
1155 byteout(opco.code, 0);
1157 else if (opco.code < 0x4000) {
1158 byteout(((opco.code >> 8) & 0xFF) | 0x80, 0);
1159 byteout((opco.code & 0xFF), 0);
1162 byteout(((opco.code >> 24) & 0xFF) | 0xC0, 0);
1163 byteout(((opco.code >> 16) & 0xFF), 0);
1164 byteout(((opco.code >> 8) & 0xFF), 0);
1165 byteout(((opco.code) & 0xFF), 0);
1168 /* ... and the operand addressing modes. There's one byte for
1169 every two operands (rounded up). We write zeroes for now;
1170 when the operands are written, we'll go back and fix them. */
1172 opmodes_pc = zcode_holding_area + zcode_ha_size;
1174 for (ix=0; ix<opco.no; ix+=2) {
1178 /* 2. Dispose of the special rules */
1179 /* There aren't any in Glulx. */
1181 /* 3. Sort out the operands */
1183 if (no_operands_given != opco.no) {
1184 goto OpcodeSyntaxError;
1187 for (ix=0; ix<no_operands_given; ix++) {
1188 int marker = AI->operand[ix].marker;
1189 int type = AI->operand[ix].type;
1190 k = AI->operand[ix].value;
1192 if ((opco.flags & Br) && (ix == no_operands_given-1)) {
1193 if (!(marker >= BRANCH_MV && marker < BRANCHMAX_MV)) {
1194 compiler_error("Assembling branch without BRANCH_MV marker");
1195 goto OpcodeSyntaxError;
1198 k = 2; /* branch no-op */
1199 type = BYTECONSTANT_OT;
1203 k = 0; /* branch return 0 */
1204 type = ZEROCONSTANT_OT;
1208 k = 1; /* branch return 1 */
1209 type = BYTECONSTANT_OT;
1213 /* branch to label k */
1214 j = subtract_pointers((zcode_holding_area + zcode_ha_size),
1217 marker = BRANCH_MV + j;
1218 if (!(marker >= BRANCH_MV && marker < BRANCHMAX_MV)) {
1219 error("*** branch marker too far from opmode byte ***");
1220 goto OpcodeSyntaxError;
1224 if ((opco.flags & St)
1225 && ((!(opco.flags & Br) && (ix == no_operands_given-1))
1226 || ((opco.flags & Br) && (ix == no_operands_given-2)))) {
1227 if (type == BYTECONSTANT_OT || type == HALFCONSTANT_OT
1228 || type == CONSTANT_OT) {
1229 error("*** instruction tried to store to a constant ***");
1230 goto OpcodeSyntaxError;
1233 if ((opco.flags & St2)
1234 && (ix == no_operands_given-2)) {
1235 if (type == BYTECONSTANT_OT || type == HALFCONSTANT_OT
1236 || type == CONSTANT_OT) {
1237 error("*** instruction tried to store to a constant ***");
1238 goto OpcodeSyntaxError;
1242 if (marker && (type == HALFCONSTANT_OT
1243 || type == BYTECONSTANT_OT
1244 || type == ZEROCONSTANT_OT)) {
1245 compiler_error("Assembling marker in less than 32-bit constant.");
1246 /* Actually we should store marker|0x80 for a byte constant,
1247 but let's hold off on that. */
1251 case LONG_CONSTANT_OT:
1252 case SHORT_CONSTANT_OT:
1255 compiler_error("Z-code OT in Glulx assembly operand.");
1259 byteout((k >> 24) & 0xFF, marker);
1260 byteout((k >> 16) & 0xFF, 0);
1261 byteout((k >> 8) & 0xFF, 0);
1262 byteout((k & 0xFF), 0);
1264 case HALFCONSTANT_OT:
1266 byteout((k >> 8) & 0xFF, marker);
1267 byteout((k & 0xFF), 0);
1269 case BYTECONSTANT_OT:
1271 byteout((k & 0xFF), marker);
1273 case ZEROCONSTANT_OT:
1276 case DEREFERENCE_OT:
1278 byteout((k >> 24) & 0xFF, marker);
1279 byteout((k >> 16) & 0xFF, 0);
1280 byteout((k >> 8) & 0xFF, 0);
1281 byteout((k & 0xFF), 0);
1284 /* Global variable -- a constant address. */
1285 k -= MAX_LOCAL_VARIABLES;
1286 if (/* DISABLES CODE */ (0)) {
1287 /* We could write the value as a marker and patch it later... */
1289 byteout(((k) >> 24) & 0xFF, VARIABLE_MV);
1290 byteout(((k) >> 16) & 0xFF, 0);
1291 byteout(((k) >> 8) & 0xFF, 0);
1292 byteout(((k) & 0xFF), 0);
1295 /* ...but it's more efficient to write it as a RAM operand,
1296 which can be 1, 2, or 4 bytes. Remember that global variables
1297 are the very first thing in RAM. */
1298 k = k * 4; /* each variable is four bytes */
1301 byteout(((k) & 0xFF), 0);
1303 else if (k <= 65535) {
1305 byteout(((k) >> 8) & 0xFF, 0);
1306 byteout(((k) & 0xFF), 0);
1310 byteout(((k) >> 24) & 0xFF, 0);
1311 byteout(((k) >> 16) & 0xFF, 0);
1312 byteout(((k) >> 8) & 0xFF, 0);
1313 byteout(((k) & 0xFF), 0);
1319 /* Stack-pointer magic variable */
1323 /* Local variable -- a byte or short offset from the
1324 frame pointer. It's an unsigned offset, so we can
1325 fit up to long 63 (offset 4*63) in a byte. */
1328 byteout((k-1)*4, 0);
1332 byteout((((k-1)*4) >> 8) & 0xFF, 0);
1333 byteout(((k-1)*4) & 0xFF, 0);
1344 opmodes_pc[ix/2] |= j;
1347 /* Print assembly trace. */
1348 if (asm_trace_level > 0) {
1350 printf("%5d +%05lx %3s %-12s ", ErrorReport.line_number,
1351 ((long int) offset),
1352 (at_seq_point)?"<*>":" ", opco.name);
1353 for (i=0; i<AI->operand_count; i++) {
1354 if ((opco.flags & Br) && (i == opco.no-1)) {
1355 if (AI->operand[i].value == -4)
1357 else if (AI->operand[i].value == -3)
1358 printf("to rfalse");
1360 printf("to L%d", AI->operand[i].value);
1363 print_operand_g(AI->operand[i]);
1368 if (asm_trace_level>=2) {
1370 start_pc<zcode_holding_area + zcode_ha_size;
1372 if (j%16==0) printf("\n ");
1373 if (/* DISABLES CODE */ (0)) {
1374 printf("%02x ", *start_pc);
1377 printf("%02x", *start_pc);
1378 if (zcode_markers[start_pc-zcode_holding_area])
1379 printf("{%02x}", zcode_markers[start_pc-zcode_holding_area]);
1387 if (module_switch) flush_link_data();
1393 make_opcode_syntax_g(opco);
1394 error_named("Assembly mistake: syntax is", opcode_syntax_string);
1397 extern void assemble_label_no(int n)
1399 if (asm_trace_level > 0)
1400 printf("%5d +%05lx .L%d\n", ErrorReport.line_number,
1401 ((long int) zmachine_pc), n);
1402 set_label_offset(n, zmachine_pc);
1403 execution_never_reaches_here = FALSE;
1406 extern void define_symbol_label(int symbol)
1407 { label_symbols[svals[symbol]] = symbol;
1410 extern int32 assemble_routine_header(int no_locals,
1411 int routine_asterisked, char *name, int embedded_flag, int the_symbol)
1413 int stackargs = FALSE;
1416 execution_never_reaches_here = FALSE;
1418 routine_locals = no_locals;
1419 for (i=0; i<MAX_LOCAL_VARIABLES; i++) variable_usage[i] = FALSE;
1422 && !strcmp(local_variables.keywords[0], "_vararg_count")) {
1426 if (veneer_mode) routine_starts_line = -1;
1427 else routine_starts_line = ErrorReport.line_number
1428 + FILE_LINE_SCALE_FACTOR*ErrorReport.file_number;
1430 if (asm_trace_level > 0)
1431 { printf("\n%5d +%05lx [ %s ", ErrorReport.line_number,
1432 ((long int) zmachine_pc), name);
1433 for (i=1; i<=no_locals; i++) printf("%s ", variable_name(i));
1437 routine_start_pc = zmachine_pc;
1439 if (track_unused_routines) {
1440 /* The name of an embedded function is in a temporary buffer,
1441 so we shouldn't keep a reference to it. (It is sad that we
1442 have to know this here.) */
1443 char *funcname = name;
1445 funcname = "<embedded>";
1447 df_note_function_start(funcname, zmachine_pc, embedded_flag,
1448 routine_starts_line);
1451 routine_symbol = the_symbol;
1452 name_length = strlen(name) + 1;
1454 my_malloc(name_length * sizeof(char), "temporary copy of routine name");
1455 strncpy(routine_name, name, name_length);
1457 /* Update the routine counter */
1461 /* Actually assemble the routine header into the code area; note */
1462 /* Inform doesn't support the setting of local variables to default */
1463 /* values other than 0 in V3 and V4. (In V5+ the Z-Machine doesn't */
1464 /* provide the possibility in any case.) */
1469 warning("Z-code does not support stack-argument function definitions.");
1471 byteout(no_locals, 0);
1473 /* Not the packed address, but the scaled offset from code area start: */
1475 rv = zmachine_pc/scale_factor;
1477 if (instruction_set_number<5)
1478 for (i=0; i<no_locals; i++) { byteout(0,0); byteout(0,0); }
1480 next_label = 0; next_sequence_point = 0; last_label = -1;
1482 /* Compile code to print out text like "a=3, b=4, c=5" when the */
1483 /* function is called, if it's required. */
1485 if ((routine_asterisked) || (define_INFIX_switch))
1486 { char fnt[256]; assembly_operand PV, RFA, CON, STP, SLF; int ln, ln2;
1491 if (define_INFIX_switch)
1494 { SLF.value = 251; SLF.type = VARIABLE_OT; SLF.marker = 0;
1495 CON.value = 0; CON.type = SHORT_CONSTANT_OT; CON.marker = 0;
1496 assemblez_2_branch(test_attr_zc, SLF, CON, ln2, FALSE);
1499 { i = no_named_routines++;
1500 named_routine_symbols[i] = the_symbol;
1501 CON.value = i/8; CON.type = LONG_CONSTANT_OT; CON.marker = 0;
1502 RFA.value = routine_flags_array_SC;
1503 RFA.type = LONG_CONSTANT_OT; RFA.marker = INCON_MV;
1504 STP.value = 0; STP.type = VARIABLE_OT; STP.marker = 0;
1505 assemblez_2_to(loadb_zc, RFA, CON, STP);
1506 CON.value = (1 << (i%8)); CON.type = SHORT_CONSTANT_OT;
1507 assemblez_2_to(and_zc, STP, CON, STP);
1508 assemblez_1_branch(jz_zc, STP, ln2, TRUE);
1511 sprintf(fnt, "[ %s(", name);
1512 AI.text = fnt; assemblez_0(print_zc);
1513 for (i=1; (i<=7)&&(i<=no_locals); i++)
1514 { if (version_number >= 5)
1515 { PV.type = SHORT_CONSTANT_OT;
1516 PV.value = i; PV.marker = 0;
1517 assemblez_1_branch(check_arg_count_zc, PV, ln, FALSE);
1519 sprintf(fnt, "%s%s = ", (i==1)?"":", ", variable_name(i));
1520 AI.text = fnt; assemblez_0(print_zc);
1521 PV.type = VARIABLE_OT; PV.value = i; PV.marker = 0;
1522 assemblez_1(print_num_zc, PV);
1524 assemble_label_no(ln);
1525 sprintf(fnt, ") ]^"); AI.text = fnt;
1526 assemblez_0(print_zc);
1527 assemble_label_no(ln2);
1535 byteout(0xC0, 0); /* Glulx type byte for function */
1537 byteout(0xC1, 0); /* Glulx type byte for function */
1539 /* Now the locals format list. This is simple; we only use
1540 four-byte locals. That's a single pair, unless we have more
1541 than 255 locals, or none at all. */
1551 /* Terminate the list with a (0, 0) pair. */
1556 /* The top stack value is the number of function arguments. Let's
1557 move that into the first local, which is _vararg_count. */
1558 /* @copy sp _vararg_count; */
1559 byteout(0x40, 0); byteout(0x98, 0); byteout(0x00, 0);
1562 next_label = 0; next_sequence_point = 0; last_label = -1;
1564 if ((routine_asterisked) || (define_INFIX_switch)) {
1567 assembly_operand AO, AO2;
1568 if (define_INFIX_switch) {
1569 /* This isn't supported */
1570 if (embedded_flag) {
1573 i = no_named_routines++;
1574 named_routine_symbols[i] = the_symbol;
1577 sprintf(fnt, "[ %s(", name);
1578 AO.marker = STRING_MV;
1579 AO.type = CONSTANT_OT;
1580 AO.value = compile_string(fnt, FALSE, FALSE);
1581 assembleg_1(streamstr_gc, AO);
1584 for (ix=1; ix<=no_locals; ix++) {
1585 sprintf(fnt, "%s%s = ", (ix==1)?"":", ", variable_name(ix));
1586 AO.marker = STRING_MV;
1587 AO.type = CONSTANT_OT;
1588 AO.value = compile_string(fnt, FALSE, FALSE);
1589 assembleg_1(streamstr_gc, AO);
1591 AO.type = LOCALVAR_OT;
1593 assembleg_1(streamnum_gc, AO);
1597 int lntop, lnbottom;
1598 sprintf(fnt, "%s = ", variable_name(1));
1599 AO.marker = STRING_MV;
1600 AO.type = CONSTANT_OT;
1601 AO.value = compile_string(fnt, FALSE, FALSE);
1602 assembleg_1(streamstr_gc, AO);
1604 AO.type = LOCALVAR_OT;
1606 assembleg_1(streamnum_gc, AO);
1607 AO2.type = BYTECONSTANT_OT;
1610 assembleg_1(streamchar_gc, AO2);
1611 AO2.type = BYTECONSTANT_OT;
1614 /* for (temp_var4=0 : temp_var4<_vararg_count : temp_var4++) {
1616 @stkpeek temp_var4 sp;
1620 assembleg_store(temp_var4, zero_operand);
1621 lntop = next_label++;
1622 lnbottom = next_label++;
1623 assemble_label_no(lntop);
1624 assembleg_2_branch(jge_gc, temp_var4, AO, lnbottom); /* AO is _vararg_count */
1625 assembleg_1(streamchar_gc, AO2); /* AO2 is space */
1626 assembleg_2(stkpeek_gc, temp_var4, stack_pointer);
1627 assembleg_1(streamnum_gc, stack_pointer);
1628 assembleg_3(add_gc, temp_var4, one_operand, temp_var4);
1629 assembleg_0_branch(jump_gc, lntop);
1630 assemble_label_no(lnbottom);
1633 AO.marker = STRING_MV;
1634 AO.type = CONSTANT_OT;
1635 AO.value = compile_string(") ]^", FALSE, FALSE);
1636 assembleg_1(streamstr_gc, AO);
1643 void assemble_routine_end(int embedded_flag, debug_locations locations)
1646 /* No marker is made in the Z-machine's code area to indicate the */
1647 /* end of a routine. Instead, we simply assemble a return opcode if */
1648 /* need be (it won't be if the last instruction was, say, a "quit"). */
1649 /* The return value is true (1) for normal routines, false (0) for */
1650 /* embedded routines (e.g. the library uses this for "before" */
1653 if (!execution_never_reaches_here)
1656 if (embedded_flag) assemblez_0(rfalse_zc);
1657 else assemblez_0(rtrue_zc);
1660 assembly_operand AO;
1665 assembleg_1(return_gc, AO);
1669 /* Dump the contents of the current routine into longer-term Z-code
1673 transfer_routine_z();
1675 transfer_routine_g();
1677 if (track_unused_routines)
1678 df_note_function_end(zmachine_pc);
1680 /* Tell the debugging file about the routine just ended. */
1682 if (debugfile_switch)
1684 debug_file_printf("<routine>");
1687 ("<identifier artificial=\"true\">%s</identifier>",
1690 else if (sflags[routine_symbol] & REPLACE_SFLAG)
1691 { /* The symbol type will be set to ROUTINE_T once the replaced
1692 version has been given; if it is already set, we must be dealing
1693 with a replacement, and we can use the routine name as-is.
1694 Otherwise we look for a rename. And if that doesn't work, we
1695 fall back to an artificial identifier. */
1696 if (stypes[routine_symbol] == ROUTINE_T)
1697 { /* Optional because there may be further replacements. */
1698 write_debug_optional_identifier(routine_symbol);
1700 else if (find_symbol_replacement(&routine_symbol))
1702 ("<identifier>%s</identifier>", symbs[routine_symbol]);
1706 ("<identifier artificial=\"true\">%s (replaced)"
1711 { debug_file_printf("<identifier>%s</identifier>", routine_name);
1713 debug_file_printf("<value>");
1715 { write_debug_code_backpatch(routine_start_pc);
1717 { write_debug_packed_code_backpatch(routine_start_pc);
1719 debug_file_printf("</value>");
1720 debug_file_printf("<address>");
1721 write_debug_code_backpatch(routine_start_pc);
1722 debug_file_printf("</address>");
1724 ("<byte-count>%d</byte-count>", zmachine_pc - routine_start_pc);
1725 write_debug_locations(locations);
1726 for (i = 1; i <= routine_locals; ++i)
1727 { debug_file_printf("<local-variable>");
1728 debug_file_printf("<identifier>%s</identifier>", variable_name(i));
1731 ("<frame-offset>%d</frame-offset>", 4 * (i - 1));
1734 { debug_file_printf("<index>%d</index>", i);
1736 debug_file_printf("</local-variable>");
1738 for (i = 0; i < next_sequence_point; ++i)
1739 { debug_file_printf("<sequence-point>");
1740 debug_file_printf("<address>");
1741 write_debug_code_backpatch
1742 (label_offsets[sequence_point_labels[i]]);
1743 debug_file_printf("</address>");
1744 write_debug_location(sequence_point_locations[i]);
1745 debug_file_printf("</sequence-point>");
1747 debug_file_printf("</routine>");
1750 my_free(&routine_name, "temporary copy of routine name");
1752 /* Issue warnings about any local variables not used in the routine. */
1754 for (i=1; i<=routine_locals; i++)
1755 if (!(variable_usage[i]))
1756 dbnu_warning("Local variable", variable_name(i),
1757 routine_starts_line);
1759 for (i=0; i<next_label; i++)
1760 { int j = label_symbols[i];
1762 { if (sflags[j] & CHANGE_SFLAG)
1763 error_named_at("Routine contains no such label as",
1764 (char *) symbs[j], slines[j]);
1766 if ((sflags[j] & USED_SFLAG) == 0)
1767 dbnu_warning("Label", (char *) symbs[j], slines[j]);
1768 stypes[j] = CONSTANT_T;
1769 sflags[j] = UNKNOWN_SFLAG;
1772 no_sequence_points += next_sequence_point;
1773 next_label = 0; next_sequence_point = 0;
1776 /* ------------------------------------------------------------------------- */
1777 /* Called when the holding area contains an entire routine of code: */
1778 /* backpatches the labels, issues module markers, then dumps the routine */
1779 /* into longer-term storage. */
1780 /* Note that in the code received, all branches have long form, and their */
1781 /* contents are not an offset but the label numbers they branch to. */
1782 /* Similarly, LABEL operands (those of "jump" instructions) are label */
1783 /* numbers. So this routine must change the label numbers to offsets, */
1784 /* slimming the code down as it does so to take advantage of short-form */
1785 /* branch operands where possible. */
1786 /* ------------------------------------------------------------------------- */
1788 static int32 adjusted_pc;
1790 static void transfer_to_temp_file(uchar *c)
1791 { fputc(*c,Temp2_fp);
1795 static void transfer_to_zcode_area(uchar *c)
1796 { write_byte_to_memory_block(&zcode_area, adjusted_pc++, *c);
1799 static void transfer_routine_z(void)
1800 { int32 i, j, pc, new_pc, label, long_form, offset_of_next, addr,
1801 branch_on_true, rstart_pc;
1802 void (* transfer_byte)(uchar *);
1804 adjusted_pc = zmachine_pc - zcode_ha_size; rstart_pc = adjusted_pc;
1806 if (asm_trace_level >= 3)
1807 { printf("Backpatching routine at %05lx: initial size %d, %d labels\n",
1808 (long int) adjusted_pc, zcode_ha_size, next_label);
1812 (temporary_files_switch)?transfer_to_temp_file:transfer_to_zcode_area;
1814 /* (1) Scan through for branches and make short/long decisions in each
1815 case. Mark omitted bytes (2nd bytes in branches converted to
1816 short form) with DELETED_MV. */
1818 for (i=0, pc=adjusted_pc; i<zcode_ha_size; i++, pc++)
1819 { if (zcode_markers[i] == BRANCH_MV)
1820 { if (asm_trace_level >= 4)
1821 printf("Branch detected at offset %04x\n", pc);
1822 j = (256*zcode_holding_area[i] + zcode_holding_area[i+1]) & 0x7fff;
1823 if (asm_trace_level >= 4)
1824 printf("To label %d, which is %d from here\n",
1825 j, label_offsets[j]-pc);
1826 if ((label_offsets[j] >= pc+2) && (label_offsets[j] < pc+64))
1827 { if (asm_trace_level >= 4) printf("Short form\n");
1828 zcode_markers[i+1] = DELETED_MV;
1833 /* (2) Calculate the new positions of the labels. Note that since the
1834 long/short decision was taken on the basis of the old labels,
1835 and since the new labels are slightly closer together because
1836 of branch bytes deleted, there may be a few further branch
1837 optimisations which are possible but which have been missed
1838 (if two labels move inside the "short" range as a result of
1839 a previous optimisation). However, this is acceptably uncommon. */
1842 { if (asm_trace_level >= 4)
1843 { printf("Opening label: %d\n", first_label);
1844 for (i=0;i<next_label;i++)
1845 printf("Label %d offset %04x next -> %d previous -> %d\n",
1846 i, label_offsets[i], label_next[i], label_prev[i]);
1849 for (i=0, pc=adjusted_pc, new_pc=adjusted_pc, label = first_label;
1850 i<zcode_ha_size; i++, pc++)
1851 { while ((label != -1) && (label_offsets[label] == pc))
1852 { if (asm_trace_level >= 4)
1853 printf("Position of L%d corrected from %04x to %04x\n",
1854 label, label_offsets[label], new_pc);
1855 label_offsets[label] = new_pc;
1856 label = label_next[label];
1858 if (zcode_markers[i] != DELETED_MV) new_pc++;
1862 /* (3) As we are transferring, replace the label numbers in branch
1863 operands with offsets to those labels. Also issue markers, now
1864 that we know where they occur in the final Z-code area. */
1866 for (i=0, new_pc=adjusted_pc; i<zcode_ha_size; i++)
1867 { switch(zcode_markers[i])
1869 long_form = 1; if (zcode_markers[i+1] == DELETED_MV) long_form = 0;
1871 j = (256*zcode_holding_area[i] + zcode_holding_area[i+1]) & 0x7fff;
1872 branch_on_true = ((zcode_holding_area[i]) & 0x80);
1873 offset_of_next = new_pc + long_form + 1;
1875 addr = label_offsets[j] - offset_of_next + 2;
1876 if (addr<-0x2000 || addr>0x1fff)
1877 fatalerror("Branch out of range: divide the routine up?");
1878 if (addr<0) addr+=(int32) 0x10000L;
1882 { zcode_holding_area[i] = branch_on_true + addr/256;
1883 zcode_holding_area[i+1] = addr%256;
1887 { compiler_error("Label out of range for branch");
1888 printf("Addr is %04x\n", addr);
1890 zcode_holding_area[i] = branch_on_true + 0x40 + (addr&0x3f);
1892 transfer_byte(zcode_holding_area + i); new_pc++;
1896 j = 256*zcode_holding_area[i] + zcode_holding_area[i+1];
1897 addr = label_offsets[j] - new_pc;
1898 if (addr<-0x8000 || addr>0x7fff)
1899 fatalerror("Jump out of range: divide the routine up?");
1900 if (addr<0) addr += (int32) 0x10000L;
1901 zcode_holding_area[i] = addr/256;
1902 zcode_holding_area[i+1] = addr%256;
1903 transfer_byte(zcode_holding_area + i); new_pc++;
1910 switch(zcode_markers[i] & 0x7f)
1911 { case NULL_MV: break;
1916 if (!module_switch) break;
1918 if ((zcode_markers[i] & 0x7f) > LARGEST_BPATCH_MV)
1919 { compiler_error("Illegal code backpatch value");
1920 printf("Illegal value of %02x at PC = %04x\n",
1921 zcode_markers[i] & 0x7f, new_pc);
1925 write_byte_to_memory_block(&zcode_backpatch_table,
1926 zcode_backpatch_size++,
1927 zcode_markers[i] + 32*(new_pc/65536));
1928 write_byte_to_memory_block(&zcode_backpatch_table,
1929 zcode_backpatch_size++, (new_pc/256)%256);
1930 write_byte_to_memory_block(&zcode_backpatch_table,
1931 zcode_backpatch_size++, new_pc%256);
1934 transfer_byte(zcode_holding_area + i); new_pc++;
1939 if (asm_trace_level >= 3)
1940 { printf("After branch optimisation, routine length is %d bytes\n",
1941 new_pc - rstart_pc);
1944 /* Insert null bytes if necessary to ensure the next routine address is */
1945 /* expressible as a packed address */
1949 if (oddeven_packing_switch)
1950 while ((adjusted_pc%(scale_factor*2))!=0) transfer_byte(zero);
1952 while ((adjusted_pc%scale_factor)!=0) transfer_byte(zero);
1955 zmachine_pc = adjusted_pc;
1959 static void transfer_routine_g(void)
1960 { int32 i, j, pc, new_pc, label, form_len, offset_of_next, addr,
1962 void (* transfer_byte)(uchar *);
1964 adjusted_pc = zmachine_pc - zcode_ha_size; rstart_pc = adjusted_pc;
1966 if (asm_trace_level >= 3)
1967 { printf("Backpatching routine at %05lx: initial size %d, %d labels\n",
1968 (long int) adjusted_pc, zcode_ha_size, next_label);
1972 (temporary_files_switch)?transfer_to_temp_file:transfer_to_zcode_area;
1974 /* (1) Scan through for branches and make short/long decisions in each
1975 case. Mark omitted bytes (bytes 2-4 in branches converted to
1976 short form) with DELETED_MV. */
1978 for (i=0, pc=adjusted_pc; i<zcode_ha_size; i++, pc++) {
1979 if (zcode_markers[i] >= BRANCH_MV && zcode_markers[i] < BRANCHMAX_MV) {
1980 int opmodeoffset = (zcode_markers[i] - BRANCH_MV);
1982 if (asm_trace_level >= 4)
1983 printf("Branch detected at offset %04x\n", pc);
1984 j = ((zcode_holding_area[i] << 24)
1985 | (zcode_holding_area[i+1] << 16)
1986 | (zcode_holding_area[i+2] << 8)
1987 | (zcode_holding_area[i+3]));
1988 offset_of_next = pc + 4;
1989 addr = (label_offsets[j] - offset_of_next) + 2;
1990 if (asm_trace_level >= 4)
1991 printf("To label %d, which is (%d-2) = %d from here\n",
1992 j, addr, label_offsets[j] - offset_of_next);
1993 if (addr >= -0x80 && addr < 0x80) {
1994 if (asm_trace_level >= 4) printf("...Byte form\n");
1995 zcode_markers[i+1] = DELETED_MV;
1996 zcode_markers[i+2] = DELETED_MV;
1997 zcode_markers[i+3] = DELETED_MV;
1998 opmodebyte = i - ((opmodeoffset+1)/2);
1999 if ((opmodeoffset & 1) == 0)
2000 zcode_holding_area[opmodebyte] =
2001 (zcode_holding_area[opmodebyte] & 0xF0) | 0x01;
2003 zcode_holding_area[opmodebyte] =
2004 (zcode_holding_area[opmodebyte] & 0x0F) | 0x10;
2006 else if (addr >= -0x8000 && addr < 0x8000) {
2007 if (asm_trace_level >= 4) printf("...Short form\n");
2008 zcode_markers[i+2] = DELETED_MV;
2009 zcode_markers[i+3] = DELETED_MV;
2010 opmodebyte = i - ((opmodeoffset+1)/2);
2011 if ((opmodeoffset & 1) == 0)
2012 zcode_holding_area[opmodebyte] =
2013 (zcode_holding_area[opmodebyte] & 0xF0) | 0x02;
2015 zcode_holding_area[opmodebyte] =
2016 (zcode_holding_area[opmodebyte] & 0x0F) | 0x20;
2021 /* (2) Calculate the new positions of the labels. Note that since the
2022 long/short decision was taken on the basis of the old labels,
2023 and since the new labels are slightly closer together because
2024 of branch bytes deleted, there may be a few further branch
2025 optimisations which are possible but which have been missed
2026 (if two labels move inside the "short" range as a result of
2027 a previous optimisation). However, this is acceptably uncommon. */
2028 if (next_label > 0) {
2029 if (asm_trace_level >= 4) {
2030 printf("Opening label: %d\n", first_label);
2031 for (i=0;i<next_label;i++)
2032 printf("Label %d offset %04x next -> %d previous -> %d\n",
2033 i, label_offsets[i], label_next[i], label_prev[i]);
2036 for (i=0, pc=adjusted_pc, new_pc=adjusted_pc, label = first_label;
2039 while ((label != -1) && (label_offsets[label] == pc)) {
2040 if (asm_trace_level >= 4)
2041 printf("Position of L%d corrected from %04x to %04x\n",
2042 label, label_offsets[label], new_pc);
2043 label_offsets[label] = new_pc;
2044 label = label_next[label];
2046 if (zcode_markers[i] != DELETED_MV) new_pc++;
2050 /* (3) As we are transferring, replace the label numbers in branch
2051 operands with offsets to those labels. Also issue markers, now
2052 that we know where they occur in the final Z-code area. */
2054 for (i=0, new_pc=adjusted_pc; i<zcode_ha_size; i++) {
2056 if (zcode_markers[i] >= BRANCH_MV && zcode_markers[i] < BRANCHMAX_MV) {
2058 if (zcode_markers[i+1] == DELETED_MV) {
2062 if (zcode_markers[i+2] == DELETED_MV)
2065 j = ((zcode_holding_area[i] << 24)
2066 | (zcode_holding_area[i+1] << 16)
2067 | (zcode_holding_area[i+2] << 8)
2068 | (zcode_holding_area[i+3]));
2070 /* At the moment, we can safely assume that the branch operand
2071 is the end of the opcode, so the next opcode starts right
2073 offset_of_next = new_pc + form_len;
2075 addr = (label_offsets[j] - offset_of_next) + 2;
2076 if (asm_trace_level >= 4) {
2077 printf("Branch at offset %04x: %04x (%s)\n",
2078 new_pc, addr, ((form_len == 1) ? "byte" :
2079 ((form_len == 2) ? "short" : "long")));
2081 if (form_len == 1) {
2082 if (addr < -0x80 && addr >= 0x80) {
2083 error("*** Label out of range for byte branch ***");
2085 zcode_holding_area[i] = (addr) & 0xFF;
2087 else if (form_len == 2) {
2088 if (addr < -0x8000 && addr >= 0x8000) {
2089 error("*** Label out of range for short branch ***");
2091 zcode_holding_area[i] = (addr >> 8) & 0xFF;
2092 zcode_holding_area[i+1] = (addr) & 0xFF;
2095 zcode_holding_area[i] = (addr >> 24) & 0xFF;
2096 zcode_holding_area[i+1] = (addr >> 16) & 0xFF;
2097 zcode_holding_area[i+2] = (addr >> 8) & 0xFF;
2098 zcode_holding_area[i+3] = (addr) & 0xFF;
2100 transfer_byte(zcode_holding_area + i); new_pc++;
2102 else if (zcode_markers[i] == LABEL_MV) {
2103 error("*** No LABEL opcodes in Glulx ***");
2105 else if (zcode_markers[i] == DELETED_MV) {
2109 switch(zcode_markers[i] & 0x7f) {
2114 if (!module_switch) break;
2118 if ((zcode_markers[i] & 0x7f) > LARGEST_BPATCH_MV) {
2119 error("*** Illegal code backpatch value ***");
2120 printf("Illegal value of %02x at PC = %04x\n",
2121 zcode_markers[i] & 0x7f, new_pc);
2124 /* The backpatch table format for Glulx:
2125 First, the marker byte (0..LARGEST_BPATCH_MV).
2126 Then a byte indicating the data size to be patched (1, 2, 4).
2127 Then the four-byte address (new_pc).
2129 write_byte_to_memory_block(&zcode_backpatch_table,
2130 zcode_backpatch_size++,
2132 write_byte_to_memory_block(&zcode_backpatch_table,
2133 zcode_backpatch_size++,
2135 write_byte_to_memory_block(&zcode_backpatch_table,
2136 zcode_backpatch_size++, ((new_pc >> 24) & 0xFF));
2137 write_byte_to_memory_block(&zcode_backpatch_table,
2138 zcode_backpatch_size++, ((new_pc >> 16) & 0xFF));
2139 write_byte_to_memory_block(&zcode_backpatch_table,
2140 zcode_backpatch_size++, ((new_pc >> 8) & 0xFF));
2141 write_byte_to_memory_block(&zcode_backpatch_table,
2142 zcode_backpatch_size++, (new_pc & 0xFF));
2145 transfer_byte(zcode_holding_area + i); new_pc++;
2149 if (asm_trace_level >= 3)
2150 { printf("After branch optimisation, routine length is %d bytes\n",
2151 new_pc - rstart_pc);
2154 zmachine_pc = adjusted_pc;
2159 /* ========================================================================= */
2160 /* Front ends for the instruction assembler: convenient shorthand forms */
2161 /* used in various code generation routines all over Inform. */
2162 /* ------------------------------------------------------------------------- */
2164 void assemble_jump(int n)
2172 void assemblez_0(int internal_number)
2173 { AI.internal_number = internal_number;
2174 AI.operand_count = 0;
2175 AI.store_variable_number = -1;
2176 AI.branch_label_number = -1;
2177 assemblez_instruction(&AI);
2180 void assemblez_0_to(int internal_number, assembly_operand o)
2181 { AI.internal_number = internal_number;
2182 AI.operand_count = 0;
2183 AI.store_variable_number = o.value;
2184 AI.branch_label_number = -1;
2185 assemblez_instruction(&AI);
2188 void assemblez_0_branch(int internal_number, int label, int flag)
2189 { AI.internal_number = internal_number;
2190 AI.operand_count = 0;
2191 AI.store_variable_number = -1;
2192 AI.branch_label_number = label;
2193 AI.branch_flag = flag;
2194 assemblez_instruction(&AI);
2197 void assemblez_1(int internal_number, assembly_operand o1)
2198 { AI.internal_number = internal_number;
2199 AI.operand_count = 1;
2201 AI.store_variable_number = -1;
2202 AI.branch_label_number = -1;
2203 assemblez_instruction(&AI);
2206 void assemblez_1_to(int internal_number,
2207 assembly_operand o1, assembly_operand st)
2208 { AI.internal_number = internal_number;
2209 AI.operand_count = 1;
2211 AI.store_variable_number = st.value;
2212 AI.branch_label_number = -1;
2213 assemblez_instruction(&AI);
2216 void assemblez_1_branch(int internal_number,
2217 assembly_operand o1, int label, int flag)
2218 { AI.internal_number = internal_number;
2219 AI.operand_count = 1;
2221 AI.branch_label_number = label;
2222 AI.store_variable_number = -1;
2223 AI.branch_flag = flag;
2224 assemblez_instruction(&AI);
2227 void assemblez_2(int internal_number,
2228 assembly_operand o1, assembly_operand o2)
2229 { AI.internal_number = internal_number;
2230 AI.operand_count = 2;
2233 AI.store_variable_number = -1;
2234 AI.branch_label_number = -1;
2235 assemblez_instruction(&AI);
2238 void assemblez_3(int internal_number,
2239 assembly_operand o1, assembly_operand o2, assembly_operand o3)
2240 { AI.internal_number = internal_number;
2241 AI.operand_count = 3;
2245 AI.store_variable_number = -1;
2246 AI.branch_label_number = -1;
2247 assemblez_instruction(&AI);
2250 void assemblez_3_to(int internal_number,
2251 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2252 assembly_operand st)
2253 { AI.internal_number = internal_number;
2254 AI.operand_count = 3;
2258 AI.store_variable_number = st.value;
2259 AI.branch_label_number = -1;
2260 assemblez_instruction(&AI);
2263 void assemblez_3_branch(int internal_number,
2264 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2265 int label, int flag)
2266 { AI.internal_number = internal_number;
2267 AI.operand_count = 3;
2271 AI.store_variable_number = -1;
2272 AI.branch_label_number = label;
2273 AI.branch_flag = flag;
2274 assemblez_instruction(&AI);
2277 void assemblez_4(int internal_number,
2278 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2279 assembly_operand o4)
2280 { AI.internal_number = internal_number;
2281 AI.operand_count = 4;
2286 AI.store_variable_number = -1;
2287 AI.branch_label_number = -1;
2288 assemblez_instruction(&AI);
2291 void assemblez_5(int internal_number,
2292 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2293 assembly_operand o4, assembly_operand o5)
2294 { AI.internal_number = internal_number;
2295 AI.operand_count = 5;
2301 AI.store_variable_number = -1;
2302 AI.branch_label_number = -1;
2303 assemblez_instruction(&AI);
2306 void assemblez_6(int internal_number,
2307 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2308 assembly_operand o4, assembly_operand o5, assembly_operand o6)
2309 { AI.internal_number = internal_number;
2310 AI.operand_count = 6;
2317 AI.store_variable_number = -1;
2318 AI.branch_label_number = -1;
2319 assemblez_instruction(&AI);
2322 void assemblez_4_branch(int internal_number,
2323 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2324 assembly_operand o4, int label, int flag)
2325 { AI.internal_number = internal_number;
2326 AI.operand_count = 4;
2331 AI.store_variable_number = -1;
2332 AI.branch_label_number = label;
2333 AI.branch_flag = flag;
2334 assemblez_instruction(&AI);
2337 void assemblez_4_to(int internal_number,
2338 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2339 assembly_operand o4, assembly_operand st)
2340 { AI.internal_number = internal_number;
2341 AI.operand_count = 4;
2346 AI.store_variable_number = st.value;
2347 AI.branch_label_number = -1;
2348 assemblez_instruction(&AI);
2351 void assemblez_5_to(int internal_number,
2352 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2353 assembly_operand o4, assembly_operand o5, assembly_operand st)
2354 { AI.internal_number = internal_number;
2355 AI.operand_count = 5;
2361 AI.store_variable_number = st.value;
2362 AI.branch_label_number = -1;
2363 assemblez_instruction(&AI);
2366 void assemblez_2_to(int internal_number,
2367 assembly_operand o1, assembly_operand o2, assembly_operand st)
2368 { AI.internal_number = internal_number;
2369 AI.operand_count = 2;
2372 AI.store_variable_number = st.value;
2373 AI.branch_label_number = -1;
2374 assemblez_instruction(&AI);
2377 void assemblez_2_branch(int internal_number,
2378 assembly_operand o1, assembly_operand o2, int label, int flag)
2379 { AI.internal_number = internal_number;
2380 AI.operand_count = 2;
2383 AI.branch_label_number = label;
2384 AI.store_variable_number = -1;
2385 AI.branch_flag = flag;
2386 assemblez_instruction(&AI);
2389 void assemblez_objcode(int internal_number,
2390 assembly_operand o1, assembly_operand st, int label, int flag)
2391 { AI.internal_number = internal_number;
2392 AI.operand_count = 1;
2394 AI.branch_label_number = label;
2395 AI.store_variable_number = st.value;
2396 AI.branch_flag = flag;
2397 assemblez_instruction(&AI);
2400 extern void assemblez_inc(assembly_operand o1)
2402 if ((o1.value >= MAX_LOCAL_VARIABLES)
2403 && (o1.value<LOWEST_SYSTEM_VAR_NUMBER))
2405 AI.internal_number = inc_zc;
2406 AI.operand_count = 1;
2407 AI.operand[0].value = o1.value;
2408 AI.operand[0].type = SHORT_CONSTANT_OT;
2409 AI.operand[0].marker = m;
2410 AI.store_variable_number = -1;
2411 AI.branch_label_number = -1;
2412 assemblez_instruction(&AI);
2415 extern void assemblez_dec(assembly_operand o1)
2417 if ((o1.value >= MAX_LOCAL_VARIABLES)
2418 && (o1.value<LOWEST_SYSTEM_VAR_NUMBER))
2420 AI.internal_number = dec_zc;
2421 AI.operand_count = 1;
2422 AI.operand[0].value = o1.value;
2423 AI.operand[0].type = SHORT_CONSTANT_OT;
2424 AI.operand[0].marker = m;
2425 AI.store_variable_number = -1;
2426 AI.branch_label_number = -1;
2427 assemblez_instruction(&AI);
2430 extern void assemblez_store(assembly_operand o1, assembly_operand o2)
2432 if ((o1.value >= MAX_LOCAL_VARIABLES)
2433 && (o1.value<LOWEST_SYSTEM_VAR_NUMBER))
2436 if ((o2.type == VARIABLE_OT) && (o2.value == 0))
2438 /* Assemble "pull VAR" rather than "store VAR sp",
2441 AI.internal_number = pull_zc;
2442 if (instruction_set_number == 6)
2443 { AI.operand_count = 0;
2444 AI.store_variable_number = o1.value;
2447 { AI.operand_count = 1;
2448 AI.operand[0].value = o1.value;
2449 AI.operand[0].type = SHORT_CONSTANT_OT;
2450 AI.operand[0].marker = m;
2451 AI.store_variable_number = -1;
2453 AI.branch_label_number = -1;
2454 assemblez_instruction(&AI);
2458 if ((o1.type == VARIABLE_OT) && (o1.value == 0))
2459 { /* Assemble "push VAR" rather than "store sp VAR",
2462 AI.internal_number = push_zc;
2463 AI.operand_count = 1;
2465 AI.store_variable_number = -1;
2466 AI.branch_label_number = -1;
2467 assemblez_instruction(&AI);
2470 AI.internal_number = store_zc;
2471 AI.operand_count = 2;
2472 AI.operand[0].value = o1.value;
2473 AI.operand[0].type = SHORT_CONSTANT_OT;
2474 AI.operand[0].marker = m;
2476 AI.store_variable_number = -1;
2477 AI.branch_label_number = -1;
2478 assemblez_instruction(&AI);
2481 void assemblez_jump(int n)
2482 { assembly_operand AO;
2483 if (n==-4) assemblez_0(rtrue_zc);
2484 else if (n==-3) assemblez_0(rfalse_zc);
2486 { AO.type = LONG_CONSTANT_OT; AO.value = n; AO.marker = 0;
2487 assemblez_1(jump_zc, AO);
2491 void assembleg_0(int internal_number)
2492 { AI.internal_number = internal_number;
2493 AI.operand_count = 0;
2494 assembleg_instruction(&AI);
2497 void assembleg_1(int internal_number, assembly_operand o1)
2498 { AI.internal_number = internal_number;
2499 AI.operand_count = 1;
2501 assembleg_instruction(&AI);
2504 void assembleg_2(int internal_number, assembly_operand o1,
2505 assembly_operand o2)
2506 { AI.internal_number = internal_number;
2507 AI.operand_count = 2;
2510 assembleg_instruction(&AI);
2513 void assembleg_3(int internal_number, assembly_operand o1,
2514 assembly_operand o2, assembly_operand o3)
2515 { AI.internal_number = internal_number;
2516 AI.operand_count = 3;
2520 assembleg_instruction(&AI);
2523 void assembleg_4(int internal_number, assembly_operand o1,
2524 assembly_operand o2, assembly_operand o3,
2525 assembly_operand o4)
2526 { AI.internal_number = internal_number;
2527 AI.operand_count = 4;
2532 assembleg_instruction(&AI);
2535 void assembleg_5(int internal_number, assembly_operand o1,
2536 assembly_operand o2, assembly_operand o3,
2537 assembly_operand o4, assembly_operand o5)
2538 { AI.internal_number = internal_number;
2539 AI.operand_count = 5;
2545 assembleg_instruction(&AI);
2548 void assembleg_0_branch(int internal_number,
2551 AI.internal_number = internal_number;
2552 AI.operand_count = 1;
2553 AI.operand[0].type = CONSTANT_OT;
2554 AI.operand[0].value = label;
2555 AI.operand[0].marker = BRANCH_MV;
2556 assembleg_instruction(&AI);
2559 void assembleg_1_branch(int internal_number,
2560 assembly_operand o1, int label)
2562 /* Some clever optimizations first. A constant is always or never equal
2564 if (o1.marker == 0 && is_constant_ot(o1.type)) {
2565 if ((internal_number == jz_gc && o1.value == 0)
2566 || (internal_number == jnz_gc && o1.value != 0)) {
2567 assembleg_0_branch(jump_gc, label);
2568 /* We clear the "can't reach statement" flag here,
2569 so that "if (1)" doesn't produce that warning. */
2570 execution_never_reaches_here = 0;
2573 if ((internal_number == jz_gc && o1.value != 0)
2574 || (internal_number == jnz_gc && o1.value == 0)) {
2575 /* assemble nothing at all! */
2579 AI.internal_number = internal_number;
2580 AI.operand_count = 2;
2582 AI.operand[1].type = CONSTANT_OT;
2583 AI.operand[1].value = label;
2584 AI.operand[1].marker = BRANCH_MV;
2585 assembleg_instruction(&AI);
2588 void assembleg_2_branch(int internal_number,
2589 assembly_operand o1, assembly_operand o2, int label)
2591 AI.internal_number = internal_number;
2592 AI.operand_count = 3;
2595 AI.operand[2].type = CONSTANT_OT;
2596 AI.operand[2].value = label;
2597 AI.operand[2].marker = BRANCH_MV;
2598 assembleg_instruction(&AI);
2601 void assembleg_call_1(assembly_operand oaddr, assembly_operand o1,
2602 assembly_operand odest)
2604 assembleg_3(callfi_gc, oaddr, o1, odest);
2607 void assembleg_call_2(assembly_operand oaddr, assembly_operand o1,
2608 assembly_operand o2, assembly_operand odest)
2610 assembleg_4(callfii_gc, oaddr, o1, o2, odest);
2613 void assembleg_call_3(assembly_operand oaddr, assembly_operand o1,
2614 assembly_operand o2, assembly_operand o3, assembly_operand odest)
2616 assembleg_5(callfiii_gc, oaddr, o1, o2, o3, odest);
2619 void assembleg_inc(assembly_operand o1)
2621 AI.internal_number = add_gc;
2622 AI.operand_count = 3;
2624 AI.operand[1] = one_operand;
2626 assembleg_instruction(&AI);
2629 void assembleg_dec(assembly_operand o1)
2631 AI.internal_number = sub_gc;
2632 AI.operand_count = 3;
2634 AI.operand[1] = one_operand;
2636 assembleg_instruction(&AI);
2639 void assembleg_store(assembly_operand o1, assembly_operand o2)
2641 /* Note the order is reversed: "o1 = o2;" */
2642 assembleg_2(copy_gc, o2, o1);
2645 void assembleg_jump(int n)
2648 assembleg_1(return_gc, one_operand);
2651 assembleg_1(return_gc, zero_operand);
2654 assembleg_0_branch(jump_gc, n);
2658 /* ========================================================================= */
2659 /* Parsing and then calling the assembler for @ (assembly language) */
2661 /* ------------------------------------------------------------------------- */
2663 static assembly_operand parse_operand_z(void)
2664 { assembly_operand AO;
2666 AO = parse_expression(ASSEMBLY_CONTEXT);
2667 if (AO.type == EXPRESSION_OT)
2668 { ebf_error("variable or constant", "expression");
2669 AO.type = SHORT_CONSTANT_OT;
2674 static void parse_assembly_z(void)
2675 { int n, min, max, indirect_addressed, error_flag = FALSE;
2678 AI.operand_count = 0;
2679 AI.store_variable_number = -1;
2680 AI.branch_label_number = -1;
2683 opcode_names.enabled = TRUE;
2685 opcode_names.enabled = FALSE;
2687 if (token_type == DQ_TT)
2689 AI.internal_number = -1;
2691 custom_opcode_z.name = (uchar *) token_text;
2692 custom_opcode_z.version1 = instruction_set_number;
2693 custom_opcode_z.version2 = instruction_set_number;
2694 custom_opcode_z.extension = -1;
2695 custom_opcode_z.flags = 0;
2696 custom_opcode_z.op_rules = 0;
2697 custom_opcode_z.flags2_set = 0;
2698 custom_opcode_z.no = ZERO;
2700 for (i=0; token_text[i]!=0; i++)
2701 { if (token_text[i] == ':')
2702 { token_text[i++] = 0;
2706 if (token_text[i] == 0)
2707 error("Opcode specification should have form \"VAR:102\"");
2710 if (strcmp(token_text, "0OP")==0) n=ZERO;
2711 if (strcmp(token_text, "1OP")==0) n=ONE;
2712 if (strcmp(token_text, "2OP")==0) n=TWO;
2713 if (strcmp(token_text, "VAR")==0) n=VAR;
2714 if (strcmp(token_text, "EXT")==0) n=EXT;
2715 if (strcmp(token_text, "VAR_LONG")==0) n=VAR_LONG;
2716 if (strcmp(token_text, "EXT_LONG")==0) n=EXT_LONG;
2718 if (i>0) token_text[i-1] = ':';
2721 { ebf_error("Expected 0OP, 1OP, 2OP, VAR, EXT, VAR_LONG or EXT_LONG",
2725 custom_opcode_z.no = n;
2727 custom_opcode_z.code = atoi(token_text+i);
2728 while (isdigit(token_text[i])) i++;
2732 { case ZERO: case ONE: max = 16; break;
2733 case VAR: case VAR_LONG: min = 32; max = 64; break;
2734 case EXT: case EXT_LONG: max = 256; break;
2735 case TWO: max = 32; break;
2737 if ((custom_opcode_z.code < min) || (custom_opcode_z.code >= max))
2739 sprintf(range, "%d to %d", min, max-1);
2740 error_named("For this operand type, opcode number must be in range",
2742 custom_opcode_z.code = min;
2746 while (token_text[i++] != 0)
2747 { switch(token_text[i-1])
2748 { case 'B': custom_opcode_z.flags |= Br; break;
2749 case 'S': custom_opcode_z.flags |= St; break;
2750 case 'T': custom_opcode_z.op_rules = TEXT; break;
2751 case 'I': custom_opcode_z.op_rules = VARIAB; break;
2752 case 'F': custom_opcode_z.flags2_set = atoi(token_text+i);
2753 while (isdigit(token_text[i])) i++; break;
2755 error("Unknown flag: options are B (branch), S (store), \
2756 T (text), I (indirect addressing), F** (set this Flags 2 bit)");
2760 O = custom_opcode_z;
2763 { if (token_type != OPCODE_NAME_TT)
2764 { ebf_error("an opcode name", token_text);
2765 panic_mode_error_recovery();
2768 AI.internal_number = token_value;
2769 O = internal_number_to_opcode_z(AI.internal_number);
2772 indirect_addressed = (O.op_rules == VARIAB);
2774 if (O.op_rules == TEXT)
2776 if (token_type != DQ_TT)
2777 ebf_error("literal text in double-quotes", token_text);
2778 AI.text = token_text;
2779 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) return;
2781 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
2782 { assemblez_instruction(&AI);
2785 ebf_error("semicolon ';' after print string", token_text);
2790 return_sp_as_variable = TRUE;
2794 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
2796 if ((token_type == SEP_TT) && (token_value == ARROW_SEP))
2797 { if (AI.store_variable_number != -1)
2798 error("Only one '->' store destination can be given");
2800 if ((token_type != SYMBOL_TT)
2801 && (token_type != LOCAL_VARIABLE_TT))
2802 ebf_error("variable name or 'sp'", token_text);
2804 if (token_type == LOCAL_VARIABLE_TT) n = token_value;
2806 { if (strcmp(token_text, "sp") == 0) n = 0;
2808 { if (stypes[token_value] != GLOBAL_VARIABLE_T)
2810 "Store '->' destination not 'sp' or a variable:",
2812 else n = svals[token_value];
2815 AI.store_variable_number = n;
2819 if ((token_type == SEP_TT) &&
2820 ((token_value == BRANCH_SEP) || (token_value == NBRANCH_SEP)))
2821 { if (AI.branch_label_number != -1)
2822 error("Only one '?' branch destination can be given");
2824 AI.branch_flag = (token_value == BRANCH_SEP);
2826 opcode_names.enabled = TRUE;
2828 opcode_names.enabled = FALSE;
2831 if ((token_type == OPCODE_NAME_TT)
2832 && (token_value == rfalse_zc)) n = -3;
2834 if ((token_type == OPCODE_NAME_TT)
2835 && (token_value == rtrue_zc)) n = -4;
2837 { if (token_type == SYMBOL_TT)
2842 ebf_error("label name after '?' or '?~'", token_text);
2844 AI.branch_label_number = n;
2848 if (AI.operand_count == 8)
2849 { error("No assembly instruction may have more than 8 operands");
2850 panic_mode_error_recovery(); break;
2853 if ((token_type == SEP_TT) && (token_value == OPEN_SQUARE_SEP))
2854 { if (!indirect_addressed)
2855 error("This opcode does not use indirect addressing");
2856 if (AI.operand_count > 0)
2857 error("Indirect addressing can only be used on the first operand");
2858 AI.operand[AI.operand_count++] = parse_operand_z();
2860 if (!((token_type == SEP_TT) && (token_value == CLOSE_SQUARE_SEP)))
2861 { ebf_error("']'", token_text);
2867 AI.operand[AI.operand_count++] = parse_operand_z();
2868 if ((indirect_addressed) && (AI.operand_count == 1)
2869 && (AI.operand[AI.operand_count-1].type == VARIABLE_OT))
2870 { AI.operand[AI.operand_count-1].type = SHORT_CONSTANT_OT;
2871 AI.operand[AI.operand_count-1].marker = VARIABLE_MV;
2877 return_sp_as_variable = FALSE;
2880 if (O.version1 == 0)
2881 { error_named("Opcode unavailable in this Z-machine version:",
2882 opcode_names.keywords[AI.internal_number]);
2886 if (((O.flags) & Br) != 0)
2887 { if (AI.branch_label_number == -1)
2888 { error_flag = TRUE;
2889 AI.branch_label_number = -2;
2893 { if (AI.branch_label_number != -1)
2894 { error_flag = TRUE;
2895 AI.branch_label_number = -1;
2898 if (((O.flags) & St) != 0)
2899 { if (AI.store_variable_number == -1)
2900 { if (AI.operand_count == 0)
2901 { error_flag = TRUE;
2902 AI.store_variable_number = 255;
2905 { AI.store_variable_number
2906 = AI.operand[--AI.operand_count].value;
2907 if (AI.operand[AI.operand_count].type != VARIABLE_OT)
2908 error("Store destination (the last operand) is not a variable");
2913 { if (AI.store_variable_number != -1)
2914 { error_flag = TRUE;
2915 AI.store_variable_number = -1;
2921 { case TWO: min = 2; max = 2;
2922 /* Exception for the V6 set_colour, which can take
2923 a third argument, thus forcing it into VAR form: */
2924 if ((version_number == 6) && (O.code == 0x1b)) max = 3;
2925 /* Also an exception for je, which can take from 1
2926 argument (useless) to 4 arguments */
2927 if (O.code == 0x01) { min = 1; max = 4; }
2929 case VAR: min = 0; max = 4; break;
2930 case VAR_LONG: min = 0; max = 8; break;
2931 case ONE: min = 1; max = 1; break;
2932 case ZERO: min = 0; max = 0; break;
2933 case EXT: min = 0; max = 4; break;
2934 case EXT_LONG: min = 0; max = 8; break;
2937 if ((AI.operand_count >= min) && (AI.operand_count <= max))
2938 assemblez_instruction(&AI);
2939 else error_flag = TRUE;
2942 { make_opcode_syntax_z(O);
2943 error_named("Assembly mistake: syntax is",
2944 opcode_syntax_string);
2948 static assembly_operand parse_operand_g(void)
2949 { assembly_operand AO;
2951 AO = parse_expression(ASSEMBLY_CONTEXT);
2952 if (AO.type == EXPRESSION_OT)
2953 { ebf_error("variable or constant", "expression");
2954 AO.type = CONSTANT_OT;
2959 static void parse_assembly_g(void)
2962 assembly_operand AO;
2963 int error_flag = FALSE, is_macro = FALSE;
2965 AI.operand_count = 0;
2967 opcode_names.enabled = TRUE;
2968 opcode_macros.enabled = TRUE;
2970 opcode_names.enabled = FALSE;
2971 opcode_macros.enabled = FALSE;
2973 if (token_type == DQ_TT) {
2977 AI.internal_number = -1;
2979 /* The format is @"FlagsCount:Code". Flags (which are optional)
2980 can include "S" for store, "SS" for two stores, "B" for branch
2981 format, "R" if execution never continues after the opcode. The
2982 Count is the number of arguments (currently limited to 0-9),
2983 and the Code is a decimal integer representing the opcode
2986 So: @"S3:123" for a three-argument opcode (load, load, store)
2987 whose opcode number is (decimal) 123. Or: @"2:234" for a
2988 two-argument opcode (load, load) whose number is 234. */
2990 custom_opcode_g.name = (uchar *) token_text;
2991 custom_opcode_g.flags = 0;
2992 custom_opcode_g.op_rules = 0;
2993 custom_opcode_g.no = 0;
2997 for (cx = token_text; *cx && *cx != ':'; cx++) {
3003 if (custom_opcode_g.flags & St)
3004 custom_opcode_g.flags |= St2;
3006 custom_opcode_g.flags |= St;
3009 custom_opcode_g.flags |= Br;
3012 custom_opcode_g.flags |= Rf;
3016 custom_opcode_g.no = (*cx) - '0';
3020 error("Unknown custom opcode flag: options are B (branch), \
3021 S (store), SS (two stores), R (execution never continues)");
3027 error("Custom opcode must have colon");
3032 error("Custom opcode must have colon followed by opcode number");
3034 custom_opcode_g.code = atoi(cx);
3037 O = custom_opcode_g;
3040 if (token_type != OPCODE_NAME_TT && token_type != OPCODE_MACRO_TT) {
3041 ebf_error("an opcode name", token_text);
3042 panic_mode_error_recovery();
3045 AI.internal_number = token_value;
3046 if (token_type == OPCODE_MACRO_TT) {
3047 O = internal_number_to_opmacro_g(AI.internal_number);
3051 O = internal_number_to_opcode_g(AI.internal_number);
3054 return_sp_as_variable = TRUE;
3059 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
3062 if (AI.operand_count == 8) {
3063 error("No assembly instruction may have more than 8 operands");
3064 panic_mode_error_recovery();
3068 if ((O.flags & Br) && (AI.operand_count == O.no-1)) {
3069 if (!((token_type == SEP_TT) && (token_value == BRANCH_SEP))) {
3071 error("Branch opcode must have '?' label");
3074 AO.type = CONSTANT_OT;
3075 AO.value = parse_label();
3076 AO.marker = BRANCH_MV;
3080 AO = parse_operand_g();
3083 AI.operand[AI.operand_count] = AO;
3087 return_sp_as_variable = FALSE;
3089 if (O.no != AI.operand_count) {
3095 assembleg_macro(&AI);
3097 assembleg_instruction(&AI);
3101 make_opcode_syntax_g(O);
3102 error_named("Assembly mistake: syntax is",
3103 opcode_syntax_string);
3107 extern void parse_assembly(void)
3115 /* ========================================================================= */
3116 /* Data structure management routines */
3117 /* ------------------------------------------------------------------------- */
3119 extern void asm_begin_pass(void)
3120 { no_instructions = 0;
3122 no_sequence_points = 0;
3124 next_sequence_point = 0;
3128 extern void init_asm_vars(void)
3131 for (i=0;i<16;i++) flags2_requirements[i]=0;
3133 uses_unicode_features = FALSE;
3134 uses_memheap_features = FALSE;
3135 uses_acceleration_features = FALSE;
3136 uses_float_features = FALSE;
3138 sequence_point_follows = TRUE;
3139 label_moved_error_already_given = FALSE;
3141 initialise_memory_block(&zcode_area);
3144 extern void asm_allocate_arrays(void)
3145 { if ((debugfile_switch) && (MAX_LABELS < 2000)) MAX_LABELS = 2000;
3147 variable_tokens = my_calloc(sizeof(int32),
3148 MAX_LOCAL_VARIABLES+MAX_GLOBAL_VARIABLES, "variable tokens");
3149 variable_usage = my_calloc(sizeof(int),
3150 MAX_LOCAL_VARIABLES+MAX_GLOBAL_VARIABLES, "variable usage");
3152 label_offsets = my_calloc(sizeof(int32), MAX_LABELS, "label offsets");
3153 label_symbols = my_calloc(sizeof(int32), MAX_LABELS, "label symbols");
3154 label_next = my_calloc(sizeof(int), MAX_LABELS, "label dll 1");
3155 label_prev = my_calloc(sizeof(int), MAX_LABELS, "label dll 1");
3156 sequence_point_labels
3157 = my_calloc(sizeof(int), MAX_LABELS, "sequence point labels");
3158 sequence_point_locations
3159 = my_calloc(sizeof(debug_location),
3161 "sequence point locations");
3163 zcode_holding_area = my_malloc(MAX_ZCODE_SIZE,"compiled routine code area");
3164 zcode_markers = my_malloc(MAX_ZCODE_SIZE, "compiled routine code area");
3166 named_routine_symbols
3167 = my_calloc(sizeof(int32), MAX_SYMBOLS, "named routine symbols");
3170 extern void asm_free_arrays(void)
3172 my_free(&variable_tokens, "variable tokens");
3173 my_free(&variable_usage, "variable usage");
3175 my_free(&label_offsets, "label offsets");
3176 my_free(&label_symbols, "label symbols");
3177 my_free(&label_next, "label dll 1");
3178 my_free(&label_prev, "label dll 2");
3179 my_free(&sequence_point_labels, "sequence point labels");
3180 my_free(&sequence_point_locations, "sequence point locations");
3182 my_free(&zcode_holding_area, "compiled routine code area");
3183 my_free(&zcode_markers, "compiled routine code markers");
3185 my_free(&named_routine_symbols, "named routine symbols");
3186 deallocate_memory_block(&zcode_area);
3189 /* ========================================================================= */