ACME release 0.96.3: Added "!hex" and "!skip" pseudoops. Added cheap locals.

Added CLI switch to change pseudoop prefix to '.'
Fixed a bug in expression parser and added a warning.


git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@94 4df02467-bbd4-4a76-a152-e7ce94205b78
This commit is contained in:
marcobaye 2017-10-29 23:29:07 +00:00
parent e1683b1e28
commit 7cb100c480
25 changed files with 370 additions and 175 deletions

View File

@ -107,6 +107,23 @@ Examples: !be32 $7fffffff, symbol, -$80000000, 14, $46a4f35
!be32 300000 - 4, a AND a2, 2 ^ tz, (3+4)*70, l1 & .j2 !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] Call: !fill AMOUNT [, VALUE]
Purpose: Fill amount of memory with value. Purpose: Fill amount of memory with value.
Parameters: AMOUNT: Any formula the value parser accepts, but it 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 !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] Call: !align ANDVALUE, EQUALVALUE [, FILLVALUE]
Purpose: Fill memory until a matching address is reached. ACME Purpose: Fill memory until a matching address is reached. ACME
outputs FILLVALUE until "program counter AND ANDVALUE" outputs FILLVALUE until "program counter AND ANDVALUE"

View File

@ -12,6 +12,25 @@ platform used. There should be another help file in this archive
outlining the platform specific changes. 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 Section: New in release 0.96.2
---------------------------------------------------------------------- ----------------------------------------------------------------------

View File

