1357 lines
37 KiB
C
1357 lines
37 KiB
C
/* tc-xgate.c -- Assembler code for Freescale XGATE
|
|
Copyright (C) 2010-2019 Free Software Foundation, Inc.
|
|
Contributed by Sean Keys <skeys@ipdatasys.com>
|
|
|
|
This file is part of GAS, the GNU Assembler.
|
|
|
|
GAS is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3, or (at your option)
|
|
any later version.
|
|
|
|
GAS is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GAS; see the file COPYING. If not, write to
|
|
the Free Software Foundation, 51 Franklin Street - Fifth Floor,
|
|
Boston, MA 02110-1301, USA. */
|
|
|
|
#include "as.h"
|
|
#include "safe-ctype.h"
|
|
#include "subsegs.h"
|
|
#include "opcode/xgate.h"
|
|
#include "dwarf2dbg.h"
|
|
#include "elf/xgate.h"
|
|
|
|
const char comment_chars[] = ";!";
|
|
const char line_comment_chars[] = "#*";
|
|
const char line_separator_chars[] = "";
|
|
const char EXP_CHARS[] = "eE";
|
|
const char FLT_CHARS[] = "dD";
|
|
|
|
/* Max opcodes per opcode handle. */
|
|
#define MAX_OPCODES 0x05
|
|
|
|
#define SIXTEENTH_BIT 0x8000
|
|
#define N_BITS_IN_WORD 16
|
|
#define MAX_NUM_OPERANDS 3
|
|
|
|
/* #define STATE_CONDITIONAL_BRANCH (1) */
|
|
#define STATE_PC_RELATIVE (2)
|
|
#define REGISTER_P(ptr) (ptr == 'r')
|
|
#define INCREMENT 01
|
|
#define DECREMENT 02
|
|
#define MAXREGISTER 07
|
|
#define MINREGISTER 00
|
|
|
|
#define OPTION_MMCU 'm'
|
|
|
|
/* This macro has no side-effects. */
|
|
#define ENCODE_RELAX(what,length) (((what) << 2) + (length))
|
|
|
|
/* Each unique opcode name has a handle. That handle may
|
|
contain pointers to opcodes with the same name but
|
|
different address modes. */
|
|
struct xgate_opcode_handle
|
|
{
|
|
int number_of_modes;
|
|
char *name;
|
|
struct xgate_opcode *opc0[MAX_OPCODES];
|
|
};
|
|
|
|
/* XGATE's registers all are 16-bit general purpose.
|
|
They are numbered according to the specifications. */
|
|
typedef enum register_id
|
|
{
|
|
REG_NONE = -1,
|
|
REG_R0 = 0,
|
|
REG_R1 = 1,
|
|
REG_R2 = 2,
|
|
REG_R3 = 3,
|
|
REG_R4 = 4,
|
|
REG_R5 = 5,
|
|
REG_R6 = 6,
|
|
REG_R7 = 7,
|
|
REG_PC = 8,
|
|
REG_CCR = 9
|
|
} register_id;
|
|
|
|
/* Operand Modifiers */
|
|
typedef enum op_modifiers
|
|
{
|
|
MOD_NONE = -1,
|
|
MOD_POSTINC = 1,
|
|
MOD_PREDEC = 2,
|
|
MOD_CONSTANT = 3,
|
|
MOD_LOAD_HIGH = 4,
|
|
MOD_LOAD_LOW = 5
|
|
}op_modifiers;
|
|
|
|
typedef struct s_operand
|
|
{
|
|
expressionS exp;
|
|
register_id reg;
|
|
op_modifiers mod;
|
|
} s_operand;
|
|
|
|
|
|
/* Forward declarations. */
|
|
static inline char *skip_whitespace (char *);
|
|
static void get_default_target (void);
|
|
static char *extract_word (char *, char *, int);
|
|
static struct xgate_opcode *xgate_find_match (struct xgate_opcode_handle *,
|
|
int, s_operand [], unsigned int);
|
|
static int cmp_opcode (struct xgate_opcode *, struct xgate_opcode *);
|
|
static void xgate_print_table (void);
|
|
static unsigned int xgate_get_operands (char *, s_operand []);
|
|
static register_id reg_name_search (char *);
|
|
static op_modifiers xgate_determine_modifiers (char **);
|
|
static void xgate_scan_operands (struct xgate_opcode *opcode, s_operand []);
|
|
static unsigned int xgate_parse_operand (struct xgate_opcode *, int *, int,
|
|
char **, s_operand);
|
|
|
|
static struct hash_control *xgate_hash;
|
|
|
|
/* Previous opcode. */
|
|
static unsigned int prev = 0;
|
|
|
|
static unsigned char fixup_required = 0;
|
|
|
|
/* Used to enable clipping of 16 bit operands into 8 bit constraints. */
|
|
static unsigned char autoHiLo = 0;
|
|
|
|
static char oper_check;
|
|
static char flag_print_insn_syntax = 0;
|
|
static char flag_print_opcodes = 0;
|
|
|
|
static int current_architecture;
|
|
static const char *default_cpu;
|
|
|
|
/* ELF flags to set in the output file header. */
|
|
static int elf_flags = E_XGATE_F64;
|
|
|
|
/* This table describes how you change sizes for the various types of variable
|
|
size expressions. This version only supports two kinds. */
|
|
|
|
/* The fields are:
|
|
How far Forward this mode will reach.
|
|
How far Backward this mode will reach.
|
|
How many bytes this mode will add to the size of the frag.
|
|
Which mode to go to if the offset won't fit in this one. */
|
|
|
|
relax_typeS md_relax_table[] =
|
|
{
|
|
{1, 1, 0, 0}, /* First entries aren't used. */
|
|
{1, 1, 0, 0}, /* For no good reason except. */
|
|
{1, 1, 0, 0}, /* that the VAX doesn't either. */
|
|
{1, 1, 0, 0},
|
|
/* XGATE 9 and 10 bit pc rel todo complete and test */
|
|
/*{(511), (-512), 0, ENCODE_RELAX (STATE_PC_RELATIVE, STATE_WORD)},
|
|
{(1023), (-1024), 0, ENCODE_RELAX (STATE_PC_RELATIVE, STATE_WORD)}, */
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
/* 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[] =
|
|
{
|
|
/* The following pseudo-ops are supported for MRI compatibility. */
|
|
{0, 0, 0}
|
|
};
|
|
|
|
const char *md_shortopts = "m:";
|
|
|
|
struct option md_longopts[] =
|
|
{
|
|
#define OPTION_PRINT_INSN_SYNTAX (OPTION_MD_BASE + 0)
|
|
{ "print-insn-syntax", no_argument, NULL, OPTION_PRINT_INSN_SYNTAX },
|
|
|
|
#define OPTION_PRINT_OPCODES (OPTION_MD_BASE + 1)
|
|
{ "print-opcodes", no_argument, NULL, OPTION_PRINT_OPCODES },
|
|
|
|
#define OPTION_GENERATE_EXAMPLE (OPTION_MD_BASE + 2)
|
|
{ "generate-example", no_argument, NULL, OPTION_GENERATE_EXAMPLE },
|
|
|
|
#define OPTION_MSHORT (OPTION_MD_BASE + 3)
|
|
{ "mshort", no_argument, NULL, OPTION_MSHORT },
|
|
|
|
#define OPTION_MLONG (OPTION_MD_BASE + 4)
|
|
{ "mlong", no_argument, NULL, OPTION_MLONG },
|
|
|
|
#define OPTION_MSHORT_DOUBLE (OPTION_MD_BASE + 5)
|
|
{ "mshort-double", no_argument, NULL, OPTION_MSHORT_DOUBLE },
|
|
|
|
#define OPTION_MLONG_DOUBLE (OPTION_MD_BASE + 6)
|
|
{ "mlong-double", no_argument, NULL, OPTION_MLONG_DOUBLE },
|
|
|
|
{ NULL, no_argument, NULL, 0 }
|
|
};
|
|
|
|
size_t md_longopts_size = sizeof (md_longopts);
|
|
|
|
const char *
|
|
md_atof (int type, char *litP, int *sizeP)
|
|
{
|
|
return ieee_md_atof (type, litP, sizeP, TRUE);
|
|
}
|
|
|
|
int
|
|
md_parse_option (int c, const char *arg)
|
|
{
|
|
switch (c)
|
|
{
|
|
case OPTION_MMCU:
|
|
if (strcasecmp (arg, "v1") == 0)
|
|
current_architecture = XGATE_V1;
|
|
else if (strcasecmp (arg, "v2") == 0)
|
|
current_architecture = XGATE_V2;
|
|
else if (strcasecmp (arg, "v3") == 0)
|
|
current_architecture = XGATE_V3;
|
|
else
|
|
as_bad (_("architecture variant invalid"));
|
|
break;
|
|
|
|
case OPTION_PRINT_INSN_SYNTAX:
|
|
flag_print_insn_syntax = 1;
|
|
break;
|
|
|
|
case OPTION_PRINT_OPCODES:
|
|
flag_print_opcodes = 1;
|
|
break;
|
|
|
|
case OPTION_GENERATE_EXAMPLE:
|
|
flag_print_opcodes = 2;
|
|
break;
|
|
|
|
case OPTION_MSHORT:
|
|
elf_flags &= ~E_XGATE_I32;
|
|
break;
|
|
|
|
case OPTION_MLONG:
|
|
elf_flags |= E_XGATE_I32;
|
|
break;
|
|
|
|
case OPTION_MSHORT_DOUBLE:
|
|
elf_flags &= ~E_XGATE_F64;
|
|
break;
|
|
|
|
case OPTION_MLONG_DOUBLE:
|
|
elf_flags |= E_XGATE_F64;
|
|
break;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
const char *
|
|
xgate_arch_format (void)
|
|
{
|
|
get_default_target ();
|
|
|
|
if (current_architecture & cpuxgate)
|
|
return "elf32-xgate";
|
|
|
|
return "error";
|
|
}
|
|
|
|
static void
|
|
get_default_target (void)
|
|
{
|
|
const bfd_target *target;
|
|
bfd abfd;
|
|
|
|
if (current_architecture != 0)
|
|
return;
|
|
|
|
default_cpu = "unknown";
|
|
target = bfd_find_target (0, &abfd);
|
|
|
|
if (target && target->name)
|
|
{
|
|
if (strcmp (target->name, "elf32-xgate") == 0)
|
|
{
|
|
current_architecture = cpuxgate;
|
|
default_cpu = "XGATE V1";
|
|
return;
|
|
}
|
|
|
|
as_bad (_("Default target `%s' is not supported."), target->name);
|
|
}
|
|
}
|
|
|
|
void
|
|
md_begin (void)
|
|
{
|
|
struct xgate_opcode *xgate_opcode_ptr = NULL;
|
|
struct xgate_opcode *xgate_op_table = NULL;
|
|
struct xgate_opcode_handle *op_handles = 0;
|
|
const char *prev_op_name = 0;
|
|
int handle_enum = 0;
|
|
int number_of_op_handles = 0;
|
|
int i, j = 0;
|
|
|
|
/* Create a local copy of our opcode table
|
|
including an extra line for NULL termination. */
|
|
xgate_op_table = XNEWVEC (struct xgate_opcode, xgate_num_opcodes);
|
|
|
|
memset (xgate_op_table, 0,
|
|
sizeof (struct xgate_opcode) * (xgate_num_opcodes));
|
|
|
|
for (xgate_opcode_ptr = (struct xgate_opcode*) xgate_opcodes, i = 0;
|
|
i < xgate_num_opcodes; i++)
|
|
xgate_op_table[i] = xgate_opcode_ptr[i];
|
|
|
|
qsort (xgate_op_table, xgate_num_opcodes, sizeof (struct xgate_opcode),
|
|
(int (*)(const void *, const void *)) cmp_opcode);
|
|
|
|
/* Calculate number of handles since this will be
|
|
smaller than the raw number of opcodes in the table. */
|
|
prev_op_name = "";
|
|
for (xgate_opcode_ptr = xgate_op_table, i = 0; i < xgate_num_opcodes;
|
|
xgate_opcode_ptr++, i++)
|
|
{
|
|
if (strcmp (prev_op_name, xgate_opcode_ptr->name))
|
|
number_of_op_handles++;
|
|
prev_op_name = xgate_opcode_ptr->name;
|
|
}
|
|
|
|
op_handles = XNEWVEC (struct xgate_opcode_handle, number_of_op_handles);
|
|
|
|
/* Insert unique opcode names into hash table, aliasing duplicates. */
|
|
xgate_hash = hash_new ();
|
|
|
|
prev_op_name = "";
|
|
for (xgate_opcode_ptr = xgate_op_table, i = 0, j = 0; i < xgate_num_opcodes;
|
|
i++, xgate_opcode_ptr++)
|
|
{
|
|
if (!strcmp (prev_op_name, xgate_opcode_ptr->name))
|
|
{
|
|
handle_enum++;
|
|
op_handles[j].opc0[handle_enum] = xgate_opcode_ptr;
|
|
}
|
|
else
|
|
{
|
|
handle_enum = 0;
|
|
if (i)
|
|
j++;
|
|
op_handles[j].name = xgate_opcode_ptr->name;
|
|
op_handles[j].opc0[0] = xgate_opcode_ptr;
|
|
hash_insert (xgate_hash, (char *) op_handles[j].name,
|
|
(char *) &(op_handles[j]));
|
|
}
|
|
op_handles[j].number_of_modes = handle_enum;
|
|
prev_op_name = op_handles[j].name;
|
|
}
|
|
|
|
if (flag_print_opcodes)
|
|
{
|
|
xgate_print_table ();
|
|
exit (EXIT_SUCCESS);
|
|
}
|
|
}
|
|
|
|
void
|
|
xgate_init_after_args (void)
|
|
{
|
|
}
|
|
|
|
void
|
|
md_show_usage (FILE * stream)
|
|
{
|
|
get_default_target ();
|
|
|
|
fprintf (stream,
|
|
_("\
|
|
Freescale XGATE co-processor options:\n\
|
|
-mshort use 16-bit int ABI (default)\n\
|
|
-mlong use 32-bit int ABI\n\
|
|
-mshort-double use 32-bit double ABI\n\
|
|
-mlong-double use 64-bit double ABI (default)\n\
|
|
--mxgate specify the processor variant [default %s]\n\
|
|
--print-insn-syntax print the syntax of instruction in case of error\n\
|
|
--print-opcodes print the list of instructions with syntax\n\
|
|
--generate-example generate an example of each instruction"),
|
|
default_cpu);
|
|
}
|
|
|
|
enum bfd_architecture
|
|
xgate_arch (void)
|
|
{
|
|
get_default_target ();
|
|
return bfd_arch_xgate;
|
|
}
|
|
|
|
int
|
|
xgate_mach (void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
xgate_print_syntax (char *name)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < xgate_num_opcodes; i++)
|
|
{
|
|
if (!strcmp (xgate_opcodes[i].name, name))
|
|
{
|
|
if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_IDR))
|
|
printf ("\tFormat is %s\tRx, Rx, Rx+|-Rx|Rx\n",
|
|
xgate_opcodes[i].name);
|
|
if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_INH))
|
|
printf ("\tFormat is %s\n", xgate_opcodes[i].name);
|
|
if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_TRI))
|
|
printf ("\tFormat is %s\tRx, Rx, Rx\n", xgate_opcodes[i].name);
|
|
if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_DYA))
|
|
printf ("\tFormat is %s\tRx, Rx\n", xgate_opcodes[i].name);
|
|
if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_IMM3))
|
|
printf ("\tFormat is %s\t<3-bit value>\n", xgate_opcodes[i].name);
|
|
if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_IMM4))
|
|
printf ("\tFormat is %s\t<4 -bit value>\n", xgate_opcodes[i].name);
|
|
if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_IMM8))
|
|
printf ("\tFormat is %s\tRx, <8-bit value>\n",
|
|
xgate_opcodes[i].name);
|
|
if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_IMM16))
|
|
printf ("\tFormat is %s\tRx, <16-bit value>\n",
|
|
xgate_opcodes[i].name);
|
|
if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_MON_R_C))
|
|
printf ("\tFormat is %s\tRx, CCR\n", xgate_opcodes[i].name);
|
|
if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_MON_C_R))
|
|
printf ("\tFormat is %s\tCCR, Rx\n", xgate_opcodes[i].name);
|
|
if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_MON_R_P))
|
|
printf ("\tFormat is %s\tRx, PC\n", xgate_opcodes[i].name);
|
|
if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_IMM16mLDW))
|
|
printf ("\tFormat is %s\tRx, <16-bit value>\n",
|
|
xgate_opcodes[i].name);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
xgate_print_table (void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < xgate_num_opcodes; i++)
|
|
xgate_print_syntax (xgate_opcodes[i].name);
|
|
|
|
return;
|
|
}
|
|
|
|
const char *
|
|
xgate_listing_header (void)
|
|
{
|
|
if (current_architecture & cpuxgate)
|
|
return "XGATE GAS ";
|
|
|
|
return "ERROR MC9S12X GAS ";
|
|
}
|
|
|
|
symbolS *
|
|
md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* GAS will call this function for each section at the end of the assembly,
|
|
to permit the CPU backend to adjust the alignment of a section. */
|
|
|
|
valueT
|
|
md_section_align (asection * seg, valueT addr)
|
|
{
|
|
int align = bfd_get_section_alignment (stdoutput, seg);
|
|
return ((addr + (1 << align) - 1) & -(1 << align));
|
|
}
|
|
|
|
void
|
|
md_assemble (char *input_line)
|
|
{
|
|
struct xgate_opcode *opcode = 0;
|
|
struct xgate_opcode *macro_opcode = 0;
|
|
struct xgate_opcode_handle *opcode_handle = 0;
|
|
/* Caller expects it to be returned as it was passed. */
|
|
char *saved_input_line = input_line;
|
|
char op_name[9] = { 0 };
|
|
unsigned int operandCount = 0;
|
|
char *p = 0;
|
|
|
|
s_operand new_operands[MAX_NUM_OPERANDS];
|
|
|
|
fixup_required = 0;
|
|
oper_check = 0; /* set error flags */
|
|
input_line = extract_word (input_line, op_name, sizeof (op_name));
|
|
|
|
/* Check to make sure we are not reading a bogus line. */
|
|
if (!op_name[0])
|
|
as_bad (_("opcode missing or not found on input line"));
|
|
|
|
if (!(opcode_handle = (struct xgate_opcode_handle *) hash_find (xgate_hash,
|
|
op_name)))
|
|
{
|
|
as_bad (_("opcode %s not found in opcode hash table"), op_name);
|
|
}
|
|
else
|
|
{
|
|
/* Parse operands so we can find the proper opcode bin. */
|
|
|
|
operandCount = xgate_get_operands (input_line, new_operands);
|
|
|
|
opcode = xgate_find_match (opcode_handle, opcode_handle->number_of_modes,
|
|
new_operands, operandCount);
|
|
|
|
if (!opcode)
|
|
{
|
|
as_bad (_("matching operands to opcode"));
|
|
xgate_print_syntax (opcode_handle->opc0[0]->name);
|
|
}
|
|
else if (opcode->size == 2)
|
|
{
|
|
/* Size is one word - assemble that native insn. */
|
|
xgate_scan_operands (opcode, new_operands);
|
|
}
|
|
else
|
|
{
|
|
/* Insn is a simplified instruction - expand it out. */
|
|
autoHiLo = 1;
|
|
unsigned int i;
|
|
|
|
/* skip past our ';' separator. */
|
|
for (i = strlen (opcode->constraints), p = opcode->constraints; i > 0;
|
|
i--, p++)
|
|
{
|
|
if (*p == ';')
|
|
{
|
|
p++;
|
|
break;
|
|
}
|
|
}
|
|
input_line = skip_whitespace (input_line);
|
|
char *macro_inline = input_line;
|
|
|
|
/* Loop though the macro's opcode list and apply operands to
|
|
each real opcode. */
|
|
for (i = 0; *p && i < (opcode->size / 2); i++)
|
|
{
|
|
/* Loop though macro operand list. */
|
|
input_line = macro_inline; /* Rewind. */
|
|
p = extract_word (p, op_name, 10);
|
|
|
|
if (!(opcode_handle = (struct xgate_opcode_handle *)
|
|
hash_find (xgate_hash, op_name)))
|
|
{
|
|
as_bad (_(": processing macro, real opcode handle"
|
|
" not found in hash"));
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
operandCount = xgate_get_operands (input_line, new_operands);
|
|
macro_opcode = xgate_find_match (opcode_handle,
|
|
opcode_handle->number_of_modes, new_operands,
|
|
operandCount);
|
|
xgate_scan_operands (macro_opcode, new_operands);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
autoHiLo = 0;
|
|
input_line = saved_input_line;
|
|
}
|
|
|
|
/* Force truly undefined symbols to their maximum size, and generally set up
|
|
the frag list to be relaxed. */
|
|
|
|
int
|
|
md_estimate_size_before_relax (fragS *fragp, asection *seg)
|
|
{
|
|
/* If symbol is undefined or located in a different section,
|
|
select the largest supported relocation. */
|
|
relax_substateT subtype;
|
|
relax_substateT rlx_state[] = { 0, 2 };
|
|
|
|
for (subtype = 0; subtype < ARRAY_SIZE (rlx_state); subtype += 2)
|
|
{
|
|
if (fragp->fr_subtype == rlx_state[subtype]
|
|
&& (!S_IS_DEFINED (fragp->fr_symbol)
|
|
|| seg != S_GET_SEGMENT (fragp->fr_symbol)))
|
|
{
|
|
fragp->fr_subtype = rlx_state[subtype + 1];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fragp->fr_subtype >= ARRAY_SIZE (md_relax_table))
|
|
abort ();
|
|
|
|
return md_relax_table[fragp->fr_subtype].rlx_length;
|
|
}
|
|
|
|
|
|
/* Relocation, relaxation and frag conversions. */
|
|
|
|
/* PC-relative offsets are relative to the start of the
|
|
next instruction. That is, the address of the offset, plus its
|
|
size, since the offset is always the last part of the insn. */
|
|
|
|
long
|
|
md_pcrel_from (fixS * fixP)
|
|
{
|
|
return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
|
|
}
|
|
|
|
/* If while processing a fixup, a reloc really needs to be created
|
|
then it is done here. */
|
|
|
|
arelent *
|
|
tc_gen_reloc (asection * section ATTRIBUTE_UNUSED, fixS * fixp)
|
|
{
|
|
arelent * reloc;
|
|
|
|
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_r_type == 0)
|
|
reloc->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_16);
|
|
else
|
|
reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
|
|
|
|
if (reloc->howto == (reloc_howto_type *) NULL)
|
|
{
|
|
as_bad_where (fixp->fx_file, fixp->fx_line, _
|
|
("Relocation %d is not supported by object file format."),
|
|
(int) fixp->fx_r_type);
|
|
return NULL;
|
|
}
|
|
|
|
/* Since we use Rel instead of Rela, encode the vtable entry to be
|
|
used in the relocation's section offset. */
|
|
if (fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
|
|
reloc->address = fixp->fx_offset;
|
|
reloc->addend = 0;
|
|
return reloc;
|
|
}
|
|
|
|
/* Patch the instruction with the resolved operand. Elf relocation
|
|
info will also be generated to take care of linker/loader fixups.
|
|
The XGATE addresses only 16-bit addresses.The BFD_RELOC_32 is necessary
|
|
for the support of --gstabs. */
|
|
|
|
void
|
|
md_apply_fix (fixS * fixP, valueT * valP, segT seg ATTRIBUTE_UNUSED)
|
|
{
|
|
char *where;
|
|
long value = *valP;
|
|
int opcode = 0;
|
|
ldiv_t result;
|
|
|
|
/* If the fixup is done mark it done so no further symbol resolution
|
|
will take place. */
|
|
if (fixP->fx_addsy == (symbolS *) NULL)
|
|
fixP->fx_done = 1;
|
|
|
|
/* We don't actually support subtracting a symbol. */
|
|
if (fixP->fx_subsy != (symbolS *) NULL)
|
|
as_bad_where (fixP->fx_file, fixP->fx_line, _("Expression too complex."));
|
|
|
|
where = fixP->fx_frag->fr_literal + fixP->fx_where;
|
|
opcode = bfd_getl16 (where);
|
|
int mask = 0;
|
|
|
|
switch (fixP->fx_r_type)
|
|
{
|
|
case R_XGATE_PCREL_9:
|
|
if (value < -512 || value > 511)
|
|
as_bad_where (fixP->fx_file, fixP->fx_line,
|
|
_("Value %ld too large for 9-bit PC-relative branch."),
|
|
value);
|
|
result = ldiv (value, 2); /* from bytes to words */
|
|
value = result.quot;
|
|
if (result.rem)
|
|
as_bad_where (fixP->fx_file, fixP->fx_line, _
|
|
("Value %ld not aligned by 2 for 9-bit"
|
|
" PC-relative branch."), value);
|
|
/* Clip into 8-bit field.
|
|
FIXME I'm sure there is a more proper place for this. */
|
|
mask = 0x1FF;
|
|
value &= mask;
|
|
number_to_chars_bigendian (where, (opcode | value), 2);
|
|
break;
|
|
case R_XGATE_PCREL_10:
|
|
if (value < -1024 || value > 1023)
|
|
as_bad_where (fixP->fx_file, fixP->fx_line,
|
|
_("Value %ld too large for 10-bit PC-relative branch."),
|
|
value);
|
|
result = ldiv (value, 2); /* from bytes to words */
|
|
value = result.quot;
|
|
if (result.rem)
|
|
as_bad_where (fixP->fx_file, fixP->fx_line, _
|
|
("Value %ld not aligned by 2 for 10-bit"
|
|
" PC-relative branch."), value);
|
|
/* Clip into 9-bit field.
|
|
FIXME I'm sure there is a more proper place for this. */
|
|
mask = 0x3FF;
|
|
value &= mask;
|
|
number_to_chars_bigendian (where, (opcode | value), 2);
|
|
break;
|
|
case BFD_RELOC_XGATE_IMM8_HI:
|
|
if (value < -65537 || value > 65535)
|
|
as_bad_where (fixP->fx_file, fixP->fx_line,
|
|
_("Value out of 16-bit range."));
|
|
value >>= 8;
|
|
value &= 0x00ff;
|
|
bfd_putb16 ((bfd_vma) value | opcode, (void *) where);
|
|
break;
|
|
case BFD_RELOC_XGATE_24:
|
|
case BFD_RELOC_XGATE_IMM8_LO:
|
|
if (value < -65537 || value > 65535)
|
|
as_bad_where (fixP->fx_file, fixP->fx_line,
|
|
_("Value out of 16-bit range."));
|
|
value &= 0x00ff;
|
|
bfd_putb16 ((bfd_vma) value | opcode, (void *) where);
|
|
break;
|
|
case BFD_RELOC_XGATE_IMM3:
|
|
if (value < 0 || value > 7)
|
|
as_bad_where (fixP->fx_file, fixP->fx_line,
|
|
_("Value out of 3-bit range."));
|
|
value <<= 8; /* make big endian */
|
|
number_to_chars_bigendian (where, (opcode | value), 2);
|
|
break;
|
|
case BFD_RELOC_XGATE_IMM4:
|
|
if (value < 0 || value > 15)
|
|
as_bad_where (fixP->fx_file, fixP->fx_line,
|
|
_("Value out of 4-bit range."));
|
|
value <<= 4; /* align the operand bits */
|
|
number_to_chars_bigendian (where, (opcode | value), 2);
|
|
break;
|
|
case BFD_RELOC_XGATE_IMM5:
|
|
if (value < 0 || value > 31)
|
|
as_bad_where (fixP->fx_file, fixP->fx_line,
|
|
_("Value out of 5-bit range."));
|
|
value <<= 5; /* align the operand bits */
|
|
number_to_chars_bigendian (where, (opcode | value), 2);
|
|
break;
|
|
case BFD_RELOC_8:
|
|
((bfd_byte *) where)[0] = (bfd_byte) value;
|
|
break;
|
|
case BFD_RELOC_32:
|
|
bfd_putb32 ((bfd_vma) value, (unsigned char *) where);
|
|
break;
|
|
case BFD_RELOC_16:
|
|
bfd_putb16 ((bfd_vma) value, (unsigned char *) where);
|
|
break;
|
|
default:
|
|
as_fatal (_("Line %d: unknown relocation type: 0x%x."), fixP->fx_line,
|
|
fixP->fx_r_type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* See whether we need to force a relocation into the output file. */
|
|
|
|
int
|
|
tc_xgate_force_relocation (fixS * fixP)
|
|
{
|
|
if (fixP->fx_r_type == BFD_RELOC_XGATE_RL_GROUP)
|
|
return 1;
|
|
return generic_force_reloc (fixP);
|
|
}
|
|
|
|
/* Here we decide which fixups can be adjusted to make them relative
|
|
to the beginning of the section instead of the symbol. Basically
|
|
we need to make sure that the linker relaxation is done
|
|
correctly, so in some cases we force the original symbol to be
|
|
used. */
|
|
|
|
int
|
|
tc_xgate_fix_adjustable (fixS * fixP)
|
|
{
|
|
switch (fixP->fx_r_type)
|
|
{
|
|
/* For the linker relaxation to work correctly, these relocs
|
|
need to be on the symbol itself. */
|
|
case BFD_RELOC_16:
|
|
case BFD_RELOC_XGATE_RL_JUMP:
|
|
case BFD_RELOC_XGATE_RL_GROUP:
|
|
case BFD_RELOC_VTABLE_INHERIT:
|
|
case BFD_RELOC_VTABLE_ENTRY:
|
|
case BFD_RELOC_32:
|
|
return 0;
|
|
default:
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
void
|
|
md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED,
|
|
asection * sec ATTRIBUTE_UNUSED,
|
|
fragS * fragP ATTRIBUTE_UNUSED)
|
|
{
|
|
as_bad (("md_convert_frag not implemented yet"));
|
|
abort ();
|
|
}
|
|
|
|
/* Set the ELF specific flags. */
|
|
|
|
void
|
|
xgate_elf_final_processing (void)
|
|
{
|
|
elf_flags |= EF_XGATE_MACH;
|
|
elf_elfheader (stdoutput)->e_flags &= ~EF_XGATE_ABI;
|
|
elf_elfheader (stdoutput)->e_flags |= elf_flags;
|
|
}
|
|
|
|
static inline char *
|
|
skip_whitespace (char *s)
|
|
{
|
|
while (*s == ' ' || *s == '\t' || *s == '(' || *s == ')')
|
|
s++;
|
|
|
|
return s;
|
|
}
|
|
|
|
/* Extract a word (continuous alpha-numeric chars) from the input line. */
|
|
|
|
static char *
|
|
extract_word (char *from, char *to, int limit)
|
|
{
|
|
char *op_end;
|
|
int size = 0;
|
|
|
|
/* Drop leading whitespace. */
|
|
from = skip_whitespace (from);
|
|
*to = 0;
|
|
/* Find the op code end. */
|
|
for (op_end = from; *op_end != 0 && is_part_of_name (*op_end);)
|
|
{
|
|
to[size++] = *op_end++;
|
|
if (size + 1 >= limit)
|
|
break;
|
|
}
|
|
to[size] = 0;
|
|
return op_end;
|
|
}
|
|
|
|
static char *
|
|
xgate_new_instruction (int size)
|
|
{
|
|
char *f = frag_more (size);
|
|
dwarf2_emit_insn (size);
|
|
return f;
|
|
}
|
|
|
|
static unsigned short
|
|
xgate_apply_operand (unsigned short new_mask,
|
|
unsigned short *availiable_mask_bits,
|
|
unsigned short mask,
|
|
unsigned char n_bits)
|
|
{
|
|
unsigned short n_shifts;
|
|
unsigned int n_drop_bits;
|
|
|
|
/* Shift until you find an available operand bit "1" and record
|
|
the number of shifts. */
|
|
for (n_shifts = 0;
|
|
!(*availiable_mask_bits & SIXTEENTH_BIT) && n_shifts < 16;
|
|
n_shifts++)
|
|
*availiable_mask_bits <<= 1;
|
|
|
|
/* Shift for the number of bits your operand requires while bits
|
|
are available. */
|
|
for (n_drop_bits = n_bits;
|
|
n_drop_bits && (*availiable_mask_bits & SIXTEENTH_BIT);
|
|
--n_drop_bits)
|
|
*availiable_mask_bits <<= 1;
|
|
|
|
if (n_drop_bits)
|
|
as_bad (_(":operand has too many bits"));
|
|
*availiable_mask_bits >>= n_shifts + n_bits;
|
|
if ((n_drop_bits == 0) && (*availiable_mask_bits == 0))
|
|
{
|
|
oper_check = 1; /* flag operand check as good */
|
|
}
|
|
new_mask <<= N_BITS_IN_WORD - (n_shifts + n_bits);
|
|
mask |= new_mask;
|
|
return mask;
|
|
}
|
|
|
|
/* Parse ordinary expression. */
|
|
|
|
static char *
|
|
xgate_parse_exp (char *s, expressionS * op)
|
|
{
|
|
input_line_pointer = s;
|
|
|
|
expression (op);
|
|
if (op->X_op == O_absent)
|
|
as_bad (_("missing operand"));
|
|
return input_line_pointer;
|
|
}
|
|
|
|
static int
|
|
cmp_opcode (struct xgate_opcode *op1, struct xgate_opcode *op2)
|
|
{
|
|
return strcmp (op1->name, op2->name);
|
|
}
|
|
|
|
static struct xgate_opcode *
|
|
xgate_find_match (struct xgate_opcode_handle *opcode_handle,
|
|
int numberOfModes, s_operand oprs[], unsigned int operandCount)
|
|
{
|
|
int i;
|
|
|
|
if (numberOfModes == 0)
|
|
return opcode_handle->opc0[0];
|
|
|
|
for (i = 0; i <= numberOfModes; i++)
|
|
{
|
|
switch (operandCount)
|
|
{
|
|
case 0:
|
|
if (!strcmp (opcode_handle->opc0[i]->constraints, XGATE_OP_INH))
|
|
return opcode_handle->opc0[i];
|
|
break;
|
|
case 1:
|
|
if (oprs[0].reg >= REG_R0 && oprs[0].reg <= REG_R7)
|
|
{
|
|
if (!strcmp (opcode_handle->opc0[i]->constraints, XGATE_OP_MON))
|
|
return opcode_handle->opc0[i];
|
|
if (!strcmp (opcode_handle->opc0[i]->constraints, XGATE_OP_DYA_MON))
|
|
return opcode_handle->opc0[i];
|
|
}
|
|
if (oprs[0].reg == REG_NONE)
|
|
if (!strcmp (opcode_handle->opc0[i]->constraints, XGATE_OP_IMM3))
|
|
return opcode_handle->opc0[i];
|
|
break;
|
|
case 2:
|
|
if (oprs[0].reg >= REG_R0 && oprs[0].reg <= REG_R7)
|
|
{
|
|
if (oprs[1].reg >= REG_R0 && oprs[1].reg <= REG_R7)
|
|
{
|
|
if (!strcmp (opcode_handle->opc0[i]->constraints, XGATE_OP_DYA))
|
|
return opcode_handle->opc0[i];
|
|
}
|
|
if (oprs[1].reg == REG_CCR)
|
|
if (!strcmp (opcode_handle->opc0[i]->constraints,
|
|
XGATE_OP_MON_R_C))
|
|
return opcode_handle->opc0[i];
|
|
if (oprs[1].reg == REG_PC)
|
|
if (!strcmp (opcode_handle->opc0[i]->constraints,
|
|
XGATE_OP_MON_R_P))
|
|
return opcode_handle->opc0[i];
|
|
if (oprs[1].reg == REG_NONE)
|
|
if (!strcmp (opcode_handle->opc0[i]->constraints, XGATE_OP_IMM16)
|
|
|| !strcmp (opcode_handle->opc0[i]->constraints, XGATE_OP_IMM8)
|
|
|| !strcmp (opcode_handle->opc0[i]->constraints, XGATE_OP_IMM4)
|
|
|| !strcmp (opcode_handle->opc0[i]->constraints,
|
|
XGATE_OP_IMM16mADD)
|
|
|| !strcmp (opcode_handle->opc0[i]->constraints,
|
|
XGATE_OP_IMM16mAND)
|
|
|| !strcmp (opcode_handle->opc0[i]->constraints,
|
|
XGATE_OP_IMM16mCPC)
|
|
|| !strcmp (opcode_handle->opc0[i]->constraints,
|
|
XGATE_OP_IMM16mSUB)
|
|
|| !strcmp (opcode_handle->opc0[i]->constraints,
|
|
XGATE_OP_IMM16mLDW))
|
|
return opcode_handle->opc0[i];
|
|
}
|
|
if (oprs[0].reg == REG_CCR)
|
|
if (!strcmp (opcode_handle->opc0[i]->constraints, XGATE_OP_MON_C_R))
|
|
return opcode_handle->opc0[i];
|
|
break;
|
|
case 3:
|
|
if (oprs[0].reg >= REG_R0 && oprs[0].reg <= REG_R7)
|
|
{
|
|
if (oprs[1].reg >= REG_R0 && oprs[1].reg <= REG_R7)
|
|
{
|
|
if (oprs[2].reg >= REG_R0 && oprs[2].reg <= REG_R7)
|
|
{
|
|
if (!strcmp (opcode_handle->opc0[i]->constraints,
|
|
XGATE_OP_IDR)
|
|
|| !strcmp (opcode_handle->opc0[i]->constraints,
|
|
XGATE_OP_TRI))
|
|
return opcode_handle->opc0[i];
|
|
}
|
|
|
|
if (oprs[2].reg == REG_NONE)
|
|
if (!strcmp (opcode_handle->opc0[i]->constraints,
|
|
XGATE_OP_IDO5))
|
|
return opcode_handle->opc0[i];
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
as_bad (_("unknown operand count"));
|
|
break;
|
|
}
|
|
}
|
|
return NULL ;
|
|
}
|
|
|
|
/* Because we are dealing with two different core that view the system
|
|
memory with different offsets, we must differentiate what core a
|
|
symbol belongs to, in order for the linker to cross-link. */
|
|
|
|
int
|
|
xgate_frob_symbol (symbolS *sym)
|
|
{
|
|
asymbol *bfdsym;
|
|
elf_symbol_type *elfsym;
|
|
|
|
bfdsym = symbol_get_bfdsym (sym);
|
|
elfsym = elf_symbol_from (bfd_asymbol_bfd (bfdsym), bfdsym);
|
|
|
|
gas_assert (elfsym);
|
|
|
|
/* Mark the symbol as being *from XGATE */
|
|
elfsym->internal_elf_sym.st_target_internal = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int
|
|
xgate_get_operands (char *line, s_operand oprs[])
|
|
{
|
|
int num_operands;
|
|
|
|
/* If there are no operands, then it must be inherent. */
|
|
if (*line == 0 || *line == '\n' || *line == '\r')
|
|
return 0;
|
|
|
|
for (num_operands = 0; strlen (line) && (num_operands < MAX_NUM_OPERANDS);
|
|
num_operands++)
|
|
{
|
|
line = skip_whitespace (line);
|
|
if (*line == '#')
|
|
line++;
|
|
|
|
oprs[num_operands].mod = xgate_determine_modifiers (&line);
|
|
|
|
if ((oprs[num_operands].reg = reg_name_search (line)) == REG_NONE)
|
|
line = xgate_parse_exp (line, &oprs[num_operands].exp);
|
|
|
|
/* skip to next operand */
|
|
while (*line != 0)
|
|
{
|
|
if (*line == ',')
|
|
{
|
|
line++;
|
|
break;
|
|
}
|
|
line++;
|
|
}
|
|
}
|
|
if (num_operands > MAX_NUM_OPERANDS)
|
|
return 0;
|
|
return num_operands;
|
|
}
|
|
|
|
/* reg_name_search() finds the register number given its name.
|
|
Returns the register number or REG_NONE on failure. */
|
|
|
|
static register_id
|
|
reg_name_search (char *name)
|
|
{
|
|
if (strncasecmp (name, "r0", 2) == 0)
|
|
return REG_R0;
|
|
if (strncasecmp (name, "r1", 2) == 0)
|
|
return REG_R1;
|
|
if (strncasecmp (name, "r2", 2) == 0)
|
|
return REG_R2;
|
|
if (strncasecmp (name, "r3", 2) == 0)
|
|
return REG_R3;
|
|
if (strncasecmp (name, "r4", 2) == 0)
|
|
return REG_R4;
|
|
if (strncasecmp (name, "r5", 2) == 0)
|
|
return REG_R5;
|
|
if (strncasecmp (name, "r6", 2) == 0)
|
|
return REG_R6;
|
|
if (strncasecmp (name, "r7", 2) == 0)
|
|
return REG_R7;
|
|
if (strncasecmp (name, "pc", 2) == 0)
|
|
return REG_PC;
|
|
if (strncasecmp (name, "ccr", 3) == 0)
|
|
return REG_CCR;
|
|
return REG_NONE;
|
|
}
|
|
|
|
/* Parse operand modifiers such as inc/dec/hi/low. */
|
|
|
|
static op_modifiers
|
|
xgate_determine_modifiers (char **line)
|
|
{
|
|
char *local_line = line[0];
|
|
|
|
if (strncasecmp (local_line, "%hi", 3) == 0)
|
|
{
|
|
*line += 3;
|
|
return MOD_LOAD_HIGH;
|
|
}
|
|
if (strncasecmp (local_line, "%lo", 3) == 0)
|
|
{
|
|
*line += 3;
|
|
return MOD_LOAD_LOW;
|
|
}
|
|
if (*(local_line + 2) == '+')
|
|
return MOD_POSTINC;
|
|
if (strncasecmp (local_line, "-r", 2) == 0)
|
|
{
|
|
*line += 1;
|
|
return MOD_PREDEC;
|
|
}
|
|
return MOD_NONE;
|
|
}
|
|
|
|
/* Parse instruction operands. */
|
|
|
|
static void
|
|
xgate_scan_operands (struct xgate_opcode *opcode, s_operand oprs[])
|
|
{
|
|
char *frag = xgate_new_instruction (opcode->size);
|
|
int where = frag - frag_now->fr_literal;
|
|
char *op = opcode->constraints;
|
|
unsigned int bin = (int) opcode->bin_opcode;
|
|
unsigned short oper_mask = 0;
|
|
int operand_bit_length = 0;
|
|
unsigned int operand = 0;
|
|
char n_operand_bits = 0;
|
|
char first_operand_equals_second = 0;
|
|
int i = 0;
|
|
char c = 0;
|
|
|
|
/* Generate available operand bits mask. */
|
|
for (i = 0; (c = opcode->format[i]); i++)
|
|
{
|
|
if (ISDIGIT (c) || (c == 's'))
|
|
{
|
|
oper_mask <<= 1;
|
|
}
|
|
else
|
|
{
|
|
oper_mask <<= 1;
|
|
oper_mask += 1;
|
|
n_operand_bits++;
|
|
}
|
|
}
|
|
|
|
/* Parse first operand. */
|
|
if (*op)
|
|
{
|
|
if (*op == '=')
|
|
{
|
|
first_operand_equals_second = 1;
|
|
++op;
|
|
}
|
|
operand = xgate_parse_operand (opcode, &operand_bit_length, where,
|
|
&op, oprs[0]);
|
|
++op;
|
|
bin = xgate_apply_operand (operand, &oper_mask, bin, operand_bit_length);
|
|
|
|
if (first_operand_equals_second)
|
|
bin = xgate_apply_operand (operand, &oper_mask, bin,
|
|
operand_bit_length);
|
|
/* Parse second operand. */
|
|
if (*op)
|
|
{
|
|
if (*op == ',')
|
|
++op;
|
|
if (first_operand_equals_second)
|
|
{
|
|
bin = xgate_apply_operand (operand, &oper_mask, bin,
|
|
operand_bit_length);
|
|
++op;
|
|
}
|
|
else
|
|
{
|
|
operand = xgate_parse_operand (opcode, &operand_bit_length, where,
|
|
&op, oprs[1]);
|
|
bin = xgate_apply_operand (operand, &oper_mask, bin,
|
|
operand_bit_length);
|
|
++op;
|
|
}
|
|
}
|
|
/* Parse the third register. */
|
|
if (*op)
|
|
{
|
|
if (*op == ',')
|
|
++op;
|
|
operand = xgate_parse_operand (opcode, &operand_bit_length, where,
|
|
&op, oprs[2]);
|
|
bin = xgate_apply_operand (operand, &oper_mask, bin,
|
|
operand_bit_length);
|
|
}
|
|
}
|
|
if (opcode->size == 2 && fixup_required)
|
|
{
|
|
bfd_putl16 (bin, frag);
|
|
}
|
|
else if ( !strcmp (opcode->constraints, XGATE_OP_REL9)
|
|
|| !strcmp (opcode->constraints, XGATE_OP_REL10))
|
|
{
|
|
/* Write our data to a frag for further processing. */
|
|
bfd_putl16 (opcode->bin_opcode, frag);
|
|
}
|
|
else
|
|
{
|
|
/* Apply operand mask(s)to bin opcode and write the output. */
|
|
/* Since we are done write this frag in xgate BE format. */
|
|
number_to_chars_bigendian (frag, bin, opcode->size);
|
|
}
|
|
prev = bin;
|
|
return;
|
|
}
|
|
|
|
static unsigned int
|
|
xgate_parse_operand (struct xgate_opcode *opcode,
|
|
int *bit_width,
|
|
int where,
|
|
char **op_con,
|
|
s_operand operand)
|
|
{
|
|
char *op_constraint = *op_con;
|
|
unsigned int op_mask = 0;
|
|
unsigned int pp_fix = 0;
|
|
unsigned short max_size = 0;
|
|
int i;
|
|
|
|
*bit_width = 0;
|
|
/* Reset. */
|
|
|
|
switch (*op_constraint)
|
|
{
|
|
case '+': /* Indexed register operand +/- or plain r. */
|
|
/* Default to neither inc or dec. */
|
|
pp_fix = 0;
|
|
*bit_width = 5;
|
|
|
|
if (operand.reg == REG_NONE)
|
|
as_bad (_(": expected register name r0-r7 ") );
|
|
op_mask = operand.reg;
|
|
if (operand.mod == MOD_POSTINC)
|
|
pp_fix = INCREMENT;
|
|
if (operand.mod == MOD_PREDEC)
|
|
pp_fix = DECREMENT;
|
|
op_mask <<= 2;
|
|
op_mask |= pp_fix;
|
|
break;
|
|
|
|
case 'r': /* Register operand. */
|
|
if (operand.reg == REG_NONE)
|
|
as_bad (_(": expected register name r0-r7 "));
|
|
|
|
*bit_width = 3;
|
|
|
|
op_mask = operand.reg;
|
|
break;
|
|
|
|
case 'i': /* Immediate value or expression expected. */
|
|
/* Advance the original format pointer. */
|
|
(*op_con)++;
|
|
op_constraint++;
|
|
if (ISDIGIT (*op_constraint))
|
|
*bit_width = (int) *op_constraint - '0';
|
|
else if (*op_constraint == 'a')
|
|
*bit_width = 0x0A;
|
|
else if (*op_constraint == 'f')
|
|
*bit_width = 0x0F;
|
|
|
|
/* http://tigcc.ticalc.org/doc/gnuasm.html#SEC31 */
|
|
if (operand.exp.X_op == O_constant)
|
|
{
|
|
op_mask = operand.exp.X_add_number;
|
|
if (((opcode->name[strlen (opcode->name) - 1] == 'l') && autoHiLo)
|
|
|| operand.mod == MOD_LOAD_LOW)
|
|
op_mask &= 0x00FF;
|
|
else if (((opcode->name[strlen (opcode->name) - 1]) == 'h'
|
|
&& autoHiLo) || operand.mod == MOD_LOAD_HIGH)
|
|
op_mask >>= 8;
|
|
|
|
/* Make sure it fits. */
|
|
for (i = *bit_width; i; i--)
|
|
{
|
|
max_size <<= 1;
|
|
max_size += 1;
|
|
}
|
|
if (op_mask > max_size)
|
|
as_bad (_(":operand value(%d) too big for constraint"), op_mask);
|
|
}
|
|
else
|
|
{
|
|
/* Should be BFD_RELOC_XGATE_IMM8_LO instead of BFD_RELOC_XGATE_24
|
|
TODO fix. */
|
|
fixup_required = 1;
|
|
if (*op_constraint == '8')
|
|
{
|
|
if (((opcode->name[strlen (opcode->name) - 1] == 'l')
|
|
&& autoHiLo) || operand.mod == MOD_LOAD_LOW)
|
|
fix_new_exp (frag_now, where, 2, &operand.exp, FALSE,
|
|
BFD_RELOC_XGATE_24);
|
|
else if (((opcode->name[strlen (opcode->name) - 1]) == 'h'
|
|
&& autoHiLo) || operand.mod == MOD_LOAD_HIGH )
|
|
fix_new_exp (frag_now, where, 2, &operand.exp, FALSE,
|
|
BFD_RELOC_XGATE_IMM8_HI);
|
|
else
|
|
as_bad (_("you must use a hi/lo directive or 16-bit macro "
|
|
"to load a 16-bit value."));
|
|
}
|
|
else if (*op_constraint == '5')
|
|
fix_new_exp (frag_now, where, 2, &operand.exp, FALSE,
|
|
BFD_RELOC_XGATE_IMM5);
|
|
else if (*op_constraint == '4')
|
|
fix_new_exp (frag_now, where, 2, &operand.exp, FALSE,
|
|
BFD_RELOC_XGATE_IMM4);
|
|
else if (*op_constraint == '3')
|
|
fix_new_exp (frag_now, where, 2, &operand.exp, FALSE,
|
|
BFD_RELOC_XGATE_IMM3);
|
|
else
|
|
as_bad (_(":unknown relocation constraint size"));
|
|
}
|
|
break;
|
|
|
|
case 'c': /* CCR register expected. */
|
|
*bit_width = 0;
|
|
if (operand.reg != REG_CCR)
|
|
as_bad (_(": expected register name ccr "));
|
|
break;
|
|
|
|
case 'p': /* PC register expected. */
|
|
*bit_width = 0;
|
|
if (operand.reg != REG_PC)
|
|
as_bad (_(": expected register name pc "));
|
|
break;
|
|
|
|
case 'b': /* Branch expected. */
|
|
(*op_con)++;
|
|
op_constraint++;
|
|
|
|
if (operand.exp.X_op != O_register)
|
|
{
|
|
if (*op_constraint == '9')
|
|
fix_new_exp (frag_now, where, 2, &operand.exp, TRUE,
|
|
R_XGATE_PCREL_9);
|
|
else if (*op_constraint == 'a')
|
|
fix_new_exp (frag_now, where, 2, &operand.exp, TRUE,
|
|
R_XGATE_PCREL_10);
|
|
}
|
|
else
|
|
as_fatal (_("Operand `%x' not recognized in fixup8."),
|
|
operand.exp.X_op);
|
|
break;
|
|
case '?':
|
|
break;
|
|
|
|
default:
|
|
as_bad (_("unknown constraint `%c'"), *op_constraint);
|
|
break;
|
|
}
|
|
return op_mask;
|
|
}
|