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
This commit is contained in:
marcobaye 2024-08-18 22:49:35 +00:00
parent 0af42c7a8a
commit df7f1bf06b
8 changed files with 82 additions and 87 deletions

View File

@ -162,17 +162,16 @@ static void report_init(struct report *report)
report->bin_used = 0; report->bin_used = 0;
} }
// open report file // 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() // show filename _now_ because I do not want to massage it into perror()
printf("Opening report file \"%s\".\n", filename); printf("Opening report file \"%s\".\n", filename);
report->fd = fopen(filename, FILE_WRITETEXT); report->fd = fopen(filename, FILE_WRITETEXT);
if (report->fd == NULL) { if (report->fd == NULL) {
perror("Error: Cannot open report file"); perror("Error: Cannot open report file");
return 1; exit(ACME_finalize(EXIT_FAILURE));
} }
input_set_report_enabled(TRUE); input_set_report_enabled(TRUE);
return 0; // success
} }
// close report file // close report file
static void report_close(struct report *report) 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 // perform a single pass
static void perform_pass(void) static void perform_pass(bits passflags)
{ {
FILE *fd; FILE *fd;
int ii; 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) if (config.process_verbosity >= 2)
printf("Pass %d:\n", pass.number); 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 cputype_passinit(); // set default cpu type
output_passinit(); // set initial pc or start with undefined pc output_passinit(); // set initial pc or start with undefined pc
encoding_passinit(); // set default encoding encoding_passinit(); // set default encoding
section_passinit(); // set initial zone (untitled) 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 // process toplevel files
for (ii = 0; ii < toplevel_src_count; ++ii) { for (ii = 0; ii < toplevel_src_count; ++ii) {
fd = fopen(toplevel_sources_plat[ii], FILE_READBINARY); fd = fopen(toplevel_sources_plat[ii], FILE_READBINARY);
@ -367,8 +380,17 @@ static void perform_pass(void)
// they would need to be evaluated. // they would need to be evaluated.
if (config.process_verbosity >= 8) if (config.process_verbosity >= 8)
printf("Undefined expressions: %d. Symbol updates: %d.\n", pass.counters.undefineds, pass.counters.symbolchanges); 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) if (pass.counters.errors)
exit(ACME_finalize(EXIT_FAILURE)); 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 // now increment pass number
// this must be done _after_ the pass because assignments done via // 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 // "-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 = &global_report; // let global pointer point to something
report_init(report); // we must init struct before doing passes report_init(report); // we must init struct before doing passes
sanity.macro_recursions_left = config.sanity_limit; sanity.macro_recursions_left = config.sanity_limit;
sanity.source_recursions_left = config.sanity_limit; sanity.source_recursions_left = config.sanity_limit;
sanity.passes_left = config.sanity_limit; sanity.passes_left = config.sanity_limit;
pass.flags.complain_about_undefined = FALSE; // disable until error pass needed // first pass:
pass.flags.do_segment_checks = TRUE; // FIXME - do in _last_ pass instead! perform_pass(PF_THROW_SEGMENT_MESSAGES); // FIXME - check segments in all passes, but only throw errors/warnings in final pass!
perform_pass(); // first pass
pass.flags.do_segment_checks = FALSE; // FIXME - do in _last_ pass instead!
// 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;
// further passes:
// keep doing passes as long as the number of undefined results keeps decreasing. // keep doing passes as long as the number of undefined results keeps decreasing.
// stop on zero (FIXME - zero-check pass.counters.needvalue instead!) // stop on zero (FIXME - zero-check pass.counters.needvalue instead!)
while ((pass.counters.undefineds && (pass.counters.undefineds < undefs_before)) || pass.counters.symbolchanges) { while ((pass.counters.undefineds && (pass.counters.undefineds < undefs_before)) || pass.counters.symbolchanges) {
undefs_before = pass.counters.undefineds; undefs_before = pass.counters.undefineds;
perform_pass(); perform_pass(0);
if (--sanity.passes_left < 0) { if (--sanity.passes_left < 0) {
// FIXME - exit with error // FIXME - exit with error
// ...or maybe do one additional pass where all errors are reported, including "not defined" and "value has changed". // ...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; //break;
} }
} }
// last pass:
// any errors left? // any errors left?
if (pass.counters.undefineds == 0) { // FIXME - use pass.counters.needvalue instead! if (pass.counters.undefineds == 0) { // FIXME - use pass.counters.needvalue instead!
// if listing report is wanted and there were no errors, // victory lap
// do another pass to generate listing report if (config.process_verbosity >= 2)
if (config.report_filename) { puts("Extra pass to generate output.");
if (config.process_verbosity >= 2) perform_pass(PF_GENERATE_OUTPUT);
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();
} else { } else {
// There are still errors (unsolvable by doing further passes), // There are still errors (unsolvable by doing further passes),
// so perform additional pass to find and show them. // so perform additional pass to find and show them.
if (config.process_verbosity >= 2) if (config.process_verbosity >= 2)
puts("Extra pass to display errors."); puts("Extra pass to display errors.");
pass.flags.complain_about_undefined = TRUE; // activate error output perform_pass(PF_COMPLAIN_ABOUT_UNDEFINEDS); // perform pass, but now show "value undefined"
perform_pass(); // perform pass, but now show "value undefined"
// FIXME - perform_pass() calls exit() when there were errors, // FIXME - perform_pass() calls exit() when there were errors,
// so if controls returns here, call BUG()! // so if controls returns here, call BUG()!
// (this can be triggered using ifdef/ifndef) // (this can be triggered using ifdef/ifndef)

View File

@ -118,8 +118,9 @@ struct pass {
// error output pass. // error output pass.
} counters; } counters;
struct { struct {
char complain_about_undefined; // will be FALSE until error pass is needed boolean 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 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; } flags;
}; };
extern struct pass pass; extern struct pass pass;

View File

@ -99,7 +99,7 @@ static void border_crossed(int current_offset)
// further: // further:
if (current_offset >= config.outbuf_size) if (current_offset >= config.outbuf_size)
Throw_serious_error("Reached memory limit."); 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); 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
} }
@ -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 // remember current outbuf index as start/limit of output file
static boolean force_file_start = FALSE; static boolean force_file_start = FALSE;
static intval_t forced_start_idx; static intval_t forced_start_idx;
@ -228,16 +205,10 @@ void outbuf_set_outfile_limit(void)
// init output struct // init output struct
// FIXME - remove this, create the buffer at start of final pass instead!
void output_createbuffer(void) void output_createbuffer(void)
{ {
out->buffer = safe_malloc(config.outbuf_size); 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 // 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;
@ -272,6 +243,7 @@ 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 // 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 - 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) static void check_segment(intval_t new_pc)
{ {
struct segment *test_segment = out->segm.list_head.next; struct segment *test_segment = out->segm.list_head.next;
@ -297,9 +269,23 @@ void output_passinit(void)
{ {
// struct segment *temp; // 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? //FIXME - why clear ring list in every pass?
// Because later pass shouldn't complain about overwriting the same segment from earlier 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) // delete segment list (and free blocks)
// while ((temp = segment_list)) { // while ((temp = segment_list)) {
// segment_list = segment_list->next; // segment_list = segment_list->next;
@ -347,7 +333,8 @@ static void end_segment(void)
intval_t amount; intval_t amount;
// only do in first or last pass // 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; return;
// if there is no segment, there is nothing to do // 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 // allow writing to output buffer
output_byte = real_output; output_byte = real_output;
// in first/last pass, check for other segments and maybe issue warning // 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)) 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

@ -34,9 +34,6 @@ extern void output_skip(int size);
// FIXME - replace by output_sequence(char *src, size_t size) // FIXME - replace by output_sequence(char *src, size_t size)
extern void (*output_byte)(intval_t); 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 // remember current outbuf index as start/limit of output file
extern void outbuf_set_outfile_start(void); extern void outbuf_set_outfile_start(void);
extern void outbuf_set_outfile_limit(void); extern void outbuf_set_outfile_limit(void);

View File

@ -72,9 +72,6 @@ static enum eos po_initmem(void)
// remember value // remember value
config.mem_init_value = intresult.val.intval & 0xff; 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; return ENSURE_EOS;
} }
@ -537,6 +534,7 @@ static enum eos po_binary(void)
int byte; int byte;
struct number size, struct number size,
skip; skip;
int amount;
size.val.intval = -1; // means "not given" => "until EOF" size.val.intval = -1; // means "not given" => "until EOF"
skip.val.intval = 0; skip.val.intval = 0;
@ -571,8 +569,7 @@ static enum eos po_binary(void)
// check whether including is a waste of time // check whether including is a waste of time
// FIXME - future changes ("several-projects-at-once") // FIXME - future changes ("several-projects-at-once")
// may be incompatible with this! // may be incompatible with this!
if ((size.val.intval >= 0) && (pass.counters.undefineds || pass.counters.errors)) { if ((size.val.intval >= 0) && (!pass.flags.generate_output)) {
//if ((size.val.intval >= 0) && (pass.counters.needvalue || pass.counters.errors)) { FIXME - use!
output_skip(size.val.intval); // really including is useless anyway output_skip(size.val.intval); // really including is useless anyway
} else { } else {
// really insert file // really insert file
@ -593,16 +590,14 @@ static enum eos po_binary(void)
output_byte(0); output_byte(0);
} while (--size.val.intval); } 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); 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; return ENSURE_EOS;
} }

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 "2 Aug" // update before release FIXME #define CHANGE_DATE "3 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

View File

@ -15,7 +15,7 @@
otherstring = "Ford" otherstring = "Ford"
+a otherstring + emptystring + somestring == "FordArthur" +a otherstring + emptystring + somestring == "FordArthur"
collection = [] !set collection = []
!for c in "Trillian" { !for c in "Trillian" {
!set collection = collection + [c] !set collection = collection + [c]
} }

View File

@ -23,7 +23,7 @@ label4
lda d lda d
+a * - label4 == 3 +a * - label4 == 3
label5 label5
e+2=5 !set e+2=5
!for e+2, 17, 17 { !for e+2, 17, 17 {
lda e lda e
} }