c1bd1bbb5c48e77417ec39481771775c416f8ccd
[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.41                                                     */
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 /*   Marker values                                                           */
36 /* ------------------------------------------------------------------------- */
37
38 extern char *describe_mv(int mval)
39 {   switch(mval)
40     {   case NULL_MV:       return("null");
41
42         /*  Marker values used in ordinary story file backpatching  */
43
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");
56
57         /*  Additional marker values used in Glulx backpatching
58             (IDENT_MV is not really used at all any more) */
59
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");
64
65     }
66     return("** No such MV **");
67 }
68
69 /* ------------------------------------------------------------------------- */
70 /*   The mending operation                                                   */
71 /* ------------------------------------------------------------------------- */
72
73 int backpatch_marker, backpatch_size, backpatch_error_flag;
74
75 static int32 backpatch_value_z(int32 value)
76 {   /*  Corrects the quantity "value" according to backpatch_marker  */
77
78     ASSERT_ZCODE();
79
80     if (bpatch_trace_setting)
81         printf("BP %s applied to %04x giving ",
82             describe_mv(backpatch_marker), value);
83
84     switch(backpatch_marker)
85     {   case STRING_MV:
86             value += strings_offset/scale_factor; break;
87         case ARRAY_MV:
88             value += variables_offset; break;
89         case STATIC_ARRAY_MV:
90             value += static_arrays_offset; break;
91         case IROUTINE_MV:
92             if (OMIT_UNUSED_ROUTINES)
93                 value = df_stripped_address_for_address(value);
94             value += code_offset/scale_factor;
95             break;
96         case VROUTINE_MV:
97             if ((value<0) || (value>=VENEER_ROUTINES))
98             {
99                 if (compiler_error
100                     ("Backpatch veneer routine number out of range"))
101                 {   printf("Illegal BP veneer routine number: %d\n", value);
102                     backpatch_error_flag = TRUE;
103                 }
104                 value = 0;
105                 break;
106             }
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;
111             break;
112         case NO_OBJS_MV:
113             value = no_objects; break;
114         case INCON_MV:
115             if ((value<0) || (value>=NO_SYSTEM_CONSTANTS))
116             {
117                 if (compiler_error
118                     ("Backpatch system constant number out of range"))
119                 {   printf("Illegal BP system constant number: %d\n", value);
120                     backpatch_error_flag = TRUE;
121                 }
122                 value = 0;
123                 break;
124             }
125             value = value_of_system_constant(value); break;
126         case DWORD_MV:
127             value = dictionary_offset + 7 +
128                     final_dict_order[value]*(DICT_ENTRY_BYTE_LENGTH);
129             break;
130         case ACTION_MV:
131             break;
132         case INHERIT_MV:
133             value = 256*zmachine_paged_memory[value + prop_values_offset]
134                     + zmachine_paged_memory[value + prop_values_offset + 1];
135             break;
136         case INHERIT_INDIV_MV:
137             value = 256*zmachine_paged_memory[value
138                         + individuals_offset]
139                     + zmachine_paged_memory[value
140                         + individuals_offset + 1];
141             break;
142         case INDIVPT_MV:
143             value += individuals_offset;
144             break;
145         case MAIN_MV:
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;
154             break;
155         case SYMBOL_MV:
156             if ((value<0) || (value>=no_symbols))
157             {
158                 if (compiler_error("Backpatch symbol number out of range"))
159                 {   printf("Illegal BP symbol number: %d\n", value);
160                     backpatch_error_flag = TRUE;
161                 }
162                 value = 0;
163                 break;
164             }
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);
170                 }
171             }
172             else
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))
178                 {
179                     compiler_error_named(
180                         "Illegal backpatch marker attached to symbol",
181                         symbols[value].name);
182                     backpatch_error_flag = TRUE;
183                 }
184                 else
185                     symbols[value].value = backpatch_value_z((symbols[value].value) % 0x10000);
186             }
187
188             symbols[value].flags |= USED_SFLAG;
189             {   int t = symbols[value].type;
190                 value = symbols[value].value;
191                 switch(t)
192                 {   case ROUTINE_T: 
193                         if (OMIT_UNUSED_ROUTINES)
194                             value = df_stripped_address_for_address(value);
195                         value += code_offset/scale_factor; 
196                         break;
197                     case ARRAY_T: value += variables_offset; break;
198                     case STATIC_ARRAY_T: value += static_arrays_offset; break;
199                 }
200             }
201             break;
202         default:
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;
207             }
208             break;
209     }
210
211     if (bpatch_trace_setting) printf(" %04x\n", value);
212
213     return(value);
214 }
215
216 static int32 backpatch_value_g(int32 value)
217 {   /*  Corrects the quantity "value" according to backpatch_marker  */
218     int32 valaddr;
219
220     ASSERT_GLULX();
221
222     if (bpatch_trace_setting)
223         printf("BP %s applied to %04x giving ",
224             describe_mv(backpatch_marker), value);
225
226     switch(backpatch_marker)
227     {
228         case STRING_MV:
229             if (value <= 0 || value > no_strings)
230               compiler_error("Illegal string marker.");
231             value = strings_offset + compressed_offsets[value-1]; break;
232         case IROUTINE_MV:
233             if (OMIT_UNUSED_ROUTINES)
234                 value = df_stripped_address_for_address(value);
235             value += code_offset;
236             break;
237         case ARRAY_MV:
238             value += arrays_offset; break;
239         case STATIC_ARRAY_MV:
240             value += static_arrays_offset; break;
241         case VARIABLE_MV:
242             value = variables_offset + (4*value); break;
243         case OBJECT_MV:
244             value = object_tree_offset + (OBJECT_BYTE_LENGTH*(value-1)); 
245             break;
246         case VROUTINE_MV:
247             if ((value<0) || (value>=VENEER_ROUTINES))
248             {
249                 if (compiler_error
250                     ("Backpatch veneer routine number out of range"))
251                 {   printf("Illegal BP veneer routine number: %d\n", value);
252                     backpatch_error_flag = TRUE;
253                 }
254                 value = 0;
255                 break;
256             }
257             value = veneer_routine_address[value];
258             if (OMIT_UNUSED_ROUTINES)
259                 value = df_stripped_address_for_address(value);
260             value += code_offset;
261             break;
262         case NO_OBJS_MV:
263             value = no_objects; break;
264         case INCON_MV:
265             if ((value<0) || (value>=NO_SYSTEM_CONSTANTS))
266             {
267                 if (compiler_error
268                     ("Backpatch system constant number out of range"))
269                 {   printf("Illegal BP system constant number: %d\n", value);
270                     backpatch_error_flag = TRUE;
271                 }
272                 value = 0;
273                 break;
274             }
275             value = value_of_system_constant(value); break;
276         case DWORD_MV:
277             value = dictionary_offset + 4 
278               + final_dict_order[value]*DICT_ENTRY_BYTE_LENGTH;
279             break;
280         case ACTION_MV:
281             break;
282         case INHERIT_MV:
283             valaddr = (prop_values_offset - Write_RAM_At) + value;
284             value = ReadInt32(zmachine_paged_memory + valaddr);
285             break;
286         case INHERIT_INDIV_MV:
287             error("*** No individual property storage in Glulx ***");
288             break;
289         case INDIVPT_MV:
290             value += individuals_offset;
291             break;
292         case MAIN_MV:
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;
301             break;
302         case SYMBOL_MV:
303             if ((value<0) || (value>=no_symbols))
304             {
305                 if (compiler_error("Backpatch symbol number out of range"))
306                 {   printf("Illegal BP symbol number: %d\n", value);
307                     backpatch_error_flag = TRUE;
308                 }
309                 value = 0;
310                 break;
311             }
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);
317                 }
318             }
319             else
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))
325                 {
326                     compiler_error_named(
327                         "Illegal backpatch marker attached to symbol",
328                         symbols[value].name);
329                     backpatch_error_flag = TRUE;
330                 }
331                 else
332                     symbols[value].value = backpatch_value_g(symbols[value].value);
333             }
334
335             symbols[value].flags |= USED_SFLAG;
336             {   int t = symbols[value].type;
337                 value = symbols[value].value;
338                 switch(t)
339                 {
340                     case ROUTINE_T:
341                         if (OMIT_UNUSED_ROUTINES)
342                             value = df_stripped_address_for_address(value);
343                         value += code_offset;
344                         break;
345                     case ARRAY_T: value += arrays_offset; break;
346                     case STATIC_ARRAY_T: value += static_arrays_offset; break;
347                     case OBJECT_T:
348                     case CLASS_T:
349                       value = object_tree_offset + 
350                         (OBJECT_BYTE_LENGTH*(value-1)); 
351                       break;
352                     case ATTRIBUTE_T:
353                       /* value is unchanged */
354                       break;
355                     case CONSTANT_T:
356                     case INDIVIDUAL_PROPERTY_T:
357                     case PROPERTY_T:
358                       /* value is unchanged */
359                       break;
360                     default:
361                       error("*** Illegal backpatch marker in forward-declared \
362 symbol");
363                       break;
364                 }
365             }
366             break;
367         default:
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;
372             }
373             break;
374     }
375
376     if (bpatch_trace_setting) printf(" %04x\n", value);
377
378     return(value);
379 }
380
381 extern int32 backpatch_value(int32 value)
382 {
383   if (!glulx_mode)
384     return backpatch_value_z(value);
385   else
386     return backpatch_value_g(value);
387 }
388
389 static void backpatch_zmachine_z(int mv, int zmachine_area, int32 offset)
390 {   
391     if (mv == OBJECT_MV) return;
392     if (mv == IDENT_MV) return;
393     if (mv == ACTION_MV) return;
394
395     if (bpatch_trace_setting >= 2)
396         printf("BP added: MV %d ZA %d Off %04x\n", mv, zmachine_area, offset);
397
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;
403 }
404
405 static void backpatch_zmachine_g(int mv, int zmachine_area, int32 offset)
406 {   
407     if (mv == IDENT_MV) return;
408     if (mv == ACTION_MV) return;
409
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.
414 */
415
416     if (bpatch_trace_setting >= 2)
417         printf("BP added: MV %d ZA %d Off %06x\n", mv, zmachine_area, offset);
418
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;
426 }
427
428 extern void backpatch_zmachine(int mv, int zmachine_area, int32 offset)
429 {
430   if (!glulx_mode)
431     backpatch_zmachine_z(mv, zmachine_area, offset);
432   else
433     backpatch_zmachine_g(mv, zmachine_area, offset);
434 }
435
436 extern void backpatch_zmachine_image_z(void)
437 {   int bm = 0, zmachine_area; int32 offset, value, addr = 0;
438     ASSERT_ZCODE();
439     backpatch_error_flag = FALSE;
440     while (bm < zmachine_backpatch_size)
441     {   backpatch_marker
442             = zmachine_backpatch_table[bm];
443         zmachine_area
444             = zmachine_backpatch_table[bm+1];
445         offset
446           = 256*zmachine_backpatch_table[bm+2]
447             + zmachine_backpatch_table[bm+3];
448         bm += 4;
449
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;
456             default:
457                 if (compiler_error("Illegal area to backpatch"))
458                     backpatch_error_flag = TRUE;
459         }
460         addr += offset;
461
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;
467
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);
472         }
473     }
474 }
475
476 extern void backpatch_zmachine_image_g(void)
477 {   int bm = 0, zmachine_area; int32 offset, value, addr = 0;
478     ASSERT_GLULX();
479     backpatch_error_flag = FALSE;
480     while (bm < zmachine_backpatch_size)
481     {   backpatch_marker
482             = zmachine_backpatch_table[bm];
483         zmachine_area
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];
492         bm += 6;
493
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 */
501         default:
502             if (compiler_error("Illegal area to backpatch"))
503               backpatch_error_flag = TRUE;
504         }
505         addr = addr + offset - Write_RAM_At;
506
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;
516
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);
521         }
522     }
523 }
524
525 /* ========================================================================= */
526 /*   Data structure management routines                                      */
527 /* ------------------------------------------------------------------------- */
528
529 extern void init_bpatch_vars(void)
530 {   zcode_backpatch_table = NULL;
531     staticarray_backpatch_table = NULL;
532     zmachine_backpatch_table = NULL;
533 }
534
535 extern void bpatch_begin_pass(void)
536 {   zcode_backpatch_size = 0;
537     staticarray_backpatch_size = 0;
538     zmachine_backpatch_size = 0;
539 }
540
541 extern void bpatch_allocate_arrays(void)
542 {
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");
552 }
553
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);
558 }
559
560 /* ========================================================================= */