1694 lines
33 KiB
C
1694 lines
33 KiB
C
|
/* 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; i<db->size; 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<<a,number_expr(0));
|
||
|
eol(s);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void handle_even(char *s)
|
||
|
{
|
||
|
do_alignment(2,number_expr(0));
|
||
|
eol(s);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void do_space(int size,expr *cnt,expr *fill)
|
||
|
{
|
||
|
add_atom(0,new_space_atom(cnt,size>>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); /* "* = * + <expr>" 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_cnt<MAX_OPERANDS) {
|
||
|
op[op_cnt] = s;
|
||
|
s = skip_oper(1,s);
|
||
|
op_len[op_cnt] = oplen(s,op[op_cnt]);
|
||
|
#if !ALLOW_EMPTY_OPS
|
||
|
if (op_len[op_cnt] <= 0)
|
||
|
syntax_error(5); /* missing operand */
|
||
|
else
|
||
|
#endif
|
||
|
op_cnt++;
|
||
|
|
||
|
if (igntrail) {
|
||
|
if (*s != ',')
|
||
|
break;
|
||
|
s++;
|
||
|
}
|
||
|
else {
|
||
|
s = skip(s);
|
||
|
if (OPERSEP_COMMA) {
|
||
|
if (*s == ',')
|
||
|
s = skip(s+1);
|
||
|
else if (!(OPERSEP_BLANK))
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
eol(s);
|
||
|
|
||
|
ip = new_inst(inst,inst_len,op_cnt,op,op_len);
|
||
|
|
||
|
#if MAX_QUALIFIERS>0
|
||
|
if (ip) {
|
||
|
int i;
|
||
|
|
||
|
for (i=0; i<ext_cnt; i++)
|
||
|
ip->qualifiers[i] = cnvstr(ext[i],ext_len[i]);
|
||
|
for(; i<MAX_QUALIFIERS; i++)
|
||
|
ip->qualifiers[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 == '<') {
|
||
|
/* \<symbol> : 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<dir_cnt; i++) {
|
||
|
data.idx = i;
|
||
|
add_hashentry(dirhash,directives[i].name,data);
|
||
|
}
|
||
|
|
||
|
cond_init();
|
||
|
set_internal_abs(REPTNSYM,-1); /* reserve the REPTN symbol */
|
||
|
current_pc_char = '*';
|
||
|
|
||
|
if (orgmode != ~0)
|
||
|
set_section(new_org(orgmode));
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
int syntax_args(char *p)
|
||
|
{
|
||
|
if (!strcmp(p,"-dotdir")) {
|
||
|
dotdirs = 1;
|
||
|
return 1;
|
||
|
}
|
||
|
else if (!strcmp(p,"-autoexp")) {
|
||
|
autoexport = 1;
|
||
|
return 1;
|
||
|
}
|
||
|
else if (!strncmp(p,"-org=",5)) {
|
||
|
orgmode = atoi(p+5);
|
||
|
return 1;
|
||
|
}
|
||
|
else if (OPERSEP_COMMA && !strcmp(p,"-i")) {
|
||
|
igntrail = 1;
|
||
|
return 1;
|
||
|
}
|
||
|
else if (!strcmp(p,"-noc")) {
|
||
|
nocprefix = 1;
|
||
|
return 1;
|
||
|
}
|
||
|
else if (!strcmp(p,"-noi")) {
|
||
|
nointelsuffix = 1;
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|