moved segment checks to final pass, made v0.98 force --strict-segments

git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@416 4df02467-bbd4-4a76-a152-e7ce94205b78
This commit is contained in:
marcobaye 2024-09-02 22:36:23 +00:00
parent a037bcc41e
commit 01313f1182
5 changed files with 72 additions and 56 deletions

View File

@ -330,10 +330,9 @@ static void save_output_file(void)
// definitions for pass flags: // definitions for pass flags:
#define PF_IS_ERROR_PASS (1u << 0) // throw "Symbol not defined" and other errors that could be suppressed #define PF_IS_FINAL_PASS (1u << 0) // mostly for special warnings
#define PF_THROW_SEGMENT_MESSAGES (1u << 1) // throw segment warnings/errors #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_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 // perform a single pass
static void perform_pass(bits passflags) 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.complain_about_undefined = !!(passflags & PF_IS_ERROR_PASS);
pass.flags.throw_all_errors = !!(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.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); pass.flags.generate_output = !!(passflags & PF_GENERATE_OUTPUT);
if (config.process_verbosity >= 2) if (config.process_verbosity >= 2)
@ -424,7 +422,7 @@ static void do_actual_work(void)
output_init(); output_init();
// first pass: // 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 // pretend there has been a previous pass, with one more undefined result
undefs_before = pass.counters.undefineds + 1; 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) else if (strcmp(string, OPTION_IGNORE_ZEROES) == 0)
config.honor_leading_zeroes = FALSE; config.honor_leading_zeroes = FALSE;
else if (strcmp(string, OPTION_STRICT_SEGMENTS) == 0) else if (strcmp(string, OPTION_STRICT_SEGMENTS) == 0)
config.debuglevel_segmentprobs = DEBUGLEVEL_ERROR; config.strict_segments = TRUE;
else if (strcmp(string, OPTION_STRICT) == 0) else if (strcmp(string, OPTION_STRICT) == 0)
config.all_warnings_are_errors = TRUE; config.all_warnings_are_errors = TRUE;
else if (strcmp(string, OPTION_DIALECT) == 0) 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); fprintf(stderr, "%sStart address of output file exceeds end+1.\n", cliargs_error);
exit(EXIT_FAILURE); 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 the actual work
do_actual_work(); do_actual_work();

View File

@ -118,7 +118,7 @@ void config_default(struct config *conf)
conf->format_color = FALSE; // enabled by --color conf->format_color = FALSE; // enabled by --color
conf->msg_stream = stderr; // set to stdout by --use-stdout conf->msg_stream = stderr; // set to stdout by --use-stdout
conf->honor_leading_zeroes = TRUE; // disabled by --ignore-zeroes 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->all_warnings_are_errors = FALSE; // enabled by --strict
conf->test_new_features = FALSE; // enabled by --test conf->test_new_features = FALSE; // enabled by --test
conf->dialect = V__CURRENT_VERSION; // changed by --dialect conf->dialect = V__CURRENT_VERSION; // changed by --dialect

View File

@ -85,7 +85,7 @@ struct config {
boolean format_color; // enabled by --color boolean format_color; // enabled by --color
FILE *msg_stream; // defaults to stderr, changed to stdout by --use-stdout FILE *msg_stream; // defaults to stderr, changed to stdout by --use-stdout
boolean honor_leading_zeroes; // TRUE, disabled by --ignore-zeroes 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 all_warnings_are_errors; // FALSE, enabled by --strict
boolean test_new_features; // FALSE, enabled by --test boolean test_new_features; // FALSE, enabled by --test
enum dialect dialect; // set by --dialect (and --test --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 complain_about_undefined; // will be FALSE until error pass is needed
boolean throw_all_errors; // 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 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 boolean generate_output; // create output and/or report file
} flags; } flags;
}; };

View File

