1fd984b0c9b8ebf8c51a25b8287afca0f9a12a83
[inform.git] / src / bpatch.c
1 /* ------------------------------------------------------------------------- */
2 /*   "bpatch" : Keeps track of, and finally acts on, backpatch markers,      */
3 /*              correcting symbol values not known at compilation time       */
4 /*                                                                           */
5 /*   Part of Inform 6.40                                                     */
6 /*   copyright (c) Graham Nelson 1993 - 2022                                 */
7 /*                                                                           */
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.                                       */
12 /*                                                                           */
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.                              */
17 /*                                                                           */
18 /* You should have received a copy of the GNU General Public License         */
19 /* along with Inform. If not, see https://gnu.org/licenses/                  */
20 /*                                                                           */
21 /* ------------------------------------------------------------------------- */
22
23 #include "header.h"
24
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;
33
34 /* ------------------------------------------------------------------------- */
35 /*   The mending operation                                                   */
36 /* ------------------------------------------------------------------------- */
37
38 int backpatch_marker, backpatch_size, backpatch_error_flag;
39
40 static int32 backpatch_value_z(int32 value)
41 {   /*  Corrects the quantity "value" according to backpatch_marker  */
42
43     ASSERT_ZCODE();
44
45     if (bpatch_trace_setting)
46         printf("BP %s applied to %04x giving ",
47             describe_mv(backpatch_marker), value);
48
49     switch(backpatch_marker)
50     {   case STRING_MV:
51             value += strings_offset/scale_factor; break;
52         case ARRAY_MV:
53             value += variables_offset; break;
54         case STATIC_ARRAY_MV:
55             value += static_arrays_offset; break;
56         case IROUTINE_MV:
57             if (OMIT_UNUSED_ROUTINES)
58                 value = df_stripped_address_for_address(value);
59             value += code_offset/scale_factor;
60             break;
61         case VROUTINE_MV:
62             if ((value<0) || (value>=VENEER_ROUTINES))
63             {   if (no_link_errors > 0) break;
64                 if (compiler_error
65                     ("Backpatch veneer routine number out of range"))
66                 {   printf("Illegal BP veneer routine number: %d\n", value);
67                     backpatch_error_flag = TRUE;
68                 }
69                 value = 0;
70                 break;
71             }
72             value = veneer_routine_address[value]; 
73             if (OMIT_UNUSED_ROUTINES)
74                 value = df_stripped_address_for_address(value);
75             value += code_offset/scale_factor;
76             break;
77         case NO_OBJS_MV:
78             value = no_objects; break;
79         case INCON_MV:
80             if ((value<0) || (value>=NO_SYSTEM_CONSTANTS))
81             {   if (no_link_errors > 0) break;
82                 if (compiler_error
83                     ("Backpatch system constant number out of range"))
84                 {   printf("Illegal BP system constant number: %d\n", value);
85                     backpatch_error_flag = TRUE;
86                 }
87                 value = 0;
88                 break;
89             }
90             value = value_of_system_constant(value); break;
91         case DWORD_MV:
92             value = dictionary_offset + 7 +
93                     final_dict_order[value]*(DICT_ENTRY_BYTE_LENGTH);
94             break;
95         case ACTION_MV:
96             break;
97         case INHERIT_MV:
98             value = 256*zmachine_paged_memory[value + prop_values_offset]
99                     + zmachine_paged_memory[value + prop_values_offset + 1];
100             break;
101         case INHERIT_INDIV_MV:
102             value = 256*zmachine_paged_memory[value
103                         + individuals_offset]
104                     + zmachine_paged_memory[value
105                         + individuals_offset + 1];
106             break;
107         case INDIVPT_MV:
108             value += individuals_offset;
109             break;
110         case MAIN_MV:
111             value = symbol_index("Main", -1);
112             if (symbols[value].type != ROUTINE_T)
113                 error("No 'Main' routine has been defined");
114             symbols[value].flags |= USED_SFLAG;
115             value = symbols[value].value;
116             if (OMIT_UNUSED_ROUTINES)
117                 value = df_stripped_address_for_address(value);
118             value += code_offset/scale_factor;
119             break;
120         case SYMBOL_MV:
121             if ((value<0) || (value>=no_symbols))
122             {   if (no_link_errors > 0) break;
123                 if (compiler_error("Backpatch symbol number out of range"))
124                 {   printf("Illegal BP symbol number: %d\n", value);
125                     backpatch_error_flag = TRUE;
126                 }
127                 value = 0;
128                 break;
129             }
130             if (symbols[value].flags & UNKNOWN_SFLAG)
131             {   if (!(symbols[value].flags & UERROR_SFLAG))
132                 {   symbols[value].flags |= UERROR_SFLAG;
133                     error_named_at("No such constant as",
134                         symbols[value].name, symbols[value].line);
135                 }
136             }
137             else
138             if (symbols[value].flags & CHANGE_SFLAG)
139             {   symbols[value].flags &= (~(CHANGE_SFLAG));
140                 backpatch_marker = (symbols[value].marker);
141                 if ((backpatch_marker < 0)
142                     || (backpatch_marker > LARGEST_BPATCH_MV))
143                 {
144                     if (no_link_errors == 0)
145                     {   compiler_error_named(
146                         "Illegal backpatch marker attached to symbol",
147                         symbols[value].name);
148                         backpatch_error_flag = TRUE;
149                     }
150                 }
151                 else
152                     symbols[value].value = backpatch_value_z((symbols[value].value) % 0x10000);
153             }
154
155             symbols[value].flags |= USED_SFLAG;
156             {   int t = symbols[value].type;
157                 value = symbols[value].value;
158                 switch(t)
159                 {   case ROUTINE_T: 
160                         if (OMIT_UNUSED_ROUTINES)
161                             value = df_stripped_address_for_address(value);
162                         value += code_offset/scale_factor; 
163                         break;
164                     case ARRAY_T: value += variables_offset; break;
165                     case STATIC_ARRAY_T: value += static_arrays_offset; break;
166                 }
167             }
168             break;
169         default:
170             if (no_link_errors > 0) break;
171             if (compiler_error("Illegal backpatch marker"))
172             {   printf("Illegal backpatch marker %d value %04x\n",
173                     backpatch_marker, value);
174                 backpatch_error_flag = TRUE;
175             }
176             break;
177     }
178
179     if (bpatch_trace_setting) printf(" %04x\n", value);
180
181     return(value);
182 }
183
184 static int32 backpatch_value_g(int32 value)
185 {   /*  Corrects the quantity "value" according to backpatch_marker  */
186     int32 valaddr;
187
188     ASSERT_GLULX();
189
190     if (bpatch_trace_setting)
191         printf("BP %s applied to %04x giving ",
192             describe_mv(backpatch_marker), value);
193
194     switch(backpatch_marker)
195     {
196         case STRING_MV:
197             if (value <= 0 || value > no_strings)
198               compiler_error("Illegal string marker.");
199             value = strings_offset + compressed_offsets[value-1]; break;
200         case IROUTINE_MV:
201             if (OMIT_UNUSED_ROUTINES)
202                 value = df_stripped_address_for_address(value);
203             value += code_offset;
204             break;
205         case ARRAY_MV:
206             value += arrays_offset; break;
207         case STATIC_ARRAY_MV:
208             value += static_arrays_offset; break;
209         case VARIABLE_MV:
210             value = variables_offset + (4*value); break;
211         case OBJECT_MV:
212             value = object_tree_offset + (OBJECT_BYTE_LENGTH*(value-1)); 
213             break;
214         case VROUTINE_MV:
215             if ((value<0) || (value>=VENEER_ROUTINES))
216             {   if (no_link_errors > 0) break;
217                 if (compiler_error
218                     ("Backpatch veneer routine number out of range"))
219                 {   printf("Illegal BP veneer routine number: %d\n", value);
220                     backpatch_error_flag = TRUE;
221                 }
222                 value = 0;
223                 break;
224             }
225             value = veneer_routine_address[value];
226             if (OMIT_UNUSED_ROUTINES)
227                 value = df_stripped_address_for_address(value);
228             value += code_offset;
229             break;
230         case NO_OBJS_MV:
231             value = no_objects; break;
232         case INCON_MV:
233             if ((value<0) || (value>=NO_SYSTEM_CONSTANTS))
234             {   if (no_link_errors > 0) break;
235                 if (compiler_error
236                     ("Backpatch system constant number out of range"))
237                 {   printf("Illegal BP system constant number: %d\n", value);
238                     backpatch_error_flag = TRUE;
239                 }
240                 value = 0;
241                 break;
242             }
243             value = value_of_system_constant(value); break;
244         case DWORD_MV:
245             value = dictionary_offset + 4 
246               + final_dict_order[value]*DICT_ENTRY_BYTE_LENGTH;
247             break;
248         case ACTION_MV:
249             break;
250         case INHERIT_MV:
251             valaddr = (prop_values_offset - Write_RAM_At) + value;
252             value = ReadInt32(zmachine_paged_memory + valaddr);
253             break;
254         case INHERIT_INDIV_MV:
255             error("*** No individual property storage in Glulx ***");
256             break;
257         case INDIVPT_MV:
258             value += individuals_offset;
259             break;
260         case MAIN_MV:
261             value = symbol_index("Main", -1);
262             if (symbols[value].type != ROUTINE_T)
263                 error("No 'Main' routine has been defined");
264             symbols[value].flags |= USED_SFLAG;
265             value = symbols[value].value;
266             if (OMIT_UNUSED_ROUTINES)
267                 value = df_stripped_address_for_address(value);
268             value += code_offset;
269             break;
270         case SYMBOL_MV:
271             if ((value<0) || (value>=no_symbols))
272             {   if (no_link_errors > 0) break;
273                 if (compiler_error("Backpatch symbol number out of range"))
274                 {   printf("Illegal BP symbol number: %d\n", value);
275                     backpatch_error_flag = TRUE;
276                 }
277                 value = 0;
278                 break;
279             }
280             if (symbols[value].flags & UNKNOWN_SFLAG)
281             {   if (!(symbols[value].flags & UERROR_SFLAG))
282                 {   symbols[value].flags |= UERROR_SFLAG;
283                     error_named_at("No such constant as",
284                         symbols[value].name, symbols[value].line);
285                 }
286             }
287             else
288             if (symbols[value].flags & CHANGE_SFLAG)
289             {   symbols[value].flags &= (~(CHANGE_SFLAG));
290                 backpatch_marker = symbols[value].marker;
291                 if ((backpatch_marker < 0)
292                     || (backpatch_marker > LARGEST_BPATCH_MV))
293                 {
294                     if (no_link_errors == 0)
295                     {   compiler_error_named(
296                         "Illegal backpatch marker attached to symbol",
297                         symbols[value].name);
298                         backpatch_error_flag = TRUE;
299                     }
300                 }
301                 else
302                     symbols[value].value = backpatch_value_g(symbols[value].value);
303             }
304
305             symbols[value].flags |= USED_SFLAG;
306             {   int t = symbols[value].type;
307                 value = symbols[value].value;
308                 switch(t)
309                 {
310                     case ROUTINE_T:
311                         if (OMIT_UNUSED_ROUTINES)
312                             value = df_stripped_address_for_address(value);
313                         value += code_offset;
314                         break;
315                     case ARRAY_T: value += arrays_offset; break;
316                     case STATIC_ARRAY_T: value += static_arrays_offset; break;
317                     case OBJECT_T:
318                     case CLASS_T:
319                       value = object_tree_offset + 
320                         (OBJECT_BYTE_LENGTH*(value-1)); 
321                       break;
322                     case ATTRIBUTE_T:
323                       /* value is unchanged */
324                       break;
325                     case CONSTANT_T:
326                     case INDIVIDUAL_PROPERTY_T:
327                     case PROPERTY_T:
328                       /* value is unchanged */
329                       break;
330                     default:
331                       error("*** Illegal backpatch marker in forward-declared \
332 symbol");
333                       break;
334                 }
335             }
336             break;
337         default:
338             if (no_link_errors > 0) break;
339             if (compiler_error("Illegal backpatch marker"))
340             {   printf("Illegal backpatch marker %d value %04x\n",
341                     backpatch_marker, value);
342                 backpatch_error_flag = TRUE;
343             }
344             break;
345     }
346
347     if (bpatch_trace_setting) printf(" %04x\n", value);
348
349     return(value);
350 }
351
352 extern int32 backpatch_value(int32 value)
353 {
354   if (!glulx_mode)
355     return backpatch_value_z(value);
356   else
357     return backpatch_value_g(value);
358 }
359
360 static void backpatch_zmachine_z(int mv, int zmachine_area, int32 offset)
361 {   if (module_switch)
362     {   if (zmachine_area == PROP_DEFAULTS_ZA) return;
363     }
364     else
365     {   if (mv == OBJECT_MV) return;
366         if (mv == IDENT_MV) return;
367         if (mv == ACTION_MV) return;
368     }
369
370     if (bpatch_trace_setting >= 2)
371         printf("BP added: MV %d ZA %d Off %04x\n", mv, zmachine_area, offset);
372
373     ensure_memory_list_available(&zmachine_backpatch_table_memlist, zmachine_backpatch_size+4);
374     zmachine_backpatch_table[zmachine_backpatch_size++] = mv;
375     zmachine_backpatch_table[zmachine_backpatch_size++] = zmachine_area;
376     zmachine_backpatch_table[zmachine_backpatch_size++] = offset/256;
377     zmachine_backpatch_table[zmachine_backpatch_size++] = offset%256;
378 }
379
380 static void backpatch_zmachine_g(int mv, int zmachine_area, int32 offset)
381 {   if (module_switch)
382     {   if (zmachine_area == PROP_DEFAULTS_ZA) return;
383     }
384     else
385     {   if (mv == IDENT_MV) return;
386         if (mv == ACTION_MV) return;
387     }
388
389 /* The backpatch table format for Glulx:
390    First, the marker byte.
391    Then, the zmachine area being patched.
392    Then the four-byte address.
393 */
394
395     if (bpatch_trace_setting >= 2)
396         printf("BP added: MV %d ZA %d Off %06x\n", mv, zmachine_area, offset);
397
398     ensure_memory_list_available(&zmachine_backpatch_table_memlist, zmachine_backpatch_size+6);
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 >> 24) & 0xFF;
402     zmachine_backpatch_table[zmachine_backpatch_size++] = (offset >> 16) & 0xFF;
403     zmachine_backpatch_table[zmachine_backpatch_size++] = (offset >> 8) & 0xFF;
404     zmachine_backpatch_table[zmachine_backpatch_size++] = (offset) & 0xFF;
405 }
406
407 extern void backpatch_zmachine(int mv, int zmachine_area, int32 offset)
408 {
409   if (!glulx_mode)
410     backpatch_zmachine_z(mv, zmachine_area, offset);
411   else
412     backpatch_zmachine_g(mv, zmachine_area, offset);
413 }
414
415 extern void backpatch_zmachine_image_z(void)
416 {   int bm = 0, zmachine_area; int32 offset, value, addr = 0;
417     ASSERT_ZCODE();
418     backpatch_error_flag = FALSE;
419     while (bm < zmachine_backpatch_size)
420     {   backpatch_marker
421             = zmachine_backpatch_table[bm];
422         zmachine_area
423             = zmachine_backpatch_table[bm+1];
424         offset
425           = 256*zmachine_backpatch_table[bm+2]
426             + zmachine_backpatch_table[bm+3];
427         bm += 4;
428
429         switch(zmachine_area)
430         {   case PROP_DEFAULTS_ZA:   addr = prop_defaults_offset; break;
431             case PROP_ZA:            addr = prop_values_offset; break;
432             case INDIVIDUAL_PROP_ZA: addr = individuals_offset; break;
433             case DYNAMIC_ARRAY_ZA:   addr = variables_offset; break;
434             case STATIC_ARRAY_ZA:    addr = static_arrays_offset; break;
435             default:
436                 if (no_link_errors == 0)
437                     if (compiler_error("Illegal area to backpatch"))
438                         backpatch_error_flag = TRUE;
439         }
440         addr += offset;
441
442         value = 256*zmachine_paged_memory[addr]
443                 + zmachine_paged_memory[addr+1];
444         value = backpatch_value_z(value);
445         zmachine_paged_memory[addr] = value/256;
446         zmachine_paged_memory[addr+1] = value%256;
447
448         if (backpatch_error_flag)
449         {   backpatch_error_flag = FALSE;
450             if (no_link_errors == 0)
451                 printf("*** MV %d ZA %d Off %04x ***\n",
452                     backpatch_marker, zmachine_area, offset);
453         }
454     }
455 }
456
457 extern void backpatch_zmachine_image_g(void)
458 {   int bm = 0, zmachine_area; int32 offset, value, addr = 0;
459     ASSERT_GLULX();
460     backpatch_error_flag = FALSE;
461     while (bm < zmachine_backpatch_size)
462     {   backpatch_marker
463             = zmachine_backpatch_table[bm];
464         zmachine_area
465             = zmachine_backpatch_table[bm+1];
466         offset = zmachine_backpatch_table[bm+2];
467         offset = (offset << 8) |
468           zmachine_backpatch_table[bm+3];
469         offset = (offset << 8) |
470           zmachine_backpatch_table[bm+4];
471         offset = (offset << 8) |
472           zmachine_backpatch_table[bm+5];
473         bm += 6;
474
475             switch(zmachine_area) {   
476         case PROP_DEFAULTS_ZA:   addr = prop_defaults_offset+4; break;
477         case PROP_ZA:            addr = prop_values_offset; break;
478         case INDIVIDUAL_PROP_ZA: addr = individuals_offset; break;
479         case DYNAMIC_ARRAY_ZA:   addr = arrays_offset; break;
480         case GLOBALVAR_ZA:       addr = variables_offset; break;
481         /* STATIC_ARRAY_ZA is in ROM and therefore not handled here */
482         default:
483           if (no_link_errors == 0)
484             if (compiler_error("Illegal area to backpatch"))
485               backpatch_error_flag = TRUE;
486         }
487         addr = addr + offset - Write_RAM_At;
488
489         value = (zmachine_paged_memory[addr] << 24)
490                 | (zmachine_paged_memory[addr+1] << 16)
491                 | (zmachine_paged_memory[addr+2] << 8)
492                 | (zmachine_paged_memory[addr+3]);
493         value = backpatch_value_g(value);
494         zmachine_paged_memory[addr] = (value >> 24) & 0xFF;
495         zmachine_paged_memory[addr+1] = (value >> 16) & 0xFF;
496         zmachine_paged_memory[addr+2] = (value >> 8) & 0xFF;
497         zmachine_paged_memory[addr+3] = (value) & 0xFF;
498
499         if (backpatch_error_flag)
500         {   backpatch_error_flag = FALSE;
501             if (no_link_errors == 0)
502                 printf("*** MV %d ZA %d Off %04x ***\n",
503                     backpatch_marker, zmachine_area, offset);
504         }
505     }
506 }
507
508 /* ========================================================================= */
509 /*   Data structure management routines                                      */
510 /* ------------------------------------------------------------------------- */
511
512 extern void init_bpatch_vars(void)
513 {   zcode_backpatch_table = NULL;
514     staticarray_backpatch_table = NULL;
515     zmachine_backpatch_table = NULL;
516 }
517
518 extern void bpatch_begin_pass(void)
519 {   zcode_backpatch_size = 0;
520     staticarray_backpatch_size = 0;
521     zmachine_backpatch_size = 0;
522 }
523
524 extern void bpatch_allocate_arrays(void)
525 {
526     initialise_memory_list(&zcode_backpatch_table_memlist,
527         sizeof(uchar), 128, (void**)&zcode_backpatch_table,
528         "code backpatch table");
529     initialise_memory_list(&staticarray_backpatch_table_memlist,
530         sizeof(uchar), 128, (void**)&staticarray_backpatch_table,
531         "static array backpatch table");
532     initialise_memory_list(&zmachine_backpatch_table_memlist,
533         sizeof(uchar), 128, (void**)&zmachine_backpatch_table,
534         "machine backpatch table");
535 }
536
537 extern void bpatch_free_arrays(void)
538 {   deallocate_memory_list(&zcode_backpatch_table_memlist);
539     deallocate_memory_list(&staticarray_backpatch_table_memlist);
540     deallocate_memory_list(&zmachine_backpatch_table_memlist);
541 }
542
543 /* ========================================================================= */