diff --git a/src/_riscos.h b/src/_riscos.h index 5523841..ae862c4 100644 --- a/src/_riscos.h +++ b/src/_riscos.h @@ -45,7 +45,7 @@ do { \ // output of platform-specific command line switches #define PLATFORM_OPTION_HELP \ -" -t, --throwback use the DDEUtils module's \"throwback\" protocol\n" +" -t, --throwback use the DDEUtils module's \"throwback\" protocol\n" // processing of platform-specific command line switches #define PLATFORM_SHORTOPTION_CODE \ diff --git a/src/acme.c b/src/acme.c index ae63ddc..f0d6ef5 100644 --- a/src/acme.c +++ b/src/acme.c @@ -54,6 +54,7 @@ static const char arg_vicelabels[] = "VICE labels filename"; #define OPTION_VICELABELS "vicelabels" #define OPTION_REPORT "report" #define OPTION_SETPC "setpc" +#define OPTION_FROM_TO "from-to" #define OPTION_CPU "cpu" #define OPTION_INITMEM "initmem" #define OPTION_MAXERRORS "maxerrors" @@ -79,9 +80,6 @@ static const char arg_vicelabels[] = "VICE labels filename"; // variables static const char **toplevel_sources; static int toplevel_src_count = 0; -#define ILLEGAL_START_ADDRESS (-1) -static signed long start_address = ILLEGAL_START_ADDRESS; -static signed long fill_value = MEMINIT_USE_DEFAULT; static const struct cpu_type *default_cpu = NULL; const char *symbollist_filename = NULL; const char *vicelabels_filename = NULL; @@ -124,40 +122,41 @@ static void show_help_and_exit(void) "acme [OPTION...] [FILE]...\n" "\n" "Options:\n" -" -h, --" OPTION_HELP " show this help and exit\n" -" -f, --" OPTION_FORMAT " FORMAT set output file format\n" -" -o, --" OPTION_OUTFILE " FILE set output file name\n" -" -r, --" OPTION_REPORT " FILE set report file name\n" -" -l, --" OPTION_SYMBOLLIST " FILE set symbol list file name\n" -" --" OPTION_LABELDUMP " (old name for --" OPTION_SYMBOLLIST ")\n" -" --" OPTION_VICELABELS " FILE set file name for label dump in VICE format\n" -" --" OPTION_SETPC " VALUE set program counter\n" -" --" OPTION_CPU " CPU set target processor\n" -" --" OPTION_INITMEM " VALUE define 'empty' memory\n" -" --" OPTION_MAXERRORS " NUMBER set number of errors before exiting\n" -" --" OPTION_MAXDEPTH " NUMBER set recursion depth for macro calls and !src\n" -" --" OPTION_IGNORE_ZEROES " do not determine number size by leading zeroes\n" -" --" OPTION_STRICT_SEGMENTS " turn segment overlap warnings into errors\n" -" --" OPTION_STRICT " treat all warnings like errors\n" -" -vDIGIT set verbosity level\n" -" -DSYMBOL=VALUE define global symbol\n" -" -I PATH/TO/DIR add search path for input files\n" +" -h, --" OPTION_HELP " show this help and exit\n" +" -f, --" OPTION_FORMAT " FORMAT set output file format\n" +" -o, --" OPTION_OUTFILE " FILE set output file name\n" +" -r, --" OPTION_REPORT " FILE set report file name\n" +" -l, --" OPTION_SYMBOLLIST " FILE set symbol list file name\n" +" --" OPTION_LABELDUMP " (old name for --" OPTION_SYMBOLLIST ")\n" +" --" OPTION_VICELABELS " FILE set file name for label dump in VICE format\n" +" --" OPTION_SETPC " VALUE set program counter\n" +" --" OPTION_FROM_TO " VALUE VALUE set start and end of output file\n" +" --" OPTION_CPU " CPU set target processor\n" +" --" OPTION_INITMEM " VALUE define 'empty' memory\n" +" --" OPTION_MAXERRORS " NUMBER set number of errors before exiting\n" +" --" OPTION_MAXDEPTH " NUMBER set recursion depth for macro calls and !src\n" +" --" OPTION_IGNORE_ZEROES " do not determine number size by leading zeroes\n" +" --" OPTION_STRICT_SEGMENTS " turn segment overlap warnings into errors\n" +" --" OPTION_STRICT " treat all warnings like errors\n" +" -vDIGIT set verbosity level\n" +" -DSYMBOL=VALUE define global symbol\n" +" -I PATH/TO/DIR add search path for input files\n" // TODO: replace these: -" -W" OPTIONWNO_LABEL_INDENT " suppress warnings about indented labels\n" -" -W" OPTIONWNO_OLD_FOR " (old, use \"--dialect 0.94.8\" instead)\n" -" -W" OPTIONWNO_BIN_LEN " suppress warnings about lengths of binary literals\n" -" -W" OPTIONWTYPE_MISMATCH " enable type checking (warn about type mismatch)\n" +" -W" OPTIONWNO_LABEL_INDENT " suppress warnings about indented labels\n" +" -W" OPTIONWNO_OLD_FOR " (old, use \"--dialect 0.94.8\" instead)\n" +" -W" OPTIONWNO_BIN_LEN " suppress warnings about lengths of binary literals\n" +" -W" OPTIONWTYPE_MISMATCH " enable type checking (warn about type mismatch)\n" // with this line and add a separate function: //" -W show warning level options\n" -" --" OPTION_USE_STDOUT " fix for 'Relaunch64' IDE (see docs)\n" -" --" OPTION_MSVC " output errors in MS VS format\n" -" --" OPTION_COLOR " use ANSI color codes for error output\n" -" --" OPTION_FULLSTOP " use '.' as pseudo opcode prefix\n" -" --" OPTION_DIALECT " VERSION behave like different version\n" -" --" OPTION_DEBUGLEVEL " VALUE drop all higher-level debug messages\n" -" --" OPTION_TEST " enable experimental features\n" +" --" OPTION_USE_STDOUT " fix for 'Relaunch64' IDE (see docs)\n" +" --" OPTION_MSVC " output errors in MS VS format\n" +" --" OPTION_COLOR " use ANSI color codes for error output\n" +" --" OPTION_FULLSTOP " use '.' as pseudo opcode prefix\n" +" --" OPTION_DIALECT " VERSION behave like different version\n" +" --" OPTION_DEBUGLEVEL " VALUE drop all higher-level debug messages\n" +" --" OPTION_TEST " enable experimental features\n" PLATFORM_OPTION_HELP -" -V, --" OPTION_VERSION " show version and exit\n"); +" -V, --" OPTION_VERSION " show version and exit\n"); exit(EXIT_SUCCESS); } @@ -256,8 +255,8 @@ static void perform_pass(void) output_passinit(); // disable output, PC undefined cputype_passinit(default_cpu); // set default cpu type // if start address was given on command line, use it: - if (start_address != ILLEGAL_START_ADDRESS) - vcpu_set_pc(start_address, 0); + if (config.initial_pc != NO_VALUE_GIVEN) + vcpu_set_pc(config.initial_pc, 0); // 0 -> no segment flags encoding_passinit(); // set default encoding section_passinit(); // set initial zone (untitled) // init variables @@ -419,29 +418,27 @@ static signed long string_to_number(const char *string) could_not_parse(end); return result; } - - -// set program counter -static void set_starting_pc(const char expression[]) +// wrapper for fn above: complain about negative numbers +static signed long string_to_nonneg_number(const char *string) { - start_address = string_to_number(expression); - if ((start_address > -1) && (start_address < 65536)) - return; + signed long result = string_to_number(string); - fprintf(stderr, "%sProgram counter out of range (0-0xffff).\n", cliargs_error); - exit(EXIT_FAILURE); + if (result < 0) { + fprintf(stderr, "%sInvalid value, number is negative: '%s'.\n", cliargs_error, string); + exit(EXIT_FAILURE); + } + return result; } // set initial memory contents static void set_mem_contents(const char expression[]) { - fill_value = string_to_number(expression); - if ((fill_value >= -128) && (fill_value <= 255)) - return; - - fprintf(stderr, "%sInitmem value out of range (0-0xff).\n", cliargs_error); - exit(EXIT_FAILURE); + config.mem_init_value = string_to_number(expression); + if ((config.mem_init_value < -128) || (config.mem_init_value > 255)) { + fprintf(stderr, "%sInitmem value out of range (0-0xff).\n", cliargs_error); + exit(EXIT_FAILURE); + } } @@ -533,8 +530,11 @@ static const char *long_option(const char *string) else if (strcmp(string, OPTION_REPORT) == 0) report_filename = cliargs_safe_get_next(arg_reportfile); else if (strcmp(string, OPTION_SETPC) == 0) - set_starting_pc(cliargs_safe_get_next("program counter")); - else if (strcmp(string, OPTION_CPU) == 0) + config.initial_pc = string_to_nonneg_number(cliargs_safe_get_next("program counter")); + else if (strcmp(string, OPTION_FROM_TO) == 0) { + config.outfile_start = string_to_nonneg_number(cliargs_safe_get_next("start address of output file")); + config.outfile_end = string_to_nonneg_number(cliargs_safe_get_next("end address of output file")); + } else if (strcmp(string, OPTION_CPU) == 0) set_starting_cpu(cliargs_get_next()); // NULL is ok (handled like unknown) else if (strcmp(string, OPTION_INITMEM) == 0) set_mem_contents(cliargs_safe_get_next("initmem value")); @@ -563,6 +563,7 @@ static const char *long_option(const char *string) else if (strcmp(string, OPTION_TEST) == 0) { config.wanted_version = VER_FUTURE; config.test_new_features = TRUE; + config.outbuf_size = 0x1000000; // 16 MiB (FIXME - give it its own cli switch!) } PLATFORM_LONGOPTION_CODE else if (strcmp(string, OPTION_COLOR) == 0) config.format_color = TRUE; @@ -656,8 +657,28 @@ int main(int argc, const char *argv[]) cliargs_handle_options(short_option, long_option); // generate list of files to process cliargs_get_rest(&toplevel_src_count, &toplevel_sources, "No top level sources given"); + + // now that we have processed all cli switches, check a few values for + // valid range: + if ((config.initial_pc != NO_VALUE_GIVEN) && (config.initial_pc >= config.outbuf_size)) { + fprintf(stderr, "%sProgram counter exceeds outbuffer size.\n", cliargs_error); + exit(EXIT_FAILURE); + } + if ((config.outfile_start != NO_VALUE_GIVEN) && (config.outfile_start >= config.outbuf_size)) { + fprintf(stderr, "%sStart address of output file exceeds outbuffer size.\n", cliargs_error); + exit(EXIT_FAILURE); + } + if ((config.outfile_end != NO_VALUE_GIVEN) && (config.outfile_end >= config.outbuf_size)) { + fprintf(stderr, "%sEnd address of output file exceeds outbuffer size.\n", cliargs_error); + exit(EXIT_FAILURE); + } + if (config.outfile_start > config.outfile_end) { + fprintf(stderr, "%sStart address of output file exceeds end address.\n", cliargs_error); + exit(EXIT_FAILURE); + } + // init output buffer - output_createbuffer(fill_value, /* use_large_buf= */ config.test_new_features); + output_createbuffer(); if (do_actual_work()) save_output_file(); return ACME_finalize(EXIT_SUCCESS); // dump labels, if wanted diff --git a/src/config.h b/src/config.h index 8d6dfc6..f79527b 100644 --- a/src/config.h +++ b/src/config.h @@ -72,8 +72,6 @@ struct listitem { // maximum nesting depth of "!src" and macro calls // is not actually a limitation, but a means of finding recursions #define MAX_NESTING 64 -// default value for output buffer -#define FILLVALUE_INITIAL 0 // default value for "!fill" #define FILLVALUE_FILL 0 diff --git a/src/global.c b/src/global.c index e095a32..efef33e 100644 --- a/src/global.c +++ b/src/global.c @@ -122,6 +122,11 @@ void config_default(struct config *conf) conf->test_new_features = FALSE; // enabled by --test conf->wanted_version = VER_CURRENT; // changed by --dialect conf->debuglevel = DEBUGLEVEL_DEBUG; // changed by --debuglevel, used by "!debug" + conf->outbuf_size = 0x10000; // 64K, "--test" changes to 16M + conf->mem_init_value = MEMINIT_USE_DEFAULT; // set by --initmem + conf->initial_pc = NO_VALUE_GIVEN; // set by --setpc + conf->outfile_start = NO_VALUE_GIVEN; // set by --from-to + conf->outfile_end = NO_VALUE_GIVEN; // set by --from-to } // memory allocation stuff @@ -164,8 +169,8 @@ boolean parser_change_nowarn_block_flag(boolean new_value) return old_value; } -#define SF_FOUND_BLANK (1u << 0) // statement had space or tab -#define SF_IMPLIED_LABEL (1u << 1) // statement had implied label def +#define SF_FOUND_BLANK (1u << 0) // statement started with space or tab +#define SF_FOUND_SYMBOL (1u << 1) // statement had label or symbol definition #define SF_ADDR_PREFIX (1u << 2) // explicit symbol definition is an address #define SF_NOWARN_PREFIX (1u << 3) // suppress warnings for this statement static bits statement_flags; @@ -181,15 +186,15 @@ extern void parser_set_nowarn_prefix(void) statement_flags |= SF_NOWARN_PREFIX; } -// Check and return whether first label of statement. Complain if not. -static int first_label_of_statement(void) +// Check and return whether first symbol of statement. Complain if not. +static int first_symbol_of_statement(void) { - if (statement_flags & SF_IMPLIED_LABEL) { + if (statement_flags & SF_FOUND_SYMBOL) { Throw_error(exception_syntax); input_skip_remainder(); return FALSE; } - statement_flags |= SF_IMPLIED_LABEL; // now there has been one + statement_flags |= SF_FOUND_SYMBOL; // now there has been one return TRUE; } @@ -270,20 +275,21 @@ static void parse_symbol_definition(scope_t scope) // Parse global symbol definition or assembler mnemonic static void parse_mnemo_or_global_symbol_def(void) { - boolean is_mnemonic; + // read keyword and ask current cpu type if it's a mnemonic + if (CPU_state.type->keyword_is_mnemonic(input_read_keyword())) + return; // statement has been handled - is_mnemonic = CPU_state.type->keyword_is_mnemonic(input_read_keyword()); - // It is only a label if it isn't a mnemonic - if ((!is_mnemonic) - && first_label_of_statement()) { - // Now GotByte = illegal char - // 04 Jun 2005: this fix should help to explain "strange" error messages. - // 17 May 2014: now it works for UTF-8 as well. - if ((*GLOBALDYNABUF_CURRENT == (char) 0xa0) - || ((GlobalDynaBuf->size >= 2) && (GLOBALDYNABUF_CURRENT[0] == (char) 0xc2) && (GLOBALDYNABUF_CURRENT[1] == (char) 0xa0))) - Throw_first_pass_warning("Label name starts with a shift-space character."); - parse_symbol_definition(SCOPE_GLOBAL); - } + // if we're here, it wasn't a mnemonic, so it can only be a symbol name + if (!first_symbol_of_statement()) + return; // more than one symbol, error has been reported + + // Now GotByte = illegal char + // 04 Jun 2005: this fix should help to explain "strange" error messages. + // 17 May 2014: now it works for UTF-8 as well. + if ((*GLOBALDYNABUF_CURRENT == (char) 0xa0) + || ((GlobalDynaBuf->size >= 2) && (GLOBALDYNABUF_CURRENT[0] == (char) 0xc2) && (GLOBALDYNABUF_CURRENT[1] == (char) 0xa0))) + Throw_first_pass_warning("Symbol name starts with a shift-space character."); + parse_symbol_definition(SCOPE_GLOBAL); } @@ -292,7 +298,7 @@ static void parse_local_symbol_def(void) { scope_t scope; - if (!first_label_of_statement()) + if (!first_symbol_of_statement()) return; if (input_read_scope_and_symbol_name(&scope) == 0) @@ -303,7 +309,7 @@ static void parse_local_symbol_def(void) // parse anonymous backward label definition. Called with GotByte == '-' static void parse_backward_anon_def(void) { - if (!first_label_of_statement()) + if (!first_symbol_of_statement()) return; dynabuf_clear(GlobalDynaBuf); @@ -319,7 +325,7 @@ static void parse_backward_anon_def(void) // parse anonymous forward label definition. called with GotByte == ? static void parse_forward_anon_def(void) { - if (!first_label_of_statement()) + if (!first_symbol_of_statement()) return; dynabuf_clear(GlobalDynaBuf); diff --git a/src/global.h b/src/global.h index daa8314..818b5d4 100644 --- a/src/global.h +++ b/src/global.h @@ -14,8 +14,8 @@ #include #include "config.h" -#define LOCAL_PREFIX '.' // FIXME - this is not yet used consistently! -#define CHEAP_PREFIX '@' // prefix character for cheap locals +#define LOCAL_PREFIX '.' // FIXME - this is not yet used consistently! +#define CHEAP_PREFIX '@' // prefix character for cheap locals // Constants @@ -77,6 +77,13 @@ struct config { boolean test_new_features; // FALSE, enabled by --test enum version wanted_version; // set by --dialect (and --test --test) signed long debuglevel; // set by --debuglevel, used by "!debug" + signed long outbuf_size; // 64K, "--test" changes to 16M +#define MEMINIT_USE_DEFAULT 256 // default value for next field if cli switch not used: + signed long mem_init_value; // set by --initmem +#define NO_VALUE_GIVEN (-1) // default value for these fields if cli switch not used: + signed long initial_pc; // set by --setpc + signed long outfile_start; // set by --from-to + signed long outfile_end; // set by --from-to }; extern struct config config; diff --git a/src/input.c b/src/input.c index 7c9a1ea..a4c576d 100644 --- a/src/input.c +++ b/src/input.c @@ -339,11 +339,25 @@ static char GetQuotedByte(void) } // Skip remainder of statement, for example on error -// FIXME - check for quotes, otherwise this might treat a quoted colon like EOS! void input_skip_remainder(void) { - while (GotByte) + while (GotByte) { GetByte(); // Read characters until end-of-statement + } +/* FIXME - check for quotes, otherwise this might treat a quoted colon like EOS! +this has already been a bug with "!to" and "!sl" where a workaround was implemented. +fix it here, once and for all, maybe like this: + dynabuf_clear(GlobalDynaBuf); + while (GotByte != CHAR_EOS) { + // check for quotes + if ((GotByte == '"') || (GotByte == '\'')) { + if (input_quoted_to_dynabuf(GotByte)) + break; // error (CHAR_EOS before closing quote) + } + GetByte(); + } + dynabuf_clear(GlobalDynaBuf); +*/ } // Ensure that the remainder of the current statement is empty, for example @@ -599,33 +613,51 @@ int input_read_and_lower_keyword(void) return length; } -// Try to read a file name. -// If "allow_library" is TRUE, library access by using <...> quoting -// is possible as well. If "uses_lib" is non-NULL, info about library -// usage is stored there. +// shared ending when trying to read a file name. // The file name given in the assembler source code is converted from // UNIX style to platform style. // Returns nonzero on error. Filename in GlobalDynaBuf. // Errors are handled and reported, but caller should call // input_skip_remainder() then. -int input_read_filename(boolean allow_library, boolean *uses_lib) +static int read_filename_shared_end(int prefix_size) { - int start_of_string; - char *lib_prefix, - terminator; + // check length + if (GlobalDynaBuf->size == prefix_size) { + Throw_error("No file name given."); + return 1; // error + } + + // resolve backslash escapes + if (input_unescape_dynabuf(prefix_size)) + return 1; // escaping error + + // terminate string + dynabuf_append(GlobalDynaBuf, '\0'); +#ifdef PLATFORM_CONVERTPATH + // platform-specific path name conversion + PLATFORM_CONVERTPATH(GLOBALDYNABUF_CURRENT + prefix_size); +#endif + return 0; // ok +} + +// try to read a file name for an input file. +// library access by using <...> quoting is allowed. function will store info +// about library usage at "uses_lib" ptr. +// The file name given in the assembler source code is converted from +// UNIX style to platform style. +// Returns nonzero on error. Filename in GlobalDynaBuf. +// Errors are handled and reported, but caller should call +// input_skip_remainder() then. +int input_read_input_filename(boolean *uses_lib) +{ + char *lib_prefix; // depends on platform + int prefix_size; // this much does not get platform-converted because it is already correct dynabuf_clear(GlobalDynaBuf); SKIPSPACE(); - switch (GotByte) { - case '<': // library access - if (uses_lib) - *uses_lib = TRUE; - // if library access forbidden, complain - if (!allow_library) { - Throw_error("Writing to library not supported."); - return 1; // error - } - + if (GotByte == '<') { + // library access: + *uses_lib = TRUE; // read platform's lib prefix lib_prefix = PLATFORM_LIBPREFIX; #ifndef NO_NEED_FOR_ENV_VAR @@ -635,45 +667,71 @@ int input_read_filename(boolean allow_library, boolean *uses_lib) return 1; // error } #endif - // copy lib path and set quoting char + // copy lib path dynabuf_add_string(GlobalDynaBuf, lib_prefix); - terminator = '>'; - break; - case '"': // normal access - if (uses_lib) - *uses_lib = FALSE; - terminator = '"'; - break; - default: // none of the above - Throw_error("File name quotes not found (\"\" or <>)."); + // remember border between optional library prefix and string from assembler source file + prefix_size = GlobalDynaBuf->size; + // read file name string (must be a single string ) + if (input_quoted_to_dynabuf('>')) + return 1; // unterminated or escaping error + + GetByte(); // eat '>' terminator + } else { + // "normal", non-library access: + *uses_lib = FALSE; + prefix_size = 0; // no prefix in DynaBuf +// old algo (do not merge with similar parts from "if" block!): + if (GotByte != '"') { + Throw_error("File name quotes not found (\"\" or <>)."); + return 1; // error + } + // read file name string + if (input_quoted_to_dynabuf('"')) + return 1; // unterminated or escaping error + + GetByte(); // eat terminator +// new algo: +// it should be possible to construct the name of input file from symbols, so +// build environments can define a name at one place and use it at another. +// FIXME - use expression parser to read filename string! +// see lines 416 and 1317 in pseudoopcodes.c for two more possible callers! + } + // check length, unescape, terminate, do platform conversion + return read_filename_shared_end(prefix_size); +} + +// try to read a file name for an output file. +// library access by using <...> quoting is forbidden. +// The file name given in the assembler source code is converted from +// UNIX style to platform style. +// Returns nonzero on error. Filename in GlobalDynaBuf. +// Errors are handled and reported, but caller should call +// input_skip_remainder() then. +// +// this is only used for "!to" and "!sl", i.e. output file names. these +// must be given as a literal string, and it should be kept this way. +int input_read_output_filename(void) +{ + SKIPSPACE(); + if (GotByte == '<') { + Throw_error("Writing to library not supported."); return 1; // error } - // remember border between optional library prefix and string from assembler source file - start_of_string = GlobalDynaBuf->size; - // read file name string - if (input_quoted_to_dynabuf(terminator)) + if (GotByte != '"') { + Throw_error("File name quotes not found (\"\")."); + return 1; // error + } + dynabuf_clear(GlobalDynaBuf); + // read file name string (must be a single string literal! do not change this!) + if (input_quoted_to_dynabuf('"')) return 1; // unterminated or escaping error GetByte(); // eat terminator - // check length - if (GlobalDynaBuf->size == start_of_string) { - Throw_error("No file name given."); - return 1; // error - } - - // resolve backslash escapes - if (input_unescape_dynabuf(start_of_string)) - return 1; // escaping error - - // terminate string - dynabuf_append(GlobalDynaBuf, '\0'); -#ifdef PLATFORM_CONVERTPATH - // platform-specific path name conversion - PLATFORM_CONVERTPATH(GLOBALDYNABUF_CURRENT + start_of_string); -#endif - return 0; // ok + // check length, unescape, terminate, do platform conversion: + return read_filename_shared_end(0); // 0 -> there is no library prefix } + // Try to read a comma, skipping spaces before and after. Return TRUE if comma // found, otherwise FALSE. int input_accept_comma(void) diff --git a/src/input.h b/src/input.h index ab70388..cd9cbc7 100644 --- a/src/input.h +++ b/src/input.h @@ -116,16 +116,24 @@ extern int input_read_keyword(void); // Zero lengths will produce a "missing string" error. extern int input_read_and_lower_keyword(void); -// Try to read a file name. -// If "allow_library" is TRUE, library access by using <...> quoting -// is possible as well. If "uses_lib" is non-NULL, info about library -// usage is stored there. +// try to read a file name for an input file. +// library access by using <...> quoting is allowed. function will store info +// about library usage at "uses_lib" ptr. // The file name given in the assembler source code is converted from // UNIX style to platform style. // Returns nonzero on error. Filename in GlobalDynaBuf. // Errors are handled and reported, but caller should call // input_skip_remainder() then. -extern int input_read_filename(boolean library_allowed, boolean *uses_lib); +extern int input_read_input_filename(boolean *uses_lib); + +// try to read a file name for an output file ("!to" and "!sl" only). +// library access by using <...> quoting is forbidden. +// The file name given in the assembler source code is converted from +// UNIX style to platform style. +// Returns nonzero on error. Filename in GlobalDynaBuf. +// Errors are handled and reported, but caller should call +// input_skip_remainder() then. +extern int input_read_output_filename(void); // Try to read a comma, skipping spaces before and after. Return TRUE if comma // found, otherwise FALSE. diff --git a/src/output.c b/src/output.c index 736a7d5..8544bb5 100644 --- a/src/output.c +++ b/src/output.c @@ -38,8 +38,7 @@ struct segment { // structure for all output stuff: struct output { // output buffer stuff - intval_t bufsize; // either 64 KiB or 16 MiB - char *buffer; // holds assembled code + char *buffer; // holds assembled code (size is config.outbuf_size) intval_t write_idx; // index of next write intval_t lowest_written; // smallest address used intval_t highest_written; // largest address used @@ -111,7 +110,7 @@ static void find_segment_max(intval_t new_pc) while (test_segment->start <= new_pc) test_segment = test_segment->next; if (test_segment == &out->segment.list_head) - out->segment.max = out->bufsize - 1; + out->segment.max = config.outbuf_size - 1; else out->segment.max = test_segment->start - 1; // last free address available } @@ -120,7 +119,7 @@ static void find_segment_max(intval_t new_pc) // static void border_crossed(int current_offset) { - if (current_offset >= out->bufsize) + if (current_offset >= config.outbuf_size) Throw_serious_error("Produced too much code."); // TODO - get rid of FIRST_PASS condition, because user can suppress these warnings if they want if (FIRST_PASS) { @@ -206,7 +205,7 @@ void output_skip(int size) // fill output buffer with given byte value static void fill_completely(char value) { - memset(out->buffer, value, out->bufsize); + memset(out->buffer, value, config.outbuf_size); } @@ -276,42 +275,54 @@ int outputfile_set_filename(void) // init output struct (done later) -void output_createbuffer(signed long fill_value, boolean use_large_buf) +void output_createbuffer(void) { - out->bufsize = use_large_buf ? 0x1000000 : 0x10000; - out->buffer = safe_malloc(out->bufsize); - if (fill_value == MEMINIT_USE_DEFAULT) { - fill_value = FILLVALUE_INITIAL; - out->initvalue_set = FALSE; + char fill_value = 0; // default value for output buffer + + out->buffer = safe_malloc(config.outbuf_size); + if (config.mem_init_value == MEMINIT_USE_DEFAULT) { + out->initvalue_set = FALSE; // "!initmem" can be used } else { - out->initvalue_set = TRUE; + out->initvalue_set = TRUE; // "!initmem" generates a warning + fill_value = 0xff & config.mem_init_value; } // init output buffer (fill memory with initial value) - fill_completely(fill_value & 0xff); + fill_completely(fill_value); // init ring list of segments out->segment.list_head.next = &out->segment.list_head; out->segment.list_head.prev = &out->segment.list_head; } -// dump used portion of output buffer into output file +// write used portion of output buffer to output file void output_save_file(FILE *fd) { intval_t start, + end, amount; - if (out->highest_written < out->lowest_written) { + start = out->lowest_written; + end = out->highest_written; + // if cli args were given, they override the actual values: + if (config.outfile_start != NO_VALUE_GIVEN) + start = config.outfile_start; + if (config.outfile_end != NO_VALUE_GIVEN) + end = config.outfile_end; + + if (end < start) { // nothing written start = 0; // I could try to use some segment start, but what for? amount = 0; + // FIXME - how about not writing anything in this case? + // a CBM file would consist of a bogus load address and nothing else! } else { - start = out->lowest_written; - amount = out->highest_written - start + 1; + amount = end - start + 1; } if (config.process_verbosity) printf("Saving %ld (0x%lx) bytes (0x%lx - 0x%lx exclusive).\n", amount, amount, start, start + amount); // output file header according to file format + // FIXME - add checks and error messages for "start is above $ffff"!) switch (output_format) { case OUTPUT_FORMAT_APPLE: PLATFORM_SETFILETYPE_APPLE(output_filename); @@ -402,13 +413,13 @@ void output_passinit(void) // } // invalidate start and end (first byte actually written will fix them) - out->lowest_written = out->bufsize - 1; + out->lowest_written = config.outbuf_size - 1; out->highest_written = 0; // deactivate output - any byte written will trigger error: output_byte = no_output; out->write_idx = 0; // same as pc on pass init! out->segment.start = NO_SEGMENT_START; // TODO - "no active segment" could be made a segment flag! - out->segment.max = out->bufsize - 1; // TODO - use end of bank? + out->segment.max = config.outbuf_size - 1; // TODO - use end of bank? out->segment.flags = 0; out->xor = 0; @@ -467,7 +478,7 @@ void output_start_segment(intval_t address_change, bits segment_flags) output_end_segment(); // calculate start of new segment - out->write_idx = (out->write_idx + address_change) & (out->bufsize - 1); + out->write_idx = (out->write_idx + address_change) & (config.outbuf_size - 1); out->segment.start = out->write_idx; out->segment.flags = segment_flags; // allow writing to output buffer @@ -568,7 +579,7 @@ int vcpu_get_statement_size(void) // adjust program counter (called at end of each statement) void vcpu_end_statement(void) { - CPU_state.pc.val.intval = (CPU_state.pc.val.intval + CPU_state.add_to_pc) & (out->bufsize - 1); + CPU_state.pc.val.intval = (CPU_state.pc.val.intval + CPU_state.add_to_pc) & (config.outbuf_size - 1); CPU_state.add_to_pc = 0; } @@ -607,7 +618,7 @@ void pseudopc_end(void) if (config.wanted_version >= VER_DISABLED_OBSOLETE_STUFF) BUG("ClosingUnopenedPseudopcBlock", 0); } else { - CPU_state.pc.val.intval = (CPU_state.pc.val.intval - pseudopc_current_context->offset) & (out->bufsize - 1); // pc might have wrapped around + CPU_state.pc.val.intval = (CPU_state.pc.val.intval - pseudopc_current_context->offset) & (config.outbuf_size - 1); // pc might have wrapped around CPU_state.pc.ntype = pseudopc_current_context->ntype; pseudopc_current_context = pseudopc_current_context->outer; // go back to outer block } @@ -631,7 +642,7 @@ int pseudopc_unpseudo(struct number *target, struct pseudopc *context, unsigned return 1; // error } // FIXME - in future, check both target and context for NUMTYPE_UNDEFINED! - target->val.intval = (target->val.intval - context->offset) & (out->bufsize - 1); // FIXME - is masking really needed? TODO + target->val.intval = (target->val.intval - context->offset) & (config.outbuf_size - 1); // FIXME - is masking really needed? TODO context = context->outer; } return 0; // ok diff --git a/src/output.h b/src/output.h index 3081c3c..e60c01a 100644 --- a/src/output.h +++ b/src/output.h @@ -12,7 +12,7 @@ // constants -#define MEMINIT_USE_DEFAULT 256 + // segment flags #define SEGMENT_FLAG_OVERLAY (1u << 0) // do not warn about this segment overwriting another one #define SEGMENT_FLAG_INVISIBLE (1u << 1) // do not warn about other segments overwriting this one @@ -43,7 +43,7 @@ extern void output_passinit(void); // outbuf stuff: // alloc and init mem buffer (done later) -extern void output_createbuffer(signed long fill_value, boolean use_large_buf); +extern void output_createbuffer(void); // skip over some bytes in output buffer without starting a new segment // (used by "!skip", and also called by "!binary" if really calling @@ -72,7 +72,7 @@ extern int outputfile_prefer_cbm_format(void); // try to set output file name held in DynaBuf. Returns zero on success. extern int outputfile_set_filename(void); -// write smallest-possible part of memory buffer to file +// write used portion of output buffer to output file extern void output_save_file(FILE *fd); // change output pointer and enable output diff --git a/src/pseudoopcodes.c b/src/pseudoopcodes.c index c71fd82..0ed7032 100644 --- a/src/pseudoopcodes.c +++ b/src/pseudoopcodes.c @@ -136,7 +136,7 @@ static enum eos po_to(void) // read filename to global dynamic buffer // if no file name given, exit (complaining will have been done) - if (input_read_filename(FALSE, NULL)) + if (input_read_output_filename()) return SKIP_REMAINDER; // only act upon this pseudo opcode in first pass @@ -360,6 +360,15 @@ static enum eos predefined_encoding(void) } // set current encoding ("!convtab" pseudo opcode) // (allows for block, so must be reentrant) +// FIXME: current code does not allow for stuff like +// !convtab some_string_symbol + ".bin" +// because anything not starting with '<' or '"' is supposed to be a keyword. +// maybe fix this by using +// !convtab file = base + ".txt" +// in the future? +// another workaround would be: +// !convtab "" + some_string_symbol + ".bin" +// but even then input_read_input_filename needs to be fixed! static enum eos po_convtab(void) { boolean uses_lib; @@ -367,7 +376,7 @@ static enum eos po_convtab(void) if ((GotByte == '<') || (GotByte == '"')) { // encoding table from file - if (input_read_filename(TRUE, &uses_lib)) + if (input_read_input_filename(&uses_lib)) return SKIP_REMAINDER; // missing or unterminated file name stream = includepaths_open_ro(uses_lib); @@ -403,6 +412,8 @@ static enum eos encode_string(const struct encoder *inner_encoder, unsigned char // eat closing quote GetByte(); // now convert to unescaped version + // FIXME - next call does nothing because wanted