diff options
author | Hans-Peter Nilsson <hp@axis.com> | 2000-07-20 17:45:28 +0000 |
---|---|---|
committer | Hans-Peter Nilsson <hp@axis.com> | 2000-07-20 17:45:28 +0000 |
commit | 3bcbcc3dcf4c2dafb2f5d7384adf5f5bf4e037f4 (patch) | |
tree | 73e2aac98fc5fe48a5d7dfaf27fcceaee01c6fa6 /gas/config | |
parent | 1b61cf923010ce608e6266ec9450d6945b6ff325 (diff) | |
download | gdb-3bcbcc3dcf4c2dafb2f5d7384adf5f5bf4e037f4.zip gdb-3bcbcc3dcf4c2dafb2f5d7384adf5f5bf4e037f4.tar.gz gdb-3bcbcc3dcf4c2dafb2f5d7384adf5f5bf4e037f4.tar.bz2 |
* configure.in: Add CRIS support.
* configure: Regenerate.
* Makefile.am: (CPU_TYPES): Add cris.
(CPU_OBJ_VALID) [aout]: Add cris.
(MULTI_CPU_TYPES): Add cris.
(MULTI_CPU_OBJ_VALID) [aout]: Add cris.
[coff]: Only i386 and mips are valid.
(TARGET_CPU_CFILES): Add config/tc-cris.c.
(TARGET_CPU_HFILES): Add config/tc-cris.h.
(MULTI_CFILES): Add config/e-crisaout.c and config/e-criself.c.
Regenerate dependencies.
* Makefile.in: Regenerate.
* aclocal.m4: Regenerate.
* as.c: Declare crisaout, criself.
* config/tc-cris.h, config/tc-cris.c: New.
* config/e-criself.c, config/e-crisaout.c: New.
* po/POTFILES.in, po/gas.pot: Regenerate.
Diffstat (limited to 'gas/config')
-rw-r--r-- | gas/config/e-crisaout.c | 17 | ||||
-rw-r--r-- | gas/config/e-criself.c | 17 | ||||
-rw-r--r-- | gas/config/tc-cris.c | 2789 | ||||
-rw-r--r-- | gas/config/tc-cris.h | 135 |
4 files changed, 2958 insertions, 0 deletions
diff --git a/gas/config/e-crisaout.c b/gas/config/e-crisaout.c new file mode 100644 index 0000000..b10bb2c --- /dev/null +++ b/gas/config/e-crisaout.c @@ -0,0 +1,17 @@ +#include "as.h" +#include "emul.h" + +static const char * +crisaout_bfd_name () +{ + abort (); + return NULL; +} + +#define emul_bfd_name crisaout_bfd_name +#define emul_format &aout_format_ops + +#define emul_name "crisaout" +#define emul_struct_name crisaout +#define emul_default_endian 0 +#include "emul-target.h" diff --git a/gas/config/e-criself.c b/gas/config/e-criself.c new file mode 100644 index 0000000..e1826a1 --- /dev/null +++ b/gas/config/e-criself.c @@ -0,0 +1,17 @@ +#include "as.h" +#include "emul.h" + +static const char * +criself_bfd_name () +{ + abort (); + return NULL; +} + +#define emul_bfd_name criself_bfd_name +#define emul_format &elf_format_ops + +#define emul_name "criself" +#define emul_struct_name criself +#define emul_default_endian 0 +#include "emul-target.h" diff --git a/gas/config/tc-cris.c b/gas/config/tc-cris.c new file mode 100644 index 0000000..5a73bb1 --- /dev/null +++ b/gas/config/tc-cris.c @@ -0,0 +1,2789 @@ +/* tc-cris.c -- Assembler code for the CRIS CPU core. + Copyright (C) 2000 Free Software Foundation, Inc. + + Contributed by Axis Communications AB, Lund, Sweden. + Originally written for GAS 1.38.1 by Mikael Asker. + Updated, BFDized and GNUified by Hans-Peter Nilsson. + + 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 <stdio.h> +#include <ctype.h> +#include "as.h" +#include "subsegs.h" +#include "opcode/cris.h" + +/* Conventions used here: + Generally speaking, pointers to binutils types such as "fragS" and + "expressionS" get parameter and variable names ending in "P", such as + "fragP", to harmonize with the rest of the binutils code. Other + pointers get a "p" suffix, such as "bufp". Any function or type-name + that could clash with a current or future binutils or GAS function get + a "cris_" prefix. */ + +/* This might be CRIS_INSN_NONE if we're assembling a prefix-insn only. + Note that some prefix-insns might be assembled as CRIS_INSN_NORMAL. */ +enum cris_insn_kind +{ + CRIS_INSN_NORMAL, CRIS_INSN_NONE, CRIS_INSN_BRANCH +}; + +/* An instruction will have one of these prefixes. + Although the same bit-pattern, we handle BDAP with an immediate + expression (eventually quick or [pc+]) different from when we only have + register expressions. */ +enum prefix_kind +{ + PREFIX_NONE, PREFIX_BDAP_IMM, PREFIX_BDAP, PREFIX_BIAP, PREFIX_DIP, + PREFIX_PUSH +}; + +/* The prefix for an instruction. */ +struct cris_prefix +{ + enum prefix_kind kind; + int base_reg_number; + unsigned int opcode; + + /* There might be an expression to be evaluated, like I in [rN+I]. */ + expressionS expr; + + /* If there's an expression, we might need a relocation. Here's the + type of what relocation to start relaxaton with. + The relocation is assumed to start immediately after the prefix insn, + so we don't provide an offset. */ + enum bfd_reloc_code_real reloc; +}; + +/* The description of the instruction being assembled. */ +struct cris_instruction +{ + /* If CRIS_INSN_NONE, then this insn is of zero length. */ + enum cris_insn_kind insn_type; + + /* If a special register was mentioned, this is its description, else + it is NULL. */ + const struct cris_spec_reg *spec_reg; + + unsigned int opcode; + + /* An insn may have at most one expression; theoretically there could be + another in its prefix (but I don't see how that could happen). */ + expressionS expr; + + /* The expression might need a relocation. Here's one to start + relaxation with. */ + enum bfd_reloc_code_real reloc; + + /* The size in bytes of an immediate expression, or zero in + nonapplicable. */ + int imm_oprnd_size; +}; + +static void cris_process_instruction PARAMS ((char *, + struct cris_instruction *, + struct cris_prefix *)); +static int get_bwd_size_modifier PARAMS ((char **, int *)); +static int get_bw_size_modifier PARAMS ((char **, int *)); +static int get_gen_reg PARAMS ((char **, int *)); +static int get_spec_reg PARAMS ((char **, + const struct cris_spec_reg **)); +static int get_autoinc_prefix_or_indir_op PARAMS ((char **, + struct cris_prefix *, + int *, int *, int *, + expressionS *)); +static int get_3op_or_dip_prefix_op PARAMS ((char **, + struct cris_prefix *)); +static int cris_get_expression PARAMS ((char **, expressionS *)); +static int get_flags PARAMS ((char **, int *)); +static void gen_bdap PARAMS ((int, expressionS *)); +static int branch_disp PARAMS ((int)); +static void gen_cond_branch_32 PARAMS ((char *, char *, fragS *, + symbolS *, symbolS *, long int)); +static void cris_number_to_imm PARAMS ((char *, long, int, fixS *)); +static void cris_create_short_jump PARAMS ((char *, addressT, addressT, + fragS *, symbolS *)); +/* Handle to the opcode hash table. */ +static struct hash_control *op_hash = NULL; + +const pseudo_typeS md_pseudo_table[] = +{ + {"dword", cons, 4}, + {NULL, 0, 0} +}; + +static int warn_for_branch_expansion = 0; + +const char cris_comment_chars[] = ";"; + +/* 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 slash-star will always start a comment */ +const char line_comment_chars[] = "#"; +const char line_separator_chars[] = "@"; + +/* Now all floating point support is shut off. See md_atof. */ +const char EXP_CHARS[] = ""; +const char FLT_CHARS[] = ""; + + +/* For CRIS, we encode the relax_substateTs (in e.g. fr_substate) as: + 2 1 0 + ---/ /--+-----------------+-----------------+-----------------+ + | what state ? | how long ? | + ---/ /--+-----------------+-----------------+-----------------+ + + The "how long" bits are 00 = byte, 01 = word, 10 = dword (long). + This is a Un*x convention. + Not all lengths are legit for a given value of (what state). + + Groups for CRIS address relaxing: + + 1. Bcc + length: byte, word, 10-byte expansion + + 2. BDAP + length: byte, word, dword */ + +#define STATE_CONDITIONAL_BRANCH (1) +#define STATE_BASE_PLUS_DISP_PREFIX (2) + +#define STATE_LENGTH_MASK (3) +#define STATE_BYTE (0) +#define STATE_WORD (1) +#define STATE_DWORD (2) +/* Symbol undefined. */ +#define STATE_UNDF (3) +#define STATE_MAX_LENGTH (3) + + +/* These displacements are relative to the adress following the opcode + word of the instruction. The first letter is Byte, Word. The 2nd + letter is Forward, Backward. */ + +#define BRANCH_BF ( 254) +#define BRANCH_BB (-256) +#define BRANCH_WF (2+ 32767) +#define BRANCH_WB (2+-32768) + +#define BDAP_BF ( 127) +#define BDAP_BB (-128) +#define BDAP_WF ( 32767) +#define BDAP_WB (-32768) + +#define ENCODE_RELAX(what, length) (((what) << 2) + (length)) + +const relax_typeS md_cris_relax_table[] = +{ + /* Error sentinel (0, 0). */ + {1, 1, 0, 0}, + + /* Unused (0, 1). */ + {1, 1, 0, 0}, + + /* Unused (0, 2). */ + {1, 1, 0, 0}, + + /* Unused (0, 3). */ + {1, 1, 0, 0}, + + /* Bcc o (1, 0). */ + {BRANCH_BF, BRANCH_BB, 0, ENCODE_RELAX (1, 1)}, + + /* Bcc [PC+] (1, 1). */ + {BRANCH_WF, BRANCH_WB, 2, ENCODE_RELAX (1, 2)}, + + /* BEXT/BWF, BA, JUMP (external), JUMP (always), Bnot_cc, JUMP (default) + (1, 2). */ + {0, 0, 10, 0}, + + /* Unused (1, 3). */ + {1, 1, 0, 0}, + + /* BDAP o (2, 0). */ + {BDAP_BF, BDAP_BB, 0, ENCODE_RELAX (2, 1)}, + + /* BDAP.[bw] [PC+] (2, 1). */ + {BDAP_WF, BDAP_WB, 2, ENCODE_RELAX (2, 2)}, + + /* BDAP.d [PC+] (2, 2). */ + {0, 0, 4, 0} +}; + +#undef BRANCH_BF +#undef BRANCH_BB +#undef BRANCH_WF +#undef BRANCH_WB +#undef BDAP_BF +#undef BDAP_BB +#undef BDAP_WF +#undef BDAP_WB + + +/* Target-specific multicharacter options, not const-declared at usage + in 2.9.1 and CVS of 2000-02-16. */ +struct option md_longopts[] = +{ + {NULL, no_argument, NULL, 0} +}; + +/* Not const-declared at usage in 2.9.1. */ +size_t md_longopts_size = sizeof (md_longopts); +const char *md_shortopts = "hHN"; + + +/* At first glance, this may seems wrong and should be 4 (ba + nop); but + since a short_jump must skip a *number* of long jumps, it must also be + a long jump. Here, we hope to make it a "ba [16bit_offs]" and a "nop" + for the delay slot and hope that the jump table at most needs + 32767/4=8191 long-jumps. A branch is better than a jump, since it is + relative; we will not have a reloc to fix up somewhere. + + Note that we can't add relocs, because relaxation uses these fixed + numbers, and md_create_short_jump is called after relaxation. */ + +const int md_short_jump_size = 6; +const int md_long_jump_size = 6; + + +/* Report output format. */ +const char * +cris_target_format () +{ + switch (OUTPUT_FLAVOR) + { + case bfd_target_aout_flavour: + return "a.out-cris"; + + case bfd_target_elf_flavour: + return "elf32-cris"; + + default: + abort (); + return NULL; + } +} + +/* Prepare machine-dependent frags for relaxation. + + Called just before relaxation starts. Any symbol that is now undefined + will not become defined. + + Return the correct fr_subtype in the frag. + + Return the initial "guess for fr_var" to caller. The guess for fr_var + is *actually* the growth beyond fr_fix. Whatever we do to grow fr_fix + or fr_var contributes to our returned value. + + Although it may not be explicit in the frag, pretend + fr_var starts with a value. */ + +int +md_estimate_size_before_relax (fragP, segment_type) + fragS *fragP; + /* The segment is either N_DATA or N_TEXT. */ + segT segment_type; +{ + int old_fr_fix; + + old_fr_fix = fragP->fr_fix; + + switch (fragP->fr_subtype) + { + case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_UNDF): + if (S_GET_SEGMENT (fragP->fr_symbol) == segment_type) + { + /* The symbol lies in the same segment - a relaxable case. */ + fragP->fr_subtype + = ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_BYTE); + } + else + { + /* Unknown or not the same segment, so not relaxable. */ + char *writep; + + /* A small branch-always (2 bytes) to the "real" branch + instruction, plus a delay-slot nop (2 bytes), plus a + jump (2 plus 4 bytes). See gen_cond_branch_32. */ + fragP->fr_fix += 2 + 2 + 2 + 4; + writep = fragP->fr_literal + old_fr_fix; + gen_cond_branch_32 (fragP->fr_opcode, writep, fragP, + fragP->fr_symbol, (symbolS *)NULL, + fragP->fr_offset); + frag_wane (fragP); + } + break; + + case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_BYTE): + case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_WORD): + /* We *might* give a better initial guess if we peek at offsets + now, but the caller will relax correctly and without this, so + don't bother. */ + break; + + case ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_UNDF): + /* Note that we can not do anything sane with relaxing + [rX + a_known_symbol_in_text], it will have to be a 32-bit + value. + + We could play tricks with managing a constant pool and make + a_known_symbol_in_text a "bdap [pc + offset]" pointing there, but + that's pointless, it can only be longer and slower. + + Off-topic: If PIC becomes *really* important, and has to be done + in the assembler and linker only (which would be weird or + clueless), we can so something. Imagine: + move.x [r + 32_bit_symbol],r + move.x [32_bit_symbol],r + move.x 32_bit_symbol,r + can be shortened by a word (8-bit offset) if we are close to the + symbol or keep its length (16-bit offset) or be a word longer + (32-bit offset). Then change the 32_bit_symbol into a "bdap [pc + + offset]", and put the offset to the 32_bit_symbol in "offset". + Weird, to say the least, and we still have to add support for a + PC-relative relocation in the loader (shared libraries). But + it's an interesting thought. */ + + if (S_GET_SEGMENT (fragP->fr_symbol) != absolute_section) + { + /* Go for dword if not absolute or same segment. */ + fragP->fr_subtype + = ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_DWORD); + fragP->fr_var += 4; + } + else + { + /* Absolute expression. */ + long int value; + value = S_GET_VALUE (fragP->fr_symbol) + fragP->fr_offset; + + if (value >= -128 && value <= 127) + { + /* Byte displacement. */ + (fragP->fr_opcode)[0] = value; + } + else + { + /* Word or dword displacement. */ + int pow2_of_size = 1; + char *writep; + + if (value < -32768 || value > 32767) + { + /* Outside word range, make it a dword. */ + pow2_of_size = 2; + } + + /* Modify the byte-offset BDAP into a word or dword offset + BDAP. Or really, a BDAP rX,8bit into a + BDAP.[wd] rX,[PC+] followed by a and a word or dword. */ + (fragP->fr_opcode)[0] = BDAP_PC_LOW + pow2_of_size * 16; + + /* Keep the register number in the highest four bits. */ + (fragP->fr_opcode)[1] &= 0xF0; + (fragP->fr_opcode)[1] |= BDAP_INCR_HIGH; + + /* It grew by two or four bytes. */ + fragP->fr_fix += 1 << pow2_of_size; + writep = fragP->fr_literal + old_fr_fix; + md_number_to_chars (writep, value, 1 << pow2_of_size); + } + frag_wane (fragP); + } + break; + + default: + BAD_CASE (fragP->fr_subtype); + } + + return fragP->fr_var + (fragP->fr_fix - old_fr_fix); +} + + +/* Perform post-processing of machine-dependent frags after relaxation. + Called after relaxation is finished. + In: Address of frag. + fr_type == rs_machine_dependent. + fr_subtype is what the address relaxed to. + + Out: Any fixS:s and constants are set up. + + The caller will turn the frag into a ".space 0". */ + +void +md_convert_frag (abfd, sec, fragP) + bfd *abfd ATTRIBUTE_UNUSED; + segT sec ATTRIBUTE_UNUSED; + fragS *fragP; +{ + /* Pointer to first byte in variable-sized part of the frag. */ + char *var_partp; + + /* Pointer to first opcode byte in frag. */ + char *opcodep; + + /* Used to check integrity of the relaxation. + One of 2 = long, 1 = word, or 0 = byte. */ + int length_code; + + /* Size in bytes of variable-sized part of frag. */ + int var_part_size = 0; + + /* This is part of *fragP. It contains all information about addresses + and offsets to varying parts. */ + symbolS *symbolP; + unsigned long var_part_offset; + + /* Where, in file space, is _var of *fragP? */ + unsigned long address_of_var_part = 0; + + /* Where, in file space, does addr point? */ + unsigned long target_address; + + know (fragP->fr_type == rs_machine_dependent); + + length_code = fragP->fr_subtype & STATE_LENGTH_MASK; + know (length_code >= 0 && length_code < STATE_MAX_LENGTH); + + var_part_offset = fragP->fr_fix; + var_partp = fragP->fr_literal + var_part_offset; + opcodep = fragP->fr_opcode; + + symbolP = fragP->fr_symbol; + target_address + = (symbolP + ? S_GET_VALUE (symbolP) + symbol_get_frag(fragP->fr_symbol)->fr_address + : 0 ) + fragP->fr_offset; + address_of_var_part = fragP->fr_address + var_part_offset; + + switch (fragP->fr_subtype) + { + case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_BYTE): + opcodep[0] = branch_disp ((target_address - address_of_var_part)); + var_part_size = 0; + break; + + case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_WORD): + /* We had a quick immediate branch, now turn it into a word one i.e. a + PC autoincrement. */ + opcodep[0] = BRANCH_PC_LOW; + opcodep[1] &= 0xF0; + opcodep[1] |= BRANCH_INCR_HIGH; + md_number_to_chars (var_partp, + (long) (target_address - (address_of_var_part + 2)), + 2); + var_part_size = 2; + break; + + case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_DWORD): + gen_cond_branch_32 (fragP->fr_opcode, var_partp, fragP, + fragP->fr_symbol, (symbolS *)NULL, + fragP->fr_offset); + /* Ten bytes added: a branch, nop and a jump. */ + var_part_size = 2 + 2 + 4 + 2; + break; + + case ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_BYTE): + var_partp[0] = target_address - (address_of_var_part + 1); + var_part_size = 0; + break; + + case ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_WORD): + /* We had a BDAP 8-bit "quick immediate", now turn it into a 16-bit + one that uses PC autoincrement. */ + opcodep[0] = BDAP_PC_LOW + (1 << 4); + opcodep[1] &= 0xF0; + opcodep[1] |= BDAP_INCR_HIGH; + md_number_to_chars (var_partp, (long)(target_address), 2); + var_part_size = 2; + break; + + case ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_DWORD): + /* We had a BDAP 16-bit "word", change the offset to a dword. */ + opcodep[0] = BDAP_PC_LOW + (2 << 4); + opcodep[1] &= 0xF0; + opcodep[1] |= BDAP_INCR_HIGH; + if (fragP->fr_symbol == NULL) + md_number_to_chars (var_partp, fragP->fr_offset, 4); + else + fix_new (fragP, var_partp - fragP->fr_literal, 4, fragP->fr_symbol, + fragP->fr_offset, 0, BFD_RELOC_32); + var_part_size = 4; + break; + + default: + BAD_CASE (fragP->fr_subtype); + break; + } + + fragP->fr_fix += var_part_size; + +} + +/* Generate a short jump around a secondary jump table. + Used by md_create_long_jump. + + This used to be md_create_short_jump, but is now called from + md_create_long_jump instead, when sufficient. + since the sizes of the jumps are the same. It used to be brittle, + making possibilities for creating bad code. */ + +static void +cris_create_short_jump (storep, from_addr, to_addr, fragP, to_symbol) + char *storep; + addressT from_addr; + addressT to_addr; + fragS *fragP ATTRIBUTE_UNUSED; + symbolS *to_symbol ATTRIBUTE_UNUSED; +{ + long int distance; + + distance = to_addr - from_addr; + + if (-254 <= distance && distance <= 256) + { + /* Create a "short" short jump: "BA distance - 2". */ + storep[0] = branch_disp (distance-2); + storep[1] = BA_QUICK_HIGH; + + /* A nop for the delay slot. */ + md_number_to_chars (storep+2, NOP_OPCODE, 2); + + /* The extra word should be filled with something sane too. Make it + a nop to keep disassembly sane. */ + md_number_to_chars (storep+4, NOP_OPCODE, 2); + } + else + { + /* Make it a "long" short jump: "BA (PC+)". */ + md_number_to_chars (storep, BA_PC_INCR_OPCODE, 2); + + /* ".WORD distance - 4". */ + md_number_to_chars (storep + 2, (long) (distance - 4), 2); + + /* A nop for the delay slot. */ + md_number_to_chars (storep+4, NOP_OPCODE, 2); + } +} + + +/* Generate a long jump in a secondary jump table. + + storep Where to store the jump instruction. + from_addr Address of the jump instruction. + to_addr Destination address of the jump. + fragP Which frag the destination address operand + lies in. + to_symbol Destination symbol. */ + +void +md_create_long_jump (storep, from_addr, to_addr, fragP, to_symbol) + char *storep; + addressT from_addr; + addressT to_addr; + fragS *fragP; + symbolS *to_symbol; +{ + long int distance; + + distance = to_addr - from_addr; + + if (-32763 <= distance && distance <= 32772) + { + /* Then make it a "short" long jump. */ + cris_create_short_jump (storep, from_addr, to_addr, fragP, + to_symbol); + } + else + { + /* We have a "long" long jump: "JUMP (PC+)". */ + md_number_to_chars (storep, JUMP_PC_INCR_OPCODE, 2); + + /* Follow with a ".DWORD to_addr". */ + fix_new (fragP, storep + 2 - fragP->fr_literal, 4, to_symbol, + 0, 0, BFD_RELOC_32); + } +} + + +/* Port-specific assembler initialization. */ +void +md_begin () +{ + const char *hashret = NULL; + int i = 0; + + /* Set up a hash table for the instructions. */ + op_hash = hash_new (); + if (op_hash == NULL) + as_fatal (_("Virtual memory exhausted")); + + while (cris_opcodes[i].name != NULL) + { + const char *name = cris_opcodes[i].name; + hashret = hash_insert (op_hash, name, (PTR) &cris_opcodes[i]); + + if (hashret != NULL && *hashret != '\0') + as_fatal (_("Can't hash `%s': %s\n"), cris_opcodes[i].name, + *hashret == 0 ? _("(unknown reason)") : hashret); + do + { + if (cris_opcodes[i].match & cris_opcodes[i].lose) + as_fatal (_("Buggy opcode: `%s' \"%s\"\n"), cris_opcodes[i].name, + cris_opcodes[i].args); + + ++i; + } while (cris_opcodes[i].name != NULL + && strcmp (cris_opcodes[i].name, name) == 0); + } +} + + +/* Assemble a source line. */ +void +md_assemble (str) + char *str; +{ + struct cris_instruction output_instruction; + struct cris_prefix prefix; + char *opcodep; + char *p; + + know (str); + + /* Do the low-level grunt - assemble to bits and split up into a prefix + and ordinary insn. */ + cris_process_instruction (str, &output_instruction, &prefix); + + /* Handle any prefixes to the instruction. */ + switch (prefix.kind) + { + case PREFIX_NONE: + break; + + /* When the expression is unknown for a BDAP, it can need 0, 2 or 4 + extra bytes, so we handle it separately. */ + case PREFIX_BDAP_IMM: + gen_bdap (prefix.base_reg_number, &prefix.expr); + break; + + case PREFIX_BDAP: + case PREFIX_BIAP: + case PREFIX_DIP: + opcodep = frag_more (2); + + /* Output the prefix opcode. */ + md_number_to_chars (opcodep, (long) prefix.opcode, 2); + + /* This only happens for DIP, but is ok for the others as they have + no reloc. */ + if (prefix.reloc != BFD_RELOC_NONE) + { + /* Output an absolute mode address. */ + p = frag_more (4); + fix_new_exp (frag_now, (p - frag_now->fr_literal), 4, + &prefix.expr, 0, prefix.reloc); + } + break; + + case PREFIX_PUSH: + opcodep = frag_more (2); + + /* Output the prefix opcode. Being a "push", we add the negative + size of the register to "sp". */ + if (output_instruction.spec_reg != NULL) + { + /* Special register. */ + opcodep[0] = -output_instruction.spec_reg->reg_size; + } + else + { + /* General register. */ + opcodep[0] = -4; + } + opcodep[1] = (REG_SP << 4) + (BDAP_QUICK_OPCODE >> 8); + break; + + default: + BAD_CASE (prefix.kind); + } + + /* If we only had a prefix insn, we're done. */ + if (output_instruction.insn_type == CRIS_INSN_NONE) + return; + + /* Done with the prefix. Continue with the main instruction. */ + opcodep = frag_more (2); + + /* Output the instruction opcode. */ + md_number_to_chars (opcodep, (long)(output_instruction.opcode), 2); + + /* Output the symbol-dependent instruction stuff. */ + if (output_instruction.insn_type == CRIS_INSN_BRANCH) + { + segT to_seg = absolute_section; + int is_undefined = 0; + int length_code; + + if (output_instruction.expr.X_op != O_constant) + { + to_seg = S_GET_SEGMENT (output_instruction.expr.X_add_symbol); + + if (to_seg == undefined_section) + is_undefined = 1; + } + + if (output_instruction.expr.X_op == O_constant + || to_seg == now_seg || is_undefined) + { + /* If is_undefined, then the expression may BECOME now_seg. */ + length_code = is_undefined ? STATE_UNDF : STATE_BYTE; + + /* Make room for max ten bytes of variable length. */ + frag_var (rs_machine_dependent, 10, 0, + ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, length_code), + output_instruction.expr.X_add_symbol, + output_instruction.expr.X_add_number, + opcodep); + } + else + { + /* We have: to_seg != now_seg && to_seg != undefined_section. + This means it is a branch to a known symbol in another + section. Code in data? Weird but valid. Emit a 32-bit + branch. */ + gen_cond_branch_32 (opcodep, frag_more (10), frag_now, + output_instruction.expr.X_add_symbol, + (symbolS *)NULL, + output_instruction.expr.X_add_number); + } + } + else + { + if (output_instruction.imm_oprnd_size > 0) + { + /* The intruction has an immediate operand. */ + enum bfd_reloc_code_real reloc = 0; + + switch (output_instruction.imm_oprnd_size) + { + /* Any byte-size immediate constants are treated as + word-size. FIXME: Thus overflow check does not work + correctly. */ + + case 2: + reloc = BFD_RELOC_16; + break; + + case 4: + reloc = BFD_RELOC_32; + break; + + default: + BAD_CASE (output_instruction.imm_oprnd_size); + } + + p = frag_more (output_instruction.imm_oprnd_size); + fix_new_exp (frag_now, (p - frag_now->fr_literal), + output_instruction.imm_oprnd_size, + &output_instruction.expr, 0, reloc); + } + else if (output_instruction.reloc != BFD_RELOC_NONE) + { + /* An immediate operand that has a relocation and needs to be + processed further. */ + + /* It is important to use fix_new_exp here and everywhere else + (and not fix_new), as fix_new_exp can handle "difference + expressions" - where the expression contains a difference of + two symbols in the same segment. */ + fix_new_exp (frag_now, (opcodep - frag_now->fr_literal), 2, + &output_instruction.expr, 0, + output_instruction.reloc); + } + } +} + + +/* Low level text-to-bits assembly. */ +static void +cris_process_instruction (insn_text, out_insnp, prefixp) + char *insn_text; + struct cris_instruction *out_insnp; + struct cris_prefix *prefixp; +{ + char *s; + char modified_char = 0; + const char *args; + struct cris_opcode *instruction; + char *operands; + int match = 0; + int mode; + int regno; + int size_bits; + + /* Reset these fields to a harmless state in case we need to return in + error. */ + prefixp->kind = PREFIX_NONE; + prefixp->reloc = BFD_RELOC_NONE; + out_insnp->insn_type = CRIS_INSN_NORMAL; + out_insnp->imm_oprnd_size = 0; + + /* Find the end of the opcode mnemonic. We assume (true in 2.9.1) + that the caller has translated the opcode to lower-case, up to the + first non-letter. */ + for (operands = insn_text; + islower (*operands); + ++operands) + ; + + /* Terminate the opcode after letters, but save the character there if + it was of significance. */ + switch (*operands) + { + case '\0': + break; + + case '.': + /* Put back the modified character later */ + modified_char = *operands; + /* FALLTHROUGH. */ + + case ' ': + /* Consume the character after the mnemonic and replace */ + /* it with '\0'. */ + *operands++ = '\0'; + break; + + default: + as_bad (_("Unknown opcode: `%s'"), insn_text); + return; + } + + /* Find the instruction. */ + instruction = (struct cris_opcode *) hash_find (op_hash, insn_text); + if (instruction == NULL) + { + as_bad (_("Unknown opcode: `%s'"), insn_text); + return; + } + + /* Put back the modified character. */ + switch (modified_char) + { + case 0: + break; + + default: + *--operands = modified_char; + } + + + /* Try to match an opcode table slot. */ + for (s = operands; + ; + ) + { + int imm_expr_found; + + /* Initialize *prefixp, perhaps after being modified for a + "near match". */ + prefixp->kind = PREFIX_NONE; + prefixp->reloc = BFD_RELOC_NONE; + + /* Initialize *out_insnp. */ + memset (out_insnp, 0, sizeof (*out_insnp)); + out_insnp->opcode = instruction->match; + out_insnp->reloc = BFD_RELOC_NONE; + out_insnp->insn_type = CRIS_INSN_NORMAL; + out_insnp->imm_oprnd_size = 0; + + imm_expr_found = 0; + + /* Build the opcode, checking as we go to make sure that the + operands match. */ + for (args = instruction->args; + ; + ++args) + { + switch (*args) + { + case '\0': + /* If we've come to the end of arguments, we're done. */ + if (*s == '\0') + match = 1; + break; + + case '!': + /* Non-matcher character for disassembly. + Ignore it here. */ + continue; + + case ',': + case ' ': + /* These must match exactly. */ + if (*s++ == *args) + continue; + break; + + case 'B': + /* This is not really an operand, but causes a "BDAP + -size,SP" prefix to be output, for PUSH instructions. */ + prefixp->kind = PREFIX_PUSH; + continue; + + case 'b': + /* This letter marks an operand that should not be matched + in the assembler. It is a branch with 16-bit + displacement. The assembler will create them from the + 8-bit flavor when necessary. The assembler does not + support the [rN+] operand, as the [r15+] that is + generated for 16-bit displacements. */ + break; + + case 'c': + /* A 5-bit unsigned immediate in bits <4:0>. */ + if (! cris_get_expression (&s, &out_insnp->expr)) + break; + else + { + if (out_insnp->expr.X_op == O_constant + && (out_insnp->expr.X_add_number < 0 + || out_insnp->expr.X_add_number > 31)) + as_bad (_("Immediate value not in 5 bit unsigned range: %ld"), + out_insnp->expr.X_add_number); + + out_insnp->reloc = BFD_RELOC_CRIS_UNSIGNED_5; + continue; + } + + case 'C': + /* A 4-bit unsigned immediate in bits <3:0>. */ + if (! cris_get_expression (&s, &out_insnp->expr)) + break; + else + { + if (out_insnp->expr.X_op == O_constant + && (out_insnp->expr.X_add_number < 0 + || out_insnp->expr.X_add_number > 15)) + as_bad (_("Immediate value not in 4 bit unsigned range: %ld"), + out_insnp->expr.X_add_number); + + out_insnp->reloc = BFD_RELOC_CRIS_UNSIGNED_4; + continue; + } + + case 'D': + /* General register in bits <15:12> and <3:0>. */ + if (! get_gen_reg (&s, ®no)) + break; + else + { + out_insnp->opcode |= regno /* << 0 */; + out_insnp->opcode |= regno << 12; + continue; + } + + case 'f': + /* Flags from the condition code register. */ + { + int flags = 0; + + if (! get_flags (&s, &flags)) + break; + + out_insnp->opcode + |= ((flags & 0xf0) << 8) | (flags & 0xf); + continue; + } + + case 'i': + /* A 6-bit signed immediate in bits <5:0>. */ + if (! cris_get_expression (&s, &out_insnp->expr)) + break; + else + { + if (out_insnp->expr.X_op == O_constant + && (out_insnp->expr.X_add_number < -32 + || out_insnp->expr.X_add_number > 31)) + as_bad (_("Immediate value not in 6 bit range: %ld"), + out_insnp->expr.X_add_number); + out_insnp->reloc = BFD_RELOC_CRIS_SIGNED_6; + continue; + } + + case 'I': + /* A 6-bit unsigned immediate in bits <5:0>. */ + if (! cris_get_expression (&s, &out_insnp->expr)) + break; + else + { + if (out_insnp->expr.X_op == O_constant + && (out_insnp->expr.X_add_number < 0 + || out_insnp->expr.X_add_number > 63)) + as_bad (_("Immediate value not in 6 bit unsigned range: %ld"), + out_insnp->expr.X_add_number); + out_insnp->reloc = BFD_RELOC_CRIS_UNSIGNED_6; + continue; + } + + case 'M': + /* A size modifier, B, W or D, to be put in a bit position + suitable for CLEAR instructions (i.e. reflecting a zero + register). */ + if (! get_bwd_size_modifier (&s, &size_bits)) + break; + else + { + switch (size_bits) + { + case 0: + out_insnp->opcode |= 0 << 12; + break; + + case 1: + out_insnp->opcode |= 4 << 12; + break; + + case 2: + out_insnp->opcode |= 8 << 12; + break; + } + continue; + } + + case 'm': + /* A size modifier, B, W or D, to be put in bits <5:4>. */ + if (! get_bwd_size_modifier (&s, &size_bits)) + break; + else + { + out_insnp->opcode |= size_bits << 4; + continue; + } + + case 'o': + /* A branch expression. */ + if (! cris_get_expression (&s, &out_insnp->expr)) + break; + else + { + out_insnp->insn_type = CRIS_INSN_BRANCH; + continue; + } + + case 'O': + /* A BDAP expression for any size, "expr,r". */ + if (! cris_get_expression (&s, &prefixp->expr)) + break; + else + { + if (*s != ',') + break; + + s++; + + if (!get_gen_reg (&s, &prefixp->base_reg_number)) + break; + + /* Since 'O' is used with an explicit bdap, we have no + "real" instruction. */ + prefixp->kind = PREFIX_BDAP_IMM; + out_insnp->insn_type = CRIS_INSN_NONE; + continue; + } + + case 'P': + /* Special register in bits <15:12>. */ + if (! get_spec_reg (&s, &out_insnp->spec_reg)) + break; + else + { + /* Use of some special register names come with a + specific warning. Note that we have no ".cpu type" + pseudo yet, so some of this is just unused + framework. */ + if (out_insnp->spec_reg->warning) + as_warn (out_insnp->spec_reg->warning); + else if (out_insnp->spec_reg->applicable_version + == cris_ver_warning) + /* Others have a generic warning. */ + as_warn (_("Unimplemented register `%s' specified"), + out_insnp->spec_reg->name); + + out_insnp->opcode + |= out_insnp->spec_reg->number << 12; + continue; + } + + case 'p': + /* This character is used in the disassembler to + recognize a prefix instruction to fold into the + addressing mode for the next instruction. It is + ignored here. */ + continue; + + case 'R': + /* General register in bits <15:12>. */ + if (! get_gen_reg (&s, ®no)) + break; + else + { + out_insnp->opcode |= regno << 12; + continue; + } + + case 'r': + /* General register in bits <3:0>. */ + if (! get_gen_reg (&s, ®no)) + break; + else + { + out_insnp->opcode |= regno /* << 0 */; + continue; + } + + case 'S': + /* Source operand in bit <10> and a prefix; a 3-operand + prefix. */ + if (! get_3op_or_dip_prefix_op (&s, prefixp)) + break; + else + continue; + + case 's': + /* Source operand in bits <10>, <3:0> and optionally a + prefix; i.e. an indirect operand or an side-effect + prefix. */ + if (! get_autoinc_prefix_or_indir_op (&s, prefixp, &mode, + ®no, + &imm_expr_found, + &out_insnp->expr)) + break; + else + { + if (prefixp->kind != PREFIX_NONE) + { + /* A prefix, so it has the autoincrement bit + set. */ + out_insnp->opcode |= (AUTOINCR_BIT << 8); + } + else + /* No prefix. The "mode" variable contains bits like + whether or not this is autoincrement mode. */ + out_insnp->opcode |= (mode << 10); + + out_insnp->opcode |= regno /* << 0 */ ; + continue; + } + + case 'x': + /* Rs.m in bits <15:12> and <5:4>. */ + if (! get_gen_reg (&s, ®no) + || ! get_bwd_size_modifier (&s, &size_bits)) + break; + else + { + out_insnp->opcode |= + (regno << 12) | (size_bits << 4); + continue; + } + + case 'y': + /* Source operand in bits <10>, <3:0> and optionally a + prefix; i.e. an indirect operand or an side-effect + prefix. + + The difference to 's' is that this does not allow an + "immediate" expression. */ + if (! get_autoinc_prefix_or_indir_op (&s, prefixp, + &mode, ®no, + &imm_expr_found, + &out_insnp->expr) + || imm_expr_found) + break; + else + { + if (prefixp->kind != PREFIX_NONE) + { + /* A prefix, and those matched here always have + side-effects (see 's' case). */ + out_insnp->opcode |= (AUTOINCR_BIT << 8); + } + else + { + /* No prefix. The "mode" variable contains bits + like whether or not this is autoincrement + mode. */ + out_insnp->opcode |= (mode << 10); + } + + out_insnp->opcode |= regno /* << 0 */; + continue; + } + + case 'z': + /* Size modifier (B or W) in bit <4>. */ + if (! get_bw_size_modifier (&s, &size_bits)) + break; + else + { + out_insnp->opcode |= size_bits << 4; + continue; + } + + default: + BAD_CASE (*args); + } + + /* We get here when we fail a match above or we found a + complete match. Break out of this loop. */ + break; + } + + /* Was it a match or a miss? */ + if (match == 0) + { + /* If it's just that the args don't match, maybe the next + item in the table is the same opcode but with + matching operands. */ + if (instruction[1].name != NULL + && ! strcmp (instruction->name, instruction[1].name)) + { + /* Yep. Restart and try that one instead. */ + ++instruction; + s = operands; + continue; + } + else + { + /* We've come to the end of instructions with this + opcode, so it must be an error. */ + as_bad (_("Illegal operands")); + return; + } + } + else + { + /* We have a match. Check if there's anything more to do. */ + if (imm_expr_found) + { + /* There was an immediate mode operand, so we must check + that it has an appropriate size. */ + + switch (instruction->imm_oprnd_size) + { + default: + case SIZE_NONE: + /* Shouldn't happen; this one does not have immediate + operands with different sizes. */ + BAD_CASE (instruction->imm_oprnd_size); + break; + + case SIZE_FIX_32: + out_insnp->imm_oprnd_size = 4; + break; + + case SIZE_SPEC_REG: + switch (out_insnp->spec_reg->reg_size) + { + case 1: + if (out_insnp->expr.X_op == O_constant + && (out_insnp->expr.X_add_number < -128 + || out_insnp->expr.X_add_number > 255)) + as_bad (_("Immediate value not in 8 bit range: %ld"), + out_insnp->expr.X_add_number); + /* FALLTHROUGH. */ + case 2: + /* FIXME: We need an indicator in the instruction + table to pass on, to indicate if we need to check + overflow for a signed or unsigned number. */ + if (out_insnp->expr.X_op == O_constant + && (out_insnp->expr.X_add_number < -32768 + || out_insnp->expr.X_add_number > 65535)) + as_bad (_("Immediate value not in 16 bit range: %ld"), + out_insnp->expr.X_add_number); + out_insnp->imm_oprnd_size = 2; + break; + + case 4: + out_insnp->imm_oprnd_size = 4; + break; + + default: + BAD_CASE (out_insnp->spec_reg->reg_size); + } + break; + + case SIZE_FIELD: + switch (size_bits) + { + case 0: + if (out_insnp->expr.X_op == O_constant + && (out_insnp->expr.X_add_number < -128 + || out_insnp->expr.X_add_number > 255)) + as_bad (_("Immediate value not in 8 bit range: %ld"), + out_insnp->expr.X_add_number); + /* FALLTHROUGH. */ + case 1: + if (out_insnp->expr.X_op == O_constant + && (out_insnp->expr.X_add_number < -32768 + || out_insnp->expr.X_add_number > 65535)) + as_bad (_("Immediate value not in 16 bit range: %ld"), + out_insnp->expr.X_add_number); + out_insnp->imm_oprnd_size = 2; + break; + + case 2: + out_insnp->imm_oprnd_size = 4; + break; + + default: + BAD_CASE (out_insnp->spec_reg->reg_size); + } + } + } + } + break; + } +} + + +/* Get a B, W, or D size modifier from the string pointed out by *cPP, + which must point to a '.' in front of the modifier. On successful + return, *cPP is advanced to the character following the size + modifier, and is undefined otherwise. + + cPP Pointer to pointer to string starting + with the size modifier. + + size_bitsp Pointer to variable to contain the size bits on + successful return. + + Return 1 iff a correct size modifier is found, else 0. */ + +static int +get_bwd_size_modifier (cPP, size_bitsp) + char **cPP; + int *size_bitsp; +{ + if (**cPP != '.') + return 0; + else + { + /* Consume the '.' */ + (*cPP)++; + + switch (**cPP) + { + case 'B': + case 'b': + *size_bitsp = 0; + break; + + case 'W': + case 'w': + *size_bitsp = 1; + break; + + case 'D': + case 'd': + *size_bitsp = 2; + break; + + default: + return 0; + } + + /* Consume the size letter. */ + (*cPP)++; + return 1; + } +} + + +/* Get a B or W size modifier from the string pointed out by *cPP, + which must point to a '.' in front of the modifier. On successful + return, *cPP is advanced to the character following the size + modifier, and is undefined otherwise. + + cPP Pointer to pointer to string starting + with the size modifier. + + size_bitsp Pointer to variable to contain the size bits on + successful return. + + Return 1 iff a correct size modifier is found, else 0. */ + +static int +get_bw_size_modifier (cPP, size_bitsp) + char **cPP; + int *size_bitsp; +{ + if (**cPP != '.') + return 0; + else + { + /* Consume the '.' */ + (*cPP)++; + + switch (**cPP) + { + case 'B': + case 'b': + *size_bitsp = 0; + break; + + case 'W': + case 'w': + *size_bitsp = 1; + break; + + default: + return 0; + } + + /* Consume the size letter. */ + (*cPP)++; + return 1; + } +} + + +/* Get a general register from the string pointed out by *cPP. The + variable *cPP is advanced to the character following the general + register name on a successful return, and has its initial position + otherwise. + + cPP Pointer to pointer to string, beginning with a general + register name. + + regnop Pointer to int containing the register number. + + Return 1 iff a correct general register designator is found, + else 0. */ + +static int +get_gen_reg (cPP, regnop) + char **cPP; + int *regnop; +{ + char *oldp; + oldp = *cPP; + + switch (**cPP) + { + case 'P': + case 'p': + /* "P" as in "PC"? Consume the "P". */ + (*cPP)++; + + if ((**cPP == 'C' || **cPP == 'c') + && ! isalnum ((*cPP)[1])) + { + /* It's "PC": consume the "c" and we're done. */ + (*cPP)++; + *regnop = REG_PC; + return 1; + } + break; + + case 'R': + case 'r': + /* Hopefully r[0-9] or r1[0-5]. Consume 'R' or 'r' */ + (*cPP)++; + + if (isdigit (**cPP)) + { + /* It's r[0-9]. Consume and check the next digit. */ + *regnop = **cPP - '0'; + (*cPP)++; + + if (! isalnum (**cPP)) + { + /* No more digits, we're done. */ + return 1; + } + else + { + /* One more digit. Consume and add. */ + *regnop = *regnop*10 + (**cPP - '0'); + + /* We need to check for a valid register number; Rn, + 0 <= n <= MAX_REG. */ + if (*regnop <= MAX_REG) + { + /* Consume second digit. */ + (*cPP)++; + return 1; + } + } + } + break; + + case 'S': + case 's': + /* "S" as in "SP"? Consume the "S". */ + (*cPP)++; + if (**cPP == 'P' || **cPP == 'p') + { + /* It's "SP": consume the "p" and we're done. */ + (*cPP)++; + *regnop = REG_SP; + return 1; + } + break; + + default: + /* Just here to silence compilation warnings. */ + ; + } + + /* We get here if we fail. Restore the pointer. */ + *cPP = oldp; + return 0; +} + + +/* Get a special register from the string pointed out by *cPP. The + variable *cPP is advanced to the character following the special + register name if one is found, and retains its original position + otherwise. + + cPP Pointer to pointer to string starting with a special register + name. + + sregpp Pointer to Pointer to struct spec_reg, where a pointer to the + register description will be stored. + + Return 1 iff a correct special register name is found. */ + +static int +get_spec_reg (cPP, sregpp) + char **cPP; + const struct cris_spec_reg **sregpp; +{ + char *s1; + const char *s2; + + const struct cris_spec_reg *sregp; + + /* Loop over all special registers. */ + for (sregp = cris_spec_regs; + sregp->name != NULL; + sregp++) + { + + /* Start over from beginning of the supposed name. */ + s1 = *cPP; + s2 = sregp->name; + + while (*s2 != '\0' + && (isupper (*s1) ? tolower (*s1) == *s2 : *s1 == *s2)) + { + s1++; + s2++; + } + + /* For a match, we must have consumed the name in the table, and we + must be outside what could be part of a name. Assume here that a + test for alphanumerics is sufficient for a name test. */ + if (*s2 == 0 && ! isalnum (*s1)) + { + /* We have a match. Update the pointer and be done. */ + *cPP = s1; + *sregpp = sregp; + return 1; + } + } + + /* If we got here, we did not find any name. */ + return 0; +} + + +/* Get an unprefixed or side-effect-prefix operand from the string pointed + out by *cPP. The pointer *cPP is advanced to the character following + the indirect operand if we have success, else it contains an undefined + value. + + cPP Pointer to pointer to string beginning with the first + character of the supposed operand. + + prefixp Pointer to structure containing an optional instruction + prefix. + + is_autoincp Pointer to int indicating the indirect or autoincrement + bits. + + src_regnop Pointer to int containing the source register number in + the instruction. + + imm_foundp Pointer to an int indicating if an immediate expression + is found. + + imm_exprP Pointer to a structure containing an immediate + expression, if success and if *imm_foundp is nonzero. + + Return 1 iff a correct indirect operand is found. */ + +static int +get_autoinc_prefix_or_indir_op (cPP, prefixp, is_autoincp, src_regnop, + imm_foundp, imm_exprP) + char **cPP; + struct cris_prefix *prefixp; + int *is_autoincp; + int *src_regnop; + int *imm_foundp; + expressionS *imm_exprP; +{ + /* Assume there was no immediate mode expression. */ + *imm_foundp = 0; + + if (**cPP == '[') + { + /* So this operand is one of: + Indirect: [rN] + Autoincrement: [rN+] + Indexed with assign: [rN=rM+rO.S] + Offset with assign: [rN=rM+I], [rN=rM+[rO].s], [rN=rM+[rO+].s] + + Either way, consume the '['. */ + (*cPP)++; + + /* Get the rN register. */ + if (! get_gen_reg (cPP, src_regnop)) + /* If there was no register, then this cannot match. */ + return 0; + else + { + /* We got the register, now check the next character. */ + switch (**cPP) + { + case ']': + /* Indirect mode. We're done here. */ + prefixp->kind = PREFIX_NONE; + *is_autoincp = 0; + break; + + case '+': + /* This must be an auto-increment mode, if there's a + match. */ + prefixp->kind = PREFIX_NONE; + *is_autoincp = 1; + + /* We consume this character and break out to check the + closing ']'. */ + (*cPP)++; + break; + + case '=': + /* This must be indexed with assign, or offset with assign + to match. */ + (*cPP)++; + + /* Either way, the next thing must be a register. */ + if (! get_gen_reg (cPP, &prefixp->base_reg_number)) + /* No register, no match. */ + return 0; + else + { + /* We've consumed "[rN=rM", so we must be looking at + "+rO.s]" or "+I]", or "-I]", or "+[rO].s]" or + "+[rO+].s]". */ + if (**cPP == '+') + { + int index_reg_number; + (*cPP)++; + + if (**cPP == '[') + { + int size_bits; + /* This must be [rx=ry+[rz].s] or + [rx=ry+[rz+].s] or no match. We must be + looking at rz after consuming the '['. */ + (*cPP)++; + + if (!get_gen_reg (cPP, &index_reg_number)) + return 0; + + prefixp->kind = PREFIX_BDAP; + prefixp->opcode + = (BDAP_INDIR_OPCODE + + (prefixp->base_reg_number << 12) + + index_reg_number); + + if (**cPP == '+') + { + /* We've seen "[rx=ry+[rz+" here, so now we + know that there must be "].s]" left to + check. */ + (*cPP)++; + prefixp->opcode |= AUTOINCR_BIT << 8; + } + + /* If it wasn't autoincrement, we don't need to + add anything. */ + + /* Check the next-to-last ']'. */ + if (**cPP != ']') + return 0; + + (*cPP)++; + + /* Check the ".s" modifier. */ + if (! get_bwd_size_modifier (cPP, &size_bits)) + return 0; + + prefixp->opcode |= size_bits << 4; + + /* Now we got [rx=ry+[rz+].s or [rx=ry+[rz].s. + We break out to check the final ']'. */ + break; + } + else + /* It wasn't an idirection. Check if it's a + register. */ + if (get_gen_reg (cPP, &index_reg_number)) + { + int size_bits; + + /* Indexed with assign mode: "[rN+rM.S]". */ + prefixp->kind = PREFIX_BIAP; + prefixp->opcode + = (BIAP_OPCODE + (index_reg_number << 12) + + prefixp->base_reg_number /* << 0 */); + + if (! get_bwd_size_modifier (cPP, &size_bits)) + /* Size missing, this isn't a match. */ + return 0; + else + { + /* Size found, break out to check the + final ']'. */ + prefixp->opcode |= size_bits << 4; + break; + } + } + else + /* Not a register. Then this must be "[rN+I]". */ + if (cris_get_expression (cPP, &prefixp->expr)) + { + /* We've got offset with assign mode. Fill + in the blanks and break out to match the + final ']'. */ + prefixp->kind = PREFIX_BDAP_IMM; + break; + } + else + /* Neither register nor expression found, so + this can't be a match. */ + return 0; + } + else + /* Not "[rN+" but perhaps "[rN-"? */ + if (**cPP == '-') + { + /* We must have an offset with assign mode. */ + if (! cris_get_expression (cPP, &prefixp->expr)) + /* No expression, no match. */ + return 0; + else + { + /* We've got offset with assign mode. Fill + in the blanks and break out to match the + final ']'. */ + prefixp->kind = PREFIX_BDAP_IMM; + break; + } + } + else + /* Neither '+' nor '-' after "[rN=rM". Lose. */ + return 0; + } + default: + /* Neither ']' nor '+' nor '=' after "[rN". Lose. */ + return 0; + } + } + + /* When we get here, we have a match and will just check the closing + ']'. We can still fail though. */ + if (**cPP != ']') + return 0; + else + { + /* Don't forget to consume the final ']'. + Then return in glory. */ + (*cPP)++; + return 1; + } + } + else + /* No indirection. Perhaps a constant? */ + if (cris_get_expression (cPP, imm_exprP)) + { + /* Expression found, this is immediate mode. */ + prefixp->kind = PREFIX_NONE; + *is_autoincp = 1; + *src_regnop = REG_PC; + *imm_foundp = 1; + return 1; + } + + /* No luck today. */ + return 0; +} + + +/* This function gets an indirect operand in a three-address operand + combination from the string pointed out by *cPP. The pointer *cPP is + advanced to the character following the indirect operand on success, or + has an unspecified value on failure. + + cPP Pointer to pointer to string begining + with the operand + + prefixp Pointer to structure containing an + instruction prefix + + Returns 1 iff a correct indirect operand is found. */ + +static int +get_3op_or_dip_prefix_op (cPP, prefixp) + char **cPP; + struct cris_prefix *prefixp; +{ + if (**cPP != '[') + /* We must have a '[' or it's a clean failure. */ + return 0; + else + { + int reg_number; + + /* Eat the first '['. */ + (*cPP)++; + + if (**cPP == '[') + { + /* A second '[', so this must be double-indirect mode. */ + (*cPP)++; + prefixp->kind = PREFIX_DIP; + prefixp->opcode = DIP_OPCODE; + + /* Get the register or fail entirely. */ + if (! get_gen_reg (cPP, ®_number)) + return 0; + else + { + prefixp->opcode |= reg_number /* << 0 */ ; + if (**cPP == '+') + { + /* Since we found a '+', this must be double-indirect + autoincrement mode. */ + (*cPP)++; + prefixp->opcode |= AUTOINCR_BIT << 8; + } + + /* There's nothing particular to do, if this was a + double-indirect *without* autoincrement. */ + } + + /* Check the first ']'. The second one is checked at the end. */ + if (**cPP != ']') + return 0; + + /* Eat the first ']', so we'll be looking at a second ']'. */ + (*cPP)++; + } + else + /* No second '['. Then we should have a register here, making + it "[rN". */ + if (get_gen_reg (cPP, &prefixp->base_reg_number)) + { + /* This must be indexed or offset mode: "[rN+I]" or + "[rN+rM.S]" or "[rN+[rM].S]" or "[rN+[rM+].S]". */ + if (**cPP == '+') + { + /* Not the first alternative, must be one of the last + three. */ + int index_reg_number; + + (*cPP)++; + + if (**cPP == '[') + { + /* This is "[rx+["... Expect a register next. */ + int size_bits; + (*cPP)++; + + if (!get_gen_reg (cPP, &index_reg_number)) + return 0; + + prefixp->kind = PREFIX_BDAP; + prefixp->opcode + = (BDAP_INDIR_OPCODE + + (prefixp->base_reg_number << 12) + + index_reg_number); + + /* We've seen "[rx+[ry", so check if this is + autoincrement. */ + if (**cPP == '+') + { + /* Yep, now at "[rx+[ry+". */ + (*cPP)++; + prefixp->opcode |= AUTOINCR_BIT << 8; + } + /* If it wasn't autoincrement, we don't need to + add anything. */ + + /* Check a first closing ']': "[rx+[ry]" or + "[rx+[ry+]". */ + if (**cPP != ']') + return 0; + (*cPP)++; + + /* Now expect a size modifier ".S". */ + if (! get_bwd_size_modifier (cPP, &size_bits)) + return 0; + + prefixp->opcode |= size_bits << 4; + + /* Ok, all interesting stuff has been seen: + "[rx+[ry+].S" or "[rx+[ry].S". We only need to + expect a final ']', which we'll do in a common + closing session. */ + } + else + /* Seen "[rN+", but not a '[', so check if we have a + register. */ + if (get_gen_reg (cPP, &index_reg_number)) + { + /* This is indexed mode: "[rN+rM.S]" or + "[rN+rM.S+]". */ + int size_bits; + prefixp->kind = PREFIX_BIAP; + prefixp->opcode + = (BIAP_OPCODE + | prefixp->base_reg_number /* << 0 */ + | (index_reg_number << 12)); + + /* */ + if (! get_bwd_size_modifier (cPP, &size_bits)) + /* Missing size, so fail. */ + return 0; + else + /* Size found. Add that piece and drop down to + the common checking of the closing ']'. */ + prefixp->opcode |= size_bits << 4; + } + else + /* Seen "[rN+", but not a '[' or a register, so then + it must be a constant "I". */ + if (cris_get_expression (cPP, &prefixp->expr)) + { + /* Expression found, so fill in the bits of offset + mode and drop down to check the closing ']'. */ + prefixp->kind = PREFIX_BDAP_IMM; + } + else + /* Nothing valid here: lose. */ + return 0; + } + else + /* Seen "[rN" but no '+', so check if it's a '-'. */ + if (**cPP == '-') + { + /* Yep, we must have offset mode. */ + if (! cris_get_expression (cPP, &prefixp->expr)) + /* No expression, so we lose. */ + return 0; + else + { + /* Expression found to make this offset mode, so + fill those bits and drop down to check the + closing ']'. */ + prefixp->kind = PREFIX_BDAP_IMM; + } + } + else + { + /* We've seen "[rN", but not '+' or '-'; rather a ']'. + Hmm. Normally this is a simple indirect mode that we + shouldn't match, but if we expect ']', then we have a + zero offset, so it can be a three-address-operand, + like "[rN],rO,rP", thus offset mode. + + Don't eat the ']', that will be done in the closing + ceremony. */ + prefixp->expr.X_op = O_constant; + prefixp->expr.X_add_number = 0; + prefixp->expr.X_add_symbol = NULL; + prefixp->expr.X_op_symbol = NULL; + prefixp->kind = PREFIX_BDAP_IMM; + } + } + else + { + /* A '[', but no second '[', and no register. Check if we + have an expression, making this "[I]" for a double-indirect + prefix. */ + if (cris_get_expression (cPP, &prefixp->expr)) + { + /* Expression found, the so called absolute mode for a + double-indirect prefix on PC. */ + prefixp->kind = PREFIX_DIP; + prefixp->opcode + = DIP_OPCODE | (AUTOINCR_BIT << 8) | REG_PC; + prefixp->reloc = BFD_RELOC_32; + } + else + /* Neither '[' nor register nor expression. We lose. */ + return 0; + } + } + + /* We get here as a closing ceremony to a successful match. We just + need to check the closing ']'. */ + if (**cPP != ']') + /* Oops. Close but no air-polluter. */ + return 0; + + /* Don't forget to consume that ']', before returning in glory. */ + (*cPP)++; + return 1; +} + + +/* Get an expression from the string pointed out by *cPP. + The pointer *cPP is advanced to the character following the expression + on a success, or retains its original value otherwise. + + cPP Pointer to pointer to string beginning with the expression. + + exprP Pointer to structure containing the expression. + + Return 1 iff a correct expression is found. */ + +static int +cris_get_expression (cPP, exprP) + char **cPP; + expressionS *exprP; +{ + char *saved_input_line_pointer; + segT exp; + + /* The "expression" function expects to find an expression at the + global variable input_line_pointer, so we have to save it to give + the impression that we don't fiddle with global variables. */ + saved_input_line_pointer = input_line_pointer; + input_line_pointer = *cPP; + + exp = expression (exprP); + if (exprP->X_op == O_illegal || exprP->X_op == O_absent) + { + input_line_pointer = saved_input_line_pointer; + return 0; + } + + /* Everything seems to be fine, just restore the global + input_line_pointer and say we're successful. */ + *cPP = input_line_pointer; + input_line_pointer = saved_input_line_pointer; + return 1; +} + + +/* Get a sequence of flag characters from *spp. The pointer *cPP is + advanced to the character following the expression. The flag + characters are consecutive, no commas or spaces. + + cPP Pointer to pointer to string beginning with the expression. + + flagp Pointer to int to return the flags expression. + + Return 1 iff a correct flags expression is found. */ + +static int +get_flags (cPP, flagsp) + char **cPP; + int *flagsp; +{ + for (;;) + { + switch (**cPP) + { + case 'd': + case 'D': + case 'm': + case 'M': + *flagsp |= 0x80; + break; + + case 'e': + case 'E': + case 'b': + case 'B': + *flagsp |= 0x40; + break; + + case 'i': + case 'I': + *flagsp |= 0x20; + break; + + case 'x': + case 'X': + *flagsp |= 0x10; + break; + + case 'n': + case 'N': + *flagsp |= 0x8; + break; + + case 'z': + case 'Z': + *flagsp |= 0x4; + break; + + case 'v': + case 'V': + *flagsp |= 0x2; + break; + + case 'c': + case 'C': + *flagsp |= 1; + break; + + default: + /* We consider this successful if we stop at a comma or + whitespace. Anything else, and we consider it a failure. */ + if (**cPP != ',' + && **cPP != 0 + && ! isspace (**cPP)) + return 0; + else + return 1; + } + + /* Don't forget to consume each flag character. */ + (*cPP)++; + } +} + + +/* Generate code and fixes for a BDAP prefix. + + base_regno Int containing the base register number. + + exprP Pointer to structure containing the offset expression. */ + +static void +gen_bdap (base_regno, exprP) + int base_regno; + expressionS *exprP; +{ + unsigned int opcode; + char *opcodep; + + /* Put out the prefix opcode; assume quick immediate mode at first. */ + opcode = BDAP_QUICK_OPCODE | (base_regno << 12); + opcodep = frag_more (2); + md_number_to_chars (opcodep, opcode, 2); + + if (exprP->X_op == O_constant) + { + /* We have an absolute expression that we know the size of right + now. */ + long int value; + int size; + + value = exprP->X_add_number; + if (value < -32768 || value > 32767) + /* Outside range for a "word", make it a dword. */ + size = 2; + else + /* Assume "word" size. */ + size = 1; + + /* If this is a signed-byte value, we can fit it into the prefix + insn itself. */ + if (value >= -128 && value <= 127) + opcodep[0] = value; + else + { + /* This is a word or dword displacement, which will be put in a + word or dword after the prefix. */ + char *p; + + opcodep[0] = BDAP_PC_LOW + (size << 4); + opcodep[1] &= 0xF0; + opcodep[1] |= BDAP_INCR_HIGH; + p = frag_more (1 << size); + md_number_to_chars (p, value, 1 << size); + } + } + else + /* The expression is not defined yet but may become absolute. We make + it a relocation to be relaxed. */ + frag_var (rs_machine_dependent, 4, 0, + ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_UNDF), + exprP->X_add_symbol, exprP->X_add_number, opcodep); +} + + +/* Encode a branch displacement in the range -256..254 into the form used + by CRIS conditional branch instructions. + + offset The displacement value in bytes. */ + +static int +branch_disp (offset) + int offset; +{ + int disp; + + disp = offset & 0xFE; + + if (offset < 0) + disp |= 1; + + return disp; +} + + +/* Generate code and fixes for a 32-bit conditional branch instruction + created by "extending" an existing 8-bit branch instruction. + + opcodep Pointer to the word containing the original 8-bit branch + instruction. + + writep Pointer to "extension area" following the first instruction + word. + + fragP Pointer to the frag containing the instruction. + + add_symP, Parts of the destination address expression. + sub_symP, + add_num. */ + +static void +gen_cond_branch_32 (opcodep, writep, fragP, add_symP, sub_symP, add_num) + char *opcodep; + char *writep; + fragS *fragP; + symbolS *add_symP; + symbolS *sub_symP; + long int add_num; +{ + if (warn_for_branch_expansion) + { + /* FIXME: Find out and change to as_warn_where. Add testcase. */ + as_warn (_("32-bit conditional branch generated")); + } + + /* Here, writep points to what will be opcodep + 2. First, we change + the actual branch in opcodep[0] and opcodep[1], so that in the + final insn, it will look like: + opcodep+10: Bcc .-6 + + This means we don't have to worry about changing the opcode or + messing with te delay-slot instruction. So, we move it to last in + the "extended" branch, and just change the displacement. Admittedly, + it's not the optimal extended construct, but we should get this + rarely enough that it shouldn't matter. */ + + writep[8] = branch_disp (-2-6); + writep[9] = opcodep[1]; + + /* Then, we change the branch to an unconditional branch over the + extended part, to the new location of the Bcc: + opcodep: BA .+10 + opcodep+2: NOP + + Note that these two writes are to currently different locations, + merged later. */ + + md_number_to_chars (opcodep, BA_QUICK_OPCODE + 8, 2); + md_number_to_chars (writep, NOP_OPCODE, 2); + + /* Then the extended thing, the 32-bit jump insn. + opcodep+4: JUMP [PC+] */ + + md_number_to_chars (writep + 2, JUMP_PC_INCR_OPCODE, 2); + + /* We have to fill in the actual value too. + opcodep+6: .DWORD + This is most probably an expression, but we can cope with an absolute + value too. FIXME: Testcase needed. */ + + if (add_symP == NULL && sub_symP == NULL) + /* An absolute address. */ + md_number_to_chars (writep + 4, add_num, 4); + else + { + /* Not absolute, we have to make it a frag for later evaluation. */ + know (sub_symP == 0); + + fix_new (fragP, writep + 4 - fragP->fr_literal, 4, add_symP, + add_num, 0, BFD_RELOC_32); + } +} + + +/* This *could* be: + + Turn a string in input_line_pointer into a floating point constant of + type type, and store the appropriate bytes in *litp. The number of + LITTLENUMS emitted is stored in *sizep. + + type A character from FLTCHARS that describes what kind of + floating-point number is wanted. + + litp A pointer to an array that the result should be stored in. + + sizep A pointer to an integer where the size of the result is stored. + + But we don't support floating point constants in assembly code *at all*, + since it's suboptimal and just opens up bug opportunities. GCC emits + the bit patterns as hex. All we could do here is to emit what GCC + would have done in the first place. *Nobody* writes floating-point + code as assembly code, but if they do, they should be able enough to + find out the correct bit patterns and use them. */ + +char * +md_atof (type, litp, sizep) + char type ATTRIBUTE_UNUSED; + char *litp ATTRIBUTE_UNUSED; + int *sizep ATTRIBUTE_UNUSED; +{ + /* FIXME: Is this function mentioned in the internals.texi manual? If + not, add it. */ + return _("Bad call to md_atof () - floating point formats are not supported"); +} + + +/* Turn a number as a fixS * into a series of bytes that represents the + number on the target machine. The purpose of this procedure is the + same as that of md_number_to_chars but this procedure is supposed to + handle general bit field fixes and machine-dependent fixups. + + bufp Pointer to an array where the result should be stored. + + val The value to store. + + n The number of bytes in "val" that should be stored. + + fixP The fix to be applied to the bit field starting at bufp. */ + +static void +cris_number_to_imm (bufp, val, n, fixP) + char *bufp; + long val; + int n; + fixS *fixP; +{ + segT sym_seg; + + know (n <= 4); + know (fixP); + + /* We put the relative "vma" for the other segment for inter-segment + relocations in the object data to stay binary "compatible" (with an + uninteresting old version) for the relocation. + Maybe delete some day. */ + if (fixP->fx_addsy + && (sym_seg = S_GET_SEGMENT (fixP->fx_addsy)) != now_seg) + val += sym_seg->vma; + + switch (fixP->fx_r_type) + { + /* Ditto here, we put the addend into the object code as + well as the reloc addend. Keep it that way for now, to simplify + regression tests on the object file contents. FIXME: Seems + uninteresting now that we have a test suite. */ + + case BFD_RELOC_32: + /* No use having warnings here, since most hosts have a 32-bit type + for "long" (which will probably change soon, now that I wrote + this). */ + bufp[3] = (val >> 24) & 0xFF; + bufp[2] = (val >> 16) & 0xFF; + bufp[1] = (val >> 8) & 0xFF; + bufp[0] = val & 0xFF; + break; + + /* FIXME: The 16 and 8-bit cases should have a way to check + whether a signed or unsigned (or any signedness) number is + accepted. + FIXME: Does the as_bad calls find the line number by themselves, + or should we change them into as_bad_where? */ + + case BFD_RELOC_16: + if (val > 0xffff || val < -32768) + as_bad (_("Value not in 16 bit range: %ld"), val); + if (! fixP->fx_addsy) + { + bufp[1] = (val >> 8) & 0xFF; + bufp[0] = val & 0xFF; + } + break; + + case BFD_RELOC_8: + if (val > 255 || val < -128) + as_bad (_("Value not in 8 bit range: %ld"), val); + if (! fixP->fx_addsy) + bufp[0] = val & 0xFF; + break; + + case BFD_RELOC_CRIS_UNSIGNED_4: + if (val > 15 || val < 0) + as_bad (_("Value not in 4 bit unsigned range: %ld"), val); + if (! fixP->fx_addsy) + bufp[0] |= val & 0x0F; + break; + + case BFD_RELOC_CRIS_UNSIGNED_5: + if (val > 31 || val < 0) + as_bad (_("Value not in 5 bit unsigned range: %ld"), val); + if (! fixP->fx_addsy) + bufp[0] |= val & 0x1F; + break; + + case BFD_RELOC_CRIS_SIGNED_6: + if (val > 31 || val < -32) + as_bad (_("Value not in 6 bit range: %ld"), val); + if (! fixP->fx_addsy) + bufp[0] |= val & 0x3F; + break; + + case BFD_RELOC_CRIS_UNSIGNED_6: + if (val > 63 || val < 0) + as_bad (_("Value not in 6 bit unsigned range: %ld"), val); + if (! fixP->fx_addsy) + bufp[0] |= val & 0x3F; + break; + + case BFD_RELOC_CRIS_BDISP8: + if (! fixP->fx_addsy) + bufp[0] = branch_disp (val); + break; + + case BFD_RELOC_NONE: + /* May actually happen automatically. For example at broken + words, if the word turns out not to be broken. + FIXME: When? Which testcase? */ + if (! fixP->fx_addsy) + md_number_to_chars (bufp, val, n); + break; + + case BFD_RELOC_VTABLE_INHERIT: + /* This borrowed from tc-ppc.c on a whim. */ + if (fixP->fx_addsy + && !S_IS_DEFINED (fixP->fx_addsy) + && !S_IS_WEAK (fixP->fx_addsy)) + S_SET_WEAK (fixP->fx_addsy); + /* FALLTHROUGH. */ + case BFD_RELOC_VTABLE_ENTRY: + /* FIXME: I'm not sure why we do this (as does other ports), but it + might be that this relocation can only be finished at link time. */ + fixP->fx_done = 0; + break; + + default: + BAD_CASE (fixP->fx_r_type); + } +} + + +/* Processes machine-dependent command line options. Called once for + each option on the command line that the machine-independent part of + GAS does not understand. */ +int +md_parse_option (arg, argp) + int arg; + char *argp ATTRIBUTE_UNUSED; +{ + switch (arg) + { + case 'H': + case 'h': + md_show_usage (stdout); + exit (0); /* Don't continue */ + + case 'N': + warn_for_branch_expansion = 1; + return 1; + + default: + return 0; + } +} + +/* Round up a section size to the appropriate boundary. */ +valueT +md_section_align (segment, size) + segT segment; + valueT size; +{ + /* Round all sects to multiple of 4, except the bss section, which + we'll round to word-size. + + FIXME: Check if this really matters. All sections should be + rounded up, and all sections should (optionally) be assumed to be + dword-aligned, it's just that there is actual usage of linking to a + multiple of two. */ + if (OUTPUT_FLAVOR == bfd_target_aout_flavour) + { + if (segment == bss_section) + return (size + 1) & ~1; + return (size + 3) & ~3; + } + else + { + /* FIXME: Is this wanted? It matches the testsuite, but that's not + really a valid reason. */ + if (segment == text_section) + return (size + 3) & ~3; + } + + return size; +} + + +/* Generate a machine-dependent relocation. */ +arelent * +tc_gen_reloc (section, fixP) + asection *section ATTRIBUTE_UNUSED; + fixS *fixP; +{ + arelent *relP; + bfd_reloc_code_real_type code; + + switch (fixP->fx_r_type) + { + case BFD_RELOC_32: + case BFD_RELOC_16: + case BFD_RELOC_8: + case BFD_RELOC_VTABLE_INHERIT: + case BFD_RELOC_VTABLE_ENTRY: + code = fixP->fx_r_type; + break; + default: + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Semantics error. This type of operand can not be relocated, it must be an assembly-time constant")); + return 0; + } + + relP = (arelent *) xmalloc (sizeof (arelent)); + assert (relP != 0); + relP->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); + *relP->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy); + relP->address = fixP->fx_frag->fr_address + fixP->fx_where; + + if (fixP->fx_pcrel) + /* FIXME: Is this correct? */ + relP->addend = fixP->fx_addnumber; + else + /* At least *this one* is correct. */ + relP->addend = fixP->fx_offset; + + /* This is the standard place for KLUDGEs to work around bugs in + bfd_install_relocation (first such note in the documentation + appears with binutils-2.8). + + That function bfd_install_relocation does the wrong thing with + putting stuff into the addend of a reloc (it should stay out) for a + weak symbol. The really bad thing is that it adds the + "segment-relative offset" of the symbol into the reloc. In this + case, the reloc should instead be relative to the symbol with no + other offset than the assembly code shows; and since the symbol is + weak, any local definition should be ignored until link time (or + thereafter). + To wit: weaksym+42 should be weaksym+42 in the reloc, + not weaksym+(offset_from_segment_of_local_weaksym_definition) + + To "work around" this, we subtract the segment-relative offset of + "known" weak symbols. This evens out the extra offset. + + That happens for a.out but not for ELF, since for ELF, + bfd_install_relocation uses the "special function" field of the + howto, and does not execute the code that needs to be undone. */ + + if (OUTPUT_FLAVOR == bfd_target_aout_flavour + && fixP->fx_addsy && S_IS_WEAK (fixP->fx_addsy) + && ! bfd_is_und_section (S_GET_SEGMENT (fixP->fx_addsy))) + { + relP->addend -= S_GET_VALUE (fixP->fx_addsy); + } + + relP->howto = bfd_reloc_type_lookup (stdoutput, code); + if (! relP->howto) + { + const char *name; + + name = S_GET_NAME (fixP->fx_addsy); + if (name == NULL) + name = _("<unknown>"); + as_fatal (_("Cannot generate relocation type for symbol %s, code %s"), + name, bfd_get_reloc_code_name (code)); + } + + return relP; +} + + +/* Machine-dependent usage-output. */ +void +md_show_usage (stream) + FILE *stream; +{ + fprintf (stream, _("\n-- GNU as for CRIS\n")); + fprintf (stream, "\n"); + fprintf (stream, + _("*** Usage: as-cris [switches] [-o objectfile] [files...]\n")); + fprintf (stream, _("Target-specific switches:\n")); + fprintf (stream, _(" -h, -H : Don't execute, print this help text.\n")); + fprintf (stream, + _(" -N : Warn when branches are expanded to jumps.\n\n")); + fprintf (stream, _("Use as-cris --help to see more options.\n")); + fprintf (stream, _("objectfile : Output file in the a.out format %s"), + _("described in the users manual.\n")); + fprintf (stream, _(" Default output file is \"a.out\".\n")); + fprintf (stream, _("files ... : Input files in the source format %s"), + _(" described in the users manual.\n")); + fprintf (stream, _(" Default input file is standard input.\n")); + fprintf (stream, _("Description : Assembler for the CRIS processor.\n")); +} + + +/* Apply a fixS (fixup of an instruction or data that we didn't have + enough info to complete immediately) to the data in a frag. */ + +int +md_apply_fix (fixP, valP) + fixS *fixP; + valueT *valP; +{ + long val = *valP; + + char *buf = fixP->fx_where + fixP->fx_frag->fr_literal; + + if (fixP->fx_addsy == 0 && !fixP->fx_pcrel) + fixP->fx_done = 1; + + if (fixP->fx_bit_fixP || fixP->fx_im_disp != 0) + { + as_bad_where (fixP->fx_file, fixP->fx_line, _("Invalid relocation")); + fixP->fx_done = 1; + } + else + { + /* I took this from tc-arc.c, since we used to not support + fx_subsy != NULL. I'm not totally sure it's TRT. */ + if (fixP->fx_subsy != (symbolS *) NULL) + { + if (S_GET_SEGMENT (fixP->fx_subsy) == absolute_section) + val -= S_GET_VALUE (fixP->fx_subsy); + else + { + /* We can't actually support subtracting a symbol. */ + as_bad_where (fixP->fx_file, fixP->fx_line, + _("expression too complex")); + } + } + + cris_number_to_imm (buf, val, fixP->fx_size, fixP); + } + + return 1; +} + + +/* All relocations are relative to the location just after the fixup; + the address of the fixup plus its size. */ + +long +md_pcrel_from (fixP) + fixS *fixP; +{ + valueT addr = fixP->fx_where + fixP->fx_frag->fr_address; + + /* FIXME: We get here only at the end of assembly, when X in ".-X" is + still unknown. Since we don't have pc-relative relocations, this + is invalid. What to do if anything for a.out, is to add + pc-relative relocations everywhere including the elinux program + loader. */ + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Invalid pc-relative relocation")); + return fixP->fx_size + addr; +} + + +/* We have no need to give defaults for symbol-values. */ +symbolS * +md_undefined_symbol (name) + char *name ATTRIBUTE_UNUSED; +{ + return 0; +} + + +/* Definition of TC_FORCE_RELOCATION. + FIXME: Unsure of this. Can we omit it? Just copied from tc-i386.c + when doing multi-object format with ELF, since it's the only other + multi-object-format target with a.out and ELF. */ +int +md_cris_force_relocation (fixp) + struct fix *fixp; +{ + if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT + || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY) + return 1; + return 0; +} + +/* Check and emit error if broken-word handling has failed to fix up a + case-table. This is called from write.c, after doing everything it + knows about how to handle broken words. */ + +void +tc_cris_check_adjusted_broken_word (new_offset, brokwP) + offsetT new_offset; + struct broken_word *brokwP; +{ + if (new_offset > 32767 || new_offset < -32768) + /* We really want a genuine error, not a warning, so make it one. */ + as_bad_where (brokwP->frag->fr_file, brokwP->frag->fr_line, + _("Adjusted signed .word (%ld) overflows: `switch'-statement too large."), + (long) new_offset); +} + + +/* + * Local variables: + * eval: (c-set-style "gnu") + * indent-tabs-mode: t + * End: + */ diff --git a/gas/config/tc-cris.h b/gas/config/tc-cris.h new file mode 100644 index 0000000..85be242 --- /dev/null +++ b/gas/config/tc-cris.h @@ -0,0 +1,135 @@ +/* tc-cris.h -- Header file for tc-cris.c, the CRIS GAS port. + Copyright (C) 2000 Free Software Foundation, Inc. + + Contributed by Axis Communications AB, Lund, Sweden. + Originally written for GAS 1.38.1 by Mikael Asker. + Updated, BFDized and GNUified by Hans-Peter Nilsson. + + 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. */ + +/* See the GAS "internal" document for general documentation on this. + It is called internals.texi (internals.info when makeinfo:d), but is + not installed or makeinfo:d by "make info". */ + +/* Functions and variables that aren't declared in tc.h are declared here, + with the type/prototype that is used in the local extern-declaration of + their usage. */ + +#ifndef TC_CRIS +#define TC_CRIS + +/* Multi-target support is always on. */ +extern const char *cris_target_format PARAMS ((void)); +#define TARGET_FORMAT cris_target_format () + +#define TARGET_ARCH bfd_arch_cris + +#define TARGET_BYTES_BIG_ENDIAN 0 + +extern const char *md_shortopts; +extern struct option md_longopts[]; +extern size_t md_longopts_size; + +extern const pseudo_typeS md_pseudo_table[]; + +#define tc_comment_chars cris_comment_chars +extern const char cris_comment_chars[]; +extern const char line_comment_chars[]; +extern const char line_separator_chars[]; +extern const char EXP_CHARS[]; +extern const char FLT_CHARS[]; + +/* This should be optional, since it is ignored as an escape (assumed to + be itself) if it is not recognized. */ +#define ONLY_STANDARD_ESCAPES + +/* Note that we do not define TC_EQUAL_IN_INSN, since its current use is + in the instruction rather than the operand, and thus does not come to + use for side-effect assignments such as "and.d [r0 = r1 + 42], r3". */ +#define md_operand(x) + +#define md_number_to_chars number_to_chars_littleendian + +extern const int md_short_jump_size; +extern const int md_long_jump_size; + +/* There's no use having different functions for this; the sizes are the + same. Note that we can't #define md_short_jump_size here. */ +#define md_create_short_jump md_create_long_jump + +extern const struct relax_type md_cris_relax_table[]; +#define TC_GENERIC_RELAX_TABLE md_cris_relax_table + +#define TC_HANDLES_FX_DONE + +#define TC_FORCE_RELOCATION(fixp) md_cris_force_relocation (fixp) +extern int md_cris_force_relocation PARAMS ((struct fix *)); + +/* This is really a workaround for a bug in write.c that resolves relocs + for weak symbols - it should be postponed to the link stage or later. + */ +#define tc_fix_adjustable(X) \ + ((! (X)->fx_addsy || ! S_IS_WEAK((X)->fx_addsy)) \ + && (X)->fx_r_type != BFD_RELOC_VTABLE_INHERIT \ + && (X)->fx_r_type != BFD_RELOC_VTABLE_ENTRY) + +/* When we have fixups against constant expressions, we get a GAS-specific + section symbol at no extra charge for obscure reasons in + adjust_reloc_syms. Since ELF outputs section symbols, it gladly + outputs this "*ABS*" symbol in every object. Avoid that. */ +#define tc_frob_symbol(symp, punt) \ + do { \ + if (OUTPUT_FLAVOR == bfd_target_elf_flavour \ + && (symp) == section_symbol (absolute_section)) \ + (punt) = 1; \ + } while (0) + +#define LISTING_HEADER "GAS for CRIS" + +#if 0 +/* The testsuite does not let me define these, although they IMHO should + be preferred over the default. */ +#define LISTING_WORD_SIZE 2 +#define LISTING_LHS_WIDTH 4 +#define LISTING_LHS_WIDTH_SECOND 4 +#endif + +/* END of declaration and definitions described in the "internals" + document. */ + +/* Do this, or we will never know what hit us when the + broken-word-fixes break. Do _not_ use WARN_SIGNED_OVERFLOW_WORD, + it is only for use with WORKING_DOT_WORD and warns about most stuff. + (still in 2.9.1). */ +struct broken_word; +extern void tc_cris_check_adjusted_broken_word PARAMS ((offsetT, + struct + broken_word *)); +#define TC_CHECK_ADJUSTED_BROKEN_DOT_WORD(new_offset, brokw) \ + tc_cris_check_adjusted_broken_word ((offsetT) (new_offset), brokw) + +/* We don't want any implicit alignment, so we do nothing. */ +#define TC_IMPLICIT_LCOMM_ALIGNMENT(SIZE, P2VAR) + +#endif /* TC_CRIS */ +/* + * Local variables: + * eval: (c-set-style "gnu") + * indent-tabs-mode: t + * End: + */ |