diff --git a/docs/AllPOs.txt b/docs/AllPOs.txt index b9cd335..0cc0e69 100644 --- a/docs/AllPOs.txt +++ b/docs/AllPOs.txt @@ -107,6 +107,23 @@ Examples: !be32 $7fffffff, symbol, -$80000000, 14, $46a4f35 !be32 300000 - 4, a AND a2, 2 ^ tz, (3+4)*70, l1 & .j2 +Call: !hex PAIRS_OF_HEX_DIGITS +Purpose: Insert byte values with a minimum of additional syntax. + This pseudo opcode was added for easier writing of external + source code generator tools. +Parameters: PAIRS_OF_HEX_DIGITS: Just hexadecimal digits, without any + "0x" or "$" prefix. Spaces and TABs are allowed, but not + needed to separate the byte values. +Aliases: "!h" +Examples: !h f0 f1 f2 f3 f4 f5 f6 f7 ; insert values 0xf0..0xf7 + !h f0f1f2f3 f4f5f6f7 ; insert values 0xf0..0xf7 + !h f0f1f2f3f4f5f6f7 ; insert values 0xf0..0xf7 + !h f0f 1f2 ; ERROR: space inside pair! + !h 0x00, $00 ; ERROR: "0x", "," and "$" are forbidden! + !h SOME_SYMBOL ; ERROR: symbols are forbidden! + !h ABCD ; insert value 0xAB, then 0xCD (CAUTION, big-endian) + + Call: !fill AMOUNT [, VALUE] Purpose: Fill amount of memory with value. Parameters: AMOUNT: Any formula the value parser accepts, but it @@ -118,6 +135,16 @@ Examples: !fi 256, $ff ; reserve 256 bytes !fill 2 ; reserve two bytes +Call: !skip AMOUNT +Purpose: Advance in output buffer without starting a new segment. +Parameters: AMOUNT: Any formula the value parser accepts, but it + must be solvable even in the first pass (this limitation + will hopefully be lifted in a future release). +Aliases: None +Examples: !skip BUFSIZE ; reserve some bytes + !skip 5 ; reserve five bytes + + Call: !align ANDVALUE, EQUALVALUE [, FILLVALUE] Purpose: Fill memory until a matching address is reached. ACME outputs FILLVALUE until "program counter AND ANDVALUE" diff --git a/docs/Changes.txt b/docs/Changes.txt index f53a59a..404abcb 100644 --- a/docs/Changes.txt +++ b/docs/Changes.txt @@ -12,6 +12,25 @@ platform used. There should be another help file in this archive outlining the platform specific changes. +---------------------------------------------------------------------- +Section: New in release 0.96.3 +---------------------------------------------------------------------- + +Added "!h"/"!hex" pseudo opcode: Now external source code generator + tools can easily put data in sources with minimal syntax overhead. +Added "!skip" pseudo opcode: "!skip N" works like "*=*+N" without + starting a new segment. +Added "cheap locals": Labels with '@' prefix have automatic scoping, + bounded by the preceding and the following global labels. +Added "--fullstop" CLI switch to change pseudo opcode prefix from '!' + to '.' (so other assemblers' sources need less conversion work) +Fixed a bug where expressions like "1)+1" crashed ACME. Thanks to + Bitbreaker for reporting this. +Added warning when using zp-indirect addressing modes where argument + is $ff because pointer wraps around to $00. Thanks to Gerrit for + the suggestion. + + ---------------------------------------------------------------------- Section: New in release 0.96.2 ---------------------------------------------------------------------- diff --git a/docs/Errors.txt b/docs/Errors.txt index a0c54b4..ea5a0e4 100644 --- a/docs/Errors.txt +++ b/docs/Errors.txt @@ -41,12 +41,14 @@ Assembling buggy JMP($xxff) instruction Note that this warning is only given for CPU types 6502 and 6510, because 65c02 and 65816 have been fixed in this respect. +Assembling unstable ANE #NONZERO instruction Assembling unstable LXA #NONZERO instruction - This warning is only ever given for CPU type 6510. LXA is one of - the undocumented ("illegal") opcodes of this CPU (opcode 0xab), - and it only works reliably if its argument is zero. Therefore ACME - issues this warning if you are about to generate this instruction - with a non-zero argument. + These warnings are only ever given for CPU type 6510. ANE and LXA + are undocumented ("illegal") opcodes of this CPU, and they only + work reliably if the argument is zero or the accumulator contains + 0xff. + Therefore ACME issues these warnings if it is about to generate + these instructions with a non-zero argument. Bug in ACME, code follows A situation has been encountered implying there is a bug in ACME. @@ -155,6 +157,13 @@ Wrong type for loop's END value - must match type of START value. In "!for" loops, START and END must have the same type, which then gets used for the loop counter. +Zeropage pointer wraps around from $ff to $00 + A zeropage-indirect addressing mode uses $ff as the argument. The + 6502 will then fetch the second pointer byte from $00 instead of + $0100, therefore this warning is issued. + With the 65816's three-byte pointers, this warning is also given + for $fe arguments. + ...called from here. If warnings and/or errors are output during a macro call, messages with this text are added to display the call stack (because you diff --git a/docs/QuickRef.txt b/docs/QuickRef.txt index d033843..74f3475 100644 --- a/docs/QuickRef.txt +++ b/docs/QuickRef.txt @@ -187,23 +187,35 @@ Available options are: This is more or less useless, because the help is also shown if ACME is run without any arguments at all. - -f, --format FORMAT set output file format ("plain", "cbm" or "apple") + -f, --format FORMAT set output file format + Use this with a bogus format type to get a list of all + supported ones (as of writing: "plain", "cbm" and "apple") -o, --outfile FILE set output file name Output file name and format can also be given using the "!to" pseudo opcode. If the format is not specified, "!to" defaults to "cbm", while the command line option defaults to "plain". + -r, --report set report file name + This creates a text listing containing the original line + number, the resulting memory address, the byte value(s) put + there and the original text line from the source file. + -l, --symbollist FILE set symbol list file name This can also be given using the "!symbollist"/"!sl" pseudo opcode. The switch was called "--labeldump" in older versions, that name still works, too. + --vicelabels FILE set file name for label dump in VICE format + The resulting file uses a format suited for the VICE emulator. + --setpc NUMBER set program counter This can also be given in the source code using "* = NUMBER". - --cpu CPU_TYPE set processor type + --cpu CPU_TYPE set target processor This can be changed in the source code using the "!cpu" pseudo opcode. Defaults to 6502. + Use this with a bogus cpu type to get a list of all supported + ones. --initmem NUMBER define 'empty' memory This can also be given using the "!initmem" pseudo opcode. @@ -212,8 +224,8 @@ Available options are: --maxerrors NUMBER set number of errors before exiting If not given, defaults to 10. - --maxdepth NUMBER set recursion depth for macro calls and the - "!source" pseudo opcode. If not given, defaults to 64. + --maxdepth NUMBER set recursion depth for macro calls and !src + The default value for this is 64. -vDIGIT set verbosity level Sets how much additional informational output is generated. @@ -253,6 +265,19 @@ Available options are: With this option, errors are written to the standard output stream instead of to the standard error stream. + --msvc output errors in MS VS format + This changes the format of the error output to that used by + a certain commercial IDE. + + --color uses ANSI color codes for error output + If your terminal emulation supports ANSI escape codes, use + this option to have warnings and errors displayed in color. + + --fullstop use '.' as pseudo opcode prefix + This changes the prefix character used to mark pseudo opcodes + from '!' to '.' (so sources intended for other assemblers can + be converted with less effort). + -V, --version show version and exit. Platform-specific versions of ACME might offer more options. @@ -353,7 +378,8 @@ $d011 hexadecimal values are indicated by either a current conversion table (none/petscii/screen), chosen using the "!ct" pseudo opcode. poll_joy2 a global symbol -.fail a local symbol, indicated by leading dot +.fail a local symbol, indicated by leading "." +@loop a "cheap local", indicated by leading "@" * the current program counter. During offset assembly, "*" gives the value of the "Pseudo PC". Just to make sure: The value of the program counter is @@ -386,10 +412,14 @@ issued (to spot typing errors - see Errors.txt for more info). Every symbol name consists of these characters: "a" to "z", "A" to "Z", "0" to "9", the underscore character "_" and all characters with values beyond 127. The first character must not be a digit though. But -it can be a dot ("."), making the symbol a local one. Two other -possibilities for label names are "all-characters-are-minus" (then it -is an anonymous backward label) and "all-characters-are-plus" (then it -is an anonymous forward label). +it can be '.' or '@', making the symbol a local one. +Local symbols beginning with '.' are only valid inside the current +zone (marked using the "!zone" pseudo opcode) or the current macro. +Local symbols beginning with '@' are only valid between the enclosing +global labels (or inside the current macro). +Two other possibilities for label names are "all-characters-are-minus" +(then it is an anonymous backward label) and "all-characters-are-plus" +(then it is an anonymous forward label). Every command is one of the following: An assembler opcode diff --git a/src/Makefile b/src/Makefile index 8cd14bd..e553fdd 100644 --- a/src/Makefile +++ b/src/Makefile @@ -43,7 +43,7 @@ output.o: config.h acme.h alu.h cpu.h dynabuf.h global.h input.h tree.h output.h platform.o: config.h platform.h platform.c -pseudoopcodes.o: acme.h alu.h input.h macro.h output.h pseudoopcodes.h pseudoopcodes.c +pseudoopcodes.o: acme.h alu.h global.h input.h macro.h output.h pseudoopcodes.h pseudoopcodes.c section.o: config.h dynabuf.h global.h symbol.h tree.h section.h section.c diff --git a/src/Makefile.dos b/src/Makefile.dos index 3713586..77644da 100644 --- a/src/Makefile.dos +++ b/src/Makefile.dos @@ -44,7 +44,7 @@ output.o: config.h acme.h alu.h cpu.h dynabuf.h global.h input.h tree.h output.h platform.o: config.h platform.h platform.c -pseudoopcodes.o: acme.h alu.h input.h macro.h output.h pseudoopcodes.h pseudoopcodes.c +pseudoopcodes.o: acme.h alu.h global.h input.h macro.h output.h pseudoopcodes.h pseudoopcodes.c section.o: config.h dynabuf.h global.h symbol.h tree.h section.h section.c diff --git a/src/Makefile.mingw b/src/Makefile.mingw index 485075b..64ef24a 100644 --- a/src/Makefile.mingw +++ b/src/Makefile.mingw @@ -47,7 +47,7 @@ output.o: config.h acme.h alu.h cpu.h dynabuf.h global.h input.h tree.h output.h platform.o: config.h platform.h platform.c -pseudoopcodes.o: acme.h alu.h input.h macro.h output.h pseudoopcodes.h pseudoopcodes.c +pseudoopcodes.o: acme.h alu.h global.h input.h macro.h output.h pseudoopcodes.h pseudoopcodes.c section.o: config.h dynabuf.h global.h symbol.h tree.h section.h section.c diff --git a/src/Makefile.riscos b/src/Makefile.riscos index 5dca2da..17e9b73 100644 --- a/src/Makefile.riscos +++ b/src/Makefile.riscos @@ -42,7 +42,7 @@ output.o: config.h acme.h alu.h cpu.h dynabuf.h global.h input.h tree.h output.h platform.o: config.h platform.h platform.c -pseudoopcodes.o: acme.h alu.h input.h macro.h output.h pseudoopcodes.h pseudoopcodes.c +pseudoopcodes.o: acme.h alu.h global.h input.h macro.h output.h pseudoopcodes.h pseudoopcodes.c section.o: config.h dynabuf.h global.h symbol.h tree.h section.h section.c diff --git a/src/acme.c b/src/acme.c index 4c4e78d..9bb3fb1 100644 --- a/src/acme.c +++ b/src/acme.c @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. -// Copyright (C) 1998-2016 Marco Baye +// Copyright (C) 1998-2017 Marco Baye // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -62,6 +62,7 @@ static const char arg_vicelabels[] = "VICE labels filename"; #define OPTION_VERSION "version" #define OPTION_MSVC "msvc" #define OPTION_COLOR "color" +#define OPTION_FULLSTOP "fullstop" // options for "-W" #define OPTIONWNO_LABEL_INDENT "no-label-indent" #define OPTIONWNO_OLD_FOR "no-old-for" @@ -138,8 +139,9 @@ static void show_help_and_exit(void) // when there are more, use next line and add a separate function: //" -W show warning level options\n" " --" OPTION_USE_STDOUT " fix for 'Relaunch64' IDE (see docs)\n" -" --" OPTION_MSVC " set output error message format to that of MS Visual Studio\n" -" --" OPTION_COLOR " enable colored error output using ANSI escape codes\n" +" --" OPTION_MSVC " output errors in MS VS format\n" +" --" OPTION_COLOR " uses ANSI color codes for error output\n" +" --" OPTION_FULLSTOP " use '.' as pseudo opcode prefix\n" PLATFORM_OPTION_HELP " -V, --" OPTION_VERSION " show version and exit\n"); exit(EXIT_SUCCESS); @@ -274,7 +276,7 @@ static int do_actual_work(void) report = &global_report; // let global pointer point to something report_init(report); // we must init struct before doing passes - if (Process_verbosity > 1) + if (config.process_verbosity > 1) puts("First pass."); pass_count = 0; undefined_curr = perform_pass(); // First pass @@ -285,7 +287,7 @@ static int do_actual_work(void) while (undefined_curr && (undefined_curr < undefined_prev)) { ++pass_count; undefined_prev = undefined_curr; - if (Process_verbosity > 1) + if (config.process_verbosity > 1) puts("Further pass."); undefined_curr = perform_pass(); } @@ -294,7 +296,7 @@ static int do_actual_work(void) // if listing report is wanted and there were no errors, // do another pass to generate listing report if (report_filename) { - if (Process_verbosity > 1) + if (config.process_verbosity > 1) puts("Extra pass to generate listing report."); if (report_open(report, report_filename) == 0) { ++pass_count; @@ -306,7 +308,7 @@ static int do_actual_work(void) } // There are still errors (unsolvable by doing further passes), // so perform additional pass to find and show them. - if (Process_verbosity > 1) + if (config.process_verbosity > 1) puts("Extra pass needed to find error."); // activate error output ALU_optional_notdef_handler = Throw_error; @@ -456,7 +458,7 @@ static const char *long_option(const char *string) else if (strcmp(string, OPTION_INITMEM) == 0) set_mem_contents(); else if (strcmp(string, OPTION_MAXERRORS) == 0) - max_errors = string_to_number(cliargs_safe_get_next("maximum error count")); + config.max_errors = string_to_number(cliargs_safe_get_next("maximum error count")); else if (strcmp(string, OPTION_MAXDEPTH) == 0) macro_recursions_left = (source_recursions_left = string_to_number(cliargs_safe_get_next("recursion depth"))); // else if (strcmp(string, "strictsyntax") == 0) @@ -464,10 +466,12 @@ static const char *long_option(const char *string) else if (strcmp(string, OPTION_USE_STDOUT) == 0) msg_stream = stdout; else if (strcmp(string, OPTION_MSVC) == 0) - format_msvc = TRUE; + config.format_msvc = TRUE; + else if (strcmp(string, OPTION_FULLSTOP) == 0) + config.pseudoop_prefix = '.'; PLATFORM_LONGOPTION_CODE else if (strcmp(string, OPTION_COLOR) == 0) - format_color = TRUE; + config.format_color = TRUE; else if (strcmp(string, OPTION_VERSION) == 0) show_version(TRUE); else @@ -499,9 +503,9 @@ static char short_option(const char *argument) report_filename = cliargs_safe_get_next(arg_reportfile); break; case 'v': // "-v" changes verbosity - ++Process_verbosity; + ++config.process_verbosity; if ((argument[1] >= '0') && (argument[1] <= '9')) - Process_verbosity = *(++argument) - '0'; + config.process_verbosity = *(++argument) - '0'; break; // platform specific switches are inserted here PLATFORM_SHORTOPTION_CODE @@ -510,13 +514,13 @@ static char short_option(const char *argument) break; case 'W': // "-W" tunes warning level if (strcmp(argument + 1, OPTIONWNO_LABEL_INDENT) == 0) { - warn_on_indented_labels = FALSE; + config.warn_on_indented_labels = FALSE; goto done; } else if (strcmp(argument + 1, OPTIONWNO_OLD_FOR) == 0) { - warn_on_old_for = FALSE; + config.warn_on_old_for = FALSE; goto done; } else if (strcmp(argument + 1, OPTIONWTYPE_MISMATCH) == 0) { - warn_on_type_mismatch = TRUE; + config.warn_on_type_mismatch = TRUE; goto done; } else { fprintf(stderr, "%sUnknown warning level.\n", cliargs_error); @@ -536,6 +540,7 @@ done: // guess what int main(int argc, const char *argv[]) { + config_default(&config); // if called without any arguments, show usage info (not full help) if (argc == 1) show_help_and_exit(); diff --git a/src/alu.c b/src/alu.c index 4ead9b2..332593d 100644 --- a/src/alu.c +++ b/src/alu.c @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. -// Copyright (C) 1998-2016 Marco Baye +// Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // Arithmetic/logic unit @@ -320,12 +320,12 @@ static intval_t my_asr(intval_t left, intval_t right) } // if undefined, remember name for error output -static void check_for_def(int flags, int prefix, char *name, size_t length) +static void check_for_def(int flags, char optional_prefix_char, char *name, size_t length) { if ((flags & MVALUE_DEFINED) == 0) { DYNABUF_CLEAR(undefsym_dyna_buf); - if (prefix) { - DynaBuf_append(undefsym_dyna_buf, LOCAL_PREFIX); + if (optional_prefix_char) { + DynaBuf_append(undefsym_dyna_buf, optional_prefix_char); length++; } DynaBuf_add_string(undefsym_dyna_buf, name); @@ -344,14 +344,14 @@ static void check_for_def(int flags, int prefix, char *name, size_t length) // their internal name is different (longer) than their displayed name. // This function is not allowed to change DynaBuf because that's where the // symbol name is stored! -static void get_symbol_value(scope_t scope, int prefix, size_t name_length) +static void get_symbol_value(scope_t scope, char optional_prefix_char, size_t name_length) { struct symbol *symbol; // if the symbol gets created now, mark it as unsure symbol = symbol_find(scope, MVALUE_UNSURE); // if needed, remember name for "undefined" error output - check_for_def(symbol->result.flags, prefix, GLOBALDYNABUF_CURRENT, name_length); + check_for_def(symbol->result.flags, optional_prefix_char, GLOBALDYNABUF_CURRENT, name_length); // in first pass, count usage if (pass_count == 0) symbol->usage++; @@ -374,7 +374,7 @@ static void parse_quoted_character(char closing_quote) // on empty string, complain if (GotByte == closing_quote) { Throw_error(exception_missing_string); - Input_skip_remainder(); + alu_state = STATE_ERROR; return; } @@ -387,7 +387,7 @@ static void parse_quoted_character(char closing_quote) if (GotByte) { // if longer than one character Throw_error("There's more than one character."); - Input_skip_remainder(); + alu_state = STATE_ERROR; } } PUSH_INTOPERAND(value, MVALUE_GIVEN | MVALUE_ISBYTE, 0); @@ -593,10 +593,12 @@ static void parse_function_call(void) // make lower case version of name in local dynamic buffer DynaBuf_to_lower(function_dyna_buf, GlobalDynaBuf); // search for tree item - if (Tree_easy_scan(function_tree, &node_body, function_dyna_buf)) + if (Tree_easy_scan(function_tree, &node_body, function_dyna_buf)) { PUSH_OPERATOR((struct operator *) node_body); - else + } else { Throw_error("Unknown function."); + alu_state = STATE_ERROR; + } } @@ -617,7 +619,7 @@ static void expect_operand_or_monadic_operator(void) while (GetByte() == '+'); ugly_length_kluge = GlobalDynaBuf->size; // FIXME - get rid of this! symbol_fix_forward_anon_name(FALSE); // FALSE: do not increment counter - get_symbol_value(section_now->scope, 0, ugly_length_kluge); + get_symbol_value(section_now->local_scope, 0, ugly_length_kluge); goto now_expect_dyadic; case '-': // NEGATION operator or anonymous backward label @@ -631,7 +633,7 @@ static void expect_operand_or_monadic_operator(void) SKIPSPACE(); if (BYTEFLAGS(GotByte) & FOLLOWS_ANON) { DynaBuf_append(GlobalDynaBuf, '\0'); - get_symbol_value(section_now->scope, 0, GlobalDynaBuf->size - 1); // -1 to not count terminator + get_symbol_value(section_now->local_scope, 0, GlobalDynaBuf->size - 1); // -1 to not count terminator goto now_expect_dyadic; } @@ -704,10 +706,23 @@ static void expect_operand_or_monadic_operator(void) if (Input_read_keyword()) { // Now GotByte = illegal char - get_symbol_value(section_now->scope, 1, GlobalDynaBuf->size - 1); // -1 to not count terminator + get_symbol_value(section_now->local_scope, LOCAL_PREFIX, GlobalDynaBuf->size - 1); // -1 to not count terminator goto now_expect_dyadic; } + // if we're here, Input_read_keyword() will have thrown an error (like "no string given"): + alu_state = STATE_ERROR; + break; + case CHEAP_PREFIX: // cheap local symbol + //printf("looking in cheap scope %d\n", section_now->cheap_scope); + GetByte(); // start after '@' + if (Input_read_keyword()) { + // Now GotByte = illegal char + get_symbol_value(section_now->cheap_scope, CHEAP_PREFIX, GlobalDynaBuf->size - 1); // -1 to not count terminator + goto now_expect_dyadic; + } + + // if we're here, Input_read_keyword() will have thrown an error (like "no string given"): alu_state = STATE_ERROR; break; // decimal values and global symbols @@ -772,7 +787,9 @@ get_byte_and_push_monadic: break; now_expect_dyadic: - alu_state = STATE_EXPECT_DYADIC_OPERATOR; + // bugfix: if in error state, do not change state back to valid one + if (alu_state < STATE_MAX_GO_ON) + alu_state = STATE_EXPECT_DYADIC_OPERATOR; break; } } @@ -1051,7 +1068,8 @@ static void try_to_reduce_stacks(int *open_parentheses) break; case OPHANDLE_CLOSING: Throw_error("Too many ')'."); - goto remove_next_to_last_operator; + alu_state = STATE_ERROR; + return; // functions case OPHANDLE_ADDR: @@ -1471,8 +1489,16 @@ static int parse_expression(struct result *result) result->flags |= MVALUE_ISBYTE; } } else { - // State is STATE_ERROR. But actually, nobody cares. - // ...errors have already been reported anyway. :) + // State is STATE_ERROR. Errors have already been reported, + // but we must make sure not to pass bogus data to caller. + result->flags = 0; // maybe set DEFINED flag to suppress follow-up errors? + result->val.intval = 0; + result->addr_refs = 0; + // make sure no additional (spurious) errors are reported: + Input_skip_remainder(); + // FIXME - remove this when new function interface gets used: + // callers must decide for themselves what to do when expression parser returns error + // (currently LDA'' results in both "no string given" AND "illegal combination of command and addressing mode"!) } // return number of open (unmatched) parentheses return open_parentheses; diff --git a/src/flow.c b/src/flow.c index 9ef0106..345c4c2 100644 --- a/src/flow.c +++ b/src/flow.c @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. -// Copyright (C) 1998-2016 Marco Baye +// Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // Flow control stuff (loops, conditional assembly etc.) @@ -225,7 +225,7 @@ void flow_parse_block_else_block(int parse_first) void flow_parse_and_close_file(FILE *fd, const char *filename) { // be verbose - if (Process_verbosity > 2) + if (config.process_verbosity > 2) printf("Parsing source file '%s'\n", filename); // set up new input Input_new_file(filename, fd); diff --git a/src/global.c b/src/global.c index 63aca02..424f3ce 100644 --- a/src/global.c +++ b/src/global.c @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. -// Copyright (C) 1998-2016 Marco Baye +// Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // Global stuff - things that are needed by several modules @@ -109,19 +109,27 @@ const char Byte_flags[256] = { // variables int pass_count; // number of current pass (starts 0) char GotByte; // Last byte read (processed) -int Process_verbosity = 0; // Level of additional output -int warn_on_indented_labels = TRUE; // warn if indented label is encountered -int warn_on_old_for = TRUE; // warn if "!for" with old syntax is found -int warn_on_type_mismatch = FALSE; // use type-checking system // global counters int pass_undefined_count; // "NeedValue" type errors int pass_real_errors; // Errors yet -signed long max_errors = MAXERRORS; // errors before giving up FILE *msg_stream = NULL; // set to stdout by --use-stdout -int format_msvc = FALSE; // actually bool, enabled by --msvc -int format_color = FALSE; // actually bool, enabled by --color struct report *report = NULL; +// configuration +struct config config; + +// set configuration to default values +void config_default(struct config *conf) +{ + conf->pseudoop_prefix = '!'; // can be changed to '.' by CLI switch + conf->process_verbosity = 0; // level of additional output + conf->warn_on_indented_labels = TRUE; // warn if indented label is encountered + conf->warn_on_old_for = TRUE; // warn if "!for" with old syntax is found + conf->warn_on_type_mismatch = FALSE; // use type-checking system + conf->max_errors = MAXERRORS; // errors before giving up + conf->format_msvc = FALSE; // actually bool, enabled by --msvc + conf->format_color = FALSE; // actually bool, enabled by --color +} // memory allocation stuff @@ -184,14 +192,14 @@ static void parse_mnemo_or_global_symbol_def(int *statement_flags) } -// parse local symbol definition -static void parse_local_symbol_def(int *statement_flags) +// parse (cheap) local symbol definition +static void parse_local_symbol_def(int *statement_flags, scope_t scope) { if (!first_label_of_statement(statement_flags)) return; - GetByte(); // start after '.' + GetByte(); // start after '.'/'@' if (Input_read_keyword()) - symbol_parse_definition(section_now->scope, *statement_flags); + symbol_parse_definition(scope, *statement_flags); } @@ -205,7 +213,7 @@ static void parse_backward_anon_def(int *statement_flags) DYNABUF_APPEND(GlobalDynaBuf, '-'); while (GetByte() == '-'); DynaBuf_append(GlobalDynaBuf, '\0'); - symbol_set_label(section_now->scope, *statement_flags, 0, TRUE); // this "TRUE" is the whole secret + symbol_set_label(section_now->local_scope, *statement_flags, 0, TRUE); // this "TRUE" is the whole secret } @@ -222,8 +230,8 @@ static void parse_forward_anon_def(int *statement_flags) } symbol_fix_forward_anon_name(TRUE); // TRUE: increment counter DynaBuf_append(GlobalDynaBuf, '\0'); - //printf("[%d, %s]\n", section_now->scope, GlobalDynaBuf->buffer); - symbol_set_label(section_now->scope, *statement_flags, 0, FALSE); + //printf("[%d, %s]\n", section_now->local_scope, GlobalDynaBuf->buffer); + symbol_set_label(section_now->local_scope, *statement_flags, 0, FALSE); } @@ -245,45 +253,51 @@ void Parse_until_eob_or_eof(void) typesystem_force_address_statement(FALSE); // Parse until end of statement. Only loops if statement // contains "label = pc" definition and something else; or - // if "!ifdef" is true, or if "!addr" is used without block. + // if "!ifdef/ifndef" is true/false, or if "!addr" is used without block. do { - switch (GotByte) { - case CHAR_EOS: // end of statement - // Ignore now, act later - // (stops from being "default") - break; - case ' ': // space - statement_flags |= SF_FOUND_BLANK; - /*FALLTHROUGH*/ - case CHAR_SOL: // start of line - GetByte(); // skip - break; - case '-': - parse_backward_anon_def(&statement_flags); - break; - case '+': - GetByte(); - if ((GotByte == LOCAL_PREFIX) - || (BYTEFLAGS(GotByte) & CONTS_KEYWORD)) - Macro_parse_call(); - else - parse_forward_anon_def(&statement_flags); - break; - case PSEUDO_OPCODE_PREFIX: + // check for pseudo opcodes was moved out of switch, + // because prefix character is now configurable. + if (GotByte == config.pseudoop_prefix) { pseudoopcode_parse(); - break; - case '*': - parse_pc_def(); - break; - case LOCAL_PREFIX: - parse_local_symbol_def(&statement_flags); - break; - default: - if (BYTEFLAGS(GotByte) & STARTS_KEYWORD) { - parse_mnemo_or_global_symbol_def(&statement_flags); - } else { - Throw_error(exception_syntax); - Input_skip_remainder(); + } else { + switch (GotByte) { + case CHAR_EOS: // end of statement + // Ignore now, act later + // (stops from being "default") + break; + case ' ': // space + statement_flags |= SF_FOUND_BLANK; + /*FALLTHROUGH*/ + case CHAR_SOL: // start of line + GetByte(); // skip + break; + case '-': + parse_backward_anon_def(&statement_flags); + break; + case '+': + GetByte(); + if ((GotByte == LOCAL_PREFIX) // TODO - allow "cheap macros"?! + || (BYTEFLAGS(GotByte) & CONTS_KEYWORD)) + Macro_parse_call(); + else + parse_forward_anon_def(&statement_flags); + break; + case '*': + parse_pc_def(); + break; + case LOCAL_PREFIX: + parse_local_symbol_def(&statement_flags, section_now->local_scope); + break; + case CHEAP_PREFIX: + parse_local_symbol_def(&statement_flags, section_now->cheap_scope); + break; + default: + if (BYTEFLAGS(GotByte) & STARTS_KEYWORD) { + parse_mnemo_or_global_symbol_def(&statement_flags); + } else { + Throw_error(exception_syntax); + Input_skip_remainder(); + } } } } while (GotByte != CHAR_EOS); // until end-of-statement @@ -322,10 +336,11 @@ int Throw_get_counter(void) // This function will do the actual output for warnings, errors and serious // errors. It shows the given message string, as well as the current // context: file name, line number, source type and source title. +// TODO: make un-static so !info and !debug can use this. static void throw_message(const char *message, const char *type) { ++throw_counter; - if (format_msvc) + if (config.format_msvc) fprintf(msg_stream, "%s(%d) : %s (%s %s): %s\n", Input_now->original_filename, Input_now->line_number, type, section_now->type, section_now->title, message); @@ -343,7 +358,7 @@ static void throw_message(const char *message, const char *type) void Throw_warning(const char *message) { PLATFORM_WARNING(message); - if (format_color) + if (config.format_color) throw_message(message, "\033[33mWarning\033[0m"); else throw_message(message, "Warning"); @@ -364,12 +379,12 @@ void Throw_first_pass_warning(const char *message) void Throw_error(const char *message) { PLATFORM_ERROR(message); - if (format_color) + if (config.format_color) throw_message(message, "\033[31mError\033[0m"); else throw_message(message, "Error"); ++pass_real_errors; - if (pass_real_errors >= max_errors) + if (pass_real_errors >= config.max_errors) exit(ACME_finalize(EXIT_FAILURE)); } @@ -381,7 +396,7 @@ void Throw_error(const char *message) void Throw_serious_error(const char *message) { PLATFORM_SERIOUS(message); - if (format_color) + if (config.format_color) throw_message(message, "\033[1m\033[31mSerious error\033[0m"); else throw_message(message, "Serious error"); diff --git a/src/global.h b/src/global.h index d0fd384..c0a5a63 100644 --- a/src/global.h +++ b/src/global.h @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. -// Copyright (C) 1998-2016 Marco Baye +// Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // Global stuff - things that are needed by several modules @@ -14,8 +14,8 @@ #include #include "config.h" -#define PSEUDO_OPCODE_PREFIX '!' // FIXME - this is not yet used consistently! #define LOCAL_PREFIX '.' // FIXME - this is not yet used consistently! +#define CHEAP_PREFIX '@' // prefix character for cheap locals // Constants @@ -60,20 +60,24 @@ extern const char Byte_flags[]; #define FOLLOWS_ANON (1u << 3) // preceding '-' are backward label // bits 2, 1 and 0 are currently unused -// TODO - put in config/runtime structs: +// TODO - put in runtime struct: extern int pass_count; -extern int Process_verbosity; // Level of additional output -extern int warn_on_indented_labels; // warn if indented label is encountered -extern int warn_on_old_for; // warn if "!for" with old syntax is found -extern int warn_on_type_mismatch; // use type-checking system extern char GotByte; // Last byte read (processed) -// global counters extern int pass_undefined_count; // "NeedValue" type errors in current pass extern int pass_real_errors; // Errors yet -extern signed long max_errors; // errors before giving up extern FILE *msg_stream; // set to stdout by --errors_to_stdout -extern int format_msvc; // actually bool, enabled by --msvc -extern int format_color; // actually bool, enabled by --color +// configuration +struct config { + char pseudoop_prefix; // '!' or '.' + int process_verbosity; // level of additional output + int warn_on_indented_labels; // warn if indented label is encountered + int warn_on_old_for; // warn if "!for" with old syntax is found + int warn_on_type_mismatch; // use type-checking system + signed long max_errors; // errors before giving up + int format_msvc; // actually bool, enabled by --msvc + int format_color; // actually bool, enabled by --color +}; +extern struct config config; // report stuff #define REPORT_ASCBUFSIZE 1024 @@ -104,6 +108,8 @@ do { \ // Prototypes +// set configuration to default values +extern void config_default(struct config *conf); // allocate memory and die if not available extern void *safe_malloc(size_t); // Parse block, beginning with next byte. diff --git a/src/input.c b/src/input.c index 43f555e..25fa8a6 100644 --- a/src/input.c +++ b/src/input.c @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. -// Copyright (C) 1998-2016 Marco Baye +// Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // Input stuff @@ -421,19 +421,20 @@ int Input_append_keyword_to_global_dynabuf(void) return length; } -// Check whether GotByte is LOCAL_PREFIX (default '.'). -// If not, store global scope value. -// If yes, store current local scope value and read next byte. +// Check GotByte. +// If LOCAL_PREFIX ('.'), store current local scope value and read next byte. +// If CHEAP_PREFIX ('@'), store current cheap scope value and read next byte. +// Otherwise, store global scope value. // Then jump to Input_read_keyword(), which returns length of keyword. int Input_read_scope_and_keyword(scope_t *scope) { SKIPSPACE(); if (GotByte == LOCAL_PREFIX) { GetByte(); - *scope = section_now->scope; -/* TODO } else if (GotByte == CHEAP_PREFIX) { + *scope = section_now->local_scope; + } else if (GotByte == CHEAP_PREFIX) { GetByte(); - *scope = symbol_cheap_scope; */ + *scope = section_now->cheap_scope; } else { *scope = SCOPE_GLOBAL; } diff --git a/src/input.h b/src/input.h index 1e20511..8c49b35 100644 --- a/src/input.h +++ b/src/input.h @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. -// Copyright (C) 1998-2016 Marco Baye +// Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // Input stuff @@ -83,9 +83,10 @@ extern void Input_until_terminator(char terminator); // Append to GlobalDynaBuf while characters are legal for keywords. // Throws "missing string" error if none. Returns number of characters added. extern int Input_append_keyword_to_global_dynabuf(void); -// Check whether GotByte is a dot. -// If not, store global scope value. -// If yes, store current scope value and read next byte. +// Check GotByte. +// If LOCAL_PREFIX ('.'), store current local scope value and read next byte. +// If CHEAP_PREFIX ('@'), store current cheap scope value and read next byte. +// Otherwise, store global scope value. // Then jump to Input_read_keyword(), which returns length of keyword. extern int Input_read_scope_and_keyword(scope_t *scope); // Clear dynamic buffer, then append to it until an illegal (for a keyword) diff --git a/src/macro.c b/src/macro.c index c310dea..903aec2 100644 --- a/src/macro.c +++ b/src/macro.c @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. -// Copyright (C) 1998-2016 Marco Baye +// Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // Macro stuff @@ -88,8 +88,10 @@ static scope_t get_scope_and_title(void) // copy macro title to private dynabuf and add separator character DYNABUF_CLEAR(user_macro_name); DYNABUF_CLEAR(internal_name); - if (macro_scope != SCOPE_GLOBAL) + if (macro_scope != SCOPE_GLOBAL) { + // TODO - allow "cheap macros"?! DynaBuf_append(user_macro_name, LOCAL_PREFIX); + } DynaBuf_add_string(user_macro_name, GLOBALDYNABUF_CURRENT); DynaBuf_add_string(internal_name, GLOBALDYNABUF_CURRENT); DynaBuf_append(user_macro_name, '\0'); @@ -174,6 +176,8 @@ void Macro_parse_definition(void) // Now GotByte = illegal char after "!macro" // Valid argument formats are: // .LOCAL_LABEL_BY_VALUE // ~.LOCAL_LABEL_BY_REFERENCE + // @CHEAP_LOCAL_LABEL_BY_VALUE + // ~@CHEAP_LOCAL_LABEL_BY_REFERENCE // GLOBAL_LABEL_BY_VALUE global args are very uncommon, // ~GLOBAL_LABEL_BY_REFERENCE but not forbidden // now GotByte = non-space @@ -187,9 +191,10 @@ void Macro_parse_definition(void) // Now GotByte = illegal char after "!macro" DynaBuf_append(GlobalDynaBuf, REFERENCE_CHAR); GetByte(); } - // handle prefix for local symbols (LOCAL_PREFIX, normally '.') - if (GotByte == LOCAL_PREFIX) { - DynaBuf_append(GlobalDynaBuf, LOCAL_PREFIX); + // handle prefix for (cheap) local symbols ('.'/'@') + if ((GotByte == LOCAL_PREFIX) + || (GotByte == CHEAP_PREFIX)) { + DynaBuf_append(GlobalDynaBuf, GotByte); GetByte(); } // handle symbol name @@ -304,6 +309,7 @@ void Macro_parse_call(void) // Now GotByte = dot or first char of macro name // start new section (with new scope) // FALSE = title mustn't be freed section_new(&new_section, "Macro", actual_macro->original_name, FALSE); + section_new_cheap_scope(&new_section); GetByte(); // fetch first byte of parameter list // assign arguments if (GotByte != CHAR_EOS) { // any at all? diff --git a/src/mnemo.c b/src/mnemo.c index 2106570..14e1b73 100644 --- a/src/mnemo.c +++ b/src/mnemo.c @@ -827,6 +827,15 @@ static unsigned int imm_ops(int *force_bit, unsigned char opcode, int immediate_ return (((unsigned int) opcode) << 8) | opcode; } +// helper function to warn if zp pointer wraps around +// call with bits=0 for 2-byte pointers and bits=1 for 3-byte pointers +static void check_zp_wraparound(struct result *result, int bits) +{ + if (((result->val.intval | bits) == 0xff) + && (result->flags & MVALUE_DEFINED)) + Throw_warning("Zeropage pointer wraps around from $ff to $00"); +} + // The main accumulator stuff (ADC, AND, CMP, EOR, LDA, ORA, SBC, STA) // plus PEI. static void group_main(int index, int immediate_mode) @@ -859,18 +868,23 @@ static void group_main(int index, int immediate_mode) break; case INDIRECT_ADDRESSING: // ($ff) make_command(force_bit, &result, accu_ind8[index]); + check_zp_wraparound(&result, 0); break; case INDIRECT_Y_INDEXED_ADDRESSING: // ($ff),y make_command(force_bit, &result, accu_indy8[index]); + check_zp_wraparound(&result, 0); break; case INDIRECT_Z_INDEXED_ADDRESSING: // ($ff),z make_command(force_bit, &result, accu_indz8[index]); + check_zp_wraparound(&result, 0); break; case LONG_INDIRECT_ADDRESSING: // [$ff] make_command(force_bit, &result, accu_lind8[index]); + check_zp_wraparound(&result, 1); break; case LONG_INDIRECT_Y_INDEXED_ADDRESSING: // [$ff],y make_command(force_bit, &result, accu_lindy8[index]); + check_zp_wraparound(&result, 1); break; case STACK_INDEXED_INDIRECT_Y_INDEXED_ADDRESSING: // ($ff,s),y make_command(force_bit, &result, accu_sindy8[index]); diff --git a/src/output.c b/src/output.c index eaef19a..138b3e9 100644 --- a/src/output.c +++ b/src/output.c @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. -// Copyright (C) 1998-2016 Marco Baye +// Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // Output stuff @@ -380,7 +380,7 @@ void Output_save_file(FILE *fd) start = out->lowest_written; amount = out->highest_written - start + 1; } - if (Process_verbosity) + 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 @@ -514,7 +514,7 @@ void Output_end_segment(void) // link to segment list link_segment(out->segment.start, amount); // announce - if (Process_verbosity > 1) + if (config.process_verbosity > 1) printf("Segment size is %ld (0x%lx) bytes (0x%lx - 0x%lx exclusive).\n", amount, amount, out->segment.start, out->write_idx); } diff --git a/src/pseudoopcodes.c b/src/pseudoopcodes.c index 064168e..eed7174 100644 --- a/src/pseudoopcodes.c +++ b/src/pseudoopcodes.c @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. -// Copyright (C) 1998-2016 Marco Baye +// Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // pseudo opcode stuff @@ -201,7 +201,6 @@ static enum eos po_le32(void) } -#if 0 // Insert bytes given as pairs of hex digits (helper for source code generators) static enum eos po_hex(void) // now GotByte = illegal char { @@ -251,7 +250,6 @@ static enum eos po_hex(void) // now GotByte = illegal char } } } -#endif // "!cbm" pseudo opcode (now obsolete) @@ -433,7 +431,7 @@ static enum eos po_binary(void) } fclose(fd); // if verbose, produce some output - if ((pass_count == 0) && (Process_verbosity > 1)) { + if ((pass_count == 0) && (config.process_verbosity > 1)) { int amount = vcpu_get_statement_size(); printf("Loaded %d (0x%04x) bytes from file offset %ld (0x%04lx).\n", @@ -458,8 +456,7 @@ static enum eos po_fill(void) } -#if 0 -// skip over some bytes in output without starting a new segment ("!skip" pseudo opcode) +// skip over some bytes in output without starting a new segment. // in contrast to "*=*+AMOUNT", "!skip AMOUNT" does not start a new segment. // (...and it will be needed in future for assemble-to-end-address) static enum eos po_skip(void) // now GotByte = illegal char @@ -473,7 +470,6 @@ static enum eos po_skip(void) // now GotByte = illegal char output_skip(amount.val.intval); return ENSURE_EOS; } -#endif // insert byte until PC fits condition @@ -858,20 +854,20 @@ static enum eos po_for(void) // now GotByte = illegal char loop.counter.addr_refs = intresult.addr_refs; if (Input_accept_comma()) { loop.old_algo = FALSE; // new format - yay! - if (!warn_on_old_for) + if (!config.warn_on_old_for) Throw_first_pass_warning("Found new \"!for\" syntax."); loop.counter.first = intresult.val.intval; // use first argument ALU_defined_int(&intresult); // read second argument loop.counter.last = intresult.val.intval; // use second argument // compare addr_ref counts and complain if not equal! - if (warn_on_type_mismatch + if (config.warn_on_type_mismatch && (intresult.addr_refs != loop.counter.addr_refs)) { Throw_first_pass_warning("Wrong type for loop's END value - must match type of START value."); } loop.counter.increment = (loop.counter.last < loop.counter.first) ? -1 : 1; } else { loop.old_algo = TRUE; // old format - booo! - if (warn_on_old_for) + if (config.warn_on_old_for) Throw_first_pass_warning("Found old \"!for\" syntax."); if (intresult.val.intval < 0) Throw_serious_error("Loop count is negative."); @@ -1019,12 +1015,19 @@ static enum eos throw_string(const char prefix[], void (*fn)(const char *)) } -//// -//static enum eos po_debug(void) -//static enum eos po_info(void) -//{ -// return throw_string(); -//} +#if 0 +// show debug data given in source code +static enum eos po_debug(void) +{ + // FIXME - make debug output depend on some cli switch + return throw_string("!debug: ", throw_message); +} +// show info given in source code +static enum eos po_info(void) +{ + return throw_string("!info: ", throw_message); +} +#endif // throw warning as given in source code @@ -1077,6 +1080,8 @@ static struct ronode pseudo_opcode_list[] = { PREDEFNODE("32", po_32), PREDEFNODE("be32", po_be32), PREDEFNODE("le32", po_le32), + PREDEFNODE("h", po_hex), + PREDEFNODE("hex", po_hex), PREDEFNODE(s_cbm, obsolete_po_cbm), PREDEFNODE("ct", po_convtab), PREDEFNODE("convtab", po_convtab), @@ -1090,6 +1095,7 @@ static struct ronode pseudo_opcode_list[] = { PREDEFNODE("binary", po_binary), PREDEFNODE("fi", po_fill), PREDEFNODE("fill", po_fill), + PREDEFNODE("skip", po_skip), PREDEFNODE("align", po_align), PREDEFNODE("pseudopc", po_pseudopc), PREDEFNODE("realpc", obsolete_po_realpc), @@ -1100,6 +1106,7 @@ static struct ronode pseudo_opcode_list[] = { PREDEFNODE("rs", po_rs), PREDEFNODE("addr", po_address), PREDEFNODE("address", po_address), +// PREDEFNODE("enum", po_enum), PREDEFNODE("set", po_set), PREDEFNODE(s_sl, po_symbollist), PREDEFNODE("symbollist", po_symbollist), @@ -1114,6 +1121,7 @@ static struct ronode pseudo_opcode_list[] = { PREDEFNODE("ifndef", po_ifndef), PREDEFNODE("for", po_for), PREDEFNODE("do", po_do), +// PREDEFNODE("while", po_while), PREDEFNODE("macro", po_macro), // PREDEFNODE("debug", po_debug), // PREDEFNODE("info", po_info), diff --git a/src/section.c b/src/section.c index 1e6df0f..3ce3037 100644 --- a/src/section.c +++ b/src/section.c @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. -// Copyright (C) 1998-2016 Marco Baye +// Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // section stuff (move to symbol.h?) @@ -12,9 +12,12 @@ #include "tree.h" +#define SCOPE_INCREMENT 2 // inc by 2 so locals are even and cheaps are odd + // fake section structure (for error msgs before any real section is in use) static struct section initial_section = { - 0, // scope value + 0, // local scope value + 1, // cheap scope value "during", // "type" => normally "zone Title" or "init", // "title" => "macro test", now "during init" FALSE, // no, title was not malloc'd @@ -24,21 +27,37 @@ static struct section initial_section = { // variables struct section *section_now = &initial_section; // current section static struct section outer_section; // outermost section struct -static scope_t scope_localcount; // highest scope number yet +static scope_t local_scope_max; // highest scope number yet +static scope_t cheap_scope_max; // highest scope number yet // write given info into given structure and activate it void section_new(struct section *section, const char *type, char *title, int allocated) { - section->scope = ++scope_localcount; + // new scope for locals + local_scope_max += SCOPE_INCREMENT; + section->local_scope = local_scope_max; + // keep scope for cheap locals + section->cheap_scope = section_now->cheap_scope; + // copy other data section->type = type; section->title = title; section->allocated = allocated; // activate new section section_now = section; - //printf("[new zone %d: %s, %s]\n", section->scope, section->type, section->title); + //printf("[new section %d: %s, %s]\n", section->local_scope, section->type, section->title); } + +// change scope of cheap locals in given section +void section_new_cheap_scope(struct section *section) +{ + // new scope for cheap locals + cheap_scope_max += SCOPE_INCREMENT; + section->cheap_scope = cheap_scope_max; +} + + // Tidy up: If necessary, release section title. // Warning - the state of the component "Allocd" may have // changed in the meantime, so don't rely on a local variable. @@ -48,9 +67,13 @@ void section_finalize(struct section *section) free(section->title); } + // setup outermost section void section_passinit(void) { - scope_localcount = SCOPE_GLOBAL; // will be incremented by next line + //printf("[old maxima: locals=%d, cheap=%d]\n", local_scope_max, cheap_scope_max); + local_scope_max = 0; // will be incremented by 2 by next line section_new(&outer_section, s_Zone, s_untitled, FALSE); + cheap_scope_max = -1; // will be incremented by 2 by next line + section_new_cheap_scope(&outer_section); } diff --git a/src/section.h b/src/section.h index 7bf3812..c3791ee 100644 --- a/src/section.h +++ b/src/section.h @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. -// Copyright (C) 1998-2016 Marco Baye +// Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // section stuff @@ -12,7 +12,8 @@ // "section" structure type definition struct section { - scope_t scope; // section's scope + scope_t local_scope; // section's scope for local symbols + scope_t cheap_scope; // section's scope for cheap locals const char *type; // "Zone", "Subzone" or "Macro" char *title; // zone title, subzone title or macro title int allocated; // whether title was malloc()'d @@ -25,6 +26,8 @@ extern struct section *section_now; // write given info into given structure and activate it extern void section_new(struct section *section, const char *type, char *title, int allocated); +// change scope of cheap locals in given section +extern void section_new_cheap_scope(struct section *section); // setup outermost section extern void section_passinit(void); // tidy up: if necessary, release section title. diff --git a/src/symbol.c b/src/symbol.c index c3b443f..00f8542 100644 --- a/src/symbol.c +++ b/src/symbol.c @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. -// Copyright (C) 1998-2016 Marco Baye +// Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // symbol stuff @@ -31,7 +31,7 @@ static void dump_one_symbol(struct rwnode *node, FILE *fd) struct symbol *symbol = node->body; // output name - if (warn_on_type_mismatch + if (config.warn_on_type_mismatch && symbol->result.addr_refs == 1) fprintf(fd, "!addr"); fprintf(fd, "\t%s", node->id_string); @@ -176,13 +176,16 @@ void symbol_set_label(scope_t scope, int stat_flags, int force_bit, int change_a symbol = symbol_find(scope, force_bit); // label definition - if ((stat_flags & SF_FOUND_BLANK) && warn_on_indented_labels) + if ((stat_flags & SF_FOUND_BLANK) && config.warn_on_indented_labels) Throw_first_pass_warning("Label name not in leftmost column."); vcpu_read_pc(&pc); result.flags = pc.flags & MVALUE_DEFINED; result.val.intval = pc.val.intval; result.addr_refs = pc.addr_refs; symbol_set_value(symbol, &result, change_allowed); + // global labels must open new scope for cheap locals + if (scope == SCOPE_GLOBAL) + section_new_cheap_scope(section_now); } @@ -259,7 +262,7 @@ void symbol_fix_forward_anon_name(int increment) // terminate name, find "counter" symbol and read value DynaBuf_append(GlobalDynaBuf, '\0'); - counter_symbol = symbol_find(section_now->scope, 0); + counter_symbol = symbol_find(section_now->local_scope, 0); // make sure it gets reset to zero in each new pass if (counter_symbol->pass != pass_count) { counter_symbol->pass = pass_count; diff --git a/src/symbol.h b/src/symbol.h index 7e443e0..b66c2d3 100644 --- a/src/symbol.h +++ b/src/symbol.h @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. -// Copyright (C) 1998-2016 Marco Baye +// Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // symbol stuff @@ -21,7 +21,6 @@ struct symbol { // Constants -// TODO: add cheap locals (so there's SCOPE_GLOBAL, scope_zone and scope_cheap) #define SCOPE_GLOBAL 0 // number of "global zone" diff --git a/src/typesystem.c b/src/typesystem.c index 8206d8e..19db02e 100644 --- a/src/typesystem.c +++ b/src/typesystem.c @@ -1,5 +1,5 @@ // ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. -// Copyright (C) 1998-2016 Marco Baye +// Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // type system stuff @@ -35,7 +35,7 @@ void typesystem_force_address_statement(int value) void typesystem_want_imm(struct result *result) { - if (!warn_on_type_mismatch) + if (!config.warn_on_type_mismatch) return; if (!(result->flags & MVALUE_DEFINED)) return; @@ -46,7 +46,7 @@ void typesystem_want_imm(struct result *result) } void typesystem_want_addr(struct result *result) { - if (!warn_on_type_mismatch) + if (!config.warn_on_type_mismatch) return; if (!(result->flags & MVALUE_DEFINED)) return; diff --git a/src/version.h b/src/version.h index 4c55cfb..95a4156 100644 --- a/src/version.h +++ b/src/version.h @@ -7,11 +7,11 @@ #define version_H -#define RELEASE "0.96.2" // update before release (FIXME) +#define RELEASE "0.96.3" // update before release FIXME #define CODENAME "Fenchurch" // update before release -#define CHANGE_DATE "21 Oct" // update before release +#define CHANGE_DATE "30 Oct" // update before release FIXME #define CHANGE_YEAR "2017" // update before release -//#define HOME_PAGE "http://home.pages.de/~mac_bacon/smorbrod/acme/" // FIXME +//#define HOME_PAGE "http://home.pages.de/~mac_bacon/smorbrod/acme/" #define HOME_PAGE "http://sourceforge.net/p/acme-crossass/" // FIXME