Update to Inform v6.42
[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.42                                                     */
6 /*   copyright (c) Graham Nelson 1993 - 2024                                 */
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 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");
57
58         /*  Additional marker values used in Glulx backpatching
59             (IDENT_MV is not really used at all any more) */
60
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");
65
66         /* Only occurs secondary to another reported error */
67         case ERROR_MV:      return("error");
68
69     }
70     return("** No such MV **");
71 }
72
73 extern char *describe_mv_short(int mval)
74 {   switch(mval)
75     {   case NULL_MV:       return("");
76
77         /*  Marker values used in ordinary story file backpatching  */
78
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");
92
93         /*  Additional marker values used in Glulx backpatching
94             (IDENT_MV is not really used at all any more) */
95
96         case VARIABLE_MV:   return("glob");
97         case IDENT_MV:      return("prop");
98         case ACTION_MV:     return("action");
99         case OBJECT_MV:     return("obj");
100
101         case LABEL_MV:      return("lbl");
102         case DELETED_MV:    return("del");
103
104         /* Only occurs secondary to another reported error */
105         case ERROR_MV:      return("err");
106
107     }
108     if (mval >= BRANCH_MV && mval < BRANCHMAX_MV) return "br";
109     
110     return("???");
111 }
112
113 /* ------------------------------------------------------------------------- */
114 /*   The mending operation                                                   */
115 /* ------------------------------------------------------------------------- */
116
117 int backpatch_marker, backpatch_size, backpatch_error_flag;
118
119 static int32 backpatch_value_z(int32 value)
120 {   /*  Corrects the quantity "value" according to backpatch_marker  */
121
122     ASSERT_ZCODE();
123
124     if (bpatch_trace_setting)
125         printf("BP %s applied to %04x giving ",
126             describe_mv(backpatch_marker), value);
127
128     switch(backpatch_marker)
129     {   case STRING_MV:
130             value += strings_offset/scale_factor; break;
131         case ARRAY_MV:
132             value += variables_offset; break;
133         case STATIC_ARRAY_MV:
134             value += static_arrays_offset; break;
135         case IROUTINE_MV:
136             if (OMIT_UNUSED_ROUTINES)
137                 value = df_stripped_address_for_address(value);
138             value += code_offset/scale_factor;
139             break;
140         case VROUTINE_MV:
141             if ((value<0) || (value>=VENEER_ROUTINES))
142             {
143                 if (compiler_error
144                     ("Backpatch veneer routine number out of range"))
145                 {   printf("Illegal BP veneer routine number: %d\n", value);
146                     backpatch_error_flag = TRUE;
147                 }
148                 value = 0;
149                 break;
150             }
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;
155             break;
156         case NO_OBJS_MV:
157             value = no_objects; break;
158         case INCON_MV:
159             if ((value<0) || (value>=NO_SYSTEM_CONSTANTS))
160             {
161                 if (compiler_error
162                     ("Backpatch system constant number out of range"))
163                 {   printf("Illegal BP system constant number: %d\n", value);
164                     backpatch_error_flag = TRUE;
165                 }
166                 value = 0;
167                 break;
168             }
169             value = value_of_system_constant(value); break;
170         case DWORD_MV:
171             value = dictionary_offset + 7 +
172                     final_dict_order[value]*(DICT_ENTRY_BYTE_LENGTH);
173             break;
174         case ACTION_MV:
175             break;
176         case INHERIT_MV:
177             value = 256*zmachine_paged_memory[value + prop_values_offset]
178                     + zmachine_paged_memory[value + prop_values_offset + 1];
179             break;
180         case INHERIT_INDIV_MV:
181             value = 256*zmachine_paged_memory[value
182                         + individuals_offset]
183                     + zmachine_paged_memory[value
184                         + individuals_offset + 1];
185             break;
186         case INDIVPT_MV:
187             value += individuals_offset;
188             break;
189         case MAIN_MV:
190             value = get_symbol_index("Main");
191             if (value < 0 || (symbols[value].flags & UNKNOWN_SFLAG)) {
192                 error("No 'Main' routine has been defined");
193                 value = 0;
194                 break;
195             }
196             if (symbols[value].type != ROUTINE_T) {
197                 ebf_symbol_error("'Main' routine", symbols[value].name, typename(symbols[value].type), symbols[value].line);
198                 value = 0;
199                 break;
200             }
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;
206             break;
207         case SYMBOL_MV:
208             if ((value<0) || (value>=no_symbols))
209             {
210                 if (compiler_error("Backpatch symbol number out of range"))
211                 {   printf("Illegal BP symbol number: %d\n", value);
212                     backpatch_error_flag = TRUE;
213                 }
214                 value = 0;
215                 break;
216             }
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);
222                 }
223             }
224             else
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))
230                 {
231                     compiler_error_named(
232                         "Illegal backpatch marker attached to symbol",
233                         symbols[value].name);
234                     backpatch_error_flag = TRUE;
235                 }
236                 else
237                     symbols[value].value = backpatch_value_z((symbols[value].value) % 0x10000);
238             }
239
240             symbols[value].flags |= USED_SFLAG;
241             {   int t = symbols[value].type;
242                 value = symbols[value].value;
243                 switch(t)
244                 {   case ROUTINE_T: 
245                         if (OMIT_UNUSED_ROUTINES)
246                             value = df_stripped_address_for_address(value);
247                         value += code_offset/scale_factor; 
248                         break;
249                     case ARRAY_T: value += variables_offset; break;
250                     case STATIC_ARRAY_T: value += static_arrays_offset; break;
251                 }
252             }
253             break;
254         default:
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;
259             }
260             break;
261     }
262
263     if (bpatch_trace_setting) printf(" %04x\n", value);
264
265     return(value);
266 }
267
268 static int32 backpatch_value_g(int32 value)
269 {   /*  Corrects the quantity "value" according to backpatch_marker  */
270     int32 valaddr;
271
272     ASSERT_GLULX();
273
274     if (bpatch_trace_setting)
275         printf("BP %s applied to %04x giving ",
276             describe_mv(backpatch_marker), value);
277
278     switch(backpatch_marker)
279     {
280         case STRING_MV:
281             if (value <= 0 || value > no_strings)
282               compiler_error("Illegal string marker.");
283             value = strings_offset + compressed_offsets[value-1]; break;
284         case IROUTINE_MV:
285             if (OMIT_UNUSED_ROUTINES)
286                 value = df_stripped_address_for_address(value);
287             value += code_offset;
288             break;
289         case ARRAY_MV:
290             value += arrays_offset; break;
291         case STATIC_ARRAY_MV:
292             value += static_arrays_offset; break;
293         case VARIABLE_MV:
294             value = variables_offset + (4*value); break;
295         case OBJECT_MV:
296             value = object_tree_offset + (OBJECT_BYTE_LENGTH*(value-1)); 
297             break;
298         case VROUTINE_MV:
299             if ((value<0) || (value>=VENEER_ROUTINES))
300             {
301                 if (compiler_error
302                     ("Backpatch veneer routine number out of range"))
303                 {   printf("Illegal BP veneer routine number: %d\n", value);
304                     backpatch_error_flag = TRUE;
305                 }
306                 value = 0;
307                 break;
308             }
309             value = veneer_routine_address[value];
310             if (OMIT_UNUSED_ROUTINES)
311                 value = df_stripped_address_for_address(value);
312             value += code_offset;
313             break;
314         case NO_OBJS_MV:
315             value = no_objects; break;
316         case INCON_MV:
317             if ((value<0) || (value>=NO_SYSTEM_CONSTANTS))
318             {
319                 if (compiler_error
320                     ("Backpatch system constant number out of range"))
321                 {   printf("Illegal BP system constant number: %d\n", value);
322                     backpatch_error_flag = TRUE;
323                 }
324                 value = 0;
325                 break;
326             }
327             value = value_of_system_constant(value); break;
328         case DWORD_MV:
329             value = dictionary_offset + 4 
330               + final_dict_order[value]*DICT_ENTRY_BYTE_LENGTH;
331             break;
332         case ACTION_MV:
333             break;
334         case INHERIT_MV:
335             valaddr = (prop_values_offset - Write_RAM_At) + value;
336             value = ReadInt32(zmachine_paged_memory + valaddr);
337             break;
338         case INHERIT_INDIV_MV:
339             error("*** No individual property storage in Glulx ***");
340             break;
341         case INDIVPT_MV:
342             value += individuals_offset;
343             break;
344         case MAIN_MV:
345             value = get_symbol_index("Main");
346             if (value < 0 || (symbols[value].flags & UNKNOWN_SFLAG)) {
347                 error("No 'Main' routine has been defined");
348                 value = 0;
349                 break;
350             }
351             if (symbols[value].type != ROUTINE_T) {
352                 ebf_symbol_error("'Main' routine", symbols[value].name, typename(symbols[value].type), symbols[value].line);
353                 value = 0;
354                 break;
355             }
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;
361             break;
362         case SYMBOL_MV:
363             if ((value<0) || (value>=no_symbols))
364             {
365                 if (compiler_error("Backpatch symbol number out of range"))
366                 {   printf("Illegal BP symbol number: %d\n", value);
367                     backpatch_error_flag = TRUE;
368                 }
369                 value = 0;
370                 break;
371             }
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);
377                 }
378             }
379             else
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))
385                 {
386                     compiler_error_named(
387                         "Illegal backpatch marker attached to symbol",
388                         symbols[value].name);
389                     backpatch_error_flag = TRUE;
390                 }
391                 else
392                     symbols[value].value = backpatch_value_g(symbols[value].value);
393             }
394
395             symbols[value].flags |= USED_SFLAG;
396             {   int t = symbols[value].type;
397                 value = symbols[value].value;
398                 switch(t)
399                 {
400                     case ROUTINE_T:
401                         if (OMIT_UNUSED_ROUTINES)
402                             value = df_stripped_address_for_address(value);
403                         value += code_offset;
404                         break;
405                     case ARRAY_T: value += arrays_offset; break;
406                     case STATIC_ARRAY_T: value += static_arrays_offset; break;
407                     case OBJECT_T:
408                     case CLASS_T:
409                       value = object_tree_offset + 
410                         (OBJECT_BYTE_LENGTH*(value-1)); 
411                       break;
412                     case ATTRIBUTE_T:
413                       /* value is unchanged */
414                       break;
415                     case CONSTANT_T:
416                     case INDIVIDUAL_PROPERTY_T:
417                     case PROPERTY_T:
418                       /* value is unchanged */
419                       break;
420                     default:
421                       error("*** Illegal backpatch marker in forward-declared \
422 symbol");
423                       break;
424                 }
425             }
426             break;
427         default:
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;
432             }
433             break;
434     }
435
436     if (bpatch_trace_setting) printf(" %04x\n", value);
437
438     return(value);
439 }
440
441 extern int32 backpatch_value(int32 value)
442 {
443   if (!glulx_mode)
444     return backpatch_value_z(value);
445   else
446     return backpatch_value_g(value);
447 }
448
449 static void backpatch_zmachine_z(int mv, int zmachine_area, int32 offset)
450 {   
451     if (mv == OBJECT_MV) return;
452     if (mv == IDENT_MV) return;
453     if (mv == ACTION_MV) return;
454
455     if (bpatch_trace_setting >= 2)
456         printf("BP added: MV %d ZA %d Off %04x\n", mv, zmachine_area, offset);
457
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;
463 }
464
465 static void backpatch_zmachine_g(int mv, int zmachine_area, int32 offset)
466 {   
467     if (mv == IDENT_MV) return;
468     if (mv == ACTION_MV) return;
469
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.
474 */
475
476     if (bpatch_trace_setting >= 2)
477         printf("BP added: MV %d ZA %d Off %06x\n", mv, zmachine_area, offset);
478
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;
486 }
487
488 extern void backpatch_zmachine(int mv, int zmachine_area, int32 offset)
489 {
490   if (!glulx_mode)
491     backpatch_zmachine_z(mv, zmachine_area, offset);
492   else
493     backpatch_zmachine_g(mv, zmachine_area, offset);
494 }
495
496 extern void backpatch_zmachine_image_z(void)
497 {   int bm = 0, zmachine_area; int32 offset, value, addr = 0;
498     ASSERT_ZCODE();
499     backpatch_error_flag = FALSE;
500     while (bm < zmachine_backpatch_size)
501     {   backpatch_marker
502             = zmachine_backpatch_table[bm];
503         zmachine_area
504             = zmachine_backpatch_table[bm+1];
505         offset
506           = 256*zmachine_backpatch_table[bm+2]
507             + zmachine_backpatch_table[bm+3];
508         bm += 4;
509
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;
516             default:
517                 if (compiler_error("Illegal area to backpatch"))
518                     backpatch_error_flag = TRUE;
519         }
520         addr += offset;
521
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;
527
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);
532         }
533     }
534 }
535
536 extern void backpatch_zmachine_image_g(void)
537 {   int bm = 0, zmachine_area; int32 offset, value, addr = 0;
538     ASSERT_GLULX();
539     backpatch_error_flag = FALSE;
540     while (bm < zmachine_backpatch_size)
541     {   backpatch_marker
542             = zmachine_backpatch_table[bm];
543         zmachine_area
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];
552         bm += 6;
553
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 */
561         default:
562             if (compiler_error("Illegal area to backpatch"))
563               backpatch_error_flag = TRUE;
564         }
565         addr = addr + offset - Write_RAM_At;
566
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;
576
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);
581         }
582     }
583 }
584
585 /* ========================================================================= */
586 /*   Data structure management routines                                      */
587 /* ------------------------------------------------------------------------- */
588
589 extern void init_bpatch_vars(void)
590 {   zcode_backpatch_table = NULL;
591     staticarray_backpatch_table = NULL;
592     zmachine_backpatch_table = NULL;
593 }
594
595 extern void bpatch_begin_pass(void)
596 {   zcode_backpatch_size = 0;
597     staticarray_backpatch_size = 0;
598     zmachine_backpatch_size = 0;
599 }
600
601 extern void bpatch_allocate_arrays(void)
602 {
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");
612 }
613
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);
618 }
619
620 /* ========================================================================= */