/*
casm - Simple, portable assembler
Copyright (C) 2003-2015 Ian Cowburn (ianc@noddybox.co.uk)
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
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
-------------------------------------------------------------------------
Collection for labels.
*/
#include
#include
#include
#include "global.h"
#include "codepage.h"
#include "stack.h"
#include "label.h"
/* ---------------------------------------- TYPES
*/
typedef struct global
{
Label label;
Label *locals;
int no_locals;
int local_size;
struct global *next;
struct global *next_scope;
} GlobalLabel;
/* ---------------------------------------- GLOBALS
*/
static GlobalLabel *head;
static GlobalLabel *tail;
static GlobalLabel *scope;
static char namespace[MAX_LABEL_SIZE + 1];
static Stack *stack;
static int address24 = FALSE;
/* ---------------------------------------- PRIVATE FUNCTIONS
*/
static void WriteNumber(FILE *fp, int num)
{
fprintf(fp, "%.11d", num);
}
static int ReadNumber(FILE *fp)
{
char buff[12];
int f;
for(f= 0 ;f < 11; f++)
{
buff[f] = getc(fp);
}
buff[f] = 0;
return atoi(buff);
}
static void WriteName(FILE *fp, const char *name)
{
fputs(name, fp);
putc(0, fp);
}
static char *ReadName(FILE *fp, char *name)
{
int l = MAX_LABEL_SIZE;
int c;
char *p;
p = name;
while((c = getc(fp)) && (l--))
{
*p++ = c;
}
*p = 0;
return name;
}
static Label *FindLocal(const char *p, GlobalLabel *in)
{
int f;
for(f = 0; f < in->no_locals; f++)
{
if (CompareString(in->locals[f].name, p))
{
return in->locals + f;
}
}
return NULL;
}
static GlobalLabel *FindGlobal(const char *p)
{
GlobalLabel *g = head;
while(g)
{
if (CompareString(g->label.name, p))
{
return g;
}
g = g->next;
}
return NULL;
}
static Label *Find(const char *p, LabelType type)
{
Label *l = NULL;
if ((type & LOCAL_LABEL) && scope)
{
l = FindLocal(p, scope);
}
if (l == NULL && (type & GLOBAL_LABEL))
{
GlobalLabel *g = FindGlobal(p);
if (g)
{
l = &g->label;
}
}
return l;
}
static void AddGlobal(const char *p, int value)
{
GlobalLabel *l = FindGlobal(p);
if (!l)
{
l = Malloc(sizeof *l);
CopyStr(l->label.name, p, sizeof l->label.name);
l->label.value = value;
l->label.type = GLOBAL_LABEL;
l->no_locals = 0;
l->locals = NULL;
l->local_size = 0;
l->next = NULL;
l->next_scope = NULL;
if (tail)
{
tail->next = l;
}
tail = l;
if (!head)
{
head = l;
}
}
else
{
l->label.value = value;
}
scope = l;
}
static void AddLocal(const char *p, int value)
{
int i;
Label *l;
if (scope == NULL)
{
fprintf(stderr, "BUG: Tried to add local %p "
"with no current scope\n", p);
exit(EXIT_FAILURE);
}
l = FindLocal(p, scope);
if (!l)
{
if (scope->no_locals >= scope->local_size)
{
scope->local_size += 32;
scope->locals =
Realloc(scope->locals,
(sizeof *scope->locals) * scope->local_size);
}
i = scope->no_locals++;
CopyStr(scope->locals[i].name, p, sizeof scope->locals[i].name);
scope->locals[i].value = value;
scope->locals[i].type = LOCAL_LABEL;
}
else
{
l->value = value;
}
}
static int ParseBase(const char *str, int base, int *result, char last)
{
char *p;
int i;
*result = (int)strtol(str, &p, base);
return *p == last;
}
/* ---------------------------------------- INTERFACES
*/
void LabelClear(void)
{
GlobalLabel *l;
l = head;
while(l)
{
GlobalLabel *tmp;
tmp = l;
l = l->next;
if (tmp->no_locals)
{
free(tmp->locals);
}
free(tmp);
}
head = NULL;
tail = NULL;
}
int LabelExpand(const char *expr, int *result)
{
Label *label;
int found = FALSE;
/* Check for special single-characters
*/
if (expr[0] && !expr[1])
{
switch(expr[0])
{
/* Current PC
*/
case '$':
if (address24)
{
*result = Bank() << 16 | PC();
}
else
{
*result = PC();
}
return TRUE;
default:
break;
}
}
/* Find the label, or evaluate the constant if not found
*/
if ((label = Find(expr, ANY_LABEL)))
{
*result = label->value;
found = TRUE;
}
if (!found)
{
size_t len;
char first, last;
len = strlen(expr);
first = expr[0];
last = expr[len - 1];
if (first == '$')
{
found = ParseBase(expr + 1, 16, result, '\0');
}
else if (last == 'h' || last == 'H')
{
found = ParseBase(expr, 16, result, last);
}
else if (first == '%')
{
found = ParseBase(expr + 1, 2, result, '\0');
}
else if (last == 'b' || last == 'B')
{
found = ParseBase(expr, 2, result, last);
}
else if (first == '\'' && last == '\'' && len == 3)
{
*result = CodepageConvert(expr[1]);
found = TRUE;
}
else
{
found = ParseBase(expr, 0, result, '\0');
}
}
return found;
}
int LabelSanatise(char *label, LabelType *type)
{
int status = TRUE;
size_t len;
*type = GLOBAL_LABEL;
len = strlen(label);
if (len && label[0] == '.')
{
*type = LOCAL_LABEL;
memmove(label, label + 1, len--);
}
if (len && label[len - 1] == ':')
{
label[--len] = 0;
}
if (len == 0)
{
status = FALSE;
}
return status;
}
const Label *LabelFind(const char *label, LabelType type)
{
return Find(label, type);
}
void LabelSet(const char *label, int value, LabelType type)
{
/* ANY_LABEL indicates that a label is being updated
*/
switch(type)
{
case ANY_LABEL:
if (!scope || CompareString(scope->label.name, label))
{
scope->label.value = value;
}
else
{
AddLocal(label,value);
}
break;
case GLOBAL_LABEL:
AddGlobal(label, value);
StackClear(stack);
break;
case LOCAL_LABEL:
AddLocal(label,value);
break;
}
}
void LabelScopePush(const char *name, int value)
{
if (!stack)
{
stack = StackCreate();
}
StackPush(stack, scope);
AddGlobal(name, value);
}
void LabelScopePop(void)
{
scope = StackPop(stack);
if (!scope)
{
fprintf(stderr, "ERROR: Popping the global scope left it empty");
exit(EXIT_FAILURE);
}
}
void LabelSetScope(const char *label)
{
scope = FindGlobal(label);
}
const char *LabelCreateNamespace(void)
{
int f;
if (namespace[0] == 0)
{
LabelResetNamespace();
}
f = 1;
while(f < MAX_LABEL_SIZE && namespace[f] == '9')
{
f++;
}
if (f < MAX_LABEL_SIZE)
{
namespace[f]++;
}
return namespace;
}
void LabelResetNamespace(void)
{
int f;
namespace[0] = '_';
for(f = 1; f < MAX_LABEL_SIZE; f++)
{
namespace[f] = '0';
}
namespace[MAX_LABEL_SIZE] = 0;
}
void LabelDump(FILE *fp, int dump_private)
{
GlobalLabel *g = head;
while(g)
{
int f;
if (g->label.name[0] != '_' || dump_private)
{
fprintf(fp, "; %-*s = $%8.8x (%d)\n", MAX_LABEL_SIZE,
g->label.name,
(unsigned)g->label.value,
g->label.value);
for(f = 0; f < g->no_locals; f++)
{
fprintf(fp, "; .%-*s = $%8.8x (%d)\n", MAX_LABEL_SIZE,
g->locals[f].name,
(unsigned)g->locals[f].value,
g->locals[f].value);
}
}
g = g->next;
}
}
void LabelWriteBlob(FILE *fp)
{
GlobalLabel *g = head;
int count;
int f;
count = 0;
while(g)
{
if (g->label.name[0] != '_')
{
count++;
}
g = g->next;
}
WriteNumber(fp, count);
g = head;
while(g)
{
if (g->label.name[0] != '_')
{
WriteName(fp, g->label.name);
WriteNumber(fp, g->label.value);
}
g = g->next;
}
}
void LabelReadBlob(FILE *fp, int offset)
{
int count;
int f;
count = ReadNumber(fp);
for(f = 0; f < count; f++)
{
char name[MAX_LABEL_SIZE + 1];
int value;
ReadName(fp, name);
value = ReadNumber(fp);
LabelSet(name, value + offset, GLOBAL_LABEL);
}
}
void LabelSetAddress24(int onoff)
{
address24 = onoff;
}
/*
vim: ai sw=4 ts=8 expandtab
*/