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