ELF2FUZIX = elf2flt
.SUFFIXES: .c .o
-SRCS = as0.c as1.c as2.c as3.c as4.c as5.c as6.c
+SRCS = as0.c as1.c as2.c as3.c as4.c as6.c
-INCS = as.h
+INCS = as.h obj.h
OBJS = $(SRCS:.c=.o)
-all: as
+all: as nm
$(OBJS): $(INCS)
$(LINKER) $^ -o $@.bin $(LINKER_OPT)
$(ELF2FUZIX) -o $@ $@.bin
+nm: $(CRT0) nm.o
+ $(LINKER) $^ -o $@.bin $(LINKER_OPT)
+ $(ELF2FUZIX) -o $@ $@.bin
+
+nm.c: obj.h
+
size.report: $(APPS)
ls -l $^ > $@
.SUFFIXES: .c .o
-SRCS = as0.c as1.c as2.c as3.c as4.c as5.c as6.c
+SRCS = as0.c as1.c as2.c as3.c as4.c as6.c
-INCS = as.h
+INCS = as.h obj.h
OBJS = $(SRCS:.c=.o)
-all: as
+all: as nm
$(OBJS): $(INCS)
as: $(OBJS) $(CRT0)
$(LINKER) -o $@ $(LINKER_OPT) $^
+nm.c: obj.h
+
+nm: nm.o $(CRT0)
+ $(LINKER) -o $@ $(LINKER_OPT) $^
+
clean:
rm -f $(OBJS) as $(SRCS:.c=) core *~
.SUFFIXES: .c .rel
-SRCS = as0.c as1.c as2.c as3.c as4.c as5.c as6.c
+SRCS = as0.c as1.c as2.c as3.c as4.c as6.c
-INCS = as.h
+INCS = as.h obj.h
OBJS = $(SRCS:.c=.rel)
LIBS = ../../../../Library/libs/syslib.lib
-all: as
+all: as nm
as: $(OBJS)
$(FCC) $(PLATFORM) $(OBJS) -o $@
+nm.c: obj.h
+
+nm: nm.rel
+ $(FCC) $(PLATFORM) nm.rel -o $@
+
$(OBJS): $(INCS)
.c.rel:
#define GOOD 0
#define BAD 1
-/*
- * Listing modes.
- */
-#define NLIST 0 /* No list */
-#define ALIST 1 /* Address only */
-#define BLIST 2 /* Byte format */
-#define WLIST 3 /* Word format */
-#define SLIST 4 /* Source text only */
-
/*
* Types. These are used
* in both symbols and in address
#define DATA 2
#define BSS 3
+/*
+ * Error message numbers
+ */
+
+#define BRACKET_EXPECTED 1
+#define MISSING_COMMA 2
+#define SQUARE_EXPECTED 3
+#define PERCENT_EXPECTED 4
+#define UNEXPECTED_CHR 10
+#define PHASE_ERROR 11
+#define MULTIPLE_DEFS 12
+#define SYNTAX_ERROR 13
+#define MUST_BE_ABSOLUTE 14
+#define MISSING_DELIMITER 15
+#define INVALID_CONST 16
+#define JR_RANGE 17
+#define CONDCODE_ONLY 18
+#define INVALID_REG 19
+#define ADDR_REQUIRED 20
+#define INVALID_ID 21
+#define REG_MUST_BE_C 22
+#define DIVIDE_BY_ZERO 23
+#define INVALID_CONSTANT 24
+#define DATA_IN_BSS 25
+#define SEGMENT_OVERFLOW 26
+
+
typedef uint16_t VALUE; /* For symbol values */
/*
* External variables.
*/
extern char *cp;
-extern char *ep;
extern char *ip;
-extern char cb[];
-extern char eb[];
extern char ib[];
extern FILE *ifp;
extern FILE *ofp;
extern VALUE dot[NSEGMENT];
extern int segment;
extern int debug_write;
+extern char *fname;
+extern int noobj;
extern void asmline(void);
extern void asmld(void);
extern void istuser(ADDR *);
extern int ccfetch(ADDR *);
extern int symhash(char *);
-extern void err(char);
+extern void err(char, uint8_t);
extern void uerr(char *);
-extern void aerr(void);
-extern void qerr(void);
+extern void aerr(uint8_t);
+extern void qerr(uint8_t);
extern void storerror(int);
extern void getid(char *, int);
extern SYM *lookup(char *, SYM *[], int);
extern void outeof(void);
extern void outbyte(uint8_t);
extern void outflush(void);
-extern void list(void);
-extern void list1(char *, int, int);
extern void syminit(void);
FILE *ifp;
FILE *ofp;
FILE *lfp;
-char cb[NCODE];
char eb[NERR];
char ib[NINPUT];
char *cp;
char *ep;
char *ip;
-int lflag;
-VALUE laddr;
-int lmode;
+char *fname;
VALUE dot[NSEGMENT];
int segment = CODE;
SYM *phash[NHASH];
int line;
jmp_buf env;
int debug_write = 1 ;
+int noobj;
/*
if (*p == '-') {
while ((c = *++p) != 0) {
switch (c) {
- case 'l':
- ++lflag;
- break;
-
default:
fprintf(stderr, "Bad option %c\n", c);
exit(BAD);
fprintf(stderr, "No source file\n");
exit(BAD);
}
+
+ fname = ifn;
+
if ((ifp=fopen(ifn, "r")) == NULL) {
fprintf(stderr, "%s: cannot open\n", ifn);
exit(BAD);
fprintf(stderr, "%s: cannot create\n", fn);
exit(BAD);
}
- if (lflag != 0) {
- mkname(fn, ifn, "lis");
- if ((lfp=fopen(fn, "w")) == NULL) {
- fprintf(stderr, "%s: cannot create\n", fn);
- exit(BAD);
- }
- }
syminit();
for (pass=0; pass<2; ++pass) {
outpass();
fseek(ifp, 0L, 0);
while (fgets(ib, NINPUT, ifp) != NULL) {
++line;
- cp = &cb[0];
ep = &eb[0];
ip = &ib[0];
if (setjmp(env) == 0)
asmline();
- if (pass != 0)
- list();
}
}
outeof();
* Assemble one line.
* The line in in "ib", the "ip"
* scans along it. The code is written
- * right out, and also stashed in the
- * "cb" for the listing.
+ * right out.
*/
void asmline(void)
{
ADDR a1;
ADDR a2;
- laddr = dot[segment];
- lmode = SLIST;
loop:
if ((c=getnb())=='\n' || c==';')
return;
if (isalpha(c) == 0 && c != '_' && c != '.')
- qerr();
+ qerr(UNEXPECTED_CHR);
getid(id, c);
if ((c=getnb()) == ':') {
sp = lookup(id, uhash, 1);
sp->s_segment = segment;
} else {
if ((sp->s_type&TMMDF) != 0)
- err('m');
+ err('m', MULTIPLE_DEFS);
if (sp->s_value != dot[segment])
- err('p');
+ err('p', PHASE_ERROR);
}
- lmode = ALIST;
goto loop;
}
/*
getid(id1, c);
if ((sp1=lookup(id1, phash, 0)) == NULL
|| (sp1->s_type&TMMODE) != TEQU) {
- err('o');
+ err('o', SYNTAX_ERROR);
return;
}
getaddr(&a1);
sp = lookup(id, uhash, 1);
if ((sp->s_type&TMMODE) != TNEW
&& (sp->s_type&TMASG) == 0)
- err('m');
+ err('m', MULTIPLE_DEFS);
sp->s_type &= ~(TMMODE|TPUBLIC);
sp->s_type |= TUSER|TMASG;
sp->s_value = a1.a_value;
sp->s_segment = a1.a_segment;
/* FIXME: review .equ to an external symbol/offset and
what should happen */
- laddr = a1.a_value;
- lmode = ALIST;
goto loop;
}
unget(c);
- lmode = BLIST;
opcode = sp->s_value;
switch (sp->s_type&TMMODE) {
case TORG:
getaddr(&a1);
istuser(&a1);
if (a1.a_segment != ABSOLUTE)
- qerr();
- lmode = ALIST;
+ qerr(MUST_BE_ABSOLUTE);
segment = 0;
- laddr = dot[segment] = a1.a_value;
+ dot[segment] = a1.a_value;
/* Tell the binary generator we've got a new absolute
segment. */
- outabsolute(laddr);
+ outabsolute(a1.a_value);
break;
case TEXPORT:
break;
case TDEFW:
- lmode = WLIST;
do {
getaddr(&a1);
istuser(&a1);
case TDEFM:
if ((delim=getnb()) == '\n')
- qerr();
+ qerr(MISSING_DELIMITER);
while ((c=get()) != delim) {
if (c == '\n')
- qerr();
+ qerr(MISSING_DELIMITER);
outab(c);
}
break;
case TDEFS:
- laddr = dot[segment];
- lmode = ALIST;
getaddr(&a1);
istuser(&a1);
/* Write out the bytes. The BSS will deal with the rest */
outab(OPRST|(a1.a_value<<3));
break;
}
- aerr();
+ aerr(INVALID_CONST);
break;
case TREL:
getaddr(&a1);
if ((cc=ccfetch(&a1)) >= 0) {
if (opcode==OPDJNZ || cc>=CPO)
- aerr();
+ aerr(SYNTAX_ERROR);
opcode = OPJR | (cc<<3);
comma();
getaddr(&a1);
}
istuser(&a1);
disp = a1.a_value-dot[segment]-2;
- if (disp<-128 || disp>127)
- aerr();
+ if (disp<-128 || disp>127 || a1.a_segment != segment)
+ aerr(JR_RANGE);
outab(opcode);
outab(disp);
break;
if (c!='\n' && c!=';') {
getaddr(&a1);
if ((cc=ccfetch(&a1)) < 0)
- aerr();
+ aerr(CONDCODE_ONLY);
opcode = OPRET | (cc<<3);
}
outab(opcode);
reg = a1.a_type&TMREG;
if (reg==M || reg==IX || reg==IY) {
if (opcode != OPJP)
- aerr();
+ aerr(INVALID_REG);
outop(OPPCHL, &a1);
break;
}
break;
case SP:
case AFPRIME:
- aerr();
+ aerr(INVALID_REG);
}
outab(opcode|(reg<<4));
break;
}
- aerr();
+ aerr(INVALID_REG);
break;
case TIM:
getaddr(&a1);
istuser(&a1);
if ((value=a1.a_value) > 2)
- aerr();
+ aerr(INVALID_CONST);
else if (value != 0)
++value;
outab(0xED);
if ((a1.a_type&TMMODE)==TBR && a2.a_type==(TBR|TMINDIR|C)) {
reg = a1.a_type&TMREG;
if (reg==M || reg==IX || reg==IY)
- aerr();
+ aerr(INVALID_REG);
outab(0xED);
if (opcode == OPIN)
opcode = OPIIN; else
outab(opcode|(reg<<3));
break;
}
- aerr();
+ aerr(INVALID_REG);
break;
case TBIT:
outop(opcode|(a1.a_value<<3)|reg, &a2);
break;
}
- aerr();
+ aerr(INVALID_REG);
break;
case TSHR:
outop(opcode|reg, &a1);
break;
}
- aerr();
+ aerr(INVALID_REG);
case TINC:
getaddr(&a1);
break;
case AF:
case AFPRIME:
- aerr();
+ aerr(INVALID_REG);
}
if (opcode == OPINC)
opcode = OPINCRP; else
outop(opcode|(reg<<3), &a1);
break;
}
- aerr();
+ aerr(INVALID_REG);
break;
case TEX:
else if (a1.a_type == (TWR|TMINDIR|SP))
opcode = OPXTHL;
else
- aerr();
+ aerr(INVALID_REG);
if (a2.a_type == (TWR|HL))
outab(opcode);
else if (a2.a_type == (TWR|IX)) {
outab(0xFD);
outab(opcode);
} else
- aerr();
+ aerr(INVALID_REG);
break;
case TSUB:
outop(opcode|reg, &a1);
break;
}
- aerr();
+ aerr(INVALID_REG);
break;
case TADD:
switch(reg = a1.a_type&TMREG) {
case IX:
if (opcode != OPADD)
- aerr();
+ aerr(INVALID_REG);
outab(0xDD);
opcode = OPDAD;
srcreg = IX;
break;
case IY:
if (opcode != OPADD)
- aerr();
+ aerr(INVALID_REG);
outab(0xFD);
opcode = OPDAD;
srcreg = IY;
srcreg = HL;
break;
default:
- aerr();
+ aerr(INVALID_REG);
}
if ((a2.a_type&TMMODE) == TWR) {
reg = a2.a_type&TMREG;
}
}
}
- aerr();
+ aerr(INVALID_REG);
break;
case TLD:
break;
default:
- err('o');
+ aerr(SYNTAX_ERROR);
}
goto loop;
}
outrab(indexap);
return;
}
- aerr();
+ aerr(INVALID_REG);
}
/*
case TWR|AF:
case TWR|AFPRIME:
- aerr();
+ aerr(INVALID_REG);
reg = HL;
}
*modep = mode;
void comma(void)
{
if (getnb() != ',')
- qerr();
+ qerr(MISSING_COMMA);
}
/*
void istuser(ADDR *ap)
{
if ((ap->a_type&TMMODE) != TUSER)
- aerr();
+ aerr(ADDR_REQUIRED);
}
/*
return (hash&HMASK);
}
+/* We may want to move this out into a a helper app at the end to dump
+ errors without using assembler space */
+
+static char *etext[] = {
+ "unexpected character",
+ "phase error",
+ "multiple definitions",
+ "syntax error",
+ "must be absolute",
+ "missing delimiter",
+ "invalid constant",
+ "JR out of range",
+ "condition required",
+ "invalid register for operation",
+ "address required",
+ "invalid id",
+ "must be C",
+ "divide by 0",
+ "invalid constant",
+ "data in BSS",
+ "segment overflow"
+};
+
+static void errstr(uint8_t code)
+{
+ if (code < 10) {
+ printf("%c expected.\n", "),]%"[code-1]);
+ return;
+ }
+ printf("%s.\n", etext[code - 10]);
+}
+
/*
* Handle an error.
* If no listing file, write out
* the error directly. Otherwise save
* the error in the error buffer.
+ *
+ * Will need tweaking once we support .include as we must show the file
+ * name then. TODO
*/
-void err(char c)
+void err(char c, uint8_t code)
{
if (pass != 0) {
- if (lflag != 0)
- storerror(c);
- else
- printf("%04d %c\n", line, c);
+ printf("%s: %d: %c: ", fname, line, toupper(c));
+ errstr(code);
+ noobj = 1;
}
if (c == 'q')
longjmp(env, 1);
}
/*
- * This routine is like
- * "err", but it has the "u"
- * code screwed into it, and it
- * prints the name of the identifier
- * "id" in the message.
+ * Not really an error any more (we resolve at link time)
*/
void uerr(char *id)
{
- if (pass != 0) {
- if (lflag != 0)
- storerror('u');
- else
- printf("%04d u %.*s\n", line, NCPS, id);
- }
}
/*
* The "a" error is common.
*/
-void aerr(void)
+void aerr(uint8_t code)
{
- err('a');
+ err('a', code);
}
/*
* Ditto the "q" error.
*/
-void qerr(void)
-{
- err('q');
-}
-
-/*
- * Put the error code
- * "c" into the error buffer.
- * Check that it is not already
- * there.
- */
-
-void storerror(int c)
+void qerr(uint8_t code)
{
- char *p;
-
- p = &eb[0];
- while (p < ep)
- if (*p++ == c)
- return;
- if (p < &eb[NERR]) {
- *p++ = c;
- ep = p;
- }
+ err('q', code);
}
/*
if (c < 0) {
c = getnb();
if (isalpha(c) == 0 && c != '_' && c != '.')
- qerr();
+ qerr(INVALID_ID);
}
p = &id[0];
do {
{
SYM *sp;
int hash;
- static int symnext;
hash = symhash(id);
sp = htable[hash];
sp->s_type = TNEW;
sp->s_value = 0;
sp->s_segment = UNKNOWN;
- sp->s_number = ++symnext;
+ sp->s_number = -1;
symcopy(sp->s_id, id);
}
return (sp);
}
expr1(ap, LOPRI, 1);
if (getnb() != ')')
- qerr();
+ qerr(BRACKET_EXPECTED);
reg = ap->a_type&TMREG;
switch (ap->a_type&TMMODE) {
case TBR:
if (reg != C)
- aerr();
+ aerr(REG_MUST_BE_C);
ap->a_type |= TMINDIR;
break;
case TSR:
case TCC:
- aerr();
+ aerr(ADDR_REQUIRED);
break;
case TUSER:
ap->a_type |= TMINDIR;
else if (reg==IX || reg==IY)
ap->a_type = TBR|reg;
else if (reg==AF || reg==AFPRIME)
- aerr();
+ aerr(INVALID_REG);
else
ap->a_type |= TMINDIR;
}
if ((a->a_type & TMMODE) != TUSER)
return;
if (a->a_segment != ABSOLUTE)
- aerr();
+ aerr(MUST_BE_ABSOLUTE);
}
static void chksegment(ADDR *left, ADDR *right, int op)
return;
}
left->a_sym = NULL;
- aerr();
+ aerr(MUST_BE_ABSOLUTE);
}
/*
istuser(&right);
chksegment(ap, &right, '/');
if (right.a_value == 0)
- err('z');
+ err('z', DIVIDE_BY_ZERO);
else
ap->a_value /= right.a_value;
}
if (c == '[') {
expr1(ap, LOPRI, 0);
if (getnb() != ']')
- qerr();
+ qerr(SQUARE_EXPECTED);
return;
}
if (c == '-') {
ap->a_segment = ABSOLUTE;
while ((c=get()) != '\'') {
if (c == '\n')
- qerr();
+ qerr(PERCENT_EXPECTED);
ap->a_value = (ap->a_value<<8) + c;
}
return;
ap->a_segment = sp->s_segment;
return;
}
- qerr();
+ qerr(SYNTAX_ERROR);
}
/*
else if (c>='a' && c<='f')
c -= 'a'-10;
else
- err('n');
+ err('n', INVALID_CONSTANT);
if (c >= radix)
- err('n');
+ err('n', INVALID_CONSTANT);
value = radix*value + c;
}
ap->a_type = TUSER;
if (reg==IX || reg==IY)
return;
}
- aerr();
+ aerr(ADDR_REQUIRED);
}
#include "as.h"
#include "obj.h"
-#define NHEX 8 /* Nice format size */
-
static uint16_t segsize[NSEGMENT];
static uint16_t truesize[NSEGMENT];
static off_t segbase[NSEGMENT];
-struct objhdr obh;
+static struct objhdr obh;
-static void outc(char c);
+static void numbersymbols(void);
void outpass(void)
{
if (i != BSS) {
obh.o_segbase[i] = base;
segbase[i] = base;
- printf("BASE %d %d\n", i, base);
base += segsize[i];
- printf("SIZE %d %d\n", i, truesize[i]);
}
obh.o_size[i] = truesize[i];
}
obh.o_magic = 0;
+ obh.o_arch = OA_Z80;
+ obh.o_flags = 0;
+ /* Will need changing if we add .Z180 and the Z180 ops */
+ obh.o_cpuflags = 0;
obh.o_symbase = base;
obh.o_dbgbase = 0; /* for now */
+ /* Number the symbols for output */
+ numbersymbols();
}
}
void outraw(ADDR *a)
{
if (a->a_segment != ABSOLUTE) {
- /* FIXME@ handle symbols */
if (segment == BSS)
- err('b');
+ err('b', DATA_IN_BSS);
if (a->a_sym == NULL) {
outbyte(REL_ESC);
outbyte((1 << 4) | REL_SIMPLE | a->a_segment);
{
/* Not allowed to put data in the BSS except zero */
if (segment == BSS && b)
- err('b');
+ err('b', DATA_IN_BSS);
if (segment == ABSOLUTE)
- err('A');
+ err('A', MUST_BE_ABSOLUTE);
outbyte(b);
if (b == 0xDA) /* Quote relocation markers */
outbyte(0x00);
++dot[segment];
++truesize[segment];
if (truesize[segment] == 0 || dot[segment] == 0)
- err('o');
+ err('o', SEGMENT_OVERFLOW);
}
void outrab(ADDR *a)
/* FIXME: handle symbols */
if (a->a_segment != ABSOLUTE) {
if (segment == BSS)
- err('b');
+ err('b', DATA_IN_BSS);
if (a->a_sym == NULL) {
outbyte(REL_ESC);
outbyte((0 << 4) | REL_SIMPLE | a->a_segment);
{
int i;
uint8_t flag = 0;
- printf("Putsymbol %s\n", s->s_id);
if (s->s_type == TNEW)
flag |= S_UNKNOWN;
else {
flag |= s->s_segment;
}
putc(flag, ofp);
- putc(s->s_number, ofp);
- putc(s->s_number >> 8, ofp);
for (i = 0; i < 16; i++) {
putc(s->s_id[i], ofp);
if (!s->s_id[i])
}
}
-static void writesymbols(SYM *hash[], FILE *ofp, int flag)
+static void enumerate(SYM *s, FILE *dummy)
+{
+ static int sym = 0;
+ s->s_number = sym++;
+}
+
+static void dosymbols(SYM *hash[], FILE *ofp, int flag, void (*op)(SYM *, FILE *f))
{
int i;
- fseek(ofp, obh.o_symbase, SEEK_SET);
for (i = 0; i < NHASH; i++) {
SYM *s;
for (s = hash[i]; s != NULL; s = s->s_fp) {
continue;
n = (t == TNEW) || (t == TUSER && (s->s_type & TPUBLIC));
if (n == flag)
- putsymbol(s, ofp);
+ op(s, ofp);
}
}
}
+static void writesymbols(SYM *hash[], FILE *ofp)
+{
+ fseek(ofp, obh.o_symbase, SEEK_SET);
+ dosymbols(hash, ofp, 1, putsymbol);
+ if (debug_write) {
+ obh.o_dbgbase = ftell(ofp);
+ dosymbols(uhash, ofp, 0, putsymbol);
+ }
+}
+
+static void numbersymbols(void)
+{
+ dosymbols(uhash, NULL, 1, enumerate);
+}
+
/*
* Put out the end of file
* hex item at the very end of
*/
void outeof(void)
{
- writesymbols(phash, ofp, 1);
- writesymbols(uhash, ofp, 1);
- if (debug_write) {
- obh.o_dbgbase = ftell(ofp);
- writesymbols(phash, ofp, 0);
- writesymbols(uhash, ofp, 0);
- }
+ /* We don't do the final write out if there was an error. That
+ leaves the magic wrong on the object file so it can't be used */
+ if (noobj || pass == 0)
+ return;
+
+ writesymbols(uhash, ofp);
rewind(ofp);
obh.o_magic = MAGIC_OBJ;
fwrite(&obh, sizeof(obh), 1, ofp);
- printf("Code %d byyes: Data %d bytes: BSS %d bytes\n",
- truesize[CODE], truesize[DATA], truesize[BSS]);
+/* printf("Code %d bytes: Data %d bytes: BSS %d bytes\n",
+ truesize[CODE], truesize[DATA], truesize[BSS]); */
}
/*
+++ /dev/null
-/*
- * Z-80 assembler.
- * Build up lines for the
- * listing file.
- */
-#include "as.h"
-
-/*
- * Copy the data in the listing
- * code buffer to the listing file.
- * Produce no file if "lfp" is NULL.
- * Honour the listing mode stored
- * in the "lmode".
- */
-void list(void)
-{
- char *wp;
- int nb;
-
- if (lfp==NULL || lmode==NLIST)
- return;
- while (ep < &eb[NERR])
- *ep++ = ' ';
- fprintf(lfp, "%.10s", eb);
- if (lmode == SLIST) {
- fprintf(lfp, "%31s %5d %s", "", line, ib);
- return;
- }
- fprintf(lfp, " %04x", laddr);
- if (lmode == ALIST) {
- fprintf(lfp, "%24s %5d %s", "", line, ib);
- return;
- }
- wp = cb;
- nb = cp - cb;
- list1(wp, nb, 1);
- fprintf(lfp, " %5d %s", line, ib);
- while ((nb -= 8) > 0) {
- wp += 8;
- fprintf(lfp, "%17s", "");
- list1(wp, nb, 0);
- fprintf(lfp, "\n");
- }
-}
-
-/*
- * Copy out a partial line
- * to the listing. Used for the first
- * and the extra lines in BLIST and
- * WLIST mode.
- */
-void list1(char *wp, int nb, int f)
-{
- int d;
- int i;
-
- if (nb > 8)
- nb = 8;
- for (i=0; i<nb; ++i) {
- d = (*wp++) & 0xFF;
- if (lmode == BLIST)
- fprintf(lfp, " %02x", d);
- else {
- d |= *wp++ << 8;
- fprintf(lfp, " %04x", d);
- ++i;
- }
- }
- if (f != 0) {
- while (i < 8) {
- fprintf(lfp, " ");
- ++i;
- }
- }
-}
--- /dev/null
+/*
+ * For now this only works on object files. It's mostly here so I can
+ * check the assembler output looks valid.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <stdint.h>
+
+#include "obj.h"
+
+static char *arg0;
+static int err;
+static int show_debug = 1;
+static int show_name;
+static int show_undef;
+
+static char segname[] = "ATDB????????????";
+
+static int do_nm(FILE *fp, const char *name)
+{
+ static struct objhdr oh;
+ off_t base;
+ uint8_t type;
+ int c;
+ uint8_t i;
+ uint16_t addr;
+ char symname[17];
+
+ if (show_name)
+ printf("%s:\n", name);
+ if (fread(&oh, sizeof(oh), 1, fp) != 1 ||
+ oh.o_magic != MAGIC_OBJ) {
+ fprintf(stderr, "%s: %s: not a valid object file.\n", arg0, name);
+ return 1;
+ }
+ base = oh.o_symbase;
+ if (base == 0) {
+ fprintf(stderr, "%s: %s: no symbols.\n", arg0, name);
+ return 0;
+ }
+ if (fseek(fp, base, SEEK_SET)) {
+ fprintf(stderr, "%s: %s: truncated file ?\n", arg0, name);
+ return 1;
+ }
+ while (1) {
+ if (base >= oh.o_dbgbase && show_debug == 0)
+ break;
+ c = fgetc(fp);
+ if (c == EOF)
+ return 0;
+ type = (uint8_t)c;
+ base++;
+ for (i = 0; i < 16; i++) {
+ c = fgetc(fp);
+ base++;
+ symname[i] = (char)c;
+ if (c == 0)
+ break;
+ }
+ symname[16] = 0;
+ /* Address if defined */
+ if (!(type & S_UNKNOWN)) {
+ addr = fgetc(fp);
+ addr |= fgetc(fp) << 8;
+ base += 2;
+ c = segname[type & S_SEGMENT];
+ /* Showing undefined only */
+ if (show_undef)
+ continue;
+ } else {
+ addr = 0;
+ c = 'U';
+ }
+ printf("%04X %c %s\n", addr, c, symname);
+ }
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int opt;
+ arg0 = argv[0];
+
+ while ((opt = getopt(argc,argv,"oAug")) != -1) {
+ switch(opt) {
+ case 'o':
+ case 'A':
+ show_name = 1;
+ break;
+ case 'u':
+ show_undef = 1;
+ break;
+ case 'g':
+ show_debug = 0;
+ break;
+ default:
+ fprintf(stderr, "%s: name ...\n", argv[0]);
+ exit(1);
+ }
+ }
+
+ /* Show names if multiple arguments */
+ if (optind - argc > 1)
+ show_name = 1;
+
+ if (optind >= argc)
+ do_nm(stdin, "-");
+ else while (optind < argc) {
+ FILE *fp = fopen(argv[optind], "r");
+ if (fp == NULL) {
+ perror(argv[optind]);
+ err |= 1;
+ } else {
+ err |= do_nm(fp, argv[optind]);
+ fclose(fp);
+ }
+ optind++;
+ }
+ exit(err);
+}
+
--- /dev/null
+#define MAGIC_OBJ 0x3D1A
+#define MAGIC_OBJ_SWAPPED 0x1A3D
+
+#define OSEG 8
+
+struct objhdr
+{
+ uint16_t o_magic;
+ uint8_t o_arch;
+#define OA_Z80 1
+ uint8_t o_flags;
+ uint16_t o_cpuflags;
+#define OA_Z80_Z180 1
+#define OA_Z80_Z280 2
+#define OA_Z80_R800 4
+ uint32_t o_segbase[OSEG];
+ uint16_t o_size[OSEG];
+ uint32_t o_symbase;
+ uint32_t o_dbgbase;
+};
+
+/* This byte introduces a relocation or may be escaped */
+#define REL_ESC 0xDA
+#define REL_REL 0x00 /* DA00 means write in DA */
+
+#define REL_SIZE 0x30 /* 1-4 bytes */
+/* If REL_SIMPLE then ... */
+#define REL_SIMPLE 0x80 /* relocationto base of segment */
+#define REL_SEG 0x0F /* segment 0-15 */
+
+/* followed by the bytes to relocate */
+
+/* Otherwise */
+#define REL_TYPE 0x0F
+/* 00 is reserved */
+#define REL_SYMBOL 0x01
+/* 02-0F reserved */
+/* Followed by 2 byte number of symbol in symbol table */
+
+/* followed by the bytes to relocate */
+
+
+/* symbols and debug are in the format
+ uint8_t flags
+ char name[1..16] (0 terminated if < 16)
+ uint16_t data if not unknown */
+
+#define S_UNKNOWN 0x80
+#define S_PUBLIC 0x40 /* unknown is public .. */
+#define S_SEGMENT 0x0F /* 00 means absolute */
+
+
+