1 /* ------------------------------------------------------------------------- */
2 /* "bpatch" : Keeps track of, and finally acts on, backpatch markers, */
3 /* correcting symbol values not known at compilation time */
5 /* Part of Inform 6.41 */
6 /* copyright (c) Graham Nelson 1993 - 2022 */
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 *staticarray_backpatch_table; /* Allocated to staticarray_backpatch_size */
26 memory_list staticarray_backpatch_table_memlist;
27 uchar *zmachine_backpatch_table; /* Allocated to zmachine_backpatch_size */
28 memory_list zmachine_backpatch_table_memlist;
29 uchar *zcode_backpatch_table; /* Allocated to zcode_backpatch_size */
30 memory_list zcode_backpatch_table_memlist;
31 int32 zcode_backpatch_size, staticarray_backpatch_size,
32 zmachine_backpatch_size;
34 /* ------------------------------------------------------------------------- */
36 /* ------------------------------------------------------------------------- */
38 extern char *describe_mv(int mval)
40 { case NULL_MV: return("null");
42 /* Marker values used in ordinary story file backpatching */
44 case DWORD_MV: return("dictionary word");
45 case STRING_MV: return("string literal");
46 case INCON_MV: return("system constant");
47 case IROUTINE_MV: return("routine");
48 case VROUTINE_MV: return("veneer routine");
49 case ARRAY_MV: return("internal array");
50 case NO_OBJS_MV: return("the number of objects");
51 case INHERIT_MV: return("inherited common p value");
52 case INDIVPT_MV: return("indiv prop table address");
53 case INHERIT_INDIV_MV: return("inherited indiv p value");
54 case MAIN_MV: return("ref to Main");
55 case SYMBOL_MV: return("ref to symbol value");
57 /* Additional marker values used in Glulx backpatching
58 (IDENT_MV is not really used at all any more) */
60 case VARIABLE_MV: return("global variable");
61 case IDENT_MV: return("prop identifier number");
62 case ACTION_MV: return("action");
63 case OBJECT_MV: return("internal object");
66 return("** No such MV **");
69 /* ------------------------------------------------------------------------- */
70 /* The mending operation */
71 /* ------------------------------------------------------------------------- */
73 int backpatch_marker, backpatch_size, backpatch_error_flag;
75 static int32 backpatch_value_z(int32 value)
76 { /* Corrects the quantity "value" according to backpatch_marker */
80 if (bpatch_trace_setting)
81 printf("BP %s applied to %04x giving ",
82 describe_mv(backpatch_marker), value);
84 switch(backpatch_marker)
86 value += strings_offset/scale_factor; break;
88 value += variables_offset; break;
90 value += static_arrays_offset; break;
92 if (OMIT_UNUSED_ROUTINES)
93 value = df_stripped_address_for_address(value);
94 value += code_offset/scale_factor;
97 if ((value<0) || (value>=VENEER_ROUTINES))
100 ("Backpatch veneer routine number out of range"))
101 { printf("Illegal BP veneer routine number: %d\n", value);
102 backpatch_error_flag = TRUE;
107 value = veneer_routine_address[value];
108 if (OMIT_UNUSED_ROUTINES)
109 value = df_stripped_address_for_address(value);
110 value += code_offset/scale_factor;
113 value = no_objects; break;
115 if ((value<0) || (value>=NO_SYSTEM_CONSTANTS))
118 ("Backpatch system constant number out of range"))
119 { printf("Illegal BP system constant number: %d\n", value);
120 backpatch_error_flag = TRUE;
125 value = value_of_system_constant(value); break;
127 value = dictionary_offset + 7 +
128 final_dict_order[value]*(DICT_ENTRY_BYTE_LENGTH);
133 value = 256*zmachine_paged_memory[value + prop_values_offset]
134 + zmachine_paged_memory[value + prop_values_offset + 1];
136 case INHERIT_INDIV_MV:
137 value = 256*zmachine_paged_memory[value
138 + individuals_offset]
139 + zmachine_paged_memory[value
140 + individuals_offset + 1];
143 value += individuals_offset;
146 value = symbol_index("Main", -1);
147 if (symbols[value].type != ROUTINE_T)
148 error("No 'Main' routine has been defined");
149 symbols[value].flags |= USED_SFLAG;
150 value = symbols[value].value;
151 if (OMIT_UNUSED_ROUTINES)
152 value = df_stripped_address_for_address(value);
153 value += code_offset/scale_factor;
156 if ((value<0) || (value>=no_symbols))
158 if (compiler_error("Backpatch symbol number out of range"))
159 { printf("Illegal BP symbol number: %d\n", value);
160 backpatch_error_flag = TRUE;
165 if (symbols[value].flags & UNKNOWN_SFLAG)
166 { if (!(symbols[value].flags & UERROR_SFLAG))
167 { symbols[value].flags |= UERROR_SFLAG;
168 error_named_at("No such constant as",
169 symbols[value].name, symbols[value].line);
173 if (symbols[value].flags & CHANGE_SFLAG)
174 { symbols[value].flags &= (~(CHANGE_SFLAG));
175 backpatch_marker = (symbols[value].marker);
176 if ((backpatch_marker < 0)
177 || (backpatch_marker > LARGEST_BPATCH_MV))
179 compiler_error_named(
180 "Illegal backpatch marker attached to symbol",
181 symbols[value].name);
182 backpatch_error_flag = TRUE;
185 symbols[value].value = backpatch_value_z((symbols[value].value) % 0x10000);
188 symbols[value].flags |= USED_SFLAG;
189 { int t = symbols[value].type;
190 value = symbols[value].value;
193 if (OMIT_UNUSED_ROUTINES)
194 value = df_stripped_address_for_address(value);
195 value += code_offset/scale_factor;
197 case ARRAY_T: value += variables_offset; break;
198 case STATIC_ARRAY_T: value += static_arrays_offset; break;
203 if (compiler_error("Illegal backpatch marker"))
204 { printf("Illegal backpatch marker %d value %04x\n",
205 backpatch_marker, value);
206 backpatch_error_flag = TRUE;
211 if (bpatch_trace_setting) printf(" %04x\n", value);
216 static int32 backpatch_value_g(int32 value)
217 { /* Corrects the quantity "value" according to backpatch_marker */
222 if (bpatch_trace_setting)
223 printf("BP %s applied to %04x giving ",
224 describe_mv(backpatch_marker), value);
226 switch(backpatch_marker)
229 if (value <= 0 || value > no_strings)
230 compiler_error("Illegal string marker.");
231 value = strings_offset + compressed_offsets[value-1]; break;
233 if (OMIT_UNUSED_ROUTINES)
234 value = df_stripped_address_for_address(value);
235 value += code_offset;
238 value += arrays_offset; break;
239 case STATIC_ARRAY_MV:
240 value += static_arrays_offset; break;
242 value = variables_offset + (4*value); break;
244 value = object_tree_offset + (OBJECT_BYTE_LENGTH*(value-1));
247 if ((value<0) || (value>=VENEER_ROUTINES))
250 ("Backpatch veneer routine number out of range"))
251 { printf("Illegal BP veneer routine number: %d\n", value);
252 backpatch_error_flag = TRUE;
257 value = veneer_routine_address[value];
258 if (OMIT_UNUSED_ROUTINES)
259 value = df_stripped_address_for_address(value);
260 value += code_offset;
263 value = no_objects; break;
265 if ((value<0) || (value>=NO_SYSTEM_CONSTANTS))
268 ("Backpatch system constant number out of range"))
269 { printf("Illegal BP system constant number: %d\n", value);
270 backpatch_error_flag = TRUE;
275 value = value_of_system_constant(value); break;
277 value = dictionary_offset + 4
278 + final_dict_order[value]*DICT_ENTRY_BYTE_LENGTH;
283 valaddr = (prop_values_offset - Write_RAM_At) + value;
284 value = ReadInt32(zmachine_paged_memory + valaddr);
286 case INHERIT_INDIV_MV:
287 error("*** No individual property storage in Glulx ***");
290 value += individuals_offset;
293 value = symbol_index("Main", -1);
294 if (symbols[value].type != ROUTINE_T)
295 error("No 'Main' routine has been defined");
296 symbols[value].flags |= USED_SFLAG;
297 value = symbols[value].value;
298 if (OMIT_UNUSED_ROUTINES)
299 value = df_stripped_address_for_address(value);
300 value += code_offset;
303 if ((value<0) || (value>=no_symbols))
305 if (compiler_error("Backpatch symbol number out of range"))
306 { printf("Illegal BP symbol number: %d\n", value);
307 backpatch_error_flag = TRUE;
312 if (symbols[value].flags & UNKNOWN_SFLAG)
313 { if (!(symbols[value].flags & UERROR_SFLAG))
314 { symbols[value].flags |= UERROR_SFLAG;
315 error_named_at("No such constant as",
316 symbols[value].name, symbols[value].line);
320 if (symbols[value].flags & CHANGE_SFLAG)
321 { symbols[value].flags &= (~(CHANGE_SFLAG));
322 backpatch_marker = symbols[value].marker;
323 if ((backpatch_marker < 0)
324 || (backpatch_marker > LARGEST_BPATCH_MV))
326 compiler_error_named(
327 "Illegal backpatch marker attached to symbol",
328 symbols[value].name);
329 backpatch_error_flag = TRUE;
332 symbols[value].value = backpatch_value_g(symbols[value].value);
335 symbols[value].flags |= USED_SFLAG;
336 { int t = symbols[value].type;
337 value = symbols[value].value;
341 if (OMIT_UNUSED_ROUTINES)
342 value = df_stripped_address_for_address(value);
343 value += code_offset;
345 case ARRAY_T: value += arrays_offset; break;
346 case STATIC_ARRAY_T: value += static_arrays_offset; break;
349 value = object_tree_offset +
350 (OBJECT_BYTE_LENGTH*(value-1));
353 /* value is unchanged */
356 case INDIVIDUAL_PROPERTY_T:
358 /* value is unchanged */
361 error("*** Illegal backpatch marker in forward-declared \
368 if (compiler_error("Illegal backpatch marker"))
369 { printf("Illegal backpatch marker %d value %04x\n",
370 backpatch_marker, value);
371 backpatch_error_flag = TRUE;
376 if (bpatch_trace_setting) printf(" %04x\n", value);
381 extern int32 backpatch_value(int32 value)
384 return backpatch_value_z(value);
386 return backpatch_value_g(value);
389 static void backpatch_zmachine_z(int mv, int zmachine_area, int32 offset)
391 if (mv == OBJECT_MV) return;
392 if (mv == IDENT_MV) return;
393 if (mv == ACTION_MV) return;
395 if (bpatch_trace_setting >= 2)
396 printf("BP added: MV %d ZA %d Off %04x\n", mv, zmachine_area, offset);
398 ensure_memory_list_available(&zmachine_backpatch_table_memlist, zmachine_backpatch_size+4);
399 zmachine_backpatch_table[zmachine_backpatch_size++] = mv;
400 zmachine_backpatch_table[zmachine_backpatch_size++] = zmachine_area;
401 zmachine_backpatch_table[zmachine_backpatch_size++] = offset/256;
402 zmachine_backpatch_table[zmachine_backpatch_size++] = offset%256;
405 static void backpatch_zmachine_g(int mv, int zmachine_area, int32 offset)
407 if (mv == IDENT_MV) return;
408 if (mv == ACTION_MV) return;
410 /* The backpatch table format for Glulx:
411 First, the marker byte.
412 Then, the zmachine area being patched.
413 Then the four-byte address.
416 if (bpatch_trace_setting >= 2)
417 printf("BP added: MV %d ZA %d Off %06x\n", mv, zmachine_area, offset);
419 ensure_memory_list_available(&zmachine_backpatch_table_memlist, zmachine_backpatch_size+6);
420 zmachine_backpatch_table[zmachine_backpatch_size++] = mv;
421 zmachine_backpatch_table[zmachine_backpatch_size++] = zmachine_area;
422 zmachine_backpatch_table[zmachine_backpatch_size++] = (offset >> 24) & 0xFF;
423 zmachine_backpatch_table[zmachine_backpatch_size++] = (offset >> 16) & 0xFF;
424 zmachine_backpatch_table[zmachine_backpatch_size++] = (offset >> 8) & 0xFF;
425 zmachine_backpatch_table[zmachine_backpatch_size++] = (offset) & 0xFF;
428 extern void backpatch_zmachine(int mv, int zmachine_area, int32 offset)
431 backpatch_zmachine_z(mv, zmachine_area, offset);
433 backpatch_zmachine_g(mv, zmachine_area, offset);
436 extern void backpatch_zmachine_image_z(void)
437 { int bm = 0, zmachine_area; int32 offset, value, addr = 0;
439 backpatch_error_flag = FALSE;
440 while (bm < zmachine_backpatch_size)
442 = zmachine_backpatch_table[bm];
444 = zmachine_backpatch_table[bm+1];
446 = 256*zmachine_backpatch_table[bm+2]
447 + zmachine_backpatch_table[bm+3];
450 switch(zmachine_area)
451 { case PROP_DEFAULTS_ZA: addr = prop_defaults_offset; break;
452 case PROP_ZA: addr = prop_values_offset; break;
453 case INDIVIDUAL_PROP_ZA: addr = individuals_offset; break;
454 case DYNAMIC_ARRAY_ZA: addr = variables_offset; break;
455 case STATIC_ARRAY_ZA: addr = static_arrays_offset; break;
457 if (compiler_error("Illegal area to backpatch"))
458 backpatch_error_flag = TRUE;
462 value = 256*zmachine_paged_memory[addr]
463 + zmachine_paged_memory[addr+1];
464 value = backpatch_value_z(value);
465 zmachine_paged_memory[addr] = value/256;
466 zmachine_paged_memory[addr+1] = value%256;
468 if (backpatch_error_flag)
469 { backpatch_error_flag = FALSE;
470 printf("*** MV %d ZA %d Off %04x ***\n",
471 backpatch_marker, zmachine_area, offset);
476 extern void backpatch_zmachine_image_g(void)
477 { int bm = 0, zmachine_area; int32 offset, value, addr = 0;
479 backpatch_error_flag = FALSE;
480 while (bm < zmachine_backpatch_size)
482 = zmachine_backpatch_table[bm];
484 = zmachine_backpatch_table[bm+1];
485 offset = zmachine_backpatch_table[bm+2];
486 offset = (offset << 8) |
487 zmachine_backpatch_table[bm+3];
488 offset = (offset << 8) |
489 zmachine_backpatch_table[bm+4];
490 offset = (offset << 8) |
491 zmachine_backpatch_table[bm+5];
494 switch(zmachine_area) {
495 case PROP_DEFAULTS_ZA: addr = prop_defaults_offset+4; break;
496 case PROP_ZA: addr = prop_values_offset; break;
497 case INDIVIDUAL_PROP_ZA: addr = individuals_offset; break;
498 case DYNAMIC_ARRAY_ZA: addr = arrays_offset; break;
499 case GLOBALVAR_ZA: addr = variables_offset; break;
500 /* STATIC_ARRAY_ZA is in ROM and therefore not handled here */
502 if (compiler_error("Illegal area to backpatch"))
503 backpatch_error_flag = TRUE;
505 addr = addr + offset - Write_RAM_At;
507 value = (zmachine_paged_memory[addr] << 24)
508 | (zmachine_paged_memory[addr+1] << 16)
509 | (zmachine_paged_memory[addr+2] << 8)
510 | (zmachine_paged_memory[addr+3]);
511 value = backpatch_value_g(value);
512 zmachine_paged_memory[addr] = (value >> 24) & 0xFF;
513 zmachine_paged_memory[addr+1] = (value >> 16) & 0xFF;
514 zmachine_paged_memory[addr+2] = (value >> 8) & 0xFF;
515 zmachine_paged_memory[addr+3] = (value) & 0xFF;
517 if (backpatch_error_flag)
518 { backpatch_error_flag = FALSE;
519 printf("*** MV %d ZA %d Off %04x ***\n",
520 backpatch_marker, zmachine_area, offset);
525 /* ========================================================================= */
526 /* Data structure management routines */
527 /* ------------------------------------------------------------------------- */
529 extern void init_bpatch_vars(void)
530 { zcode_backpatch_table = NULL;
531 staticarray_backpatch_table = NULL;
532 zmachine_backpatch_table = NULL;
535 extern void bpatch_begin_pass(void)
536 { zcode_backpatch_size = 0;
537 staticarray_backpatch_size = 0;
538 zmachine_backpatch_size = 0;
541 extern void bpatch_allocate_arrays(void)
543 initialise_memory_list(&zcode_backpatch_table_memlist,
544 sizeof(uchar), 128, (void**)&zcode_backpatch_table,
545 "code backpatch table");
546 initialise_memory_list(&staticarray_backpatch_table_memlist,
547 sizeof(uchar), 128, (void**)&staticarray_backpatch_table,
548 "static array backpatch table");
549 initialise_memory_list(&zmachine_backpatch_table_memlist,
550 sizeof(uchar), 128, (void**)&zmachine_backpatch_table,
551 "machine backpatch table");
554 extern void bpatch_free_arrays(void)
555 { deallocate_memory_list(&zcode_backpatch_table_memlist);
556 deallocate_memory_list(&staticarray_backpatch_table_memlist);
557 deallocate_memory_list(&zmachine_backpatch_table_memlist);
560 /* ========================================================================= */