diff options
author | John Darrington <john@darrington.wattle.id.au> | 2018-05-18 15:26:18 +0100 |
---|---|---|
committer | Nick Clifton <nickc@redhat.com> | 2018-05-18 15:26:18 +0100 |
commit | 7b4ae824289504c173a597e86a00ceab452095b7 (patch) | |
tree | 98efc51666beecffead172a6c29c4c1f75b14174 /gas/config/tc-s12z.c | |
parent | 011b32fd4270fb7111ee1f63695ccd44562ee7df (diff) | |
download | gdb-7b4ae824289504c173a597e86a00ceab452095b7.zip gdb-7b4ae824289504c173a597e86a00ceab452095b7.tar.gz gdb-7b4ae824289504c173a597e86a00ceab452095b7.tar.bz2 |
Add support for the Freescale s12z processor.
bfd * Makefile.am: Add s12z files.
* Makefile.in: Regenerate.
* archures.c: Add bfd_s12z_arch.
* bfd-in.h: Add exports of bfd_putb24 and bfd_putl24.
* bfd-in2.h: Regenerate.
* config.bfd: Add s12z target.
* configure.ac: Add s12z target.
* configure: Regenerate.
* cpu-s12z.c: New file.
* elf32-s12z.c: New file.
* libbfd.c (bfd_putb24): New function.
(bfd_putl24): New function.
* libbfd.h: Regenerate.
* reloc.c: Add s12z relocations.
(bfd_get_reloc_size): Handle size 5 relocs.
* targets.c: Add s12z_elf32_vec.
opcodes * Makefile.am: Add support for s12z architecture.
* configure.ac: Likewise.
* disassemble.c: Likewise.
* disassemble.h: Likewise.
* Makefile.in: Regenerate.
* configure: Regenerate.
* s12z-dis.c: New file.
* s12z.h: New file.
include * elf/s12z.h: New header.
ld * Makefile.am: Add support for s12z architecture.
* configure.tgt: Likewise.
* Makefile.in: Regenerate.
* emulparams/m9s12zelf.sh: New file.
* scripttempl/elfm9s12z.sc: New file.
* testsuite/ld-discard/static.d: Expect to fail for the s12z
target.
* testsuite/ld-elf/endsym.d: Likewise.
* testsuite/ld-elf/merge.d: Likewise.
* testsuite/ld-elf/pr14926.d: Skip for the s12z target.
* testsuite/ld-elf/sec64k.exp: Likewise.
* testsuite/ld-s12z: New directory.
* testsuite/ld-s12z/opr-linking.d: New file.
* testsuite/ld-s12z/opr-linking.s: New file.
* testsuite/ld-s12z/relative-linking.d: New file.
* testsuite/ld-s12z/relative-linking.s: New file.
* testsuite/ld-s12z/z12s.exp: New file.
gas * Makefile.am: Add support for s12z target.
* Makefile.in: Regenerate.
* NEWS: Mention the new support.
* config/tc-s12z.c: New file.
* config/tc-s12z.h: New file.
* configure.tgt: Add s12z support.
* doc/Makefile.am: Likewise.
* doc/Makefile.in: Regenerate.
* doc/all.texi: Add s12z documentation.
* doc/as.textinfo: Likewise.
* doc/c-s12z.texi: New file.
* testsuite/gas/s12z: New directory.
* testsuite/gas/s12z/abs.d: New file.
* testsuite/gas/s12z/abs.s: New file.
* testsuite/gas/s12z/adc-imm.d: New file.
* testsuite/gas/s12z/adc-imm.s: New file.
* testsuite/gas/s12z/adc-opr.d: New file.
* testsuite/gas/s12z/adc-opr.s: New file.
* testsuite/gas/s12z/add-imm.d: New file.
* testsuite/gas/s12z/add-imm.s: New file.
* testsuite/gas/s12z/add-opr.d: New file.
* testsuite/gas/s12z/add-opr.s: New file.
* testsuite/gas/s12z/and-imm.d: New file.
* testsuite/gas/s12z/and-imm.s: New file.
* testsuite/gas/s12z/and-opr.d: New file.
* testsuite/gas/s12z/and-opr.s: New file.
* testsuite/gas/s12z/and-or-cc.d: New file.
* testsuite/gas/s12z/and-or-cc.s: New file.
* testsuite/gas/s12z/bfext-special.d: New file.
* testsuite/gas/s12z/bfext-special.s: New file.
* testsuite/gas/s12z/bfext.d: New file.
* testsuite/gas/s12z/bfext.s: New file.
* testsuite/gas/s12z/bit-manip.d: New file.
* testsuite/gas/s12z/bit-manip.s: New file.
* testsuite/gas/s12z/bit.d: New file.
* testsuite/gas/s12z/bit.s: New file.
* testsuite/gas/s12z/bra-expression-defined.d: New file.
* testsuite/gas/s12z/bra-expression-defined.s: New file.
* testsuite/gas/s12z/bra-expression-undef.d: New file.
* testsuite/gas/s12z/bra-expression-undef.s: New file.
* testsuite/gas/s12z/bra.d: New file.
* testsuite/gas/s12z/bra.s: New file.
* testsuite/gas/s12z/brclr-symbols.d: New file.
* testsuite/gas/s12z/brclr-symbols.s: New file.
* testsuite/gas/s12z/brset-clr-opr-imm-rel.d: New file.
* testsuite/gas/s12z/brset-clr-opr-imm-rel.s: New file.
* testsuite/gas/s12z/brset-clr-opr-reg-rel.d: New file.
* testsuite/gas/s12z/brset-clr-opr-reg-rel.s: New file.
* testsuite/gas/s12z/brset-clr-reg-imm-rel.d: New file.
* testsuite/gas/s12z/brset-clr-reg-imm-rel.s: New file.
* testsuite/gas/s12z/brset-clr-reg-reg-rel.d: New file.
* testsuite/gas/s12z/brset-clr-reg-reg-rel.s: New file.
* testsuite/gas/s12z/clb.d: New file.
* testsuite/gas/s12z/clb.s: New file.
* testsuite/gas/s12z/clr-opr.d: New file.
* testsuite/gas/s12z/clr-opr.s: New file.
* testsuite/gas/s12z/clr.d: New file.
* testsuite/gas/s12z/clr.s: New file.
* testsuite/gas/s12z/cmp-imm.d: New file.
* testsuite/gas/s12z/cmp-imm.s: New file.
* testsuite/gas/s12z/cmp-opr-inc.d: New file.
* testsuite/gas/s12z/cmp-opr-inc.s: New file.
* testsuite/gas/s12z/cmp-opr-rdirect.d: New file.
* testsuite/gas/s12z/cmp-opr-rdirect.s: New file.
* testsuite/gas/s12z/cmp-opr-reg.d: New file.
* testsuite/gas/s12z/cmp-opr-reg.s: New file.
* testsuite/gas/s12z/cmp-opr-rindirect.d: New file.
* testsuite/gas/s12z/cmp-opr-rindirect.s: New file.
* testsuite/gas/s12z/cmp-opr-sxe4.d: New file.
* testsuite/gas/s12z/cmp-opr-sxe4.s: New file.
* testsuite/gas/s12z/cmp-opr-xys.d: New file.
* testsuite/gas/s12z/cmp-opr-xys.s: New file.
* testsuite/gas/s12z/cmp-s-imm.d: New file.
* testsuite/gas/s12z/cmp-s-imm.s: New file.
* testsuite/gas/s12z/cmp-s-opr.d: New file.
* testsuite/gas/s12z/cmp-s-opr.s: New file.
* testsuite/gas/s12z/cmp-xy.d: New file.
* testsuite/gas/s12z/cmp-xy.s: New file.
* testsuite/gas/s12z/com-opr.d: New file.
* testsuite/gas/s12z/com-opr.s: New file.
* testsuite/gas/s12z/complex-shifts.d: New file.
* testsuite/gas/s12z/complex-shifts.s: New file.
* testsuite/gas/s12z/db-tb-cc-opr.d: New file.
* testsuite/gas/s12z/db-tb-cc-opr.s: New file.
* testsuite/gas/s12z/db-tb-cc-reg.d: New file.
* testsuite/gas/s12z/db-tb-cc-reg.s: New file.
* testsuite/gas/s12z/dbCC.d: New file.
* testsuite/gas/s12z/dbCC.s: New file.
* testsuite/gas/s12z/dec-opr.d: New file.
* testsuite/gas/s12z/dec-opr.s: New file.
* testsuite/gas/s12z/dec.d: New file.
* testsuite/gas/s12z/dec.s: New file.
* testsuite/gas/s12z/div.d: New file.
* testsuite/gas/s12z/div.s: New file.
* testsuite/gas/s12z/eor.d: New file.
* testsuite/gas/s12z/eor.s: New file.
* testsuite/gas/s12z/exg.d: New file.
* testsuite/gas/s12z/exg.s: New file.
* testsuite/gas/s12z/ext24-ld-xy.d: New file.
* testsuite/gas/s12z/ext24-ld-xy.s: New file.
* testsuite/gas/s12z/inc-opr.d: New file.
* testsuite/gas/s12z/inc-opr.s: New file.
* testsuite/gas/s12z/inc.d: New file.
* testsuite/gas/s12z/inc.s: New file.
* testsuite/gas/s12z/inh.d: New file.
* testsuite/gas/s12z/inh.s: New file.
* testsuite/gas/s12z/jmp.d: New file.
* testsuite/gas/s12z/jmp.s: New file.
* testsuite/gas/s12z/jsr.d: New file.
* testsuite/gas/s12z/jsr.s: New file.
* testsuite/gas/s12z/ld-imm-page2.d: New file.
* testsuite/gas/s12z/ld-imm-page2.s: New file.
* testsuite/gas/s12z/ld-imm.d: New file.
* testsuite/gas/s12z/ld-imm.s: New file.
* testsuite/gas/s12z/ld-immu18.d: New file.
* testsuite/gas/s12z/ld-immu18.s: New file.
* testsuite/gas/s12z/ld-large-direct.d: New file.
* testsuite/gas/s12z/ld-large-direct.s: New file.
* testsuite/gas/s12z/ld-opr.d: New file.
* testsuite/gas/s12z/ld-opr.s: New file.
* testsuite/gas/s12z/ld-s-opr.d: New file.
* testsuite/gas/s12z/ld-s-opr.s: New file.
* testsuite/gas/s12z/ld-small-direct.d: New file.
* testsuite/gas/s12z/ld-small-direct.s: New file.
* testsuite/gas/s12z/lea-immu18.d: New file.
* testsuite/gas/s12z/lea-immu18.s: New file.
* testsuite/gas/s12z/lea.d: New file.
* testsuite/gas/s12z/lea.s: New file.
* testsuite/gas/s12z/mac.d: New file.
* testsuite/gas/s12z/mac.s: New file.
* testsuite/gas/s12z/min-max.d: New file.
* testsuite/gas/s12z/min-max.s: New file.
* testsuite/gas/s12z/mod.d: New file.
* testsuite/gas/s12z/mod.s: New file.
* testsuite/gas/s12z/mov.d: New file.
* testsuite/gas/s12z/mov.s: New file.
* testsuite/gas/s12z/mul-imm.d: New file.
* testsuite/gas/s12z/mul-imm.s: New file.
* testsuite/gas/s12z/mul-opr-opr.d: New file.
* testsuite/gas/s12z/mul-opr-opr.s: New file.
* testsuite/gas/s12z/mul-opr.d: New file.
* testsuite/gas/s12z/mul-opr.s: New file.
* testsuite/gas/s12z/mul-reg.d: New file.
* testsuite/gas/s12z/mul-reg.s: New file.
* testsuite/gas/s12z/mul.d: New file.
* testsuite/gas/s12z/mul.s: New file.
* testsuite/gas/s12z/neg-opr.d: New file.
* testsuite/gas/s12z/neg-opr.s: New file.
* testsuite/gas/s12z/not-so-simple-shifts.d: New file.
* testsuite/gas/s12z/not-so-simple-shifts.s: New file.
* testsuite/gas/s12z/opr-18u.d: New file.
* testsuite/gas/s12z/opr-18u.s: New file.
* testsuite/gas/s12z/opr-expr.d: New file.
* testsuite/gas/s12z/opr-expr.s: New file.
* testsuite/gas/s12z/opr-ext-18.d: New file.
* testsuite/gas/s12z/opr-ext-18.s: New file.
* testsuite/gas/s12z/opr-idx-24-reg.d: New file.
* testsuite/gas/s12z/opr-idx-24-reg.s: New file.
* testsuite/gas/s12z/opr-idx3-reg.d: New file.
* testsuite/gas/s12z/opr-idx3-reg.s: New file.
* testsuite/gas/s12z/opr-idx3-xysp-24.d: New file.
* testsuite/gas/s12z/opr-idx3-xysp-24.s: New file.
* testsuite/gas/s12z/opr-indirect-expr.d: New file.
* testsuite/gas/s12z/opr-indirect-expr.s: New file.
* testsuite/gas/s12z/opr-symbol.d: New file.
* testsuite/gas/s12z/opr-symbol.s: New file.
* testsuite/gas/s12z/or-imm.d: New file.
* testsuite/gas/s12z/or-imm.s: New file.
* testsuite/gas/s12z/or-opr.d: New file.
* testsuite/gas/s12z/or-opr.s: New file.
* testsuite/gas/s12z/p2-mul.d: New file.
* testsuite/gas/s12z/p2-mul.s: New file.
* testsuite/gas/s12z/page2-inh.d: New file.
* testsuite/gas/s12z/page2-inh.s: New file.
* testsuite/gas/s12z/psh-pul.d: New file.
* testsuite/gas/s12z/psh-pul.s: New file.
* testsuite/gas/s12z/qmul.d: New file.
* testsuite/gas/s12z/qmul.s: New file.
* testsuite/gas/s12z/rotate.d: New file.
* testsuite/gas/s12z/rotate.s: New file.
* testsuite/gas/s12z/s12z.exp: New file.
* testsuite/gas/s12z/sat.d: New file.
* testsuite/gas/s12z/sat.s: New file.
* testsuite/gas/s12z/sbc-imm.d: New file.
* testsuite/gas/s12z/sbc-imm.s: New file.
* testsuite/gas/s12z/sbc-opr.d: New file.
* testsuite/gas/s12z/sbc-opr.s: New file.
* testsuite/gas/s12z/shift.d: New file.
* testsuite/gas/s12z/shift.s: New file.
* testsuite/gas/s12z/simple-shift.d: New file.
* testsuite/gas/s12z/simple-shift.s: New file.
* testsuite/gas/s12z/single-ops.d: New file.
* testsuite/gas/s12z/single-ops.s: New file.
* testsuite/gas/s12z/specd6.d: New file.
* testsuite/gas/s12z/specd6.s: New file.
* testsuite/gas/s12z/st-large-direct.d: New file.
* testsuite/gas/s12z/st-large-direct.s: New file.
* testsuite/gas/s12z/st-opr.d: New file.
* testsuite/gas/s12z/st-opr.s: New file.
* testsuite/gas/s12z/st-s-opr.d: New file.
* testsuite/gas/s12z/st-s-opr.s: New file.
* testsuite/gas/s12z/st-small-direct.d: New file.
* testsuite/gas/s12z/st-small-direct.s: New file.
* testsuite/gas/s12z/st-xy.d: New file.
* testsuite/gas/s12z/st-xy.s: New file.
* testsuite/gas/s12z/sub-imm.d: New file.
* testsuite/gas/s12z/sub-imm.s: New file.
* testsuite/gas/s12z/sub-opr.d: New file.
* testsuite/gas/s12z/sub-opr.s: New file.
* testsuite/gas/s12z/tfr.d: New file.
* testsuite/gas/s12z/tfr.s: New file.
* testsuite/gas/s12z/trap.d: New file.
* testsuite/gas/s12z/trap.s: New file.
binutils* readelf.c: Add support for s12z architecture.
* testsuite/lib/binutils-common.exp (is_elf_format): Excluse s12z
targets.
Diffstat (limited to 'gas/config/tc-s12z.c')
-rw-r--r-- | gas/config/tc-s12z.c | 3840 |
1 files changed, 3840 insertions, 0 deletions
diff --git a/gas/config/tc-s12z.c b/gas/config/tc-s12z.c new file mode 100644 index 0000000..e024e72 --- /dev/null +++ b/gas/config/tc-s12z.c @@ -0,0 +1,3840 @@ +/* tc-s12z.c -- Assembler code for the Freescale S12Z + Copyright (C) 2018 Free Software Foundation, Inc. + + 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 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GAS; see the file COPYING. If not, write to + the Free Software Foundation, 51 Franklin Street - Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "as.h" +#include "safe-ctype.h" +#include "subsegs.h" +#include "dwarf2dbg.h" +#include "opcodes/s12z.h" +#include <stdint.h> +#include <limits.h> +#include <stdbool.h> + +const char comment_chars[] = ";"; + +const char line_comment_chars[] = "#*"; +const char line_separator_chars[] = ""; + +const char EXP_CHARS[] = "eE"; +const char FLT_CHARS[] = "dD"; + +static char *fail_line_pointer; + + +/* Options and initialization. */ + +const char *md_shortopts = "Sm:"; + +struct option md_longopts[] = + { + }; + +size_t md_longopts_size = sizeof (md_longopts); + + +relax_typeS md_relax_table[] = + { + + }; + +/* This table describes all the machine specific pseudo-ops the assembler + has to support. The fields are: + pseudo-op name without dot + function to call to execute this pseudo-op + Integer arg to pass to the function. */ +const pseudo_typeS md_pseudo_table[] = + { + {0, 0, 0} + }; + + +/* Get the target cpu for the assembler. */ +const char * +s12z_arch_format (void) +{ + return "elf32-s12z"; +} + +enum bfd_architecture +s12z_arch (void) +{ + return bfd_arch_s12z; +} + +int +s12z_mach (void) +{ + return 0; +} + +/* Listing header selected according to cpu. */ +const char * +s12z_listing_header (void) +{ + return "S12Z GAS "; +} + +void +md_show_usage (FILE *stream ATTRIBUTE_UNUSED) +{ +} + +void +s12z_print_statistics (FILE *file ATTRIBUTE_UNUSED) +{ +} + +int +md_parse_option (int c ATTRIBUTE_UNUSED, const char *arg ATTRIBUTE_UNUSED) +{ + return 0; +} + +symbolS * +md_undefined_symbol (char *name ATTRIBUTE_UNUSED) +{ + return 0; +} + +const char * +md_atof (int type, char *litP, int *sizeP) +{ + return ieee_md_atof (type, litP, sizeP, TRUE); +} + +valueT +md_section_align (asection *seg, valueT addr) +{ + int align = bfd_get_section_alignment (stdoutput, seg); + return ((addr + (1 << align) - 1) & -(1 << align)); +} + +void +md_begin (void) +{ +} + +void +s12z_init_after_args (void) +{ +} + +/* Builtin help. */ + + +static char * +skip_whites (char *p) +{ + while (*p == ' ' || *p == '\t') + p++; + + return p; +} + + + +/* Start a new insn that contains at least 'size' bytes. Record the + line information of that insn in the dwarf2 debug sections. */ +static char * +s12z_new_insn (int size) +{ + char *f = frag_more (size); + + dwarf2_emit_insn (size); + + return f; +} + + + +static int lex_reg_name (uint16_t which, int *reg); + +static int +lex_constant (long *v) +{ + char *end = NULL; + char *p = input_line_pointer; + + /* A constant may not have the same value as a register + eg: "d6" */ + int dummy; + if (lex_reg_name (~0, &dummy)) + { + input_line_pointer = p; + return 0; + } + + errno = 0; + *v = strtol (p, &end, 0); + if (errno == 0 && end != p) + { + input_line_pointer = end; + return 1; + } + + return 0; +} + +static int +lex_match (char x) +{ + char *p = input_line_pointer; + if (*p != x) + return 0; + + input_line_pointer++; + return 1; +} + + +static int +lex_expression (expressionS *exp) +{ + char *ilp = input_line_pointer; + int dummy; + exp->X_op = O_absent; + + if (lex_match ('#')) + goto fail; + + if (lex_reg_name (~0, &dummy)) + goto fail; + + expression (exp); + if (exp->X_op != O_absent) + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + +/* immediate operand */ +static int +lex_imm (long *v) +{ + char *ilp = input_line_pointer; + + if (*input_line_pointer != '#') + goto fail; + + input_line_pointer++; + expressionS exp; + if (!lex_expression (&exp)) + goto fail; + + if (exp.X_op != O_constant) + goto fail; + + *v = exp.X_add_number; + return 1; + +fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + +/* Short mmediate operand */ +static int +lex_imm_e4 (long *val) +{ + char *ilp = input_line_pointer; + if ((lex_imm (val))) + { + if ((*val == -1) || (*val > 0 && *val <= 15)) + { + return 1; + } + } + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + +static int +lex_match_string (const char *s) +{ + char *p = input_line_pointer; + while (p != 0 && *p != '\t' && *p != ' ' && *p != '\0') + { + p++; + } + + size_t len = p - input_line_pointer; + if (len != strlen (s)) + return 0; + + if (0 == strncasecmp (s, input_line_pointer, len)) + { + input_line_pointer = p; + return 1; + } + + return 0; +} + +/* Parse a register name. + WHICH is a ORwise combination of the registers which are accepted. + ~0 accepts all. + On success, REG will be filled with the index of the register which + was successfully scanned. +*/ +static int +lex_reg_name (uint16_t which, int *reg) +{ + char *p = input_line_pointer; + while (p != 0 && + ((*p >= 'a' && *p <='z') || (*p >= '0' && *p <= '9') || (*p >= 'A' && *p <='Z'))) + { + p++; + } + + int len = p - input_line_pointer; + + if (len <= 0) + return 0; + + int i; + for (i = 0; i < S12Z_N_REGISTERS; ++i) + { + gas_assert (registers[i].name); + + if (0 == strncasecmp (registers[i].name, input_line_pointer, len)) + { + if ((0x1U << i) & which) + { + input_line_pointer = p; + *reg = i; + return 1; + } + } + } + + return 0; +} + +static int +lex_force_match (char x) +{ + char *p = input_line_pointer; + if (*p != x) + { + as_bad (_("Expecting '%c'"), x); + return 0; + } + + input_line_pointer++; + return 1; +} + +static int +lex_opr (uint8_t *buffer, int *n_bytes, expressionS *exp) +{ + char *ilp = input_line_pointer; + uint8_t *xb = buffer; + int reg; + long imm; + exp->X_op = O_absent; + *n_bytes = 0; + *xb = 0; + if (lex_imm_e4 (&imm)) + { + if (imm > 0) + *xb = imm; + else + *xb = 0; + *xb |= 0x70; + *n_bytes = 1; + return 1; + } + else if (lex_reg_name (REG_BIT_Dn, ®)) + { + *xb = reg; + *xb |= 0xb8; + *n_bytes = 1; + return 1; + } + else if (lex_match ('[')) + { + if (lex_expression (exp)) + { + long c = exp->X_add_number; + if (lex_match (',')) + { + if (lex_reg_name (REG_BIT_XYSP, ®)) + { + int i; + if (c <= 255 && c >= -256) + { + *n_bytes = 2; + *xb |= 0xc4; + } + else + { + *n_bytes = 4; + *xb |= 0xc6; + } + *xb |= (reg - REG_X) << 4; + + if (c < 0) + *xb |= 0x01; + for (i = 1; i < *n_bytes ; ++i) + { + buffer[i] = c >> (8 * (*n_bytes - i - 1)); + } + } + else + { + as_bad (_("Bad operand for constant offset")); + goto fail; + } + } + else + { + *xb = 0xfe; + *n_bytes = 4; + buffer[1] = c >> 16; + buffer[2] = c >> 8; + buffer[3] = c; + } + } + else if (lex_reg_name (REG_BIT_Dn, ®)) + { + if (!lex_force_match (',')) + goto fail; + + int reg2; + if (lex_reg_name (REG_BIT_XY, ®2)) + { + *n_bytes = 1; + *xb = reg; + *xb |= (reg2 - REG_X) << 4; + *xb |= 0xc8; + } + else + { + as_bad (_("Invalid operand for register offset")); + goto fail; + } + } + else + { + goto fail; + } + if (!lex_force_match (']')) + goto fail; + return 1; + } + else if (lex_match ('(')) + { + long c; + if (lex_constant (&c)) + { + if (!lex_force_match (',')) + goto fail; + int reg2; + if (lex_reg_name (REG_BIT_XYSP, ®2)) + { + if (reg2 != REG_P && c >= 0 && c <= 15) + { + *n_bytes = 1; + *xb = 0x40; + *xb |= (reg2 - REG_X) << 4; + *xb |= c; + } + else if (c >= -256 && c <= 255) + { + *n_bytes = 2; + *xb = 0xc0; + *xb |= (reg2 - REG_X) << 4; + if (c < 0) + *xb |= 0x01; + buffer[1] = c; + } + else + { + *n_bytes = 4; + *xb = 0xc2; + *xb |= (reg2 - REG_X) << 4; + buffer[1] = c >> 16; + buffer[2] = c >> 8; + buffer[3] = c; + } + } + else if (lex_reg_name (REG_BIT_Dn, ®2)) + { + if (c >= -1 * (long) (0x1u << 17) + && + c < (long) (0x1u << 17) - 1) + { + *n_bytes = 3; + *xb = 0x80; + *xb |= reg2; + *xb |= ((c >> 16) & 0x03) << 4; + buffer[1] = c >> 8; + buffer[2] = c; + } + else + { + *n_bytes = 4; + *xb = 0xe8; + *xb |= reg2; + buffer[1] = c >> 16; + buffer[2] = c >> 8; + buffer[3] = c; + } + } + else + { + as_bad (_("Bad operand for constant offset")); + goto fail; + } + } + else if (lex_reg_name (REG_BIT_Dn, ®)) + { + if (lex_match (',')) + { + int reg2; + if (lex_reg_name (REG_BIT_XYS, ®2)) + { + *n_bytes = 1; + *xb = 0x88; + *xb |= (reg2 - REG_X) << 4; + *xb |= reg; + } + else + { + as_bad (_("Invalid operand for register offset")); + goto fail; + } + } + else + { + goto fail; + } + } + else if (lex_reg_name (REG_BIT_XYS, ®)) + { + if (lex_match ('-')) + { + if (reg == REG_S) + { + as_bad (_("Invalid register for postdecrement operation")); + goto fail; + } + *n_bytes = 1; + if (reg == REG_X) + *xb = 0xc7; + else if (reg == REG_Y) + *xb = 0xd7; + } + else if (lex_match ('+')) + { + *n_bytes = 1; + if (reg == REG_X) + *xb = 0xe7; + else if (reg == REG_Y) + *xb = 0xf7; + else if (reg == REG_S) + *xb = 0xff; + } + else + { + goto fail; + } + } + else if (lex_match ('+')) + { + if (lex_reg_name (REG_BIT_XY, ®)) + { + *n_bytes = 1; + if (reg == REG_X) + *xb = 0xe3; + else if (reg == REG_Y) + *xb = 0xf3; + } + else + { + as_bad (_("Invalid register for preincrement operation")); + goto fail; + } + } + else if (lex_match ('-')) + { + if (lex_reg_name (REG_BIT_XYS, ®)) + { + *n_bytes = 1; + if (reg == REG_X) + *xb = 0xc3; + else if (reg == REG_Y) + *xb = 0xd3; + else if (reg == REG_S) + *xb = 0xfb; + } + else + { + as_bad (_("Invalid register for predecrement operation")); + goto fail; + } + } + else + { + goto fail; + } + + if (! lex_match (')')) + goto fail; + return 1; + } + else if (lex_expression (exp)) + { + *xb = 0xfa; + *n_bytes = 4; + buffer[1] = 0; + buffer[2] = 0; + buffer[3] = 0; + if (exp->X_op == O_constant) + { + if (exp->X_add_number < (0x1U << 14)) + { + *xb = 0x00; + *n_bytes = 2; + *xb |= exp->X_add_number >> 8; + buffer[1] = exp->X_add_number; + } + else if (exp->X_add_number < (0x1U << 19)) + { + *xb = 0xf8; + if (exp->X_add_number & (0x1U << 17)) + *xb |= 0x04; + if (exp->X_add_number & (0x1U << 16)) + *xb |= 0x01; + *n_bytes = 3; + buffer[1] = exp->X_add_number >> 8; + buffer[2] = exp->X_add_number; + } + else + { + *xb = 0xfa; + *n_bytes = 4; + buffer[1] = exp->X_add_number >> 16; + buffer[2] = exp->X_add_number >> 8; + buffer[3] = exp->X_add_number; + } + } + return 1; + } + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + +static int +lex_offset (long *val) +{ + char *end = NULL; + char *p = input_line_pointer; + + if (*p++ != '*') + return 0; + + if (*p != '+' && *p != '-') + return 0; + + bool negative = (*p == '-'); + p++; + + errno = 0; + *val = strtol (p, &end, 0); + if (errno == 0) + { + if (negative) + *val *= -1; + input_line_pointer = end; + return 1; + } + + return 0; +} + + + +struct instruction; + +typedef int (*parse_operand_func) (const struct instruction *); + +struct instruction +{ + const char *name; + + /* The "page" to which the instruction belongs. + This is also only a hint. Some instructions might have modes in both + pages... */ + char page; + + /* This is a hint - and only a hint - about the opcode of the instruction. + The parse_operand_func is free to ignore it. + */ + uint8_t opc; + + parse_operand_func parse_operands; + + /* Some instructions can be encoded with a different opcode */ + uint8_t alt_opc; +}; + +static int +no_operands (const struct instruction *insn) +{ + if (*input_line_pointer != '\0') + { + as_bad (_("Garbage at end of instruction")); + return 0; + } + + char *f = s12z_new_insn (insn->page); + if (insn->page == 2) + number_to_chars_bigendian (f++, PAGE2_PREBYTE, 1); + + number_to_chars_bigendian (f++, insn->opc, 1); + + return 1; +} + +/* Emit the code for an OPR address mode operand */ +static char * +emit_opr (char *f, const uint8_t *buffer, int n_bytes, expressionS *exp) +{ + int i; + number_to_chars_bigendian (f++, buffer[0], 1); + if (exp->X_op != O_absent && exp->X_op != O_constant) + { + fix_new_exp (frag_now, + f - frag_now->fr_literal, + 3, + exp, + FALSE, + BFD_RELOC_24); + } + for (i = 1; i < n_bytes; ++i) + number_to_chars_bigendian (f++, buffer[i], 1); + + return f; +} + +/* Emit the code for a 24 bit direct address operand */ +static char * +emit_ext24 (char *f, long v) +{ + number_to_chars_bigendian (f, v, 3); + + return f + 3; +} + +static int +opr (const struct instruction *insn) +{ + uint8_t buffer[4]; + int n_bytes; + expressionS exp; + if (lex_opr (buffer, &n_bytes, &exp)) + { + /* Large constant direct values are more efficiently encoded as ext24 mode. + Otherwise a decision has to be deferred to a relax. */ + if (exp.X_op == O_constant + && buffer[0] == 0xFA + && insn->alt_opc != 0) + { + char *f = s12z_new_insn (4); + + /* I don't think there are any instances of page 2 opcodes in this case */ + gas_assert (insn->page == 1); + + number_to_chars_bigendian (f++, insn->alt_opc, 1); + + emit_ext24 (f, exp.X_add_number); + } + else + { + char *f = s12z_new_insn (n_bytes + 1); + number_to_chars_bigendian (f++, insn->opc, 1); + + emit_opr (f, buffer, n_bytes, &exp); + } + return 1; + } + + return 0; +} + +/* Parse a 15 bit offset, as an expression. + LONG_DISPLACEMENT will be set to true if the offset is wider than 7 bits. + */ +static int +lex_15_bit_offset (bool *long_displacement, expressionS *exp) +{ + char *ilp = input_line_pointer; + + long val; + if (lex_offset (&val)) + { + exp->X_op = O_absent; + exp->X_add_number = val; + } + else if (lex_expression (exp)) + { + if (exp->X_op == O_constant) + { + val = exp->X_add_number; + } + else + { + /* If a symbol was parsed we don't know the displacement. + We have to assume it is long, and relax it later if possible. */ + *long_displacement = true; + return 1; + } + } + else + { + exp->X_op = O_absent; + goto fail; + } + + if (val > 0x3FFF || val < -0x4000) + { + as_fatal (_("Offset is outside of 15 bit range")); + return 0; + } + + *long_displacement = (val > 63 || val < -64); + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + +static void +emit_15_bit_offset (char *f, int where, expressionS *exp) +{ + gas_assert (exp); + if (exp->X_op != O_absent && exp->X_op != O_constant) + { + exp->X_add_number += where; + fixS *fix = fix_new_exp (frag_now, + f - frag_now->fr_literal, + 2, + exp, + TRUE, + BFD_RELOC_16_PCREL); + fix->fx_addnumber = where - 2; + } + else + { + long val = exp->X_add_number; + bool long_displacement = (val > 63 || val < -64); + if (long_displacement) + val |= 0x8000; + else + val &= 0x7F; + + number_to_chars_bigendian (f++, val, long_displacement ? 2 : 1); + } +} + +static int +rel (const struct instruction *insn) +{ + bool long_displacement; + + expressionS exp; + if (! lex_15_bit_offset (&long_displacement, &exp)) + return 0; + + char *f = s12z_new_insn (long_displacement ? 3 : 2); + number_to_chars_bigendian (f++, insn->opc, 1); + emit_15_bit_offset (f, 3, &exp); + return 1; +} + +static int +reg_inh (const struct instruction *insn) +{ + int reg; + if (lex_reg_name (REG_BIT_Dn, ®)) + { + char *f = s12z_new_insn (insn->page); + if (insn->page == 2) + number_to_chars_bigendian (f++, PAGE2_PREBYTE, 1); + + number_to_chars_bigendian (f++, insn->opc + reg, 1); + return 1; + } + + return 0; +} + + +/* Special case for CLR X and CLR Y */ +static int +clr_xy (const struct instruction *insn ATTRIBUTE_UNUSED) +{ + int reg; + if (lex_reg_name (REG_BIT_XY, ®)) + { + char *f = s12z_new_insn (1); + number_to_chars_bigendian (f, 0x9a + reg - REG_X, 1); + return 1; + } + + return 0; +} + +/* Some instructions have a suffix like ".l", ".b", ".w" etc + which indicates the size of the operands. */ +static int +size_from_suffix (const struct instruction *insn, int idx) +{ + const char *dot = strchr (insn->name, '.'); + + if (dot == NULL) + return -3; + + int size = -2; + switch (dot[1 + idx]) + { + case 'b': + size = 1; + break; + case 'w': + size = 2; + break; + case 'p': + size = 3; + break; + case 'l': + size = 4; + break; + default: + as_fatal (_("Bad size")); + }; + + return size; +} + +static int +mul_reg_reg_reg (const struct instruction *insn) +{ + char *ilp = input_line_pointer; + + int Dd; + if (!lex_reg_name (REG_BIT_Dn, &Dd)) + goto fail; + + if (!lex_match (',')) + goto fail; + + int Dj; + if (!lex_reg_name (REG_BIT_Dn, &Dj)) + goto fail; + + if (!lex_match (',')) + goto fail; + + int Dk; + if (!lex_reg_name (REG_BIT_Dn, &Dk)) + goto fail; + + char *f = s12z_new_insn (insn->page + 1); + if (insn->page == 2) + number_to_chars_bigendian (f++, PAGE2_PREBYTE, 1); + + number_to_chars_bigendian (f++, insn->opc + Dd, 1); + const char *dot = strchrnul (insn->name, '.'); + uint8_t mb ; + switch (dot[-1]) + { + case 's': + mb = 0x80; + break; + case 'u': + mb = 0x00; + break; + default: + as_fatal (_("BAD MUL")); + break; + } + + mb |= Dj << 3; + mb |= Dk; + + number_to_chars_bigendian (f++, mb, 1); + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + + +static int +mul_reg_reg_imm (const struct instruction *insn) +{ + char *ilp = input_line_pointer; + + int Dd; + if (!lex_reg_name (REG_BIT_Dn, &Dd)) + goto fail; + + if (!lex_match (',')) + goto fail; + + int Dj; + if (!lex_reg_name (REG_BIT_Dn, &Dj)) + goto fail; + + if (!lex_match (',')) + goto fail; + + long imm; + if (!lex_imm (&imm)) + goto fail; + + + int size = size_from_suffix (insn, 0); + + char *f = s12z_new_insn (insn->page + 1 + size); + if (insn->page == 2) + number_to_chars_bigendian (f++, PAGE2_PREBYTE, 1); + + number_to_chars_bigendian (f++, insn->opc + Dd, 1); + uint8_t mb = 0x44; + const char *dot = strchrnul (insn->name, '.'); + switch (dot[-1]) + { + case 's': + mb |= 0x80; + break; + case 'u': + mb |= 0x00; + break; + default: + as_fatal (_("BAD MUL")); + break; + } + + mb |= Dj << 3; + mb |= size - 1; + + number_to_chars_bigendian (f++, mb, 1); + number_to_chars_bigendian (f++, imm, size); + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + + +static int +mul_reg_reg_opr (const struct instruction *insn) +{ + char *ilp = input_line_pointer; + + int Dd; + if (!lex_reg_name (REG_BIT_Dn, &Dd)) + goto fail; + + if (!lex_match (',')) + goto fail; + + int Dj; + if (!lex_reg_name (REG_BIT_Dn, &Dj)) + goto fail; + + if (!lex_match (',')) + goto fail; + + uint8_t buffer[4]; + int n_bytes; + expressionS exp; + if (!lex_opr (buffer, &n_bytes, &exp)) + goto fail; + + int size = size_from_suffix (insn, 0); + + char *f = s12z_new_insn (insn->page + 1 + n_bytes); + if (insn->page == 2) + number_to_chars_bigendian (f++, PAGE2_PREBYTE, 1); + + number_to_chars_bigendian (f++, insn->opc + Dd, 1); + uint8_t mb = 0x40; + const char *dot = strchrnul (insn->name, '.'); + switch (dot[-1]) + { + case 's': + mb |= 0x80; + break; + case 'u': + mb |= 0x00; + break; + default: + as_fatal (_("BAD MUL")); + break; + } + + mb |= Dj << 3; + mb |= size - 1; + + number_to_chars_bigendian (f++, mb, 1); + + emit_opr (f, buffer, n_bytes, &exp); + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + +static int +mul_reg_opr_opr (const struct instruction *insn) +{ + char *ilp = input_line_pointer; + + int Dd; + if (!lex_reg_name (REG_BIT_Dn, &Dd)) + goto fail; + + if (!lex_match (',')) + goto fail; + + uint8_t buffer1[4]; + int n_bytes1; + expressionS exp1; + if (!lex_opr (buffer1, &n_bytes1, &exp1)) + goto fail; + + if (!lex_match (',')) + goto fail; + + uint8_t buffer2[4]; + int n_bytes2; + expressionS exp2; + if (!lex_opr (buffer2, &n_bytes2, &exp2)) + goto fail; + + int size1 = size_from_suffix (insn, 0); + int size2 = size_from_suffix (insn, 1); + + char *f = s12z_new_insn (insn->page + 1 + n_bytes1 + n_bytes2); + if (insn->page == 2) + number_to_chars_bigendian (f++, PAGE2_PREBYTE, 1); + + number_to_chars_bigendian (f++, insn->opc + Dd, 1); + uint8_t mb = 0x42; + const char *dot = strchrnul (insn->name, '.'); + switch (dot[-1]) + { + case 's': + mb |= 0x80; + break; + case 'u': + mb |= 0x00; + break; + default: + as_fatal (_("BAD MUL")); + break; + } + + mb |= (size1 - 1) << 4; + mb |= (size2 - 1) << 2; + number_to_chars_bigendian (f++, mb, 1); + + f = emit_opr (f, buffer1, n_bytes1, &exp1); + f = emit_opr (f, buffer2, n_bytes2, &exp2); + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + + +#define REG_BIT_GRP0 \ + ((0x1U << REG_D2) | \ + (0x1U << REG_D3) | \ + (0x1U << REG_CCH) | \ + (0x1U << REG_CCL) | \ + (0x1U << REG_D0) | \ + (0x1U << REG_D1)) + +#define REG_BIT_GRP1 \ + ((0x1U << REG_D4) | \ + (0x1U << REG_D5) | \ + (0x1U << REG_D6) | \ + (0x1U << REG_D7) | \ + (0x1U << REG_X) | \ + (0x1U << REG_Y)) + +static const uint8_t reg_map [] = + { + 0x02, // D2 + 0x01, // D3 + 0x20, + 0x10, // D5 + 0x08, // D0 + 0x04, // D1 + 0x08, // D6 + 0x04, // D7 + 0x02, + 0x01, // Y + 0x00, + 0x00, + 0x20, // CCH + 0x10, // CCL + 0x00 + }; + +static int +lex_reg_list (uint16_t grp, uint16_t *reg_bits) +{ + if (lex_match (',')) + { + int reg; + if (!lex_reg_name (grp, ®)) + return 0; + *reg_bits |= 0x1u << reg; + lex_reg_list (grp, reg_bits); + } + + /* Empty list */ + return 1; +} + +static int +psh_pull (const struct instruction *insn) +{ + uint8_t pb = + (0 == strcmp ("pul", insn->name)) ? 0x80: 0x00; + + if (lex_match_string ("all16b")) + { + pb |= 0x40; + } + else if (lex_match_string ("all")) + { + /* Nothing to do */ + } + else + { + int reg1; + if (!lex_reg_name (REG_BIT_GRP1 | REG_BIT_GRP0, ®1)) + goto fail; + uint16_t admitted_group = 0; + + if ((0x1U << reg1) & REG_BIT_GRP1) + admitted_group = REG_BIT_GRP1; + else if ((0x1U << reg1) & REG_BIT_GRP0) + admitted_group = REG_BIT_GRP0; + + uint16_t reg_bits = 0x1 << reg1; + if (!lex_reg_list (admitted_group, ®_bits)) + goto fail; + + if (reg_bits & REG_BIT_GRP1) + pb |= 0x40; + + int i; + for (i = 0; i < 16; ++i) + { + if (reg_bits & (0x1u << i)) + pb |= reg_map[i]; + } + } + + char *f = s12z_new_insn (2); + number_to_chars_bigendian (f++, insn->opc, 1); + number_to_chars_bigendian (f++, pb, 1); + return 1; + + fail: + fail_line_pointer = input_line_pointer; + return 0; +} + + +static int +tfr (const struct instruction *insn) +{ + int reg1; + if (!lex_reg_name (~0, ®1)) + goto fail; + + if (!lex_match (',')) + goto fail; + + int reg2; + if (!lex_reg_name (~0, ®2)) + goto fail; + + if ((0 == strcasecmp ("sex", insn->name)) + || (0 == strcasecmp ("zex", insn->name))) + { + if (registers[reg1].bytes >= registers[reg2].bytes) + { + as_bad (_("Source register for %s must be smaller that the destination register"), + insn->name); + goto fail; + } + } + + char *f = s12z_new_insn (1 + insn->page); + if (insn->page == 2) + number_to_chars_bigendian (f++, PAGE2_PREBYTE, 1); + + number_to_chars_bigendian (f++, insn->opc, 1); + number_to_chars_bigendian (f++, reg1 << 4 | reg2, 1); + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + return 0; +} + +static int +imm8 (const struct instruction *insn) +{ + long imm; + if (! lex_imm (&imm)) + return 0; + if (imm > 127 || imm < -128) + { + as_bad (_("Immediate value %ld is out of range for instruction %s"), + imm, insn->name); + } + + char *f = s12z_new_insn (2); + number_to_chars_bigendian (f++, insn->opc, 1); + number_to_chars_bigendian (f++, imm, 1); + + return 1; +} + +static int +reg_imm (const struct instruction *insn, int allowed_reg) +{ + char *ilp = input_line_pointer; + int reg; + if (lex_reg_name (allowed_reg, ®)) + { + if (!lex_force_match (',')) + goto fail; + long imm; + if (! lex_imm (&imm)) + goto fail; + + short size = registers[reg].bytes; + char *f = s12z_new_insn (insn->page + size); + if (insn->page == 2) + number_to_chars_bigendian (f++, PAGE2_PREBYTE, 1); + + number_to_chars_bigendian (f++, insn->opc + reg, 1); + number_to_chars_bigendian (f++, imm, size); + return 1; + } + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + + +static int +regd_imm (const struct instruction *insn) +{ + return reg_imm (insn, REG_BIT_Dn); +} + +static int +regdxy_imm (const struct instruction *insn) +{ + return reg_imm (insn, REG_BIT_Dn | REG_BIT_XY); +} + + +static int +regs_imm (const struct instruction *insn) +{ + return reg_imm (insn, 0x1U << REG_S); +} + +static int +trap_imm (const struct instruction *insn ATTRIBUTE_UNUSED) +{ + long imm = -1; + if (! lex_imm (&imm)) + goto fail; + + if (imm < 0x92 || imm > 0xFF || + (imm >= 0xA0 && imm <= 0xA7) || + (imm >= 0xB0 && imm <= 0xB7)) + { + as_bad (_("trap value %ld is not valid"), imm); + return 0; + } + else + { + char *f = s12z_new_insn (2); + number_to_chars_bigendian (f++, PAGE2_PREBYTE, 1); + number_to_chars_bigendian (f++, imm & 0xFF, 1); + return 1; + } + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + return 0; +} + + + +/* Special one byte instruction CMP X, Y */ +static int +regx_regy (const struct instruction *insn) +{ + int reg; + if (lex_reg_name (0x1U << REG_X, ®)) + { + if (lex_force_match (',')) + { + if (lex_reg_name (0x1U << REG_Y, ®)) + { + char *f = s12z_new_insn (1); + number_to_chars_bigendian (f, insn->opc, 1); + return 1; + } + } + } + return 0; +} + +/* Special one byte instruction SUB D6, X, Y */ +static int +regd6_regx_regy (const struct instruction *insn) +{ + char *ilp = input_line_pointer; + int reg; + if (!lex_reg_name (0x1U << REG_D6, ®)) + goto fail; + + if (!lex_match (',')) + goto fail; + + if (!lex_reg_name (0x1U << REG_X, ®)) + goto fail; + + if (!lex_match (',')) + goto fail; + + if (!lex_reg_name (0x1U << REG_Y, ®)) + goto fail; + + char *f = s12z_new_insn (1); + number_to_chars_bigendian (f, insn->opc, 1); + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + +/* Special one byte instruction SUB D6, Y, X */ +static int +regd6_regy_regx (const struct instruction *insn) +{ + char *ilp = input_line_pointer; + int reg; + if (!lex_reg_name (0x1U << REG_D6, ®)) + goto fail; + + if (!lex_match (',')) + goto fail; + + if (!lex_reg_name (0x1U << REG_Y, ®)) + goto fail; + + if (!lex_match (',')) + goto fail; + + if (!lex_reg_name (0x1U << REG_X, ®)) + goto fail; + + char *f = s12z_new_insn (1); + number_to_chars_bigendian (f, insn->opc, 1); + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + +static int +reg_opr (const struct instruction *insn, int allowed_regs) +{ + char *ilp = input_line_pointer; + int reg; + if (lex_reg_name (allowed_regs, ®)) + { + if (!lex_force_match (',')) + goto fail; + + uint8_t buffer[4]; + int n_bytes; + expressionS exp; + if (lex_opr (buffer, &n_bytes, &exp)) + { + /* Large constant direct values are more efficiently encoded as ext24 mode. + Otherwise a decision has to be deferred to a relax. */ + if (exp.X_op == O_constant + && buffer[0] == 0xFA + && insn->alt_opc != 0) + { + char *f = s12z_new_insn (4); + + /* I don't think there are any instances of page 2 opcodes in this case */ + gas_assert (insn->page == 1); + + number_to_chars_bigendian (f++, insn->alt_opc + reg, 1); + + emit_ext24 (f, exp.X_add_number); + } + else + { + char *f = s12z_new_insn (n_bytes + insn->page); + + if (insn->page == 2) + number_to_chars_bigendian (f++, PAGE2_PREBYTE, 1); + + number_to_chars_bigendian (f++, insn->opc + reg, 1); + + emit_opr (f, buffer, n_bytes, &exp); + } + + return 1; + } + } + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + + +static int +regdxy_opr (const struct instruction *insn) +{ + return reg_opr (insn, REG_BIT_Dn | REG_BIT_XY); +} + +static int +regd_opr (const struct instruction *insn) +{ + return reg_opr (insn, REG_BIT_Dn); +} + + +static int +regs_opr (const struct instruction *insn) +{ + return reg_opr (insn, 0x1U << REG_S); +} + +static int +imm_opr (const struct instruction *insn) +{ + char *ilp = input_line_pointer; + long imm; + if (!lex_imm (&imm)) + goto fail; + + if (!lex_match (',')) + goto fail; + + uint8_t buffer[4]; + int n_bytes; + expressionS exp; + if (!lex_opr (buffer, &n_bytes, &exp)) + goto fail; + + int size = size_from_suffix (insn, 0); + char *f = s12z_new_insn (1 + n_bytes + size); + number_to_chars_bigendian (f++, insn->opc, 1); + + int i; + for (i = 0; i < size; ++i) + number_to_chars_bigendian (f++, imm >> (CHAR_BIT * (size - i - 1)), 1); + + emit_opr (f, buffer, n_bytes, &exp); + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + +static int +opr_opr (const struct instruction *insn) +{ + char *ilp = input_line_pointer; + + uint8_t buffer1[4]; + int n_bytes1; + expressionS exp1; + if (!lex_opr (buffer1, &n_bytes1, &exp1)) + goto fail; + + + if (!lex_match (',')) + goto fail; + + uint8_t buffer2[4]; + int n_bytes2; + expressionS exp2; + if (!lex_opr (buffer2, &n_bytes2, &exp2)) + goto fail; + + char *f = s12z_new_insn (1 + n_bytes1 + n_bytes2); + number_to_chars_bigendian (f++, insn->opc, 1); + + f = emit_opr (f, buffer1, n_bytes1, &exp1); + f = emit_opr (f, buffer2, n_bytes2, &exp2); + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + +static int +reg67sxy_opr (const struct instruction *insn) +{ + int reg; + if (!lex_reg_name (REG_BIT_XYS | (0x1U << REG_D6) | (0x1U << REG_D7), ®)) + return 0; + + if (!lex_match (',')) + return 0; + + uint8_t buffer[4]; + int n_bytes; + expressionS exp; + if (!lex_opr (buffer, &n_bytes, &exp)) + return 0; + + char *f = s12z_new_insn (1 + n_bytes); + number_to_chars_bigendian (f++, insn->opc + reg - REG_D6, 1); + emit_opr (f, buffer, n_bytes, &exp); + + return 1; +} + +static int +rotate (const struct instruction *insn, short dir) +{ + uint8_t buffer[4]; + int n_bytes; + expressionS exp; + if (lex_opr (buffer, &n_bytes, &exp)) + { + char *f = s12z_new_insn (n_bytes + 2); + number_to_chars_bigendian (f++, insn->opc, 1); + int size = size_from_suffix (insn, 0); + if (size < 0) + size = 1; + uint8_t sb = 0x24; + sb |= size - 1; + if (dir) + sb |= 0x40; + number_to_chars_bigendian (f++, sb, 1); + emit_opr (f, buffer, n_bytes, &exp); + + return 1; + } + + return 0; +} + +static int +rol (const struct instruction *insn) +{ + return rotate (insn, 1); +} + +static int +ror (const struct instruction *insn) +{ + return rotate (insn, 0); +} + + +/* Shift instruction with a register operand and an immediate #1 or #2 + left = 1; right = 0; + logical = 0; arithmetic = 1; +*/ +static int +lex_shift_reg_imm1 (const struct instruction *insn, short type, short dir) +{ + /* + This function is highly unusual and a bit wierd! + It first matches the input against a register {d0, d1, ... d7} followed by an immediate + {#1, #2}. + Then, it rewinds the input and parses it again as a OPR. + */ + char *ilp = input_line_pointer; + + int Dd; + if (!lex_reg_name (REG_BIT_Dn, &Dd)) + { + goto fail; + } + + if (!lex_match (',')) + goto fail; + + long imm = -1; + if (!lex_imm (&imm)) + goto fail; + + if (imm != 1 && imm != 2) + goto fail; + input_line_pointer = ilp; + + /* Now parse the first operand again */ + + uint8_t buffer[4]; + int n_bytes; + + expressionS exp; + if (!lex_opr (buffer, &n_bytes, &exp)) + goto fail; + + gas_assert (n_bytes == 1); + + uint8_t sb = 0x34; + sb |= dir << 6; + sb |= type << 7; + if (imm == 2) + sb |= 0x08; + + char *f = s12z_new_insn (3); + number_to_chars_bigendian (f++, insn->opc, 1); + number_to_chars_bigendian (f++, sb, 1); + emit_opr (f, buffer, n_bytes, &exp); + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + +/* Shift instruction with a register operand. + left = 1; right = 0; + logical = 0; arithmetic = 1; */ +static int +lex_shift_reg (const struct instruction *insn, short type, short dir) +{ + int Dd, Ds, Dn; + if (!lex_reg_name (REG_BIT_Dn, &Dd)) + { + goto fail; + } + + if (!lex_match (',')) + goto fail; + + if (!lex_reg_name (REG_BIT_Dn, &Ds)) + { + goto fail; + } + + if (!lex_match (',')) + goto fail; + + uint8_t sb = 0x10; + sb |= Ds; + sb |= dir << 6; + sb |= type << 7; + long imm; + if (lex_reg_name (REG_BIT_Dn, &Dn)) + { + char *f = s12z_new_insn (3); + number_to_chars_bigendian (f++, insn->opc | Dd, 1); + number_to_chars_bigendian (f++, sb, 1); + uint8_t xb = 0xb8; + xb |= Dn; + number_to_chars_bigendian (f++, xb, 1); + + return 1; + } + else if (lex_imm (&imm)) + { + if (imm < 0 || imm > 31) + { + as_bad (_("Shift value should be in the range [0,31]")); + goto fail; + } + + int n_bytes = 3; + if (imm == 1 || imm == 2) + { + n_bytes = 2; + sb &= ~0x10; + } + else + { + sb |= (imm & 0x01) << 3; + } + + char *f = s12z_new_insn (n_bytes); + number_to_chars_bigendian (f++, insn->opc | Dd, 1); + number_to_chars_bigendian (f++, sb, 1); + if (n_bytes > 2) + { + uint8_t xb = 0x70; + xb |= imm >> 1; + number_to_chars_bigendian (f++, xb, 1); + } + + return 1; + } + + fail: + fail_line_pointer = input_line_pointer; + return 0; +} + +static void +impute_shift_dir_and_type (const struct instruction *insn, short *type, short *dir) +{ + *dir = -1; + *type = -1; + switch (insn->name[0]) + { + case 'l': + *type = 0; + break; + case 'a': + *type = 1; + break; + default: + as_fatal (_("Bad shift mode")); + break; + } + + switch (insn->name[2]) + { + case 'l': + *dir = 1; + break; + case 'r': + *dir = 0; + break; + default: + as_fatal (_("Bad shift *direction")); + break; + } +} + +/* Shift instruction with a OPR operand */ +static int +shift_two_operand (const struct instruction *insn) +{ + uint8_t sb = 0x34; + char *ilp = input_line_pointer; + + short dir = -1; + short type = -1; + impute_shift_dir_and_type (insn, &type, &dir); + sb |= dir << 6; + sb |= type << 7; + + int size = size_from_suffix (insn, 0); + sb |= size - 1; + + uint8_t buffer[4]; + int n_opr_bytes; + expressionS exp; + if (!lex_opr (buffer, &n_opr_bytes, &exp)) + goto fail; + + if (!lex_match (',')) + goto fail; + + long imm = -1; + if (!lex_imm (&imm)) + goto fail; + + if (imm != 1 && imm != 2) + goto fail; + + if (imm == 2) + sb |= 0x08; + + char *f = s12z_new_insn (2 + n_opr_bytes); + number_to_chars_bigendian (f++, insn->opc, 1); + number_to_chars_bigendian (f++, sb, 1); + emit_opr (f, buffer, n_opr_bytes, &exp); + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + +/* Shift instruction with a OPR operand */ +static int +shift_opr_imm (const struct instruction *insn) +{ + char *ilp = input_line_pointer; + + short dir = -1; + short type = -1; + impute_shift_dir_and_type (insn, &type, &dir); + + int Dd = 0; + if (!lex_reg_name (REG_BIT_Dn, &Dd)) + goto fail; + + if (!lex_match (',')) + goto fail; + + int n_bytes = 2; + + uint8_t buffer1[4]; + int n_opr_bytes1; + + expressionS exp1; + if (!lex_opr (buffer1, &n_opr_bytes1, &exp1)) + goto fail; + + n_bytes += n_opr_bytes1; + if (!lex_match (',')) + goto fail; + + uint8_t buffer2[4]; + int n_opr_bytes2 = 0; + expressionS exp2; + long imm; + bool immediate = false; + if (lex_imm (&imm)) + { + immediate = true; + } + else if (!lex_opr (buffer2, &n_opr_bytes2, &exp2)) + goto fail; + + uint8_t sb = 0x20; + + int size = size_from_suffix (insn, 0); + + if (size != -1) + sb |= size - 1; + + sb |= dir << 6; + sb |= type << 7; + + if (immediate) + { + if (imm == 2 || imm == 1) + { + if (imm == 2) + sb |= 0x08; + } + else + { + n_bytes++; + sb |= 0x10; + if (imm % 2) + sb |= 0x08; + } + } + else + { + n_bytes += n_opr_bytes2; + sb |= 0x10; + } + + char *f = s12z_new_insn (n_bytes); + number_to_chars_bigendian (f++, insn->opc | Dd, 1); + number_to_chars_bigendian (f++, sb, 1); + f = emit_opr (f, buffer1, n_opr_bytes1, &exp1); + if (immediate) + { + if (imm != 1 && imm != 2) + { + number_to_chars_bigendian (f++, 0x70 | (imm >> 1), 1); + } + } + else + { + f = emit_opr (f, buffer2, n_opr_bytes2, &exp2); + } + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + +/* Shift instruction with a register operand */ +static int +shift_reg (const struct instruction *insn) +{ + short dir = -1; + short type = -1; + impute_shift_dir_and_type (insn, &type, &dir); + + if (lex_shift_reg_imm1 (insn, type, dir)) + return 1; + + return lex_shift_reg (insn, type, dir); +} + +static int +bm_regd_imm (const struct instruction *insn) +{ + char *ilp = input_line_pointer; + int Di = 0; + if (!lex_reg_name (REG_BIT_Dn, &Di)) + goto fail; + + if (!lex_match (',')) + goto fail; + + long imm; + if (!lex_imm (&imm)) + goto fail; + + + uint8_t bm = imm << 3; + bm |= Di; + + char *f = s12z_new_insn (2); + number_to_chars_bigendian (f++, insn->opc, 1); + number_to_chars_bigendian (f++, bm, 1); + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + +static int +bm_opr_reg (const struct instruction *insn) +{ + char *ilp = input_line_pointer; + + uint8_t buffer[4]; + int n_opr_bytes; + + expressionS exp; + if (!lex_opr (buffer, &n_opr_bytes, &exp)) + goto fail; + + if (!lex_match (',')) + goto fail; + + int Dn = 0; + if (!lex_reg_name (REG_BIT_Dn, &Dn)) + goto fail; + + uint8_t bm = Dn << 4; + int size = size_from_suffix (insn, 0); + bm |= (size - 1) << 2; + bm |= 0x81; + + char *f = s12z_new_insn (2 + n_opr_bytes); + number_to_chars_bigendian (f++, insn->opc, 1); + number_to_chars_bigendian (f++, bm, 1); + + emit_opr (f, buffer, n_opr_bytes, &exp); + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + + +static int +bm_opr_imm (const struct instruction *insn) +{ + char *ilp = input_line_pointer; + + uint8_t buffer[4]; + int n_opr_bytes; + + expressionS exp; + if (!lex_opr (buffer, &n_opr_bytes, &exp)) + goto fail; + + if (!lex_match (',')) + goto fail; + + + long imm; + if (!lex_imm (&imm)) + goto fail; + + int size = size_from_suffix (insn, 0); + + if (imm < 0 || imm >= size * 8) + { + as_bad (_("Immediate operand %ld is inappropriate for size of instruction"), imm); + goto fail; + } + + uint8_t bm = 0x80; + if (size == 2) + bm |= 0x02; + else if (size == 4) + bm |= 0x08; + bm |= (imm & 0x07) << 4; + bm |= (imm >> 3); + + + char *f = s12z_new_insn (2 + n_opr_bytes); + number_to_chars_bigendian (f++, insn->opc, 1); + number_to_chars_bigendian (f++, bm, 1); + emit_opr (f, buffer, n_opr_bytes, &exp); + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + + +static int +bm_regd_reg (const struct instruction *insn) +{ + char *ilp = input_line_pointer; + int Di = 0; + if (!lex_reg_name (REG_BIT_Dn, &Di)) + goto fail; + + if (!lex_match (',')) + goto fail; + + int Dn = 0; + if (!lex_reg_name (REG_BIT_Dn, &Dn)) + goto fail; + + uint8_t bm = Dn << 4; + bm |= 0x81; + + uint8_t xb = Di | 0xb8; + + char *f = s12z_new_insn (3); + number_to_chars_bigendian (f++, insn->opc, 1); + number_to_chars_bigendian (f++, bm, 1); + number_to_chars_bigendian (f++, xb, 1); + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + + + + + +static int +bf_reg_opr_imm (const struct instruction *insn, short ie) +{ + char *ilp = input_line_pointer; + int Dd = 0; + if (!lex_reg_name (REG_BIT_Dn, &Dd)) + goto fail; + + if (!lex_match (',')) + goto fail; + + uint8_t buffer[4]; + int n_bytes; + + expressionS exp; + if (!lex_opr (buffer, &n_bytes, &exp)) + goto fail; + + if (!lex_match (',')) + goto fail; + + long width; + if (!lex_imm (&width)) + goto fail; + + if (width < 0 || width > 31) + { + as_bad (_("Invalid width value for %s"), insn->name); + goto fail; + } + + if (!lex_match (':')) + goto fail; + + long offset; + if (!lex_constant (&offset)) + goto fail; + + if (offset < 0 || offset > 31) + { + as_bad (_("Invalid offset value for %s"), insn->name); + goto fail; + } + + uint8_t i1 = width << 5; + i1 |= offset; + + int size = size_from_suffix (insn, 0); + uint8_t bb = ie ? 0x80 : 0x00; + bb |= 0x60; + bb |= (size - 1) << 2; + bb |= width >> 3; + + char *f = s12z_new_insn (4 + n_bytes); + number_to_chars_bigendian (f++, PAGE2_PREBYTE, 1); + number_to_chars_bigendian (f++, 0x08 | Dd, 1); + number_to_chars_bigendian (f++, bb, 1); + number_to_chars_bigendian (f++, i1, 1); + + emit_opr (f, buffer, n_bytes, &exp); + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + + +static int +bf_opr_reg_imm (const struct instruction *insn, short ie) +{ + char *ilp = input_line_pointer; + uint8_t buffer[4]; + int n_bytes; + expressionS exp; + if (!lex_opr (buffer, &n_bytes, &exp)) + goto fail; + + if (!lex_match (',')) + goto fail; + + int Ds = 0; + if (!lex_reg_name (REG_BIT_Dn, &Ds)) + goto fail; + + if (!lex_match (',')) + goto fail; + + long width; + if (!lex_imm (&width)) + goto fail; + + if (width < 0 || width > 31) + { + as_bad (_("Invalid width value for %s"), insn->name); + goto fail; + } + + if (!lex_match (':')) + goto fail; + + long offset; + if (!lex_constant (&offset)) + goto fail; + + if (offset < 0 || offset > 31) + { + as_bad (_("Invalid offset value for %s"), insn->name); + goto fail; + } + + uint8_t i1 = width << 5; + i1 |= offset; + + int size = size_from_suffix (insn, 0); + uint8_t bb = ie ? 0x80 : 0x00; + bb |= 0x70; + bb |= (size - 1) << 2; + bb |= width >> 3; + + char *f = s12z_new_insn (4 + n_bytes); + number_to_chars_bigendian (f++, PAGE2_PREBYTE, 1); + number_to_chars_bigendian (f++, 0x08 | Ds, 1); + number_to_chars_bigendian (f++, bb, 1); + number_to_chars_bigendian (f++, i1, 1); + + emit_opr (f, buffer, n_bytes, &exp); + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + + + +static int +bf_reg_reg_imm (const struct instruction *insn, short ie) +{ + char *ilp = input_line_pointer; + int Dd = 0; + if (!lex_reg_name (REG_BIT_Dn, &Dd)) + goto fail; + + if (!lex_match (',')) + goto fail; + + int Ds = 0; + if (!lex_reg_name (REG_BIT_Dn, &Ds)) + goto fail; + + if (!lex_match (',')) + goto fail; + + long width; + if (!lex_imm (&width)) + goto fail; + + if (width < 0 || width > 31) + { + as_bad (_("Invalid width value for %s"), insn->name); + goto fail; + } + + if (!lex_match (':')) + goto fail; + + long offset; + if (!lex_constant (&offset)) + goto fail; + + if (offset < 0 || offset > 31) + { + as_bad (_("Invalid offset value for %s"), insn->name); + goto fail; + } + + uint8_t bb = ie ? 0x80 : 0x00; + bb |= 0x20; + bb |= Ds << 2; + bb |= width >> 3; + + uint8_t i1 = width << 5; + i1 |= offset; + + char *f = s12z_new_insn (4); + number_to_chars_bigendian (f++, PAGE2_PREBYTE, 1); + number_to_chars_bigendian (f++, 0x08 | Dd, 1); + number_to_chars_bigendian (f++, bb, 1); + number_to_chars_bigendian (f++, i1, 1); + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + +static int +bf_reg_reg_reg (const struct instruction *insn ATTRIBUTE_UNUSED, short ie) +{ + char *ilp = input_line_pointer; + int Dd = 0; + if (!lex_reg_name (REG_BIT_Dn, &Dd)) + goto fail; + + if (!lex_match (',')) + goto fail; + + int Ds = 0; + if (!lex_reg_name (REG_BIT_Dn, &Ds)) + goto fail; + + if (!lex_match (',')) + goto fail; + + int Dp = 0; + if (!lex_reg_name ((0x01u << REG_D2) | + (0x01u << REG_D3) | + (0x01u << REG_D4) | + (0x01u << REG_D5), + &Dp)) + goto fail; + + uint8_t bb = ie ? 0x80 : 0x00; + bb |= Ds << 2; + bb |= Dp; + + char *f = s12z_new_insn (3); + number_to_chars_bigendian (f++, PAGE2_PREBYTE, 1); + number_to_chars_bigendian (f++, 0x08 | Dd, 1); + number_to_chars_bigendian (f++, bb , 1); + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + +static int +bf_opr_reg_reg (const struct instruction *insn, short ie) +{ + char *ilp = input_line_pointer; + + uint8_t buffer[4]; + int n_bytes; + expressionS exp; + if (!lex_opr (buffer, &n_bytes, &exp)) + goto fail; + + if (!lex_match (',')) + goto fail; + + + int Ds = 0; + if (!lex_reg_name (REG_BIT_Dn, &Ds)) + goto fail; + + if (!lex_match (',')) + goto fail; + + + int Dp = 0; + if (!lex_reg_name ((0x01u << REG_D2) | + (0x01u << REG_D3) | + (0x01u << REG_D4) | + (0x01u << REG_D5), + &Dp)) + goto fail; + + int size = size_from_suffix (insn, 0); + uint8_t bb = ie ? 0x80 : 0x00; + bb |= 0x50; + bb |= Dp; + bb |= (size - 1) << 2; + + char *f = s12z_new_insn (3 + n_bytes); + number_to_chars_bigendian (f++, PAGE2_PREBYTE, 1); + number_to_chars_bigendian (f++, 0x08 | Ds, 1); + number_to_chars_bigendian (f++, bb , 1); + + emit_opr (f, buffer, n_bytes, &exp); + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + + +static int +bf_reg_opr_reg (const struct instruction *insn, short ie) +{ + char *ilp = input_line_pointer; + int Dd = 0; + if (!lex_reg_name (REG_BIT_Dn, &Dd)) + goto fail; + + if (!lex_match (',')) + goto fail; + + + uint8_t buffer[4]; + int n_bytes; + expressionS exp; + if (!lex_opr (buffer, &n_bytes, &exp)) + goto fail; + + if (!lex_match (',')) + goto fail; + + int Dp = 0; + if (!lex_reg_name ((0x01u << REG_D2) | + (0x01u << REG_D3) | + (0x01u << REG_D4) | + (0x01u << REG_D5), + &Dp)) + goto fail; + + int size = size_from_suffix (insn, 0); + uint8_t bb = ie ? 0x80 : 0x00; + bb |= 0x40; + bb |= Dp; + bb |= (size - 1) << 2; + + char *f = s12z_new_insn (3 + n_bytes); + number_to_chars_bigendian (f++, PAGE2_PREBYTE, 1); + number_to_chars_bigendian (f++, 0x08 | Dd, 1); + number_to_chars_bigendian (f++, bb , 1); + + emit_opr (f, buffer, n_bytes, &exp); + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + + + +static int +bfe_reg_reg_reg (const struct instruction *insn) +{ + return bf_reg_reg_reg (insn, 0); +} + +static int +bfi_reg_reg_reg (const struct instruction *insn) +{ + return bf_reg_reg_reg (insn, 1); +} + +static int +bfe_reg_reg_imm (const struct instruction *insn) +{ + return bf_reg_reg_imm (insn, 0); +} + +static int +bfi_reg_reg_imm (const struct instruction *insn) +{ + return bf_reg_reg_imm (insn, 1); +} + + +static int +bfe_reg_opr_reg (const struct instruction *insn) +{ + return bf_reg_opr_reg (insn, 0); +} + +static int +bfi_reg_opr_reg (const struct instruction *insn) +{ + return bf_reg_opr_reg (insn, 1); +} + + +static int +bfe_opr_reg_reg (const struct instruction *insn) +{ + return bf_opr_reg_reg (insn, 0); +} + +static int +bfi_opr_reg_reg (const struct instruction *insn) +{ + return bf_opr_reg_reg (insn, 1); +} + +static int +bfe_reg_opr_imm (const struct instruction *insn) +{ + return bf_reg_opr_imm (insn, 0); +} + +static int +bfi_reg_opr_imm (const struct instruction *insn) +{ + return bf_reg_opr_imm (insn, 1); +} + +static int +bfe_opr_reg_imm (const struct instruction *insn) +{ + return bf_opr_reg_imm (insn, 0); +} + +static int +bfi_opr_reg_imm (const struct instruction *insn) +{ + return bf_opr_reg_imm (insn, 1); +} + + + + +static int +tb_reg_rel (const struct instruction *insn) +{ + char *ilp = input_line_pointer; + + int reg; + if (!lex_reg_name (REG_BIT_Dn | REG_BIT_XY, ®)) + goto fail; + + if (!lex_match (',')) + goto fail; + + bool long_displacement; + expressionS exp; + if (! lex_15_bit_offset (&long_displacement, &exp)) + goto fail; + + uint8_t lb = 0x00; + if (reg == REG_X || reg == REG_Y) + { + lb |= 0x08; + } + else + { + lb |= reg; + } + if (reg == REG_Y) + lb |= 0x01; + + if (0 == strncmp (insn->name + 2, "ne", 2)) + lb |= 0x00 << 4; + else if (0 == strncmp (insn->name + 2, "eq", 2)) + lb |= 0x01 << 4; + else if (0 == strncmp (insn->name + 2, "pl", 2)) + lb |= 0x02 << 4; + else if (0 == strncmp (insn->name + 2, "mi", 2)) + lb |= 0x03 << 4; + else if (0 == strncmp (insn->name + 2, "gt", 2)) + lb |= 0x04 << 4; + else if (0 == strncmp (insn->name + 2, "le", 2)) + lb |= 0x05 << 4; + + switch (insn->name[0]) + { + case 'd': + lb |= 0x80; + break; + case 't': + break; + default: + gas_assert (0); + break; + }; + + char *f = s12z_new_insn (long_displacement ? 4 : 3); + number_to_chars_bigendian (f++, insn->opc, 1); + number_to_chars_bigendian (f++, lb, 1); + + emit_15_bit_offset (f, 4, &exp); + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + + +static int +tb_opr_rel (const struct instruction *insn) +{ + char *ilp = input_line_pointer; + + uint8_t buffer[4]; + int n_bytes; + expressionS exp; + if (!lex_opr (buffer, &n_bytes, &exp)) + goto fail; + + if (!lex_match (',')) + goto fail; + + bool long_displacement; + expressionS exp2; + if (! lex_15_bit_offset (&long_displacement, &exp2)) + goto fail; + + uint8_t lb = 0x0C; + + if (0 == strncmp (insn->name + 2, "ne", 2)) + lb |= 0x00 << 4; + else if (0 == strncmp (insn->name + 2, "eq", 2)) + lb |= 0x01 << 4; + else if (0 == strncmp (insn->name + 2, "pl", 2)) + lb |= 0x02 << 4; + else if (0 == strncmp (insn->name + 2, "mi", 2)) + lb |= 0x03 << 4; + else if (0 == strncmp (insn->name + 2, "gt", 2)) + lb |= 0x04 << 4; + else if (0 == strncmp (insn->name + 2, "le", 2)) + lb |= 0x05 << 4; + + switch (insn->name[0]) + { + case 'd': + lb |= 0x80; + break; + case 't': + break; + default: + gas_assert (0); + break; + }; + + int size = size_from_suffix (insn, 0); + + lb |= size -1; + + char *f = s12z_new_insn (n_bytes + (long_displacement ? 4 : 3)); + number_to_chars_bigendian (f++, insn->opc, 1); + number_to_chars_bigendian (f++, lb, 1); + f = emit_opr (f, buffer, n_bytes, &exp); + + emit_15_bit_offset (f, n_bytes + 4, &exp2); + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + + + + +static int +test_br_reg_reg_rel (const struct instruction *insn) +{ + char *ilp = input_line_pointer; + + int Di = 0; + if (!lex_reg_name (REG_BIT_Dn, &Di)) + goto fail; + + if (!lex_match (',')) + goto fail; + + + int Dn = 0; + if (!lex_reg_name (REG_BIT_Dn, &Dn)) + goto fail; + + if (!lex_match (',')) + goto fail; + + + bool long_displacement; + expressionS exp; + if (! lex_15_bit_offset (&long_displacement, &exp)) + goto fail; + + uint8_t bm = 0x81; + uint8_t xb = 0xb8; + + bm |= Dn << 4; + xb |= Di; + + char *f = s12z_new_insn (long_displacement ? 5 : 4); + number_to_chars_bigendian (f++, insn->opc, 1); + number_to_chars_bigendian (f++, bm, 1); + number_to_chars_bigendian (f++, xb, 1); + + emit_15_bit_offset (f, 5, &exp); + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + +static int +test_br_opr_reg_rel (const struct instruction *insn) +{ + char *ilp = input_line_pointer; + + uint8_t buffer[4]; + int n_bytes; + expressionS exp; + if (!lex_opr (buffer, &n_bytes, &exp)) + goto fail; + + if (!lex_match (',')) + goto fail; + + int Dn = 0; + if (!lex_reg_name (REG_BIT_Dn, &Dn)) + goto fail; + + if (!lex_match (',')) + goto fail; + + uint8_t bm = 0x81; + bm |= Dn << 4; + int size = size_from_suffix (insn, 0); + bm |= (size -1) << 2; + + bool long_displacement; + + expressionS exp2; + if (! lex_15_bit_offset (&long_displacement, &exp2)) + goto fail; + + int n = n_bytes + (long_displacement ? 4 : 3); + char *f = s12z_new_insn (n); + number_to_chars_bigendian (f++, insn->opc, 1); + number_to_chars_bigendian (f++, bm, 1); + f = emit_opr (f, buffer, n_bytes, &exp); + + emit_15_bit_offset (f, n, &exp2); + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + + +static int +test_br_opr_imm_rel (const struct instruction *insn) +{ + char *ilp = input_line_pointer; + + uint8_t buffer[4]; + int n_bytes; + expressionS exp; + if (!lex_opr (buffer, &n_bytes, &exp)) + goto fail; + + if (!lex_match (',')) + goto fail; + + long imm; + if (!lex_imm (&imm)) + goto fail; + + if (imm < 0 || imm > 31) + goto fail; + + if (!lex_match (',')) + goto fail; + + bool long_displacement; + expressionS exp2; + if (! lex_15_bit_offset (&long_displacement, &exp2)) + goto fail; + + int size = size_from_suffix (insn, 0); + + uint8_t bm = 0x80; + bm |= (imm & 0x07) << 4; + bm |= (imm >> 3) & 0x03; + if (size == 4) + bm |= 0x08; + else if (size == 2) + bm |= 0x02; + + char *f = s12z_new_insn (n_bytes + (long_displacement ? 4 : 3)); + number_to_chars_bigendian (f++, insn->opc, 1); + number_to_chars_bigendian (f++, bm, 1); + f = emit_opr (f, buffer, n_bytes, &exp); + + emit_15_bit_offset (f, n_bytes + 4, &exp2); + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + + +static int +test_br_reg_imm_rel (const struct instruction *insn) +{ + char *ilp = input_line_pointer; + + int Di = 0; + if (!lex_reg_name (REG_BIT_Dn, &Di)) + goto fail; + + if (!lex_match (',')) + goto fail; + + long imm; + if (!lex_imm (&imm)) + goto fail; + + if (imm < 0 || imm > 31) + goto fail; + + + if (!lex_match (',')) + goto fail; + + bool long_displacement; + expressionS exp; + if (! lex_15_bit_offset (&long_displacement, &exp)) + goto fail; + + uint8_t bm = Di; + bm |= imm << 3; + + char *f = s12z_new_insn (long_displacement ? 4 : 3); + number_to_chars_bigendian (f++, insn->opc, 1); + number_to_chars_bigendian (f++, bm, 1); + + emit_15_bit_offset (f, 4, &exp); + + return 1; + + fail: + fail_line_pointer = input_line_pointer; + input_line_pointer = ilp; + return 0; +} + + + + +static const struct instruction opcodes[] = { + {"bgnd", 1, 0x00, no_operands, 0}, + {"nop", 1, 0x01, no_operands, 0}, + + {"brclr", 1, 0x02, test_br_reg_reg_rel, 0}, + {"brset", 1, 0x03, test_br_reg_reg_rel, 0}, + + {"brclr", 1, 0x02, test_br_reg_imm_rel, 0}, + {"brset", 1, 0x03, test_br_reg_imm_rel, 0}, + + {"brclr.b", 1, 0x02, test_br_opr_reg_rel, 0}, + {"brclr.w", 1, 0x02, test_br_opr_reg_rel, 0}, + {"brclr.l", 1, 0x02, test_br_opr_reg_rel, 0}, + + {"brset.b", 1, 0x03, test_br_opr_reg_rel, 0}, + {"brset.w", 1, 0x03, test_br_opr_reg_rel, 0}, + {"brset.l", 1, 0x03, test_br_opr_reg_rel, 0}, + + {"brclr.b", 1, 0x02, test_br_opr_imm_rel, 0}, + {"brclr.w", 1, 0x02, test_br_opr_imm_rel, 0}, + {"brclr.l", 1, 0x02, test_br_opr_imm_rel, 0}, + + {"brset.b", 1, 0x03, test_br_opr_imm_rel, 0}, + {"brset.w", 1, 0x03, test_br_opr_imm_rel, 0}, + {"brset.l", 1, 0x03, test_br_opr_imm_rel, 0}, + + {"psh", 1, 0x04, psh_pull, 0}, + {"pul", 1, 0x04, psh_pull, 0}, + + {"rts", 1, 0x05, no_operands, 0}, + {"lea", 1, 0x06, reg67sxy_opr, 0}, + + {"dbne", 1, 0x0b, tb_reg_rel, 0}, + {"dbeq", 1, 0x0b, tb_reg_rel, 0}, + {"dbpl", 1, 0x0b, tb_reg_rel, 0}, + {"dbmi", 1, 0x0b, tb_reg_rel, 0}, + {"dbgt", 1, 0x0b, tb_reg_rel, 0}, + {"dble", 1, 0x0b, tb_reg_rel, 0}, + + {"dbne.b", 1, 0x0b, tb_opr_rel, 0}, + {"dbeq.b", 1, 0x0b, tb_opr_rel, 0}, + {"dbpl.b", 1, 0x0b, tb_opr_rel, 0}, + {"dbmi.b", 1, 0x0b, tb_opr_rel, 0}, + {"dbgt.b", 1, 0x0b, tb_opr_rel, 0}, + {"dble.b", 1, 0x0b, tb_opr_rel, 0}, + + {"dbne.w", 1, 0x0b, tb_opr_rel, 0}, + {"dbeq.w", 1, 0x0b, tb_opr_rel, 0}, + {"dbpl.w", 1, 0x0b, tb_opr_rel, 0}, + {"dbmi.w", 1, 0x0b, tb_opr_rel, 0}, + {"dbgt.w", 1, 0x0b, tb_opr_rel, 0}, + {"dble.w", 1, 0x0b, tb_opr_rel, 0}, + + {"dbne.p", 1, 0x0b, tb_opr_rel, 0}, + {"dbeq.p", 1, 0x0b, tb_opr_rel, 0}, + {"dbpl.p", 1, 0x0b, tb_opr_rel, 0}, + {"dbmi.p", 1, 0x0b, tb_opr_rel, 0}, + {"dbgt.p", 1, 0x0b, tb_opr_rel, 0}, + {"dble.p", 1, 0x0b, tb_opr_rel, 0}, + + {"dbne.l", 1, 0x0b, tb_opr_rel, 0}, + {"dbeq.l", 1, 0x0b, tb_opr_rel, 0}, + {"dbpl.l", 1, 0x0b, tb_opr_rel, 0}, + {"dbmi.l", 1, 0x0b, tb_opr_rel, 0}, + {"dbgt.l", 1, 0x0b, tb_opr_rel, 0}, + {"dble.l", 1, 0x0b, tb_opr_rel, 0}, + + {"tbne", 1, 0x0b, tb_reg_rel, 0}, + {"tbeq", 1, 0x0b, tb_reg_rel, 0}, + {"tbpl", 1, 0x0b, tb_reg_rel, 0}, + {"tbmi", 1, 0x0b, tb_reg_rel, 0}, + {"tbgt", 1, 0x0b, tb_reg_rel, 0}, + {"tble", 1, 0x0b, tb_reg_rel, 0}, + + {"tbne.b", 1, 0x0b, tb_opr_rel, 0}, + {"tbeq.b", 1, 0x0b, tb_opr_rel, 0}, + {"tbpl.b", 1, 0x0b, tb_opr_rel, 0}, + {"tbmi.b", 1, 0x0b, tb_opr_rel, 0}, + {"tbgt.b", 1, 0x0b, tb_opr_rel, 0}, + {"tble.b", 1, 0x0b, tb_opr_rel, 0}, + + {"tbne.w", 1, 0x0b, tb_opr_rel, 0}, + {"tbeq.w", 1, 0x0b, tb_opr_rel, 0}, + {"tbpl.w", 1, 0x0b, tb_opr_rel, 0}, + {"tbmi.w", 1, 0x0b, tb_opr_rel, 0}, + {"tbgt.w", 1, 0x0b, tb_opr_rel, 0}, + {"tble.w", 1, 0x0b, tb_opr_rel, 0}, + + {"tbne.p", 1, 0x0b, tb_opr_rel, 0}, + {"tbeq.p", 1, 0x0b, tb_opr_rel, 0}, + {"tbpl.p", 1, 0x0b, tb_opr_rel, 0}, + {"tbmi.p", 1, 0x0b, tb_opr_rel, 0}, + {"tbgt.p", 1, 0x0b, tb_opr_rel, 0}, + {"tble.p", 1, 0x0b, tb_opr_rel, 0}, + + {"tbne.l", 1, 0x0b, tb_opr_rel, 0}, + {"tbeq.l", 1, 0x0b, tb_opr_rel, 0}, + {"tbpl.l", 1, 0x0b, tb_opr_rel, 0}, + {"tbmi.l", 1, 0x0b, tb_opr_rel, 0}, + {"tbgt.l", 1, 0x0b, tb_opr_rel, 0}, + {"tble.l", 1, 0x0b, tb_opr_rel, 0}, + + {"mov.b", 1, 0x0c, imm_opr, 0}, + {"mov.w", 1, 0x0d, imm_opr, 0}, + {"mov.p", 1, 0x0e, imm_opr, 0}, + {"mov.l", 1, 0x0f, imm_opr, 0}, + + {"rol", 1, 0x10, rol, 0}, + {"rol.b", 1, 0x10, rol, 0}, + {"rol.w", 1, 0x10, rol, 0}, + {"rol.p", 1, 0x10, rol, 0}, + {"rol.l", 1, 0x10, rol, 0}, + + {"ror", 1, 0x10, ror, 0}, + {"ror.b", 1, 0x10, ror, 0}, + {"ror.w", 1, 0x10, ror, 0}, + {"ror.p", 1, 0x10, ror, 0}, + {"ror.l", 1, 0x10, ror, 0}, + + {"lsl", 1, 0x10, shift_reg, 0}, + {"lsr", 1, 0x10, shift_reg, 0}, + {"asl", 1, 0x10, shift_reg, 0}, + {"asr", 1, 0x10, shift_reg, 0}, + + {"lsl.b", 1, 0x10, shift_two_operand, 0}, + {"lsl.w", 1, 0x10, shift_two_operand, 0}, + {"lsl.p", 1, 0x10, shift_two_operand, 0}, + {"lsl.l", 1, 0x10, shift_two_operand, 0}, + {"asl.b", 1, 0x10, shift_two_operand, 0}, + {"asl.w", 1, 0x10, shift_two_operand, 0}, + {"asl.p", 1, 0x10, shift_two_operand, 0}, + {"asl.l", 1, 0x10, shift_two_operand, 0}, + + {"lsr.b", 1, 0x10, shift_two_operand, 0}, + {"lsr.w", 1, 0x10, shift_two_operand, 0}, + {"lsr.p", 1, 0x10, shift_two_operand, 0}, + {"lsr.l", 1, 0x10, shift_two_operand, 0}, + {"asr.b", 1, 0x10, shift_two_operand, 0}, + {"asr.w", 1, 0x10, shift_two_operand, 0}, + {"asr.p", 1, 0x10, shift_two_operand, 0}, + {"asr.l", 1, 0x10, shift_two_operand, 0}, + + {"lsl.b", 1, 0x10, shift_opr_imm, 0}, + {"lsl.w", 1, 0x10, shift_opr_imm, 0}, + {"lsl.p", 1, 0x10, shift_opr_imm, 0}, + {"lsl.l", 1, 0x10, shift_opr_imm, 0}, + {"asl.b", 1, 0x10, shift_opr_imm, 0}, + {"asl.w", 1, 0x10, shift_opr_imm, 0}, + {"asl.p", 1, 0x10, shift_opr_imm, 0}, + {"asl.l", 1, 0x10, shift_opr_imm, 0}, + + {"lsr.b", 1, 0x10, shift_opr_imm, 0}, + {"lsr.w", 1, 0x10, shift_opr_imm, 0}, + {"lsr.p", 1, 0x10, shift_opr_imm, 0}, + {"lsr.l", 1, 0x10, shift_opr_imm, 0}, + {"asr.b", 1, 0x10, shift_opr_imm, 0}, + {"asr.w", 1, 0x10, shift_opr_imm, 0}, + {"asr.p", 1, 0x10, shift_opr_imm, 0}, + {"asr.l", 1, 0x10, shift_opr_imm, 0}, + + {"mov.b", 1, 0x1c, opr_opr, 0}, + {"mov.w", 1, 0x1d, opr_opr, 0}, + {"mov.p", 1, 0x1e, opr_opr, 0}, + {"mov.l", 1, 0x1f, opr_opr, 0}, + + {"bra", 1, 0x20, rel, 0}, + {"bsr", 1, 0x21, rel, 0}, + {"bhi", 1, 0x22, rel, 0}, + {"bls", 1, 0x23, rel, 0}, + {"bcc", 1, 0x24, rel, 0}, + {"bcs", 1, 0x25, rel, 0}, + {"bne", 1, 0x26, rel, 0}, + {"beq", 1, 0x27, rel, 0}, + {"bvc", 1, 0x28, rel, 0}, + {"bvs", 1, 0x29, rel, 0}, + {"bpl", 1, 0x2a, rel, 0}, + {"bmi", 1, 0x2b, rel, 0}, + {"bge", 1, 0x2c, rel, 0}, + {"blt", 1, 0x2d, rel, 0}, + {"bgt", 1, 0x2e, rel, 0}, + {"ble", 1, 0x2f, rel, 0}, + + {"inc", 1, 0x30, reg_inh, 0}, + {"clr", 1, 0x38, reg_inh, 0}, + {"dec", 1, 0x40, reg_inh, 0}, + + {"muls", 1, 0x48, mul_reg_reg_reg, 0}, + {"mulu", 1, 0x48, mul_reg_reg_reg, 0}, + + {"muls.b", 1, 0x48, mul_reg_reg_opr, 0}, + {"muls.w", 1, 0x48, mul_reg_reg_opr, 0}, + {"muls.l", 1, 0x48, mul_reg_reg_opr, 0}, + + {"mulu.b", 1, 0x48, mul_reg_reg_opr, 0}, + {"mulu.w", 1, 0x48, mul_reg_reg_opr, 0}, + {"mulu.l", 1, 0x48, mul_reg_reg_opr, 0}, + + {"muls.b", 1, 0x48, mul_reg_reg_imm, 0}, + {"muls.w", 1, 0x48, mul_reg_reg_imm, 0}, + {"muls.l", 1, 0x48, mul_reg_reg_imm, 0}, + + {"mulu.b", 1, 0x48, mul_reg_reg_imm, 0}, + {"mulu.w", 1, 0x48, mul_reg_reg_imm, 0}, + {"mulu.l", 1, 0x48, mul_reg_reg_imm, 0}, + + {"muls.bb", 1, 0x48, mul_reg_opr_opr, 0}, + {"muls.bw", 1, 0x48, mul_reg_opr_opr, 0}, + {"muls.bp", 1, 0x48, mul_reg_opr_opr, 0}, + {"muls.bl", 1, 0x48, mul_reg_opr_opr, 0}, + + {"muls.wb", 1, 0x48, mul_reg_opr_opr, 0}, + {"muls.ww", 1, 0x48, mul_reg_opr_opr, 0}, + {"muls.wp", 1, 0x48, mul_reg_opr_opr, 0}, + {"muls.wl", 1, 0x48, mul_reg_opr_opr, 0}, + + {"muls.pb", 1, 0x48, mul_reg_opr_opr, 0}, + {"muls.pw", 1, 0x48, mul_reg_opr_opr, 0}, + {"muls.pp", 1, 0x48, mul_reg_opr_opr, 0}, + {"muls.pl", 1, 0x48, mul_reg_opr_opr, 0}, + + {"muls.lb", 1, 0x48, mul_reg_opr_opr, 0}, + {"muls.lw", 1, 0x48, mul_reg_opr_opr, 0}, + {"muls.lp", 1, 0x48, mul_reg_opr_opr, 0}, + {"muls.ll", 1, 0x48, mul_reg_opr_opr, 0}, + + {"mulu.bb", 1, 0x48, mul_reg_opr_opr, 0}, + {"mulu.bw", 1, 0x48, mul_reg_opr_opr, 0}, + {"mulu.bp", 1, 0x48, mul_reg_opr_opr, 0}, + {"mulu.bl", 1, 0x48, mul_reg_opr_opr, 0}, + + {"mulu.wb", 1, 0x48, mul_reg_opr_opr, 0}, + {"mulu.ww", 1, 0x48, mul_reg_opr_opr, 0}, + {"mulu.wp", 1, 0x48, mul_reg_opr_opr, 0}, + {"mulu.wl", 1, 0x48, mul_reg_opr_opr, 0}, + + {"mulu.pb", 1, 0x48, mul_reg_opr_opr, 0}, + {"mulu.pw", 1, 0x48, mul_reg_opr_opr, 0}, + {"mulu.pp", 1, 0x48, mul_reg_opr_opr, 0}, + {"mulu.pl", 1, 0x48, mul_reg_opr_opr, 0}, + + {"mulu.lb", 1, 0x48, mul_reg_opr_opr, 0}, + {"mulu.lw", 1, 0x48, mul_reg_opr_opr, 0}, + {"mulu.lp", 1, 0x48, mul_reg_opr_opr, 0}, + {"mulu.ll", 1, 0x48, mul_reg_opr_opr, 0}, + + {"add", 1, 0x50, regd_imm, 0}, + {"and", 1, 0x58, regd_imm, 0}, + + {"add", 1, 0x60, regd_opr, 0}, + {"and", 1, 0x68, regd_opr, 0}, + + {"sub", 1, 0x70, regd_imm, 0}, + {"or", 1, 0x78, regd_imm, 0}, + + {"sub", 1, 0x80, regd_opr, 0}, + {"or", 1, 0x88, regd_opr, 0}, + + {"ld", 1, 0x90, regdxy_imm, 0}, + + {"clr", 1, 0x9a, clr_xy, 0}, + {"tfr", 1, 0x9e, tfr, 0}, + {"zex", 1, 0x9e, tfr, 0}, + + {"ld", 1, 0xa0, regdxy_opr, 0xb0}, + + {"jmp", 1, 0xaa, opr, 0xba}, + {"jsr", 1, 0xab, opr, 0xbb}, + + {"exg", 1, 0xae, tfr, 0}, + {"sex", 1, 0xae, tfr, 0}, + + {"st", 1, 0xc0, regdxy_opr, 0xd0}, + + {"andcc", 1, 0xce, imm8, 0}, + {"orcc", 1, 0xde, imm8, 0}, + + {"inc.b", 1, 0x9c, opr, 0}, + {"inc.w", 1, 0x9d, opr, 0}, + {"inc.l", 1, 0x9f, opr, 0}, + + {"dec.b", 1, 0xac, opr, 0}, + {"dec.w", 1, 0xad, opr, 0}, + {"dec.l", 1, 0xaf, opr, 0}, + + {"clr.b", 1, 0xbc, opr, 0}, + {"clr.w", 1, 0xbd, opr, 0}, + {"clr.p", 1, 0xbe, opr, 0}, + {"clr.l", 1, 0xbf, opr, 0}, + + {"com.b", 1, 0xcc, opr, 0}, + {"com.w", 1, 0xcd, opr, 0}, + {"com.l", 1, 0xcf, opr, 0}, + + {"neg.b", 1, 0xdc, opr, 0}, + {"neg.w", 1, 0xdd, opr, 0}, + {"neg.l", 1, 0xdf, opr, 0}, + + {"bclr", 1, 0xec, bm_regd_imm, 0}, + {"bset", 1, 0xed, bm_regd_imm, 0}, + {"btgl", 1, 0xee, bm_regd_imm, 0}, + + {"bclr", 1, 0xec, bm_regd_reg, 0}, + {"bset", 1, 0xed, bm_regd_reg, 0}, + {"btgl", 1, 0xee, bm_regd_reg, 0}, + + {"bclr.b", 1, 0xec, bm_opr_imm, 0}, + {"bclr.w", 1, 0xec, bm_opr_imm, 0}, + {"bclr.l", 1, 0xec, bm_opr_imm, 0}, + + {"bset.b", 1, 0xed, bm_opr_imm, 0}, + {"bset.w", 1, 0xed, bm_opr_imm, 0}, + {"bset.l", 1, 0xed, bm_opr_imm, 0}, + + {"btgl.b", 1, 0xee, bm_opr_imm, 0}, + {"btgl.w", 1, 0xee, bm_opr_imm, 0}, + {"btgl.l", 1, 0xee, bm_opr_imm, 0}, + + {"bclr.b", 1, 0xec, bm_opr_reg, 0}, + {"bclr.w", 1, 0xec, bm_opr_reg, 0}, + {"bclr.l", 1, 0xec, bm_opr_reg, 0}, + + {"bset.b", 1, 0xed, bm_opr_reg, 0}, + {"bset.w", 1, 0xed, bm_opr_reg, 0}, + {"bset.l", 1, 0xed, bm_opr_reg, 0}, + + {"btgl.b", 1, 0xee, bm_opr_reg, 0}, + {"btgl.w", 1, 0xee, bm_opr_reg, 0}, + {"btgl.l", 1, 0xee, bm_opr_reg, 0}, + + {"cmp", 1, 0xe0, regdxy_imm, 0}, + {"cmp", 1, 0xf0, regdxy_opr, 0}, + + {"cmp", 1, 0xfc, regx_regy, 0}, + {"sub", 1, 0xfd, regd6_regx_regy, 0}, + {"sub", 1, 0xfe, regd6_regy_regx, 0}, + + {"swi", 1, 0xff, no_operands, 0}, + + /* Page 2 */ + + /* The -10 below is a kludge. The opcode is in fact 0x00 */ + {"ld", 2, -10, regs_opr, 0}, + + /* The -9 below is a kludge. The opcode is in fact 0x01 */ + {"st", 2, -9, regs_opr, 0}, + + /* The -8 below is a kludge. The opcode is in fact 0x02 */ + {"cmp", 2, -8, regs_opr, 0}, + + /* The -7 below is a kludge. The opcode is in fact 0x03 */ + {"ld", 2, -7, regs_imm, 0}, + + /* The -6 below is a kludge. The opcode is in fact 0x04 */ + {"cmp", 2, -6, regs_imm, 0}, + + {"bfext", 2, 0x08, bfe_reg_reg_reg, 0}, + {"bfext", 2, 0x08, bfe_reg_reg_imm, 0}, + {"bfext.b", 2, 0x08, bfe_reg_opr_reg, 0}, + {"bfext.w", 2, 0x08, bfe_reg_opr_reg, 0}, + {"bfext.p", 2, 0x08, bfe_reg_opr_reg, 0}, + {"bfext.l", 2, 0x08, bfe_reg_opr_reg, 0}, + {"bfext.b", 2, 0x08, bfe_opr_reg_reg, 0}, + {"bfext.w", 2, 0x08, bfe_opr_reg_reg, 0}, + {"bfext.p", 2, 0x08, bfe_opr_reg_reg, 0}, + {"bfext.l", 2, 0x08, bfe_opr_reg_reg, 0}, + {"bfext.b", 2, 0x08, bfe_reg_opr_imm, 0}, + {"bfext.w", 2, 0x08, bfe_reg_opr_imm, 0}, + {"bfext.p", 2, 0x08, bfe_reg_opr_imm, 0}, + {"bfext.l", 2, 0x08, bfe_reg_opr_imm, 0}, + {"bfext.b", 2, 0x08, bfe_opr_reg_imm, 0}, + {"bfext.w", 2, 0x08, bfe_opr_reg_imm, 0}, + {"bfext.p", 2, 0x08, bfe_opr_reg_imm, 0}, + {"bfext.l", 2, 0x08, bfe_opr_reg_imm, 0}, + + + {"bfins", 2, 0x08, bfi_reg_reg_reg, 0}, + {"bfins", 2, 0x08, bfi_reg_reg_imm, 0}, + {"bfins.b", 2, 0x08, bfi_reg_opr_reg, 0}, + {"bfins.w", 2, 0x08, bfi_reg_opr_reg, 0}, + {"bfins.p", 2, 0x08, bfi_reg_opr_reg, 0}, + {"bfins.l", 2, 0x08, bfi_reg_opr_reg, 0}, + {"bfins.b", 2, 0x08, bfi_opr_reg_reg, 0}, + {"bfins.w", 2, 0x08, bfi_opr_reg_reg, 0}, + {"bfins.p", 2, 0x08, bfi_opr_reg_reg, 0}, + {"bfins.l", 2, 0x08, bfi_opr_reg_reg, 0}, + {"bfins.b", 2, 0x08, bfi_reg_opr_imm, 0}, + {"bfins.w", 2, 0x08, bfi_reg_opr_imm, 0}, + {"bfins.p", 2, 0x08, bfi_reg_opr_imm, 0}, + {"bfins.l", 2, 0x08, bfi_reg_opr_imm, 0}, + {"bfins.b", 2, 0x08, bfi_opr_reg_imm, 0}, + {"bfins.w", 2, 0x08, bfi_opr_reg_imm, 0}, + {"bfins.p", 2, 0x08, bfi_opr_reg_imm, 0}, + {"bfins.l", 2, 0x08, bfi_opr_reg_imm, 0}, + + + {"minu", 2, 0x10, regd_opr, 0}, + {"maxu", 2, 0x18, regd_opr, 0}, + {"mins", 2, 0x20, regd_opr, 0}, + {"maxs", 2, 0x28, regd_opr, 0}, + + {"clb", 2, 0x91, tfr, 0}, + + {"trap", 2, 0x00, trap_imm, 0}, + {"abs", 2, 0x40, reg_inh, 0}, + {"sat", 2, 0xa0, reg_inh, 0}, + + {"rti", 2, 0x90, no_operands, 0}, + {"stop", 2, 0x05, no_operands, 0}, + {"wai", 2, 0x06, no_operands, 0}, + {"sys", 2, 0x07, no_operands, 0}, + + {"bit", 2, 0x58, regd_imm, 0}, + {"bit", 2, 0x68, regd_opr, 0}, + + {"adc", 2, 0x50, regd_imm, 0}, + {"adc", 2, 0x60, regd_opr, 0}, + + {"sbc", 2, 0x70, regd_imm, 0}, + {"eor", 2, 0x78, regd_imm, 0}, + + {"sbc", 2, 0x80, regd_opr, 0}, + {"eor", 2, 0x88, regd_opr, 0}, + + {"divs", 2, 0x30, mul_reg_reg_reg, 0}, + {"divu", 2, 0x30, mul_reg_reg_reg, 0}, + + {"divs.b", 2, 0x30, mul_reg_reg_opr, 0}, + {"divs.w", 2, 0x30, mul_reg_reg_opr, 0}, + {"divs.l", 2, 0x30, mul_reg_reg_opr, 0}, + + {"divu.b", 2, 0x30, mul_reg_reg_opr, 0}, + {"divu.w", 2, 0x30, mul_reg_reg_opr, 0}, + {"divu.l", 2, 0x30, mul_reg_reg_opr, 0}, + + {"divs.b", 2, 0x30, mul_reg_reg_imm, 0}, + {"divs.w", 2, 0x30, mul_reg_reg_imm, 0}, + {"divs.l", 2, 0x30, mul_reg_reg_imm, 0}, + + {"divu.b", 2, 0x30, mul_reg_reg_imm, 0}, + {"divu.w", 2, 0x30, mul_reg_reg_imm, 0}, + {"divu.l", 2, 0x30, mul_reg_reg_imm, 0}, + + {"divs.bb", 2, 0x30, mul_reg_opr_opr, 0}, + {"divs.bw", 2, 0x30, mul_reg_opr_opr, 0}, + {"divs.bp", 2, 0x30, mul_reg_opr_opr, 0}, + {"divs.bl", 2, 0x30, mul_reg_opr_opr, 0}, + + {"divs.wb", 2, 0x30, mul_reg_opr_opr, 0}, + {"divs.ww", 2, 0x30, mul_reg_opr_opr, 0}, + {"divs.wp", 2, 0x30, mul_reg_opr_opr, 0}, + {"divs.wl", 2, 0x30, mul_reg_opr_opr, 0}, + + {"divs.pb", 2, 0x30, mul_reg_opr_opr, 0}, + {"divs.pw", 2, 0x30, mul_reg_opr_opr, 0}, + {"divs.pp", 2, 0x30, mul_reg_opr_opr, 0}, + {"divs.pl", 2, 0x30, mul_reg_opr_opr, 0}, + + {"divs.lb", 2, 0x30, mul_reg_opr_opr, 0}, + {"divs.lw", 2, 0x30, mul_reg_opr_opr, 0}, + {"divs.lp", 2, 0x30, mul_reg_opr_opr, 0}, + {"divs.ll", 2, 0x30, mul_reg_opr_opr, 0}, + + {"divu.bb", 2, 0x30, mul_reg_opr_opr, 0}, + {"divu.bw", 2, 0x30, mul_reg_opr_opr, 0}, + {"divu.bp", 2, 0x30, mul_reg_opr_opr, 0}, + {"divu.bl", 2, 0x30, mul_reg_opr_opr, 0}, + + {"divu.wb", 2, 0x30, mul_reg_opr_opr, 0}, + {"divu.ww", 2, 0x30, mul_reg_opr_opr, 0}, + {"divu.wp", 2, 0x30, mul_reg_opr_opr, 0}, + {"divu.wl", 2, 0x30, mul_reg_opr_opr, 0}, + + {"divu.pb", 2, 0x30, mul_reg_opr_opr, 0}, + {"divu.pw", 2, 0x30, mul_reg_opr_opr, 0}, + {"divu.pp", 2, 0x30, mul_reg_opr_opr, 0}, + {"divu.pl", 2, 0x30, mul_reg_opr_opr, 0}, + + {"divu.lb", 2, 0x30, mul_reg_opr_opr, 0}, + {"divu.lw", 2, 0x30, mul_reg_opr_opr, 0}, + {"divu.lp", 2, 0x30, mul_reg_opr_opr, 0}, + {"divu.ll", 2, 0x30, mul_reg_opr_opr, 0}, + + // + + {"qmuls", 2, 0xb0, mul_reg_reg_reg, 0}, + {"qmulu", 2, 0xb0, mul_reg_reg_reg, 0}, + + {"qmuls.b", 2, 0xb0, mul_reg_reg_opr, 0}, + {"qmuls.w", 2, 0xb0, mul_reg_reg_opr, 0}, + {"qmuls.l", 2, 0xb0, mul_reg_reg_opr, 0}, + + {"qmulu.b", 2, 0xb0, mul_reg_reg_opr, 0}, + {"qmulu.w", 2, 0xb0, mul_reg_reg_opr, 0}, + {"qmulu.l", 2, 0xb0, mul_reg_reg_opr, 0}, + + {"qmuls.b", 2, 0xb0, mul_reg_reg_imm, 0}, + {"qmuls.w", 2, 0xb0, mul_reg_reg_imm, 0}, + {"qmuls.l", 2, 0xb0, mul_reg_reg_imm, 0}, + + {"qmulu.b", 2, 0xb0, mul_reg_reg_imm, 0}, + {"qmulu.w", 2, 0xb0, mul_reg_reg_imm, 0}, + {"qmulu.l", 2, 0xb0, mul_reg_reg_imm, 0}, + + {"qmuls.bb", 2, 0xb0, mul_reg_opr_opr, 0}, + {"qmuls.bw", 2, 0xb0, mul_reg_opr_opr, 0}, + {"qmuls.bp", 2, 0xb0, mul_reg_opr_opr, 0}, + {"qmuls.bl", 2, 0xb0, mul_reg_opr_opr, 0}, + + {"qmuls.wb", 2, 0xb0, mul_reg_opr_opr, 0}, + {"qmuls.ww", 2, 0xb0, mul_reg_opr_opr, 0}, + {"qmuls.wp", 2, 0xb0, mul_reg_opr_opr, 0}, + {"qmuls.wl", 2, 0xb0, mul_reg_opr_opr, 0}, + + {"qmuls.pb", 2, 0xb0, mul_reg_opr_opr, 0}, + {"qmuls.pw", 2, 0xb0, mul_reg_opr_opr, 0}, + {"qmuls.pp", 2, 0xb0, mul_reg_opr_opr, 0}, + {"qmuls.pl", 2, 0xb0, mul_reg_opr_opr, 0}, + + {"qmuls.lb", 2, 0xb0, mul_reg_opr_opr, 0}, + {"qmuls.lw", 2, 0xb0, mul_reg_opr_opr, 0}, + {"qmuls.lp", 2, 0xb0, mul_reg_opr_opr, 0}, + {"qmuls.ll", 2, 0xb0, mul_reg_opr_opr, 0}, + + {"qmulu.bb", 2, 0xb0, mul_reg_opr_opr, 0}, + {"qmulu.bw", 2, 0xb0, mul_reg_opr_opr, 0}, + {"qmulu.bp", 2, 0xb0, mul_reg_opr_opr, 0}, + {"qmulu.bl", 2, 0xb0, mul_reg_opr_opr, 0}, + + {"qmulu.wb", 2, 0xb0, mul_reg_opr_opr, 0}, + {"qmulu.ww", 2, 0xb0, mul_reg_opr_opr, 0}, + {"qmulu.wp", 2, 0xb0, mul_reg_opr_opr, 0}, + {"qmulu.wl", 2, 0xb0, mul_reg_opr_opr, 0}, + + {"qmulu.pb", 2, 0xb0, mul_reg_opr_opr, 0}, + {"qmulu.pw", 2, 0xb0, mul_reg_opr_opr, 0}, + {"qmulu.pp", 2, 0xb0, mul_reg_opr_opr, 0}, + {"qmulu.pl", 2, 0xb0, mul_reg_opr_opr, 0}, + + {"qmulu.lb", 2, 0xb0, mul_reg_opr_opr, 0}, + {"qmulu.lw", 2, 0xb0, mul_reg_opr_opr, 0}, + {"qmulu.lp", 2, 0xb0, mul_reg_opr_opr, 0}, + {"qmulu.ll", 2, 0xb0, mul_reg_opr_opr, 0}, + + + // + + {"macs", 2, 0x48, mul_reg_reg_reg, 0}, + {"macu", 2, 0x48, mul_reg_reg_reg, 0}, + + {"macs.b", 2, 0x48, mul_reg_reg_opr, 0}, + {"macs.w", 2, 0x48, mul_reg_reg_opr, 0}, + {"macs.l", 2, 0x48, mul_reg_reg_opr, 0}, + + {"macu.b", 2, 0x48, mul_reg_reg_opr, 0}, + {"macu.w", 2, 0x48, mul_reg_reg_opr, 0}, + {"macu.l", 2, 0x48, mul_reg_reg_opr, 0}, + + {"macs.b", 2, 0x48, mul_reg_reg_imm, 0}, + {"macs.w", 2, 0x48, mul_reg_reg_imm, 0}, + {"macs.l", 2, 0x48, mul_reg_reg_imm, 0}, + + {"macu.b", 2, 0x48, mul_reg_reg_imm, 0}, + {"macu.w", 2, 0x48, mul_reg_reg_imm, 0}, + {"macu.l", 2, 0x48, mul_reg_reg_imm, 0}, + + {"macs.bb", 2, 0x48, mul_reg_opr_opr, 0}, + {"macs.bw", 2, 0x48, mul_reg_opr_opr, 0}, + {"macs.bp", 2, 0x48, mul_reg_opr_opr, 0}, + {"macs.bl", 2, 0x48, mul_reg_opr_opr, 0}, + + {"macs.wb", 2, 0x48, mul_reg_opr_opr, 0}, + {"macs.ww", 2, 0x48, mul_reg_opr_opr, 0}, + {"macs.wp", 2, 0x48, mul_reg_opr_opr, 0}, + {"macs.wl", 2, 0x48, mul_reg_opr_opr, 0}, + + {"macs.pb", 2, 0x48, mul_reg_opr_opr, 0}, + {"macs.pw", 2, 0x48, mul_reg_opr_opr, 0}, + {"macs.pp", 2, 0x48, mul_reg_opr_opr, 0}, + {"macs.pl", 2, 0x48, mul_reg_opr_opr, 0}, + + {"macs.lb", 2, 0x48, mul_reg_opr_opr, 0}, + {"macs.lw", 2, 0x48, mul_reg_opr_opr, 0}, + {"macs.lp", 2, 0x48, mul_reg_opr_opr, 0}, + {"macs.ll", 2, 0x48, mul_reg_opr_opr, 0}, + + {"macu.bb", 2, 0x48, mul_reg_opr_opr, 0}, + {"macu.bw", 2, 0x48, mul_reg_opr_opr, 0}, + {"macu.bp", 2, 0x48, mul_reg_opr_opr, 0}, + {"macu.bl", 2, 0x48, mul_reg_opr_opr, 0}, + + {"macu.wb", 2, 0x48, mul_reg_opr_opr, 0}, + {"macu.ww", 2, 0x48, mul_reg_opr_opr, 0}, + {"macu.wp", 2, 0x48, mul_reg_opr_opr, 0}, + {"macu.wl", 2, 0x48, mul_reg_opr_opr, 0}, + + {"macu.pb", 2, 0x48, mul_reg_opr_opr, 0}, + {"macu.pw", 2, 0x48, mul_reg_opr_opr, 0}, + {"macu.pp", 2, 0x48, mul_reg_opr_opr, 0}, + {"macu.pl", 2, 0x48, mul_reg_opr_opr, 0}, + + {"macu.lb", 2, 0x48, mul_reg_opr_opr, 0}, + {"macu.lw", 2, 0x48, mul_reg_opr_opr, 0}, + {"macu.lp", 2, 0x48, mul_reg_opr_opr, 0}, + {"macu.ll", 2, 0x48, mul_reg_opr_opr, 0}, + + + // + + {"mods", 2, 0x38, mul_reg_reg_reg, 0}, + {"modu", 2, 0x38, mul_reg_reg_reg, 0}, + + {"mods.b", 2, 0x38, mul_reg_reg_opr, 0}, + {"mods.w", 2, 0x38, mul_reg_reg_opr, 0}, + {"mods.l", 2, 0x38, mul_reg_reg_opr, 0}, + + {"modu.b", 2, 0x38, mul_reg_reg_opr, 0}, + {"modu.w", 2, 0x38, mul_reg_reg_opr, 0}, + {"modu.l", 2, 0x38, mul_reg_reg_opr, 0}, + + {"mods.b", 2, 0x38, mul_reg_reg_imm, 0}, + {"mods.w", 2, 0x38, mul_reg_reg_imm, 0}, + {"mods.l", 2, 0x38, mul_reg_reg_imm, 0}, + + {"modu.b", 2, 0x38, mul_reg_reg_imm, 0}, + {"modu.w", 2, 0x38, mul_reg_reg_imm, 0}, + {"modu.l", 2, 0x38, mul_reg_reg_imm, 0}, + + {"mods.bb", 2, 0x38, mul_reg_opr_opr, 0}, + {"mods.bw", 2, 0x38, mul_reg_opr_opr, 0}, + {"mods.bp", 2, 0x38, mul_reg_opr_opr, 0}, + {"mods.bl", 2, 0x38, mul_reg_opr_opr, 0}, + + {"mods.wb", 2, 0x38, mul_reg_opr_opr, 0}, + {"mods.ww", 2, 0x38, mul_reg_opr_opr, 0}, + {"mods.wp", 2, 0x38, mul_reg_opr_opr, 0}, + {"mods.wl", 2, 0x38, mul_reg_opr_opr, 0}, + + {"mods.pb", 2, 0x38, mul_reg_opr_opr, 0}, + {"mods.pw", 2, 0x38, mul_reg_opr_opr, 0}, + {"mods.pp", 2, 0x38, mul_reg_opr_opr, 0}, + {"mods.pl", 2, 0x38, mul_reg_opr_opr, 0}, + + {"mods.lb", 2, 0x38, mul_reg_opr_opr, 0}, + {"mods.lw", 2, 0x38, mul_reg_opr_opr, 0}, + {"mods.lp", 2, 0x38, mul_reg_opr_opr, 0}, + {"mods.ll", 2, 0x38, mul_reg_opr_opr, 0}, + + {"modu.bb", 2, 0x38, mul_reg_opr_opr, 0}, + {"modu.bw", 2, 0x38, mul_reg_opr_opr, 0}, + {"modu.bp", 2, 0x38, mul_reg_opr_opr, 0}, + {"modu.bl", 2, 0x38, mul_reg_opr_opr, 0}, + + {"modu.wb", 2, 0x38, mul_reg_opr_opr, 0}, + {"modu.ww", 2, 0x38, mul_reg_opr_opr, 0}, + {"modu.wp", 2, 0x38, mul_reg_opr_opr, 0}, + {"modu.wl", 2, 0x38, mul_reg_opr_opr, 0}, + + {"modu.pb", 2, 0x38, mul_reg_opr_opr, 0}, + {"modu.pw", 2, 0x38, mul_reg_opr_opr, 0}, + {"modu.pp", 2, 0x38, mul_reg_opr_opr, 0}, + {"modu.pl", 2, 0x38, mul_reg_opr_opr, 0}, + + {"modu.lb", 2, 0x38, mul_reg_opr_opr, 0}, + {"modu.lw", 2, 0x38, mul_reg_opr_opr, 0}, + {"modu.lp", 2, 0x38, mul_reg_opr_opr, 0}, + {"modu.ll", 2, 0x38, mul_reg_opr_opr, 0} +}; + + +/* Gas line assembler entry point. */ + +/* This is the main entry point for the machine-dependent assembler. str + points to a machine-dependent instruction. This function is supposed to + emit the frags/bytes it assembles to. */ +void +md_assemble (char *str) +{ + char *op_start; + char *op_end; + char name[20]; + size_t nlen = 0; + + fail_line_pointer = NULL; + + /* Find the opcode end and get the opcode in 'name'. The opcode is forced + lower case (the opcode table only has lower case op-codes). */ + for (op_start = op_end = str; + *op_end && !is_end_of_line[(int)*op_end] && *op_end != ' '; + op_end++) + { + name[nlen] = TOLOWER (op_start[nlen]); + nlen++; + gas_assert (nlen < sizeof (name) - 1); + } + name[nlen] = 0; + + if (nlen == 0) + { + as_bad (_("No instruction or missing opcode.")); + return; + } + + input_line_pointer = skip_whites (op_end); + + size_t i; + for (i = 0; i < sizeof (opcodes) / sizeof (opcodes[0]); ++i) + { + const struct instruction *opc = opcodes + i; + if (0 == strcmp (name, opc->name)) + { + if (opc->parse_operands (opc)) + return; + continue; + } + } + + as_bad (_("Invalid instruction: \"%s\""), str); + as_bad (_("First invalid token: \"%s\""), fail_line_pointer); + while (*input_line_pointer++) + ; +} + + + + + +/* Relocation, relaxation and frag conversions. */ + +/* PC-relative offsets are relative to the start of the + next instruction. That is, the address of the offset, plus its + size, since the offset is always the last part of the insn. */ +long +md_pcrel_from (fixS *fixP) +{ + long ret = fixP->fx_size + fixP->fx_frag->fr_address; + if (fixP->fx_addsy && S_IS_DEFINED (fixP->fx_addsy)) + ret += fixP->fx_where; + + return ret; +} + + +/* We need a port-specific relaxation function to cope with sym2 - sym1 + relative expressions with both symbols in the same segment (but not + necessarily in the same frag as this insn), for example: + ldab sym2-(sym1-2),pc + sym1: + The offset can be 5, 9 or 16 bits long. */ + +long +s12z_relax_frag (segT seg ATTRIBUTE_UNUSED, fragS *fragP ATTRIBUTE_UNUSED, + long stretch ATTRIBUTE_UNUSED) +{ + return 0; +} + +void +md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, asection *sec ATTRIBUTE_UNUSED, + fragS *fragP ATTRIBUTE_UNUSED) +{ +} + +/* On an ELF system, we can't relax a weak symbol. The weak symbol + can be overridden at final link time by a non weak symbol. We can + relax externally visible symbol because there is no shared library + and such symbol can't be overridden (unless they are weak). */ + +/* Force truly undefined symbols to their maximum size, and generally set up + the frag list to be relaxed. */ +int +md_estimate_size_before_relax (fragS *fragP ATTRIBUTE_UNUSED, asection *segment ATTRIBUTE_UNUSED) +{ + return 0; +} + + +/* If while processing a fixup, a reloc really needs to be created + then it is done here. */ +arelent * +tc_gen_reloc (asection *section, fixS *fixp) +{ + arelent *reloc = XNEW (arelent); + reloc->sym_ptr_ptr = XNEW (asymbol *); + *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); + reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; + reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type); + if (reloc->howto == (reloc_howto_type *) NULL) + { + as_bad_where (fixp->fx_file, fixp->fx_line, + _("Relocation %d is not supported by object file format."), + (int) fixp->fx_r_type); + return NULL; + } + + if (0 == (section->flags & SEC_CODE)) + reloc->addend = fixp->fx_offset; + else + reloc->addend = fixp->fx_addnumber; + + return reloc; +} + +/* See whether we need to force a relocation into the output file. */ +int +tc_s12z_force_relocation (fixS *fixP) +{ + return generic_force_reloc (fixP); +} + +/* Here we decide which fixups can be adjusted to make them relative + to the beginning of the section instead of the symbol. Basically + we need to make sure that the linker relaxation is done + correctly, so in some cases we force the original symbol to be + used. */ +int +tc_s12z_fix_adjustable (fixS *fixP ATTRIBUTE_UNUSED) +{ + return 1; +} + +void +md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) +{ + long value = *valP; + + if (fixP->fx_addsy == (symbolS *) NULL) + fixP->fx_done = 1; + + /* We don't actually support subtracting a symbol. */ + if (fixP->fx_subsy != (symbolS *) NULL) + as_bad_where (fixP->fx_file, fixP->fx_line, _("Expression too complex.")); + + /* + Patch the instruction with the resolved operand. Elf relocation + info will also be generated to take care of linker/loader fixups. + */ + char *where = fixP->fx_frag->fr_literal + fixP->fx_where; + + switch (fixP->fx_r_type) + { + case BFD_RELOC_8: + ((bfd_byte *) where)[0] = (bfd_byte) value; + break; + case BFD_RELOC_24: + bfd_putb24 ((bfd_vma) value, (unsigned char *) where); + break; + case BFD_RELOC_32: + bfd_putb32 ((bfd_vma) value, (unsigned char *) where); + break; + case BFD_RELOC_16_PCREL: + if (value < -0x8000 || value > 0x7FFF) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Value out of 16-bit range.")); + + bfd_putb16 ((bfd_vma) value | 0x8000, (unsigned char *) where); + break; + + default: + as_fatal (_("Line %d: unknown relocation type: 0x%x."), + fixP->fx_line, fixP->fx_r_type); + } +} + +/* Set the ELF specific flags. */ +void +s12z_elf_final_processing (void) +{ +} |