mirror of
https://github.com/uffejakobsen/acme.git
synced 2024-11-22 03:30:46 +00:00
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:
parent
0af42c7a8a
commit
df7f1bf06b
71
src/acme.c
71
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;
|
||||
|
||||
if (config.process_verbosity >= 2)
|
||||
printf("Pass %d:\n", pass.number);
|
||||
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;
|
||||
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)
|
||||
// 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
|
||||
@ -391,17 +413,17 @@ static void do_actual_work(void)
|
||||
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) {
|
||||
// victory lap
|
||||
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();
|
||||
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)
|
||||
|
@ -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;
|
||||
|
55
src/output.c
55
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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
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();
|
||||
|
||||
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);
|
||||
return ENSURE_EOS;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -15,7 +15,7 @@
|
||||
otherstring = "Ford"
|
||||
+a otherstring + emptystring + somestring == "FordArthur"
|
||||
|
||||
collection = []
|
||||
!set collection = []
|
||||
!for c in "Trillian" {
|
||||
!set collection = collection + [c]
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user