enum options
{
- OPTION_MACH_Z80 = OPTION_MD_BASE,
+ OPTION_MARCH = OPTION_MD_BASE,
+ OPTION_MACH_Z80,
OPTION_MACH_R800,
OPTION_MACH_Z180,
OPTION_MACH_EZ80_Z80,
OPTION_MACH_EZ80_ADL,
- OPTION_MACH_GBZ80,
- OPTION_MACH_Z80N,
OPTION_MACH_INST,
OPTION_MACH_NO_INST,
OPTION_MACH_IUD,
struct option md_longopts[] =
{
+ { "march", required_argument, NULL, OPTION_MARCH},
{ "z80", no_argument, NULL, OPTION_MACH_Z80},
{ "r800", no_argument, NULL, OPTION_MACH_R800},
{ "z180", no_argument, NULL, OPTION_MACH_Z180},
{ "ez80", no_argument, NULL, OPTION_MACH_EZ80_Z80},
{ "ez80-adl", no_argument, NULL, OPTION_MACH_EZ80_ADL},
- { "gbz80", no_argument, NULL, OPTION_MACH_GBZ80},
- { "z80n", no_argument, NULL, OPTION_MACH_Z80N},
{ "fp-s", required_argument, NULL, OPTION_FP_SINGLE_FORMAT},
{ "fp-d", required_argument, NULL, OPTION_FP_DOUBLE_FORMAT},
{ "strict", no_argument, NULL, OPTION_MACH_FUD},
#define INST_MODE_FORCED 4 /* CPU mode changed by instruction suffix*/
static char inst_mode;
+struct match_info
+{
+ const char *name;
+ int ins_ok;
+ int ins_err;
+ int cpu_mode;
+ const char *comment;
+};
+
+static const struct match_info
+match_cpu_table [] =
+{
+ {"z80", INS_Z80, 0, 0, "Zilog Z80" },
+ {"ez80", INS_EZ80, 0, 0, "Zilog eZ80" },
+ {"gbz80", INS_GBZ80, INS_UNDOC|INS_UNPORT, 0, "GameBoy Z80" },
+ {"r800", INS_R800, INS_UNPORT, 0, "Ascii R800" },
+ {"z180", INS_Z180, INS_UNDOC|INS_UNPORT, 0, "Zilog Z180" },
+ {"z80n", INS_Z80N, 0, 0, "Z80 Next" }
+};
+
+static const struct match_info
+match_ext_table [] =
+{
+ {"full", INS_UNDOC|INS_UNPORT, 0, 0, "assemble all known instructions" },
+ {"adl", 0, 0, 1, "eZ80 ADL mode by default" },
+ {"xyhl", INS_IDX_HALF, 0, 0, "instructions with halves of index registers" },
+ {"infc", INS_IN_F_C, 0, 0, "instruction IN F,(C)" },
+ {"outc0", INS_OUT_C_0, 0, 0, "instruction OUT (C),0" },
+ {"sli", INS_SLI, 0, 0, "instruction known as SLI, SLL, or SL1" },
+ {"xdcb", INS_ROT_II_LD, 0, 0, "instructions like RL (IX+d),R (DD/FD CB dd oo)" }
+};
+
+static void
+setup_march (const char *name, int *ok, int *err, int *mode)
+{
+ unsigned i;
+ size_t len = strcspn (name, "+-");
+ for (i = 0; i < ARRAY_SIZE (match_cpu_table); ++i)
+ if (!strncasecmp (name, match_cpu_table[i].name, len)
+ && strlen (match_cpu_table[i].name) == len)
+ {
+ *ok = match_cpu_table[i].ins_ok;
+ *err = match_cpu_table[i].ins_err;
+ *mode = match_cpu_table[i].cpu_mode;
+ break;
+ }
+
+ if (i >= ARRAY_SIZE (match_cpu_table))
+ as_fatal (_("Invalid CPU is specified: %s"), name);
+
+ while (name[len])
+ {
+ name = &name[len + 1];
+ len = strcspn (name, "+-");
+ for (i = 0; i < ARRAY_SIZE (match_ext_table); ++i)
+ if (!strncasecmp (name, match_ext_table[i].name, len)
+ && strlen (match_ext_table[i].name) == len)
+ {
+ if (name[-1] == '+')
+ {
+ *ok |= match_ext_table[i].ins_ok;
+ *err &= ~match_ext_table[i].ins_ok;
+ *mode |= match_ext_table[i].cpu_mode;
+ }
+ else
+ {
+ *ok &= ~match_ext_table[i].ins_ok;
+ *err |= match_ext_table[i].ins_ok;
+ *mode &= ~match_ext_table[i].cpu_mode;
+ }
+ break;
+ }
+ if (i >= ARRAY_SIZE (match_ext_table))
+ as_fatal (_("Invalid EXTENTION is specified: %s"), name);
+ }
+}
+
static int
setup_instruction (const char *inst, int *add, int *sub)
{
{
default:
return 0;
+ case OPTION_MARCH:
+ setup_march (arg, & ins_ok, & ins_err, & cpu_mode);
+ break;
case OPTION_MACH_Z80:
- ins_ok = (ins_ok & INS_TUNE_MASK) | INS_Z80;
- ins_err = (ins_err & INS_MARCH_MASK) | (~INS_Z80 & INS_MARCH_MASK);
+ setup_march ("z80", & ins_ok, & ins_err, & cpu_mode);
break;
case OPTION_MACH_R800:
- ins_ok = INS_R800 | INS_IDX_HALF | INS_IN_F_C;
- ins_err = INS_UNPORT;
+ setup_march ("r800", & ins_ok, & ins_err, & cpu_mode);
break;
case OPTION_MACH_Z180:
- ins_ok = INS_Z180;
- ins_err = INS_UNDOC | INS_UNPORT;
+ setup_march ("z180", & ins_ok, & ins_err, & cpu_mode);
break;
case OPTION_MACH_EZ80_Z80:
- ins_ok = INS_EZ80 | INS_IDX_HALF;
- ins_err = (INS_UNDOC | INS_UNPORT) & ~INS_IDX_HALF;
- cpu_mode = 0;
+ setup_march ("ez80", & ins_ok, & ins_err, & cpu_mode);
break;
case OPTION_MACH_EZ80_ADL:
- ins_ok = INS_EZ80 | INS_IDX_HALF;
- ins_err = (INS_UNDOC | INS_UNPORT) & ~INS_IDX_HALF;
- cpu_mode = 1;
- break;
- case OPTION_MACH_GBZ80:
- ins_ok = INS_GBZ80;
- ins_err = INS_UNDOC | INS_UNPORT;
- break;
- case OPTION_MACH_Z80N:
- ins_ok = INS_Z80N | INS_UNPORT | INS_UNDOC;
- ins_err = 0;
+ setup_march ("ez80+adl", & ins_ok, & ins_err, & cpu_mode);
break;
case OPTION_FP_SINGLE_FORMAT:
str_to_float = get_str_to_float (arg);
break;
case OPTION_COMPAT_SDCC:
sdcc_compat = 1;
- local_label_prefix = "_";
break;
case OPTION_COMPAT_COLONLESS:
colonless_labels = 1;
void
md_show_usage (FILE * f)
{
- fprintf (f, "\n\
+ unsigned i;
+ fprintf (f, _("\n\
CPU model options:\n\
- -z80\t\t\t assemble for Zilog Z80\n\
- -r800\t\t\t assemble for Ascii R800\n\
- -z180\t\t\t assemble for Zilog Z180\n\
- -ez80\t\t\t assemble for Zilog eZ80 in Z80 mode by default\n\
- -ez80-adl\t\t assemble for Zilog eZ80 in ADL mode by default\n\
- -gbz80\t\t assemble for GameBoy Z80\n\
- -z80n\t\t\t assemble for Z80N\n\
-\n\
+ -march=CPU[+EXT...][-EXT...]\n\
+\t\t\t generate code for CPU, where CPU is one of:\n"));
+ for (i = 0; i < ARRAY_SIZE(match_cpu_table); ++i)
+ fprintf (f, " %-8s\t\t %s\n", match_cpu_table[i].name, match_cpu_table[i].comment);
+ fprintf (f, _("And EXT is combination (+EXT - add, -EXT - remove) of:\n"));
+ for (i = 0; i < ARRAY_SIZE(match_ext_table); ++i)
+ fprintf (f, " %-8s\t\t %s\n", match_ext_table[i].name, match_ext_table[i].comment);
+ fprintf (f, _("\n\
Compatibility options:\n\
-local-prefix=TEXT\t treat labels prefixed by TEXT as local\n\
-colonless\t\t permit colonless labels\n\
-fp-s=FORMAT\t\t set single precission FP numbers format\n\
-fp-d=FORMAT\t\t set double precission FP numbers format\n\
Where FORMAT one of:\n\
- ieee754\t\t IEEE754 compatible\n\
+ ieee754\t\t IEEE754 compatible (depends on directive)\n\
half\t\t\t IEEE754 half precision (16 bit)\n\
single\t\t IEEE754 single precision (32 bit)\n\
double\t\t IEEE754 double precision (64 bit)\n\
zeda32\t\t Zeda z80float library 32 bit format\n\
math48\t\t 48 bit format from Math48 library\n\
\n\
-Support for known undocumented instructions:\n\
- -strict\t\t assemble only documented instructions\n\
- -full\t\t\t assemble all undocumented instructions\n\
- -with-inst=INST[,...]\n\
- -Wnins INST[,...]\t assemble specified instruction(s)\n\
- -without-inst=INST[,...]\n\
- -Fins INST[,...]\t do not assemble specified instruction(s)\n\
-Where INST is one of:\n\
- idx-reg-halves\t instructions with halves of index registers\n\
- sli\t\t\t instruction SLI/SLL\n\
- op-ii-ld\t\t instructions like SLA (II+dd),R (opcodes DD/FD CB dd xx)\n\
- in-f-c\t\t instruction IN F,(C)\n\
- out-c-0\t\t instruction OUT (C),0\n\
-\n\
-Obsolete options:\n\
- -ignore-undocumented-instructions\n\
- -Wnud\t\t\t silently assemble undocumented Z80-instructions that work on R800\n\
- -ignore-unportable-instructions\n\
- -Wnup\t\t\t silently assemble all undocumented Z80-instructions\n\
- -warn-undocumented-instructions\n\
- -Wud\t\t\t issue warnings for undocumented Z80-instructions that work on R800\n\
- -warn-unportable-instructions\n\
- -Wup\t\t\t issue warnings for other undocumented Z80-instructions\n\
- -forbid-undocumented-instructions\n\
- -Fud\t\t\t treat all undocumented Z80-instructions as errors\n\
- -forbid-unportable-instructions\n\
- -Fup\t\t\t treat undocumented Z80-instructions that do not work on R800 as errors\n\
-\n\
-Default: -z80 -ignore-undocumented-instructions -warn-unportable-instructions.\n");
+Default: -march=z80+xyhl+infc\n"));
}
static symbolS * zero;
{
const char* name;
int number;
+ int isa;
};
#define R_STACKABLE (0x80)
#define R_ARITH (0x40)
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 },
- {"mb", REG_MB },
- {"r", REG_R },
- {"sp", REG_SP },
+ {"a", REG_A, INS_ALL },
+ {"af", REG_AF, INS_ALL },
+ {"b", REG_B, INS_ALL },
+ {"bc", REG_BC, INS_ALL },
+ {"c", REG_C, INS_ALL },
+ {"d", REG_D, INS_ALL },
+ {"de", REG_DE, INS_ALL },
+ {"e", REG_E, INS_ALL },
+ {"f", REG_F, INS_IN_F_C | INS_Z80N | INS_R800 },
+ {"h", REG_H, INS_ALL },
+ {"hl", REG_HL, INS_ALL },
+ {"i", REG_I, INS_NOT_GBZ80 },
+ {"ix", REG_IX, INS_NOT_GBZ80 },
+ {"ixh", REG_H | R_IX, INS_IDX_HALF | INS_EZ80 | INS_R800 | INS_Z80N },
+ {"ixl", REG_L | R_IX, INS_IDX_HALF | INS_EZ80 | INS_R800 | INS_Z80N },
+ {"iy", REG_IY, INS_NOT_GBZ80 },
+ {"iyh", REG_H | R_IY, INS_IDX_HALF | INS_EZ80 | INS_R800 | INS_Z80N },
+ {"iyl", REG_L | R_IY, INS_IDX_HALF | INS_EZ80 | INS_R800 | INS_Z80N },
+ {"l", REG_L, INS_ALL },
+ {"mb", REG_MB, INS_EZ80 },
+ {"r", REG_R, INS_NOT_GBZ80 },
+ {"sp", REG_SP, INS_ALL },
} ;
#define BUFLEN 8 /* Large enough for any keyword. */
reg.X_add_symbol = reg.X_op_symbol = 0;
for ( i = 0 ; i < ARRAY_SIZE ( regtable ) ; ++i )
{
+ if (regtable[i].isa && !(regtable[i].isa & ins_ok))
+ continue;
reg.X_add_number = regtable[i].number;
k = strlen ( regtable[i].name );
buf[k] = 0;
switch (ins_ok & INS_MARCH_MASK)
{
case INS_Z80:
- if (ins_ok & INS_UNPORT)
- mach_type = bfd_mach_z80full;
- else if (ins_ok & INS_UNDOC)
- mach_type = bfd_mach_z80;
- else
- mach_type = bfd_mach_z80strict;
+ mach_type = bfd_mach_z80;
break;
case INS_R800:
mach_type = bfd_mach_r800;
default:
mach_type = 0;
}
-
bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach_type);
}
#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
void
z80_elf_final_processing (void)
-{
+{/* nothing to do, all is done by BFD itself */
+/*
unsigned elf_flags;
- switch (ins_ok & INS_MARCH_MASK)
- {
- case INS_Z80:
- elf_flags = EF_Z80_MACH_Z80;
- break;
- case INS_R800:
- elf_flags = EF_Z80_MACH_R800;
- break;
- case INS_Z180:
- elf_flags = EF_Z80_MACH_Z180;
- break;
- case INS_GBZ80:
- elf_flags = EF_Z80_MACH_GBZ80;
- break;
- case INS_EZ80:
- elf_flags = cpu_mode ? EF_Z80_MACH_EZ80_ADL : EF_Z80_MACH_EZ80_Z80;
- break;
- case INS_Z80N:
- elf_flags = EF_Z80_MACH_Z80N;
- break;
- default:
- elf_flags = 0;
- }
-
elf_elfheader (stdoutput)->e_flags = elf_flags;
+*/
}
#endif
break;
}
}
- /* Check for <label>[:] [.](EQU|DEFL) <value>. */
+ /* Check for <label>[:] =|([.](EQU|DEFL)) <value>. */
if (is_name_beginner (*input_line_pointer))
{
char *name;
line_start = input_line_pointer;
if (ignore_input ())
return 0;
-
c = get_symbol_name (&name);
rest = input_line_pointer + 1;
-
- if (ISSPACE (c) && colonless_labels)
- {
- if (c == '\n')
- {
- bump_line_counters ();
- LISTING_NEWLINE ();
- }
- c = ':';
- }
- if (c == ':' && sdcc_compat && rest[-2] != '$')
- dollar_label_clear ();
- if (*rest == ':')
+ if (c == ':' && *rest == ':')
{
/* remove second colon if SDCC compatibility enabled */
if (sdcc_compat)
++rest;
}
rest = (char*)skip_space (rest);
- if (*rest == '.')
- ++rest;
- if (strncasecmp (rest, "EQU", 3) == 0)
- len = 3;
- else if (strncasecmp (rest, "DEFL", 4) == 0)
- len = 4;
+ if (*rest == '=')
+ len = (rest[1] == '=') ? 2 : 1;
else
- len = 0;
- if (len && (!ISALPHA (rest[len])))
+ {
+ if (*rest == '.')
+ ++rest;
+ if (strncasecmp (rest, "EQU", 3) == 0)
+ len = 3;
+ else if (strncasecmp (rest, "DEFL", 4) == 0)
+ len = 4;
+ else
+ len = 0;
+ }
+ if (len && (len <= 2 || !ISALPHA (rest[len])))
{
/* Handle assignment here. */
if (line_start[-1] == '\n')
}
input_line_pointer = rest + len - 1;
/* Allow redefining with "DEFL" (len == 4), but not with "EQU". */
- equals (name, len == 4);
+ switch (len)
+ {
+ case 1: /* label = expr */
+ case 4: /* label DEFL expr */
+ equals (name, 1);
+ break;
+ case 2: /* label == expr */
+ case 3: /* label EQU expr */
+ equals (name, 0);
+ break;
+ }
return 1;
}
else
}
/* Check whether a symbol involves a register. */
-static int
+static bfd_boolean
contains_register (symbolS *sym)
{
if (sym)
{
- expressionS * ex = symbol_get_value_expression(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));
+ switch (ex->X_op)
+ {
+ case O_register:
+ return TRUE;
+
+ case O_add:
+ case O_subtract:
+ if (ex->X_op_symbol && contains_register (ex->X_op_symbol))
+ return TRUE;
+ /* Fall through. */
+ case O_uminus:
+ case O_symbol:
+ if (ex->X_add_symbol && contains_register (ex->X_add_symbol))
+ return TRUE;
+ break;
+
+ default:
+ break;
+ }
}
- return 0;
+ return FALSE;
}
/* Parse general expression, not looking for indexed addressing. */
/* Condition codes, including some synonyms provided by HiTech zas. */
static const struct reg_entry cc_tab[] =
{
- { "age", 6 << 3 },
- { "alt", 7 << 3 },
- { "c", 3 << 3 },
- { "di", 4 << 3 },
- { "ei", 5 << 3 },
- { "lge", 2 << 3 },
- { "llt", 3 << 3 },
- { "m", 7 << 3 },
- { "nc", 2 << 3 },
- { "nz", 0 << 3 },
- { "p", 6 << 3 },
- { "pe", 5 << 3 },
- { "po", 4 << 3 },
- { "z", 1 << 3 },
+ { "age", 6 << 3, INS_ALL },
+ { "alt", 7 << 3, INS_ALL },
+ { "c", 3 << 3, INS_ALL },
+ { "di", 4 << 3, INS_ALL },
+ { "ei", 5 << 3, INS_ALL },
+ { "lge", 2 << 3, INS_ALL },
+ { "llt", 3 << 3, INS_ALL },
+ { "m", 7 << 3, INS_ALL },
+ { "nc", 2 << 3, INS_ALL },
+ { "nz", 0 << 3, INS_ALL },
+ { "p", 6 << 3, INS_ALL },
+ { "pe", 5 << 3, INS_ALL },
+ { "po", 4 << 3, INS_ALL },
+ { "z", 1 << 3, INS_ALL },
} ;
/* Parse condition code. */
}
p = frag_more (1);
*p = val->X_add_number;
- if ( contains_register (val->X_add_symbol) || contains_register (val->X_op_symbol) )
+ if (contains_register (val->X_add_symbol) || contains_register (val->X_op_symbol))
{
ill_op ();
}
}
else
{
- /* For symbols only, constants are stored at begin of function */
+ /* For symbols only, constants are stored at begin of function. */
fix_new_exp (frag_now, p - frag_now->fr_literal, 1, val,
(r_type == BFD_RELOC_8_PCREL) ? TRUE : FALSE, r_type);
}
if ((prefix == 0) && (rnum & R_INDEX))
{
prefix = (rnum & R_IX) ? 0xDD : 0xFD;
- if (!(ins_ok & INS_EZ80))
+ if (!(ins_ok & (INS_EZ80|INS_R800|INS_Z80N)))
check_mach (INS_IDX_HALF);
rnum &= ~R_INDEX;
}
ill_op ();
break;
}
- check_mach (INS_ROT_II_LD);
+ if (!(ins_ok & INS_Z80N))
+ check_mach (INS_ROT_II_LD);
}
/* Fall through. */
case O_register:
{
if (port.X_add_number == REG_BC && !(ins_ok & INS_EZ80))
ill_op ();
- else if (reg.X_add_number == REG_F && !(ins_ok & INS_R800))
+ else if (reg.X_add_number == REG_F && !(ins_ok & (INS_R800|INS_Z80N)))
check_mach (INS_IN_F_C);
q = frag_more (2);
*q++ = 0xED;
/* Allow "out (c), 0" as unportable instruction. */
if (reg.X_op == O_constant && reg.X_add_number == 0)
{
- check_mach (INS_OUT_C_0);
+ if (!(ins_ok & INS_Z80N))
+ check_mach (INS_OUT_C_0);
reg.X_op = O_register;
reg.X_add_number = 6;
}
{
if (ins_ok & INS_GBZ80)
ill_op ();
- else if (!(ins_ok & INS_EZ80))
+ else if (!(ins_ok & (INS_EZ80|INS_R800|INS_Z80N)))
check_mach (INS_IDX_HALF);
*q++ = prefix;
}
}
if ((ins_ok & INS_GBZ80) && prefix != 0)
ill_op ();
- if (ii_halves && !(ins_ok & INS_EZ80))
+ if (ii_halves && !(ins_ok & (INS_EZ80|INS_R800|INS_Z80N)))
check_mach (INS_IDX_HALF);
if (prefix == 0 && (ins_ok & INS_EZ80))
{
case REG_BC: opcode = 0x07; break;
case REG_DE: opcode = 0x17; break;
case REG_HL: opcode = 0x27; break;
- case REG_IX: opcode = (!prefix || prefix == 0xDD) ? 0x37 : 0x31; break;
- case REG_IY: opcode = prefix ? ((prefix == 0xDD) ? 0x31 : 0x37) : 0x36; break;
+ case REG_IX: opcode = (prefix == 0xED || prefix == 0xDD) ? 0x37 : 0x31; break;
+ case REG_IY: opcode = (prefix == 0xED || prefix == 0xDD) ? 0x31 : 0x37; break;
default:
ill_op ();
}
{ "dw", z80_cons, 2},
{ "psect", psect, 0}, /* TODO: Translate attributes. */
{ "set", 0, 0}, /* Real instruction on z80. */
+ { "xdef", s_globl, 0}, /* Synonym for .GLOBAL */
+ { "xref", s_ignore, 0}, /* Synonym for .EXTERN */
{ NULL, 0, 0 }
} ;
{ "scf", 0x00, 0x37, emit_insn, INS_ALL },
{ "set", 0xCB, 0xC0, emit_bit, INS_ALL },
{ "setae",0xED, 0x95, emit_insn, INS_Z80N },
- { "sl1", 0xCB, 0x30, emit_mr, INS_SLI },
+ { "sl1", 0xCB, 0x30, emit_mr, INS_SLI|INS_Z80N },
{ "sla", 0xCB, 0x20, emit_mr, INS_ALL },
- { "sli", 0xCB, 0x30, emit_mr, INS_SLI },
- { "sll", 0xCB, 0x30, emit_mr, INS_SLI },
+ { "sli", 0xCB, 0x30, emit_mr, INS_SLI|INS_Z80N },
+ { "sll", 0xCB, 0x30, emit_mr, INS_SLI|INS_Z80N },
{ "slp", 0xED, 0x76, emit_insn, INS_Z180|INS_EZ80 },
{ "sra", 0xCB, 0x28, emit_mr, INS_ALL },
{ "srl", 0xCB, 0x38, emit_mr, INS_ALL },
"first unrecognized character is `%c'"), *p);
}
}
-end:
+ end:
input_line_pointer = old_ptr;
}
return reloc;
}
+int
+z80_tc_labels_without_colon (void)
+{
+ return colonless_labels;
+}
+
int
z80_tc_label_is_local (const char *name)
{