/* syntax.c syntax module for vasm */ /* (c) in 2002-2018 by Frank Wille */ #include "vasm.h" /* The syntax module parses the input (read_next_line), handles assembly-directives (section, data-storage etc.) and parses mnemonics. Assembly instructions are split up in mnemonic name, qualifiers and operands. new_inst returns a matching instruction, if one exists. Routines for creating sections and adding atoms to sections will be provided by the main module. */ char *syntax_copyright="vasm oldstyle syntax module 0.13f (c) 2002-2018 Frank Wille"; hashtable *dirhash; static char textname[]=".text",textattr[]="acrx"; static char dataname[]=".data",dataattr[]="adrw"; static char rodataname[]=".rodata",rodataattr[]="adr"; static char bssname[]=".bss",bssattr[]="aurw"; char commentchar=';'; char *defsectname = textname; char *defsecttype = "acrwx"; static char macname[] = ".mac"; static char macroname[] = ".macro"; static char eqname[] = ".eq"; static char equname[] = ".equ"; static char setname[] = ".set"; static char endmname[] = ".endmacro"; static char endrname[] = ".endrepeat"; static char reptname[] = ".rept"; static char repeatname[] = ".repeat"; static struct namelen endm_dirlist[] = { { 4,&endmname[1] }, { 6,&endmname[1] }, { 8,&endmname[1] }, { 0,0 } }; static struct namelen rept_dirlist[] = { { 4,&reptname[1] }, { 6,&repeatname[1] }, { 0,0 } }; static struct namelen endr_dirlist[] = { { 4,&endrname[1] }, { 6,&endrname[1] }, { 9,&endrname[1] }, { 0,0 } }; static struct namelen dendm_dirlist[] = { { 5,&endmname[0] }, { 7,&endmname[0] }, { 9,&endmname[0] }, { 0,0 } }; static struct namelen drept_dirlist[] = { { 5,&reptname[0] }, { 7,&repeatname[0] }, { 0,0 } }; static struct namelen dendr_dirlist[] = { { 5,&endrname[0] }, { 7,&endrname[0] }, { 10,&endrname[0] }, { 0,0 } }; static int dotdirs,autoexport,parse_end,igntrail,nocprefix,nointelsuffix; static taddr orgmode = ~0; char *skip(char *s) { while (isspace((unsigned char )*s)) s++; return s; } /* check for end of line, issue error, if not */ void eol(char *s) { if (igntrail) { if (!ISEOL(s) && !isspace((unsigned char)*s)) syntax_error(6); } else { s = skip(s); if (!ISEOL(s)) syntax_error(6); } } char *exp_skip(char *s) { if (!igntrail) { s = skip(s); if (*s == commentchar) *s = '\0'; /* rest of operand is ignored */ } else if (isspace((unsigned char)*s) || *s==commentchar) *s = '\0'; /* rest of operand is ignored */ return s; } static char *skip_oper(int instoper,char *s) { #ifdef VASM_CPU_Z80 unsigned char lastuc = 0; #endif int par_cnt = 0; char c = 0; for (;;) { s = exp_skip(s); #ifdef VASM_CPU_Z80 if (c) lastuc = toupper((unsigned char)*(s-1)); #endif c = *s; if (START_PARENTH(c)) par_cnt++; else if (END_PARENTH(c)) { if (par_cnt>0) par_cnt--; else syntax_error(3); /* too many closing parentheses */ } #ifdef VASM_CPU_Z80 /* For the Z80 ignore ' behind a letter, as it may be a register */ else if ((c=='\'' && (lastuc<'A' || lastuc>'Z')) || c=='\"') #else else if (c=='\'' || c=='\"') #endif s = skip_string(s,c,NULL) - 1; else if (instoper && OPERSEP_COMMA && c==',' && par_cnt==0) break; else if (instoper && OPERSEP_BLANK && isspace((unsigned char)c) && par_cnt==0) break; else if (!instoper && c==',' && par_cnt==0) break; else if (c == '\0') break; s++; } if(par_cnt != 0) syntax_error(4); /* missing closing parentheses */ return s; } char *skip_operand(char *s) { return skip_oper(1,s); } char *my_skip_macro_arg(char *s) { if (*s == '\\') s++; /* leading \ in argument list is optional */ return skip_identifier(s); } #define handle_data(a,b) handle_data_offset(a,b,0) static void handle_data_offset(char *s,int size,int offset) { for (;;) { char *opstart = s; operand *op; dblock *db = NULL; if (size==8 && (*s=='\"' || *s=='\'')) { if (db = parse_string(&opstart,*s,8)) { #if defined(VASM_CPU_650X) || defined(VASM_CPU_Z80) || defined(VASM_CPU_6800) if (offset != 0) { int i; for (i=0; isize; i++) db->data[i] = db->data[i] + offset; } #endif add_atom(0,new_data_atom(db,1)); s = opstart; } } if (!db) { op = new_operand(); s = skip_oper(0,s); if (parse_operand(opstart,s-opstart,op,DATA_OPERAND(size))) { atom *a; #if defined(VASM_CPU_650X) || defined(VASM_CPU_Z80) || defined(VASM_CPU_6800) if (offset != 0) op->value = make_expr(ADD,number_expr(offset),op->value); #endif a = new_datadef_atom(abs(size),op); a->align = 1; add_atom(0,a); } else syntax_error(8); /* invalid data operand */ } s = skip(s); if (*s == ',') { s = skip(s+1); } else if (*s == commentchar) { break; } else if (*s) { syntax_error(9); /* , expected */ return; } else break; } eol(s); } static void handle_text(char *s) { char *opstart = s; operand *op; dblock *db = NULL; if (db = parse_string(&opstart,*s,8)) { add_atom(0,new_data_atom(db,1)); s = opstart; } if (!db) { op = new_operand(); s = skip_oper(0,s); if (parse_operand(opstart,s-opstart,op,DATA_OPERAND(8))) { atom *a; a = new_datadef_atom(8,op); a->align = 1; add_atom(0,a); } else syntax_error(8); /* invalid data operand */ } eol(s); } static void handle_d8(char *s) { handle_data(s,8); } static void handle_d16(char *s) { handle_data(s,16); } static void handle_d24(char *s) { handle_data(s,24); } static void handle_d32(char *s) { handle_data(s,32); } #if defined(VASM_CPU_650X) || defined(VASM_CPU_Z80) || defined(VASM_CPU_6800) static void handle_d8_offset(char *s) { taddr offs = parse_constexpr(&s); s = skip(s); if (*s == ',') { s = skip(s+1); handle_data_offset(s,8,offs); } else syntax_error(9); /* , expected */ } #endif static void do_alignment(taddr align,expr *offset) { atom *a = new_space_atom(offset,1,0); a->align = align; add_atom(0,a); } static void handle_align(char *s) { taddr a = parse_constexpr(&s); if (a > 63) syntax_error(21); /* bad alignment */ do_alignment(1LL<>3,fill)); } static void handle_space(char *s,int size) { expr *cnt,*fill=0; cnt = parse_expr_tmplab(&s); s = skip(s); if (*s == ',') { s = skip(s+1); fill = parse_expr_tmplab(&s); } do_space(size,cnt,fill); eol(s); } static void handle_fixedspc1(char *s) { do_space(8,number_expr(1),0); eol(s); } static void handle_fixedspc2(char *s) { do_space(8,number_expr(2),0); eol(s); } static void handle_spc8(char *s) { handle_space(s,8); } static void handle_spc16(char *s) { handle_space(s,16); } #if 0 static void handle_spc24(char *s) { handle_space(s,24); } static void handle_spc32(char *s) { handle_space(s,32); } #endif static void handle_string(char *s) { handle_data(s,8); add_atom(0,new_space_atom(number_expr(1),1,0)); /* terminating zero */ } static void handle_end(char *s) { parse_end = 1; eol(s); } static void handle_fail(char *s) { add_atom(0,new_assert_atom(NULL,NULL,mystrdup(s))); } static void handle_org(char *s) { if (*s == current_pc_char) { char *s2 = skip(s+1); if (*s2++ == '+') { handle_space(skip(s2),8); /* "* = * + " to reserves bytes */ return; } } set_section(new_org(parse_constexpr(&s))); eol(s); } static void handle_rorg(char *s) { start_rorg(parse_constexpr(&s)); eol(s); } static void handle_rend(char *s) { if (end_rorg()) eol(s); } static void handle_roffs(char *s) { add_atom(0,new_roffs_atom(parse_expr_tmplab(&s))); } static void handle_section(char *s) { char *name,*attr; if (!(name=parse_name(&s))) return; if (*s==',') { s = skip(s+1); attr = s; if (*s!= '\"') syntax_error(7,'\"'); /* " expected */ else s++; attr = s; while (*s&&*s!='\"') s++; attr = cnvstr(attr,s-attr); s = skip(s+1); } else { if (!strcmp(name,textname)) attr = textattr; if (!strcmp(name,dataname)) attr = dataattr; if (!strcmp(name,rodataname)) attr = rodataattr; if (!strcmp(name,bssname)) attr = bssattr; else attr = defsecttype; } new_section(name,attr,1); switch_section(name,attr); eol(s); } static void do_binding(char *s,int bind) { symbol *sym; char *name; while (1) { if (!(name=parse_identifier(&s))) return; sym = new_import(name); myfree(name); if (sym->flags&(EXPORT|WEAK|LOCAL)!=0 && sym->flags&(EXPORT|WEAK|LOCAL)!=bind) general_error(62,sym->name,get_bind_name(sym)); /* binding already set */ else sym->flags |= bind; s = skip(s); if (*s != ',') break; s = skip(s+1); } eol(s); } static void handle_global(char *s) { do_binding(s,EXPORT); } static void handle_weak(char *s) { do_binding(s,WEAK); } static void handle_local(char *s) { do_binding(s,LOCAL); } static void ifdef(char *s,int b) { char *name; symbol *sym; int result; if (!(name = parse_symbol(&s))) { syntax_error(10); /* identifier expected */ return; } if (sym = find_symbol(name)) result = sym->type != IMPORT; else result = 0; myfree(name); cond_if(result == b); eol(s); } static void ifused(char *s, int b) { char *name; symbol *sym; int result; if (!(name = parse_identifier(&s))) { syntax_error(10); /* identifier expected */ return; } if (sym = find_symbol(name)) { if (sym->type != IMPORT) { syntax_error(22,name); result = 0; } else result = 1; } else result = 0; myfree(name); cond_if(result == b); eol(s); } static void handle_ifused(char *s) { ifused(s,1); } static void handle_ifnused(char *s) { ifused(s,0); } static void handle_ifd(char *s) { ifdef(s,1); } static void handle_ifnd(char *s) { ifdef(s,0); } static char *ifexp(char *s,int c) { expr *condexp = parse_expr_tmplab(&s); taddr val; int b; if (eval_expr(condexp,&val,NULL,0)) { switch (c) { case 0: b = val == 0; break; case 1: b = val != 0; break; case 2: b = val > 0; break; case 3: b = val >= 0; break; case 4: b = val < 0; break; case 5: b = val <= 0; break; default: ierror(0); break; } } else { general_error(30); /* expression must be constant */ b = 0; } cond_if(b); free_expr(condexp); return s; } static void handle_ifeq(char *s) { eol(ifexp(s,0)); } static void handle_ifne(char *s) { eol(ifexp(s,1)); } static void handle_ifgt(char *s) { eol(ifexp(s,2)); } static void handle_ifge(char *s) { eol(ifexp(s,3)); } static void handle_iflt(char *s) { eol(ifexp(s,4)); } static void handle_ifle(char *s) { eol(ifexp(s,5)); } static void handle_else(char *s) { eol(s); cond_skipelse(); } static void handle_endif(char *s) { eol(s); cond_endif(); } static void handle_assert(char *s) { char *expstr,*msgstr; size_t explen; expr *aexp; atom *a; expstr = skip(s); aexp = parse_expr(&s); explen = s - expstr; s = skip(s); if (*s == ',') { s = skip(s+1); msgstr = parse_name(&s); } else msgstr = NULL; a = new_assert_atom(aexp,cnvstr(expstr,explen),msgstr); add_atom(0,a); } static void handle_incdir(char *s) { char *name; if (name = parse_name(&s)) new_include_path(name); eol(s); } static void handle_include(char *s) { char *name; if (name = parse_name(&s)) { eol(s); include_source(name); } } static void handle_incbin(char *s) { char *name; long delta = 0; unsigned long nbbytes = 0; if (name = parse_name(&s)) { s = skip(s); if (*s == ',') { s = skip(s+1); delta = parse_constexpr(&s); if (delta < 0) delta = 0; s = skip(s); if (*s == ',') { s = skip(s+1); nbbytes = parse_constexpr(&s); } } eol(s); include_binary_file(name,delta,nbbytes); } } static void handle_rept(char *s) { utaddr cnt = parse_constexpr(&s); eol(s); new_repeat((int)cnt,NULL,NULL, dotdirs?drept_dirlist:rept_dirlist, dotdirs?dendr_dirlist:endr_dirlist); } static void handle_endr(char *s) { syntax_error(12,&endrname[1],&repeatname[1]); /* unexpected endr without rept */ } static void handle_macro(char *s) { char *name; if (name = parse_identifier(&s)) { s = skip(s); if (*s == ',') { /* named macro arguments are given? */ s++; } else { eol(s); s = NULL; } new_macro(name,dotdirs?dendm_dirlist:endm_dirlist,s); myfree(name); } else syntax_error(10); /* identifier expected */ } static void handle_endm(char *s) { syntax_error(12,&endmname[1],¯oname[1]); /* unexpected endm without macro */ } static void handle_defc(char *s) { char *name; s = skip(s); name = parse_identifier(&s); if ( name != NULL ) { s = skip(s); if ( *s == '=' ) { s = skip(s+1); new_abs(name,parse_expr_tmplab(&s)); } myfree(name); } else syntax_error(10); } static void handle_list(char *s) { set_listing(1); } static void handle_nolist(char *s) { set_listing(0); } static void handle_listttl(char *s) { /* set listing file title */ } static void handle_listsubttl(char *s) { /* set listing file sub-title */ } static void handle_listpage(char *s) { /* new listing page */ } static void handle_listspace(char *s) { /* insert listing space */ } static void handle_struct(char *s) { char *name; if (name = parse_identifier(&s)) { s = skip(s); eol(s); if (new_structure(name)) current_section->flags |= LABELS_ARE_LOCAL; myfree(name); } else syntax_error(10); /* identifier expected */ } static void handle_endstruct(char *s) { section *prevsec; symbol *szlabel; if (end_structure(&prevsec)) { /* create the structure name as label defining the structure size */ current_section->flags &= ~LABELS_ARE_LOCAL; szlabel = new_labsym(0,current_section->name); add_atom(0,new_label_atom(szlabel)); /* end structure declaration by switching to previous section */ set_section(prevsec); } eol(s); } struct { char *name; void (*func)(char *); } directives[] = { "org",handle_org, "rorg",handle_rorg, "rend",handle_rend, "phase",handle_rorg, "dephase",handle_rend, "roffs",handle_roffs, "align",handle_align, "even",handle_even, "byte",handle_d8, "db",handle_d8, "dfb",handle_d8, "defb",handle_d8, "asc",handle_d8, "data",handle_d8, "defm",handle_text, "text",handle_text, "wor",handle_d16, "word",handle_d16, "addr",handle_d16, "dw",handle_d16, "dfw",handle_d16, "defw",handle_d16, "dd",handle_d32, #if defined(VASM_CPU_650X) || defined(VASM_CPU_Z80) || defined(VASM_CPU_6800) "abyte",handle_d8_offset, #endif "ds",handle_spc8, "dsb",handle_spc8, "fill",handle_spc8, "reserve",handle_spc8, "spc",handle_spc8, "dsw",handle_spc16, "blk",handle_spc8, "blkw",handle_spc16, "dc",handle_spc8, "byt",handle_fixedspc1, "wrd",handle_fixedspc2, "assert",handle_assert, #if defined(VASM_CPU_TR3200) /* Clash with IFxx instructions of TR3200 cpu */ "if_def",handle_ifd, "if_ndef",handle_ifnd, "if_eq",handle_ifeq, "if_ne",handle_ifne, "if_gt",handle_ifgt, "if_ge",handle_ifge, "if_lt",handle_iflt, "if_le",handle_ifle, "if_used",handle_ifused, "if_nused",handle_ifnused, #else "ifdef",handle_ifd, "ifndef",handle_ifnd, "ifd",handle_ifd, "ifnd",handle_ifnd, "ifeq",handle_ifeq, "ifne",handle_ifne, "ifgt",handle_ifgt, "ifge",handle_ifge, "iflt",handle_iflt, "ifle",handle_ifle, "ifused",handle_ifused, "ifnused",handle_ifnused, #endif "if",handle_ifne, "else",handle_else, "el",handle_else, "endif",handle_endif, #if !defined(VASM_CPU_Z80) && !defined(VASM_CPU_6800) "ei",handle_endif, /* Clashes with z80 opcode */ #endif "incbin",handle_incbin, "mdat",handle_incbin, "incdir",handle_incdir, "include",handle_include, "rept",handle_rept, "repeat",handle_rept, "endr",handle_endr, "endrep",handle_endr, "endrepeat",handle_endr, "mac",handle_macro, "macro",handle_macro, "endm",handle_endm, "endmac",handle_endm, "endmacro",handle_endm, "end",handle_end, "fail",handle_fail, "section",handle_section, "binary",handle_incbin, "defs",handle_spc8, "defp",handle_d24, "defl",handle_d32, "defc",handle_defc, "xdef",handle_global, "xref",handle_global, "lib",handle_global, "xlib",handle_global, "global",handle_global, "extern",handle_global, "local",handle_local, "weak",handle_weak, "ascii",handle_string, "asciiz",handle_string, "string",handle_string, "list",handle_list, "nolist",handle_nolist, "struct",handle_struct, "structure",handle_struct, "endstruct",handle_endstruct, "endstructure",handle_endstruct, #if !defined(VASM_CPU_650X) "rmb",handle_spc8, #endif "fcc",handle_text, "fcb",handle_d8, "fdb",handle_d16, "bsz",handle_spc8, "zmb",handle_spc8, "nam",handle_listttl, "subttl",handle_listsubttl, "page",handle_listpage, "space",handle_listspace }; int dir_cnt = sizeof(directives) / sizeof(directives[0]); /* checks for a valid directive, and return index when found, -1 otherwise */ static int check_directive(char **line) { char *s,*name; hashdata data; s = skip(*line); if (!ISIDSTART(*s)) return -1; name = s++; while (ISIDCHAR(*s)) s++; if (*name=='.' && dotdirs) name++; if (!find_namelen_nc(dirhash,name,s-name,&data)) return -1; *line = s; return data.idx; } /* Handles assembly directives; returns non-zero if the line was a directive. */ static int handle_directive(char *line) { int idx = check_directive(&line); if (idx >= 0) { directives[idx].func(skip(line)); return 1; } return 0; } static int oplen(char *e,char *s) { while(s!=e&&isspace((unsigned char)e[-1])) e--; return e-s; } /* When a structure with this name exists, insert its atoms and either initialize with new values or accept its default values. */ static int execute_struct(char *name,int name_len,char *s) { section *str; atom *p; str = find_structure(name,name_len); if (str == NULL) return 0; for (p=str->first; p; p=p->next) { atom *new; char *opp; int opl; if (p->type==DATA || p->type==SPACE || p->type==DATADEF) { opp = s = skip(s); s = skip_oper(0,s); opl = oplen(s,opp); if (opl > 0) { /* initialize this atom with a new expression */ if (p->type == DATADEF) { /* parse a new data operand of the declared bitsize */ operand *op; op = new_operand(); if (parse_operand(opp,opl,op, DATA_OPERAND(p->content.defb->bitsize))) { new = new_datadef_atom(p->content.defb->bitsize,op); new->align = p->align; add_atom(0,new); } else syntax_error(8); /* invalid data operand */ } else if (p->type == SPACE) { /* parse the fill expression for this space */ new = clone_atom(p); new->content.sb = new_sblock(p->content.sb->space_exp, p->content.sb->size, parse_expr_tmplab(&opp)); new->content.sb->space = p->content.sb->space; add_atom(0,new); } else { /* parse constant data - probably a string, or a single constant */ dblock *db; db = new_dblock(); db->size = p->content.db->size; db->data = db->size ? mycalloc(db->size) : NULL; if (db->data) { if (*opp=='\"' || *opp=='\'') { dblock *strdb; strdb = parse_string(&opp,*opp,8); if (strdb->size) { if (strdb->size > db->size) syntax_error(24,strdb->size-db->size); /* cut last chars */ memcpy(db->data,strdb->data, strdb->size > db->size ? db->size : strdb->size); myfree(strdb->data); } myfree(strdb); } else { taddr val = parse_constexpr(&opp); void *p; if (db->size > sizeof(taddr) && BIGENDIAN) p = db->data + db->size - sizeof(taddr); else p = db->data; setval(BIGENDIAN,p,sizeof(taddr),val); } } add_atom(0,new_data_atom(db,p->align)); } } else { /* empty: use default values from original atom */ add_atom(0,clone_atom(p)); } s = skip(s); if (*s == ',') s++; } else if (p->type == INSTRUCTION) syntax_error(23); /* skipping instruction in struct init */ /* other atoms are silently ignored */ } eol(s); return 1; } static char *parse_label_or_pc(char **start) { char *s,*name; name = parse_labeldef(start,0); s = skip(*start); if (name==NULL && *s==current_pc_char && !ISIDCHAR(*(s+1))) { name = cnvstr(s,1); s = skip(s+1); } if (name) *start = s; return name; } #ifdef STATEMENT_DELIMITER static char *read_next_statement(void) { static char *s = NULL; char *line,c; if (s == NULL) { char *lab; s = line = read_next_line(); if (s == NULL) return NULL; /* no more lines in source */ /* skip label field and possible statement delimiters therein */ if (lab = parse_label_or_pc(&s)) myfree(lab); } else { /* make the new statement start with a blank - there is no label field */ *s = ' '; line = s++; } /* find next statement delimiter in line buffer */ for (;;) { #ifdef VASM_CPU_Z80 unsigned char lastuc; #endif c = *s; #ifdef VASM_CPU_Z80 /* For the Z80 ignore ' behind a letter, as it may be a register */ lastuc = toupper((unsigned char)*(s-1)); if ((c=='\'' && (lastuc<'A' || lastuc>'Z')) || c=='\"') { #else if (c=='\'' || c=='\"') { #endif s = skip_string(s,c,NULL); } else if (c == STATEMENT_DELIMITER) { *s = '\0'; /* terminate the statement here temporarily */ break; } else if (c=='\0' || c==commentchar) { s = NULL; /* ignore delimiters in rest of line */ break; } else s++; } return line; } #endif void parse(void) { char *s,*line,*inst,*labname; char *ext[MAX_QUALIFIERS?MAX_QUALIFIERS:1]; char *op[MAX_OPERANDS]; int ext_len[MAX_QUALIFIERS?MAX_QUALIFIERS:1]; int op_len[MAX_OPERANDS]; int ext_cnt,op_cnt,inst_len; instruction *ip; #ifdef STATEMENT_DELIMITER while (line = read_next_statement()) { #else while (line = read_next_line()) { #endif if (parse_end) continue; if (!cond_state()) { /* skip source until ELSE or ENDIF */ int idx; s = line; if (labname = parse_label_or_pc(&s)) myfree(labname); idx = check_directive(&s); if (idx >= 0) { if (!strncmp(directives[idx].name,"if",2)) cond_skipif(); else if (directives[idx].func == handle_else) cond_else(); else if (directives[idx].func == handle_endif) cond_endif(); } continue; } s = line; if (labname = parse_label_or_pc(&s)) { /* we have found a global or local label, or current-pc character */ symbol *label,*labsym; int equlen = 0; if (!strnicmp(s,equname+!dotdirs,3+dotdirs) && isspace((unsigned char)*(s+3+dotdirs))) equlen = 3+dotdirs; else if (!strnicmp(s,eqname+!dotdirs,2+dotdirs) && isspace((unsigned char)*(s+2+dotdirs))) equlen = 2+dotdirs; else if (*s == '=') equlen = 1; if (equlen) { /* found a kind of equate directive */ if (*labname == current_pc_char) { handle_org(skip(s+equlen)); continue; } else { s = skip(s+equlen); label = new_equate(labname,parse_expr_tmplab(&s)); } } else if (!strnicmp(s,setname+!dotdirs,3+dotdirs) && isspace((unsigned char)*(s+3+dotdirs))) { /* SET allows redefinitions */ if (*labname == current_pc_char) { syntax_error(10); /* identifier expected */ } else { s = skip(s+3+dotdirs); label = new_abs(labname,parse_expr_tmplab(&s)); } } else if (!strnicmp(s,macname+!dotdirs,3+dotdirs) && (isspace((unsigned char)*(s+3+dotdirs)) || *(s+3+dotdirs)=='\0') || !strnicmp(s,macroname+!dotdirs,5+dotdirs) && (isspace((unsigned char)*(s+5+dotdirs)) || *(s+5+dotdirs)=='\0')) { char *params = skip(s + (*(s+3+dotdirs)=='r'?5+dotdirs:3+dotdirs)); s = line; myfree(labname); if (!(labname = parse_identifier(&s))) ierror(0); new_macro(labname,dotdirs?dendm_dirlist:endm_dirlist,params); myfree(labname); continue; } else { /* it's just a label */ label = new_labsym(0,labname); add_atom(0,new_label_atom(label)); } if (!is_local_label(labname) && autoexport) label->flags |= EXPORT; myfree(labname); } /* check for directives first */ s = skip(s); if (*s==commentchar) continue; s = parse_cpu_special(s); if (ISEOL(s)) continue; if (*s==current_pc_char && *(s+1)=='=') { /* "*=" org directive */ handle_org(skip(s+2)); continue; } if (handle_directive(s)) continue; s = skip(s); if (ISEOL(s)) continue; /* read mnemonic name */ inst = s; ext_cnt = 0; if (!ISIDSTART(*s)) { syntax_error(10); /* identifier expected */ continue; } #if MAX_QUALIFIERS==0 while (*s && !isspace((unsigned char)*s)) s++; inst_len = s - inst; #else s = parse_instruction(s,&inst_len,ext,ext_len,&ext_cnt); #endif if (!isspace((unsigned char)*s) && *s!='\0') syntax_error(2); /* no space before operands */ s = skip(s); if (execute_macro(inst,inst_len,ext,ext_len,ext_cnt,s)) continue; if (execute_struct(inst,inst_len,s)) continue; /* read operands, terminated by comma or blank (unless in parentheses) */ op_cnt = 0; while (!ISEOL(s) && op_cnt0 if (ip) { int i; for (i=0; iqualifiers[i] = cnvstr(ext[i],ext_len[i]); for(; iqualifiers[i] = NULL; } #endif if (ip) add_atom(0,new_inst_atom(ip)); } cond_check(); } /* parse next macro argument */ char *parse_macro_arg(struct macro *m,char *s, struct namelen *param,struct namelen *arg) { arg->len = 0; /* cannot select specific named arguments */ param->name = s; if (*s=='\"' || *s=='\'') { s = skip_string(s,*s,NULL); param->len = s - param->name; } else { s = skip_operand(s); param->len = s - param->name; } return s; } /* expands arguments and special escape codes into macro context */ int expand_macro(source *src,char **line,char *d,int dlen) { int nc = 0; int n; char *s = *line; char *end; if (*s++ == '\\') { /* possible macro expansion detected */ if (*s == '\\') { if (dlen >= 1) { *d++ = *s++; if (esc_sequences) { if (dlen >= 2) { *d++ = '\\'; /* make it a double \ again */ nc = 2; } else nc = -1; } else nc = 1; } else nc = -1; } else if (*s == '@') { /* \@: insert a unique id */ nc = snprintf(d,dlen,"_%06lu",src->id); s++; if (nc >= dlen) nc = -1; } else if (*s=='(' && *(s+1)==')') { /* \() is just skipped, useful to terminate named macro parameters */ nc = 0; s += 2; } else if (*s == '<') { /* \ : insert absolute unsigned symbol value */ char *name; symbol *sym; taddr val; s++; if (name = parse_symbol(&s)) { if ((sym = find_symbol(name)) && sym->type==EXPRESSION) { if (eval_expr(sym->expr,&val,NULL,0)) nc = sprintf(d,"%lu",(unsigned long)(uint32_t)val); } myfree(name); if (*s++!='>' || nc<0) { syntax_error(11); /* invalid numeric expansion */ return 0; } } else { syntax_error(10); /* identifier expected */ return 0; } } else if (isdigit((unsigned char)*s)) { /* \1..\9,\0 : insert macro parameter 1..9,10 */ nc = copy_macro_param(src,*s=='0'?0:*s-'1',d,dlen); s++; } else if ((end = skip_identifier(s)) != NULL) { if ((n = find_macarg_name(src,s,end-s)) >= 0) { /* \argname: insert named macro parameter n */ nc = copy_macro_param(src,n,d,dlen); s = end; } } if (nc >= 0) *line = s; /* update line pointer when expansion took place */ } return nc; /* number of chars written to line buffer, -1: out of space */ } static int intel_suffix(char *s) /* check for constants with h, d, o, q or b suffix */ { int base,lastbase; char c; base = 2; while (isxdigit((unsigned char)*s)) { lastbase = base; switch (base) { case 2: if (*s <= '1') break; base = 8; case 8: if (*s <= '7') break; base = 10; case 10: if (*s <= '9') break; base = 16; } s++; } c = tolower((unsigned char)*s); if (c == 'h') return 16; if ((c=='o' || c=='q') && base<=8) return 8; c = tolower((unsigned char)*(s-1)); if (c=='d' && lastbase<=10) return 10; if (c=='b' && lastbase<=2) return 2; return 0; } char *const_prefix(char *s,int *base) { if (isdigit((unsigned char)*s)) { if (!nointelsuffix && (*base = intel_suffix(s))) return s; if (!nocprefix) { if (*s == '0') { if (s[1]=='x' || s[1]=='X'){ *base = 16; return s+2; } if (s[1]=='b' || s[1]=='B'){ *base = 2; return s+2; } *base = 8; return s; } else if (s[1]=='#' && *s>='2' && *s<='9') { *base = *s & 0xf; return s+2; } } *base = 10; return s; } if (*s=='$' && isxdigit((unsigned char)s[1])) { *base = 16; return s+1; } #if defined(VASM_CPU_Z80) if ((*s=='&' || *s=='#') && isxdigit((unsigned char)s[1])) { *base = 16; return s+1; } #endif if (*s=='@') { #if defined(VASM_CPU_Z80) *base = 2; #else *base = 8; #endif return s+1; } if (*s == '%') { *base = 2; return s+1; } *base = 0; return s; } char *const_suffix(char *start,char *end) { if (intel_suffix(start)) return end+1; return end; } static char *skip_local(char *p) { char *s; if (ISIDSTART(*p) || isdigit((unsigned char)*p)) { /* may start with digit */ s = p++; while (ISIDCHAR(*p)) p++; } else p = NULL; return p; } char *get_local_label(char **start) /* Local labels start with a '.' or end with '$': "1234$", ".1" */ { char *s,*p,*name; name = NULL; s = *start; p = skip_local(s); if (p!=NULL && *p=='.' && ISIDCHAR(*(p+1)) && ISIDSTART(*s) && *s!='.' && *(p-1)!='$') { /* skip local part of global.local label */ s = p + 1; p = skip_local(p); name = make_local_label(*start,(s-1)-*start,s,p-s); *start = skip(p); } else if (p!=NULL && p>(s+1) && *s=='.') { /* .label */ s++; name = make_local_label(NULL,0,s,p-s); *start = skip(p); } else if (p!=NULL && p>s && *p=='$') { /* label$ */ p++; name = make_local_label(NULL,0,s,p-s); *start = skip(p); } return name; } int init_syntax() { size_t i; hashdata data; dirhash = new_hashtable(0x200); /* @@@ */ for (i=0; i