| /* tc-m68k.c -- Assemble for the m68k family |
| Copyright 1987, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, |
| 2000, 2001 |
| 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 2, 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, 59 Temple Place - Suite 330, Boston, MA |
| 02111-1307, USA. */ |
| |
| #include <ctype.h> |
| #include "as.h" |
| #include "obstack.h" |
| #include "subsegs.h" |
| #include "dwarf2dbg.h" |
| |
| #include "opcode/m68k.h" |
| #include "m68k-parse.h" |
| |
| #if defined (OBJ_ELF) |
| #include "elf/m68k.h" |
| #endif |
| |
| /* This string holds the chars that always start a comment. If the |
| pre-processor is disabled, these aren't very useful. The macro |
| tc_comment_chars points to this. We use this, rather than the |
| usual comment_chars, so that the --bitwise-or option will work. */ |
| #if defined (TE_SVR4) || defined (TE_DELTA) |
| const char *m68k_comment_chars = "|#"; |
| #else |
| const char *m68k_comment_chars = "|"; |
| #endif |
| |
| /* This array holds the chars that only start a comment at the beginning of |
| a line. If the line seems to have the form '# 123 filename' |
| .line and .file directives will appear in the pre-processed output */ |
| /* Note that input_file.c hand checks for '#' at the beginning of the |
| first line of the input file. This is because the compiler outputs |
| #NO_APP at the beginning of its output. */ |
| /* Also note that comments like this one will always work. */ |
| const char line_comment_chars[] = "#*"; |
| |
| const char line_separator_chars[] = ";"; |
| |
| /* Chars that can be used to separate mant from exp in floating point nums */ |
| CONST char EXP_CHARS[] = "eE"; |
| |
| /* Chars that mean this number is a floating point constant, as |
| in "0f12.456" or "0d1.2345e12". */ |
| |
| CONST char FLT_CHARS[] = "rRsSfFdDxXeEpP"; |
| |
| /* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be |
| changed in read.c . Ideally it shouldn't have to know about it at all, |
| but nothing is ideal around here. */ |
| |
| const int md_reloc_size = 8; /* Size of relocation record */ |
| |
| /* Are we trying to generate PIC code? If so, absolute references |
| ought to be made into linkage table references or pc-relative |
| references. Not implemented. For ELF there are other means |
| to denote pic relocations. */ |
| int flag_want_pic; |
| |
| static int flag_short_refs; /* -l option */ |
| static int flag_long_jumps; /* -S option */ |
| static int flag_keep_pcrel; /* --pcrel option. */ |
| |
| #ifdef REGISTER_PREFIX_OPTIONAL |
| int flag_reg_prefix_optional = REGISTER_PREFIX_OPTIONAL; |
| #else |
| int flag_reg_prefix_optional; |
| #endif |
| |
| /* Whether --register-prefix-optional was used on the command line. */ |
| static int reg_prefix_optional_seen; |
| |
| /* The floating point coprocessor to use by default. */ |
| static enum m68k_register m68k_float_copnum = COP1; |
| |
| /* If this is non-zero, then references to number(%pc) will be taken |
| to refer to number, rather than to %pc + number. */ |
| static int m68k_abspcadd; |
| |
| /* If this is non-zero, then the quick forms of the move, add, and sub |
| instructions are used when possible. */ |
| static int m68k_quick = 1; |
| |
| /* If this is non-zero, then if the size is not specified for a base |
| or outer displacement, the assembler assumes that the size should |
| be 32 bits. */ |
| static int m68k_rel32 = 1; |
| |
| /* This is non-zero if m68k_rel32 was set from the command line. */ |
| static int m68k_rel32_from_cmdline; |
| |
| /* The default width to use for an index register when using a base |
| displacement. */ |
| static enum m68k_size m68k_index_width_default = SIZE_LONG; |
| |
| /* We want to warn if any text labels are misaligned. In order to get |
| the right line number, we need to record the line number for each |
| label. */ |
| |
| struct label_line |
| { |
| struct label_line *next; |
| symbolS *label; |
| char *file; |
| unsigned int line; |
| int text; |
| }; |
| |
| /* The list of labels. */ |
| |
| static struct label_line *labels; |
| |
| /* The current label. */ |
| |
| static struct label_line *current_label; |
| |
| /* Its an arbitrary name: This means I don't approve of it */ |
| /* See flames below */ |
| static struct obstack robyn; |
| |
| struct m68k_incant |
| { |
| const char *m_operands; |
| unsigned long m_opcode; |
| short m_opnum; |
| short m_codenum; |
| int m_arch; |
| struct m68k_incant *m_next; |
| }; |
| |
| #define getone(x) ((((x)->m_opcode)>>16)&0xffff) |
| #define gettwo(x) (((x)->m_opcode)&0xffff) |
| |
| static const enum m68k_register m68000_control_regs[] = { 0 }; |
| static const enum m68k_register m68010_control_regs[] = { |
| SFC, DFC, USP, VBR, |
| 0 |
| }; |
| static const enum m68k_register m68020_control_regs[] = { |
| SFC, DFC, USP, VBR, CACR, CAAR, MSP, ISP, |
| 0 |
| }; |
| static const enum m68k_register m68040_control_regs[] = { |
| SFC, DFC, CACR, TC, ITT0, ITT1, DTT0, DTT1, |
| USP, VBR, MSP, ISP, MMUSR, URP, SRP, |
| 0 |
| }; |
| static const enum m68k_register m68060_control_regs[] = { |
| SFC, DFC, CACR, TC, ITT0, ITT1, DTT0, DTT1, BUSCR, |
| USP, VBR, URP, SRP, PCR, |
| 0 |
| }; |
| static const enum m68k_register mcf_control_regs[] = { |
| CACR, TC, ITT0, ITT1, DTT0, DTT1, VBR, ROMBAR, |
| RAMBAR0, RAMBAR1, MBAR, |
| 0 |
| }; |
| #define cpu32_control_regs m68010_control_regs |
| |
| static const enum m68k_register *control_regs; |
| |
| /* internal form of a 68020 instruction */ |
| struct m68k_it |
| { |
| const char *error; |
| const char *args; /* list of opcode info */ |
| int numargs; |
| |
| int numo; /* Number of shorts in opcode */ |
| short opcode[11]; |
| |
| struct m68k_op operands[6]; |
| |
| int nexp; /* number of exprs in use */ |
| struct m68k_exp exprs[4]; |
| |
| int nfrag; /* Number of frags we have to produce */ |
| struct |
| { |
| int fragoff; /* Where in the current opcode the frag ends */ |
| symbolS *fadd; |
| offsetT foff; |
| int fragty; |
| } |
| fragb[4]; |
| |
| int nrel; /* Num of reloc strucs in use */ |
| struct |
| { |
| int n; |
| expressionS exp; |
| char wid; |
| char pcrel; |
| /* In a pc relative address the difference between the address |
| of the offset and the address that the offset is relative |
| to. This depends on the addressing mode. Basically this |
| is the value to put in the offset field to address the |
| first byte of the offset, without regarding the special |
| significance of some values (in the branch instruction, for |
| example). */ |
| int pcrel_fix; |
| #ifdef OBJ_ELF |
| /* Whether this expression needs special pic relocation, and if |
| so, which. */ |
| enum pic_relocation pic_reloc; |
| #endif |
| } |
| reloc[5]; /* Five is enough??? */ |
| }; |
| |
| #define cpu_of_arch(x) ((x) & (m68000up|mcf)) |
| #define float_of_arch(x) ((x) & mfloat) |
| #define mmu_of_arch(x) ((x) & mmmu) |
| #define arch_coldfire_p(x) (((x) & mcf) != 0) |
| |
| /* Macros for determining if cpu supports a specific addressing mode */ |
| #define HAVE_LONG_BRANCH(x) ((x) & (m68020|m68030|m68040|m68060|cpu32|mcf5407)) |
| |
| static struct m68k_it the_ins; /* the instruction being assembled */ |
| |
| #define op(ex) ((ex)->exp.X_op) |
| #define adds(ex) ((ex)->exp.X_add_symbol) |
| #define subs(ex) ((ex)->exp.X_op_symbol) |
| #define offs(ex) ((ex)->exp.X_add_number) |
| |
| /* Macros for adding things to the m68k_it struct */ |
| |
| #define addword(w) the_ins.opcode[the_ins.numo++]=(w) |
| |
| /* Static functions. */ |
| |
| static void insop PARAMS ((int, const struct m68k_incant *)); |
| static void add_fix PARAMS ((int, struct m68k_exp *, int, int)); |
| static void add_frag PARAMS ((symbolS *, offsetT, int)); |
| |
| /* Like addword, but goes BEFORE general operands */ |
| static void |
| insop (w, opcode) |
| int w; |
| const struct m68k_incant *opcode; |
| { |
| int z; |
| for(z=the_ins.numo;z>opcode->m_codenum;--z) |
| the_ins.opcode[z]=the_ins.opcode[z-1]; |
| for(z=0;z<the_ins.nrel;z++) |
| the_ins.reloc[z].n+=2; |
| for (z = 0; z < the_ins.nfrag; z++) |
| the_ins.fragb[z].fragoff++; |
| the_ins.opcode[opcode->m_codenum]=w; |
| the_ins.numo++; |
| } |
| |
| /* The numo+1 kludge is so we can hit the low order byte of the prev word. |
| Blecch. */ |
| static void |
| add_fix (width, exp, pc_rel, pc_fix) |
| int width; |
| struct m68k_exp *exp; |
| int pc_rel; |
| int pc_fix; |
| { |
| the_ins.reloc[the_ins.nrel].n = ((width == 'B' || width == '3') |
| ? (the_ins.numo*2-1) |
| : (((width)=='b') |
| ? (the_ins.numo*2+1) |
| : (the_ins.numo*2))); |
| the_ins.reloc[the_ins.nrel].exp = exp->exp; |
| the_ins.reloc[the_ins.nrel].wid = width; |
| the_ins.reloc[the_ins.nrel].pcrel_fix = pc_fix; |
| #ifdef OBJ_ELF |
| the_ins.reloc[the_ins.nrel].pic_reloc = exp->pic_reloc; |
| #endif |
| the_ins.reloc[the_ins.nrel++].pcrel = pc_rel; |
| } |
| |
| /* Cause an extra frag to be generated here, inserting up to 10 bytes |
| (that value is chosen in the frag_var call in md_assemble). TYPE |
| is the subtype of the frag to be generated; its primary type is |
| rs_machine_dependent. |
| |
| The TYPE parameter is also used by md_convert_frag_1 and |
| md_estimate_size_before_relax. The appropriate type of fixup will |
| be emitted by md_convert_frag_1. |
| |
| ADD becomes the FR_SYMBOL field of the frag, and OFF the FR_OFFSET. */ |
| static void |
| add_frag (add, off, type) |
| symbolS *add; |
| offsetT off; |
| int type; |
| { |
| the_ins.fragb[the_ins.nfrag].fragoff=the_ins.numo; |
| the_ins.fragb[the_ins.nfrag].fadd=add; |
| the_ins.fragb[the_ins.nfrag].foff=off; |
| the_ins.fragb[the_ins.nfrag++].fragty=type; |
| } |
| |
| #define isvar(ex) \ |
| (op (ex) != O_constant && op (ex) != O_big) |
| |
| static char *crack_operand PARAMS ((char *str, struct m68k_op *opP)); |
| static int get_num PARAMS ((struct m68k_exp *exp, int ok)); |
| static void m68k_ip PARAMS ((char *)); |
| static void insert_reg PARAMS ((const char *, int)); |
| static void select_control_regs PARAMS ((void)); |
| static void init_regtable PARAMS ((void)); |
| static int reverse_16_bits PARAMS ((int in)); |
| static int reverse_8_bits PARAMS ((int in)); |
| static void install_gen_operand PARAMS ((int mode, int val)); |
| static void install_operand PARAMS ((int mode, int val)); |
| static void s_bss PARAMS ((int)); |
| static void s_data1 PARAMS ((int)); |
| static void s_data2 PARAMS ((int)); |
| static void s_even PARAMS ((int)); |
| static void s_proc PARAMS ((int)); |
| static void mri_chip PARAMS ((void)); |
| static void s_chip PARAMS ((int)); |
| static void s_fopt PARAMS ((int)); |
| static void s_opt PARAMS ((int)); |
| static void s_reg PARAMS ((int)); |
| static void s_restore PARAMS ((int)); |
| static void s_save PARAMS ((int)); |
| static void s_mri_if PARAMS ((int)); |
| static void s_mri_else PARAMS ((int)); |
| static void s_mri_endi PARAMS ((int)); |
| static void s_mri_break PARAMS ((int)); |
| static void s_mri_next PARAMS ((int)); |
| static void s_mri_for PARAMS ((int)); |
| static void s_mri_endf PARAMS ((int)); |
| static void s_mri_repeat PARAMS ((int)); |
| static void s_mri_until PARAMS ((int)); |
| static void s_mri_while PARAMS ((int)); |
| static void s_mri_endw PARAMS ((int)); |
| static void md_apply_fix_2 PARAMS ((fixS *, offsetT)); |
| static void md_convert_frag_1 PARAMS ((fragS *)); |
| |
| static int current_architecture; |
| |
| struct m68k_cpu |
| { |
| unsigned long arch; |
| const char *name; |
| int alias; |
| }; |
| |
| static const struct m68k_cpu archs[] = |
| { |
| { m68000, "68000", 0 }, |
| { m68010, "68010", 0 }, |
| { m68020, "68020", 0 }, |
| { m68030, "68030", 0 }, |
| { m68040, "68040", 0 }, |
| { m68060, "68060", 0 }, |
| { cpu32, "cpu32", 0 }, |
| { m68881, "68881", 0 }, |
| { m68851, "68851", 0 }, |
| { mcf5200, "5200", 0 }, |
| { mcf5206e, "5206e", 0 }, |
| { mcf5307, "5307", 0}, |
| { mcf5407, "5407", 0}, |
| /* Aliases (effectively, so far as gas is concerned) for the above |
| cpus. */ |
| { m68020, "68k", 1 }, |
| { m68000, "68008", 1 }, |
| { m68000, "68302", 1 }, |
| { m68000, "68306", 1 }, |
| { m68000, "68307", 1 }, |
| { m68000, "68322", 1 }, |
| { m68000, "68356", 1 }, |
| { m68000, "68ec000", 1 }, |
| { m68000, "68hc000", 1 }, |
| { m68000, "68hc001", 1 }, |
| { m68020, "68ec020", 1 }, |
| { m68030, "68ec030", 1 }, |
| { m68040, "68ec040", 1 }, |
| { m68060, "68ec060", 1 }, |
| { cpu32, "68330", 1 }, |
| { cpu32, "68331", 1 }, |
| { cpu32, "68332", 1 }, |
| { cpu32, "68333", 1 }, |
| { cpu32, "68334", 1 }, |
| { cpu32, "68336", 1 }, |
| { cpu32, "68340", 1 }, |
| { cpu32, "68341", 1 }, |
| { cpu32, "68349", 1 }, |
| { cpu32, "68360", 1 }, |
| { m68881, "68882", 1 }, |
| { mcf5200, "5202", 1 }, |
| { mcf5200, "5204", 1 }, |
| { mcf5200, "5206", 1 }, |
| }; |
| |
| static const int n_archs = sizeof (archs) / sizeof (archs[0]); |
| |
| /* This is the assembler relaxation table for m68k. m68k is a rich CISC |
| architecture and we have a lot of relaxation modes. */ |
| |
| /* Macros used in the relaxation code. */ |
| #define TAB(x,y) (((x) << 2) + (y)) |
| #define TABTYPE(x) ((x) >> 2) |
| |
| /* Relaxation states. */ |
| #define BYTE 0 |
| #define SHORT 1 |
| #define LONG 2 |
| #define SZ_UNDEF 3 |
| |
| /* Here are all the relaxation modes we support. First we can relax ordinary |
| branches. On 68020 and higher and on CPU32 all branch instructions take |
| three forms, so on these CPUs all branches always remain as such. When we |
| have to expand to the LONG form on a 68000, though, we substitute an |
| absolute jump instead. This is a direct replacement for unconditional |
| branches and a branch over a jump for conditional branches. However, if the |
| user requires PIC and disables this with --pcrel, we can only relax between |
| BYTE and SHORT forms, punting if that isn't enough. This gives us four |
| different relaxation modes for branches: */ |
| |
| #define BRANCHBWL 1 /* branch byte, word, or long */ |
| #define BRABSJUNC 2 /* absolute jump for LONG, unconditional */ |
| #define BRABSJCOND 3 /* absolute jump for LONG, conditional */ |
| #define BRANCHBW 4 /* branch byte or word */ |
| |
| /* We also relax coprocessor branches and DBcc's. All CPUs that support |
| coprocessor branches support them in word and long forms, so we have only |
| one relaxation mode for them. DBcc's are word only on all CPUs. We can |
| relax them to the LONG form with a branch-around sequence. This sequence |
| can use a long branch (if available) or an absolute jump (if acceptable). |
| This gives us two relaxation modes. If long branches are not available and |
| absolute jumps are not acceptable, we don't relax DBcc's. */ |
| |
| #define FBRANCH 5 /* coprocessor branch */ |
| #define DBCCLBR 6 /* DBcc relaxable with a long branch */ |
| #define DBCCABSJ 7 /* DBcc relaxable with an absolute jump */ |
| |
| /* That's all for instruction relaxation. However, we also relax PC-relative |
| operands. Specifically, we have three operand relaxation modes. On the |
| 68000 PC-relative operands can only be 16-bit, but on 68020 and higher and |
| on CPU32 they may be 16-bit or 32-bit. For the latter we relax between the |
| two. Also PC+displacement+index operands in their simple form (with a non- |
| suppressed index without memory indirection) are supported on all CPUs, but |
| on the 68000 the displacement can be 8-bit only, whereas on 68020 and higher |
| and on CPU32 we relax it to SHORT and LONG forms as well using the extended |
| form of the PC+displacement+index operand. Finally, some absolute operands |
| can be relaxed down to 16-bit PC-relative. */ |
| |
| #define PCREL1632 8 /* 16-bit or 32-bit PC-relative */ |
| #define PCINDEX 9 /* PC+displacement+index */ |
| #define ABSTOPCREL 10 /* absolute relax down to 16-bit PC-relative */ |
| |
| /* Note that calls to frag_var need to specify the maximum expansion |
| needed; this is currently 10 bytes for DBCC. */ |
| |
| /* The fields are: |
| How far Forward this mode will reach: |
| How far Backward this mode will reach: |
| How many bytes this mode will add to the size of the frag |
| Which mode to go to if the offset won't fit in this one |
| */ |
| relax_typeS md_relax_table[] = |
| { |
| {1, 1, 0, 0}, /* First entries aren't used */ |
| {1, 1, 0, 0}, /* For no good reason except */ |
| {1, 1, 0, 0}, /* that the VAX doesn't either */ |
| {1, 1, 0, 0}, |
| |
| {(127), (-128), 0, TAB (BRANCHBWL, SHORT)}, |
| {(32767), (-32768), 2, TAB (BRANCHBWL, LONG)}, |
| {0, 0, 4, 0}, |
| {1, 1, 0, 0}, |
| |
| {(127), (-128), 0, TAB (BRABSJUNC, SHORT)}, |
| {(32767), (-32768), 2, TAB (BRABSJUNC, LONG)}, |
| {0, 0, 4, 0}, |
| {1, 1, 0, 0}, |
| |
| {(127), (-128), 0, TAB (BRABSJCOND, SHORT)}, |
| {(32767), (-32768), 2, TAB (BRABSJCOND, LONG)}, |
| {0, 0, 6, 0}, |
| {1, 1, 0, 0}, |
| |
| {(127), (-128), 0, TAB (BRANCHBW, SHORT)}, |
| {0, 0, 2, 0}, |
| {1, 1, 0, 0}, |
| {1, 1, 0, 0}, |
| |
| {1, 1, 0, 0}, /* FBRANCH doesn't come BYTE */ |
| {(32767), (-32768), 2, TAB (FBRANCH, LONG)}, |
| {0, 0, 4, 0}, |
| {1, 1, 0, 0}, |
| |
| {1, 1, 0, 0}, /* DBCC doesn't come BYTE */ |
| {(32767), (-32768), 2, TAB (DBCCLBR, LONG)}, |
| {0, 0, 10, 0}, |
| {1, 1, 0, 0}, |
| |
| {1, 1, 0, 0}, /* DBCC doesn't come BYTE */ |
| {(32767), (-32768), 2, TAB (DBCCABSJ, LONG)}, |
| {0, 0, 10, 0}, |
| {1, 1, 0, 0}, |
| |
| {1, 1, 0, 0}, /* PCREL1632 doesn't come BYTE */ |
| {32767, -32768, 2, TAB (PCREL1632, LONG)}, |
| {0, 0, 6, 0}, |
| {1, 1, 0, 0}, |
| |
| {125, -130, 0, TAB (PCINDEX, SHORT)}, |
| {32765, -32770, 2, TAB (PCINDEX, LONG)}, |
| {0, 0, 4, 0}, |
| {1, 1, 0, 0}, |
| |
| {1, 1, 0, 0}, /* ABSTOPCREL doesn't come BYTE */ |
| {(32767), (-32768), 2, TAB (ABSTOPCREL, LONG)}, |
| {0, 0, 4, 0}, |
| {1, 1, 0, 0}, |
| }; |
| |
| /* These are the machine dependent pseudo-ops. These are included so |
| the assembler can work on the output from the SUN C compiler, which |
| generates these. |
| */ |
| |
| /* 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[] = |
| { |
| {"data1", s_data1, 0}, |
| {"data2", s_data2, 0}, |
| {"bss", s_bss, 0}, |
| {"even", s_even, 0}, |
| {"skip", s_space, 0}, |
| {"proc", s_proc, 0}, |
| #if defined (TE_SUN3) || defined (OBJ_ELF) |
| {"align", s_align_bytes, 0}, |
| #endif |
| #ifdef OBJ_ELF |
| {"swbeg", s_ignore, 0}, |
| #endif |
| {"extend", float_cons, 'x'}, |
| {"ldouble", float_cons, 'x'}, |
| |
| #ifdef OBJ_ELF |
| /* Dwarf2 support for Gcc. */ |
| {"file", dwarf2_directive_file, 0}, |
| {"loc", dwarf2_directive_loc, 0}, |
| #endif |
| |
| /* The following pseudo-ops are supported for MRI compatibility. */ |
| {"chip", s_chip, 0}, |
| {"comline", s_space, 1}, |
| {"fopt", s_fopt, 0}, |
| {"mask2", s_ignore, 0}, |
| {"opt", s_opt, 0}, |
| {"reg", s_reg, 0}, |
| {"restore", s_restore, 0}, |
| {"save", s_save, 0}, |
| |
| {"if", s_mri_if, 0}, |
| {"if.b", s_mri_if, 'b'}, |
| {"if.w", s_mri_if, 'w'}, |
| {"if.l", s_mri_if, 'l'}, |
| {"else", s_mri_else, 0}, |
| {"else.s", s_mri_else, 's'}, |
| {"else.l", s_mri_else, 'l'}, |
| {"endi", s_mri_endi, 0}, |
| {"break", s_mri_break, 0}, |
| {"break.s", s_mri_break, 's'}, |
| {"break.l", s_mri_break, 'l'}, |
| {"next", s_mri_next, 0}, |
| {"next.s", s_mri_next, 's'}, |
| {"next.l", s_mri_next, 'l'}, |
| {"for", s_mri_for, 0}, |
| {"for.b", s_mri_for, 'b'}, |
| {"for.w", s_mri_for, 'w'}, |
| {"for.l", s_mri_for, 'l'}, |
| {"endf", s_mri_endf, 0}, |
| {"repeat", s_mri_repeat, 0}, |
| {"until", s_mri_until, 0}, |
| {"until.b", s_mri_until, 'b'}, |
| {"until.w", s_mri_until, 'w'}, |
| {"until.l", s_mri_until, 'l'}, |
| {"while", s_mri_while, 0}, |
| {"while.b", s_mri_while, 'b'}, |
| {"while.w", s_mri_while, 'w'}, |
| {"while.l", s_mri_while, 'l'}, |
| {"endw", s_mri_endw, 0}, |
| |
| {0, 0, 0} |
| }; |
| |
| /* The mote pseudo ops are put into the opcode table, since they |
| don't start with a . they look like opcodes to gas. |
| */ |
| |
| #ifdef M68KCOFF |
| extern void obj_coff_section PARAMS ((int)); |
| #endif |
| |
| CONST pseudo_typeS mote_pseudo_table[] = |
| { |
| |
| {"dcl", cons, 4}, |
| {"dc", cons, 2}, |
| {"dcw", cons, 2}, |
| {"dcb", cons, 1}, |
| |
| {"dsl", s_space, 4}, |
| {"ds", s_space, 2}, |
| {"dsw", s_space, 2}, |
| {"dsb", s_space, 1}, |
| |
| {"xdef", s_globl, 0}, |
| #ifdef OBJ_ELF |
| {"align", s_align_bytes, 0}, |
| #else |
| {"align", s_align_ptwo, 0}, |
| #endif |
| #ifdef M68KCOFF |
| {"sect", obj_coff_section, 0}, |
| {"section", obj_coff_section, 0}, |
| #endif |
| {0, 0, 0} |
| }; |
| |
| #define issbyte(x) ((x)>=-128 && (x)<=127) |
| #define isubyte(x) ((x)>=0 && (x)<=255) |
| #define issword(x) ((x)>=-32768 && (x)<=32767) |
| #define isuword(x) ((x)>=0 && (x)<=65535) |
| |
| #define isbyte(x) ((x)>= -255 && (x)<=255) |
| #define isword(x) ((x)>=-65536 && (x)<=65535) |
| #define islong(x) (1) |
| |
| extern char *input_line_pointer; |
| |
| static char mklower_table[256]; |
| #define mklower(c) (mklower_table[(unsigned char) (c)]) |
| static char notend_table[256]; |
| static char alt_notend_table[256]; |
| #define notend(s) \ |
| (! (notend_table[(unsigned char) *s] \ |
| || (*s == ':' \ |
| && alt_notend_table[(unsigned char) s[1]]))) |
| |
| #if defined (M68KCOFF) && !defined (BFD_ASSEMBLER) |
| |
| #ifdef NO_PCREL_RELOCS |
| |
| int |
| make_pcrel_absolute(fixP, add_number) |
| fixS *fixP; |
| long *add_number; |
| { |
| register unsigned char *opcode = fixP->fx_frag->fr_opcode; |
| |
| /* rewrite the PC relative instructions to absolute address ones. |
| * these are rumoured to be faster, and the apollo linker refuses |
| * to deal with the PC relative relocations. |
| */ |
| if (opcode[0] == 0x60 && opcode[1] == 0xff) /* BRA -> JMP */ |
| { |
| opcode[0] = 0x4e; |
| opcode[1] = 0xf9; |
| } |
| else if (opcode[0] == 0x61 && opcode[1] == 0xff) /* BSR -> JSR */ |
| { |
| opcode[0] = 0x4e; |
| opcode[1] = 0xb9; |
| } |
| else |
| as_fatal (_("Unknown PC relative instruction")); |
| *add_number -= 4; |
| return 0; |
| } |
| |
| #endif /* NO_PCREL_RELOCS */ |
| |
| short |
| tc_coff_fix2rtype (fixP) |
| fixS *fixP; |
| { |
| if (fixP->fx_tcbit && fixP->fx_size == 4) |
| return R_RELLONG_NEG; |
| #ifdef NO_PCREL_RELOCS |
| know (fixP->fx_pcrel == 0); |
| return (fixP->fx_size == 1 ? R_RELBYTE |
| : fixP->fx_size == 2 ? R_DIR16 |
| : R_DIR32); |
| #else |
| return (fixP->fx_pcrel ? |
| (fixP->fx_size == 1 ? R_PCRBYTE : |
| fixP->fx_size == 2 ? R_PCRWORD : |
| R_PCRLONG) : |
| (fixP->fx_size == 1 ? R_RELBYTE : |
| fixP->fx_size == 2 ? R_RELWORD : |
| R_RELLONG)); |
| #endif |
| } |
| |
| #endif |
| |
| #ifdef OBJ_ELF |
| |
| /* Return zero if the reference to SYMBOL from within the same segment may |
| be relaxed. */ |
| |
| /* On an ELF system, we can't relax an externally visible symbol, |
| because it may be overridden by a shared library. However, if |
| TARGET_OS is "elf", then we presume that we are assembling for an |
| embedded system, in which case we don't have to worry about shared |
| libraries, and we can relax any external sym. */ |
| |
| #define relaxable_symbol(symbol) \ |
| (!((S_IS_EXTERNAL (symbol) && strcmp (TARGET_OS, "elf") != 0) \ |
| || S_IS_WEAK (symbol))) |
| |
| /* Compute the relocation code for a fixup of SIZE bytes, using pc |
| relative relocation if PCREL is non-zero. PIC says whether a special |
| pic relocation was requested. */ |
| |
| static bfd_reloc_code_real_type get_reloc_code |
| PARAMS ((int, int, enum pic_relocation)); |
| |
| static bfd_reloc_code_real_type |
| get_reloc_code (size, pcrel, pic) |
| int size; |
| int pcrel; |
| enum pic_relocation pic; |
| { |
| switch (pic) |
| { |
| case pic_got_pcrel: |
| switch (size) |
| { |
| case 1: |
| return BFD_RELOC_8_GOT_PCREL; |
| case 2: |
| return BFD_RELOC_16_GOT_PCREL; |
| case 4: |
| return BFD_RELOC_32_GOT_PCREL; |
| } |
| break; |
| |
| case pic_got_off: |
| switch (size) |
| { |
| case 1: |
| return BFD_RELOC_8_GOTOFF; |
| case 2: |
| return BFD_RELOC_16_GOTOFF; |
| case 4: |
| return BFD_RELOC_32_GOTOFF; |
| } |
| break; |
| |
| case pic_plt_pcrel: |
| switch (size) |
| { |
| case 1: |
| return BFD_RELOC_8_PLT_PCREL; |
| case 2: |
| return BFD_RELOC_16_PLT_PCREL; |
| case 4: |
| return BFD_RELOC_32_PLT_PCREL; |
| } |
| break; |
| |
| case pic_plt_off: |
| switch (size) |
| { |
| case 1: |
| return BFD_RELOC_8_PLTOFF; |
| case 2: |
| return BFD_RELOC_16_PLTOFF; |
| case 4: |
| return BFD_RELOC_32_PLTOFF; |
| } |
| break; |
| |
| case pic_none: |
| if (pcrel) |
| { |
| switch (size) |
| { |
| case 1: |
| return BFD_RELOC_8_PCREL; |
| case 2: |
| return BFD_RELOC_16_PCREL; |
| case 4: |
| return BFD_RELOC_32_PCREL; |
| } |
| } |
| else |
| { |
| switch (size) |
| { |
| case 1: |
| return BFD_RELOC_8; |
| case 2: |
| return BFD_RELOC_16; |
| case 4: |
| return BFD_RELOC_32; |
| } |
| } |
| } |
| |
| if (pcrel) |
| { |
| if (pic == pic_none) |
| as_bad (_("Can not do %d byte pc-relative relocation"), size); |
| else |
| as_bad (_("Can not do %d byte pc-relative pic relocation"), size); |
| } |
| else |
| { |
| if (pic == pic_none) |
| as_bad (_("Can not do %d byte relocation"), size); |
| else |
| as_bad (_("Can not do %d byte pic relocation"), size); |
| } |
| |
| return BFD_RELOC_NONE; |
| } |
| |
| /* 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 dynamic relocations are done |
| correctly, so in some cases we force the original symbol to be |
| used. */ |
| int |
| tc_m68k_fix_adjustable (fixP) |
| fixS *fixP; |
| { |
| /* Prevent all adjustments to global symbols. */ |
| if (! relaxable_symbol (fixP->fx_addsy)) |
| return 0; |
| |
| /* adjust_reloc_syms doesn't know about the GOT */ |
| switch (fixP->fx_r_type) |
| { |
| case BFD_RELOC_8_GOT_PCREL: |
| case BFD_RELOC_16_GOT_PCREL: |
| case BFD_RELOC_32_GOT_PCREL: |
| case BFD_RELOC_8_GOTOFF: |
| case BFD_RELOC_16_GOTOFF: |
| case BFD_RELOC_32_GOTOFF: |
| case BFD_RELOC_8_PLT_PCREL: |
| case BFD_RELOC_16_PLT_PCREL: |
| case BFD_RELOC_32_PLT_PCREL: |
| case BFD_RELOC_8_PLTOFF: |
| case BFD_RELOC_16_PLTOFF: |
| case BFD_RELOC_32_PLTOFF: |
| return 0; |
| |
| case BFD_RELOC_VTABLE_INHERIT: |
| case BFD_RELOC_VTABLE_ENTRY: |
| return 0; |
| |
| default: |
| return 1; |
| } |
| } |
| |
| #else /* !OBJ_ELF */ |
| |
| #define get_reloc_code(SIZE,PCREL,OTHER) NO_RELOC |
| |
| #define relaxable_symbol(symbol) 1 |
| |
| #endif /* OBJ_ELF */ |
| |
| #ifdef BFD_ASSEMBLER |
| |
| arelent * |
| tc_gen_reloc (section, fixp) |
| asection *section; |
| fixS *fixp; |
| { |
| arelent *reloc; |
| bfd_reloc_code_real_type code; |
| |
| /* If the tcbit is set, then this was a fixup of a negative value |
| that was never resolved. We do not have a reloc to handle this, |
| so just return. We assume that other code will have detected this |
| situation and produced a helpful error message, so we just tell the |
| user that the reloc cannot be produced. */ |
| if (fixp->fx_tcbit) |
| { |
| if (fixp->fx_addsy) |
| as_bad (_("Unable to produce reloc against symbol '%s'"), |
| S_GET_NAME (fixp->fx_addsy)); |
| return NULL; |
| } |
| |
| if (fixp->fx_r_type != BFD_RELOC_NONE) |
| { |
| code = fixp->fx_r_type; |
| |
| /* Since DIFF_EXPR_OK is defined in tc-m68k.h, it is possible |
| that fixup_segment converted a non-PC relative reloc into a |
| PC relative reloc. In such a case, we need to convert the |
| reloc code. */ |
| if (fixp->fx_pcrel) |
| { |
| switch (code) |
| { |
| case BFD_RELOC_8: |
| code = BFD_RELOC_8_PCREL; |
| break; |
| case BFD_RELOC_16: |
| code = BFD_RELOC_16_PCREL; |
| break; |
| case BFD_RELOC_32: |
| code = BFD_RELOC_32_PCREL; |
| break; |
| case BFD_RELOC_8_PCREL: |
| case BFD_RELOC_16_PCREL: |
| case BFD_RELOC_32_PCREL: |
| case BFD_RELOC_8_GOT_PCREL: |
| case BFD_RELOC_16_GOT_PCREL: |
| case BFD_RELOC_32_GOT_PCREL: |
| case BFD_RELOC_8_GOTOFF: |
| case BFD_RELOC_16_GOTOFF: |
| case BFD_RELOC_32_GOTOFF: |
| case BFD_RELOC_8_PLT_PCREL: |
| case BFD_RELOC_16_PLT_PCREL: |
| case BFD_RELOC_32_PLT_PCREL: |
| case BFD_RELOC_8_PLTOFF: |
| case BFD_RELOC_16_PLTOFF: |
| case BFD_RELOC_32_PLTOFF: |
| break; |
| default: |
| as_bad_where (fixp->fx_file, fixp->fx_line, |
| _("Cannot make %s relocation PC relative"), |
| bfd_get_reloc_code_name (code)); |
| } |
| } |
| } |
| else |
| { |
| #define F(SZ,PCREL) (((SZ) << 1) + (PCREL)) |
| switch (F (fixp->fx_size, fixp->fx_pcrel)) |
| { |
| #define MAP(SZ,PCREL,TYPE) case F(SZ,PCREL): code = (TYPE); break |
| MAP (1, 0, BFD_RELOC_8); |
| MAP (2, 0, BFD_RELOC_16); |
| MAP (4, 0, BFD_RELOC_32); |
| MAP (1, 1, BFD_RELOC_8_PCREL); |
| MAP (2, 1, BFD_RELOC_16_PCREL); |
| MAP (4, 1, BFD_RELOC_32_PCREL); |
| default: |
| abort (); |
| } |
| } |
| #undef F |
| #undef MAP |
| |
| reloc = (arelent *) xmalloc (sizeof (arelent)); |
| reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); |
| *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); |
| reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; |
| #ifndef OBJ_ELF |
| if (fixp->fx_pcrel) |
| reloc->addend = fixp->fx_addnumber; |
| else |
| reloc->addend = 0; |
| #else |
| if (!fixp->fx_pcrel) |
| reloc->addend = fixp->fx_addnumber; |
| else |
| reloc->addend = (section->vma |
| /* Explicit sign extension in case char is |
| unsigned. */ |
| + ((fixp->fx_pcrel_adjust & 0xff) ^ 0x80) - 0x80 |
| + fixp->fx_addnumber |
| + md_pcrel_from (fixp)); |
| #endif |
| |
| reloc->howto = bfd_reloc_type_lookup (stdoutput, code); |
| assert (reloc->howto != 0); |
| |
| return reloc; |
| } |
| |
| #endif /* BFD_ASSEMBLER */ |
| |
| /* Handle of the OPCODE hash table. NULL means any use before |
| m68k_ip_begin() will crash. */ |
| static struct hash_control *op_hash; |
| |
| /* Assemble an m68k instruction. */ |
| |
| static void |
| m68k_ip (instring) |
| char *instring; |
| { |
| register char *p; |
| register struct m68k_op *opP; |
| register const struct m68k_incant *opcode; |
| register const char *s; |
| register int tmpreg = 0, baseo = 0, outro = 0, nextword; |
| char *pdot, *pdotmove; |
| enum m68k_size siz1, siz2; |
| char c; |
| int losing; |
| int opsfound; |
| LITTLENUM_TYPE words[6]; |
| LITTLENUM_TYPE *wordp; |
| unsigned long ok_arch = 0; |
| |
| if (*instring == ' ') |
| instring++; /* skip leading whitespace */ |
| |
| /* Scan up to end of operation-code, which MUST end in end-of-string |
| or exactly 1 space. */ |
| pdot = 0; |
| for (p = instring; *p != '\0'; p++) |
| { |
| if (*p == ' ') |
| break; |
| if (*p == '.') |
| pdot = p; |
| } |
| |
| if (p == instring) |
| { |
| the_ins.error = _("No operator"); |
| return; |
| } |
| |
| /* p now points to the end of the opcode name, probably whitespace. |
| Make sure the name is null terminated by clobbering the |
| whitespace, look it up in the hash table, then fix it back. |
| Remove a dot, first, since the opcode tables have none. */ |
| if (pdot != NULL) |
| { |
| for (pdotmove = pdot; pdotmove < p; pdotmove++) |
| *pdotmove = pdotmove[1]; |
| p--; |
| } |
| |
| c = *p; |
| *p = '\0'; |
| opcode = (const struct m68k_incant *) hash_find (op_hash, instring); |
| *p = c; |
| |
| if (pdot != NULL) |
| { |
| for (pdotmove = p; pdotmove > pdot; pdotmove--) |
| *pdotmove = pdotmove[-1]; |
| *pdot = '.'; |
| ++p; |
| } |
| |
| if (opcode == NULL) |
| { |
| the_ins.error = _("Unknown operator"); |
| return; |
| } |
| |
| /* found a legitimate opcode, start matching operands */ |
| while (*p == ' ') |
| ++p; |
| |
| if (opcode->m_operands == 0) |
| { |
| char *old = input_line_pointer; |
| *old = '\n'; |
| input_line_pointer = p; |
| /* Ahh - it's a motorola style psuedo op */ |
| mote_pseudo_table[opcode->m_opnum].poc_handler |
| (mote_pseudo_table[opcode->m_opnum].poc_val); |
| input_line_pointer = old; |
| *old = 0; |
| |
| return; |
| } |
| |
| if (flag_mri && opcode->m_opnum == 0) |
| { |
| /* In MRI mode, random garbage is allowed after an instruction |
| which accepts no operands. */ |
| the_ins.args = opcode->m_operands; |
| the_ins.numargs = opcode->m_opnum; |
| the_ins.numo = opcode->m_codenum; |
| the_ins.opcode[0] = getone (opcode); |
| the_ins.opcode[1] = gettwo (opcode); |
| return; |
| } |
| |
| for (opP = &the_ins.operands[0]; *p; opP++) |
| { |
| p = crack_operand (p, opP); |
| |
| if (opP->error) |
| { |
| the_ins.error = opP->error; |
| return; |
| } |
| } |
| |
| opsfound = opP - &the_ins.operands[0]; |
| |
| /* This ugly hack is to support the floating pt opcodes in their |
| standard form. Essentially, we fake a first enty of type COP#1 */ |
| if (opcode->m_operands[0] == 'I') |
| { |
| int n; |
| |
| for (n = opsfound; n > 0; --n) |
| the_ins.operands[n] = the_ins.operands[n - 1]; |
| |
| memset ((char *) (&the_ins.operands[0]), '\0', |
| sizeof (the_ins.operands[0])); |
| the_ins.operands[0].mode = CONTROL; |
| the_ins.operands[0].reg = m68k_float_copnum; |
| opsfound++; |
| } |
| |
| /* We've got the operands. Find an opcode that'll accept them */ |
| for (losing = 0;;) |
| { |
| /* If we didn't get the right number of ops, or we have no |
| common model with this pattern then reject this pattern. */ |
| |
| ok_arch |= opcode->m_arch; |
| if (opsfound != opcode->m_opnum |
| || ((opcode->m_arch & current_architecture) == 0)) |
| ++losing; |
| else |
| { |
| for (s = opcode->m_operands, opP = &the_ins.operands[0]; |
| *s && !losing; |
| s += 2, opP++) |
| { |
| /* Warning: this switch is huge! */ |
| /* I've tried to organize the cases into this order: |
| non-alpha first, then alpha by letter. Lower-case |
| goes directly before uppercase counterpart. */ |
| /* Code with multiple case ...: gets sorted by the lowest |
| case ... it belongs to. I hope this makes sense. */ |
| switch (*s) |
| { |
| case '!': |
| switch (opP->mode) |
| { |
| case IMMED: |
| case DREG: |
| case AREG: |
| case FPREG: |
| case CONTROL: |
| case AINC: |
| case ADEC: |
| case REGLST: |
| losing++; |
| break; |
| default: |
| break; |
| } |
| break; |
| |
| case '<': |
| switch (opP->mode) |
| { |
| case DREG: |
| case AREG: |
| case FPREG: |
| case CONTROL: |
| case IMMED: |
| case ADEC: |
| case REGLST: |
| losing++; |
| break; |
| default: |
| break; |
| } |
| break; |
| |
| case '>': |
| switch (opP->mode) |
| { |
| case DREG: |
| case AREG: |
| case FPREG: |
| case CONTROL: |
| case IMMED: |
| case AINC: |
| case REGLST: |
| losing++; |
| break; |
| case ABSL: |
| break; |
| default: |
| if (opP->reg == PC |
| || opP->reg == ZPC) |
| losing++; |
| break; |
| } |
| break; |
| |
| case 'm': |
| switch (opP->mode) |
| { |
| case DREG: |
| case AREG: |
| case AINDR: |
| case AINC: |
| case ADEC: |
| break; |
| default: |
| losing++; |
| } |
| break; |
| |
| case 'n': |
| switch (opP->mode) |
| { |
| case DISP: |
| break; |
| default: |
| losing++; |
| } |
| break; |
| |
| case 'o': |
| switch (opP->mode) |
| { |
| case BASE: |
| case ABSL: |
| case IMMED: |
| break; |
| default: |
| losing++; |
| } |
| break; |
| |
| case 'p': |
| switch (opP->mode) |
| { |
| case DREG: |
| case AREG: |
| case AINDR: |
| case AINC: |
| case ADEC: |
| break; |
| case DISP: |
| if (opP->reg == PC || opP->reg == ZPC) |
| losing++; |
| break; |
| default: |
| losing++; |
| } |
| break; |
| |
| case 'q': |
| switch (opP->mode) |
| { |
| case DREG: |
| case AINDR: |
| case AINC: |
| case ADEC: |
| break; |
| case DISP: |
| if (opP->reg == PC || opP->reg == ZPC) |
| losing++; |
| break; |
| default: |
| losing++; |
| break; |
| } |
| break; |
| |
| case 'v': |
| switch (opP->mode) |
| { |
| case DREG: |
| case AINDR: |
| case AINC: |
| case ADEC: |
| case ABSL: |
| break; |
| case DISP: |
| if (opP->reg == PC || opP->reg == ZPC) |
| losing++; |
| break; |
| default: |
| losing++; |
| break; |
| } |
| break; |
| |
| case '#': |
| if (opP->mode != IMMED) |
| losing++; |
| else if (s[1] == 'b' |
| && ! isvar (&opP->disp) |
| && (opP->disp.exp.X_op != O_constant |
| || ! isbyte (opP->disp.exp.X_add_number))) |
| losing++; |
| else if (s[1] == 'B' |
| && ! isvar (&opP->disp) |
| && (opP->disp.exp.X_op != O_constant |
| || ! issbyte (opP->disp.exp.X_add_number))) |
| losing++; |
| else if (s[1] == 'w' |
| && ! isvar (&opP->disp) |
| && (opP->disp.exp.X_op != O_constant |
| || ! isword (opP->disp.exp.X_add_number))) |
| losing++; |
| else if (s[1] == 'W' |
| && ! isvar (&opP->disp) |
| && (opP->disp.exp.X_op != O_constant |
| || ! issword (opP->disp.exp.X_add_number))) |
| losing++; |
| break; |
| |
| case '^': |
| case 'T': |
| if (opP->mode != IMMED) |
| losing++; |
| break; |
| |
| case '$': |
| if (opP->mode == AREG |
| || opP->mode == CONTROL |
| || opP->mode == FPREG |
| || opP->mode == IMMED |
| || opP->mode == REGLST |
| || (opP->mode != ABSL |
| && (opP->reg == PC |
| || opP->reg == ZPC))) |
| losing++; |
| break; |
| |
| case '%': |
| if (opP->mode == CONTROL |
| || opP->mode == FPREG |
| || opP->mode == REGLST |
| || opP->mode == IMMED |
| || (opP->mode != ABSL |
| && (opP->reg == PC |
| || opP->reg == ZPC))) |
| losing++; |
| break; |
| |
| case '&': |
| switch (opP->mode) |
| { |
| case DREG: |
| case AREG: |
| case FPREG: |
| case CONTROL: |
| case IMMED: |
| case AINC: |
| case ADEC: |
| case REGLST: |
| losing++; |
| break; |
| case ABSL: |
| break; |
| default: |
| if (opP->reg == PC |
| || opP->reg == ZPC) |
| losing++; |
| break; |
| } |
| break; |
| |
| case '*': |
| if (opP->mode == CONTROL |
| || opP->mode == FPREG |
| || opP->mode == REGLST) |
| losing++; |
| break; |
| |
| case '+': |
| if (opP->mode != AINC) |
| losing++; |
| break; |
| |
| case '-': |
| if (opP->mode != ADEC) |
| losing++; |
| break; |
| |
| case '/': |
| switch (opP->mode) |
| { |
| case AREG: |
| case CONTROL: |
| case FPREG: |
| case AINC: |
| case ADEC: |
| case IMMED: |
| case REGLST: |
| losing++; |
| break; |
| default: |
| break; |
| } |
| break; |
| |
| case ';': |
| switch (opP->mode) |
| { |
| case AREG: |
| case CONTROL: |
| case FPREG: |
| case REGLST: |
| losing++; |
| break; |
| default: |
| break; |
| } |
| break; |
| |
| case '?': |
| switch (opP->mode) |
| { |
| case AREG: |
| case CONTROL: |
| case FPREG: |
| case AINC: |
| case ADEC: |
| case IMMED: |
| case REGLST: |
| losing++; |
| break; |
| case ABSL: |
| break; |
| default: |
| if (opP->reg == PC || opP->reg == ZPC) |
| losing++; |
| break; |
| } |
| break; |
| |
| case '@': |
| switch (opP->mode) |
| { |
| case AREG: |
| case CONTROL: |
| case FPREG: |
| case IMMED: |
| case REGLST: |
| losing++; |
| break; |
| default: |
| break; |
| } |
| break; |
| |
| case '~': /* For now! (JF FOO is this right?) */ |
| switch (opP->mode) |
| { |
| case DREG: |
| case AREG: |
| case CONTROL: |
| case FPREG: |
| case IMMED: |
| case REGLST: |
| losing++; |
| break; |
| case ABSL: |
| break; |
| default: |
| if (opP->reg == PC |
| || opP->reg == ZPC) |
| losing++; |
| break; |
| } |
| break; |
| |
| case '3': |
| if (opP->mode != CONTROL |
| || (opP->reg != TT0 && opP->reg != TT1)) |
| losing++; |
| break; |
| |
| case 'A': |
| if (opP->mode != AREG) |
| losing++; |
| break; |
| |
| case 'a': |
| if (opP->mode != AINDR) |
| ++losing; |
| break; |
| |
| case 'B': /* FOO */ |
| if (opP->mode != ABSL |
| || (flag_long_jumps |
| && strncmp (instring, "jbsr", 4) == 0)) |
| losing++; |
| break; |
| |
| case 'C': |
| if (opP->mode != CONTROL || opP->reg != CCR) |
| losing++; |
| break; |
| |
| case 'd': |
| if (opP->mode != DISP |
| || opP->reg < ADDR0 |
| || opP->reg > ADDR7) |
| losing++; |
| break; |
| |
| case 'D': |
| if (opP->mode != DREG) |
| losing++; |
| break; |
| |
| case 'E': |
| if (opP->reg != ACC) |
| losing++; |
| break; |
| |
| case 'F': |
| if (opP->mode != FPREG) |
| losing++; |
| break; |
| |
| case 'G': |
| if (opP->reg != MACSR) |
| losing++; |
| break; |
| |
| case 'H': |
| if (opP->reg != MASK) |
| losing++; |
| break; |
| |
| case 'I': |
| if (opP->mode != CONTROL |
| || opP->reg < COP0 |
| || opP->reg > COP7) |
| losing++; |
| break; |
| |
| case 'J': |
| if (opP->mode != CONTROL |
| || opP->reg < USP |
| || opP->reg > last_movec_reg) |
| losing++; |
| else |
| { |
| const enum m68k_register *rp; |
| for (rp = control_regs; *rp; rp++) |
| if (*rp == opP->reg) |
| break; |
| if (*rp == 0) |
| losing++; |
| } |
| break; |
| |
| case 'k': |
| if (opP->mode != IMMED) |
| losing++; |
| break; |
| |
| case 'l': |
| case 'L': |
| if (opP->mode == DREG |
| || opP->mode == AREG |
| || opP->mode == FPREG) |
| { |
| if (s[1] == '8') |
| losing++; |
| else |
| { |
| switch (opP->mode) |
| { |
| case DREG: |
| opP->mask = 1 << (opP->reg - DATA0); |
| break; |
| case AREG: |
| opP->mask = 1 << (opP->reg - ADDR0 + 8); |
| break; |
| case FPREG: |
| opP->mask = 1 << (opP->reg - FP0 + 16); |
| break; |
| default: |
| abort (); |
| } |
| opP->mode = REGLST; |
| } |
| } |
| else if (opP->mode == CONTROL) |
| { |
| if (s[1] != '8') |
| losing++; |
| else |
| { |
| switch (opP->reg) |
| { |
| case FPI: |
| opP->mask = 1 << 24; |
| break; |
| case FPS: |
| opP->mask = 1 << 25; |
| break; |
| case FPC: |
| opP->mask = 1 << 26; |
| break; |
| default: |
| losing++; |
| break; |
| } |
| opP->mode = REGLST; |
| } |
| } |
| else if (opP->mode != REGLST) |
| losing++; |
| else if (s[1] == '8' && (opP->mask & 0x0ffffff) != 0) |
| losing++; |
| else if (s[1] == '3' && (opP->mask & 0x7000000) != 0) |
| losing++; |
| break; |
| |
| case 'M': |
| if (opP->mode != IMMED) |
| losing++; |
| else if (opP->disp.exp.X_op != O_constant |
| || ! issbyte (opP->disp.exp.X_add_number)) |
| losing++; |
| else if (! m68k_quick |
| && instring[3] != 'q' |
| && instring[4] != 'q') |
| losing++; |
| break; |
| |
| case 'O': |
| if (opP->mode != DREG |
| && opP->mode != IMMED |
| && opP->mode != ABSL) |
| losing++; |
| break; |
| |
| case 'Q': |
| if (opP->mode != IMMED) |
| losing++; |
| else if (opP->disp.exp.X_op != O_constant |
| || opP->disp.exp.X_add_number < 1 |
| || opP->disp.exp.X_add_number > 8) |
| losing++; |
| else if (! m68k_quick |
| && (strncmp (instring, "add", 3) == 0 |
| || strncmp (instring, "sub", 3) == 0) |
| && instring[3] != 'q') |
| losing++; |
| break; |
| |
| case 'R': |
| if (opP->mode != DREG && opP->mode != AREG) |
| losing++; |
| break; |
| |
| case 'r': |
| if (opP->mode != AINDR |
| && (opP->mode != BASE |
| || (opP->reg != 0 |
| && opP->reg != ZADDR0) |
| || opP->disp.exp.X_op != O_absent |
| || ((opP->index.reg < DATA0 |
| || opP->index.reg > DATA7) |
| && (opP->index.reg < ADDR0 |
| || opP->index.reg > ADDR7)) |
| || opP->index.size != SIZE_UNSPEC |
| || opP->index.scale != 1)) |
| losing++; |
| break; |
| |
| case 's': |
| if (opP->mode != CONTROL |
| || ! (opP->reg == FPI |
| || opP->reg == FPS |
| || opP->reg == FPC)) |
| losing++; |
| break; |
| |
| case 'S': |
| if (opP->mode != CONTROL || opP->reg != SR) |
| losing++; |
| break; |
| |
| case 't': |
| if (opP->mode != IMMED) |
| losing++; |
| else if (opP->disp.exp.X_op != O_constant |
| || opP->disp.exp.X_add_number < 0 |
| || opP->disp.exp.X_add_number > 7) |
| losing++; |
| break; |
| |
| case 'U': |
| if (opP->mode != CONTROL || opP->reg != USP) |
| losing++; |
| break; |
| |
| /* JF these are out of order. We could put them |
| in order if we were willing to put up with |
| bunches of #ifdef m68851s in the code. |
| |
| Don't forget that you need these operands |
| to use 68030 MMU instructions. */ |
| #ifndef NO_68851 |
| /* Memory addressing mode used by pflushr */ |
| case '|': |
| if (opP->mode == CONTROL |
| || opP->mode == FPREG |
| || opP->mode == DREG |
| || opP->mode == AREG |
| || opP->mode == REGLST) |
| losing++; |
| /* We should accept immediate operands, but they |
| supposedly have to be quad word, and we don't |
| handle that. I would like to see what a Motorola |
| assembler does before doing something here. */ |
| if (opP->mode == IMMED) |
| losing++; |
| break; |
| |
| case 'f': |
| if (opP->mode != CONTROL |
| || (opP->reg != SFC && opP->reg != DFC)) |
| losing++; |
| break; |
| |
| case '0': |
| if (opP->mode != CONTROL || opP->reg != TC) |
| losing++; |
| break; |
| |
| case '1': |
| if (opP->mode != CONTROL || opP->reg != AC) |
| losing++; |
| break; |
| |
| case '2': |
| if (opP->mode != CONTROL |
| || (opP->reg != CAL |
| && opP->reg != VAL |
| && opP->reg != SCC)) |
| losing++; |
| break; |
| |
| case 'V': |
| if (opP->mode != CONTROL |
| || opP->reg != VAL) |
| losing++; |
| break; |
| |
| case 'W': |
| if (opP->mode != CONTROL |
| || (opP->reg != DRP |
| && opP->reg != SRP |
| && opP->reg != CRP)) |
| losing++; |
| break; |
| |
| case 'X': |
| if (opP->mode != CONTROL |
| || (!(opP->reg >= BAD && opP->reg <= BAD + 7) |
| && !(opP->reg >= BAC && opP->reg <= BAC + 7))) |
| losing++; |
| break; |
| |
| case 'Y': |
| if (opP->mode != CONTROL || opP->reg != PSR) |
| losing++; |
| break; |
| |
| case 'Z': |
| if (opP->mode != CONTROL || opP->reg != PCSR) |
| losing++; |
| break; |
| #endif |
| case 'c': |
| if (opP->mode != CONTROL |
| || (opP->reg != NC |
| && opP->reg != IC |
| && opP->reg != DC |
| && opP->reg != BC)) |
| { |
| losing++; |
| } /* not a cache specifier. */ |
| break; |
| |
| case '_': |
| if (opP->mode != ABSL) |
| ++losing; |
| break; |
| |
| case 'u': |
| if (opP->reg < DATA0L || opP->reg > ADDR7U) |
| losing++; |
| /* FIXME: kludge instead of fixing parser: |
| upper/lower registers are *not* CONTROL |
| registers, but ordinary ones. */ |
| if ((opP->reg >= DATA0L && opP->reg <= DATA7L) |
| || (opP->reg >= DATA0U && opP->reg <= DATA7U)) |
| opP->mode = DREG; |
| else |
| opP->mode = AREG; |
| break; |
| |
| default: |
| abort (); |
| } /* switch on type of operand */ |
| |
| if (losing) |
| break; |
| } /* for each operand */ |
| } /* if immediately wrong */ |
| |
| if (!losing) |
| { |
| break; |
| } /* got it. */ |
| |
| opcode = opcode->m_next; |
| |
| if (!opcode) |
| { |
| if (ok_arch |
| && !(ok_arch & current_architecture)) |
| { |
| char buf[200], *cp; |
| |
| strcpy (buf, |
| _("invalid instruction for this architecture; needs ")); |
| cp = buf + strlen (buf); |
| switch (ok_arch) |
| { |
| case mfloat: |
| strcpy (cp, _("fpu (68040, 68060 or 68881/68882)")); |
| break; |
| case mmmu: |
| strcpy (cp, _("mmu (68030 or 68851)")); |
| break; |
| case m68020up: |
| strcpy (cp, _("68020 or higher")); |
| break; |
| case m68000up: |
| strcpy (cp, _("68000 or higher")); |
| break; |
| case m68010up: |
| strcpy (cp, _("68010 or higher")); |
| break; |
| default: |
| { |
| int got_one = 0, idx; |
| for (idx = 0; |
| idx < (int) (sizeof (archs) / sizeof (archs[0])); |
| idx++) |
| { |
| if ((archs[idx].arch & ok_arch) |
| && ! archs[idx].alias) |
| { |
| if (got_one) |
| { |
| strcpy (cp, " or "); |
| cp += strlen (cp); |
| } |
| got_one = 1; |
| strcpy (cp, archs[idx].name); |
| cp += strlen (cp); |
| } |
| } |
| } |
| } |
| cp = xmalloc (strlen (buf) + 1); |
| strcpy (cp, buf); |
| the_ins.error = cp; |
| } |
| else |
| the_ins.error = _("operands mismatch"); |
| return; |
| } /* Fell off the end */ |
| |
| losing = 0; |
| } |
| |
| /* now assemble it */ |
| |
| the_ins.args = opcode->m_operands; |
| the_ins.numargs = opcode->m_opnum; |
| the_ins.numo = opcode->m_codenum; |
| the_ins.opcode[0] = getone (opcode); |
| the_ins.opcode[1] = gettwo (opcode); |
| |
| for (s = the_ins.args, opP = &the_ins.operands[0]; *s; s += 2, opP++) |
| { |
| /* This switch is a doozy. |
| Watch the first step; its a big one! */ |
| switch (s[0]) |
| { |
| |
| case '*': |
| case '~': |
| case '%': |
| case ';': |
| case '@': |
| case '!': |
| case '&': |
| case '$': |
| case '?': |
| case '/': |
| case '<': |
| case '>': |
| case 'm': |
| case 'n': |
| case 'o': |
| case 'p': |
| case 'q': |
| case 'v': |
| #ifndef NO_68851 |
| case '|': |
| #endif |
| switch (opP->mode) |
| { |
| case IMMED: |
| tmpreg = 0x3c; /* 7.4 */ |
| if (strchr ("bwl", s[1])) |
| nextword = get_num (&opP->disp, 80); |
| else |
| nextword = get_num (&opP->disp, 0); |
| if (isvar (&opP->disp)) |
| add_fix (s[1], &opP->disp, 0, 0); |
| switch (s[1]) |
| { |
| case 'b': |
| if (!isbyte (nextword)) |
| opP->error = _("operand out of range"); |
| addword (nextword); |
| baseo = 0; |
| break; |
| case 'w': |
| if (!isword (nextword)) |
| opP->error = _("operand out of range"); |
| addword (nextword); |
| baseo = 0; |
| break; |
| case 'W': |
| if (!issword (nextword)) |
| opP->error = _("operand out of range"); |
| addword (nextword); |
| baseo = 0; |
| break; |
| case 'l': |
| addword (nextword >> 16); |
| addword (nextword); |
| baseo = 0; |
| break; |
| |
| case 'f': |
| baseo = 2; |
| outro = 8; |
| break; |
| case 'F': |
| baseo = 4; |
| outro = 11; |
| break; |
| case 'x': |
| baseo = 6; |
| outro = 15; |
| break; |
| case 'p': |
| baseo = 6; |
| outro = -1; |
| break; |
| default: |
| abort (); |
| } |
| if (!baseo) |
| break; |
| |
| /* We gotta put out some float */ |
| if (op (&opP->disp) != O_big) |
| { |
| valueT val; |
| int gencnt; |
| |
| /* Can other cases happen here? */ |
| if (op (&opP->disp) != O_constant) |
| abort (); |
| |
| val = (valueT) offs (&opP->disp); |
| gencnt = 0; |
| do |
| { |
| generic_bignum[gencnt] = (LITTLENUM_TYPE) val; |
| val >>= LITTLENUM_NUMBER_OF_BITS; |
| ++gencnt; |
| } |
| while (val != 0); |
| offs (&opP->disp) = gencnt; |
| } |
| if (offs (&opP->disp) > 0) |
| { |
| if (offs (&opP->disp) > baseo) |
| { |
| as_warn (_("Bignum too big for %c format; truncated"), |
| s[1]); |
| offs (&opP->disp) = baseo; |
| } |
| baseo -= offs (&opP->disp); |
| while (baseo--) |
| addword (0); |
| for (wordp = generic_bignum + offs (&opP->disp) - 1; |
| offs (&opP->disp)--; |
| --wordp) |
| addword (*wordp); |
| break; |
| } |
| gen_to_words (words, baseo, (long) outro); |
| for (wordp = words; baseo--; wordp++) |
| addword (*wordp); |
| break; |
| case DREG: |
| tmpreg = opP->reg - DATA; /* 0.dreg */ |
| break; |
| case AREG: |
| tmpreg = 0x08 + opP->reg - ADDR; /* 1.areg */ |
| break; |
| case AINDR: |
| tmpreg = 0x10 + opP->reg - ADDR; /* 2.areg */ |
| break; |
| case ADEC: |
| tmpreg = 0x20 + opP->reg - ADDR; /* 4.areg */ |
| break; |
| case AINC: |
| tmpreg = 0x18 + opP->reg - ADDR; /* 3.areg */ |
| break; |
| case DISP: |
| |
| nextword = get_num (&opP->disp, 80); |
| |
| if (opP->reg == PC |
| && ! isvar (&opP->disp) |
| && m68k_abspcadd) |
| { |
| opP->disp.exp.X_op = O_symbol; |
| #ifndef BFD_ASSEMBLER |
| opP->disp.exp.X_add_symbol = &abs_symbol; |
| #else |
| opP->disp.exp.X_add_symbol = |
| section_symbol (absolute_section); |
| #endif |
| } |
| |
| /* Force into index mode. Hope this works */ |
| |
| /* We do the first bit for 32-bit displacements, and the |
| second bit for 16 bit ones. It is possible that we |
| should make the default be WORD instead of LONG, but |
| I think that'd break GCC, so we put up with a little |
| inefficiency for the sake of working output. */ |
| |
| if (!issword (nextword) |
| || (isvar (&opP->disp) |
| && ((opP->disp.size == SIZE_UNSPEC |
| && flag_short_refs == 0 |
| && cpu_of_arch (current_architecture) >= m68020 |
| && ! arch_coldfire_p (current_architecture)) |
| || opP->disp.size == SIZE_LONG))) |
| { |
| if (cpu_of_arch (current_architecture) < m68020 |
| || arch_coldfire_p (current_architecture)) |
| opP->error = |
| _("displacement too large for this architecture; needs 68020 or higher"); |
| if (opP->reg == PC) |
| tmpreg = 0x3B; /* 7.3 */ |
| else |
| tmpreg = 0x30 + opP->reg - ADDR; /* 6.areg */ |
| if (isvar (&opP->disp)) |
| { |
| if (opP->reg == PC) |
| { |
| if (opP->disp.size == SIZE_LONG |
| #ifdef OBJ_ELF |
| /* If the displacement needs pic |
| relocation it cannot be relaxed. */ |
| || opP->disp.pic_reloc != pic_none |
| #endif |
| ) |
| { |
| addword (0x0170); |
| add_fix ('l', &opP->disp, 1, 2); |
| } |
| else |
| { |
| add_frag (adds (&opP->disp), |
| offs (&opP->disp), |
| TAB (PCREL1632, SZ_UNDEF)); |
| break; |
| } |
| } |
| else |
| { |
| addword (0x0170); |
| add_fix ('l', &opP->disp, 0, 0); |
| } |
| } |
| else |
| addword (0x0170); |
| addword (nextword >> 16); |
| } |
| else |
| { |
| if (opP->reg == PC) |
| tmpreg = 0x3A; /* 7.2 */ |
| else |
| tmpreg = 0x28 + opP->reg - ADDR; /* 5.areg */ |
| |
| if (isvar (&opP->disp)) |
| { |
| if (opP->reg == PC) |
| { |
| add_fix ('w', &opP->disp, 1, 0); |
| } |
| else |
| add_fix ('w', &opP->disp, 0, 0); |
| } |
| } |
| addword (nextword); |
| break; |
| |
| case POST: |
| case PRE: |
| case BASE: |
| nextword = 0; |
| baseo = get_num (&opP->disp, 80); |
| if (opP->mode == POST || opP->mode == PRE) |
| outro = get_num (&opP->odisp, 80); |
| /* Figure out the `addressing mode'. |
| Also turn on the BASE_DISABLE bit, if needed. */ |
| if (opP->reg == PC || opP->reg == ZPC) |
| { |
| tmpreg = 0x3b; /* 7.3 */ |
| if (opP->reg == ZPC) |
| nextword |= 0x80; |
| } |
| else if (opP->reg == 0) |
| { |
| nextword |= 0x80; |
| tmpreg = 0x30; /* 6.garbage */ |
| } |
| else if (opP->reg >= ZADDR0 && opP->reg <= ZADDR7) |
| { |
| nextword |= 0x80; |
| tmpreg = 0x30 + opP->reg - ZADDR0; |
| } |
| else |
| tmpreg = 0x30 + opP->reg - ADDR; /* 6.areg */ |
| |
| siz1 = opP->disp.size; |
| if (opP->mode == POST || opP->mode == PRE) |
| siz2 = opP->odisp.size; |
| else |
| siz2 = SIZE_UNSPEC; |
| |
| /* Index register stuff */ |
| if (opP->index.reg != 0 |
| && opP->index.reg >= DATA |
| && opP->index.reg <= ADDR7) |
| { |
| nextword |= (opP->index.reg - DATA) << 12; |
| |
| if (opP->index.size == SIZE_LONG |
| || (opP->index.size == SIZE_UNSPEC |
| && m68k_index_width_default == SIZE_LONG)) |
| nextword |= 0x800; |
| |
| if ((opP->index.scale != 1 |
| && cpu_of_arch (current_architecture) < m68020) |
| || (opP->index.scale == 8 |
| && arch_coldfire_p (current_architecture))) |
| { |
| opP->error = |
| _("scale factor invalid on this architecture; needs cpu32 or 68020 or higher"); |
| } |
| |
| if (arch_coldfire_p (current_architecture) |
| && opP->index.size == SIZE_WORD) |
| opP->error = _("invalid index size for coldfire"); |
| |
| switch (opP->index.scale) |
| { |
| case 1: |
| break; |
| case 2: |
| nextword |= 0x200; |
| break; |
| case 4: |
| nextword |= 0x400; |
| break; |
| case 8: |
| nextword |= 0x600; |
| break; |
| default: |
| abort (); |
| } |
| /* IF its simple, |
| GET US OUT OF HERE! */ |
| |
| /* Must be INDEX, with an index register. Address |
| register cannot be ZERO-PC, and either :b was |
| forced, or we know it will fit. For a 68000 or |
| 68010, force this mode anyways, because the |
| larger modes aren't supported. */ |
| if (opP->mode == BASE |
| && ((opP->reg >= ADDR0 |
| && opP->reg <= ADDR7) |
| || opP->reg == PC)) |
| { |
| if (siz1 == SIZE_BYTE |
| || cpu_of_arch (current_architecture) < m68020 |
| || arch_coldfire_p (current_architecture) |
| || (siz1 == SIZE_UNSPEC |
| && ! isvar (&opP->disp) |
| && issbyte (baseo))) |
| { |
| nextword += baseo & 0xff; |
| addword (nextword); |
| if (isvar (&opP->disp)) |
| { |
| /* Do a byte relocation. If it doesn't |
| fit (possible on m68000) let the |
| fixup processing complain later. */ |
| if (opP->reg == PC) |
| add_fix ('B', &opP->disp, 1, 1); |
| else |
| add_fix ('B', &opP->disp, 0, 0); |
| } |
| else if (siz1 != SIZE_BYTE) |
| { |
| if (siz1 != SIZE_UNSPEC) |
| as_warn (_("Forcing byte displacement")); |
| if (! issbyte (baseo)) |
| opP->error = _("byte displacement out of range"); |
| } |
| |
| break; |
| } |
| else if (siz1 == SIZE_UNSPEC |
| && opP->reg == PC |
| && isvar (&opP->disp) |
| && subs (&opP->disp) == NULL |
| #ifdef OBJ_ELF |
| /* If the displacement needs pic |
| relocation it cannot be relaxed. */ |
| && opP->disp.pic_reloc == pic_none |
| #endif |
| ) |
| { |
| /* The code in md_convert_frag_1 needs to be |
| able to adjust nextword. Call frag_grow |
| to ensure that we have enough space in |
| the frag obstack to make all the bytes |
| contiguous. */ |
| frag_grow (14); |
| nextword += baseo & 0xff; |
| addword (nextword); |
| add_frag (adds (&opP->disp), offs (&opP->disp), |
| TAB (PCINDEX, SZ_UNDEF)); |
| |
| break; |
| } |
| } |
| } |
| else |
| { |
| nextword |= 0x40; /* No index reg */ |
| if (opP->index.reg >= ZDATA0 |
| && opP->index.reg <= ZDATA7) |
| nextword |= (opP->index.reg - ZDATA0) << 12; |
| else if (opP->index.reg >= ZADDR0 |
| || opP->index.reg <= ZADDR7) |
| nextword |= (opP->index.reg - ZADDR0 + 8) << 12; |
| } |
| |
| /* It isn't simple. */ |
| |
| if (cpu_of_arch (current_architecture) < m68020 |
| || arch_coldfire_p (current_architecture)) |
| opP->error = |
| _("invalid operand mode for this architecture; needs 68020 or higher"); |
| |
| nextword |= 0x100; |
| /* If the guy specified a width, we assume that it is |
| wide enough. Maybe it isn't. If so, we lose. */ |
| switch (siz1) |
| { |
| case SIZE_UNSPEC: |
| if (isvar (&opP->disp) |
| ? m68k_rel32 |
| : ! issword (baseo)) |
| { |
| siz1 = SIZE_LONG; |
| nextword |= 0x30; |
| } |
| else if (! isvar (&opP->disp) && baseo == 0) |
| nextword |= 0x10; |
| else |
| { |
| nextword |= 0x20; |
| siz1 = SIZE_WORD; |
| } |
| break; |
| case SIZE_BYTE: |
| as_warn (_(":b not permitted; defaulting to :w")); |
| /* Fall through. */ |
| case SIZE_WORD: |
| nextword |= 0x20; |
| break; |
| case SIZE_LONG: |
| nextword |= 0x30; |
| break; |
| } |
| |
| /* Figure out innner displacement stuff */ |
| if (opP->mode == POST || opP->mode == PRE) |
| { |
| if (cpu_of_arch (current_architecture) & cpu32) |
| opP->error = _("invalid operand mode for this architecture; needs 68020 or higher"); |
| switch (siz2) |
| { |
| case SIZE_UNSPEC: |
| if (isvar (&opP->odisp) |
| ? m68k_rel32 |
| : ! issword (outro)) |
| { |
| siz2 = SIZE_LONG; |
| nextword |= 0x3; |
| } |
| else if (! isvar (&opP->odisp) && outro == 0) |
| nextword |= 0x1; |
| else |
| { |
| nextword |= 0x2; |
| siz2 = SIZE_WORD; |
| } |
| break; |
| case 1: |
| as_warn (_(":b not permitted; defaulting to :w")); |
| /* Fall through. */ |
| case 2: |
| nextword |= 0x2; |
| break; |
| case 3: |
| nextword |= 0x3; |
| break; |
| } |
| if (opP->mode == POST |
| && (nextword & 0x40) == 0) |
| nextword |= 0x04; |
| } |
| addword (nextword); |
| |
| if (siz1 != SIZE_UNSPEC && isvar (&opP->disp)) |
| { |
| if (opP->reg == PC || opP->reg == ZPC) |
| add_fix (siz1 == SIZE_LONG ? 'l' : 'w', &opP->disp, 1, 2); |
| else |
| add_fix (siz1 == SIZE_LONG ? 'l' : 'w', &opP->disp, 0, 0); |
| } |
| if (siz1 == SIZE_LONG) |
| addword (baseo >> 16); |
| if (siz1 != SIZE_UNSPEC) |
| addword (baseo); |
| |
| if (siz2 != SIZE_UNSPEC && isvar (&opP->odisp)) |
| add_fix (siz2 == SIZE_LONG ? 'l' : 'w', &opP->odisp, 0, 0); |
| if (siz2 == SIZE_LONG) |
| addword (outro >> 16); |
| if (siz2 != SIZE_UNSPEC) |
| addword (outro); |
| |
| break; |
| |
| case ABSL: |
| nextword = get_num (&opP->disp, 80); |
| switch (opP->disp.size) |
| { |
| default: |
| abort (); |
| case SIZE_UNSPEC: |
| if (!isvar (&opP->disp) && issword (offs (&opP->disp))) |
| { |
| tmpreg = 0x38; /* 7.0 */ |
| addword (nextword); |
| break; |
| } |
| if (isvar (&opP->disp) |
| && !subs (&opP->disp) |
| && adds (&opP->disp) |
| #ifdef OBJ_ELF |
| /* If the displacement needs pic relocation it |
| cannot be relaxed. */ |
| && opP->disp.pic_reloc == pic_none |
| #endif |
| && !flag_long_jumps |
| && !strchr ("~%&$?", s[0])) |
| { |
| tmpreg = 0x3A; /* 7.2 */ |
| add_frag (adds (&opP->disp), |
| offs (&opP->disp), |
| TAB (ABSTOPCREL, SZ_UNDEF)); |
| break; |
| } |
| /* Fall through into long */ |
| case SIZE_LONG: |
| if (isvar (&opP->disp)) |
| add_fix ('l', &opP->disp, 0, 0); |
| |
| tmpreg = 0x39;/* 7.1 mode */ |
| addword (nextword >> 16); |
| addword (nextword); |
| break; |
| |
| case SIZE_BYTE: |
| as_bad (_("unsupported byte value; use a different suffix")); |
| /* Fall through. */ |
| case SIZE_WORD: /* Word */ |
| if (isvar (&opP->disp)) |
| add_fix ('w', &opP->disp, 0, 0); |
| |
| tmpreg = 0x38;/* 7.0 mode */ |
| addword (nextword); |
| break; |
| } |
| break; |
| case CONTROL: |
| case FPREG: |
| default: |
| as_bad (_("unknown/incorrect operand")); |
| /* abort (); */ |
| } |
| install_gen_operand (s[1], tmpreg); |
| break; |
| |
| case '#': |
| case '^': |
| switch (s[1]) |
| { /* JF: I hate floating point! */ |
| case 'j': |
| tmpreg = 70; |
| break; |
| case '8': |
| tmpreg = 20; |
| break; |
| case 'C': |
| tmpreg = 50; |
| break; |
| case '3': |
| default: |
| tmpreg = 80; |
| break; |
| } |
| tmpreg = get_num (&opP->disp, tmpreg); |
| if (isvar (&opP->disp)) |
| add_fix (s[1], &opP->disp, 0, 0); |
| switch (s[1]) |
| { |
| case 'b': /* Danger: These do no check for |
| certain types of overflow. |
| user beware! */ |
| if (!isbyte (tmpreg)) |
| opP->error = _("out of range"); |
| insop (tmpreg, opcode); |
| if (isvar (&opP->disp)) |
| the_ins.reloc[the_ins.nrel - 1].n = |
| (opcode->m_codenum) * 2 + 1; |
| break; |
| case 'B': |
| if (!issbyte (tmpreg)) |
| opP->error = _("out of range"); |
| the_ins.opcode[the_ins.numo - 1] |= tmpreg & 0xff; |
| if (isvar (&opP->disp)) |
| the_ins.reloc[the_ins.nrel - 1].n = opcode->m_codenum * 2 - 1; |
| break; |
| case 'w': |
| if (!isword (tmpreg)) |
| opP->error = _("out of range"); |
| insop (tmpreg, opcode); |
| if (isvar (&opP->disp)) |
| the_ins.reloc[the_ins.nrel - 1].n = (opcode->m_codenum) * 2; |
| break; |
| case 'W': |
| if (!issword (tmpreg)) |
| opP->error = _("out of range"); |
| insop (tmpreg, opcode); |
| if (isvar (&opP->disp)) |
| the_ins.reloc[the_ins.nrel - 1].n = (opcode->m_codenum) * 2; |
| break; |
| case 'l': |
| /* Because of the way insop works, we put these two out |
| backwards. */ |
| insop (tmpreg, opcode); |
| insop (tmpreg >> 16, opcode); |
| if (isvar (&opP->disp)) |
| the_ins.reloc[the_ins.nrel - 1].n = (opcode->m_codenum) * 2; |
| break; |
| case '3': |
| tmpreg &= 0xFF; |
| case '8': |
| case 'C': |
| case 'j': |
| install_operand (s[1], tmpreg); |
| break; |
| default: |
| abort (); |
| } |
| break; |
| |
| case '+': |
| case '-': |
| case 'A': |
| case 'a': |
| install_operand (s[1], opP->reg - ADDR); |
| break; |
| |
| case 'B': |
| tmpreg = get_num (&opP->disp, 80); |
| switch (s[1]) |
| { |
| case 'B': |
| add_fix ('B', &opP->disp, 1, -1); |
| break; |
| case 'W': |
| add_fix ('w', &opP->disp, 1, 0); |
| addword (0); |
| break; |
| case 'L': |
| long_branch: |
| if (! HAVE_LONG_BRANCH (current_architecture)) |
| as_warn (_("Can't use long branches on 68000/68010/5200")); |
| the_ins.opcode[0] |= 0xff; |
| add_fix ('l', &opP->disp, 1, 0); |
| addword (0); |
| addword (0); |
| break; |
| case 'g': |
| if (subs (&opP->disp)) /* We can't relax it */ |
| goto long_branch; |
| |
| #ifdef OBJ_ELF |
| /* If the displacement needs pic relocation it cannot be |
| relaxed. */ |
| if (opP->disp.pic_reloc != pic_none) |
| goto long_branch; |
| #endif |
| /* This could either be a symbol, or an absolute |
| address. If it's an absolute address, turn it into |
| an absolute jump right here and keep it out of the |
| relaxer. */ |
| if (adds (&opP->disp) == 0) |
| { |
| if (the_ins.opcode[0] == 0x6000) /* jbra */ |
| the_ins.opcode[0] = 0x4EF9; |
| else if (the_ins.opcode[0] == 0x6100) /* jbsr */ |
| the_ins.opcode[0] = 0x4EB9; |
| else /* jCC */ |
| { |
| the_ins.opcode[0] ^= 0x0100; |
| the_ins.opcode[0] |= 0x0006; |
| addword (0x4EF9); |
| } |
| add_fix ('l', &opP->disp, 0, 0); |
| addword (0); |
| addword (0); |
| break; |
| } |
| |
| /* Now we know it's going into the relaxer. Now figure |
| out which mode. We try in this order of preference: |
| long branch, absolute jump, byte/word branches only. */ |
| if (HAVE_LONG_BRANCH (current_architecture)) |
| add_frag (adds (&opP->disp), offs (&opP->disp), |
| TAB (BRANCHBWL, SZ_UNDEF)); |
| else if (! flag_keep_pcrel) |
| { |
| if ((the_ins.opcode[0] == 0x6000) |
| || (the_ins.opcode[0] == 0x6100)) |
| add_frag (adds (&opP->disp), offs (&opP->disp), |
| TAB (BRABSJUNC, SZ_UNDEF)); |
| else |
| add_frag (adds (&opP->disp), offs (&opP->disp), |
| TAB (BRABSJCOND, SZ_UNDEF)); |
| } |
| else |
| add_frag (adds (&opP->disp), offs (&opP->disp), |
| TAB (BRANCHBW, SZ_UNDEF)); |
| break; |
| case 'w': |
| if (isvar (&opP->disp)) |
| { |
| /* Check for DBcc instructions. We can relax them, |
| but only if we have long branches and/or absolute |
| jumps. */ |
| if (((the_ins.opcode[0] & 0xf0f8) == 0x50c8) |
| && (HAVE_LONG_BRANCH (current_architecture) |
| || (! flag_keep_pcrel))) |
| { |
| if (HAVE_LONG_BRANCH (current_architecture)) |
| add_frag (adds (&opP->disp), offs (&opP->disp), |
| TAB (DBCCLBR, SZ_UNDEF)); |
| else |
| add_frag (adds (&opP->disp), offs (&opP->disp), |
| TAB (DBCCABSJ, SZ_UNDEF)); |
| break; |
| } |
| add_fix ('w', &opP->disp, 1, 0); |
| } |
| addword (0); |
| break; |
| case 'C': /* Fixed size LONG coproc branches */ |
| add_fix ('l', &opP->disp, 1, 0); |
| addword (0); |
| addword (0); |
| break; |
| case 'c': /* Var size Coprocesssor branches */ |
| if (subs (&opP->disp) || (adds (&opP->disp) == 0)) |
| { |
| the_ins.opcode[the_ins.numo - 1] |= 0x40; |
| add_fix ('l', &opP->disp, 1, 0); |
| addword (0); |
| addword (0); |
| } |
| else |
| add_frag (adds (&opP->disp), offs (&opP->disp), |
| TAB (FBRANCH, SZ_UNDEF)); |
| break; |
| default: |
| abort (); |
| } |
| break; |
| |
| case 'C': /* Ignore it */ |
| break; |
| |
| case 'd': /* JF this is a kludge */ |
| install_operand ('s', opP->reg - ADDR); |
| tmpreg = get_num (&opP->disp, 80); |
| if (!issword (tmpreg)) |
| { |
| as_warn (_("Expression out of range, using 0")); |
| tmpreg = 0; |
| } |
| addword (tmpreg); |
| break; |
| |
| case 'D': |
| install_operand (s[1], opP->reg - DATA); |
| break; |
| |
| case 'E': /* Ignore it */ |
| break; |
| |
| case 'F': |
| install_operand (s[1], opP->reg - FP0); |
| break; |
| |
| case 'G': /* Ignore it */ |
| case 'H': |
| break; |
| |
| case 'I': |
| tmpreg = opP->reg - COP0; |
| install_operand (s[1], tmpreg); |
| break; |
| |
| case 'J': /* JF foo */ |
| switch (opP->reg) |
| { |
| case SFC: |
| tmpreg = 0x000; |
| break; |
| case DFC: |
| tmpreg = 0x001; |
| break; |
| case CACR: |
| tmpreg = 0x002; |
| break; |
| case TC: |
| tmpreg = 0x003; |
| break; |
| case ITT0: |
| tmpreg = 0x004; |
| break; |
| case ITT1: |
| tmpreg = 0x005; |
| break; |
| case DTT0: |
| tmpreg = 0x006; |
| break; |
| case DTT1: |
| tmpreg = 0x007; |
| break; |
| case BUSCR: |
| tmpreg = 0x008; |
| break; |
| |
| case USP: |
| tmpreg = 0x800; |
| break; |
| case VBR: |
| tmpreg = 0x801; |
| break; |
| case CAAR: |
| tmpreg = 0x802; |
| break; |
| case MSP: |
| tmpreg = 0x803; |
|