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