diff --git a/src/acme.c b/src/acme.c index 21f6f31..1c9fcc8 100644 --- a/src/acme.c +++ b/src/acme.c @@ -330,10 +330,9 @@ static void save_output_file(void) // definitions for pass flags: -#define PF_IS_ERROR_PASS (1u << 0) // throw "Symbol not defined" and other errors that could be suppressed -#define PF_THROW_SEGMENT_MESSAGES (1u << 1) // throw segment warnings/errors +#define PF_IS_FINAL_PASS (1u << 0) // mostly for special warnings +#define PF_IS_ERROR_PASS (1u << 1) // throw "Symbol not defined" and other errors that could be suppressed #define PF_GENERATE_OUTPUT (1u << 2) // generate output and/or report file -#define PF_IS_FINAL_PASS (1u << 3) // mostly for special warnings // perform a single pass static void perform_pass(bits passflags) @@ -351,7 +350,6 @@ static void perform_pass(bits passflags) pass.flags.complain_about_undefined = !!(passflags & PF_IS_ERROR_PASS); pass.flags.throw_all_errors = !!(passflags & PF_IS_ERROR_PASS); pass.flags.is_final_pass = !!(passflags & PF_IS_FINAL_PASS); - pass.flags.throw_segment_messages = !!(passflags & PF_THROW_SEGMENT_MESSAGES); pass.flags.generate_output = !!(passflags & PF_GENERATE_OUTPUT); if (config.process_verbosity >= 2) @@ -424,7 +422,7 @@ static void do_actual_work(void) output_init(); // first pass: - perform_pass(PF_THROW_SEGMENT_MESSAGES); // FIXME - check segments in all passes, but only throw errors/warnings in final pass! + perform_pass(0); // pretend there has been a previous pass, with one more undefined result undefs_before = pass.counters.undefineds + 1; @@ -683,7 +681,7 @@ static const char *long_option(const char *string) else if (strcmp(string, OPTION_IGNORE_ZEROES) == 0) config.honor_leading_zeroes = FALSE; else if (strcmp(string, OPTION_STRICT_SEGMENTS) == 0) - config.debuglevel_segmentprobs = DEBUGLEVEL_ERROR; + config.strict_segments = TRUE; else if (strcmp(string, OPTION_STRICT) == 0) config.all_warnings_are_errors = TRUE; else if (strcmp(string, OPTION_DIALECT) == 0) @@ -821,6 +819,9 @@ int main(int argc, const char *argv[]) fprintf(stderr, "%sStart address of output file exceeds end+1.\n", cliargs_error); exit(EXIT_FAILURE); } + // since version 0.98, "--strict-segments" are default: + if (config.dialect >= V0_98__PATHS_AND_SYMBOLCHANGE) + config.strict_segments = TRUE; // do the actual work do_actual_work(); diff --git a/src/global.c b/src/global.c index 32ec7f9..8998eba 100644 --- a/src/global.c +++ b/src/global.c @@ -118,7 +118,7 @@ void config_default(struct config *conf) conf->format_color = FALSE; // enabled by --color conf->msg_stream = stderr; // set to stdout by --use-stdout conf->honor_leading_zeroes = TRUE; // disabled by --ignore-zeroes - conf->debuglevel_segmentprobs = DEBUGLEVEL_WARNING; // changed to ERROR by --strict-segments TODO - toggle default? + conf->strict_segments = FALSE; // enabled by --strict-segments and V0.98 conf->all_warnings_are_errors = FALSE; // enabled by --strict conf->test_new_features = FALSE; // enabled by --test conf->dialect = V__CURRENT_VERSION; // changed by --dialect diff --git a/src/global.h b/src/global.h index 07852ee..163fdf7 100644 --- a/src/global.h +++ b/src/global.h @@ -85,7 +85,7 @@ struct config { boolean format_color; // enabled by --color FILE *msg_stream; // defaults to stderr, changed to stdout by --use-stdout boolean honor_leading_zeroes; // TRUE, disabled by --ignore-zeroes - enum debuglevel debuglevel_segmentprobs; // WARNING, changed to ERROR by --strict-segments + boolean strict_segments; // FALSE, enabled by --strict-segments and V0.98 boolean all_warnings_are_errors; // FALSE, enabled by --strict boolean test_new_features; // FALSE, enabled by --test enum dialect dialect; // set by --dialect (and --test --test) @@ -127,7 +127,6 @@ struct pass { boolean complain_about_undefined; // will be FALSE until error pass is needed boolean throw_all_errors; // will be FALSE until error pass is needed boolean is_final_pass; // needed for warnings like "converted float to int for xor" - boolean throw_segment_messages; // atm only used in pass 1, should be used in _last_ pass! boolean generate_output; // create output and/or report file } flags; }; diff --git a/src/output.c b/src/output.c index 58757c9..b498575 100644 --- a/src/output.c +++ b/src/output.c @@ -10,13 +10,13 @@ // 22 Sep 2015 Added big-endian output functions // 20 Apr 2019 Prepared for "make segment overlap warnings into errors" later on #include "output.h" +#include // for free() #include // for memset() #include "cpu.h" #include "global.h" -// constants -#define NO_SEGMENT_START (-1) // invalid value to signal "not in a segment" +// types // structure for linked list of segment data @@ -37,8 +37,8 @@ struct output { intval_t forced_start_idx; // first index to go into output file intval_t forced_limit_idx; // first index to not go into output file struct { - intval_t start; // start of current segment (or NO_SEGMENT_START) - intval_t max; // highest address segment may use + intval_t start; // start of current segment (or NO_VALUE_GIVEN if none) + intval_t max; // highest address segment may use FIXME - try to change max to limit (+1) bits flags; // segment flags ("overlay" and "invisible", see header file) struct segment list_head; // head element of doubly-linked ring list } segm; @@ -78,6 +78,16 @@ static void report_binary(char value) } +// wrapper fn for segment problems -> either warning or error +static void throwsegmentproblem(const char msg[]) +{ + if (config.strict_segments) + countorthrow_value_error(msg); + else + throw_finalpass_warning(msg); +} + + // set up new out->segment.max value according to the given address. // just find the next segment start and subtract 1. static void find_segment_max(intval_t new_pc) @@ -87,7 +97,6 @@ static void find_segment_max(intval_t new_pc) // search for smallest segment start address that // is larger than given address // use list head as sentinel -// FIXME - if +1 overflows intval_t, we have an infinite loop! out->segm.list_head.start = new_pc + 1; while (test_segment->start <= new_pc) test_segment = test_segment->next; @@ -106,10 +115,10 @@ static void border_crossed(int current_offset) // further: if (current_offset >= OUTBUF_MAXSIZE) throw_serious_error("Reached memory limit."); - if (pass.flags.throw_segment_messages) { - throw_message(config.debuglevel_segmentprobs, "Segment reached another one, overwriting it.", NULL); - find_segment_max(current_offset + 1); // find new (next) limit - } + throwsegmentproblem("Segment reached another one, overwriting it."); // FIXME - add segment name to msg! + find_segment_max(current_offset + 1); // find new (next) limit + // FIXME - the line above adds 1 and the fn adds 1 -> one address of potential + // segment start is skipped! } @@ -143,6 +152,7 @@ static void real_output(intval_t byte) // FIXME - change this to BUG and add code to make sure it does not happen! // or maybe at least enlarge the buffer by 16 bytes and place a canary in // it so we can do a sanity check at the end! + // (but that only helps for sequential writes without holes...) } out->buffer[out->write_idx] = (byte & 0xff) ^ out->xor; } @@ -219,6 +229,10 @@ void outbuf_set_outfile_limit(void) // link segment data into segment ring +// segments are sorted first by start address and then by length, example: +// start $0400, length $0100 +// start $0401, length $0100 +// start $0401, length $0101 static void link_segment(intval_t start, intval_t length) { struct segment *new_segment, @@ -244,8 +258,6 @@ static void link_segment(intval_t start, intval_t length) // check whether given PC is inside segment. -// only call in first pass, otherwise too many warnings might be thrown -// FIXME - do it the other way round and only complain if there were no other errors! // FIXME - move this fn to its single caller! static void check_segment(intval_t new_pc) { @@ -258,10 +270,9 @@ static void check_segment(intval_t new_pc) while (test_segment->start <= new_pc) { if ((test_segment->start + test_segment->length) > new_pc) { // TODO - include overlap size in error message! - throw_message(config.debuglevel_segmentprobs, "Segment starts inside another one, overwriting it.", NULL); - return; + throwsegmentproblem("Segment starts inside another one, overwriting it."); + return; // we found one problem, no need to go on looking for more } - test_segment = test_segment->next; } } @@ -270,19 +281,33 @@ static void check_segment(intval_t new_pc) // called once on startup, inits structs void output_init(void) { - // init ring list of segments (FIXME - move to passinit) + // init ring list of segments out->segm.list_head.next = &out->segm.list_head; out->segm.list_head.prev = &out->segm.list_head; // init the one field not initialized by output_passinit: out->needed_bufsize = NO_VALUE_GIVEN; } +// helper fn to clear segment list +static void free_list(struct segment *list_head) +{ + struct segment *next, + *tmp; + + next = list_head->next; + while (next != list_head) { + tmp = next; + next = next->next; + free(tmp); + } + // re-init head element + list_head->next = list_head; + list_head->prev = list_head; +} // called before each pass, clears segment list and disables output void output_passinit(void) { - //struct segment *temp; - // init output struct: if (pass.flags.generate_output) { // we are supposed to actually generate correct output, so @@ -316,20 +341,12 @@ void output_passinit(void) out->forced_start_idx = NO_VALUE_GIVEN; out->forced_limit_idx = NO_VALUE_GIVEN; // not in a segment - out->segm.start = NO_SEGMENT_START; // TODO - "no active segment" could be made a segment flag! + out->segm.start = NO_VALUE_GIVEN; // TODO - "no active segment" could be made a segment flag! out->segm.max = OUTBUF_MAXSIZE - 1; out->segm.flags = 0; -//FIXME - why clear ring list in every pass? -// Because later pass shouldn't complain about overwriting the same segment from earlier pass! -// Currently this does not happen because segment warnings are only generated in first pass. -// FIXME - in future we want to _output_ the errors/warnings in the final pass only, -// but we need to _check_ for them in every pass because only the results of the second-to-last -// pass are important. so for the time being, keep this code: - // delete segment list (and free blocks) -// while ((temp = segment_list)) { -// segment_list = segment_list->next; -// free(temp); -// } + // clear list of segments, otherwise most segments would collide with + // their older selves from earlier passes: + free_list(&out->segm.list_head); // no "encryption": out->xor = 0; // needed size of buffer will be calculated at end of pass, so @@ -357,13 +374,8 @@ static void end_segment(void) { intval_t amount; - // only do in first or last pass - // FIXME - do the _checking_ in all passes, but only throw errors/warnings in final pass! - if (!pass.flags.throw_segment_messages) - return; - // if there is no segment, there is nothing to do - if (out->segm.start == NO_SEGMENT_START) + if (out->segm.start == NO_VALUE_GIVEN) return; // ignore "invisible" segments @@ -378,26 +390,32 @@ static void end_segment(void) // link to segment list link_segment(out->segm.start, amount); // announce - if (config.process_verbosity >= 2) + if (pass.flags.is_final_pass && (config.process_verbosity >= 2)) { // TODO - change output to start, limit, size, name: - // TODO - output hex numbers as %04x? What about limit 0x10000? - printf("Segment size is %d (0x%x) bytes (0x%x - 0x%x exclusive).\n", + printf("Segment size is %d (0x%04x) bytes (0x%04x - 0x%04x exclusive).\n", amount, amount, out->segm.start, out->write_idx); + } } // called after each pass, closes last code segment and calculates outbuffer size void output_endofpass(void) { + intval_t bufsize; + // properly finalize previous segment (link to list, announce) end_segment(); // calculate size of output buffer if (out->highest_written >= out->lowest_written) { - out->needed_bufsize = out->highest_written + 1; + bufsize = out->highest_written + 1; } else { - out->needed_bufsize = NO_VALUE_GIVEN; + bufsize = NO_VALUE_GIVEN; } + // changing the size counts as a symbol change + if (out->needed_bufsize != bufsize) + ++pass.counters.symbolchanges; + out->needed_bufsize = bufsize; //fprintf(stderr, "Need outbuf size of 0x%04x bytes.\n", out->needed_bufsize); } @@ -419,12 +437,10 @@ static void start_segment(intval_t address_change, bits segment_flags) out->segm.flags = segment_flags; // allow "writing to buffer" (even though buffer does not really exist until final pass) output_byte = real_output; - // in first/last pass, check for other segments and maybe issue warning - if (pass.flags.throw_segment_messages) { - if (!(segment_flags & SEGMENT_FLAG_OVERLAY)) - check_segment(out->segm.start); - find_segment_max(out->segm.start); - } + // check for other segments and maybe count/throw warning or error + if (!(segment_flags & SEGMENT_FLAG_OVERLAY)) + check_segment(out->segm.start); + find_segment_max(out->segm.start); } diff --git a/src/version.h b/src/version.h index 389f2fd..d9eea5c 100644 --- a/src/version.h +++ b/src/version.h @@ -9,7 +9,7 @@ #define RELEASE "0.97" // update before release FIXME #define CODENAME "Zem" // update before release -#define CHANGE_DATE "18 Aug" // update before release FIXME +#define CHANGE_DATE "19 Aug" // update before release FIXME #define CHANGE_YEAR "2024" // update before release //#define HOME_PAGE "http://home.pages.de/~mac_bacon/smorbrod/acme/" #define HOME_PAGE "http://sourceforge.net/p/acme-crossass/" // FIXME