1
0
mirror of https://github.com/fachat/xa65.git synced 2024-06-08 23:29:30 +00:00
xa65/xa/src/xal.c
2023-12-07 23:34:19 +01:00

853 lines
18 KiB
C

/* xa65 - 65xx/65816 cross-assembler and utility suite
*
* Copyright (C) 1989-1997 Andre Fachat (afachat@gmx.de)
*
* Label management module (also see xau.c)
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
/* structs and defs */
#include "xad.h"
#include "xah.h"
#include "xar.h"
#include "xah2.h"
#include "xap.h"
#include "xa.h"
/* externals */
#include "xam.h"
#include "xal.h"
/* exported globals */
char *lz;
/* local prototypes */
static int b_fget(int*, int);
static int b_ltest(int, int);
static int b_get(int*);
static int b_test(int);
static int ll_def(char *s, int *n, int b, xalabel_t ltype);
static int b_link(int);
static int b_new(void);
static void cll_init();
static int cll_get();
static void cll_clear();
static int cll_getcur();
static Labtab *ltp;
int l_init(void) {
cll_init();
//unn_init();
return 0;
}
int ga_lab(void) {
return (afile->la.lti);
}
int gm_lab(void) {
return (ANZLAB);
}
long gm_labm(void) {
return ((long) LABMEM);
}
long ga_labm(void) {
return (0 /*lni*/);
}
void printllist(fp)
FILE *fp; {
int i;
LabOcc *p;
char *fname = NULL;
for (i = 0; i < afile->la.lti; i++) {
ltp = afile->la.lt + i;
fprintf(fp, "%s, 0x%04x, %d, 0x%04x\n", ltp->n, ltp->val, ltp->blk,
ltp->afl);
p = ltp->occlist;
if (p) {
while (p) {
if (fname != p->fname) {
if (p != ltp->occlist)
fprintf(fp, "\n");
fprintf(fp, " %s", p->fname);
fname = p->fname;
}
fprintf(fp, " %d", p->line);
p = p->next;
}
fprintf(fp, "\n");
}
fname = NULL;
}
}
/**********************************************************************************
* cheap local labels
*/
static int cll_current = 0; /* the current cheap local labels block */
/**
* init the cheap local labels
*/
void cll_init() {
cll_current = 0;
}
/**
* get the block number for a new cheap local label block
*/
int cll_get() {
if (cll_current == 0) {
cll_current = b_new();
}
return cll_current;
}
/**
* clear the local labels
*/
void cll_clear() {
cll_current = 0;
}
int cll_getcur() {
return cll_current;
}
/**********************************************************************************/
/**
* define a global label (from the "-L" command line parameter)
*/
int lg_set(char *s) {
int n, er;
er = ll_search(s, &n, STD);
if (er == E_OK) {
fprintf(stderr, "Warning: global label doubly defined!\n");
} else {
if (!(er = ll_def(s, &n, 0, STD))) {
return lg_import(n);
}
}
return er;
}
/**
* define a global label (from the .import pseudo opcode))
* "s" is a pointer to the first label character, end is at \0
* or at non-alphanumeric/_ char
*/
int lg_import(int n) {
int er = E_OK;
ltp = afile->la.lt + n;
ltp->fl = 2;
ltp->afl = SEG_UNDEF;
return er;
}
/*
* re-define a previously undef'd label as globally undefined
* (for -U option)
*/
int lg_toglobal(char *s) {
int n, er;
//printf("lg_toglobal(%s)\n", s);
er = ll_search(s, &n, STD);
if (er == E_OK && ltp->fl != 3) {
// fonnd, but not yet set as global undef'd label
ltp = afile->la.lt + n;
ltp->fl = 3;
ltp->afl = SEG_UNDEF;
ltp->origblk = ltp->blk;
ltp->blk = 0;
}
return er;
}
/**
* define a global zeropage label (from the .importzp pseudo opcode))
* "s" is a pointer to the first label character, end is at \0
* or at non-alphanumeric/_ char
*/
int lg_importzp(int n) {
int er = E_OK;
ltp = afile->la.lt + n;
ltp->fl = 2;
ltp->afl = SEG_UNDEFZP;
return er;
}
/**********************************************************************************/
int l_def(char *s, int *l, int *x, int *f) {
int n, er, b, i = 0;
xalabel_t cll_fl;
*f = 0; /* flag (given as param) that the label is to be re-defined and the
"label defined error" is to be skipped */
b = 0; /* block level on block stack, resp. block number */
n = 0; /* flag, when set, b is absolute block number and not being translated */
cll_fl = STD; /* when 0, clear the cheap local label block */
if (s[0] == ':') {
// ca65 unnamed label
i++;
//n++; /* block number b is absolute */
//b=unn_get(); /* current (possibly newly allocated) unnamed label block */
cll_fl = UNNAMED; // keep the cheap local label block
} else if (s[0] == '-') {
*f += 1; /* label is being redefined */
i++;
} else if (s[0] == '@') {
i++;
n++; /* block number b is absolute */
b = cll_get(); /* current (possibly newly allocated) cheap label block */
cll_fl = CHEAP; /* do not clear the cll block again... */
} else if (s[0] == '+') {
i++;
n++; /* block number b is absolute */
b = 0; /* global block number */
}
while (s[i] == '&') {
if (n)
b = 0; /* reset block number */
n = 0; /* block number is relative */
i++;
b++; /* one (more) level up the block stack */
}
if (!n) {
/* translate from block stack level to absolute block number */
b_fget(&b, b);
}
if (cll_fl == STD) {
/* clear cheap local labels */
cll_clear();
}
if ((!isalpha(s[i])) && (s[i] != '_')
&& !(ca65 && ((cll_fl == UNNAMED) || isdigit(s[i])))) {
//printf("SYNTAX cll_fl=%d, i=%d, s[i]=%02x (%c)\n", cll_fl, i, s[i], s[i]);
er = E_SYNTAX;
} else {
er = E_NODEF;
if (cll_fl != UNNAMED) {
er = ll_search(s + i, &n, cll_fl);
}
if (er == E_OK) {
//printf("l_def OK: cll_fl=%d, i=%d, s=%s\n", cll_fl, i, s);
/* we actually found an existing label in the same scope */
ltp = afile->la.lt + n;
if (*f) {
/* redefinition of label */
*l = ltp->len + i;
} else if (ltp->fl == 0) {
/* label has not been defined yet, (e.g. pass1 forward ref), so we try to set it. */
*l = ltp->len + i;
if (b_ltest(ltp->blk, b))
er = E_LABDEF;
else
ltp->blk = b;
} else if (ltp->fl == 3) {
/* label has been defined as -U undef'd label so far - we need to check */
*l = ltp->len + i;
if (b_ltest(ltp->origblk, b))
er = E_LABDEF;
else
ltp->blk = b;
} else
er = E_LABDEF;
} else if (er == E_NODEF) {
if (!(er = ll_def(s + i, &n, b, cll_fl))) /* store the label in the table of labels */
{
ltp = afile->la.lt + n;
*l = ltp->len + i;
ltp->fl = 0;
ltp->is_cll = cll_fl;
}
//printf("l_def NODEF: n=%d, s=%s\n", n, ltp->n);
}
*x = n;
}
return (er);
}
int l_search(char *s, int *l, int *x, int *v, int *afl) {
int n, er, b;
xalabel_t cll_fl;
*afl = 0;
/* check cheap local label */
cll_fl = STD;
if (s[0] == '@') {
cll_fl = CHEAP;
s++;
} else if (s[0] == ':') {
cll_fl = UNNAMED_DEF;
s++;
}
er = E_NODEF;
if (cll_fl != UNNAMED_DEF) {
er = ll_search(s, &n, cll_fl);
}
//printf("l_search: lab=%s(afl=%d, er=%d, cll_fl=%d, cll_cur=%d)\n",s,*afl,er, cll_fl, cll_getcur());
if (er == E_OK) {
ltp = afile->la.lt + n;
*l = ltp->len + ((cll_fl == STD) ? 0 : 1);
if (ltp->fl == 1) {
l_get(n, v, afl);/* *v=lt[n].val;*/
*x = n;
} else {
er = E_NODEF;
lz = ltp->n;
*x = n;
}
} else {
if (cll_fl == CHEAP) {
b = cll_get();
} else if (cll_fl == UNNAMED_DEF) {
b_get(&b); // b=unn_get();
} else {
b_get(&b);
}
er = ll_def(s, x, b, cll_fl); /* ll_def(...,*v); */
ltp = afile->la.lt + (*x);
ltp->is_cll = cll_fl;
*l = ltp->len + ((cll_fl == STD) ? 0 : 1);
//*l=ltp->len + cll_fl;
if (!er) {
er = E_NODEF;
lz = ltp->n;
}
}
return (er);
}
int l_vget(int n, int *v, char **s) {
ltp = afile->la.lt + n;
(*v) = ltp->val;
*s = ltp->n;
return 0;
}
void l_addocc(int n, int *v, int *afl) {
LabOcc *p, *pp;
(void) v; /* quench warning */
(void) afl; /* quench warning */
ltp = afile->la.lt + n;
pp = NULL;
p = ltp->occlist;
while (p) {
if (p->line == filep->fline && p->fname == filep->fname)
return;
pp = p;
p = p->next;
}
p = malloc(sizeof(LabOcc));
if (!p) {
fprintf(stderr, "Oops, out of memory!\n");
exit(1);
}
p->next = NULL;
p->line = filep->fline;
p->fname = filep->fname;
if (pp) {
pp->next = p;
} else {
ltp->occlist = p;
}
}
/* for the list functionality */
char* l_get_name(int n, xalabel_t *is_cll) {
if (n > afile->la.ltm) {
fprintf(stderr, "Corrupted structures! n=%d, but max=%d\n", n,
afile->la.ltm);
exit(1);
}
ltp = afile->la.lt + n;
*is_cll = ltp->is_cll;
return ltp->n;
}
// works on the static(!) ltp "label table pointer"
// also returns the actual index in the table of the current ltp
static int resolve_unnamed() {
// need to count up/down in the linkd label list for the block
char *namep = ltp->n;
int nextp = -1;
//printf("::: unnamed_def: %s, n=%d\n", namep, n);
while ((*namep == '+') || (*namep == '-')) {
char c = *namep;
nextp = -1;
if (c == '+') {
nextp = ltp->blknext;
} else if (c == '-') {
nextp = ltp->blkprev;
}
//printf("::: nextp=%d\n", nextp);
if (nextp == -1) {
return -1; // E_NODEF
}
ltp = afile->la.lt + nextp;
//printf("::: leads to: %s, nextp=%d\n", ltp->n, nextp);
if (ltp->is_cll == UNNAMED) {
namep++;
}
}
return nextp;
}
/* for the listing, esp. html links; returns a pointer to a static buffer, available till next call */
char* l_get_unique_name(int n) {
static char buf[MAXLINE];
ltp = afile->la.lt + n;
if (ltp->is_cll == CHEAP || ltp->is_cll == STD) {
sprintf(buf, "%d%c%s", ltp->blk, (ltp->is_cll == CHEAP) ? 'C' : '_',
ltp->n);
} else if (ltp->is_cll == UNNAMED) {
// definition of unnamed label - name is NULL
// so use the actual index
sprintf(buf, "%dU%d", ltp->blk, n);
} else if (ltp->is_cll == UNNAMED_DEF) {
// we actually need to find the correct label from the "+" and "-"
// in the name
int tmp = resolve_unnamed();
if (tmp >= 0) {
sprintf(buf, "%dU%d", ltp->blk, tmp);
} else {
sprintf(buf, "__%d", tmp);
}
} else {
buf[0] = 0; // no value
}
return buf;
}
int l_get(int n, int *v, int *afl) {
if (crossref)
l_addocc(n, v, afl);
ltp = afile->la.lt + n;
if (ltp->is_cll == UNNAMED_DEF) {
int tmp = resolve_unnamed();
if (tmp == -1)
return E_NODEF;
// now ltp is set to the actual label
}
(*v) = ltp->val;
lz = ltp->n;
*afl = ltp->afl;
/*printf("l_get('%s'(%d), v=$%04x, afl=%d, fl=%d\n",ltp->n, n, *v, *afl, ltp->fl);*/
return ((ltp->fl == 1) ? E_OK : E_NODEF);
}
void l_set(int n, int v, int afl) {
ltp = afile->la.lt + n;
ltp->val = v;
ltp->fl = 1;
ltp->afl = afl;
//printf("l_set('%s'(%d), v=$%04x, afl=%d\n",ltp->n, n, v, afl);
}
static void ll_exblk(int a, int b) {
int i;
for (i = 0; i < afile->la.lti; i++) {
ltp = afile->la.lt + i;
if ((!ltp->fl) && (ltp->blk == a))
ltp->blk = b;
}
}
/* defines next label, returns new label number in out param n */
static int ll_def(char *s, int *n, int b, xalabel_t ltype) {
int j = 0, er = E_NOMEM, hash;
char *s2 = NULL;
//printf("ll_def: s=%s, ltype=%d, no_name=%d\n",s, ltype, no_name);
// label table for the file ...
if (!afile->la.lt) {
// ... does not exist yet, so malloc it
afile->la.lti = 0;
afile->la.ltm = 1000;
afile->la.lt = malloc(afile->la.ltm * sizeof(Labtab));
}
if (afile->la.lti >= afile->la.ltm) {
// ... or is at its capacity limit, so realloc it
afile->la.ltm *= 1.5;
afile->la.lt = realloc(afile->la.lt, afile->la.ltm * sizeof(Labtab));
}
if (!afile->la.lt) {
fprintf(stderr, "Oops: no memory!\n");
exit(1);
}
// current pointer in label table
ltp = afile->la.lt + afile->la.lti;
if (ltype != UNNAMED) {
// alloc space and copy over name
if (ltype == UNNAMED_DEF) {
// unnamed lables are like ":--" or ":+" with variable length
while ((s[j] != '\0') && (s[j] == '+' || s[j] == '-'))
j++;
} else {
// standard (and cheap) labels are normal text
while ((s[j] != '\0') && (isalnum(s[j]) || (s[j] == '_')))
j++;
}
s2 = malloc(j + 1);
if (!s2) {
fprintf(stderr, "Oops: no memory!\n");
exit(1);
}
strncpy(s2, s, j);
s2[j] = 0;
}
// init new entry in label table
er = E_OK;
ltp->len = j; // length of label
ltp->n = s2; // name of label (char*)
ltp->blk = b; // block number
ltp->fl = 0;
ltp->afl = 0;
ltp->is_cll = ltype; // STD, CHEAP, or UNNAMED label
ltp->occlist = NULL;
hash = hashcode(s, j); // compute hashcode
ltp->nextindex = afile->la.hashindex[hash]; // and link in before last entry with same hashcode
afile->la.hashindex[hash] = afile->la.lti;// set as start of list for that hashcode
// TODO: does not work across files!
ltp->blknext = -1; // no next block
ltp->blkprev = b_link(afile->la.lti);// previous block, linked within block
if (ltp->blkprev != -1) {
ltp = afile->la.lt + ltp->blkprev;
ltp->blknext = afile->la.lti;
}
*n = afile->la.lti; // return the list index for that file in the out parameter n
afile->la.lti++; // increase last index in lable table
return (er);
}
/**
* search a label name in the label table. Return the label number
* in "n". Finds only labels that are in a block that is in the current
* set of blocks (in the block stack)
*
* If cll_fl is set, the label is also searched in the local cheap label scope
*
* Do not define the label (as is done in l_search()!)
*/
int ll_search(char *s, int *n, xalabel_t cll_fl) /* search Label in Tabelle ,nr->n */
{
int i, j = 0, k, er = E_NODEF, hash;
while (s[j] && (isalnum(s[j]) || (s[j] == '_')))
j++;
hash = hashcode(s, j);
i = afile->la.hashindex[hash];
if (i >= afile->la.ltm)
return E_NODEF;
do {
ltp = afile->la.lt + i;
if (j == ltp->len) {
for (k = 0; (k < j) && (ltp->n[k] == s[k]); k++)
;
if ((j == k) && cll_fl == CHEAP) {
if (ltp->blk == cll_getcur()) {
er = E_OK;
break;
}
} else if (cll_fl == UNNAMED) {
// TODO
} else {
//printf("ll_search:match labels %s with %p (%s) from block %d, block check is %d\n", s, ltp, ltp->n, ltp->blk, b_test(ltp->blk));
/* check if the found label is in any of the blocks in the
current block stack */
if ((j == k) && (!b_test(ltp->blk))) {
/* ok, label found and it is reachable (its block nr is in the current block stack */
er = E_OK;
break;
}
}
}
if (!i)
break;
i = ltp->nextindex;
} while (1);
*n = i;
#if 0
if(er!=E_OK && er!=E_NODEF)
{
fprintf(stderr, "Fehler in ll_search:er=%d\n",er);
getchar();
}
#endif
//printf("l_search(%s) returns er=%d, n=%d\n", s, er, *n);
return (er);
}
int ll_pdef(char *t) {
int n;
if (ll_search(t, &n, STD) == E_OK) {
ltp = afile->la.lt + n;
if (ltp->fl)
return (E_OK);
}
return (E_NODEF);
}
/*
* Write out the list of global labels in an o65 file
*/
int l_write(FILE *fp) {
int i, afl, n = 0;
if (noglob) {
fputc(0, fp);
fputc(0, fp);
return 0;
}
// calculate number of global labels
for (i = 0; i < afile->la.lti; i++) {
ltp = afile->la.lt + i;
if ((!ltp->blk) && (ltp->fl == 1)) {
n++;
}
}
// write number of globals to file
fputc(n & 255, fp);
fputc((n >> 8) & 255, fp);
// iterate over labels and write out label
for (i = 0; i < afile->la.lti; i++) {
ltp = afile->la.lt + i;
if ((!ltp->blk) && (ltp->fl == 1)) {
// write global name
fprintf(fp, "%s", ltp->n);
fputc(0, fp);
// segment byte
afl = ltp->afl;
// hack to switch undef and abs flag from internal to file format
// if asolute of undefined (< SEG_TEXT, i.e. 0 or 1)
// then invert bit 0 (0 = absolute)
if ((afl & (A_FMASK >> 8)) < SEG_TEXT) {
afl ^= 1;
}
// remove residue flags, only write out real segment number
// according to o65 file format definition
afl = afl & (A_FMASK >> 8);
fputc(afl, fp);
// value
fputc(ltp->val & 255, fp);
fputc((ltp->val >> 8) & 255, fp);
}
}
/*fputc(0,fp);*/
return 0;
}
/*******************************************************************************************
* block management code. Here the ".(" and ".)" blocks are maintained.
*
* Blocks are numbered uniquely, every time a new block is opened, the "blk" variable
* is increased and its number used as block number.
*
* The currently open blocks are maintained in a stack (bt[]). The lowest entry is the outermost
* block number, adding block numbers as blocks are opened. When a block is closed,
* the block stack is shortened again (bi has the length of the block stack)
*
* Methods exist to open new blocks, close a block, and do some checks, e.g. whether
* a specific block number is contained in the current block stack.
*/
static int bt[MAXBLK]; /* block stack */
static int labind; /* last allocated label, -1 none yet alloc'd - used for linking to find unnamed labels */
static int bi; /* length of the block stack (minus 1, i.e. bi[bi] has the innermost block) */
static int blk; /* current block number for allocation */
int b_init(void) {
blk = 0;
bi = 0;
bt[bi] = blk;
labind = -1;
return (E_OK);
}
int b_new(void) {
return ++blk;
}
int b_depth(void) {
return bi;
}
int ga_blk(void) {
return (blk);
}
/**
* open a new block scope
*/
int b_open(void) {
int er = E_BLKOVR;
if (bi < MAXBLK - 1) {
bi++;
bt[bi] = b_new();
er = E_OK;
}
return (er);
}
/**
* close a block scope
*/
int b_close(void) {
if (bi) {
ll_exblk(bt[bi], bt[bi - 1]);
bi--;
} else {
return E_BLOCK;
}
cll_clear();
//unn_clear();
return (E_OK);
}
/**
* get the block number of the current innermost block
*/
static int b_get(int *n) {
*n = bt[bi];
return (E_OK);
}
/**
* returns the block number of the block "i" levels up in the current block stack
*/
static int b_fget(int *n, int i) {
if ((bi - i) >= 0)
*n = bt[bi - i];
else
*n = 0;
return (E_OK);
}
/**
* tests whether the given block number n is in the current stack of
* current block numbers bt[]
*/
static int b_test(int n) {
int i = bi;
while (i >= 0 && n != bt[i])
i--;
return (i + 1 ? E_OK : E_NOBLK);
}
/**
* tests whether the given block number "a" is in the
*/
static int b_ltest(int a, int b) /* testet ob bt^-1(b) in intervall [0,bt^-1(a)] */
{
int i = 0, er = E_OK;
if (a != b) {
er = E_OK;
while (i <= bi && b != bt[i]) {
if (bt[i] == a) {
er = E_NOBLK;
break;
}
i++;
}
}
return (er);
}
int b_link(int newlab) {
int tmp = labind;
//printf("b_link: old was %d, set to %d\n", tmp, newlab);
labind = newlab;
return tmp;
}