X-Git-Url: https://jxself.org/git/?p=inform.git;a=blobdiff_plain;f=src%2Fbpatch.c;fp=src%2Fbpatch.c;h=ccc87badabc23049659ce1bf93f754e3487620b9;hp=0000000000000000000000000000000000000000;hb=81ffe9a7de1db0b3a318a053b38882d1b7ab304c;hpb=d1090135a32de7b38b48c55d4e21f95da4c405bc diff --git a/src/bpatch.c b/src/bpatch.c new file mode 100644 index 0000000..ccc87ba --- /dev/null +++ b/src/bpatch.c @@ -0,0 +1,525 @@ +/* ------------------------------------------------------------------------- */ +/* "bpatch" : Keeps track of, and finally acts on, backpatch markers, */ +/* correcting symbol values not known at compilation time */ +/* */ +/* Copyright (c) Graham Nelson 1993 - 2018 */ +/* */ +/* This file is part of Inform. */ +/* */ +/* Inform is free software: you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation, either version 3 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* Inform is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with Inform. If not, see https://gnu.org/licenses/ */ +/* */ +/* ------------------------------------------------------------------------- */ + +#include "header.h" + +memory_block zcode_backpatch_table, zmachine_backpatch_table; +int32 zcode_backpatch_size, zmachine_backpatch_size; + +/* ------------------------------------------------------------------------- */ +/* The mending operation */ +/* ------------------------------------------------------------------------- */ + +int backpatch_marker, backpatch_size, backpatch_error_flag; + +static int32 backpatch_value_z(int32 value) +{ /* Corrects the quantity "value" according to backpatch_marker */ + + ASSERT_ZCODE(); + + if (asm_trace_level >= 4) + printf("BP %s applied to %04x giving ", + describe_mv(backpatch_marker), value); + + switch(backpatch_marker) + { case STRING_MV: + value += strings_offset/scale_factor; break; + case ARRAY_MV: + value += variables_offset; break; + case IROUTINE_MV: + if (OMIT_UNUSED_ROUTINES) + value = df_stripped_address_for_address(value); + value += code_offset/scale_factor; + break; + case VROUTINE_MV: + if ((value<0) || (value>=VENEER_ROUTINES)) + { if (no_link_errors > 0) break; + if (compiler_error + ("Backpatch veneer routine number out of range")) + { printf("Illegal BP veneer routine number: %d\n", value); + backpatch_error_flag = TRUE; + } + value = 0; + break; + } + value = veneer_routine_address[value]; + if (OMIT_UNUSED_ROUTINES) + value = df_stripped_address_for_address(value); + value += code_offset/scale_factor; + break; + case NO_OBJS_MV: + value = no_objects; break; + case INCON_MV: + if ((value<0) || (value>=NO_SYSTEM_CONSTANTS)) + { if (no_link_errors > 0) break; + if (compiler_error + ("Backpatch system constant number out of range")) + { printf("Illegal BP system constant number: %d\n", value); + backpatch_error_flag = TRUE; + } + value = 0; + break; + } + value = value_of_system_constant(value); break; + case DWORD_MV: + value = dictionary_offset + 7 + + final_dict_order[value]*((version_number==3)?7:9); + break; + case ACTION_MV: + break; + case INHERIT_MV: + value = 256*zmachine_paged_memory[value + prop_values_offset] + + zmachine_paged_memory[value + prop_values_offset + 1]; + break; + case INHERIT_INDIV_MV: + value = 256*zmachine_paged_memory[value + + individuals_offset] + + zmachine_paged_memory[value + + individuals_offset + 1]; + break; + case INDIVPT_MV: + value += individuals_offset; + break; + case MAIN_MV: + value = symbol_index("Main", -1); + if (stypes[value] != ROUTINE_T) + error("No 'Main' routine has been defined"); + sflags[value] |= USED_SFLAG; + value = svals[value]; + if (OMIT_UNUSED_ROUTINES) + value = df_stripped_address_for_address(value); + value += code_offset/scale_factor; + break; + case SYMBOL_MV: + if ((value<0) || (value>=no_symbols)) + { if (no_link_errors > 0) break; + if (compiler_error("Backpatch symbol number out of range")) + { printf("Illegal BP symbol number: %d\n", value); + backpatch_error_flag = TRUE; + } + value = 0; + break; + } + if (sflags[value] & UNKNOWN_SFLAG) + { if (!(sflags[value] & UERROR_SFLAG)) + { sflags[value] |= UERROR_SFLAG; + error_named_at("No such constant as", + (char *) symbs[value], slines[value]); + } + } + else + if (sflags[value] & CHANGE_SFLAG) + { sflags[value] &= (~(CHANGE_SFLAG)); + backpatch_marker = (svals[value]/0x10000); + if ((backpatch_marker < 0) + || (backpatch_marker > LARGEST_BPATCH_MV)) + { + if (no_link_errors == 0) + { compiler_error_named( + "Illegal backpatch marker attached to symbol", + (char *) symbs[value]); + backpatch_error_flag = TRUE; + } + } + else + svals[value] = backpatch_value_z((svals[value]) % 0x10000); + } + + sflags[value] |= USED_SFLAG; + { int t = stypes[value]; + value = svals[value]; + switch(t) + { case ROUTINE_T: + if (OMIT_UNUSED_ROUTINES) + value = df_stripped_address_for_address(value); + value += code_offset/scale_factor; + break; + case ARRAY_T: value += variables_offset; break; + } + } + break; + default: + if (no_link_errors > 0) break; + if (compiler_error("Illegal backpatch marker")) + { printf("Illegal backpatch marker %d value %04x\n", + backpatch_marker, value); + backpatch_error_flag = TRUE; + } + break; + } + + if (asm_trace_level >= 4) printf(" %04x\n", value); + + return(value); +} + +static int32 backpatch_value_g(int32 value) +{ /* Corrects the quantity "value" according to backpatch_marker */ + int32 valaddr; + + ASSERT_GLULX(); + + if (asm_trace_level >= 4) + printf("BP %s applied to %04x giving ", + describe_mv(backpatch_marker), value); + + switch(backpatch_marker) + { + case STRING_MV: + if (value <= 0 || value > no_strings) + compiler_error("Illegal string marker."); + value = strings_offset + compressed_offsets[value-1]; break; + case IROUTINE_MV: + if (OMIT_UNUSED_ROUTINES) + value = df_stripped_address_for_address(value); + value += code_offset; + break; + case ARRAY_MV: + value += arrays_offset; break; + case VARIABLE_MV: + value = variables_offset + (4*value); break; + case OBJECT_MV: + value = object_tree_offset + (OBJECT_BYTE_LENGTH*(value-1)); + break; + case VROUTINE_MV: + if ((value<0) || (value>=VENEER_ROUTINES)) + { if (no_link_errors > 0) break; + if (compiler_error + ("Backpatch veneer routine number out of range")) + { printf("Illegal BP veneer routine number: %d\n", value); + backpatch_error_flag = TRUE; + } + value = 0; + break; + } + value = veneer_routine_address[value]; + if (OMIT_UNUSED_ROUTINES) + value = df_stripped_address_for_address(value); + value += code_offset; + break; + case NO_OBJS_MV: + value = no_objects; break; + case INCON_MV: + if ((value<0) || (value>=NO_SYSTEM_CONSTANTS)) + { if (no_link_errors > 0) break; + if (compiler_error + ("Backpatch system constant number out of range")) + { printf("Illegal BP system constant number: %d\n", value); + backpatch_error_flag = TRUE; + } + value = 0; + break; + } + value = value_of_system_constant(value); break; + case DWORD_MV: + value = dictionary_offset + 4 + + final_dict_order[value]*DICT_ENTRY_BYTE_LENGTH; + break; + case ACTION_MV: + break; + case INHERIT_MV: + valaddr = (prop_values_offset - Write_RAM_At) + value; + value = ReadInt32(zmachine_paged_memory + valaddr); + break; + case INHERIT_INDIV_MV: + error("*** No individual property storage in Glulx ***"); + break; + case INDIVPT_MV: + value += individuals_offset; + break; + case MAIN_MV: + value = symbol_index("Main", -1); + if (stypes[value] != ROUTINE_T) + error("No 'Main' routine has been defined"); + sflags[value] |= USED_SFLAG; + value = svals[value]; + if (OMIT_UNUSED_ROUTINES) + value = df_stripped_address_for_address(value); + value += code_offset; + break; + case SYMBOL_MV: + if ((value<0) || (value>=no_symbols)) + { if (no_link_errors > 0) break; + if (compiler_error("Backpatch symbol number out of range")) + { printf("Illegal BP symbol number: %d\n", value); + backpatch_error_flag = TRUE; + } + value = 0; + break; + } + if (sflags[value] & UNKNOWN_SFLAG) + { if (!(sflags[value] & UERROR_SFLAG)) + { sflags[value] |= UERROR_SFLAG; + error_named_at("No such constant as", + (char *) symbs[value], slines[value]); + } + } + else + if (sflags[value] & CHANGE_SFLAG) + { sflags[value] &= (~(CHANGE_SFLAG)); + backpatch_marker = smarks[value]; + if ((backpatch_marker < 0) + || (backpatch_marker > LARGEST_BPATCH_MV)) + { + if (no_link_errors == 0) + { compiler_error_named( + "Illegal backpatch marker attached to symbol", + (char *) symbs[value]); + backpatch_error_flag = TRUE; + } + } + else + svals[value] = backpatch_value_g(svals[value]); + } + + sflags[value] |= USED_SFLAG; + { int t = stypes[value]; + value = svals[value]; + switch(t) + { + case ROUTINE_T: + if (OMIT_UNUSED_ROUTINES) + value = df_stripped_address_for_address(value); + value += code_offset; + break; + case ARRAY_T: value += arrays_offset; break; + case OBJECT_T: + case CLASS_T: + value = object_tree_offset + + (OBJECT_BYTE_LENGTH*(value-1)); + break; + case ATTRIBUTE_T: + /* value is unchanged */ + break; + case CONSTANT_T: + case INDIVIDUAL_PROPERTY_T: + /* value is unchanged */ + break; + default: + error("*** Illegal backpatch marker in forward-declared \ +symbol"); + break; + } + } + break; + default: + if (no_link_errors > 0) break; + if (compiler_error("Illegal backpatch marker")) + { printf("Illegal backpatch marker %d value %04x\n", + backpatch_marker, value); + backpatch_error_flag = TRUE; + } + break; + } + + if (asm_trace_level >= 4) printf(" %04x\n", value); + + return(value); +} + +extern int32 backpatch_value(int32 value) +{ + if (!glulx_mode) + return backpatch_value_z(value); + else + return backpatch_value_g(value); +} + +static void backpatch_zmachine_z(int mv, int zmachine_area, int32 offset) +{ if (module_switch) + { if (zmachine_area == PROP_DEFAULTS_ZA) return; + } + else + { if (mv == OBJECT_MV) return; + if (mv == IDENT_MV) return; + if (mv == ACTION_MV) return; + } + + /* printf("MV %d ZA %d Off %04x\n", mv, zmachine_area, offset); */ + + write_byte_to_memory_block(&zmachine_backpatch_table, + zmachine_backpatch_size++, mv); + write_byte_to_memory_block(&zmachine_backpatch_table, + zmachine_backpatch_size++, zmachine_area); + write_byte_to_memory_block(&zmachine_backpatch_table, + zmachine_backpatch_size++, offset/256); + write_byte_to_memory_block(&zmachine_backpatch_table, + zmachine_backpatch_size++, offset%256); +} + +static void backpatch_zmachine_g(int mv, int zmachine_area, int32 offset) +{ if (module_switch) + { if (zmachine_area == PROP_DEFAULTS_ZA) return; + } + else + { if (mv == IDENT_MV) return; + if (mv == ACTION_MV) return; + } + +/* The backpatch table format for Glulx: + First, the marker byte. + Then, the zmachine area being patched. + Then the four-byte address. +*/ + +/* printf("+MV %d ZA %d Off %06x\n", mv, zmachine_area, offset); */ + + write_byte_to_memory_block(&zmachine_backpatch_table, + zmachine_backpatch_size++, mv); + write_byte_to_memory_block(&zmachine_backpatch_table, + zmachine_backpatch_size++, zmachine_area); + write_byte_to_memory_block(&zmachine_backpatch_table, + zmachine_backpatch_size++, (offset >> 24) & 0xFF); + write_byte_to_memory_block(&zmachine_backpatch_table, + zmachine_backpatch_size++, (offset >> 16) & 0xFF); + write_byte_to_memory_block(&zmachine_backpatch_table, + zmachine_backpatch_size++, (offset >> 8) & 0xFF); + write_byte_to_memory_block(&zmachine_backpatch_table, + zmachine_backpatch_size++, (offset) & 0xFF); +} + +extern void backpatch_zmachine(int mv, int zmachine_area, int32 offset) +{ + if (!glulx_mode) + backpatch_zmachine_z(mv, zmachine_area, offset); + else + backpatch_zmachine_g(mv, zmachine_area, offset); +} + +extern void backpatch_zmachine_image_z(void) +{ int bm = 0, zmachine_area; int32 offset, value, addr = 0; + ASSERT_ZCODE(); + backpatch_error_flag = FALSE; + while (bm < zmachine_backpatch_size) + { backpatch_marker + = read_byte_from_memory_block(&zmachine_backpatch_table, bm); + zmachine_area + = read_byte_from_memory_block(&zmachine_backpatch_table, bm+1); + offset + = 256*read_byte_from_memory_block(&zmachine_backpatch_table,bm+2) + + read_byte_from_memory_block(&zmachine_backpatch_table, bm+3); + bm += 4; + + switch(zmachine_area) + { case PROP_DEFAULTS_ZA: addr = prop_defaults_offset; break; + case PROP_ZA: addr = prop_values_offset; break; + case INDIVIDUAL_PROP_ZA: addr = individuals_offset; break; + case DYNAMIC_ARRAY_ZA: addr = variables_offset; break; + default: + if (no_link_errors == 0) + if (compiler_error("Illegal area to backpatch")) + backpatch_error_flag = TRUE; + } + addr += offset; + + value = 256*zmachine_paged_memory[addr] + + zmachine_paged_memory[addr+1]; + value = backpatch_value_z(value); + zmachine_paged_memory[addr] = value/256; + zmachine_paged_memory[addr+1] = value%256; + + if (backpatch_error_flag) + { backpatch_error_flag = FALSE; + if (no_link_errors == 0) + printf("*** MV %d ZA %d Off %04x ***\n", + backpatch_marker, zmachine_area, offset); + } + } +} + +extern void backpatch_zmachine_image_g(void) +{ int bm = 0, zmachine_area; int32 offset, value, addr = 0; + ASSERT_GLULX(); + backpatch_error_flag = FALSE; + while (bm < zmachine_backpatch_size) + { backpatch_marker + = read_byte_from_memory_block(&zmachine_backpatch_table, bm); + zmachine_area + = read_byte_from_memory_block(&zmachine_backpatch_table, bm+1); + offset = read_byte_from_memory_block(&zmachine_backpatch_table, bm+2); + offset = (offset << 8) | + read_byte_from_memory_block(&zmachine_backpatch_table, bm+3); + offset = (offset << 8) | + read_byte_from_memory_block(&zmachine_backpatch_table, bm+4); + offset = (offset << 8) | + read_byte_from_memory_block(&zmachine_backpatch_table, bm+5); + bm += 6; + + /* printf("-MV %d ZA %d Off %06x\n", backpatch_marker, zmachine_area, offset); */ + + switch(zmachine_area) { + case PROP_DEFAULTS_ZA: addr = prop_defaults_offset+4; break; + case PROP_ZA: addr = prop_values_offset; break; + case INDIVIDUAL_PROP_ZA: addr = individuals_offset; break; + case ARRAY_ZA: addr = arrays_offset; break; + case GLOBALVAR_ZA: addr = variables_offset; break; + default: + if (no_link_errors == 0) + if (compiler_error("Illegal area to backpatch")) + backpatch_error_flag = TRUE; + } + addr = addr + offset - Write_RAM_At; + + value = (zmachine_paged_memory[addr] << 24) + | (zmachine_paged_memory[addr+1] << 16) + | (zmachine_paged_memory[addr+2] << 8) + | (zmachine_paged_memory[addr+3]); + value = backpatch_value_g(value); + zmachine_paged_memory[addr] = (value >> 24) & 0xFF; + zmachine_paged_memory[addr+1] = (value >> 16) & 0xFF; + zmachine_paged_memory[addr+2] = (value >> 8) & 0xFF; + zmachine_paged_memory[addr+3] = (value) & 0xFF; + + if (backpatch_error_flag) + { backpatch_error_flag = FALSE; + if (no_link_errors == 0) + printf("*** MV %d ZA %d Off %04x ***\n", + backpatch_marker, zmachine_area, offset); + } + } +} + +/* ========================================================================= */ +/* Data structure management routines */ +/* ------------------------------------------------------------------------- */ + +extern void init_bpatch_vars(void) +{ initialise_memory_block(&zcode_backpatch_table); + initialise_memory_block(&zmachine_backpatch_table); +} + +extern void bpatch_begin_pass(void) +{ zcode_backpatch_size = 0; + zmachine_backpatch_size = 0; +} + +extern void bpatch_allocate_arrays(void) +{ +} + +extern void bpatch_free_arrays(void) +{ deallocate_memory_block(&zcode_backpatch_table); + deallocate_memory_block(&zmachine_backpatch_table); +} + +/* ========================================================================= */