mirror of
https://github.com/uffejakobsen/acme.git
synced 2024-12-23 10:29:46 +00:00
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:
parent
e1683b1e28
commit
7cb100c480
@ -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"
|
||||
|
@ -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
|
||||
----------------------------------------------------------------------
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
35
src/acme.c
35
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();
|
||||
|
60
src/alu.c
60
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;
|
||||
|
@ -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);
|
||||
|
129
src/global.c
129
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");
|
||||
|
28
src/global.h
28
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 <string.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 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.
|
||||
|
15
src/input.c
15
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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
16
src/macro.c
16
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?
|
||||
|
14
src/mnemo.c
14
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]);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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.
|
||||
|
11
src/symbol.c
11
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;
|
||||
|
@ -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"
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user