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.42 */
6 /* copyright (c) Graham Nelson 1993 - 2024 */
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 STATIC_ARRAY_MV: return("internal static array");
51 case NO_OBJS_MV: return("the number of objects");
52 case INHERIT_MV: return("inherited common p value");
53 case INDIVPT_MV: return("indiv prop table address");
54 case INHERIT_INDIV_MV: return("inherited indiv p value");
55 case MAIN_MV: return("ref to Main");
56 case SYMBOL_MV: return("ref to symbol value");
58 /* Additional marker values used in Glulx backpatching
59 (IDENT_MV is not really used at all any more) */
61 case VARIABLE_MV: return("global variable");
62 case IDENT_MV: return("prop identifier number");
63 case ACTION_MV: return("action");
64 case OBJECT_MV: return("internal object");
66 /* Only occurs secondary to another reported error */
67 case ERROR_MV: return("error");
70 return("** No such MV **");
73 extern char *describe_mv_short(int mval)
75 { case NULL_MV: return("");
77 /* Marker values used in ordinary story file backpatching */
79 case DWORD_MV: return("dict");
80 case STRING_MV: return("str");
81 case INCON_MV: return("syscon");
82 case IROUTINE_MV: return("rtn");
83 case VROUTINE_MV: return("vrtn");
84 case ARRAY_MV: return("arr");
85 case STATIC_ARRAY_MV: return("stat-arr");
86 case NO_OBJS_MV: return("obj-count");
87 case INHERIT_MV: return("inh-com");
88 case INDIVPT_MV: return("indiv-ptab");
89 case INHERIT_INDIV_MV: return("inh-indiv");
90 case MAIN_MV: return("main");
91 case SYMBOL_MV: return("sym");
93 /* Additional marker values used in Glulx backpatching
94 (IDENT_MV is not really used at all any more) */
96 case VARIABLE_MV: return("glob");
97 case IDENT_MV: return("prop");
98 case ACTION_MV: return("action");
99 case OBJECT_MV: return("obj");
101 case LABEL_MV: return("lbl");
102 case DELETED_MV: return("del");
104 /* Only occurs secondary to another reported error */
105 case ERROR_MV: return("err");
108 if (mval >= BRANCH_MV && mval < BRANCHMAX_MV) return "br";
113 /* ------------------------------------------------------------------------- */
114 /* The mending operation */
115 /* ------------------------------------------------------------------------- */
117 int backpatch_marker, backpatch_size, backpatch_error_flag;
119 static int32 backpatch_value_z(int32 value)
120 { /* Corrects the quantity "value" according to backpatch_marker */
124 if (bpatch_trace_setting)
125 printf("BP %s applied to %04x giving ",
126 describe_mv(backpatch_marker), value);
128 switch(backpatch_marker)
130 value += strings_offset/scale_factor; break;
132 value += variables_offset; break;
133 case STATIC_ARRAY_MV:
134 value += static_arrays_offset; break;
136 if (OMIT_UNUSED_ROUTINES)
137 value = df_stripped_address_for_address(value);
138 value += code_offset/scale_factor;
141 if ((value<0) || (value>=VENEER_ROUTINES))
144 ("Backpatch veneer routine number out of range"))
145 { printf("Illegal BP veneer routine number: %d\n", value);
146 backpatch_error_flag = TRUE;
151 value = veneer_routine_address[value];
152 if (OMIT_UNUSED_ROUTINES)
153 value = df_stripped_address_for_address(value);
154 value += code_offset/scale_factor;
157 value = no_objects; break;
159 if ((value<0) || (value>=NO_SYSTEM_CONSTANTS))
162 ("Backpatch system constant number out of range"))
163 { printf("Illegal BP system constant number: %d\n", value);
164 backpatch_error_flag = TRUE;
169 value = value_of_system_constant(value); break;
171 value = dictionary_offset + 7 +
172 final_dict_order[value]*(DICT_ENTRY_BYTE_LENGTH);
177 value = 256*zmachine_paged_memory[value + prop_values_offset]
178 + zmachine_paged_memory[value + prop_values_offset + 1];
180 case INHERIT_INDIV_MV:
181 value = 256*zmachine_paged_memory[value
182 + individuals_offset]
183 + zmachine_paged_memory[value
184 + individuals_offset + 1];
187 value += individuals_offset;
190 value = get_symbol_index("Main");
191 if (value < 0 || (symbols[value].flags & UNKNOWN_SFLAG)) {
192 error("No 'Main' routine has been defined");
196 if (symbols[value].type != ROUTINE_T) {
197 ebf_symbol_error("'Main' routine", symbols[value].name, typename(symbols[value].type), symbols[value].line);
201 symbols[value].flags |= USED_SFLAG;
202 value = symbols[value].value;
203 if (OMIT_UNUSED_ROUTINES)
204 value = df_stripped_address_for_address(value);
205 value += code_offset/scale_factor;
208 if ((value<0) || (value>=no_symbols))
210 if (compiler_error("Backpatch symbol number out of range"))
211 { printf("Illegal BP symbol number: %d\n", value);
212 backpatch_error_flag = TRUE;
217 if (symbols[value].flags & UNKNOWN_SFLAG)
218 { if (!(symbols[value].flags & UERROR_SFLAG))
219 { symbols[value].flags |= UERROR_SFLAG;
220 error_named_at("No such constant as",
221 symbols[value].name, symbols[value].line);
225 if (symbols[value].flags & CHANGE_SFLAG)
226 { symbols[value].flags &= (~(CHANGE_SFLAG));
227 backpatch_marker = (symbols[value].marker);
228 if ((backpatch_marker < 0)
229 || (backpatch_marker > LARGEST_BPATCH_MV))
231 compiler_error_named(
232 "Illegal backpatch marker attached to symbol",
233 symbols[value].name);
234 backpatch_error_flag = TRUE;
237 symbols[value].value = backpatch_value_z((symbols[value].value) % 0x10000);
240 symbols[value].flags |= USED_SFLAG;
241 { int t = symbols[value].type;
242 value = symbols[value].value;
245 if (OMIT_UNUSED_ROUTINES)
246 value = df_stripped_address_for_address(value);
247 value += code_offset/scale_factor;
249 case ARRAY_T: value += variables_offset; break;
250 case STATIC_ARRAY_T: value += static_arrays_offset; break;
255 if (compiler_error("Illegal backpatch marker"))
256 { printf("Illegal backpatch marker %d value %04x\n",
257 backpatch_marker, value);
258 backpatch_error_flag = TRUE;
263 if (bpatch_trace_setting) printf(" %04x\n", value);
268 static int32 backpatch_value_g(int32 value)
269 { /* Corrects the quantity "value" according to backpatch_marker */
274 if (bpatch_trace_setting)
275 printf("BP %s applied to %04x giving ",
276 describe_mv(backpatch_marker), value);
278 switch(backpatch_marker)
281 if (value <= 0 || value > no_strings)
282 compiler_error("Illegal string marker.");
283 value = strings_offset + compressed_offsets[value-1]; break;
285 if (OMIT_UNUSED_ROUTINES)
286 value = df_stripped_address_for_address(value);
287 value += code_offset;
290 value += arrays_offset; break;
291 case STATIC_ARRAY_MV:
292 value += static_arrays_offset; break;
294 value = variables_offset + (4*value); break;
296 value = object_tree_offset + (OBJECT_BYTE_LENGTH*(value-1));
299 if ((value<0) || (value>=VENEER_ROUTINES))
302 ("Backpatch veneer routine number out of range"))
303 { printf("Illegal BP veneer routine number: %d\n", value);
304 backpatch_error_flag = TRUE;
309 value = veneer_routine_address[value];
310 if (OMIT_UNUSED_ROUTINES)
311 value = df_stripped_address_for_address(value);
312 value += code_offset;
315 value = no_objects; break;
317 if ((value<0) || (value>=NO_SYSTEM_CONSTANTS))
320 ("Backpatch system constant number out of range"))
321 { printf("Illegal BP system constant number: %d\n", value);
322 backpatch_error_flag = TRUE;
327 value = value_of_system_constant(value); break;
329 value = dictionary_offset + 4
330 + final_dict_order[value]*DICT_ENTRY_BYTE_LENGTH;
335 valaddr = (prop_values_offset - Write_RAM_At) + value;
336 value = ReadInt32(zmachine_paged_memory + valaddr);
338 case INHERIT_INDIV_MV:
339 error("*** No individual property storage in Glulx ***");
342 value += individuals_offset;
345 value = get_symbol_index("Main");
346 if (value < 0 || (symbols[value].flags & UNKNOWN_SFLAG)) {
347 error("No 'Main' routine has been defined");
351 if (symbols[value].type != ROUTINE_T) {
352 ebf_symbol_error("'Main' routine", symbols[value].name, typename(symbols[value].type), symbols[value].line);
356 symbols[value].flags |= USED_SFLAG;
357 value = symbols[value].value;
358 if (OMIT_UNUSED_ROUTINES)
359 value = df_stripped_address_for_address(value);
360 value += code_offset;
363 if ((value<0) || (value>=no_symbols))
365 if (compiler_error("Backpatch symbol number out of range"))
366 { printf("Illegal BP symbol number: %d\n", value);
367 backpatch_error_flag = TRUE;
372 if (symbols[value].flags & UNKNOWN_SFLAG)
373 { if (!(symbols[value].flags & UERROR_SFLAG))
374 { symbols[value].flags |= UERROR_SFLAG;
375 error_named_at("No such constant as",
376 symbols[value].name, symbols[value].line);
380 if (symbols[value].flags & CHANGE_SFLAG)
381 { symbols[value].flags &= (~(CHANGE_SFLAG));
382 backpatch_marker = symbols[value].marker;
383 if ((backpatch_marker < 0)
384 || (backpatch_marker > LARGEST_BPATCH_MV))
386 compiler_error_named(
387 "Illegal backpatch marker attached to symbol",
388 symbols[value].name);
389 backpatch_error_flag = TRUE;
392 symbols[value].value = backpatch_value_g(symbols[value].value);
395 symbols[value].flags |= USED_SFLAG;
396 { int t = symbols[value].type;
397 value = symbols[value].value;
401 if (OMIT_UNUSED_ROUTINES)
402 value = df_stripped_address_for_address(value);
403 value += code_offset;
405 case ARRAY_T: value += arrays_offset; break;
406 case STATIC_ARRAY_T: value += static_arrays_offset; break;
409 value = object_tree_offset +
410 (OBJECT_BYTE_LENGTH*(value-1));
413 /* value is unchanged */
416 case INDIVIDUAL_PROPERTY_T:
418 /* value is unchanged */
421 error("*** Illegal backpatch marker in forward-declared \
428 if (compiler_error("Illegal backpatch marker"))
429 { printf("Illegal backpatch marker %d value %04x\n",
430 backpatch_marker, value);
431 backpatch_error_flag = TRUE;
436 if (bpatch_trace_setting) printf(" %04x\n", value);
441 extern int32 backpatch_value(int32 value)
444 return backpatch_value_z(value);
446 return backpatch_value_g(value);
449 static void backpatch_zmachine_z(int mv, int zmachine_area, int32 offset)
451 if (mv == OBJECT_MV) return;
452 if (mv == IDENT_MV) return;
453 if (mv == ACTION_MV) return;
455 if (bpatch_trace_setting >= 2)
456 printf("BP added: MV %d ZA %d Off %04x\n", mv, zmachine_area, offset);
458 ensure_memory_list_available(&zmachine_backpatch_table_memlist, zmachine_backpatch_size+4);
459 zmachine_backpatch_table[zmachine_backpatch_size++] = mv;
460 zmachine_backpatch_table[zmachine_backpatch_size++] = zmachine_area;
461 zmachine_backpatch_table[zmachine_backpatch_size++] = offset/256;
462 zmachine_backpatch_table[zmachine_backpatch_size++] = offset%256;
465 static void backpatch_zmachine_g(int mv, int zmachine_area, int32 offset)
467 if (mv == IDENT_MV) return;
468 if (mv == ACTION_MV) return;
470 /* The backpatch table format for Glulx:
471 First, the marker byte.
472 Then, the zmachine area being patched.
473 Then the four-byte address.
476 if (bpatch_trace_setting >= 2)
477 printf("BP added: MV %d ZA %d Off %06x\n", mv, zmachine_area, offset);
479 ensure_memory_list_available(&zmachine_backpatch_table_memlist, zmachine_backpatch_size+6);
480 zmachine_backpatch_table[zmachine_backpatch_size++] = mv;
481 zmachine_backpatch_table[zmachine_backpatch_size++] = zmachine_area;
482 zmachine_backpatch_table[zmachine_backpatch_size++] = (offset >> 24) & 0xFF;
483 zmachine_backpatch_table[zmachine_backpatch_size++] = (offset >> 16) & 0xFF;
484 zmachine_backpatch_table[zmachine_backpatch_size++] = (offset >> 8) & 0xFF;
485 zmachine_backpatch_table[zmachine_backpatch_size++] = (offset) & 0xFF;
488 extern void backpatch_zmachine(int mv, int zmachine_area, int32 offset)
491 backpatch_zmachine_z(mv, zmachine_area, offset);
493 backpatch_zmachine_g(mv, zmachine_area, offset);
496 extern void backpatch_zmachine_image_z(void)
497 { int bm = 0, zmachine_area; int32 offset, value, addr = 0;
499 backpatch_error_flag = FALSE;
500 while (bm < zmachine_backpatch_size)
502 = zmachine_backpatch_table[bm];
504 = zmachine_backpatch_table[bm+1];
506 = 256*zmachine_backpatch_table[bm+2]
507 + zmachine_backpatch_table[bm+3];
510 switch(zmachine_area)
511 { case PROP_DEFAULTS_ZA: addr = prop_defaults_offset; break;
512 case PROP_ZA: addr = prop_values_offset; break;
513 case INDIVIDUAL_PROP_ZA: addr = individuals_offset; break;
514 case DYNAMIC_ARRAY_ZA: addr = variables_offset; break;
515 case STATIC_ARRAY_ZA: addr = static_arrays_offset; break;
517 if (compiler_error("Illegal area to backpatch"))
518 backpatch_error_flag = TRUE;
522 value = 256*zmachine_paged_memory[addr]
523 + zmachine_paged_memory[addr+1];
524 value = backpatch_value_z(value);
525 zmachine_paged_memory[addr] = value/256;
526 zmachine_paged_memory[addr+1] = value%256;
528 if (backpatch_error_flag)
529 { backpatch_error_flag = FALSE;
530 printf("*** MV %d ZA %d Off %04x ***\n",
531 backpatch_marker, zmachine_area, offset);
536 extern void backpatch_zmachine_image_g(void)
537 { int bm = 0, zmachine_area; int32 offset, value, addr = 0;
539 backpatch_error_flag = FALSE;
540 while (bm < zmachine_backpatch_size)
542 = zmachine_backpatch_table[bm];
544 = zmachine_backpatch_table[bm+1];
545 offset = zmachine_backpatch_table[bm+2];
546 offset = (offset << 8) |
547 zmachine_backpatch_table[bm+3];
548 offset = (offset << 8) |
549 zmachine_backpatch_table[bm+4];
550 offset = (offset << 8) |
551 zmachine_backpatch_table[bm+5];
554 switch(zmachine_area) {
555 case PROP_DEFAULTS_ZA: addr = prop_defaults_offset+4; break;
556 case PROP_ZA: addr = prop_values_offset; break;
557 case INDIVIDUAL_PROP_ZA: addr = individuals_offset; break;
558 case DYNAMIC_ARRAY_ZA: addr = arrays_offset; break;
559 case GLOBALVAR_ZA: addr = variables_offset; break;
560 /* STATIC_ARRAY_ZA is in ROM and therefore not handled here */
562 if (compiler_error("Illegal area to backpatch"))
563 backpatch_error_flag = TRUE;
565 addr = addr + offset - Write_RAM_At;
567 value = (zmachine_paged_memory[addr] << 24)
568 | (zmachine_paged_memory[addr+1] << 16)
569 | (zmachine_paged_memory[addr+2] << 8)
570 | (zmachine_paged_memory[addr+3]);
571 value = backpatch_value_g(value);
572 zmachine_paged_memory[addr] = (value >> 24) & 0xFF;
573 zmachine_paged_memory[addr+1] = (value >> 16) & 0xFF;
574 zmachine_paged_memory[addr+2] = (value >> 8) & 0xFF;
575 zmachine_paged_memory[addr+3] = (value) & 0xFF;
577 if (backpatch_error_flag)
578 { backpatch_error_flag = FALSE;
579 printf("*** MV %d ZA %d Off %04x ***\n",
580 backpatch_marker, zmachine_area, offset);
585 /* ========================================================================= */
586 /* Data structure management routines */
587 /* ------------------------------------------------------------------------- */
589 extern void init_bpatch_vars(void)
590 { zcode_backpatch_table = NULL;
591 staticarray_backpatch_table = NULL;
592 zmachine_backpatch_table = NULL;
595 extern void bpatch_begin_pass(void)
596 { zcode_backpatch_size = 0;
597 staticarray_backpatch_size = 0;
598 zmachine_backpatch_size = 0;
601 extern void bpatch_allocate_arrays(void)
603 initialise_memory_list(&zcode_backpatch_table_memlist,
604 sizeof(uchar), 128, (void**)&zcode_backpatch_table,
605 "code backpatch table");
606 initialise_memory_list(&staticarray_backpatch_table_memlist,
607 sizeof(uchar), 128, (void**)&staticarray_backpatch_table,
608 "static array backpatch table");
609 initialise_memory_list(&zmachine_backpatch_table_memlist,
610 sizeof(uchar), 128, (void**)&zmachine_backpatch_table,
611 "machine backpatch table");
614 extern void bpatch_free_arrays(void)
615 { deallocate_memory_list(&zcode_backpatch_table_memlist);
616 deallocate_memory_list(&staticarray_backpatch_table_memlist);
617 deallocate_memory_list(&zmachine_backpatch_table_memlist);
620 /* ========================================================================= */