From df7f1bf06bc22053cf8ad88b325c42f34e529319 Mon Sep 17 00:00:00 2001 From: marcobaye Date: Sun, 18 Aug 2024 22:49:35 +0000 Subject: [PATCH] added "output pass", to ease adding of new features. I had to modify two regression tests because they barfed in the second pass, but real-world code would not do this, so I'm okay with this incompatibility. git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@400 4df02467-bbd4-4a76-a152-e7ce94205b78 --- src/acme.c | 79 ++++++++++++++++++++++++---------------- src/global.h | 5 ++- src/output.c | 55 +++++++++++----------------- src/output.h | 3 -- src/pseudoopcodes.c | 21 ++++------- src/version.h | 2 +- testing/auto/iterables.a | 2 +- testing/auto/postfix.a | 2 +- 8 files changed, 82 insertions(+), 87 deletions(-) diff --git a/src/acme.c b/src/acme.c index ce719ed..00d3312 100644 --- a/src/acme.c +++ b/src/acme.c @@ -162,17 +162,16 @@ static void report_init(struct report *report) report->bin_used = 0; } // open report file -static int report_open(struct report *report, const char *filename) +static void report_open(struct report *report, const char *filename) { // show filename _now_ because I do not want to massage it into perror() printf("Opening report file \"%s\".\n", filename); report->fd = fopen(filename, FILE_WRITETEXT); if (report->fd == NULL) { perror("Error: Cannot open report file"); - return 1; + exit(ACME_finalize(EXIT_FAILURE)); } input_set_report_enabled(TRUE); - return 0; // success } // close report file static void report_close(struct report *report) @@ -330,24 +329,38 @@ static void save_output_file(void) } +// definitions for pass flags: +#define PF_COMPLAIN_ABOUT_UNDEFINEDS (1u << 0) // throw "Symbol not defined" errors +#define PF_THROW_SEGMENT_MESSAGES (1u << 1) // throw segment warnings/errors +#define PF_GENERATE_OUTPUT (1u << 2) // generate output and/or report file // perform a single pass -static void perform_pass(void) +static void perform_pass(bits passflags) { FILE *fd; int ii; + // init variables + pass.counters.undefineds = 0; + //pass.counters.needvalue = 0; FIXME - use + pass.counters.symbolchanges = 0; + pass.counters.errors = 0; + pass.counters.warnings = 0; + pass.flags.complain_about_undefined = !!(passflags & PF_COMPLAIN_ABOUT_UNDEFINEDS); + pass.flags.throw_segment_messages = !!(passflags & PF_THROW_SEGMENT_MESSAGES); + pass.flags.generate_output = !!(passflags & PF_GENERATE_OUTPUT); + if (config.process_verbosity >= 2) printf("Pass %d:\n", pass.number); + + if (pass.flags.generate_output) { + // if wanted, open report listing + if (config.report_filename) + report_open(report, config.report_filename); + } cputype_passinit(); // set default cpu type output_passinit(); // set initial pc or start with undefined pc encoding_passinit(); // set default encoding section_passinit(); // set initial zone (untitled) - // init variables - pass.counters.undefineds = 0; - //pass.counters.needvalue = 0; FIXME - use - pass.counters.symbolchanges = 0; - pass.counters.errors = 0; - pass.counters.warnings = 0; // process toplevel files for (ii = 0; ii < toplevel_src_count; ++ii) { fd = fopen(toplevel_sources_plat[ii], FILE_READBINARY); @@ -367,8 +380,17 @@ static void perform_pass(void) // they would need to be evaluated. if (config.process_verbosity >= 8) printf("Undefined expressions: %d. Symbol updates: %d.\n", pass.counters.undefineds, pass.counters.symbolchanges); + // FIXME - make this into next if's "else" block: if (pass.counters.errors) exit(ACME_finalize(EXIT_FAILURE)); + + if (pass.flags.generate_output) { + // FIXME - add some code here to do "if there were errors, call BUG()" + // if report listing is open, close it + if (config.report_filename) + report_close(report); + save_output_file(); + } // now increment pass number // this must be done _after_ the pass because assignments done via // "-DSYMBOL=VALUE" cli args must be handled as if they were done at the @@ -387,21 +409,21 @@ static void do_actual_work(void) report = &global_report; // let global pointer point to something report_init(report); // we must init struct before doing passes - sanity.macro_recursions_left = config.sanity_limit; - sanity.source_recursions_left = config.sanity_limit; - sanity.passes_left = config.sanity_limit; + sanity.macro_recursions_left = config.sanity_limit; + sanity.source_recursions_left = config.sanity_limit; + sanity.passes_left = config.sanity_limit; - pass.flags.complain_about_undefined = FALSE; // disable until error pass needed - pass.flags.do_segment_checks = TRUE; // FIXME - do in _last_ pass instead! - perform_pass(); // first pass - pass.flags.do_segment_checks = FALSE; // FIXME - do in _last_ pass instead! +// first pass: + perform_pass(PF_THROW_SEGMENT_MESSAGES); // FIXME - check segments in all passes, but only throw errors/warnings in final pass! // pretend there has been a previous pass, with one more undefined result undefs_before = pass.counters.undefineds + 1; + +// further passes: // keep doing passes as long as the number of undefined results keeps decreasing. // stop on zero (FIXME - zero-check pass.counters.needvalue instead!) while ((pass.counters.undefineds && (pass.counters.undefineds < undefs_before)) || pass.counters.symbolchanges) { undefs_before = pass.counters.undefineds; - perform_pass(); + perform_pass(0); if (--sanity.passes_left < 0) { // FIXME - exit with error // ...or maybe do one additional pass where all errors are reported, including "not defined" and "value has changed". @@ -409,27 +431,20 @@ static void do_actual_work(void) //break; } } + +// last pass: // any errors left? if (pass.counters.undefineds == 0) { // FIXME - use pass.counters.needvalue instead! - // if listing report is wanted and there were no errors, - // do another pass to generate listing report - if (config.report_filename) { - if (config.process_verbosity >= 2) - puts("Extra pass to generate listing report."); - if (report_open(report, config.report_filename) == 0) { - perform_pass(); - report_close(report); - } - // FIXME - add some code here to do "if there were errors, call BUG()" - } - save_output_file(); + // victory lap + if (config.process_verbosity >= 2) + puts("Extra pass to generate output."); + perform_pass(PF_GENERATE_OUTPUT); } else { // There are still errors (unsolvable by doing further passes), // so perform additional pass to find and show them. if (config.process_verbosity >= 2) puts("Extra pass to display errors."); - pass.flags.complain_about_undefined = TRUE; // activate error output - perform_pass(); // perform pass, but now show "value undefined" + perform_pass(PF_COMPLAIN_ABOUT_UNDEFINEDS); // perform pass, but now show "value undefined" // FIXME - perform_pass() calls exit() when there were errors, // so if controls returns here, call BUG()! // (this can be triggered using ifdef/ifndef) diff --git a/src/global.h b/src/global.h index ee4a1b9..ead7690 100644 --- a/src/global.h +++ b/src/global.h @@ -118,8 +118,9 @@ struct pass { // error output pass. } counters; struct { - char complain_about_undefined; // will be FALSE until error pass is needed - char do_segment_checks; // atm only used in pass 1, should be used in _last_ pass! + boolean complain_about_undefined; // will be FALSE until error pass is needed + 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; }; extern struct pass pass; diff --git a/src/output.c b/src/output.c index 9381871..9d603b2 100644 --- a/src/output.c +++ b/src/output.c @@ -99,7 +99,7 @@ static void border_crossed(int current_offset) // further: if (current_offset >= config.outbuf_size) Throw_serious_error("Reached memory limit."); - if (pass.flags.do_segment_checks) { + 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 } @@ -177,29 +177,6 @@ void output_skip(int size) } -// fill output buffer with given byte value -static void fill_completely(char value) -{ - memset(out->buffer, value, config.outbuf_size); -} - - -// default value for empty memory has changed (called by "!initmem" pseudo opcode) -void output_newdefault(void) -{ - // init memory - fill_completely(config.mem_init_value); - // enforce another pass (FIXME - no, just do a separate output pass anyway!) - if (pass.counters.undefineds == 0) - pass.counters.undefineds = 1; - //if (pass.counters.needvalue == 0) FIXME - use? instead or additionally? - // pass.counters.needvalue = 1; -// enforcing another pass is not needed if there hasn't been any -// output yet. But that's tricky to detect without too much overhead. -// The old solution was to add &&(out->lowest_written < out->highest_written+1) to "if" above -// in future, just allocate and init outbuf at the start of the "last" pass! -} - // remember current outbuf index as start/limit of output file static boolean force_file_start = FALSE; static intval_t forced_start_idx; @@ -228,16 +205,10 @@ void outbuf_set_outfile_limit(void) // init output struct +// FIXME - remove this, create the buffer at start of final pass instead! void output_createbuffer(void) { out->buffer = safe_malloc(config.outbuf_size); -// FIXME - in future, do both of these only at start of "last" pass: - // fill memory with initial value - if (config.mem_init_value == NO_VALUE_GIVEN) { - fill_completely(0); // default value - } else { - fill_completely(config.mem_init_value & 0xff); - } // init ring list of segments out->segm.list_head.next = &out->segm.list_head; out->segm.list_head.prev = &out->segm.list_head; @@ -272,6 +243,7 @@ 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) { struct segment *test_segment = out->segm.list_head.next; @@ -297,9 +269,23 @@ void output_passinit(void) { // struct segment *temp; + // are we supposed to actually generate correct output? + if (pass.flags.generate_output) { + // FIXME - allocate output buffer using size info gathered in previous pass! + // fill output buffer with initial byte value + if (config.mem_init_value == NO_VALUE_GIVEN) { + memset(out->buffer, 0, config.outbuf_size); // default value + } else { + memset(out->buffer, config.mem_init_value & 0xff, config.outbuf_size); + } + } + //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! +// 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; @@ -347,7 +333,8 @@ static void end_segment(void) intval_t amount; // only do in first or last pass - if (!pass.flags.do_segment_checks) + // 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 @@ -396,7 +383,7 @@ static void start_segment(intval_t address_change, bits segment_flags) // allow writing to output buffer output_byte = real_output; // in first/last pass, check for other segments and maybe issue warning - if (pass.flags.do_segment_checks) { + if (pass.flags.throw_segment_messages) { if (!(segment_flags & SEGMENT_FLAG_OVERLAY)) check_segment(out->segm.start); find_segment_max(out->segm.start); diff --git a/src/output.h b/src/output.h index 136c42e..02a109a 100644 --- a/src/output.h +++ b/src/output.h @@ -34,9 +34,6 @@ extern void output_skip(int size); // FIXME - replace by output_sequence(char *src, size_t size) extern void (*output_byte)(intval_t); -// default value for empty memory has changed (called by "!initmem" pseudo opcode) -extern void output_newdefault(void); - // remember current outbuf index as start/limit of output file extern void outbuf_set_outfile_start(void); extern void outbuf_set_outfile_limit(void); diff --git a/src/pseudoopcodes.c b/src/pseudoopcodes.c index b5992a6..24179cd 100644 --- a/src/pseudoopcodes.c +++ b/src/pseudoopcodes.c @@ -72,9 +72,6 @@ static enum eos po_initmem(void) // remember value config.mem_init_value = intresult.val.intval & 0xff; - // fill outbuffer and enforce another pass - output_newdefault(); // FIXME - remove when outbuffer gets initialized only right before "last" pass! - return ENSURE_EOS; } @@ -537,6 +534,7 @@ static enum eos po_binary(void) int byte; struct number size, skip; + int amount; size.val.intval = -1; // means "not given" => "until EOF" skip.val.intval = 0; @@ -571,8 +569,7 @@ static enum eos po_binary(void) // check whether including is a waste of time // FIXME - future changes ("several-projects-at-once") // may be incompatible with this! - if ((size.val.intval >= 0) && (pass.counters.undefineds || pass.counters.errors)) { - //if ((size.val.intval >= 0) && (pass.counters.needvalue || pass.counters.errors)) { FIXME - use! + if ((size.val.intval >= 0) && (!pass.flags.generate_output)) { output_skip(size.val.intval); // really including is useless anyway } else { // really insert file @@ -593,16 +590,14 @@ static enum eos po_binary(void) output_byte(0); } while (--size.val.intval); } + // if verbose, produce some output + if (config.process_verbosity >= 2) { + amount = output_get_statement_size(); + printf("Loaded %d (0x%04x) bytes from file offset %d (0x%04x).\n", + amount, amount, skip.val.intval, skip.val.intval); + } } fclose(stream); - // if verbose, produce some output - // FIXME - do in _last_ pass instead of first! - if ((pass.number == 1) && (config.process_verbosity >= 2)) { - int amount = output_get_statement_size(); - - printf("Loaded %d (0x%04x) bytes from file offset %d (0x%04x).\n", - amount, amount, skip.val.intval, skip.val.intval); - } return ENSURE_EOS; } diff --git a/src/version.h b/src/version.h index 418563d..10c1eb5 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 "2 Aug" // update before release FIXME +#define CHANGE_DATE "3 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 diff --git a/testing/auto/iterables.a b/testing/auto/iterables.a index 119d5ae..9379962 100644 --- a/testing/auto/iterables.a +++ b/testing/auto/iterables.a @@ -15,7 +15,7 @@ otherstring = "Ford" +a otherstring + emptystring + somestring == "FordArthur" - collection = [] + !set collection = [] !for c in "Trillian" { !set collection = collection + [c] } diff --git a/testing/auto/postfix.a b/testing/auto/postfix.a index 17dd228..a8d5292 100644 --- a/testing/auto/postfix.a +++ b/testing/auto/postfix.a @@ -23,7 +23,7 @@ label4 lda d +a * - label4 == 3 label5 - e+2=5 + !set e+2=5 !for e+2, 17, 17 { lda e }