diff options
Diffstat (limited to 'gas/config/tc-z80.c')
-rw-r--r-- | gas/config/tc-z80.c | 2487 |
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, ®); + 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, ®); + 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; +} |