6502/vasm/symbol.c

486 lines
9.2 KiB
C

/* symbol.c - manage all kinds of symbols */
/* (c) in 2014-2018 by Volker Barthelmann and Frank Wille */
#include "vasm.h"
symbol *first_symbol=NULL;
static symbol *saved_symbol=NULL;
static char *last_global_label=emptystr;
#ifndef SYMHTABSIZE
#define SYMHTABSIZE 0x10000
#endif
static hashtable *symhash;
static hashtable *regsymhash;
static void print_type(FILE *f,symbol *p)
{
static const char *typename[] = {"???","obj","func","sect","file"};
int t;
if (p == NULL)
ierror(0);
t = TYPE(p);
fprintf(f,"type=%s ",typename[t<=TYPE_LAST?t:0]);
}
void print_symbol(FILE *f,symbol *p)
{
if (p==NULL)
ierror(0); /* this is usually an error in a cpu-backend, don't crash! */
fprintf(f,"%s ",p->name);
if (p->type==LABSYM)
fprintf(f,"LAB (0x%llx) ",ULLTADDR(p->pc));
if (p->type==IMPORT)
fprintf(f,"IMP ");
if (p->type==EXPRESSION){
fprintf(f,"EXPR(");
print_expr(f,p->expr);
fprintf(f,") ");
}
if (!(p->flags&(USED|VASMINTERN)))
fprintf(f,"UNUSED ");
if (p->flags&VASMINTERN)
fprintf(f,"INTERNAL ");
if (p->flags&EXPORT)
fprintf(f,"EXPORT ");
if (p->flags&COMMON)
fprintf(f,"COMMON ");
if (p->flags&WEAK)
fprintf(f,"WEAK ");
if (p->flags&LOCAL)
fprintf(f,"LOCAL ");
if (p->flags&PROTECTED)
fprintf(f,"PROT ");
if (p->flags&REFERENCED)
fprintf(f,"REF ");
if (p->flags&ABSLABEL)
fprintf(f,"ABS ");
if (p->flags&EQUATE)
fprintf(f,"EQU ");
if (p->flags&REGLIST)
fprintf(f,"REGL ");
if (TYPE(p))
print_type(f,p);
if (p->size){
fprintf(f,"size=");
print_expr(f,p->size);
fprintf(f," ");
}
if (p->align)
fprintf(f,"align=%lu ",(unsigned long)p->align);
if (p->sec)
fprintf(f,"sec=%s ",p->sec->name);
}
char *get_bind_name(symbol *p)
{
if (p->flags&EXPORT)
return "global";
else if (p->flags&WEAK)
return "weak";
else if (p->flags&LOCAL)
return "local";
return "unknown";
}
void add_symbol(symbol *p)
{
hashdata data;
p->next = first_symbol;
first_symbol = p;
data.ptr = p;
add_hashentry(symhash,p->name,data);
}
symbol *find_symbol(char *name)
{
hashdata data;
if (!find_name(symhash,name,&data))
return 0;
return data.ptr;
}
void refer_symbol(symbol *sym,char *refname)
/* refer to an existing symbol with an additional name */
{
hashdata data;
data.ptr=sym;
add_hashentry(symhash,refname,data);
}
void save_symbols(void)
/* remember current list of symbols to be restored later */
{
saved_symbol = first_symbol;
}
void restore_symbols(void)
/* restore to a previously saved symbol list */
{
symbol *firstprot=NULL, *lastprot=NULL;
symbol *symp;
if (saved_symbol) {
while (first_symbol != saved_symbol) {
symp = first_symbol;
first_symbol = symp->next;
if (symp->flags & PROTECTED) {
/* keep this symbol */
symp->next = firstprot;
firstprot = symp;
if (!lastprot)
lastprot = symp;
}
else {
rem_hashentry(symhash,symp->name,nocase);
/* myfree(symp->name); could be dangerous? */
myfree(symp);
}
}
if (firstprot) {
/* add protected symbols to the list again */
lastprot->next = first_symbol;
first_symbol = firstprot;
}
saved_symbol = NULL;
}
}
int check_symbol(char *name)
/* issue an error when symbol is already defined in the current source */
{
symbol *sym;
if (sym = find_symbol(name)) {
if (sym->type != IMPORT) {
general_error(67,name); /* repeatedly defined symbol */
return 1;
}
}
return 0;
}
char *set_last_global_label(char *name)
{
char *prevlgl = last_global_label;
last_global_label = name;
return prevlgl;
}
int is_local_label(char *name)
/* returns true when name belong to a label with local scope */
{
return *name == ' ';
}
char *make_local_label(char *glob,int glen,char *loc,int llen)
/* construct a local label of the form:
" " + global_label_name + " " + local_label_name */
{
char *name,*p;
if (glen == 0) {
/* use the last defined global label */
glob = last_global_label;
glen = strlen(last_global_label);
}
p = name = mymalloc(llen+glen+3);
*p++ = ' ';
if (glen) {
memcpy(p,glob,glen);
p += glen;
}
*p++ = ' ';
memcpy(p,loc,llen);
*(p + llen) = '\0';
return name;
}
symbol *new_abs(char *name,expr *tree)
{
symbol *new = find_symbol(name);
int add;
if (new) {
if (new->flags&EQUATE)
general_error(67,name); /* repeatedly defined symbol (error) */
if (new->type!=IMPORT && new->type!=EXPRESSION)
general_error(5,name); /* symbol redefined (warning) */
add=0;
}
else {
new = mymalloc(sizeof(*new));
new->name = mystrdup(name);
add = 1;
}
new->type = EXPRESSION;
new->sec = 0;
new->expr = tree;
if (add) {
add_symbol(new);
new->flags = 0;
new->size = 0;
new->align = 0;
}
return new;
}
symbol *new_equate(char *name,expr *tree)
{
symbol *sym;
check_symbol(name);
sym = new_abs(name,tree);
sym->flags |= EQUATE; /* not allowed to change this absolute symbol */
return sym;
}
symbol *new_import(char *name)
{
symbol *new = find_symbol(name);
if (new)
return new;
new = mymalloc(sizeof(*new));
new->type = IMPORT;
new->flags = 0;
new->name = mystrdup(name);
new->sec = 0;
new->pc = 0;
new->size = 0;
new->align = 0;
add_symbol(new);
return new;
}
symbol *new_labsym(section *sec,char *name)
{
symbol *new;
int add;
if (chklabels) {
hashdata data;
if (find_name_nc(mnemohash,name,&data))
general_error(39); /* name conflicts with mnemonic */
else if (find_name_nc(dirhash,name,&data))
general_error(40); /* name conflicts with directive */
}
if (!sec) {
sec = default_section();
if (!sec) {
general_error(3);
return new_import(name);
}
}
sec->flags |= HAS_SYMBOLS;
if (sec->flags&LABELS_ARE_LOCAL)
name = make_local_label(sec->name,strlen(sec->name),name,strlen(name));
if (new = find_symbol(name)) {
if (new->type!=IMPORT) {
symbol *old = new;
new = mymalloc(sizeof(*new));
*new = *old;
general_error(74,name); /* label redefined (error) */
}
add = 0;
}
else {
new = mymalloc(sizeof(*new));
if (sec->flags&LABELS_ARE_LOCAL)
new->name = name;
else
new->name = mystrdup(name);
add = 1;
}
new->type = LABSYM;
new->sec = sec;
new->pc = sec->pc;
if (add) {
add_symbol(new);
new->flags = 0;
new->size = 0;
new->align = 0;
}
if (*name != ' ')
last_global_label = new->name;
if (sec->flags & ABSOLUTE)
new->flags |= ABSLABEL;
else
new->flags &= ~ABSLABEL;
return new;
}
symbol *new_tmplabel(section *sec)
{
static unsigned long tmplabcnt = 0;
char tmpnam[16];
sprintf(tmpnam," *tmp%09lu*",tmplabcnt++);
return new_labsym(sec,tmpnam);
}
symbol *internal_abs(char *name)
{
symbol *new = find_symbol(name);
if (new) {
if (new->type!=EXPRESSION || (new->flags&(EXPORT|COMMON|WEAK)))
general_error(37,name); /* internal symbol redefined by user */
}
else {
new = new_abs(name,number_expr(0));
new->flags |= VASMINTERN;
}
return new;
}
expr *set_internal_abs(char *name,taddr newval)
{
symbol *sym = internal_abs(name);
expr *oldexpr = sym->expr;
taddr oldval;
if (oldexpr == NULL)
ierror(0);
eval_expr(oldexpr,&oldval,NULL,0);
if (newval != oldval)
sym->expr = number_expr(newval);
return oldexpr;
}
#ifdef HAVE_REGSYMS
void add_regsym(regsym *rsym)
{
hashdata data;
data.ptr = rsym;
add_hashentry(regsymhash,rsym->reg_name,data);
}
regsym *find_regsym(char *name,int len)
{
hashdata data;
if (find_namelen(regsymhash,name,len,&data))
return data.ptr;
return NULL;
}
regsym *find_regsym_nc(char *name,int len)
{
hashdata data;
if (find_namelen_nc(regsymhash,name,len,&data))
return data.ptr;
return NULL;
}
regsym *new_regsym(int redef,int no_case,char *name,int type,
unsigned int flags,unsigned int num)
{
int len = strlen(name);
regsym *rsym;
/* check if register symbol already exists */
rsym = no_case!=0 ? find_regsym_nc(name,len) : find_regsym(name,len);
if (rsym!=NULL && !redef) {
general_error(58,name); /* register symbol redefined */
return rsym;
}
if (rsym == NULL) {
rsym = mymalloc(sizeof(regsym));
rsym->reg_name = mystrdup(name);
rsym->reg_type = type;
rsym->reg_flags = flags;
rsym->reg_num = num;
add_regsym(rsym);
}
else {
/* just update */
rsym->reg_type = type;
rsym->reg_flags = flags;
rsym->reg_num = num;
}
return rsym;
}
/* remove an already defined register symbol from the hash table */
int undef_regsym(char *name,int no_case,int type)
{
regsym *rsym = no_case!=0 ?
find_regsym_nc(name,strlen(name)) :
find_regsym(name,strlen(name));
if (rsym != NULL) {
if (rsym->reg_type == type) {
rem_hashentry(regsymhash,name,no_case);
return 1;
}
else
general_error(70,name); /* register symbol has wrong type */
}
else
general_error(69,name); /* register does not exist */
return 0;
}
#endif /* HAVE_REGSYMS */
int init_symbol(void)
{
symhash = new_hashtable(SYMHTABSIZE);
#ifdef HAVE_REGSYMS
regsymhash = new_hashtable(REGSYMHTSIZE);
#endif
return 1;
}