aboutsummaryrefslogtreecommitdiff
path: root/gas/config/tc-z80.c
diff options
context:
space:
mode:
Diffstat (limited to 'gas/config/tc-z80.c')
-rw-r--r--gas/config/tc-z80.c2487
1 files changed, 2034 insertions, 453 deletions
diff --git a/gas/config/tc-z80.c b/gas/config/tc-z80.c
index f2f7b1d..a93b579 100644
--- a/gas/config/tc-z80.c
+++ b/gas/config/tc-z80.c
@@ -1,4 +1,4 @@
-/* tc-z80.c -- Assemble code for the Zilog Z80 and ASCII R800
+/* tc-z80.c -- Assemble code for the Zilog Z80, Z180, EZ80 and ASCII R800
Copyright (C) 2005-2020 Free Software Foundation, Inc.
Contributed by Arnold Metselaar <arnold_m@operamail.com>
@@ -22,6 +22,7 @@
#include "as.h"
#include "safe-ctype.h"
#include "subsegs.h"
+#include "elf/z80.h"
/* Exported constants. */
const char comment_chars[] = ";\0";
@@ -37,23 +38,63 @@ enum options
{
OPTION_MACH_Z80 = OPTION_MD_BASE,
OPTION_MACH_R800,
+ OPTION_MACH_Z180,
+ OPTION_MACH_EZ80_Z80,
+ OPTION_MACH_EZ80_ADL,
+ OPTION_MACH_GBZ80,
+ OPTION_MACH_INST,
+ OPTION_MACH_NO_INST,
OPTION_MACH_IUD,
OPTION_MACH_WUD,
OPTION_MACH_FUD,
OPTION_MACH_IUP,
OPTION_MACH_WUP,
- OPTION_MACH_FUP
+ OPTION_MACH_FUP,
+ OPTION_FLOAT_FORMAT,
+ OPTION_DOUBLE_FORMAT,
+ OPTION_COMPAT_LL_PREFIX,
+ OPTION_COMPAT_COLONLESS,
+ OPTION_COMPAT_SDCC
};
-#define INS_Z80 1
-#define INS_UNDOC 2
-#define INS_UNPORT 4
-#define INS_R800 8
+#define INS_Z80 (1 << 0)
+#define INS_R800 (1 << 1)
+#define INS_GBZ80 (1 << 2)
+#define INS_Z180 (1 << 3)
+#define INS_EZ80 (1 << 4)
+#define INS_MARCH_MASK 0xffff
+
+#define INS_IDX_HALF (1 << 16)
+#define INS_IN_F_C (1 << 17)
+#define INS_OUT_C_0 (1 << 18)
+#define INS_SLI (1 << 19)
+#define INS_ROT_II_LD (1 << 20) /* instructions like SLA (ii+d),r; which is: LD r,(ii+d); SLA r; LD (ii+d),r */
+#define INS_TUNE_MASK 0xffff0000
+
+#define INS_NOT_GBZ80 (INS_Z80 | INS_Z180 | INS_R800 | INS_EZ80)
+
+#define INS_ALL 0
+#define INS_UNDOC (INS_IDX_HALF | INS_IN_F_C)
+#define INS_UNPORT (INS_OUT_C_0 | INS_SLI | INS_ROT_II_LD)
struct option md_longopts[] =
{
{ "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},
+ { "float", required_argument, NULL, OPTION_FLOAT_FORMAT},
+ { "double", required_argument, NULL, OPTION_DOUBLE_FORMAT},
+ { "strict", no_argument, NULL, OPTION_MACH_FUD},
+ { "full", no_argument, NULL, OPTION_MACH_IUP},
+ { "with-inst", required_argument, NULL, OPTION_MACH_INST},
+ { "Wnins", required_argument, NULL, OPTION_MACH_INST},
+ { "without-inst", required_argument, NULL, OPTION_MACH_NO_INST},
+ { "local-prefix", required_argument, NULL, OPTION_COMPAT_LL_PREFIX},
+ { "colonless", no_argument, NULL, OPTION_COMPAT_COLONLESS},
+ { "sdcc", no_argument, NULL, OPTION_COMPAT_SDCC},
+ { "Fins", required_argument, NULL, OPTION_MACH_NO_INST},
{ "ignore-undocumented-instructions", no_argument, NULL, OPTION_MACH_IUD },
{ "Wnud", no_argument, NULL, OPTION_MACH_IUD },
{ "warn-undocumented-instructions", no_argument, NULL, OPTION_MACH_WUD },
@@ -76,46 +117,169 @@ extern int coff_flags;
/* Instruction classes that silently assembled. */
static int ins_ok = INS_Z80 | INS_UNDOC;
/* Instruction classes that generate errors. */
-static int ins_err = INS_R800;
-/* Instruction classes actually used, determines machine type. */
-static int ins_used = INS_Z80;
+static int ins_err = ~(INS_Z80 | INS_UNDOC);
+/* eZ80 CPU mode (ADL or Z80) */
+static int cpu_mode = 0; /* 0 - Z80, 1 - ADL */
+/* accept SDCC specific instruction encoding */
+static int sdcc_compat = 0;
+/* accept colonless labels */
+static int colonless_labels = 0;
+/* local label prefix (NULL - default) */
+static const char *local_label_prefix = NULL;
+/* floating point support */
+typedef const char *(*str_to_float_t)(char *litP, int *sizeP);
+static str_to_float_t str_to_float;
+static str_to_float_t str_to_double;
+
+/* mode of current instruction */
+#define INST_MODE_S 0 /* short data mode */
+#define INST_MODE_IS 0 /* short instruction mode */
+#define INST_MODE_L 2 /* long data mode */
+#define INST_MODE_IL 1 /* long instruction mode */
+#define INST_MODE_FORCED 4 /* CPU mode changed by instruction suffix*/
+static char inst_mode;
+
+static int
+setup_instruction (const char *inst, int *add, int *sub)
+{
+ int n;
+ if (!strcmp (inst, "idx-reg-halves"))
+ n = INS_IDX_HALF;
+ else if (!strcmp (inst, "sli"))
+ n = INS_SLI;
+ else if (!strcmp (inst, "op-ii-ld"))
+ n = INS_ROT_II_LD;
+ else if (!strcmp (inst, "in-f-c"))
+ n = INS_IN_F_C;
+ else if (!strcmp (inst, "out-c-0"))
+ n = INS_OUT_C_0;
+ else
+ return 0;
+ *add |= n;
+ *sub &= ~n;
+ return 1;
+}
+
+static const char *
+str_to_zeda32 (char *litP, int *sizeP);
+static const char *
+str_to_float48 (char *litP, int *sizeP);
+
+static str_to_float_t
+get_str_to_float (const char *arg)
+{
+ if (strcasecmp(arg, "zeda32") == 0)
+ return str_to_zeda32;
+
+ if (strcasecmp(arg, "math48") == 0)
+ return str_to_float48;
+
+ if (strcasecmp(arg, "ieee754") != 0)
+ as_fatal (_("invalid floating point numbers type `%s'"), arg);
+ return NULL;
+}
+
+static int
+setup_instruction_list (const char *list, int *add, int *sub)
+{
+ char buf[16];
+ const char *b;
+ const char *e;
+ int sz;
+ int res = 0;
+ for (b = list; *b != '\0';)
+ {
+ e = strchr (b, ',');
+ if (e == NULL)
+ sz = strlen (b);
+ else
+ sz = e - b;
+ if (sz == 0 || sz >= (int)sizeof (buf))
+ {
+ as_bad (_("invalid INST in command line: %s"), b);
+ return 0;
+ }
+ memcpy (buf, b, sz);
+ buf[sz] = '\0';
+ if (setup_instruction (buf, add, sub))
+ res++;
+ else
+ {
+ as_bad (_("invalid INST in command line: %s"), buf);
+ return 0;
+ }
+ b = &b[sz];
+ if (*b == ',')
+ ++b;
+ }
+ return res;
+}
int
-md_parse_option (int c, const char* arg ATTRIBUTE_UNUSED)
+md_parse_option (int c, const char* arg)
{
switch (c)
{
default:
return 0;
case OPTION_MACH_Z80:
- ins_ok &= ~INS_R800;
- ins_err |= INS_R800;
+ ins_ok = (ins_ok & INS_TUNE_MASK) | INS_Z80;
+ ins_err = (ins_err & INS_MARCH_MASK) | (~INS_Z80 & INS_MARCH_MASK);
break;
case OPTION_MACH_R800:
- ins_ok = INS_Z80 | INS_UNDOC | INS_R800;
+ ins_ok = INS_R800 | INS_IDX_HALF;
ins_err = INS_UNPORT;
break;
- case OPTION_MACH_IUD:
- ins_ok |= INS_UNDOC;
- ins_err &= ~INS_UNDOC;
+ case OPTION_MACH_Z180:
+ ins_ok = INS_Z180;
+ ins_err = INS_UNDOC | INS_UNPORT;
break;
- case OPTION_MACH_IUP:
- ins_ok |= INS_UNDOC | INS_UNPORT;
- ins_err &= ~(INS_UNDOC | INS_UNPORT);
+ case OPTION_MACH_EZ80_Z80:
+ ins_ok = INS_EZ80;
+ ins_err = (INS_UNDOC | INS_UNPORT) & ~INS_IDX_HALF;
+ cpu_mode = 0;
+ break;
+ case OPTION_MACH_EZ80_ADL:
+ ins_ok = INS_EZ80;
+ 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_FLOAT_FORMAT:
+ str_to_float = get_str_to_float (arg);
+ break;
+ case OPTION_DOUBLE_FORMAT:
+ str_to_double = get_str_to_float (arg);
+ break;
+ case OPTION_MACH_INST:
+ if ((ins_ok & INS_GBZ80) == 0)
+ return setup_instruction_list(arg, & ins_ok, & ins_err);
+ break;
+ case OPTION_MACH_NO_INST:
+ if ((ins_ok & INS_GBZ80) == 0)
+ return setup_instruction_list(arg, & ins_err, & ins_ok);
break;
case OPTION_MACH_WUD:
- if ((ins_ok & INS_R800) == 0)
- {
- ins_ok &= ~(INS_UNDOC|INS_UNPORT);
- ins_err &= ~INS_UNDOC;
- }
+ case OPTION_MACH_IUD:
+ if ((ins_ok & INS_GBZ80) == 0)
+ {
+ ins_ok |= INS_UNDOC;
+ ins_err &= ~INS_UNDOC;
+ }
break;
case OPTION_MACH_WUP:
- ins_ok &= ~INS_UNPORT;
- ins_err &= ~(INS_UNDOC|INS_UNPORT);
+ case OPTION_MACH_IUP:
+ if ((ins_ok & INS_GBZ80) == 0)
+ {
+ ins_ok |= INS_UNDOC | INS_UNPORT;
+ ins_err &= ~(INS_UNDOC | INS_UNPORT);
+ }
break;
case OPTION_MACH_FUD:
- if ((ins_ok & INS_R800) == 0)
+ if ((ins_ok & (INS_R800 | INS_GBZ80)) == 0)
{
ins_ok &= (INS_UNDOC | INS_UNPORT);
ins_err |= INS_UNDOC | INS_UNPORT;
@@ -125,6 +289,16 @@ md_parse_option (int c, const char* arg ATTRIBUTE_UNUSED)
ins_ok &= ~INS_UNPORT;
ins_err |= INS_UNPORT;
break;
+ case OPTION_COMPAT_LL_PREFIX:
+ local_label_prefix = (arg && *arg) ? arg : NULL;
+ break;
+ case OPTION_COMPAT_SDCC:
+ sdcc_compat = 1;
+ local_label_prefix = "_";
+ break;
+ case OPTION_COMPAT_COLONLESS:
+ colonless_labels = 1;
+ break;
}
return 1;
@@ -134,28 +308,52 @@ void
md_show_usage (FILE * f)
{
fprintf (f, "\n\
-CPU model/instruction set options:\n\
+CPU model options:\n\
+ -z80\t\t\t assemble for Z80\n\
+ -r800\t\t\t assemble for R800\n\
+ -z180\t\t\t assemble for Z180\n\
+ -ez80\t\t\t assemble for eZ80 in Z80 mode by default\n\
+ -ez80-adl\t\t assemble for eZ80 in ADL mode by default\n\
+\n\
+Compatibility options:\n\
+ -local-prefix=TEXT\t treat labels prefixed by TEXT as local\n\
+ -colonless\t\t permit colonless labels\n\
+ -sdcc\t\t\t accept SDCC specific instruction syntax\n\
+ -float=FORMAT\t\t set floating point numbers format\n\
+ -double=FORMAT\t\t set floating point numbers format\n\
+Where FORMAT one of:\n\
+ ieee754\t\t IEEE754 compatible\n\
+ zeda32\t\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\
- -z80\t\t assemble for Z80\n\
+Obsolete options:\n\
-ignore-undocumented-instructions\n\
- -Wnud\n\
-\tsilently assemble undocumented Z80-instructions that work on R800\n\
+ -Wnud\t\t\t silently assemble undocumented Z80-instructions that work on R800\n\
-ignore-unportable-instructions\n\
- -Wnup\n\
-\tsilently assemble all undocumented Z80-instructions\n\
+ -Wnup\t\t\t silently assemble all undocumented Z80-instructions\n\
-warn-undocumented-instructions\n\
- -Wud\n\
-\tissue warnings for undocumented Z80-instructions that work on R800\n\
+ -Wud\t\t\t issue warnings for undocumented Z80-instructions that work on R800\n\
-warn-unportable-instructions\n\
- -Wup\n\
-\tissue warnings for other undocumented Z80-instructions\n\
+ -Wup\t\t\t issue warnings for other undocumented Z80-instructions\n\
-forbid-undocumented-instructions\n\
- -Fud\n\
-\ttreat all undocumented z80-instructions as errors\n\
+ -Fud\t\t\t treat all undocumented Z80-instructions as errors\n\
-forbid-unportable-instructions\n\
- -Fup\n\
-\ttreat undocumented z80-instructions that do not work on R800 as errors\n\
- -r800\t assemble for R800\n\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");
}
@@ -182,6 +380,7 @@ struct reg_entry
#define REG_F (6 | 8)
#define REG_I (9)
#define REG_R (10)
+#define REG_MB (11)
#define REG_AF (3 | R_STACKABLE)
#define REG_BC (0 | R_STACKABLE | R_ARITH)
@@ -212,6 +411,7 @@ static const struct reg_entry regtable[] =
{"iyh",REG_H | R_IY },
{"iyl",REG_L | R_IY },
{"l", REG_L },
+ {"mb", REG_MB },
{"r", REG_R },
{"sp", REG_SP },
} ;
@@ -226,6 +426,9 @@ md_begin (void)
unsigned int i, j, k;
char buf[BUFLEN];
+ if (ins_ok & INS_EZ80) /* if select EZ80 cpu then */
+ listing_lhs_width = 6; /* use 6 bytes per line in the listing */
+
reg.X_op = O_register;
reg.X_md = 0;
reg.X_add_symbol = reg.X_op_symbol = 0;
@@ -263,22 +466,27 @@ z80_md_end (void)
{
int mach_type;
- if (ins_used & (INS_UNPORT | INS_R800))
- ins_used |= INS_UNDOC;
-
- switch (ins_used)
+ switch (ins_ok & INS_MARCH_MASK)
{
case INS_Z80:
- mach_type = bfd_mach_z80strict;
+ 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;
break;
- case INS_Z80|INS_UNDOC:
- mach_type = bfd_mach_z80;
+ case INS_R800:
+ mach_type = bfd_mach_r800;
break;
- case INS_Z80|INS_UNDOC|INS_UNPORT:
- mach_type = bfd_mach_z80full;
+ case INS_Z180:
+ mach_type = bfd_mach_z180;
break;
- case INS_Z80|INS_UNDOC|INS_R800:
- mach_type = bfd_mach_r800;
+ case INS_GBZ80:
+ mach_type = bfd_mach_gbz80;
+ break;
+ case INS_EZ80:
+ mach_type = cpu_mode ? bfd_mach_ez80_adl : bfd_mach_ez80_z80;
break;
default:
mach_type = 0;
@@ -287,6 +495,36 @@ z80_md_end (void)
bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach_type);
}
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+void
+z80_elf_final_processing (void)
+{
+ 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;
+ default:
+ elf_flags = 0;
+ }
+
+ elf_elfheader (stdoutput)->e_flags = elf_flags;
+}
+#endif
+
static const char *
skip_space (const char *s)
{
@@ -328,6 +566,10 @@ z80_start_line_hook (void)
return 1;
}
break;
+ case '#':
+ if (sdcc_compat)
+ *p = (*skip_space (p + 1) == '(') ? '+' : ' ';
+ break;
}
}
/* Check for <label>[:] [.](EQU|DEFL) <value>. */
@@ -344,10 +586,25 @@ z80_start_line_hook (void)
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 == ':')
- ++rest;
- if (*rest == ' ' || *rest == '\t')
- ++rest;
+ {
+ /* remove second colon if SDCC compatibility enabled */
+ if (sdcc_compat)
+ *rest = ' ';
+ ++rest;
+ }
+ rest = (char*)skip_space (rest);
if (*rest == '.')
++rest;
if (strncasecmp (rest, "EQU", 3) == 0)
@@ -386,10 +643,22 @@ md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
}
const char *
-md_atof (int type ATTRIBUTE_UNUSED, char *litP ATTRIBUTE_UNUSED,
- int *sizeP ATTRIBUTE_UNUSED)
+md_atof (int type, char *litP, int *sizeP)
{
- return _("floating point numbers are not implemented");
+ switch (type)
+ {
+ case 'f':
+ case 'F':
+ if (str_to_float)
+ return str_to_float (litP, sizeP);
+ break;
+ case 'd':
+ case 'D':
+ if (str_to_double)
+ return str_to_double (litP, sizeP);
+ break;
+ }
+ return ieee_md_atof (type, litP, sizeP, FALSE);
}
valueT
@@ -401,8 +670,7 @@ md_section_align (segT seg ATTRIBUTE_UNUSED, valueT size)
long
md_pcrel_from (fixS * fixp)
{
- return fixp->fx_where +
- fixp->fx_frag->fr_address + 1;
+ return fixp->fx_where + fixp->fx_frag->fr_address;
}
typedef const char * (asfunc)(char, char, const char*);
@@ -413,6 +681,7 @@ typedef struct _table_t
unsigned char prefix;
unsigned char opcode;
asfunc * fp;
+ unsigned inss; /*0 - all CPU types or list of supported INS_* */
} table_t;
/* Compares the key for structs that start with a char * to the key. */
@@ -436,6 +705,9 @@ static char err_flag;
static void
error (const char * message)
{
+ if (err_flag)
+ return;
+
as_bad ("%s", message);
err_flag = 1;
}
@@ -449,27 +721,10 @@ ill_op (void)
static void
wrong_mach (int ins_type)
{
- const char *p;
-
- switch (ins_type)
- {
- case INS_UNDOC:
- p = "undocumented instruction";
- break;
- case INS_UNPORT:
- p = "instruction does not work on R800";
- break;
- case INS_R800:
- p = "instruction only works R800";
- break;
- default:
- p = 0; /* Not reachable. */
- }
-
if (ins_type & ins_err)
- error (_(p));
+ ill_op();
else
- as_warn ("%s", _(p));
+ as_warn (_("undocumented instruction"));
}
static void
@@ -477,7 +732,6 @@ check_mach (int ins_type)
{
if ((ins_type & ins_ok) == 0)
wrong_mach (ins_type);
- ins_used |= ins_type;
}
/* Check whether an expression is indirect. */
@@ -546,8 +800,24 @@ parse_exp_not_indexed (const char *s, expressionS *op)
{
const char *p;
int indir;
+ int make_shift = -1;
p = skip_space (s);
+ if (sdcc_compat && (*p == '<' || *p == '>'))
+ {
+ switch (*p)
+ {
+ case '<': /* LSB request */
+ make_shift = 0;
+ break;
+ case '>': /* MSB request */
+ make_shift = cpu_mode ? 16 : 8;
+ break;
+ }
+ s = ++p;
+ p = skip_space (p);
+ }
+
op->X_md = indir = is_indir (p);
input_line_pointer = (char*) s ;
expression (op);
@@ -562,9 +832,65 @@ parse_exp_not_indexed (const char *s, expressionS *op)
default:
break;
}
+
+ if (make_shift >= 0)
+ {
+ /* replace [op] by [op >> shift] */
+ expressionS data;
+ op->X_add_symbol = make_expr_symbol (op);
+ op->X_add_number = 0;
+ op->X_op = O_right_shift;
+ memset (&data, 0, sizeof (data));
+ data.X_op = O_constant;
+ data.X_add_number = make_shift;
+ op->X_op_symbol = make_expr_symbol (&data);
+ }
return input_line_pointer;
}
+static int
+unify_indexed (expressionS *op)
+{
+ if (O_register != symbol_get_value_expression(op->X_add_symbol)->X_op)
+ return 0;
+
+ 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();
+ return 0;
+ }
+
+ /* convert subtraction to addition of negative value */
+ 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;
+ }
+ /* clear X_add_number of the expression */
+ if (op->X_add_number != 0)
+ {
+ expressionS add;
+ memset (&add, 0, sizeof (add));
+ add.X_op = O_symbol;
+ add.X_add_number = op->X_add_number;
+ add.X_add_symbol = op->X_op_symbol;
+ add.X_op_symbol = 0;
+ op->X_add_symbol = make_expr_symbol(&add);
+ }
+ else
+ op->X_add_symbol = op->X_op_symbol;
+
+ op->X_add_number = rnum;
+ op->X_op_symbol = 0;
+ return 1;
+}
+
/* Parse expression, change operator to O_md1 for indexed addressing*/
static const char *
parse_exp (const char *s, expressionS *op)
@@ -574,32 +900,8 @@ parse_exp (const char *s, expressionS *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;
- }
- }
+ if (unify_indexed(op) && op->X_md)
+ 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)) )
@@ -608,6 +910,19 @@ parse_exp (const char *s, expressionS *op)
op->X_op = O_md1;
}
break;
+ case O_constant:
+ /* parse SDCC syntax where index register offset placed before parentheses */
+ if (sdcc_compat && is_indir (res))
+ {
+ expressionS off;
+ off = *op;
+ res = parse_exp (res, op);
+ if (op->X_op != O_md1 || op->X_add_symbol != zero)
+ ill_op ();
+ else
+ op->X_add_symbol = make_expr_symbol (&off);
+ }
+ break;
default:
break;
}
@@ -706,11 +1021,89 @@ void z80_cons_fix_new (fragS *frag_p, int offset, int nbytes, expressionS *exp)
}
static void
+emit_data_val (expressionS * val, int size)
+{
+ char *p;
+ bfd_reloc_code_real_type r_type;
+
+ p = frag_more (size);
+ if (val->X_op == O_constant)
+ {
+ int i;
+ for (i = 0; i < size; ++i)
+ p[i] = (char)(val->X_add_number >> (i*8));
+ return;
+ }
+
+ switch (size)
+ {
+ case 1: r_type = BFD_RELOC_8; break;
+ case 2: r_type = BFD_RELOC_16; break;
+ case 3: r_type = BFD_RELOC_24; break;
+ case 4: r_type = BFD_RELOC_32; break;
+ case 8: r_type = BFD_RELOC_64; break;
+ default:
+ as_fatal (_("invalid data size %d"), size);
+ }
+
+ if ( (val->X_op == O_register)
+ || (val->X_op == O_md1)
+ || contains_register(val->X_add_symbol)
+ || contains_register(val->X_op_symbol) )
+ ill_op ();
+
+ if (size <= 2 && val->X_op_symbol)
+ {
+ bfd_boolean simplify = TRUE;
+ int shift = symbol_get_value_expression(val->X_op_symbol)->X_add_number;
+ if (val->X_op == O_bit_and && shift == (1 << (size*8))-1)
+ shift = 0;
+ else if (val->X_op != O_right_shift)
+ shift = -1;
+
+ if (size == 1)
+ {
+ switch (shift)
+ {
+ case 0: r_type = BFD_RELOC_Z80_BYTE0; break;
+ case 8: r_type = BFD_RELOC_Z80_BYTE1; break;
+ case 16: r_type = BFD_RELOC_Z80_BYTE2; break;
+ case 24: r_type = BFD_RELOC_Z80_BYTE3; break;
+ default: simplify = FALSE;
+ }
+ }
+ else /* if (size == 2) */
+ {
+ switch (shift)
+ {
+ case 0: r_type = BFD_RELOC_Z80_WORD0; break;
+ case 16: r_type = BFD_RELOC_Z80_WORD1; break;
+ default: simplify = FALSE;
+ }
+ }
+
+ if (simplify)
+ {
+ val->X_op = O_symbol;
+ val->X_op_symbol = NULL;
+ val->X_add_number = 0;
+ }
+ }
+
+ fix_new_exp (frag_now, p - frag_now->fr_literal, size, val, FALSE, r_type);
+}
+
+static void
emit_byte (expressionS * val, bfd_reloc_code_real_type r_type)
{
char *p;
int lo, hi;
+ if (r_type == BFD_RELOC_8)
+ {
+ emit_data_val (val, 1);
+ return;
+ }
p = frag_more (1);
*p = val->X_add_number;
if ( contains_register(val->X_add_symbol) || contains_register(val->X_op_symbol) )
@@ -736,31 +1129,16 @@ emit_byte (expressionS * val, bfd_reloc_code_real_type r_type)
}
else
{
+ /* 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);
- /* FIXME : Process constant offsets immediately. */
}
}
static void
emit_word (expressionS * val)
{
- char *p;
-
- p = frag_more (2);
- if ( (val->X_op == O_register)
- || (val->X_op == O_md1)
- || contains_register(val->X_add_symbol)
- || contains_register(val->X_op_symbol) )
- ill_op ();
- else
- {
- *p = val->X_add_number;
- p[1] = (val->X_add_number>>8);
- if (val->X_op != O_constant)
- fix_new_exp (frag_now, p - frag_now->fr_literal, 2,
- val, FALSE, BFD_RELOC_16);
- }
+ emit_data_val (val, (inst_mode & INST_MODE_IL) ? 3 : 2);
}
static void
@@ -790,7 +1168,8 @@ emit_mx (char prefix, char opcode, int shift, expressionS * arg)
if ((prefix == 0) && (rnum & R_INDEX))
{
prefix = (rnum & R_IX) ? 0xDD : 0xFD;
- check_mach (INS_UNDOC);
+ if (!(ins_ok & INS_EZ80))
+ check_mach (INS_IDX_HALF);
rnum &= ~R_INDEX;
}
if (rnum > 7)
@@ -805,6 +1184,11 @@ emit_mx (char prefix, char opcode, int shift, expressionS * arg)
* q ++ = opcode + (rnum << shift);
break;
case O_md1:
+ if (ins_ok & INS_GBZ80)
+ {
+ ill_op ();
+ break;
+ }
q = frag_more (2);
*q++ = (rnum & R_IX) ? 0xDD : 0xFD;
*q = (prefix) ? prefix : (opcode + (6 << shift));
@@ -851,7 +1235,7 @@ emit_m (char prefix, char opcode, const char *args)
are allowed). */
static const char *
-emit_mr (char prefix, char opcode, const char *args, bfd_boolean unportable)
+emit_mr (char prefix, char opcode, const char *args)
{
expressionS arg_m, arg_r;
const char *p;
@@ -874,13 +1258,10 @@ emit_mr (char prefix, char opcode, const char *args, bfd_boolean unportable)
ill_op ();
break;
}
- check_mach (INS_UNPORT);
- unportable = TRUE;
+ check_mach (INS_ROT_II_LD);
}
/* Fall through. */
case O_register:
- if (unportable)
- check_mach (INS_UNPORT);
emit_mx (prefix, opcode, 0, & arg_m);
break;
default:
@@ -889,18 +1270,6 @@ emit_mr (char prefix, char opcode, const char *args, bfd_boolean unportable)
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)
{
@@ -934,6 +1303,13 @@ emit_s (char prefix, char opcode, const char *args)
const char *p;
p = parse_exp (args, & arg_s);
+ if (*p == ',' && arg_s.X_md == 0 && arg_s.X_op == O_register && arg_s.X_add_number == REG_A)
+ { /* possible instruction in generic format op A,x */
+ if (!(ins_ok & INS_EZ80) && !sdcc_compat)
+ ill_op();
+ ++p;
+ p = parse_exp (p, & arg_s);
+ }
emit_sx (prefix, opcode, & arg_s);
return p;
}
@@ -999,6 +1375,7 @@ emit_jr (char prefix ATTRIBUTE_UNUSED, char opcode, const char * args)
{
q = frag_more (1);
*q = opcode;
+ addr.X_add_number--; /* pcrel computes after offset code */
emit_byte (&addr, BFD_RELOC_8_PCREL);
}
return p;
@@ -1219,7 +1596,7 @@ emit_bit (char prefix, char opcode, const char * args)
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, FALSE);
+ p = emit_mr (prefix, opcode + (bn << 3), p);
}
else
ill_op ();
@@ -1349,16 +1726,15 @@ emit_in (char prefix ATTRIBUTE_UNUSED, char opcode ATTRIBUTE_UNUSED,
}
else
{
- if (port.X_add_number == REG_C)
+ if (port.X_add_number == REG_C || port.X_add_number == REG_BC)
{
- if (reg.X_add_number == REG_F)
- check_mach (INS_UNDOC);
- else
- {
- q = frag_more (2);
- *q++ = 0xED;
- *q = 0x40|((reg.X_add_number&7)<<3);
- }
+ 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))
+ check_mach (INS_IN_F_C);
+ q = frag_more (2);
+ *q++ = 0xED;
+ *q = 0x40|((reg.X_add_number&7)<<3);
}
else
ill_op ();
@@ -1370,6 +1746,39 @@ emit_in (char prefix ATTRIBUTE_UNUSED, char opcode ATTRIBUTE_UNUSED,
}
static const char *
+emit_in0 (char prefix ATTRIBUTE_UNUSED, char opcode ATTRIBUTE_UNUSED,
+ const char * args)
+{
+ expressionS reg, port;
+ const char *p;
+ char *q;
+
+ p = parse_exp (args, &reg);
+ if (*p++ != ',')
+ {
+ error (_("bad instruction syntax"));
+ return p;
+ }
+
+ p = parse_exp (p, &port);
+ if (reg.X_md == 0
+ && reg.X_op == O_register
+ && reg.X_add_number <= 7
+ && port.X_md
+ && port.X_op != O_md1
+ && port.X_op != O_register)
+ {
+ q = frag_more (2);
+ *q++ = 0xED;
+ *q = 0x00|(reg.X_add_number << 3);
+ emit_byte (&port, BFD_RELOC_8);
+ }
+ else
+ ill_op ();
+ return p;
+}
+
+static const char *
emit_out (char prefix ATTRIBUTE_UNUSED, char opcode ATTRIBUTE_UNUSED,
const char * args)
{
@@ -1389,7 +1798,7 @@ emit_out (char prefix ATTRIBUTE_UNUSED, char opcode ATTRIBUTE_UNUSED,
/* Allow "out (c), 0" as unportable instruction. */
if (reg.X_op == O_constant && reg.X_add_number == 0)
{
- check_mach (INS_UNPORT);
+ check_mach (INS_OUT_C_0);
reg.X_op = O_register;
reg.X_add_number = 6;
}
@@ -1411,8 +1820,10 @@ emit_out (char prefix ATTRIBUTE_UNUSED, char opcode ATTRIBUTE_UNUSED,
}
else
{
- if (REG_C == port.X_add_number)
+ if (REG_C == port.X_add_number || port.X_add_number == REG_BC)
{
+ if (port.X_add_number == REG_BC && !(ins_ok & INS_EZ80))
+ ill_op ();
q = frag_more (2);
*q++ = 0xED;
*q = 0x41 | (reg.X_add_number << 3);
@@ -1424,6 +1835,38 @@ emit_out (char prefix ATTRIBUTE_UNUSED, char opcode ATTRIBUTE_UNUSED,
}
static const char *
+emit_out0 (char prefix ATTRIBUTE_UNUSED, char opcode ATTRIBUTE_UNUSED,
+ const char * args)
+{
+ expressionS reg, port;
+ const char *p;
+ char *q;
+
+ p = parse_exp (args, & port);
+ if (*p++ != ',')
+ {
+ error (_("bad instruction syntax"));
+ return p;
+ }
+ p = parse_exp (p, &reg);
+ if (port.X_md != 0
+ && port.X_op != O_register
+ && port.X_op != O_md1
+ && reg.X_md == 0
+ && reg.X_op == O_register
+ && reg.X_add_number <= 7)
+ {
+ q = frag_more (2);
+ *q++ = 0xED;
+ *q = 0x01 | (reg.X_add_number << 3);
+ emit_byte (&port, BFD_RELOC_8);
+ }
+ else
+ ill_op ();
+ return p;
+}
+
+static const char *
emit_rst (char prefix ATTRIBUTE_UNUSED, char opcode, const char * args)
{
expressionS addr;
@@ -1448,194 +1891,548 @@ emit_rst (char prefix ATTRIBUTE_UNUSED, char opcode, const char * args)
}
static void
-emit_ldxhl (char prefix, char opcode, expressionS *src, expressionS *d)
-{
+emit_ld_m_n(expressionS *dst, expressionS *src)
+{ /* for 8-bit indirect load to memory instructions like: LD (HL),n or LD (ii+d),n */
char *q;
+ char prefix;
+ expressionS dst_offset;
- if (src->X_md)
+ switch (dst->X_add_number)
+ {
+ case REG_HL: prefix = 0x00; break;
+ case REG_IX: prefix = 0xDD; break;
+ case REG_IY: prefix = 0xFD; break;
+ default:
+ ill_op ();
+ return;
+ }
+
+ q = frag_more (prefix ? 2 : 1);
+ if (prefix)
+ *q++ = prefix;
+ *q = 0x36;
+ if (prefix)
+ {
+ dst_offset = *dst;
+ dst_offset.X_op = O_symbol;
+ dst_offset.X_add_number = 0;
+ emit_byte (& dst_offset, BFD_RELOC_Z80_DISP8);
+ }
+ emit_byte (src, BFD_RELOC_8);
+}
+
+static void
+emit_ld_m_r(expressionS *dst, expressionS *src)
+{ /* for 8-bit load register to memory instructions: LD (<expression>),r */
+ char *q;
+ char prefix = 0;
+ expressionS dst_offset;
+
+ switch (dst->X_op)
+ {
+ case O_md1:
+ prefix = (dst->X_add_number == REG_IX) ? 0xDD : 0xFD;
+ /* Fall through. */
+ case O_register:
+ switch (dst->X_add_number)
+ {
+ case REG_BC: /* LD (BC),A */
+ case REG_DE: /* LD (DE),A */
+ if (src->X_add_number == REG_A)
+ {
+ q = frag_more (1);
+ *q = 0x02 | ((dst->X_add_number & 3) << 4);
+ return;
+ }
+ break;
+ case REG_IX:
+ case REG_IY:
+ case REG_HL: /* LD (HL),r or LD (ii+d),r */
+ if (src->X_add_number <= 7)
+ {
+ q = frag_more (prefix ? 2 : 1);
+ if (prefix)
+ *q++ = prefix;
+ *q = 0x70 | src->X_add_number;
+ if (prefix)
+ {
+ dst_offset = *dst;
+ dst_offset.X_op = O_symbol;
+ dst_offset.X_add_number = 0;
+ emit_byte (& dst_offset, BFD_RELOC_Z80_DISP8);
+ }
+ return;
+ }
+ break;
+ default:;
+ }
+ break;
+ default: /* LD (nn),A */
+ if (src->X_add_number == REG_A)
+ {
+ q = frag_more (1);
+ *q = 0x32;
+ emit_word (dst);
+ return;
+ }
+ break;
+ }
ill_op ();
- else
+}
+
+static void
+emit_ld_m_rr(expressionS *dst, expressionS *src)
+{ /* for 16-bit load register to memory instructions: LD (<expression>),rr */
+ char *q;
+ char prefix = 0;
+ char opcode = 0;
+ expressionS dst_offset;
+
+ switch (dst->X_op)
{
- if (src->X_op == O_register)
- {
- if (src->X_add_number>7)
- ill_op ();
- if (prefix)
- {
- q = frag_more (2);
- *q++ = prefix;
- }
- else
- q = frag_more (1);
- *q = opcode + src->X_add_number;
- if (d)
- emit_byte (d, BFD_RELOC_Z80_DISP8);
- }
+ case O_md1: /* eZ80 instructions LD (ii+d),rr */
+ case O_register: /* eZ80 instructions LD (HL),rr */
+ if (!(ins_ok & INS_EZ80)) /* 16-bit indirect load group is supported by eZ80 only */
+ ill_op ();
+ switch (dst->X_add_number)
+ {
+ case REG_IX: prefix = 0xDD; break;
+ case REG_IY: prefix = 0xFD; break;
+ case REG_HL: prefix = 0xED; break;
+ default:
+ ill_op ();
+ }
+ switch (src->X_add_number)
+ {
+ case REG_BC: opcode = 0x0F; break;
+ case REG_DE: opcode = 0x1F; break;
+ case REG_HL: opcode = 0x2F; break;
+ case REG_IX: opcode = (prefix != '\xfd') ? 0x3F : 0x3E; break;
+ case REG_IY: opcode = (prefix != '\xfd') ? 0x3E : 0x3F; break;
+ default:
+ ill_op ();
+ }
+ q = frag_more (prefix ? 2 : 1);
+ *q++ = prefix;
+ *q = opcode;
+ if (prefix == '\xfd' || prefix == '\xdd')
+ {
+ dst_offset = *dst;
+ dst_offset.X_op = O_symbol;
+ dst_offset.X_add_number = 0;
+ emit_byte (& dst_offset, BFD_RELOC_Z80_DISP8);
+ }
+ break;
+ default: /* LD (nn),rr */
+ if (ins_ok & INS_GBZ80)
+ {
+ /* GBZ80 supports only LD (nn),SP */
+ if (src->X_add_number == REG_SP)
+ {
+ prefix = 0x00;
+ opcode = 0x08;
+ }
+ else
+ ill_op ();
+ }
else
- {
- if (prefix)
- {
- q = frag_more (2);
- *q++ = prefix;
- }
- else
- q = frag_more (1);
- *q = opcode^0x46;
- if (d)
- emit_byte (d, BFD_RELOC_Z80_DISP8);
- emit_byte (src, BFD_RELOC_8);
- }
+ {
+ switch (src->X_add_number)
+ {
+ case REG_BC: prefix = 0xED; opcode = 0x43; break;
+ case REG_DE: prefix = 0xED; opcode = 0x53; break;
+ case REG_HL: prefix = 0x00; opcode = 0x22; break;
+ case REG_IX: prefix = 0xDD; opcode = 0x22; break;
+ case REG_IY: prefix = 0xFD; opcode = 0x22; break;
+ case REG_SP: prefix = 0xED; opcode = 0x73; break;
+ default:
+ ill_op ();
+ }
+ }
+ q = frag_more (prefix ? 2 : 1);
+ if (prefix)
+ *q++ = prefix;
+ *q = opcode;
+ emit_word (dst);
}
}
static void
-emit_ldreg (int dest, expressionS * src)
-{
+emit_ld_r_m (expressionS *dst, expressionS *src)
+{ /* for 8-bit memory load to register: LD r,(xxx) */
char *q;
- int rnum;
+ char prefix = 0;
+ char opcode = 0;
+ expressionS src_offset;
- switch (dest)
+ if (dst->X_add_number == REG_A && src->X_op == O_register)
+ { /* LD A,(BC) or LD A,(DE) */
+ switch (src->X_add_number)
+ {
+ case REG_BC: opcode = 0x0A; break;
+ case REG_DE: opcode = 0x1A; break;
+ default: break;
+ }
+ if (opcode != 0)
+ {
+ q = frag_more (1);
+ *q = opcode;
+ return;
+ }
+ }
+
+ switch (src->X_op)
{
- /* 8 Bit ld group: */
- case REG_I:
- case REG_R:
- if (src->X_md == 0 && src->X_op == O_register && src->X_add_number == REG_A)
- {
- q = frag_more (2);
- *q++ = 0xED;
- *q = (dest == REG_I) ? 0x47 : 0x4F;
- }
- else
- ill_op ();
+ case O_md1:
+ case O_register:
+ if (dst->X_add_number > 7)
+ ill_op ();
+ opcode = 0x46; /* LD B,(HL) */
+ switch (src->X_add_number)
+ {
+ case REG_HL: prefix = 0x00; break;
+ case REG_IX: prefix = 0xDD; break;
+ case REG_IY: prefix = 0xFD; break;
+ default:
+ ill_op ();
+ }
+ q = frag_more (prefix ? 2 : 1);
+ if (prefix)
+ *q++ = prefix;
+ *q = opcode | ((dst->X_add_number & 7) << 3);
+ if (prefix)
+ {
+ src_offset = *src;
+ src_offset.X_op = O_symbol;
+ src_offset.X_add_number = 0;
+ emit_byte (& src_offset, BFD_RELOC_Z80_DISP8);
+ }
break;
+ default: /* LD A,(nn) */
+ if (dst->X_add_number == REG_A)
+ {
+ q = frag_more (1);
+ *q = 0x3A;
+ emit_word (src);
+ }
+ }
+}
- case REG_A:
- if ((src->X_md) && src->X_op != O_register && src->X_op != O_md1)
- {
- q = frag_more (1);
- *q = 0x3A;
- emit_word (src);
- break;
- }
-
- if ((src->X_md)
- && src->X_op == O_register
- && (src->X_add_number == REG_BC || src->X_add_number == REG_DE))
- {
- q = frag_more (1);
- *q = 0x0A + ((src->X_add_number & 1) << 4);
- break;
- }
+static void
+emit_ld_r_n (expressionS *dst, expressionS *src)
+{ /* for 8-bit immediate value load to register: LD r,n */
+ char *q;
+ char prefix = 0;
- if ((!src->X_md)
- && src->X_op == O_register
- && (src->X_add_number == REG_R || src->X_add_number == REG_I))
- {
- q = frag_more (2);
- *q++ = 0xED;
- *q = (src->X_add_number == REG_I) ? 0x57 : 0x5F;
- break;
- }
- /* Fall through. */
+ switch (dst->X_add_number)
+ {
+ case REG_H|R_IX:
+ case REG_L|R_IX:
+ prefix = 0xDD;
+ break;
+ case REG_H|R_IY:
+ case REG_L|R_IY:
+ prefix = 0xFD;
+ break;
+ case REG_A:
case REG_B:
case REG_C:
case REG_D:
case REG_E:
- emit_sx (0, 0x40 + (dest << 3), src);
- break;
-
case REG_H:
case REG_L:
- if ((src->X_md == 0)
- && (src->X_op == O_register)
- && (src->X_add_number & R_INDEX))
- ill_op ();
- else
- emit_sx (0, 0x40 + (dest << 3), src);
break;
+ default:
+ ill_op ();
+// return;
+ }
- case R_IX | REG_H:
- case R_IX | REG_L:
- case R_IY | REG_H:
- case R_IY | REG_L:
- if (src->X_md)
- {
- ill_op ();
- break;
- }
- check_mach (INS_UNDOC);
- if (src-> X_op == O_register)
- {
- rnum = src->X_add_number;
- if ((rnum & ~R_INDEX) < 8
- && ((rnum & R_INDEX) == (dest & R_INDEX)
- || ( (rnum & ~R_INDEX) != REG_H
- && (rnum & ~R_INDEX) != REG_L)))
- {
- q = frag_more (2);
- *q++ = (dest & R_IX) ? 0xDD : 0xFD;
- *q = 0x40 + ((dest & 0x07) << 3) + (rnum & 7);
- }
- else
- ill_op ();
- }
+ q = frag_more (prefix ? 2 : 1);
+ if (prefix)
+ {
+ if (ins_ok & INS_GBZ80)
+ ill_op ();
+ else if (!(ins_ok & INS_EZ80))
+ check_mach (INS_IDX_HALF);
+ *q++ = prefix;
+ }
+ *q = 0x06 | ((dst->X_add_number & 7) << 3);
+ emit_byte (src, BFD_RELOC_8);
+}
+
+static void
+emit_ld_r_r (expressionS *dst, expressionS *src)
+{ /* mostly 8-bit load register from register instructions: LD r,r */
+ /* there are some exceptions: LD SP,HL/IX/IY; LD I,HL and LD HL,I */
+ char *q;
+ char prefix = 0;
+ char opcode = 0;
+ int ii_halves = 0;
+
+ switch (dst->X_add_number)
+ {
+ case REG_SP:
+ switch (src->X_add_number)
+ {
+ case REG_HL: prefix = 0x00; break;
+ case REG_IX: prefix = 0xDD; break;
+ case REG_IY: prefix = 0xFD; break;
+ default:
+ ill_op ();
+ }
+ if (ins_ok & INS_GBZ80)
+ ill_op ();
+ opcode = 0xF9;
+ break;
+ case REG_HL:
+ if (!(ins_ok & INS_EZ80))
+ ill_op ();
+ if (src->X_add_number != REG_I)
+ ill_op ();
+ if (cpu_mode < 1)
+ error (_("ADL mode instruction"));
+ /* LD HL,I */
+ prefix = 0xED;
+ opcode = 0xD7;
+ break;
+ case REG_I:
+ if (src->X_add_number == REG_HL)
+ {
+ if (!(ins_ok & INS_EZ80))
+ ill_op ();
+ if (cpu_mode < 1)
+ error (_("ADL mode instruction"));
+ prefix = 0xED;
+ opcode = 0xC7;
+ }
+ else if (src->X_add_number == REG_A)
+ {
+ prefix = 0xED;
+ opcode = 0x47;
+ }
else
- {
- q = frag_more (2);
- *q++ = (dest & R_IX) ? 0xDD : 0xFD;
- *q = 0x06 + ((dest & 0x07) << 3);
- emit_byte (src, BFD_RELOC_8);
- }
+ ill_op ();
+ break;
+ case REG_MB:
+ if (!(ins_ok & INS_EZ80) || (src->X_add_number != REG_A))
+ ill_op ();
+ if (cpu_mode < 1)
+ error (_("ADL mode instruction"));
+ prefix = 0xED;
+ opcode = 0x6D;
break;
+ case REG_R:
+ if (src->X_add_number == REG_A) /* LD R,A */
+ {
+ prefix = 0xED;
+ opcode = 0x4F;
+ }
+ else
+ ill_op ();
+ break;
+ case REG_A:
+ if (src->X_add_number == REG_I) /* LD A,I */
+ {
+ prefix = 0xED;
+ opcode = 0x57;
+ break;
+ }
+ else if (src->X_add_number == REG_R) /* LD A,R */
+ {
+ prefix = 0xED;
+ opcode = 0x5F;
+ break;
+ }
+ else if (src->X_add_number == REG_MB) /* LD A,MB */
+ {
+ if (!(ins_ok & INS_EZ80))
+ ill_op ();
+ else
+ {
+ if (cpu_mode < 1)
+ error (_("ADL mode instruction"));
+ prefix = 0xED;
+ opcode = 0x6E;
+ }
+ break;
+ }
+ /* Fall through. */
+ case REG_B:
+ case REG_C:
+ case REG_D:
+ case REG_E:
+ case REG_H:
+ case REG_L:
+ prefix = 0x00;
+ break;
+ case REG_H|R_IX:
+ case REG_L|R_IX:
+ prefix = 0xDD;
+ ii_halves = 1;
+ break;
+ case REG_H|R_IY:
+ case REG_L|R_IY:
+ prefix = 0xFD;
+ ii_halves = 1;
+ break;
+ default:
+ ill_op ();
+ }
- /* 16 Bit ld group: */
- case REG_SP:
- if (src->X_md == 0
- && src->X_op == O_register
- && REG_HL == (src->X_add_number &~ R_INDEX))
- {
- q = frag_more ((src->X_add_number & R_INDEX) ? 2 : 1);
- if (src->X_add_number & R_INDEX)
- *q++ = (src->X_add_number & R_IX) ? 0xDD : 0xFD;
- *q = 0xF9;
- break;
- }
+ if (opcode == 0)
+ {
+ switch (src->X_add_number)
+ {
+ case REG_A:
+ case REG_B:
+ case REG_C:
+ case REG_D:
+ case REG_E:
+ break;
+ case REG_H:
+ case REG_L:
+ if (prefix != 0)
+ ill_op (); /* LD iiH/L,H/L are not permitted */
+ break;
+ case REG_H|R_IX:
+ case REG_L|R_IX:
+ if (prefix == '\xfd' || dst->X_add_number == REG_H || dst->X_add_number == REG_L)
+ ill_op (); /* LD IYL,IXL and LD H,IXH are not permitted */
+ prefix = 0xDD;
+ ii_halves = 1;
+ break;
+ case REG_H|R_IY:
+ case REG_L|R_IY:
+ if (prefix == '\xdd' || dst->X_add_number == REG_H || dst->X_add_number == REG_L)
+ ill_op (); /* LD IXH,IYH and LD L,IYL are not permitted */
+ prefix = 0xFD;
+ ii_halves = 1;
+ break;
+ default:
+ ill_op ();
+ }
+ opcode = 0x40 + ((dst->X_add_number & 7) << 3) + (src->X_add_number & 7);
+ }
+ if ((ins_ok & INS_GBZ80) && prefix != 0)
+ ill_op ();
+ if (ii_halves && !(ins_ok & INS_EZ80))
+ check_mach (INS_IDX_HALF);
+ if (prefix == 0 && (ins_ok & INS_EZ80))
+ {
+ switch (opcode)
+ {
+ case 0x40: /* SIS prefix, in Z80 it is LD B,B */
+ case 0x49: /* LIS prefix, in Z80 it is LD C,C */
+ case 0x52: /* SIL prefix, in Z80 it is LD D,D */
+ case 0x5B: /* LIL prefix, in Z80 it is LD E,E */
+ as_warn(_("unsupported instruction, assembled as NOP"));
+ opcode = 0x00;
+ break;
+ default:;
+ }
+ }
+ q = frag_more (prefix ? 2 : 1);
+ if (prefix)
+ *q++ = prefix;
+ *q = opcode;
+}
+
+static void
+emit_ld_rr_m (expressionS *dst, expressionS *src)
+{ /* for 16-bit indirect load from memory to register: LD rr,(xxx) */
+ char *q;
+ char prefix = 0;
+ char opcode = 0;
+ expressionS src_offset;
+
+ /* GBZ80 has no support for 16-bit load from memory instructions */
+ if (ins_ok & INS_GBZ80)
+ ill_op ();
+
+ prefix = 0xED;
+ switch (src->X_op)
+ {
+ case O_md1: /* LD rr,(ii+d) */
+ prefix = (src->X_add_number == REG_IX) ? 0xDD : 0xFD;
/* Fall through. */
- case REG_BC:
- case REG_DE:
- if (src->X_op == O_register || src->X_op == O_md1)
- ill_op ();
- q = frag_more (src->X_md ? 2 : 1);
- if (src->X_md)
- {
- *q++ = 0xED;
- *q = 0x4B + ((dest & 3) << 4);
- }
- else
- *q = 0x01 + ((dest & 3) << 4);
- emit_word (src);
+ case O_register: /* LD rr,(HL) */
+ /* currently only EZ80 has support for 16bit indirect memory load instructions */
+ if (!(ins_ok & INS_EZ80))
+ ill_op ();
+ switch (dst->X_add_number)
+ {
+ case REG_BC: opcode = 0x07; break;
+ case REG_DE: opcode = 0x17; break;
+ case REG_HL: opcode = 0x27; break;
+ case REG_IX: opcode = (!prefix || prefix == '\xdd') ? 0x37 : 0x31; break;
+ case REG_IY: opcode = prefix ? ((prefix == '\xdd') ? 0x31 : 0x37) : 0x36; break;
+ default:
+ ill_op ();
+ }
+ q = frag_more (2);
+ *q++ = prefix;
+ *q = opcode;
+ if (prefix != '\xed')
+ {
+ src_offset = *src;
+ src_offset.X_op = O_symbol;
+ src_offset.X_add_number = 0;
+ emit_byte (& src_offset, BFD_RELOC_Z80_DISP8);
+ }
break;
+ default: /* LD rr,(nn) */
+ switch (dst->X_add_number)
+ {
+ case REG_BC: prefix = 0xED; opcode = 0x4B; break;
+ case REG_DE: prefix = 0xED; opcode = 0x5B; break;
+ case REG_HL: prefix = 0x00; opcode = 0x2A; break;
+ case REG_SP: prefix = 0xED; opcode = 0x7B; break;
+ case REG_IX: prefix = 0xDD; opcode = 0x2A; break;
+ case REG_IY: prefix = 0xFD; opcode = 0x2A; break;
+ default:
+ ill_op ();
+ }
+ q = frag_more (prefix ? 2 : 1);
+ if (prefix)
+ *q++ = prefix;
+ *q = opcode;
+ emit_word (src);
+ }
+ return;
+}
+static void
+emit_ld_rr_nn (expressionS *dst, expressionS *src)
+{ /* mostly load imediate value to multibyte register instructions: LD rr,nn */
+ char *q;
+ char prefix = 0x00;
+ char opcode = 0x21; /* LD HL,nn */
+ switch (dst->X_add_number)
+ {
+ case REG_IX:
+ prefix = 0xDD;
+ break;
+ case REG_IY:
+ prefix = 0xFD;
+ break;
case REG_HL:
- case REG_HL | R_IX:
- case REG_HL | R_IY:
- if (src->X_op == O_register || src->X_op == O_md1)
- ill_op ();
- q = frag_more ((dest & R_INDEX) ? 2 : 1);
- if (dest & R_INDEX)
- * q ++ = (dest & R_IX) ? 0xDD : 0xFD;
- *q = (src->X_md) ? 0x2A : 0x21;
- emit_word (src);
break;
-
- case REG_AF:
- case REG_F:
- ill_op ();
+ case REG_BC:
+ case REG_DE:
+ case REG_SP:
+ opcode = 0x01 + ((dst->X_add_number & 3) << 4);
break;
-
default:
- abort ();
+ ill_op ();
+ return;
}
+ if (prefix && (ins_ok & INS_GBZ80))
+ ill_op ();
+ q = frag_more (prefix ? 2 : 1);
+ if (prefix)
+ *q++ = prefix;
+ *q = opcode;
+ emit_word (src);
}
static const char *
@@ -1644,80 +2441,343 @@ emit_ld (char prefix_in ATTRIBUTE_UNUSED, char opcode_in ATTRIBUTE_UNUSED,
{
expressionS dst, src;
const char *p;
+
+ p = parse_exp (args, & dst);
+ if (*p++ != ',')
+ error (_("bad instruction syntax"));
+ p = parse_exp (p, & src);
+
+ if (dst.X_md)
+ {
+ if (src.X_op == O_register)
+ {
+ if (src.X_add_number <= 7)
+ emit_ld_m_r (& dst, & src); /* LD (xxx),r */
+ else
+ emit_ld_m_rr (& dst, & src); /* LD (xxx),rr */
+ }
+ else
+ emit_ld_m_n (& dst, & src); /* LD (hl),n or LD (ix/y+r),n */
+ }
+ else if (dst.X_op == O_register)
+ {
+ if (src.X_md)
+ {
+ if (dst.X_add_number <= 7)
+ emit_ld_r_m (& dst, & src);
+ else
+ emit_ld_rr_m (& dst, & src);
+ }
+ else if (src.X_op == O_register)
+ emit_ld_r_r (& dst, & src);
+ else if ((dst.X_add_number & ~R_INDEX) <= 7)
+ emit_ld_r_n (& dst, & src);
+ else
+ emit_ld_rr_nn (& dst, & src);
+ }
+ else
+ ill_op ();
+
+ return p;
+}
+
+static const char *
+emit_lddldi (char prefix, char opcode, const char * args)
+{
+ expressionS dst, src;
+ const char *p;
char *q;
- char prefix, opcode;
- p = parse_exp (args, &dst);
+ if (!(ins_ok & INS_GBZ80))
+ return emit_insn(prefix, opcode, args);
+
+ p = parse_exp (args, & dst);
if (*p++ != ',')
error (_("bad instruction syntax"));
- p = parse_exp (p, &src);
+ p = parse_exp (args, & src);
+
+ if (dst.X_op != O_register || src.X_op != O_register)
+ ill_op ();
+
+ /* convert opcode 0xA0 . 0x22, 0xA8 . 0x32 */
+ opcode = (opcode & 0x08) * 2 + 0x22;
+
+ if (dst.X_md != 0
+ && dst.X_add_number == REG_HL
+ && src.X_md == 0
+ && src.X_add_number == REG_A)
+ opcode |= 0x00; /* LDx (HL),A */
+ else if (dst.X_md == 0
+ && dst.X_add_number == REG_A
+ && src.X_md != 0
+ && src.X_add_number == REG_HL)
+ opcode |= 0x08; /* LDx A,(HL) */
+ else
+ ill_op ();
- switch (dst.X_op)
+ q = frag_more (1);
+ *q = opcode;
+ return p;
+}
+
+static const char *
+emit_ldh (char prefix ATTRIBUTE_UNUSED, char opcode ATTRIBUTE_UNUSED,
+ const char * args)
+{
+ expressionS dst, src;
+ const char *p;
+ char *q;
+
+ p = parse_exp (args, & dst);
+ if (*p++ != ',')
{
- case O_md1:
- {
- expressionS dst_offset = dst;
- dst_offset.X_op = O_symbol;
- dst_offset.X_add_number = 0;
- emit_ldxhl ((dst.X_add_number & R_IX) ? 0xDD : 0xFD, 0x70,
- &src, &dst_offset);
- }
- break;
+ error (_("bad instruction syntax"));
+ return p;
+ }
- case O_register:
- if (dst.X_md)
- {
- switch (dst.X_add_number)
- {
- case REG_BC:
- case REG_DE:
- if (src.X_md == 0 && src.X_op == O_register && src.X_add_number == REG_A)
- {
- q = frag_more (1);
- *q = 0x02 + ( (dst.X_add_number & 1) << 4);
- }
- else
- ill_op ();
- break;
- case REG_HL:
- emit_ldxhl (0, 0x70, &src, NULL);
- break;
- default:
- ill_op ();
- }
- }
+ p = parse_exp (p, & src);
+ if (dst.X_md == 0
+ && dst.X_op == O_register
+ && dst.X_add_number == REG_A
+ && src.X_md != 0
+ && src.X_op != O_md1
+ && src.X_op != O_register)
+ {
+ q = frag_more (1);
+ *q = 0xF0;
+ emit_byte (& src, BFD_RELOC_8);
+ }
+ else if (dst.X_md != 0
+ && dst.X_op != O_md1
+ && src.X_md == 0
+ && src.X_op == O_register
+ && src.X_add_number == REG_A)
+ {
+ if (dst.X_op == O_register)
+ {
+ if (dst.X_add_number == REG_C)
+ {
+ q = frag_more (1);
+ *q = 0xE2;
+ }
+ else
+ ill_op();
+ }
else
- emit_ldreg (dst.X_add_number, &src);
+ {
+ q = frag_more (1);
+ *q = 0xE0;
+ emit_byte (& dst, BFD_RELOC_8);
+ }
+ }
+ else
+ ill_op ();
+
+ return p;
+}
+
+static const char *
+parse_lea_pea_args (const char * args, expressionS *op)
+{
+ const char *p;
+ p = parse_exp (args, op);
+ if (sdcc_compat && *p == ',' && op->X_op == O_register)
+ {
+ expressionS off;
+ p = parse_exp (p + 1, &off);
+ op->X_op = O_add;
+ op->X_add_symbol = make_expr_symbol (&off);
+ }
+ return p;
+}
+
+static const char *
+emit_lea (char prefix, char opcode, const char * args)
+{
+ expressionS dst, src;
+ const char *p;
+ char *q;
+ int rnum;
+
+ p = parse_exp (args, & dst);
+ if (dst.X_md != 0 || dst.X_op != O_register)
+ ill_op ();
+
+ rnum = dst.X_add_number;
+ switch (rnum)
+ {
+ case REG_BC:
+ case REG_DE:
+ case REG_HL:
+ opcode = 0x02 | ((rnum & 0x03) << 4);
+ break;
+ case REG_IX:
+ opcode = 0x32; /* lea ix,ix+d has opcode 0x32; lea ix,iy+d has opcode 0x54 */
break;
+ case REG_IY:
+ opcode = 0x33; /* lea iy,iy+d has opcode 0x33; lea iy,ix+d has opcode 0x55 */
+ break;
+ default:
+ ill_op ();
+ }
+ if (*p++ != ',')
+ error (_("bad instruction syntax"));
+
+ p = parse_lea_pea_args (p, & src);
+ if (src.X_md != 0 || src.X_op != O_add /*&& src.X_op != O_register*/)
+ ill_op ();
+
+ rnum = src.X_add_number;
+ switch (src.X_op)
+ {
+ case O_add:
+ break;
+ case O_register: /* permit instructions like LEA rr,IX without displacement specified */
+ src.X_add_symbol = zero;
+ break;
default:
- if (src.X_md != 0 || src.X_op != O_register)
- ill_op ();
- prefix = opcode = 0;
- switch (src.X_add_number)
- {
- case REG_A:
- opcode = 0x32; break;
- case REG_BC: case REG_DE: case REG_SP:
- prefix = 0xED; opcode = 0x43 + ((src.X_add_number&3)<<4); break;
- case REG_HL:
- opcode = 0x22; break;
- case REG_HL|R_IX:
- prefix = 0xDD; opcode = 0x22; break;
- case REG_HL|R_IY:
- prefix = 0xFD; opcode = 0x22; break;
- }
- if (opcode)
- {
- q = frag_more (prefix?2:1);
- if (prefix)
- *q++ = prefix;
- *q = opcode;
- emit_word (&dst);
- }
- else
- ill_op ();
+ ill_op ();
+ }
+
+ switch (rnum)
+ {
+ case REG_IX:
+ opcode = (opcode == 0x33) ? 0x55 : (opcode|0x00);
+ break;
+ case REG_IY:
+ opcode = (opcode == 0x32) ? 0x54 : (opcode|0x01);
}
+
+ q = frag_more (2);
+ *q++ = prefix;
+ *q = opcode;
+
+ src.X_op = O_symbol;
+ src.X_add_number = 0;
+ emit_byte (& src, BFD_RELOC_Z80_DISP8);
+
+ return p;
+}
+
+static const char *
+emit_mlt (char prefix, char opcode, const char * args)
+{
+ expressionS arg;
+ const char *p;
+ char *q;
+
+ p = parse_exp (args, & arg);
+ if (arg.X_md != 0 || arg.X_op != O_register || !(arg.X_add_number & R_ARITH))
+ ill_op ();
+
+ q = frag_more (2);
+ *q++ = prefix;
+ *q = opcode | ((arg.X_add_number & 3) << 4);
+
+ return p;
+}
+
+static const char *
+emit_pea (char prefix, char opcode, const char * args)
+{
+ expressionS arg;
+ const char *p;
+ char *q;
+
+ p = parse_lea_pea_args (args, & arg);
+ if (arg.X_md != 0
+ || (/*arg.X_op != O_register &&*/ arg.X_op != O_add)
+ || !(arg.X_add_number & R_INDEX))
+ ill_op ();
+ /* PEA ii without displacement is mostly typo,
+ because there is PUSH instruction which is shorter and faster */
+ /*if (arg.X_op == O_register)
+ as_warn(_("PEA is used without displacement, use PUSH instead"));*/
+
+ q = frag_more (2);
+ *q++ = prefix;
+ *q = opcode + (arg.X_add_number == REG_IY ? 1 : 0);
+
+ arg.X_op = O_symbol;
+ arg.X_add_number = 0;
+ emit_byte (& arg, BFD_RELOC_Z80_DISP8);
+
+ return p;
+}
+
+static const char *
+emit_reti (char prefix, char opcode, const char * args)
+{
+ if (ins_ok & INS_GBZ80)
+ return emit_insn(0x00, 0xD9, args);
+
+ return emit_insn(prefix, opcode, args);
+}
+
+static const char *
+emit_tst (char prefix, char opcode, const char *args)
+{
+ expressionS arg_s;
+ const char *p;
+ char *q;
+ int rnum;
+
+ p = parse_exp (args, & arg_s);
+ if (*p == ',' && arg_s.X_md == 0 && arg_s.X_op == O_register && arg_s.X_add_number == REG_A)
+ {
+ if (!(ins_ok & INS_EZ80))
+ ill_op();
+ ++p;
+ p = parse_exp (p, & arg_s);
+ }
+
+ rnum = arg_s.X_add_number;
+ switch (arg_s.X_op)
+ {
+ case O_md1:
+ ill_op ();
+ break;
+ case O_register:
+ rnum = arg_s.X_add_number;
+ if (arg_s.X_md != 0)
+ {
+ if (rnum != REG_HL)
+ ill_op ();
+ else
+ rnum = 6;
+ }
+ q = frag_more (2);
+ *q++ = prefix;
+ *q = opcode | (rnum << 3);
+ break;
+ default:
+ if (arg_s.X_md)
+ ill_op ();
+ q = frag_more (2);
+ *q++ = prefix;
+ *q = opcode | 0x60;
+ emit_byte (& arg_s, BFD_RELOC_8);
+ }
+ return p;
+}
+
+static const char *
+emit_tstio (char prefix, char opcode, const char *args)
+{
+ expressionS arg;
+ const char *p;
+ char *q;
+
+ p = parse_exp (args, & arg);
+ if (arg.X_md || arg.X_op == O_register || arg.X_op == O_md1)
+ ill_op ();
+
+ q = frag_more (2);
+ *q++ = prefix;
+ *q = opcode;
+ emit_byte(& arg, BFD_RELOC_8);
+
return p;
}
@@ -1767,6 +2827,76 @@ emit_data (int size ATTRIBUTE_UNUSED)
input_line_pointer = (char *)(p-1);
}
+static void
+z80_cons (int size)
+{
+ const char *p;
+ expressionS exp;
+
+ if (is_it_end_of_statement ())
+ {
+ demand_empty_rest_of_line ();
+ return;
+ }
+ p = skip_space (input_line_pointer);
+
+ do
+ {
+ p = parse_exp (p, &exp);
+ if (exp.X_op == O_md1 || exp.X_op == O_register)
+ {
+ ill_op ();
+ break;
+ }
+ if (exp.X_md)
+ as_warn (_("parentheses ignored"));
+ emit_data_val (&exp, size);
+ p = skip_space (p);
+ } while (*p++ == ',') ;
+ input_line_pointer = (char *)(p-1);
+}
+
+/* next functions were commented out because it is difficult to mix
+ both ADL and Z80 mode instructions within one COFF file:
+ objdump cannot recognize point of mode switching.
+*/
+static void
+set_cpu_mode (int mode)
+{
+ if (ins_ok & INS_EZ80)
+ cpu_mode = mode;
+ else
+ error (_("CPU mode is unsupported by target"));
+}
+
+static void
+assume (int arg ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char c;
+ int n;
+
+ input_line_pointer = (char*)skip_space (input_line_pointer);
+ c = get_symbol_name (& name);
+ if (strncasecmp(name, "ADL", 4) != 0)
+ {
+ ill_op ();
+ return;
+ }
+
+ restore_line_pointer (c);
+ input_line_pointer = (char*)skip_space (input_line_pointer);
+ if (*input_line_pointer++ != '=')
+ {
+ error (_("assignment expected"));
+ return;
+ }
+ input_line_pointer = (char*)skip_space (input_line_pointer);
+ n = get_single_number ();
+
+ set_cpu_mode (n);
+}
+
static const char *
emit_mulub (char prefix ATTRIBUTE_UNUSED, char opcode, const char * args)
{
@@ -1834,97 +2964,267 @@ emit_muluw (char prefix ATTRIBUTE_UNUSED, char opcode, const char * args)
return p;
}
+static int
+assemble_suffix (const char **suffix)
+{
+ static
+ const char sf[8][4] =
+ {
+ "il",
+ "is",
+ "l",
+ "lil",
+ "lis",
+ "s",
+ "sil",
+ "sis"
+ };
+ const char *p;
+ const char (*t)[4];
+ char sbuf[4];
+ int i;
+
+ p = *suffix;
+ if (*p++ != '.')
+ return 0;
+
+ for (i = 0; (i < 3) && (ISALPHA (*p)); i++)
+ sbuf[i] = TOLOWER (*p++);
+ if (*p && !ISSPACE(*p))
+ return 0;
+ *suffix = p;
+ sbuf[i] = 0;
+
+ t = bsearch(sbuf, sf, ARRAY_SIZE (sf), sizeof(sf[0]), (int(*)(const void*, const void*))strcmp);
+ if (t == NULL)
+ return 0;
+ i = t - sf;
+ switch (i)
+ {
+ case 0: /* IL */
+ i = cpu_mode ? 0x5B : 0x52;
+ break;
+ case 1: /* IS */
+ i = cpu_mode ? 0x49 : 0x40;
+ break;
+ case 2: /* L */
+ i = cpu_mode ? 0x5B : 0x49;
+ break;
+ case 3: /* LIL */
+ i = 0x5B;
+ break;
+ case 4: /* LIS */
+ i = 0x49;
+ break;
+ case 5: /* S */
+ i = cpu_mode ? 0x52 : 0x40;
+ break;
+ case 6: /* SIL */
+ i = 0x52;
+ break;
+ case 7: /* SIS */
+ i = 0x40;
+ break;
+ }
+ *frag_more(1) = (char)i;
+ switch (i)
+ {
+ case 0x40: inst_mode = INST_MODE_FORCED | INST_MODE_S | INST_MODE_IS; break;
+ case 0x49: inst_mode = INST_MODE_FORCED | INST_MODE_L | INST_MODE_IS; break;
+ case 0x52: inst_mode = INST_MODE_FORCED | INST_MODE_S | INST_MODE_IL; break;
+ case 0x5B: inst_mode = INST_MODE_FORCED | INST_MODE_L | INST_MODE_IL; break;
+ }
+ return 1;
+}
+
+static void
+psect (int arg)
+{
+#if defined(OBJ_ELF)
+ return obj_elf_section (arg);
+#elif defined(OBJ_COFF)
+ return obj_coff_section (arg);
+#else
+#error Unknown object format
+#endif
+}
+
+static void
+set_inss (int inss)
+{
+ int old_ins;
+
+ if (!sdcc_compat)
+ as_fatal (_("Invalid directive"));
+
+ old_ins = ins_ok;
+ ins_ok &= INS_MARCH_MASK;
+ ins_ok |= inss;
+ if (old_ins != ins_ok)
+ cpu_mode = 0;
+}
+
+static void
+ignore (int arg ATTRIBUTE_UNUSED)
+{
+ ignore_rest_of_line ();
+}
+
+static void
+area (int arg)
+{
+ char *p;
+ if (!sdcc_compat)
+ as_fatal (_("Invalid directive"));
+ for (p = input_line_pointer; *p && *p != '(' && *p != '\n'; p++)
+ ;
+ if (*p == '(')
+ {
+ *p = '\n';
+ psect (arg);
+ *p++ = '(';
+ ignore_rest_of_line ();
+ }
+ else
+ psect (arg);
+}
+
/* Port specific pseudo ops. */
const pseudo_typeS md_pseudo_table[] =
{
+ { ".area", area, 0},
+ { ".assume", assume, 0},
+ { ".ez80", set_inss, INS_EZ80},
+ { ".gbz80", set_inss, INS_GBZ80},
+ { ".module", ignore, 0},
+ { ".optsdcc", ignore, 0},
+ { ".r800", set_inss, INS_R800},
+ { ".set", s_set, 0},
+ { ".z180", set_inss, INS_Z180},
+ { ".z80", set_inss, INS_Z80},
{ "db" , emit_data, 1},
- { "d24", cons, 3},
- { "d32", cons, 4},
- { "def24", cons, 3},
- { "def32", cons, 4},
+ { "d24", z80_cons, 3},
+ { "d32", z80_cons, 4},
+ { "def24", z80_cons, 3},
+ { "def32", z80_cons, 4},
{ "defb", emit_data, 1},
+ { "defm", emit_data, 1},
{ "defs", s_space, 1}, /* Synonym for ds on some assemblers. */
- { "defw", cons, 2},
+ { "defw", z80_cons, 2},
{ "ds", s_space, 1}, /* Fill with bytes rather than words. */
- { "dw", cons, 2},
- { "psect", obj_coff_section, 0}, /* TODO: Translate attributes. */
+ { "dw", z80_cons, 2},
+ { "psect", psect, 0}, /* TODO: Translate attributes. */
{ "set", 0, 0}, /* Real instruction on z80. */
{ NULL, 0, 0 }
} ;
static table_t instab[] =
{
- { "adc", 0x88, 0x4A, emit_adc },
- { "add", 0x80, 0x09, emit_add },
- { "and", 0x00, 0xA0, emit_s },
- { "bit", 0xCB, 0x40, emit_bit },
- { "call", 0xCD, 0xC4, emit_jpcc },
- { "ccf", 0x00, 0x3F, emit_insn },
- { "cp", 0x00, 0xB8, emit_s },
- { "cpd", 0xED, 0xA9, emit_insn },
- { "cpdr", 0xED, 0xB9, emit_insn },
- { "cpi", 0xED, 0xA1, emit_insn },
- { "cpir", 0xED, 0xB1, emit_insn },
- { "cpl", 0x00, 0x2F, emit_insn },
- { "daa", 0x00, 0x27, emit_insn },
- { "dec", 0x0B, 0x05, emit_incdec },
- { "di", 0x00, 0xF3, emit_insn },
- { "djnz", 0x00, 0x10, emit_jr },
- { "ei", 0x00, 0xFB, emit_insn },
- { "ex", 0x00, 0x00, emit_ex},
- { "exx", 0x00, 0xD9, emit_insn },
- { "halt", 0x00, 0x76, emit_insn },
- { "im", 0xED, 0x46, emit_im },
- { "in", 0x00, 0x00, emit_in },
- { "inc", 0x03, 0x04, emit_incdec },
- { "ind", 0xED, 0xAA, emit_insn },
- { "indr", 0xED, 0xBA, emit_insn },
- { "ini", 0xED, 0xA2, emit_insn },
- { "inir", 0xED, 0xB2, emit_insn },
- { "jp", 0xC3, 0xC2, emit_jpcc },
- { "jr", 0x18, 0x20, emit_jrcc },
- { "ld", 0x00, 0x00, emit_ld },
- { "ldd", 0xED, 0xA8, emit_insn },
- { "lddr", 0xED, 0xB8, emit_insn },
- { "ldi", 0xED, 0xA0, emit_insn },
- { "ldir", 0xED, 0xB0, emit_insn },
- { "mulub", 0xED, 0xC5, emit_mulub }, /* R800 only. */
- { "muluw", 0xED, 0xC3, emit_muluw }, /* R800 only. */
- { "neg", 0xed, 0x44, emit_insn },
- { "nop", 0x00, 0x00, emit_insn },
- { "or", 0x00, 0xB0, emit_s },
- { "otdr", 0xED, 0xBB, emit_insn },
- { "otir", 0xED, 0xB3, emit_insn },
- { "out", 0x00, 0x00, emit_out },
- { "outd", 0xED, 0xAB, emit_insn },
- { "outi", 0xED, 0xA3, emit_insn },
- { "pop", 0x00, 0xC1, emit_pop },
- { "push", 0x00, 0xC5, emit_pop },
- { "res", 0xCB, 0x80, emit_bit },
- { "ret", 0xC9, 0xC0, emit_retcc },
- { "reti", 0xED, 0x4D, emit_insn },
- { "retn", 0xED, 0x45, emit_insn },
- { "rl", 0xCB, 0x10, emit_mr_z80 },
- { "rla", 0x00, 0x17, emit_insn },
- { "rlc", 0xCB, 0x00, emit_mr_z80 },
- { "rlca", 0x00, 0x07, emit_insn },
- { "rld", 0xED, 0x6F, emit_insn },
- { "rr", 0xCB, 0x18, emit_mr_z80 },
- { "rra", 0x00, 0x1F, emit_insn },
- { "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_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 },
+ { "adc", 0x88, 0x4A, emit_adc, INS_ALL },
+ { "add", 0x80, 0x09, emit_add, INS_ALL },
+ { "and", 0x00, 0xA0, emit_s, INS_ALL },
+ { "bit", 0xCB, 0x40, emit_bit, INS_ALL },
+ { "call", 0xCD, 0xC4, emit_jpcc, INS_ALL },
+ { "ccf", 0x00, 0x3F, emit_insn, INS_ALL },
+ { "cp", 0x00, 0xB8, emit_s, INS_ALL },
+ { "cpd", 0xED, 0xA9, emit_insn, INS_NOT_GBZ80 },
+ { "cpdr", 0xED, 0xB9, emit_insn, INS_NOT_GBZ80 },
+ { "cpi", 0xED, 0xA1, emit_insn, INS_NOT_GBZ80 },
+ { "cpir", 0xED, 0xB1, emit_insn, INS_NOT_GBZ80 },
+ { "cpl", 0x00, 0x2F, emit_insn, INS_ALL },
+ { "daa", 0x00, 0x27, emit_insn, INS_ALL },
+ { "dec", 0x0B, 0x05, emit_incdec,INS_ALL },
+ { "di", 0x00, 0xF3, emit_insn, INS_ALL },
+ { "djnz", 0x00, 0x10, emit_jr, INS_NOT_GBZ80 },
+ { "ei", 0x00, 0xFB, emit_insn, INS_ALL },
+ { "ex", 0x00, 0x00, emit_ex, INS_NOT_GBZ80 },
+ { "exx", 0x00, 0xD9, emit_insn, INS_NOT_GBZ80 },
+ { "halt", 0x00, 0x76, emit_insn, INS_ALL },
+ { "im", 0xED, 0x46, emit_im, INS_NOT_GBZ80 },
+ { "in", 0x00, 0x00, emit_in, INS_NOT_GBZ80 },
+ { "in0", 0xED, 0x00, emit_in0, INS_Z180|INS_EZ80 },
+ { "inc", 0x03, 0x04, emit_incdec,INS_ALL },
+ { "ind", 0xED, 0xAA, emit_insn, INS_NOT_GBZ80 },
+ { "ind2", 0xED, 0x8C, emit_insn, INS_EZ80 },
+ { "ind2r",0xED, 0x9C, emit_insn, INS_EZ80 },
+ { "indm", 0xED, 0x8A, emit_insn, INS_EZ80 },
+ { "indmr",0xED, 0x9A, emit_insn, INS_EZ80 },
+ { "indr", 0xED, 0xBA, emit_insn, INS_NOT_GBZ80 },
+ { "indrx",0xED, 0xCA, emit_insn, INS_EZ80 },
+ { "ini", 0xED, 0xA2, emit_insn, INS_NOT_GBZ80 },
+ { "ini2", 0xED, 0x84, emit_insn, INS_EZ80 },
+ { "ini2r",0xED, 0x94, emit_insn, INS_EZ80 },
+ { "inim", 0xED, 0x82, emit_insn, INS_EZ80 },
+ { "inimr",0xED, 0x92, emit_insn, INS_EZ80 },
+ { "inir", 0xED, 0xB2, emit_insn, INS_NOT_GBZ80 },
+ { "inirx",0xED, 0xC2, emit_insn, INS_EZ80 },
+ { "jp", 0xC3, 0xC2, emit_jpcc, INS_ALL },
+ { "jr", 0x18, 0x20, emit_jrcc, INS_ALL },
+ { "ld", 0x00, 0x00, emit_ld, INS_ALL },
+ { "ldd", 0xED, 0xA8, emit_lddldi,INS_ALL }, /* GBZ80 has special meaning */
+ { "lddr", 0xED, 0xB8, emit_insn, INS_NOT_GBZ80 },
+ { "ldh", 0xE0, 0x00, emit_ldh, INS_GBZ80 },
+ { "ldhl", 0xE0, 0x00, emit_ldh, INS_GBZ80 },
+ { "ldi", 0xED, 0xA0, emit_lddldi,INS_ALL }, /* GBZ80 has special meaning */
+ { "ldir", 0xED, 0xB0, emit_insn, INS_NOT_GBZ80 },
+ { "lea", 0xED, 0x02, emit_lea, INS_EZ80 },
+ { "mlt", 0xED, 0x4C, emit_mlt, INS_Z180|INS_EZ80 },
+ { "mulub",0xED, 0xC5, emit_mulub,INS_R800 },
+ { "muluw",0xED, 0xC3, emit_muluw,INS_R800 },
+ { "neg", 0xed, 0x44, emit_insn, INS_NOT_GBZ80 },
+ { "nop", 0x00, 0x00, emit_insn, INS_ALL },
+ { "or", 0x00, 0xB0, emit_s, INS_ALL },
+ { "otd2r",0xED, 0xBC, emit_insn, INS_EZ80 },
+ { "otdm", 0xED, 0x8B, emit_insn, INS_Z180|INS_EZ80 },
+ { "otdmr",0xED, 0x9B, emit_insn, INS_Z180|INS_EZ80 },
+ { "otdr", 0xED, 0xBB, emit_insn, INS_NOT_GBZ80 },
+ { "otdrx",0xED, 0xCB, emit_insn, INS_EZ80 },
+ { "oti2r",0xED, 0xB4, emit_insn, INS_EZ80 },
+ { "otim", 0xED, 0x83, emit_insn, INS_Z180|INS_EZ80 },
+ { "otimr",0xED, 0x93, emit_insn, INS_Z180|INS_EZ80 },
+ { "otir", 0xED, 0xB3, emit_insn, INS_NOT_GBZ80 },
+ { "otirx",0xED, 0xC3, emit_insn, INS_EZ80 },
+ { "out", 0x00, 0x00, emit_out, INS_NOT_GBZ80 },
+ { "out0", 0xED, 0x01, emit_out0, INS_Z180|INS_EZ80 },
+ { "outd", 0xED, 0xAB, emit_insn, INS_NOT_GBZ80 },
+ { "outd2",0xED, 0xAC, emit_insn, INS_EZ80 },
+ { "outi", 0xED, 0xA3, emit_insn, INS_NOT_GBZ80 },
+ { "outi2",0xED, 0xA4, emit_insn, INS_EZ80 },
+ { "pea", 0xED, 0x65, emit_pea, INS_EZ80 },
+ { "pop", 0x00, 0xC1, emit_pop, INS_ALL },
+ { "push", 0x00, 0xC5, emit_pop, INS_ALL },
+ { "res", 0xCB, 0x80, emit_bit, INS_ALL },
+ { "ret", 0xC9, 0xC0, emit_retcc,INS_ALL },
+ { "reti", 0xED, 0x4D, emit_reti, INS_ALL }, /*GBZ80 has its own opcode for it*/
+ { "retn", 0xED, 0x45, emit_insn, INS_NOT_GBZ80 },
+ { "rl", 0xCB, 0x10, emit_mr, INS_ALL },
+ { "rla", 0x00, 0x17, emit_insn, INS_ALL },
+ { "rlc", 0xCB, 0x00, emit_mr, INS_ALL },
+ { "rlca", 0x00, 0x07, emit_insn, INS_ALL },
+ { "rld", 0xED, 0x6F, emit_insn, INS_NOT_GBZ80 },
+ { "rr", 0xCB, 0x18, emit_mr, INS_ALL },
+ { "rra", 0x00, 0x1F, emit_insn, INS_ALL },
+ { "rrc", 0xCB, 0x08, emit_mr, INS_ALL },
+ { "rrca", 0x00, 0x0F, emit_insn, INS_ALL },
+ { "rrd", 0xED, 0x67, emit_insn, INS_NOT_GBZ80 },
+ { "rsmix",0xED, 0x7E, emit_insn, INS_EZ80 },
+ { "rst", 0x00, 0xC7, emit_rst, INS_ALL },
+ { "sbc", 0x98, 0x42, emit_adc, INS_ALL },
+ { "scf", 0x00, 0x37, emit_insn, INS_ALL },
+ { "set", 0xCB, 0xC0, emit_bit, INS_ALL },
+ { "sla", 0xCB, 0x20, emit_mr, INS_ALL },
+ { "sli", 0xCB, 0x30, emit_mr, INS_SLI },
+ { "sll", 0xCB, 0x30, emit_mr, INS_SLI },
+ { "slp", 0xED, 0x76, emit_insn, INS_Z180|INS_EZ80 },
+ { "sra", 0xCB, 0x28, emit_mr, INS_ALL },
+ { "srl", 0xCB, 0x38, emit_mr, INS_ALL },
+ { "stmix",0xED, 0x7D, emit_insn, INS_EZ80 },
+ { "stop", 0x00, 0x10, emit_insn, INS_GBZ80 },
+ { "sub", 0x00, 0x90, emit_s, INS_ALL },
+ { "swap", 0xCB, 0x30, emit_mr, INS_GBZ80 },
+ { "tst", 0xED, 0x04, emit_tst, INS_Z180|INS_EZ80 },
+ { "tstio",0xED, 0x74, emit_tstio,INS_Z180|INS_EZ80 },
+ { "xor", 0x00, 0xA8, emit_s, INS_ALL },
} ;
void
@@ -1936,9 +3236,10 @@ md_assemble (char *str)
table_t *insp;
err_flag = 0;
+ inst_mode = cpu_mode ? (INST_MODE_L | INST_MODE_IL) : (INST_MODE_S | INST_MODE_IS);
old_ptr = input_line_pointer;
p = skip_space (str);
- for (i = 0; (i < BUFLEN) && (ISALPHA (*p));)
+ for (i = 0; (i < BUFLEN) && (ISALPHA (*p) || ISDIGIT (*p));)
buf[i++] = TOLOWER (*p++);
if (i == BUFLEN)
@@ -1947,18 +3248,27 @@ md_assemble (char *str)
buf[BUFLEN-1] = 0;
as_bad (_("Unknown instruction '%s'"), buf);
}
- else if ((*p) && (!ISSPACE (*p)))
- as_bad (_("syntax error"));
else
{
+ if ((*p) && (!ISSPACE (*p)))
+ {
+ if (*p != '.' || !(ins_ok & INS_EZ80) || !assemble_suffix(&p))
+ {
+ as_bad (_("syntax error"));
+ goto end;
+ }
+ }
buf[i] = 0;
p = skip_space (p);
key = buf;
insp = bsearch (&key, instab, ARRAY_SIZE (instab),
sizeof (instab[0]), key_cmp);
- if (!insp)
- as_bad (_("Unknown instruction '%s'"), buf);
+ if (!insp || (insp->inss && !(insp->inss & ins_ok)))
+ {
+ as_bad (_("Unknown instruction '%s'"), buf);
+ *frag_more(1) = 0;
+ }
else
{
p = insp->fp (insp->prefix, insp->opcode, p);
@@ -1968,6 +3278,7 @@ md_assemble (char *str)
*p);
}
}
+end:
input_line_pointer = old_ptr;
}
@@ -2013,6 +3324,34 @@ md_apply_fix (fixS * fixP, valueT* valP, segT seg ATTRIBUTE_UNUSED)
}
break;
+ case BFD_RELOC_Z80_BYTE0:
+ *p_lit++ = val;
+ fixP->fx_no_overflow = 1;
+ if (fixP->fx_addsy == NULL)
+ fixP->fx_done = 1;
+ break;
+
+ case BFD_RELOC_Z80_BYTE1:
+ *p_lit++ = (val >> 8);
+ fixP->fx_no_overflow = 1;
+ if (fixP->fx_addsy == NULL)
+ fixP->fx_done = 1;
+ break;
+
+ case BFD_RELOC_Z80_BYTE2:
+ *p_lit++ = (val >> 16);
+ fixP->fx_no_overflow = 1;
+ if (fixP->fx_addsy == NULL)
+ fixP->fx_done = 1;
+ break;
+
+ case BFD_RELOC_Z80_BYTE3:
+ *p_lit++ = (val >> 24);
+ fixP->fx_no_overflow = 1;
+ if (fixP->fx_addsy == NULL)
+ fixP->fx_done = 1;
+ break;
+
case BFD_RELOC_8:
if (val > 255 || val < -128)
as_warn_where (fixP->fx_file, fixP->fx_line, _("overflow"));
@@ -2022,6 +3361,15 @@ md_apply_fix (fixS * fixP, valueT* valP, segT seg ATTRIBUTE_UNUSED)
fixP->fx_done = 1;
break;
+ case BFD_RELOC_Z80_WORD1:
+ *p_lit++ = (val >> 16);
+ *p_lit++ = (val >> 24);
+ fixP->fx_no_overflow = 1;
+ if (fixP->fx_addsy == NULL)
+ fixP->fx_done = 1;
+ break;
+
+ case BFD_RELOC_Z80_WORD0:
case BFD_RELOC_16:
*p_lit++ = val;
*p_lit++ = (val >> 8);
@@ -2086,3 +3434,236 @@ tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED , fixS *fixp)
return reloc;
}
+
+int
+z80_tc_label_is_local (const char *name)
+{
+ const char *n;
+ const char *p;
+ if (local_label_prefix == NULL)
+ return 0;
+ for (p = local_label_prefix, n = name; *p && *n && *n == *p; p++, n++)
+ ;
+ return *p == '\0';
+}
+
+/* Parse floating point number from string and compute mantissa and
+ exponent. Mantissa is normalized.
+*/
+#define EXP_MIN -0x10000
+#define EXP_MAX 0x10000
+static int
+str_to_broken_float (bfd_boolean *signP, bfd_uint64_t *mantissaP, int *expP)
+{
+ char *p;
+ bfd_boolean sign;
+ bfd_uint64_t mantissa = 0;
+ int exponent = 0;
+ int i;
+
+ p = (char*)skip_space (input_line_pointer);
+ sign = (*p == '-');
+ *signP = sign;
+ if (sign || *p == '+')
+ ++p;
+ if (strncasecmp(p, "NaN", 3) == 0)
+ {
+ *mantissaP = 0;
+ *expP = 0;
+ input_line_pointer = p + 3;
+ return 1;
+ }
+ if (strncasecmp(p, "inf", 3) == 0)
+ {
+ *mantissaP = 1ull << 63;
+ *expP = EXP_MAX;
+ input_line_pointer = p + 3;
+ return 1;
+ }
+ for (; ISDIGIT(*p); ++p)
+ {
+ if (mantissa >> 60)
+ {
+ if (*p >= '5')
+ mantissa++;
+ break;
+ }
+ mantissa = mantissa * 10 + (*p - '0');
+ }
+ /* skip non-significant digits */
+ for (; ISDIGIT(*p); ++p)
+ exponent++;
+
+ if (*p == '.')
+ {
+ p++;
+ if (!exponent) /* if no precission overflow */
+ {
+ for (; ISDIGIT(*p); ++p, --exponent)
+ {
+ if (mantissa >> 60)
+ {
+ if (*p >= '5')
+ mantissa++;
+ break;
+ }
+ mantissa = mantissa * 10 + (*p - '0');
+ }
+ }
+ for (; ISDIGIT(*p); ++p)
+ ;
+ }
+ if (*p == 'e' || *p == 'E')
+ {
+ int es;
+ int t = 0;
+ ++p;
+ es = (*p == '-');
+ if (es || *p == '+')
+ p++;
+ for (; ISDIGIT(*p); ++p)
+ {
+ if (t < 100)
+ t = t * 10 + (*p - '0');
+ }
+ exponent += (es) ? -t : t;
+ }
+ if (ISALNUM(*p) || *p == '.')
+ return 0;
+ input_line_pointer = p;
+ if (mantissa == 0)
+ {
+ *mantissaP = 1ull << 63;
+ *expP = EXP_MIN;
+ return 1; /* result is 0 */
+ }
+ /* normalization */
+ for (; mantissa <= ~0ull/10; --exponent)
+ mantissa *= 10;
+ /*
+ now we have sign, mantissa, and signed decimal exponent
+ need to recompute to binary exponent
+ */
+ for (i = 64; exponent > 0; --exponent)
+ {
+ /* be sure that no integer overflow */
+ while (mantissa > ~0ull/10)
+ {
+ mantissa >>= 1;
+ i += 1;
+ }
+ mantissa *= 10;
+ }
+ for (; exponent < 0; ++exponent)
+ {
+ while (!(mantissa >> 63))
+ {
+ mantissa <<= 1;
+ i -= 1;
+ }
+ mantissa /= 10;
+ }
+ /* normalization */
+ for (; !(mantissa >> 63); --i)
+ mantissa <<= 1;
+ *mantissaP = mantissa;
+ *expP = i;
+ return 1;
+}
+
+static const char *
+str_to_zeda32(char *litP, int *sizeP)
+{
+ bfd_uint64_t mantissa;
+ bfd_boolean sign;
+ int exponent;
+ unsigned i;
+
+ *sizeP = 4;
+ if (!str_to_broken_float (&sign, &mantissa, &exponent))
+ return _("invalid syntax");
+ /* I do not know why decrement is needed */
+ --exponent;
+ /* shift by 39 bits right keeping 25 bit mantissa for rounding */
+ mantissa >>= 39;
+ /* do rounding */
+ ++mantissa;
+ /* make 24 bit mantissa */
+ mantissa >>= 1;
+ /* check for overflow */
+ if (mantissa >> 24)
+ {
+ mantissa >>= 1;
+ ++exponent;
+ }
+ /* check for 0 */
+ if (exponent < -127)
+ {
+ exponent = -128;
+ mantissa = 0;
+ }
+ else if (exponent > 127)
+ {
+ exponent = -128;
+ mantissa = sign ? 0xc00000 : 0x400000;
+ }
+ else if (mantissa == 0)
+ {
+ exponent = -128;
+ mantissa = 0x200000;
+ }
+ else if (!sign)
+ mantissa &= (1ull << 23) - 1;
+ for (i = 0; i < 24; i += 8)
+ *litP++ = (char)(mantissa >> i);
+ *litP = (char)(0x80 + exponent);
+ return NULL;
+}
+
+/*
+ Math48 by Anders Hejlsberg support.
+ Mantissa is 39 bits wide, exponent 8 bit wide.
+ Format is:
+ bit 47: sign
+ bit 46-8: normalized mantissa (bits 38-0, bit39 assumed to be 1)
+ bit 7-0: exponent+128 (0 - value is null)
+ MIN: 2.938735877e-39
+ MAX: 1.701411835e+38
+*/
+static const char *
+str_to_float48(char *litP, int *sizeP)
+{
+ bfd_uint64_t mantissa;
+ bfd_boolean sign;
+ int exponent;
+ unsigned i;
+
+ *sizeP = 6;
+ if (!str_to_broken_float (&sign, &mantissa, &exponent))
+ return _("invalid syntax");
+ /* shift by 23 bits right keeping 41 bit mantissa for rounding */
+ mantissa >>= 23;
+ /* do rounding */
+ ++mantissa;
+ /* make 40 bit mantissa */
+ mantissa >>= 1;
+ /* check for overflow */
+ if (mantissa >> 40)
+ {
+ mantissa >>= 1;
+ ++exponent;
+ }
+ if (exponent < -127)
+ {
+ memset (litP, 0, 6);
+ return NULL;
+ }
+ if (exponent > 127)
+ return _("overflow");
+ if (!sign)
+ mantissa &= (1ull << 39) - 1;
+ *litP++ = (char)(0x80 + exponent);
+ for (i = 0; i < 40; i += 8)
+ *litP++ = (char)(mantissa >> i);
+ return NULL;
+}