/* tc-z80.c -- Assemble code for the Zilog Z80 and ASCII R800
- Copyright 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+ Copyright (C) 2005-2019 Free Software Foundation, Inc.
Contributed by Arnold Metselaar <arnold_m@operamail.com>
This file is part of GAS, the GNU Assembler.
static int ins_used = INS_Z80;
int
-md_parse_option (int c, char* arg ATTRIBUTE_UNUSED)
+md_parse_option (int c, const char* arg ATTRIBUTE_UNUSED)
{
switch (c)
{
-Fup\n\
\ttreat undocumented z80-instructions that do not work on R800 as errors\n\
-r800\t assemble for R800\n\n\
-Default: -z80 -ignore-undocument-instructions -warn-unportable-instructions.\n");
+Default: -z80 -ignore-undocumented-instructions -warn-unportable-instructions.\n");
}
static symbolS * zero;
+struct reg_entry
+{
+ const char* name;
+ int number;
+};
+#define R_STACKABLE (0x80)
+#define R_ARITH (0x40)
+#define R_IX (0x20)
+#define R_IY (0x10)
+#define R_INDEX (R_IX | R_IY)
+
+#define REG_A (7)
+#define REG_B (0)
+#define REG_C (1)
+#define REG_D (2)
+#define REG_E (3)
+#define REG_H (4)
+#define REG_L (5)
+#define REG_F (6 | 8)
+#define REG_I (9)
+#define REG_R (10)
+
+#define REG_AF (3 | R_STACKABLE)
+#define REG_BC (0 | R_STACKABLE | R_ARITH)
+#define REG_DE (1 | R_STACKABLE | R_ARITH)
+#define REG_HL (2 | R_STACKABLE | R_ARITH)
+#define REG_IX (REG_HL | R_IX)
+#define REG_IY (REG_HL | R_IY)
+#define REG_SP (3 | R_ARITH)
+
+static const struct reg_entry regtable[] =
+{
+ {"a", REG_A },
+ {"af", REG_AF },
+ {"b", REG_B },
+ {"bc", REG_BC },
+ {"c", REG_C },
+ {"d", REG_D },
+ {"de", REG_DE },
+ {"e", REG_E },
+ {"f", REG_F },
+ {"h", REG_H },
+ {"hl", REG_HL },
+ {"i", REG_I },
+ {"ix", REG_IX },
+ {"ixh",REG_H | R_IX },
+ {"ixl",REG_L | R_IX },
+ {"iy", REG_IY },
+ {"iyh",REG_H | R_IY },
+ {"iyl",REG_L | R_IY },
+ {"l", REG_L },
+ {"r", REG_R },
+ {"sp", REG_SP },
+} ;
+
+#define BUFLEN 8 /* Large enough for any keyword. */
+
void
md_begin (void)
{
- expressionS nul;
+ expressionS nul, reg;
char * p;
+ unsigned int i, j, k;
+ char buf[BUFLEN];
+ reg.X_op = O_register;
+ reg.X_md = 0;
+ reg.X_add_symbol = reg.X_op_symbol = 0;
+ for ( i = 0 ; i < ARRAY_SIZE ( regtable ) ; ++i )
+ {
+ reg.X_add_number = regtable[i].number;
+ k = strlen ( regtable[i].name );
+ buf[k] = 0;
+ if ( k+1 < BUFLEN )
+ {
+ for ( j = ( 1<<k ) ; j ; --j )
+ {
+ for ( k = 0 ; regtable[i].name[k] ; ++k )
+ {
+ buf[k] = ( j & ( 1<<k ) ) ? TOUPPER ( regtable[i].name[k] ) : regtable[i].name[k];
+ }
+ symbolS * psym = symbol_find_or_make(buf);
+ S_SET_SEGMENT(psym, reg_section);
+ symbol_set_value_expression(psym, ®);
+ }
+ }
+ }
p = input_line_pointer;
- input_line_pointer = "0";
+ input_line_pointer = (char *) "0";
nul.X_md=0;
expression (& nul);
input_line_pointer = p;
*p++ = buf[2];
break;
}
+ /* Fall through. */
case '"':
for (quote = *p++; quote != *p && '\n' != *p; ++p)
/* No escapes. */ ;
/* Check for <label>[:] [.](EQU|DEFL) <value>. */
if (is_name_beginner (*input_line_pointer))
{
+ char *name;
char c, *rest, *line_start;
int len;
if (ignore_input ())
return 0;
- c = get_symbol_end ();
+ c = get_symbol_name (&name);
rest = input_line_pointer + 1;
if (*rest == ':')
}
input_line_pointer = rest + len - 1;
/* Allow redefining with "DEFL" (len == 4), but not with "EQU". */
- equals (line_start, len == 4);
+ equals (name, len == 4);
return 1;
}
else
{
/* Restore line and pointer. */
- *input_line_pointer = c;
+ (void) restore_line_pointer (c);
input_line_pointer = line_start;
}
}
return NULL;
}
-char *
+const char *
md_atof (int type ATTRIBUTE_UNUSED, char *litP ATTRIBUTE_UNUSED,
int *sizeP ATTRIBUTE_UNUSED)
{
typedef struct _table_t
{
- char* name;
- char prefix;
- char opcode;
+ const char* name;
+ unsigned char prefix;
+ unsigned char opcode;
asfunc * fp;
} table_t;
return strcmp (str_a, str_b);
}
-#define BUFLEN 8 /* Large enough for any keyword. */
-
char buf[BUFLEN];
const char *key = buf;
-#define R_STACKABLE (0x80)
-#define R_ARITH (0x40)
-#define R_IX (0x20)
-#define R_IY (0x10)
-#define R_INDEX (R_IX | R_IY)
-
-#define REG_A (7)
-#define REG_B (0)
-#define REG_C (1)
-#define REG_D (2)
-#define REG_E (3)
-#define REG_H (4)
-#define REG_L (5)
-#define REG_F (6 | 8)
-#define REG_I (9)
-#define REG_R (10)
-
-#define REG_AF (3 | R_STACKABLE)
-#define REG_BC (0 | R_STACKABLE | R_ARITH)
-#define REG_DE (1 | R_STACKABLE | R_ARITH)
-#define REG_HL (2 | R_STACKABLE | R_ARITH)
-#define REG_SP (3 | R_ARITH)
-
-static const struct reg_entry
-{
- char* name;
- int number;
-} regtable[] =
-{
- {"a", REG_A },
- {"af", REG_AF },
- {"b", REG_B },
- {"bc", REG_BC },
- {"c", REG_C },
- {"d", REG_D },
- {"de", REG_DE },
- {"e", REG_E },
- {"f", REG_F },
- {"h", REG_H },
- {"hl", REG_HL },
- {"i", REG_I },
- {"ix", REG_HL | R_IX },
- {"ixh",REG_H | R_IX },
- {"ixl",REG_L | R_IX },
- {"iy", REG_HL | R_IY },
- {"iyh",REG_H | R_IY },
- {"iyl",REG_L | R_IY },
- {"l", REG_L },
- {"r", REG_R },
- {"sp", REG_SP },
-} ;
-
/* Prevent an error on a line from also generating
a "junk at end of line" error message. */
static char err_flag;
if (ins_type & ins_err)
error (_(p));
else
- as_warn (_(p));
+ as_warn ("%s", _(p));
}
static void
return indir;
}
-/* Parse general expression. */
+/* Check whether a symbol involves a register. */
+static int
+contains_register(symbolS *sym)
+{
+ if (sym)
+ {
+ expressionS * ex = symbol_get_value_expression(sym);
+ return (O_register == ex->X_op)
+ || (ex->X_add_symbol && contains_register(ex->X_add_symbol))
+ || (ex->X_op_symbol && contains_register(ex->X_op_symbol));
+ }
+ else
+ return 0;
+}
+
+/* Parse general expression, not looking for indexed addressing. */
static const char *
-parse_exp2 (const char *s, expressionS *op, segT *pseg)
+parse_exp_not_indexed (const char *s, expressionS *op)
{
const char *p;
int indir;
- int i;
- const struct reg_entry * regp;
- expressionS offset;
p = skip_space (s);
op->X_md = indir = is_indir (p);
- if (indir)
- p = skip_space (p + 1);
-
- for (i = 0; i < BUFLEN; ++i)
- {
- if (!ISALPHA (p[i])) /* Register names consist of letters only. */
- break;
- buf[i] = TOLOWER (p[i]);
- }
-
- if ((i < BUFLEN) && ((p[i] == 0) || (strchr (")+-, \t", p[i]))))
+ input_line_pointer = (char*) s ;
+ expression (op);
+ switch (op->X_op)
{
- buf[i] = 0;
- regp = bsearch (& key, regtable, ARRAY_SIZE (regtable),
- sizeof (regtable[0]), key_cmp);
- if (regp)
- {
- *pseg = reg_section;
- op->X_add_symbol = op->X_op_symbol = 0;
- op->X_add_number = regp->number;
- op->X_op = O_register;
- p += strlen (regp->name);
- p = skip_space (p);
- if (indir)
- {
- if (*p == ')')
- ++p;
- if ((regp->number & R_INDEX) && (regp->number & R_ARITH))
- {
- op->X_op = O_md1;
-
- if ((*p == '+') || (*p == '-'))
- {
- input_line_pointer = (char*) p;
- expression (& offset);
- p = skip_space (input_line_pointer);
- if (*p != ')')
- error (_("bad offset expression syntax"));
- else
- ++ p;
- op->X_add_symbol = make_expr_symbol (& offset);
- return p;
- }
-
- /* We treat (i[xy]) as (i[xy]+0), which is how it will
- end up anyway, unless we're processing jp (i[xy]). */
- op->X_add_symbol = zero;
- }
- }
- p = skip_space (p);
-
- if ((*p == 0) || (*p == ','))
- return p;
- }
+ case O_absent:
+ error (_("missing operand"));
+ break;
+ case O_illegal:
+ error (_("bad expression syntax"));
+ break;
+ default:
+ break;
}
- /* Not an argument involving a register; use the generic parser. */
- input_line_pointer = (char*) s ;
- *pseg = expression (op);
- if (op->X_op == O_absent)
- error (_("missing operand"));
- if (op->X_op == O_illegal)
- error (_("bad expression syntax"));
return input_line_pointer;
}
+/* Parse expression, change operator to O_md1 for indexed addressing*/
static const char *
parse_exp (const char *s, expressionS *op)
{
- segT dummy;
- return parse_exp2 (s, op, & dummy);
+ const char* res = parse_exp_not_indexed (s, op);
+ switch (op->X_op)
+ {
+ case O_add:
+ case O_subtract:
+ if (op->X_md && (O_register == symbol_get_value_expression(op->X_add_symbol)->X_op))
+ {
+ int rnum = symbol_get_value_expression(op->X_add_symbol)->X_add_number;
+ if ( ((REG_IX != rnum) && (REG_IY != rnum)) || contains_register(op->X_op_symbol) )
+ {
+ ill_op();
+ }
+ else
+ {
+ if (O_subtract == op->X_op)
+ {
+ expressionS minus;
+ minus.X_op = O_uminus;
+ minus.X_add_number = 0;
+ minus.X_add_symbol = op->X_op_symbol;
+ minus.X_op_symbol = 0;
+ op->X_op_symbol = make_expr_symbol(&minus);
+ op->X_op = O_add;
+ }
+ symbol_get_value_expression(op->X_op_symbol)->X_add_number += op->X_add_number;
+ op->X_add_number = rnum;
+ op->X_add_symbol = op->X_op_symbol;
+ op->X_op_symbol = 0;
+ op->X_op = O_md1;
+ }
+ }
+ break;
+ case O_register:
+ if ( op->X_md && ((REG_IX == op->X_add_number)||(REG_IY == op->X_add_number)) )
+ {
+ op->X_add_symbol = zero;
+ op->X_op = O_md1;
+ }
+ break;
+ default:
+ break;
+ }
+ return res;
}
/* Condition codes, including some synonyms provided by HiTech zas. */
BFD_RELOC_32
};
- if (nbytes < 1 || nbytes > 4)
+ if (nbytes < 1 || nbytes > 4)
{
as_bad (_("unsupported BFD relocation size %u"), nbytes);
}
{
char *p;
int lo, hi;
- fixS * fixp;
p = frag_more (1);
*p = val->X_add_number;
- if ((r_type == BFD_RELOC_8_PCREL) && (val->X_op == O_constant))
+ if ( contains_register(val->X_add_symbol) || contains_register(val->X_op_symbol) )
+ {
+ ill_op();
+ }
+ else if ((r_type == BFD_RELOC_8_PCREL) && (val->X_op == O_constant))
{
as_bad (_("cannot make a relative jump to an absolute location"));
}
}
else
{
- fixp = fix_new_exp (frag_now, p - frag_now->fr_literal, 1, val,
- (r_type == BFD_RELOC_8_PCREL) ? TRUE : FALSE, r_type);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, 1, val,
+ (r_type == BFD_RELOC_8_PCREL) ? TRUE : FALSE, r_type);
/* FIXME : Process constant offsets immediately. */
}
}
p = frag_more (2);
if ( (val->X_op == O_register)
- || (val->X_op == O_md1))
+ || (val->X_op == O_md1)
+ || contains_register(val->X_add_symbol)
+ || contains_register(val->X_op_symbol) )
ill_op ();
else
{
/* The operand m may be as above or one of the undocumented
combinations (ix+d),r and (iy+d),r (if unportable instructions
are allowed). */
+
static const char *
-emit_mr (char prefix, char opcode, const char *args)
+emit_mr (char prefix, char opcode, const char *args, bfd_boolean unportable)
{
expressionS arg_m, arg_r;
const char *p;
if ((arg_r.X_md == 0)
&& (arg_r.X_op == O_register)
&& (arg_r.X_add_number < 8))
- opcode += arg_r.X_add_number-6; /* Emit_mx () will add 6. */
+ opcode += arg_r.X_add_number - 6; /* Emit_mx () will add 6. */
else
{
ill_op ();
break;
}
check_mach (INS_UNPORT);
+ unportable = TRUE;
}
+ /* Fall through. */
case O_register:
+ if (unportable)
+ check_mach (INS_UNPORT);
emit_mx (prefix, opcode, 0, & arg_m);
break;
default:
return p;
}
+static const char *
+emit_mr_z80 (char prefix, char opcode, const char *args)
+{
+ return emit_mr (prefix, opcode, args, FALSE);
+}
+
+static const char *
+emit_mr_unport (char prefix, char opcode, const char *args)
+{
+ return emit_mr (prefix, opcode, args, TRUE);
+}
+
static void
emit_sx (char prefix, char opcode, expressionS * arg_p)
{
expressionS addr;
const char *p; char *q;
- p = parse_exp (args, &addr);
+ p = parse_exp_not_indexed (args, &addr);
if (addr.X_md)
ill_op ();
else
const char *p;
char *q;
- p = parse_exp (args, &addr);
+ p = parse_exp_not_indexed (args, &addr);
if (addr.X_md)
ill_op ();
else
char *q;
int rnum;
- p = parse_exp (args, & addr);
+ p = parse_exp_not_indexed (args, & addr);
if (addr.X_md)
{
rnum = addr.X_add_number;
- if ((addr.X_op == O_register && (rnum & ~R_INDEX) == REG_HL)
- /* An operand (i[xy]) would have been rewritten to (i[xy]+0)
- in parse_exp (). */
- || (addr.X_op == O_md1 && addr.X_add_symbol == zero))
+ if ((O_register == addr.X_op) && (REG_HL == (rnum & ~R_INDEX)))
{
q = frag_more ((rnum & R_INDEX) ? 2 : 1);
if (rnum & R_INDEX)
p = parse_exp (args, &term);
if (*p++ != ',')
{
- error (_("bad intruction syntax"));
+ error (_("bad instruction syntax"));
return p;
}
p = parse_exp (args, &term);
if (*p++ != ',')
{
- error (_("bad intruction syntax"));
+ error (_("bad instruction syntax"));
return p;
}
p = parse_exp (args, &b);
if (*p++ != ',')
- error (_("bad intruction syntax"));
+ error (_("bad instruction syntax"));
bn = b.X_add_number;
if ((!b.X_md)
p = emit_m (prefix, opcode + (bn << 3), p);
else
/* Set, res : resulting byte can be copied to register. */
- p = emit_mr (prefix, opcode + (bn << 3), p);
+ p = emit_mr (prefix, opcode + (bn << 3), p, FALSE);
}
else
ill_op ();
const char * p;
char prefix, opcode;
- p = parse_exp (args, &op);
+ p = parse_exp_not_indexed (args, &op);
p = skip_space (p);
if (*p++ != ',')
{
p = parse_exp (args, ®);
if (*p++ != ',')
{
- error (_("bad intruction syntax"));
+ error (_("bad instruction syntax"));
return p;
}
p = parse_exp (args, & port);
if (*p++ != ',')
{
- error (_("bad intruction syntax"));
+ error (_("bad instruction syntax"));
return p;
}
p = parse_exp (p, ®);
const char *p;
char *q;
- p = parse_exp (args, &addr);
+ p = parse_exp_not_indexed (args, &addr);
if (addr.X_op != O_constant)
{
error ("rst needs constant address");
p = parse_exp (args, &dst);
if (*p++ != ',')
- error (_("bad intruction syntax"));
+ error (_("bad instruction syntax"));
p = parse_exp (p, &src);
switch (dst.X_op)
*q = opcode + ((reg - 'b') << 3);
break;
}
+ /* Fall through. */
default:
ill_op ();
}
{ "d32", cons, 4},
{ "def24", cons, 3},
{ "def32", cons, 4},
- { "defb", emit_data, 1},
+ { "defb", emit_data, 1},
{ "defs", s_space, 1}, /* Synonym for ds on some assemblers. */
{ "defw", cons, 2},
{ "ds", s_space, 1}, /* Fill with bytes rather than words. */
{ "ret", 0xC9, 0xC0, emit_retcc },
{ "reti", 0xED, 0x4D, emit_insn },
{ "retn", 0xED, 0x45, emit_insn },
- { "rl", 0xCB, 0x10, emit_mr },
+ { "rl", 0xCB, 0x10, emit_mr_z80 },
{ "rla", 0x00, 0x17, emit_insn },
- { "rlc", 0xCB, 0x00, emit_mr },
+ { "rlc", 0xCB, 0x00, emit_mr_z80 },
{ "rlca", 0x00, 0x07, emit_insn },
{ "rld", 0xED, 0x6F, emit_insn },
- { "rr", 0xCB, 0x18, emit_mr },
+ { "rr", 0xCB, 0x18, emit_mr_z80 },
{ "rra", 0x00, 0x1F, emit_insn },
- { "rrc", 0xCB, 0x08, emit_mr },
+ { "rrc", 0xCB, 0x08, emit_mr_z80 },
{ "rrca", 0x00, 0x0F, emit_insn },
{ "rrd", 0xED, 0x67, emit_insn },
{ "rst", 0x00, 0xC7, emit_rst},
{ "sbc", 0x98, 0x42, emit_adc },
{ "scf", 0x00, 0x37, emit_insn },
{ "set", 0xCB, 0xC0, emit_bit },
- { "sla", 0xCB, 0x20, emit_mr },
- { "sli", 0xCB, 0x30, emit_mr },
- { "sll", 0xCB, 0x30, emit_mr },
- { "sra", 0xCB, 0x28, emit_mr },
- { "srl", 0xCB, 0x38, emit_mr },
+ { "sla", 0xCB, 0x20, emit_mr_z80 },
+ { "sli", 0xCB, 0x30, emit_mr_unport },
+ { "sll", 0xCB, 0x30, emit_mr_unport },
+ { "sra", 0xCB, 0x28, emit_mr_z80 },
+ { "srl", 0xCB, 0x38, emit_mr_z80 },
{ "sub", 0x00, 0x90, emit_s },
{ "xor", 0x00, 0xA8, emit_s },
} ;
void
-md_assemble (char* str)
+md_assemble (char *str)
{
const char *p;
char * old_ptr;
}
else if ((*p) && (!ISSPACE (*p)))
as_bad (_("syntax error"));
- else
+ else
{
buf[i] = 0;
p = skip_space (p);
key = buf;
-
+
insp = bsearch (&key, instab, ARRAY_SIZE (instab),
sizeof (instab[0]), key_cmp);
if (!insp)
md_apply_fix (fixS * fixP, valueT* valP, segT seg ATTRIBUTE_UNUSED)
{
long val = * (long *) valP;
- char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+ char *p_lit = fixP->fx_where + fixP->fx_frag->fr_literal;
switch (fixP->fx_r_type)
{
if (!fixP->fx_no_overflow)
as_bad_where (fixP->fx_file, fixP->fx_line,
_("relative jump out of range"));
- *buf++ = val;
+ *p_lit++ = val;
fixP->fx_done = 1;
}
break;
fixP->fx_no_overflow = (-128 <= val && val < 128);
if (!fixP->fx_no_overflow)
as_bad_where (fixP->fx_file, fixP->fx_line,
- _("index offset out of range"));
- *buf++ = val;
+ _("index offset out of range"));
+ *p_lit++ = val;
fixP->fx_done = 1;
}
break;
case BFD_RELOC_8:
if (val > 255 || val < -128)
as_warn_where (fixP->fx_file, fixP->fx_line, _("overflow"));
- *buf++ = val;
- fixP->fx_no_overflow = 1;
+ *p_lit++ = val;
+ fixP->fx_no_overflow = 1;
if (fixP->fx_addsy == NULL)
fixP->fx_done = 1;
break;
case BFD_RELOC_16:
- *buf++ = val;
- *buf++ = (val >> 8);
- fixP->fx_no_overflow = 1;
+ *p_lit++ = val;
+ *p_lit++ = (val >> 8);
+ fixP->fx_no_overflow = 1;
if (fixP->fx_addsy == NULL)
fixP->fx_done = 1;
break;
case BFD_RELOC_24: /* Def24 may produce this. */
- *buf++ = val;
- *buf++ = (val >> 8);
- *buf++ = (val >> 16);
- fixP->fx_no_overflow = 1;
+ *p_lit++ = val;
+ *p_lit++ = (val >> 8);
+ *p_lit++ = (val >> 16);
+ fixP->fx_no_overflow = 1;
if (fixP->fx_addsy == NULL)
fixP->fx_done = 1;
break;
case BFD_RELOC_32: /* Def32 and .long may produce this. */
- *buf++ = val;
- *buf++ = (val >> 8);
- *buf++ = (val >> 16);
- *buf++ = (val >> 24);
+ *p_lit++ = val;
+ *p_lit++ = (val >> 8);
+ *p_lit++ = (val >> 16);
+ *p_lit++ = (val >> 24);
if (fixP->fx_addsy == NULL)
fixP->fx_done = 1;
break;
return NULL;
}
- reloc = xmalloc (sizeof (arelent));
- reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ reloc = XNEW (arelent);
+ reloc->sym_ptr_ptr = XNEW (asymbol *);
*reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);