#include #include #include #include #include #include "addrmodes.h" #include "parse-fsm.h" #include "parse-action.h" extern const char *__progname; #define MOP_NIL 1 #define MOP_NEG 2 #define MOP_CMP 3 #define MOP_NOT 4 #define DOP_ADD 5 #define DOP_SUB 6 #define DOP_MUL 7 #define DOP_DIV 8 #define DOP_MOD 9 #define DOP_AND 10 #define DOP_OR 11 #define DOP_XOR 12 #define DOP_LSH 13 #define DOP_RSH 14 #define DOP_LT 15 #define DOP_GT 16 #define DOP_LE 17 #define DOP_GE 18 #define DOP_EQ 19 #define DOP_NE 20 typedef struct sym SYM; typedef struct opcode OPCODE; typedef struct isrc ISRC; typedef struct expr EXPR; typedef struct patchup PATCHUP; typedef struct patchlist PATCHLIST; typedef struct xstack XSTACK; typedef struct mempatch MEMPATCH; typedef struct fileline FILELINE; typedef struct writing WRITING; typedef struct strlist STRLIST; typedef struct intlist INTLIST; typedef struct cstack CSTACK; struct cstack { CSTACK *link; int curtrue; int evertrue; const char *file; int line; } ; struct strlist { STRLIST *link; char *s; } ; struct intlist { INTLIST *link; int i; } ; struct writing { WRITING *link; unsigned short int loc; int size; int wrote; } ; struct fileline { FILELINE *link; const char *file; int line; } ; struct mempatch { unsigned short int loc; int size; int count; const char *onerr; } ; struct xstack { XSTACK *link; char type; #define XST_UOP 1 #define XST_BOP 2 #define XST_PAR 3 #define XST_EXP 4 union { int op; EXPR *exp; } u; } ; struct patchlist { PATCHLIST *link; PATCHUP *patchup; } ; struct patchup { EXPR *expr; const char *file; int line; int refcnt; void (*whenfixed)(PATCHUP *); void *arg; } ; struct expr { char type; #define X_CONST 1 #define X_SYMBOL 2 #define X_MONAD 3 #define X_DYAD 4 union { long int val; SYM *sym; struct { char op; EXPR *arg; } monad; struct { char op; EXPR *lhs; EXPR *rhs; } dyad; } u; } ; struct isrc { ISRC *link; const char *name; int curline; FILE *f; } ; struct sym { SYM *link; const char *name; unsigned int flags; EXPR *value; PATCHLIST *patchups; } ; struct opcode { const char *name; unsigned int addrmodes; unsigned char opcodes[AM__N]; } ; #define OM(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x)\ (\ (a?(1U< 0) { skip --; continue; } #define WANTARGS(n) do { skip += (wantedargs = (n)); if (i+skip >= ac) goto needarg; } while (0) if (av[i][0] == '-') { if (!strcmp(av[i],"-in")) { WANTARGS(1); fn_in = av[i+skip]; } else if (!strcmp(av[i],"-out") || !strcmp(av[i],"-o")) { WANTARGS(1); fn_srec = av[i+skip]; } else if (!strcmp(av[i],"-sbase")) { WANTARGS(1); sbase = strtol(av[i+skip],0,16); sbase_arg = 1; } else if (!strcmp(av[i],"-list") || !strcmp(av[i],"-l")) { WANTARGS(1); fn_list = av[i+skip]; } else if (!strcmp(av[i],"-debug")) { debugging = 1; } else { fprintf(stderr,"%s: %s: unrecognized flag\n",__progname,av[i]); errs ++; } } else { if (! fn_in) { fn_in = av[i]; } else if (! fn_srec) { fn_srec = av[i]; } else if (! fn_list) { fn_list = av[i]; } else { fprintf(stderr,"%s: %s: stray extra argument\n",__progname,av[i]); errs ++; } } } if (0) { needarg:; fprintf(stderr,"%s: %s needs ",__progname,av[i]); if (wantedargs == 1) { fprintf(stderr,"a following arg\n"); } else { fprintf(stderr,"%d following args\n",wantedargs); } errs ++; } if (! fn_in) { fprintf(stderr,"%s: need an input file\n",__progname); errs ++; } if (!fn_srec && !fn_list) { fprintf(stderr,"%s: need an output file (s-record or listing)\n",__progname); errs ++; } if (errs) exit(1); } static int null_r(void *arg __attribute__((__unused__)), char *buf __attribute__((__unused__)), int nb __attribute__((__unused__))) { return(0); } static int null_w(void *arg __attribute__((__unused__)), const char *buf __attribute__((__unused__)), int nb) { return(nb); } static FILE *open_file(const char *fn, const char *how, const char *what) { FILE *f; if (fn) { f = fopen(fn,how); if (f == 0) { fprintf(stderr,"%s: can't open %s file %s: %s\n",__progname,what,fn,strerror(errno)); exit(1); } } else { f = funopen(0,null_r,null_w,0,0); if (f == 0) { fprintf(stderr,"%s: can't open dummy %s file: %s\n",__progname,what,strerror(errno)); exit(1); } } return(f); } static void check_opcs(void) { int i; int a; char seen[256]; unsigned char seen_inx[256]; unsigned char seen_am[256]; for (i=0;i<256;i++) seen[i] = 0; for (i=0;opcs[i].name;i++) { if ((strlen(opcs[i].name)%4) != 3) { fprintf(stderr,"%s: %s name length is wrong\n",__progname,opcs[i].name); errs ++; } if (opcs[i].addrmodes == 0) { fprintf(stderr,"%s: %s has no addressing modes\n",__progname,opcs[i].name); errs ++; } for (a=0;acurtrue); } static SYM *newsym(const char *name) { SYM *s; s = malloc(sizeof(SYM)); s->name = name ? strdup(name) : 0; s->flags = 0; s->value = 0; s->patchups = 0; return(s); } static SYM *lookup_sym(const char *name, int createp) { SYM **sp; SYM *s; sp = &syms; while ((s = *sp)) { if (!strcmp(name,s->name)) { if (sp != &syms) { *sp = s->link; s->link = syms; syms = s; } return(s); } sp = &s->link; } if (! createp) return(0); s = newsym(name); s->link = syms; syms = s; return(s); } static EXPR *expr_make(int type, ...) { va_list ap; EXPR *e; e = malloc(sizeof(EXPR)); e->type = type; va_start(ap,type); switch (type) { default: abort(); break; case X_CONST: e->u.val = va_arg(ap,long int); break; case X_SYMBOL: e->u.sym = va_arg(ap,SYM *); break; case X_MONAD: e->u.monad.op = va_arg(ap,int); e->u.monad.arg = va_arg(ap,EXPR *); break; case X_DYAD: e->u.dyad.lhs = va_arg(ap,EXPR *); e->u.dyad.op = va_arg(ap,int); e->u.dyad.rhs = va_arg(ap,EXPR *); break; } return(e); } static void init_stab(void) { int i; syms = 0; dotsym = lookup_sym(".",1); dotsym->value = expr_make(X_CONST,0L); for (i=0;i<10;i++) { char name[3]; name[0] = "0123456789"[i]; name[1] = 'b'; name[2] = '\0'; locsym_b[i] = newsym(&name[0]); name[1] = 'f'; locsym_f[i] = newsym(&name[0]); } } static void reset_file_line(void) { if (istack) { file = istack->name; line = istack->curline; } else { file = 0; line = -1; } } static void pop_istack(void) { ISRC *i; i = istack->link; fclose(istack->f); free(deconst(istack->name)); free(istack); istack = i; reset_file_line(); } static char *getline(void) { char *buf; int len; int have; int c; buf = malloc(1); len = 0; have = 0; while (1) { if (istack == 0) return(0); c = getc(istack->f); if (c == EOF) { if (len > 0) { if (passno == 1) { fprintf(stderr,"%s: %s: missing newline supplied at EOF\n",__progname,file); } buf[len] = '\0'; return(buf); } pop_istack(); continue; } if (c == '\n') { buf[len] = '\0'; return(buf); } if (len >= have) buf = realloc(buf,(have=(len+16))+1); buf[len++] = c; } } static void include_file(const char *fn, const char *tag) { FILE *f; ISRC *i; if (istack && (*fn != '/')) { char *ls; ls = rindex(istack->name,'/'); if (ls) { char *n; n = malloc((ls-istack->name)+1+strlen(fn)+1); sprintf(n,"%.*s/%s",(int)(ls-istack->name),istack->name,fn); free(deconst(fn)); fn = n; } } f = fopen(fn,"r"); if (f == 0) { fprintf(stderr,"%s: can't open %s file %s: %s\n",__progname,tag,fn,strerror(errno)); return; } i = malloc(sizeof(ISRC)); i->link = istack; i->name = fn; i->curline = 0; i->f = f; istack = i; reset_file_line(); } static void init_mem(void) { bzero(&memflags[0],sizeof(memflags)); } static int msg_write(void *arg __attribute__((__unused__)), const char *buf, int len) { msgbuf = realloc(msgbuf,msglen+len+1); bcopy(buf,msgbuf+msglen,len); msglen += len; return(len); } static int err_and_list(void *arg __attribute__((__unused__)), const char *buf, int len) { fwrite(buf,1,len,stderr); fwrite(buf,1,len,msgf); return(len); } static void strlist_push(STRLIST **stk, char *s) { STRLIST *sl; sl = malloc(sizeof(STRLIST)); sl->s = s; sl->link = *stk; *stk = sl; } static char *strlist_pop(STRLIST **stk) { STRLIST *sl; char *rv; sl = *stk; *stk = sl->link; rv = sl->s; free(sl); return(rv); } static void verrmsgfl(const char *, int, const char *, va_list) __attribute__((__format__(__printf__,3,0))); static void verrmsgfl(const char *file, int line, const char *fmt, va_list ap) { FILE *f; fprintf(stderr,"\"%s\", line %d: ",file,line); msgbuf = malloc(1); msglen = 0; msgf = fwopen(0,msg_write); if (flstack) fprintf(msgf,"(\"%s\", line %d) ",file,line); f = fwopen(0,err_and_list); vfprintf(f,fmt,ap); fclose(f); fclose(msgf); msgbuf[msglen] = '\0'; strlist_push(&msgs,msgbuf); fprintf(stderr,"\n"); errs ++; } static void errmsg(const char *, ...) __attribute__((__format__(__printf__,1,2))); static void errmsg(const char *fmt, ...) { va_list ap; va_start(ap,fmt); verrmsgfl(file,line,fmt,ap); va_end(ap); } static void reset_assembled_data(void) { written = 0; } static WRITING *sort_writings(WRITING *list) { WRITING *w1; WRITING *w2; WRITING *w; WRITING **wp; if (!list || !list->link) return(list); w1 = 0; w2 = 0; while (list) { w = list; list = w->link; w->link = w1; w1 = w2; w2 = w; } w1 = sort_writings(w1); w2 = sort_writings(w2); wp = &list; while (w1 || w2) { if (!w2 || (w1 && (w1->loc < w2->loc))) { w = w1; w1 = w->link; } else { w = w2; w2 = w->link; } *wp = w; wp = &w->link; } *wp = 0; return(list); } static void dump_writings(unsigned short int loc, int lno, const char **ltext) { WRITING **wp; WRITING *w; int atloc; int linen; int inx; int mark; static void maybe_print_line(void) { if (*ltext) { if ((atloc < 0) && (list_showvalue != LSV_NO)) { switch (list_showvalue) { case LSV_ASSIGN: if (list_value & ~0xffffL) { fprintf(f_list,"%-18lx",list_value); } else { fprintf(f_list,"%04lx ",list_value); } break; case LSV_UNKASGN: fprintf(f_list,"?""? "); break; case LSV_LABEL: fprintf(f_list,"%04lx: ",list_value); break; default: abort(); break; } } else { if (atloc < 0) { fprintf(f_list," "); linen = 0; } fprintf(f_list,"%*s",(4-linen)*3,""); } fprintf(f_list,"%5d %s",lno,*ltext); *ltext = 0; } } static void do_mark(void) { if (mark) fprintf(f_list,"("); } static void do_newline(void) { if (mark) fprintf(f_list,")"); fprintf(f_list,"\n"); } if (debugging) { fprintf(stderr,"[dump_writings loc=%04hx lno=%d showvalue=%d value=%lx ltext=%p",loc,lno,list_showvalue,list_value,(const void *)*ltext); if (*ltext) fprintf(stderr,"=%s",*ltext); fprintf(stderr,"]\n"); } mark = !*ltext; wp = &written; atloc = -1; while ((w = *wp)) { if (w->loc == loc) { *wp = w->link; inx = 0; while (1) { if (atloc < 0) { do_mark(); fprintf(f_list,"%04hx: ",loc); atloc = loc; linen = 0; continue; } if ( (linen > 3) || ((w->size > 16) && (inx == 0) && (linen > 0)) ) { maybe_print_line(); do_newline(); atloc = -1; continue; } if (w->wrote) { fprintf(f_list," %02x",mem[w->loc+inx]); loc ++; atloc ++; inx ++; linen ++; } else { fprintf(f_list," ?""?"); loc ++; atloc ++; inx ++; linen ++; } if ((w->size > 16) && (inx == 8)) { int n; n = w->size - 16; loc += n; atloc += n; inx += n; do_newline(); do_mark(); fprintf(f_list," .. .. .. .."); } if (inx >= w->size) break; } free(w); } else { wp = &w->link; } } if (*ltext || ((atloc >= 0) && (linen > 0))) { maybe_print_line(); do_newline(); } } static void dump_to_listing(unsigned short int predot, const char *l) { STRLIST *sl; written = sort_writings(written); if (list_on || list_force || msgs) dump_writings(predot,line,&l); sl = 0; while (msgs) strlist_push(&sl,strlist_pop(&msgs)); while (sl) { char *s; s = strlist_pop(&sl); fprintf(f_list,"*** %s\n",s); free(s); } if (list_on) while (written) dump_writings(written->loc,line,&l); while (written) { WRITING *w; w = written; written = w->link; free(w); } } static unsigned short int dot(void) { return(dotsym->value->u.val); } static void assemble(void) { while (1) { char *l; unsigned short int predot; l = getline(); if (! l) break; istack->curline ++; reset_file_line(); if (debugging) { fprintf(stderr,"[line %d in %s: %s]\n",line,file,l); } reset_assembled_data(); predot = dot(); list_force = 0; list_showvalue = LSV_NO; if (! parseline(l)) errmsg("Parse failed"); dump_to_listing(predot,l); free(l); } } static void dump_s_records(void) { int loc; int first; f_srec = open_file(fn_srec,"w","S-record"); first = -1; for (loc=0;loc<=65536;loc++) { if (memflags[loc] & MF_SET) { if (first < 0) first = loc; } else { if (first >= 0) { if (first < sbase) { fprintf(stderr,"%s: data assembled at %04x, below S-record base of %04x, discarded\n",__progname,first,sbase); first = sbase; } while (first < loc) { int n; int i; int s; n = loc - first; if (n > 32) n = 32; i = first - sbase; fprintf(f_srec,"S1%02X%04X",n+3,i); s = (n + 3) + (i & 0xff) + (i >> 8); for (i=0;itype) { default: abort(); break; case X_CONST: case X_SYMBOL: break; case X_MONAD: expr_free(e->u.monad.arg); break; case X_DYAD: expr_free(e->u.dyad.lhs); expr_free(e->u.dyad.rhs); break; } free(e); } static void push_file_line(const char *f, int l) { FILELINE *fl; fl = malloc(sizeof(FILELINE)); fl->file = file; fl->line = line; file = f; line = l; fl->link = flstack; flstack = fl; } static long int perform_op(int op, ...) { va_list ap; long int v; va_start(ap,op); v = va_arg(ap,long int); switch (op) { case MOP_NEG: return(-v); break; case MOP_CMP: return(~v); break; case MOP_NOT: return(!v); break; case DOP_ADD: return(v+va_arg(ap,long int)); break; case DOP_SUB: return(v-va_arg(ap,long int)); break; case DOP_MUL: return(v*va_arg(ap,long int)); break; case DOP_AND: return(v&va_arg(ap,long int)); break; case DOP_OR: return(v|va_arg(ap,long int)); break; case DOP_XOR: return(v^va_arg(ap,long int)); break; case DOP_LSH: return(v<>va_arg(ap,long int)); break; case DOP_LT: return(vva_arg(ap,long int)); break; case DOP_LE: return(v<=va_arg(ap,long int)); break; case DOP_GE: return(v>=va_arg(ap,long int)); break; case DOP_EQ: return(v==va_arg(ap,long int)); break; case DOP_NE: return(v!=va_arg(ap,long int)); break; case DOP_DIV: case DOP_MOD: { long int v2; v2 = va_arg(ap,long int); if (v2 == 0) { errmsg("/ or %% by zero"); return(0); } switch (op) { case DOP_DIV: return(v/v2); break; case DOP_MOD: return(v%v2); break; } } break; } abort(); } static EXPR *simplify_expr(EXPR *e) { switch (e->type) { default: abort(); break; case X_CONST: return(e); break; case X_SYMBOL: if (e->u.sym->value) { e->u.sym->value = simplify_expr(e->u.sym->value); if (e->u.sym->value->type == X_CONST) { long int v; v = e->u.sym->value->u.val; expr_free(e); return(expr_make(X_CONST,v)); } } break; case X_MONAD: e->u.monad.arg = simplify_expr(e->u.monad.arg); if (e->u.monad.arg->type == X_CONST) { long int v; v = perform_op(e->u.monad.op,e->u.monad.arg->u.val); expr_free(e); return(expr_make(X_CONST,v)); } break; case X_DYAD: e->u.dyad.lhs = simplify_expr(e->u.dyad.lhs); e->u.dyad.rhs = simplify_expr(e->u.dyad.rhs); if ( (e->u.dyad.lhs->type == X_CONST) && (e->u.dyad.rhs->type == X_CONST) ) { long int v; v = perform_op(e->u.monad.op,e->u.dyad.lhs->u.val,e->u.dyad.rhs->u.val); expr_free(e); return(expr_make(X_CONST,v)); } break; } return(e); } static void pop_file_line(void) { FILELINE *fl; fl = flstack; flstack = fl->link; file = fl->file; line = fl->line; free(fl); } static void resolve_patchups(SYM *s) { PATCHLIST *l; PATCHUP *p; if (s->value->type != X_CONST) return; if (debugging) fprintf(stderr,"[resolving patchups for %s=%ld]\n",s->name,s->value->u.val); while (s->patchups) { l = s->patchups; s->patchups = l->link; p = l->patchup; free(l); p->refcnt --; if (p->expr) { push_file_line(p->file,p->line); p->expr = simplify_expr(p->expr); if (p->expr->type == X_CONST) { (*p->whenfixed)(p); expr_free(p->expr); p->expr = 0; } pop_file_line(); } if (p->refcnt < 1) { if (p->expr) abort(); free(p); } } } static void locsym_boundary(void) { int i; SYM *s; PATCHLIST *l; for (i=0;i<10;i++) { s = locsym_b[i]; expr_free(s->value); s->value = 0; s = locsym_f[i]; for (l=s->patchups;l;l=l->link) { push_file_line(l->patchup->file,l->patchup->line); errmsg("undefined local label reference %df",i); pop_file_line(); } s->value = expr_make(X_CONST,0L); resolve_patchups(s); expr_free(s->value); s->value = 0; } } static CSTACK *new_cs(void) { CSTACK *cs; cs = malloc(sizeof(CSTACK)); cs->file = 0; return(cs); } static void free_cs(CSTACK *cs) { free(deconst(cs->file)); free(cs); } static CSTACK *pop_cs(void) { CSTACK *cs; cs = ifstack; if (cs) ifstack = cs->link; return(cs); } static void push_cs(CSTACK *cs) { cs->link = ifstack; ifstack = cs; } static void finish_assembly(void) { SYM *s; CSTACK *cs; locsym_boundary(); for (s=syms;s;s=s->link) { if (s->patchups) { fprintf(stderr,"%s: symbol `%s' value never finalized\n",__progname,s->name); } } if (!sbase_arg && !sbase_asm) sbase = 0; while ((cs=pop_cs())) { push_file_line(cs->file,cs->line); errmsg("unmatched .if"); pop_file_line(); free_cs(cs); } } static void init_list(void) { list_on = 1; liststack = 0; } static void init_ifs(void) { ifstack = 0; } int main(int, char **); int main(int ac, char **av) { errs = 0; handleargs(ac,av); istack = 0; flstack = 0; include_file(strdup(fn_in),"input"); f_list = open_file(fn_list,"w","listing"); check_opcs(); init_stab(); init_mem(); init_list(); init_ifs(); assemble(); finish_assembly(); if (! errs) dump_s_records(); exit(errs?1:0); } void pl_trace(int n, const char *name) { if (! debugging) return; fprintf(stderr,"\n"); } void pl_action(const char *s) { if (! debugging) return; fprintf(stderr,"\n",s); } static void parse_blanks(void) { parselinegetarg()->flags |= FSM_FLAG_PARSE_BLANKS; } static void skip_blanks(void) { parselinegetarg()->flags &= ~FSM_FLAG_PARSE_BLANKS; } int check_opcode(const char *str) { int i; const char *n; if (strlen(str) != 3) return(0); for (i=0;opcs[i].name;i++) { n = opcs[i].name; while (1) { if (!strncasecmp(str,n,3) && (opcs[i].addrmodes & ~UNUSED_AMS)) { opcode_index = i; return(1); } if (n[3] == '\0') break; n += 4; } } return(0); } void init_string(void) { if (! string_buf) { string_buf = malloc(1); string_have = 0; } string_len = 0; } void begin_string_part(char term) { string_term = term; parse_blanks(); } int is_string_term(char c) { if (c == string_term) { skip_blanks(); return(1); } return(0); } void save_string_char(char c) { if (string_len >= string_have) { string_buf = realloc(string_buf,(string_have=string_len+16)+1); } string_buf[string_len++] = c; } void abort_string(void) { } int end_string(int abort) { if ((string_len > 0) && !abort) { string_buf[string_len] = '\0'; return(1); } return(0); } void push_include(void) { include_file(strdup(string_buf),".include"); } void locsym_set_digit(char c) { locsym_n = c - '0'; } void locsym_set_direction(char c) { locsym_dir = c; } static void xst_pop(void) { XSTACK *x; if (! xstack) abort(); x = xstack->link; free(xstack); xstack = x; } static void xst_push_expr(EXPR *e) { XSTACK *x; x = malloc(sizeof(XSTACK)); x->link = xstack; x->type = XST_EXP; x->u.exp = e; xstack = x; } static void xst_push_par(void) { XSTACK *x; x = malloc(sizeof(XSTACK)); x->link = xstack; x->type = XST_PAR; xstack = x; } static void xst_push_uop(int op) { XSTACK *x; x = malloc(sizeof(XSTACK)); x->link = xstack; x->type = XST_UOP; x->u.op = op; xstack = x; } static int dop_pri(int dop) { switch (dop) { case DOP_MUL: case DOP_DIV: case DOP_MOD: return(5); break; case DOP_ADD: case DOP_SUB: return(4); break; case DOP_LSH: case DOP_RSH: return(3); break; case DOP_LT: case DOP_GT: case DOP_LE: case DOP_GE: case DOP_EQ: case DOP_NE: return(2); break; case DOP_AND: case DOP_OR: case DOP_XOR: return(1); break; } abort(); } static void xst_collapse1(void) { EXPR *lhs; EXPR *rhs; char opc; if ( !xstack || (xstack->type != XST_EXP) || !xstack->link || (xstack->link->type == XST_EXP) ) abort(); switch (xstack->link->type) { case XST_UOP: rhs = xstack->u.exp; xst_pop(); opc = xstack->u.op; xst_pop(); switch (opc) { default: abort(); break; case MOP_NIL: xst_push_expr(rhs); break; case MOP_NEG: case MOP_CMP: case MOP_NOT: xst_push_expr(expr_make(X_MONAD,opc,rhs)); break; } break; case XST_BOP: rhs = xstack->u.exp; xst_pop(); opc = xstack->u.op; xst_pop(); lhs = xstack->u.exp; xst_pop(); xst_push_expr(expr_make(X_DYAD,lhs,opc,rhs)); break; case XST_PAR: lhs = xstack->u.exp; xst_pop(); xst_pop(); xst_push_expr(lhs); break; } } static void xst_collapse(int pri) { while (1) { if ( !xstack || (xstack->type != XST_EXP) || !xstack->link || (xstack->link->type == XST_EXP) ) return; switch (xstack->link->type) { default: abort(); break; case XST_UOP: xst_collapse1(); break; case XST_BOP: if (dop_pri(xstack->link->u.op) < pri) return; xst_collapse1(); break; case XST_PAR: return; break; } } } static void xst_push_bop(int op) { XSTACK *x; xst_collapse(dop_pri(op)); x = malloc(sizeof(XSTACK)); x->link = xstack; x->type = XST_BOP; x->u.op = op; xstack = x; } static int xst_collapse_paren(void) { while (1) { if (!xstack || (xstack->type != XST_EXP)) abort(); if (! xstack->link) return(0); if (xstack->link->type == XST_EXP) abort(); switch (xstack->link->type) { default: abort(); break; case XST_UOP: xst_collapse1(); break; case XST_BOP: xst_collapse1(); break; case XST_PAR: xst_collapse1(); return(1); break; } } } void begin_expression(void) { xstack = 0; parse_blanks(); } void end_expression(void) { if (! expr_can_end()) abort(); skip_blanks(); } void abort_expression(void) { skip_blanks(); } int expr_paren(char c) { switch (c) { case '(': xst_push_par(); break; case ')': if (! xst_collapse_paren()) return(0); break; } return(1); } int expr_op(char c) { int uop; int bop; uop = -1; bop = -1; switch (c) { default: abort(); break; case '+': uop = MOP_NIL; bop = DOP_ADD; break; case '-': uop = MOP_NEG; bop = DOP_SUB; break; case '*': bop = DOP_MUL; break; case '/': bop = DOP_DIV; break; case '%': bop = DOP_MOD; break; case '&': bop = DOP_AND; break; case '|': bop = DOP_OR; break; case '^': bop = DOP_XOR; break; case '<': bop = DOP_LT; break; case '>': bop = DOP_GT; break; case '~': uop = MOP_CMP; break; case '!': uop = MOP_NOT; break; } if (xstack && (xstack->type == XST_EXP)) { if (bop < 0) return(0); } else { if (uop < 0) return(0); xst_push_uop(uop); return(1); } xst_push_bop(bop); return(1); } int expr_str_op(const char *s) { int op; if (!strcmp(s,"<<")) op = DOP_LSH; else if (!strcmp(s,">>")) op = DOP_RSH; else if (!strcmp(s,"<=")) op = DOP_LE; else if (!strcmp(s,">=")) op = DOP_GE; else if (!strcmp(s,"==")) op = DOP_EQ; else if (!strcmp(s,"!=")) op = DOP_NE; else abort(); if (!xstack || (xstack->type != XST_EXP)) return(0); xst_push_bop(op); return(1); } static int xst_terminal_ok(void) { return(!xstack || (xstack->type != XST_EXP)); } int expr_constant(unsigned long int v) { if (! xst_terminal_ok()) return(0); xst_push_expr(expr_make(X_CONST,v)); return(1); } int expr_symbol(const char *name) { if (! xst_terminal_ok()) return(0); xst_push_expr(expr_make(X_SYMBOL,lookup_sym(name,1))); return(1); } int expr_locsymref(void) { SYM *s; if (! xst_terminal_ok()) return(0); switch (locsym_dir) { case 'b': s = locsym_b[locsym_n]; if (! s->value) { errmsg("undefined local label reference %db",locsym_n); xst_push_expr(expr_make(X_CONST,0L)); } else { xst_push_expr(expr_make(X_SYMBOL,s)); } break; case 'f': s = locsym_f[locsym_n]; xst_push_expr(expr_make(X_SYMBOL,s)); break; } return(1); } int expr_can_end(void) { xst_collapse(0); return(xstack && !xstack->link && (xstack->type == XST_EXP)); } void set_data_size(int n) { data_size = n; } static const char *op_str(int op) { switch (op) { case MOP_NEG: return("-"); break; case MOP_CMP: return("~"); break; case MOP_NOT: return("!"); break; case DOP_ADD: return("+"); break; case DOP_SUB: return("-"); break; case DOP_MUL: return("*"); break; case DOP_DIV: return("/"); break; case DOP_MOD: return("%"); break; case DOP_AND: return("&"); break; case DOP_OR: return("|"); break; case DOP_XOR: return("^"); break; case DOP_LSH: return("<<"); break; case DOP_RSH: return(">>"); break; case DOP_LT: return("<"); break; case DOP_GT: return("<"); break; case DOP_LE: return("<="); break; case DOP_GE: return("<="); break; case DOP_EQ: return("=="); break; case DOP_NE: return("!="); break; } abort(); } static void fprint_expr(FILE *f, EXPR *e) { switch (e->type) { default: fprintf(f,"[?%d]",e->type); break; case X_CONST: fprintf(f,"%ld",e->u.val); break; case X_SYMBOL: fprintf(f,"[%s]",e->u.sym->name); break; case X_MONAD: fprintf(f,"%s",op_str(e->u.monad.op)); fprint_expr(f,e->u.monad.arg); break; case X_DYAD: fprintf(f,"<"); fprint_expr(f,e->u.dyad.lhs); fprintf(f,"%s",op_str(e->u.dyad.op)); fprint_expr(f,e->u.dyad.rhs); fprintf(f,">"); break; } } static EXPR *parsed_expr(void) { EXPR *e; if (!xstack || (xstack->type != XST_EXP) || xstack->link) abort(); e = xstack->u.exp; xst_pop(); if (debugging) { fprintf(stderr,"[parsed_expr: "); fprint_expr(stderr,e); fprintf(stderr," -> "); } e = simplify_expr(e); if (debugging) { fprint_expr(stderr,e); fprintf(stderr,"]\n"); } return(e); } static int setmem_(unsigned short int loc, long int val, int size) { switch (size) { case 1: mem[loc] = val; memflags[loc] |= MF_SET; return((val < -0x80) || (val > 0xff)); break; case 2: mem[loc] = val & 0xff; memflags[loc] |= MF_SET; mem[loc+1] = val >> 8; memflags[loc+1] |= MF_SET; return((val < -0x8000) || (val > 0xffff)); break; case 3: mem[loc] = val & 0xff; memflags[loc] |= MF_SET; mem[loc+1] = (val >> 8) & 0xff; memflags[loc+1] |= MF_SET; mem[loc+2] = val >> 16; memflags[loc+2] |= MF_SET; return((val < -0x800000) || (val > 0xffffff)); break; } abort(); } static void record_write(unsigned short int loc, int size, int wrote) { WRITING *w; if (written && (written->wrote == wrote)) { if (written->loc+written->size == loc) { written->size += size; return; } if (loc+size == written->loc) { written->size += size; written->loc = loc; return; } } w = malloc(sizeof(WRITING)); w->link = written; w->loc = loc; w->size = size; w->wrote = wrote; written = w; } static int setmem(unsigned short int loc, long int val, int size) { if (setmem_(loc,val,size)) return(1); record_write(loc,size,1); return(0); } static int setmem_multi(unsigned short int loc, long int val, int size, int count) { int i; for (i=count;i>0;i--) { if (setmem(loc,val,size)) return(1); loc += size; } return(0); } static int add_a_patchup(EXPR *e, PATCHUP *p) { PATCHLIST *l; switch (e->type) { default: abort(); break; case X_CONST: return(0); break; case X_SYMBOL: if (debugging) fprintf(stderr,"[adding patchup for %s]\n",e->u.sym->name); l = malloc(sizeof(PATCHLIST)); l->link = e->u.sym->patchups; l->patchup = p; e->u.sym->patchups = l; p->refcnt ++; return(1); break; case X_MONAD: return(add_a_patchup(e->u.monad.arg,p)); break; case X_DYAD: return( add_a_patchup(e->u.dyad.lhs,p) + add_a_patchup(e->u.dyad.rhs,p) ); break; } } static void add_patchups(EXPR *e, void (*fixup)(PATCHUP *), void *arg) { PATCHUP *p; p = malloc(sizeof(PATCHUP)); p->expr = e; p->file = strdup(file); p->line = line; p->refcnt = 0; p->whenfixed = fixup; p->arg = arg; add_a_patchup(e,p); } static void mem_patch_fixup(PATCHUP *p) { MEMPATCH *mp; mp = p->arg; if (p->expr->type != X_CONST) abort(); if (debugging) fprintf(stderr,"[mem_patch_fixup: loc=$%04hx,val=%ld,size=%d,count=%d]\n",mp->loc,p->expr->u.val,mp->size,mp->count); if (setmem_multi(mp->loc,p->expr->u.val,mp->size,mp->count)) { errmsg("%s",mp->onerr); } } static void add_mem_patchups(EXPR *e, unsigned short int loc, int size, int count, const char *onerr) { MEMPATCH *mp; mp = malloc(sizeof(MEMPATCH)); mp->loc = loc; mp->size = size; mp->count = count; mp->onerr = onerr; add_patchups(e,mem_patch_fixup,mp); } static void store_or_patchup(EXPR *e, unsigned short int loc, int size, const char *onerr) { if (e->type == X_CONST) { if (setmem(loc,e->u.val,size)) { errmsg("%s",onerr); } expr_free(e); } else { add_mem_patchups(e,loc,size,1,onerr); record_write(loc,size,0); } } static void incdot(int n) { dotsym->value->u.val += n; } void assemble_data_expression(void) { EXPR *e; e = parsed_expr(); switch (data_size) { default: abort(); break; case 1: store_or_patchup(e,dot(),1,"byte value out of range"); incdot(1); break; case 2: store_or_patchup(e,dot(),2,"word value out of range"); incdot(2); break; } } void do_assignment(void) { if ((assign_sym == dotsym) && (assign_expr->type != X_CONST)) { errmsg("can't assign nonconstant value to ."); } else { if (assign_expr->type == X_CONST) { list_value = assign_expr->u.val; list_showvalue = LSV_ASSIGN; } else { list_showvalue = LSV_UNKASGN; } expr_free(assign_sym->value); assign_sym->value = assign_expr; resolve_patchups(assign_sym); } } void save_assignment_symbol(const char *name) { assign_sym = lookup_sym(name,1); } void save_assignment_expression(void) { assign_expr = parsed_expr(); } void save_label_symbol(const char *name) { label_sym = lookup_sym(name,1); } void set_label(void) { locsym_boundary(); expr_free(label_sym->value); label_sym->value = expr_make(X_CONST,dot()); list_value = dot(); list_showvalue = LSV_LABEL; resolve_patchups(label_sym); } void set_loclabel(void) { SYM *s; s = locsym_f[locsym_n]; s->value = expr_make(X_CONST,dot()); resolve_patchups(s); expr_free(s->value); s->value = 0; s = locsym_b[locsym_n]; expr_free(s->value); s->value = expr_make(X_CONST,dot()); } int assemble_space_expression(void) { EXPR *e; e = parsed_expr(); if (e->type == X_CONST) { incdot(e->u.val); expr_free(e); return(1); } else { expr_free(e); return(0); } } int assemble_repeat_count(void) { EXPR *e; e = parsed_expr(); if (e->type == X_CONST) { repeat_count = e->u.val; expr_free(e); if ((repeat_count < 0) || (repeat_count > 0xffff)) return(0); return(1); } else { expr_free(e); return(0); } } void assemble_repeat_value(void) { EXPR *e; const char *onerr; e = parsed_expr(); switch (data_size) { default: abort(); break; case 1: onerr = "rbyte value out of range"; break; case 2: onerr = "rword value out of range"; break; } if (e->type != X_CONST) { add_mem_patchups(e,dot(),data_size,repeat_count,onerr); } else { if (setmem_multi(dot(),e->u.val,data_size,repeat_count)) { errmsg("%s",onerr); } } incdot(repeat_count*data_size); } int am_ok(int n, ...) { va_list ap; int i; int am; va_start(ap,n); for (i=0;iarg; if (p->expr->type != X_CONST) abort(); setmem(mp->loc,p->expr->u.val,1); if (debugging) fprintf(stderr,"[branch_patch_fixup: loc=$%04hx,val=%ld]\n",mp->loc,p->expr->u.val); if ((p->expr->u.val < -128) || (p->expr->u.val > 127)) errmsg("branch too far"); } static void branch(unsigned char opc, EXPR *to) { setmem(dot(),opc,1); incdot(1); to = simplify_expr(expr_make(X_DYAD,to,DOP_SUB,expr_make(X_CONST,dot()+1))); if (to->type == X_CONST) { setmem(dot(),to->u.val,1); if ((to->u.val < -128) || (to->u.val > 127)) errmsg("branch too far"); expr_free(to); } else { MEMPATCH *mp; mp = malloc(sizeof(MEMPATCH)); mp->loc = dot(); mp->size = 1; mp->count = 1; mp->onerr = 0; add_patchups(to,branch_patch_fixup,mp); record_write(dot(),1,0); } incdot(1); } static void abs_or_dir(int amabs, int amdir) { EXPR *e; int abs_ok; int dir_ok; int use_abs; e = parsed_expr(); if (opcs[opcode_index].addrmodes & (1U << amabs)) abs_ok = 1; else abs_ok = 0; if (opcs[opcode_index].addrmodes & (1U << amdir)) dir_ok = 1; else dir_ok = 0; if (!abs_ok && !dir_ok) abort(); if (e->type == X_CONST) { if (e->u.val < 0) { errmsg("address is negative, masked to %d bits",abs_ok?16:8); e->u.val &= abs_ok ? 0xffff : 0xff; use_abs = abs_ok; } else if (e->u.val > (abs_ok?0xffff:0xff)) { errmsg("address too large"); e->u.val &= abs_ok ? 0xffff : 0xff; use_abs = abs_ok; } else { use_abs = (e->u.val > 0xff); if (! abs_ok) use_abs = 0; if (! dir_ok) use_abs = 1; } if (use_abs) { setmem(dot(),opcs[opcode_index].opcodes[amabs],1); incdot(1); setmem(dot(),e->u.val,2); incdot(2); } else { setmem(dot(),opcs[opcode_index].opcodes[amdir],1); incdot(1); setmem(dot(),e->u.val,1); incdot(1); } expr_free(e); } else { if (abs_ok) { setmem(dot(),opcs[opcode_index].opcodes[amabs],1); incdot(1); store_or_patchup(e,dot(),2,"absolute address out of range"); incdot(2); } else { setmem(dot(),opcs[opcode_index].opcodes[amdir],1); incdot(1); store_or_patchup(e,dot(),1,"direct address out of range"); incdot(1); } } } void am_do_expr(void) { if (opcs[opcode_index].addrmodes & (1U << AM_PCREL)) { branch(opcs[opcode_index].opcodes[AM_PCREL],parsed_expr()); return; } abs_or_dir(AM_ABS,AM_DIRECT); } void am_do_stk_rel(void) { setmem(dot(),opcs[opcode_index].opcodes[AM_STK_REL],1); incdot(1); store_or_patchup(parsed_expr(),dot(),1,"stack offset out of range"); incdot(1); } void am_do_expr_x(void) { abs_or_dir(AM_ABS_INX_X,AM_DIR_INX_X); } void am_do_expr_y(void) { abs_or_dir(AM_ABS_INX_Y,AM_DIR_INX_Y); } void am_do_paren_expr(void) { abs_or_dir(AM_ABS_IND,AM_DIR_IND); } void am_do_paren_x_expr(void) { abs_or_dir(AM_ABS_INX_IND,AM_DIR_INX_IND); } void am_do_paren_expr_y(void) { setmem(dot(),opcs[opcode_index].opcodes[AM_DIR_IND_INX],1); incdot(1); store_or_patchup(parsed_expr(),dot(),1,"direct address out of range"); incdot(1); } void am_do_paren_s_expr_y(void) { setmem(dot(),opcs[opcode_index].opcodes[AM_STK_REL_IND_INX],1); incdot(1); store_or_patchup(parsed_expr(),dot(),1,"stack offset out of range"); incdot(1); } void am_do_block(void) { setmem(dot(),opcs[opcode_index].opcodes[AM_BLOCK],1); incdot(1); setmem(dot(),0,2); incdot(2); errmsg("block move instructions unimplemented"); } void assemble_ascii(void) { int i; for (i=0;i 255) { errmsg(".ascic string longer than 255 bytes"); } setmem(dot(),string_len&255,1); incdot(1); for (i=0;ii = v; il->link = *stk; *stk = il; } static int intlist_pop(INTLIST **stk) { INTLIST *il; int rv; il = *stk; *stk = il->link; rv = il->i; free(il); return(rv); } void push_listing(void) { intlist_push(&liststack,list_on); list_force = 1; } void pop_listing(void) { if (liststack == 0) { errmsg("unmatched .list pop"); } else { list_on = intlist_pop(&liststack); } list_force = 1; } void set_sbase(void) { EXPR *e; e = parsed_expr(); if (e->type != X_CONST) { errmsg("non-constant .sbase expression"); } else { if (! sbase_arg) { sbase = e->u.val; sbase_asm = 1; } } expr_free(e); } void do_if(void) { CSTACK *cs; EXPR *e; e = parsed_expr(); if (false_if()) { cs = new_cs(); cs->curtrue = 0; cs->evertrue = 1; } else { if (e->type != X_CONST) { errmsg(".if expression value not known"); cs = 0; } else { cs = new_cs(); cs->curtrue = (e->u.val != 0); cs->evertrue = cs->curtrue; } } if (cs) { cs->file = strdup(file); cs->line = line; push_cs(cs); } expr_free(e); } void do_elif(void) { CSTACK *cs; EXPR *e; e = parsed_expr(); cs = pop_cs(); if (! cs) { errmsg(".elif not inside a .if/.endif"); } else { if (! cs->evertrue) { if (e->type != X_CONST) { errmsg(".elif expression value not known"); cs->curtrue = 0; cs->evertrue = 1; } else { cs->curtrue = (e->u.val != 0); cs->evertrue = cs->curtrue; } } push_cs(cs); } expr_free(e); } void do_else(void) { CSTACK *cs; cs = pop_cs(); if (cs) { cs->curtrue = ! cs->evertrue; cs->evertrue = 1; push_cs(cs); } else { errmsg(".else not inside a .if/.endif"); } } void do_endif(void) { CSTACK *cs; cs = pop_cs(); if (cs) { free_cs(cs); } else { errmsg("unmatched .endif"); } }