@ -41,12 +41,14 @@ Assembling buggy JMP($xxff) instruction
Note that this warning is only given for CPU types 6502 and 6510, Note that this warning is only given for CPU types 6502 and 6510,
because 65c02 and 65816 have been fixed in this respect. because 65c02 and 65816 have been fixed in this respect.
Assembling unstable ANE #NONZERO instruction
Assembling unstable LXA #NONZERO instruction Assembling unstable LXA #NONZERO instruction
This warning is only ever given for CPU type 6510. LXA is one of These warnings are only ever given for CPU type 6510. ANE and LXA
the undocumented ("illegal") opcodes of this CPU (opcode 0xab), are undocumented ("illegal") opcodes of this CPU, and they only
and it only works reliably if its argument is zero. Therefore ACME work reliably if the argument is zero or the accumulator contains
issues this warning if you are about to generate this instruction 0xff.
with a non-zero argument. Therefore ACME issues these warnings if it is about to generate
these instructions with a non-zero argument.
Bug in ACME, code follows Bug in ACME, code follows
A situation has been encountered implying there is a bug in ACME. 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 In "!for" loops, START and END must have the same type, which then
gets used for the loop counter. 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. ...called from here.
If warnings and/or errors are output during a macro call, messages If warnings and/or errors are output during a macro call, messages
with this text are added to display the call stack (because you with this text are added to display the call stack (because you

View File

@ -187,23 +187,35 @@ Available options are:
This is more or less useless, because the help is also shown This is more or less useless, because the help is also shown
if ACME is run without any arguments at all. 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 -o, --outfile FILE set output file name
Output file name and format can also be given using the "!to" Output file name and format can also be given using the "!to"
pseudo opcode. If the format is not specified, "!to" defaults pseudo opcode. If the format is not specified, "!to" defaults
to "cbm", while the command line option defaults to "plain". 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 -l, --symbollist FILE set symbol list file name
This can also be given using the "!symbollist"/"!sl" pseudo This can also be given using the "!symbollist"/"!sl" pseudo
opcode. The switch was called "--labeldump" in older versions, opcode. The switch was called "--labeldump" in older versions,
that name still works, too. 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 --setpc NUMBER set program counter
This can also be given in the source code using "* = NUMBER". 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 This can be changed in the source code using the "!cpu" pseudo
opcode. Defaults to 6502. opcode. Defaults to 6502.
Use this with a bogus cpu type to get a list of all supported
ones.
--initmem NUMBER define 'empty' memory --initmem NUMBER define 'empty' memory
This can also be given using the "!initmem" pseudo opcode. 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 --maxerrors NUMBER set number of errors before exiting
If not given, defaults to 10. If not given, defaults to 10.
--maxdepth NUMBER set recursion depth for macro calls and the --maxdepth NUMBER set recursion depth for macro calls and !src
"!source" pseudo opcode. If not given, defaults to 64. The default value for this is 64.
-vDIGIT set verbosity level -vDIGIT set verbosity level
Sets how much additional informational output is generated. 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 With this option, errors are written to the standard output
stream instead of to the standard error stream. 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. -V, --version show version and exit.
Platform-specific versions of ACME might offer more options. 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), current conversion table (none/petscii/screen),
chosen using the "!ct" pseudo opcode. chosen using the "!ct" pseudo opcode.
poll_joy2 a global symbol 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, * the current program counter. During offset assembly,
"*" gives the value of the "Pseudo PC". Just to "*" gives the value of the "Pseudo PC". Just to
make sure: The value of the program counter is 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 Every symbol name consists of these characters: "a" to "z", "A" to
"Z", "0" to "9", the underscore character "_" and all characters with "Z", "0" to "9", the underscore character "_" and all characters with
values beyond 127. The first character must not be a digit though. But 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 it can be '.' or '@', making the symbol a local one.
possibilities for label names are "all-characters-are-minus" (then it Local symbols beginning with '.' are only valid inside the current
is an anonymous backward label) and "all-characters-are-plus" (then it zone (marked using the "!zone" pseudo opcode) or the current macro.
is an anonymous forward label). 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: Every command is one of the following:
An assembler opcode An assembler opcode

View File

@ -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 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 section.o: config.h dynabuf.h global.h symbol.h tree.h section.h section.c

View File

@ -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 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 section.o: config.h dynabuf.h global.h symbol.h tree.h section.h section.c

View File

@ -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 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 section.o: config.h dynabuf.h global.h symbol.h tree.h section.h section.c

View File

@ -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 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 section.o: config.h dynabuf.h global.h symbol.h tree.h section.h section.c

View File

@ -1,5 +1,5 @@
// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // 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 // 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 // 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_VERSION "version"
#define OPTION_MSVC "msvc" #define OPTION_MSVC "msvc"
#define OPTION_COLOR "color" #define OPTION_COLOR "color"
#define OPTION_FULLSTOP "fullstop"
// options for "-W" // options for "-W"
#define OPTIONWNO_LABEL_INDENT "no-label-indent" #define OPTIONWNO_LABEL_INDENT "no-label-indent"
#define OPTIONWNO_OLD_FOR "no-old-for" #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: // when there are more, use next line and add a separate function:
//" -W show warning level options\n" //" -W show warning level options\n"
" --" OPTION_USE_STDOUT " fix for 'Relaunch64' IDE (see docs)\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_MSVC " output errors in MS VS format\n"
" --" OPTION_COLOR " enable colored error output using ANSI escape codes\n" " --" OPTION_COLOR " uses ANSI color codes for error output\n"
" --" OPTION_FULLSTOP " use '.' as pseudo opcode prefix\n"
PLATFORM_OPTION_HELP PLATFORM_OPTION_HELP
" -V, --" OPTION_VERSION " show version and exit\n"); " -V, --" OPTION_VERSION " show version and exit\n");
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
@ -274,7 +276,7 @@ static int do_actual_work(void)
report = &global_report; // let global pointer point to something report = &global_report; // let global pointer point to something
report_init(report); // we must init struct before doing passes report_init(report); // we must init struct before doing passes
if (Process_verbosity > 1) if (config.process_verbosity > 1)
puts("First pass."); puts("First pass.");
pass_count = 0; pass_count = 0;
undefined_curr = perform_pass(); // First pass undefined_curr = perform_pass(); // First pass
@ -285,7 +287,7 @@ static int do_actual_work(void)
while (undefined_curr && (undefined_curr < undefined_prev)) { while (undefined_curr && (undefined_curr < undefined_prev)) {
++pass_count; ++pass_count;
undefined_prev = undefined_curr; undefined_prev = undefined_curr;
if (Process_verbosity > 1) if (config.process_verbosity > 1)
puts("Further pass."); puts("Further pass.");
undefined_curr = perform_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, // if listing report is wanted and there were no errors,
// do another pass to generate listing report // do another pass to generate listing report
if (report_filename) { if (report_filename) {
if (Process_verbosity > 1) if (config.process_verbosity > 1)
puts("Extra pass to generate listing report."); puts("Extra pass to generate listing report.");
if (report_open(report, report_filename) == 0) { if (report_open(report, report_filename) == 0) {
++pass_count; ++pass_count;
@ -306,7 +308,7 @@ static int do_actual_work(void)
} }
// There are still errors (unsolvable by doing further passes), // There are still errors (unsolvable by doing further passes),
// so perform additional pass to find and show them. // so perform additional pass to find and show them.
if (Process_verbosity > 1) if (config.process_verbosity > 1)
puts("Extra pass needed to find error."); puts("Extra pass needed to find error.");
// activate error output // activate error output
ALU_optional_notdef_handler = Throw_error; 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) else if (strcmp(string, OPTION_INITMEM) == 0)
set_mem_contents(); set_mem_contents();
else if (strcmp(string, OPTION_MAXERRORS) == 0) 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) else if (strcmp(string, OPTION_MAXDEPTH) == 0)
macro_recursions_left = (source_recursions_left = string_to_number(cliargs_safe_get_next("recursion depth"))); macro_recursions_left = (source_recursions_left = string_to_number(cliargs_safe_get_next("recursion depth")));
// else if (strcmp(string, "strictsyntax") == 0) // 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) else if (strcmp(string, OPTION_USE_STDOUT) == 0)
msg_stream = stdout; msg_stream = stdout;
else if (strcmp(string, OPTION_MSVC) == 0) 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 PLATFORM_LONGOPTION_CODE
else if (strcmp(string, OPTION_COLOR) == 0) else if (strcmp(string, OPTION_COLOR) == 0)
format_color = TRUE; config.format_color = TRUE;
else if (strcmp(string, OPTION_VERSION) == 0) else if (strcmp(string, OPTION_VERSION) == 0)
show_version(TRUE); show_version(TRUE);
else else
@ -499,9 +503,9 @@ static char short_option(const char *argument)
report_filename = cliargs_safe_get_next(arg_reportfile); report_filename = cliargs_safe_get_next(arg_reportfile);
break; break;
case 'v': // "-v" changes verbosity case 'v': // "-v" changes verbosity
++Process_verbosity; ++config.process_verbosity;
if ((argument[1] >= '0') && (argument[1] <= '9')) if ((argument[1] >= '0') && (argument[1] <= '9'))
Process_verbosity = *(++argument) - '0'; config.process_verbosity = *(++argument) - '0';
break; break;
// platform specific switches are inserted here // platform specific switches are inserted here
PLATFORM_SHORTOPTION_CODE PLATFORM_SHORTOPTION_CODE
@ -510,13 +514,13 @@ static char short_option(const char *argument)
break; break;
case 'W': // "-W" tunes warning level case 'W': // "-W" tunes warning level
if (strcmp(argument + 1, OPTIONWNO_LABEL_INDENT) == 0) { if (strcmp(argument + 1, OPTIONWNO_LABEL_INDENT) == 0) {
warn_on_indented_labels = FALSE; config.warn_on_indented_labels = FALSE;
goto done; goto done;
} else if (strcmp(argument + 1, OPTIONWNO_OLD_FOR) == 0) { } else if (strcmp(argument + 1, OPTIONWNO_OLD_FOR) == 0) {
warn_on_old_for = FALSE; config.warn_on_old_for = FALSE;
goto done; goto done;
} else if (strcmp(argument + 1, OPTIONWTYPE_MISMATCH) == 0) { } else if (strcmp(argument + 1, OPTIONWTYPE_MISMATCH) == 0) {
warn_on_type_mismatch = TRUE; config.warn_on_type_mismatch = TRUE;
goto done; goto done;
} else { } else {
fprintf(stderr, "%sUnknown warning level.\n", cliargs_error); fprintf(stderr, "%sUnknown warning level.\n", cliargs_error);
@ -536,6 +540,7 @@ done:
// guess what // guess what
int main(int argc, const char *argv[]) int main(int argc, const char *argv[])
{ {
config_default(&config);
// if called without any arguments, show usage info (not full help) // if called without any arguments, show usage info (not full help)
if (argc == 1) if (argc == 1)
show_help_and_exit(); show_help_and_exit();

View File

@ -1,5 +1,5 @@
// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // 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 // Have a look at "acme.c" for further info
// //
// Arithmetic/logic unit // 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 // 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) { if ((flags & MVALUE_DEFINED) == 0) {
DYNABUF_CLEAR(undefsym_dyna_buf); DYNABUF_CLEAR(undefsym_dyna_buf);
if (prefix) { if (optional_prefix_char) {
DynaBuf_append(undefsym_dyna_buf, LOCAL_PREFIX); DynaBuf_append(undefsym_dyna_buf, optional_prefix_char);
length++; length++;
} }
DynaBuf_add_string(undefsym_dyna_buf, name); 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. // their internal name is different (longer) than their displayed name.
// This function is not allowed to change DynaBuf because that's where the // This function is not allowed to change DynaBuf because that's where the
// symbol name is stored! // 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; struct symbol *symbol;
// if the symbol gets created now, mark it as unsure // if the symbol gets created now, mark it as unsure
symbol = symbol_find(scope, MVALUE_UNSURE); symbol = symbol_find(scope, MVALUE_UNSURE);
// if needed, remember name for "undefined" error output // 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 // in first pass, count usage
if (pass_count == 0) if (pass_count == 0)
symbol->usage++; symbol->usage++;
@ -374,7 +374,7 @@ static void parse_quoted_character(char closing_quote)
// on empty string, complain // on empty string, complain
if (GotByte == closing_quote) { if (GotByte == closing_quote) {
Throw_error(exception_missing_string); Throw_error(exception_missing_string);
Input_skip_remainder(); alu_state = STATE_ERROR;
return; return;
} }
@ -387,7 +387,7 @@ static void parse_quoted_character(char closing_quote)
if (GotByte) { if (GotByte) {
// if longer than one character // if longer than one character
Throw_error("There's more 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); 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 // make lower case version of name in local dynamic buffer
DynaBuf_to_lower(function_dyna_buf, GlobalDynaBuf); DynaBuf_to_lower(function_dyna_buf, GlobalDynaBuf);
// search for tree item // 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); PUSH_OPERATOR((struct operator *) node_body);
else } else {
Throw_error("Unknown function."); Throw_error("Unknown function.");
alu_state = STATE_ERROR;
}
} }
@ -617,7 +619,7 @@ static void expect_operand_or_monadic_operator(void)
while (GetByte() == '+'); while (GetByte() == '+');
ugly_length_kluge = GlobalDynaBuf->size; // FIXME - get rid of this! ugly_length_kluge = GlobalDynaBuf->size; // FIXME - get rid of this!
symbol_fix_forward_anon_name(FALSE); // FALSE: do not increment counter 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; goto now_expect_dyadic;
case '-': // NEGATION operator or anonymous backward label case '-': // NEGATION operator or anonymous backward label
@ -631,7 +633,7 @@ static void expect_operand_or_monadic_operator(void)
SKIPSPACE(); SKIPSPACE();
if (BYTEFLAGS(GotByte) & FOLLOWS_ANON) { if (BYTEFLAGS(GotByte) & FOLLOWS_ANON) {
DynaBuf_append(GlobalDynaBuf, '\0'); 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; goto now_expect_dyadic;
} }
@ -704,10 +706,23 @@ static void expect_operand_or_monadic_operator(void)
if (Input_read_keyword()) { if (Input_read_keyword()) {
// Now GotByte = illegal char // 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; 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; alu_state = STATE_ERROR;
break; break;
// decimal values and global symbols // decimal values and global symbols
@ -772,7 +787,9 @@ get_byte_and_push_monadic:
break; break;
now_expect_dyadic: 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; break;
} }
} }
@ -1051,7 +1068,8 @@ static void try_to_reduce_stacks(int *open_parentheses)
break; break;
case OPHANDLE_CLOSING: case OPHANDLE_CLOSING:
Throw_error("Too many ')'."); Throw_error("Too many ')'.");
goto remove_next_to_last_operator; alu_state = STATE_ERROR;
return;
// functions // functions
case OPHANDLE_ADDR: case OPHANDLE_ADDR:
@ -1471,8 +1489,16 @@ static int parse_expression(struct result *result)
result->flags |= MVALUE_ISBYTE; result->flags |= MVALUE_ISBYTE;
} }
} else { } else {
// State is STATE_ERROR. But actually, nobody cares. // State is STATE_ERROR. Errors have already been reported,
// ...errors have already been reported anyway. :) // 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 number of open (unmatched) parentheses
return open_parentheses; return open_parentheses;

View File

@ -1,5 +1,5 @@
// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // 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 // Have a look at "acme.c" for further info
// //
// Flow control stuff (loops, conditional assembly etc.) // 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) void flow_parse_and_close_file(FILE *fd, const char *filename)
{ {
// be verbose // be verbose
if (Process_verbosity > 2) if (config.process_verbosity > 2)
printf("Parsing source file '%s'\n", filename); printf("Parsing source file '%s'\n", filename);
// set up new input // set up new input
Input_new_file(filename, fd); Input_new_file(filename, fd);

View File

@ -1,5 +1,5 @@
// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // 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 // Have a look at "acme.c" for further info
// //
// Global stuff - things that are needed by several modules // Global stuff - things that are needed by several modules
@ -109,19 +109,27 @@ const char Byte_flags[256] = {
// variables // variables
int pass_count; // number of current pass (starts 0) int pass_count; // number of current pass (starts 0)
char GotByte; // Last byte read (processed) 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 // global counters
int pass_undefined_count; // "NeedValue" type errors int pass_undefined_count; // "NeedValue" type errors
int pass_real_errors; // Errors yet 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 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; 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 // memory allocation stuff
@ -184,14 +192,14 @@ static void parse_mnemo_or_global_symbol_def(int *statement_flags)
} }
// parse local symbol definition // parse (cheap) local symbol definition
static void parse_local_symbol_def(int *statement_flags) static void parse_local_symbol_def(int *statement_flags, scope_t scope)
{ {
if (!first_label_of_statement(statement_flags)) if (!first_label_of_statement(statement_flags))
return; return;
GetByte(); // start after '.' GetByte(); // start after '.'/'@'
if (Input_read_keyword()) 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, '-'); DYNABUF_APPEND(GlobalDynaBuf, '-');
while (GetByte() == '-'); while (GetByte() == '-');
DynaBuf_append(GlobalDynaBuf, '\0'); 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 symbol_fix_forward_anon_name(TRUE); // TRUE: increment counter
DynaBuf_append(GlobalDynaBuf, '\0'); DynaBuf_append(GlobalDynaBuf, '\0');
//printf("[%d, %s]\n", section_now->scope, GlobalDynaBuf->buffer); //printf("[%d, %s]\n", section_now->local_scope, GlobalDynaBuf->buffer);
symbol_set_label(section_now->scope, *statement_flags, 0, FALSE); 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); typesystem_force_address_statement(FALSE);
// Parse until end of statement. Only loops if statement // Parse until end of statement. Only loops if statement
// contains "label = pc" definition and something else; or // 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 { do {
switch (GotByte) { // check for pseudo opcodes was moved out of switch,
case CHAR_EOS: // end of statement // because prefix character is now configurable.
// Ignore now, act later if (GotByte == config.pseudoop_prefix) {
// (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:
pseudoopcode_parse(); pseudoopcode_parse();
break; } else {
case '*': switch (GotByte) {
parse_pc_def(); case CHAR_EOS: // end of statement
break; // Ignore now, act later
case LOCAL_PREFIX: // (stops from being "default")
parse_local_symbol_def(&statement_flags); break;
break; case ' ': // space
default: statement_flags |= SF_FOUND_BLANK;
if (BYTEFLAGS(GotByte) & STARTS_KEYWORD) { /*FALLTHROUGH*/
parse_mnemo_or_global_symbol_def(&statement_flags); case CHAR_SOL: // start of line
} else { GetByte(); // skip
Throw_error(exception_syntax); break;
Input_skip_remainder(); 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 } 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 // This function will do the actual output for warnings, errors and serious
// errors. It shows the given message string, as well as the current // errors. It shows the given message string, as well as the current
// context: file name, line number, source type and source title. // 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) static void throw_message(const char *message, const char *type)
{ {
++throw_counter; ++throw_counter;
if (format_msvc) if (config.format_msvc)
fprintf(msg_stream, "%s(%d) : %s (%s %s): %s\n", fprintf(msg_stream, "%s(%d) : %s (%s %s): %s\n",
Input_now->original_filename, Input_now->line_number, Input_now->original_filename, Input_now->line_number,
type, section_now->type, section_now->title, message); 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) void Throw_warning(const char *message)
{ {
PLATFORM_WARNING(message); PLATFORM_WARNING(message);
if (format_color) if (config.format_color)
throw_message(message, "\033[33mWarning\033[0m"); throw_message(message, "\033[33mWarning\033[0m");
else else
throw_message(message, "Warning"); throw_message(message, "Warning");
@ -364,12 +379,12 @@ void Throw_first_pass_warning(const char *message)
void Throw_error(const char *message) void Throw_error(const char *message)
{ {
PLATFORM_ERROR(message); PLATFORM_ERROR(message);
if (format_color) if (config.format_color)
throw_message(message, "\033[31mError\033[0m"); throw_message(message, "\033[31mError\033[0m");
else else
throw_message(message, "Error"); throw_message(message, "Error");
++pass_real_errors; ++pass_real_errors;
if (pass_real_errors >= max_errors) if (pass_real_errors >= config.max_errors)
exit(ACME_finalize(EXIT_FAILURE)); exit(ACME_finalize(EXIT_FAILURE));
} }
@ -381,7 +396,7 @@ void Throw_error(const char *message)
void Throw_serious_error(const char *message) void Throw_serious_error(const char *message)
{ {
PLATFORM_SERIOUS(message); PLATFORM_SERIOUS(message);
if (format_color) if (config.format_color)
throw_message(message, "\033[1m\033[31mSerious error\033[0m"); throw_message(message, "\033[1m\033[31mSerious error\033[0m");
else else
throw_message(message, "Serious error"); throw_message(message, "Serious error");

View File

@ -1,5 +1,5 @@
// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // 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 // Have a look at "acme.c" for further info
// //
// Global stuff - things that are needed by several modules // Global stuff - things that are needed by several modules
@ -14,8 +14,8 @@
#include <string.h> #include <string.h>
#include "config.h" #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 LOCAL_PREFIX '.' // FIXME - this is not yet used consistently!
#define CHEAP_PREFIX '@' // prefix character for cheap locals
// Constants // Constants
@ -60,20 +60,24 @@ extern const char Byte_flags[];
#define FOLLOWS_ANON (1u << 3) // preceding '-' are backward label #define FOLLOWS_ANON (1u << 3) // preceding '-' are backward label
// bits 2, 1 and 0 are currently unused // 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 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) extern char GotByte; // Last byte read (processed)
// global counters
extern int pass_undefined_count; // "NeedValue" type errors in current pass extern int pass_undefined_count; // "NeedValue" type errors in current pass
extern int pass_real_errors; // Errors yet 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 FILE *msg_stream; // set to stdout by --errors_to_stdout
extern int format_msvc; // actually bool, enabled by --msvc // configuration
extern int format_color; // actually bool, enabled by --color 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 // report stuff
#define REPORT_ASCBUFSIZE 1024 #define REPORT_ASCBUFSIZE 1024
@ -104,6 +108,8 @@ do { \
// Prototypes // Prototypes
// set configuration to default values
extern void config_default(struct config *conf);
// allocate memory and die if not available // allocate memory and die if not available
extern void *safe_malloc(size_t); extern void *safe_malloc(size_t);
// Parse block, beginning with next byte. // Parse block, beginning with next byte.

View File

@ -1,5 +1,5 @@
// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // 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 // Have a look at "acme.c" for further info
// //
// Input stuff // Input stuff
@ -421,19 +421,20 @@ int Input_append_keyword_to_global_dynabuf(void)
return length; return length;
} }
// Check whether GotByte is LOCAL_PREFIX (default '.'). // Check GotByte.
// If not, store global scope value. // If LOCAL_PREFIX ('.'), store current local scope value and read next byte.
// If yes, 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. // Then jump to Input_read_keyword(), which returns length of keyword.
int Input_read_scope_and_keyword(scope_t *scope) int Input_read_scope_and_keyword(scope_t *scope)
{ {
SKIPSPACE(); SKIPSPACE();
if (GotByte == LOCAL_PREFIX) { if (GotByte == LOCAL_PREFIX) {
GetByte(); GetByte();
*scope = section_now->scope; *scope = section_now->local_scope;
/* TODO } else if (GotByte == CHEAP_PREFIX) { } else if (GotByte == CHEAP_PREFIX) {
GetByte(); GetByte();
*scope = symbol_cheap_scope; */ *scope = section_now->cheap_scope;
} else { } else {
*scope = SCOPE_GLOBAL; *scope = SCOPE_GLOBAL;
} }

View File

@ -1,5 +1,5 @@
// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // 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 // Have a look at "acme.c" for further info
// //
// Input stuff // Input stuff
@ -83,9 +83,10 @@ extern void Input_until_terminator(char terminator);
// Append to GlobalDynaBuf while characters are legal for keywords. // Append to GlobalDynaBuf while characters are legal for keywords.
// Throws "missing string" error if none. Returns number of characters added. // Throws "missing string" error if none. Returns number of characters added.
extern int Input_append_keyword_to_global_dynabuf(void); extern int Input_append_keyword_to_global_dynabuf(void);
// Check whether GotByte is a dot. // Check GotByte.
// If not, store global scope value. // If LOCAL_PREFIX ('.'), store current local scope value and read next byte.
// If yes, store current 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. // Then jump to Input_read_keyword(), which returns length of keyword.
extern int Input_read_scope_and_keyword(scope_t *scope); extern int Input_read_scope_and_keyword(scope_t *scope);
// Clear dynamic buffer, then append to it until an illegal (for a keyword) // Clear dynamic buffer, then append to it until an illegal (for a keyword)

View File

@ -1,5 +1,5 @@
// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // 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 // Have a look at "acme.c" for further info
// //
// Macro stuff // Macro stuff
@ -88,8 +88,10 @@ static scope_t get_scope_and_title(void)
// copy macro title to private dynabuf and add separator character // copy macro title to private dynabuf and add separator character
DYNABUF_CLEAR(user_macro_name); DYNABUF_CLEAR(user_macro_name);
DYNABUF_CLEAR(internal_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_append(user_macro_name, LOCAL_PREFIX);
}
DynaBuf_add_string(user_macro_name, GLOBALDYNABUF_CURRENT); DynaBuf_add_string(user_macro_name, GLOBALDYNABUF_CURRENT);
DynaBuf_add_string(internal_name, GLOBALDYNABUF_CURRENT); DynaBuf_add_string(internal_name, GLOBALDYNABUF_CURRENT);
DynaBuf_append(user_macro_name, '\0'); 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: // Valid argument formats are:
// .LOCAL_LABEL_BY_VALUE // .LOCAL_LABEL_BY_VALUE
// ~.LOCAL_LABEL_BY_REFERENCE // ~.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_VALUE global args are very uncommon,
// ~GLOBAL_LABEL_BY_REFERENCE but not forbidden // ~GLOBAL_LABEL_BY_REFERENCE but not forbidden
// now GotByte = non-space // now GotByte = non-space
@ -187,9 +191,10 @@ void Macro_parse_definition(void) // Now GotByte = illegal char after "!macro"
DynaBuf_append(GlobalDynaBuf, REFERENCE_CHAR); DynaBuf_append(GlobalDynaBuf, REFERENCE_CHAR);
GetByte(); GetByte();
} }
// handle prefix for local symbols (LOCAL_PREFIX, normally '.') // handle prefix for (cheap) local symbols ('.'/'@')
if (GotByte == LOCAL_PREFIX) { if ((GotByte == LOCAL_PREFIX)
DynaBuf_append(GlobalDynaBuf, LOCAL_PREFIX); || (GotByte == CHEAP_PREFIX)) {
DynaBuf_append(GlobalDynaBuf, GotByte);
GetByte(); GetByte();
} }
// handle symbol name // 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) // start new section (with new scope)
// FALSE = title mustn't be freed // FALSE = title mustn't be freed
section_new(&new_section, "Macro", actual_macro->original_name, FALSE); section_new(&new_section, "Macro", actual_macro->original_name, FALSE);
section_new_cheap_scope(&new_section);
GetByte(); // fetch first byte of parameter list GetByte(); // fetch first byte of parameter list
// assign arguments // assign arguments
if (GotByte != CHAR_EOS) { // any at all? if (GotByte != CHAR_EOS) { // any at all?

View File

@ -827,6 +827,15 @@ static unsigned int imm_ops(int *force_bit, unsigned char opcode, int immediate_
return (((unsigned int) opcode) << 8) | opcode; 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) // The main accumulator stuff (ADC, AND, CMP, EOR, LDA, ORA, SBC, STA)
// plus PEI. // plus PEI.
static void group_main(int index, int immediate_mode) static void group_main(int index, int immediate_mode)
@ -859,18 +868,23 @@ static void group_main(int index, int immediate_mode)
break; break;
case INDIRECT_ADDRESSING: // ($ff) case INDIRECT_ADDRESSING: // ($ff)
make_command(force_bit, &result, accu_ind8[index]); make_command(force_bit, &result, accu_ind8[index]);
check_zp_wraparound(&result, 0);
break; break;
case INDIRECT_Y_INDEXED_ADDRESSING: // ($ff),y case INDIRECT_Y_INDEXED_ADDRESSING: // ($ff),y
make_command(force_bit, &result, accu_indy8[index]); make_command(force_bit, &result, accu_indy8[index]);
check_zp_wraparound(&result, 0);
break; break;
case INDIRECT_Z_INDEXED_ADDRESSING: // ($ff),z case INDIRECT_Z_INDEXED_ADDRESSING: // ($ff),z
make_command(force_bit, &result, accu_indz8[index]); make_command(force_bit, &result, accu_indz8[index]);
check_zp_wraparound(&result, 0);
break; break;
case LONG_INDIRECT_ADDRESSING: // [$ff] case LONG_INDIRECT_ADDRESSING: // [$ff]
make_command(force_bit, &result, accu_lind8[index]); make_command(force_bit, &result, accu_lind8[index]);
check_zp_wraparound(&result, 1);
break; break;
case LONG_INDIRECT_Y_INDEXED_ADDRESSING: // [$ff],y case LONG_INDIRECT_Y_INDEXED_ADDRESSING: // [$ff],y
make_command(force_bit, &result, accu_lindy8[index]); make_command(force_bit, &result, accu_lindy8[index]);
check_zp_wraparound(&result, 1);
break; break;
case STACK_INDEXED_INDIRECT_Y_INDEXED_ADDRESSING: // ($ff,s),y case STACK_INDEXED_INDIRECT_Y_INDEXED_ADDRESSING: // ($ff,s),y
make_command(force_bit, &result, accu_sindy8[index]); make_command(force_bit, &result, accu_sindy8[index]);

View File

@ -1,5 +1,5 @@
// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // 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 // Have a look at "acme.c" for further info
// //
// Output stuff // Output stuff
@ -380,7 +380,7 @@ void Output_save_file(FILE *fd)
start = out->lowest_written; start = out->lowest_written;
amount = out->highest_written - start + 1; 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", printf("Saving %ld (0x%lx) bytes (0x%lx - 0x%lx exclusive).\n",
amount, amount, start, start + amount); amount, amount, start, start + amount);
// output file header according to file format // output file header according to file format
@ -514,7 +514,7 @@ void Output_end_segment(void)
// link to segment list // link to segment list
link_segment(out->segment.start, amount); link_segment(out->segment.start, amount);
// announce // announce
if (Process_verbosity > 1) if (config.process_verbosity > 1)
printf("Segment size is %ld (0x%lx) bytes (0x%lx - 0x%lx exclusive).\n", printf("Segment size is %ld (0x%lx) bytes (0x%lx - 0x%lx exclusive).\n",
amount, amount, out->segment.start, out->write_idx); amount, amount, out->segment.start, out->write_idx);
} }

View File

@ -1,5 +1,5 @@
// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // 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 // Have a look at "acme.c" for further info
// //
// pseudo opcode stuff // 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) // Insert bytes given as pairs of hex digits (helper for source code generators)
static enum eos po_hex(void) // now GotByte = illegal char 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) // "!cbm" pseudo opcode (now obsolete)
@ -433,7 +431,7 @@ static enum eos po_binary(void)
} }
fclose(fd); fclose(fd);
// if verbose, produce some output // 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(); int amount = vcpu_get_statement_size();
printf("Loaded %d (0x%04x) bytes from file offset %ld (0x%04lx).\n", 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 over some bytes in output without starting a new segment ("!skip" pseudo opcode)
// in contrast to "*=*+AMOUNT", "!skip AMOUNT" does not start 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) // (...and it will be needed in future for assemble-to-end-address)
static enum eos po_skip(void) // now GotByte = illegal char 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); output_skip(amount.val.intval);
return ENSURE_EOS; return ENSURE_EOS;
} }
#endif
// insert byte until PC fits condition // 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; loop.counter.addr_refs = intresult.addr_refs;
if (Input_accept_comma()) { if (Input_accept_comma()) {
loop.old_algo = FALSE; // new format - yay! 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."); Throw_first_pass_warning("Found new \"!for\" syntax.");
loop.counter.first = intresult.val.intval; // use first argument loop.counter.first = intresult.val.intval; // use first argument
ALU_defined_int(&intresult); // read second argument ALU_defined_int(&intresult); // read second argument
loop.counter.last = intresult.val.intval; // use second argument loop.counter.last = intresult.val.intval; // use second argument
// compare addr_ref counts and complain if not equal! // 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)) { && (intresult.addr_refs != loop.counter.addr_refs)) {
Throw_first_pass_warning("Wrong type for loop's END value - must match type of START value."); 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; loop.counter.increment = (loop.counter.last < loop.counter.first) ? -1 : 1;
} else { } else {
loop.old_algo = TRUE; // old format - booo! 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."); Throw_first_pass_warning("Found old \"!for\" syntax.");
if (intresult.val.intval < 0) if (intresult.val.intval < 0)
Throw_serious_error("Loop count is negative."); Throw_serious_error("Loop count is negative.");
@ -1019,12 +1015,19 @@ static enum eos throw_string(const char prefix[], void (*fn)(const char *))
} }
//// #if 0
//static enum eos po_debug(void) // show debug data given in source code
//static enum eos po_info(void) static enum eos po_debug(void)
//{ {
// return throw_string(); // 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 // throw warning as given in source code
@ -1077,6 +1080,8 @@ static struct ronode pseudo_opcode_list[] = {
PREDEFNODE("32", po_32), PREDEFNODE("32", po_32),
PREDEFNODE("be32", po_be32), PREDEFNODE("be32", po_be32),
PREDEFNODE("le32", po_le32), PREDEFNODE("le32", po_le32),
PREDEFNODE("h", po_hex),
PREDEFNODE("hex", po_hex),
PREDEFNODE(s_cbm, obsolete_po_cbm), PREDEFNODE(s_cbm, obsolete_po_cbm),
PREDEFNODE("ct", po_convtab), PREDEFNODE("ct", po_convtab),
PREDEFNODE("convtab", po_convtab), PREDEFNODE("convtab", po_convtab),
@ -1090,6 +1095,7 @@ static struct ronode pseudo_opcode_list[] = {
PREDEFNODE("binary", po_binary), PREDEFNODE("binary", po_binary),
PREDEFNODE("fi", po_fill), PREDEFNODE("fi", po_fill),
PREDEFNODE("fill", po_fill), PREDEFNODE("fill", po_fill),
PREDEFNODE("skip", po_skip),
PREDEFNODE("align", po_align), PREDEFNODE("align", po_align),
PREDEFNODE("pseudopc", po_pseudopc), PREDEFNODE("pseudopc", po_pseudopc),
PREDEFNODE("realpc", obsolete_po_realpc), PREDEFNODE("realpc", obsolete_po_realpc),
@ -1100,6 +1106,7 @@ static struct ronode pseudo_opcode_list[] = {
PREDEFNODE("rs", po_rs), PREDEFNODE("rs", po_rs),
PREDEFNODE("addr", po_address), PREDEFNODE("addr", po_address),
PREDEFNODE("address", po_address), PREDEFNODE("address", po_address),
// PREDEFNODE("enum", po_enum),
PREDEFNODE("set", po_set), PREDEFNODE("set", po_set),
PREDEFNODE(s_sl, po_symbollist), PREDEFNODE(s_sl, po_symbollist),
PREDEFNODE("symbollist", po_symbollist), PREDEFNODE("symbollist", po_symbollist),
@ -1114,6 +1121,7 @@ static struct ronode pseudo_opcode_list[] = {
PREDEFNODE("ifndef", po_ifndef), PREDEFNODE("ifndef", po_ifndef),
PREDEFNODE("for", po_for), PREDEFNODE("for", po_for),
PREDEFNODE("do", po_do), PREDEFNODE("do", po_do),
// PREDEFNODE("while", po_while),
PREDEFNODE("macro", po_macro), PREDEFNODE("macro", po_macro),
// PREDEFNODE("debug", po_debug), // PREDEFNODE("debug", po_debug),
// PREDEFNODE("info", po_info), // PREDEFNODE("info", po_info),

View File

@ -1,5 +1,5 @@
// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // 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 // Have a look at "acme.c" for further info
// //
// section stuff (move to symbol.h?) // section stuff (move to symbol.h?)
@ -12,9 +12,12 @@
#include "tree.h" #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) // fake section structure (for error msgs before any real section is in use)
static struct section initial_section = { static struct section initial_section = {
0, // scope value 0, // local scope value
1, // cheap scope value
"during", // "type" => normally "zone Title" or "during", // "type" => normally "zone Title" or
"init", // "title" => "macro test", now "during init" "init", // "title" => "macro test", now "during init"
FALSE, // no, title was not malloc'd FALSE, // no, title was not malloc'd
@ -24,21 +27,37 @@ static struct section initial_section = {
// variables // variables
struct section *section_now = &initial_section; // current section struct section *section_now = &initial_section; // current section
static struct section outer_section; // outermost section struct 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 // write given info into given structure and activate it
void section_new(struct section *section, const char *type, char *title, int allocated) 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->type = type;
section->title = title; section->title = title;
section->allocated = allocated; section->allocated = allocated;
// activate new section // activate new section
section_now = 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. // Tidy up: If necessary, release section title.
// Warning - the state of the component "Allocd" may have // Warning - the state of the component "Allocd" may have
// changed in the meantime, so don't rely on a local variable. // 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); free(section->title);
} }
// setup outermost section // setup outermost section
void section_passinit(void) 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); 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);
} }

View File

@ -1,5 +1,5 @@
// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // 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 // Have a look at "acme.c" for further info
// //
// section stuff // section stuff
@ -12,7 +12,8 @@
// "section" structure type definition // "section" structure type definition
struct section { 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" const char *type; // "Zone", "Subzone" or "Macro"
char *title; // zone title, subzone title or macro title char *title; // zone title, subzone title or macro title
int allocated; // whether title was malloc()'d 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 // write given info into given structure and activate it
extern void section_new(struct section *section, const char *type, char *title, int allocated); 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 // setup outermost section
extern void section_passinit(void); extern void section_passinit(void);
// tidy up: if necessary, release section title. // tidy up: if necessary, release section title.

View File

@ -1,5 +1,5 @@
// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // 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 // Have a look at "acme.c" for further info
// //
// symbol stuff // symbol stuff
@ -31,7 +31,7 @@ static void dump_one_symbol(struct rwnode *node, FILE *fd)
struct symbol *symbol = node->body; struct symbol *symbol = node->body;
// output name // output name
if (warn_on_type_mismatch if (config.warn_on_type_mismatch
&& symbol->result.addr_refs == 1) && symbol->result.addr_refs == 1)
fprintf(fd, "!addr"); fprintf(fd, "!addr");
fprintf(fd, "\t%s", node->id_string); 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); symbol = symbol_find(scope, force_bit);
// label definition // 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."); Throw_first_pass_warning("Label name not in leftmost column.");
vcpu_read_pc(&pc); vcpu_read_pc(&pc);
result.flags = pc.flags & MVALUE_DEFINED; result.flags = pc.flags & MVALUE_DEFINED;
result.val.intval = pc.val.intval; result.val.intval = pc.val.intval;
result.addr_refs = pc.addr_refs; result.addr_refs = pc.addr_refs;
symbol_set_value(symbol, &result, change_allowed); 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 // terminate name, find "counter" symbol and read value
DynaBuf_append(GlobalDynaBuf, '\0'); 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 // make sure it gets reset to zero in each new pass
if (counter_symbol->pass != pass_count) { if (counter_symbol->pass != pass_count) {
counter_symbol->pass = pass_count; counter_symbol->pass = pass_count;

View File

@ -1,5 +1,5 @@
// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // 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 // Have a look at "acme.c" for further info
// //
// symbol stuff // symbol stuff
@ -21,7 +21,6 @@ struct symbol {
// Constants // Constants
// TODO: add cheap locals (so there's SCOPE_GLOBAL, scope_zone and scope_cheap)
#define SCOPE_GLOBAL 0 // number of "global zone" #define SCOPE_GLOBAL 0 // number of "global zone"

View File

@ -1,5 +1,5 @@
// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // 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 // Have a look at "acme.c" for further info
// //
// type system stuff // type system stuff
@ -35,7 +35,7 @@ void typesystem_force_address_statement(int value)
void typesystem_want_imm(struct result *result) void typesystem_want_imm(struct result *result)
{ {
if (!warn_on_type_mismatch) if (!config.warn_on_type_mismatch)
return; return;
if (!(result->flags & MVALUE_DEFINED)) if (!(result->flags & MVALUE_DEFINED))
return; return;
@ -46,7 +46,7 @@ void typesystem_want_imm(struct result *result)
} }
void typesystem_want_addr(struct result *result) void typesystem_want_addr(struct result *result)
{ {
if (!warn_on_type_mismatch) if (!config.warn_on_type_mismatch)
return; return;
if (!(result->flags & MVALUE_DEFINED)) if (!(result->flags & MVALUE_DEFINED))
return; return;

View File

@ -7,11 +7,11 @@
#define version_H #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 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 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 #define HOME_PAGE "http://sourceforge.net/p/acme-crossass/" // FIXME