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