mirror of
https://github.com/uffejakobsen/acme.git
synced 2025-02-07 06:31:03 +00:00
added "--from-to" (and did some internal cleanup)
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@334 4df02467-bbd4-4a76-a152-e7ce94205b78
This commit is contained in:
parent
10af90860d
commit
f52430f620
@ -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 \
|
||||
|
127
src/acme.c
127
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
|
||||
|
@ -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
|
||||
|
||||
|
50
src/global.c
50
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);
|
||||
|
11
src/global.h
11
src/global.h
@ -14,8 +14,8 @@
|
||||
#include <string.h>
|
||||
#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;
|
||||
|
||||
|
160
src/input.c
160
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 <literal>)
|
||||
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)
|
||||
|
18
src/input.h
18
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.
|
||||
|
57
src/output.c
57
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
|
||||
|
@ -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
|
||||
|
@ -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<escaping!
|
||||
// FIXME - there is another block like this in line 1317!
|
||||
if (input_unescape_dynabuf(0))
|
||||
return SKIP_REMAINDER; // escaping error
|
||||
|
||||
@ -467,7 +478,7 @@ static enum eos po_binary(void)
|
||||
skip.val.intval = 0;
|
||||
|
||||
// if file name is missing, don't bother continuing
|
||||
if (input_read_filename(TRUE, &uses_lib))
|
||||
if (input_read_input_filename(&uses_lib))
|
||||
return SKIP_REMAINDER;
|
||||
|
||||
// try to open file
|
||||
@ -767,7 +778,7 @@ static enum eos po_symbollist(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 process this pseudo opcode in first pass
|
||||
@ -849,7 +860,7 @@ static enum eos po_source(void) // now GotByte = illegal char
|
||||
if (--source_recursions_left < 0)
|
||||
Throw_serious_error("Too deeply nested. Recursive \"!source\"?");
|
||||
// read file name. quit function on error
|
||||
if (input_read_filename(TRUE, &uses_lib))
|
||||
if (input_read_input_filename(&uses_lib))
|
||||
return SKIP_REMAINDER;
|
||||
|
||||
// if file could be opened, parse it. otherwise, complain
|
||||
@ -1302,6 +1313,8 @@ static enum eos throw_src_string(enum debuglevel level, const char prefix[])
|
||||
// eat closing quote
|
||||
GetByte();
|
||||
// now convert to unescaped version
|
||||
// FIXME - next call does nothing because wanted<escaping!
|
||||
// FIXME - there is another block like this in line 416!
|
||||
if (input_unescape_dynabuf(0))
|
||||
return SKIP_REMAINDER; // escaping error
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
#define RELEASE "0.97" // update before release FIXME
|
||||
#define CODENAME "Zem" // update before release
|
||||
#define CHANGE_DATE "10 Feb" // update before release FIXME
|
||||
#define CHANGE_DATE "11 Feb" // 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
|
||||
|
Loading…
x
Reference in New Issue
Block a user