@ -10,13 +10,13 @@
// 22 Sep 2015 Added big-endian output functions // 22 Sep 2015 Added big-endian output functions
// 20 Apr 2019 Prepared for "make segment overlap warnings into errors" later on // 20 Apr 2019 Prepared for "make segment overlap warnings into errors" later on
#include "output.h" #include "output.h"
#include <stdlib.h> // for free()
#include <string.h> // for memset() #include <string.h> // for memset()
#include "cpu.h" #include "cpu.h"
#include "global.h" #include "global.h"
// constants // types
#define NO_SEGMENT_START (-1) // invalid value to signal "not in a segment"
// structure for linked list of segment data // 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_start_idx; // first index to go into output file
intval_t forced_limit_idx; // first index to not go into output file intval_t forced_limit_idx; // first index to not go into output file
struct { struct {
intval_t start; // start of current segment (or NO_SEGMENT_START) intval_t start; // start of current segment (or NO_VALUE_GIVEN if none)
intval_t max; // highest address segment may use 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) bits flags; // segment flags ("overlay" and "invisible", see header file)
struct segment list_head; // head element of doubly-linked ring list struct segment list_head; // head element of doubly-linked ring list
} segm; } 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. // set up new out->segment.max value according to the given address.
// just find the next segment start and subtract 1. // just find the next segment start and subtract 1.
static void find_segment_max(intval_t new_pc) 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 // search for smallest segment start address that
// is larger than given address // is larger than given address
// use list head as sentinel // use list head as sentinel
// FIXME - if +1 overflows intval_t, we have an infinite loop!
out->segm.list_head.start = new_pc + 1; out->segm.list_head.start = new_pc + 1;
while (test_segment->start <= new_pc) while (test_segment->start <= new_pc)
test_segment = test_segment->next; test_segment = test_segment->next;
@ -106,10 +115,10 @@ static void border_crossed(int current_offset)
// further: // further:
if (current_offset >= OUTBUF_MAXSIZE) if (current_offset >= OUTBUF_MAXSIZE)
throw_serious_error("Reached memory limit."); throw_serious_error("Reached memory limit.");
if (pass.flags.throw_segment_messages) { throwsegmentproblem("Segment reached another one, overwriting it."); // FIXME - add segment name to msg!
throw_message(config.debuglevel_segmentprobs, "Segment reached another one, overwriting it.", NULL); find_segment_max(current_offset + 1); // find new (next) limit
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! // 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 // 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! // 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; 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 // 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) static void link_segment(intval_t start, intval_t length)
{ {
struct segment *new_segment, 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. // 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! // FIXME - move this fn to its single caller!
static void check_segment(intval_t new_pc) 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) { while (test_segment->start <= new_pc) {
if ((test_segment->start + test_segment->length) > new_pc) { if ((test_segment->start + test_segment->length) > new_pc) {
// TODO - include overlap size in error message! // TODO - include overlap size in error message!
throw_message(config.debuglevel_segmentprobs, "Segment starts inside another one, overwriting it.", NULL); throwsegmentproblem("Segment starts inside another one, overwriting it.");
return; return; // we found one problem, no need to go on looking for more
} }
test_segment = test_segment->next; test_segment = test_segment->next;
} }
} }
@ -270,19 +281,33 @@ static void check_segment(intval_t new_pc)
// called once on startup, inits structs // called once on startup, inits structs
void output_init(void) 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.next = &out->segm.list_head;
out->segm.list_head.prev = &out->segm.list_head; out->segm.list_head.prev = &out->segm.list_head;
// init the one field not initialized by output_passinit: // init the one field not initialized by output_passinit:
out->needed_bufsize = NO_VALUE_GIVEN; 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 // called before each pass, clears segment list and disables output
void output_passinit(void) void output_passinit(void)
{ {
//struct segment *temp;
// init output struct: // init output struct:
if (pass.flags.generate_output) { if (pass.flags.generate_output) {
// we are supposed to actually generate correct output, so // 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_start_idx = NO_VALUE_GIVEN;
out->forced_limit_idx = NO_VALUE_GIVEN; out->forced_limit_idx = NO_VALUE_GIVEN;
// not in a segment // 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.max = OUTBUF_MAXSIZE - 1;
out->segm.flags = 0; out->segm.flags = 0;
//FIXME - why clear ring list in every pass? // clear list of segments, otherwise most segments would collide with
// Because later pass shouldn't complain about overwriting the same segment from earlier pass! // their older selves from earlier passes:
// Currently this does not happen because segment warnings are only generated in first pass. free_list(&out->segm.list_head);
// 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);
// }
// no "encryption": // no "encryption":
out->xor = 0; out->xor = 0;
// needed size of buffer will be calculated at end of pass, so // needed size of buffer will be calculated at end of pass, so
@ -357,13 +374,8 @@ static void end_segment(void)
{ {
intval_t amount; 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 there is no segment, there is nothing to do
if (out->segm.start == NO_SEGMENT_START) if (out->segm.start == NO_VALUE_GIVEN)
return; return;
// ignore "invisible" segments // ignore "invisible" segments
@ -378,26 +390,32 @@ static void end_segment(void)
// link to segment list // link to segment list
link_segment(out->segm.start, amount); link_segment(out->segm.start, amount);
// announce // 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 - change output to start, limit, size, name:
// TODO - output hex numbers as %04x? What about limit 0x10000? printf("Segment size is %d (0x%04x) bytes (0x%04x - 0x%04x exclusive).\n",
printf("Segment size is %d (0x%x) bytes (0x%x - 0x%x exclusive).\n",
amount, amount, out->segm.start, out->write_idx); amount, amount, out->segm.start, out->write_idx);
}
} }
// called after each pass, closes last code segment and calculates outbuffer size // called after each pass, closes last code segment and calculates outbuffer size
void output_endofpass(void) void output_endofpass(void)
{ {
intval_t bufsize;
// properly finalize previous segment (link to list, announce) // properly finalize previous segment (link to list, announce)
end_segment(); end_segment();
// calculate size of output buffer // calculate size of output buffer
if (out->highest_written >= out->lowest_written) { if (out->highest_written >= out->lowest_written) {
out->needed_bufsize = out->highest_written + 1; bufsize = out->highest_written + 1;
} else { } 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); //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; out->segm.flags = segment_flags;
// allow "writing to buffer" (even though buffer does not really exist until final pass) // allow "writing to buffer" (even though buffer does not really exist until final pass)
output_byte = real_output; output_byte = real_output;
// in first/last pass, check for other segments and maybe issue warning // check for other segments and maybe count/throw warning or error
if (pass.flags.throw_segment_messages) { if (!(segment_flags & SEGMENT_FLAG_OVERLAY))
if (!(segment_flags & SEGMENT_FLAG_OVERLAY)) check_segment(out->segm.start);
check_segment(out->segm.start); find_segment_max(out->segm.start);
find_segment_max(out->segm.start);
}
} }

View File

@ -9,7 +9,7 @@
#define RELEASE "0.97" // update before release FIXME #define RELEASE "0.97" // update before release FIXME
#define CODENAME "Zem" // update before release #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 CHANGE_YEAR "2024" // update before release
//#define HOME_PAGE "http://home.pages.de/~mac_bacon/smorbrod/acme/" //#define HOME_PAGE "http://home.pages.de/~mac_bacon/smorbrod/acme/"
#define HOME_PAGE "http://sourceforge.net/p/acme-crossass/" // FIXME #define HOME_PAGE "http://sourceforge.net/p/acme-crossass/" // FIXME