1 /* ------------------------------------------------------------------------- */
2 /* "asm" : The Inform assembler */
4 /* Copyright (c) Graham Nelson 1993 - 2018 */
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 = blank_brief_location;
1427 else routine_starts_line = get_brief_location(&ErrorReport);
1429 if (asm_trace_level > 0)
1430 { printf("\n%5d +%05lx [ %s ", ErrorReport.line_number,
1431 ((long int) zmachine_pc), name);
1432 for (i=1; i<=no_locals; i++) printf("%s ", variable_name(i));
1436 routine_start_pc = zmachine_pc;
1438 if (track_unused_routines) {
1439 /* The name of an embedded function is in a temporary buffer,
1440 so we shouldn't keep a reference to it. (It is sad that we
1441 have to know this here.) */
1442 char *funcname = name;
1444 funcname = "<embedded>";
1446 df_note_function_start(funcname, zmachine_pc, embedded_flag,
1447 routine_starts_line);
1450 routine_symbol = the_symbol;
1451 name_length = strlen(name) + 1;
1453 my_malloc(name_length * sizeof(char), "temporary copy of routine name");
1454 strncpy(routine_name, name, name_length);
1456 /* Update the routine counter */
1460 /* Actually assemble the routine header into the code area; note */
1461 /* Inform doesn't support the setting of local variables to default */
1462 /* values other than 0 in V3 and V4. (In V5+ the Z-Machine doesn't */
1463 /* provide the possibility in any case.) */
1468 warning("Z-code does not support stack-argument function definitions.");
1470 byteout(no_locals, 0);
1472 /* Not the packed address, but the scaled offset from code area start: */
1474 rv = zmachine_pc/scale_factor;
1476 if (instruction_set_number<5)
1477 for (i=0; i<no_locals; i++) { byteout(0,0); byteout(0,0); }
1479 next_label = 0; next_sequence_point = 0; last_label = -1;
1481 /* Compile code to print out text like "a=3, b=4, c=5" when the */
1482 /* function is called, if it's required. */
1484 if ((routine_asterisked) || (define_INFIX_switch))
1485 { char fnt[256]; assembly_operand PV, RFA, CON, STP, SLF; int ln, ln2;
1490 if (define_INFIX_switch)
1493 { SLF.value = 251; SLF.type = VARIABLE_OT; SLF.marker = 0;
1494 CON.value = 0; CON.type = SHORT_CONSTANT_OT; CON.marker = 0;
1495 assemblez_2_branch(test_attr_zc, SLF, CON, ln2, FALSE);
1498 { i = no_named_routines++;
1499 named_routine_symbols[i] = the_symbol;
1500 CON.value = i/8; CON.type = LONG_CONSTANT_OT; CON.marker = 0;
1501 RFA.value = routine_flags_array_SC;
1502 RFA.type = LONG_CONSTANT_OT; RFA.marker = INCON_MV;
1503 STP.value = 0; STP.type = VARIABLE_OT; STP.marker = 0;
1504 assemblez_2_to(loadb_zc, RFA, CON, STP);
1505 CON.value = (1 << (i%8)); CON.type = SHORT_CONSTANT_OT;
1506 assemblez_2_to(and_zc, STP, CON, STP);
1507 assemblez_1_branch(jz_zc, STP, ln2, TRUE);
1510 sprintf(fnt, "[ %s(", name);
1511 AI.text = fnt; assemblez_0(print_zc);
1512 for (i=1; (i<=7)&&(i<=no_locals); i++)
1513 { if (version_number >= 5)
1514 { PV.type = SHORT_CONSTANT_OT;
1515 PV.value = i; PV.marker = 0;
1516 assemblez_1_branch(check_arg_count_zc, PV, ln, FALSE);
1518 sprintf(fnt, "%s%s = ", (i==1)?"":", ", variable_name(i));
1519 AI.text = fnt; assemblez_0(print_zc);
1520 PV.type = VARIABLE_OT; PV.value = i; PV.marker = 0;
1521 assemblez_1(print_num_zc, PV);
1523 assemble_label_no(ln);
1524 sprintf(fnt, ") ]^"); AI.text = fnt;
1525 assemblez_0(print_zc);
1526 assemble_label_no(ln2);
1534 byteout(0xC0, 0); /* Glulx type byte for function */
1536 byteout(0xC1, 0); /* Glulx type byte for function */
1538 /* Now the locals format list. This is simple; we only use
1539 four-byte locals. That's a single pair, unless we have more
1540 than 255 locals, or none at all. */
1550 /* Terminate the list with a (0, 0) pair. */
1555 /* The top stack value is the number of function arguments. Let's
1556 move that into the first local, which is _vararg_count. */
1557 /* @copy sp _vararg_count; */
1558 byteout(0x40, 0); byteout(0x98, 0); byteout(0x00, 0);
1561 next_label = 0; next_sequence_point = 0; last_label = -1;
1563 if ((routine_asterisked) || (define_INFIX_switch)) {
1566 assembly_operand AO, AO2;
1567 if (define_INFIX_switch) {
1568 /* This isn't supported */
1569 if (embedded_flag) {
1572 i = no_named_routines++;
1573 named_routine_symbols[i] = the_symbol;
1576 sprintf(fnt, "[ %s(", name);
1577 AO.marker = STRING_MV;
1578 AO.type = CONSTANT_OT;
1579 AO.value = compile_string(fnt, FALSE, FALSE);
1580 assembleg_1(streamstr_gc, AO);
1583 for (ix=1; ix<=no_locals; ix++) {
1584 sprintf(fnt, "%s%s = ", (ix==1)?"":", ", variable_name(ix));
1585 AO.marker = STRING_MV;
1586 AO.type = CONSTANT_OT;
1587 AO.value = compile_string(fnt, FALSE, FALSE);
1588 assembleg_1(streamstr_gc, AO);
1590 AO.type = LOCALVAR_OT;
1592 assembleg_1(streamnum_gc, AO);
1596 int lntop, lnbottom;
1597 sprintf(fnt, "%s = ", variable_name(1));
1598 AO.marker = STRING_MV;
1599 AO.type = CONSTANT_OT;
1600 AO.value = compile_string(fnt, FALSE, FALSE);
1601 assembleg_1(streamstr_gc, AO);
1603 AO.type = LOCALVAR_OT;
1605 assembleg_1(streamnum_gc, AO);
1606 AO2.type = BYTECONSTANT_OT;
1609 assembleg_1(streamchar_gc, AO2);
1610 AO2.type = BYTECONSTANT_OT;
1613 /* for (temp_var4=0 : temp_var4<_vararg_count : temp_var4++) {
1615 @stkpeek temp_var4 sp;
1619 assembleg_store(temp_var4, zero_operand);
1620 lntop = next_label++;
1621 lnbottom = next_label++;
1622 assemble_label_no(lntop);
1623 assembleg_2_branch(jge_gc, temp_var4, AO, lnbottom); /* AO is _vararg_count */
1624 assembleg_1(streamchar_gc, AO2); /* AO2 is space */
1625 assembleg_2(stkpeek_gc, temp_var4, stack_pointer);
1626 assembleg_1(streamnum_gc, stack_pointer);
1627 assembleg_3(add_gc, temp_var4, one_operand, temp_var4);
1628 assembleg_0_branch(jump_gc, lntop);
1629 assemble_label_no(lnbottom);
1632 AO.marker = STRING_MV;
1633 AO.type = CONSTANT_OT;
1634 AO.value = compile_string(") ]^", FALSE, FALSE);
1635 assembleg_1(streamstr_gc, AO);
1642 void assemble_routine_end(int embedded_flag, debug_locations locations)
1645 /* No marker is made in the Z-machine's code area to indicate the */
1646 /* end of a routine. Instead, we simply assemble a return opcode if */
1647 /* need be (it won't be if the last instruction was, say, a "quit"). */
1648 /* The return value is true (1) for normal routines, false (0) for */
1649 /* embedded routines (e.g. the library uses this for "before" */
1652 if (!execution_never_reaches_here)
1655 if (embedded_flag) assemblez_0(rfalse_zc);
1656 else assemblez_0(rtrue_zc);
1659 assembly_operand AO;
1664 assembleg_1(return_gc, AO);
1668 /* Dump the contents of the current routine into longer-term Z-code
1672 transfer_routine_z();
1674 transfer_routine_g();
1676 if (track_unused_routines)
1677 df_note_function_end(zmachine_pc);
1679 /* Tell the debugging file about the routine just ended. */
1681 if (debugfile_switch)
1683 debug_file_printf("<routine>");
1686 ("<identifier artificial=\"true\">%s</identifier>",
1689 else if (sflags[routine_symbol] & REPLACE_SFLAG)
1690 { /* The symbol type will be set to ROUTINE_T once the replaced
1691 version has been given; if it is already set, we must be dealing
1692 with a replacement, and we can use the routine name as-is.
1693 Otherwise we look for a rename. And if that doesn't work, we
1694 fall back to an artificial identifier. */
1695 if (stypes[routine_symbol] == ROUTINE_T)
1696 { /* Optional because there may be further replacements. */
1697 write_debug_optional_identifier(routine_symbol);
1699 else if (find_symbol_replacement(&routine_symbol))
1701 ("<identifier>%s</identifier>", symbs[routine_symbol]);
1705 ("<identifier artificial=\"true\">%s (replaced)"
1710 { debug_file_printf("<identifier>%s</identifier>", routine_name);
1712 debug_file_printf("<value>");
1714 { write_debug_code_backpatch(routine_start_pc);
1716 { write_debug_packed_code_backpatch(routine_start_pc);
1718 debug_file_printf("</value>");
1719 debug_file_printf("<address>");
1720 write_debug_code_backpatch(routine_start_pc);
1721 debug_file_printf("</address>");
1723 ("<byte-count>%d</byte-count>", zmachine_pc - routine_start_pc);
1724 write_debug_locations(locations);
1725 for (i = 1; i <= routine_locals; ++i)
1726 { debug_file_printf("<local-variable>");
1727 debug_file_printf("<identifier>%s</identifier>", variable_name(i));
1730 ("<frame-offset>%d</frame-offset>", 4 * (i - 1));
1733 { debug_file_printf("<index>%d</index>", i);
1735 debug_file_printf("</local-variable>");
1737 for (i = 0; i < next_sequence_point; ++i)
1738 { debug_file_printf("<sequence-point>");
1739 debug_file_printf("<address>");
1740 write_debug_code_backpatch
1741 (label_offsets[sequence_point_labels[i]]);
1742 debug_file_printf("</address>");
1743 write_debug_location(sequence_point_locations[i]);
1744 debug_file_printf("</sequence-point>");
1746 debug_file_printf("</routine>");
1749 my_free(&routine_name, "temporary copy of routine name");
1751 /* Issue warnings about any local variables not used in the routine. */
1753 for (i=1; i<=routine_locals; i++)
1754 if (!(variable_usage[i]))
1755 dbnu_warning("Local variable", variable_name(i),
1756 routine_starts_line);
1758 for (i=0; i<next_label; i++)
1759 { int j = label_symbols[i];
1761 { if (sflags[j] & CHANGE_SFLAG)
1762 error_named_at("Routine contains no such label as",
1763 (char *) symbs[j], slines[j]);
1765 if ((sflags[j] & USED_SFLAG) == 0)
1766 dbnu_warning("Label", (char *) symbs[j], slines[j]);
1767 stypes[j] = CONSTANT_T;
1768 sflags[j] = UNKNOWN_SFLAG;
1771 no_sequence_points += next_sequence_point;
1772 next_label = 0; next_sequence_point = 0;
1775 /* ------------------------------------------------------------------------- */
1776 /* Called when the holding area contains an entire routine of code: */
1777 /* backpatches the labels, issues module markers, then dumps the routine */
1778 /* into longer-term storage. */
1779 /* Note that in the code received, all branches have long form, and their */
1780 /* contents are not an offset but the label numbers they branch to. */
1781 /* Similarly, LABEL operands (those of "jump" instructions) are label */
1782 /* numbers. So this routine must change the label numbers to offsets, */
1783 /* slimming the code down as it does so to take advantage of short-form */
1784 /* branch operands where possible. */
1785 /* ------------------------------------------------------------------------- */
1787 static int32 adjusted_pc;
1789 static void transfer_to_temp_file(uchar *c)
1790 { fputc(*c,Temp2_fp);
1794 static void transfer_to_zcode_area(uchar *c)
1795 { write_byte_to_memory_block(&zcode_area, adjusted_pc++, *c);
1798 static void transfer_routine_z(void)
1799 { int32 i, j, pc, new_pc, label, long_form, offset_of_next, addr,
1800 branch_on_true, rstart_pc;
1801 void (* transfer_byte)(uchar *);
1803 adjusted_pc = zmachine_pc - zcode_ha_size; rstart_pc = adjusted_pc;
1805 if (asm_trace_level >= 3)
1806 { printf("Backpatching routine at %05lx: initial size %d, %d labels\n",
1807 (long int) adjusted_pc, zcode_ha_size, next_label);
1811 (temporary_files_switch)?transfer_to_temp_file:transfer_to_zcode_area;
1813 /* (1) Scan through for branches and make short/long decisions in each
1814 case. Mark omitted bytes (2nd bytes in branches converted to
1815 short form) with DELETED_MV. */
1817 for (i=0, pc=adjusted_pc; i<zcode_ha_size; i++, pc++)
1818 { if (zcode_markers[i] == BRANCH_MV)
1819 { if (asm_trace_level >= 4)
1820 printf("Branch detected at offset %04x\n", pc);
1821 j = (256*zcode_holding_area[i] + zcode_holding_area[i+1]) & 0x7fff;
1822 if (asm_trace_level >= 4)
1823 printf("To label %d, which is %d from here\n",
1824 j, label_offsets[j]-pc);
1825 if ((label_offsets[j] >= pc+2) && (label_offsets[j] < pc+64))
1826 { if (asm_trace_level >= 4) printf("Short form\n");
1827 zcode_markers[i+1] = DELETED_MV;
1832 /* (2) Calculate the new positions of the labels. Note that since the
1833 long/short decision was taken on the basis of the old labels,
1834 and since the new labels are slightly closer together because
1835 of branch bytes deleted, there may be a few further branch
1836 optimisations which are possible but which have been missed
1837 (if two labels move inside the "short" range as a result of
1838 a previous optimisation). However, this is acceptably uncommon. */
1841 { if (asm_trace_level >= 4)
1842 { printf("Opening label: %d\n", first_label);
1843 for (i=0;i<next_label;i++)
1844 printf("Label %d offset %04x next -> %d previous -> %d\n",
1845 i, label_offsets[i], label_next[i], label_prev[i]);
1848 for (i=0, pc=adjusted_pc, new_pc=adjusted_pc, label = first_label;
1849 i<zcode_ha_size; i++, pc++)
1850 { while ((label != -1) && (label_offsets[label] == pc))
1851 { if (asm_trace_level >= 4)
1852 printf("Position of L%d corrected from %04x to %04x\n",
1853 label, label_offsets[label], new_pc);
1854 label_offsets[label] = new_pc;
1855 label = label_next[label];
1857 if (zcode_markers[i] != DELETED_MV) new_pc++;
1861 /* (3) As we are transferring, replace the label numbers in branch
1862 operands with offsets to those labels. Also issue markers, now
1863 that we know where they occur in the final Z-code area. */
1865 for (i=0, new_pc=adjusted_pc; i<zcode_ha_size; i++)
1866 { switch(zcode_markers[i])
1868 long_form = 1; if (zcode_markers[i+1] == DELETED_MV) long_form = 0;
1870 j = (256*zcode_holding_area[i] + zcode_holding_area[i+1]) & 0x7fff;
1871 branch_on_true = ((zcode_holding_area[i]) & 0x80);
1872 offset_of_next = new_pc + long_form + 1;
1874 addr = label_offsets[j] - offset_of_next + 2;
1875 if (addr<-0x2000 || addr>0x1fff)
1876 fatalerror("Branch out of range: divide the routine up?");
1877 if (addr<0) addr+=(int32) 0x10000L;
1881 { zcode_holding_area[i] = branch_on_true + addr/256;
1882 zcode_holding_area[i+1] = addr%256;
1886 { compiler_error("Label out of range for branch");
1887 printf("Addr is %04x\n", addr);
1889 zcode_holding_area[i] = branch_on_true + 0x40 + (addr&0x3f);
1891 transfer_byte(zcode_holding_area + i); new_pc++;
1895 j = 256*zcode_holding_area[i] + zcode_holding_area[i+1];
1896 addr = label_offsets[j] - new_pc;
1897 if (addr<-0x8000 || addr>0x7fff)
1898 fatalerror("Jump out of range: divide the routine up?");
1899 if (addr<0) addr += (int32) 0x10000L;
1900 zcode_holding_area[i] = addr/256;
1901 zcode_holding_area[i+1] = addr%256;
1902 transfer_byte(zcode_holding_area + i); new_pc++;
1909 switch(zcode_markers[i] & 0x7f)
1910 { case NULL_MV: break;
1915 if (!module_switch) break;
1917 if ((zcode_markers[i] & 0x7f) > LARGEST_BPATCH_MV)
1918 { compiler_error("Illegal code backpatch value");
1919 printf("Illegal value of %02x at PC = %04x\n",
1920 zcode_markers[i] & 0x7f, new_pc);
1924 write_byte_to_memory_block(&zcode_backpatch_table,
1925 zcode_backpatch_size++,
1926 zcode_markers[i] + 32*(new_pc/65536));
1927 write_byte_to_memory_block(&zcode_backpatch_table,
1928 zcode_backpatch_size++, (new_pc/256)%256);
1929 write_byte_to_memory_block(&zcode_backpatch_table,
1930 zcode_backpatch_size++, new_pc%256);
1933 transfer_byte(zcode_holding_area + i); new_pc++;
1938 if (asm_trace_level >= 3)
1939 { printf("After branch optimisation, routine length is %d bytes\n",
1940 new_pc - rstart_pc);
1943 /* Insert null bytes if necessary to ensure the next routine address is */
1944 /* expressible as a packed address */
1948 if (oddeven_packing_switch)
1949 while ((adjusted_pc%(scale_factor*2))!=0) transfer_byte(zero);
1951 while ((adjusted_pc%scale_factor)!=0) transfer_byte(zero);
1954 zmachine_pc = adjusted_pc;
1958 static void transfer_routine_g(void)
1959 { int32 i, j, pc, new_pc, label, form_len, offset_of_next, addr,
1961 void (* transfer_byte)(uchar *);
1963 adjusted_pc = zmachine_pc - zcode_ha_size; rstart_pc = adjusted_pc;
1965 if (asm_trace_level >= 3)
1966 { printf("Backpatching routine at %05lx: initial size %d, %d labels\n",
1967 (long int) adjusted_pc, zcode_ha_size, next_label);
1971 (temporary_files_switch)?transfer_to_temp_file:transfer_to_zcode_area;
1973 /* (1) Scan through for branches and make short/long decisions in each
1974 case. Mark omitted bytes (bytes 2-4 in branches converted to
1975 short form) with DELETED_MV. */
1977 for (i=0, pc=adjusted_pc; i<zcode_ha_size; i++, pc++) {
1978 if (zcode_markers[i] >= BRANCH_MV && zcode_markers[i] < BRANCHMAX_MV) {
1979 int opmodeoffset = (zcode_markers[i] - BRANCH_MV);
1981 if (asm_trace_level >= 4)
1982 printf("Branch detected at offset %04x\n", pc);
1983 j = ((zcode_holding_area[i] << 24)
1984 | (zcode_holding_area[i+1] << 16)
1985 | (zcode_holding_area[i+2] << 8)
1986 | (zcode_holding_area[i+3]));
1987 offset_of_next = pc + 4;
1988 addr = (label_offsets[j] - offset_of_next) + 2;
1989 if (asm_trace_level >= 4)
1990 printf("To label %d, which is (%d-2) = %d from here\n",
1991 j, addr, label_offsets[j] - offset_of_next);
1992 if (addr >= -0x80 && addr < 0x80) {
1993 if (asm_trace_level >= 4) printf("...Byte form\n");
1994 zcode_markers[i+1] = DELETED_MV;
1995 zcode_markers[i+2] = DELETED_MV;
1996 zcode_markers[i+3] = DELETED_MV;
1997 opmodebyte = i - ((opmodeoffset+1)/2);
1998 if ((opmodeoffset & 1) == 0)
1999 zcode_holding_area[opmodebyte] =
2000 (zcode_holding_area[opmodebyte] & 0xF0) | 0x01;
2002 zcode_holding_area[opmodebyte] =
2003 (zcode_holding_area[opmodebyte] & 0x0F) | 0x10;
2005 else if (addr >= -0x8000 && addr < 0x8000) {
2006 if (asm_trace_level >= 4) printf("...Short form\n");
2007 zcode_markers[i+2] = DELETED_MV;
2008 zcode_markers[i+3] = DELETED_MV;
2009 opmodebyte = i - ((opmodeoffset+1)/2);
2010 if ((opmodeoffset & 1) == 0)
2011 zcode_holding_area[opmodebyte] =
2012 (zcode_holding_area[opmodebyte] & 0xF0) | 0x02;
2014 zcode_holding_area[opmodebyte] =
2015 (zcode_holding_area[opmodebyte] & 0x0F) | 0x20;
2020 /* (2) Calculate the new positions of the labels. Note that since the
2021 long/short decision was taken on the basis of the old labels,
2022 and since the new labels are slightly closer together because
2023 of branch bytes deleted, there may be a few further branch
2024 optimisations which are possible but which have been missed
2025 (if two labels move inside the "short" range as a result of
2026 a previous optimisation). However, this is acceptably uncommon. */
2027 if (next_label > 0) {
2028 if (asm_trace_level >= 4) {
2029 printf("Opening label: %d\n", first_label);
2030 for (i=0;i<next_label;i++)
2031 printf("Label %d offset %04x next -> %d previous -> %d\n",
2032 i, label_offsets[i], label_next[i], label_prev[i]);
2035 for (i=0, pc=adjusted_pc, new_pc=adjusted_pc, label = first_label;
2038 while ((label != -1) && (label_offsets[label] == pc)) {
2039 if (asm_trace_level >= 4)
2040 printf("Position of L%d corrected from %04x to %04x\n",
2041 label, label_offsets[label], new_pc);
2042 label_offsets[label] = new_pc;
2043 label = label_next[label];
2045 if (zcode_markers[i] != DELETED_MV) new_pc++;
2049 /* (3) As we are transferring, replace the label numbers in branch
2050 operands with offsets to those labels. Also issue markers, now
2051 that we know where they occur in the final Z-code area. */
2053 for (i=0, new_pc=adjusted_pc; i<zcode_ha_size; i++) {
2055 if (zcode_markers[i] >= BRANCH_MV && zcode_markers[i] < BRANCHMAX_MV) {
2057 if (zcode_markers[i+1] == DELETED_MV) {
2061 if (zcode_markers[i+2] == DELETED_MV)
2064 j = ((zcode_holding_area[i] << 24)
2065 | (zcode_holding_area[i+1] << 16)
2066 | (zcode_holding_area[i+2] << 8)
2067 | (zcode_holding_area[i+3]));
2069 /* At the moment, we can safely assume that the branch operand
2070 is the end of the opcode, so the next opcode starts right
2072 offset_of_next = new_pc + form_len;
2074 addr = (label_offsets[j] - offset_of_next) + 2;
2075 if (asm_trace_level >= 4) {
2076 printf("Branch at offset %04x: %04x (%s)\n",
2077 new_pc, addr, ((form_len == 1) ? "byte" :
2078 ((form_len == 2) ? "short" : "long")));
2080 if (form_len == 1) {
2081 if (addr < -0x80 && addr >= 0x80) {
2082 error("*** Label out of range for byte branch ***");
2084 zcode_holding_area[i] = (addr) & 0xFF;
2086 else if (form_len == 2) {
2087 if (addr < -0x8000 && addr >= 0x8000) {
2088 error("*** Label out of range for short branch ***");
2090 zcode_holding_area[i] = (addr >> 8) & 0xFF;
2091 zcode_holding_area[i+1] = (addr) & 0xFF;
2094 zcode_holding_area[i] = (addr >> 24) & 0xFF;
2095 zcode_holding_area[i+1] = (addr >> 16) & 0xFF;
2096 zcode_holding_area[i+2] = (addr >> 8) & 0xFF;
2097 zcode_holding_area[i+3] = (addr) & 0xFF;
2099 transfer_byte(zcode_holding_area + i); new_pc++;
2101 else if (zcode_markers[i] == LABEL_MV) {
2102 error("*** No LABEL opcodes in Glulx ***");
2104 else if (zcode_markers[i] == DELETED_MV) {
2108 switch(zcode_markers[i] & 0x7f) {
2113 if (!module_switch) break;
2117 if ((zcode_markers[i] & 0x7f) > LARGEST_BPATCH_MV) {
2118 error("*** Illegal code backpatch value ***");
2119 printf("Illegal value of %02x at PC = %04x\n",
2120 zcode_markers[i] & 0x7f, new_pc);
2123 /* The backpatch table format for Glulx:
2124 First, the marker byte (0..LARGEST_BPATCH_MV).
2125 Then a byte indicating the data size to be patched (1, 2, 4).
2126 Then the four-byte address (new_pc).
2128 write_byte_to_memory_block(&zcode_backpatch_table,
2129 zcode_backpatch_size++,
2131 write_byte_to_memory_block(&zcode_backpatch_table,
2132 zcode_backpatch_size++,
2134 write_byte_to_memory_block(&zcode_backpatch_table,
2135 zcode_backpatch_size++, ((new_pc >> 24) & 0xFF));
2136 write_byte_to_memory_block(&zcode_backpatch_table,
2137 zcode_backpatch_size++, ((new_pc >> 16) & 0xFF));
2138 write_byte_to_memory_block(&zcode_backpatch_table,
2139 zcode_backpatch_size++, ((new_pc >> 8) & 0xFF));
2140 write_byte_to_memory_block(&zcode_backpatch_table,
2141 zcode_backpatch_size++, (new_pc & 0xFF));
2144 transfer_byte(zcode_holding_area + i); new_pc++;
2148 if (asm_trace_level >= 3)
2149 { printf("After branch optimisation, routine length is %d bytes\n",
2150 new_pc - rstart_pc);
2153 zmachine_pc = adjusted_pc;
2158 /* ========================================================================= */
2159 /* Front ends for the instruction assembler: convenient shorthand forms */
2160 /* used in various code generation routines all over Inform. */
2161 /* ------------------------------------------------------------------------- */
2163 void assemble_jump(int n)
2171 void assemblez_0(int internal_number)
2172 { AI.internal_number = internal_number;
2173 AI.operand_count = 0;
2174 AI.store_variable_number = -1;
2175 AI.branch_label_number = -1;
2176 assemblez_instruction(&AI);
2179 void assemblez_0_to(int internal_number, assembly_operand o)
2180 { AI.internal_number = internal_number;
2181 AI.operand_count = 0;
2182 AI.store_variable_number = o.value;
2183 AI.branch_label_number = -1;
2184 assemblez_instruction(&AI);
2187 void assemblez_0_branch(int internal_number, int label, int flag)
2188 { AI.internal_number = internal_number;
2189 AI.operand_count = 0;
2190 AI.store_variable_number = -1;
2191 AI.branch_label_number = label;
2192 AI.branch_flag = flag;
2193 assemblez_instruction(&AI);
2196 void assemblez_1(int internal_number, assembly_operand o1)
2197 { AI.internal_number = internal_number;
2198 AI.operand_count = 1;
2200 AI.store_variable_number = -1;
2201 AI.branch_label_number = -1;
2202 assemblez_instruction(&AI);
2205 void assemblez_1_to(int internal_number,
2206 assembly_operand o1, assembly_operand st)
2207 { AI.internal_number = internal_number;
2208 AI.operand_count = 1;
2210 AI.store_variable_number = st.value;
2211 AI.branch_label_number = -1;
2212 assemblez_instruction(&AI);
2215 void assemblez_1_branch(int internal_number,
2216 assembly_operand o1, int label, int flag)
2217 { AI.internal_number = internal_number;
2218 AI.operand_count = 1;
2220 AI.branch_label_number = label;
2221 AI.store_variable_number = -1;
2222 AI.branch_flag = flag;
2223 assemblez_instruction(&AI);
2226 void assemblez_2(int internal_number,
2227 assembly_operand o1, assembly_operand o2)
2228 { AI.internal_number = internal_number;
2229 AI.operand_count = 2;
2232 AI.store_variable_number = -1;
2233 AI.branch_label_number = -1;
2234 assemblez_instruction(&AI);
2237 void assemblez_3(int internal_number,
2238 assembly_operand o1, assembly_operand o2, assembly_operand o3)
2239 { AI.internal_number = internal_number;
2240 AI.operand_count = 3;
2244 AI.store_variable_number = -1;
2245 AI.branch_label_number = -1;
2246 assemblez_instruction(&AI);
2249 void assemblez_3_to(int internal_number,
2250 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2251 assembly_operand st)
2252 { AI.internal_number = internal_number;
2253 AI.operand_count = 3;
2257 AI.store_variable_number = st.value;
2258 AI.branch_label_number = -1;
2259 assemblez_instruction(&AI);
2262 void assemblez_3_branch(int internal_number,
2263 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2264 int label, int flag)
2265 { AI.internal_number = internal_number;
2266 AI.operand_count = 3;
2270 AI.store_variable_number = -1;
2271 AI.branch_label_number = label;
2272 AI.branch_flag = flag;
2273 assemblez_instruction(&AI);
2276 void assemblez_4(int internal_number,
2277 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2278 assembly_operand o4)
2279 { AI.internal_number = internal_number;
2280 AI.operand_count = 4;
2285 AI.store_variable_number = -1;
2286 AI.branch_label_number = -1;
2287 assemblez_instruction(&AI);
2290 void assemblez_5(int internal_number,
2291 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2292 assembly_operand o4, assembly_operand o5)
2293 { AI.internal_number = internal_number;
2294 AI.operand_count = 5;
2300 AI.store_variable_number = -1;
2301 AI.branch_label_number = -1;
2302 assemblez_instruction(&AI);
2305 void assemblez_6(int internal_number,
2306 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2307 assembly_operand o4, assembly_operand o5, assembly_operand o6)
2308 { AI.internal_number = internal_number;
2309 AI.operand_count = 6;
2316 AI.store_variable_number = -1;
2317 AI.branch_label_number = -1;
2318 assemblez_instruction(&AI);
2321 void assemblez_4_branch(int internal_number,
2322 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2323 assembly_operand o4, int label, int flag)
2324 { AI.internal_number = internal_number;
2325 AI.operand_count = 4;
2330 AI.store_variable_number = -1;
2331 AI.branch_label_number = label;
2332 AI.branch_flag = flag;
2333 assemblez_instruction(&AI);
2336 void assemblez_4_to(int internal_number,
2337 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2338 assembly_operand o4, assembly_operand st)
2339 { AI.internal_number = internal_number;
2340 AI.operand_count = 4;
2345 AI.store_variable_number = st.value;
2346 AI.branch_label_number = -1;
2347 assemblez_instruction(&AI);
2350 void assemblez_5_to(int internal_number,
2351 assembly_operand o1, assembly_operand o2, assembly_operand o3,
2352 assembly_operand o4, assembly_operand o5, assembly_operand st)
2353 { AI.internal_number = internal_number;
2354 AI.operand_count = 5;
2360 AI.store_variable_number = st.value;
2361 AI.branch_label_number = -1;
2362 assemblez_instruction(&AI);
2365 void assemblez_2_to(int internal_number,
2366 assembly_operand o1, assembly_operand o2, assembly_operand st)
2367 { AI.internal_number = internal_number;
2368 AI.operand_count = 2;
2371 AI.store_variable_number = st.value;
2372 AI.branch_label_number = -1;
2373 assemblez_instruction(&AI);
2376 void assemblez_2_branch(int internal_number,
2377 assembly_operand o1, assembly_operand o2, int label, int flag)
2378 { AI.internal_number = internal_number;
2379 AI.operand_count = 2;
2382 AI.branch_label_number = label;
2383 AI.store_variable_number = -1;
2384 AI.branch_flag = flag;
2385 assemblez_instruction(&AI);
2388 void assemblez_objcode(int internal_number,
2389 assembly_operand o1, assembly_operand st, int label, int flag)
2390 { AI.internal_number = internal_number;
2391 AI.operand_count = 1;
2393 AI.branch_label_number = label;
2394 AI.store_variable_number = st.value;
2395 AI.branch_flag = flag;
2396 assemblez_instruction(&AI);
2399 extern void assemblez_inc(assembly_operand o1)
2401 if ((o1.value >= MAX_LOCAL_VARIABLES)
2402 && (o1.value<LOWEST_SYSTEM_VAR_NUMBER))
2404 AI.internal_number = inc_zc;
2405 AI.operand_count = 1;
2406 AI.operand[0].value = o1.value;
2407 AI.operand[0].type = SHORT_CONSTANT_OT;
2408 AI.operand[0].marker = m;
2409 AI.store_variable_number = -1;
2410 AI.branch_label_number = -1;
2411 assemblez_instruction(&AI);
2414 extern void assemblez_dec(assembly_operand o1)
2416 if ((o1.value >= MAX_LOCAL_VARIABLES)
2417 && (o1.value<LOWEST_SYSTEM_VAR_NUMBER))
2419 AI.internal_number = dec_zc;
2420 AI.operand_count = 1;
2421 AI.operand[0].value = o1.value;
2422 AI.operand[0].type = SHORT_CONSTANT_OT;
2423 AI.operand[0].marker = m;
2424 AI.store_variable_number = -1;
2425 AI.branch_label_number = -1;
2426 assemblez_instruction(&AI);
2429 extern void assemblez_store(assembly_operand o1, assembly_operand o2)
2431 if ((o1.value >= MAX_LOCAL_VARIABLES)
2432 && (o1.value<LOWEST_SYSTEM_VAR_NUMBER))
2435 if ((o2.type == VARIABLE_OT) && (o2.value == 0))
2437 /* Assemble "pull VAR" rather than "store VAR sp",
2440 AI.internal_number = pull_zc;
2441 if (instruction_set_number == 6)
2442 { AI.operand_count = 0;
2443 AI.store_variable_number = o1.value;
2446 { AI.operand_count = 1;
2447 AI.operand[0].value = o1.value;
2448 AI.operand[0].type = SHORT_CONSTANT_OT;
2449 AI.operand[0].marker = m;
2450 AI.store_variable_number = -1;
2452 AI.branch_label_number = -1;
2453 assemblez_instruction(&AI);
2457 if ((o1.type == VARIABLE_OT) && (o1.value == 0))
2458 { /* Assemble "push VAR" rather than "store sp VAR",
2461 AI.internal_number = push_zc;
2462 AI.operand_count = 1;
2464 AI.store_variable_number = -1;
2465 AI.branch_label_number = -1;
2466 assemblez_instruction(&AI);
2469 AI.internal_number = store_zc;
2470 AI.operand_count = 2;
2471 AI.operand[0].value = o1.value;
2472 AI.operand[0].type = SHORT_CONSTANT_OT;
2473 AI.operand[0].marker = m;
2475 AI.store_variable_number = -1;
2476 AI.branch_label_number = -1;
2477 assemblez_instruction(&AI);
2480 void assemblez_jump(int n)
2481 { assembly_operand AO;
2482 if (n==-4) assemblez_0(rtrue_zc);
2483 else if (n==-3) assemblez_0(rfalse_zc);
2485 { AO.type = LONG_CONSTANT_OT; AO.value = n; AO.marker = 0;
2486 assemblez_1(jump_zc, AO);
2490 void assembleg_0(int internal_number)
2491 { AI.internal_number = internal_number;
2492 AI.operand_count = 0;
2493 assembleg_instruction(&AI);
2496 void assembleg_1(int internal_number, assembly_operand o1)
2497 { AI.internal_number = internal_number;
2498 AI.operand_count = 1;
2500 assembleg_instruction(&AI);
2503 void assembleg_2(int internal_number, assembly_operand o1,
2504 assembly_operand o2)
2505 { AI.internal_number = internal_number;
2506 AI.operand_count = 2;
2509 assembleg_instruction(&AI);
2512 void assembleg_3(int internal_number, assembly_operand o1,
2513 assembly_operand o2, assembly_operand o3)
2514 { AI.internal_number = internal_number;
2515 AI.operand_count = 3;
2519 assembleg_instruction(&AI);
2522 void assembleg_4(int internal_number, assembly_operand o1,
2523 assembly_operand o2, assembly_operand o3,
2524 assembly_operand o4)
2525 { AI.internal_number = internal_number;
2526 AI.operand_count = 4;
2531 assembleg_instruction(&AI);
2534 void assembleg_5(int internal_number, assembly_operand o1,
2535 assembly_operand o2, assembly_operand o3,
2536 assembly_operand o4, assembly_operand o5)
2537 { AI.internal_number = internal_number;
2538 AI.operand_count = 5;
2544 assembleg_instruction(&AI);
2547 void assembleg_0_branch(int internal_number,
2550 AI.internal_number = internal_number;
2551 AI.operand_count = 1;
2552 AI.operand[0].type = CONSTANT_OT;
2553 AI.operand[0].value = label;
2554 AI.operand[0].marker = BRANCH_MV;
2555 assembleg_instruction(&AI);
2558 void assembleg_1_branch(int internal_number,
2559 assembly_operand o1, int label)
2561 /* Some clever optimizations first. A constant is always or never equal
2563 if (o1.marker == 0 && is_constant_ot(o1.type)) {
2564 if ((internal_number == jz_gc && o1.value == 0)
2565 || (internal_number == jnz_gc && o1.value != 0)) {
2566 assembleg_0_branch(jump_gc, label);
2567 /* We clear the "can't reach statement" flag here,
2568 so that "if (1)" doesn't produce that warning. */
2569 execution_never_reaches_here = 0;
2572 if ((internal_number == jz_gc && o1.value != 0)
2573 || (internal_number == jnz_gc && o1.value == 0)) {
2574 /* assemble nothing at all! */
2578 AI.internal_number = internal_number;
2579 AI.operand_count = 2;
2581 AI.operand[1].type = CONSTANT_OT;
2582 AI.operand[1].value = label;
2583 AI.operand[1].marker = BRANCH_MV;
2584 assembleg_instruction(&AI);
2587 void assembleg_2_branch(int internal_number,
2588 assembly_operand o1, assembly_operand o2, int label)
2590 AI.internal_number = internal_number;
2591 AI.operand_count = 3;
2594 AI.operand[2].type = CONSTANT_OT;
2595 AI.operand[2].value = label;
2596 AI.operand[2].marker = BRANCH_MV;
2597 assembleg_instruction(&AI);
2600 void assembleg_call_1(assembly_operand oaddr, assembly_operand o1,
2601 assembly_operand odest)
2603 assembleg_3(callfi_gc, oaddr, o1, odest);
2606 void assembleg_call_2(assembly_operand oaddr, assembly_operand o1,
2607 assembly_operand o2, assembly_operand odest)
2609 assembleg_4(callfii_gc, oaddr, o1, o2, odest);
2612 void assembleg_call_3(assembly_operand oaddr, assembly_operand o1,
2613 assembly_operand o2, assembly_operand o3, assembly_operand odest)
2615 assembleg_5(callfiii_gc, oaddr, o1, o2, o3, odest);
2618 void assembleg_inc(assembly_operand o1)
2620 AI.internal_number = add_gc;
2621 AI.operand_count = 3;
2623 AI.operand[1] = one_operand;
2625 assembleg_instruction(&AI);
2628 void assembleg_dec(assembly_operand o1)
2630 AI.internal_number = sub_gc;
2631 AI.operand_count = 3;
2633 AI.operand[1] = one_operand;
2635 assembleg_instruction(&AI);
2638 void assembleg_store(assembly_operand o1, assembly_operand o2)
2640 /* Note the order is reversed: "o1 = o2;" */
2641 assembleg_2(copy_gc, o2, o1);
2644 void assembleg_jump(int n)
2647 assembleg_1(return_gc, one_operand);
2650 assembleg_1(return_gc, zero_operand);
2653 assembleg_0_branch(jump_gc, n);
2657 /* ========================================================================= */
2658 /* Parsing and then calling the assembler for @ (assembly language) */
2660 /* ------------------------------------------------------------------------- */
2662 static assembly_operand parse_operand_z(void)
2663 { assembly_operand AO;
2665 AO = parse_expression(ASSEMBLY_CONTEXT);
2666 if (AO.type == EXPRESSION_OT)
2667 { ebf_error("variable or constant", "expression");
2668 AO.type = SHORT_CONSTANT_OT;
2673 static void parse_assembly_z(void)
2674 { int n, min, max, indirect_addressed, error_flag = FALSE;
2677 AI.operand_count = 0;
2678 AI.store_variable_number = -1;
2679 AI.branch_label_number = -1;
2682 opcode_names.enabled = TRUE;
2684 opcode_names.enabled = FALSE;
2686 if (token_type == DQ_TT)
2688 AI.internal_number = -1;
2690 custom_opcode_z.name = (uchar *) token_text;
2691 custom_opcode_z.version1 = instruction_set_number;
2692 custom_opcode_z.version2 = instruction_set_number;
2693 custom_opcode_z.extension = -1;
2694 custom_opcode_z.flags = 0;
2695 custom_opcode_z.op_rules = 0;
2696 custom_opcode_z.flags2_set = 0;
2697 custom_opcode_z.no = ZERO;
2699 for (i=0; token_text[i]!=0; i++)
2700 { if (token_text[i] == ':')
2701 { token_text[i++] = 0;
2705 if (token_text[i] == 0)
2706 error("Opcode specification should have form \"VAR:102\"");
2709 if (strcmp(token_text, "0OP")==0) n=ZERO;
2710 if (strcmp(token_text, "1OP")==0) n=ONE;
2711 if (strcmp(token_text, "2OP")==0) n=TWO;
2712 if (strcmp(token_text, "VAR")==0) n=VAR;
2713 if (strcmp(token_text, "EXT")==0) n=EXT;
2714 if (strcmp(token_text, "VAR_LONG")==0) n=VAR_LONG;
2715 if (strcmp(token_text, "EXT_LONG")==0) n=EXT_LONG;
2717 if (i>0) token_text[i-1] = ':';
2720 { ebf_error("Expected 0OP, 1OP, 2OP, VAR, EXT, VAR_LONG or EXT_LONG",
2724 custom_opcode_z.no = n;
2726 custom_opcode_z.code = atoi(token_text+i);
2727 while (isdigit(token_text[i])) i++;
2731 { case ZERO: case ONE: max = 16; break;
2732 case VAR: case VAR_LONG: min = 32; max = 64; break;
2733 case EXT: case EXT_LONG: max = 256; break;
2734 case TWO: max = 32; break;
2736 if ((custom_opcode_z.code < min) || (custom_opcode_z.code >= max))
2738 sprintf(range, "%d to %d", min, max-1);
2739 error_named("For this operand type, opcode number must be in range",
2741 custom_opcode_z.code = min;
2745 while (token_text[i++] != 0)
2746 { switch(token_text[i-1])
2747 { case 'B': custom_opcode_z.flags |= Br; break;
2748 case 'S': custom_opcode_z.flags |= St; break;
2749 case 'T': custom_opcode_z.op_rules = TEXT; break;
2750 case 'I': custom_opcode_z.op_rules = VARIAB; break;
2751 case 'F': custom_opcode_z.flags2_set = atoi(token_text+i);
2752 while (isdigit(token_text[i])) i++; break;
2754 error("Unknown flag: options are B (branch), S (store), \
2755 T (text), I (indirect addressing), F** (set this Flags 2 bit)");
2759 O = custom_opcode_z;
2762 { if (token_type != OPCODE_NAME_TT)
2763 { ebf_error("an opcode name", token_text);
2764 panic_mode_error_recovery();
2767 AI.internal_number = token_value;
2768 O = internal_number_to_opcode_z(AI.internal_number);
2771 indirect_addressed = (O.op_rules == VARIAB);
2773 if (O.op_rules == TEXT)
2775 if (token_type != DQ_TT)
2776 ebf_error("literal text in double-quotes", token_text);
2777 AI.text = token_text;
2778 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) return;
2780 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
2781 { assemblez_instruction(&AI);
2784 ebf_error("semicolon ';' after print string", token_text);
2789 return_sp_as_variable = TRUE;
2793 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break;
2795 if ((token_type == SEP_TT) && (token_value == ARROW_SEP))
2796 { if (AI.store_variable_number != -1)
2797 error("Only one '->' store destination can be given");
2799 if ((token_type != SYMBOL_TT)
2800 && (token_type != LOCAL_VARIABLE_TT))
2801 ebf_error("variable name or 'sp'", token_text);
2803 if (token_type == LOCAL_VARIABLE_TT) n = token_value;
2805 { if (strcmp(token_text, "sp") == 0) n = 0;
2807 { if (stypes[token_value] != GLOBAL_VARIABLE_T)
2809 "Store '->' destination not 'sp' or a variable:",
2811 else n = svals[token_value];
2814 AI.store_variable_number = n;
2818 if ((token_type == SEP_TT) &&
2819 ((token_value == BRANCH_SEP) || (token_value == NBRANCH_SEP)))
2820 { if (AI.branch_label_number != -1)
2821 error("Only one '?' branch destination can be given");
2823 AI.branch_flag = (token_value == BRANCH_SEP);
2825 opcode_names.enabled = TRUE;
2827 opcode_names.enabled = FALSE;
2830 if ((token_type == OPCODE_NAME_TT)
2831 && (token_value == rfalse_zc)) n = -3;
2833 if ((token_type == OPCODE_NAME_TT)
2834 && (token_value == rtrue_zc)) n = -4;
2836 { if (token_type == SYMBOL_TT)
2841 ebf_error("label name after '?' or '?~'", token_text);
2843 AI.branch_label_number = n;
2847 if (AI.operand_count == 8)
2848 { error("No assembly instruction may have more than 8 operands");
2849 panic_mode_error_recovery(); break;
2852 if ((token_type == SEP_TT) && (token_value == OPEN_SQUARE_SEP))
2853 { if (!indirect_addressed)
2854 error("This opcode does not use indirect addressing");
2855 if (AI.operand_count > 0)
2856 error("Indirect addressing can only be used on the first operand");
2857 AI.operand[AI.operand_count++] = parse_operand_z();
2859 if (!((token_type == SEP_TT) && (token_value == CLOSE_SQUARE_SEP)))
2860 { ebf_error("']'", token_text);
2866 AI.operand[AI.operand_count++] = parse_operand_z();
2867 if ((indirect_addressed) && (AI.operand_count == 1)
2868 && (AI.operand[AI.operand_count-1].type == VARIABLE_OT))
2869 { AI.operand[AI.operand_count-1].type = SHORT_CONSTANT_OT;
2870 AI.operand[AI.operand_count-1].marker = VARIABLE_MV;
2876 return_sp_as_variable = FALSE;
2879 if (O.version1 == 0)
2880 { error_named("Opcode unavailable in this Z-machine version:",
2881 opcode_names.keywords[AI.internal_number]);
2885 if (((O.flags) & Br) != 0)
2886 { if (AI.branch_label_number == -1)
2887 { error_flag = TRUE;
2888 AI.branch_label_number = -2;
2892 { if (AI.branch_label_number != -1)
2893 { error_flag = TRUE;
2894 AI.branch_label_number = -1;
2897 if (((O.flags) & St) != 0)
2898 { if (AI.store_variable_number == -1)
2899 { if (AI.operand_count == 0)
2900 { error_flag = TRUE;
2901 AI.store_variable_number = 255;
2904 { AI.store_variable_number
2905 = AI.operand[--AI.operand_count].value;
2906 if (AI.operand[AI.operand_count].type != VARIABLE_OT)
2907 error("Store destination (the last operand) is not a variable");
2912 { if (AI.store_variable_number != -1)
2913 { error_flag = TRUE;
2914 AI.store_variable_number = -1;
2920 { case TWO: min = 2; max = 2;
2921 /* Exception for the V6 set_colour, which can take
2922 a third argument, thus forcing it into VAR form: */
2923 if ((version_number == 6) && (O.code == 0x1b)) max = 3;
2924 /* Also an exception for je, which can take from 1
2925 argument (useless) to 4 arguments */
2926 if (O.code == 0x01) { min = 1; max = 4; }
2928 case VAR: min = 0; max = 4; break;
2929 case VAR_LONG: min = 0; max = 8; break;
2930 case ONE: min = 1; max = 1; break;
2931 case ZERO: min = 0; max = 0; break;
2932 case EXT: min = 0; max = 4; break;
2933 case EXT_LONG: min = 0; max = 8; break;
2936 if ((AI.operand_count >= min) && (AI.operand_count <= max))
2937 assemblez_instruction(&AI);
2938 else error_flag = TRUE;
2941 { make_opcode_syntax_z(O);
2942 error_named("Assembly mistake: syntax is",
2943 opcode_syntax_string);
2947 static assembly_operand parse_operand_g(void)
2948 { assembly_operand AO;
2950 AO = parse_expression(ASSEMBLY_CONTEXT);
2951 if (AO.type == EXPRESSION_OT)
2952 { ebf_error("variable or constant", "expression");
2953 AO.type = CONSTANT_OT;
2958 static void parse_assembly_g(void)
2961 assembly_operand AO;
2962 int error_flag = FALSE, is_macro = FALSE;
2964 AI.operand_count = 0;
2966 opcode_names.enabled = TRUE;
2967 opcode_macros.enabled = TRUE;
2969 opcode_names.enabled = FALSE;
2970 opcode_macros.enabled = FALSE;
2972 if (token_type == DQ_TT) {
2976 AI.internal_number = -1;
2978 /* The format is @"FlagsCount:Code". Flags (which are optional)
2979 can include "S" for store, "SS" for two stores, "B" for branch
2980 format, "R" if execution never continues after the opcode. The
2981 Count is the number of arguments (currently limited to 0-9),
2982 and the Code is a decimal integer representing the opcode
2985 So: @"S3:123" for a three-argument opcode (load, load, store)
2986 whose opcode number is (decimal) 123. Or: @"2:234" for a
2987 two-argument opcode (load, load) whose number is 234. */
2989 custom_opcode_g.name = (uchar *) token_text;
2990 custom_opcode_g.flags = 0;
2991 custom_opcode_g.op_rules = 0;
2992 custom_opcode_g.no = 0;
2996 for (cx = token_text; *cx && *cx != ':'; cx++) {
3002 if (custom_opcode_g.flags & St)
3003 custom_opcode_g.flags |= St2;
3005 custom_opcode_g.flags |= St;
3008 custom_opcode_g.flags |= Br;
3011 custom_opcode_g.flags |= Rf;
3015 custom_opcode_g.no = (*cx) - '0';
3019 error("Unknown custom opcode flag: options are B (branch), \
3020 S (store), SS (two stores), R (execution never continues)");
3026 error("Custom opcode must have colon");
3031 error("Custom opcode must have colon followed by opcode number");
3033 custom_opcode_g.code = atoi(cx);
3036 O = custom_opcode_g;
3039 if (token_type != OPCODE_NAME_TT && token_type != OPCODE_MACRO_TT) {
3040 ebf_error("an opcode name", token_text);
3041 panic_mode_error_recovery();
3044 AI.internal_number = token_value;
3045 if (token_type == OPCODE_MACRO_TT) {
3046 O = internal_number_to_opmacro_g(AI.internal_number);
3050 O = internal_number_to_opcode_g(AI.internal_number);
3053 return_sp_as_variable = TRUE;
3058 if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))
3061 if (AI.operand_count == 8) {
3062 error("No assembly instruction may have more than 8 operands");
3063 panic_mode_error_recovery();
3067 if ((O.flags & Br) && (AI.operand_count == O.no-1)) {
3068 if (!((token_type == SEP_TT) && (token_value == BRANCH_SEP))) {
3070 error("Branch opcode must have '?' label");
3073 AO.type = CONSTANT_OT;
3074 AO.value = parse_label();
3075 AO.marker = BRANCH_MV;
3079 AO = parse_operand_g();
3082 AI.operand[AI.operand_count] = AO;
3086 return_sp_as_variable = FALSE;
3088 if (O.no != AI.operand_count) {
3094 assembleg_macro(&AI);
3096 assembleg_instruction(&AI);
3100 make_opcode_syntax_g(O);
3101 error_named("Assembly mistake: syntax is",
3102 opcode_syntax_string);
3106 extern void parse_assembly(void)
3114 /* ========================================================================= */
3115 /* Data structure management routines */
3116 /* ------------------------------------------------------------------------- */
3118 extern void asm_begin_pass(void)
3119 { no_instructions = 0;
3121 no_sequence_points = 0;
3123 next_sequence_point = 0;
3127 extern void init_asm_vars(void)
3130 for (i=0;i<16;i++) flags2_requirements[i]=0;
3132 uses_unicode_features = FALSE;
3133 uses_memheap_features = FALSE;
3134 uses_acceleration_features = FALSE;
3135 uses_float_features = FALSE;
3137 sequence_point_follows = TRUE;
3138 label_moved_error_already_given = FALSE;
3140 initialise_memory_block(&zcode_area);
3143 extern void asm_allocate_arrays(void)
3144 { if ((debugfile_switch) && (MAX_LABELS < 2000)) MAX_LABELS = 2000;
3146 variable_tokens = my_calloc(sizeof(int32),
3147 MAX_LOCAL_VARIABLES+MAX_GLOBAL_VARIABLES, "variable tokens");
3148 variable_usage = my_calloc(sizeof(int),
3149 MAX_LOCAL_VARIABLES+MAX_GLOBAL_VARIABLES, "variable usage");
3151 label_offsets = my_calloc(sizeof(int32), MAX_LABELS, "label offsets");
3152 label_symbols = my_calloc(sizeof(int32), MAX_LABELS, "label symbols");
3153 label_next = my_calloc(sizeof(int), MAX_LABELS, "label dll 1");
3154 label_prev = my_calloc(sizeof(int), MAX_LABELS, "label dll 1");
3155 sequence_point_labels
3156 = my_calloc(sizeof(int), MAX_LABELS, "sequence point labels");
3157 sequence_point_locations
3158 = my_calloc(sizeof(debug_location),
3160 "sequence point locations");
3162 zcode_holding_area = my_malloc(MAX_ZCODE_SIZE,"compiled routine code area");
3163 zcode_markers = my_malloc(MAX_ZCODE_SIZE, "compiled routine code area");
3165 named_routine_symbols
3166 = my_calloc(sizeof(int32), MAX_SYMBOLS, "named routine symbols");
3169 extern void asm_free_arrays(void)
3171 my_free(&variable_tokens, "variable tokens");
3172 my_free(&variable_usage, "variable usage");
3174 my_free(&label_offsets, "label offsets");
3175 my_free(&label_symbols, "label symbols");
3176 my_free(&label_next, "label dll 1");
3177 my_free(&label_prev, "label dll 2");
3178 my_free(&sequence_point_labels, "sequence point labels");
3179 my_free(&sequence_point_locations, "sequence point locations");
3181 my_free(&zcode_holding_area, "compiled routine code area");
3182 my_free(&zcode_markers, "compiled routine code markers");
3184 my_free(&named_routine_symbols, "named routine symbols");
3185 deallocate_memory_block(&zcode_area);
3188 /* ========================================================================= */