acme/src/symbol.c
marcobaye 294fe25c36 ACME Release 0.96: Added experimental support for instruction sets of Rockwell 65C02, WDC 65C02(S), CSG 65CE02 and CSG 4502.
Stack indexing can now be given either as ",s" or as ",sp" (only relevant for 65816 and 65CE02).


git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@78 4df02467-bbd4-4a76-a152-e7ce94205b78
2016-12-28 20:32:00 +00:00

279 lines
8.6 KiB
C

// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code.
// Copyright (C) 1998-2016 Marco Baye
// Have a look at "acme.c" for further info
//
// symbol stuff
//
// 22 Nov 2007 "warn on indented labels" is now a CLI switch
// 25 Sep 2011 Fixed bug in !sl (colons in filename could be interpreted as EOS)
// 23 Nov 2014 Added label output in VICE format
#include "symbol.h"
#include <stdio.h>
#include "acme.h"
#include "alu.h"
#include "dynabuf.h"
#include "global.h" // FIXME - remove when no longer needed
#include "input.h"
#include "output.h"
#include "platform.h"
#include "section.h"
#include "tree.h"
#include "typesystem.h"
// variables
struct rwnode *symbols_forest[256] = { NULL }; // because of 8-bit hash - must be (at least partially) pre-defined so array will be zeroed!
// Dump symbol value and flags to dump file
static void dump_one_symbol(struct rwnode *node, FILE *fd)
{
struct symbol *symbol = node->body;
// output name
if (warn_on_type_mismatch
&& symbol->result.addr_refs == 1)
fprintf(fd, "!addr");
fprintf(fd, "\t%s", node->id_string);
switch (symbol->result.flags & MVALUE_FORCEBITS) {
case MVALUE_FORCE16:
fprintf(fd, "+2\t= ");
break;
case MVALUE_FORCE16 | MVALUE_FORCE24:
/*FALLTHROUGH*/
case MVALUE_FORCE24:
fprintf(fd, "+3\t= ");
break;
default:
fprintf(fd, "\t= ");
}
if (symbol->result.flags & MVALUE_DEFINED) {
if (symbol->result.flags & MVALUE_IS_FP)
fprintf(fd, "%.30f", symbol->result.val.fpval); //FIXME %g
else
fprintf(fd, "$%x", (unsigned) symbol->result.val.intval);
} else {
fprintf(fd, " ?");
}
if (symbol->result.flags & MVALUE_UNSURE)
fprintf(fd, "\t; ?");
if (symbol->usage == 0)
fprintf(fd, "\t; unused");
fprintf(fd, "\n");
}
// output symbols in VICE format (example: "al C:09ae .nmi1")
static void dump_vice_address(struct rwnode *node, FILE *fd)
{
struct symbol *symbol = node->body;
// dump address symbols even if they are not used
if ((symbol->result.flags & MVALUE_DEFINED)
&& !(symbol->result.flags & MVALUE_IS_FP)
&& (symbol->result.addr_refs == 1))
fprintf(fd, "al C:%04x .%s\n", (unsigned) symbol->result.val.intval, node->id_string);
}
static void dump_vice_usednonaddress(struct rwnode *node, FILE *fd)
{
struct symbol *symbol = node->body;
// dump non-addresses that are used
if (symbol->usage
&& (symbol->result.flags & MVALUE_DEFINED)
&& !(symbol->result.flags & MVALUE_IS_FP)
&& (symbol->result.addr_refs != 1))
fprintf(fd, "al C:%04x .%s\n", (unsigned) symbol->result.val.intval, node->id_string);
}
static void dump_vice_unusednonaddress(struct rwnode *node, FILE *fd)
{
struct symbol *symbol = node->body;
// dump non-addresses that are unused
if (!symbol->usage
&& (symbol->result.flags & MVALUE_DEFINED)
&& !(symbol->result.flags & MVALUE_IS_FP)
&& (symbol->result.addr_refs != 1))
fprintf(fd, "al C:%04x .%s\n", (unsigned) symbol->result.val.intval, node->id_string);
}
// search for symbol. create if nonexistant. if created, give it flags "flags".
// the symbol name must be held in GlobalDynaBuf.
struct symbol *symbol_find(scope_t scope, int flags)
{
struct rwnode *node;
struct symbol *symbol;
int node_created,
force_bits = flags & MVALUE_FORCEBITS;
node_created = Tree_hard_scan(&node, symbols_forest, scope, TRUE);
// if node has just been created, create symbol as well
if (node_created) {
// create new symbol structure
symbol = safe_malloc(sizeof(*symbol));
// finish empty symbol item
symbol->result.flags = flags;
symbol->result.addr_refs = 0;
if (flags & MVALUE_IS_FP)
symbol->result.val.fpval = 0;
else
symbol->result.val.intval = 0;
symbol->usage = 0; // usage count
symbol->pass = pass_count;
node->body = symbol;
} else {
symbol = node->body;
}
// make sure the force bits don't clash
if ((node_created == FALSE) && force_bits)
if ((symbol->result.flags & MVALUE_FORCEBITS) != force_bits)
Throw_error("Too late for postfix.");
return symbol;
}
// assign value to symbol. the function acts upon the symbol's flag bits and
// produces an error if needed.
void symbol_set_value(struct symbol *symbol, struct result *new_value, int change_allowed)
{
int oldflags = symbol->result.flags;
// value stuff
if ((oldflags & MVALUE_DEFINED) && (change_allowed == FALSE)) {
// symbol is already defined, so compare new and old values
// if different type OR same type but different value, complain
if (((oldflags ^ new_value->flags) & MVALUE_IS_FP)
|| ((oldflags & MVALUE_IS_FP) ? (symbol->result.val.fpval != new_value->val.fpval) : (symbol->result.val.intval != new_value->val.intval)))
Throw_error("Symbol already defined.");
} else {
// symbol is not defined yet OR redefinitions are allowed
symbol->result = *new_value;
}
// flags stuff
// Ensure that "unsure" symbols without "isByte" state don't get that
if ((oldflags & (MVALUE_UNSURE | MVALUE_ISBYTE)) == MVALUE_UNSURE)
new_value->flags &= ~MVALUE_ISBYTE;
if (change_allowed) {
oldflags = (oldflags & MVALUE_UNSURE) | new_value->flags;
} else {
if ((oldflags & MVALUE_FORCEBITS) == 0)
if ((oldflags & (MVALUE_UNSURE | MVALUE_DEFINED)) == 0)
oldflags |= new_value->flags & MVALUE_FORCEBITS;
oldflags |= new_value->flags & ~MVALUE_FORCEBITS;
}
symbol->result.flags = oldflags;
}
// parse label definition (can be either global or local).
// name must be held in GlobalDynaBuf.
void symbol_set_label(scope_t scope, int stat_flags, int force_bit, int change_allowed)
{
struct result pc,
result;
struct symbol *symbol;
symbol = symbol_find(scope, force_bit);
// label definition
if ((stat_flags & SF_FOUND_BLANK) && 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);
}
// parse symbol definition (can be either global or local, may turn out to be a label).
// name must be held in GlobalDynaBuf.
void symbol_parse_definition(scope_t scope, int stat_flags)
{
struct result result;
struct symbol *symbol;
int force_bit = Input_get_force_bit(); // skips spaces after
// FIXME - force bit is allowed for label definitions?!
if (GotByte == '=') {
// explicit symbol definition (symbol = <something>)
symbol = symbol_find(scope, force_bit);
// symbol = parsed value
GetByte(); // skip '='
ALU_any_result(&result);
// if wanted, mark as address reference
if (typesystem_says_address())
result.addr_refs = 1;
symbol_set_value(symbol, &result, FALSE);
Input_ensure_EOS();
} else {
symbol_set_label(scope, stat_flags, force_bit, FALSE);
}
}
// set global symbol to value, no questions asked (for "-D" switch)
// Name must be held in GlobalDynaBuf.
void symbol_define(intval_t value)
{
struct result result;
struct symbol *symbol;
result.flags = MVALUE_GIVEN;
result.val.intval = value;
symbol = symbol_find(SCOPE_GLOBAL, 0);
symbol_set_value(symbol, &result, TRUE);
}
// dump global symbols to file
void symbols_list(FILE *fd)
{
Tree_dump_forest(symbols_forest, SCOPE_GLOBAL, dump_one_symbol, fd);
}
void symbols_vicelabels(FILE *fd)
{
// FIXME - if type checking is enabled, maybe only output addresses?
// the order of dumped labels is important because VICE will prefer later defined labels
// dump unused labels
Tree_dump_forest(symbols_forest, SCOPE_GLOBAL, dump_vice_unusednonaddress, fd);
fputc('\n', fd);
// dump other used labels
Tree_dump_forest(symbols_forest, SCOPE_GLOBAL, dump_vice_usednonaddress, fd);
fputc('\n', fd);
// dump address symbols
Tree_dump_forest(symbols_forest, SCOPE_GLOBAL, dump_vice_address, fd);
}
// fix name of anonymous forward label (held in DynaBuf, NOT TERMINATED!) so it
// references the *next* anonymous forward label definition. The tricky bit is,
// each name length would need its own counter. But hey, ACME's real quick in
// finding symbols, so I'll just abuse the symbol system to store those counters.
void symbol_fix_forward_anon_name(int increment)
{
struct symbol *counter_symbol;
unsigned long number;
// terminate name, find "counter" symbol and read value
DynaBuf_append(GlobalDynaBuf, '\0');
counter_symbol = symbol_find(section_now->scope, 0);
// make sure it gets reset to zero in each new pass
if (counter_symbol->pass != pass_count) {
counter_symbol->pass = pass_count;
counter_symbol->result.val.intval = 0;
}
number = (unsigned long) counter_symbol->result.val.intval;
// now append to the name to make it unique
GlobalDynaBuf->size--; // forget terminator, we want to append
do {
DYNABUF_APPEND(GlobalDynaBuf, 'a' + (number & 15));
number >>= 4;
} while (number);
DynaBuf_append(GlobalDynaBuf, '\0');
if (increment)
counter_symbol->result.val.intval++;
}