| /* tc-m68k.c -- Assemble for the m68k family |
| Copyright (C) 1987-2021 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 "obstack.h" |
| #include "subsegs.h" |
| #include "dwarf2dbg.h" |
| #include "dw2gencfi.h" |
| |
| #include "opcode/m68k.h" |
| #include "m68k-parse.h" |
| #include "elf/m68k.h" |
| |
| static void m68k_elf_cons (int); |
| static void m68k_elf_gnu_attribute (int); |
| |
| /* 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. */ |
| |
| /* 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; |
| const 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; |
| |
| /* Pointer to list holding the opcodes sorted by name. */ |
| static struct m68k_opcode const ** m68k_sorted_opcodes; |
| |
| /* It's 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_ctrl[] = { 0 }; |
| static const enum m68k_register m68010_ctrl[] = { |
| SFC, DFC, USP, VBR, |
| 0 |
| }; |
| static const enum m68k_register m68020_ctrl[] = { |
| SFC, DFC, USP, VBR, CACR, CAAR, MSP, ISP, |
| 0 |
| }; |
| static const enum m68k_register m68040_ctrl[] = { |
| SFC, DFC, CACR, TC, ITT0, ITT1, DTT0, DTT1, |
| USP, VBR, MSP, ISP, MMUSR, URP, SRP, |
| 0 |
| }; |
| static const enum m68k_register m68060_ctrl[] = { |
| SFC, DFC, CACR, TC, ITT0, ITT1, DTT0, DTT1, BUSCR, |
| USP, VBR, URP, SRP, PCR, |
| 0 |
| }; |
| static const enum m68k_register mcf_ctrl[] = { |
| CACR, TC, ACR0, ACR1, ACR2, ACR3, VBR, ROMBAR, |
| RAMBAR0, RAMBAR1, RAMBAR, MBAR, |
| 0 |
| }; |
| static const enum m68k_register mcf51_ctrl[] = { |
| VBR, CPUCR, |
| 0 |
| }; |
| static const enum m68k_register mcf5206_ctrl[] = { |
| CACR, ACR0, ACR1, VBR, RAMBAR0, RAMBAR_ALT, MBAR, |
| 0 |
| }; |
| static const enum m68k_register mcf5208_ctrl[] = { |
| CACR, ACR0, ACR1, VBR, RAMBAR, RAMBAR1, |
| 0 |
| }; |
| static const enum m68k_register mcf5210a_ctrl[] = { |
| VBR, CACR, ACR0, ACR1, ROMBAR, RAMBAR, RAMBAR1, MBAR, |
| 0 |
| }; |
| static const enum m68k_register mcf5213_ctrl[] = { |
| VBR, RAMBAR, RAMBAR1, FLASHBAR, |
| 0 |
| }; |
| static const enum m68k_register mcf5216_ctrl[] = { |
| VBR, CACR, ACR0, ACR1, FLASHBAR, RAMBAR, RAMBAR1, |
| 0 |
| }; |
| static const enum m68k_register mcf5221x_ctrl[] = { |
| VBR, FLASHBAR, RAMBAR, RAMBAR1, |
| 0 |
| }; |
| static const enum m68k_register mcf52223_ctrl[] = { |
| VBR, FLASHBAR, RAMBAR, RAMBAR1, |
| 0 |
| }; |
| static const enum m68k_register mcf52235_ctrl[] = { |
| VBR, FLASHBAR, RAMBAR, RAMBAR1, |
| 0 |
| }; |
| static const enum m68k_register mcf5225_ctrl[] = { |
| VBR, CACR, ACR0, ACR1, FLASHBAR, RAMBAR, MBAR, RAMBAR1, |
| 0 |
| }; |
| static const enum m68k_register mcf52259_ctrl[] = { |
| VBR, FLASHBAR, RAMBAR, RAMBAR1, |
| 0 |
| }; |
| static const enum m68k_register mcf52277_ctrl[] = { |
| VBR, CACR, ACR0, ACR1, RAMBAR, RAMBAR1, |
| 0 |
| }; |
| static const enum m68k_register mcf5235_ctrl[] = { |
| VBR, CACR, ACR0, ACR1, RAMBAR, RAMBAR1, |
| 0 |
| }; |
| static const enum m68k_register mcf5249_ctrl[] = { |
| VBR, CACR, ACR0, ACR1, RAMBAR0, RAMBAR1, RAMBAR, MBAR, MBAR2, |
| 0 |
| }; |
| static const enum m68k_register mcf5250_ctrl[] = { |
| VBR, |
| 0 |
| }; |
| static const enum m68k_register mcf5253_ctrl[] = { |
| VBR, CACR, ACR0, ACR1, RAMBAR0, RAMBAR1, RAMBAR, MBAR, MBAR2, |
| 0 |
| }; |
| static const enum m68k_register mcf5271_ctrl[] = { |
| VBR, CACR, ACR0, ACR1, RAMBAR, RAMBAR1, |
| 0 |
| }; |
| static const enum m68k_register mcf5272_ctrl[] = { |
| VBR, CACR, ACR0, ACR1, ROMBAR, RAMBAR_ALT, RAMBAR0, MBAR, |
| 0 |
| }; |
| static const enum m68k_register mcf5275_ctrl[] = { |
| VBR, CACR, ACR0, ACR1, RAMBAR, RAMBAR1, |
| 0 |
| }; |
| static const enum m68k_register mcf5282_ctrl[] = { |
| VBR, CACR, ACR0, ACR1, FLASHBAR, RAMBAR, RAMBAR1, |
| 0 |
| }; |
| static const enum m68k_register mcf53017_ctrl[] = { |
| VBR, CACR, ACR0, ACR1, RAMBAR, RAMBAR1, |
| 0 |
| }; |
| static const enum m68k_register mcf5307_ctrl[] = { |
| VBR, CACR, ACR0, ACR1, RAMBAR0, RAMBAR_ALT, MBAR, |
| 0 |
| }; |
| static const enum m68k_register mcf5329_ctrl[] = { |
| VBR, CACR, ACR0, ACR1, RAMBAR, RAMBAR1, |
| 0 |
| }; |
| static const enum m68k_register mcf5373_ctrl[] = { |
| VBR, CACR, ACR0, ACR1, RAMBAR, RAMBAR1, |
| 0 |
| }; |
| static const enum m68k_register mcfv4e_ctrl[] = { |
| CACR, ASID, ACR0, ACR1, ACR2, ACR3, MMUBAR, |
| VBR, PC, ROMBAR0, ROMBAR1, RAMBAR0, RAMBAR1, |
| MBAR, SECMBAR, |
| MPCR /* Multiprocessor Control register */, |
| EDRAMBAR /* Embedded DRAM Base Address Register */, |
| /* Permutation control registers. */ |
| PCR1U0, PCR1L0, PCR1U1, PCR1L1, PCR2U0, PCR2L0, PCR2U1, PCR2L1, |
| PCR3U0, PCR3L0, PCR3U1, PCR3L1, |
| /* Legacy names */ |
| TC /* ASID */, BUSCR /* MMUBAR */, |
| ITT0 /* ACR0 */, ITT1 /* ACR1 */, DTT0 /* ACR2 */, DTT1 /* ACR3 */, |
| MBAR1 /* MBAR */, MBAR2 /* SECMBAR */, MBAR0 /* SECMBAR */, |
| ROMBAR /* ROMBAR0 */, RAMBAR /* RAMBAR1 */, |
| 0 |
| }; |
| static const enum m68k_register mcf5407_ctrl[] = { |
| CACR, ASID, ACR0, ACR1, ACR2, ACR3, |
| VBR, PC, RAMBAR0, RAMBAR1, MBAR, |
| /* Legacy names */ |
| TC /* ASID */, |
| ITT0 /* ACR0 */, ITT1 /* ACR1 */, DTT0 /* ACR2 */, DTT1 /* ACR3 */, |
| MBAR1 /* MBAR */, RAMBAR /* RAMBAR1 */, |
| 0 |
| }; |
| static const enum m68k_register mcf54418_ctrl[] = { |
| CACR, ASID, ACR0, ACR1, ACR2, ACR3, ACR4, ACR5, ACR6, ACR7, MMUBAR, RGPIOBAR, |
| VBR, PC, RAMBAR1, |
| /* Legacy names */ |
| TC /* ASID */, BUSCR /* MMUBAR */, |
| ITT0 /* ACR0 */, ITT1 /* ACR1 */, DTT0 /* ACR2 */, DTT1 /* ACR3 */, |
| RAMBAR /* RAMBAR1 */, |
| 0 |
| }; |
| static const enum m68k_register mcf54455_ctrl[] = { |
| CACR, ASID, ACR0, ACR1, ACR2, ACR3, MMUBAR, |
| VBR, PC, RAMBAR1, |
| /* Legacy names */ |
| TC /* ASID */, BUSCR /* MMUBAR */, |
| ITT0 /* ACR0 */, ITT1 /* ACR1 */, DTT0 /* ACR2 */, DTT1 /* ACR3 */, |
| RAMBAR /* RAMBAR1 */, |
| 0 |
| }; |
| static const enum m68k_register mcf5475_ctrl[] = { |
| CACR, ASID, ACR0, ACR1, ACR2, ACR3, MMUBAR, |
| VBR, PC, RAMBAR0, RAMBAR1, MBAR, |
| /* Legacy names */ |
| TC /* ASID */, BUSCR /* MMUBAR */, |
| ITT0 /* ACR0 */, ITT1 /* ACR1 */, DTT0 /* ACR2 */, DTT1 /* ACR3 */, |
| MBAR1 /* MBAR */, RAMBAR /* RAMBAR1 */, |
| 0 |
| }; |
| static const enum m68k_register mcf5485_ctrl[] = { |
| CACR, ASID, ACR0, ACR1, ACR2, ACR3, MMUBAR, |
| VBR, PC, RAMBAR0, RAMBAR1, MBAR, |
| /* Legacy names */ |
| TC /* ASID */, BUSCR /* MMUBAR */, |
| ITT0 /* ACR0 */, ITT1 /* ACR1 */, DTT0 /* ACR2 */, DTT1 /* ACR3 */, |
| MBAR1 /* MBAR */, RAMBAR /* RAMBAR1 */, |
| 0 |
| }; |
| static const enum m68k_register fido_ctrl[] = { |
| SFC, DFC, USP, VBR, CAC, MBO, |
| 0 |
| }; |
| #define cpu32_ctrl m68010_ctrl |
| |
| 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 structs 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; |
| /* Whether this expression needs special pic relocation, and if |
| so, which. */ |
| enum pic_relocation pic_reloc; |
| } |
| reloc[5]; /* Five is enough??? */ |
| }; |
| |
| #define cpu_of_arch(x) ((x) & (m68000up | mcfisa_a | fido_a)) |
| #define float_of_arch(x) ((x) & mfloat) |
| #define mmu_of_arch(x) ((x) & mmmu) |
| #define arch_coldfire_p(x) ((x) & mcfisa_a) |
| #define arch_coldfire_fpu(x) ((x) & cfloat) |
| |
| /* Macros for determining if cpu supports a specific addressing mode. */ |
| #define HAVE_LONG_DISP(x) \ |
| ((x) & (m68020|m68030|m68040|m68060|cpu32|fido_a|mcfisa_b|mcfisa_c)) |
| #define HAVE_LONG_CALL(x) \ |
| ((x) & (m68020|m68030|m68040|m68060|cpu32|fido_a|mcfisa_b|mcfisa_c)) |
| #define HAVE_LONG_COND(x) \ |
| ((x) & (m68020|m68030|m68040|m68060|cpu32|fido_a|mcfisa_b|mcfisa_c)) |
| #define HAVE_LONG_BRANCH(x) \ |
| ((x) & (m68020|m68030|m68040|m68060|cpu32|fido_a|mcfisa_b)) |
| #define LONG_BRANCH_VIA_COND(x) (HAVE_LONG_COND(x) && !HAVE_LONG_BRANCH(x)) |
| |
| 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)) |
| |
| /* Like addword, but goes BEFORE general operands. */ |
| |
| static void |
| insop (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 (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; |
| the_ins.reloc[the_ins.nrel].pic_reloc = exp->pic_reloc; |
| 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 (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 (char *str, struct m68k_op *opP); |
| static int get_num (struct m68k_exp *exp, int ok); |
| static int reverse_16_bits (int in); |
| static int reverse_8_bits (int in); |
| static void install_gen_operand (int mode, int val); |
| static void install_operand (int mode, int val); |
| static void s_bss (int); |
| static void s_data1 (int); |
| static void s_data2 (int); |
| static void s_even (int); |
| static void s_proc (int); |
| static void s_chip (int); |
| static void s_fopt (int); |
| static void s_opt (int); |
| static void s_reg (int); |
| static void s_restore (int); |
| static void s_save (int); |
| static void s_mri_if (int); |
| static void s_mri_else (int); |
| static void s_mri_endi (int); |
| static void s_mri_break (int); |
| static void s_mri_next (int); |
| static void s_mri_for (int); |
| static void s_mri_endf (int); |
| static void s_mri_repeat (int); |
| static void s_mri_until (int); |
| static void s_mri_while (int); |
| static void s_mri_endw (int); |
| static void s_m68k_cpu (int); |
| static void s_m68k_arch (int); |
| |
| struct m68k_cpu |
| { |
| unsigned long arch; /* Architecture features. */ |
| const enum m68k_register *control_regs; /* Control regs on chip */ |
| const char *name; /* Name */ |
| int alias; /* Alias for a canonical name. If 1, then |
| succeeds canonical name, if -1 then |
| succeeds canonical name, if <-1 ||>1 this is a |
| deprecated name, and the next/previous name |
| should be used. */ |
| }; |
| |
| /* We hold flags for features explicitly enabled and explicitly |
| disabled. */ |
| static int current_architecture; |
| static int not_current_architecture; |
| static const struct m68k_cpu *selected_arch; |
| static const struct m68k_cpu *selected_cpu; |
| static int initialized; |
| |
| /* Architecture models. */ |
| static const struct m68k_cpu m68k_archs[] = |
| { |
| {m68000, m68000_ctrl, "68000", 0}, |
| {m68010, m68010_ctrl, "68010", 0}, |
| {m68020|m68881|m68851, m68020_ctrl, "68020", 0}, |
| {m68030|m68881|m68851, m68020_ctrl, "68030", 0}, |
| {m68040, m68040_ctrl, "68040", 0}, |
| {m68060, m68060_ctrl, "68060", 0}, |
| {cpu32|m68881, cpu32_ctrl, "cpu32", 0}, |
| {fido_a, fido_ctrl, "fidoa", 0}, |
| {mcfisa_a|mcfhwdiv, NULL, "isaa", 0}, |
| {mcfisa_a|mcfhwdiv|mcfisa_aa|mcfusp, NULL, "isaaplus", 0}, |
| {mcfisa_a|mcfhwdiv|mcfisa_b|mcfusp, NULL, "isab", 0}, |
| {mcfisa_a|mcfhwdiv|mcfisa_c|mcfusp, NULL, "isac", 0}, |
| {mcfisa_a|mcfhwdiv|mcfisa_b|mcfmac|mcfusp, mcf_ctrl, "cfv4", 0}, |
| {mcfisa_a|mcfhwdiv|mcfisa_b|mcfemac|mcfusp|cfloat, mcfv4e_ctrl, "cfv4e", 0}, |
| {0,0,NULL, 0} |
| }; |
| |
| /* For -mno-mac we want to turn off all types of mac. */ |
| static const unsigned no_mac = mcfmac | mcfemac; |
| |
| /* Architecture extensions, here 'alias' -1 for m68k, +1 for cf and 0 |
| for either. */ |
| static const struct m68k_cpu m68k_extensions[] = |
| { |
| {m68851, NULL, "68851", -1}, |
| {m68881, NULL, "68881", -1}, |
| {m68881, NULL, "68882", -1}, |
| |
| {cfloat|m68881, NULL, "float", 0}, |
| |
| {mcfhwdiv, NULL, "div", 1}, |
| {mcfusp, NULL, "usp", 1}, |
| {mcfmac, (void *)&no_mac, "mac", 1}, |
| {mcfemac, NULL, "emac", 1}, |
| |
| {0,NULL,NULL, 0} |
| }; |
| |
| /* Processor list */ |
| static const struct m68k_cpu m68k_cpus[] = |
| { |
| {m68000, m68000_ctrl, "68000", 0}, |
| {m68000, m68000_ctrl, "68ec000", 1}, |
| {m68000, m68000_ctrl, "68hc000", 1}, |
| {m68000, m68000_ctrl, "68hc001", 1}, |
| {m68000, m68000_ctrl, "68008", 1}, |
| {m68000, m68000_ctrl, "68302", 1}, |
| {m68000, m68000_ctrl, "68306", 1}, |
| {m68000, m68000_ctrl, "68307", 1}, |
| {m68000, m68000_ctrl, "68322", 1}, |
| {m68000, m68000_ctrl, "68356", 1}, |
| {m68010, m68010_ctrl, "68010", 0}, |
| {m68020|m68881|m68851, m68020_ctrl, "68020", 0}, |
| {m68020|m68881|m68851, m68020_ctrl, "68k", 1}, |
| {m68020|m68881|m68851, m68020_ctrl, "68ec020", 1}, |
| {m68030|m68881|m68851, m68020_ctrl, "68030", 0}, |
| {m68030|m68881|m68851, m68020_ctrl, "68ec030", 1}, |
| {m68040, m68040_ctrl, "68040", 0}, |
| {m68040, m68040_ctrl, "68ec040", 1}, |
| {m68060, m68060_ctrl, "68060", 0}, |
| {m68060, m68060_ctrl, "68ec060", 1}, |
| |
| {cpu32|m68881, cpu32_ctrl, "cpu32", 0}, |
| {cpu32|m68881, cpu32_ctrl, "68330", 1}, |
| {cpu32|m68881, cpu32_ctrl, "68331", 1}, |
| {cpu32|m68881, cpu32_ctrl, "68332", 1}, |
| {cpu32|m68881, cpu32_ctrl, "68333", 1}, |
| {cpu32|m68881, cpu32_ctrl, "68334", 1}, |
| {cpu32|m68881, cpu32_ctrl, "68336", 1}, |
| {cpu32|m68881, cpu32_ctrl, "68340", 1}, |
| {cpu32|m68881, cpu32_ctrl, "68341", 1}, |
| {cpu32|m68881, cpu32_ctrl, "68349", 1}, |
| {cpu32|m68881, cpu32_ctrl, "68360", 1}, |
| |
| {mcfisa_a|mcfisa_c|mcfusp, mcf51_ctrl, "51", 0}, |
| {mcfisa_a|mcfisa_c|mcfusp, mcf51_ctrl, "51ac", 1}, |
| {mcfisa_a|mcfisa_c|mcfusp, mcf51_ctrl, "51ag", 1}, |
| {mcfisa_a|mcfisa_c|mcfusp, mcf51_ctrl, "51cn", 1}, |
| {mcfisa_a|mcfisa_c|mcfusp|mcfmac, mcf51_ctrl, "51em", 1}, |
| {mcfisa_a|mcfisa_c|mcfusp|mcfmac, mcf51_ctrl, "51je", 1}, |
| {mcfisa_a|mcfisa_c|mcfusp|mcfemac, mcf51_ctrl, "51jf", 1}, |
| {mcfisa_a|mcfisa_c|mcfusp|mcfemac, mcf51_ctrl, "51jg", 1}, |
| {mcfisa_a|mcfisa_c|mcfusp, mcf51_ctrl, "51jm", 1}, |
| {mcfisa_a|mcfisa_c|mcfusp|mcfmac, mcf51_ctrl, "51mm", 1}, |
| {mcfisa_a|mcfisa_c|mcfusp, mcf51_ctrl, "51qe", 1}, |
| {mcfisa_a|mcfisa_c|mcfusp|mcfemac, mcf51_ctrl, "51qm", 1}, |
| |
| {mcfisa_a, mcf_ctrl, "5200", 0}, |
| {mcfisa_a, mcf_ctrl, "5202", 1}, |
| {mcfisa_a, mcf_ctrl, "5204", 1}, |
| {mcfisa_a, mcf5206_ctrl, "5206", 1}, |
| |
| {mcfisa_a|mcfhwdiv|mcfmac, mcf5206_ctrl, "5206e", 0}, |
| |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5208_ctrl, "5207", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5208_ctrl, "5208", 0}, |
| |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfmac|mcfusp, mcf5210a_ctrl, "5210a", 0}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfmac|mcfusp, mcf5210a_ctrl, "5211a", 1}, |
| |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfmac|mcfusp, mcf5213_ctrl, "5211", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfmac|mcfusp, mcf5213_ctrl, "5212", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfmac|mcfusp, mcf5213_ctrl, "5213", 0}, |
| |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5216_ctrl, "5214", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5216_ctrl, "5216", 0}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5216_ctrl, "521x", 2}, |
| |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfmac|mcfusp, mcf5221x_ctrl, "5221x", 0}, |
| |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfmac|mcfusp, mcf52223_ctrl, "52221", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfmac|mcfusp, mcf52223_ctrl, "52223", 0}, |
| |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf52235_ctrl, "52230", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf52235_ctrl, "52233", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf52235_ctrl, "52234", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf52235_ctrl, "52235", 0}, |
| |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfmac|mcfusp, mcf5225_ctrl, "5224", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfmac|mcfusp, mcf5225_ctrl, "5225", 0}, |
| |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf52277_ctrl, "52274", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf52277_ctrl, "52277", 0}, |
| |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5235_ctrl, "5232", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5235_ctrl, "5233", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5235_ctrl, "5234", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5235_ctrl, "5235", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5235_ctrl, "523x", 0}, |
| |
| {mcfisa_a|mcfhwdiv|mcfemac, mcf5249_ctrl, "5249", 0}, |
| {mcfisa_a|mcfhwdiv|mcfemac, mcf5250_ctrl, "5250", 0}, |
| {mcfisa_a|mcfhwdiv|mcfemac, mcf5253_ctrl, "5253", 0}, |
| |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf52259_ctrl, "52252", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf52259_ctrl, "52254", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf52259_ctrl, "52255", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf52259_ctrl, "52256", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf52259_ctrl, "52258", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf52259_ctrl, "52259", 0}, |
| |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5271_ctrl, "5270", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5271_ctrl, "5271", 0}, |
| |
| {mcfisa_a|mcfhwdiv|mcfmac, mcf5272_ctrl, "5272", 0}, |
| |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5275_ctrl, "5274", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5275_ctrl, "5275", 0}, |
| |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5282_ctrl, "5280", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5282_ctrl, "5281", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5282_ctrl, "5282", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5282_ctrl, "528x", 0}, |
| |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf53017_ctrl, "53011", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf53017_ctrl, "53012", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf53017_ctrl, "53013", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf53017_ctrl, "53014", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf53017_ctrl, "53015", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf53017_ctrl, "53016", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf53017_ctrl, "53017", 0}, |
| |
| {mcfisa_a|mcfhwdiv|mcfmac, mcf5307_ctrl, "5307", 0}, |
| |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5329_ctrl, "5327", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5329_ctrl, "5328", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5329_ctrl, "5329", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5329_ctrl, "532x", 0}, |
| |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5373_ctrl, "5372", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5373_ctrl, "5373", -1}, |
| {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5373_ctrl, "537x", 0}, |
| |
| {mcfisa_a|mcfisa_b|mcfhwdiv|mcfmac, mcf5407_ctrl, "5407",0}, |
| |
| {mcfisa_a|mcfisa_c|mcfhwdiv|mcfemac|mcfusp, mcf54418_ctrl, "54410", -1}, |
| {mcfisa_a|mcfisa_c|mcfhwdiv|mcfemac|mcfusp, mcf54418_ctrl, "54415", -1}, |
| {mcfisa_a|mcfisa_c|mcfhwdiv|mcfemac|mcfusp, mcf54418_ctrl, "54416", -1}, |
| {mcfisa_a|mcfisa_c|mcfhwdiv|mcfemac|mcfusp, mcf54418_ctrl, "54417", -1}, |
| {mcfisa_a|mcfisa_c|mcfhwdiv|mcfemac|mcfusp, mcf54418_ctrl, "54418", 0}, |
| |
| {mcfisa_a|mcfisa_c|mcfhwdiv|mcfemac|mcfusp, mcf54455_ctrl, "54450", -1}, |
| {mcfisa_a|mcfisa_c|mcfhwdiv|mcfemac|mcfusp, mcf54455_ctrl, "54451", -1}, |
| {mcfisa_a|mcfisa_c|mcfhwdiv|mcfemac|mcfusp, mcf54455_ctrl, "54452", -1}, |
| {mcfisa_a|mcfisa_c|mcfhwdiv|mcfemac|mcfusp, mcf54455_ctrl, "54453", -1}, |
| {mcfisa_a|mcfisa_c|mcfhwdiv|mcfemac|mcfusp, mcf54455_ctrl, "54454", -1}, |
| {mcfisa_a|mcfisa_c|mcfhwdiv|mcfemac|mcfusp, mcf54455_ctrl, "54455", 0}, |
| |
| {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5475_ctrl, "5470", -1}, |
| {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5475_ctrl, "5471", -1}, |
| {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5475_ctrl, "5472", -1}, |
| {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5475_ctrl, "5473", -1}, |
| {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5475_ctrl, "5474", -1}, |
| {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5475_ctrl, "5475", -1}, |
| {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5475_ctrl, "547x", 0}, |
| |
| {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5485_ctrl, "5480", -1}, |
| {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5485_ctrl, "5481", -1}, |
| {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5485_ctrl, "5482", -1}, |
| {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5485_ctrl, "5483", -1}, |
| {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5485_ctrl, "5484", -1}, |
| {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5485_ctrl, "5485", -1}, |
| {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5485_ctrl, "548x", 0}, |
| |
| {fido_a, fido_ctrl, "fidoa", 0}, |
| {fido_a, fido_ctrl, "fido", 1}, |
| |
| {0,NULL,NULL, 0} |
| }; |
| |
| static const struct m68k_cpu *m68k_lookup_cpu |
| (const char *, const struct m68k_cpu *, int, int *); |
| static int m68k_set_arch (const char *, int, int); |
| static int m68k_set_cpu (const char *, int, int); |
| static int m68k_set_extension (const char *, int, int); |
| static void m68k_init_arch (void); |
| |
| /* 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 0 /* Branch byte, word, or long. */ |
| #define BRABSJUNC 1 /* Absolute jump for LONG, unconditional. */ |
| #define BRABSJCOND 2 /* Absolute jump for LONG, conditional. */ |
| #define BRANCHBW 3 /* 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 4 /* Coprocessor branch. */ |
| #define DBCCLBR 5 /* DBcc relaxable with a long branch. */ |
| #define DBCCABSJ 6 /* 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 7 /* 16-bit or 32-bit PC-relative. */ |
| #define PCINDEX 8 /* PC + displacement + index. */ |
| #define ABSTOPCREL 9 /* Absolute relax down to 16-bit PC-relative. */ |
| |
| /* This relaxation is required for branches where there is no long |
| branch and we are in pcrel mode. We generate a bne/beq pair. */ |
| #define BRANCHBWPL 10 /* Branch byte, word or pair of longs |
| */ |
| |
| /* Note that calls to frag_var need to specify the maximum expansion |
| needed; this is currently 12 bytes for bne/beq pair. */ |
| #define FRAG_VAR_SIZE 12 |
| |
| /* 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 |
| |
| Please check tc-m68k.h:md_prepare_relax_scan if changing this table. */ |
| relax_typeS md_relax_table[] = |
| { |
| { 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 }, |
| |
| { 127, -128, 0, TAB (BRANCHBWPL, SHORT) }, |
| { 32767, -32768, 2, TAB (BRANCHBWPL, LONG) }, |
| { 0, 0, 10, 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}, |
| {"align", s_align_bytes, 0}, |
| {"swbeg", s_ignore, 0}, |
| {"long", m68k_elf_cons, 4}, |
| {"extend", float_cons, 'x'}, |
| {"ldouble", float_cons, 'x'}, |
| |
| {"arch", s_m68k_arch, 0}, |
| {"cpu", s_m68k_cpu, 0}, |
| {"gnu_attribute", m68k_elf_gnu_attribute, 0}, |
| |
| /* 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. */ |
| |
| 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}, |
| {"align", s_align_bytes, 0}, |
| {0, 0, 0} |
| }; |
| |
| /* Truncate and sign-extend at 32 bits, so that building on a 64-bit host |
| gives identical results to a 32-bit host. */ |
| #define TRUNC(X) ((valueT) (X) & 0xffffffff) |
| #define SEXT(X) ((TRUNC (X) ^ 0x80000000) - 0x80000000) |
| |
| #define issbyte(x) ((valueT) SEXT (x) + 0x80 < 0x100) |
| #define isubyte(x) ((valueT) TRUNC (x) < 0x100) |
| #define issword(x) ((valueT) SEXT (x) + 0x8000 < 0x10000) |
| #define isuword(x) ((valueT) TRUNC (x) < 0x10000) |
| |
| #define isbyte(x) ((valueT) SEXT (x) + 0xff < 0x1ff) |
| #define isword(x) ((valueT) SEXT (x) + 0xffff < 0x1ffff) |
| #define islong(x) (1) |
| |
| 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]]))) |
| |
| |
| /* 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) && EXTERN_FORCE_RELOC) \ |
| || 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 (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_tls_gd: |
| switch (size) |
| { |
| case 1: |
| return BFD_RELOC_68K_TLS_GD8; |
| case 2: |
| return BFD_RELOC_68K_TLS_GD16; |
| case 4: |
| return BFD_RELOC_68K_TLS_GD32; |
| } |
| break; |
| |
| case pic_tls_ldm: |
| switch (size) |
| { |
| case 1: |
| return BFD_RELOC_68K_TLS_LDM8; |
| case 2: |
| return BFD_RELOC_68K_TLS_LDM16; |
| case 4: |
| return BFD_RELOC_68K_TLS_LDM32; |
| } |
| break; |
| |
| case pic_tls_ldo: |
| switch (size) |
| { |
| case 1: |
| return BFD_RELOC_68K_TLS_LDO8; |
| case 2: |
| return BFD_RELOC_68K_TLS_LDO16; |
| case 4: |
| return BFD_RELOC_68K_TLS_LDO32; |
| } |
| break; |
| |
| case pic_tls_ie: |
| switch (size) |
| { |
| case 1: |
| return BFD_RELOC_68K_TLS_IE8; |
| case 2: |
| return BFD_RELOC_68K_TLS_IE16; |
| case 4: |
| return BFD_RELOC_68K_TLS_IE32; |
| } |
| break; |
| |
| case pic_tls_le: |
| switch (size) |
| { |
| case 1: |
| return BFD_RELOC_68K_TLS_LE8; |
| case 2: |
| return BFD_RELOC_68K_TLS_LE16; |
| case 4: |
| return BFD_RELOC_68K_TLS_LE32; |
| } |
| 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 (fixS *fixP) |
| { |
| /* 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: |
| case BFD_RELOC_68K_TLS_GD32: |
| case BFD_RELOC_68K_TLS_GD16: |
| case BFD_RELOC_68K_TLS_GD8: |
| case BFD_RELOC_68K_TLS_LDM32: |
| case BFD_RELOC_68K_TLS_LDM16: |
| case BFD_RELOC_68K_TLS_LDM8: |
| case BFD_RELOC_68K_TLS_LDO32: |
| case BFD_RELOC_68K_TLS_LDO16: |
| case BFD_RELOC_68K_TLS_LDO8: |
| case BFD_RELOC_68K_TLS_IE32: |
| case BFD_RELOC_68K_TLS_IE16: |
| case BFD_RELOC_68K_TLS_IE8: |
| case BFD_RELOC_68K_TLS_LE32: |
| case BFD_RELOC_68K_TLS_LE16: |
| case BFD_RELOC_68K_TLS_LE8: |
| return 0; |
| |
| case BFD_RELOC_VTABLE_INHERIT: |
| case BFD_RELOC_VTABLE_ENTRY: |
| return 0; |
| |
| default: |
| return 1; |
| } |
| } |
| |
| arelent * |
| tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, 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_where (fixp->fx_file, fixp->fx_line, |
| _("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: |
| case BFD_RELOC_68K_TLS_GD32: |
| case BFD_RELOC_68K_TLS_GD16: |
| case BFD_RELOC_68K_TLS_GD8: |
| case BFD_RELOC_68K_TLS_LDM32: |
| case BFD_RELOC_68K_TLS_LDM16: |
| case BFD_RELOC_68K_TLS_LDM8: |
| case BFD_RELOC_68K_TLS_LDO32: |
| case BFD_RELOC_68K_TLS_LDO16: |
| case BFD_RELOC_68K_TLS_LDO8: |
| case BFD_RELOC_68K_TLS_IE32: |
| case BFD_RELOC_68K_TLS_IE16: |
| case BFD_RELOC_68K_TLS_IE8: |
| case BFD_RELOC_68K_TLS_LE32: |
| case BFD_RELOC_68K_TLS_LE16: |
| case BFD_RELOC_68K_TLS_LE8: |
| 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 = 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; |
| if (!fixp->fx_pcrel) |
| reloc->addend = fixp->fx_addnumber; |
| else |
| reloc->addend = (section->vma |
| + fixp->fx_pcrel_adjust |
| + fixp->fx_addnumber |
| + md_pcrel_from (fixp)); |
| |
| reloc->howto = bfd_reloc_type_lookup (stdoutput, code); |
| gas_assert (reloc->howto != 0); |
| |
| return reloc; |
| } |
| |
| /* Handle of the OPCODE hash table. NULL means any use before |
| m68k_ip_begin() will crash. */ |
| static htab_t op_hash; |
| |
| /* Assemble an m68k instruction. */ |
| |
| static void |
| m68k_ip (char *instring) |
| { |
| char *p; |
| struct m68k_op *opP; |
| const struct m68k_incant *opcode; |
| const char *s; |
| int tmpreg = 0, baseo = 0, outro = 0, nextword; |
| char *pdot, *pdotmove; |
| enum m68k_size siz1, siz2; |
| char c; |
| int losing; |
| int opsfound; |
| struct m68k_op operands_backup[6]; |
| 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 *) str_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 pseudo 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 entry 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 (&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 |
| { |
| int i; |
| |
| /* Make a copy of the operands of this insn so that |
| we can modify them safely, should we want to. */ |
| gas_assert (opsfound <= (int) ARRAY_SIZE (operands_backup)); |
| for (i = 0; i < opsfound; i++) |
| operands_backup[i] = the_ins.operands[i]; |
| |
| for (s = opcode->m_operands, opP = &operands_backup[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 '4': |
| if (opP->mode != AINDR && opP->mode != AINC && opP->mode != ADEC |
| && (opP->mode != DISP |
| || opP->reg < ADDR0 |
| || opP->reg > ADDR7)) |
| ++losing; |
| break; |
| |
| case 'B': /* FOO */ |
| if (opP->mode != ABSL |
| || (flag_long_jumps |
| && startswith (instring, "jbsr"))) |
| losing++; |
| break; |
| |
| case 'b': |
| switch (opP->mode) |
| { |
| case IMMED: |
| case ABSL: |
| case AREG: |
| case FPREG: |
| case CONTROL: |
| case POST: |
| case PRE: |
| case REGLST: |
| losing++; |
| break; |
| default: |
| break; |
| } |
| 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 'e': |
| if (opP->reg != ACC && opP->reg != ACC1 |
| && opP->reg != ACC2 && opP->reg != ACC3) |
| losing++; |
| break; |
| |
| case 'F': |
| if (opP->mode != FPREG) |
| losing++; |
| break; |
| |
| case 'G': |
| if (opP->reg != MACSR) |
| losing++; |
| break; |
| |
| case 'g': |
| if (opP->reg != ACCEXT01 && opP->reg != ACCEXT23) |
| 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 'i': |
| if (opP->mode != LSH && opP->mode != RSH) |
| losing++; |
| break; |
| |
| case 'J': |
| if (opP->mode != CONTROL |
| || opP->reg < USP |
| || opP->reg > last_movec_reg |
| || !control_regs) |
| losing++; |
| else |
| { |
| const enum m68k_register *rp; |
| |
| for (rp = control_regs; *rp; rp++) |
| { |
| if (*rp == opP->reg) |
| break; |
| /* In most CPUs RAMBAR refers to control reg |
| c05 (RAMBAR1), but a few CPUs have it |
| refer to c04 (RAMBAR0). */ |
| else if (*rp == RAMBAR_ALT && opP->reg == RAMBAR) |
| { |
| opP->reg = RAMBAR_ALT; |
| 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 |
| || TRUNC (opP->disp.exp.X_add_number) - 1 > 7) |
| losing++; |
| else if (! m68k_quick |
| && (startswith (instring, "add") |
| || startswith (instring, "sub")) |
| && 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 |
| || TRUNC (opP->disp.exp.X_add_number) > 7) |
| losing++; |
| break; |
| |
| case 'U': |
| if (opP->mode != CONTROL || opP->reg != USP) |
| losing++; |
| break; |
| |
| case 'x': |
| if (opP->mode != IMMED) |
| losing++; |
| else if (opP->disp.exp.X_op != O_constant |
| || (TRUNC (opP->disp.exp.X_add_number) != 0xffffffff |
| && TRUNC (opP->disp.exp.X_add_number) - 1 > 6)) |
| losing++; |
| break; |
| |
| case 'j': |
| if (opP->mode != IMMED) |
| losing++; |
| else if (opP->disp.exp.X_op != O_constant |
| || TRUNC (opP->disp.exp.X_add_number) - 1 > 7) |
| losing++; |
| break; |
| |
| case 'K': |
| if (opP->mode != IMMED) |
| losing++; |
| else if (opP->disp.exp.X_op != O_constant |
| || TRUNC (opP->disp.exp.X_add_number) > 511) |
| 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 'w': |
| switch (opP->mode) |
| { |
| case IMMED: |
| case ABSL: |
| case AREG: |
| case DREG: |
| case FPREG: |
| case CONTROL: |
| case POST: |
| case PRE: |
| case REGLST: |
| losing++; |
| break; |
| default: |
| break; |
| } |
| 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++; |
| 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; |
| |
| case 'y': |
| if (!(opP->mode == AINDR |
| || (opP->mode == DISP |
| && !(opP->reg == PC || opP->reg == ZPC)))) |
| losing++; |
| break; |
| |
| case 'z': |
| if (!(opP->mode == AINDR || opP->mode == DISP)) |
| losing++; |
| break; |
| |
| default: |
| abort (); |
| } |
| |
| if (losing) |
| break; |
| } |
| |
| /* Since we have found the correct instruction, copy |
| in the modifications that we may have made. */ |
| if (!losing) |
| for (i = 0; i < opsfound; i++) |
| the_ins.operands[i] = operands_backup[i]; |
| } |
| |
| if (!losing) |
| break; |
| |
| opcode = opcode->m_next; |
| |
| if (!opcode) |
| { |
| if (ok_arch |
| && !(ok_arch & current_architecture)) |
| { |
| const struct m68k_cpu *cpu; |
| int any = 0; |
| size_t space = 400; |
| char *buf = XNEWVEC (char, space + 1); |
| size_t len; |
| int paren = 1; |
| |
| the_ins.error = buf; |
| /* Make sure there's a NUL at the end of the buffer -- strncpy |
| won't write one when it runs out of buffer. */ |
| buf[space] = 0; |
| #define APPEND(STRING) \ |
| (strncpy (buf, STRING, space), len = strlen (buf), buf += len, space -= len) |
| |
| APPEND (_("invalid instruction for this architecture; needs ")); |
| switch (ok_arch) |
| { |
| case mcfisa_a: |
| APPEND ("ColdFire ISA_A"); |
| break; |
| case mcfhwdiv: |
| APPEND ("ColdFire "); |
| APPEND (_("hardware divide")); |
| break; |
| case mcfisa_aa: |
| APPEND ("ColdFire ISA_A+"); |
| break; |
| case mcfisa_b: |
| APPEND ("ColdFire ISA_B"); |
| break; |
| case mcfisa_c: |
| APPEND ("ColdFire ISA_C"); |
| break; |
| case cfloat: |
| APPEND ("ColdFire fpu"); |
| break; |
| case mfloat: |
| APPEND ("M68K fpu"); |
| break; |
| case mmmu: |
| APPEND ("M68K mmu"); |
| break; |
| case m68020up: |
| APPEND ("68020 "); |
| APPEND (_("or higher")); |
| break; |
| case m68000up: |
| APPEND ("68000 "); |
| APPEND (_("or higher")); |
| break; |
| case m68010up: |
| APPEND ("68010 "); |
| APPEND (_("or higher")); |
| break; |
| default: |
| paren = 0; |
| } |
| if (paren) |
| APPEND (" ("); |
| |
| for (cpu = m68k_cpus; cpu->name; cpu++) |
| if (!cpu->alias && (cpu->arch & ok_arch)) |
| { |
| const struct m68k_cpu *alias; |
| int seen_master = 0; |
| |
| if (any) |
| APPEND (", "); |
| any = 0; |
| APPEND (cpu->name); |
| for (alias = cpu; alias != m68k_cpus; alias--) |
| if (alias[-1].alias >= 0) |
| break; |
| for (; !seen_master || alias->alias > 0; alias++) |
| { |
| if (!alias->alias) |
| seen_master = 1; |
| else |
| { |
| if (any) |
| APPEND (", "); |
| else |
| APPEND (" ["); |
| APPEND (alias->name); |
| any = 1; |
| } |
| } |
| if (any) |
| APPEND ("]"); |
| any = 1; |
| } |
| if (paren) |
| APPEND (")"); |
| #undef APPEND |
| if (!space) |
| { |
| /* We ran out of space, so replace the end of the list |
| with ellipsis. */ |
| buf -= 4; |
| while (*buf != ' ') |
| buf--; |
| strcpy (buf, " ..."); |
| } |
| } |
| else |
| the_ins.error = _("operands mismatch"); |
| return; |
| } |
| |
| 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++) |
| { |
| int have_disp = 0; |
| int use_pl = 0; |
| |
| /* This switch is a doozy. |
| Watch the first step; it's a big one! */ |
| switch (s[0]) |
| { |
| |
| case '*': |
| case '~': |
| case '%': |
| case ';': |
| case '@': |
| case '!': |
| case '&': |
| case '$': |
| case '?': |
| case '/': |
| case '<': |
| case '>': |
| case 'b': |
| case 'm': |
| case 'n': |
| case 'o': |
| case 'p': |
| case 'q': |
| case 'v': |
| case 'w': |
| case 'y': |
| case 'z': |
| case '4': |
| #ifndef NO_68851 |
| case '|': |
| #endif |
| switch (opP->mode) |
| { |
| case IMMED: |
| tmpreg = 0x3c; /* 7.4 */ |
| if (strchr ("bwl", s[1])) |
| nextword = get_num (&opP->disp, 90); |
| 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, 90); |
| |
| /* Convert mode 5 addressing with a zero offset into |
| mode 2 addressing to reduce the instruction size by a |
| word. */ |
| if (! isvar (&opP->disp) |
| && (nextword == 0) |
| && (opP->disp.size == SIZE_UNSPEC) |
| && (opP->reg >= ADDR0) |
| && (opP->reg <= ADDR7)) |
| { |
| tmpreg = 0x10 + opP->reg - ADDR; /* 2.areg */ |
| break; |
| } |
| |
| if (opP->reg == PC |
| && ! isvar (&opP->disp) |
| && m68k_abspcadd) |
| { |
| opP->disp.exp.X_op = O_symbol; |
| opP->disp.exp.X_add_symbol = |
| section_symbol (absolute_section); |
| } |
| |
| /* 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 |
| /* If the displacement needs pic |
| relocation it cannot be relaxed. */ |
| || opP->disp.pic_reloc != pic_none) |
| { |
| addword (0x0170); |
| add_fix ('l', &opP->disp, 1, 2); |
| } |
| else |
| { |
| add_frag (adds (&opP->disp), |
| SEXT (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, 90); |
| if (opP->mode == POST || opP->mode == PRE) |
| outro = get_num (&opP->odisp, 90); |
| /* 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) |
| && !arch_coldfire_fpu (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 it's 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 |
| /* If the displacement needs pic |
| relocation it cannot be relaxed. */ |
| && opP->disp.pic_reloc == pic_none) |
| { |
| /* 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), |
| SEXT (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 inner 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', |