X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gas%2Fconfig%2Ftc-z80.c;h=28b0f2b3f528c7c5133f5b34f9edd009fa1770cb;hb=301a9420d947da145884261ac31a7a52438c2894;hp=413e336a2c89194d02ee9b335fe210dd0c50a21e;hpb=01f0fe5e0450edf168c1f612feb93cf588e4e7ea;p=deliverable%2Fbinutils-gdb.git diff --git a/gas/config/tc-z80.c b/gas/config/tc-z80.c index 413e336a2c..28b0f2b3f5 100644 --- a/gas/config/tc-z80.c +++ b/gas/config/tc-z80.c @@ -1,12 +1,12 @@ -/* tc-z80.c -- Assemble code for the Zilog Z80 and ASCII R800 - Copyright 2005 Free Software Foundation, Inc. +/* 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 This file is part of GAS, the GNU Assembler. GAS is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) + the Free Software Foundation; either version 3, or (at your option) any later version. GAS is distributed in the hope that it will be useful, @@ -20,12 +20,9 @@ 02110-1301, USA. */ #include "as.h" -#include "listing.h" -#include "bfd.h" #include "safe-ctype.h" #include "subsegs.h" -#include "symbols.h" -#include "libiberty.h" +#include "elf/z80.h" /* Exported constants. */ const char comment_chars[] = ";\0"; @@ -41,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 }, @@ -80,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, 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; @@ -129,6 +289,16 @@ md_parse_option (int c, 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; @@ -138,41 +308,151 @@ 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\ -Default: -z80 -ignore-undocument-instructions -warn-unportable-instructions.\n"); + -Fup\t\t\t treat undocumented Z80-instructions that do not work on R800 as errors\n\ +\n\ +Default: -z80 -ignore-undocumented-instructions -warn-unportable-instructions.\n"); } static symbolS * zero; +struct reg_entry +{ + const char* name; + int number; +}; +#define R_STACKABLE (0x80) +#define R_ARITH (0x40) +#define R_IX (0x20) +#define R_IY (0x10) +#define R_INDEX (R_IX | R_IY) + +#define REG_A (7) +#define REG_B (0) +#define REG_C (1) +#define REG_D (2) +#define REG_E (3) +#define REG_H (4) +#define REG_L (5) +#define REG_F (6 | 8) +#define REG_I (9) +#define REG_R (10) +#define REG_MB (11) + +#define REG_AF (3 | R_STACKABLE) +#define REG_BC (0 | R_STACKABLE | R_ARITH) +#define REG_DE (1 | R_STACKABLE | R_ARITH) +#define REG_HL (2 | R_STACKABLE | R_ARITH) +#define REG_IX (REG_HL | R_IX) +#define REG_IY (REG_HL | R_IY) +#define REG_SP (3 | R_ARITH) + +static const struct reg_entry regtable[] = +{ + {"a", REG_A }, + {"af", REG_AF }, + {"b", REG_B }, + {"bc", REG_BC }, + {"c", REG_C }, + {"d", REG_D }, + {"de", REG_DE }, + {"e", REG_E }, + {"f", REG_F }, + {"h", REG_H }, + {"hl", REG_HL }, + {"i", REG_I }, + {"ix", REG_IX }, + {"ixh",REG_H | R_IX }, + {"ixl",REG_L | R_IX }, + {"iy", REG_IY }, + {"iyh",REG_H | R_IY }, + {"iyl",REG_L | R_IY }, + {"l", REG_L }, + {"mb", REG_MB }, + {"r", REG_R }, + {"sp", REG_SP }, +} ; + +#define BUFLEN 8 /* Large enough for any keyword. */ + void md_begin (void) { - expressionS nul; + expressionS nul, reg; char * p; + unsigned int i, j, k; + char buf[BUFLEN]; + + 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; + for ( i = 0 ; i < ARRAY_SIZE ( regtable ) ; ++i ) + { + reg.X_add_number = regtable[i].number; + k = strlen ( regtable[i].name ); + buf[k] = 0; + if ( k+1 < BUFLEN ) + { + for ( j = ( 1<e_flags = elf_flags; +} +#endif + static const char * skip_space (const char *s) { @@ -240,6 +555,7 @@ z80_start_line_hook (void) *p++ = buf[2]; break; } + /* Fall through. */ case '"': for (quote = *p++; quote != *p && '\n' != *p; ++p) /* No escapes. */ ; @@ -250,27 +566,45 @@ z80_start_line_hook (void) return 1; } break; + case '#': + if (sdcc_compat) + *p = (*skip_space (p + 1) == '(') ? '+' : ' '; + break; } } /* Check for